diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index 20843a420c..0000000000 --- a/.cirrus.yml +++ /dev/null @@ -1,109 +0,0 @@ -env: - CIRRUS_CLONE_DEPTH: 1 - -windows_msys2_task: - timeout_in: 90m - windows_container: - image: cirrusci/windowsservercore:2019 - os_version: 2019 - cpu: 8 - memory: 8G - env: - CIRRUS_SHELL: powershell - MSYS: winsymlinks:nativestrict - MSYSTEM: MINGW64 - MSYS2_URL: https://github.com/msys2/msys2-installer/releases/download/2022-05-03/msys2-base-x86_64-20220503.sfx.exe - MSYS2_FINGERPRINT: 0 - MSYS2_PACKAGES: " - diffutils git grep make pkg-config sed - mingw-w64-x86_64-python - mingw-w64-x86_64-python-sphinx - mingw-w64-x86_64-toolchain - mingw-w64-x86_64-SDL2 - mingw-w64-x86_64-SDL2_image - mingw-w64-x86_64-gtk3 - mingw-w64-x86_64-glib2 - mingw-w64-x86_64-ninja - mingw-w64-x86_64-jemalloc - mingw-w64-x86_64-lzo2 - mingw-w64-x86_64-zstd - mingw-w64-x86_64-libjpeg-turbo - mingw-w64-x86_64-pixman - mingw-w64-x86_64-libgcrypt - mingw-w64-x86_64-libpng - mingw-w64-x86_64-libssh - mingw-w64-x86_64-snappy - mingw-w64-x86_64-libusb - mingw-w64-x86_64-usbredir - mingw-w64-x86_64-libtasn1 - mingw-w64-x86_64-nettle - mingw-w64-x86_64-cyrus-sasl - mingw-w64-x86_64-curl - mingw-w64-x86_64-gnutls - mingw-w64-x86_64-libnfs - " - CHERE_INVOKING: 1 - msys2_cache: - folder: C:\tools\archive - reupload_on_changes: false - # These env variables are used to generate fingerprint to trigger the cache procedure - # If wanna to force re-populate msys2, increase MSYS2_FINGERPRINT - fingerprint_script: - - | - echo $env:CIRRUS_TASK_NAME - echo $env:MSYS2_URL - echo $env:MSYS2_FINGERPRINT - echo $env:MSYS2_PACKAGES - populate_script: - - | - md -Force C:\tools\archive\pkg - $start_time = Get-Date - bitsadmin /transfer msys_download /dynamic /download /priority FOREGROUND $env:MSYS2_URL C:\tools\archive\base.exe - Write-Output "Download time taken: $((Get-Date).Subtract($start_time))" - cd C:\tools - C:\tools\archive\base.exe -y - del -Force C:\tools\archive\base.exe - Write-Output "Base install time taken: $((Get-Date).Subtract($start_time))" - $start_time = Get-Date - - ((Get-Content -path C:\tools\msys64\etc\\post-install\\07-pacman-key.post -Raw) -replace '--refresh-keys', '--version') | Set-Content -Path C:\tools\msys64\etc\\post-install\\07-pacman-key.post - C:\tools\msys64\usr\bin\bash.exe -lc "sed -i 's/^CheckSpace/#CheckSpace/g' /etc/pacman.conf" - C:\tools\msys64\usr\bin\bash.exe -lc "export" - C:\tools\msys64\usr\bin\pacman.exe --noconfirm -Sy - echo Y | C:\tools\msys64\usr\bin\pacman.exe --noconfirm -Suu --overwrite=* - taskkill /F /FI "MODULES eq msys-2.0.dll" - tasklist - C:\tools\msys64\usr\bin\bash.exe -lc "mv -f /etc/pacman.conf.pacnew /etc/pacman.conf || true" - C:\tools\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Syuu --overwrite=*" - Write-Output "Core install time taken: $((Get-Date).Subtract($start_time))" - $start_time = Get-Date - - C:\tools\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -S --needed $env:MSYS2_PACKAGES" - Write-Output "Package install time taken: $((Get-Date).Subtract($start_time))" - $start_time = Get-Date - - del -Force -ErrorAction SilentlyContinue C:\tools\msys64\etc\mtab - del -Force -ErrorAction SilentlyContinue C:\tools\msys64\dev\fd - del -Force -ErrorAction SilentlyContinue C:\tools\msys64\dev\stderr - del -Force -ErrorAction SilentlyContinue C:\tools\msys64\dev\stdin - del -Force -ErrorAction SilentlyContinue C:\tools\msys64\dev\stdout - del -Force -Recurse -ErrorAction SilentlyContinue C:\tools\msys64\var\cache\pacman\pkg - tar cf C:\tools\archive\msys64.tar -C C:\tools\ msys64 - - Write-Output "Package archive time taken: $((Get-Date).Subtract($start_time))" - del -Force -Recurse -ErrorAction SilentlyContinue c:\tools\msys64 - install_script: - - | - $start_time = Get-Date - cd C:\tools - ls C:\tools\archive\msys64.tar - tar xf C:\tools\archive\msys64.tar - Write-Output "Extract msys2 time taken: $((Get-Date).Subtract($start_time))" - script: - - C:\tools\msys64\usr\bin\bash.exe -lc "mkdir build" - - C:\tools\msys64\usr\bin\bash.exe -lc "cd build && ../configure --python=python3" - - C:\tools\msys64\usr\bin\bash.exe -lc "cd build && make -j8" - - exit $LastExitCode - test_script: - - C:\tools\msys64\usr\bin\bash.exe -lc "cd build && make V=1 check" - - exit $LastExitCode diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..93718ef425 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,21 @@ +# +# List of code-formatting clean ups the git blame can ignore +# +# git blame --ignore-revs-file .git-blame-ignore-revs +# +# or +# +# git config blame.ignoreRevsFile .git-blame-ignore-revs +# + +# gdbstub: clean-up indents +ad9e4585b3c7425759d3eea697afbca71d2c2082 + +# e1000e: fix code style +0eadd56bf53ab196a16d492d7dd31c62e1c24c32 + +# target/riscv: coding style fixes +8c7feddddd9218b407792120bcfda0347ed16205 + +# replace TABs with spaces +48805df9c22a0700fba4b3b548fafaa21726ca68 diff --git a/.gitignore b/.gitignore index 9726a778b3..61fa39967b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,13 @@ /GNUmakefile /build/ +/.cache/ +/.vscode/ *.pyc .sdk .stgit-* .git-submodule-status +.clang-format +.gdb_history cscope.* tags TAGS diff --git a/.gitlab-ci.d/base.yml b/.gitlab-ci.d/base.yml index f334f3ded7..bf3d8efab6 100644 --- a/.gitlab-ci.d/base.yml +++ b/.gitlab-ci.d/base.yml @@ -1,59 +1,108 @@ +variables: + # On stable branches this is changed by later rules. Should also + # be overridden per pipeline if running pipelines concurrently + # for different branches in contributor forks. + QEMU_CI_CONTAINER_TAG: latest + + # For purposes of CI rules, upstream is the gitlab.com/qemu-project + # namespace. When testing CI, it might be usefult to override this + # to point to a fork repo + QEMU_CI_UPSTREAM: qemu-project + # The order of rules defined here is critically important. # They are evaluated in order and first match wins. # # Thus we group them into a number of stages, ordered from # most restrictive to least restrictive # +# For pipelines running for stable "staging-X.Y" branches +# we must override QEMU_CI_CONTAINER_TAG +# .base_job_template: + variables: + # Each script line from will be in a collapsible section in the job output + # and show the duration of each line. + FF_SCRIPT_SECTIONS: 1 + # The project has a fairly fat GIT repo so we try and avoid bringing in things + # we don't need. The --filter options avoid blobs and tree references we aren't going to use + # and we also avoid fetching tags. + GIT_FETCH_EXTRA_FLAGS: --filter=blob:none --filter=tree:0 --no-tags --prune --quiet + + interruptible: true + rules: ############################################################# # Stage 1: exclude scenarios where we definitely don't # want jobs to run ############################################################# + # Never run jobs upstream on stable branch, staging branch jobs already ran + - if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /^stable-/' + when: never + + # Never run jobs upstream on tags, staging branch jobs already ran + - if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_TAG' + when: never + + # Scheduled runs on mainline don't get pipelines except for the special Coverity job + - if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_PIPELINE_SOURCE == "schedule"' + when: never + # Cirrus jobs can't run unless the creds / target repo are set - - if: '$QEMU_JOB_CIRRUS && ($CIRRUS_GITHUB_REPO == "" || $CIRRUS_API_TOKEN == "")' + - if: '$QEMU_JOB_CIRRUS && ($CIRRUS_GITHUB_REPO == null || $CIRRUS_API_TOKEN == null)' when: never # Publishing jobs should only run on the default branch in upstream - - if: '$QEMU_JOB_PUBLISH == "1" && $CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH' + - if: '$QEMU_JOB_PUBLISH == "1" && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH' when: never # Non-publishing jobs should only run on staging branches in upstream - - if: '$QEMU_JOB_PUBLISH != "1" && $CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH !~ /staging/' + - if: '$QEMU_JOB_PUBLISH != "1" && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH !~ /staging/' when: never # Jobs only intended for forks should always be skipped on upstream - - if: '$QEMU_JOB_ONLY_FORKS == "1" && $CI_PROJECT_NAMESPACE == "qemu-project"' + - if: '$QEMU_JOB_ONLY_FORKS == "1" && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM' when: never # Forks don't get pipelines unless QEMU_CI=1 or QEMU_CI=2 is set - - if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != "qemu-project"' + - if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM' when: never # Avocado jobs don't run in forks unless $QEMU_CI_AVOCADO_TESTING is set - - if: '$QEMU_JOB_AVOCADO && $QEMU_CI_AVOCADO_TESTING != "1" && $CI_PROJECT_NAMESPACE != "qemu-project"' + - if: '$QEMU_JOB_AVOCADO && $QEMU_CI_AVOCADO_TESTING != "1" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM' when: never ############################################################# # Stage 2: fine tune execution of jobs in specific scenarios - # where the catch all logic is inapprorpaite + # where the catch all logic is inappropriate ############################################################# # Optional jobs should not be run unless manually triggered + - if: '$QEMU_JOB_OPTIONAL && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/' + when: manual + allow_failure: true + variables: + QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG + - if: '$QEMU_JOB_OPTIONAL' when: manual allow_failure: true # Skipped jobs should not be run unless manually triggered + - if: '$QEMU_JOB_SKIPPED && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/' + when: manual + allow_failure: true + variables: + QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG + - if: '$QEMU_JOB_SKIPPED' when: manual allow_failure: true # Avocado jobs can be manually start in forks if $QEMU_CI_AVOCADO_TESTING is unset - - if: '$QEMU_JOB_AVOCADO && $CI_PROJECT_NAMESPACE != "qemu-project"' + - if: '$QEMU_JOB_AVOCADO && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM' when: manual allow_failure: true @@ -65,8 +114,23 @@ # Forks pipeline jobs don't start automatically unless # QEMU_CI=2 is set - - if: '$QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != "qemu-project"' + - if: '$QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM' when: manual - # Jobs can run if any jobs they depend on were successfull + # Upstream pipeline jobs start automatically unless told not to + # by setting QEMU_CI=1 + - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/' + when: manual + variables: + QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG + + - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM' + when: manual + + # Jobs can run if any jobs they depend on were successful + - if: '$QEMU_JOB_SKIPPED && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/' + when: on_success + variables: + QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG + - when: on_success diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 73ecfabb8d..22045add80 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -1,39 +1,62 @@ .native_build_job_template: extends: .base_job_template stage: build - image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest + image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG + cache: + paths: + - ccache + key: "$CI_JOB_NAME" + when: always before_script: - JOBS=$(expr $(nproc) + 1) script: - - if test -n "$LD_JOBS"; - then - scripts/git-submodule.sh update meson ; - fi + - export CCACHE_BASEDIR="$(pwd)" + - export CCACHE_DIR="$CCACHE_BASEDIR/ccache" + - export CCACHE_MAXSIZE="500M" + - export PATH="$CCACHE_WRAPPERSDIR:$PATH" + - du -sh .git - mkdir build - cd build - - if test -n "$TARGETS"; - then - ../configure --enable-werror --disable-docs ${LD_JOBS:+--meson=git} $CONFIGURE_ARGS --target-list="$TARGETS" ; - else - ../configure --enable-werror --disable-docs ${LD_JOBS:+--meson=git} $CONFIGURE_ARGS ; - fi || { cat config.log meson-logs/meson-log.txt && exit 1; } + - ccache --zero-stats + - ../configure --enable-werror --disable-docs --enable-fdt=system + ${TARGETS:+--target-list="$TARGETS"} + $CONFIGURE_ARGS || + { cat config.log meson-logs/meson-log.txt && exit 1; } - if test -n "$LD_JOBS"; then - ../meson/meson.py configure . -Dbackend_max_links="$LD_JOBS" ; + pyvenv/bin/meson configure . -Dbackend_max_links="$LD_JOBS" ; fi || exit 1; - make -j"$JOBS" - if test -n "$MAKE_CHECK_ARGS"; then make -j"$JOBS" $MAKE_CHECK_ARGS ; fi + - ccache --show-stats + +# We jump some hoops in common_test_job_template to avoid +# rebuilding all the object files we skip in the artifacts +.native_build_artifact_template: + artifacts: + when: on_success + expire_in: 2 days + paths: + - build + - .git-submodule-status + exclude: + - build/**/*.p + - build/**/*.a.p + - build/**/*.fa.p + - build/**/*.c.o + - build/**/*.c.o.d + - build/**/*.fa .common_test_job_template: extends: .base_job_template stage: test - image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest + image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG script: - - scripts/git-submodule.sh update - $(sed -n '/GIT_SUBMODULES=/ s/.*=// p' build/config-host.mak) + - scripts/git-submodule.sh update roms/SLOF + - meson subprojects download $(cd build/subprojects && echo *) - cd build - find . -type f -exec touch {} + # Avoid recompiling by hiding ninja with NINJA=":" @@ -43,6 +66,7 @@ extends: .common_test_job_template artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" + when: always expire_in: 7 days paths: - build/meson-logs/testlog.txt @@ -58,7 +82,7 @@ policy: pull-push artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" - when: on_failure + when: always expire_in: 7 days paths: - build/tests/results/latest/results.xml diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 544385f5be..cfdff175c3 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -2,20 +2,16 @@ include: - local: '/.gitlab-ci.d/buildtest-template.yml' build-system-alpine: - extends: .native_build_job_template + extends: + - .native_build_job_template + - .native_build_artifact_template needs: - job: amd64-alpine-container variables: IMAGE: alpine - TARGETS: aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu - microblazeel-softmmu mips64el-softmmu + TARGETS: avr-softmmu loongarch64-softmmu mips64-softmmu mipsel-softmmu MAKE_CHECK_ARGS: check-build CONFIGURE_ARGS: --enable-docs --enable-trace-backends=log,simple,syslog - artifacts: - expire_in: 2 days - paths: - - .git-submodule-status - - build check-system-alpine: extends: .native_test_job_template @@ -34,22 +30,19 @@ avocado-system-alpine: variables: IMAGE: alpine MAKE_CHECK_ARGS: check-avocado + AVOCADO_TAGS: arch:avr arch:loongarch64 arch:mips64 arch:mipsel build-system-ubuntu: - extends: .native_build_job_template + extends: + - .native_build_job_template + - .native_build_artifact_template needs: - job: amd64-ubuntu2004-container + job: amd64-ubuntu2204-container variables: - IMAGE: ubuntu2004 - CONFIGURE_ARGS: --enable-docs --enable-fdt=system --enable-slirp=system - --enable-capstone - TARGETS: aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu - microblazeel-softmmu mips64el-softmmu + IMAGE: ubuntu2204 + CONFIGURE_ARGS: --enable-docs + TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu MAKE_CHECK_ARGS: check-build - artifacts: - expire_in: 2 days - paths: - - build check-system-ubuntu: extends: .native_test_job_template @@ -57,7 +50,7 @@ check-system-ubuntu: - job: build-system-ubuntu artifacts: true variables: - IMAGE: ubuntu2004 + IMAGE: ubuntu2204 MAKE_CHECK_ARGS: check avocado-system-ubuntu: @@ -66,22 +59,22 @@ avocado-system-ubuntu: - job: build-system-ubuntu artifacts: true variables: - IMAGE: ubuntu2004 + IMAGE: ubuntu2204 MAKE_CHECK_ARGS: check-avocado + AVOCADO_TAGS: arch:alpha arch:microblazeel arch:mips64el build-system-debian: - extends: .native_build_job_template + extends: + - .native_build_job_template + - .native_build_artifact_template needs: job: amd64-debian-container variables: - IMAGE: debian-amd64 - TARGETS: arm-softmmu avr-softmmu i386-softmmu mipsel-softmmu - riscv64-softmmu sh4eb-softmmu sparc-softmmu xtensaeb-softmmu + IMAGE: debian + CONFIGURE_ARGS: --with-coroutine=sigaltstack + TARGETS: arm-softmmu i386-softmmu riscv64-softmmu sh4eb-softmmu + sparc-softmmu xtensa-softmmu MAKE_CHECK_ARGS: check-build - artifacts: - expire_in: 2 days - paths: - - build check-system-debian: extends: .native_test_job_template @@ -89,7 +82,7 @@ check-system-debian: - job: build-system-debian artifacts: true variables: - IMAGE: debian-amd64 + IMAGE: debian MAKE_CHECK_ARGS: check avocado-system-debian: @@ -98,8 +91,9 @@ avocado-system-debian: - job: build-system-debian artifacts: true variables: - IMAGE: debian-amd64 + IMAGE: debian MAKE_CHECK_ARGS: check-avocado + AVOCADO_TAGS: arch:arm arch:i386 arch:riscv64 arch:sh4 arch:sparc arch:xtensa crash-test-debian: extends: .native_test_job_template @@ -107,27 +101,24 @@ crash-test-debian: - job: build-system-debian artifacts: true variables: - IMAGE: debian-amd64 + IMAGE: debian script: - cd build - - make check-venv - - tests/venv/bin/python3 scripts/device-crash-test -q ./qemu-system-i386 + - make NINJA=":" check-venv + - pyvenv/bin/python3 scripts/device-crash-test -q --tcg-only ./qemu-system-i386 build-system-fedora: - extends: .native_build_job_template + extends: + - .native_build_job_template + - .native_build_artifact_template needs: job: amd64-fedora-container variables: IMAGE: fedora CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs - --enable-fdt=system --enable-slirp=system --enable-capstone - TARGETS: tricore-softmmu microblaze-softmmu mips-softmmu + TARGETS: microblaze-softmmu mips-softmmu xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu MAKE_CHECK_ARGS: check-build - artifacts: - expire_in: 2 days - paths: - - build check-system-fedora: extends: .native_test_job_template @@ -146,6 +137,8 @@ avocado-system-fedora: variables: IMAGE: fedora MAKE_CHECK_ARGS: check-avocado + AVOCADO_TAGS: arch:microblaze arch:mips arch:xtensa arch:m68k + arch:riscv32 arch:ppc arch:sparc64 crash-test-fedora: extends: .native_test_job_template @@ -156,25 +149,92 @@ crash-test-fedora: IMAGE: fedora script: - cd build - - make check-venv - - tests/venv/bin/python3 scripts/device-crash-test -q ./qemu-system-ppc - - tests/venv/bin/python3 scripts/device-crash-test -q ./qemu-system-riscv32 + - make NINJA=":" check-venv + - pyvenv/bin/python3 scripts/device-crash-test -q ./qemu-system-ppc + - pyvenv/bin/python3 scripts/device-crash-test -q ./qemu-system-riscv32 build-system-centos: - extends: .native_build_job_template + extends: + - .native_build_job_template + - .native_build_artifact_template needs: job: amd64-centos8-container variables: IMAGE: centos8 - CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-fdt=system + CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-vfio-user-server --enable-modules --enable-trace-backends=dtrace --enable-docs TARGETS: ppc64-softmmu or1k-softmmu s390x-softmmu x86_64-softmmu rx-softmmu sh4-softmmu nios2-softmmu MAKE_CHECK_ARGS: check-build + +# Previous QEMU release. Used for cross-version migration tests. +build-previous-qemu: + extends: .native_build_job_template artifacts: + when: on_success expire_in: 2 days paths: - - build + - build-previous + exclude: + - build-previous/**/*.p + - build-previous/**/*.a.p + - build-previous/**/*.fa.p + - build-previous/**/*.c.o + - build-previous/**/*.c.o.d + - build-previous/**/*.fa + needs: + job: amd64-opensuse-leap-container + variables: + IMAGE: opensuse-leap + TARGETS: x86_64-softmmu aarch64-softmmu + # Override the default flags as we need more to grab the old version + GIT_FETCH_EXTRA_FLAGS: --prune --quiet + before_script: + - export QEMU_PREV_VERSION="$(sed 's/\([0-9.]*\)\.[0-9]*/v\1.0/' VERSION)" + - git remote add upstream https://gitlab.com/qemu-project/qemu + - git fetch upstream refs/tags/$QEMU_PREV_VERSION:refs/tags/$QEMU_PREV_VERSION + - git checkout $QEMU_PREV_VERSION + after_script: + - mv build build-previous + +.migration-compat-common: + extends: .common_test_job_template + needs: + - job: build-previous-qemu + - job: build-system-opensuse + # The old QEMU could have bugs unrelated to migration that are + # already fixed in the current development branch, so this test + # might fail. + allow_failure: true + variables: + IMAGE: opensuse-leap + MAKE_CHECK_ARGS: check-build + script: + # Use the migration-tests from the older QEMU tree. This avoids + # testing an old QEMU against new features/tests that it is not + # compatible with. + - cd build-previous + # old to new + - QTEST_QEMU_BINARY_SRC=./qemu-system-${TARGET} + QTEST_QEMU_BINARY=../build/qemu-system-${TARGET} ./tests/qtest/migration-test + # new to old + - QTEST_QEMU_BINARY_DST=./qemu-system-${TARGET} + QTEST_QEMU_BINARY=../build/qemu-system-${TARGET} ./tests/qtest/migration-test + +# This job needs to be disabled until we can have an aarch64 CPU model that +# will both (1) support both KVM and TCG, and (2) provide a stable ABI. +# Currently only "-cpu max" can provide (1), however it doesn't guarantee +# (2). Mark this test skipped until later. +migration-compat-aarch64: + extends: .migration-compat-common + variables: + TARGET: aarch64 + QEMU_JOB_SKIPPED: 1 + +migration-compat-x86_64: + extends: .migration-compat-common + variables: + TARGET: x86_64 check-system-centos: extends: .native_test_job_template @@ -193,20 +253,19 @@ avocado-system-centos: variables: IMAGE: centos8 MAKE_CHECK_ARGS: check-avocado + AVOCADO_TAGS: arch:ppc64 arch:or1k arch:s390x arch:x86_64 arch:rx + arch:sh4 arch:nios2 build-system-opensuse: - extends: .native_build_job_template + extends: + - .native_build_job_template + - .native_build_artifact_template needs: job: amd64-opensuse-leap-container variables: IMAGE: opensuse-leap - CONFIGURE_ARGS: --enable-fdt=system TARGETS: s390x-softmmu x86_64-softmmu aarch64-softmmu MAKE_CHECK_ARGS: check-build - artifacts: - expire_in: 2 days - paths: - - build check-system-opensuse: extends: .native_test_job_template @@ -225,7 +284,38 @@ avocado-system-opensuse: variables: IMAGE: opensuse-leap MAKE_CHECK_ARGS: check-avocado + AVOCADO_TAGS: arch:s390x arch:x86_64 arch:aarch64 +# +# Flaky tests. We don't run these by default and they are allow fail +# but often the CI system is the only way to trigger the failures. +# + +build-system-flaky: + extends: + - .native_build_job_template + - .native_build_artifact_template + needs: + job: amd64-debian-container + variables: + IMAGE: debian + QEMU_JOB_OPTIONAL: 1 + TARGETS: aarch64-softmmu arm-softmmu mips64el-softmmu + ppc64-softmmu rx-softmmu s390x-softmmu sh4-softmmu x86_64-softmmu + MAKE_CHECK_ARGS: check-build + +avocado-system-flaky: + extends: .avocado_test_job_template + needs: + - job: build-system-flaky + artifacts: true + allow_failure: true + variables: + IMAGE: debian + MAKE_CHECK_ARGS: check-avocado + QEMU_JOB_OPTIONAL: 1 + QEMU_TEST_FLAKY_TESTS: 1 + AVOCADO_TAGS: flaky # This jobs explicitly disable TCG (--disable-tcg), KVM is detected by # the configure script. The container doesn't contain Xen headers so @@ -244,6 +334,7 @@ build-tcg-disabled: - mkdir build - cd build - ../configure --disable-tcg --audio-drv-list="" --with-coroutine=ucontext + --disable-docs --disable-sdl --disable-gtk --disable-vnc || { cat config.log meson-logs/meson-log.txt && exit 1; } - make -j"$JOBS" - make check-unit @@ -264,6 +355,7 @@ build-user: variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --disable-system + --target-list-exclude=alpha-linux-user,sh4-linux-user MAKE_CHECK_ARGS: check-tcg build-user-static: @@ -273,23 +365,33 @@ build-user-static: variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --disable-system --static + --target-list-exclude=alpha-linux-user,sh4-linux-user + MAKE_CHECK_ARGS: check-tcg + +# targets stuck on older compilers +build-legacy: + extends: .native_build_job_template + needs: + job: amd64-debian-legacy-cross-container + variables: + IMAGE: debian-legacy-test-cross + TARGETS: alpha-linux-user alpha-softmmu sh4-linux-user + CONFIGURE_ARGS: --disable-tools MAKE_CHECK_ARGS: check-tcg -# Because the hexagon cross-compiler takes so long to build we don't rely -# on the CI system to build it and hence this job has an optional dependency -# declared. The image is manually uploaded. build-user-hexagon: extends: .native_build_job_template needs: job: hexagon-cross-container - optional: true variables: IMAGE: debian-hexagon-cross TARGETS: hexagon-linux-user CONFIGURE_ARGS: --disable-tools --disable-docs --enable-debug-tcg MAKE_CHECK_ARGS: check-tcg -# Only build the softmmu targets we have check-tcg tests for +# Build the softmmu targets we have check-tcg tests and compilers in +# our omnibus all-test-cross container. Those targets that haven't got +# Debian cross compiler support need to use special containers. build-some-softmmu: extends: .native_build_job_template needs: @@ -297,7 +399,18 @@ build-some-softmmu: variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --enable-debug - TARGETS: xtensa-softmmu arm-softmmu aarch64-softmmu alpha-softmmu + TARGETS: arm-softmmu aarch64-softmmu i386-softmmu riscv64-softmmu + s390x-softmmu x86_64-softmmu + MAKE_CHECK_ARGS: check-tcg + +build-loongarch64: + extends: .native_build_job_template + needs: + job: loongarch-debian-cross-container + variables: + IMAGE: debian-loongarch-cross + CONFIGURE_ARGS: --disable-tools --enable-debug + TARGETS: loongarch64-linux-user loongarch64-softmmu MAKE_CHECK_ARGS: check-tcg # We build tricore in a very minimal tricore only container @@ -319,18 +432,18 @@ clang-system: IMAGE: fedora CONFIGURE_ARGS: --cc=clang --cxx=clang++ --extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined - TARGETS: alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu - ppc-softmmu s390x-softmmu + TARGETS: alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu s390x-softmmu MAKE_CHECK_ARGS: check-qtest check-tcg clang-user: extends: .native_build_job_template needs: job: amd64-debian-user-cross-container + timeout: 70m variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --cc=clang --cxx=clang++ --disable-system - --target-list-exclude=microblazeel-linux-user,aarch64_be-linux-user,i386-linux-user,m68k-linux-user,mipsn32el-linux-user,xtensaeb-linux-user + --target-list-exclude=alpha-linux-user,microblazeel-linux-user,aarch64_be-linux-user,i386-linux-user,m68k-linux-user,mipsn32el-linux-user,xtensaeb-linux-user --extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined MAKE_CHECK_ARGS: check-unit check-tcg @@ -338,15 +451,15 @@ clang-user: # On gitlab runners, default value sometimes end up calling 2 lds concurrently and # triggers an Out-Of-Memory error # -# Since slirp callbacks are used in QEMU Timers, slirp needs to be compiled together -# with QEMU and linked as a static library to avoid false positives in CFI checks. -# This can be accomplished by using -enable-slirp=git, which avoids the use of -# a system-wide version of the library +# Since slirp callbacks are used in QEMU Timers, we cannot use libslirp with +# CFI builds, and thus have to disable it here. # # Split in three sets of build/check/avocado to limit the execution time of each # job build-cfi-aarch64: - extends: .native_build_job_template + extends: + - .native_build_job_template + - .native_build_artifact_template needs: - job: amd64-fedora-container variables: @@ -354,19 +467,14 @@ build-cfi-aarch64: AR: llvm-ar IMAGE: fedora CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug - --enable-safe-stack --enable-slirp=git + --enable-safe-stack --disable-slirp TARGETS: aarch64-softmmu MAKE_CHECK_ARGS: check-build - timeout: 70m - artifacts: - expire_in: 2 days - paths: - - build - variables: # FIXME: This job is often failing, likely due to out-of-memory problems in # the constrained containers of the shared runners. Thus this is marked as # skipped until the situation has been solved. QEMU_JOB_SKIPPED: 1 + timeout: 90m check-cfi-aarch64: extends: .native_test_job_template @@ -387,7 +495,9 @@ avocado-cfi-aarch64: MAKE_CHECK_ARGS: check-avocado build-cfi-ppc64-s390x: - extends: .native_build_job_template + extends: + - .native_build_job_template + - .native_build_artifact_template needs: - job: amd64-fedora-container variables: @@ -395,19 +505,14 @@ build-cfi-ppc64-s390x: AR: llvm-ar IMAGE: fedora CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug - --enable-safe-stack --enable-slirp=git + --enable-safe-stack --disable-slirp TARGETS: ppc64-softmmu s390x-softmmu MAKE_CHECK_ARGS: check-build - timeout: 70m - artifacts: - expire_in: 2 days - paths: - - build - variables: # FIXME: This job is often failing, likely due to out-of-memory problems in # the constrained containers of the shared runners. Thus this is marked as # skipped until the situation has been solved. QEMU_JOB_SKIPPED: 1 + timeout: 80m check-cfi-ppc64-s390x: extends: .native_test_job_template @@ -428,7 +533,9 @@ avocado-cfi-ppc64-s390x: MAKE_CHECK_ARGS: check-avocado build-cfi-x86_64: - extends: .native_build_job_template + extends: + - .native_build_job_template + - .native_build_artifact_template needs: - job: amd64-fedora-container variables: @@ -436,14 +543,10 @@ build-cfi-x86_64: AR: llvm-ar IMAGE: fedora CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug - --enable-safe-stack --enable-slirp=git + --enable-safe-stack --disable-slirp TARGETS: x86_64-softmmu MAKE_CHECK_ARGS: check-build timeout: 70m - artifacts: - expire_in: 2 days - paths: - - build check-cfi-x86_64: extends: .native_test_job_template @@ -466,38 +569,40 @@ avocado-cfi-x86_64: tsan-build: extends: .native_build_job_template needs: - job: amd64-ubuntu2004-container + job: amd64-ubuntu2204-container variables: - IMAGE: ubuntu2004 - CONFIGURE_ARGS: --enable-tsan --cc=clang-10 --cxx=clang++-10 - --enable-trace-backends=ust --enable-fdt=system --enable-slirp=system + IMAGE: ubuntu2204 + CONFIGURE_ARGS: --enable-tsan --cc=clang --cxx=clang++ + --enable-trace-backends=ust --disable-slirp TARGETS: x86_64-softmmu ppc64-softmmu riscv64-softmmu x86_64-linux-user - MAKE_CHECK_ARGS: bench V=1 -# gprof/gcov are GCC features -build-gprof-gcov: +# gcov is a GCC features +gcov: extends: .native_build_job_template needs: - job: amd64-ubuntu2004-container + job: amd64-ubuntu2204-container + timeout: 80m variables: - IMAGE: ubuntu2004 - CONFIGURE_ARGS: --enable-gprof --enable-gcov + IMAGE: ubuntu2204 + CONFIGURE_ARGS: --enable-gcov TARGETS: aarch64-softmmu ppc64-softmmu s390x-softmmu x86_64-softmmu - artifacts: - expire_in: 1 days - paths: - - build - -check-gprof-gcov: - extends: .native_test_job_template - needs: - - job: build-gprof-gcov - artifacts: true - variables: - IMAGE: ubuntu2004 - MAKE_CHECK_ARGS: check + MAKE_CHECK_ARGS: check-unit check-softfloat after_script: - - ${CI_PROJECT_DIR}/scripts/ci/coverage-summary.sh + - cd build + - gcovr --xml-pretty --exclude-unreachable-branches --print-summary + -o coverage.xml --root ${CI_PROJECT_DIR} . *.p + coverage: /^\s*lines:\s*\d+.\d+\%/ + artifacts: + name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA} + when: always + expire_in: 2 days + paths: + - build/meson-logs/testlog.txt + reports: + junit: build/meson-logs/testlog.junit.xml + coverage_report: + coverage_format: cobertura + path: build/coverage.xml build-oss-fuzz: extends: .native_build_job_template @@ -507,6 +612,7 @@ build-oss-fuzz: IMAGE: fedora script: - mkdir build-oss-fuzz + - export LSAN_OPTIONS=suppressions=scripts/oss-fuzz/lsan_suppressions.txt - CC="clang" CXX="clang++" CFLAGS="-fsanitize=address" ./scripts/oss-fuzz/build.sh - export ASAN_OPTIONS="fast_unwind_on_malloc=0" @@ -524,11 +630,12 @@ build-tci: variables: IMAGE: debian-all-test-cross script: - - TARGETS="aarch64 alpha arm hppa m68k microblaze ppc64 s390x x86_64" + - TARGETS="aarch64 arm hppa m68k microblaze ppc64 s390x x86_64" - mkdir build - cd build - - ../configure --enable-tcg-interpreter - --target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)" || { cat config.log meson-logs/meson-log.txt && exit 1; } + - ../configure --enable-tcg-interpreter --disable-docs --disable-gtk --disable-vnc + --target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)" + || { cat config.log meson-logs/meson-log.txt && exit 1; } - make -j"$JOBS" - make tests/qtest/boot-serial-test tests/qtest/cdrom-test tests/qtest/pxe-test - for tg in $TARGETS ; do @@ -540,48 +647,28 @@ build-tci: - QTEST_QEMU_BINARY="./qemu-system-s390x" ./tests/qtest/pxe-test -m slow - make check-tcg -# Alternate coroutines implementations are only really of interest to KVM users -# However we can't test against KVM on Gitlab-CI so we can only run unit tests -build-coroutine-sigaltstack: - extends: .native_build_job_template - needs: - job: amd64-ubuntu2004-container - variables: - IMAGE: ubuntu2004 - CONFIGURE_ARGS: --with-coroutine=sigaltstack --disable-tcg - --enable-trace-backends=ftrace - MAKE_CHECK_ARGS: check-unit - # Check our reduced build configurations -build-without-default-devices: +build-without-defaults: extends: .native_build_job_template needs: job: amd64-centos8-container variables: IMAGE: centos8 - CONFIGURE_ARGS: --without-default-devices --disable-user - -build-without-default-features: - extends: .native_build_job_template - needs: - job: amd64-fedora-container - variables: - IMAGE: fedora CONFIGURE_ARGS: + --without-default-devices --without-default-features - --disable-capstone + --disable-fdt --disable-pie --disable-qom-cast-debug - --disable-slirp --disable-strip - TARGETS: avr-softmmu i386-softmmu mips64-softmmu s390x-softmmu sh4-softmmu + TARGETS: avr-softmmu s390x-softmmu sh4-softmmu sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user - MAKE_CHECK_ARGS: check-unit check-qtest SPEED=slow + MAKE_CHECK_ARGS: check build-libvhost-user: extends: .base_job_template stage: build - image: $CI_REGISTRY_IMAGE/qemu/fedora:latest + image: $CI_REGISTRY_IMAGE/qemu/fedora:$QEMU_CI_CONTAINER_TAG needs: job: amd64-fedora-container script: @@ -593,20 +680,18 @@ build-libvhost-user: # No targets are built here, just tools, docs, and unit tests. This # also feeds into the eventual documentation deployment steps later build-tools-and-docs-debian: - extends: .native_build_job_template + extends: + - .native_build_job_template + - .native_build_artifact_template needs: job: amd64-debian-container # when running on 'master' we use pre-existing container optional: true variables: - IMAGE: debian-amd64 - MAKE_CHECK_ARGS: check-unit check-softfloat ctags TAGS cscope + IMAGE: debian + MAKE_CHECK_ARGS: check-unit ctags TAGS cscope CONFIGURE_ARGS: --disable-system --disable-user --enable-docs --enable-tools QEMU_JOB_PUBLISH: 1 - artifacts: - expire_in: 2 days - paths: - - build # Prepare for GitLab pages deployment. Anything copied into the # "public" directory will be deployed to $USER.gitlab.io/$PROJECT @@ -623,7 +708,7 @@ build-tools-and-docs-debian: # of what topic branch they're currently using pages: extends: .base_job_template - image: $CI_REGISTRY_IMAGE/qemu/debian-amd64:latest + image: $CI_REGISTRY_IMAGE/qemu/debian:$QEMU_CI_CONTAINER_TAG stage: test needs: - job: build-tools-and-docs-debian @@ -631,14 +716,55 @@ pages: - mkdir -p public # HTML-ised source tree - make gtags - - htags -anT --tree-view=filetree -m qemu_init + # We unset variables to work around a bug in some htags versions + # which causes it to fail when the environment is large + - CI_COMMIT_MESSAGE= CI_COMMIT_TAG_MESSAGE= htags + -anT --tree-view=filetree -m qemu_init -t "Welcome to the QEMU sourcecode" - mv HTML public/src # Project documentation - make -C build install DESTDIR=$(pwd)/temp-install - mv temp-install/usr/local/share/doc/qemu/* public/ artifacts: + when: on_success paths: - public variables: QEMU_JOB_PUBLISH: 1 + +coverity: + image: $CI_REGISTRY_IMAGE/qemu/fedora:$QEMU_CI_CONTAINER_TAG + stage: build + allow_failure: true + timeout: 3h + needs: + - job: amd64-fedora-container + optional: true + before_script: + - dnf install -y curl wget + script: + # would be nice to cancel the job if over quota (https://gitlab.com/gitlab-org/gitlab/-/issues/256089) + # for example: + # curl --request POST --header "PRIVATE-TOKEN: $CI_JOB_TOKEN" "${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/jobs/${CI_JOB_ID}/cancel + - 'scripts/coverity-scan/run-coverity-scan --check-upload-only || { exitcode=$?; if test $exitcode = 1; then + exit 0; + else + exit $exitcode; + fi; }; + scripts/coverity-scan/run-coverity-scan --update-tools-only > update-tools.log 2>&1 || { cat update-tools.log; exit 1; }; + scripts/coverity-scan/run-coverity-scan --no-update-tools' + rules: + - if: '$COVERITY_TOKEN == null' + when: never + - if: '$COVERITY_EMAIL == null' + when: never + # Never included on upstream pipelines, except for schedules + - if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_PIPELINE_SOURCE == "schedule"' + when: on_success + - if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM' + when: never + # Forks don't get any pipeline unless QEMU_CI=1 or QEMU_CI=2 is set + - if: '$QEMU_CI != "1" && $QEMU_CI != "2"' + when: never + # Always manual on forks even if $QEMU_CI == "2" + - when: manual diff --git a/.gitlab-ci.d/cirrus.yml b/.gitlab-ci.d/cirrus.yml index 609c364308..4671f069c3 100644 --- a/.gitlab-ci.d/cirrus.yml +++ b/.gitlab-ci.d/cirrus.yml @@ -13,10 +13,12 @@ .cirrus_build_job: extends: .base_job_template stage: build - image: registry.gitlab.com/libvirt/libvirt-ci/cirrus-run:master + image: registry.gitlab.com/libvirt/libvirt-ci/cirrus-run:latest needs: [] + # 20 mins larger than "timeout_in" in cirrus/build.yml + # as there's often a 5-10 minute delay before Cirrus CI + # actually starts the task timeout: 80m - allow_failure: true script: - source .gitlab-ci.d/cirrus/$NAME.vars - sed -e "s|[@]CI_REPOSITORY_URL@|$CI_REPOSITORY_URL|g" @@ -44,47 +46,50 @@ variables: QEMU_JOB_CIRRUS: 1 -x64-freebsd-12-build: - extends: .cirrus_build_job - variables: - NAME: freebsd-12 - CIRRUS_VM_INSTANCE_TYPE: freebsd_instance - CIRRUS_VM_IMAGE_SELECTOR: image_family - CIRRUS_VM_IMAGE_NAME: freebsd-12-3 - CIRRUS_VM_CPUS: 8 - CIRRUS_VM_RAM: 8G - UPDATE_COMMAND: pkg update - INSTALL_COMMAND: pkg install -y - TEST_TARGETS: check - x64-freebsd-13-build: extends: .cirrus_build_job variables: NAME: freebsd-13 CIRRUS_VM_INSTANCE_TYPE: freebsd_instance CIRRUS_VM_IMAGE_SELECTOR: image_family - CIRRUS_VM_IMAGE_NAME: freebsd-13-0 + CIRRUS_VM_IMAGE_NAME: freebsd-13-3 CIRRUS_VM_CPUS: 8 CIRRUS_VM_RAM: 8G - UPDATE_COMMAND: pkg update + UPDATE_COMMAND: pkg update; pkg upgrade -y INSTALL_COMMAND: pkg install -y TEST_TARGETS: check -x64-macos-11-base-build: +aarch64-macos-13-base-build: extends: .cirrus_build_job variables: - NAME: macos-11 - CIRRUS_VM_INSTANCE_TYPE: osx_instance + NAME: macos-13 + CIRRUS_VM_INSTANCE_TYPE: macos_instance CIRRUS_VM_IMAGE_SELECTOR: image - CIRRUS_VM_IMAGE_NAME: big-sur-base + CIRRUS_VM_IMAGE_NAME: ghcr.io/cirruslabs/macos-ventura-base:latest CIRRUS_VM_CPUS: 12 CIRRUS_VM_RAM: 24G UPDATE_COMMAND: brew update INSTALL_COMMAND: brew install - PATH_EXTRA: /usr/local/opt/ccache/libexec:/usr/local/opt/gettext/bin - PKG_CONFIG_PATH: /usr/local/opt/curl/lib/pkgconfig:/usr/local/opt/ncurses/lib/pkgconfig:/usr/local/opt/readline/lib/pkgconfig + PATH_EXTRA: /opt/homebrew/ccache/libexec:/opt/homebrew/gettext/bin + PKG_CONFIG_PATH: /opt/homebrew/curl/lib/pkgconfig:/opt/homebrew/ncurses/lib/pkgconfig:/opt/homebrew/readline/lib/pkgconfig TEST_TARGETS: check-unit check-block check-qapi-schema check-softfloat check-qtest-x86_64 +aarch64-macos-14-base-build: + extends: .cirrus_build_job + variables: + NAME: macos-14 + CIRRUS_VM_INSTANCE_TYPE: macos_instance + CIRRUS_VM_IMAGE_SELECTOR: image + CIRRUS_VM_IMAGE_NAME: ghcr.io/cirruslabs/macos-sonoma-base:latest + CIRRUS_VM_CPUS: 12 + CIRRUS_VM_RAM: 24G + UPDATE_COMMAND: brew update + INSTALL_COMMAND: brew install + PATH_EXTRA: /opt/homebrew/ccache/libexec:/opt/homebrew/gettext/bin + PKG_CONFIG_PATH: /opt/homebrew/curl/lib/pkgconfig:/opt/homebrew/ncurses/lib/pkgconfig:/opt/homebrew/readline/lib/pkgconfig + TEST_TARGETS: check-unit check-block check-qapi-schema check-softfloat check-qtest-x86_64 + QEMU_JOB_OPTIONAL: 1 + # The following jobs run VM-based tests via KVM on a Linux-based Cirrus-CI job .cirrus_kvm_job: diff --git a/.gitlab-ci.d/cirrus/build.yml b/.gitlab-ci.d/cirrus/build.yml index c555f5d36e..43dd52dd19 100644 --- a/.gitlab-ci.d/cirrus/build.yml +++ b/.gitlab-ci.d/cirrus/build.yml @@ -16,10 +16,12 @@ env: TEST_TARGETS: "@TEST_TARGETS@" build_task: + # A little shorter than GitLab timeout in ../cirrus.yml + timeout_in: 60m install_script: - @UPDATE_COMMAND@ - @INSTALL_COMMAND@ @PKGS@ - - if test -n "@PYPI_PKGS@" ; then @PIP3@ install @PYPI_PKGS@ ; fi + - if test -n "@PYPI_PKGS@" ; then PYLIB=$(@PYTHON@ -c 'import sysconfig; print(sysconfig.get_path("stdlib"))'); rm -f $PYLIB/EXTERNALLY-MANAGED; @PIP3@ install @PYPI_PKGS@ ; fi clone_script: - git clone --depth 100 "$CI_REPOSITORY_URL" . - git fetch origin "$CI_COMMIT_REF_NAME" @@ -34,3 +36,7 @@ build_task: do $MAKE -j$(sysctl -n hw.ncpu) $TARGET V=1 ; done + always: + build_result_artifacts: + path: build/meson-logs/*log.txt + type: text/plain diff --git a/.gitlab-ci.d/cirrus/freebsd-12.vars b/.gitlab-ci.d/cirrus/freebsd-12.vars deleted file mode 100644 index b4842271b2..0000000000 --- a/.gitlab-ci.d/cirrus/freebsd-12.vars +++ /dev/null @@ -1,16 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool variables freebsd-12 qemu -# -# https://gitlab.com/libvirt/libvirt-ci - -CCACHE='/usr/local/bin/ccache' -CPAN_PKGS='' -CROSS_PKGS='' -MAKE='/usr/local/bin/gmake' -NINJA='/usr/local/bin/ninja' -PACKAGING_COMMAND='pkg' -PIP3='/usr/local/bin/pip-3.8' -PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' -PYPI_PKGS='' -PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/cirrus/freebsd-13.vars b/.gitlab-ci.d/cirrus/freebsd-13.vars index 546a82dd75..3785afca36 100644 --- a/.gitlab-ci.d/cirrus/freebsd-13.vars +++ b/.gitlab-ci.d/cirrus/freebsd-13.vars @@ -11,6 +11,6 @@ MAKE='/usr/local/bin/gmake' NINJA='/usr/local/bin/ninja' PACKAGING_COMMAND='pkg' PIP3='/usr/local/bin/pip-3.8' -PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' +PKGS='alsa-lib bash bison bzip2 ca_root_nss capstone4 ccache cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-tomli py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 xorriso zstd' PYPI_PKGS='' PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/cirrus/kvm-build.yml b/.gitlab-ci.d/cirrus/kvm-build.yml index 4334fabf39..a93881aa8b 100644 --- a/.gitlab-ci.d/cirrus/kvm-build.yml +++ b/.gitlab-ci.d/cirrus/kvm-build.yml @@ -15,7 +15,7 @@ env: folder: $HOME/.cache/qemu-vm install_script: - dnf update -y - - dnf install -y git make openssh-clients qemu-img qemu-system-x86 wget + - dnf install -y git make openssh-clients qemu-img qemu-system-x86 wget meson clone_script: - git clone --depth 100 "$CI_REPOSITORY_URL" . - git fetch origin "$CI_COMMIT_REF_NAME" diff --git a/.gitlab-ci.d/cirrus/macos-11.vars b/.gitlab-ci.d/cirrus/macos-11.vars deleted file mode 100644 index cfe9181fd4..0000000000 --- a/.gitlab-ci.d/cirrus/macos-11.vars +++ /dev/null @@ -1,16 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool variables macos-11 qemu -# -# https://gitlab.com/libvirt/libvirt-ci - -CCACHE='/usr/local/bin/ccache' -CPAN_PKGS='' -CROSS_PKGS='' -MAKE='/usr/local/bin/gmake' -NINJA='/usr/local/bin/ninja' -PACKAGING_COMMAND='brew' -PIP3='/usr/local/bin/pip3' -PKGS='bash bc bzip2 capstone ccache ctags curl dbus diffutils dtc gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson ncurses nettle ninja perl pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy sparse spice-protocol tesseract texinfo usbredir vde vte3 zlib zstd' -PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme virtualenv' -PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/cirrus/macos-13.vars b/.gitlab-ci.d/cirrus/macos-13.vars new file mode 100644 index 0000000000..534f029956 --- /dev/null +++ b/.gitlab-ci.d/cirrus/macos-13.vars @@ -0,0 +1,16 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool variables macos-13 qemu +# +# https://gitlab.com/libvirt/libvirt-ci + +CCACHE='/opt/homebrew/bin/ccache' +CPAN_PKGS='' +CROSS_PKGS='' +MAKE='/opt/homebrew/bin/gmake' +NINJA='/opt/homebrew/bin/ninja' +PACKAGING_COMMAND='brew' +PIP3='/opt/homebrew/bin/pip3' +PKGS='bash bc bison bzip2 capstone ccache cmocka ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo json-c libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy socat sparse spice-protocol swtpm tesseract usbredir vde vte3 xorriso zlib zstd' +PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli' +PYTHON='/opt/homebrew/bin/python3' diff --git a/.gitlab-ci.d/cirrus/macos-14.vars b/.gitlab-ci.d/cirrus/macos-14.vars new file mode 100644 index 0000000000..43070f4a26 --- /dev/null +++ b/.gitlab-ci.d/cirrus/macos-14.vars @@ -0,0 +1,16 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool variables macos-14 qemu +# +# https://gitlab.com/libvirt/libvirt-ci + +CCACHE='/opt/homebrew/bin/ccache' +CPAN_PKGS='' +CROSS_PKGS='' +MAKE='/opt/homebrew/bin/gmake' +NINJA='/opt/homebrew/bin/ninja' +PACKAGING_COMMAND='brew' +PIP3='/opt/homebrew/bin/pip3' +PKGS='bash bc bison bzip2 capstone ccache cmocka ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo json-c libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy socat sparse spice-protocol swtpm tesseract usbredir vde vte3 xorriso zlib zstd' +PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli' +PYTHON='/opt/homebrew/bin/python3' diff --git a/.gitlab-ci.d/container-core.yml b/.gitlab-ci.d/container-core.yml index e8dd1f476a..08f8450fa1 100644 --- a/.gitlab-ci.d/container-core.yml +++ b/.gitlab-ci.d/container-core.yml @@ -10,8 +10,3 @@ amd64-fedora-container: extends: .container_job_template variables: NAME: fedora - -amd64-debian10-container: - extends: .container_job_template - variables: - NAME: debian10 diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index b7963498a3..e3103940a0 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -1,24 +1,21 @@ -alpha-debian-cross-container: - extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian10-container'] - variables: - NAME: debian-alpha-cross - amd64-debian-cross-container: extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian10-container'] + stage: containers variables: NAME: debian-amd64-cross amd64-debian-user-cross-container: extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian10-container'] + stage: containers variables: NAME: debian-all-test-cross +amd64-debian-legacy-cross-container: + extends: .container_job_template + stage: containers + variables: + NAME: debian-legacy-test-cross + arm64-debian-cross-container: extends: .container_job_template stage: containers @@ -37,52 +34,23 @@ armhf-debian-cross-container: variables: NAME: debian-armhf-cross -# We never want to build hexagon in the CI system and by default we -# always want to refer to the master registry where it lives. hexagon-cross-container: - extends: .base_job_template - image: docker:stable + extends: .container_job_template stage: containers variables: NAME: debian-hexagon-cross - GIT_DEPTH: 1 - QEMU_JOB_ONLY_FORKS: 1 - services: - - docker:dind - before_script: - - export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:latest" - - export COMMON_TAG="$CI_REGISTRY/qemu-project/qemu/qemu/$NAME:latest" - - docker info - - docker login $CI_REGISTRY -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" - script: - - echo "TAG:$TAG" - - echo "COMMON_TAG:$COMMON_TAG" - - docker pull $COMMON_TAG - - docker tag $COMMON_TAG $TAG - - docker push "$TAG" - after_script: - - docker logout -hppa-debian-cross-container: +loongarch-debian-cross-container: extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian10-container'] + stage: containers variables: - NAME: debian-hppa-cross + NAME: debian-loongarch-cross -m68k-debian-cross-container: +i686-debian-cross-container: extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian10-container'] + stage: containers variables: - NAME: debian-m68k-cross - -mips64-debian-cross-container: - extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian10-container'] - variables: - NAME: debian-mips64-cross + NAME: debian-i686-cross mips64el-debian-cross-container: extends: .container_job_template @@ -90,26 +58,12 @@ mips64el-debian-cross-container: variables: NAME: debian-mips64el-cross -mips-debian-cross-container: - extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian10-container'] - variables: - NAME: debian-mips-cross - mipsel-debian-cross-container: extends: .container_job_template stage: containers variables: NAME: debian-mipsel-cross -powerpc-test-cross-container: - extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian11-container'] - variables: - NAME: debian-powerpc-test-cross - ppc64el-debian-cross-container: extends: .container_job_template stage: containers @@ -123,14 +77,7 @@ riscv64-debian-cross-container: allow_failure: true variables: NAME: debian-riscv64-cross - -# we can however build TCG tests using a non-sid base -riscv64-debian-test-cross-container: - extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian11-container'] - variables: - NAME: debian-riscv64-test-cross + QEMU_JOB_OPTIONAL: 1 s390x-debian-cross-container: extends: .container_job_template @@ -138,24 +85,9 @@ s390x-debian-cross-container: variables: NAME: debian-s390x-cross -sh4-debian-cross-container: - extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian10-container'] - variables: - NAME: debian-sh4-cross - -sparc64-debian-cross-container: - extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian10-container'] - variables: - NAME: debian-sparc64-cross - tricore-debian-cross-container: extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian10-container'] + stage: containers variables: NAME: debian-tricore-cross @@ -169,16 +101,6 @@ cris-fedora-cross-container: variables: NAME: fedora-cris-cross -i386-fedora-cross-container: - extends: .container_job_template - variables: - NAME: fedora-i386-cross - -win32-fedora-cross-container: - extends: .container_job_template - variables: - NAME: fedora-win32-cross - win64-fedora-cross-container: extends: .container_job_template variables: diff --git a/.gitlab-ci.d/container-template.yml b/.gitlab-ci.d/container-template.yml index c434b9c8f3..4eec72f383 100644 --- a/.gitlab-ci.d/container-template.yml +++ b/.gitlab-ci.d/container-template.yml @@ -1,22 +1,21 @@ .container_job_template: extends: .base_job_template - image: docker:stable + image: docker:latest stage: containers services: - docker:dind before_script: - - export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:latest" - - export COMMON_TAG="$CI_REGISTRY/qemu-project/qemu/$NAME:latest" - - apk add python3 - - docker info + - export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:$QEMU_CI_CONTAINER_TAG" + # Always ':latest' because we always use upstream as a common cache source + - export COMMON_TAG="$CI_REGISTRY/qemu-project/qemu/qemu/$NAME:latest" - docker login $CI_REGISTRY -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" + - until docker info; do sleep 1; done script: - echo "TAG:$TAG" - echo "COMMON_TAG:$COMMON_TAG" - - ./tests/docker/docker.py --engine docker build - -t "qemu/$NAME" -f "tests/docker/dockerfiles/$NAME.docker" - -r $CI_REGISTRY/qemu-project/qemu - - docker tag "qemu/$NAME" "$TAG" + - docker build --tag "$TAG" --cache-from "$TAG" --cache-from "$COMMON_TAG" + --build-arg BUILDKIT_INLINE_CACHE=1 + -f "tests/docker/dockerfiles/$NAME.docker" "." - docker push "$TAG" after_script: - docker logout diff --git a/.gitlab-ci.d/containers.yml b/.gitlab-ci.d/containers.yml index be34cbc7ba..ae79d4c58b 100644 --- a/.gitlab-ci.d/containers.yml +++ b/.gitlab-ci.d/containers.yml @@ -7,21 +7,16 @@ amd64-alpine-container: variables: NAME: alpine -amd64-debian11-container: - extends: .container_job_template - variables: - NAME: debian11 - amd64-debian-container: extends: .container_job_template stage: containers variables: - NAME: debian-amd64 + NAME: debian -amd64-ubuntu2004-container: +amd64-ubuntu2204-container: extends: .container_job_template variables: - NAME: ubuntu2004 + NAME: ubuntu2204 amd64-opensuse-leap-container: extends: .container_job_template diff --git a/.gitlab-ci.d/crossbuild-template.yml b/.gitlab-ci.d/crossbuild-template.yml index 28b2142ec2..3e5f4d9cd8 100644 --- a/.gitlab-ci.d/crossbuild-template.yml +++ b/.gitlab-ci.d/crossbuild-template.yml @@ -1,14 +1,24 @@ .cross_system_build_job: extends: .base_job_template stage: build - image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest + image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG + cache: + paths: + - ccache + key: "$CI_JOB_NAME" + when: always timeout: 80m script: + - export CCACHE_BASEDIR="$(pwd)" + - export CCACHE_DIR="$CCACHE_BASEDIR/ccache" + - export CCACHE_MAXSIZE="500M" + - export PATH="$CCACHE_WRAPPERSDIR:$PATH" - mkdir build - cd build - - PKG_CONFIG_PATH=$PKG_CONFIG_PATH - ../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS - --disable-user --target-list-exclude="arm-softmmu cris-softmmu + - ccache --zero-stats + - ../configure --enable-werror --disable-docs --enable-fdt=system + --disable-user $QEMU_CONFIGURE_OPTS $EXTRA_CONFIGURE_OPTS + --target-list-exclude="arm-softmmu cris-softmmu i386-softmmu microblaze-softmmu mips-softmmu mipsel-softmmu mips64-softmmu ppc-softmmu riscv32-softmmu sh4-softmmu sparc-softmmu xtensa-softmmu $CROSS_SKIP_TARGETS" @@ -18,6 +28,7 @@ version="$(git describe --match v[0-9]* 2>/dev/null || git rev-parse --short HEAD)"; mv -v qemu-setup*.exe qemu-setup-${version}.exe; fi + - ccache --show-stats # Job to cross-build specific accelerators. # @@ -27,24 +38,52 @@ .cross_accel_build_job: extends: .base_job_template stage: build - image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest + image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG timeout: 30m + cache: + paths: + - ccache/ + key: "$CI_JOB_NAME" script: + - export CCACHE_BASEDIR="$(pwd)" + - export CCACHE_DIR="$CCACHE_BASEDIR/ccache" + - export CCACHE_MAXSIZE="500M" + - export PATH="$CCACHE_WRAPPERSDIR:$PATH" - mkdir build - cd build - - PKG_CONFIG_PATH=$PKG_CONFIG_PATH - ../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS + - ../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS --disable-tools --enable-${ACCEL:-kvm} $EXTRA_CONFIGURE_OPTS - make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS .cross_user_build_job: extends: .base_job_template stage: build - image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest + image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG + cache: + paths: + - ccache/ + key: "$CI_JOB_NAME" script: + - export CCACHE_BASEDIR="$(pwd)" + - export CCACHE_DIR="$CCACHE_BASEDIR/ccache" + - export CCACHE_MAXSIZE="500M" - mkdir build - cd build - - PKG_CONFIG_PATH=$PKG_CONFIG_PATH - ../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS - --disable-system + - ../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS + --disable-system --target-list-exclude="aarch64_be-linux-user + alpha-linux-user cris-linux-user m68k-linux-user microblazeel-linux-user + nios2-linux-user or1k-linux-user ppc-linux-user sparc-linux-user + xtensa-linux-user $CROSS_SKIP_TARGETS" - make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS + +# We can still run some tests on some of our cross build jobs. They can add this +# template to their extends to save the build logs and test results +.cross_test_artifacts: + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" + when: always + expire_in: 7 days + paths: + - build/meson-logs/testlog.txt + reports: + junit: build/meson-logs/testlog.junit.xml diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml index 4a5fb6ea2a..987ba9694b 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -1,13 +1,6 @@ include: - local: '/.gitlab-ci.d/crossbuild-template.yml' -cross-armel-system: - extends: .cross_system_build_job - needs: - job: armel-debian-cross-container - variables: - IMAGE: debian-armel-cross - cross-armel-user: extends: .cross_user_build_job needs: @@ -15,13 +8,6 @@ cross-armel-user: variables: IMAGE: debian-armel-cross -cross-armhf-system: - extends: .cross_system_build_job - needs: - job: armhf-debian-cross-container - variables: - IMAGE: debian-armhf-cross - cross-armhf-user: extends: .cross_user_build_job needs: @@ -43,47 +29,37 @@ cross-arm64-user: variables: IMAGE: debian-arm64-cross -cross-i386-system: - extends: .cross_system_build_job +cross-arm64-kvm-only: + extends: .cross_accel_build_job needs: - job: i386-fedora-cross-container + job: arm64-debian-cross-container variables: - IMAGE: fedora-i386-cross - MAKE_CHECK_ARGS: check-qtest + IMAGE: debian-arm64-cross + EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-features -cross-i386-user: - extends: .cross_user_build_job +cross-i686-user: + extends: + - .cross_user_build_job + - .cross_test_artifacts needs: - job: i386-fedora-cross-container + job: i686-debian-cross-container variables: - IMAGE: fedora-i386-cross + IMAGE: debian-i686-cross MAKE_CHECK_ARGS: check -cross-i386-tci: - extends: .cross_accel_build_job +cross-i686-tci: + extends: + - .cross_accel_build_job + - .cross_test_artifacts timeout: 60m needs: - job: i386-fedora-cross-container + job: i686-debian-cross-container variables: - IMAGE: fedora-i386-cross + IMAGE: debian-i686-cross ACCEL: tcg-interpreter - EXTRA_CONFIGURE_OPTS: --target-list=i386-softmmu,i386-linux-user,aarch64-softmmu,aarch64-linux-user,ppc-softmmu,ppc-linux-user + EXTRA_CONFIGURE_OPTS: --target-list=i386-softmmu,i386-linux-user,aarch64-softmmu,aarch64-linux-user,ppc-softmmu,ppc-linux-user --disable-plugins MAKE_CHECK_ARGS: check check-tcg -cross-mips-system: - extends: .cross_system_build_job - needs: - job: mips-debian-cross-container - variables: - IMAGE: debian-mips-cross - -cross-mips-user: - extends: .cross_user_build_job - needs: - job: mips-debian-cross-container - variables: - IMAGE: debian-mips-cross - cross-mipsel-system: extends: .cross_system_build_job needs: @@ -126,6 +102,14 @@ cross-ppc64el-user: variables: IMAGE: debian-ppc64el-cross +cross-ppc64el-kvm-only: + extends: .cross_accel_build_job + needs: + job: ppc64el-debian-cross-container + variables: + IMAGE: debian-ppc64el-cross + EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-devices + # The riscv64 cross-builds currently use a 'sid' container to get # compilers and libraries. Until something more stable is found we # allow_failure so as not to block CI. @@ -165,7 +149,7 @@ cross-s390x-kvm-only: job: s390x-debian-cross-container variables: IMAGE: debian-s390x-cross - EXTRA_CONFIGURE_OPTS: --disable-tcg + EXTRA_CONFIGURE_OPTS: --disable-tcg --enable-trace-backends=ftrace cross-mips64el-kvm-only: extends: .cross_accel_build_job @@ -175,27 +159,19 @@ cross-mips64el-kvm-only: IMAGE: debian-mips64el-cross EXTRA_CONFIGURE_OPTS: --disable-tcg --target-list=mips64el-softmmu -cross-win32-system: - extends: .cross_system_build_job - needs: - job: win32-fedora-cross-container - variables: - IMAGE: fedora-win32-cross - CROSS_SKIP_TARGETS: alpha-softmmu avr-softmmu hppa-softmmu m68k-softmmu - microblazeel-softmmu mips64el-softmmu nios2-softmmu - artifacts: - paths: - - build/qemu-setup*.exe - cross-win64-system: extends: .cross_system_build_job needs: job: win64-fedora-cross-container variables: IMAGE: fedora-win64-cross - CROSS_SKIP_TARGETS: or1k-softmmu rx-softmmu sh4eb-softmmu sparc64-softmmu + EXTRA_CONFIGURE_OPTS: --enable-fdt=internal --disable-plugins + CROSS_SKIP_TARGETS: alpha-softmmu avr-softmmu hppa-softmmu + m68k-softmmu microblazeel-softmmu nios2-softmmu + or1k-softmmu rx-softmmu sh4eb-softmmu sparc64-softmmu tricore-softmmu xtensaeb-softmmu artifacts: + when: on_success paths: - build/qemu-setup*.exe diff --git a/.gitlab-ci.d/custom-runners.yml b/.gitlab-ci.d/custom-runners.yml index 15aaccc481..a0e79acd39 100644 --- a/.gitlab-ci.d/custom-runners.yml +++ b/.gitlab-ci.d/custom-runners.yml @@ -10,11 +10,26 @@ # gitlab-runner. To avoid problems that gitlab-runner can cause while # reusing the GIT repository, let's enable the clone strategy, which # guarantees a fresh repository on each job run. -variables: - GIT_STRATEGY: clone + +# All custom runners can extend this template to upload the testlog +# data as an artifact and also feed the junit report +.custom_runner_template: + extends: .base_job_template + variables: + GIT_STRATEGY: clone + GIT_FETCH_EXTRA_FLAGS: --no-tags --prune --quiet + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" + expire_in: 7 days + when: always + paths: + - build/build.ninja + - build/meson-logs + reports: + junit: build/meson-logs/testlog.junit.xml include: - local: '/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml' - - local: '/.gitlab-ci.d/custom-runners/ubuntu-20.04-aarch64.yml' - - local: '/.gitlab-ci.d/custom-runners/ubuntu-20.04-aarch32.yml' + - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml' + - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml' - local: '/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml' diff --git a/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml b/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml index 49aa703f55..367424db78 100644 --- a/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml +++ b/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml @@ -1,4 +1,9 @@ +# All centos-stream-8 jobs should run successfully in an environment +# setup by the scripts/ci/setup/stream/8/build-environment.yml task +# "Installation of extra packages to build QEMU" + centos-stream-8-x86_64: + extends: .custom_runner_template allow_failure: true needs: [] stage: build @@ -8,21 +13,12 @@ centos-stream-8-x86_64: rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - if: "$CENTOS_STREAM_8_x86_64_RUNNER_AVAILABLE" - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" - when: on_failure - expire_in: 7 days - paths: - - build/tests/results/latest/results.xml - - build/tests/results/latest/test-results - reports: - junit: build/tests/results/latest/results.xml before_script: - JOBS=$(expr $(nproc) + 1) script: - mkdir build - cd build - ../scripts/ci/org.centos/stream/8/x86_64/configure + || { cat config.log meson-logs/meson-log.txt; exit 1; } - make -j"$JOBS" - - make NINJA=":" check - - ../scripts/ci/org.centos/stream/8/x86_64/test-avocado + - make NINJA=":" check check-avocado diff --git a/.gitlab-ci.d/custom-runners/ubuntu-20.04-aarch64.yml b/.gitlab-ci.d/custom-runners/ubuntu-20.04-aarch64.yml deleted file mode 100644 index 951e490db1..0000000000 --- a/.gitlab-ci.d/custom-runners/ubuntu-20.04-aarch64.yml +++ /dev/null @@ -1,118 +0,0 @@ -# All ubuntu-20.04 jobs should run successfully in an environment -# setup by the scripts/ci/setup/qemu/build-environment.yml task -# "Install basic packages to build QEMU on Ubuntu 20.04" - -ubuntu-20.04-aarch64-all-linux-static: - needs: [] - stage: build - tags: - - ubuntu_20.04 - - aarch64 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - - if: "$AARCH64_RUNNER_AVAILABLE" - script: - # --disable-libssh is needed because of https://bugs.launchpad.net/qemu/+bug/1838763 - # --disable-glusterfs is needed because there's no static version of those libs in distro supplied packages - - mkdir build - - cd build - - ../configure --enable-debug --static --disable-system --disable-glusterfs --disable-libssh - - make --output-sync -j`nproc` - - make --output-sync -j`nproc` check V=1 - - make --output-sync -j`nproc` check-tcg V=1 - -ubuntu-20.04-aarch64-all: - needs: [] - stage: build - tags: - - ubuntu_20.04 - - aarch64 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - when: manual - allow_failure: true - - if: "$AARCH64_RUNNER_AVAILABLE" - when: manual - allow_failure: true - script: - - mkdir build - - cd build - - ../configure --disable-libssh - - make --output-sync -j`nproc` - - make --output-sync -j`nproc` check V=1 - -ubuntu-20.04-aarch64-alldbg: - needs: [] - stage: build - tags: - - ubuntu_20.04 - - aarch64 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - - if: "$AARCH64_RUNNER_AVAILABLE" - script: - - mkdir build - - cd build - - ../configure --enable-debug --disable-libssh - - make clean - - make --output-sync -j`nproc` - - make --output-sync -j`nproc` check V=1 - -ubuntu-20.04-aarch64-clang: - needs: [] - stage: build - tags: - - ubuntu_20.04 - - aarch64 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - when: manual - allow_failure: true - - if: "$AARCH64_RUNNER_AVAILABLE" - when: manual - allow_failure: true - script: - - mkdir build - - cd build - - ../configure --disable-libssh --cc=clang-10 --cxx=clang++-10 --enable-sanitizers - - make --output-sync -j`nproc` - - make --output-sync -j`nproc` check V=1 - -ubuntu-20.04-aarch64-tci: - needs: [] - stage: build - tags: - - ubuntu_20.04 - - aarch64 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - when: manual - allow_failure: true - - if: "$AARCH64_RUNNER_AVAILABLE" - when: manual - allow_failure: true - script: - - mkdir build - - cd build - - ../configure --disable-libssh --enable-tcg-interpreter - - make --output-sync -j`nproc` - -ubuntu-20.04-aarch64-notcg: - needs: [] - stage: build - tags: - - ubuntu_20.04 - - aarch64 - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - when: manual - allow_failure: true - - if: "$AARCH64_RUNNER_AVAILABLE" - when: manual - allow_failure: true - script: - - mkdir build - - cd build - - ../configure --disable-libssh --disable-tcg - - make --output-sync -j`nproc` - - make --output-sync -j`nproc` check V=1 diff --git a/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml index 4f292a8a5b..cdae6c5212 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml @@ -3,13 +3,12 @@ # "Install basic packages to build QEMU on Ubuntu 20.04/20.04" ubuntu-20.04-s390x-all-linux-static: + extends: .custom_runner_template needs: [] stage: build tags: - ubuntu_20.04 - s390x - variables: - DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - if: "$S390X_RUNNER_AVAILABLE" @@ -19,18 +18,19 @@ ubuntu-20.04-s390x-all-linux-static: - mkdir build - cd build - ../configure --enable-debug --static --disable-system --disable-glusterfs --disable-libssh + || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` - - make --output-sync -j`nproc` check V=1 - - make --output-sync -j`nproc` check-tcg V=1 + - make --output-sync check-tcg + - make --output-sync -j`nproc` check ubuntu-20.04-s390x-all: + extends: .custom_runner_template needs: [] stage: build tags: - ubuntu_20.04 - s390x - variables: - DFLTCC: 0 + timeout: 75m rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - if: "$S390X_RUNNER_AVAILABLE" @@ -38,17 +38,17 @@ ubuntu-20.04-s390x-all: - mkdir build - cd build - ../configure --disable-libssh + || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` - - make --output-sync -j`nproc` check V=1 + - make --output-sync -j`nproc` check ubuntu-20.04-s390x-alldbg: + extends: .custom_runner_template needs: [] stage: build tags: - ubuntu_20.04 - s390x - variables: - DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' when: manual @@ -60,18 +60,18 @@ ubuntu-20.04-s390x-alldbg: - mkdir build - cd build - ../configure --enable-debug --disable-libssh + || { cat config.log meson-logs/meson-log.txt; exit 1; } - make clean - make --output-sync -j`nproc` - - make --output-sync -j`nproc` check V=1 + - make --output-sync -j`nproc` check ubuntu-20.04-s390x-clang: + extends: .custom_runner_template needs: [] stage: build tags: - ubuntu_20.04 - s390x - variables: - DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' when: manual @@ -83,8 +83,9 @@ ubuntu-20.04-s390x-clang: - mkdir build - cd build - ../configure --disable-libssh --cc=clang --cxx=clang++ --enable-sanitizers + || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` - - make --output-sync -j`nproc` check V=1 + - make --output-sync -j`nproc` check ubuntu-20.04-s390x-tci: needs: [] @@ -92,8 +93,6 @@ ubuntu-20.04-s390x-tci: tags: - ubuntu_20.04 - s390x - variables: - DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' when: manual @@ -105,16 +104,16 @@ ubuntu-20.04-s390x-tci: - mkdir build - cd build - ../configure --disable-libssh --enable-tcg-interpreter + || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` ubuntu-20.04-s390x-notcg: + extends: .custom_runner_template needs: [] stage: build tags: - ubuntu_20.04 - s390x - variables: - DFLTCC: 0 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' when: manual @@ -126,5 +125,6 @@ ubuntu-20.04-s390x-notcg: - mkdir build - cd build - ../configure --disable-libssh --disable-tcg + || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc` - - make --output-sync -j`nproc` check V=1 + - make --output-sync -j`nproc` check diff --git a/.gitlab-ci.d/custom-runners/ubuntu-20.04-aarch32.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml similarity index 53% rename from .gitlab-ci.d/custom-runners/ubuntu-20.04-aarch32.yml rename to .gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml index 47856ac53c..b8a0d75162 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-20.04-aarch32.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml @@ -1,12 +1,13 @@ -# All ubuntu-20.04 jobs should run successfully in an environment +# All ubuntu-22.04 jobs should run successfully in an environment # setup by the scripts/ci/setup/qemu/build-environment.yml task -# "Install basic packages to build QEMU on Ubuntu 20.04" +# "Install basic packages to build QEMU on Ubuntu 22.04" -ubuntu-20.04-aarch32-all: +ubuntu-22.04-aarch32-all: + extends: .custom_runner_template needs: [] stage: build tags: - - ubuntu_20.04 + - ubuntu_22.04 - aarch32 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' @@ -19,5 +20,6 @@ ubuntu-20.04-aarch32-all: - mkdir build - cd build - ../configure --cross-prefix=arm-linux-gnueabihf- - - make --output-sync -j`nproc` - - make --output-sync -j`nproc` check V=1 + || { cat config.log meson-logs/meson-log.txt; exit 1; } + - make --output-sync -j`nproc --ignore=40` + - make --output-sync -j`nproc --ignore=40` check diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml new file mode 100644 index 0000000000..374b0956c3 --- /dev/null +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml @@ -0,0 +1,151 @@ +# All ubuntu-22.04 jobs should run successfully in an environment +# setup by the scripts/ci/setup/qemu/build-environment.yml task +# "Install basic packages to build QEMU on Ubuntu 22.04" + +ubuntu-22.04-aarch64-all-linux-static: + extends: .custom_runner_template + needs: [] + stage: build + tags: + - ubuntu_22.04 + - aarch64 + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + - if: "$AARCH64_RUNNER_AVAILABLE" + script: + - mkdir build + - cd build + # Disable -static-pie due to build error with system libc: + # https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/1987438 + - ../configure --enable-debug --static --disable-system --disable-pie + || { cat config.log meson-logs/meson-log.txt; exit 1; } + - make --output-sync -j`nproc --ignore=40` + - make check-tcg + - make --output-sync -j`nproc --ignore=40` check + +ubuntu-22.04-aarch64-all: + extends: .custom_runner_template + needs: [] + stage: build + tags: + - ubuntu_22.04 + - aarch64 + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$AARCH64_RUNNER_AVAILABLE" + when: manual + allow_failure: true + script: + - mkdir build + - cd build + - ../configure + || { cat config.log meson-logs/meson-log.txt; exit 1; } + - make --output-sync -j`nproc --ignore=40` + - make --output-sync -j`nproc --ignore=40` check + +ubuntu-22.04-aarch64-without-defaults: + extends: .custom_runner_template + needs: [] + stage: build + tags: + - ubuntu_22.04 + - aarch64 + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$AARCH64_RUNNER_AVAILABLE" + when: manual + allow_failure: true + script: + - mkdir build + - cd build + - ../configure --disable-user --without-default-devices --without-default-features + || { cat config.log meson-logs/meson-log.txt; exit 1; } + - make --output-sync -j`nproc --ignore=40` + - make --output-sync -j`nproc --ignore=40` check + +ubuntu-22.04-aarch64-alldbg: + extends: .custom_runner_template + needs: [] + stage: build + tags: + - ubuntu_22.04 + - aarch64 + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + - if: "$AARCH64_RUNNER_AVAILABLE" + script: + - mkdir build + - cd build + - ../configure --enable-debug + || { cat config.log meson-logs/meson-log.txt; exit 1; } + - make clean + - make --output-sync -j`nproc --ignore=40` + - make --output-sync -j`nproc --ignore=40` check + +ubuntu-22.04-aarch64-clang: + extends: .custom_runner_template + needs: [] + stage: build + tags: + - ubuntu_22.04 + - aarch64 + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$AARCH64_RUNNER_AVAILABLE" + when: manual + allow_failure: true + script: + - mkdir build + - cd build + - ../configure --disable-libssh --cc=clang --cxx=clang++ --enable-sanitizers + || { cat config.log meson-logs/meson-log.txt; exit 1; } + - make --output-sync -j`nproc --ignore=40` + - make --output-sync -j`nproc --ignore=40` check + +ubuntu-22.04-aarch64-tci: + needs: [] + stage: build + tags: + - ubuntu_22.04 + - aarch64 + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$AARCH64_RUNNER_AVAILABLE" + when: manual + allow_failure: true + script: + - mkdir build + - cd build + - ../configure --enable-tcg-interpreter + || { cat config.log meson-logs/meson-log.txt; exit 1; } + - make --output-sync -j`nproc --ignore=40` + +ubuntu-22.04-aarch64-notcg: + extends: .custom_runner_template + needs: [] + stage: build + tags: + - ubuntu_22.04 + - aarch64 + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$AARCH64_RUNNER_AVAILABLE" + when: manual + allow_failure: true + script: + - mkdir build + - cd build + - ../configure --disable-tcg --with-devices-aarch64=minimal + || { cat config.log meson-logs/meson-log.txt; exit 1; } + - make --output-sync -j`nproc --ignore=40` + - make --output-sync -j`nproc --ignore=40` check diff --git a/.gitlab-ci.d/edk2.yml b/.gitlab-ci.d/edk2.yml deleted file mode 100644 index 13d0f8b019..0000000000 --- a/.gitlab-ci.d/edk2.yml +++ /dev/null @@ -1,60 +0,0 @@ -# All jobs needing docker-edk2 must use the same rules it uses. -.edk2_job_rules: - rules: # Only run this job when ... - - changes: - # this file is modified - - .gitlab-ci.d/edk2.yml - # or the Dockerfile is modified - - .gitlab-ci.d/edk2/Dockerfile - # or roms/edk2/ is modified (submodule updated) - - roms/edk2/* - when: on_success - - if: '$CI_COMMIT_REF_NAME =~ /^edk2/' # or the branch/tag starts with 'edk2' - when: on_success - - if: '$CI_COMMIT_MESSAGE =~ /edk2/i' # or last commit description contains 'EDK2' - when: on_success - -docker-edk2: - extends: .edk2_job_rules - stage: containers - 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: - extends: .edk2_job_rules - stage: build - needs: ['docker-edk2'] - 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 -- - ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 - BaseTools/Source/C/BrotliCompress/brotli - CryptoPkg/Library/OpensslLib/openssl - MdeModulePkg/Library/BrotliCustomDecompressLib/brotli - - 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.d/edk2/Dockerfile b/.gitlab-ci.d/edk2/Dockerfile deleted file mode 100644 index bbe50ff832..0000000000 --- a/.gitlab-ci.d/edk2/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -# -# Docker image to cross-compile EDK2 firmware binaries -# -FROM ubuntu:18.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 \ - python3 \ - uuid-dev \ - && \ - \ - rm -rf /var/lib/apt/lists/* diff --git a/.gitlab-ci.d/opensbi.yml b/.gitlab-ci.d/opensbi.yml index 29a22930d1..42f137d624 100644 --- a/.gitlab-ci.d/opensbi.yml +++ b/.gitlab-ci.d/opensbi.yml @@ -1,61 +1,88 @@ # All jobs needing docker-opensbi must use the same rules it uses. .opensbi_job_rules: - rules: # Only run this job when ... - - changes: - # this file is modified - - .gitlab-ci.d/opensbi.yml - # or the Dockerfile is modified - - .gitlab-ci.d/opensbi/Dockerfile - when: on_success - - changes: # or roms/opensbi/ is modified (submodule updated) - - roms/opensbi/* - when: on_success - - if: '$CI_COMMIT_REF_NAME =~ /^opensbi/' # or the branch/tag starts with 'opensbi' - when: on_success - - if: '$CI_COMMIT_MESSAGE =~ /opensbi/i' # or last commit description contains 'OpenSBI' - when: on_success + rules: + # Forks don't get pipelines unless QEMU_CI=1 or QEMU_CI=2 is set + - if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != "qemu-project"' + when: never + + # In forks, if QEMU_CI=1 is set, then create manual job + # if any files affecting the build output are touched + - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project"' + changes: + - .gitlab-ci.d/opensbi.yml + - .gitlab-ci.d/opensbi/Dockerfile + - roms/opensbi/* + when: manual + + # In forks, if QEMU_CI=1 is set, then create manual job + # if the branch/tag starts with 'opensbi' + - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project" && $CI_COMMIT_REF_NAME =~ /^opensbi/' + when: manual + + # In forks, if QEMU_CI=1 is set, then create manual job + # if the last commit msg contains 'OpenSBI' (case insensitive) + - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project" && $CI_COMMIT_MESSAGE =~ /opensbi/i' + when: manual + + # Scheduled runs on mainline don't get pipelines except for the special Coverity job + - if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_PIPELINE_SOURCE == "schedule"' + when: never + + # Run if any files affecting the build output are touched + - changes: + - .gitlab-ci.d/opensbi.yml + - .gitlab-ci.d/opensbi/Dockerfile + - roms/opensbi/* + when: on_success + + # Run if the branch/tag starts with 'opensbi' + - if: '$CI_COMMIT_REF_NAME =~ /^opensbi/' + when: on_success + + # Run if the last commit msg contains 'OpenSBI' (case insensitive) + - if: '$CI_COMMIT_MESSAGE =~ /opensbi/i' + when: on_success docker-opensbi: - extends: .opensbi_job_rules - stage: containers - 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 + extends: .opensbi_job_rules + stage: containers + image: docker:latest + services: + - docker:dind + variables: + GIT_DEPTH: 3 + IMAGE_TAG: $CI_REGISTRY_IMAGE:opensbi-cross-build + before_script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - until docker info; do sleep 1; done + 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: - extends: .opensbi_job_rules - stage: build - needs: ['docker-opensbi'] - artifacts: - paths: # 'artifacts.zip' will contains the following files: - - pc-bios/opensbi-riscv32-generic-fw_dynamic.bin - - pc-bios/opensbi-riscv64-generic-fw_dynamic.bin - - opensbi32-generic-stdout.log - - opensbi32-generic-stderr.log - - opensbi64-generic-stdout.log - - opensbi64-generic-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-generic 2>&1 1>opensbi32-generic-stdout.log | tee -a opensbi32-generic-stderr.log >&2 - - make -j${JOBS} -C roms/opensbi clean - - make -j${JOBS} -C roms opensbi64-generic 2>&1 1>opensbi64-generic-stdout.log | tee -a opensbi64-generic-stderr.log >&2 + extends: .opensbi_job_rules + stage: build + needs: ['docker-opensbi'] + artifacts: + when: on_success + paths: # 'artifacts.zip' will contains the following files: + - pc-bios/opensbi-riscv32-generic-fw_dynamic.bin + - pc-bios/opensbi-riscv64-generic-fw_dynamic.bin + - opensbi32-generic-stdout.log + - opensbi32-generic-stderr.log + - opensbi64-generic-stdout.log + - opensbi64-generic-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-generic 2>&1 1>opensbi32-generic-stdout.log | tee -a opensbi32-generic-stderr.log >&2 + - make -j${JOBS} -C roms/opensbi clean + - make -j${JOBS} -C roms opensbi64-generic 2>&1 1>opensbi64-generic-stdout.log | tee -a opensbi64-generic-stderr.log >&2 diff --git a/.gitlab-ci.d/opensbi/Dockerfile b/.gitlab-ci.d/opensbi/Dockerfile index 4ba8a4de86..5ccf4151f4 100644 --- a/.gitlab-ci.d/opensbi/Dockerfile +++ b/.gitlab-ci.d/opensbi/Dockerfile @@ -15,6 +15,7 @@ RUN apt update \ ca-certificates \ git \ make \ + python3 \ wget \ && \ \ diff --git a/.gitlab-ci.d/qemu-project.yml b/.gitlab-ci.d/qemu-project.yml index 691d9bf5dc..4d914c4897 100644 --- a/.gitlab-ci.d/qemu-project.yml +++ b/.gitlab-ci.d/qemu-project.yml @@ -1,10 +1,16 @@ # This file contains the set of jobs run by the QEMU project: # https://gitlab.com/qemu-project/qemu/-/pipelines +variables: + RUNNER_TAG: "" + +default: + tags: + - $RUNNER_TAG + include: - local: '/.gitlab-ci.d/base.yml' - local: '/.gitlab-ci.d/stages.yml' - - local: '/.gitlab-ci.d/edk2.yml' - local: '/.gitlab-ci.d/opensbi.yml' - local: '/.gitlab-ci.d/containers.yml' - local: '/.gitlab-ci.d/crossbuilds.yml' diff --git a/.gitlab-ci.d/stages.yml b/.gitlab-ci.d/stages.yml index f50826018d..f92f57a27d 100644 --- a/.gitlab-ci.d/stages.yml +++ b/.gitlab-ci.d/stages.yml @@ -3,6 +3,5 @@ # - test (for test stages, using build artefacts from a build stage) stages: - containers - - containers-layer2 - build - test diff --git a/.gitlab-ci.d/static_checks.yml b/.gitlab-ci.d/static_checks.yml index 289ad1359e..ad9f426a52 100644 --- a/.gitlab-ci.d/static_checks.yml +++ b/.gitlab-ci.d/static_checks.yml @@ -23,12 +23,12 @@ check-dco: before_script: - apk -U add git -check-python-pipenv: +check-python-minreqs: extends: .base_job_template stage: test - image: $CI_REGISTRY_IMAGE/qemu/python:latest + image: $CI_REGISTRY_IMAGE/qemu/python:$QEMU_CI_CONTAINER_TAG script: - - make -C python check-pipenv + - make -C python check-minreqs variables: GIT_DEPTH: 1 needs: @@ -37,7 +37,7 @@ check-python-pipenv: check-python-tox: extends: .base_job_template stage: test - image: $CI_REGISTRY_IMAGE/qemu/python:latest + image: $CI_REGISTRY_IMAGE/qemu/python:$QEMU_CI_CONTAINER_TAG script: - make -C python check-tox variables: diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index 1b2ede49e1..94834269ec 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -1,25 +1,77 @@ -.shared_msys2_builder: +msys2-64bit: extends: .base_job_template tags: - shared-windows - windows - windows-1809 cache: - key: "${CI_JOB_NAME}-cache" + key: "$CI_JOB_NAME" paths: - - ${CI_PROJECT_DIR}/msys64/var/cache + - msys64/var/cache + - ccache + when: always needs: [] stage: build - timeout: 70m + timeout: 100m + variables: + # Select the "64 bit, gcc and MSVCRT" MSYS2 environment + MSYSTEM: MINGW64 + # This feature doesn't (currently) work with PowerShell, it stops + # the echo'ing of commands being run and doesn't show any timing + FF_SCRIPT_SECTIONS: 0 + # do not remove "--without-default-devices"! + # commit 9f8e6cad65a6 ("gitlab-ci: Speed up the msys2-64bit job by using --without-default-devices" + # changed to compile QEMU with the --without-default-devices switch + # for this job, because otherwise the build could not complete within + # the project timeout. + CONFIGURE_ARGS: --target-list=x86_64-softmmu --without-default-devices -Ddebug=false -Doptimization=0 + # qTests don't run successfully with "--without-default-devices", + # so let's exclude the qtests from CI for now. + TEST_ARGS: --no-suite qtest + # The Windows git is a bit older so override the default + GIT_FETCH_EXTRA_FLAGS: --no-tags --prune --quiet + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" + expire_in: 7 days + paths: + - build/meson-logs/testlog.txt + reports: + junit: "build/meson-logs/testlog.junit.xml" before_script: + - Write-Output "Acquiring msys2.exe installer at $(Get-Date -Format u)" - If ( !(Test-Path -Path msys64\var\cache ) ) { mkdir msys64\var\cache } - - If ( !(Test-Path -Path msys64\var\cache\msys2.exe ) ) { - Invoke-WebRequest - "https://github.com/msys2/msys2-installer/releases/download/2022-05-03/msys2-base-x86_64-20220503.sfx.exe" - -outfile "msys64\var\cache\msys2.exe" + - Invoke-WebRequest + "https://repo.msys2.org/distrib/msys2-x86_64-latest.sfx.exe.sig" + -outfile "msys2.exe.sig" + - if ( Test-Path -Path msys64\var\cache\msys2.exe.sig ) { + Write-Output "Cached installer sig" ; + if ( ((Get-FileHash msys2.exe.sig).Hash -ne (Get-FileHash msys64\var\cache\msys2.exe.sig).Hash) ) { + Write-Output "Mis-matched installer sig, new installer download required" ; + Remove-Item -Path msys64\var\cache\msys2.exe.sig ; + if ( Test-Path -Path msys64\var\cache\msys2.exe ) { + Remove-Item -Path msys64\var\cache\msys2.exe + } + } else { + Write-Output "Matched installer sig, cached installer still valid" + } + } else { + Write-Output "No cached installer sig, new installer download required" ; + if ( Test-Path -Path msys64\var\cache\msys2.exe ) { + Remove-Item -Path msys64\var\cache\msys2.exe + } } + - if ( !(Test-Path -Path msys64\var\cache\msys2.exe ) ) { + Write-Output "Fetching latest installer" ; + Invoke-WebRequest + "https://repo.msys2.org/distrib/msys2-x86_64-latest.sfx.exe" + -outfile "msys64\var\cache\msys2.exe" ; + Copy-Item -Path msys2.exe.sig -Destination msys64\var\cache\msys2.exe.sig + } else { + Write-Output "Using cached installer" + } + - Write-Output "Invoking msys2.exe installer at $(Get-Date -Format u)" - msys64\var\cache\msys2.exe -y - ((Get-Content -path .\msys64\etc\\post-install\\07-pacman-key.post -Raw) -replace '--refresh-keys', '--version') | @@ -28,23 +80,29 @@ - .\msys64\usr\bin\bash -lc 'pacman --noconfirm -Syuu' # Core update - .\msys64\usr\bin\bash -lc 'pacman --noconfirm -Syuu' # Normal update - taskkill /F /FI "MODULES eq msys-2.0.dll" - -msys2-64bit: - extends: .shared_msys2_builder script: + - Write-Output "Installing mingw packages at $(Get-Date -Format u)" - .\msys64\usr\bin\bash -lc "pacman -Sy --noconfirm --needed - diffutils git grep make sed + bison diffutils flex + git grep make sed + mingw-w64-x86_64-binutils mingw-w64-x86_64-capstone + mingw-w64-x86_64-ccache mingw-w64-x86_64-curl mingw-w64-x86_64-cyrus-sasl + mingw-w64-x86_64-dtc mingw-w64-x86_64-gcc mingw-w64-x86_64-glib2 mingw-w64-x86_64-gnutls + mingw-w64-x86_64-gtk3 + mingw-w64-x86_64-libgcrypt + mingw-w64-x86_64-libjpeg-turbo mingw-w64-x86_64-libnfs mingw-w64-x86_64-libpng mingw-w64-x86_64-libssh mingw-w64-x86_64-libtasn1 mingw-w64-x86_64-libusb + mingw-w64-x86_64-lzo2 mingw-w64-x86_64-nettle mingw-w64-x86_64-ninja mingw-w64-x86_64-pixman @@ -53,44 +111,22 @@ msys2-64bit: mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_image mingw-w64-x86_64-snappy + mingw-w64-x86_64-spice mingw-w64-x86_64-usbredir - mingw-w64-x86_64-zstd " + mingw-w64-x86_64-zstd" + - Write-Output "Running build at $(Get-Date -Format u)" - $env:CHERE_INVOKING = 'yes' # Preserve the current working directory - - $env:MSYSTEM = 'MINGW64' # Start a 64 bit Mingw environment - - .\msys64\usr\bin\bash -lc './configure --target-list=x86_64-softmmu - --enable-capstone --without-default-devices' - - .\msys64\usr\bin\bash -lc "sed -i '/^ROMS=/d' build/config-host.mak" - - .\msys64\usr\bin\bash -lc 'make -j2' - - .\msys64\usr\bin\bash -lc 'make check' - -msys2-32bit: - extends: .shared_msys2_builder - script: - - .\msys64\usr\bin\bash -lc "pacman -Sy --noconfirm --needed - diffutils git grep make sed - mingw-w64-i686-capstone - mingw-w64-i686-curl - mingw-w64-i686-cyrus-sasl - mingw-w64-i686-gcc - mingw-w64-i686-glib2 - mingw-w64-i686-gnutls - mingw-w64-i686-gtk3 - mingw-w64-i686-libgcrypt - mingw-w64-i686-libjpeg-turbo - mingw-w64-i686-libssh - mingw-w64-i686-libtasn1 - mingw-w64-i686-libusb - mingw-w64-i686-lzo2 - mingw-w64-i686-ninja - mingw-w64-i686-pixman - mingw-w64-i686-pkgconf - mingw-w64-i686-python - mingw-w64-i686-snappy - mingw-w64-i686-usbredir " - - $env:CHERE_INVOKING = 'yes' # Preserve the current working directory - - $env:MSYSTEM = 'MINGW32' # Start a 32-bit MinG environment - - mkdir output - - cd output - - ..\msys64\usr\bin\bash -lc "../configure --target-list=ppc64-softmmu" - - ..\msys64\usr\bin\bash -lc 'make -j2' - - ..\msys64\usr\bin\bash -lc 'make check' + - $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink + - $env:CCACHE_BASEDIR = "$env:CI_PROJECT_DIR" + - $env:CCACHE_DIR = "$env:CCACHE_BASEDIR/ccache" + - $env:CCACHE_MAXSIZE = "500M" + - $env:CCACHE_DEPEND = 1 # cache misses are too expensive with preprocessor mode + - $env:CC = "ccache gcc" + - mkdir build + - cd build + - ..\msys64\usr\bin\bash -lc "ccache --zero-stats" + - ..\msys64\usr\bin\bash -lc "../configure --enable-fdt=system $CONFIGURE_ARGS" + - ..\msys64\usr\bin\bash -lc "make" + - ..\msys64\usr\bin\bash -lc "make check MTESTARGS='$TEST_ARGS' || { cat meson-logs/testlog.txt; exit 1; } ;" + - ..\msys64\usr\bin\bash -lc "ccache --show-stats" + - Write-Output "Finished build at $(Get-Date -Format u)" diff --git a/.gitlab/issue_templates/bug.md b/.gitlab/issue_templates/bug.md index e910f7b1c2..53a79f5828 100644 --- a/.gitlab/issue_templates/bug.md +++ b/.gitlab/issue_templates/bug.md @@ -18,11 +18,11 @@ https://www.qemu.org/contribute/security-process/ --> ## Host environment - - Operating system: (Windows 10 21H1, Fedora 34, etc.) - - OS/kernel version: (For POSIX hosts, use `uname -a`) - - Architecture: (x86, ARM, s390x, etc.) - - QEMU flavor: (qemu-system-x86_64, qemu-aarch64, qemu-img, etc.) - - QEMU version: (e.g. `qemu-system-x86_64 --version`) + - Operating system: + - OS/kernel version: + - Architecture: + - QEMU flavor: + - QEMU version: - QEMU command line: + - OS/kernel version: + - Architecture: ## Description of problem diff --git a/.gitmodules b/.gitmodules index b8bff47df8..73cae4cd4d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,12 +13,6 @@ [submodule "roms/qemu-palcode"] path = roms/qemu-palcode url = https://gitlab.com/qemu-project/qemu-palcode.git -[submodule "roms/sgabios"] - path = roms/sgabios - url = https://gitlab.com/qemu-project/sgabios.git -[submodule "dtc"] - path = dtc - url = https://gitlab.com/qemu-project/dtc.git [submodule "roms/u-boot"] path = roms/u-boot url = https://gitlab.com/qemu-project/u-boot.git @@ -28,36 +22,21 @@ [submodule "roms/QemuMacDrivers"] path = roms/QemuMacDrivers url = https://gitlab.com/qemu-project/QemuMacDrivers.git -[submodule "ui/keycodemapdb"] - path = ui/keycodemapdb - url = https://gitlab.com/qemu-project/keycodemapdb.git [submodule "roms/seabios-hppa"] path = roms/seabios-hppa url = https://gitlab.com/qemu-project/seabios-hppa.git [submodule "roms/u-boot-sam460ex"] path = roms/u-boot-sam460ex url = https://gitlab.com/qemu-project/u-boot-sam460ex.git -[submodule "tests/fp/berkeley-testfloat-3"] - path = tests/fp/berkeley-testfloat-3 - url = https://gitlab.com/qemu-project/berkeley-testfloat-3.git -[submodule "tests/fp/berkeley-softfloat-3"] - path = tests/fp/berkeley-softfloat-3 - url = https://gitlab.com/qemu-project/berkeley-softfloat-3.git [submodule "roms/edk2"] path = roms/edk2 url = https://gitlab.com/qemu-project/edk2.git -[submodule "slirp"] - path = slirp - url = https://gitlab.com/qemu-project/libslirp.git [submodule "roms/opensbi"] path = roms/opensbi url = https://gitlab.com/qemu-project/opensbi.git [submodule "roms/qboot"] path = roms/qboot url = https://gitlab.com/qemu-project/qboot.git -[submodule "meson"] - path = meson - url = https://gitlab.com/qemu-project/meson.git [submodule "roms/vbootrom"] path = roms/vbootrom url = https://gitlab.com/qemu-project/vbootrom.git diff --git a/.mailmap b/.mailmap index 8c326709cf..88fb68143e 100644 --- a/.mailmap +++ b/.mailmap @@ -30,21 +30,41 @@ malc malc # Corrupted Author fields Aaron Larson alarson@ddci.com Andreas Färber Andreas Färber +fanwenjie fanwj@mail.ustc.edu.cn Jason Wang Jason Wang Marek Dolata mkdolata@us.ibm.com Michael Ellerman michael@ozlabs.org Nick Hudson hnick@vmware.com +Timothée Cocault timothee.cocault@gmail.com +Stefan Weil +Stefan Weil Stefan Weil # There is also a: # (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> # for the cvs2svn initialization commit e63c3dc74bf. # Next, translate a few commits where mailman rewrote the From: line due -# to strict SPF, although we prefer to avoid adding more entries like that. +# to strict SPF and DMARC. Usually, our build process should be flagging +# commits like these before maintainer merges; if you find the need to add +# a line here, please also report a bug against the part of the build +# process that let the mis-attribution slip through in the first place. +# +# If the mailing list munges your emails, use: +# git config sendemail.from '"Your Name" ' +# the use of "" in that line will differ from the typically unquoted +# 'git config user.name', which in turn is sufficient for 'git send-email' +# to add an extra From: line in the body of your email that takes +# precedence over any munged From: in the mail's headers. +# See https://lists.openembedded.org/g/openembedded-core/message/166515 +# and https://lists.gnu.org/archive/html/qemu-devel/2023-09/msg06784.html Ed Swierk Ed Swierk via Qemu-devel Ian McKellar Ian McKellar via Qemu-devel Julia Suvorova Julia Suvorova via Qemu-devel Justin Terry (VM) Justin Terry (VM) via Qemu-devel +Stefan Weil Stefan Weil via +Stefan Weil Stefan Weil via +Andrey Drobyshev Andrey Drobyshev via +BALATON Zoltan BALATON Zoltan via # Next, replace old addresses by a more recent one. Aleksandar Markovic @@ -53,8 +73,10 @@ Aleksandar Markovic Aleksandar Rikalo Aleksandar Rikalo Alexander Graf +Ani Sinha Anthony Liguori Anthony Liguori Christian Borntraeger +Damien Hedde Filip Bozuta Frederic Konrad Frederic Konrad @@ -62,15 +84,25 @@ Greg Kurz Huacai Chen Huacai Chen James Hogan +Juan Quintela Leif Lindholm Leif Lindholm +Luc Michel +Luc Michel +Luc Michel Radoslaw Biernacki +Paul Brook Paul Burton Paul Burton Paul Burton Paul Burton -Philippe Mathieu-Daudé +Philippe Mathieu-Daudé +Philippe Mathieu-Daudé +Philippe Mathieu-Daudé +Roman Bolshakov Stefan Brankovic +Stefan Weil Stefan Weil +Taylor Simpson Yongbok Kim # Also list preferred name forms where people have changed their diff --git a/.readthedocs.yml b/.readthedocs.yml index 7fb7b8dd61..0b262469ce 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -5,16 +5,21 @@ # Required version: 2 +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py +# We recommend specifying your dependencies to enable reproducible builds: +# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + - requirements: docs/requirements.txt + # 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.6 diff --git a/.travis.yml b/.travis.yml index 9afc4a54b8..8a3ae76a7c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,43 +16,6 @@ cache: - $HOME/avocado/data/cache -addons: - apt: - packages: - # Build dependencies - - libaio-dev - - libattr1-dev - - libbrlapi-dev - - libcap-ng-dev - - libcacard-dev - - libgcc-7-dev - - libgnutls28-dev - - libgtk-3-dev - - libiscsi-dev - - liblttng-ust-dev - - libncurses5-dev - - libnfs-dev - - libpixman-1-dev - - libpng-dev - - librados-dev - - libsdl2-dev - - libsdl2-image-dev - - libseccomp-dev - - libspice-protocol-dev - - libspice-server-dev - - libssh-dev - - liburcu-dev - - libusb-1.0-0-dev - - libvdeplug-dev - - libvte-2.91-dev - - libzstd-dev - - ninja-build - - sparse - - uuid-dev - # Tests dependencies - - genisoimage - - # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu # to prevent IRC notifications from forks. This was created using: # $ travis encrypt -r "qemu/qemu" "irc.oftc.net#qemu" @@ -71,8 +34,8 @@ env: - 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" + # This is broadly a list of "mainline" system targets which have support across the major distros + - MAIN_SYSTEM_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 @@ -128,6 +91,7 @@ jobs: - libbrlapi-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgcrypt20-dev - libgnutls28-dev - libgtk-3-dev @@ -149,7 +113,8 @@ jobs: - genisoimage env: - TEST_CMD="make check check-tcg V=1" - - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS} --cxx=/bin/false" + - CONFIG="--disable-containers --enable-fdt=system + --target-list=${MAIN_SYSTEM_TARGETS} --cxx=/bin/false" - UNRELIABLE=true - name: "[ppc64] GCC check-tcg" @@ -162,6 +127,7 @@ jobs: - libbrlapi-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgcrypt20-dev - libgnutls28-dev - libgtk-3-dev @@ -183,7 +149,8 @@ jobs: - genisoimage env: - TEST_CMD="make check check-tcg V=1" - - CONFIG="--disable-containers --target-list=ppc64-softmmu,ppc64le-linux-user" + - CONFIG="--disable-containers --enable-fdt=system + --target-list=ppc64-softmmu,ppc64le-linux-user" - name: "[s390x] GCC check-tcg" arch: s390x @@ -195,6 +162,7 @@ jobs: - libbrlapi-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgcrypt20-dev - libgnutls28-dev - libgtk-3-dev @@ -216,20 +184,20 @@ jobs: - genisoimage env: - TEST_CMD="make check check-tcg V=1" - - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user" + - CONFIG="--disable-containers + --target-list=hppa-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" - UNRELIABLE=true - - DFLTCC=0 script: - BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$? - | if [ "$BUILD_RC" -eq 0 ] ; then - mv pc-bios/s390-ccw/*.img pc-bios/ ; + mv pc-bios/s390-ccw/*.img qemu-bundle/usr/local/share/qemu ; ${TEST_CMD} ; else $(exit $BUILD_RC); fi - - name: "[s390x] GCC (other-softmmu)" + - name: "[s390x] GCC (other-system)" arch: s390x dist: focal addons: @@ -238,6 +206,7 @@ jobs: - libattr1-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgnutls28-dev - libiscsi-dev - liblttng-ust-dev @@ -251,14 +220,13 @@ jobs: - libsnappy-dev - libzstd-dev - nettle-dev - - xfslibs-dev - ninja-build # Tests dependencies - genisoimage env: - CONFIG="--disable-containers --audio-drv-list=sdl --disable-user - --target-list-exclude=${MAIN_SOFTMMU_TARGETS}" - - DFLTCC=0 + --target-list=arm-softmmu,avr-softmmu,microblaze-softmmu,sh4eb-softmmu,sparc64-softmmu,xtensaeb-softmmu" + - name: "[s390x] GCC (user)" arch: s390x dist: focal @@ -268,13 +236,16 @@ jobs: - libglib2.0-dev - libgnutls28-dev - ninja-build + - flex + - bison env: + - TEST_CMD="make check check-tcg V=1" - CONFIG="--disable-containers --disable-system" - - DFLTCC=0 + - name: "[s390x] Clang (disable-tcg)" arch: s390x dist: focal - compiler: clang + compiler: clang-10 addons: apt_packages: - libaio-dev @@ -282,6 +253,7 @@ jobs: - libbrlapi-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgcrypt20-dev - libgnutls28-dev - libgtk-3-dev @@ -299,9 +271,9 @@ jobs: - libvdeplug-dev - libvte-2.91-dev - ninja-build + - clang-10 env: - TEST_CMD="make check-unit" - - CONFIG="--disable-containers --disable-tcg --enable-kvm - --disable-tools --host-cc=clang --cxx=clang++" + - CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools + --enable-fdt=system --host-cc=clang --cxx=clang++" - UNRELIABLE=true - - DFLTCC=0 diff --git a/Kconfig.host b/Kconfig.host index 1165c4eacd..f496475f8e 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -11,6 +11,9 @@ config OPENGL config X11 bool +config PIXMAN + bool + config SPICE bool @@ -42,3 +45,10 @@ config MULTIPROCESS_ALLOWED config FUZZ bool select SPARSE_MEM + +config VFIO_USER_SERVER_ALLOWED + bool + imply VFIO_USER_SERVER + +config HV_BALLOON_POSSIBLE + bool diff --git a/MAINTAINERS b/MAINTAINERS index 5580a36b68..f1f6922025 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23,7 +23,7 @@ Descriptions of section entries: W: Web-page with status/info Q: Patchwork web based patch tracking system site T: SCM tree type and location. Type is one of: git, hg, quilt, stgit. - S: Status, one of the following: + S: Status, one of the following (keep in sync with docs/devel/maintainers.rst): Supported: Someone is actually paid to look after this. Maintained: Someone actually looks after it. Odd Fixes: It has a maintainer but they don't have time to do @@ -64,6 +64,20 @@ L: qemu-devel@nongnu.org F: * F: */ +Project policy and developer guides +R: Alex Bennée +R: Daniel P. Berrangé +R: Thomas Huth +R: Markus Armbruster +R: Philippe Mathieu-Daudé +W: https://www.qemu.org/docs/master/devel/index.html +S: Odd Fixes +F: docs/devel/style.rst +F: docs/devel/code-of-conduct.rst +F: docs/devel/conflict-resolution.rst +F: docs/devel/submitting-a-patch.rst +F: docs/devel/submitting-a-pull-request.rst + Responsible Disclosure, Reporting Security Issues ------------------------------------------------- W: https://wiki.qemu.org/SecurityProcess @@ -78,13 +92,13 @@ M: Laurent Vivier S: Maintained L: qemu-trivial@nongnu.org K: ^Subject:.*(?i)trivial +F: docs/devel/trivial-patches.rst T: git git://git.corpit.ru/qemu.git trivial-patches T: git https://github.com/vivier/qemu.git trivial-patches Architecture support -------------------- S390 general architecture support -M: Cornelia Huck M: Thomas Huth S: Supported F: configs/devices/s390x-softmmu/default.mak @@ -106,14 +120,26 @@ F: docs/system/target-s390x.rst F: docs/system/s390x/ F: tests/migration/s390x/ K: ^Subject:.*(?i)s390x? -T: git https://gitlab.com/cohuck/qemu.git s390-next L: qemu-s390x@nongnu.org MIPS general architecture support -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé R: Jiaxun Yang S: Odd Fixes K: ^Subject:.*(?i)mips +F: docs/system/target-mips.rst +F: configs/targets/mips* + +X86 general architecture support +M: Paolo Bonzini +S: Maintained +F: configs/devices/i386-softmmu/default.mak +F: configs/targets/i386-softmmu.mak +F: configs/targets/x86_64-softmmu.mak +F: docs/system/target-i386* +F: target/i386/*.[ch] +F: target/i386/Kconfig +F: target/i386/meson.build Guest CPU cores (TCG) --------------------- @@ -121,9 +147,11 @@ Overall TCG CPUs M: Richard Henderson R: Paolo Bonzini S: Maintained -F: softmmu/cpus.c -F: cpus-common.c -F: page-vary.c +F: system/cpus.c +F: system/watchpoint.c +F: cpu-common.c +F: cpu-target.c +F: page-vary-target.c F: page-vary-common.c F: accel/tcg/ F: accel/stubs/tcg-stub.c @@ -131,12 +159,21 @@ F: util/cacheinfo.c F: util/cacheflush.c F: scripts/decodetree.py F: docs/devel/decodetree.rst +F: docs/devel/tcg* F: include/exec/cpu*.h F: include/exec/exec-all.h +F: include/exec/tb-flush.h +F: include/exec/target_long.h F: include/exec/helper*.h +F: include/exec/helper*.h.inc +F: include/exec/helper-info.c.inc F: include/sysemu/cpus.h F: include/sysemu/tcg.h F: include/hw/core/tcg-cpu-ops.h +F: host/include/*/host/cpuinfo.h +F: util/cpuinfo-*.c +F: include/tcg/ +F: tests/decode/ FPU emulation M: Aurelien Jarno @@ -159,14 +196,13 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: target/arm/ +F: target/arm/tcg/ F: tests/tcg/arm/ F: tests/tcg/aarch64/ F: tests/qtest/arm-cpu-features.c F: hw/arm/ F: hw/cpu/a*mpcore.c F: include/hw/cpu/a*mpcore.h -F: disas/arm-a64.cc -F: disas/libvixl/ F: docs/system/target-arm.rst F: docs/system/arm/cpu-features.rst @@ -196,34 +232,45 @@ F: tests/tcg/cris/ F: disas/cris.c Hexagon TCG CPUs -M: Taylor Simpson +M: Brian Cain S: Supported F: target/hexagon/ +X: target/hexagon/idef-parser/ +X: target/hexagon/gen_idef_parser_funcs.py F: linux-user/hexagon/ F: tests/tcg/hexagon/ F: disas/hexagon.c F: configs/targets/hexagon-linux-user/default.mak F: docker/dockerfiles/debian-hexagon-cross.docker -F: docker/dockerfiles/debian-hexagon-cross.docker.d/build-toolchain.sh +F: gdb-xml/hexagon*.xml + +Hexagon idef-parser +M: Alessandro Di Federico +M: Anton Johansson +S: Supported +F: target/hexagon/idef-parser/ +F: target/hexagon/gen_idef_parser_funcs.py HPPA (PA-RISC) TCG CPUs M: Richard Henderson S: Maintained F: target/hppa/ F: disas/hppa.c +F: tests/tcg/hppa/ LoongArch TCG CPUs M: Song Gao -M: Xiaojuan Yang S: Maintained F: target/loongarch/ F: tests/tcg/loongarch64/ +F: tests/avocado/machine_loongarch.py M68K TCG CPUs M: Laurent Vivier S: Maintained F: target/m68k/ F: disas/m68k.c +F: tests/tcg/m68k/ MicroBlaze TCG CPUs M: Edgar E. Iglesias @@ -232,71 +279,91 @@ F: target/microblaze/ F: hw/microblaze/ F: disas/microblaze.c F: tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh -F: tests/tcg/nios2/Makefile.target MIPS TCG CPUs -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé R: Aurelien Jarno R: Jiaxun Yang R: Aleksandar Rikalo S: Odd Fixes F: target/mips/ -F: disas/mips.c +F: disas/*mips.c F: docs/system/cpu-models-mips.rst.inc F: tests/tcg/mips/ -MIPS TCG CPUs (nanoMIPS ISA) -S: Orphan -F: disas/nanomips.* -F: target/mips/tcg/*nanomips* - NiosII TCG CPUs -M: Chris Wulff -M: Marek Vasut -S: Maintained +R: Chris Wulff +R: Marek Vasut +S: Orphan F: target/nios2/ F: hw/nios2/ +F: hw/intc/nios2_vic.c F: disas/nios2.c +F: include/hw/intc/nios2_vic.h F: configs/devices/nios2-softmmu/default.mak F: tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh +F: tests/tcg/nios2/ OpenRISC TCG CPUs M: Stafford Horne S: Odd Fixes +F: docs/system/openrisc/cpu-features.rst F: target/openrisc/ F: hw/openrisc/ +F: include/hw/openrisc/ F: tests/tcg/openrisc/ PowerPC TCG CPUs -M: Cédric Le Goater +M: Nicholas Piggin M: Daniel Henrique Barboza -R: David Gibson -R: Greg Kurz L: qemu-ppc@nongnu.org -S: Maintained +S: Odd Fixes F: target/ppc/ F: hw/ppc/ppc.c F: hw/ppc/ppc_booke.c F: include/hw/ppc/ppc.h +F: hw/ppc/meson.build +F: hw/ppc/trace* +F: configs/devices/ppc* +F: docs/system/ppc/embedded.rst +F: docs/system/target-ppc.rst +F: tests/tcg/ppc*/* RISC-V TCG CPUs M: Palmer Dabbelt M: Alistair Francis M: Bin Meng +R: Weiwei Li +R: Daniel Henrique Barboza +R: Liu Zhiwei L: qemu-riscv@nongnu.org S: Supported +F: configs/targets/riscv* +F: docs/system/target-riscv.rst F: target/riscv/ F: hw/riscv/ +F: hw/intc/riscv* F: include/hw/riscv/ F: linux-user/host/riscv32/ F: linux-user/host/riscv64/ +F: tests/tcg/riscv64/ + +RISC-V XThead* extensions +M: Christoph Muellner +M: LIU Zhiwei +L: qemu-riscv@nongnu.org +S: Supported +F: target/riscv/insn_trans/trans_xthead.c.inc +F: target/riscv/xthead*.decode +F: disas/riscv-xthead* RISC-V XVentanaCondOps extension M: Philipp Tomsich L: qemu-riscv@nongnu.org -S: Supported +S: Maintained F: target/riscv/XVentanaCondOps.decode F: target/riscv/insn_trans/trans_xventanacondops.c.inc +F: disas/riscv-xventana* RENESAS RX CPUs R: Yoshinori Sato @@ -306,6 +373,7 @@ F: target/rx/ S390 TCG CPUs M: Richard Henderson M: David Hildenbrand +R: Ilya Leoshkevich S: Maintained F: target/s390x/ F: target/s390x/tcg @@ -320,6 +388,7 @@ F: target/sh4/ F: hw/sh4/ F: disas/sh4.c F: include/hw/sh4/ +F: tests/tcg/sh4/ SPARC TCG CPUs M: Mark Cave-Ayland @@ -330,6 +399,7 @@ F: hw/sparc/ F: hw/sparc64/ F: include/hw/sparc/sparc64.h F: disas/sparc.c +F: tests/tcg/sparc64/ X86 TCG CPUs M: Paolo Bonzini @@ -340,6 +410,7 @@ F: target/i386/tcg/ F: tests/tcg/i386/ F: tests/tcg/x86_64/ F: hw/i386/ +F: docs/system/i386/cpu.rst F: docs/system/cpu-models-x86* T: git https://gitlab.com/ehabkost/qemu.git x86-next @@ -350,6 +421,7 @@ S: Maintained F: target/xtensa/ F: hw/xtensa/ F: tests/tcg/xtensa/ +F: tests/tcg/xtensaeb/ F: disas/xtensa.c F: include/hw/xtensa/xtensa-isa.h F: configs/devices/xtensa*/default.mak @@ -393,11 +465,9 @@ F: target/mips/kvm* F: target/mips/sysemu/ PPC KVM CPUs -M: Cédric Le Goater -M: Daniel Henrique Barboza -R: David Gibson -R: Greg Kurz -S: Maintained +M: Nicholas Piggin +R: Daniel Henrique Barboza +S: Odd Fixes F: target/ppc/kvm.c S390 KVM CPUs @@ -407,8 +477,6 @@ S: Supported F: target/s390x/kvm/ F: target/s390x/machine.c F: target/s390x/sigp.c -F: hw/s390x/pv.c -F: include/hw/s390x/pv.h F: gdb-xml/s390*.xml T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org @@ -424,6 +492,15 @@ F: target/i386/kvm/ F: target/i386/sev* F: scripts/kvm/vmxcap +Xen emulation on X86 KVM CPUs +M: David Woodhouse +M: Paul Durrant +S: Supported +F: include/sysemu/kvm_xen.h +F: target/i386/kvm/xen* +F: hw/i386/kvm/xen* +F: tests/avocado/kvm_xen_guest.py + Guest CPU Cores (other accelerators) ------------------------------------ Overall @@ -431,7 +508,7 @@ M: Richard Henderson R: Paolo Bonzini S: Maintained F: include/qemu/accel.h -F: include/sysemu/accel-ops.h +F: include/sysemu/accel-*.h F: include/hw/core/accel-cpu.h F: accel/accel-*.c F: accel/Makefile.objs @@ -444,14 +521,14 @@ F: target/arm/hvf/ X86 HVF CPUs M: Cameron Esfahani -M: Roman Bolshakov +M: Roman Bolshakov W: https://wiki.qemu.org/Features/HVF S: Maintained F: target/i386/hvf/ HVF M: Cameron Esfahani -M: Roman Bolshakov +M: Roman Bolshakov W: https://wiki.qemu.org/Features/HVF S: Maintained F: accel/hvf/ @@ -491,21 +568,9 @@ F: include/sysemu/xen.h F: include/sysemu/xen-mapcache.h F: stubs/xen-hw-stub.c -Guest CPU Cores (HAXM) ---------------------- -X86 HAXM CPUs -M: Wenchao Wang -L: haxm-team@intel.com -W: https://github.com/intel/haxm/issues -S: Maintained -F: accel/stubs/hax-stub.c -F: include/sysemu/hax.h -F: target/i386/hax/ - Guest CPU Cores (NVMM) ---------------------- NetBSD Virtual Machine Monitor (NVMM) CPU support -M: Kamil Rytarowski M: Reinoud Zandijk S: Maintained F: include/sysemu/nvmm.h @@ -519,6 +584,7 @@ M: Cornelia Huck M: Paolo Bonzini S: Maintained F: linux-headers/ +F: include/standard-headers/ F: scripts/update-linux-headers.sh POSIX @@ -530,7 +596,6 @@ F: util/*posix*.c F: include/qemu/*posix*.h NETBSD -M: Kamil Rytarowski M: Reinoud Zandijk M: Ryo ONODERA S: Maintained @@ -549,12 +614,14 @@ F: */*win32* F: include/*/*win32* X: qga/*win32* F: qemu.nsi +F: scripts/nsis.py Darwin (macOS, iOS) -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé S: Odd Fixes F: .gitlab-ci.d/cirrus/macos-* F: */*.m +F: scripts/entitlement.sh Alpha Machines -------------- @@ -569,12 +636,15 @@ ARM Machines Allwinner-a10 M: Beniamino Galvani M: Peter Maydell +R: Strahinja Jankovic L: qemu-arm@nongnu.org S: Odd Fixes F: hw/*/allwinner* +F: hw/ide/ahci-allwinner.c F: include/hw/*/allwinner* F: hw/arm/cubieboard.c F: docs/system/arm/cubieboard.rst +F: hw/misc/axp209.c Allwinner-h3 M: Niek Linnenbank @@ -597,6 +667,7 @@ F: include/hw/dma/pl080.h F: hw/dma/pl330.c F: hw/gpio/pl061.c F: hw/input/pl050.c +F: include/hw/input/pl050.h F: hw/intc/pl190.c F: hw/sd/pl181.c F: hw/ssi/pl022.c @@ -627,7 +698,7 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/intc/arm* -F: hw/intc/gic_internal.h +F: hw/intc/gic*_internal.h F: hw/misc/a9scu.c F: hw/misc/arm11scu.c F: hw/misc/arm_l2x0.c @@ -682,7 +753,7 @@ F: include/hw/rtc/goldfish_rtc.h Gumstix M: Peter Maydell -R: Philippe Mathieu-Daudé +R: Philippe Mathieu-Daudé L: qemu-arm@nongnu.org S: Odd Fixes F: hw/arm/gumstix.c @@ -747,12 +818,13 @@ F: include/hw/misc/imx7_*.h F: hw/pci-host/designware.c F: include/hw/pci-host/designware.h -MPS2 +MPS2 / MPS3 M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/arm/mps2.c F: hw/arm/mps2-tz.c +F: hw/arm/mps3r.c F: hw/misc/mps2-*.c F: include/hw/misc/mps2-*.h F: hw/arm/armsse.c @@ -794,13 +866,15 @@ F: include/hw/net/mv88w8618_eth.h F: docs/system/arm/musicpal.rst Nuvoton NPCM7xx -M: Havard Skinnemoen M: Tyrone Ting +M: Hao Wu L: qemu-arm@nongnu.org S: Supported -F: hw/*/npcm7xx* -F: include/hw/*/npcm7xx* -F: tests/qtest/npcm7xx* +F: hw/*/npcm* +F: hw/sensor/adm1266.c +F: include/hw/*/npcm* +F: tests/qtest/npcm* +F: tests/qtest/adm1266-test.c F: pc-bios/npcm7xx_bootrom.bin F: roms/vbootrom F: docs/system/arm/nuvoton.rst @@ -833,13 +907,13 @@ F: docs/system/arm/palm.rst Raspberry Pi M: Peter Maydell -R: Philippe Mathieu-Daudé +R: Philippe Mathieu-Daudé L: qemu-arm@nongnu.org S: Odd Fixes F: hw/arm/raspi.c F: hw/arm/raspi_platform.h F: hw/*/bcm283* -F: include/hw/arm/raspi* +F: include/hw/arm/rasp* F: include/hw/*/bcm283* F: docs/system/arm/raspi.rst @@ -865,6 +939,7 @@ F: hw/*/pxa2xx* F: hw/display/tc6393xb.c F: hw/gpio/max7310.c F: hw/gpio/zaurus.c +F: hw/input/ads7846.c F: hw/misc/mst_fpga.c F: hw/adc/max111x.c F: include/hw/adc/max111x.h @@ -879,6 +954,7 @@ M: Peter Maydell R: Jean-Christophe Dubois L: qemu-arm@nongnu.org S: Odd Fixes +F: docs/system/arm/sabrelite.rst F: hw/arm/sabrelite.c F: hw/arm/fsl-imx6.c F: hw/misc/imx6_*.c @@ -893,10 +969,15 @@ SBSA-REF M: Radoslaw Biernacki M: Peter Maydell R: Leif Lindholm +R: Marcin Juszkiewicz L: qemu-arm@nongnu.org S: Maintained F: hw/arm/sbsa-ref.c +F: hw/misc/sbsa_ec.c +F: hw/watchdog/sbsa_gwdt.c +F: include/hw/watchdog/sbsa_gwdt.h F: docs/system/arm/sbsa.rst +F: tests/avocado/machine_aarch64_sbsaref.py Sharp SL-5500 (Collie) PDA M: Peter Maydell @@ -911,7 +992,9 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/*/stellaris* +F: hw/display/ssd03* F: include/hw/input/gamepad.h +F: include/hw/timer/stellaris-gptm.h F: docs/system/arm/stellaris.rst STM32VLDISCOVERY @@ -926,6 +1009,7 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/arm/vexpress.c +F: hw/display/sii9022.c F: docs/system/arm/vexpress.rst Versatile PB @@ -933,6 +1017,7 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/*/versatile* +F: hw/i2c/arm_sbcon_i2c.c F: include/hw/i2c/arm_sbcon_i2c.h F: hw/misc/arm_sysctl.c F: docs/system/arm/versatile.rst @@ -979,11 +1064,15 @@ S: Maintained F: hw/ssi/xlnx-versal-ospi.c F: include/hw/ssi/xlnx-versal-ospi.h -ARM ACPI Subsystem -M: Shannon Zhao -L: qemu-arm@nongnu.org +Xilinx Versal CFI +M: Francisco Iglesias S: Maintained -F: hw/arm/virt-acpi-build.c +F: hw/misc/xlnx-cfi-if.c +F: include/hw/misc/xlnx-cfi-if.h +F: hw/misc/xlnx-versal-cfu.c +F: include/hw/misc/xlnx-versal-cfu.h +F: hw/misc/xlnx-versal-cframe-reg.c +F: include/hw/misc/xlnx-versal-cframe-reg.h STM32F100 M: Alexandre Iooss @@ -1027,6 +1116,31 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/netduinoplus2.c +Olimex STM32 H405 +M: Felipe Balbi +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/olimex-stm32-h405.c + +STM32L4x5 SoC Family +M: Arnaud Minier +M: Inès Varhol +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/stm32l4x5_soc.c +F: hw/misc/stm32l4x5_exti.c +F: hw/misc/stm32l4x5_syscfg.c +F: hw/misc/stm32l4x5_rcc.c +F: hw/gpio/stm32l4x5_gpio.c +F: include/hw/*/stm32l4x5_*.h + +B-L475E-IOT01A IoT Node +M: Arnaud Minier +M: Inès Varhol +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/b-l475e-iot01a.c + SmartFusion2 M: Subbaraya Sundeep M: Peter Maydell @@ -1054,18 +1168,17 @@ F: docs/system/arm/emcraft-sf2.rst ASPEED BMCs M: Cédric Le Goater M: Peter Maydell -R: Andrew Jeffery +R: Andrew Jeffery R: Joel Stanley L: qemu-arm@nongnu.org S: Maintained F: hw/*/*aspeed* -F: hw/misc/pca9552.c F: include/hw/*/*aspeed* -F: include/hw/misc/pca9552*.h F: hw/net/ftgmac100.c F: include/hw/net/ftgmac100.h F: docs/system/arm/aspeed.rst -F: tests/qtest/*aspeed* +F: tests/*/*aspeed* +F: hw/arm/fby35.c NRF51 M: Joel Stanley @@ -1095,7 +1208,7 @@ F: include/hw/misc/avr_power.h F: hw/misc/avr_power.c Arduino -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé S: Maintained F: hw/avr/arduino.c @@ -1109,27 +1222,32 @@ F: hw/*/etraxfs_*.c HP-PARISC Machines ------------------ -HP B160L +HP B160L, HP C3700 M: Richard Henderson R: Helge Deller S: Odd Fixes F: configs/devices/hppa-softmmu/default.mak +F: hw/display/artist.c F: hw/hppa/ +F: hw/input/lasips2.c F: hw/net/*i82596* F: hw/misc/lasi.c +F: hw/pci-host/astro.c F: hw/pci-host/dino.c +F: include/hw/input/lasips2.h F: include/hw/misc/lasi.h F: include/hw/net/lasi_82596.h +F: include/hw/pci-host/astro.h F: include/hw/pci-host/dino.h F: pc-bios/hppa-firmware.img +F: roms/seabios-hppa/ LoongArch Machines ------------------ Virt -M: Xiaojuan Yang M: Song Gao S: Maintained -F: docs/system/loongarch/loongson3.rst +F: docs/system/loongarch/virt.rst F: configs/targets/loongarch64-softmmu.mak F: configs/devices/loongarch64-softmmu/default.mak F: hw/loongarch/ @@ -1168,10 +1286,14 @@ q800 M: Laurent Vivier S: Maintained F: hw/m68k/q800.c +F: hw/m68k/q800-glue.c F: hw/misc/mac_via.c F: hw/nubus/* F: hw/display/macfb.c F: hw/block/swim.c +F: hw/misc/djmemc.c +F: hw/misc/iosb.c +F: hw/audio/asc.c F: hw/m68k/bootinfo.h F: include/standard-headers/asm-m68k/bootinfo.h F: include/standard-headers/asm-m68k/bootinfo-mac.h @@ -1179,6 +1301,11 @@ F: include/hw/misc/mac_via.h F: include/hw/nubus/* F: include/hw/display/macfb.h F: include/hw/block/swim.h +F: include/hw/m68k/q800.h +F: include/hw/m68k/q800-glue.h +F: include/hw/misc/djmemc.h +F: include/hw/misc/iosb.h +F: include/hw/audio/asc.h virt M: Laurent Vivier @@ -1192,6 +1319,7 @@ F: include/hw/char/goldfish_tty.h F: include/hw/intc/goldfish_pic.h F: include/hw/intc/m68k_irqc.h F: include/hw/misc/virt_ctrl.h +F: docs/specs/virt-ctlr.rst MicroBlaze Machines ------------------- @@ -1210,7 +1338,7 @@ F: hw/microblaze/petalogix_ml605_mmu.c MIPS Machines ------------- Overall MIPS Machines -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé S: Odd Fixes F: configs/devices/mips*/* F: hw/mips/ @@ -1221,17 +1349,20 @@ M: Hervé Poussineau R: Aleksandar Rikalo S: Maintained F: hw/mips/jazz.c +F: hw/display/g364fb.c F: hw/display/jazz_led.c F: hw/dma/rc4030.c +F: hw/nvram/ds1225y.c Malta -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé R: Aurelien Jarno S: Odd Fixes -F: hw/isa/piix4.c +F: hw/isa/piix.c +F: hw/isa/fdc37m81x-superio.c F: hw/acpi/piix4.c F: hw/mips/malta.c -F: hw/mips/gt64xxx_pci.c +F: hw/pci-host/gt64120.c F: include/hw/southbridge/piix.h F: tests/avocado/linux_ssh_mips_malta.py F: tests/avocado/machine_mips_malta.py @@ -1244,14 +1375,12 @@ F: hw/net/mipsnet.c Fuloong 2E M: Huacai Chen -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé R: Jiaxun Yang S: Odd Fixes F: hw/mips/fuloong2e.c -F: hw/isa/vt82c686.c F: hw/pci-host/bonito.c -F: hw/usb/vt82c686-uhci-pci.c -F: include/hw/isa/vt82c686.h +F: include/hw/pci-host/bonito.h F: tests/avocado/machine_mips_fuloong2e.py Loongson-3 virtual platforms @@ -1262,6 +1391,7 @@ F: hw/intc/loongson_liointc.c F: hw/mips/loongson3_bootp.c F: hw/mips/loongson3_bootp.h F: hw/mips/loongson3_virt.c +F: include/hw/intc/loongson_liointc.h F: tests/avocado/machine_mips_loongson3v.py Boston @@ -1278,25 +1408,30 @@ OpenRISC Machines or1k-sim M: Jia Liu S: Maintained +F: docs/system/openrisc/or1k-sim.rst +F: hw/intc/ompic.c F: hw/openrisc/openrisc_sim.c PowerPC Machines ---------------- -405 (ref405ep and taihu) +405 (ref405ep) L: qemu-ppc@nongnu.org S: Orphan -F: hw/ppc/ppc405_boards.c +F: hw/ppc/ppc405* +F: tests/avocado/ppc_405.py Bamboo L: qemu-ppc@nongnu.org S: Orphan F: hw/ppc/ppc440_bamboo.c +F: hw/pci-host/ppc4xx_pci.c F: tests/avocado/ppc_bamboo.py e500 L: qemu-ppc@nongnu.org S: Orphan F: hw/ppc/e500* +F: hw/ppc/ppce500_spin.c F: hw/gpio/mpc8xxx.c F: hw/i2c/mpc_i2c.c F: hw/net/fsl_etsec/ @@ -1304,8 +1439,9 @@ F: hw/pci-host/ppce500.c F: include/hw/ppc/ppc_e500.h F: include/hw/pci-host/ppce500.h F: pc-bios/u-boot.e500 -F: hw/intc/openpic_kvm.h +F: hw/intc/openpic_kvm.c F: include/hw/ppc/openpic_kvm.h +F: docs/system/ppc/ppce500.rst mpc8544ds L: qemu-ppc@nongnu.org @@ -1325,9 +1461,11 @@ F: hw/pci-bridge/dec.[hc] F: hw/misc/macio/ F: hw/misc/mos6522.c F: hw/nvram/mac_nvram.c +F: hw/ppc/fw_cfg.c F: hw/input/adb* F: include/hw/misc/macio/ F: include/hw/misc/mos6522.h +F: include/hw/nvram/mac_nvram.h F: include/hw/ppc/mac_dbdma.h F: include/hw/pci-host/uninorth.h F: include/hw/input/adb* @@ -1345,6 +1483,7 @@ F: hw/intc/heathrow_pic.c F: hw/input/adb* F: include/hw/intc/heathrow_pic.h F: include/hw/input/adb* +F: include/hw/pci-host/grackle.h F: pc-bios/qemu_vga.ndrv PReP @@ -1365,16 +1504,20 @@ F: include/hw/rtc/m48t59.h F: tests/avocado/ppc_prep_40p.py sPAPR (pseries) -M: Cédric Le Goater -M: Daniel Henrique Barboza +M: Nicholas Piggin +R: Daniel Henrique Barboza R: David Gibson -R: Greg Kurz +R: Harsh Prateek Bora L: qemu-ppc@nongnu.org -S: Maintained +S: Odd Fixes F: hw/*/spapr* F: include/hw/*/spapr* F: hw/*/xics* F: include/hw/*/xics* +F: include/hw/ppc/fdt.h +F: hw/ppc/fdt.c +F: include/hw/ppc/pef.h +F: hw/ppc/pef.c F: pc-bios/slof.bin F: docs/system/ppc/pseries.rst F: docs/specs/ppc-spapr-* @@ -1383,11 +1526,14 @@ F: tests/qtest/libqos/*spapr* F: tests/qtest/rtas* F: tests/qtest/libqos/rtas* F: tests/avocado/ppc_pseries.py +F: tests/avocado/ppc_hv_tests.py PowerNV (Non-Virtualized) M: Cédric Le Goater +M: Nicholas Piggin +R: Frédéric Barrat L: qemu-ppc@nongnu.org -S: Maintained +S: Odd Fixes F: docs/system/ppc/powernv.rst F: hw/ppc/pnv* F: hw/intc/pnv* @@ -1398,6 +1544,14 @@ F: include/hw/pci-host/pnv* F: pc-bios/skiboot.lid F: tests/qtest/pnv* +pca955x +M: Glenn Miles +L: qemu-ppc@nongnu.org +L: qemu-arm@nongnu.org +S: Odd Fixes +F: hw/gpio/pca955*.c +F: include/hw/gpio/pca955*.h + virtex_ml507 M: Edgar E. Iglesias L: qemu-ppc@nongnu.org @@ -1410,13 +1564,15 @@ M: BALATON Zoltan L: qemu-ppc@nongnu.org S: Maintained F: hw/ppc/sam460ex.c -F: hw/ppc/ppc440_pcix.c +F: hw/ppc/ppc440_uc.c +F: hw/pci-host/ppc440_pcix.c F: hw/display/sm501* F: hw/ide/sii3112.c F: hw/rtc/m41t80.c F: pc-bios/canyonlands.dt[sb] F: pc-bios/u-boot-sam460ex-20100605.bin F: roms/u-boot-sam460ex +F: docs/system/ppc/amigang.rst pegasos2 M: BALATON Zoltan @@ -1427,14 +1583,19 @@ F: hw/pci-host/mv64361.c F: hw/pci-host/mv643xx.h F: include/hw/pci-host/mv64361.h -Virtual Open Firmware (VOF) -M: Alexey Kardashevskiy -R: Cédric Le Goater -R: Daniel Henrique Barboza -R: David Gibson -R: Greg Kurz +amigaone +M: BALATON Zoltan L: qemu-ppc@nongnu.org S: Maintained +F: hw/ppc/amigaone.c +F: hw/pci-host/articia.c +F: include/hw/pci-host/articia.h + +Virtual Open Firmware (VOF) +M: Alexey Kardashevskiy +R: David Gibson +L: qemu-ppc@nongnu.org +S: Odd Fixes F: hw/ppc/spapr_vof* F: hw/ppc/vof* F: include/hw/ppc/vof* @@ -1456,6 +1617,7 @@ Microchip PolarFire SoC Icicle Kit M: Bin Meng L: qemu-riscv@nongnu.org S: Supported +F: docs/system/riscv/microchip-icicle-kit.rst F: hw/riscv/microchip_pfsoc.c F: hw/char/mchp_pfsoc_mmuart.c F: hw/misc/mchp_pfsoc_dmc.c @@ -1471,6 +1633,7 @@ Shakti C class SoC M: Vijai Kumar K L: qemu-riscv@nongnu.org S: Supported +F: docs/system/riscv/shakti-c.rst F: hw/riscv/shakti_c.c F: hw/char/shakti_uart.c F: include/hw/riscv/shakti_c.h @@ -1482,6 +1645,7 @@ M: Bin Meng M: Palmer Dabbelt L: qemu-riscv@nongnu.org S: Supported +F: docs/system/riscv/sifive_u.rst F: hw/*/*sifive*.c F: include/hw/*/*sifive*.h @@ -1506,6 +1670,7 @@ F: hw/intc/sh_intc.c F: hw/pci-host/sh_pci.c F: hw/timer/sh_timer.c F: include/hw/sh4/sh_intc.h +F: include/hw/timer/tmu012.h Shix R: Yoshinori Sato @@ -1556,7 +1721,7 @@ F: hw/rtc/sun4v-rtc.c F: include/hw/rtc/sun4v-rtc.h Leon3 -M: Fabien Chouteau +M: Clément Chigot M: Frederic Konrad S: Maintained F: hw/sparc/leon3.c @@ -1629,6 +1794,16 @@ F: hw/s390x/event-facility.c F: hw/s390x/sclp*.c L: qemu-s390x@nongnu.org +S390 CPU topology +M: Nina Schoetterl-Glausch +S: Supported +F: include/hw/s390x/cpu-topology.h +F: hw/s390x/cpu-topology.c +F: target/s390x/kvm/stsi-topology.c +F: docs/devel/s390-cpu-topology.rst +F: docs/system/s390x/cpu-topology.rst +F: tests/avocado/s390_topology.py + X86 Machines ------------ PC @@ -1643,17 +1818,17 @@ F: hw/pci-host/pam.c F: include/hw/pci-host/i440fx.h F: include/hw/pci-host/q35.h F: include/hw/pci-host/pam.h -F: hw/isa/piix3.c +F: hw/isa/piix.c F: hw/isa/lpc_ich9.c F: hw/i2c/smbus_ich9.c F: hw/acpi/piix4.c -F: hw/acpi/ich9.c -F: include/hw/acpi/ich9.h +F: hw/acpi/ich9*.c +F: include/hw/acpi/ich9*.h +F: include/hw/southbridge/ich9.h F: include/hw/southbridge/piix.h -F: hw/misc/sga.c F: hw/isa/apm.c F: include/hw/isa/apm.h -F: tests/unit/test-x86-cpuid.c +F: tests/unit/test-x86-topo.c F: tests/qtest/test-x86-cpuid-compat.c PC Chipset @@ -1678,10 +1853,12 @@ F: hw/rtc/mc146818rtc* F: hw/watchdog/wdt_ib700.c F: hw/watchdog/wdt_i6300esb.c F: include/hw/display/vga.h -F: include/hw/char/parallel.h +F: include/hw/char/parallel*.h F: include/hw/dma/i8257.h F: include/hw/i2c/pm_smbus.h F: include/hw/input/i8042.h +F: include/hw/intc/ioapic* +F: include/hw/intc/i8259.h F: include/hw/isa/i8259_internal.h F: include/hw/isa/superio.h F: include/hw/timer/hpet.h @@ -1700,11 +1877,11 @@ F: pc-bios/bios-microvm.bin Machine core M: Eduardo Habkost M: Marcel Apfelbaum -R: Philippe Mathieu-Daudé +R: Philippe Mathieu-Daudé R: Yanan Wang S: Supported -F: cpu.c -F: hw/core/cpu.c +F: hw/core/cpu-common.c +F: hw/core/cpu-sysemu.c F: hw/core/machine-qmp-cmds.c F: hw/core/machine.c F: hw/core/machine-smp.c @@ -1712,6 +1889,7 @@ F: hw/core/null-machine.c F: hw/core/numa.c F: hw/cpu/cluster.c F: qapi/machine.json +F: qapi/machine-common.json F: qapi/machine-target.json F: include/hw/boards.h F: include/hw/core/cpu.h @@ -1737,6 +1915,7 @@ M: Max Filippov S: Maintained F: hw/xtensa/xtfpga.c F: hw/net/opencores_eth.c +F: include/hw/xtensa/mx_pic.h Devices ------- @@ -1751,23 +1930,23 @@ F: tests/qtest/intel-hda-test.c F: tests/qtest/fuzz-sb16-test.c Xilinx CAN -M: Vikram Garhwal -M: Francisco Iglesias +M: Vikram Garhwal +M: Francisco Iglesias S: Maintained F: hw/net/can/xlnx-* F: include/hw/net/xlnx-* -F: tests/qtest/xlnx-can-test* +F: tests/qtest/xlnx-can*-test* EDU M: Jiri Slaby S: Maintained F: hw/misc/edu.c +F: docs/specs/edu.rst IDE M: John Snow L: qemu-block@nongnu.org -S: Supported -F: include/hw/ide.h +S: Odd Fixes F: include/hw/ide/ F: hw/ide/ F: hw/block/block.c @@ -1791,7 +1970,7 @@ T: git https://github.com/cminyard/qemu.git master-ipmi-rebase Floppy M: John Snow L: qemu-block@nongnu.org -S: Supported +S: Odd Fixes F: hw/block/fdc.c F: hw/block/fdc-internal.h F: hw/block/fdc-isa.c @@ -1832,14 +2011,20 @@ F: qapi/pci.json F: docs/pci* F: docs/specs/*pci* +PCIE DOE +M: Huai-Cheng Kuo +M: Chris Browy +S: Supported +F: include/hw/pci/pcie_doe.h +F: hw/pci/pcie_doe.c + ACPI/SMBIOS M: Michael S. Tsirkin M: Igor Mammedov -R: Ani Sinha +R: Ani Sinha S: Supported F: include/hw/acpi/* F: include/hw/firmware/smbios.h -F: hw/mem/* F: hw/acpi/* F: hw/smbios/* F: hw/i386/acpi-build.[hc] @@ -1850,15 +2035,36 @@ F: tests/qtest/acpi-utils.[hc] F: tests/data/acpi/ F: docs/specs/acpi_cpu_hotplug.rst F: docs/specs/acpi_mem_hotplug.rst +F: docs/specs/acpi_nvdimm.rst F: docs/specs/acpi_pci_hotplug.rst F: docs/specs/acpi_hw_reduced_hotplug.rst +ARM ACPI Subsystem +M: Shannon Zhao +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/virt-acpi-build.c + +RISC-V ACPI Subsystem +M: Sunil V L +L: qemu-riscv@nongnu.org +S: Maintained +F: hw/riscv/virt-acpi-build.c + ACPI/VIOT M: Jean-Philippe Brucker S: Supported F: hw/acpi/viot.c F: hw/acpi/viot.h +ACPI/AVOCADO/BIOSBITS +M: Ani Sinha +M: Michael S. Tsirkin +S: Supported +F: tests/avocado/acpi-bits/* +F: tests/avocado/acpi-bits.py +F: docs/devel/acpi-bits.rst + ACPI/HEST/GHES R: Dongjiu Geng L: qemu-arm@nongnu.org @@ -1870,8 +2076,11 @@ F: docs/specs/acpi_hest_ghes.rst ppc4xx L: qemu-ppc@nongnu.org S: Orphan -F: hw/ppc/ppc4*.c +F: hw/ppc/ppc4xx*.c +F: hw/ppc/ppc440_uc.c +F: hw/ppc/ppc440.h F: hw/i2c/ppc4xx_i2c.c +F: include/hw/pci-host/ppc4xx.h F: include/hw/ppc/ppc4xx.h F: include/hw/i2c/ppc4xx_i2c.h F: hw/intc/ppc-uic.c @@ -1882,6 +2091,7 @@ M: Marc-André Lureau R: Paolo Bonzini S: Odd Fixes F: hw/char/ +F: include/hw/char/ Network devices M: Jason Wang @@ -1893,7 +2103,7 @@ F: docs/virtio-net-failover.rst T: git https://github.com/jasowang/qemu.git net Parallel NOR Flash devices -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé T: git https://gitlab.com/philmd/qemu.git pflash-next S: Maintained F: hw/block/pflash_cfi*.c @@ -1915,7 +2125,7 @@ SSI M: Alistair Francis S: Maintained F: hw/ssi/* -F: hw/block/m25p80.c +F: hw/block/m25p80* F: include/hw/ssi/ssi.h X: hw/ssi/xilinx_* F: tests/qtest/m25p80-test.c @@ -1926,7 +2136,7 @@ S: Maintained F: hw/ssi/xilinx_* SD (Secure Card) -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé M: Bin Meng L: qemu-block@nongnu.org S: Odd Fixes @@ -1955,11 +2165,12 @@ F: hw/usb/dev-serial.c VFIO M: Alex Williamson +M: Cédric Le Goater S: Supported F: hw/vfio/* F: include/hw/vfio/ F: docs/igd-assign.txt -F: docs/devel/vfio-migration.rst +F: docs/devel/migration/vfio.rst vfio-ccw M: Eric Farman @@ -1984,6 +2195,17 @@ F: hw/vfio/ap.c F: docs/system/s390x/vfio-ap.rst L: qemu-s390x@nongnu.org +iommufd +M: Yi Liu +M: Eric Auger +M: Zhenzhong Duan +S: Supported +F: backends/iommufd.c +F: include/sysemu/iommufd.h +F: include/qemu/chardev_open.h +F: util/chardev_open.c +F: docs/devel/vfio-iommufd.rst + vhost M: Michael S. Tsirkin S: Supported @@ -1993,6 +2215,11 @@ F: docs/interop/vhost-user.rst F: contrib/vhost-user-*/ F: backends/vhost-user.c F: include/sysemu/vhost-user-backend.h +F: subprojects/libvhost-user/ + +vhost-shadow-virtqueue +R: Eugenio Pérez +F: hw/virtio/vhost-shadow-virtqueue.* virtio M: Michael S. Tsirkin @@ -2000,8 +2227,11 @@ S: Supported F: hw/*/virtio* F: hw/virtio/Makefile.objs F: hw/virtio/trace-events +F: qapi/virtio.json F: net/vhost-user.c F: include/hw/virtio/ +F: docs/devel/virtio* +F: docs/devel/migration/virtio.rst virtio-balloon M: Michael S. Tsirkin @@ -2010,28 +2240,38 @@ S: Maintained F: docs/interop/virtio-balloon-stats.rst F: hw/virtio/virtio-balloon*.c F: include/hw/virtio/virtio-balloon.h -F: softmmu/balloon.c +F: system/balloon.c F: include/sysemu/balloon.h virtio-9p M: Greg Kurz M: Christian Schoenebeck -S: Odd Fixes +S: Maintained W: https://wiki.qemu.org/Documentation/9p F: hw/9pfs/ X: hw/9pfs/xen-9p* +X: hw/9pfs/9p-proxy* F: fsdev/ -F: docs/tools/virtfs-proxy-helper.rst +X: fsdev/virtfs-proxy-helper.c F: tests/qtest/virtio-9p-test.c +F: tests/qtest/libqos/virtio-9p* T: git https://gitlab.com/gkurz/qemu.git 9p-next T: git https://github.com/cschoenebeck/qemu.git 9p.next +virtio-9p-proxy +F: hw/9pfs/9p-proxy* +F: fsdev/virtfs-proxy-helper.c +F: docs/tools/virtfs-proxy-helper.rst +S: Obsolete + virtio-blk M: Stefan Hajnoczi L: qemu-block@nongnu.org S: Supported +F: hw/block/virtio-blk-common.c F: hw/block/virtio-blk.c F: hw/block/dataplane/* +F: include/hw/virtio/virtio-blk-common.h F: tests/qtest/virtio-blk-test.c T: git https://github.com/stefanha/qemu.git block @@ -2046,21 +2286,26 @@ T: git https://gitlab.com/cohuck/qemu.git s390-next T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org +virtio-dmabuf +M: Albert Esteve +S: Supported +F: hw/display/virtio-dmabuf.c +F: include/hw/virtio/virtio-dmabuf.h +F: tests/unit/test-virtio-dmabuf.c + 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/tools/virtiofsd.rst -L: virtio-fs@redhat.com +L: virtio-fs@lists.linux.dev virtio-input M: Gerd Hoffmann S: Odd Fixes -F: hw/input/vhost-user-input.c +F: docs/system/devices/vhost-user-input.rst F: hw/input/virtio-input*.c +F: hw/virtio/vhost-user-input.c F: include/hw/virtio/virtio-input.h F: contrib/vhost-user-input/* @@ -2089,15 +2334,43 @@ F: include/sysemu/rng*.h F: backends/rng*.c F: tests/qtest/virtio-rng-test.c +vhost-user-stubs +M: Alex Bennée +S: Maintained +F: hw/virtio/vhost-user-base.c +F: hw/virtio/vhost-user-device* + vhost-user-rng M: Mathieu Poirier S: Supported -F: docs/tools/vhost-user-rng.rst +F: docs/system/devices/vhost-user-rng.rst F: hw/virtio/vhost-user-rng.c F: hw/virtio/vhost-user-rng-pci.c F: include/hw/virtio/vhost-user-rng.h F: tools/vhost-user-rng/* +vhost-user-gpio +M: Alex Bennée +R: Viresh Kumar +S: Maintained +F: hw/virtio/vhost-user-gpio* +F: include/hw/virtio/vhost-user-gpio.h +F: tests/qtest/libqos/virtio-gpio.* + +vhost-user-snd +M: Alex Bennée +R: Manos Pitsidianakis +S: Maintained +F: hw/virtio/vhost-user-snd* +F: include/hw/virtio/vhost-user-snd.h + +vhost-user-scmi +R: mzamazal@redhat.com +S: Supported +F: hw/virtio/vhost-user-scmi* +F: include/hw/virtio/vhost-user-scmi.h +F: tests/qtest/libqos/virtio-scmi.* + virtio-crypto M: Gonglei S: Supported @@ -2105,6 +2378,13 @@ F: hw/virtio/virtio-crypto.c F: hw/virtio/virtio-crypto-pci.c F: include/hw/virtio/virtio-crypto.h +virtio based memory device +M: David Hildenbrand +S: Supported +F: hw/virtio/virtio-md-pci.c +F: include/hw/virtio/virtio-md-pci.h +F: stubs/virtio-md-pci.c + virtio-mem M: David Hildenbrand S: Supported @@ -2114,17 +2394,34 @@ F: hw/virtio/virtio-mem-pci.h F: hw/virtio/virtio-mem-pci.c F: include/hw/virtio/virtio-mem.h +virtio-snd +M: Gerd Hoffmann +R: Manos Pitsidianakis +S: Supported +F: hw/audio/virtio-snd.c +F: hw/audio/virtio-snd-pci.c +F: include/hw/audio/virtio-snd.h +F: docs/system/devices/virtio-snd.rst + nvme M: Keith Busch M: Klaus Jensen +R: Jesper Devantier L: qemu-block@nongnu.org S: Supported F: hw/nvme/* F: include/block/nvme.h F: tests/qtest/nvme-test.c -F: docs/system/nvme.rst +F: docs/system/devices/nvme.rst T: git git://git.infradead.org/qemu-nvme.git nvme-next +ufs +M: Jeuk Kim +S: Supported +F: hw/ufs/* +F: include/block/ufs.h +F: tests/qtest/ufs-test.c + megasas M: Hannes Reinecke L: qemu-block@nongnu.org @@ -2136,6 +2433,7 @@ F: tests/qtest/fuzz-megasas-test.c Network packet abstractions M: Dmitry Fleytman +R: Akihiko Odaki S: Maintained F: include/net/eth.h F: net/eth.c @@ -2145,9 +2443,15 @@ F: hw/net/net_tx_pkt* Vmware M: Dmitry Fleytman S: Maintained +F: docs/specs/vmw_pvscsi-spec.txt +F: hw/display/vmware_vga.c F: hw/net/vmxnet* F: hw/scsi/vmw_pvscsi* +F: pc-bios/efi-vmxnet3.rom +F: pc-bios/vgabios-vmware.bin +F: roms/config.vga-vmware F: tests/qtest/vmxnet3-test.c +F: docs/specs/vwm_pvscsi-spec.rst Rocker M: Jiri Pirko @@ -2157,25 +2461,30 @@ F: qapi/rocker.json F: tests/rocker/ F: docs/specs/rocker.txt -NVDIMM -M: Xiao Guangrong -S: Maintained -F: hw/acpi/nvdimm.c -F: hw/mem/nvdimm.c -F: include/hw/mem/nvdimm.h -F: docs/nvdimm.txt -F: docs/specs/acpi_nvdimm.rst - e1000x M: Dmitry Fleytman +R: Akihiko Odaki S: Maintained F: hw/net/e1000x* e1000e M: Dmitry Fleytman +R: Akihiko Odaki S: Maintained F: hw/net/e1000e* F: tests/qtest/fuzz-e1000e-test.c +F: tests/qtest/e1000e-test.c +F: tests/qtest/libqos/e1000e.* + +igb +M: Akihiko Odaki +R: Sriram Yagnaraman +S: Maintained +F: docs/system/devices/igb.rst +F: hw/net/igb* +F: tests/avocado/netdev-ethtool.py +F: tests/qtest/igb-test.c +F: tests/qtest/libqos/igb.c eepro100 M: Stefan Weil @@ -2194,10 +2503,17 @@ S: Maintained F: hw/i2c/i2c_mux_pca954x.c F: include/hw/i2c/i2c_mux_pca954x.h +pcf8574 +M: Dmitrii Sharikhin +S: Maintained +F: hw/gpio/pcf8574.c +F: include/gpio/pcf8574.h + Generic Loader M: Alistair Francis S: Maintained F: hw/core/generic-loader.c +F: hw/core/uboot_image.h F: include/hw/core/generic-loader.h F: docs/system/generic-loader.rst @@ -2226,19 +2542,18 @@ S: Orphan R: Ani Sinha F: hw/acpi/vmgenid.c F: include/hw/acpi/vmgenid.h -F: docs/specs/vmgenid.txt +F: docs/specs/vmgenid.rst F: tests/qtest/vmgenid-test.c -F: stubs/vmgenid.c LED -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé S: Maintained F: include/hw/misc/led.h F: hw/misc/led.c Unimplemented device M: Peter Maydell -R: Philippe Mathieu-Daudé +R: Philippe Mathieu-Daudé R: Ani Sinha S: Maintained F: include/hw/misc/unimp.h @@ -2246,7 +2561,7 @@ F: hw/misc/unimp.c Empty slot M: Artyom Tarasenko -R: Philippe Mathieu-Daudé +R: Philippe Mathieu-Daudé R: Ani Sinha S: Maintained F: include/hw/misc/empty_slot.h @@ -2259,6 +2574,7 @@ F: hw/display/vga* F: hw/display/bochs-display.c F: include/hw/display/vga.h F: include/hw/display/bochs-vbe.h +F: docs/specs/standard-vga.rst ramfb M: Gerd Hoffmann @@ -2272,18 +2588,21 @@ S: Odd Fixes F: hw/display/virtio-gpu* F: hw/display/virtio-vga.* F: include/hw/virtio/virtio-gpu.h +F: docs/system/devices/virtio-gpu.rst vhost-user-blk -M: Raphael Norwitz +M: Raphael Norwitz S: Maintained F: contrib/vhost-user-blk/ F: contrib/vhost-user-scsi/ F: hw/block/vhost-user-blk.c +F: hw/block/virtio-blk-common.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 +F: include/hw/virtio/virtio-blk-common.h vhost-user-gpu M: Marc-André Lureau @@ -2308,13 +2627,22 @@ F: qemu-edid.c PIIX4 South Bridge (i82371AB) M: Hervé Poussineau -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé S: Maintained -F: hw/isa/piix4.c +F: hw/isa/piix.c F: include/hw/southbridge/piix.h +VIA South Bridges (VT82C686B, VT8231) +M: BALATON Zoltan +M: Philippe Mathieu-Daudé +R: Jiaxun Yang +S: Maintained +F: hw/isa/vt82c686.c +F: hw/usb/vt82c686-uhci-pci.c +F: include/hw/isa/vt82c686.h + Firmware configuration (fw_cfg) -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé R: Gerd Hoffmann S: Supported F: docs/specs/fw_cfg.txt @@ -2328,8 +2656,9 @@ T: git https://github.com/philmd/qemu.git fw_cfg-next XIVE M: Cédric Le Goater +R: Frédéric Barrat L: qemu-ppc@nongnu.org -S: Supported +S: Odd Fixes F: hw/*/*xive* F: include/hw/*/*xive* F: docs/*/*xive* @@ -2362,6 +2691,7 @@ W: https://canbus.pages.fel.cvut.cz/ F: net/can/* F: hw/net/can/* F: include/net/can_*.h +F: docs/system/devices/can.rst OpenPIC interrupt controller M: Mark Cave-Ayland @@ -2370,13 +2700,13 @@ F: hw/intc/openpic.c F: include/hw/ppc/openpic.h MIPS CPS -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé S: Odd Fixes F: hw/misc/mips_* F: include/hw/misc/mips_* MIPS GIC -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé S: Odd Fixes F: hw/intc/mips_gic.c F: hw/timer/mips_gictimer.c @@ -2405,7 +2735,7 @@ M: Halil Pasic M: Christian Borntraeger S: Supported F: hw/s390x/storage-keys.h -F: hw/390x/s390-skeys*.c +F: hw/s390x/s390-skeys*.c L: qemu-s390x@nongnu.org S390 storage attribute device @@ -2413,7 +2743,7 @@ M: Halil Pasic M: Christian Borntraeger S: Supported F: hw/s390x/storage-attributes.h -F: hw/s390/s390-stattrib*.c +F: hw/s390x/s390-stattrib*.c L: qemu-s390x@nongnu.org S390 floating interrupt controller @@ -2425,10 +2755,27 @@ F: hw/intc/s390_flic*.c F: include/hw/s390x/s390_flic.h L: qemu-s390x@nongnu.org +CanoKey +M: Hongren (Zenithal) Zheng +S: Maintained +R: Canokeys.org +F: hw/usb/canokey.c +F: hw/usb/canokey.h +F: docs/system/devices/canokey.rst + +Hyper-V Dynamic Memory Protocol +M: Maciej S. Szmigiero +S: Supported +F: hw/hyperv/hv-balloon*.c +F: hw/hyperv/hv-balloon*.h +F: include/hw/hyperv/dynmem-proto.h +F: include/hw/hyperv/hv-balloon.h + Subsystems ---------- Overall Audio backends M: Gerd Hoffmann +M: Marc-André Lureau S: Odd Fixes F: audio/ X: audio/alsaaudio.c @@ -2438,6 +2785,7 @@ X: audio/jackaudio.c X: audio/ossaudio.c X: audio/paaudio.c X: audio/sdlaudio.c +X: audio/sndioaudio.c X: audio/spiceaudio.c F: qapi/audio.json @@ -2449,9 +2797,9 @@ F: audio/alsaaudio.c Core Audio framework backend M: Gerd Hoffmann -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé R: Christian Schoenebeck -R: Akihiko Odaki +R: Akihiko Odaki S: Odd Fixes F: audio/coreaudio.c @@ -2482,6 +2830,12 @@ R: Thomas Huth S: Odd Fixes F: audio/sdlaudio.c +Sndio Audio backend +M: Gerd Hoffmann +R: Alexandre Ratchov +S: Odd Fixes +F: audio/sndioaudio.c + Block layer core M: Kevin Wolf M: Hanna Reitz @@ -2490,7 +2844,10 @@ S: Supported F: block* F: block/ F: hw/block/ +F: qapi/block*.json +F: qapi/transaction.json F: include/block/ +F: include/sysemu/block-*.h F: qemu-img* F: docs/tools/qemu-img.rst F: qemu-io* @@ -2517,11 +2874,13 @@ S: Supported F: util/async.c F: util/aio-*.c F: util/aio-*.h +F: util/defer-call.c F: util/fdmon-*.c F: block/io.c F: migration/block* F: include/block/aio.h F: include/block/aio-wait.h +F: include/qemu/defer-call.h F: scripts/qemugdb/aio.py F: tests/unit/test-fdmon-epoll.c T: git https://github.com/stefanha/qemu.git block @@ -2536,7 +2895,7 @@ F: scsi/* Block Jobs M: John Snow -M: Vladimir Sementsov-Ogievskiy +M: Vladimir Sementsov-Ogievskiy L: qemu-block@nongnu.org S: Supported F: blockjob.c @@ -2561,28 +2920,20 @@ F: block/aio_task.c F: util/qemu-co-shared-resource.c F: include/qemu/co-shared-resource.h T: git https://gitlab.com/jsnow/qemu.git jobs -T: git https://src.openvz.org/scm/~vsementsov/qemu.git jobs - -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 -T: git https://repo.or.cz/qemu/armbru.git block-next +T: git https://gitlab.com/vsementsov/qemu.git block Compute Express Link -M: Ben Widawsky M: Jonathan Cameron +R: Fan Ni S: Supported F: hw/cxl/ +F: hw/mem/cxl_type3.c F: include/hw/cxl/ +F: qapi/cxl.json Dirty Bitmaps M: Eric Blake -M: Vladimir Sementsov-Ogievskiy +M: Vladimir Sementsov-Ogievskiy R: John Snow L: qemu-block@nongnu.org S: Supported @@ -2596,6 +2947,7 @@ F: util/hbitmap.c F: tests/unit/test-hbitmap.c F: docs/interop/bitmaps.rst T: git https://repo.or.cz/qemu/ericb.git bitmaps +T: git https://gitlab.com/vsementsov/qemu.git block Character device backends M: Marc-André Lureau @@ -2633,7 +2985,7 @@ Device Tree M: Alistair Francis R: David Gibson S: Maintained -F: softmmu/device_tree.c +F: system/device_tree.c F: include/sysemu/device_tree.h Dump @@ -2648,6 +3000,7 @@ F: include/sysemu/dump.h F: qapi/dump.json F: scripts/dump-guest-memory.py F: stubs/dump.c +F: docs/specs/vmcoreinfo.rst Error reporting M: Markus Armbruster @@ -2656,7 +3009,7 @@ F: include/qapi/error.h F: include/qemu/error-report.h F: qapi/error.json F: util/error.c -F: util/qemu-error.c +F: util/error-report.c F: scripts/coccinelle/err-bad-newline.cocci F: scripts/coccinelle/error-use-after-free.cocci F: scripts/coccinelle/error_propagate_null.cocci @@ -2666,18 +3019,22 @@ F: scripts/coccinelle/errp-guard.cocci GDB stub M: Alex Bennée -R: Philippe Mathieu-Daudé +R: Philippe Mathieu-Daudé S: Maintained -F: gdbstub* +F: docs/system/gdb.rst +F: gdbstub/* F: include/exec/gdbstub.h +F: include/gdbstub/* F: gdb-xml/ -F: tests/tcg/multiarch/gdbstub/ +F: tests/tcg/multiarch/gdbstub/* +F: scripts/feature_to_c.py +F: scripts/probe-gdb-support.py Memory API M: Paolo Bonzini M: Peter Xu M: David Hildenbrand -R: Philippe Mathieu-Daudé +R: Philippe Mathieu-Daudé S: Supported F: include/exec/ioport.h F: include/exec/memop.h @@ -2685,14 +3042,28 @@ F: include/exec/memory.h F: include/exec/ram_addr.h F: include/exec/ramblock.h F: include/sysemu/memory_mapping.h -F: softmmu/dma-helpers.c -F: softmmu/ioport.c -F: softmmu/memory.c -F: softmmu/memory_mapping.c -F: softmmu/physmem.c +F: system/dma-helpers.c +F: system/ioport.c +F: system/memory.c +F: system/memory_mapping.c +F: system/physmem.c F: include/exec/memory-internal.h F: scripts/coccinelle/memory-region-housekeeping.cocci +Memory devices +M: David Hildenbrand +M: Igor Mammedov +R: Xiao Guangrong +S: Supported +F: hw/mem/memory-device.c +F: hw/mem/nvdimm.c +F: hw/mem/pc-dimm.c +F: include/hw/mem/memory-device.h +F: include/hw/mem/nvdimm.h +F: include/hw/mem/pc-dimm.h +F: stubs/memory_device.c +F: docs/nvdimm.txt + SPICE M: Gerd Hoffmann S: Odd Fixes @@ -2706,47 +3077,49 @@ F: docs/spice-port-fqdn.txt Graphics M: Gerd Hoffmann +M: Marc-André Lureau S: Odd Fixes F: ui/ F: include/ui/ F: qapi/ui.json F: util/drm.c +F: docs/devel/ui.rst Cocoa graphics M: Peter Maydell -M: Philippe Mathieu-Daudé -R: Akihiko Odaki +M: Philippe Mathieu-Daudé +R: Akihiko Odaki S: Odd Fixes F: ui/cocoa.m Main loop M: Paolo Bonzini S: Maintained -F: include/exec/gen-icount.h F: include/qemu/main-loop.h F: include/sysemu/runstate.h F: include/sysemu/runstate-action.h F: util/main-loop.c -F: util/qemu-timer.c -F: softmmu/vl.c -F: softmmu/main.c -F: softmmu/cpus.c -F: softmmu/cpu-throttle.c -F: softmmu/cpu-timers.c -F: softmmu/icount.c -F: softmmu/runstate-action.c +F: util/qemu-timer*.c +F: system/vl.c +F: system/main.c +F: system/cpus.c +F: system/cpu-throttle.c +F: system/cpu-timers.c +F: system/runstate* F: qapi/run-state.json Read, Copy, Update (RCU) M: Paolo Bonzini S: Maintained +F: docs/devel/lockcnt.txt +F: docs/devel/rcu.txt F: include/qemu/rcu*.h F: tests/unit/rcutorture.c F: tests/unit/test-rcu-*.c F: util/rcu.c Human Monitor (HMP) -M: Dr. David Alan Gilbert +M: Dr. David Alan Gilbert S: Maintained F: monitor/monitor-internal.h F: monitor/misc.c @@ -2776,19 +3149,26 @@ W: http://info.iet.unipi.it/~luigi/netmap/ S: Maintained F: net/netmap.c +AF_XDP network backend +R: Ilya Maximets +F: net/af-xdp.c + Host Memory Backends M: David Hildenbrand M: Igor Mammedov S: Maintained F: backends/hostmem*.c F: include/sysemu/hostmem.h +F: docs/system/vm-templating.rst T: git https://gitlab.com/ehabkost/qemu.git machine-next Cryptodev Backends M: Gonglei +M: zhenwei pi S: Maintained F: include/sysemu/cryptodev*.h F: backends/cryptodev*.c +F: qapi/cryptodev.json Python library M: John Snow @@ -2806,16 +3186,17 @@ F: scripts/*.py F: tests/*.py Benchmark util -M: Vladimir Sementsov-Ogievskiy +M: Vladimir Sementsov-Ogievskiy S: Maintained F: scripts/simplebench/ -T: git https://src.openvz.org/scm/~vsementsov/qemu.git simplebench +T: git https://gitlab.com/vsementsov/qemu.git simplebench Transactions helper -M: Vladimir Sementsov-Ogievskiy +M: Vladimir Sementsov-Ogievskiy S: Maintained F: include/qemu/transactions.h F: util/transactions.c +T: git https://gitlab.com/vsementsov/qemu.git block QAPI M: Markus Armbruster @@ -2864,12 +3245,14 @@ T: git https://repo.or.cz/qemu/armbru.git qapi-next QEMU Guest Agent M: Michael Roth +M: Konstantin Kostiuk S: Maintained F: qga/ +F: contrib/systemd/qemu-guest-agent.service F: docs/interop/qemu-ga.rst F: docs/interop/qemu-ga-ref.rst F: scripts/qemu-guest-agent/ -F: tests/unit/test-qga.c +F: tests/*/test-qga* T: git https://github.com/mdroth/qemu.git qga QEMU Guest Agent Win32 @@ -2885,6 +3268,7 @@ M: Paolo Bonzini R: Daniel P. Berrange R: Eduardo Habkost S: Supported +F: docs/devel/qom.rst F: docs/qdev-device-use.txt F: hw/core/qdev* F: hw/core/bus.c @@ -2895,7 +3279,8 @@ F: include/qom/ F: qapi/qom.json F: qapi/qdev.json F: scripts/coccinelle/qom-parent-type.cocci -F: softmmu/qdev-monitor.c +F: scripts/qom-cast-macro-clean-cocci-gen.py +F: system/qdev-monitor.c F: stubs/qdev.c F: qom/ F: tests/unit/check-qom-interface.c @@ -2929,10 +3314,12 @@ M: Thomas Huth M: Laurent Vivier R: Paolo Bonzini S: Maintained -F: softmmu/qtest.c +F: system/qtest.c +F: include/sysemu/qtest.h F: accel/qtest/ F: tests/qtest/ F: docs/devel/qgraph.rst +F: docs/devel/qtest.rst X: tests/qtest/bios-tables-test* Device Fuzzing @@ -2946,6 +3333,7 @@ R: Qiuhao Li S: Maintained F: tests/qtest/fuzz/ F: tests/qtest/fuzz-*test.c +F: tests/docker/test-fuzz F: scripts/oss-fuzz/ F: hw/mem/sparse-mem.c F: docs/devel/fuzzing.rst @@ -2960,11 +3348,16 @@ F: include/hw/registerfields.h SLIRP M: Samuel Thibault S: Maintained -F: slirp/ F: net/slirp.c F: include/net/slirp.h T: git https://people.debian.org/~sthibault/qemu.git slirp +Stats +S: Orphan +F: include/sysemu/stats.h +F: stats/ +F: qapi/stats.json + Streams M: Edgar E. Iglesias S: Maintained @@ -2978,6 +3371,7 @@ F: stubs/ Tracing M: Stefan Hajnoczi +R: Mads Ynddal S: Maintained F: trace/ F: trace-events @@ -2990,16 +3384,22 @@ F: docs/tools/qemu-trace-stap.rst F: docs/devel/tracing.rst T: git https://github.com/stefanha/qemu.git tracing +Simpletrace +M: Mads Ynddal +S: Maintained +F: scripts/simpletrace.py + TPM M: Stefan Berger S: Maintained -F: tpm.c +F: system/tpm* F: hw/tpm/* F: include/hw/acpi/tpm.h F: include/sysemu/tpm* F: qapi/tpm.json F: backends/tpm/ F: tests/qtest/*tpm* +F: docs/specs/tpm.rst T: git https://github.com/stefanberger/qemu-tpm.git tpm-next Checkpatch @@ -3007,19 +3407,43 @@ S: Odd Fixes F: scripts/checkpatch.pl Migration -M: Juan Quintela -M: Dr. David Alan Gilbert +M: Peter Xu +M: Fabiano Rosas S: Maintained F: hw/core/vmstate-if.c F: include/hw/vmstate-if.h F: include/migration/ +F: include/qemu/userfaultfd.h F: migration/ F: scripts/vmstate-static-checker.py F: tests/vmstate-static-checker-data/ F: tests/qtest/migration-test.c -F: docs/devel/migration.rst +F: docs/devel/migration/ F: qapi/migration.json F: tests/migration/ +F: util/userfaultfd.c +X: migration/rdma* + +RDMA Migration +R: Li Zhijian +R: Peter Xu +S: Odd Fixes +F: migration/rdma* + +Migration dirty limit and dirty page rate +M: Hyman Huang +S: Maintained +F: system/dirtylimit.c +F: include/sysemu/dirtylimit.h +F: migration/dirtyrate.c +F: migration/dirtyrate.h +F: include/sysemu/dirtyrate.h +F: docs/devel/migration/dirty-limit.rst + +Detached LUKS header +M: Hyman Huang +S: Maintained +F: tests/qemu-iotests/tests/luks-detached-header D-Bus M: Marc-André Lureau @@ -3034,11 +3458,12 @@ F: docs/interop/dbus* F: docs/sphinx/dbus* F: docs/sphinx/fakedbusdoc.py F: tests/qtest/dbus* +F: scripts/xml-preprocess* Seccomp M: Daniel P. Berrange S: Odd Fixes -F: softmmu/qemu-seccomp.c +F: system/qemu-seccomp.c F: include/sysemu/seccomp.h F: tests/unit/test-seccomp.c @@ -3047,6 +3472,7 @@ M: Daniel P. Berrange S: Maintained F: crypto/ F: include/crypto/ +F: host/include/*/host/crypto/ F: qapi/crypto.json F: tests/unit/test-crypto-* F: tests/bench/benchmark-crypto-* @@ -3150,8 +3576,10 @@ S: Supported F: replay/* F: block/blkreplay.c F: net/filter-replay.c +F: include/exec/replay-core.h F: include/sysemu/replay.h -F: docs/replay.txt +F: docs/devel/replay.rst +F: docs/system/replay.rst F: stubs/replay.c F: tests/avocado/replay_kernel.py F: tests/avocado/replay_linux.py @@ -3166,9 +3594,16 @@ F: util/iova-tree.c elf2dmp M: Viktor Prutyanov +R: Akihiko Odaki S: Maintained F: contrib/elf2dmp/ +Overall sensors +M: Philippe Mathieu-Daudé +S: Odd Fixes +F: hw/sensor +F: include/hw/sensor + I2C and SMBus M: Corey Minyard S: Maintained @@ -3194,15 +3629,24 @@ F: tests/qtest/adm1272-test.c F: tests/qtest/max34451-test.c F: tests/qtest/isl_pmbus_vr-test.c +FSI +M: Ninad Palsule +R: Cédric Le Goater +S: Maintained +F: hw/fsi/* +F: include/hw/fsi/* +F: docs/specs/fsi.rst +F: tests/qtest/aspeed_fsi-test.c + Firmware schema specifications -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé R: Daniel P. Berrange R: Kashyap Chamarthy S: Maintained F: docs/interop/firmware.json EDK2 Firmware -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé M: Gerd Hoffmann S: Supported F: hw/i386/*ovmf* @@ -3213,18 +3657,19 @@ F: roms/edk2 F: roms/edk2-* F: tests/data/uefi-boot-images/ F: tests/uefi-test-tools/ -F: .gitlab-ci.d/edk2.yml -F: .gitlab-ci.d/edk2/ VT-d Emulation M: Michael S. Tsirkin -M: Peter Xu R: Jason Wang S: Supported F: hw/i386/intel_iommu.c F: hw/i386/intel_iommu_internal.h F: include/hw/i386/intel_iommu.h +AMD-Vi Emulation +S: Orphan +F: hw/i386/amd_iommu.? + OpenSBI Firmware M: Bin Meng S: Supported @@ -3234,7 +3679,7 @@ F: .gitlab-ci.d/opensbi/ Clock framework M: Luc Michel -R: Damien Hedde +R: Damien Hedde S: Maintained F: include/hw/clock.h F: include/hw/qdev-clock.h @@ -3243,6 +3688,16 @@ F: hw/core/clock-vmstate.c F: hw/core/qdev-clock.c F: docs/devel/clocks.rst +Reset framework +M: Peter Maydell +S: Maintained +F: include/hw/resettable.h +F: include/hw/core/resetcontainer.h +F: include/sysemu/reset.h +F: hw/core/reset.c +F: hw/core/resettable.c +F: hw/core/resetcontainer.c + Usermode Emulation ------------------ Overall usermode emulation @@ -3283,6 +3738,7 @@ TCG Plugins M: Alex Bennée R: Alexandre Iooss R: Mahmoud Mandour +R: Pierrick Bouvier S: Maintained F: docs/devel/tcg-plugins.rst F: plugins/ @@ -3295,8 +3751,6 @@ M: Richard Henderson S: Maintained L: qemu-arm@nongnu.org F: tcg/aarch64/ -F: disas/arm-a64.cc -F: disas/libvixl/ ARM TCG target M: Richard Henderson @@ -3315,7 +3769,7 @@ S: Maintained F: tcg/loongarch64/ MIPS TCG target -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé R: Aurelien Jarno R: Huacai Chen R: Jiaxun Yang @@ -3334,7 +3788,7 @@ M: Alistair Francis L: qemu-riscv@nongnu.org S: Maintained F: tcg/riscv/ -F: disas/riscv.c +F: disas/riscv.[ch] S390 TCG target M: Richard Henderson @@ -3344,7 +3798,7 @@ L: qemu-s390x@nongnu.org SPARC TCG target S: Odd Fixes -F: tcg/sparc/ +F: tcg/sparc64/ F: disas/sparc.c TCI TCG target @@ -3381,6 +3835,12 @@ L: qemu-block@nongnu.org S: Maintained F: block/vdi.c +blkio +M: Stefan Hajnoczi +L: qemu-block@nongnu.org +S: Maintained +F: block/blkio.c + iSCSI M: Ronnie Sahlberg M: Paolo Bonzini @@ -3392,7 +3852,7 @@ F: block/iscsi-opts.c Network Block Device (NBD) M: Eric Blake -M: Vladimir Sementsov-Ogievskiy +M: Vladimir Sementsov-Ogievskiy L: qemu-block@nongnu.org S: Maintained F: block/nbd* @@ -3404,7 +3864,7 @@ F: docs/interop/nbd.txt F: docs/tools/qemu-nbd.rst F: tests/qemu-iotests/tests/*nbd* T: git https://repo.or.cz/qemu/ericb.git nbd -T: git https://src.openvz.org/scm/~vsementsov/qemu.git nbd +T: git https://gitlab.com/vsementsov/qemu.git block NFS M: Peter Lieven @@ -3438,7 +3898,7 @@ F: block/null.c NVMe Block Driver M: Stefan Hajnoczi R: Fam Zheng -R: Philippe Mathieu-Daudé +R: Philippe Mathieu-Daudé L: qemu-block@nongnu.org S: Supported F: block/nvme* @@ -3448,7 +3908,7 @@ T: git https://github.com/stefanha/qemu.git block Bootdevice M: Gonglei S: Maintained -F: softmmu/bootdevice.c +F: system/bootdevice.c Quorum M: Alberto Garcia @@ -3489,13 +3949,12 @@ F: block/dmg.c parallels M: Stefan Hajnoczi M: Denis V. Lunev -M: Vladimir Sementsov-Ogievskiy L: qemu-block@nongnu.org S: Supported F: block/parallels.c F: block/parallels-ext.c F: docs/interop/parallels.txt -T: git https://src.openvz.org/scm/~vsementsov/qemu.git parallels +T: git https://src.openvz.org/scm/~den/qemu.git parallels qed M: Stefan Hajnoczi @@ -3568,6 +4027,8 @@ M: Coiby Xu S: Maintained F: block/export/vhost-user-blk-server.c F: block/export/vhost-user-blk-server.h +F: block/export/virtio-blk-handler.c +F: block/export/virtio-blk-handler.h F: include/qemu/vhost-user-server.h F: tests/qtest/libqos/vhost-user-blk.c F: tests/qtest/libqos/vhost-user-blk.h @@ -3580,6 +4041,13 @@ L: qemu-block@nongnu.org S: Supported F: block/export/fuse.c +VDUSE library and block device exports +M: Xie Yongji +S: Maintained +F: subprojects/libvduse/ +F: block/export/vduse-blk.c +F: block/export/vduse-blk.h + Replication M: Wen Congyang M: Xie Changlong @@ -3592,7 +4060,7 @@ F: docs/block-replication.txt PVRDMA M: Yuval Shaia M: Marcel Apfelbaum -S: Maintained +S: Odd Fixes F: hw/rdma/* F: hw/rdma/vmw/* F: docs/pvrdma.txt @@ -3610,7 +4078,6 @@ F: tests/tcg/aarch64/system/semiheap.c Multi-process QEMU M: Elena Ufimtseva M: Jagannathan Raman -M: John G Johnson S: Maintained F: docs/devel/multi-process.rst F: docs/system/multi-process.rst @@ -3630,12 +4097,18 @@ F: hw/remote/proxy-memory-listener.c F: include/hw/remote/proxy-memory-listener.h F: hw/remote/iohub.c F: include/hw/remote/iohub.h +F: subprojects/libvfio-user +F: hw/remote/vfio-user-obj.c +F: include/hw/remote/vfio-user-obj.h +F: hw/remote/iommu.c +F: include/hw/remote/iommu.h EBPF: M: Jason Wang R: Andrew Melnychenko R: Yuri Benditovich S: Maintained +F: docs/devel/ebpf_rss.rst F: ebpf/* F: tools/ebpf/* @@ -3643,7 +4116,7 @@ Build and test automation ------------------------- Build and test automation, general continuous integration M: Alex Bennée -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé M: Thomas Huth R: Wainer dos Santos Moschetta R: Beraldo Leal @@ -3652,11 +4125,14 @@ F: .github/workflows/lockdown.yml F: .gitlab-ci.yml F: .gitlab-ci.d/ F: .travis.yml +F: docs/devel/ci* F: scripts/ci/ F: tests/docker/ F: tests/vm/ F: tests/lcitool/ +F: tests/avocado/tuxrun_baselines.py F: scripts/archive-source.sh +F: docs/devel/testing.rst W: https://gitlab.com/qemu-project/qemu/pipelines W: https://travis-ci.org/qemu/qemu @@ -3671,20 +4147,18 @@ W: https://cirrus-ci.com/github/qemu/qemu Windows Hosted Continuous Integration M: Yonggang Luo S: Maintained -F: .cirrus.yml -W: https://cirrus-ci.com/github/qemu/qemu +F: .gitlab-ci.d/windows.yml Guest Test Compilation Support M: Alex Bennée -R: Philippe Mathieu-Daudé +R: Philippe Mathieu-Daudé S: Maintained -F: tests/tcg/Makefile -F: tests/tcg/Makefile.include +F: tests/tcg/Makefile.target Integration Testing with the Avocado framework W: https://trello.com/b/6Qi1pxVn/avocado-qemu R: Cleber Rosa -R: Philippe Mathieu-Daudé +R: Philippe Mathieu-Daudé R: Wainer dos Santos Moschetta R: Beraldo Leal S: Odd Fixes @@ -3692,9 +4166,10 @@ F: tests/avocado/ GitLab custom runner (Works On Arm Sponsored) M: Alex Bennée -M: Philippe Mathieu-Daudé +M: Philippe Mathieu-Daudé S: Maintained -F: .gitlab-ci.d/custom-runners/ubuntu-20.04-aarch64.yml +F: .gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml +F: .gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml Documentation ------------- @@ -3710,11 +4185,44 @@ F: gitdm.config F: contrib/gitdm/* Incompatible changes -R: libvir-list@redhat.com +R: devel@lists.libvirt.org F: docs/about/deprecated.rst Build System ------------ +Meson +M: Paolo Bonzini +R: Marc-André Lureau +R: Daniel P. Berrange +R: Thomas Huth +R: Philippe Mathieu-Daudé +S: Maintained +F: meson.build +F: meson_options.txt +F: scripts/meson-buildoptions.* +F: scripts/check_sparse.py +F: scripts/symlink-install-tree.py + +Top Level Makefile and configure +M: Paolo Bonzini +R: Alex Bennée +R: Thomas Huth +S: Maintained +F: Makefile +F: configure +F: scripts/mtest2make.py +F: tests/Makefile.include + +Kconfig +M: Paolo Bonzini +S: Maintained +F: scripts/minikconf.py +F: docs/devel/kconfig.rst +F: Kconfig* +F: */Kconfig* +F: hw/*/Kconfig* +F: target/*/Kconfig* + GIT submodules M: Daniel P. Berrange S: Odd Fixes @@ -3731,6 +4239,7 @@ F: docs/conf.py F: docs/*/conf.py F: docs/sphinx/ F: docs/_templates/ +F: docs/devel/docs.rst Miscellaneous ------------- @@ -3738,3 +4247,8 @@ Performance Tools and Tests M: Ahmed Karaman S: Maintained F: scripts/performance/ + +Code Coverage Tools +M: Alex Bennée +S: Odd Fixes +F: scripts/coverage/ diff --git a/Makefile b/Makefile index 3c0d89057e..02a257584b 100644 --- a/Makefile +++ b/Makefile @@ -26,9 +26,9 @@ quiet-command-run = $(if $(V),,$(if $2,printf " %-7s %s\n" $2 $3 && ))$1 quiet-@ = $(if $(V),,@) quiet-command = $(quiet-@)$(call quiet-command-run,$1,$2,$3) -UNCHECKED_GOALS := %clean TAGS cscope ctags dist \ +UNCHECKED_GOALS := TAGS gtags cscope ctags dist \ help check-help print-% \ - docker docker-% vm-help vm-test vm-build-% + docker docker-% lcitool-refresh vm-help vm-test vm-build-% all: .PHONY: all clean distclean recurse-all dist msi FORCE @@ -42,17 +42,8 @@ configure: ; ifneq ($(wildcard config-host.mak),) include config-host.mak -git-submodule-update: -.git-submodule-status: git-submodule-update config-host.mak -Makefile: .git-submodule-status - -.PHONY: git-submodule-update -git-submodule-update: -ifneq ($(GIT_SUBMODULES_ACTION),ignore) - $(call quiet-command, \ - (GIT="$(GIT)" "$(SRC_PATH)/scripts/git-submodule.sh" $(GIT_SUBMODULES_ACTION) $(GIT_SUBMODULES)), \ - "GIT","$(GIT_SUBMODULES)") -endif +include Makefile.prereqs +Makefile.prereqs: config-host.mak # 0. ensure the build tree is okay @@ -87,21 +78,22 @@ x := $(shell rm -rf meson-private meson-info meson-logs) endif # 1. ensure config-host.mak is up-to-date -config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/scripts/meson-buildoptions.sh $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION +config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/scripts/meson-buildoptions.sh $(SRC_PATH)/VERSION @echo config-host.mak is out-of-date, running configure @if test -f meson-private/coredata.dat; then \ ./config.status --skip-meson; \ else \ - ./config.status && touch build.ninja.stamp; \ + ./config.status; \ fi # 2. meson.stamp exists if meson has run at least once (so ninja reconfigure # works), but otherwise never needs to be updated + meson-private/coredata.dat: meson.stamp meson.stamp: config-host.mak @touch meson.stamp -# 3. ensure generated build files are up-to-date +# 3. ensure meson-generated build files are up-to-date ifneq ($(NINJA),) Makefile.ninja: build.ninja @@ -112,15 +104,23 @@ Makefile.ninja: build.ninja $(NINJA) -t query build.ninja | sed -n '1,/^ input:/d; /^ outputs:/q; s/$$/ \\/p'; \ } > $@.tmp && mv $@.tmp $@ -include Makefile.ninja - -# A separate rule is needed for Makefile dependencies to avoid -n -build.ninja: build.ninja.stamp -$(build-files): -build.ninja.stamp: meson.stamp $(build-files) - $(NINJA) $(if $V,-v,) build.ninja && touch $@ endif ifneq ($(MESON),) +# The path to meson always points to pyvenv/bin/meson, but the absolute +# paths could change. In that case, force a regeneration of build.ninja. +# Note that this invocation of $(NINJA), just like when Make rebuilds +# Makefiles, does not include -n. +build.ninja: build.ninja.stamp +$(build-files): +build.ninja.stamp: meson.stamp $(build-files) + @if test "$$(cat build.ninja.stamp)" = "$(MESON)" && test -n "$(NINJA)"; then \ + $(NINJA) build.ninja; \ + else \ + echo "$(MESON) setup --reconfigure $(SRC_PATH)"; \ + $(MESON) setup --reconfigure $(SRC_PATH); \ + fi && echo "$(MESON)" > $@ + Makefile.mtest: build.ninja scripts/mtest2make.py $(MESON) introspect --targets --tests --benchmarks | $(PYTHON) scripts/mtest2make.py > $@ -include Makefile.mtest @@ -141,13 +141,18 @@ MAKE.n = $(findstring n,$(firstword $(filter-out --%,$(MAKEFLAGS)))) MAKE.k = $(findstring k,$(firstword $(filter-out --%,$(MAKEFLAGS)))) MAKE.q = $(findstring q,$(firstword $(filter-out --%,$(MAKEFLAGS)))) MAKE.nq = $(if $(word 2, $(MAKE.n) $(MAKE.q)),nq) -NINJAFLAGS = $(if $V,-v) $(if $(MAKE.n), -n) $(if $(MAKE.k), -k0) \ - $(filter-out -j, $(lastword -j1 $(filter -l% -j%, $(MAKEFLAGS)))) \ +NINJAFLAGS = \ + $(if $V,-v) \ + $(if $(MAKE.n), -n) \ + $(if $(MAKE.k), -k0) \ + $(filter-out -j, \ + $(or $(filter -l% -j%, $(MAKEFLAGS)), \ + $(if $(filter --jobserver-auth=%, $(MAKEFLAGS)),, -j1))) \ -d keepdepfile ninja-cmd-goals = $(or $(MAKECMDGOALS), all) -ninja-cmd-goals += $(foreach g, $(MAKECMDGOALS), $(.ninja-goals.$g)))) +ninja-cmd-goals += $(foreach g, $(MAKECMDGOALS), $(.ninja-goals.$g)) -makefile-targets := build.ninja ctags TAGS cscope dist clean uninstall +makefile-targets := build.ninja ctags TAGS cscope dist clean # "ninja -t targets" also lists all prerequisites. If build system # files are marked as PHONY, however, Make will always try to execute # "ninja build.ninja". @@ -164,19 +169,9 @@ ifneq ($(filter $(ninja-targets), $(ninja-cmd-goals)),) endif endif -ifeq ($(CONFIG_PLUGIN),y) -.PHONY: plugins -plugins: - $(call quiet-command,\ - $(MAKE) $(SUBDIR_MAKEFLAGS) -C contrib/plugins V="$(V)", \ - "BUILD", "example plugins") -endif # $(CONFIG_PLUGIN) - else # config-host.mak does not exist -config-host.mak: ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail)) - @echo "Please call configure before running make!" - @exit 1 +$(error Please call configure before running make) endif endif # config-host.mak does not exist @@ -186,16 +181,20 @@ include $(SRC_PATH)/tests/Makefile.include all: recurse-all -ROM_DIRS = $(addprefix pc-bios/, $(ROMS)) -ROM_DIRS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROM_DIRS))) -# Only keep -O and -g cflags -.PHONY: $(ROM_DIRS_RULES) -$(ROM_DIRS_RULES): +SUBDIR_RULES=$(foreach t, all clean distclean, $(addsuffix /$(t), $(SUBDIRS))) +.PHONY: $(SUBDIR_RULES) +$(SUBDIR_RULES): $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" $(notdir $@),) +ifneq ($(filter contrib/plugins, $(SUBDIRS)),) +.PHONY: plugins +plugins: contrib/plugins/all +endif + .PHONY: recurse-all recurse-clean -recurse-all: $(addsuffix /all, $(ROM_DIRS)) -recurse-clean: $(addsuffix /clean, $(ROM_DIRS)) +recurse-all: $(addsuffix /all, $(SUBDIRS)) +recurse-clean: $(addsuffix /clean, $(SUBDIRS)) +recurse-distclean: $(addsuffix /distclean, $(SUBDIRS)) ###################################################################### @@ -208,6 +207,7 @@ clean: recurse-clean ! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-arm.a \ -exec rm {} + rm -f TAGS cscope.* *~ */*~ + @$(MAKE) -Ctests/qemu-iotests clean VERSION = $(shell cat $(SRC_PATH)/VERSION) @@ -216,10 +216,10 @@ dist: qemu-$(VERSION).tar.bz2 qemu-%.tar.bz2: $(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)" -distclean: clean +distclean: clean recurse-distclean -$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean -g || : - rm -f config-host.mak - rm -f tests/tcg/config-*.mak + rm -f config-host.mak Makefile.prereqs + rm -f tests/tcg/*/config-target.mak tests/tcg/config-host.mak rm -f config.status rm -f roms/seabios/config.mak rm -f qemu-plugins-ld.symbols qemu-plugins-ld64.symbols @@ -228,7 +228,7 @@ distclean: clean rm -f Makefile.ninja Makefile.mtest build.ninja.stamp meson.stamp rm -f config.log rm -f linux-headers/asm - rm -Rf .sdk + rm -Rf .sdk qemu-bundle find-src-path = find "$(SRC_PATH)" -path "$(SRC_PATH)/meson" -prune -o \ -type l -prune -o \( -name "*.[chsS]" -o -name "*.[ch].inc" \) @@ -289,6 +289,13 @@ include $(SRC_PATH)/tests/vm/Makefile.include print-help-run = printf " %-30s - %s\\n" "$1" "$2" print-help = @$(call print-help-run,$1,$2) +.PHONY: update-linux-vdso +update-linux-vdso: + @for m in $(SRC_PATH)/linux-user/*/Makefile.vdso; do \ + $(MAKE) $(SUBDIR_MAKEFLAGS) -C $$(dirname $$m) -f Makefile.vdso \ + SRC_PATH=$(SRC_PATH) BUILD_DIR=$(BUILD_DIR); \ + done + .PHONY: help help: @echo 'Generic targets:' @@ -299,7 +306,7 @@ help: $(call print-help,cscope,Generate cscope index) $(call print-help,sparse,Run sparse on the QEMU source) @echo '' -ifeq ($(CONFIG_PLUGIN),y) +ifneq ($(filter contrib/plugins, $(SUBDIRS)),) @echo 'Plugin targets:' $(call print-help,plugins,Build the example TCG plugins) @echo '' @@ -309,6 +316,9 @@ endif $(call print-help,distclean,Remove all generated files) $(call print-help,dist,Build a distributable tarball) @echo '' + @echo 'Linux-user targets:' + $(call print-help,update-linux-vdso,Build linux-user vdso images) + @echo '' @echo 'Test targets:' $(call print-help,check,Run all tests (check-help for details)) $(call print-help,bench,Run all benchmarks) @@ -319,7 +329,7 @@ endif @echo 'Documentation targets:' $(call print-help,html man,Build documentation in specified format) @echo '' -ifdef CONFIG_WIN32 +ifneq ($(filter msi, $(ninja-targets)),) @echo 'Windows targets:' $(call print-help,installer,Build NSIS-based installer for QEMU) $(call print-help,msi,Build MSI-based installer for qemu-ga) diff --git a/README.rst b/README.rst index 23795b8377..21df79ef43 100644 --- a/README.rst +++ b/README.rst @@ -39,7 +39,7 @@ Documentation can be found hosted online at current development version that is available at ``_ is generated from the ``docs/`` folder in the source tree, and is built by `Sphinx -_`. +`_. Building @@ -78,7 +78,7 @@ format-patch' and/or 'git send-email' to format & send the mail to the qemu-devel@nongnu.org mailing list. All patches submitted must contain a 'Signed-off-by' line from the author. Patches should follow the guidelines set out in the `style section -` of +`_ of the Developers Guide. Additional information on submitting patches can be found online via diff --git a/VERSION b/VERSION index 59f2fcbfc0..f7ee06693c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.0.50 +9.0.0 diff --git a/accel/Kconfig b/accel/Kconfig index 8bdedb7d15..794e0d18d2 100644 --- a/accel/Kconfig +++ b/accel/Kconfig @@ -4,9 +4,6 @@ config WHPX config NVMM bool -config HAX - bool - config HVF bool @@ -19,3 +16,4 @@ config KVM config XEN bool select FSDEV_9P if VIRTFS + select XEN_BUS diff --git a/accel/accel-blocker.c b/accel/accel-blocker.c new file mode 100644 index 0000000000..e083f24aa8 --- /dev/null +++ b/accel/accel-blocker.c @@ -0,0 +1,154 @@ +/* + * Lock to inhibit accelerator ioctls + * + * Copyright (c) 2022 Red Hat Inc. + * + * Author: Emanuele Giuseppe Esposito + * + * 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/thread.h" +#include "qemu/main-loop.h" +#include "hw/core/cpu.h" +#include "sysemu/accel-blocker.h" + +static QemuLockCnt accel_in_ioctl_lock; +static QemuEvent accel_in_ioctl_event; + +void accel_blocker_init(void) +{ + qemu_lockcnt_init(&accel_in_ioctl_lock); + qemu_event_init(&accel_in_ioctl_event, false); +} + +void accel_ioctl_begin(void) +{ + if (likely(bql_locked())) { + return; + } + + /* block if lock is taken in kvm_ioctl_inhibit_begin() */ + qemu_lockcnt_inc(&accel_in_ioctl_lock); +} + +void accel_ioctl_end(void) +{ + if (likely(bql_locked())) { + return; + } + + qemu_lockcnt_dec(&accel_in_ioctl_lock); + /* change event to SET. If event was BUSY, wake up all waiters */ + qemu_event_set(&accel_in_ioctl_event); +} + +void accel_cpu_ioctl_begin(CPUState *cpu) +{ + if (unlikely(bql_locked())) { + return; + } + + /* block if lock is taken in kvm_ioctl_inhibit_begin() */ + qemu_lockcnt_inc(&cpu->in_ioctl_lock); +} + +void accel_cpu_ioctl_end(CPUState *cpu) +{ + if (unlikely(bql_locked())) { + return; + } + + qemu_lockcnt_dec(&cpu->in_ioctl_lock); + /* change event to SET. If event was BUSY, wake up all waiters */ + qemu_event_set(&accel_in_ioctl_event); +} + +static bool accel_has_to_wait(void) +{ + CPUState *cpu; + bool needs_to_wait = false; + + CPU_FOREACH(cpu) { + if (qemu_lockcnt_count(&cpu->in_ioctl_lock)) { + /* exit the ioctl, if vcpu is running it */ + qemu_cpu_kick(cpu); + needs_to_wait = true; + } + } + + return needs_to_wait || qemu_lockcnt_count(&accel_in_ioctl_lock); +} + +void accel_ioctl_inhibit_begin(void) +{ + CPUState *cpu; + + /* + * We allow to inhibit only when holding the BQL, so we can identify + * when an inhibitor wants to issue an ioctl easily. + */ + g_assert(bql_locked()); + + /* Block further invocations of the ioctls outside the BQL. */ + CPU_FOREACH(cpu) { + qemu_lockcnt_lock(&cpu->in_ioctl_lock); + } + qemu_lockcnt_lock(&accel_in_ioctl_lock); + + /* Keep waiting until there are running ioctls */ + while (true) { + + /* Reset event to FREE. */ + qemu_event_reset(&accel_in_ioctl_event); + + if (accel_has_to_wait()) { + /* + * If event is still FREE, and there are ioctls still in progress, + * wait. + * + * If an ioctl finishes before qemu_event_wait(), it will change + * the event state to SET. This will prevent qemu_event_wait() from + * blocking, but it's not a problem because if other ioctls are + * still running the loop will iterate once more and reset the event + * status to FREE so that it can wait properly. + * + * If an ioctls finishes while qemu_event_wait() is blocking, then + * it will be waken up, but also here the while loop makes sure + * to re-enter the wait if there are other running ioctls. + */ + qemu_event_wait(&accel_in_ioctl_event); + } else { + /* No ioctl is running */ + return; + } + } +} + +void accel_ioctl_inhibit_end(void) +{ + CPUState *cpu; + + qemu_lockcnt_unlock(&accel_in_ioctl_lock); + CPU_FOREACH(cpu) { + qemu_lockcnt_unlock(&cpu->in_ioctl_lock); + } +} + diff --git a/accel/accel-softmmu.c b/accel/accel-system.c similarity index 87% rename from accel/accel-softmmu.c rename to accel/accel-system.c index 67276e4f52..f6c947dd82 100644 --- a/accel/accel-softmmu.c +++ b/accel/accel-system.c @@ -27,8 +27,8 @@ #include "qemu/accel.h" #include "hw/boards.h" #include "sysemu/cpus.h" - -#include "accel-softmmu.h" +#include "qemu/error-report.h" +#include "accel-system.h" int accel_init_machine(AccelState *accel, MachineState *ms) { @@ -62,10 +62,11 @@ void accel_setup_post(MachineState *ms) } /* initialize the arch-independent accel operation interfaces */ -void accel_init_ops_interfaces(AccelClass *ac) +void accel_system_init_ops_interfaces(AccelClass *ac) { const char *ac_name; char *ops_name; + ObjectClass *oc; AccelOpsClass *ops; ac_name = object_class_get_name(OBJECT_CLASS(ac)); @@ -73,8 +74,13 @@ void accel_init_ops_interfaces(AccelClass *ac) ops_name = g_strdup_printf("%s" ACCEL_OPS_SUFFIX, ac_name); ops = ACCEL_OPS_CLASS(module_object_class_by_name(ops_name)); + oc = module_object_class_by_name(ops_name); + if (!oc) { + error_report("fatal: could not load module for type '%s'", ops_name); + exit(1); + } g_free(ops_name); - + ops = ACCEL_OPS_CLASS(oc); /* * all accelerators need to define ops, providing at least a mandatory * non-NULL create_vcpu_thread operation. @@ -93,8 +99,8 @@ static const TypeInfo accel_ops_type_info = { .class_size = sizeof(AccelOpsClass), }; -static void accel_softmmu_register_types(void) +static void accel_system_register_types(void) { type_register_static(&accel_ops_type_info); } -type_init(accel_softmmu_register_types); +type_init(accel_system_register_types); diff --git a/accel/accel-softmmu.h b/accel/accel-system.h similarity index 63% rename from accel/accel-softmmu.h rename to accel/accel-system.h index 5e192f1882..2d37c73c97 100644 --- a/accel/accel-softmmu.h +++ b/accel/accel-system.h @@ -7,9 +7,9 @@ * See the COPYING file in the top-level directory. */ -#ifndef ACCEL_SOFTMMU_H -#define ACCEL_SOFTMMU_H +#ifndef ACCEL_SYSTEM_H +#define ACCEL_SYSTEM_H -void accel_init_ops_interfaces(AccelClass *ac); +void accel_system_init_ops_interfaces(AccelClass *ac); -#endif /* ACCEL_SOFTMMU_H */ +#endif /* ACCEL_SYSTEM_H */ diff --git a/accel/accel-common.c b/accel/accel-target.c similarity index 76% rename from accel/accel-common.c rename to accel/accel-target.c index 7b8ec7e0f7..08626c00c2 100644 --- a/accel/accel-common.c +++ b/accel/accel-target.c @@ -30,7 +30,7 @@ #include "hw/core/accel-cpu.h" #ifndef CONFIG_USER_ONLY -#include "accel-softmmu.h" +#include "accel-system.h" #endif /* !CONFIG_USER_ONLY */ static const TypeInfo accel_type = { @@ -49,6 +49,14 @@ AccelClass *accel_find(const char *opt_name) return ac; } +/* Return the name of the current accelerator */ +const char *current_accel_name(void) +{ + AccelClass *ac = ACCEL_GET_CLASS(current_accel()); + + return ac->name; +} + static void accel_init_cpu_int_aux(ObjectClass *klass, void *opaque) { CPUClass *cc = CPU_CLASS(klass); @@ -96,7 +104,7 @@ static void accel_init_cpu_interfaces(AccelClass *ac) void accel_init_interfaces(AccelClass *ac) { #ifndef CONFIG_USER_ONLY - accel_init_ops_interfaces(ac); + accel_system_init_ops_interfaces(ac); #endif /* !CONFIG_USER_ONLY */ accel_init_cpu_interfaces(ac); @@ -111,16 +119,47 @@ void accel_cpu_instance_init(CPUState *cpu) } } -bool accel_cpu_realizefn(CPUState *cpu, Error **errp) +bool accel_cpu_common_realize(CPUState *cpu, Error **errp) { CPUClass *cc = CPU_GET_CLASS(cpu); + AccelState *accel = current_accel(); + AccelClass *acc = ACCEL_GET_CLASS(accel); - if (cc->accel_cpu && cc->accel_cpu->cpu_realizefn) { - return cc->accel_cpu->cpu_realizefn(cpu, errp); + /* target specific realization */ + if (cc->accel_cpu && cc->accel_cpu->cpu_target_realize + && !cc->accel_cpu->cpu_target_realize(cpu, errp)) { + return false; } + + /* generic realization */ + if (acc->cpu_common_realize && !acc->cpu_common_realize(cpu, errp)) { + return false; + } + return true; } +void accel_cpu_common_unrealize(CPUState *cpu) +{ + AccelState *accel = current_accel(); + AccelClass *acc = ACCEL_GET_CLASS(accel); + + /* generic unrealization */ + if (acc->cpu_common_unrealize) { + acc->cpu_common_unrealize(cpu); + } +} + +int accel_supported_gdbstub_sstep_flags(void) +{ + AccelState *accel = current_accel(); + AccelClass *acc = ACCEL_GET_CLASS(accel); + if (acc->gdbstub_supported_sstep_flags) { + return acc->gdbstub_supported_sstep_flags(); + } + return 0; +} + static const TypeInfo accel_cpu_type = { .name = TYPE_ACCEL_CPU, .parent = TYPE_OBJECT, diff --git a/accel/dummy-cpus.c b/accel/dummy-cpus.c index 10429fdfb2..20519f1ea4 100644 --- a/accel/dummy-cpus.c +++ b/accel/dummy-cpus.c @@ -21,26 +21,29 @@ static void *dummy_cpu_thread_fn(void *arg) { CPUState *cpu = arg; - sigset_t waitset; - int r; rcu_register_thread(); - qemu_mutex_lock_iothread(); + bql_lock(); qemu_thread_get_self(cpu->thread); cpu->thread_id = qemu_get_thread_id(); - cpu->can_do_io = 1; current_cpu = cpu; +#ifndef _WIN32 + sigset_t waitset; + int r; + sigemptyset(&waitset); sigaddset(&waitset, SIG_IPI); +#endif /* signal CPU creation */ cpu_thread_signal_created(cpu); qemu_guest_random_seed_thread_part2(cpu->random_seed); do { - qemu_mutex_unlock_iothread(); + bql_unlock(); +#ifndef _WIN32 do { int sig; r = sigwait(&waitset, &sig); @@ -49,11 +52,14 @@ static void *dummy_cpu_thread_fn(void *arg) perror("sigwait"); exit(1); } - qemu_mutex_lock_iothread(); +#else + qemu_sem_wait(&cpu->sem); +#endif + bql_lock(); qemu_wait_io_event(cpu); } while (!cpu->unplug); - qemu_mutex_unlock_iothread(); + bql_unlock(); rcu_unregister_thread(); return NULL; } @@ -69,4 +75,7 @@ void dummy_start_vcpu_thread(CPUState *cpu) cpu->cpu_index); qemu_thread_create(cpu->thread, thread_name, dummy_cpu_thread_fn, cpu, QEMU_THREAD_JOINABLE); +#ifdef _WIN32 + qemu_sem_init(&cpu->sem, 0); +#endif } diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 24913ca9c4..d94d41ab6d 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -52,6 +52,7 @@ #include "qemu/main-loop.h" #include "exec/address-spaces.h" #include "exec/exec-all.h" +#include "exec/gdbstub.h" #include "sysemu/cpus.h" #include "sysemu/hvf.h" #include "sysemu/hvf_int.h" @@ -303,7 +304,7 @@ static void hvf_region_del(MemoryListener *listener, static MemoryListener hvf_memory_listener = { .name = "hvf", - .priority = 10, + .priority = MEMORY_LISTENER_PRIORITY_ACCEL, .region_add = hvf_region_add, .region_del = hvf_region_del, .log_start = hvf_log_start, @@ -334,18 +335,26 @@ static int hvf_accel_init(MachineState *ms) s->slots[x].slot_id = x; } + QTAILQ_INIT(&s->hvf_sw_breakpoints); + hvf_state = s; memory_listener_register(&hvf_memory_listener, &address_space_memory); return hvf_arch_init(); } +static inline int hvf_gdbstub_sstep_flags(void) +{ + return SSTEP_ENABLE | SSTEP_NOIRQ; +} + static void hvf_accel_class_init(ObjectClass *oc, void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "HVF"; ac->init_machine = hvf_accel_init; ac->allowed = &hvf_allowed; + ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags; } static const TypeInfo hvf_accel_type = { @@ -363,19 +372,19 @@ type_init(hvf_type_init); static void hvf_vcpu_destroy(CPUState *cpu) { - hv_return_t ret = hv_vcpu_destroy(cpu->hvf->fd); + hv_return_t ret = hv_vcpu_destroy(cpu->accel->fd); assert_hvf_ok(ret); hvf_arch_vcpu_destroy(cpu); - g_free(cpu->hvf); - cpu->hvf = NULL; + g_free(cpu->accel); + cpu->accel = NULL; } static int hvf_init_vcpu(CPUState *cpu) { int r; - cpu->hvf = g_malloc0(sizeof(*cpu->hvf)); + cpu->accel = g_new0(AccelCPUState, 1); /* init cpu signals */ struct sigaction sigact; @@ -384,17 +393,20 @@ static int hvf_init_vcpu(CPUState *cpu) sigact.sa_handler = dummy_signal; sigaction(SIG_IPI, &sigact, NULL); - pthread_sigmask(SIG_BLOCK, NULL, &cpu->hvf->unblock_ipi_mask); - sigdelset(&cpu->hvf->unblock_ipi_mask, SIG_IPI); + pthread_sigmask(SIG_BLOCK, NULL, &cpu->accel->unblock_ipi_mask); + sigdelset(&cpu->accel->unblock_ipi_mask, SIG_IPI); #ifdef __aarch64__ - r = hv_vcpu_create(&cpu->hvf->fd, (hv_vcpu_exit_t **)&cpu->hvf->exit, NULL); + r = hv_vcpu_create(&cpu->accel->fd, + (hv_vcpu_exit_t **)&cpu->accel->exit, NULL); #else - r = hv_vcpu_create((hv_vcpuid_t *)&cpu->hvf->fd, HV_VCPU_DEFAULT); + r = hv_vcpu_create((hv_vcpuid_t *)&cpu->accel->fd, HV_VCPU_DEFAULT); #endif cpu->vcpu_dirty = 1; assert_hvf_ok(r); + cpu->accel->guest_debug_enabled = false; + return hvf_arch_init_vcpu(cpu); } @@ -412,11 +424,10 @@ static void *hvf_cpu_thread_fn(void *arg) rcu_register_thread(); - qemu_mutex_lock_iothread(); + bql_lock(); qemu_thread_get_self(cpu->thread); cpu->thread_id = qemu_get_thread_id(); - cpu->can_do_io = 1; current_cpu = cpu; hvf_init_vcpu(cpu); @@ -437,7 +448,7 @@ static void *hvf_cpu_thread_fn(void *arg) hvf_vcpu_destroy(cpu); cpu_thread_signal_destroyed(cpu); - qemu_mutex_unlock_iothread(); + bql_unlock(); rcu_unregister_thread(); return NULL; } @@ -462,6 +473,108 @@ static void hvf_start_vcpu_thread(CPUState *cpu) cpu, QEMU_THREAD_JOINABLE); } +static int hvf_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len) +{ + struct hvf_sw_breakpoint *bp; + int err; + + if (type == GDB_BREAKPOINT_SW) { + bp = hvf_find_sw_breakpoint(cpu, addr); + if (bp) { + bp->use_count++; + return 0; + } + + bp = g_new(struct hvf_sw_breakpoint, 1); + bp->pc = addr; + bp->use_count = 1; + err = hvf_arch_insert_sw_breakpoint(cpu, bp); + if (err) { + g_free(bp); + return err; + } + + QTAILQ_INSERT_HEAD(&hvf_state->hvf_sw_breakpoints, bp, entry); + } else { + err = hvf_arch_insert_hw_breakpoint(addr, len, type); + if (err) { + return err; + } + } + + CPU_FOREACH(cpu) { + err = hvf_update_guest_debug(cpu); + if (err) { + return err; + } + } + return 0; +} + +static int hvf_remove_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len) +{ + struct hvf_sw_breakpoint *bp; + int err; + + if (type == GDB_BREAKPOINT_SW) { + bp = hvf_find_sw_breakpoint(cpu, addr); + if (!bp) { + return -ENOENT; + } + + if (bp->use_count > 1) { + bp->use_count--; + return 0; + } + + err = hvf_arch_remove_sw_breakpoint(cpu, bp); + if (err) { + return err; + } + + QTAILQ_REMOVE(&hvf_state->hvf_sw_breakpoints, bp, entry); + g_free(bp); + } else { + err = hvf_arch_remove_hw_breakpoint(addr, len, type); + if (err) { + return err; + } + } + + CPU_FOREACH(cpu) { + err = hvf_update_guest_debug(cpu); + if (err) { + return err; + } + } + return 0; +} + +static void hvf_remove_all_breakpoints(CPUState *cpu) +{ + struct hvf_sw_breakpoint *bp, *next; + CPUState *tmpcpu; + + QTAILQ_FOREACH_SAFE(bp, &hvf_state->hvf_sw_breakpoints, entry, next) { + if (hvf_arch_remove_sw_breakpoint(cpu, bp) != 0) { + /* Try harder to find a CPU that currently sees the breakpoint. */ + CPU_FOREACH(tmpcpu) + { + if (hvf_arch_remove_sw_breakpoint(tmpcpu, bp) == 0) { + break; + } + } + } + QTAILQ_REMOVE(&hvf_state->hvf_sw_breakpoints, bp, entry); + g_free(bp); + } + hvf_arch_remove_all_hw_breakpoints(); + + CPU_FOREACH(cpu) { + hvf_update_guest_debug(cpu); + } +} + static void hvf_accel_ops_class_init(ObjectClass *oc, void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); @@ -473,6 +586,12 @@ static void hvf_accel_ops_class_init(ObjectClass *oc, void *data) ops->synchronize_post_init = hvf_cpu_synchronize_post_init; ops->synchronize_state = hvf_cpu_synchronize_state; ops->synchronize_pre_loadvm = hvf_cpu_synchronize_pre_loadvm; + + ops->insert_breakpoint = hvf_insert_breakpoint; + ops->remove_breakpoint = hvf_remove_breakpoint; + ops->remove_all_breakpoints = hvf_remove_all_breakpoints; + ops->update_guest_debug = hvf_update_guest_debug; + ops->supports_guest_debug = hvf_arch_supports_guest_debug; }; static const TypeInfo hvf_accel_ops_type = { .name = ACCEL_OPS_NAME("hvf"), diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index 0043f4d308..db05b81be5 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -38,9 +38,38 @@ void assert_hvf_ok(hv_return_t ret) case HV_UNSUPPORTED: error_report("Error: HV_UNSUPPORTED"); break; +#if defined(MAC_OS_VERSION_11_0) && \ + MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 + case HV_DENIED: + error_report("Error: HV_DENIED"); + break; +#endif default: error_report("Unknown Error"); } abort(); } + +struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, vaddr pc) +{ + struct hvf_sw_breakpoint *bp; + + QTAILQ_FOREACH(bp, &hvf_state->hvf_sw_breakpoints, entry) { + if (bp->pc == pc) { + return bp; + } + } + return NULL; +} + +int hvf_sw_breakpoints_active(CPUState *cpu) +{ + return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints); +} + +int hvf_update_guest_debug(CPUState *cpu) +{ + hvf_arch_update_guest_debug(cpu); + return 0; +} diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index c4244a23c6..b3c946dc4b 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -16,12 +16,14 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +#include "sysemu/kvm.h" #include "sysemu/kvm_int.h" #include "sysemu/runstate.h" #include "sysemu/cpus.h" #include "qemu/guest-random.h" #include "qapi/error.h" +#include #include "kvm-cpus.h" static void *kvm_vcpu_thread_fn(void *arg) @@ -31,10 +33,9 @@ static void *kvm_vcpu_thread_fn(void *arg) rcu_register_thread(); - qemu_mutex_lock_iothread(); + bql_lock(); qemu_thread_get_self(cpu->thread); cpu->thread_id = qemu_get_thread_id(); - cpu->can_do_io = 1; current_cpu = cpu; r = kvm_init_vcpu(cpu, &error_fatal); @@ -56,7 +57,7 @@ static void *kvm_vcpu_thread_fn(void *arg) kvm_destroy_vcpu(cpu); cpu_thread_signal_destroyed(cpu); - qemu_mutex_unlock_iothread(); + bql_unlock(); rcu_unregister_thread(); return NULL; } @@ -84,6 +85,13 @@ static bool kvm_cpus_are_resettable(void) return !kvm_enabled() || kvm_cpu_check_are_resettable(); } +#ifdef KVM_CAP_SET_GUEST_DEBUG +static int kvm_update_guest_debug_ops(CPUState *cpu) +{ + return kvm_update_guest_debug(cpu, 0); +} +#endif + static void kvm_accel_ops_class_init(ObjectClass *oc, void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); @@ -95,6 +103,14 @@ static void kvm_accel_ops_class_init(ObjectClass *oc, void *data) ops->synchronize_post_init = kvm_cpu_synchronize_post_init; ops->synchronize_state = kvm_cpu_synchronize_state; ops->synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm; + +#ifdef KVM_CAP_SET_GUEST_DEBUG + ops->update_guest_debug = kvm_update_guest_debug_ops; + ops->supports_guest_debug = kvm_supports_guest_debug; + ops->insert_breakpoint = kvm_insert_breakpoint; + ops->remove_breakpoint = kvm_remove_breakpoint; + ops->remove_all_breakpoints = kvm_remove_all_breakpoints; +#endif } static const TypeInfo kvm_accel_ops_type = { diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index a4c4863f53..931f74256e 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -31,6 +31,7 @@ #include "sysemu/kvm_int.h" #include "sysemu/runstate.h" #include "sysemu/cpus.h" +#include "sysemu/accel-blocker.h" #include "qemu/bswap.h" #include "exec/memory.h" #include "exec/ram_addr.h" @@ -45,8 +46,11 @@ #include "qemu/guest-random.h" #include "sysemu/hw_accel.h" #include "kvm-cpus.h" +#include "sysemu/dirtylimit.h" +#include "qemu/range.h" #include "hw/boards.h" +#include "sysemu/stats.h" /* This check must be after config-host.h is included */ #ifdef CONFIG_EVENTFD @@ -65,103 +69,17 @@ #define KVM_GUESTDBG_BLOCKIRQ 0 #endif -//#define DEBUG_KVM - -#ifdef DEBUG_KVM -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -#define KVM_MSI_HASHTAB_SIZE 256 - struct KVMParkedVcpu { unsigned long vcpu_id; int kvm_fd; QLIST_ENTRY(KVMParkedVcpu) node; }; -enum KVMDirtyRingReaperState { - KVM_DIRTY_RING_REAPER_NONE = 0, - /* The reaper is sleeping */ - KVM_DIRTY_RING_REAPER_WAIT, - /* The reaper is reaping for dirty pages */ - KVM_DIRTY_RING_REAPER_REAPING, -}; - -/* - * KVM reaper instance, responsible for collecting the KVM dirty bits - * via the dirty ring. - */ -struct KVMDirtyRingReaper { - /* The reaper thread */ - QemuThread reaper_thr; - volatile uint64_t reaper_iteration; /* iteration number of reaper thr */ - volatile enum KVMDirtyRingReaperState reaper_state; /* reap thr state */ -}; - -struct KVMState -{ - AccelState parent_obj; - - int nr_slots; - int fd; - int vmfd; - int coalesced_mmio; - int coalesced_pio; - struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; - bool coalesced_flush_in_progress; - int vcpu_events; - int robust_singlestep; - int debugregs; -#ifdef KVM_CAP_SET_GUEST_DEBUG - QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; -#endif - 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; - uint64_t manual_dirty_log_protect; - /* The man page (and posix) say ioctl numbers are signed int, but - * they're not. Linux, glibc and *BSD all treat ioctl numbers as - * unsigned, and treating them as signed here can break things */ - unsigned irq_set_ioctl; - unsigned int sigmask_len; - GHashTable *gsimap; -#ifdef KVM_CAP_IRQ_ROUTING - struct kvm_irq_routing *irq_routes; - int nr_allocated_irq_routes; - unsigned long *used_gsi_bitmap; - unsigned int gsi_count; - QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; -#endif - KVMMemoryListener memory_listener; - QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; - - /* For "info mtree -f" to tell if an MR is registered in KVM */ - int nr_as; - struct KVMAs { - KVMMemoryListener *ml; - AddressSpace *as; - } *as; - uint64_t kvm_dirty_ring_bytes; /* Size of the per-vcpu dirty ring */ - uint32_t kvm_dirty_ring_size; /* Number of dirty GFNs per ring */ - struct KVMDirtyRingReaper reaper; -}; - KVMState *kvm_state; bool kvm_kernel_irqchip; bool kvm_split_irqchip; bool kvm_async_interrupts_allowed; bool kvm_halt_in_kernel_allowed; -bool kvm_eventfds_allowed; -bool kvm_irqfds_allowed; bool kvm_resamplefds_allowed; bool kvm_msi_via_irqfd_allowed; bool kvm_gsi_routing_allowed; @@ -169,11 +87,9 @@ bool kvm_gsi_direct_mapping; bool kvm_allowed; bool kvm_readonly_mem_allowed; bool kvm_vm_attributes_allowed; -bool kvm_direct_msi_allowed; -bool kvm_ioeventfd_any_length_allowed; bool kvm_msi_use_devid; -bool kvm_has_guest_debug; -int kvm_sstep_flags; +static bool kvm_has_guest_debug; +static int kvm_sstep_flags; static bool kvm_immediate_exit; static hwaddr kvm_max_slot_size = ~0; @@ -181,6 +97,9 @@ static const KVMCapabilityInfo kvm_required_capabilites[] = { KVM_CAP_INFO(USER_MEMORY), KVM_CAP_INFO(DESTROY_MEMORY_REGION_WORKS), KVM_CAP_INFO(JOIN_MEMORY_REGIONS_WORKS), + KVM_CAP_INFO(INTERNAL_ERROR_DATA), + KVM_CAP_INFO(IOEVENTFD), + KVM_CAP_INFO(IOEVENTFD_ANY_LENGTH), KVM_CAP_LAST_INFO }; @@ -244,13 +163,31 @@ void kvm_resample_fd_notify(int gsi) } } -int kvm_get_max_memslots(void) +unsigned int kvm_get_max_memslots(void) { KVMState *s = KVM_STATE(current_accel()); return s->nr_slots; } +unsigned int kvm_get_free_memslots(void) +{ + unsigned int used_slots = 0; + KVMState *s = kvm_state; + int i; + + kvm_slots_lock(); + for (i = 0; i < s->nr_as; i++) { + if (!s->as[i].ml) { + continue; + } + used_slots = MAX(used_slots, s->as[i].ml->nr_used_slots); + } + kvm_slots_unlock(); + + return s->nr_slots - used_slots; +} + /* Called with KVMMemoryListener.slots_lock held */ static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) { @@ -266,19 +203,6 @@ static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) return NULL; } -bool kvm_has_free_slot(MachineState *ms) -{ - KVMState *s = KVM_STATE(ms->accelerator); - bool result; - KVMMemoryListener *kml = &s->memory_listener; - - kvm_slots_lock(); - result = !!kvm_get_free_slot(kml); - kvm_slots_unlock(); - - return result; -} - /* Called with KVMMemoryListener.slots_lock held */ static KVMSlot *kvm_alloc_slot(KVMMemoryListener *kml) { @@ -397,7 +321,7 @@ static int do_kvm_destroy_vcpu(CPUState *cpu) struct KVMParkedVcpu *vcpu = NULL; int ret = 0; - DPRINTF("kvm_destroy_vcpu\n"); + trace_kvm_destroy_vcpu(); ret = kvm_arch_destroy_vcpu(cpu); if (ret < 0) { @@ -407,7 +331,7 @@ static int do_kvm_destroy_vcpu(CPUState *cpu) mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); if (mmap_size < 0) { ret = mmap_size; - DPRINTF("KVM_GET_VCPU_MMAP_SIZE failed\n"); + trace_kvm_failed_get_vcpu_mmap_size(); goto err; } @@ -476,6 +400,7 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp) cpu->kvm_state = s; cpu->vcpu_dirty = true; cpu->dirty_pages = 0; + cpu->throttle_us_per_full = 0; mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); if (mmap_size < 0) { @@ -508,7 +433,6 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp) PAGE_SIZE * KVM_DIRTY_LOG_PAGE_OFFSET); if (cpu->kvm_dirty_gfns == MAP_FAILED) { ret = -errno; - DPRINTF("mmap'ing vcpu dirty gfns failed: %d\n", ret); goto err; } } @@ -519,6 +443,8 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp) "kvm_init_vcpu: kvm_arch_init_vcpu failed (%lu)", kvm_arch_vcpu_id(cpu)); } + cpu->kvm_vcpu_stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL); + err: return ret; } @@ -716,12 +642,32 @@ static void kvm_dirty_ring_mark_page(KVMState *s, uint32_t as_id, static bool dirty_gfn_is_dirtied(struct kvm_dirty_gfn *gfn) { - return gfn->flags == KVM_DIRTY_GFN_F_DIRTY; + /* + * Read the flags before the value. Pairs with barrier in + * KVM's kvm_dirty_ring_push() function. + */ + return qatomic_load_acquire(&gfn->flags) == KVM_DIRTY_GFN_F_DIRTY; } static void dirty_gfn_set_collected(struct kvm_dirty_gfn *gfn) { - gfn->flags = KVM_DIRTY_GFN_F_RESET; + /* + * Use a store-release so that the CPU that executes KVM_RESET_DIRTY_RINGS + * sees the full content of the ring: + * + * CPU0 CPU1 CPU2 + * ------------------------------------------------------------------------------ + * fill gfn0 + * store-rel flags for gfn0 + * load-acq flags for gfn0 + * store-rel RESET for gfn0 + * ioctl(RESET_RINGS) + * load-acq flags for gfn0 + * check if flags have RESET + * + * The synchronization goes from CPU2 to CPU0 to CPU1. + */ + qatomic_store_release(&gfn->flags, KVM_DIRTY_GFN_F_RESET); } /* @@ -734,6 +680,15 @@ static uint32_t kvm_dirty_ring_reap_one(KVMState *s, CPUState *cpu) uint32_t ring_size = s->kvm_dirty_ring_size; uint32_t count = 0, fetch = cpu->kvm_fetch_index; + /* + * It's possible that we race with vcpu creation code where the vcpu is + * put onto the vcpus list but not yet initialized the dirty ring + * structures. If so, skip it. + */ + if (!cpu->created) { + return 0; + } + assert(dirty_gfns && ring_size); trace_kvm_dirty_ring_reap_vcpu(cpu->cpu_index); @@ -756,17 +711,20 @@ static uint32_t kvm_dirty_ring_reap_one(KVMState *s, CPUState *cpu) } /* Must be with slots_lock held */ -static uint64_t kvm_dirty_ring_reap_locked(KVMState *s) +static uint64_t kvm_dirty_ring_reap_locked(KVMState *s, CPUState* cpu) { int ret; - CPUState *cpu; uint64_t total = 0; int64_t stamp; stamp = get_clock(); - CPU_FOREACH(cpu) { - total += kvm_dirty_ring_reap_one(s, cpu); + if (cpu) { + total = kvm_dirty_ring_reap_one(s, cpu); + } else { + CPU_FOREACH(cpu) { + total += kvm_dirty_ring_reap_one(s, cpu); + } } if (total) { @@ -787,7 +745,7 @@ static uint64_t kvm_dirty_ring_reap_locked(KVMState *s) * Currently for simplicity, we must hold BQL before calling this. We can * consider to drop the BQL if we're clear with all the race conditions. */ -static uint64_t kvm_dirty_ring_reap(KVMState *s) +static uint64_t kvm_dirty_ring_reap(KVMState *s, CPUState *cpu) { uint64_t total; @@ -807,7 +765,7 @@ static uint64_t kvm_dirty_ring_reap(KVMState *s) * reset below. */ kvm_slots_lock(); - total = kvm_dirty_ring_reap_locked(s); + total = kvm_dirty_ring_reap_locked(s, cpu); kvm_slots_unlock(); return total; @@ -848,13 +806,13 @@ static void kvm_dirty_ring_flush(void) * should always be with BQL held, serialization is guaranteed. * However, let's be sure of it. */ - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); /* * First make sure to flush the hardware buffers by kicking all * vcpus out in a synchronous way. */ kvm_cpu_synchronize_kick_all(); - kvm_dirty_ring_reap(kvm_state); + kvm_dirty_ring_reap(kvm_state, NULL); trace_kvm_dirty_ring_flush(1); } @@ -1136,12 +1094,6 @@ static void kvm_coalesce_pio_del(MemoryListener *listener, } } -static MemoryListener kvm_coalesced_pio_listener = { - .name = "kvm-coalesced-pio", - .coalesced_io_add = kvm_coalesce_pio_add, - .coalesced_io_del = kvm_coalesce_pio_del, -}; - int kvm_check_extension(KVMState *s, unsigned int extension) { int ret; @@ -1167,6 +1119,11 @@ int kvm_vm_check_extension(KVMState *s, unsigned int extension) return ret; } +/* + * We track the poisoned pages to be able to: + * - replace them on VM reset + * - block a migration for a VM with a poisoned page + */ typedef struct HWPoisonPage { ram_addr_t ram_addr; QLIST_ENTRY(HWPoisonPage) list; @@ -1200,6 +1157,11 @@ void kvm_hwpoison_page_add(ram_addr_t ram_addr) QLIST_INSERT_HEAD(&hwpoison_page_list, page, list); } +bool kvm_hwpoisoned_mem(void) +{ + return !QLIST_EMPTY(&hwpoison_page_list); +} + static uint32_t adjust_ioeventfd_endianness(uint32_t val, uint32_t size) { #if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN @@ -1283,43 +1245,6 @@ static int kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint16_t val, } -static int kvm_check_many_ioeventfds(void) -{ - /* Userspace can use ioeventfd for io notification. This requires a host - * that supports eventfd(2) and an I/O thread; since eventfd does not - * support SIGIO it cannot interrupt the vcpu. - * - * Older kernels have a 6 device limit on the KVM io bus. Find out so we - * can avoid creating too many ioeventfds. - */ -#if defined(CONFIG_EVENTFD) - int ioeventfds[7]; - int i, ret = 0; - for (i = 0; i < ARRAY_SIZE(ioeventfds); i++) { - ioeventfds[i] = eventfd(0, EFD_CLOEXEC); - if (ioeventfds[i] < 0) { - break; - } - ret = kvm_set_ioeventfd_pio(ioeventfds[i], 0, i, true, 2, true); - if (ret < 0) { - close(ioeventfds[i]); - break; - } - } - - /* Decide whether many devices are supported or not */ - ret = i == ARRAY_SIZE(ioeventfds); - - while (i-- > 0) { - kvm_set_ioeventfd_pio(ioeventfds[i], 0, i, false, 2, true); - close(ioeventfds[i]); - } - return ret; -#else - return 0; -#endif -} - static const KVMCapabilityInfo * kvm_check_extension_list(KVMState *s, const KVMCapabilityInfo *list) { @@ -1340,6 +1265,7 @@ void kvm_set_max_memslot_size(hwaddr max_slot_size) kvm_max_slot_size = max_slot_size; } +/* Called with KVMMemoryListener.slots_lock held */ static void kvm_set_phys_mem(KVMMemoryListener *kml, MemoryRegionSection *section, bool add) { @@ -1374,14 +1300,12 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, ram = memory_region_get_ram_ptr(mr) + mr_offset; ram_start_offset = memory_region_get_ram_addr(mr) + mr_offset; - kvm_slots_lock(); - if (!add) { do { slot_size = MIN(kvm_max_slot_size, size); mem = kvm_lookup_matching_slot(kml, start_addr, slot_size); if (!mem) { - goto out; + return; } if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) { /* @@ -1398,7 +1322,11 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, * Not easy. Let's cross the fingers until it's fixed. */ if (kvm_state->kvm_dirty_ring_size) { - kvm_dirty_ring_reap_locked(kvm_state); + kvm_dirty_ring_reap_locked(kvm_state, NULL); + if (kvm_state->kvm_dirty_ring_with_bitmap) { + kvm_slot_sync_dirty_pages(mem); + kvm_slot_get_dirty_log(kvm_state, mem); + } } else { kvm_slot_get_dirty_log(kvm_state, mem); } @@ -1418,8 +1346,9 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, } start_addr += slot_size; size -= slot_size; + kml->nr_used_slots--; } while (size); - goto out; + return; } /* register the new slot */ @@ -1443,10 +1372,8 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, ram_start_offset += slot_size; ram += slot_size; size -= slot_size; + kml->nr_used_slots++; } while (size); - -out: - kvm_slots_unlock(); } static void *kvm_dirty_ring_reaper_thread(void *data) @@ -1466,12 +1393,17 @@ static void *kvm_dirty_ring_reaper_thread(void *data) */ sleep(1); + /* keep sleeping so that dirtylimit not be interfered by reaper */ + if (dirtylimit_in_service()) { + continue; + } + trace_kvm_dirty_ring_reaper("wakeup"); r->reaper_state = KVM_DIRTY_RING_REAPER_REAPING; - qemu_mutex_lock_iothread(); - kvm_dirty_ring_reap(s); - qemu_mutex_unlock_iothread(); + bql_lock(); + kvm_dirty_ring_reap(s, NULL); + bql_unlock(); r->reaper_iteration++; } @@ -1483,13 +1415,74 @@ static void *kvm_dirty_ring_reaper_thread(void *data) return NULL; } -static int kvm_dirty_ring_reaper_init(KVMState *s) +static void kvm_dirty_ring_reaper_init(KVMState *s) { struct KVMDirtyRingReaper *r = &s->reaper; qemu_thread_create(&r->reaper_thr, "kvm-reaper", kvm_dirty_ring_reaper_thread, s, QEMU_THREAD_JOINABLE); +} + +static int kvm_dirty_ring_init(KVMState *s) +{ + uint32_t ring_size = s->kvm_dirty_ring_size; + uint64_t ring_bytes = ring_size * sizeof(struct kvm_dirty_gfn); + unsigned int capability = KVM_CAP_DIRTY_LOG_RING; + int ret; + + s->kvm_dirty_ring_size = 0; + s->kvm_dirty_ring_bytes = 0; + + /* Bail if the dirty ring size isn't specified */ + if (!ring_size) { + return 0; + } + + /* + * Read the max supported pages. Fall back to dirty logging mode + * if the dirty ring isn't supported. + */ + ret = kvm_vm_check_extension(s, capability); + if (ret <= 0) { + capability = KVM_CAP_DIRTY_LOG_RING_ACQ_REL; + ret = kvm_vm_check_extension(s, capability); + } + + if (ret <= 0) { + warn_report("KVM dirty ring not available, using bitmap method"); + return 0; + } + + if (ring_bytes > ret) { + error_report("KVM dirty ring size %" PRIu32 " too big " + "(maximum is %ld). Please use a smaller value.", + ring_size, (long)ret / sizeof(struct kvm_dirty_gfn)); + return -EINVAL; + } + + ret = kvm_vm_enable_cap(s, capability, 0, ring_bytes); + if (ret) { + error_report("Enabling of KVM dirty ring failed: %s. " + "Suggested minimum value is 1024.", strerror(-ret)); + return -EIO; + } + + /* Enable the backup bitmap if it is supported */ + ret = kvm_vm_check_extension(s, KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP); + if (ret > 0) { + ret = kvm_vm_enable_cap(s, KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP, 0); + if (ret) { + error_report("Enabling of KVM dirty ring's backup bitmap failed: " + "%s. ", strerror(-ret)); + return -EIO; + } + + s->kvm_dirty_ring_with_bitmap = true; + } + + s->kvm_dirty_ring_size = ring_size; + s->kvm_dirty_ring_bytes = ring_bytes; return 0; } @@ -1498,18 +1491,95 @@ static void kvm_region_add(MemoryListener *listener, MemoryRegionSection *section) { KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener); + KVMMemoryUpdate *update; - memory_region_ref(section->mr); - kvm_set_phys_mem(kml, section, true); + update = g_new0(KVMMemoryUpdate, 1); + update->section = *section; + + QSIMPLEQ_INSERT_TAIL(&kml->transaction_add, update, next); } static void kvm_region_del(MemoryListener *listener, MemoryRegionSection *section) { KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener); + KVMMemoryUpdate *update; - kvm_set_phys_mem(kml, section, false); - memory_region_unref(section->mr); + update = g_new0(KVMMemoryUpdate, 1); + update->section = *section; + + QSIMPLEQ_INSERT_TAIL(&kml->transaction_del, update, next); +} + +static void kvm_region_commit(MemoryListener *listener) +{ + KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, + listener); + KVMMemoryUpdate *u1, *u2; + bool need_inhibit = false; + + if (QSIMPLEQ_EMPTY(&kml->transaction_add) && + QSIMPLEQ_EMPTY(&kml->transaction_del)) { + return; + } + + /* + * We have to be careful when regions to add overlap with ranges to remove. + * We have to simulate atomic KVM memslot updates by making sure no ioctl() + * is currently active. + * + * The lists are order by addresses, so it's easy to find overlaps. + */ + u1 = QSIMPLEQ_FIRST(&kml->transaction_del); + u2 = QSIMPLEQ_FIRST(&kml->transaction_add); + while (u1 && u2) { + Range r1, r2; + + range_init_nofail(&r1, u1->section.offset_within_address_space, + int128_get64(u1->section.size)); + range_init_nofail(&r2, u2->section.offset_within_address_space, + int128_get64(u2->section.size)); + + if (range_overlaps_range(&r1, &r2)) { + need_inhibit = true; + break; + } + if (range_lob(&r1) < range_lob(&r2)) { + u1 = QSIMPLEQ_NEXT(u1, next); + } else { + u2 = QSIMPLEQ_NEXT(u2, next); + } + } + + kvm_slots_lock(); + if (need_inhibit) { + accel_ioctl_inhibit_begin(); + } + + /* Remove all memslots before adding the new ones. */ + while (!QSIMPLEQ_EMPTY(&kml->transaction_del)) { + u1 = QSIMPLEQ_FIRST(&kml->transaction_del); + QSIMPLEQ_REMOVE_HEAD(&kml->transaction_del, next); + + kvm_set_phys_mem(kml, &u1->section, false); + memory_region_unref(u1->section.mr); + + g_free(u1); + } + while (!QSIMPLEQ_EMPTY(&kml->transaction_add)) { + u1 = QSIMPLEQ_FIRST(&kml->transaction_add); + QSIMPLEQ_REMOVE_HEAD(&kml->transaction_add, next); + + memory_region_ref(u1->section.mr); + kvm_set_phys_mem(kml, &u1->section, true); + + g_free(u1); + } + + if (need_inhibit) { + accel_ioctl_inhibit_end(); + } + kvm_slots_unlock(); } static void kvm_log_sync(MemoryListener *listener, @@ -1522,7 +1592,7 @@ static void kvm_log_sync(MemoryListener *listener, kvm_slots_unlock(); } -static void kvm_log_sync_global(MemoryListener *l) +static void kvm_log_sync_global(MemoryListener *l, bool last_stage) { KVMMemoryListener *kml = container_of(l, KVMMemoryListener, listener); KVMState *s = kvm_state; @@ -1541,6 +1611,12 @@ static void kvm_log_sync_global(MemoryListener *l) mem = &kml->slots[i]; if (mem->memory_size && mem->flags & KVM_MEM_LOG_DIRTY_PAGES) { kvm_slot_sync_dirty_pages(mem); + + if (s->kvm_dirty_ring_with_bitmap && last_stage && + kvm_slot_get_dirty_log(s, mem)) { + kvm_slot_sync_dirty_pages(mem); + } + /* * This is not needed by KVM_GET_DIRTY_LOG because the * ioctl will unconditionally overwrite the whole region. @@ -1653,11 +1729,15 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, kml->slots[i].slot = i; } + QSIMPLEQ_INIT(&kml->transaction_add); + QSIMPLEQ_INIT(&kml->transaction_del); + kml->listener.region_add = kvm_region_add; kml->listener.region_del = kvm_region_del; + kml->listener.commit = kvm_region_commit; kml->listener.log_start = kvm_log_start; kml->listener.log_stop = kvm_log_stop; - kml->listener.priority = 10; + kml->listener.priority = MEMORY_LISTENER_PRIORITY_ACCEL; kml->listener.name = name; if (s->kvm_dirty_ring_size) { @@ -1680,9 +1760,11 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, static MemoryListener kvm_io_listener = { .name = "kvm-io", + .coalesced_io_add = kvm_coalesce_pio_add, + .coalesced_io_del = kvm_coalesce_pio_del, .eventfd_add = kvm_io_ioeventfd_add, .eventfd_del = kvm_io_ioeventfd_del, - .priority = 10, + .priority = MEMORY_LISTENER_PRIORITY_DEV_BACKEND, }; int kvm_set_irq(KVMState *s, int irq, int level) @@ -1721,7 +1803,7 @@ static void clear_gsi(KVMState *s, unsigned int gsi) void kvm_init_irq_routing(KVMState *s) { - int gsi_count, i; + int gsi_count; gsi_count = kvm_check_extension(s, KVM_CAP_IRQ_ROUTING) - 1; if (gsi_count > 0) { @@ -1733,12 +1815,6 @@ void kvm_init_irq_routing(KVMState *s) s->irq_routes = g_malloc0(sizeof(*s->irq_routes)); s->nr_allocated_irq_routes = 0; - if (!kvm_direct_msi_allowed) { - for (i = 0; i < KVM_MSI_HASHTAB_SIZE; i++) { - QTAILQ_INIT(&s->msi_hashtab[i]); - } - } - kvm_arch_init_irq_routing(s); } @@ -1858,41 +1934,10 @@ void kvm_irqchip_change_notify(void) notifier_list_notify(&kvm_irqchip_change_notifiers, NULL); } -static unsigned int kvm_hash_msi(uint32_t data) -{ - /* This is optimized for IA32 MSI layout. However, no other arch shall - * repeat the mistake of not providing a direct MSI injection API. */ - return data & 0xff; -} - -static void kvm_flush_dynamic_msi_routes(KVMState *s) -{ - KVMMSIRoute *route, *next; - unsigned int hash; - - for (hash = 0; hash < KVM_MSI_HASHTAB_SIZE; hash++) { - QTAILQ_FOREACH_SAFE(route, &s->msi_hashtab[hash], entry, next) { - kvm_irqchip_release_virq(s, route->kroute.gsi); - QTAILQ_REMOVE(&s->msi_hashtab[hash], route, entry); - g_free(route); - } - } -} - static int kvm_irqchip_get_virq(KVMState *s) { int next_virq; - /* - * PIC and IOAPIC share the first 16 GSI numbers, thus the available - * GSI numbers are more than the number of IRQ route. Allocating a GSI - * number can succeed even though a new route entry cannot be added. - * When this happens, flush dynamic MSI entries to free IRQ route entries. - */ - if (!kvm_direct_msi_allowed && s->irq_routes->nr == s->gsi_count) { - kvm_flush_dynamic_msi_routes(s); - } - /* Return the lowest unused GSI in the bitmap */ next_virq = find_first_zero_bit(s->used_gsi_bitmap, s->gsi_count); if (next_virq >= s->gsi_count) { @@ -1902,63 +1947,17 @@ static int kvm_irqchip_get_virq(KVMState *s) } } -static KVMMSIRoute *kvm_lookup_msi_route(KVMState *s, MSIMessage msg) -{ - unsigned int hash = kvm_hash_msi(msg.data); - KVMMSIRoute *route; - - QTAILQ_FOREACH(route, &s->msi_hashtab[hash], entry) { - if (route->kroute.u.msi.address_lo == (uint32_t)msg.address && - route->kroute.u.msi.address_hi == (msg.address >> 32) && - route->kroute.u.msi.data == le32_to_cpu(msg.data)) { - return route; - } - } - return NULL; -} - int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg) { struct kvm_msi msi; - KVMMSIRoute *route; - if (kvm_direct_msi_allowed) { - msi.address_lo = (uint32_t)msg.address; - msi.address_hi = msg.address >> 32; - msi.data = le32_to_cpu(msg.data); - msi.flags = 0; - memset(msi.pad, 0, sizeof(msi.pad)); + msi.address_lo = (uint32_t)msg.address; + msi.address_hi = msg.address >> 32; + msi.data = le32_to_cpu(msg.data); + msi.flags = 0; + memset(msi.pad, 0, sizeof(msi.pad)); - return kvm_vm_ioctl(s, KVM_SIGNAL_MSI, &msi); - } - - route = kvm_lookup_msi_route(s, msg); - if (!route) { - int virq; - - virq = kvm_irqchip_get_virq(s); - if (virq < 0) { - return virq; - } - - route = g_new0(KVMMSIRoute, 1); - route->kroute.gsi = virq; - route->kroute.type = KVM_IRQ_ROUTING_MSI; - route->kroute.flags = 0; - route->kroute.u.msi.address_lo = (uint32_t)msg.address; - route->kroute.u.msi.address_hi = msg.address >> 32; - route->kroute.u.msi.data = le32_to_cpu(msg.data); - - kvm_add_routing_entry(s, &route->kroute); - kvm_irqchip_commit_routes(s); - - QTAILQ_INSERT_TAIL(&s->msi_hashtab[kvm_hash_msi(msg.data)], route, - entry); - } - - assert(route->kroute.type == KVM_IRQ_ROUTING_MSI); - - return kvm_set_irq(s, route->kroute.gsi, 1); + return kvm_vm_ioctl(s, KVM_SIGNAL_MSI, &msi); } int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev) @@ -2000,12 +1999,17 @@ int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev) return -EINVAL; } - trace_kvm_irqchip_add_msi_route(dev ? dev->name : (char *)"N/A", - vector, virq); + if (s->irq_routes->nr < s->gsi_count) { + trace_kvm_irqchip_add_msi_route(dev ? dev->name : (char *)"N/A", + vector, virq); - kvm_add_routing_entry(s, &kroute); - kvm_arch_add_msi_route_post(&kroute, vector, dev); - c->changes++; + kvm_add_routing_entry(s, &kroute); + kvm_arch_add_msi_route_post(&kroute, vector, dev); + c->changes++; + } else { + kvm_irqchip_release_virq(s, virq); + return -ENOSPC; + } return virq; } @@ -2085,10 +2089,6 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, EventNotifier *event, } } - if (!kvm_irqfds_enabled()) { - return -ENOSYS; - } - return kvm_vm_ioctl(s, KVM_IRQFD, &irqfd); } @@ -2249,12 +2249,17 @@ static void kvm_irqchip_create(KVMState *s) return; } + if (kvm_check_extension(s, KVM_CAP_IRQFD) <= 0) { + fprintf(stderr, "kvm: irqfd not implemented\n"); + exit(1); + } + /* First probe and see if there's a arch-specific hook to create the * in-kernel irqchip for us */ ret = kvm_arch_irqchip_create(s); if (ret == 0) { if (s->kernel_irqchip_split == ON_OFF_AUTO_ON) { - perror("Split IRQ chip mode not supported."); + error_report("Split IRQ chip mode not supported."); exit(1); } else { ret = kvm_vm_ioctl(s, KVM_CREATE_IRQCHIP); @@ -2310,25 +2315,34 @@ bool kvm_dirty_ring_enabled(void) return kvm_state->kvm_dirty_ring_size ? true : false; } +static void query_stats_cb(StatsResultList **result, StatsTarget target, + strList *names, strList *targets, Error **errp); +static void query_stats_schemas_cb(StatsSchemaList **result, Error **errp); + +uint32_t kvm_dirty_ring_size(void) +{ + return kvm_state->kvm_dirty_ring_size; +} + static int kvm_init(MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); static const char upgrade_note[] = "Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n" "(see http://sourceforge.net/projects/kvm).\n"; - struct { + const struct { const char *name; int num; } num_cpus[] = { { "SMP", ms->smp.cpus }, { "hotpluggable", ms->smp.max_cpus }, - { NULL, } + { /* end of list */ } }, *nc = num_cpus; int soft_vcpus_limit, hard_vcpus_limit; KVMState *s; const KVMCapabilityInfo *missing_cap; int ret; - int type = 0; + int type; uint64_t dirty_log_manual_caps; qemu_mutex_init(&kml_slots_lock); @@ -2344,12 +2358,13 @@ static int kvm_init(MachineState *ms) assert(TARGET_PAGE_SIZE <= qemu_real_host_page_size()); s->sigmask_len = 8; + accel_blocker_init(); #ifdef KVM_CAP_SET_GUEST_DEBUG QTAILQ_INIT(&s->kvm_sw_breakpoints); #endif QLIST_INIT(&s->kvm_parked_vcpus); - s->fd = qemu_open_old("/dev/kvm", O_RDWR); + s->fd = qemu_open_old(s->device ?: "/dev/kvm", O_RDWR); if (s->fd == -1) { fprintf(stderr, "Could not access KVM kernel module: %m\n"); ret = -errno; @@ -2392,6 +2407,13 @@ static int kvm_init(MachineState *ms) type = mc->kvm_type(ms, kvm_type); } else if (mc->kvm_type) { type = mc->kvm_type(ms, NULL); + } else { + type = kvm_arch_get_default_type(ms); + } + + if (type < 0) { + ret = -EINVAL; + goto err; } do { @@ -2466,35 +2488,9 @@ static int kvm_init(MachineState *ms) * Enable KVM dirty ring if supported, otherwise fall back to * dirty logging mode */ - if (s->kvm_dirty_ring_size > 0) { - uint64_t ring_bytes; - - ring_bytes = s->kvm_dirty_ring_size * sizeof(struct kvm_dirty_gfn); - - /* Read the max supported pages */ - ret = kvm_vm_check_extension(s, KVM_CAP_DIRTY_LOG_RING); - if (ret > 0) { - if (ring_bytes > ret) { - error_report("KVM dirty ring size %" PRIu32 " too big " - "(maximum is %ld). Please use a smaller value.", - s->kvm_dirty_ring_size, - (long)ret / sizeof(struct kvm_dirty_gfn)); - ret = -EINVAL; - goto err; - } - - ret = kvm_vm_enable_cap(s, KVM_CAP_DIRTY_LOG_RING, 0, ring_bytes); - if (ret) { - error_report("Enabling of KVM dirty ring failed: %s. " - "Suggested minimum value is 1024.", strerror(-ret)); - goto err; - } - - s->kvm_dirty_ring_bytes = ring_bytes; - } else { - warn_report("KVM dirty ring not available, using bitmap method"); - s->kvm_dirty_ring_size = 0; - } + ret = kvm_dirty_ring_init(s); + if (ret < 0) { + goto err; } /* @@ -2532,22 +2528,8 @@ static int kvm_init(MachineState *ms) #ifdef KVM_CAP_VCPU_EVENTS s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); #endif - - s->robust_singlestep = - kvm_check_extension(s, KVM_CAP_X86_ROBUST_SINGLESTEP); - -#ifdef KVM_CAP_DEBUGREGS - s->debugregs = kvm_check_extension(s, KVM_CAP_DEBUGREGS); -#endif - s->max_nested_state_len = kvm_check_extension(s, KVM_CAP_NESTED_STATE); -#ifdef KVM_CAP_IRQ_ROUTING - kvm_direct_msi_allowed = (kvm_check_extension(s, KVM_CAP_SIGNAL_MSI) > 0); -#endif - - s->intx_set_mask = kvm_check_extension(s, KVM_CAP_PCI_2_3); - s->irq_set_ioctl = KVM_IRQ_LINE; if (kvm_check_extension(s, KVM_CAP_IRQ_INJECT_STATUS)) { s->irq_set_ioctl = KVM_IRQ_LINE_STATUS; @@ -2556,21 +2538,12 @@ static int kvm_init(MachineState *ms) kvm_readonly_mem_allowed = (kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0); - kvm_eventfds_allowed = - (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); - - kvm_irqfds_allowed = - (kvm_check_extension(s, KVM_CAP_IRQFD) > 0); - kvm_resamplefds_allowed = (kvm_check_extension(s, KVM_CAP_IRQFD_RESAMPLE) > 0); kvm_vm_attributes_allowed = (kvm_check_extension(s, KVM_CAP_VM_ATTRIBUTES) > 0); - kvm_ioeventfd_any_length_allowed = - (kvm_check_extension(s, KVM_CAP_IOEVENTFD_ANY_LENGTH) > 0); - #ifdef KVM_CAP_SET_GUEST_DEBUG kvm_has_guest_debug = (kvm_check_extension(s, KVM_CAP_SET_GUEST_DEBUG) > 0); @@ -2607,24 +2580,16 @@ static int kvm_init(MachineState *ms) kvm_irqchip_create(s); } - if (kvm_eventfds_allowed) { - s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; - s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; - } + s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add; + s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del; s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region; s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region; kvm_memory_listener_register(s, &s->memory_listener, &address_space_memory, 0, "kvm-memory"); - if (kvm_eventfds_allowed) { - memory_listener_register(&kvm_io_listener, - &address_space_io); - } - memory_listener_register(&kvm_coalesced_pio_listener, + memory_listener_register(&kvm_io_listener, &address_space_io); - s->many_ioeventfds = kvm_check_many_ioeventfds(); - s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); if (!s->sync_mmu) { ret = ram_block_discard_disable(true); @@ -2632,10 +2597,12 @@ static int kvm_init(MachineState *ms) } if (s->kvm_dirty_ring_size) { - ret = kvm_dirty_ring_reaper_init(s); - if (ret) { - goto err; - } + kvm_dirty_ring_reaper_init(s); + } + + if (kvm_check_extension(kvm_state, KVM_CAP_BINARY_STATS_FD)) { + add_stats_callbacks(STATS_PROVIDER_KVM, query_stats_cb, + query_stats_schemas_cb); } return 0; @@ -2648,6 +2615,7 @@ err: if (s->fd != -1) { close(s->fd); } + g_free(s->as); g_free(s->memory_listener.slots); return ret; @@ -2674,16 +2642,14 @@ static void kvm_handle_io(uint16_t port, MemTxAttrs attrs, void *data, int direc static int kvm_handle_internal_error(CPUState *cpu, struct kvm_run *run) { + int i; + fprintf(stderr, "KVM internal error. Suberror: %d\n", run->internal.suberror); - if (kvm_check_extension(kvm_state, KVM_CAP_INTERNAL_ERROR_DATA)) { - int i; - - for (i = 0; i < run->internal.ndata; ++i) { - fprintf(stderr, "extra data[%d]: 0x%016"PRIx64"\n", - i, (uint64_t)run->internal.data[i]); - } + for (i = 0; i < run->internal.ndata; ++i) { + fprintf(stderr, "extra data[%d]: 0x%016"PRIx64"\n", + i, (uint64_t)run->internal.data[i]); } if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) { fprintf(stderr, "emulation failure\n"); @@ -2702,7 +2668,7 @@ void kvm_flush_coalesced_mmio_buffer(void) { KVMState *s = kvm_state; - if (s->coalesced_flush_in_progress) { + if (!s || s->coalesced_flush_in_progress) { return; } @@ -2738,7 +2704,13 @@ bool kvm_cpu_check_are_resettable(void) static void do_kvm_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { if (!cpu->vcpu_dirty) { - kvm_arch_get_registers(cpu); + int ret = kvm_arch_get_registers(cpu); + if (ret) { + error_report("Failed to get registers: %s", strerror(-ret)); + cpu_dump_state(cpu, stderr, CPU_DUMP_CODE); + vm_stop(RUN_STATE_INTERNAL_ERROR); + } + cpu->vcpu_dirty = true; } } @@ -2752,7 +2724,13 @@ void kvm_cpu_synchronize_state(CPUState *cpu) static void do_kvm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) { - kvm_arch_put_registers(cpu, KVM_PUT_RESET_STATE); + int ret = kvm_arch_put_registers(cpu, KVM_PUT_RESET_STATE); + if (ret) { + error_report("Failed to put registers after reset: %s", strerror(-ret)); + cpu_dump_state(cpu, stderr, CPU_DUMP_CODE); + vm_stop(RUN_STATE_INTERNAL_ERROR); + } + cpu->vcpu_dirty = false; } @@ -2763,7 +2741,12 @@ void kvm_cpu_synchronize_post_reset(CPUState *cpu) static void do_kvm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) { - kvm_arch_put_registers(cpu, KVM_PUT_FULL_STATE); + int ret = kvm_arch_put_registers(cpu, KVM_PUT_FULL_STATE); + if (ret) { + error_report("Failed to put registers after init: %s", strerror(-ret)); + exit(1); + } + cpu->vcpu_dirty = false; } @@ -2842,27 +2825,34 @@ int kvm_cpu_exec(CPUState *cpu) struct kvm_run *run = cpu->kvm_run; int ret, run_ret; - DPRINTF("kvm_cpu_exec()\n"); + trace_kvm_cpu_exec(); if (kvm_arch_process_async_events(cpu)) { qatomic_set(&cpu->exit_request, 0); return EXCP_HLT; } - qemu_mutex_unlock_iothread(); + bql_unlock(); cpu_exec_start(cpu); do { MemTxAttrs attrs; if (cpu->vcpu_dirty) { - kvm_arch_put_registers(cpu, KVM_PUT_RUNTIME_STATE); + ret = kvm_arch_put_registers(cpu, KVM_PUT_RUNTIME_STATE); + if (ret) { + error_report("Failed to put registers after init: %s", + strerror(-ret)); + ret = -1; + break; + } + cpu->vcpu_dirty = false; } kvm_arch_pre_run(cpu, run); if (qatomic_read(&cpu->exit_request)) { - DPRINTF("interrupt exit requested\n"); + trace_kvm_interrupt_exit_request(); /* * KVM requires us to reenter the kernel after IO exits to complete * instruction emulation. This self-signal will ensure that we @@ -2882,17 +2872,17 @@ int kvm_cpu_exec(CPUState *cpu) #ifdef KVM_HAVE_MCE_INJECTION if (unlikely(have_sigbus_pending)) { - qemu_mutex_lock_iothread(); + bql_lock(); kvm_arch_on_sigbus_vcpu(cpu, pending_sigbus_code, pending_sigbus_addr); have_sigbus_pending = false; - qemu_mutex_unlock_iothread(); + bql_unlock(); } #endif if (run_ret < 0) { if (run_ret == -EINTR || run_ret == -EAGAIN) { - DPRINTF("io window exit\n"); + trace_kvm_io_window_exit(); kvm_eat_signals(cpu); ret = EXCP_INTERRUPT; break; @@ -2914,7 +2904,6 @@ int kvm_cpu_exec(CPUState *cpu) trace_kvm_run_exit(cpu->cpu_index, run->exit_reason); switch (run->exit_reason) { case KVM_EXIT_IO: - DPRINTF("handle_io\n"); /* Called outside BQL */ kvm_handle_io(run->io.port, attrs, (uint8_t *)run + run->io.data_offset, @@ -2924,7 +2913,6 @@ int kvm_cpu_exec(CPUState *cpu) ret = 0; break; case KVM_EXIT_MMIO: - DPRINTF("handle_mmio\n"); /* Called outside BQL */ address_space_rw(&address_space_memory, run->mmio.phys_addr, attrs, @@ -2934,11 +2922,9 @@ int kvm_cpu_exec(CPUState *cpu) ret = 0; break; case KVM_EXIT_IRQ_WINDOW_OPEN: - DPRINTF("irq_window_open\n"); ret = EXCP_INTERRUPT; break; case KVM_EXIT_SHUTDOWN: - DPRINTF("shutdown\n"); qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); ret = EXCP_INTERRUPT; break; @@ -2956,12 +2942,24 @@ int kvm_cpu_exec(CPUState *cpu) * still full. Got kicked by KVM_RESET_DIRTY_RINGS. */ trace_kvm_dirty_ring_full(cpu->cpu_index); - qemu_mutex_lock_iothread(); - kvm_dirty_ring_reap(kvm_state); - qemu_mutex_unlock_iothread(); + bql_lock(); + /* + * We throttle vCPU by making it sleep once it exit from kernel + * due to dirty ring full. In the dirtylimit scenario, reaping + * all vCPUs after a single vCPU dirty ring get full result in + * the miss of sleep, so just reap the ring-fulled vCPU. + */ + if (dirtylimit_in_service()) { + kvm_dirty_ring_reap(kvm_state, cpu); + } else { + kvm_dirty_ring_reap(kvm_state, NULL); + } + bql_unlock(); + dirtylimit_vcpu_execute(cpu); ret = 0; break; case KVM_EXIT_SYSTEM_EVENT: + trace_kvm_run_exit_system_event(cpu->cpu_index, run->system_event.type); switch (run->system_event.type) { case KVM_SYSTEM_EVENT_SHUTDOWN: qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); @@ -2973,26 +2971,24 @@ int kvm_cpu_exec(CPUState *cpu) break; case KVM_SYSTEM_EVENT_CRASH: kvm_cpu_synchronize_state(cpu); - qemu_mutex_lock_iothread(); + bql_lock(); qemu_system_guest_panicked(cpu_get_crash_info(cpu)); - qemu_mutex_unlock_iothread(); + bql_unlock(); ret = 0; break; default: - DPRINTF("kvm_arch_handle_exit\n"); ret = kvm_arch_handle_exit(cpu, run); break; } break; default: - DPRINTF("kvm_arch_handle_exit\n"); ret = kvm_arch_handle_exit(cpu, run); break; } } while (ret == 0); cpu_exec_end(cpu); - qemu_mutex_lock_iothread(); + bql_lock(); if (ret < 0) { cpu_dump_state(cpu, stderr, CPU_DUMP_CODE); @@ -3032,7 +3028,9 @@ int kvm_vm_ioctl(KVMState *s, int type, ...) va_end(ap); trace_kvm_vm_ioctl(type, arg); + accel_ioctl_begin(); ret = ioctl(s->vmfd, type, arg); + accel_ioctl_end(); if (ret == -1) { ret = -errno; } @@ -3050,7 +3048,9 @@ int kvm_vcpu_ioctl(CPUState *cpu, int type, ...) va_end(ap); trace_kvm_vcpu_ioctl(cpu->cpu_index, type, arg); + accel_cpu_ioctl_begin(cpu); ret = ioctl(cpu->kvm_fd, type, arg); + accel_cpu_ioctl_end(cpu); if (ret == -1) { ret = -errno; } @@ -3068,7 +3068,9 @@ int kvm_device_ioctl(int fd, int type, ...) va_end(ap); trace_kvm_device_ioctl(fd, type, arg); + accel_ioctl_begin(); ret = ioctl(fd, type, arg); + accel_ioctl_end(); if (ret == -1) { ret = -errno; } @@ -3136,29 +3138,11 @@ int kvm_has_vcpu_events(void) return kvm_state->vcpu_events; } -int kvm_has_robust_singlestep(void) -{ - return kvm_state->robust_singlestep; -} - -int kvm_has_debugregs(void) -{ - return kvm_state->debugregs; -} - int kvm_max_nested_state_length(void) { return kvm_state->max_nested_state_len; } -int kvm_has_many_ioeventfds(void) -{ - if (!kvm_enabled()) { - return 0; - } - return kvm_state->many_ioeventfds; -} - int kvm_has_gsi_routing(void) { #ifdef KVM_CAP_IRQ_ROUTING @@ -3168,19 +3152,13 @@ int kvm_has_gsi_routing(void) #endif } -int kvm_has_intx_set_mask(void) -{ - return kvm_state->intx_set_mask; -} - bool kvm_arm_supports_user_irq(void) { return kvm_check_extension(kvm_state, KVM_CAP_ARM_USER_IRQ); } #ifdef KVM_CAP_SET_GUEST_DEBUG -struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu, - target_ulong pc) +struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu, vaddr pc) { struct kvm_sw_breakpoint *bp; @@ -3231,8 +3209,13 @@ int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap) return data.err; } -int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, - target_ulong len, int type) +bool kvm_supports_guest_debug(void) +{ + /* probed during kvm_init() */ + return kvm_has_guest_debug; +} + +int kvm_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len) { struct kvm_sw_breakpoint *bp; int err; @@ -3270,8 +3253,7 @@ int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, return 0; } -int kvm_remove_breakpoint(CPUState *cpu, target_ulong addr, - target_ulong len, int type) +int kvm_remove_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len) { struct kvm_sw_breakpoint *bp; int err; @@ -3335,28 +3317,6 @@ void kvm_remove_all_breakpoints(CPUState *cpu) } } -#else /* !KVM_CAP_SET_GUEST_DEBUG */ - -int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap) -{ - return -EINVAL; -} - -int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, - target_ulong len, int type) -{ - return -EINVAL; -} - -int kvm_remove_breakpoint(CPUState *cpu, target_ulong addr, - target_ulong len, int type) -{ - return -EINVAL; -} - -void kvm_remove_all_breakpoints(CPUState *cpu) -{ -} #endif /* !KVM_CAP_SET_GUEST_DEBUG */ static int kvm_set_signal_mask(CPUState *cpu, const sigset_t *sigset) @@ -3622,7 +3582,6 @@ static void kvm_set_dirty_ring_size(Object *obj, Visitor *v, Error **errp) { KVMState *s = KVM_STATE(obj); - Error *error = NULL; uint32_t value; if (s->fd != -1) { @@ -3630,9 +3589,7 @@ static void kvm_set_dirty_ring_size(Object *obj, Visitor *v, return; } - visit_type_uint32(v, name, &value, &error); - if (error) { - error_propagate(errp, error); + if (!visit_type_uint32(v, name, &value, errp)) { return; } if (value & (value - 1)) { @@ -3643,6 +3600,24 @@ static void kvm_set_dirty_ring_size(Object *obj, Visitor *v, s->kvm_dirty_ring_size = value; } +static char *kvm_get_device(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + KVMState *s = KVM_STATE(obj); + + return g_strdup(s->device); +} + +static void kvm_set_device(Object *obj, + const char *value, + Error **errp G_GNUC_UNUSED) +{ + KVMState *s = KVM_STATE(obj); + + g_free(s->device); + s->device = g_strdup(value); +} + static void kvm_accel_instance_init(Object *obj) { KVMState *s = KVM_STATE(obj); @@ -3654,6 +3629,25 @@ static void kvm_accel_instance_init(Object *obj) s->kernel_irqchip_split = ON_OFF_AUTO_AUTO; /* KVM dirty ring is by default off */ s->kvm_dirty_ring_size = 0; + s->kvm_dirty_ring_with_bitmap = false; + s->kvm_eager_split_size = 0; + s->notify_vmexit = NOTIFY_VMEXIT_OPTION_RUN; + s->notify_window = 0; + s->xen_version = 0; + s->xen_gnttab_max_frames = 64; + s->xen_evtchn_max_pirq = 256; + s->device = NULL; +} + +/** + * kvm_gdbstub_sstep_flags(): + * + * Returns: SSTEP_* flags that KVM supports for guest debug. The + * support is probed during kvm_init() + */ +static int kvm_gdbstub_sstep_flags(void) +{ + return kvm_sstep_flags; } static void kvm_accel_class_init(ObjectClass *oc, void *data) @@ -3663,6 +3657,7 @@ 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; + ac->gdbstub_supported_sstep_flags = kvm_gdbstub_sstep_flags; object_class_property_add(oc, "kernel-irqchip", "on|off|split", NULL, kvm_set_kernel_irqchip, @@ -3681,6 +3676,12 @@ static void kvm_accel_class_init(ObjectClass *oc, void *data) NULL, NULL); object_class_property_set_description(oc, "dirty-ring-size", "Size of KVM dirty page ring buffer (default: 0, i.e. use bitmap)"); + + object_class_property_add_str(oc, "device", kvm_get_device, kvm_set_device); + object_class_property_set_description(oc, "device", + "Path to the device node to use (default: /dev/kvm)"); + + kvm_arch_accel_class_init(oc); } static const TypeInfo kvm_accel_type = { @@ -3697,3 +3698,404 @@ static void kvm_type_init(void) } type_init(kvm_type_init); + +typedef struct StatsArgs { + union StatsResultsType { + StatsResultList **stats; + StatsSchemaList **schema; + } result; + strList *names; + Error **errp; +} StatsArgs; + +static StatsList *add_kvmstat_entry(struct kvm_stats_desc *pdesc, + uint64_t *stats_data, + StatsList *stats_list, + Error **errp) +{ + + Stats *stats; + uint64List *val_list = NULL; + + /* Only add stats that we understand. */ + switch (pdesc->flags & KVM_STATS_TYPE_MASK) { + case KVM_STATS_TYPE_CUMULATIVE: + case KVM_STATS_TYPE_INSTANT: + case KVM_STATS_TYPE_PEAK: + case KVM_STATS_TYPE_LINEAR_HIST: + case KVM_STATS_TYPE_LOG_HIST: + break; + default: + return stats_list; + } + + switch (pdesc->flags & KVM_STATS_UNIT_MASK) { + case KVM_STATS_UNIT_NONE: + case KVM_STATS_UNIT_BYTES: + case KVM_STATS_UNIT_CYCLES: + case KVM_STATS_UNIT_SECONDS: + case KVM_STATS_UNIT_BOOLEAN: + break; + default: + return stats_list; + } + + switch (pdesc->flags & KVM_STATS_BASE_MASK) { + case KVM_STATS_BASE_POW10: + case KVM_STATS_BASE_POW2: + break; + default: + return stats_list; + } + + /* Alloc and populate data list */ + stats = g_new0(Stats, 1); + stats->name = g_strdup(pdesc->name); + stats->value = g_new0(StatsValue, 1);; + + if ((pdesc->flags & KVM_STATS_UNIT_MASK) == KVM_STATS_UNIT_BOOLEAN) { + stats->value->u.boolean = *stats_data; + stats->value->type = QTYPE_QBOOL; + } else if (pdesc->size == 1) { + stats->value->u.scalar = *stats_data; + stats->value->type = QTYPE_QNUM; + } else { + int i; + for (i = 0; i < pdesc->size; i++) { + QAPI_LIST_PREPEND(val_list, stats_data[i]); + } + stats->value->u.list = val_list; + stats->value->type = QTYPE_QLIST; + } + + QAPI_LIST_PREPEND(stats_list, stats); + return stats_list; +} + +static StatsSchemaValueList *add_kvmschema_entry(struct kvm_stats_desc *pdesc, + StatsSchemaValueList *list, + Error **errp) +{ + StatsSchemaValueList *schema_entry = g_new0(StatsSchemaValueList, 1); + schema_entry->value = g_new0(StatsSchemaValue, 1); + + switch (pdesc->flags & KVM_STATS_TYPE_MASK) { + case KVM_STATS_TYPE_CUMULATIVE: + schema_entry->value->type = STATS_TYPE_CUMULATIVE; + break; + case KVM_STATS_TYPE_INSTANT: + schema_entry->value->type = STATS_TYPE_INSTANT; + break; + case KVM_STATS_TYPE_PEAK: + schema_entry->value->type = STATS_TYPE_PEAK; + break; + case KVM_STATS_TYPE_LINEAR_HIST: + schema_entry->value->type = STATS_TYPE_LINEAR_HISTOGRAM; + schema_entry->value->bucket_size = pdesc->bucket_size; + schema_entry->value->has_bucket_size = true; + break; + case KVM_STATS_TYPE_LOG_HIST: + schema_entry->value->type = STATS_TYPE_LOG2_HISTOGRAM; + break; + default: + goto exit; + } + + switch (pdesc->flags & KVM_STATS_UNIT_MASK) { + case KVM_STATS_UNIT_NONE: + break; + case KVM_STATS_UNIT_BOOLEAN: + schema_entry->value->has_unit = true; + schema_entry->value->unit = STATS_UNIT_BOOLEAN; + break; + case KVM_STATS_UNIT_BYTES: + schema_entry->value->has_unit = true; + schema_entry->value->unit = STATS_UNIT_BYTES; + break; + case KVM_STATS_UNIT_CYCLES: + schema_entry->value->has_unit = true; + schema_entry->value->unit = STATS_UNIT_CYCLES; + break; + case KVM_STATS_UNIT_SECONDS: + schema_entry->value->has_unit = true; + schema_entry->value->unit = STATS_UNIT_SECONDS; + break; + default: + goto exit; + } + + schema_entry->value->exponent = pdesc->exponent; + if (pdesc->exponent) { + switch (pdesc->flags & KVM_STATS_BASE_MASK) { + case KVM_STATS_BASE_POW10: + schema_entry->value->has_base = true; + schema_entry->value->base = 10; + break; + case KVM_STATS_BASE_POW2: + schema_entry->value->has_base = true; + schema_entry->value->base = 2; + break; + default: + goto exit; + } + } + + schema_entry->value->name = g_strdup(pdesc->name); + schema_entry->next = list; + return schema_entry; +exit: + g_free(schema_entry->value); + g_free(schema_entry); + return list; +} + +/* Cached stats descriptors */ +typedef struct StatsDescriptors { + const char *ident; /* cache key, currently the StatsTarget */ + struct kvm_stats_desc *kvm_stats_desc; + struct kvm_stats_header kvm_stats_header; + QTAILQ_ENTRY(StatsDescriptors) next; +} StatsDescriptors; + +static QTAILQ_HEAD(, StatsDescriptors) stats_descriptors = + QTAILQ_HEAD_INITIALIZER(stats_descriptors); + +/* + * Return the descriptors for 'target', that either have already been read + * or are retrieved from 'stats_fd'. + */ +static StatsDescriptors *find_stats_descriptors(StatsTarget target, int stats_fd, + Error **errp) +{ + StatsDescriptors *descriptors; + const char *ident; + struct kvm_stats_desc *kvm_stats_desc; + struct kvm_stats_header *kvm_stats_header; + size_t size_desc; + ssize_t ret; + + ident = StatsTarget_str(target); + QTAILQ_FOREACH(descriptors, &stats_descriptors, next) { + if (g_str_equal(descriptors->ident, ident)) { + return descriptors; + } + } + + descriptors = g_new0(StatsDescriptors, 1); + + /* Read stats header */ + kvm_stats_header = &descriptors->kvm_stats_header; + ret = pread(stats_fd, kvm_stats_header, sizeof(*kvm_stats_header), 0); + if (ret != sizeof(*kvm_stats_header)) { + error_setg(errp, "KVM stats: failed to read stats header: " + "expected %zu actual %zu", + sizeof(*kvm_stats_header), ret); + g_free(descriptors); + return NULL; + } + size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size; + + /* Read stats descriptors */ + kvm_stats_desc = g_malloc0_n(kvm_stats_header->num_desc, size_desc); + ret = pread(stats_fd, kvm_stats_desc, + size_desc * kvm_stats_header->num_desc, + kvm_stats_header->desc_offset); + + if (ret != size_desc * kvm_stats_header->num_desc) { + error_setg(errp, "KVM stats: failed to read stats descriptors: " + "expected %zu actual %zu", + size_desc * kvm_stats_header->num_desc, ret); + g_free(descriptors); + g_free(kvm_stats_desc); + return NULL; + } + descriptors->kvm_stats_desc = kvm_stats_desc; + descriptors->ident = ident; + QTAILQ_INSERT_TAIL(&stats_descriptors, descriptors, next); + return descriptors; +} + +static void query_stats(StatsResultList **result, StatsTarget target, + strList *names, int stats_fd, CPUState *cpu, + Error **errp) +{ + struct kvm_stats_desc *kvm_stats_desc; + struct kvm_stats_header *kvm_stats_header; + StatsDescriptors *descriptors; + g_autofree uint64_t *stats_data = NULL; + struct kvm_stats_desc *pdesc; + StatsList *stats_list = NULL; + size_t size_desc, size_data = 0; + ssize_t ret; + int i; + + descriptors = find_stats_descriptors(target, stats_fd, errp); + if (!descriptors) { + return; + } + + kvm_stats_header = &descriptors->kvm_stats_header; + kvm_stats_desc = descriptors->kvm_stats_desc; + size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size; + + /* Tally the total data size; read schema data */ + for (i = 0; i < kvm_stats_header->num_desc; ++i) { + pdesc = (void *)kvm_stats_desc + i * size_desc; + size_data += pdesc->size * sizeof(*stats_data); + } + + stats_data = g_malloc0(size_data); + ret = pread(stats_fd, stats_data, size_data, kvm_stats_header->data_offset); + + if (ret != size_data) { + error_setg(errp, "KVM stats: failed to read data: " + "expected %zu actual %zu", size_data, ret); + return; + } + + for (i = 0; i < kvm_stats_header->num_desc; ++i) { + uint64_t *stats; + pdesc = (void *)kvm_stats_desc + i * size_desc; + + /* Add entry to the list */ + stats = (void *)stats_data + pdesc->offset; + if (!apply_str_list_filter(pdesc->name, names)) { + continue; + } + stats_list = add_kvmstat_entry(pdesc, stats, stats_list, errp); + } + + if (!stats_list) { + return; + } + + switch (target) { + case STATS_TARGET_VM: + add_stats_entry(result, STATS_PROVIDER_KVM, NULL, stats_list); + break; + case STATS_TARGET_VCPU: + add_stats_entry(result, STATS_PROVIDER_KVM, + cpu->parent_obj.canonical_path, + stats_list); + break; + default: + g_assert_not_reached(); + } +} + +static void query_stats_schema(StatsSchemaList **result, StatsTarget target, + int stats_fd, Error **errp) +{ + struct kvm_stats_desc *kvm_stats_desc; + struct kvm_stats_header *kvm_stats_header; + StatsDescriptors *descriptors; + struct kvm_stats_desc *pdesc; + StatsSchemaValueList *stats_list = NULL; + size_t size_desc; + int i; + + descriptors = find_stats_descriptors(target, stats_fd, errp); + if (!descriptors) { + return; + } + + kvm_stats_header = &descriptors->kvm_stats_header; + kvm_stats_desc = descriptors->kvm_stats_desc; + size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size; + + /* Tally the total data size; read schema data */ + for (i = 0; i < kvm_stats_header->num_desc; ++i) { + pdesc = (void *)kvm_stats_desc + i * size_desc; + stats_list = add_kvmschema_entry(pdesc, stats_list, errp); + } + + add_stats_schema(result, STATS_PROVIDER_KVM, target, stats_list); +} + +static void query_stats_vcpu(CPUState *cpu, StatsArgs *kvm_stats_args) +{ + int stats_fd = cpu->kvm_vcpu_stats_fd; + Error *local_err = NULL; + + if (stats_fd == -1) { + error_setg_errno(&local_err, errno, "KVM stats: ioctl failed"); + error_propagate(kvm_stats_args->errp, local_err); + return; + } + query_stats(kvm_stats_args->result.stats, STATS_TARGET_VCPU, + kvm_stats_args->names, stats_fd, cpu, + kvm_stats_args->errp); +} + +static void query_stats_schema_vcpu(CPUState *cpu, StatsArgs *kvm_stats_args) +{ + int stats_fd = cpu->kvm_vcpu_stats_fd; + Error *local_err = NULL; + + if (stats_fd == -1) { + error_setg_errno(&local_err, errno, "KVM stats: ioctl failed"); + error_propagate(kvm_stats_args->errp, local_err); + return; + } + query_stats_schema(kvm_stats_args->result.schema, STATS_TARGET_VCPU, stats_fd, + kvm_stats_args->errp); +} + +static void query_stats_cb(StatsResultList **result, StatsTarget target, + strList *names, strList *targets, Error **errp) +{ + KVMState *s = kvm_state; + CPUState *cpu; + int stats_fd; + + switch (target) { + case STATS_TARGET_VM: + { + stats_fd = kvm_vm_ioctl(s, KVM_GET_STATS_FD, NULL); + if (stats_fd == -1) { + error_setg_errno(errp, errno, "KVM stats: ioctl failed"); + return; + } + query_stats(result, target, names, stats_fd, NULL, errp); + close(stats_fd); + break; + } + case STATS_TARGET_VCPU: + { + StatsArgs stats_args; + stats_args.result.stats = result; + stats_args.names = names; + stats_args.errp = errp; + CPU_FOREACH(cpu) { + if (!apply_str_list_filter(cpu->parent_obj.canonical_path, targets)) { + continue; + } + query_stats_vcpu(cpu, &stats_args); + } + break; + } + default: + break; + } +} + +void query_stats_schemas_cb(StatsSchemaList **result, Error **errp) +{ + StatsArgs stats_args; + KVMState *s = kvm_state; + int stats_fd; + + stats_fd = kvm_vm_ioctl(s, KVM_GET_STATS_FD, NULL); + if (stats_fd == -1) { + error_setg_errno(errp, errno, "KVM stats: ioctl failed"); + return; + } + query_stats_schema(result, STATS_TARGET_VM, stats_fd, errp); + close(stats_fd); + + if (first_cpu) { + stats_args.result.schema = result; + stats_args.errp = errp; + query_stats_schema_vcpu(first_cpu, &stats_args); + } +} diff --git a/accel/kvm/kvm-cpus.h b/accel/kvm/kvm-cpus.h index bf0bd1bee4..ca40add32c 100644 --- a/accel/kvm/kvm-cpus.h +++ b/accel/kvm/kvm-cpus.h @@ -18,5 +18,9 @@ void kvm_destroy_vcpu(CPUState *cpu); void kvm_cpu_synchronize_post_reset(CPUState *cpu); void kvm_cpu_synchronize_post_init(CPUState *cpu); void kvm_cpu_synchronize_pre_loadvm(CPUState *cpu); +bool kvm_supports_guest_debug(void); +int kvm_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len); +int kvm_remove_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len); +void kvm_remove_all_breakpoints(CPUState *cpu); #endif /* KVM_CPUS_H */ diff --git a/accel/kvm/trace-events b/accel/kvm/trace-events index 399aaeb0ec..a25902597b 100644 --- a/accel/kvm/trace-events +++ b/accel/kvm/trace-events @@ -25,4 +25,9 @@ kvm_dirty_ring_reaper(const char *s) "%s" kvm_dirty_ring_reap(uint64_t count, int64_t t) "reaped %"PRIu64" pages (took %"PRIi64" us)" kvm_dirty_ring_reaper_kick(const char *reason) "%s" kvm_dirty_ring_flush(int finished) "%d" - +kvm_destroy_vcpu(void) "" +kvm_failed_get_vcpu_mmap_size(void) "" +kvm_cpu_exec(void) "" +kvm_interrupt_exit_request(void) "" +kvm_io_window_exit(void) "" +kvm_run_exit_system_event(int cpu_index, uint32_t event_type) "cpu_index %d, system_even_type %"PRIu32 diff --git a/accel/meson.build b/accel/meson.build index b9a963cf80..5eaeb68338 100644 --- a/accel/meson.build +++ b/accel/meson.build @@ -1,5 +1,5 @@ -specific_ss.add(files('accel-common.c')) -softmmu_ss.add(files('accel-softmmu.c')) +specific_ss.add(files('accel-target.c')) +system_ss.add(files('accel-system.c', 'accel-blocker.c')) user_ss.add(files('accel-user.c')) subdir('tcg') @@ -11,10 +11,5 @@ if have_system subdir('stubs') endif -dummy_ss = ss.source_set() -dummy_ss.add(files( - 'dummy-cpus.c', -)) - -specific_ss.add_all(when: ['CONFIG_SOFTMMU', 'CONFIG_POSIX'], if_true: dummy_ss) -specific_ss.add_all(when: ['CONFIG_XEN'], if_true: dummy_ss) +# qtest +system_ss.add(files('dummy-cpus.c')) diff --git a/accel/qtest/meson.build b/accel/qtest/meson.build index 4c65600293..2018de8a05 100644 --- a/accel/qtest/meson.build +++ b/accel/qtest/meson.build @@ -1,2 +1 @@ -qtest_module_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_POSIX'], - if_true: files('qtest.c')) +qtest_module_ss.add(when: ['CONFIG_SYSTEM_ONLY'], if_true: files('qtest.c')) diff --git a/accel/stubs/hax-stub.c b/accel/stubs/hax-stub.c deleted file mode 100644 index 2fe31aaa9a..0000000000 --- a/accel/stubs/hax-stub.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * QEMU HAXM support - * - * Copyright (c) 2015, Intel Corporation - * - * Copyright 2016 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "sysemu/hax.h" - -bool hax_allowed; - -int hax_sync_vcpus(void) -{ - return 0; -} diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index 3345882d85..ca38172884 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -17,15 +17,12 @@ KVMState *kvm_state; bool kvm_kernel_irqchip; bool kvm_async_interrupts_allowed; -bool kvm_eventfds_allowed; -bool kvm_irqfds_allowed; bool kvm_resamplefds_allowed; bool kvm_msi_via_irqfd_allowed; bool kvm_gsi_routing_allowed; bool kvm_gsi_direct_mapping; bool kvm_allowed; bool kvm_readonly_mem_allowed; -bool kvm_ioeventfd_any_length_allowed; bool kvm_msi_use_devid; void kvm_flush_coalesced_mmio_buffer(void) @@ -41,32 +38,6 @@ bool kvm_has_sync_mmu(void) return false; } -int kvm_has_many_ioeventfds(void) -{ - return 0; -} - -int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap) -{ - return -ENOSYS; -} - -int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, - target_ulong len, int type) -{ - return -EINVAL; -} - -int kvm_remove_breakpoint(CPUState *cpu, target_ulong addr, - target_ulong len, int type) -{ - return -EINVAL; -} - -void kvm_remove_all_breakpoints(CPUState *cpu) -{ -} - int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) { return 1; @@ -112,11 +83,6 @@ void kvm_irqchip_change_notify(void) { } -int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) -{ - return -ENOSYS; -} - int kvm_irqchip_add_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, EventNotifier *rn, int virq) { @@ -129,9 +95,14 @@ int kvm_irqchip_remove_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, return -ENOSYS; } -bool kvm_has_free_slot(MachineState *ms) +unsigned int kvm_get_max_memslots(void) { - return false; + return 0; +} + +unsigned int kvm_get_free_memslots(void) +{ + return 0; } void kvm_init_cpu_signals(CPUState *cpu) @@ -148,3 +119,13 @@ bool kvm_dirty_ring_enabled(void) { return false; } + +uint32_t kvm_dirty_ring_size(void) +{ + return 0; +} + +bool kvm_hwpoisoned_mem(void) +{ + return false; +} diff --git a/accel/stubs/meson.build b/accel/stubs/meson.build index 0249b9258f..91a2d21925 100644 --- a/accel/stubs/meson.build +++ b/accel/stubs/meson.build @@ -1,7 +1,6 @@ -sysemu_stubs_ss = ss.source_set() -sysemu_stubs_ss.add(when: 'CONFIG_HAX', if_false: files('hax-stub.c')) -sysemu_stubs_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c')) -sysemu_stubs_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) -sysemu_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c')) +system_stubs_ss = ss.source_set() +system_stubs_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c')) +system_stubs_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) +system_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c')) -specific_ss.add_all(when: ['CONFIG_SOFTMMU'], if_true: sysemu_stubs_ss) +specific_ss.add_all(when: ['CONFIG_SYSTEM_ONLY'], if_true: system_stubs_ss) diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index ea4a0dd2fb..8a496a2a6f 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -11,17 +11,25 @@ */ #include "qemu/osdep.h" +#include "exec/tb-flush.h" #include "exec/exec-all.h" void tb_flush(CPUState *cpu) { } -void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) +void tlb_set_dirty(CPUState *cpu, vaddr vaddr) { } -void *probe_access(CPUArchState *env, target_ulong addr, int size, +int probe_access_flags(CPUArchState *env, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + bool nonfault, void **phost, uintptr_t retaddr) +{ + g_assert_not_reached(); +} + +void *probe_access(CPUArchState *env, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { /* Handled by hardware accelerator. */ diff --git a/accel/tcg/atomic_common.c.inc b/accel/tcg/atomic_common.c.inc index 6602d7689f..95a5c5ff12 100644 --- a/accel/tcg/atomic_common.c.inc +++ b/accel/tcg/atomic_common.c.inc @@ -13,26 +13,12 @@ * See the COPYING file in the top-level directory. */ -static void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr, +static void atomic_trace_rmw_post(CPUArchState *env, uint64_t addr, MemOpIdx oi) { qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_RW); } -#if HAVE_ATOMIC128 -static void atomic_trace_ld_post(CPUArchState *env, target_ulong addr, - MemOpIdx oi) -{ - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); -} - -static void atomic_trace_st_post(CPUArchState *env, target_ulong addr, - MemOpIdx oi) -{ - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); -} -#endif - /* * Atomic helpers callable from TCG. * These have a common interface and all defer to cpu_atomic_* @@ -40,7 +26,7 @@ static void atomic_trace_st_post(CPUArchState *env, target_ulong addr, */ #define CMPXCHG_HELPER(OP, TYPE) \ - TYPE HELPER(atomic_##OP)(CPUArchState *env, target_ulong addr, \ + TYPE HELPER(atomic_##OP)(CPUArchState *env, uint64_t addr, \ TYPE oldv, TYPE newv, uint32_t oi) \ { return cpu_atomic_##OP##_mmu(env, addr, oldv, newv, oi, GETPC()); } @@ -55,10 +41,35 @@ CMPXCHG_HELPER(cmpxchgq_be, uint64_t) CMPXCHG_HELPER(cmpxchgq_le, uint64_t) #endif +#if HAVE_CMPXCHG128 +CMPXCHG_HELPER(cmpxchgo_be, Int128) +CMPXCHG_HELPER(cmpxchgo_le, Int128) +#endif + #undef CMPXCHG_HELPER +Int128 HELPER(nonatomic_cmpxchgo)(CPUArchState *env, uint64_t addr, + Int128 cmpv, Int128 newv, uint32_t oi) +{ +#if TCG_TARGET_REG_BITS == 32 + uintptr_t ra = GETPC(); + Int128 oldv; + + oldv = cpu_ld16_mmu(env, addr, oi, ra); + if (int128_eq(oldv, cmpv)) { + cpu_st16_mmu(env, addr, newv, oi, ra); + } else { + /* Even with comparison failure, still need a write cycle. */ + probe_write(env, addr, 16, get_mmuidx(oi), ra); + } + return oldv; +#else + g_assert_not_reached(); +#endif +} + #define ATOMIC_HELPER(OP, TYPE) \ - TYPE HELPER(glue(atomic_,OP))(CPUArchState *env, target_ulong addr, \ + TYPE HELPER(glue(atomic_,OP))(CPUArchState *env, uint64_t addr, \ TYPE val, uint32_t oi) \ { return glue(glue(cpu_atomic_,OP),_mmu)(env, addr, val, oi, GETPC()); } diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index 404a530f7c..1dc2151daf 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -69,12 +69,12 @@ # define END _le #endif -ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, +ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE cmpv, ABI_TYPE newv, MemOpIdx oi, uintptr_t retaddr) { - DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, - PAGE_READ | PAGE_WRITE, retaddr); + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); DATA_TYPE ret; #if DATA_SIZE == 16 @@ -87,38 +87,12 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, return ret; } -#if DATA_SIZE >= 16 -#if HAVE_ATOMIC128 -ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) -{ - DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, - PAGE_READ, retaddr); - DATA_TYPE val; - - val = atomic16_read(haddr); - ATOMIC_MMU_CLEANUP; - atomic_trace_ld_post(env, addr, oi); - return val; -} - -void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val, - MemOpIdx oi, uintptr_t retaddr) -{ - DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, - PAGE_WRITE, retaddr); - - atomic16_set(haddr, val); - ATOMIC_MMU_CLEANUP; - atomic_trace_st_post(env, addr, oi); -} -#endif -#else -ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val, +#if DATA_SIZE < 16 +ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) { - DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, - PAGE_READ | PAGE_WRITE, retaddr); + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); DATA_TYPE ret; ret = qatomic_xchg__nocheck(haddr, val); @@ -128,12 +102,11 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val, } #define GEN_ATOMIC_HELPER(X) \ -ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \ ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ { \ - DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ - PAGE_READ | PAGE_WRITE, retaddr); \ - DATA_TYPE ret; \ + DATA_TYPE *haddr, ret; \ + haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, DATA_SIZE, retaddr); \ ret = qatomic_##X(haddr, val); \ ATOMIC_MMU_CLEANUP; \ atomic_trace_rmw_post(env, addr, oi); \ @@ -160,12 +133,11 @@ GEN_ATOMIC_HELPER(xor_fetch) * of CF_PARALLEL's value, we'll trace just a read and a write. */ #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \ -ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \ ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \ { \ - XDATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ - PAGE_READ | PAGE_WRITE, retaddr); \ - XDATA_TYPE cmp, old, new, val = xval; \ + XDATA_TYPE *haddr, cmp, old, new, val = xval; \ + haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, DATA_SIZE, retaddr); \ smp_mb(); \ cmp = qatomic_read__nocheck(haddr); \ do { \ @@ -188,7 +160,7 @@ GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) #undef GEN_ATOMIC_HELPER_FN -#endif /* DATA SIZE >= 16 */ +#endif /* DATA SIZE < 16 */ #undef END @@ -202,12 +174,12 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) # define END _be #endif -ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, +ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE cmpv, ABI_TYPE newv, MemOpIdx oi, uintptr_t retaddr) { - DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, - PAGE_READ | PAGE_WRITE, retaddr); + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); DATA_TYPE ret; #if DATA_SIZE == 16 @@ -220,39 +192,12 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, return BSWAP(ret); } -#if DATA_SIZE >= 16 -#if HAVE_ATOMIC128 -ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) -{ - DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, - PAGE_READ, retaddr); - DATA_TYPE val; - - val = atomic16_read(haddr); - ATOMIC_MMU_CLEANUP; - atomic_trace_ld_post(env, addr, oi); - return BSWAP(val); -} - -void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val, - MemOpIdx oi, uintptr_t retaddr) -{ - DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, - PAGE_WRITE, retaddr); - - val = BSWAP(val); - atomic16_set(haddr, val); - ATOMIC_MMU_CLEANUP; - atomic_trace_st_post(env, addr, oi); -} -#endif -#else -ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val, +#if DATA_SIZE < 16 +ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) { - DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, - PAGE_READ | PAGE_WRITE, retaddr); + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); ABI_TYPE ret; ret = qatomic_xchg__nocheck(haddr, BSWAP(val)); @@ -262,12 +207,11 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val, } #define GEN_ATOMIC_HELPER(X) \ -ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \ ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ { \ - DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ - PAGE_READ | PAGE_WRITE, retaddr); \ - DATA_TYPE ret; \ + DATA_TYPE *haddr, ret; \ + haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, DATA_SIZE, retaddr); \ ret = qatomic_##X(haddr, BSWAP(val)); \ ATOMIC_MMU_CLEANUP; \ atomic_trace_rmw_post(env, addr, oi); \ @@ -291,12 +235,11 @@ GEN_ATOMIC_HELPER(xor_fetch) * of CF_PARALLEL's value, we'll trace just a read and a write. */ #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \ -ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \ ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \ { \ - XDATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ - PAGE_READ | PAGE_WRITE, retaddr); \ - XDATA_TYPE ldo, ldn, old, new, val = xval; \ + XDATA_TYPE *haddr, ldo, ldn, old, new, val = xval; \ + haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, DATA_SIZE, retaddr); \ smp_mb(); \ ldn = qatomic_read__nocheck(haddr); \ do { \ @@ -326,7 +269,7 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) #undef ADD #undef GEN_ATOMIC_HELPER_FN -#endif /* DATA_SIZE >= 16 */ +#endif /* DATA_SIZE < 16 */ #undef END #endif /* DATA_SIZE > 1 */ diff --git a/accel/tcg/cpu-exec-common.c b/accel/tcg/cpu-exec-common.c index be6fe45aa5..bc9b1a260e 100644 --- a/accel/tcg/cpu-exec-common.c +++ b/accel/tcg/cpu-exec-common.c @@ -20,7 +20,8 @@ #include "qemu/osdep.h" #include "sysemu/cpus.h" #include "sysemu/tcg.h" -#include "exec/exec-all.h" +#include "qemu/plugin.h" +#include "internal-common.h" bool tcg_allowed; @@ -31,53 +32,27 @@ void cpu_loop_exit_noexc(CPUState *cpu) cpu_loop_exit(cpu); } -#if defined(CONFIG_SOFTMMU) -void cpu_reloading_memory_map(void) -{ - if (qemu_in_vcpu_thread() && current_cpu->running) { - /* The guest can in theory prolong the RCU critical section as long - * as it feels like. The major problem with this is that because it - * can do multiple reconfigurations of the memory map within the - * critical section, we could potentially accumulate an unbounded - * collection of memory data structures awaiting reclamation. - * - * Because the only thing we're currently protecting with RCU is the - * memory data structures, it's sufficient to break the critical section - * in this callback, which we know will get called every time the - * memory map is rearranged. - * - * (If we add anything else in the system that uses RCU to protect - * its data structures, we will need to implement some other mechanism - * to force TCG CPUs to exit the critical section, at which point this - * part of this callback might become unnecessary.) - * - * This pair matches cpu_exec's rcu_read_lock()/rcu_read_unlock(), which - * only protects cpu->as->dispatch. Since we know our caller is about - * to reload it, it's safe to split the critical section. - */ - rcu_read_unlock(); - rcu_read_lock(); - } -} -#endif - void cpu_loop_exit(CPUState *cpu) { /* Undo the setting in cpu_tb_exec. */ - cpu->can_do_io = 1; + cpu->neg.can_do_io = true; + /* Undo any setting in generated code. */ + qemu_plugin_disable_mem_helpers(cpu); siglongjmp(cpu->jmp_env, 1); } void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) { if (pc) { - cpu_restore_state(cpu, pc, true); + cpu_restore_state(cpu, pc); } cpu_loop_exit(cpu); } void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc) { + /* Prevent looping if already executing in a serial context. */ + g_assert(!cpu_in_serial_context(cpu)); cpu->exception_index = EXCP_ATOMIC; cpu_loop_exit_restore(cpu, pc); } diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 635aeecc0a..5c70748060 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qemu/qemu-print.h" #include "qapi/error.h" -#include "qapi/qapi-commands-machine.h" #include "qapi/type-helpers.h" #include "hw/core/tcg-cpu-ops.h" #include "trace.h" @@ -28,23 +27,20 @@ #include "exec/exec-all.h" #include "tcg/tcg.h" #include "qemu/atomic.h" -#include "qemu/compiler.h" -#include "qemu/timer.h" #include "qemu/rcu.h" #include "exec/log.h" #include "qemu/main-loop.h" -#if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) -#include "hw/i386/apic.h" -#endif #include "sysemu/cpus.h" #include "exec/cpu-all.h" #include "sysemu/cpu-timers.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" #include "sysemu/tcg.h" -#include "exec/helper-proto.h" +#include "exec/helper-proto-common.h" +#include "tb-jmp-cache.h" #include "tb-hash.h" #include "tb-context.h" -#include "internal.h" +#include "internal-common.h" +#include "internal-target.h" /* -icount align implementation. */ @@ -64,8 +60,8 @@ typedef struct SyncClocks { #define MAX_DELAY_PRINT_RATE 2000000000LL #define MAX_NB_PRINTS 100 -static int64_t max_delay; -static int64_t max_advance; +int64_t max_delay; +int64_t max_advance; static void align_clocks(SyncClocks *sc, CPUState *cpu) { @@ -75,7 +71,7 @@ static void align_clocks(SyncClocks *sc, CPUState *cpu) return; } - cpu_icount = cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low; + cpu_icount = cpu->icount_extra + cpu->neg.icount_decr.u16.low; sc->diff_clk += icount_to_ns(sc->last_cpu_icount - cpu_icount); sc->last_cpu_icount = cpu_icount; @@ -126,7 +122,7 @@ static void init_delay_params(SyncClocks *sc, CPUState *cpu) sc->realtime_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT); sc->diff_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - sc->realtime_clock; sc->last_cpu_icount - = cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low; + = cpu->icount_extra + cpu->neg.icount_decr.u16.low; if (sc->diff_clk < max_delay) { max_delay = sc->diff_clk; } @@ -161,7 +157,7 @@ uint32_t curr_cflags(CPUState *cpu) */ if (unlikely(cpu->singlestep_enabled)) { cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | CF_SINGLE_STEP | 1; - } else if (singlestep) { + } else if (qatomic_read(&one_insn_per_tb)) { cflags |= CF_NO_GOTO_TB | 1; } else if (qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { cflags |= CF_NO_GOTO_TB; @@ -170,49 +166,126 @@ uint32_t curr_cflags(CPUState *cpu) return cflags; } +struct tb_desc { + vaddr pc; + uint64_t cs_base; + CPUArchState *env; + tb_page_addr_t page_addr0; + uint32_t flags; + uint32_t cflags; +}; + +static bool tb_lookup_cmp(const void *p, const void *d) +{ + const TranslationBlock *tb = p; + const struct tb_desc *desc = d; + + if ((tb_cflags(tb) & CF_PCREL || tb->pc == desc->pc) && + tb_page_addr0(tb) == desc->page_addr0 && + tb->cs_base == desc->cs_base && + tb->flags == desc->flags && + tb_cflags(tb) == desc->cflags) { + /* check next page if needed */ + tb_page_addr_t tb_phys_page1 = tb_page_addr1(tb); + if (tb_phys_page1 == -1) { + return true; + } else { + tb_page_addr_t phys_page1; + vaddr virt_page1; + + /* + * We know that the first page matched, and an otherwise valid TB + * encountered an incomplete instruction at the end of that page, + * therefore we know that generating a new TB from the current PC + * must also require reading from the next page -- even if the + * second pages do not match, and therefore the resulting insn + * is different for the new TB. Therefore any exception raised + * here by the faulting lookup is not premature. + */ + virt_page1 = TARGET_PAGE_ALIGN(desc->pc); + phys_page1 = get_page_addr_code(desc->env, virt_page1); + if (tb_phys_page1 == phys_page1) { + return true; + } + } + } + return false; +} + +static TranslationBlock *tb_htable_lookup(CPUState *cpu, vaddr pc, + uint64_t cs_base, uint32_t flags, + uint32_t cflags) +{ + tb_page_addr_t phys_pc; + struct tb_desc desc; + uint32_t h; + + desc.env = cpu_env(cpu); + desc.cs_base = cs_base; + desc.flags = flags; + desc.cflags = cflags; + desc.pc = pc; + phys_pc = get_page_addr_code(desc.env, pc); + if (phys_pc == -1) { + return NULL; + } + desc.page_addr0 = phys_pc; + h = tb_hash_func(phys_pc, (cflags & CF_PCREL ? 0 : pc), + flags, cs_base, cflags); + return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); +} + /* Might cause an exception, so have a longjmp destination ready */ -static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc, - target_ulong cs_base, - uint32_t flags, uint32_t cflags) +static inline TranslationBlock *tb_lookup(CPUState *cpu, vaddr pc, + uint64_t cs_base, uint32_t flags, + uint32_t cflags) { TranslationBlock *tb; + CPUJumpCache *jc; uint32_t hash; /* we should never be trying to look up an INVALID tb */ tcg_debug_assert(!(cflags & CF_INVALID)); hash = tb_jmp_cache_hash_func(pc); - tb = qatomic_rcu_read(&cpu->tb_jmp_cache[hash]); + jc = cpu->tb_jmp_cache; + tb = qatomic_read(&jc->array[hash].tb); if (likely(tb && - tb->pc == pc && + jc->array[hash].pc == pc && tb->cs_base == cs_base && tb->flags == flags && - tb->trace_vcpu_dstate == *cpu->trace_dstate && tb_cflags(tb) == cflags)) { - return tb; + goto hit; } + tb = tb_htable_lookup(cpu, pc, cs_base, flags, cflags); if (tb == NULL) { return NULL; } - qatomic_set(&cpu->tb_jmp_cache[hash], tb); + + jc->array[hash].pc = pc; + qatomic_set(&jc->array[hash].tb, tb); + +hit: + /* + * As long as tb is not NULL, the contents are consistent. Therefore, + * the virtual PC has to match for non-CF_PCREL translations. + */ + assert((tb_cflags(tb) & CF_PCREL) || tb->pc == pc); return tb; } -static inline void log_cpu_exec(target_ulong pc, CPUState *cpu, - const TranslationBlock *tb) +static void log_cpu_exec(vaddr pc, CPUState *cpu, + const TranslationBlock *tb) { - if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_CPU | CPU_LOG_EXEC)) - && qemu_log_in_addr_range(pc)) { - + if (qemu_log_in_addr_range(pc)) { qemu_log_mask(CPU_LOG_EXEC, - "Trace %d: %p [" TARGET_FMT_lx - "/" TARGET_FMT_lx "/%08x/%08x] %s\n", + "Trace %d: %p [%08" PRIx64 + "/%016" VADDR_PRIx "/%08x/%08x] %s\n", cpu->cpu_index, tb->tc.ptr, tb->cs_base, pc, tb->flags, tb->cflags, lookup_symbol(pc)); -#if defined(DEBUG_DISAS) if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) { FILE *logfile = qemu_log_trylock(); if (logfile) { @@ -224,24 +297,22 @@ static inline void log_cpu_exec(target_ulong pc, CPUState *cpu, #if defined(TARGET_I386) flags |= CPU_DUMP_CCOP; #endif + if (qemu_loglevel_mask(CPU_LOG_TB_VPU)) { + flags |= CPU_DUMP_VPU; + } cpu_dump_state(cpu, logfile, flags); qemu_log_unlock(logfile); } } -#endif /* DEBUG_DISAS */ } } -static bool check_for_breakpoints(CPUState *cpu, target_ulong pc, - uint32_t *cflags) +static bool check_for_breakpoints_slow(CPUState *cpu, vaddr pc, + uint32_t *cflags) { CPUBreakpoint *bp; bool match_page = false; - if (likely(QTAILQ_EMPTY(&cpu->breakpoints))) { - return false; - } - /* * Singlestep overrides breakpoints. * This requirement is visible in the record-replay tests, where @@ -269,9 +340,9 @@ static bool check_for_breakpoints(CPUState *cpu, target_ulong pc, #ifdef CONFIG_USER_ONLY g_assert_not_reached(); #else - CPUClass *cc = CPU_GET_CLASS(cpu); - assert(cc->tcg_ops->debug_check_breakpoint); - match_bp = cc->tcg_ops->debug_check_breakpoint(cpu); + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; + assert(tcg_ops->debug_check_breakpoint); + match_bp = tcg_ops->debug_check_breakpoint(cpu); #endif } @@ -302,6 +373,13 @@ static bool check_for_breakpoints(CPUState *cpu, target_ulong pc, return false; } +static inline bool check_for_breakpoints(CPUState *cpu, vaddr pc, + uint32_t *cflags) +{ + return unlikely(!QTAILQ_EMPTY(&cpu->breakpoints)) && + check_for_breakpoints_slow(cpu, pc, cflags); +} + /** * helper_lookup_tb_ptr: quick check for next tb * @env: current cpu state @@ -314,9 +392,18 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env) { CPUState *cpu = env_cpu(env); TranslationBlock *tb; - target_ulong cs_base, pc; + vaddr pc; + uint64_t cs_base; uint32_t flags, cflags; + /* + * By definition we've just finished a TB, so I/O is OK. + * Avoid the possibility of calling cpu_io_recompile() if + * a page table walk triggered by tb_lookup() calling + * probe_access_internal() happens to touch an MMIO device. + * The next TB, if we chain to it, will clear the flag again. + */ + cpu->neg.can_do_io = true; cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); cflags = curr_cflags(cpu); @@ -329,7 +416,9 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env) return tcg_code_gen_epilogue; } - log_cpu_exec(pc, cpu, tb); + if (qemu_loglevel_mask(CPU_LOG_TB_CPU | CPU_LOG_EXEC)) { + log_cpu_exec(pc, cpu, tb); + } return tb->tc.ptr; } @@ -347,16 +436,18 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env) static inline TranslationBlock * QEMU_DISABLE_CFI cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit) { - CPUArchState *env = cpu->env_ptr; uintptr_t ret; TranslationBlock *last_tb; const void *tb_ptr = itb->tc.ptr; - log_cpu_exec(itb->pc, cpu, itb); + if (qemu_loglevel_mask(CPU_LOG_TB_CPU | CPU_LOG_EXEC)) { + log_cpu_exec(log_pc(cpu, itb), cpu, itb); + } qemu_thread_jit_execute(); - ret = tcg_qemu_tb_exec(env, tb_ptr); - cpu->can_do_io = 1; + ret = tcg_qemu_tb_exec(cpu_env(cpu), tb_ptr); + cpu->neg.can_do_io = true; + qemu_plugin_disable_mem_helpers(cpu); /* * TODO: Delay swapping back to the read-write region of the TB * until we actually need to modify the TB. The read-only copy, @@ -375,18 +466,24 @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit) * counter hit zero); we must restore the guest PC to the address * of the start of the TB. */ - CPUClass *cc = CPU_GET_CLASS(cpu); - qemu_log_mask_and_addr(CPU_LOG_EXEC, last_tb->pc, - "Stopped execution of TB chain before %p [" - TARGET_FMT_lx "] %s\n", - last_tb->tc.ptr, last_tb->pc, - lookup_symbol(last_tb->pc)); - if (cc->tcg_ops->synchronize_from_tb) { - cc->tcg_ops->synchronize_from_tb(cpu, last_tb); + CPUClass *cc = cpu->cc; + const TCGCPUOps *tcg_ops = cc->tcg_ops; + + if (tcg_ops->synchronize_from_tb) { + tcg_ops->synchronize_from_tb(cpu, last_tb); } else { + tcg_debug_assert(!(tb_cflags(last_tb) & CF_PCREL)); assert(cc->set_pc); cc->set_pc(cpu, last_tb->pc); } + if (qemu_loglevel_mask(CPU_LOG_EXEC)) { + vaddr pc = log_pc(cpu, last_tb); + if (qemu_log_in_addr_range(pc)) { + qemu_log("Stopped execution of TB chain before %p [%016" + VADDR_PRIx "] %s\n", + last_tb->tc.ptr, pc, lookup_symbol(pc)); + } + } } /* @@ -405,27 +502,65 @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit) static void cpu_exec_enter(CPUState *cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; - if (cc->tcg_ops->cpu_exec_enter) { - cc->tcg_ops->cpu_exec_enter(cpu); + if (tcg_ops->cpu_exec_enter) { + tcg_ops->cpu_exec_enter(cpu); } } static void cpu_exec_exit(CPUState *cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; - if (cc->tcg_ops->cpu_exec_exit) { - cc->tcg_ops->cpu_exec_exit(cpu); + if (tcg_ops->cpu_exec_exit) { + tcg_ops->cpu_exec_exit(cpu); } } +static void cpu_exec_longjmp_cleanup(CPUState *cpu) +{ + /* Non-buggy compilers preserve this; assert the correct value. */ + g_assert(cpu == current_cpu); + +#ifdef CONFIG_USER_ONLY + clear_helper_retaddr(); + if (have_mmap_lock()) { + mmap_unlock(); + } +#else + /* + * For softmmu, a tlb_fill fault during translation will land here, + * and we need to release any page locks held. In system mode we + * have one tcg_ctx per thread, so we know it was this cpu doing + * the translation. + * + * Alternative 1: Install a cleanup to be called via an exception + * handling safe longjmp. It seems plausible that all our hosts + * support such a thing. We'd have to properly register unwind info + * for the JIT for EH, rather that just for GDB. + * + * Alternative 2: Set and restore cpu->jmp_env in tb_gen_code to + * capture the cpu_loop_exit longjmp, perform the cleanup, and + * jump again to arrive here. + */ + if (tcg_ctx->gen_tb) { + tb_unlock_pages(tcg_ctx->gen_tb); + tcg_ctx->gen_tb = NULL; + } +#endif + if (bql_locked()) { + bql_unlock(); + } + assert_no_pages_locked(); +} + void cpu_exec_step_atomic(CPUState *cpu) { - CPUArchState *env = cpu->env_ptr; + CPUArchState *env = cpu_env(cpu); TranslationBlock *tb; - target_ulong cs_base, pc; + vaddr pc; + uint64_t cs_base; uint32_t flags, cflags; int tb_exit; @@ -462,19 +597,7 @@ void cpu_exec_step_atomic(CPUState *cpu) cpu_tb_exec(cpu, tb, &tb_exit); cpu_exec_exit(cpu); } else { - /* - * The mmap_lock is dropped by tb_gen_code if it runs out of - * memory. - */ -#ifndef CONFIG_SOFTMMU - clear_helper_retaddr(); - tcg_debug_assert(!have_mmap_lock()); -#endif - if (qemu_mutex_iothread_locked()) { - qemu_mutex_unlock_iothread(); - } - assert_no_pages_locked(); - qemu_plugin_disable_mem_helpers(cpu); + cpu_exec_longjmp_cleanup(cpu); } /* @@ -487,78 +610,20 @@ void cpu_exec_step_atomic(CPUState *cpu) end_exclusive(); } -struct tb_desc { - target_ulong pc; - target_ulong cs_base; - CPUArchState *env; - tb_page_addr_t phys_page1; - uint32_t flags; - uint32_t cflags; - uint32_t trace_vcpu_dstate; -}; - -static bool tb_lookup_cmp(const void *p, const void *d) -{ - const TranslationBlock *tb = p; - const struct tb_desc *desc = d; - - if (tb->pc == desc->pc && - tb->page_addr[0] == desc->phys_page1 && - tb->cs_base == desc->cs_base && - tb->flags == desc->flags && - tb->trace_vcpu_dstate == desc->trace_vcpu_dstate && - tb_cflags(tb) == desc->cflags) { - /* check next page if needed */ - if (tb->page_addr[1] == -1) { - return true; - } else { - tb_page_addr_t phys_page2; - target_ulong virt_page2; - - virt_page2 = (desc->pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - phys_page2 = get_page_addr_code(desc->env, virt_page2); - if (tb->page_addr[1] == phys_page2) { - return true; - } - } - } - return false; -} - -TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, - target_ulong cs_base, uint32_t flags, - uint32_t cflags) -{ - tb_page_addr_t phys_pc; - struct tb_desc desc; - uint32_t h; - - desc.env = cpu->env_ptr; - desc.cs_base = cs_base; - desc.flags = flags; - desc.cflags = cflags; - desc.trace_vcpu_dstate = *cpu->trace_dstate; - desc.pc = pc; - phys_pc = get_page_addr_code(desc.env, pc); - if (phys_pc == -1) { - return NULL; - } - desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; - h = tb_hash_func(phys_pc, pc, flags, cflags, *cpu->trace_dstate); - return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); -} - void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) { - if (TCG_TARGET_HAS_direct_jump) { - uintptr_t offset = tb->jmp_target_arg[n]; - uintptr_t tc_ptr = (uintptr_t)tb->tc.ptr; - uintptr_t jmp_rx = tc_ptr + offset; - uintptr_t jmp_rw = jmp_rx - tcg_splitwx_diff; - tb_target_set_jmp_target(tc_ptr, jmp_rx, jmp_rw, addr); - } else { - tb->jmp_target_arg[n] = addr; - } + /* + * Get the rx view of the structure, from which we find the + * executable code address, and tb_target_set_jmp_target can + * produce a pc-relative displacement to jmp_target_addr[n]. + */ + const TranslationBlock *c_tb = tcg_splitwx_to_rx(tb); + uintptr_t offset = tb->jmp_insn_offset[n]; + uintptr_t jmp_rx = (uintptr_t)tb->tc.ptr + offset; + uintptr_t jmp_rw = jmp_rx - tcg_splitwx_diff; + + tb->jmp_target_addr[n] = addr; + tb_target_set_jmp_target(c_tb, n, jmp_rx, jmp_rw); } static inline void tb_add_jump(TranslationBlock *tb, int n, @@ -590,11 +655,8 @@ static inline void tb_add_jump(TranslationBlock *tb, int n, qemu_spin_unlock(&tb_next->jmp_lock); - qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, - "Linking TBs %p [" TARGET_FMT_lx - "] index %d -> %p [" TARGET_FMT_lx "]\n", - tb->tc.ptr, tb->pc, n, - tb_next->tc.ptr, tb_next->pc); + qemu_log_mask(CPU_LOG_EXEC, "Linking TBs %p index %d -> %p\n", + tb->tc.ptr, n, tb_next->tc.ptr); return; out_unlock_next: @@ -606,15 +668,11 @@ static inline bool cpu_handle_halt(CPUState *cpu) { #ifndef CONFIG_USER_ONLY if (cpu->halted) { -#if defined(TARGET_I386) - if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { - X86CPU *x86_cpu = X86_CPU(cpu); - qemu_mutex_lock_iothread(); - apic_poll_irq(x86_cpu->apic_state); - cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); - qemu_mutex_unlock_iothread(); + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; + + if (tcg_ops->cpu_exec_halt) { + tcg_ops->cpu_exec_halt(cpu); } -#endif /* TARGET_I386 */ if (!cpu_has_work(cpu)) { return true; } @@ -628,7 +686,7 @@ static inline bool cpu_handle_halt(CPUState *cpu) static inline void cpu_handle_debug_exception(CPUState *cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; CPUWatchpoint *wp; if (!cpu->watchpoint_hit) { @@ -637,8 +695,8 @@ static inline void cpu_handle_debug_exception(CPUState *cpu) } } - if (cc->tcg_ops->debug_excp_handler) { - cc->tcg_ops->debug_excp_handler(cpu); + if (tcg_ops->debug_excp_handler) { + tcg_ops->debug_excp_handler(cpu); } } @@ -647,7 +705,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) if (cpu->exception_index < 0) { #ifndef CONFIG_USER_ONLY if (replay_has_exception() - && cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0) { + && cpu->neg.icount_decr.u16.low + cpu->icount_extra == 0) { /* Execute just one insn to trigger exception pending in the log */ cpu->cflags_next_tb = (curr_cflags(cpu) & ~CF_USE_ICOUNT) | CF_NOIRQ | 1; @@ -655,6 +713,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) #endif return false; } + if (cpu->exception_index >= EXCP_INTERRUPT) { /* exit request from the cpu execution loop */ *ret = cpu->exception_index; @@ -663,62 +722,59 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) } cpu->exception_index = -1; return true; - } else { -#if defined(CONFIG_USER_ONLY) - /* if user mode only, we simulate a fake exception - which will be handled outside the cpu execution - loop */ -#if defined(TARGET_I386) - CPUClass *cc = CPU_GET_CLASS(cpu); - cc->tcg_ops->fake_user_interrupt(cpu); -#endif /* TARGET_I386 */ - *ret = cpu->exception_index; - cpu->exception_index = -1; - return true; -#else - if (replay_exception()) { - CPUClass *cc = CPU_GET_CLASS(cpu); - qemu_mutex_lock_iothread(); - cc->tcg_ops->do_interrupt(cpu); - qemu_mutex_unlock_iothread(); - cpu->exception_index = -1; + } - if (unlikely(cpu->singlestep_enabled)) { - /* - * After processing the exception, ensure an EXCP_DEBUG is - * raised when single-stepping so that GDB doesn't miss the - * next instruction. - */ - *ret = EXCP_DEBUG; - cpu_handle_debug_exception(cpu); - return true; - } - } else if (!replay_has_interrupt()) { - /* give a chance to iothread in replay mode */ - *ret = EXCP_INTERRUPT; +#if defined(CONFIG_USER_ONLY) + /* + * If user mode only, we simulate a fake exception which will be + * handled outside the cpu execution loop. + */ +#if defined(TARGET_I386) + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; + tcg_ops->fake_user_interrupt(cpu); +#endif /* TARGET_I386 */ + *ret = cpu->exception_index; + cpu->exception_index = -1; + return true; +#else + if (replay_exception()) { + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; + + bql_lock(); + tcg_ops->do_interrupt(cpu); + bql_unlock(); + cpu->exception_index = -1; + + if (unlikely(cpu->singlestep_enabled)) { + /* + * After processing the exception, ensure an EXCP_DEBUG is + * raised when single-stepping so that GDB doesn't miss the + * next instruction. + */ + *ret = EXCP_DEBUG; + cpu_handle_debug_exception(cpu); return true; } -#endif + } else if (!replay_has_interrupt()) { + /* give a chance to iothread in replay mode */ + *ret = EXCP_INTERRUPT; + return true; } +#endif return false; } -#ifndef CONFIG_USER_ONLY -/* - * CPU_INTERRUPT_POLL is a virtual event which gets converted into a - * "real" interrupt event later. It does not need to be recorded for - * replay purposes. - */ -static inline bool need_replay_interrupt(int interrupt_request) +static inline bool icount_exit_request(CPUState *cpu) { -#if defined(TARGET_I386) - return !(interrupt_request & CPU_INTERRUPT_POLL); -#else - return true; -#endif + if (!icount_enabled()) { + return false; + } + if (cpu->cflags_next_tb != -1 && !(cpu->cflags_next_tb & CF_USE_ICOUNT)) { + return false; + } + return cpu->neg.icount_decr.u16.low + cpu->icount_extra == 0; } -#endif /* !CONFIG_USER_ONLY */ static inline bool cpu_handle_interrupt(CPUState *cpu, TranslationBlock **last_tb) @@ -737,11 +793,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, * Ensure zeroing happens before reading cpu->exit_request or * cpu->interrupt_request (see also smp_wmb in cpu_exit()) */ - qatomic_mb_set(&cpu_neg(cpu)->icount_decr.u16.high, 0); + qatomic_set_mb(&cpu->neg.icount_decr.u16.high, 0); if (unlikely(qatomic_read(&cpu->interrupt_request))) { int interrupt_request; - qemu_mutex_lock_iothread(); + bql_lock(); interrupt_request = cpu->interrupt_request; if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) { /* Mask out external interrupts for this step. */ @@ -750,7 +806,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, if (interrupt_request & CPU_INTERRUPT_DEBUG) { cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; cpu->exception_index = EXCP_DEBUG; - qemu_mutex_unlock_iothread(); + bql_unlock(); return true; } #if !defined(CONFIG_USER_ONLY) @@ -761,7 +817,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; cpu->halted = 1; cpu->exception_index = EXCP_HLT; - qemu_mutex_unlock_iothread(); + bql_unlock(); return true; } #if defined(TARGET_I386) @@ -772,14 +828,14 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0); do_cpu_init(x86_cpu); cpu->exception_index = EXCP_HALTED; - qemu_mutex_unlock_iothread(); + bql_unlock(); return true; } #else else if (interrupt_request & CPU_INTERRUPT_RESET) { replay_interrupt(); cpu_reset(cpu); - qemu_mutex_unlock_iothread(); + bql_unlock(); return true; } #endif /* !TARGET_I386 */ @@ -788,11 +844,12 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, True when it is, and we should restart on a new TB, and via longjmp via cpu_loop_exit. */ else { - CPUClass *cc = CPU_GET_CLASS(cpu); + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; - if (cc->tcg_ops->cpu_exec_interrupt && - cc->tcg_ops->cpu_exec_interrupt(cpu, interrupt_request)) { - if (need_replay_interrupt(interrupt_request)) { + if (tcg_ops->cpu_exec_interrupt && + tcg_ops->cpu_exec_interrupt(cpu, interrupt_request)) { + if (!tcg_ops->need_replay_interrupt || + tcg_ops->need_replay_interrupt(interrupt_request)) { replay_interrupt(); } /* @@ -802,7 +859,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, */ if (unlikely(cpu->singlestep_enabled)) { cpu->exception_index = EXCP_DEBUG; - qemu_mutex_unlock_iothread(); + bql_unlock(); return true; } cpu->exception_index = -1; @@ -821,14 +878,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, } /* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */ - qemu_mutex_unlock_iothread(); + bql_unlock(); } /* Finally, check if we need to exit to the main loop. */ - if (unlikely(qatomic_read(&cpu->exit_request)) - || (icount_enabled() - && (cpu->cflags_next_tb == -1 || cpu->cflags_next_tb & CF_USE_ICOUNT) - && cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0)) { + if (unlikely(qatomic_read(&cpu->exit_request)) || icount_exit_request(cpu)) { qatomic_set(&cpu->exit_request, 0); if (cpu->exception_index == -1) { cpu->exception_index = EXCP_INTERRUPT; @@ -840,11 +894,12 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, } static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, - TranslationBlock **last_tb, int *tb_exit) + vaddr pc, TranslationBlock **last_tb, + int *tb_exit) { int32_t insns_left; - trace_exec_tb(tb, tb->pc); + trace_exec_tb(tb, pc); tb = cpu_tb_exec(cpu, tb, tb_exit); if (*tb_exit != TB_EXIT_REQUESTED) { *last_tb = tb; @@ -852,7 +907,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, } *last_tb = NULL; - insns_left = qatomic_read(&cpu_neg(cpu)->icount_decr.u32); + insns_left = qatomic_read(&cpu->neg.icount_decr.u32); if (insns_left < 0) { /* Something asked us to stop executing chained TBs; just * continue round the main loop. Whatever requested the exit @@ -871,7 +926,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, icount_update(cpu); /* Refill decrementer and continue execution. */ insns_left = MIN(0xffff, cpu->icount_budget); - cpu_neg(cpu)->icount_decr.u16.low = insns_left; + cpu->neg.icount_decr.u16.low = insns_left; cpu->icount_extra = cpu->icount_budget - insns_left; /* @@ -889,62 +944,10 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, /* main execution loop */ -int cpu_exec(CPUState *cpu) +static int __attribute__((noinline)) +cpu_exec_loop(CPUState *cpu, SyncClocks *sc) { int ret; - SyncClocks sc = { 0 }; - - /* replay_interrupt may need current_cpu */ - current_cpu = cpu; - - if (cpu_handle_halt(cpu)) { - return EXCP_HALTED; - } - - rcu_read_lock(); - - cpu_exec_enter(cpu); - - /* Calculate difference between guest clock and host clock. - * This delay includes the delay of the last cycle, so - * what we have to do is sleep until it is 0. As for the - * advance/delay we gain here, we try to fix it next time. - */ - init_delay_params(&sc, cpu); - - /* prepare setjmp context for exception handling */ - if (sigsetjmp(cpu->jmp_env, 0) != 0) { -#if defined(__clang__) - /* - * Some compilers wrongly smash all local variables after - * siglongjmp (the spec requires that only non-volatile locals - * which are changed between the sigsetjmp and siglongjmp are - * permitted to be trashed). There were bug reports for gcc - * 4.5.0 and clang. The bug is fixed in all versions of gcc - * that we support, but is still unfixed in clang: - * https://bugs.llvm.org/show_bug.cgi?id=21183 - * - * Reload an essential local variable here for those compilers. - * Newer versions of gcc would complain about this code (-Wclobbered), - * so we only perform the workaround for clang. - */ - cpu = current_cpu; -#else - /* Non-buggy compilers preserve this; assert the correct value. */ - g_assert(cpu == current_cpu); -#endif - -#ifndef CONFIG_SOFTMMU - clear_helper_retaddr(); - tcg_debug_assert(!have_mmap_lock()); -#endif - if (qemu_mutex_iothread_locked()) { - qemu_mutex_unlock_iothread(); - } - qemu_plugin_disable_mem_helpers(cpu); - - assert_no_pages_locked(); - } /* if an exception is pending, we execute it here */ while (!cpu_handle_exception(cpu, &ret)) { @@ -953,10 +956,11 @@ int cpu_exec(CPUState *cpu) while (!cpu_handle_interrupt(cpu, &last_tb)) { TranslationBlock *tb; - target_ulong cs_base, pc; + vaddr pc; + uint64_t cs_base; uint32_t flags, cflags; - cpu_get_tb_cpu_state(cpu->env_ptr, &pc, &cs_base, &flags); + cpu_get_tb_cpu_state(cpu_env(cpu), &pc, &cs_base, &flags); /* * When requested, use an exact setting for cflags for the next @@ -978,14 +982,21 @@ int cpu_exec(CPUState *cpu) tb = tb_lookup(cpu, pc, cs_base, flags, cflags); if (tb == NULL) { + CPUJumpCache *jc; + uint32_t h; + mmap_lock(); tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); mmap_unlock(); + /* * We add the TB in the virtual pc hash table * for the fast lookup */ - qatomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); + h = tb_jmp_cache_hash_func(pc); + jc = cpu->tb_jmp_cache; + jc->array[h].pc = pc; + qatomic_set(&jc->array[h].tb, tb); } #ifndef CONFIG_USER_ONLY @@ -995,7 +1006,7 @@ int cpu_exec(CPUState *cpu) * direct jump to a TB spanning two pages because the mapping * for the second page can change. */ - if (tb->page_addr[1] != -1) { + if (tb_page_addr1(tb) != -1) { last_tb = NULL; } #endif @@ -1004,35 +1015,72 @@ int cpu_exec(CPUState *cpu) tb_add_jump(last_tb, tb_exit, tb); } - cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit); + cpu_loop_exec_tb(cpu, tb, pc, &last_tb, &tb_exit); /* Try to align the host and virtual clocks if the guest is in advance */ - align_clocks(&sc, cpu); + align_clocks(sc, cpu); } } - - cpu_exec_exit(cpu); - rcu_read_unlock(); - return ret; } -void tcg_exec_realizefn(CPUState *cpu, Error **errp) +static int cpu_exec_setjmp(CPUState *cpu, SyncClocks *sc) +{ + /* Prepare setjmp context for exception handling. */ + if (unlikely(sigsetjmp(cpu->jmp_env, 0) != 0)) { + cpu_exec_longjmp_cleanup(cpu); + } + + return cpu_exec_loop(cpu, sc); +} + +int cpu_exec(CPUState *cpu) +{ + int ret; + SyncClocks sc = { 0 }; + + /* replay_interrupt may need current_cpu */ + current_cpu = cpu; + + if (cpu_handle_halt(cpu)) { + return EXCP_HALTED; + } + + RCU_READ_LOCK_GUARD(); + cpu_exec_enter(cpu); + + /* + * Calculate difference between guest clock and host clock. + * This delay includes the delay of the last cycle, so + * what we have to do is sleep until it is 0. As for the + * advance/delay we gain here, we try to fix it next time. + */ + init_delay_params(&sc, cpu); + + ret = cpu_exec_setjmp(cpu, &sc); + + cpu_exec_exit(cpu); + return ret; +} + +bool tcg_exec_realizefn(CPUState *cpu, Error **errp) { static bool tcg_target_initialized; - CPUClass *cc = CPU_GET_CLASS(cpu); if (!tcg_target_initialized) { - cc->tcg_ops->initialize(); + cpu->cc->tcg_ops->initialize(); tcg_target_initialized = true; } - tlb_init(cpu); - qemu_plugin_vcpu_init_hook(cpu); + cpu->tb_jmp_cache = g_new0(CPUJumpCache, 1); + tlb_init(cpu); #ifndef CONFIG_USER_ONLY tcg_iommu_init_notifier_list(cpu); #endif /* !CONFIG_USER_ONLY */ + /* qemu_plugin_vcpu_init_hook delayed until cpu_index assigned. */ + + return true; } /* undo the initializations in reverse order */ @@ -1042,89 +1090,6 @@ void tcg_exec_unrealizefn(CPUState *cpu) tcg_iommu_free_notifier_list(cpu); #endif /* !CONFIG_USER_ONLY */ - qemu_plugin_vcpu_exit_hook(cpu); tlb_destroy(cpu); + g_free_rcu(cpu->tb_jmp_cache, rcu); } - -#ifndef CONFIG_USER_ONLY - -void dump_drift_info(GString *buf) -{ - if (!icount_enabled()) { - return; - } - - g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n", - (cpu_get_clock() - icount_get()) / SCALE_MS); - if (icount_align_option) { - g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n", - -max_delay / SCALE_MS); - g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n", - max_advance / SCALE_MS); - } else { - g_string_append_printf(buf, "Max guest delay NA\n"); - g_string_append_printf(buf, "Max guest advance NA\n"); - } -} - -HumanReadableText *qmp_x_query_jit(Error **errp) -{ - g_autoptr(GString) buf = g_string_new(""); - - if (!tcg_enabled()) { - error_setg(errp, "JIT information is only available with accel=tcg"); - return NULL; - } - - dump_exec_info(buf); - dump_drift_info(buf); - - return human_readable_text_from_str(buf); -} - -HumanReadableText *qmp_x_query_opcount(Error **errp) -{ - g_autoptr(GString) buf = g_string_new(""); - - if (!tcg_enabled()) { - error_setg(errp, "Opcode count information is only available with accel=tcg"); - return NULL; - } - - dump_opcount_info(buf); - - return human_readable_text_from_str(buf); -} - -#ifdef CONFIG_PROFILER - -int64_t dev_time; - -HumanReadableText *qmp_x_query_profile(Error **errp) -{ - g_autoptr(GString) buf = g_string_new(""); - static int64_t last_cpu_exec_time; - int64_t cpu_exec_time; - int64_t delta; - - cpu_exec_time = tcg_cpu_exec_time(); - delta = cpu_exec_time - last_cpu_exec_time; - - g_string_append_printf(buf, "async time %" PRId64 " (%0.3f)\n", - dev_time, dev_time / (double)NANOSECONDS_PER_SECOND); - g_string_append_printf(buf, "qemu time %" PRId64 " (%0.3f)\n", - delta, delta / (double)NANOSECONDS_PER_SECOND); - last_cpu_exec_time = cpu_exec_time; - dev_time = 0; - - return human_readable_text_from_str(buf); -} -#else -HumanReadableText *qmp_x_query_profile(Error **errp) -{ - error_setg(errp, "Internal profiler not compiled"); - return NULL; -} -#endif - -#endif /* !CONFIG_USER_ONLY */ diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index f90f4312ea..93b1ca810b 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -24,22 +24,25 @@ #include "exec/memory.h" #include "exec/cpu_ldst.h" #include "exec/cputlb.h" +#include "exec/tb-flush.h" #include "exec/memory-internal.h" #include "exec/ram_addr.h" #include "tcg/tcg.h" #include "qemu/error-report.h" #include "exec/log.h" -#include "exec/helper-proto.h" +#include "exec/helper-proto-common.h" #include "qemu/atomic.h" #include "qemu/atomic128.h" #include "exec/translate-all.h" -#include "trace/trace-root.h" +#include "trace.h" #include "tb-hash.h" -#include "internal.h" +#include "internal-common.h" +#include "internal-target.h" #ifdef CONFIG_PLUGIN #include "qemu/plugin-memory.h" #endif #include "tcg/tcg-ldst.h" +#include "tcg/oversized-guest.h" /* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */ /* #define DEBUG_TLB */ @@ -73,8 +76,9 @@ } while (0) /* run_on_cpu_data.target_ptr should always be big enough for a - * target_ulong even on 32 bit builds */ -QEMU_BUILD_BUG_ON(sizeof(target_ulong) > sizeof(run_on_cpu_data)); + * vaddr even on 32 bit builds + */ +QEMU_BUILD_BUG_ON(sizeof(vaddr) > sizeof(run_on_cpu_data)); /* We currently can't handle more than 16 bits in the MMUIDX bitmask. */ @@ -98,21 +102,19 @@ static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns, desc->window_max_entries = max_entries; } -static void tb_jmp_cache_clear_page(CPUState *cpu, target_ulong page_addr) +static void tb_jmp_cache_clear_page(CPUState *cpu, vaddr page_addr) { - unsigned int i, i0 = tb_jmp_cache_hash_page(page_addr); + CPUJumpCache *jc = cpu->tb_jmp_cache; + int i, i0; - for (i = 0; i < TB_JMP_PAGE_SIZE; i++) { - qatomic_set(&cpu->tb_jmp_cache[i0 + i], NULL); + if (unlikely(!jc)) { + return; } -} -static void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr) -{ - /* Discard jump cache entries for any tb which might potentially - overlap the flushed page. */ - tb_jmp_cache_clear_page(cpu, addr - TARGET_PAGE_SIZE); - tb_jmp_cache_clear_page(cpu, addr); + i0 = tb_jmp_cache_hash_page(page_addr); + for (i = 0; i < TB_JMP_PAGE_SIZE; i++) { + qatomic_set(&jc->array[i0 + i].tb, NULL); + } } /** @@ -200,13 +202,13 @@ static void tlb_mmu_resize_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast, } g_free(fast->table); - g_free(desc->iotlb); + g_free(desc->fulltlb); tlb_window_reset(desc, now, 0); /* desc->n_used_entries is cleared by the caller */ 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); + desc->fulltlb = g_try_new(CPUTLBEntryFull, new_size); /* * If the allocations fail, try smaller sizes. We just freed some @@ -215,7 +217,7 @@ static void tlb_mmu_resize_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast, * allocations to fail though, so we progressively reduce the allocation * size, aborting if we cannot even allocate the smallest TLB we support. */ - while (fast->table == NULL || desc->iotlb == NULL) { + while (fast->table == NULL || desc->fulltlb == NULL) { if (new_size == (1 << CPU_TLB_DYN_MIN_BITS)) { error_report("%s: %s", __func__, strerror(errno)); abort(); @@ -224,9 +226,9 @@ static void tlb_mmu_resize_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast, fast->mask = (new_size - 1) << CPU_TLB_ENTRY_BITS; g_free(fast->table); - g_free(desc->iotlb); + g_free(desc->fulltlb); fast->table = g_try_new(CPUTLBEntry, new_size); - desc->iotlb = g_try_new(CPUIOTLBEntry, new_size); + desc->fulltlb = g_try_new(CPUTLBEntryFull, new_size); } } @@ -240,11 +242,11 @@ static void tlb_mmu_flush_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast) memset(desc->vtable, -1, sizeof(desc->vtable)); } -static void tlb_flush_one_mmuidx_locked(CPUArchState *env, int mmu_idx, +static void tlb_flush_one_mmuidx_locked(CPUState *cpu, int mmu_idx, int64_t now) { - CPUTLBDesc *desc = &env_tlb(env)->d[mmu_idx]; - CPUTLBDescFast *fast = &env_tlb(env)->f[mmu_idx]; + CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx]; + CPUTLBDescFast *fast = &cpu->neg.tlb.f[mmu_idx]; tlb_mmu_resize_locked(desc, fast, now); tlb_mmu_flush_locked(desc, fast); @@ -258,48 +260,46 @@ static void tlb_mmu_init(CPUTLBDesc *desc, CPUTLBDescFast *fast, int64_t now) 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); + desc->fulltlb = g_new(CPUTLBEntryFull, n_entries); tlb_mmu_flush_locked(desc, fast); } -static inline void tlb_n_used_entries_inc(CPUArchState *env, uintptr_t mmu_idx) +static inline void tlb_n_used_entries_inc(CPUState *cpu, uintptr_t mmu_idx) { - env_tlb(env)->d[mmu_idx].n_used_entries++; + cpu->neg.tlb.d[mmu_idx].n_used_entries++; } -static inline void tlb_n_used_entries_dec(CPUArchState *env, uintptr_t mmu_idx) +static inline void tlb_n_used_entries_dec(CPUState *cpu, uintptr_t mmu_idx) { - env_tlb(env)->d[mmu_idx].n_used_entries--; + cpu->neg.tlb.d[mmu_idx].n_used_entries--; } 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); + qemu_spin_init(&cpu->neg.tlb.c.lock); /* All tlbs are initialized flushed. */ - env_tlb(env)->c.dirty = 0; + cpu->neg.tlb.c.dirty = 0; for (i = 0; i < NB_MMU_MODES; i++) { - tlb_mmu_init(&env_tlb(env)->d[i], &env_tlb(env)->f[i], now); + tlb_mmu_init(&cpu->neg.tlb.d[i], &cpu->neg.tlb.f[i], now); } } void tlb_destroy(CPUState *cpu) { - CPUArchState *env = cpu->env_ptr; int i; - qemu_spin_destroy(&env_tlb(env)->c.lock); + qemu_spin_destroy(&cpu->neg.tlb.c.lock); for (i = 0; i < NB_MMU_MODES; i++) { - CPUTLBDesc *desc = &env_tlb(env)->d[i]; - CPUTLBDescFast *fast = &env_tlb(env)->f[i]; + CPUTLBDesc *desc = &cpu->neg.tlb.d[i]; + CPUTLBDescFast *fast = &cpu->neg.tlb.f[i]; g_free(fast->table); - g_free(desc->iotlb); + g_free(desc->fulltlb); } } @@ -322,26 +322,8 @@ static void flush_all_helper(CPUState *src, run_on_cpu_func fn, } } -void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) -{ - CPUState *cpu; - size_t full = 0, part = 0, elide = 0; - - CPU_FOREACH(cpu) { - CPUArchState *env = cpu->env_ptr; - - full += qatomic_read(&env_tlb(env)->c.full_flush_count); - part += qatomic_read(&env_tlb(env)->c.part_flush_count); - elide += qatomic_read(&env_tlb(env)->c.elide_flush_count); - } - *pfull = full; - *ppart = part; - *pelide = elide; -} - 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(); @@ -350,32 +332,32 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) tlb_debug("mmu_idx:0x%04" PRIx16 "\n", asked); - qemu_spin_lock(&env_tlb(env)->c.lock); + qemu_spin_lock(&cpu->neg.tlb.c.lock); - all_dirty = env_tlb(env)->c.dirty; + all_dirty = cpu->neg.tlb.c.dirty; to_clean = asked & all_dirty; all_dirty &= ~to_clean; - env_tlb(env)->c.dirty = all_dirty; + cpu->neg.tlb.c.dirty = all_dirty; for (work = to_clean; work != 0; work &= work - 1) { int mmu_idx = ctz32(work); - tlb_flush_one_mmuidx_locked(env, mmu_idx, now); + tlb_flush_one_mmuidx_locked(cpu, mmu_idx, now); } - qemu_spin_unlock(&env_tlb(env)->c.lock); + qemu_spin_unlock(&cpu->neg.tlb.c.lock); - cpu_tb_jmp_cache_clear(cpu); + tcg_flush_jmp_cache(cpu); if (to_clean == ALL_MMUIDX_BITS) { - qatomic_set(&env_tlb(env)->c.full_flush_count, - env_tlb(env)->c.full_flush_count + 1); + qatomic_set(&cpu->neg.tlb.c.full_flush_count, + cpu->neg.tlb.c.full_flush_count + 1); } else { - qatomic_set(&env_tlb(env)->c.part_flush_count, - env_tlb(env)->c.part_flush_count + ctpop16(to_clean)); + qatomic_set(&cpu->neg.tlb.c.part_flush_count, + cpu->neg.tlb.c.part_flush_count + ctpop16(to_clean)); if (to_clean != asked) { - qatomic_set(&env_tlb(env)->c.elide_flush_count, - env_tlb(env)->c.elide_flush_count + - ctpop16(asked & ~to_clean)); + qatomic_set(&cpu->neg.tlb.c.elide_flush_count, + cpu->neg.tlb.c.elide_flush_count + + ctpop16(asked & ~to_clean)); } } } @@ -428,7 +410,7 @@ void tlb_flush_all_cpus_synced(CPUState *src_cpu) } static bool tlb_hit_page_mask_anyprot(CPUTLBEntry *tlb_entry, - target_ulong page, target_ulong mask) + vaddr page, vaddr mask) { page &= mask; mask &= TARGET_PAGE_MASK | TLB_INVALID_MASK; @@ -438,8 +420,7 @@ static bool tlb_hit_page_mask_anyprot(CPUTLBEntry *tlb_entry, page == (tlb_entry->addr_code & mask)); } -static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry, - target_ulong page) +static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry, vaddr page) { return tlb_hit_page_mask_anyprot(tlb_entry, page, -1); } @@ -455,8 +436,8 @@ static inline bool tlb_entry_is_empty(const CPUTLBEntry *te) /* Called with tlb_c.lock held */ static bool tlb_flush_entry_mask_locked(CPUTLBEntry *tlb_entry, - target_ulong page, - target_ulong mask) + vaddr page, + vaddr mask) { if (tlb_hit_page_mask_anyprot(tlb_entry, page, mask)) { memset(tlb_entry, -1, sizeof(*tlb_entry)); @@ -465,51 +446,49 @@ static bool tlb_flush_entry_mask_locked(CPUTLBEntry *tlb_entry, return false; } -static inline bool tlb_flush_entry_locked(CPUTLBEntry *tlb_entry, - target_ulong page) +static inline bool tlb_flush_entry_locked(CPUTLBEntry *tlb_entry, vaddr page) { return tlb_flush_entry_mask_locked(tlb_entry, page, -1); } /* Called with tlb_c.lock held */ -static void tlb_flush_vtlb_page_mask_locked(CPUArchState *env, int mmu_idx, - target_ulong page, - target_ulong mask) +static void tlb_flush_vtlb_page_mask_locked(CPUState *cpu, int mmu_idx, + vaddr page, + vaddr mask) { - CPUTLBDesc *d = &env_tlb(env)->d[mmu_idx]; + CPUTLBDesc *d = &cpu->neg.tlb.d[mmu_idx]; int k; - assert_cpu_is_self(env_cpu(env)); + assert_cpu_is_self(cpu); for (k = 0; k < CPU_VTLB_SIZE; k++) { if (tlb_flush_entry_mask_locked(&d->vtable[k], page, mask)) { - tlb_n_used_entries_dec(env, mmu_idx); + tlb_n_used_entries_dec(cpu, mmu_idx); } } } -static inline void tlb_flush_vtlb_page_locked(CPUArchState *env, int mmu_idx, - target_ulong page) +static inline void tlb_flush_vtlb_page_locked(CPUState *cpu, int mmu_idx, + vaddr page) { - tlb_flush_vtlb_page_mask_locked(env, mmu_idx, page, -1); + tlb_flush_vtlb_page_mask_locked(cpu, mmu_idx, page, -1); } -static void tlb_flush_page_locked(CPUArchState *env, int midx, - target_ulong page) +static void tlb_flush_page_locked(CPUState *cpu, int midx, vaddr page) { - target_ulong lp_addr = env_tlb(env)->d[midx].large_page_addr; - target_ulong lp_mask = env_tlb(env)->d[midx].large_page_mask; + vaddr lp_addr = cpu->neg.tlb.d[midx].large_page_addr; + vaddr lp_mask = cpu->neg.tlb.d[midx].large_page_mask; /* Check if we need to flush due to large pages. */ if ((page & lp_mask) == lp_addr) { - tlb_debug("forcing full flush midx %d (" - TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", + tlb_debug("forcing full flush midx %d (%016" + VADDR_PRIx "/%016" VADDR_PRIx ")\n", midx, lp_addr, lp_mask); - tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime()); + tlb_flush_one_mmuidx_locked(cpu, midx, get_clock_realtime()); } else { - if (tlb_flush_entry_locked(tlb_entry(env, midx, page), page)) { - tlb_n_used_entries_dec(env, midx); + if (tlb_flush_entry_locked(tlb_entry(cpu, midx, page), page)) { + tlb_n_used_entries_dec(cpu, midx); } - tlb_flush_vtlb_page_locked(env, midx, page); + tlb_flush_vtlb_page_locked(cpu, midx, page); } } @@ -523,25 +502,29 @@ static void tlb_flush_page_locked(CPUArchState *env, int midx, * at @addr from the tlbs indicated by @idxmap from @cpu. */ static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu, - target_ulong addr, + vaddr addr, uint16_t idxmap) { - CPUArchState *env = cpu->env_ptr; int mmu_idx; assert_cpu_is_self(cpu); - tlb_debug("page addr:" TARGET_FMT_lx " mmu_map:0x%x\n", addr, idxmap); + tlb_debug("page addr: %016" VADDR_PRIx " mmu_map:0x%x\n", addr, idxmap); - qemu_spin_lock(&env_tlb(env)->c.lock); + qemu_spin_lock(&cpu->neg.tlb.c.lock); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { if ((idxmap >> mmu_idx) & 1) { - tlb_flush_page_locked(env, mmu_idx, addr); + tlb_flush_page_locked(cpu, mmu_idx, addr); } } - qemu_spin_unlock(&env_tlb(env)->c.lock); + qemu_spin_unlock(&cpu->neg.tlb.c.lock); - tb_flush_jmp_cache(cpu, addr); + /* + * Discard jump cache entries for any tb which might potentially + * overlap the flushed page, which includes the previous. + */ + tb_jmp_cache_clear_page(cpu, addr - TARGET_PAGE_SIZE); + tb_jmp_cache_clear_page(cpu, addr); } /** @@ -557,15 +540,15 @@ static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu, 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; + vaddr addr_and_idxmap = data.target_ptr; + vaddr 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; + vaddr addr; uint16_t idxmap; } TLBFlushPageByMMUIdxData; @@ -588,9 +571,9 @@ static void tlb_flush_page_by_mmuidx_async_2(CPUState *cpu, g_free(d); } -void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, uint16_t idxmap) +void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap) { - tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%" PRIx16 "\n", addr, idxmap); + tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%" PRIx16 "\n", addr, idxmap); /* This should already be page aligned */ addr &= TARGET_PAGE_MASK; @@ -616,15 +599,15 @@ void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, uint16_t idxmap) } } -void tlb_flush_page(CPUState *cpu, target_ulong addr) +void tlb_flush_page(CPUState *cpu, vaddr addr) { tlb_flush_page_by_mmuidx(cpu, addr, ALL_MMUIDX_BITS); } -void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, target_ulong addr, +void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, vaddr addr, uint16_t idxmap) { - tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap); + tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap); /* This should already be page aligned */ addr &= TARGET_PAGE_MASK; @@ -656,16 +639,16 @@ void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, target_ulong addr, tlb_flush_page_by_mmuidx_async_0(src_cpu, addr, idxmap); } -void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr) +void tlb_flush_page_all_cpus(CPUState *src, vaddr addr) { tlb_flush_page_by_mmuidx_all_cpus(src, addr, ALL_MMUIDX_BITS); } void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu, - target_ulong addr, + vaddr addr, uint16_t idxmap) { - tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap); + tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap); /* This should already be page aligned */ addr &= TARGET_PAGE_MASK; @@ -702,18 +685,18 @@ void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu, } } -void tlb_flush_page_all_cpus_synced(CPUState *src, target_ulong addr) +void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr) { tlb_flush_page_by_mmuidx_all_cpus_synced(src, addr, ALL_MMUIDX_BITS); } -static void tlb_flush_range_locked(CPUArchState *env, int midx, - target_ulong addr, target_ulong len, +static void tlb_flush_range_locked(CPUState *cpu, int midx, + vaddr addr, vaddr len, unsigned bits) { - CPUTLBDesc *d = &env_tlb(env)->d[midx]; - CPUTLBDescFast *f = &env_tlb(env)->f[midx]; - target_ulong mask = MAKE_64BIT_MASK(0, bits); + CPUTLBDesc *d = &cpu->neg.tlb.d[midx]; + CPUTLBDescFast *f = &cpu->neg.tlb.f[midx]; + vaddr mask = MAKE_64BIT_MASK(0, bits); /* * If @bits is smaller than the tlb size, there may be multiple entries @@ -727,9 +710,9 @@ static void tlb_flush_range_locked(CPUArchState *env, int midx, */ if (mask < f->mask || len > f->mask) { tlb_debug("forcing full flush midx %d (" - TARGET_FMT_lx "/" TARGET_FMT_lx "+" TARGET_FMT_lx ")\n", + "%016" VADDR_PRIx "/%016" VADDR_PRIx "+%016" VADDR_PRIx ")\n", midx, addr, mask, len); - tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime()); + tlb_flush_one_mmuidx_locked(cpu, midx, get_clock_realtime()); return; } @@ -740,26 +723,26 @@ static void tlb_flush_range_locked(CPUArchState *env, int midx, */ if (((addr + len - 1) & d->large_page_mask) == d->large_page_addr) { tlb_debug("forcing full flush midx %d (" - TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", + "%016" VADDR_PRIx "/%016" VADDR_PRIx ")\n", midx, d->large_page_addr, d->large_page_mask); - tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime()); + tlb_flush_one_mmuidx_locked(cpu, midx, get_clock_realtime()); return; } - for (target_ulong i = 0; i < len; i += TARGET_PAGE_SIZE) { - target_ulong page = addr + i; - CPUTLBEntry *entry = tlb_entry(env, midx, page); + for (vaddr i = 0; i < len; i += TARGET_PAGE_SIZE) { + vaddr page = addr + i; + CPUTLBEntry *entry = tlb_entry(cpu, midx, page); if (tlb_flush_entry_mask_locked(entry, page, mask)) { - tlb_n_used_entries_dec(env, midx); + tlb_n_used_entries_dec(cpu, midx); } - tlb_flush_vtlb_page_mask_locked(env, midx, page, mask); + tlb_flush_vtlb_page_mask_locked(cpu, midx, page, mask); } } typedef struct { - target_ulong addr; - target_ulong len; + vaddr addr; + vaddr len; uint16_t idxmap; uint16_t bits; } TLBFlushRangeData; @@ -767,33 +750,38 @@ typedef struct { static void tlb_flush_range_by_mmuidx_async_0(CPUState *cpu, TLBFlushRangeData d) { - CPUArchState *env = cpu->env_ptr; int mmu_idx; assert_cpu_is_self(cpu); - tlb_debug("range:" TARGET_FMT_lx "/%u+" TARGET_FMT_lx " mmu_map:0x%x\n", + tlb_debug("range: %016" VADDR_PRIx "/%u+%016" VADDR_PRIx " mmu_map:0x%x\n", d.addr, d.bits, d.len, d.idxmap); - qemu_spin_lock(&env_tlb(env)->c.lock); + qemu_spin_lock(&cpu->neg.tlb.c.lock); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { if ((d.idxmap >> mmu_idx) & 1) { - tlb_flush_range_locked(env, mmu_idx, d.addr, d.len, d.bits); + tlb_flush_range_locked(cpu, mmu_idx, d.addr, d.len, d.bits); } } - qemu_spin_unlock(&env_tlb(env)->c.lock); + qemu_spin_unlock(&cpu->neg.tlb.c.lock); /* * If the length is larger than the jump cache size, then it will take * longer to clear each entry individually than it will to clear it all. */ if (d.len >= (TARGET_PAGE_SIZE * TB_JMP_CACHE_SIZE)) { - cpu_tb_jmp_cache_clear(cpu); + tcg_flush_jmp_cache(cpu); return; } - for (target_ulong i = 0; i < d.len; i += TARGET_PAGE_SIZE) { - tb_flush_jmp_cache(cpu, d.addr + i); + /* + * Discard jump cache entries for any tb which might potentially + * overlap the flushed pages, which includes the previous. + */ + d.addr -= TARGET_PAGE_SIZE; + for (vaddr i = 0, n = d.len / TARGET_PAGE_SIZE + 1; i < n; i++) { + tb_jmp_cache_clear_page(cpu, d.addr); + d.addr += TARGET_PAGE_SIZE; } } @@ -805,8 +793,8 @@ static void tlb_flush_range_by_mmuidx_async_1(CPUState *cpu, g_free(d); } -void tlb_flush_range_by_mmuidx(CPUState *cpu, target_ulong addr, - target_ulong len, uint16_t idxmap, +void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, + vaddr len, uint16_t idxmap, unsigned bits) { TLBFlushRangeData d; @@ -841,14 +829,14 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, target_ulong addr, } } -void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, target_ulong addr, +void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap, unsigned bits) { tlb_flush_range_by_mmuidx(cpu, addr, TARGET_PAGE_SIZE, idxmap, bits); } void tlb_flush_range_by_mmuidx_all_cpus(CPUState *src_cpu, - target_ulong addr, target_ulong len, + vaddr addr, vaddr len, uint16_t idxmap, unsigned bits) { TLBFlushRangeData d; @@ -888,16 +876,16 @@ void tlb_flush_range_by_mmuidx_all_cpus(CPUState *src_cpu, } void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *src_cpu, - target_ulong addr, - uint16_t idxmap, unsigned bits) + vaddr addr, uint16_t idxmap, + unsigned bits) { tlb_flush_range_by_mmuidx_all_cpus(src_cpu, addr, TARGET_PAGE_SIZE, idxmap, bits); } void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu, - target_ulong addr, - target_ulong len, + vaddr addr, + vaddr len, uint16_t idxmap, unsigned bits) { @@ -939,7 +927,7 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu, } void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu, - target_ulong addr, + vaddr addr, uint16_t idxmap, unsigned bits) { @@ -951,7 +939,8 @@ void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu, can be detected */ void tlb_protect_code(ram_addr_t ram_addr) { - cpu_physical_memory_test_and_clear_dirty(ram_addr, TARGET_PAGE_SIZE, + cpu_physical_memory_test_and_clear_dirty(ram_addr & TARGET_PAGE_MASK, + TARGET_PAGE_SIZE, DIRTY_MEMORY_CODE); } @@ -989,11 +978,15 @@ static void tlb_reset_dirty_range_locked(CPUTLBEntry *tlb_entry, addr &= TARGET_PAGE_MASK; addr += tlb_entry->addend; if ((addr - start) < length) { -#if TCG_OVERSIZED_GUEST +#if TARGET_LONG_BITS == 32 + uint32_t *ptr_write = (uint32_t *)&tlb_entry->addr_write; + ptr_write += HOST_BIG_ENDIAN; + qatomic_set(ptr_write, *ptr_write | TLB_NOTDIRTY); +#elif TCG_OVERSIZED_GUEST tlb_entry->addr_write |= TLB_NOTDIRTY; #else qatomic_set(&tlb_entry->addr_write, - tlb_entry->addr_write | TLB_NOTDIRTY); + tlb_entry->addr_write | TLB_NOTDIRTY); #endif } } @@ -1015,138 +1008,147 @@ static inline void copy_tlb_helper_locked(CPUTLBEntry *d, const CPUTLBEntry *s) */ void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length) { - CPUArchState *env; - int mmu_idx; - env = cpu->env_ptr; - qemu_spin_lock(&env_tlb(env)->c.lock); + qemu_spin_lock(&cpu->neg.tlb.c.lock); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { unsigned int i; - unsigned int n = tlb_n_entries(&env_tlb(env)->f[mmu_idx]); + unsigned int n = tlb_n_entries(&cpu->neg.tlb.f[mmu_idx]); for (i = 0; i < n; i++) { - tlb_reset_dirty_range_locked(&env_tlb(env)->f[mmu_idx].table[i], + tlb_reset_dirty_range_locked(&cpu->neg.tlb.f[mmu_idx].table[i], start1, length); } for (i = 0; i < CPU_VTLB_SIZE; i++) { - tlb_reset_dirty_range_locked(&env_tlb(env)->d[mmu_idx].vtable[i], + tlb_reset_dirty_range_locked(&cpu->neg.tlb.d[mmu_idx].vtable[i], start1, length); } } - qemu_spin_unlock(&env_tlb(env)->c.lock); + qemu_spin_unlock(&cpu->neg.tlb.c.lock); } /* Called with tlb_c.lock held */ static inline void tlb_set_dirty1_locked(CPUTLBEntry *tlb_entry, - target_ulong vaddr) + vaddr addr) { - if (tlb_entry->addr_write == (vaddr | TLB_NOTDIRTY)) { - tlb_entry->addr_write = vaddr; + if (tlb_entry->addr_write == (addr | TLB_NOTDIRTY)) { + tlb_entry->addr_write = addr; } } /* update the TLB corresponding to virtual page vaddr so that it is no longer dirty */ -void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) +void tlb_set_dirty(CPUState *cpu, vaddr addr) { - CPUArchState *env = cpu->env_ptr; int mmu_idx; assert_cpu_is_self(cpu); - vaddr &= TARGET_PAGE_MASK; - qemu_spin_lock(&env_tlb(env)->c.lock); + addr &= TARGET_PAGE_MASK; + qemu_spin_lock(&cpu->neg.tlb.c.lock); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { - tlb_set_dirty1_locked(tlb_entry(env, mmu_idx, vaddr), vaddr); + tlb_set_dirty1_locked(tlb_entry(cpu, mmu_idx, addr), addr); } for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { int k; for (k = 0; k < CPU_VTLB_SIZE; k++) { - tlb_set_dirty1_locked(&env_tlb(env)->d[mmu_idx].vtable[k], vaddr); + tlb_set_dirty1_locked(&cpu->neg.tlb.d[mmu_idx].vtable[k], addr); } } - qemu_spin_unlock(&env_tlb(env)->c.lock); + qemu_spin_unlock(&cpu->neg.tlb.c.lock); } /* Our TLB does not support large pages, so remember the area covered by large pages and trigger a full TLB flush if these are invalidated. */ -static void tlb_add_large_page(CPUArchState *env, int mmu_idx, - target_ulong vaddr, target_ulong size) +static void tlb_add_large_page(CPUState *cpu, int mmu_idx, + vaddr addr, uint64_t size) { - target_ulong lp_addr = env_tlb(env)->d[mmu_idx].large_page_addr; - target_ulong lp_mask = ~(size - 1); + vaddr lp_addr = cpu->neg.tlb.d[mmu_idx].large_page_addr; + vaddr lp_mask = ~(size - 1); - if (lp_addr == (target_ulong)-1) { + if (lp_addr == (vaddr)-1) { /* No previous large page. */ - lp_addr = vaddr; + lp_addr = addr; } else { /* Extend the existing region to include the new page. This is a compromise between unnecessary flushes and the cost of maintaining a full variable size TLB. */ - lp_mask &= env_tlb(env)->d[mmu_idx].large_page_mask; - while (((lp_addr ^ vaddr) & lp_mask) != 0) { + lp_mask &= cpu->neg.tlb.d[mmu_idx].large_page_mask; + while (((lp_addr ^ addr) & lp_mask) != 0) { lp_mask <<= 1; } } - env_tlb(env)->d[mmu_idx].large_page_addr = lp_addr & lp_mask; - env_tlb(env)->d[mmu_idx].large_page_mask = lp_mask; + cpu->neg.tlb.d[mmu_idx].large_page_addr = lp_addr & lp_mask; + cpu->neg.tlb.d[mmu_idx].large_page_mask = lp_mask; } -/* Add a new TLB entry. At most one entry for a given virtual address +static inline void tlb_set_compare(CPUTLBEntryFull *full, CPUTLBEntry *ent, + vaddr address, int flags, + MMUAccessType access_type, bool enable) +{ + if (enable) { + address |= flags & TLB_FLAGS_MASK; + flags &= TLB_SLOW_FLAGS_MASK; + if (flags) { + address |= TLB_FORCE_SLOW; + } + } else { + address = -1; + flags = 0; + } + ent->addr_idx[access_type] = address; + full->slow_flags[access_type] = flags; +} + +/* + * Add a new TLB entry. At most one entry for a given virtual address * is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the * supplied size is only used by tlb_flush_page. * * Called from TCG-generated code, which is under an RCU read-side * critical section. */ -void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, - hwaddr paddr, MemTxAttrs attrs, int prot, - int mmu_idx, target_ulong size) +void tlb_set_page_full(CPUState *cpu, int mmu_idx, + vaddr addr, CPUTLBEntryFull *full) { - CPUArchState *env = cpu->env_ptr; - CPUTLB *tlb = env_tlb(env); + CPUTLB *tlb = &cpu->neg.tlb; CPUTLBDesc *desc = &tlb->d[mmu_idx]; MemoryRegionSection *section; - unsigned int index; - target_ulong address; - target_ulong write_address; + unsigned int index, read_flags, write_flags; uintptr_t addend; CPUTLBEntry *te, tn; hwaddr iotlb, xlat, sz, paddr_page; - target_ulong vaddr_page; - int asidx = cpu_asidx_from_attrs(cpu, attrs); - int wp_flags; + vaddr addr_page; + int asidx, wp_flags, prot; bool is_ram, is_romd; assert_cpu_is_self(cpu); - if (size <= TARGET_PAGE_SIZE) { + if (full->lg_page_size <= TARGET_PAGE_BITS) { sz = TARGET_PAGE_SIZE; } else { - tlb_add_large_page(env, mmu_idx, vaddr, size); - sz = size; + sz = (hwaddr)1 << full->lg_page_size; + tlb_add_large_page(cpu, mmu_idx, addr, sz); } - vaddr_page = vaddr & TARGET_PAGE_MASK; - paddr_page = paddr & TARGET_PAGE_MASK; + addr_page = addr & TARGET_PAGE_MASK; + paddr_page = full->phys_addr & TARGET_PAGE_MASK; + prot = full->prot; + asidx = cpu_asidx_from_attrs(cpu, full->attrs); section = address_space_translate_for_iotlb(cpu, asidx, paddr_page, - &xlat, &sz, attrs, &prot); + &xlat, &sz, full->attrs, &prot); assert(sz >= TARGET_PAGE_SIZE); - tlb_debug("vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx + tlb_debug("vaddr=%016" VADDR_PRIx " paddr=0x" HWADDR_FMT_plx " prot=%x idx=%d\n", - vaddr, paddr, prot, mmu_idx); + addr, full->phys_addr, prot, mmu_idx); - address = vaddr_page; - if (size < TARGET_PAGE_SIZE) { + read_flags = full->tlb_fill_flags; + if (full->lg_page_size < TARGET_PAGE_BITS) { /* Repeat the MMU check and TLB fill on every access. */ - address |= TLB_INVALID_MASK; - } - if (attrs.byte_swap) { - address |= TLB_BSWAP; + read_flags |= TLB_INVALID_MASK; } is_ram = memory_region_is_ram(section->mr); @@ -1160,18 +1162,19 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, addend = 0; } - write_address = address; + write_flags = read_flags; if (is_ram) { iotlb = memory_region_get_ram_addr(section->mr) + xlat; + assert(!(iotlb & ~TARGET_PAGE_MASK)); /* * Computing is_clean is expensive; avoid all that unless * the page is actually writable. */ if (prot & PAGE_WRITE) { if (section->readonly) { - write_address |= TLB_DISCARD_WRITE; + write_flags |= TLB_DISCARD_WRITE; } else if (cpu_physical_memory_is_clean(iotlb)) { - write_address |= TLB_NOTDIRTY; + write_flags |= TLB_NOTDIRTY; } } } else { @@ -1182,17 +1185,17 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, * Reads to romd devices go through the ram_ptr found above, * but of course reads to I/O must go through MMIO. */ - write_address |= TLB_MMIO; + write_flags |= TLB_MMIO; if (!is_romd) { - address = write_address; + read_flags = write_flags; } } - wp_flags = cpu_watchpoint_address_matches(cpu, vaddr_page, + wp_flags = cpu_watchpoint_address_matches(cpu, addr_page, TARGET_PAGE_SIZE); - index = tlb_index(env, mmu_idx, vaddr_page); - te = tlb_entry(env, mmu_idx, vaddr_page); + index = tlb_index(cpu, mmu_idx, addr_page); + te = tlb_entry(cpu, mmu_idx, addr_page); /* * Hold the TLB lock for the rest of the function. We could acquire/release @@ -1207,92 +1210,89 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, tlb->c.dirty |= 1 << mmu_idx; /* Make sure there's no cached translation for the new page. */ - tlb_flush_vtlb_page_locked(env, mmu_idx, vaddr_page); + tlb_flush_vtlb_page_locked(cpu, mmu_idx, addr_page); /* * Only evict the old entry to the victim tlb if it's for a * different page; otherwise just overwrite the stale data. */ - if (!tlb_hit_page_anyprot(te, vaddr_page) && !tlb_entry_is_empty(te)) { + if (!tlb_hit_page_anyprot(te, addr_page) && !tlb_entry_is_empty(te)) { unsigned vidx = desc->vindex++ % CPU_VTLB_SIZE; CPUTLBEntry *tv = &desc->vtable[vidx]; /* Evict the old entry into the victim tlb. */ copy_tlb_helper_locked(tv, te); - desc->viotlb[vidx] = desc->iotlb[index]; - tlb_n_used_entries_dec(env, mmu_idx); + desc->vfulltlb[vidx] = desc->fulltlb[index]; + tlb_n_used_entries_dec(cpu, mmu_idx); } /* refill the tlb */ /* - * At this point iotlb contains a physical section number in the lower - * TARGET_PAGE_BITS, and either - * + the ram_addr_t of the page base of the target RAM (RAM) - * + the offset within section->mr of the page base (I/O, ROMD) - * We subtract the vaddr_page (which is page aligned and thus won't + * When memory region is ram, iotlb contains a TARGET_PAGE_BITS + * aligned ram_addr_t of the page base of the target RAM. + * Otherwise, iotlb contains + * - a physical section number in the lower TARGET_PAGE_BITS + * - the offset within section->mr of the page base (I/O, ROMD) with the + * TARGET_PAGE_BITS masked off. + * We subtract addr_page (which is page aligned and thus won't * disturb the low bits) to give an offset which can be added to the * (non-page-aligned) vaddr of the eventual memory access to get * the MemoryRegion offset for the access. Note that the vaddr we * subtract here is that of the page base, and not the same as the - * vaddr we add back in io_readx()/io_writex()/get_page_addr_code(). + * vaddr we add back in io_prepare()/get_page_addr_code(). */ - desc->iotlb[index].addr = iotlb - vaddr_page; - desc->iotlb[index].attrs = attrs; + desc->fulltlb[index] = *full; + full = &desc->fulltlb[index]; + full->xlat_section = iotlb - addr_page; + full->phys_addr = paddr_page; /* Now calculate the new entry */ - tn.addend = addend - vaddr_page; - if (prot & PAGE_READ) { - tn.addr_read = address; - if (wp_flags & BP_MEM_READ) { - tn.addr_read |= TLB_WATCHPOINT; - } - } else { - tn.addr_read = -1; - } + tn.addend = addend - addr_page; - if (prot & PAGE_EXEC) { - tn.addr_code = address; - } else { - tn.addr_code = -1; - } + tlb_set_compare(full, &tn, addr_page, read_flags, + MMU_INST_FETCH, prot & PAGE_EXEC); - tn.addr_write = -1; - if (prot & PAGE_WRITE) { - tn.addr_write = write_address; - if (prot & PAGE_WRITE_INV) { - tn.addr_write |= TLB_INVALID_MASK; - } - if (wp_flags & BP_MEM_WRITE) { - tn.addr_write |= TLB_WATCHPOINT; - } + if (wp_flags & BP_MEM_READ) { + read_flags |= TLB_WATCHPOINT; } + tlb_set_compare(full, &tn, addr_page, read_flags, + MMU_DATA_LOAD, prot & PAGE_READ); + + if (prot & PAGE_WRITE_INV) { + write_flags |= TLB_INVALID_MASK; + } + if (wp_flags & BP_MEM_WRITE) { + write_flags |= TLB_WATCHPOINT; + } + tlb_set_compare(full, &tn, addr_page, write_flags, + MMU_DATA_STORE, prot & PAGE_WRITE); copy_tlb_helper_locked(te, &tn); - tlb_n_used_entries_inc(env, mmu_idx); + tlb_n_used_entries_inc(cpu, mmu_idx); qemu_spin_unlock(&tlb->c.lock); } -/* Add a new TLB entry, but without specifying the memory - * transaction attributes to be used. - */ -void tlb_set_page(CPUState *cpu, target_ulong vaddr, - hwaddr paddr, int prot, - int mmu_idx, target_ulong size) +void tlb_set_page_with_attrs(CPUState *cpu, vaddr addr, + hwaddr paddr, MemTxAttrs attrs, int prot, + int mmu_idx, uint64_t size) { - tlb_set_page_with_attrs(cpu, vaddr, paddr, MEMTXATTRS_UNSPECIFIED, - prot, mmu_idx, size); + CPUTLBEntryFull full = { + .phys_addr = paddr, + .attrs = attrs, + .prot = prot, + .lg_page_size = ctz64(size) + }; + + assert(is_power_of_2(size)); + tlb_set_page_full(cpu, mmu_idx, addr, &full); } -static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) +void tlb_set_page(CPUState *cpu, vaddr addr, + hwaddr paddr, int prot, + int mmu_idx, uint64_t size) { - ram_addr_t ram_addr; - - ram_addr = qemu_ram_addr_from_host(ptr); - if (ram_addr == RAM_ADDR_INVALID) { - error_report("Bad ram pointer %p", ptr); - abort(); - } - return ram_addr; + tlb_set_page_with_attrs(cpu, addr, paddr, MEMTXATTRS_UNSPECIFIED, + prot, mmu_idx, size); } /* @@ -1300,18 +1300,17 @@ static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) * caller's prior references to the TLB table (e.g. CPUTLBEntry pointers) must * be discarded and looked up again (e.g. via tlb_entry()). */ -static void tlb_fill(CPUState *cpu, target_ulong addr, int size, +static void tlb_fill(CPUState *cpu, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { - CPUClass *cc = CPU_GET_CLASS(cpu); bool ok; /* * This is not a probe, so only valid return is success; failure * should result in exception + longjmp to the cpu loop. */ - ok = cc->tcg_ops->tlb_fill(cpu, addr, size, - access_type, mmu_idx, false, retaddr); + ok = cpu->cc->tcg_ops->tlb_fill(cpu, addr, size, + access_type, mmu_idx, false, retaddr); assert(ok); } @@ -1319,248 +1318,83 @@ static inline void cpu_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - cc->tcg_ops->do_unaligned_access(cpu, addr, access_type, mmu_idx, retaddr); + cpu->cc->tcg_ops->do_unaligned_access(cpu, addr, access_type, + mmu_idx, retaddr); } -static inline void cpu_transaction_failed(CPUState *cpu, hwaddr physaddr, - vaddr addr, unsigned size, - MMUAccessType access_type, - int mmu_idx, MemTxAttrs attrs, - MemTxResult response, - uintptr_t retaddr) +static MemoryRegionSection * +io_prepare(hwaddr *out_offset, CPUState *cpu, hwaddr xlat, + MemTxAttrs attrs, vaddr addr, uintptr_t retaddr) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (!cpu->ignore_memory_transaction_failures && - cc->tcg_ops->do_transaction_failed) { - cc->tcg_ops->do_transaction_failed(cpu, physaddr, addr, size, - access_type, mmu_idx, attrs, - response, retaddr); - } -} - -static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, - int mmu_idx, target_ulong addr, uintptr_t retaddr, - MMUAccessType access_type, MemOp op) -{ - CPUState *cpu = env_cpu(env); - hwaddr mr_offset; MemoryRegionSection *section; - MemoryRegion *mr; - uint64_t val; - bool locked = false; - MemTxResult r; + hwaddr mr_offset; - section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); - mr = section->mr; - mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; + section = iotlb_to_section(cpu, xlat, attrs); + mr_offset = (xlat & TARGET_PAGE_MASK) + addr; cpu->mem_io_pc = retaddr; - if (!cpu->can_do_io) { + if (!cpu->neg.can_do_io) { cpu_io_recompile(cpu, retaddr); } - if (!qemu_mutex_iothread_locked()) { - qemu_mutex_lock_iothread(); - locked = true; - } - r = memory_region_dispatch_read(mr, mr_offset, &val, op, iotlbentry->attrs); - if (r != MEMTX_OK) { - hwaddr physaddr = mr_offset + - section->offset_within_address_space - - section->offset_within_region; - - cpu_transaction_failed(cpu, physaddr, addr, memop_size(op), access_type, - mmu_idx, iotlbentry->attrs, r, retaddr); - } - if (locked) { - qemu_mutex_unlock_iothread(); - } - - return val; + *out_offset = mr_offset; + return section; } -/* - * Save a potentially trashed IOTLB entry for later lookup by plugin. - * This is read by tlb_plugin_lookup if the iotlb entry doesn't match - * because of the side effect of io_writex changing memory layout. - */ -static void save_iotlb_data(CPUState *cs, hwaddr addr, - MemoryRegionSection *section, hwaddr mr_offset) +static void io_failed(CPUState *cpu, CPUTLBEntryFull *full, vaddr addr, + unsigned size, MMUAccessType access_type, int mmu_idx, + MemTxResult response, uintptr_t retaddr) { -#ifdef CONFIG_PLUGIN - SavedIOTLB *saved = &cs->saved_iotlb; - saved->addr = addr; - saved->section = section; - saved->mr_offset = mr_offset; -#endif -} + if (!cpu->ignore_memory_transaction_failures + && cpu->cc->tcg_ops->do_transaction_failed) { + hwaddr physaddr = full->phys_addr | (addr & ~TARGET_PAGE_MASK); -static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, - int mmu_idx, uint64_t val, target_ulong addr, - uintptr_t retaddr, MemOp op) -{ - CPUState *cpu = env_cpu(env); - hwaddr mr_offset; - MemoryRegionSection *section; - MemoryRegion *mr; - bool locked = false; - MemTxResult r; - - section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); - mr = section->mr; - mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; - if (!cpu->can_do_io) { - cpu_io_recompile(cpu, retaddr); + cpu->cc->tcg_ops->do_transaction_failed(cpu, physaddr, addr, size, + access_type, mmu_idx, + full->attrs, response, retaddr); } - cpu->mem_io_pc = retaddr; - - /* - * The memory_region_dispatch may trigger a flush/resize - * so for plugins we save the iotlb_data just in case. - */ - save_iotlb_data(cpu, iotlbentry->addr, section, mr_offset); - - if (!qemu_mutex_iothread_locked()) { - qemu_mutex_lock_iothread(); - locked = true; - } - r = memory_region_dispatch_write(mr, mr_offset, val, op, iotlbentry->attrs); - if (r != MEMTX_OK) { - hwaddr physaddr = mr_offset + - section->offset_within_address_space - - section->offset_within_region; - - cpu_transaction_failed(cpu, physaddr, addr, memop_size(op), - MMU_DATA_STORE, mmu_idx, iotlbentry->attrs, r, - retaddr); - } - if (locked) { - qemu_mutex_unlock_iothread(); - } -} - -static inline target_ulong tlb_read_ofs(CPUTLBEntry *entry, size_t ofs) -{ -#if TCG_OVERSIZED_GUEST - return *(target_ulong *)((uintptr_t)entry + ofs); -#else - /* ofs might correspond to .addr_write, so use qatomic_read */ - return qatomic_read((target_ulong *)((uintptr_t)entry + ofs)); -#endif } /* Return true if ADDR is present in the victim tlb, and has been copied back to the main tlb. */ -static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, - size_t elt_ofs, target_ulong page) +static bool victim_tlb_hit(CPUState *cpu, size_t mmu_idx, size_t index, + MMUAccessType access_type, vaddr page) { size_t vidx; - assert_cpu_is_self(env_cpu(env)); + assert_cpu_is_self(cpu); for (vidx = 0; vidx < CPU_VTLB_SIZE; ++vidx) { - CPUTLBEntry *vtlb = &env_tlb(env)->d[mmu_idx].vtable[vidx]; - target_ulong cmp; - - /* elt_ofs might correspond to .addr_write, so use qatomic_read */ -#if TCG_OVERSIZED_GUEST - cmp = *(target_ulong *)((uintptr_t)vtlb + elt_ofs); -#else - cmp = qatomic_read((target_ulong *)((uintptr_t)vtlb + elt_ofs)); -#endif + CPUTLBEntry *vtlb = &cpu->neg.tlb.d[mmu_idx].vtable[vidx]; + uint64_t cmp = tlb_read_idx(vtlb, access_type); if (cmp == page) { /* Found entry in victim tlb, swap tlb and iotlb. */ - CPUTLBEntry tmptlb, *tlb = &env_tlb(env)->f[mmu_idx].table[index]; + CPUTLBEntry tmptlb, *tlb = &cpu->neg.tlb.f[mmu_idx].table[index]; - qemu_spin_lock(&env_tlb(env)->c.lock); + qemu_spin_lock(&cpu->neg.tlb.c.lock); copy_tlb_helper_locked(&tmptlb, tlb); copy_tlb_helper_locked(tlb, vtlb); copy_tlb_helper_locked(vtlb, &tmptlb); - qemu_spin_unlock(&env_tlb(env)->c.lock); + qemu_spin_unlock(&cpu->neg.tlb.c.lock); - CPUIOTLBEntry tmpio, *io = &env_tlb(env)->d[mmu_idx].iotlb[index]; - CPUIOTLBEntry *vio = &env_tlb(env)->d[mmu_idx].viotlb[vidx]; - tmpio = *io; *io = *vio; *vio = tmpio; + CPUTLBEntryFull *f1 = &cpu->neg.tlb.d[mmu_idx].fulltlb[index]; + CPUTLBEntryFull *f2 = &cpu->neg.tlb.d[mmu_idx].vfulltlb[vidx]; + CPUTLBEntryFull tmpf; + tmpf = *f1; *f1 = *f2; *f2 = tmpf; return true; } } return false; } -/* Macro to call the above, with local variables from the use context. */ -#define VICTIM_TLB_HIT(TY, ADDR) \ - victim_tlb_hit(env, mmu_idx, index, offsetof(CPUTLBEntry, TY), \ - (ADDR) & TARGET_PAGE_MASK) - -/* - * Return a ram_addr_t for the virtual address for execution. - * - * Return -1 if we can't translate and execute from an entire page - * of RAM. This will force us to execute by loading and translating - * one insn at a time, without caching. - * - * NOTE: This function will trigger an exception if the page is - * not executable. - */ -tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong addr, - void **hostp) -{ - uintptr_t mmu_idx = cpu_mmu_index(env, true); - uintptr_t index = tlb_index(env, mmu_idx, addr); - CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); - void *p; - - if (unlikely(!tlb_hit(entry->addr_code, addr))) { - if (!VICTIM_TLB_HIT(addr_code, addr)) { - tlb_fill(env_cpu(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0); - index = tlb_index(env, mmu_idx, addr); - entry = tlb_entry(env, mmu_idx, addr); - - if (unlikely(entry->addr_code & TLB_INVALID_MASK)) { - /* - * The MMU protection covers a smaller range than a target - * page, so we must redo the MMU check for every insn. - */ - return -1; - } - } - assert(tlb_hit(entry->addr_code, addr)); - } - - if (unlikely(entry->addr_code & TLB_MMIO)) { - /* The region is not backed by RAM. */ - if (hostp) { - *hostp = NULL; - } - return -1; - } - - p = (void *)((uintptr_t)addr + entry->addend); - if (hostp) { - *hostp = p; - } - return qemu_ram_addr_from_host_nofail(p); -} - -tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) -{ - return get_page_addr_code_hostp(env, addr, NULL); -} - static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size, - CPUIOTLBEntry *iotlbentry, uintptr_t retaddr) + CPUTLBEntryFull *full, uintptr_t retaddr) { - ram_addr_t ram_addr = mem_vaddr + iotlbentry->addr; + ram_addr_t ram_addr = mem_vaddr + full->xlat_section; trace_memory_notdirty_write_access(mem_vaddr, ram_addr, size); if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) { - struct page_collection *pages - = page_collection_lock(ram_addr, ram_addr + size); - tb_invalidate_phys_page_fast(pages, ram_addr, size, retaddr); - page_collection_unlock(pages); + tb_invalidate_phys_range_fast(ram_addr, size, retaddr); } /* @@ -1576,54 +1410,51 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size, } } -static int probe_access_internal(CPUArchState *env, target_ulong addr, +static int probe_access_internal(CPUState *cpu, vaddr addr, int fault_size, MMUAccessType access_type, int mmu_idx, bool nonfault, - void **phost, uintptr_t retaddr) + void **phost, CPUTLBEntryFull **pfull, + uintptr_t retaddr, bool check_mem_cbs) { - uintptr_t index = tlb_index(env, mmu_idx, addr); - CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); - target_ulong tlb_addr, page_addr; - size_t elt_ofs; - int flags; + uintptr_t index = tlb_index(cpu, mmu_idx, addr); + CPUTLBEntry *entry = tlb_entry(cpu, mmu_idx, addr); + uint64_t tlb_addr = tlb_read_idx(entry, access_type); + vaddr page_addr = addr & TARGET_PAGE_MASK; + int flags = TLB_FLAGS_MASK & ~TLB_FORCE_SLOW; + bool force_mmio = check_mem_cbs && cpu_plugin_mem_cbs_enabled(cpu); + CPUTLBEntryFull *full; - switch (access_type) { - case MMU_DATA_LOAD: - elt_ofs = offsetof(CPUTLBEntry, addr_read); - break; - case MMU_DATA_STORE: - elt_ofs = offsetof(CPUTLBEntry, addr_write); - break; - case MMU_INST_FETCH: - elt_ofs = offsetof(CPUTLBEntry, addr_code); - break; - default: - g_assert_not_reached(); - } - tlb_addr = tlb_read_ofs(entry, elt_ofs); - - page_addr = addr & TARGET_PAGE_MASK; if (!tlb_hit_page(tlb_addr, page_addr)) { - if (!victim_tlb_hit(env, mmu_idx, index, elt_ofs, page_addr)) { - CPUState *cs = env_cpu(env); - CPUClass *cc = CPU_GET_CLASS(cs); - - if (!cc->tcg_ops->tlb_fill(cs, addr, fault_size, access_type, - mmu_idx, nonfault, retaddr)) { + if (!victim_tlb_hit(cpu, mmu_idx, index, access_type, page_addr)) { + if (!cpu->cc->tcg_ops->tlb_fill(cpu, addr, fault_size, access_type, + mmu_idx, nonfault, retaddr)) { /* Non-faulting page table read failed. */ *phost = NULL; + *pfull = NULL; return TLB_INVALID_MASK; } /* TLB resize via tlb_fill may have moved the entry. */ - entry = tlb_entry(env, mmu_idx, addr); + index = tlb_index(cpu, mmu_idx, addr); + entry = tlb_entry(cpu, mmu_idx, addr); + + /* + * With PAGE_WRITE_INV, we set TLB_INVALID_MASK immediately, + * to force the next access through tlb_fill. We've just + * called tlb_fill, so we know that this entry *is* valid. + */ + flags &= ~TLB_INVALID_MASK; } - tlb_addr = tlb_read_ofs(entry, elt_ofs); + tlb_addr = tlb_read_idx(entry, access_type); } - flags = tlb_addr & TLB_FLAGS_MASK; + flags &= tlb_addr; + + *pfull = full = &cpu->neg.tlb.d[mmu_idx].fulltlb[index]; + flags |= full->slow_flags[access_type]; /* Fold all "mmio-like" bits into TLB_MMIO. This is not RAM. */ - if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY))) { + if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY | TLB_CHECK_ALIGNED)) + || (access_type != MMU_INST_FETCH && force_mmio)) { *phost = NULL; return TLB_MMIO; } @@ -1633,37 +1464,84 @@ static int probe_access_internal(CPUArchState *env, target_ulong addr, return flags; } -int probe_access_flags(CPUArchState *env, target_ulong addr, - MMUAccessType access_type, int mmu_idx, - bool nonfault, void **phost, uintptr_t retaddr) +int probe_access_full(CPUArchState *env, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + bool nonfault, void **phost, CPUTLBEntryFull **pfull, + uintptr_t retaddr) { - int flags; - - flags = probe_access_internal(env, addr, 0, access_type, mmu_idx, - nonfault, phost, retaddr); + int flags = probe_access_internal(env_cpu(env), addr, size, access_type, + mmu_idx, nonfault, phost, pfull, retaddr, + true); /* Handle clean RAM pages. */ if (unlikely(flags & TLB_NOTDIRTY)) { - uintptr_t index = tlb_index(env, mmu_idx, addr); - CPUIOTLBEntry *iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index]; - - notdirty_write(env_cpu(env), addr, 1, iotlbentry, retaddr); + int dirtysize = size == 0 ? 1 : size; + notdirty_write(env_cpu(env), addr, dirtysize, *pfull, retaddr); flags &= ~TLB_NOTDIRTY; } return flags; } -void *probe_access(CPUArchState *env, target_ulong addr, int size, +int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + void **phost, CPUTLBEntryFull **pfull) +{ + void *discard_phost; + CPUTLBEntryFull *discard_tlb; + + /* privately handle users that don't need full results */ + phost = phost ? phost : &discard_phost; + pfull = pfull ? pfull : &discard_tlb; + + int flags = probe_access_internal(env_cpu(env), addr, size, access_type, + mmu_idx, true, phost, pfull, 0, false); + + /* Handle clean RAM pages. */ + if (unlikely(flags & TLB_NOTDIRTY)) { + int dirtysize = size == 0 ? 1 : size; + notdirty_write(env_cpu(env), addr, dirtysize, *pfull, 0); + flags &= ~TLB_NOTDIRTY; + } + + return flags; +} + +int probe_access_flags(CPUArchState *env, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + bool nonfault, void **phost, uintptr_t retaddr) +{ + CPUTLBEntryFull *full; + int flags; + + g_assert(-(addr | TARGET_PAGE_MASK) >= size); + + flags = probe_access_internal(env_cpu(env), addr, size, access_type, + mmu_idx, nonfault, phost, &full, retaddr, + true); + + /* Handle clean RAM pages. */ + if (unlikely(flags & TLB_NOTDIRTY)) { + int dirtysize = size == 0 ? 1 : size; + notdirty_write(env_cpu(env), addr, dirtysize, full, retaddr); + flags &= ~TLB_NOTDIRTY; + } + + return flags; +} + +void *probe_access(CPUArchState *env, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { + CPUTLBEntryFull *full; void *host; int flags; g_assert(-(addr | TARGET_PAGE_MASK) >= size); - flags = probe_access_internal(env, addr, size, access_type, mmu_idx, - false, &host, retaddr); + flags = probe_access_internal(env_cpu(env), addr, size, access_type, + mmu_idx, false, &host, &full, retaddr, + true); /* Per the interface, size == 0 merely faults the access. */ if (size == 0) { @@ -1671,20 +1549,17 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, } if (unlikely(flags & (TLB_NOTDIRTY | TLB_WATCHPOINT))) { - uintptr_t index = tlb_index(env, mmu_idx, addr); - CPUIOTLBEntry *iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index]; - /* Handle watchpoints. */ if (flags & TLB_WATCHPOINT) { int wp_access = (access_type == MMU_DATA_STORE ? BP_MEM_WRITE : BP_MEM_READ); cpu_check_watchpoint(env_cpu(env), addr, size, - iotlbentry->attrs, wp_access, retaddr); + full->attrs, wp_access, retaddr); } /* Handle clean RAM pages. */ if (flags & TLB_NOTDIRTY) { - notdirty_write(env_cpu(env), addr, 1, iotlbentry, retaddr); + notdirty_write(env_cpu(env), addr, size, full, retaddr); } } @@ -1694,16 +1569,53 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, MMUAccessType access_type, int mmu_idx) { + CPUTLBEntryFull *full; void *host; int flags; - flags = probe_access_internal(env, addr, 0, access_type, - mmu_idx, true, &host, 0); + flags = probe_access_internal(env_cpu(env), addr, 0, access_type, + mmu_idx, true, &host, &full, 0, false); /* No combination of flags are expected by the caller. */ return flags ? NULL : host; } +/* + * Return a ram_addr_t for the virtual address for execution. + * + * Return -1 if we can't translate and execute from an entire page + * of RAM. This will force us to execute by loading and translating + * one insn at a time, without caching. + * + * NOTE: This function will trigger an exception if the page is + * not executable. + */ +tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, + void **hostp) +{ + CPUTLBEntryFull *full; + void *p; + + (void)probe_access_internal(env_cpu(env), addr, 1, MMU_INST_FETCH, + cpu_mmu_index(env_cpu(env), true), false, + &p, &full, 0, false); + if (p == NULL) { + return -1; + } + + if (full->lg_page_size < TARGET_PAGE_BITS) { + return -1; + } + + if (hostp) { + *hostp = p; + } + return qemu_ram_addr_from_host_nofail(p); +} + +/* Load/store with atomicity primitives. */ +#include "ldst_atomicity.c.inc" + #ifdef CONFIG_PLUGIN /* * Perform a TLB lookup and populate the qemu_plugin_hwaddr structure. @@ -1711,63 +1623,261 @@ void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, * in the softmmu lookup code (or helper). We don't handle re-fills or * checking the victim table. This is purely informational. * - * This almost never fails as the memory access being instrumented - * should have just filled the TLB. The one corner case is io_writex - * which can cause TLB flushes and potential resizing of the TLBs - * losing the information we need. In those cases we need to recover - * data from a copy of the iotlbentry. As long as this always occurs - * from the same thread (which a mem callback will be) this is safe. + * The one corner case is i/o write, which can cause changes to the + * address space. Those changes, and the corresponding tlb flush, + * should be delayed until the next TB, so even then this ought not fail. + * But check, Just in Case. */ - -bool tlb_plugin_lookup(CPUState *cpu, target_ulong addr, int mmu_idx, +bool tlb_plugin_lookup(CPUState *cpu, vaddr addr, int mmu_idx, bool is_store, struct qemu_plugin_hwaddr *data) { - CPUArchState *env = cpu->env_ptr; - CPUTLBEntry *tlbe = tlb_entry(env, mmu_idx, addr); - uintptr_t index = tlb_index(env, mmu_idx, addr); - target_ulong tlb_addr = is_store ? tlb_addr_write(tlbe) : tlbe->addr_read; + CPUTLBEntry *tlbe = tlb_entry(cpu, mmu_idx, addr); + uintptr_t index = tlb_index(cpu, mmu_idx, addr); + MMUAccessType access_type = is_store ? MMU_DATA_STORE : MMU_DATA_LOAD; + uint64_t tlb_addr = tlb_read_idx(tlbe, access_type); + CPUTLBEntryFull *full; - if (likely(tlb_hit(tlb_addr, addr))) { - /* We must have an iotlb entry for MMIO */ - if (tlb_addr & TLB_MMIO) { - CPUIOTLBEntry *iotlbentry; - iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index]; - data->is_io = true; - data->v.io.section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); - data->v.io.offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; - } else { - data->is_io = false; - data->v.ram.hostaddr = (void *)((uintptr_t)addr + tlbe->addend); - } - return true; - } else { - SavedIOTLB *saved = &cpu->saved_iotlb; - data->is_io = true; - data->v.io.section = saved->section; - data->v.io.offset = saved->mr_offset; - return true; + if (unlikely(!tlb_hit(tlb_addr, addr))) { + return false; } + + full = &cpu->neg.tlb.d[mmu_idx].fulltlb[index]; + data->phys_addr = full->phys_addr | (addr & ~TARGET_PAGE_MASK); + + /* We must have an iotlb entry for MMIO */ + if (tlb_addr & TLB_MMIO) { + MemoryRegionSection *section = + iotlb_to_section(cpu, full->xlat_section & ~TARGET_PAGE_MASK, + full->attrs); + data->is_io = true; + data->mr = section->mr; + } else { + data->is_io = false; + data->mr = NULL; + } + return true; +} +#endif + +/* + * Probe for a load/store operation. + * Return the host address and into @flags. + */ + +typedef struct MMULookupPageData { + CPUTLBEntryFull *full; + void *haddr; + vaddr addr; + int flags; + int size; +} MMULookupPageData; + +typedef struct MMULookupLocals { + MMULookupPageData page[2]; + MemOp memop; + int mmu_idx; +} MMULookupLocals; + +/** + * mmu_lookup1: translate one page + * @cpu: generic cpu state + * @data: lookup parameters + * @mmu_idx: virtual address context + * @access_type: load/store/code + * @ra: return address into tcg generated code, or 0 + * + * Resolve the translation for the one page at @data.addr, filling in + * the rest of @data with the results. If the translation fails, + * tlb_fill will longjmp out. Return true if the softmmu tlb for + * @mmu_idx may have resized. + */ +static bool mmu_lookup1(CPUState *cpu, MMULookupPageData *data, + int mmu_idx, MMUAccessType access_type, uintptr_t ra) +{ + vaddr addr = data->addr; + uintptr_t index = tlb_index(cpu, mmu_idx, addr); + CPUTLBEntry *entry = tlb_entry(cpu, mmu_idx, addr); + uint64_t tlb_addr = tlb_read_idx(entry, access_type); + bool maybe_resized = false; + CPUTLBEntryFull *full; + int flags; + + /* If the TLB entry is for a different page, reload and try again. */ + if (!tlb_hit(tlb_addr, addr)) { + if (!victim_tlb_hit(cpu, mmu_idx, index, access_type, + addr & TARGET_PAGE_MASK)) { + tlb_fill(cpu, addr, data->size, access_type, mmu_idx, ra); + maybe_resized = true; + index = tlb_index(cpu, mmu_idx, addr); + entry = tlb_entry(cpu, mmu_idx, addr); + } + tlb_addr = tlb_read_idx(entry, access_type) & ~TLB_INVALID_MASK; + } + + full = &cpu->neg.tlb.d[mmu_idx].fulltlb[index]; + flags = tlb_addr & (TLB_FLAGS_MASK & ~TLB_FORCE_SLOW); + flags |= full->slow_flags[access_type]; + + data->full = full; + data->flags = flags; + /* Compute haddr speculatively; depending on flags it might be invalid. */ + data->haddr = (void *)((uintptr_t)addr + entry->addend); + + return maybe_resized; } -#endif +/** + * mmu_watch_or_dirty + * @cpu: generic cpu state + * @data: lookup parameters + * @access_type: load/store/code + * @ra: return address into tcg generated code, or 0 + * + * Trigger watchpoints for @data.addr:@data.size; + * record writes to protected clean pages. + */ +static void mmu_watch_or_dirty(CPUState *cpu, MMULookupPageData *data, + MMUAccessType access_type, uintptr_t ra) +{ + CPUTLBEntryFull *full = data->full; + vaddr addr = data->addr; + int flags = data->flags; + int size = data->size; + + /* On watchpoint hit, this will longjmp out. */ + if (flags & TLB_WATCHPOINT) { + int wp = access_type == MMU_DATA_STORE ? BP_MEM_WRITE : BP_MEM_READ; + cpu_check_watchpoint(cpu, addr, size, full->attrs, wp, ra); + flags &= ~TLB_WATCHPOINT; + } + + /* Note that notdirty is only set for writes. */ + if (flags & TLB_NOTDIRTY) { + notdirty_write(cpu, addr, size, full, ra); + flags &= ~TLB_NOTDIRTY; + } + data->flags = flags; +} + +/** + * mmu_lookup: translate page(s) + * @cpu: generic cpu state + * @addr: virtual address + * @oi: combined mmu_idx and MemOp + * @ra: return address into tcg generated code, or 0 + * @access_type: load/store/code + * @l: output result + * + * Resolve the translation for the page(s) beginning at @addr, for MemOp.size + * bytes. Return true if the lookup crosses a page boundary. + */ +static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi, + uintptr_t ra, MMUAccessType type, MMULookupLocals *l) +{ + unsigned a_bits; + bool crosspage; + int flags; + + l->memop = get_memop(oi); + l->mmu_idx = get_mmuidx(oi); + + tcg_debug_assert(l->mmu_idx < NB_MMU_MODES); + + /* Handle CPU specific unaligned behaviour */ + a_bits = get_alignment_bits(l->memop); + if (addr & ((1 << a_bits) - 1)) { + cpu_unaligned_access(cpu, addr, type, l->mmu_idx, ra); + } + + l->page[0].addr = addr; + l->page[0].size = memop_size(l->memop); + l->page[1].addr = (addr + l->page[0].size - 1) & TARGET_PAGE_MASK; + l->page[1].size = 0; + crosspage = (addr ^ l->page[1].addr) & TARGET_PAGE_MASK; + + if (likely(!crosspage)) { + mmu_lookup1(cpu, &l->page[0], l->mmu_idx, type, ra); + + flags = l->page[0].flags; + if (unlikely(flags & (TLB_WATCHPOINT | TLB_NOTDIRTY))) { + mmu_watch_or_dirty(cpu, &l->page[0], type, ra); + } + if (unlikely(flags & TLB_BSWAP)) { + l->memop ^= MO_BSWAP; + } + } else { + /* Finish compute of page crossing. */ + int size0 = l->page[1].addr - addr; + l->page[1].size = l->page[0].size - size0; + l->page[0].size = size0; + + /* + * Lookup both pages, recognizing exceptions from either. If the + * second lookup potentially resized, refresh first CPUTLBEntryFull. + */ + mmu_lookup1(cpu, &l->page[0], l->mmu_idx, type, ra); + if (mmu_lookup1(cpu, &l->page[1], l->mmu_idx, type, ra)) { + uintptr_t index = tlb_index(cpu, l->mmu_idx, addr); + l->page[0].full = &cpu->neg.tlb.d[l->mmu_idx].fulltlb[index]; + } + + flags = l->page[0].flags | l->page[1].flags; + if (unlikely(flags & (TLB_WATCHPOINT | TLB_NOTDIRTY))) { + mmu_watch_or_dirty(cpu, &l->page[0], type, ra); + mmu_watch_or_dirty(cpu, &l->page[1], type, ra); + } + + /* + * Since target/sparc is the only user of TLB_BSWAP, and all + * Sparc accesses are aligned, any treatment across two pages + * would be arbitrary. Refuse it until there's a use. + */ + tcg_debug_assert((flags & TLB_BSWAP) == 0); + } + + /* + * This alignment check differs from the one above, in that this is + * based on the atomicity of the operation. The intended use case is + * the ARM memory type field of each PTE, where access to pages with + * Device memory type require alignment. + */ + if (unlikely(flags & TLB_CHECK_ALIGNED)) { + MemOp size = l->memop & MO_SIZE; + + switch (l->memop & MO_ATOM_MASK) { + case MO_ATOM_NONE: + size = MO_8; + break; + case MO_ATOM_IFALIGN_PAIR: + case MO_ATOM_WITHIN16_PAIR: + size = size ? size - 1 : 0; + break; + default: + break; + } + if (addr & ((1 << size) - 1)) { + cpu_unaligned_access(cpu, addr, type, l->mmu_idx, ra); + } + } + + return crosspage; +} /* * Probe for an atomic operation. Do not allow unaligned operations, * or io operations to proceed. Return the host address. - * - * @prot may be PAGE_READ, PAGE_WRITE, or PAGE_READ|PAGE_WRITE. */ -static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, - MemOpIdx oi, int size, int prot, - uintptr_t retaddr) +static void *atomic_mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi, + int size, uintptr_t retaddr) { uintptr_t mmu_idx = get_mmuidx(oi); MemOp mop = get_memop(oi); int a_bits = get_alignment_bits(mop); uintptr_t index; CPUTLBEntry *tlbe; - target_ulong tlb_addr; + vaddr tlb_addr; void *hostaddr; + CPUTLBEntryFull *full; tcg_debug_assert(mmu_idx < NB_MMU_MODES); @@ -1777,7 +1887,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, /* Enforce guest required alignment. */ if (unlikely(a_bits > 0 && (addr & ((1 << a_bits) - 1)))) { /* ??? Maybe indicate atomic op to cpu_unaligned_access */ - cpu_unaligned_access(env_cpu(env), addr, MMU_DATA_STORE, + cpu_unaligned_access(cpu, addr, MMU_DATA_STORE, mmu_idx, retaddr); } @@ -1790,84 +1900,74 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, goto stop_the_world; } - index = tlb_index(env, mmu_idx, addr); - tlbe = tlb_entry(env, mmu_idx, addr); + index = tlb_index(cpu, mmu_idx, addr); + tlbe = tlb_entry(cpu, mmu_idx, addr); /* Check TLB entry and enforce page permissions. */ - if (prot & PAGE_WRITE) { - tlb_addr = tlb_addr_write(tlbe); - if (!tlb_hit(tlb_addr, addr)) { - if (!VICTIM_TLB_HIT(addr_write, addr)) { - tlb_fill(env_cpu(env), addr, size, - MMU_DATA_STORE, mmu_idx, retaddr); - index = tlb_index(env, mmu_idx, addr); - tlbe = tlb_entry(env, mmu_idx, addr); - } - tlb_addr = tlb_addr_write(tlbe) & ~TLB_INVALID_MASK; - } - - /* Let the guest notice RMW on a write-only page. */ - if ((prot & PAGE_READ) && - unlikely(tlbe->addr_read != (tlb_addr & ~TLB_NOTDIRTY))) { - tlb_fill(env_cpu(env), addr, size, - MMU_DATA_LOAD, mmu_idx, retaddr); - /* - * Since we don't support reads and writes to different addresses, - * and we do have the proper page loaded for write, this shouldn't - * ever return. But just in case, handle via stop-the-world. - */ - goto stop_the_world; - } - } else /* if (prot & PAGE_READ) */ { - tlb_addr = tlbe->addr_read; - if (!tlb_hit(tlb_addr, addr)) { - if (!VICTIM_TLB_HIT(addr_write, addr)) { - tlb_fill(env_cpu(env), addr, size, - MMU_DATA_LOAD, mmu_idx, retaddr); - index = tlb_index(env, mmu_idx, addr); - tlbe = tlb_entry(env, mmu_idx, addr); - } - tlb_addr = tlbe->addr_read & ~TLB_INVALID_MASK; + tlb_addr = tlb_addr_write(tlbe); + if (!tlb_hit(tlb_addr, addr)) { + if (!victim_tlb_hit(cpu, mmu_idx, index, MMU_DATA_STORE, + addr & TARGET_PAGE_MASK)) { + tlb_fill(cpu, addr, size, + MMU_DATA_STORE, mmu_idx, retaddr); + index = tlb_index(cpu, mmu_idx, addr); + tlbe = tlb_entry(cpu, mmu_idx, addr); } + tlb_addr = tlb_addr_write(tlbe) & ~TLB_INVALID_MASK; } + /* + * Let the guest notice RMW on a write-only page. + * We have just verified that the page is writable. + * Subpage lookups may have left TLB_INVALID_MASK set, + * but addr_read will only be -1 if PAGE_READ was unset. + */ + if (unlikely(tlbe->addr_read == -1)) { + tlb_fill(cpu, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr); + /* + * Since we don't support reads and writes to different + * addresses, and we do have the proper page loaded for + * write, this shouldn't ever return. But just in case, + * handle via stop-the-world. + */ + goto stop_the_world; + } + /* Collect tlb flags for read. */ + tlb_addr |= tlbe->addr_read; + /* Notice an IO access or a needs-MMU-lookup access */ - if (unlikely(tlb_addr & TLB_MMIO)) { + if (unlikely(tlb_addr & (TLB_MMIO | TLB_DISCARD_WRITE))) { /* There's really nothing that can be done to support this apart from stop-the-world. */ goto stop_the_world; } hostaddr = (void *)((uintptr_t)addr + tlbe->addend); + full = &cpu->neg.tlb.d[mmu_idx].fulltlb[index]; if (unlikely(tlb_addr & TLB_NOTDIRTY)) { - notdirty_write(env_cpu(env), addr, size, - &env_tlb(env)->d[mmu_idx].iotlb[index], retaddr); + notdirty_write(cpu, addr, size, full, retaddr); + } + + if (unlikely(tlb_addr & TLB_FORCE_SLOW)) { + int wp_flags = 0; + + if (full->slow_flags[MMU_DATA_STORE] & TLB_WATCHPOINT) { + wp_flags |= BP_MEM_WRITE; + } + if (full->slow_flags[MMU_DATA_LOAD] & TLB_WATCHPOINT) { + wp_flags |= BP_MEM_READ; + } + if (wp_flags) { + cpu_check_watchpoint(cpu, addr, size, + full->attrs, wp_flags, retaddr); + } } return hostaddr; stop_the_world: - cpu_loop_exit_atomic(env_cpu(env), retaddr); -} - -/* - * Verify that we have passed the correct MemOp to the correct function. - * - * In the case of the helper_*_mmu functions, we will have done this by - * using the MemOp to look up the helper during code generation. - * - * In the case of the cpu_*_mmu functions, this is up to the caller. - * We could present one function to target code, and dispatch based on - * the MemOp, but so far we have worked hard to avoid an indirect function - * call along the memory path. - */ -static void validate_memop(MemOpIdx oi, MemOp expected) -{ -#ifdef CONFIG_DEBUG_TCG - MemOp have = get_memop(oi) & (MO_SIZE | MO_BSWAP); - assert(have == expected); -#endif + cpu_loop_exit_atomic(cpu, retaddr); } /* @@ -1877,146 +1977,7 @@ static void validate_memop(MemOpIdx oi, MemOp expected) * specifically for reading instructions from system memory. It is * called by the translation loop and in some helpers where the code * is disassembled. It shouldn't be called directly by guest code. - */ - -typedef uint64_t FullLoadHelper(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); - -static inline uint64_t QEMU_ALWAYS_INLINE -load_memop(const void *haddr, MemOp op) -{ - switch (op) { - case MO_UB: - return ldub_p(haddr); - case MO_BEUW: - return lduw_be_p(haddr); - case MO_LEUW: - return lduw_le_p(haddr); - case MO_BEUL: - return (uint32_t)ldl_be_p(haddr); - case MO_LEUL: - return (uint32_t)ldl_le_p(haddr); - case MO_BEUQ: - return ldq_be_p(haddr); - case MO_LEUQ: - return ldq_le_p(haddr); - default: - qemu_build_not_reached(); - } -} - -static inline uint64_t QEMU_ALWAYS_INLINE -load_helper(CPUArchState *env, target_ulong addr, MemOpIdx oi, - uintptr_t retaddr, MemOp op, bool code_read, - FullLoadHelper *full_load) -{ - const size_t tlb_off = code_read ? - offsetof(CPUTLBEntry, addr_code) : offsetof(CPUTLBEntry, addr_read); - const MMUAccessType access_type = - code_read ? MMU_INST_FETCH : MMU_DATA_LOAD; - const unsigned a_bits = get_alignment_bits(get_memop(oi)); - const size_t size = memop_size(op); - uintptr_t mmu_idx = get_mmuidx(oi); - uintptr_t index; - CPUTLBEntry *entry; - target_ulong tlb_addr; - void *haddr; - uint64_t res; - - tcg_debug_assert(mmu_idx < NB_MMU_MODES); - - /* Handle CPU specific unaligned behaviour */ - if (addr & ((1 << a_bits) - 1)) { - cpu_unaligned_access(env_cpu(env), addr, access_type, - mmu_idx, retaddr); - } - - index = tlb_index(env, mmu_idx, addr); - entry = tlb_entry(env, mmu_idx, addr); - tlb_addr = code_read ? entry->addr_code : entry->addr_read; - - /* If the TLB entry is for a different page, reload and try again. */ - if (!tlb_hit(tlb_addr, addr)) { - if (!victim_tlb_hit(env, mmu_idx, index, tlb_off, - addr & TARGET_PAGE_MASK)) { - tlb_fill(env_cpu(env), addr, size, - access_type, mmu_idx, retaddr); - index = tlb_index(env, mmu_idx, addr); - entry = tlb_entry(env, mmu_idx, addr); - } - tlb_addr = code_read ? entry->addr_code : entry->addr_read; - tlb_addr &= ~TLB_INVALID_MASK; - } - - /* Handle anything that isn't just a straight memory access. */ - if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) { - CPUIOTLBEntry *iotlbentry; - bool need_swap; - - /* For anything that is unaligned, recurse through full_load. */ - if ((addr & (size - 1)) != 0) { - goto do_unaligned_access; - } - - iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index]; - - /* Handle watchpoints. */ - if (unlikely(tlb_addr & TLB_WATCHPOINT)) { - /* On watchpoint hit, this will longjmp out. */ - cpu_check_watchpoint(env_cpu(env), addr, size, - iotlbentry->attrs, BP_MEM_READ, retaddr); - } - - need_swap = size > 1 && (tlb_addr & TLB_BSWAP); - - /* Handle I/O access. */ - if (likely(tlb_addr & TLB_MMIO)) { - return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, - access_type, op ^ (need_swap * MO_BSWAP)); - } - - haddr = (void *)((uintptr_t)addr + entry->addend); - - /* - * Keep these two load_memop separate to ensure that the compiler - * is able to fold the entire function to a single instruction. - * There is a build-time assert inside to remind you of this. ;-) - */ - if (unlikely(need_swap)) { - return load_memop(haddr, op ^ MO_BSWAP); - } - return load_memop(haddr, op); - } - - /* Handle slow unaligned access (it spans two pages or IO). */ - if (size > 1 - && unlikely((addr & ~TARGET_PAGE_MASK) + size - 1 - >= TARGET_PAGE_SIZE)) { - target_ulong addr1, addr2; - uint64_t r1, r2; - unsigned shift; - do_unaligned_access: - addr1 = addr & ~((target_ulong)size - 1); - addr2 = addr1 + size; - r1 = full_load(env, addr1, oi, retaddr); - r2 = full_load(env, addr2, oi, retaddr); - shift = (addr & (size - 1)) * 8; - - if (memop_big_endian(op)) { - /* Big-endian combine. */ - res = (r1 << shift) | (r2 >> ((size * 8) - shift)); - } else { - /* Little-endian combine. */ - res = (r1 >> shift) | (r2 << ((size * 8) - shift)); - } - return res & MAKE_64BIT_MASK(0, size * 8); - } - - haddr = (void *)((uintptr_t)addr + entry->addend); - return load_memop(haddr, op); -} - -/* + * * For the benefit of TCG generated code, we want to avoid the * complication of ABI-specific return type promotion and always * return a value extended to the register size of the host. This is @@ -2026,533 +1987,946 @@ load_helper(CPUArchState *env, target_ulong addr, MemOpIdx oi, * We don't bother with this widened value for SOFTMMU_CODE_ACCESS. */ -static uint64_t full_ldub_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) +/** + * do_ld_mmio_beN: + * @cpu: generic cpu state + * @full: page parameters + * @ret_be: accumulated data + * @addr: virtual address + * @size: number of bytes + * @mmu_idx: virtual address context + * @ra: return address into tcg generated code, or 0 + * Context: BQL held + * + * Load @size bytes from @addr, which is memory-mapped i/o. + * The bytes are concatenated in big-endian order with @ret_be. + */ +static uint64_t int_ld_mmio_beN(CPUState *cpu, CPUTLBEntryFull *full, + uint64_t ret_be, vaddr addr, int size, + int mmu_idx, MMUAccessType type, uintptr_t ra, + MemoryRegion *mr, hwaddr mr_offset) { - validate_memop(oi, MO_UB); - return load_helper(env, addr, oi, retaddr, MO_UB, false, full_ldub_mmu); + do { + MemOp this_mop; + unsigned this_size; + uint64_t val; + MemTxResult r; + + /* Read aligned pieces up to 8 bytes. */ + this_mop = ctz32(size | (int)addr | 8); + this_size = 1 << this_mop; + this_mop |= MO_BE; + + r = memory_region_dispatch_read(mr, mr_offset, &val, + this_mop, full->attrs); + if (unlikely(r != MEMTX_OK)) { + io_failed(cpu, full, addr, this_size, type, mmu_idx, r, ra); + } + if (this_size == 8) { + return val; + } + + ret_be = (ret_be << (this_size * 8)) | val; + addr += this_size; + mr_offset += this_size; + size -= this_size; + } while (size); + + return ret_be; } -tcg_target_ulong helper_ret_ldub_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) +static uint64_t do_ld_mmio_beN(CPUState *cpu, CPUTLBEntryFull *full, + uint64_t ret_be, vaddr addr, int size, + int mmu_idx, MMUAccessType type, uintptr_t ra) { - return full_ldub_mmu(env, addr, oi, retaddr); + MemoryRegionSection *section; + MemoryRegion *mr; + hwaddr mr_offset; + MemTxAttrs attrs; + + tcg_debug_assert(size > 0 && size <= 8); + + attrs = full->attrs; + section = io_prepare(&mr_offset, cpu, full->xlat_section, attrs, addr, ra); + mr = section->mr; + + BQL_LOCK_GUARD(); + return int_ld_mmio_beN(cpu, full, ret_be, addr, size, mmu_idx, + type, ra, mr, mr_offset); } -static uint64_t full_le_lduw_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) +static Int128 do_ld16_mmio_beN(CPUState *cpu, CPUTLBEntryFull *full, + uint64_t ret_be, vaddr addr, int size, + int mmu_idx, uintptr_t ra) { - validate_memop(oi, MO_LEUW); - return load_helper(env, addr, oi, retaddr, MO_LEUW, false, - full_le_lduw_mmu); + MemoryRegionSection *section; + MemoryRegion *mr; + hwaddr mr_offset; + MemTxAttrs attrs; + uint64_t a, b; + + tcg_debug_assert(size > 8 && size <= 16); + + attrs = full->attrs; + section = io_prepare(&mr_offset, cpu, full->xlat_section, attrs, addr, ra); + mr = section->mr; + + BQL_LOCK_GUARD(); + a = int_ld_mmio_beN(cpu, full, ret_be, addr, size - 8, mmu_idx, + MMU_DATA_LOAD, ra, mr, mr_offset); + b = int_ld_mmio_beN(cpu, full, ret_be, addr + size - 8, 8, mmu_idx, + MMU_DATA_LOAD, ra, mr, mr_offset + size - 8); + return int128_make128(b, a); } -tcg_target_ulong helper_le_lduw_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) +/** + * do_ld_bytes_beN + * @p: translation parameters + * @ret_be: accumulated data + * + * Load @p->size bytes from @p->haddr, which is RAM. + * The bytes to concatenated in big-endian order with @ret_be. + */ +static uint64_t do_ld_bytes_beN(MMULookupPageData *p, uint64_t ret_be) { - return full_le_lduw_mmu(env, addr, oi, retaddr); + uint8_t *haddr = p->haddr; + int i, size = p->size; + + for (i = 0; i < size; i++) { + ret_be = (ret_be << 8) | haddr[i]; + } + return ret_be; } -static uint64_t full_be_lduw_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) +/** + * do_ld_parts_beN + * @p: translation parameters + * @ret_be: accumulated data + * + * As do_ld_bytes_beN, but atomically on each aligned part. + */ +static uint64_t do_ld_parts_beN(MMULookupPageData *p, uint64_t ret_be) { - validate_memop(oi, MO_BEUW); - return load_helper(env, addr, oi, retaddr, MO_BEUW, false, - full_be_lduw_mmu); + void *haddr = p->haddr; + int size = p->size; + + do { + uint64_t x; + int n; + + /* + * Find minimum of alignment and size. + * This is slightly stronger than required by MO_ATOM_SUBALIGN, which + * would have only checked the low bits of addr|size once at the start, + * but is just as easy. + */ + switch (((uintptr_t)haddr | size) & 7) { + case 4: + x = cpu_to_be32(load_atomic4(haddr)); + ret_be = (ret_be << 32) | x; + n = 4; + break; + case 2: + case 6: + x = cpu_to_be16(load_atomic2(haddr)); + ret_be = (ret_be << 16) | x; + n = 2; + break; + default: + x = *(uint8_t *)haddr; + ret_be = (ret_be << 8) | x; + n = 1; + break; + case 0: + g_assert_not_reached(); + } + haddr += n; + size -= n; + } while (size != 0); + return ret_be; } -tcg_target_ulong helper_be_lduw_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) +/** + * do_ld_parts_be4 + * @p: translation parameters + * @ret_be: accumulated data + * + * As do_ld_bytes_beN, but with one atomic load. + * Four aligned bytes are guaranteed to cover the load. + */ +static uint64_t do_ld_whole_be4(MMULookupPageData *p, uint64_t ret_be) { - return full_be_lduw_mmu(env, addr, oi, retaddr); + int o = p->addr & 3; + uint32_t x = load_atomic4(p->haddr - o); + + x = cpu_to_be32(x); + x <<= o * 8; + x >>= (4 - p->size) * 8; + return (ret_be << (p->size * 8)) | x; } -static uint64_t full_le_ldul_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) +/** + * do_ld_parts_be8 + * @p: translation parameters + * @ret_be: accumulated data + * + * As do_ld_bytes_beN, but with one atomic load. + * Eight aligned bytes are guaranteed to cover the load. + */ +static uint64_t do_ld_whole_be8(CPUState *cpu, uintptr_t ra, + MMULookupPageData *p, uint64_t ret_be) { - validate_memop(oi, MO_LEUL); - return load_helper(env, addr, oi, retaddr, MO_LEUL, false, - full_le_ldul_mmu); + int o = p->addr & 7; + uint64_t x = load_atomic8_or_exit(cpu, ra, p->haddr - o); + + x = cpu_to_be64(x); + x <<= o * 8; + x >>= (8 - p->size) * 8; + return (ret_be << (p->size * 8)) | x; } -tcg_target_ulong helper_le_ldul_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) +/** + * do_ld_parts_be16 + * @p: translation parameters + * @ret_be: accumulated data + * + * As do_ld_bytes_beN, but with one atomic load. + * 16 aligned bytes are guaranteed to cover the load. + */ +static Int128 do_ld_whole_be16(CPUState *cpu, uintptr_t ra, + MMULookupPageData *p, uint64_t ret_be) { - return full_le_ldul_mmu(env, addr, oi, retaddr); -} + int o = p->addr & 15; + Int128 x, y = load_atomic16_or_exit(cpu, ra, p->haddr - o); + int size = p->size; -static uint64_t full_be_ldul_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) -{ - validate_memop(oi, MO_BEUL); - return load_helper(env, addr, oi, retaddr, MO_BEUL, false, - full_be_ldul_mmu); -} - -tcg_target_ulong helper_be_ldul_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) -{ - return full_be_ldul_mmu(env, addr, oi, retaddr); -} - -uint64_t helper_le_ldq_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) -{ - validate_memop(oi, MO_LEUQ); - return load_helper(env, addr, oi, retaddr, MO_LEUQ, false, - helper_le_ldq_mmu); -} - -uint64_t helper_be_ldq_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) -{ - validate_memop(oi, MO_BEUQ); - return load_helper(env, addr, oi, retaddr, MO_BEUQ, false, - helper_be_ldq_mmu); + if (!HOST_BIG_ENDIAN) { + y = bswap128(y); + } + y = int128_lshift(y, o * 8); + y = int128_urshift(y, (16 - size) * 8); + x = int128_make64(ret_be); + x = int128_lshift(x, size * 8); + return int128_or(x, y); } /* - * Provide signed versions of the load routines as well. We can of course - * avoid this for 64-bit data, or for 32-bit data on 32-bit host. + * Wrapper for the above. */ - - -tcg_target_ulong helper_ret_ldsb_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) +static uint64_t do_ld_beN(CPUState *cpu, MMULookupPageData *p, + uint64_t ret_be, int mmu_idx, MMUAccessType type, + MemOp mop, uintptr_t ra) { - return (int8_t)helper_ret_ldub_mmu(env, addr, oi, retaddr); -} + MemOp atom; + unsigned tmp, half_size; -tcg_target_ulong helper_le_ldsw_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) -{ - return (int16_t)helper_le_lduw_mmu(env, addr, oi, retaddr); -} + if (unlikely(p->flags & TLB_MMIO)) { + return do_ld_mmio_beN(cpu, p->full, ret_be, p->addr, p->size, + mmu_idx, type, ra); + } -tcg_target_ulong helper_be_ldsw_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) -{ - return (int16_t)helper_be_lduw_mmu(env, addr, oi, retaddr); -} + /* + * It is a given that we cross a page and therefore there is no + * atomicity for the load as a whole, but subobjects may need attention. + */ + atom = mop & MO_ATOM_MASK; + switch (atom) { + case MO_ATOM_SUBALIGN: + return do_ld_parts_beN(p, ret_be); -tcg_target_ulong helper_le_ldsl_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) -{ - return (int32_t)helper_le_ldul_mmu(env, addr, oi, retaddr); -} + case MO_ATOM_IFALIGN_PAIR: + case MO_ATOM_WITHIN16_PAIR: + tmp = mop & MO_SIZE; + tmp = tmp ? tmp - 1 : 0; + half_size = 1 << tmp; + if (atom == MO_ATOM_IFALIGN_PAIR + ? p->size == half_size + : p->size >= half_size) { + if (!HAVE_al8_fast && p->size < 4) { + return do_ld_whole_be4(p, ret_be); + } else { + return do_ld_whole_be8(cpu, ra, p, ret_be); + } + } + /* fall through */ -tcg_target_ulong helper_be_ldsl_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) -{ - return (int32_t)helper_be_ldul_mmu(env, addr, oi, retaddr); + case MO_ATOM_IFALIGN: + case MO_ATOM_WITHIN16: + case MO_ATOM_NONE: + return do_ld_bytes_beN(p, ret_be); + + default: + g_assert_not_reached(); + } } /* - * Load helpers for cpu_ldst.h. + * Wrapper for the above, for 8 < size < 16. */ - -static inline uint64_t cpu_load_helper(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t retaddr, - FullLoadHelper *full_load) +static Int128 do_ld16_beN(CPUState *cpu, MMULookupPageData *p, + uint64_t a, int mmu_idx, MemOp mop, uintptr_t ra) { - uint64_t ret; + int size = p->size; + uint64_t b; + MemOp atom; - ret = full_load(env, addr, oi, retaddr); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); + if (unlikely(p->flags & TLB_MMIO)) { + return do_ld16_mmio_beN(cpu, p->full, a, p->addr, size, mmu_idx, ra); + } + + /* + * It is a given that we cross a page and therefore there is no + * atomicity for the load as a whole, but subobjects may need attention. + */ + atom = mop & MO_ATOM_MASK; + switch (atom) { + case MO_ATOM_SUBALIGN: + p->size = size - 8; + a = do_ld_parts_beN(p, a); + p->haddr += size - 8; + p->size = 8; + b = do_ld_parts_beN(p, 0); + break; + + case MO_ATOM_WITHIN16_PAIR: + /* Since size > 8, this is the half that must be atomic. */ + return do_ld_whole_be16(cpu, ra, p, a); + + case MO_ATOM_IFALIGN_PAIR: + /* + * Since size > 8, both halves are misaligned, + * and so neither is atomic. + */ + case MO_ATOM_IFALIGN: + case MO_ATOM_WITHIN16: + case MO_ATOM_NONE: + p->size = size - 8; + a = do_ld_bytes_beN(p, a); + b = ldq_be_p(p->haddr + size - 8); + break; + + default: + g_assert_not_reached(); + } + + return int128_make128(b, a); +} + +static uint8_t do_ld_1(CPUState *cpu, MMULookupPageData *p, int mmu_idx, + MMUAccessType type, uintptr_t ra) +{ + if (unlikely(p->flags & TLB_MMIO)) { + return do_ld_mmio_beN(cpu, p->full, 0, p->addr, 1, mmu_idx, type, ra); + } else { + return *(uint8_t *)p->haddr; + } +} + +static uint16_t do_ld_2(CPUState *cpu, MMULookupPageData *p, int mmu_idx, + MMUAccessType type, MemOp memop, uintptr_t ra) +{ + uint16_t ret; + + if (unlikely(p->flags & TLB_MMIO)) { + ret = do_ld_mmio_beN(cpu, p->full, 0, p->addr, 2, mmu_idx, type, ra); + if ((memop & MO_BSWAP) == MO_LE) { + ret = bswap16(ret); + } + } else { + /* Perform the load host endian, then swap if necessary. */ + ret = load_atom_2(cpu, ra, p->haddr, memop); + if (memop & MO_BSWAP) { + ret = bswap16(ret); + } + } return ret; } -uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra) +static uint32_t do_ld_4(CPUState *cpu, MMULookupPageData *p, int mmu_idx, + MMUAccessType type, MemOp memop, uintptr_t ra) { - return cpu_load_helper(env, addr, oi, ra, full_ldub_mmu); + uint32_t ret; + + if (unlikely(p->flags & TLB_MMIO)) { + ret = do_ld_mmio_beN(cpu, p->full, 0, p->addr, 4, mmu_idx, type, ra); + if ((memop & MO_BSWAP) == MO_LE) { + ret = bswap32(ret); + } + } else { + /* Perform the load host endian. */ + ret = load_atom_4(cpu, ra, p->haddr, memop); + if (memop & MO_BSWAP) { + ret = bswap32(ret); + } + } + return ret; } -uint16_t cpu_ldw_be_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) +static uint64_t do_ld_8(CPUState *cpu, MMULookupPageData *p, int mmu_idx, + MMUAccessType type, MemOp memop, uintptr_t ra) { - return cpu_load_helper(env, addr, oi, ra, full_be_lduw_mmu); + uint64_t ret; + + if (unlikely(p->flags & TLB_MMIO)) { + ret = do_ld_mmio_beN(cpu, p->full, 0, p->addr, 8, mmu_idx, type, ra); + if ((memop & MO_BSWAP) == MO_LE) { + ret = bswap64(ret); + } + } else { + /* Perform the load host endian. */ + ret = load_atom_8(cpu, ra, p->haddr, memop); + if (memop & MO_BSWAP) { + ret = bswap64(ret); + } + } + return ret; } -uint32_t cpu_ldl_be_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) +static uint8_t do_ld1_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, + uintptr_t ra, MMUAccessType access_type) { - return cpu_load_helper(env, addr, oi, ra, full_be_ldul_mmu); + MMULookupLocals l; + bool crosspage; + + cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l); + tcg_debug_assert(!crosspage); + + return do_ld_1(cpu, &l.page[0], l.mmu_idx, access_type, ra); } -uint64_t cpu_ldq_be_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) +static uint16_t do_ld2_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, + uintptr_t ra, MMUAccessType access_type) { - return cpu_load_helper(env, addr, oi, ra, helper_be_ldq_mmu); + MMULookupLocals l; + bool crosspage; + uint16_t ret; + uint8_t a, b; + + cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l); + if (likely(!crosspage)) { + return do_ld_2(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra); + } + + a = do_ld_1(cpu, &l.page[0], l.mmu_idx, access_type, ra); + b = do_ld_1(cpu, &l.page[1], l.mmu_idx, access_type, ra); + + if ((l.memop & MO_BSWAP) == MO_LE) { + ret = a | (b << 8); + } else { + ret = b | (a << 8); + } + return ret; } -uint16_t cpu_ldw_le_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) +static uint32_t do_ld4_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, + uintptr_t ra, MMUAccessType access_type) { - return cpu_load_helper(env, addr, oi, ra, full_le_lduw_mmu); + MMULookupLocals l; + bool crosspage; + uint32_t ret; + + cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l); + if (likely(!crosspage)) { + return do_ld_4(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra); + } + + ret = do_ld_beN(cpu, &l.page[0], 0, l.mmu_idx, access_type, l.memop, ra); + ret = do_ld_beN(cpu, &l.page[1], ret, l.mmu_idx, access_type, l.memop, ra); + if ((l.memop & MO_BSWAP) == MO_LE) { + ret = bswap32(ret); + } + return ret; } -uint32_t cpu_ldl_le_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) +static uint64_t do_ld8_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, + uintptr_t ra, MMUAccessType access_type) { - return cpu_load_helper(env, addr, oi, ra, full_le_ldul_mmu); + MMULookupLocals l; + bool crosspage; + uint64_t ret; + + cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l); + if (likely(!crosspage)) { + return do_ld_8(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra); + } + + ret = do_ld_beN(cpu, &l.page[0], 0, l.mmu_idx, access_type, l.memop, ra); + ret = do_ld_beN(cpu, &l.page[1], ret, l.mmu_idx, access_type, l.memop, ra); + if ((l.memop & MO_BSWAP) == MO_LE) { + ret = bswap64(ret); + } + return ret; } -uint64_t cpu_ldq_le_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) +static Int128 do_ld16_mmu(CPUState *cpu, vaddr addr, + MemOpIdx oi, uintptr_t ra) { - return cpu_load_helper(env, addr, oi, ra, helper_le_ldq_mmu); + MMULookupLocals l; + bool crosspage; + uint64_t a, b; + Int128 ret; + int first; + + cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_LOAD, &l); + if (likely(!crosspage)) { + if (unlikely(l.page[0].flags & TLB_MMIO)) { + ret = do_ld16_mmio_beN(cpu, l.page[0].full, 0, addr, 16, + l.mmu_idx, ra); + if ((l.memop & MO_BSWAP) == MO_LE) { + ret = bswap128(ret); + } + } else { + /* Perform the load host endian. */ + ret = load_atom_16(cpu, ra, l.page[0].haddr, l.memop); + if (l.memop & MO_BSWAP) { + ret = bswap128(ret); + } + } + return ret; + } + + first = l.page[0].size; + if (first == 8) { + MemOp mop8 = (l.memop & ~MO_SIZE) | MO_64; + + a = do_ld_8(cpu, &l.page[0], l.mmu_idx, MMU_DATA_LOAD, mop8, ra); + b = do_ld_8(cpu, &l.page[1], l.mmu_idx, MMU_DATA_LOAD, mop8, ra); + if ((mop8 & MO_BSWAP) == MO_LE) { + ret = int128_make128(a, b); + } else { + ret = int128_make128(b, a); + } + return ret; + } + + if (first < 8) { + a = do_ld_beN(cpu, &l.page[0], 0, l.mmu_idx, + MMU_DATA_LOAD, l.memop, ra); + ret = do_ld16_beN(cpu, &l.page[1], a, l.mmu_idx, l.memop, ra); + } else { + ret = do_ld16_beN(cpu, &l.page[0], 0, l.mmu_idx, l.memop, ra); + b = int128_getlo(ret); + ret = int128_lshift(ret, l.page[1].size * 8); + a = int128_gethi(ret); + b = do_ld_beN(cpu, &l.page[1], b, l.mmu_idx, + MMU_DATA_LOAD, l.memop, ra); + ret = int128_make128(b, a); + } + if ((l.memop & MO_BSWAP) == MO_LE) { + ret = bswap128(ret); + } + return ret; } /* * Store Helpers */ -static inline void QEMU_ALWAYS_INLINE -store_memop(void *haddr, uint64_t val, MemOp op) +/** + * do_st_mmio_leN: + * @cpu: generic cpu state + * @full: page parameters + * @val_le: data to store + * @addr: virtual address + * @size: number of bytes + * @mmu_idx: virtual address context + * @ra: return address into tcg generated code, or 0 + * Context: BQL held + * + * Store @size bytes at @addr, which is memory-mapped i/o. + * The bytes to store are extracted in little-endian order from @val_le; + * return the bytes of @val_le beyond @p->size that have not been stored. + */ +static uint64_t int_st_mmio_leN(CPUState *cpu, CPUTLBEntryFull *full, + uint64_t val_le, vaddr addr, int size, + int mmu_idx, uintptr_t ra, + MemoryRegion *mr, hwaddr mr_offset) { - switch (op) { - case MO_UB: - stb_p(haddr, val); - break; - case MO_BEUW: - stw_be_p(haddr, val); - break; - case MO_LEUW: - stw_le_p(haddr, val); - break; - case MO_BEUL: - stl_be_p(haddr, val); - break; - case MO_LEUL: - stl_le_p(haddr, val); - break; - case MO_BEUQ: - stq_be_p(haddr, val); - break; - case MO_LEUQ: - stq_le_p(haddr, val); - break; - default: - qemu_build_not_reached(); - } -} + do { + MemOp this_mop; + unsigned this_size; + MemTxResult r; -static void full_stb_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr); + /* Store aligned pieces up to 8 bytes. */ + this_mop = ctz32(size | (int)addr | 8); + this_size = 1 << this_mop; + this_mop |= MO_LE; -static void __attribute__((noinline)) -store_helper_unaligned(CPUArchState *env, target_ulong addr, uint64_t val, - uintptr_t retaddr, size_t size, uintptr_t mmu_idx, - bool big_endian) -{ - const size_t tlb_off = offsetof(CPUTLBEntry, addr_write); - uintptr_t index, index2; - CPUTLBEntry *entry, *entry2; - target_ulong page2, tlb_addr, tlb_addr2; - MemOpIdx oi; - size_t size2; - int i; - - /* - * Ensure the second page is in the TLB. Note that the first page - * is already guaranteed to be filled, and that the second page - * cannot evict the first. - */ - page2 = (addr + size) & TARGET_PAGE_MASK; - size2 = (addr + size) & ~TARGET_PAGE_MASK; - index2 = tlb_index(env, mmu_idx, page2); - entry2 = tlb_entry(env, mmu_idx, page2); - - tlb_addr2 = tlb_addr_write(entry2); - if (!tlb_hit_page(tlb_addr2, page2)) { - if (!victim_tlb_hit(env, mmu_idx, index2, tlb_off, page2)) { - tlb_fill(env_cpu(env), page2, size2, MMU_DATA_STORE, - mmu_idx, retaddr); - index2 = tlb_index(env, mmu_idx, page2); - entry2 = tlb_entry(env, mmu_idx, page2); + r = memory_region_dispatch_write(mr, mr_offset, val_le, + this_mop, full->attrs); + if (unlikely(r != MEMTX_OK)) { + io_failed(cpu, full, addr, this_size, MMU_DATA_STORE, + mmu_idx, r, ra); } - tlb_addr2 = tlb_addr_write(entry2); - } - - index = tlb_index(env, mmu_idx, addr); - entry = tlb_entry(env, mmu_idx, addr); - tlb_addr = tlb_addr_write(entry); - - /* - * Handle watchpoints. Since this may trap, all checks - * must happen before any store. - */ - if (unlikely(tlb_addr & TLB_WATCHPOINT)) { - cpu_check_watchpoint(env_cpu(env), addr, size - size2, - env_tlb(env)->d[mmu_idx].iotlb[index].attrs, - BP_MEM_WRITE, retaddr); - } - if (unlikely(tlb_addr2 & TLB_WATCHPOINT)) { - cpu_check_watchpoint(env_cpu(env), page2, size2, - env_tlb(env)->d[mmu_idx].iotlb[index2].attrs, - BP_MEM_WRITE, retaddr); - } - - /* - * XXX: not efficient, but simple. - * This loop must go in the forward direction to avoid issues - * with self-modifying code in Windows 64-bit. - */ - oi = make_memop_idx(MO_UB, mmu_idx); - if (big_endian) { - for (i = 0; i < size; ++i) { - /* Big-endian extract. */ - uint8_t val8 = val >> (((size - 1) * 8) - (i * 8)); - full_stb_mmu(env, addr + i, val8, oi, retaddr); - } - } else { - for (i = 0; i < size; ++i) { - /* Little-endian extract. */ - uint8_t val8 = val >> (i * 8); - full_stb_mmu(env, addr + i, val8, oi, retaddr); - } - } -} - -static inline void QEMU_ALWAYS_INLINE -store_helper(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr, MemOp op) -{ - const size_t tlb_off = offsetof(CPUTLBEntry, addr_write); - const unsigned a_bits = get_alignment_bits(get_memop(oi)); - const size_t size = memop_size(op); - uintptr_t mmu_idx = get_mmuidx(oi); - uintptr_t index; - CPUTLBEntry *entry; - target_ulong tlb_addr; - void *haddr; - - tcg_debug_assert(mmu_idx < NB_MMU_MODES); - - /* Handle CPU specific unaligned behaviour */ - if (addr & ((1 << a_bits) - 1)) { - cpu_unaligned_access(env_cpu(env), addr, MMU_DATA_STORE, - mmu_idx, retaddr); - } - - index = tlb_index(env, mmu_idx, addr); - entry = tlb_entry(env, mmu_idx, addr); - tlb_addr = tlb_addr_write(entry); - - /* If the TLB entry is for a different page, reload and try again. */ - if (!tlb_hit(tlb_addr, addr)) { - if (!victim_tlb_hit(env, mmu_idx, index, tlb_off, - addr & TARGET_PAGE_MASK)) { - tlb_fill(env_cpu(env), addr, size, MMU_DATA_STORE, - mmu_idx, retaddr); - index = tlb_index(env, mmu_idx, addr); - entry = tlb_entry(env, mmu_idx, addr); - } - tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK; - } - - /* Handle anything that isn't just a straight memory access. */ - if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) { - CPUIOTLBEntry *iotlbentry; - bool need_swap; - - /* For anything that is unaligned, recurse through byte stores. */ - if ((addr & (size - 1)) != 0) { - goto do_unaligned_access; + if (this_size == 8) { + return 0; } - iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index]; + val_le >>= this_size * 8; + addr += this_size; + mr_offset += this_size; + size -= this_size; + } while (size); - /* Handle watchpoints. */ - if (unlikely(tlb_addr & TLB_WATCHPOINT)) { - /* On watchpoint hit, this will longjmp out. */ - cpu_check_watchpoint(env_cpu(env), addr, size, - iotlbentry->attrs, BP_MEM_WRITE, retaddr); - } - - need_swap = size > 1 && (tlb_addr & TLB_BSWAP); - - /* Handle I/O access. */ - if (tlb_addr & TLB_MMIO) { - io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, - op ^ (need_swap * MO_BSWAP)); - return; - } - - /* Ignore writes to ROM. */ - if (unlikely(tlb_addr & TLB_DISCARD_WRITE)) { - return; - } - - /* Handle clean RAM pages. */ - if (tlb_addr & TLB_NOTDIRTY) { - notdirty_write(env_cpu(env), addr, size, iotlbentry, retaddr); - } - - haddr = (void *)((uintptr_t)addr + entry->addend); - - /* - * Keep these two store_memop separate to ensure that the compiler - * is able to fold the entire function to a single instruction. - * There is a build-time assert inside to remind you of this. ;-) - */ - if (unlikely(need_swap)) { - store_memop(haddr, val, op ^ MO_BSWAP); - } else { - store_memop(haddr, val, op); - } - return; - } - - /* Handle slow unaligned access (it spans two pages or IO). */ - if (size > 1 - && unlikely((addr & ~TARGET_PAGE_MASK) + size - 1 - >= TARGET_PAGE_SIZE)) { - do_unaligned_access: - store_helper_unaligned(env, addr, val, retaddr, size, - mmu_idx, memop_big_endian(op)); - return; - } - - haddr = (void *)((uintptr_t)addr + entry->addend); - store_memop(haddr, val, op); + return val_le; } -static void __attribute__((noinline)) -full_stb_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr) +static uint64_t do_st_mmio_leN(CPUState *cpu, CPUTLBEntryFull *full, + uint64_t val_le, vaddr addr, int size, + int mmu_idx, uintptr_t ra) { - validate_memop(oi, MO_UB); - store_helper(env, addr, val, oi, retaddr, MO_UB); + MemoryRegionSection *section; + hwaddr mr_offset; + MemoryRegion *mr; + MemTxAttrs attrs; + + tcg_debug_assert(size > 0 && size <= 8); + + attrs = full->attrs; + section = io_prepare(&mr_offset, cpu, full->xlat_section, attrs, addr, ra); + mr = section->mr; + + BQL_LOCK_GUARD(); + return int_st_mmio_leN(cpu, full, val_le, addr, size, mmu_idx, + ra, mr, mr_offset); } -void helper_ret_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val, - MemOpIdx oi, uintptr_t retaddr) +static uint64_t do_st16_mmio_leN(CPUState *cpu, CPUTLBEntryFull *full, + Int128 val_le, vaddr addr, int size, + int mmu_idx, uintptr_t ra) { - full_stb_mmu(env, addr, val, oi, retaddr); -} + MemoryRegionSection *section; + MemoryRegion *mr; + hwaddr mr_offset; + MemTxAttrs attrs; -static void full_le_stw_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr) -{ - validate_memop(oi, MO_LEUW); - store_helper(env, addr, val, oi, retaddr, MO_LEUW); -} + tcg_debug_assert(size > 8 && size <= 16); -void helper_le_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val, - MemOpIdx oi, uintptr_t retaddr) -{ - full_le_stw_mmu(env, addr, val, oi, retaddr); -} + attrs = full->attrs; + section = io_prepare(&mr_offset, cpu, full->xlat_section, attrs, addr, ra); + mr = section->mr; -static void full_be_stw_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr) -{ - validate_memop(oi, MO_BEUW); - store_helper(env, addr, val, oi, retaddr, MO_BEUW); -} - -void helper_be_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val, - MemOpIdx oi, uintptr_t retaddr) -{ - full_be_stw_mmu(env, addr, val, oi, retaddr); -} - -static void full_le_stl_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr) -{ - validate_memop(oi, MO_LEUL); - store_helper(env, addr, val, oi, retaddr, MO_LEUL); -} - -void helper_le_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, - MemOpIdx oi, uintptr_t retaddr) -{ - full_le_stl_mmu(env, addr, val, oi, retaddr); -} - -static void full_be_stl_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr) -{ - validate_memop(oi, MO_BEUL); - store_helper(env, addr, val, oi, retaddr, MO_BEUL); -} - -void helper_be_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, - MemOpIdx oi, uintptr_t retaddr) -{ - full_be_stl_mmu(env, addr, val, oi, retaddr); -} - -void helper_le_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr) -{ - validate_memop(oi, MO_LEUQ); - store_helper(env, addr, val, oi, retaddr, MO_LEUQ); -} - -void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr) -{ - validate_memop(oi, MO_BEUQ); - store_helper(env, addr, val, oi, retaddr, MO_BEUQ); + BQL_LOCK_GUARD(); + int_st_mmio_leN(cpu, full, int128_getlo(val_le), addr, 8, + mmu_idx, ra, mr, mr_offset); + return int_st_mmio_leN(cpu, full, int128_gethi(val_le), addr + 8, + size - 8, mmu_idx, ra, mr, mr_offset + 8); } /* - * Store Helpers for cpu_ldst.h + * Wrapper for the above. */ - -typedef void FullStoreHelper(CPUArchState *env, target_ulong addr, - uint64_t val, MemOpIdx oi, uintptr_t retaddr); - -static inline void cpu_store_helper(CPUArchState *env, target_ulong addr, - uint64_t val, MemOpIdx oi, uintptr_t ra, - FullStoreHelper *full_store) +static uint64_t do_st_leN(CPUState *cpu, MMULookupPageData *p, + uint64_t val_le, int mmu_idx, + MemOp mop, uintptr_t ra) { - full_store(env, addr, val, oi, ra); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); + MemOp atom; + unsigned tmp, half_size; + + if (unlikely(p->flags & TLB_MMIO)) { + return do_st_mmio_leN(cpu, p->full, val_le, p->addr, + p->size, mmu_idx, ra); + } else if (unlikely(p->flags & TLB_DISCARD_WRITE)) { + return val_le >> (p->size * 8); + } + + /* + * It is a given that we cross a page and therefore there is no atomicity + * for the store as a whole, but subobjects may need attention. + */ + atom = mop & MO_ATOM_MASK; + switch (atom) { + case MO_ATOM_SUBALIGN: + return store_parts_leN(p->haddr, p->size, val_le); + + case MO_ATOM_IFALIGN_PAIR: + case MO_ATOM_WITHIN16_PAIR: + tmp = mop & MO_SIZE; + tmp = tmp ? tmp - 1 : 0; + half_size = 1 << tmp; + if (atom == MO_ATOM_IFALIGN_PAIR + ? p->size == half_size + : p->size >= half_size) { + if (!HAVE_al8_fast && p->size <= 4) { + return store_whole_le4(p->haddr, p->size, val_le); + } else if (HAVE_al8) { + return store_whole_le8(p->haddr, p->size, val_le); + } else { + cpu_loop_exit_atomic(cpu, ra); + } + } + /* fall through */ + + case MO_ATOM_IFALIGN: + case MO_ATOM_WITHIN16: + case MO_ATOM_NONE: + return store_bytes_leN(p->haddr, p->size, val_le); + + default: + g_assert_not_reached(); + } } -void cpu_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val, - MemOpIdx oi, uintptr_t retaddr) +/* + * Wrapper for the above, for 8 < size < 16. + */ +static uint64_t do_st16_leN(CPUState *cpu, MMULookupPageData *p, + Int128 val_le, int mmu_idx, + MemOp mop, uintptr_t ra) { - cpu_store_helper(env, addr, val, oi, retaddr, full_stb_mmu); + int size = p->size; + MemOp atom; + + if (unlikely(p->flags & TLB_MMIO)) { + return do_st16_mmio_leN(cpu, p->full, val_le, p->addr, + size, mmu_idx, ra); + } else if (unlikely(p->flags & TLB_DISCARD_WRITE)) { + return int128_gethi(val_le) >> ((size - 8) * 8); + } + + /* + * It is a given that we cross a page and therefore there is no atomicity + * for the store as a whole, but subobjects may need attention. + */ + atom = mop & MO_ATOM_MASK; + switch (atom) { + case MO_ATOM_SUBALIGN: + store_parts_leN(p->haddr, 8, int128_getlo(val_le)); + return store_parts_leN(p->haddr + 8, p->size - 8, + int128_gethi(val_le)); + + case MO_ATOM_WITHIN16_PAIR: + /* Since size > 8, this is the half that must be atomic. */ + if (!HAVE_CMPXCHG128) { + cpu_loop_exit_atomic(cpu, ra); + } + return store_whole_le16(p->haddr, p->size, val_le); + + case MO_ATOM_IFALIGN_PAIR: + /* + * Since size > 8, both halves are misaligned, + * and so neither is atomic. + */ + case MO_ATOM_IFALIGN: + case MO_ATOM_WITHIN16: + case MO_ATOM_NONE: + stq_le_p(p->haddr, int128_getlo(val_le)); + return store_bytes_leN(p->haddr + 8, p->size - 8, + int128_gethi(val_le)); + + default: + g_assert_not_reached(); + } } -void cpu_stw_be_mmu(CPUArchState *env, target_ulong addr, uint16_t val, - MemOpIdx oi, uintptr_t retaddr) +static void do_st_1(CPUState *cpu, MMULookupPageData *p, uint8_t val, + int mmu_idx, uintptr_t ra) { - cpu_store_helper(env, addr, val, oi, retaddr, full_be_stw_mmu); + if (unlikely(p->flags & TLB_MMIO)) { + do_st_mmio_leN(cpu, p->full, val, p->addr, 1, mmu_idx, ra); + } else if (unlikely(p->flags & TLB_DISCARD_WRITE)) { + /* nothing */ + } else { + *(uint8_t *)p->haddr = val; + } } -void cpu_stl_be_mmu(CPUArchState *env, target_ulong addr, uint32_t val, - MemOpIdx oi, uintptr_t retaddr) +static void do_st_2(CPUState *cpu, MMULookupPageData *p, uint16_t val, + int mmu_idx, MemOp memop, uintptr_t ra) { - cpu_store_helper(env, addr, val, oi, retaddr, full_be_stl_mmu); + if (unlikely(p->flags & TLB_MMIO)) { + if ((memop & MO_BSWAP) != MO_LE) { + val = bswap16(val); + } + do_st_mmio_leN(cpu, p->full, val, p->addr, 2, mmu_idx, ra); + } else if (unlikely(p->flags & TLB_DISCARD_WRITE)) { + /* nothing */ + } else { + /* Swap to host endian if necessary, then store. */ + if (memop & MO_BSWAP) { + val = bswap16(val); + } + store_atom_2(cpu, ra, p->haddr, memop, val); + } } -void cpu_stq_be_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr) +static void do_st_4(CPUState *cpu, MMULookupPageData *p, uint32_t val, + int mmu_idx, MemOp memop, uintptr_t ra) { - cpu_store_helper(env, addr, val, oi, retaddr, helper_be_stq_mmu); + if (unlikely(p->flags & TLB_MMIO)) { + if ((memop & MO_BSWAP) != MO_LE) { + val = bswap32(val); + } + do_st_mmio_leN(cpu, p->full, val, p->addr, 4, mmu_idx, ra); + } else if (unlikely(p->flags & TLB_DISCARD_WRITE)) { + /* nothing */ + } else { + /* Swap to host endian if necessary, then store. */ + if (memop & MO_BSWAP) { + val = bswap32(val); + } + store_atom_4(cpu, ra, p->haddr, memop, val); + } } -void cpu_stw_le_mmu(CPUArchState *env, target_ulong addr, uint16_t val, - MemOpIdx oi, uintptr_t retaddr) +static void do_st_8(CPUState *cpu, MMULookupPageData *p, uint64_t val, + int mmu_idx, MemOp memop, uintptr_t ra) { - cpu_store_helper(env, addr, val, oi, retaddr, full_le_stw_mmu); + if (unlikely(p->flags & TLB_MMIO)) { + if ((memop & MO_BSWAP) != MO_LE) { + val = bswap64(val); + } + do_st_mmio_leN(cpu, p->full, val, p->addr, 8, mmu_idx, ra); + } else if (unlikely(p->flags & TLB_DISCARD_WRITE)) { + /* nothing */ + } else { + /* Swap to host endian if necessary, then store. */ + if (memop & MO_BSWAP) { + val = bswap64(val); + } + store_atom_8(cpu, ra, p->haddr, memop, val); + } } -void cpu_stl_le_mmu(CPUArchState *env, target_ulong addr, uint32_t val, - MemOpIdx oi, uintptr_t retaddr) +static void do_st1_mmu(CPUState *cpu, vaddr addr, uint8_t val, + MemOpIdx oi, uintptr_t ra) { - cpu_store_helper(env, addr, val, oi, retaddr, full_le_stl_mmu); + MMULookupLocals l; + bool crosspage; + + cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l); + tcg_debug_assert(!crosspage); + + do_st_1(cpu, &l.page[0], val, l.mmu_idx, ra); } -void cpu_stq_le_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr) +static void do_st2_mmu(CPUState *cpu, vaddr addr, uint16_t val, + MemOpIdx oi, uintptr_t ra) { - cpu_store_helper(env, addr, val, oi, retaddr, helper_le_stq_mmu); + MMULookupLocals l; + bool crosspage; + uint8_t a, b; + + cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l); + if (likely(!crosspage)) { + do_st_2(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra); + return; + } + + if ((l.memop & MO_BSWAP) == MO_LE) { + a = val, b = val >> 8; + } else { + b = val, a = val >> 8; + } + do_st_1(cpu, &l.page[0], a, l.mmu_idx, ra); + do_st_1(cpu, &l.page[1], b, l.mmu_idx, ra); +} + +static void do_st4_mmu(CPUState *cpu, vaddr addr, uint32_t val, + MemOpIdx oi, uintptr_t ra) +{ + MMULookupLocals l; + bool crosspage; + + cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l); + if (likely(!crosspage)) { + do_st_4(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra); + return; + } + + /* Swap to little endian for simplicity, then store by bytes. */ + if ((l.memop & MO_BSWAP) != MO_LE) { + val = bswap32(val); + } + val = do_st_leN(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra); + (void) do_st_leN(cpu, &l.page[1], val, l.mmu_idx, l.memop, ra); +} + +static void do_st8_mmu(CPUState *cpu, vaddr addr, uint64_t val, + MemOpIdx oi, uintptr_t ra) +{ + MMULookupLocals l; + bool crosspage; + + cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l); + if (likely(!crosspage)) { + do_st_8(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra); + return; + } + + /* Swap to little endian for simplicity, then store by bytes. */ + if ((l.memop & MO_BSWAP) != MO_LE) { + val = bswap64(val); + } + val = do_st_leN(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra); + (void) do_st_leN(cpu, &l.page[1], val, l.mmu_idx, l.memop, ra); +} + +static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val, + MemOpIdx oi, uintptr_t ra) +{ + MMULookupLocals l; + bool crosspage; + uint64_t a, b; + int first; + + cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l); + if (likely(!crosspage)) { + if (unlikely(l.page[0].flags & TLB_MMIO)) { + if ((l.memop & MO_BSWAP) != MO_LE) { + val = bswap128(val); + } + do_st16_mmio_leN(cpu, l.page[0].full, val, addr, 16, l.mmu_idx, ra); + } else if (unlikely(l.page[0].flags & TLB_DISCARD_WRITE)) { + /* nothing */ + } else { + /* Swap to host endian if necessary, then store. */ + if (l.memop & MO_BSWAP) { + val = bswap128(val); + } + store_atom_16(cpu, ra, l.page[0].haddr, l.memop, val); + } + return; + } + + first = l.page[0].size; + if (first == 8) { + MemOp mop8 = (l.memop & ~(MO_SIZE | MO_BSWAP)) | MO_64; + + if (l.memop & MO_BSWAP) { + val = bswap128(val); + } + if (HOST_BIG_ENDIAN) { + b = int128_getlo(val), a = int128_gethi(val); + } else { + a = int128_getlo(val), b = int128_gethi(val); + } + do_st_8(cpu, &l.page[0], a, l.mmu_idx, mop8, ra); + do_st_8(cpu, &l.page[1], b, l.mmu_idx, mop8, ra); + return; + } + + if ((l.memop & MO_BSWAP) != MO_LE) { + val = bswap128(val); + } + if (first < 8) { + do_st_leN(cpu, &l.page[0], int128_getlo(val), l.mmu_idx, l.memop, ra); + val = int128_urshift(val, first * 8); + do_st16_leN(cpu, &l.page[1], val, l.mmu_idx, l.memop, ra); + } else { + b = do_st16_leN(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra); + do_st_leN(cpu, &l.page[1], b, l.mmu_idx, l.memop, ra); + } } #include "ldst_common.c.inc" @@ -2583,57 +2957,61 @@ void cpu_stq_le_mmu(CPUArchState *env, target_ulong addr, uint64_t val, #include "atomic_template.h" #endif -#if HAVE_CMPXCHG128 || HAVE_ATOMIC128 +#if defined(CONFIG_ATOMIC128) || HAVE_CMPXCHG128 #define DATA_SIZE 16 #include "atomic_template.h" #endif /* Code access functions. */ -static uint64_t full_ldub_code(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) -{ - return load_helper(env, addr, oi, retaddr, MO_8, true, full_ldub_code); -} - uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr) { - MemOpIdx oi = make_memop_idx(MO_UB, cpu_mmu_index(env, true)); - return full_ldub_code(env, addr, oi, 0); -} - -static uint64_t full_lduw_code(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) -{ - return load_helper(env, addr, oi, retaddr, MO_TEUW, true, full_lduw_code); + CPUState *cs = env_cpu(env); + MemOpIdx oi = make_memop_idx(MO_UB, cpu_mmu_index(cs, true)); + return do_ld1_mmu(cs, addr, oi, 0, MMU_INST_FETCH); } uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr) { - MemOpIdx oi = make_memop_idx(MO_TEUW, cpu_mmu_index(env, true)); - return full_lduw_code(env, addr, oi, 0); -} - -static uint64_t full_ldl_code(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) -{ - return load_helper(env, addr, oi, retaddr, MO_TEUL, true, full_ldl_code); + CPUState *cs = env_cpu(env); + MemOpIdx oi = make_memop_idx(MO_TEUW, cpu_mmu_index(cs, true)); + return do_ld2_mmu(cs, addr, oi, 0, MMU_INST_FETCH); } uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr) { - MemOpIdx oi = make_memop_idx(MO_TEUL, cpu_mmu_index(env, true)); - return full_ldl_code(env, addr, oi, 0); -} - -static uint64_t full_ldq_code(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr) -{ - return load_helper(env, addr, oi, retaddr, MO_TEUQ, true, full_ldq_code); + CPUState *cs = env_cpu(env); + MemOpIdx oi = make_memop_idx(MO_TEUL, cpu_mmu_index(cs, true)); + return do_ld4_mmu(cs, addr, oi, 0, MMU_INST_FETCH); } uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr) { - MemOpIdx oi = make_memop_idx(MO_TEUQ, cpu_mmu_index(env, true)); - return full_ldq_code(env, addr, oi, 0); + CPUState *cs = env_cpu(env); + MemOpIdx oi = make_memop_idx(MO_TEUQ, cpu_mmu_index(cs, true)); + return do_ld8_mmu(cs, addr, oi, 0, MMU_INST_FETCH); +} + +uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t retaddr) +{ + return do_ld1_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH); +} + +uint16_t cpu_ldw_code_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t retaddr) +{ + return do_ld2_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH); +} + +uint32_t cpu_ldl_code_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t retaddr) +{ + return do_ld4_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH); +} + +uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t retaddr) +{ + return do_ld8_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH); } diff --git a/accel/tcg/hmp.c b/accel/tcg/hmp.c deleted file mode 100644 index bb67941420..0000000000 --- a/accel/tcg/hmp.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qapi/error.h" -#include "qapi/qapi-commands-machine.h" -#include "exec/exec-all.h" -#include "monitor/monitor.h" - -static void hmp_tcg_register(void) -{ - monitor_register_hmp_info_hrt("jit", qmp_x_query_jit); - monitor_register_hmp_info_hrt("opcount", qmp_x_query_opcount); -} - -type_init(hmp_tcg_register); diff --git a/softmmu/icount.c b/accel/tcg/icount-common.c similarity index 93% rename from softmmu/icount.c rename to accel/tcg/icount-common.c index 4504433e16..a4a747d1dc 100644 --- a/softmmu/icount.c +++ b/accel/tcg/icount-common.c @@ -27,7 +27,6 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "exec/exec-all.h" #include "sysemu/cpus.h" #include "sysemu/qtest.h" #include "qemu/main-loop.h" @@ -38,7 +37,7 @@ #include "hw/core/cpu.h" #include "sysemu/cpu-timers.h" #include "sysemu/cpu-throttle.h" -#include "timers-state.h" +#include "sysemu/cpu-timers-internal.h" /* * ICOUNT: Instruction Counter @@ -50,21 +49,19 @@ static bool icount_sleep = true; /* Arbitrarily pick 1MIPS as the minimum allowable speed. */ #define MAX_ICOUNT_SHIFT 10 -/* - * 0 = Do not count executed instructions. - * 1 = Fixed conversion of insn to ns via "shift" option - * 2 = Runtime adaptive algorithm to compute shift - */ -int use_icount; +/* Do not count executed instructions */ +ICountMode use_icount = ICOUNT_DISABLED; static void icount_enable_precise(void) { - use_icount = 1; + /* Fixed conversion of insn to ns via "shift" option */ + use_icount = ICOUNT_PRECISE; } static void icount_enable_adaptive(void) { - use_icount = 2; + /* Runtime adaptive algorithm to compute shift */ + use_icount = ICOUNT_ADAPTATIVE; } /* @@ -75,7 +72,7 @@ static void icount_enable_adaptive(void) static int64_t icount_get_executed(CPUState *cpu) { return (cpu->icount_budget - - (cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra)); + (cpu->neg.icount_decr.u16.low + cpu->icount_extra)); } /* @@ -111,7 +108,7 @@ static int64_t icount_get_raw_locked(void) CPUState *cpu = current_cpu; if (cpu && cpu->running) { - if (!cpu->can_do_io) { + if (!cpu->neg.can_do_io) { error_report("Bad icount read"); exit(1); } @@ -257,13 +254,18 @@ static void icount_warp_rt(void) int64_t warp_delta; warp_delta = clock - timers_state.vm_clock_warp_start; - if (icount_enabled() == 2) { + if (icount_enabled() == ICOUNT_ADAPTATIVE) { /* - * In adaptive mode, do not let QEMU_CLOCK_VIRTUAL run too - * far ahead of real time. + * In adaptive mode, do not let QEMU_CLOCK_VIRTUAL run too far + * ahead of real time (it might already be ahead so careful not + * to go backwards). */ int64_t cur_icount = icount_get_locked(); int64_t delta = clock - cur_icount; + + if (delta < 0) { + delta = 0; + } warp_delta = MIN(warp_delta, delta); } qatomic_set_i64(&timers_state.qemu_icount_bias, @@ -320,7 +322,7 @@ void icount_start_warp_timer(void) * vCPU is sleeping and warp can't be started. * It is probably a race condition: notification sent * to vCPU was processed in advance and vCPU went to sleep. - * Therefore we have to wake it up for doing someting. + * Therefore we have to wake it up for doing something. */ if (replay_has_event()) { qemu_clock_notify(QEMU_CLOCK_VIRTUAL); @@ -415,7 +417,7 @@ void icount_account_warp_timer(void) icount_warp_rt(); } -void icount_configure(QemuOpts *opts, Error **errp) +bool icount_configure(QemuOpts *opts, Error **errp) { const char *option = qemu_opt_get(opts, "shift"); bool sleep = qemu_opt_get_bool(opts, "sleep", true); @@ -425,27 +427,28 @@ void icount_configure(QemuOpts *opts, Error **errp) if (!option) { if (qemu_opt_get(opts, "align") != NULL) { error_setg(errp, "Please specify shift option when using align"); + return false; } - return; + return true; } if (align && !sleep) { error_setg(errp, "align=on and sleep=off are incompatible"); - return; + return false; } if (strcmp(option, "auto") != 0) { if (qemu_strtol(option, NULL, 0, &time_shift) < 0 || time_shift < 0 || time_shift > MAX_ICOUNT_SHIFT) { error_setg(errp, "icount: Invalid shift value"); - return; + return false; } } else if (icount_align_option) { error_setg(errp, "shift=auto and align=on are incompatible"); - return; + return false; } else if (!icount_sleep) { error_setg(errp, "shift=auto and sleep=off are incompatible"); - return; + return false; } icount_sleep = sleep; @@ -459,7 +462,7 @@ void icount_configure(QemuOpts *opts, Error **errp) if (time_shift >= 0) { timers_state.icount_time_shift = time_shift; icount_enable_precise(); - return; + return true; } icount_enable_adaptive(); @@ -487,11 +490,14 @@ void icount_configure(QemuOpts *opts, Error **errp) timer_mod(timers_state.icount_vm_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10); + return true; } void icount_notify_exit(void) { - if (icount_enabled() && current_cpu) { + assert(icount_enabled()); + + if (current_cpu) { qemu_cpu_kick(current_cpu); qemu_clock_notify(QEMU_CLOCK_VIRTUAL); } diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h new file mode 100644 index 0000000000..edefd0dcb7 --- /dev/null +++ b/accel/tcg/internal-common.h @@ -0,0 +1,26 @@ +/* + * Internal execution defines for qemu (target agnostic) + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_INTERNAL_COMMON_H +#define ACCEL_TCG_INTERNAL_COMMON_H + +#include "exec/translation-block.h" + +extern int64_t max_delay; +extern int64_t max_advance; + +/* + * Return true if CS is not running in parallel with other cpus, either + * because there are no other cpus or we are within an exclusive context. + */ +static inline bool cpu_in_serial_context(CPUState *cs) +{ + return !(cs->tcg_cflags & CF_PARALLEL) || cpu_in_exclusive_context(cs); +} + +#endif diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h new file mode 100644 index 0000000000..4e36cf858e --- /dev/null +++ b/accel/tcg/internal-target.h @@ -0,0 +1,132 @@ +/* + * Internal execution defines for qemu (target specific) + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_INTERNAL_TARGET_H +#define ACCEL_TCG_INTERNAL_TARGET_H + +#include "exec/exec-all.h" +#include "exec/translate-all.h" + +/* + * Access to the various translations structures need to be serialised + * via locks for consistency. In user-mode emulation access to the + * memory related structures are protected with mmap_lock. + * In !user-mode we use per-page locks. + */ +#ifdef CONFIG_USER_ONLY +#define assert_memory_lock() tcg_debug_assert(have_mmap_lock()) +#else +#define assert_memory_lock() +#endif + +#if defined(CONFIG_SOFTMMU) && defined(CONFIG_DEBUG_TCG) +void assert_no_pages_locked(void); +#else +static inline void assert_no_pages_locked(void) { } +#endif + +#ifdef CONFIG_USER_ONLY +static inline void page_table_config_init(void) { } +#else +void page_table_config_init(void); +#endif + +#ifdef CONFIG_USER_ONLY +/* + * For user-only, page_protect sets the page read-only. + * Since most execution is already on read-only pages, and we'd need to + * account for other TBs on the same page, defer undoing any page protection + * until we receive the write fault. + */ +static inline void tb_lock_page0(tb_page_addr_t p0) +{ + page_protect(p0); +} + +static inline void tb_lock_page1(tb_page_addr_t p0, tb_page_addr_t p1) +{ + page_protect(p1); +} + +static inline void tb_unlock_page1(tb_page_addr_t p0, tb_page_addr_t p1) { } +static inline void tb_unlock_pages(TranslationBlock *tb) { } +#else +void tb_lock_page0(tb_page_addr_t); +void tb_lock_page1(tb_page_addr_t, tb_page_addr_t); +void tb_unlock_page1(tb_page_addr_t, tb_page_addr_t); +void tb_unlock_pages(TranslationBlock *); +#endif + +#ifdef CONFIG_SOFTMMU +void tb_invalidate_phys_range_fast(ram_addr_t ram_addr, + unsigned size, + uintptr_t retaddr); +G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); +#endif /* CONFIG_SOFTMMU */ + +TranslationBlock *tb_gen_code(CPUState *cpu, vaddr pc, + uint64_t cs_base, uint32_t flags, + int cflags); +void page_init(void); +void tb_htable_init(void); +void tb_reset_jump(TranslationBlock *tb, int n); +TranslationBlock *tb_link_page(TranslationBlock *tb); +bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc); +void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, + uintptr_t host_pc); + +bool tcg_exec_realizefn(CPUState *cpu, Error **errp); +void tcg_exec_unrealizefn(CPUState *cpu); + +/* Return the current PC from CPU, which may be cached in TB. */ +static inline vaddr log_pc(CPUState *cpu, const TranslationBlock *tb) +{ + if (tb_cflags(tb) & CF_PCREL) { + return cpu->cc->get_pc(cpu); + } else { + return tb->pc; + } +} + +extern bool one_insn_per_tb; + +/** + * tcg_req_mo: + * @type: TCGBar + * + * Filter @type to the barrier that is required for the guest + * memory ordering vs the host memory ordering. A non-zero + * result indicates that some barrier is required. + * + * If TCG_GUEST_DEFAULT_MO is not defined, assume that the + * guest requires strict ordering. + * + * This is a macro so that it's constant even without optimization. + */ +#ifdef TCG_GUEST_DEFAULT_MO +# define tcg_req_mo(type) \ + ((type) & TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO) +#else +# define tcg_req_mo(type) ((type) & ~TCG_TARGET_DEFAULT_MO) +#endif + +/** + * cpu_req_mo: + * @type: TCGBar + * + * If tcg_req_mo indicates a barrier for @type is required + * for the guest memory model, issue a host memory barrier. + */ +#define cpu_req_mo(type) \ + do { \ + if (tcg_req_mo(type)) { \ + smp_mb(); \ + } \ + } while (0) + +#endif /* ACCEL_TCG_INTERNAL_H */ diff --git a/accel/tcg/internal.h b/accel/tcg/internal.h deleted file mode 100644 index 3092bfa964..0000000000 --- a/accel/tcg/internal.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Internal execution defines for qemu - * - * Copyright (c) 2003 Fabrice Bellard - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef ACCEL_TCG_INTERNAL_H -#define ACCEL_TCG_INTERNAL_H - -#include "exec/exec-all.h" - -TranslationBlock *tb_gen_code(CPUState *cpu, target_ulong pc, - target_ulong cs_base, uint32_t flags, - int cflags); -G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); -void page_init(void); -void tb_htable_init(void); - -#endif /* ACCEL_TCG_INTERNAL_H */ diff --git a/accel/tcg/ldst_atomicity.c.inc b/accel/tcg/ldst_atomicity.c.inc new file mode 100644 index 0000000000..97dae70d53 --- /dev/null +++ b/accel/tcg/ldst_atomicity.c.inc @@ -0,0 +1,1111 @@ +/* + * Routines common to user and system emulation of load/store. + * + * Copyright (c) 2022 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "host/load-extract-al16-al8.h" +#include "host/store-insert-al16.h" + +#ifdef CONFIG_ATOMIC64 +# define HAVE_al8 true +#else +# define HAVE_al8 false +#endif +#define HAVE_al8_fast (ATOMIC_REG_SIZE >= 8) + +/** + * required_atomicity: + * + * Return the lg2 bytes of atomicity required by @memop for @p. + * If the operation must be split into two operations to be + * examined separately for atomicity, return -lg2. + */ +static int required_atomicity(CPUState *cpu, uintptr_t p, MemOp memop) +{ + MemOp atom = memop & MO_ATOM_MASK; + MemOp size = memop & MO_SIZE; + MemOp half = size ? size - 1 : 0; + unsigned tmp; + int atmax; + + switch (atom) { + case MO_ATOM_NONE: + atmax = MO_8; + break; + + case MO_ATOM_IFALIGN_PAIR: + size = half; + /* fall through */ + + case MO_ATOM_IFALIGN: + tmp = (1 << size) - 1; + atmax = p & tmp ? MO_8 : size; + break; + + case MO_ATOM_WITHIN16: + tmp = p & 15; + atmax = (tmp + (1 << size) <= 16 ? size : MO_8); + break; + + case MO_ATOM_WITHIN16_PAIR: + tmp = p & 15; + if (tmp + (1 << size) <= 16) { + atmax = size; + } else if (tmp + (1 << half) == 16) { + /* + * The pair exactly straddles the boundary. + * Both halves are naturally aligned and atomic. + */ + atmax = half; + } else { + /* + * One of the pair crosses the boundary, and is non-atomic. + * The other of the pair does not cross, and is atomic. + */ + atmax = -half; + } + break; + + case MO_ATOM_SUBALIGN: + /* + * Examine the alignment of p to determine if there are subobjects + * that must be aligned. Note that we only really need ctz4() -- + * any more significant bits are discarded by the immediately + * following comparison. + */ + tmp = ctz32(p); + atmax = MIN(size, tmp); + break; + + default: + g_assert_not_reached(); + } + + /* + * Here we have the architectural atomicity of the operation. + * However, when executing in a serial context, we need no extra + * host atomicity in order to avoid racing. This reduction + * avoids looping with cpu_loop_exit_atomic. + */ + if (cpu_in_serial_context(cpu)) { + return MO_8; + } + return atmax; +} + +/** + * load_atomic2: + * @pv: host address + * + * Atomically load 2 aligned bytes from @pv. + */ +static inline uint16_t load_atomic2(void *pv) +{ + uint16_t *p = __builtin_assume_aligned(pv, 2); + return qatomic_read(p); +} + +/** + * load_atomic4: + * @pv: host address + * + * Atomically load 4 aligned bytes from @pv. + */ +static inline uint32_t load_atomic4(void *pv) +{ + uint32_t *p = __builtin_assume_aligned(pv, 4); + return qatomic_read(p); +} + +/** + * load_atomic8: + * @pv: host address + * + * Atomically load 8 aligned bytes from @pv. + */ +static inline uint64_t load_atomic8(void *pv) +{ + uint64_t *p = __builtin_assume_aligned(pv, 8); + + qemu_build_assert(HAVE_al8); + return qatomic_read__nocheck(p); +} + +/** + * load_atomic8_or_exit: + * @cpu: generic cpu state + * @ra: host unwind address + * @pv: host address + * + * Atomically load 8 aligned bytes from @pv. + * If this is not possible, longjmp out to restart serially. + */ +static uint64_t load_atomic8_or_exit(CPUState *cpu, uintptr_t ra, void *pv) +{ + if (HAVE_al8) { + return load_atomic8(pv); + } + +#ifdef CONFIG_USER_ONLY + /* + * If the page is not writable, then assume the value is immutable + * and requires no locking. This ignores the case of MAP_SHARED with + * another process, because the fallback start_exclusive solution + * provides no protection across processes. + */ + WITH_MMAP_LOCK_GUARD() { + if (!page_check_range(h2g(pv), 8, PAGE_WRITE_ORG)) { + uint64_t *p = __builtin_assume_aligned(pv, 8); + return *p; + } + } +#endif + + /* Ultimate fallback: re-execute in serial context. */ + cpu_loop_exit_atomic(cpu, ra); +} + +/** + * load_atomic16_or_exit: + * @cpu: generic cpu state + * @ra: host unwind address + * @pv: host address + * + * Atomically load 16 aligned bytes from @pv. + * If this is not possible, longjmp out to restart serially. + */ +static Int128 load_atomic16_or_exit(CPUState *cpu, uintptr_t ra, void *pv) +{ + Int128 *p = __builtin_assume_aligned(pv, 16); + + if (HAVE_ATOMIC128_RO) { + return atomic16_read_ro(p); + } + + /* + * We can only use cmpxchg to emulate a load if the page is writable. + * If the page is not writable, then assume the value is immutable + * and requires no locking. This ignores the case of MAP_SHARED with + * another process, because the fallback start_exclusive solution + * provides no protection across processes. + * + * In system mode all guest pages are writable. For user mode, + * we must take mmap_lock so that the query remains valid until + * the write is complete -- tests/tcg/multiarch/munmap-pthread.c + * is an example that can race. + */ + WITH_MMAP_LOCK_GUARD() { +#ifdef CONFIG_USER_ONLY + if (!page_check_range(h2g(p), 16, PAGE_WRITE_ORG)) { + return *p; + } +#endif + if (HAVE_ATOMIC128_RW) { + return atomic16_read_rw(p); + } + } + + /* Ultimate fallback: re-execute in serial context. */ + cpu_loop_exit_atomic(cpu, ra); +} + +/** + * load_atom_extract_al4x2: + * @pv: host address + * + * Load 4 bytes from @p, from two sequential atomic 4-byte loads. + */ +static uint32_t load_atom_extract_al4x2(void *pv) +{ + uintptr_t pi = (uintptr_t)pv; + int sh = (pi & 3) * 8; + uint32_t a, b; + + pv = (void *)(pi & ~3); + a = load_atomic4(pv); + b = load_atomic4(pv + 4); + + if (HOST_BIG_ENDIAN) { + return (a << sh) | (b >> (-sh & 31)); + } else { + return (a >> sh) | (b << (-sh & 31)); + } +} + +/** + * load_atom_extract_al8x2: + * @pv: host address + * + * Load 8 bytes from @p, from two sequential atomic 8-byte loads. + */ +static uint64_t load_atom_extract_al8x2(void *pv) +{ + uintptr_t pi = (uintptr_t)pv; + int sh = (pi & 7) * 8; + uint64_t a, b; + + pv = (void *)(pi & ~7); + a = load_atomic8(pv); + b = load_atomic8(pv + 8); + + if (HOST_BIG_ENDIAN) { + return (a << sh) | (b >> (-sh & 63)); + } else { + return (a >> sh) | (b << (-sh & 63)); + } +} + +/** + * load_atom_extract_al8_or_exit: + * @cpu: generic cpu state + * @ra: host unwind address + * @pv: host address + * @s: object size in bytes, @s <= 4. + * + * Atomically load @s bytes from @p, when p % s != 0, and [p, p+s-1] does + * not cross an 8-byte boundary. This means that we can perform an atomic + * 8-byte load and extract. + * The value is returned in the low bits of a uint32_t. + */ +static uint32_t load_atom_extract_al8_or_exit(CPUState *cpu, uintptr_t ra, + void *pv, int s) +{ + uintptr_t pi = (uintptr_t)pv; + int o = pi & 7; + int shr = (HOST_BIG_ENDIAN ? 8 - s - o : o) * 8; + + pv = (void *)(pi & ~7); + return load_atomic8_or_exit(cpu, ra, pv) >> shr; +} + +/** + * load_atom_extract_al16_or_exit: + * @cpu: generic cpu state + * @ra: host unwind address + * @p: host address + * @s: object size in bytes, @s <= 8. + * + * Atomically load @s bytes from @p, when p % 16 < 8 + * and p % 16 + s > 8. I.e. does not cross a 16-byte + * boundary, but *does* cross an 8-byte boundary. + * This is the slow version, so we must have eliminated + * any faster load_atom_extract_al8_or_exit case. + * + * If this is not possible, longjmp out to restart serially. + */ +static uint64_t load_atom_extract_al16_or_exit(CPUState *cpu, uintptr_t ra, + void *pv, int s) +{ + uintptr_t pi = (uintptr_t)pv; + int o = pi & 7; + int shr = (HOST_BIG_ENDIAN ? 16 - s - o : o) * 8; + Int128 r; + + /* + * Note constraints above: p & 8 must be clear. + * Provoke SIGBUS if possible otherwise. + */ + pv = (void *)(pi & ~7); + r = load_atomic16_or_exit(cpu, ra, pv); + + r = int128_urshift(r, shr); + return int128_getlo(r); +} + +/** + * load_atom_4_by_2: + * @pv: host address + * + * Load 4 bytes from @pv, with two 2-byte atomic loads. + */ +static inline uint32_t load_atom_4_by_2(void *pv) +{ + uint32_t a = load_atomic2(pv); + uint32_t b = load_atomic2(pv + 2); + + if (HOST_BIG_ENDIAN) { + return (a << 16) | b; + } else { + return (b << 16) | a; + } +} + +/** + * load_atom_8_by_2: + * @pv: host address + * + * Load 8 bytes from @pv, with four 2-byte atomic loads. + */ +static inline uint64_t load_atom_8_by_2(void *pv) +{ + uint32_t a = load_atom_4_by_2(pv); + uint32_t b = load_atom_4_by_2(pv + 4); + + if (HOST_BIG_ENDIAN) { + return ((uint64_t)a << 32) | b; + } else { + return ((uint64_t)b << 32) | a; + } +} + +/** + * load_atom_8_by_4: + * @pv: host address + * + * Load 8 bytes from @pv, with two 4-byte atomic loads. + */ +static inline uint64_t load_atom_8_by_4(void *pv) +{ + uint32_t a = load_atomic4(pv); + uint32_t b = load_atomic4(pv + 4); + + if (HOST_BIG_ENDIAN) { + return ((uint64_t)a << 32) | b; + } else { + return ((uint64_t)b << 32) | a; + } +} + +/** + * load_atom_8_by_8_or_4: + * @pv: host address + * + * Load 8 bytes from aligned @pv, with at least 4-byte atomicity. + */ +static inline uint64_t load_atom_8_by_8_or_4(void *pv) +{ + if (HAVE_al8_fast) { + return load_atomic8(pv); + } else { + return load_atom_8_by_4(pv); + } +} + +/** + * load_atom_2: + * @p: host address + * @memop: the full memory op + * + * Load 2 bytes from @p, honoring the atomicity of @memop. + */ +static uint16_t load_atom_2(CPUState *cpu, uintptr_t ra, + void *pv, MemOp memop) +{ + uintptr_t pi = (uintptr_t)pv; + int atmax; + + if (likely((pi & 1) == 0)) { + return load_atomic2(pv); + } + if (HAVE_ATOMIC128_RO) { + intptr_t left_in_page = -(pi | TARGET_PAGE_MASK); + if (likely(left_in_page > 8)) { + return load_atom_extract_al16_or_al8(pv, 2); + } + } + + atmax = required_atomicity(cpu, pi, memop); + switch (atmax) { + case MO_8: + return lduw_he_p(pv); + case MO_16: + /* The only case remaining is MO_ATOM_WITHIN16. */ + if (!HAVE_al8_fast && (pi & 3) == 1) { + /* Big or little endian, we want the middle two bytes. */ + return load_atomic4(pv - 1) >> 8; + } + if ((pi & 15) != 7) { + return load_atom_extract_al8_or_exit(cpu, ra, pv, 2); + } + return load_atom_extract_al16_or_exit(cpu, ra, pv, 2); + default: + g_assert_not_reached(); + } +} + +/** + * load_atom_4: + * @p: host address + * @memop: the full memory op + * + * Load 4 bytes from @p, honoring the atomicity of @memop. + */ +static uint32_t load_atom_4(CPUState *cpu, uintptr_t ra, + void *pv, MemOp memop) +{ + uintptr_t pi = (uintptr_t)pv; + int atmax; + + if (likely((pi & 3) == 0)) { + return load_atomic4(pv); + } + if (HAVE_ATOMIC128_RO) { + intptr_t left_in_page = -(pi | TARGET_PAGE_MASK); + if (likely(left_in_page > 8)) { + return load_atom_extract_al16_or_al8(pv, 4); + } + } + + atmax = required_atomicity(cpu, pi, memop); + switch (atmax) { + case MO_8: + case MO_16: + case -MO_16: + /* + * For MO_ATOM_IFALIGN, this is more atomicity than required, + * but it's trivially supported on all hosts, better than 4 + * individual byte loads (when the host requires alignment), + * and overlaps with the MO_ATOM_SUBALIGN case of p % 2 == 0. + */ + return load_atom_extract_al4x2(pv); + case MO_32: + if (!(pi & 4)) { + return load_atom_extract_al8_or_exit(cpu, ra, pv, 4); + } + return load_atom_extract_al16_or_exit(cpu, ra, pv, 4); + default: + g_assert_not_reached(); + } +} + +/** + * load_atom_8: + * @p: host address + * @memop: the full memory op + * + * Load 8 bytes from @p, honoring the atomicity of @memop. + */ +static uint64_t load_atom_8(CPUState *cpu, uintptr_t ra, + void *pv, MemOp memop) +{ + uintptr_t pi = (uintptr_t)pv; + int atmax; + + /* + * If the host does not support 8-byte atomics, wait until we have + * examined the atomicity parameters below. + */ + if (HAVE_al8 && likely((pi & 7) == 0)) { + return load_atomic8(pv); + } + if (HAVE_ATOMIC128_RO) { + return load_atom_extract_al16_or_al8(pv, 8); + } + + atmax = required_atomicity(cpu, pi, memop); + if (atmax == MO_64) { + if (!HAVE_al8 && (pi & 7) == 0) { + load_atomic8_or_exit(cpu, ra, pv); + } + return load_atom_extract_al16_or_exit(cpu, ra, pv, 8); + } + if (HAVE_al8_fast) { + return load_atom_extract_al8x2(pv); + } + switch (atmax) { + case MO_8: + return ldq_he_p(pv); + case MO_16: + return load_atom_8_by_2(pv); + case MO_32: + return load_atom_8_by_4(pv); + case -MO_32: + if (HAVE_al8) { + return load_atom_extract_al8x2(pv); + } + cpu_loop_exit_atomic(cpu, ra); + default: + g_assert_not_reached(); + } +} + +/** + * load_atom_16: + * @p: host address + * @memop: the full memory op + * + * Load 16 bytes from @p, honoring the atomicity of @memop. + */ +static Int128 load_atom_16(CPUState *cpu, uintptr_t ra, + void *pv, MemOp memop) +{ + uintptr_t pi = (uintptr_t)pv; + int atmax; + Int128 r; + uint64_t a, b; + + /* + * If the host does not support 16-byte atomics, wait until we have + * examined the atomicity parameters below. + */ + if (HAVE_ATOMIC128_RO && likely((pi & 15) == 0)) { + return atomic16_read_ro(pv); + } + + atmax = required_atomicity(cpu, pi, memop); + switch (atmax) { + case MO_8: + memcpy(&r, pv, 16); + return r; + case MO_16: + a = load_atom_8_by_2(pv); + b = load_atom_8_by_2(pv + 8); + break; + case MO_32: + a = load_atom_8_by_4(pv); + b = load_atom_8_by_4(pv + 8); + break; + case MO_64: + if (!HAVE_al8) { + cpu_loop_exit_atomic(cpu, ra); + } + a = load_atomic8(pv); + b = load_atomic8(pv + 8); + break; + case -MO_64: + if (!HAVE_al8) { + cpu_loop_exit_atomic(cpu, ra); + } + a = load_atom_extract_al8x2(pv); + b = load_atom_extract_al8x2(pv + 8); + break; + case MO_128: + return load_atomic16_or_exit(cpu, ra, pv); + default: + g_assert_not_reached(); + } + return int128_make128(HOST_BIG_ENDIAN ? b : a, HOST_BIG_ENDIAN ? a : b); +} + +/** + * store_atomic2: + * @pv: host address + * @val: value to store + * + * Atomically store 2 aligned bytes to @pv. + */ +static inline void store_atomic2(void *pv, uint16_t val) +{ + uint16_t *p = __builtin_assume_aligned(pv, 2); + qatomic_set(p, val); +} + +/** + * store_atomic4: + * @pv: host address + * @val: value to store + * + * Atomically store 4 aligned bytes to @pv. + */ +static inline void store_atomic4(void *pv, uint32_t val) +{ + uint32_t *p = __builtin_assume_aligned(pv, 4); + qatomic_set(p, val); +} + +/** + * store_atomic8: + * @pv: host address + * @val: value to store + * + * Atomically store 8 aligned bytes to @pv. + */ +static inline void store_atomic8(void *pv, uint64_t val) +{ + uint64_t *p = __builtin_assume_aligned(pv, 8); + + qemu_build_assert(HAVE_al8); + qatomic_set__nocheck(p, val); +} + +/** + * store_atom_4x2 + */ +static inline void store_atom_4_by_2(void *pv, uint32_t val) +{ + store_atomic2(pv, val >> (HOST_BIG_ENDIAN ? 16 : 0)); + store_atomic2(pv + 2, val >> (HOST_BIG_ENDIAN ? 0 : 16)); +} + +/** + * store_atom_8_by_2 + */ +static inline void store_atom_8_by_2(void *pv, uint64_t val) +{ + store_atom_4_by_2(pv, val >> (HOST_BIG_ENDIAN ? 32 : 0)); + store_atom_4_by_2(pv + 4, val >> (HOST_BIG_ENDIAN ? 0 : 32)); +} + +/** + * store_atom_8_by_4 + */ +static inline void store_atom_8_by_4(void *pv, uint64_t val) +{ + store_atomic4(pv, val >> (HOST_BIG_ENDIAN ? 32 : 0)); + store_atomic4(pv + 4, val >> (HOST_BIG_ENDIAN ? 0 : 32)); +} + +/** + * store_atom_insert_al4: + * @p: host address + * @val: shifted value to store + * @msk: mask for value to store + * + * Atomically store @val to @p, masked by @msk. + */ +static void store_atom_insert_al4(uint32_t *p, uint32_t val, uint32_t msk) +{ + uint32_t old, new; + + p = __builtin_assume_aligned(p, 4); + old = qatomic_read(p); + do { + new = (old & ~msk) | val; + } while (!__atomic_compare_exchange_n(p, &old, new, true, + __ATOMIC_RELAXED, __ATOMIC_RELAXED)); +} + +/** + * store_atom_insert_al8: + * @p: host address + * @val: shifted value to store + * @msk: mask for value to store + * + * Atomically store @val to @p masked by @msk. + */ +static void store_atom_insert_al8(uint64_t *p, uint64_t val, uint64_t msk) +{ + uint64_t old, new; + + qemu_build_assert(HAVE_al8); + p = __builtin_assume_aligned(p, 8); + old = qatomic_read__nocheck(p); + do { + new = (old & ~msk) | val; + } while (!__atomic_compare_exchange_n(p, &old, new, true, + __ATOMIC_RELAXED, __ATOMIC_RELAXED)); +} + +/** + * store_bytes_leN: + * @pv: host address + * @size: number of bytes to store + * @val_le: data to store + * + * Store @size bytes at @p. The bytes to store are extracted in little-endian order + * from @val_le; return the bytes of @val_le beyond @size that have not been stored. + */ +static uint64_t store_bytes_leN(void *pv, int size, uint64_t val_le) +{ + uint8_t *p = pv; + for (int i = 0; i < size; i++, val_le >>= 8) { + p[i] = val_le; + } + return val_le; +} + +/** + * store_parts_leN + * @pv: host address + * @size: number of bytes to store + * @val_le: data to store + * + * As store_bytes_leN, but atomically on each aligned part. + */ +G_GNUC_UNUSED +static uint64_t store_parts_leN(void *pv, int size, uint64_t val_le) +{ + do { + int n; + + /* Find minimum of alignment and size */ + switch (((uintptr_t)pv | size) & 7) { + case 4: + store_atomic4(pv, le32_to_cpu(val_le)); + val_le >>= 32; + n = 4; + break; + case 2: + case 6: + store_atomic2(pv, le16_to_cpu(val_le)); + val_le >>= 16; + n = 2; + break; + default: + *(uint8_t *)pv = val_le; + val_le >>= 8; + n = 1; + break; + case 0: + g_assert_not_reached(); + } + pv += n; + size -= n; + } while (size != 0); + + return val_le; +} + +/** + * store_whole_le4 + * @pv: host address + * @size: number of bytes to store + * @val_le: data to store + * + * As store_bytes_leN, but atomically as a whole. + * Four aligned bytes are guaranteed to cover the store. + */ +static uint64_t store_whole_le4(void *pv, int size, uint64_t val_le) +{ + int sz = size * 8; + int o = (uintptr_t)pv & 3; + int sh = o * 8; + uint32_t m = MAKE_64BIT_MASK(0, sz); + uint32_t v; + + if (HOST_BIG_ENDIAN) { + v = bswap32(val_le) >> sh; + m = bswap32(m) >> sh; + } else { + v = val_le << sh; + m <<= sh; + } + store_atom_insert_al4(pv - o, v, m); + return val_le >> sz; +} + +/** + * store_whole_le8 + * @pv: host address + * @size: number of bytes to store + * @val_le: data to store + * + * As store_bytes_leN, but atomically as a whole. + * Eight aligned bytes are guaranteed to cover the store. + */ +static uint64_t store_whole_le8(void *pv, int size, uint64_t val_le) +{ + int sz = size * 8; + int o = (uintptr_t)pv & 7; + int sh = o * 8; + uint64_t m = MAKE_64BIT_MASK(0, sz); + uint64_t v; + + qemu_build_assert(HAVE_al8); + if (HOST_BIG_ENDIAN) { + v = bswap64(val_le) >> sh; + m = bswap64(m) >> sh; + } else { + v = val_le << sh; + m <<= sh; + } + store_atom_insert_al8(pv - o, v, m); + return val_le >> sz; +} + +/** + * store_whole_le16 + * @pv: host address + * @size: number of bytes to store + * @val_le: data to store + * + * As store_bytes_leN, but atomically as a whole. + * 16 aligned bytes are guaranteed to cover the store. + */ +static uint64_t store_whole_le16(void *pv, int size, Int128 val_le) +{ + int sz = size * 8; + int o = (uintptr_t)pv & 15; + int sh = o * 8; + Int128 m, v; + + qemu_build_assert(HAVE_CMPXCHG128); + + /* Like MAKE_64BIT_MASK(0, sz), but larger. */ + if (sz <= 64) { + m = int128_make64(MAKE_64BIT_MASK(0, sz)); + } else { + m = int128_make128(-1, MAKE_64BIT_MASK(0, sz - 64)); + } + + if (HOST_BIG_ENDIAN) { + v = int128_urshift(bswap128(val_le), sh); + m = int128_urshift(bswap128(m), sh); + } else { + v = int128_lshift(val_le, sh); + m = int128_lshift(m, sh); + } + store_atom_insert_al16(pv - o, v, m); + + if (sz <= 64) { + return 0; + } + return int128_gethi(val_le) >> (sz - 64); +} + +/** + * store_atom_2: + * @p: host address + * @val: the value to store + * @memop: the full memory op + * + * Store 2 bytes to @p, honoring the atomicity of @memop. + */ +static void store_atom_2(CPUState *cpu, uintptr_t ra, + void *pv, MemOp memop, uint16_t val) +{ + uintptr_t pi = (uintptr_t)pv; + int atmax; + + if (likely((pi & 1) == 0)) { + store_atomic2(pv, val); + return; + } + + atmax = required_atomicity(cpu, pi, memop); + if (atmax == MO_8) { + stw_he_p(pv, val); + return; + } + + /* + * The only case remaining is MO_ATOM_WITHIN16. + * Big or little endian, we want the middle two bytes in each test. + */ + if ((pi & 3) == 1) { + store_atom_insert_al4(pv - 1, (uint32_t)val << 8, MAKE_64BIT_MASK(8, 16)); + return; + } else if ((pi & 7) == 3) { + if (HAVE_al8) { + store_atom_insert_al8(pv - 3, (uint64_t)val << 24, MAKE_64BIT_MASK(24, 16)); + return; + } + } else if ((pi & 15) == 7) { + if (HAVE_CMPXCHG128) { + Int128 v = int128_lshift(int128_make64(val), 56); + Int128 m = int128_lshift(int128_make64(0xffff), 56); + store_atom_insert_al16(pv - 7, v, m); + return; + } + } else { + g_assert_not_reached(); + } + + cpu_loop_exit_atomic(cpu, ra); +} + +/** + * store_atom_4: + * @p: host address + * @val: the value to store + * @memop: the full memory op + * + * Store 4 bytes to @p, honoring the atomicity of @memop. + */ +static void store_atom_4(CPUState *cpu, uintptr_t ra, + void *pv, MemOp memop, uint32_t val) +{ + uintptr_t pi = (uintptr_t)pv; + int atmax; + + if (likely((pi & 3) == 0)) { + store_atomic4(pv, val); + return; + } + + atmax = required_atomicity(cpu, pi, memop); + switch (atmax) { + case MO_8: + stl_he_p(pv, val); + return; + case MO_16: + store_atom_4_by_2(pv, val); + return; + case -MO_16: + { + uint32_t val_le = cpu_to_le32(val); + int s2 = pi & 3; + int s1 = 4 - s2; + + switch (s2) { + case 1: + val_le = store_whole_le4(pv, s1, val_le); + *(uint8_t *)(pv + 3) = val_le; + break; + case 3: + *(uint8_t *)pv = val_le; + store_whole_le4(pv + 1, s2, val_le >> 8); + break; + case 0: /* aligned */ + case 2: /* atmax MO_16 */ + default: + g_assert_not_reached(); + } + } + return; + case MO_32: + if ((pi & 7) < 4) { + if (HAVE_al8) { + store_whole_le8(pv, 4, cpu_to_le32(val)); + return; + } + } else { + if (HAVE_CMPXCHG128) { + store_whole_le16(pv, 4, int128_make64(cpu_to_le32(val))); + return; + } + } + cpu_loop_exit_atomic(cpu, ra); + default: + g_assert_not_reached(); + } +} + +/** + * store_atom_8: + * @p: host address + * @val: the value to store + * @memop: the full memory op + * + * Store 8 bytes to @p, honoring the atomicity of @memop. + */ +static void store_atom_8(CPUState *cpu, uintptr_t ra, + void *pv, MemOp memop, uint64_t val) +{ + uintptr_t pi = (uintptr_t)pv; + int atmax; + + if (HAVE_al8 && likely((pi & 7) == 0)) { + store_atomic8(pv, val); + return; + } + + atmax = required_atomicity(cpu, pi, memop); + switch (atmax) { + case MO_8: + stq_he_p(pv, val); + return; + case MO_16: + store_atom_8_by_2(pv, val); + return; + case MO_32: + store_atom_8_by_4(pv, val); + return; + case -MO_32: + if (HAVE_al8) { + uint64_t val_le = cpu_to_le64(val); + int s2 = pi & 7; + int s1 = 8 - s2; + + switch (s2) { + case 1 ... 3: + val_le = store_whole_le8(pv, s1, val_le); + store_bytes_leN(pv + s1, s2, val_le); + break; + case 5 ... 7: + val_le = store_bytes_leN(pv, s1, val_le); + store_whole_le8(pv + s1, s2, val_le); + break; + case 0: /* aligned */ + case 4: /* atmax MO_32 */ + default: + g_assert_not_reached(); + } + return; + } + break; + case MO_64: + if (HAVE_CMPXCHG128) { + store_whole_le16(pv, 8, int128_make64(cpu_to_le64(val))); + return; + } + break; + default: + g_assert_not_reached(); + } + cpu_loop_exit_atomic(cpu, ra); +} + +/** + * store_atom_16: + * @p: host address + * @val: the value to store + * @memop: the full memory op + * + * Store 16 bytes to @p, honoring the atomicity of @memop. + */ +static void store_atom_16(CPUState *cpu, uintptr_t ra, + void *pv, MemOp memop, Int128 val) +{ + uintptr_t pi = (uintptr_t)pv; + uint64_t a, b; + int atmax; + + if (HAVE_ATOMIC128_RW && likely((pi & 15) == 0)) { + atomic16_set(pv, val); + return; + } + + atmax = required_atomicity(cpu, pi, memop); + + a = HOST_BIG_ENDIAN ? int128_gethi(val) : int128_getlo(val); + b = HOST_BIG_ENDIAN ? int128_getlo(val) : int128_gethi(val); + switch (atmax) { + case MO_8: + memcpy(pv, &val, 16); + return; + case MO_16: + store_atom_8_by_2(pv, a); + store_atom_8_by_2(pv + 8, b); + return; + case MO_32: + store_atom_8_by_4(pv, a); + store_atom_8_by_4(pv + 8, b); + return; + case MO_64: + if (HAVE_al8) { + store_atomic8(pv, a); + store_atomic8(pv + 8, b); + return; + } + break; + case -MO_64: + if (HAVE_CMPXCHG128) { + uint64_t val_le; + int s2 = pi & 15; + int s1 = 16 - s2; + + if (HOST_BIG_ENDIAN) { + val = bswap128(val); + } + switch (s2) { + case 1 ... 7: + val_le = store_whole_le16(pv, s1, val); + store_bytes_leN(pv + s1, s2, val_le); + break; + case 9 ... 15: + store_bytes_leN(pv, s1, int128_getlo(val)); + val = int128_urshift(val, s1 * 8); + store_whole_le16(pv + s1, s2, val); + break; + case 0: /* aligned */ + case 8: /* atmax MO_64 */ + default: + g_assert_not_reached(); + } + return; + } + break; + case MO_128: + break; + default: + g_assert_not_reached(); + } + cpu_loop_exit_atomic(cpu, ra); +} diff --git a/accel/tcg/ldst_common.c.inc b/accel/tcg/ldst_common.c.inc index 6ac8d871a3..c82048e377 100644 --- a/accel/tcg/ldst_common.c.inc +++ b/accel/tcg/ldst_common.c.inc @@ -8,6 +8,231 @@ * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ +/* + * Load helpers for tcg-ldst.h + */ + +tcg_target_ulong helper_ldub_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) +{ + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_8); + return do_ld1_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); +} + +tcg_target_ulong helper_lduw_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) +{ + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16); + return do_ld2_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); +} + +tcg_target_ulong helper_ldul_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) +{ + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32); + return do_ld4_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); +} + +uint64_t helper_ldq_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) +{ + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64); + return do_ld8_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); +} + +/* + * Provide signed versions of the load routines as well. We can of course + * avoid this for 64-bit data, or for 32-bit data on 32-bit host. + */ + +tcg_target_ulong helper_ldsb_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) +{ + return (int8_t)helper_ldub_mmu(env, addr, oi, retaddr); +} + +tcg_target_ulong helper_ldsw_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) +{ + return (int16_t)helper_lduw_mmu(env, addr, oi, retaddr); +} + +tcg_target_ulong helper_ldsl_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) +{ + return (int32_t)helper_ldul_mmu(env, addr, oi, retaddr); +} + +Int128 helper_ld16_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) +{ + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128); + return do_ld16_mmu(env_cpu(env), addr, oi, retaddr); +} + +Int128 helper_ld_i128(CPUArchState *env, uint64_t addr, uint32_t oi) +{ + return helper_ld16_mmu(env, addr, oi, GETPC()); +} + +/* + * Store helpers for tcg-ldst.h + */ + +void helper_stb_mmu(CPUArchState *env, uint64_t addr, uint32_t val, + MemOpIdx oi, uintptr_t ra) +{ + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_8); + do_st1_mmu(env_cpu(env), addr, val, oi, ra); +} + +void helper_stw_mmu(CPUArchState *env, uint64_t addr, uint32_t val, + MemOpIdx oi, uintptr_t retaddr) +{ + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16); + do_st2_mmu(env_cpu(env), addr, val, oi, retaddr); +} + +void helper_stl_mmu(CPUArchState *env, uint64_t addr, uint32_t val, + MemOpIdx oi, uintptr_t retaddr) +{ + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32); + do_st4_mmu(env_cpu(env), addr, val, oi, retaddr); +} + +void helper_stq_mmu(CPUArchState *env, uint64_t addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr) +{ + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64); + do_st8_mmu(env_cpu(env), addr, val, oi, retaddr); +} + +void helper_st16_mmu(CPUArchState *env, uint64_t addr, Int128 val, + MemOpIdx oi, uintptr_t retaddr) +{ + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128); + do_st16_mmu(env_cpu(env), addr, val, oi, retaddr); +} + +void helper_st_i128(CPUArchState *env, uint64_t addr, Int128 val, MemOpIdx oi) +{ + helper_st16_mmu(env, addr, val, oi, GETPC()); +} + +/* + * Load helpers for cpu_ldst.h + */ + +static void plugin_load_cb(CPUArchState *env, abi_ptr addr, MemOpIdx oi) +{ + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); +} + +uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra) +{ + uint8_t ret; + + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_UB); + ret = do_ld1_mmu(env_cpu(env), addr, oi, ra, MMU_DATA_LOAD); + plugin_load_cb(env, addr, oi); + return ret; +} + +uint16_t cpu_ldw_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) +{ + uint16_t ret; + + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16); + ret = do_ld2_mmu(env_cpu(env), addr, oi, ra, MMU_DATA_LOAD); + plugin_load_cb(env, addr, oi); + return ret; +} + +uint32_t cpu_ldl_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) +{ + uint32_t ret; + + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32); + ret = do_ld4_mmu(env_cpu(env), addr, oi, ra, MMU_DATA_LOAD); + plugin_load_cb(env, addr, oi); + return ret; +} + +uint64_t cpu_ldq_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) +{ + uint64_t ret; + + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64); + ret = do_ld8_mmu(env_cpu(env), addr, oi, ra, MMU_DATA_LOAD); + plugin_load_cb(env, addr, oi); + return ret; +} + +Int128 cpu_ld16_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) +{ + Int128 ret; + + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128); + ret = do_ld16_mmu(env_cpu(env), addr, oi, ra); + plugin_load_cb(env, addr, oi); + return ret; +} + +/* + * Store helpers for cpu_ldst.h + */ + +static void plugin_store_cb(CPUArchState *env, abi_ptr addr, MemOpIdx oi) +{ + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); +} + +void cpu_stb_mmu(CPUArchState *env, abi_ptr addr, uint8_t val, + MemOpIdx oi, uintptr_t retaddr) +{ + helper_stb_mmu(env, addr, val, oi, retaddr); + plugin_store_cb(env, addr, oi); +} + +void cpu_stw_mmu(CPUArchState *env, abi_ptr addr, uint16_t val, + MemOpIdx oi, uintptr_t retaddr) +{ + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16); + do_st2_mmu(env_cpu(env), addr, val, oi, retaddr); + plugin_store_cb(env, addr, oi); +} + +void cpu_stl_mmu(CPUArchState *env, abi_ptr addr, uint32_t val, + MemOpIdx oi, uintptr_t retaddr) +{ + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32); + do_st4_mmu(env_cpu(env), addr, val, oi, retaddr); + plugin_store_cb(env, addr, oi); +} + +void cpu_stq_mmu(CPUArchState *env, abi_ptr addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr) +{ + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64); + do_st8_mmu(env_cpu(env), addr, val, oi, retaddr); + plugin_store_cb(env, addr, oi); +} + +void cpu_st16_mmu(CPUArchState *env, abi_ptr addr, Int128 val, + MemOpIdx oi, uintptr_t retaddr) +{ + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128); + do_st16_mmu(env_cpu(env), addr, val, oi, retaddr); + plugin_store_cb(env, addr, oi); +} + +/* + * Wrappers of the above + */ uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) @@ -26,7 +251,7 @@ uint32_t cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) { MemOpIdx oi = make_memop_idx(MO_BEUW | MO_UNALN, mmu_idx); - return cpu_ldw_be_mmu(env, addr, oi, ra); + return cpu_ldw_mmu(env, addr, oi, ra); } int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, @@ -39,21 +264,21 @@ uint32_t cpu_ldl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) { MemOpIdx oi = make_memop_idx(MO_BEUL | MO_UNALN, mmu_idx); - return cpu_ldl_be_mmu(env, addr, oi, ra); + return cpu_ldl_mmu(env, addr, oi, ra); } uint64_t cpu_ldq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) { MemOpIdx oi = make_memop_idx(MO_BEUQ | MO_UNALN, mmu_idx); - return cpu_ldq_be_mmu(env, addr, oi, ra); + return cpu_ldq_mmu(env, addr, oi, ra); } uint32_t cpu_lduw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) { MemOpIdx oi = make_memop_idx(MO_LEUW | MO_UNALN, mmu_idx); - return cpu_ldw_le_mmu(env, addr, oi, ra); + return cpu_ldw_mmu(env, addr, oi, ra); } int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, @@ -66,14 +291,14 @@ uint32_t cpu_ldl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) { MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx); - return cpu_ldl_le_mmu(env, addr, oi, ra); + return cpu_ldl_mmu(env, addr, oi, ra); } uint64_t cpu_ldq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) { MemOpIdx oi = make_memop_idx(MO_LEUQ | MO_UNALN, mmu_idx); - return cpu_ldq_le_mmu(env, addr, oi, ra); + return cpu_ldq_mmu(env, addr, oi, ra); } void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, @@ -87,49 +312,50 @@ void cpu_stw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, int mmu_idx, uintptr_t ra) { MemOpIdx oi = make_memop_idx(MO_BEUW | MO_UNALN, mmu_idx); - cpu_stw_be_mmu(env, addr, val, oi, ra); + cpu_stw_mmu(env, addr, val, oi, ra); } void cpu_stl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, int mmu_idx, uintptr_t ra) { MemOpIdx oi = make_memop_idx(MO_BEUL | MO_UNALN, mmu_idx); - cpu_stl_be_mmu(env, addr, val, oi, ra); + cpu_stl_mmu(env, addr, val, oi, ra); } void cpu_stq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, int mmu_idx, uintptr_t ra) { MemOpIdx oi = make_memop_idx(MO_BEUQ | MO_UNALN, mmu_idx); - cpu_stq_be_mmu(env, addr, val, oi, ra); + cpu_stq_mmu(env, addr, val, oi, ra); } void cpu_stw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, int mmu_idx, uintptr_t ra) { MemOpIdx oi = make_memop_idx(MO_LEUW | MO_UNALN, mmu_idx); - cpu_stw_le_mmu(env, addr, val, oi, ra); + cpu_stw_mmu(env, addr, val, oi, ra); } void cpu_stl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, int mmu_idx, uintptr_t ra) { MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx); - cpu_stl_le_mmu(env, addr, val, oi, ra); + cpu_stl_mmu(env, addr, val, oi, ra); } void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, int mmu_idx, uintptr_t ra) { MemOpIdx oi = make_memop_idx(MO_LEUQ | MO_UNALN, mmu_idx); - cpu_stq_le_mmu(env, addr, val, oi, ra); + cpu_stq_mmu(env, addr, val, oi, ra); } /*--------------------------*/ uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) { - return cpu_ldub_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldub_mmuidx_ra(env, addr, mmu_index, ra); } int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) @@ -139,7 +365,8 @@ int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) uint32_t cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) { - return cpu_lduw_be_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_lduw_be_mmuidx_ra(env, addr, mmu_index, ra); } int cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) @@ -149,17 +376,20 @@ int cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) uint32_t cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) { - return cpu_ldl_be_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldl_be_mmuidx_ra(env, addr, mmu_index, ra); } uint64_t cpu_ldq_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) { - return cpu_ldq_be_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldq_be_mmuidx_ra(env, addr, mmu_index, ra); } uint32_t cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) { - return cpu_lduw_le_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_lduw_le_mmuidx_ra(env, addr, mmu_index, ra); } int cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) @@ -169,54 +399,63 @@ int cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) uint32_t cpu_ldl_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) { - return cpu_ldl_le_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldl_le_mmuidx_ra(env, addr, mmu_index, ra); } uint64_t cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) { - return cpu_ldq_le_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldq_le_mmuidx_ra(env, addr, mmu_index, ra); } void cpu_stb_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) { - cpu_stb_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stb_mmuidx_ra(env, addr, val, mmu_index, ra); } void cpu_stw_be_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) { - cpu_stw_be_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stw_be_mmuidx_ra(env, addr, val, mmu_index, ra); } void cpu_stl_be_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) { - cpu_stl_be_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stl_be_mmuidx_ra(env, addr, val, mmu_index, ra); } void cpu_stq_be_data_ra(CPUArchState *env, abi_ptr addr, uint64_t val, uintptr_t ra) { - cpu_stq_be_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stq_be_mmuidx_ra(env, addr, val, mmu_index, ra); } void cpu_stw_le_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) { - cpu_stw_le_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stw_le_mmuidx_ra(env, addr, val, mmu_index, ra); } void cpu_stl_le_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) { - cpu_stl_le_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stl_le_mmuidx_ra(env, addr, val, mmu_index, ra); } void cpu_stq_le_data_ra(CPUArchState *env, abi_ptr addr, uint64_t val, uintptr_t ra) { - cpu_stq_le_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stq_le_mmuidx_ra(env, addr, val, mmu_index, ra); } /*--------------------------*/ diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 7a0a79d731..aef80de967 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -1,24 +1,34 @@ -tcg_ss = ss.source_set() -tcg_ss.add(files( - 'tcg-all.c', +common_ss.add(when: 'CONFIG_TCG', if_true: files( 'cpu-exec-common.c', +)) +tcg_specific_ss = ss.source_set() +tcg_specific_ss.add(files( + 'tcg-all.c', 'cpu-exec.c', + 'tb-maint.c', 'tcg-runtime-gvec.c', 'tcg-runtime.c', 'translate-all.c', 'translator.c', )) -tcg_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c')) -tcg_ss.add(when: 'CONFIG_SOFTMMU', if_false: files('user-exec-stub.c')) -tcg_ss.add(when: 'CONFIG_PLUGIN', if_true: [files('plugin-gen.c')]) -specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss) +tcg_specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c')) +tcg_specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_false: files('user-exec-stub.c')) +if get_option('plugins') + tcg_specific_ss.add(files('plugin-gen.c')) +endif +specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss) -specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files( +specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( 'cputlb.c', - 'hmp.c', + 'watchpoint.c', )) -tcg_module_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files( +system_ss.add(when: ['CONFIG_TCG'], if_true: files( + 'icount-common.c', + 'monitor.c', +)) + +tcg_module_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( 'tcg-accel-ops.c', 'tcg-accel-ops-mttcg.c', 'tcg-accel-ops-icount.c', diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c new file mode 100644 index 0000000000..093efe9714 --- /dev/null +++ b/accel/tcg/monitor.c @@ -0,0 +1,244 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * QEMU TCG monitor + * + * Copyright (c) 2003-2005 Fabrice Bellard + */ + +#include "qemu/osdep.h" +#include "qemu/accel.h" +#include "qemu/qht.h" +#include "qapi/error.h" +#include "qapi/type-helpers.h" +#include "qapi/qapi-commands-machine.h" +#include "monitor/monitor.h" +#include "sysemu/cpus.h" +#include "sysemu/cpu-timers.h" +#include "sysemu/tcg.h" +#include "tcg/tcg.h" +#include "internal-common.h" +#include "tb-context.h" + + +static void dump_drift_info(GString *buf) +{ + if (!icount_enabled()) { + return; + } + + g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n", + (cpu_get_clock() - icount_get()) / SCALE_MS); + if (icount_align_option) { + g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n", + -max_delay / SCALE_MS); + g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n", + max_advance / SCALE_MS); + } else { + g_string_append_printf(buf, "Max guest delay NA\n"); + g_string_append_printf(buf, "Max guest advance NA\n"); + } +} + +static void dump_accel_info(GString *buf) +{ + AccelState *accel = current_accel(); + bool one_insn_per_tb = object_property_get_bool(OBJECT(accel), + "one-insn-per-tb", + &error_fatal); + + g_string_append_printf(buf, "Accelerator settings:\n"); + g_string_append_printf(buf, "one-insn-per-tb: %s\n\n", + one_insn_per_tb ? "on" : "off"); +} + +static void print_qht_statistics(struct qht_stats hst, GString *buf) +{ + uint32_t hgram_opts; + size_t hgram_bins; + char *hgram; + + if (!hst.head_buckets) { + return; + } + g_string_append_printf(buf, "TB hash buckets %zu/%zu " + "(%0.2f%% head buckets used)\n", + hst.used_head_buckets, hst.head_buckets, + (double)hst.used_head_buckets / + hst.head_buckets * 100); + + hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; + hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT; + if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) { + hgram_opts |= QDIST_PR_NODECIMAL; + } + hgram = qdist_pr(&hst.occupancy, 10, hgram_opts); + g_string_append_printf(buf, "TB hash occupancy %0.2f%% avg chain occ. " + "Histogram: %s\n", + qdist_avg(&hst.occupancy) * 100, hgram); + g_free(hgram); + + hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; + hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain); + if (hgram_bins > 10) { + hgram_bins = 10; + } else { + hgram_bins = 0; + hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE; + } + hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts); + g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. " + "Histogram: %s\n", + qdist_avg(&hst.chain), hgram); + g_free(hgram); +} + +struct tb_tree_stats { + size_t nb_tbs; + size_t host_size; + size_t target_size; + size_t max_target_size; + size_t direct_jmp_count; + size_t direct_jmp2_count; + size_t cross_page; +}; + +static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data) +{ + const TranslationBlock *tb = value; + struct tb_tree_stats *tst = data; + + tst->nb_tbs++; + tst->host_size += tb->tc.size; + tst->target_size += tb->size; + if (tb->size > tst->max_target_size) { + tst->max_target_size = tb->size; + } + if (tb->page_addr[1] != -1) { + tst->cross_page++; + } + if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) { + tst->direct_jmp_count++; + if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) { + tst->direct_jmp2_count++; + } + } + return false; +} + +static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) +{ + CPUState *cpu; + size_t full = 0, part = 0, elide = 0; + + CPU_FOREACH(cpu) { + full += qatomic_read(&cpu->neg.tlb.c.full_flush_count); + part += qatomic_read(&cpu->neg.tlb.c.part_flush_count); + elide += qatomic_read(&cpu->neg.tlb.c.elide_flush_count); + } + *pfull = full; + *ppart = part; + *pelide = elide; +} + +static void tcg_dump_info(GString *buf) +{ + g_string_append_printf(buf, "[TCG profiler not compiled]\n"); +} + +static void dump_exec_info(GString *buf) +{ + struct tb_tree_stats tst = {}; + struct qht_stats hst; + size_t nb_tbs, flush_full, flush_part, flush_elide; + + tcg_tb_foreach(tb_tree_stats_iter, &tst); + nb_tbs = tst.nb_tbs; + /* XXX: avoid using doubles ? */ + g_string_append_printf(buf, "Translation buffer state:\n"); + /* + * Report total code size including the padding and TB structs; + * otherwise users might think "-accel tcg,tb-size" is not honoured. + * For avg host size we use the precise numbers from tb_tree_stats though. + */ + g_string_append_printf(buf, "gen code size %zu/%zu\n", + tcg_code_size(), tcg_code_capacity()); + g_string_append_printf(buf, "TB count %zu\n", nb_tbs); + g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n", + nb_tbs ? tst.target_size / nb_tbs : 0, + tst.max_target_size); + g_string_append_printf(buf, "TB avg host size %zu bytes " + "(expansion ratio: %0.1f)\n", + nb_tbs ? tst.host_size / nb_tbs : 0, + tst.target_size ? + (double)tst.host_size / tst.target_size : 0); + g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n", + tst.cross_page, + nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0); + g_string_append_printf(buf, "direct jump count %zu (%zu%%) " + "(2 jumps=%zu %zu%%)\n", + tst.direct_jmp_count, + nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0, + tst.direct_jmp2_count, + nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0); + + qht_statistics_init(&tb_ctx.htable, &hst); + print_qht_statistics(hst, buf); + qht_statistics_destroy(&hst); + + g_string_append_printf(buf, "\nStatistics:\n"); + g_string_append_printf(buf, "TB flush count %u\n", + qatomic_read(&tb_ctx.tb_flush_count)); + g_string_append_printf(buf, "TB invalidate count %u\n", + qatomic_read(&tb_ctx.tb_phys_invalidate_count)); + + tlb_flush_counts(&flush_full, &flush_part, &flush_elide); + g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full); + g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part); + g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide); + tcg_dump_info(buf); +} + +HumanReadableText *qmp_x_query_jit(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + if (!tcg_enabled()) { + error_setg(errp, "JIT information is only available with accel=tcg"); + return NULL; + } + + dump_accel_info(buf); + dump_exec_info(buf); + dump_drift_info(buf); + + return human_readable_text_from_str(buf); +} + +static void tcg_dump_op_count(GString *buf) +{ + g_string_append_printf(buf, "[TCG profiler not compiled]\n"); +} + +HumanReadableText *qmp_x_query_opcount(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + if (!tcg_enabled()) { + error_setg(errp, + "Opcode count information is only available with accel=tcg"); + return NULL; + } + + tcg_dump_op_count(buf); + + return human_readable_text_from_str(buf); +} + +static void hmp_tcg_register(void) +{ + monitor_register_hmp_info_hrt("jit", qmp_x_query_jit); + monitor_register_hmp_info_hrt("opcount", qmp_x_query_opcount); +} + +type_init(hmp_tcg_register); diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 3d0b101e34..cd78ef94a1 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -43,17 +43,19 @@ * CPU's index into a TCG temp, since the first callback did it already. */ #include "qemu/osdep.h" +#include "qemu/plugin.h" +#include "cpu.h" #include "tcg/tcg.h" +#include "tcg/tcg-temp-internal.h" #include "tcg/tcg-op.h" #include "exec/exec-all.h" #include "exec/plugin-gen.h" #include "exec/translator.h" +#include "exec/helper-proto-common.h" -#ifdef CONFIG_SOFTMMU -# define CONFIG_SOFTMMU_GATE 1 -#else -# define CONFIG_SOFTMMU_GATE 0 -#endif +#define HELPER_H "accel/tcg/plugin-helpers.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H /* * plugin_cb_start TCG op args[]: @@ -72,6 +74,7 @@ enum plugin_gen_from { enum plugin_gen_cb { PLUGIN_GEN_CB_UDATA, + PLUGIN_GEN_CB_UDATA_R, PLUGIN_GEN_CB_INLINE, PLUGIN_GEN_CB_MEM, PLUGIN_GEN_ENABLE_MEM_HELPER, @@ -83,7 +86,10 @@ enum plugin_gen_cb { * These helpers are stubs that get dynamically switched out for calls * direct to the plugin if they are subscribed to. */ -void HELPER(plugin_vcpu_udata_cb)(uint32_t cpu_index, void *udata) +void HELPER(plugin_vcpu_udata_cb_no_wg)(uint32_t cpu_index, void *udata) +{ } + +void HELPER(plugin_vcpu_udata_cb_no_rwg)(uint32_t cpu_index, void *udata) { } void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index, @@ -91,36 +97,28 @@ void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index, void *userdata) { } -static void do_gen_mem_cb(TCGv vaddr, uint32_t info) +static void gen_empty_udata_cb(void (*gen_helper)(TCGv_i32, TCGv_ptr)) { - TCGv_i32 cpu_index = tcg_temp_new_i32(); - TCGv_i32 meminfo = tcg_const_i32(info); - TCGv_i64 vaddr64 = tcg_temp_new_i64(); - TCGv_ptr udata = tcg_const_ptr(NULL); + TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); + TCGv_ptr udata = tcg_temp_ebb_new_ptr(); - tcg_gen_ld_i32(cpu_index, cpu_env, + tcg_gen_movi_ptr(udata, 0); + tcg_gen_ld_i32(cpu_index, tcg_env, -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); - tcg_gen_extu_tl_i64(vaddr64, vaddr); - - gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, vaddr64, udata); + gen_helper(cpu_index, udata); tcg_temp_free_ptr(udata); - tcg_temp_free_i64(vaddr64); - tcg_temp_free_i32(meminfo); tcg_temp_free_i32(cpu_index); } -static void gen_empty_udata_cb(void) +static void gen_empty_udata_cb_no_wg(void) { - TCGv_i32 cpu_index = tcg_temp_new_i32(); - TCGv_ptr udata = tcg_const_ptr(NULL); /* will be overwritten later */ + gen_empty_udata_cb(gen_helper_plugin_vcpu_udata_cb_no_wg); +} - tcg_gen_ld_i32(cpu_index, cpu_env, - -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); - gen_helper_plugin_vcpu_udata_cb(cpu_index, udata); - - tcg_temp_free_ptr(udata); - tcg_temp_free_i32(cpu_index); +static void gen_empty_udata_cb_no_rwg(void) +{ + gen_empty_udata_cb(gen_helper_plugin_vcpu_udata_cb_no_rwg); } /* @@ -129,20 +127,46 @@ static void gen_empty_udata_cb(void) */ static void gen_empty_inline_cb(void) { - TCGv_i64 val = tcg_temp_new_i64(); - TCGv_ptr ptr = tcg_const_ptr(NULL); /* overwritten later */ + TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); + TCGv_ptr cpu_index_as_ptr = tcg_temp_ebb_new_ptr(); + TCGv_i64 val = tcg_temp_ebb_new_i64(); + TCGv_ptr ptr = tcg_temp_ebb_new_ptr(); + tcg_gen_ld_i32(cpu_index, tcg_env, + -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); + /* second operand will be replaced by immediate value */ + tcg_gen_mul_i32(cpu_index, cpu_index, cpu_index); + tcg_gen_ext_i32_ptr(cpu_index_as_ptr, cpu_index); + + tcg_gen_movi_ptr(ptr, 0); + tcg_gen_add_ptr(ptr, ptr, cpu_index_as_ptr); tcg_gen_ld_i64(val, ptr, 0); - /* pass an immediate != 0 so that it doesn't get optimized away */ - tcg_gen_addi_i64(val, val, 0xdeadface); + /* second operand will be replaced by immediate value */ + tcg_gen_add_i64(val, val, val); + tcg_gen_st_i64(val, ptr, 0); tcg_temp_free_ptr(ptr); tcg_temp_free_i64(val); + tcg_temp_free_ptr(cpu_index_as_ptr); + tcg_temp_free_i32(cpu_index); } -static void gen_empty_mem_cb(TCGv addr, uint32_t info) +static void gen_empty_mem_cb(TCGv_i64 addr, uint32_t info) { - do_gen_mem_cb(addr, info); + TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); + TCGv_i32 meminfo = tcg_temp_ebb_new_i32(); + TCGv_ptr udata = tcg_temp_ebb_new_ptr(); + + tcg_gen_movi_i32(meminfo, info); + tcg_gen_movi_ptr(udata, 0); + tcg_gen_ld_i32(cpu_index, tcg_env, + -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); + + gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, addr, udata); + + tcg_temp_free_ptr(udata); + tcg_temp_free_i32(meminfo); + tcg_temp_free_i32(cpu_index); } /* @@ -151,10 +175,10 @@ static void gen_empty_mem_cb(TCGv addr, uint32_t info) */ static void gen_empty_mem_helper(void) { - TCGv_ptr ptr; + TCGv_ptr ptr = tcg_temp_ebb_new_ptr(); - ptr = tcg_const_ptr(NULL); - tcg_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs) - + tcg_gen_movi_ptr(ptr, 0); + tcg_gen_st_ptr(ptr, tcg_env, offsetof(CPUState, plugin_mem_cbs) - offsetof(ArchCPU, env)); tcg_temp_free_ptr(ptr); } @@ -189,7 +213,8 @@ static void plugin_gen_empty_callback(enum plugin_gen_from from) gen_empty_mem_helper); /* fall through */ case PLUGIN_GEN_FROM_TB: - gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb); + gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb_no_rwg); + gen_wrapped(from, PLUGIN_GEN_CB_UDATA_R, gen_empty_udata_cb_no_wg); gen_wrapped(from, PLUGIN_GEN_CB_INLINE, gen_empty_inline_cb); break; default: @@ -197,35 +222,17 @@ static void plugin_gen_empty_callback(enum plugin_gen_from from) } } -union mem_gen_fn { - void (*mem_fn)(TCGv, uint32_t); - void (*inline_fn)(void); -}; - -static void gen_mem_wrapped(enum plugin_gen_cb type, - const union mem_gen_fn *f, TCGv addr, - uint32_t info, bool is_mem) +void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info) { enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); - gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, type, rw); - if (is_mem) { - f->mem_fn(addr, info); - } else { - f->inline_fn(); - } + gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_MEM, rw); + gen_empty_mem_cb(addr, info); tcg_gen_plugin_cb_end(); -} -void plugin_gen_empty_mem_callback(TCGv addr, uint32_t info) -{ - union mem_gen_fn fn; - - fn.mem_fn = gen_empty_mem_cb; - gen_mem_wrapped(PLUGIN_GEN_CB_MEM, &fn, addr, info, true); - - fn.inline_fn = gen_empty_inline_cb; - gen_mem_wrapped(PLUGIN_GEN_CB_INLINE, &fn, 0, info, false); + gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_INLINE, rw); + gen_empty_inline_cb(); + tcg_gen_plugin_cb_end(); } static TCGOp *find_op(TCGOp *op, TCGOpcode opc) @@ -258,10 +265,13 @@ static TCGOp *rm_ops(TCGOp *op) static TCGOp *copy_op_nocheck(TCGOp **begin_op, TCGOp *op) { - *begin_op = QTAILQ_NEXT(*begin_op, link); - tcg_debug_assert(*begin_op); - op = tcg_op_insert_after(tcg_ctx, op, (*begin_op)->opc); - memcpy(op->args, (*begin_op)->args, sizeof(op->args)); + TCGOp *old_op = QTAILQ_NEXT(*begin_op, link); + unsigned nargs = old_op->nargs; + + *begin_op = old_op; + op = tcg_op_insert_after(tcg_ctx, op, old_op->opc, nargs); + memcpy(op->args, old_op->args, sizeof(op->args[0]) * nargs); + return op; } @@ -272,33 +282,6 @@ static TCGOp *copy_op(TCGOp **begin_op, TCGOp *op, TCGOpcode opc) return op; } -static TCGOp *copy_extu_i32_i64(TCGOp **begin_op, TCGOp *op) -{ - if (TCG_TARGET_REG_BITS == 32) { - /* mov_i32 */ - op = copy_op(begin_op, op, INDEX_op_mov_i32); - /* mov_i32 w/ $0 */ - op = copy_op(begin_op, op, INDEX_op_mov_i32); - } else { - /* extu_i32_i64 */ - op = copy_op(begin_op, op, INDEX_op_extu_i32_i64); - } - return op; -} - -static TCGOp *copy_mov_i64(TCGOp **begin_op, TCGOp *op) -{ - if (TCG_TARGET_REG_BITS == 32) { - /* 2x mov_i32 */ - op = copy_op(begin_op, op, INDEX_op_mov_i32); - op = copy_op(begin_op, op, INDEX_op_mov_i32); - } else { - /* mov_i64 */ - op = copy_op(begin_op, op, INDEX_op_mov_i64); - } - return op; -} - static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr) { if (UINTPTR_MAX == UINT32_MAX) { @@ -313,14 +296,27 @@ static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr) return op; } -static TCGOp *copy_extu_tl_i64(TCGOp **begin_op, TCGOp *op) +static TCGOp *copy_ld_i32(TCGOp **begin_op, TCGOp *op) { - if (TARGET_LONG_BITS == 32) { - /* extu_i32_i64 */ - op = copy_extu_i32_i64(begin_op, op); + return copy_op(begin_op, op, INDEX_op_ld_i32); +} + +static TCGOp *copy_ext_i32_ptr(TCGOp **begin_op, TCGOp *op) +{ + if (UINTPTR_MAX == UINT32_MAX) { + op = copy_op(begin_op, op, INDEX_op_mov_i32); } else { - /* mov_i64 */ - op = copy_mov_i64(begin_op, op); + op = copy_op(begin_op, op, INDEX_op_ext_i32_i64); + } + return op; +} + +static TCGOp *copy_add_ptr(TCGOp **begin_op, TCGOp *op) +{ + if (UINTPTR_MAX == UINT32_MAX) { + op = copy_op(begin_op, op, INDEX_op_add_i32); + } else { + op = copy_op(begin_op, op, INDEX_op_add_i64); } return op; } @@ -329,8 +325,8 @@ static TCGOp *copy_ld_i64(TCGOp **begin_op, TCGOp *op) { if (TCG_TARGET_REG_BITS == 32) { /* 2x ld_i32 */ - op = copy_op(begin_op, op, INDEX_op_ld_i32); - op = copy_op(begin_op, op, INDEX_op_ld_i32); + op = copy_ld_i32(begin_op, op); + op = copy_ld_i32(begin_op, op); } else { /* ld_i64 */ op = copy_op(begin_op, op, INDEX_op_ld_i64); @@ -366,6 +362,13 @@ static TCGOp *copy_add_i64(TCGOp **begin_op, TCGOp *op, uint64_t v) return op; } +static TCGOp *copy_mul_i32(TCGOp **begin_op, TCGOp *op, uint32_t v) +{ + op = copy_op(begin_op, op, INDEX_op_mul_i32); + op->args[2] = tcgv_i32_arg(tcg_constant_i32(v)); + return op; +} + static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op) { if (UINTPTR_MAX == UINT32_MAX) { @@ -378,35 +381,25 @@ static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op) return op; } -static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func, - void *func, int *cb_idx) +static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *func, int *cb_idx) { + TCGOp *old_op; + int func_idx; + /* copy all ops until the call */ do { op = copy_op_nocheck(begin_op, op); } while (op->opc != INDEX_op_call); /* fill in the op call */ - op->param1 = (*begin_op)->param1; - op->param2 = (*begin_op)->param2; + old_op = *begin_op; + TCGOP_CALLI(op) = TCGOP_CALLI(old_op); + TCGOP_CALLO(op) = TCGOP_CALLO(old_op); tcg_debug_assert(op->life == 0); - if (*cb_idx == -1) { - int i; - /* - * Instead of working out the position of the callback in args[], just - * look for @empty_func, since it should be a unique pointer. - */ - for (i = 0; i < MAX_OPC_PARAM_ARGS; i++) { - if ((uintptr_t)(*begin_op)->args[i] == (uintptr_t)empty_func) { - *cb_idx = i; - break; - } - } - tcg_debug_assert(i < MAX_OPC_PARAM_ARGS); - } - op->args[*cb_idx] = (uintptr_t)func; - op->args[*cb_idx + 1] = (*begin_op)->args[*cb_idx + 1]; + func_idx = TCGOP_CALLO(op) + TCGOP_CALLI(op); + *cb_idx = func_idx; + op->args[func_idx] = (uintptr_t)func; return op; } @@ -424,16 +417,15 @@ static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb, op = copy_const_ptr(&begin_op, op, cb->userp); /* copy the ld_i32, but note that we only have to copy it once */ - begin_op = QTAILQ_NEXT(begin_op, link); - tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32); if (*cb_idx == -1) { - op = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32); - memcpy(op->args, begin_op->args, sizeof(op->args)); + op = copy_op(&begin_op, op, INDEX_op_ld_i32); + } else { + begin_op = QTAILQ_NEXT(begin_op, link); + tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32); } /* call */ - op = copy_call(&begin_op, op, HELPER(plugin_vcpu_udata_cb), - cb->f.vcpu_udata, cb_idx); + op = copy_call(&begin_op, op, cb->f.vcpu_udata, cb_idx); return op; } @@ -442,18 +434,19 @@ static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb, TCGOp *begin_op, TCGOp *op, int *unused) { - /* const_ptr */ - op = copy_const_ptr(&begin_op, op, cb->userp); + char *ptr = cb->inline_insn.entry.score->data->data; + size_t elem_size = g_array_get_element_size( + cb->inline_insn.entry.score->data); + size_t offset = cb->inline_insn.entry.offset; - /* ld_i64 */ + op = copy_ld_i32(&begin_op, op); + op = copy_mul_i32(&begin_op, op, elem_size); + op = copy_ext_i32_ptr(&begin_op, op); + op = copy_const_ptr(&begin_op, op, ptr + offset); + op = copy_add_ptr(&begin_op, op); op = copy_ld_i64(&begin_op, op); - - /* add_i64 */ op = copy_add_i64(&begin_op, op, cb->inline_insn.imm); - - /* st_i64 */ op = copy_st_i64(&begin_op, op); - return op; } @@ -471,20 +464,16 @@ static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb, op = copy_const_ptr(&begin_op, op, cb->userp); /* copy the ld_i32, but note that we only have to copy it once */ - begin_op = QTAILQ_NEXT(begin_op, link); - tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32); if (*cb_idx == -1) { - op = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32); - memcpy(op->args, begin_op->args, sizeof(op->args)); + op = copy_op(&begin_op, op, INDEX_op_ld_i32); + } else { + begin_op = QTAILQ_NEXT(begin_op, link); + tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32); } - /* extu_tl_i64 */ - op = copy_extu_tl_i64(&begin_op, op); - if (type == PLUGIN_GEN_CB_MEM) { /* call */ - op = copy_call(&begin_op, op, HELPER(plugin_vcpu_mem_cb), - cb->f.vcpu_udata, cb_idx); + op = copy_call(&begin_op, op, cb->f.vcpu_udata, cb_idx); } return op; @@ -585,7 +574,8 @@ static void inject_mem_helper(TCGOp *begin_op, GArray *arr) * is possible that the code we generate after the instruction is * dead, we also add checks before generating tb_exit etc. */ -static void inject_mem_enable_helper(struct qemu_plugin_insn *plugin_insn, +static void inject_mem_enable_helper(struct qemu_plugin_tb *ptb, + struct qemu_plugin_insn *plugin_insn, TCGOp *begin_op) { GArray *cbs[2]; @@ -605,6 +595,7 @@ static void inject_mem_enable_helper(struct qemu_plugin_insn *plugin_insn, rm_ops(begin_op); return; } + ptb->mem_helper = true; arr = g_array_sized_new(false, false, sizeof(struct qemu_plugin_dyn_cb), n_cbs); @@ -630,17 +621,20 @@ static void inject_mem_disable_helper(struct qemu_plugin_insn *plugin_insn, /* called before finishing a TB with exit_tb, goto_tb or goto_ptr */ void plugin_gen_disable_mem_helpers(void) { - TCGv_ptr ptr; - - if (likely(tcg_ctx->plugin_insn == NULL || - !tcg_ctx->plugin_insn->mem_helper)) { + /* + * We could emit the clearing unconditionally and be done. However, this can + * be wasteful if for instance plugins don't track memory accesses, or if + * most TBs don't use helpers. Instead, emit the clearing iff the TB calls + * helpers that might access guest memory. + * + * Note: we do not reset plugin_tb->mem_helper here; a TB might have several + * exit points, and we want to emit the clearing from all of them. + */ + if (!tcg_ctx->plugin_tb->mem_helper) { return; } - ptr = tcg_const_ptr(NULL); - tcg_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs) - - offsetof(ArchCPU, env)); - tcg_temp_free_ptr(ptr); - tcg_ctx->plugin_insn->mem_helper = false; + tcg_gen_st_ptr(tcg_constant_ptr(NULL), tcg_env, + offsetof(CPUState, plugin_mem_cbs) - offsetof(ArchCPU, env)); } static void plugin_gen_tb_udata(const struct qemu_plugin_tb *ptb, @@ -649,6 +643,12 @@ static void plugin_gen_tb_udata(const struct qemu_plugin_tb *ptb, inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR], begin_op); } +static void plugin_gen_tb_udata_r(const struct qemu_plugin_tb *ptb, + TCGOp *begin_op) +{ + inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR_R], begin_op); +} + static void plugin_gen_tb_inline(const struct qemu_plugin_tb *ptb, TCGOp *begin_op) { @@ -663,6 +663,14 @@ static void plugin_gen_insn_udata(const struct qemu_plugin_tb *ptb, inject_udata_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], begin_op); } +static void plugin_gen_insn_udata_r(const struct qemu_plugin_tb *ptb, + TCGOp *begin_op, int insn_idx) +{ + struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); + + inject_udata_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR_R], begin_op); +} + static void plugin_gen_insn_inline(const struct qemu_plugin_tb *ptb, TCGOp *begin_op, int insn_idx) { @@ -688,14 +696,14 @@ static void plugin_gen_mem_inline(const struct qemu_plugin_tb *ptb, inject_inline_cb(cbs, begin_op, op_rw); } -static void plugin_gen_enable_mem_helper(const struct qemu_plugin_tb *ptb, +static void plugin_gen_enable_mem_helper(struct qemu_plugin_tb *ptb, TCGOp *begin_op, int insn_idx) { struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); - inject_mem_enable_helper(insn, begin_op); + inject_mem_enable_helper(ptb, insn, begin_op); } -static void plugin_gen_disable_mem_helper(const struct qemu_plugin_tb *ptb, +static void plugin_gen_disable_mem_helper(struct qemu_plugin_tb *ptb, TCGOp *begin_op, int insn_idx) { struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); @@ -756,7 +764,7 @@ static void pr_ops(void) #endif } -static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb) +static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) { TCGOp *op; int insn_idx = -1; @@ -782,6 +790,9 @@ static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb) case PLUGIN_GEN_CB_UDATA: plugin_gen_tb_udata(plugin_tb, op); break; + case PLUGIN_GEN_CB_UDATA_R: + plugin_gen_tb_udata_r(plugin_tb, op); + break; case PLUGIN_GEN_CB_INLINE: plugin_gen_tb_inline(plugin_tb, op); break; @@ -798,6 +809,9 @@ static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb) case PLUGIN_GEN_CB_UDATA: plugin_gen_insn_udata(plugin_tb, op, insn_idx); break; + case PLUGIN_GEN_CB_UDATA_R: + plugin_gen_insn_udata_r(plugin_tb, op, insn_idx); + break; case PLUGIN_GEN_CB_INLINE: plugin_gen_insn_inline(plugin_tb, op, insn_idx); break; @@ -852,11 +866,12 @@ static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb) pr_ops(); } -bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb, bool mem_only) +bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db, + bool mem_only) { bool ret = false; - if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_mask)) { + if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_state->event_mask)) { struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; int i; @@ -870,11 +885,12 @@ bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb, bool mem_onl ret = true; - ptb->vaddr = tb->pc; + ptb->vaddr = db->pc_first; ptb->vaddr2 = -1; - get_page_addr_code_hostp(cpu->env_ptr, tb->pc, &ptb->haddr1); + ptb->haddr1 = db->host_addr[0]; ptb->haddr2 = NULL; ptb->mem_only = mem_only; + ptb->mem_helper = false; plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB); } @@ -898,16 +914,15 @@ void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db) * Note that we skip this when haddr1 == NULL, e.g. when we're * fetching instructions from a region not backed by RAM. */ - if (likely(ptb->haddr1 != NULL && ptb->vaddr2 == -1) && - unlikely((db->pc_next & TARGET_PAGE_MASK) != - (db->pc_first & TARGET_PAGE_MASK))) { - get_page_addr_code_hostp(cpu->env_ptr, db->pc_next, - &ptb->haddr2); - ptb->vaddr2 = db->pc_next; - } - if (likely(ptb->vaddr2 == -1)) { + if (ptb->haddr1 == NULL) { + pinsn->haddr = NULL; + } else if (is_same_page(db, db->pc_next)) { pinsn->haddr = ptb->haddr1 + pinsn->vaddr - ptb->vaddr; } else { + if (ptb->vaddr2 == -1) { + ptb->vaddr2 = TARGET_PAGE_ALIGN(db->pc_first); + get_page_addr_code_hostp(cpu_env(cpu), ptb->vaddr2, &ptb->haddr2); + } pinsn->haddr = ptb->haddr2 + pinsn->vaddr - ptb->vaddr2; } } @@ -923,10 +938,14 @@ void plugin_gen_insn_end(void) * do any clean-up here and make sure things are reset in * plugin_gen_tb_start. */ -void plugin_gen_tb_end(CPUState *cpu) +void plugin_gen_tb_end(CPUState *cpu, size_t num_insns) { struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; + /* translator may have removed instructions, update final count */ + g_assert(num_insns <= ptb->n); + ptb->n = num_insns; + /* collect instrumentation requests */ qemu_plugin_tb_trans_cb(cpu, ptb); diff --git a/accel/tcg/plugin-helpers.h b/accel/tcg/plugin-helpers.h index 9829abe4a9..11796436f3 100644 --- a/accel/tcg/plugin-helpers.h +++ b/accel/tcg/plugin-helpers.h @@ -1,4 +1,5 @@ #ifdef CONFIG_PLUGIN -DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb, TCG_CALL_NO_RWG, void, i32, ptr) -DEF_HELPER_FLAGS_4(plugin_vcpu_mem_cb, TCG_CALL_NO_RWG, void, i32, i32, i64, ptr) +DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb_no_wg, TCG_CALL_NO_WG | TCG_CALL_PLUGIN, void, i32, ptr) +DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb_no_rwg, TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, void, i32, ptr) +DEF_HELPER_FLAGS_4(plugin_vcpu_mem_cb, TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, void, i32, i32, i64, ptr) #endif diff --git a/accel/tcg/tb-hash.h b/accel/tcg/tb-hash.h index 0a273d9605..a0c61f25cd 100644 --- a/accel/tcg/tb-hash.h +++ b/accel/tcg/tb-hash.h @@ -23,6 +23,7 @@ #include "exec/cpu-defs.h" #include "exec/exec-all.h" #include "qemu/xxhash.h" +#include "tb-jmp-cache.h" #ifdef CONFIG_SOFTMMU @@ -34,16 +35,16 @@ #define TB_JMP_ADDR_MASK (TB_JMP_PAGE_SIZE - 1) #define TB_JMP_PAGE_MASK (TB_JMP_CACHE_SIZE - TB_JMP_PAGE_SIZE) -static inline unsigned int tb_jmp_cache_hash_page(target_ulong pc) +static inline unsigned int tb_jmp_cache_hash_page(vaddr pc) { - target_ulong tmp; + vaddr tmp; tmp = pc ^ (pc >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS)); return (tmp >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS)) & TB_JMP_PAGE_MASK; } -static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc) +static inline unsigned int tb_jmp_cache_hash_func(vaddr pc) { - target_ulong tmp; + vaddr tmp; tmp = pc ^ (pc >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS)); return (((tmp >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS)) & TB_JMP_PAGE_MASK) | (tmp & TB_JMP_ADDR_MASK)); @@ -52,7 +53,7 @@ static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc) #else /* In user-mode we can get better hashing because we do not have a TLB */ -static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc) +static inline unsigned int tb_jmp_cache_hash_func(vaddr pc) { return (pc ^ (pc >> TB_JMP_CACHE_BITS)) & (TB_JMP_CACHE_SIZE - 1); } @@ -60,10 +61,10 @@ static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc) #endif /* CONFIG_SOFTMMU */ static inline -uint32_t tb_hash_func(tb_page_addr_t phys_pc, target_ulong pc, uint32_t flags, - uint32_t cf_mask, uint32_t trace_vcpu_dstate) +uint32_t tb_hash_func(tb_page_addr_t phys_pc, vaddr pc, + uint32_t flags, uint64_t flags2, uint32_t cf_mask) { - return qemu_xxhash7(phys_pc, pc, flags, cf_mask, trace_vcpu_dstate); + return qemu_xxhash8(phys_pc, pc, flags2, flags, cf_mask); } #endif diff --git a/accel/tcg/tb-jmp-cache.h b/accel/tcg/tb-jmp-cache.h new file mode 100644 index 0000000000..4ab8553afc --- /dev/null +++ b/accel/tcg/tb-jmp-cache.h @@ -0,0 +1,30 @@ +/* + * The per-CPU TranslationBlock jump cache. + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ACCEL_TCG_TB_JMP_CACHE_H +#define ACCEL_TCG_TB_JMP_CACHE_H + +#define TB_JMP_CACHE_BITS 12 +#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS) + +/* + * Invalidated in parallel; all accesses to 'tb' must be atomic. + * A valid entry is read/written by a single CPU, therefore there is + * no need for qatomic_rcu_read() and pc is always consistent with a + * non-NULL value of 'tb'. Strictly speaking pc is only needed for + * CF_PCREL, but it's used always for simplicity. + */ +struct CPUJumpCache { + struct rcu_head rcu; + struct { + TranslationBlock *tb; + vaddr pc; + } array[TB_JMP_CACHE_SIZE]; +}; + +#endif /* ACCEL_TCG_TB_JMP_CACHE_H */ diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c new file mode 100644 index 0000000000..da39a43bd8 --- /dev/null +++ b/accel/tcg/tb-maint.c @@ -0,0 +1,1229 @@ +/* + * Translation Block Maintenance + * + * 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.1 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/interval-tree.h" +#include "qemu/qtree.h" +#include "exec/cputlb.h" +#include "exec/log.h" +#include "exec/exec-all.h" +#include "exec/tb-flush.h" +#include "exec/translate-all.h" +#include "sysemu/tcg.h" +#include "tcg/tcg.h" +#include "tb-hash.h" +#include "tb-context.h" +#include "internal-common.h" +#include "internal-target.h" + + +/* List iterators for lists of tagged pointers in TranslationBlock. */ +#define TB_FOR_EACH_TAGGED(head, tb, n, field) \ + for (n = (head) & 1, tb = (TranslationBlock *)((head) & ~1); \ + tb; tb = (TranslationBlock *)tb->field[n], n = (uintptr_t)tb & 1, \ + tb = (TranslationBlock *)((uintptr_t)tb & ~1)) + +#define TB_FOR_EACH_JMP(head_tb, tb, n) \ + TB_FOR_EACH_TAGGED((head_tb)->jmp_list_head, tb, n, jmp_list_next) + +static bool tb_cmp(const void *ap, const void *bp) +{ + const TranslationBlock *a = ap; + const TranslationBlock *b = bp; + + return ((tb_cflags(a) & CF_PCREL || a->pc == b->pc) && + a->cs_base == b->cs_base && + a->flags == b->flags && + (tb_cflags(a) & ~CF_INVALID) == (tb_cflags(b) & ~CF_INVALID) && + tb_page_addr0(a) == tb_page_addr0(b) && + tb_page_addr1(a) == tb_page_addr1(b)); +} + +void tb_htable_init(void) +{ + unsigned int mode = QHT_MODE_AUTO_RESIZE; + + qht_init(&tb_ctx.htable, tb_cmp, CODE_GEN_HTABLE_SIZE, mode); +} + +typedef struct PageDesc PageDesc; + +#ifdef CONFIG_USER_ONLY + +/* + * In user-mode page locks aren't used; mmap_lock is enough. + */ +#define assert_page_locked(pd) tcg_debug_assert(have_mmap_lock()) + +static inline void tb_lock_pages(const TranslationBlock *tb) { } + +/* + * For user-only, since we are protecting all of memory with a single lock, + * and because the two pages of a TranslationBlock are always contiguous, + * use a single data structure to record all TranslationBlocks. + */ +static IntervalTreeRoot tb_root; + +static void tb_remove_all(void) +{ + assert_memory_lock(); + memset(&tb_root, 0, sizeof(tb_root)); +} + +/* Call with mmap_lock held. */ +static void tb_record(TranslationBlock *tb) +{ + vaddr addr; + int flags; + + assert_memory_lock(); + tb->itree.last = tb->itree.start + tb->size - 1; + + /* translator_loop() must have made all TB pages non-writable */ + addr = tb_page_addr0(tb); + flags = page_get_flags(addr); + assert(!(flags & PAGE_WRITE)); + + addr = tb_page_addr1(tb); + if (addr != -1) { + flags = page_get_flags(addr); + assert(!(flags & PAGE_WRITE)); + } + + interval_tree_insert(&tb->itree, &tb_root); +} + +/* Call with mmap_lock held. */ +static void tb_remove(TranslationBlock *tb) +{ + assert_memory_lock(); + interval_tree_remove(&tb->itree, &tb_root); +} + +/* TODO: For now, still shared with translate-all.c for system mode. */ +#define PAGE_FOR_EACH_TB(start, last, pagedesc, T, N) \ + for (T = foreach_tb_first(start, last), \ + N = foreach_tb_next(T, start, last); \ + T != NULL; \ + T = N, N = foreach_tb_next(N, start, last)) + +typedef TranslationBlock *PageForEachNext; + +static PageForEachNext foreach_tb_first(tb_page_addr_t start, + tb_page_addr_t last) +{ + IntervalTreeNode *n = interval_tree_iter_first(&tb_root, start, last); + return n ? container_of(n, TranslationBlock, itree) : NULL; +} + +static PageForEachNext foreach_tb_next(PageForEachNext tb, + tb_page_addr_t start, + tb_page_addr_t last) +{ + IntervalTreeNode *n; + + if (tb) { + n = interval_tree_iter_next(&tb->itree, start, last); + if (n) { + return container_of(n, TranslationBlock, itree); + } + } + return NULL; +} + +#else +/* + * In system mode we want L1_MAP to be based on ram offsets. + */ +#if HOST_LONG_BITS < TARGET_PHYS_ADDR_SPACE_BITS +# define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS +#else +# define L1_MAP_ADDR_SPACE_BITS TARGET_PHYS_ADDR_SPACE_BITS +#endif + +/* Size of the L2 (and L3, etc) page tables. */ +#define V_L2_BITS 10 +#define V_L2_SIZE (1 << V_L2_BITS) + +/* + * L1 Mapping properties + */ +static int v_l1_size; +static int v_l1_shift; +static int v_l2_levels; + +/* + * The bottom level has pointers to PageDesc, and is indexed by + * anything from 4 to (V_L2_BITS + 3) bits, depending on target page size. + */ +#define V_L1_MIN_BITS 4 +#define V_L1_MAX_BITS (V_L2_BITS + 3) +#define V_L1_MAX_SIZE (1 << V_L1_MAX_BITS) + +static void *l1_map[V_L1_MAX_SIZE]; + +struct PageDesc { + QemuSpin lock; + /* list of TBs intersecting this ram page */ + uintptr_t first_tb; +}; + +void page_table_config_init(void) +{ + uint32_t v_l1_bits; + + assert(TARGET_PAGE_BITS); + /* The bits remaining after N lower levels of page tables. */ + v_l1_bits = (L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % V_L2_BITS; + if (v_l1_bits < V_L1_MIN_BITS) { + v_l1_bits += V_L2_BITS; + } + + v_l1_size = 1 << v_l1_bits; + v_l1_shift = L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS - v_l1_bits; + v_l2_levels = v_l1_shift / V_L2_BITS - 1; + + assert(v_l1_bits <= V_L1_MAX_BITS); + assert(v_l1_shift % V_L2_BITS == 0); + assert(v_l2_levels >= 0); +} + +static PageDesc *page_find_alloc(tb_page_addr_t index, bool alloc) +{ + PageDesc *pd; + void **lp; + + /* Level 1. Always allocated. */ + lp = l1_map + ((index >> v_l1_shift) & (v_l1_size - 1)); + + /* Level 2..N-1. */ + for (int i = v_l2_levels; i > 0; i--) { + void **p = qatomic_rcu_read(lp); + + if (p == NULL) { + void *existing; + + if (!alloc) { + return NULL; + } + p = g_new0(void *, V_L2_SIZE); + existing = qatomic_cmpxchg(lp, NULL, p); + if (unlikely(existing)) { + g_free(p); + p = existing; + } + } + + lp = p + ((index >> (i * V_L2_BITS)) & (V_L2_SIZE - 1)); + } + + pd = qatomic_rcu_read(lp); + if (pd == NULL) { + void *existing; + + if (!alloc) { + return NULL; + } + + pd = g_new0(PageDesc, V_L2_SIZE); + for (int i = 0; i < V_L2_SIZE; i++) { + qemu_spin_init(&pd[i].lock); + } + + existing = qatomic_cmpxchg(lp, NULL, pd); + if (unlikely(existing)) { + for (int i = 0; i < V_L2_SIZE; i++) { + qemu_spin_destroy(&pd[i].lock); + } + g_free(pd); + pd = existing; + } + } + + return pd + (index & (V_L2_SIZE - 1)); +} + +static inline PageDesc *page_find(tb_page_addr_t index) +{ + return page_find_alloc(index, false); +} + +/** + * struct page_entry - page descriptor entry + * @pd: pointer to the &struct PageDesc of the page this entry represents + * @index: page index of the page + * @locked: whether the page is locked + * + * This struct helps us keep track of the locked state of a page, without + * bloating &struct PageDesc. + * + * A page lock protects accesses to all fields of &struct PageDesc. + * + * See also: &struct page_collection. + */ +struct page_entry { + PageDesc *pd; + tb_page_addr_t index; + bool locked; +}; + +/** + * struct page_collection - tracks a set of pages (i.e. &struct page_entry's) + * @tree: Binary search tree (BST) of the pages, with key == page index + * @max: Pointer to the page in @tree with the highest page index + * + * To avoid deadlock we lock pages in ascending order of page index. + * When operating on a set of pages, we need to keep track of them so that + * we can lock them in order and also unlock them later. For this we collect + * pages (i.e. &struct page_entry's) in a binary search @tree. Given that the + * @tree implementation we use does not provide an O(1) operation to obtain the + * highest-ranked element, we use @max to keep track of the inserted page + * with the highest index. This is valuable because if a page is not in + * the tree and its index is higher than @max's, then we can lock it + * without breaking the locking order rule. + * + * Note on naming: 'struct page_set' would be shorter, but we already have a few + * page_set_*() helpers, so page_collection is used instead to avoid confusion. + * + * See also: page_collection_lock(). + */ +struct page_collection { + QTree *tree; + struct page_entry *max; +}; + +typedef int PageForEachNext; +#define PAGE_FOR_EACH_TB(start, last, pagedesc, tb, n) \ + TB_FOR_EACH_TAGGED((pagedesc)->first_tb, tb, n, page_next) + +#ifdef CONFIG_DEBUG_TCG + +static __thread GHashTable *ht_pages_locked_debug; + +static void ht_pages_locked_debug_init(void) +{ + if (ht_pages_locked_debug) { + return; + } + ht_pages_locked_debug = g_hash_table_new(NULL, NULL); +} + +static bool page_is_locked(const PageDesc *pd) +{ + PageDesc *found; + + ht_pages_locked_debug_init(); + found = g_hash_table_lookup(ht_pages_locked_debug, pd); + return !!found; +} + +static void page_lock__debug(PageDesc *pd) +{ + ht_pages_locked_debug_init(); + g_assert(!page_is_locked(pd)); + g_hash_table_insert(ht_pages_locked_debug, pd, pd); +} + +static void page_unlock__debug(const PageDesc *pd) +{ + bool removed; + + ht_pages_locked_debug_init(); + g_assert(page_is_locked(pd)); + removed = g_hash_table_remove(ht_pages_locked_debug, pd); + g_assert(removed); +} + +static void do_assert_page_locked(const PageDesc *pd, + const char *file, int line) +{ + if (unlikely(!page_is_locked(pd))) { + error_report("assert_page_lock: PageDesc %p not locked @ %s:%d", + pd, file, line); + abort(); + } +} +#define assert_page_locked(pd) do_assert_page_locked(pd, __FILE__, __LINE__) + +void assert_no_pages_locked(void) +{ + ht_pages_locked_debug_init(); + g_assert(g_hash_table_size(ht_pages_locked_debug) == 0); +} + +#else /* !CONFIG_DEBUG_TCG */ + +static inline void page_lock__debug(const PageDesc *pd) { } +static inline void page_unlock__debug(const PageDesc *pd) { } +static inline void assert_page_locked(const PageDesc *pd) { } + +#endif /* CONFIG_DEBUG_TCG */ + +static void page_lock(PageDesc *pd) +{ + page_lock__debug(pd); + qemu_spin_lock(&pd->lock); +} + +/* Like qemu_spin_trylock, returns false on success */ +static bool page_trylock(PageDesc *pd) +{ + bool busy = qemu_spin_trylock(&pd->lock); + if (!busy) { + page_lock__debug(pd); + } + return busy; +} + +static void page_unlock(PageDesc *pd) +{ + qemu_spin_unlock(&pd->lock); + page_unlock__debug(pd); +} + +void tb_lock_page0(tb_page_addr_t paddr) +{ + page_lock(page_find_alloc(paddr >> TARGET_PAGE_BITS, true)); +} + +void tb_lock_page1(tb_page_addr_t paddr0, tb_page_addr_t paddr1) +{ + tb_page_addr_t pindex0 = paddr0 >> TARGET_PAGE_BITS; + tb_page_addr_t pindex1 = paddr1 >> TARGET_PAGE_BITS; + PageDesc *pd0, *pd1; + + if (pindex0 == pindex1) { + /* Identical pages, and the first page is already locked. */ + return; + } + + pd1 = page_find_alloc(pindex1, true); + if (pindex0 < pindex1) { + /* Correct locking order, we may block. */ + page_lock(pd1); + return; + } + + /* Incorrect locking order, we cannot block lest we deadlock. */ + if (!page_trylock(pd1)) { + return; + } + + /* + * Drop the lock on page0 and get both page locks in the right order. + * Restart translation via longjmp. + */ + pd0 = page_find_alloc(pindex0, false); + page_unlock(pd0); + page_lock(pd1); + page_lock(pd0); + siglongjmp(tcg_ctx->jmp_trans, -3); +} + +void tb_unlock_page1(tb_page_addr_t paddr0, tb_page_addr_t paddr1) +{ + tb_page_addr_t pindex0 = paddr0 >> TARGET_PAGE_BITS; + tb_page_addr_t pindex1 = paddr1 >> TARGET_PAGE_BITS; + + if (pindex0 != pindex1) { + page_unlock(page_find_alloc(pindex1, false)); + } +} + +static void tb_lock_pages(TranslationBlock *tb) +{ + tb_page_addr_t paddr0 = tb_page_addr0(tb); + tb_page_addr_t paddr1 = tb_page_addr1(tb); + tb_page_addr_t pindex0 = paddr0 >> TARGET_PAGE_BITS; + tb_page_addr_t pindex1 = paddr1 >> TARGET_PAGE_BITS; + + if (unlikely(paddr0 == -1)) { + return; + } + if (unlikely(paddr1 != -1) && pindex0 != pindex1) { + if (pindex0 < pindex1) { + page_lock(page_find_alloc(pindex0, true)); + page_lock(page_find_alloc(pindex1, true)); + return; + } + page_lock(page_find_alloc(pindex1, true)); + } + page_lock(page_find_alloc(pindex0, true)); +} + +void tb_unlock_pages(TranslationBlock *tb) +{ + tb_page_addr_t paddr0 = tb_page_addr0(tb); + tb_page_addr_t paddr1 = tb_page_addr1(tb); + tb_page_addr_t pindex0 = paddr0 >> TARGET_PAGE_BITS; + tb_page_addr_t pindex1 = paddr1 >> TARGET_PAGE_BITS; + + if (unlikely(paddr0 == -1)) { + return; + } + if (unlikely(paddr1 != -1) && pindex0 != pindex1) { + page_unlock(page_find_alloc(pindex1, false)); + } + page_unlock(page_find_alloc(pindex0, false)); +} + +static inline struct page_entry * +page_entry_new(PageDesc *pd, tb_page_addr_t index) +{ + struct page_entry *pe = g_malloc(sizeof(*pe)); + + pe->index = index; + pe->pd = pd; + pe->locked = false; + return pe; +} + +static void page_entry_destroy(gpointer p) +{ + struct page_entry *pe = p; + + g_assert(pe->locked); + page_unlock(pe->pd); + g_free(pe); +} + +/* returns false on success */ +static bool page_entry_trylock(struct page_entry *pe) +{ + bool busy = page_trylock(pe->pd); + if (!busy) { + g_assert(!pe->locked); + pe->locked = true; + } + return busy; +} + +static void do_page_entry_lock(struct page_entry *pe) +{ + page_lock(pe->pd); + g_assert(!pe->locked); + pe->locked = true; +} + +static gboolean page_entry_lock(gpointer key, gpointer value, gpointer data) +{ + struct page_entry *pe = value; + + do_page_entry_lock(pe); + return FALSE; +} + +static gboolean page_entry_unlock(gpointer key, gpointer value, gpointer data) +{ + struct page_entry *pe = value; + + if (pe->locked) { + pe->locked = false; + page_unlock(pe->pd); + } + return FALSE; +} + +/* + * Trylock a page, and if successful, add the page to a collection. + * Returns true ("busy") if the page could not be locked; false otherwise. + */ +static bool page_trylock_add(struct page_collection *set, tb_page_addr_t addr) +{ + tb_page_addr_t index = addr >> TARGET_PAGE_BITS; + struct page_entry *pe; + PageDesc *pd; + + pe = q_tree_lookup(set->tree, &index); + if (pe) { + return false; + } + + pd = page_find(index); + if (pd == NULL) { + return false; + } + + pe = page_entry_new(pd, index); + q_tree_insert(set->tree, &pe->index, pe); + + /* + * If this is either (1) the first insertion or (2) a page whose index + * is higher than any other so far, just lock the page and move on. + */ + if (set->max == NULL || pe->index > set->max->index) { + set->max = pe; + do_page_entry_lock(pe); + return false; + } + /* + * Try to acquire out-of-order lock; if busy, return busy so that we acquire + * locks in order. + */ + return page_entry_trylock(pe); +} + +static gint tb_page_addr_cmp(gconstpointer ap, gconstpointer bp, gpointer udata) +{ + tb_page_addr_t a = *(const tb_page_addr_t *)ap; + tb_page_addr_t b = *(const tb_page_addr_t *)bp; + + if (a == b) { + return 0; + } else if (a < b) { + return -1; + } + return 1; +} + +/* + * Lock a range of pages ([@start,@last]) as well as the pages of all + * intersecting TBs. + * Locking order: acquire locks in ascending order of page index. + */ +static struct page_collection *page_collection_lock(tb_page_addr_t start, + tb_page_addr_t last) +{ + struct page_collection *set = g_malloc(sizeof(*set)); + tb_page_addr_t index; + PageDesc *pd; + + start >>= TARGET_PAGE_BITS; + last >>= TARGET_PAGE_BITS; + g_assert(start <= last); + + set->tree = q_tree_new_full(tb_page_addr_cmp, NULL, NULL, + page_entry_destroy); + set->max = NULL; + assert_no_pages_locked(); + + retry: + q_tree_foreach(set->tree, page_entry_lock, NULL); + + for (index = start; index <= last; index++) { + TranslationBlock *tb; + PageForEachNext n; + + pd = page_find(index); + if (pd == NULL) { + continue; + } + if (page_trylock_add(set, index << TARGET_PAGE_BITS)) { + q_tree_foreach(set->tree, page_entry_unlock, NULL); + goto retry; + } + assert_page_locked(pd); + PAGE_FOR_EACH_TB(unused, unused, pd, tb, n) { + if (page_trylock_add(set, tb_page_addr0(tb)) || + (tb_page_addr1(tb) != -1 && + page_trylock_add(set, tb_page_addr1(tb)))) { + /* drop all locks, and reacquire in order */ + q_tree_foreach(set->tree, page_entry_unlock, NULL); + goto retry; + } + } + } + return set; +} + +static void page_collection_unlock(struct page_collection *set) +{ + /* entries are unlocked and freed via page_entry_destroy */ + q_tree_destroy(set->tree); + g_free(set); +} + +/* Set to NULL all the 'first_tb' fields in all PageDescs. */ +static void tb_remove_all_1(int level, void **lp) +{ + int i; + + if (*lp == NULL) { + return; + } + if (level == 0) { + PageDesc *pd = *lp; + + for (i = 0; i < V_L2_SIZE; ++i) { + page_lock(&pd[i]); + pd[i].first_tb = (uintptr_t)NULL; + page_unlock(&pd[i]); + } + } else { + void **pp = *lp; + + for (i = 0; i < V_L2_SIZE; ++i) { + tb_remove_all_1(level - 1, pp + i); + } + } +} + +static void tb_remove_all(void) +{ + int i, l1_sz = v_l1_size; + + for (i = 0; i < l1_sz; i++) { + tb_remove_all_1(v_l2_levels, l1_map + i); + } +} + +/* + * Add the tb in the target page and protect it if necessary. + * Called with @p->lock held. + */ +static void tb_page_add(PageDesc *p, TranslationBlock *tb, unsigned int n) +{ + bool page_already_protected; + + assert_page_locked(p); + + tb->page_next[n] = p->first_tb; + page_already_protected = p->first_tb != 0; + p->first_tb = (uintptr_t)tb | n; + + /* + * If some code is already present, then the pages are already + * protected. So we handle the case where only the first TB is + * allocated in a physical page. + */ + if (!page_already_protected) { + tlb_protect_code(tb->page_addr[n] & TARGET_PAGE_MASK); + } +} + +static void tb_record(TranslationBlock *tb) +{ + tb_page_addr_t paddr0 = tb_page_addr0(tb); + tb_page_addr_t paddr1 = tb_page_addr1(tb); + tb_page_addr_t pindex0 = paddr0 >> TARGET_PAGE_BITS; + tb_page_addr_t pindex1 = paddr0 >> TARGET_PAGE_BITS; + + assert(paddr0 != -1); + if (unlikely(paddr1 != -1) && pindex0 != pindex1) { + tb_page_add(page_find_alloc(pindex1, false), tb, 1); + } + tb_page_add(page_find_alloc(pindex0, false), tb, 0); +} + +static void tb_page_remove(PageDesc *pd, TranslationBlock *tb) +{ + TranslationBlock *tb1; + uintptr_t *pprev; + PageForEachNext n1; + + assert_page_locked(pd); + pprev = &pd->first_tb; + PAGE_FOR_EACH_TB(unused, unused, pd, tb1, n1) { + if (tb1 == tb) { + *pprev = tb1->page_next[n1]; + return; + } + pprev = &tb1->page_next[n1]; + } + g_assert_not_reached(); +} + +static void tb_remove(TranslationBlock *tb) +{ + tb_page_addr_t paddr0 = tb_page_addr0(tb); + tb_page_addr_t paddr1 = tb_page_addr1(tb); + tb_page_addr_t pindex0 = paddr0 >> TARGET_PAGE_BITS; + tb_page_addr_t pindex1 = paddr0 >> TARGET_PAGE_BITS; + + assert(paddr0 != -1); + if (unlikely(paddr1 != -1) && pindex0 != pindex1) { + tb_page_remove(page_find_alloc(pindex1, false), tb); + } + tb_page_remove(page_find_alloc(pindex0, false), tb); +} +#endif /* CONFIG_USER_ONLY */ + +/* flush all the translation blocks */ +static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) +{ + bool did_flush = false; + + mmap_lock(); + /* If it is already been done on request of another CPU, just retry. */ + if (tb_ctx.tb_flush_count != tb_flush_count.host_int) { + goto done; + } + did_flush = true; + + CPU_FOREACH(cpu) { + tcg_flush_jmp_cache(cpu); + } + + qht_reset_size(&tb_ctx.htable, CODE_GEN_HTABLE_SIZE); + tb_remove_all(); + + tcg_region_reset_all(); + /* XXX: flush processor icache at this point if cache flush is expensive */ + qatomic_inc(&tb_ctx.tb_flush_count); + +done: + mmap_unlock(); + if (did_flush) { + qemu_plugin_flush_cb(); + } +} + +void tb_flush(CPUState *cpu) +{ + if (tcg_enabled()) { + unsigned tb_flush_count = qatomic_read(&tb_ctx.tb_flush_count); + + if (cpu_in_serial_context(cpu)) { + do_tb_flush(cpu, RUN_ON_CPU_HOST_INT(tb_flush_count)); + } else { + async_safe_run_on_cpu(cpu, do_tb_flush, + RUN_ON_CPU_HOST_INT(tb_flush_count)); + } + } +} + +/* remove @orig from its @n_orig-th jump list */ +static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig) +{ + uintptr_t ptr, ptr_locked; + TranslationBlock *dest; + TranslationBlock *tb; + uintptr_t *pprev; + int n; + + /* mark the LSB of jmp_dest[] so that no further jumps can be inserted */ + ptr = qatomic_or_fetch(&orig->jmp_dest[n_orig], 1); + dest = (TranslationBlock *)(ptr & ~1); + if (dest == NULL) { + return; + } + + qemu_spin_lock(&dest->jmp_lock); + /* + * While acquiring the lock, the jump might have been removed if the + * destination TB was invalidated; check again. + */ + ptr_locked = qatomic_read(&orig->jmp_dest[n_orig]); + if (ptr_locked != ptr) { + qemu_spin_unlock(&dest->jmp_lock); + /* + * The only possibility is that the jump was unlinked via + * tb_jump_unlink(dest). Seeing here another destination would be a bug, + * because we set the LSB above. + */ + g_assert(ptr_locked == 1 && dest->cflags & CF_INVALID); + return; + } + /* + * We first acquired the lock, and since the destination pointer matches, + * we know for sure that @orig is in the jmp list. + */ + pprev = &dest->jmp_list_head; + TB_FOR_EACH_JMP(dest, tb, n) { + if (tb == orig && n == n_orig) { + *pprev = tb->jmp_list_next[n]; + /* no need to set orig->jmp_dest[n]; setting the LSB was enough */ + qemu_spin_unlock(&dest->jmp_lock); + return; + } + pprev = &tb->jmp_list_next[n]; + } + g_assert_not_reached(); +} + +/* + * Reset the jump entry 'n' of a TB so that it is not chained to another TB. + */ +void tb_reset_jump(TranslationBlock *tb, int n) +{ + uintptr_t addr = (uintptr_t)(tb->tc.ptr + tb->jmp_reset_offset[n]); + tb_set_jmp_target(tb, n, addr); +} + +/* remove any jumps to the TB */ +static inline void tb_jmp_unlink(TranslationBlock *dest) +{ + TranslationBlock *tb; + int n; + + qemu_spin_lock(&dest->jmp_lock); + + TB_FOR_EACH_JMP(dest, tb, n) { + tb_reset_jump(tb, n); + qatomic_and(&tb->jmp_dest[n], (uintptr_t)NULL | 1); + /* No need to clear the list entry; setting the dest ptr is enough */ + } + dest->jmp_list_head = (uintptr_t)NULL; + + qemu_spin_unlock(&dest->jmp_lock); +} + +static void tb_jmp_cache_inval_tb(TranslationBlock *tb) +{ + CPUState *cpu; + + if (tb_cflags(tb) & CF_PCREL) { + /* A TB may be at any virtual address */ + CPU_FOREACH(cpu) { + tcg_flush_jmp_cache(cpu); + } + } else { + uint32_t h = tb_jmp_cache_hash_func(tb->pc); + + CPU_FOREACH(cpu) { + CPUJumpCache *jc = cpu->tb_jmp_cache; + + if (qatomic_read(&jc->array[h].tb) == tb) { + qatomic_set(&jc->array[h].tb, NULL); + } + } + } +} + +/* + * In user-mode, call with mmap_lock held. + * In !user-mode, if @rm_from_page_list is set, call with the TB's pages' + * locks held. + */ +static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) +{ + uint32_t h; + tb_page_addr_t phys_pc; + uint32_t orig_cflags = tb_cflags(tb); + + assert_memory_lock(); + + /* make sure no further incoming jumps will be chained to this TB */ + qemu_spin_lock(&tb->jmp_lock); + qatomic_set(&tb->cflags, tb->cflags | CF_INVALID); + qemu_spin_unlock(&tb->jmp_lock); + + /* remove the TB from the hash list */ + phys_pc = tb_page_addr0(tb); + h = tb_hash_func(phys_pc, (orig_cflags & CF_PCREL ? 0 : tb->pc), + tb->flags, tb->cs_base, orig_cflags); + if (!qht_remove(&tb_ctx.htable, tb, h)) { + return; + } + + /* remove the TB from the page list */ + if (rm_from_page_list) { + tb_remove(tb); + } + + /* remove the TB from the hash list */ + tb_jmp_cache_inval_tb(tb); + + /* suppress this TB from the two jump lists */ + tb_remove_from_jmp_list(tb, 0); + tb_remove_from_jmp_list(tb, 1); + + /* suppress any remaining jumps to this TB */ + tb_jmp_unlink(tb); + + qatomic_set(&tb_ctx.tb_phys_invalidate_count, + tb_ctx.tb_phys_invalidate_count + 1); +} + +static void tb_phys_invalidate__locked(TranslationBlock *tb) +{ + qemu_thread_jit_write(); + do_tb_phys_invalidate(tb, true); + qemu_thread_jit_execute(); +} + +/* + * Invalidate one TB. + * Called with mmap_lock held in user-mode. + */ +void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) +{ + if (page_addr == -1 && tb_page_addr0(tb) != -1) { + tb_lock_pages(tb); + do_tb_phys_invalidate(tb, true); + tb_unlock_pages(tb); + } else { + do_tb_phys_invalidate(tb, false); + } +} + +/* + * Add a new TB and link it to the physical page tables. + * Called with mmap_lock held for user-mode emulation. + * + * Returns a pointer @tb, or a pointer to an existing TB that matches @tb. + * Note that in !user-mode, another thread might have already added a TB + * for the same block of guest code that @tb corresponds to. In that case, + * the caller should discard the original @tb, and use instead the returned TB. + */ +TranslationBlock *tb_link_page(TranslationBlock *tb) +{ + void *existing_tb = NULL; + uint32_t h; + + assert_memory_lock(); + tcg_debug_assert(!(tb->cflags & CF_INVALID)); + + tb_record(tb); + + /* add in the hash table */ + h = tb_hash_func(tb_page_addr0(tb), (tb->cflags & CF_PCREL ? 0 : tb->pc), + tb->flags, tb->cs_base, tb->cflags); + qht_insert(&tb_ctx.htable, tb, h, &existing_tb); + + /* remove TB from the page(s) if we couldn't insert it */ + if (unlikely(existing_tb)) { + tb_remove(tb); + tb_unlock_pages(tb); + return existing_tb; + } + + tb_unlock_pages(tb); + return tb; +} + +#ifdef CONFIG_USER_ONLY +/* + * Invalidate all TBs which intersect with the target address range. + * Called with mmap_lock held for user-mode emulation. + * NOTE: this function must not be called while a TB is running. + */ +void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last) +{ + TranslationBlock *tb; + PageForEachNext n; + + assert_memory_lock(); + + PAGE_FOR_EACH_TB(start, last, unused, tb, n) { + tb_phys_invalidate__locked(tb); + } +} + +/* + * Invalidate all TBs which intersect with the target address page @addr. + * Called with mmap_lock held for user-mode emulation + * NOTE: this function must not be called while a TB is running. + */ +static void tb_invalidate_phys_page(tb_page_addr_t addr) +{ + tb_page_addr_t start, last; + + start = addr & TARGET_PAGE_MASK; + last = addr | ~TARGET_PAGE_MASK; + tb_invalidate_phys_range(start, last); +} + +/* + * Called with mmap_lock held. If pc is not 0 then it indicates the + * host PC of the faulting store instruction that caused this invalidate. + * Returns true if the caller needs to abort execution of the current + * TB (because it was modified by this store and the guest CPU has + * precise-SMC semantics). + */ +bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc) +{ + TranslationBlock *current_tb; + bool current_tb_modified; + TranslationBlock *tb; + PageForEachNext n; + tb_page_addr_t last; + + /* + * Without precise smc semantics, or when outside of a TB, + * we can skip to invalidate. + */ +#ifndef TARGET_HAS_PRECISE_SMC + pc = 0; +#endif + if (!pc) { + tb_invalidate_phys_page(addr); + return false; + } + + assert_memory_lock(); + current_tb = tcg_tb_lookup(pc); + + last = addr | ~TARGET_PAGE_MASK; + addr &= TARGET_PAGE_MASK; + current_tb_modified = false; + + PAGE_FOR_EACH_TB(addr, last, unused, tb, n) { + if (current_tb == tb && + (tb_cflags(current_tb) & CF_COUNT_MASK) != 1) { + /* + * If we are modifying the current TB, we must stop its + * execution. We could be more precise by checking that + * the modification is after the current PC, but it would + * require a specialized function to partially restore + * the CPU state. + */ + current_tb_modified = true; + cpu_restore_state_from_tb(current_cpu, current_tb, pc); + } + tb_phys_invalidate__locked(tb); + } + + if (current_tb_modified) { + /* Force execution of one insn next time. */ + CPUState *cpu = current_cpu; + cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(current_cpu); + return true; + } + return false; +} +#else +/* + * @p must be non-NULL. + * Call with all @pages locked. + */ +static void +tb_invalidate_phys_page_range__locked(struct page_collection *pages, + PageDesc *p, tb_page_addr_t start, + tb_page_addr_t last, + uintptr_t retaddr) +{ + TranslationBlock *tb; + PageForEachNext n; +#ifdef TARGET_HAS_PRECISE_SMC + bool current_tb_modified = false; + TranslationBlock *current_tb = retaddr ? tcg_tb_lookup(retaddr) : NULL; +#endif /* TARGET_HAS_PRECISE_SMC */ + + /* Range may not cross a page. */ + tcg_debug_assert(((start ^ last) & TARGET_PAGE_MASK) == 0); + + /* + * We remove all the TBs in the range [start, last]. + * XXX: see if in some cases it could be faster to invalidate all the code + */ + PAGE_FOR_EACH_TB(start, last, p, tb, n) { + tb_page_addr_t tb_start, tb_last; + + /* NOTE: this is subtle as a TB may span two physical pages */ + tb_start = tb_page_addr0(tb); + tb_last = tb_start + tb->size - 1; + if (n == 0) { + tb_last = MIN(tb_last, tb_start | ~TARGET_PAGE_MASK); + } else { + tb_start = tb_page_addr1(tb); + tb_last = tb_start + (tb_last & ~TARGET_PAGE_MASK); + } + if (!(tb_last < start || tb_start > last)) { +#ifdef TARGET_HAS_PRECISE_SMC + if (current_tb == tb && + (tb_cflags(current_tb) & CF_COUNT_MASK) != 1) { + /* + * If we are modifying the current TB, we must stop + * its execution. We could be more precise by checking + * that the modification is after the current PC, but it + * would require a specialized function to partially + * restore the CPU state. + */ + current_tb_modified = true; + cpu_restore_state_from_tb(current_cpu, current_tb, retaddr); + } +#endif /* TARGET_HAS_PRECISE_SMC */ + tb_phys_invalidate__locked(tb); + } + } + + /* if no code remaining, no need to continue to use slow writes */ + if (!p->first_tb) { + tlb_unprotect_code(start); + } + +#ifdef TARGET_HAS_PRECISE_SMC + if (current_tb_modified) { + page_collection_unlock(pages); + /* Force execution of one insn next time. */ + current_cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(current_cpu); + mmap_unlock(); + cpu_loop_exit_noexc(current_cpu); + } +#endif +} + +/* + * Invalidate all TBs which intersect with the target physical address range + * [start;last]. NOTE: start and end may refer to *different* physical pages. + * 'is_cpu_write_access' should be true if called from a real cpu write + * access: the virtual CPU will exit the current TB if code is modified inside + * this TB. + */ +void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last) +{ + struct page_collection *pages; + tb_page_addr_t index, index_last; + + pages = page_collection_lock(start, last); + + index_last = last >> TARGET_PAGE_BITS; + for (index = start >> TARGET_PAGE_BITS; index <= index_last; index++) { + PageDesc *pd = page_find(index); + tb_page_addr_t page_start, page_last; + + if (pd == NULL) { + continue; + } + assert_page_locked(pd); + page_start = index << TARGET_PAGE_BITS; + page_last = page_start | ~TARGET_PAGE_MASK; + page_last = MIN(page_last, last); + tb_invalidate_phys_page_range__locked(pages, pd, + page_start, page_last, 0); + } + page_collection_unlock(pages); +} + +/* + * Call with all @pages in the range [@start, @start + len[ locked. + */ +static void tb_invalidate_phys_page_fast__locked(struct page_collection *pages, + tb_page_addr_t start, + unsigned len, uintptr_t ra) +{ + PageDesc *p; + + p = page_find(start >> TARGET_PAGE_BITS); + if (!p) { + return; + } + + assert_page_locked(p); + tb_invalidate_phys_page_range__locked(pages, p, start, start + len - 1, ra); +} + +/* + * len must be <= 8 and start must be a multiple of len. + * Called via softmmu_template.h when code areas are written to with + * iothread mutex not held. + */ +void tb_invalidate_phys_range_fast(ram_addr_t ram_addr, + unsigned size, + uintptr_t retaddr) +{ + struct page_collection *pages; + + pages = page_collection_lock(ram_addr, ram_addr + size - 1); + tb_invalidate_phys_page_fast__locked(pages, ram_addr, size, retaddr); + page_collection_unlock(pages); +} + +#endif /* CONFIG_USER_ONLY */ diff --git a/accel/tcg/tcg-accel-ops-icount.c b/accel/tcg/tcg-accel-ops-icount.c index 8f1dda4344..9e1ae66f65 100644 --- a/accel/tcg/tcg-accel-ops-icount.c +++ b/accel/tcg/tcg-accel-ops-icount.c @@ -89,7 +89,20 @@ void icount_handle_deadline(void) } } -void icount_prepare_for_run(CPUState *cpu) +/* Distribute the budget evenly across all CPUs */ +int64_t icount_percpu_budget(int cpu_count) +{ + int64_t limit = icount_get_limit(); + int64_t timeslice = limit / cpu_count; + + if (timeslice == 0) { + timeslice = limit; + } + + return timeslice; +} + +void icount_prepare_for_run(CPUState *cpu, int64_t cpu_budget) { int insns_left; @@ -98,18 +111,24 @@ void icount_prepare_for_run(CPUState *cpu) * each vCPU execution. However u16.high can be raised * asynchronously by cpu_exit/cpu_interrupt/tcg_handle_interrupt */ - g_assert(cpu_neg(cpu)->icount_decr.u16.low == 0); + g_assert(cpu->neg.icount_decr.u16.low == 0); g_assert(cpu->icount_extra == 0); - cpu->icount_budget = icount_get_limit(); - insns_left = MIN(0xffff, cpu->icount_budget); - cpu_neg(cpu)->icount_decr.u16.low = insns_left; - cpu->icount_extra = cpu->icount_budget - insns_left; - replay_mutex_lock(); + cpu->icount_budget = MIN(icount_get_limit(), cpu_budget); + insns_left = MIN(0xffff, cpu->icount_budget); + cpu->neg.icount_decr.u16.low = insns_left; + cpu->icount_extra = cpu->icount_budget - insns_left; + if (cpu->icount_budget == 0) { + /* + * We're called without the BQL, so must take it while + * we're calling timer handlers. + */ + bql_lock(); icount_notify_aio_contexts(); + bql_unlock(); } } @@ -119,7 +138,7 @@ void icount_process_data(CPUState *cpu) icount_update(cpu); /* Reset the counters */ - cpu_neg(cpu)->icount_decr.u16.low = 0; + cpu->neg.icount_decr.u16.low = 0; cpu->icount_extra = 0; cpu->icount_budget = 0; @@ -134,7 +153,7 @@ void icount_handle_interrupt(CPUState *cpu, int mask) tcg_handle_interrupt(cpu, mask); if (qemu_cpu_is_self(cpu) && - !cpu->can_do_io + !cpu->neg.can_do_io && (mask & ~old_mask) != 0) { cpu_abort(cpu, "Raised interrupt while not in I/O function"); } diff --git a/accel/tcg/tcg-accel-ops-icount.h b/accel/tcg/tcg-accel-ops-icount.h index 1b6fd9c607..16a301b6dc 100644 --- a/accel/tcg/tcg-accel-ops-icount.h +++ b/accel/tcg/tcg-accel-ops-icount.h @@ -11,7 +11,8 @@ #define TCG_ACCEL_OPS_ICOUNT_H void icount_handle_deadline(void); -void icount_prepare_for_run(CPUState *cpu); +void icount_prepare_for_run(CPUState *cpu, int64_t cpu_budget); +int64_t icount_percpu_budget(int cpu_count); void icount_process_data(CPUState *cpu); void icount_handle_interrupt(CPUState *cpu, int mask); diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index d50239e0e2..c552b45b8e 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -32,7 +32,7 @@ #include "qemu/guest-random.h" #include "exec/exec-all.h" #include "hw/boards.h" - +#include "tcg/startup.h" #include "tcg-accel-ops.h" #include "tcg-accel-ops-mttcg.h" @@ -76,11 +76,11 @@ static void *mttcg_cpu_thread_fn(void *arg) rcu_add_force_rcu_notifier(&force_rcu.notifier); tcg_register_thread(); - qemu_mutex_lock_iothread(); + bql_lock(); qemu_thread_get_self(cpu->thread); cpu->thread_id = qemu_get_thread_id(); - cpu->can_do_io = 1; + cpu->neg.can_do_io = true; current_cpu = cpu; cpu_thread_signal_created(cpu); qemu_guest_random_seed_thread_part2(cpu->random_seed); @@ -91,40 +91,35 @@ static void *mttcg_cpu_thread_fn(void *arg) do { if (cpu_can_run(cpu)) { int r; - qemu_mutex_unlock_iothread(); - r = tcg_cpus_exec(cpu); - qemu_mutex_lock_iothread(); + bql_unlock(); + r = tcg_cpu_exec(cpu); + bql_lock(); switch (r) { case EXCP_DEBUG: cpu_handle_guest_debug(cpu); break; case EXCP_HALTED: /* - * during start-up the vCPU is reset and the thread is - * kicked several times. If we don't ensure we go back - * to sleep in the halted state we won't cleanly - * start-up when the vCPU is enabled. - * - * cpu->halted should ensure we sleep in wait_io_event + * Usually cpu->halted is set, but may have already been + * reset by another thread by the time we arrive here. */ - g_assert(cpu->halted); break; case EXCP_ATOMIC: - qemu_mutex_unlock_iothread(); + bql_unlock(); cpu_exec_step_atomic(cpu); - qemu_mutex_lock_iothread(); + bql_lock(); default: /* Ignore everything else? */ break; } } - qatomic_mb_set(&cpu->exit_request, 0); + qatomic_set_mb(&cpu->exit_request, 0); qemu_wait_io_event(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); - tcg_cpus_destroy(cpu); - qemu_mutex_unlock_iothread(); + tcg_cpu_destroy(cpu); + bql_unlock(); rcu_remove_force_rcu_notifier(&force_rcu.notifier); rcu_unregister_thread(); return NULL; @@ -152,8 +147,4 @@ void mttcg_start_vcpu_thread(CPUState *cpu) qemu_thread_create(cpu->thread, thread_name, mttcg_cpu_thread_fn, cpu, QEMU_THREAD_JOINABLE); - -#ifdef _WIN32 - cpu->hThread = qemu_thread_get_handle(cpu->thread); -#endif } diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 1a72149f0e..894e73e52c 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qemu/lockable.h" #include "sysemu/tcg.h" #include "sysemu/replay.h" #include "sysemu/cpu-timers.h" @@ -31,7 +32,7 @@ #include "qemu/notify.h" #include "qemu/guest-random.h" #include "exec/exec-all.h" - +#include "tcg/startup.h" #include "tcg-accel-ops.h" #include "tcg-accel-ops-rr.h" #include "tcg-accel-ops-icount.h" @@ -51,7 +52,7 @@ void rr_kick_vcpu_thread(CPUState *unused) * * The kick timer is responsible for moving single threaded vCPU * emulation on to the next vCPU. If more than one vCPU is running a - * timer event with force a cpu->exit so the next vCPU can get + * timer event we force a cpu->exit so the next vCPU can get * scheduled. * * The timer is removed if all vCPUs are idle and restarted again once @@ -71,11 +72,13 @@ static void rr_kick_next_cpu(void) { CPUState *cpu; do { - cpu = qatomic_mb_read(&rr_current_cpu); + cpu = qatomic_read(&rr_current_cpu); if (cpu) { cpu_exit(cpu); } - } while (cpu != qatomic_mb_read(&rr_current_cpu)); + /* Finish kicking this cpu before reading again. */ + smp_mb(); + } while (cpu != qatomic_read(&rr_current_cpu)); } static void rr_kick_thread(void *opaque) @@ -106,9 +109,9 @@ static void rr_wait_io_event(void) { CPUState *cpu; - while (all_cpu_threads_idle()) { + while (all_cpu_threads_idle() && replay_can_wait()) { rr_stop_kick_timer(); - qemu_cond_wait_iothread(first_cpu->halt_cond); + qemu_cond_wait_bql(first_cpu->halt_cond); } rr_start_kick_timer(); @@ -128,7 +131,7 @@ static void rr_deal_with_unplugged_cpus(void) CPU_FOREACH(cpu) { if (cpu->unplug && !cpu_can_run(cpu)) { - tcg_cpus_destroy(cpu); + tcg_cpu_destroy(cpu); break; } } @@ -139,6 +142,33 @@ static void rr_force_rcu(Notifier *notify, void *data) rr_kick_next_cpu(); } +/* + * Calculate the number of CPUs that we will process in a single iteration of + * the main CPU thread loop so that we can fairly distribute the instruction + * count across CPUs. + * + * The CPU count is cached based on the CPU list generation ID to avoid + * iterating the list every time. + */ +static int rr_cpu_count(void) +{ + static unsigned int last_gen_id = ~0; + static int cpu_count; + CPUState *cpu; + + QEMU_LOCK_GUARD(&qemu_cpu_list_lock); + + if (cpu_list_generation_id_get() != last_gen_id) { + cpu_count = 0; + CPU_FOREACH(cpu) { + ++cpu_count; + } + last_gen_id = cpu_list_generation_id_get(); + } + + return cpu_count; +} + /* * In the single-threaded case each vCPU is simulated in turn. If * there is more than a single vCPU we create a simple timer to kick @@ -158,17 +188,17 @@ static void *rr_cpu_thread_fn(void *arg) rcu_add_force_rcu_notifier(&force_rcu); tcg_register_thread(); - qemu_mutex_lock_iothread(); + bql_lock(); qemu_thread_get_self(cpu->thread); cpu->thread_id = qemu_get_thread_id(); - cpu->can_do_io = 1; + cpu->neg.can_do_io = true; cpu_thread_signal_created(cpu); qemu_guest_random_seed_thread_part2(cpu->random_seed); /* wait for initial kick-off after machine start */ while (first_cpu->stopped) { - qemu_cond_wait_iothread(first_cpu->halt_cond); + qemu_cond_wait_bql(first_cpu->halt_cond); /* process any pending work */ CPU_FOREACH(cpu) { @@ -185,11 +215,16 @@ static void *rr_cpu_thread_fn(void *arg) cpu->exit_request = 1; while (1) { - qemu_mutex_unlock_iothread(); + /* Only used for icount_enabled() */ + int64_t cpu_budget = 0; + + bql_unlock(); replay_mutex_lock(); - qemu_mutex_lock_iothread(); + bql_lock(); if (icount_enabled()) { + int cpu_count = rr_cpu_count(); + /* Account partial waits to QEMU_CLOCK_VIRTUAL. */ icount_account_warp_timer(); /* @@ -197,6 +232,8 @@ static void *rr_cpu_thread_fn(void *arg) * waking up the I/O thread and waiting for completion. */ icount_handle_deadline(); + + cpu_budget = icount_percpu_budget(cpu_count); } replay_mutex_unlock(); @@ -206,8 +243,9 @@ static void *rr_cpu_thread_fn(void *arg) } while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) { + /* Store rr_current_cpu before evaluating cpu_can_run(). */ + qatomic_set_mb(&rr_current_cpu, cpu); - qatomic_mb_set(&rr_current_cpu, cpu); current_cpu = cpu; qemu_clock_enable(QEMU_CLOCK_VIRTUAL, @@ -216,23 +254,23 @@ static void *rr_cpu_thread_fn(void *arg) if (cpu_can_run(cpu)) { int r; - qemu_mutex_unlock_iothread(); + bql_unlock(); if (icount_enabled()) { - icount_prepare_for_run(cpu); + icount_prepare_for_run(cpu, cpu_budget); } - r = tcg_cpus_exec(cpu); + r = tcg_cpu_exec(cpu); if (icount_enabled()) { icount_process_data(cpu); } - qemu_mutex_lock_iothread(); + bql_lock(); if (r == EXCP_DEBUG) { cpu_handle_guest_debug(cpu); break; } else if (r == EXCP_ATOMIC) { - qemu_mutex_unlock_iothread(); + bql_unlock(); cpu_exec_step_atomic(cpu); - qemu_mutex_lock_iothread(); + bql_lock(); break; } } else if (cpu->stop) { @@ -245,11 +283,11 @@ static void *rr_cpu_thread_fn(void *arg) cpu = CPU_NEXT(cpu); } /* while (cpu && !cpu->exit_request).. */ - /* Does not need qatomic_mb_set because a spurious wakeup is okay. */ + /* Does not need a memory barrier because a spurious wakeup is okay. */ qatomic_set(&rr_current_cpu, NULL); if (cpu && cpu->exit_request) { - qatomic_mb_set(&cpu->exit_request, 0); + qatomic_set_mb(&cpu->exit_request, 0); } if (icount_enabled() && all_cpu_threads_idle()) { @@ -291,15 +329,12 @@ void rr_start_vcpu_thread(CPUState *cpu) single_tcg_halt_cond = cpu->halt_cond; single_tcg_cpu_thread = cpu->thread; -#ifdef _WIN32 - cpu->hThread = qemu_thread_get_handle(cpu->thread); -#endif } else { /* we share the thread */ cpu->thread = single_tcg_cpu_thread; cpu->halt_cond = single_tcg_halt_cond; cpu->thread_id = first_cpu->thread_id; - cpu->can_do_io = 1; + cpu->neg.can_do_io = 1; cpu->created = true; } } diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 684dc5a137..9c957f421c 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -31,7 +31,11 @@ #include "sysemu/cpu-timers.h" #include "qemu/main-loop.h" #include "qemu/guest-random.h" +#include "qemu/timer.h" #include "exec/exec-all.h" +#include "exec/hwaddr.h" +#include "exec/tb-flush.h" +#include "exec/gdbstub.h" #include "tcg-accel-ops.h" #include "tcg-accel-ops-mttcg.h" @@ -42,41 +46,49 @@ void tcg_cpu_init_cflags(CPUState *cpu, bool parallel) { - uint32_t cflags = cpu->cluster_index << CF_CLUSTER_SHIFT; + uint32_t cflags; + + /* + * Include the cluster number in the hash we use to look up TBs. + * This is important because a TB that is valid for one cluster at + * a given physical address and set of CPU flags is not necessarily + * valid for another: + * the two clusters may have different views of physical memory, or + * may have different CPU features (eg FPU present or absent). + */ + cflags = cpu->cluster_index << CF_CLUSTER_SHIFT; + cflags |= parallel ? CF_PARALLEL : 0; cflags |= icount_enabled() ? CF_USE_ICOUNT : 0; - cpu->tcg_cflags = cflags; + cpu->tcg_cflags |= cflags; } -void tcg_cpus_destroy(CPUState *cpu) +void tcg_cpu_destroy(CPUState *cpu) { cpu_thread_signal_destroyed(cpu); } -int tcg_cpus_exec(CPUState *cpu) +int tcg_cpu_exec(CPUState *cpu) { int ret; -#ifdef CONFIG_PROFILER - int64_t ti; -#endif assert(tcg_enabled()); -#ifdef CONFIG_PROFILER - ti = profile_getclock(); -#endif cpu_exec_start(cpu); ret = cpu_exec(cpu); cpu_exec_end(cpu); -#ifdef CONFIG_PROFILER - qatomic_set(&tcg_ctx->prof.cpu_exec_time, - tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti); -#endif return ret; } +static void tcg_cpu_reset_hold(CPUState *cpu) +{ + tcg_flush_jmp_cache(cpu); + + tlb_flush(cpu); +} + /* mask must never be zero, except for A20 change call */ void tcg_handle_interrupt(CPUState *cpu, int mask) { - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); cpu->interrupt_request |= mask; @@ -87,27 +99,125 @@ void tcg_handle_interrupt(CPUState *cpu, int mask) if (!qemu_cpu_is_self(cpu)) { qemu_cpu_kick(cpu); } else { - qatomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1); + qatomic_set(&cpu->neg.icount_decr.u16.high, -1); } } +static bool tcg_supports_guest_debug(void) +{ + return true; +} + +/* Translate GDB watchpoint type to a flags value for cpu_watchpoint_* */ +static inline int xlat_gdb_type(CPUState *cpu, int gdbtype) +{ + static const int xlat[] = { + [GDB_WATCHPOINT_WRITE] = BP_GDB | BP_MEM_WRITE, + [GDB_WATCHPOINT_READ] = BP_GDB | BP_MEM_READ, + [GDB_WATCHPOINT_ACCESS] = BP_GDB | BP_MEM_ACCESS, + }; + + CPUClass *cc = CPU_GET_CLASS(cpu); + int cputype = xlat[gdbtype]; + + if (cc->gdb_stop_before_watchpoint) { + cputype |= BP_STOP_BEFORE_ACCESS; + } + return cputype; +} + +static int tcg_insert_breakpoint(CPUState *cs, int type, vaddr addr, vaddr len) +{ + CPUState *cpu; + int err = 0; + + switch (type) { + case GDB_BREAKPOINT_SW: + case GDB_BREAKPOINT_HW: + CPU_FOREACH(cpu) { + err = cpu_breakpoint_insert(cpu, addr, BP_GDB, NULL); + if (err) { + break; + } + } + return err; + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_READ: + case GDB_WATCHPOINT_ACCESS: + CPU_FOREACH(cpu) { + err = cpu_watchpoint_insert(cpu, addr, len, + xlat_gdb_type(cpu, type), NULL); + if (err) { + break; + } + } + return err; + default: + return -ENOSYS; + } +} + +static int tcg_remove_breakpoint(CPUState *cs, int type, vaddr addr, vaddr len) +{ + CPUState *cpu; + int err = 0; + + switch (type) { + case GDB_BREAKPOINT_SW: + case GDB_BREAKPOINT_HW: + CPU_FOREACH(cpu) { + err = cpu_breakpoint_remove(cpu, addr, BP_GDB); + if (err) { + break; + } + } + return err; + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_READ: + case GDB_WATCHPOINT_ACCESS: + CPU_FOREACH(cpu) { + err = cpu_watchpoint_remove(cpu, addr, len, + xlat_gdb_type(cpu, type)); + if (err) { + break; + } + } + return err; + default: + return -ENOSYS; + } +} + +static inline void tcg_remove_all_breakpoints(CPUState *cpu) +{ + cpu_breakpoint_remove_all(cpu, BP_GDB); + cpu_watchpoint_remove_all(cpu, BP_GDB); +} + static void tcg_accel_ops_init(AccelOpsClass *ops) { if (qemu_tcg_mttcg_enabled()) { ops->create_vcpu_thread = mttcg_start_vcpu_thread; ops->kick_vcpu_thread = mttcg_kick_vcpu_thread; ops->handle_interrupt = tcg_handle_interrupt; - } else if (icount_enabled()) { - ops->create_vcpu_thread = rr_start_vcpu_thread; - ops->kick_vcpu_thread = rr_kick_vcpu_thread; - ops->handle_interrupt = icount_handle_interrupt; - ops->get_virtual_clock = icount_get; - ops->get_elapsed_ticks = icount_get; } else { ops->create_vcpu_thread = rr_start_vcpu_thread; ops->kick_vcpu_thread = rr_kick_vcpu_thread; - ops->handle_interrupt = tcg_handle_interrupt; + + if (icount_enabled()) { + ops->handle_interrupt = icount_handle_interrupt; + ops->get_virtual_clock = icount_get; + ops->get_elapsed_ticks = icount_get; + } else { + ops->handle_interrupt = tcg_handle_interrupt; + } } + + ops->cpu_reset_hold = tcg_cpu_reset_hold; + ops->supports_guest_debug = tcg_supports_guest_debug; + ops->insert_breakpoint = tcg_insert_breakpoint; + ops->remove_breakpoint = tcg_remove_breakpoint; + ops->remove_all_breakpoints = tcg_remove_all_breakpoints; } static void tcg_accel_ops_class_init(ObjectClass *oc, void *data) diff --git a/accel/tcg/tcg-accel-ops.h b/accel/tcg/tcg-accel-ops.h index f9bc6330e2..44c4079972 100644 --- a/accel/tcg/tcg-accel-ops.h +++ b/accel/tcg/tcg-accel-ops.h @@ -14,8 +14,8 @@ #include "sysemu/cpus.h" -void tcg_cpus_destroy(CPUState *cpu); -int tcg_cpus_exec(CPUState *cpu); +void tcg_cpu_destroy(CPUState *cpu); +int tcg_cpu_exec(CPUState *cpu); void tcg_handle_interrupt(CPUState *cpu, int mask); void tcg_cpu_init_cflags(CPUState *cpu, bool parallel); diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 47952eecd7..c6619f5b98 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -25,22 +25,26 @@ #include "qemu/osdep.h" #include "sysemu/tcg.h" +#include "exec/replay-core.h" #include "sysemu/cpu-timers.h" -#include "tcg/tcg.h" +#include "tcg/startup.h" +#include "tcg/oversized-guest.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/accel.h" +#include "qemu/atomic.h" #include "qapi/qapi-builtin-visit.h" #include "qemu/units.h" #if !defined(CONFIG_USER_ONLY) #include "hw/boards.h" #endif -#include "internal.h" +#include "internal-target.h" struct TCGState { AccelState parent_obj; bool mttcg_enabled; + bool one_insn_per_tb; int splitwx_enabled; unsigned long tb_size; }; @@ -60,37 +64,23 @@ DECLARE_INSTANCE_CHECKER(TCGState, TCG_STATE, * 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. + * there is one remaining limitation to check: + * - The guest can't be oversized (e.g. 64 bit guest on 32 bit host) */ -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 (icount_enabled() || TCG_OVERSIZED_GUEST) { return false; - } else { -#ifdef TARGET_SUPPORTS_MTTCG - return check_tcg_memory_orders_compatible(); -#else - return false; -#endif } +#ifdef TARGET_SUPPORTS_MTTCG +# ifndef TCG_GUEST_DEFAULT_MO +# error "TARGET_SUPPORTS_MTTCG without TCG_GUEST_DEFAULT_MO" +# endif + return true; +#else + return false; +#endif } static void tcg_accel_instance_init(Object *obj) @@ -108,6 +98,7 @@ static void tcg_accel_instance_init(Object *obj) } bool mttcg_enabled; +bool one_insn_per_tb; static int tcg_init_machine(MachineState *ms) { @@ -130,7 +121,7 @@ static int tcg_init_machine(MachineState *ms) * There's no guest base to take into account, so go ahead and * initialize the prologue now. */ - tcg_prologue_init(tcg_ctx); + tcg_prologue_init(); #endif return 0; @@ -157,11 +148,6 @@ static void tcg_set_thread(Object *obj, const char *value, Error **errp) 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) { @@ -207,12 +193,44 @@ static void tcg_set_splitwx(Object *obj, bool value, Error **errp) s->splitwx_enabled = value; } +static bool tcg_get_one_insn_per_tb(Object *obj, Error **errp) +{ + TCGState *s = TCG_STATE(obj); + return s->one_insn_per_tb; +} + +static void tcg_set_one_insn_per_tb(Object *obj, bool value, Error **errp) +{ + TCGState *s = TCG_STATE(obj); + s->one_insn_per_tb = value; + /* Set the global also: this changes the behaviour */ + qatomic_set(&one_insn_per_tb, value); +} + +static int tcg_gdbstub_supported_sstep_flags(void) +{ + /* + * In replay mode all events will come from the log and can't be + * suppressed otherwise we would break determinism. However as those + * events are tied to the number of executed instructions we won't see + * them occurring every time we single step. + */ + if (replay_mode != REPLAY_MODE_NONE) { + return SSTEP_ENABLE; + } else { + return SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER; + } +} + static void tcg_accel_class_init(ObjectClass *oc, void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "tcg"; ac->init_machine = tcg_init_machine; + ac->cpu_common_realize = tcg_exec_realizefn; + ac->cpu_common_unrealize = tcg_exec_unrealizefn; ac->allowed = &tcg_allowed; + ac->gdbstub_supported_sstep_flags = tcg_gdbstub_supported_sstep_flags; object_class_property_add_str(oc, "thread", tcg_get_thread, @@ -228,6 +246,12 @@ static void tcg_accel_class_init(ObjectClass *oc, void *data) tcg_get_splitwx, tcg_set_splitwx); object_class_property_set_description(oc, "split-wx", "Map jit pages into separate RW and RX regions"); + + object_class_property_add_bool(oc, "one-insn-per-tb", + tcg_get_one_insn_per_tb, + tcg_set_one_insn_per_tb); + object_class_property_set_description(oc, "one-insn-per-tb", + "Only put one guest insn in each translation block"); } static const TypeInfo tcg_accel_type = { diff --git a/accel/tcg/tcg-runtime-gvec.c b/accel/tcg/tcg-runtime-gvec.c index ac7d28c251..afca89baa1 100644 --- a/accel/tcg/tcg-runtime-gvec.c +++ b/accel/tcg/tcg-runtime-gvec.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/host-utils.h" #include "cpu.h" -#include "exec/helper-proto.h" +#include "exec/helper-proto-common.h" #include "tcg/tcg-gvec-desc.h" @@ -550,6 +550,17 @@ void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) clear_high(d, oprsz, desc); } +void HELPER(gvec_andcs)(void *d, void *a, uint64_t b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) & ~b; + } + clear_high(d, oprsz, desc); +} + void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); @@ -1031,6 +1042,32 @@ DO_CMP2(64) #undef DO_CMP1 #undef DO_CMP2 +#define DO_CMP1(NAME, TYPE, OP) \ +void HELPER(NAME)(void *d, void *a, uint64_t b64, uint32_t desc) \ +{ \ + intptr_t oprsz = simd_oprsz(desc); \ + TYPE inv = simd_data(desc), b = b64; \ + for (intptr_t i = 0; i < oprsz; i += sizeof(TYPE)) { \ + *(TYPE *)(d + i) = -((*(TYPE *)(a + i) OP b) ^ inv); \ + } \ + clear_high(d, oprsz, desc); \ +} + +#define DO_CMP2(SZ) \ + DO_CMP1(gvec_eqs##SZ, uint##SZ##_t, ==) \ + DO_CMP1(gvec_lts##SZ, int##SZ##_t, <) \ + DO_CMP1(gvec_les##SZ, int##SZ##_t, <=) \ + DO_CMP1(gvec_ltus##SZ, uint##SZ##_t, <) \ + DO_CMP1(gvec_leus##SZ, uint##SZ##_t, <=) + +DO_CMP2(8) +DO_CMP2(16) +DO_CMP2(32) +DO_CMP2(64) + +#undef DO_CMP1 +#undef DO_CMP2 + void HELPER(gvec_ssadd8)(void *d, void *a, void *b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); diff --git a/accel/tcg/tcg-runtime.c b/accel/tcg/tcg-runtime.c index e4e030043f..9fa539ad3d 100644 --- a/accel/tcg/tcg-runtime.c +++ b/accel/tcg/tcg-runtime.c @@ -24,13 +24,17 @@ #include "qemu/osdep.h" #include "qemu/host-utils.h" #include "cpu.h" -#include "exec/helper-proto.h" +#include "exec/helper-proto-common.h" #include "exec/cpu_ldst.h" #include "exec/exec-all.h" #include "disas/disas.h" #include "exec/log.h" #include "tcg/tcg.h" +#define HELPER_H "accel/tcg/tcg-runtime.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + /* 32-bit helpers */ int32_t HELPER(div_i32)(int32_t arg1, int32_t arg2) diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h index 37cbd722bf..c23b5e66c4 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h @@ -39,51 +39,63 @@ DEF_HELPER_FLAGS_1(exit_atomic, TCG_CALL_NO_WG, noreturn, env) DEF_HELPER_FLAGS_3(memset, TCG_CALL_NO_RWG, ptr, ptr, int, ptr) #endif /* IN_HELPER_PROTO */ +DEF_HELPER_FLAGS_3(ld_i128, TCG_CALL_NO_WG, i128, env, i64, i32) +DEF_HELPER_FLAGS_4(st_i128, TCG_CALL_NO_WG, void, env, i64, i128, i32) + DEF_HELPER_FLAGS_5(atomic_cmpxchgb, TCG_CALL_NO_WG, - i32, env, tl, i32, i32, i32) + i32, env, i64, i32, i32, i32) DEF_HELPER_FLAGS_5(atomic_cmpxchgw_be, TCG_CALL_NO_WG, - i32, env, tl, i32, i32, i32) + i32, env, i64, i32, i32, i32) DEF_HELPER_FLAGS_5(atomic_cmpxchgw_le, TCG_CALL_NO_WG, - i32, env, tl, i32, i32, i32) + i32, env, i64, i32, i32, i32) DEF_HELPER_FLAGS_5(atomic_cmpxchgl_be, TCG_CALL_NO_WG, - i32, env, tl, i32, i32, i32) + i32, env, i64, i32, i32, i32) DEF_HELPER_FLAGS_5(atomic_cmpxchgl_le, TCG_CALL_NO_WG, - i32, env, tl, i32, i32, i32) + i32, env, i64, i32, i32, i32) #ifdef CONFIG_ATOMIC64 DEF_HELPER_FLAGS_5(atomic_cmpxchgq_be, TCG_CALL_NO_WG, - i64, env, tl, i64, i64, i32) + i64, env, i64, i64, i64, i32) DEF_HELPER_FLAGS_5(atomic_cmpxchgq_le, TCG_CALL_NO_WG, - i64, env, tl, i64, i64, i32) + i64, env, i64, i64, i64, i32) #endif +#if HAVE_CMPXCHG128 +DEF_HELPER_FLAGS_5(atomic_cmpxchgo_be, TCG_CALL_NO_WG, + i128, env, i64, i128, i128, i32) +DEF_HELPER_FLAGS_5(atomic_cmpxchgo_le, TCG_CALL_NO_WG, + i128, env, i64, i128, i128, i32) +#endif + +DEF_HELPER_FLAGS_5(nonatomic_cmpxchgo, TCG_CALL_NO_WG, + i128, env, i64, i128, i128, i32) #ifdef CONFIG_ATOMIC64 #define GEN_ATOMIC_HELPERS(NAME) \ DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), b), \ - TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + TCG_CALL_NO_WG, i32, env, i64, i32, i32) \ DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_le), \ - TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + TCG_CALL_NO_WG, i32, env, i64, i32, i32) \ DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_be), \ - TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + TCG_CALL_NO_WG, i32, env, i64, i32, i32) \ DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_le), \ - TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + TCG_CALL_NO_WG, i32, env, i64, i32, i32) \ DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_be), \ - TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + TCG_CALL_NO_WG, i32, env, i64, i32, i32) \ DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), q_le), \ - TCG_CALL_NO_WG, i64, env, tl, i64, i32) \ + TCG_CALL_NO_WG, i64, env, i64, i64, i32) \ DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), q_be), \ - TCG_CALL_NO_WG, i64, env, tl, i64, i32) + TCG_CALL_NO_WG, i64, env, i64, i64, i32) #else #define GEN_ATOMIC_HELPERS(NAME) \ DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), b), \ - TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + TCG_CALL_NO_WG, i32, env, i64, i32, i32) \ DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_le), \ - TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + TCG_CALL_NO_WG, i32, env, i64, i32, i32) \ DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_be), \ - TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + TCG_CALL_NO_WG, i32, env, i64, i32, i32) \ DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_le), \ - TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + TCG_CALL_NO_WG, i32, env, i64, i32, i32) \ DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_be), \ - TCG_CALL_NO_WG, i32, env, tl, i32, i32) + TCG_CALL_NO_WG, i32, env, i64, i32, i32) #endif /* CONFIG_ATOMIC64 */ GEN_ATOMIC_HELPERS(fetch_add) @@ -206,6 +218,7 @@ DEF_HELPER_FLAGS_4(gvec_nor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_eqv, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_andcs, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(gvec_ors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) @@ -284,4 +297,29 @@ DEF_HELPER_FLAGS_4(gvec_leu16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_leu32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_leu64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_eqs8, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_eqs16, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_eqs32, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_eqs64, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(gvec_lts8, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_lts16, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_lts32, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_lts64, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(gvec_les8, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_les16, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_les32, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_les64, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(gvec_ltus8, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_ltus16, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_ltus32, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_ltus64, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(gvec_leus8, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_leus16, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_leus32, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(gvec_leus64, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + DEF_HELPER_FLAGS_5(gvec_bitsel, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/accel/tcg/trace-events b/accel/tcg/trace-events index 59eab96f26..4e9b450520 100644 --- a/accel/tcg/trace-events +++ b/accel/tcg/trace-events @@ -6,5 +6,9 @@ exec_tb(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR exec_tb_nocache(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR exec_tb_exit(void *last_tb, unsigned int flags) "tb:%p flags=0x%x" +# cputlb.c +memory_notdirty_write_access(uint64_t vaddr, uint64_t ram_addr, unsigned size) "0x%" PRIx64 " ram_addr 0x%" PRIx64 " size %u" +memory_notdirty_set_dirty(uint64_t vaddr) "0x%" PRIx64 + # translate-all.c translate_block(void *tb, uintptr_t pc, const void *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 291034cb09..83cc14fbde 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" -#define NO_CPU_IO_DEFS #include "trace.h" #include "disas/disas.h" #include "exec/exec-all.h" @@ -46,203 +45,34 @@ #include "exec/cputlb.h" #include "exec/translate-all.h" +#include "exec/translator.h" +#include "exec/tb-flush.h" #include "qemu/bitmap.h" #include "qemu/qemu-print.h" -#include "qemu/timer.h" #include "qemu/main-loop.h" #include "qemu/cacheinfo.h" +#include "qemu/timer.h" #include "exec/log.h" #include "sysemu/cpus.h" #include "sysemu/cpu-timers.h" #include "sysemu/tcg.h" #include "qapi/error.h" #include "hw/core/tcg-cpu-ops.h" +#include "tb-jmp-cache.h" #include "tb-hash.h" #include "tb-context.h" -#include "internal.h" - -/* #define DEBUG_TB_INVALIDATE */ -/* #define DEBUG_TB_FLUSH */ -/* make various TB consistency checks */ -/* #define DEBUG_TB_CHECK */ - -#ifdef DEBUG_TB_INVALIDATE -#define DEBUG_TB_INVALIDATE_GATE 1 -#else -#define DEBUG_TB_INVALIDATE_GATE 0 -#endif - -#ifdef DEBUG_TB_FLUSH -#define DEBUG_TB_FLUSH_GATE 1 -#else -#define DEBUG_TB_FLUSH_GATE 0 -#endif - -#if !defined(CONFIG_USER_ONLY) -/* TB consistency checks only implemented for usermode emulation. */ -#undef DEBUG_TB_CHECK -#endif - -#ifdef DEBUG_TB_CHECK -#define DEBUG_TB_CHECK_GATE 1 -#else -#define DEBUG_TB_CHECK_GATE 0 -#endif - -/* Access to the various translations structures need to be serialised via locks - * for consistency. - * In user-mode emulation access to the memory related structures are protected - * with mmap_lock. - * In !user-mode we use per-page locks. - */ -#ifdef CONFIG_SOFTMMU -#define assert_memory_lock() -#else -#define assert_memory_lock() tcg_debug_assert(have_mmap_lock()) -#endif - -#define SMC_BITMAP_USE_THRESHOLD 10 - -typedef struct PageDesc { - /* list of TBs intersecting this ram page */ - uintptr_t first_tb; -#ifdef CONFIG_SOFTMMU - /* in order to optimize self modifying code, we count the number - of lookups we do to a given page to use a bitmap */ - unsigned long *code_bitmap; - unsigned int code_write_count; -#else - unsigned long flags; - void *target_data; -#endif -#ifndef CONFIG_USER_ONLY - QemuSpin lock; -#endif -} PageDesc; - -/** - * struct page_entry - page descriptor entry - * @pd: pointer to the &struct PageDesc of the page this entry represents - * @index: page index of the page - * @locked: whether the page is locked - * - * This struct helps us keep track of the locked state of a page, without - * bloating &struct PageDesc. - * - * A page lock protects accesses to all fields of &struct PageDesc. - * - * See also: &struct page_collection. - */ -struct page_entry { - PageDesc *pd; - tb_page_addr_t index; - bool locked; -}; - -/** - * struct page_collection - tracks a set of pages (i.e. &struct page_entry's) - * @tree: Binary search tree (BST) of the pages, with key == page index - * @max: Pointer to the page in @tree with the highest page index - * - * To avoid deadlock we lock pages in ascending order of page index. - * When operating on a set of pages, we need to keep track of them so that - * we can lock them in order and also unlock them later. For this we collect - * pages (i.e. &struct page_entry's) in a binary search @tree. Given that the - * @tree implementation we use does not provide an O(1) operation to obtain the - * highest-ranked element, we use @max to keep track of the inserted page - * with the highest index. This is valuable because if a page is not in - * the tree and its index is higher than @max's, then we can lock it - * without breaking the locking order rule. - * - * Note on naming: 'struct page_set' would be shorter, but we already have a few - * page_set_*() helpers, so page_collection is used instead to avoid confusion. - * - * See also: page_collection_lock(). - */ -struct page_collection { - GTree *tree; - struct page_entry *max; -}; - -/* list iterators for lists of tagged pointers in TranslationBlock */ -#define TB_FOR_EACH_TAGGED(head, tb, n, field) \ - for (n = (head) & 1, tb = (TranslationBlock *)((head) & ~1); \ - tb; tb = (TranslationBlock *)tb->field[n], n = (uintptr_t)tb & 1, \ - tb = (TranslationBlock *)((uintptr_t)tb & ~1)) - -#define PAGE_FOR_EACH_TB(pagedesc, tb, n) \ - TB_FOR_EACH_TAGGED((pagedesc)->first_tb, tb, n, page_next) - -#define TB_FOR_EACH_JMP(head_tb, tb, n) \ - TB_FOR_EACH_TAGGED((head_tb)->jmp_list_head, tb, n, jmp_list_next) - -/* - * In system mode we want L1_MAP to be based on ram offsets, - * while in user mode we want it to be based on virtual addresses. - * - * TODO: For user mode, see the caveat re host vs guest virtual - * address spaces near GUEST_ADDR_MAX. - */ -#if !defined(CONFIG_USER_ONLY) -#if HOST_LONG_BITS < TARGET_PHYS_ADDR_SPACE_BITS -# define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS -#else -# define L1_MAP_ADDR_SPACE_BITS TARGET_PHYS_ADDR_SPACE_BITS -#endif -#else -# define L1_MAP_ADDR_SPACE_BITS MIN(HOST_LONG_BITS, TARGET_ABI_BITS) -#endif - -/* Size of the L2 (and L3, etc) page tables. */ -#define V_L2_BITS 10 -#define V_L2_SIZE (1 << V_L2_BITS) - -/* Make sure all possible CPU event bits fit in tb->trace_vcpu_dstate */ -QEMU_BUILD_BUG_ON(CPU_TRACE_DSTATE_MAX_EVENTS > - sizeof_field(TranslationBlock, trace_vcpu_dstate) - * BITS_PER_BYTE); - -/* - * L1 Mapping properties - */ -static int v_l1_size; -static int v_l1_shift; -static int v_l2_levels; - -/* The bottom level has pointers to PageDesc, and is indexed by - * anything from 4 to (V_L2_BITS + 3) bits, depending on target page size. - */ -#define V_L1_MIN_BITS 4 -#define V_L1_MAX_BITS (V_L2_BITS + 3) -#define V_L1_MAX_SIZE (1 << V_L1_MAX_BITS) - -static void *l1_map[V_L1_MAX_SIZE]; +#include "internal-common.h" +#include "internal-target.h" +#include "tcg/perf.h" +#include "tcg/insn-start-words.h" TBContext tb_ctx; -static void page_table_config_init(void) -{ - uint32_t v_l1_bits; - - assert(TARGET_PAGE_BITS); - /* The bits remaining after N lower levels of page tables. */ - v_l1_bits = (L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % V_L2_BITS; - if (v_l1_bits < V_L1_MIN_BITS) { - v_l1_bits += V_L2_BITS; - } - - v_l1_size = 1 << v_l1_bits; - v_l1_shift = L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS - v_l1_bits; - v_l2_levels = v_l1_shift / V_L2_BITS - 1; - - assert(v_l1_bits <= V_L1_MAX_BITS); - assert(v_l1_shift % V_L2_BITS == 0); - assert(v_l2_levels >= 0); -} - -/* Encode VAL as a signed leb128 sequence at P. - Return P incremented past the encoded value. */ -static uint8_t *encode_sleb128(uint8_t *p, target_long val) +/* + * Encode VAL as a signed leb128 sequence at P. + * Return P incremented past the encoded value. + */ +static uint8_t *encode_sleb128(uint8_t *p, int64_t val) { int more, byte; @@ -260,21 +90,23 @@ static uint8_t *encode_sleb128(uint8_t *p, target_long val) return p; } -/* Decode a signed leb128 sequence at *PP; increment *PP past the - decoded value. Return the decoded value. */ -static target_long decode_sleb128(const uint8_t **pp) +/* + * Decode a signed leb128 sequence at *PP; increment *PP past the + * decoded value. Return the decoded value. + */ +static int64_t decode_sleb128(const uint8_t **pp) { const uint8_t *p = *pp; - target_long val = 0; + int64_t val = 0; int byte, shift = 0; do { byte = *p++; - val |= (target_ulong)(byte & 0x7f) << shift; + val |= (int64_t)(byte & 0x7f) << shift; shift += 7; } while (byte & 0x80); if (shift < TARGET_LONG_BITS && (byte & 0x40)) { - val |= -(target_ulong)1 << shift; + val |= -(int64_t)1 << shift; } *pp = p; @@ -296,22 +128,26 @@ static target_long decode_sleb128(const uint8_t **pp) static int encode_search(TranslationBlock *tb, uint8_t *block) { uint8_t *highwater = tcg_ctx->code_gen_highwater; + uint64_t *insn_data = tcg_ctx->gen_insn_data; + uint16_t *insn_end_off = tcg_ctx->gen_insn_end_off; uint8_t *p = block; int i, j, n; for (i = 0, n = tb->icount; i < n; ++i) { - target_ulong prev; + uint64_t prev, curr; for (j = 0; j < TARGET_INSN_START_WORDS; ++j) { if (i == 0) { - prev = (j == 0 ? tb->pc : 0); + prev = (!(tb_cflags(tb) & CF_PCREL) && j == 0 ? tb->pc : 0); } else { - prev = tcg_ctx->gen_insn_data[i - 1][j]; + prev = insn_data[(i - 1) * TARGET_INSN_START_WORDS + j]; } - p = encode_sleb128(p, tcg_ctx->gen_insn_data[i][j] - prev); + curr = insn_data[i * TARGET_INSN_START_WORDS + j]; + p = encode_sleb128(p, curr - prev); } - prev = (i == 0 ? 0 : tcg_ctx->gen_insn_end_off[i - 1]); - p = encode_sleb128(p, tcg_ctx->gen_insn_end_off[i] - prev); + prev = (i == 0 ? 0 : insn_end_off[i - 1]); + curr = insn_end_off[i]; + p = encode_sleb128(p, curr - prev); /* Test for (pending) buffer overflow. The assumption is that any one row beginning below the high water mark cannot overrun @@ -325,60 +161,67 @@ static int encode_search(TranslationBlock *tb, uint8_t *block) return p - block; } -/* The cpu state corresponding to 'searched_pc' is restored. - * When reset_icount is true, current TB will be interrupted and - * icount should be recalculated. - */ -static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, - uintptr_t searched_pc, bool reset_icount) +static int cpu_unwind_data_from_tb(TranslationBlock *tb, uintptr_t host_pc, + uint64_t *data) { - target_ulong data[TARGET_INSN_START_WORDS] = { tb->pc }; - uintptr_t host_pc = (uintptr_t)tb->tc.ptr; - CPUArchState *env = cpu->env_ptr; + uintptr_t iter_pc = (uintptr_t)tb->tc.ptr; const uint8_t *p = tb->tc.ptr + tb->tc.size; int i, j, num_insns = tb->icount; -#ifdef CONFIG_PROFILER - TCGProfile *prof = &tcg_ctx->prof; - int64_t ti = profile_getclock(); -#endif - searched_pc -= GETPC_ADJ; + host_pc -= GETPC_ADJ; - if (searched_pc < host_pc) { + if (host_pc < iter_pc) { return -1; } - /* Reconstruct the stored insn data while looking for the point at - which the end of the insn exceeds the searched_pc. */ + memset(data, 0, sizeof(uint64_t) * TARGET_INSN_START_WORDS); + if (!(tb_cflags(tb) & CF_PCREL)) { + data[0] = tb->pc; + } + + /* + * Reconstruct the stored insn data while looking for the point + * at which the end of the insn exceeds host_pc. + */ for (i = 0; i < num_insns; ++i) { for (j = 0; j < TARGET_INSN_START_WORDS; ++j) { data[j] += decode_sleb128(&p); } - host_pc += decode_sleb128(&p); - if (host_pc > searched_pc) { - goto found; + iter_pc += decode_sleb128(&p); + if (iter_pc > host_pc) { + return num_insns - i; } } return -1; - - found: - if (reset_icount && (tb_cflags(tb) & CF_USE_ICOUNT)) { - assert(icount_enabled()); - /* Reset the cycle counter to the start of the block - and shift if to the number of actually executed instructions */ - cpu_neg(cpu)->icount_decr.u16.low += num_insns - i; - } - restore_state_to_opc(env, tb, data); - -#ifdef CONFIG_PROFILER - qatomic_set(&prof->restore_time, - prof->restore_time + profile_getclock() - ti); - qatomic_set(&prof->restore_count, prof->restore_count + 1); -#endif - return 0; } -bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit) +/* + * The cpu state corresponding to 'host_pc' is restored in + * preparation for exiting the TB. + */ +void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, + uintptr_t host_pc) +{ + uint64_t data[TARGET_INSN_START_WORDS]; + int insns_left = cpu_unwind_data_from_tb(tb, host_pc, data); + + if (insns_left < 0) { + return; + } + + if (tb_cflags(tb) & CF_USE_ICOUNT) { + assert(icount_enabled()); + /* + * Reset the cycle counter to the start of the block and + * shift if to the number of actually executed instructions. + */ + cpu->neg.icount_decr.u16.low += insns_left; + } + + cpu->cc->tcg_ops->restore_state_to_opc(cpu, tb, data); +} + +bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc) { /* * The host_pc has to be in the rx region of the code buffer. @@ -393,1014 +236,74 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit) if (in_code_gen_buffer((const void *)(host_pc - tcg_splitwx_diff))) { TranslationBlock *tb = tcg_tb_lookup(host_pc); if (tb) { - cpu_restore_state_from_tb(cpu, tb, host_pc, will_exit); + cpu_restore_state_from_tb(cpu, tb, host_pc); return true; } } return false; } +bool cpu_unwind_state_data(CPUState *cpu, uintptr_t host_pc, uint64_t *data) +{ + if (in_code_gen_buffer((const void *)(host_pc - tcg_splitwx_diff))) { + TranslationBlock *tb = tcg_tb_lookup(host_pc); + if (tb) { + return cpu_unwind_data_from_tb(tb, host_pc, data) >= 0; + } + } + return false; +} + void page_init(void) { - page_size_init(); page_table_config_init(); - -#if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY) - { -#ifdef HAVE_KINFO_GETVMMAP - struct kinfo_vmentry *freep; - int i, cnt; - - freep = kinfo_getvmmap(getpid(), &cnt); - if (freep) { - mmap_lock(); - for (i = 0; i < cnt; i++) { - unsigned long startaddr, endaddr; - - startaddr = freep[i].kve_start; - endaddr = freep[i].kve_end; - if (h2g_valid(startaddr)) { - startaddr = h2g(startaddr) & TARGET_PAGE_MASK; - - if (h2g_valid(endaddr)) { - endaddr = h2g(endaddr); - page_set_flags(startaddr, endaddr, PAGE_RESERVED); - } else { -#if TARGET_ABI_BITS <= L1_MAP_ADDR_SPACE_BITS - endaddr = ~0ul; - page_set_flags(startaddr, endaddr, PAGE_RESERVED); -#endif - } - } - } - free(freep); - mmap_unlock(); - } -#else - FILE *f; - - last_brk = (unsigned long)sbrk(0); - - f = fopen("/compat/linux/proc/self/maps", "r"); - if (f) { - mmap_lock(); - - do { - unsigned long startaddr, endaddr; - int n; - - n = fscanf(f, "%lx-%lx %*[^\n]\n", &startaddr, &endaddr); - - if (n == 2 && h2g_valid(startaddr)) { - startaddr = h2g(startaddr) & TARGET_PAGE_MASK; - - if (h2g_valid(endaddr)) { - endaddr = h2g(endaddr); - } else { - endaddr = ~0ul; - } - page_set_flags(startaddr, endaddr, PAGE_RESERVED); - } - } while (!feof(f)); - - fclose(f); - mmap_unlock(); - } -#endif - } -#endif -} - -static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) -{ - PageDesc *pd; - void **lp; - int i; - - /* Level 1. Always allocated. */ - lp = l1_map + ((index >> v_l1_shift) & (v_l1_size - 1)); - - /* Level 2..N-1. */ - for (i = v_l2_levels; i > 0; i--) { - void **p = qatomic_rcu_read(lp); - - if (p == NULL) { - void *existing; - - if (!alloc) { - return NULL; - } - p = g_new0(void *, V_L2_SIZE); - existing = qatomic_cmpxchg(lp, NULL, p); - if (unlikely(existing)) { - g_free(p); - p = existing; - } - } - - lp = p + ((index >> (i * V_L2_BITS)) & (V_L2_SIZE - 1)); - } - - pd = qatomic_rcu_read(lp); - if (pd == NULL) { - void *existing; - - if (!alloc) { - return NULL; - } - pd = g_new0(PageDesc, V_L2_SIZE); -#ifndef CONFIG_USER_ONLY - { - int i; - - for (i = 0; i < V_L2_SIZE; i++) { - qemu_spin_init(&pd[i].lock); - } - } -#endif - existing = qatomic_cmpxchg(lp, NULL, pd); - if (unlikely(existing)) { -#ifndef CONFIG_USER_ONLY - { - int i; - - for (i = 0; i < V_L2_SIZE; i++) { - qemu_spin_destroy(&pd[i].lock); - } - } -#endif - g_free(pd); - pd = existing; - } - } - - return pd + (index & (V_L2_SIZE - 1)); -} - -static inline PageDesc *page_find(tb_page_addr_t index) -{ - return page_find_alloc(index, 0); -} - -static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1, - PageDesc **ret_p2, tb_page_addr_t phys2, int alloc); - -/* In user-mode page locks aren't used; mmap_lock is enough */ -#ifdef CONFIG_USER_ONLY - -#define assert_page_locked(pd) tcg_debug_assert(have_mmap_lock()) - -static inline void page_lock(PageDesc *pd) -{ } - -static inline void page_unlock(PageDesc *pd) -{ } - -static inline void page_lock_tb(const TranslationBlock *tb) -{ } - -static inline void page_unlock_tb(const TranslationBlock *tb) -{ } - -struct page_collection * -page_collection_lock(tb_page_addr_t start, tb_page_addr_t end) -{ - return NULL; -} - -void page_collection_unlock(struct page_collection *set) -{ } -#else /* !CONFIG_USER_ONLY */ - -#ifdef CONFIG_DEBUG_TCG - -static __thread GHashTable *ht_pages_locked_debug; - -static void ht_pages_locked_debug_init(void) -{ - if (ht_pages_locked_debug) { - return; - } - ht_pages_locked_debug = g_hash_table_new(NULL, NULL); -} - -static bool page_is_locked(const PageDesc *pd) -{ - PageDesc *found; - - ht_pages_locked_debug_init(); - found = g_hash_table_lookup(ht_pages_locked_debug, pd); - return !!found; -} - -static void page_lock__debug(PageDesc *pd) -{ - ht_pages_locked_debug_init(); - g_assert(!page_is_locked(pd)); - g_hash_table_insert(ht_pages_locked_debug, pd, pd); -} - -static void page_unlock__debug(const PageDesc *pd) -{ - bool removed; - - ht_pages_locked_debug_init(); - g_assert(page_is_locked(pd)); - removed = g_hash_table_remove(ht_pages_locked_debug, pd); - g_assert(removed); -} - -static void -do_assert_page_locked(const PageDesc *pd, const char *file, int line) -{ - if (unlikely(!page_is_locked(pd))) { - error_report("assert_page_lock: PageDesc %p not locked @ %s:%d", - pd, file, line); - abort(); - } -} - -#define assert_page_locked(pd) do_assert_page_locked(pd, __FILE__, __LINE__) - -void assert_no_pages_locked(void) -{ - ht_pages_locked_debug_init(); - g_assert(g_hash_table_size(ht_pages_locked_debug) == 0); -} - -#else /* !CONFIG_DEBUG_TCG */ - -#define assert_page_locked(pd) - -static inline void page_lock__debug(const PageDesc *pd) -{ -} - -static inline void page_unlock__debug(const PageDesc *pd) -{ -} - -#endif /* CONFIG_DEBUG_TCG */ - -static inline void page_lock(PageDesc *pd) -{ - page_lock__debug(pd); - qemu_spin_lock(&pd->lock); -} - -static inline void page_unlock(PageDesc *pd) -{ - qemu_spin_unlock(&pd->lock); - page_unlock__debug(pd); -} - -/* lock the page(s) of a TB in the correct acquisition order */ -static inline void page_lock_tb(const TranslationBlock *tb) -{ - page_lock_pair(NULL, tb->page_addr[0], NULL, tb->page_addr[1], 0); -} - -static inline void page_unlock_tb(const TranslationBlock *tb) -{ - PageDesc *p1 = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS); - - page_unlock(p1); - if (unlikely(tb->page_addr[1] != -1)) { - PageDesc *p2 = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS); - - if (p2 != p1) { - page_unlock(p2); - } - } -} - -static inline struct page_entry * -page_entry_new(PageDesc *pd, tb_page_addr_t index) -{ - struct page_entry *pe = g_malloc(sizeof(*pe)); - - pe->index = index; - pe->pd = pd; - pe->locked = false; - return pe; -} - -static void page_entry_destroy(gpointer p) -{ - struct page_entry *pe = p; - - g_assert(pe->locked); - page_unlock(pe->pd); - g_free(pe); -} - -/* returns false on success */ -static bool page_entry_trylock(struct page_entry *pe) -{ - bool busy; - - busy = qemu_spin_trylock(&pe->pd->lock); - if (!busy) { - g_assert(!pe->locked); - pe->locked = true; - page_lock__debug(pe->pd); - } - return busy; -} - -static void do_page_entry_lock(struct page_entry *pe) -{ - page_lock(pe->pd); - g_assert(!pe->locked); - pe->locked = true; -} - -static gboolean page_entry_lock(gpointer key, gpointer value, gpointer data) -{ - struct page_entry *pe = value; - - do_page_entry_lock(pe); - return FALSE; -} - -static gboolean page_entry_unlock(gpointer key, gpointer value, gpointer data) -{ - struct page_entry *pe = value; - - if (pe->locked) { - pe->locked = false; - page_unlock(pe->pd); - } - return FALSE; } /* - * Trylock a page, and if successful, add the page to a collection. - * Returns true ("busy") if the page could not be locked; false otherwise. + * Isolate the portion of code gen which can setjmp/longjmp. + * Return the size of the generated code, or negative on error. */ -static bool page_trylock_add(struct page_collection *set, tb_page_addr_t addr) +static int setjmp_gen_code(CPUArchState *env, TranslationBlock *tb, + vaddr pc, void *host_pc, + int *max_insns, int64_t *ti) { - tb_page_addr_t index = addr >> TARGET_PAGE_BITS; - struct page_entry *pe; - PageDesc *pd; - - pe = g_tree_lookup(set->tree, &index); - if (pe) { - return false; + int ret = sigsetjmp(tcg_ctx->jmp_trans, 0); + if (unlikely(ret != 0)) { + return ret; } - pd = page_find(index); - if (pd == NULL) { - return false; - } + tcg_func_start(tcg_ctx); - pe = page_entry_new(pd, index); - g_tree_insert(set->tree, &pe->index, pe); + tcg_ctx->cpu = env_cpu(env); + gen_intermediate_code(env_cpu(env), tb, max_insns, pc, host_pc); + assert(tb->size != 0); + tcg_ctx->cpu = NULL; + *max_insns = tb->icount; - /* - * If this is either (1) the first insertion or (2) a page whose index - * is higher than any other so far, just lock the page and move on. - */ - if (set->max == NULL || pe->index > set->max->index) { - set->max = pe; - do_page_entry_lock(pe); - return false; - } - /* - * Try to acquire out-of-order lock; if busy, return busy so that we acquire - * locks in order. - */ - return page_entry_trylock(pe); -} - -static gint tb_page_addr_cmp(gconstpointer ap, gconstpointer bp, gpointer udata) -{ - tb_page_addr_t a = *(const tb_page_addr_t *)ap; - tb_page_addr_t b = *(const tb_page_addr_t *)bp; - - if (a == b) { - return 0; - } else if (a < b) { - return -1; - } - return 1; -} - -/* - * Lock a range of pages ([@start,@end[) as well as the pages of all - * intersecting TBs. - * Locking order: acquire locks in ascending order of page index. - */ -struct page_collection * -page_collection_lock(tb_page_addr_t start, tb_page_addr_t end) -{ - struct page_collection *set = g_malloc(sizeof(*set)); - tb_page_addr_t index; - PageDesc *pd; - - start >>= TARGET_PAGE_BITS; - end >>= TARGET_PAGE_BITS; - g_assert(start <= end); - - set->tree = g_tree_new_full(tb_page_addr_cmp, NULL, NULL, - page_entry_destroy); - set->max = NULL; - assert_no_pages_locked(); - - retry: - g_tree_foreach(set->tree, page_entry_lock, NULL); - - for (index = start; index <= end; index++) { - TranslationBlock *tb; - int n; - - pd = page_find(index); - if (pd == NULL) { - continue; - } - if (page_trylock_add(set, index << TARGET_PAGE_BITS)) { - g_tree_foreach(set->tree, page_entry_unlock, NULL); - goto retry; - } - assert_page_locked(pd); - PAGE_FOR_EACH_TB(pd, tb, n) { - if (page_trylock_add(set, tb->page_addr[0]) || - (tb->page_addr[1] != -1 && - page_trylock_add(set, tb->page_addr[1]))) { - /* drop all locks, and reacquire in order */ - g_tree_foreach(set->tree, page_entry_unlock, NULL); - goto retry; - } - } - } - return set; -} - -void page_collection_unlock(struct page_collection *set) -{ - /* entries are unlocked and freed via page_entry_destroy */ - g_tree_destroy(set->tree); - g_free(set); -} - -#endif /* !CONFIG_USER_ONLY */ - -static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1, - PageDesc **ret_p2, tb_page_addr_t phys2, int alloc) -{ - PageDesc *p1, *p2; - tb_page_addr_t page1; - tb_page_addr_t page2; - - assert_memory_lock(); - g_assert(phys1 != -1); - - page1 = phys1 >> TARGET_PAGE_BITS; - page2 = phys2 >> TARGET_PAGE_BITS; - - p1 = page_find_alloc(page1, alloc); - if (ret_p1) { - *ret_p1 = p1; - } - if (likely(phys2 == -1)) { - page_lock(p1); - return; - } else if (page1 == page2) { - page_lock(p1); - if (ret_p2) { - *ret_p2 = p1; - } - return; - } - p2 = page_find_alloc(page2, alloc); - if (ret_p2) { - *ret_p2 = p2; - } - if (page1 < page2) { - page_lock(p1); - page_lock(p2); - } else { - page_lock(p2); - page_lock(p1); - } -} - -static bool tb_cmp(const void *ap, const void *bp) -{ - const TranslationBlock *a = ap; - const TranslationBlock *b = bp; - - return a->pc == b->pc && - a->cs_base == b->cs_base && - a->flags == b->flags && - (tb_cflags(a) & ~CF_INVALID) == (tb_cflags(b) & ~CF_INVALID) && - a->trace_vcpu_dstate == b->trace_vcpu_dstate && - a->page_addr[0] == b->page_addr[0] && - a->page_addr[1] == b->page_addr[1]; -} - -void tb_htable_init(void) -{ - unsigned int mode = QHT_MODE_AUTO_RESIZE; - - qht_init(&tb_ctx.htable, tb_cmp, CODE_GEN_HTABLE_SIZE, mode); -} - -/* call with @p->lock held */ -static inline void invalidate_page_bitmap(PageDesc *p) -{ - assert_page_locked(p); -#ifdef CONFIG_SOFTMMU - g_free(p->code_bitmap); - p->code_bitmap = NULL; - p->code_write_count = 0; -#endif -} - -/* Set to NULL all the 'first_tb' fields in all PageDescs. */ -static void page_flush_tb_1(int level, void **lp) -{ - int i; - - if (*lp == NULL) { - return; - } - if (level == 0) { - PageDesc *pd = *lp; - - for (i = 0; i < V_L2_SIZE; ++i) { - page_lock(&pd[i]); - pd[i].first_tb = (uintptr_t)NULL; - invalidate_page_bitmap(pd + i); - page_unlock(&pd[i]); - } - } else { - void **pp = *lp; - - for (i = 0; i < V_L2_SIZE; ++i) { - page_flush_tb_1(level - 1, pp + i); - } - } -} - -static void page_flush_tb(void) -{ - int i, l1_sz = v_l1_size; - - for (i = 0; i < l1_sz; i++) { - page_flush_tb_1(v_l2_levels, l1_map + i); - } -} - -static gboolean tb_host_size_iter(gpointer key, gpointer value, gpointer data) -{ - const TranslationBlock *tb = value; - size_t *size = data; - - *size += tb->tc.size; - return false; -} - -/* flush all the translation blocks */ -static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) -{ - bool did_flush = false; - - mmap_lock(); - /* If it is already been done on request of another CPU, - * just retry. - */ - if (tb_ctx.tb_flush_count != tb_flush_count.host_int) { - goto done; - } - did_flush = true; - - if (DEBUG_TB_FLUSH_GATE) { - size_t nb_tbs = tcg_nb_tbs(); - size_t host_size = 0; - - tcg_tb_foreach(tb_host_size_iter, &host_size); - printf("qemu: flush code_size=%zu nb_tbs=%zu avg_tb_size=%zu\n", - tcg_code_size(), nb_tbs, nb_tbs > 0 ? host_size / nb_tbs : 0); - } - - CPU_FOREACH(cpu) { - cpu_tb_jmp_cache_clear(cpu); - } - - qht_reset_size(&tb_ctx.htable, CODE_GEN_HTABLE_SIZE); - page_flush_tb(); - - tcg_region_reset_all(); - /* XXX: flush processor icache at this point if cache flush is - expensive */ - qatomic_mb_set(&tb_ctx.tb_flush_count, tb_ctx.tb_flush_count + 1); - -done: - mmap_unlock(); - if (did_flush) { - qemu_plugin_flush_cb(); - } -} - -void tb_flush(CPUState *cpu) -{ - if (tcg_enabled()) { - unsigned tb_flush_count = qatomic_mb_read(&tb_ctx.tb_flush_count); - - if (cpu_in_exclusive_context(cpu)) { - do_tb_flush(cpu, RUN_ON_CPU_HOST_INT(tb_flush_count)); - } else { - async_safe_run_on_cpu(cpu, do_tb_flush, - RUN_ON_CPU_HOST_INT(tb_flush_count)); - } - } -} - -/* - * Formerly ifdef DEBUG_TB_CHECK. These debug functions are user-mode-only, - * so in order to prevent bit rot we compile them unconditionally in user-mode, - * and let the optimizer get rid of them by wrapping their user-only callers - * with if (DEBUG_TB_CHECK_GATE). - */ -#ifdef CONFIG_USER_ONLY - -static void do_tb_invalidate_check(void *p, uint32_t hash, void *userp) -{ - TranslationBlock *tb = p; - target_ulong addr = *(target_ulong *)userp; - - if (!(addr + TARGET_PAGE_SIZE <= tb->pc || addr >= tb->pc + tb->size)) { - printf("ERROR invalidate: address=" TARGET_FMT_lx - " PC=%08lx size=%04x\n", addr, (long)tb->pc, tb->size); - } -} - -/* verify that all the pages have correct rights for code - * - * Called with mmap_lock held. - */ -static void tb_invalidate_check(target_ulong address) -{ - address &= TARGET_PAGE_MASK; - qht_iter(&tb_ctx.htable, do_tb_invalidate_check, &address); -} - -static void do_tb_page_check(void *p, uint32_t hash, void *userp) -{ - TranslationBlock *tb = p; - int flags1, flags2; - - flags1 = page_get_flags(tb->pc); - flags2 = page_get_flags(tb->pc + tb->size - 1); - if ((flags1 & PAGE_WRITE) || (flags2 & PAGE_WRITE)) { - printf("ERROR page flags: PC=%08lx size=%04x f1=%x f2=%x\n", - (long)tb->pc, tb->size, flags1, flags2); - } -} - -/* verify that all the pages have correct rights for code */ -static void tb_page_check(void) -{ - qht_iter(&tb_ctx.htable, do_tb_page_check, NULL); -} - -#endif /* CONFIG_USER_ONLY */ - -/* - * user-mode: call with mmap_lock held - * !user-mode: call with @pd->lock held - */ -static inline void tb_page_remove(PageDesc *pd, TranslationBlock *tb) -{ - TranslationBlock *tb1; - uintptr_t *pprev; - unsigned int n1; - - assert_page_locked(pd); - pprev = &pd->first_tb; - PAGE_FOR_EACH_TB(pd, tb1, n1) { - if (tb1 == tb) { - *pprev = tb1->page_next[n1]; - return; - } - pprev = &tb1->page_next[n1]; - } - g_assert_not_reached(); -} - -/* remove @orig from its @n_orig-th jump list */ -static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig) -{ - uintptr_t ptr, ptr_locked; - TranslationBlock *dest; - TranslationBlock *tb; - uintptr_t *pprev; - int n; - - /* mark the LSB of jmp_dest[] so that no further jumps can be inserted */ - ptr = qatomic_or_fetch(&orig->jmp_dest[n_orig], 1); - dest = (TranslationBlock *)(ptr & ~1); - if (dest == NULL) { - return; - } - - qemu_spin_lock(&dest->jmp_lock); - /* - * While acquiring the lock, the jump might have been removed if the - * destination TB was invalidated; check again. - */ - ptr_locked = qatomic_read(&orig->jmp_dest[n_orig]); - if (ptr_locked != ptr) { - qemu_spin_unlock(&dest->jmp_lock); - /* - * The only possibility is that the jump was unlinked via - * tb_jump_unlink(dest). Seeing here another destination would be a bug, - * because we set the LSB above. - */ - g_assert(ptr_locked == 1 && dest->cflags & CF_INVALID); - return; - } - /* - * We first acquired the lock, and since the destination pointer matches, - * we know for sure that @orig is in the jmp list. - */ - pprev = &dest->jmp_list_head; - TB_FOR_EACH_JMP(dest, tb, n) { - if (tb == orig && n == n_orig) { - *pprev = tb->jmp_list_next[n]; - /* no need to set orig->jmp_dest[n]; setting the LSB was enough */ - qemu_spin_unlock(&dest->jmp_lock); - return; - } - pprev = &tb->jmp_list_next[n]; - } - g_assert_not_reached(); -} - -/* reset the jump entry 'n' of a TB so that it is not chained to - another TB */ -static inline void tb_reset_jump(TranslationBlock *tb, int n) -{ - uintptr_t addr = (uintptr_t)(tb->tc.ptr + tb->jmp_reset_offset[n]); - tb_set_jmp_target(tb, n, addr); -} - -/* remove any jumps to the TB */ -static inline void tb_jmp_unlink(TranslationBlock *dest) -{ - TranslationBlock *tb; - int n; - - qemu_spin_lock(&dest->jmp_lock); - - TB_FOR_EACH_JMP(dest, tb, n) { - tb_reset_jump(tb, n); - qatomic_and(&tb->jmp_dest[n], (uintptr_t)NULL | 1); - /* No need to clear the list entry; setting the dest ptr is enough */ - } - dest->jmp_list_head = (uintptr_t)NULL; - - qemu_spin_unlock(&dest->jmp_lock); -} - -/* - * In user-mode, call with mmap_lock held. - * In !user-mode, if @rm_from_page_list is set, call with the TB's pages' - * locks held. - */ -static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) -{ - CPUState *cpu; - PageDesc *p; - uint32_t h; - tb_page_addr_t phys_pc; - uint32_t orig_cflags = tb_cflags(tb); - - assert_memory_lock(); - - /* make sure no further incoming jumps will be chained to this TB */ - qemu_spin_lock(&tb->jmp_lock); - qatomic_set(&tb->cflags, tb->cflags | CF_INVALID); - qemu_spin_unlock(&tb->jmp_lock); - - /* remove the TB from the hash list */ - phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK); - h = tb_hash_func(phys_pc, tb->pc, tb->flags, orig_cflags, - tb->trace_vcpu_dstate); - if (!qht_remove(&tb_ctx.htable, tb, h)) { - return; - } - - /* remove the TB from the page list */ - if (rm_from_page_list) { - p = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS); - tb_page_remove(p, tb); - invalidate_page_bitmap(p); - if (tb->page_addr[1] != -1) { - p = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS); - tb_page_remove(p, tb); - invalidate_page_bitmap(p); - } - } - - /* remove the TB from the hash list */ - h = tb_jmp_cache_hash_func(tb->pc); - CPU_FOREACH(cpu) { - if (qatomic_read(&cpu->tb_jmp_cache[h]) == tb) { - qatomic_set(&cpu->tb_jmp_cache[h], NULL); - } - } - - /* suppress this TB from the two jump lists */ - tb_remove_from_jmp_list(tb, 0); - tb_remove_from_jmp_list(tb, 1); - - /* suppress any remaining jumps to this TB */ - tb_jmp_unlink(tb); - - qatomic_set(&tb_ctx.tb_phys_invalidate_count, - tb_ctx.tb_phys_invalidate_count + 1); -} - -static void tb_phys_invalidate__locked(TranslationBlock *tb) -{ - qemu_thread_jit_write(); - do_tb_phys_invalidate(tb, true); - qemu_thread_jit_execute(); -} - -/* invalidate one TB - * - * Called with mmap_lock held in user-mode. - */ -void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) -{ - if (page_addr == -1 && tb->page_addr[0] != -1) { - page_lock_tb(tb); - do_tb_phys_invalidate(tb, true); - page_unlock_tb(tb); - } else { - do_tb_phys_invalidate(tb, false); - } -} - -#ifdef CONFIG_SOFTMMU -/* call with @p->lock held */ -static void build_page_bitmap(PageDesc *p) -{ - int n, tb_start, tb_end; - TranslationBlock *tb; - - assert_page_locked(p); - p->code_bitmap = bitmap_new(TARGET_PAGE_SIZE); - - PAGE_FOR_EACH_TB(p, tb, n) { - /* NOTE: this is subtle as a TB may span two physical pages */ - if (n == 0) { - /* NOTE: tb_end may be after the end of the page, but - it is not a problem */ - tb_start = tb->pc & ~TARGET_PAGE_MASK; - tb_end = tb_start + tb->size; - if (tb_end > TARGET_PAGE_SIZE) { - tb_end = TARGET_PAGE_SIZE; - } - } else { - tb_start = 0; - tb_end = ((tb->pc + tb->size) & ~TARGET_PAGE_MASK); - } - bitmap_set(p->code_bitmap, tb_start, tb_end - tb_start); - } -} -#endif - -/* add the tb in the target page and protect it if necessary - * - * Called with mmap_lock held for user-mode emulation. - * Called with @p->lock held in !user-mode. - */ -static inline void tb_page_add(PageDesc *p, TranslationBlock *tb, - unsigned int n, tb_page_addr_t page_addr) -{ -#ifndef CONFIG_USER_ONLY - bool page_already_protected; -#endif - - assert_page_locked(p); - - tb->page_addr[n] = page_addr; - tb->page_next[n] = p->first_tb; -#ifndef CONFIG_USER_ONLY - page_already_protected = p->first_tb != (uintptr_t)NULL; -#endif - p->first_tb = (uintptr_t)tb | n; - invalidate_page_bitmap(p); - -#if defined(CONFIG_USER_ONLY) - /* translator_loop() must have made all TB pages non-writable */ - assert(!(p->flags & PAGE_WRITE)); -#else - /* if some code is already present, then the pages are already - protected. So we handle the case where only the first TB is - allocated in a physical page */ - if (!page_already_protected) { - tlb_protect_code(page_addr); - } -#endif -} - -/* - * Add a new TB and link it to the physical page tables. phys_page2 is - * (-1) to indicate that only one page contains the TB. - * - * Called with mmap_lock held for user-mode emulation. - * - * Returns a pointer @tb, or a pointer to an existing TB that matches @tb. - * Note that in !user-mode, another thread might have already added a TB - * for the same block of guest code that @tb corresponds to. In that case, - * the caller should discard the original @tb, and use instead the returned TB. - */ -static TranslationBlock * -tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, - tb_page_addr_t phys_page2) -{ - PageDesc *p; - PageDesc *p2 = NULL; - void *existing_tb = NULL; - uint32_t h; - - assert_memory_lock(); - tcg_debug_assert(!(tb->cflags & CF_INVALID)); - - /* - * Add the TB to the page list, acquiring first the pages's locks. - * We keep the locks held until after inserting the TB in the hash table, - * so that if the insertion fails we know for sure that the TBs are still - * in the page descriptors. - * Note that inserting into the hash table first isn't an option, since - * we can only insert TBs that are fully initialized. - */ - page_lock_pair(&p, phys_pc, &p2, phys_page2, 1); - tb_page_add(p, tb, 0, phys_pc & TARGET_PAGE_MASK); - if (p2) { - tb_page_add(p2, tb, 1, phys_page2); - } else { - tb->page_addr[1] = -1; - } - - /* add in the hash table */ - h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags, - tb->trace_vcpu_dstate); - qht_insert(&tb_ctx.htable, tb, h, &existing_tb); - - /* remove TB from the page(s) if we couldn't insert it */ - if (unlikely(existing_tb)) { - tb_page_remove(p, tb); - invalidate_page_bitmap(p); - if (p2) { - tb_page_remove(p2, tb); - invalidate_page_bitmap(p2); - } - tb = existing_tb; - } - - if (p2 && p2 != p) { - page_unlock(p2); - } - page_unlock(p); - -#ifdef CONFIG_USER_ONLY - if (DEBUG_TB_CHECK_GATE) { - tb_page_check(); - } -#endif - return tb; + return tcg_gen_code(tcg_ctx, tb, pc); } /* Called with mmap_lock held for user mode emulation. */ TranslationBlock *tb_gen_code(CPUState *cpu, - target_ulong pc, target_ulong cs_base, + vaddr pc, uint64_t cs_base, uint32_t flags, int cflags) { - CPUArchState *env = cpu->env_ptr; + CPUArchState *env = cpu_env(cpu); TranslationBlock *tb, *existing_tb; - tb_page_addr_t phys_pc, phys_page2; - target_ulong virt_page2; + tb_page_addr_t phys_pc, phys_p2; tcg_insn_unit *gen_code_buf; int gen_code_size, search_size, max_insns; -#ifdef CONFIG_PROFILER - TCGProfile *prof = &tcg_ctx->prof; int64_t ti; -#endif + void *host_pc; assert_memory_lock(); qemu_thread_jit_write(); - phys_pc = get_page_addr_code(env, pc); + phys_pc = get_page_addr_code_hostp(env, pc, &host_pc); if (phys_pc == -1) { /* Generate a one-shot TB with 1 insn in it */ - cflags = (cflags & ~CF_COUNT_MASK) | CF_LAST_IO | 1; + cflags = (cflags & ~CF_COUNT_MASK) | 1; } max_insns = cflags & CF_COUNT_MASK; @@ -1410,6 +313,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, QEMU_BUILD_BUG_ON(CF_COUNT_MASK + 1 != TCG_MAX_INSNS); buffer_overflow: + assert_no_pages_locked(); tb = tcg_tb_alloc(tcg_ctx); if (unlikely(!tb)) { /* flush must be done */ @@ -1422,57 +326,37 @@ TranslationBlock *tb_gen_code(CPUState *cpu, gen_code_buf = tcg_ctx->code_gen_ptr; tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf); - tb->pc = pc; + if (!(cflags & CF_PCREL)) { + tb->pc = pc; + } tb->cs_base = cs_base; tb->flags = flags; tb->cflags = cflags; - tb->trace_vcpu_dstate = *cpu->trace_dstate; - tcg_ctx->tb_cflags = cflags; - tb_overflow: - -#ifdef CONFIG_PROFILER - /* includes aborted translations because of exceptions */ - qatomic_set(&prof->tb_count1, prof->tb_count1 + 1); - ti = profile_getclock(); -#endif - - gen_code_size = sigsetjmp(tcg_ctx->jmp_trans, 0); - if (unlikely(gen_code_size != 0)) { - goto error_return; + tb_set_page_addr0(tb, phys_pc); + tb_set_page_addr1(tb, -1); + if (phys_pc != -1) { + tb_lock_page0(phys_pc); } - tcg_func_start(tcg_ctx); - - tcg_ctx->cpu = env_cpu(env); - gen_intermediate_code(cpu, tb, max_insns); - assert(tb->size != 0); - tcg_ctx->cpu = NULL; - max_insns = tb->icount; - - trace_translate_block(tb, tb->pc, tb->tc.ptr); - - /* generate machine code */ - tb->jmp_reset_offset[0] = TB_JMP_RESET_OFFSET_INVALID; - tb->jmp_reset_offset[1] = TB_JMP_RESET_OFFSET_INVALID; - tcg_ctx->tb_jmp_reset_offset = tb->jmp_reset_offset; - if (TCG_TARGET_HAS_direct_jump) { - tcg_ctx->tb_jmp_insn_offset = tb->jmp_target_arg; - tcg_ctx->tb_jmp_target_addr = NULL; - } else { - tcg_ctx->tb_jmp_insn_offset = NULL; - tcg_ctx->tb_jmp_target_addr = tb->jmp_target_arg; - } - -#ifdef CONFIG_PROFILER - qatomic_set(&prof->tb_count, prof->tb_count + 1); - qatomic_set(&prof->interm_time, - prof->interm_time + profile_getclock() - ti); - ti = profile_getclock(); + tcg_ctx->gen_tb = tb; + tcg_ctx->addr_type = TARGET_LONG_BITS == 32 ? TCG_TYPE_I32 : TCG_TYPE_I64; +#ifdef CONFIG_SOFTMMU + tcg_ctx->page_bits = TARGET_PAGE_BITS; + tcg_ctx->page_mask = TARGET_PAGE_MASK; + tcg_ctx->tlb_dyn_max_bits = CPU_TLB_DYN_MAX_BITS; +#endif + tcg_ctx->insn_start_words = TARGET_INSN_START_WORDS; +#ifdef TCG_GUEST_DEFAULT_MO + tcg_ctx->guest_mo = TCG_GUEST_DEFAULT_MO; +#else + tcg_ctx->guest_mo = TCG_MO_ALL; #endif - gen_code_size = tcg_gen_code(tcg_ctx, tb); + restart_translate: + trace_translate_block(tb, pc, tb->tc.ptr); + + gen_code_size = setjmp_gen_code(env, tb, pc, host_pc, &max_insns, &ti); if (unlikely(gen_code_size < 0)) { - error_return: switch (gen_code_size) { case -1: /* @@ -1487,6 +371,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu, qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, "Restarting code generation for " "code_gen_buffer overflow\n"); + tb_unlock_pages(tb); + tcg_ctx->gen_tb = NULL; goto buffer_overflow; case -2: @@ -1505,28 +391,51 @@ TranslationBlock *tb_gen_code(CPUState *cpu, "Restarting code generation with " "smaller translation block (max %d insns)\n", max_insns); - goto tb_overflow; + + /* + * The half-sized TB may not cross pages. + * TODO: Fix all targets that cross pages except with + * the first insn, at which point this can't be reached. + */ + phys_p2 = tb_page_addr1(tb); + if (unlikely(phys_p2 != -1)) { + tb_unlock_page1(phys_pc, phys_p2); + tb_set_page_addr1(tb, -1); + } + goto restart_translate; + + case -3: + /* + * We had a page lock ordering problem. In order to avoid + * deadlock we had to drop the lock on page0, which means + * that everything we translated so far is compromised. + * Restart with locks held on both pages. + */ + qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, + "Restarting code generation with re-locked pages"); + goto restart_translate; default: g_assert_not_reached(); } } + tcg_ctx->gen_tb = NULL; + search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size); if (unlikely(search_size < 0)) { + tb_unlock_pages(tb); goto buffer_overflow; } tb->tc.size = gen_code_size; -#ifdef CONFIG_PROFILER - qatomic_set(&prof->code_time, prof->code_time + profile_getclock() - ti); - qatomic_set(&prof->code_in_len, prof->code_in_len + tb->size); - qatomic_set(&prof->code_out_len, prof->code_out_len + gen_code_size); - qatomic_set(&prof->search_out_len, prof->search_out_len + search_size); -#endif + /* + * For CF_PCREL, attribute all executions of the generated code + * to its first mapping. + */ + perf_report_code(pc, tb, tcg_splitwx_to_rx(gen_code_buf)); -#ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) && - qemu_log_in_addr_range(tb->pc)) { + qemu_log_in_addr_range(pc)) { FILE *logfile = qemu_log_trylock(); if (logfile) { int code_size, data_size; @@ -1547,8 +456,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu, /* Dump header and the first instruction */ fprintf(logfile, "OUT: [size=%d]\n", gen_code_size); fprintf(logfile, - " -- guest addr 0x" TARGET_FMT_lx " + tb prologue\n", - tcg_ctx->gen_insn_data[insn][0]); + " -- guest addr 0x%016" PRIx64 " + tb prologue\n", + tcg_ctx->gen_insn_data[insn * TARGET_INSN_START_WORDS]); chunk_start = tcg_ctx->gen_insn_end_off[insn]; disas(logfile, tb->tc.ptr, chunk_start); @@ -1560,8 +469,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu, while (insn < tb->icount) { size_t chunk_end = tcg_ctx->gen_insn_end_off[insn]; if (chunk_end > chunk_start) { - fprintf(logfile, " -- guest addr 0x" TARGET_FMT_lx "\n", - tcg_ctx->gen_insn_data[insn][0]); + fprintf(logfile, " -- guest addr 0x%016" PRIx64 "\n", + tcg_ctx->gen_insn_data[insn * TARGET_INSN_START_WORDS]); disas(logfile, tb->tc.ptr + chunk_start, chunk_end - chunk_start); chunk_start = chunk_end; @@ -1597,7 +506,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu, qemu_log_unlock(logfile); } } -#endif qatomic_set(&tcg_ctx->code_gen_ptr, (void *) ROUND_UP((uintptr_t)gen_code_buf + gen_code_size + search_size, @@ -1612,21 +520,20 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tb->jmp_dest[1] = (uintptr_t)NULL; /* init original jump addresses which have been set during tcg_gen_code() */ - if (tb->jmp_reset_offset[0] != TB_JMP_RESET_OFFSET_INVALID) { + if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) { tb_reset_jump(tb, 0); } - if (tb->jmp_reset_offset[1] != TB_JMP_RESET_OFFSET_INVALID) { + if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) { tb_reset_jump(tb, 1); } /* - * If the TB is not associated with a physical RAM page then - * it must be a temporary one-insn TB, and we have nothing to do - * except fill in the page_addr[] fields. Return early before - * attempting to link to other TBs or add to the lookup table. + * If the TB is not associated with a physical RAM page then it must be + * a temporary one-insn TB, and we have nothing left to do. Return early + * before attempting to link to other TBs or add to the lookup table. */ - if (phys_pc == -1) { - tb->page_addr[0] = tb->page_addr[1] = -1; + if (tb_page_addr0(tb) == -1) { + assert_no_pages_locked(); return tb; } @@ -1637,17 +544,13 @@ TranslationBlock *tb_gen_code(CPUState *cpu, */ tcg_tb_insert(tb); - /* check next page if needed */ - virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK; - phys_page2 = -1; - if ((pc & TARGET_PAGE_MASK) != virt_page2) { - phys_page2 = get_page_addr_code(env, virt_page2); - } /* * No explicit memory barrier is required -- tb_link_page() makes the * TB visible in a consistent state. */ - existing_tb = tb_link_page(tb, phys_pc, phys_page2); + existing_tb = tb_link_page(tb); + assert_no_pages_locked(); + /* if the TB already exists, discard what we just translated */ if (unlikely(existing_tb != tb)) { uintptr_t orig_aligned = (uintptr_t)gen_code_buf; @@ -1660,267 +563,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu, return tb; } -/* - * @p must be non-NULL. - * user-mode: call with mmap_lock held. - * !user-mode: call with all @pages locked. - */ -static void -tb_invalidate_phys_page_range__locked(struct page_collection *pages, - PageDesc *p, tb_page_addr_t start, - tb_page_addr_t end, - uintptr_t retaddr) -{ - TranslationBlock *tb; - tb_page_addr_t tb_start, tb_end; - int n; -#ifdef TARGET_HAS_PRECISE_SMC - CPUState *cpu = current_cpu; - CPUArchState *env = NULL; - bool current_tb_not_found = retaddr != 0; - bool current_tb_modified = false; - TranslationBlock *current_tb = NULL; - target_ulong current_pc = 0; - target_ulong current_cs_base = 0; - uint32_t current_flags = 0; -#endif /* TARGET_HAS_PRECISE_SMC */ - - assert_page_locked(p); - -#if defined(TARGET_HAS_PRECISE_SMC) - if (cpu != NULL) { - env = cpu->env_ptr; - } -#endif - - /* we remove all the TBs in the range [start, end[ */ - /* XXX: see if in some cases it could be faster to invalidate all - the code */ - PAGE_FOR_EACH_TB(p, tb, n) { - assert_page_locked(p); - /* NOTE: this is subtle as a TB may span two physical pages */ - if (n == 0) { - /* NOTE: tb_end may be after the end of the page, but - it is not a problem */ - tb_start = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK); - tb_end = tb_start + tb->size; - } else { - tb_start = tb->page_addr[1]; - tb_end = tb_start + ((tb->pc + tb->size) & ~TARGET_PAGE_MASK); - } - if (!(tb_end <= start || tb_start >= end)) { -#ifdef TARGET_HAS_PRECISE_SMC - if (current_tb_not_found) { - current_tb_not_found = false; - /* now we have a real cpu fault */ - current_tb = tcg_tb_lookup(retaddr); - } - if (current_tb == tb && - (tb_cflags(current_tb) & CF_COUNT_MASK) != 1) { - /* - * If we are modifying the current TB, we must stop - * its execution. We could be more precise by checking - * that the modification is after the current PC, but it - * would require a specialized function to partially - * restore the CPU state. - */ - current_tb_modified = true; - cpu_restore_state_from_tb(cpu, current_tb, retaddr, true); - cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, - ¤t_flags); - } -#endif /* TARGET_HAS_PRECISE_SMC */ - tb_phys_invalidate__locked(tb); - } - } -#if !defined(CONFIG_USER_ONLY) - /* if no code remaining, no need to continue to use slow writes */ - if (!p->first_tb) { - invalidate_page_bitmap(p); - tlb_unprotect_code(start); - } -#endif -#ifdef TARGET_HAS_PRECISE_SMC - if (current_tb_modified) { - page_collection_unlock(pages); - /* Force execution of one insn next time. */ - cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); - mmap_unlock(); - cpu_loop_exit_noexc(cpu); - } -#endif -} - -/* - * Invalidate all TBs which intersect with the target physical address range - * [start;end[. NOTE: start and end must refer to the *same* physical page. - * 'is_cpu_write_access' should be true if called from a real cpu write - * access: the virtual CPU will exit the current TB if code is modified inside - * this TB. - * - * Called with mmap_lock held for user-mode emulation - */ -void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end) -{ - struct page_collection *pages; - PageDesc *p; - - assert_memory_lock(); - - p = page_find(start >> TARGET_PAGE_BITS); - if (p == NULL) { - return; - } - pages = page_collection_lock(start, end); - tb_invalidate_phys_page_range__locked(pages, p, start, end, 0); - page_collection_unlock(pages); -} - -/* - * Invalidate all TBs which intersect with the target physical address range - * [start;end[. NOTE: start and end may refer to *different* physical pages. - * 'is_cpu_write_access' should be true if called from a real cpu write - * access: the virtual CPU will exit the current TB if code is modified inside - * this TB. - * - * Called with mmap_lock held for user-mode emulation. - */ -#ifdef CONFIG_SOFTMMU -void tb_invalidate_phys_range(ram_addr_t start, ram_addr_t end) -#else -void tb_invalidate_phys_range(target_ulong start, target_ulong end) -#endif -{ - struct page_collection *pages; - tb_page_addr_t next; - - assert_memory_lock(); - - pages = page_collection_lock(start, end); - for (next = (start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - start < end; - start = next, next += TARGET_PAGE_SIZE) { - PageDesc *pd = page_find(start >> TARGET_PAGE_BITS); - tb_page_addr_t bound = MIN(next, end); - - if (pd == NULL) { - continue; - } - tb_invalidate_phys_page_range__locked(pages, pd, start, bound, 0); - } - page_collection_unlock(pages); -} - -#ifdef CONFIG_SOFTMMU -/* len must be <= 8 and start must be a multiple of len. - * Called via softmmu_template.h when code areas are written to with - * iothread mutex not held. - * - * Call with all @pages in the range [@start, @start + len[ locked. - */ -void tb_invalidate_phys_page_fast(struct page_collection *pages, - tb_page_addr_t start, int len, - uintptr_t retaddr) -{ - PageDesc *p; - - assert_memory_lock(); - - p = page_find(start >> TARGET_PAGE_BITS); - if (!p) { - return; - } - - assert_page_locked(p); - if (!p->code_bitmap && - ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD) { - build_page_bitmap(p); - } - if (p->code_bitmap) { - unsigned int nr; - unsigned long b; - - nr = start & ~TARGET_PAGE_MASK; - b = p->code_bitmap[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG - 1)); - if (b & ((1 << len) - 1)) { - goto do_invalidate; - } - } else { - do_invalidate: - tb_invalidate_phys_page_range__locked(pages, p, start, start + len, - retaddr); - } -} -#else -/* Called with mmap_lock held. If pc is not 0 then it indicates the - * host PC of the faulting store instruction that caused this invalidate. - * Returns true if the caller needs to abort execution of the current - * TB (because it was modified by this store and the guest CPU has - * precise-SMC semantics). - */ -static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc) -{ - TranslationBlock *tb; - PageDesc *p; - int n; -#ifdef TARGET_HAS_PRECISE_SMC - TranslationBlock *current_tb = NULL; - CPUState *cpu = current_cpu; - CPUArchState *env = NULL; - int current_tb_modified = 0; - target_ulong current_pc = 0; - target_ulong current_cs_base = 0; - uint32_t current_flags = 0; -#endif - - assert_memory_lock(); - - addr &= TARGET_PAGE_MASK; - p = page_find(addr >> TARGET_PAGE_BITS); - if (!p) { - return false; - } - -#ifdef TARGET_HAS_PRECISE_SMC - if (p->first_tb && pc != 0) { - current_tb = tcg_tb_lookup(pc); - } - if (cpu != NULL) { - env = cpu->env_ptr; - } -#endif - assert_page_locked(p); - PAGE_FOR_EACH_TB(p, tb, n) { -#ifdef TARGET_HAS_PRECISE_SMC - if (current_tb == tb && - (tb_cflags(current_tb) & CF_COUNT_MASK) != 1) { - /* If we are modifying the current TB, we must stop - its execution. We could be more precise by checking - that the modification is after the current PC, but it - would require a specialized function to partially - restore the CPU state */ - - current_tb_modified = 1; - cpu_restore_state_from_tb(cpu, current_tb, pc, true); - cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, - ¤t_flags); - } -#endif /* TARGET_HAS_PRECISE_SMC */ - tb_phys_invalidate(tb, addr); - } - p->first_tb = (uintptr_t)NULL; -#ifdef TARGET_HAS_PRECISE_SMC - if (current_tb_modified) { - /* Force execution of one insn next time. */ - cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); - return true; - } -#endif - - return false; -} -#endif - /* user-mode: call with mmap_lock held */ void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr) { @@ -1931,20 +573,21 @@ void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr) tb = tcg_tb_lookup(retaddr); if (tb) { /* We can use retranslation to find the PC. */ - cpu_restore_state_from_tb(cpu, tb, retaddr, true); + cpu_restore_state_from_tb(cpu, tb, retaddr); tb_phys_invalidate(tb, -1); } else { /* The exception probably happened in a helper. The CPU state should have been saved before calling it. Fetch the PC from there. */ - CPUArchState *env = cpu->env_ptr; - target_ulong pc, cs_base; + CPUArchState *env = cpu_env(cpu); + vaddr pc; + uint64_t cs_base; tb_page_addr_t addr; uint32_t flags; cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); addr = get_page_addr_code(env, pc); if (addr != -1) { - tb_invalidate_phys_range(addr, addr + 1); + tb_invalidate_phys_range(addr, addr); } } } @@ -1967,7 +610,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) cpu_abort(cpu, "cpu_io_recompile: could not find TB for pc=%p", (void *)retaddr); } - cpu_restore_state_from_tb(cpu, tb, retaddr, true); + cpu_restore_state_from_tb(cpu, tb, retaddr); /* * Some guests must re-execute the branch when re-executing a delay @@ -1978,7 +621,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) cc = CPU_GET_CLASS(cpu); if (cc->tcg_ops->io_recompile_replay_branch && cc->tcg_ops->io_recompile_replay_branch(cpu, tb)) { - cpu_neg(cpu)->icount_decr.u16.low++; + cpu->neg.icount_decr.u16.low++; n = 2; } @@ -1988,509 +631,44 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) * operations only (which execute after completion) so we don't * double instrument the instruction. */ - cpu->cflags_next_tb = curr_cflags(cpu) | CF_MEMI_ONLY | CF_LAST_IO | n; + cpu->cflags_next_tb = curr_cflags(cpu) | CF_MEMI_ONLY | n; - qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, - "cpu_io_recompile: rewound execution of TB to " - TARGET_FMT_lx "\n", tb->pc); - - cpu_loop_exit_noexc(cpu); -} - -static void print_qht_statistics(struct qht_stats hst, GString *buf) -{ - uint32_t hgram_opts; - size_t hgram_bins; - char *hgram; - - if (!hst.head_buckets) { - return; - } - g_string_append_printf(buf, "TB hash buckets %zu/%zu " - "(%0.2f%% head buckets used)\n", - hst.used_head_buckets, hst.head_buckets, - (double)hst.used_head_buckets / - hst.head_buckets * 100); - - hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; - hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT; - if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) { - hgram_opts |= QDIST_PR_NODECIMAL; - } - hgram = qdist_pr(&hst.occupancy, 10, hgram_opts); - g_string_append_printf(buf, "TB hash occupancy %0.2f%% avg chain occ. " - "Histogram: %s\n", - qdist_avg(&hst.occupancy) * 100, hgram); - g_free(hgram); - - hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; - hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain); - if (hgram_bins > 10) { - hgram_bins = 10; - } else { - hgram_bins = 0; - hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE; - } - hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts); - g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. " - "Histogram: %s\n", - qdist_avg(&hst.chain), hgram); - g_free(hgram); -} - -struct tb_tree_stats { - size_t nb_tbs; - size_t host_size; - size_t target_size; - size_t max_target_size; - size_t direct_jmp_count; - size_t direct_jmp2_count; - size_t cross_page; -}; - -static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data) -{ - const TranslationBlock *tb = value; - struct tb_tree_stats *tst = data; - - tst->nb_tbs++; - tst->host_size += tb->tc.size; - tst->target_size += tb->size; - if (tb->size > tst->max_target_size) { - tst->max_target_size = tb->size; - } - if (tb->page_addr[1] != -1) { - tst->cross_page++; - } - if (tb->jmp_reset_offset[0] != TB_JMP_RESET_OFFSET_INVALID) { - tst->direct_jmp_count++; - if (tb->jmp_reset_offset[1] != TB_JMP_RESET_OFFSET_INVALID) { - tst->direct_jmp2_count++; + if (qemu_loglevel_mask(CPU_LOG_EXEC)) { + vaddr pc = cpu->cc->get_pc(cpu); + if (qemu_log_in_addr_range(pc)) { + qemu_log("cpu_io_recompile: rewound execution of TB to %016" + VADDR_PRIx "\n", pc); } } - return false; -} -void dump_exec_info(GString *buf) -{ - struct tb_tree_stats tst = {}; - struct qht_stats hst; - size_t nb_tbs, flush_full, flush_part, flush_elide; - - tcg_tb_foreach(tb_tree_stats_iter, &tst); - nb_tbs = tst.nb_tbs; - /* XXX: avoid using doubles ? */ - g_string_append_printf(buf, "Translation buffer state:\n"); - /* - * Report total code size including the padding and TB structs; - * otherwise users might think "-accel tcg,tb-size" is not honoured. - * For avg host size we use the precise numbers from tb_tree_stats though. - */ - g_string_append_printf(buf, "gen code size %zu/%zu\n", - tcg_code_size(), tcg_code_capacity()); - g_string_append_printf(buf, "TB count %zu\n", nb_tbs); - g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n", - nb_tbs ? tst.target_size / nb_tbs : 0, - tst.max_target_size); - g_string_append_printf(buf, "TB avg host size %zu bytes " - "(expansion ratio: %0.1f)\n", - nb_tbs ? tst.host_size / nb_tbs : 0, - tst.target_size ? - (double)tst.host_size / tst.target_size : 0); - g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n", - tst.cross_page, - nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0); - g_string_append_printf(buf, "direct jump count %zu (%zu%%) " - "(2 jumps=%zu %zu%%)\n", - tst.direct_jmp_count, - nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0, - tst.direct_jmp2_count, - nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0); - - qht_statistics_init(&tb_ctx.htable, &hst); - print_qht_statistics(hst, buf); - qht_statistics_destroy(&hst); - - g_string_append_printf(buf, "\nStatistics:\n"); - g_string_append_printf(buf, "TB flush count %u\n", - qatomic_read(&tb_ctx.tb_flush_count)); - g_string_append_printf(buf, "TB invalidate count %u\n", - qatomic_read(&tb_ctx.tb_phys_invalidate_count)); - - tlb_flush_counts(&flush_full, &flush_part, &flush_elide); - g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full); - g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part); - g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide); - tcg_dump_info(buf); -} - -void dump_opcount_info(GString *buf) -{ - tcg_dump_op_count(buf); + cpu_loop_exit_noexc(cpu); } #else /* CONFIG_USER_ONLY */ void cpu_interrupt(CPUState *cpu, int mask) { - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); cpu->interrupt_request |= mask; - qatomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1); + qatomic_set(&cpu->neg.icount_decr.u16.high, -1); } -/* - * Walks guest process memory "regions" one by one - * and calls callback function 'fn' for each region. - */ -struct walk_memory_regions_data { - walk_memory_regions_fn fn; - void *priv; - target_ulong start; - int prot; -}; - -static int walk_memory_regions_end(struct walk_memory_regions_data *data, - target_ulong end, int new_prot) -{ - if (data->start != -1u) { - int rc = data->fn(data->priv, data->start, end, data->prot); - if (rc != 0) { - return rc; - } - } - - data->start = (new_prot ? end : -1u); - data->prot = new_prot; - - return 0; -} - -static int walk_memory_regions_1(struct walk_memory_regions_data *data, - target_ulong base, int level, void **lp) -{ - target_ulong pa; - int i, rc; - - if (*lp == NULL) { - return walk_memory_regions_end(data, base, 0); - } - - if (level == 0) { - PageDesc *pd = *lp; - - for (i = 0; i < V_L2_SIZE; ++i) { - int prot = pd[i].flags; - - pa = base | (i << TARGET_PAGE_BITS); - if (prot != data->prot) { - rc = walk_memory_regions_end(data, pa, prot); - if (rc != 0) { - return rc; - } - } - } - } else { - void **pp = *lp; - - for (i = 0; i < V_L2_SIZE; ++i) { - pa = base | ((target_ulong)i << - (TARGET_PAGE_BITS + V_L2_BITS * level)); - rc = walk_memory_regions_1(data, pa, level - 1, pp + i); - if (rc != 0) { - return rc; - } - } - } - - return 0; -} - -int walk_memory_regions(void *priv, walk_memory_regions_fn fn) -{ - struct walk_memory_regions_data data; - uintptr_t i, l1_sz = v_l1_size; - - data.fn = fn; - data.priv = priv; - data.start = -1u; - data.prot = 0; - - for (i = 0; i < l1_sz; i++) { - target_ulong base = i << (v_l1_shift + TARGET_PAGE_BITS); - int rc = walk_memory_regions_1(&data, base, v_l2_levels, l1_map + i); - if (rc != 0) { - return rc; - } - } - - return walk_memory_regions_end(&data, 0, 0); -} - -static int dump_region(void *priv, target_ulong start, - target_ulong end, unsigned long prot) -{ - FILE *f = (FILE *)priv; - - (void) fprintf(f, TARGET_FMT_lx"-"TARGET_FMT_lx - " "TARGET_FMT_lx" %c%c%c\n", - start, end, end - start, - ((prot & PAGE_READ) ? 'r' : '-'), - ((prot & PAGE_WRITE) ? 'w' : '-'), - ((prot & PAGE_EXEC) ? 'x' : '-')); - - return 0; -} - -/* dump memory mappings */ -void page_dump(FILE *f) -{ - const int length = sizeof(target_ulong) * 2; - (void) fprintf(f, "%-*s %-*s %-*s %s\n", - length, "start", length, "end", length, "size", "prot"); - walk_memory_regions(f, dump_region); -} - -int page_get_flags(target_ulong address) -{ - PageDesc *p; - - p = page_find(address >> TARGET_PAGE_BITS); - if (!p) { - return 0; - } - return p->flags; -} - -/* Modify the flags of a page and invalidate the code if necessary. - The flag PAGE_WRITE_ORG is positioned automatically depending - on PAGE_WRITE. The mmap_lock should already be held. */ -void page_set_flags(target_ulong start, target_ulong end, int flags) -{ - target_ulong addr, len; - bool reset_target_data; - - /* This function should never be called with addresses outside the - guest address space. If this assert fires, it probably indicates - a missing call to h2g_valid. */ - assert(end - 1 <= GUEST_ADDR_MAX); - assert(start < end); - /* Only set PAGE_ANON with new mappings. */ - assert(!(flags & PAGE_ANON) || (flags & PAGE_RESET)); - assert_memory_lock(); - - start = start & TARGET_PAGE_MASK; - end = TARGET_PAGE_ALIGN(end); - - if (flags & PAGE_WRITE) { - flags |= PAGE_WRITE_ORG; - } - reset_target_data = !(flags & PAGE_VALID) || (flags & PAGE_RESET); - flags &= ~PAGE_RESET; - - for (addr = start, len = end - start; - len != 0; - len -= TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) { - PageDesc *p = page_find_alloc(addr >> TARGET_PAGE_BITS, 1); - - /* If the write protection bit is set, then we invalidate - the code inside. */ - if (!(p->flags & PAGE_WRITE) && - (flags & PAGE_WRITE) && - p->first_tb) { - tb_invalidate_phys_page(addr, 0); - } - if (reset_target_data) { - g_free(p->target_data); - p->target_data = NULL; - p->flags = flags; - } else { - /* Using mprotect on a page does not change MAP_ANON. */ - p->flags = (p->flags & PAGE_ANON) | flags; - } - } -} - -void *page_get_target_data(target_ulong address) -{ - PageDesc *p = page_find(address >> TARGET_PAGE_BITS); - return p ? p->target_data : NULL; -} - -void *page_alloc_target_data(target_ulong address, size_t size) -{ - PageDesc *p = page_find(address >> TARGET_PAGE_BITS); - void *ret = NULL; - - if (p->flags & PAGE_VALID) { - ret = p->target_data; - if (!ret) { - p->target_data = ret = g_malloc0(size); - } - } - return ret; -} - -int page_check_range(target_ulong start, target_ulong len, int flags) -{ - PageDesc *p; - target_ulong end; - target_ulong addr; - - /* This function should never be called with addresses outside the - guest address space. If this assert fires, it probably indicates - a missing call to h2g_valid. */ - if (TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS) { - assert(start < ((target_ulong)1 << L1_MAP_ADDR_SPACE_BITS)); - } - - if (len == 0) { - return 0; - } - if (start + len - 1 < start) { - /* We've wrapped around. */ - return -1; - } - - /* must do before we loose bits in the next step */ - end = TARGET_PAGE_ALIGN(start + len); - start = start & TARGET_PAGE_MASK; - - for (addr = start, len = end - start; - len != 0; - len -= TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) { - p = page_find(addr >> TARGET_PAGE_BITS); - if (!p) { - return -1; - } - if (!(p->flags & PAGE_VALID)) { - return -1; - } - - if ((flags & PAGE_READ) && !(p->flags & PAGE_READ)) { - return -1; - } - if (flags & PAGE_WRITE) { - if (!(p->flags & PAGE_WRITE_ORG)) { - return -1; - } - /* unprotect the page if it was put read-only because it - contains translated code */ - if (!(p->flags & PAGE_WRITE)) { - if (!page_unprotect(addr, 0)) { - return -1; - } - } - } - } - return 0; -} - -void page_protect(tb_page_addr_t page_addr) -{ - target_ulong addr; - PageDesc *p; - int prot; - - p = page_find(page_addr >> TARGET_PAGE_BITS); - if (p && (p->flags & PAGE_WRITE)) { - /* - * Force the host page as non writable (writes will have a page fault + - * mprotect overhead). - */ - page_addr &= qemu_host_page_mask; - prot = 0; - for (addr = page_addr; addr < page_addr + qemu_host_page_size; - addr += TARGET_PAGE_SIZE) { - - p = page_find(addr >> TARGET_PAGE_BITS); - if (!p) { - continue; - } - prot |= p->flags; - p->flags &= ~PAGE_WRITE; - } - mprotect(g2h_untagged(page_addr), qemu_host_page_size, - (prot & PAGE_BITS) & ~PAGE_WRITE); - if (DEBUG_TB_INVALIDATE_GATE) { - printf("protecting code page: 0x" TB_PAGE_ADDR_FMT "\n", page_addr); - } - } -} - -/* called from signal handler: invalidate the code and unprotect the - * page. Return 0 if the fault was not handled, 1 if it was handled, - * and 2 if it was handled but the caller must cause the TB to be - * immediately exited. (We can only return 2 if the 'pc' argument is - * non-zero.) - */ -int page_unprotect(target_ulong address, uintptr_t pc) -{ - unsigned int prot; - bool current_tb_invalidated; - PageDesc *p; - target_ulong host_start, host_end, addr; - - /* Technically this isn't safe inside a signal handler. However we - know this only ever happens in a synchronous SEGV handler, so in - practice it seems to be ok. */ - mmap_lock(); - - p = page_find(address >> TARGET_PAGE_BITS); - if (!p) { - mmap_unlock(); - return 0; - } - - /* if the page was really writable, then we change its - protection back to writable */ - if (p->flags & PAGE_WRITE_ORG) { - current_tb_invalidated = false; - if (p->flags & PAGE_WRITE) { - /* If the page is actually marked WRITE then assume this is because - * this thread raced with another one which got here first and - * set the page to PAGE_WRITE and did the TB invalidate for us. - */ -#ifdef TARGET_HAS_PRECISE_SMC - TranslationBlock *current_tb = tcg_tb_lookup(pc); - if (current_tb) { - current_tb_invalidated = tb_cflags(current_tb) & CF_INVALID; - } -#endif - } else { - host_start = address & qemu_host_page_mask; - host_end = host_start + qemu_host_page_size; - - prot = 0; - for (addr = host_start; addr < host_end; addr += TARGET_PAGE_SIZE) { - p = page_find(addr >> TARGET_PAGE_BITS); - p->flags |= PAGE_WRITE; - prot |= p->flags; - - /* and since the content will be modified, we must invalidate - the corresponding translated code. */ - current_tb_invalidated |= tb_invalidate_phys_page(addr, pc); -#ifdef CONFIG_USER_ONLY - if (DEBUG_TB_CHECK_GATE) { - tb_invalidate_check(addr); - } -#endif - } - mprotect((void *)g2h_untagged(host_start), qemu_host_page_size, - prot & PAGE_BITS); - } - mmap_unlock(); - /* If current TB was invalidated return to main loop */ - return current_tb_invalidated ? 2 : 1; - } - mmap_unlock(); - return 0; -} #endif /* CONFIG_USER_ONLY */ -/* This is a wrapper for common code that can not use CONFIG_SOFTMMU */ -void tcg_flush_softmmu_tlb(CPUState *cs) +/* + * Called by generic code at e.g. cpu reset after cpu creation, + * therefore we must be prepared to allocate the jump cache. + */ +void tcg_flush_jmp_cache(CPUState *cpu) { -#ifdef CONFIG_SOFTMMU - tlb_flush(cs); -#endif + CPUJumpCache *jc = cpu->tb_jmp_cache; + + /* During early initialization, the cache may not yet be allocated. */ + if (unlikely(jc == NULL)) { + return; + } + + for (int i = 0; i < TB_JMP_CACHE_SIZE; i++) { + qatomic_set(&jc->array[i].tb, NULL); + } } diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index fe7af9b943..9de0bc34c8 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -8,30 +8,98 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qemu/error-report.h" -#include "tcg/tcg.h" -#include "tcg/tcg-op.h" #include "exec/exec-all.h" -#include "exec/gen-icount.h" -#include "exec/log.h" #include "exec/translator.h" #include "exec/plugin-gen.h" -#include "sysemu/replay.h" +#include "tcg/tcg-op-common.h" +#include "internal-target.h" -/* Pairs with tcg_clear_temp_count. - To be called by #TranslatorOps.{translate_insn,tb_stop} if - (1) the target is sufficiently clean to support reporting, - (2) as and when all temporaries are known to be consumed. - For most targets, (2) is at the end of translate_insn. */ -void translator_loop_temp_check(DisasContextBase *db) +static void set_can_do_io(DisasContextBase *db, bool val) { - if (tcg_check_temp_count()) { - qemu_log("warning: TCG temporary leaks before " - TARGET_FMT_lx "\n", db->pc_next); + QEMU_BUILD_BUG_ON(sizeof_field(CPUState, neg.can_do_io) != 1); + tcg_gen_st8_i32(tcg_constant_i32(val), tcg_env, + offsetof(ArchCPU, parent_obj.neg.can_do_io) - + offsetof(ArchCPU, env)); +} + +bool translator_io_start(DisasContextBase *db) +{ + /* + * Ensure that this instruction will be the last in the TB. + * The target may override this to something more forceful. + */ + if (db->is_jmp == DISAS_NEXT) { + db->is_jmp = DISAS_TOO_MANY; + } + return true; +} + +static TCGOp *gen_tb_start(DisasContextBase *db, uint32_t cflags) +{ + TCGv_i32 count = NULL; + TCGOp *icount_start_insn = NULL; + + if ((cflags & CF_USE_ICOUNT) || !(cflags & CF_NOIRQ)) { + count = tcg_temp_new_i32(); + tcg_gen_ld_i32(count, tcg_env, + offsetof(ArchCPU, parent_obj.neg.icount_decr.u32) + - offsetof(ArchCPU, env)); + } + + if (cflags & CF_USE_ICOUNT) { + /* + * We emit a sub with a dummy immediate argument. Keep the insn index + * of the sub so that we later (when we know the actual insn count) + * can update the argument with the actual insn count. + */ + tcg_gen_sub_i32(count, count, tcg_constant_i32(0)); + icount_start_insn = tcg_last_op(); + } + + /* + * Emit the check against icount_decr.u32 to see if we should exit + * unless we suppress the check with CF_NOIRQ. If we are using + * icount and have suppressed interruption the higher level code + * should have ensured we don't run more instructions than the + * budget. + */ + if (cflags & CF_NOIRQ) { + tcg_ctx->exitreq_label = NULL; + } else { + tcg_ctx->exitreq_label = gen_new_label(); + tcg_gen_brcondi_i32(TCG_COND_LT, count, 0, tcg_ctx->exitreq_label); + } + + if (cflags & CF_USE_ICOUNT) { + tcg_gen_st16_i32(count, tcg_env, + offsetof(ArchCPU, parent_obj.neg.icount_decr.u16.low) + - offsetof(ArchCPU, env)); + } + + return icount_start_insn; +} + +static void gen_tb_end(const TranslationBlock *tb, uint32_t cflags, + TCGOp *icount_start_insn, int num_insns) +{ + if (cflags & CF_USE_ICOUNT) { + /* + * Update the num_insn immediate parameter now that we know + * the actual insn count. + */ + tcg_set_insn_param(icount_start_insn, 2, + tcgv_i32_arg(tcg_constant_i32(num_insns))); + } + + if (tcg_ctx->exitreq_label) { + gen_set_label(tcg_ctx->exitreq_label); + tcg_gen_exit_tb(tb, TB_EXIT_REQUESTED); } } -bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest) +bool translator_use_goto_tb(DisasContextBase *db, vaddr dest) { /* Suppress goto_tb if requested. */ if (tb_cflags(db->tb) & CF_NO_GOTO_TB) { @@ -42,65 +110,70 @@ bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest) return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0; } -static inline void translator_page_protect(DisasContextBase *dcbase, - target_ulong pc) -{ -#ifdef CONFIG_USER_ONLY - dcbase->page_protect_end = pc | ~TARGET_PAGE_MASK; - page_protect(pc); -#endif -} - -void translator_loop(const TranslatorOps *ops, DisasContextBase *db, - CPUState *cpu, TranslationBlock *tb, int max_insns) +void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc, const TranslatorOps *ops, + DisasContextBase *db) { uint32_t cflags = tb_cflags(tb); + TCGOp *icount_start_insn; + TCGOp *first_insn_start = NULL; bool plugin_enabled; /* Initialize DisasContext */ db->tb = tb; - db->pc_first = tb->pc; - db->pc_next = db->pc_first; + db->pc_first = pc; + db->pc_next = pc; db->is_jmp = DISAS_NEXT; db->num_insns = 0; - db->max_insns = max_insns; + db->max_insns = *max_insns; db->singlestep_enabled = cflags & CF_SINGLE_STEP; - translator_page_protect(db, db->pc_next); + db->insn_start = NULL; + db->host_addr[0] = host_pc; + db->host_addr[1] = NULL; ops->init_disas_context(db, cpu); tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ - /* Reset the temp count so that we can identify leaks */ - tcg_clear_temp_count(); - /* Start translating. */ - gen_tb_start(db->tb); + icount_start_insn = gen_tb_start(db, cflags); ops->tb_start(db, cpu); tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ - plugin_enabled = plugin_gen_tb_start(cpu, tb, cflags & CF_MEMI_ONLY); + plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY); + db->plugin_enabled = plugin_enabled; while (true) { - db->num_insns++; + *max_insns = ++db->num_insns; ops->insn_start(db, cpu); + db->insn_start = tcg_last_op(); + if (first_insn_start == NULL) { + first_insn_start = db->insn_start; + } tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ if (plugin_enabled) { plugin_gen_insn_start(cpu, db); } - /* Disassemble one instruction. The translate_insn hook should - update db->pc_next and db->is_jmp to indicate what should be - done next -- either exiting this loop or locate the start of - the next instruction. */ - if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) { - /* Accept I/O on the last instruction. */ - gen_io_start(); - ops->translate_insn(db, cpu); - } else { - /* we should only see CF_MEMI_ONLY for io_recompile */ - tcg_debug_assert(!(cflags & CF_MEMI_ONLY)); - ops->translate_insn(db, cpu); + /* + * Disassemble one instruction. The translate_insn hook should + * update db->pc_next and db->is_jmp to indicate what should be + * done next -- either exiting this loop or locate the start of + * the next instruction. + */ + ops->translate_insn(db, cpu); + + /* + * We can't instrument after instructions that change control + * flow although this only really affects post-load operations. + * + * Calling plugin_gen_insn_end() before we possibly stop translation + * is important. Even if this ends up as dead code, plugin generation + * needs to see a matching plugin_gen_insn_{start,end}() pair in order + * to accurately track instrumented helpers that might access memory. + */ + if (plugin_enabled) { + plugin_gen_insn_end(); } /* Stop translation if translate_insn so indicated. */ @@ -108,14 +181,6 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, break; } - /* - * We can't instrument after instructions that change control - * flow although this only really affects post-load operations. - */ - if (plugin_enabled) { - plugin_gen_insn_end(); - } - /* Stop translation if the output buffer is full, or we have executed all of the allowed instructions. */ if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { @@ -126,17 +191,31 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, /* Emit code to exit the TB, as indicated by db->is_jmp. */ ops->tb_stop(db, cpu); - gen_tb_end(db->tb, db->num_insns); + gen_tb_end(tb, cflags, icount_start_insn, db->num_insns); + + /* + * Manage can_do_io for the translation block: set to false before + * the first insn and set to true before the last insn. + */ + if (db->num_insns == 1) { + tcg_debug_assert(first_insn_start == db->insn_start); + } else { + tcg_debug_assert(first_insn_start != db->insn_start); + tcg_ctx->emit_before_op = first_insn_start; + set_can_do_io(db, false); + } + tcg_ctx->emit_before_op = db->insn_start; + set_can_do_io(db, true); + tcg_ctx->emit_before_op = NULL; if (plugin_enabled) { - plugin_gen_tb_end(cpu); + plugin_gen_tb_end(cpu, db->num_insns); } /* The disas_log hook may use these values rather than recompute. */ tb->size = db->pc_next - db->pc_first; tb->icount = db->num_insns; -#ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && qemu_log_in_addr_range(db->pc_first)) { FILE *logfile = qemu_log_trylock(); @@ -147,34 +226,154 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, qemu_log_unlock(logfile); } } -#endif } -static inline void translator_maybe_page_protect(DisasContextBase *dcbase, - target_ulong pc, size_t len) +static void *translator_access(CPUArchState *env, DisasContextBase *db, + vaddr pc, size_t len) { -#ifdef CONFIG_USER_ONLY - target_ulong end = pc + len - 1; + void *host; + vaddr base, end; + TranslationBlock *tb; - if (end > dcbase->page_protect_end) { - translator_page_protect(dcbase, end); + tb = db->tb; + + /* Use slow path if first page is MMIO. */ + if (unlikely(tb_page_addr0(tb) == -1)) { + return NULL; } + + end = pc + len - 1; + if (likely(is_same_page(db, end))) { + host = db->host_addr[0]; + base = db->pc_first; + } else { + host = db->host_addr[1]; + base = TARGET_PAGE_ALIGN(db->pc_first); + if (host == NULL) { + tb_page_addr_t page0, old_page1, new_page1; + + new_page1 = get_page_addr_code_hostp(env, base, &db->host_addr[1]); + + /* + * If the second page is MMIO, treat as if the first page + * was MMIO as well, so that we do not cache the TB. + */ + if (unlikely(new_page1 == -1)) { + tb_unlock_pages(tb); + tb_set_page_addr0(tb, -1); + return NULL; + } + + /* + * If this is not the first time around, and page1 matches, + * then we already have the page locked. Alternately, we're + * not doing anything to prevent the PTE from changing, so + * we might wind up with a different page, requiring us to + * re-do the locking. + */ + old_page1 = tb_page_addr1(tb); + if (likely(new_page1 != old_page1)) { + page0 = tb_page_addr0(tb); + if (unlikely(old_page1 != -1)) { + tb_unlock_page1(page0, old_page1); + } + tb_set_page_addr1(tb, new_page1); + tb_lock_page1(page0, new_page1); + } + host = db->host_addr[1]; + } + + /* Use slow path when crossing pages. */ + if (is_same_page(db, pc)) { + return NULL; + } + } + + tcg_debug_assert(pc >= base); + return host + (pc - base); +} + +static void plugin_insn_append(abi_ptr pc, const void *from, size_t size) +{ +#ifdef CONFIG_PLUGIN + struct qemu_plugin_insn *insn = tcg_ctx->plugin_insn; + abi_ptr off; + + if (insn == NULL) { + return; + } + off = pc - insn->vaddr; + if (off < insn->data->len) { + g_byte_array_set_size(insn->data, off); + } else if (off > insn->data->len) { + /* we have an unexpected gap */ + g_assert_not_reached(); + } + + insn->data = g_byte_array_append(insn->data, from, size); #endif } -#define GEN_TRANSLATOR_LD(fullname, type, load_fn, swap_fn) \ - type fullname ## _swap(CPUArchState *env, DisasContextBase *dcbase, \ - abi_ptr pc, bool do_swap) \ - { \ - translator_maybe_page_protect(dcbase, pc, sizeof(type)); \ - type ret = load_fn(env, pc); \ - if (do_swap) { \ - ret = swap_fn(ret); \ - } \ - plugin_insn_append(pc, &ret, sizeof(ret)); \ - return ret; \ +uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc) +{ + uint8_t ret; + void *p = translator_access(env, db, pc, sizeof(ret)); + + if (p) { + plugin_insn_append(pc, p, sizeof(ret)); + return ldub_p(p); } + ret = cpu_ldub_code(env, pc); + plugin_insn_append(pc, &ret, sizeof(ret)); + return ret; +} -FOR_EACH_TRANSLATOR_LD(GEN_TRANSLATOR_LD) +uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc) +{ + uint16_t ret, plug; + void *p = translator_access(env, db, pc, sizeof(ret)); -#undef GEN_TRANSLATOR_LD + if (p) { + plugin_insn_append(pc, p, sizeof(ret)); + return lduw_p(p); + } + ret = cpu_lduw_code(env, pc); + plug = tswap16(ret); + plugin_insn_append(pc, &plug, sizeof(ret)); + return ret; +} + +uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc) +{ + uint32_t ret, plug; + void *p = translator_access(env, db, pc, sizeof(ret)); + + if (p) { + plugin_insn_append(pc, p, sizeof(ret)); + return ldl_p(p); + } + ret = cpu_ldl_code(env, pc); + plug = tswap32(ret); + plugin_insn_append(pc, &plug, sizeof(ret)); + return ret; +} + +uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc) +{ + uint64_t ret, plug; + void *p = translator_access(env, db, pc, sizeof(ret)); + + if (p) { + plugin_insn_append(pc, p, sizeof(ret)); + return ldq_p(p); + } + ret = cpu_ldq_code(env, pc); + plug = tswap64(ret); + plugin_insn_append(pc, &plug, sizeof(ret)); + return ret; +} + +void translator_fake_ldb(uint8_t insn8, abi_ptr pc) +{ + plugin_insn_append(pc, &insn8, sizeof(insn8)); +} diff --git a/accel/tcg/user-exec-stub.c b/accel/tcg/user-exec-stub.c index 968cd3ca60..4fbe2dbdc8 100644 --- a/accel/tcg/user-exec-stub.c +++ b/accel/tcg/user-exec-stub.c @@ -1,8 +1,6 @@ #include "qemu/osdep.h" #include "hw/core/cpu.h" -#include "sysemu/replay.h" - -bool enable_cpu_pm = false; +#include "exec/replay-core.h" void cpu_resume(CPUState *cpu) { @@ -16,6 +14,10 @@ void qemu_init_vcpu(CPUState *cpu) { } +void cpu_exec_reset_hold(CPUState *cpu) +{ +} + /* User mode emulation does not support record/replay yet. */ bool replay_exception(void) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 20ada5472b..3cac3a78c4 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -22,13 +22,15 @@ #include "exec/exec-all.h" #include "tcg/tcg.h" #include "qemu/bitops.h" +#include "qemu/rcu.h" #include "exec/cpu_ldst.h" #include "exec/translate-all.h" #include "exec/helper-proto.h" #include "qemu/atomic128.h" #include "trace/trace-root.h" #include "tcg/tcg-ldst.h" -#include "internal.h" +#include "internal-common.h" +#include "internal-target.h" __thread uintptr_t helper_retaddr; @@ -80,10 +82,7 @@ MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write) * (and if the translator doesn't handle page boundaries correctly * there's little we can do about that here). Therefore, do not * trigger the unwinder. - * - * Like tb_gen_code, release the memory lock before cpu_loop_exit. */ - mmap_unlock(); *pc = 0; return MMU_INST_FETCH; } @@ -138,7 +137,642 @@ bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, } } -static int probe_access_internal(CPUArchState *env, target_ulong addr, +typedef struct PageFlagsNode { + struct rcu_head rcu; + IntervalTreeNode itree; + int flags; +} PageFlagsNode; + +static IntervalTreeRoot pageflags_root; + +static PageFlagsNode *pageflags_find(target_ulong start, target_ulong last) +{ + IntervalTreeNode *n; + + n = interval_tree_iter_first(&pageflags_root, start, last); + return n ? container_of(n, PageFlagsNode, itree) : NULL; +} + +static PageFlagsNode *pageflags_next(PageFlagsNode *p, target_ulong start, + target_ulong last) +{ + IntervalTreeNode *n; + + n = interval_tree_iter_next(&p->itree, start, last); + return n ? container_of(n, PageFlagsNode, itree) : NULL; +} + +int walk_memory_regions(void *priv, walk_memory_regions_fn fn) +{ + IntervalTreeNode *n; + int rc = 0; + + mmap_lock(); + for (n = interval_tree_iter_first(&pageflags_root, 0, -1); + n != NULL; + n = interval_tree_iter_next(n, 0, -1)) { + PageFlagsNode *p = container_of(n, PageFlagsNode, itree); + + rc = fn(priv, n->start, n->last + 1, p->flags); + if (rc != 0) { + break; + } + } + mmap_unlock(); + + return rc; +} + +static int dump_region(void *priv, target_ulong start, + target_ulong end, unsigned long prot) +{ + FILE *f = (FILE *)priv; + + fprintf(f, TARGET_FMT_lx"-"TARGET_FMT_lx" "TARGET_FMT_lx" %c%c%c\n", + start, end, end - start, + ((prot & PAGE_READ) ? 'r' : '-'), + ((prot & PAGE_WRITE) ? 'w' : '-'), + ((prot & PAGE_EXEC) ? 'x' : '-')); + return 0; +} + +/* dump memory mappings */ +void page_dump(FILE *f) +{ + const int length = sizeof(target_ulong) * 2; + + fprintf(f, "%-*s %-*s %-*s %s\n", + length, "start", length, "end", length, "size", "prot"); + walk_memory_regions(f, dump_region); +} + +int page_get_flags(target_ulong address) +{ + PageFlagsNode *p = pageflags_find(address, address); + + /* + * See util/interval-tree.c re lockless lookups: no false positives but + * there are false negatives. If we find nothing, retry with the mmap + * lock acquired. + */ + if (p) { + return p->flags; + } + if (have_mmap_lock()) { + return 0; + } + + mmap_lock(); + p = pageflags_find(address, address); + mmap_unlock(); + return p ? p->flags : 0; +} + +/* A subroutine of page_set_flags: insert a new node for [start,last]. */ +static void pageflags_create(target_ulong start, target_ulong last, int flags) +{ + PageFlagsNode *p = g_new(PageFlagsNode, 1); + + p->itree.start = start; + p->itree.last = last; + p->flags = flags; + interval_tree_insert(&p->itree, &pageflags_root); +} + +/* A subroutine of page_set_flags: remove everything in [start,last]. */ +static bool pageflags_unset(target_ulong start, target_ulong last) +{ + bool inval_tb = false; + + while (true) { + PageFlagsNode *p = pageflags_find(start, last); + target_ulong p_last; + + if (!p) { + break; + } + + if (p->flags & PAGE_EXEC) { + inval_tb = true; + } + + interval_tree_remove(&p->itree, &pageflags_root); + p_last = p->itree.last; + + if (p->itree.start < start) { + /* Truncate the node from the end, or split out the middle. */ + p->itree.last = start - 1; + interval_tree_insert(&p->itree, &pageflags_root); + if (last < p_last) { + pageflags_create(last + 1, p_last, p->flags); + break; + } + } else if (p_last <= last) { + /* Range completely covers node -- remove it. */ + g_free_rcu(p, rcu); + } else { + /* Truncate the node from the start. */ + p->itree.start = last + 1; + interval_tree_insert(&p->itree, &pageflags_root); + break; + } + } + + return inval_tb; +} + +/* + * A subroutine of page_set_flags: nothing overlaps [start,last], + * but check adjacent mappings and maybe merge into a single range. + */ +static void pageflags_create_merge(target_ulong start, target_ulong last, + int flags) +{ + PageFlagsNode *next = NULL, *prev = NULL; + + if (start > 0) { + prev = pageflags_find(start - 1, start - 1); + if (prev) { + if (prev->flags == flags) { + interval_tree_remove(&prev->itree, &pageflags_root); + } else { + prev = NULL; + } + } + } + if (last + 1 != 0) { + next = pageflags_find(last + 1, last + 1); + if (next) { + if (next->flags == flags) { + interval_tree_remove(&next->itree, &pageflags_root); + } else { + next = NULL; + } + } + } + + if (prev) { + if (next) { + prev->itree.last = next->itree.last; + g_free_rcu(next, rcu); + } else { + prev->itree.last = last; + } + interval_tree_insert(&prev->itree, &pageflags_root); + } else if (next) { + next->itree.start = start; + interval_tree_insert(&next->itree, &pageflags_root); + } else { + pageflags_create(start, last, flags); + } +} + +/* + * Allow the target to decide if PAGE_TARGET_[12] may be reset. + * By default, they are not kept. + */ +#ifndef PAGE_TARGET_STICKY +#define PAGE_TARGET_STICKY 0 +#endif +#define PAGE_STICKY (PAGE_ANON | PAGE_PASSTHROUGH | PAGE_TARGET_STICKY) + +/* A subroutine of page_set_flags: add flags to [start,last]. */ +static bool pageflags_set_clear(target_ulong start, target_ulong last, + int set_flags, int clear_flags) +{ + PageFlagsNode *p; + target_ulong p_start, p_last; + int p_flags, merge_flags; + bool inval_tb = false; + + restart: + p = pageflags_find(start, last); + if (!p) { + if (set_flags) { + pageflags_create_merge(start, last, set_flags); + } + goto done; + } + + p_start = p->itree.start; + p_last = p->itree.last; + p_flags = p->flags; + /* Using mprotect on a page does not change sticky bits. */ + merge_flags = (p_flags & ~clear_flags) | set_flags; + + /* + * Need to flush if an overlapping executable region + * removes exec, or adds write. + */ + if ((p_flags & PAGE_EXEC) + && (!(merge_flags & PAGE_EXEC) + || (merge_flags & ~p_flags & PAGE_WRITE))) { + inval_tb = true; + } + + /* + * If there is an exact range match, update and return without + * attempting to merge with adjacent regions. + */ + if (start == p_start && last == p_last) { + if (merge_flags) { + p->flags = merge_flags; + } else { + interval_tree_remove(&p->itree, &pageflags_root); + g_free_rcu(p, rcu); + } + goto done; + } + + /* + * If sticky bits affect the original mapping, then we must be more + * careful about the existing intervals and the separate flags. + */ + if (set_flags != merge_flags) { + if (p_start < start) { + interval_tree_remove(&p->itree, &pageflags_root); + p->itree.last = start - 1; + interval_tree_insert(&p->itree, &pageflags_root); + + if (last < p_last) { + if (merge_flags) { + pageflags_create(start, last, merge_flags); + } + pageflags_create(last + 1, p_last, p_flags); + } else { + if (merge_flags) { + pageflags_create(start, p_last, merge_flags); + } + if (p_last < last) { + start = p_last + 1; + goto restart; + } + } + } else { + if (start < p_start && set_flags) { + pageflags_create(start, p_start - 1, set_flags); + } + if (last < p_last) { + interval_tree_remove(&p->itree, &pageflags_root); + p->itree.start = last + 1; + interval_tree_insert(&p->itree, &pageflags_root); + if (merge_flags) { + pageflags_create(start, last, merge_flags); + } + } else { + if (merge_flags) { + p->flags = merge_flags; + } else { + interval_tree_remove(&p->itree, &pageflags_root); + g_free_rcu(p, rcu); + } + if (p_last < last) { + start = p_last + 1; + goto restart; + } + } + } + goto done; + } + + /* If flags are not changing for this range, incorporate it. */ + if (set_flags == p_flags) { + if (start < p_start) { + interval_tree_remove(&p->itree, &pageflags_root); + p->itree.start = start; + interval_tree_insert(&p->itree, &pageflags_root); + } + if (p_last < last) { + start = p_last + 1; + goto restart; + } + goto done; + } + + /* Maybe split out head and/or tail ranges with the original flags. */ + interval_tree_remove(&p->itree, &pageflags_root); + if (p_start < start) { + p->itree.last = start - 1; + interval_tree_insert(&p->itree, &pageflags_root); + + if (p_last < last) { + goto restart; + } + if (last < p_last) { + pageflags_create(last + 1, p_last, p_flags); + } + } else if (last < p_last) { + p->itree.start = last + 1; + interval_tree_insert(&p->itree, &pageflags_root); + } else { + g_free_rcu(p, rcu); + goto restart; + } + if (set_flags) { + pageflags_create(start, last, set_flags); + } + + done: + return inval_tb; +} + +/* + * Modify the flags of a page and invalidate the code if necessary. + * The flag PAGE_WRITE_ORG is positioned automatically depending + * on PAGE_WRITE. The mmap_lock should already be held. + */ +void page_set_flags(target_ulong start, target_ulong last, int flags) +{ + bool reset = false; + bool inval_tb = false; + + /* This function should never be called with addresses outside the + guest address space. If this assert fires, it probably indicates + a missing call to h2g_valid. */ + assert(start <= last); + assert(last <= GUEST_ADDR_MAX); + /* Only set PAGE_ANON with new mappings. */ + assert(!(flags & PAGE_ANON) || (flags & PAGE_RESET)); + assert_memory_lock(); + + start &= TARGET_PAGE_MASK; + last |= ~TARGET_PAGE_MASK; + + if (!(flags & PAGE_VALID)) { + flags = 0; + } else { + reset = flags & PAGE_RESET; + flags &= ~PAGE_RESET; + if (flags & PAGE_WRITE) { + flags |= PAGE_WRITE_ORG; + } + } + + if (!flags || reset) { + page_reset_target_data(start, last); + inval_tb |= pageflags_unset(start, last); + } + if (flags) { + inval_tb |= pageflags_set_clear(start, last, flags, + ~(reset ? 0 : PAGE_STICKY)); + } + if (inval_tb) { + tb_invalidate_phys_range(start, last); + } +} + +bool page_check_range(target_ulong start, target_ulong len, int flags) +{ + target_ulong last; + int locked; /* tri-state: =0: unlocked, +1: global, -1: local */ + bool ret; + + if (len == 0) { + return true; /* trivial length */ + } + + last = start + len - 1; + if (last < start) { + return false; /* wrap around */ + } + + locked = have_mmap_lock(); + while (true) { + PageFlagsNode *p = pageflags_find(start, last); + int missing; + + if (!p) { + if (!locked) { + /* + * Lockless lookups have false negatives. + * Retry with the lock held. + */ + mmap_lock(); + locked = -1; + p = pageflags_find(start, last); + } + if (!p) { + ret = false; /* entire region invalid */ + break; + } + } + if (start < p->itree.start) { + ret = false; /* initial bytes invalid */ + break; + } + + missing = flags & ~p->flags; + if (missing & ~PAGE_WRITE) { + ret = false; /* page doesn't match */ + break; + } + if (missing & PAGE_WRITE) { + if (!(p->flags & PAGE_WRITE_ORG)) { + ret = false; /* page not writable */ + break; + } + /* Asking about writable, but has been protected: undo. */ + if (!page_unprotect(start, 0)) { + ret = false; + break; + } + /* TODO: page_unprotect should take a range, not a single page. */ + if (last - start < TARGET_PAGE_SIZE) { + ret = true; /* ok */ + break; + } + start += TARGET_PAGE_SIZE; + continue; + } + + if (last <= p->itree.last) { + ret = true; /* ok */ + break; + } + start = p->itree.last + 1; + } + + /* Release the lock if acquired locally. */ + if (locked < 0) { + mmap_unlock(); + } + return ret; +} + +bool page_check_range_empty(target_ulong start, target_ulong last) +{ + assert(last >= start); + assert_memory_lock(); + return pageflags_find(start, last) == NULL; +} + +target_ulong page_find_range_empty(target_ulong min, target_ulong max, + target_ulong len, target_ulong align) +{ + target_ulong len_m1, align_m1; + + assert(min <= max); + assert(max <= GUEST_ADDR_MAX); + assert(len != 0); + assert(is_power_of_2(align)); + assert_memory_lock(); + + len_m1 = len - 1; + align_m1 = align - 1; + + /* Iteratively narrow the search region. */ + while (1) { + PageFlagsNode *p; + + /* Align min and double-check there's enough space remaining. */ + min = (min + align_m1) & ~align_m1; + if (min > max) { + return -1; + } + if (len_m1 > max - min) { + return -1; + } + + p = pageflags_find(min, min + len_m1); + if (p == NULL) { + /* Found! */ + return min; + } + if (max <= p->itree.last) { + /* Existing allocation fills the remainder of the search region. */ + return -1; + } + /* Skip across existing allocation. */ + min = p->itree.last + 1; + } +} + +void page_protect(tb_page_addr_t address) +{ + PageFlagsNode *p; + target_ulong start, last; + int host_page_size = qemu_real_host_page_size(); + int prot; + + assert_memory_lock(); + + if (host_page_size <= TARGET_PAGE_SIZE) { + start = address & TARGET_PAGE_MASK; + last = start + TARGET_PAGE_SIZE - 1; + } else { + start = address & -host_page_size; + last = start + host_page_size - 1; + } + + p = pageflags_find(start, last); + if (!p) { + return; + } + prot = p->flags; + + if (unlikely(p->itree.last < last)) { + /* More than one protection region covers the one host page. */ + assert(TARGET_PAGE_SIZE < host_page_size); + while ((p = pageflags_next(p, start, last)) != NULL) { + prot |= p->flags; + } + } + + if (prot & PAGE_WRITE) { + pageflags_set_clear(start, last, 0, PAGE_WRITE); + mprotect(g2h_untagged(start), last - start + 1, + prot & (PAGE_READ | PAGE_EXEC) ? PROT_READ : PROT_NONE); + } +} + +/* + * Called from signal handler: invalidate the code and unprotect the + * page. Return 0 if the fault was not handled, 1 if it was handled, + * and 2 if it was handled but the caller must cause the TB to be + * immediately exited. (We can only return 2 if the 'pc' argument is + * non-zero.) + */ +int page_unprotect(target_ulong address, uintptr_t pc) +{ + PageFlagsNode *p; + bool current_tb_invalidated; + + /* + * Technically this isn't safe inside a signal handler. However we + * know this only ever happens in a synchronous SEGV handler, so in + * practice it seems to be ok. + */ + mmap_lock(); + + p = pageflags_find(address, address); + + /* If this address was not really writable, nothing to do. */ + if (!p || !(p->flags & PAGE_WRITE_ORG)) { + mmap_unlock(); + return 0; + } + + current_tb_invalidated = false; + if (p->flags & PAGE_WRITE) { + /* + * If the page is actually marked WRITE then assume this is because + * this thread raced with another one which got here first and + * set the page to PAGE_WRITE and did the TB invalidate for us. + */ +#ifdef TARGET_HAS_PRECISE_SMC + TranslationBlock *current_tb = tcg_tb_lookup(pc); + if (current_tb) { + current_tb_invalidated = tb_cflags(current_tb) & CF_INVALID; + } +#endif + } else { + int host_page_size = qemu_real_host_page_size(); + target_ulong start, len, i; + int prot; + + if (host_page_size <= TARGET_PAGE_SIZE) { + start = address & TARGET_PAGE_MASK; + len = TARGET_PAGE_SIZE; + prot = p->flags | PAGE_WRITE; + pageflags_set_clear(start, start + len - 1, PAGE_WRITE, 0); + current_tb_invalidated = tb_invalidate_phys_page_unwind(start, pc); + } else { + start = address & -host_page_size; + len = host_page_size; + prot = 0; + + for (i = 0; i < len; i += TARGET_PAGE_SIZE) { + target_ulong addr = start + i; + + p = pageflags_find(addr, addr); + if (p) { + prot |= p->flags; + if (p->flags & PAGE_WRITE_ORG) { + prot |= PAGE_WRITE; + pageflags_set_clear(addr, addr + TARGET_PAGE_SIZE - 1, + PAGE_WRITE, 0); + } + } + /* + * Since the content will be modified, we must invalidate + * the corresponding translated code. + */ + current_tb_invalidated |= + tb_invalidate_phys_page_unwind(addr, pc); + } + } + if (prot & PAGE_EXEC) { + prot = (prot & ~PAGE_EXEC) | PAGE_READ; + } + mprotect((void *)g2h_untagged(start), len, prot & PAGE_BITS); + } + mmap_unlock(); + + /* If current TB was invalidated return to main loop */ + return current_tb_invalidated ? 2 : 1; +} + +static int probe_access_internal(CPUArchState *env, vaddr addr, int fault_size, MMUAccessType access_type, bool nonfault, uintptr_t ra) { @@ -162,6 +796,10 @@ static int probe_access_internal(CPUArchState *env, target_ulong addr, if (guest_addr_valid_untagged(addr)) { int page_flags = page_get_flags(addr); if (page_flags & acc_flag) { + if ((acc_flag == PAGE_READ || acc_flag == PAGE_WRITE) + && cpu_plugin_mem_cbs_enabled(env_cpu(env))) { + return TLB_MMIO; + } return 0; /* success */ } maperr = !(page_flags & PAGE_VALID); @@ -176,253 +814,316 @@ static int probe_access_internal(CPUArchState *env, target_ulong addr, cpu_loop_exit_sigsegv(env_cpu(env), addr, access_type, maperr, ra); } -int probe_access_flags(CPUArchState *env, target_ulong addr, +int probe_access_flags(CPUArchState *env, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, bool nonfault, void **phost, uintptr_t ra) { int flags; - flags = probe_access_internal(env, addr, 0, access_type, nonfault, ra); - *phost = flags ? NULL : g2h(env_cpu(env), addr); + g_assert(-(addr | TARGET_PAGE_MASK) >= size); + flags = probe_access_internal(env, addr, size, access_type, nonfault, ra); + *phost = (flags & TLB_INVALID_MASK) ? NULL : g2h(env_cpu(env), addr); return flags; } -void *probe_access(CPUArchState *env, target_ulong addr, int size, +void *probe_access(CPUArchState *env, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t ra) { int flags; g_assert(-(addr | TARGET_PAGE_MASK) >= size); flags = probe_access_internal(env, addr, size, access_type, false, ra); - g_assert(flags == 0); + g_assert((flags & ~TLB_MMIO) == 0); return size ? g2h(env_cpu(env), addr) : NULL; } -/* The softmmu versions of these helpers are in cputlb.c. */ +tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, + void **hostp) +{ + int flags; + flags = probe_access_internal(env, addr, 1, MMU_INST_FETCH, false, 0); + g_assert(flags == 0); + + if (hostp) { + *hostp = g2h_untagged(addr); + } + return addr; +} + +#ifdef TARGET_PAGE_DATA_SIZE /* - * Verify that we have passed the correct MemOp to the correct function. - * - * We could present one function to target code, and dispatch based on - * the MemOp, but so far we have worked hard to avoid an indirect function - * call along the memory path. + * Allocate chunks of target data together. For the only current user, + * if we allocate one hunk per page, we have overhead of 40/128 or 40%. + * Therefore, allocate memory for 64 pages at a time for overhead < 1%. */ -static void validate_memop(MemOpIdx oi, MemOp expected) +#define TPD_PAGES 64 +#define TBD_MASK (TARGET_PAGE_MASK * TPD_PAGES) + +typedef struct TargetPageDataNode { + struct rcu_head rcu; + IntervalTreeNode itree; + char data[] __attribute__((aligned)); +} TargetPageDataNode; + +static IntervalTreeRoot targetdata_root; + +void page_reset_target_data(target_ulong start, target_ulong last) { -#ifdef CONFIG_DEBUG_TCG - MemOp have = get_memop(oi) & (MO_SIZE | MO_BSWAP); - assert(have == expected); -#endif + IntervalTreeNode *n, *next; + + assert_memory_lock(); + + start &= TARGET_PAGE_MASK; + last |= ~TARGET_PAGE_MASK; + + for (n = interval_tree_iter_first(&targetdata_root, start, last), + next = n ? interval_tree_iter_next(n, start, last) : NULL; + n != NULL; + n = next, + next = next ? interval_tree_iter_next(n, start, last) : NULL) { + target_ulong n_start, n_last, p_ofs, p_len; + TargetPageDataNode *t = container_of(n, TargetPageDataNode, itree); + + if (n->start >= start && n->last <= last) { + interval_tree_remove(n, &targetdata_root); + g_free_rcu(t, rcu); + continue; + } + + if (n->start < start) { + n_start = start; + p_ofs = (start - n->start) >> TARGET_PAGE_BITS; + } else { + n_start = n->start; + p_ofs = 0; + } + n_last = MIN(last, n->last); + p_len = (n_last + 1 - n_start) >> TARGET_PAGE_BITS; + + memset(t->data + p_ofs * TARGET_PAGE_DATA_SIZE, 0, + p_len * TARGET_PAGE_DATA_SIZE); + } } -void helper_unaligned_ld(CPUArchState *env, target_ulong addr) +void *page_get_target_data(target_ulong address) { - cpu_loop_exit_sigbus(env_cpu(env), addr, MMU_DATA_LOAD, GETPC()); -} + IntervalTreeNode *n; + TargetPageDataNode *t; + target_ulong page, region, p_ofs; -void helper_unaligned_st(CPUArchState *env, target_ulong addr) -{ - cpu_loop_exit_sigbus(env_cpu(env), addr, MMU_DATA_STORE, GETPC()); -} + page = address & TARGET_PAGE_MASK; + region = address & TBD_MASK; -static void *cpu_mmu_lookup(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t ra, MMUAccessType type) + n = interval_tree_iter_first(&targetdata_root, page, page); + if (!n) { + /* + * See util/interval-tree.c re lockless lookups: no false positives + * but there are false negatives. If we find nothing, retry with + * the mmap lock acquired. We also need the lock for the + * allocation + insert. + */ + mmap_lock(); + n = interval_tree_iter_first(&targetdata_root, page, page); + if (!n) { + t = g_malloc0(sizeof(TargetPageDataNode) + + TPD_PAGES * TARGET_PAGE_DATA_SIZE); + n = &t->itree; + n->start = region; + n->last = region | ~TBD_MASK; + interval_tree_insert(n, &targetdata_root); + } + mmap_unlock(); + } + + t = container_of(n, TargetPageDataNode, itree); + p_ofs = (page - region) >> TARGET_PAGE_BITS; + return t->data + p_ofs * TARGET_PAGE_DATA_SIZE; +} +#else +void page_reset_target_data(target_ulong start, target_ulong last) { } +#endif /* TARGET_PAGE_DATA_SIZE */ + +/* The system-mode versions of these helpers are in cputlb.c. */ + +static void *cpu_mmu_lookup(CPUState *cpu, vaddr addr, + MemOp mop, uintptr_t ra, MMUAccessType type) { - MemOp mop = get_memop(oi); int a_bits = get_alignment_bits(mop); void *ret; /* Enforce guest required alignment. */ if (unlikely(addr & ((1 << a_bits) - 1))) { - cpu_loop_exit_sigbus(env_cpu(env), addr, type, ra); + cpu_loop_exit_sigbus(cpu, addr, type, ra); } - ret = g2h(env_cpu(env), addr); + ret = g2h(cpu, addr); set_helper_retaddr(ra); return ret; } -uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) +#include "ldst_atomicity.c.inc" + +static uint8_t do_ld1_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, + uintptr_t ra, MMUAccessType access_type) { void *haddr; uint8_t ret; - validate_memop(oi, MO_UB); - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); + cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + haddr = cpu_mmu_lookup(cpu, addr, get_memop(oi), ra, access_type); ret = ldub_p(haddr); clear_helper_retaddr(); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); return ret; } -uint16_t cpu_ldw_be_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) +static uint16_t do_ld2_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, + uintptr_t ra, MMUAccessType access_type) { void *haddr; uint16_t ret; + MemOp mop = get_memop(oi); - validate_memop(oi, MO_BEUW); - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); - ret = lduw_be_p(haddr); + cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + haddr = cpu_mmu_lookup(cpu, addr, mop, ra, access_type); + ret = load_atom_2(cpu, ra, haddr, mop); clear_helper_retaddr(); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); + + if (mop & MO_BSWAP) { + ret = bswap16(ret); + } return ret; } -uint32_t cpu_ldl_be_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) +static uint32_t do_ld4_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, + uintptr_t ra, MMUAccessType access_type) { void *haddr; uint32_t ret; + MemOp mop = get_memop(oi); - validate_memop(oi, MO_BEUL); - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); - ret = ldl_be_p(haddr); + cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + haddr = cpu_mmu_lookup(cpu, addr, mop, ra, access_type); + ret = load_atom_4(cpu, ra, haddr, mop); clear_helper_retaddr(); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); + + if (mop & MO_BSWAP) { + ret = bswap32(ret); + } return ret; } -uint64_t cpu_ldq_be_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) +static uint64_t do_ld8_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, + uintptr_t ra, MMUAccessType access_type) { void *haddr; uint64_t ret; + MemOp mop = get_memop(oi); - validate_memop(oi, MO_BEUQ); - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); - ret = ldq_be_p(haddr); + cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + haddr = cpu_mmu_lookup(cpu, addr, mop, ra, access_type); + ret = load_atom_8(cpu, ra, haddr, mop); clear_helper_retaddr(); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); + + if (mop & MO_BSWAP) { + ret = bswap64(ret); + } return ret; } -uint16_t cpu_ldw_le_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) +static Int128 do_ld16_mmu(CPUState *cpu, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) { void *haddr; - uint16_t ret; + Int128 ret; + MemOp mop = get_memop(oi); - validate_memop(oi, MO_LEUW); - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); - ret = lduw_le_p(haddr); + tcg_debug_assert((mop & MO_SIZE) == MO_128); + cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_LOAD); + ret = load_atom_16(cpu, ra, haddr, mop); clear_helper_retaddr(); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); + + if (mop & MO_BSWAP) { + ret = bswap128(ret); + } return ret; } -uint32_t cpu_ldl_le_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) -{ - void *haddr; - uint32_t ret; - - validate_memop(oi, MO_LEUL); - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); - ret = ldl_le_p(haddr); - clear_helper_retaddr(); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); - return ret; -} - -uint64_t cpu_ldq_le_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) -{ - void *haddr; - uint64_t ret; - - validate_memop(oi, MO_LEUQ); - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); - ret = ldq_le_p(haddr); - clear_helper_retaddr(); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); - return ret; -} - -void cpu_stb_mmu(CPUArchState *env, abi_ptr addr, uint8_t val, - MemOpIdx oi, uintptr_t ra) +static void do_st1_mmu(CPUState *cpu, vaddr addr, uint8_t val, + MemOpIdx oi, uintptr_t ra) { void *haddr; - validate_memop(oi, MO_UB); - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); + cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + haddr = cpu_mmu_lookup(cpu, addr, get_memop(oi), ra, MMU_DATA_STORE); stb_p(haddr, val); clear_helper_retaddr(); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } -void cpu_stw_be_mmu(CPUArchState *env, abi_ptr addr, uint16_t val, - MemOpIdx oi, uintptr_t ra) +static void do_st2_mmu(CPUState *cpu, vaddr addr, uint16_t val, + MemOpIdx oi, uintptr_t ra) { void *haddr; + MemOp mop = get_memop(oi); - validate_memop(oi, MO_BEUW); - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); - stw_be_p(haddr, val); + cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE); + + if (mop & MO_BSWAP) { + val = bswap16(val); + } + store_atom_2(cpu, ra, haddr, mop, val); clear_helper_retaddr(); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } -void cpu_stl_be_mmu(CPUArchState *env, abi_ptr addr, uint32_t val, - MemOpIdx oi, uintptr_t ra) +static void do_st4_mmu(CPUState *cpu, vaddr addr, uint32_t val, + MemOpIdx oi, uintptr_t ra) { void *haddr; + MemOp mop = get_memop(oi); - validate_memop(oi, MO_BEUL); - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); - stl_be_p(haddr, val); + cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE); + + if (mop & MO_BSWAP) { + val = bswap32(val); + } + store_atom_4(cpu, ra, haddr, mop, val); clear_helper_retaddr(); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } -void cpu_stq_be_mmu(CPUArchState *env, abi_ptr addr, uint64_t val, - MemOpIdx oi, uintptr_t ra) +static void do_st8_mmu(CPUState *cpu, vaddr addr, uint64_t val, + MemOpIdx oi, uintptr_t ra) { void *haddr; + MemOp mop = get_memop(oi); - validate_memop(oi, MO_BEUQ); - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); - stq_be_p(haddr, val); + cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE); + + if (mop & MO_BSWAP) { + val = bswap64(val); + } + store_atom_8(cpu, ra, haddr, mop, val); clear_helper_retaddr(); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } -void cpu_stw_le_mmu(CPUArchState *env, abi_ptr addr, uint16_t val, - MemOpIdx oi, uintptr_t ra) +static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val, + MemOpIdx oi, uintptr_t ra) { void *haddr; + MemOpIdx mop = get_memop(oi); - validate_memop(oi, MO_LEUW); - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); - stw_le_p(haddr, val); + cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE); + + if (mop & MO_BSWAP) { + val = bswap128(val); + } + store_atom_16(cpu, ra, haddr, mop, val); clear_helper_retaddr(); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); -} - -void cpu_stl_le_mmu(CPUArchState *env, abi_ptr addr, uint32_t val, - MemOpIdx oi, uintptr_t ra) -{ - void *haddr; - - validate_memop(oi, MO_LEUL); - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); - stl_le_p(haddr, val); - clear_helper_retaddr(); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); -} - -void cpu_stq_le_mmu(CPUArchState *env, abi_ptr addr, uint64_t val, - MemOpIdx oi, uintptr_t ra) -{ - void *haddr; - - validate_memop(oi, MO_LEUQ); - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); - stq_le_p(haddr, val); - clear_helper_retaddr(); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr ptr) @@ -465,16 +1166,70 @@ uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr) return ret; } +uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) +{ + void *haddr; + uint8_t ret; + + haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_INST_FETCH); + ret = ldub_p(haddr); + clear_helper_retaddr(); + return ret; +} + +uint16_t cpu_ldw_code_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) +{ + void *haddr; + uint16_t ret; + + haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_INST_FETCH); + ret = lduw_p(haddr); + clear_helper_retaddr(); + if (get_memop(oi) & MO_BSWAP) { + ret = bswap16(ret); + } + return ret; +} + +uint32_t cpu_ldl_code_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) +{ + void *haddr; + uint32_t ret; + + haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_INST_FETCH); + ret = ldl_p(haddr); + clear_helper_retaddr(); + if (get_memop(oi) & MO_BSWAP) { + ret = bswap32(ret); + } + return ret; +} + +uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra) +{ + void *haddr; + uint64_t ret; + + haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_DATA_LOAD); + ret = ldq_p(haddr); + clear_helper_retaddr(); + if (get_memop(oi) & MO_BSWAP) { + ret = bswap64(ret); + } + return ret; +} + #include "ldst_common.c.inc" /* * Do not allow unaligned operations to proceed. Return the host address. - * - * @prot may be PAGE_READ, PAGE_WRITE, or PAGE_READ|PAGE_WRITE. */ -static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, - MemOpIdx oi, int size, int prot, - uintptr_t retaddr) +static void *atomic_mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi, + int size, uintptr_t retaddr) { MemOp mop = get_memop(oi); int a_bits = get_alignment_bits(mop); @@ -482,16 +1237,15 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, /* Enforce guest required alignment. */ if (unlikely(addr & ((1 << a_bits) - 1))) { - MMUAccessType t = prot == PAGE_READ ? MMU_DATA_LOAD : MMU_DATA_STORE; - cpu_loop_exit_sigbus(env_cpu(env), addr, t, retaddr); + cpu_loop_exit_sigbus(cpu, addr, MMU_DATA_STORE, retaddr); } /* Enforce qemu required alignment. */ if (unlikely(addr & (size - 1))) { - cpu_loop_exit_atomic(env_cpu(env), retaddr); + cpu_loop_exit_atomic(cpu, retaddr); } - ret = g2h(env_cpu(env), addr); + ret = g2h(cpu, addr); set_helper_retaddr(retaddr); return ret; } @@ -521,7 +1275,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, #include "atomic_template.h" #endif -#if HAVE_ATOMIC128 || HAVE_CMPXCHG128 +#if defined(CONFIG_ATOMIC128) || HAVE_CMPXCHG128 #define DATA_SIZE 16 #include "atomic_template.h" #endif diff --git a/accel/tcg/watchpoint.c b/accel/tcg/watchpoint.c new file mode 100644 index 0000000000..d3aab11458 --- /dev/null +++ b/accel/tcg/watchpoint.c @@ -0,0 +1,143 @@ +/* + * CPU watchpoints + * + * 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.1 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 "qemu/error-report.h" +#include "exec/exec-all.h" +#include "exec/translate-all.h" +#include "sysemu/tcg.h" +#include "sysemu/replay.h" +#include "hw/core/tcg-cpu-ops.h" +#include "hw/core/cpu.h" + +/* + * Return true if this watchpoint address matches the specified + * access (ie the address range covered by the watchpoint overlaps + * partially or completely with the address range covered by the + * access). + */ +static inline bool watchpoint_address_matches(CPUWatchpoint *wp, + vaddr addr, vaddr len) +{ + /* + * We know the lengths are non-zero, but a little caution is + * required to avoid errors in the case where the range ends + * exactly at the top of the address space and so addr + len + * wraps round to zero. + */ + vaddr wpend = wp->vaddr + wp->len - 1; + vaddr addrend = addr + len - 1; + + return !(addr > wpend || wp->vaddr > addrend); +} + +/* Return flags for watchpoints that match addr + prot. */ +int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len) +{ + CPUWatchpoint *wp; + int ret = 0; + + QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { + if (watchpoint_address_matches(wp, addr, len)) { + ret |= wp->flags; + } + } + return ret; +} + +/* Generate a debug exception if a watchpoint has been hit. */ +void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, + MemTxAttrs attrs, int flags, uintptr_t ra) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + CPUWatchpoint *wp; + + assert(tcg_enabled()); + if (cpu->watchpoint_hit) { + /* + * We re-entered the check after replacing the TB. + * Now raise the debug interrupt so that it will + * trigger after the current instruction. + */ + bql_lock(); + cpu_interrupt(cpu, CPU_INTERRUPT_DEBUG); + bql_unlock(); + return; + } + + if (cc->tcg_ops->adjust_watchpoint_address) { + /* this is currently used only by ARM BE32 */ + addr = cc->tcg_ops->adjust_watchpoint_address(cpu, addr, len); + } + + assert((flags & ~BP_MEM_ACCESS) == 0); + QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { + int hit_flags = wp->flags & flags; + + if (hit_flags && watchpoint_address_matches(wp, addr, len)) { + if (replay_running_debug()) { + /* + * replay_breakpoint reads icount. + * Force recompile to succeed, because icount may + * be read only at the end of the block. + */ + if (!cpu->neg.can_do_io) { + /* Force execution of one insn next time. */ + cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); + cpu_loop_exit_restore(cpu, ra); + } + /* + * Don't process the watchpoints when we are + * in a reverse debugging operation. + */ + replay_breakpoint(); + return; + } + + wp->flags |= hit_flags << BP_HIT_SHIFT; + wp->hitaddr = MAX(addr, wp->vaddr); + wp->hitattrs = attrs; + + if (wp->flags & BP_CPU + && cc->tcg_ops->debug_check_watchpoint + && !cc->tcg_ops->debug_check_watchpoint(cpu, wp)) { + wp->flags &= ~BP_WATCHPOINT_HIT; + continue; + } + cpu->watchpoint_hit = wp; + + mmap_lock(); + /* This call also restores vCPU state */ + tb_check_watchpoint(cpu, ra); + if (wp->flags & BP_STOP_BEFORE_ACCESS) { + cpu->exception_index = EXCP_DEBUG; + mmap_unlock(); + cpu_loop_exit(cpu); + } else { + /* Force execution of one insn next time. */ + cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); + mmap_unlock(); + cpu_loop_exit_noexc(cpu); + } + } else { + wp->flags &= ~BP_WATCHPOINT_HIT; + } + } +} diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index 69aa7d018b..0bdefce537 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -12,8 +12,10 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qapi/error.h" +#include "hw/xen/xen_native.h" #include "hw/xen/xen-legacy-backend.h" #include "hw/xen/xen_pt.h" +#include "hw/xen/xen_igd.h" #include "chardev/char.h" #include "qemu/accel.h" #include "sysemu/cpus.h" @@ -23,99 +25,18 @@ #include "migration/global_state.h" #include "hw/boards.h" -//#define DEBUG_XEN - -#ifdef DEBUG_XEN -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "xen: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - bool xen_allowed; xc_interface *xen_xc; xenforeignmemory_handle *xen_fmem; xendevicemodel_handle *xen_dmod; -static int store_dev_info(int domid, Chardev *cs, const char *string) -{ - struct xs_handle *xs = NULL; - char *path = NULL; - char *newpath = NULL; - char *pts = NULL; - int ret = -1; - - /* Only continue if we're talking to a pty. */ - if (!CHARDEV_IS_PTY(cs)) { - return 0; - } - pts = cs->filename + 4; - - /* We now have everything we need to set the xenstore entry. */ - xs = xs_open(0); - if (xs == NULL) { - fprintf(stderr, "Could not contact XenStore\n"); - goto out; - } - - path = xs_get_domain_path(xs, domid); - if (path == NULL) { - fprintf(stderr, "xs_get_domain_path() error\n"); - goto out; - } - newpath = realloc(path, (strlen(path) + strlen(string) + - strlen("/tty") + 1)); - if (newpath == NULL) { - fprintf(stderr, "realloc error\n"); - goto out; - } - path = newpath; - - strcat(path, string); - strcat(path, "/tty"); - if (!xs_write(xs, XBT_NULL, path, pts, strlen(pts))) { - fprintf(stderr, "xs_write for '%s' fail", string); - goto out; - } - ret = 0; - -out: - free(path); - xs_close(xs); - - return ret; -} - -void xenstore_store_pv_console_info(int i, Chardev *chr) -{ - if (i == 0) { - store_dev_info(xen_domid, chr, "/console"); - } else { - char buf[32]; - snprintf(buf, sizeof(buf), "/device/console/%d", i); - store_dev_info(xen_domid, chr, buf); - } -} - - -static void xenstore_record_dm_state(struct xs_handle *xs, const char *state) +static void xenstore_record_dm_state(const char *state) { char path[50]; - if (xs == NULL) { - error_report("xenstore connection not initialized"); - exit(1); - } - snprintf(path, sizeof (path), "device-model/%u/state", xen_domid); - /* - * This call may fail when running restricted so don't make it fatal in - * that case. Toolstacks should instead use QMP to listen for state changes. - */ - if (!xs_write(xs, XBT_NULL, path, state, strlen(state)) && - !xen_domid_restrict) { + if (!qemu_xen_xs_write(xenstore, XBT_NULL, path, state, strlen(state))) { error_report("error recording dm state"); exit(1); } @@ -127,7 +48,7 @@ static void xen_change_state_handler(void *opaque, bool running, { if (running) { /* record state running */ - xenstore_record_dm_state(xenstore, "running"); + xenstore_record_dm_state("running"); } } @@ -176,11 +97,21 @@ static int xen_init(MachineState *ms) xc_interface_close(xen_xc); return -1; } - qemu_add_vm_change_state_handler(xen_change_state_handler, NULL); + + /* + * The XenStore write would fail when running restricted so don't attempt + * it in that case. Toolstacks should instead use QMP to listen for state + * changes. + */ + if (!xen_domid_restrict) { + 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; + + xen_mode = XEN_ATTACH; return 0; } diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 4a61378cd7..cacae1ea59 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -222,11 +222,7 @@ static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask) return -1; } - pfds = audio_calloc ("alsa_poll_helper", count, sizeof (*pfds)); - if (!pfds) { - dolog ("Could not initialize poll mode\n"); - return -1; - } + pfds = g_new0(struct pollfd, count); err = snd_pcm_poll_descriptors (handle, pfds, count); if (err < 0) { @@ -449,7 +445,7 @@ static int alsa_open(bool in, struct alsa_params_req *req, snd_pcm_hw_params_t *hw_params; int err; unsigned int freq, nchannels; - const char *pcm_name = apdo->has_dev ? apdo->dev : "default"; + const char *pcm_name = apdo->dev ?: "default"; snd_pcm_uframes_t obt_buffer_size; const char *typ = in ? "ADC" : "DAC"; snd_pcm_format_t obtfmt; @@ -602,6 +598,42 @@ static int alsa_open(bool in, struct alsa_params_req *req, return -1; } +static size_t alsa_buffer_get_free(HWVoiceOut *hw) +{ + ALSAVoiceOut *alsa = (ALSAVoiceOut *)hw; + snd_pcm_sframes_t avail; + size_t alsa_free, generic_free, generic_in_use; + + avail = snd_pcm_avail_update(alsa->handle); + if (avail < 0) { + if (avail == -EPIPE) { + if (!alsa_recover(alsa->handle)) { + avail = snd_pcm_avail_update(alsa->handle); + } + } + if (avail < 0) { + alsa_logerr(avail, + "Could not obtain number of available frames\n"); + avail = 0; + } + } + + alsa_free = avail * hw->info.bytes_per_frame; + generic_free = audio_generic_buffer_get_free(hw); + generic_in_use = hw->samples * hw->info.bytes_per_frame - generic_free; + if (generic_in_use) { + /* + * This code can only be reached in the unlikely case that + * snd_pcm_avail_update() returned a larger number of frames + * than snd_pcm_writei() could write. Make sure that all + * remaining bytes in the generic buffer can be written. + */ + alsa_free = alsa_free > generic_in_use ? alsa_free - generic_in_use : 0; + } + + return alsa_free; +} + static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len) { ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; @@ -872,7 +904,7 @@ static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo) } } -static void *alsa_audio_init(Audiodev *dev) +static void *alsa_audio_init(Audiodev *dev, Error **errp) { AudiodevAlsaOptions *aopts; assert(dev->driver == AUDIODEV_DRIVER_ALSA); @@ -881,28 +913,23 @@ static void *alsa_audio_init(Audiodev *dev) alsa_init_per_direction(aopts->in); alsa_init_per_direction(aopts->out); - /* - * need to define them, as otherwise alsa produces no sound - * doesn't set has_* so alsa_open can identify it wasn't set by the user - */ + /* don't set has_* so alsa_open can identify it wasn't set by the user */ if (!dev->u.alsa.out->has_period_length) { - /* 1024 frames assuming 44100Hz */ - dev->u.alsa.out->period_length = 1024 * 1000000 / 44100; + /* 256 frames assuming 44100Hz */ + dev->u.alsa.out->period_length = 5805; } if (!dev->u.alsa.out->has_buffer_length) { /* 4096 frames assuming 44100Hz */ - dev->u.alsa.out->buffer_length = 4096ll * 1000000 / 44100; + dev->u.alsa.out->buffer_length = 92880; } - /* - * OptsVisitor sets unspecified optional fields to zero, but do not depend - * on it... - */ if (!dev->u.alsa.in->has_period_length) { - dev->u.alsa.in->period_length = 0; + /* 256 frames assuming 44100Hz */ + dev->u.alsa.in->period_length = 5805; } if (!dev->u.alsa.in->has_buffer_length) { - dev->u.alsa.in->buffer_length = 0; + /* 4096 frames assuming 44100Hz */ + dev->u.alsa.in->buffer_length = 92880; } return dev; @@ -916,7 +943,7 @@ static struct audio_pcm_ops alsa_pcm_ops = { .init_out = alsa_init_out, .fini_out = alsa_fini_out, .write = alsa_write, - .buffer_get_free = audio_generic_buffer_get_free, + .buffer_get_free = alsa_buffer_get_free, .run_buffer_out = audio_generic_run_buffer_out, .enable_out = alsa_enable_out, @@ -933,7 +960,6 @@ static struct audio_driver alsa_audio_driver = { .init = alsa_audio_init, .fini = alsa_audio_fini, .pcm_ops = &alsa_pcm_ops, - .can_be_default = 1, .max_voices_out = INT_MAX, .max_voices_in = INT_MAX, .voice_size_out = sizeof (ALSAVoiceOut), diff --git a/audio/audio-hmp-cmds.c b/audio/audio-hmp-cmds.c new file mode 100644 index 0000000000..c9608b715b --- /dev/null +++ b/audio/audio-hmp-cmds.c @@ -0,0 +1,85 @@ +/* + * HMP commands related to audio backends + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "audio/audio.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" + +static QLIST_HEAD (capture_list_head, CaptureState) capture_head; + +void hmp_info_capture(Monitor *mon, const QDict *qdict) +{ + int i; + CaptureState *s; + + for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) { + monitor_printf(mon, "[%d]: ", i); + s->ops.info (s->opaque); + } +} + +void hmp_stopcapture(Monitor *mon, const QDict *qdict) +{ + int i; + int n = qdict_get_int(qdict, "n"); + CaptureState *s; + + for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) { + if (i == n) { + s->ops.destroy (s->opaque); + QLIST_REMOVE (s, entries); + g_free (s); + return; + } + } +} + +void hmp_wavcapture(Monitor *mon, const QDict *qdict) +{ + const char *path = qdict_get_str(qdict, "path"); + int freq = qdict_get_try_int(qdict, "freq", 44100); + int bits = qdict_get_try_int(qdict, "bits", 16); + int nchannels = qdict_get_try_int(qdict, "nchannels", 2); + const char *audiodev = qdict_get_str(qdict, "audiodev"); + CaptureState *s; + Error *local_err = NULL; + AudioState *as = audio_state_by_name(audiodev, &local_err); + + if (!as) { + error_report_err(local_err); + return; + } + + s = g_malloc0 (sizeof (*s)); + + if (wav_start_capture(as, s, path, freq, bits, nchannels)) { + monitor_printf(mon, "Failed to add wave capture\n"); + g_free (s); + return; + } + QLIST_INSERT_HEAD (&capture_head, s, entries); +} diff --git a/audio/audio.c b/audio/audio.c index a02f3ce5c6..af0ae33fed 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -28,10 +28,16 @@ #include "monitor/monitor.h" #include "qemu/timer.h" #include "qapi/error.h" +#include "qapi/clone-visitor.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-audio.h" +#include "qapi/qapi-commands-audio.h" +#include "qapi/qmp/qdict.h" #include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qemu/log.h" #include "qemu/module.h" +#include "qemu/help_option.h" #include "sysemu/sysemu.h" #include "sysemu/replay.h" #include "sysemu/runstate.h" @@ -57,40 +63,48 @@ const char *audio_prio_list[] = { "spice", CONFIG_AUDIO_DRIVERS "none", - "wav", NULL }; static QLIST_HEAD(, audio_driver) audio_drivers; -static AudiodevListHead audiodevs = QSIMPLEQ_HEAD_INITIALIZER(audiodevs); +static AudiodevListHead audiodevs = + QSIMPLEQ_HEAD_INITIALIZER(audiodevs); +static AudiodevListHead default_audiodevs = + QSIMPLEQ_HEAD_INITIALIZER(default_audiodevs); + void audio_driver_register(audio_driver *drv) { QLIST_INSERT_HEAD(&audio_drivers, drv, next); } -audio_driver *audio_driver_lookup(const char *name) +static audio_driver *audio_driver_lookup(const char *name) { struct audio_driver *d; + Error *local_err = NULL; + int rv; QLIST_FOREACH(d, &audio_drivers, next) { if (strcmp(name, d->name) == 0) { return d; } } - - audio_module_load_one(name); - QLIST_FOREACH(d, &audio_drivers, next) { - if (strcmp(name, d->name) == 0) { - return d; + rv = audio_module_load(name, &local_err); + if (rv > 0) { + QLIST_FOREACH(d, &audio_drivers, next) { + if (strcmp(name, d->name) == 0) { + return d; + } } + } else if (rv < 0) { + error_report_err(local_err); } - return NULL; } static QTAILQ_HEAD(AudioStateHead, AudioState) audio_states = QTAILQ_HEAD_INITIALIZER(audio_states); +static AudioState *default_audio_state; const struct mixeng_volume nominal_volume = { .mute = 0, @@ -103,8 +117,6 @@ const struct mixeng_volume nominal_volume = { #endif }; -static bool legacy_config = true; - int audio_bug (const char *funcname, int cond) { if (cond) { @@ -137,30 +149,10 @@ static inline int audio_bits_to_index (int bits) default: audio_bug ("bits_to_index", 1); AUD_log (NULL, "invalid bits %d\n", bits); - abort(); + return 0; } } -void *audio_calloc (const char *funcname, int nmemb, size_t size) -{ - int cond; - size_t len; - - len = nmemb * size; - cond = !nmemb || !size; - cond |= nmemb < 0; - cond |= len < size; - - if (audio_bug ("audio_calloc", cond)) { - AUD_log (NULL, "%s passed invalid arguments to audio_calloc\n", - funcname); - AUD_log (NULL, "nmemb=%d size=%zu (len=%zu)\n", nmemb, size, len); - abort(); - } - - return g_malloc0 (len); -} - void AUD_vlog (const char *cap, const char *fmt, va_list ap) { if (cap) { @@ -393,13 +385,6 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) /* * Capture */ -static void noop_conv (struct st_sample *dst, const void *src, int samples) -{ - (void) src; - (void) dst; - (void) samples; -} - static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioState *s, struct audsettings *as) { @@ -497,15 +482,8 @@ static int audio_attach_capture (HWVoiceOut *hw) sw->info = hw->info; sw->empty = 1; sw->active = hw->enabled; - sw->conv = noop_conv; - sw->ratio = ((int64_t) hw_cap->info.freq << 32) / sw->info.freq; sw->vol = nominal_volume; sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq); - if (!sw->rate) { - dolog ("Could not start rate conversion for `%s'\n", SW_NAME (sw)); - g_free (sw); - return -1; - } QLIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries); QLIST_INSERT_HEAD (&hw->cap_head, sc, entries); #ifdef DEBUG_CAPTURE @@ -540,9 +518,9 @@ static size_t audio_pcm_hw_find_min_in (HWVoiceIn *hw) static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw) { size_t live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw); - if (audio_bug(__func__, live > hw->conv_buf->size)) { - dolog("live=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size); - abort(); + if (audio_bug(__func__, live > hw->conv_buf.size)) { + dolog("live=%zu hw->conv_buf.size=%zu\n", live, hw->conv_buf.size); + return 0; } return live; } @@ -550,13 +528,13 @@ static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw) static size_t audio_pcm_hw_conv_in(HWVoiceIn *hw, void *pcm_buf, size_t samples) { size_t conv = 0; - STSampleBuffer *conv_buf = hw->conv_buf; + STSampleBuffer *conv_buf = &hw->conv_buf; while (samples) { uint8_t *src = advance(pcm_buf, conv * hw->info.bytes_per_frame); size_t proc = MIN(samples, conv_buf->size - conv_buf->pos); - hw->conv(conv_buf->samples + conv_buf->pos, src, proc); + hw->conv(conv_buf->buffer + conv_buf->pos, src, proc); conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size; samples -= proc; conv += proc; @@ -568,56 +546,65 @@ static size_t audio_pcm_hw_conv_in(HWVoiceIn *hw, void *pcm_buf, size_t samples) /* * Soft voice (capture) */ -static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size) +static void audio_pcm_sw_resample_in(SWVoiceIn *sw, + size_t frames_in_max, size_t frames_out_max, + size_t *total_in, size_t *total_out) { HWVoiceIn *hw = sw->hw; - size_t samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0; - struct st_sample *src, *dst = sw->buf; + struct st_sample *src, *dst; + size_t live, rpos, frames_in, frames_out; + + live = hw->total_samples_captured - sw->total_hw_samples_acquired; + rpos = audio_ring_posb(hw->conv_buf.pos, live, hw->conv_buf.size); + + /* resample conv_buf from rpos to end of buffer */ + src = hw->conv_buf.buffer + rpos; + frames_in = MIN(frames_in_max, hw->conv_buf.size - rpos); + dst = sw->resample_buf.buffer; + frames_out = frames_out_max; + st_rate_flow(sw->rate, src, dst, &frames_in, &frames_out); + rpos += frames_in; + *total_in = frames_in; + *total_out = frames_out; + + /* resample conv_buf from start of buffer if there are input frames left */ + if (frames_in_max - frames_in && rpos == hw->conv_buf.size) { + src = hw->conv_buf.buffer; + frames_in = frames_in_max - frames_in; + dst += frames_out; + frames_out = frames_out_max - frames_out; + st_rate_flow(sw->rate, src, dst, &frames_in, &frames_out); + *total_in += frames_in; + *total_out += frames_out; + } +} + +static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t buf_len) +{ + HWVoiceIn *hw = sw->hw; + size_t live, frames_out_max, total_in, total_out; live = hw->total_samples_captured - sw->total_hw_samples_acquired; if (!live) { return 0; } - if (audio_bug(__func__, live > hw->conv_buf->size)) { - dolog("live_in=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size); - abort(); + if (audio_bug(__func__, live > hw->conv_buf.size)) { + dolog("live_in=%zu hw->conv_buf.size=%zu\n", live, hw->conv_buf.size); + return 0; } - rpos = audio_ring_posb(hw->conv_buf->pos, live, hw->conv_buf->size); + frames_out_max = MIN(buf_len / sw->info.bytes_per_frame, + sw->resample_buf.size); - samples = size / sw->info.bytes_per_frame; - - swlim = (live * sw->ratio) >> 32; - swlim = MIN (swlim, samples); - - while (swlim) { - src = hw->conv_buf->samples + rpos; - if (hw->conv_buf->pos > rpos) { - isamp = hw->conv_buf->pos - rpos; - } else { - isamp = hw->conv_buf->size - rpos; - } - - if (!isamp) { - break; - } - osamp = swlim; - - st_rate_flow (sw->rate, src, dst, &isamp, &osamp); - swlim -= osamp; - rpos = (rpos + isamp) % hw->conv_buf->size; - dst += osamp; - ret += osamp; - total += isamp; - } + audio_pcm_sw_resample_in(sw, live, frames_out_max, &total_in, &total_out); if (!hw->pcm_ops->volume_in) { - mixeng_volume (sw->buf, ret, &sw->vol); + mixeng_volume(sw->resample_buf.buffer, total_out, &sw->vol); } + sw->clip(buf, sw->resample_buf.buffer, total_out); - sw->clip (buf, sw->buf, ret); - sw->total_hw_samples_acquired += total; - return ret * sw->info.bytes_per_frame; + sw->total_hw_samples_acquired += total_in; + return total_out * sw->info.bytes_per_frame; } /* @@ -653,9 +640,9 @@ static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live) if (nb_live1) { size_t live = smin; - if (audio_bug(__func__, live > hw->mix_buf->size)) { - dolog("live=%zu hw->mix_buf->size=%zu\n", live, hw->mix_buf->size); - abort(); + if (audio_bug(__func__, live > hw->mix_buf.size)) { + dolog("live=%zu hw->mix_buf.size=%zu\n", live, hw->mix_buf.size); + return 0; } return live; } @@ -671,17 +658,17 @@ static size_t audio_pcm_hw_get_free(HWVoiceOut *hw) static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len) { size_t clipped = 0; - size_t pos = hw->mix_buf->pos; + size_t pos = hw->mix_buf.pos; while (len) { - st_sample *src = hw->mix_buf->samples + pos; + st_sample *src = hw->mix_buf.buffer + pos; uint8_t *dst = advance(pcm_buf, clipped * hw->info.bytes_per_frame); - size_t samples_till_end_of_buf = hw->mix_buf->size - pos; + size_t samples_till_end_of_buf = hw->mix_buf.size - pos; size_t samples_to_clip = MIN(len, samples_till_end_of_buf); hw->clip(dst, src, samples_to_clip); - pos = (pos + samples_to_clip) % hw->mix_buf->size; + pos = (pos + samples_to_clip) % hw->mix_buf.size; len -= samples_to_clip; clipped += samples_to_clip; } @@ -690,84 +677,113 @@ static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len) /* * Soft voice (playback) */ -static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size) +static void audio_pcm_sw_resample_out(SWVoiceOut *sw, + size_t frames_in_max, size_t frames_out_max, + size_t *total_in, size_t *total_out) { - size_t hwsamples, samples, isamp, osamp, wpos, live, dead, left, blck; - size_t hw_free; - size_t ret = 0, pos = 0, total = 0; - - if (!sw) { - return size; - } - - hwsamples = sw->hw->mix_buf->size; + HWVoiceOut *hw = sw->hw; + struct st_sample *src, *dst; + size_t live, wpos, frames_in, frames_out; live = sw->total_hw_samples_mixed; - if (audio_bug(__func__, live > hwsamples)) { - dolog("live=%zu hw->mix_buf->size=%zu\n", live, hwsamples); - abort(); + wpos = (hw->mix_buf.pos + live) % hw->mix_buf.size; + + /* write to mix_buf from wpos to end of buffer */ + src = sw->resample_buf.buffer; + frames_in = frames_in_max; + dst = hw->mix_buf.buffer + wpos; + frames_out = MIN(frames_out_max, hw->mix_buf.size - wpos); + st_rate_flow_mix(sw->rate, src, dst, &frames_in, &frames_out); + wpos += frames_out; + *total_in = frames_in; + *total_out = frames_out; + + /* write to mix_buf from start of buffer if there are input frames left */ + if (frames_in_max - frames_in > 0 && wpos == hw->mix_buf.size) { + src += frames_in; + frames_in = frames_in_max - frames_in; + dst = hw->mix_buf.buffer; + frames_out = frames_out_max - frames_out; + st_rate_flow_mix(sw->rate, src, dst, &frames_in, &frames_out); + *total_in += frames_in; + *total_out += frames_out; + } +} + +static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t buf_len) +{ + HWVoiceOut *hw = sw->hw; + size_t live, dead, hw_free, sw_max, fe_max; + size_t frames_in_max, frames_out_max, total_in, total_out; + + live = sw->total_hw_samples_mixed; + if (audio_bug(__func__, live > hw->mix_buf.size)) { + dolog("live=%zu hw->mix_buf.size=%zu\n", live, hw->mix_buf.size); + return 0; } - if (live == hwsamples) { + if (live == hw->mix_buf.size) { #ifdef DEBUG_OUT dolog ("%s is full %zu\n", sw->name, live); #endif return 0; } - wpos = (sw->hw->mix_buf->pos + live) % hwsamples; - - dead = hwsamples - live; - hw_free = audio_pcm_hw_get_free(sw->hw); + dead = hw->mix_buf.size - live; + hw_free = audio_pcm_hw_get_free(hw); hw_free = hw_free > live ? hw_free - live : 0; - samples = ((int64_t)MIN(dead, hw_free) << 32) / sw->ratio; - samples = MIN(samples, size / sw->info.bytes_per_frame); - if (samples) { - sw->conv(sw->buf, buf, samples); + frames_out_max = MIN(dead, hw_free); + sw_max = st_rate_frames_in(sw->rate, frames_out_max); + fe_max = MIN(buf_len / sw->info.bytes_per_frame + sw->resample_buf.pos, + sw->resample_buf.size); + frames_in_max = MIN(sw_max, fe_max); + if (!frames_in_max) { + return 0; + } + + if (frames_in_max > sw->resample_buf.pos) { + sw->conv(sw->resample_buf.buffer + sw->resample_buf.pos, + buf, frames_in_max - sw->resample_buf.pos); if (!sw->hw->pcm_ops->volume_out) { - mixeng_volume(sw->buf, samples, &sw->vol); + mixeng_volume(sw->resample_buf.buffer + sw->resample_buf.pos, + frames_in_max - sw->resample_buf.pos, &sw->vol); } } - while (samples) { - dead = hwsamples - live; - left = hwsamples - wpos; - blck = MIN (dead, left); - if (!blck) { - break; - } - isamp = samples; - osamp = blck; - st_rate_flow_mix ( - sw->rate, - sw->buf + pos, - sw->hw->mix_buf->samples + wpos, - &isamp, - &osamp - ); - ret += isamp; - samples -= isamp; - pos += isamp; - live += osamp; - wpos = (wpos + osamp) % hwsamples; - total += osamp; - } + audio_pcm_sw_resample_out(sw, frames_in_max, frames_out_max, + &total_in, &total_out); - sw->total_hw_samples_mixed += total; + sw->total_hw_samples_mixed += total_out; sw->empty = sw->total_hw_samples_mixed == 0; + /* + * Upsampling may leave one audio frame in the resample buffer. Decrement + * total_in by one if there was a leftover frame from the previous resample + * pass in the resample buffer. Increment total_in by one if the current + * resample pass left one frame in the resample buffer. + */ + if (frames_in_max - total_in == 1) { + /* copy one leftover audio frame to the beginning of the buffer */ + *sw->resample_buf.buffer = *(sw->resample_buf.buffer + total_in); + total_in += 1 - sw->resample_buf.pos; + sw->resample_buf.pos = 1; + } else if (total_in >= sw->resample_buf.pos) { + total_in -= sw->resample_buf.pos; + sw->resample_buf.pos = 0; + } + #ifdef DEBUG_OUT dolog ( - "%s: write size %zu ret %zu total sw %zu\n", - SW_NAME (sw), - size / sw->info.bytes_per_frame, - ret, + "%s: write size %zu written %zu total mixed %zu\n", + SW_NAME(sw), + buf_len / sw->info.bytes_per_frame, + total_in, sw->total_hw_samples_mixed ); #endif - return ret * sw->info.bytes_per_frame; + return total_in * sw->info.bytes_per_frame; } #ifdef DEBUG_AUDIO @@ -994,24 +1010,19 @@ static size_t audio_get_avail (SWVoiceIn *sw) } live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; - if (audio_bug(__func__, live > sw->hw->conv_buf->size)) { - dolog("live=%zu sw->hw->conv_buf->size=%zu\n", live, - sw->hw->conv_buf->size); - abort(); + if (audio_bug(__func__, live > sw->hw->conv_buf.size)) { + dolog("live=%zu sw->hw->conv_buf.size=%zu\n", live, + sw->hw->conv_buf.size); + return 0; } ldebug ( - "%s: get_avail live %zu ret %" PRId64 "\n", + "%s: get_avail live %zu frontend frames %u\n", SW_NAME (sw), - live, (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame + live, st_rate_frames_out(sw->rate, live) ); - return (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame; -} - -static size_t audio_sw_bytes_free(SWVoiceOut *sw, size_t free) -{ - return (((int64_t)free << 32) / sw->ratio) * sw->info.bytes_per_frame; + return live; } static size_t audio_get_free(SWVoiceOut *sw) @@ -1024,17 +1035,17 @@ static size_t audio_get_free(SWVoiceOut *sw) live = sw->total_hw_samples_mixed; - if (audio_bug(__func__, live > sw->hw->mix_buf->size)) { - dolog("live=%zu sw->hw->mix_buf->size=%zu\n", live, - sw->hw->mix_buf->size); - abort(); + if (audio_bug(__func__, live > sw->hw->mix_buf.size)) { + dolog("live=%zu sw->hw->mix_buf.size=%zu\n", live, + sw->hw->mix_buf.size); + return 0; } - dead = sw->hw->mix_buf->size - live; + dead = sw->hw->mix_buf.size - live; #ifdef DEBUG_OUT - dolog("%s: get_free live %zu dead %zu sw_bytes %zu\n", - SW_NAME(sw), live, dead, audio_sw_bytes_free(sw, dead)); + dolog("%s: get_free live %zu dead %zu frontend frames %u\n", + SW_NAME(sw), live, dead, st_rate_frames_in(sw->rate, dead)); #endif return dead; @@ -1050,32 +1061,40 @@ static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos, for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) { SWVoiceOut *sw = &sc->sw; - int rpos2 = rpos; + size_t rpos2 = rpos; n = samples; while (n) { - size_t till_end_of_hw = hw->mix_buf->size - rpos2; - size_t to_write = MIN(till_end_of_hw, n); - size_t bytes = to_write * hw->info.bytes_per_frame; - size_t written; + size_t till_end_of_hw = hw->mix_buf.size - rpos2; + size_t to_read = MIN(till_end_of_hw, n); + size_t live, frames_in, frames_out; - sw->buf = hw->mix_buf->samples + rpos2; - written = audio_pcm_sw_write (sw, NULL, bytes); - if (written - bytes) { - dolog("Could not mix %zu bytes into a capture " + sw->resample_buf.buffer = hw->mix_buf.buffer + rpos2; + sw->resample_buf.size = to_read; + live = sw->total_hw_samples_mixed; + + audio_pcm_sw_resample_out(sw, + to_read, sw->hw->mix_buf.size - live, + &frames_in, &frames_out); + + sw->total_hw_samples_mixed += frames_out; + sw->empty = sw->total_hw_samples_mixed == 0; + + if (to_read - frames_in) { + dolog("Could not mix %zu frames into a capture " "buffer, mixed %zu\n", - bytes, written); + to_read, frames_in); break; } - n -= to_write; - rpos2 = (rpos2 + to_write) % hw->mix_buf->size; + n -= to_read; + rpos2 = (rpos2 + to_read) % hw->mix_buf.size; } } } - n = MIN(samples, hw->mix_buf->size - rpos); - mixeng_clear(hw->mix_buf->samples + rpos, n); - mixeng_clear(hw->mix_buf->samples, samples - n); + n = MIN(samples, hw->mix_buf.size - rpos); + mixeng_clear(hw->mix_buf.buffer + rpos, n); + mixeng_clear(hw->mix_buf.buffer, samples - n); } static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live) @@ -1101,7 +1120,7 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live) live -= proc; clipped += proc; - hw->mix_buf->pos = (hw->mix_buf->pos + proc) % hw->mix_buf->size; + hw->mix_buf.pos = (hw->mix_buf.pos + proc) % hw->mix_buf.size; if (proc == 0 || proc < decr) { break; @@ -1120,8 +1139,12 @@ static void audio_run_out (AudioState *s) HWVoiceOut *hw = NULL; SWVoiceOut *sw; - if (!audio_get_pdo_out(s->dev)->mixing_engine) { - while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) { + while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) { + size_t played, live, prev_rpos; + size_t hw_free = audio_pcm_hw_get_free(hw); + int nb_live; + + if (!audio_get_pdo_out(s->dev)->mixing_engine) { /* there is exactly 1 sw for each hw with no mixeng */ sw = hw->sw_head.lh_first; @@ -1134,16 +1157,16 @@ static void audio_run_out (AudioState *s) } if (sw->active) { - sw->callback.fn(sw->callback.opaque, INT_MAX); + sw->callback.fn(sw->callback.opaque, + hw_free * sw->info.bytes_per_frame); } - } - return; - } - while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) { - size_t played, live, prev_rpos; - size_t hw_free = audio_pcm_hw_get_free(hw); - int nb_live; + if (hw->pcm_ops->run_buffer_out) { + hw->pcm_ops->run_buffer_out(hw); + } + + continue; + } for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { if (sw->active) { @@ -1151,13 +1174,16 @@ static void audio_run_out (AudioState *s) size_t free; if (hw_free > sw->total_hw_samples_mixed) { - free = audio_sw_bytes_free(sw, + free = st_rate_frames_in(sw->rate, MIN(sw_free, hw_free - sw->total_hw_samples_mixed)); } else { free = 0; } - if (free > 0) { - sw->callback.fn(sw->callback.opaque, free); + if (free > sw->resample_buf.pos) { + free = MIN(free, sw->resample_buf.size) + - sw->resample_buf.pos; + sw->callback.fn(sw->callback.opaque, + free * sw->info.bytes_per_frame); } } } @@ -1167,9 +1193,9 @@ static void audio_run_out (AudioState *s) live = 0; } - if (audio_bug(__func__, live > hw->mix_buf->size)) { - dolog("live=%zu hw->mix_buf->size=%zu\n", live, hw->mix_buf->size); - abort(); + if (audio_bug(__func__, live > hw->mix_buf.size)) { + dolog("live=%zu hw->mix_buf.size=%zu\n", live, hw->mix_buf.size); + continue; } if (hw->pending_disable && !nb_live) { @@ -1196,13 +1222,13 @@ static void audio_run_out (AudioState *s) continue; } - prev_rpos = hw->mix_buf->pos; + prev_rpos = hw->mix_buf.pos; played = audio_pcm_hw_run_out(hw, live); replay_audio_out(&played); - if (audio_bug(__func__, hw->mix_buf->pos >= hw->mix_buf->size)) { - dolog("hw->mix_buf->pos=%zu hw->mix_buf->size=%zu played=%zu\n", - hw->mix_buf->pos, hw->mix_buf->size, played); - abort(); + if (audio_bug(__func__, hw->mix_buf.pos >= hw->mix_buf.size)) { + dolog("hw->mix_buf.pos=%zu hw->mix_buf.size=%zu played=%zu\n", + hw->mix_buf.pos, hw->mix_buf.size, played); + hw->mix_buf.pos = 0; } #ifdef DEBUG_OUT @@ -1222,7 +1248,7 @@ static void audio_run_out (AudioState *s) if (audio_bug(__func__, played > sw->total_hw_samples_mixed)) { dolog("played=%zu sw->total_hw_samples_mixed=%zu\n", played, sw->total_hw_samples_mixed); - abort(); + played = sw->total_hw_samples_mixed; } sw->total_hw_samples_mixed -= played; @@ -1283,10 +1309,10 @@ static void audio_run_in (AudioState *s) if (replay_mode != REPLAY_MODE_PLAY) { captured = audio_pcm_hw_run_in( - hw, hw->conv_buf->size - audio_pcm_hw_get_live_in(hw)); + hw, hw->conv_buf.size - audio_pcm_hw_get_live_in(hw)); } - replay_audio_in(&captured, hw->conv_buf->samples, &hw->conv_buf->pos, - hw->conv_buf->size); + replay_audio_in(&captured, hw->conv_buf.buffer, &hw->conv_buf.pos, + hw->conv_buf.size); min = audio_pcm_hw_find_min_in (hw); hw->total_samples_captured += captured - min; @@ -1296,11 +1322,14 @@ static void audio_run_in (AudioState *s) sw->total_hw_samples_acquired -= min; if (sw->active) { + size_t sw_avail = audio_get_avail(sw); size_t avail; - avail = audio_get_avail (sw); + avail = st_rate_frames_out(sw->rate, sw_avail); if (avail > 0) { - sw->callback.fn (sw->callback.opaque, avail); + avail = MIN(avail, sw->resample_buf.size); + sw->callback.fn(sw->callback.opaque, + avail * sw->info.bytes_per_frame); } } } @@ -1317,14 +1346,14 @@ static void audio_run_capture (AudioState *s) SWVoiceOut *sw; captured = live = audio_pcm_hw_get_live_out (hw, NULL); - rpos = hw->mix_buf->pos; + rpos = hw->mix_buf.pos; while (live) { - size_t left = hw->mix_buf->size - rpos; + size_t left = hw->mix_buf.size - rpos; size_t to_capture = MIN(live, left); struct st_sample *src; struct capture_callback *cb; - src = hw->mix_buf->samples + rpos; + src = hw->mix_buf.buffer + rpos; hw->clip (cap->buf, src, to_capture); mixeng_clear (src, to_capture); @@ -1332,10 +1361,10 @@ static void audio_run_capture (AudioState *s) cb->ops.capture (cb->opaque, cap->buf, to_capture * hw->info.bytes_per_frame); } - rpos = (rpos + to_capture) % hw->mix_buf->size; + rpos = (rpos + to_capture) % hw->mix_buf.size; live -= to_capture; } - hw->mix_buf->pos = rpos; + hw->mix_buf.pos = rpos; for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { if (!sw->active && sw->empty) { @@ -1345,7 +1374,7 @@ static void audio_run_capture (AudioState *s) if (audio_bug(__func__, captured > sw->total_hw_samples_mixed)) { dolog("captured=%zu sw->total_hw_samples_mixed=%zu\n", captured, sw->total_hw_samples_mixed); - abort(); + captured = sw->total_hw_samples_mixed; } sw->total_hw_samples_mixed -= captured; @@ -1500,10 +1529,6 @@ size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size) } } - if (hw->pcm_ops->run_buffer_out) { - hw->pcm_ops->run_buffer_out(hw); - } - return total; } @@ -1532,9 +1557,11 @@ size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size) } static int audio_driver_init(AudioState *s, struct audio_driver *drv, - bool msg, Audiodev *dev) + Audiodev *dev, Error **errp) { - s->drv_opaque = drv->init(dev); + Error *local_err = NULL; + + s->drv_opaque = drv->init(dev, &local_err); if (s->drv_opaque) { if (!drv->pcm_ops->get_buffer_in) { @@ -1546,13 +1573,15 @@ static int audio_driver_init(AudioState *s, struct audio_driver *drv, drv->pcm_ops->put_buffer_out = audio_generic_put_buffer_out; } - audio_init_nb_voices_out(s, drv); - audio_init_nb_voices_in(s, drv); + audio_init_nb_voices_out(s, drv, 1); + audio_init_nb_voices_in(s, drv, 0); s->drv = drv; return 0; } else { - if (msg) { - dolog("Could not init `%s' audio driver\n", drv->name); + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, "Could not init `%s' audio driver", drv->name); } return -1; } @@ -1632,6 +1661,7 @@ static void free_audio_state(AudioState *s) void audio_cleanup(void) { + default_audio_state = NULL; while (!QTAILQ_EMPTY(&audio_states)) { AudioState *s = QTAILQ_FIRST(&audio_states); QTAILQ_REMOVE(&audio_states, s, list); @@ -1653,24 +1683,30 @@ static const VMStateDescription vmstate_audio = { .version_id = 1, .minimum_version_id = 1, .needed = vmstate_audio_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_END_OF_LIST() } }; -static void audio_validate_opts(Audiodev *dev, Error **errp); - -static AudiodevListEntry *audiodev_find( - AudiodevListHead *head, const char *drvname) +void audio_create_default_audiodevs(void) { - AudiodevListEntry *e; - QSIMPLEQ_FOREACH(e, head, next) { - if (strcmp(AudiodevDriver_str(e->dev->driver), drvname) == 0) { - return e; + for (int i = 0; audio_prio_list[i]; i++) { + if (audio_driver_lookup(audio_prio_list[i])) { + QDict *dict = qdict_new(); + Audiodev *dev = NULL; + Visitor *v; + + qdict_put_str(dict, "driver", audio_prio_list[i]); + qdict_put_str(dict, "id", "#default"); + + v = qobject_input_visitor_new_keyval(QOBJECT(dict)); + qobject_unref(dict); + visit_type_Audiodev(v, NULL, &dev, &error_fatal); + visit_free(v); + + audio_define_default(dev, &error_abort); } } - - return NULL; } /* @@ -1679,62 +1715,16 @@ static AudiodevListEntry *audiodev_find( * if dev == NULL => legacy implicit initialization, return the already created * state or create a new one */ -static AudioState *audio_init(Audiodev *dev, const char *name) +static AudioState *audio_init(Audiodev *dev, Error **errp) { static bool atexit_registered; - size_t i; int done = 0; - const char *drvname = NULL; - VMChangeStateEntry *e; + const char *drvname; + VMChangeStateEntry *vmse; AudioState *s; struct audio_driver *driver; - /* silence gcc warning about uninitialized variable */ - AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head); - - if (using_spice) { - /* - * When using spice allow the spice audio driver being picked - * as default. - * - * Temporary hack. Using audio devices without explicit - * audiodev= property is already deprecated. Same goes for - * the -soundhw switch. Once this support gets finally - * removed we can also drop the concept of a default audio - * backend and this can go away. - */ - driver = audio_driver_lookup("spice"); - if (driver) { - driver->can_be_default = 1; - } - } - - if (dev) { - /* -audiodev option */ - legacy_config = false; - drvname = AudiodevDriver_str(dev->driver); - } else if (!QTAILQ_EMPTY(&audio_states)) { - if (!legacy_config) { - dolog("Device %s: audiodev default parameter is deprecated, please " - "specify audiodev=%s\n", name, - QTAILQ_FIRST(&audio_states)->dev->id); - } - return QTAILQ_FIRST(&audio_states); - } else { - /* legacy implicit initialization */ - head = audio_handle_legacy_opts(); - /* - * In case of legacy initialization, all Audiodevs in the list will have - * the same configuration (except the driver), so it doesn't matter which - * one we chose. We need an Audiodev to set up AudioState before we can - * init a driver. Also note that dev at this point is still in the - * list. - */ - dev = QSIMPLEQ_FIRST(&head)->dev; - audio_validate_opts(dev, &error_abort); - } s = g_new0(AudioState, 1); - s->dev = dev; QLIST_INIT (&s->hw_head_out); QLIST_INIT (&s->hw_head_in); @@ -1743,56 +1733,42 @@ static AudioState *audio_init(Audiodev *dev, const char *name) atexit(audio_cleanup); atexit_registered = true; } - QTAILQ_INSERT_TAIL(&audio_states, s, list); s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s); - s->nb_hw_voices_out = audio_get_pdo_out(dev)->voices; - s->nb_hw_voices_in = audio_get_pdo_in(dev)->voices; - - if (s->nb_hw_voices_out <= 0) { - dolog ("Bogus number of playback voices %d, setting to 1\n", - s->nb_hw_voices_out); - s->nb_hw_voices_out = 1; - } - - if (s->nb_hw_voices_in <= 0) { - dolog ("Bogus number of capture voices %d, setting to 0\n", - s->nb_hw_voices_in); - s->nb_hw_voices_in = 0; - } - - if (drvname) { + if (dev) { + /* -audiodev option */ + s->dev = dev; + drvname = AudiodevDriver_str(dev->driver); driver = audio_driver_lookup(drvname); if (driver) { - done = !audio_driver_init(s, driver, true, dev); + done = !audio_driver_init(s, driver, dev, errp); } else { - dolog ("Unknown audio driver `%s'\n", drvname); + error_setg(errp, "Unknown audio driver `%s'", drvname); + } + if (!done) { + goto out; } } else { - for (i = 0; audio_prio_list[i]; i++) { - AudiodevListEntry *e = audiodev_find(&head, audio_prio_list[i]); - driver = audio_driver_lookup(audio_prio_list[i]); - - if (e && driver) { - s->dev = dev = e->dev; - audio_validate_opts(dev, &error_abort); - done = !audio_driver_init(s, driver, false, dev); - if (done) { - e->dev = NULL; - break; - } + assert(!default_audio_state); + for (;;) { + AudiodevListEntry *e = QSIMPLEQ_FIRST(&default_audiodevs); + if (!e) { + error_setg(errp, "no default audio driver available"); + goto out; } + s->dev = dev = e->dev; + QSIMPLEQ_REMOVE_HEAD(&default_audiodevs, next); + g_free(e); + drvname = AudiodevDriver_str(dev->driver); + driver = audio_driver_lookup(drvname); + if (!audio_driver_init(s, driver, dev, NULL)) { + break; + } + qapi_free_Audiodev(dev); + s->dev = NULL; } } - audio_free_audiodev_list(&head); - - if (!done) { - driver = audio_driver_lookup("none"); - done = !audio_driver_init(s, driver, false, dev); - assert(done); - dolog("warning: Using timer based audio emulation\n"); - } if (dev->timer_period <= 0) { s->period_ticks = 1; @@ -1800,36 +1776,51 @@ static AudioState *audio_init(Audiodev *dev, const char *name) s->period_ticks = dev->timer_period * (int64_t)SCALE_US; } - e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s); - if (!e) { + vmse = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s); + if (!vmse) { dolog ("warning: Could not register change state handler\n" "(Audio can continue looping even after stopping the VM)\n"); } + QTAILQ_INSERT_TAIL(&audio_states, s, list); QLIST_INIT (&s->card_head); - vmstate_register (NULL, 0, &vmstate_audio, s); + vmstate_register_any(NULL, &vmstate_audio, s); return s; + +out: + free_audio_state(s); + return NULL; } -void audio_free_audiodev_list(AudiodevListHead *head) +AudioState *audio_get_default_audio_state(Error **errp) { - AudiodevListEntry *e; - while ((e = QSIMPLEQ_FIRST(head))) { - QSIMPLEQ_REMOVE_HEAD(head, next); - qapi_free_Audiodev(e->dev); - g_free(e); + if (!default_audio_state) { + default_audio_state = audio_init(NULL, errp); + if (!default_audio_state) { + if (!QSIMPLEQ_EMPTY(&audiodevs)) { + error_append_hint(errp, "Perhaps you wanted to use -audio or set audiodev=%s?\n", + QSIMPLEQ_FIRST(&audiodevs)->dev->id); + } + } } + + return default_audio_state; } -void AUD_register_card (const char *name, QEMUSoundCard *card) +bool AUD_register_card (const char *name, QEMUSoundCard *card, Error **errp) { if (!card->state) { - card->state = audio_init(NULL, name); + card->state = audio_get_default_audio_state(errp); + if (!card->state) { + return false; + } } card->name = g_strdup (name); memset (&card->entries, 0, sizeof (card->entries)); QLIST_INSERT_HEAD(&card->state->card_head, card, entries); + + return true; } void AUD_remove_card (QEMUSoundCard *card) @@ -1851,10 +1842,8 @@ CaptureVoiceOut *AUD_add_capture( struct capture_callback *cb; if (!s) { - if (!legacy_config) { - dolog("Capturing without setting an audiodev is deprecated\n"); - } - s = audio_init(NULL, NULL); + error_report("Capturing without setting an audiodev is not supported"); + abort(); } if (!audio_get_pdo_out(s->dev)->mixing_engine) { @@ -1875,10 +1864,8 @@ CaptureVoiceOut *AUD_add_capture( cap = audio_pcm_capture_find_specific(s, as); if (cap) { QLIST_INSERT_HEAD (&cap->cb_head, cb, entries); - return cap; } else { HWVoiceOut *hw; - CaptureVoiceOut *cap; cap = g_malloc0(sizeof(*cap)); @@ -1894,7 +1881,7 @@ CaptureVoiceOut *AUD_add_capture( audio_pcm_init_info (&hw->info, as); - cap->buf = g_malloc0_n(hw->mix_buf->size, hw->info.bytes_per_frame); + cap->buf = g_malloc0_n(hw->mix_buf.size, hw->info.bytes_per_frame); if (hw->info.is_float) { hw->clip = mixeng_clip_float[hw->info.nchannels == 2]; @@ -1912,8 +1899,9 @@ CaptureVoiceOut *AUD_add_capture( QLIST_FOREACH(hw, &s->hw_head_out, entries) { audio_attach_capture (hw); } - return cap; } + + return cap; } void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque) @@ -1946,7 +1934,7 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque) sw = sw1; } QLIST_REMOVE (cap, entries); - g_free (cap->hw.mix_buf); + g_free(cap->hw.mix_buf.buffer); g_free (cap->buf); g_free (cap); } @@ -2004,28 +1992,50 @@ void audio_create_pdos(Audiodev *dev) switch (dev->driver) { #define CASE(DRIVER, driver, pdo_name) \ case AUDIODEV_DRIVER_##DRIVER: \ - if (!dev->u.driver.has_in) { \ + if (!dev->u.driver.in) { \ dev->u.driver.in = g_malloc0( \ sizeof(Audiodev##pdo_name##PerDirectionOptions)); \ - dev->u.driver.has_in = true; \ } \ - if (!dev->u.driver.has_out) { \ + if (!dev->u.driver.out) { \ dev->u.driver.out = g_malloc0( \ sizeof(Audiodev##pdo_name##PerDirectionOptions)); \ - dev->u.driver.has_out = true; \ } \ break CASE(NONE, none, ); +#ifdef CONFIG_AUDIO_ALSA CASE(ALSA, alsa, Alsa); +#endif +#ifdef CONFIG_AUDIO_COREAUDIO CASE(COREAUDIO, coreaudio, Coreaudio); +#endif +#ifdef CONFIG_DBUS_DISPLAY CASE(DBUS, dbus, ); +#endif +#ifdef CONFIG_AUDIO_DSOUND CASE(DSOUND, dsound, ); +#endif +#ifdef CONFIG_AUDIO_JACK CASE(JACK, jack, Jack); +#endif +#ifdef CONFIG_AUDIO_OSS CASE(OSS, oss, Oss); +#endif +#ifdef CONFIG_AUDIO_PA CASE(PA, pa, Pa); +#endif +#ifdef CONFIG_AUDIO_PIPEWIRE + CASE(PIPEWIRE, pipewire, Pipewire); +#endif +#ifdef CONFIG_AUDIO_SDL CASE(SDL, sdl, Sdl); +#endif +#ifdef CONFIG_AUDIO_SNDIO + CASE(SNDIO, sndio, ); +#endif +#ifdef CONFIG_SPICE CASE(SPICE, spice, ); +#endif CASE(WAV, wav, ); case AUDIODEV_DRIVER__MAX: @@ -2097,10 +2107,28 @@ static void audio_validate_opts(Audiodev *dev, Error **errp) } } +void audio_help(void) +{ + int i; + + printf("Available audio drivers:\n"); + + for (i = 0; i < AUDIODEV_DRIVER__MAX; i++) { + audio_driver *driver = audio_driver_lookup(AudiodevDriver_str(i)); + if (driver) { + printf("%s\n", driver->name); + } + } +} + void audio_parse_option(const char *opt) { Audiodev *dev = NULL; + if (is_help_option(opt)) { + audio_help(); + exit(EXIT_SUCCESS); + } Visitor *v = qobject_input_visitor_new_str(opt, "driver", &error_fatal); visit_type_Audiodev(v, NULL, &dev, &error_fatal); visit_free(v); @@ -2119,12 +2147,23 @@ void audio_define(Audiodev *dev) QSIMPLEQ_INSERT_TAIL(&audiodevs, e, next); } +void audio_define_default(Audiodev *dev, Error **errp) +{ + AudiodevListEntry *e; + + audio_validate_opts(dev, errp); + + e = g_new0(AudiodevListEntry, 1); + e->dev = dev; + QSIMPLEQ_INSERT_TAIL(&default_audiodevs, e, next); +} + void audio_init_audiodevs(void) { AudiodevListEntry *e; QSIMPLEQ_FOREACH(e, &audiodevs, next) { - audio_init(e->dev, NULL); + audio_init(e->dev, &error_fatal); } } @@ -2187,7 +2226,7 @@ int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo, audioformat_bytes_per_sample(as->fmt); } -AudioState *audio_state_by_name(const char *name) +AudioState *audio_state_by_name(const char *name, Error **errp) { AudioState *s; QTAILQ_FOREACH(s, &audio_states, list) { @@ -2196,6 +2235,7 @@ AudioState *audio_state_by_name(const char *name) return s; } } + error_setg(errp, "audiodev '%s' not found", name); return NULL; } @@ -2223,26 +2263,49 @@ void audio_rate_start(RateCtl *rate) rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); } -size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate, - size_t bytes_avail) +size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info) { int64_t now; int64_t ticks; int64_t bytes; - int64_t samples; - size_t ret; + int64_t frames; now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ticks = now - rate->start_ticks; bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND); - samples = (bytes - rate->bytes_sent) / info->bytes_per_frame; - if (samples < 0 || samples > 65536) { - AUD_log(NULL, "Resetting rate control (%" PRId64 " samples)\n", samples); + frames = (bytes - rate->bytes_sent) / info->bytes_per_frame; + if (frames < 0 || frames > 65536) { + AUD_log(NULL, "Resetting rate control (%" PRId64 " frames)\n", frames); audio_rate_start(rate); - samples = 0; + frames = 0; } - ret = MIN(samples * info->bytes_per_frame, bytes_avail); - rate->bytes_sent += ret; + return frames * info->bytes_per_frame; +} + +void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used) +{ + rate->bytes_sent += bytes_used; +} + +size_t audio_rate_get_bytes(RateCtl *rate, struct audio_pcm_info *info, + size_t bytes_avail) +{ + size_t bytes; + + bytes = audio_rate_peek_bytes(rate, info); + bytes = MIN(bytes, bytes_avail); + audio_rate_add_bytes(rate, bytes); + + return bytes; +} + +AudiodevList *qmp_query_audiodevs(Error **errp) +{ + AudiodevList *ret = NULL; + AudiodevListEntry *e; + QSIMPLEQ_FOREACH(e, &audiodevs, next) { + QAPI_LIST_PREPEND(ret, QAPI_CLONE(Audiodev, e->dev)); + } return ret; } diff --git a/audio/audio.h b/audio/audio.h index b5e17cd218..fcc22307be 100644 --- a/audio/audio.h +++ b/audio/audio.h @@ -94,7 +94,7 @@ typedef struct QEMUAudioTimeStamp { void AUD_vlog (const char *cap, const char *fmt, va_list ap) G_GNUC_PRINTF(2, 0); void AUD_log (const char *cap, const char *fmt, ...) G_GNUC_PRINTF(2, 3); -void AUD_register_card (const char *name, QEMUSoundCard *card); +bool AUD_register_card (const char *name, QEMUSoundCard *card, Error **errp); void AUD_remove_card (QEMUSoundCard *card); CaptureVoiceOut *AUD_add_capture( AudioState *s, @@ -169,11 +169,14 @@ void audio_sample_from_uint64(void *samples, int pos, uint64_t left, uint64_t right); void audio_define(Audiodev *audio); +void audio_define_default(Audiodev *dev, Error **errp); void audio_parse_option(const char *opt); +void audio_create_default_audiodevs(void); void audio_init_audiodevs(void); -void audio_legacy_help(void); +void audio_help(void); -AudioState *audio_state_by_name(const char *name); +AudioState *audio_state_by_name(const char *name, Error **errp); +AudioState *audio_get_default_audio_state(Error **errp); const char *audio_get_id(QEMUSoundCard *card); #define DEFINE_AUDIO_PROPERTIES(_s, _f) \ diff --git a/audio/audio_int.h b/audio/audio_int.h index 2a6914d2aa..2d079d00a2 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -58,7 +58,7 @@ typedef struct SWVoiceCap SWVoiceCap; typedef struct STSampleBuffer { size_t pos, size; - st_sample samples[]; + st_sample *buffer; } STSampleBuffer; typedef struct HWVoiceOut { @@ -71,7 +71,7 @@ typedef struct HWVoiceOut { f_sample *clip; uint64_t ts_helper; - STSampleBuffer *mix_buf; + STSampleBuffer mix_buf; void *buf_emul; size_t pos_emul, pending_emul, size_emul; @@ -93,7 +93,7 @@ typedef struct HWVoiceIn { size_t total_samples_captured; uint64_t ts_helper; - STSampleBuffer *conv_buf; + STSampleBuffer conv_buf; void *buf_emul; size_t pos_emul, pending_emul, size_emul; @@ -108,8 +108,7 @@ struct SWVoiceOut { AudioState *s; struct audio_pcm_info info; t_sample *conv; - int64_t ratio; - struct st_sample *buf; + STSampleBuffer resample_buf; void *rate; size_t total_hw_samples_mixed; int active; @@ -126,10 +125,9 @@ struct SWVoiceIn { AudioState *s; int active; struct audio_pcm_info info; - int64_t ratio; void *rate; size_t total_hw_samples_acquired; - struct st_sample *buf; + STSampleBuffer resample_buf; f_sample *clip; HWVoiceIn *hw; char *name; @@ -142,17 +140,16 @@ typedef struct audio_driver audio_driver; struct audio_driver { const char *name; const char *descr; - void *(*init) (Audiodev *); + void *(*init) (Audiodev *, Error **); void (*fini) (void *); #ifdef CONFIG_GIO - void (*set_dbus_server) (AudioState *s, GDBusObjectManagerServer *manager); + void (*set_dbus_server) (AudioState *s, GDBusObjectManagerServer *manager, bool p2p); #endif struct audio_pcm_ops *pcm_ops; - int can_be_default; int max_voices_out; int max_voices_in; - int voice_size_out; - int voice_size_in; + size_t voice_size_out; + size_t voice_size_in; QLIST_ENTRY(audio_driver) next; }; @@ -245,13 +242,11 @@ extern const struct mixeng_volume nominal_volume; extern const char *audio_prio_list[]; void audio_driver_register(audio_driver *drv); -audio_driver *audio_driver_lookup(const char *name); void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); int audio_bug (const char *funcname, int cond); -void *audio_calloc (const char *funcname, int nmemb, size_t size); void audio_run(AudioState *s, const char *msg); @@ -263,7 +258,9 @@ typedef struct RateCtl { } RateCtl; void audio_rate_start(RateCtl *rate); -size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate, +size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info); +void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used); +size_t audio_rate_get_bytes(RateCtl *rate, struct audio_pcm_info *info, size_t bytes_avail); static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len) @@ -292,18 +289,12 @@ static inline size_t audio_ring_posb(size_t pos, size_t dist, size_t len) #define ldebug(fmt, ...) (void)0 #endif -#define AUDIO_STRINGIFY_(n) #n -#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n) - typedef struct AudiodevListEntry { Audiodev *dev; QSIMPLEQ_ENTRY(AudiodevListEntry) next; } AudiodevListEntry; typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead; -AudiodevListHead audio_handle_legacy_opts(void); - -void audio_free_audiodev_list(AudiodevListHead *head); void audio_create_pdos(Audiodev *dev); AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev); diff --git a/audio/audio_legacy.c b/audio/audio_legacy.c deleted file mode 100644 index 595949f52c..0000000000 --- a/audio/audio_legacy.c +++ /dev/null @@ -1,555 +0,0 @@ -/* - * QEMU Audio subsystem: legacy configuration handling - * - * Copyright (c) 2015-2019 Zoltán Kővágó - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "audio.h" -#include "audio_int.h" -#include "qemu/cutils.h" -#include "qemu/timer.h" -#include "qapi/error.h" -#include "qapi/qapi-visit-audio.h" -#include "qapi/visitor-impl.h" - -#define AUDIO_CAP "audio-legacy" -#include "audio_int.h" - -static uint32_t toui32(const char *str) -{ - unsigned long long ret; - if (parse_uint_full(str, &ret, 10) || ret > UINT32_MAX) { - dolog("Invalid integer value `%s'\n", str); - exit(1); - } - return ret; -} - -/* helper functions to convert env variables */ -static void get_bool(const char *env, bool *dst, bool *has_dst) -{ - const char *val = getenv(env); - if (val) { - *dst = toui32(val) != 0; - *has_dst = true; - } -} - -static void get_int(const char *env, uint32_t *dst, bool *has_dst) -{ - const char *val = getenv(env); - if (val) { - *dst = toui32(val); - *has_dst = true; - } -} - -static void get_str(const char *env, char **dst, bool *has_dst) -{ - const char *val = getenv(env); - if (val) { - if (*has_dst) { - g_free(*dst); - } - *dst = g_strdup(val); - *has_dst = true; - } -} - -static void get_fmt(const char *env, AudioFormat *dst, bool *has_dst) -{ - const char *val = getenv(env); - if (val) { - size_t i; - for (i = 0; AudioFormat_lookup.size; ++i) { - if (strcasecmp(val, AudioFormat_lookup.array[i]) == 0) { - *dst = i; - *has_dst = true; - return; - } - } - - dolog("Invalid audio format `%s'\n", val); - exit(1); - } -} - - -static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst) -{ - const char *val = getenv(env); - if (val) { - *dst = toui32(val) * 1000; - *has_dst = true; - } -} - -static uint32_t frames_to_usecs(uint32_t frames, - AudiodevPerDirectionOptions *pdo) -{ - uint32_t freq = pdo->has_frequency ? pdo->frequency : 44100; - return (frames * 1000000 + freq / 2) / freq; -} - - -static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst, - AudiodevPerDirectionOptions *pdo) -{ - const char *val = getenv(env); - if (val) { - *dst = frames_to_usecs(toui32(val), pdo); - *has_dst = true; - } -} - -static uint32_t samples_to_usecs(uint32_t samples, - AudiodevPerDirectionOptions *pdo) -{ - uint32_t channels = pdo->has_channels ? pdo->channels : 2; - return frames_to_usecs(samples / channels, pdo); -} - -static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst, - AudiodevPerDirectionOptions *pdo) -{ - const char *val = getenv(env); - if (val) { - *dst = samples_to_usecs(toui32(val), pdo); - *has_dst = true; - } -} - -static uint32_t bytes_to_usecs(uint32_t bytes, AudiodevPerDirectionOptions *pdo) -{ - AudioFormat fmt = pdo->has_format ? pdo->format : AUDIO_FORMAT_S16; - uint32_t bytes_per_sample = audioformat_bytes_per_sample(fmt); - return samples_to_usecs(bytes / bytes_per_sample, pdo); -} - -static void get_bytes_to_usecs(const char *env, uint32_t *dst, bool *has_dst, - AudiodevPerDirectionOptions *pdo) -{ - const char *val = getenv(env); - if (val) { - *dst = bytes_to_usecs(toui32(val), pdo); - *has_dst = true; - } -} - -/* backend specific functions */ -/* ALSA */ -static void handle_alsa_per_direction( - AudiodevAlsaPerDirectionOptions *apdo, const char *prefix) -{ - char buf[64]; - size_t len = strlen(prefix); - bool size_in_usecs = false; - bool dummy; - - memcpy(buf, prefix, len); - strcpy(buf + len, "TRY_POLL"); - get_bool(buf, &apdo->try_poll, &apdo->has_try_poll); - - strcpy(buf + len, "DEV"); - get_str(buf, &apdo->dev, &apdo->has_dev); - - strcpy(buf + len, "SIZE_IN_USEC"); - get_bool(buf, &size_in_usecs, &dummy); - - strcpy(buf + len, "PERIOD_SIZE"); - get_int(buf, &apdo->period_length, &apdo->has_period_length); - if (apdo->has_period_length && !size_in_usecs) { - apdo->period_length = frames_to_usecs( - apdo->period_length, - qapi_AudiodevAlsaPerDirectionOptions_base(apdo)); - } - - strcpy(buf + len, "BUFFER_SIZE"); - get_int(buf, &apdo->buffer_length, &apdo->has_buffer_length); - if (apdo->has_buffer_length && !size_in_usecs) { - apdo->buffer_length = frames_to_usecs( - apdo->buffer_length, - qapi_AudiodevAlsaPerDirectionOptions_base(apdo)); - } -} - -static void handle_alsa(Audiodev *dev) -{ - AudiodevAlsaOptions *aopt = &dev->u.alsa; - handle_alsa_per_direction(aopt->in, "QEMU_ALSA_ADC_"); - handle_alsa_per_direction(aopt->out, "QEMU_ALSA_DAC_"); - - get_millis_to_usecs("QEMU_ALSA_THRESHOLD", - &aopt->threshold, &aopt->has_threshold); -} - -/* coreaudio */ -static void handle_coreaudio(Audiodev *dev) -{ - get_frames_to_usecs( - "QEMU_COREAUDIO_BUFFER_SIZE", - &dev->u.coreaudio.out->buffer_length, - &dev->u.coreaudio.out->has_buffer_length, - qapi_AudiodevCoreaudioPerDirectionOptions_base(dev->u.coreaudio.out)); - get_int("QEMU_COREAUDIO_BUFFER_COUNT", - &dev->u.coreaudio.out->buffer_count, - &dev->u.coreaudio.out->has_buffer_count); -} - -/* dsound */ -static void handle_dsound(Audiodev *dev) -{ - get_millis_to_usecs("QEMU_DSOUND_LATENCY_MILLIS", - &dev->u.dsound.latency, &dev->u.dsound.has_latency); - get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_OUT", - &dev->u.dsound.out->buffer_length, - &dev->u.dsound.out->has_buffer_length, - dev->u.dsound.out); - get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_IN", - &dev->u.dsound.in->buffer_length, - &dev->u.dsound.in->has_buffer_length, - dev->u.dsound.in); -} - -/* OSS */ -static void handle_oss_per_direction( - AudiodevOssPerDirectionOptions *opdo, const char *try_poll_env, - const char *dev_env) -{ - get_bool(try_poll_env, &opdo->try_poll, &opdo->has_try_poll); - get_str(dev_env, &opdo->dev, &opdo->has_dev); - - get_bytes_to_usecs("QEMU_OSS_FRAGSIZE", - &opdo->buffer_length, &opdo->has_buffer_length, - qapi_AudiodevOssPerDirectionOptions_base(opdo)); - get_int("QEMU_OSS_NFRAGS", &opdo->buffer_count, - &opdo->has_buffer_count); -} - -static void handle_oss(Audiodev *dev) -{ - AudiodevOssOptions *oopt = &dev->u.oss; - handle_oss_per_direction(oopt->in, "QEMU_AUDIO_ADC_TRY_POLL", - "QEMU_OSS_ADC_DEV"); - handle_oss_per_direction(oopt->out, "QEMU_AUDIO_DAC_TRY_POLL", - "QEMU_OSS_DAC_DEV"); - - get_bool("QEMU_OSS_MMAP", &oopt->try_mmap, &oopt->has_try_mmap); - get_bool("QEMU_OSS_EXCLUSIVE", &oopt->exclusive, &oopt->has_exclusive); - get_int("QEMU_OSS_POLICY", &oopt->dsp_policy, &oopt->has_dsp_policy); -} - -/* pulseaudio */ -static void handle_pa_per_direction( - AudiodevPaPerDirectionOptions *ppdo, const char *env) -{ - get_str(env, &ppdo->name, &ppdo->has_name); -} - -static void handle_pa(Audiodev *dev) -{ - handle_pa_per_direction(dev->u.pa.in, "QEMU_PA_SOURCE"); - handle_pa_per_direction(dev->u.pa.out, "QEMU_PA_SINK"); - - get_samples_to_usecs( - "QEMU_PA_SAMPLES", &dev->u.pa.in->buffer_length, - &dev->u.pa.in->has_buffer_length, - qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.in)); - get_samples_to_usecs( - "QEMU_PA_SAMPLES", &dev->u.pa.out->buffer_length, - &dev->u.pa.out->has_buffer_length, - qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.out)); - - get_str("QEMU_PA_SERVER", &dev->u.pa.server, &dev->u.pa.has_server); -} - -/* SDL */ -static void handle_sdl(Audiodev *dev) -{ - /* SDL is output only */ - get_samples_to_usecs("QEMU_SDL_SAMPLES", &dev->u.sdl.out->buffer_length, - &dev->u.sdl.out->has_buffer_length, - qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.out)); -} - -/* wav */ -static void handle_wav(Audiodev *dev) -{ - get_int("QEMU_WAV_FREQUENCY", - &dev->u.wav.out->frequency, &dev->u.wav.out->has_frequency); - get_fmt("QEMU_WAV_FORMAT", &dev->u.wav.out->format, - &dev->u.wav.out->has_format); - get_int("QEMU_WAV_DAC_FIXED_CHANNELS", - &dev->u.wav.out->channels, &dev->u.wav.out->has_channels); - get_str("QEMU_WAV_PATH", &dev->u.wav.path, &dev->u.wav.has_path); -} - -/* general */ -static void handle_per_direction( - AudiodevPerDirectionOptions *pdo, const char *prefix) -{ - char buf[64]; - size_t len = strlen(prefix); - - memcpy(buf, prefix, len); - strcpy(buf + len, "FIXED_SETTINGS"); - get_bool(buf, &pdo->fixed_settings, &pdo->has_fixed_settings); - - strcpy(buf + len, "FIXED_FREQ"); - get_int(buf, &pdo->frequency, &pdo->has_frequency); - - strcpy(buf + len, "FIXED_FMT"); - get_fmt(buf, &pdo->format, &pdo->has_format); - - strcpy(buf + len, "FIXED_CHANNELS"); - get_int(buf, &pdo->channels, &pdo->has_channels); - - strcpy(buf + len, "VOICES"); - get_int(buf, &pdo->voices, &pdo->has_voices); -} - -static AudiodevListEntry *legacy_opt(const char *drvname) -{ - AudiodevListEntry *e = g_new0(AudiodevListEntry, 1); - e->dev = g_new0(Audiodev, 1); - e->dev->id = g_strdup(drvname); - e->dev->driver = qapi_enum_parse( - &AudiodevDriver_lookup, drvname, -1, &error_abort); - - audio_create_pdos(e->dev); - - handle_per_direction(audio_get_pdo_in(e->dev), "QEMU_AUDIO_ADC_"); - handle_per_direction(audio_get_pdo_out(e->dev), "QEMU_AUDIO_DAC_"); - - /* Original description: Timer period in HZ (0 - use lowest possible) */ - get_int("QEMU_AUDIO_TIMER_PERIOD", - &e->dev->timer_period, &e->dev->has_timer_period); - if (e->dev->has_timer_period && e->dev->timer_period) { - e->dev->timer_period = NANOSECONDS_PER_SECOND / 1000 / - e->dev->timer_period; - } - - switch (e->dev->driver) { - case AUDIODEV_DRIVER_ALSA: - handle_alsa(e->dev); - break; - - case AUDIODEV_DRIVER_COREAUDIO: - handle_coreaudio(e->dev); - break; - - case AUDIODEV_DRIVER_DSOUND: - handle_dsound(e->dev); - break; - - case AUDIODEV_DRIVER_OSS: - handle_oss(e->dev); - break; - - case AUDIODEV_DRIVER_PA: - handle_pa(e->dev); - break; - - case AUDIODEV_DRIVER_SDL: - handle_sdl(e->dev); - break; - - case AUDIODEV_DRIVER_WAV: - handle_wav(e->dev); - break; - - default: - break; - } - - return e; -} - -AudiodevListHead audio_handle_legacy_opts(void) -{ - const char *drvname = getenv("QEMU_AUDIO_DRV"); - AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head); - - if (drvname) { - AudiodevListEntry *e; - audio_driver *driver = audio_driver_lookup(drvname); - if (!driver) { - dolog("Unknown audio driver `%s'\n", drvname); - exit(1); - } - e = legacy_opt(drvname); - QSIMPLEQ_INSERT_TAIL(&head, e, next); - } else { - for (int i = 0; audio_prio_list[i]; i++) { - audio_driver *driver = audio_driver_lookup(audio_prio_list[i]); - if (driver && driver->can_be_default) { - AudiodevListEntry *e = legacy_opt(driver->name); - QSIMPLEQ_INSERT_TAIL(&head, e, next); - } - } - if (QSIMPLEQ_EMPTY(&head)) { - dolog("Internal error: no default audio driver available\n"); - exit(1); - } - } - - return head; -} - -/* visitor to print -audiodev option */ -typedef struct { - Visitor visitor; - - bool comma; - GList *path; -} LegacyPrintVisitor; - -static bool lv_start_struct(Visitor *v, const char *name, void **obj, - size_t size, Error **errp) -{ - LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; - lv->path = g_list_append(lv->path, g_strdup(name)); - return true; -} - -static void lv_end_struct(Visitor *v, void **obj) -{ - LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; - lv->path = g_list_delete_link(lv->path, g_list_last(lv->path)); -} - -static void lv_print_key(Visitor *v, const char *name) -{ - GList *e; - LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; - if (lv->comma) { - putchar(','); - } else { - lv->comma = true; - } - - for (e = lv->path; e; e = e->next) { - if (e->data) { - printf("%s.", (const char *) e->data); - } - } - - printf("%s=", name); -} - -static bool lv_type_int64(Visitor *v, const char *name, int64_t *obj, - Error **errp) -{ - lv_print_key(v, name); - printf("%" PRIi64, *obj); - return true; -} - -static bool lv_type_uint64(Visitor *v, const char *name, uint64_t *obj, - Error **errp) -{ - lv_print_key(v, name); - printf("%" PRIu64, *obj); - return true; -} - -static bool lv_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) -{ - lv_print_key(v, name); - printf("%s", *obj ? "on" : "off"); - return true; -} - -static bool lv_type_str(Visitor *v, const char *name, char **obj, Error **errp) -{ - const char *str = *obj; - lv_print_key(v, name); - - while (*str) { - if (*str == ',') { - putchar(','); - } - putchar(*str++); - } - return true; -} - -static void lv_complete(Visitor *v, void *opaque) -{ - LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; - assert(lv->path == NULL); -} - -static void lv_free(Visitor *v) -{ - LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; - - g_list_free_full(lv->path, g_free); - g_free(lv); -} - -static Visitor *legacy_visitor_new(void) -{ - LegacyPrintVisitor *lv = g_new0(LegacyPrintVisitor, 1); - - lv->visitor.start_struct = lv_start_struct; - lv->visitor.end_struct = lv_end_struct; - /* lists not supported */ - lv->visitor.type_int64 = lv_type_int64; - lv->visitor.type_uint64 = lv_type_uint64; - lv->visitor.type_bool = lv_type_bool; - lv->visitor.type_str = lv_type_str; - - lv->visitor.type = VISITOR_OUTPUT; - lv->visitor.complete = lv_complete; - lv->visitor.free = lv_free; - - return &lv->visitor; -} - -void audio_legacy_help(void) -{ - AudiodevListHead head; - AudiodevListEntry *e; - - printf("Environment variable based configuration deprecated.\n"); - printf("Please use the new -audiodev option.\n"); - - head = audio_handle_legacy_opts(); - printf("\nEquivalent -audiodev to your current environment variables:\n"); - if (!getenv("QEMU_AUDIO_DRV")) { - printf("(Since you didn't specify QEMU_AUDIO_DRV, I'll list all " - "possibilities)\n"); - } - - QSIMPLEQ_FOREACH(e, &head, next) { - Visitor *v; - Audiodev *dev = e->dev; - printf("-audiodev "); - - v = legacy_visitor_new(); - visit_type_Audiodev(v, NULL, &dev, &error_abort); - visit_free(v); - - printf("\n"); - } - audio_free_audiodev_list(&head); -} diff --git a/audio/audio_template.h b/audio/audio_template.h index 7192b19e73..7ccfec0116 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -37,11 +37,12 @@ #endif static void glue(audio_init_nb_voices_, TYPE)(AudioState *s, - struct audio_driver *drv) + struct audio_driver *drv, int min_voices) { int max_voices = glue (drv->max_voices_, TYPE); - int voice_size = glue (drv->voice_size_, TYPE); + size_t voice_size = glue(drv->voice_size_, TYPE); + glue (s->nb_hw_voices_, TYPE) = glue(audio_get_pdo_, TYPE)(s->dev)->voices; if (glue (s->nb_hw_voices_, TYPE) > max_voices) { if (!max_voices) { #ifdef DAC @@ -56,24 +57,30 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s, glue (s->nb_hw_voices_, TYPE) = max_voices; } + if (glue (s->nb_hw_voices_, TYPE) < min_voices) { + dolog ("Bogus number of " NAME " voices %d, setting to %d\n", + glue (s->nb_hw_voices_, TYPE), + min_voices); + } + if (audio_bug(__func__, !voice_size && max_voices)) { dolog ("drv=`%s' voice_size=0 max_voices=%d\n", drv->name, max_voices); - abort(); + glue (s->nb_hw_voices_, TYPE) = 0; } if (audio_bug(__func__, voice_size && !max_voices)) { - dolog ("drv=`%s' voice_size=%d max_voices=0\n", - drv->name, voice_size); - abort(); + dolog("drv=`%s' voice_size=%zu max_voices=0\n", + drv->name, voice_size); } } static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw) { g_free(hw->buf_emul); - g_free (HWBUF); - HWBUF = NULL; + g_free(HWBUF.buffer); + HWBUF.buffer = NULL; + HWBUF.size = 0; } static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw) @@ -82,55 +89,69 @@ static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw) size_t samples = hw->samples; if (audio_bug(__func__, samples == 0)) { dolog("Attempted to allocate empty buffer\n"); - abort(); } - HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample) * samples); - HWBUF->size = samples; + HWBUF.buffer = g_new0(st_sample, samples); + HWBUF.size = samples; + HWBUF.pos = 0; } else { - HWBUF = NULL; + HWBUF.buffer = NULL; + HWBUF.size = 0; } } static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw) { - g_free (sw->buf); + g_free(sw->resample_buf.buffer); + sw->resample_buf.buffer = NULL; + sw->resample_buf.size = 0; if (sw->rate) { st_rate_stop (sw->rate); } - - sw->buf = NULL; sw->rate = NULL; } static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw) { - int samples; + HW *hw = sw->hw; + uint64_t samples; if (!glue(audio_get_pdo_, TYPE)(sw->s->dev)->mixing_engine) { return 0; } - samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio; + samples = muldiv64(HWBUF.size, sw->info.freq, hw->info.freq); + if (samples == 0) { + uint64_t f_fe_min; + uint64_t f_be = (uint32_t)hw->info.freq; - sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample)); - if (!sw->buf) { - dolog ("Could not allocate buffer for `%s' (%d samples)\n", - SW_NAME (sw), samples); + /* f_fe_min = ceil(1 [frames] * f_be [Hz] / size_be [frames]) */ + f_fe_min = (f_be + HWBUF.size - 1) / HWBUF.size; + qemu_log_mask(LOG_UNIMP, + AUDIO_CAP ": The guest selected a " NAME " sample rate" + " of %d Hz for %s. Only sample rates >= %" PRIu64 " Hz" + " are supported.\n", + sw->info.freq, sw->name, f_fe_min); return -1; } + /* + * Allocate one additional audio frame that is needed for upsampling + * if the resample buffer size is small. For large buffer sizes take + * care of overflows and truncation. + */ + samples = samples < SIZE_MAX ? samples + 1 : SIZE_MAX; + sw->resample_buf.buffer = g_new0(st_sample, samples); + sw->resample_buf.size = samples; + sw->resample_buf.pos = 0; + #ifdef DAC - sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq); + sw->rate = st_rate_start(sw->info.freq, hw->info.freq); #else - sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq); + sw->rate = st_rate_start(hw->info.freq, sw->info.freq); #endif - if (!sw->rate) { - g_free (sw->buf); - sw->buf = NULL; - return -1; - } + return 0; } @@ -147,11 +168,8 @@ static int glue (audio_pcm_sw_init_, TYPE) ( sw->hw = hw; sw->active = 0; #ifdef DAC - sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq; sw->total_hw_samples_mixed = 0; sw->empty = 1; -#else - sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq; #endif if (sw->info.is_float) { @@ -254,21 +272,19 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, if (audio_bug(__func__, !drv)) { dolog ("No host audio driver\n"); - abort(); + return NULL; } if (audio_bug(__func__, !drv->pcm_ops)) { dolog ("Host audio driver without pcm_ops\n"); - abort(); - } - - hw = audio_calloc(__func__, 1, glue(drv->voice_size_, TYPE)); - if (!hw) { - dolog ("Can not allocate voice `%s' size %d\n", - drv->name, glue (drv->voice_size_, TYPE)); return NULL; } + /* + * Since glue(s->nb_hw_voices_, TYPE) is != 0, glue(drv->voice_size_, TYPE) + * is guaranteed to be != 0. See the audio_init_nb_voices_* functions. + */ + hw = g_malloc0(glue(drv->voice_size_, TYPE)); hw->s = s; hw->pcm_ops = drv->pcm_ops; @@ -277,13 +293,12 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, QLIST_INIT (&hw->cap_head); #endif if (glue (hw->pcm_ops->init_, TYPE) (hw, as, s->drv_opaque)) { - g_free(hw); - return NULL; + goto err0; } if (audio_bug(__func__, hw->samples <= 0)) { dolog("hw->samples=%zd\n", hw->samples); - abort(); + goto err1; } if (hw->info.is_float) { @@ -312,6 +327,12 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, audio_attach_capture (hw); #endif return hw; + + err1: + glue (hw->pcm_ops->fini_, TYPE) (hw); + err0: + g_free (hw); + return NULL; } AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) @@ -319,25 +340,51 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) switch (dev->driver) { case AUDIODEV_DRIVER_NONE: return dev->u.none.TYPE; +#ifdef CONFIG_AUDIO_ALSA case AUDIODEV_DRIVER_ALSA: return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE); +#endif +#ifdef CONFIG_AUDIO_COREAUDIO case AUDIODEV_DRIVER_COREAUDIO: return qapi_AudiodevCoreaudioPerDirectionOptions_base( dev->u.coreaudio.TYPE); +#endif +#ifdef CONFIG_DBUS_DISPLAY case AUDIODEV_DRIVER_DBUS: return dev->u.dbus.TYPE; +#endif +#ifdef CONFIG_AUDIO_DSOUND case AUDIODEV_DRIVER_DSOUND: return dev->u.dsound.TYPE; +#endif +#ifdef CONFIG_AUDIO_JACK case AUDIODEV_DRIVER_JACK: return qapi_AudiodevJackPerDirectionOptions_base(dev->u.jack.TYPE); +#endif +#ifdef CONFIG_AUDIO_OSS case AUDIODEV_DRIVER_OSS: return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE); +#endif +#ifdef CONFIG_AUDIO_PA case AUDIODEV_DRIVER_PA: return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE); +#endif +#ifdef CONFIG_AUDIO_PIPEWIRE + case AUDIODEV_DRIVER_PIPEWIRE: + return qapi_AudiodevPipewirePerDirectionOptions_base(dev->u.pipewire.TYPE); +#endif +#ifdef CONFIG_AUDIO_SDL case AUDIODEV_DRIVER_SDL: return qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.TYPE); +#endif +#ifdef CONFIG_AUDIO_SNDIO + case AUDIODEV_DRIVER_SNDIO: + return dev->u.sndio.TYPE; +#endif +#ifdef CONFIG_SPICE case AUDIODEV_DRIVER_SPICE: return dev->u.spice.TYPE; +#endif case AUDIODEV_DRIVER_WAV: return dev->u.wav.TYPE; @@ -389,33 +436,28 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)( hw_as = *as; } - sw = audio_calloc(__func__, 1, sizeof(*sw)); - if (!sw) { - dolog ("Could not allocate soft voice `%s' (%zu bytes)\n", - sw_name ? sw_name : "unknown", sizeof (*sw)); - goto err1; - } + sw = g_new0(SW, 1); sw->s = s; hw = glue(audio_pcm_hw_add_, TYPE)(s, &hw_as); if (!hw) { - goto err2; + dolog("Could not create a backend for voice `%s'\n", sw_name); + goto err1; } glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw); if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) { - goto err3; + goto err2; } return sw; -err3: +err2: glue (audio_pcm_hw_del_sw_, TYPE) (sw); glue (audio_pcm_hw_gc_, TYPE) (&hw); -err2: - g_free (sw); err1: + g_free(sw); return NULL; } @@ -432,7 +474,7 @@ void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw) if (sw) { if (audio_bug(__func__, !card)) { dolog ("card=%p\n", card); - abort(); + return; } glue (audio_close_, TYPE) (sw); @@ -454,7 +496,7 @@ SW *glue (AUD_open_, TYPE) ( if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { dolog ("card=%p name=%p callback_fn=%p as=%p\n", card, name, callback_fn, as); - abort(); + goto fail; } s = card->state; @@ -465,12 +507,12 @@ SW *glue (AUD_open_, TYPE) ( if (audio_bug(__func__, audio_validate_settings(as))) { audio_print_settings (as); - abort(); + goto fail; } if (audio_bug(__func__, !s->drv)) { dolog ("Can not open `%s' (no host audio driver)\n", name); - abort(); + goto fail; } if (sw && audio_pcm_info_eq (&sw->info, as)) { @@ -486,8 +528,8 @@ SW *glue (AUD_open_, TYPE) ( HW *hw = sw->hw; if (!hw) { - dolog ("Internal logic error voice `%s' has no hardware store\n", - SW_NAME (sw)); + dolog("Internal logic error: voice `%s' has no backend\n", + SW_NAME(sw)); goto fail; } @@ -498,7 +540,6 @@ SW *glue (AUD_open_, TYPE) ( } else { sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as); if (!sw) { - dolog ("Failed to create voice `%s'\n", name); return NULL; } } diff --git a/audio/coreaudio.m b/audio/coreaudio.m index 4695291621..ab632b9bbb 100644 --- a/audio/coreaudio.m +++ b/audio/coreaudio.m @@ -299,7 +299,7 @@ COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size), #undef COREAUDIO_WRAPPER_FUNC /* - * callback to feed audiooutput buffer. called without iothread lock. + * callback to feed audiooutput buffer. called without BQL. * allowed to lock "buf_mutex", but disallowed to have any other locks. */ static OSStatus audioDeviceIOProc( @@ -538,7 +538,7 @@ static void update_device_playback_state(coreaudioVoiceOut *core) } } -/* called without iothread lock. */ +/* called without BQL. */ static OSStatus handle_voice_change( AudioObjectID in_object_id, UInt32 in_number_addresses, @@ -547,7 +547,7 @@ static OSStatus handle_voice_change( { coreaudioVoiceOut *core = in_client_data; - qemu_mutex_lock_iothread(); + bql_lock(); if (core->outputDeviceID) { fini_out_device(core); @@ -557,7 +557,7 @@ static OSStatus handle_voice_change( update_device_playback_state(core); } - qemu_mutex_unlock_iothread(); + bql_unlock(); return 0; } @@ -644,7 +644,7 @@ static void coreaudio_enable_out(HWVoiceOut *hw, bool enable) update_device_playback_state(core); } -static void *coreaudio_audio_init(Audiodev *dev) +static void *coreaudio_audio_init(Audiodev *dev, Error **errp) { return dev; } @@ -673,7 +673,6 @@ static struct audio_driver coreaudio_audio_driver = { .init = coreaudio_audio_init, .fini = coreaudio_audio_fini, .pcm_ops = &coreaudio_pcm_ops, - .can_be_default = 1, .max_voices_out = 1, .max_voices_in = 0, .voice_size_out = sizeof (coreaudioVoiceOut), diff --git a/audio/dbusaudio.c b/audio/dbusaudio.c index a3d656d3b0..60fcf643ec 100644 --- a/audio/dbusaudio.c +++ b/audio/dbusaudio.c @@ -29,7 +29,11 @@ #include "qemu/timer.h" #include "qemu/dbus.h" +#ifdef G_OS_UNIX #include +#endif + +#include "ui/dbus.h" #include "ui/dbus-display1.h" #define AUDIO_CAP "dbus" @@ -43,6 +47,7 @@ typedef struct DBusAudio { GDBusObjectManagerServer *server; + bool p2p; GDBusObjectSkeleton *audio; QemuDBusDisplay1Audio *iface; GHashTable *out_listeners; @@ -82,7 +87,7 @@ static void *dbus_get_buffer_out(HWVoiceOut *hw, size_t *size) } *size = MIN(vo->buf_size - vo->buf_pos, *size); - *size = audio_rate_get_bytes(&hw->info, &vo->rate, *size); + *size = audio_rate_get_bytes(&vo->rate, &hw->info, *size); return vo->buf + vo->buf_pos; @@ -343,7 +348,7 @@ dbus_read(HWVoiceIn *hw, void *buf, size_t size) trace_dbus_audio_read(size); - /* size = audio_rate_get_bytes(&hw->info, &vo->rate, size); */ + /* size = audio_rate_get_bytes(&vo->rate, &hw->info, size); */ g_hash_table_iter_init(&iter, da->in_listeners); while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) { @@ -390,7 +395,7 @@ dbus_enable_in(HWVoiceIn *hw, bool enable) } static void * -dbus_audio_init(Audiodev *dev) +dbus_audio_init(Audiodev *dev, Error **errp) { DBusAudio *da = g_new0(DBusAudio, 1); @@ -443,12 +448,15 @@ listener_in_vanished_cb(GDBusConnection *connection, static gboolean dbus_audio_register_listener(AudioState *s, GDBusMethodInvocation *invocation, +#ifdef G_OS_UNIX GUnixFDList *fd_list, +#endif GVariant *arg_listener, bool out) { DBusAudio *da = s->drv_opaque; - const char *sender = g_dbus_method_invocation_get_sender(invocation); + const char *sender = + da->p2p ? "p2p" : g_dbus_method_invocation_get_sender(invocation); g_autoptr(GDBusConnection) listener_conn = NULL; g_autoptr(GError) err = NULL; g_autoptr(GSocket) socket = NULL; @@ -469,6 +477,11 @@ dbus_audio_register_listener(AudioState *s, return DBUS_METHOD_INVOCATION_HANDLED; } +#ifdef G_OS_WIN32 + if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) { + return DBUS_METHOD_INVOCATION_HANDLED; + } +#else fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err); if (err) { g_dbus_method_invocation_return_error(invocation, @@ -478,6 +491,7 @@ dbus_audio_register_listener(AudioState *s, err->message); return DBUS_METHOD_INVOCATION_HANDLED; } +#endif socket = g_socket_new_from_fd(fd, &err); if (err) { @@ -486,15 +500,28 @@ dbus_audio_register_listener(AudioState *s, DBUS_DISPLAY_ERROR_FAILED, "Couldn't make a socket: %s", err->message); +#ifdef G_OS_WIN32 + closesocket(fd); +#else + close(fd); +#endif return DBUS_METHOD_INVOCATION_HANDLED; } socket_conn = g_socket_connection_factory_create_connection(socket); if (out) { qemu_dbus_display1_audio_complete_register_out_listener( - da->iface, invocation, NULL); + da->iface, invocation +#ifdef G_OS_UNIX + , NULL +#endif + ); } else { qemu_dbus_display1_audio_complete_register_in_listener( - da->iface, invocation, NULL); + da->iface, invocation +#ifdef G_OS_UNIX + , NULL +#endif + ); } listener_conn = @@ -572,26 +599,36 @@ dbus_audio_register_listener(AudioState *s, static gboolean dbus_audio_register_out_listener(AudioState *s, GDBusMethodInvocation *invocation, +#ifdef G_OS_UNIX GUnixFDList *fd_list, +#endif GVariant *arg_listener) { return dbus_audio_register_listener(s, invocation, - fd_list, arg_listener, true); +#ifdef G_OS_UNIX + fd_list, +#endif + arg_listener, true); } static gboolean dbus_audio_register_in_listener(AudioState *s, GDBusMethodInvocation *invocation, +#ifdef G_OS_UNIX GUnixFDList *fd_list, +#endif GVariant *arg_listener) { return dbus_audio_register_listener(s, invocation, - fd_list, arg_listener, false); +#ifdef G_OS_UNIX + fd_list, +#endif + arg_listener, false); } static void -dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server) +dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p) { DBusAudio *da = s->drv_opaque; @@ -599,6 +636,7 @@ dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server) g_assert(!da->server); da->server = g_object_ref(server); + da->p2p = p2p; da->audio = g_dbus_object_skeleton_new(DBUS_DISPLAY1_AUDIO_PATH); da->iface = qemu_dbus_display1_audio_skeleton_new(); @@ -638,7 +676,6 @@ static struct audio_driver dbus_audio_driver = { .fini = dbus_audio_fini, .set_dbus_server = dbus_audio_set_server, .pcm_ops = &dbus_pcm_ops, - .can_be_default = 1, .max_voices_out = INT_MAX, .max_voices_in = INT_MAX, .voice_size_out = sizeof(DBusVoiceOut), diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c index 3fb67ec3ee..f3bb48d007 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -619,7 +619,7 @@ static void dsound_audio_fini (void *opaque) g_free(s); } -static void *dsound_audio_init(Audiodev *dev) +static void *dsound_audio_init(Audiodev *dev, Error **errp) { int err; HRESULT hr; @@ -721,7 +721,6 @@ static struct audio_driver dsound_audio_driver = { .init = dsound_audio_init, .fini = dsound_audio_fini, .pcm_ops = &dsound_pcm_ops, - .can_be_default = 1, .max_voices_out = INT_MAX, .max_voices_in = 1, .voice_size_out = sizeof (DSoundVoiceOut), diff --git a/audio/jackaudio.c b/audio/jackaudio.c index 5bdf3d7a78..974a3caad3 100644 --- a/audio/jackaudio.c +++ b/audio/jackaudio.c @@ -70,6 +70,9 @@ typedef struct QJackClient { int buffersize; jack_port_t **port; QJackBuffer fifo; + + /* Used as workspace by qjack_process() */ + float **process_buffers; } QJackClient; @@ -267,22 +270,21 @@ static int qjack_process(jack_nframes_t nframes, void *arg) } /* get the buffers for the ports */ - float *buffers[c->nchannels]; for (int i = 0; i < c->nchannels; ++i) { - buffers[i] = jack_port_get_buffer(c->port[i], nframes); + c->process_buffers[i] = jack_port_get_buffer(c->port[i], nframes); } if (c->out) { if (likely(c->enabled)) { - qjack_buffer_read_l(&c->fifo, buffers, nframes); + qjack_buffer_read_l(&c->fifo, c->process_buffers, nframes); } else { for (int i = 0; i < c->nchannels; ++i) { - memset(buffers[i], 0, nframes * sizeof(float)); + memset(c->process_buffers[i], 0, nframes * sizeof(float)); } } } else { if (likely(c->enabled)) { - qjack_buffer_write_l(&c->fifo, buffers, nframes); + qjack_buffer_write_l(&c->fifo, c->process_buffers, nframes); } } @@ -400,7 +402,8 @@ static void qjack_client_connect_ports(QJackClient *c) static int qjack_client_init(QJackClient *c) { jack_status_t status; - char client_name[jack_client_name_size()]; + int client_name_len = jack_client_name_size(); /* includes NUL */ + g_autofree char *client_name = g_new(char, client_name_len); jack_options_t options = JackNullOption; if (c->state == QJACK_STATE_RUNNING) { @@ -409,7 +412,7 @@ static int qjack_client_init(QJackClient *c) c->connect_ports = true; - snprintf(client_name, sizeof(client_name), "%s-%s", + snprintf(client_name, client_name_len, "%s-%s", c->out ? "out" : "in", c->opt->client_name ? c->opt->client_name : audio_application_name()); @@ -447,6 +450,9 @@ static int qjack_client_init(QJackClient *c) jack_get_client_name(c->client)); } + /* Allocate working buffer for process callback */ + c->process_buffers = g_new(float *, c->nchannels); + jack_set_process_callback(c->client, qjack_process , c); jack_set_port_registration_callback(c->client, qjack_port_registration, c); jack_set_xrun_callback(c->client, qjack_xrun, c); @@ -578,6 +584,7 @@ static void qjack_client_fini_locked(QJackClient *c) qjack_buffer_free(&c->fifo); g_free(c->port); + g_free(c->process_buffers); c->state = QJACK_STATE_DISCONNECTED; /* fallthrough */ @@ -638,7 +645,7 @@ static int qjack_thread_creator(jack_native_thread_t *thread, } #endif -static void *qjack_init(Audiodev *dev) +static void *qjack_init(Audiodev *dev, Error **errp) { assert(dev->driver == AUDIODEV_DRIVER_JACK); return dev; @@ -669,7 +676,6 @@ static struct audio_driver jack_driver = { .init = qjack_init, .fini = qjack_fini, .pcm_ops = &jack_pcm_ops, - .can_be_default = 1, .max_voices_out = INT_MAX, .max_voices_in = INT_MAX, .voice_size_out = sizeof(QJackOut), diff --git a/audio/meson.build b/audio/meson.build index 94dab16891..608f35e6af 100644 --- a/audio/meson.build +++ b/audio/meson.build @@ -1,14 +1,14 @@ -softmmu_ss.add([spice_headers, files('audio.c')]) -softmmu_ss.add(files( - 'audio_legacy.c', +system_ss.add([spice_headers, files('audio.c')]) +system_ss.add(files( + 'audio-hmp-cmds.c', 'mixeng.c', 'noaudio.c', 'wavaudio.c', 'wavcapture.c', )) -softmmu_ss.add(when: coreaudio, if_true: files('coreaudio.m')) -softmmu_ss.add(when: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c')) +system_ss.add(when: coreaudio, if_true: files('coreaudio.m')) +system_ss.add(when: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c')) audio_modules = {} foreach m : [ @@ -17,6 +17,8 @@ foreach m : [ ['pa', pulse, files('paaudio.c')], ['sdl', sdl, files('sdlaudio.c')], ['jack', jack, files('jackaudio.c')], + ['sndio', sndio, files('sndioaudio.c')], + ['pipewire', pipewire, files('pwaudio.c')], ['spice', spice, files('spiceaudio.c')] ] if m[1].found() @@ -28,7 +30,8 @@ endforeach if dbus_display module_ss = ss.source_set() - module_ss.add(when: [gio, pixman, opengl, 'CONFIG_GIO'], if_true: files('dbusaudio.c')) + module_ss.add(when: [gio, dbus_display1_dep, pixman], + if_true: files('dbusaudio.c')) audio_modules += {'dbus': module_ss} endif diff --git a/audio/mixeng.c b/audio/mixeng.c index 100a306d6f..69f6549224 100644 --- a/audio/mixeng.c +++ b/audio/mixeng.c @@ -414,12 +414,7 @@ struct rate { */ void *st_rate_start (int inrate, int outrate) { - struct rate *rate = audio_calloc(__func__, 1, sizeof(*rate)); - - if (!rate) { - dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate)); - return NULL; - } + struct rate *rate = g_new0(struct rate, 1); rate->opos = 0; @@ -445,6 +440,86 @@ void st_rate_stop (void *opaque) g_free (opaque); } +/** + * st_rate_frames_out() - returns the number of frames the resampling code + * generates from frames_in frames + * + * @opaque: pointer to struct rate + * @frames_in: number of frames + * + * When upsampling, there may be more than one correct result. In this case, + * the function returns the maximum number of output frames the resampling + * code can generate. + */ +uint32_t st_rate_frames_out(void *opaque, uint32_t frames_in) +{ + struct rate *rate = opaque; + uint64_t opos_end, opos_delta; + uint32_t ipos_end; + uint32_t frames_out; + + if (rate->opos_inc == 1ULL << 32) { + return frames_in; + } + + /* no output frame without at least one input frame */ + if (!frames_in) { + return 0; + } + + /* last frame read was at rate->ipos - 1 */ + ipos_end = rate->ipos - 1 + frames_in; + opos_end = (uint64_t)ipos_end << 32; + + /* last frame written was at rate->opos - rate->opos_inc */ + if (opos_end + rate->opos_inc <= rate->opos) { + return 0; + } + opos_delta = opos_end - rate->opos + rate->opos_inc; + frames_out = opos_delta / rate->opos_inc; + + return opos_delta % rate->opos_inc ? frames_out : frames_out - 1; +} + +/** + * st_rate_frames_in() - returns the number of frames needed to + * get frames_out frames after resampling + * + * @opaque: pointer to struct rate + * @frames_out: number of frames + * + * When downsampling, there may be more than one correct result. In this + * case, the function returns the maximum number of input frames needed. + */ +uint32_t st_rate_frames_in(void *opaque, uint32_t frames_out) +{ + struct rate *rate = opaque; + uint64_t opos_start, opos_end; + uint32_t ipos_start, ipos_end; + + if (rate->opos_inc == 1ULL << 32) { + return frames_out; + } + + if (frames_out) { + opos_start = rate->opos; + ipos_start = rate->ipos; + } else { + uint64_t offset; + + /* add offset = ceil(opos_inc) to opos and ipos to avoid an underflow */ + offset = (rate->opos_inc + (1ULL << 32) - 1) & ~((1ULL << 32) - 1); + opos_start = rate->opos + offset; + ipos_start = rate->ipos + (offset >> 32); + } + /* last frame written was at opos_start - rate->opos_inc */ + opos_end = opos_start - rate->opos_inc + rate->opos_inc * frames_out; + ipos_end = (opos_end >> 32) + 1; + + /* last frame read was at ipos_start - 1 */ + return ipos_end + 1 > ipos_start ? ipos_end + 1 - ipos_start : 0; +} + void mixeng_clear (struct st_sample *buf, int len) { memset (buf, 0, len * sizeof (struct st_sample)); diff --git a/audio/mixeng.h b/audio/mixeng.h index 2dcd6df245..a5f56d2c26 100644 --- a/audio/mixeng.h +++ b/audio/mixeng.h @@ -38,7 +38,7 @@ 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] */ +/* indices: [stereo][signed][swap endianness][8, 16 or 32-bits] */ extern t_sample *mixeng_conv[2][2][2][3]; extern f_sample *mixeng_clip[2][2][2][3]; @@ -52,6 +52,8 @@ void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf, void st_rate_flow_mix(void *opaque, st_sample *ibuf, st_sample *obuf, size_t *isamp, size_t *osamp); void st_rate_stop (void *opaque); +uint32_t st_rate_frames_out(void *opaque, uint32_t frames_in); +uint32_t st_rate_frames_in(void *opaque, uint32_t frames_out); void mixeng_clear (struct st_sample *buf, int len); void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol); diff --git a/audio/noaudio.c b/audio/noaudio.c index 84a6bfbb1c..1b60d8518a 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -44,7 +44,7 @@ typedef struct NoVoiceIn { static size_t no_write(HWVoiceOut *hw, void *buf, size_t len) { NoVoiceOut *no = (NoVoiceOut *) hw; - return audio_rate_get_bytes(&hw->info, &no->rate, len); + return audio_rate_get_bytes(&no->rate, &hw->info, len); } static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) @@ -89,7 +89,7 @@ static void no_fini_in (HWVoiceIn *hw) static size_t no_read(HWVoiceIn *hw, void *buf, size_t size) { NoVoiceIn *no = (NoVoiceIn *) hw; - int64_t bytes = audio_rate_get_bytes(&hw->info, &no->rate, size); + int64_t bytes = audio_rate_get_bytes(&no->rate, &hw->info, size); audio_pcm_info_clear_buf(&hw->info, buf, bytes / hw->info.bytes_per_frame); return bytes; @@ -104,7 +104,7 @@ static void no_enable_in(HWVoiceIn *hw, bool enable) } } -static void *no_audio_init(Audiodev *dev) +static void *no_audio_init(Audiodev *dev, Error **errp) { return &no_audio_init; } @@ -135,7 +135,6 @@ static struct audio_driver no_audio_driver = { .init = no_audio_init, .fini = no_audio_fini, .pcm_ops = &no_pcm_ops, - .can_be_default = 1, .max_voices_out = INT_MAX, .max_voices_in = INT_MAX, .voice_size_out = sizeof (NoVoiceOut), diff --git a/audio/ossaudio.c b/audio/ossaudio.c index 8e075edb70..c5858284a1 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -28,6 +28,7 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/host-utils.h" +#include "qapi/error.h" #include "audio.h" #include "trace.h" @@ -252,7 +253,7 @@ static int oss_open(int in, struct oss_params *req, audsettings *as, audio_buf_info abinfo; int fmt, freq, nchannels; int setfragment = 1; - const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp"; + const char *dspname = opdo->dev ?: "/dev/dsp"; const char *typ = in ? "ADC" : "DAC"; #ifdef USE_DSP_POLICY int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5; @@ -548,7 +549,6 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, hw->size_emul); hw->buf_emul = NULL; } else { - int err; int trig = 0; if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n"); @@ -736,7 +736,7 @@ static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo) } } -static void *oss_audio_init(Audiodev *dev) +static void *oss_audio_init(Audiodev *dev, Error **errp) { AudiodevOssOptions *oopts; assert(dev->driver == AUDIODEV_DRIVER_OSS); @@ -745,10 +745,12 @@ static void *oss_audio_init(Audiodev *dev) oss_init_per_direction(oopts->in); oss_init_per_direction(oopts->out); - if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp", - R_OK | W_OK) < 0 || - access(oopts->out->has_dev ? oopts->out->dev : "/dev/dsp", - R_OK | W_OK) < 0) { + if (access(oopts->in->dev ?: "/dev/dsp", R_OK | W_OK) < 0) { + error_setg_errno(errp, errno, "%s not accessible", oopts->in->dev ?: "/dev/dsp"); + return NULL; + } + if (access(oopts->out->dev ?: "/dev/dsp", R_OK | W_OK) < 0) { + error_setg_errno(errp, errno, "%s not accessible", oopts->out->dev ?: "/dev/dsp"); return NULL; } return dev; @@ -781,7 +783,6 @@ static struct audio_driver oss_audio_driver = { .init = oss_audio_init, .fini = oss_audio_fini, .pcm_ops = &oss_pcm_ops, - .can_be_default = 1, .max_voices_out = INT_MAX, .max_voices_in = INT_MAX, .voice_size_out = sizeof (OSSVoiceOut), diff --git a/audio/paaudio.c b/audio/paaudio.c index e91116f239..f3193b08c3 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -3,7 +3,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "audio.h" -#include "qapi/opts-visitor.h" +#include "qapi/error.h" #include @@ -536,9 +536,9 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, pa->stream = qpa_simple_new ( c, - ppdo->has_stream_name ? ppdo->stream_name : g->dev->id, + ppdo->stream_name ?: g->dev->id, PA_STREAM_PLAYBACK, - ppdo->has_name ? ppdo->name : NULL, + ppdo->name, &ss, &ba, /* buffering attributes */ &error @@ -585,9 +585,9 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) pa->stream = qpa_simple_new ( c, - ppdo->has_stream_name ? ppdo->stream_name : g->dev->id, + ppdo->stream_name ?: g->dev->id, PA_STREAM_RECORD, - ppdo->has_name ? ppdo->name : NULL, + ppdo->name, &ss, &ba, /* buffering attributes */ &error @@ -818,7 +818,7 @@ fail: return NULL; } -static void *qpa_audio_init(Audiodev *dev) +static void *qpa_audio_init(Audiodev *dev, Error **errp) { paaudio *g; AudiodevPaOptions *popts = &dev->u.pa; @@ -827,17 +827,19 @@ static void *qpa_audio_init(Audiodev *dev) assert(dev->driver == AUDIODEV_DRIVER_PA); - if (!popts->has_server) { + if (!popts->server) { char pidfile[64]; char *runtime; struct stat st; runtime = getenv("XDG_RUNTIME_DIR"); if (!runtime) { + error_setg(errp, "XDG_RUNTIME_DIR not set"); return NULL; } snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime); if (stat(pidfile, &st) != 0) { + error_setg_errno(errp, errno, "could not stat pidfile %s", pidfile); return NULL; } } @@ -850,7 +852,7 @@ static void *qpa_audio_init(Audiodev *dev) } g = g_new0(paaudio, 1); - server = popts->has_server ? popts->server : NULL; + server = popts->server; g->dev = dev; @@ -867,6 +869,7 @@ static void *qpa_audio_init(Audiodev *dev) } if (!g->conn) { g_free(g); + error_setg(errp, "could not connect to PulseAudio server"); return NULL; } @@ -928,7 +931,6 @@ static struct audio_driver pa_audio_driver = { .init = qpa_audio_init, .fini = qpa_audio_fini, .pcm_ops = &qpa_pcm_ops, - .can_be_default = 1, .max_voices_out = INT_MAX, .max_voices_in = INT_MAX, .voice_size_out = sizeof (PAVoiceOut), diff --git a/audio/pwaudio.c b/audio/pwaudio.c new file mode 100644 index 0000000000..3b14e04fbb --- /dev/null +++ b/audio/pwaudio.c @@ -0,0 +1,857 @@ +/* + * QEMU PipeWire audio driver + * + * Copyright (c) 2023 Red Hat Inc. + * + * Author: Dorinda Bassey + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "audio.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include +#include +#include +#include + +#include +#include "trace.h" + +#define AUDIO_CAP "pipewire" +#define RINGBUFFER_SIZE (1u << 22) +#define RINGBUFFER_MASK (RINGBUFFER_SIZE - 1) + +#include "audio_int.h" + +typedef struct pwvolume { + uint32_t channels; + float values[SPA_AUDIO_MAX_CHANNELS]; +} pwvolume; + +typedef struct pwaudio { + Audiodev *dev; + struct pw_thread_loop *thread_loop; + struct pw_context *context; + + struct pw_core *core; + struct spa_hook core_listener; + int last_seq, pending_seq, error; +} pwaudio; + +typedef struct PWVoice { + pwaudio *g; + struct pw_stream *stream; + struct spa_hook stream_listener; + struct spa_audio_info_raw info; + uint32_t highwater_mark; + uint32_t frame_size, req; + struct spa_ringbuffer ring; + uint8_t buffer[RINGBUFFER_SIZE]; + + pwvolume volume; + bool muted; +} PWVoice; + +typedef struct PWVoiceOut { + HWVoiceOut hw; + PWVoice v; +} PWVoiceOut; + +typedef struct PWVoiceIn { + HWVoiceIn hw; + PWVoice v; +} PWVoiceIn; + +#define PW_VOICE_IN(v) ((PWVoiceIn *)v) +#define PW_VOICE_OUT(v) ((PWVoiceOut *)v) + +static void +stream_destroy(void *data) +{ + PWVoice *v = (PWVoice *) data; + spa_hook_remove(&v->stream_listener); + v->stream = NULL; +} + +/* output data processing function to read stuffs from the buffer */ +static void +playback_on_process(void *data) +{ + PWVoice *v = data; + void *p; + struct pw_buffer *b; + struct spa_buffer *buf; + uint32_t req, index, n_bytes; + int32_t avail; + + assert(v->stream); + + /* obtain a buffer to read from */ + b = pw_stream_dequeue_buffer(v->stream); + if (b == NULL) { + error_report("out of buffers: %s", strerror(errno)); + return; + } + + buf = b->buffer; + p = buf->datas[0].data; + if (p == NULL) { + return; + } + /* calculate the total no of bytes to read data from buffer */ + req = b->requested * v->frame_size; + if (req == 0) { + req = v->req; + } + n_bytes = SPA_MIN(req, buf->datas[0].maxsize); + + /* get no of available bytes to read data from buffer */ + avail = spa_ringbuffer_get_read_index(&v->ring, &index); + + if (avail <= 0) { + PWVoiceOut *vo = container_of(data, PWVoiceOut, v); + audio_pcm_info_clear_buf(&vo->hw.info, p, n_bytes / v->frame_size); + } else { + if ((uint32_t) avail < n_bytes) { + /* + * PipeWire immediately calls this callback again if we provide + * less than n_bytes. Then audio_pcm_info_clear_buf() fills the + * rest of the buffer with silence. + */ + n_bytes = avail; + } + + spa_ringbuffer_read_data(&v->ring, + v->buffer, RINGBUFFER_SIZE, + index & RINGBUFFER_MASK, p, n_bytes); + + index += n_bytes; + spa_ringbuffer_read_update(&v->ring, index); + + } + buf->datas[0].chunk->offset = 0; + buf->datas[0].chunk->stride = v->frame_size; + buf->datas[0].chunk->size = n_bytes; + + /* queue the buffer for playback */ + pw_stream_queue_buffer(v->stream, b); +} + +/* output data processing function to generate stuffs in the buffer */ +static void +capture_on_process(void *data) +{ + PWVoice *v = (PWVoice *) data; + void *p; + struct pw_buffer *b; + struct spa_buffer *buf; + int32_t filled; + uint32_t index, offs, n_bytes; + + assert(v->stream); + + /* obtain a buffer */ + b = pw_stream_dequeue_buffer(v->stream); + if (b == NULL) { + error_report("out of buffers: %s", strerror(errno)); + return; + } + + /* Write data into buffer */ + buf = b->buffer; + p = buf->datas[0].data; + if (p == NULL) { + return; + } + offs = SPA_MIN(buf->datas[0].chunk->offset, buf->datas[0].maxsize); + n_bytes = SPA_MIN(buf->datas[0].chunk->size, buf->datas[0].maxsize - offs); + + filled = spa_ringbuffer_get_write_index(&v->ring, &index); + + + if (filled < 0) { + error_report("%p: underrun write:%u filled:%d", p, index, filled); + } else { + if ((uint32_t) filled + n_bytes > RINGBUFFER_SIZE) { + error_report("%p: overrun write:%u filled:%d + size:%u > max:%u", + p, index, filled, n_bytes, RINGBUFFER_SIZE); + } + } + spa_ringbuffer_write_data(&v->ring, + v->buffer, RINGBUFFER_SIZE, + index & RINGBUFFER_MASK, + SPA_PTROFF(p, offs, void), n_bytes); + index += n_bytes; + spa_ringbuffer_write_update(&v->ring, index); + + /* queue the buffer for playback */ + pw_stream_queue_buffer(v->stream, b); +} + +static void +on_stream_state_changed(void *data, enum pw_stream_state old, + enum pw_stream_state state, const char *error) +{ + PWVoice *v = (PWVoice *) data; + + trace_pw_state_changed(pw_stream_get_node_id(v->stream), + pw_stream_state_as_string(state)); +} + +static const struct pw_stream_events capture_stream_events = { + PW_VERSION_STREAM_EVENTS, + .destroy = stream_destroy, + .state_changed = on_stream_state_changed, + .process = capture_on_process +}; + +static const struct pw_stream_events playback_stream_events = { + PW_VERSION_STREAM_EVENTS, + .destroy = stream_destroy, + .state_changed = on_stream_state_changed, + .process = playback_on_process +}; + +static size_t +qpw_read(HWVoiceIn *hw, void *data, size_t len) +{ + PWVoiceIn *pw = (PWVoiceIn *) hw; + PWVoice *v = &pw->v; + pwaudio *c = v->g; + const char *error = NULL; + size_t l; + int32_t avail; + uint32_t index; + + pw_thread_loop_lock(c->thread_loop); + if (pw_stream_get_state(v->stream, &error) != PW_STREAM_STATE_STREAMING) { + /* wait for stream to become ready */ + l = 0; + goto done_unlock; + } + /* get no of available bytes to read data from buffer */ + avail = spa_ringbuffer_get_read_index(&v->ring, &index); + + trace_pw_read(avail, index, len); + + if (avail < (int32_t) len) { + len = avail; + } + + spa_ringbuffer_read_data(&v->ring, + v->buffer, RINGBUFFER_SIZE, + index & RINGBUFFER_MASK, data, len); + index += len; + spa_ringbuffer_read_update(&v->ring, index); + l = len; + +done_unlock: + pw_thread_loop_unlock(c->thread_loop); + return l; +} + +static size_t qpw_buffer_get_free(HWVoiceOut *hw) +{ + PWVoiceOut *pw = (PWVoiceOut *)hw; + PWVoice *v = &pw->v; + pwaudio *c = v->g; + const char *error = NULL; + int32_t filled, avail; + uint32_t index; + + pw_thread_loop_lock(c->thread_loop); + if (pw_stream_get_state(v->stream, &error) != PW_STREAM_STATE_STREAMING) { + /* wait for stream to become ready */ + avail = 0; + goto done_unlock; + } + + filled = spa_ringbuffer_get_write_index(&v->ring, &index); + avail = v->highwater_mark - filled; + +done_unlock: + pw_thread_loop_unlock(c->thread_loop); + return avail; +} + +static size_t +qpw_write(HWVoiceOut *hw, void *data, size_t len) +{ + PWVoiceOut *pw = (PWVoiceOut *) hw; + PWVoice *v = &pw->v; + pwaudio *c = v->g; + const char *error = NULL; + int32_t filled, avail; + uint32_t index; + + pw_thread_loop_lock(c->thread_loop); + if (pw_stream_get_state(v->stream, &error) != PW_STREAM_STATE_STREAMING) { + /* wait for stream to become ready */ + len = 0; + goto done_unlock; + } + filled = spa_ringbuffer_get_write_index(&v->ring, &index); + avail = v->highwater_mark - filled; + + trace_pw_write(filled, avail, index, len); + + if (len > avail) { + len = avail; + } + + if (filled < 0) { + error_report("%p: underrun write:%u filled:%d", pw, index, filled); + } else { + if ((uint32_t) filled + len > RINGBUFFER_SIZE) { + error_report("%p: overrun write:%u filled:%d + size:%zu > max:%u", + pw, index, filled, len, RINGBUFFER_SIZE); + } + } + + spa_ringbuffer_write_data(&v->ring, + v->buffer, RINGBUFFER_SIZE, + index & RINGBUFFER_MASK, data, len); + index += len; + spa_ringbuffer_write_update(&v->ring, index); + +done_unlock: + pw_thread_loop_unlock(c->thread_loop); + return len; +} + +static int +audfmt_to_pw(AudioFormat fmt, int endianness) +{ + int format; + + switch (fmt) { + case AUDIO_FORMAT_S8: + format = SPA_AUDIO_FORMAT_S8; + break; + case AUDIO_FORMAT_U8: + format = SPA_AUDIO_FORMAT_U8; + break; + case AUDIO_FORMAT_S16: + format = endianness ? SPA_AUDIO_FORMAT_S16_BE : SPA_AUDIO_FORMAT_S16_LE; + break; + case AUDIO_FORMAT_U16: + format = endianness ? SPA_AUDIO_FORMAT_U16_BE : SPA_AUDIO_FORMAT_U16_LE; + break; + case AUDIO_FORMAT_S32: + format = endianness ? SPA_AUDIO_FORMAT_S32_BE : SPA_AUDIO_FORMAT_S32_LE; + break; + case AUDIO_FORMAT_U32: + format = endianness ? SPA_AUDIO_FORMAT_U32_BE : SPA_AUDIO_FORMAT_U32_LE; + break; + case AUDIO_FORMAT_F32: + format = endianness ? SPA_AUDIO_FORMAT_F32_BE : SPA_AUDIO_FORMAT_F32_LE; + break; + default: + dolog("Internal logic error: Bad audio format %d\n", fmt); + format = SPA_AUDIO_FORMAT_U8; + break; + } + return format; +} + +static AudioFormat +pw_to_audfmt(enum spa_audio_format fmt, int *endianness, + uint32_t *sample_size) +{ + switch (fmt) { + case SPA_AUDIO_FORMAT_S8: + *sample_size = 1; + return AUDIO_FORMAT_S8; + case SPA_AUDIO_FORMAT_U8: + *sample_size = 1; + return AUDIO_FORMAT_U8; + case SPA_AUDIO_FORMAT_S16_BE: + *sample_size = 2; + *endianness = 1; + return AUDIO_FORMAT_S16; + case SPA_AUDIO_FORMAT_S16_LE: + *sample_size = 2; + *endianness = 0; + return AUDIO_FORMAT_S16; + case SPA_AUDIO_FORMAT_U16_BE: + *sample_size = 2; + *endianness = 1; + return AUDIO_FORMAT_U16; + case SPA_AUDIO_FORMAT_U16_LE: + *sample_size = 2; + *endianness = 0; + return AUDIO_FORMAT_U16; + case SPA_AUDIO_FORMAT_S32_BE: + *sample_size = 4; + *endianness = 1; + return AUDIO_FORMAT_S32; + case SPA_AUDIO_FORMAT_S32_LE: + *sample_size = 4; + *endianness = 0; + return AUDIO_FORMAT_S32; + case SPA_AUDIO_FORMAT_U32_BE: + *sample_size = 4; + *endianness = 1; + return AUDIO_FORMAT_U32; + case SPA_AUDIO_FORMAT_U32_LE: + *sample_size = 4; + *endianness = 0; + return AUDIO_FORMAT_U32; + case SPA_AUDIO_FORMAT_F32_BE: + *sample_size = 4; + *endianness = 1; + return AUDIO_FORMAT_F32; + case SPA_AUDIO_FORMAT_F32_LE: + *sample_size = 4; + *endianness = 0; + return AUDIO_FORMAT_F32; + default: + *sample_size = 1; + dolog("Internal logic error: Bad spa_audio_format %d\n", fmt); + return AUDIO_FORMAT_U8; + } +} + +static int +qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name, + const char *name, enum spa_direction dir) +{ + int res; + uint32_t n_params; + const struct spa_pod *params[2]; + uint8_t buffer[1024]; + struct spa_pod_builder b; + uint64_t buf_samples; + struct pw_properties *props; + + props = pw_properties_new(NULL, NULL); + if (!props) { + error_report("Failed to create PW properties: %s", g_strerror(errno)); + return -1; + } + + /* 75% of the timer period for faster updates */ + buf_samples = (uint64_t)v->g->dev->timer_period * v->info.rate + * 3 / 4 / 1000000; + pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%" PRIu64 "/%u", + buf_samples, v->info.rate); + + trace_pw_period(buf_samples, v->info.rate); + if (name) { + pw_properties_set(props, PW_KEY_TARGET_OBJECT, name); + } + v->stream = pw_stream_new(c->core, stream_name, props); + if (v->stream == NULL) { + error_report("Failed to create PW stream: %s", g_strerror(errno)); + return -1; + } + + if (dir == SPA_DIRECTION_INPUT) { + pw_stream_add_listener(v->stream, + &v->stream_listener, &capture_stream_events, v); + } else { + pw_stream_add_listener(v->stream, + &v->stream_listener, &playback_stream_events, v); + } + + n_params = 0; + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + params[n_params++] = spa_format_audio_raw_build(&b, + SPA_PARAM_EnumFormat, + &v->info); + + /* connect the stream to a sink or source */ + res = pw_stream_connect(v->stream, + dir == + SPA_DIRECTION_INPUT ? PW_DIRECTION_INPUT : + PW_DIRECTION_OUTPUT, PW_ID_ANY, + PW_STREAM_FLAG_AUTOCONNECT | + PW_STREAM_FLAG_INACTIVE | + PW_STREAM_FLAG_MAP_BUFFERS | + PW_STREAM_FLAG_RT_PROCESS, params, n_params); + if (res < 0) { + error_report("Failed to connect PW stream: %s", g_strerror(errno)); + pw_stream_destroy(v->stream); + return -1; + } + + return 0; +} + +static void +qpw_set_position(uint32_t channels, uint32_t position[SPA_AUDIO_MAX_CHANNELS]) +{ + memcpy(position, (uint32_t[SPA_AUDIO_MAX_CHANNELS]) { SPA_AUDIO_CHANNEL_UNKNOWN, }, + sizeof(uint32_t) * SPA_AUDIO_MAX_CHANNELS); + /* + * TODO: This currently expects the only frontend supporting more than 2 + * channels is the usb-audio. We will need some means to set channel + * order when a new frontend gains multi-channel support. + */ + switch (channels) { + case 8: + position[6] = SPA_AUDIO_CHANNEL_SL; + position[7] = SPA_AUDIO_CHANNEL_SR; + /* fallthrough */ + case 6: + position[2] = SPA_AUDIO_CHANNEL_FC; + position[3] = SPA_AUDIO_CHANNEL_LFE; + position[4] = SPA_AUDIO_CHANNEL_RL; + position[5] = SPA_AUDIO_CHANNEL_RR; + /* fallthrough */ + case 2: + position[0] = SPA_AUDIO_CHANNEL_FL; + position[1] = SPA_AUDIO_CHANNEL_FR; + break; + case 1: + position[0] = SPA_AUDIO_CHANNEL_MONO; + break; + default: + dolog("Internal error: unsupported channel count %d\n", channels); + } +} + +static int +qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) +{ + PWVoiceOut *pw = (PWVoiceOut *) hw; + PWVoice *v = &pw->v; + struct audsettings obt_as = *as; + pwaudio *c = v->g = drv_opaque; + AudiodevPipewireOptions *popts = &c->dev->u.pipewire; + AudiodevPipewirePerDirectionOptions *ppdo = popts->out; + int r; + + pw_thread_loop_lock(c->thread_loop); + + v->info.format = audfmt_to_pw(as->fmt, as->endianness); + v->info.channels = as->nchannels; + qpw_set_position(as->nchannels, v->info.position); + v->info.rate = as->freq; + + obt_as.fmt = + pw_to_audfmt(v->info.format, &obt_as.endianness, &v->frame_size); + v->frame_size *= as->nchannels; + + v->req = (uint64_t)c->dev->timer_period * v->info.rate + * 1 / 2 / 1000000 * v->frame_size; + + /* call the function that creates a new stream for playback */ + r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id, + ppdo->name, SPA_DIRECTION_OUTPUT); + if (r < 0) { + pw_thread_loop_unlock(c->thread_loop); + return -1; + } + + /* report the audio format we support */ + audio_pcm_init_info(&hw->info, &obt_as); + + /* report the buffer size to qemu */ + hw->samples = audio_buffer_frames( + qapi_AudiodevPipewirePerDirectionOptions_base(ppdo), &obt_as, 46440); + v->highwater_mark = MIN(RINGBUFFER_SIZE, + (ppdo->has_latency ? ppdo->latency : 46440) + * (uint64_t)v->info.rate / 1000000 * v->frame_size); + + pw_thread_loop_unlock(c->thread_loop); + return 0; +} + +static int +qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) +{ + PWVoiceIn *pw = (PWVoiceIn *) hw; + PWVoice *v = &pw->v; + struct audsettings obt_as = *as; + pwaudio *c = v->g = drv_opaque; + AudiodevPipewireOptions *popts = &c->dev->u.pipewire; + AudiodevPipewirePerDirectionOptions *ppdo = popts->in; + int r; + + pw_thread_loop_lock(c->thread_loop); + + v->info.format = audfmt_to_pw(as->fmt, as->endianness); + v->info.channels = as->nchannels; + qpw_set_position(as->nchannels, v->info.position); + v->info.rate = as->freq; + + obt_as.fmt = + pw_to_audfmt(v->info.format, &obt_as.endianness, &v->frame_size); + v->frame_size *= as->nchannels; + + /* call the function that creates a new stream for recording */ + r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id, + ppdo->name, SPA_DIRECTION_INPUT); + if (r < 0) { + pw_thread_loop_unlock(c->thread_loop); + return -1; + } + + /* report the audio format we support */ + audio_pcm_init_info(&hw->info, &obt_as); + + /* report the buffer size to qemu */ + hw->samples = audio_buffer_frames( + qapi_AudiodevPipewirePerDirectionOptions_base(ppdo), &obt_as, 46440); + + pw_thread_loop_unlock(c->thread_loop); + return 0; +} + +static void +qpw_voice_fini(PWVoice *v) +{ + pwaudio *c = v->g; + + if (!v->stream) { + return; + } + pw_thread_loop_lock(c->thread_loop); + pw_stream_destroy(v->stream); + v->stream = NULL; + pw_thread_loop_unlock(c->thread_loop); +} + +static void +qpw_fini_out(HWVoiceOut *hw) +{ + qpw_voice_fini(&PW_VOICE_OUT(hw)->v); +} + +static void +qpw_fini_in(HWVoiceIn *hw) +{ + qpw_voice_fini(&PW_VOICE_IN(hw)->v); +} + +static void +qpw_voice_set_enabled(PWVoice *v, bool enable) +{ + pwaudio *c = v->g; + pw_thread_loop_lock(c->thread_loop); + pw_stream_set_active(v->stream, enable); + pw_thread_loop_unlock(c->thread_loop); +} + +static void +qpw_enable_out(HWVoiceOut *hw, bool enable) +{ + qpw_voice_set_enabled(&PW_VOICE_OUT(hw)->v, enable); +} + +static void +qpw_enable_in(HWVoiceIn *hw, bool enable) +{ + qpw_voice_set_enabled(&PW_VOICE_IN(hw)->v, enable); +} + +static void +qpw_voice_set_volume(PWVoice *v, Volume *vol) +{ + pwaudio *c = v->g; + int i, ret; + + pw_thread_loop_lock(c->thread_loop); + v->volume.channels = vol->channels; + + for (i = 0; i < vol->channels; ++i) { + v->volume.values[i] = (float)vol->vol[i] / 255; + } + + ret = pw_stream_set_control(v->stream, + SPA_PROP_channelVolumes, v->volume.channels, v->volume.values, 0); + trace_pw_vol(ret == 0 ? "success" : "failed"); + + v->muted = vol->mute; + float val = v->muted ? 1.f : 0.f; + ret = pw_stream_set_control(v->stream, SPA_PROP_mute, 1, &val, 0); + pw_thread_loop_unlock(c->thread_loop); +} + +static void +qpw_volume_out(HWVoiceOut *hw, Volume *vol) +{ + qpw_voice_set_volume(&PW_VOICE_OUT(hw)->v, vol); +} + +static void +qpw_volume_in(HWVoiceIn *hw, Volume *vol) +{ + qpw_voice_set_volume(&PW_VOICE_IN(hw)->v, vol); +} + +static int wait_resync(pwaudio *pw) +{ + int res; + pw->pending_seq = pw_core_sync(pw->core, PW_ID_CORE, pw->pending_seq); + + while (true) { + pw_thread_loop_wait(pw->thread_loop); + + res = pw->error; + if (res < 0) { + pw->error = 0; + return res; + } + if (pw->pending_seq == pw->last_seq) { + break; + } + } + return 0; +} + +static void +on_core_error(void *data, uint32_t id, int seq, int res, const char *message) +{ + pwaudio *pw = data; + + error_report("error id:%u seq:%d res:%d (%s): %s", + id, seq, res, spa_strerror(res), message); + + /* stop and exit the thread loop */ + pw_thread_loop_signal(pw->thread_loop, FALSE); +} + +static void +on_core_done(void *data, uint32_t id, int seq) +{ + pwaudio *pw = data; + assert(id == PW_ID_CORE); + pw->last_seq = seq; + if (pw->pending_seq == seq) { + /* stop and exit the thread loop */ + pw_thread_loop_signal(pw->thread_loop, FALSE); + } +} + +static const struct pw_core_events core_events = { + PW_VERSION_CORE_EVENTS, + .done = on_core_done, + .error = on_core_error, +}; + +static void * +qpw_audio_init(Audiodev *dev, Error **errp) +{ + g_autofree pwaudio *pw = g_new0(pwaudio, 1); + + assert(dev->driver == AUDIODEV_DRIVER_PIPEWIRE); + trace_pw_audio_init(); + + pw_init(NULL, NULL); + + pw->dev = dev; + pw->thread_loop = pw_thread_loop_new("PipeWire thread loop", NULL); + if (pw->thread_loop == NULL) { + error_setg_errno(errp, errno, "Could not create PipeWire loop"); + goto fail; + } + + pw->context = + pw_context_new(pw_thread_loop_get_loop(pw->thread_loop), NULL, 0); + if (pw->context == NULL) { + error_setg_errno(errp, errno, "Could not create PipeWire context"); + goto fail; + } + + if (pw_thread_loop_start(pw->thread_loop) < 0) { + error_setg_errno(errp, errno, "Could not start PipeWire loop"); + goto fail; + } + + pw_thread_loop_lock(pw->thread_loop); + + pw->core = pw_context_connect(pw->context, NULL, 0); + if (pw->core == NULL) { + pw_thread_loop_unlock(pw->thread_loop); + goto fail_error; + } + + if (pw_core_add_listener(pw->core, &pw->core_listener, + &core_events, pw) < 0) { + pw_thread_loop_unlock(pw->thread_loop); + goto fail_error; + } + if (wait_resync(pw) < 0) { + pw_thread_loop_unlock(pw->thread_loop); + } + + pw_thread_loop_unlock(pw->thread_loop); + + return g_steal_pointer(&pw); + +fail_error: + error_setg(errp, "Failed to initialize PW context"); +fail: + if (pw->thread_loop) { + pw_thread_loop_stop(pw->thread_loop); + } + g_clear_pointer(&pw->context, pw_context_destroy); + g_clear_pointer(&pw->thread_loop, pw_thread_loop_destroy); + return NULL; +} + +static void +qpw_audio_fini(void *opaque) +{ + pwaudio *pw = opaque; + + if (pw->thread_loop) { + pw_thread_loop_stop(pw->thread_loop); + } + + if (pw->core) { + spa_hook_remove(&pw->core_listener); + spa_zero(pw->core_listener); + pw_core_disconnect(pw->core); + } + + if (pw->context) { + pw_context_destroy(pw->context); + } + pw_thread_loop_destroy(pw->thread_loop); + + g_free(pw); +} + +static struct audio_pcm_ops qpw_pcm_ops = { + .init_out = qpw_init_out, + .fini_out = qpw_fini_out, + .write = qpw_write, + .buffer_get_free = qpw_buffer_get_free, + .run_buffer_out = audio_generic_run_buffer_out, + .enable_out = qpw_enable_out, + .volume_out = qpw_volume_out, + .volume_in = qpw_volume_in, + + .init_in = qpw_init_in, + .fini_in = qpw_fini_in, + .read = qpw_read, + .run_buffer_in = audio_generic_run_buffer_in, + .enable_in = qpw_enable_in +}; + +static struct audio_driver pw_audio_driver = { + .name = "pipewire", + .descr = "http://www.pipewire.org/", + .init = qpw_audio_init, + .fini = qpw_audio_fini, + .pcm_ops = &qpw_pcm_ops, + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof(PWVoiceOut), + .voice_size_in = sizeof(PWVoiceIn), +}; + +static void +register_audio_pw(void) +{ + audio_driver_register(&pw_audio_driver); +} + +type_init(register_audio_pw); diff --git a/audio/rate_template.h b/audio/rate_template.h index f94c940c61..6648f0d2e5 100644 --- a/audio/rate_template.h +++ b/audio/rate_template.h @@ -40,8 +40,6 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, int64_t t; #endif - ilast = rate->ilast; - istart = ibuf; iend = ibuf + *isamp; @@ -59,32 +57,40 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, return; } - while (obuf < oend) { + /* without input samples, there's nothing to do */ + if (ibuf >= iend) { + *osamp = 0; + return; + } - /* Safety catch to make sure we have input samples. */ - if (ibuf >= iend) { - break; - } + ilast = rate->ilast; + + while (true) { /* read as many input samples so that ipos > opos */ - while (rate->ipos <= (rate->opos >> 32)) { ilast = *ibuf++; rate->ipos++; - /* if ipos overflow, there is a infinite loop */ - if (rate->ipos == 0xffffffff) { - rate->ipos = 1; - rate->opos = rate->opos & 0xffffffff; - } /* See if we finished the input buffer yet */ if (ibuf >= iend) { goto the_end; } } + /* make sure that the next output sample can be written */ + if (obuf >= oend) { + break; + } + icur = *ibuf; + /* wrap ipos and opos around long before they overflow */ + if (rate->ipos >= 0x10001) { + rate->ipos = 1; + rate->opos &= 0xffffffff; + } + /* interpolate */ #ifdef FLOAT_MIXENG #ifdef RECIPROCAL diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c index 68a237b76b..641357e5ee 100644 --- a/audio/sdlaudio.c +++ b/audio/sdlaudio.c @@ -26,6 +26,7 @@ #include #include #include "qemu/module.h" +#include "qapi/error.h" #include "audio.h" #ifndef _WIN32 @@ -449,10 +450,10 @@ static void sdl_enable_in(HWVoiceIn *hw, bool enable) SDL_PauseAudioDevice(sdl->devid, !enable); } -static void *sdl_audio_init(Audiodev *dev) +static void *sdl_audio_init(Audiodev *dev, Error **errp) { if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { - sdl_logerr ("SDL failed to initialize audio subsystem\n"); + error_setg(errp, "SDL failed to initialize audio subsystem"); return NULL; } @@ -493,7 +494,6 @@ static struct audio_driver sdl_audio_driver = { .init = sdl_audio_init, .fini = sdl_audio_fini, .pcm_ops = &sdl_pcm_ops, - .can_be_default = 1, .max_voices_out = INT_MAX, .max_voices_in = INT_MAX, .voice_size_out = sizeof(SDLVoiceOut), diff --git a/audio/sndioaudio.c b/audio/sndioaudio.c new file mode 100644 index 0000000000..8eb35e1e53 --- /dev/null +++ b/audio/sndioaudio.c @@ -0,0 +1,564 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2019 Alexandre Ratchov + */ + +/* + * TODO : + * + * Use a single device and open it in full-duplex rather than + * opening it twice (once for playback once for recording). + * + * This is the only way to ensure that playback doesn't drift with respect + * to recording, which is what guest systems expect. + */ + +#include "qemu/osdep.h" +#include +#include +#include "qemu/main-loop.h" +#include "audio.h" +#include "trace.h" + +#define AUDIO_CAP "sndio" +#include "audio_int.h" + +/* default latency in microseconds if no option is set */ +#define SNDIO_LATENCY_US 50000 + +typedef struct SndioVoice { + union { + HWVoiceOut out; + HWVoiceIn in; + } hw; + struct sio_par par; + struct sio_hdl *hdl; + struct pollfd *pfds; + struct pollindex { + struct SndioVoice *self; + int index; + } *pindexes; + unsigned char *buf; + size_t buf_size; + size_t sndio_pos; + size_t qemu_pos; + unsigned int mode; + unsigned int nfds; + bool enabled; +} SndioVoice; + +typedef struct SndioConf { + const char *devname; + unsigned int latency; +} SndioConf; + +/* needed for forward reference */ +static void sndio_poll_in(void *arg); +static void sndio_poll_out(void *arg); + +/* + * stop polling descriptors + */ +static void sndio_poll_clear(SndioVoice *self) +{ + struct pollfd *pfd; + int i; + + for (i = 0; i < self->nfds; i++) { + pfd = &self->pfds[i]; + qemu_set_fd_handler(pfd->fd, NULL, NULL, NULL); + } + + self->nfds = 0; +} + +/* + * write data to the device until it blocks or + * all of our buffered data is written + */ +static void sndio_write(SndioVoice *self) +{ + size_t todo, n; + + todo = self->qemu_pos - self->sndio_pos; + + /* + * transfer data to device, until it blocks + */ + while (todo > 0) { + n = sio_write(self->hdl, self->buf + self->sndio_pos, todo); + if (n == 0) { + break; + } + self->sndio_pos += n; + todo -= n; + } + + if (self->sndio_pos == self->buf_size) { + /* + * we complete the block + */ + self->sndio_pos = 0; + self->qemu_pos = 0; + } +} + +/* + * read data from the device until it blocks or + * there no room any longer + */ +static void sndio_read(SndioVoice *self) +{ + size_t todo, n; + + todo = self->buf_size - self->sndio_pos; + + /* + * transfer data from the device, until it blocks + */ + while (todo > 0) { + n = sio_read(self->hdl, self->buf + self->sndio_pos, todo); + if (n == 0) { + break; + } + self->sndio_pos += n; + todo -= n; + } +} + +/* + * Set handlers for all descriptors libsndio needs to + * poll + */ +static void sndio_poll_wait(SndioVoice *self) +{ + struct pollfd *pfd; + int events, i; + + events = 0; + if (self->mode == SIO_PLAY) { + if (self->sndio_pos < self->qemu_pos) { + events |= POLLOUT; + } + } else { + if (self->sndio_pos < self->buf_size) { + events |= POLLIN; + } + } + + /* + * fill the given array of descriptors with the events sndio + * wants, they are different from our 'event' variable because + * sndio may use descriptors internally. + */ + self->nfds = sio_pollfd(self->hdl, self->pfds, events); + + for (i = 0; i < self->nfds; i++) { + pfd = &self->pfds[i]; + if (pfd->fd < 0) { + continue; + } + qemu_set_fd_handler(pfd->fd, + (pfd->events & POLLIN) ? sndio_poll_in : NULL, + (pfd->events & POLLOUT) ? sndio_poll_out : NULL, + &self->pindexes[i]); + pfd->revents = 0; + } +} + +/* + * call-back called when one of the descriptors + * became readable or writable + */ +static void sndio_poll_event(SndioVoice *self, int index, int event) +{ + int revents; + + /* + * ensure we're not called twice this cycle + */ + sndio_poll_clear(self); + + /* + * make self->pfds[] look as we're returning from poll syscal, + * this is how sio_revents expects events to be. + */ + self->pfds[index].revents = event; + + /* + * tell sndio to handle events and return whether we can read or + * write without blocking. + */ + revents = sio_revents(self->hdl, self->pfds); + if (self->mode == SIO_PLAY) { + if (revents & POLLOUT) { + sndio_write(self); + } + + if (self->qemu_pos < self->buf_size) { + audio_run(self->hw.out.s, "sndio_out"); + } + } else { + if (revents & POLLIN) { + sndio_read(self); + } + + if (self->qemu_pos < self->sndio_pos) { + audio_run(self->hw.in.s, "sndio_in"); + } + } + + /* + * audio_run() may have changed state + */ + if (self->enabled) { + sndio_poll_wait(self); + } +} + +/* + * return the upper limit of the amount of free play buffer space + */ +static size_t sndio_buffer_get_free(HWVoiceOut *hw) +{ + SndioVoice *self = (SndioVoice *) hw; + + return self->buf_size - self->qemu_pos; +} + +/* + * return a buffer where data to play can be stored, + * its size is stored in the location pointed by the size argument. + */ +static void *sndio_get_buffer_out(HWVoiceOut *hw, size_t *size) +{ + SndioVoice *self = (SndioVoice *) hw; + + *size = self->buf_size - self->qemu_pos; + return self->buf + self->qemu_pos; +} + +/* + * put back to sndio back-end a buffer returned by sndio_get_buffer_out() + */ +static size_t sndio_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size) +{ + SndioVoice *self = (SndioVoice *) hw; + + self->qemu_pos += size; + sndio_poll_wait(self); + return size; +} + +/* + * return a buffer from where recorded data is available, + * its size is stored in the location pointed by the size argument. + * it may not exceed the initial value of "*size". + */ +static void *sndio_get_buffer_in(HWVoiceIn *hw, size_t *size) +{ + SndioVoice *self = (SndioVoice *) hw; + size_t todo, max_todo; + + /* + * unlike the get_buffer_out() method, get_buffer_in() + * must return a buffer of at most the given size, see audio.c + */ + max_todo = *size; + + todo = self->sndio_pos - self->qemu_pos; + if (todo > max_todo) { + todo = max_todo; + } + + *size = todo; + return self->buf + self->qemu_pos; +} + +/* + * discard the given amount of recorded data + */ +static void sndio_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size) +{ + SndioVoice *self = (SndioVoice *) hw; + + self->qemu_pos += size; + if (self->qemu_pos == self->buf_size) { + self->qemu_pos = 0; + self->sndio_pos = 0; + } + sndio_poll_wait(self); +} + +/* + * call-back called when one of our descriptors becomes writable + */ +static void sndio_poll_out(void *arg) +{ + struct pollindex *pindex = (struct pollindex *) arg; + + sndio_poll_event(pindex->self, pindex->index, POLLOUT); +} + +/* + * call-back called when one of our descriptors becomes readable + */ +static void sndio_poll_in(void *arg) +{ + struct pollindex *pindex = (struct pollindex *) arg; + + sndio_poll_event(pindex->self, pindex->index, POLLIN); +} + +static void sndio_fini(SndioVoice *self) +{ + if (self->hdl) { + sio_close(self->hdl); + self->hdl = NULL; + } + + g_free(self->pfds); + g_free(self->pindexes); + g_free(self->buf); +} + +static int sndio_init(SndioVoice *self, + struct audsettings *as, int mode, Audiodev *dev) +{ + AudiodevSndioOptions *opts = &dev->u.sndio; + unsigned long long latency; + const char *dev_name; + struct sio_par req; + unsigned int nch; + int i, nfds; + + dev_name = opts->dev ?: SIO_DEVANY; + latency = opts->has_latency ? opts->latency : SNDIO_LATENCY_US; + + /* open the device in non-blocking mode */ + self->hdl = sio_open(dev_name, mode, 1); + if (self->hdl == NULL) { + dolog("failed to open device\n"); + return -1; + } + + self->mode = mode; + + sio_initpar(&req); + + switch (as->fmt) { + case AUDIO_FORMAT_S8: + req.bits = 8; + req.sig = 1; + break; + case AUDIO_FORMAT_U8: + req.bits = 8; + req.sig = 0; + break; + case AUDIO_FORMAT_S16: + req.bits = 16; + req.sig = 1; + break; + case AUDIO_FORMAT_U16: + req.bits = 16; + req.sig = 0; + break; + case AUDIO_FORMAT_S32: + req.bits = 32; + req.sig = 1; + break; + case AUDIO_FORMAT_U32: + req.bits = 32; + req.sig = 0; + break; + default: + dolog("unknown audio sample format\n"); + return -1; + } + + if (req.bits > 8) { + req.le = as->endianness ? 0 : 1; + } + + req.rate = as->freq; + if (mode == SIO_PLAY) { + req.pchan = as->nchannels; + } else { + req.rchan = as->nchannels; + } + + /* set on-device buffer size */ + req.appbufsz = req.rate * latency / 1000000; + + if (!sio_setpar(self->hdl, &req)) { + dolog("failed set audio params\n"); + goto fail; + } + + if (!sio_getpar(self->hdl, &self->par)) { + dolog("failed get audio params\n"); + goto fail; + } + + nch = (mode == SIO_PLAY) ? self->par.pchan : self->par.rchan; + + /* + * With the default setup, sndio supports any combination of parameters + * so these checks are mostly to catch configuration errors. + */ + if (self->par.bits != req.bits || self->par.bps != req.bits / 8 || + self->par.sig != req.sig || (req.bits > 8 && self->par.le != req.le) || + self->par.rate != as->freq || nch != as->nchannels) { + dolog("unsupported audio params\n"); + goto fail; + } + + /* + * we use one block as buffer size; this is how + * transfers get well aligned + */ + self->buf_size = self->par.round * self->par.bps * nch; + + self->buf = g_malloc(self->buf_size); + if (self->buf == NULL) { + dolog("failed to allocate audio buffer\n"); + goto fail; + } + + nfds = sio_nfds(self->hdl); + + self->pfds = g_malloc_n(nfds, sizeof(struct pollfd)); + if (self->pfds == NULL) { + dolog("failed to allocate pollfd structures\n"); + goto fail; + } + + self->pindexes = g_malloc_n(nfds, sizeof(struct pollindex)); + if (self->pindexes == NULL) { + dolog("failed to allocate pollindex structures\n"); + goto fail; + } + + for (i = 0; i < nfds; i++) { + self->pindexes[i].self = self; + self->pindexes[i].index = i; + } + + return 0; +fail: + sndio_fini(self); + return -1; +} + +static void sndio_enable(SndioVoice *self, bool enable) +{ + if (enable) { + sio_start(self->hdl); + self->enabled = true; + sndio_poll_wait(self); + } else { + self->enabled = false; + sndio_poll_clear(self); + sio_stop(self->hdl); + } +} + +static void sndio_enable_out(HWVoiceOut *hw, bool enable) +{ + SndioVoice *self = (SndioVoice *) hw; + + sndio_enable(self, enable); +} + +static void sndio_enable_in(HWVoiceIn *hw, bool enable) +{ + SndioVoice *self = (SndioVoice *) hw; + + sndio_enable(self, enable); +} + +static int sndio_init_out(HWVoiceOut *hw, struct audsettings *as, void *opaque) +{ + SndioVoice *self = (SndioVoice *) hw; + + if (sndio_init(self, as, SIO_PLAY, opaque) == -1) { + return -1; + } + + audio_pcm_init_info(&hw->info, as); + hw->samples = self->par.round; + return 0; +} + +static int sndio_init_in(HWVoiceIn *hw, struct audsettings *as, void *opaque) +{ + SndioVoice *self = (SndioVoice *) hw; + + if (sndio_init(self, as, SIO_REC, opaque) == -1) { + return -1; + } + + audio_pcm_init_info(&hw->info, as); + hw->samples = self->par.round; + return 0; +} + +static void sndio_fini_out(HWVoiceOut *hw) +{ + SndioVoice *self = (SndioVoice *) hw; + + sndio_fini(self); +} + +static void sndio_fini_in(HWVoiceIn *hw) +{ + SndioVoice *self = (SndioVoice *) hw; + + sndio_fini(self); +} + +static void *sndio_audio_init(Audiodev *dev, Error **errp) +{ + assert(dev->driver == AUDIODEV_DRIVER_SNDIO); + return dev; +} + +static void sndio_audio_fini(void *opaque) +{ +} + +static struct audio_pcm_ops sndio_pcm_ops = { + .init_out = sndio_init_out, + .fini_out = sndio_fini_out, + .enable_out = sndio_enable_out, + .write = audio_generic_write, + .buffer_get_free = sndio_buffer_get_free, + .get_buffer_out = sndio_get_buffer_out, + .put_buffer_out = sndio_put_buffer_out, + .init_in = sndio_init_in, + .fini_in = sndio_fini_in, + .read = audio_generic_read, + .enable_in = sndio_enable_in, + .get_buffer_in = sndio_get_buffer_in, + .put_buffer_in = sndio_put_buffer_in, +}; + +static struct audio_driver sndio_audio_driver = { + .name = "sndio", + .descr = "sndio https://sndio.org", + .init = sndio_audio_init, + .fini = sndio_audio_fini, + .pcm_ops = &sndio_pcm_ops, + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof(SndioVoice), + .voice_size_in = sizeof(SndioVoice) +}; + +static void register_audio_sndio(void) +{ + audio_driver_register(&sndio_audio_driver); +} + +type_init(register_audio_sndio); diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c index a8d370fe6f..7f02f7285c 100644 --- a/audio/spiceaudio.c +++ b/audio/spiceaudio.c @@ -22,6 +22,7 @@ #include "qemu/module.h" #include "qemu/error-report.h" #include "qemu/timer.h" +#include "qapi/error.h" #include "ui/qemu-spice.h" #define AUDIO_CAP "spice" @@ -71,11 +72,13 @@ static const SpiceRecordInterface record_sif = { .base.minor_version = SPICE_INTERFACE_RECORD_MINOR, }; -static void *spice_audio_init(Audiodev *dev) +static void *spice_audio_init(Audiodev *dev, Error **errp) { if (!using_spice) { + error_setg(errp, "Cannot use spice audio without -spice"); return NULL; } + return &spice_audio_init; } @@ -120,6 +123,13 @@ static void line_out_fini (HWVoiceOut *hw) spice_server_remove_interface (&out->sin.base); } +static size_t line_out_get_free(HWVoiceOut *hw) +{ + SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw); + + return audio_rate_peek_bytes(&out->rate, &hw->info); +} + static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size) { SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw); @@ -133,8 +143,6 @@ static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size) *size = MIN((out->fsize - out->fpos) << 2, *size); } - *size = audio_rate_get_bytes(&hw->info, &out->rate, *size); - return out->frame + out->fpos; } @@ -142,6 +150,8 @@ static size_t line_out_put_buffer(HWVoiceOut *hw, void *buf, size_t size) { SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw); + audio_rate_add_bytes(&out->rate, size); + if (buf) { assert(buf == out->frame + out->fpos && out->fpos <= out->fsize); out->fpos += size >> 2; @@ -232,10 +242,13 @@ static void line_in_fini (HWVoiceIn *hw) static size_t line_in_read(HWVoiceIn *hw, void *buf, size_t len) { SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); - uint64_t to_read = audio_rate_get_bytes(&hw->info, &in->rate, len) >> 2; + uint64_t to_read = audio_rate_get_bytes(&in->rate, &hw->info, len) >> 2; size_t ready = spice_server_record_get_samples(&in->sin, buf, to_read); - /* XXX: do we need this? */ + /* + * If the client didn't send new frames, it most likely disconnected. + * Generate silence in this case to avoid a stalled audio stream. + */ if (ready == 0) { memset(buf, 0, to_read << 2); ready = to_read; @@ -282,6 +295,7 @@ static struct audio_pcm_ops audio_callbacks = { .init_out = line_out_init, .fini_out = line_out_fini, .write = audio_generic_write, + .buffer_get_free = line_out_get_free, .get_buffer_out = line_out_get_buffer, .put_buffer_out = line_out_put_buffer, .enable_out = line_out_enable, diff --git a/audio/trace-events b/audio/trace-events index e1ab643add..ab04f020ce 100644 --- a/audio/trace-events +++ b/audio/trace-events @@ -18,6 +18,14 @@ dbus_audio_register(const char *s, const char *dir) "sender = %s, dir = %s" dbus_audio_put_buffer_out(size_t len) "len = %zu" dbus_audio_read(size_t len) "len = %zu" +# pwaudio.c +pw_state_changed(int nodeid, const char *s) "node id: %d stream state: %s" +pw_read(int32_t avail, uint32_t index, size_t len) "avail=%d index=%u len=%zu" +pw_write(int32_t filled, int32_t avail, uint32_t index, size_t len) "filled=%d avail=%d index=%u len=%zu" +pw_vol(const char *ret) "set volume: %s" +pw_period(uint64_t quantum, uint32_t rate) "period =%" PRIu64 "/%u" +pw_audio_init(void) "Initialize PipeWire context" + # audio.c audio_timer_start(int interval) "interval %d ms" audio_timer_stop(void) "" diff --git a/audio/wavaudio.c b/audio/wavaudio.c index ac666335c7..a8798a1c42 100644 --- a/audio/wavaudio.c +++ b/audio/wavaudio.c @@ -42,7 +42,7 @@ typedef struct WAVVoiceOut { static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len) { WAVVoiceOut *wav = (WAVVoiceOut *) hw; - int64_t bytes = audio_rate_get_bytes(&hw->info, &wav->rate, len); + int64_t bytes = audio_rate_get_bytes(&wav->rate, &hw->info, len); assert(bytes % hw->info.bytes_per_frame == 0); if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) { @@ -78,7 +78,7 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, Audiodev *dev = drv_opaque; AudiodevWavOptions *wopts = &dev->u.wav; struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out); - const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav"; + const char *wav_path = wopts->path ?: "qemu.wav"; stereo = wav_as.nchannels == 2; switch (wav_as.fmt) { @@ -97,6 +97,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, dolog ("WAVE files can not handle 32bit formats\n"); return -1; + case AUDIO_FORMAT_F32: + dolog("WAVE files can not handle float formats\n"); + return -1; + default: abort(); } @@ -182,7 +186,7 @@ static void wav_enable_out(HWVoiceOut *hw, bool enable) } } -static void *wav_audio_init(Audiodev *dev) +static void *wav_audio_init(Audiodev *dev, Error **errp) { assert(dev->driver == AUDIODEV_DRIVER_WAV); return dev; @@ -208,7 +212,6 @@ static struct audio_driver wav_audio_driver = { .init = wav_audio_init, .fini = wav_audio_fini, .pcm_ops = &wav_pcm_ops, - .can_be_default = 0, .max_voices_out = 1, .max_voices_in = 0, .voice_size_out = sizeof (WAVVoiceOut), diff --git a/authz/listfile.c b/authz/listfile.c index da3a0e69a2..45a60e987d 100644 --- a/authz/listfile.c +++ b/authz/listfile.c @@ -30,7 +30,6 @@ #include "qapi/qapi-visit-authz.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qobject.h" -#include "qapi/qmp/qerror.h" #include "qapi/qobject-input-visitor.h" diff --git a/backends/Kconfig b/backends/Kconfig index f35abc1609..2cb23f62fa 100644 --- a/backends/Kconfig +++ b/backends/Kconfig @@ -1 +1,5 @@ source tpm/Kconfig + +config IOMMUFD + bool + depends on VFIO diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c index 0671bf9f3e..a514bbb310 100644 --- a/backends/cryptodev-builtin.c +++ b/backends/cryptodev-builtin.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "standard-headers/linux/virtio_crypto.h" #include "crypto/cipher.h" +#include "crypto/akcipher.h" #include "qom/object.h" @@ -42,10 +43,11 @@ typedef struct CryptoDevBackendBuiltinSession { QCryptoCipher *cipher; uint8_t direction; /* encryption or decryption */ uint8_t type; /* cipher? hash? aead? */ + QCryptoAkCipher *akcipher; QTAILQ_ENTRY(CryptoDevBackendBuiltinSession) next; } CryptoDevBackendBuiltinSession; -/* Max number of symmetric sessions */ +/* Max number of symmetric/asymmetric sessions */ #define MAX_NUM_SESSIONS 256 #define CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN 512 @@ -57,6 +59,19 @@ struct CryptoDevBackendBuiltin { CryptoDevBackendBuiltinSession *sessions[MAX_NUM_SESSIONS]; }; +static void cryptodev_builtin_init_akcipher(CryptoDevBackend *backend) +{ + QCryptoAkCipherOptions opts; + + opts.alg = QCRYPTO_AKCIPHER_ALG_RSA; + opts.u.rsa.padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW; + if (qcrypto_akcipher_supports(&opts)) { + backend->conf.crypto_services |= + (1u << QCRYPTODEV_BACKEND_SERVICE_AKCIPHER); + backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA; + } +} + static void cryptodev_builtin_init( CryptoDevBackend *backend, Error **errp) { @@ -70,17 +85,16 @@ static void cryptodev_builtin_init( return; } - cc = cryptodev_backend_new_client( - "cryptodev-builtin", NULL); + cc = cryptodev_backend_new_client(); cc->info_str = g_strdup_printf("cryptodev-builtin0"); cc->queue_index = 0; - cc->type = CRYPTODEV_BACKEND_TYPE_BUILTIN; + cc->type = QCRYPTODEV_BACKEND_TYPE_BUILTIN; backend->conf.peers.ccs[0] = cc; backend->conf.crypto_services = - 1u << VIRTIO_CRYPTO_SERVICE_CIPHER | - 1u << VIRTIO_CRYPTO_SERVICE_HASH | - 1u << VIRTIO_CRYPTO_SERVICE_MAC; + 1u << QCRYPTODEV_BACKEND_SERVICE_CIPHER | + 1u << QCRYPTODEV_BACKEND_SERVICE_HASH | + 1u << QCRYPTODEV_BACKEND_SERVICE_MAC; backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC; backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1; /* @@ -88,9 +102,10 @@ static void cryptodev_builtin_init( * Why this value? Just avoid to overflow when * memory allocation for each crypto request. */ - backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendSymOpInfo); + backend->conf.max_size = LONG_MAX - sizeof(CryptoDevBackendOpInfo); backend->conf.max_cipher_key_len = CRYPTODEV_BUITLIN_MAX_CIPHER_KEY_LEN; backend->conf.max_auth_key_len = CRYPTODEV_BUITLIN_MAX_AUTH_KEY_LEN; + cryptodev_builtin_init_akcipher(backend); cryptodev_backend_set_ready(backend, true); } @@ -148,6 +163,55 @@ err: return -1; } +static int cryptodev_builtin_get_rsa_hash_algo( + int virtio_rsa_hash, Error **errp) +{ + switch (virtio_rsa_hash) { + case VIRTIO_CRYPTO_RSA_MD5: + return QCRYPTO_HASH_ALG_MD5; + + case VIRTIO_CRYPTO_RSA_SHA1: + return QCRYPTO_HASH_ALG_SHA1; + + case VIRTIO_CRYPTO_RSA_SHA256: + return QCRYPTO_HASH_ALG_SHA256; + + case VIRTIO_CRYPTO_RSA_SHA512: + return QCRYPTO_HASH_ALG_SHA512; + + default: + error_setg(errp, "Unsupported rsa hash algo: %d", virtio_rsa_hash); + return -1; + } +} + +static int cryptodev_builtin_set_rsa_options( + int virtio_padding_algo, + int virtio_hash_algo, + QCryptoAkCipherOptionsRSA *opt, + Error **errp) +{ + if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) { + int hash_alg; + + hash_alg = cryptodev_builtin_get_rsa_hash_algo(virtio_hash_algo, errp); + if (hash_alg < 0) { + return -1; + } + opt->hash_alg = hash_alg; + opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1; + return 0; + } + + if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_RAW_PADDING) { + opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW; + return 0; + } + + error_setg(errp, "Unsupported rsa padding algo: %d", virtio_padding_algo); + return -1; +} + static int cryptodev_builtin_create_cipher_session( CryptoDevBackendBuiltin *builtin, CryptoDevBackendSymSessionInfo *sess_info, @@ -240,78 +304,160 @@ static int cryptodev_builtin_create_cipher_session( return index; } -static int64_t cryptodev_builtin_sym_create_session( - CryptoDevBackend *backend, - CryptoDevBackendSymSessionInfo *sess_info, - uint32_t queue_index, Error **errp) +static int cryptodev_builtin_create_akcipher_session( + CryptoDevBackendBuiltin *builtin, + CryptoDevBackendAsymSessionInfo *sess_info, + Error **errp) { - CryptoDevBackendBuiltin *builtin = - CRYPTODEV_BACKEND_BUILTIN(backend); - int64_t session_id = -1; - int ret; + CryptoDevBackendBuiltinSession *sess; + QCryptoAkCipher *akcipher; + int index; + QCryptoAkCipherKeyType type; + QCryptoAkCipherOptions opts; - switch (sess_info->op_code) { - case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: - ret = cryptodev_builtin_create_cipher_session( - builtin, sess_info, errp); - if (ret < 0) { - return ret; - } else { - session_id = ret; + switch (sess_info->algo) { + case VIRTIO_CRYPTO_AKCIPHER_RSA: + opts.alg = QCRYPTO_AKCIPHER_ALG_RSA; + if (cryptodev_builtin_set_rsa_options(sess_info->u.rsa.padding_algo, + sess_info->u.rsa.hash_algo, &opts.u.rsa, errp) != 0) { + return -1; } break; - case VIRTIO_CRYPTO_HASH_CREATE_SESSION: - case VIRTIO_CRYPTO_MAC_CREATE_SESSION: + + /* TODO support DSA&ECDSA until qemu crypto framework support these */ + default: - error_setg(errp, "Unsupported opcode :%" PRIu32 "", - sess_info->op_code); + error_setg(errp, "Unsupported akcipher alg %u", sess_info->algo); return -1; } - return session_id; + switch (sess_info->keytype) { + case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC: + type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC; + break; + + case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE: + type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE; + break; + + default: + error_setg(errp, "Unsupported akcipher keytype %u", sess_info->keytype); + return -1; + } + + index = cryptodev_builtin_get_unused_session_index(builtin); + if (index < 0) { + error_setg(errp, "Total number of sessions created exceeds %u", + MAX_NUM_SESSIONS); + return -1; + } + + akcipher = qcrypto_akcipher_new(&opts, type, sess_info->key, + sess_info->keylen, errp); + if (!akcipher) { + return -1; + } + + sess = g_new0(CryptoDevBackendBuiltinSession, 1); + sess->akcipher = akcipher; + + builtin->sessions[index] = sess; + + return index; } -static int cryptodev_builtin_sym_close_session( +static int cryptodev_builtin_create_session( CryptoDevBackend *backend, - uint64_t session_id, - uint32_t queue_index, Error **errp) + CryptoDevBackendSessionInfo *sess_info, + uint32_t queue_index, + CryptoDevCompletionFunc cb, + void *opaque) { CryptoDevBackendBuiltin *builtin = CRYPTODEV_BACKEND_BUILTIN(backend); + CryptoDevBackendSymSessionInfo *sym_sess_info; + CryptoDevBackendAsymSessionInfo *asym_sess_info; + int ret, status; + Error *local_error = NULL; - assert(session_id < MAX_NUM_SESSIONS && builtin->sessions[session_id]); + switch (sess_info->op_code) { + case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: + sym_sess_info = &sess_info->u.sym_sess_info; + ret = cryptodev_builtin_create_cipher_session( + builtin, sym_sess_info, &local_error); + break; - qcrypto_cipher_free(builtin->sessions[session_id]->cipher); - g_free(builtin->sessions[session_id]); + case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION: + asym_sess_info = &sess_info->u.asym_sess_info; + ret = cryptodev_builtin_create_akcipher_session( + builtin, asym_sess_info, &local_error); + break; + + case VIRTIO_CRYPTO_HASH_CREATE_SESSION: + case VIRTIO_CRYPTO_MAC_CREATE_SESSION: + default: + error_setg(&local_error, "Unsupported opcode :%" PRIu32 "", + sess_info->op_code); + return -VIRTIO_CRYPTO_NOTSUPP; + } + + if (local_error) { + error_report_err(local_error); + } + if (ret < 0) { + status = -VIRTIO_CRYPTO_ERR; + } else { + sess_info->session_id = ret; + status = VIRTIO_CRYPTO_OK; + } + if (cb) { + cb(opaque, status); + } + return 0; +} + +static int cryptodev_builtin_close_session( + CryptoDevBackend *backend, + uint64_t session_id, + uint32_t queue_index, + CryptoDevCompletionFunc cb, + void *opaque) +{ + CryptoDevBackendBuiltin *builtin = + CRYPTODEV_BACKEND_BUILTIN(backend); + CryptoDevBackendBuiltinSession *session; + + if (session_id >= MAX_NUM_SESSIONS || !builtin->sessions[session_id]) { + return -VIRTIO_CRYPTO_INVSESS; + } + + session = builtin->sessions[session_id]; + if (session->cipher) { + qcrypto_cipher_free(session->cipher); + } else if (session->akcipher) { + qcrypto_akcipher_free(session->akcipher); + } + + g_free(session); builtin->sessions[session_id] = NULL; + if (cb) { + cb(opaque, VIRTIO_CRYPTO_OK); + } return 0; } static int cryptodev_builtin_sym_operation( - CryptoDevBackend *backend, - CryptoDevBackendSymOpInfo *op_info, - uint32_t queue_index, Error **errp) + CryptoDevBackendBuiltinSession *sess, + CryptoDevBackendSymOpInfo *op_info, Error **errp) { - CryptoDevBackendBuiltin *builtin = - CRYPTODEV_BACKEND_BUILTIN(backend); - CryptoDevBackendBuiltinSession *sess; int ret; - if (op_info->session_id >= MAX_NUM_SESSIONS || - builtin->sessions[op_info->session_id] == NULL) { - error_setg(errp, "Cannot find a valid session id: %" PRIu64 "", - op_info->session_id); - return -VIRTIO_CRYPTO_INVSESS; - } - if (op_info->op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { error_setg(errp, "Algorithm chain is unsupported for cryptdoev-builtin"); return -VIRTIO_CRYPTO_NOTSUPP; } - sess = builtin->sessions[op_info->session_id]; - if (op_info->iv_len > 0) { ret = qcrypto_cipher_setiv(sess->cipher, op_info->iv, op_info->iv_len, errp); @@ -333,9 +479,106 @@ static int cryptodev_builtin_sym_operation( return -VIRTIO_CRYPTO_ERR; } } + return VIRTIO_CRYPTO_OK; } +static int cryptodev_builtin_asym_operation( + CryptoDevBackendBuiltinSession *sess, uint32_t op_code, + CryptoDevBackendAsymOpInfo *op_info, Error **errp) +{ + int ret; + + switch (op_code) { + case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT: + ret = qcrypto_akcipher_encrypt(sess->akcipher, + op_info->src, op_info->src_len, + op_info->dst, op_info->dst_len, errp); + break; + + case VIRTIO_CRYPTO_AKCIPHER_DECRYPT: + ret = qcrypto_akcipher_decrypt(sess->akcipher, + op_info->src, op_info->src_len, + op_info->dst, op_info->dst_len, errp); + break; + + case VIRTIO_CRYPTO_AKCIPHER_SIGN: + ret = qcrypto_akcipher_sign(sess->akcipher, + op_info->src, op_info->src_len, + op_info->dst, op_info->dst_len, errp); + break; + + case VIRTIO_CRYPTO_AKCIPHER_VERIFY: + ret = qcrypto_akcipher_verify(sess->akcipher, + op_info->src, op_info->src_len, + op_info->dst, op_info->dst_len, errp); + break; + + default: + return -VIRTIO_CRYPTO_ERR; + } + + if (ret < 0) { + if (op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY) { + return -VIRTIO_CRYPTO_KEY_REJECTED; + } + return -VIRTIO_CRYPTO_ERR; + } + + /* Buffer is too short, typically the driver should handle this case */ + if (unlikely(ret > op_info->dst_len)) { + if (errp && !*errp) { + error_setg(errp, "dst buffer too short"); + } + + return -VIRTIO_CRYPTO_ERR; + } + + op_info->dst_len = ret; + + return VIRTIO_CRYPTO_OK; +} + +static int cryptodev_builtin_operation( + CryptoDevBackend *backend, + CryptoDevBackendOpInfo *op_info) +{ + CryptoDevBackendBuiltin *builtin = + CRYPTODEV_BACKEND_BUILTIN(backend); + CryptoDevBackendBuiltinSession *sess; + CryptoDevBackendSymOpInfo *sym_op_info; + CryptoDevBackendAsymOpInfo *asym_op_info; + QCryptodevBackendAlgType algtype = op_info->algtype; + int status = -VIRTIO_CRYPTO_ERR; + Error *local_error = NULL; + + if (op_info->session_id >= MAX_NUM_SESSIONS || + builtin->sessions[op_info->session_id] == NULL) { + error_setg(&local_error, "Cannot find a valid session id: %" PRIu64 "", + op_info->session_id); + return -VIRTIO_CRYPTO_INVSESS; + } + + sess = builtin->sessions[op_info->session_id]; + if (algtype == QCRYPTODEV_BACKEND_ALG_SYM) { + sym_op_info = op_info->u.sym_op_info; + status = cryptodev_builtin_sym_operation(sess, sym_op_info, + &local_error); + } else if (algtype == QCRYPTODEV_BACKEND_ALG_ASYM) { + asym_op_info = op_info->u.asym_op_info; + status = cryptodev_builtin_asym_operation(sess, op_info->op_code, + asym_op_info, &local_error); + } + + if (local_error) { + error_report_err(local_error); + } + if (op_info->cb) { + op_info->cb(op_info->opaque, status); + } + return 0; +} + static void cryptodev_builtin_cleanup( CryptoDevBackend *backend, Error **errp) @@ -348,7 +591,7 @@ static void cryptodev_builtin_cleanup( for (i = 0; i < MAX_NUM_SESSIONS; i++) { if (builtin->sessions[i] != NULL) { - cryptodev_builtin_sym_close_session(backend, i, 0, &error_abort); + cryptodev_builtin_close_session(backend, i, 0, NULL, NULL); } } @@ -370,9 +613,9 @@ cryptodev_builtin_class_init(ObjectClass *oc, void *data) bc->init = cryptodev_builtin_init; bc->cleanup = cryptodev_builtin_cleanup; - bc->create_session = cryptodev_builtin_sym_create_session; - bc->close_session = cryptodev_builtin_sym_close_session; - bc->do_sym_op = cryptodev_builtin_sym_operation; + bc->create_session = cryptodev_builtin_create_session; + bc->close_session = cryptodev_builtin_close_session; + bc->do_op = cryptodev_builtin_operation; } static const TypeInfo cryptodev_builtin_info = { diff --git a/backends/cryptodev-hmp-cmds.c b/backends/cryptodev-hmp-cmds.c new file mode 100644 index 0000000000..4f7220bb13 --- /dev/null +++ b/backends/cryptodev-hmp-cmds.c @@ -0,0 +1,54 @@ +/* + * HMP commands related to cryptodev + * + * Copyright (c) 2023 Bytedance.Inc + * + * Authors: + * zhenwei pi + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/qapi-commands-cryptodev.h" +#include "qapi/qmp/qdict.h" + + +void hmp_info_cryptodev(Monitor *mon, const QDict *qdict) +{ + QCryptodevInfoList *il; + QCryptodevBackendServiceTypeList *sl; + QCryptodevBackendClientList *cl; + + for (il = qmp_query_cryptodev(NULL); il; il = il->next) { + g_autofree char *services = NULL; + QCryptodevInfo *info = il->value; + char *tmp_services; + + /* build a string like 'service=[akcipher|mac|hash|cipher]' */ + for (sl = info->service; sl; sl = sl->next) { + const char *service = QCryptodevBackendServiceType_str(sl->value); + + if (!services) { + services = g_strdup(service); + } else { + tmp_services = g_strjoin("|", services, service, NULL); + g_free(services); + services = tmp_services; + } + } + monitor_printf(mon, "%s: service=[%s]\n", info->id, services); + + for (cl = info->client; cl; cl = cl->next) { + QCryptodevBackendClient *client = cl->value; + monitor_printf(mon, " queue %" PRIu32 ": type=%s\n", + client->queue, + QCryptodevBackendType_str(client->type)); + } + } + + qapi_free_QCryptodevInfoList(il); +} diff --git a/backends/cryptodev-lkcf.c b/backends/cryptodev-lkcf.c new file mode 100644 index 0000000000..45aba1ff67 --- /dev/null +++ b/backends/cryptodev-lkcf.c @@ -0,0 +1,642 @@ +/* + * QEMU Cryptodev backend for QEMU cipher APIs + * + * Copyright (c) 2022 Bytedance.Inc + * + * Authors: + * lei he + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "crypto/cipher.h" +#include "crypto/akcipher.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "qemu/thread.h" +#include "qemu/error-report.h" +#include "qemu/queue.h" +#include "qom/object.h" +#include "sysemu/cryptodev.h" +#include "standard-headers/linux/virtio_crypto.h" + +#include +#include + +/** + * @TYPE_CRYPTODEV_BACKEND_LKCF: + * name of backend that uses linux kernel crypto framework + */ +#define TYPE_CRYPTODEV_BACKEND_LKCF "cryptodev-backend-lkcf" + +OBJECT_DECLARE_SIMPLE_TYPE(CryptoDevBackendLKCF, CRYPTODEV_BACKEND_LKCF) + +#define INVALID_KEY_ID -1 +#define MAX_SESSIONS 256 +#define NR_WORKER_THREAD 64 + +#define KCTL_KEY_TYPE_PKEY "asymmetric" +/** + * Here the key is uploaded to the thread-keyring of worker thread, at least + * util linux-6.0: + * 1. process keyring seems to behave unexpectedly if main-thread does not + * create the keyring before creating any other thread. + * 2. at present, the guest kernel never perform multiple operations on a + * session. + * 3. it can reduce the load of the main-loop because the key passed by the + * guest kernel has been already checked. + */ +#define KCTL_KEY_RING KEY_SPEC_THREAD_KEYRING + +typedef struct CryptoDevBackendLKCFSession { + uint8_t *key; + size_t keylen; + QCryptoAkCipherKeyType keytype; + QCryptoAkCipherOptions akcipher_opts; +} CryptoDevBackendLKCFSession; + +typedef struct CryptoDevBackendLKCF CryptoDevBackendLKCF; +typedef struct CryptoDevLKCFTask CryptoDevLKCFTask; +struct CryptoDevLKCFTask { + CryptoDevBackendLKCFSession *sess; + CryptoDevBackendOpInfo *op_info; + CryptoDevCompletionFunc cb; + void *opaque; + int status; + CryptoDevBackendLKCF *lkcf; + QSIMPLEQ_ENTRY(CryptoDevLKCFTask) queue; +}; + +typedef struct CryptoDevBackendLKCF { + CryptoDevBackend parent_obj; + CryptoDevBackendLKCFSession *sess[MAX_SESSIONS]; + QSIMPLEQ_HEAD(, CryptoDevLKCFTask) requests; + QSIMPLEQ_HEAD(, CryptoDevLKCFTask) responses; + QemuMutex mutex; + QemuCond cond; + QemuMutex rsp_mutex; + + /** + * There is no async interface for asymmetric keys like AF_ALG sockets, + * we don't seem to have better way than create a lots of thread. + */ + QemuThread worker_threads[NR_WORKER_THREAD]; + bool running; + int eventfd; +} CryptoDevBackendLKCF; + +static void *cryptodev_lkcf_worker(void *arg); +static int cryptodev_lkcf_close_session(CryptoDevBackend *backend, + uint64_t session_id, + uint32_t queue_index, + CryptoDevCompletionFunc cb, + void *opaque); + +static void cryptodev_lkcf_handle_response(void *opaque) +{ + CryptoDevBackendLKCF *lkcf = (CryptoDevBackendLKCF *)opaque; + QSIMPLEQ_HEAD(, CryptoDevLKCFTask) responses; + CryptoDevLKCFTask *task, *next; + eventfd_t nevent; + + QSIMPLEQ_INIT(&responses); + eventfd_read(lkcf->eventfd, &nevent); + + qemu_mutex_lock(&lkcf->rsp_mutex); + QSIMPLEQ_PREPEND(&responses, &lkcf->responses); + qemu_mutex_unlock(&lkcf->rsp_mutex); + + QSIMPLEQ_FOREACH_SAFE(task, &responses, queue, next) { + if (task->cb) { + task->cb(task->opaque, task->status); + } + g_free(task); + } +} + +static int cryptodev_lkcf_set_op_desc(QCryptoAkCipherOptions *opts, + char *key_desc, + size_t desc_len, + Error **errp) +{ + QCryptoAkCipherOptionsRSA *rsa_opt; + if (opts->alg != QCRYPTO_AKCIPHER_ALG_RSA) { + error_setg(errp, "Unsupported alg: %u", opts->alg); + return -1; + } + + rsa_opt = &opts->u.rsa; + if (rsa_opt->padding_alg == QCRYPTO_RSA_PADDING_ALG_PKCS1) { + snprintf(key_desc, desc_len, "enc=%s hash=%s", + QCryptoRSAPaddingAlgorithm_str(rsa_opt->padding_alg), + QCryptoHashAlgorithm_str(rsa_opt->hash_alg)); + + } else { + snprintf(key_desc, desc_len, "enc=%s", + QCryptoRSAPaddingAlgorithm_str(rsa_opt->padding_alg)); + } + return 0; +} + +static int cryptodev_lkcf_set_rsa_opt(int virtio_padding_alg, + int virtio_hash_alg, + QCryptoAkCipherOptionsRSA *opt, + Error **errp) +{ + if (virtio_padding_alg == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) { + opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1; + + switch (virtio_hash_alg) { + case VIRTIO_CRYPTO_RSA_MD5: + opt->hash_alg = QCRYPTO_HASH_ALG_MD5; + break; + + case VIRTIO_CRYPTO_RSA_SHA1: + opt->hash_alg = QCRYPTO_HASH_ALG_SHA1; + break; + + case VIRTIO_CRYPTO_RSA_SHA256: + opt->hash_alg = QCRYPTO_HASH_ALG_SHA256; + break; + + case VIRTIO_CRYPTO_RSA_SHA512: + opt->hash_alg = QCRYPTO_HASH_ALG_SHA512; + break; + + default: + error_setg(errp, "Unsupported rsa hash algo: %d", virtio_hash_alg); + return -1; + } + return 0; + } + + if (virtio_padding_alg == VIRTIO_CRYPTO_RSA_RAW_PADDING) { + opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW; + return 0; + } + + error_setg(errp, "Unsupported rsa padding algo: %u", virtio_padding_alg); + return -1; +} + +static int cryptodev_lkcf_get_unused_session_index(CryptoDevBackendLKCF *lkcf) +{ + size_t i; + + for (i = 0; i < MAX_SESSIONS; i++) { + if (lkcf->sess[i] == NULL) { + return i; + } + } + return -1; +} + +static void cryptodev_lkcf_init(CryptoDevBackend *backend, Error **errp) +{ + /* Only support one queue */ + int queues = backend->conf.peers.queues, i; + CryptoDevBackendClient *cc; + CryptoDevBackendLKCF *lkcf = + CRYPTODEV_BACKEND_LKCF(backend); + + if (queues != 1) { + error_setg(errp, + "Only support one queue in cryptodev-builtin backend"); + return; + } + lkcf->eventfd = eventfd(0, 0); + if (lkcf->eventfd < 0) { + error_setg(errp, "Failed to create eventfd: %d", errno); + return; + } + + cc = cryptodev_backend_new_client(); + cc->info_str = g_strdup_printf("cryptodev-lkcf0"); + cc->queue_index = 0; + cc->type = QCRYPTODEV_BACKEND_TYPE_LKCF; + backend->conf.peers.ccs[0] = cc; + + backend->conf.crypto_services = + 1u << QCRYPTODEV_BACKEND_SERVICE_AKCIPHER; + backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA; + lkcf->running = true; + + QSIMPLEQ_INIT(&lkcf->requests); + QSIMPLEQ_INIT(&lkcf->responses); + qemu_mutex_init(&lkcf->mutex); + qemu_mutex_init(&lkcf->rsp_mutex); + qemu_cond_init(&lkcf->cond); + for (i = 0; i < NR_WORKER_THREAD; i++) { + qemu_thread_create(&lkcf->worker_threads[i], "lkcf-worker", + cryptodev_lkcf_worker, lkcf, 0); + } + qemu_set_fd_handler( + lkcf->eventfd, cryptodev_lkcf_handle_response, NULL, lkcf); + cryptodev_backend_set_ready(backend, true); +} + +static void cryptodev_lkcf_cleanup(CryptoDevBackend *backend, Error **errp) +{ + CryptoDevBackendLKCF *lkcf = CRYPTODEV_BACKEND_LKCF(backend); + size_t i; + int queues = backend->conf.peers.queues; + CryptoDevBackendClient *cc; + CryptoDevLKCFTask *task, *next; + + qemu_mutex_lock(&lkcf->mutex); + lkcf->running = false; + qemu_mutex_unlock(&lkcf->mutex); + qemu_cond_broadcast(&lkcf->cond); + + close(lkcf->eventfd); + for (i = 0; i < NR_WORKER_THREAD; i++) { + qemu_thread_join(&lkcf->worker_threads[i]); + } + + QSIMPLEQ_FOREACH_SAFE(task, &lkcf->requests, queue, next) { + if (task->cb) { + task->cb(task->opaque, task->status); + } + g_free(task); + } + + QSIMPLEQ_FOREACH_SAFE(task, &lkcf->responses, queue, next) { + if (task->cb) { + task->cb(task->opaque, task->status); + } + g_free(task); + } + + qemu_mutex_destroy(&lkcf->mutex); + qemu_cond_destroy(&lkcf->cond); + qemu_mutex_destroy(&lkcf->rsp_mutex); + + for (i = 0; i < MAX_SESSIONS; i++) { + if (lkcf->sess[i] != NULL) { + cryptodev_lkcf_close_session(backend, i, 0, NULL, NULL); + } + } + + for (i = 0; i < queues; i++) { + cc = backend->conf.peers.ccs[i]; + if (cc) { + cryptodev_backend_free_client(cc); + backend->conf.peers.ccs[i] = NULL; + } + } + + cryptodev_backend_set_ready(backend, false); +} + +static void cryptodev_lkcf_execute_task(CryptoDevLKCFTask *task) +{ + CryptoDevBackendLKCFSession *session = task->sess; + CryptoDevBackendAsymOpInfo *asym_op_info; + bool kick = false; + int ret, status, op_code = task->op_info->op_code; + size_t p8info_len; + g_autofree uint8_t *p8info = NULL; + Error *local_error = NULL; + key_serial_t key_id = INVALID_KEY_ID; + char op_desc[64]; + g_autoptr(QCryptoAkCipher) akcipher = NULL; + + /** + * We only offload private key session: + * 1. currently, the Linux kernel can only accept public key wrapped + * with X.509 certificates, but unfortunately the cost of making a + * ceritificate with public key is too expensive. + * 2. generally, public key related compution is fast, just compute it with + * thread-pool. + */ + if (session->keytype == QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE) { + if (qcrypto_akcipher_export_p8info(&session->akcipher_opts, + session->key, session->keylen, + &p8info, &p8info_len, + &local_error) != 0 || + cryptodev_lkcf_set_op_desc(&session->akcipher_opts, op_desc, + sizeof(op_desc), &local_error) != 0) { + error_report_err(local_error); + } else { + key_id = add_key(KCTL_KEY_TYPE_PKEY, "lkcf-backend-priv-key", + p8info, p8info_len, KCTL_KEY_RING); + } + } + + if (key_id < 0) { + if (!qcrypto_akcipher_supports(&session->akcipher_opts)) { + status = -VIRTIO_CRYPTO_NOTSUPP; + goto out; + } + akcipher = qcrypto_akcipher_new(&session->akcipher_opts, + session->keytype, + session->key, session->keylen, + &local_error); + if (!akcipher) { + status = -VIRTIO_CRYPTO_ERR; + goto out; + } + } + + asym_op_info = task->op_info->u.asym_op_info; + switch (op_code) { + case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT: + if (key_id >= 0) { + ret = keyctl_pkey_encrypt(key_id, op_desc, + asym_op_info->src, asym_op_info->src_len, + asym_op_info->dst, asym_op_info->dst_len); + } else { + ret = qcrypto_akcipher_encrypt(akcipher, + asym_op_info->src, asym_op_info->src_len, + asym_op_info->dst, asym_op_info->dst_len, &local_error); + } + break; + + case VIRTIO_CRYPTO_AKCIPHER_DECRYPT: + if (key_id >= 0) { + ret = keyctl_pkey_decrypt(key_id, op_desc, + asym_op_info->src, asym_op_info->src_len, + asym_op_info->dst, asym_op_info->dst_len); + } else { + ret = qcrypto_akcipher_decrypt(akcipher, + asym_op_info->src, asym_op_info->src_len, + asym_op_info->dst, asym_op_info->dst_len, &local_error); + } + break; + + case VIRTIO_CRYPTO_AKCIPHER_SIGN: + if (key_id >= 0) { + ret = keyctl_pkey_sign(key_id, op_desc, + asym_op_info->src, asym_op_info->src_len, + asym_op_info->dst, asym_op_info->dst_len); + } else { + ret = qcrypto_akcipher_sign(akcipher, + asym_op_info->src, asym_op_info->src_len, + asym_op_info->dst, asym_op_info->dst_len, &local_error); + } + break; + + case VIRTIO_CRYPTO_AKCIPHER_VERIFY: + if (key_id >= 0) { + ret = keyctl_pkey_verify(key_id, op_desc, + asym_op_info->src, asym_op_info->src_len, + asym_op_info->dst, asym_op_info->dst_len); + } else { + ret = qcrypto_akcipher_verify(akcipher, + asym_op_info->src, asym_op_info->src_len, + asym_op_info->dst, asym_op_info->dst_len, &local_error); + } + break; + + default: + error_setg(&local_error, "Unknown opcode: %u", op_code); + status = -VIRTIO_CRYPTO_ERR; + goto out; + } + + if (ret < 0) { + if (!local_error) { + if (errno != EKEYREJECTED) { + error_report("Failed do operation with keyctl: %d", errno); + } + } else { + error_report_err(local_error); + } + status = op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY ? + -VIRTIO_CRYPTO_KEY_REJECTED : -VIRTIO_CRYPTO_ERR; + } else { + status = VIRTIO_CRYPTO_OK; + asym_op_info->dst_len = ret; + } + +out: + if (key_id >= 0) { + keyctl_unlink(key_id, KCTL_KEY_RING); + } + task->status = status; + + qemu_mutex_lock(&task->lkcf->rsp_mutex); + if (QSIMPLEQ_EMPTY(&task->lkcf->responses)) { + kick = true; + } + QSIMPLEQ_INSERT_TAIL(&task->lkcf->responses, task, queue); + qemu_mutex_unlock(&task->lkcf->rsp_mutex); + + if (kick) { + eventfd_write(task->lkcf->eventfd, 1); + } +} + +static void *cryptodev_lkcf_worker(void *arg) +{ + CryptoDevBackendLKCF *backend = (CryptoDevBackendLKCF *)arg; + CryptoDevLKCFTask *task; + + for (;;) { + task = NULL; + qemu_mutex_lock(&backend->mutex); + while (backend->running && QSIMPLEQ_EMPTY(&backend->requests)) { + qemu_cond_wait(&backend->cond, &backend->mutex); + } + if (backend->running) { + task = QSIMPLEQ_FIRST(&backend->requests); + QSIMPLEQ_REMOVE_HEAD(&backend->requests, queue); + } + qemu_mutex_unlock(&backend->mutex); + + /* stopped */ + if (!task) { + break; + } + cryptodev_lkcf_execute_task(task); + } + + return NULL; +} + +static int cryptodev_lkcf_operation( + CryptoDevBackend *backend, + CryptoDevBackendOpInfo *op_info) +{ + CryptoDevBackendLKCF *lkcf = + CRYPTODEV_BACKEND_LKCF(backend); + CryptoDevBackendLKCFSession *sess; + QCryptodevBackendAlgType algtype = op_info->algtype; + CryptoDevLKCFTask *task; + + if (op_info->session_id >= MAX_SESSIONS || + lkcf->sess[op_info->session_id] == NULL) { + error_report("Cannot find a valid session id: %" PRIu64 "", + op_info->session_id); + return -VIRTIO_CRYPTO_INVSESS; + } + + sess = lkcf->sess[op_info->session_id]; + if (algtype != QCRYPTODEV_BACKEND_ALG_ASYM) { + error_report("algtype not supported: %u", algtype); + return -VIRTIO_CRYPTO_NOTSUPP; + } + + task = g_new0(CryptoDevLKCFTask, 1); + task->op_info = op_info; + task->cb = op_info->cb; + task->opaque = op_info->opaque; + task->sess = sess; + task->lkcf = lkcf; + task->status = -VIRTIO_CRYPTO_ERR; + + qemu_mutex_lock(&lkcf->mutex); + QSIMPLEQ_INSERT_TAIL(&lkcf->requests, task, queue); + qemu_mutex_unlock(&lkcf->mutex); + qemu_cond_signal(&lkcf->cond); + + return VIRTIO_CRYPTO_OK; +} + +static int cryptodev_lkcf_create_asym_session( + CryptoDevBackendLKCF *lkcf, + CryptoDevBackendAsymSessionInfo *sess_info, + uint64_t *session_id) +{ + Error *local_error = NULL; + int index; + g_autofree CryptoDevBackendLKCFSession *sess = + g_new0(CryptoDevBackendLKCFSession, 1); + + switch (sess_info->algo) { + case VIRTIO_CRYPTO_AKCIPHER_RSA: + sess->akcipher_opts.alg = QCRYPTO_AKCIPHER_ALG_RSA; + if (cryptodev_lkcf_set_rsa_opt( + sess_info->u.rsa.padding_algo, sess_info->u.rsa.hash_algo, + &sess->akcipher_opts.u.rsa, &local_error) != 0) { + error_report_err(local_error); + return -VIRTIO_CRYPTO_ERR; + } + break; + + default: + error_report("Unsupported asym alg %u", sess_info->algo); + return -VIRTIO_CRYPTO_NOTSUPP; + } + + switch (sess_info->keytype) { + case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC: + sess->keytype = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC; + break; + + case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE: + sess->keytype = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE; + break; + + default: + error_report("Unknown akcipher keytype: %u", sess_info->keytype); + return -VIRTIO_CRYPTO_ERR; + } + + index = cryptodev_lkcf_get_unused_session_index(lkcf); + if (index < 0) { + error_report("Total number of sessions created exceeds %u", + MAX_SESSIONS); + return -VIRTIO_CRYPTO_ERR; + } + + sess->keylen = sess_info->keylen; + sess->key = g_malloc(sess_info->keylen); + memcpy(sess->key, sess_info->key, sess_info->keylen); + + lkcf->sess[index] = g_steal_pointer(&sess); + *session_id = index; + + return VIRTIO_CRYPTO_OK; +} + +static int cryptodev_lkcf_create_session( + CryptoDevBackend *backend, + CryptoDevBackendSessionInfo *sess_info, + uint32_t queue_index, + CryptoDevCompletionFunc cb, + void *opaque) +{ + CryptoDevBackendAsymSessionInfo *asym_sess_info; + CryptoDevBackendLKCF *lkcf = + CRYPTODEV_BACKEND_LKCF(backend); + int ret; + + switch (sess_info->op_code) { + case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION: + asym_sess_info = &sess_info->u.asym_sess_info; + ret = cryptodev_lkcf_create_asym_session( + lkcf, asym_sess_info, &sess_info->session_id); + break; + + default: + ret = -VIRTIO_CRYPTO_NOTSUPP; + error_report("Unsupported opcode: %" PRIu32 "", + sess_info->op_code); + break; + } + if (cb) { + cb(opaque, ret); + } + return 0; +} + +static int cryptodev_lkcf_close_session(CryptoDevBackend *backend, + uint64_t session_id, + uint32_t queue_index, + CryptoDevCompletionFunc cb, + void *opaque) +{ + CryptoDevBackendLKCF *lkcf = CRYPTODEV_BACKEND_LKCF(backend); + CryptoDevBackendLKCFSession *session; + + assert(session_id < MAX_SESSIONS && lkcf->sess[session_id]); + session = lkcf->sess[session_id]; + lkcf->sess[session_id] = NULL; + + g_free(session->key); + g_free(session); + + if (cb) { + cb(opaque, VIRTIO_CRYPTO_OK); + } + return 0; +} + +static void cryptodev_lkcf_class_init(ObjectClass *oc, void *data) +{ + CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc); + + bc->init = cryptodev_lkcf_init; + bc->cleanup = cryptodev_lkcf_cleanup; + bc->create_session = cryptodev_lkcf_create_session; + bc->close_session = cryptodev_lkcf_close_session; + bc->do_op = cryptodev_lkcf_operation; +} + +static const TypeInfo cryptodev_builtin_info = { + .name = TYPE_CRYPTODEV_BACKEND_LKCF, + .parent = TYPE_CRYPTODEV_BACKEND, + .class_init = cryptodev_lkcf_class_init, + .instance_size = sizeof(CryptoDevBackendLKCF), +}; + +static void cryptodev_lkcf_register_types(void) +{ + type_register_static(&cryptodev_builtin_info); +} + +type_init(cryptodev_lkcf_register_types); diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c index bedb452474..c3283ba84a 100644 --- a/backends/cryptodev-vhost-user.c +++ b/backends/cryptodev-vhost-user.c @@ -67,7 +67,7 @@ cryptodev_vhost_user_get_vhost( { CryptoDevBackendVhostUser *s = CRYPTODEV_BACKEND_VHOST_USER(b); - assert(cc->type == CRYPTODEV_BACKEND_TYPE_VHOST_USER); + assert(cc->type == QCRYPTODEV_BACKEND_TYPE_VHOST_USER); assert(queue < MAX_CRYPTO_QUEUE_NUM); return s->vhost_crypto[queue]; @@ -198,12 +198,11 @@ static void cryptodev_vhost_user_init( s->opened = true; for (i = 0; i < queues; i++) { - cc = cryptodev_backend_new_client( - "cryptodev-vhost-user", NULL); + cc = cryptodev_backend_new_client(); cc->info_str = g_strdup_printf("cryptodev-vhost-user%zu to %s ", i, chr->label); cc->queue_index = i; - cc->type = CRYPTODEV_BACKEND_TYPE_VHOST_USER; + cc->type = QCRYPTODEV_BACKEND_TYPE_VHOST_USER; backend->conf.peers.ccs[i] = cc; @@ -222,9 +221,9 @@ static void cryptodev_vhost_user_init( cryptodev_vhost_user_event, NULL, s, NULL, true); backend->conf.crypto_services = - 1u << VIRTIO_CRYPTO_SERVICE_CIPHER | - 1u << VIRTIO_CRYPTO_SERVICE_HASH | - 1u << VIRTIO_CRYPTO_SERVICE_MAC; + 1u << QCRYPTODEV_BACKEND_SERVICE_CIPHER | + 1u << QCRYPTODEV_BACKEND_SERVICE_HASH | + 1u << QCRYPTODEV_BACKEND_SERVICE_MAC; backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC; backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1; @@ -233,9 +232,9 @@ static void cryptodev_vhost_user_init( backend->conf.max_auth_key_len = VHOST_USER_MAX_AUTH_KEY_LEN; } -static int64_t cryptodev_vhost_user_sym_create_session( +static int64_t cryptodev_vhost_user_crypto_create_session( CryptoDevBackend *backend, - CryptoDevBackendSymSessionInfo *sess_info, + CryptoDevBackendSessionInfo *sess_info, uint32_t queue_index, Error **errp) { CryptoDevBackendClient *cc = @@ -259,15 +258,60 @@ static int64_t cryptodev_vhost_user_sym_create_session( return -1; } -static int cryptodev_vhost_user_sym_close_session( +static int cryptodev_vhost_user_create_session( + CryptoDevBackend *backend, + CryptoDevBackendSessionInfo *sess_info, + uint32_t queue_index, + CryptoDevCompletionFunc cb, + void *opaque) +{ + uint32_t op_code = sess_info->op_code; + int64_t ret; + Error *local_error = NULL; + int status; + + switch (op_code) { + case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: + case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION: + case VIRTIO_CRYPTO_HASH_CREATE_SESSION: + case VIRTIO_CRYPTO_MAC_CREATE_SESSION: + case VIRTIO_CRYPTO_AEAD_CREATE_SESSION: + ret = cryptodev_vhost_user_crypto_create_session(backend, sess_info, + queue_index, &local_error); + break; + + default: + error_setg(&local_error, "Unsupported opcode :%" PRIu32 "", + sess_info->op_code); + return -VIRTIO_CRYPTO_NOTSUPP; + } + + if (local_error) { + error_report_err(local_error); + } + if (ret < 0) { + status = -VIRTIO_CRYPTO_ERR; + } else { + sess_info->session_id = ret; + status = VIRTIO_CRYPTO_OK; + } + if (cb) { + cb(opaque, status); + } + return 0; +} + +static int cryptodev_vhost_user_close_session( CryptoDevBackend *backend, uint64_t session_id, - uint32_t queue_index, Error **errp) + uint32_t queue_index, + CryptoDevCompletionFunc cb, + void *opaque) { CryptoDevBackendClient *cc = backend->conf.peers.ccs[queue_index]; CryptoDevBackendVhost *vhost_crypto; - int ret; + int ret = -1, status; vhost_crypto = cryptodev_vhost_user_get_vhost(cc, backend, queue_index); if (vhost_crypto) { @@ -275,12 +319,17 @@ static int cryptodev_vhost_user_sym_close_session( ret = dev->vhost_ops->vhost_crypto_close_session(dev, session_id); if (ret < 0) { - return -1; + status = -VIRTIO_CRYPTO_ERR; } else { - return 0; + status = VIRTIO_CRYPTO_OK; } + } else { + status = -VIRTIO_CRYPTO_NOTSUPP; } - return -1; + if (cb) { + cb(opaque, status); + } + return 0; } static void cryptodev_vhost_user_cleanup( @@ -313,7 +362,7 @@ static void cryptodev_vhost_user_set_chardev(Object *obj, CRYPTODEV_BACKEND_VHOST_USER(obj); if (s->opened) { - error_setg(errp, QERR_PERMISSION_DENIED); + error_setg(errp, "Property 'chardev' can no longer be set"); } else { g_free(s->chr_name); s->chr_name = g_strdup(value); @@ -351,9 +400,9 @@ cryptodev_vhost_user_class_init(ObjectClass *oc, void *data) bc->init = cryptodev_vhost_user_init; bc->cleanup = cryptodev_vhost_user_cleanup; - bc->create_session = cryptodev_vhost_user_sym_create_session; - bc->close_session = cryptodev_vhost_user_sym_close_session; - bc->do_sym_op = NULL; + bc->create_session = cryptodev_vhost_user_create_session; + bc->close_session = cryptodev_vhost_user_close_session; + bc->do_op = NULL; object_class_property_add_str(oc, "chardev", cryptodev_vhost_user_get_chardev, diff --git a/backends/cryptodev-vhost.c b/backends/cryptodev-vhost.c index bc13e466b4..93523732f3 100644 --- a/backends/cryptodev-vhost.c +++ b/backends/cryptodev-vhost.c @@ -28,7 +28,6 @@ #ifdef CONFIG_VHOST_CRYPTO #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "hw/virtio/virtio-crypto.h" #include "sysemu/cryptodev-vhost-user.h" @@ -94,7 +93,7 @@ cryptodev_vhost_start_one(CryptoDevBackendVhost *crypto, goto fail_notifiers; } - r = vhost_dev_start(&crypto->dev, dev); + r = vhost_dev_start(&crypto->dev, dev, false); if (r < 0) { goto fail_start; } @@ -111,7 +110,7 @@ static void cryptodev_vhost_stop_one(CryptoDevBackendVhost *crypto, VirtIODevice *dev) { - vhost_dev_stop(&crypto->dev, dev); + vhost_dev_stop(&crypto->dev, dev, false); vhost_dev_disable_notifiers(&crypto->dev, dev); } @@ -128,7 +127,7 @@ cryptodev_get_vhost(CryptoDevBackendClient *cc, switch (cc->type) { #if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX) - case CRYPTODEV_BACKEND_TYPE_VHOST_USER: + case QCRYPTODEV_BACKEND_TYPE_VHOST_USER: vhost_crypto = cryptodev_vhost_user_get_vhost(cc, b, queue); break; #endif @@ -196,7 +195,7 @@ int cryptodev_vhost_start(VirtIODevice *dev, int total_queues) * because vhost user doesn't interrupt masking/unmasking * properly. */ - if (cc->type == CRYPTODEV_BACKEND_TYPE_VHOST_USER) { + if (cc->type == QCRYPTODEV_BACKEND_TYPE_VHOST_USER) { dev->use_guest_notifier_mask = false; } } diff --git a/backends/cryptodev.c b/backends/cryptodev.c index 2b105e433c..fff89fd62a 100644 --- a/backends/cryptodev.c +++ b/backends/cryptodev.c @@ -23,28 +23,92 @@ #include "qemu/osdep.h" #include "sysemu/cryptodev.h" +#include "sysemu/stats.h" #include "qapi/error.h" +#include "qapi/qapi-commands-cryptodev.h" +#include "qapi/qapi-types-stats.h" #include "qapi/visitor.h" #include "qemu/config-file.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" #include "qom/object_interfaces.h" #include "hw/virtio/virtio-crypto.h" +#define SYM_ENCRYPT_OPS_STR "sym-encrypt-ops" +#define SYM_DECRYPT_OPS_STR "sym-decrypt-ops" +#define SYM_ENCRYPT_BYTES_STR "sym-encrypt-bytes" +#define SYM_DECRYPT_BYTES_STR "sym-decrypt-bytes" + +#define ASYM_ENCRYPT_OPS_STR "asym-encrypt-ops" +#define ASYM_DECRYPT_OPS_STR "asym-decrypt-ops" +#define ASYM_SIGN_OPS_STR "asym-sign-ops" +#define ASYM_VERIFY_OPS_STR "asym-verify-ops" +#define ASYM_ENCRYPT_BYTES_STR "asym-encrypt-bytes" +#define ASYM_DECRYPT_BYTES_STR "asym-decrypt-bytes" +#define ASYM_SIGN_BYTES_STR "asym-sign-bytes" +#define ASYM_VERIFY_BYTES_STR "asym-verify-bytes" + +typedef struct StatsArgs { + union StatsResultsType { + StatsResultList **stats; + StatsSchemaList **schema; + } result; + strList *names; + Error **errp; +} StatsArgs; static QTAILQ_HEAD(, CryptoDevBackendClient) crypto_clients; +static int qmp_query_cryptodev_foreach(Object *obj, void *data) +{ + CryptoDevBackend *backend; + QCryptodevInfoList **infolist = data; + uint32_t services, i; -CryptoDevBackendClient * -cryptodev_backend_new_client(const char *model, - const char *name) + if (!object_dynamic_cast(obj, TYPE_CRYPTODEV_BACKEND)) { + return 0; + } + + QCryptodevInfo *info = g_new0(QCryptodevInfo, 1); + info->id = g_strdup(object_get_canonical_path_component(obj)); + + backend = CRYPTODEV_BACKEND(obj); + services = backend->conf.crypto_services; + for (i = 0; i < QCRYPTODEV_BACKEND_SERVICE__MAX; i++) { + if (services & (1 << i)) { + QAPI_LIST_PREPEND(info->service, i); + } + } + + for (i = 0; i < backend->conf.peers.queues; i++) { + CryptoDevBackendClient *cc = backend->conf.peers.ccs[i]; + QCryptodevBackendClient *client = g_new0(QCryptodevBackendClient, 1); + + client->queue = cc->queue_index; + client->type = cc->type; + QAPI_LIST_PREPEND(info->client, client); + } + + QAPI_LIST_PREPEND(*infolist, info); + + return 0; +} + +QCryptodevInfoList *qmp_query_cryptodev(Error **errp) +{ + QCryptodevInfoList *list = NULL; + Object *objs = container_get(object_get_root(), "/objects"); + + object_child_foreach(objs, qmp_query_cryptodev_foreach, &list); + + return list; +} + +CryptoDevBackendClient *cryptodev_backend_new_client(void) { CryptoDevBackendClient *cc; cc = g_new0(CryptoDevBackendClient, 1); - cc->model = g_strdup(model); - if (name) { - cc->name = g_strdup(name); - } - QTAILQ_INSERT_TAIL(&crypto_clients, cc, next); return cc; @@ -54,8 +118,6 @@ void cryptodev_backend_free_client( CryptoDevBackendClient *cc) { QTAILQ_REMOVE(&crypto_clients, cc, next); - g_free(cc->name); - g_free(cc->model); g_free(cc->info_str); g_free(cc); } @@ -70,73 +132,161 @@ void cryptodev_backend_cleanup( if (bc->cleanup) { bc->cleanup(backend, errp); } + + g_free(backend->sym_stat); + g_free(backend->asym_stat); } -int64_t cryptodev_backend_sym_create_session( +int cryptodev_backend_create_session( CryptoDevBackend *backend, - CryptoDevBackendSymSessionInfo *sess_info, - uint32_t queue_index, Error **errp) + CryptoDevBackendSessionInfo *sess_info, + uint32_t queue_index, + CryptoDevCompletionFunc cb, + void *opaque) { CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(backend); if (bc->create_session) { - return bc->create_session(backend, sess_info, queue_index, errp); + return bc->create_session(backend, sess_info, queue_index, cb, opaque); } - - return -1; + return -VIRTIO_CRYPTO_NOTSUPP; } -int cryptodev_backend_sym_close_session( +int cryptodev_backend_close_session( CryptoDevBackend *backend, uint64_t session_id, - uint32_t queue_index, Error **errp) + uint32_t queue_index, + CryptoDevCompletionFunc cb, + void *opaque) { CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(backend); if (bc->close_session) { - return bc->close_session(backend, session_id, queue_index, errp); + return bc->close_session(backend, session_id, queue_index, cb, opaque); } - - return -1; + return -VIRTIO_CRYPTO_NOTSUPP; } -static int cryptodev_backend_sym_operation( +static int cryptodev_backend_operation( CryptoDevBackend *backend, - CryptoDevBackendSymOpInfo *op_info, - uint32_t queue_index, Error **errp) + CryptoDevBackendOpInfo *op_info) { CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(backend); - if (bc->do_sym_op) { - return bc->do_sym_op(backend, op_info, queue_index, errp); + if (bc->do_op) { + return bc->do_op(backend, op_info); + } + return -VIRTIO_CRYPTO_NOTSUPP; +} + +static int cryptodev_backend_account(CryptoDevBackend *backend, + CryptoDevBackendOpInfo *op_info) +{ + enum QCryptodevBackendAlgType algtype = op_info->algtype; + int len; + + if (algtype == QCRYPTODEV_BACKEND_ALG_ASYM) { + CryptoDevBackendAsymOpInfo *asym_op_info = op_info->u.asym_op_info; + len = asym_op_info->src_len; + + if (unlikely(!backend->asym_stat)) { + error_report("cryptodev: Unexpected asym operation"); + return -VIRTIO_CRYPTO_NOTSUPP; + } + switch (op_info->op_code) { + case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT: + CryptodevAsymStatIncEncrypt(backend, len); + break; + case VIRTIO_CRYPTO_AKCIPHER_DECRYPT: + CryptodevAsymStatIncDecrypt(backend, len); + break; + case VIRTIO_CRYPTO_AKCIPHER_SIGN: + CryptodevAsymStatIncSign(backend, len); + break; + case VIRTIO_CRYPTO_AKCIPHER_VERIFY: + CryptodevAsymStatIncVerify(backend, len); + break; + default: + return -VIRTIO_CRYPTO_NOTSUPP; + } + } else if (algtype == QCRYPTODEV_BACKEND_ALG_SYM) { + CryptoDevBackendSymOpInfo *sym_op_info = op_info->u.sym_op_info; + len = sym_op_info->src_len; + + if (unlikely(!backend->sym_stat)) { + error_report("cryptodev: Unexpected sym operation"); + return -VIRTIO_CRYPTO_NOTSUPP; + } + switch (op_info->op_code) { + case VIRTIO_CRYPTO_CIPHER_ENCRYPT: + CryptodevSymStatIncEncrypt(backend, len); + break; + case VIRTIO_CRYPTO_CIPHER_DECRYPT: + CryptodevSymStatIncDecrypt(backend, len); + break; + default: + return -VIRTIO_CRYPTO_NOTSUPP; + } + } else { + error_report("Unsupported cryptodev alg type: %" PRIu32 "", algtype); + return -VIRTIO_CRYPTO_NOTSUPP; } - return -VIRTIO_CRYPTO_ERR; + return len; +} + +static void cryptodev_backend_throttle_timer_cb(void *opaque) +{ + CryptoDevBackend *backend = (CryptoDevBackend *)opaque; + CryptoDevBackendOpInfo *op_info, *tmpop; + int ret; + + QTAILQ_FOREACH_SAFE(op_info, &backend->opinfos, next, tmpop) { + QTAILQ_REMOVE(&backend->opinfos, op_info, next); + ret = cryptodev_backend_account(backend, op_info); + if (ret < 0) { + op_info->cb(op_info->opaque, ret); + continue; + } + + throttle_account(&backend->ts, THROTTLE_WRITE, ret); + cryptodev_backend_operation(backend, op_info); + if (throttle_enabled(&backend->tc) && + throttle_schedule_timer(&backend->ts, &backend->tt, + THROTTLE_WRITE)) { + break; + } + } } int cryptodev_backend_crypto_operation( CryptoDevBackend *backend, - void *opaque, - uint32_t queue_index, Error **errp) + CryptoDevBackendOpInfo *op_info) { - VirtIOCryptoReq *req = opaque; + int ret; - if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) { - CryptoDevBackendSymOpInfo *op_info; - op_info = req->u.sym_op_info; - - return cryptodev_backend_sym_operation(backend, - op_info, queue_index, errp); - } else { - error_setg(errp, "Unsupported cryptodev alg type: %" PRIu32 "", - req->flags); - return -VIRTIO_CRYPTO_NOTSUPP; + if (!throttle_enabled(&backend->tc)) { + goto do_account; } - return -VIRTIO_CRYPTO_ERR; + if (throttle_schedule_timer(&backend->ts, &backend->tt, THROTTLE_WRITE) || + !QTAILQ_EMPTY(&backend->opinfos)) { + QTAILQ_INSERT_TAIL(&backend->opinfos, op_info, next); + return 0; + } + +do_account: + ret = cryptodev_backend_account(backend, op_info); + if (ret < 0) { + return ret; + } + + throttle_account(&backend->ts, THROTTLE_WRITE, ret); + + return cryptodev_backend_operation(backend, op_info); } static void @@ -167,14 +317,119 @@ cryptodev_backend_set_queues(Object *obj, Visitor *v, const char *name, backend->conf.peers.queues = value; } +static void cryptodev_backend_set_throttle(CryptoDevBackend *backend, int field, + uint64_t value, Error **errp) +{ + uint64_t orig = backend->tc.buckets[field].avg; + bool enabled = throttle_enabled(&backend->tc); + + if (orig == value) { + return; + } + + backend->tc.buckets[field].avg = value; + if (!throttle_enabled(&backend->tc)) { + throttle_timers_destroy(&backend->tt); + cryptodev_backend_throttle_timer_cb(backend); /* drain opinfos */ + return; + } + + if (!throttle_is_valid(&backend->tc, errp)) { + backend->tc.buckets[field].avg = orig; /* revert change */ + return; + } + + if (!enabled) { + throttle_init(&backend->ts); + throttle_timers_init(&backend->tt, qemu_get_aio_context(), + QEMU_CLOCK_REALTIME, NULL, + cryptodev_backend_throttle_timer_cb, backend); + } + + throttle_config(&backend->ts, QEMU_CLOCK_REALTIME, &backend->tc); +} + +static void cryptodev_backend_get_bps(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); + uint64_t value = backend->tc.buckets[THROTTLE_BPS_TOTAL].avg; + + visit_type_uint64(v, name, &value, errp); +} + +static void cryptodev_backend_set_bps(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); + uint64_t value; + + if (!visit_type_uint64(v, name, &value, errp)) { + return; + } + + cryptodev_backend_set_throttle(backend, THROTTLE_BPS_TOTAL, value, errp); +} + +static void cryptodev_backend_get_ops(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); + uint64_t value = backend->tc.buckets[THROTTLE_OPS_TOTAL].avg; + + visit_type_uint64(v, name, &value, errp); +} + +static void cryptodev_backend_set_ops(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); + uint64_t value; + + if (!visit_type_uint64(v, name, &value, errp)) { + return; + } + + cryptodev_backend_set_throttle(backend, THROTTLE_OPS_TOTAL, value, errp); +} + static void cryptodev_backend_complete(UserCreatable *uc, Error **errp) { + ERRP_GUARD(); CryptoDevBackend *backend = CRYPTODEV_BACKEND(uc); CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(uc); + uint32_t services; + uint64_t value; + + QTAILQ_INIT(&backend->opinfos); + value = backend->tc.buckets[THROTTLE_OPS_TOTAL].avg; + cryptodev_backend_set_throttle(backend, THROTTLE_OPS_TOTAL, value, errp); + if (*errp) { + return; + } + value = backend->tc.buckets[THROTTLE_BPS_TOTAL].avg; + cryptodev_backend_set_throttle(backend, THROTTLE_BPS_TOTAL, value, errp); + if (*errp) { + return; + } if (bc->init) { bc->init(backend, errp); + if (*errp) { + return; + } + } + + services = backend->conf.crypto_services; + if (services & (1 << QCRYPTODEV_BACKEND_SERVICE_CIPHER)) { + backend->sym_stat = g_new0(CryptodevBackendSymStat, 1); + } + + if (services & (1 << QCRYPTODEV_BACKEND_SERVICE_AKCIPHER)) { + backend->asym_stat = g_new0(CryptodevBackendAsymStat, 1); } } @@ -206,8 +461,12 @@ cryptodev_backend_can_be_deleted(UserCreatable *uc) static void cryptodev_backend_instance_init(Object *obj) { + CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); + /* Initialize devices' queues property to 1 */ object_property_set_int(obj, "queues", 1, NULL); + + throttle_config_init(&backend->tc); } static void cryptodev_backend_finalize(Object *obj) @@ -215,6 +474,137 @@ static void cryptodev_backend_finalize(Object *obj) CryptoDevBackend *backend = CRYPTODEV_BACKEND(obj); cryptodev_backend_cleanup(backend, NULL); + if (throttle_enabled(&backend->tc)) { + throttle_timers_destroy(&backend->tt); + } +} + +static StatsList *cryptodev_backend_stats_add(const char *name, int64_t *val, + StatsList *stats_list) +{ + Stats *stats = g_new0(Stats, 1); + + stats->name = g_strdup(name); + stats->value = g_new0(StatsValue, 1); + stats->value->type = QTYPE_QNUM; + stats->value->u.scalar = *val; + + QAPI_LIST_PREPEND(stats_list, stats); + return stats_list; +} + +static int cryptodev_backend_stats_query(Object *obj, void *data) +{ + StatsArgs *stats_args = data; + StatsResultList **stats_results = stats_args->result.stats; + StatsList *stats_list = NULL; + StatsResult *entry; + CryptoDevBackend *backend; + CryptodevBackendSymStat *sym_stat; + CryptodevBackendAsymStat *asym_stat; + + if (!object_dynamic_cast(obj, TYPE_CRYPTODEV_BACKEND)) { + return 0; + } + + backend = CRYPTODEV_BACKEND(obj); + sym_stat = backend->sym_stat; + if (sym_stat) { + stats_list = cryptodev_backend_stats_add(SYM_ENCRYPT_OPS_STR, + &sym_stat->encrypt_ops, stats_list); + stats_list = cryptodev_backend_stats_add(SYM_DECRYPT_OPS_STR, + &sym_stat->decrypt_ops, stats_list); + stats_list = cryptodev_backend_stats_add(SYM_ENCRYPT_BYTES_STR, + &sym_stat->encrypt_bytes, stats_list); + stats_list = cryptodev_backend_stats_add(SYM_DECRYPT_BYTES_STR, + &sym_stat->decrypt_bytes, stats_list); + } + + asym_stat = backend->asym_stat; + if (asym_stat) { + stats_list = cryptodev_backend_stats_add(ASYM_ENCRYPT_OPS_STR, + &asym_stat->encrypt_ops, stats_list); + stats_list = cryptodev_backend_stats_add(ASYM_DECRYPT_OPS_STR, + &asym_stat->decrypt_ops, stats_list); + stats_list = cryptodev_backend_stats_add(ASYM_SIGN_OPS_STR, + &asym_stat->sign_ops, stats_list); + stats_list = cryptodev_backend_stats_add(ASYM_VERIFY_OPS_STR, + &asym_stat->verify_ops, stats_list); + stats_list = cryptodev_backend_stats_add(ASYM_ENCRYPT_BYTES_STR, + &asym_stat->encrypt_bytes, stats_list); + stats_list = cryptodev_backend_stats_add(ASYM_DECRYPT_BYTES_STR, + &asym_stat->decrypt_bytes, stats_list); + stats_list = cryptodev_backend_stats_add(ASYM_SIGN_BYTES_STR, + &asym_stat->sign_bytes, stats_list); + stats_list = cryptodev_backend_stats_add(ASYM_VERIFY_BYTES_STR, + &asym_stat->verify_bytes, stats_list); + } + + entry = g_new0(StatsResult, 1); + entry->provider = STATS_PROVIDER_CRYPTODEV; + entry->qom_path = object_get_canonical_path(obj); + entry->stats = stats_list; + QAPI_LIST_PREPEND(*stats_results, entry); + + return 0; +} + +static void cryptodev_backend_stats_cb(StatsResultList **result, + StatsTarget target, + strList *names, strList *targets, + Error **errp) +{ + switch (target) { + case STATS_TARGET_CRYPTODEV: + { + Object *objs = container_get(object_get_root(), "/objects"); + StatsArgs stats_args; + stats_args.result.stats = result; + stats_args.names = names; + stats_args.errp = errp; + + object_child_foreach(objs, cryptodev_backend_stats_query, &stats_args); + break; + } + default: + break; + } +} + +static StatsSchemaValueList *cryptodev_backend_schemas_add(const char *name, + StatsSchemaValueList *list) +{ + StatsSchemaValueList *schema_entry = g_new0(StatsSchemaValueList, 1); + + schema_entry->value = g_new0(StatsSchemaValue, 1); + schema_entry->value->type = STATS_TYPE_CUMULATIVE; + schema_entry->value->name = g_strdup(name); + schema_entry->next = list; + + return schema_entry; +} + +static void cryptodev_backend_schemas_cb(StatsSchemaList **result, + Error **errp) +{ + StatsSchemaValueList *stats_list = NULL; + const char *sym_stats[] = { SYM_ENCRYPT_OPS_STR, SYM_DECRYPT_OPS_STR, + SYM_ENCRYPT_BYTES_STR, SYM_DECRYPT_BYTES_STR }; + const char *asym_stats[] = { ASYM_ENCRYPT_OPS_STR, ASYM_DECRYPT_OPS_STR, + ASYM_SIGN_OPS_STR, ASYM_VERIFY_OPS_STR, + ASYM_ENCRYPT_BYTES_STR, ASYM_DECRYPT_BYTES_STR, + ASYM_SIGN_BYTES_STR, ASYM_VERIFY_BYTES_STR }; + + for (int i = 0; i < ARRAY_SIZE(sym_stats); i++) { + stats_list = cryptodev_backend_schemas_add(sym_stats[i], stats_list); + } + + for (int i = 0; i < ARRAY_SIZE(asym_stats); i++) { + stats_list = cryptodev_backend_schemas_add(asym_stats[i], stats_list); + } + + add_stats_schema(result, STATS_PROVIDER_CRYPTODEV, STATS_TARGET_CRYPTODEV, + stats_list); } static void @@ -230,6 +620,17 @@ cryptodev_backend_class_init(ObjectClass *oc, void *data) cryptodev_backend_get_queues, cryptodev_backend_set_queues, NULL, NULL); + object_class_property_add(oc, "throttle-bps", "uint64", + cryptodev_backend_get_bps, + cryptodev_backend_set_bps, + NULL, NULL); + object_class_property_add(oc, "throttle-ops", "uint64", + cryptodev_backend_get_ops, + cryptodev_backend_set_ops, + NULL, NULL); + + add_stats_callbacks(STATS_PROVIDER_CRYPTODEV, cryptodev_backend_stats_cb, + cryptodev_backend_schemas_cb); } static const TypeInfo cryptodev_backend_info = { diff --git a/backends/dbus-vmstate.c b/backends/dbus-vmstate.c index 9cfd758c42..be6c4d8e0a 100644 --- a/backends/dbus-vmstate.c +++ b/backends/dbus-vmstate.c @@ -114,14 +114,19 @@ dbus_get_proxies(DBusVMState *self, GError **err) "org.qemu.VMState1", NULL, err); if (!proxy) { - return NULL; + if (err != NULL && *err != NULL) { + warn_report("%s: Failed to create proxy: %s", + __func__, (*err)->message); + g_clear_error(err); + } + continue; } 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; + warn_report("%s: VMState Id property is missing.", __func__); + g_clear_object(&proxy); + continue; } id = g_variant_dup_string(result, &size); @@ -388,7 +393,7 @@ static const VMStateDescription dbus_vmstate = { .version_id = 0, .pre_save = dbus_vmstate_pre_save, .post_load = dbus_vmstate_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(data_size, DBusVMState), VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size), VMSTATE_END_OF_LIST() @@ -421,8 +426,7 @@ dbus_vmstate_complete(UserCreatable *uc, Error **errp) return; } - if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY, - &dbus_vmstate, self) < 0) { + if (vmstate_register_any(VMSTATE_IF(self), &dbus_vmstate, self) < 0) { error_setg(errp, "Failed to register vmstate"); } } diff --git a/backends/hostmem-epc.c b/backends/hostmem-epc.c index 037292d267..735e2e1cf8 100644 --- a/backends/hostmem-epc.c +++ b/backends/hostmem-epc.c @@ -9,39 +9,37 @@ * 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 #include "qemu/osdep.h" +#include #include "qom/object_interfaces.h" #include "qapi/error.h" #include "sysemu/hostmem.h" #include "hw/i386/hostmem-epc.h" -static void +static bool sgx_epc_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) { + g_autofree char *name = NULL; uint32_t ram_flags; - char *name; int fd; if (!backend->size) { error_setg(errp, "can't create backend with size 0"); - return; + return false; } fd = qemu_open_old("/dev/sgx_vepc", O_RDWR); if (fd < 0) { error_setg_errno(errp, errno, "failed to open /dev/sgx_vepc to alloc SGX EPC"); - return; + return false; } name = object_get_canonical_path(OBJECT(backend)); ram_flags = (backend->share ? RAM_SHARED : 0) | RAM_PROTECTED; - memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), - name, backend->size, ram_flags, - fd, 0, errp); - g_free(name); + return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), name, + backend->size, ram_flags, fd, 0, errp); } static void sgx_epc_backend_instance_init(Object *obj) diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index 25141283c4..ac3e433cbd 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -18,6 +18,8 @@ #include "sysemu/hostmem.h" #include "qom/object_interfaces.h" #include "qom/object.h" +#include "qapi/visitor.h" +#include "qapi/qapi-visit-common.h" OBJECT_DECLARE_SIMPLE_TYPE(HostMemoryBackendFile, MEMORY_BACKEND_FILE) @@ -27,39 +29,67 @@ struct HostMemoryBackendFile { char *mem_path; uint64_t align; + uint64_t offset; bool discard_data; bool is_pmem; bool readonly; + OnOffAuto rom; }; -static void +static bool file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) { #ifndef CONFIG_POSIX error_setg(errp, "backend '%s' not supported on this host", object_get_typename(OBJECT(backend))); + return false; #else HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); + g_autofree gchar *name = NULL; uint32_t ram_flags; - gchar *name; if (!backend->size) { error_setg(errp, "can't create backend with size 0"); - return; + return false; } if (!fb->mem_path) { error_setg(errp, "mem-path property not set"); - return; + return false; + } + + switch (fb->rom) { + case ON_OFF_AUTO_AUTO: + /* Traditionally, opening the file readonly always resulted in ROM. */ + fb->rom = fb->readonly ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + break; + case ON_OFF_AUTO_ON: + if (!fb->readonly) { + error_setg(errp, "property 'rom' = 'on' is not supported with" + " 'readonly' = 'off'"); + return false; + } + break; + case ON_OFF_AUTO_OFF: + if (fb->readonly && backend->share) { + error_setg(errp, "property 'rom' = 'off' is incompatible with" + " 'readonly' = 'on' and 'share' = 'on'"); + return false; + } + break; + default: + g_assert_not_reached(); } name = host_memory_backend_get_name(backend); ram_flags = backend->share ? RAM_SHARED : 0; + ram_flags |= fb->readonly ? RAM_READONLY_FD : 0; + ram_flags |= fb->rom == ON_OFF_AUTO_ON ? RAM_READONLY : 0; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; ram_flags |= fb->is_pmem ? RAM_PMEM : 0; - memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), name, - backend->size, fb->align, ram_flags, - fb->mem_path, fb->readonly, errp); - g_free(name); + ram_flags |= RAM_NAMED_FILE; + return memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), name, + backend->size, fb->align, ram_flags, + fb->mem_path, fb->offset, errp); #endif } @@ -125,6 +155,36 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, fb->align = val; } +static void file_memory_backend_get_offset(Object *o, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); + uint64_t val = fb->offset; + + visit_type_size(v, name, &val, errp); +} + +static void file_memory_backend_set_offset(Object *o, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(o); + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); + uint64_t val; + + if (host_memory_backend_mr_inited(backend)) { + error_setg(errp, "cannot change property '%s' of %s", name, + object_get_typename(o)); + return; + } + + if (!visit_type_size(v, name, &val, errp)) { + return; + } + fb->offset = val; +} + #ifdef CONFIG_LIBPMEM static bool file_memory_backend_get_pmem(Object *o, Error **errp) { @@ -168,6 +228,32 @@ static void file_memory_backend_set_readonly(Object *obj, bool value, fb->readonly = value; } +static void file_memory_backend_get_rom(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(obj); + OnOffAuto rom = fb->rom; + + visit_type_OnOffAuto(v, name, &rom, errp); +} + +static void file_memory_backend_set_rom(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(obj); + + if (host_memory_backend_mr_inited(backend)) { + error_setg(errp, "cannot change property '%s' of %s.", name, + object_get_typename(obj)); + return; + } + + visit_type_OnOffAuto(v, name, &fb->rom, errp); +} + static void file_backend_unparent(Object *obj) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); @@ -197,6 +283,12 @@ file_backend_class_init(ObjectClass *oc, void *data) file_memory_backend_get_align, file_memory_backend_set_align, NULL, NULL); + object_class_property_add(oc, "offset", "int", + file_memory_backend_get_offset, + file_memory_backend_set_offset, + NULL, NULL); + object_class_property_set_description(oc, "offset", + "Offset into the target file (ex: 1G)"); #ifdef CONFIG_LIBPMEM object_class_property_add_bool(oc, "pmem", file_memory_backend_get_pmem, file_memory_backend_set_pmem); @@ -204,6 +296,10 @@ file_backend_class_init(ObjectClass *oc, void *data) object_class_property_add_bool(oc, "readonly", file_memory_backend_get_readonly, file_memory_backend_set_readonly); + object_class_property_add(oc, "rom", "OnOffAuto", + file_memory_backend_get_rom, file_memory_backend_set_rom, NULL, NULL); + object_class_property_set_description(oc, "rom", + "Whether to create Read Only Memory (ROM)"); } static void file_backend_instance_finalize(Object *o) diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c index 3fc85c3db8..3923ea9364 100644 --- a/backends/hostmem-memfd.c +++ b/backends/hostmem-memfd.c @@ -31,17 +31,17 @@ struct HostMemoryBackendMemfd { bool seal; }; -static void +static bool memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) { HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(backend); + g_autofree char *name = NULL; uint32_t ram_flags; - char *name; int fd; if (!backend->size) { error_setg(errp, "can't create backend with size 0"); - return; + return false; } fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, @@ -49,15 +49,14 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL : 0, errp); if (fd == -1) { - return; + return false; } name = host_memory_backend_get_name(backend); ram_flags = backend->share ? RAM_SHARED : 0; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; - memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), name, - backend->size, ram_flags, fd, 0, errp); - g_free(name); + return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), name, + backend->size, ram_flags, fd, 0, errp); } static bool diff --git a/backends/hostmem-ram.c b/backends/hostmem-ram.c index b8e55cdbd0..d121249f0f 100644 --- a/backends/hostmem-ram.c +++ b/backends/hostmem-ram.c @@ -16,23 +16,23 @@ #include "qemu/module.h" #include "qom/object_interfaces.h" -static void +static bool ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) { + g_autofree char *name = NULL; uint32_t ram_flags; - char *name; if (!backend->size) { error_setg(errp, "can't create backend with size 0"); - return; + return false; } name = host_memory_backend_get_name(backend); ram_flags = backend->share ? RAM_SHARED : 0; ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; - memory_region_init_ram_flags_nomigrate(&backend->mr, OBJECT(backend), name, - backend->size, ram_flags, errp); - g_free(name); + return memory_region_init_ram_flags_nomigrate(&backend->mr, OBJECT(backend), + name, backend->size, + ram_flags, errp); } static void diff --git a/backends/hostmem.c b/backends/hostmem.c index 624bb7ecd3..81a72ce40b 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -20,10 +20,16 @@ #include "qom/object_interfaces.h" #include "qemu/mmap-alloc.h" #include "qemu/madvise.h" +#include "hw/qdev-core.h" #ifdef CONFIG_NUMA #include +#include QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_DEFAULT != MPOL_DEFAULT); +/* + * HOST_MEM_POLICY_PREFERRED may either translate to MPOL_PREFERRED or + * MPOL_PREFERRED_MANY, see comments further below. + */ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_PREFERRED != MPOL_PREFERRED); QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND); QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); @@ -214,7 +220,6 @@ static bool host_memory_backend_get_prealloc(Object *obj, Error **errp) static void host_memory_backend_set_prealloc(Object *obj, bool value, Error **errp) { - Error *local_err = NULL; HostMemoryBackend *backend = MEMORY_BACKEND(obj); if (!backend->reserve && value) { @@ -232,9 +237,8 @@ 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, backend->prealloc_threads, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!qemu_prealloc_mem(fd, ptr, sz, backend->prealloc_threads, + backend->prealloc_context, false, errp)) { return; } backend->prealloc = true; @@ -306,102 +310,104 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend) return backend->is_mapped; } -#ifdef __linux__ size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) { - Object *obj = OBJECT(memdev); - char *path = object_property_get_str(obj, "mem-path", NULL); - size_t pagesize = qemu_mempath_getpagesize(path); - - g_free(path); + size_t pagesize = qemu_ram_pagesize(memdev->mr.ram_block); + g_assert(pagesize >= qemu_real_host_page_size()); return pagesize; } -#else -size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) -{ - return qemu_real_host_page_size(); -} -#endif static void host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(uc); HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc); - Error *local_err = NULL; void *ptr; uint64_t sz; + bool async = !phase_check(PHASE_LATE_BACKENDS_CREATED); - if (bc->alloc) { - bc->alloc(backend, &local_err); - if (local_err) { - goto out; - } + if (!bc->alloc) { + return; + } + if (!bc->alloc(backend, errp)) { + return; + } - ptr = memory_region_get_ram_ptr(&backend->mr); - sz = memory_region_size(&backend->mr); + ptr = memory_region_get_ram_ptr(&backend->mr); + sz = memory_region_size(&backend->mr); - if (backend->merge) { - qemu_madvise(ptr, sz, QEMU_MADV_MERGEABLE); - } - if (!backend->dump) { - qemu_madvise(ptr, sz, QEMU_MADV_DONTDUMP); - } + if (backend->merge) { + qemu_madvise(ptr, sz, QEMU_MADV_MERGEABLE); + } + if (!backend->dump) { + qemu_madvise(ptr, sz, QEMU_MADV_DONTDUMP); + } #ifdef CONFIG_NUMA - unsigned long lastbit = find_last_bit(backend->host_nodes, MAX_NODES); - /* lastbit == MAX_NODES means maxnode = 0 */ - unsigned long maxnode = (lastbit + 1) % (MAX_NODES + 1); - /* ensure policy won't be ignored in case memory is preallocated - * before mbind(). note: MPOL_MF_STRICT is ignored on hugepages so - * this doesn't catch hugepage case. */ - unsigned flags = MPOL_MF_STRICT | MPOL_MF_MOVE; + unsigned long lastbit = find_last_bit(backend->host_nodes, MAX_NODES); + /* lastbit == MAX_NODES means maxnode = 0 */ + unsigned long maxnode = (lastbit + 1) % (MAX_NODES + 1); + /* + * Ensure policy won't be ignored in case memory is preallocated + * before mbind(). note: MPOL_MF_STRICT is ignored on hugepages so + * this doesn't catch hugepage case. + */ + unsigned flags = MPOL_MF_STRICT | MPOL_MF_MOVE; + int mode = backend->policy; - /* check for invalid host-nodes and policies and give more verbose - * error messages than mbind(). */ - if (maxnode && backend->policy == MPOL_DEFAULT) { - error_setg(errp, "host-nodes must be empty for policy default," - " or you should explicitly specify a policy other" - " than default"); - return; - } else if (maxnode == 0 && backend->policy != MPOL_DEFAULT) { - error_setg(errp, "host-nodes must be set for policy %s", - HostMemPolicy_str(backend->policy)); - return; - } + /* check for invalid host-nodes and policies and give more verbose + * error messages than mbind(). */ + if (maxnode && backend->policy == MPOL_DEFAULT) { + error_setg(errp, "host-nodes must be empty for policy default," + " or you should explicitly specify a policy other" + " than default"); + return; + } else if (maxnode == 0 && backend->policy != MPOL_DEFAULT) { + error_setg(errp, "host-nodes must be set for policy %s", + HostMemPolicy_str(backend->policy)); + return; + } - /* We can have up to MAX_NODES nodes, but we need to pass maxnode+1 - * as argument to mbind() due to an old Linux bug (feature?) which - * cuts off the last specified node. This means backend->host_nodes - * must have MAX_NODES+1 bits available. + /* + * We can have up to MAX_NODES nodes, but we need to pass maxnode+1 + * as argument to mbind() due to an old Linux bug (feature?) which + * cuts off the last specified node. This means backend->host_nodes + * must have MAX_NODES+1 bits available. + */ + assert(sizeof(backend->host_nodes) >= + BITS_TO_LONGS(MAX_NODES + 1) * sizeof(unsigned long)); + assert(maxnode <= MAX_NODES); + +#ifdef HAVE_NUMA_HAS_PREFERRED_MANY + if (mode == MPOL_PREFERRED && numa_has_preferred_many() > 0) { + /* + * Replace with MPOL_PREFERRED_MANY otherwise the mbind() below + * silently picks the first node. */ - assert(sizeof(backend->host_nodes) >= - BITS_TO_LONGS(MAX_NODES + 1) * sizeof(unsigned long)); - assert(maxnode <= MAX_NODES); - - if (maxnode && - mbind(ptr, sz, backend->policy, backend->host_nodes, maxnode + 1, - flags)) { - if (backend->policy != MPOL_DEFAULT || errno != ENOSYS) { - error_setg_errno(errp, errno, - "cannot bind memory to host NUMA nodes"); - return; - } - } + mode = MPOL_PREFERRED_MANY; + } #endif - /* Preallocate memory after the NUMA policy has been instantiated. - * This is necessary to guarantee memory is allocated with - * specified NUMA policy in place. - */ - if (backend->prealloc) { - os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz, - backend->prealloc_threads, &local_err); - if (local_err) { - goto out; - } + + if (maxnode && + mbind(ptr, sz, mode, backend->host_nodes, maxnode + 1, flags)) { + if (backend->policy != MPOL_DEFAULT || errno != ENOSYS) { + error_setg_errno(errp, errno, + "cannot bind memory to host NUMA nodes"); + return; } } -out: - error_propagate(errp, local_err); +#endif + /* + * Preallocate memory after the NUMA policy has been instantiated. + * This is necessary to guarantee memory is allocated with + * specified NUMA policy in place. + */ + if (backend->prealloc && !qemu_prealloc_mem(memory_region_get_fd(&backend->mr), + ptr, sz, + backend->prealloc_threads, + backend->prealloc_context, + async, errp)) { + return; + } } static bool @@ -502,6 +508,11 @@ host_memory_backend_class_init(ObjectClass *oc, void *data) NULL, NULL); object_class_property_set_description(oc, "prealloc-threads", "Number of CPU threads to use for prealloc"); + object_class_property_add_link(oc, "prealloc-context", + TYPE_THREAD_CONTEXT, offsetof(HostMemoryBackend, prealloc_context), + object_property_allow_set_link, OBJ_PROP_LINK_STRONG); + object_class_property_set_description(oc, "prealloc-context", + "Context to use for creating CPU threads for preallocation"); object_class_property_add(oc, "size", "int", host_memory_backend_get_size, host_memory_backend_set_size, diff --git a/backends/iommufd.c b/backends/iommufd.c new file mode 100644 index 0000000000..62a79fa6b0 --- /dev/null +++ b/backends/iommufd.c @@ -0,0 +1,234 @@ +/* + * iommufd container backend + * + * Copyright (C) 2023 Intel Corporation. + * Copyright Red Hat, Inc. 2023 + * + * Authors: Yi Liu + * Eric Auger + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "sysemu/iommufd.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "qemu/module.h" +#include "qom/object_interfaces.h" +#include "qemu/error-report.h" +#include "monitor/monitor.h" +#include "trace.h" +#include +#include + +static void iommufd_backend_init(Object *obj) +{ + IOMMUFDBackend *be = IOMMUFD_BACKEND(obj); + + be->fd = -1; + be->users = 0; + be->owned = true; +} + +static void iommufd_backend_finalize(Object *obj) +{ + IOMMUFDBackend *be = IOMMUFD_BACKEND(obj); + + if (be->owned) { + close(be->fd); + be->fd = -1; + } +} + +static void iommufd_backend_set_fd(Object *obj, const char *str, Error **errp) +{ + ERRP_GUARD(); + IOMMUFDBackend *be = IOMMUFD_BACKEND(obj); + int fd = -1; + + fd = monitor_fd_param(monitor_cur(), str, errp); + if (fd == -1) { + error_prepend(errp, "Could not parse remote object fd %s:", str); + return; + } + be->fd = fd; + be->owned = false; + trace_iommu_backend_set_fd(be->fd); +} + +static bool iommufd_backend_can_be_deleted(UserCreatable *uc) +{ + IOMMUFDBackend *be = IOMMUFD_BACKEND(uc); + + return !be->users; +} + +static void iommufd_backend_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->can_be_deleted = iommufd_backend_can_be_deleted; + + object_class_property_add_str(oc, "fd", NULL, iommufd_backend_set_fd); +} + +int iommufd_backend_connect(IOMMUFDBackend *be, Error **errp) +{ + int fd, ret = 0; + + if (be->owned && !be->users) { + fd = qemu_open_old("/dev/iommu", O_RDWR); + if (fd < 0) { + error_setg_errno(errp, errno, "/dev/iommu opening failed"); + ret = fd; + goto out; + } + be->fd = fd; + } + be->users++; +out: + trace_iommufd_backend_connect(be->fd, be->owned, + be->users, ret); + return ret; +} + +void iommufd_backend_disconnect(IOMMUFDBackend *be) +{ + if (!be->users) { + goto out; + } + be->users--; + if (!be->users && be->owned) { + close(be->fd); + be->fd = -1; + } +out: + trace_iommufd_backend_disconnect(be->fd, be->users); +} + +int iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id, + Error **errp) +{ + int ret, fd = be->fd; + struct iommu_ioas_alloc alloc_data = { + .size = sizeof(alloc_data), + .flags = 0, + }; + + ret = ioctl(fd, IOMMU_IOAS_ALLOC, &alloc_data); + if (ret) { + error_setg_errno(errp, errno, "Failed to allocate ioas"); + return ret; + } + + *ioas_id = alloc_data.out_ioas_id; + trace_iommufd_backend_alloc_ioas(fd, *ioas_id, ret); + + return ret; +} + +void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id) +{ + int ret, fd = be->fd; + struct iommu_destroy des = { + .size = sizeof(des), + .id = id, + }; + + ret = ioctl(fd, IOMMU_DESTROY, &des); + trace_iommufd_backend_free_id(fd, id, ret); + if (ret) { + error_report("Failed to free id: %u %m", id); + } +} + +int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, + ram_addr_t size, void *vaddr, bool readonly) +{ + int ret, fd = be->fd; + struct iommu_ioas_map map = { + .size = sizeof(map), + .flags = IOMMU_IOAS_MAP_READABLE | + IOMMU_IOAS_MAP_FIXED_IOVA, + .ioas_id = ioas_id, + .__reserved = 0, + .user_va = (uintptr_t)vaddr, + .iova = iova, + .length = size, + }; + + if (!readonly) { + map.flags |= IOMMU_IOAS_MAP_WRITEABLE; + } + + ret = ioctl(fd, IOMMU_IOAS_MAP, &map); + trace_iommufd_backend_map_dma(fd, ioas_id, iova, size, + vaddr, readonly, ret); + if (ret) { + ret = -errno; + + /* TODO: Not support mapping hardware PCI BAR region for now. */ + if (errno == EFAULT) { + warn_report("IOMMU_IOAS_MAP failed: %m, PCI BAR?"); + } else { + error_report("IOMMU_IOAS_MAP failed: %m"); + } + } + return ret; +} + +int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, + hwaddr iova, ram_addr_t size) +{ + int ret, fd = be->fd; + struct iommu_ioas_unmap unmap = { + .size = sizeof(unmap), + .ioas_id = ioas_id, + .iova = iova, + .length = size, + }; + + ret = ioctl(fd, IOMMU_IOAS_UNMAP, &unmap); + /* + * IOMMUFD takes mapping as some kind of object, unmapping + * nonexistent mapping is treated as deleting a nonexistent + * object and return ENOENT. This is different from legacy + * backend which allows it. vIOMMU may trigger a lot of + * redundant unmapping, to avoid flush the log, treat them + * as succeess for IOMMUFD just like legacy backend. + */ + if (ret && errno == ENOENT) { + trace_iommufd_backend_unmap_dma_non_exist(fd, ioas_id, iova, size, ret); + ret = 0; + } else { + trace_iommufd_backend_unmap_dma(fd, ioas_id, iova, size, ret); + } + + if (ret) { + ret = -errno; + error_report("IOMMU_IOAS_UNMAP failed: %m"); + } + return ret; +} + +static const TypeInfo iommufd_backend_info = { + .name = TYPE_IOMMUFD_BACKEND, + .parent = TYPE_OBJECT, + .instance_size = sizeof(IOMMUFDBackend), + .instance_init = iommufd_backend_init, + .instance_finalize = iommufd_backend_finalize, + .class_size = sizeof(IOMMUFDBackendClass), + .class_init = iommufd_backend_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void register_types(void) +{ + type_register_static(&iommufd_backend_info); +} + +type_init(register_types); diff --git a/backends/meson.build b/backends/meson.build index b1884a88ec..8b2b111497 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -1,5 +1,6 @@ -softmmu_ss.add([files( +system_ss.add([files( 'cryptodev-builtin.c', + 'cryptodev-hmp-cmds.c', 'cryptodev.c', 'hostmem-ram.c', 'hostmem.c', @@ -9,17 +10,25 @@ softmmu_ss.add([files( 'confidential-guest-support.c', ), numa]) -softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files('rng-random.c')) -softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files('hostmem-file.c')) -softmmu_ss.add(when: 'CONFIG_LINUX', if_true: files('hostmem-memfd.c')) +if host_os != 'windows' + system_ss.add(files('rng-random.c')) + system_ss.add(files('hostmem-file.c')) +endif +if host_os == 'linux' + system_ss.add(files('hostmem-memfd.c')) +endif +if keyutils.found() + system_ss.add(keyutils, files('cryptodev-lkcf.c')) +endif if have_vhost_user - softmmu_ss.add(when: 'CONFIG_VIRTIO', if_true: files('vhost-user.c')) + system_ss.add(when: 'CONFIG_VIRTIO', if_true: files('vhost-user.c')) endif -softmmu_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('cryptodev-vhost.c')) +system_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('cryptodev-vhost.c')) +system_ss.add(when: 'CONFIG_IOMMUFD', if_true: files('iommufd.c')) if have_vhost_user_crypto - softmmu_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('cryptodev-vhost-user.c')) + system_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('cryptodev-vhost-user.c')) endif -softmmu_ss.add(when: gio, if_true: files('dbus-vmstate.c')) -softmmu_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c')) +system_ss.add(when: gio, if_true: files('dbus-vmstate.c')) +system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c')) subdir('tpm') diff --git a/backends/rng-egd.c b/backends/rng-egd.c index 4de142b9dc..684c3cf3d6 100644 --- a/backends/rng-egd.c +++ b/backends/rng-egd.c @@ -116,7 +116,7 @@ static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp) RngEgd *s = RNG_EGD(b); if (b->opened) { - error_setg(errp, QERR_PERMISSION_DENIED); + error_setg(errp, "Property 'chardev' can no longer be set"); } else { g_free(s->chr_name); s->chr_name = g_strdup(value); diff --git a/backends/rng-random.c b/backends/rng-random.c index 7add272edd..80eb5be138 100644 --- a/backends/rng-random.c +++ b/backends/rng-random.c @@ -96,7 +96,7 @@ static void rng_random_set_filename(Object *obj, const char *filename, RngRandom *s = RNG_RANDOM(obj); if (b->opened) { - error_setg(errp, QERR_PERMISSION_DENIED); + error_setg(errp, "Property 'filename' can no longer be set"); return; } diff --git a/backends/rng.c b/backends/rng.c index 6c7bf64426..9bbd0c77b6 100644 --- a/backends/rng.c +++ b/backends/rng.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "sysemu/rng.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qemu/module.h" #include "qom/object_interfaces.h" diff --git a/backends/tpm/meson.build b/backends/tpm/meson.build index 7f2503f84e..0bfa6c422b 100644 --- a/backends/tpm/meson.build +++ b/backends/tpm/meson.build @@ -1,6 +1,6 @@ if have_tpm - softmmu_ss.add(files('tpm_backend.c')) - softmmu_ss.add(files('tpm_util.c')) - softmmu_ss.add(when: 'CONFIG_TPM_PASSTHROUGH', if_true: files('tpm_passthrough.c')) - softmmu_ss.add(when: 'CONFIG_TPM_EMULATOR', if_true: files('tpm_emulator.c')) + system_ss.add(files('tpm_backend.c')) + system_ss.add(files('tpm_util.c')) + system_ss.add(when: 'CONFIG_TPM_PASSTHROUGH', if_true: files('tpm_passthrough.c')) + system_ss.add(when: 'CONFIG_TPM_EMULATOR', if_true: files('tpm_emulator.c')) endif diff --git a/backends/tpm/tpm_backend.c b/backends/tpm/tpm_backend.c index 375587e743..485a20b9e0 100644 --- a/backends/tpm/tpm_backend.c +++ b/backends/tpm/tpm_backend.c @@ -100,8 +100,6 @@ bool tpm_backend_had_startup_error(TPMBackend *s) void tpm_backend_deliver_request(TPMBackend *s, TPMBackendCmd *cmd) { - ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context()); - if (s->cmd != NULL) { error_report("There is a TPM request pending"); return; @@ -109,7 +107,7 @@ void tpm_backend_deliver_request(TPMBackend *s, TPMBackendCmd *cmd) s->cmd = cmd; object_ref(OBJECT(s)); - thread_pool_submit_aio(pool, tpm_backend_worker_thread, s, + thread_pool_submit_aio(tpm_backend_worker_thread, s, tpm_backend_request_completed, s); } diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c index 87d061e9bb..5a8fba9bde 100644 --- a/backends/tpm/tpm_emulator.c +++ b/backends/tpm/tpm_emulator.c @@ -32,6 +32,7 @@ #include "qemu/sockets.h" #include "qemu/lockable.h" #include "io/channel-socket.h" +#include "sysemu/runstate.h" #include "sysemu/tpm_backend.h" #include "sysemu/tpm_util.h" #include "tpm_int.h" @@ -81,6 +82,9 @@ struct TPMEmulator { unsigned int established_flag_cached:1; TPMBlobBuffers state_blobs; + + bool relock_storage; + VMChangeStateEntry *vmstate; }; struct tpm_error { @@ -302,6 +306,35 @@ static int tpm_emulator_stop_tpm(TPMBackend *tb) return 0; } +static int tpm_emulator_lock_storage(TPMEmulator *tpm_emu) +{ + ptm_lockstorage pls; + + if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_LOCK_STORAGE)) { + trace_tpm_emulator_lock_storage_cmd_not_supt(); + return 0; + } + + /* give failing side 300 * 10ms time to release lock */ + pls.u.req.retries = cpu_to_be32(300); + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_LOCK_STORAGE, &pls, + sizeof(pls.u.req), sizeof(pls.u.resp)) < 0) { + error_report("tpm-emulator: Could not lock storage within 3 seconds: " + "%s", strerror(errno)); + return -1; + } + + pls.u.resp.tpm_result = be32_to_cpu(pls.u.resp.tpm_result); + if (pls.u.resp.tpm_result != 0) { + error_report("tpm-emulator: TPM result for CMD_LOCK_STORAGE: 0x%x %s", + pls.u.resp.tpm_result, + tpm_emulator_strerror(pls.u.resp.tpm_result)); + return -1; + } + + return 0; +} + static int tpm_emulator_set_buffer_size(TPMBackend *tb, size_t wanted_size, size_t *actual_size) @@ -383,6 +416,15 @@ err_exit: static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) { + /* TPM startup will be done from post_load hook */ + if (runstate_check(RUN_STATE_INMIGRATE)) { + if (buffersize != 0) { + return tpm_emulator_set_buffer_size(tb, buffersize, NULL); + } + + return 0; + } + return tpm_emulator_startup_tpm_resume(tb, buffersize, false); } @@ -492,11 +534,8 @@ static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) error_setg(&tpm_emu->migration_blocker, "Migration disabled: TPM emulator does not support " "migration"); - if (migrate_add_blocker(tpm_emu->migration_blocker, &err) < 0) { + if (migrate_add_blocker(&tpm_emu->migration_blocker, &err) < 0) { error_report_err(err); - error_free(tpm_emu->migration_blocker); - tpm_emu->migration_blocker = NULL; - return -1; } } @@ -510,7 +549,7 @@ static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) Error *err = NULL; int fds[2] = { -1, -1 }; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + if (qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { error_report("tpm-emulator: Failed to create socketpair"); return -1; } @@ -531,13 +570,13 @@ static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) goto err_exit; } - closesocket(fds[1]); + close(fds[1]); return 0; err_exit: - closesocket(fds[0]); - closesocket(fds[1]); + close(fds[0]); + close(fds[1]); return -1; } @@ -843,13 +882,34 @@ static int tpm_emulator_pre_save(void *opaque) { TPMBackend *tb = opaque; TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + int ret; trace_tpm_emulator_pre_save(); tpm_backend_finish_sync(tb); /* get the state blobs from the TPM */ - return tpm_emulator_get_state_blobs(tpm_emu); + ret = tpm_emulator_get_state_blobs(tpm_emu); + + tpm_emu->relock_storage = ret == 0; + + return ret; +} + +static void tpm_emulator_vm_state_change(void *opaque, bool running, + RunState state) +{ + TPMBackend *tb = opaque; + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + + trace_tpm_emulator_vm_state_change(running, state); + + if (!running || !tpm_emu->relock_storage) { + return; + } + + /* lock storage after migration fall-back */ + tpm_emulator_lock_storage(tpm_emu); } /* @@ -879,7 +939,7 @@ static const VMStateDescription vmstate_tpm_emulator = { .version_id = 0, .pre_save = tpm_emulator_pre_save, .post_load = tpm_emulator_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator), VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator), VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.permanent.buffer, @@ -911,9 +971,11 @@ static void tpm_emulator_inst_init(Object *obj) tpm_emu->options = g_new0(TPMEmulatorOptions, 1); tpm_emu->cur_locty_number = ~0; qemu_mutex_init(&tpm_emu->mutex); + tpm_emu->vmstate = + qemu_add_vm_change_state_handler(tpm_emulator_vm_state_change, + tpm_emu); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, - &vmstate_tpm_emulator, obj); + vmstate_register_any(NULL, &vmstate_tpm_emulator, obj); } /* @@ -950,16 +1012,14 @@ static void tpm_emulator_inst_finalize(Object *obj) qapi_free_TPMEmulatorOptions(tpm_emu->options); - if (tpm_emu->migration_blocker) { - migrate_del_blocker(tpm_emu->migration_blocker); - error_free(tpm_emu->migration_blocker); - } + migrate_del_blocker(&tpm_emu->migration_blocker); tpm_sized_buffer_reset(&state_blobs->volatil); tpm_sized_buffer_reset(&state_blobs->permanent); tpm_sized_buffer_reset(&state_blobs->savestate); qemu_mutex_destroy(&tpm_emu->mutex); + qemu_del_vm_change_state_handler(tpm_emu->vmstate); vmstate_unregister(NULL, &vmstate_tpm_emulator, obj); } diff --git a/backends/tpm/tpm_ioctl.h b/backends/tpm/tpm_ioctl.h index bd6c12cb86..1933ab6855 100644 --- a/backends/tpm/tpm_ioctl.h +++ b/backends/tpm/tpm_ioctl.h @@ -5,12 +5,17 @@ * * This file is licensed under the terms of the 3-clause BSD license */ +#ifndef _TPM_IOCTL_H_ +#define _TPM_IOCTL_H_ -#ifndef TPM_IOCTL_H -#define TPM_IOCTL_H +#if defined(__CYGWIN__) +# define __USE_LINUX_IOCTL_DEFS +#endif +#ifndef _WIN32 #include #include +#endif #ifdef HAVE_SYS_IOCCOM_H #include @@ -194,6 +199,48 @@ struct ptm_setbuffersize { } u; }; +#define PTM_GETINFO_SIZE (3 * 1024) +/* + * PTM_GET_INFO: Get info about the TPM implementation (from libtpms) + * + * This request allows to indirectly call TPMLIB_GetInfo(flags) and + * retrieve information from libtpms. + * Only one transaction is currently necessary for returning results + * to a client. Therefore, totlength and length will be the same if + * offset is 0. + */ +struct ptm_getinfo { + union { + struct { + uint64_t flags; + uint32_t offset; /* offset from where to read */ + uint32_t pad; /* 32 bit arch */ + } req; /* request */ + struct { + ptm_res tpm_result; + uint32_t totlength; + uint32_t length; + char buffer[PTM_GETINFO_SIZE]; + } resp; /* response */ + } u; +}; + +#define SWTPM_INFO_TPMSPECIFICATION ((uint64_t)1 << 0) +#define SWTPM_INFO_TPMATTRIBUTES ((uint64_t)1 << 1) + +/* + * PTM_LOCK_STORAGE: Lock the storage and retry n times + */ +struct ptm_lockstorage { + union { + struct { + uint32_t retries; /* number of retries */ + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; typedef uint64_t ptm_cap; typedef struct ptm_est ptm_est; @@ -205,6 +252,8 @@ typedef struct ptm_getstate ptm_getstate; typedef struct ptm_setstate ptm_setstate; typedef struct ptm_getconfig ptm_getconfig; typedef struct ptm_setbuffersize ptm_setbuffersize; +typedef struct ptm_getinfo ptm_getinfo; +typedef struct ptm_lockstorage ptm_lockstorage; /* capability flags returned by PTM_GET_CAPABILITY */ #define PTM_CAP_INIT (1) @@ -221,7 +270,11 @@ typedef struct ptm_setbuffersize ptm_setbuffersize; #define PTM_CAP_GET_CONFIG (1 << 11) #define PTM_CAP_SET_DATAFD (1 << 12) #define PTM_CAP_SET_BUFFERSIZE (1 << 13) +#define PTM_CAP_GET_INFO (1 << 14) +#define PTM_CAP_SEND_COMMAND_HEADER (1 << 15) +#define PTM_CAP_LOCK_STORAGE (1 << 16) +#ifndef _WIN32 enum { PTM_GET_CAPABILITY = _IOR('P', 0, ptm_cap), PTM_INIT = _IOWR('P', 1, ptm_init), @@ -240,7 +293,10 @@ enum { PTM_GET_CONFIG = _IOR('P', 14, ptm_getconfig), PTM_SET_DATAFD = _IOR('P', 15, ptm_res), PTM_SET_BUFFERSIZE = _IOWR('P', 16, ptm_setbuffersize), + PTM_GET_INFO = _IOWR('P', 17, ptm_getinfo), + PTM_LOCK_STORAGE = _IOWR('P', 18, ptm_lockstorage), }; +#endif /* * Commands used by the non-CUSE TPMs @@ -253,23 +309,25 @@ enum { * and ptm_set_state:u.req.data) are 0xffffffff. */ enum { - CMD_GET_CAPABILITY = 1, - CMD_INIT, - CMD_SHUTDOWN, - CMD_GET_TPMESTABLISHED, - CMD_SET_LOCALITY, - CMD_HASH_START, - CMD_HASH_DATA, - CMD_HASH_END, - CMD_CANCEL_TPM_CMD, - CMD_STORE_VOLATILE, - CMD_RESET_TPMESTABLISHED, - CMD_GET_STATEBLOB, - CMD_SET_STATEBLOB, - CMD_STOP, - CMD_GET_CONFIG, - CMD_SET_DATAFD, - CMD_SET_BUFFERSIZE, + CMD_GET_CAPABILITY = 1, /* 0x01 */ + CMD_INIT, /* 0x02 */ + CMD_SHUTDOWN, /* 0x03 */ + CMD_GET_TPMESTABLISHED, /* 0x04 */ + CMD_SET_LOCALITY, /* 0x05 */ + CMD_HASH_START, /* 0x06 */ + CMD_HASH_DATA, /* 0x07 */ + CMD_HASH_END, /* 0x08 */ + CMD_CANCEL_TPM_CMD, /* 0x09 */ + CMD_STORE_VOLATILE, /* 0x0a */ + CMD_RESET_TPMESTABLISHED, /* 0x0b */ + CMD_GET_STATEBLOB, /* 0x0c */ + CMD_SET_STATEBLOB, /* 0x0d */ + CMD_STOP, /* 0x0e */ + CMD_GET_CONFIG, /* 0x0f */ + CMD_SET_DATAFD, /* 0x10 */ + CMD_SET_BUFFERSIZE, /* 0x11 */ + CMD_GET_INFO, /* 0x12 */ + CMD_LOCK_STORAGE, /* 0x13 */ }; -#endif /* TPM_IOCTL_H */ +#endif /* _TPM_IOCTL_H_ */ diff --git a/backends/tpm/tpm_passthrough.c b/backends/tpm/tpm_passthrough.c index 5a2f74db1b..179697a3a9 100644 --- a/backends/tpm/tpm_passthrough.c +++ b/backends/tpm/tpm_passthrough.c @@ -259,12 +259,10 @@ tpm_passthrough_handle_device_opts(TPMPassthruState *tpm_pt, QemuOpts *opts) value = qemu_opt_get(opts, "cancel-path"); if (value) { tpm_pt->options->cancel_path = g_strdup(value); - tpm_pt->options->has_cancel_path = true; } value = qemu_opt_get(opts, "path"); if (value) { - tpm_pt->options->has_path = true; tpm_pt->options->path = g_strdup(value); } diff --git a/backends/tpm/tpm_util.c b/backends/tpm/tpm_util.c index a6e6d3e72f..1856589c3b 100644 --- a/backends/tpm/tpm_util.c +++ b/backends/tpm/tpm_util.c @@ -112,12 +112,8 @@ static int tpm_util_request(int fd, void *response, size_t responselen) { - fd_set readfds; + GPollFD fds[1] = { {.fd = fd, .events = G_IO_IN } }; int n; - struct timeval tv = { - .tv_sec = 1, - .tv_usec = 0, - }; n = write(fd, request, requestlen); if (n < 0) { @@ -127,11 +123,8 @@ static int tpm_util_request(int fd, return -EFAULT; } - FD_ZERO(&readfds); - FD_SET(fd, &readfds); - /* wait for a second */ - n = select(fd + 1, &readfds, NULL, NULL, &tv); + n = RETRY_ON_EINTR(g_poll(fds, 1, 1000)); if (n != 1) { return -errno; } diff --git a/backends/tpm/trace-events b/backends/tpm/trace-events index 3298766dd7..1ecef42a07 100644 --- a/backends/tpm/trace-events +++ b/backends/tpm/trace-events @@ -20,6 +20,8 @@ tpm_emulator_set_buffer_size(uint32_t buffersize, uint32_t minsize, uint32_t max tpm_emulator_startup_tpm_resume(bool is_resume, size_t buffersize) "is_resume: %d, buffer size: %zu" tpm_emulator_get_tpm_established_flag(uint8_t flag) "got established flag: %d" tpm_emulator_cancel_cmd_not_supt(void) "Backend does not support CANCEL_TPM_CMD" +tpm_emulator_lock_storage_cmd_not_supt(void) "Backend does not support LOCK_STORAGE" +tpm_emulator_vm_state_change(int running, int state) "state change to running %d state %d" tpm_emulator_handle_device_opts_tpm12(void) "TPM Version 1.2" tpm_emulator_handle_device_opts_tpm2(void) "TPM Version 2" tpm_emulator_handle_device_opts_unspec(void) "TPM Version Unspecified" diff --git a/backends/trace-events b/backends/trace-events index 652eb76a57..d45c6e31a6 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -5,3 +5,13 @@ 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" + +# iommufd.c +iommufd_backend_connect(int fd, bool owned, uint32_t users, int ret) "fd=%d owned=%d users=%d (%d)" +iommufd_backend_disconnect(int fd, uint32_t users) "fd=%d users=%d" +iommu_backend_set_fd(int fd) "pre-opened /dev/iommu fd=%d" +iommufd_backend_map_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, void *vaddr, bool readonly, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" addr=%p readonly=%d (%d)" +iommufd_backend_unmap_dma_non_exist(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " Unmap nonexistent mapping: iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)" +iommufd_backend_unmap_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)" +iommufd_backend_alloc_ioas(int iommufd, uint32_t ioas, int ret) " iommufd=%d ioas=%d (%d)" +iommufd_backend_free_id(int iommufd, uint32_t id, int ret) " iommufd=%d id=%d (%d)" diff --git a/backends/vhost-user.c b/backends/vhost-user.c index 10b39992d2..94c6a82d52 100644 --- a/backends/vhost-user.c +++ b/backends/vhost-user.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qom/object_interfaces.h" #include "sysemu/vhost-user-backend.h" @@ -21,12 +20,6 @@ #include "io/channel-command.h" #include "hw/virtio/virtio-bus.h" -static bool -ioeventfd_enabled(void) -{ - return kvm_enabled() && kvm_eventfds_enabled(); -} - int vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev, unsigned nvqs, Error **errp) @@ -35,11 +28,6 @@ vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev, assert(!b->vdev && vdev); - if (!ioeventfd_enabled()) { - error_setg(errp, "vhost initialization failed: requires kvm"); - return -1; - } - if (!vhost_user_init(&b->vhost_user, &b->chr, errp)) { return -1; } @@ -85,7 +73,7 @@ vhost_user_backend_start(VhostUserBackend *b) } b->dev.acked_features = b->vdev->guest_features; - ret = vhost_dev_start(&b->dev, b->vdev); + ret = vhost_dev_start(&b->dev, b->vdev, true); if (ret < 0) { error_report("Error start vhost dev"); goto err_guest_notifiers; @@ -120,7 +108,7 @@ vhost_user_backend_stop(VhostUserBackend *b) return; } - vhost_dev_stop(&b->dev, b->vdev); + vhost_dev_stop(&b->dev, b->vdev, true); if (k->set_guest_notifiers) { ret = k->set_guest_notifiers(qbus->parent, @@ -141,7 +129,7 @@ static void set_chardev(Object *obj, const char *value, Error **errp) Chardev *chr; if (b->completed) { - error_setg(errp, QERR_PERMISSION_DENIED); + error_setg(errp, "Property 'chardev' can no longer be set"); return; } diff --git a/block.c b/block.c index 2c00dddd80..468cf5e67d 100644 --- a/block.c +++ b/block.c @@ -27,6 +27,7 @@ #include "block/trace.h" #include "block/block_int.h" #include "block/blockjob.h" +#include "block/dirty-bitmap.h" #include "block/fuse.h" #include "block/nbd.h" #include "block/qdict.h" @@ -90,15 +91,11 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, static bool bdrv_recurse_has_child(BlockDriverState *bs, BlockDriverState *child); -static void bdrv_child_free(BdrvChild *child); -static void bdrv_replace_child_noperm(BdrvChild **child, - BlockDriverState *new_bs, - bool free_empty_child); -static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, - BdrvChild *child, - Transaction *tran); -static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs, - Transaction *tran); +static void GRAPH_WRLOCK +bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs); + +static void GRAPH_WRLOCK +bdrv_remove_child(BdrvChild *child, Transaction *tran); static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, @@ -108,6 +105,10 @@ static void bdrv_reopen_abort(BDRVReopenState *reopen_state); static bool bdrv_backing_overridden(BlockDriverState *bs); +static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp); + /* If non-zero, use only whitelisted block drivers */ static int use_bdrv_whitelist; @@ -278,8 +279,9 @@ bool bdrv_is_read_only(BlockDriverState *bs) return !(bs->open_flags & BDRV_O_RDWR); } -int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, - bool ignore_allow_rdw, Error **errp) +static int GRAPH_RDLOCK +bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, + bool ignore_allow_rdw, Error **errp) { IO_CODE(); @@ -370,8 +372,9 @@ char *bdrv_get_full_backing_filename_from_filename(const char *backed, * setting @errp. In all other cases, NULL will only be returned with * @errp set. */ -static char *bdrv_make_absolute_filename(BlockDriverState *relative_to, - const char *filename, Error **errp) +static char * GRAPH_RDLOCK +bdrv_make_absolute_filename(BlockDriverState *relative_to, + const char *filename, Error **errp) { char *dir, *full_name; @@ -416,7 +419,7 @@ BlockDriverState *bdrv_new(void) for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { QLIST_INIT(&bs->op_blockers[i]); } - qemu_co_mutex_init(&bs->reqs_lock); + qemu_mutex_init(&bs->reqs_lock); qemu_mutex_init(&bs->dirty_bitmap_mutex); bs->refcnt = 1; bs->aio_context = qemu_get_aio_context(); @@ -464,12 +467,18 @@ BlockDriver *bdrv_find_format(const char *format_name) /* The driver isn't registered, maybe we need to load a module */ for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) { if (!strcmp(block_driver_modules[i].format_name, format_name)) { - block_module_load_one(block_driver_modules[i].library_name); + Error *local_err = NULL; + int rv = block_module_load(block_driver_modules[i].library_name, + &local_err); + if (rv > 0) { + return bdrv_do_find_format(format_name); + } else if (rv < 0) { + error_report_err(local_err); + } break; } } - - return bdrv_do_find_format(format_name); + return NULL; } static int bdrv_format_is_whitelisted(const char *format_name, bool read_only) @@ -522,65 +531,24 @@ typedef struct CreateCo { Error *err; } CreateCo; -static void coroutine_fn bdrv_create_co_entry(void *opaque) +int coroutine_fn bdrv_co_create(BlockDriver *drv, const char *filename, + QemuOpts *opts, Error **errp) { - Error *local_err = NULL; + ERRP_GUARD(); int ret; - - CreateCo *cco = opaque; - assert(cco->drv); GLOBAL_STATE_CODE(); - ret = cco->drv->bdrv_co_create_opts(cco->drv, - cco->filename, cco->opts, &local_err); - error_propagate(&cco->err, local_err); - cco->ret = ret; -} - -int bdrv_create(BlockDriver *drv, const char* filename, - QemuOpts *opts, Error **errp) -{ - int ret; - - GLOBAL_STATE_CODE(); - - Coroutine *co; - CreateCo cco = { - .drv = drv, - .filename = g_strdup(filename), - .opts = opts, - .ret = NOT_DONE, - .err = NULL, - }; - if (!drv->bdrv_co_create_opts) { - error_setg(errp, "Driver '%s' does not support image creation", drv->format_name); - ret = -ENOTSUP; - goto out; + error_setg(errp, "Driver '%s' does not support image creation", + drv->format_name); + return -ENOTSUP; } - if (qemu_in_coroutine()) { - /* Fast-path if already in coroutine context */ - bdrv_create_co_entry(&cco); - } else { - co = qemu_coroutine_create(bdrv_create_co_entry, &cco); - qemu_coroutine_enter(co); - while (cco.ret == NOT_DONE) { - aio_poll(qemu_get_aio_context(), true); - } + ret = drv->bdrv_co_create_opts(drv, filename, opts, errp); + if (ret < 0 && !*errp) { + error_setg_errno(errp, -ret, "Could not create image"); } - ret = cco.ret; - if (ret < 0) { - if (cco.err) { - error_propagate(errp, cco.err); - } else { - error_setg_errno(errp, -ret, "Could not create image"); - } - } - -out: - g_free(cco.filename); return ret; } @@ -591,8 +559,9 @@ out: * 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) +static int64_t coroutine_fn GRAPH_UNLOCKED +create_file_fallback_truncate(BlockBackend *blk, int64_t minimum_size, + Error **errp) { Error *local_err = NULL; int64_t size; @@ -600,14 +569,14 @@ static int64_t create_file_fallback_truncate(BlockBackend *blk, GLOBAL_STATE_CODE(); - ret = blk_truncate(blk, minimum_size, false, PREALLOC_MODE_OFF, 0, - &local_err); + ret = blk_co_truncate(blk, minimum_size, false, PREALLOC_MODE_OFF, 0, + &local_err); if (ret < 0 && ret != -ENOTSUP) { error_propagate(errp, local_err); return ret; } - size = blk_getlength(blk); + size = blk_co_getlength(blk); if (size < 0) { error_free(local_err); error_setg_errno(errp, -size, @@ -631,9 +600,10 @@ static int64_t create_file_fallback_truncate(BlockBackend *blk, * 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) +static int coroutine_fn +create_file_fallback_zero_first_sector(BlockBackend *blk, + int64_t current_size, + Error **errp) { int64_t bytes_to_clear; int ret; @@ -642,7 +612,7 @@ static int create_file_fallback_zero_first_sector(BlockBackend *blk, 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); + ret = blk_co_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"); @@ -663,6 +633,7 @@ int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, QemuOpts *opts, Error **errp) { + ERRP_GUARD(); BlockBackend *blk; QDict *options; int64_t size = 0; @@ -692,11 +663,13 @@ int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, 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); + blk = blk_co_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: ", + error_prepend(errp, "Protocol driver '%s' does not support creating " + "new images, so an existing image must be selected as " + "the target; however, opening the given target as an " + "existing image failed: ", drv->format_name); return -EINVAL; } @@ -714,11 +687,12 @@ int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, ret = 0; out: - blk_unref(blk); + blk_co_unref(blk); return ret; } -int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp) +int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts, + Error **errp) { QemuOpts *protocol_opts; BlockDriver *drv; @@ -759,7 +733,7 @@ int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp) goto out; } - ret = bdrv_create(drv, filename, protocol_opts, errp); + ret = bdrv_co_create(drv, filename, protocol_opts, errp); out: qemu_opts_del(protocol_opts); qobject_unref(qdict); @@ -773,6 +747,7 @@ int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp) IO_CODE(); assert(bs != NULL); + assert_bdrv_graph_readable(); if (!bs->drv) { error_setg(errp, "Block node '%s' is not opened", bs->filename); @@ -846,12 +821,17 @@ int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo) { BlockDriver *drv = bs->drv; - BlockDriverState *filtered = bdrv_filter_bs(bs); + BlockDriverState *filtered; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (drv && drv->bdrv_probe_geometry) { return drv->bdrv_probe_geometry(bs, geo); - } else if (filtered) { + } + + filtered = bdrv_filter_bs(bs); + if (filtered) { return bdrv_probe_geometry(filtered, geo); } @@ -860,38 +840,42 @@ int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo) /* * Create a uniquely-named empty temporary file. - * Return 0 upon success, otherwise a negative errno value. + * Return the actual file name used upon success, otherwise NULL. + * This string should be freed with g_free() when not needed any longer. + * + * Note: creating a temporary file for the caller to (re)open is + * inherently racy. Use g_file_open_tmp() instead whenever practical. */ -int get_tmp_filename(char *filename, int size) +char *create_tmp_file(Error **errp) { -#ifdef _WIN32 - char temp_dir[MAX_PATH]; - /* GetTempFileName requires that its output buffer (4th param) - have length MAX_PATH or greater. */ - assert(size >= MAX_PATH); - return (GetTempPath(MAX_PATH, temp_dir) - && GetTempFileName(temp_dir, "qem", 0, filename) - ? 0 : -GetLastError()); -#else int fd; const char *tmpdir; - tmpdir = getenv("TMPDIR"); - if (!tmpdir) { + g_autofree char *filename = NULL; + + tmpdir = g_get_tmp_dir(); +#ifndef _WIN32 + /* + * See commit 69bef79 ("block: use /var/tmp instead of /tmp for -snapshot") + * + * This function is used to create temporary disk images (like -snapshot), + * so the files can become very large. /tmp is often a tmpfs where as + * /var/tmp is usually on a disk, so more appropriate for disk images. + */ + if (!g_strcmp0(tmpdir, "/tmp")) { tmpdir = "/var/tmp"; } - if (snprintf(filename, size, "%s/vl.XXXXXX", tmpdir) >= size) { - return -EOVERFLOW; - } - fd = mkstemp(filename); - if (fd < 0) { - return -errno; - } - if (close(fd) != 0) { - unlink(filename); - return -errno; - } - return 0; #endif + + filename = g_strdup_printf("%s/vl.XXXXXX", tmpdir); + fd = g_mkstemp(filename); + if (fd < 0) { + error_setg_errno(errp, errno, "Could not open temporary file '%s'", + filename); + return NULL; + } + close(fd); + + return g_steal_pointer(&filename); } /* @@ -976,12 +960,16 @@ BlockDriver *bdrv_find_protocol(const char *filename, for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) { if (block_driver_modules[i].protocol_name && !strcmp(block_driver_modules[i].protocol_name, protocol)) { - block_module_load_one(block_driver_modules[i].library_name); + int rv = block_module_load(block_driver_modules[i].library_name, errp); + if (rv > 0) { + drv1 = bdrv_do_find_protocol(protocol); + } else if (rv < 0) { + return NULL; + } break; } } - drv1 = bdrv_do_find_protocol(protocol); if (!drv1) { error_setg(errp, "Unknown protocol '%s'", protocol); } @@ -1037,7 +1025,7 @@ static int find_image_format(BlockBackend *file, const char *filename, return ret; } - ret = blk_pread(file, 0, buf, sizeof(buf)); + ret = blk_pread(file, 0, sizeof(buf), buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read image for determining its " "format"); @@ -1045,36 +1033,40 @@ static int find_image_format(BlockBackend *file, const char *filename, return ret; } - drv = bdrv_probe_all(buf, ret, filename); + drv = bdrv_probe_all(buf, sizeof(buf), filename); if (!drv) { error_setg(errp, "Could not determine image format: No compatible " "driver found"); - ret = -ENOENT; + *pdrv = NULL; + return -ENOENT; } + *pdrv = drv; - return ret; + return 0; } /** * Set the current 'total_sectors' value * Return 0 on success, -errno on error. */ -int refresh_total_sectors(BlockDriverState *bs, int64_t hint) +int coroutine_fn bdrv_co_refresh_total_sectors(BlockDriverState *bs, + int64_t hint) { BlockDriver *drv = bs->drv; IO_CODE(); + assert_bdrv_graph_readable(); if (!drv) { return -ENOMEDIUM; } - /* Do not attempt drv->bdrv_getlength() on scsi-generic devices */ + /* Do not attempt drv->bdrv_co_getlength() on scsi-generic devices */ if (bdrv_is_sg(bs)) return 0; /* query actual device if possible, otherwise just trust the hint */ - if (drv->bdrv_getlength) { - int64_t length = drv->bdrv_getlength(bs); + if (drv->bdrv_co_getlength) { + int64_t length = drv->bdrv_co_getlength(bs); if (length < 0) { return length; } @@ -1208,23 +1200,22 @@ static char *bdrv_child_get_parent_desc(BdrvChild *c) return g_strdup_printf("node '%s'", bdrv_get_node_name(parent)); } -static void bdrv_child_cb_drained_begin(BdrvChild *child) +static void GRAPH_RDLOCK bdrv_child_cb_drained_begin(BdrvChild *child) { BlockDriverState *bs = child->opaque; - bdrv_do_drained_begin_quiesce(bs, NULL, false); + bdrv_do_drained_begin_quiesce(bs, NULL); } -static bool bdrv_child_cb_drained_poll(BdrvChild *child) +static bool GRAPH_RDLOCK bdrv_child_cb_drained_poll(BdrvChild *child) { BlockDriverState *bs = child->opaque; - return bdrv_drain_poll(bs, false, NULL, false); + return bdrv_drain_poll(bs, NULL, false); } -static void bdrv_child_cb_drained_end(BdrvChild *child, - int *drained_end_counter) +static void GRAPH_RDLOCK bdrv_child_cb_drained_end(BdrvChild *child) { BlockDriverState *bs = child->opaque; - bdrv_drained_end_no_poll(bs, drained_end_counter); + bdrv_drained_end(bs); } static int bdrv_child_cb_inactivate(BdrvChild *child) @@ -1235,18 +1226,12 @@ static int bdrv_child_cb_inactivate(BdrvChild *child) return 0; } -static bool bdrv_child_cb_can_set_aio_ctx(BdrvChild *child, AioContext *ctx, - GSList **ignore, Error **errp) +static bool bdrv_child_cb_change_aio_ctx(BdrvChild *child, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp) { BlockDriverState *bs = child->opaque; - return bdrv_can_set_aio_context(bs, ctx, ignore, errp); -} - -static void bdrv_child_cb_set_aio_ctx(BdrvChild *child, AioContext *ctx, - GSList **ignore) -{ - BlockDriverState *bs = child->opaque; - return bdrv_set_aio_context_ignore(bs, ctx, ignore); + return bdrv_change_aio_context(bs, ctx, visited, tran, errp); } /* @@ -1273,7 +1258,7 @@ static void bdrv_temp_snapshot_options(int *child_flags, QDict *child_options, *child_flags &= ~BDRV_O_NATIVE_AIO; } -static void bdrv_backing_attach(BdrvChild *c) +static void GRAPH_WRLOCK bdrv_backing_attach(BdrvChild *c) { BlockDriverState *parent = c->opaque; BlockDriverState *backing_hd = c->bs; @@ -1325,11 +1310,14 @@ static void bdrv_backing_detach(BdrvChild *c) } static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, - const char *filename, Error **errp) + const char *filename, + bool backing_mask_protocol, + Error **errp) { BlockDriverState *parent = c->opaque; bool read_only = bdrv_is_read_only(parent); int ret; + const char *format_name; GLOBAL_STATE_CODE(); if (read_only) { @@ -1339,9 +1327,23 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, } } - ret = bdrv_change_backing_file(parent, filename, - base->drv ? base->drv->format_name : "", - false); + if (base->drv) { + /* + * If the new base image doesn't have a format driver layer, which we + * detect by the fact that @base is a protocol driver, we record + * 'raw' as the format instead of putting the protocol name as the + * backing format + */ + if (backing_mask_protocol && base->drv->protocol_name) { + format_name = "raw"; + } else { + format_name = base->drv->format_name; + } + } else { + format_name = ""; + } + + ret = bdrv_change_backing_file(parent, filename, format_name, false); if (ret < 0) { error_setg_errno(errp, -ret, "Could not update backing file link"); } @@ -1434,21 +1436,49 @@ static void bdrv_inherited_options(BdrvChildRole role, bool parent_is_format, *child_flags = flags; } -static void bdrv_child_cb_attach(BdrvChild *child) +static void GRAPH_WRLOCK bdrv_child_cb_attach(BdrvChild *child) { BlockDriverState *bs = child->opaque; - assert_bdrv_graph_writable(bs); + assert_bdrv_graph_writable(); QLIST_INSERT_HEAD(&bs->children, child, next); + if (bs->drv->is_filter || (child->role & BDRV_CHILD_FILTERED)) { + /* + * Here we handle filters and block/raw-format.c when it behave like + * filter. They generally have a single PRIMARY child, which is also the + * FILTERED child, and that they may have multiple more children, which + * are neither PRIMARY nor FILTERED. And never we have a COW child here. + * So bs->file will be the PRIMARY child, unless the PRIMARY child goes + * into bs->backing on exceptional cases; and bs->backing will be + * nothing else. + */ + assert(!(child->role & BDRV_CHILD_COW)); + if (child->role & BDRV_CHILD_PRIMARY) { + assert(child->role & BDRV_CHILD_FILTERED); + assert(!bs->backing); + assert(!bs->file); - if (child->role & BDRV_CHILD_COW) { + if (bs->drv->filtered_child_is_backing) { + bs->backing = child; + } else { + bs->file = child; + } + } else { + assert(!(child->role & BDRV_CHILD_FILTERED)); + } + } else if (child->role & BDRV_CHILD_COW) { + assert(bs->drv->supports_backing); + assert(!(child->role & BDRV_CHILD_PRIMARY)); + assert(!bs->backing); + bs->backing = child; bdrv_backing_attach(child); + } else if (child->role & BDRV_CHILD_PRIMARY) { + assert(!bs->file); + bs->file = child; } - - bdrv_apply_subtree_drain(child, bs); } -static void bdrv_child_cb_detach(BdrvChild *child) +static void GRAPH_WRLOCK bdrv_child_cb_detach(BdrvChild *child) { BlockDriverState *bs = child->opaque; @@ -1456,17 +1486,25 @@ static void bdrv_child_cb_detach(BdrvChild *child) bdrv_backing_detach(child); } - bdrv_unapply_subtree_drain(child, bs); - - assert_bdrv_graph_writable(bs); + assert_bdrv_graph_writable(); QLIST_REMOVE(child, next); + if (child == bs->backing) { + assert(child != bs->file); + bs->backing = NULL; + } else if (child == bs->file) { + bs->file = NULL; + } } static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base, - const char *filename, Error **errp) + const char *filename, + bool backing_mask_protocol, + Error **errp) { if (c->role & BDRV_CHILD_COW) { - return bdrv_backing_update_filename(c, base, filename, errp); + return bdrv_backing_update_filename(c, base, filename, + backing_mask_protocol, + errp); } return 0; } @@ -1489,15 +1527,14 @@ const BdrvChildClass child_of_bds = { .attach = bdrv_child_cb_attach, .detach = bdrv_child_cb_detach, .inactivate = bdrv_child_cb_inactivate, - .can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx, - .set_aio_ctx = bdrv_child_cb_set_aio_ctx, + .change_aio_ctx = bdrv_child_cb_change_aio_ctx, .update_filename = bdrv_child_cb_update_filename, .get_parent_aio_context = child_of_bds_get_parent_aio_context, }; AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c) { - GLOBAL_STATE_CODE(); + IO_CODE(); return c->klass->get_parent_aio_context(c); } @@ -1601,9 +1638,9 @@ out: g_free(gen_node_name); } -static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, - const char *node_name, QDict *options, - int open_flags, Error **errp) +static int no_coroutine_fn GRAPH_UNLOCKED +bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, + QDict *options, int open_flags, Error **errp) { Error *local_err = NULL; int i, ret; @@ -1638,13 +1675,30 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, goto open_failed; } - ret = refresh_total_sectors(bs, bs->total_sectors); + assert(!(bs->supported_read_flags & ~BDRV_REQ_MASK)); + assert(!(bs->supported_write_flags & ~BDRV_REQ_MASK)); + + /* + * Always allow the BDRV_REQ_REGISTERED_BUF optimization hint. This saves + * drivers that pass read/write requests through to a child the trouble of + * declaring support explicitly. + * + * Drivers must not propagate this flag accidentally when they initiate I/O + * to a bounce buffer. That case should be rare though. + */ + bs->supported_read_flags |= BDRV_REQ_REGISTERED_BUF; + bs->supported_write_flags |= BDRV_REQ_REGISTERED_BUF; + + ret = bdrv_refresh_total_sectors(bs, bs->total_sectors); if (ret < 0) { error_setg_errno(errp, -ret, "Could not refresh total sector count"); return ret; } + bdrv_graph_rdlock_main_loop(); bdrv_refresh_limits(bs, NULL, &local_err); + bdrv_graph_rdunlock_main_loop(); + if (local_err) { error_propagate(errp, local_err); return -EINVAL; @@ -1655,18 +1709,22 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, assert(is_power_of_2(bs->bl.request_alignment)); for (i = 0; i < bs->quiesce_counter; i++) { - if (drv->bdrv_co_drain_begin) { - drv->bdrv_co_drain_begin(bs); + if (drv->bdrv_drain_begin) { + drv->bdrv_drain_begin(bs); } } return 0; open_failed: bs->drv = NULL; + + bdrv_graph_wrlock(); if (bs->file != NULL) { bdrv_unref_child(bs, bs->file); - bs->file = NULL; + assert(!bs->file); } + bdrv_graph_wrunlock(); + g_free(bs->opaque); bs->opaque = NULL; return ret; @@ -1808,9 +1866,12 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, Error *local_err = NULL; bool ro; + GLOBAL_STATE_CODE(); + + bdrv_graph_rdlock_main_loop(); assert(bs->file == NULL); assert(options != NULL && bs->options != options); - GLOBAL_STATE_CODE(); + bdrv_graph_rdunlock_main_loop(); opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); if (!qemu_opts_absorb_qdict(opts, options, errp)) { @@ -1835,7 +1896,10 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, } if (file != NULL) { + bdrv_graph_rdlock_main_loop(); bdrv_refresh_filename(blk_bs(file)); + bdrv_graph_rdunlock_main_loop(); + filename = blk_bs(file)->filename; } else { /* @@ -1862,7 +1926,9 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, ro)) { if (!ro && bdrv_is_whitelisted(drv, true)) { + bdrv_graph_rdlock_main_loop(); ret = bdrv_apply_auto_read_only(bs, NULL, NULL); + bdrv_graph_rdunlock_main_loop(); } else { ret = -ENOTSUP; } @@ -1933,6 +1999,7 @@ fail_opts: static QDict *parse_json_filename(const char *filename, Error **errp) { + ERRP_GUARD(); QObject *options_obj; QDict *options; int ret; @@ -2080,7 +2147,6 @@ static int bdrv_fill_options(QDict **options, const char *filename, typedef struct BlockReopenQueueEntry { bool prepared; - bool perms_checked; BDRVReopenState state; QTAILQ_ENTRY(BlockReopenQueueEntry) entry; } BlockReopenQueueEntry; @@ -2166,7 +2232,8 @@ static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp) return false; } -static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp) +static bool GRAPH_RDLOCK +bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp) { BdrvChild *a, *b; GLOBAL_STATE_CODE(); @@ -2191,11 +2258,12 @@ static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp) return false; } -static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, - BdrvChild *c, BdrvChildRole role, - BlockReopenQueue *reopen_queue, - uint64_t parent_perm, uint64_t parent_shared, - uint64_t *nperm, uint64_t *nshared) +static void GRAPH_RDLOCK +bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, + BdrvChild *c, BdrvChildRole role, + BlockReopenQueue *reopen_queue, + uint64_t parent_perm, uint64_t parent_shared, + uint64_t *nperm, uint64_t *nshared) { assert(bs->drv && bs->drv->bdrv_child_perm); GLOBAL_STATE_CODE(); @@ -2219,8 +2287,8 @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, * simplest way to satisfy this criteria: use only result of * bdrv_topological_dfs() or NULL as @list parameter. */ -static GSList *bdrv_topological_dfs(GSList *list, GHashTable *found, - BlockDriverState *bs) +static GSList * GRAPH_RDLOCK +bdrv_topological_dfs(GSList *list, GHashTable *found, BlockDriverState *bs) { BdrvChild *child; g_autoptr(GHashTable) local_found = NULL; @@ -2283,7 +2351,7 @@ static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, tran_add(tran, &bdrv_child_set_pem_drv, s); } -static void bdrv_drv_set_perm_commit(void *opaque) +static void GRAPH_RDLOCK bdrv_drv_set_perm_commit(void *opaque) { BlockDriverState *bs = opaque; uint64_t cumulative_perms, cumulative_shared_perms; @@ -2296,7 +2364,7 @@ static void bdrv_drv_set_perm_commit(void *opaque) } } -static void bdrv_drv_set_perm_abort(void *opaque) +static void GRAPH_RDLOCK bdrv_drv_set_perm_abort(void *opaque) { BlockDriverState *bs = opaque; GLOBAL_STATE_CODE(); @@ -2311,9 +2379,13 @@ TransactionActionDrv bdrv_drv_set_perm_drv = { .commit = bdrv_drv_set_perm_commit, }; -static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm, - uint64_t shared_perm, Transaction *tran, - Error **errp) +/* + * After calling this function, the transaction @tran may only be completed + * while holding a reader lock for the graph. + */ +static int GRAPH_RDLOCK +bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, + Transaction *tran, Error **errp) { GLOBAL_STATE_CODE(); if (!bs->drv) { @@ -2336,56 +2408,42 @@ static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm, typedef struct BdrvReplaceChildState { BdrvChild *child; - BdrvChild **childp; BlockDriverState *old_bs; - bool free_empty_child; } BdrvReplaceChildState; -static void bdrv_replace_child_commit(void *opaque) +static void GRAPH_WRLOCK bdrv_replace_child_commit(void *opaque) { BdrvReplaceChildState *s = opaque; GLOBAL_STATE_CODE(); - if (s->free_empty_child && !s->child->bs) { - bdrv_child_free(s->child); - } - bdrv_unref(s->old_bs); + bdrv_schedule_unref(s->old_bs); } -static void bdrv_replace_child_abort(void *opaque) +static void GRAPH_WRLOCK bdrv_replace_child_abort(void *opaque) { BdrvReplaceChildState *s = opaque; BlockDriverState *new_bs = s->child->bs; GLOBAL_STATE_CODE(); - /* - * old_bs reference is transparently moved from @s to s->child. - * - * Pass &s->child here instead of s->childp, because: - * (1) s->old_bs must be non-NULL, so bdrv_replace_child_noperm() will not - * modify the BdrvChild * pointer we indirectly pass to it, i.e. it - * will not modify s->child. From that perspective, it does not matter - * whether we pass s->childp or &s->child. - * (2) If new_bs is not NULL, s->childp will be NULL. We then cannot use - * it here. - * (3) If new_bs is NULL, *s->childp will have been NULLed by - * bdrv_replace_child_tran()'s bdrv_replace_child_noperm() call, and we - * must not pass a NULL *s->childp here. - * - * So whether new_bs was NULL or not, we cannot pass s->childp here; and in - * any case, there is no reason to pass it anyway. - */ - bdrv_replace_child_noperm(&s->child, s->old_bs, true); - /* - * The child was pre-existing, so s->old_bs must be non-NULL, and - * s->child thus must not have been freed - */ - assert(s->child != NULL); - if (!new_bs) { - /* As described above, *s->childp was cleared, so restore it */ - assert(s->childp != NULL); - *s->childp = s->child; + assert_bdrv_graph_writable(); + + /* old_bs reference is transparently moved from @s to @s->child */ + if (!s->child->bs) { + /* + * The parents were undrained when removing old_bs from the child. New + * requests can't have been made, though, because the child was empty. + * + * TODO Make bdrv_replace_child_noperm() transactionable to avoid + * undraining the parent in the first place. Once this is done, having + * new_bs drained when calling bdrv_replace_child_tran() is not a + * requirement any more. + */ + bdrv_parent_drained_begin_single(s->child); + assert(!bdrv_parent_drained_poll_single(s->child)); } + assert(s->child->quiesced_parent); + bdrv_replace_child_noperm(s->child, s->old_bs); + bdrv_unref(new_bs); } @@ -2400,55 +2458,47 @@ static TransactionActionDrv bdrv_replace_child_drv = { * * Note: real unref of old_bs is done only on commit. * + * Both @child->bs and @new_bs (if non-NULL) must be drained. @new_bs must be + * kept drained until the transaction is completed. + * + * After calling this function, the transaction @tran may only be completed + * while holding a writer lock for the graph. + * * The function doesn't update permissions, caller is responsible for this. - * - * (*childp)->bs must not be NULL. - * - * Note that if new_bs == NULL, @childp is stored in a state object attached - * to @tran, so that the old child can be reinstated in the abort handler. - * Therefore, if @new_bs can be NULL, @childp must stay valid until the - * transaction is committed or aborted. - * - * If @free_empty_child is true and @new_bs is NULL, the BdrvChild is - * freed (on commit). @free_empty_child should only be false if the - * caller will free the BDrvChild themselves (which may be important - * if this is in turn called in another transactional context). */ -static void bdrv_replace_child_tran(BdrvChild **childp, - BlockDriverState *new_bs, - Transaction *tran, - bool free_empty_child) +static void GRAPH_WRLOCK +bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs, + Transaction *tran) { BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1); + + assert(child->quiesced_parent); + assert(!new_bs || new_bs->quiesce_counter); + *s = (BdrvReplaceChildState) { - .child = *childp, - .childp = new_bs == NULL ? childp : NULL, - .old_bs = (*childp)->bs, - .free_empty_child = free_empty_child, + .child = child, + .old_bs = child->bs, }; tran_add(tran, &bdrv_replace_child_drv, s); - /* The abort handler relies on this */ - assert(s->old_bs != NULL); - if (new_bs) { bdrv_ref(new_bs); } - /* - * Pass free_empty_child=false, we will free the child (if - * necessary) in bdrv_replace_child_commit() (if our - * @free_empty_child parameter was true). - */ - bdrv_replace_child_noperm(childp, new_bs, false); - /* old_bs reference is transparently moved from *childp to @s */ + + bdrv_replace_child_noperm(child, new_bs); + /* old_bs reference is transparently moved from @child to @s */ } /* * Refresh permissions in @bs subtree. The function is intended to be called * after some graph modification that was done without permission update. + * + * After calling this function, the transaction @tran may only be completed + * while holding a reader lock for the graph. */ -static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q, - Transaction *tran, Error **errp) +static int GRAPH_RDLOCK +bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q, + Transaction *tran, Error **errp) { BlockDriver *drv = bs->drv; BdrvChild *c; @@ -2518,8 +2568,16 @@ static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q, return 0; } -static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, - Transaction *tran, Error **errp) +/* + * @list is a product of bdrv_topological_dfs() (may be called several times) - + * a topologically sorted subgraph. + * + * After calling this function, the transaction @tran may only be completed + * while holding a reader lock for the graph. + */ +static int GRAPH_RDLOCK +bdrv_do_refresh_perms(GSList *list, BlockReopenQueue *q, Transaction *tran, + Error **errp) { int ret; BlockDriverState *bs; @@ -2541,6 +2599,28 @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, return 0; } +/* + * @list is any list of nodes. List is completed by all subtrees and + * topologically sorted. It's not a problem if some node occurs in the @list + * several times. + * + * After calling this function, the transaction @tran may only be completed + * while holding a reader lock for the graph. + */ +static int GRAPH_RDLOCK +bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, Transaction *tran, + Error **errp) +{ + g_autoptr(GHashTable) found = g_hash_table_new(NULL, NULL); + g_autoptr(GSList) refresh_list = NULL; + + for ( ; list; list = list->next) { + refresh_list = bdrv_topological_dfs(refresh_list, found, list->data); + } + + return bdrv_do_refresh_perms(refresh_list, q, tran, errp); +} + void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, uint64_t *shared_perm) { @@ -2588,15 +2668,29 @@ char *bdrv_perm_names(uint64_t perm) } -static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp) +/* + * @tran is allowed to be NULL. In this case no rollback is possible. + * + * After calling this function, the transaction @tran may only be completed + * while holding a reader lock for the graph. + */ +static int GRAPH_RDLOCK +bdrv_refresh_perms(BlockDriverState *bs, Transaction *tran, Error **errp) { int ret; - Transaction *tran = tran_new(); + Transaction *local_tran = NULL; g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); GLOBAL_STATE_CODE(); - ret = bdrv_list_refresh_perms(list, NULL, tran, errp); - tran_finalize(tran, ret); + if (!tran) { + tran = local_tran = tran_new(); + } + + ret = bdrv_do_refresh_perms(list, NULL, tran, errp); + + if (local_tran) { + tran_finalize(local_tran, ret); + } return ret; } @@ -2612,7 +2706,7 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, bdrv_child_set_perm(c, perm, shared, tran); - ret = bdrv_refresh_perms(c->bs, &local_err); + ret = bdrv_refresh_perms(c->bs, tran, &local_err); tran_finalize(tran, ret); @@ -2821,29 +2915,41 @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm) return permissions[qapi_perm]; } -/** - * Replace (*childp)->bs by @new_bs. +/* + * Replaces the node that a BdrvChild points to without updating permissions. * - * If @new_bs is NULL, *childp will be set to NULL, too: BDS parents - * generally cannot handle a BdrvChild with .bs == NULL, so clearing - * BdrvChild.bs should generally immediately be followed by the - * BdrvChild pointer being cleared as well. - * - * If @free_empty_child is true and @new_bs is NULL, the BdrvChild is - * freed. @free_empty_child should only be false if the caller will - * free the BdrvChild themselves (this may be important in a - * transactional context, where it may only be freed on commit). + * If @new_bs is non-NULL, the parent of @child must already be drained through + * @child. */ -static void bdrv_replace_child_noperm(BdrvChild **childp, - BlockDriverState *new_bs, - bool free_empty_child) +static void GRAPH_WRLOCK +bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs) { - BdrvChild *child = *childp; BlockDriverState *old_bs = child->bs; int new_bs_quiesce_counter; - int drain_saldo; assert(!child->frozen); + + /* + * If we want to change the BdrvChild to point to a drained node as its new + * child->bs, we need to make sure that its new parent is drained, too. In + * other words, either child->quiesce_parent must already be true or we must + * be able to set it and keep the parent's quiesce_counter consistent with + * that, but without polling or starting new requests (this function + * guarantees that it doesn't poll, and starting new requests would be + * against the invariants of drain sections). + * + * To keep things simple, we pick the first option (child->quiesce_parent + * must already be true). We also generalise the rule a bit to make it + * easier to verify in callers and more likely to be covered in test cases: + * The parent must be quiesced through this child even if new_bs isn't + * currently drained. + * + * The only exception is for callers that always pass new_bs == NULL. In + * this case, we obviously never need to consider the case of a drained + * new_bs, so we can keep the callers simpler by allowing them not to drain + * the parent. + */ + assert(!new_bs || child->quiesced_parent); assert(old_bs != new_bs); GLOBAL_STATE_CODE(); @@ -2851,66 +2957,30 @@ static void bdrv_replace_child_noperm(BdrvChild **childp, assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)); } - new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0); - drain_saldo = new_bs_quiesce_counter - child->parent_quiesce_counter; - - /* - * If the new child node is drained but the old one was not, flush - * all outstanding requests to the old child node. - */ - while (drain_saldo > 0 && child->klass->drained_begin) { - bdrv_parent_drained_begin_single(child, true); - drain_saldo--; - } - if (old_bs) { - /* Detach first so that the recursive drain sections coming from @child - * are already gone and we only end the drain sections that came from - * elsewhere. */ if (child->klass->detach) { child->klass->detach(child); } - assert_bdrv_graph_writable(old_bs); QLIST_REMOVE(child, next_parent); } child->bs = new_bs; - if (!new_bs) { - *childp = NULL; - } if (new_bs) { - assert_bdrv_graph_writable(new_bs); QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent); - - /* - * Detaching the old node may have led to the new node's - * quiesce_counter having been decreased. Not a problem, we - * just need to recognize this here and then invoke - * drained_end appropriately more often. - */ - assert(new_bs->quiesce_counter <= new_bs_quiesce_counter); - drain_saldo += new_bs->quiesce_counter - new_bs_quiesce_counter; - - /* Attach only after starting new drained sections, so that recursive - * drain sections coming from @child don't get an extra .drained_begin - * callback. */ if (child->klass->attach) { child->klass->attach(child); } } /* - * If the old child node was drained but the new one is not, allow - * requests to come in only after the new node has been attached. + * If the parent was drained through this BdrvChild previously, but new_bs + * is not drained, allow requests to come in only after the new node has + * been attached. */ - while (drain_saldo < 0 && child->klass->drained_end) { + new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0); + if (!new_bs_quiesce_counter && child->quiesced_parent) { bdrv_parent_drained_end_single(child); - drain_saldo++; - } - - if (free_empty_child && !child->bs) { - bdrv_child_free(child); } } @@ -2924,6 +2994,8 @@ static void bdrv_child_free(BdrvChild *child) { assert(!child->bs); GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + assert(!child->next.le_prev); /* not in children list */ g_free(child->name); @@ -2931,47 +3003,45 @@ static void bdrv_child_free(BdrvChild *child) } typedef struct BdrvAttachChildCommonState { - BdrvChild **child; + BdrvChild *child; AioContext *old_parent_ctx; AioContext *old_child_ctx; } BdrvAttachChildCommonState; -static void bdrv_attach_child_common_abort(void *opaque) +static void GRAPH_WRLOCK bdrv_attach_child_common_abort(void *opaque) { BdrvAttachChildCommonState *s = opaque; - BdrvChild *child = *s->child; - BlockDriverState *bs = child->bs; + BlockDriverState *bs = s->child->bs; GLOBAL_STATE_CODE(); - /* - * Pass free_empty_child=false, because we still need the child - * for the AioContext operations on the parent below; those - * BdrvChildClass methods all work on a BdrvChild object, so we - * need to keep it as an empty shell (after this function, it will - * not be attached to any parent, and it will not have a .bs). - */ - bdrv_replace_child_noperm(s->child, NULL, false); + assert_bdrv_graph_writable(); + + bdrv_replace_child_noperm(s->child, NULL); if (bdrv_get_aio_context(bs) != s->old_child_ctx) { - bdrv_try_set_aio_context(bs, s->old_child_ctx, &error_abort); + bdrv_try_change_aio_context(bs, s->old_child_ctx, NULL, &error_abort); } - if (bdrv_child_get_parent_aio_context(child) != s->old_parent_ctx) { - GSList *ignore; + if (bdrv_child_get_parent_aio_context(s->child) != s->old_parent_ctx) { + Transaction *tran; + GHashTable *visited; + bool ret; - /* No need to ignore `child`, because it has been detached already */ - ignore = NULL; - child->klass->can_set_aio_ctx(child, s->old_parent_ctx, &ignore, - &error_abort); - g_slist_free(ignore); + tran = tran_new(); - ignore = NULL; - child->klass->set_aio_ctx(child, s->old_parent_ctx, &ignore); - g_slist_free(ignore); + /* No need to visit `child`, because it has been detached already */ + visited = g_hash_table_new(NULL, NULL); + ret = s->child->klass->change_aio_ctx(s->child, s->old_parent_ctx, + visited, tran, &error_abort); + g_hash_table_destroy(visited); + + /* transaction is supposed to always succeed */ + assert(ret == true); + tran_commit(tran); } - bdrv_unref(bs); - bdrv_child_free(child); + bdrv_schedule_unref(bs); + bdrv_child_free(s->child); } static TransactionActionDrv bdrv_attach_child_common_drv = { @@ -2982,28 +3052,29 @@ static TransactionActionDrv bdrv_attach_child_common_drv = { /* * Common part of attaching bdrv child to bs or to blk or to job * - * Resulting new child is returned through @child. - * At start *@child must be NULL. - * @child is saved to a new entry of @tran, so that *@child could be reverted to - * NULL on abort(). So referenced variable must live at least until transaction - * end. - * * Function doesn't update permissions, caller is responsible for this. + * + * After calling this function, the transaction @tran may only be completed + * while holding a writer lock for the graph. + * + * Returns new created child. + * + * Both @parent_bs and @child_bs can move to a different AioContext in this + * function. */ -static int bdrv_attach_child_common(BlockDriverState *child_bs, - const char *child_name, - const BdrvChildClass *child_class, - BdrvChildRole child_role, - uint64_t perm, uint64_t shared_perm, - void *opaque, BdrvChild **child, - Transaction *tran, Error **errp) +static BdrvChild * GRAPH_WRLOCK +bdrv_attach_child_common(BlockDriverState *child_bs, + const char *child_name, + const BdrvChildClass *child_class, + BdrvChildRole child_role, + uint64_t perm, uint64_t shared_perm, + void *opaque, + Transaction *tran, Error **errp) { BdrvChild *new_child; AioContext *parent_ctx; AioContext *child_ctx = bdrv_get_aio_context(child_bs); - assert(child); - assert(*child == NULL); assert(child_class->get_parent_desc); GLOBAL_STATE_CODE(); @@ -3026,63 +3097,83 @@ static int bdrv_attach_child_common(BlockDriverState *child_bs, parent_ctx = bdrv_child_get_parent_aio_context(new_child); if (child_ctx != parent_ctx) { Error *local_err = NULL; - int ret = bdrv_try_set_aio_context(child_bs, parent_ctx, &local_err); + int ret = bdrv_try_change_aio_context(child_bs, parent_ctx, NULL, + &local_err); - if (ret < 0 && child_class->can_set_aio_ctx) { - GSList *ignore = g_slist_prepend(NULL, new_child); - if (child_class->can_set_aio_ctx(new_child, child_ctx, &ignore, - NULL)) - { + if (ret < 0 && child_class->change_aio_ctx) { + Transaction *aio_ctx_tran = tran_new(); + GHashTable *visited = g_hash_table_new(NULL, NULL); + bool ret_child; + + g_hash_table_add(visited, new_child); + ret_child = child_class->change_aio_ctx(new_child, child_ctx, + visited, aio_ctx_tran, + NULL); + if (ret_child == true) { error_free(local_err); ret = 0; - g_slist_free(ignore); - ignore = g_slist_prepend(NULL, new_child); - child_class->set_aio_ctx(new_child, child_ctx, &ignore); } - g_slist_free(ignore); + tran_finalize(aio_ctx_tran, ret_child == true ? 0 : -1); + g_hash_table_destroy(visited); } if (ret < 0) { error_propagate(errp, local_err); bdrv_child_free(new_child); - return ret; + return NULL; } } bdrv_ref(child_bs); - bdrv_replace_child_noperm(&new_child, child_bs, true); - /* child_bs was non-NULL, so new_child must not have been freed */ - assert(new_child != NULL); - - *child = new_child; + /* + * Let every new BdrvChild start with a drained parent. Inserting the child + * in the graph with bdrv_replace_child_noperm() will undrain it if + * @child_bs is not drained. + * + * The child was only just created and is not yet visible in global state + * until bdrv_replace_child_noperm() inserts it into the graph, so nobody + * could have sent requests and polling is not necessary. + * + * Note that this means that the parent isn't fully drained yet, we only + * stop new requests from coming in. This is fine, we don't care about the + * old requests here, they are not for this child. If another place enters a + * drain section for the same parent, but wants it to be fully quiesced, it + * will not run most of the the code in .drained_begin() again (which is not + * a problem, we already did this), but it will still poll until the parent + * is fully quiesced, so it will not be negatively affected either. + */ + bdrv_parent_drained_begin_single(new_child); + bdrv_replace_child_noperm(new_child, child_bs); BdrvAttachChildCommonState *s = g_new(BdrvAttachChildCommonState, 1); *s = (BdrvAttachChildCommonState) { - .child = child, + .child = new_child, .old_parent_ctx = parent_ctx, .old_child_ctx = child_ctx, }; tran_add(tran, &bdrv_attach_child_common_drv, s); - return 0; + return new_child; } /* - * Variable referenced by @child must live at least until transaction end. - * (see bdrv_attach_child_common() doc for details) - * * Function doesn't update permissions, caller is responsible for this. + * + * Both @parent_bs and @child_bs can move to a different AioContext in this + * function. + * + * After calling this function, the transaction @tran may only be completed + * while holding a writer lock for the graph. */ -static int bdrv_attach_child_noperm(BlockDriverState *parent_bs, - BlockDriverState *child_bs, - const char *child_name, - const BdrvChildClass *child_class, - BdrvChildRole child_role, - BdrvChild **child, - Transaction *tran, - Error **errp) +static BdrvChild * GRAPH_WRLOCK +bdrv_attach_child_noperm(BlockDriverState *parent_bs, + BlockDriverState *child_bs, + const char *child_name, + const BdrvChildClass *child_class, + BdrvChildRole child_role, + Transaction *tran, + Error **errp) { - int ret; uint64_t perm, shared_perm; assert(parent_bs->drv); @@ -3091,44 +3182,16 @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs, if (bdrv_recurse_has_child(child_bs, parent_bs)) { error_setg(errp, "Making '%s' a %s child of '%s' would create a cycle", child_bs->node_name, child_name, parent_bs->node_name); - return -EINVAL; + return NULL; } bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm); bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL, perm, shared_perm, &perm, &shared_perm); - ret = bdrv_attach_child_common(child_bs, child_name, child_class, - child_role, perm, shared_perm, parent_bs, - child, tran, errp); - if (ret < 0) { - return ret; - } - - return 0; -} - -static void bdrv_detach_child(BdrvChild **childp) -{ - BlockDriverState *old_bs = (*childp)->bs; - - GLOBAL_STATE_CODE(); - bdrv_replace_child_noperm(childp, NULL, true); - - if (old_bs) { - /* - * Update permissions for old node. We're just taking a parent away, so - * we're loosening restrictions. Errors of permission update are not - * fatal in this case, ignore them. - */ - bdrv_refresh_perms(old_bs, NULL); - - /* - * When the parent requiring a non-default AioContext is removed, the - * node moves back to the main AioContext - */ - bdrv_try_set_aio_context(old_bs, qemu_get_aio_context(), NULL); - } + return bdrv_attach_child_common(child_bs, child_name, child_class, + child_role, perm, shared_perm, parent_bs, + tran, errp); } /* @@ -3137,9 +3200,6 @@ static void bdrv_detach_child(BdrvChild **childp) * * On failure NULL is returned, errp is set and the reference to * child_bs is also dropped. - * - * The caller must hold the AioContext lock @child_bs, but not that of @ctx - * (unless @child_bs is already in @ctx). */ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, const char *child_name, @@ -3149,27 +3209,27 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, void *opaque, Error **errp) { int ret; - BdrvChild *child = NULL; + BdrvChild *child; Transaction *tran = tran_new(); GLOBAL_STATE_CODE(); - ret = bdrv_attach_child_common(child_bs, child_name, child_class, + child = bdrv_attach_child_common(child_bs, child_name, child_class, child_role, perm, shared_perm, opaque, - &child, tran, errp); - if (ret < 0) { + tran, errp); + if (!child) { + ret = -EINVAL; goto out; } - ret = bdrv_refresh_perms(child_bs, errp); + ret = bdrv_refresh_perms(child_bs, tran, errp); out: tran_finalize(tran, ret); - /* child is unset on failure by bdrv_attach_child_common_abort() */ - assert((ret < 0) == !child); - bdrv_unref(child_bs); - return child; + bdrv_schedule_unref(child_bs); + + return ret < 0 ? NULL : child; } /* @@ -3179,9 +3239,6 @@ out: * * On failure NULL is returned, errp is set and the reference to * child_bs is also dropped. - * - * If @parent_bs and @child_bs are in different AioContexts, the caller must - * hold the AioContext lock for @child_bs, but not for @parent_bs. */ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, @@ -3191,42 +3248,57 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, Error **errp) { int ret; - BdrvChild *child = NULL; + BdrvChild *child; Transaction *tran = tran_new(); GLOBAL_STATE_CODE(); - ret = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, child_class, - child_role, &child, tran, errp); - if (ret < 0) { + child = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, + child_class, child_role, tran, errp); + if (!child) { + ret = -EINVAL; goto out; } - ret = bdrv_refresh_perms(parent_bs, errp); + ret = bdrv_refresh_perms(parent_bs, tran, errp); if (ret < 0) { goto out; } out: tran_finalize(tran, ret); - /* child is unset on failure by bdrv_attach_child_common_abort() */ - assert((ret < 0) == !child); - bdrv_unref(child_bs); + bdrv_schedule_unref(child_bs); - return child; + return ret < 0 ? NULL : child; } /* Callers must ensure that child->frozen is false. */ void bdrv_root_unref_child(BdrvChild *child) { - BlockDriverState *child_bs; + BlockDriverState *child_bs = child->bs; GLOBAL_STATE_CODE(); + bdrv_replace_child_noperm(child, NULL); + bdrv_child_free(child); - child_bs = child->bs; - bdrv_detach_child(&child); - bdrv_unref(child_bs); + if (child_bs) { + /* + * Update permissions for old node. We're just taking a parent away, so + * we're loosening restrictions. Errors of permission update are not + * fatal in this case, ignore them. + */ + bdrv_refresh_perms(child_bs, NULL, NULL); + + /* + * When the parent requiring a non-default AioContext is removed, the + * node moves back to the main AioContext + */ + bdrv_try_change_aio_context(child_bs, qemu_get_aio_context(), NULL, + NULL); + } + + bdrv_schedule_unref(child_bs); } typedef struct BdrvSetInheritsFrom { @@ -3270,8 +3342,9 @@ static void bdrv_set_inherits_from(BlockDriverState *bs, * @root that point to @root, where necessary. * @tran is allowed to be NULL. In this case no rollback is possible */ -static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child, - Transaction *tran) +static void GRAPH_WRLOCK +bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child, + Transaction *tran) { BdrvChild *c; @@ -3308,7 +3381,8 @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) } -static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load) +static void GRAPH_RDLOCK +bdrv_parent_cb_change_media(BlockDriverState *bs, bool load) { BdrvChild *c; GLOBAL_STATE_CODE(); @@ -3349,14 +3423,23 @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs) * Sets the bs->backing or bs->file link of a BDS. A new reference is created; * callers which don't need their own reference any more must call bdrv_unref(). * + * If the respective child is already present (i.e. we're detaching a node), + * that child node must be drained. + * * Function doesn't update permissions, caller is responsible for this. + * + * Both @parent_bs and @child_bs can move to a different AioContext in this + * function. + * + * After calling this function, the transaction @tran may only be completed + * while holding a writer lock for the graph. */ -static int bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, - BlockDriverState *child_bs, - bool is_backing, - Transaction *tran, Error **errp) +static int GRAPH_WRLOCK +bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, + BlockDriverState *child_bs, + bool is_backing, + Transaction *tran, Error **errp) { - int ret = 0; bool update_inherits_from = bdrv_inherits_from_recursive(child_bs, parent_bs); BdrvChild *child = is_backing ? parent_bs->backing : parent_bs->file; @@ -3406,22 +3489,21 @@ static int bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, } if (child) { + assert(child->bs->quiesce_counter); bdrv_unset_inherits_from(parent_bs, child, tran); - bdrv_remove_file_or_backing_child(parent_bs, child, tran); + bdrv_remove_child(child, tran); } if (!child_bs) { goto out; } - ret = bdrv_attach_child_noperm(parent_bs, child_bs, - is_backing ? "backing" : "file", - &child_of_bds, role, - is_backing ? &parent_bs->backing : - &parent_bs->file, - tran, errp); - if (ret < 0) { - return ret; + child = bdrv_attach_child_noperm(parent_bs, child_bs, + is_backing ? "backing" : "file", + &child_of_bds, role, + tran, errp); + if (!child) { + return -EINVAL; } @@ -3439,33 +3521,55 @@ out: return 0; } -static int bdrv_set_backing_noperm(BlockDriverState *bs, - BlockDriverState *backing_hd, - Transaction *tran, Error **errp) -{ - GLOBAL_STATE_CODE(); - return bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp); -} - -int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, - Error **errp) +/* + * Both @bs and @backing_hd can move to a different AioContext in this + * function. + * + * If a backing child is already present (i.e. we're detaching a node), that + * child node must be drained. + */ +int bdrv_set_backing_hd_drained(BlockDriverState *bs, + BlockDriverState *backing_hd, + Error **errp) { int ret; Transaction *tran = tran_new(); GLOBAL_STATE_CODE(); - bdrv_drained_begin(bs); + assert(bs->quiesce_counter > 0); + if (bs->backing) { + assert(bs->backing->bs->quiesce_counter > 0); + } - ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp); + ret = bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp); if (ret < 0) { goto out; } - ret = bdrv_refresh_perms(bs, errp); + ret = bdrv_refresh_perms(bs, tran, errp); out: tran_finalize(tran, ret); + return ret; +} - bdrv_drained_end(bs); +int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, + Error **errp) +{ + BlockDriverState *drain_bs; + int ret; + GLOBAL_STATE_CODE(); + + bdrv_graph_rdlock_main_loop(); + drain_bs = bs->backing ? bs->backing->bs : bs; + bdrv_graph_rdunlock_main_loop(); + + bdrv_ref(drain_bs); + bdrv_drained_begin(drain_bs); + bdrv_graph_wrlock(); + ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp); + bdrv_graph_wrunlock(); + bdrv_drained_end(drain_bs); + bdrv_unref(drain_bs); return ret; } @@ -3483,6 +3587,7 @@ out: int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, const char *bdref_key, Error **errp) { + ERRP_GUARD(); char *backing_filename = NULL; char *bdref_key_dot; const char *reference = NULL; @@ -3494,6 +3599,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, Error *local_err = NULL; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (bs->backing != NULL) { goto free_exit; @@ -3575,6 +3681,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, * backing_hd reference now */ ret = bdrv_set_backing_hd(bs, backing_hd, errp); bdrv_unref(backing_hd); + if (ret < 0) { goto free_exit; } @@ -3644,6 +3751,8 @@ done: * BlockdevRef. * * The BlockdevRef will be removed from the options QDict. + * + * @parent can move to a different AioContext in this function. */ BdrvChild *bdrv_open_child(const char *filename, QDict *options, const char *bdref_key, @@ -3653,6 +3762,7 @@ BdrvChild *bdrv_open_child(const char *filename, bool allow_none, Error **errp) { BlockDriverState *bs; + BdrvChild *child; GLOBAL_STATE_CODE(); @@ -3662,8 +3772,37 @@ BdrvChild *bdrv_open_child(const char *filename, return NULL; } - return bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, - errp); + bdrv_graph_wrlock(); + child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, + errp); + bdrv_graph_wrunlock(); + + return child; +} + +/* + * Wrapper on bdrv_open_child() for most popular case: open primary child of bs. + * + * @parent can move to a different AioContext in this function. + */ +int bdrv_open_file_child(const char *filename, + QDict *options, const char *bdref_key, + BlockDriverState *parent, Error **errp) +{ + BdrvChildRole role; + + /* commit_top and mirror_top don't use this function */ + assert(!parent->drv->filtered_child_is_backing); + role = parent->drv->is_filter ? + (BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY) : BDRV_CHILD_IMAGE; + + if (!bdrv_open_child(filename, options, bdref_key, parent, + &child_of_bds, role, false, errp)) + { + return -EINVAL; + } + + return 0; } /* @@ -3715,8 +3854,8 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, QDict *snapshot_options, Error **errp) { - /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ - char *tmp_filename = g_malloc0(PATH_MAX + 1); + ERRP_GUARD(); + g_autofree char *tmp_filename = NULL; int64_t total_size; QemuOpts *opts = NULL; BlockDriverState *bs_snapshot = NULL; @@ -3729,15 +3868,15 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, /* Get the required size from the image */ total_size = bdrv_getlength(bs); + if (total_size < 0) { error_setg_errno(errp, -total_size, "Could not get image size"); goto out; } /* Create the temporary image */ - ret = get_tmp_filename(tmp_filename, PATH_MAX + 1); - if (ret < 0) { - error_setg_errno(errp, -ret, "Could not get temporary filename"); + tmp_filename = create_tmp_file(errp); + if (!tmp_filename) { goto out; } @@ -3771,7 +3910,6 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, out: qobject_unref(snapshot_options); - g_free(tmp_filename); return bs_snapshot; } @@ -3790,13 +3928,11 @@ out: * should be opened. If specified, neither options nor a filename may be given, * nor can an existing BDS be reused (that is, *pbs has to be NULL). */ -static BlockDriverState *bdrv_open_inherit(const char *filename, - const char *reference, - QDict *options, int flags, - BlockDriverState *parent, - const BdrvChildClass *child_class, - BdrvChildRole child_role, - Error **errp) +static BlockDriverState * no_coroutine_fn +bdrv_open_inherit(const char *filename, const char *reference, QDict *options, + int flags, BlockDriverState *parent, + const BdrvChildClass *child_class, BdrvChildRole child_role, + Error **errp) { int ret; BlockBackend *file = NULL; @@ -3812,6 +3948,10 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, assert(!child_class || !flags); assert(!child_class == !parent); GLOBAL_STATE_CODE(); + assert(!qemu_in_coroutine()); + + /* TODO We'll eventually have to take a writer lock in this function */ + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (reference) { bool options_non_empty = options ? qdict_size(options) : false; @@ -3944,9 +4084,11 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, /* Not requesting BLK_PERM_CONSISTENT_READ because we're only * looking at the header to guess the image format. This works even * in cases where a guest would not see a consistent state. */ - file = blk_new(bdrv_get_aio_context(file_bs), 0, BLK_PERM_ALL); + AioContext *ctx = bdrv_get_aio_context(file_bs); + file = blk_new(ctx, 0, BLK_PERM_ALL); blk_insert_bs(file, file_bs, &local_err); bdrv_unref(file_bs); + if (local_err) { goto fail; } @@ -4136,8 +4278,8 @@ static int bdrv_reset_options_allowed(BlockDriverState *bs, /* * Returns true if @child can be reached recursively from @bs */ -static bool bdrv_recurse_has_child(BlockDriverState *bs, - BlockDriverState *child) +static bool GRAPH_RDLOCK +bdrv_recurse_has_child(BlockDriverState *bs, BlockDriverState *child) { BdrvChild *c; @@ -4174,17 +4316,16 @@ static bool bdrv_recurse_has_child(BlockDriverState *bs, * returns a pointer to bs_queue, which is either the newly allocated * bs_queue, or the existing bs_queue being used. * - * bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple(). + * bs is drained here and undrained by bdrv_reopen_queue_free(). + * + * To be called with bs->aio_context locked. */ -static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, - BlockDriverState *bs, - QDict *options, - const BdrvChildClass *klass, - BdrvChildRole role, - bool parent_is_format, - QDict *parent_options, - int parent_flags, - bool keep_old_opts) +static BlockReopenQueue * GRAPH_RDLOCK +bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockDriverState *bs, + QDict *options, const BdrvChildClass *klass, + BdrvChildRole role, bool parent_is_format, + QDict *parent_options, int parent_flags, + bool keep_old_opts) { assert(bs != NULL); @@ -4194,12 +4335,15 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, int flags; QemuOpts *opts; - /* Make sure that the caller remembered to use a drained section. This is - * important to avoid graph changes between the recursive queuing here and - * bdrv_reopen_multiple(). */ - assert(bs->quiesce_counter > 0); GLOBAL_STATE_CODE(); + /* + * Strictly speaking, draining is illegal under GRAPH_RDLOCK. We know that + * we've been called with bdrv_graph_rdlock_main_loop(), though, so it's ok + * in practice. + */ + bdrv_drained_begin(bs); + if (bs_queue == NULL) { bs_queue = g_new0(BlockReopenQueue, 1); QTAILQ_INIT(bs_queue); @@ -4333,11 +4477,13 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, return bs_queue; } +/* To be called with bs->aio_context locked */ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, QDict *options, bool keep_old_opts) { GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, 0, false, NULL, 0, keep_old_opts); @@ -4349,6 +4495,7 @@ void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue) if (bs_queue) { BlockReopenQueueEntry *bs_entry, *next; QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { + bdrv_drained_end(bs_entry->state.bs); qobject_unref(bs_entry->state.explicit_options); qobject_unref(bs_entry->state.options); g_free(bs_entry); @@ -4380,9 +4527,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) { int ret = -1; BlockReopenQueueEntry *bs_entry, *next; - AioContext *ctx; Transaction *tran = tran_new(); - g_autoptr(GHashTable) found = NULL; g_autoptr(GSList) refresh_list = NULL; assert(qemu_get_current_aio_context() == qemu_get_aio_context()); @@ -4390,10 +4535,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) GLOBAL_STATE_CODE(); QTAILQ_FOREACH(bs_entry, bs_queue, entry) { - ctx = bdrv_get_aio_context(bs_entry->state.bs); - aio_context_acquire(ctx); ret = bdrv_flush(bs_entry->state.bs); - aio_context_release(ctx); if (ret < 0) { error_setg_errno(errp, -ret, "Error flushing drive"); goto abort; @@ -4402,28 +4544,22 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) QTAILQ_FOREACH(bs_entry, bs_queue, entry) { assert(bs_entry->state.bs->quiesce_counter > 0); - ctx = bdrv_get_aio_context(bs_entry->state.bs); - aio_context_acquire(ctx); ret = bdrv_reopen_prepare(&bs_entry->state, bs_queue, tran, errp); - aio_context_release(ctx); if (ret < 0) { goto abort; } bs_entry->prepared = true; } - found = g_hash_table_new(NULL, NULL); QTAILQ_FOREACH(bs_entry, bs_queue, entry) { BDRVReopenState *state = &bs_entry->state; - refresh_list = bdrv_topological_dfs(refresh_list, found, state->bs); + refresh_list = g_slist_prepend(refresh_list, state->bs); if (state->old_backing_bs) { - refresh_list = bdrv_topological_dfs(refresh_list, found, - state->old_backing_bs); + refresh_list = g_slist_prepend(refresh_list, state->old_backing_bs); } if (state->old_file_bs) { - refresh_list = bdrv_topological_dfs(refresh_list, found, - state->old_file_bs); + refresh_list = g_slist_prepend(refresh_list, state->old_file_bs); } } @@ -4433,7 +4569,10 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) * reconfiguring the fd and that's why it does it in raw_check_perm(), not * in raw_reopen_prepare() which is called with "old" permissions. */ + bdrv_graph_rdlock_main_loop(); ret = bdrv_list_refresh_perms(refresh_list, bs_queue, tran, errp); + bdrv_graph_rdunlock_main_loop(); + if (ret < 0) { goto abort; } @@ -4448,22 +4587,18 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) * to first element. */ QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) { - ctx = bdrv_get_aio_context(bs_entry->state.bs); - aio_context_acquire(ctx); bdrv_reopen_commit(&bs_entry->state); - aio_context_release(ctx); } + bdrv_graph_wrlock(); tran_commit(tran); + bdrv_graph_wrunlock(); QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) { BlockDriverState *bs = bs_entry->state.bs; if (bs->drv->bdrv_reopen_commit_post) { - ctx = bdrv_get_aio_context(bs); - aio_context_acquire(ctx); bs->drv->bdrv_reopen_commit_post(&bs_entry->state); - aio_context_release(ctx); } } @@ -4471,13 +4606,13 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) goto cleanup; abort: + bdrv_graph_wrlock(); tran_abort(tran); + bdrv_graph_wrunlock(); + QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { if (bs_entry->prepared) { - ctx = bdrv_get_aio_context(bs_entry->state.bs); - aio_context_acquire(ctx); bdrv_reopen_abort(&bs_entry->state); - aio_context_release(ctx); } } @@ -4490,26 +4625,13 @@ cleanup: int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts, Error **errp) { - AioContext *ctx = bdrv_get_aio_context(bs); BlockReopenQueue *queue; - int ret; GLOBAL_STATE_CODE(); - bdrv_subtree_drained_begin(bs); - if (ctx != qemu_get_aio_context()) { - aio_context_release(ctx); - } - queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts); - ret = bdrv_reopen_multiple(queue, errp); - if (ctx != qemu_get_aio_context()) { - aio_context_acquire(ctx); - } - bdrv_subtree_drained_end(bs); - - return ret; + return bdrv_reopen_multiple(queue, errp); } int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, @@ -4539,19 +4661,27 @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, * true and reopen_state->new_backing_bs contains a pointer to the new * backing BlockDriverState (or NULL). * + * After calling this function, the transaction @tran may only be completed + * while holding a writer lock for the graph. + * * Return 0 on success, otherwise return < 0 and set @errp. + * + * @reopen_state->bs can move to a different AioContext in this function. */ -static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, - bool is_backing, Transaction *tran, - Error **errp) +static int GRAPH_UNLOCKED +bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, + bool is_backing, Transaction *tran, + Error **errp) { BlockDriverState *bs = reopen_state->bs; BlockDriverState *new_child_bs; - BlockDriverState *old_child_bs = is_backing ? child_bs(bs->backing) : - child_bs(bs->file); + BlockDriverState *old_child_bs; + const char *child_name = is_backing ? "backing" : "file"; QObject *value; const char *str; + bool has_child; + int ret; GLOBAL_STATE_CODE(); @@ -4560,6 +4690,8 @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, return 0; } + bdrv_graph_rdlock_main_loop(); + switch (qobject_type(value)) { case QTYPE_QNULL: assert(is_backing); /* The 'file' option does not allow a null value */ @@ -4569,11 +4701,16 @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, str = qstring_get_str(qobject_to(QString, value)); new_child_bs = bdrv_lookup_bs(NULL, str, errp); if (new_child_bs == NULL) { - return -EINVAL; - } else if (bdrv_recurse_has_child(new_child_bs, bs)) { + ret = -EINVAL; + goto out_rdlock; + } + + has_child = bdrv_recurse_has_child(new_child_bs, bs); + if (has_child) { error_setg(errp, "Making '%s' a %s child of '%s' would create a " "cycle", str, child_name, bs->node_name); - return -EINVAL; + ret = -EINVAL; + goto out_rdlock; } break; default: @@ -4584,19 +4721,23 @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, g_assert_not_reached(); } + old_child_bs = is_backing ? child_bs(bs->backing) : child_bs(bs->file); if (old_child_bs == new_child_bs) { - return 0; + ret = 0; + goto out_rdlock; } if (old_child_bs) { if (bdrv_skip_implicit_filters(old_child_bs) == new_child_bs) { - return 0; + ret = 0; + goto out_rdlock; } if (old_child_bs->implicit) { error_setg(errp, "Cannot replace implicit %s child of %s", child_name, bs->node_name); - return -EPERM; + ret = -EPERM; + goto out_rdlock; } } @@ -4607,7 +4748,8 @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, */ error_setg(errp, "'%s' is a %s filter node that does not support a " "%s child", bs->node_name, bs->drv->format_name, child_name); - return -EINVAL; + ret = -EINVAL; + goto out_rdlock; } if (is_backing) { @@ -4616,8 +4758,29 @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, reopen_state->old_file_bs = old_child_bs; } - return bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing, - tran, errp); + if (old_child_bs) { + bdrv_ref(old_child_bs); + bdrv_drained_begin(old_child_bs); + } + + bdrv_graph_rdunlock_main_loop(); + bdrv_graph_wrlock(); + + ret = bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing, + tran, errp); + + bdrv_graph_wrunlock(); + + if (old_child_bs) { + bdrv_drained_end(old_child_bs); + bdrv_unref(old_child_bs); + } + + return ret; + +out_rdlock: + bdrv_graph_rdunlock_main_loop(); + return ret; } /* @@ -4636,10 +4799,12 @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, * It is the responsibility of the caller to then call the abort() or * commit() for any other BDS that have been left in a prepare() state * + * After calling this function, the transaction @change_child_tran may only be + * completed while holding a writer lock for the graph. */ -static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, - BlockReopenQueue *queue, - Transaction *change_child_tran, Error **errp) +static int GRAPH_UNLOCKED +bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, + Transaction *change_child_tran, Error **errp) { int ret = -1; int old_flags; @@ -4701,7 +4866,10 @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, * to r/w. Attempting to set to r/w may fail if either BDRV_O_ALLOW_RDWR is * not set, or if the BDS still has copy_on_read enabled */ read_only = !(reopen_state->flags & BDRV_O_RDWR); + + bdrv_graph_rdlock_main_loop(); ret = bdrv_can_set_read_only(reopen_state->bs, read_only, true, &local_err); + bdrv_graph_rdunlock_main_loop(); if (local_err) { error_propagate(errp, local_err); goto error; @@ -4724,7 +4892,9 @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, if (local_err != NULL) { error_propagate(errp, local_err); } else { + bdrv_graph_rdlock_main_loop(); bdrv_refresh_filename(reopen_state->bs); + bdrv_graph_rdunlock_main_loop(); error_setg(errp, "failed while preparing to reopen image '%s'", reopen_state->bs->filename); } @@ -4733,9 +4903,11 @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, } else { /* It is currently mandatory to have a bdrv_reopen_prepare() * handler for each supported drv. */ + bdrv_graph_rdlock_main_loop(); error_setg(errp, "Block format '%s' used by node '%s' " "does not support reopening files", drv->format_name, bdrv_get_device_or_node_name(reopen_state->bs)); + bdrv_graph_rdunlock_main_loop(); ret = -1; goto error; } @@ -4747,13 +4919,16 @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, * file or if the image file has a backing file name as part of * its metadata. Otherwise the 'backing' option can be omitted. */ + bdrv_graph_rdlock_main_loop(); if (drv->supports_backing && reopen_state->backing_missing && (reopen_state->bs->backing || reopen_state->bs->backing_file[0])) { error_setg(errp, "backing is missing for '%s'", reopen_state->bs->node_name); + bdrv_graph_rdunlock_main_loop(); ret = -EINVAL; goto error; } + bdrv_graph_rdunlock_main_loop(); /* * Allow changing the 'backing' option. The new value can be @@ -4781,6 +4956,8 @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, if (qdict_size(reopen_state->options)) { const QDictEntry *entry = qdict_first(reopen_state->options); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + do { QObject *new = entry->value; QObject *old = qdict_get(reopen_state->bs->options, entry->key); @@ -4854,7 +5031,7 @@ error: * makes them final by swapping the staging BlockDriverState contents into * the active BlockDriverState contents. */ -static void bdrv_reopen_commit(BDRVReopenState *reopen_state) +static void GRAPH_UNLOCKED bdrv_reopen_commit(BDRVReopenState *reopen_state) { BlockDriver *drv; BlockDriverState *bs; @@ -4871,6 +5048,8 @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state) drv->bdrv_reopen_commit(reopen_state); } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + /* set BDS specific flags now */ qobject_unref(bs->explicit_options); qobject_unref(bs->options); @@ -4893,13 +5072,14 @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state) qdict_del(bs->options, "backing"); bdrv_refresh_limits(bs, NULL, NULL); + bdrv_refresh_total_sectors(bs, bs->total_sectors); } /* * Abort the reopen, and delete and free the staged changes in * reopen_state */ -static void bdrv_reopen_abort(BDRVReopenState *reopen_state) +static void GRAPH_UNLOCKED bdrv_reopen_abort(BDRVReopenState *reopen_state) { BlockDriver *drv; @@ -4934,12 +5114,15 @@ static void bdrv_close(BlockDriverState *bs) bs->drv = NULL; } + bdrv_graph_wrlock(); QLIST_FOREACH_SAFE(child, &bs->children, next, next) { bdrv_unref_child(bs, child); } - bs->backing = NULL; - bs->file = NULL; + assert(!bs->backing); + assert(!bs->file); + bdrv_graph_wrunlock(); + g_free(bs->opaque); bs->opaque = NULL; qatomic_set(&bs->copy_on_read, 0); @@ -4978,8 +5161,8 @@ static void bdrv_close(BlockDriverState *bs) void bdrv_close_all(void) { - assert(job_next(NULL) == NULL); GLOBAL_STATE_CODE(); + assert(job_next(NULL) == NULL); /* Drop references from requests still in flight, such as canceled block * jobs whose AIO context has not been polled yet */ @@ -4991,7 +5174,7 @@ void bdrv_close_all(void) assert(QTAILQ_EMPTY(&all_bdrv_states)); } -static bool should_update_child(BdrvChild *c, BlockDriverState *to) +static bool GRAPH_RDLOCK should_update_child(BdrvChild *c, BlockDriverState *to) { GQueue *queue; GHashTable *found; @@ -5070,119 +5253,58 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) return ret; } -typedef struct BdrvRemoveFilterOrCowChild { - BdrvChild *child; - BlockDriverState *bs; - bool is_backing; -} BdrvRemoveFilterOrCowChild; - -static void bdrv_remove_filter_or_cow_child_abort(void *opaque) +static void bdrv_remove_child_commit(void *opaque) { - BdrvRemoveFilterOrCowChild *s = opaque; - BlockDriverState *parent_bs = s->child->opaque; - - if (s->is_backing) { - parent_bs->backing = s->child; - } else { - parent_bs->file = s->child; - } - - /* - * We don't have to restore child->bs here to undo bdrv_replace_child_tran() - * because that function is transactionable and it registered own completion - * entries in @tran, so .abort() for bdrv_replace_child_safe() will be - * called automatically. - */ -} - -static void bdrv_remove_filter_or_cow_child_commit(void *opaque) -{ - BdrvRemoveFilterOrCowChild *s = opaque; GLOBAL_STATE_CODE(); - bdrv_child_free(s->child); + bdrv_child_free(opaque); } -static void bdrv_remove_filter_or_cow_child_clean(void *opaque) -{ - BdrvRemoveFilterOrCowChild *s = opaque; - - /* Drop the bs reference after the transaction is done */ - bdrv_unref(s->bs); - g_free(s); -} - -static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = { - .abort = bdrv_remove_filter_or_cow_child_abort, - .commit = bdrv_remove_filter_or_cow_child_commit, - .clean = bdrv_remove_filter_or_cow_child_clean, +static TransactionActionDrv bdrv_remove_child_drv = { + .commit = bdrv_remove_child_commit, }; /* - * A function to remove backing or file child of @bs. * Function doesn't update permissions, caller is responsible for this. + * + * @child->bs (if non-NULL) must be drained. + * + * After calling this function, the transaction @tran may only be completed + * while holding a writer lock for the graph. */ -static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, - BdrvChild *child, - Transaction *tran) +static void GRAPH_WRLOCK bdrv_remove_child(BdrvChild *child, Transaction *tran) { - BdrvChild **childp; - BdrvRemoveFilterOrCowChild *s; - if (!child) { return; } - /* - * Keep a reference to @bs so @childp will stay valid throughout the - * transaction (required by bdrv_replace_child_tran()) - */ - bdrv_ref(bs); - if (child == bs->backing) { - childp = &bs->backing; - } else if (child == bs->file) { - childp = &bs->file; - } else { - g_assert_not_reached(); - } - if (child->bs) { - /* - * Pass free_empty_child=false, we will free the child in - * bdrv_remove_filter_or_cow_child_commit() - */ - bdrv_replace_child_tran(childp, NULL, tran, false); + assert(child->quiesced_parent); + bdrv_replace_child_tran(child, NULL, tran); } - s = g_new(BdrvRemoveFilterOrCowChild, 1); - *s = (BdrvRemoveFilterOrCowChild) { - .child = child, - .bs = bs, - .is_backing = (childp == &bs->backing), - }; - tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s); + tran_add(tran, &bdrv_remove_child_drv, child); } /* - * A function to remove backing-chain child of @bs if exists: cow child for - * format nodes (always .backing) and filter child for filters (may be .file or - * .backing) + * Both @from and @to (if non-NULL) must be drained. @to must be kept drained + * until the transaction is completed. + * + * After calling this function, the transaction @tran may only be completed + * while holding a writer lock for the graph. */ -static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs, - Transaction *tran) -{ - bdrv_remove_file_or_backing_child(bs, bdrv_filter_or_cow_child(bs), tran); -} - -static int bdrv_replace_node_noperm(BlockDriverState *from, - BlockDriverState *to, - bool auto_skip, Transaction *tran, - Error **errp) +static int GRAPH_WRLOCK +bdrv_replace_node_noperm(BlockDriverState *from, + BlockDriverState *to, + bool auto_skip, Transaction *tran, + Error **errp) { BdrvChild *c, *next; - assert(to != NULL); GLOBAL_STATE_CODE(); + assert(from->quiesce_counter); + assert(to->quiesce_counter); + QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { assert(c->bs == from); if (!should_update_child(c, to)) { @@ -5198,18 +5320,16 @@ static int bdrv_replace_node_noperm(BlockDriverState *from, c->name, from->node_name); return -EPERM; } - - /* - * Passing a pointer to the local variable @c is fine here, because - * @to is not NULL, and so &c will not be attached to the transaction. - */ - bdrv_replace_child_tran(&c, to, tran, true); + bdrv_replace_child_tran(c, to, tran); } return 0; } /* + * Switch all parents of @from to point to @to instead. @from and @to must be in + * the same AioContext and both must be drained. + * * With auto_skip=true bdrv_replace_node_common skips updating from parents * if it creates a parent-child relation loop or if parent is block-job. * @@ -5218,22 +5338,21 @@ static int bdrv_replace_node_noperm(BlockDriverState *from, * * With @detach_subchain=true @to must be in a backing chain of @from. In this * case backing link of the cow-parent of @to is removed. - * - * @to must not be NULL. */ -static int bdrv_replace_node_common(BlockDriverState *from, - BlockDriverState *to, - bool auto_skip, bool detach_subchain, - Error **errp) +static int GRAPH_WRLOCK +bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to, + bool auto_skip, bool detach_subchain, Error **errp) { Transaction *tran = tran_new(); - g_autoptr(GHashTable) found = NULL; g_autoptr(GSList) refresh_list = NULL; BlockDriverState *to_cow_parent = NULL; int ret; GLOBAL_STATE_CODE(); - assert(to != NULL); + + assert(from->quiesce_counter); + assert(to->quiesce_counter); + assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to)); if (detach_subchain) { assert(bdrv_chain_contains(from, to)); @@ -5246,14 +5365,6 @@ static int bdrv_replace_node_common(BlockDriverState *from, } } - /* Make sure that @from doesn't go away until we have successfully attached - * all of its parents to @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); - /* * Do the replacement without permission update. * Replacement may influence the permissions, we should calculate new @@ -5266,13 +5377,12 @@ static int bdrv_replace_node_common(BlockDriverState *from, } if (detach_subchain) { - bdrv_remove_filter_or_cow_child(to_cow_parent, tran); + /* to_cow_parent is already drained because from is drained */ + bdrv_remove_child(bdrv_filter_or_cow_child(to_cow_parent), tran); } - found = g_hash_table_new(NULL, NULL); - - refresh_list = bdrv_topological_dfs(refresh_list, found, to); - refresh_list = bdrv_topological_dfs(refresh_list, found, from); + refresh_list = g_slist_prepend(refresh_list, to); + refresh_list = g_slist_prepend(refresh_list, from); ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp); if (ret < 0) { @@ -5283,30 +5393,33 @@ static int bdrv_replace_node_common(BlockDriverState *from, out: tran_finalize(tran, ret); - - bdrv_drained_end(from); - bdrv_unref(from); - return ret; } -/** - * Replace node @from by @to (where neither may be NULL). - */ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, Error **errp) { - GLOBAL_STATE_CODE(); - return bdrv_replace_node_common(from, to, true, false, errp); } int bdrv_drop_filter(BlockDriverState *bs, Error **errp) { + BlockDriverState *child_bs; + int ret; + GLOBAL_STATE_CODE(); - return bdrv_replace_node_common(bs, bdrv_filter_or_cow_bs(bs), true, true, - errp); + bdrv_graph_rdlock_main_loop(); + child_bs = bdrv_filter_or_cow_bs(bs); + bdrv_graph_rdunlock_main_loop(); + + bdrv_drained_begin(child_bs); + bdrv_graph_wrlock(); + ret = bdrv_replace_node_common(bs, child_bs, true, true, errp); + bdrv_graph_wrunlock(); + bdrv_drained_end(child_bs); + + return ret; } /* @@ -5325,16 +5438,25 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, Error **errp) { int ret; + BdrvChild *child; Transaction *tran = tran_new(); GLOBAL_STATE_CODE(); + bdrv_graph_rdlock_main_loop(); assert(!bs_new->backing); + bdrv_graph_rdunlock_main_loop(); - ret = bdrv_attach_child_noperm(bs_new, bs_top, "backing", - &child_of_bds, bdrv_backing_role(bs_new), - &bs_new->backing, tran, errp); - if (ret < 0) { + bdrv_drained_begin(bs_top); + bdrv_drained_begin(bs_new); + + bdrv_graph_wrlock(); + + child = bdrv_attach_child_noperm(bs_new, bs_top, "backing", + &child_of_bds, bdrv_backing_role(bs_new), + tran, errp); + if (!child) { + ret = -EINVAL; goto out; } @@ -5343,11 +5465,15 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, goto out; } - ret = bdrv_refresh_perms(bs_new, errp); + ret = bdrv_refresh_perms(bs_new, tran, errp); out: tran_finalize(tran, ret); bdrv_refresh_limits(bs_top, NULL, NULL); + bdrv_graph_wrunlock(); + + bdrv_drained_end(bs_top); + bdrv_drained_end(bs_new); return ret; } @@ -5358,7 +5484,6 @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, { int ret; Transaction *tran = tran_new(); - g_autoptr(GHashTable) found = NULL; g_autoptr(GSList) refresh_list = NULL; BlockDriverState *old_bs = child->bs; @@ -5367,19 +5492,18 @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, bdrv_ref(old_bs); bdrv_drained_begin(old_bs); bdrv_drained_begin(new_bs); + bdrv_graph_wrlock(); - bdrv_replace_child_tran(&child, new_bs, tran, true); - /* @new_bs must have been non-NULL, so @child must not have been freed */ - assert(child != NULL); + bdrv_replace_child_tran(child, new_bs, tran); - found = g_hash_table_new(NULL, NULL); - refresh_list = bdrv_topological_dfs(refresh_list, found, old_bs); - refresh_list = bdrv_topological_dfs(refresh_list, found, new_bs); + refresh_list = g_slist_prepend(refresh_list, old_bs); + refresh_list = g_slist_prepend(refresh_list, new_bs); ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp); tran_finalize(tran, ret); + bdrv_graph_wrunlock(); bdrv_drained_end(old_bs); bdrv_drained_end(new_bs); bdrv_unref(old_bs); @@ -5401,6 +5525,8 @@ static void bdrv_delete(BlockDriverState *bs) bdrv_close(bs); + qemu_mutex_destroy(&bs->reqs_lock); + g_free(bs); } @@ -5412,12 +5538,16 @@ static void bdrv_delete(BlockDriverState *bs) * empty set of options. The reference to the QDict belongs to the block layer * after the call (even on failure), so if the caller intends to reuse the * dictionary, it needs to use qobject_ref() before calling bdrv_open. + * + * The caller must make sure that @bs stays in the same AioContext, i.e. + * @options must not refer to nodes in a different AioContext. */ BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, int flags, Error **errp) { ERRP_GUARD(); int ret; + AioContext *ctx = bdrv_get_aio_context(bs); BlockDriverState *new_node_bs = NULL; const char *drvname, *node_name; BlockDriver *drv; @@ -5440,15 +5570,27 @@ BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, new_node_bs = bdrv_new_open_driver_opts(drv, node_name, options, flags, errp); + assert(bdrv_get_aio_context(bs) == ctx); + options = NULL; /* bdrv_new_open_driver() eats options */ if (!new_node_bs) { error_prepend(errp, "Could not create node: "); goto fail; } + /* + * Make sure that @bs doesn't go away until we have successfully attached + * all of its parents to @new_node_bs and undrained it again. + */ + bdrv_ref(bs); bdrv_drained_begin(bs); + bdrv_drained_begin(new_node_bs); + bdrv_graph_wrlock(); ret = bdrv_replace_node(bs, new_node_bs, errp); + bdrv_graph_wrunlock(); + bdrv_drained_end(new_node_bs); bdrv_drained_end(bs); + bdrv_unref(bs); if (ret < 0) { error_prepend(errp, "Could not replace node: "); @@ -5474,6 +5616,7 @@ int coroutine_fn bdrv_co_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) { IO_CODE(); + assert_bdrv_graph_readable(); if (bs->drv == NULL) { return -ENOMEDIUM; } @@ -5493,13 +5636,14 @@ int coroutine_fn bdrv_co_check(BlockDriverState *bs, * image file header * -ENOTSUP - format driver doesn't support changing the backing file */ -int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, - const char *backing_fmt, bool require) +int coroutine_fn +bdrv_co_change_backing_file(BlockDriverState *bs, const char *backing_file, + const char *backing_fmt, bool require) { BlockDriver *drv = bs->drv; int ret; - GLOBAL_STATE_CODE(); + IO_CODE(); if (!drv) { return -ENOMEDIUM; @@ -5514,8 +5658,8 @@ int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, return -EINVAL; } - if (drv->bdrv_change_backing_file != NULL) { - ret = drv->bdrv_change_backing_file(bs, backing_file, backing_fmt); + if (drv->bdrv_co_change_backing_file != NULL) { + ret = drv->bdrv_co_change_backing_file(bs, backing_file, backing_fmt); } else { ret = -ENOTSUP; } @@ -5572,8 +5716,9 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs) * between @bs and @base is frozen. @errp is set if that's the case. * @base must be reachable from @bs, or NULL. */ -bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, - Error **errp) +static bool GRAPH_RDLOCK +bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, + Error **errp) { BlockDriverState *i; BdrvChild *child; @@ -5683,7 +5828,8 @@ void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base) * */ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, - const char *backing_file_str) + const char *backing_file_str, + bool backing_mask_protocol) { BlockDriverState *explicit_top = top; bool update_inherits_from; @@ -5696,15 +5842,16 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, GLOBAL_STATE_CODE(); bdrv_ref(top); - bdrv_subtree_drained_begin(top); + bdrv_drained_begin(base); + bdrv_graph_wrlock(); if (!top->drv || !base->drv) { - goto exit; + goto exit_wrlock; } /* Make sure that base is in the backing chain of top */ if (!bdrv_chain_contains(top, base)) { - goto exit; + goto exit_wrlock; } /* If 'base' recursively inherits from 'top' then we should set @@ -5736,6 +5883,8 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, * That's a FIXME. */ bdrv_replace_node_common(top, base, false, false, &local_err); + bdrv_graph_wrunlock(); + if (local_err) { error_report_err(local_err); goto exit; @@ -5746,6 +5895,7 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, if (c->klass->update_filename) { ret = c->klass->update_filename(c, base, backing_file_str, + backing_mask_protocol, &local_err); if (ret < 0) { /* @@ -5768,18 +5918,23 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, } ret = 0; + goto exit; + +exit_wrlock: + bdrv_graph_wrunlock(); exit: - bdrv_subtree_drained_end(top); + bdrv_drained_end(base); bdrv_unref(top); return ret; } /** - * Implementation of BlockDriver.bdrv_get_allocated_file_size() that + * Implementation of BlockDriver.bdrv_co_get_allocated_file_size() that * sums the size of all data-bearing children. (This excludes backing * children.) */ -static int64_t bdrv_sum_allocated_file_size(BlockDriverState *bs) +static int64_t coroutine_fn GRAPH_RDLOCK +bdrv_sum_allocated_file_size(BlockDriverState *bs) { BdrvChild *child; int64_t child_size, sum = 0; @@ -5788,7 +5943,7 @@ static int64_t bdrv_sum_allocated_file_size(BlockDriverState *bs) if (child->role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA | BDRV_CHILD_FILTERED)) { - child_size = bdrv_get_allocated_file_size(child->bs); + child_size = bdrv_co_get_allocated_file_size(child->bs); if (child_size < 0) { return child_size; } @@ -5803,16 +5958,17 @@ static int64_t bdrv_sum_allocated_file_size(BlockDriverState *bs) * Length of a allocated file in bytes. Sparse files are counted by actual * allocated space. Return < 0 if error or unknown. */ -int64_t bdrv_get_allocated_file_size(BlockDriverState *bs) +int64_t coroutine_fn bdrv_co_get_allocated_file_size(BlockDriverState *bs) { BlockDriver *drv = bs->drv; IO_CODE(); + assert_bdrv_graph_readable(); if (!drv) { return -ENOMEDIUM; } - if (drv->bdrv_get_allocated_file_size) { - return drv->bdrv_get_allocated_file_size(bs); + if (drv->bdrv_co_get_allocated_file_size) { + return drv->bdrv_co_get_allocated_file_size(bs); } if (drv->bdrv_file_open) { @@ -5824,7 +5980,7 @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs) return -ENOTSUP; } else if (drv->is_filter) { /* Filter drivers default to the size of their filtered child */ - return bdrv_get_allocated_file_size(bdrv_filter_bs(bs)); + return bdrv_co_get_allocated_file_size(bdrv_filter_bs(bs)); } else { /* Other drivers default to summing their children's sizes */ return bdrv_sum_allocated_file_size(bs); @@ -5870,16 +6026,17 @@ BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts, /** * Return number of sectors on success, -errno on error. */ -int64_t bdrv_nb_sectors(BlockDriverState *bs) +int64_t coroutine_fn bdrv_co_nb_sectors(BlockDriverState *bs) { BlockDriver *drv = bs->drv; IO_CODE(); + assert_bdrv_graph_readable(); if (!drv) return -ENOMEDIUM; - if (drv->has_variable_length) { - int ret = refresh_total_sectors(bs, bs->total_sectors); + if (bs->bl.has_variable_length) { + int ret = bdrv_co_refresh_total_sectors(bs, bs->total_sectors); if (ret < 0) { return ret; } @@ -5887,15 +6044,39 @@ int64_t bdrv_nb_sectors(BlockDriverState *bs) return bs->total_sectors; } +/* + * This wrapper is written by hand because this function is in the hot I/O path, + * via blk_get_geometry. + */ +int64_t coroutine_mixed_fn bdrv_nb_sectors(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + IO_CODE(); + + if (!drv) + return -ENOMEDIUM; + + if (bs->bl.has_variable_length) { + int ret = bdrv_refresh_total_sectors(bs, bs->total_sectors); + if (ret < 0) { + return ret; + } + } + + return bs->total_sectors; +} + /** * Return length in bytes on success, -errno on error. * The length is always a multiple of BDRV_SECTOR_SIZE. */ -int64_t bdrv_getlength(BlockDriverState *bs) +int64_t coroutine_fn bdrv_co_getlength(BlockDriverState *bs) { - int64_t ret = bdrv_nb_sectors(bs); + int64_t ret; IO_CODE(); + assert_bdrv_graph_readable(); + ret = bdrv_co_nb_sectors(bs); if (ret < 0) { return ret; } @@ -5905,15 +6086,6 @@ int64_t bdrv_getlength(BlockDriverState *bs) return ret * BDRV_SECTOR_SIZE; } -/* return 0 as number of sectors if no device present or error */ -void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr) -{ - int64_t nb_sectors = bdrv_nb_sectors(bs); - IO_CODE(); - - *nb_sectors_ptr = nb_sectors < 0 ? 0 : nb_sectors; -} - bool bdrv_is_sg(BlockDriverState *bs) { IO_CODE(); @@ -5968,12 +6140,12 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name), QLIST_FOREACH(drv, &bdrv_drivers, list) { if (drv->format_name) { bool found = false; - int i = count; if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, read_only)) { continue; } + i = count; while (formats && i && !found) { found = !strcmp(formats[--i], drv->format_name); } @@ -6041,6 +6213,7 @@ BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, BlockDriverState *bs; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); list = NULL; QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { @@ -6165,13 +6338,16 @@ XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp) } } - for (job = block_job_next(NULL); job; job = block_job_next(job)) { - GSList *el; + WITH_JOB_LOCK_GUARD() { + for (job = block_job_next_locked(NULL); job; + job = block_job_next_locked(job)) { + GSList *el; - xdbg_graph_add_node(gr, job, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_JOB, - job->job.id); - for (el = job->nodes; el; el = el->next) { - xdbg_graph_add_edge(gr, job, (BdrvChild *)el->data); + xdbg_graph_add_node(gr, job, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_JOB, + job->job.id); + for (el = job->nodes; el; el = el->next) { + xdbg_graph_add_edge(gr, job, (BdrvChild *)el->data); + } } } @@ -6308,7 +6484,7 @@ int bdrv_has_zero_init_1(BlockDriverState *bs) return 1; } -int bdrv_has_zero_init(BlockDriverState *bs) +int coroutine_mixed_fn bdrv_has_zero_init(BlockDriverState *bs) { BlockDriverState *filtered; GLOBAL_STATE_CODE(); @@ -6352,24 +6528,33 @@ void bdrv_get_backing_filename(BlockDriverState *bs, pstrcpy(filename, filename_size, bs->backing_file); } -int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +int coroutine_fn bdrv_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { int ret; BlockDriver *drv = bs->drv; IO_CODE(); + assert_bdrv_graph_readable(); + /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ if (!drv) { return -ENOMEDIUM; } - if (!drv->bdrv_get_info) { + if (!drv->bdrv_co_get_info) { BlockDriverState *filtered = bdrv_filter_bs(bs); if (filtered) { - return bdrv_get_info(filtered, bdi); + return bdrv_co_get_info(filtered, bdi); } return -ENOTSUP; } memset(bdi, 0, sizeof(*bdi)); - ret = drv->bdrv_get_info(bs, bdi); + ret = drv->bdrv_co_get_info(bs, bdi); + if (bdi->subcluster_size == 0) { + /* + * If the driver left this unset, subclusters are not supported. + * Then it is safe to treat each cluster as having only one subcluster. + */ + bdi->subcluster_size = bdi->cluster_size; + } if (ret < 0) { return ret; } @@ -6402,17 +6587,20 @@ BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs) return drv->bdrv_get_specific_stats(bs); } -void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event) +void coroutine_fn bdrv_co_debug_event(BlockDriverState *bs, BlkdebugEvent event) { IO_CODE(); - if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) { + assert_bdrv_graph_readable(); + + if (!bs || !bs->drv || !bs->drv->bdrv_co_debug_event) { return; } - bs->drv->bdrv_debug_event(bs, event); + bs->drv->bdrv_co_debug_event(bs, event); } -static BlockDriverState *bdrv_find_debug_node(BlockDriverState *bs) +static BlockDriverState * GRAPH_RDLOCK +bdrv_find_debug_node(BlockDriverState *bs) { GLOBAL_STATE_CODE(); while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) { @@ -6431,6 +6619,8 @@ int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, const char *tag) { GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bs = bdrv_find_debug_node(bs); if (bs) { return bs->drv->bdrv_debug_breakpoint(bs, event, tag); @@ -6442,6 +6632,8 @@ int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag) { GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bs = bdrv_find_debug_node(bs); if (bs) { return bs->drv->bdrv_debug_remove_breakpoint(bs, tag); @@ -6453,6 +6645,8 @@ int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag) int bdrv_debug_resume(BlockDriverState *bs, const char *tag) { GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + while (bs && (!bs->drv || !bs->drv->bdrv_debug_resume)) { bs = bdrv_primary_bs(bs); } @@ -6467,6 +6661,8 @@ int bdrv_debug_resume(BlockDriverState *bs, const char *tag) bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag) { GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + while (bs && bs->drv && !bs->drv->bdrv_debug_is_suspended) { bs = bdrv_primary_bs(bs); } @@ -6495,6 +6691,7 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, BlockDriverState *bs_below; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!bs || !bs->drv || !backing_file) { return NULL; @@ -6614,6 +6811,7 @@ int bdrv_activate(BlockDriverState *bs, Error **errp) BdrvDirtyBitmap *bm; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!bs->drv) { return -ENOMEDIUM; @@ -6642,7 +6840,7 @@ int bdrv_activate(BlockDriverState *bs, Error **errp) */ if (bs->open_flags & BDRV_O_INACTIVE) { bs->open_flags &= ~BDRV_O_INACTIVE; - ret = bdrv_refresh_perms(bs, errp); + ret = bdrv_refresh_perms(bs, NULL, errp); if (ret < 0) { bs->open_flags |= BDRV_O_INACTIVE; return ret; @@ -6658,7 +6856,7 @@ int bdrv_activate(BlockDriverState *bs, Error **errp) bdrv_dirty_bitmap_skip_store(bm, false); } - ret = refresh_total_sectors(bs, bs->total_sectors); + ret = bdrv_refresh_total_sectors(bs, bs->total_sectors); if (ret < 0) { bs->open_flags |= BDRV_O_INACTIVE; error_setg_errno(errp, -ret, "Could not refresh total sector count"); @@ -6686,6 +6884,7 @@ int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp) IO_CODE(); assert(!(bs->open_flags & BDRV_O_INACTIVE)); + assert_bdrv_graph_readable(); if (bs->drv->bdrv_co_invalidate_cache) { bs->drv->bdrv_co_invalidate_cache(bs, &local_err); @@ -6704,14 +6903,12 @@ void bdrv_activate_all(Error **errp) BdrvNextIterator it; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { - AioContext *aio_context = bdrv_get_aio_context(bs); int ret; - aio_context_acquire(aio_context); ret = bdrv_activate(bs, errp); - aio_context_release(aio_context); if (ret < 0) { bdrv_next_cleanup(&it); return; @@ -6719,7 +6916,8 @@ void bdrv_activate_all(Error **errp) } } -static bool bdrv_has_bds_parent(BlockDriverState *bs, bool only_active) +static bool GRAPH_RDLOCK +bdrv_has_bds_parent(BlockDriverState *bs, bool only_active) { BdrvChild *parent; GLOBAL_STATE_CODE(); @@ -6736,7 +6934,7 @@ static bool bdrv_has_bds_parent(BlockDriverState *bs, bool only_active) return false; } -static int bdrv_inactivate_recurse(BlockDriverState *bs) +static int GRAPH_RDLOCK bdrv_inactivate_recurse(BlockDriverState *bs) { BdrvChild *child, *parent; int ret; @@ -6787,7 +6985,7 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs) * We only tried to loosen restrictions, so errors are not fatal, ignore * them. */ - bdrv_refresh_perms(bs, NULL); + bdrv_refresh_perms(bs, NULL, NULL); /* Recursively inactivate children */ QLIST_FOREACH(child, &bs->children, next) { @@ -6805,18 +7003,9 @@ int bdrv_inactivate_all(void) BlockDriverState *bs = NULL; BdrvNextIterator it; int ret = 0; - GSList *aio_ctxs = NULL, *ctx; GLOBAL_STATE_CODE(); - - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { - AioContext *aio_context = bdrv_get_aio_context(bs); - - if (!g_slist_find(aio_ctxs, aio_context)) { - aio_ctxs = g_slist_prepend(aio_ctxs, aio_context); - aio_context_acquire(aio_context); - } - } + GRAPH_RDLOCK_GUARD_MAINLOOP(); for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { /* Nodes with BDS parents are covered by recursion from the last @@ -6828,17 +7017,10 @@ int bdrv_inactivate_all(void) ret = bdrv_inactivate_recurse(bs); if (ret < 0) { bdrv_next_cleanup(&it); - goto out; + break; } } -out: - for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) { - AioContext *aio_context = ctx->data; - aio_context_release(aio_context); - } - g_slist_free(aio_ctxs); - return ret; } @@ -6848,20 +7030,21 @@ out: /** * Return TRUE if the media is present */ -bool bdrv_is_inserted(BlockDriverState *bs) +bool coroutine_fn bdrv_co_is_inserted(BlockDriverState *bs) { BlockDriver *drv = bs->drv; BdrvChild *child; IO_CODE(); + assert_bdrv_graph_readable(); if (!drv) { return false; } - if (drv->bdrv_is_inserted) { - return drv->bdrv_is_inserted(bs); + if (drv->bdrv_co_is_inserted) { + return drv->bdrv_co_is_inserted(bs); } QLIST_FOREACH(child, &bs->children, next) { - if (!bdrv_is_inserted(child->bs)) { + if (!bdrv_co_is_inserted(child->bs)) { return false; } } @@ -6871,13 +7054,14 @@ bool bdrv_is_inserted(BlockDriverState *bs) /** * If eject_flag is TRUE, eject the media. Otherwise, close the tray */ -void bdrv_eject(BlockDriverState *bs, bool eject_flag) +void coroutine_fn bdrv_co_eject(BlockDriverState *bs, bool eject_flag) { BlockDriver *drv = bs->drv; IO_CODE(); + assert_bdrv_graph_readable(); - if (drv && drv->bdrv_eject) { - drv->bdrv_eject(bs, eject_flag); + if (drv && drv->bdrv_co_eject) { + drv->bdrv_co_eject(bs, eject_flag); } } @@ -6885,14 +7069,15 @@ void bdrv_eject(BlockDriverState *bs, bool eject_flag) * Lock or unlock the media (if it is locked, the user won't be able * to eject it manually). */ -void bdrv_lock_medium(BlockDriverState *bs, bool locked) +void coroutine_fn bdrv_co_lock_medium(BlockDriverState *bs, bool locked) { BlockDriver *drv = bs->drv; IO_CODE(); + assert_bdrv_graph_readable(); trace_bdrv_lock_medium(bs, locked); - if (drv && drv->bdrv_lock_medium) { - drv->bdrv_lock_medium(bs, locked); + if (drv && drv->bdrv_co_lock_medium) { + drv->bdrv_co_lock_medium(bs, locked); } } @@ -6918,6 +7103,29 @@ void bdrv_unref(BlockDriverState *bs) } } +static void bdrv_schedule_unref_bh(void *opaque) +{ + BlockDriverState *bs = opaque; + + bdrv_unref(bs); +} + +/* + * Release a BlockDriverState reference while holding the graph write lock. + * + * Calling bdrv_unref() directly is forbidden while holding the graph lock + * because bdrv_close() both involves polling and taking the graph lock + * internally. bdrv_schedule_unref() instead delays decreasing the refcount and + * possibly closing @bs until the graph lock is released. + */ +void bdrv_schedule_unref(BlockDriverState *bs) +{ + if (!bs) { + return; + } + aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_schedule_unref_bh, bs); +} + struct BdrvOpBlocker { Error *reason; QLIST_ENTRY(BdrvOpBlocker) list; @@ -6927,6 +7135,7 @@ bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp) { BdrvOpBlocker *blocker; GLOBAL_STATE_CODE(); + assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); if (!QLIST_EMPTY(&bs->op_blockers[op])) { blocker = QLIST_FIRST(&bs->op_blockers[op]); @@ -6992,6 +7201,10 @@ bool bdrv_op_blocker_is_empty(BlockDriverState *bs) return true; } +/* + * Must not be called while holding the lock of an AioContext other than the + * current one. + */ void bdrv_img_create(const char *filename, const char *fmt, const char *base_filename, const char *base_fmt, char *options, uint64_t img_size, int flags, bool quiet, @@ -7124,7 +7337,7 @@ void bdrv_img_create(const char *filename, const char *fmt, if (!backing_fmt) { error_setg(&local_err, "Backing file specified without backing format"); - error_append_hint(&local_err, "Detected format of %s.", + error_append_hint(&local_err, "Detected format of %s.\n", bs->drv->format_name); goto out; } @@ -7148,7 +7361,10 @@ void bdrv_img_create(const char *filename, const char *fmt, goto out; } - if (size == -1) { + /* Parameter 'size' is not needed for detached LUKS header */ + if (size == -1 && + !(!strcmp(fmt, "luks") && + qemu_opt_get_bool(opts, "detached-header", false))) { error_setg(errp, "Image creation needs a size parameter"); goto out; } @@ -7213,39 +7429,6 @@ void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx) bdrv_dec_in_flight(bs); } -void coroutine_fn bdrv_co_lock(BlockDriverState *bs) -{ - AioContext *ctx = bdrv_get_aio_context(bs); - - /* In the main thread, bs->aio_context won't change concurrently */ - assert(qemu_get_current_aio_context() == qemu_get_aio_context()); - - /* - * We're in coroutine context, so we already hold the lock of the main - * loop AioContext. Don't lock it twice to avoid deadlocks. - */ - assert(qemu_in_coroutine()); - if (ctx != qemu_get_aio_context()) { - aio_context_acquire(ctx); - } -} - -void coroutine_fn bdrv_co_unlock(BlockDriverState *bs) -{ - AioContext *ctx = bdrv_get_aio_context(bs); - - assert(qemu_in_coroutine()); - if (ctx != qemu_get_aio_context()) { - aio_context_release(ctx); - } -} - -void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co) -{ - IO_CODE(); - aio_co_enter(bdrv_get_aio_context(bs), co); -} - static void bdrv_do_remove_aio_context_notifier(BdrvAioNotifier *ban) { GLOBAL_STATE_CODE(); @@ -7276,9 +7459,6 @@ static void bdrv_detach_aio_context(BlockDriverState *bs) bs->drv->bdrv_detach_aio_context(bs); } - if (bs->quiesce_counter) { - aio_enable_external(bs->aio_context); - } bs->aio_context = NULL; } @@ -7288,10 +7468,6 @@ static void bdrv_attach_aio_context(BlockDriverState *bs, BdrvAioNotifier *ban, *ban_tmp; GLOBAL_STATE_CODE(); - if (bs->quiesce_counter) { - aio_disable_external(new_context); - } - bs->aio_context = new_context; if (bs->drv && bs->drv->bdrv_attach_aio_context) { @@ -7310,193 +7486,175 @@ static void bdrv_attach_aio_context(BlockDriverState *bs, bs->walking_aio_notifiers = false; } -/* - * Changes the AioContext used for fd handlers, timers, and BHs by this - * BlockDriverState and all its children and parents. - * - * Must be called from the main AioContext. - * - * The caller must own the AioContext lock for the old AioContext of bs, but it - * must not own the AioContext lock for new_context (unless new_context is the - * same as the current context of bs). - * - * @ignore will accumulate all visited BdrvChild object. The caller is - * responsible for freeing the list afterwards. - */ -void bdrv_set_aio_context_ignore(BlockDriverState *bs, - AioContext *new_context, GSList **ignore) -{ - AioContext *old_context = bdrv_get_aio_context(bs); - GSList *children_to_process = NULL; - GSList *parents_to_process = NULL; - GSList *entry; - BdrvChild *child, *parent; +typedef struct BdrvStateSetAioContext { + AioContext *new_ctx; + BlockDriverState *bs; +} BdrvStateSetAioContext; - g_assert(qemu_get_current_aio_context() == qemu_get_aio_context()); - GLOBAL_STATE_CODE(); - - if (old_context == new_context) { - return; - } - - bdrv_drained_begin(bs); - - QLIST_FOREACH(child, &bs->children, next) { - if (g_slist_find(*ignore, child)) { - continue; - } - *ignore = g_slist_prepend(*ignore, child); - children_to_process = g_slist_prepend(children_to_process, child); - } - - QLIST_FOREACH(parent, &bs->parents, next_parent) { - if (g_slist_find(*ignore, parent)) { - continue; - } - *ignore = g_slist_prepend(*ignore, parent); - parents_to_process = g_slist_prepend(parents_to_process, parent); - } - - for (entry = children_to_process; - entry != NULL; - entry = g_slist_next(entry)) { - child = entry->data; - bdrv_set_aio_context_ignore(child->bs, new_context, ignore); - } - g_slist_free(children_to_process); - - for (entry = parents_to_process; - entry != NULL; - entry = g_slist_next(entry)) { - parent = entry->data; - assert(parent->klass->set_aio_ctx); - parent->klass->set_aio_ctx(parent, new_context, ignore); - } - g_slist_free(parents_to_process); - - bdrv_detach_aio_context(bs); - - /* Acquire the new context, if necessary */ - if (qemu_get_aio_context() != new_context) { - aio_context_acquire(new_context); - } - - bdrv_attach_aio_context(bs, new_context); - - /* - * If this function was recursively called from - * bdrv_set_aio_context_ignore(), there may be nodes in the - * subtree that have not yet been moved to the new AioContext. - * Release the old one so bdrv_drained_end() can poll them. - */ - if (qemu_get_aio_context() != old_context) { - aio_context_release(old_context); - } - - bdrv_drained_end(bs); - - if (qemu_get_aio_context() != old_context) { - aio_context_acquire(old_context); - } - if (qemu_get_aio_context() != new_context) { - aio_context_release(new_context); - } -} - -static bool bdrv_parent_can_set_aio_context(BdrvChild *c, AioContext *ctx, - GSList **ignore, Error **errp) +static bool bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx, + GHashTable *visited, + Transaction *tran, + Error **errp) { GLOBAL_STATE_CODE(); - if (g_slist_find(*ignore, c)) { + if (g_hash_table_contains(visited, c)) { return true; } - *ignore = g_slist_prepend(*ignore, c); + g_hash_table_add(visited, c); /* * A BdrvChildClass that doesn't handle AioContext changes cannot * tolerate any AioContext changes */ - if (!c->klass->can_set_aio_ctx) { + if (!c->klass->change_aio_ctx) { char *user = bdrv_child_user_desc(c); error_setg(errp, "Changing iothreads is not supported by %s", user); g_free(user); return false; } - if (!c->klass->can_set_aio_ctx(c, ctx, ignore, errp)) { + if (!c->klass->change_aio_ctx(c, ctx, visited, tran, errp)) { assert(!errp || *errp); return false; } return true; } -bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx, - GSList **ignore, Error **errp) +bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp) { GLOBAL_STATE_CODE(); - if (g_slist_find(*ignore, c)) { + if (g_hash_table_contains(visited, c)) { return true; } - *ignore = g_slist_prepend(*ignore, c); - return bdrv_can_set_aio_context(c->bs, ctx, ignore, errp); + g_hash_table_add(visited, c); + return bdrv_change_aio_context(c->bs, ctx, visited, tran, errp); } -/* @ignore will accumulate all visited BdrvChild object. The caller is - * responsible for freeing the list afterwards. */ -bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx, - GSList **ignore, Error **errp) +static void bdrv_set_aio_context_clean(void *opaque) +{ + BdrvStateSetAioContext *state = (BdrvStateSetAioContext *) opaque; + BlockDriverState *bs = (BlockDriverState *) state->bs; + + /* Paired with bdrv_drained_begin in bdrv_change_aio_context() */ + bdrv_drained_end(bs); + + g_free(state); +} + +static void bdrv_set_aio_context_commit(void *opaque) +{ + BdrvStateSetAioContext *state = (BdrvStateSetAioContext *) opaque; + BlockDriverState *bs = (BlockDriverState *) state->bs; + AioContext *new_context = state->new_ctx; + + bdrv_detach_aio_context(bs); + bdrv_attach_aio_context(bs, new_context); +} + +static TransactionActionDrv set_aio_context = { + .commit = bdrv_set_aio_context_commit, + .clean = bdrv_set_aio_context_clean, +}; + +/* + * Changes the AioContext used for fd handlers, timers, and BHs by this + * BlockDriverState and all its children and parents. + * + * Must be called from the main AioContext. + * + * @visited will accumulate all visited BdrvChild objects. The caller is + * responsible for freeing the list afterwards. + */ +static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp) { BdrvChild *c; + BdrvStateSetAioContext *state; + + GLOBAL_STATE_CODE(); if (bdrv_get_aio_context(bs) == ctx) { return true; } - GLOBAL_STATE_CODE(); - + bdrv_graph_rdlock_main_loop(); QLIST_FOREACH(c, &bs->parents, next_parent) { - if (!bdrv_parent_can_set_aio_context(c, ctx, ignore, errp)) { + if (!bdrv_parent_change_aio_context(c, ctx, visited, tran, errp)) { + bdrv_graph_rdunlock_main_loop(); return false; } } + QLIST_FOREACH(c, &bs->children, next) { - if (!bdrv_child_can_set_aio_context(c, ctx, ignore, errp)) { + if (!bdrv_child_change_aio_context(c, ctx, visited, tran, errp)) { + bdrv_graph_rdunlock_main_loop(); return false; } } + bdrv_graph_rdunlock_main_loop(); + + state = g_new(BdrvStateSetAioContext, 1); + *state = (BdrvStateSetAioContext) { + .new_ctx = ctx, + .bs = bs, + }; + + /* Paired with bdrv_drained_end in bdrv_set_aio_context_clean() */ + bdrv_drained_begin(bs); + + tran_add(tran, &set_aio_context, state); return true; } -int bdrv_child_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, - BdrvChild *ignore_child, Error **errp) +/* + * Change bs's and recursively all of its parents' and children's AioContext + * to the given new context, returning an error if that isn't possible. + * + * If ignore_child is not NULL, that child (and its subgraph) will not + * be touched. + */ +int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, + BdrvChild *ignore_child, Error **errp) { - GSList *ignore; - bool ret; - + Transaction *tran; + GHashTable *visited; + int ret; GLOBAL_STATE_CODE(); - ignore = ignore_child ? g_slist_prepend(NULL, ignore_child) : NULL; - ret = bdrv_can_set_aio_context(bs, ctx, &ignore, errp); - g_slist_free(ignore); + /* + * Recursion phase: go through all nodes of the graph. + * Take care of checking that all nodes support changing AioContext + * and drain them, building a linear list of callbacks to run if everything + * is successful (the transaction itself). + */ + tran = tran_new(); + visited = g_hash_table_new(NULL, NULL); + if (ignore_child) { + g_hash_table_add(visited, ignore_child); + } + ret = bdrv_change_aio_context(bs, ctx, visited, tran, errp); + g_hash_table_destroy(visited); + + /* + * Linear phase: go through all callbacks collected in the transaction. + * Run all callbacks collected in the recursion to switch every node's + * AioContext (transaction commit), or undo all changes done in the + * recursion (transaction abort). + */ if (!ret) { + /* Just run clean() callbacks. No AioContext changed. */ + tran_abort(tran); return -EPERM; } - ignore = ignore_child ? g_slist_prepend(NULL, ignore_child) : NULL; - bdrv_set_aio_context_ignore(bs, ctx, &ignore); - g_slist_free(ignore); - + tran_commit(tran); return 0; } -int bdrv_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, - Error **errp) -{ - GLOBAL_STATE_CODE(); - return bdrv_child_try_set_aio_context(bs, ctx, NULL, errp); -} - void bdrv_add_aio_context_notifier(BlockDriverState *bs, void (*attached_aio_context)(AioContext *new_context, void *opaque), void (*detach_aio_context)(void *opaque), void *opaque) @@ -7615,7 +7773,6 @@ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, const char *node_name, Error **errp) { BlockDriverState *to_replace_bs = bdrv_find_node(node_name); - AioContext *aio_context; GLOBAL_STATE_CODE(); @@ -7624,12 +7781,8 @@ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, return NULL; } - aio_context = bdrv_get_aio_context(to_replace_bs); - aio_context_acquire(aio_context); - if (bdrv_op_is_blocked(to_replace_bs, BLOCK_OP_TYPE_REPLACE, errp)) { - to_replace_bs = NULL; - goto out; + return NULL; } /* We don't want arbitrary node of the BDS chain to be replaced only the top @@ -7642,12 +7795,9 @@ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, "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; + return NULL; } -out: - aio_context_release(aio_context); return to_replace_bs; } @@ -7746,7 +7896,7 @@ static bool append_strong_runtime_options(QDict *d, BlockDriverState *bs) /* Note: This function may return false positives; it may return true * even if opening the backing file specified by bs's image header * would result in exactly bs->backing. */ -static bool bdrv_backing_overridden(BlockDriverState *bs) +static bool GRAPH_RDLOCK bdrv_backing_overridden(BlockDriverState *bs) { GLOBAL_STATE_CODE(); if (bs->backing) { @@ -7945,6 +8095,25 @@ void bdrv_add_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, return; } + /* + * Non-zoned block drivers do not follow zoned storage constraints + * (i.e. sequential writes to zones). Refuse mixing zoned and non-zoned + * drivers in a graph. + */ + if (!parent_bs->drv->supports_zoned_children && + child_bs->bl.zoned == BLK_Z_HM) { + /* + * The host-aware model allows zoned storage constraints and random + * write. Allow mixing host-aware and non-zoned drivers. Using + * host-aware device as a regular device. + */ + error_setg(errp, "Cannot add a %s child to a %s parent", + child_bs->bl.zoned == BLK_Z_HM ? "zoned" : "non-zoned", + parent_bs->drv->supports_zoned_children ? + "support zoned children" : "not support zoned children"); + return; + } + if (!QLIST_EMPTY(&child_bs->parents)) { error_setg(errp, "The node %s already has a parent", child_bs->node_name); @@ -8101,8 +8270,8 @@ BdrvChild *bdrv_primary_child(BlockDriverState *bs) return found; } -static BlockDriverState *bdrv_do_skip_filters(BlockDriverState *bs, - bool stop_on_explicit_filter) +static BlockDriverState * GRAPH_RDLOCK +bdrv_do_skip_filters(BlockDriverState *bs, bool stop_on_explicit_filter) { BdrvChild *c; diff --git a/block/accounting.c b/block/accounting.c index 2030851d79..2829745377 100644 --- a/block/accounting.c +++ b/block/accounting.c @@ -38,13 +38,31 @@ void block_acct_init(BlockAcctStats *stats) if (qtest_enabled()) { clock_type = QEMU_CLOCK_VIRTUAL; } + stats->account_invalid = true; + stats->account_failed = true; } -void block_acct_setup(BlockAcctStats *stats, bool account_invalid, - bool account_failed) +static bool bool_from_onoffauto(OnOffAuto val, bool def) { - stats->account_invalid = account_invalid; - stats->account_failed = account_failed; + switch (val) { + case ON_OFF_AUTO_AUTO: + return def; + case ON_OFF_AUTO_ON: + return true; + case ON_OFF_AUTO_OFF: + return false; + default: + abort(); + } +} + +void block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid, + enum OnOffAuto account_failed) +{ + stats->account_invalid = bool_from_onoffauto(account_invalid, + stats->account_invalid); + stats->account_failed = bool_from_onoffauto(account_failed, + stats->account_failed); } void block_acct_cleanup(BlockAcctStats *stats) diff --git a/block/amend.c b/block/amend.c index f696a006e3..53a410247c 100644 --- a/block/amend.c +++ b/block/amend.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "block/block-io.h" #include "block/block_int.h" #include "qemu/job.h" #include "qemu/main-loop.h" @@ -45,6 +46,7 @@ static int coroutine_fn blockdev_amend_run(Job *job, Error **errp) { BlockdevAmendJob *s = container_of(job, BlockdevAmendJob, common); int ret; + GRAPH_RDLOCK_GUARD(); job_progress_set_remaining(&s->common, 1); ret = s->bs->drv->bdrv_co_amend(s->bs, s->opts, s->force, errp); @@ -53,7 +55,8 @@ static int coroutine_fn blockdev_amend_run(Job *job, Error **errp) return ret; } -static int blockdev_amend_pre_run(BlockdevAmendJob *s, Error **errp) +static int GRAPH_RDLOCK +blockdev_amend_pre_run(BlockdevAmendJob *s, Error **errp) { if (s->bs->drv->bdrv_amend_pre_run) { return s->bs->drv->bdrv_amend_pre_run(s->bs, errp); @@ -66,9 +69,11 @@ static void blockdev_amend_free(Job *job) { BlockdevAmendJob *s = container_of(job, BlockdevAmendJob, common); + bdrv_graph_rdlock_main_loop(); if (s->bs->drv->bdrv_amend_clean) { s->bs->drv->bdrv_amend_clean(s->bs); } + bdrv_graph_rdunlock_main_loop(); bdrv_unref(s->bs); } @@ -92,6 +97,8 @@ void qmp_x_blockdev_amend(const char *job_id, BlockDriver *drv = bdrv_find_format(fmt); BlockDriverState *bs; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bs = bdrv_lookup_bs(NULL, node_name, errp); if (!bs) { return; diff --git a/block/backup.c b/block/backup.c index 5cfd0b999c..ec29d6b810 100644 --- a/block/backup.c +++ b/block/backup.c @@ -20,8 +20,8 @@ #include "block/blockjob_int.h" #include "block/block_backup.h" #include "block/block-copy.h" +#include "block/dirty-bitmap.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qemu/cutils.h" #include "sysemu/block-backend.h" #include "qemu/bitmap.h" @@ -228,15 +228,13 @@ out: 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) { bdrv_clear_dirty_bitmap(bcs_bitmap, NULL); - ret = bdrv_dirty_bitmap_merge_internal(bcs_bitmap, job->sync_bitmap, - NULL, true); - assert(ret); + bdrv_dirty_bitmap_merge_internal(bcs_bitmap, job->sync_bitmap, NULL, + true); } else if (job->sync_mode == MIRROR_SYNC_MODE_TOP) { /* * We can't hog the coroutine to initialize this thoroughly. @@ -271,7 +269,10 @@ static int coroutine_fn backup_run(Job *job, Error **errp) return -ECANCELED; } + /* rdlock protects the subsequent call to bdrv_is_allocated() */ + bdrv_graph_co_rdlock(); ret = block_copy_reset_unallocated(s->bcs, offset, &count); + bdrv_graph_co_rdunlock(); if (ret < 0) { return ret; } @@ -311,7 +312,7 @@ static void coroutine_fn backup_pause(Job *job) } } -static void coroutine_fn backup_set_speed(BlockJob *job, int64_t speed) +static void backup_set_speed(BlockJob *job, int64_t speed) { BackupBlockJob *s = container_of(job, BackupBlockJob, common); @@ -383,31 +384,33 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, return NULL; } + bdrv_graph_rdlock_main_loop(); if (!bdrv_is_inserted(bs)) { error_setg(errp, "Device is not inserted: %s", bdrv_get_device_name(bs)); - return NULL; + goto error_rdlock; } if (!bdrv_is_inserted(target)) { error_setg(errp, "Device is not inserted: %s", bdrv_get_device_name(target)); - return NULL; + goto error_rdlock; } if (compress && !bdrv_supports_compressed_writes(target)) { error_setg(errp, "Compression is not supported for this drive %s", bdrv_get_device_name(target)); - return NULL; + goto error_rdlock; } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { - return NULL; + goto error_rdlock; } if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) { - return NULL; + goto error_rdlock; } + bdrv_graph_rdunlock_main_loop(); if (perf->max_workers < 1 || perf->max_workers > INT_MAX) { error_setg(errp, "max-workers must be between 1 and %d", INT_MAX); @@ -435,6 +438,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, len = bdrv_getlength(bs); if (len < 0) { + GRAPH_RDLOCK_GUARD_MAINLOOP(); error_setg_errno(errp, -len, "Unable to get length for '%s'", bdrv_get_device_or_node_name(bs)); goto error; @@ -442,6 +446,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, target_len = bdrv_getlength(target); if (target_len < 0) { + GRAPH_RDLOCK_GUARD_MAINLOOP(); error_setg_errno(errp, -target_len, "Unable to get length for '%s'", bdrv_get_device_or_node_name(bs)); goto error; @@ -491,8 +496,10 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, block_copy_set_speed(bcs, speed); /* Required permissions are taken by copy-before-write filter target */ + bdrv_graph_wrlock(); block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, &error_abort); + bdrv_graph_wrunlock(); return &job->common; @@ -505,4 +512,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, } return NULL; + +error_rdlock: + bdrv_graph_rdunlock_main_loop(); + return NULL; } diff --git a/block/blkdebug.c b/block/blkdebug.c index bbf2948703..9da8c9eddc 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -27,6 +27,7 @@ #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/config-file.h" +#include "block/block-io.h" #include "block/block_int.h" #include "block/qdict.h" #include "qemu/module.h" @@ -297,9 +298,7 @@ static int read_config(BDRVBlkdebugState *s, const char *filename, } } - qemu_config_parse_qdict(options, config_groups, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!qemu_config_parse_qdict(options, config_groups, errp)) { ret = -EINVAL; goto fail; } @@ -503,15 +502,14 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, } /* Open the image file */ - bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image", - bs, &child_of_bds, - BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, errp); - if (!bs->file) { - ret = -EINVAL; + ret = bdrv_open_file_child(qemu_opt_get(opts, "x-image"), options, "image", + bs, errp); + if (ret < 0) { goto out; } + bdrv_graph_rdlock_main_loop(); + 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 | @@ -524,7 +522,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, if (s->align && (s->align >= INT_MAX || !is_power_of_2(s->align))) { error_setg(errp, "Cannot meet constraints with align %" PRIu64, s->align); - goto out; + goto out_rdlock; } align = MAX(s->align, bs->file->bs->bl.request_alignment); @@ -534,7 +532,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, !QEMU_IS_ALIGNED(s->max_transfer, align))) { error_setg(errp, "Cannot meet constraints with max-transfer %" PRIu64, s->max_transfer); - goto out; + goto out_rdlock; } s->opt_write_zero = qemu_opt_get_size(opts, "opt-write-zero", 0); @@ -543,7 +541,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, !QEMU_IS_ALIGNED(s->opt_write_zero, align))) { error_setg(errp, "Cannot meet constraints with opt-write-zero %" PRIu64, s->opt_write_zero); - goto out; + goto out_rdlock; } s->max_write_zero = qemu_opt_get_size(opts, "max-write-zero", 0); @@ -553,7 +551,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, MAX(s->opt_write_zero, align)))) { error_setg(errp, "Cannot meet constraints with max-write-zero %" PRIu64, s->max_write_zero); - goto out; + goto out_rdlock; } s->opt_discard = qemu_opt_get_size(opts, "opt-discard", 0); @@ -562,7 +560,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, !QEMU_IS_ALIGNED(s->opt_discard, align))) { error_setg(errp, "Cannot meet constraints with opt-discard %" PRIu64, s->opt_discard); - goto out; + goto out_rdlock; } s->max_discard = qemu_opt_get_size(opts, "max-discard", 0); @@ -572,12 +570,14 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, MAX(s->opt_discard, align)))) { error_setg(errp, "Cannot meet constraints with max-discard %" PRIu64, s->max_discard); - goto out; + goto out_rdlock; } bdrv_debug_event(bs, BLKDBG_NONE); ret = 0; +out_rdlock: + bdrv_graph_rdunlock_main_loop(); out: if (ret < 0) { qemu_mutex_destroy(&s->lock); @@ -587,8 +587,8 @@ out: return ret; } -static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - BlkdebugIOType iotype) +static int coroutine_fn rule_check(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, BlkdebugIOType iotype) { BDRVBlkdebugState *s = bs->opaque; BlkdebugRule *rule = NULL; @@ -630,7 +630,7 @@ static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes, return -error; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blkdebug_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -651,7 +651,7 @@ blkdebug_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blkdebug_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -672,7 +672,7 @@ blkdebug_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); } -static int blkdebug_co_flush(BlockDriverState *bs) +static int GRAPH_RDLOCK coroutine_fn blkdebug_co_flush(BlockDriverState *bs) { int err = rule_check(bs, 0, 0, BLKDEBUG_IO_TYPE_FLUSH); @@ -683,9 +683,9 @@ static int blkdebug_co_flush(BlockDriverState *bs) return bdrv_co_flush(bs->file->bs); } -static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int64_t bytes, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +blkdebug_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + BdrvRequestFlags flags) { uint32_t align = MAX(bs->bl.request_alignment, bs->bl.pwrite_zeroes_alignment); @@ -716,8 +716,8 @@ static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs, return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); } -static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs, - int64_t offset, int64_t bytes) +static int coroutine_fn GRAPH_RDLOCK +blkdebug_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { uint32_t align = bs->bl.pdiscard_alignment; int err; @@ -750,13 +750,10 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs, return bdrv_co_pdiscard(bs->file, offset, bytes); } -static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs, - bool want_zero, - int64_t offset, - int64_t bytes, - int64_t *pnum, - int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +blkdebug_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, + int64_t bytes, int64_t *pnum, int64_t *map, + BlockDriverState **file) { int err; @@ -840,7 +837,8 @@ static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, } } -static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event) +static void coroutine_fn +blkdebug_co_debug_event(BlockDriverState *bs, BlkdebugEvent event) { BDRVBlkdebugState *s = bs->opaque; struct BlkdebugRule *rule, *next; @@ -970,12 +968,13 @@ static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag) return false; } -static int64_t blkdebug_getlength(BlockDriverState *bs) +static int64_t coroutine_fn GRAPH_RDLOCK +blkdebug_co_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + return bdrv_co_getlength(bs->file->bs); } -static void blkdebug_refresh_filename(BlockDriverState *bs) +static void GRAPH_RDLOCK blkdebug_refresh_filename(BlockDriverState *bs) { BDRVBlkdebugState *s = bs->opaque; const QDictEntry *e; @@ -1079,7 +1078,7 @@ static BlockDriver bdrv_blkdebug = { .bdrv_reopen_prepare = blkdebug_reopen_prepare, .bdrv_child_perm = blkdebug_child_perm, - .bdrv_getlength = blkdebug_getlength, + .bdrv_co_getlength = blkdebug_co_getlength, .bdrv_refresh_filename = blkdebug_refresh_filename, .bdrv_refresh_limits = blkdebug_refresh_limits, @@ -1090,7 +1089,7 @@ static BlockDriver bdrv_blkdebug = { .bdrv_co_pdiscard = blkdebug_co_pdiscard, .bdrv_co_block_status = blkdebug_co_block_status, - .bdrv_debug_event = blkdebug_debug_event, + .bdrv_co_debug_event = blkdebug_co_debug_event, .bdrv_debug_breakpoint = blkdebug_debug_breakpoint, .bdrv_debug_remove_breakpoint = blkdebug_debug_remove_breakpoint, diff --git a/block/blkio.c b/block/blkio.c new file mode 100644 index 0000000000..882e1c297b --- /dev/null +++ b/block/blkio.c @@ -0,0 +1,1152 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * libblkio BlockDriver + * + * Copyright Red Hat, Inc. + * + * Author: + * Stefan Hajnoczi + */ + +#include "qemu/osdep.h" +#include +#include "block/block_int.h" +#include "exec/memory.h" +#include "exec/cpu-common.h" /* for qemu_ram_get_fd() */ +#include "qemu/defer-call.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qapi/qmp/qdict.h" +#include "qemu/module.h" +#include "sysemu/block-backend.h" +#include "exec/memory.h" /* for ram_block_discard_disable() */ + +#include "block/block-io.h" + +/* + * Allocated bounce buffers are kept in a list sorted by buffer address. + */ +typedef struct BlkioBounceBuf { + QLIST_ENTRY(BlkioBounceBuf) next; + + /* The bounce buffer */ + struct iovec buf; +} BlkioBounceBuf; + +typedef struct { + /* + * libblkio is not thread-safe so this lock protects ->blkio and + * ->blkioq. + */ + QemuMutex blkio_lock; + struct blkio *blkio; + struct blkioq *blkioq; /* make this multi-queue in the future... */ + int completion_fd; + + /* + * Polling fetches the next completion into this field. + * + * No lock is necessary since only one thread calls aio_poll() and invokes + * fd and poll handlers. + */ + struct blkio_completion poll_completion; + + /* + * Protects ->bounce_pool, ->bounce_bufs, ->bounce_available. + * + * Lock ordering: ->bounce_lock before ->blkio_lock. + */ + CoMutex bounce_lock; + + /* Bounce buffer pool */ + struct blkio_mem_region bounce_pool; + + /* Sorted list of allocated bounce buffers */ + QLIST_HEAD(, BlkioBounceBuf) bounce_bufs; + + /* Queue for coroutines waiting for bounce buffer space */ + CoQueue bounce_available; + + /* The value of the "mem-region-alignment" property */ + uint64_t mem_region_alignment; + + /* Can we skip adding/deleting blkio_mem_regions? */ + bool needs_mem_regions; + + /* Are file descriptors necessary for blkio_mem_regions? */ + bool needs_mem_region_fd; + + /* Are madvise(MADV_DONTNEED)-style operations unavailable? */ + bool may_pin_mem_regions; +} BDRVBlkioState; + +/* Called with s->bounce_lock held */ +static int blkio_resize_bounce_pool(BDRVBlkioState *s, int64_t bytes) +{ + /* There can be no allocated bounce buffers during resize */ + assert(QLIST_EMPTY(&s->bounce_bufs)); + + /* Pad size to reduce frequency of resize calls */ + bytes += 128 * 1024; + + /* Align the pool size to avoid blkio_alloc_mem_region() failure */ + bytes = QEMU_ALIGN_UP(bytes, s->mem_region_alignment); + + WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { + int ret; + + if (s->bounce_pool.addr) { + blkio_unmap_mem_region(s->blkio, &s->bounce_pool); + blkio_free_mem_region(s->blkio, &s->bounce_pool); + memset(&s->bounce_pool, 0, sizeof(s->bounce_pool)); + } + + /* Automatically freed when s->blkio is destroyed */ + ret = blkio_alloc_mem_region(s->blkio, &s->bounce_pool, bytes); + if (ret < 0) { + return ret; + } + + ret = blkio_map_mem_region(s->blkio, &s->bounce_pool); + if (ret < 0) { + blkio_free_mem_region(s->blkio, &s->bounce_pool); + memset(&s->bounce_pool, 0, sizeof(s->bounce_pool)); + return ret; + } + } + + return 0; +} + +/* Called with s->bounce_lock held */ +static bool +blkio_do_alloc_bounce_buffer(BDRVBlkioState *s, BlkioBounceBuf *bounce, + int64_t bytes) +{ + void *addr = s->bounce_pool.addr; + BlkioBounceBuf *cur = NULL; + BlkioBounceBuf *prev = NULL; + ptrdiff_t space; + + /* + * This is just a linear search over the holes between requests. An + * efficient allocator would be nice. + */ + QLIST_FOREACH(cur, &s->bounce_bufs, next) { + space = cur->buf.iov_base - addr; + if (bytes <= space) { + QLIST_INSERT_BEFORE(cur, bounce, next); + bounce->buf.iov_base = addr; + bounce->buf.iov_len = bytes; + return true; + } + + addr = cur->buf.iov_base + cur->buf.iov_len; + prev = cur; + } + + /* Is there space after the last request? */ + space = s->bounce_pool.addr + s->bounce_pool.len - addr; + if (bytes > space) { + return false; + } + if (prev) { + QLIST_INSERT_AFTER(prev, bounce, next); + } else { + QLIST_INSERT_HEAD(&s->bounce_bufs, bounce, next); + } + bounce->buf.iov_base = addr; + bounce->buf.iov_len = bytes; + return true; +} + +static int coroutine_fn +blkio_alloc_bounce_buffer(BDRVBlkioState *s, BlkioBounceBuf *bounce, + int64_t bytes) +{ + /* + * Ensure fairness: first time around we join the back of the queue, + * subsequently we join the front so we don't lose our place. + */ + CoQueueWaitFlags wait_flags = 0; + + QEMU_LOCK_GUARD(&s->bounce_lock); + + /* Ensure fairness: don't even try if other requests are already waiting */ + if (!qemu_co_queue_empty(&s->bounce_available)) { + qemu_co_queue_wait_flags(&s->bounce_available, &s->bounce_lock, + wait_flags); + wait_flags = CO_QUEUE_WAIT_FRONT; + } + + while (true) { + if (blkio_do_alloc_bounce_buffer(s, bounce, bytes)) { + /* Kick the next queued request since there may be space */ + qemu_co_queue_next(&s->bounce_available); + return 0; + } + + /* + * If there are no in-flight requests then the pool was simply too + * small. + */ + if (QLIST_EMPTY(&s->bounce_bufs)) { + bool ok; + int ret; + + ret = blkio_resize_bounce_pool(s, bytes); + if (ret < 0) { + /* Kick the next queued request since that may fail too */ + qemu_co_queue_next(&s->bounce_available); + return ret; + } + + ok = blkio_do_alloc_bounce_buffer(s, bounce, bytes); + assert(ok); /* must have space this time */ + return 0; + } + + qemu_co_queue_wait_flags(&s->bounce_available, &s->bounce_lock, + wait_flags); + wait_flags = CO_QUEUE_WAIT_FRONT; + } +} + +static void coroutine_fn blkio_free_bounce_buffer(BDRVBlkioState *s, + BlkioBounceBuf *bounce) +{ + QEMU_LOCK_GUARD(&s->bounce_lock); + + QLIST_REMOVE(bounce, next); + + /* Wake up waiting coroutines since space may now be available */ + qemu_co_queue_next(&s->bounce_available); +} + +/* For async to .bdrv_co_*() conversion */ +typedef struct { + Coroutine *coroutine; + int ret; +} BlkioCoData; + +static void blkio_completion_fd_read(void *opaque) +{ + BlockDriverState *bs = opaque; + BDRVBlkioState *s = bs->opaque; + uint64_t val; + int ret; + + /* Polling may have already fetched a completion */ + if (s->poll_completion.user_data != NULL) { + BlkioCoData *cod = s->poll_completion.user_data; + cod->ret = s->poll_completion.ret; + + /* Clear it in case aio_co_wake() enters a nested event loop */ + s->poll_completion.user_data = NULL; + + aio_co_wake(cod->coroutine); + } + + /* Reset completion fd status */ + ret = read(s->completion_fd, &val, sizeof(val)); + + /* Ignore errors, there's nothing we can do */ + (void)ret; + + /* + * Reading one completion at a time makes nested event loop re-entrancy + * simple. Change this loop to get multiple completions in one go if it + * becomes a performance bottleneck. + */ + while (true) { + struct blkio_completion completion; + + WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { + ret = blkioq_do_io(s->blkioq, &completion, 0, 1, NULL); + } + if (ret != 1) { + break; + } + + BlkioCoData *cod = completion.user_data; + cod->ret = completion.ret; + aio_co_wake(cod->coroutine); + } +} + +static bool blkio_completion_fd_poll(void *opaque) +{ + BlockDriverState *bs = opaque; + BDRVBlkioState *s = bs->opaque; + int ret; + + /* Just in case we already fetched a completion */ + if (s->poll_completion.user_data != NULL) { + return true; + } + + WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { + ret = blkioq_do_io(s->blkioq, &s->poll_completion, 0, 1, NULL); + } + return ret == 1; +} + +static void blkio_completion_fd_poll_ready(void *opaque) +{ + blkio_completion_fd_read(opaque); +} + +static void blkio_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + BDRVBlkioState *s = bs->opaque; + + aio_set_fd_handler(new_context, s->completion_fd, + blkio_completion_fd_read, NULL, + blkio_completion_fd_poll, + blkio_completion_fd_poll_ready, bs); +} + +static void blkio_detach_aio_context(BlockDriverState *bs) +{ + BDRVBlkioState *s = bs->opaque; + + aio_set_fd_handler(bdrv_get_aio_context(bs), s->completion_fd, NULL, NULL, + NULL, NULL, NULL); +} + +/* + * Called by defer_call_end() or immediately if not in a deferred section. + * Called without blkio_lock. + */ +static void blkio_deferred_fn(void *opaque) +{ + BDRVBlkioState *s = opaque; + + WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { + blkioq_do_io(s->blkioq, NULL, 0, 0, NULL); + } +} + +/* + * Schedule I/O submission after enqueuing a new request. Called without + * blkio_lock. + */ +static void blkio_submit_io(BlockDriverState *bs) +{ + BDRVBlkioState *s = bs->opaque; + + defer_call(blkio_deferred_fn, s); +} + +static int coroutine_fn +blkio_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) +{ + BDRVBlkioState *s = bs->opaque; + BlkioCoData cod = { + .coroutine = qemu_coroutine_self(), + }; + + WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { + blkioq_discard(s->blkioq, offset, bytes, &cod, 0); + } + + blkio_submit_io(bs); + qemu_coroutine_yield(); + return cod.ret; +} + +static int coroutine_fn +blkio_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) +{ + BlkioCoData cod = { + .coroutine = qemu_coroutine_self(), + }; + BDRVBlkioState *s = bs->opaque; + bool use_bounce_buffer = + s->needs_mem_regions && !(flags & BDRV_REQ_REGISTERED_BUF); + BlkioBounceBuf bounce; + struct iovec *iov = qiov->iov; + int iovcnt = qiov->niov; + + if (use_bounce_buffer) { + int ret = blkio_alloc_bounce_buffer(s, &bounce, bytes); + if (ret < 0) { + return ret; + } + + iov = &bounce.buf; + iovcnt = 1; + } + + WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { + blkioq_readv(s->blkioq, offset, iov, iovcnt, &cod, 0); + } + + blkio_submit_io(bs); + qemu_coroutine_yield(); + + if (use_bounce_buffer) { + if (cod.ret == 0) { + qemu_iovec_from_buf(qiov, 0, + bounce.buf.iov_base, + bounce.buf.iov_len); + } + + blkio_free_bounce_buffer(s, &bounce); + } + + return cod.ret; +} + +static int coroutine_fn blkio_co_pwritev(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) +{ + uint32_t blkio_flags = (flags & BDRV_REQ_FUA) ? BLKIO_REQ_FUA : 0; + BlkioCoData cod = { + .coroutine = qemu_coroutine_self(), + }; + BDRVBlkioState *s = bs->opaque; + bool use_bounce_buffer = + s->needs_mem_regions && !(flags & BDRV_REQ_REGISTERED_BUF); + BlkioBounceBuf bounce; + struct iovec *iov = qiov->iov; + int iovcnt = qiov->niov; + + if (use_bounce_buffer) { + int ret = blkio_alloc_bounce_buffer(s, &bounce, bytes); + if (ret < 0) { + return ret; + } + + qemu_iovec_to_buf(qiov, 0, bounce.buf.iov_base, bytes); + iov = &bounce.buf; + iovcnt = 1; + } + + WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { + blkioq_writev(s->blkioq, offset, iov, iovcnt, &cod, blkio_flags); + } + + blkio_submit_io(bs); + qemu_coroutine_yield(); + + if (use_bounce_buffer) { + blkio_free_bounce_buffer(s, &bounce); + } + + return cod.ret; +} + +static int coroutine_fn blkio_co_flush(BlockDriverState *bs) +{ + BDRVBlkioState *s = bs->opaque; + BlkioCoData cod = { + .coroutine = qemu_coroutine_self(), + }; + + WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { + blkioq_flush(s->blkioq, &cod, 0); + } + + blkio_submit_io(bs); + qemu_coroutine_yield(); + return cod.ret; +} + +static int coroutine_fn blkio_co_pwrite_zeroes(BlockDriverState *bs, + int64_t offset, int64_t bytes, BdrvRequestFlags flags) +{ + BDRVBlkioState *s = bs->opaque; + BlkioCoData cod = { + .coroutine = qemu_coroutine_self(), + }; + uint32_t blkio_flags = 0; + + if (flags & BDRV_REQ_FUA) { + blkio_flags |= BLKIO_REQ_FUA; + } + if (!(flags & BDRV_REQ_MAY_UNMAP)) { + blkio_flags |= BLKIO_REQ_NO_UNMAP; + } + if (flags & BDRV_REQ_NO_FALLBACK) { + blkio_flags |= BLKIO_REQ_NO_FALLBACK; + } + + WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { + blkioq_write_zeroes(s->blkioq, offset, bytes, &cod, blkio_flags); + } + + blkio_submit_io(bs); + qemu_coroutine_yield(); + return cod.ret; +} + +typedef enum { + BMRR_OK, + BMRR_SKIP, + BMRR_FAIL, +} BlkioMemRegionResult; + +/* + * Produce a struct blkio_mem_region for a given address and size. + * + * This function produces identical results when called multiple times with the + * same arguments. This property is necessary because blkio_unmap_mem_region() + * must receive the same struct blkio_mem_region field values that were passed + * to blkio_map_mem_region(). + */ +static BlkioMemRegionResult +blkio_mem_region_from_host(BlockDriverState *bs, + void *host, size_t size, + struct blkio_mem_region *region, + Error **errp) +{ + BDRVBlkioState *s = bs->opaque; + int fd = -1; + ram_addr_t fd_offset = 0; + + if (((uintptr_t)host | size) % s->mem_region_alignment) { + error_setg(errp, "unaligned buf %p with size %zu", host, size); + return BMRR_FAIL; + } + + /* Attempt to find the fd for the underlying memory */ + if (s->needs_mem_region_fd) { + RAMBlock *ram_block; + RAMBlock *end_block; + ram_addr_t offset; + + /* + * bdrv_register_buf() is called with the BQL held so mr lives at least + * until this function returns. + */ + ram_block = qemu_ram_block_from_host(host, false, &fd_offset); + if (ram_block) { + fd = qemu_ram_get_fd(ram_block); + } + if (fd == -1) { + /* + * Ideally every RAMBlock would have an fd. pc-bios and other + * things don't. Luckily they are usually not I/O buffers and we + * can just ignore them. + */ + return BMRR_SKIP; + } + + /* Make sure the fd covers the entire range */ + end_block = qemu_ram_block_from_host(host + size - 1, false, &offset); + if (ram_block != end_block) { + error_setg(errp, "registered buffer at %p with size %zu extends " + "beyond RAMBlock", host, size); + return BMRR_FAIL; + } + } + + *region = (struct blkio_mem_region){ + .addr = host, + .len = size, + .fd = fd, + .fd_offset = fd_offset, + }; + return BMRR_OK; +} + +static bool blkio_register_buf(BlockDriverState *bs, void *host, size_t size, + Error **errp) +{ + BDRVBlkioState *s = bs->opaque; + struct blkio_mem_region region; + BlkioMemRegionResult region_result; + int ret; + + /* + * Mapping memory regions conflicts with RAM discard (virtio-mem) when + * there is pinning, so only do it when necessary. + */ + if (!s->needs_mem_regions && s->may_pin_mem_regions) { + return true; + } + + region_result = blkio_mem_region_from_host(bs, host, size, ®ion, errp); + if (region_result == BMRR_SKIP) { + return true; + } else if (region_result != BMRR_OK) { + return false; + } + + WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { + ret = blkio_map_mem_region(s->blkio, ®ion); + } + + if (ret < 0) { + error_setg(errp, "Failed to add blkio mem region %p with size %zu: %s", + host, size, blkio_get_error_msg()); + return false; + } + return true; +} + +static void blkio_unregister_buf(BlockDriverState *bs, void *host, size_t size) +{ + BDRVBlkioState *s = bs->opaque; + struct blkio_mem_region region; + + /* See blkio_register_buf() */ + if (!s->needs_mem_regions && s->may_pin_mem_regions) { + return; + } + + if (blkio_mem_region_from_host(bs, host, size, ®ion, NULL) != BMRR_OK) { + return; + } + + WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { + blkio_unmap_mem_region(s->blkio, ®ion); + } +} + +static int blkio_io_uring_connect(BlockDriverState *bs, QDict *options, + int flags, Error **errp) +{ + const char *filename = qdict_get_str(options, "filename"); + BDRVBlkioState *s = bs->opaque; + int ret; + + ret = blkio_set_str(s->blkio, "path", filename); + qdict_del(options, "filename"); + if (ret < 0) { + error_setg_errno(errp, -ret, "failed to set path: %s", + blkio_get_error_msg()); + return ret; + } + + if (flags & BDRV_O_NOCACHE) { + ret = blkio_set_bool(s->blkio, "direct", true); + if (ret < 0) { + error_setg_errno(errp, -ret, "failed to set direct: %s", + blkio_get_error_msg()); + return ret; + } + } + + ret = blkio_connect(s->blkio); + if (ret < 0) { + error_setg_errno(errp, -ret, "blkio_connect failed: %s", + blkio_get_error_msg()); + return ret; + } + + return 0; +} + +static int blkio_nvme_io_uring_connect(BlockDriverState *bs, QDict *options, + int flags, Error **errp) +{ + const char *path = qdict_get_try_str(options, "path"); + BDRVBlkioState *s = bs->opaque; + int ret; + + if (!path) { + error_setg(errp, "missing 'path' option"); + return -EINVAL; + } + + ret = blkio_set_str(s->blkio, "path", path); + qdict_del(options, "path"); + if (ret < 0) { + error_setg_errno(errp, -ret, "failed to set path: %s", + blkio_get_error_msg()); + return ret; + } + + if (!(flags & BDRV_O_NOCACHE)) { + error_setg(errp, "cache.direct=off is not supported"); + return -EINVAL; + } + + ret = blkio_connect(s->blkio); + if (ret < 0) { + error_setg_errno(errp, -ret, "blkio_connect failed: %s", + blkio_get_error_msg()); + return ret; + } + + return 0; +} + +static int blkio_virtio_blk_connect(BlockDriverState *bs, QDict *options, + int flags, Error **errp) +{ + const char *path = qdict_get_try_str(options, "path"); + BDRVBlkioState *s = bs->opaque; + bool fd_supported = false; + int fd = -1, ret; + + if (!path) { + error_setg(errp, "missing 'path' option"); + return -EINVAL; + } + + if (!(flags & BDRV_O_NOCACHE)) { + error_setg(errp, "cache.direct=off is not supported"); + return -EINVAL; + } + + if (blkio_set_int(s->blkio, "fd", -1) == 0) { + fd_supported = true; + } + + /* + * If the libblkio driver supports fd passing, let's always use qemu_open() + * to open the `path`, so we can handle fd passing from the management + * layer through the "/dev/fdset/N" special path. + */ + if (fd_supported) { + /* + * `path` can contain the path of a character device + * (e.g. /dev/vhost-vdpa-0 or /dev/vfio/vfio) or a unix socket. + * + * So, we should always open it with O_RDWR flag, also if BDRV_O_RDWR + * is not set in the open flags, because the exchange of IOCTL commands + * for example will fail. + * + * In order to open the device read-only, we are using the `read-only` + * property of the libblkio driver in blkio_file_open(). + */ + fd = qemu_open(path, O_RDWR, NULL); + if (fd < 0) { + /* + * qemu_open() can fail if the user specifies a path that is not + * a file or device, for example in the case of Unix Domain Socket + * for the virtio-blk-vhost-user driver. In such cases let's have + * libblkio open the path directly. + */ + fd_supported = false; + } else { + ret = blkio_set_int(s->blkio, "fd", fd); + if (ret < 0) { + fd_supported = false; + qemu_close(fd); + fd = -1; + } + } + } + + if (!fd_supported) { + ret = blkio_set_str(s->blkio, "path", path); + if (ret < 0) { + error_setg_errno(errp, -ret, "failed to set path: %s", + blkio_get_error_msg()); + return ret; + } + } + + ret = blkio_connect(s->blkio); + if (ret < 0 && fd >= 0) { + /* Failed to give the FD to libblkio, close it */ + qemu_close(fd); + fd = -1; + } + + /* + * Before https://gitlab.com/libblkio/libblkio/-/merge_requests/208 + * (libblkio <= v1.3.0), setting the `fd` property is not enough to check + * whether the driver supports the `fd` property or not. In that case, + * blkio_connect() will fail with -EINVAL. + * So let's try calling blkio_connect() again by directly setting `path` + * to cover this scenario. + */ + if (fd_supported && ret == -EINVAL) { + /* + * We need to clear the `fd` property we set previously by setting + * it to -1. + */ + ret = blkio_set_int(s->blkio, "fd", -1); + if (ret < 0) { + error_setg_errno(errp, -ret, "failed to set fd: %s", + blkio_get_error_msg()); + return ret; + } + + ret = blkio_set_str(s->blkio, "path", path); + if (ret < 0) { + error_setg_errno(errp, -ret, "failed to set path: %s", + blkio_get_error_msg()); + return ret; + } + + ret = blkio_connect(s->blkio); + } + + if (ret < 0) { + error_setg_errno(errp, -ret, "blkio_connect failed: %s", + blkio_get_error_msg()); + return ret; + } + + qdict_del(options, "path"); + + return 0; +} + +static int blkio_file_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + const char *blkio_driver = bs->drv->protocol_name; + BDRVBlkioState *s = bs->opaque; + int ret; + + ret = blkio_create(blkio_driver, &s->blkio); + if (ret < 0) { + error_setg_errno(errp, -ret, "blkio_create failed: %s", + blkio_get_error_msg()); + return ret; + } + + if (!(flags & BDRV_O_RDWR)) { + ret = blkio_set_bool(s->blkio, "read-only", true); + if (ret < 0) { + error_setg_errno(errp, -ret, "failed to set read-only: %s", + blkio_get_error_msg()); + blkio_destroy(&s->blkio); + return ret; + } + } + + if (strcmp(blkio_driver, "io_uring") == 0) { + ret = blkio_io_uring_connect(bs, options, flags, errp); + } else if (strcmp(blkio_driver, "nvme-io_uring") == 0) { + ret = blkio_nvme_io_uring_connect(bs, options, flags, errp); + } else if (strcmp(blkio_driver, "virtio-blk-vfio-pci") == 0) { + ret = blkio_virtio_blk_connect(bs, options, flags, errp); + } else if (strcmp(blkio_driver, "virtio-blk-vhost-user") == 0) { + ret = blkio_virtio_blk_connect(bs, options, flags, errp); + } else if (strcmp(blkio_driver, "virtio-blk-vhost-vdpa") == 0) { + ret = blkio_virtio_blk_connect(bs, options, flags, errp); + } else { + g_assert_not_reached(); + } + if (ret < 0) { + blkio_destroy(&s->blkio); + return ret; + } + + ret = blkio_get_bool(s->blkio, + "needs-mem-regions", + &s->needs_mem_regions); + if (ret < 0) { + error_setg_errno(errp, -ret, + "failed to get needs-mem-regions: %s", + blkio_get_error_msg()); + blkio_destroy(&s->blkio); + return ret; + } + + ret = blkio_get_bool(s->blkio, + "needs-mem-region-fd", + &s->needs_mem_region_fd); + if (ret < 0) { + error_setg_errno(errp, -ret, + "failed to get needs-mem-region-fd: %s", + blkio_get_error_msg()); + blkio_destroy(&s->blkio); + return ret; + } + + ret = blkio_get_uint64(s->blkio, + "mem-region-alignment", + &s->mem_region_alignment); + if (ret < 0) { + error_setg_errno(errp, -ret, + "failed to get mem-region-alignment: %s", + blkio_get_error_msg()); + blkio_destroy(&s->blkio); + return ret; + } + + ret = blkio_get_bool(s->blkio, + "may-pin-mem-regions", + &s->may_pin_mem_regions); + if (ret < 0) { + /* Be conservative (assume pinning) if the property is not supported */ + s->may_pin_mem_regions = s->needs_mem_regions; + } + + /* + * Notify if libblkio drivers pin memory and prevent features like + * virtio-mem from working. + */ + if (s->may_pin_mem_regions) { + ret = ram_block_discard_disable(true); + if (ret < 0) { + error_setg_errno(errp, -ret, "ram_block_discard_disable() failed"); + blkio_destroy(&s->blkio); + return ret; + } + } + + ret = blkio_start(s->blkio); + if (ret < 0) { + error_setg_errno(errp, -ret, "blkio_start failed: %s", + blkio_get_error_msg()); + blkio_destroy(&s->blkio); + if (s->may_pin_mem_regions) { + ram_block_discard_disable(false); + } + return ret; + } + + bs->supported_write_flags = BDRV_REQ_FUA | BDRV_REQ_REGISTERED_BUF; + bs->supported_zero_flags = BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | + BDRV_REQ_NO_FALLBACK; + + qemu_mutex_init(&s->blkio_lock); + qemu_co_mutex_init(&s->bounce_lock); + qemu_co_queue_init(&s->bounce_available); + QLIST_INIT(&s->bounce_bufs); + s->blkioq = blkio_get_queue(s->blkio, 0); + s->completion_fd = blkioq_get_completion_fd(s->blkioq); + blkioq_set_completion_fd_enabled(s->blkioq, true); + + blkio_attach_aio_context(bs, bdrv_get_aio_context(bs)); + return 0; +} + +static void blkio_close(BlockDriverState *bs) +{ + BDRVBlkioState *s = bs->opaque; + + /* There is no destroy() API for s->bounce_lock */ + + qemu_mutex_destroy(&s->blkio_lock); + blkio_detach_aio_context(bs); + blkio_destroy(&s->blkio); + + if (s->may_pin_mem_regions) { + ram_block_discard_disable(false); + } +} + +static int64_t coroutine_fn blkio_co_getlength(BlockDriverState *bs) +{ + BDRVBlkioState *s = bs->opaque; + uint64_t capacity; + int ret; + + WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { + ret = blkio_get_uint64(s->blkio, "capacity", &capacity); + } + if (ret < 0) { + return -ret; + } + + return capacity; +} + +static int coroutine_fn blkio_truncate(BlockDriverState *bs, int64_t offset, + bool exact, PreallocMode prealloc, + BdrvRequestFlags flags, Error **errp) +{ + int64_t current_length; + + if (prealloc != PREALLOC_MODE_OFF) { + error_setg(errp, "Unsupported preallocation mode '%s'", + PreallocMode_str(prealloc)); + return -ENOTSUP; + } + + current_length = blkio_co_getlength(bs); + + if (offset > current_length) { + error_setg(errp, "Cannot grow device"); + return -EINVAL; + } else if (exact && offset != current_length) { + error_setg(errp, "Cannot resize device"); + return -ENOTSUP; + } + + return 0; +} + +static int coroutine_fn +blkio_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + return 0; +} + +static void blkio_refresh_limits(BlockDriverState *bs, Error **errp) +{ + BDRVBlkioState *s = bs->opaque; + QEMU_LOCK_GUARD(&s->blkio_lock); + int value; + int ret; + + ret = blkio_get_int(s->blkio, "request-alignment", &value); + if (ret < 0) { + error_setg_errno(errp, -ret, "failed to get \"request-alignment\": %s", + blkio_get_error_msg()); + return; + } + bs->bl.request_alignment = value; + if (bs->bl.request_alignment < 1 || + bs->bl.request_alignment >= INT_MAX || + !is_power_of_2(bs->bl.request_alignment)) { + error_setg(errp, "invalid \"request-alignment\" value %" PRIu32 ", " + "must be a power of 2 less than INT_MAX", + bs->bl.request_alignment); + return; + } + + ret = blkio_get_int(s->blkio, "optimal-io-size", &value); + if (ret < 0) { + error_setg_errno(errp, -ret, "failed to get \"optimal-io-size\": %s", + blkio_get_error_msg()); + return; + } + bs->bl.opt_transfer = value; + if (bs->bl.opt_transfer > INT_MAX || + (bs->bl.opt_transfer % bs->bl.request_alignment)) { + error_setg(errp, "invalid \"optimal-io-size\" value %" PRIu32 ", must " + "be a multiple of %" PRIu32, bs->bl.opt_transfer, + bs->bl.request_alignment); + return; + } + + ret = blkio_get_int(s->blkio, "max-transfer", &value); + if (ret < 0) { + error_setg_errno(errp, -ret, "failed to get \"max-transfer\": %s", + blkio_get_error_msg()); + return; + } + bs->bl.max_transfer = value; + if ((bs->bl.max_transfer % bs->bl.request_alignment) || + (bs->bl.opt_transfer && (bs->bl.max_transfer % bs->bl.opt_transfer))) { + error_setg(errp, "invalid \"max-transfer\" value %" PRIu32 ", must be " + "a multiple of %" PRIu32 " and %" PRIu32 " (if non-zero)", + bs->bl.max_transfer, bs->bl.request_alignment, + bs->bl.opt_transfer); + return; + } + + ret = blkio_get_int(s->blkio, "buf-alignment", &value); + if (ret < 0) { + error_setg_errno(errp, -ret, "failed to get \"buf-alignment\": %s", + blkio_get_error_msg()); + return; + } + if (value < 1) { + error_setg(errp, "invalid \"buf-alignment\" value %d, must be " + "positive", value); + return; + } + bs->bl.min_mem_alignment = value; + + ret = blkio_get_int(s->blkio, "optimal-buf-alignment", &value); + if (ret < 0) { + error_setg_errno(errp, -ret, + "failed to get \"optimal-buf-alignment\": %s", + blkio_get_error_msg()); + return; + } + if (value < 1) { + error_setg(errp, "invalid \"optimal-buf-alignment\" value %d, " + "must be positive", value); + return; + } + bs->bl.opt_mem_alignment = value; + + ret = blkio_get_int(s->blkio, "max-segments", &value); + if (ret < 0) { + error_setg_errno(errp, -ret, "failed to get \"max-segments\": %s", + blkio_get_error_msg()); + return; + } + if (value < 1) { + error_setg(errp, "invalid \"max-segments\" value %d, must be positive", + value); + return; + } + bs->bl.max_iov = value; +} + +/* + * TODO + * Missing libblkio APIs: + * - block_status + * - co_invalidate_cache + * + * Out of scope? + * - create + * - truncate + */ + +/* + * Do not include .format_name and .protocol_name because module_block.py + * does not parse macros in the source code. + */ +#define BLKIO_DRIVER_COMMON \ + .instance_size = sizeof(BDRVBlkioState), \ + .bdrv_file_open = blkio_file_open, \ + .bdrv_close = blkio_close, \ + .bdrv_co_getlength = blkio_co_getlength, \ + .bdrv_co_truncate = blkio_truncate, \ + .bdrv_co_get_info = blkio_co_get_info, \ + .bdrv_attach_aio_context = blkio_attach_aio_context, \ + .bdrv_detach_aio_context = blkio_detach_aio_context, \ + .bdrv_co_pdiscard = blkio_co_pdiscard, \ + .bdrv_co_preadv = blkio_co_preadv, \ + .bdrv_co_pwritev = blkio_co_pwritev, \ + .bdrv_co_flush_to_disk = blkio_co_flush, \ + .bdrv_co_pwrite_zeroes = blkio_co_pwrite_zeroes, \ + .bdrv_refresh_limits = blkio_refresh_limits, \ + .bdrv_register_buf = blkio_register_buf, \ + .bdrv_unregister_buf = blkio_unregister_buf, + +/* + * Use the same .format_name and .protocol_name as the libblkio driver name for + * consistency. + */ + +static BlockDriver bdrv_io_uring = { + .format_name = "io_uring", + .protocol_name = "io_uring", + .bdrv_needs_filename = true, + BLKIO_DRIVER_COMMON +}; + +static BlockDriver bdrv_nvme_io_uring = { + .format_name = "nvme-io_uring", + .protocol_name = "nvme-io_uring", + BLKIO_DRIVER_COMMON +}; + +static BlockDriver bdrv_virtio_blk_vfio_pci = { + .format_name = "virtio-blk-vfio-pci", + .protocol_name = "virtio-blk-vfio-pci", + BLKIO_DRIVER_COMMON +}; + +static BlockDriver bdrv_virtio_blk_vhost_user = { + .format_name = "virtio-blk-vhost-user", + .protocol_name = "virtio-blk-vhost-user", + BLKIO_DRIVER_COMMON +}; + +static BlockDriver bdrv_virtio_blk_vhost_vdpa = { + .format_name = "virtio-blk-vhost-vdpa", + .protocol_name = "virtio-blk-vhost-vdpa", + BLKIO_DRIVER_COMMON +}; + +static void bdrv_blkio_init(void) +{ + bdrv_register(&bdrv_io_uring); + bdrv_register(&bdrv_nvme_io_uring); + bdrv_register(&bdrv_virtio_blk_vfio_pci); + bdrv_register(&bdrv_virtio_blk_vhost_user); + bdrv_register(&bdrv_virtio_blk_vhost_vdpa); +} + +block_init(bdrv_blkio_init); diff --git a/block/blklogwrites.c b/block/blklogwrites.c index f7a251e91f..ed38a93f21 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -3,7 +3,7 @@ * * Copyright (c) 2017 Tuomas Tynkkynen * Copyright (c) 2018 Aapo Vienamo - * Copyright (c) 2018 Ari Sundholm + * Copyright (c) 2018-2024 Ari Sundholm * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/sockets.h" /* for EINPROGRESS on Windows */ +#include "block/block-io.h" #include "block/block_int.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" @@ -54,9 +55,34 @@ typedef struct { BdrvChild *log_file; uint32_t sectorsize; uint32_t sectorbits; + uint64_t update_interval; + + /* + * The mutable state of the driver, consisting of the current log sector + * and the number of log entries. + * + * May be read and/or written from multiple threads, and the mutex must be + * held when accessing these fields. + */ uint64_t cur_log_sector; uint64_t nr_entries; - uint64_t update_interval; + QemuMutex mutex; + + /* + * The super block sequence number. Non-zero if a super block update is in + * progress. + * + * The mutex must be held when accessing this field. + */ + uint64_t super_update_seq; + + /* + * A coroutine-aware queue to serialize super block updates. + * + * Used with the mutex to ensure that only one thread be updating the super + * block at a time. + */ + CoQueue super_update_queue; } BDRVBlkLogWritesState; static QemuOptsList runtime_opts = { @@ -107,8 +133,8 @@ static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log, struct log_write_entry cur_entry; while (cur_idx < nr_entries) { - int read_ret = bdrv_pread(log, cur_sector << sector_bits, &cur_entry, - sizeof(cur_entry)); + int read_ret = bdrv_pread(log, cur_sector << sector_bits, + sizeof(cur_entry), &cur_entry, 0); if (read_ret < 0) { error_setg_errno(errp, -read_ret, "Failed to read log entry %"PRIu64, cur_idx); @@ -155,11 +181,8 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, } /* Open the file */ - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, false, - errp); - if (!bs->file) { - ret = -EINVAL; + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { goto fail; } @@ -171,6 +194,9 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } + qemu_mutex_init(&s->mutex); + qemu_co_queue_init(&s->super_update_queue); + log_append = qemu_opt_get_bool(opts, "log-append", false); if (log_append) { @@ -190,7 +216,7 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, log_sb.nr_entries = cpu_to_le64(0); log_sb.sectorsize = cpu_to_le32(BDRV_SECTOR_SIZE); } else { - ret = bdrv_pread(s->log_file, 0, &log_sb, sizeof(log_sb)); + ret = bdrv_pread(s->log_file, 0, sizeof(log_sb), &log_sb, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read log superblock"); goto fail_log; @@ -233,6 +259,8 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, s->nr_entries = 0; } + s->super_update_seq = 0; + if (!blk_log_writes_sector_size_valid(log_sector_size)) { ret = -EINVAL; error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size); @@ -253,14 +281,13 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, ret = 0; fail_log: if (ret < 0) { + bdrv_graph_wrlock(); bdrv_unref_child(bs, s->log_file); + bdrv_graph_wrunlock(); s->log_file = NULL; + qemu_mutex_destroy(&s->mutex); } fail: - if (ret < 0) { - bdrv_unref_child(bs, bs->file); - bs->file = NULL; - } qemu_opts_del(opts); return ret; } @@ -269,13 +296,17 @@ static void blk_log_writes_close(BlockDriverState *bs) { BDRVBlkLogWritesState *s = bs->opaque; + bdrv_graph_wrlock(); bdrv_unref_child(bs, s->log_file); s->log_file = NULL; + bdrv_graph_wrunlock(); + qemu_mutex_destroy(&s->mutex); } -static int64_t blk_log_writes_getlength(BlockDriverState *bs) +static int64_t coroutine_fn GRAPH_RDLOCK +blk_log_writes_co_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + return bdrv_co_getlength(bs->file->bs); } static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c, @@ -296,11 +327,11 @@ static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c, static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp) { - BDRVBlkLogWritesState *s = bs->opaque; + const BDRVBlkLogWritesState *s = bs->opaque; bs->bl.request_alignment = s->sectorsize; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blk_log_writes_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -313,7 +344,7 @@ typedef struct BlkLogWritesFileReq { uint64_t bytes; int file_flags; QEMUIOVector *qiov; - int (*func)(struct BlkLogWritesFileReq *r); + int GRAPH_RDLOCK_PTR (*func)(struct BlkLogWritesFileReq *r); int file_ret; } BlkLogWritesFileReq; @@ -325,41 +356,89 @@ typedef struct { int log_ret; } BlkLogWritesLogReq; -static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr) +static void coroutine_fn GRAPH_RDLOCK +blk_log_writes_co_do_log(BlkLogWritesLogReq *lr) { BDRVBlkLogWritesState *s = lr->bs->opaque; - uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits; - s->nr_entries++; - s->cur_log_sector += - ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits; + /* + * Determine the offsets and sizes of different parts of the entry, and + * update the state of the driver. + * + * This needs to be done in one go, before any actual I/O is done, as the + * log entry may have to be written in two parts, and the state of the + * driver may be modified by other driver operations while waiting for the + * I/O to complete. + */ + qemu_mutex_lock(&s->mutex); + const uint64_t entry_start_sector = s->cur_log_sector; + const uint64_t entry_offset = entry_start_sector << s->sectorbits; + const uint64_t qiov_aligned_size = ROUND_UP(lr->qiov->size, s->sectorsize); + const uint64_t entry_aligned_size = qiov_aligned_size + + ROUND_UP(lr->zero_size, s->sectorsize); + const uint64_t entry_nr_sectors = entry_aligned_size >> s->sectorbits; + const uint64_t entry_seq = s->nr_entries + 1; - lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size, + s->nr_entries = entry_seq; + s->cur_log_sector += entry_nr_sectors; + qemu_mutex_unlock(&s->mutex); + + /* + * Write the log entry. Note that if this is a "write zeroes" operation, + * only the entry header is written here, with the zeroing being done + * separately below. + */ + lr->log_ret = bdrv_co_pwritev(s->log_file, entry_offset, lr->qiov->size, lr->qiov, 0); /* Logging for the "write zeroes" operation */ if (lr->log_ret == 0 && lr->zero_size) { - cur_log_offset = s->cur_log_sector << s->sectorbits; - s->cur_log_sector += - ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits; + const uint64_t zeroes_offset = entry_offset + qiov_aligned_size; - lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset, + lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, zeroes_offset, lr->zero_size, 0); } /* Update super block on flush or every update interval */ if (lr->log_ret == 0 && ((lr->entry.flags & LOG_FLUSH_FLAG) - || (s->nr_entries % s->update_interval == 0))) + || (entry_seq % s->update_interval == 0))) { struct log_write_super super = { .magic = cpu_to_le64(WRITE_LOG_MAGIC), .version = cpu_to_le64(WRITE_LOG_VERSION), - .nr_entries = cpu_to_le64(s->nr_entries), + .nr_entries = 0, /* updated below */ .sectorsize = cpu_to_le32(s->sectorsize), }; - void *zeroes = g_malloc0(s->sectorsize - sizeof(super)); + void *zeroes; QEMUIOVector qiov; + /* + * Wait if a super block update is already in progress. + * Bail out if a newer update got its turn before us. + */ + WITH_QEMU_LOCK_GUARD(&s->mutex) { + CoQueueWaitFlags wait_flags = 0; + while (s->super_update_seq) { + if (entry_seq < s->super_update_seq) { + return; + } + qemu_co_queue_wait_flags(&s->super_update_queue, + &s->mutex, wait_flags); + + /* + * In case the wait condition remains true after wakeup, + * to avoid starvation, make sure that this request is + * scheduled to rerun next by pushing it to the front of the + * queue. + */ + wait_flags = CO_QUEUE_WAIT_FRONT; + } + s->super_update_seq = entry_seq; + super.nr_entries = cpu_to_le64(s->nr_entries); + } + + zeroes = g_malloc0(s->sectorsize - sizeof(super)); + qemu_iovec_init(&qiov, 2); qemu_iovec_add(&qiov, &super, sizeof(super)); qemu_iovec_add(&qiov, zeroes, s->sectorsize - sizeof(super)); @@ -369,25 +448,33 @@ static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr) if (lr->log_ret == 0) { lr->log_ret = bdrv_co_flush(s->log_file->bs); } + + /* The super block has been updated. Let another request have a go. */ + qemu_mutex_lock(&s->mutex); + s->super_update_seq = 0; + (void) qemu_co_queue_next(&s->super_update_queue); + qemu_mutex_unlock(&s->mutex); + qemu_iovec_destroy(&qiov); g_free(zeroes); } } -static void coroutine_fn blk_log_writes_co_do_file(BlkLogWritesFileReq *fr) +static void coroutine_fn GRAPH_RDLOCK +blk_log_writes_co_do_file(BlkLogWritesFileReq *fr) { fr->file_ret = fr->func(fr); } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags, - int (*file_func)(BlkLogWritesFileReq *r), + int /*GRAPH_RDLOCK*/ (*file_func)(BlkLogWritesFileReq *r), uint64_t entry_flags, bool is_zero_write) { QEMUIOVector log_qiov; size_t niov = qiov ? qiov->niov : 0; - BDRVBlkLogWritesState *s = bs->opaque; + const BDRVBlkLogWritesState *s = bs->opaque; BlkLogWritesFileReq fr = { .bs = bs, .offset = offset, @@ -434,32 +521,33 @@ blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes, return fr.file_ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blk_log_writes_co_do_file_pwritev(BlkLogWritesFileReq *fr) { return bdrv_co_pwritev(fr->bs->file, fr->offset, fr->bytes, fr->qiov, fr->file_flags); } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blk_log_writes_co_do_file_pwrite_zeroes(BlkLogWritesFileReq *fr) { return bdrv_co_pwrite_zeroes(fr->bs->file, fr->offset, fr->bytes, fr->file_flags); } -static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr) +static int coroutine_fn GRAPH_RDLOCK +blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr) { return bdrv_co_flush(fr->bs->file->bs); } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr) { return bdrv_co_pdiscard(fr->bs->file, fr->offset, fr->bytes); } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blk_log_writes_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -467,7 +555,7 @@ blk_log_writes_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, blk_log_writes_co_do_file_pwritev, 0, false); } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, BdrvRequestFlags flags) { @@ -476,14 +564,15 @@ blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, true); } -static int coroutine_fn blk_log_writes_co_flush_to_disk(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK +blk_log_writes_co_flush_to_disk(BlockDriverState *bs) { return blk_log_writes_co_log(bs, 0, 0, NULL, 0, blk_log_writes_co_do_file_flush, LOG_FLUSH_FLAG, false); } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { return blk_log_writes_co_log(bs, offset, bytes, NULL, 0, @@ -504,7 +593,7 @@ static BlockDriver bdrv_blk_log_writes = { .bdrv_open = blk_log_writes_open, .bdrv_close = blk_log_writes_close, - .bdrv_getlength = blk_log_writes_getlength, + .bdrv_co_getlength = blk_log_writes_co_getlength, .bdrv_child_perm = blk_log_writes_child_perm, .bdrv_refresh_limits = blk_log_writes_refresh_limits, diff --git a/block/blkreplay.c b/block/blkreplay.c index dcbe780ddb..792d980aa9 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" +#include "block/block-io.h" #include "block/block_int.h" #include "sysemu/replay.h" #include "qapi/error.h" @@ -26,11 +27,8 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags, int ret; /* Open the image file */ - bs->file = bdrv_open_child(NULL, options, "image", bs, &child_of_bds, - BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, errp); - if (!bs->file) { - ret = -EINVAL; + ret = bdrv_open_file_child(NULL, options, "image", bs, errp); + if (ret < 0) { goto fail; } @@ -42,9 +40,10 @@ fail: return ret; } -static int64_t blkreplay_getlength(BlockDriverState *bs) +static int64_t coroutine_fn GRAPH_RDLOCK +blkreplay_co_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + return bdrv_co_getlength(bs->file->bs); } /* This bh is used for synchronization of return from coroutines. @@ -71,8 +70,9 @@ static void block_request_create(uint64_t reqid, BlockDriverState *bs, replay_block_event(req->bh, reqid); } -static int coroutine_fn blkreplay_co_preadv(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +blkreplay_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { uint64_t reqid = blkreplay_next_id(); int ret = bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); @@ -82,8 +82,9 @@ static int coroutine_fn blkreplay_co_preadv(BlockDriverState *bs, return ret; } -static int coroutine_fn blkreplay_co_pwritev(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +blkreplay_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { uint64_t reqid = blkreplay_next_id(); int ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); @@ -93,8 +94,9 @@ static int coroutine_fn blkreplay_co_pwritev(BlockDriverState *bs, return ret; } -static int coroutine_fn blkreplay_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int64_t bytes, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +blkreplay_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + BdrvRequestFlags flags) { uint64_t reqid = blkreplay_next_id(); int ret = bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); @@ -104,8 +106,8 @@ static int coroutine_fn blkreplay_co_pwrite_zeroes(BlockDriverState *bs, return ret; } -static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs, - int64_t offset, int64_t bytes) +static int coroutine_fn GRAPH_RDLOCK +blkreplay_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { uint64_t reqid = blkreplay_next_id(); int ret = bdrv_co_pdiscard(bs->file, offset, bytes); @@ -115,7 +117,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs, return ret; } -static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK blkreplay_co_flush(BlockDriverState *bs) { uint64_t reqid = blkreplay_next_id(); int ret = bdrv_co_flush(bs->file->bs); @@ -128,7 +130,13 @@ static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs) static int blkreplay_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) { - return bdrv_snapshot_goto(bs->file->bs, snapshot_id, NULL); + BlockDriverState *file_bs; + + bdrv_graph_rdlock_main_loop(); + file_bs = bs->file->bs; + bdrv_graph_rdunlock_main_loop(); + + return bdrv_snapshot_goto(file_bs, snapshot_id, NULL); } static BlockDriver bdrv_blkreplay = { @@ -138,7 +146,7 @@ static BlockDriver bdrv_blkreplay = { .bdrv_open = blkreplay_open, .bdrv_child_perm = bdrv_default_perms, - .bdrv_getlength = blkreplay_getlength, + .bdrv_co_getlength = blkreplay_co_getlength, .bdrv_co_preadv = blkreplay_co_preadv, .bdrv_co_pwritev = blkreplay_co_pwritev, diff --git a/block/blkverify.c b/block/blkverify.c index e4a37af3b2..ec45d8335e 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/sockets.h" /* for EINPROGRESS on Windows */ +#include "block/block-io.h" #include "block/block_int.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" @@ -32,8 +33,8 @@ typedef struct BlkverifyRequest { uint64_t bytes; int flags; - int (*request_fn)(BdrvChild *, int64_t, int64_t, QEMUIOVector *, - BdrvRequestFlags); + int GRAPH_RDLOCK_PTR (*request_fn)( + BdrvChild *, int64_t, int64_t, QEMUIOVector *, BdrvRequestFlags); int ret; /* test image result */ int raw_ret; /* raw image result */ @@ -122,12 +123,9 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, } /* Open the raw file */ - bs->file = bdrv_open_child(qemu_opt_get(opts, "x-raw"), options, "raw", - bs, &child_of_bds, - BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, errp); - if (!bs->file) { - ret = -EINVAL; + ret = bdrv_open_file_child(qemu_opt_get(opts, "x-raw"), options, "raw", + bs, errp); + if (ret < 0) { goto fail; } @@ -153,15 +151,18 @@ static void blkverify_close(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; + bdrv_graph_wrlock(); bdrv_unref_child(bs, s->test_file); s->test_file = NULL; + bdrv_graph_wrunlock(); } -static int64_t blkverify_getlength(BlockDriverState *bs) +static int64_t coroutine_fn GRAPH_RDLOCK +blkverify_co_getlength(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; - return bdrv_getlength(s->test_file->bs); + return bdrv_co_getlength(s->test_file->bs); } static void coroutine_fn blkverify_do_test_req(void *opaque) @@ -169,8 +170,11 @@ static void coroutine_fn blkverify_do_test_req(void *opaque) BlkverifyRequest *r = opaque; BDRVBlkverifyState *s = r->bs->opaque; + bdrv_graph_co_rdlock(); r->ret = r->request_fn(s->test_file, r->offset, r->bytes, r->qiov, r->flags); + bdrv_graph_co_rdunlock(); + r->done++; qemu_coroutine_enter_if_inactive(r->co); } @@ -179,13 +183,16 @@ static void coroutine_fn blkverify_do_raw_req(void *opaque) { BlkverifyRequest *r = opaque; + bdrv_graph_co_rdlock(); r->raw_ret = r->request_fn(r->bs->file, r->offset, r->bytes, r->raw_qiov, r->flags); + bdrv_graph_co_rdunlock(); + r->done++; qemu_coroutine_enter_if_inactive(r->co); } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blkverify_co_prwv(BlockDriverState *bs, BlkverifyRequest *r, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, QEMUIOVector *raw_qiov, int flags, bool is_write) @@ -221,7 +228,7 @@ blkverify_co_prwv(BlockDriverState *bs, BlkverifyRequest *r, uint64_t offset, return r->ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blkverify_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -235,8 +242,8 @@ blkverify_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, qemu_iovec_init(&raw_qiov, qiov->niov); qemu_iovec_clone(&raw_qiov, qiov, buf); - ret = blkverify_co_prwv(bs, &r, offset, bytes, qiov, &raw_qiov, flags, - false); + ret = blkverify_co_prwv(bs, &r, offset, bytes, qiov, &raw_qiov, + flags & ~BDRV_REQ_REGISTERED_BUF, false); cmp_offset = qemu_iovec_compare(qiov, &raw_qiov); if (cmp_offset != -1) { @@ -250,7 +257,7 @@ blkverify_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, return ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK blkverify_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -258,7 +265,7 @@ blkverify_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, return blkverify_co_prwv(bs, &r, offset, bytes, qiov, qiov, flags, true); } -static int blkverify_co_flush(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK blkverify_co_flush(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; @@ -266,8 +273,9 @@ static int blkverify_co_flush(BlockDriverState *bs) return bdrv_co_flush(s->test_file->bs); } -static bool blkverify_recurse_can_replace(BlockDriverState *bs, - BlockDriverState *to_replace) +static bool GRAPH_RDLOCK +blkverify_recurse_can_replace(BlockDriverState *bs, + BlockDriverState *to_replace) { BDRVBlkverifyState *s = bs->opaque; @@ -280,7 +288,7 @@ static bool blkverify_recurse_can_replace(BlockDriverState *bs, bdrv_recurse_can_replace(s->test_file->bs, to_replace); } -static void blkverify_refresh_filename(BlockDriverState *bs) +static void GRAPH_RDLOCK blkverify_refresh_filename(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; @@ -316,7 +324,7 @@ static BlockDriver bdrv_blkverify = { .bdrv_file_open = blkverify_open, .bdrv_close = blkverify_close, .bdrv_child_perm = bdrv_default_perms, - .bdrv_getlength = blkverify_getlength, + .bdrv_co_getlength = blkverify_co_getlength, .bdrv_refresh_filename = blkverify_refresh_filename, .bdrv_dirname = blkverify_dirname, diff --git a/block/block-backend.c b/block/block-backend.c index e0e1aff4b1..db6f9b92a3 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -33,8 +33,6 @@ #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ -static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); - typedef struct BlockBackendAioNotifier { void (*attached_aio_context)(AioContext *new_context, void *opaque); void (*detach_aio_context)(void *opaque); @@ -46,7 +44,7 @@ struct BlockBackend { char *name; int refcnt; BdrvChild *root; - AioContext *ctx; + AioContext *ctx; /* access with atomic operations only */ DriveInfo *legacy_dinfo; /* null unless created by drive_new() */ QTAILQ_ENTRY(BlockBackend) link; /* for block_backends */ QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ @@ -56,9 +54,6 @@ struct BlockBackend { const BlockDevOps *dev_ops; void *dev_opaque; - /* the block size for which the guest device expects atomicity */ - int guest_block_size; - /* If the BDS tree is removed, some of its options are stored here (which * can be used to restore those options in the new BDS on insert) */ BlockBackendRootState root_state; @@ -83,9 +78,10 @@ struct BlockBackend { NotifierList remove_bs_notifiers, insert_bs_notifiers; QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers; - int quiesce_counter; + int quiesce_counter; /* atomic: written under BQL, read by other threads */ + QemuMutex queued_requests_lock; /* protects queued_requests */ CoQueue queued_requests; - bool disable_request_queuing; + bool disable_request_queuing; /* atomic */ VMChangeStateEntry *vmsh; bool force_allow_inactivate; @@ -105,7 +101,6 @@ typedef struct BlockBackendAIOCB { } BlockBackendAIOCB; static const AIOCBInfo block_backend_aiocb_info = { - .get_aio_context = blk_aiocb_get_aio_context, .aiocb_size = sizeof(BlockBackendAIOCB), }; @@ -123,6 +118,10 @@ static QTAILQ_HEAD(, BlockBackend) block_backends = static QTAILQ_HEAD(, BlockBackend) monitor_block_backends = QTAILQ_HEAD_INITIALIZER(monitor_block_backends); +static int coroutine_mixed_fn GRAPH_RDLOCK +blk_set_perm_locked(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, + Error **errp); + static void blk_root_inherit_options(BdrvChildRole role, bool parent_is_format, int *child_flags, QDict *child_options, int parent_flags, QDict *parent_options) @@ -132,15 +131,14 @@ static void blk_root_inherit_options(BdrvChildRole role, bool parent_is_format, } static void blk_root_drained_begin(BdrvChild *child); static bool blk_root_drained_poll(BdrvChild *child); -static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter); +static void blk_root_drained_end(BdrvChild *child); static void blk_root_change_media(BdrvChild *child, bool load); static void blk_root_resize(BdrvChild *child); -static bool blk_root_can_set_aio_ctx(BdrvChild *child, AioContext *ctx, - GSList **ignore, Error **errp); -static void blk_root_set_aio_ctx(BdrvChild *child, AioContext *ctx, - GSList **ignore); +static bool blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp); static char *blk_root_get_parent_desc(BdrvChild *child) { @@ -189,7 +187,7 @@ static void blk_vm_state_changed(void *opaque, bool running, RunState state) * * If an error is returned, the VM cannot be allowed to be resumed. */ -static void blk_root_activate(BdrvChild *child, Error **errp) +static void GRAPH_RDLOCK blk_root_activate(BdrvChild *child, Error **errp) { BlockBackend *blk = child->opaque; Error *local_err = NULL; @@ -210,7 +208,7 @@ static void blk_root_activate(BdrvChild *child, Error **errp) */ saved_shared_perm = blk->shared_perm; - blk_set_perm(blk, blk->perm, BLK_PERM_ALL, &local_err); + blk_set_perm_locked(blk, blk->perm, BLK_PERM_ALL, &local_err); if (local_err) { error_propagate(errp, local_err); blk->disable_perm = true; @@ -229,7 +227,7 @@ static void blk_root_activate(BdrvChild *child, Error **errp) return; } - blk_set_perm(blk, blk->perm, blk->shared_perm, &local_err); + blk_set_perm_locked(blk, blk->perm, blk->shared_perm, &local_err); if (local_err) { error_propagate(errp, local_err); blk->disable_perm = true; @@ -262,7 +260,7 @@ static bool blk_can_inactivate(BlockBackend *blk) return blk->force_allow_inactivate; } -static int blk_root_inactivate(BdrvChild *child) +static int GRAPH_RDLOCK blk_root_inactivate(BdrvChild *child) { BlockBackend *blk = child->opaque; @@ -315,6 +313,7 @@ static void blk_root_detach(BdrvChild *child) static AioContext *blk_root_get_parent_aio_context(BdrvChild *c) { BlockBackend *blk = c->opaque; + IO_CODE(); return blk_get_aio_context(blk); } @@ -337,8 +336,7 @@ static const BdrvChildClass child_root = { .attach = blk_root_attach, .detach = blk_root_detach, - .can_set_aio_ctx = blk_root_can_set_aio_ctx, - .set_aio_ctx = blk_root_set_aio_ctx, + .change_aio_ctx = blk_root_change_aio_ctx, .get_parent_aio_context = blk_root_get_parent_aio_context, }; @@ -372,6 +370,7 @@ BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm) block_acct_init(&blk->stats); + qemu_mutex_init(&blk->queued_requests_lock); qemu_co_queue_init(&blk->queued_requests); notifier_list_init(&blk->remove_bs_notifiers); notifier_list_init(&blk->insert_bs_notifiers); @@ -408,7 +407,9 @@ BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm, /* * Creates a new BlockBackend, opens a new BlockDriverState, and connects both. - * The new BlockBackend is in the main AioContext. + * By default, the new BlockBackend is in the main AioContext, but if the + * parameters connect it with any existing node in a different AioContext, it + * may end up there instead. * * Just as with bdrv_open(), after having called this function the reference to * @options belongs to the block layer (even on failure). @@ -453,16 +454,19 @@ BlockBackend *blk_new_open(const char *filename, const char *reference, shared = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED; } - blk = blk_new(qemu_get_aio_context(), perm, shared); bs = bdrv_open(filename, reference, options, flags, errp); if (!bs) { - blk_unref(blk); return NULL; } - blk->root = bdrv_root_attach_child(bs, "root", &child_root, - BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - perm, shared, blk, errp); + /* bdrv_open() could have moved bs to a different AioContext */ + blk = blk_new(bdrv_get_aio_context(bs), perm, shared); + blk->perm = perm; + blk->shared_perm = shared; + + blk_insert_bs(blk, bs, errp); + bdrv_unref(bs); + if (!blk->root) { blk_unref(blk); return NULL; @@ -489,6 +493,8 @@ static void blk_delete(BlockBackend *blk) assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); assert(QLIST_EMPTY(&blk->aio_notifiers)); + assert(qemu_co_queue_empty(&blk->queued_requests)); + qemu_mutex_destroy(&blk->queued_requests_lock); QTAILQ_REMOVE(&block_backends, blk, link); drive_info_del(blk->legacy_dinfo); block_acct_cleanup(&blk->stats); @@ -561,13 +567,9 @@ void blk_remove_all_bs(void) GLOBAL_STATE_CODE(); while ((blk = blk_all_next(blk)) != NULL) { - AioContext *ctx = blk_get_aio_context(blk); - - aio_context_acquire(ctx); if (blk->root) { blk_remove_bs(blk); } - aio_context_release(ctx); } } @@ -597,14 +599,14 @@ BlockDriverState *bdrv_next(BdrvNextIterator *it) /* Must be called from the main loop */ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + old_bs = it->bs; + /* First, return all root nodes of BlockBackends. In order to avoid * returning a BDS twice when multiple BBs refer to it, we only return it * if the BB is the first one in the parent list of the BDS. */ if (it->phase == BDRV_NEXT_BACKEND_ROOTS) { BlockBackend *old_blk = it->blk; - old_bs = old_blk ? blk_bs(old_blk) : NULL; - do { it->blk = blk_all_next(it->blk); bs = it->blk ? blk_bs(it->blk) : NULL; @@ -618,11 +620,10 @@ BlockDriverState *bdrv_next(BdrvNextIterator *it) if (bs) { bdrv_ref(bs); bdrv_unref(old_bs); + it->bs = bs; return bs; } it->phase = BDRV_NEXT_MONITOR_OWNED; - } else { - old_bs = it->bs; } /* Then return the monitor-owned BDSes without a BB attached. Ignore all @@ -662,13 +663,10 @@ void bdrv_next_cleanup(BdrvNextIterator *it) /* Must be called from the main loop */ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); - if (it->phase == BDRV_NEXT_BACKEND_ROOTS) { - if (it->blk) { - bdrv_unref(blk_bs(it->blk)); - blk_unref(it->blk); - } - } else { - bdrv_unref(it->bs); + bdrv_unref(it->bs); + + if (it->phase == BDRV_NEXT_BACKEND_ROOTS && it->blk) { + blk_unref(it->blk); } bdrv_next_reset(it); @@ -764,11 +762,12 @@ BlockDriverState *blk_bs(BlockBackend *blk) return blk->root ? blk->root->bs : NULL; } -static BlockBackend *bdrv_first_blk(BlockDriverState *bs) +static BlockBackend * GRAPH_RDLOCK bdrv_first_blk(BlockDriverState *bs) { BdrvChild *child; GLOBAL_STATE_CODE(); + assert_bdrv_graph_readable(); QLIST_FOREACH(child, &bs->parents, next_parent) { if (child->klass == &child_root) { @@ -796,6 +795,8 @@ bool bdrv_is_root_node(BlockDriverState *bs) BdrvChild *c; GLOBAL_STATE_CODE(); + assert_bdrv_graph_readable(); + QLIST_FOREACH(c, &bs->parents, next_parent) { if (c->klass != &child_root) { return false; @@ -896,7 +897,10 @@ void blk_remove_bs(BlockBackend *blk) blk_drain(blk); root = blk->root; blk->root = NULL; + + bdrv_graph_wrlock(); bdrv_root_unref_child(root); + bdrv_graph_wrunlock(); } /* @@ -905,12 +909,15 @@ void blk_remove_bs(BlockBackend *blk) int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) { ThrottleGroupMember *tgm = &blk->public.throttle_group_member; + GLOBAL_STATE_CODE(); bdrv_ref(bs); + bdrv_graph_wrlock(); blk->root = bdrv_root_attach_child(bs, "root", &child_root, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, blk->perm, blk->shared_perm, blk, errp); + bdrv_graph_wrunlock(); if (blk->root == NULL) { return -EPERM; } @@ -936,8 +943,9 @@ int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp) /* * Sets the permission bitmasks that the user of the BlockBackend needs. */ -int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, - Error **errp) +static int coroutine_mixed_fn GRAPH_RDLOCK +blk_set_perm_locked(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, + Error **errp) { int ret; GLOBAL_STATE_CODE(); @@ -955,6 +963,15 @@ int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, return 0; } +int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, + Error **errp) +{ + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + return blk_set_perm_locked(blk, perm, shared_perm, errp); +} + void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) { GLOBAL_STATE_CODE(); @@ -998,7 +1015,6 @@ void blk_detach_dev(BlockBackend *blk, DeviceState *dev) blk->dev = NULL; blk->dev_ops = NULL; blk->dev_opaque = NULL; - blk->guest_block_size = 512; blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort); blk_unref(blk); } @@ -1062,7 +1078,7 @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, blk->dev_opaque = opaque; /* Are we currently quiesced? Should we enforce this right now? */ - if (blk->quiesce_counter && ops->drained_begin) { + if (qatomic_read(&blk->quiesce_counter) && ops && ops->drained_begin) { ops->drained_begin(opaque); } } @@ -1237,11 +1253,11 @@ void blk_set_allow_aio_context_change(BlockBackend *blk, bool allow) void blk_set_disable_request_queuing(BlockBackend *blk, bool disable) { IO_CODE(); - blk->disable_request_queuing = disable; + qatomic_set(&blk->disable_request_queuing, disable); } -static int blk_check_byte_request(BlockBackend *blk, int64_t offset, - int64_t bytes) +static int coroutine_fn GRAPH_RDLOCK +blk_check_byte_request(BlockBackend *blk, int64_t offset, int64_t bytes) { int64_t len; @@ -1249,7 +1265,7 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset, return -EIO; } - if (!blk_is_available(blk)) { + if (!blk_co_is_available(blk)) { return -ENOMEDIUM; } @@ -1258,7 +1274,7 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset, } if (!blk->allow_write_beyond_eof) { - len = blk_getlength(blk); + len = bdrv_co_getlength(blk_bs(blk)); if (len < 0) { return len; } @@ -1271,28 +1287,45 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset, return 0; } +/* Are we currently in a drained section? */ +bool blk_in_drain(BlockBackend *blk) +{ + GLOBAL_STATE_CODE(); /* change to IO_OR_GS_CODE(), if necessary */ + return qatomic_read(&blk->quiesce_counter); +} + /* 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) { + if (qatomic_read(&blk->quiesce_counter) && + !qatomic_read(&blk->disable_request_queuing)) { + /* + * Take lock before decrementing in flight counter so main loop thread + * waits for us to enqueue ourselves before it can leave the drained + * section. + */ + qemu_mutex_lock(&blk->queued_requests_lock); blk_dec_in_flight(blk); - qemu_co_queue_wait(&blk->queued_requests, NULL); + qemu_co_queue_wait(&blk->queued_requests, &blk->queued_requests_lock); blk_inc_in_flight(blk); + qemu_mutex_unlock(&blk->queued_requests_lock); } } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -int coroutine_fn -blk_co_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags) +static int coroutine_fn +blk_co_do_preadv_part(BlockBackend *blk, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) { int ret; BlockDriverState *bs; IO_CODE(); blk_wait_while_drained(blk); + GRAPH_RDLOCK_GUARD(); /* Call blk_bs() only after waiting, the graph may have changed */ bs = blk_bs(blk); @@ -1308,14 +1341,26 @@ blk_co_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, /* throttling disk I/O */ if (blk->public.throttle_group_member.throttle_state) { throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member, - bytes, false); + bytes, THROTTLE_READ); } - ret = bdrv_co_preadv(blk->root, offset, bytes, qiov, flags); + ret = bdrv_co_preadv_part(blk->root, offset, bytes, qiov, qiov_offset, + flags); bdrv_dec_in_flight(bs); return ret; } +int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, int64_t bytes, + void *buf, BdrvRequestFlags flags) +{ + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + IO_OR_GS_CODE(); + + assert(bytes <= SIZE_MAX); + + return blk_co_preadv(blk, offset, bytes, &qiov, flags); +} + int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) @@ -1324,14 +1369,28 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, IO_OR_GS_CODE(); blk_inc_in_flight(blk); - ret = blk_co_do_preadv(blk, offset, bytes, qiov, flags); + ret = blk_co_do_preadv_part(blk, offset, bytes, qiov, 0, flags); + blk_dec_in_flight(blk); + + return ret; +} + +int coroutine_fn blk_co_preadv_part(BlockBackend *blk, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + size_t qiov_offset, BdrvRequestFlags flags) +{ + int ret; + IO_OR_GS_CODE(); + + blk_inc_in_flight(blk); + ret = blk_co_do_preadv_part(blk, offset, bytes, qiov, qiov_offset, flags); blk_dec_in_flight(blk); return ret; } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -int coroutine_fn +static int coroutine_fn blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) @@ -1341,6 +1400,7 @@ blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, IO_CODE(); blk_wait_while_drained(blk); + GRAPH_RDLOCK_GUARD(); /* Call blk_bs() only after waiting, the graph may have changed */ bs = blk_bs(blk); @@ -1355,7 +1415,7 @@ blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, /* throttling disk I/O */ if (blk->public.throttle_group_member.throttle_state) { throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member, - bytes, true); + bytes, THROTTLE_WRITE); } if (!blk->enable_write_cache) { @@ -1383,6 +1443,17 @@ int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, return ret; } +int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, int64_t bytes, + const void *buf, BdrvRequestFlags flags) +{ + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + IO_OR_GS_CODE(); + + assert(bytes <= SIZE_MAX); + + return blk_co_pwritev(blk, offset, bytes, &qiov, flags); +} + int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) @@ -1391,18 +1462,27 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, return blk_co_pwritev_part(blk, offset, bytes, qiov, 0, flags); } -static int coroutine_fn blk_pwritev_part(BlockBackend *blk, int64_t offset, - int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, - BdrvRequestFlags flags) +int coroutine_fn blk_co_block_status_above(BlockBackend *blk, + BlockDriverState *base, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, + BlockDriverState **file) { - int ret; + IO_CODE(); + GRAPH_RDLOCK_GUARD(); + return bdrv_co_block_status_above(blk_bs(blk), base, offset, bytes, pnum, + map, file); +} - 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_is_allocated_above(BlockBackend *blk, + BlockDriverState *base, + bool include_base, int64_t offset, + int64_t bytes, int64_t *pnum) +{ + IO_CODE(); + GRAPH_RDLOCK_GUARD(); + return bdrv_co_is_allocated_above(blk_bs(blk), base, include_base, offset, + bytes, pnum); } typedef struct BlkRwCo { @@ -1413,14 +1493,6 @@ typedef struct BlkRwCo { BdrvRequestFlags flags; } BlkRwCo; -int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int64_t bytes, BdrvRequestFlags flags) -{ - IO_OR_GS_CODE(); - return blk_pwritev_part(blk, offset, bytes, NULL, 0, - flags | BDRV_REQ_ZERO_WRITE); -} - int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags) { GLOBAL_STATE_CODE(); @@ -1461,7 +1533,7 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, acb->blk = blk; acb->ret = ret; - replay_bh_schedule_oneshot_event(blk_get_aio_context(blk), + replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), error_callback_bh, acb); return &acb->common; } @@ -1473,16 +1545,8 @@ typedef struct BlkAioEmAIOCB { bool has_returned; } BlkAioEmAIOCB; -static AioContext *blk_aio_em_aiocb_get_aio_context(BlockAIOCB *acb_) -{ - BlkAioEmAIOCB *acb = container_of(acb_, BlkAioEmAIOCB, common); - - return blk_get_aio_context(acb->rwco.blk); -} - static const AIOCBInfo blk_aio_em_aiocb_info = { .aiocb_size = sizeof(BlkAioEmAIOCB), - .get_aio_context = blk_aio_em_aiocb_get_aio_context, }; static void blk_aio_complete(BlkAioEmAIOCB *acb) @@ -1523,30 +1587,30 @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, acb->has_returned = false; co = qemu_coroutine_create(co_entry, acb); - bdrv_coroutine_enter(blk_bs(blk), co); + aio_co_enter(qemu_get_current_aio_context(), co); acb->has_returned = true; if (acb->rwco.ret != NOT_DONE) { - replay_bh_schedule_oneshot_event(blk_get_aio_context(blk), + replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), blk_aio_complete_bh, acb); } return &acb->common; } -static void blk_aio_read_entry(void *opaque) +static void coroutine_fn blk_aio_read_entry(void *opaque) { BlkAioEmAIOCB *acb = opaque; BlkRwCo *rwco = &acb->rwco; QEMUIOVector *qiov = rwco->iobuf; assert(qiov->size == acb->bytes); - rwco->ret = blk_co_do_preadv(rwco->blk, rwco->offset, acb->bytes, - qiov, rwco->flags); + rwco->ret = blk_co_do_preadv_part(rwco->blk, rwco->offset, acb->bytes, qiov, + 0, rwco->flags); blk_aio_complete(acb); } -static void blk_aio_write_entry(void *opaque) +static void coroutine_fn blk_aio_write_entry(void *opaque) { BlkAioEmAIOCB *acb = opaque; BlkRwCo *rwco = &acb->rwco; @@ -1567,59 +1631,65 @@ BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, flags | BDRV_REQ_ZERO_WRITE, cb, opaque); } -int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int bytes) -{ - int ret; - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - IO_OR_GS_CODE(); - - blk_inc_in_flight(blk); - ret = blk_do_preadv(blk, offset, bytes, &qiov, 0); - blk_dec_in_flight(blk); - - return ret < 0 ? ret : bytes; -} - -int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int bytes, - BdrvRequestFlags flags) -{ - int ret; - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - IO_OR_GS_CODE(); - - ret = blk_pwritev_part(blk, offset, bytes, &qiov, 0, flags); - - return ret < 0 ? ret : bytes; -} - -int64_t blk_getlength(BlockBackend *blk) +int64_t coroutine_fn blk_co_getlength(BlockBackend *blk) { IO_CODE(); - if (!blk_is_available(blk)) { + GRAPH_RDLOCK_GUARD(); + + if (!blk_co_is_available(blk)) { return -ENOMEDIUM; } - return bdrv_getlength(blk_bs(blk)); + return bdrv_co_getlength(blk_bs(blk)); } -void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr) +int64_t coroutine_fn blk_co_nb_sectors(BlockBackend *blk) { + BlockDriverState *bs = blk_bs(blk); + IO_CODE(); - if (!blk_bs(blk)) { - *nb_sectors_ptr = 0; + GRAPH_RDLOCK_GUARD(); + + if (!bs) { + return -ENOMEDIUM; } else { - bdrv_get_geometry(blk_bs(blk), nb_sectors_ptr); + return bdrv_co_nb_sectors(bs); } } -int64_t blk_nb_sectors(BlockBackend *blk) +/* + * This wrapper is written by hand because this function is in the hot I/O path, + * via blk_get_geometry. + */ +int64_t coroutine_mixed_fn blk_nb_sectors(BlockBackend *blk) { - IO_CODE(); - if (!blk_is_available(blk)) { - return -ENOMEDIUM; - } + BlockDriverState *bs = blk_bs(blk); - return bdrv_nb_sectors(blk_bs(blk)); + IO_CODE(); + + if (!bs) { + return -ENOMEDIUM; + } else { + return bdrv_nb_sectors(bs); + } +} + +/* return 0 as number of sectors if no device present or error */ +void coroutine_fn blk_co_get_geometry(BlockBackend *blk, + uint64_t *nb_sectors_ptr) +{ + int64_t ret = blk_co_nb_sectors(blk); + *nb_sectors_ptr = ret < 0 ? 0 : ret; +} + +/* + * This wrapper is written by hand because this function is in the hot I/O path. + */ +void coroutine_mixed_fn blk_get_geometry(BlockBackend *blk, + uint64_t *nb_sectors_ptr) +{ + int64_t ret = blk_nb_sectors(blk); + *nb_sectors_ptr = ret < 0 ? 0 : ret; } BlockAIOCB *blk_aio_preadv(BlockBackend *blk, int64_t offset, @@ -1655,33 +1725,35 @@ void blk_aio_cancel_async(BlockAIOCB *acb) } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -int coroutine_fn +static int coroutine_fn blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf) { IO_CODE(); blk_wait_while_drained(blk); + GRAPH_RDLOCK_GUARD(); - if (!blk_is_available(blk)) { + if (!blk_co_is_available(blk)) { return -ENOMEDIUM; } return bdrv_co_ioctl(blk_bs(blk), req, buf); } -int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) +int coroutine_fn blk_co_ioctl(BlockBackend *blk, unsigned long int req, + void *buf) { int ret; IO_OR_GS_CODE(); blk_inc_in_flight(blk); - ret = blk_do_ioctl(blk, req, buf); + ret = blk_co_do_ioctl(blk, req, buf); blk_dec_in_flight(blk); return ret; } -static void blk_aio_ioctl_entry(void *opaque) +static void coroutine_fn blk_aio_ioctl_entry(void *opaque) { BlkAioEmAIOCB *acb = opaque; BlkRwCo *rwco = &acb->rwco; @@ -1699,13 +1771,14 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, } /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -int coroutine_fn +static int coroutine_fn blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) { int ret; IO_CODE(); blk_wait_while_drained(blk); + GRAPH_RDLOCK_GUARD(); ret = blk_check_byte_request(blk, offset, bytes); if (ret < 0) { @@ -1715,7 +1788,7 @@ blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) return bdrv_co_pdiscard(blk->root, offset, bytes); } -static void blk_aio_pdiscard_entry(void *opaque) +static void coroutine_fn blk_aio_pdiscard_entry(void *opaque) { BlkAioEmAIOCB *acb = opaque; BlkRwCo *rwco = &acb->rwco; @@ -1746,32 +1819,21 @@ int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, return ret; } -int blk_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) -{ - int ret; - IO_OR_GS_CODE(); - - blk_inc_in_flight(blk); - ret = blk_do_pdiscard(blk, offset, bytes); - blk_dec_in_flight(blk); - - return ret; -} - /* To be called between exactly one pair of blk_inc/dec_in_flight() */ -int coroutine_fn blk_co_do_flush(BlockBackend *blk) +static int coroutine_fn blk_co_do_flush(BlockBackend *blk) { - blk_wait_while_drained(blk); IO_CODE(); + blk_wait_while_drained(blk); + GRAPH_RDLOCK_GUARD(); - if (!blk_is_available(blk)) { + if (!blk_co_is_available(blk)) { return -ENOMEDIUM; } return bdrv_co_flush(blk_bs(blk)); } -static void blk_aio_flush_entry(void *opaque) +static void coroutine_fn blk_aio_flush_entry(void *opaque) { BlkAioEmAIOCB *acb = opaque; BlkRwCo *rwco = &acb->rwco; @@ -1799,14 +1861,201 @@ int coroutine_fn blk_co_flush(BlockBackend *blk) return ret; } -int blk_flush(BlockBackend *blk) +static void coroutine_fn blk_aio_zone_report_entry(void *opaque) { - int ret; + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; + + rwco->ret = blk_co_zone_report(rwco->blk, rwco->offset, + (unsigned int*)(uintptr_t)acb->bytes, + rwco->iobuf); + blk_aio_complete(acb); +} + +BlockAIOCB *blk_aio_zone_report(BlockBackend *blk, int64_t offset, + unsigned int *nr_zones, + BlockZoneDescriptor *zones, + BlockCompletionFunc *cb, void *opaque) +{ + BlkAioEmAIOCB *acb; + Coroutine *co; + IO_CODE(); blk_inc_in_flight(blk); - ret = blk_do_flush(blk); - blk_dec_in_flight(blk); + acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); + acb->rwco = (BlkRwCo) { + .blk = blk, + .offset = offset, + .iobuf = zones, + .ret = NOT_DONE, + }; + acb->bytes = (int64_t)(uintptr_t)nr_zones, + acb->has_returned = false; + co = qemu_coroutine_create(blk_aio_zone_report_entry, acb); + aio_co_enter(qemu_get_current_aio_context(), co); + + acb->has_returned = true; + if (acb->rwco.ret != NOT_DONE) { + replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), + blk_aio_complete_bh, acb); + } + + return &acb->common; +} + +static void coroutine_fn blk_aio_zone_mgmt_entry(void *opaque) +{ + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; + + rwco->ret = blk_co_zone_mgmt(rwco->blk, + (BlockZoneOp)(uintptr_t)rwco->iobuf, + rwco->offset, acb->bytes); + blk_aio_complete(acb); +} + +BlockAIOCB *blk_aio_zone_mgmt(BlockBackend *blk, BlockZoneOp op, + int64_t offset, int64_t len, + BlockCompletionFunc *cb, void *opaque) { + BlkAioEmAIOCB *acb; + Coroutine *co; + IO_CODE(); + + blk_inc_in_flight(blk); + acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); + acb->rwco = (BlkRwCo) { + .blk = blk, + .offset = offset, + .iobuf = (void *)(uintptr_t)op, + .ret = NOT_DONE, + }; + acb->bytes = len; + acb->has_returned = false; + + co = qemu_coroutine_create(blk_aio_zone_mgmt_entry, acb); + aio_co_enter(qemu_get_current_aio_context(), co); + + acb->has_returned = true; + if (acb->rwco.ret != NOT_DONE) { + replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), + blk_aio_complete_bh, acb); + } + + return &acb->common; +} + +static void coroutine_fn blk_aio_zone_append_entry(void *opaque) +{ + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; + + rwco->ret = blk_co_zone_append(rwco->blk, (int64_t *)(uintptr_t)acb->bytes, + rwco->iobuf, rwco->flags); + blk_aio_complete(acb); +} + +BlockAIOCB *blk_aio_zone_append(BlockBackend *blk, int64_t *offset, + QEMUIOVector *qiov, BdrvRequestFlags flags, + BlockCompletionFunc *cb, void *opaque) { + BlkAioEmAIOCB *acb; + Coroutine *co; + IO_CODE(); + + blk_inc_in_flight(blk); + acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); + acb->rwco = (BlkRwCo) { + .blk = blk, + .ret = NOT_DONE, + .flags = flags, + .iobuf = qiov, + }; + acb->bytes = (int64_t)(uintptr_t)offset; + acb->has_returned = false; + + co = qemu_coroutine_create(blk_aio_zone_append_entry, acb); + aio_co_enter(qemu_get_current_aio_context(), co); + acb->has_returned = true; + if (acb->rwco.ret != NOT_DONE) { + replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), + blk_aio_complete_bh, acb); + } + + return &acb->common; +} + +/* + * Send a zone_report command. + * offset is a byte offset from the start of the device. No alignment + * required for offset. + * nr_zones represents IN maximum and OUT actual. + */ +int coroutine_fn blk_co_zone_report(BlockBackend *blk, int64_t offset, + unsigned int *nr_zones, + BlockZoneDescriptor *zones) +{ + int ret; + IO_CODE(); + + blk_inc_in_flight(blk); /* increase before waiting */ + blk_wait_while_drained(blk); + GRAPH_RDLOCK_GUARD(); + if (!blk_is_available(blk)) { + blk_dec_in_flight(blk); + return -ENOMEDIUM; + } + ret = bdrv_co_zone_report(blk_bs(blk), offset, nr_zones, zones); + blk_dec_in_flight(blk); + return ret; +} + +/* + * Send a zone_management command. + * op is the zone operation; + * offset is the byte offset from the start of the zoned device; + * len is the maximum number of bytes the command should operate on. It + * should be aligned with the device zone size. + */ +int coroutine_fn blk_co_zone_mgmt(BlockBackend *blk, BlockZoneOp op, + int64_t offset, int64_t len) +{ + int ret; + IO_CODE(); + + blk_inc_in_flight(blk); + blk_wait_while_drained(blk); + GRAPH_RDLOCK_GUARD(); + + ret = blk_check_byte_request(blk, offset, len); + if (ret < 0) { + blk_dec_in_flight(blk); + return ret; + } + + ret = bdrv_co_zone_mgmt(blk_bs(blk), op, offset, len); + blk_dec_in_flight(blk); + return ret; +} + +/* + * Send a zone_append command. + */ +int coroutine_fn blk_co_zone_append(BlockBackend *blk, int64_t *offset, + QEMUIOVector *qiov, BdrvRequestFlags flags) +{ + int ret; + IO_CODE(); + + blk_inc_in_flight(blk); + blk_wait_while_drained(blk); + GRAPH_RDLOCK_GUARD(); + if (!blk_is_available(blk)) { + blk_dec_in_flight(blk); + return -ENOMEDIUM; + } + + ret = bdrv_co_zone_append(blk_bs(blk), offset, qiov, flags); + blk_dec_in_flight(blk); return ret; } @@ -1822,7 +2071,7 @@ void blk_drain(BlockBackend *blk) /* We may have -ENOMEDIUM completions in flight */ AIO_WAIT_WHILE(blk_get_aio_context(blk), - qatomic_mb_read(&blk->in_flight) > 0); + qatomic_read(&blk->in_flight) > 0); if (bs) { bdrv_drained_end(bs); @@ -1839,14 +2088,8 @@ void blk_drain_all(void) bdrv_drain_all_begin(); while ((blk = blk_all_next(blk)) != NULL) { - AioContext *ctx = blk_get_aio_context(blk); - - aio_context_acquire(ctx); - /* We may have -ENOMEDIUM completions in flight */ - AIO_WAIT_WHILE(ctx, qatomic_mb_read(&blk->in_flight) > 0); - - aio_context_release(ctx); + AIO_WAIT_WHILE_UNLOCKED(NULL, qatomic_read(&blk->in_flight) > 0); } bdrv_drain_all_end(); @@ -1896,7 +2139,7 @@ static void send_qmp_error_event(BlockBackend *blk, BlockDriverState *bs = blk_bs(blk); optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; - qapi_event_send_block_io_error(blk_name(blk), !!bs, + qapi_event_send_block_io_error(blk_name(blk), bs ? bdrv_get_node_name(bs) : NULL, optype, action, blk_iostatus_is_enabled(blk), error == ENOSPC, strerror(error)); @@ -1981,7 +2224,7 @@ bool blk_enable_write_cache(BlockBackend *blk) void blk_set_enable_write_cache(BlockBackend *blk, bool wce) { - GLOBAL_STATE_CODE(); + IO_CODE(); blk->enable_write_cache = wce; } @@ -1995,41 +2238,53 @@ void blk_activate(BlockBackend *blk, Error **errp) return; } - bdrv_activate(bs, errp); -} - -bool blk_is_inserted(BlockBackend *blk) -{ - BlockDriverState *bs = blk_bs(blk); - IO_CODE(); - - return bs && bdrv_is_inserted(bs); -} - -bool blk_is_available(BlockBackend *blk) -{ - IO_CODE(); - return blk_is_inserted(blk) && !blk_dev_is_tray_open(blk); -} - -void blk_lock_medium(BlockBackend *blk, bool locked) -{ - BlockDriverState *bs = blk_bs(blk); - IO_CODE(); - - if (bs) { - bdrv_lock_medium(bs, locked); + /* + * Migration code can call this function in coroutine context, so leave + * coroutine context if necessary. + */ + if (qemu_in_coroutine()) { + bdrv_co_activate(bs, errp); + } else { + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bdrv_activate(bs, errp); } } -void blk_eject(BlockBackend *blk, bool eject_flag) +bool coroutine_fn blk_co_is_inserted(BlockBackend *blk) +{ + BlockDriverState *bs = blk_bs(blk); + IO_CODE(); + assert_bdrv_graph_readable(); + + return bs && bdrv_co_is_inserted(bs); +} + +bool coroutine_fn blk_co_is_available(BlockBackend *blk) +{ + IO_CODE(); + return blk_co_is_inserted(blk) && !blk_dev_is_tray_open(blk); +} + +void coroutine_fn blk_co_lock_medium(BlockBackend *blk, bool locked) +{ + BlockDriverState *bs = blk_bs(blk); + IO_CODE(); + GRAPH_RDLOCK_GUARD(); + + if (bs) { + bdrv_co_lock_medium(bs, locked); + } +} + +void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag) { BlockDriverState *bs = blk_bs(blk); char *id; IO_CODE(); + GRAPH_RDLOCK_GUARD(); if (bs) { - bdrv_eject(bs, eject_flag); + bdrv_co_eject(bs, eject_flag); } /* Whether or not we ejected on the backend, @@ -2100,12 +2355,6 @@ int blk_get_max_iov(BlockBackend *blk) return blk->root->bs->bl.max_iov; } -void blk_set_guest_block_size(BlockBackend *blk, int align) -{ - IO_CODE(); - blk->guest_block_size = align; -} - void *blk_try_blockalign(BlockBackend *blk, size_t size) { IO_CODE(); @@ -2122,6 +2371,7 @@ bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp) { BlockDriverState *bs = blk_bs(blk); GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!bs) { return false; @@ -2160,91 +2410,105 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason) } } +/** + * Return BB's current AioContext. Note that this context may change + * concurrently at any time, with one exception: If the BB has a root node + * attached, its context will only change through bdrv_try_change_aio_context(), + * which creates a drained section. Therefore, incrementing such a BB's + * in-flight counter will prevent its context from changing. + */ AioContext *blk_get_aio_context(BlockBackend *blk) { - BlockDriverState *bs = blk_bs(blk); IO_CODE(); - if (bs) { - AioContext *ctx = bdrv_get_aio_context(blk_bs(blk)); - assert(ctx == blk->ctx); + if (!blk) { + return qemu_get_aio_context(); } - return blk->ctx; -} - -static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) -{ - BlockBackendAIOCB *blk_acb = DO_UPCAST(BlockBackendAIOCB, common, acb); - return blk_get_aio_context(blk_acb->blk); -} - -static int blk_do_set_aio_context(BlockBackend *blk, AioContext *new_context, - bool update_root_node, Error **errp) -{ - BlockDriverState *bs = blk_bs(blk); - ThrottleGroupMember *tgm = &blk->public.throttle_group_member; - int ret; - - if (bs) { - bdrv_ref(bs); - - if (update_root_node) { - ret = bdrv_child_try_set_aio_context(bs, new_context, blk->root, - errp); - if (ret < 0) { - bdrv_unref(bs); - return ret; - } - } - if (tgm->throttle_state) { - bdrv_drained_begin(bs); - throttle_group_detach_aio_context(tgm); - throttle_group_attach_aio_context(tgm, new_context); - bdrv_drained_end(bs); - } - - bdrv_unref(bs); - } - - blk->ctx = new_context; - return 0; + return qatomic_read(&blk->ctx); } int blk_set_aio_context(BlockBackend *blk, AioContext *new_context, Error **errp) { + bool old_allow_change; + BlockDriverState *bs = blk_bs(blk); + int ret; + GLOBAL_STATE_CODE(); - return blk_do_set_aio_context(blk, new_context, true, errp); + + if (!bs) { + qatomic_set(&blk->ctx, new_context); + return 0; + } + + bdrv_ref(bs); + + old_allow_change = blk->allow_aio_context_change; + blk->allow_aio_context_change = true; + + ret = bdrv_try_change_aio_context(bs, new_context, NULL, errp); + + blk->allow_aio_context_change = old_allow_change; + + bdrv_unref(bs); + return ret; } -static bool blk_root_can_set_aio_ctx(BdrvChild *child, AioContext *ctx, - GSList **ignore, Error **errp) +typedef struct BdrvStateBlkRootContext { + AioContext *new_ctx; + BlockBackend *blk; +} BdrvStateBlkRootContext; + +static void blk_root_set_aio_ctx_commit(void *opaque) +{ + BdrvStateBlkRootContext *s = opaque; + BlockBackend *blk = s->blk; + AioContext *new_context = s->new_ctx; + ThrottleGroupMember *tgm = &blk->public.throttle_group_member; + + qatomic_set(&blk->ctx, new_context); + if (tgm->throttle_state) { + throttle_group_detach_aio_context(tgm); + throttle_group_attach_aio_context(tgm, new_context); + } +} + +static TransactionActionDrv set_blk_root_context = { + .commit = blk_root_set_aio_ctx_commit, + .clean = g_free, +}; + +static bool blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp) { BlockBackend *blk = child->opaque; + BdrvStateBlkRootContext *s; - if (blk->allow_aio_context_change) { - return true; + if (!blk->allow_aio_context_change) { + /* + * Manually created BlockBackends (those with a name) that are not + * attached to anything can change their AioContext without updating + * their user; return an error for others. + */ + if (!blk->name || blk->dev) { + /* TODO Add BB name/QOM path */ + error_setg(errp, "Cannot change iothread of active block backend"); + return false; + } } - /* Only manually created BlockBackends that are not attached to anything - * can change their AioContext without updating their user. */ - if (!blk->name || blk->dev) { - /* TODO Add BB name/QOM path */ - error_setg(errp, "Cannot change iothread of active block backend"); - return false; - } + s = g_new(BdrvStateBlkRootContext, 1); + *s = (BdrvStateBlkRootContext) { + .new_ctx = ctx, + .blk = blk, + }; + tran_add(tran, &set_blk_root_context, s); return true; } -static void blk_root_set_aio_ctx(BdrvChild *child, AioContext *ctx, - GSList **ignore) -{ - BlockBackend *blk = child->opaque; - blk_do_set_aio_context(blk, ctx, false, &error_abort); -} - void blk_add_aio_context_notifier(BlockBackend *blk, void (*attached_aio_context)(AioContext *new_context, void *opaque), void (*detach_aio_context)(void *opaque), void *opaque) @@ -2306,26 +2570,6 @@ void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify) notifier_list_add(&blk->insert_bs_notifiers, notify); } -void blk_io_plug(BlockBackend *blk) -{ - BlockDriverState *bs = blk_bs(blk); - IO_CODE(); - - if (bs) { - bdrv_io_plug(bs); - } -} - -void blk_io_unplug(BlockBackend *blk) -{ - BlockDriverState *bs = blk_bs(blk); - IO_CODE(); - - if (bs) { - bdrv_io_unplug(bs); - } -} - BlockAcctStats *blk_get_stats(BlockBackend *blk) { IO_CODE(); @@ -2347,25 +2591,27 @@ int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, flags | BDRV_REQ_ZERO_WRITE); } -int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf, - int64_t bytes) +int coroutine_fn blk_co_pwrite_compressed(BlockBackend *blk, int64_t offset, + int64_t bytes, const void *buf) { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); IO_OR_GS_CODE(); - return blk_pwritev_part(blk, offset, bytes, &qiov, 0, - BDRV_REQ_WRITE_COMPRESSED); + return blk_co_pwritev_part(blk, offset, bytes, &qiov, 0, + BDRV_REQ_WRITE_COMPRESSED); } -int blk_truncate(BlockBackend *blk, int64_t offset, bool exact, - PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) +int coroutine_fn blk_co_truncate(BlockBackend *blk, int64_t offset, bool exact, + PreallocMode prealloc, BdrvRequestFlags flags, + Error **errp) { IO_OR_GS_CODE(); - if (!blk_is_available(blk)) { + GRAPH_RDLOCK_GUARD(); + if (!blk_co_is_available(blk)) { error_setg(errp, "No medium inserted"); return -ENOMEDIUM; } - return bdrv_truncate(blk->root, offset, exact, prealloc, flags, errp); + return bdrv_co_truncate(blk->root, offset, exact, prealloc, flags, errp); } int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, @@ -2403,6 +2649,8 @@ int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) { GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!blk_is_available(blk)) { return -ENOMEDIUM; } @@ -2463,22 +2711,19 @@ int blk_commit_all(void) { BlockBackend *blk = NULL; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); while ((blk = blk_all_next(blk)) != NULL) { - AioContext *aio_context = blk_get_aio_context(blk); BlockDriverState *unfiltered_bs = bdrv_skip_filters(blk_bs(blk)); - aio_context_acquire(aio_context); if (blk_is_inserted(blk) && bdrv_cow_child(unfiltered_bs)) { int ret; ret = bdrv_commit(unfiltered_bs); if (ret < 0) { - aio_context_release(aio_context); return ret; } } - aio_context_release(aio_context); } return 0; } @@ -2541,7 +2786,7 @@ static void blk_root_drained_begin(BdrvChild *child) BlockBackend *blk = child->opaque; ThrottleGroupMember *tgm = &blk->public.throttle_group_member; - if (++blk->quiesce_counter == 1) { + if (qatomic_fetch_inc(&blk->quiesce_counter) == 0) { if (blk->dev_ops && blk->dev_ops->drained_begin) { blk->dev_ops->drained_begin(blk->dev_opaque); } @@ -2559,7 +2804,7 @@ static bool blk_root_drained_poll(BdrvChild *child) { BlockBackend *blk = child->opaque; bool busy = false; - assert(blk->quiesce_counter); + assert(qatomic_read(&blk->quiesce_counter)); if (blk->dev_ops && blk->dev_ops->drained_poll) { busy = blk->dev_ops->drained_poll(blk->dev_opaque); @@ -2567,34 +2812,48 @@ static bool blk_root_drained_poll(BdrvChild *child) return busy || !!blk->in_flight; } -static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter) +static void blk_root_drained_end(BdrvChild *child) { BlockBackend *blk = child->opaque; - assert(blk->quiesce_counter); + assert(qatomic_read(&blk->quiesce_counter)); assert(blk->public.throttle_group_member.io_limits_disabled); qatomic_dec(&blk->public.throttle_group_member.io_limits_disabled); - if (--blk->quiesce_counter == 0) { + if (qatomic_fetch_dec(&blk->quiesce_counter) == 1) { if (blk->dev_ops && blk->dev_ops->drained_end) { blk->dev_ops->drained_end(blk->dev_opaque); } - while (qemu_co_enter_next(&blk->queued_requests, NULL)) { + qemu_mutex_lock(&blk->queued_requests_lock); + while (qemu_co_enter_next(&blk->queued_requests, + &blk->queued_requests_lock)) { /* Resume all queued requests */ } + qemu_mutex_unlock(&blk->queued_requests_lock); } } -void blk_register_buf(BlockBackend *blk, void *host, size_t size) +bool blk_register_buf(BlockBackend *blk, void *host, size_t size, Error **errp) { + BlockDriverState *bs = blk_bs(blk); + GLOBAL_STATE_CODE(); - bdrv_register_buf(blk_bs(blk), host, size); + + if (bs) { + return bdrv_register_buf(bs, host, size, errp); + } + return true; } -void blk_unregister_buf(BlockBackend *blk, void *host) +void blk_unregister_buf(BlockBackend *blk, void *host, size_t size) { + BlockDriverState *bs = blk_bs(blk); + GLOBAL_STATE_CODE(); - bdrv_unregister_buf(blk_bs(blk), host); + + if (bs) { + bdrv_unregister_buf(bs, host, size); + } } int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, @@ -2604,6 +2863,7 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, { int r; IO_CODE(); + GRAPH_RDLOCK_GUARD(); r = blk_check_byte_request(blk_in, off_in, bytes); if (r) { @@ -2613,6 +2873,7 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, if (r) { return r; } + return bdrv_co_copy_range(blk_in->root, off_in, blk_out->root, off_out, bytes, read_flags, write_flags); @@ -2627,6 +2888,8 @@ const BdrvChild *blk_root(BlockBackend *blk) int blk_make_empty(BlockBackend *blk, Error **errp) { GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!blk_is_available(blk)) { error_setg(errp, "No medium inserted"); return -ENOMEDIUM; diff --git a/block/block-copy.c b/block/block-copy.c index ec46775ea5..9ee3dd7ef5 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -17,10 +17,14 @@ #include "trace.h" #include "qapi/error.h" #include "block/block-copy.h" +#include "block/block_int-io.h" +#include "block/dirty-bitmap.h" #include "block/reqlist.h" #include "sysemu/block-backend.h" #include "qemu/units.h" +#include "qemu/co-shared-resource.h" #include "qemu/coroutine.h" +#include "qemu/ratelimit.h" #include "block/aio_task.h" #include "qemu/error-report.h" #include "qemu/memalign.h" @@ -63,7 +67,7 @@ typedef struct BlockCopyCallState { QLIST_ENTRY(BlockCopyCallState) list; /* - * Fields that report information about return values and erros. + * Fields that report information about return values and errors. * Protected by lock in BlockCopyState. */ bool error_is_read; @@ -309,7 +313,12 @@ static int64_t block_copy_calculate_cluster_size(BlockDriverState *target, { int ret; BlockDriverInfo bdi; - bool target_does_cow = bdrv_backing_chain_next(target); + bool target_does_cow; + + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + target_does_cow = bdrv_backing_chain_next(target); /* * If there is no backing file on the target, we cannot rely on COW if our @@ -351,6 +360,8 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, BdrvDirtyBitmap *copy_bitmap; bool is_fleecing; + GLOBAL_STATE_CODE(); + cluster_size = block_copy_calculate_cluster_size(target->bs, errp); if (cluster_size < 0) { return NULL; @@ -388,7 +399,9 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, * For more information see commit f8d59dfb40bb and test * tests/qemu-iotests/222 */ + bdrv_graph_rdlock_main_loop(); is_fleecing = bdrv_chain_contains(target->bs, source->bs); + bdrv_graph_rdunlock_main_loop(); s = g_new(BlockCopyState, 1); *s = (BlockCopyState) { @@ -458,17 +471,16 @@ static coroutine_fn int block_copy_task_run(AioTaskPool *pool, * 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. + * No sync here: neither bitmap nor intersecting requests handling, only copy. * * @method is an in-out argument, so that copy_range can be either extended to * a full-size buffer or disabled if the copy_range attempt fails. The output * value of @method should be used for subsequent tasks. * Returns 0 on success. */ -static int coroutine_fn block_copy_do_copy(BlockCopyState *s, - int64_t offset, int64_t bytes, - BlockCopyMethod *method, - bool *error_is_read) +static int coroutine_fn GRAPH_RDLOCK +block_copy_do_copy(BlockCopyState *s, int64_t offset, int64_t bytes, + BlockCopyMethod *method, bool *error_is_read) { int ret; int64_t nbytes = MIN(offset + bytes, s->len) - offset; @@ -554,8 +566,10 @@ static coroutine_fn int block_copy_task_entry(AioTask *task) BlockCopyMethod method = t->method; int ret; - ret = block_copy_do_copy(s, t->req.offset, t->req.bytes, &method, - &error_is_read); + WITH_GRAPH_RDLOCK_GUARD() { + ret = block_copy_do_copy(s, t->req.offset, t->req.bytes, &method, + &error_is_read); + } WITH_QEMU_LOCK_GUARD(&s->lock) { if (s->method == t->method) { @@ -577,8 +591,9 @@ static coroutine_fn int block_copy_task_entry(AioTask *task) return ret; } -static int block_copy_block_status(BlockCopyState *s, int64_t offset, - int64_t bytes, int64_t *pnum) +static coroutine_fn GRAPH_RDLOCK +int block_copy_block_status(BlockCopyState *s, int64_t offset, int64_t bytes, + int64_t *pnum) { int64_t num; BlockDriverState *base; @@ -590,8 +605,8 @@ static int block_copy_block_status(BlockCopyState *s, int64_t offset, base = NULL; } - ret = bdrv_block_status_above(s->source->bs, base, offset, bytes, &num, - NULL, NULL); + ret = bdrv_co_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 @@ -613,8 +628,9 @@ static int block_copy_block_status(BlockCopyState *s, int64_t offset, * Check if the cluster starting at offset is allocated or not. * return via pnum the number of contiguous clusters sharing this allocation. */ -static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset, - int64_t *pnum) +static int coroutine_fn GRAPH_RDLOCK +block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset, + int64_t *pnum) { BlockDriverState *bs = s->source->bs; int64_t count, total_count = 0; @@ -624,7 +640,8 @@ static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset, assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); while (true) { - ret = bdrv_is_allocated(bs, offset, bytes, &count); + /* protected in backup_run() */ + ret = bdrv_co_is_allocated(bs, offset, bytes, &count); if (ret < 0) { return ret; } @@ -669,8 +686,9 @@ void block_copy_reset(BlockCopyState *s, int64_t offset, int64_t bytes) * @return 0 when the cluster at @offset was unallocated, * 1 otherwise, and -ret on error. */ -int64_t block_copy_reset_unallocated(BlockCopyState *s, - int64_t offset, int64_t *count) +int64_t coroutine_fn block_copy_reset_unallocated(BlockCopyState *s, + int64_t offset, + int64_t *count) { int ret; int64_t clusters, bytes; @@ -697,7 +715,7 @@ int64_t block_copy_reset_unallocated(BlockCopyState *s, * Returns 1 if dirty clusters found and successfully copied, 0 if no dirty * clusters found and -errno on failure. */ -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK block_copy_dirty_clusters(BlockCopyCallState *call_state) { BlockCopyState *s = call_state->s; @@ -820,7 +838,8 @@ void block_copy_kick(BlockCopyCallState *call_state) * it means that some I/O operation failed in context of _this_ block_copy call, * not some parallel operation. */ -static int coroutine_fn block_copy_common(BlockCopyCallState *call_state) +static int coroutine_fn GRAPH_RDLOCK +block_copy_common(BlockCopyCallState *call_state) { int ret; BlockCopyState *s = call_state->s; @@ -883,23 +902,43 @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state) return ret; } -int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes, - bool ignore_ratelimit) +static void coroutine_fn block_copy_async_co_entry(void *opaque) { - BlockCopyCallState call_state = { + GRAPH_RDLOCK_GUARD(); + block_copy_common(opaque); +} + +int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes, + bool ignore_ratelimit, uint64_t timeout_ns, + BlockCopyAsyncCallbackFunc cb, + void *cb_opaque) +{ + int ret; + BlockCopyCallState *call_state = g_new(BlockCopyCallState, 1); + + *call_state = (BlockCopyCallState) { .s = s, .offset = start, .bytes = bytes, .ignore_ratelimit = ignore_ratelimit, .max_workers = BLOCK_COPY_MAX_WORKERS, + .cb = cb, + .cb_opaque = cb_opaque, }; - return block_copy_common(&call_state); -} + ret = qemu_co_timeout(block_copy_async_co_entry, call_state, timeout_ns, + g_free); + if (ret < 0) { + assert(ret == -ETIMEDOUT); + block_copy_call_cancel(call_state); + /* call_state will be freed by running coroutine. */ + return ret; + } -static void coroutine_fn block_copy_async_co_entry(void *opaque) -{ - block_copy_common(opaque); + ret = call_state->ret; + g_free(call_state); + + return ret; } BlockCopyCallState *block_copy_async(BlockCopyState *s, diff --git a/block/block-gen.h b/block/block-gen.h index f80cf4897d..89b7daaa1f 100644 --- a/block/block-gen.h +++ b/block/block-gen.h @@ -30,20 +30,17 @@ /* Base structure for argument packing structures */ typedef struct BdrvPollCo { - BlockDriverState *bs; + AioContext *ctx; bool in_progress; - int ret; Coroutine *co; /* Keep pointer here for debugging */ } BdrvPollCo; -static inline int bdrv_poll_co(BdrvPollCo *s) +static inline void bdrv_poll_co(BdrvPollCo *s) { assert(!qemu_in_coroutine()); - bdrv_coroutine_enter(s->bs, s->co); - BDRV_POLL_WHILE(s->bs, s->in_progress); - - return s->ret; + aio_co_enter(s->ctx, s->co); + AIO_WAIT_WHILE(s->ctx, s->in_progress); } #endif /* BLOCK_BLOCK_GEN_H */ diff --git a/block/block-ram-registrar.c b/block/block-ram-registrar.c new file mode 100644 index 0000000000..25dbafa789 --- /dev/null +++ b/block/block-ram-registrar.c @@ -0,0 +1,58 @@ +/* + * BlockBackend RAM Registrar + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "sysemu/block-backend.h" +#include "sysemu/block-ram-registrar.h" +#include "qapi/error.h" + +static void ram_block_added(RAMBlockNotifier *n, void *host, size_t size, + size_t max_size) +{ + BlockRAMRegistrar *r = container_of(n, BlockRAMRegistrar, notifier); + Error *err = NULL; + + if (!r->ok) { + return; /* don't try again if we've already failed */ + } + + if (!blk_register_buf(r->blk, host, max_size, &err)) { + error_report_err(err); + ram_block_notifier_remove(&r->notifier); + r->ok = false; + } +} + +static void ram_block_removed(RAMBlockNotifier *n, void *host, size_t size, + size_t max_size) +{ + BlockRAMRegistrar *r = container_of(n, BlockRAMRegistrar, notifier); + blk_unregister_buf(r->blk, host, max_size); +} + +void blk_ram_registrar_init(BlockRAMRegistrar *r, BlockBackend *blk) +{ + r->blk = blk; + r->notifier = (RAMBlockNotifier){ + .ram_block_added = ram_block_added, + .ram_block_removed = ram_block_removed, + + /* + * .ram_block_resized() is not necessary because we use the max_size + * value that does not change across resize. + */ + }; + r->ok = true; + + ram_block_notifier_add(&r->notifier); +} + +void blk_ram_registrar_destroy(BlockRAMRegistrar *r) +{ + if (r->ok) { + ram_block_notifier_remove(&r->notifier); + } +} diff --git a/block/bochs.c b/block/bochs.c index 4d68658087..b099fb52fe 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" #include "qapi/error.h" +#include "block/block-io.h" #include "block/block_int.h" #include "qemu/module.h" #include "qemu/bswap.h" @@ -104,19 +105,24 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, struct bochs_header bochs; int ret; + GLOBAL_STATE_CODE(); + /* No write support yet */ + bdrv_graph_rdlock_main_loop(); ret = bdrv_apply_auto_read_only(bs, NULL, errp); + bdrv_graph_rdunlock_main_loop(); if (ret < 0) { return ret; } - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_IMAGE, false, errp); - if (!bs->file) { - return -EINVAL; + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; } - ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + ret = bdrv_pread(bs->file, 0, sizeof(bochs), &bochs, 0); if (ret < 0) { return ret; } @@ -150,8 +156,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, return -ENOMEM; } - ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap, - s->catalog_size * 4); + ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_size * 4, + s->catalog_bitmap, 0); if (ret < 0) { goto fail; } @@ -203,7 +209,8 @@ static void bochs_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */ } -static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) +static int64_t coroutine_fn GRAPH_RDLOCK +seek_to_sector(BlockDriverState *bs, int64_t sector_num) { BDRVBochsState *s = bs->opaque; uint64_t offset = sector_num * 512; @@ -224,8 +231,8 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) (s->extent_blocks + s->bitmap_blocks)); /* read in bitmap for current extent */ - ret = bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8), - &bitmap_entry, 1); + ret = bdrv_co_pread(bs->file, bitmap_offset + (extent_offset / 8), 1, + &bitmap_entry, 0); if (ret < 0) { return ret; } @@ -237,7 +244,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK bochs_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { diff --git a/block/cloop.c b/block/cloop.c index b8c6d0eccd..443af1444e 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/error-report.h" +#include "block/block-io.h" #include "block/block_int.h" #include "qemu/module.h" #include "qemu/bswap.h" @@ -66,19 +67,24 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, uint32_t offsets_size, max_compressed_block_size = 1, i; int ret; + GLOBAL_STATE_CODE(); + + bdrv_graph_rdlock_main_loop(); ret = bdrv_apply_auto_read_only(bs, NULL, errp); + bdrv_graph_rdunlock_main_loop(); if (ret < 0) { return ret; } - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_IMAGE, false, errp); - if (!bs->file) { - return -EINVAL; + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + /* read header */ - ret = bdrv_pread(bs->file, 128, &s->block_size, 4); + ret = bdrv_pread(bs->file, 128, 4, &s->block_size, 0); if (ret < 0) { return ret; } @@ -104,7 +110,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4); + ret = bdrv_pread(bs->file, 128 + 4, 4, &s->n_blocks, 0); if (ret < 0) { return ret; } @@ -135,7 +141,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return -ENOMEM; } - ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size); + ret = bdrv_pread(bs->file, 128 + 4 + 4, offsets_size, s->offsets, 0); if (ret < 0) { goto fail; } @@ -212,7 +218,8 @@ static void cloop_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */ } -static inline int cloop_read_block(BlockDriverState *bs, int block_num) +static int coroutine_fn GRAPH_RDLOCK +cloop_read_block(BlockDriverState *bs, int block_num) { BDRVCloopState *s = bs->opaque; @@ -220,9 +227,9 @@ static inline int cloop_read_block(BlockDriverState *bs, int block_num) int ret; uint32_t bytes = s->offsets[block_num + 1] - s->offsets[block_num]; - ret = bdrv_pread(bs->file, s->offsets[block_num], - s->compressed_block, bytes); - if (ret != bytes) { + ret = bdrv_co_pread(bs->file, s->offsets[block_num], bytes, + s->compressed_block, 0); + if (ret < 0) { return -1; } @@ -244,7 +251,7 @@ static inline int cloop_read_block(BlockDriverState *bs, int block_num) return 0; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK cloop_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { diff --git a/block/commit.c b/block/commit.c index 851d1c557a..7c3fdcb0ca 100644 --- a/block/commit.c +++ b/block/commit.c @@ -18,7 +18,6 @@ #include "block/block_int.h" #include "block/blockjob_int.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qemu/ratelimit.h" #include "qemu/memalign.h" #include "sysemu/block-backend.h" @@ -43,14 +42,17 @@ typedef struct CommitBlockJob { bool base_read_only; bool chain_frozen; char *backing_file_str; + bool backing_mask_protocol; } CommitBlockJob; static int commit_prepare(Job *job) { CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); + bdrv_graph_rdlock_main_loop(); bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs); s->chain_frozen = false; + bdrv_graph_rdunlock_main_loop(); /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before * the normal backing chain can be restored. */ @@ -60,16 +62,20 @@ static int commit_prepare(Job *job) /* FIXME: bdrv_drop_intermediate treats total failures and partial failures * identically. Further work is needed to disambiguate these cases. */ return bdrv_drop_intermediate(s->commit_top_bs, s->base_bs, - s->backing_file_str); + s->backing_file_str, + s->backing_mask_protocol); } static void commit_abort(Job *job) { CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); BlockDriverState *top_bs = blk_bs(s->top); + BlockDriverState *commit_top_backing_bs; if (s->chain_frozen) { + bdrv_graph_rdlock_main_loop(); bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs); + bdrv_graph_rdunlock_main_loop(); } /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ @@ -91,8 +97,15 @@ static void commit_abort(Job *job) * XXX Can (or should) we somehow keep 'consistent read' blocked even * after the failed/cancelled commit job is gone? If we already wrote * something to base, the intermediate images aren't valid any more. */ - bdrv_replace_node(s->commit_top_bs, s->commit_top_bs->backing->bs, - &error_abort); + bdrv_graph_rdlock_main_loop(); + commit_top_backing_bs = s->commit_top_bs->backing->bs; + bdrv_graph_rdunlock_main_loop(); + + bdrv_drained_begin(commit_top_backing_bs); + bdrv_graph_wrlock(); + bdrv_replace_node(s->commit_top_bs, commit_top_backing_bs, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drained_end(commit_top_backing_bs); bdrv_unref(s->commit_top_bs); bdrv_unref(top_bs); @@ -117,25 +130,24 @@ static int coroutine_fn commit_run(Job *job, Error **errp) { CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); int64_t offset; - uint64_t delay_ns = 0; int ret = 0; int64_t n = 0; /* bytes */ QEMU_AUTO_VFREE void *buf = NULL; int64_t len, base_len; - len = blk_getlength(s->top); + len = blk_co_getlength(s->top); if (len < 0) { return len; } job_progress_set_remaining(&s->common.job, len); - base_len = blk_getlength(s->base); + base_len = blk_co_getlength(s->base); if (base_len < 0) { return base_len; } if (base_len < len) { - ret = blk_truncate(s->base, len, false, PREALLOC_MODE_OFF, 0, NULL); + ret = blk_co_truncate(s->base, len, false, PREALLOC_MODE_OFF, 0, NULL); if (ret) { return ret; } @@ -150,13 +162,13 @@ static int coroutine_fn commit_run(Job *job, Error **errp) /* 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. */ - job_sleep_ns(&s->common.job, delay_ns); + block_job_ratelimit_sleep(&s->common); if (job_is_cancelled(&s->common.job)) { break; } /* Copy if allocated above the base */ - ret = bdrv_is_allocated_above(blk_bs(s->top), s->base_overlay, true, - offset, COMMIT_BUFFER_SIZE, &n); + ret = blk_co_is_allocated_above(s->top, s->base_overlay, true, + offset, COMMIT_BUFFER_SIZE, &n); copy = (ret > 0); trace_commit_one_iteration(s, offset, n, ret); if (copy) { @@ -185,9 +197,7 @@ static int coroutine_fn commit_run(Job *job, Error **errp) job_progress_update(&s->common.job, n); if (copy) { - delay_ns = block_job_ratelimit_get_delay(&s->common, n); - } else { - delay_ns = 0; + block_job_ratelimit_processed_bytes(&s->common, n); } } @@ -207,13 +217,14 @@ static const BlockJobDriver commit_job_driver = { }, }; -static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +bdrv_commit_top_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); } -static void bdrv_commit_top_refresh_filename(BlockDriverState *bs) +static GRAPH_RDLOCK void bdrv_commit_top_refresh_filename(BlockDriverState *bs) { pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->backing->bs->filename); @@ -238,12 +249,14 @@ static BlockDriver bdrv_commit_top = { .bdrv_child_perm = bdrv_commit_top_child_perm, .is_filter = true, + .filtered_child_is_backing = true, }; void commit_start(const char *job_id, BlockDriverState *bs, BlockDriverState *base, BlockDriverState *top, int creation_flags, int64_t speed, BlockdevOnError on_error, const char *backing_file_str, + bool backing_mask_protocol, const char *filter_node_name, Error **errp) { CommitBlockJob *s; @@ -257,10 +270,13 @@ void commit_start(const char *job_id, BlockDriverState *bs, GLOBAL_STATE_CODE(); assert(top != bs); + bdrv_graph_rdlock_main_loop(); if (bdrv_skip_filters(top) == bdrv_skip_filters(base)) { error_setg(errp, "Invalid files for merge: top and base are the same"); + bdrv_graph_rdunlock_main_loop(); return; } + bdrv_graph_rdunlock_main_loop(); base_size = bdrv_getlength(base); if (base_size < 0) { @@ -326,6 +342,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, * this is the responsibility of the interface (i.e. whoever calls * commit_start()). */ + bdrv_graph_wrlock(); s->base_overlay = bdrv_find_overlay(top, base); assert(s->base_overlay); @@ -356,16 +373,20 @@ void commit_start(const char *job_id, BlockDriverState *bs, ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, iter_shared_perms, errp); if (ret < 0) { + bdrv_graph_wrunlock(); goto fail; } } if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) { + bdrv_graph_wrunlock(); goto fail; } s->chain_frozen = true; ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp); + bdrv_graph_wrunlock(); + if (ret < 0) { goto fail; } @@ -390,6 +411,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, blk_set_disable_request_queuing(s->top, true); s->backing_file_str = g_strdup(backing_file_str); + s->backing_mask_protocol = backing_mask_protocol; s->on_error = on_error; trace_commit_start(bs, base, top, s); @@ -398,7 +420,9 @@ void commit_start(const char *job_id, BlockDriverState *bs, fail: if (s->chain_frozen) { + bdrv_graph_rdlock_main_loop(); bdrv_unfreeze_backing_chain(commit_top_bs, base); + bdrv_graph_rdunlock_main_loop(); } if (s->base) { blk_unref(s->base); @@ -413,7 +437,11 @@ fail: /* commit_top_bs has to be replaced after deleting the block job, * otherwise this would fail because of lack of permissions. */ if (commit_top_bs) { + bdrv_drained_begin(top); + bdrv_graph_wrlock(); bdrv_replace_node(commit_top_bs, top, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drained_end(top); } } @@ -436,6 +464,7 @@ int bdrv_commit(BlockDriverState *bs) Error *local_err = NULL; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!drv) return -ENOMEDIUM; @@ -527,12 +556,12 @@ int bdrv_commit(BlockDriverState *bs) goto ro_cleanup; } if (ret) { - ret = blk_pread(src, offset, buf, n); + ret = blk_pread(src, offset, n, buf, 0); if (ret < 0) { goto ro_cleanup; } - ret = blk_pwrite(backing, offset, buf, n, 0); + ret = blk_pwrite(backing, offset, n, buf, 0); if (ret < 0) { goto ro_cleanup; } diff --git a/block/copy-before-write.c b/block/copy-before-write.c index a8a06fdc09..8aba27a71d 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qapi/qmp/qjson.h" #include "sysemu/block-backend.h" #include "qemu/cutils.h" @@ -31,6 +32,7 @@ #include "block/block_int.h" #include "block/qdict.h" #include "block/block-copy.h" +#include "block/dirty-bitmap.h" #include "block/copy-before-write.h" #include "block/reqlist.h" @@ -40,6 +42,8 @@ typedef struct BDRVCopyBeforeWriteState { BlockCopyState *bcs; BdrvChild *target; + OnCbwError on_cbw_error; + uint32_t cbw_timeout_ns; /* * @lock: protects access to @access_bitmap, @done_bitmap and @@ -64,15 +68,30 @@ typedef struct BDRVCopyBeforeWriteState { * node. These areas must not be rewritten by guest. */ BlockReqList frozen_read_reqs; + + /* + * @snapshot_error is normally zero. But on first copy-before-write failure + * when @on_cbw_error == ON_CBW_ERROR_BREAK_SNAPSHOT, @snapshot_error takes + * value of this error (<0). After that all in-flight and further + * snapshot-API requests will fail with that error. + */ + int snapshot_error; } BDRVCopyBeforeWriteState; -static coroutine_fn int cbw_co_preadv( - BlockDriverState *bs, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +cbw_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); } +static void block_copy_cb(void *opaque) +{ + BlockDriverState *bs = opaque; + + bdrv_dec_in_flight(bs); +} + /* * Do copy-before-write operation. * @@ -94,24 +113,44 @@ static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs, return 0; } + if (s->snapshot_error) { + return 0; + } + off = QEMU_ALIGN_DOWN(offset, cluster_size); end = QEMU_ALIGN_UP(offset + bytes, cluster_size); - ret = block_copy(s->bcs, off, end - off, true); - if (ret < 0) { + /* + * Increase in_flight, so that in case of timed-out block-copy, the + * remaining background block_copy() request (which can't be immediately + * cancelled by timeout) is presented in bs->in_flight. This way we are + * sure that on bs close() we'll previously wait for all timed-out but yet + * running block_copy calls. + */ + bdrv_inc_in_flight(bs); + ret = block_copy(s->bcs, off, end - off, true, s->cbw_timeout_ns, + block_copy_cb, bs); + if (ret < 0 && s->on_cbw_error == ON_CBW_ERROR_BREAK_GUEST_WRITE) { return ret; } WITH_QEMU_LOCK_GUARD(&s->lock) { - bdrv_set_dirty_bitmap(s->done_bitmap, off, end - off); + if (ret < 0) { + assert(s->on_cbw_error == ON_CBW_ERROR_BREAK_SNAPSHOT); + if (!s->snapshot_error) { + s->snapshot_error = ret; + } + } else { + bdrv_set_dirty_bitmap(s->done_bitmap, off, end - off); + } reqlist_wait_all(&s->frozen_read_reqs, off, end - off, &s->lock); } return 0; } -static int coroutine_fn cbw_co_pdiscard(BlockDriverState *bs, - int64_t offset, int64_t bytes) +static int coroutine_fn GRAPH_RDLOCK +cbw_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { int ret = cbw_do_copy_before_write(bs, offset, bytes, 0); if (ret < 0) { @@ -121,8 +160,9 @@ static int coroutine_fn cbw_co_pdiscard(BlockDriverState *bs, return bdrv_co_pdiscard(bs->file, offset, bytes); } -static int coroutine_fn cbw_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int64_t bytes, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +cbw_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + BdrvRequestFlags flags) { int ret = cbw_do_copy_before_write(bs, offset, bytes, flags); if (ret < 0) { @@ -132,11 +172,9 @@ static int coroutine_fn cbw_co_pwrite_zeroes(BlockDriverState *bs, return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); } -static coroutine_fn int cbw_co_pwritev(BlockDriverState *bs, - int64_t offset, - int64_t bytes, - QEMUIOVector *qiov, - BdrvRequestFlags flags) +static coroutine_fn GRAPH_RDLOCK +int cbw_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { int ret = cbw_do_copy_before_write(bs, offset, bytes, flags); if (ret < 0) { @@ -146,7 +184,7 @@ static coroutine_fn int cbw_co_pwritev(BlockDriverState *bs, return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); } -static int coroutine_fn cbw_co_flush(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK cbw_co_flush(BlockDriverState *bs) { if (!bs->file) { return 0; @@ -165,9 +203,9 @@ static int coroutine_fn cbw_co_flush(BlockDriverState *bs) * It's guaranteed that guest writes will not interact in the region until * cbw_snapshot_read_unlock() called. */ -static BlockReq *cbw_snapshot_read_lock(BlockDriverState *bs, - int64_t offset, int64_t bytes, - int64_t *pnum, BdrvChild **file) +static BlockReq * coroutine_fn GRAPH_RDLOCK +cbw_snapshot_read_lock(BlockDriverState *bs, int64_t offset, int64_t bytes, + int64_t *pnum, BdrvChild **file) { BDRVCopyBeforeWriteState *s = bs->opaque; BlockReq *req = g_new(BlockReq, 1); @@ -175,6 +213,11 @@ static BlockReq *cbw_snapshot_read_lock(BlockDriverState *bs, QEMU_LOCK_GUARD(&s->lock); + if (s->snapshot_error) { + g_free(req); + return NULL; + } + if (bdrv_dirty_bitmap_next_zero(s->access_bitmap, offset, bytes) != -1) { g_free(req); return NULL; @@ -197,7 +240,8 @@ static BlockReq *cbw_snapshot_read_lock(BlockDriverState *bs, return req; } -static void cbw_snapshot_read_unlock(BlockDriverState *bs, BlockReq *req) +static coroutine_fn void +cbw_snapshot_read_unlock(BlockDriverState *bs, BlockReq *req) { BDRVCopyBeforeWriteState *s = bs->opaque; @@ -212,7 +256,7 @@ static void cbw_snapshot_read_unlock(BlockDriverState *bs, BlockReq *req) g_free(req); } -static coroutine_fn int +static int coroutine_fn GRAPH_RDLOCK cbw_co_preadv_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset) { @@ -244,7 +288,7 @@ cbw_co_preadv_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes, return 0; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK cbw_co_snapshot_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, @@ -261,7 +305,7 @@ cbw_co_snapshot_block_status(BlockDriverState *bs, return -EACCES; } - ret = bdrv_block_status(child->bs, offset, cur_bytes, pnum, map, file); + ret = bdrv_co_block_status(child->bs, offset, cur_bytes, pnum, map, file); if (child == s->target) { /* * We refer to s->target only for areas that we've written to it. @@ -277,8 +321,8 @@ cbw_co_snapshot_block_status(BlockDriverState *bs, return ret; } -static int coroutine_fn cbw_co_pdiscard_snapshot(BlockDriverState *bs, - int64_t offset, int64_t bytes) +static int coroutine_fn GRAPH_RDLOCK +cbw_co_pdiscard_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes) { BDRVCopyBeforeWriteState *s = bs->opaque; @@ -291,17 +335,17 @@ static int coroutine_fn cbw_co_pdiscard_snapshot(BlockDriverState *bs, return bdrv_co_pdiscard(s->target, offset, bytes); } -static void cbw_refresh_filename(BlockDriverState *bs) +static void GRAPH_RDLOCK cbw_refresh_filename(BlockDriverState *bs) { pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->file->bs->filename); } -static void cbw_child_perm(BlockDriverState *bs, BdrvChild *c, - BdrvChildRole role, - BlockReopenQueue *reopen_queue, - uint64_t perm, uint64_t shared, - uint64_t *nperm, uint64_t *nshared) +static void GRAPH_RDLOCK +cbw_child_perm(BlockDriverState *bs, BdrvChild *c, BdrvChildRole role, + BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared) { if (!(role & BDRV_CHILD_FILTERED)) { /* @@ -328,61 +372,60 @@ static void cbw_child_perm(BlockDriverState *bs, BdrvChild *c, } } -static bool cbw_parse_bitmap_option(QDict *options, BdrvDirtyBitmap **bitmap, - Error **errp) +static BlockdevOptions *cbw_parse_options(QDict *options, Error **errp) { - QDict *bitmap_qdict = NULL; - BlockDirtyBitmap *bmp_param = NULL; + BlockdevOptions *opts = NULL; Visitor *v = NULL; - bool ret = false; - *bitmap = NULL; + qdict_put_str(options, "driver", "copy-before-write"); - qdict_extract_subqdict(options, &bitmap_qdict, "bitmap."); - if (!qdict_size(bitmap_qdict)) { - ret = true; - goto out; - } - - v = qobject_input_visitor_new_flat_confused(bitmap_qdict, errp); + v = qobject_input_visitor_new_flat_confused(options, errp); if (!v) { goto out; } - visit_type_BlockDirtyBitmap(v, NULL, &bmp_param, errp); - if (!bmp_param) { + visit_type_BlockdevOptions(v, NULL, &opts, errp); + if (!opts) { goto out; } - *bitmap = block_dirty_bitmap_lookup(bmp_param->node, bmp_param->name, NULL, - errp); - if (!*bitmap) { - goto out; - } - - ret = true; + /* + * Delete options which we are going to parse through BlockdevOptions + * object for original options. + */ + qdict_extract_subqdict(options, NULL, "bitmap"); + qdict_del(options, "on-cbw-error"); + qdict_del(options, "cbw-timeout"); out: - qapi_free_BlockDirtyBitmap(bmp_param); visit_free(v); - qobject_unref(bitmap_qdict); + qdict_del(options, "driver"); - return ret; + return opts; } static int cbw_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { + ERRP_GUARD(); BDRVCopyBeforeWriteState *s = bs->opaque; BdrvDirtyBitmap *bitmap = NULL; int64_t cluster_size; + g_autoptr(BlockdevOptions) full_opts = NULL; + BlockdevOptionsCbw *opts; + int ret; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, errp); - if (!bs->file) { + full_opts = cbw_parse_options(options, errp); + if (!full_opts) { return -EINVAL; } + assert(full_opts->driver == BLOCKDEV_DRIVER_COPY_BEFORE_WRITE); + opts = &full_opts->u.copy_before_write; + + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; + } s->target = bdrv_open_child(NULL, options, "target", bs, &child_of_bds, BDRV_CHILD_DATA, false, errp); @@ -390,9 +433,19 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - if (!cbw_parse_bitmap_option(options, &bitmap, errp)) { - return -EINVAL; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + if (opts->bitmap) { + bitmap = block_dirty_bitmap_lookup(opts->bitmap->node, + opts->bitmap->name, NULL, errp); + if (!bitmap) { + return -EINVAL; + } } + s->on_cbw_error = opts->has_on_cbw_error ? opts->on_cbw_error : + ON_CBW_ERROR_BREAK_GUEST_WRITE; + s->cbw_timeout_ns = opts->has_cbw_timeout ? + opts->cbw_timeout * NANOSECONDS_PER_SECOND : 0; bs->total_sectors = bs->file->bs->total_sectors; bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | @@ -427,7 +480,6 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, qemu_co_mutex_init(&s->lock); QLIST_INIT(&s->frozen_read_reqs); - return 0; } @@ -442,7 +494,7 @@ static void cbw_close(BlockDriverState *bs) s->bcs = NULL; } -BlockDriver bdrv_cbw_filter = { +static BlockDriver bdrv_cbw_filter = { .format_name = "copy-before-write", .instance_size = sizeof(BDRVCopyBeforeWriteState), @@ -472,7 +524,6 @@ BlockDriverState *bdrv_cbw_append(BlockDriverState *source, BlockCopyState **bcs, Error **errp) { - ERRP_GUARD(); BDRVCopyBeforeWriteState *state; BlockDriverState *top; QDict *opts; diff --git a/block/copy-on-read.c b/block/copy-on-read.c index 1fc7fb3333..c36f253d16 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -21,6 +21,7 @@ */ #include "qemu/osdep.h" +#include "block/block-io.h" #include "block/block_int.h" #include "qemu/module.h" #include "qapi/error.h" @@ -34,21 +35,24 @@ typedef struct BDRVStateCOR { } BDRVStateCOR; -static int cor_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +static int GRAPH_UNLOCKED +cor_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BlockDriverState *bottom_bs = NULL; BDRVStateCOR *state = bs->opaque; /* Find a bottom node name, if any */ const char *bottom_node = qdict_get_try_str(options, "bottom"); + int ret; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, errp); - if (!bs->file) { - return -EINVAL; + GLOBAL_STATE_CODE(); + + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bs->supported_read_flags = BDRV_REQ_PREFETCH; bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | @@ -121,17 +125,16 @@ static void cor_child_perm(BlockDriverState *bs, BdrvChild *c, } -static int64_t cor_getlength(BlockDriverState *bs) +static int64_t coroutine_fn GRAPH_RDLOCK cor_co_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + return bdrv_co_getlength(bs->file->bs); } -static int coroutine_fn cor_co_preadv_part(BlockDriverState *bs, - int64_t offset, int64_t bytes, - QEMUIOVector *qiov, - size_t qiov_offset, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +cor_co_preadv_part(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) { int64_t n; int local_flags; @@ -147,11 +150,11 @@ static int coroutine_fn cor_co_preadv_part(BlockDriverState *bs, local_flags = flags; /* In case of failure, try to copy-on-read anyway */ - ret = bdrv_is_allocated(bs->file->bs, offset, bytes, &n); + ret = bdrv_co_is_allocated(bs->file->bs, offset, bytes, &n); if (ret <= 0) { - ret = bdrv_is_allocated_above(bdrv_backing_chain_next(bs->file->bs), - state->bottom_bs, true, offset, - n, &n); + ret = bdrv_co_is_allocated_above(bdrv_backing_chain_next(bs->file->bs), + state->bottom_bs, true, offset, + n, &n); if (ret > 0 || ret < 0) { local_flags |= BDRV_REQ_COPY_ON_READ; } @@ -180,62 +183,65 @@ static int coroutine_fn cor_co_preadv_part(BlockDriverState *bs, } -static int coroutine_fn cor_co_pwritev_part(BlockDriverState *bs, - int64_t offset, - int64_t bytes, - QEMUIOVector *qiov, - size_t qiov_offset, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +cor_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) { return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset, flags); } -static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int64_t bytes, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +cor_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + BdrvRequestFlags flags) { return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); } -static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs, - int64_t offset, int64_t bytes) +static int coroutine_fn GRAPH_RDLOCK +cor_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { return bdrv_co_pdiscard(bs->file, offset, bytes); } -static int coroutine_fn cor_co_pwritev_compressed(BlockDriverState *bs, - int64_t offset, - int64_t bytes, - QEMUIOVector *qiov) +static int coroutine_fn GRAPH_RDLOCK +cor_co_pwritev_compressed(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov) { return bdrv_co_pwritev(bs->file, offset, bytes, qiov, BDRV_REQ_WRITE_COMPRESSED); } -static void cor_eject(BlockDriverState *bs, bool eject_flag) +static void coroutine_fn GRAPH_RDLOCK +cor_co_eject(BlockDriverState *bs, bool eject_flag) { - bdrv_eject(bs->file->bs, eject_flag); + bdrv_co_eject(bs->file->bs, eject_flag); } -static void cor_lock_medium(BlockDriverState *bs, bool locked) +static void coroutine_fn GRAPH_RDLOCK +cor_co_lock_medium(BlockDriverState *bs, bool locked) { - bdrv_lock_medium(bs->file->bs, locked); + bdrv_co_lock_medium(bs->file->bs, locked); } -static void cor_close(BlockDriverState *bs) +static void GRAPH_UNLOCKED cor_close(BlockDriverState *bs) { BDRVStateCOR *s = bs->opaque; + GLOBAL_STATE_CODE(); + if (s->chain_frozen) { + bdrv_graph_rdlock_main_loop(); s->chain_frozen = false; bdrv_unfreeze_backing_chain(bs, s->bottom_bs); + bdrv_graph_rdunlock_main_loop(); } bdrv_unref(s->bottom_bs); @@ -250,7 +256,7 @@ static BlockDriver bdrv_copy_on_read = { .bdrv_close = cor_close, .bdrv_child_perm = cor_child_perm, - .bdrv_getlength = cor_getlength, + .bdrv_co_getlength = cor_co_getlength, .bdrv_co_preadv_part = cor_co_preadv_part, .bdrv_co_pwritev_part = cor_co_pwritev_part, @@ -258,20 +264,22 @@ static BlockDriver bdrv_copy_on_read = { .bdrv_co_pdiscard = cor_co_pdiscard, .bdrv_co_pwritev_compressed = cor_co_pwritev_compressed, - .bdrv_eject = cor_eject, - .bdrv_lock_medium = cor_lock_medium, + .bdrv_co_eject = cor_co_eject, + .bdrv_co_lock_medium = cor_co_lock_medium, - .has_variable_length = true, .is_filter = true, }; -void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs) +void no_coroutine_fn bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs) { BDRVStateCOR *s = cor_filter_bs->opaque; + GLOBAL_STATE_CODE(); + /* unfreeze, as otherwise bdrv_replace_node() will fail */ if (s->chain_frozen) { + GRAPH_RDLOCK_GUARD_MAINLOOP(); s->chain_frozen = false; bdrv_unfreeze_backing_chain(cor_filter_bs, s->bottom_bs); } diff --git a/block/copy-on-read.h b/block/copy-on-read.h index 1d8ad38c74..72f9b378ea 100644 --- a/block/copy-on-read.h +++ b/block/copy-on-read.h @@ -27,6 +27,7 @@ #include "block/block_int.h" -void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs); +void no_coroutine_fn GRAPH_UNLOCKED +bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs); #endif /* BLOCK_COPY_ON_READ_H */ diff --git a/block/coroutines.h b/block/coroutines.h index 830ecaa733..f3226682d6 100644 --- a/block/coroutines.h +++ b/block/coroutines.h @@ -37,11 +37,13 @@ * the I/O API. */ -int coroutine_fn bdrv_co_check(BlockDriverState *bs, - BdrvCheckResult *res, BdrvCheckMode fix); -int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp); +int coroutine_fn GRAPH_RDLOCK +bdrv_co_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); -int coroutine_fn +int coroutine_fn GRAPH_RDLOCK +bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp); + +int coroutine_fn GRAPH_RDLOCK bdrv_co_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, bool include_base, @@ -53,35 +55,17 @@ bdrv_co_common_block_status_above(BlockDriverState *bs, BlockDriverState **file, int *depth); -int coroutine_fn bdrv_co_readv_vmstate(BlockDriverState *bs, - QEMUIOVector *qiov, int64_t pos); -int coroutine_fn bdrv_co_writev_vmstate(BlockDriverState *bs, - QEMUIOVector *qiov, int64_t pos); +int coroutine_fn GRAPH_RDLOCK +bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); -int coroutine_fn +int coroutine_fn GRAPH_RDLOCK +bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); + +int coroutine_fn GRAPH_RDLOCK nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking, Error **errp); -int coroutine_fn -blk_co_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags); - - -int coroutine_fn -blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, - BdrvRequestFlags flags); - -int coroutine_fn -blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf); - -int coroutine_fn -blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); - -int coroutine_fn blk_co_do_flush(BlockBackend *blk); - - /* * "I/O or GS" API functions. These functions can run without * the BQL, but only in one specific iothread/main loop. @@ -90,15 +74,7 @@ int coroutine_fn blk_co_do_flush(BlockBackend *blk); * the "I/O or GS" API. */ -int generated_co_wrapper -bdrv_preadv(BdrvChild *child, int64_t offset, unsigned int bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags); - -int generated_co_wrapper -bdrv_pwritev(BdrvChild *child, int64_t offset, unsigned int bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags); - -int generated_co_wrapper +int co_wrapper_mixed_bdrv_rdlock bdrv_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, bool include_base, @@ -109,24 +85,8 @@ bdrv_common_block_status_above(BlockDriverState *bs, int64_t *map, BlockDriverState **file, int *depth); -int generated_co_wrapper + +int co_wrapper_mixed_bdrv_rdlock nbd_do_establish_connection(BlockDriverState *bs, bool blocking, Error **errp); -int generated_co_wrapper -blk_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags); - -int generated_co_wrapper -blk_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, - BdrvRequestFlags flags); - -int generated_co_wrapper -blk_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf); - -int generated_co_wrapper -blk_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); - -int generated_co_wrapper blk_do_flush(BlockBackend *blk); - #endif /* BLOCK_COROUTINES_H */ diff --git a/block/create.c b/block/create.c index 4df43f11f4..6b23a21675 100644 --- a/block/create.c +++ b/block/create.c @@ -59,6 +59,12 @@ static const JobDriver blockdev_create_job_driver = { .run = blockdev_create_run, }; +/* Checking whether the function is present doesn't require the graph lock */ +static inline bool TSA_NO_TSA has_bdrv_co_create(BlockDriver *drv) +{ + return drv->bdrv_co_create; +} + void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, Error **errp) { @@ -79,7 +85,7 @@ void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, } /* Error out if the driver doesn't support .bdrv_co_create */ - if (!drv->bdrv_co_create) { + if (!has_bdrv_co_create(drv)) { error_setg(errp, "Driver does not support blockdev-create"); return; } diff --git a/block/crypto.c b/block/crypto.c index 1ba82984ef..21eed909c1 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -39,6 +39,7 @@ typedef struct BlockCrypto BlockCrypto; struct BlockCrypto { QCryptoBlock *block; bool updating_keys; + BdrvChild *header; /* Reference to the detached LUKS header */ }; @@ -55,40 +56,50 @@ static int block_crypto_probe_generic(QCryptoBlockFormat format, } -static ssize_t block_crypto_read_func(QCryptoBlock *block, - size_t offset, - uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp) +static int block_crypto_read_func(QCryptoBlock *block, + size_t offset, + uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp) { BlockDriverState *bs = opaque; + BlockCrypto *crypto = bs->opaque; ssize_t ret; - ret = bdrv_pread(bs->file, offset, buf, buflen); + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + ret = bdrv_pread(crypto->header ? crypto->header : bs->file, + offset, buflen, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read encryption header"); return ret; } - return ret; + return 0; } -static ssize_t block_crypto_write_func(QCryptoBlock *block, - size_t offset, - const uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp) +static int block_crypto_write_func(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp) { BlockDriverState *bs = opaque; + BlockCrypto *crypto = bs->opaque; ssize_t ret; - ret = bdrv_pwrite(bs->file, offset, buf, buflen); + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + ret = bdrv_pwrite(crypto->header ? crypto->header : bs->file, + offset, buflen, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write encryption header"); return ret; } - return ret; + return 0; } @@ -99,28 +110,25 @@ struct BlockCryptoCreateData { }; -static ssize_t block_crypto_create_write_func(QCryptoBlock *block, - size_t offset, - const uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +block_crypto_create_write_func(QCryptoBlock *block, size_t offset, + const uint8_t *buf, size_t buflen, void *opaque, + Error **errp) { struct BlockCryptoCreateData *data = opaque; ssize_t ret; - ret = blk_pwrite(data->blk, offset, buf, buflen, 0); + ret = blk_pwrite(data->blk, offset, buflen, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write encryption header"); return ret; } - return ret; + return 0; } -static ssize_t block_crypto_create_init_func(QCryptoBlock *block, - size_t headerlen, - void *opaque, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +block_crypto_create_init_func(QCryptoBlock *block, size_t headerlen, + void *opaque, Error **errp) { struct BlockCryptoCreateData *data = opaque; Error *local_error = NULL; @@ -139,7 +147,7 @@ static ssize_t block_crypto_create_init_func(QCryptoBlock *block, data->prealloc, 0, &local_error); if (ret >= 0) { - return ret; + return 0; } error: @@ -154,6 +162,48 @@ error: return ret; } +static int coroutine_fn GRAPH_UNLOCKED +block_crypto_co_format_luks_payload(BlockdevCreateOptionsLUKS *luks_opts, + Error **errp) +{ + BlockDriverState *bs = NULL; + BlockBackend *blk = NULL; + Error *local_error = NULL; + int ret; + + if (luks_opts->size > INT64_MAX) { + return -EFBIG; + } + + bs = bdrv_co_open_blockdev_ref(luks_opts->file, errp); + if (bs == NULL) { + return -EIO; + } + + blk = blk_co_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, + BLK_PERM_ALL, errp); + if (!blk) { + ret = -EPERM; + goto fail; + } + + ret = blk_truncate(blk, luks_opts->size, true, + luks_opts->preallocation, 0, &local_error); + if (ret < 0) { + if (ret == -EFBIG) { + /* Replace the error message with a better one */ + error_free(local_error); + error_setg(errp, "The requested file size is too large"); + } + goto fail; + } + + ret = 0; + +fail: + bdrv_co_unref(bs); + return ret; +} static QemuOptsList block_crypto_runtime_opts_luks = { .name = "crypto", @@ -181,6 +231,7 @@ static QemuOptsList block_crypto_create_opts_luks = { BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG(""), BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG(""), BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME(""), + BLOCK_CRYPTO_OPT_DEF_LUKS_DETACHED_HEADER(""), { /* end of list */ } }, }; @@ -259,24 +310,37 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, int flags, Error **errp) { + ERRP_GUARD(); + BlockCrypto *crypto = bs->opaque; QemuOpts *opts = NULL; - int ret = -EINVAL; + int ret; QCryptoBlockOpenOptions *open_opts = NULL; unsigned int cflags = 0; QDict *cryptoopts = NULL; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_IMAGE, false, errp); - if (!bs->file) { + GLOBAL_STATE_CODE(); + + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; + } + + crypto->header = bdrv_open_child(NULL, options, "header", bs, + &child_of_bds, BDRV_CHILD_METADATA, + true, errp); + if (*errp != NULL) { return -EINVAL; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bs->supported_write_flags = BDRV_REQ_FUA & bs->file->bs->supported_write_flags; opts = qemu_opts_create(opts_spec, NULL, 0, &error_abort); if (!qemu_opts_absorb_qdict(opts, options, errp)) { + ret = -EINVAL; goto cleanup; } @@ -285,12 +349,16 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, open_opts = block_crypto_open_opts_init(cryptoopts, errp); if (!open_opts) { + ret = -EINVAL; goto cleanup; } if (flags & BDRV_O_NO_IO) { cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; } + if (crypto->header != NULL) { + cflags |= QCRYPTO_BLOCK_OPEN_DETACHED; + } crypto->block = qcrypto_block_open(open_opts, NULL, block_crypto_read_func, bs, @@ -313,19 +381,20 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, } -static int block_crypto_co_create_generic(BlockDriverState *bs, - int64_t size, - QCryptoBlockCreateOptions *opts, - PreallocMode prealloc, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +block_crypto_co_create_generic(BlockDriverState *bs, int64_t size, + QCryptoBlockCreateOptions *opts, + PreallocMode prealloc, + unsigned int flags, + Error **errp) { int ret; BlockBackend *blk; QCryptoBlock *crypto = NULL; struct BlockCryptoCreateData data; - blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, - errp); + blk = blk_co_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, + errp); if (!blk) { ret = -EPERM; goto cleanup; @@ -337,7 +406,7 @@ static int block_crypto_co_create_generic(BlockDriverState *bs, data = (struct BlockCryptoCreateData) { .blk = blk, - .size = size, + .size = flags & QCRYPTO_BLOCK_CREATE_DETACHED ? 0 : size, .prealloc = prealloc, }; @@ -345,6 +414,7 @@ static int block_crypto_co_create_generic(BlockDriverState *bs, block_crypto_create_init_func, block_crypto_create_write_func, &data, + flags, errp); if (!crypto) { @@ -355,11 +425,11 @@ static int block_crypto_co_create_generic(BlockDriverState *bs, ret = 0; cleanup: qcrypto_block_free(crypto); - blk_unref(blk); + blk_co_unref(blk); return ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK block_crypto_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) @@ -397,7 +467,7 @@ static int block_crypto_reopen_prepare(BDRVReopenState *state, */ #define BLOCK_CRYPTO_MAX_IO_SIZE (1024 * 1024) -static coroutine_fn int +static int coroutine_fn GRAPH_RDLOCK block_crypto_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -410,7 +480,6 @@ block_crypto_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, uint64_t sector_size = qcrypto_block_get_sector_size(crypto->block); uint64_t payload_offset = qcrypto_block_get_payload_offset(crypto->block); - assert(!flags); assert(payload_offset < INT64_MAX); assert(QEMU_IS_ALIGNED(offset, sector_size)); assert(QEMU_IS_ALIGNED(bytes, sector_size)); @@ -460,7 +529,7 @@ block_crypto_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, } -static coroutine_fn int +static int coroutine_fn GRAPH_RDLOCK block_crypto_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -473,7 +542,8 @@ block_crypto_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, uint64_t sector_size = qcrypto_block_get_sector_size(crypto->block); uint64_t payload_offset = qcrypto_block_get_payload_offset(crypto->block); - assert(!(flags & ~BDRV_REQ_FUA)); + flags &= ~BDRV_REQ_REGISTERED_BUF; + assert(payload_offset < INT64_MAX); assert(QEMU_IS_ALIGNED(offset, sector_size)); assert(QEMU_IS_ALIGNED(bytes, sector_size)); @@ -530,10 +600,11 @@ static void block_crypto_refresh_limits(BlockDriverState *bs, Error **errp) } -static int64_t block_crypto_getlength(BlockDriverState *bs) +static int64_t coroutine_fn GRAPH_RDLOCK +block_crypto_co_getlength(BlockDriverState *bs) { BlockCrypto *crypto = bs->opaque; - int64_t len = bdrv_getlength(bs->file->bs); + int64_t len = bdrv_co_getlength(bs->file->bs); uint64_t offset = qcrypto_block_get_payload_offset(crypto->block); assert(offset < INT64_MAX); @@ -626,21 +697,31 @@ static int block_crypto_open_luks(BlockDriverState *bs, bs, options, flags, errp); } -static int coroutine_fn +static int coroutine_fn GRAPH_UNLOCKED block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp) { BlockdevCreateOptionsLUKS *luks_opts; + BlockDriverState *hdr_bs = NULL; BlockDriverState *bs = NULL; QCryptoBlockCreateOptions create_opts; PreallocMode preallocation = PREALLOC_MODE_OFF; + unsigned int cflags = 0; int ret; assert(create_options->driver == BLOCKDEV_DRIVER_LUKS); luks_opts = &create_options->u.luks; - bs = bdrv_open_blockdev_ref(luks_opts->file, errp); - if (bs == NULL) { - return -EIO; + if (luks_opts->header == NULL && luks_opts->file == NULL) { + error_setg(errp, "Either the parameter 'header' or 'file' must " + "be specified"); + return -EINVAL; + } + + if ((luks_opts->preallocation != PREALLOC_MODE_OFF) && + (luks_opts->file == NULL)) { + error_setg(errp, "Parameter 'preallocation' requires 'file' to be " + "specified for formatting LUKS disk"); + return -EINVAL; } create_opts = (QCryptoBlockCreateOptions) { @@ -652,22 +733,58 @@ block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp) preallocation = luks_opts->preallocation; } - ret = block_crypto_co_create_generic(bs, luks_opts->size, &create_opts, - preallocation, errp); - if (ret < 0) { - goto fail; + if (luks_opts->header) { + /* LUKS volume with detached header */ + hdr_bs = bdrv_co_open_blockdev_ref(luks_opts->header, errp); + if (hdr_bs == NULL) { + return -EIO; + } + + cflags |= QCRYPTO_BLOCK_CREATE_DETACHED; + + /* Format the LUKS header node */ + ret = block_crypto_co_create_generic(hdr_bs, 0, &create_opts, + PREALLOC_MODE_OFF, cflags, errp); + if (ret < 0) { + goto fail; + } + + /* Format the LUKS payload node */ + if (luks_opts->file) { + ret = block_crypto_co_format_luks_payload(luks_opts, errp); + if (ret < 0) { + goto fail; + } + } + } else if (luks_opts->file) { + /* LUKS volume with none-detached header */ + bs = bdrv_co_open_blockdev_ref(luks_opts->file, errp); + if (bs == NULL) { + return -EIO; + } + + ret = block_crypto_co_create_generic(bs, luks_opts->size, &create_opts, + preallocation, cflags, errp); + if (ret < 0) { + goto fail; + } } ret = 0; fail: - bdrv_unref(bs); + if (hdr_bs != NULL) { + bdrv_co_unref(hdr_bs); + } + + if (bs != NULL) { + bdrv_co_unref(bs); + } return ret; } -static int coroutine_fn block_crypto_co_create_opts_luks(BlockDriver *drv, - const char *filename, - QemuOpts *opts, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename, + QemuOpts *opts, Error **errp) { QCryptoBlockCreateOptions *create_opts = NULL; BlockDriverState *bs = NULL; @@ -675,6 +792,9 @@ static int coroutine_fn block_crypto_co_create_opts_luks(BlockDriver *drv, PreallocMode prealloc; char *buf = NULL; int64_t size; + bool detached_hdr = + qemu_opt_get_bool(opts, "detached-header", false); + unsigned int cflags = 0; int ret; Error *local_err = NULL; @@ -702,20 +822,25 @@ static int coroutine_fn block_crypto_co_create_opts_luks(BlockDriver *drv, } /* Create protocol layer */ - ret = bdrv_create_file(filename, opts, errp); + ret = bdrv_co_create_file(filename, opts, errp); if (ret < 0) { goto fail; } - bs = bdrv_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); + bs = bdrv_co_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); if (!bs) { ret = -EINVAL; goto fail; } + if (detached_hdr) { + cflags |= QCRYPTO_BLOCK_CREATE_DETACHED; + } + /* Create format layer */ - ret = block_crypto_co_create_generic(bs, size, create_opts, prealloc, errp); + ret = block_crypto_co_create_generic(bs, size, create_opts, + prealloc, cflags, errp); if (ret < 0) { goto fail; } @@ -727,22 +852,24 @@ fail: * beforehand, it has been truncated and corrupted in the process. */ if (ret) { + bdrv_graph_co_rdlock(); bdrv_co_delete_file_noerr(bs); + bdrv_graph_co_rdunlock(); } - bdrv_unref(bs); + bdrv_co_unref(bs); qapi_free_QCryptoBlockCreateOptions(create_opts); qobject_unref(cryptoopts); return ret; } -static int block_crypto_get_info_luks(BlockDriverState *bs, - BlockDriverInfo *bdi) +static int coroutine_fn GRAPH_RDLOCK +block_crypto_co_get_info_luks(BlockDriverState *bs, BlockDriverInfo *bdi) { BlockDriverInfo subbdi; int ret; - ret = bdrv_get_info(bs->file->bs, &subbdi); + ret = bdrv_co_get_info(bs->file->bs, &subbdi); if (ret != 0) { return ret; } @@ -778,7 +905,7 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp) return spec_info; } -static int +static int GRAPH_RDLOCK block_crypto_amend_prepare(BlockDriverState *bs, Error **errp) { BlockCrypto *crypto = bs->opaque; @@ -794,7 +921,7 @@ block_crypto_amend_prepare(BlockDriverState *bs, Error **errp) return ret; } -static void +static void GRAPH_RDLOCK block_crypto_amend_cleanup(BlockDriverState *bs) { BlockCrypto *crypto = bs->opaque; @@ -829,7 +956,7 @@ block_crypto_amend_options_generic_luks(BlockDriverState *bs, errp); } -static int +static int GRAPH_RDLOCK block_crypto_amend_options_luks(BlockDriverState *bs, QemuOpts *opts, BlockDriverAmendStatusCB *status_cb, @@ -952,9 +1079,9 @@ static BlockDriver bdrv_crypto_luks = { .bdrv_refresh_limits = block_crypto_refresh_limits, .bdrv_co_preadv = block_crypto_co_preadv, .bdrv_co_pwritev = block_crypto_co_pwritev, - .bdrv_getlength = block_crypto_getlength, + .bdrv_co_getlength = block_crypto_co_getlength, .bdrv_measure = block_crypto_measure, - .bdrv_get_info = block_crypto_get_info_luks, + .bdrv_co_get_info = block_crypto_co_get_info_luks, .bdrv_get_specific_info = block_crypto_get_specific_info_luks, .bdrv_amend_options = block_crypto_amend_options_luks, .bdrv_co_amend = block_crypto_co_amend_luks, diff --git a/block/crypto.h b/block/crypto.h index 72e792c9af..dc3d2d5ed9 100644 --- a/block/crypto.h +++ b/block/crypto.h @@ -41,6 +41,7 @@ #define BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG "ivgen-hash-alg" #define BLOCK_CRYPTO_OPT_LUKS_HASH_ALG "hash-alg" #define BLOCK_CRYPTO_OPT_LUKS_ITER_TIME "iter-time" +#define BLOCK_CRYPTO_OPT_LUKS_DETACHED_HEADER "detached-header" #define BLOCK_CRYPTO_OPT_LUKS_KEYSLOT "keyslot" #define BLOCK_CRYPTO_OPT_LUKS_STATE "state" #define BLOCK_CRYPTO_OPT_LUKS_OLD_SECRET "old-secret" @@ -100,6 +101,13 @@ .help = "Select new state of affected keyslots (active/inactive)",\ } +#define BLOCK_CRYPTO_OPT_DEF_LUKS_DETACHED_HEADER(prefix) \ + { \ + .name = prefix BLOCK_CRYPTO_OPT_LUKS_DETACHED_HEADER, \ + .type = QEMU_OPT_BOOL, \ + .help = "Create a detached LUKS header", \ + } + #define BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT(prefix) \ { \ .name = prefix BLOCK_CRYPTO_OPT_LUKS_KEYSLOT, \ diff --git a/block/curl.c b/block/curl.c index 1e0f609579..419f7c89ef 100644 --- a/block/curl.c +++ b/block/curl.c @@ -27,6 +27,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/option.h" +#include "block/block-io.h" #include "block/block_int.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" @@ -37,8 +38,15 @@ // #define DEBUG_VERBOSE +/* CURL 7.85.0 switches to a string based API for specifying + * the desired protocols. + */ +#if LIBCURL_VERSION_NUM >= 0x075500 +#define PROTOCOLS "HTTP,HTTPS,FTP,FTPS" +#else #define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \ CURLPROTO_FTP | CURLPROTO_FTPS) +#endif #define CURL_NUM_STATES 8 #define CURL_NUM_ACB 8 @@ -124,7 +132,7 @@ static gboolean curl_drop_socket(void *key, void *value, void *opaque) CURLSocket *socket = value; BDRVCURLState *s = socket->s; - aio_set_fd_handler(s->aio_context, socket->fd, false, + aio_set_fd_handler(s->aio_context, socket->fd, NULL, NULL, NULL, NULL, NULL); return true; } @@ -172,20 +180,20 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, trace_curl_sock_cb(action, (int)fd); switch (action) { case CURL_POLL_IN: - aio_set_fd_handler(s->aio_context, fd, false, + aio_set_fd_handler(s->aio_context, fd, curl_multi_do, NULL, NULL, NULL, socket); break; case CURL_POLL_OUT: - aio_set_fd_handler(s->aio_context, fd, false, + aio_set_fd_handler(s->aio_context, fd, NULL, curl_multi_do, NULL, NULL, socket); break; case CURL_POLL_INOUT: - aio_set_fd_handler(s->aio_context, fd, false, + aio_set_fd_handler(s->aio_context, fd, curl_multi_do, curl_multi_do, NULL, NULL, socket); break; case CURL_POLL_REMOVE: - aio_set_fd_handler(s->aio_context, fd, false, + aio_set_fd_handler(s->aio_context, fd, NULL, NULL, NULL, NULL, NULL); break; } @@ -509,9 +517,18 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state) * obscure protocols. For example, do not allow POP3/SMTP/IMAP see * CVE-2013-0249. * - * Restricting protocols is only supported from 7.19.4 upwards. + * Restricting protocols is only supported from 7.19.4 upwards. Note: + * version 7.85.0 deprecates CURLOPT_*PROTOCOLS in favour of a string + * based CURLOPT_*PROTOCOLS_STR API. */ -#if LIBCURL_VERSION_NUM >= 0x071304 +#if LIBCURL_VERSION_NUM >= 0x075500 + if (curl_easy_setopt(state->curl, + CURLOPT_PROTOCOLS_STR, PROTOCOLS) || + curl_easy_setopt(state->curl, + CURLOPT_REDIR_PROTOCOLS_STR, PROTOCOLS)) { + goto err; + } +#elif LIBCURL_VERSION_NUM >= 0x071304 if (curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS) || curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS)) { goto err; @@ -669,13 +686,20 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, const char *file; const char *cookie; const char *cookie_secret; - double d; + /* CURL >= 7.55.0 uses curl_off_t for content length instead of a double */ +#if LIBCURL_VERSION_NUM >= 0x073700 + curl_off_t cl; +#else + double cl; +#endif const char *secretid; const char *protocol_delimiter; int ret; + bdrv_graph_rdlock_main_loop(); ret = bdrv_apply_auto_read_only(bs, "curl driver does not support writes", errp); + bdrv_graph_rdunlock_main_loop(); if (ret < 0) { return ret; } @@ -796,27 +820,36 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, } if (curl_easy_perform(state->curl)) goto out; - if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d)) { + /* CURL 7.55.0 deprecates CURLINFO_CONTENT_LENGTH_DOWNLOAD in favour of + * the *_T version which returns a more sensible type for content length. + */ +#if LIBCURL_VERSION_NUM >= 0x073700 + if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &cl)) { goto out; } +#else + if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &cl)) { + goto out; + } +#endif /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not * know or the size is zero. From 7.19.4 CURL returns -1 if size is not * known and zero if it is really zero-length file. */ #if LIBCURL_VERSION_NUM >= 0x071304 - if (d < 0) { + if (cl < 0) { pstrcpy(state->errmsg, CURL_ERROR_SIZE, "Server didn't report file size."); goto out; } #else - if (d <= 0) { + if (cl <= 0) { pstrcpy(state->errmsg, CURL_ERROR_SIZE, "Unknown file size or zero-length file."); goto out; } #endif - s->len = d; + s->len = cl; if ((!strncasecmp(s->url, "http://", strlen("http://")) || !strncasecmp(s->url, "https://", strlen("https://"))) @@ -849,13 +882,15 @@ out_noclean: g_free(s->username); g_free(s->proxyusername); g_free(s->proxypassword); - curl_drop_all_sockets(s->sockets); - g_hash_table_destroy(s->sockets); + if (s->sockets) { + curl_drop_all_sockets(s->sockets); + g_hash_table_destroy(s->sockets); + } qemu_opts_del(opts); return -EINVAL; } -static void curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb) +static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb) { CURLState *state; int running; @@ -957,7 +992,7 @@ static void curl_close(BlockDriverState *bs) g_free(s->proxypassword); } -static int64_t curl_getlength(BlockDriverState *bs) +static int64_t coroutine_fn curl_co_getlength(BlockDriverState *bs) { BDRVCURLState *s = bs->opaque; return s->len; @@ -1001,7 +1036,7 @@ static BlockDriver bdrv_http = { .bdrv_parse_filename = curl_parse_filename, .bdrv_file_open = curl_open, .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .bdrv_co_getlength = curl_co_getlength, .bdrv_co_preadv = curl_co_preadv, @@ -1020,7 +1055,7 @@ static BlockDriver bdrv_https = { .bdrv_parse_filename = curl_parse_filename, .bdrv_file_open = curl_open, .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .bdrv_co_getlength = curl_co_getlength, .bdrv_co_preadv = curl_co_preadv, @@ -1039,7 +1074,7 @@ static BlockDriver bdrv_ftp = { .bdrv_parse_filename = curl_parse_filename, .bdrv_file_open = curl_open, .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .bdrv_co_getlength = curl_co_getlength, .bdrv_co_preadv = curl_co_preadv, @@ -1058,7 +1093,7 @@ static BlockDriver bdrv_ftps = { .bdrv_parse_filename = curl_parse_filename, .bdrv_file_open = curl_open, .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .bdrv_co_getlength = curl_co_getlength, .bdrv_co_preadv = curl_co_preadv, diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index da1b91166f..13a1979755 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -24,8 +24,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "trace.h" +#include "block/block-io.h" #include "block/block_int.h" #include "block/blockjob.h" +#include "block/dirty-bitmap.h" #include "qemu/main-loop.h" struct BdrvDirtyBitmap { @@ -309,10 +311,7 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *parent, return NULL; } - if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) { - error_setg(errp, "Merging of parent and successor bitmap failed"); - return NULL; - } + hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap); parent->disabled = successor->disabled; parent->busy = false; @@ -391,10 +390,11 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) * not fail. * This function doesn't release corresponding BdrvDirtyBitmap. */ -static int coroutine_fn +int coroutine_fn bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, Error **errp) { + assert_bdrv_graph_readable(); if (bs->drv && bs->drv->bdrv_co_remove_persistent_dirty_bitmap) { return bs->drv->bdrv_co_remove_persistent_dirty_bitmap(bs, name, errp); } @@ -402,45 +402,6 @@ bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, return 0; } -typedef struct BdrvRemovePersistentDirtyBitmapCo { - BlockDriverState *bs; - const char *name; - Error **errp; - int ret; -} BdrvRemovePersistentDirtyBitmapCo; - -static void coroutine_fn -bdrv_co_remove_persistent_dirty_bitmap_entry(void *opaque) -{ - BdrvRemovePersistentDirtyBitmapCo *s = opaque; - - s->ret = bdrv_co_remove_persistent_dirty_bitmap(s->bs, s->name, s->errp); - aio_wait_kick(); -} - -int bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, - Error **errp) -{ - if (qemu_in_coroutine()) { - return bdrv_co_remove_persistent_dirty_bitmap(bs, name, errp); - } else { - Coroutine *co; - BdrvRemovePersistentDirtyBitmapCo s = { - .bs = bs, - .name = name, - .errp = errp, - .ret = -EINPROGRESS, - }; - - co = qemu_coroutine_create(bdrv_co_remove_persistent_dirty_bitmap_entry, - &s); - bdrv_coroutine_enter(bs, co); - BDRV_POLL_WHILE(bs, s.ret == -EINPROGRESS); - - return s.ret; - } -} - bool bdrv_supports_persistent_dirty_bitmap(BlockDriverState *bs) { @@ -450,11 +411,12 @@ bdrv_supports_persistent_dirty_bitmap(BlockDriverState *bs) return false; } -static bool coroutine_fn +bool coroutine_fn bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, uint32_t granularity, Error **errp) { BlockDriver *drv = bs->drv; + assert_bdrv_graph_readable(); if (!drv) { error_setg_errno(errp, ENOMEDIUM, @@ -473,51 +435,6 @@ bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, return drv->bdrv_co_can_store_new_dirty_bitmap(bs, name, granularity, errp); } -typedef struct BdrvCanStoreNewDirtyBitmapCo { - BlockDriverState *bs; - const char *name; - uint32_t granularity; - Error **errp; - bool ret; - - bool in_progress; -} BdrvCanStoreNewDirtyBitmapCo; - -static void coroutine_fn bdrv_co_can_store_new_dirty_bitmap_entry(void *opaque) -{ - BdrvCanStoreNewDirtyBitmapCo *s = opaque; - - s->ret = bdrv_co_can_store_new_dirty_bitmap(s->bs, s->name, s->granularity, - s->errp); - s->in_progress = false; - aio_wait_kick(); -} - -bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, - uint32_t granularity, Error **errp) -{ - IO_CODE(); - if (qemu_in_coroutine()) { - return bdrv_co_can_store_new_dirty_bitmap(bs, name, granularity, errp); - } else { - Coroutine *co; - BdrvCanStoreNewDirtyBitmapCo s = { - .bs = bs, - .name = name, - .granularity = granularity, - .errp = errp, - .in_progress = true, - }; - - co = qemu_coroutine_create(bdrv_co_can_store_new_dirty_bitmap_entry, - &s); - bdrv_coroutine_enter(bs, co); - BDRV_POLL_WHILE(bs, s.in_progress); - - return s.ret; - } -} - void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) { bdrv_dirty_bitmaps_lock(bitmap->bs); @@ -544,7 +461,6 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) info->count = bdrv_get_dirty_count(bm); info->granularity = bdrv_dirty_bitmap_granularity(bm); - info->has_name = !!bm->name; info->name = g_strdup(bm->name); info->recording = bdrv_dirty_bitmap_recording(bm); info->busy = bdrv_dirty_bitmap_busy(bm); @@ -912,13 +828,15 @@ bool bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, goto out; } - if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) { - error_setg(errp, "Bitmaps are incompatible and can't be merged"); + if (bdrv_dirty_bitmap_size(src) != bdrv_dirty_bitmap_size(dest)) { + error_setg(errp, "Bitmaps are of different sizes (destination size is %" + PRId64 ", source size is %" PRId64 ") and can't be merged", + bdrv_dirty_bitmap_size(dest), bdrv_dirty_bitmap_size(src)); goto out; } - ret = bdrv_dirty_bitmap_merge_internal(dest, src, backup, false); - assert(ret); + bdrv_dirty_bitmap_merge_internal(dest, src, backup, false); + ret = true; out: bdrv_dirty_bitmaps_unlock(dest->bs); @@ -932,17 +850,16 @@ out: /** * bdrv_dirty_bitmap_merge_internal: merge src into dest. * Does NOT check bitmap permissions; not suitable for use as public API. + * @dest, @src and @backup (if not NULL) must have same size. * * @backup: If provided, make a copy of dest here prior to merge. * @lock: If true, lock and unlock bitmaps on the way in/out. - * returns true if the merge succeeded; false if unattempted. */ -bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, +void bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, HBitmap **backup, bool lock) { - bool ret; IO_CODE(); assert(!bdrv_dirty_bitmap_readonly(dest)); @@ -959,9 +876,9 @@ bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, if (backup) { *backup = dest->bitmap; dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup)); - ret = hbitmap_merge(*backup, src->bitmap, dest->bitmap); + hbitmap_merge(*backup, src->bitmap, dest->bitmap); } else { - ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap); + hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap); } if (lock) { @@ -970,6 +887,4 @@ bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, bdrv_dirty_bitmaps_unlock(src->bs); } } - - return ret; } diff --git a/block/dmg-lzfse.c b/block/dmg-lzfse.c index 6798cf4fbf..4ea0b9b20d 100644 --- a/block/dmg-lzfse.c +++ b/block/dmg-lzfse.c @@ -23,7 +23,12 @@ */ #include "qemu/osdep.h" #include "dmg.h" + +/* Work around a -Wstrict-prototypes warning in LZFSE headers */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" #include +#pragma GCC diagnostic pop static int dmg_uncompress_lzfse_do(char *next_in, unsigned int avail_in, char *next_out, unsigned int avail_out) diff --git a/block/dmg.c b/block/dmg.c index c626587f9c..33dcb3a349 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" #include "qapi/error.h" +#include "block/block-io.h" #include "block/block_int.h" #include "qemu/bswap.h" #include "qemu/error-report.h" @@ -30,11 +31,8 @@ #include "qemu/memalign.h" #include "dmg.h" -int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, - char *next_out, unsigned int avail_out); - -int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in, - char *next_out, unsigned int avail_out); +BdrvDmgUncompressFunc *dmg_uncompress_bz2; +BdrvDmgUncompressFunc *dmg_uncompress_lzfse; enum { /* Limit chunk sizes to prevent unreasonable amounts of memory being used @@ -72,12 +70,13 @@ static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result) +static int GRAPH_RDLOCK +read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result) { uint64_t buffer; int ret; - ret = bdrv_pread(bs->file, offset, &buffer, 8); + ret = bdrv_pread(bs->file, offset, 8, &buffer, 0); if (ret < 0) { return ret; } @@ -86,12 +85,13 @@ static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result) return 0; } -static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) +static int GRAPH_RDLOCK +read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) { uint32_t buffer; int ret; - ret = bdrv_pread(bs->file, offset, &buffer, 4); + ret = bdrv_pread(bs->file, offset, 4, &buffer, 0); if (ret < 0) { return ret; } @@ -172,7 +172,7 @@ static int64_t dmg_find_koly_offset(BdrvChild *file, Error **errp) offset = length - 511 - 512; } length = length < 515 ? length : 515; - ret = bdrv_pread(file, offset, buffer, length); + ret = bdrv_pread(file, offset, length, buffer, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed while reading UDIF trailer"); return ret; @@ -254,6 +254,25 @@ static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds, for (i = s->n_chunks; i < s->n_chunks + chunk_count; i++) { s->types[i] = buff_read_uint32(buffer, offset); if (!dmg_is_known_block_type(s->types[i])) { + switch (s->types[i]) { + case UDBZ: + warn_report_once("dmg-bzip2 module is missing, accessing bzip2 " + "compressed blocks will result in I/O errors"); + break; + case ULFO: + warn_report_once("dmg-lzfse module is missing, accessing lzfse " + "compressed blocks will result in I/O errors"); + break; + case UDCM: + case UDLE: + /* Comments and last entry can be ignored without problems */ + break; + default: + warn_report_once("Image contains chunks of unknown type %x, " + "accessing them will result in I/O errors", + s->types[i]); + break; + } chunk_count--; i--; offset += 40; @@ -304,8 +323,9 @@ fail: return ret; } -static int dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds, - uint64_t info_begin, uint64_t info_length) +static int GRAPH_RDLOCK +dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds, + uint64_t info_begin, uint64_t info_length) { BDRVDMGState *s = bs->opaque; int ret; @@ -352,7 +372,7 @@ static int dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds, offset += 4; buffer = g_realloc(buffer, count); - ret = bdrv_pread(bs->file, offset, buffer, count); + ret = bdrv_pread(bs->file, offset, count, buffer, 0); if (ret < 0) { goto fail; } @@ -371,8 +391,9 @@ fail: return ret; } -static int dmg_read_plist_xml(BlockDriverState *bs, DmgHeaderState *ds, - uint64_t info_begin, uint64_t info_length) +static int GRAPH_RDLOCK +dmg_read_plist_xml(BlockDriverState *bs, DmgHeaderState *ds, + uint64_t info_begin, uint64_t info_length) { BDRVDMGState *s = bs->opaque; int ret; @@ -389,8 +410,8 @@ static int dmg_read_plist_xml(BlockDriverState *bs, DmgHeaderState *ds, buffer = g_malloc(info_length + 1); buffer[info_length] = '\0'; - ret = bdrv_pread(bs->file, info_begin, buffer, info_length); - if (ret != info_length) { + ret = bdrv_pread(bs->file, info_begin, info_length, buffer, 0); + if (ret < 0) { ret = -EINVAL; goto fail; } @@ -435,19 +456,33 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, int64_t offset; int ret; + GLOBAL_STATE_CODE(); + + bdrv_graph_rdlock_main_loop(); ret = bdrv_apply_auto_read_only(bs, NULL, errp); + bdrv_graph_rdunlock_main_loop(); if (ret < 0) { return ret; } - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_IMAGE, false, errp); - if (!bs->file) { - return -EINVAL; + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; } - block_module_load_one("dmg-bz2"); - block_module_load_one("dmg-lzfse"); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + /* + * NB: if uncompress submodules are absent, + * ie block_module_load return value == 0, the function pointers + * dmg_uncompress_bz2 and dmg_uncompress_lzfse will be NULL. + */ + if (block_module_load("dmg-bz2", errp) < 0) { + return -EINVAL; + } + if (block_module_load("dmg-lzfse", errp) < 0) { + return -EINVAL; + } s->n_chunks = 0; s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; @@ -592,7 +627,8 @@ err: return s->n_chunks; /* error */ } -static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) +static int coroutine_fn GRAPH_RDLOCK +dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) { BDRVDMGState *s = bs->opaque; @@ -609,9 +645,9 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) case UDZO: { /* zlib compressed */ /* we need to buffer, because only the chunk as whole can be * inflated. */ - ret = bdrv_pread(bs->file, s->offsets[chunk], - s->compressed_chunk, s->lengths[chunk]); - if (ret != s->lengths[chunk]) { + ret = bdrv_co_pread(bs->file, s->offsets[chunk], s->lengths[chunk], + s->compressed_chunk, 0); + if (ret < 0) { return -1; } @@ -635,9 +671,9 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) } /* we need to buffer, because only the chunk as whole can be * inflated. */ - ret = bdrv_pread(bs->file, s->offsets[chunk], - s->compressed_chunk, s->lengths[chunk]); - if (ret != s->lengths[chunk]) { + ret = bdrv_co_pread(bs->file, s->offsets[chunk], s->lengths[chunk], + s->compressed_chunk, 0); + if (ret < 0) { return -1; } @@ -656,9 +692,9 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) } /* we need to buffer, because only the chunk as whole can be * inflated. */ - ret = bdrv_pread(bs->file, s->offsets[chunk], - s->compressed_chunk, s->lengths[chunk]); - if (ret != s->lengths[chunk]) { + ret = bdrv_co_pread(bs->file, s->offsets[chunk], s->lengths[chunk], + s->compressed_chunk, 0); + if (ret < 0) { return -1; } @@ -672,9 +708,9 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) } break; case UDRW: /* copy */ - ret = bdrv_pread(bs->file, s->offsets[chunk], - s->uncompressed_chunk, s->lengths[chunk]); - if (ret != s->lengths[chunk]) { + ret = bdrv_co_pread(bs->file, s->offsets[chunk], s->lengths[chunk], + s->uncompressed_chunk, 0); + if (ret < 0) { return -1; } break; @@ -689,7 +725,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) return 0; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK dmg_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { diff --git a/block/dmg.h b/block/dmg.h index e488601b62..dcd6165e63 100644 --- a/block/dmg.h +++ b/block/dmg.h @@ -51,10 +51,10 @@ typedef struct BDRVDMGState { z_stream zstream; } BDRVDMGState; -extern int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, - char *next_out, unsigned int avail_out); +typedef int BdrvDmgUncompressFunc(char *next_in, unsigned int avail_in, + char *next_out, unsigned int avail_out); -extern int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in, - char *next_out, unsigned int avail_out); +extern BdrvDmgUncompressFunc *dmg_uncompress_bz2; +extern BdrvDmgUncompressFunc *dmg_uncompress_lzfse; #endif diff --git a/block/export/export.c b/block/export/export.c index 7253af3bc3..6d51ae8ed7 100644 --- a/block/export/export.c +++ b/block/export/export.c @@ -26,6 +26,9 @@ #ifdef CONFIG_VHOST_USER_BLK_SERVER #include "vhost-user-blk-server.h" #endif +#ifdef CONFIG_VDUSE_BLK_EXPORT +#include "vduse-blk.h" +#endif static const BlockExportDriver *blk_exp_drivers[] = { &blk_exp_nbd, @@ -35,6 +38,9 @@ static const BlockExportDriver *blk_exp_drivers[] = { #ifdef CONFIG_FUSE &blk_exp_fuse, #endif +#ifdef CONFIG_VDUSE_BLK_EXPORT + &blk_exp_vduse_blk, +#endif }; /* Only accessed from the main thread */ @@ -77,6 +83,8 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) uint64_t perm; int ret; + GLOBAL_STATE_CODE(); + if (!id_wellformed(export->id)) { error_setg(errp, "Invalid block export id"); return NULL; @@ -106,9 +114,8 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) } ctx = bdrv_get_aio_context(bs); - aio_context_acquire(ctx); - if (export->has_iothread) { + if (export->iothread) { IOThread *iothread; AioContext *new_ctx; Error **set_context_errp; @@ -123,10 +130,8 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) /* Ignore errors with fixed-iothread=false */ set_context_errp = fixed_iothread ? errp : NULL; - ret = bdrv_try_set_aio_context(bs, new_ctx, set_context_errp); + ret = bdrv_try_change_aio_context(bs, new_ctx, NULL, set_context_errp); if (ret == 0) { - aio_context_release(ctx); - aio_context_acquire(new_ctx); ctx = new_ctx; } else if (fixed_iothread) { goto fail; @@ -139,7 +144,9 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) * access since the export could be available before migration handover. * ctx was acquired in the caller. */ + bdrv_graph_rdlock_main_loop(); bdrv_activate(bs, NULL); + bdrv_graph_rdunlock_main_loop(); perm = BLK_PERM_CONSISTENT_READ; if (export->writable) { @@ -181,13 +188,13 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) assert(exp->blk != NULL); QLIST_INSERT_HEAD(&block_exports, exp, next); - - aio_context_release(ctx); return exp; fail: - blk_unref(blk); - aio_context_release(ctx); + if (blk) { + blk_set_dev_ops(blk, NULL, NULL); + blk_unref(blk); + } if (exp) { g_free(exp->id); g_free(exp); @@ -195,37 +202,31 @@ fail: return NULL; } -/* Callers must hold exp->ctx lock */ void blk_exp_ref(BlockExport *exp) { - assert(exp->refcount > 0); - exp->refcount++; + assert(qatomic_read(&exp->refcount) > 0); + qatomic_inc(&exp->refcount); } /* Runs in the main thread */ static void blk_exp_delete_bh(void *opaque) { BlockExport *exp = opaque; - AioContext *aio_context = exp->ctx; - - aio_context_acquire(aio_context); assert(exp->refcount == 0); QLIST_REMOVE(exp, next); exp->drv->delete(exp); + blk_set_dev_ops(exp->blk, NULL, NULL); blk_unref(exp->blk); qapi_event_send_block_export_deleted(exp->id); g_free(exp->id); g_free(exp); - - aio_context_release(aio_context); } -/* Callers must hold exp->ctx lock */ void blk_exp_unref(BlockExport *exp) { - assert(exp->refcount > 0); - if (--exp->refcount == 0) { + assert(qatomic_read(&exp->refcount) > 0); + if (qatomic_fetch_dec(&exp->refcount) == 1) { /* Touch the block_exports list only in the main thread */ aio_bh_schedule_oneshot(qemu_get_aio_context(), blk_exp_delete_bh, exp); @@ -237,22 +238,16 @@ void blk_exp_unref(BlockExport *exp) * connections and other internally held references start to shut down. When * the function returns, there may still be active references while the export * is in the process of shutting down. - * - * Acquires exp->ctx internally. Callers must *not* hold the lock. */ void blk_exp_request_shutdown(BlockExport *exp) { - AioContext *aio_context = exp->ctx; - - aio_context_acquire(aio_context); - /* * If the user doesn't own the export any more, it is already shutting * down. We must not call .request_shutdown and decrease the refcount a * second time. */ if (!exp->user_owned) { - goto out; + return; } exp->drv->request_shutdown(exp); @@ -260,9 +255,6 @@ void blk_exp_request_shutdown(BlockExport *exp) assert(exp->user_owned); exp->user_owned = false; blk_exp_unref(exp); - -out: - aio_context_release(aio_context); } /* @@ -300,7 +292,7 @@ void blk_exp_close_all_type(BlockExportType type) blk_exp_request_shutdown(exp); } - AIO_WAIT_WHILE(NULL, blk_exp_has_type(type)); + AIO_WAIT_WHILE_UNLOCKED(NULL, blk_exp_has_type(type)); } void blk_exp_close_all(void) @@ -333,7 +325,8 @@ void qmp_block_export_del(const char *id, if (!has_mode) { mode = BLOCK_EXPORT_REMOVE_MODE_SAFE; } - if (mode == BLOCK_EXPORT_REMOVE_MODE_SAFE && exp->refcount > 1) { + if (mode == BLOCK_EXPORT_REMOVE_MODE_SAFE && + qatomic_read(&exp->refcount) > 1) { error_setg(errp, "export '%s' still in use", exp->id); error_append_hint(errp, "Use mode='hard' to force client " "disconnect\n"); diff --git a/block/export/fuse.c b/block/export/fuse.c index e80b24a867..3307b64089 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -21,12 +21,13 @@ #include "qemu/osdep.h" #include "qemu/memalign.h" #include "block/aio.h" -#include "block/block.h" +#include "block/block_int-common.h" #include "block/export.h" #include "block/fuse.h" #include "block/qapi.h" #include "qapi/error.h" #include "qapi/qapi-commands-block.h" +#include "qemu/main-loop.h" #include "sysemu/block-backend.h" #include @@ -49,6 +50,7 @@ typedef struct FuseExport { struct fuse_session *fuse_session; struct fuse_buf fuse_buf; + unsigned int in_flight; /* atomic */ bool mounted, fd_handler_set_up; char *mountpoint; @@ -77,6 +79,42 @@ static void read_from_fuse_export(void *opaque); static bool is_regular_file(const char *path, Error **errp); +static void fuse_export_drained_begin(void *opaque) +{ + FuseExport *exp = opaque; + + aio_set_fd_handler(exp->common.ctx, + fuse_session_fd(exp->fuse_session), + NULL, NULL, NULL, NULL, NULL); + exp->fd_handler_set_up = false; +} + +static void fuse_export_drained_end(void *opaque) +{ + FuseExport *exp = opaque; + + /* Refresh AioContext in case it changed */ + exp->common.ctx = blk_get_aio_context(exp->common.blk); + + aio_set_fd_handler(exp->common.ctx, + fuse_session_fd(exp->fuse_session), + read_from_fuse_export, NULL, NULL, NULL, exp); + exp->fd_handler_set_up = true; +} + +static bool fuse_export_drained_poll(void *opaque) +{ + FuseExport *exp = opaque; + + return qatomic_read(&exp->in_flight) > 0; +} + +static const BlockDevOps fuse_export_blk_dev_ops = { + .drained_begin = fuse_export_drained_begin, + .drained_end = fuse_export_drained_end, + .drained_poll = fuse_export_drained_poll, +}; + static int fuse_export_create(BlockExport *blk_exp, BlockExportOptions *blk_exp_args, Error **errp) @@ -100,6 +138,15 @@ static int fuse_export_create(BlockExport *blk_exp, } } + blk_set_dev_ops(exp->common.blk, &fuse_export_blk_dev_ops, exp); + + /* + * We handle draining ourselves using an in-flight counter and by disabling + * the FUSE fd handler. Do not queue BlockBackend requests, they need to + * complete so the in-flight counter reaches zero. + */ + blk_set_disable_request_queuing(exp->common.blk, true); + init_exports_table(); /* @@ -223,7 +270,7 @@ static int setup_fuse_export(FuseExport *exp, const char *mountpoint, g_hash_table_insert(exports, g_strdup(mountpoint), NULL); aio_set_fd_handler(exp->common.ctx, - fuse_session_fd(exp->fuse_session), true, + fuse_session_fd(exp->fuse_session), read_from_fuse_export, NULL, NULL, NULL, exp); exp->fd_handler_set_up = true; @@ -245,6 +292,8 @@ static void read_from_fuse_export(void *opaque) blk_exp_ref(&exp->common); + qatomic_inc(&exp->in_flight); + do { ret = fuse_session_receive_buf(exp->fuse_session, &exp->fuse_buf); } while (ret == -EINTR); @@ -255,6 +304,10 @@ static void read_from_fuse_export(void *opaque) fuse_session_process_buf(exp->fuse_session, &exp->fuse_buf); out: + if (qatomic_fetch_dec(&exp->in_flight) == 1) { + aio_wait_kick(); /* wake AIO_WAIT_WHILE() */ + } + blk_exp_unref(&exp->common); } @@ -267,7 +320,7 @@ static void fuse_export_shutdown(BlockExport *blk_exp) if (exp->fd_handler_set_up) { aio_set_fd_handler(exp->common.ctx, - fuse_session_fd(exp->fuse_session), true, + fuse_session_fd(exp->fuse_session), NULL, NULL, NULL, NULL, NULL); exp->fd_handler_set_up = false; } @@ -554,7 +607,7 @@ static void fuse_read(fuse_req_t req, fuse_ino_t inode, return; } - ret = blk_pread(exp->common.blk, offset, buf, size); + ret = blk_pread(exp->common.blk, offset, size, buf, 0); if (ret >= 0) { fuse_reply_buf(req, buf, size); } else { @@ -607,7 +660,7 @@ static void fuse_write(fuse_req_t req, fuse_ino_t inode, const char *buf, } } - ret = blk_pwrite(exp->common.blk, offset, buf, size, 0); + ret = blk_pwrite(exp->common.blk, offset, size, buf, 0); if (ret >= 0) { fuse_reply_write(req, size); } else { @@ -672,7 +725,16 @@ static void fuse_fallocate(fuse_req_t req, fuse_ino_t inode, int mode, do { int size = MIN(length, BDRV_REQUEST_MAX_BYTES); - ret = blk_pdiscard(exp->common.blk, offset, size); + ret = blk_pwrite_zeroes(exp->common.blk, offset, size, + BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK); + if (ret == -ENOTSUP) { + /* + * fallocate() specifies to return EOPNOTSUPP for unsupported + * operations + */ + ret = -EOPNOTSUPP; + } + offset += size; length -= size; } while (ret == 0 && length > 0); diff --git a/block/export/meson.build b/block/export/meson.build index 0a08e384c7..c60116f455 100644 --- a/block/export/meson.build +++ b/block/export/meson.build @@ -1,7 +1,12 @@ blockdev_ss.add(files('export.c')) if have_vhost_user_blk_server - blockdev_ss.add(files('vhost-user-blk-server.c')) + blockdev_ss.add(files('vhost-user-blk-server.c', 'virtio-blk-handler.c')) endif blockdev_ss.add(when: fuse, if_true: files('fuse.c')) + +if have_vduse_blk_export + blockdev_ss.add(files('vduse-blk.c', 'virtio-blk-handler.c')) + blockdev_ss.add(libvduse) +endif diff --git a/block/export/vduse-blk.c b/block/export/vduse-blk.c new file mode 100644 index 0000000000..172f73cef4 --- /dev/null +++ b/block/export/vduse-blk.c @@ -0,0 +1,422 @@ +/* + * Export QEMU block device via VDUSE + * + * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved. + * + * Author: + * Xie Yongji + * + * 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 "qapi/error.h" +#include "block/export.h" +#include "qemu/error-report.h" +#include "util/block-helpers.h" +#include "subprojects/libvduse/libvduse.h" +#include "virtio-blk-handler.h" + +#include "standard-headers/linux/virtio_blk.h" + +#define VDUSE_DEFAULT_NUM_QUEUE 1 +#define VDUSE_DEFAULT_QUEUE_SIZE 256 + +typedef struct VduseBlkExport { + BlockExport export; + VirtioBlkHandler handler; + VduseDev *dev; + uint16_t num_queues; + char *recon_file; + unsigned int inflight; /* atomic */ + bool vqs_started; +} VduseBlkExport; + +typedef struct VduseBlkReq { + VduseVirtqElement elem; + VduseVirtq *vq; +} VduseBlkReq; + +static void vduse_blk_inflight_inc(VduseBlkExport *vblk_exp) +{ + if (qatomic_fetch_inc(&vblk_exp->inflight) == 0) { + /* Prevent export from being deleted */ + blk_exp_ref(&vblk_exp->export); + } +} + +static void vduse_blk_inflight_dec(VduseBlkExport *vblk_exp) +{ + if (qatomic_fetch_dec(&vblk_exp->inflight) == 1) { + /* Wake AIO_WAIT_WHILE() */ + aio_wait_kick(); + + /* Now the export can be deleted */ + blk_exp_unref(&vblk_exp->export); + } +} + +static void vduse_blk_req_complete(VduseBlkReq *req, size_t in_len) +{ + vduse_queue_push(req->vq, &req->elem, in_len); + vduse_queue_notify(req->vq); + + free(req); +} + +static void coroutine_fn vduse_blk_virtio_process_req(void *opaque) +{ + VduseBlkReq *req = opaque; + VduseVirtq *vq = req->vq; + VduseDev *dev = vduse_queue_get_dev(vq); + VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev); + VirtioBlkHandler *handler = &vblk_exp->handler; + VduseVirtqElement *elem = &req->elem; + struct iovec *in_iov = elem->in_sg; + struct iovec *out_iov = elem->out_sg; + unsigned in_num = elem->in_num; + unsigned out_num = elem->out_num; + int in_len; + + in_len = virtio_blk_process_req(handler, in_iov, + out_iov, in_num, out_num); + if (in_len < 0) { + free(req); + return; + } + + vduse_blk_req_complete(req, in_len); + vduse_blk_inflight_dec(vblk_exp); +} + +static void vduse_blk_vq_handler(VduseDev *dev, VduseVirtq *vq) +{ + VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev); + + while (1) { + VduseBlkReq *req; + + req = vduse_queue_pop(vq, sizeof(VduseBlkReq)); + if (!req) { + break; + } + req->vq = vq; + + Coroutine *co = + qemu_coroutine_create(vduse_blk_virtio_process_req, req); + + vduse_blk_inflight_inc(vblk_exp); + qemu_coroutine_enter(co); + } +} + +static void on_vduse_vq_kick(void *opaque) +{ + VduseVirtq *vq = opaque; + VduseDev *dev = vduse_queue_get_dev(vq); + int fd = vduse_queue_get_fd(vq); + eventfd_t kick_data; + + if (eventfd_read(fd, &kick_data) == -1) { + error_report("failed to read data from eventfd"); + return; + } + + vduse_blk_vq_handler(dev, vq); +} + +static void vduse_blk_enable_queue(VduseDev *dev, VduseVirtq *vq) +{ + VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev); + + if (!vblk_exp->vqs_started) { + return; /* vduse_blk_drained_end() will start vqs later */ + } + + aio_set_fd_handler(vblk_exp->export.ctx, vduse_queue_get_fd(vq), + on_vduse_vq_kick, NULL, NULL, NULL, vq); + /* Make sure we don't miss any kick after reconnecting */ + eventfd_write(vduse_queue_get_fd(vq), 1); +} + +static void vduse_blk_disable_queue(VduseDev *dev, VduseVirtq *vq) +{ + VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev); + int fd = vduse_queue_get_fd(vq); + + if (fd < 0) { + return; + } + + aio_set_fd_handler(vblk_exp->export.ctx, fd, + NULL, NULL, NULL, NULL, NULL); +} + +static const VduseOps vduse_blk_ops = { + .enable_queue = vduse_blk_enable_queue, + .disable_queue = vduse_blk_disable_queue, +}; + +static void on_vduse_dev_kick(void *opaque) +{ + VduseDev *dev = opaque; + + vduse_dev_handler(dev); +} + +static void vduse_blk_attach_ctx(VduseBlkExport *vblk_exp, AioContext *ctx) +{ + aio_set_fd_handler(vblk_exp->export.ctx, vduse_dev_get_fd(vblk_exp->dev), + on_vduse_dev_kick, NULL, NULL, NULL, + vblk_exp->dev); + + /* Virtqueues are handled by vduse_blk_drained_end() */ +} + +static void vduse_blk_detach_ctx(VduseBlkExport *vblk_exp) +{ + aio_set_fd_handler(vblk_exp->export.ctx, vduse_dev_get_fd(vblk_exp->dev), + NULL, NULL, NULL, NULL, NULL); + + /* Virtqueues are handled by vduse_blk_drained_begin() */ +} + + +static void blk_aio_attached(AioContext *ctx, void *opaque) +{ + VduseBlkExport *vblk_exp = opaque; + + vblk_exp->export.ctx = ctx; + vduse_blk_attach_ctx(vblk_exp, ctx); +} + +static void blk_aio_detach(void *opaque) +{ + VduseBlkExport *vblk_exp = opaque; + + vduse_blk_detach_ctx(vblk_exp); + vblk_exp->export.ctx = NULL; +} + +static void vduse_blk_resize(void *opaque) +{ + BlockExport *exp = opaque; + VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); + struct virtio_blk_config config; + + config.capacity = + cpu_to_le64(blk_getlength(exp->blk) >> VIRTIO_BLK_SECTOR_BITS); + vduse_dev_update_config(vblk_exp->dev, sizeof(config.capacity), + offsetof(struct virtio_blk_config, capacity), + (char *)&config.capacity); +} + +static void vduse_blk_stop_virtqueues(VduseBlkExport *vblk_exp) +{ + for (uint16_t i = 0; i < vblk_exp->num_queues; i++) { + VduseVirtq *vq = vduse_dev_get_queue(vblk_exp->dev, i); + vduse_blk_disable_queue(vblk_exp->dev, vq); + } + + vblk_exp->vqs_started = false; +} + +static void vduse_blk_start_virtqueues(VduseBlkExport *vblk_exp) +{ + vblk_exp->vqs_started = true; + + for (uint16_t i = 0; i < vblk_exp->num_queues; i++) { + VduseVirtq *vq = vduse_dev_get_queue(vblk_exp->dev, i); + vduse_blk_enable_queue(vblk_exp->dev, vq); + } +} + +static void vduse_blk_drained_begin(void *opaque) +{ + BlockExport *exp = opaque; + VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); + + vduse_blk_stop_virtqueues(vblk_exp); +} + +static void vduse_blk_drained_end(void *opaque) +{ + BlockExport *exp = opaque; + VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); + + vduse_blk_start_virtqueues(vblk_exp); +} + +static bool vduse_blk_drained_poll(void *opaque) +{ + BlockExport *exp = opaque; + VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); + + return qatomic_read(&vblk_exp->inflight) > 0; +} + +static const BlockDevOps vduse_block_ops = { + .resize_cb = vduse_blk_resize, + .drained_begin = vduse_blk_drained_begin, + .drained_end = vduse_blk_drained_end, + .drained_poll = vduse_blk_drained_poll, +}; + +static int vduse_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, + Error **errp) +{ + VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); + BlockExportOptionsVduseBlk *vblk_opts = &opts->u.vduse_blk; + uint64_t logical_block_size = VIRTIO_BLK_SECTOR_SIZE; + uint16_t num_queues = VDUSE_DEFAULT_NUM_QUEUE; + uint16_t queue_size = VDUSE_DEFAULT_QUEUE_SIZE; + Error *local_err = NULL; + struct virtio_blk_config config = { 0 }; + uint64_t features; + int i, ret; + + if (vblk_opts->has_num_queues) { + num_queues = vblk_opts->num_queues; + if (num_queues == 0) { + error_setg(errp, "num-queues must be greater than 0"); + return -EINVAL; + } + } + + if (vblk_opts->has_queue_size) { + queue_size = vblk_opts->queue_size; + if (queue_size <= 2 || !is_power_of_2(queue_size) || + queue_size > VIRTQUEUE_MAX_SIZE) { + error_setg(errp, "queue-size is invalid"); + return -EINVAL; + } + } + + if (vblk_opts->has_logical_block_size) { + logical_block_size = vblk_opts->logical_block_size; + check_block_size(exp->id, "logical-block-size", logical_block_size, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + } + vblk_exp->num_queues = num_queues; + vblk_exp->handler.blk = exp->blk; + vblk_exp->handler.serial = g_strdup(vblk_opts->serial ?: ""); + vblk_exp->handler.logical_block_size = logical_block_size; + vblk_exp->handler.writable = opts->writable; + vblk_exp->vqs_started = true; + + config.capacity = + cpu_to_le64(blk_getlength(exp->blk) >> VIRTIO_BLK_SECTOR_BITS); + config.seg_max = cpu_to_le32(queue_size - 2); + config.min_io_size = cpu_to_le16(1); + config.opt_io_size = cpu_to_le32(1); + config.num_queues = cpu_to_le16(num_queues); + config.blk_size = cpu_to_le32(logical_block_size); + config.max_discard_sectors = cpu_to_le32(VIRTIO_BLK_MAX_DISCARD_SECTORS); + config.max_discard_seg = cpu_to_le32(1); + config.discard_sector_alignment = + cpu_to_le32(logical_block_size >> VIRTIO_BLK_SECTOR_BITS); + config.max_write_zeroes_sectors = + cpu_to_le32(VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS); + config.max_write_zeroes_seg = cpu_to_le32(1); + + features = vduse_get_virtio_features() | + (1ULL << VIRTIO_BLK_F_SEG_MAX) | + (1ULL << VIRTIO_BLK_F_TOPOLOGY) | + (1ULL << VIRTIO_BLK_F_BLK_SIZE) | + (1ULL << VIRTIO_BLK_F_FLUSH) | + (1ULL << VIRTIO_BLK_F_DISCARD) | + (1ULL << VIRTIO_BLK_F_WRITE_ZEROES); + + if (num_queues > 1) { + features |= 1ULL << VIRTIO_BLK_F_MQ; + } + if (!opts->writable) { + features |= 1ULL << VIRTIO_BLK_F_RO; + } + + vblk_exp->dev = vduse_dev_create(vblk_opts->name, VIRTIO_ID_BLOCK, 0, + features, num_queues, + sizeof(struct virtio_blk_config), + (char *)&config, &vduse_blk_ops, + vblk_exp); + if (!vblk_exp->dev) { + error_setg(errp, "failed to create vduse device"); + ret = -ENOMEM; + goto err_dev; + } + + vblk_exp->recon_file = g_strdup_printf("%s/vduse-blk-%s", + g_get_tmp_dir(), vblk_opts->name); + if (vduse_set_reconnect_log_file(vblk_exp->dev, vblk_exp->recon_file)) { + error_setg(errp, "failed to set reconnect log file"); + ret = -EINVAL; + goto err; + } + + for (i = 0; i < num_queues; i++) { + vduse_dev_setup_queue(vblk_exp->dev, i, queue_size); + } + + aio_set_fd_handler(exp->ctx, vduse_dev_get_fd(vblk_exp->dev), + on_vduse_dev_kick, NULL, NULL, NULL, vblk_exp->dev); + + blk_add_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, + vblk_exp); + blk_set_dev_ops(exp->blk, &vduse_block_ops, exp); + + /* + * We handle draining ourselves using an in-flight counter and by disabling + * virtqueue fd handlers. Do not queue BlockBackend requests, they need to + * complete so the in-flight counter reaches zero. + */ + blk_set_disable_request_queuing(exp->blk, true); + + return 0; +err: + vduse_dev_destroy(vblk_exp->dev); + g_free(vblk_exp->recon_file); +err_dev: + g_free(vblk_exp->handler.serial); + return ret; +} + +static void vduse_blk_exp_delete(BlockExport *exp) +{ + VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); + int ret; + + assert(qatomic_read(&vblk_exp->inflight) == 0); + + vduse_blk_detach_ctx(vblk_exp); + blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, + vblk_exp); + ret = vduse_dev_destroy(vblk_exp->dev); + if (ret != -EBUSY) { + unlink(vblk_exp->recon_file); + } + g_free(vblk_exp->recon_file); + g_free(vblk_exp->handler.serial); +} + +/* Called with exp->ctx acquired */ +static void vduse_blk_exp_request_shutdown(BlockExport *exp) +{ + VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); + + vduse_blk_stop_virtqueues(vblk_exp); +} + +const BlockExportDriver blk_exp_vduse_blk = { + .type = BLOCK_EXPORT_TYPE_VDUSE_BLK, + .instance_size = sizeof(VduseBlkExport), + .create = vduse_blk_exp_create, + .delete = vduse_blk_exp_delete, + .request_shutdown = vduse_blk_exp_request_shutdown, +}; diff --git a/block/export/vduse-blk.h b/block/export/vduse-blk.h new file mode 100644 index 0000000000..c4eeb1b70e --- /dev/null +++ b/block/export/vduse-blk.h @@ -0,0 +1,20 @@ +/* + * Export QEMU block device via VDUSE + * + * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved. + * + * Author: + * Xie Yongji + * + * 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 VDUSE_BLK_H +#define VDUSE_BLK_H + +#include "block/export.h" + +extern const BlockExportDriver blk_exp_vduse_blk; + +#endif /* VDUSE_BLK_H */ diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c index a129204c44..50c358e8cd 100644 --- a/block/export/vhost-user-blk-server.c +++ b/block/export/vhost-user-blk-server.c @@ -1,5 +1,5 @@ /* - * Sharing QEMU block devices via vhost-user protocal + * Sharing QEMU block devices via vhost-user protocol * * Parts of the code based on nbd/server.c. * @@ -10,6 +10,7 @@ * later. See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "block/block.h" #include "subprojects/libvhost-user/libvhost-user.h" /* only for the type definitions */ #include "standard-headers/linux/virtio_blk.h" @@ -17,31 +18,15 @@ #include "vhost-user-blk-server.h" #include "qapi/error.h" #include "qom/object_interfaces.h" -#include "sysemu/block-backend.h" #include "util/block-helpers.h" - -/* - * Sector units are 512 bytes regardless of the - * virtio_blk_config->blk_size value. - */ -#define VIRTIO_BLK_SECTOR_BITS 9 -#define VIRTIO_BLK_SECTOR_SIZE (1ull << VIRTIO_BLK_SECTOR_BITS) +#include "virtio-blk-handler.h" enum { VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1, - VHOST_USER_BLK_MAX_DISCARD_SECTORS = 32768, - VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS = 32768, -}; -struct virtio_blk_inhdr { - unsigned char status; }; typedef struct VuBlkReq { VuVirtqElement elem; - int64_t sector_num; - size_t size; - struct virtio_blk_inhdr *in; - struct virtio_blk_outhdr out; VuServer *server; struct VuVirtq *vq; } VuBlkReq; @@ -50,249 +35,48 @@ typedef struct VuBlkReq { typedef struct { BlockExport export; VuServer vu_server; - uint32_t blk_size; + VirtioBlkHandler handler; QIOChannelSocket *sioc; struct virtio_blk_config blkcfg; - bool writable; } VuBlkExport; -static void vu_blk_req_complete(VuBlkReq *req) +static void vu_blk_req_complete(VuBlkReq *req, size_t in_len) { VuDev *vu_dev = &req->server->vu_dev; - /* IO size with 1 extra status byte */ - vu_queue_push(vu_dev, req->vq, &req->elem, req->size + 1); + vu_queue_push(vu_dev, req->vq, &req->elem, in_len); vu_queue_notify(vu_dev, req->vq); free(req); } -static bool vu_blk_sect_range_ok(VuBlkExport *vexp, uint64_t sector, - size_t size) -{ - uint64_t nb_sectors; - uint64_t total_sectors; - - if (size % VIRTIO_BLK_SECTOR_SIZE) { - return false; - } - - nb_sectors = size >> VIRTIO_BLK_SECTOR_BITS; - - QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != VIRTIO_BLK_SECTOR_SIZE); - if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) { - return false; - } - if ((sector << VIRTIO_BLK_SECTOR_BITS) % vexp->blk_size) { - return false; - } - blk_get_geometry(vexp->export.blk, &total_sectors); - if (sector > total_sectors || nb_sectors > total_sectors - sector) { - return false; - } - return true; -} - -static int coroutine_fn -vu_blk_discard_write_zeroes(VuBlkExport *vexp, struct iovec *iov, - uint32_t iovcnt, uint32_t type) -{ - BlockBackend *blk = vexp->export.blk; - struct virtio_blk_discard_write_zeroes desc; - ssize_t size; - uint64_t sector; - uint32_t num_sectors; - uint32_t max_sectors; - uint32_t flags; - int bytes; - - /* Only one desc is currently supported */ - if (unlikely(iov_size(iov, iovcnt) > sizeof(desc))) { - return VIRTIO_BLK_S_UNSUPP; - } - - size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc)); - if (unlikely(size != sizeof(desc))) { - error_report("Invalid size %zd, expected %zu", size, sizeof(desc)); - return VIRTIO_BLK_S_IOERR; - } - - sector = le64_to_cpu(desc.sector); - num_sectors = le32_to_cpu(desc.num_sectors); - flags = le32_to_cpu(desc.flags); - max_sectors = (type == VIRTIO_BLK_T_WRITE_ZEROES) ? - VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS : - VHOST_USER_BLK_MAX_DISCARD_SECTORS; - - /* This check ensures that 'bytes' fits in an int */ - if (unlikely(num_sectors > max_sectors)) { - return VIRTIO_BLK_S_IOERR; - } - - bytes = num_sectors << VIRTIO_BLK_SECTOR_BITS; - - if (unlikely(!vu_blk_sect_range_ok(vexp, sector, bytes))) { - return VIRTIO_BLK_S_IOERR; - } - - /* - * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard - * and write zeroes commands if any unknown flag is set. - */ - if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { - return VIRTIO_BLK_S_UNSUPP; - } - - if (type == VIRTIO_BLK_T_WRITE_ZEROES) { - int blk_flags = 0; - - if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) { - blk_flags |= BDRV_REQ_MAY_UNMAP; - } - - if (blk_co_pwrite_zeroes(blk, sector << VIRTIO_BLK_SECTOR_BITS, - bytes, blk_flags) == 0) { - return VIRTIO_BLK_S_OK; - } - } else if (type == VIRTIO_BLK_T_DISCARD) { - /* - * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for - * discard commands if the unmap flag is set. - */ - if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { - return VIRTIO_BLK_S_UNSUPP; - } - - if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS, - bytes) == 0) { - return VIRTIO_BLK_S_OK; - } - } - - return VIRTIO_BLK_S_IOERR; -} - -/* Called with server refcount increased, must decrease before returning */ +/* + * Called with server in_flight counter increased, must decrease before + * returning. + */ static void coroutine_fn vu_blk_virtio_process_req(void *opaque) { VuBlkReq *req = opaque; VuServer *server = req->server; VuVirtqElement *elem = &req->elem; - uint32_t type; - VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server); - BlockBackend *blk = vexp->export.blk; - + VirtioBlkHandler *handler = &vexp->handler; struct iovec *in_iov = elem->in_sg; struct iovec *out_iov = elem->out_sg; unsigned in_num = elem->in_num; unsigned out_num = elem->out_num; + int in_len; - /* refer to hw/block/virtio_blk.c */ - if (elem->out_num < 1 || elem->in_num < 1) { - error_report("virtio-blk request missing headers"); - goto err; + in_len = virtio_blk_process_req(handler, in_iov, out_iov, + in_num, out_num); + if (in_len < 0) { + free(req); + vhost_user_server_dec_in_flight(server); + return; } - if (unlikely(iov_to_buf(out_iov, out_num, 0, &req->out, - sizeof(req->out)) != sizeof(req->out))) { - error_report("virtio-blk request outhdr too short"); - goto err; - } - - iov_discard_front(&out_iov, &out_num, sizeof(req->out)); - - if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) { - error_report("virtio-blk request inhdr too short"); - goto err; - } - - /* We always touch the last byte, so just see how big in_iov is. */ - req->in = (void *)in_iov[in_num - 1].iov_base - + in_iov[in_num - 1].iov_len - - sizeof(struct virtio_blk_inhdr); - iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); - - type = le32_to_cpu(req->out.type); - switch (type & ~VIRTIO_BLK_T_BARRIER) { - case VIRTIO_BLK_T_IN: - case VIRTIO_BLK_T_OUT: { - QEMUIOVector qiov; - int64_t offset; - ssize_t ret = 0; - bool is_write = type & VIRTIO_BLK_T_OUT; - req->sector_num = le64_to_cpu(req->out.sector); - - if (is_write && !vexp->writable) { - req->in->status = VIRTIO_BLK_S_IOERR; - break; - } - - if (is_write) { - qemu_iovec_init_external(&qiov, out_iov, out_num); - } else { - qemu_iovec_init_external(&qiov, in_iov, in_num); - } - - if (unlikely(!vu_blk_sect_range_ok(vexp, - req->sector_num, - qiov.size))) { - req->in->status = VIRTIO_BLK_S_IOERR; - break; - } - - offset = req->sector_num << VIRTIO_BLK_SECTOR_BITS; - - if (is_write) { - ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0); - } else { - ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0); - } - if (ret >= 0) { - req->in->status = VIRTIO_BLK_S_OK; - } else { - req->in->status = VIRTIO_BLK_S_IOERR; - } - break; - } - case VIRTIO_BLK_T_FLUSH: - if (blk_co_flush(blk) == 0) { - req->in->status = VIRTIO_BLK_S_OK; - } else { - req->in->status = VIRTIO_BLK_S_IOERR; - } - break; - case VIRTIO_BLK_T_GET_ID: { - size_t size = MIN(iov_size(&elem->in_sg[0], in_num), - VIRTIO_BLK_ID_BYTES); - snprintf(elem->in_sg[0].iov_base, size, "%s", "vhost_user_blk"); - req->in->status = VIRTIO_BLK_S_OK; - req->size = elem->in_sg[0].iov_len; - break; - } - case VIRTIO_BLK_T_DISCARD: - case VIRTIO_BLK_T_WRITE_ZEROES: { - if (!vexp->writable) { - req->in->status = VIRTIO_BLK_S_IOERR; - break; - } - - req->in->status = vu_blk_discard_write_zeroes(vexp, out_iov, out_num, - type); - break; - } - default: - req->in->status = VIRTIO_BLK_S_UNSUPP; - break; - } - - vu_blk_req_complete(req); - vhost_user_server_unref(server); - return; - -err: - free(req); - vhost_user_server_unref(server); + vu_blk_req_complete(req, in_len); + vhost_user_server_dec_in_flight(server); } static void vu_blk_process_vq(VuDev *vu_dev, int idx) @@ -314,7 +98,7 @@ static void vu_blk_process_vq(VuDev *vu_dev, int idx) Coroutine *co = qemu_coroutine_create(vu_blk_virtio_process_req, req); - vhost_user_server_ref(server); + vhost_user_server_inc_in_flight(server); qemu_coroutine_enter(co); } } @@ -348,7 +132,7 @@ static uint64_t vu_blk_get_features(VuDev *dev) 1ull << VIRTIO_RING_F_EVENT_IDX | 1ull << VHOST_USER_F_PROTOCOL_FEATURES; - if (!vexp->writable) { + if (!vexp->handler.writable) { features |= 1ull << VIRTIO_BLK_F_RO; } @@ -383,7 +167,7 @@ vu_blk_set_config(VuDev *vu_dev, const uint8_t *data, uint8_t wce; /* don't support live migration */ - if (flags != VHOST_SET_CONFIG_TYPE_MASTER) { + if (flags != VHOST_SET_CONFIG_TYPE_FRONTEND) { return -EINVAL; } @@ -428,15 +212,21 @@ static void blk_aio_attached(AioContext *ctx, void *opaque) { VuBlkExport *vexp = opaque; + /* + * The actual attach will happen in vu_blk_drained_end() and we just + * restore ctx here. + */ vexp->export.ctx = ctx; - vhost_user_server_attach_aio_context(&vexp->vu_server, ctx); } static void blk_aio_detach(void *opaque) { VuBlkExport *vexp = opaque; - vhost_user_server_detach_aio_context(&vexp->vu_server); + /* + * The actual detach already happened in vu_blk_drained_begin() but from + * this point on we must not access ctx anymore. + */ vexp->export.ctx = NULL; } @@ -455,12 +245,12 @@ vu_blk_initialize_config(BlockDriverState *bs, config->opt_io_size = cpu_to_le32(1); config->num_queues = cpu_to_le16(num_queues); config->max_discard_sectors = - cpu_to_le32(VHOST_USER_BLK_MAX_DISCARD_SECTORS); + cpu_to_le32(VIRTIO_BLK_MAX_DISCARD_SECTORS); config->max_discard_seg = cpu_to_le32(1); config->discard_sector_alignment = cpu_to_le32(blk_size >> VIRTIO_BLK_SECTOR_BITS); config->max_write_zeroes_sectors - = cpu_to_le32(VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS); + = cpu_to_le32(VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS); config->max_write_zeroes_seg = cpu_to_le32(1); } @@ -471,6 +261,59 @@ static void vu_blk_exp_request_shutdown(BlockExport *exp) vhost_user_server_stop(&vexp->vu_server); } +static void vu_blk_exp_resize(void *opaque) +{ + VuBlkExport *vexp = opaque; + BlockDriverState *bs = blk_bs(vexp->handler.blk); + int64_t new_size = bdrv_getlength(bs); + + if (new_size < 0) { + error_printf("Failed to get length of block node '%s'", + bdrv_get_node_name(bs)); + return; + } + + vexp->blkcfg.capacity = cpu_to_le64(new_size >> VIRTIO_BLK_SECTOR_BITS); + + vu_config_change_msg(&vexp->vu_server.vu_dev); +} + +static void vu_blk_drained_begin(void *opaque) +{ + VuBlkExport *vexp = opaque; + + vexp->vu_server.quiescing = true; + vhost_user_server_detach_aio_context(&vexp->vu_server); +} + +static void vu_blk_drained_end(void *opaque) +{ + VuBlkExport *vexp = opaque; + + vexp->vu_server.quiescing = false; + vhost_user_server_attach_aio_context(&vexp->vu_server, vexp->export.ctx); +} + +/* + * Ensures that bdrv_drained_begin() waits until in-flight requests complete + * and the server->co_trip coroutine has terminated. It will be restarted in + * vhost_user_server_attach_aio_context(). + */ +static bool vu_blk_drained_poll(void *opaque) +{ + VuBlkExport *vexp = opaque; + VuServer *server = &vexp->vu_server; + + return server->co_trip || vhost_user_server_has_in_flight(server); +} + +static const BlockDevOps vu_blk_dev_ops = { + .drained_begin = vu_blk_drained_begin, + .drained_end = vu_blk_drained_end, + .drained_poll = vu_blk_drained_poll, + .resize_cb = vu_blk_exp_resize, +}; + static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, Error **errp) { @@ -480,7 +323,6 @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, uint64_t logical_block_size; uint16_t num_queues = VHOST_USER_BLK_NUM_QUEUES_DEFAULT; - vexp->writable = opts->writable; vexp->blkcfg.wce = 0; if (vu_opts->has_logical_block_size) { @@ -494,8 +336,6 @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, error_propagate(errp, local_err); return -EINVAL; } - vexp->blk_size = logical_block_size; - blk_set_guest_block_size(exp->blk, logical_block_size); if (vu_opts->has_num_queues) { num_queues = vu_opts->num_queues; @@ -504,6 +344,10 @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, error_setg(errp, "num-queues must be greater than 0"); return -EINVAL; } + vexp->handler.blk = exp->blk; + vexp->handler.serial = g_strdup("vhost_user_blk"); + vexp->handler.logical_block_size = logical_block_size; + vexp->handler.writable = opts->writable; vu_blk_initialize_config(blk_bs(exp->blk), &vexp->blkcfg, logical_block_size, num_queues); @@ -511,10 +355,13 @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, blk_add_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, vexp); + blk_set_dev_ops(exp->blk, &vu_blk_dev_ops, vexp); + if (!vhost_user_server_start(&vexp->vu_server, vu_opts->addr, exp->ctx, num_queues, &vu_blk_iface, errp)) { blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, vexp); + g_free(vexp->handler.serial); return -EADDRNOTAVAIL; } @@ -527,6 +374,7 @@ static void vu_blk_exp_delete(BlockExport *exp) blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, vexp); + g_free(vexp->handler.serial); } const BlockExportDriver blk_exp_vhost_user_blk = { diff --git a/block/export/vhost-user-blk-server.h b/block/export/vhost-user-blk-server.h index fcf46fc8a5..77fb5c0131 100644 --- a/block/export/vhost-user-blk-server.h +++ b/block/export/vhost-user-blk-server.h @@ -1,5 +1,5 @@ /* - * Sharing QEMU block devices via vhost-user protocal + * Sharing QEMU block devices via vhost-user protocol * * Copyright (c) Coiby Xu . * Copyright (c) 2020 Red Hat, Inc. diff --git a/block/export/virtio-blk-handler.c b/block/export/virtio-blk-handler.c new file mode 100644 index 0000000000..bc1cec6757 --- /dev/null +++ b/block/export/virtio-blk-handler.c @@ -0,0 +1,241 @@ +/* + * Handler for virtio-blk I/O + * + * Copyright (c) 2020 Red Hat, Inc. + * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved. + * + * Author: + * Coiby Xu + * Xie Yongji + * + * 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 "virtio-blk-handler.h" + +#include "standard-headers/linux/virtio_blk.h" + +struct virtio_blk_inhdr { + unsigned char status; +}; + +static bool coroutine_fn +virtio_blk_sect_range_ok(BlockBackend *blk, uint32_t block_size, + uint64_t sector, size_t size) +{ + uint64_t nb_sectors; + uint64_t total_sectors; + + if (size % VIRTIO_BLK_SECTOR_SIZE) { + return false; + } + + nb_sectors = size >> VIRTIO_BLK_SECTOR_BITS; + + QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != VIRTIO_BLK_SECTOR_SIZE); + if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return false; + } + if ((sector << VIRTIO_BLK_SECTOR_BITS) % block_size) { + return false; + } + blk_co_get_geometry(blk, &total_sectors); + if (sector > total_sectors || nb_sectors > total_sectors - sector) { + return false; + } + return true; +} + +static int coroutine_fn +virtio_blk_discard_write_zeroes(VirtioBlkHandler *handler, struct iovec *iov, + uint32_t iovcnt, uint32_t type) +{ + BlockBackend *blk = handler->blk; + struct virtio_blk_discard_write_zeroes desc; + ssize_t size; + uint64_t sector; + uint32_t num_sectors; + uint32_t max_sectors; + uint32_t flags; + int bytes; + + /* Only one desc is currently supported */ + if (unlikely(iov_size(iov, iovcnt) > sizeof(desc))) { + return VIRTIO_BLK_S_UNSUPP; + } + + size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc)); + if (unlikely(size != sizeof(desc))) { + error_report("Invalid size %zd, expected %zu", size, sizeof(desc)); + return VIRTIO_BLK_S_IOERR; + } + + sector = le64_to_cpu(desc.sector); + num_sectors = le32_to_cpu(desc.num_sectors); + flags = le32_to_cpu(desc.flags); + max_sectors = (type == VIRTIO_BLK_T_WRITE_ZEROES) ? + VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS : + VIRTIO_BLK_MAX_DISCARD_SECTORS; + + /* This check ensures that 'bytes' fits in an int */ + if (unlikely(num_sectors > max_sectors)) { + return VIRTIO_BLK_S_IOERR; + } + + bytes = num_sectors << VIRTIO_BLK_SECTOR_BITS; + + if (unlikely(!virtio_blk_sect_range_ok(blk, handler->logical_block_size, + sector, bytes))) { + return VIRTIO_BLK_S_IOERR; + } + + /* + * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard + * and write zeroes commands if any unknown flag is set. + */ + if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { + return VIRTIO_BLK_S_UNSUPP; + } + + if (type == VIRTIO_BLK_T_WRITE_ZEROES) { + int blk_flags = 0; + + if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) { + blk_flags |= BDRV_REQ_MAY_UNMAP; + } + + if (blk_co_pwrite_zeroes(blk, sector << VIRTIO_BLK_SECTOR_BITS, + bytes, blk_flags) == 0) { + return VIRTIO_BLK_S_OK; + } + } else if (type == VIRTIO_BLK_T_DISCARD) { + /* + * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for + * discard commands if the unmap flag is set. + */ + if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { + return VIRTIO_BLK_S_UNSUPP; + } + + if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS, + bytes) == 0) { + return VIRTIO_BLK_S_OK; + } + } + + return VIRTIO_BLK_S_IOERR; +} + +int coroutine_fn virtio_blk_process_req(VirtioBlkHandler *handler, + struct iovec *in_iov, + struct iovec *out_iov, + unsigned int in_num, + unsigned int out_num) +{ + BlockBackend *blk = handler->blk; + struct virtio_blk_inhdr *in; + struct virtio_blk_outhdr out; + uint32_t type; + int in_len; + + if (out_num < 1 || in_num < 1) { + error_report("virtio-blk request missing headers"); + return -EINVAL; + } + + if (unlikely(iov_to_buf(out_iov, out_num, 0, &out, + sizeof(out)) != sizeof(out))) { + error_report("virtio-blk request outhdr too short"); + return -EINVAL; + } + + iov_discard_front(&out_iov, &out_num, sizeof(out)); + + if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) { + error_report("virtio-blk request inhdr too short"); + return -EINVAL; + } + + /* We always touch the last byte, so just see how big in_iov is. */ + in_len = iov_size(in_iov, in_num); + in = (void *)in_iov[in_num - 1].iov_base + + in_iov[in_num - 1].iov_len + - sizeof(struct virtio_blk_inhdr); + iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); + + type = le32_to_cpu(out.type); + switch (type & ~VIRTIO_BLK_T_BARRIER) { + case VIRTIO_BLK_T_IN: + case VIRTIO_BLK_T_OUT: { + QEMUIOVector qiov; + int64_t offset; + ssize_t ret = 0; + bool is_write = type & VIRTIO_BLK_T_OUT; + int64_t sector_num = le64_to_cpu(out.sector); + + if (is_write && !handler->writable) { + in->status = VIRTIO_BLK_S_IOERR; + break; + } + + if (is_write) { + qemu_iovec_init_external(&qiov, out_iov, out_num); + } else { + qemu_iovec_init_external(&qiov, in_iov, in_num); + } + + if (unlikely(!virtio_blk_sect_range_ok(blk, + handler->logical_block_size, + sector_num, qiov.size))) { + in->status = VIRTIO_BLK_S_IOERR; + break; + } + + offset = sector_num << VIRTIO_BLK_SECTOR_BITS; + + if (is_write) { + ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0); + } else { + ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0); + } + if (ret >= 0) { + in->status = VIRTIO_BLK_S_OK; + } else { + in->status = VIRTIO_BLK_S_IOERR; + } + break; + } + case VIRTIO_BLK_T_FLUSH: + if (blk_co_flush(blk) == 0) { + in->status = VIRTIO_BLK_S_OK; + } else { + in->status = VIRTIO_BLK_S_IOERR; + } + break; + case VIRTIO_BLK_T_GET_ID: { + size_t size = MIN(strlen(handler->serial) + 1, + MIN(iov_size(in_iov, in_num), + VIRTIO_BLK_ID_BYTES)); + iov_from_buf(in_iov, in_num, 0, handler->serial, size); + in->status = VIRTIO_BLK_S_OK; + break; + } + case VIRTIO_BLK_T_DISCARD: + case VIRTIO_BLK_T_WRITE_ZEROES: + if (!handler->writable) { + in->status = VIRTIO_BLK_S_IOERR; + break; + } + in->status = virtio_blk_discard_write_zeroes(handler, out_iov, + out_num, type); + break; + default: + in->status = VIRTIO_BLK_S_UNSUPP; + break; + } + + return in_len; +} diff --git a/block/export/virtio-blk-handler.h b/block/export/virtio-blk-handler.h new file mode 100644 index 0000000000..150d44cff2 --- /dev/null +++ b/block/export/virtio-blk-handler.h @@ -0,0 +1,37 @@ +/* + * Handler for virtio-blk I/O + * + * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved. + * + * Author: + * Xie Yongji + * + * 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 VIRTIO_BLK_HANDLER_H +#define VIRTIO_BLK_HANDLER_H + +#include "sysemu/block-backend.h" + +#define VIRTIO_BLK_SECTOR_BITS 9 +#define VIRTIO_BLK_SECTOR_SIZE (1ULL << VIRTIO_BLK_SECTOR_BITS) + +#define VIRTIO_BLK_MAX_DISCARD_SECTORS 32768 +#define VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS 32768 + +typedef struct { + BlockBackend *blk; + char *serial; + uint32_t logical_block_size; + bool writable; +} VirtioBlkHandler; + +int coroutine_fn virtio_blk_process_req(VirtioBlkHandler *handler, + struct iovec *in_iov, + struct iovec *out_iov, + unsigned int in_num, + unsigned int out_num); + +#endif /* VIRTIO_BLK_HANDLER_H */ diff --git a/block/file-posix.c b/block/file-posix.c index 48cd096624..35684f7e21 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/error-report.h" +#include "block/block-io.h" #include "block/block_int.h" #include "qemu/module.h" #include "qemu/option.h" @@ -67,6 +68,9 @@ #include #include #include +#if defined(CONFIG_BLKZONED) +#include +#endif #include #include #include @@ -154,7 +158,6 @@ typedef struct BDRVRawState { bool has_discard:1; bool has_write_zeroes:1; - bool discard_zeroes:1; bool use_linux_aio:1; bool use_linux_io_uring:1; int page_cache_inconsistent; /* errno from fdatasync failure */ @@ -216,6 +219,13 @@ typedef struct RawPosixAIOData { PreallocMode prealloc; Error **errp; } truncate; + struct { + unsigned int *nr_zones; + BlockZoneDescriptor *zones; + } zone_report; + struct { + unsigned long op; + } zone_mgmt; }; } RawPosixAIOData; @@ -702,17 +712,11 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, #ifdef CONFIG_LINUX_AIO /* Currently Linux does AIO only for files opened with O_DIRECT */ - if (s->use_linux_aio) { - if (!(s->open_flags & O_DIRECT)) { - error_setg(errp, "aio=native was specified, but it requires " - "cache.direct=on, which was not specified."); - ret = -EINVAL; - goto fail; - } - if (!aio_setup_linux_aio(bdrv_get_aio_context(bs), errp)) { - error_prepend(errp, "Unable to use native AIO: "); - goto fail; - } + if (s->use_linux_aio && !(s->open_flags & O_DIRECT)) { + error_setg(errp, "aio=native was specified, but it requires " + "cache.direct=on, which was not specified."); + ret = -EINVAL; + goto fail; } #else if (s->use_linux_aio) { @@ -723,14 +727,7 @@ 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 +#ifndef CONFIG_LINUX_IO_URING if (s->use_linux_io_uring) { error_setg(errp, "aio=io_uring was specified, but is not supported " "in this build."); @@ -755,7 +752,6 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, ret = -EINVAL; goto fail; } else { - s->discard_zeroes = true; s->has_fallocate = true; } } else { @@ -767,21 +763,26 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, goto fail; } } +#ifdef CONFIG_BLKZONED + /* + * The kernel page cache does not reliably work for writes to SWR zones + * of zoned block device because it can not guarantee the order of writes. + */ + if ((bs->bl.zoned != BLK_Z_NONE) && + (!(s->open_flags & O_DIRECT))) { + error_setg(errp, "The driver supports zoned devices, and it requires " + "cache.direct=on, which was not specified."); + return -EINVAL; /* No host kernel page cache */ + } +#endif if (S_ISBLK(st.st_mode)) { -#ifdef BLKDISCARDZEROES - unsigned int arg; - if (ioctl(s->fd, BLKDISCARDZEROES, &arg) == 0 && arg) { - s->discard_zeroes = true; - } -#endif #ifdef __linux__ /* On Linux 3.10, BLKDISCARD leaves stale data in the page cache. Do * not rely on the contents of discarded blocks unless using O_DIRECT. * Same for BLKZEROOUT. */ if (!(bs->open_flags & BDRV_O_NOCACHE)) { - s->discard_zeroes = false; s->has_write_zeroes = false; } #endif @@ -1144,9 +1145,9 @@ static int raw_reopen_prepare(BDRVReopenState *state, * As part of reopen prepare we also want to create new fd by * raw_reconfigure_getfd(). But it wants updated "perm", when in * bdrv_reopen_multiple() .bdrv_reopen_prepare() callback called prior to - * permission update. Happily, permission update is always a part (a seprate - * stage) of bdrv_reopen_multiple() so we can rely on this fact and - * reconfigure fd in raw_check_perm(). + * permission update. Happily, permission update is always a part + * (a separate stage) of bdrv_reopen_multiple() so we can rely on this + * fact and reconfigure fd in raw_check_perm(). */ s->reopen_state = state; @@ -1210,15 +1211,89 @@ static int hdev_get_max_hw_transfer(int fd, struct stat *st) #endif } +/* + * Get a sysfs attribute value as character string. + */ +#ifdef CONFIG_LINUX +static int get_sysfs_str_val(struct stat *st, const char *attribute, + char **val) { + g_autofree char *sysfspath = NULL; + size_t len; + + if (!S_ISBLK(st->st_mode)) { + return -ENOTSUP; + } + + sysfspath = g_strdup_printf("/sys/dev/block/%u:%u/queue/%s", + major(st->st_rdev), minor(st->st_rdev), + attribute); + if (!g_file_get_contents(sysfspath, val, &len, NULL)) { + return -ENOENT; + } + + /* The file is ended with '\n' */ + char *p; + p = *val; + if (*(p + len - 1) == '\n') { + *(p + len - 1) = '\0'; + } + return 0; +} +#endif + +#if defined(CONFIG_BLKZONED) +static int get_sysfs_zoned_model(struct stat *st, BlockZoneModel *zoned) +{ + g_autofree char *val = NULL; + int ret; + + ret = get_sysfs_str_val(st, "zoned", &val); + if (ret < 0) { + return ret; + } + + if (strcmp(val, "host-managed") == 0) { + *zoned = BLK_Z_HM; + } else if (strcmp(val, "host-aware") == 0) { + *zoned = BLK_Z_HA; + } else if (strcmp(val, "none") == 0) { + *zoned = BLK_Z_NONE; + } else { + return -ENOTSUP; + } + return 0; +} +#endif /* defined(CONFIG_BLKZONED) */ + +/* + * Get a sysfs attribute value as a long integer. + */ +#ifdef CONFIG_LINUX +static long get_sysfs_long_val(struct stat *st, const char *attribute) +{ + g_autofree char *str = NULL; + const char *end; + long val; + int ret; + + ret = get_sysfs_str_val(st, attribute, &str); + if (ret < 0) { + return ret; + } + + /* The file is ended with '\n', pass 'end' to accept that. */ + ret = qemu_strtol(str, &end, 10, &val); + if (ret == 0 && end && *end == '\0') { + ret = val; + } + return ret; +} +#endif + static int hdev_get_max_segments(int fd, struct stat *st) { #ifdef CONFIG_LINUX - char buf[32]; - const char *end; - char *sysfspath = NULL; int ret; - int sysfd = -1; - long max_segments; if (S_ISCHR(st->st_mode)) { if (ioctl(fd, SG_GET_SG_TABLESIZE, &ret) == 0) { @@ -1226,46 +1301,179 @@ static int hdev_get_max_segments(int fd, struct stat *st) } return -ENOTSUP; } - - if (!S_ISBLK(st->st_mode)) { - return -ENOTSUP; - } - - sysfspath = g_strdup_printf("/sys/dev/block/%u:%u/queue/max_segments", - major(st->st_rdev), minor(st->st_rdev)); - sysfd = open(sysfspath, O_RDONLY); - if (sysfd == -1) { - ret = -errno; - goto out; - } - do { - ret = read(sysfd, buf, sizeof(buf) - 1); - } while (ret == -1 && errno == EINTR); - if (ret < 0) { - ret = -errno; - goto out; - } else if (ret == 0) { - ret = -EIO; - goto out; - } - buf[ret] = 0; - /* The file is ended with '\n', pass 'end' to accept that. */ - ret = qemu_strtol(buf, &end, 10, &max_segments); - if (ret == 0 && end && *end == '\n') { - ret = max_segments; - } - -out: - if (sysfd != -1) { - close(sysfd); - } - g_free(sysfspath); - return ret; + return get_sysfs_long_val(st, "max_segments"); #else return -ENOTSUP; #endif } +#if defined(CONFIG_BLKZONED) +/* + * If the reset_all flag is true, then the wps of zone whose state is + * not readonly or offline should be all reset to the start sector. + * Else, take the real wp of the device. + */ +static int get_zones_wp(BlockDriverState *bs, int fd, int64_t offset, + unsigned int nrz, bool reset_all) +{ + struct blk_zone *blkz; + size_t rep_size; + uint64_t sector = offset >> BDRV_SECTOR_BITS; + BlockZoneWps *wps = bs->wps; + unsigned int j = offset / bs->bl.zone_size; + unsigned int n = 0, i = 0; + int ret; + rep_size = sizeof(struct blk_zone_report) + nrz * sizeof(struct blk_zone); + g_autofree struct blk_zone_report *rep = NULL; + + rep = g_malloc(rep_size); + blkz = (struct blk_zone *)(rep + 1); + while (n < nrz) { + memset(rep, 0, rep_size); + rep->sector = sector; + rep->nr_zones = nrz - n; + + do { + ret = ioctl(fd, BLKREPORTZONE, rep); + } while (ret != 0 && errno == EINTR); + if (ret != 0) { + error_report("%d: ioctl BLKREPORTZONE at %" PRId64 " failed %d", + fd, offset, errno); + return -errno; + } + + if (!rep->nr_zones) { + break; + } + + for (i = 0; i < rep->nr_zones; ++i, ++n, ++j) { + /* + * The wp tracking cares only about sequential writes required and + * sequential write preferred zones so that the wp can advance to + * the right location. + * Use the most significant bit of the wp location to indicate the + * zone type: 0 for SWR/SWP zones and 1 for conventional zones. + */ + if (blkz[i].type == BLK_ZONE_TYPE_CONVENTIONAL) { + wps->wp[j] |= 1ULL << 63; + } else { + switch(blkz[i].cond) { + case BLK_ZONE_COND_FULL: + case BLK_ZONE_COND_READONLY: + /* Zone not writable */ + wps->wp[j] = (blkz[i].start + blkz[i].len) << BDRV_SECTOR_BITS; + break; + case BLK_ZONE_COND_OFFLINE: + /* Zone not writable nor readable */ + wps->wp[j] = (blkz[i].start) << BDRV_SECTOR_BITS; + break; + default: + if (reset_all) { + wps->wp[j] = blkz[i].start << BDRV_SECTOR_BITS; + } else { + wps->wp[j] = blkz[i].wp << BDRV_SECTOR_BITS; + } + break; + } + } + } + sector = blkz[i - 1].start + blkz[i - 1].len; + } + + return 0; +} + +static void update_zones_wp(BlockDriverState *bs, int fd, int64_t offset, + unsigned int nrz) +{ + if (get_zones_wp(bs, fd, offset, nrz, 0) < 0) { + error_report("update zone wp failed"); + } +} + +static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st, + Error **errp) +{ + BDRVRawState *s = bs->opaque; + BlockZoneModel zoned; + int ret; + + ret = get_sysfs_zoned_model(st, &zoned); + if (ret < 0 || zoned == BLK_Z_NONE) { + goto no_zoned; + } + bs->bl.zoned = zoned; + + ret = get_sysfs_long_val(st, "max_open_zones"); + if (ret >= 0) { + bs->bl.max_open_zones = ret; + } + + ret = get_sysfs_long_val(st, "max_active_zones"); + if (ret >= 0) { + bs->bl.max_active_zones = ret; + } + + /* + * The zoned device must at least have zone size and nr_zones fields. + */ + ret = get_sysfs_long_val(st, "chunk_sectors"); + if (ret < 0) { + error_setg_errno(errp, -ret, "Unable to read chunk_sectors " + "sysfs attribute"); + goto no_zoned; + } else if (!ret) { + error_setg(errp, "Read 0 from chunk_sectors sysfs attribute"); + goto no_zoned; + } + bs->bl.zone_size = ret << BDRV_SECTOR_BITS; + + ret = get_sysfs_long_val(st, "nr_zones"); + if (ret < 0) { + error_setg_errno(errp, -ret, "Unable to read nr_zones " + "sysfs attribute"); + goto no_zoned; + } else if (!ret) { + error_setg(errp, "Read 0 from nr_zones sysfs attribute"); + goto no_zoned; + } + bs->bl.nr_zones = ret; + + ret = get_sysfs_long_val(st, "zone_append_max_bytes"); + if (ret > 0) { + bs->bl.max_append_sectors = ret >> BDRV_SECTOR_BITS; + } + + ret = get_sysfs_long_val(st, "physical_block_size"); + if (ret >= 0) { + bs->bl.write_granularity = ret; + } + + /* The refresh_limits() function can be called multiple times. */ + g_free(bs->wps); + bs->wps = g_malloc(sizeof(BlockZoneWps) + + sizeof(int64_t) * bs->bl.nr_zones); + ret = get_zones_wp(bs, s->fd, 0, bs->bl.nr_zones, 0); + if (ret < 0) { + error_setg_errno(errp, -ret, "report wps failed"); + goto no_zoned; + } + qemu_co_mutex_init(&bs->wps->colock); + return; + +no_zoned: + bs->bl.zoned = BLK_Z_NONE; + g_free(bs->wps); + bs->wps = NULL; +} +#else /* !defined(CONFIG_BLKZONED) */ +static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st, + Error **errp) +{ + bs->bl.zoned = BLK_Z_NONE; +} +#endif /* !defined(CONFIG_BLKZONED) */ + static void raw_refresh_limits(BlockDriverState *bs, Error **errp) { BDRVRawState *s = bs->opaque; @@ -1295,7 +1503,7 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) } #endif - if (bs->sg || S_ISBLK(st.st_mode)) { + if (bdrv_is_sg(bs) || S_ISBLK(st.st_mode)) { int ret = hdev_get_max_hw_transfer(s->fd, &st); if (ret > 0 && ret <= BDRV_REQUEST_MAX_BYTES) { @@ -1307,6 +1515,8 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.max_hw_iov = ret; } } + + raw_refresh_zoned_limits(bs, &st, errp); } static int check_for_dasd(int fd) @@ -1330,9 +1540,12 @@ static int hdev_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) BDRVRawState *s = bs->opaque; int ret; - /* If DASD, get blocksizes */ + /* If DASD or zoned devices, get blocksizes */ if (check_for_dasd(s->fd) < 0) { - return -ENOTSUP; + /* zoned devices are not DASD */ + if (bs->bl.zoned == BLK_Z_NONE) { + return -ENOTSUP; + } } ret = probe_logical_blocksize(s->fd, &bsz->log); if (ret < 0) { @@ -1388,9 +1601,9 @@ static int handle_aiocb_ioctl(void *opaque) RawPosixAIOData *aiocb = opaque; int ret; - do { - ret = ioctl(aiocb->aio_fildes, aiocb->ioctl.cmd, aiocb->ioctl.buf); - } while (ret == -1 && errno == EINTR); + ret = RETRY_ON_EINTR( + ioctl(aiocb->aio_fildes, aiocb->ioctl.cmd, aiocb->ioctl.buf) + ); if (ret == -1) { return -errno; } @@ -1472,18 +1685,17 @@ static ssize_t handle_aiocb_rw_vector(RawPosixAIOData *aiocb) { ssize_t len; - do { - if (aiocb->aio_type & QEMU_AIO_WRITE) - len = qemu_pwritev(aiocb->aio_fildes, - aiocb->io.iov, - aiocb->io.niov, - aiocb->aio_offset); - else - len = qemu_preadv(aiocb->aio_fildes, - aiocb->io.iov, - aiocb->io.niov, - aiocb->aio_offset); - } while (len == -1 && errno == EINTR); + len = RETRY_ON_EINTR( + (aiocb->aio_type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) ? + qemu_pwritev(aiocb->aio_fildes, + aiocb->io.iov, + aiocb->io.niov, + aiocb->aio_offset) : + qemu_preadv(aiocb->aio_fildes, + aiocb->io.iov, + aiocb->io.niov, + aiocb->aio_offset) + ); if (len == -1) { return -errno; @@ -1503,7 +1715,7 @@ static ssize_t handle_aiocb_rw_linear(RawPosixAIOData *aiocb, char *buf) ssize_t len; while (offset < aiocb->aio_nbytes) { - if (aiocb->aio_type & QEMU_AIO_WRITE) { + if (aiocb->aio_type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) { len = pwrite(aiocb->aio_fildes, (const char *)buf + offset, aiocb->aio_nbytes - offset, @@ -1596,7 +1808,7 @@ static int handle_aiocb_rw(void *opaque) } nbytes = handle_aiocb_rw_linear(aiocb, buf); - if (!(aiocb->aio_type & QEMU_AIO_WRITE)) { + if (!(aiocb->aio_type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND))) { char *p = buf; size_t count = aiocb->aio_nbytes, copy; int i; @@ -1749,7 +1961,7 @@ static int handle_aiocb_write_zeroes(void *opaque) #ifdef CONFIG_FALLOCATE /* Last resort: we are trying to extend the file with zeroed data. This * can be done via fallocate(fd, 0) */ - len = bdrv_getlength(aiocb->bs); + len = raw_getlength(aiocb->bs); if (s->has_fallocate && len >= 0 && aiocb->aio_offset >= len) { int ret = do_fallocate(s->fd, 0, aiocb->aio_offset, aiocb->aio_nbytes); if (ret == 0 || ret != -ENOTSUP) { @@ -1801,6 +2013,147 @@ static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, } #endif +/* + * parse_zone - Fill a zone descriptor + */ +#if defined(CONFIG_BLKZONED) +static inline int parse_zone(struct BlockZoneDescriptor *zone, + const struct blk_zone *blkz) { + zone->start = blkz->start << BDRV_SECTOR_BITS; + zone->length = blkz->len << BDRV_SECTOR_BITS; + zone->wp = blkz->wp << BDRV_SECTOR_BITS; + +#ifdef HAVE_BLK_ZONE_REP_CAPACITY + zone->cap = blkz->capacity << BDRV_SECTOR_BITS; +#else + zone->cap = blkz->len << BDRV_SECTOR_BITS; +#endif + + switch (blkz->type) { + case BLK_ZONE_TYPE_SEQWRITE_REQ: + zone->type = BLK_ZT_SWR; + break; + case BLK_ZONE_TYPE_SEQWRITE_PREF: + zone->type = BLK_ZT_SWP; + break; + case BLK_ZONE_TYPE_CONVENTIONAL: + zone->type = BLK_ZT_CONV; + break; + default: + error_report("Unsupported zone type: 0x%x", blkz->type); + return -ENOTSUP; + } + + switch (blkz->cond) { + case BLK_ZONE_COND_NOT_WP: + zone->state = BLK_ZS_NOT_WP; + break; + case BLK_ZONE_COND_EMPTY: + zone->state = BLK_ZS_EMPTY; + break; + case BLK_ZONE_COND_IMP_OPEN: + zone->state = BLK_ZS_IOPEN; + break; + case BLK_ZONE_COND_EXP_OPEN: + zone->state = BLK_ZS_EOPEN; + break; + case BLK_ZONE_COND_CLOSED: + zone->state = BLK_ZS_CLOSED; + break; + case BLK_ZONE_COND_READONLY: + zone->state = BLK_ZS_RDONLY; + break; + case BLK_ZONE_COND_FULL: + zone->state = BLK_ZS_FULL; + break; + case BLK_ZONE_COND_OFFLINE: + zone->state = BLK_ZS_OFFLINE; + break; + default: + error_report("Unsupported zone state: 0x%x", blkz->cond); + return -ENOTSUP; + } + return 0; +} +#endif + +#if defined(CONFIG_BLKZONED) +static int handle_aiocb_zone_report(void *opaque) +{ + RawPosixAIOData *aiocb = opaque; + int fd = aiocb->aio_fildes; + unsigned int *nr_zones = aiocb->zone_report.nr_zones; + BlockZoneDescriptor *zones = aiocb->zone_report.zones; + /* zoned block devices use 512-byte sectors */ + uint64_t sector = aiocb->aio_offset / 512; + + struct blk_zone *blkz; + size_t rep_size; + unsigned int nrz; + int ret; + unsigned int n = 0, i = 0; + + nrz = *nr_zones; + rep_size = sizeof(struct blk_zone_report) + nrz * sizeof(struct blk_zone); + g_autofree struct blk_zone_report *rep = NULL; + rep = g_malloc(rep_size); + + blkz = (struct blk_zone *)(rep + 1); + while (n < nrz) { + memset(rep, 0, rep_size); + rep->sector = sector; + rep->nr_zones = nrz - n; + + do { + ret = ioctl(fd, BLKREPORTZONE, rep); + } while (ret != 0 && errno == EINTR); + if (ret != 0) { + error_report("%d: ioctl BLKREPORTZONE at %" PRId64 " failed %d", + fd, sector, errno); + return -errno; + } + + if (!rep->nr_zones) { + break; + } + + for (i = 0; i < rep->nr_zones; i++, n++) { + ret = parse_zone(&zones[n], &blkz[i]); + if (ret != 0) { + return ret; + } + + /* The next report should start after the last zone reported */ + sector = blkz[i].start + blkz[i].len; + } + } + + *nr_zones = n; + return 0; +} +#endif + +#if defined(CONFIG_BLKZONED) +static int handle_aiocb_zone_mgmt(void *opaque) +{ + RawPosixAIOData *aiocb = opaque; + int fd = aiocb->aio_fildes; + uint64_t sector = aiocb->aio_offset / 512; + int64_t nr_sectors = aiocb->aio_nbytes / 512; + struct blk_zone_range range; + int ret; + + /* Execute the operation */ + range.sector = sector; + range.nr_sectors = nr_sectors; + do { + ret = ioctl(fd, aiocb->zone_mgmt.op, &range); + } while (ret != 0 && errno == EINTR); + + return ret < 0 ? -errno : ret; +} +#endif + static int handle_aiocb_copy_range(void *opaque) { RawPosixAIOData *aiocb = opaque; @@ -1908,9 +2261,7 @@ static int allocate_first_block(int fd, size_t max_size) buf = qemu_memalign(max_align, write_size); memset(buf, 0, write_size); - do { - n = pwrite(fd, buf, write_size, 0); - } while (n == -1 && errno == EINTR); + n = RETRY_ON_EINTR(pwrite(fd, buf, write_size, 0)); ret = (n == -1) ? -errno : 0; @@ -2053,22 +2404,95 @@ out: return result; } -static int coroutine_fn raw_thread_pool_submit(BlockDriverState *bs, - ThreadPoolFunc func, void *arg) +static int coroutine_fn raw_thread_pool_submit(ThreadPoolFunc func, void *arg) { - /* @bs can be NULL, bdrv_get_aio_context() returns the main context then */ - ThreadPool *pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); - return thread_pool_submit_co(pool, func, arg); + return thread_pool_submit_co(func, arg); } -static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, +/* + * Check if all memory in this vector is sector aligned. + */ +static bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) +{ + int i; + size_t alignment = bdrv_min_mem_align(bs); + size_t len = bs->bl.request_alignment; + IO_CODE(); + + for (i = 0; i < qiov->niov; i++) { + if ((uintptr_t) qiov->iov[i].iov_base % alignment) { + return false; + } + if (qiov->iov[i].iov_len % len) { + return false; + } + } + + return true; +} + +#ifdef CONFIG_LINUX_IO_URING +static inline bool raw_check_linux_io_uring(BDRVRawState *s) +{ + Error *local_err = NULL; + AioContext *ctx; + + if (!s->use_linux_io_uring) { + return false; + } + + ctx = qemu_get_current_aio_context(); + if (unlikely(!aio_setup_linux_io_uring(ctx, &local_err))) { + error_reportf_err(local_err, "Unable to use linux io_uring, " + "falling back to thread pool: "); + s->use_linux_io_uring = false; + return false; + } + return true; +} +#endif + +#ifdef CONFIG_LINUX_AIO +static inline bool raw_check_linux_aio(BDRVRawState *s) +{ + Error *local_err = NULL; + AioContext *ctx; + + if (!s->use_linux_aio) { + return false; + } + + ctx = qemu_get_current_aio_context(); + if (unlikely(!aio_setup_linux_aio(ctx, &local_err))) { + error_reportf_err(local_err, "Unable to use Linux AIO, " + "falling back to thread pool: "); + s->use_linux_aio = false; + return false; + } + return true; +} +#endif + +static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr, uint64_t bytes, QEMUIOVector *qiov, int type) { BDRVRawState *s = bs->opaque; RawPosixAIOData acb; + int ret; + uint64_t offset = *offset_ptr; if (fd_open(bs) < 0) return -EIO; +#if defined(CONFIG_BLKZONED) + if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) && + bs->bl.zoned != BLK_Z_NONE) { + qemu_co_mutex_lock(&bs->wps->colock); + if (type & QEMU_AIO_ZONE_APPEND) { + int index = offset / bs->bl.zone_size; + offset = bs->wps->wp[index]; + } + } +#endif /* * When using O_DIRECT, the request must be aligned to be able to use @@ -2079,17 +2503,17 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, 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)); + } else if (raw_check_linux_io_uring(s)) { assert(qiov->size == bytes); - return luring_co_submit(bs, aio, s->fd, offset, qiov, type); + ret = luring_co_submit(bs, s->fd, offset, qiov, type); + goto out; #endif #ifdef CONFIG_LINUX_AIO - } else if (s->use_linux_aio) { - LinuxAioState *aio = aio_get_linux_aio(bdrv_get_aio_context(bs)); + } else if (raw_check_linux_aio(s)) { assert(qiov->size == bytes); - return laio_co_submit(bs, aio, s->fd, offset, qiov, type, + ret = laio_co_submit(s->fd, offset, qiov, type, s->aio_max_batch); + goto out; #endif } @@ -2106,59 +2530,55 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, }; assert(qiov->size == bytes); - return raw_thread_pool_submit(bs, handle_aiocb_rw, &acb); + ret = raw_thread_pool_submit(handle_aiocb_rw, &acb); + goto out; /* Avoid the compiler err of unused label */ + +out: +#if defined(CONFIG_BLKZONED) + if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) && + bs->bl.zoned != BLK_Z_NONE) { + BlockZoneWps *wps = bs->wps; + if (ret == 0) { + uint64_t *wp = &wps->wp[offset / bs->bl.zone_size]; + if (!BDRV_ZT_IS_CONV(*wp)) { + if (type & QEMU_AIO_ZONE_APPEND) { + *offset_ptr = *wp; + trace_zbd_zone_append_complete(bs, *offset_ptr + >> BDRV_SECTOR_BITS); + } + /* Advance the wp if needed */ + if (offset + bytes > *wp) { + *wp = offset + bytes; + } + } + } else { + /* + * write and append write are not allowed to cross zone boundaries + */ + update_zones_wp(bs, s->fd, offset, 1); + } + + qemu_co_mutex_unlock(&wps->colock); + } +#endif + return ret; } static int coroutine_fn raw_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { - return raw_co_prw(bs, offset, bytes, qiov, QEMU_AIO_READ); + return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_READ); } static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { - assert(flags == 0); - return raw_co_prw(bs, offset, bytes, qiov, QEMU_AIO_WRITE); + return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_WRITE); } -static void raw_aio_plug(BlockDriverState *bs) -{ - BDRVRawState __attribute__((unused)) *s = bs->opaque; -#ifdef CONFIG_LINUX_AIO - 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 - if (s->use_linux_aio) { - LinuxAioState *aio = aio_get_linux_aio(bdrv_get_aio_context(bs)); - laio_io_unplug(bs, aio, s->aio_max_batch); - } -#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) +static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; RawPosixAIOData acb; @@ -2176,38 +2596,11 @@ static int raw_co_flush_to_disk(BlockDriverState *bs) }; #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 - if (s->use_linux_aio) { - Error *local_err = NULL; - if (!aio_setup_linux_aio(new_context, &local_err)) { - error_reportf_err(local_err, "Unable to use native AIO, " - "falling back to thread pool: "); - s->use_linux_aio = false; - } - } -#endif -#ifdef CONFIG_LINUX_IO_URING - if (s->use_linux_io_uring) { - Error *local_err = NULL; - 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; - } + if (raw_check_linux_io_uring(s)) { + return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH); } #endif + return raw_thread_pool_submit(handle_aiocb_flush, &acb); } static void raw_close(BlockDriverState *bs) @@ -2215,6 +2608,9 @@ static void raw_close(BlockDriverState *bs) BDRVRawState *s = bs->opaque; if (s->fd >= 0) { +#if defined(CONFIG_BLKZONED) + g_free(bs->wps); +#endif qemu_close(s->fd); s->fd = -1; } @@ -2243,7 +2639,7 @@ raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset, }, }; - return raw_thread_pool_submit(bs, handle_aiocb_truncate, &acb); + return raw_thread_pool_submit(handle_aiocb_truncate, &acb); } static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, @@ -2456,7 +2852,12 @@ static int64_t raw_getlength(BlockDriverState *bs) } #endif -static int64_t raw_get_allocated_file_size(BlockDriverState *bs) +static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs) +{ + return raw_getlength(bs); +} + +static int64_t coroutine_fn raw_co_get_allocated_file_size(BlockDriverState *bs) { struct stat st; BDRVRawState *s = bs->opaque; @@ -2599,10 +3000,9 @@ out: return result; } -static int coroutine_fn raw_co_create_opts(BlockDriver *drv, - const char *filename, - QemuOpts *opts, - Error **errp) +static int coroutine_fn GRAPH_RDLOCK +raw_co_create_opts(BlockDriver *drv, const char *filename, + QemuOpts *opts, Error **errp) { BlockdevCreateOptions options; int64_t total_size = 0; @@ -2912,8 +3312,8 @@ static void check_cache_dropped(BlockDriverState *bs, Error **errp) } #endif /* __linux__ */ -static void coroutine_fn raw_co_invalidate_cache(BlockDriverState *bs, - Error **errp) +static void coroutine_fn GRAPH_RDLOCK +raw_co_invalidate_cache(BlockDriverState *bs, Error **errp) { BDRVRawState *s = bs->opaque; int ret; @@ -2973,6 +3373,169 @@ static void raw_account_discard(BDRVRawState *s, uint64_t nbytes, int ret) } } +/* + * zone report - Get a zone block device's information in the form + * of an array of zone descriptors. + * zones is an array of zone descriptors to hold zone information on reply; + * offset can be any byte within the entire size of the device; + * nr_zones is the maximum number of sectors the command should operate on. + */ +#if defined(CONFIG_BLKZONED) +static int coroutine_fn raw_co_zone_report(BlockDriverState *bs, int64_t offset, + unsigned int *nr_zones, + BlockZoneDescriptor *zones) { + BDRVRawState *s = bs->opaque; + RawPosixAIOData acb = (RawPosixAIOData) { + .bs = bs, + .aio_fildes = s->fd, + .aio_type = QEMU_AIO_ZONE_REPORT, + .aio_offset = offset, + .zone_report = { + .nr_zones = nr_zones, + .zones = zones, + }, + }; + + trace_zbd_zone_report(bs, *nr_zones, offset >> BDRV_SECTOR_BITS); + return raw_thread_pool_submit(handle_aiocb_zone_report, &acb); +} +#endif + +/* + * zone management operations - Execute an operation on a zone + */ +#if defined(CONFIG_BLKZONED) +static int coroutine_fn raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, + int64_t offset, int64_t len) { + BDRVRawState *s = bs->opaque; + RawPosixAIOData acb; + int64_t zone_size, zone_size_mask; + const char *op_name; + unsigned long zo; + int ret; + BlockZoneWps *wps = bs->wps; + int64_t capacity = bs->total_sectors << BDRV_SECTOR_BITS; + + zone_size = bs->bl.zone_size; + zone_size_mask = zone_size - 1; + if (offset & zone_size_mask) { + error_report("sector offset %" PRId64 " is not aligned to zone size " + "%" PRId64 "", offset / 512, zone_size / 512); + return -EINVAL; + } + + if (((offset + len) < capacity && len & zone_size_mask) || + offset + len > capacity) { + error_report("number of sectors %" PRId64 " is not aligned to zone size" + " %" PRId64 "", len / 512, zone_size / 512); + return -EINVAL; + } + + uint32_t i = offset / bs->bl.zone_size; + uint32_t nrz = len / bs->bl.zone_size; + uint64_t *wp = &wps->wp[i]; + if (BDRV_ZT_IS_CONV(*wp) && len != capacity) { + error_report("zone mgmt operations are not allowed for conventional zones"); + return -EIO; + } + + switch (op) { + case BLK_ZO_OPEN: + op_name = "BLKOPENZONE"; + zo = BLKOPENZONE; + break; + case BLK_ZO_CLOSE: + op_name = "BLKCLOSEZONE"; + zo = BLKCLOSEZONE; + break; + case BLK_ZO_FINISH: + op_name = "BLKFINISHZONE"; + zo = BLKFINISHZONE; + break; + case BLK_ZO_RESET: + op_name = "BLKRESETZONE"; + zo = BLKRESETZONE; + break; + default: + error_report("Unsupported zone op: 0x%x", op); + return -ENOTSUP; + } + + acb = (RawPosixAIOData) { + .bs = bs, + .aio_fildes = s->fd, + .aio_type = QEMU_AIO_ZONE_MGMT, + .aio_offset = offset, + .aio_nbytes = len, + .zone_mgmt = { + .op = zo, + }, + }; + + trace_zbd_zone_mgmt(bs, op_name, offset >> BDRV_SECTOR_BITS, + len >> BDRV_SECTOR_BITS); + ret = raw_thread_pool_submit(handle_aiocb_zone_mgmt, &acb); + if (ret != 0) { + update_zones_wp(bs, s->fd, offset, nrz); + error_report("ioctl %s failed %d", op_name, ret); + return ret; + } + + if (zo == BLKRESETZONE && len == capacity) { + ret = get_zones_wp(bs, s->fd, 0, bs->bl.nr_zones, 1); + if (ret < 0) { + error_report("reporting single wp failed"); + return ret; + } + } else if (zo == BLKRESETZONE) { + for (unsigned int j = 0; j < nrz; ++j) { + wp[j] = offset + j * zone_size; + } + } else if (zo == BLKFINISHZONE) { + for (unsigned int j = 0; j < nrz; ++j) { + /* The zoned device allows the last zone smaller that the + * zone size. */ + wp[j] = MIN(offset + (j + 1) * zone_size, offset + len); + } + } + + return ret; +} +#endif + +#if defined(CONFIG_BLKZONED) +static int coroutine_fn raw_co_zone_append(BlockDriverState *bs, + int64_t *offset, + QEMUIOVector *qiov, + BdrvRequestFlags flags) { + assert(flags == 0); + int64_t zone_size_mask = bs->bl.zone_size - 1; + int64_t iov_len = 0; + int64_t len = 0; + + if (*offset & zone_size_mask) { + error_report("sector offset %" PRId64 " is not aligned to zone size " + "%" PRId32 "", *offset / 512, bs->bl.zone_size / 512); + return -EINVAL; + } + + int64_t wg = bs->bl.write_granularity; + int64_t wg_mask = wg - 1; + for (int i = 0; i < qiov->niov; i++) { + iov_len = qiov->iov[i].iov_len; + if (iov_len & wg_mask) { + error_report("len of IOVector[%d] %" PRId64 " is not aligned to " + "block size %" PRId64 "", i, iov_len, wg); + return -EINVAL; + } + len += iov_len; + } + + trace_zbd_zone_append(bs, *offset >> BDRV_SECTOR_BITS); + return raw_co_prw(bs, offset, len, qiov, QEMU_AIO_ZONE_APPEND); +} +#endif + static coroutine_fn int raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes, bool blkdev) @@ -2993,7 +3556,7 @@ raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes, acb.aio_type |= QEMU_AIO_BLKDEV; } - ret = raw_thread_pool_submit(bs, handle_aiocb_discard, &acb); + ret = raw_thread_pool_submit(handle_aiocb_discard, &acb); raw_account_discard(s, bytes, ret); return ret; } @@ -3068,7 +3631,7 @@ raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, handler = handle_aiocb_write_zeroes; } - return raw_thread_pool_submit(bs, handler, &acb); + return raw_thread_pool_submit(handler, &acb); } static int coroutine_fn raw_co_pwrite_zeroes( @@ -3078,11 +3641,40 @@ static int coroutine_fn raw_co_pwrite_zeroes( return raw_do_pwrite_zeroes(bs, offset, bytes, flags, false); } -static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +raw_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { return 0; } +static ImageInfoSpecific *raw_get_specific_info(BlockDriverState *bs, + Error **errp) +{ + ImageInfoSpecificFile *file_info = g_new0(ImageInfoSpecificFile, 1); + ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1); + + *spec_info = (ImageInfoSpecific){ + .type = IMAGE_INFO_SPECIFIC_KIND_FILE, + .u.file.data = file_info, + }; + +#ifdef FS_IOC_FSGETXATTR + { + BDRVRawState *s = bs->opaque; + struct fsxattr attr; + int ret; + + ret = ioctl(s->fd, FS_IOC_FSGETXATTR, &attr); + if (!ret && attr.fsx_extsize != 0) { + file_info->has_extent_size_hint = true; + file_info->extent_size_hint = attr.fsx_extsize; + } + } +#endif + + return spec_info; +} + static BlockStatsSpecificFile get_blockstats_specific_file(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; @@ -3235,7 +3827,7 @@ static void raw_abort_perm_update(BlockDriverState *bs) raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL); } -static int coroutine_fn raw_co_copy_range_from( +static int coroutine_fn GRAPH_RDLOCK raw_co_copy_range_from( BlockDriverState *bs, BdrvChild *src, int64_t src_offset, BdrvChild *dst, int64_t dst_offset, int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) @@ -3244,14 +3836,12 @@ static int coroutine_fn raw_co_copy_range_from( read_flags, write_flags); } -static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, - BdrvChild *src, - int64_t src_offset, - BdrvChild *dst, - int64_t dst_offset, - int64_t bytes, - BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags) +static int coroutine_fn GRAPH_RDLOCK +raw_co_copy_range_to(BlockDriverState *bs, + BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { RawPosixAIOData acb; BDRVRawState *s = bs->opaque; @@ -3279,7 +3869,7 @@ static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, }, }; - return raw_thread_pool_submit(bs, handle_aiocb_copy_range, &acb); + return raw_thread_pool_submit(handle_aiocb_copy_range, &acb); } BlockDriver bdrv_file = { @@ -3309,15 +3899,12 @@ BlockDriver bdrv_file = { .bdrv_co_copy_range_from = raw_co_copy_range_from, .bdrv_co_copy_range_to = raw_co_copy_range_to, .bdrv_refresh_limits = raw_refresh_limits, - .bdrv_io_plug = raw_aio_plug, - .bdrv_io_unplug = raw_aio_unplug, - .bdrv_attach_aio_context = raw_aio_attach_aio_context, - .bdrv_co_truncate = raw_co_truncate, - .bdrv_getlength = raw_getlength, - .bdrv_get_info = raw_get_info, - .bdrv_get_allocated_file_size - = raw_get_allocated_file_size, + .bdrv_co_truncate = raw_co_truncate, + .bdrv_co_getlength = raw_co_getlength, + .bdrv_co_get_info = raw_co_get_info, + .bdrv_get_specific_info = raw_get_specific_info, + .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, .bdrv_get_specific_stats = raw_get_specific_stats, .bdrv_check_perm = raw_check_perm, .bdrv_set_perm = raw_set_perm, @@ -3609,7 +4196,7 @@ hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) struct sg_io_hdr *io_hdr = buf; if (io_hdr->cmdp[0] == PERSISTENT_RESERVE_OUT || io_hdr->cmdp[0] == PERSISTENT_RESERVE_IN) { - return pr_manager_execute(s->pr_mgr, bdrv_get_aio_context(bs), + return pr_manager_execute(s->pr_mgr, qemu_get_current_aio_context(), s->fd, io_hdr); } } @@ -3625,7 +4212,7 @@ hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) }, }; - return raw_thread_pool_submit(bs, handle_aiocb_ioctl, &acb); + return raw_thread_pool_submit(handle_aiocb_ioctl, &acb); } #endif /* linux */ @@ -3681,15 +4268,12 @@ static BlockDriver bdrv_host_device = { .bdrv_co_copy_range_from = raw_co_copy_range_from, .bdrv_co_copy_range_to = raw_co_copy_range_to, .bdrv_refresh_limits = raw_refresh_limits, - .bdrv_io_plug = raw_aio_plug, - .bdrv_io_unplug = raw_aio_unplug, - .bdrv_attach_aio_context = raw_aio_attach_aio_context, - .bdrv_co_truncate = raw_co_truncate, - .bdrv_getlength = raw_getlength, - .bdrv_get_info = raw_get_info, - .bdrv_get_allocated_file_size - = raw_get_allocated_file_size, + .bdrv_co_truncate = raw_co_truncate, + .bdrv_co_getlength = raw_co_getlength, + .bdrv_co_get_info = raw_co_get_info, + .bdrv_get_specific_info = raw_get_specific_info, + .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, .bdrv_get_specific_stats = hdev_get_specific_stats, .bdrv_check_perm = raw_check_perm, .bdrv_set_perm = raw_set_perm, @@ -3701,6 +4285,14 @@ static BlockDriver bdrv_host_device = { #ifdef __linux__ .bdrv_co_ioctl = hdev_co_ioctl, #endif + + /* zoned device */ +#if defined(CONFIG_BLKZONED) + /* zone management operations */ + .bdrv_co_zone_report = raw_co_zone_report, + .bdrv_co_zone_mgmt = raw_co_zone_mgmt, + .bdrv_co_zone_append = raw_co_zone_append, +#endif }; #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) @@ -3709,6 +4301,12 @@ static void cdrom_parse_filename(const char *filename, QDict *options, { bdrv_parse_filename_strip_prefix(filename, "host_cdrom:", options); } + +static void cdrom_refresh_limits(BlockDriverState *bs, Error **errp) +{ + bs->bl.has_variable_length = true; + raw_refresh_limits(bs, errp); +} #endif #ifdef __linux__ @@ -3749,7 +4347,7 @@ out: return prio; } -static bool cdrom_is_inserted(BlockDriverState *bs) +static bool coroutine_fn cdrom_co_is_inserted(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; int ret; @@ -3758,7 +4356,7 @@ static bool cdrom_is_inserted(BlockDriverState *bs) return ret == CDS_DISC_OK; } -static void cdrom_eject(BlockDriverState *bs, bool eject_flag) +static void coroutine_fn cdrom_co_eject(BlockDriverState *bs, bool eject_flag) { BDRVRawState *s = bs->opaque; @@ -3771,7 +4369,7 @@ static void cdrom_eject(BlockDriverState *bs, bool eject_flag) } } -static void cdrom_lock_medium(BlockDriverState *bs, bool locked) +static void coroutine_fn cdrom_co_lock_medium(BlockDriverState *bs, bool locked) { BDRVRawState *s = bs->opaque; @@ -3804,21 +4402,16 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_co_preadv = raw_co_preadv, .bdrv_co_pwritev = raw_co_pwritev, .bdrv_co_flush_to_disk = raw_co_flush_to_disk, - .bdrv_refresh_limits = raw_refresh_limits, - .bdrv_io_plug = raw_aio_plug, - .bdrv_io_unplug = raw_aio_unplug, - .bdrv_attach_aio_context = raw_aio_attach_aio_context, + .bdrv_refresh_limits = cdrom_refresh_limits, - .bdrv_co_truncate = raw_co_truncate, - .bdrv_getlength = raw_getlength, - .has_variable_length = true, - .bdrv_get_allocated_file_size - = raw_get_allocated_file_size, + .bdrv_co_truncate = raw_co_truncate, + .bdrv_co_getlength = raw_co_getlength, + .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, /* removable device support */ - .bdrv_is_inserted = cdrom_is_inserted, - .bdrv_eject = cdrom_eject, - .bdrv_lock_medium = cdrom_lock_medium, + .bdrv_co_is_inserted = cdrom_co_is_inserted, + .bdrv_co_eject = cdrom_co_eject, + .bdrv_co_lock_medium = cdrom_co_lock_medium, /* generic scsi device */ .bdrv_co_ioctl = hdev_co_ioctl, @@ -3875,12 +4468,12 @@ static int cdrom_reopen(BlockDriverState *bs) return 0; } -static bool cdrom_is_inserted(BlockDriverState *bs) +static bool coroutine_fn cdrom_co_is_inserted(BlockDriverState *bs) { return raw_getlength(bs) > 0; } -static void cdrom_eject(BlockDriverState *bs, bool eject_flag) +static void coroutine_fn cdrom_co_eject(BlockDriverState *bs, bool eject_flag) { BDRVRawState *s = bs->opaque; @@ -3900,7 +4493,7 @@ static void cdrom_eject(BlockDriverState *bs, bool eject_flag) cdrom_reopen(bs); } -static void cdrom_lock_medium(BlockDriverState *bs, bool locked) +static void coroutine_fn cdrom_co_lock_medium(BlockDriverState *bs, bool locked) { BDRVRawState *s = bs->opaque; @@ -3934,21 +4527,16 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_co_preadv = raw_co_preadv, .bdrv_co_pwritev = raw_co_pwritev, .bdrv_co_flush_to_disk = raw_co_flush_to_disk, - .bdrv_refresh_limits = raw_refresh_limits, - .bdrv_io_plug = raw_aio_plug, - .bdrv_io_unplug = raw_aio_unplug, - .bdrv_attach_aio_context = raw_aio_attach_aio_context, + .bdrv_refresh_limits = cdrom_refresh_limits, - .bdrv_co_truncate = raw_co_truncate, - .bdrv_getlength = raw_getlength, - .has_variable_length = true, - .bdrv_get_allocated_file_size - = raw_get_allocated_file_size, + .bdrv_co_truncate = raw_co_truncate, + .bdrv_co_getlength = raw_co_getlength, + .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, /* removable device support */ - .bdrv_is_inserted = cdrom_is_inserted, - .bdrv_eject = cdrom_eject, - .bdrv_lock_medium = cdrom_lock_medium, + .bdrv_co_is_inserted = cdrom_co_is_inserted, + .bdrv_co_eject = cdrom_co_eject, + .bdrv_co_lock_medium = cdrom_co_lock_medium, }; #endif /* __FreeBSD__ */ diff --git a/block/file-win32.c b/block/file-win32.c index ec9d64d0e4..48b790d917 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/cutils.h" +#include "block/block-io.h" #include "block/block_int.h" #include "qemu/module.h" #include "qemu/option.h" @@ -152,7 +153,6 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile, BlockCompletionFunc *cb, void *opaque, int type) { RawWin32AIOData *acb = g_new(RawWin32AIOData, 1); - ThreadPool *pool; acb->bs = bs; acb->hfile = hfile; @@ -167,8 +167,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile, acb->aio_offset = offset; trace_file_paio_submit(acb, opaque, offset, count, type); - pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); - return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); + return thread_pool_submit_aio(aio_worker, acb, cb, opaque); } int qemu_ftruncate64(int fd, int64_t length) @@ -525,7 +524,7 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, return 0; } -static int64_t raw_getlength(BlockDriverState *bs) +static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; LARGE_INTEGER l; @@ -558,7 +557,7 @@ static int64_t raw_getlength(BlockDriverState *bs) return l.QuadPart; } -static int64_t raw_get_allocated_file_size(BlockDriverState *bs) +static int64_t coroutine_fn raw_co_get_allocated_file_size(BlockDriverState *bs) { typedef DWORD (WINAPI * get_compressed_t)(const char *filename, DWORD * high); @@ -612,10 +611,9 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) return 0; } -static int coroutine_fn raw_co_create_opts(BlockDriver *drv, - const char *filename, - QemuOpts *opts, - Error **errp) +static int coroutine_fn GRAPH_RDLOCK +raw_co_create_opts(BlockDriver *drv, const char *filename, + QemuOpts *opts, Error **errp) { BlockdevCreateOptions options; int64_t total_size = 0; @@ -763,9 +761,9 @@ BlockDriver bdrv_file = { .bdrv_aio_flush = raw_aio_flush, .bdrv_co_truncate = raw_co_truncate, - .bdrv_getlength = raw_getlength, - .bdrv_get_allocated_file_size - = raw_get_allocated_file_size, + .bdrv_co_getlength = raw_co_getlength, + .bdrv_co_get_allocated_file_size + = raw_co_get_allocated_file_size, .create_opts = &raw_create_opts, }; @@ -838,6 +836,7 @@ static void hdev_refresh_limits(BlockDriverState *bs, Error **errp) { /* XXX Does Windows support AIO on less than 512-byte alignment? */ bs->bl.request_alignment = 512; + bs->bl.has_variable_length = true; } static int hdev_open(BlockDriverState *bs, QDict *options, int flags, @@ -932,11 +931,8 @@ static BlockDriver bdrv_host_device = { .bdrv_detach_aio_context = raw_detach_aio_context, .bdrv_attach_aio_context = raw_attach_aio_context, - .bdrv_getlength = raw_getlength, - .has_variable_length = true, - - .bdrv_get_allocated_file_size - = raw_get_allocated_file_size, + .bdrv_co_getlength = raw_co_getlength, + .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, }; static void bdrv_file_init(void) diff --git a/block/filter-compress.c b/block/filter-compress.c index d5be538619..9b68a2be64 100644 --- a/block/filter-compress.c +++ b/block/filter-compress.c @@ -22,6 +22,7 @@ */ #include "qemu/osdep.h" +#include "block/block-io.h" #include "block/block_int.h" #include "qemu/module.h" #include "qapi/error.h" @@ -30,13 +31,13 @@ static int compress_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, errp); - if (!bs->file) { - return -EINVAL; + int ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!bs->file->bs->drv || !block_driver_can_compress(bs->file->bs->drv)) { error_setg(errp, "Compression is not supported for underlying format: %s", @@ -56,51 +57,50 @@ static int compress_open(BlockDriverState *bs, QDict *options, int flags, } -static int64_t compress_getlength(BlockDriverState *bs) +static int64_t coroutine_fn GRAPH_RDLOCK +compress_co_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + return bdrv_co_getlength(bs->file->bs); } -static int coroutine_fn compress_co_preadv_part(BlockDriverState *bs, - int64_t offset, int64_t bytes, - QEMUIOVector *qiov, - size_t qiov_offset, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +compress_co_preadv_part(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) { return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset, flags); } -static int coroutine_fn compress_co_pwritev_part(BlockDriverState *bs, - int64_t offset, - int64_t bytes, - QEMUIOVector *qiov, - size_t qiov_offset, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +compress_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags 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, int64_t bytes, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +compress_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t 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, int64_t bytes) +static int coroutine_fn GRAPH_RDLOCK +compress_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { return bdrv_co_pdiscard(bs->file, offset, bytes); } -static void compress_refresh_limits(BlockDriverState *bs, Error **errp) +static void GRAPH_RDLOCK +compress_refresh_limits(BlockDriverState *bs, Error **errp) { BlockDriverInfo bdi; int ret; @@ -118,15 +118,17 @@ static void compress_refresh_limits(BlockDriverState *bs, Error **errp) } -static void compress_eject(BlockDriverState *bs, bool eject_flag) +static void coroutine_fn GRAPH_RDLOCK +compress_co_eject(BlockDriverState *bs, bool eject_flag) { - bdrv_eject(bs->file->bs, eject_flag); + bdrv_co_eject(bs->file->bs, eject_flag); } -static void compress_lock_medium(BlockDriverState *bs, bool locked) +static void coroutine_fn GRAPH_RDLOCK +compress_co_lock_medium(BlockDriverState *bs, bool locked) { - bdrv_lock_medium(bs->file->bs, locked); + bdrv_co_lock_medium(bs->file->bs, locked); } @@ -136,7 +138,7 @@ static BlockDriver bdrv_compress = { .bdrv_open = compress_open, .bdrv_child_perm = bdrv_default_perms, - .bdrv_getlength = compress_getlength, + .bdrv_co_getlength = compress_co_getlength, .bdrv_co_preadv_part = compress_co_preadv_part, .bdrv_co_pwritev_part = compress_co_pwritev_part, @@ -144,10 +146,9 @@ static BlockDriver bdrv_compress = { .bdrv_co_pdiscard = compress_co_pdiscard, .bdrv_refresh_limits = compress_refresh_limits, - .bdrv_eject = compress_eject, - .bdrv_lock_medium = compress_lock_medium, + .bdrv_co_eject = compress_co_eject, + .bdrv_co_lock_medium = compress_co_lock_medium, - .has_variable_length = true, .is_filter = true, }; diff --git a/block/gluster.c b/block/gluster.c index 398976bc66..cc74af06dc 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include +#include "block/block-io.h" #include "block/block_int.h" #include "block/qdict.h" #include "qapi/error.h" @@ -423,7 +424,7 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf, int ret; int old_errno; SocketAddressList *server; - unsigned long long port; + uint64_t port; glfs = glfs_find_preopened(gconf->volume); if (glfs) { @@ -444,7 +445,7 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf, server->value->u.q_unix.path, 0); break; case SOCKET_ADDRESS_TYPE_INET: - if (parse_uint_full(server->value->u.inet.port, &port, 10) < 0 || + if (parse_uint_full(server->value->u.inet.port, 10, &port) < 0 || port > 65535) { error_setg(errp, "'%s' is not a valid port number", server->value->u.inet.port); @@ -830,7 +831,6 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options, s->logfile = g_strdup(logfile ? logfile : GLUSTER_LOGFILE_DEFAULT); gconf->logfile = g_strdup(s->logfile); - gconf->has_logfile = true; s->glfs = qemu_gluster_init(gconf, filename, options, errp); if (!s->glfs) { @@ -863,11 +863,13 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options, if (ret == -EACCES || ret == -EROFS) { /* Try to degrade to read-only, but if it doesn't work, still use the * normal error message. */ + bdrv_graph_rdlock_main_loop(); if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) { open_flags = (open_flags & ~O_RDWR) | O_RDONLY; s->fd = glfs_open(s->glfs, gconf->path, open_flags); ret = s->fd ? 0 : -errno; } + bdrv_graph_rdunlock_main_loop(); } s->supports_seek_data = qemu_gluster_test_seek(s->fd); @@ -891,7 +893,7 @@ out: static void qemu_gluster_refresh_limits(BlockDriverState *bs, Error **errp) { bs->bl.max_transfer = GLUSTER_MAX_TRANSFER; - bs->bl.max_pdiscard = SIZE_MAX; + bs->bl.max_pdiscard = MIN(SIZE_MAX, INT64_MAX); } static int qemu_gluster_reopen_prepare(BDRVReopenState *state, @@ -917,7 +919,6 @@ static int qemu_gluster_reopen_prepare(BDRVReopenState *state, gconf->debug = s->debug; gconf->has_debug = true; gconf->logfile = g_strdup(s->logfile); - gconf->has_logfile = true; /* * If 'state->bs->exact_filename' is empty, 'state->options' should contain @@ -1162,7 +1163,6 @@ static int coroutine_fn qemu_gluster_co_create_opts(BlockDriver *drv, if (!gconf->logfile) { gconf->logfile = g_strdup(GLUSTER_LOGFILE_DEFAULT); } - gconf->has_logfile = true; ret = qemu_gluster_parse(gconf, filename, NULL, errp); if (ret < 0) { @@ -1236,7 +1236,6 @@ static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs, QEMUIOVector *qiov, int flags) { - assert(!flags); return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1); } @@ -1321,7 +1320,7 @@ static coroutine_fn int qemu_gluster_co_pdiscard(BlockDriverState *bs, } #endif -static int64_t qemu_gluster_getlength(BlockDriverState *bs) +static int64_t coroutine_fn qemu_gluster_co_getlength(BlockDriverState *bs) { BDRVGlusterState *s = bs->opaque; int64_t ret; @@ -1334,7 +1333,8 @@ static int64_t qemu_gluster_getlength(BlockDriverState *bs) } } -static int64_t qemu_gluster_allocated_file_size(BlockDriverState *bs) +static int64_t coroutine_fn +qemu_gluster_co_get_allocated_file_size(BlockDriverState *bs) { BDRVGlusterState *s = bs->opaque; struct stat st; @@ -1513,7 +1513,7 @@ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs, * round up if necessary. */ if (!QEMU_IS_ALIGNED(*pnum, bs->bl.request_alignment)) { - int64_t file_length = qemu_gluster_getlength(bs); + int64_t file_length = qemu_gluster_co_getlength(bs); if (file_length > 0) { /* Ignore errors, this is just a safeguard */ assert(hole == file_length); @@ -1555,7 +1555,6 @@ static BlockDriver bdrv_gluster = { .format_name = "gluster", .protocol_name = "gluster", .instance_size = sizeof(BDRVGlusterState), - .bdrv_needs_filename = false, .bdrv_file_open = qemu_gluster_open, .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, .bdrv_reopen_commit = qemu_gluster_reopen_commit, @@ -1563,8 +1562,8 @@ static BlockDriver bdrv_gluster = { .bdrv_close = qemu_gluster_close, .bdrv_co_create = qemu_gluster_co_create, .bdrv_co_create_opts = qemu_gluster_co_create_opts, - .bdrv_getlength = qemu_gluster_getlength, - .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, + .bdrv_co_getlength = qemu_gluster_co_getlength, + .bdrv_co_get_allocated_file_size = qemu_gluster_co_get_allocated_file_size, .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, @@ -1585,7 +1584,6 @@ static BlockDriver bdrv_gluster_tcp = { .format_name = "gluster", .protocol_name = "gluster+tcp", .instance_size = sizeof(BDRVGlusterState), - .bdrv_needs_filename = false, .bdrv_file_open = qemu_gluster_open, .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, .bdrv_reopen_commit = qemu_gluster_reopen_commit, @@ -1593,8 +1591,8 @@ static BlockDriver bdrv_gluster_tcp = { .bdrv_close = qemu_gluster_close, .bdrv_co_create = qemu_gluster_co_create, .bdrv_co_create_opts = qemu_gluster_co_create_opts, - .bdrv_getlength = qemu_gluster_getlength, - .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, + .bdrv_co_getlength = qemu_gluster_co_getlength, + .bdrv_co_get_allocated_file_size = qemu_gluster_co_get_allocated_file_size, .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, @@ -1615,7 +1613,6 @@ static BlockDriver bdrv_gluster_unix = { .format_name = "gluster", .protocol_name = "gluster+unix", .instance_size = sizeof(BDRVGlusterState), - .bdrv_needs_filename = true, .bdrv_file_open = qemu_gluster_open, .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, .bdrv_reopen_commit = qemu_gluster_reopen_commit, @@ -1623,8 +1620,8 @@ static BlockDriver bdrv_gluster_unix = { .bdrv_close = qemu_gluster_close, .bdrv_co_create = qemu_gluster_co_create, .bdrv_co_create_opts = qemu_gluster_co_create_opts, - .bdrv_getlength = qemu_gluster_getlength, - .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, + .bdrv_co_getlength = qemu_gluster_co_getlength, + .bdrv_co_get_allocated_file_size = qemu_gluster_co_get_allocated_file_size, .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, @@ -1651,7 +1648,6 @@ static BlockDriver bdrv_gluster_rdma = { .format_name = "gluster", .protocol_name = "gluster+rdma", .instance_size = sizeof(BDRVGlusterState), - .bdrv_needs_filename = true, .bdrv_file_open = qemu_gluster_open, .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, .bdrv_reopen_commit = qemu_gluster_reopen_commit, @@ -1659,8 +1655,8 @@ static BlockDriver bdrv_gluster_rdma = { .bdrv_close = qemu_gluster_close, .bdrv_co_create = qemu_gluster_co_create, .bdrv_co_create_opts = qemu_gluster_co_create_opts, - .bdrv_getlength = qemu_gluster_getlength, - .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, + .bdrv_co_getlength = qemu_gluster_co_getlength, + .bdrv_co_get_allocated_file_size = qemu_gluster_co_get_allocated_file_size, .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, diff --git a/block/graph-lock.c b/block/graph-lock.c new file mode 100644 index 0000000000..c81162b147 --- /dev/null +++ b/block/graph-lock.c @@ -0,0 +1,281 @@ +/* + * Graph lock: rwlock to protect block layer graph manipulations (add/remove + * edges and nodes) + * + * Copyright (c) 2022 Red Hat + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "block/graph-lock.h" +#include "block/block.h" +#include "block/block_int.h" + +/* Dummy lock object to use for Thread Safety Analysis (TSA) */ +BdrvGraphLock graph_lock; + +/* Protects the list of aiocontext and orphaned_reader_count */ +static QemuMutex aio_context_list_lock; + +/* Written and read with atomic operations. */ +static int has_writer; + +/* + * A reader coroutine could move from an AioContext to another. + * If this happens, there is no problem from the point of view of + * counters. The problem is that the total count becomes + * unbalanced if one of the two AioContexts gets deleted. + * The count of readers must remain correct, so the AioContext's + * balance is transferred to this glboal variable. + * Protected by aio_context_list_lock. + */ +static uint32_t orphaned_reader_count; + +/* Queue of readers waiting for the writer to finish */ +static CoQueue reader_queue; + +struct BdrvGraphRWlock { + /* How many readers are currently reading the graph. */ + uint32_t reader_count; + + /* + * List of BdrvGraphRWlock kept in graph-lock.c + * Protected by aio_context_list_lock + */ + QTAILQ_ENTRY(BdrvGraphRWlock) next_aio; +}; + +/* + * List of BdrvGraphRWlock. This list ensures that each BdrvGraphRWlock + * can safely modify only its own counter, avoid reading/writing + * others and thus improving performances by avoiding cacheline bounces. + */ +static QTAILQ_HEAD(, BdrvGraphRWlock) aio_context_list = + QTAILQ_HEAD_INITIALIZER(aio_context_list); + +static void __attribute__((__constructor__)) bdrv_init_graph_lock(void) +{ + qemu_mutex_init(&aio_context_list_lock); + qemu_co_queue_init(&reader_queue); +} + +void register_aiocontext(AioContext *ctx) +{ + ctx->bdrv_graph = g_new0(BdrvGraphRWlock, 1); + QEMU_LOCK_GUARD(&aio_context_list_lock); + assert(ctx->bdrv_graph->reader_count == 0); + QTAILQ_INSERT_TAIL(&aio_context_list, ctx->bdrv_graph, next_aio); +} + +void unregister_aiocontext(AioContext *ctx) +{ + QEMU_LOCK_GUARD(&aio_context_list_lock); + orphaned_reader_count += ctx->bdrv_graph->reader_count; + QTAILQ_REMOVE(&aio_context_list, ctx->bdrv_graph, next_aio); + g_free(ctx->bdrv_graph); +} + +static uint32_t reader_count(void) +{ + BdrvGraphRWlock *brdv_graph; + uint32_t rd; + + QEMU_LOCK_GUARD(&aio_context_list_lock); + + /* rd can temporarily be negative, but the total will *always* be >= 0 */ + rd = orphaned_reader_count; + QTAILQ_FOREACH(brdv_graph, &aio_context_list, next_aio) { + rd += qatomic_read(&brdv_graph->reader_count); + } + + /* shouldn't overflow unless there are 2^31 readers */ + assert((int32_t)rd >= 0); + return rd; +} + +void no_coroutine_fn bdrv_graph_wrlock(void) +{ + GLOBAL_STATE_CODE(); + assert(!qatomic_read(&has_writer)); + assert(!qemu_in_coroutine()); + + /* Make sure that constantly arriving new I/O doesn't cause starvation */ + bdrv_drain_all_begin_nopoll(); + + /* + * reader_count == 0: this means writer will read has_reader as 1 + * reader_count >= 1: we don't know if writer read has_writer == 0 or 1, + * but we need to wait. + * Wait by allowing other coroutine (and possible readers) to continue. + */ + do { + /* + * has_writer must be 0 while polling, otherwise we get a deadlock if + * any callback involved during AIO_WAIT_WHILE() tries to acquire the + * reader lock. + */ + qatomic_set(&has_writer, 0); + AIO_WAIT_WHILE_UNLOCKED(NULL, reader_count() >= 1); + qatomic_set(&has_writer, 1); + + /* + * We want to only check reader_count() after has_writer = 1 is visible + * to other threads. That way no more readers can sneak in after we've + * determined reader_count() == 0. + */ + smp_mb(); + } while (reader_count() >= 1); + + bdrv_drain_all_end(); +} + +void no_coroutine_fn bdrv_graph_wrunlock(void) +{ + GLOBAL_STATE_CODE(); + assert(qatomic_read(&has_writer)); + + WITH_QEMU_LOCK_GUARD(&aio_context_list_lock) { + /* + * No need for memory barriers, this works in pair with + * the slow path of rdlock() and both take the lock. + */ + qatomic_store_release(&has_writer, 0); + + /* Wake up all coroutines that are waiting to read the graph */ + qemu_co_enter_all(&reader_queue, &aio_context_list_lock); + } + + /* + * Run any BHs that were scheduled during the wrlock section and that + * callers might expect to have finished (in particular, this is important + * for bdrv_schedule_unref()). + * + * Do this only after restarting coroutines so that nested event loops in + * BHs don't deadlock if their condition relies on the coroutine making + * progress. + */ + aio_bh_poll(qemu_get_aio_context()); +} + +void coroutine_fn bdrv_graph_co_rdlock(void) +{ + BdrvGraphRWlock *bdrv_graph; + bdrv_graph = qemu_get_current_aio_context()->bdrv_graph; + + for (;;) { + qatomic_set(&bdrv_graph->reader_count, + bdrv_graph->reader_count + 1); + /* make sure writer sees reader_count before we check has_writer */ + smp_mb(); + + /* + * has_writer == 0: this means writer will read reader_count as >= 1 + * has_writer == 1: we don't know if writer read reader_count == 0 + * or > 0, but we need to wait anyways because + * it will write. + */ + if (!qatomic_read(&has_writer)) { + break; + } + + /* + * Synchronize access with reader_count() in bdrv_graph_wrlock(). + * Case 1: + * If this critical section gets executed first, reader_count will + * decrease and the reader will go to sleep. + * Then the writer will read reader_count that does not take into + * account this reader, and if there's no other reader it will + * enter the write section. + * Case 2: + * If reader_count() critical section gets executed first, + * then writer will read reader_count >= 1. + * It will wait in AIO_WAIT_WHILE(), but once it releases the lock + * we will enter this critical section and call aio_wait_kick(). + */ + WITH_QEMU_LOCK_GUARD(&aio_context_list_lock) { + /* + * Additional check when we use the above lock to synchronize + * with bdrv_graph_wrunlock(). + * Case 1: + * If this gets executed first, has_writer is still 1, so we reduce + * reader_count and go to sleep. + * Then the writer will set has_writer to 0 and wake up all readers, + * us included. + * Case 2: + * If bdrv_graph_wrunlock() critical section gets executed first, + * then it will set has_writer to 0 and wake up all other readers. + * Then we execute this critical section, and therefore must check + * again for has_writer, otherwise we sleep without any writer + * actually running. + */ + if (!qatomic_read(&has_writer)) { + return; + } + + /* slow path where reader sleeps */ + bdrv_graph->reader_count--; + aio_wait_kick(); + qemu_co_queue_wait(&reader_queue, &aio_context_list_lock); + } + } +} + +void coroutine_fn bdrv_graph_co_rdunlock(void) +{ + BdrvGraphRWlock *bdrv_graph; + bdrv_graph = qemu_get_current_aio_context()->bdrv_graph; + + qatomic_store_release(&bdrv_graph->reader_count, + bdrv_graph->reader_count - 1); + /* make sure writer sees reader_count before we check has_writer */ + smp_mb(); + + /* + * has_writer == 0: this means reader will read reader_count decreased + * has_writer == 1: we don't know if writer read reader_count old or + * new. Therefore, kick again so on next iteration + * writer will for sure read the updated value. + */ + if (qatomic_read(&has_writer)) { + aio_wait_kick(); + } +} + +void bdrv_graph_rdlock_main_loop(void) +{ + GLOBAL_STATE_CODE(); + assert(!qemu_in_coroutine()); +} + +void bdrv_graph_rdunlock_main_loop(void) +{ + GLOBAL_STATE_CODE(); + assert(!qemu_in_coroutine()); +} + +void assert_bdrv_graph_readable(void) +{ + /* reader_count() is slow due to aio_context_list_lock lock contention */ +#ifdef CONFIG_DEBUG_GRAPH_LOCK + assert(qemu_in_main_thread() || reader_count()); +#endif +} + +void assert_bdrv_graph_writable(void) +{ + assert(qemu_in_main_thread()); + assert(qatomic_read(&has_writer)); +} diff --git a/block/io.c b/block/io.c index 789e6373d5..7217cf811b 100644 --- a/block/io.c +++ b/block/io.c @@ -30,6 +30,7 @@ #include "block/blockjob_int.h" #include "block/block_int.h" #include "block/coroutines.h" +#include "block/dirty-bitmap.h" #include "block/write-threshold.h" #include "qemu/cutils.h" #include "qemu/memalign.h" @@ -41,68 +42,72 @@ /* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */ #define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) -static void bdrv_parent_cb_resize(BlockDriverState *bs); +static void coroutine_fn GRAPH_RDLOCK +bdrv_parent_cb_resize(BlockDriverState *bs); + static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, BdrvRequestFlags flags); -static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, - bool ignore_bds_parents) +static void GRAPH_RDLOCK +bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore) { BdrvChild *c, *next; + IO_OR_GS_CODE(); + assert_bdrv_graph_readable(); QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { - if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) { + if (c == ignore) { continue; } - bdrv_parent_drained_begin_single(c, false); - } -} - -static void bdrv_parent_drained_end_single_no_poll(BdrvChild *c, - int *drained_end_counter) -{ - assert(c->parent_quiesce_counter > 0); - c->parent_quiesce_counter--; - if (c->klass->drained_end) { - c->klass->drained_end(c, drained_end_counter); + bdrv_parent_drained_begin_single(c); } } void bdrv_parent_drained_end_single(BdrvChild *c) { - int drained_end_counter = 0; - IO_OR_GS_CODE(); - bdrv_parent_drained_end_single_no_poll(c, &drained_end_counter); - BDRV_POLL_WHILE(c->bs, qatomic_read(&drained_end_counter) > 0); -} + GLOBAL_STATE_CODE(); -static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore, - bool ignore_bds_parents, - int *drained_end_counter) -{ - BdrvChild *c; + assert(c->quiesced_parent); + c->quiesced_parent = false; - QLIST_FOREACH(c, &bs->parents, next_parent) { - if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) { - continue; - } - bdrv_parent_drained_end_single_no_poll(c, drained_end_counter); + if (c->klass->drained_end) { + c->klass->drained_end(c); } } -static bool bdrv_parent_drained_poll_single(BdrvChild *c) +static void GRAPH_RDLOCK +bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) { + BdrvChild *c; + IO_OR_GS_CODE(); + assert_bdrv_graph_readable(); + + QLIST_FOREACH(c, &bs->parents, next_parent) { + if (c == ignore) { + continue; + } + bdrv_parent_drained_end_single(c); + } +} + +bool bdrv_parent_drained_poll_single(BdrvChild *c) +{ + IO_OR_GS_CODE(); + if (c->klass->drained_poll) { return c->klass->drained_poll(c); } return false; } -static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, - bool ignore_bds_parents) +static bool GRAPH_RDLOCK +bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, + bool ignore_bds_parents) { BdrvChild *c, *next; bool busy = false; + IO_OR_GS_CODE(); + assert_bdrv_graph_readable(); QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) { @@ -114,16 +119,17 @@ static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, return busy; } -void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll) +void bdrv_parent_drained_begin_single(BdrvChild *c) { - IO_OR_GS_CODE(); - c->parent_quiesce_counter++; + GLOBAL_STATE_CODE(); + + assert(!c->quiesced_parent); + c->quiesced_parent = true; + if (c->klass->drained_begin) { + /* called with rdlock taken, but it doesn't really need it. */ c->klass->drained_begin(c); } - if (poll) { - BDRV_POLL_WHILE(c->bs, bdrv_parent_drained_poll_single(c)); - } } static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) @@ -197,6 +203,10 @@ void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp) bdrv_merge_limits(&bs->bl, &c->bs->bl); have_limits = true; } + + if (c->role & BDRV_CHILD_FILTERED) { + bs->bl.has_variable_length |= c->bs->bl.has_variable_length; + } } if (!have_limits) { @@ -243,70 +253,15 @@ typedef struct { BlockDriverState *bs; bool done; bool begin; - bool recursive; bool poll; BdrvChild *parent; - bool ignore_bds_parents; - int *drained_end_counter; } BdrvCoDrainData; -static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) -{ - BdrvCoDrainData *data = opaque; - BlockDriverState *bs = data->bs; - - if (data->begin) { - bs->drv->bdrv_co_drain_begin(bs); - } else { - bs->drv->bdrv_co_drain_end(bs); - } - - /* Set data->done and decrement drained_end_counter before bdrv_wakeup() */ - qatomic_mb_set(&data->done, true); - if (!data->begin) { - qatomic_dec(data->drained_end_counter); - } - bdrv_dec_in_flight(bs); - - g_free(data); -} - -/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */ -static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, - int *drained_end_counter) -{ - BdrvCoDrainData *data; - - if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) || - (!begin && !bs->drv->bdrv_co_drain_end)) { - return; - } - - data = g_new(BdrvCoDrainData, 1); - *data = (BdrvCoDrainData) { - .bs = bs, - .done = false, - .begin = begin, - .drained_end_counter = drained_end_counter, - }; - - if (!begin) { - qatomic_inc(drained_end_counter); - } - - /* Make sure the driver callback completes during the polling phase for - * drain_begin. */ - bdrv_inc_in_flight(bs); - data->co = qemu_coroutine_create(bdrv_drain_invoke_entry, data); - aio_co_schedule(bdrv_get_aio_context(bs), data->co); -} - /* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */ -bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, - BdrvChild *ignore_parent, bool ignore_bds_parents) +bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent, + bool ignore_bds_parents) { - BdrvChild *child, *next; - IO_OR_GS_CODE(); + GLOBAL_STATE_CODE(); if (bdrv_parent_drained_poll(bs, ignore_parent, ignore_bds_parents)) { return true; @@ -316,30 +271,21 @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, return true; } - if (recursive) { - assert(!ignore_bds_parents); - QLIST_FOREACH_SAFE(child, &bs->children, next, next) { - if (bdrv_drain_poll(child->bs, recursive, child, false)) { - return true; - } - } - } - return false; } -static bool bdrv_drain_poll_top_level(BlockDriverState *bs, bool recursive, +static bool bdrv_drain_poll_top_level(BlockDriverState *bs, BdrvChild *ignore_parent) { - return bdrv_drain_poll(bs, recursive, ignore_parent, false); + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + return bdrv_drain_poll(bs, ignore_parent, false); } -static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, - BdrvChild *parent, bool ignore_bds_parents, +static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent, bool poll); -static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, - BdrvChild *parent, bool ignore_bds_parents, - int *drained_end_counter); +static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent); static void bdrv_co_drain_bh_cb(void *opaque) { @@ -348,20 +294,13 @@ static void bdrv_co_drain_bh_cb(void *opaque) BlockDriverState *bs = data->bs; if (bs) { - AioContext *ctx = bdrv_get_aio_context(bs); - aio_context_acquire(ctx); bdrv_dec_in_flight(bs); if (data->begin) { - assert(!data->drained_end_counter); - bdrv_do_drained_begin(bs, data->recursive, data->parent, - data->ignore_bds_parents, data->poll); + bdrv_do_drained_begin(bs, data->parent, data->poll); } else { assert(!data->poll); - bdrv_do_drained_end(bs, data->recursive, data->parent, - data->ignore_bds_parents, - data->drained_end_counter); + bdrv_do_drained_end(bs, data->parent); } - aio_context_release(ctx); } else { assert(data->begin); bdrv_drain_all_begin(); @@ -372,16 +311,12 @@ static void bdrv_co_drain_bh_cb(void *opaque) } static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, - bool begin, bool recursive, + bool begin, BdrvChild *parent, - bool ignore_bds_parents, - bool poll, - int *drained_end_counter) + bool poll) { BdrvCoDrainData data; Coroutine *self = qemu_coroutine_self(); - AioContext *ctx = bdrv_get_aio_context(bs); - AioContext *co_ctx = qemu_coroutine_get_aio_context(self); /* Calling bdrv_drain() from a BH ensures the current coroutine yields and * other coroutines run if they were queued by aio_co_enter(). */ @@ -392,76 +327,41 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, .bs = bs, .done = false, .begin = begin, - .recursive = recursive, .parent = parent, - .ignore_bds_parents = ignore_bds_parents, .poll = poll, - .drained_end_counter = drained_end_counter, }; if (bs) { bdrv_inc_in_flight(bs); } - /* - * Temporarily drop the lock across yield or we would get deadlocks. - * bdrv_co_drain_bh_cb() reaquires the lock as needed. - * - * When we yield below, the lock for the current context will be - * released, so if this is actually the lock that protects bs, don't drop - * it a second time. - */ - if (ctx != co_ctx) { - aio_context_release(ctx); - } - replay_bh_schedule_oneshot_event(ctx, bdrv_co_drain_bh_cb, &data); + replay_bh_schedule_oneshot_event(qemu_get_aio_context(), + bdrv_co_drain_bh_cb, &data); qemu_coroutine_yield(); /* If we are resumed from some other event (such as an aio completion or a * timer callback), it is a bug in the caller that should be fixed. */ assert(data.done); - - /* Reaquire the AioContext of bs if we dropped it */ - if (ctx != co_ctx) { - aio_context_acquire(ctx); - } } -void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, - BdrvChild *parent, bool ignore_bds_parents) -{ - IO_OR_GS_CODE(); - assert(!qemu_in_coroutine()); - - /* Stop things in parent-to-child order */ - if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) { - aio_disable_external(bdrv_get_aio_context(bs)); - } - - bdrv_parent_drained_begin(bs, parent, ignore_bds_parents); - bdrv_drain_invoke(bs, true, NULL); -} - -static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, - BdrvChild *parent, bool ignore_bds_parents, +static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent, bool poll) { - BdrvChild *child, *next; + IO_OR_GS_CODE(); if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs, true, recursive, parent, ignore_bds_parents, - poll, NULL); + bdrv_co_yield_to_drain(bs, true, parent, poll); return; } - bdrv_do_drained_begin_quiesce(bs, parent, ignore_bds_parents); + GLOBAL_STATE_CODE(); - if (recursive) { - assert(!ignore_bds_parents); - bs->recursive_quiesce_counter++; - QLIST_FOREACH_SAFE(child, &bs->children, next, next) { - bdrv_do_drained_begin(child->bs, true, child, ignore_bds_parents, - false); + /* Stop things in parent-to-child order */ + if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) { + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bdrv_parent_drained_begin(bs, parent); + if (bs->drv && bs->drv->bdrv_drain_begin) { + bs->drv->bdrv_drain_begin(bs); } } @@ -475,132 +375,57 @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, * nodes. */ if (poll) { - assert(!ignore_bds_parents); - BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, recursive, parent)); + BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent)); } } -void bdrv_drained_begin(BlockDriverState *bs) +void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent) { - IO_OR_GS_CODE(); - bdrv_do_drained_begin(bs, false, NULL, false, true); + bdrv_do_drained_begin(bs, parent, false); } -void bdrv_subtree_drained_begin(BlockDriverState *bs) +void coroutine_mixed_fn +bdrv_drained_begin(BlockDriverState *bs) { IO_OR_GS_CODE(); - bdrv_do_drained_begin(bs, true, NULL, false, true); + bdrv_do_drained_begin(bs, NULL, true); } /** * This function does not poll, nor must any of its recursively called - * functions. The *drained_end_counter pointee will be incremented - * once for every background operation scheduled, and decremented once - * the operation settles. Therefore, the pointer must remain valid - * until the pointee reaches 0. That implies that whoever sets up the - * pointee has to poll until it is 0. - * - * We use atomic operations to access *drained_end_counter, because - * (1) when called from bdrv_set_aio_context_ignore(), the subgraph of - * @bs may contain nodes in different AioContexts, - * (2) bdrv_drain_all_end() uses the same counter for all nodes, - * regardless of which AioContext they are in. + * functions. */ -static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, - BdrvChild *parent, bool ignore_bds_parents, - int *drained_end_counter) +static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent) { - BdrvChild *child; int old_quiesce_counter; - assert(drained_end_counter != NULL); + IO_OR_GS_CODE(); if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs, false, recursive, parent, ignore_bds_parents, - false, drained_end_counter); + bdrv_co_yield_to_drain(bs, false, parent, false); return; } + + /* At this point, we should be always running in the main loop. */ + GLOBAL_STATE_CODE(); assert(bs->quiesce_counter > 0); + GLOBAL_STATE_CODE(); /* Re-enable things in child-to-parent order */ - bdrv_drain_invoke(bs, false, drained_end_counter); - bdrv_parent_drained_end(bs, parent, ignore_bds_parents, - drained_end_counter); - old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter); if (old_quiesce_counter == 1) { - aio_enable_external(bdrv_get_aio_context(bs)); - } - - if (recursive) { - assert(!ignore_bds_parents); - bs->recursive_quiesce_counter--; - QLIST_FOREACH(child, &bs->children, next) { - bdrv_do_drained_end(child->bs, true, child, ignore_bds_parents, - drained_end_counter); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (bs->drv && bs->drv->bdrv_drain_end) { + bs->drv->bdrv_drain_end(bs); } + bdrv_parent_drained_end(bs, parent); } } void bdrv_drained_end(BlockDriverState *bs) { - int drained_end_counter = 0; IO_OR_GS_CODE(); - bdrv_do_drained_end(bs, false, NULL, false, &drained_end_counter); - BDRV_POLL_WHILE(bs, qatomic_read(&drained_end_counter) > 0); -} - -void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter) -{ - IO_CODE(); - bdrv_do_drained_end(bs, false, NULL, false, drained_end_counter); -} - -void bdrv_subtree_drained_end(BlockDriverState *bs) -{ - int drained_end_counter = 0; - IO_OR_GS_CODE(); - bdrv_do_drained_end(bs, true, NULL, false, &drained_end_counter); - BDRV_POLL_WHILE(bs, qatomic_read(&drained_end_counter) > 0); -} - -void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent) -{ - int i; - IO_OR_GS_CODE(); - - for (i = 0; i < new_parent->recursive_quiesce_counter; i++) { - bdrv_do_drained_begin(child->bs, true, child, false, true); - } -} - -void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent) -{ - int drained_end_counter = 0; - int i; - IO_OR_GS_CODE(); - - for (i = 0; i < old_parent->recursive_quiesce_counter; i++) { - bdrv_do_drained_end(child->bs, true, child, false, - &drained_end_counter); - } - - BDRV_POLL_WHILE(child->bs, qatomic_read(&drained_end_counter) > 0); -} - -/* - * Wait for pending requests to complete on a single BlockDriverState subtree, - * and suspend block driver's internal I/O until next request arrives. - * - * Note that unlike bdrv_drain_all(), the caller must hold the BlockDriverState - * AioContext. - */ -void coroutine_fn bdrv_co_drain(BlockDriverState *bs) -{ - IO_OR_GS_CODE(); - assert(qemu_in_coroutine()); - bdrv_drained_begin(bs); - bdrv_drained_end(bs); + bdrv_do_drained_end(bs, NULL); } void bdrv_drain(BlockDriverState *bs) @@ -613,6 +438,8 @@ void bdrv_drain(BlockDriverState *bs) static void bdrv_drain_assert_idle(BlockDriverState *bs) { BdrvChild *child, *next; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); assert(qatomic_read(&bs->in_flight) == 0); QLIST_FOREACH_SAFE(child, &bs->children, next, next) { @@ -626,15 +453,16 @@ static bool bdrv_drain_all_poll(void) { BlockDriverState *bs = NULL; bool result = false; - GLOBAL_STATE_CODE(); - /* bdrv_drain_poll() can't make changes to the graph and we are holding the - * main AioContext lock, so iterating bdrv_next_all_states() is safe. */ + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + /* + * bdrv_drain_poll() can't make changes to the graph and we hold the BQL, + * so iterating bdrv_next_all_states() is safe. + */ while ((bs = bdrv_next_all_states(bs))) { - AioContext *aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - result |= bdrv_drain_poll(bs, false, NULL, true); - aio_context_release(aio_context); + result |= bdrv_drain_poll(bs, NULL, true); } return result; @@ -652,16 +480,11 @@ static bool bdrv_drain_all_poll(void) * NOTE: no new block jobs or BlockDriverStates can be created between * the bdrv_drain_all_begin() and bdrv_drain_all_end() calls. */ -void bdrv_drain_all_begin(void) +void bdrv_drain_all_begin_nopoll(void) { BlockDriverState *bs = NULL; GLOBAL_STATE_CODE(); - if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(NULL, true, false, NULL, true, true, NULL); - return; - } - /* * bdrv queue is managed by record/replay, * waiting for finishing the I/O requests may @@ -680,15 +503,32 @@ void bdrv_drain_all_begin(void) /* Quiesce all nodes, without polling in-flight requests yet. The graph * cannot change during this loop. */ while ((bs = bdrv_next_all_states(bs))) { - AioContext *aio_context = bdrv_get_aio_context(bs); + bdrv_do_drained_begin(bs, NULL, false); + } +} - aio_context_acquire(aio_context); - bdrv_do_drained_begin(bs, false, NULL, true, false); - aio_context_release(aio_context); +void coroutine_mixed_fn bdrv_drain_all_begin(void) +{ + BlockDriverState *bs = NULL; + + if (qemu_in_coroutine()) { + bdrv_co_yield_to_drain(NULL, true, NULL, true); + return; } + /* + * bdrv queue is managed by record/replay, + * waiting for finishing the I/O requests may + * be infinite + */ + if (replay_events_enabled()) { + return; + } + + bdrv_drain_all_begin_nopoll(); + /* Now poll the in-flight requests */ - AIO_WAIT_WHILE(NULL, bdrv_drain_all_poll()); + AIO_WAIT_WHILE_UNLOCKED(NULL, bdrv_drain_all_poll()); while ((bs = bdrv_next_all_states(bs))) { bdrv_drain_assert_idle(bs); @@ -697,22 +537,19 @@ void bdrv_drain_all_begin(void) void bdrv_drain_all_end_quiesce(BlockDriverState *bs) { - int drained_end_counter = 0; GLOBAL_STATE_CODE(); g_assert(bs->quiesce_counter > 0); g_assert(!bs->refcnt); while (bs->quiesce_counter) { - bdrv_do_drained_end(bs, false, NULL, true, &drained_end_counter); + bdrv_do_drained_end(bs, NULL); } - BDRV_POLL_WHILE(bs, qatomic_read(&drained_end_counter) > 0); } void bdrv_drain_all_end(void) { BlockDriverState *bs = NULL; - int drained_end_counter = 0; GLOBAL_STATE_CODE(); /* @@ -725,16 +562,10 @@ void bdrv_drain_all_end(void) } while ((bs = bdrv_next_all_states(bs))) { - AioContext *aio_context = bdrv_get_aio_context(bs); - - aio_context_acquire(aio_context); - bdrv_do_drained_end(bs, false, NULL, true, &drained_end_counter); - aio_context_release(aio_context); + bdrv_do_drained_end(bs, NULL); } assert(qemu_get_current_aio_context() == qemu_get_aio_context()); - AIO_WAIT_WHILE(NULL, qatomic_read(&drained_end_counter) > 0); - assert(bdrv_drain_all_count > 0); bdrv_drain_all_count--; } @@ -757,20 +588,26 @@ static void coroutine_fn tracked_request_end(BdrvTrackedRequest *req) qatomic_dec(&req->bs->serialising_in_flight); } - qemu_co_mutex_lock(&req->bs->reqs_lock); + qemu_mutex_lock(&req->bs->reqs_lock); QLIST_REMOVE(req, list); + qemu_mutex_unlock(&req->bs->reqs_lock); + + /* + * At this point qemu_co_queue_wait(&req->wait_queue, ...) won't be called + * anymore because the request has been removed from the list, so it's safe + * to restart the queue outside reqs_lock to minimize the critical section. + */ qemu_co_queue_restart_all(&req->wait_queue); - qemu_co_mutex_unlock(&req->bs->reqs_lock); } /** * Add an active request to the tracked requests list */ -static void tracked_request_begin(BdrvTrackedRequest *req, - BlockDriverState *bs, - int64_t offset, - int64_t bytes, - enum BdrvTrackedRequestType type) +static void coroutine_fn tracked_request_begin(BdrvTrackedRequest *req, + BlockDriverState *bs, + int64_t offset, + int64_t bytes, + enum BdrvTrackedRequestType type) { bdrv_check_request(offset, bytes, &error_abort); @@ -787,9 +624,9 @@ static void tracked_request_begin(BdrvTrackedRequest *req, qemu_co_queue_init(&req->wait_queue); - qemu_co_mutex_lock(&bs->reqs_lock); + qemu_mutex_lock(&bs->reqs_lock); QLIST_INSERT_HEAD(&bs->tracked_requests, req, list); - qemu_co_mutex_unlock(&bs->reqs_lock); + qemu_mutex_unlock(&bs->reqs_lock); } static bool tracked_request_overlaps(BdrvTrackedRequest *req, @@ -809,7 +646,7 @@ static bool tracked_request_overlaps(BdrvTrackedRequest *req, } /* Called with self->bs->reqs_lock held */ -static BdrvTrackedRequest * +static coroutine_fn BdrvTrackedRequest * bdrv_find_conflicting_request(BdrvTrackedRequest *self) { BdrvTrackedRequest *req; @@ -843,20 +680,16 @@ bdrv_find_conflicting_request(BdrvTrackedRequest *self) } /* Called with self->bs->reqs_lock held */ -static bool coroutine_fn +static void coroutine_fn bdrv_wait_serialising_requests_locked(BdrvTrackedRequest *self) { BdrvTrackedRequest *req; - bool waited = false; while ((req = bdrv_find_conflicting_request(self))) { self->waiting_for = req; qemu_co_queue_wait(&req->wait_queue, &self->bs->reqs_lock); self->waiting_for = NULL; - waited = true; } - - return waited; } /* Called with req->bs->reqs_lock held */ @@ -898,31 +731,30 @@ BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs) } /** - * Round a region to cluster boundaries + * Round a region to subcluster (if supported) or cluster boundaries */ -void bdrv_round_to_clusters(BlockDriverState *bs, - int64_t offset, int64_t bytes, - int64_t *cluster_offset, - int64_t *cluster_bytes) +void coroutine_fn GRAPH_RDLOCK +bdrv_round_to_subclusters(BlockDriverState *bs, int64_t offset, int64_t bytes, + int64_t *align_offset, int64_t *align_bytes) { BlockDriverInfo bdi; IO_CODE(); - if (bdrv_get_info(bs, &bdi) < 0 || bdi.cluster_size == 0) { - *cluster_offset = offset; - *cluster_bytes = bytes; + if (bdrv_co_get_info(bs, &bdi) < 0 || bdi.subcluster_size == 0) { + *align_offset = offset; + *align_bytes = bytes; } else { - int64_t c = bdi.cluster_size; - *cluster_offset = QEMU_ALIGN_DOWN(offset, c); - *cluster_bytes = QEMU_ALIGN_UP(offset - *cluster_offset + bytes, c); + int64_t c = bdi.subcluster_size; + *align_offset = QEMU_ALIGN_DOWN(offset, c); + *align_bytes = QEMU_ALIGN_UP(offset - *align_offset + bytes, c); } } -static int bdrv_get_cluster_size(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK bdrv_get_cluster_size(BlockDriverState *bs) { BlockDriverInfo bdi; int ret; - ret = bdrv_get_info(bs, &bdi); + ret = bdrv_co_get_info(bs, &bdi); if (ret < 0 || bdi.cluster_size == 0) { return bs->bl.request_alignment; } else { @@ -949,36 +781,31 @@ void bdrv_dec_in_flight(BlockDriverState *bs) bdrv_wakeup(bs); } -static bool coroutine_fn bdrv_wait_serialising_requests(BdrvTrackedRequest *self) +static void coroutine_fn +bdrv_wait_serialising_requests(BdrvTrackedRequest *self) { BlockDriverState *bs = self->bs; - bool waited = false; if (!qatomic_read(&bs->serialising_in_flight)) { - return false; + return; } - qemu_co_mutex_lock(&bs->reqs_lock); - waited = bdrv_wait_serialising_requests_locked(self); - qemu_co_mutex_unlock(&bs->reqs_lock); - - return waited; + qemu_mutex_lock(&bs->reqs_lock); + bdrv_wait_serialising_requests_locked(self); + qemu_mutex_unlock(&bs->reqs_lock); } -bool coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, +void coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, uint64_t align) { - bool waited; IO_CODE(); - qemu_co_mutex_lock(&req->bs->reqs_lock); + qemu_mutex_lock(&req->bs->reqs_lock); tracked_request_set_serialising(req, align); - waited = bdrv_wait_serialising_requests_locked(req); + bdrv_wait_serialising_requests_locked(req); - qemu_co_mutex_unlock(&req->bs->reqs_lock); - - return waited; + qemu_mutex_unlock(&req->bs->reqs_lock); } int bdrv_check_qiov_request(int64_t offset, int64_t bytes, @@ -1061,14 +888,6 @@ static int bdrv_check_request32(int64_t offset, int64_t bytes, return 0; } -int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, - int64_t bytes, BdrvRequestFlags flags) -{ - IO_CODE(); - return bdrv_pwritev(child, offset, bytes, NULL, - BDRV_REQ_ZERO_WRITE | flags); -} - /* * Completely zero out a block device with the help of bdrv_pwrite_zeroes. * The operation is sped up by checking the block status and only writing @@ -1111,62 +930,26 @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags) } } -/* See bdrv_pwrite() for the return codes */ -int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int64_t bytes) -{ - int ret; - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - IO_CODE(); - - if (bytes < 0) { - return -EINVAL; - } - - ret = bdrv_preadv(child, offset, bytes, &qiov, 0); - - return ret < 0 ? ret : bytes; -} - -/* Return no. of bytes on success or < 0 on error. Important errors are: - -EIO generic I/O error (may happen for all errors) - -ENOMEDIUM No media inserted. - -EINVAL Invalid offset or number of bytes - -EACCES Trying to write a read-only device -*/ -int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, - int64_t bytes) -{ - int ret; - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - IO_CODE(); - - if (bytes < 0) { - return -EINVAL; - } - - ret = bdrv_pwritev(child, offset, bytes, &qiov, 0); - - return ret < 0 ? ret : bytes; -} - /* * Writes to the file and ensures that no writes are reordered across this * request (acts as a barrier) * * Returns 0 on success, -errno in error cases. */ -int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, - const void *buf, int64_t count) +int coroutine_fn bdrv_co_pwrite_sync(BdrvChild *child, int64_t offset, + int64_t bytes, const void *buf, + BdrvRequestFlags flags) { int ret; IO_CODE(); + assert_bdrv_graph_readable(); - ret = bdrv_pwrite(child, offset, buf, count); + ret = bdrv_co_pwrite(child, offset, bytes, buf, flags); if (ret < 0) { return ret; } - ret = bdrv_flush(child->bs); + ret = bdrv_co_flush(child->bs); if (ret < 0) { return ret; } @@ -1187,20 +970,19 @@ static void bdrv_co_io_em_complete(void *opaque, int ret) aio_co_wake(co->coroutine); } -static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, - int64_t offset, int64_t bytes, - QEMUIOVector *qiov, - size_t qiov_offset, int flags) +static int coroutine_fn GRAPH_RDLOCK +bdrv_driver_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, int flags) { BlockDriver *drv = bs->drv; int64_t sector_num; unsigned int nb_sectors; QEMUIOVector local_qiov; int ret; + assert_bdrv_graph_readable(); bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); - assert(!(flags & ~BDRV_REQ_MASK)); - assert(!(flags & BDRV_REQ_NO_FALLBACK)); + assert(!(flags & ~bs->supported_read_flags)); if (!drv) { return -ENOMEDIUM; @@ -1257,30 +1039,36 @@ out: return ret; } -static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, - int64_t offset, int64_t bytes, - QEMUIOVector *qiov, - size_t qiov_offset, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +bdrv_driver_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) { BlockDriver *drv = bs->drv; + bool emulate_fua = false; int64_t sector_num; unsigned int nb_sectors; QEMUIOVector local_qiov; int ret; + assert_bdrv_graph_readable(); bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); - assert(!(flags & ~BDRV_REQ_MASK)); - assert(!(flags & BDRV_REQ_NO_FALLBACK)); if (!drv) { return -ENOMEDIUM; } + if ((flags & BDRV_REQ_FUA) && + (~bs->supported_write_flags & BDRV_REQ_FUA)) { + flags &= ~BDRV_REQ_FUA; + emulate_fua = true; + } + + flags &= bs->supported_write_flags; + if (drv->bdrv_co_pwritev_part) { ret = drv->bdrv_co_pwritev_part(bs, offset, bytes, qiov, qiov_offset, - flags & bs->supported_write_flags); - flags &= ~bs->supported_write_flags; + flags); goto emulate_flags; } @@ -1290,9 +1078,7 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, } if (drv->bdrv_co_pwritev) { - ret = drv->bdrv_co_pwritev(bs, offset, bytes, qiov, - flags & bs->supported_write_flags); - flags &= ~bs->supported_write_flags; + ret = drv->bdrv_co_pwritev(bs, offset, bytes, qiov, flags); goto emulate_flags; } @@ -1302,10 +1088,8 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, .coroutine = qemu_coroutine_self(), }; - acb = drv->bdrv_aio_pwritev(bs, offset, bytes, qiov, - flags & bs->supported_write_flags, + acb = drv->bdrv_aio_pwritev(bs, offset, bytes, qiov, flags, bdrv_co_io_em_complete, &co); - flags &= ~bs->supported_write_flags; if (acb == NULL) { ret = -EIO; } else { @@ -1323,12 +1107,10 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, assert(bytes <= BDRV_REQUEST_MAX_BYTES); assert(drv->bdrv_co_writev); - ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov, - flags & bs->supported_write_flags); - flags &= ~bs->supported_write_flags; + ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov, flags); emulate_flags: - if (ret == 0 && (flags & BDRV_REQ_FUA)) { + if (ret == 0 && emulate_fua) { ret = bdrv_co_flush(bs); } @@ -1339,7 +1121,7 @@ emulate_flags: return ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK bdrv_driver_pwritev_compressed(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset) @@ -1347,6 +1129,7 @@ bdrv_driver_pwritev_compressed(BlockDriverState *bs, int64_t offset, BlockDriver *drv = bs->drv; QEMUIOVector local_qiov; int ret; + assert_bdrv_graph_readable(); bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); @@ -1374,9 +1157,9 @@ bdrv_driver_pwritev_compressed(BlockDriverState *bs, int64_t offset, return ret; } -static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, - size_t qiov_offset, int flags) +static int coroutine_fn GRAPH_RDLOCK +bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, int flags) { BlockDriverState *bs = child->bs; @@ -1388,8 +1171,8 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, void *bounce_buffer = NULL; BlockDriver *drv = bs->drv; - int64_t cluster_offset; - int64_t cluster_bytes; + int64_t align_offset; + int64_t align_bytes; int64_t skip_bytes; int ret; int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, @@ -1423,28 +1206,28 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, * BDRV_REQUEST_MAX_BYTES (even when the original read did not), which * is one reason we loop rather than doing it all at once. */ - bdrv_round_to_clusters(bs, offset, bytes, &cluster_offset, &cluster_bytes); - skip_bytes = offset - cluster_offset; + bdrv_round_to_subclusters(bs, offset, bytes, &align_offset, &align_bytes); + skip_bytes = offset - align_offset; trace_bdrv_co_do_copy_on_readv(bs, offset, bytes, - cluster_offset, cluster_bytes); + align_offset, align_bytes); - while (cluster_bytes) { + while (align_bytes) { int64_t pnum; if (skip_write) { ret = 1; /* "already allocated", so nothing will be copied */ - pnum = MIN(cluster_bytes, max_transfer); + pnum = MIN(align_bytes, max_transfer); } else { - ret = bdrv_is_allocated(bs, cluster_offset, - MIN(cluster_bytes, max_transfer), &pnum); + ret = bdrv_co_is_allocated(bs, align_offset, + MIN(align_bytes, max_transfer), &pnum); if (ret < 0) { /* * Safe to treat errors in querying allocation as if * unallocated; we'll probably fail again soon on the * read, but at least that will set a decent errno. */ - pnum = MIN(cluster_bytes, max_transfer); + pnum = MIN(align_bytes, max_transfer); } /* Stop at EOF if the image ends in the middle of the cluster */ @@ -1462,7 +1245,7 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, /* Must copy-on-read; use the bounce buffer */ pnum = MIN(pnum, MAX_BOUNCE_BUFFER); if (!bounce_buffer) { - int64_t max_we_need = MAX(pnum, cluster_bytes - pnum); + int64_t max_we_need = MAX(pnum, align_bytes - pnum); int64_t max_allowed = MIN(max_transfer, MAX_BOUNCE_BUFFER); int64_t bounce_buffer_len = MIN(max_we_need, max_allowed); @@ -1474,25 +1257,25 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, } qemu_iovec_init_buf(&local_qiov, bounce_buffer, pnum); - ret = bdrv_driver_preadv(bs, cluster_offset, pnum, + ret = bdrv_driver_preadv(bs, align_offset, pnum, &local_qiov, 0, 0); if (ret < 0) { goto err; } - bdrv_debug_event(bs, BLKDBG_COR_WRITE); + bdrv_co_debug_event(bs, BLKDBG_COR_WRITE); if (drv->bdrv_co_pwrite_zeroes && buffer_is_zero(bounce_buffer, pnum)) { /* FIXME: Should we (perhaps conditionally) be setting * BDRV_REQ_MAY_UNMAP, if it will allow for a sparser copy * that still correctly reads as zero? */ - ret = bdrv_co_do_pwrite_zeroes(bs, cluster_offset, pnum, + ret = bdrv_co_do_pwrite_zeroes(bs, align_offset, pnum, BDRV_REQ_WRITE_UNCHANGED); } else { /* This does not change the data on the disk, it is not * necessary to flush even in cache=writethrough mode. */ - ret = bdrv_driver_pwritev(bs, cluster_offset, pnum, + ret = bdrv_driver_pwritev(bs, align_offset, pnum, &local_qiov, 0, BDRV_REQ_WRITE_UNCHANGED); } @@ -1521,8 +1304,8 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, } } - cluster_offset += pnum; - cluster_bytes -= pnum; + align_offset += pnum; + align_bytes -= pnum; progress += pnum - skip_bytes; skip_bytes = 0; } @@ -1538,9 +1321,10 @@ err: * handles copy on read, zeroing after EOF, and fragmentation of large * reads; any other features must be implemented by the caller. */ -static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, - BdrvTrackedRequest *req, int64_t offset, int64_t bytes, - int64_t align, QEMUIOVector *qiov, size_t qiov_offset, int flags) +static int coroutine_fn GRAPH_RDLOCK +bdrv_aligned_preadv(BdrvChild *child, BdrvTrackedRequest *req, + int64_t offset, int64_t bytes, int64_t align, + QEMUIOVector *qiov, size_t qiov_offset, int flags) { BlockDriverState *bs = child->bs; int64_t total_bytes, max_bytes; @@ -1556,11 +1340,14 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX), align); - /* TODO: We would need a per-BDS .supported_read_flags and + /* + * TODO: We would need a per-BDS .supported_read_flags and * 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_COPY_ON_READ | BDRV_REQ_PREFETCH))); + * passthrough flags except the BDRV_REQ_REGISTERED_BUF optimization hint. + */ + assert(!(flags & ~(BDRV_REQ_COPY_ON_READ | BDRV_REQ_PREFETCH | + BDRV_REQ_REGISTERED_BUF))); /* Handle Copy on Read and associated serialisation */ if (flags & BDRV_REQ_COPY_ON_READ) { @@ -1580,7 +1367,7 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, /* The flag BDRV_REQ_COPY_ON_READ has reached its addressee */ flags &= ~BDRV_REQ_COPY_ON_READ; - ret = bdrv_is_allocated(bs, offset, bytes, &pnum); + ret = bdrv_co_is_allocated(bs, offset, bytes, &pnum); if (ret < 0) { goto out; } @@ -1595,13 +1382,13 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, } /* Forward the request to the BlockDriver, possibly fragmenting it */ - total_bytes = bdrv_getlength(bs); + total_bytes = bdrv_co_getlength(bs); if (total_bytes < 0) { ret = total_bytes; goto out; } - assert(!(flags & ~bs->supported_read_flags)); + assert(!(flags & ~(bs->supported_read_flags | BDRV_REQ_REGISTERED_BUF))); max_bytes = ROUND_UP(MAX(0, total_bytes - offset), align); if (bytes <= max_bytes && bytes <= max_transfer) { @@ -1657,6 +1444,14 @@ out: * @merge_reads is true for small requests, * if @buf_len == @head + bytes + @tail. In this case it is possible that both * head and tail exist but @buf_len == align and @tail_buf == @buf. + * + * @write is true for write requests, false for read requests. + * + * If padding makes the vector too long (exceeding IOV_MAX), then we need to + * merge existing vector elements into a single one. @collapse_bounce_buf acts + * as the bounce buffer in such cases. @pre_collapse_qiov has the pre-collapse + * I/O vector elements so for read requests, the data can be copied back after + * the read is done. */ typedef struct BdrvRequestPadding { uint8_t *buf; @@ -1665,11 +1460,17 @@ typedef struct BdrvRequestPadding { size_t head; size_t tail; bool merge_reads; + bool write; QEMUIOVector local_qiov; + + uint8_t *collapse_bounce_buf; + size_t collapse_len; + QEMUIOVector pre_collapse_qiov; } BdrvRequestPadding; static bool bdrv_init_padding(BlockDriverState *bs, int64_t offset, int64_t bytes, + bool write, BdrvRequestPadding *pad) { int64_t align = bs->bl.request_alignment; @@ -1701,13 +1502,14 @@ static bool bdrv_init_padding(BlockDriverState *bs, pad->tail_buf = pad->buf + pad->buf_len - align; } + pad->write = write; + return true; } -static int bdrv_padding_rmw_read(BdrvChild *child, - BdrvTrackedRequest *req, - BdrvRequestPadding *pad, - bool zero_middle) +static int coroutine_fn GRAPH_RDLOCK +bdrv_padding_rmw_read(BdrvChild *child, BdrvTrackedRequest *req, + BdrvRequestPadding *pad, bool zero_middle) { QEMUIOVector local_qiov; BlockDriverState *bs = child->bs; @@ -1722,10 +1524,10 @@ static int bdrv_padding_rmw_read(BdrvChild *child, qemu_iovec_init_buf(&local_qiov, pad->buf, bytes); if (pad->head) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); } if (pad->merge_reads && pad->tail) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); } ret = bdrv_aligned_preadv(child, req, req->overlap_offset, bytes, align, &local_qiov, 0, 0); @@ -1733,10 +1535,10 @@ static int bdrv_padding_rmw_read(BdrvChild *child, return ret; } if (pad->head) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); } if (pad->merge_reads && pad->tail) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); } if (pad->merge_reads) { @@ -1747,7 +1549,7 @@ static int bdrv_padding_rmw_read(BdrvChild *child, if (pad->tail) { qemu_iovec_init_buf(&local_qiov, pad->tail_buf, align); - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); ret = bdrv_aligned_preadv( child, req, req->overlap_offset + req->overlap_bytes - align, @@ -1755,7 +1557,7 @@ static int bdrv_padding_rmw_read(BdrvChild *child, if (ret < 0) { return ret; } - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); } zero_mem: @@ -1766,8 +1568,23 @@ zero_mem: return 0; } -static void bdrv_padding_destroy(BdrvRequestPadding *pad) +/** + * Free *pad's associated buffers, and perform any necessary finalization steps. + */ +static void bdrv_padding_finalize(BdrvRequestPadding *pad) { + if (pad->collapse_bounce_buf) { + if (!pad->write) { + /* + * If padding required elements in the vector to be collapsed into a + * bounce buffer, copy the bounce buffer content back + */ + qemu_iovec_from_buf(&pad->pre_collapse_qiov, 0, + pad->collapse_bounce_buf, pad->collapse_len); + } + qemu_vfree(pad->collapse_bounce_buf); + qemu_iovec_destroy(&pad->pre_collapse_qiov); + } if (pad->buf) { qemu_vfree(pad->buf); qemu_iovec_destroy(&pad->local_qiov); @@ -1775,6 +1592,101 @@ static void bdrv_padding_destroy(BdrvRequestPadding *pad) memset(pad, 0, sizeof(*pad)); } +/* + * Create pad->local_qiov by wrapping @iov in the padding head and tail, while + * ensuring that the resulting vector will not exceed IOV_MAX elements. + * + * To ensure this, when necessary, the first two or three elements of @iov are + * merged into pad->collapse_bounce_buf and replaced by a reference to that + * bounce buffer in pad->local_qiov. + * + * After performing a read request, the data from the bounce buffer must be + * copied back into pad->pre_collapse_qiov (e.g. by bdrv_padding_finalize()). + */ +static int bdrv_create_padded_qiov(BlockDriverState *bs, + BdrvRequestPadding *pad, + struct iovec *iov, int niov, + size_t iov_offset, size_t bytes) +{ + int padded_niov, surplus_count, collapse_count; + + /* Assert this invariant */ + assert(niov <= IOV_MAX); + + /* + * Cannot pad if resulting length would exceed SIZE_MAX. Returning an error + * to the guest is not ideal, but there is little else we can do. At least + * this will practically never happen on 64-bit systems. + */ + if (SIZE_MAX - pad->head < bytes || + SIZE_MAX - pad->head - bytes < pad->tail) + { + return -EINVAL; + } + + /* Length of the resulting IOV if we just concatenated everything */ + padded_niov = !!pad->head + niov + !!pad->tail; + + qemu_iovec_init(&pad->local_qiov, MIN(padded_niov, IOV_MAX)); + + if (pad->head) { + qemu_iovec_add(&pad->local_qiov, pad->buf, pad->head); + } + + /* + * If padded_niov > IOV_MAX, we cannot just concatenate everything. + * Instead, merge the first two or three elements of @iov to reduce the + * number of vector elements as necessary. + */ + if (padded_niov > IOV_MAX) { + /* + * Only head and tail can have lead to the number of entries exceeding + * IOV_MAX, so we can exceed it by the head and tail at most. We need + * to reduce the number of elements by `surplus_count`, so we merge that + * many elements plus one into one element. + */ + surplus_count = padded_niov - IOV_MAX; + assert(surplus_count <= !!pad->head + !!pad->tail); + collapse_count = surplus_count + 1; + + /* + * Move the elements to collapse into `pad->pre_collapse_qiov`, then + * advance `iov` (and associated variables) by those elements. + */ + qemu_iovec_init(&pad->pre_collapse_qiov, collapse_count); + qemu_iovec_concat_iov(&pad->pre_collapse_qiov, iov, + collapse_count, iov_offset, SIZE_MAX); + iov += collapse_count; + iov_offset = 0; + niov -= collapse_count; + bytes -= pad->pre_collapse_qiov.size; + + /* + * Construct the bounce buffer to match the length of the to-collapse + * vector elements, and for write requests, initialize it with the data + * from those elements. Then add it to `pad->local_qiov`. + */ + pad->collapse_len = pad->pre_collapse_qiov.size; + pad->collapse_bounce_buf = qemu_blockalign(bs, pad->collapse_len); + if (pad->write) { + qemu_iovec_to_buf(&pad->pre_collapse_qiov, 0, + pad->collapse_bounce_buf, pad->collapse_len); + } + qemu_iovec_add(&pad->local_qiov, + pad->collapse_bounce_buf, pad->collapse_len); + } + + qemu_iovec_concat_iov(&pad->local_qiov, iov, niov, iov_offset, bytes); + + if (pad->tail) { + qemu_iovec_add(&pad->local_qiov, + pad->buf + pad->buf_len - pad->tail, pad->tail); + } + + assert(pad->local_qiov.niov == MIN(padded_niov, IOV_MAX)); + return 0; +} + /* * bdrv_pad_request * @@ -1782,6 +1694,8 @@ static void bdrv_padding_destroy(BdrvRequestPadding *pad) * read of padding, bdrv_padding_rmw_read() should be called separately if * needed. * + * @write is true for write requests, false for read requests. + * * Request parameters (@qiov, &qiov_offset, &offset, &bytes) are in-out: * - on function start they represent original request * - on failure or when padding is not needed they are unchanged @@ -1790,34 +1704,58 @@ static void bdrv_padding_destroy(BdrvRequestPadding *pad) static int bdrv_pad_request(BlockDriverState *bs, QEMUIOVector **qiov, size_t *qiov_offset, int64_t *offset, int64_t *bytes, - BdrvRequestPadding *pad, bool *padded) + bool write, + BdrvRequestPadding *pad, bool *padded, + BdrvRequestFlags *flags) { int ret; + struct iovec *sliced_iov; + int sliced_niov; + size_t sliced_head, sliced_tail; - bdrv_check_qiov_request(*offset, *bytes, *qiov, *qiov_offset, &error_abort); + /* Should have been checked by the caller already */ + ret = bdrv_check_request32(*offset, *bytes, *qiov, *qiov_offset); + if (ret < 0) { + return ret; + } - if (!bdrv_init_padding(bs, *offset, *bytes, pad)) { + if (!bdrv_init_padding(bs, *offset, *bytes, write, pad)) { if (padded) { *padded = false; } return 0; } - ret = qemu_iovec_init_extended(&pad->local_qiov, pad->buf, pad->head, - *qiov, *qiov_offset, *bytes, - pad->buf + pad->buf_len - pad->tail, - pad->tail); - if (ret < 0) { - bdrv_padding_destroy(pad); - return ret; + /* + * For prefetching in stream_populate(), no qiov is passed along, because + * only copy-on-read matters. + */ + if (*qiov) { + sliced_iov = qemu_iovec_slice(*qiov, *qiov_offset, *bytes, + &sliced_head, &sliced_tail, + &sliced_niov); + + /* Guaranteed by bdrv_check_request32() */ + assert(*bytes <= SIZE_MAX); + ret = bdrv_create_padded_qiov(bs, pad, sliced_iov, sliced_niov, + sliced_head, *bytes); + if (ret < 0) { + bdrv_padding_finalize(pad); + return ret; + } + *qiov = &pad->local_qiov; + *qiov_offset = 0; } + *bytes += pad->head + pad->tail; *offset -= pad->head; - *qiov = &pad->local_qiov; - *qiov_offset = 0; if (padded) { *padded = true; } + if (flags) { + /* Can't use optimization hint with bounce buffer */ + *flags &= ~BDRV_REQ_REGISTERED_BUF; + } return 0; } @@ -1843,7 +1781,7 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, trace_bdrv_co_preadv_part(bs, offset, bytes, flags); - if (!bdrv_is_inserted(bs)) { + if (!bdrv_co_is_inserted(bs)) { return -ENOMEDIUM; } @@ -1871,8 +1809,8 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, flags |= BDRV_REQ_COPY_ON_READ; } - ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad, - NULL); + ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, false, + &pad, NULL, &flags); if (ret < 0) { goto fail; } @@ -1882,7 +1820,7 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, bs->bl.request_alignment, qiov, qiov_offset, flags); tracked_request_end(&req); - bdrv_padding_destroy(&pad); + bdrv_padding_finalize(&pad); fail: bdrv_dec_in_flight(bs); @@ -1890,8 +1828,9 @@ fail: return ret; } -static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int64_t bytes, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + BdrvRequestFlags flags) { BlockDriver *drv = bs->drv; QEMUIOVector qiov; @@ -1907,6 +1846,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, bs->bl.request_alignment); int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, MAX_BOUNCE_BUFFER); + assert_bdrv_graph_readable(); bdrv_check_request(offset, bytes, &error_abort); if (!drv) { @@ -1917,6 +1857,11 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, return -ENOTSUP; } + /* By definition there is no user buffer so this flag doesn't make sense */ + if (flags & BDRV_REQ_REGISTERED_BUF) { + return -EINVAL; + } + /* Invalidate the cached block-status data range if this write overlaps */ bdrv_bsc_invalidate_range(bs, offset, bytes); @@ -2007,7 +1952,7 @@ fail: return ret; } -static inline int coroutine_fn +static inline int coroutine_fn GRAPH_RDLOCK bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, int64_t bytes, BdrvTrackedRequest *req, int flags) { @@ -2061,7 +2006,7 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, int64_t bytes, } } -static inline void coroutine_fn +static inline void coroutine_fn GRAPH_RDLOCK bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, int64_t bytes, BdrvTrackedRequest *req, int ret) { @@ -2105,10 +2050,11 @@ bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, int64_t bytes, * Forwards an already correctly aligned write request to the BlockDriver, * after possibly fragmenting it. */ -static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, - BdrvTrackedRequest *req, int64_t offset, int64_t bytes, - int64_t align, QEMUIOVector *qiov, size_t qiov_offset, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +bdrv_aligned_pwritev(BdrvChild *child, BdrvTrackedRequest *req, + int64_t offset, int64_t bytes, int64_t align, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) { BlockDriverState *bs = child->bs; BlockDriver *drv = bs->drv; @@ -2142,21 +2088,24 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, if (bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP) { flags |= BDRV_REQ_MAY_UNMAP; } + + /* Can't use optimization hint with bufferless zero write */ + flags &= ~BDRV_REQ_REGISTERED_BUF; } if (ret < 0) { /* Do nothing, write notifier decided to fail this request */ } else if (flags & BDRV_REQ_ZERO_WRITE) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_ZERO); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_ZERO); ret = bdrv_co_do_pwrite_zeroes(bs, offset, bytes, flags); } else if (flags & BDRV_REQ_WRITE_COMPRESSED) { ret = bdrv_driver_pwritev_compressed(bs, offset, bytes, qiov, qiov_offset); } else if (bytes <= max_transfer) { - bdrv_debug_event(bs, BLKDBG_PWRITEV); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV); ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, qiov_offset, flags); } else { - bdrv_debug_event(bs, BLKDBG_PWRITEV); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV); while (bytes_remaining) { int num = MIN(bytes_remaining, max_transfer); int local_flags = flags; @@ -2179,7 +2128,7 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, bytes_remaining -= num; } } - bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_DONE); if (ret >= 0) { ret = 0; @@ -2189,11 +2138,9 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, return ret; } -static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, - int64_t offset, - int64_t bytes, - BdrvRequestFlags flags, - BdrvTrackedRequest *req) +static int coroutine_fn GRAPH_RDLOCK +bdrv_co_do_zero_pwritev(BdrvChild *child, int64_t offset, int64_t bytes, + BdrvRequestFlags flags, BdrvTrackedRequest *req) { BlockDriverState *bs = child->bs; QEMUIOVector local_qiov; @@ -2202,7 +2149,10 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, bool padding; BdrvRequestPadding pad; - padding = bdrv_init_padding(bs, offset, bytes, &pad); + /* This flag doesn't make sense for padding or zero writes */ + flags &= ~BDRV_REQ_REGISTERED_BUF; + + padding = bdrv_init_padding(bs, offset, bytes, true, &pad); if (padding) { assert(!(flags & BDRV_REQ_NO_WAIT)); bdrv_make_request_serialising(req, align); @@ -2250,7 +2200,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, } out: - bdrv_padding_destroy(&pad); + bdrv_padding_finalize(&pad); return ret; } @@ -2280,7 +2230,7 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, trace_bdrv_co_pwritev_part(child->bs, offset, bytes, flags); - if (!bdrv_is_inserted(bs)) { + if (!bdrv_co_is_inserted(bs)) { return -ENOMEDIUM; } @@ -2318,8 +2268,8 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, * bdrv_co_do_zero_pwritev() does aligning by itself, so, we do * alignment only if there is no ZERO flag. */ - ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad, - &padded); + ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, true, + &pad, &padded, &flags); if (ret < 0) { return ret; } @@ -2349,7 +2299,7 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, ret = bdrv_aligned_pwritev(child, &req, offset, bytes, align, qiov, qiov_offset, flags); - bdrv_padding_destroy(&pad); + bdrv_padding_finalize(&pad); out: tracked_request_end(&req); @@ -2363,6 +2313,7 @@ int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, { IO_CODE(); trace_bdrv_co_pwrite_zeroes(child->bs, offset, bytes, flags); + assert_bdrv_graph_readable(); if (!(child->bs->open_flags & BDRV_O_UNMAP)) { flags &= ~BDRV_REQ_MAY_UNMAP; @@ -2382,6 +2333,7 @@ int bdrv_flush_all(void) int result = 0; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); /* * bdrv queue is managed by record/replay, @@ -2393,15 +2345,10 @@ int bdrv_flush_all(void) } for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { - AioContext *aio_context = bdrv_get_aio_context(bs); - int ret; - - aio_context_acquire(aio_context); - ret = bdrv_flush(bs); + int ret = bdrv_flush(bs); if (ret < 0 && !result) { result = ret; } - aio_context_release(aio_context); } return result; @@ -2434,11 +2381,10 @@ int bdrv_flush_all(void) * BDRV_BLOCK_OFFSET_VALID bit is set, 'map' and 'file' (if non-NULL) are * set to the host mapping and BDS corresponding to the guest offset. */ -static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, - bool want_zero, - int64_t offset, int64_t bytes, - int64_t *pnum, int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, BlockDriverState **file) { int64_t total_size; int64_t n; /* bytes */ @@ -2450,8 +2396,9 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, bool has_filtered_child; assert(pnum); + assert_bdrv_graph_readable(); *pnum = 0; - total_size = bdrv_getlength(bs); + total_size = bdrv_co_getlength(bs); if (total_size < 0) { ret = total_size; goto early_out; @@ -2471,7 +2418,7 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, bytes = n; } - /* Must be non-NULL or bdrv_getlength() would have failed */ + /* Must be non-NULL or bdrv_co_getlength() would have failed */ assert(bs->drv); has_filtered_child = bdrv_filter_child(bs); if (!bs->drv->bdrv_co_block_status && !has_filtered_child) { @@ -2596,8 +2543,8 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, if (ret & BDRV_BLOCK_RAW) { assert(ret & BDRV_BLOCK_OFFSET_VALID && local_file); - ret = bdrv_co_block_status(local_file, want_zero, local_map, - *pnum, pnum, &local_map, &local_file); + ret = bdrv_co_do_block_status(local_file, want_zero, local_map, + *pnum, pnum, &local_map, &local_file); goto out; } @@ -2609,7 +2556,7 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, if (!cow_bs) { ret |= BDRV_BLOCK_ZERO; } else if (want_zero) { - int64_t size2 = bdrv_getlength(cow_bs); + int64_t size2 = bdrv_co_getlength(cow_bs); if (size2 >= 0 && offset >= size2) { ret |= BDRV_BLOCK_ZERO; @@ -2624,8 +2571,8 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, int64_t file_pnum; int ret2; - ret2 = bdrv_co_block_status(local_file, want_zero, local_map, - *pnum, &file_pnum, NULL, NULL); + ret2 = bdrv_co_do_block_status(local_file, want_zero, local_map, + *pnum, &file_pnum, NULL, NULL); if (ret2 >= 0) { /* Ignore errors. This is just providing extra information, it * is useful but not necessary. @@ -2644,6 +2591,16 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, ret |= (ret2 & BDRV_BLOCK_ZERO); } } + + /* + * Now that the recursive search was done, clear the flag. Otherwise, + * with more complicated block graphs like snapshot-access -> + * copy-before-write -> qcow2, where the return value will be propagated + * further up to a parent bdrv_co_do_block_status() call, both the + * BDRV_BLOCK_RECURSE and BDRV_BLOCK_ZERO flags would be set, which is + * not allowed. + */ + ret &= ~BDRV_BLOCK_RECURSE; } out: @@ -2680,6 +2637,7 @@ bdrv_co_common_block_status_above(BlockDriverState *bs, IO_CODE(); assert(!include_base || base); /* Can't include NULL base */ + assert_bdrv_graph_readable(); if (!depth) { depth = &dummy; @@ -2691,7 +2649,8 @@ bdrv_co_common_block_status_above(BlockDriverState *bs, return 0; } - ret = bdrv_co_block_status(bs, want_zero, offset, bytes, pnum, map, file); + ret = bdrv_co_do_block_status(bs, want_zero, offset, bytes, pnum, + map, file); ++*depth; if (ret < 0 || *pnum == 0 || ret & BDRV_BLOCK_ALLOCATED || bs == base) { return ret; @@ -2707,8 +2666,8 @@ bdrv_co_common_block_status_above(BlockDriverState *bs, for (p = bdrv_filter_or_cow_bs(bs); include_base || p != base; p = bdrv_filter_or_cow_bs(p)) { - ret = bdrv_co_block_status(p, want_zero, offset, bytes, pnum, map, - file); + ret = bdrv_co_do_block_status(p, want_zero, offset, bytes, pnum, + map, file); ++*depth; if (ret < 0) { return ret; @@ -2763,21 +2722,24 @@ bdrv_co_common_block_status_above(BlockDriverState *bs, return ret; } -int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base, - int64_t offset, int64_t bytes, int64_t *pnum, - int64_t *map, BlockDriverState **file) +int coroutine_fn bdrv_co_block_status_above(BlockDriverState *bs, + BlockDriverState *base, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, + BlockDriverState **file) { IO_CODE(); - return bdrv_common_block_status_above(bs, base, false, true, offset, bytes, - pnum, map, file, NULL); + return bdrv_co_common_block_status_above(bs, base, false, true, offset, + bytes, pnum, map, file, NULL); } -int bdrv_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes, - int64_t *pnum, int64_t *map, BlockDriverState **file) +int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, int64_t offset, + int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file) { IO_CODE(); - return bdrv_block_status_above(bs, bdrv_filter_or_cow_bs(bs), - offset, bytes, pnum, map, file); + return bdrv_co_block_status_above(bs, bdrv_filter_or_cow_bs(bs), + offset, bytes, pnum, map, file); } /* @@ -2798,8 +2760,8 @@ int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, return 1; } - ret = bdrv_common_block_status_above(bs, NULL, false, false, offset, - bytes, &pnum, NULL, NULL, NULL); + ret = bdrv_co_common_block_status_above(bs, NULL, false, false, offset, + bytes, &pnum, NULL, NULL, NULL); if (ret < 0) { return ret; @@ -2808,16 +2770,16 @@ int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, return (pnum == bytes) && (ret & BDRV_BLOCK_ZERO); } -int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset, - int64_t bytes, int64_t *pnum) +int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset, + int64_t bytes, int64_t *pnum) { int ret; int64_t dummy; IO_CODE(); - ret = bdrv_common_block_status_above(bs, bs, true, false, offset, - bytes, pnum ? pnum : &dummy, NULL, - NULL, NULL); + ret = bdrv_co_common_block_status_above(bs, bs, true, false, offset, + bytes, pnum ? pnum : &dummy, NULL, + NULL, NULL); if (ret < 0) { return ret; } @@ -2841,16 +2803,18 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset, * words, the result is not necessarily the maximum possible range); * but 'pnum' will only be 0 when end of file is reached. */ -int bdrv_is_allocated_above(BlockDriverState *top, - BlockDriverState *base, - bool include_base, int64_t offset, - int64_t bytes, int64_t *pnum) +int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *bs, + BlockDriverState *base, + bool include_base, int64_t offset, + int64_t bytes, int64_t *pnum) { int depth; - int ret = bdrv_common_block_status_above(top, base, include_base, false, - offset, bytes, pnum, NULL, NULL, - &depth); + int ret; IO_CODE(); + + ret = bdrv_co_common_block_status_above(bs, base, include_base, false, + offset, bytes, pnum, NULL, NULL, + &depth); if (ret < 0) { return ret; } @@ -2868,6 +2832,7 @@ bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) BlockDriverState *child_bs = bdrv_primary_bs(bs); int ret; IO_CODE(); + assert_bdrv_graph_readable(); ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL); if (ret < 0) { @@ -2880,8 +2845,8 @@ bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) bdrv_inc_in_flight(bs); - if (drv->bdrv_load_vmstate) { - ret = drv->bdrv_load_vmstate(bs, qiov, pos); + if (drv->bdrv_co_load_vmstate) { + ret = drv->bdrv_co_load_vmstate(bs, qiov, pos); } else if (child_bs) { ret = bdrv_co_readv_vmstate(child_bs, qiov, pos); } else { @@ -2900,6 +2865,7 @@ bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) BlockDriverState *child_bs = bdrv_primary_bs(bs); int ret; IO_CODE(); + assert_bdrv_graph_readable(); ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL); if (ret < 0) { @@ -2912,8 +2878,8 @@ bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) bdrv_inc_in_flight(bs); - if (drv->bdrv_save_vmstate) { - ret = drv->bdrv_save_vmstate(bs, qiov, pos); + if (drv->bdrv_co_save_vmstate) { + ret = drv->bdrv_co_save_vmstate(bs, qiov, pos); } else if (child_bs) { ret = bdrv_co_writev_vmstate(child_bs, qiov, pos); } else { @@ -2948,25 +2914,18 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, /**************************************************************/ /* async I/Os */ +/** + * Synchronously cancels an acb. Must be called with the BQL held and the acb + * must be processed with the BQL held too (IOThreads are not allowed). + * + * Use bdrv_aio_cancel_async() instead when possible. + */ void bdrv_aio_cancel(BlockAIOCB *acb) { - IO_CODE(); + GLOBAL_STATE_CODE(); qemu_aio_ref(acb); bdrv_aio_cancel_async(acb); - while (acb->refcnt > 1) { - if (acb->aiocb_info->get_aio_context) { - aio_poll(acb->aiocb_info->get_aio_context(acb), true); - } else if (acb->bs) { - /* qemu_aio_ref and qemu_aio_unref are not thread-safe, so - * assert that we're not using an I/O thread. Thread-safe - * code should use bdrv_aio_cancel_async exclusively. - */ - assert(bdrv_get_aio_context(acb->bs) == qemu_get_aio_context()); - aio_poll(bdrv_get_aio_context(acb->bs), true); - } else { - abort(); - } - } + AIO_WAIT_WHILE_UNLOCKED(NULL, acb->refcnt > 1); qemu_aio_unref(acb); } @@ -2992,14 +2951,15 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) int ret = 0; IO_CODE(); + assert_bdrv_graph_readable(); bdrv_inc_in_flight(bs); - if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs) || + if (!bdrv_co_is_inserted(bs) || bdrv_is_read_only(bs) || bdrv_is_sg(bs)) { goto early_exit; } - qemu_co_mutex_lock(&bs->reqs_lock); + qemu_mutex_lock(&bs->reqs_lock); current_gen = qatomic_read(&bs->write_gen); /* Wait until any previous flushes are completed */ @@ -3009,7 +2969,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) /* Flushes reach this point in nondecreasing current_gen order. */ bs->active_flush_req = true; - qemu_co_mutex_unlock(&bs->reqs_lock); + qemu_mutex_unlock(&bs->reqs_lock); /* Write back all layers by calling one driver function */ if (bs->drv->bdrv_co_flush) { @@ -3018,7 +2978,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) } /* Write back cached data to the OS even with cache=unsafe */ - BLKDBG_EVENT(primary_child, BLKDBG_FLUSH_TO_OS); + BLKDBG_CO_EVENT(primary_child, BLKDBG_FLUSH_TO_OS); if (bs->drv->bdrv_co_flush_to_os) { ret = bs->drv->bdrv_co_flush_to_os(bs); if (ret < 0) { @@ -3036,7 +2996,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) goto flush_children; } - BLKDBG_EVENT(primary_child, BLKDBG_FLUSH_TO_DISK); + BLKDBG_CO_EVENT(primary_child, BLKDBG_FLUSH_TO_DISK); if (!bs->drv) { /* bs->drv->bdrv_co_flush() might have ejected the BDS * (even in case of apparent success) */ @@ -3097,11 +3057,11 @@ out: bs->flushed_gen = current_gen; } - qemu_co_mutex_lock(&bs->reqs_lock); + qemu_mutex_lock(&bs->reqs_lock); bs->active_flush_req = false; /* Return value is ignored - it's ok if wait queue is empty */ qemu_co_queue_next(&bs->flush_queue); - qemu_co_mutex_unlock(&bs->reqs_lock); + qemu_mutex_unlock(&bs->reqs_lock); early_exit: bdrv_dec_in_flight(bs); @@ -3117,8 +3077,9 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int head, tail, align; BlockDriverState *bs = child->bs; IO_CODE(); + assert_bdrv_graph_readable(); - if (!bs || !bs->drv || !bdrv_is_inserted(bs)) { + if (!bs || !bs->drv || !bdrv_co_is_inserted(bs)) { return -ENOMEDIUM; } @@ -3228,7 +3189,7 @@ out: return ret; } -int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf) +int coroutine_fn bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf) { BlockDriver *drv = bs->drv; CoroutineIOCompletion co = { @@ -3236,6 +3197,7 @@ int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf) }; BlockAIOCB *acb; IO_CODE(); + assert_bdrv_graph_readable(); bdrv_inc_in_flight(bs); if (!drv || (!drv->bdrv_aio_ioctl && !drv->bdrv_co_ioctl)) { @@ -3258,6 +3220,74 @@ out: return co.ret; } +int coroutine_fn bdrv_co_zone_report(BlockDriverState *bs, int64_t offset, + unsigned int *nr_zones, + BlockZoneDescriptor *zones) +{ + BlockDriver *drv = bs->drv; + CoroutineIOCompletion co = { + .coroutine = qemu_coroutine_self(), + }; + IO_CODE(); + + bdrv_inc_in_flight(bs); + if (!drv || !drv->bdrv_co_zone_report || bs->bl.zoned == BLK_Z_NONE) { + co.ret = -ENOTSUP; + goto out; + } + co.ret = drv->bdrv_co_zone_report(bs, offset, nr_zones, zones); +out: + bdrv_dec_in_flight(bs); + return co.ret; +} + +int coroutine_fn bdrv_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, + int64_t offset, int64_t len) +{ + BlockDriver *drv = bs->drv; + CoroutineIOCompletion co = { + .coroutine = qemu_coroutine_self(), + }; + IO_CODE(); + + bdrv_inc_in_flight(bs); + if (!drv || !drv->bdrv_co_zone_mgmt || bs->bl.zoned == BLK_Z_NONE) { + co.ret = -ENOTSUP; + goto out; + } + co.ret = drv->bdrv_co_zone_mgmt(bs, op, offset, len); +out: + bdrv_dec_in_flight(bs); + return co.ret; +} + +int coroutine_fn bdrv_co_zone_append(BlockDriverState *bs, int64_t *offset, + QEMUIOVector *qiov, + BdrvRequestFlags flags) +{ + int ret; + BlockDriver *drv = bs->drv; + CoroutineIOCompletion co = { + .coroutine = qemu_coroutine_self(), + }; + IO_CODE(); + + ret = bdrv_check_qiov_request(*offset, qiov->size, qiov, 0, NULL); + if (ret < 0) { + return ret; + } + + bdrv_inc_in_flight(bs); + if (!drv || !drv->bdrv_co_zone_append || bs->bl.zoned == BLK_Z_NONE) { + co.ret = -ENOTSUP; + goto out; + } + co.ret = drv->bdrv_co_zone_append(bs, offset, qiov, flags); +out: + bdrv_dec_in_flight(bs); + return co.ret; +} + void *qemu_blockalign(BlockDriverState *bs, size_t size) { IO_CODE(); @@ -3296,89 +3326,67 @@ void *qemu_try_blockalign0(BlockDriverState *bs, size_t size) return mem; } -/* - * Check if all memory in this vector is sector aligned. - */ -bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) +/* Helper that undoes bdrv_register_buf() when it fails partway through */ +static void GRAPH_RDLOCK +bdrv_register_buf_rollback(BlockDriverState *bs, void *host, size_t size, + BdrvChild *final_child) { - int i; - size_t alignment = bdrv_min_mem_align(bs); - IO_CODE(); + BdrvChild *child; - for (i = 0; i < qiov->niov; i++) { - if ((uintptr_t) qiov->iov[i].iov_base % alignment) { - return false; + GLOBAL_STATE_CODE(); + assert_bdrv_graph_readable(); + + QLIST_FOREACH(child, &bs->children, next) { + if (child == final_child) { + break; } - if (qiov->iov[i].iov_len % alignment) { + + bdrv_unregister_buf(child->bs, host, size); + } + + if (bs->drv && bs->drv->bdrv_unregister_buf) { + bs->drv->bdrv_unregister_buf(bs, host, size); + } +} + +bool bdrv_register_buf(BlockDriverState *bs, void *host, size_t size, + Error **errp) +{ + BdrvChild *child; + + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + if (bs->drv && bs->drv->bdrv_register_buf) { + if (!bs->drv->bdrv_register_buf(bs, host, size, errp)) { + return false; + } + } + QLIST_FOREACH(child, &bs->children, next) { + if (!bdrv_register_buf(child->bs, host, size, errp)) { + bdrv_register_buf_rollback(bs, host, size, child); return false; } } - return true; } -void bdrv_io_plug(BlockDriverState *bs) -{ - BdrvChild *child; - IO_CODE(); - - QLIST_FOREACH(child, &bs->children, next) { - bdrv_io_plug(child->bs); - } - - if (qatomic_fetch_inc(&bs->io_plugged) == 0) { - BlockDriver *drv = bs->drv; - if (drv && drv->bdrv_io_plug) { - drv->bdrv_io_plug(bs); - } - } -} - -void bdrv_io_unplug(BlockDriverState *bs) -{ - BdrvChild *child; - IO_CODE(); - - assert(bs->io_plugged); - if (qatomic_fetch_dec(&bs->io_plugged) == 1) { - BlockDriver *drv = bs->drv; - if (drv && drv->bdrv_io_unplug) { - drv->bdrv_io_unplug(bs); - } - } - - QLIST_FOREACH(child, &bs->children, next) { - bdrv_io_unplug(child->bs); - } -} - -void bdrv_register_buf(BlockDriverState *bs, void *host, size_t size) +void bdrv_unregister_buf(BlockDriverState *bs, void *host, size_t size) { BdrvChild *child; GLOBAL_STATE_CODE(); - if (bs->drv && bs->drv->bdrv_register_buf) { - bs->drv->bdrv_register_buf(bs, host, size); - } - QLIST_FOREACH(child, &bs->children, next) { - bdrv_register_buf(child->bs, host, size); - } -} + GRAPH_RDLOCK_GUARD_MAINLOOP(); -void bdrv_unregister_buf(BlockDriverState *bs, void *host) -{ - BdrvChild *child; - - GLOBAL_STATE_CODE(); if (bs->drv && bs->drv->bdrv_unregister_buf) { - bs->drv->bdrv_unregister_buf(bs, host); + bs->drv->bdrv_unregister_buf(bs, host, size); } QLIST_FOREACH(child, &bs->children, next) { - bdrv_unregister_buf(child->bs, host); + bdrv_unregister_buf(child->bs, host, size); } } -static int coroutine_fn bdrv_co_copy_range_internal( +static int coroutine_fn GRAPH_RDLOCK bdrv_co_copy_range_internal( BdrvChild *src, int64_t src_offset, BdrvChild *dst, int64_t dst_offset, int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags, @@ -3386,6 +3394,7 @@ static int coroutine_fn bdrv_co_copy_range_internal( { BdrvTrackedRequest req; int ret; + assert_bdrv_graph_readable(); /* TODO We can support BDRV_REQ_NO_FALLBACK here */ assert(!(read_flags & BDRV_REQ_NO_FALLBACK)); @@ -3393,7 +3402,7 @@ static int coroutine_fn bdrv_co_copy_range_internal( assert(!(read_flags & BDRV_REQ_NO_WAIT)); assert(!(write_flags & BDRV_REQ_NO_WAIT)); - if (!dst || !dst->bs || !bdrv_is_inserted(dst->bs)) { + if (!dst || !dst->bs || !bdrv_co_is_inserted(dst->bs)) { return -ENOMEDIUM; } ret = bdrv_check_request32(dst_offset, bytes, NULL, 0); @@ -3404,7 +3413,7 @@ static int coroutine_fn bdrv_co_copy_range_internal( return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, write_flags); } - if (!src || !src->bs || !bdrv_is_inserted(src->bs)) { + if (!src || !src->bs || !bdrv_co_is_inserted(src->bs)) { return -ENOMEDIUM; } ret = bdrv_check_request32(src_offset, bytes, NULL, 0); @@ -3467,6 +3476,7 @@ int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, BdrvRequestFlags write_flags) { IO_CODE(); + assert_bdrv_graph_readable(); trace_bdrv_co_copy_range_from(src, src_offset, dst, dst_offset, bytes, read_flags, write_flags); return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, @@ -3484,6 +3494,7 @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, BdrvRequestFlags write_flags) { IO_CODE(); + assert_bdrv_graph_readable(); trace_bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, read_flags, write_flags); return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, @@ -3496,14 +3507,20 @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, BdrvRequestFlags write_flags) { IO_CODE(); + assert_bdrv_graph_readable(); + return bdrv_co_copy_range_from(src, src_offset, dst, dst_offset, bytes, read_flags, write_flags); } -static void bdrv_parent_cb_resize(BlockDriverState *bs) +static void coroutine_fn GRAPH_RDLOCK +bdrv_parent_cb_resize(BlockDriverState *bs) { BdrvChild *c; + + assert_bdrv_graph_readable(); + QLIST_FOREACH(c, &bs->parents, next_parent) { if (c->klass->resize) { c->klass->resize(c); @@ -3529,6 +3546,7 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, int64_t old_size, new_bytes; int ret; IO_CODE(); + assert_bdrv_graph_readable(); /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ if (!drv) { @@ -3545,7 +3563,7 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, return ret; } - old_size = bdrv_getlength(bs); + old_size = bdrv_co_getlength(bs); if (old_size < 0) { error_setg_errno(errp, -old_size, "Failed to get old image size"); return old_size; @@ -3596,7 +3614,7 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, if (new_bytes && backing) { int64_t backing_len; - backing_len = bdrv_getlength(backing->bs); + backing_len = bdrv_co_getlength(backing->bs); if (backing_len < 0) { ret = backing_len; error_setg_errno(errp, -ret, "Could not get backing file size"); @@ -3626,15 +3644,17 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, goto out; } - ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); + ret = bdrv_co_refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); if (ret < 0) { error_setg_errno(errp, -ret, "Could not refresh total sector count"); } else { offset = bs->total_sectors * BDRV_SECTOR_SIZE; } - /* It's possible that truncation succeeded but refresh_total_sectors + /* + * It's possible that truncation succeeded but bdrv_refresh_total_sectors * failed, but the latter doesn't affect how we should finish the request. - * Pass 0 as the last parameter so that dirty bitmaps etc. are handled. */ + * Pass 0 as the last parameter so that dirty bitmaps etc. are handled. + */ bdrv_co_write_req_finish(child, offset - new_bytes, new_bytes, &req, 0); out: @@ -3647,6 +3667,8 @@ out: void bdrv_cancel_in_flight(BlockDriverState *bs) { GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!bs || !bs->drv) { return; } @@ -3664,6 +3686,7 @@ bdrv_co_preadv_snapshot(BdrvChild *child, int64_t offset, int64_t bytes, BlockDriver *drv = bs->drv; int ret; IO_CODE(); + assert_bdrv_graph_readable(); if (!drv) { return -ENOMEDIUM; @@ -3689,6 +3712,7 @@ bdrv_co_snapshot_block_status(BlockDriverState *bs, BlockDriver *drv = bs->drv; int ret; IO_CODE(); + assert_bdrv_graph_readable(); if (!drv) { return -ENOMEDIUM; @@ -3712,6 +3736,7 @@ bdrv_co_pdiscard_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes) BlockDriver *drv = bs->drv; int ret; IO_CODE(); + assert_bdrv_graph_readable(); if (!drv) { return -ENOMEDIUM; diff --git a/block/io_uring.c b/block/io_uring.c index 0b401512b9..d11b2051ab 100644 --- a/block/io_uring.c +++ b/block/io_uring.c @@ -15,9 +15,14 @@ #include "block/block.h" #include "block/raw-aio.h" #include "qemu/coroutine.h" +#include "qemu/defer-call.h" #include "qapi/error.h" +#include "sysemu/block-backend.h" #include "trace.h" +/* Only used for assertions. */ +#include "qemu/coroutine_int.h" + /* io_uring ring size */ #define MAX_ENTRIES 128 @@ -38,24 +43,22 @@ typedef struct LuringAIOCB { } 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 { +struct LuringState { AioContext *aio_context; struct io_uring ring; - /* io queue for submit at batch. Protected by AioContext lock. */ + /* No locking required, only accessed from AioContext home thread */ LuringQueue io_q; - /* I/O completion processing. Only runs in I/O thread. */ QEMUBH *completion_bh; -} LuringState; +}; /** * luring_resubmit: @@ -72,12 +75,8 @@ static void luring_resubmit(LuringState *s, LuringAIOCB *luringcb) /** * 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. + * Short reads are rare but may occur. The remaining read request needs to be + * resubmitted. */ static void luring_resubmit_short_read(LuringState *s, LuringAIOCB *luringcb, int nread) @@ -88,7 +87,7 @@ static void luring_resubmit_short_read(LuringState *s, LuringAIOCB *luringcb, trace_luring_resubmit_short_read(s, luringcb, nread); /* Update read position */ - luringcb->total_read = nread; + luringcb->total_read += nread; remaining = luringcb->qiov->size - luringcb->total_read; /* Shorten qiov */ @@ -102,8 +101,8 @@ static void luring_resubmit_short_read(LuringState *s, LuringAIOCB *luringcb, remaining); /* Update sqe */ - luringcb->sqeq.off = nread; - luringcb->sqeq.addr = (__u64)(uintptr_t)luringcb->resubmit_qiov.iov; + luringcb->sqeq.off += nread; + luringcb->sqeq.addr = (uintptr_t)luringcb->resubmit_qiov.iov; luringcb->sqeq.len = luringcb->resubmit_qiov.niov; luring_resubmit(s, luringcb); @@ -126,6 +125,9 @@ static void luring_process_completions(LuringState *s) { struct io_uring_cqe *cqes; int total_bytes; + + defer_call_begin(); + /* * Request completion callbacks can run the nested event loop. * Schedule ourselves so the nested event loop will "see" remaining @@ -213,11 +215,15 @@ end: * eventually runs later. Coroutines cannot be entered recursively * so avoid doing that! */ + assert(luringcb->co->ctx == s->aio_context); if (!qemu_coroutine_entered(luringcb->co)) { aio_co_wake(luringcb->co); } } + qemu_bh_cancel(s->completion_bh); + + defer_call_end(); } static int ioq_submit(LuringState *s) @@ -266,13 +272,11 @@ static int ioq_submit(LuringState *s) 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) { + if (s->io_q.in_queue > 0) { ioq_submit(s); } - aio_context_release(s->aio_context); } static void qemu_luring_completion_bh(void *opaque) @@ -304,25 +308,17 @@ static void qemu_luring_poll_ready(void *opaque) 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) +static void luring_deferred_fn(void *opaque) { - 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) { + LuringState *s = opaque; + trace_luring_unplug_fn(s, s->io_q.blocked, s->io_q.in_queue, + s->io_q.in_flight); + if (!s->io_q.blocked && s->io_q.in_queue > 0) { ioq_submit(s); } } @@ -349,6 +345,10 @@ static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s, io_uring_prep_writev(sqes, fd, luringcb->qiov->iov, luringcb->qiov->niov, offset); break; + case QEMU_AIO_ZONE_APPEND: + 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); @@ -365,22 +365,26 @@ static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s, 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; + trace_luring_do_submit(s, s->io_q.blocked, s->io_q.in_queue, + s->io_q.in_flight); + if (!s->io_q.blocked) { + if (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; + } + + defer_call(luring_deferred_fn, s); } return 0; } -int coroutine_fn luring_co_submit(BlockDriverState *bs, LuringState *s, int fd, - uint64_t offset, QEMUIOVector *qiov, int type) +int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd, uint64_t offset, + QEMUIOVector *qiov, int type) { int ret; + AioContext *ctx = qemu_get_current_aio_context(); + LuringState *s = aio_get_linux_io_uring(ctx); LuringAIOCB luringcb = { .co = qemu_coroutine_self(), .ret = -EINPROGRESS, @@ -403,7 +407,7 @@ int coroutine_fn luring_co_submit(BlockDriverState *bs, LuringState *s, int fd, void luring_detach_aio_context(LuringState *s, AioContext *old_context) { - aio_set_fd_handler(old_context, s->ring.ring_fd, false, + aio_set_fd_handler(old_context, s->ring.ring_fd, NULL, NULL, NULL, NULL, s); qemu_bh_delete(s->completion_bh); s->aio_context = NULL; @@ -413,7 +417,7 @@ 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, + aio_set_fd_handler(s->aio_context, s->ring.ring_fd, qemu_luring_completion_cb, NULL, qemu_luring_poll_cb, qemu_luring_poll_ready, s); } @@ -428,7 +432,7 @@ LuringState *luring_init(Error **errp) rc = io_uring_queue_init(MAX_ENTRIES, ring, 0); if (rc < 0) { - error_setg_errno(errp, errno, "failed to init linux io_uring ring"); + error_setg_errno(errp, -rc, "failed to init linux io_uring ring"); g_free(s); return NULL; } diff --git a/block/iscsi.c b/block/iscsi.c index d707d0b354..2ff14b7472 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -33,6 +33,7 @@ #include "qemu/error-report.h" #include "qemu/bitops.h" #include "qemu/bitmap.h" +#include "block/block-io.h" #include "block/block_int.h" #include "block/qdict.h" #include "scsi/constants.h" @@ -268,6 +269,7 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, timer_mod(&iTask->retry_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + retry_time); iTask->do_retry = 1; + return; } else if (status == SCSI_STATUS_CHECK_CONDITION) { int error = iscsi_translate_sense(&task->sense); if (error == EAGAIN) { @@ -290,7 +292,8 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, } } -static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask) +static void coroutine_fn +iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask) { *iTask = (struct IscsiTask) { .co = qemu_coroutine_self(), @@ -360,7 +363,6 @@ iscsi_set_events(IscsiLun *iscsilun) if (ev != iscsilun->events) { aio_set_fd_handler(iscsilun->aio_context, iscsi_get_fd(iscsi), - false, (ev & POLLIN) ? iscsi_process_read : NULL, (ev & POLLOUT) ? iscsi_process_write : NULL, NULL, NULL, @@ -1056,6 +1058,7 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, return NULL; } + /* Must use malloc(): this is freed via scsi_free_scsi_task() */ acb->task = malloc(sizeof(struct scsi_task)); if (acb->task == NULL) { error_report("iSCSI: Failed to allocate task for scsi command. %s", @@ -1125,8 +1128,8 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, #endif -static int64_t -iscsi_getlength(BlockDriverState *bs) +static int64_t coroutine_fn +iscsi_co_getlength(BlockDriverState *bs) { IscsiLun *iscsilun = bs->opaque; int64_t len; @@ -1351,6 +1354,9 @@ static void apply_chap(struct iscsi_context *iscsi, QemuOpts *opts, } else if (!password) { error_setg(errp, "CHAP username specified but no password was given"); return; + } else { + warn_report("iSCSI block driver 'password' option is deprecated, " + "use 'password-secret' instead"); } if (iscsi_set_initiator_username_pwd(iscsi, user, password)) { @@ -1534,7 +1540,7 @@ static void iscsi_detach_aio_context(BlockDriverState *bs) IscsiLun *iscsilun = bs->opaque; aio_set_fd_handler(iscsilun->aio_context, iscsi_get_fd(iscsilun->iscsi), - false, NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL, NULL); iscsilun->events = 0; if (iscsilun->nop_timer) { @@ -1919,7 +1925,9 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, /* Check the write protect flag of the LUN if we want to write */ if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) && iscsilun->write_protected) { + bdrv_graph_rdlock_main_loop(); ret = bdrv_apply_auto_read_only(bs, "LUN is write protected", errp); + bdrv_graph_rdunlock_main_loop(); if (ret < 0) { goto out; } @@ -2065,7 +2073,7 @@ static void iscsi_refresh_limits(BlockDriverState *bs, Error **errp) uint64_t max_xfer_len = iscsilun->use_16_for_rw ? 0xffffffff : 0xffff; unsigned int block_size = MAX(BDRV_SECTOR_SIZE, iscsilun->block_size); - assert(iscsilun->block_size >= BDRV_SECTOR_SIZE || bs->sg); + assert(iscsilun->block_size >= BDRV_SECTOR_SIZE || bdrv_is_sg(bs)); bs->bl.request_alignment = block_size; @@ -2153,7 +2161,7 @@ static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset, return -EIO; } - cur_length = iscsi_getlength(bs); + cur_length = iscsi_co_getlength(bs); if (offset != cur_length && exact) { error_setg(errp, "Cannot resize iSCSI devices"); return -ENOTSUP; @@ -2169,7 +2177,8 @@ static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset, return 0; } -static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +iscsi_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { IscsiLun *iscsilun = bs->opaque; bdi->cluster_size = iscsilun->cluster_size; @@ -2183,14 +2192,12 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs, iscsi_allocmap_invalidate(iscsilun); } -static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs, - BdrvChild *src, - int64_t src_offset, - BdrvChild *dst, - int64_t dst_offset, - int64_t bytes, - BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags) +static int coroutine_fn GRAPH_RDLOCK +iscsi_co_copy_range_from(BlockDriverState *bs, + BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, read_flags, write_flags); @@ -2324,14 +2331,12 @@ static void iscsi_xcopy_data(struct iscsi_data *data, src_lba, dst_lba); } -static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs, - BdrvChild *src, - int64_t src_offset, - BdrvChild *dst, - int64_t dst_offset, - int64_t bytes, - BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags) +static int coroutine_fn GRAPH_RDLOCK +iscsi_co_copy_range_to(BlockDriverState *bs, + BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { IscsiLun *dst_lun = dst->bs->opaque; IscsiLun *src_lun; @@ -2432,8 +2437,8 @@ static BlockDriver bdrv_iscsi = { .bdrv_reopen_commit = iscsi_reopen_commit, .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache, - .bdrv_getlength = iscsi_getlength, - .bdrv_get_info = iscsi_get_info, + .bdrv_co_getlength = iscsi_co_getlength, + .bdrv_co_get_info = iscsi_co_get_info, .bdrv_co_truncate = iscsi_co_truncate, .bdrv_refresh_limits = iscsi_refresh_limits, @@ -2471,8 +2476,8 @@ static BlockDriver bdrv_iser = { .bdrv_reopen_commit = iscsi_reopen_commit, .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache, - .bdrv_getlength = iscsi_getlength, - .bdrv_get_info = iscsi_get_info, + .bdrv_co_getlength = iscsi_co_getlength, + .bdrv_co_get_info = iscsi_co_get_info, .bdrv_co_truncate = iscsi_co_truncate, .bdrv_refresh_limits = iscsi_refresh_limits, diff --git a/block/linux-aio.c b/block/linux-aio.c index 4c423fcccf..ec05d946f3 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -14,7 +14,12 @@ #include "block/raw-aio.h" #include "qemu/event_notifier.h" #include "qemu/coroutine.h" +#include "qemu/defer-call.h" #include "qapi/error.h" +#include "sysemu/block-backend.h" + +/* Only used for assertions. */ +#include "qemu/coroutine_int.h" #include @@ -43,7 +48,6 @@ struct qemu_laiocb { }; typedef struct { - int plugged; unsigned int in_queue; unsigned int in_flight; bool blocked; @@ -56,10 +60,8 @@ struct LinuxAioState { io_context_t ctx; EventNotifier e; - /* io queue for submit at batch. Protected by AioContext lock. */ + /* No locking required, only accessed from AioContext home thread */ LaioQueue io_q; - - /* I/O completion processing. Only runs in I/O thread. */ QEMUBH *completion_bh; int event_idx; int event_max; @@ -102,6 +104,7 @@ static void qemu_laio_process_completion(struct qemu_laiocb *laiocb) * later. Coroutines cannot be entered recursively so avoid doing * that! */ + assert(laiocb->co->ctx == laiocb->ctx->aio_context); if (!qemu_coroutine_entered(laiocb->co)) { aio_co_wake(laiocb->co); } @@ -202,6 +205,8 @@ static void qemu_laio_process_completions(LinuxAioState *s) { struct io_event *events; + defer_call_begin(); + /* Reschedule so nested event loops see currently pending completions */ qemu_bh_schedule(s->completion_bh); @@ -225,20 +230,20 @@ static void qemu_laio_process_completions(LinuxAioState *s) /* If we are nested we have to notify the level above that we are done * by setting event_max to zero, upper level will then jump out of it's - * own `for` loop. If we are the last all counters droped to zero. */ + * own `for` loop. If we are the last all counters dropped to zero. */ s->event_max = 0; s->event_idx = 0; + + defer_call_end(); } static void qemu_laio_process_completions_and_submit(LinuxAioState *s) { - aio_context_acquire(s->aio_context); qemu_laio_process_completions(s); - if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { + if (!QSIMPLEQ_EMPTY(&s->io_q.pending)) { ioq_submit(s); } - aio_context_release(s->aio_context); } static void qemu_laio_completion_bh(void *opaque) @@ -277,7 +282,6 @@ static void qemu_laio_poll_ready(EventNotifier *opaque) static void ioq_init(LaioQueue *io_q) { QSIMPLEQ_INIT(&io_q->pending); - io_q->plugged = 0; io_q->in_queue = 0; io_q->in_flight = 0; io_q->blocked = false; @@ -354,18 +358,11 @@ static uint64_t laio_max_batch(LinuxAioState *s, uint64_t dev_max_batch) return max_batch; } -void laio_io_plug(BlockDriverState *bs, LinuxAioState *s) +static void laio_deferred_fn(void *opaque) { - s->io_q.plugged++; -} + LinuxAioState *s = opaque; -void laio_io_unplug(BlockDriverState *bs, LinuxAioState *s, - uint64_t dev_max_batch) -{ - assert(s->io_q.plugged); - if (s->io_q.in_queue >= laio_max_batch(s, dev_max_batch) || - (--s->io_q.plugged == 0 && - !s->io_q.blocked && !QSIMPLEQ_EMPTY(&s->io_q.pending))) { + if (!s->io_q.blocked && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { ioq_submit(s); } } @@ -381,6 +378,9 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, case QEMU_AIO_WRITE: io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); break; + case QEMU_AIO_ZONE_APPEND: + io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); + break; case QEMU_AIO_READ: io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset); break; @@ -394,24 +394,26 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, QSIMPLEQ_INSERT_TAIL(&s->io_q.pending, laiocb, next); s->io_q.in_queue++; - if (!s->io_q.blocked && - (!s->io_q.plugged || - s->io_q.in_queue >= laio_max_batch(s, dev_max_batch))) { - ioq_submit(s); + if (!s->io_q.blocked) { + if (s->io_q.in_queue >= laio_max_batch(s, dev_max_batch)) { + ioq_submit(s); + } else { + defer_call(laio_deferred_fn, s); + } } return 0; } -int coroutine_fn laio_co_submit(BlockDriverState *bs, LinuxAioState *s, int fd, - uint64_t offset, QEMUIOVector *qiov, int type, - uint64_t dev_max_batch) +int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, + int type, uint64_t dev_max_batch) { int ret; + AioContext *ctx = qemu_get_current_aio_context(); struct qemu_laiocb laiocb = { .co = qemu_coroutine_self(), .nbytes = qiov->size, - .ctx = s, + .ctx = aio_get_linux_aio(ctx), .ret = -EINPROGRESS, .is_read = (type == QEMU_AIO_READ), .qiov = qiov, @@ -430,7 +432,7 @@ int coroutine_fn laio_co_submit(BlockDriverState *bs, LinuxAioState *s, int fd, void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context) { - aio_set_event_notifier(old_context, &s->e, false, NULL, NULL, NULL); + aio_set_event_notifier(old_context, &s->e, NULL, NULL, NULL); qemu_bh_delete(s->completion_bh); s->aio_context = NULL; } @@ -439,7 +441,7 @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context) { s->aio_context = new_context; s->completion_bh = aio_bh_new(new_context, qemu_laio_completion_bh, s); - aio_set_event_notifier(new_context, &s->e, false, + aio_set_event_notifier(new_context, &s->e, qemu_laio_completion_cb, qemu_laio_poll_cb, qemu_laio_poll_ready); @@ -453,7 +455,7 @@ LinuxAioState *laio_init(Error **errp) s = g_malloc0(sizeof(*s)); rc = event_notifier_init(&s->e, false); if (rc < 0) { - error_setg_errno(errp, -rc, "failed to to initialize event notifier"); + error_setg_errno(errp, -rc, "failed to initialize event notifier"); goto out_free_state; } diff --git a/block/meson.build b/block/meson.build index 0b2a60c99b..e1f03fd773 100644 --- a/block/meson.build +++ b/block/meson.build @@ -4,48 +4,45 @@ block_ss.add(files( 'aio_task.c', 'amend.c', 'backup.c', - 'copy-before-write.c', 'blkdebug.c', 'blklogwrites.c', 'blkverify.c', 'block-backend.c', 'block-copy.c', 'commit.c', + 'copy-before-write.c', 'copy-on-read.c', - 'preallocate.c', - 'progress_meter.c', 'create.c', 'crypto.c', 'dirty-bitmap.c', 'filter-compress.c', + 'graph-lock.c', 'io.c', 'mirror.c', 'nbd.c', 'null.c', + 'preallocate.c', + 'progress_meter.c', 'qapi.c', + 'qcow2.c', 'qcow2-bitmap.c', 'qcow2-cache.c', 'qcow2-cluster.c', 'qcow2-refcount.c', 'qcow2-snapshot.c', 'qcow2-threads.c', - 'qcow2.c', 'quorum.c', 'raw-format.c', 'reqlist.c', 'snapshot.c', 'snapshot-access.c', - 'throttle-groups.c', 'throttle.c', - 'vhdx-endian.c', - 'vhdx-log.c', - 'vhdx.c', - 'vmdk.c', - 'vpc.c', + 'throttle-groups.c', 'write-threshold.c', ), zstd, zlib, gnutls) -softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('blkreplay.c')) +system_ss.add(when: 'CONFIG_TCG', if_true: files('blkreplay.c')) +system_ss.add(files('block-ram-registrar.c')) if get_option('qcow1').allowed() block_ss.add(files('qcow.c')) @@ -53,6 +50,19 @@ endif if get_option('vdi').allowed() block_ss.add(files('vdi.c')) endif +if get_option('vhdx').allowed() + block_ss.add(files( + 'vhdx-endian.c', + 'vhdx-log.c', + 'vhdx.c' + )) +endif +if get_option('vmdk').allowed() + block_ss.add(files('vmdk.c')) +endif +if get_option('vpc').allowed() + block_ss.add(files('vpc.c')) +endif if get_option('cloop').allowed() block_ss.add(files('cloop.c')) endif @@ -78,11 +88,16 @@ if get_option('parallels').allowed() block_ss.add(files('parallels.c', 'parallels-ext.c')) endif -block_ss.add(when: 'CONFIG_WIN32', if_true: files('file-win32.c', 'win32-aio.c')) -block_ss.add(when: 'CONFIG_POSIX', if_true: [files('file-posix.c'), coref, iokit]) +if host_os == 'windows' + block_ss.add(files('file-win32.c', 'win32-aio.c')) +else + block_ss.add(files('file-posix.c'), coref, iokit) +endif block_ss.add(when: libiscsi, if_true: files('iscsi-opts.c')) -block_ss.add(when: 'CONFIG_LINUX', if_true: files('nvme.c')) -if not get_option('replication').disabled() +if host_os == 'linux' + block_ss.add(files('nvme.c')) +endif +if get_option('replication').allowed() block_ss.add(files('replication.c')) endif block_ss.add(when: libaio, if_true: files('linux-aio.c')) @@ -92,6 +107,7 @@ block_modules = {} modsrc = [] foreach m : [ + [blkio, 'blkio', files('blkio.c')], [curl, 'curl', files('curl.c')], [glusterfs, 'gluster', files('gluster.c')], [libiscsi, 'iscsi', [files('iscsi.c'), libm]], @@ -135,7 +151,11 @@ block_gen_c = custom_target('block-gen.c', output: 'block-gen.c', input: files( '../include/block/block-io.h', + '../include/block/dirty-bitmap.h', + '../include/block/block_int-io.h', '../include/block/block-global-state.h', + '../include/sysemu/block-backend-global-state.h', + '../include/sysemu/block-backend-io.h', 'coroutines.h' ), command: [wrapper_py, '@OUTPUT@', '@INPUT@']) @@ -143,7 +163,7 @@ block_ss.add(block_gen_c) block_ss.add(files('stream.c')) -softmmu_ss.add(files('qapi-sysemu.c')) +system_ss.add(files('qapi-sysemu.c')) subdir('export') subdir('monitor') diff --git a/block/mirror.c b/block/mirror.c index d8ecb9efa2..1bdce3b657 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -18,9 +18,9 @@ #include "trace.h" #include "block/blockjob_int.h" #include "block/block_int.h" +#include "block/dirty-bitmap.h" #include "sysemu/block-backend.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qemu/ratelimit.h" #include "qemu/bitmap.h" #include "qemu/memalign.h" @@ -55,10 +55,18 @@ typedef struct MirrorBlockJob { BlockMirrorBackingMode backing_mode; /* Whether the target image requires explicit zero-initialization */ bool zero_target; + /* + * To be accesssed with atomics. Written only under the BQL (required by the + * current implementation of mirror_change()). + */ MirrorCopyMode copy_mode; BlockdevOnError on_source_error, on_target_error; - /* Set when the target is synced (dirty bitmap is clean, nothing - * in flight) and the job is running in active mode */ + /* + * To be accessed with atomics. + * + * Set when the target is synced (dirty bitmap is clean, nothing in flight) + * and the job is running in active mode. + */ bool actively_synced; bool should_complete; int64_t granularity; @@ -73,7 +81,7 @@ typedef struct MirrorBlockJob { uint64_t last_pause_ns; unsigned long *in_flight_bitmap; - int in_flight; + unsigned in_flight; int64_t bytes_in_flight; QTAILQ_HEAD(, MirrorOp) ops_in_flight; int ret; @@ -82,6 +90,7 @@ typedef struct MirrorBlockJob { int max_iov; bool initial_zeroing_ongoing; int in_active_write_counter; + int64_t active_write_bytes_in_flight; bool prepared; bool in_drain; } MirrorBlockJob; @@ -121,7 +130,7 @@ typedef enum MirrorMethod { static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, int error) { - s->actively_synced = false; + qatomic_set(&s->actively_synced, false); if (read) { return block_job_error_action(&s->common, s->on_source_error, true, error); @@ -269,8 +278,8 @@ static inline int64_t mirror_clip_bytes(MirrorBlockJob *s, /* Round offset and/or bytes to target cluster if COW is needed, and * return the offset of the adjusted tail against original. */ -static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset, - uint64_t *bytes) +static int coroutine_fn mirror_cow_align(MirrorBlockJob *s, int64_t *offset, + uint64_t *bytes) { bool need_cow; int ret = 0; @@ -282,8 +291,8 @@ static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset, need_cow |= !test_bit((*offset + *bytes - 1) / s->granularity, s->cow_bitmap); if (need_cow) { - bdrv_round_to_clusters(blk_bs(s->target), *offset, *bytes, - &align_offset, &align_bytes); + bdrv_round_to_subclusters(blk_bs(s->target), *offset, *bytes, + &align_offset, &align_bytes); } if (align_bytes > max_bytes) { @@ -304,19 +313,21 @@ static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset, } static inline void coroutine_fn -mirror_wait_for_any_operation(MirrorBlockJob *s, bool active) +mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) { MirrorOp *op; QTAILQ_FOREACH(op, &s->ops_in_flight, next) { - /* Do not wait on pseudo ops, because it may in turn wait on + /* + * Do not wait on pseudo ops, because it may in turn wait on * some other operation to start, which may in fact be the * 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_in_flight && - op->is_active_write == active) - { + * to wait on. + * Also, do not wait on active operations, because they do not + * use up in-flight slots. + */ + if (!op->is_pseudo_op && op->is_in_flight && !op->is_active_write) { qemu_co_queue_wait(&op->waiting_requests, NULL); return; } @@ -324,13 +335,6 @@ mirror_wait_for_any_operation(MirrorBlockJob *s, bool active) abort(); } -static inline void coroutine_fn -mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) -{ - /* Only non-active operations use up in-flight slots */ - mirror_wait_for_any_operation(s, false); -} - /* Perform a mirror copy operation. * * *op->bytes_handled is set to the number of bytes copied after and @@ -393,8 +397,10 @@ static void coroutine_fn mirror_co_read(void *opaque) 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, - &op->qiov, 0); + WITH_GRAPH_RDLOCK_GUARD() { + ret = bdrv_co_preadv(s->mirror_top_bs->backing, op->offset, op->bytes, + &op->qiov, 0); + } mirror_read_complete(op, ret); } @@ -473,17 +479,20 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, return bytes_handled; } -static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) +static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s) { - BlockDriverState *source = s->mirror_top_bs->backing->bs; + BlockDriverState *source; MirrorOp *pseudo_op; int64_t offset; - uint64_t delay_ns = 0, ret = 0; /* At least the first dirty chunk is mirrored in one iteration. */ int nb_chunks = 1; bool write_zeroes_ok = bdrv_can_write_zeroes_with_unmap(blk_bs(s->target)); int max_io_bytes = MAX(s->buf_size / MAX_IN_FLIGHT, MAX_IO_BYTES); + bdrv_graph_co_rdlock(); + source = s->mirror_top_bs->backing->bs; + bdrv_graph_co_rdunlock(); + bdrv_dirty_bitmap_lock(s->dirty_bitmap); offset = bdrv_dirty_iter_next(s->dbi); if (offset < 0) { @@ -494,11 +503,18 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) } bdrv_dirty_bitmap_unlock(s->dirty_bitmap); + /* + * Wait for concurrent requests to @offset. The next loop will limit the + * copied area based on in_flight_bitmap so we only copy an area that does + * not overlap with concurrent in-flight requests. Still, we would like to + * copy something, so wait until there are at least no more requests to the + * very beginning of the area. + */ mirror_wait_on_conflicts(NULL, s, offset, 1); job_pause_point(&s->common.job); - /* Find the number of consective dirty chunks following the first dirty + /* Find the number of consecutive dirty chunks following the first dirty * one, and wait for in flight requests in them. */ bdrv_dirty_bitmap_lock(s->dirty_bitmap); while (nb_chunks * s->granularity < s->buf_size) { @@ -554,9 +570,11 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) MirrorMethod mirror_method = MIRROR_METHOD_COPY; assert(!(offset % s->granularity)); - ret = bdrv_block_status_above(source, NULL, offset, - nb_chunks * s->granularity, - &io_bytes, NULL, NULL); + WITH_GRAPH_RDLOCK_GUARD() { + ret = bdrv_co_block_status_above(source, NULL, offset, + nb_chunks * s->granularity, + &io_bytes, NULL, NULL); + } if (ret < 0) { io_bytes = MIN(nb_chunks * s->granularity, max_io_bytes); } else if (ret & BDRV_BLOCK_DATA) { @@ -569,8 +587,10 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) } else if (ret >= 0 && !(ret & BDRV_BLOCK_DATA)) { int64_t target_offset; int64_t target_bytes; - bdrv_round_to_clusters(blk_bs(s->target), offset, io_bytes, - &target_offset, &target_bytes); + WITH_GRAPH_RDLOCK_GUARD() { + bdrv_round_to_subclusters(blk_bs(s->target), offset, io_bytes, + &target_offset, &target_bytes); + } if (target_offset == offset && target_bytes == io_bytes) { mirror_method = ret & BDRV_BLOCK_ZERO ? @@ -599,16 +619,13 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) assert(io_bytes); offset += io_bytes; nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity); - delay_ns = block_job_ratelimit_get_delay(&s->common, io_bytes_acct); + block_job_ratelimit_processed_bytes(&s->common, io_bytes_acct); } - ret = delay_ns; fail: QTAILQ_REMOVE(&s->ops_in_flight, pseudo_op, next); qemu_co_queue_restart_all(&pseudo_op->waiting_requests); g_free(pseudo_op); - - return ret; } static void mirror_free_init(MirrorBlockJob *s) @@ -649,7 +666,6 @@ static int mirror_exit_common(Job *job) MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); BlockJob *bjob = &s->common; MirrorBDSOpaque *bs_opaque; - AioContext *replace_aio_context = NULL; BlockDriverState *src; BlockDriverState *target_bs; BlockDriverState *mirror_top_bs; @@ -657,11 +673,15 @@ static int mirror_exit_common(Job *job) bool abort = job->ret < 0; int ret = 0; + GLOBAL_STATE_CODE(); + if (s->prepared) { return 0; } s->prepared = true; + bdrv_graph_rdlock_main_loop(); + mirror_top_bs = s->mirror_top_bs; bs_opaque = mirror_top_bs->opaque; src = mirror_top_bs->backing->bs; @@ -679,6 +699,8 @@ static int mirror_exit_common(Job *job) bdrv_ref(mirror_top_bs); bdrv_ref(target_bs); + bdrv_graph_rdunlock_main_loop(); + /* * Remove target parent that still uses BLK_PERM_WRITE/RESIZE before * inserting target_bs at s->to_replace, where we might not be able to get @@ -692,9 +714,13 @@ static int mirror_exit_common(Job *job) * these permissions any more means that we can't allow any new requests on * mirror_top_bs from now on, so keep it drained. */ bdrv_drained_begin(mirror_top_bs); + bdrv_drained_begin(target_bs); bs_opaque->stop = true; + + bdrv_graph_rdlock_main_loop(); bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing, &error_abort); + if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { BlockDriverState *backing = s->is_none_mode ? src : s->base; BlockDriverState *unfiltered_target = bdrv_skip_filters(target_bs); @@ -716,11 +742,7 @@ static int mirror_exit_common(Job *job) local_err = NULL; } } - - if (s->to_replace) { - replace_aio_context = bdrv_get_aio_context(s->to_replace); - aio_context_acquire(replace_aio_context); - } + bdrv_graph_rdunlock_main_loop(); if (s->should_complete && !abort) { BlockDriverState *to_replace = s->to_replace ?: src; @@ -733,12 +755,13 @@ static int mirror_exit_common(Job *job) /* The mirror job has no requests in flight any more, but we need to * drain potential other users of the BDS before changing the graph. */ assert(s->in_drain); - bdrv_drained_begin(target_bs); + bdrv_drained_begin(to_replace); /* * Cannot use check_to_replace_node() here, because that would * check for an op blocker on @to_replace, and we have our own * there. */ + bdrv_graph_wrlock(); if (bdrv_recurse_can_replace(src, to_replace)) { bdrv_replace_node(to_replace, target_bs, &local_err); } else { @@ -747,7 +770,8 @@ static int mirror_exit_common(Job *job) "would not lead to an abrupt change of visible data", to_replace->node_name, target_bs->node_name); } - bdrv_drained_end(target_bs); + bdrv_graph_wrunlock(); + bdrv_drained_end(to_replace); if (local_err) { error_report_err(local_err); ret = -EPERM; @@ -758,11 +782,7 @@ static int mirror_exit_common(Job *job) error_free(s->replace_blocker); bdrv_unref(s->to_replace); } - if (replace_aio_context) { - aio_context_release(replace_aio_context); - } g_free(s->replaces); - bdrv_unref(target_bs); /* * Remove the mirror filter driver from the graph. Before this, get rid of @@ -770,7 +790,12 @@ static int mirror_exit_common(Job *job) * valid. */ block_job_remove_all_bdrv(bjob); + bdrv_graph_wrlock(); bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort); + bdrv_graph_wrunlock(); + + bdrv_drained_end(target_bs); + bdrv_unref(target_bs); bs_opaque->job = NULL; @@ -806,14 +831,18 @@ static void coroutine_fn mirror_throttle(MirrorBlockJob *s) } } -static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) +static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) { int64_t offset; - BlockDriverState *bs = s->mirror_top_bs->backing->bs; + BlockDriverState *bs; BlockDriverState *target_bs = blk_bs(s->target); int ret; int64_t count; + bdrv_graph_co_rdlock(); + bs = s->mirror_top_bs->backing->bs; + bdrv_graph_co_rdunlock(); + if (s->zero_target) { if (!bdrv_can_write_zeroes_with_unmap(target_bs)) { bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length); @@ -859,8 +888,10 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) return 0; } - ret = bdrv_is_allocated_above(bs, s->base_overlay, true, offset, bytes, - &count); + WITH_GRAPH_RDLOCK_GUARD() { + ret = bdrv_co_is_allocated_above(bs, s->base_overlay, true, offset, + bytes, &count); + } if (ret < 0) { return ret; } @@ -877,9 +908,9 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) /* Called when going out of the streaming phase to flush the bulk of the * data to the medium, or just before completing. */ -static int mirror_flush(MirrorBlockJob *s) +static int coroutine_fn mirror_flush(MirrorBlockJob *s) { - int ret = blk_flush(s->target); + int ret = blk_co_flush(s->target); if (ret < 0) { if (mirror_error_action(s, false, -ret) == BLOCK_ERROR_ACTION_REPORT) { s->ret = ret; @@ -891,9 +922,11 @@ static int mirror_flush(MirrorBlockJob *s) static int coroutine_fn mirror_run(Job *job, Error **errp) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); - BlockDriverState *bs = s->mirror_top_bs->backing->bs; + BlockDriverState *bs; + MirrorBDSOpaque *mirror_top_opaque = s->mirror_top_bs->opaque; BlockDriverState *target_bs = blk_bs(s->target); bool need_drain = true; + BlockDeviceIoStatus iostatus; int64_t length; int64_t target_length; BlockDriverInfo bdi; @@ -901,17 +934,24 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) checking for a NULL string */ int ret = 0; + bdrv_graph_co_rdlock(); + bs = bdrv_filter_bs(s->mirror_top_bs); + bdrv_graph_co_rdunlock(); + if (job_is_cancelled(&s->common.job)) { goto immediate_exit; } - s->bdev_length = bdrv_getlength(bs); + bdrv_graph_co_rdlock(); + s->bdev_length = bdrv_co_getlength(bs); + bdrv_graph_co_rdunlock(); + if (s->bdev_length < 0) { ret = s->bdev_length; goto immediate_exit; } - target_length = blk_getlength(s->target); + target_length = blk_co_getlength(s->target); if (target_length < 0) { ret = target_length; goto immediate_exit; @@ -921,8 +961,8 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) * active layer. */ if (s->base == blk_bs(s->target)) { if (s->bdev_length > target_length) { - ret = blk_truncate(s->target, s->bdev_length, false, - PREALLOC_MODE_OFF, 0, NULL); + ret = blk_co_truncate(s->target, s->bdev_length, false, + PREALLOC_MODE_OFF, 0, NULL); if (ret < 0) { goto immediate_exit; } @@ -936,7 +976,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) if (s->bdev_length == 0) { /* Transition to the READY state and wait for complete. */ job_transition_to_ready(&s->common.job); - s->actively_synced = true; + qatomic_set(&s->actively_synced, true); while (!job_cancel_requested(&s->common.job) && !s->should_complete) { job_yield(&s->common.job); } @@ -952,7 +992,8 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) */ bdrv_get_backing_filename(target_bs, backing_filename, sizeof(backing_filename)); - if (!bdrv_get_info(target_bs, &bdi) && bdi.cluster_size) { + bdrv_graph_co_rdlock(); + if (!bdrv_co_get_info(target_bs, &bdi) && bdi.cluster_size) { s->target_cluster_size = bdi.cluster_size; } else { s->target_cluster_size = BDRV_SECTOR_SIZE; @@ -963,6 +1004,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) s->cow_bitmap = bitmap_new(length); } s->max_iov = MIN(bs->bl.max_iov, target_bs->bl.max_iov); + bdrv_graph_co_rdunlock(); s->buf = qemu_try_blockalign(bs, s->buf_size); if (s->buf == NULL) { @@ -980,19 +1022,18 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) } } + /* + * Only now the job is fully initialised and mirror_top_bs should start + * accessing it. + */ + mirror_top_opaque->job = s; + assert(!s->dbi); s->dbi = bdrv_dirty_iter_new(s->dirty_bitmap); for (;;) { - uint64_t delay_ns = 0; int64_t cnt, delta; bool should_complete; - /* Do not start passive operations while there are active - * writes in progress */ - while (s->in_active_write_counter) { - mirror_wait_for_any_operation(s, true); - } - if (s->ret < 0) { ret = s->ret; goto immediate_exit; @@ -1009,22 +1050,27 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) /* cnt is the number of dirty bytes remaining and s->bytes_in_flight is * the number of bytes currently being processed; together those are * the current remaining operation length */ - job_progress_set_remaining(&s->common.job, s->bytes_in_flight + cnt); + job_progress_set_remaining(&s->common.job, + s->bytes_in_flight + cnt + + s->active_write_bytes_in_flight); /* Note that even when no rate limit is applied we need to yield * periodically with no pending I/O so that bdrv_drain_all() returns. * We do so every BLKOCK_JOB_SLICE_TIME nanoseconds, or when there is * an error, or when the source is clean, whichever comes first. */ delta = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->last_pause_ns; + WITH_JOB_LOCK_GUARD() { + iostatus = s->common.iostatus; + } if (delta < BLOCK_JOB_SLICE_TIME && - s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) { + iostatus == BLOCK_DEVICE_IO_STATUS_OK) { if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 || (cnt == 0 && s->in_flight > 0)) { trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight); mirror_wait_for_free_in_flight_slot(s); continue; } else if (cnt != 0) { - delay_ns = mirror_iteration(s); + mirror_iteration(s); } } @@ -1042,9 +1088,9 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) * the target in a consistent state. */ job_transition_to_ready(&s->common.job); - if (s->copy_mode != MIRROR_COPY_MODE_BACKGROUND) { - s->actively_synced = true; - } + } + if (qatomic_read(&s->copy_mode) != MIRROR_COPY_MODE_BACKGROUND) { + qatomic_set(&s->actively_synced, true); } should_complete = s->should_complete || @@ -1067,6 +1113,10 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) s->in_drain = true; bdrv_drained_begin(bs); + + /* Must be zero because we are drained */ + assert(s->in_active_write_counter == 0); + cnt = bdrv_get_dirty_count(s->dirty_bitmap); if (cnt > 0 || mirror_flush(s) < 0) { bdrv_drained_end(bs); @@ -1083,12 +1133,14 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) } if (job_is_ready(&s->common.job) && !should_complete) { - delay_ns = (s->in_flight == 0 && - cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0); + if (s->in_flight == 0 && cnt == 0) { + trace_mirror_before_sleep(s, cnt, job_is_ready(&s->common.job), + BLOCK_JOB_SLICE_TIME); + job_sleep_ns(&s->common.job, BLOCK_JOB_SLICE_TIME); + } + } else { + block_job_ratelimit_sleep(&s->common); } - trace_mirror_before_sleep(s, cnt, job_is_ready(&s->common.job), - delay_ns); - job_sleep_ns(&s->common.job, delay_ns); s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); } @@ -1129,31 +1181,26 @@ static void mirror_complete(Job *job, Error **errp) /* block all operations on to_replace bs */ if (s->replaces) { - AioContext *replace_aio_context; - s->to_replace = bdrv_find_node(s->replaces); if (!s->to_replace) { error_setg(errp, "Node name '%s' not found", s->replaces); return; } - replace_aio_context = bdrv_get_aio_context(s->to_replace); - aio_context_acquire(replace_aio_context); - /* TODO Translate this into child freeze system. */ error_setg(&s->replace_blocker, "block device is in use by block-job-complete"); bdrv_op_block_all(s->to_replace, s->replace_blocker); bdrv_ref(s->to_replace); - - aio_context_release(replace_aio_context); } s->should_complete = true; /* If the job is paused, it will be re-entered when it is resumed */ - if (!job->paused) { - job_enter(job); + WITH_JOB_LOCK_GUARD() { + if (!job->paused) { + job_enter_cond_locked(job, NULL); + } } } @@ -1173,8 +1220,11 @@ static bool mirror_drained_poll(BlockJob *job) * from one of our own drain sections, to avoid a deadlock waiting for * ourselves. */ - if (!s->common.job.paused && !job_is_cancelled(&job->job) && !s->in_drain) { - return true; + WITH_JOB_LOCK_GUARD() { + if (!s->common.job.paused && !job_is_cancelled_locked(&job->job) + && !s->in_drain) { + return true; + } } return !!s->in_flight; @@ -1203,6 +1253,48 @@ static bool commit_active_cancel(Job *job, bool force) return force || !job_is_ready(job); } +static void mirror_change(BlockJob *job, BlockJobChangeOptions *opts, + Error **errp) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + BlockJobChangeOptionsMirror *change_opts = &opts->u.mirror; + MirrorCopyMode current; + + /* + * The implementation relies on the fact that copy_mode is only written + * under the BQL. Otherwise, further synchronization would be required. + */ + + GLOBAL_STATE_CODE(); + + if (qatomic_read(&s->copy_mode) == change_opts->copy_mode) { + return; + } + + if (change_opts->copy_mode != MIRROR_COPY_MODE_WRITE_BLOCKING) { + error_setg(errp, "Change to copy mode '%s' is not implemented", + MirrorCopyMode_str(change_opts->copy_mode)); + return; + } + + current = qatomic_cmpxchg(&s->copy_mode, MIRROR_COPY_MODE_BACKGROUND, + change_opts->copy_mode); + if (current != MIRROR_COPY_MODE_BACKGROUND) { + error_setg(errp, "Expected current copy mode '%s', got '%s'", + MirrorCopyMode_str(MIRROR_COPY_MODE_BACKGROUND), + MirrorCopyMode_str(current)); + } +} + +static void mirror_query(BlockJob *job, BlockJobInfo *info) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + + info->u.mirror = (BlockJobInfoMirror) { + .actively_synced = qatomic_read(&s->actively_synced), + }; +} + static const BlockJobDriver mirror_job_driver = { .job_driver = { .instance_size = sizeof(MirrorBlockJob), @@ -1217,6 +1309,8 @@ static const BlockJobDriver mirror_job_driver = { .cancel = mirror_cancel, }, .drained_poll = mirror_drained_poll, + .change = mirror_change, + .query = mirror_query, }; static const BlockJobDriver commit_active_job_driver = { @@ -1297,6 +1391,7 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, } job_progress_increase_remaining(&job->common.job, bytes); + job->active_write_bytes_in_flight += bytes; switch (method) { case MIRROR_METHOD_COPY: @@ -1318,6 +1413,7 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, abort(); } + job->active_write_bytes_in_flight -= bytes; if (ret >= 0) { job_progress_update(&job->common.job, bytes); } else { @@ -1333,7 +1429,7 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, bitmap_end = QEMU_ALIGN_UP(offset + bytes, job->granularity); bdrv_set_dirty_bitmap(job->dirty_bitmap, bitmap_offset, bitmap_end - bitmap_offset); - job->actively_synced = false; + qatomic_set(&job->actively_synced, false); action = mirror_error_action(job, false, -ret); if (action == BLOCK_ERROR_ACTION_REPORT) { @@ -1366,6 +1462,19 @@ static MirrorOp *coroutine_fn active_write_prepare(MirrorBlockJob *s, s->in_active_write_counter++; + /* + * Wait for concurrent requests affecting the area. If there are already + * running requests that are copying off now-to-be stale data in the area, + * we must wait for them to finish before we begin writing fresh data to the + * target so that the write operations appear in the correct order. + * Note that background requests (see mirror_iteration()) in contrast only + * wait for conflicting requests at the start of the dirty area, and then + * (based on the in_flight_bitmap) truncate the area to copy so it will not + * conflict with any requests beyond that. For active writes, however, we + * cannot truncate that area. The request from our parent must be blocked + * until the area is copied in full. Therefore, we must wait for the whole + * area to become free of concurrent requests. + */ mirror_wait_on_conflicts(op, s, offset, bytes); bitmap_set(s->in_flight_bitmap, start_chunk, end_chunk - start_chunk); @@ -1373,13 +1482,14 @@ static MirrorOp *coroutine_fn active_write_prepare(MirrorBlockJob *s, return op; } -static void coroutine_fn active_write_settle(MirrorOp *op) +static void coroutine_fn GRAPH_RDLOCK active_write_settle(MirrorOp *op) { uint64_t start_chunk = op->offset / op->s->granularity; uint64_t end_chunk = DIV_ROUND_UP(op->offset + op->bytes, op->s->granularity); - if (!--op->s->in_active_write_counter && op->s->actively_synced) { + if (!--op->s->in_active_write_counter && + qatomic_read(&op->s->actively_synced)) { BdrvChild *source = op->s->mirror_top_bs->backing; if (QLIST_FIRST(&source->bs->parents) == source && @@ -1398,24 +1508,28 @@ static void coroutine_fn active_write_settle(MirrorOp *op) g_free(op); } -static int coroutine_fn bdrv_mirror_top_preadv(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +bdrv_mirror_top_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); } -static int coroutine_fn bdrv_mirror_top_do_write(BlockDriverState *bs, - MirrorMethod method, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, - int flags) +static bool should_copy_to_target(MirrorBDSOpaque *s) +{ + return s->job && s->job->ret >= 0 && + !job_is_cancelled(&s->job->common.job) && + qatomic_read(&s->job->copy_mode) == MIRROR_COPY_MODE_WRITE_BLOCKING; +} + +static int coroutine_fn GRAPH_RDLOCK +bdrv_mirror_top_do_write(BlockDriverState *bs, MirrorMethod method, + bool copy_to_target, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) { MirrorOp *op = NULL; MirrorBDSOpaque *s = bs->opaque; int ret = 0; - bool copy_to_target; - - copy_to_target = s->job->ret >= 0 && - !job_is_cancelled(&s->job->common.job) && - s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; if (copy_to_target) { op = active_write_prepare(s->job, offset, bytes); @@ -1438,6 +1552,11 @@ static int coroutine_fn bdrv_mirror_top_do_write(BlockDriverState *bs, abort(); } + if (!copy_to_target && s->job && s->job->dirty_bitmap) { + qatomic_set(&s->job->actively_synced, false); + bdrv_set_dirty_bitmap(s->job->dirty_bitmap, offset, bytes); + } + if (ret < 0) { goto out; } @@ -1453,18 +1572,14 @@ out: return ret; } -static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +bdrv_mirror_top_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { - MirrorBDSOpaque *s = bs->opaque; QEMUIOVector bounce_qiov; void *bounce_buf; int ret = 0; - bool copy_to_target; - - copy_to_target = s->job->ret >= 0 && - !job_is_cancelled(&s->job->common.job) && - s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; + bool copy_to_target = should_copy_to_target(bs->opaque); if (copy_to_target) { /* The guest might concurrently modify the data to write; but @@ -1477,10 +1592,12 @@ static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs, qemu_iovec_init(&bounce_qiov, 1); qemu_iovec_add(&bounce_qiov, bounce_buf, bytes); qiov = &bounce_qiov; + + flags &= ~BDRV_REQ_REGISTERED_BUF; } - ret = bdrv_mirror_top_do_write(bs, MIRROR_METHOD_COPY, offset, bytes, qiov, - flags); + ret = bdrv_mirror_top_do_write(bs, MIRROR_METHOD_COPY, copy_to_target, + offset, bytes, qiov, flags); if (copy_to_target) { qemu_iovec_destroy(&bounce_qiov); @@ -1490,7 +1607,7 @@ static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs, return ret; } -static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK bdrv_mirror_top_flush(BlockDriverState *bs) { if (bs->backing == NULL) { /* we can be here after failed bdrv_append in mirror_start_job */ @@ -1499,21 +1616,24 @@ static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs) return bdrv_co_flush(bs->backing->bs); } -static int coroutine_fn bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int64_t bytes, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, int64_t offset, + int64_t bytes, BdrvRequestFlags flags) { - return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_ZERO, offset, bytes, NULL, - flags); + bool copy_to_target = should_copy_to_target(bs->opaque); + return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_ZERO, copy_to_target, + offset, bytes, NULL, flags); } -static int coroutine_fn bdrv_mirror_top_pdiscard(BlockDriverState *bs, - int64_t offset, int64_t bytes) +static int coroutine_fn GRAPH_RDLOCK +bdrv_mirror_top_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { - return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_DISCARD, offset, bytes, - NULL, 0); + bool copy_to_target = should_copy_to_target(bs->opaque); + return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_DISCARD, copy_to_target, + offset, bytes, NULL, 0); } -static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs) +static void GRAPH_RDLOCK bdrv_mirror_top_refresh_filename(BlockDriverState *bs) { if (bs->backing == NULL) { /* we can be here after failed bdrv_attach_child in @@ -1578,6 +1698,7 @@ static BlockDriver bdrv_mirror_top = { .bdrv_child_perm = bdrv_mirror_top_child_perm, .is_filter = true, + .filtered_child_is_backing = true, }; static BlockJob *mirror_start_job( @@ -1605,6 +1726,8 @@ static BlockJob *mirror_start_job( uint64_t target_perms, target_shared_perms; int ret; + GLOBAL_STATE_CODE(); + if (granularity == 0) { granularity = bdrv_get_default_bitmap_granularity(target); } @@ -1620,12 +1743,15 @@ static BlockJob *mirror_start_job( buf_size = DEFAULT_MIRROR_BUF_SIZE; } + bdrv_graph_rdlock_main_loop(); if (bdrv_skip_filters(bs) == bdrv_skip_filters(target)) { error_setg(errp, "Can't mirror node into itself"); + bdrv_graph_rdunlock_main_loop(); return NULL; } target_is_backing = bdrv_chain_contains(bs, target); + bdrv_graph_rdunlock_main_loop(); /* In the case of active commit, add dummy driver to provide consistent * reads on the top, while disabling it in the intermediate nodes, and make @@ -1669,7 +1795,6 @@ static BlockJob *mirror_start_job( if (!s) { goto fail; } - bs_opaque->job = s; /* The block job now has a reference to this node */ bdrv_unref(mirror_top_bs); @@ -1709,14 +1834,19 @@ static BlockJob *mirror_start_job( } target_shared_perms |= BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE; - } else if (bdrv_chain_contains(bs, bdrv_skip_filters(target))) { - /* - * We may want to allow this in the future, but it would - * require taking some extra care. - */ - error_setg(errp, "Cannot mirror to a filter on top of a node in the " - "source's backing chain"); - goto fail; + } else { + bdrv_graph_rdlock_main_loop(); + if (bdrv_chain_contains(bs, bdrv_skip_filters(target))) { + /* + * We may want to allow this in the future, but it would + * require taking some extra care. + */ + error_setg(errp, "Cannot mirror to a filter on top of a node in " + "the source's backing chain"); + bdrv_graph_rdunlock_main_loop(); + goto fail; + } + bdrv_graph_rdunlock_main_loop(); } s->target = blk_new(s->common.job.aio_context, @@ -1737,13 +1867,14 @@ static BlockJob *mirror_start_job( blk_set_allow_aio_context_change(s->target, true); blk_set_disable_request_queuing(s->target, true); + bdrv_graph_rdlock_main_loop(); s->replaces = g_strdup(replaces); s->on_source_error = on_source_error; s->on_target_error = on_target_error; s->is_none_mode = is_none_mode; s->backing_mode = backing_mode; s->zero_target = zero_target; - s->copy_mode = copy_mode; + qatomic_set(&s->copy_mode, copy_mode); s->base = base; s->base_overlay = bdrv_find_overlay(bs, base); s->granularity = granularity; @@ -1752,20 +1883,27 @@ static BlockJob *mirror_start_job( if (auto_complete) { s->should_complete = true; } + bdrv_graph_rdunlock_main_loop(); - s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp); + s->dirty_bitmap = bdrv_create_dirty_bitmap(s->mirror_top_bs, granularity, + NULL, errp); if (!s->dirty_bitmap) { goto fail; } - if (s->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING) { - bdrv_disable_dirty_bitmap(s->dirty_bitmap); - } + /* + * The dirty bitmap is set by bdrv_mirror_top_do_write() when not in active + * mode. + */ + bdrv_disable_dirty_bitmap(s->dirty_bitmap); + + bdrv_graph_wrlock(); ret = block_job_add_bdrv(&s->common, "source", bs, 0, BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ, errp); if (ret < 0) { + bdrv_graph_wrunlock(); goto fail; } @@ -1810,14 +1948,17 @@ static BlockJob *mirror_start_job( ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, iter_shared_perms, errp); if (ret < 0) { + bdrv_graph_wrunlock(); goto fail; } } if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) { + bdrv_graph_wrunlock(); goto fail; } } + bdrv_graph_wrunlock(); QTAILQ_INIT(&s->ops_in_flight); @@ -1842,9 +1983,14 @@ fail: } bs_opaque->stop = true; + bdrv_drained_begin(bs); + bdrv_graph_wrlock(); + assert(mirror_top_bs->backing->bs == bs); bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing, &error_abort); - bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort); + bdrv_replace_node(mirror_top_bs, bs, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drained_end(bs); bdrv_unref(mirror_top_bs); @@ -1873,8 +2019,12 @@ void mirror_start(const char *job_id, BlockDriverState *bs, MirrorSyncMode_str(mode)); return; } + + bdrv_graph_rdlock_main_loop(); is_none_mode = mode == MIRROR_SYNC_MODE_NONE; base = mode == MIRROR_SYNC_MODE_TOP ? bdrv_backing_chain_next(bs) : NULL; + bdrv_graph_rdunlock_main_loop(); + mirror_start_job(job_id, bs, creation_flags, target, replaces, speed, granularity, buf_size, backing_mode, zero_target, on_source_error, on_target_error, unmap, NULL, NULL, diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmds.c index 2b677c4a2f..a738e7bbf7 100644 --- a/block/monitor/bitmap-qmp-cmds.c +++ b/block/monitor/bitmap-qmp-cmds.c @@ -32,7 +32,9 @@ #include "qemu/osdep.h" +#include "block/block-io.h" #include "block/block_int.h" +#include "block/dirty-bitmap.h" #include "qapi/qapi-commands-block.h" #include "qapi/error.h" @@ -93,7 +95,6 @@ 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"); @@ -105,14 +106,11 @@ 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"); - goto out; + return; } } else { /* Default to cluster size, if available: */ @@ -130,12 +128,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)) { - goto out; + return; } bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp); if (bitmap == NULL) { - goto out; + return; } if (disabled) { @@ -143,9 +141,6 @@ 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); } BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, @@ -155,7 +150,6 @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, { BlockDriverState *bs; BdrvDirtyBitmap *bitmap; - AioContext *aio_context; GLOBAL_STATE_CODE(); @@ -164,19 +158,14 @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, 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) { - aio_context_release(aio_context); return NULL; } @@ -188,7 +177,6 @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, *bitmap_bs = bs; } - aio_context_release(aio_context); return release ? NULL : bitmap; } @@ -256,64 +244,69 @@ void qmp_block_dirty_bitmap_disable(const char *node, const char *name, bdrv_disable_dirty_bitmap(bitmap); } -BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, +BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *dst_node, + const char *dst_bitmap, BlockDirtyBitmapOrStrList *bms, HBitmap **backup, Error **errp) { BlockDriverState *bs; - BdrvDirtyBitmap *dst, *src, *anon; + BdrvDirtyBitmap *dst, *src; BlockDirtyBitmapOrStrList *lst; + const char *src_node, *src_bitmap; + HBitmap *local_backup = NULL; GLOBAL_STATE_CODE(); - dst = block_dirty_bitmap_lookup(node, target, &bs, errp); + dst = block_dirty_bitmap_lookup(dst_node, dst_bitmap, &bs, errp); if (!dst) { return NULL; } - anon = bdrv_create_dirty_bitmap(bs, bdrv_dirty_bitmap_granularity(dst), - NULL, errp); - if (!anon) { - return NULL; - } - for (lst = bms; lst; lst = lst->next) { switch (lst->value->type) { - const char *name, *node; case QTYPE_QSTRING: - name = lst->value->u.local; - src = bdrv_find_dirty_bitmap(bs, name); + src_bitmap = lst->value->u.local; + src = bdrv_find_dirty_bitmap(bs, src_bitmap); if (!src) { - error_setg(errp, "Dirty bitmap '%s' not found", name); - dst = NULL; - goto out; + error_setg(errp, "Dirty bitmap '%s' not found", src_bitmap); + goto fail; } break; case QTYPE_QDICT: - node = lst->value->u.external.node; - name = lst->value->u.external.name; - src = block_dirty_bitmap_lookup(node, name, NULL, errp); + src_node = lst->value->u.external.node; + src_bitmap = lst->value->u.external.name; + src = block_dirty_bitmap_lookup(src_node, src_bitmap, NULL, errp); if (!src) { - dst = NULL; - goto out; + goto fail; } break; default: abort(); } - if (!bdrv_merge_dirty_bitmap(anon, src, NULL, errp)) { - dst = NULL; - goto out; + /* We do backup only for first merge operation */ + if (!bdrv_merge_dirty_bitmap(dst, src, + local_backup ? NULL : &local_backup, + errp)) + { + goto fail; } } - /* Merge into dst; dst is unchanged on failure. */ - bdrv_merge_dirty_bitmap(dst, anon, backup, errp); + if (backup) { + *backup = local_backup; + } else { + hbitmap_free(local_backup); + } - out: - bdrv_release_dirty_bitmap(anon); return dst; + +fail: + if (local_backup) { + bdrv_restore_dirty_bitmap(dst, local_backup); + } + + return NULL; } void qmp_block_dirty_bitmap_merge(const char *node, const char *target, diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index bfb3c043a0..d954bec6f1 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -48,6 +48,7 @@ #include "qemu/option.h" #include "qemu/sockets.h" #include "qemu/cutils.h" +#include "qemu/error-report.h" #include "sysemu/sysemu.h" #include "monitor/monitor.h" #include "monitor/hmp.h" @@ -140,9 +141,11 @@ 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; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bs = bdrv_find_node(id); if (bs) { qmp_blockdev_del(id, &local_err); @@ -164,14 +167,10 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) 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; } @@ -192,8 +191,6 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) } else { blk_unref(blk); } - - aio_context_release(aio_context); } void hmp_commit(Monitor *mon, const QDict *qdict) @@ -202,29 +199,28 @@ void hmp_commit(Monitor *mon, const QDict *qdict) BlockBackend *blk; int ret; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + 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; } + + bs = bdrv_skip_implicit_filters(blk_bs(blk)); + if (!blk_is_available(blk)) { error_report("Device '%s' has no medium", device); return; } - bs = bdrv_skip_implicit_filters(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)); @@ -241,7 +237,6 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict) 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, @@ -270,7 +265,6 @@ void hmp_drive_backup(Monitor *mon, const QDict *qdict) 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, @@ -360,9 +354,7 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict) } mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS; - qmp_blockdev_snapshot_sync(true, device, false, NULL, - filename, false, NULL, - !!format, format, + qmp_blockdev_snapshot_sync(device, NULL, filename, NULL, format, true, mode, &err); end: hmp_handle_error(mon, err); @@ -385,8 +377,7 @@ void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict) const char *id = qdict_get_try_str(qdict, "id"); Error *err = NULL; - qmp_blockdev_snapshot_delete_internal_sync(device, !!id, id, - true, name, &err); + qmp_blockdev_snapshot_delete_internal_sync(device, id, name, &err); hmp_handle_error(mon, err); } @@ -427,7 +418,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) block_list = qmp_query_block(NULL); for (info = block_list; info; info = info->next) { - if (!info->value->has_inserted) { + if (!info->value->inserted) { continue; } @@ -460,7 +451,6 @@ void hmp_nbd_server_add(Monitor *mon, const QDict *qdict) NbdServerAddOptions export = { .device = (char *) device, - .has_name = !!name, .name = (char *) name, .has_writable = true, .writable = writable, @@ -489,13 +479,13 @@ void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, err); } -void hmp_block_resize(Monitor *mon, const QDict *qdict) +void coroutine_fn 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); + qmp_block_resize(device, NULL, size, &err); hmp_handle_error(mon, err); } @@ -506,11 +496,10 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) 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, false, NULL, - qdict_haskey(qdict, "speed"), speed, true, - BLOCKDEV_ON_ERROR_REPORT, false, NULL, false, false, false, - false, &error); + qmp_block_stream(device, device, base, NULL, NULL, false, false, NULL, + qdict_haskey(qdict, "speed"), speed, + true, BLOCKDEV_ON_ERROR_REPORT, NULL, + false, false, false, false, &error); hmp_handle_error(mon, error); } @@ -534,10 +523,8 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) * 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; } @@ -551,7 +538,7 @@ void hmp_eject(Monitor *mon, const QDict *qdict) const char *device = qdict_get_str(qdict, "device"); Error *err = NULL; - qmp_eject(true, device, false, NULL, true, force, &err); + qmp_eject(device, NULL, true, force, &err); hmp_handle_error(mon, err); } @@ -560,7 +547,6 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) BlockBackend *blk = NULL; BlockDriverState *bs = NULL; BlockBackend *local_blk = NULL; - AioContext *ctx = 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"); @@ -582,9 +568,6 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) } } - ctx = blk ? blk_get_aio_context(blk) : bdrv_get_aio_context(bs); - aio_context_acquire(ctx); - if (bs) { blk = local_blk = blk_new(bdrv_get_aio_context(bs), 0, BLK_PERM_ALL); ret = blk_insert_bs(blk, bs, &err); @@ -622,11 +605,6 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) fail: blk_unref(local_blk); - - if (ctx) { - aio_context_release(ctx); - } - hmp_handle_error(mon, err); } @@ -635,19 +613,19 @@ static void print_block_info(Monitor *mon, BlockInfo *info, { ImageInfo *image_info; - assert(!info || !info->has_inserted || info->inserted == inserted); + assert(!info || !info->inserted || info->inserted == inserted); if (info && *info->device) { - monitor_printf(mon, "%s", info->device); - if (inserted && inserted->has_node_name) { + monitor_puts(mon, info->device); + if (inserted && inserted->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 - : ""); + monitor_puts(mon, + inserted && inserted->node_name ? inserted->node_name + : info && info->qdev ? info->qdev + : ""); } if (inserted) { @@ -661,7 +639,7 @@ static void print_block_info(Monitor *mon, BlockInfo *info, } if (info) { - if (info->has_qdev) { + if (info->qdev) { monitor_printf(mon, " Attached to: %s\n", info->qdev); } if (info->has_io_status && info->io_status != BLOCK_DEVICE_IO_STATUS_OK) { @@ -686,7 +664,7 @@ static void print_block_info(Monitor *mon, BlockInfo *info, inserted->cache->direct ? ", direct" : "", inserted->cache->no_flush ? ", ignore flushes" : ""); - if (inserted->has_backing_file) { + if (inserted->backing_file) { monitor_printf(mon, " Backing file: %s " "(chain depth: %" PRId64 ")\n", @@ -734,8 +712,8 @@ static void print_block_info(Monitor *mon, BlockInfo *info, monitor_printf(mon, "\nImages:\n"); image_info = inserted->image; while (1) { - bdrv_image_info_dump(image_info); - if (image_info->has_backing_image) { + bdrv_node_info_dump(qapi_ImageInfo_base(image_info), 0, false); + if (image_info->backing_image) { image_info = image_info->backing_image; } else { break; @@ -769,8 +747,7 @@ void hmp_info_block(Monitor *mon, const QDict *qdict) monitor_printf(mon, "\n"); } - print_block_info(mon, info->value, info->value->has_inserted - ? info->value->inserted : NULL, + print_block_info(mon, info->value, info->value->inserted, verbose); printed = true; } @@ -784,7 +761,7 @@ void hmp_info_block(Monitor *mon, const QDict *qdict) /* 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); + assert(blockdev->value->node_name); if (device && strcmp(device, blockdev->value->node_name)) { continue; } @@ -805,7 +782,7 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict) stats_list = qmp_query_blockstats(false, false, NULL); for (stats = stats_list; stats; stats = stats->next) { - if (!stats->value->has_device) { + if (!stats->value->device) { continue; } @@ -850,7 +827,7 @@ void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) } while (list) { - if (strcmp(list->value->type, "stream") == 0) { + if (list->value->type == JOB_TYPE_STREAM) { monitor_printf(mon, "Streaming device %s: Completed %" PRId64 " of %" PRId64 " bytes, speed limit %" PRId64 " bytes/s\n", @@ -862,7 +839,7 @@ void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) monitor_printf(mon, "Type %s, device %s: Completed %" PRId64 " of %" PRId64 " bytes, speed limit %" PRId64 " bytes/s\n", - list->value->type, + JobType_str(list->value->type), list->value->device, list->value->offset, list->value->len, @@ -883,7 +860,6 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) int nb_sns, i; int total; int *global_snapshots; - AioContext *aio_context; typedef struct SnapshotEntry { QEMUSnapshotInfo sn; @@ -903,16 +879,15 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) SnapshotEntry *snapshot_entry; Error *err = NULL; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bs = bdrv_all_find_vmstate_bs(NULL, false, NULL, &err); if (!bs) { error_report_err(err); 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); @@ -923,9 +898,7 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) 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); @@ -943,7 +916,6 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) } g_free(sn); } - aio_context_release(ctx); } if (no_snapshot) { @@ -1015,3 +987,24 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) g_free(sn_tab); g_free(global_snapshots); } + +void hmp_change_medium(Monitor *mon, const char *device, const char *target, + const char *arg, const char *read_only, bool force, + Error **errp) +{ + ERRP_GUARD(); + BlockdevChangeReadOnlyMode read_only_mode = 0; + + if (read_only) { + read_only_mode = + qapi_enum_parse(&BlockdevChangeReadOnlyMode_lookup, + read_only, + BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN, errp); + if (*errp) { + return; + } + } + + qmp_blockdev_change_medium(device, NULL, target, arg, true, force, + !!read_only, read_only_mode, errp); +} diff --git a/block/monitor/meson.build b/block/monitor/meson.build index 374aac1140..1022516e93 100644 --- a/block/monitor/meson.build +++ b/block/monitor/meson.build @@ -1,2 +1,2 @@ -softmmu_ss.add(files('block-hmp-cmds.c')) +system_ss.add(files('block-hmp-cmds.c')) block_ss.add(files('bitmap-qmp-cmds.c')) diff --git a/block/nbd.c b/block/nbd.c index 6085ab1d2c..ef05f7cdfd 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -1,8 +1,8 @@ /* - * QEMU Block driver for NBD + * QEMU Block driver for NBD * * Copyright (c) 2019 Virtuozzo International GmbH. - * Copyright (C) 2016 Red Hat, Inc. + * Copyright Red Hat * Copyright (C) 2008 Bull S.A.S. * Author: Laurent Vivier * @@ -50,8 +50,8 @@ #define EN_OPTSTR ":exportname=" #define MAX_NBD_REQUESTS 16 -#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ (uint64_t)(intptr_t)(bs)) -#define INDEX_TO_HANDLE(bs, index) ((index) ^ (uint64_t)(intptr_t)(bs)) +#define COOKIE_TO_INDEX(cookie) ((cookie) - 1) +#define INDEX_TO_COOKIE(index) ((index) + 1) typedef struct { Coroutine *coroutine; @@ -77,7 +77,7 @@ typedef struct BDRVNBDState { QemuMutex requests_lock; NBDClientState state; CoQueue free_sema; - int in_flight; + unsigned in_flight; NBDClientRequest requests[MAX_NBD_REQUESTS]; QEMUTimer *reconnect_delay_timer; @@ -275,7 +275,8 @@ static bool nbd_client_will_reconnect(BDRVNBDState *s) * Return failure if the server's advertised options are incompatible with the * client's needs. */ -static int nbd_handle_updated_info(BlockDriverState *bs, Error **errp) +static int coroutine_fn GRAPH_RDLOCK +nbd_handle_updated_info(BlockDriverState *bs, Error **errp) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; int ret; @@ -322,6 +323,7 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs, int ret; IO_CODE(); + assert_bdrv_graph_readable(); assert(!s->ioc); s->ioc = nbd_co_establish_connection(s->conn, &s->info, blocking, errp); @@ -338,7 +340,7 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs, * We have connected, but must fail for other reasons. * Send NBD_CMD_DISC as a courtesy to the server. */ - NBDRequest request = { .type = NBD_CMD_DISC }; + NBDRequest request = { .type = NBD_CMD_DISC, .mode = s->info.mode }; nbd_send_request(s->ioc, &request); @@ -351,7 +353,7 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs, } qio_channel_set_blocking(s->ioc, false, NULL); - qio_channel_attach_aio_context(s->ioc, bdrv_get_aio_context(bs)); + qio_channel_set_follow_coroutine_ctx(s->ioc, true); /* successfully connected */ WITH_QEMU_LOCK_GUARD(&s->requests_lock) { @@ -369,8 +371,9 @@ static bool nbd_client_connecting(BDRVNBDState *s) } /* Called with s->requests_lock taken. */ -static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) +static void coroutine_fn GRAPH_RDLOCK nbd_reconnect_attempt(BDRVNBDState *s) { + int ret; bool blocking = s->state == NBD_CLIENT_CONNECTING_WAIT; /* @@ -380,6 +383,8 @@ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) assert(nbd_client_connecting(s)); assert(s->in_flight == 1); + trace_nbd_reconnect_attempt(s->bs->in_flight); + if (blocking && !s->reconnect_delay_timer) { /* * It's the first reconnect attempt after switching to @@ -393,7 +398,6 @@ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) /* Finalize previous connection if any */ if (s->ioc) { - qio_channel_detach_aio_context(QIO_CHANNEL(s->ioc)); yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), nbd_yank, s->bs); object_unref(OBJECT(s->ioc)); @@ -401,7 +405,8 @@ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) } qemu_mutex_unlock(&s->requests_lock); - nbd_co_do_establish_connection(s->bs, blocking, NULL); + ret = nbd_co_do_establish_connection(s->bs, blocking, NULL); + trace_nbd_reconnect_attempt_result(ret, s->bs->in_flight); qemu_mutex_lock(&s->requests_lock); /* @@ -412,25 +417,26 @@ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) reconnect_delay_timer_del(s); } -static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t handle) +static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t cookie, + Error **errp) { int ret; - uint64_t ind = HANDLE_TO_INDEX(s, handle), ind2; + uint64_t ind = COOKIE_TO_INDEX(cookie), ind2; QEMU_LOCK_GUARD(&s->receive_mutex); while (true) { - if (s->reply.handle == handle) { + if (s->reply.cookie == cookie) { /* We are done */ return 0; } - if (s->reply.handle != 0) { + if (s->reply.cookie != 0) { /* * Some other request is being handled now. It should already be - * woken by whoever set s->reply.handle (or never wait in this + * woken by whoever set s->reply.cookie (or never wait in this * yield). So, we should not wake it here. */ - ind2 = HANDLE_TO_INDEX(s, s->reply.handle); + ind2 = COOKIE_TO_INDEX(s->reply.cookie); assert(!s->requests[ind2].receiving); s->requests[ind].receiving = true; @@ -440,9 +446,9 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t handle) /* * We may be woken for 2 reasons: * 1. From this function, executing in parallel coroutine, when our - * handle is received. + * cookie is received. * 2. From nbd_co_receive_one_chunk(), when previous request is - * finished and s->reply.handle set to 0. + * finished and s->reply.cookie set to 0. * Anyway, it's OK to lock the mutex and go to the next iteration. */ @@ -451,24 +457,30 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t handle) continue; } - /* We are under mutex and handle is 0. We have to do the dirty work. */ - assert(s->reply.handle == 0); - ret = nbd_receive_reply(s->bs, s->ioc, &s->reply, NULL); - if (ret <= 0) { - ret = ret ? ret : -EIO; + /* We are under mutex and cookie is 0. We have to do the dirty work. */ + assert(s->reply.cookie == 0); + ret = nbd_receive_reply(s->bs, s->ioc, &s->reply, s->info.mode, errp); + if (ret == 0) { + ret = -EIO; + error_setg(errp, "server dropped connection"); + } + if (ret < 0) { nbd_channel_error(s, ret); return ret; } - if (nbd_reply_is_structured(&s->reply) && !s->info.structured_reply) { + if (nbd_reply_is_structured(&s->reply) && + s->info.mode < NBD_MODE_STRUCTURED) { nbd_channel_error(s, -EINVAL); + error_setg(errp, "unexpected structured reply"); return -EINVAL; } - ind2 = HANDLE_TO_INDEX(s, s->reply.handle); + ind2 = COOKIE_TO_INDEX(s->reply.cookie); if (ind2 >= MAX_NBD_REQUESTS || !s->requests[ind2].coroutine) { nbd_channel_error(s, -EINVAL); + error_setg(errp, "unexpected cookie value"); return -EINVAL; } - if (s->reply.handle == handle) { + if (s->reply.cookie == cookie) { /* We are done */ return 0; } @@ -476,9 +488,9 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t handle) } } -static int coroutine_fn nbd_co_send_request(BlockDriverState *bs, - NBDRequest *request, - QEMUIOVector *qiov) +static int coroutine_fn GRAPH_RDLOCK +nbd_co_send_request(BlockDriverState *bs, NBDRequest *request, + QEMUIOVector *qiov) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; int rc, i = -1; @@ -514,19 +526,16 @@ static int coroutine_fn nbd_co_send_request(BlockDriverState *bs, qemu_mutex_unlock(&s->requests_lock); qemu_co_mutex_lock(&s->send_mutex); - request->handle = INDEX_TO_HANDLE(s, i); + request->cookie = INDEX_TO_COOKIE(i); + request->mode = s->info.mode; assert(s->ioc); if (qiov) { qio_channel_set_cork(s->ioc, true); rc = nbd_send_request(s->ioc, request); - if (rc >= 0) { - if (qio_channel_writev_all(s->ioc, qiov->iov, qiov->niov, - NULL) < 0) { - rc = -EIO; - } - } else if (rc >= 0) { + if (rc >= 0 && qio_channel_writev_all(s->ioc, qiov->iov, qiov->niov, + NULL) < 0) { rc = -EIO; } qio_channel_set_cork(s->ioc, false); @@ -607,13 +616,17 @@ static int nbd_parse_offset_hole_payload(BDRVNBDState *s, */ static int nbd_parse_blockstatus_payload(BDRVNBDState *s, NBDStructuredReplyChunk *chunk, - uint8_t *payload, uint64_t orig_length, - NBDExtent *extent, Error **errp) + uint8_t *payload, bool wide, + uint64_t orig_length, + NBDExtent64 *extent, Error **errp) { uint32_t context_id; + uint32_t count; + size_t ext_len = wide ? sizeof(*extent) : sizeof(NBDExtent32); + size_t pay_len = sizeof(context_id) + wide * sizeof(count) + ext_len; /* The server succeeded, so it must have sent [at least] one extent */ - if (chunk->length < sizeof(context_id) + sizeof(*extent)) { + if (chunk->length < pay_len) { error_setg(errp, "Protocol error: invalid payload for " "NBD_REPLY_TYPE_BLOCK_STATUS"); return -EINVAL; @@ -628,8 +641,15 @@ static int nbd_parse_blockstatus_payload(BDRVNBDState *s, return -EINVAL; } - extent->length = payload_advance32(&payload); - extent->flags = payload_advance32(&payload); + if (wide) { + count = payload_advance32(&payload); + extent->length = payload_advance64(&payload); + extent->flags = payload_advance64(&payload); + } else { + count = 0; + extent->length = payload_advance32(&payload); + extent->flags = payload_advance32(&payload); + } if (extent->length == 0) { error_setg(errp, "Protocol error: server sent status chunk with " @@ -650,7 +670,7 @@ static int nbd_parse_blockstatus_payload(BDRVNBDState *s, * (always a safe status, even if it loses information). */ if (s->info.min_block && !QEMU_IS_ALIGNED(extent->length, - s->info.min_block)) { + s->info.min_block)) { trace_nbd_parse_blockstatus_compliance("extent length is unaligned"); if (extent->length > s->info.min_block) { extent->length = QEMU_ALIGN_DOWN(extent->length, @@ -664,13 +684,15 @@ static int nbd_parse_blockstatus_payload(BDRVNBDState *s, /* * We used NBD_CMD_FLAG_REQ_ONE, so the server should not have * sent us any more than one extent, nor should it have included - * status beyond our request in that extent. However, it's easy - * enough to ignore the server's noncompliance without killing the + * status beyond our request in that extent. Furthermore, a wide + * server should have replied with an accurate count (we left + * count at 0 for a narrow server). However, it's easy enough to + * ignore the server's noncompliance without killing the * connection; just ignore trailing extents, and clamp things to * the length of our request. */ - if (chunk->length > sizeof(context_id) + sizeof(*extent)) { - trace_nbd_parse_blockstatus_compliance("more than one extent"); + if (count != wide || chunk->length > pay_len) { + trace_nbd_parse_blockstatus_compliance("unexpected extent count"); } if (extent->length > orig_length) { extent->length = orig_length; @@ -827,11 +849,12 @@ static coroutine_fn int nbd_co_receive_structured_payload( * corresponding to the server's error reply), and errp is unchanged. */ static coroutine_fn int nbd_co_do_receive_one_chunk( - BDRVNBDState *s, uint64_t handle, bool only_structured, + BDRVNBDState *s, uint64_t cookie, bool only_structured, int *request_ret, QEMUIOVector *qiov, void **payload, Error **errp) { + ERRP_GUARD(); int ret; - int i = HANDLE_TO_INDEX(s, handle); + int i = COOKIE_TO_INDEX(cookie); void *local_payload = NULL; NBDStructuredReplyChunk *chunk; @@ -840,14 +863,14 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( } *request_ret = 0; - ret = nbd_receive_replies(s, handle); + ret = nbd_receive_replies(s, cookie, errp); if (ret < 0) { - error_setg(errp, "Connection closed"); + error_prepend(errp, "Connection closed: "); return -EIO; } assert(s->ioc); - assert(s->reply.handle == handle); + assert(s->reply.cookie == cookie); if (nbd_reply_is_simple(&s->reply)) { if (only_structured) { @@ -866,7 +889,7 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( } /* handle structured reply chunk */ - assert(s->info.structured_reply); + assert(s->info.mode >= NBD_MODE_STRUCTURED); chunk = &s->reply.structured; if (chunk->type == NBD_REPLY_TYPE_NONE) { @@ -917,11 +940,11 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( * Return value is a fatal error code or normal nbd reply error code */ static coroutine_fn int nbd_co_receive_one_chunk( - BDRVNBDState *s, uint64_t handle, bool only_structured, + BDRVNBDState *s, uint64_t cookie, bool only_structured, int *request_ret, QEMUIOVector *qiov, NBDReply *reply, void **payload, Error **errp) { - int ret = nbd_co_do_receive_one_chunk(s, handle, only_structured, + int ret = nbd_co_do_receive_one_chunk(s, cookie, only_structured, request_ret, qiov, payload, errp); if (ret < 0) { @@ -931,7 +954,7 @@ static coroutine_fn int nbd_co_receive_one_chunk( /* For assert at loop start in nbd_connection_entry */ *reply = s->reply; } - s->reply.handle = 0; + s->reply.cookie = 0; nbd_recv_coroutines_wake(s); @@ -974,20 +997,21 @@ static void nbd_iter_request_error(NBDReplyChunkIter *iter, int ret) * NBD_FOREACH_REPLY_CHUNK * The pointer stored in @payload requires g_free() to free it. */ -#define NBD_FOREACH_REPLY_CHUNK(s, iter, handle, structured, \ +#define NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, structured, \ qiov, reply, payload) \ for (iter = (NBDReplyChunkIter) { .only_structured = structured }; \ - nbd_reply_chunk_iter_receive(s, &iter, handle, qiov, reply, payload);) + nbd_reply_chunk_iter_receive(s, &iter, cookie, qiov, reply, payload);) /* * nbd_reply_chunk_iter_receive * The pointer stored in @payload requires g_free() to free it. */ -static bool nbd_reply_chunk_iter_receive(BDRVNBDState *s, - NBDReplyChunkIter *iter, - uint64_t handle, - QEMUIOVector *qiov, NBDReply *reply, - void **payload) +static bool coroutine_fn nbd_reply_chunk_iter_receive(BDRVNBDState *s, + NBDReplyChunkIter *iter, + uint64_t cookie, + QEMUIOVector *qiov, + NBDReply *reply, + void **payload) { int ret, request_ret; NBDReply local_reply; @@ -1003,7 +1027,7 @@ static bool nbd_reply_chunk_iter_receive(BDRVNBDState *s, reply = &local_reply; } - ret = nbd_co_receive_one_chunk(s, handle, iter->only_structured, + ret = nbd_co_receive_one_chunk(s, cookie, iter->only_structured, &request_ret, qiov, reply, payload, &local_err); if (ret < 0) { @@ -1036,7 +1060,7 @@ static bool nbd_reply_chunk_iter_receive(BDRVNBDState *s, break_loop: qemu_mutex_lock(&s->requests_lock); - s->requests[HANDLE_TO_INDEX(s, handle)].coroutine = NULL; + s->requests[COOKIE_TO_INDEX(cookie)].coroutine = NULL; s->in_flight--; qemu_co_queue_next(&s->free_sema); qemu_mutex_unlock(&s->requests_lock); @@ -1044,12 +1068,13 @@ break_loop: return false; } -static int coroutine_fn nbd_co_receive_return_code(BDRVNBDState *s, uint64_t handle, - int *request_ret, Error **errp) +static int coroutine_fn +nbd_co_receive_return_code(BDRVNBDState *s, uint64_t cookie, + int *request_ret, Error **errp) { NBDReplyChunkIter iter; - NBD_FOREACH_REPLY_CHUNK(s, iter, handle, false, NULL, NULL, NULL) { + NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, false, NULL, NULL, NULL) { /* nbd_reply_chunk_iter_receive does all the work */ } @@ -1058,16 +1083,18 @@ static int coroutine_fn nbd_co_receive_return_code(BDRVNBDState *s, uint64_t han return iter.ret; } -static int coroutine_fn nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t handle, - uint64_t offset, QEMUIOVector *qiov, - int *request_ret, Error **errp) +static int coroutine_fn +nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t cookie, + uint64_t offset, QEMUIOVector *qiov, + int *request_ret, Error **errp) { NBDReplyChunkIter iter; NBDReply reply; void *payload = NULL; Error *local_err = NULL; - NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply, + NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, + s->info.mode >= NBD_MODE_STRUCTURED, qiov, &reply, &payload) { int ret; @@ -1110,10 +1137,10 @@ static int coroutine_fn nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t h return iter.ret; } -static int coroutine_fn nbd_co_receive_blockstatus_reply(BDRVNBDState *s, - uint64_t handle, uint64_t length, - NBDExtent *extent, - int *request_ret, Error **errp) +static int coroutine_fn +nbd_co_receive_blockstatus_reply(BDRVNBDState *s, uint64_t cookie, + uint64_t length, NBDExtent64 *extent, + int *request_ret, Error **errp) { NBDReplyChunkIter iter; NBDReply reply; @@ -1122,14 +1149,20 @@ static int coroutine_fn nbd_co_receive_blockstatus_reply(BDRVNBDState *s, bool received = false; assert(!extent->length); - NBD_FOREACH_REPLY_CHUNK(s, iter, handle, false, NULL, &reply, &payload) { + NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, false, NULL, &reply, &payload) { int ret; NBDStructuredReplyChunk *chunk = &reply.structured; + bool wide; assert(nbd_reply_is_structured(&reply)); switch (chunk->type) { + case NBD_REPLY_TYPE_BLOCK_STATUS_EXT: case NBD_REPLY_TYPE_BLOCK_STATUS: + wide = chunk->type == NBD_REPLY_TYPE_BLOCK_STATUS_EXT; + if ((s->info.mode >= NBD_MODE_EXTENDED) != wide) { + trace_nbd_extended_headers_compliance("block_status"); + } if (received) { nbd_channel_error(s, -EINVAL); error_setg(&local_err, "Several BLOCK_STATUS chunks in reply"); @@ -1137,9 +1170,9 @@ static int coroutine_fn nbd_co_receive_blockstatus_reply(BDRVNBDState *s, } received = true; - ret = nbd_parse_blockstatus_payload(s, &reply.structured, - payload, length, extent, - &local_err); + ret = nbd_parse_blockstatus_payload( + s, &reply.structured, payload, wide, + length, extent, &local_err); if (ret < 0) { nbd_channel_error(s, ret); nbd_iter_channel_error(&iter, ret, &local_err); @@ -1170,8 +1203,9 @@ static int coroutine_fn nbd_co_receive_blockstatus_reply(BDRVNBDState *s, return iter.ret; } -static int coroutine_fn nbd_co_request(BlockDriverState *bs, NBDRequest *request, - QEMUIOVector *write_qiov) +static int coroutine_fn GRAPH_RDLOCK +nbd_co_request(BlockDriverState *bs, NBDRequest *request, + QEMUIOVector *write_qiov) { int ret, request_ret; Error *local_err = NULL; @@ -1191,11 +1225,11 @@ static int coroutine_fn nbd_co_request(BlockDriverState *bs, NBDRequest *request continue; } - ret = nbd_co_receive_return_code(s, request->handle, + ret = nbd_co_receive_return_code(s, request->cookie, &request_ret, &local_err); if (local_err) { trace_nbd_co_request_fail(request->from, request->len, - request->handle, request->flags, + request->cookie, request->flags, request->type, nbd_cmd_lookup(request->type), ret, error_get_pretty(local_err)); @@ -1207,9 +1241,9 @@ static int coroutine_fn nbd_co_request(BlockDriverState *bs, NBDRequest *request return ret ? ret : request_ret; } -static int coroutine_fn nbd_client_co_preadv(BlockDriverState *bs, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +nbd_client_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { int ret, request_ret; Error *local_err = NULL; @@ -1221,7 +1255,6 @@ static int coroutine_fn nbd_client_co_preadv(BlockDriverState *bs, int64_t offse }; assert(bytes <= NBD_MAX_BUFFER_SIZE); - assert(!flags); if (!bytes) { return 0; @@ -1251,10 +1284,10 @@ static int coroutine_fn nbd_client_co_preadv(BlockDriverState *bs, int64_t offse continue; } - ret = nbd_co_receive_cmdread_reply(s, request.handle, offset, qiov, + ret = nbd_co_receive_cmdread_reply(s, request.cookie, offset, qiov, &request_ret, &local_err); if (local_err) { - trace_nbd_co_request_fail(request.from, request.len, request.handle, + trace_nbd_co_request_fail(request.from, request.len, request.cookie, request.flags, request.type, nbd_cmd_lookup(request.type), ret, error_get_pretty(local_err)); @@ -1266,9 +1299,9 @@ static int coroutine_fn nbd_client_co_preadv(BlockDriverState *bs, int64_t offse return ret ? ret : request_ret; } -static int coroutine_fn nbd_client_co_pwritev(BlockDriverState *bs, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +nbd_client_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; NBDRequest request = { @@ -1291,17 +1324,19 @@ static int coroutine_fn nbd_client_co_pwritev(BlockDriverState *bs, int64_t offs return nbd_co_request(bs, &request, qiov); } -static int coroutine_fn nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, - int64_t bytes, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + BdrvRequestFlags flags) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; NBDRequest request = { .type = NBD_CMD_WRITE_ZEROES, .from = offset, - .len = bytes, /* .len is uint32_t actually */ + .len = bytes, }; - assert(bytes <= UINT32_MAX); /* rely on max_pwrite_zeroes */ + /* rely on max_pwrite_zeroes */ + assert(bytes <= UINT32_MAX || s->info.mode >= NBD_MODE_EXTENDED); assert(!(s->info.flags & NBD_FLAG_READ_ONLY)); if (!(s->info.flags & NBD_FLAG_SEND_WRITE_ZEROES)) { @@ -1326,7 +1361,7 @@ static int coroutine_fn nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_ return nbd_co_request(bs, &request, NULL); } -static int coroutine_fn nbd_client_co_flush(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK nbd_client_co_flush(BlockDriverState *bs) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; NBDRequest request = { .type = NBD_CMD_FLUSH }; @@ -1341,17 +1376,18 @@ static int coroutine_fn nbd_client_co_flush(BlockDriverState *bs) return nbd_co_request(bs, &request, NULL); } -static int coroutine_fn nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, - int64_t bytes) +static int coroutine_fn GRAPH_RDLOCK +nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; NBDRequest request = { .type = NBD_CMD_TRIM, .from = offset, - .len = bytes, /* len is uint32_t */ + .len = bytes, }; - assert(bytes <= UINT32_MAX); /* rely on max_pdiscard */ + /* rely on max_pdiscard */ + assert(bytes <= UINT32_MAX || s->info.mode >= NBD_MODE_EXTENDED); assert(!(s->info.flags & NBD_FLAG_READ_ONLY)); if (!(s->info.flags & NBD_FLAG_SEND_TRIM) || !bytes) { @@ -1361,20 +1397,19 @@ static int coroutine_fn nbd_client_co_pdiscard(BlockDriverState *bs, int64_t off return nbd_co_request(bs, &request, NULL); } -static int coroutine_fn nbd_client_co_block_status( +static int coroutine_fn GRAPH_RDLOCK nbd_client_co_block_status( BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { int ret, request_ret; - NBDExtent extent = { 0 }; + NBDExtent64 extent = { 0 }; BDRVNBDState *s = (BDRVNBDState *)bs->opaque; Error *local_err = NULL; NBDRequest request = { .type = NBD_CMD_BLOCK_STATUS, .from = offset, - .len = MIN(QEMU_ALIGN_DOWN(INT_MAX, bs->bl.request_alignment), - MIN(bytes, s->info.size - offset)), + .len = MIN(bytes, s->info.size - offset), .flags = NBD_CMD_FLAG_REQ_ONE, }; @@ -1384,6 +1419,10 @@ static int coroutine_fn nbd_client_co_block_status( *file = bs; return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; } + if (s->info.mode < NBD_MODE_EXTENDED) { + request.len = MIN(QEMU_ALIGN_DOWN(INT_MAX, bs->bl.request_alignment), + request.len); + } /* * Work around the fact that the block layer doesn't do @@ -1408,11 +1447,11 @@ static int coroutine_fn nbd_client_co_block_status( continue; } - ret = nbd_co_receive_blockstatus_reply(s, request.handle, bytes, + ret = nbd_co_receive_blockstatus_reply(s, request.cookie, bytes, &extent, &request_ret, &local_err); if (local_err) { - trace_nbd_co_request_fail(request.from, request.len, request.handle, + trace_nbd_co_request_fail(request.from, request.len, request.cookie, request.flags, request.type, nbd_cmd_lookup(request.type), ret, error_get_pretty(local_err)); @@ -1452,14 +1491,14 @@ static void nbd_yank(void *opaque) BDRVNBDState *s = (BDRVNBDState *)bs->opaque; QEMU_LOCK_GUARD(&s->requests_lock); - qio_channel_shutdown(QIO_CHANNEL(s->ioc), QIO_CHANNEL_SHUTDOWN_BOTH, NULL); + qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); s->state = NBD_CLIENT_QUIT; } static void nbd_client_close(BlockDriverState *bs) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; - NBDRequest request = { .type = NBD_CMD_DISC }; + NBDRequest request = { .type = NBD_CMD_DISC, .mode = s->info.mode }; if (s->ioc) { nbd_send_request(s->ioc, &request); @@ -1920,11 +1959,6 @@ fail: return ret; } -static int coroutine_fn nbd_co_flush(BlockDriverState *bs) -{ - return nbd_client_co_flush(bs); -} - static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; @@ -1953,6 +1987,14 @@ static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.max_pwrite_zeroes = max; bs->bl.max_transfer = max; + /* + * Assume that if the server supports extended headers, it also + * supports unlimited size zero and trim commands. + */ + if (s->info.mode >= NBD_MODE_EXTENDED) { + bs->bl.max_pdiscard = bs->bl.max_pwrite_zeroes = 0; + } + if (s->info.opt_block && s->info.opt_block > bs->bl.opt_transfer) { bs->bl.opt_transfer = s->info.opt_block; @@ -1992,7 +2034,7 @@ static int coroutine_fn nbd_co_truncate(BlockDriverState *bs, int64_t offset, return 0; } -static int64_t nbd_getlength(BlockDriverState *bs) +static int64_t coroutine_fn nbd_co_getlength(BlockDriverState *bs) { BDRVNBDState *s = bs->opaque; @@ -2089,10 +2131,6 @@ static void nbd_attach_aio_context(BlockDriverState *bs, * the reconnect_delay_timer cannot be active here. */ assert(!s->reconnect_delay_timer); - - if (s->ioc) { - qio_channel_attach_aio_context(s->ioc, new_context); - } } static void nbd_detach_aio_context(BlockDriverState *bs) @@ -2101,10 +2139,6 @@ static void nbd_detach_aio_context(BlockDriverState *bs) assert(!s->open_timer); assert(!s->reconnect_delay_timer); - - if (s->ioc) { - qio_channel_detach_aio_context(s->ioc); - } } static BlockDriver bdrv_nbd = { @@ -2120,11 +2154,11 @@ static BlockDriver bdrv_nbd = { .bdrv_co_pwritev = nbd_client_co_pwritev, .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, .bdrv_close = nbd_close, - .bdrv_co_flush_to_os = nbd_co_flush, + .bdrv_co_flush_to_os = nbd_client_co_flush, .bdrv_co_pdiscard = nbd_client_co_pdiscard, .bdrv_refresh_limits = nbd_refresh_limits, .bdrv_co_truncate = nbd_co_truncate, - .bdrv_getlength = nbd_getlength, + .bdrv_co_getlength = nbd_co_getlength, .bdrv_refresh_filename = nbd_refresh_filename, .bdrv_co_block_status = nbd_client_co_block_status, .bdrv_dirname = nbd_dirname, @@ -2148,11 +2182,11 @@ static BlockDriver bdrv_nbd_tcp = { .bdrv_co_pwritev = nbd_client_co_pwritev, .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, .bdrv_close = nbd_close, - .bdrv_co_flush_to_os = nbd_co_flush, + .bdrv_co_flush_to_os = nbd_client_co_flush, .bdrv_co_pdiscard = nbd_client_co_pdiscard, .bdrv_refresh_limits = nbd_refresh_limits, .bdrv_co_truncate = nbd_co_truncate, - .bdrv_getlength = nbd_getlength, + .bdrv_co_getlength = nbd_co_getlength, .bdrv_refresh_filename = nbd_refresh_filename, .bdrv_co_block_status = nbd_client_co_block_status, .bdrv_dirname = nbd_dirname, @@ -2176,11 +2210,11 @@ static BlockDriver bdrv_nbd_unix = { .bdrv_co_pwritev = nbd_client_co_pwritev, .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, .bdrv_close = nbd_close, - .bdrv_co_flush_to_os = nbd_co_flush, + .bdrv_co_flush_to_os = nbd_client_co_flush, .bdrv_co_pdiscard = nbd_client_co_pdiscard, .bdrv_refresh_limits = nbd_refresh_limits, .bdrv_co_truncate = nbd_co_truncate, - .bdrv_getlength = nbd_getlength, + .bdrv_co_getlength = nbd_co_getlength, .bdrv_refresh_filename = nbd_refresh_filename, .bdrv_co_block_status = nbd_client_co_block_status, .bdrv_dirname = nbd_dirname, diff --git a/block/nfs.c b/block/nfs.c index 444c40b458..f737e19cd3 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -30,6 +30,7 @@ #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "block/block-io.h" #include "block/block_int.h" #include "block/qdict.h" #include "trace.h" @@ -113,13 +114,13 @@ static int nfs_parse_uri(const char *filename, QDict *options, Error **errp) qdict_put_str(options, "path", uri->path); for (i = 0; i < qp->n; i++) { - unsigned long long val; + uint64_t val; if (!qp->p[i].value) { error_setg(errp, "Value for NFS parameter expected: %s", qp->p[i].name); goto out; } - if (parse_uint_full(qp->p[i].value, &val, 0)) { + if (parse_uint_full(qp->p[i].value, 0, &val)) { error_setg(errp, "Illegal value for NFS parameter: %s", qp->p[i].name); goto out; @@ -194,7 +195,6 @@ static void nfs_set_events(NFSClient *client) int ev = nfs_which_events(client->context); if (ev != client->events) { aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), - false, (ev & POLLIN) ? nfs_process_read : NULL, (ev & POLLOUT) ? nfs_process_write : NULL, NULL, NULL, client); @@ -223,7 +223,7 @@ static void nfs_process_write(void *arg) qemu_mutex_unlock(&client->mutex); } -static void nfs_co_init_task(BlockDriverState *bs, NFSRPC *task) +static void coroutine_fn nfs_co_init_task(BlockDriverState *bs, NFSRPC *task) { *task = (NFSRPC) { .co = qemu_coroutine_self(), @@ -372,7 +372,7 @@ static void nfs_detach_aio_context(BlockDriverState *bs) NFSClient *client = bs->opaque; aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), - false, NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL, NULL); client->events = 0; } @@ -390,7 +390,7 @@ static void nfs_client_close(NFSClient *client) if (client->context) { qemu_mutex_lock(&client->mutex); aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context), - false, NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL, NULL); qemu_mutex_unlock(&client->mutex); if (client->fh) { nfs_close(client->context, client->fh); @@ -418,7 +418,11 @@ static int64_t nfs_client_open(NFSClient *client, BlockdevOptionsNfs *opts, int flags, int open_flags, Error **errp) { int64_t ret = -EINVAL; +#ifdef _WIN32 + struct __stat64 st; +#else struct stat st; +#endif char *file = NULL, *strp = NULL; qemu_mutex_init(&client->mutex); @@ -721,13 +725,11 @@ nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data, if (task->ret < 0) { error_report("NFS Error: %s", nfs_get_error(nfs)); } - - /* Set task->complete before reading bs->wakeup. */ - qatomic_mb_set(&task->complete, 1); - bdrv_wakeup(task->bs); + replay_bh_schedule_oneshot_event(task->client->aio_context, + nfs_co_generic_bh_cb, task); } -static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) +static int64_t coroutine_fn nfs_co_get_allocated_file_size(BlockDriverState *bs) { NFSClient *client = bs->opaque; NFSRPC task = {0}; @@ -738,15 +740,19 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) return client->st_blocks * 512; } - task.bs = bs; + nfs_co_init_task(bs, &task); task.st = &st; - if (nfs_fstat_async(client->context, client->fh, nfs_get_allocated_file_size_cb, - &task) != 0) { - return -ENOMEM; - } + WITH_QEMU_LOCK_GUARD(&client->mutex) { + if (nfs_fstat_async(client->context, client->fh, nfs_get_allocated_file_size_cb, + &task) != 0) { + return -ENOMEM; + } - nfs_set_events(client); - BDRV_POLL_WHILE(bs, !task.complete); + nfs_set_events(client); + } + while (!task.complete) { + qemu_coroutine_yield(); + } return (task.ret < 0 ? task.ret : st.st_blocks * 512); } @@ -781,7 +787,11 @@ static int nfs_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, Error **errp) { NFSClient *client = state->bs->opaque; +#ifdef _WIN32 + struct __stat64 st; +#else struct stat st; +#endif int ret = 0; if (state->flags & BDRV_O_RDWR && bdrv_is_read_only(state->bs)) { @@ -833,7 +843,7 @@ static void nfs_refresh_filename(BlockDriverState *bs) } } -static char *nfs_dirname(BlockDriverState *bs, Error **errp) +static char * GRAPH_RDLOCK nfs_dirname(BlockDriverState *bs, Error **errp) { NFSClient *client = bs->opaque; @@ -876,7 +886,7 @@ static BlockDriver bdrv_nfs = { .bdrv_has_zero_init = nfs_has_zero_init, /* libnfs does not provide the allocated filesize of a file on win32. */ #if !defined(_WIN32) - .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, + .bdrv_co_get_allocated_file_size = nfs_co_get_allocated_file_size, #endif .bdrv_co_truncate = nfs_file_co_truncate, diff --git a/block/null.c b/block/null.c index 75f7d0db40..4808704ffd 100644 --- a/block/null.c +++ b/block/null.c @@ -16,6 +16,7 @@ #include "qapi/qmp/qstring.h" #include "qemu/module.h" #include "qemu/option.h" +#include "block/block-io.h" #include "block/block_int.h" #include "sysemu/replay.h" @@ -99,7 +100,7 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags, return ret; } -static int64_t null_getlength(BlockDriverState *bs) +static int64_t coroutine_fn null_co_getlength(BlockDriverState *bs) { BDRVNullState *s = bs->opaque; return s->length; @@ -264,7 +265,8 @@ static void null_refresh_filename(BlockDriverState *bs) bs->drv->format_name); } -static int64_t null_allocated_file_size(BlockDriverState *bs) +static int64_t coroutine_fn +null_co_get_allocated_file_size(BlockDriverState *bs) { return 0; } @@ -283,8 +285,8 @@ static BlockDriver bdrv_null_co = { .bdrv_file_open = null_file_open, .bdrv_parse_filename = null_co_parse_filename, - .bdrv_getlength = null_getlength, - .bdrv_get_allocated_file_size = null_allocated_file_size, + .bdrv_co_getlength = null_co_getlength, + .bdrv_co_get_allocated_file_size = null_co_get_allocated_file_size, .bdrv_co_preadv = null_co_preadv, .bdrv_co_pwritev = null_co_pwritev, @@ -304,8 +306,8 @@ static BlockDriver bdrv_null_aio = { .bdrv_file_open = null_file_open, .bdrv_parse_filename = null_aio_parse_filename, - .bdrv_getlength = null_getlength, - .bdrv_get_allocated_file_size = null_allocated_file_size, + .bdrv_co_getlength = null_co_getlength, + .bdrv_co_get_allocated_file_size = null_co_get_allocated_file_size, .bdrv_aio_preadv = null_aio_preadv, .bdrv_aio_pwritev = null_aio_pwritev, diff --git a/block/nvme.c b/block/nvme.c index 01fb28aa63..3a3c6da73d 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -16,6 +16,7 @@ #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" +#include "qemu/defer-call.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" @@ -23,7 +24,9 @@ #include "qemu/option.h" #include "qemu/memalign.h" #include "qemu/vfio-helpers.h" +#include "block/block-io.h" #include "block/block_int.h" +#include "sysemu/block-backend.h" #include "sysemu/replay.h" #include "trace.h" @@ -118,7 +121,6 @@ struct BDRVNVMeState { int blkshift; uint64_t max_transfer; - bool plugged; bool supports_write_zeroes; bool supports_discard; @@ -166,6 +168,7 @@ static QemuOptsList runtime_opts = { static bool nvme_init_queue(BDRVNVMeState *s, NVMeQueue *q, unsigned nentries, size_t entry_bytes, Error **errp) { + ERRP_GUARD(); size_t bytes; int r; @@ -219,6 +222,7 @@ static NVMeQueuePair *nvme_create_queue_pair(BDRVNVMeState *s, unsigned idx, size_t size, Error **errp) { + ERRP_GUARD(); int i, r; NVMeQueuePair *q; uint64_t prp_list_iova; @@ -281,7 +285,7 @@ static void nvme_kick(NVMeQueuePair *q) { BDRVNVMeState *s = q->s; - if (s->plugged || !q->need_kick) { + if (!q->need_kick) { return; } trace_nvme_kick(s, q->index); @@ -293,34 +297,42 @@ static void nvme_kick(NVMeQueuePair *q) q->need_kick = 0; } -/* Find a free request element if any, otherwise: - * a) if in coroutine context, try to wait for one to become available; - * b) if not in coroutine, return NULL; - */ -static NVMeRequest *nvme_get_free_req(NVMeQueuePair *q) +static NVMeRequest *nvme_get_free_req_nofail_locked(NVMeQueuePair *q) { NVMeRequest *req; - qemu_mutex_lock(&q->lock); - - while (q->free_req_head == -1) { - if (qemu_in_coroutine()) { - trace_nvme_free_req_queue_wait(q->s, q->index); - qemu_co_queue_wait(&q->free_req_queue, &q->lock); - } else { - qemu_mutex_unlock(&q->lock); - return NULL; - } - } - req = &q->reqs[q->free_req_head]; q->free_req_head = req->free_req_next; req->free_req_next = -1; - - qemu_mutex_unlock(&q->lock); return req; } +/* Return a free request element if any, otherwise return NULL. */ +static NVMeRequest *nvme_get_free_req_nowait(NVMeQueuePair *q) +{ + QEMU_LOCK_GUARD(&q->lock); + if (q->free_req_head == -1) { + return NULL; + } + return nvme_get_free_req_nofail_locked(q); +} + +/* + * Wait for a free request to become available if necessary, then + * return it. + */ +static coroutine_fn NVMeRequest *nvme_get_free_req(NVMeQueuePair *q) +{ + QEMU_LOCK_GUARD(&q->lock); + + while (q->free_req_head == -1) { + trace_nvme_free_req_queue_wait(q->s, q->index); + qemu_co_queue_wait(&q->free_req_queue, &q->lock); + } + + return nvme_get_free_req_nofail_locked(q); +} + /* With q->lock */ static void nvme_put_free_req_locked(NVMeQueuePair *q, NVMeRequest *req) { @@ -378,10 +390,6 @@ static bool nvme_process_completion(NVMeQueuePair *q) NvmeCqe *c; trace_nvme_process_completion(s, q->index, q->inflight); - if (s->plugged) { - trace_nvme_process_completion_queue_plugged(s, q->index); - return false; - } /* * Support re-entrancy when a request cb() function invokes aio_poll(). @@ -411,9 +419,10 @@ static bool nvme_process_completion(NVMeQueuePair *q) q->cq_phase = !q->cq_phase; } cid = le16_to_cpu(c->cid); - if (cid == 0 || cid > NVME_QUEUE_SIZE) { - warn_report("NVMe: Unexpected CID in completion queue: %"PRIu32", " - "queue size: %u", cid, NVME_QUEUE_SIZE); + if (cid == 0 || cid > NVME_NUM_REQS) { + warn_report("NVMe: Unexpected CID in completion queue: %" PRIu32 + ", should be within: 1..%u inclusively", cid, + NVME_NUM_REQS); continue; } trace_nvme_complete_command(s, q->index, cid); @@ -471,6 +480,15 @@ static void nvme_trace_command(const NvmeCmd *cmd) } } +static void nvme_deferred_fn(void *opaque) +{ + NVMeQueuePair *q = opaque; + + QEMU_LOCK_GUARD(&q->lock); + nvme_kick(q); + nvme_process_completion(q); +} + static void nvme_submit_command(NVMeQueuePair *q, NVMeRequest *req, NvmeCmd *cmd, BlockCompletionFunc cb, void *opaque) @@ -487,9 +505,9 @@ static void nvme_submit_command(NVMeQueuePair *q, NVMeRequest *req, q->sq.tail * NVME_SQ_ENTRY_BYTES, cmd, sizeof(*cmd)); q->sq.tail = (q->sq.tail + 1) % NVME_QUEUE_SIZE; q->need_kick++; - nvme_kick(q); - nvme_process_completion(q); qemu_mutex_unlock(&q->lock); + + defer_call(nvme_deferred_fn, q); } static void nvme_admin_cmd_sync_cb(void *opaque, int ret) @@ -506,7 +524,7 @@ static int nvme_admin_cmd_sync(BlockDriverState *bs, NvmeCmd *cmd) AioContext *aio_context = bdrv_get_aio_context(bs); NVMeRequest *req; int ret = -EINPROGRESS; - req = nvme_get_free_req(q); + req = nvme_get_free_req_nowait(q); if (!req) { return -EBUSY; } @@ -519,6 +537,7 @@ static int nvme_admin_cmd_sync(BlockDriverState *bs, NvmeCmd *cmd) /* Returns true on success, false on failure. */ static bool nvme_identify(BlockDriverState *bs, int namespace, Error **errp) { + ERRP_GUARD(); BDRVNVMeState *s = bs->opaque; bool ret = false; QEMU_AUTO_VFREE union { @@ -853,7 +872,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, } aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier[MSIX_SHARED_IRQ_IDX], - false, nvme_handle_event, nvme_poll_cb, + nvme_handle_event, nvme_poll_cb, nvme_poll_ready); if (!nvme_identify(bs, namespace, errp)) { @@ -939,7 +958,7 @@ static void nvme_close(BlockDriverState *bs) g_free(s->queues); aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier[MSIX_SHARED_IRQ_IDX], - false, NULL, NULL, NULL); + NULL, NULL, NULL); event_notifier_cleanup(&s->irq_notifier[MSIX_SHARED_IRQ_IDX]); qemu_vfio_pci_unmap_bar(s->vfio, 0, s->bar0_wo_map, 0, sizeof(NvmeBar) + NVME_DOORBELL_SIZE); @@ -993,7 +1012,7 @@ fail: return ret; } -static int64_t nvme_getlength(BlockDriverState *bs) +static int64_t coroutine_fn nvme_co_getlength(BlockDriverState *bs) { BDRVNVMeState *s = bs->opaque; return s->nsze << s->blkshift; @@ -1234,8 +1253,10 @@ static inline bool nvme_qiov_aligned(BlockDriverState *bs, return true; } -static int nvme_co_prw(BlockDriverState *bs, uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, bool is_write, int flags) +static coroutine_fn int nvme_co_prw(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, bool is_write, + int flags) { BDRVNVMeState *s = bs->opaque; int r; @@ -1475,7 +1496,7 @@ static int coroutine_fn nvme_co_truncate(BlockDriverState *bs, int64_t offset, return -ENOTSUP; } - cur_length = nvme_getlength(bs); + cur_length = nvme_co_getlength(bs); if (offset != cur_length && exact) { error_setg(errp, "Cannot resize NVMe devices"); return -ENOTSUP; @@ -1535,7 +1556,7 @@ static void nvme_detach_aio_context(BlockDriverState *bs) aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier[MSIX_SHARED_IRQ_IDX], - false, NULL, NULL, NULL); + NULL, NULL, NULL); } static void nvme_attach_aio_context(BlockDriverState *bs, @@ -1545,7 +1566,7 @@ static void nvme_attach_aio_context(BlockDriverState *bs, s->aio_context = new_context; aio_set_event_notifier(new_context, &s->irq_notifier[MSIX_SHARED_IRQ_IDX], - false, nvme_handle_event, nvme_poll_cb, + nvme_handle_event, nvme_poll_cb, nvme_poll_ready); for (unsigned i = 0; i < s->queue_count; i++) { @@ -1556,43 +1577,22 @@ static void nvme_attach_aio_context(BlockDriverState *bs, } } -static void nvme_aio_plug(BlockDriverState *bs) -{ - BDRVNVMeState *s = bs->opaque; - assert(!s->plugged); - s->plugged = true; -} - -static void nvme_aio_unplug(BlockDriverState *bs) -{ - BDRVNVMeState *s = bs->opaque; - assert(s->plugged); - s->plugged = false; - for (unsigned i = INDEX_IO(0); i < s->queue_count; i++) { - NVMeQueuePair *q = s->queues[i]; - qemu_mutex_lock(&q->lock); - nvme_kick(q); - nvme_process_completion(q); - qemu_mutex_unlock(&q->lock); - } -} - -static void nvme_register_buf(BlockDriverState *bs, void *host, size_t size) +static bool nvme_register_buf(BlockDriverState *bs, void *host, size_t size, + Error **errp) { int ret; - Error *local_err = NULL; BDRVNVMeState *s = bs->opaque; - ret = qemu_vfio_dma_map(s->vfio, host, size, false, NULL, &local_err); - if (ret) { - /* FIXME: we may run out of IOVA addresses after repeated - * bdrv_register_buf/bdrv_unregister_buf, because nvme_vfio_dma_unmap - * doesn't reclaim addresses for fixed mappings. */ - error_reportf_err(local_err, "nvme_register_buf failed: "); - } + /* + * FIXME: we may run out of IOVA addresses after repeated + * bdrv_register_buf/bdrv_unregister_buf, because nvme_vfio_dma_unmap + * doesn't reclaim addresses for fixed mappings. + */ + ret = qemu_vfio_dma_map(s->vfio, host, size, false, NULL, errp); + return ret == 0; } -static void nvme_unregister_buf(BlockDriverState *bs, void *host) +static void nvme_unregister_buf(BlockDriverState *bs, void *host, size_t size) { BDRVNVMeState *s = bs->opaque; @@ -1632,7 +1632,7 @@ static BlockDriver bdrv_nvme = { .bdrv_parse_filename = nvme_parse_filename, .bdrv_file_open = nvme_file_open, .bdrv_close = nvme_close, - .bdrv_getlength = nvme_getlength, + .bdrv_co_getlength = nvme_co_getlength, .bdrv_probe_blocksizes = nvme_probe_blocksizes, .bdrv_co_truncate = nvme_co_truncate, @@ -1653,9 +1653,6 @@ static BlockDriver bdrv_nvme = { .bdrv_detach_aio_context = nvme_detach_aio_context, .bdrv_attach_aio_context = nvme_attach_aio_context, - .bdrv_io_plug = nvme_aio_plug, - .bdrv_io_unplug = nvme_aio_unplug, - .bdrv_register_buf = nvme_register_buf, .bdrv_unregister_buf = nvme_unregister_buf, }; diff --git a/block/parallels-ext.c b/block/parallels-ext.c index 5122f67ac2..b4e14c88f2 100644 --- a/block/parallels-ext.c +++ b/block/parallels-ext.c @@ -25,7 +25,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "block/block-io.h" #include "block/block_int.h" +#include "block/dirty-bitmap.h" #include "parallels.h" #include "crypto/hash.h" #include "qemu/uuid.h" @@ -57,11 +59,10 @@ typedef struct ParallelsDirtyBitmapFeature { } QEMU_PACKED ParallelsDirtyBitmapFeature; /* Given L1 table read bitmap data from the image and populate @bitmap */ -static int parallels_load_bitmap_data(BlockDriverState *bs, - const uint64_t *l1_table, - uint32_t l1_size, - BdrvDirtyBitmap *bitmap, - Error **errp) +static int GRAPH_RDLOCK +parallels_load_bitmap_data(BlockDriverState *bs, const uint64_t *l1_table, + uint32_t l1_size, BdrvDirtyBitmap *bitmap, + Error **errp) { BDRVParallelsState *s = bs->opaque; int ret = 0; @@ -93,8 +94,8 @@ static int parallels_load_bitmap_data(BlockDriverState *bs, if (entry == 1) { bdrv_dirty_bitmap_deserialize_ones(bitmap, offset, count, false); } else { - ret = bdrv_pread(bs->file, entry << BDRV_SECTOR_BITS, buf, - s->cluster_size); + ret = bdrv_pread(bs->file, entry << BDRV_SECTOR_BITS, + s->cluster_size, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read bitmap data cluster"); @@ -118,17 +119,16 @@ finish: * @data buffer (of @data_size size) is the Dirty bitmaps feature which * consists of ParallelsDirtyBitmapFeature followed by L1 table. */ -static BdrvDirtyBitmap *parallels_load_bitmap(BlockDriverState *bs, - uint8_t *data, - size_t data_size, - Error **errp) +static BdrvDirtyBitmap * GRAPH_RDLOCK +parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size, + Error **errp) { int ret; ParallelsDirtyBitmapFeature bf; g_autofree uint64_t *l1_table = NULL; BdrvDirtyBitmap *bitmap; QemuUUID uuid; - char uuidstr[UUID_FMT_LEN + 1]; + char uuidstr[UUID_STR_LEN]; int i; if (data_size < sizeof(bf)) { @@ -181,8 +181,9 @@ static BdrvDirtyBitmap *parallels_load_bitmap(BlockDriverState *bs, return bitmap; } -static int parallels_parse_format_extension(BlockDriverState *bs, - uint8_t *ext_cluster, Error **errp) +static int GRAPH_RDLOCK +parallels_parse_format_extension(BlockDriverState *bs, uint8_t *ext_cluster, + Error **errp) { BDRVParallelsState *s = bs->opaque; int ret; @@ -286,7 +287,7 @@ int parallels_read_format_extension(BlockDriverState *bs, assert(ext_off > 0); - ret = bdrv_pread(bs->file, ext_off, ext_cluster, s->cluster_size); + ret = bdrv_pread(bs->file, ext_off, s->cluster_size, ext_cluster, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read Format Extension cluster"); goto out; diff --git a/block/parallels.c b/block/parallels.c index 8879b7027a..9205a0864f 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -136,6 +136,12 @@ static int cluster_remainder(BDRVParallelsState *s, int64_t sector_num, return MIN(nb_sectors, ret); } +static uint32_t host_cluster_index(BDRVParallelsState *s, int64_t off) +{ + off -= s->data_start << BDRV_SECTOR_BITS; + return off / s->cluster_size; +} + static int64_t block_status(BDRVParallelsState *s, int64_t sector_num, int nb_sectors, int *pnum) { @@ -165,12 +171,89 @@ static int64_t block_status(BDRVParallelsState *s, int64_t sector_num, return start_off; } -static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) +static void parallels_set_bat_entry(BDRVParallelsState *s, + uint32_t index, uint32_t offset) +{ + s->bat_bitmap[index] = cpu_to_le32(offset); + bitmap_set(s->bat_dirty_bmap, bat_entry_off(index) / s->bat_dirty_block, 1); +} + +static int mark_used(BlockDriverState *bs, unsigned long *bitmap, + uint32_t bitmap_size, int64_t off, uint32_t count) +{ + BDRVParallelsState *s = bs->opaque; + uint32_t cluster_index = host_cluster_index(s, off); + unsigned long next_used; + if (cluster_index + count > bitmap_size) { + return -E2BIG; + } + next_used = find_next_bit(bitmap, bitmap_size, cluster_index); + if (next_used < cluster_index + count) { + return -EBUSY; + } + bitmap_set(bitmap, cluster_index, count); + return 0; +} + +/* + * Collect used bitmap. The image can contain errors, we should fill the + * bitmap anyway, as much as we can. This information will be used for + * error resolution. + */ +static int GRAPH_RDLOCK parallels_fill_used_bitmap(BlockDriverState *bs) +{ + BDRVParallelsState *s = bs->opaque; + int64_t payload_bytes; + uint32_t i; + int err = 0; + + payload_bytes = bdrv_getlength(bs->file->bs); + if (payload_bytes < 0) { + return payload_bytes; + } + payload_bytes -= s->data_start * BDRV_SECTOR_SIZE; + if (payload_bytes < 0) { + return -EINVAL; + } + + s->used_bmap_size = DIV_ROUND_UP(payload_bytes, s->cluster_size); + if (s->used_bmap_size == 0) { + return 0; + } + s->used_bmap = bitmap_try_new(s->used_bmap_size); + if (s->used_bmap == NULL) { + return -ENOMEM; + } + + for (i = 0; i < s->bat_size; i++) { + int err2; + int64_t host_off = bat2sect(s, i) << BDRV_SECTOR_BITS; + if (host_off == 0) { + continue; + } + + err2 = mark_used(bs, s->used_bmap, s->used_bmap_size, host_off, 1); + if (err2 < 0 && err == 0) { + err = err2; + } + } + return err; +} + +static void parallels_free_used_bitmap(BlockDriverState *bs) +{ + BDRVParallelsState *s = bs->opaque; + s->used_bmap_size = 0; + g_free(s->used_bmap); +} + +static int64_t coroutine_fn GRAPH_RDLOCK +allocate_clusters(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum) { int ret = 0; BDRVParallelsState *s = bs->opaque; - int64_t pos, space, idx, to_allocate, i, len; + int64_t i, pos, idx, to_allocate, first_free, host_off; pos = block_status(s, sector_num, nb_sectors, pnum); if (pos > 0) { @@ -180,7 +263,8 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, idx = sector_num / s->tracks; to_allocate = DIV_ROUND_UP(sector_num + *pnum, s->tracks) - idx; - /* This function is called only by parallels_co_writev(), which will never + /* + * This function is called only by parallels_co_writev(), which will never * pass a sector_num at or beyond the end of the image (because the block * layer never passes such a sector_num to that function). Therefore, idx * is always below s->bat_size. @@ -188,47 +272,79 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, * exceed the image end. Therefore, idx + to_allocate cannot exceed * s->bat_size. * Note that s->bat_size is an unsigned int, therefore idx + to_allocate - * will always fit into a uint32_t. */ + * will always fit into a uint32_t. + */ assert(idx < s->bat_size && idx + to_allocate <= s->bat_size); - space = to_allocate * s->tracks; - len = bdrv_getlength(bs->file->bs); - if (len < 0) { - return len; - } - if (s->data_end + space > (len >> BDRV_SECTOR_BITS)) { - space += s->prealloc_size; + first_free = find_first_zero_bit(s->used_bmap, s->used_bmap_size); + if (first_free == s->used_bmap_size) { + uint32_t new_usedsize; + int64_t bytes = to_allocate * s->cluster_size; + bytes += s->prealloc_size * BDRV_SECTOR_SIZE; + + host_off = s->data_end * BDRV_SECTOR_SIZE; + /* * We require the expanded size to read back as zero. If the * user permitted truncation, we try that; but if it fails, we * force the safer-but-slower fallocate. */ if (s->prealloc_mode == PRL_PREALLOC_MODE_TRUNCATE) { - ret = bdrv_truncate(bs->file, - (s->data_end + space) << BDRV_SECTOR_BITS, - false, PREALLOC_MODE_OFF, BDRV_REQ_ZERO_WRITE, - NULL); + ret = bdrv_co_truncate(bs->file, host_off + bytes, + false, PREALLOC_MODE_OFF, + BDRV_REQ_ZERO_WRITE, NULL); if (ret == -ENOTSUP) { s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE; } } if (s->prealloc_mode == PRL_PREALLOC_MODE_FALLOCATE) { - ret = bdrv_pwrite_zeroes(bs->file, - s->data_end << BDRV_SECTOR_BITS, - space << BDRV_SECTOR_BITS, 0); + ret = bdrv_co_pwrite_zeroes(bs->file, host_off, bytes, 0); } if (ret < 0) { return ret; } + + new_usedsize = s->used_bmap_size + bytes / s->cluster_size; + s->used_bmap = bitmap_zero_extend(s->used_bmap, s->used_bmap_size, + new_usedsize); + s->used_bmap_size = new_usedsize; + } else { + int64_t next_used; + next_used = find_next_bit(s->used_bmap, s->used_bmap_size, first_free); + + /* Not enough continuous clusters in the middle, adjust the size */ + if (next_used - first_free < to_allocate) { + to_allocate = next_used - first_free; + *pnum = (idx + to_allocate) * s->tracks - sector_num; + } + + host_off = s->data_start * BDRV_SECTOR_SIZE; + host_off += first_free * s->cluster_size; + + /* + * No need to preallocate if we are using tail area from the above + * branch. In the other case we are likely re-using hole. Preallocate + * the space if required by the prealloc_mode. + */ + if (s->prealloc_mode == PRL_PREALLOC_MODE_FALLOCATE && + host_off < s->data_end * BDRV_SECTOR_SIZE) { + ret = bdrv_co_pwrite_zeroes(bs->file, host_off, + s->cluster_size * to_allocate, 0); + if (ret < 0) { + return ret; + } + } } - /* Try to read from backing to fill empty clusters + /* + * Try to read from backing to fill empty clusters * FIXME: 1. previous write_zeroes may be redundant * 2. most of data we read from backing will be rewritten by * parallels_co_writev. On aligned-to-cluster write we do not need * this read at all. * 3. it would be good to combine write of data from backing and new - * data into one write call */ + * data into one write call. + */ if (bs->backing) { int64_t nb_cow_sectors = to_allocate * s->tracks; int64_t nb_cow_bytes = nb_cow_sectors << BDRV_SECTOR_BITS; @@ -241,26 +357,34 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, return ret; } - ret = bdrv_co_pwritev(bs->file, s->data_end * BDRV_SECTOR_SIZE, - nb_cow_bytes, buf, 0); + ret = bdrv_co_pwrite(bs->file, s->data_end * BDRV_SECTOR_SIZE, + nb_cow_bytes, buf, 0); qemu_vfree(buf); if (ret < 0) { return ret; } } + ret = mark_used(bs, s->used_bmap, s->used_bmap_size, host_off, to_allocate); + if (ret < 0) { + /* Image consistency is broken. Alarm! */ + return ret; + } for (i = 0; i < to_allocate; i++) { - s->bat_bitmap[idx + i] = cpu_to_le32(s->data_end / s->off_multiplier); - s->data_end += s->tracks; - bitmap_set(s->bat_dirty_bmap, - bat_entry_off(idx + i) / s->bat_dirty_block, 1); + parallels_set_bat_entry(s, idx + i, + host_off / BDRV_SECTOR_SIZE / s->off_multiplier); + host_off += s->cluster_size; + } + if (host_off > s->data_end * BDRV_SECTOR_SIZE) { + s->data_end = host_off / BDRV_SECTOR_SIZE; } return bat2sect(s, idx) + sector_num % s->tracks; } -static coroutine_fn int parallels_co_flush_to_os(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK +parallels_co_flush_to_os(BlockDriverState *bs) { BDRVParallelsState *s = bs->opaque; unsigned long size = DIV_ROUND_UP(s->header_size, s->bat_dirty_block); @@ -277,8 +401,8 @@ static coroutine_fn int parallels_co_flush_to_os(BlockDriverState *bs) if (off + to_write > s->header_size) { to_write = s->header_size - off; } - ret = bdrv_pwrite(bs->file, off, (uint8_t *)s->header + off, - to_write); + ret = bdrv_co_pwrite(bs->file, off, to_write, + (uint8_t *)s->header + off, 0); if (ret < 0) { qemu_co_mutex_unlock(&s->lock); return ret; @@ -291,14 +415,10 @@ static coroutine_fn int parallels_co_flush_to_os(BlockDriverState *bs) return 0; } - -static int coroutine_fn parallels_co_block_status(BlockDriverState *bs, - bool want_zero, - int64_t offset, - int64_t bytes, - int64_t *pnum, - int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +parallels_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, + int64_t bytes, int64_t *pnum, int64_t *map, + BlockDriverState **file) { BDRVParallelsState *s = bs->opaque; int count; @@ -319,16 +439,15 @@ static int coroutine_fn parallels_co_block_status(BlockDriverState *bs, return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; } -static coroutine_fn int parallels_co_writev(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, - QEMUIOVector *qiov, int flags) +static int coroutine_fn GRAPH_RDLOCK +parallels_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + QEMUIOVector *qiov, int flags) { BDRVParallelsState *s = bs->opaque; uint64_t bytes_done = 0; QEMUIOVector hd_qiov; int ret = 0; - assert(!flags); qemu_iovec_init(&hd_qiov, qiov->niov); while (nb_sectors > 0) { @@ -363,8 +482,9 @@ static coroutine_fn int parallels_co_writev(BlockDriverState *bs, return ret; } -static coroutine_fn int parallels_co_readv(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) +static int coroutine_fn GRAPH_RDLOCK +parallels_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + QEMUIOVector *qiov) { BDRVParallelsState *s = bs->opaque; uint64_t bytes_done = 0; @@ -414,88 +534,234 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs, } -static int coroutine_fn parallels_co_check(BlockDriverState *bs, - BdrvCheckResult *res, - BdrvCheckMode fix) +static int coroutine_fn GRAPH_RDLOCK +parallels_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) +{ + int ret = 0; + uint32_t cluster, count; + BDRVParallelsState *s = bs->opaque; + + /* + * The image does not support ZERO mark inside the BAT, which means that + * stale data could be exposed from the backing file. + */ + if (bs->backing) { + return -ENOTSUP; + } + + if (!QEMU_IS_ALIGNED(offset, s->cluster_size)) { + return -ENOTSUP; + } else if (!QEMU_IS_ALIGNED(bytes, s->cluster_size)) { + return -ENOTSUP; + } + + cluster = offset / s->cluster_size; + count = bytes / s->cluster_size; + + qemu_co_mutex_lock(&s->lock); + for (; count > 0; cluster++, count--) { + int64_t host_off = bat2sect(s, cluster) << BDRV_SECTOR_BITS; + if (host_off == 0) { + continue; + } + + ret = bdrv_co_pdiscard(bs->file, host_off, s->cluster_size); + if (ret < 0) { + goto done; + } + + parallels_set_bat_entry(s, cluster, 0); + bitmap_clear(s->used_bmap, host_cluster_index(s, host_off), 1); + } +done: + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +static int coroutine_fn GRAPH_RDLOCK +parallels_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + BdrvRequestFlags flags) +{ + /* + * The zero flag is missed in the Parallels format specification. We can + * resort to discard if we have no backing file (this condition is checked + * inside parallels_co_pdiscard(). + */ + return parallels_co_pdiscard(bs, offset, bytes); +} + + +static void parallels_check_unclean(BlockDriverState *bs, + BdrvCheckResult *res, + BdrvCheckMode fix) { BDRVParallelsState *s = bs->opaque; - int64_t size, prev_off, high_off; - int ret; - uint32_t i; - bool flush_bat = false; - size = bdrv_getlength(bs->file->bs); + if (!s->header_unclean) { + return; + } + + fprintf(stderr, "%s image was not closed correctly\n", + fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR"); + res->corruptions++; + if (fix & BDRV_FIX_ERRORS) { + /* parallels_close will do the job right */ + res->corruptions_fixed++; + s->header_unclean = false; + } +} + +/* + * Returns true if data_off is correct, otherwise false. In both cases + * correct_offset is set to the proper value. + */ +static bool parallels_test_data_off(BDRVParallelsState *s, + int64_t file_nb_sectors, + uint32_t *correct_offset) +{ + uint32_t data_off, min_off; + bool old_magic; + + /* + * There are two slightly different image formats: with "WithoutFreeSpace" + * or "WithouFreSpacExt" magic words. Call the first one as "old magic". + * In such images data_off field can be zero. In this case the offset is + * calculated as the end of BAT table plus some padding to ensure sector + * size alignment. + */ + old_magic = !memcmp(s->header->magic, HEADER_MAGIC, 16); + + min_off = DIV_ROUND_UP(bat_entry_off(s->bat_size), BDRV_SECTOR_SIZE); + if (!old_magic) { + min_off = ROUND_UP(min_off, s->cluster_size / BDRV_SECTOR_SIZE); + } + + if (correct_offset) { + *correct_offset = min_off; + } + + data_off = le32_to_cpu(s->header->data_off); + if (data_off == 0 && old_magic) { + return true; + } + + if (data_off < min_off || data_off > file_nb_sectors) { + return false; + } + + if (correct_offset) { + *correct_offset = data_off; + } + + return true; +} + +static int coroutine_fn GRAPH_RDLOCK +parallels_check_data_off(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix) +{ + BDRVParallelsState *s = bs->opaque; + int64_t file_size; + uint32_t data_off; + + file_size = bdrv_co_nb_sectors(bs->file->bs); + if (file_size < 0) { + res->check_errors++; + return file_size; + } + + if (parallels_test_data_off(s, file_size, &data_off)) { + return 0; + } + + res->corruptions++; + if (fix & BDRV_FIX_ERRORS) { + int err; + s->header->data_off = cpu_to_le32(data_off); + s->data_start = data_off; + + parallels_free_used_bitmap(bs); + err = parallels_fill_used_bitmap(bs); + if (err == -ENOMEM) { + res->check_errors++; + return err; + } + + res->corruptions_fixed++; + } + + fprintf(stderr, "%s data_off field has incorrect value\n", + fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR"); + + return 0; +} + +static int coroutine_fn GRAPH_RDLOCK +parallels_check_outside_image(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix) +{ + BDRVParallelsState *s = bs->opaque; + uint32_t i; + int64_t off, high_off, size; + + size = bdrv_co_getlength(bs->file->bs); if (size < 0) { res->check_errors++; return size; } - qemu_co_mutex_lock(&s->lock); - if (s->header_unclean) { - fprintf(stderr, "%s image was not closed correctly\n", - fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR"); - res->corruptions++; - if (fix & BDRV_FIX_ERRORS) { - /* parallels_close will do the job right */ - res->corruptions_fixed++; - s->header_unclean = false; - } - } - - res->bfi.total_clusters = s->bat_size; - res->bfi.compressed_clusters = 0; /* compression is not supported */ - high_off = 0; - prev_off = 0; for (i = 0; i < s->bat_size; i++) { - int64_t off = bat2sect(s, i) << BDRV_SECTOR_BITS; - if (off == 0) { - prev_off = 0; - continue; - } - - /* cluster outside the image */ - if (off > size) { + off = bat2sect(s, i) << BDRV_SECTOR_BITS; + if (off + s->cluster_size > size) { fprintf(stderr, "%s cluster %u is outside image\n", fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); res->corruptions++; if (fix & BDRV_FIX_ERRORS) { - prev_off = 0; - s->bat_bitmap[i] = 0; + parallels_set_bat_entry(s, i, 0); res->corruptions_fixed++; - flush_bat = true; - continue; } + continue; } - - res->bfi.allocated_clusters++; - if (off > high_off) { + if (high_off < off) { high_off = off; } - - if (prev_off != 0 && (prev_off + s->cluster_size) != off) { - res->bfi.fragmented_clusters++; - } - prev_off = off; } - ret = 0; - if (flush_bat) { - ret = bdrv_pwrite_sync(bs->file, 0, s->header, s->header_size); - if (ret < 0) { - res->check_errors++; - goto out; - } + if (high_off == 0) { + res->image_end_offset = s->data_end << BDRV_SECTOR_BITS; + } else { + res->image_end_offset = high_off + s->cluster_size; + s->data_end = res->image_end_offset >> BDRV_SECTOR_BITS; + } + + return 0; +} + +static int coroutine_fn GRAPH_RDLOCK +parallels_check_leak(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix, bool explicit) +{ + BDRVParallelsState *s = bs->opaque; + int64_t size; + int ret; + + size = bdrv_co_getlength(bs->file->bs); + if (size < 0) { + res->check_errors++; + return size; } - res->image_end_offset = high_off + s->cluster_size; if (size > res->image_end_offset) { int64_t count; count = DIV_ROUND_UP(size - res->image_end_offset, s->cluster_size); - fprintf(stderr, "%s space leaked at the end of the image %" PRId64 "\n", - fix & BDRV_FIX_LEAKS ? "Repairing" : "ERROR", - size - res->image_end_offset); - res->leaks += count; + if (explicit) { + fprintf(stderr, + "%s space leaked at the end of the image %" PRId64 "\n", + fix & BDRV_FIX_LEAKS ? "Repairing" : "ERROR", + size - res->image_end_offset); + res->leaks += count; + } if (fix & BDRV_FIX_LEAKS) { Error *local_err = NULL; @@ -503,25 +769,231 @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs, * In order to really repair the image, we must shrink it. * That means we have to pass exact=true. */ - ret = bdrv_truncate(bs->file, res->image_end_offset, true, - PREALLOC_MODE_OFF, 0, &local_err); + ret = bdrv_co_truncate(bs->file, res->image_end_offset, true, + PREALLOC_MODE_OFF, 0, &local_err); if (ret < 0) { error_report_err(local_err); res->check_errors++; - goto out; + return ret; + } + if (explicit) { + res->leaks_fixed += count; } - res->leaks_fixed += count; } } -out: - qemu_co_mutex_unlock(&s->lock); + return 0; +} + +static int coroutine_fn GRAPH_RDLOCK +parallels_check_duplicate(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix) +{ + BDRVParallelsState *s = bs->opaque; + int64_t host_off, host_sector, guest_sector; + unsigned long *bitmap; + uint32_t i, bitmap_size, bat_entry; + int n, ret = 0; + uint64_t *buf = NULL; + bool fixed = false; + + /* + * Create a bitmap of used clusters. + * If a bit is set, there is a BAT entry pointing to this cluster. + * Loop through the BAT entries, check bits relevant to an entry offset. + * If bit is set, this entry is duplicated. Otherwise set the bit. + * + * We shouldn't worry about newly allocated clusters outside the image + * because they are created higher then any existing cluster pointed by + * a BAT entry. + */ + bitmap_size = host_cluster_index(s, res->image_end_offset); + if (bitmap_size == 0) { + return 0; + } + if (res->image_end_offset % s->cluster_size) { + /* A not aligned image end leads to a bitmap shorter by 1 */ + bitmap_size++; + } + + bitmap = bitmap_new(bitmap_size); + + buf = qemu_blockalign(bs, s->cluster_size); + + for (i = 0; i < s->bat_size; i++) { + host_off = bat2sect(s, i) << BDRV_SECTOR_BITS; + if (host_off == 0) { + continue; + } + + ret = mark_used(bs, bitmap, bitmap_size, host_off, 1); + assert(ret != -E2BIG); + if (ret == 0) { + continue; + } + + /* this cluster duplicates another one */ + fprintf(stderr, "%s duplicate offset in BAT entry %u\n", + fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); + + res->corruptions++; + + if (!(fix & BDRV_FIX_ERRORS)) { + continue; + } + + /* + * Reset the entry and allocate a new cluster + * for the relevant guest offset. In this way we let + * the lower layer to place the new cluster properly. + * Copy the original cluster to the allocated one. + * But before save the old offset value for repairing + * if we have an error. + */ + bat_entry = s->bat_bitmap[i]; + parallels_set_bat_entry(s, i, 0); + + ret = bdrv_co_pread(bs->file, host_off, s->cluster_size, buf, 0); + if (ret < 0) { + res->check_errors++; + goto out_repair_bat; + } + + guest_sector = (i * (int64_t)s->cluster_size) >> BDRV_SECTOR_BITS; + host_sector = allocate_clusters(bs, guest_sector, s->tracks, &n); + if (host_sector < 0) { + res->check_errors++; + goto out_repair_bat; + } + host_off = host_sector << BDRV_SECTOR_BITS; + + ret = bdrv_co_pwrite(bs->file, host_off, s->cluster_size, buf, 0); + if (ret < 0) { + res->check_errors++; + goto out_repair_bat; + } + + if (host_off + s->cluster_size > res->image_end_offset) { + res->image_end_offset = host_off + s->cluster_size; + } + + /* + * In the future allocate_cluster() will reuse holed offsets + * inside the image. Keep the used clusters bitmap content + * consistent for the new allocated clusters too. + * + * Note, clusters allocated outside the current image are not + * considered, and the bitmap size doesn't change. This specifically + * means that -E2BIG is OK. + */ + ret = mark_used(bs, bitmap, bitmap_size, host_off, 1); + if (ret == -EBUSY) { + res->check_errors++; + goto out_repair_bat; + } + + fixed = true; + res->corruptions_fixed++; + + } + + if (fixed) { + /* + * When new clusters are allocated, the file size increases by + * 128 Mb. We need to truncate the file to the right size. Let + * the leak fix code make its job without res changing. + */ + ret = parallels_check_leak(bs, res, fix, false); + } + +out_free: + g_free(buf); + g_free(bitmap); + return ret; +/* + * We can get here only from places where index and old_offset have + * meaningful values. + */ +out_repair_bat: + s->bat_bitmap[i] = bat_entry; + goto out_free; +} + +static void parallels_collect_statistics(BlockDriverState *bs, + BdrvCheckResult *res, + BdrvCheckMode fix) +{ + BDRVParallelsState *s = bs->opaque; + int64_t off, prev_off; + uint32_t i; + + res->bfi.total_clusters = s->bat_size; + res->bfi.compressed_clusters = 0; /* compression is not supported */ + + prev_off = 0; + for (i = 0; i < s->bat_size; i++) { + off = bat2sect(s, i) << BDRV_SECTOR_BITS; + /* + * If BDRV_FIX_ERRORS is not set, out-of-image BAT entries were not + * fixed. Skip not allocated and out-of-image BAT entries. + */ + if (off == 0 || off + s->cluster_size > res->image_end_offset) { + prev_off = 0; + continue; + } + + if (prev_off != 0 && (prev_off + s->cluster_size) != off) { + res->bfi.fragmented_clusters++; + } + prev_off = off; + res->bfi.allocated_clusters++; + } +} + +static int coroutine_fn GRAPH_RDLOCK +parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix) +{ + BDRVParallelsState *s = bs->opaque; + int ret; + + WITH_QEMU_LOCK_GUARD(&s->lock) { + parallels_check_unclean(bs, res, fix); + + ret = parallels_check_data_off(bs, res, fix); + if (ret < 0) { + return ret; + } + + ret = parallels_check_outside_image(bs, res, fix); + if (ret < 0) { + return ret; + } + + ret = parallels_check_leak(bs, res, fix, true); + if (ret < 0) { + return ret; + } + + ret = parallels_check_duplicate(bs, res, fix); + if (ret < 0) { + return ret; + } + + parallels_collect_statistics(bs, res, fix); + } + + ret = bdrv_co_flush(bs); + if (ret < 0) { + res->check_errors++; + } + return ret; } -static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +parallels_co_create(BlockdevCreateOptions* opts, Error **errp) { BlockdevCreateOptionsParallels *parallels_opts; BlockDriverState *bs; @@ -565,13 +1037,13 @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts, } /* Create BlockBackend to write to the image */ - bs = bdrv_open_blockdev_ref(parallels_opts->file, errp); + bs = bdrv_co_open_blockdev_ref(parallels_opts->file, errp); if (bs == NULL) { return -EIO; } - blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, - errp); + blk = blk_co_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, + errp); if (!blk) { ret = -EPERM; goto out; @@ -599,20 +1071,20 @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts, memset(tmp, 0, sizeof(tmp)); memcpy(tmp, &header, sizeof(header)); - ret = blk_pwrite(blk, 0, tmp, BDRV_SECTOR_SIZE, 0); + ret = blk_co_pwrite(blk, 0, BDRV_SECTOR_SIZE, tmp, 0); if (ret < 0) { goto exit; } - ret = blk_pwrite_zeroes(blk, BDRV_SECTOR_SIZE, - (bat_sectors - 1) << BDRV_SECTOR_BITS, 0); + ret = blk_co_pwrite_zeroes(blk, BDRV_SECTOR_SIZE, + (bat_sectors - 1) << BDRV_SECTOR_BITS, 0); if (ret < 0) { goto exit; } ret = 0; out: - blk_unref(blk); - bdrv_unref(bs); + blk_co_unref(blk); + bdrv_co_unref(bs); return ret; exit: @@ -620,10 +1092,9 @@ exit: goto out; } -static int coroutine_fn parallels_co_create_opts(BlockDriver *drv, - const char *filename, - QemuOpts *opts, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +parallels_co_create_opts(BlockDriver *drv, const char *filename, + QemuOpts *opts, Error **errp) { BlockdevCreateOptions *create_options = NULL; BlockDriverState *bs = NULL; @@ -646,13 +1117,13 @@ static int coroutine_fn parallels_co_create_opts(BlockDriver *drv, } /* Create and open the file (protocol layer) */ - ret = bdrv_create_file(filename, opts, errp); + ret = bdrv_co_create_file(filename, opts, errp); if (ret < 0) { goto done; } - bs = bdrv_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); + bs = bdrv_co_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); if (bs == NULL) { ret = -EIO; goto done; @@ -690,7 +1161,7 @@ static int coroutine_fn parallels_co_create_opts(BlockDriver *drv, done: qobject_unref(qdict); - bdrv_unref(bs); + bdrv_co_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; } @@ -714,7 +1185,7 @@ static int parallels_probe(const uint8_t *buf, int buf_size, return 0; } -static int parallels_update_header(BlockDriverState *bs) +static int GRAPH_RDLOCK parallels_update_header(BlockDriverState *bs) { BDRVParallelsState *s = bs->opaque; unsigned size = MAX(bdrv_opt_mem_align(bs->file->bs), @@ -723,7 +1194,45 @@ static int parallels_update_header(BlockDriverState *bs) if (size > s->header_size) { size = s->header_size; } - return bdrv_pwrite_sync(bs->file, 0, s->header, size); + return bdrv_pwrite_sync(bs->file, 0, size, s->header, 0); +} + + +static int parallels_opts_prealloc(BlockDriverState *bs, QDict *options, + Error **errp) +{ + int err; + char *buf; + int64_t bytes; + BDRVParallelsState *s = bs->opaque; + Error *local_err = NULL; + QemuOpts *opts = qemu_opts_create(¶llels_runtime_opts, NULL, 0, errp); + if (!opts) { + return -ENOMEM; + } + + err = -EINVAL; + if (!qemu_opts_absorb_qdict(opts, options, errp)) { + goto done; + } + + bytes = qemu_opt_get_size_del(opts, PARALLELS_OPT_PREALLOC_SIZE, 0); + s->prealloc_size = bytes >> BDRV_SECTOR_BITS; + buf = qemu_opt_get_del(opts, PARALLELS_OPT_PREALLOC_MODE); + /* prealloc_mode can be downgraded later during allocate_clusters */ + s->prealloc_mode = qapi_enum_parse(&prealloc_mode_lookup, buf, + PRL_PREALLOC_MODE_FALLOCATE, + &local_err); + g_free(buf); + if (local_err != NULL) { + error_propagate(errp, local_err); + goto done; + } + err = 0; + +done: + qemu_opts_del(opts); + return err; } static int parallels_open(BlockDriverState *bs, QDict *options, int flags, @@ -732,19 +1241,30 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, BDRVParallelsState *s = bs->opaque; ParallelsHeader ph; int ret, size, i; - QemuOpts *opts = NULL; - Error *local_err = NULL; - char *buf; + int64_t file_nb_sectors, sector; + uint32_t data_start; + bool need_check = false; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_IMAGE, false, errp); - if (!bs->file) { + ret = parallels_opts_prealloc(bs, options, errp); + if (ret < 0) { + return ret; + } + + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; + } + + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + file_nb_sectors = bdrv_nb_sectors(bs->file->bs); + if (file_nb_sectors < 0) { return -EINVAL; } - ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph)); + ret = bdrv_pread(bs->file, 0, sizeof(ph), &ph, 0); if (ret < 0) { - goto fail; + return ret; } bs->total_sectors = le64_to_cpu(ph.nb_sectors); @@ -764,85 +1284,51 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, s->tracks = le32_to_cpu(ph.tracks); if (s->tracks == 0) { error_setg(errp, "Invalid image: Zero sectors per track"); - ret = -EINVAL; - goto fail; + return -EINVAL; } if (s->tracks > INT32_MAX/513) { error_setg(errp, "Invalid image: Too big cluster"); - ret = -EFBIG; - goto fail; + return -EFBIG; } + s->prealloc_size = MAX(s->tracks, s->prealloc_size); s->cluster_size = s->tracks << BDRV_SECTOR_BITS; s->bat_size = le32_to_cpu(ph.bat_entries); if (s->bat_size > INT_MAX / sizeof(uint32_t)) { error_setg(errp, "Catalog too large"); - ret = -EFBIG; - goto fail; + return -EFBIG; } size = bat_entry_off(s->bat_size); s->header_size = ROUND_UP(size, bdrv_opt_mem_align(bs->file->bs)); s->header = qemu_try_blockalign(bs->file->bs, s->header_size); if (s->header == NULL) { - ret = -ENOMEM; - goto fail; - } - s->data_end = le32_to_cpu(ph.data_off); - if (s->data_end == 0) { - s->data_end = ROUND_UP(bat_entry_off(s->bat_size), BDRV_SECTOR_SIZE); - } - if (s->data_end < s->header_size) { - /* there is not enough unused space to fit to block align between BAT - and actual data. We can't avoid read-modify-write... */ - s->header_size = size; + return -ENOMEM; } - ret = bdrv_pread(bs->file, 0, s->header, s->header_size); + ret = bdrv_pread(bs->file, 0, s->header_size, s->header, 0); if (ret < 0) { goto fail; } s->bat_bitmap = (uint32_t *)(s->header + 1); - for (i = 0; i < s->bat_size; i++) { - int64_t off = bat2sect(s, i); - if (off >= s->data_end) { - s->data_end = off + s->tracks; - } - } - if (le32_to_cpu(ph.inuse) == HEADER_INUSE_MAGIC) { - /* Image was not closed correctly. The check is mandatory */ - s->header_unclean = true; - if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_CHECK)) { - error_setg(errp, "parallels: Image was not closed correctly; " - "cannot be opened read/write"); - ret = -EACCES; - goto fail; - } + need_check = s->header_unclean = true; } - opts = qemu_opts_create(¶llels_runtime_opts, NULL, 0, errp); - if (!opts) { - goto fail_options; + { + bool ok = parallels_test_data_off(s, file_nb_sectors, &data_start); + need_check = need_check || !ok; } - if (!qemu_opts_absorb_qdict(opts, options, errp)) { - goto fail_options; - } - - s->prealloc_size = - qemu_opt_get_size_del(opts, PARALLELS_OPT_PREALLOC_SIZE, 0); - s->prealloc_size = MAX(s->tracks, s->prealloc_size >> BDRV_SECTOR_BITS); - buf = qemu_opt_get_del(opts, PARALLELS_OPT_PREALLOC_MODE); - /* prealloc_mode can be downgraded later during allocate_clusters */ - s->prealloc_mode = qapi_enum_parse(&prealloc_mode_lookup, buf, - PRL_PREALLOC_MODE_FALLOCATE, - &local_err); - g_free(buf); - if (local_err != NULL) { - error_propagate(errp, local_err); - goto fail_options; + s->data_start = data_start; + s->data_end = s->data_start; + if (s->data_end < (s->header_size >> BDRV_SECTOR_BITS)) { + /* + * There is not enough unused space to fit to block align between BAT + * and actual data. We can't avoid read-modify-write... + */ + s->header_size = size; } if (ph.ext_off) { @@ -878,19 +1364,61 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, error_setg(&s->migration_blocker, "The Parallels format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - ret = migrate_add_blocker(s->migration_blocker, errp); + + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { - error_free(s->migration_blocker); goto fail; } qemu_co_mutex_init(&s->lock); + + for (i = 0; i < s->bat_size; i++) { + sector = bat2sect(s, i); + if (sector + s->tracks > s->data_end) { + s->data_end = sector + s->tracks; + } + } + need_check = need_check || s->data_end > file_nb_sectors; + + if (!need_check) { + ret = parallels_fill_used_bitmap(bs); + if (ret == -ENOMEM) { + goto fail; + } + need_check = need_check || ret < 0; /* These are correctable errors */ + } + + /* + * We don't repair the image here if it's opened for checks. Also we don't + * want to change inactive images and can't change readonly images. + */ + if ((flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) || !(flags & BDRV_O_RDWR)) { + return 0; + } + + /* Repair the image if corruption was detected. */ + if (need_check) { + BdrvCheckResult res; + ret = bdrv_check(bs, &res, BDRV_FIX_ERRORS | BDRV_FIX_LEAKS); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not repair corrupted image"); + migrate_del_blocker(&s->migration_blocker); + goto fail; + } + } return 0; fail_format: error_setg(errp, "Image not in Parallels format"); -fail_options: - ret = -EINVAL; + return -EINVAL; + fail: + /* + * "s" object was allocated by g_malloc0 so we can safely + * try to free its fields even they were not allocated. + */ + parallels_free_used_bitmap(bs); + + g_free(s->bat_dirty_bmap); qemu_vfree(s->header); return ret; } @@ -900,6 +1428,8 @@ static void parallels_close(BlockDriverState *bs) { BDRVParallelsState *s = bs->opaque; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if ((bs->open_flags & BDRV_O_RDWR) && !(bs->open_flags & BDRV_O_INACTIVE)) { s->header->inuse = 0; parallels_update_header(bs); @@ -909,31 +1439,42 @@ static void parallels_close(BlockDriverState *bs) PREALLOC_MODE_OFF, 0, NULL); } + parallels_free_used_bitmap(bs); + g_free(s->bat_dirty_bmap); qemu_vfree(s->header); - migrate_del_blocker(s->migration_blocker); - error_free(s->migration_blocker); + migrate_del_blocker(&s->migration_blocker); +} + +static bool parallels_is_support_dirty_bitmaps(BlockDriverState *bs) +{ + return 1; } static BlockDriver bdrv_parallels = { - .format_name = "parallels", - .instance_size = sizeof(BDRVParallelsState), - .bdrv_probe = parallels_probe, - .bdrv_open = parallels_open, - .bdrv_close = parallels_close, - .bdrv_child_perm = bdrv_default_perms, - .bdrv_co_block_status = parallels_co_block_status, - .bdrv_has_zero_init = bdrv_has_zero_init_1, - .bdrv_co_flush_to_os = parallels_co_flush_to_os, - .bdrv_co_readv = parallels_co_readv, - .bdrv_co_writev = parallels_co_writev, - .is_format = true, - .supports_backing = true, - .bdrv_co_create = parallels_co_create, - .bdrv_co_create_opts = parallels_co_create_opts, - .bdrv_co_check = parallels_co_check, - .create_opts = ¶llels_create_opts, + .format_name = "parallels", + .instance_size = sizeof(BDRVParallelsState), + .create_opts = ¶llels_create_opts, + .is_format = true, + .supports_backing = true, + + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_supports_persistent_dirty_bitmap = parallels_is_support_dirty_bitmaps, + + .bdrv_probe = parallels_probe, + .bdrv_open = parallels_open, + .bdrv_close = parallels_close, + .bdrv_child_perm = bdrv_default_perms, + .bdrv_co_block_status = parallels_co_block_status, + .bdrv_co_flush_to_os = parallels_co_flush_to_os, + .bdrv_co_readv = parallels_co_readv, + .bdrv_co_writev = parallels_co_writev, + .bdrv_co_create = parallels_co_create, + .bdrv_co_create_opts = parallels_co_create_opts, + .bdrv_co_check = parallels_co_check, + .bdrv_co_pdiscard = parallels_co_pdiscard, + .bdrv_co_pwrite_zeroes = parallels_co_pwrite_zeroes, }; static void bdrv_parallels_init(void) diff --git a/block/parallels.h b/block/parallels.h index f22f43f988..423b2ad727 100644 --- a/block/parallels.h +++ b/block/parallels.h @@ -72,9 +72,13 @@ typedef struct BDRVParallelsState { unsigned long *bat_dirty_bmap; unsigned int bat_dirty_block; + unsigned long *used_bmap; + unsigned long used_bmap_size; + uint32_t *bat_bitmap; unsigned int bat_size; + int64_t data_start; int64_t data_end; uint64_t prealloc_size; ParallelsPreallocMode prealloc_mode; @@ -86,7 +90,8 @@ typedef struct BDRVParallelsState { Error *migration_blocker; } BDRVParallelsState; -int parallels_read_format_extension(BlockDriverState *bs, - int64_t ext_off, Error **errp); +int GRAPH_RDLOCK +parallels_read_format_extension(BlockDriverState *bs, int64_t ext_off, + Error **errp); #endif diff --git a/block/preallocate.c b/block/preallocate.c index e15cb8c74a..d215bc5d6d 100644 --- a/block/preallocate.c +++ b/block/preallocate.c @@ -30,6 +30,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "qemu/units.h" +#include "block/block-io.h" #include "block/block_int.h" @@ -74,8 +75,14 @@ typedef struct BDRVPreallocateState { * be invalid (< 0) when we don't have both exclusive BLK_PERM_RESIZE and * BLK_PERM_WRITE permissions on file child. */ + + /* Gives up the resize permission on children when parents don't need it */ + QEMUBH *drop_resize_bh; } BDRVPreallocateState; +static int preallocate_drop_resize(BlockDriverState *bs, Error **errp); +static void preallocate_drop_resize_bh(void *opaque); + #define PREALLOCATE_OPT_PREALLOC_ALIGN "prealloc-align" #define PREALLOCATE_OPT_PREALLOC_SIZE "prealloc-size" static QemuOptsList runtime_opts = { @@ -134,20 +141,24 @@ static int preallocate_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVPreallocateState *s = bs->opaque; + int ret; + + GLOBAL_STATE_CODE(); /* * s->data_end and friends should be initialized on permission update. * For this to work, mark them invalid. */ s->file_end = s->zero_start = s->data_end = -EINVAL; + s->drop_resize_bh = qemu_bh_new(preallocate_drop_resize_bh, bs); - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, errp); - if (!bs->file) { - return -EINVAL; + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!preallocate_absorb_opts(&s->opts, options, bs->file->bs, errp)) { return -EINVAL; } @@ -162,26 +173,46 @@ static int preallocate_open(BlockDriverState *bs, QDict *options, int flags, return 0; } -static void preallocate_close(BlockDriverState *bs) +static int GRAPH_RDLOCK +preallocate_truncate_to_real_size(BlockDriverState *bs, Error **errp) { - int ret; BDRVPreallocateState *s = bs->opaque; - - if (s->data_end < 0) { - return; - } + int ret; if (s->file_end < 0) { s->file_end = bdrv_getlength(bs->file->bs); if (s->file_end < 0) { - return; + error_setg_errno(errp, -s->file_end, "Failed to get file length"); + return s->file_end; } } if (s->data_end < s->file_end) { ret = bdrv_truncate(bs->file, s->data_end, true, PREALLOC_MODE_OFF, 0, NULL); - s->file_end = ret < 0 ? ret : s->data_end; + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to drop preallocation"); + s->file_end = ret; + return ret; + } + s->file_end = s->data_end; + } + + return 0; +} + +static void preallocate_close(BlockDriverState *bs) +{ + BDRVPreallocateState *s = bs->opaque; + + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + qemu_bh_cancel(s->drop_resize_bh); + qemu_bh_delete(s->drop_resize_bh); + + if (s->data_end >= 0) { + preallocate_truncate_to_real_size(bs, NULL); } } @@ -198,6 +229,10 @@ static int preallocate_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp) { PreallocateOpts *opts = g_new0(PreallocateOpts, 1); + int ret; + + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!preallocate_absorb_opts(opts, reopen_state->options, reopen_state->bs->file->bs, errp)) { @@ -205,6 +240,19 @@ static int preallocate_reopen_prepare(BDRVReopenState *reopen_state, return -EINVAL; } + /* + * Drop the preallocation already here if reopening read-only. The child + * might also be reopened read-only and then scheduling a BH during the + * permission update is too late. + */ + if ((reopen_state->flags & BDRV_O_RDWR) == 0) { + ret = preallocate_drop_resize(reopen_state->bs, errp); + if (ret < 0) { + g_free(opts); + return ret; + } + } + reopen_state->opaque = opts; return 0; @@ -226,16 +274,17 @@ static void preallocate_reopen_abort(BDRVReopenState *state) state->opaque = NULL; } -static coroutine_fn int preallocate_co_preadv_part( - BlockDriverState *bs, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +preallocate_co_preadv_part(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) { return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset, flags); } -static int coroutine_fn preallocate_co_pdiscard(BlockDriverState *bs, - int64_t offset, int64_t bytes) +static int coroutine_fn GRAPH_RDLOCK +preallocate_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { return bdrv_co_pdiscard(bs->file, offset, bytes); } @@ -245,7 +294,7 @@ static bool can_write_resize(uint64_t perm) return (perm & BLK_PERM_WRITE) && (perm & BLK_PERM_RESIZE); } -static bool has_prealloc_perms(BlockDriverState *bs) +static bool GRAPH_RDLOCK has_prealloc_perms(BlockDriverState *bs) { BDRVPreallocateState *s = bs->opaque; @@ -269,8 +318,9 @@ static bool has_prealloc_perms(BlockDriverState *bs) * want_merge_zero is used to merge write-zero request with preallocation in * one bdrv_co_pwrite_zeroes() call. */ -static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset, - int64_t bytes, bool want_merge_zero) +static bool coroutine_fn GRAPH_RDLOCK +handle_write(BlockDriverState *bs, int64_t offset, int64_t bytes, + bool want_merge_zero) { BDRVPreallocateState *s = bs->opaque; int64_t end = offset + bytes; @@ -287,7 +337,7 @@ static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset, } if (s->data_end < 0) { - s->data_end = bdrv_getlength(bs->file->bs); + s->data_end = bdrv_co_getlength(bs->file->bs); if (s->data_end < 0) { return false; } @@ -309,7 +359,7 @@ static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset, } if (s->file_end < 0) { - s->file_end = bdrv_getlength(bs->file->bs); + s->file_end = bdrv_co_getlength(bs->file->bs); if (s->file_end < 0) { return false; } @@ -345,8 +395,9 @@ static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset, return want_merge_zero; } -static int coroutine_fn preallocate_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int64_t bytes, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +preallocate_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, + int64_t bytes, BdrvRequestFlags flags) { bool want_merge_zero = !(flags & ~(BDRV_REQ_ZERO_WRITE | BDRV_REQ_NO_FALLBACK)); @@ -357,12 +408,10 @@ static int coroutine_fn preallocate_co_pwrite_zeroes(BlockDriverState *bs, return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); } -static coroutine_fn int preallocate_co_pwritev_part(BlockDriverState *bs, - int64_t offset, - int64_t bytes, - QEMUIOVector *qiov, - size_t qiov_offset, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +preallocate_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) { handle_write(bs, offset, bytes, false); @@ -370,7 +419,7 @@ static coroutine_fn int preallocate_co_pwritev_part(BlockDriverState *bs, flags); } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK preallocate_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) @@ -381,7 +430,7 @@ preallocate_co_truncate(BlockDriverState *bs, int64_t offset, if (s->data_end >= 0 && offset > s->data_end) { if (s->file_end < 0) { - s->file_end = bdrv_getlength(bs->file->bs); + s->file_end = bdrv_co_getlength(bs->file->bs); if (s->file_end < 0) { error_setg(errp, "failed to get file length"); return s->file_end; @@ -437,12 +486,13 @@ preallocate_co_truncate(BlockDriverState *bs, int64_t offset, return 0; } -static int coroutine_fn preallocate_co_flush(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK preallocate_co_flush(BlockDriverState *bs) { return bdrv_co_flush(bs->file->bs); } -static int64_t preallocate_getlength(BlockDriverState *bs) +static int64_t coroutine_fn GRAPH_RDLOCK +preallocate_co_getlength(BlockDriverState *bs) { int64_t ret; BDRVPreallocateState *s = bs->opaque; @@ -451,7 +501,7 @@ static int64_t preallocate_getlength(BlockDriverState *bs) return s->data_end; } - ret = bdrv_getlength(bs->file->bs); + ret = bdrv_co_getlength(bs->file->bs); if (has_prealloc_perms(bs)) { s->file_end = s->zero_start = s->data_end = ret; @@ -460,58 +510,63 @@ static int64_t preallocate_getlength(BlockDriverState *bs) return ret; } -static int preallocate_check_perm(BlockDriverState *bs, - uint64_t perm, uint64_t shared, Error **errp) +static int GRAPH_RDLOCK +preallocate_drop_resize(BlockDriverState *bs, Error **errp) { BDRVPreallocateState *s = bs->opaque; + int ret; - if (s->data_end >= 0 && !can_write_resize(perm)) { - /* - * Lose permissions. - * We should truncate in check_perm, as in set_perm bs->file->perm will - * be already changed, and we should not violate it. - */ - if (s->file_end < 0) { - s->file_end = bdrv_getlength(bs->file->bs); - if (s->file_end < 0) { - error_setg(errp, "Failed to get file length"); - return s->file_end; - } - } - - if (s->data_end < s->file_end) { - int ret = bdrv_truncate(bs->file, s->data_end, true, - PREALLOC_MODE_OFF, 0, NULL); - if (ret < 0) { - error_setg(errp, "Failed to drop preallocation"); - s->file_end = ret; - return ret; - } - s->file_end = s->data_end; - } + if (s->data_end < 0) { + return 0; } + /* + * Before switching children to be read-only, truncate them to remove + * the preallocation and let them have the real size. + */ + ret = preallocate_truncate_to_real_size(bs, errp); + if (ret < 0) { + return ret; + } + + /* + * We'll drop our permissions and will allow other users to take write and + * resize permissions (see preallocate_child_perm). Anyone will be able to + * change the child, so mark all states invalid. We'll regain control if a + * parent requests write access again. + */ + s->data_end = s->file_end = s->zero_start = -EINVAL; + + bdrv_child_refresh_perms(bs, bs->file, NULL); + return 0; } -static void preallocate_set_perm(BlockDriverState *bs, - uint64_t perm, uint64_t shared) +static void preallocate_drop_resize_bh(void *opaque) +{ + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + /* + * In case of errors, we'll simply keep the exclusive lock on the image + * indefinitely. + */ + preallocate_drop_resize(opaque, NULL); +} + +static void GRAPH_RDLOCK +preallocate_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared) { BDRVPreallocateState *s = bs->opaque; if (can_write_resize(perm)) { + qemu_bh_cancel(s->drop_resize_bh); if (s->data_end < 0) { s->data_end = s->file_end = s->zero_start = - bdrv_getlength(bs->file->bs); + bs->file->bs->total_sectors * BDRV_SECTOR_SIZE; } } else { - /* - * We drop our permissions, as well as allow shared - * permissions (see preallocate_child_perm), anyone will be able to - * change the child, so mark all states invalid. We'll regain control if - * get good permissions back. - */ - s->data_end = s->file_end = s->zero_start = -EINVAL; + qemu_bh_schedule(s->drop_resize_bh); } } @@ -519,10 +574,16 @@ static void preallocate_child_perm(BlockDriverState *bs, BdrvChild *c, BdrvChildRole role, BlockReopenQueue *reopen_queue, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) { + BDRVPreallocateState *s = bs->opaque; + bdrv_default_perms(bs, c, role, reopen_queue, perm, shared, nperm, nshared); - if (can_write_resize(perm)) { - /* This should come by default, but let's enforce: */ + /* + * We need exclusive write and resize permissions on the child not only when + * the parent can write to it, but also after the parent gave up write + * permissions until preallocate_drop_resize() has completed. + */ + if (can_write_resize(perm) || s->data_end != -EINVAL) { *nperm |= BLK_PERM_WRITE | BLK_PERM_RESIZE; /* @@ -533,13 +594,13 @@ static void preallocate_child_perm(BlockDriverState *bs, BdrvChild *c, } } -BlockDriver bdrv_preallocate_filter = { +static BlockDriver bdrv_preallocate_filter = { .format_name = "preallocate", .instance_size = sizeof(BDRVPreallocateState), - .bdrv_getlength = preallocate_getlength, - .bdrv_open = preallocate_open, - .bdrv_close = preallocate_close, + .bdrv_co_getlength = preallocate_co_getlength, + .bdrv_open = preallocate_open, + .bdrv_close = preallocate_close, .bdrv_reopen_prepare = preallocate_reopen_prepare, .bdrv_reopen_commit = preallocate_reopen_commit, @@ -552,11 +613,9 @@ BlockDriver bdrv_preallocate_filter = { .bdrv_co_flush = preallocate_co_flush, .bdrv_co_truncate = preallocate_co_truncate, - .bdrv_check_perm = preallocate_check_perm, .bdrv_set_perm = preallocate_set_perm, .bdrv_child_perm = preallocate_child_perm, - .has_variable_length = true, .is_filter = true, }; diff --git a/block/progress_meter.c b/block/progress_meter.c index aa2e60248c..31a170a2cd 100644 --- a/block/progress_meter.c +++ b/block/progress_meter.c @@ -23,7 +23,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + #include "qemu/osdep.h" +#include "qemu/coroutine.h" #include "qemu/progress_meter.h" void progress_init(ProgressMeter *pm) diff --git a/block/qapi-sysemu.c b/block/qapi-sysemu.c index 680c7ee342..e4282631d2 100644 --- a/block/qapi-sysemu.c +++ b/block/qapi-sysemu.c @@ -32,6 +32,7 @@ #include "qemu/osdep.h" +#include "block/block_int.h" #include "qapi/error.h" #include "qapi/qapi-commands-block.h" #include "qapi/qmp/qdict.h" @@ -116,8 +117,8 @@ static int do_open_tray(const char *blk_name, const char *qdev_id, return 0; } -void qmp_blockdev_open_tray(bool has_device, const char *device, - bool has_id, const char *id, +void qmp_blockdev_open_tray(const char *device, + const char *id, bool has_force, bool force, Error **errp) { @@ -127,9 +128,7 @@ void qmp_blockdev_open_tray(bool has_device, const char *device, if (!has_force) { force = false; } - rc = do_open_tray(has_device ? device : NULL, - has_id ? id : NULL, - force, &local_err); + rc = do_open_tray(device, id, force, &local_err); if (rc && rc != -ENOSYS && rc != -EINPROGRESS) { error_propagate(errp, local_err); return; @@ -137,16 +136,13 @@ void qmp_blockdev_open_tray(bool has_device, const char *device, error_free(local_err); } -void qmp_blockdev_close_tray(bool has_device, const char *device, - bool has_id, const char *id, +void qmp_blockdev_close_tray(const char *device, + 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; @@ -173,16 +169,14 @@ void qmp_blockdev_close_tray(bool has_device, const char *device, } } -static void blockdev_remove_medium(bool has_device, const char *device, - bool has_id, const char *id, Error **errp) +static void GRAPH_UNLOCKED +blockdev_remove_medium(const char *device, 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; + GLOBAL_STATE_CODE(); blk = qmp_get_blk(device, id, errp); if (!blk) { @@ -209,12 +203,12 @@ static void blockdev_remove_medium(bool has_device, const char *device, return; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - + bdrv_graph_rdlock_main_loop(); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) { - goto out; + bdrv_graph_rdunlock_main_loop(); + return; } + bdrv_graph_rdunlock_main_loop(); blk_remove_bs(blk); @@ -225,14 +219,11 @@ static void blockdev_remove_medium(bool has_device, const char *device, * 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); + blockdev_remove_medium(NULL, id, errp); } static void qmp_blockdev_insert_anon_medium(BlockBackend *blk, @@ -280,16 +271,15 @@ static void qmp_blockdev_insert_anon_medium(BlockBackend *blk, } } -static void blockdev_insert_medium(bool has_device, const char *device, - bool has_id, const char *id, +static void blockdev_insert_medium(const char *device, 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); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + blk = qmp_get_blk(device, id, errp); if (!blk) { return; } @@ -311,13 +301,13 @@ static void blockdev_insert_medium(bool has_device, const char *device, void qmp_blockdev_insert_medium(const char *id, const char *node_name, Error **errp) { - blockdev_insert_medium(false, NULL, true, id, node_name, errp); + blockdev_insert_medium(NULL, id, node_name, errp); } -void qmp_blockdev_change_medium(bool has_device, const char *device, - bool has_id, const char *id, +void qmp_blockdev_change_medium(const char *device, + const char *id, const char *filename, - bool has_format, const char *format, + const char *format, bool has_force, bool force, bool has_read_only, BlockdevChangeReadOnlyMode read_only, @@ -331,9 +321,7 @@ void qmp_blockdev_change_medium(bool has_device, const char *device, QDict *options = NULL; Error *err = NULL; - blk = qmp_get_blk(has_device ? device : NULL, - has_id ? id : NULL, - errp); + blk = qmp_get_blk(device, id, errp); if (!blk) { goto fail; } @@ -370,18 +358,17 @@ void qmp_blockdev_change_medium(bool has_device, const char *device, detect_zeroes = blk_get_detect_zeroes_from_root_state(blk); qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off"); - if (has_format) { + if (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, - force, &err); + rc = do_open_tray(device, id, force, &err); if (rc && rc != -ENOSYS) { error_propagate(errp, err); goto fail; @@ -389,7 +376,7 @@ void qmp_blockdev_change_medium(bool has_device, const char *device, error_free(err); err = NULL; - blockdev_remove_medium(has_device, device, has_id, id, &err); + blockdev_remove_medium(device, id, &err); if (err) { error_propagate(errp, err); goto fail; @@ -401,7 +388,7 @@ void qmp_blockdev_change_medium(bool has_device, const char *device, goto fail; } - qmp_blockdev_close_tray(has_device, device, has_id, id, errp); + qmp_blockdev_close_tray(device, id, errp); fail: /* If the medium has been inserted, the device has its own reference, so @@ -410,8 +397,7 @@ fail: bdrv_unref(medium_bs); } -void qmp_eject(bool has_device, const char *device, - bool has_id, const char *id, +void qmp_eject(const char *device, const char *id, bool has_force, bool force, Error **errp) { Error *local_err = NULL; @@ -421,16 +407,14 @@ void qmp_eject(bool has_device, const char *device, force = false; } - rc = do_open_tray(has_device ? device : NULL, - has_id ? id : NULL, - force, &local_err); + rc = do_open_tray(device, id, 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); + blockdev_remove_medium(device, id, errp); } /* throttling disk I/O limits */ @@ -439,22 +423,16 @@ 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); + blk = qmp_get_blk(arg->device, arg->id, 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; + return; } throttle_config_init(&cfg); @@ -509,18 +487,15 @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp) } if (!throttle_is_valid(&cfg, errp)) { - goto out; + return; } 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_enable(blk, arg->group ?: arg->device ?: arg->id); + } else if (arg->group) { blk_io_limits_update_group(blk, arg->group); } /* Set the new throttling configuration */ @@ -529,9 +504,6 @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp) /* 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( @@ -539,6 +511,7 @@ void qmp_block_latency_histogram_set( bool has_boundaries, uint64List *boundaries, bool has_boundaries_read, uint64List *boundaries_read, bool has_boundaries_write, uint64List *boundaries_write, + bool has_boundaries_append, uint64List *boundaries_append, bool has_boundaries_flush, uint64List *boundaries_flush, Error **errp) { @@ -579,6 +552,16 @@ void qmp_block_latency_histogram_set( } } + if (has_boundaries || has_boundaries_append) { + ret = block_latency_histogram_set( + stats, BLOCK_ACCT_ZONE_APPEND, + has_boundaries_append ? boundaries_append : boundaries); + if (ret) { + error_setg(errp, "Device '%s' set append write boundaries fail", id); + return; + } + } + if (has_boundaries || has_boundaries_flush) { ret = block_latency_histogram_set( stats, BLOCK_ACCT_FLUSH, diff --git a/block/qapi.c b/block/qapi.c index cf557e3aea..2b5793f1d9 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -26,6 +26,7 @@ #include "qemu/cutils.h" #include "block/qapi.h" #include "block/block_int.h" +#include "block/dirty-bitmap.h" #include "block/throttle-groups.h" #include "block/write-threshold.h" #include "qapi/error.h" @@ -39,15 +40,16 @@ #include "qapi/qmp/qstring.h" #include "qemu/qemu-print.h" #include "sysemu/block-backend.h" -#include "qemu/cutils.h" BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, BlockDriverState *bs, bool flat, Error **errp) { + ERRP_GUARD(); ImageInfo **p_image_info; - BlockDriverState *bs0, *backing; + ImageInfo *backing_info; + BlockDriverState *backing; BlockDeviceInfo *info; if (!bs->drv) { @@ -71,13 +73,11 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, }; if (bs->node_name[0]) { - info->has_node_name = true; info->node_name = g_strdup(bs->node_name); } backing = bdrv_cow_bs(bs); if (backing) { - info->has_backing_file = true; info->backing_file = g_strdup(backing->filename); } @@ -139,48 +139,29 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, info->has_iops_size = cfg.op_size; info->iops_size = cfg.op_size; - info->has_group = true; info->group = g_strdup(throttle_group_get_name(&blkp->throttle_group_member)); } info->write_threshold = bdrv_write_threshold_get(bs); - bs0 = bs; p_image_info = &info->image; info->backing_file_depth = 0; - while (1) { - Error *local_err = NULL; - bdrv_query_image_info(bs0, p_image_info, &local_err); - if (local_err) { - error_propagate(errp, local_err); - qapi_free_BlockDeviceInfo(info); - return NULL; - } - /* stop gathering data for flat output */ - if (flat) { - break; - } + /* + * Skip automatically inserted nodes that the user isn't aware of for + * query-block (blk != NULL), but not for query-named-block-nodes + */ + bdrv_query_image_info(bs, p_image_info, flat, blk != NULL, errp); + if (*errp) { + qapi_free_BlockDeviceInfo(info); + return NULL; + } - if (bs0->drv && bdrv_filter_or_cow_child(bs0)) { - /* - * Put any filtered child here (for backwards compatibility to when - * we put bs0->backing here, which might be any filtered child). - */ - info->backing_file_depth++; - bs0 = bdrv_filter_or_cow_bs(bs0); - (*p_image_info)->has_backing_image = true; - p_image_info = &((*p_image_info)->backing_image); - } else { - break; - } - - /* Skip automatically inserted nodes that the user isn't aware of for - * query-block (blk != NULL), but not for query-named-block-nodes */ - if (blk) { - bs0 = bdrv_skip_implicit_filters(bs0); - } + backing_info = info->image->backing_image; + while (backing_info) { + info->backing_file_depth++; + backing_info = backing_info->backing_image; } return info; @@ -241,43 +222,27 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs, } /** - * bdrv_query_image_info: - * @bs: block device to examine - * @p_info: location to store image information - * @errp: location to store error information - * - * Store "flat" image information in @p_info. - * - * "Flat" means it does *not* query backing image information, - * i.e. (*pinfo)->has_backing_image will be set to false and - * (*pinfo)->backing_image to NULL even when the image does in fact have - * a backing image. - * - * @p_info will be set only on success. On error, store error in @errp. + * Helper function for other query info functions. Store information about @bs + * in @info, setting @errp on error. */ -void bdrv_query_image_info(BlockDriverState *bs, - ImageInfo **p_info, - Error **errp) +static void GRAPH_RDLOCK +bdrv_do_query_node_info(BlockDriverState *bs, BlockNodeInfo *info, Error **errp) { int64_t size; const char *backing_filename; BlockDriverInfo bdi; int ret; Error *err = NULL; - ImageInfo *info; - - aio_context_acquire(bdrv_get_aio_context(bs)); size = bdrv_getlength(bs); if (size < 0) { error_setg_errno(errp, -size, "Can't get image size '%s'", bs->exact_filename); - goto out; + return; } bdrv_refresh_filename(bs); - info = g_new0(ImageInfo, 1); info->filename = g_strdup(bs->filename); info->format = g_strdup(bdrv_get_format_name(bs)); info->virtual_size = size; @@ -298,29 +263,23 @@ void bdrv_query_image_info(BlockDriverState *bs, info->format_specific = bdrv_get_specific_info(bs, &err); if (err) { error_propagate(errp, err); - qapi_free_ImageInfo(info); - goto out; + return; } - info->has_format_specific = info->format_specific != NULL; - backing_filename = bs->backing_file; if (backing_filename[0] != '\0') { char *backing_filename2; info->backing_filename = g_strdup(backing_filename); - info->has_backing_filename = true; backing_filename2 = bdrv_get_full_backing_filename(bs, NULL); /* Always report the full_backing_filename if present, even if it's the * same as backing_filename. That they are same is useful info. */ if (backing_filename2) { info->full_backing_filename = g_strdup(backing_filename2); - info->has_full_backing_filename = true; } if (bs->backing_format[0]) { info->backing_filename_format = g_strdup(bs->backing_format); - info->has_backing_filename_format = true; } g_free(backing_filename2); } @@ -339,19 +298,127 @@ void bdrv_query_image_info(BlockDriverState *bs, break; default: error_propagate(errp, err); - qapi_free_ImageInfo(info); - goto out; + return; + } +} + +/** + * bdrv_query_image_info: + * @bs: block node to examine + * @p_info: location to store image information + * @flat: skip backing node information + * @skip_implicit_filters: skip implicit filters in the backing chain + * @errp: location to store error information + * + * Store image information in @p_info, potentially recursively covering the + * backing chain. + * + * If @flat is true, do not query backing image information, i.e. + * (*p_info)->has_backing_image will be set to false and + * (*p_info)->backing_image to NULL even when the image does in fact have a + * backing image. + * + * If @skip_implicit_filters is true, implicit filter nodes in the backing chain + * will be skipped when querying backing image information. + * (@skip_implicit_filters is ignored when @flat is true.) + * + * @p_info will be set only on success. On error, store error in @errp. + */ +void bdrv_query_image_info(BlockDriverState *bs, + ImageInfo **p_info, + bool flat, + bool skip_implicit_filters, + Error **errp) +{ + ERRP_GUARD(); + ImageInfo *info; + + info = g_new0(ImageInfo, 1); + bdrv_do_query_node_info(bs, qapi_ImageInfo_base(info), errp); + if (*errp) { + goto fail; + } + + if (!flat) { + BlockDriverState *backing; + + /* + * Use any filtered child here (for backwards compatibility to when + * we always took bs->backing, which might be any filtered child). + */ + backing = bdrv_filter_or_cow_bs(bs); + if (skip_implicit_filters) { + backing = bdrv_skip_implicit_filters(backing); + } + + if (backing) { + bdrv_query_image_info(backing, &info->backing_image, false, + skip_implicit_filters, errp); + if (*errp) { + goto fail; + } + } } *p_info = info; + return; -out: - aio_context_release(bdrv_get_aio_context(bs)); +fail: + assert(*errp); + qapi_free_ImageInfo(info); +} + +/** + * bdrv_query_block_graph_info: + * @bs: root node to start from + * @p_info: location to store image information + * @errp: location to store error information + * + * Store image information about the graph starting from @bs in @p_info. + * + * @p_info will be set only on success. On error, store error in @errp. + */ +void bdrv_query_block_graph_info(BlockDriverState *bs, + BlockGraphInfo **p_info, + Error **errp) +{ + ERRP_GUARD(); + BlockGraphInfo *info; + BlockChildInfoList **children_list_tail; + BdrvChild *c; + + info = g_new0(BlockGraphInfo, 1); + bdrv_do_query_node_info(bs, qapi_BlockGraphInfo_base(info), errp); + if (*errp) { + goto fail; + } + + children_list_tail = &info->children; + + QLIST_FOREACH(c, &bs->children, next) { + BlockChildInfo *c_info; + + c_info = g_new0(BlockChildInfo, 1); + QAPI_LIST_APPEND(children_list_tail, c_info); + + c_info->name = g_strdup(c->name); + bdrv_query_block_graph_info(c->bs, &c_info->info, errp); + if (*errp) { + goto fail; + } + } + + *p_info = info; + return; + +fail: + assert(*errp != NULL); + qapi_free_BlockGraphInfo(info); } /* @p_info will be set only on success. */ -static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, - Error **errp) +static void GRAPH_RDLOCK +bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, Error **errp) { BlockInfo *info = g_malloc0(sizeof(*info)); BlockDriverState *bs = blk_bs(blk); @@ -367,7 +434,6 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, qdev = blk_get_attached_dev_id(blk); if (qdev && *qdev) { - info->has_qdev = true; info->qdev = qdev; } else { g_free(qdev); @@ -384,7 +450,6 @@ 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, false, errp); if (info->inserted == NULL) { goto err; @@ -411,47 +476,59 @@ static uint64List *uint64_list(uint64_t *list, int size) return out_list; } -static void bdrv_latency_histogram_stats(BlockLatencyHistogram *hist, - bool *not_null, - BlockLatencyHistogramInfo **info) +static BlockLatencyHistogramInfo * +bdrv_latency_histogram_stats(BlockLatencyHistogram *hist) { - *not_null = hist->bins != NULL; - if (*not_null) { - *info = g_new0(BlockLatencyHistogramInfo, 1); + BlockLatencyHistogramInfo *info; - (*info)->boundaries = uint64_list(hist->boundaries, hist->nbins - 1); - (*info)->bins = uint64_list(hist->bins, hist->nbins); + if (!hist->bins) { + return NULL; } + + info = g_new0(BlockLatencyHistogramInfo, 1); + info->boundaries = uint64_list(hist->boundaries, hist->nbins - 1); + info->bins = uint64_list(hist->bins, hist->nbins); + return info; } static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) { BlockAcctStats *stats = blk_get_stats(blk); BlockAcctTimedStats *ts = NULL; + BlockLatencyHistogram *hgram; ds->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ]; ds->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE]; + ds->zone_append_bytes = stats->nr_bytes[BLOCK_ACCT_ZONE_APPEND]; ds->unmap_bytes = stats->nr_bytes[BLOCK_ACCT_UNMAP]; ds->rd_operations = stats->nr_ops[BLOCK_ACCT_READ]; ds->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE]; + ds->zone_append_operations = stats->nr_ops[BLOCK_ACCT_ZONE_APPEND]; ds->unmap_operations = stats->nr_ops[BLOCK_ACCT_UNMAP]; ds->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ]; ds->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE]; + ds->failed_zone_append_operations = + stats->failed_ops[BLOCK_ACCT_ZONE_APPEND]; ds->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH]; ds->failed_unmap_operations = stats->failed_ops[BLOCK_ACCT_UNMAP]; ds->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ]; ds->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE]; + ds->invalid_zone_append_operations = + stats->invalid_ops[BLOCK_ACCT_ZONE_APPEND]; ds->invalid_flush_operations = stats->invalid_ops[BLOCK_ACCT_FLUSH]; ds->invalid_unmap_operations = stats->invalid_ops[BLOCK_ACCT_UNMAP]; ds->rd_merged = stats->merged[BLOCK_ACCT_READ]; ds->wr_merged = stats->merged[BLOCK_ACCT_WRITE]; + ds->zone_append_merged = stats->merged[BLOCK_ACCT_ZONE_APPEND]; ds->unmap_merged = stats->merged[BLOCK_ACCT_UNMAP]; ds->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH]; ds->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE]; + ds->zone_append_total_time_ns = + stats->total_time_ns[BLOCK_ACCT_ZONE_APPEND]; ds->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ]; ds->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH]; ds->unmap_total_time_ns = stats->total_time_ns[BLOCK_ACCT_UNMAP]; @@ -469,6 +546,7 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) TimedAverage *rd = &ts->latency[BLOCK_ACCT_READ]; TimedAverage *wr = &ts->latency[BLOCK_ACCT_WRITE]; + TimedAverage *zap = &ts->latency[BLOCK_ACCT_ZONE_APPEND]; TimedAverage *fl = &ts->latency[BLOCK_ACCT_FLUSH]; dev_stats->interval_length = ts->interval_length; @@ -481,6 +559,10 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) dev_stats->max_wr_latency_ns = timed_average_max(wr); dev_stats->avg_wr_latency_ns = timed_average_avg(wr); + dev_stats->min_zone_append_latency_ns = timed_average_min(zap); + dev_stats->max_zone_append_latency_ns = timed_average_max(zap); + dev_stats->avg_zone_append_latency_ns = timed_average_avg(zap); + dev_stats->min_flush_latency_ns = timed_average_min(fl); dev_stats->max_flush_latency_ns = timed_average_max(fl); dev_stats->avg_flush_latency_ns = timed_average_avg(fl); @@ -489,23 +571,25 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) block_acct_queue_depth(ts, BLOCK_ACCT_READ); dev_stats->avg_wr_queue_depth = block_acct_queue_depth(ts, BLOCK_ACCT_WRITE); + dev_stats->avg_zone_append_queue_depth = + block_acct_queue_depth(ts, BLOCK_ACCT_ZONE_APPEND); QAPI_LIST_PREPEND(ds->timed_stats, dev_stats); } - bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ], - &ds->has_rd_latency_histogram, - &ds->rd_latency_histogram); - bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRITE], - &ds->has_wr_latency_histogram, - &ds->wr_latency_histogram); - bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUSH], - &ds->has_flush_latency_histogram, - &ds->flush_latency_histogram); + hgram = stats->latency_histogram; + ds->rd_latency_histogram + = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_READ]); + ds->wr_latency_histogram + = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_WRITE]); + ds->zone_append_latency_histogram + = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_ZONE_APPEND]); + ds->flush_latency_histogram + = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_FLUSH]); } -static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs, - bool blk_level) +static BlockStats * GRAPH_RDLOCK +bdrv_query_bds_stats(BlockDriverState *bs, bool blk_level) { BdrvChild *parent_child; BlockDriverState *filter_or_cow_bs; @@ -526,16 +610,12 @@ static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs, } if (bdrv_get_node_name(bs)[0]) { - s->has_node_name = true; s->node_name = g_strdup(bdrv_get_node_name(bs)); } s->stats->wr_highest_offset = stat64_get(&bs->wr_highest_offset); s->driver_specific = bdrv_get_specific_stats(bs); - if (s->driver_specific) { - s->has_driver_specific = true; - } parent_child = bdrv_primary_child(bs); if (!parent_child || @@ -564,7 +644,6 @@ static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs, } } if (parent_child) { - s->has_parent = true; s->parent = bdrv_query_bds_stats(parent_child->bs, blk_level); } @@ -575,7 +654,6 @@ static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs, * compatibility to when we put bs0->backing here, which might * be either) */ - s->has_backing = true; s->backing = bdrv_query_bds_stats(filter_or_cow_bs, blk_level); } @@ -588,6 +666,8 @@ BlockInfoList *qmp_query_block(Error **errp) BlockBackend *blk; Error *local_err = NULL; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { BlockInfoList *info; @@ -619,18 +699,15 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, BlockBackend *blk; BlockDriverState *bs; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + /* Just to be safe if query_nodes is not always initialized */ if (has_query_nodes && query_nodes) { for (bs = bdrv_next_node(NULL); bs; bs = bdrv_next_node(bs)) { - AioContext *ctx = bdrv_get_aio_context(bs); - - aio_context_acquire(ctx); QAPI_LIST_APPEND(tail, bdrv_query_bds_stats(bs, false)); - aio_context_release(ctx); } } else { for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { - AioContext *ctx = blk_get_aio_context(blk); BlockStats *s; char *qdev; @@ -638,21 +715,17 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, continue; } - aio_context_acquire(ctx); s = bdrv_query_bds_stats(blk_bs(blk), true); - s->has_device = true; s->device = g_strdup(blk_name(blk)); qdev = blk_get_attached_dev_id(blk); if (qdev && *qdev) { - s->has_qdev = true; s->qdev = qdev; } else { g_free(qdev); } bdrv_query_blk_stats(s->stats, blk); - aio_context_release(ctx); QAPI_LIST_APPEND(tail, s); } @@ -669,15 +742,15 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn) char *sizing = NULL; if (!sn) { - qemu_printf("%-10s%-17s%8s%20s%13s%11s", - "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK", "ICOUNT"); + qemu_printf("%-7s %-16s %8s %19s %15s %10s", + "ID", "TAG", "VM_SIZE", "DATE", "VM_CLOCK", "ICOUNT"); } else { g_autoptr(GDateTime) date = g_date_time_new_from_unix_local(sn->date_sec); g_autofree char *date_buf = g_date_time_format(date, "%Y-%m-%d %H:%M:%S"); secs = sn->vm_clock_nsec / 1000000000; snprintf(clock_buf, sizeof(clock_buf), - "%02d:%02d:%02d.%03d", + "%04d:%02d:%02d.%03d", (int)(secs / 3600), (int)((secs / 60) % 60), (int)(secs % 60), @@ -686,8 +759,10 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn) if (sn->icount != -1ULL) { snprintf(icount_buf, sizeof(icount_buf), "%"PRId64, sn->icount); + } else { + snprintf(icount_buf, sizeof(icount_buf), "--"); } - qemu_printf("%-9s %-16s %8s%20s%13s%11s", + qemu_printf("%-7s %-16s %8s %19s %15s %10s", sn->id_str, sn->name, sizing, date_buf, @@ -777,7 +852,36 @@ static void dump_qdict(int indentation, QDict *dict) } } -void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec) +/* + * Return whether dumping the given QObject with dump_qobject() would + * yield an empty dump, i.e. not print anything. + */ +static bool qobject_is_empty_dump(const QObject *obj) +{ + switch (qobject_type(obj)) { + case QTYPE_QNUM: + case QTYPE_QSTRING: + case QTYPE_QBOOL: + return false; + + case QTYPE_QDICT: + return qdict_size(qobject_to(QDict, obj)) == 0; + + case QTYPE_QLIST: + return qlist_empty(qobject_to(QList, obj)); + + default: + abort(); + } +} + +/** + * Dumps the given ImageInfoSpecific object in a human-readable form, + * prepending an optional prefix if the dump is not empty. + */ +void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec, + const char *prefix, + int indentation) { QObject *obj, *data; Visitor *v = qobject_output_visitor_new(&obj); @@ -785,62 +889,96 @@ void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec) visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort); visit_complete(v, &obj); data = qdict_get(qobject_to(QDict, obj), "data"); - dump_qobject(1, data); + if (!qobject_is_empty_dump(data)) { + if (prefix) { + qemu_printf("%*s%s", indentation * 4, "", prefix); + } + dump_qobject(indentation + 1, data); + } qobject_unref(obj); visit_free(v); } -void bdrv_image_info_dump(ImageInfo *info) +/** + * Print the given @info object in human-readable form. Every field is indented + * using the given @indentation (four spaces per indentation level). + * + * When using this to print a whole block graph, @protocol can be set to true to + * signify that the given information is associated with a protocol node, i.e. + * just data storage for an image, such that the data it presents is not really + * a full VM disk. If so, several fields change name: For example, "virtual + * size" is printed as "file length". + * (Consider a qcow2 image, which is represented by a qcow2 node and a file + * node. Printing a "virtual size" for the file node does not make sense, + * because without the qcow2 node, it is not really a guest disk, so it does not + * have a "virtual size". Therefore, we call it "file length" instead.) + * + * @protocol is ignored when @indentation is 0, because we take that to mean + * that the associated node is the root node in the queried block graph, and + * thus is always to be interpreted as a standalone guest disk. + */ +void bdrv_node_info_dump(BlockNodeInfo *info, int indentation, bool protocol) { char *size_buf, *dsize_buf; + g_autofree char *ind_s = g_strdup_printf("%*s", indentation * 4, ""); + + if (indentation == 0) { + /* Top level, consider this a normal image */ + protocol = false; + } + if (!info->has_actual_size) { dsize_buf = g_strdup("unavailable"); } else { dsize_buf = size_to_str(info->actual_size); } size_buf = size_to_str(info->virtual_size); - qemu_printf("image: %s\n" - "file format: %s\n" - "virtual size: %s (%" PRId64 " bytes)\n" - "disk size: %s\n", - info->filename, info->format, size_buf, - info->virtual_size, - dsize_buf); + qemu_printf("%s%s: %s\n" + "%s%s: %s\n" + "%s%s: %s (%" PRId64 " bytes)\n" + "%sdisk size: %s\n", + ind_s, protocol ? "filename" : "image", info->filename, + ind_s, protocol ? "protocol type" : "file format", + info->format, + ind_s, protocol ? "file length" : "virtual size", + size_buf, info->virtual_size, + ind_s, dsize_buf); g_free(size_buf); g_free(dsize_buf); if (info->has_encrypted && info->encrypted) { - qemu_printf("encrypted: yes\n"); + qemu_printf("%sencrypted: yes\n", ind_s); } if (info->has_cluster_size) { - qemu_printf("cluster_size: %" PRId64 "\n", - info->cluster_size); + qemu_printf("%scluster_size: %" PRId64 "\n", + ind_s, info->cluster_size); } if (info->has_dirty_flag && info->dirty_flag) { - qemu_printf("cleanly shut down: no\n"); + qemu_printf("%scleanly shut down: no\n", ind_s); } - if (info->has_backing_filename) { - qemu_printf("backing file: %s", info->backing_filename); - if (!info->has_full_backing_filename) { + if (info->backing_filename) { + qemu_printf("%sbacking file: %s", ind_s, info->backing_filename); + if (!info->full_backing_filename) { qemu_printf(" (cannot determine actual path)"); } else if (strcmp(info->backing_filename, info->full_backing_filename) != 0) { qemu_printf(" (actual path: %s)", info->full_backing_filename); } qemu_printf("\n"); - if (info->has_backing_filename_format) { - qemu_printf("backing file format: %s\n", - info->backing_filename_format); + if (info->backing_filename_format) { + qemu_printf("%sbacking file format: %s\n", + ind_s, info->backing_filename_format); } } if (info->has_snapshots) { SnapshotInfoList *elem; - qemu_printf("Snapshot list:\n"); + qemu_printf("%sSnapshot list:\n", ind_s); + qemu_printf("%s", ind_s); bdrv_snapshot_dump(NULL); qemu_printf("\n"); @@ -860,13 +998,15 @@ void bdrv_image_info_dump(ImageInfo *info) pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id); pstrcpy(sn.name, sizeof(sn.name), elem->value->name); + qemu_printf("%s", ind_s); bdrv_snapshot_dump(&sn); qemu_printf("\n"); } } - if (info->has_format_specific) { - qemu_printf("Format specific information:\n"); - bdrv_image_info_specific_dump(info->format_specific); + if (info->format_specific) { + bdrv_image_info_specific_dump(info->format_specific, + "Format specific information:\n", + indentation); } } diff --git a/block/qcow.c b/block/qcow.c index 4fba1b9e36..ca8e1d5ec8 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -92,7 +92,8 @@ typedef struct BDRVQcowState { static QemuOptsList qcow_create_opts; -static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); +static int coroutine_fn GRAPH_RDLOCK +decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) { @@ -121,14 +122,14 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, qdict_extract_subqdict(options, &encryptopts, "encrypt."); encryptfmt = qdict_get_try_str(encryptopts, "format"); - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_IMAGE, false, errp); - if (!bs->file) { - ret = -EINVAL; - goto fail; + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + goto fail_unlocked; } - ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); + bdrv_graph_rdlock_main_loop(); + + ret = bdrv_pread(bs->file, 0, sizeof(header), &header, 0); if (ret < 0) { goto fail; } @@ -260,8 +261,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, - s->l1_size * sizeof(uint64_t)); + ret = bdrv_pread(bs->file, s->l1_table_offset, + s->l1_size * sizeof(uint64_t), s->l1_table, 0); if (ret < 0) { goto fail; } @@ -291,8 +292,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, ret = -EINVAL; goto fail; } - ret = bdrv_pread(bs->file, header.backing_file_offset, - bs->auto_backing_file, len); + ret = bdrv_pread(bs->file, header.backing_file_offset, len, + bs->auto_backing_file, 0); if (ret < 0) { goto fail; } @@ -305,18 +306,21 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, error_setg(&s->migration_blocker, "The qcow format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - ret = migrate_add_blocker(s->migration_blocker, errp); + + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { - error_free(s->migration_blocker); goto fail; } qobject_unref(encryptopts); qapi_free_QCryptoBlockOpenOptions(crypto_opts); qemu_co_mutex_init(&s->lock); + bdrv_graph_rdunlock_main_loop(); return 0; - fail: +fail: + bdrv_graph_rdunlock_main_loop(); +fail_unlocked: g_free(s->l1_table); qemu_vfree(s->l2_cache); g_free(s->cluster_cache); @@ -351,10 +355,10 @@ static int qcow_reopen_prepare(BDRVReopenState *state, * return 0 if not allocated, 1 if *result is assigned, and negative * errno on failure. */ -static int get_cluster_offset(BlockDriverState *bs, - uint64_t offset, int allocate, - int compressed_size, - int n_start, int n_end, uint64_t *result) +static int coroutine_fn GRAPH_RDLOCK +get_cluster_offset(BlockDriverState *bs, uint64_t offset, int allocate, + int compressed_size, int n_start, int n_end, + uint64_t *result) { BDRVQcowState *s = bs->opaque; int min_index, i, j, l1_index, l2_index, ret; @@ -371,7 +375,7 @@ static int get_cluster_offset(BlockDriverState *bs, if (!allocate) return 0; /* allocate a new l2 entry */ - l2_offset = bdrv_getlength(bs->file->bs); + l2_offset = bdrv_co_getlength(bs->file->bs); if (l2_offset < 0) { return l2_offset; } @@ -380,10 +384,10 @@ static int get_cluster_offset(BlockDriverState *bs, /* update the L1 entry */ s->l1_table[l1_index] = l2_offset; tmp = cpu_to_be64(l2_offset); - BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); - ret = bdrv_pwrite_sync(bs->file, - s->l1_table_offset + l1_index * sizeof(tmp), - &tmp, sizeof(tmp)); + BLKDBG_CO_EVENT(bs->file, BLKDBG_L1_UPDATE); + ret = bdrv_co_pwrite_sync(bs->file, + s->l1_table_offset + l1_index * sizeof(tmp), + sizeof(tmp), &tmp, 0); if (ret < 0) { return ret; } @@ -411,17 +415,17 @@ static int get_cluster_offset(BlockDriverState *bs, } } l2_table = s->l2_cache + (min_index << s->l2_bits); - BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); + BLKDBG_CO_EVENT(bs->file, BLKDBG_L2_LOAD); if (new_l2_table) { memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); - ret = bdrv_pwrite_sync(bs->file, l2_offset, l2_table, - s->l2_size * sizeof(uint64_t)); + ret = bdrv_co_pwrite_sync(bs->file, l2_offset, + s->l2_size * sizeof(uint64_t), l2_table, 0); if (ret < 0) { return ret; } } else { - ret = bdrv_pread(bs->file, l2_offset, l2_table, - s->l2_size * sizeof(uint64_t)); + ret = bdrv_co_pread(bs->file, l2_offset, + s->l2_size * sizeof(uint64_t), l2_table, 0); if (ret < 0) { return ret; } @@ -435,7 +439,7 @@ static int get_cluster_offset(BlockDriverState *bs, ((cluster_offset & QCOW_OFLAG_COMPRESSED) && allocate == 1)) { if (!allocate) return 0; - BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); + BLKDBG_CO_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); assert(QEMU_IS_ALIGNED(n_start | n_end, BDRV_SECTOR_SIZE)); /* allocate a new cluster */ if ((cluster_offset & QCOW_OFLAG_COMPRESSED) && @@ -446,20 +450,20 @@ static int get_cluster_offset(BlockDriverState *bs, if (decompress_cluster(bs, cluster_offset) < 0) { return -EIO; } - cluster_offset = bdrv_getlength(bs->file->bs); + cluster_offset = bdrv_co_getlength(bs->file->bs); if ((int64_t) cluster_offset < 0) { return cluster_offset; } cluster_offset = QEMU_ALIGN_UP(cluster_offset, s->cluster_size); /* write the cluster content */ - BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); - ret = bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, - s->cluster_size); + BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); + ret = bdrv_co_pwrite(bs->file, cluster_offset, s->cluster_size, + s->cluster_cache, 0); if (ret < 0) { return ret; } } else { - cluster_offset = bdrv_getlength(bs->file->bs); + cluster_offset = bdrv_co_getlength(bs->file->bs); if ((int64_t) cluster_offset < 0) { return cluster_offset; } @@ -469,8 +473,9 @@ static int get_cluster_offset(BlockDriverState *bs, if (cluster_offset + s->cluster_size > INT64_MAX) { return -E2BIG; } - ret = bdrv_truncate(bs->file, cluster_offset + s->cluster_size, - false, PREALLOC_MODE_OFF, 0, NULL); + ret = bdrv_co_truncate(bs->file, + cluster_offset + s->cluster_size, + false, PREALLOC_MODE_OFF, 0, NULL); if (ret < 0) { return ret; } @@ -491,11 +496,10 @@ static int get_cluster_offset(BlockDriverState *bs, NULL) < 0) { return -EIO; } - BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); - ret = bdrv_pwrite(bs->file, - cluster_offset + i, - s->cluster_data, - BDRV_SECTOR_SIZE); + BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); + ret = bdrv_co_pwrite(bs->file, cluster_offset + i, + BDRV_SECTOR_SIZE, + s->cluster_data, 0); if (ret < 0) { return ret; } @@ -511,12 +515,12 @@ static int get_cluster_offset(BlockDriverState *bs, tmp = cpu_to_be64(cluster_offset); l2_table[l2_index] = tmp; if (allocate == 2) { - BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); + BLKDBG_CO_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); } else { - BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); + BLKDBG_CO_EVENT(bs->file, BLKDBG_L2_UPDATE); } - ret = bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp), - &tmp, sizeof(tmp)); + ret = bdrv_co_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp), + sizeof(tmp), &tmp, 0); if (ret < 0) { return ret; } @@ -525,11 +529,10 @@ static int get_cluster_offset(BlockDriverState *bs, return 1; } -static int coroutine_fn qcow_co_block_status(BlockDriverState *bs, - bool want_zero, - int64_t offset, int64_t bytes, - int64_t *pnum, int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +qcow_co_block_status(BlockDriverState *bs, bool want_zero, + int64_t offset, int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file) { BDRVQcowState *s = bs->opaque; int index_in_cluster, ret; @@ -551,7 +554,10 @@ static int coroutine_fn qcow_co_block_status(BlockDriverState *bs, if (!cluster_offset) { return 0; } - if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypto) { + if (cluster_offset & QCOW_OFLAG_COMPRESSED) { + return BDRV_BLOCK_DATA | BDRV_BLOCK_COMPRESSED; + } + if (s->crypto) { return BDRV_BLOCK_DATA; } *map = cluster_offset | index_in_cluster; @@ -586,7 +592,8 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size, return 0; } -static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) +static int coroutine_fn GRAPH_RDLOCK +decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) { BDRVQcowState *s = bs->opaque; int ret, csize; @@ -596,9 +603,9 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) if (s->cluster_cache_offset != coffset) { csize = cluster_offset >> (63 - s->cluster_bits); csize &= (s->cluster_size - 1); - BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); - ret = bdrv_pread(bs->file, coffset, s->cluster_data, csize); - if (ret != csize) + BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_COMPRESSED); + ret = bdrv_co_pread(bs->file, coffset, csize, s->cluster_data, 0); + if (ret < 0) return -1; if (decompress_buffer(s->cluster_cache, s->cluster_size, s->cluster_data, csize) < 0) { @@ -618,9 +625,9 @@ static void qcow_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.request_alignment = BDRV_SECTOR_SIZE; } -static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +qcow_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVQcowState *s = bs->opaque; int offset_in_cluster; @@ -629,7 +636,6 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, int64_t offset, uint8_t *buf; void *orig_buf; - assert(!flags); if (qiov->niov > 1) { buf = orig_buf = qemu_try_blockalign(bs, qiov->size); if (buf == NULL) { @@ -659,7 +665,7 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, int64_t offset, /* read from the base image */ qemu_co_mutex_unlock(&s->lock); /* qcow2 emits this on bs->file instead of bs->backing */ - BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); + BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); ret = bdrv_co_pread(bs->backing, offset, n, buf, 0); qemu_co_mutex_lock(&s->lock); if (ret < 0) { @@ -682,7 +688,7 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, int64_t offset, break; } qemu_co_mutex_unlock(&s->lock); - BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); + BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_AIO); ret = bdrv_co_pread(bs->file, cluster_offset + offset_in_cluster, n, buf, 0); qemu_co_mutex_lock(&s->lock); @@ -715,9 +721,9 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, int64_t offset, return ret; } -static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +qcow_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVQcowState *s = bs->opaque; int offset_in_cluster; @@ -726,7 +732,6 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, int64_t offset, uint8_t *buf; void *orig_buf; - assert(!flags); s->cluster_cache_offset = -1; /* disable compressed cache */ /* We must always copy the iov when encrypting, so we @@ -768,7 +773,7 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, int64_t offset, } qemu_co_mutex_unlock(&s->lock); - BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); + BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); ret = bdrv_co_pwrite(bs->file, cluster_offset + offset_in_cluster, n, buf, 0); qemu_co_mutex_lock(&s->lock); @@ -799,12 +804,11 @@ static void qcow_close(BlockDriverState *bs) g_free(s->cluster_cache); g_free(s->cluster_data); - migrate_del_blocker(s->migration_blocker); - error_free(s->migration_blocker); + migrate_del_blocker(&s->migration_blocker); } -static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +qcow_co_create(BlockdevCreateOptions *opts, Error **errp) { BlockdevCreateOptionsQcow *qcow_opts; int header_size, backing_filename_len, l1_size, shift, i; @@ -826,7 +830,7 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, return -EINVAL; } - if (qcow_opts->has_encrypt && + if (qcow_opts->encrypt && qcow_opts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_QCOW) { error_setg(errp, "Unsupported encryption format"); @@ -834,13 +838,13 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, } /* Create BlockBackend to write to the image */ - bs = bdrv_open_blockdev_ref(qcow_opts->file, errp); + bs = bdrv_co_open_blockdev_ref(qcow_opts->file, errp); if (bs == NULL) { return -EIO; } - qcow_blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, - BLK_PERM_ALL, errp); + qcow_blk = blk_co_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, + BLK_PERM_ALL, errp); if (!qcow_blk) { ret = -EPERM; goto exit; @@ -854,7 +858,7 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, header.size = cpu_to_be64(total_size); header_size = sizeof(header); backing_filename_len = 0; - if (qcow_opts->has_backing_file) { + if (qcow_opts->backing_file) { if (strcmp(qcow_opts->backing_file, "fat:")) { header.backing_file_offset = cpu_to_be64(header_size); backing_filename_len = strlen(qcow_opts->backing_file); @@ -862,7 +866,7 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, header_size += backing_filename_len; } else { /* special backing file for vvfat */ - qcow_opts->has_backing_file = false; + qcow_opts->backing_file = NULL; } header.cluster_bits = 9; /* 512 byte cluster to avoid copying unmodified sectors */ @@ -877,11 +881,11 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, header.l1_table_offset = cpu_to_be64(header_size); - if (qcow_opts->has_encrypt) { + if (qcow_opts->encrypt) { header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES); crypto = qcrypto_block_create(qcow_opts->encrypt, "encrypt.", - NULL, NULL, NULL, errp); + NULL, NULL, NULL, 0, errp); if (!crypto) { ret = -EINVAL; goto exit; @@ -891,15 +895,15 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, } /* write all the data */ - ret = blk_pwrite(qcow_blk, 0, &header, sizeof(header), 0); - if (ret != sizeof(header)) { + ret = blk_co_pwrite(qcow_blk, 0, sizeof(header), &header, 0); + if (ret < 0) { goto exit; } - if (qcow_opts->has_backing_file) { - ret = blk_pwrite(qcow_blk, sizeof(header), - qcow_opts->backing_file, backing_filename_len, 0); - if (ret != backing_filename_len) { + if (qcow_opts->backing_file) { + ret = blk_co_pwrite(qcow_blk, sizeof(header), backing_filename_len, + qcow_opts->backing_file, 0); + if (ret < 0) { goto exit; } } @@ -907,9 +911,9 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, tmp = g_malloc0(BDRV_SECTOR_SIZE); for (i = 0; i < DIV_ROUND_UP(sizeof(uint64_t) * l1_size, BDRV_SECTOR_SIZE); i++) { - ret = blk_pwrite(qcow_blk, header_size + BDRV_SECTOR_SIZE * i, - tmp, BDRV_SECTOR_SIZE, 0); - if (ret != BDRV_SECTOR_SIZE) { + ret = blk_co_pwrite(qcow_blk, header_size + BDRV_SECTOR_SIZE * i, + BDRV_SECTOR_SIZE, tmp, 0); + if (ret < 0) { g_free(tmp); goto exit; } @@ -918,15 +922,15 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, g_free(tmp); ret = 0; exit: - blk_unref(qcow_blk); - bdrv_unref(bs); + blk_co_unref(qcow_blk); + bdrv_co_unref(bs); qcrypto_block_free(crypto); return ret; } -static int coroutine_fn qcow_co_create_opts(BlockDriver *drv, - const char *filename, - QemuOpts *opts, Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +qcow_co_create_opts(BlockDriver *drv, const char *filename, + QemuOpts *opts, Error **errp) { BlockdevCreateOptions *create_options = NULL; BlockDriverState *bs = NULL; @@ -974,13 +978,13 @@ static int coroutine_fn qcow_co_create_opts(BlockDriver *drv, } /* Create and open the file (protocol layer) */ - ret = bdrv_create_file(filename, opts, errp); + ret = bdrv_co_create_file(filename, opts, errp); if (ret < 0) { goto fail; } - bs = bdrv_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); + bs = bdrv_co_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); if (bs == NULL) { ret = -EIO; goto fail; @@ -1018,20 +1022,20 @@ static int coroutine_fn qcow_co_create_opts(BlockDriver *drv, fail: g_free(backing_fmt); qobject_unref(qdict); - bdrv_unref(bs); + bdrv_co_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; } -static int qcow_make_empty(BlockDriverState *bs) +static int GRAPH_RDLOCK qcow_make_empty(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; uint32_t l1_length = s->l1_size * sizeof(uint64_t); int ret; memset(s->l1_table, 0, l1_length); - if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table, - l1_length) < 0) + if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, l1_length, s->l1_table, + 0) < 0) return -1; ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length, false, PREALLOC_MODE_OFF, 0, NULL); @@ -1047,7 +1051,7 @@ static int qcow_make_empty(BlockDriverState *bs) /* XXX: put compressed sectors first, then all the cluster aligned tables to avoid losing bytes in alignment */ -static coroutine_fn int +static int coroutine_fn GRAPH_RDLOCK qcow_co_pwritev_compressed(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov) { @@ -1117,7 +1121,7 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, int64_t offset, int64_t bytes, } cluster_offset &= s->cluster_offset_mask; - BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED); + BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED); ret = bdrv_co_pwrite(bs->file, cluster_offset, out_len, out_buf, 0); if (ret < 0) { goto fail; @@ -1130,7 +1134,8 @@ fail: return ret; } -static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +qcow_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVQcowState *s = bs->opaque; bdi->cluster_size = s->cluster_size; @@ -1199,7 +1204,7 @@ static BlockDriver bdrv_qcow = { .bdrv_make_empty = qcow_make_empty, .bdrv_co_pwritev_compressed = qcow_co_pwritev_compressed, - .bdrv_get_info = qcow_get_info, + .bdrv_co_get_info = qcow_co_get_info, .create_opts = &qcow_create_opts, .strong_runtime_opts = qcow_strong_runtime_opts, diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 8fb4731551..874ea56948 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -26,6 +26,8 @@ */ #include "qemu/osdep.h" +#include "block/block-io.h" +#include "block/dirty-bitmap.h" #include "qapi/error.h" #include "qemu/cutils.h" @@ -103,7 +105,7 @@ static inline bool can_write(BlockDriverState *bs) return !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE); } -static int update_header_sync(BlockDriverState *bs) +static int GRAPH_RDLOCK update_header_sync(BlockDriverState *bs) { int ret; @@ -115,7 +117,7 @@ static int update_header_sync(BlockDriverState *bs) return bdrv_flush(bs->file->bs); } -static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size) +static inline void bitmap_table_bswap_be(uint64_t *bitmap_table, size_t size) { size_t i; @@ -154,10 +156,9 @@ static int64_t get_bitmap_bytes_needed(int64_t len, uint32_t granularity) return DIV_ROUND_UP(num_bits, 8); } -static int check_constraints_on_bitmap(BlockDriverState *bs, - const char *name, - uint32_t granularity, - Error **errp) +static int GRAPH_RDLOCK +check_constraints_on_bitmap(BlockDriverState *bs, const char *name, + uint32_t granularity, Error **errp) { BDRVQcow2State *s = bs->opaque; int granularity_bits = ctz32(granularity); @@ -202,8 +203,9 @@ static int check_constraints_on_bitmap(BlockDriverState *bs, return 0; } -static void clear_bitmap_table(BlockDriverState *bs, uint64_t *bitmap_table, - uint32_t bitmap_table_size) +static void GRAPH_RDLOCK +clear_bitmap_table(BlockDriverState *bs, uint64_t *bitmap_table, + uint32_t bitmap_table_size) { BDRVQcow2State *s = bs->opaque; int i; @@ -219,8 +221,9 @@ static void clear_bitmap_table(BlockDriverState *bs, uint64_t *bitmap_table, } } -static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb, - uint64_t **bitmap_table) +static int GRAPH_RDLOCK +bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb, + uint64_t **bitmap_table) { int ret; BDRVQcow2State *s = bs->opaque; @@ -234,8 +237,8 @@ static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb, } assert(tb->size <= BME_MAX_TABLE_SIZE); - ret = bdrv_pread(bs->file, tb->offset, - table, tb->size * BME_TABLE_ENTRY_SIZE); + ret = bdrv_pread(bs->file, tb->offset, tb->size * BME_TABLE_ENTRY_SIZE, + table, 0); if (ret < 0) { goto fail; } @@ -257,7 +260,8 @@ fail: return ret; } -static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) +static int GRAPH_RDLOCK +free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) { int ret; uint64_t *bitmap_table; @@ -281,10 +285,9 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) /* load_bitmap_data * @bitmap_table entries must satisfy specification constraints. * @bitmap must be cleared */ -static int load_bitmap_data(BlockDriverState *bs, - const uint64_t *bitmap_table, - uint32_t bitmap_table_size, - BdrvDirtyBitmap *bitmap) +static int coroutine_fn GRAPH_RDLOCK +load_bitmap_data(BlockDriverState *bs, const uint64_t *bitmap_table, + uint32_t bitmap_table_size, BdrvDirtyBitmap *bitmap) { int ret = 0; BDRVQcow2State *s = bs->opaque; @@ -317,7 +320,7 @@ static int load_bitmap_data(BlockDriverState *bs, * already cleared */ } } else { - ret = bdrv_pread(bs->file, data_offset, buf, s->cluster_size); + ret = bdrv_co_pread(bs->file, data_offset, s->cluster_size, buf, 0); if (ret < 0) { goto finish; } @@ -335,8 +338,9 @@ finish: return ret; } -static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs, - Qcow2Bitmap *bm, Error **errp) +static coroutine_fn GRAPH_RDLOCK +BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs, + Qcow2Bitmap *bm, Error **errp) { int ret; uint64_t *bitmap_table = NULL; @@ -548,8 +552,9 @@ static uint32_t bitmap_list_count(Qcow2BitmapList *bm_list) * Get bitmap list from qcow2 image. Actually reads bitmap directory, * checks it and convert to bitmap list. */ -static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset, - uint64_t size, Error **errp) +static Qcow2BitmapList * GRAPH_RDLOCK +bitmap_list_load(BlockDriverState *bs, uint64_t offset, uint64_t size, + Error **errp) { int ret; BDRVQcow2State *s = bs->opaque; @@ -575,7 +580,7 @@ static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset, } dir_end = dir + size; - ret = bdrv_pread(bs->file, offset, dir, size); + ret = bdrv_pread(bs->file, offset, size, dir, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read bitmap directory"); goto fail; @@ -647,9 +652,10 @@ fail: return NULL; } -int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, - void **refcount_table, - int64_t *refcount_table_size) +int coroutine_fn +qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, + void **refcount_table, + int64_t *refcount_table_size) { int ret; BDRVQcow2State *s = bs->opaque; @@ -727,8 +733,9 @@ out: * Store bitmap list to qcow2 image as a bitmap directory. * Everything is checked. */ -static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list, - uint64_t *offset, uint64_t *size, bool in_place) +static int GRAPH_RDLOCK +bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list, + uint64_t *offset, uint64_t *size, bool in_place) { int ret; uint8_t *dir; @@ -787,10 +794,10 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list, } } - /* Actually, even in in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY is not - * necessary, because we drop QCOW2_AUTOCLEAR_BITMAPS when updating bitmap - * directory in-place (actually, turn-off the extension), which is checked - * in qcow2_check_metadata_overlap() */ + /* Actually, even in the in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY + * is not necessary, because we drop QCOW2_AUTOCLEAR_BITMAPS when updating + * bitmap directory in-place (actually, turn-off the extension), which is + * checked in qcow2_check_metadata_overlap() */ ret = qcow2_pre_write_overlap_check( bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size, false); @@ -798,7 +805,7 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list, goto fail; } - ret = bdrv_pwrite(bs->file, dir_offset, dir, dir_size); + ret = bdrv_pwrite(bs->file, dir_offset, dir_size, dir, 0); if (ret < 0) { goto fail; } @@ -826,8 +833,9 @@ fail: * Bitmap List end */ -static int update_ext_header_and_dir_in_place(BlockDriverState *bs, - Qcow2BitmapList *bm_list) +static int GRAPH_RDLOCK +update_ext_header_and_dir_in_place(BlockDriverState *bs, + Qcow2BitmapList *bm_list) { BDRVQcow2State *s = bs->opaque; int ret; @@ -874,8 +882,8 @@ static int update_ext_header_and_dir_in_place(BlockDriverState *bs, */ } -static int update_ext_header_and_dir(BlockDriverState *bs, - Qcow2BitmapList *bm_list) +static int GRAPH_RDLOCK +update_ext_header_and_dir(BlockDriverState *bs, Qcow2BitmapList *bm_list) { BDRVQcow2State *s = bs->opaque; int ret; @@ -955,8 +963,9 @@ static void set_readonly_helper(gpointer bitmap, gpointer value) * If header_updated is not NULL then it is set appropriately regardless of * the return value. */ -bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, - Error **errp) +bool coroutine_fn +qcow2_load_dirty_bitmaps(BlockDriverState *bs, + bool *header_updated, Error **errp) { BDRVQcow2State *s = bs->opaque; Qcow2BitmapList *bm_list; @@ -1208,7 +1217,7 @@ int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp) } } - g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, false); + g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, (gpointer)false); ret = 0; out: @@ -1219,7 +1228,7 @@ out: } /* Checks to see if it's safe to resize bitmaps */ -int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp) +int coroutine_fn qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp) { BDRVQcow2State *s = bs->opaque; Qcow2BitmapList *bm_list; @@ -1267,9 +1276,9 @@ out: /* store_bitmap_data() * Store bitmap to image, filling bitmap table accordingly. */ -static uint64_t *store_bitmap_data(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap, - uint32_t *bitmap_table_size, Error **errp) +static uint64_t * GRAPH_RDLOCK +store_bitmap_data(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, + uint32_t *bitmap_table_size, Error **errp) { int ret; BDRVQcow2State *s = bs->opaque; @@ -1339,7 +1348,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, goto fail; } - ret = bdrv_pwrite(bs->file, off, buf, s->cluster_size); + ret = bdrv_pwrite(bs->file, off, s->cluster_size, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write bitmap '%s' to file", bm_name); @@ -1366,7 +1375,8 @@ fail: * Store bm->dirty_bitmap to qcow2. * Set bm->table_offset and bm->table_size accordingly. */ -static int store_bitmap(BlockDriverState *bs, Qcow2Bitmap *bm, Error **errp) +static int GRAPH_RDLOCK +store_bitmap(BlockDriverState *bs, Qcow2Bitmap *bm, Error **errp) { int ret; uint64_t *tb; @@ -1401,9 +1411,10 @@ static int store_bitmap(BlockDriverState *bs, Qcow2Bitmap *bm, Error **errp) goto fail; } - bitmap_table_to_be(tb, tb_size); - ret = bdrv_pwrite(bs->file, tb_offset, tb, tb_size * sizeof(tb[0])); + bitmap_table_bswap_be(tb, tb_size); + ret = bdrv_pwrite(bs->file, tb_offset, tb_size * sizeof(tb[0]), tb, 0); if (ret < 0) { + bitmap_table_bswap_be(tb, tb_size); error_setg_errno(errp, -ret, "Failed to write bitmap '%s' to file", bm_name); goto fail; @@ -1550,7 +1561,6 @@ bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, FOR_EACH_DIRTY_BITMAP(bs, bitmap) { const char *name = bdrv_dirty_bitmap_name(bitmap); uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap); - Qcow2Bitmap *bm; if (!bdrv_dirty_bitmap_get_persistence(bitmap) || bdrv_dirty_bitmap_inconsistent(bitmap)) { @@ -1620,7 +1630,7 @@ bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, /* allocate clusters and store bitmaps */ QSIMPLEQ_FOREACH(bm, bm_list, entry) { - BdrvDirtyBitmap *bitmap = bm->dirty_bitmap; + bitmap = bm->dirty_bitmap; if (bitmap == NULL || bdrv_dirty_bitmap_readonly(bitmap)) { continue; @@ -1700,6 +1710,7 @@ bool coroutine_fn qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs, uint32_t granularity, Error **errp) { + ERRP_GUARD(); BDRVQcow2State *s = bs->opaque; BdrvDirtyBitmap *bitmap; uint64_t bitmap_directory_size = 0; diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c index 539f9ca2d5..23d9588b08 100644 --- a/block/qcow2-cache.c +++ b/block/qcow2-cache.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "block/block-io.h" #include "qemu/memalign.h" #include "qcow2.h" #include "trace.h" @@ -162,7 +163,8 @@ int qcow2_cache_destroy(Qcow2Cache *c) return 0; } -static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c) +static int GRAPH_RDLOCK +qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c) { int ret; @@ -177,7 +179,8 @@ static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c) return 0; } -static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) +static int GRAPH_RDLOCK +qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) { BDRVQcow2State *s = bs->opaque; int ret = 0; @@ -223,8 +226,8 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); } - ret = bdrv_pwrite(bs->file, c->entries[i].offset, - qcow2_cache_get_table_addr(c, i), c->table_size); + ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->table_size, + qcow2_cache_get_table_addr(c, i), 0); if (ret < 0) { return ret; } @@ -317,8 +320,9 @@ int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c) return 0; } -static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, - uint64_t offset, void **table, bool read_from_disk) +static int GRAPH_RDLOCK +qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table, bool read_from_disk) { BDRVQcow2State *s = bs->opaque; int i; @@ -379,9 +383,8 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); } - ret = bdrv_pread(bs->file, offset, - qcow2_cache_get_table_addr(c, i), - c->table_size); + ret = bdrv_pread(bs->file, offset, c->table_size, + qcow2_cache_get_table_addr(c, i), 0); if (ret < 0) { return ret; } diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 20a16ba6ee..ce8c0076b3 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -25,13 +25,15 @@ #include "qemu/osdep.h" #include +#include "block/block-io.h" #include "qapi/error.h" #include "qcow2.h" #include "qemu/bswap.h" #include "qemu/memalign.h" #include "trace.h" -int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t exact_size) +int coroutine_fn qcow2_shrink_l1_table(BlockDriverState *bs, + uint64_t exact_size) { BDRVQcow2State *s = bs->opaque; int new_l1_size, i, ret; @@ -46,20 +48,20 @@ int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t exact_size) fprintf(stderr, "shrink l1_table from %d to %d\n", s->l1_size, new_l1_size); #endif - BLKDBG_EVENT(bs->file, BLKDBG_L1_SHRINK_WRITE_TABLE); - ret = bdrv_pwrite_zeroes(bs->file, s->l1_table_offset + - new_l1_size * L1E_SIZE, - (s->l1_size - new_l1_size) * L1E_SIZE, 0); + BLKDBG_CO_EVENT(bs->file, BLKDBG_L1_SHRINK_WRITE_TABLE); + ret = bdrv_co_pwrite_zeroes(bs->file, + s->l1_table_offset + new_l1_size * L1E_SIZE, + (s->l1_size - new_l1_size) * L1E_SIZE, 0); if (ret < 0) { goto fail; } - ret = bdrv_flush(bs->file->bs); + ret = bdrv_co_flush(bs->file->bs); if (ret < 0) { goto fail; } - BLKDBG_EVENT(bs->file, BLKDBG_L1_SHRINK_FREE_L2_CLUSTERS); + BLKDBG_CO_EVENT(bs->file, BLKDBG_L1_SHRINK_FREE_L2_CLUSTERS); for (i = s->l1_size - 1; i > new_l1_size - 1; i--) { if ((s->l1_table[i] & L1E_OFFSET_MASK) == 0) { continue; @@ -159,8 +161,8 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE); for(i = 0; i < s->l1_size; i++) new_l1_table[i] = cpu_to_be64(new_l1_table[i]); - ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, - new_l1_table, new_l1_size2); + ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_size2, + new_l1_table, 0); if (ret < 0) goto fail; for(i = 0; i < s->l1_size; i++) @@ -171,7 +173,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, stl_be_p(data, new_l1_size); stq_be_p(data + 4, new_l1_table_offset); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), - data, sizeof(data)); + sizeof(data), data, 0); if (ret < 0) { goto fail; } @@ -205,8 +207,9 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, * the cache is used; otherwise the L2 slice is loaded from the image * file. */ -static int l2_load(BlockDriverState *bs, uint64_t offset, - uint64_t l2_offset, uint64_t **l2_slice) +static int GRAPH_RDLOCK +l2_load(BlockDriverState *bs, uint64_t offset, + uint64_t l2_offset, uint64_t **l2_slice) { BDRVQcow2State *s = bs->opaque; int start_of_slice = l2_entry_size(s) * @@ -249,7 +252,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 + L1E_SIZE * l1_start_index, - buf, bufsize); + bufsize, buf, 0); if (ret < 0) { return ret; } @@ -267,7 +270,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) * */ -static int l2_allocate(BlockDriverState *bs, int l1_index) +static int GRAPH_RDLOCK l2_allocate(BlockDriverState *bs, int l1_index) { BDRVQcow2State *s = bs->opaque; uint64_t old_l2_offset; @@ -388,11 +391,10 @@ fail: * If the L2 entry is invalid return -errno and set @type to * QCOW2_SUBCLUSTER_INVALID. */ -static int qcow2_get_subcluster_range_type(BlockDriverState *bs, - uint64_t l2_entry, - uint64_t l2_bitmap, - unsigned sc_from, - QCow2SubclusterType *type) +static int GRAPH_RDLOCK +qcow2_get_subcluster_range_type(BlockDriverState *bs, uint64_t l2_entry, + uint64_t l2_bitmap, unsigned sc_from, + QCow2SubclusterType *type) { BDRVQcow2State *s = bs->opaque; uint32_t val; @@ -439,9 +441,10 @@ static int qcow2_get_subcluster_range_type(BlockDriverState *bs, * On failure return -errno and update @l2_index to point to the * invalid entry. */ -static int count_contiguous_subclusters(BlockDriverState *bs, int nb_clusters, - unsigned sc_index, uint64_t *l2_slice, - unsigned *l2_index) +static int GRAPH_RDLOCK +count_contiguous_subclusters(BlockDriverState *bs, int nb_clusters, + unsigned sc_index, uint64_t *l2_slice, + unsigned *l2_index) { BDRVQcow2State *s = bs->opaque; int i, count = 0; @@ -489,10 +492,9 @@ static int count_contiguous_subclusters(BlockDriverState *bs, int nb_clusters, return count; } -static int coroutine_fn do_perform_cow_read(BlockDriverState *bs, - uint64_t src_cluster_offset, - unsigned offset_in_cluster, - QEMUIOVector *qiov) +static int coroutine_fn GRAPH_RDLOCK +do_perform_cow_read(BlockDriverState *bs, uint64_t src_cluster_offset, + unsigned offset_in_cluster, QEMUIOVector *qiov) { int ret; @@ -500,7 +502,7 @@ static int coroutine_fn do_perform_cow_read(BlockDriverState *bs, return 0; } - BLKDBG_EVENT(bs->file, BLKDBG_COW_READ); + BLKDBG_CO_EVENT(bs->file, BLKDBG_COW_READ); if (!bs->drv) { return -ENOMEDIUM; @@ -533,10 +535,9 @@ static int coroutine_fn do_perform_cow_read(BlockDriverState *bs, return 0; } -static int coroutine_fn do_perform_cow_write(BlockDriverState *bs, - uint64_t cluster_offset, - unsigned offset_in_cluster, - QEMUIOVector *qiov) +static int coroutine_fn GRAPH_RDLOCK +do_perform_cow_write(BlockDriverState *bs, uint64_t cluster_offset, + unsigned offset_in_cluster, QEMUIOVector *qiov) { BDRVQcow2State *s = bs->opaque; int ret; @@ -551,7 +552,7 @@ static int coroutine_fn do_perform_cow_write(BlockDriverState *bs, return ret; } - BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE); + BLKDBG_CO_EVENT(bs->file, BLKDBG_COW_WRITE); ret = bdrv_co_pwritev(s->data_file, cluster_offset + offset_in_cluster, qiov->size, qiov, 0); if (ret < 0) { @@ -751,9 +752,9 @@ fail: * * Returns 0 on success, -errno in failure case */ -static int get_cluster_table(BlockDriverState *bs, uint64_t offset, - uint64_t **new_l2_slice, - int *new_l2_index) +static int GRAPH_RDLOCK +get_cluster_table(BlockDriverState *bs, uint64_t offset, + uint64_t **new_l2_slice, int *new_l2_index) { BDRVQcow2State *s = bs->opaque; unsigned int l2_index; @@ -823,10 +824,9 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, * * Return 0 on success and -errno in error cases */ -int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, - uint64_t offset, - int compressed_size, - uint64_t *host_offset) +int coroutine_fn GRAPH_RDLOCK +qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset, + int compressed_size, uint64_t *host_offset) { BDRVQcow2State *s = bs->opaque; int l2_index, ret; @@ -872,7 +872,7 @@ int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, /* compressed clusters never have the copied flag */ - BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); + BLKDBG_CO_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); set_l2_entry(s, l2_slice, l2_index, cluster_offset); if (has_subclusters(s)) { @@ -884,7 +884,8 @@ int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, return 0; } -static int perform_cow(BlockDriverState *bs, QCowL2Meta *m) +static int coroutine_fn GRAPH_RDLOCK +perform_cow(BlockDriverState *bs, QCowL2Meta *m) { BDRVQcow2State *s = bs->opaque; Qcow2COWRegion *start = &m->cow_start; @@ -991,7 +992,7 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m) /* NOTE: we have a write_aio blkdebug event here followed by * a cow_write one in do_perform_cow_write(), but there's only * one single I/O operation */ - BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); + BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); ret = do_perform_cow_write(bs, m->alloc_offset, start->offset, &qiov); } else { /* If there's no guest data then write both COW regions separately */ @@ -1024,7 +1025,8 @@ fail: return ret; } -int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) +int coroutine_fn qcow2_alloc_cluster_link_l2(BlockDriverState *bs, + QCowL2Meta *m) { BDRVQcow2State *s = bs->opaque; int i, j = 0, l2_index, ret; @@ -1124,7 +1126,7 @@ err: * Frees the allocated clusters because the request failed and they won't * actually be linked. */ -void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m) +void coroutine_fn qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m) { BDRVQcow2State *s = bs->opaque; if (!has_data_file(bs) && !m->keep_old_clusters) { @@ -1154,9 +1156,10 @@ void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m) * * Returns 0 on success, -errno on failure. */ -static int calculate_l2_meta(BlockDriverState *bs, uint64_t host_cluster_offset, - uint64_t guest_offset, unsigned bytes, - uint64_t *l2_slice, QCowL2Meta **m, bool keep_old) +static int coroutine_fn GRAPH_RDLOCK +calculate_l2_meta(BlockDriverState *bs, uint64_t host_cluster_offset, + uint64_t guest_offset, unsigned bytes, uint64_t *l2_slice, + QCowL2Meta **m, bool keep_old) { BDRVQcow2State *s = bs->opaque; int sc_index, l2_index = offset_to_l2_slice_index(s, guest_offset); @@ -1326,7 +1329,8 @@ static int calculate_l2_meta(BlockDriverState *bs, uint64_t host_cluster_offset, * requires a new allocation (that is, if the cluster is unallocated * or has refcount > 1 and therefore cannot be written in-place). */ -static bool cluster_needs_new_alloc(BlockDriverState *bs, uint64_t l2_entry) +static bool GRAPH_RDLOCK +cluster_needs_new_alloc(BlockDriverState *bs, uint64_t l2_entry) { switch (qcow2_get_cluster_type(bs, l2_entry)) { case QCOW2_CLUSTER_NORMAL: @@ -1357,9 +1361,9 @@ static bool cluster_needs_new_alloc(BlockDriverState *bs, uint64_t l2_entry) * allocated and can be overwritten in-place (this includes clusters * of type QCOW2_CLUSTER_ZERO_ALLOC). */ -static int count_single_write_clusters(BlockDriverState *bs, int nb_clusters, - uint64_t *l2_slice, int l2_index, - bool new_alloc) +static int GRAPH_RDLOCK +count_single_write_clusters(BlockDriverState *bs, int nb_clusters, + uint64_t *l2_slice, int l2_index, bool new_alloc) { BDRVQcow2State *s = bs->opaque; uint64_t l2_entry = get_l2_entry(s, l2_slice, l2_index); @@ -1397,8 +1401,9 @@ static int count_single_write_clusters(BlockDriverState *bs, int nb_clusters, * information on cluster allocation may be invalid now. The caller * must start over anyway, so consider *cur_bytes undefined. */ -static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset, - uint64_t *cur_bytes, QCowL2Meta **m) +static int coroutine_fn handle_dependencies(BlockDriverState *bs, + uint64_t guest_offset, + uint64_t *cur_bytes, QCowL2Meta **m) { BDRVQcow2State *s = bs->opaque; QCowL2Meta *old_alloc; @@ -1486,8 +1491,9 @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset, * * -errno: in error cases */ -static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, - uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) +static int coroutine_fn GRAPH_RDLOCK +handle_copied(BlockDriverState *bs, uint64_t guest_offset, + uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) { BDRVQcow2State *s = bs->opaque; int l2_index; @@ -1595,8 +1601,9 @@ out: * function has been waiting for another request and the allocation must be * restarted, but the whole request should not be failed. */ -static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, - uint64_t *host_offset, uint64_t *nb_clusters) +static int coroutine_fn GRAPH_RDLOCK +do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, + uint64_t *host_offset, uint64_t *nb_clusters) { BDRVQcow2State *s = bs->opaque; @@ -1651,8 +1658,9 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, * * -errno: in error cases */ -static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, - uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) +static int coroutine_fn GRAPH_RDLOCK +handle_alloc(BlockDriverState *bs, uint64_t guest_offset, + uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) { BDRVQcow2State *s = bs->opaque; int l2_index; @@ -1772,9 +1780,10 @@ out: * * Return 0 on success and -errno in error cases */ -int qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, - unsigned int *bytes, uint64_t *host_offset, - QCowL2Meta **m) +int coroutine_fn qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, + unsigned int *bytes, + uint64_t *host_offset, + QCowL2Meta **m) { BDRVQcow2State *s = bs->opaque; uint64_t start, remaining; @@ -1889,9 +1898,9 @@ again: * all clusters in the same L2 slice) and returns the number of discarded * clusters. */ -static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, - uint64_t nb_clusters, - enum qcow2_discard_type type, bool full_discard) +static int GRAPH_RDLOCK +discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, uint64_t nb_clusters, + enum qcow2_discard_type type, bool full_discard) { BDRVQcow2State *s = bs->opaque; uint64_t *l2_slice; @@ -1915,6 +1924,10 @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, uint64_t new_l2_bitmap = old_l2_bitmap; QCow2ClusterType cluster_type = qcow2_get_cluster_type(bs, old_l2_entry); + bool keep_reference = (cluster_type != QCOW2_CLUSTER_COMPRESSED) && + !full_discard && + (s->discard_no_unref && + type == QCOW2_DISCARD_REQUEST); /* * If full_discard is true, the cluster should not read back as zeroes, @@ -1933,10 +1946,22 @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, new_l2_entry = new_l2_bitmap = 0; } else if (bs->backing || qcow2_cluster_is_allocated(cluster_type)) { if (has_subclusters(s)) { - new_l2_entry = 0; + if (keep_reference) { + new_l2_entry = old_l2_entry; + } else { + new_l2_entry = 0; + } new_l2_bitmap = QCOW_L2_BITMAP_ALL_ZEROES; } else { - new_l2_entry = s->qcow_version >= 3 ? QCOW_OFLAG_ZERO : 0; + if (s->qcow_version >= 3) { + if (keep_reference) { + new_l2_entry |= QCOW_OFLAG_ZERO; + } else { + new_l2_entry = QCOW_OFLAG_ZERO; + } + } else { + new_l2_entry = 0; + } } } @@ -1950,8 +1975,16 @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, if (has_subclusters(s)) { set_l2_bitmap(s, l2_slice, l2_index + i, new_l2_bitmap); } - /* Then decrease the refcount */ - qcow2_free_any_cluster(bs, old_l2_entry, type); + if (!keep_reference) { + /* Then decrease the refcount */ + qcow2_free_any_cluster(bs, old_l2_entry, type); + } else if (s->discard_passthrough[type] && + (cluster_type == QCOW2_CLUSTER_NORMAL || + cluster_type == QCOW2_CLUSTER_ZERO_ALLOC)) { + /* If we keep the reference, pass on the discard still */ + bdrv_pdiscard(s->data_file, old_l2_entry & L2E_OFFSET_MASK, + s->cluster_size); + } } qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); @@ -2004,8 +2037,9 @@ fail: * all clusters in the same L2 slice) and returns the number of zeroed * clusters. */ -static int zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, - uint64_t nb_clusters, int flags) +static int coroutine_fn GRAPH_RDLOCK +zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, + uint64_t nb_clusters, int flags) { BDRVQcow2State *s = bs->opaque; uint64_t *l2_slice; @@ -2028,9 +2062,15 @@ static int zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, QCow2ClusterType type = qcow2_get_cluster_type(bs, old_l2_entry); bool unmap = (type == QCOW2_CLUSTER_COMPRESSED) || ((flags & BDRV_REQ_MAY_UNMAP) && qcow2_cluster_is_allocated(type)); - uint64_t new_l2_entry = unmap ? 0 : old_l2_entry; + bool keep_reference = + (s->discard_no_unref && type != QCOW2_CLUSTER_COMPRESSED); + uint64_t new_l2_entry = old_l2_entry; uint64_t new_l2_bitmap = old_l2_bitmap; + if (unmap && !keep_reference) { + new_l2_entry = 0; + } + if (has_subclusters(s)) { new_l2_bitmap = QCOW_L2_BITMAP_ALL_ZEROES; } else { @@ -2048,9 +2088,17 @@ static int zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, set_l2_bitmap(s, l2_slice, l2_index + i, new_l2_bitmap); } - /* Then decrease the refcount */ if (unmap) { - qcow2_free_any_cluster(bs, old_l2_entry, QCOW2_DISCARD_REQUEST); + if (!keep_reference) { + /* Then decrease the refcount */ + qcow2_free_any_cluster(bs, old_l2_entry, QCOW2_DISCARD_REQUEST); + } else if (s->discard_passthrough[QCOW2_DISCARD_REQUEST] && + (type == QCOW2_CLUSTER_NORMAL || + type == QCOW2_CLUSTER_ZERO_ALLOC)) { + /* If we keep the reference, pass on the discard still */ + bdrv_pdiscard(s->data_file, old_l2_entry & L2E_OFFSET_MASK, + s->cluster_size); + } } } @@ -2059,8 +2107,9 @@ static int zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, return nb_clusters; } -static int zero_l2_subclusters(BlockDriverState *bs, uint64_t offset, - unsigned nb_subclusters) +static int coroutine_fn GRAPH_RDLOCK +zero_l2_subclusters(BlockDriverState *bs, uint64_t offset, + unsigned nb_subclusters) { BDRVQcow2State *s = bs->opaque; uint64_t *l2_slice; @@ -2105,8 +2154,8 @@ out: return ret; } -int qcow2_subcluster_zeroize(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, int flags) +int coroutine_fn qcow2_subcluster_zeroize(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, int flags) { BDRVQcow2State *s = bs->opaque; uint64_t end_offset = offset + bytes; @@ -2196,11 +2245,12 @@ fail: * status_cb(). l1_entries contains the total number of L1 entries and * *visited_l1_entries counts all visited L1 entries. */ -static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, - int l1_size, int64_t *visited_l1_entries, - int64_t l1_entries, - BlockDriverAmendStatusCB *status_cb, - void *cb_opaque) +static int GRAPH_RDLOCK +expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, + int l1_size, int64_t *visited_l1_entries, + int64_t l1_entries, + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque) { BDRVQcow2State *s = bs->opaque; bool is_active_l1 = (l1_table == s->l1_table); @@ -2260,7 +2310,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, (void **)&l2_slice); } else { /* load inactive L2 tables from disk */ - ret = bdrv_pread(bs->file, slice_offset, l2_slice, slice_size2); + ret = bdrv_pread(bs->file, slice_offset, slice_size2, + l2_slice, 0); } if (ret < 0) { goto fail; @@ -2376,8 +2427,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, goto fail; } - ret = bdrv_pwrite(bs->file, slice_offset, - l2_slice, slice_size2); + ret = bdrv_pwrite(bs->file, slice_offset, slice_size2, + l2_slice, 0); if (ret < 0) { goto fail; } @@ -2470,8 +2521,8 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, l1_table = new_l1_table; - ret = bdrv_pread(bs->file, s->snapshots[i].l1_table_offset, - l1_table, l1_size2); + ret = bdrv_pread(bs->file, s->snapshots[i].l1_table_offset, l1_size2, + l1_table, 0); if (ret < 0) { goto fail; } diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index ed0ecfaa89..0266542cee 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "block/block-io.h" #include "qapi/error.h" #include "qcow2.h" #include "qemu/range.h" @@ -97,7 +98,7 @@ static void update_max_refcount_table_index(BDRVQcow2State *s) s->max_refcount_table_index = i; } -int qcow2_refcount_init(BlockDriverState *bs) +int coroutine_fn qcow2_refcount_init(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; unsigned int refcount_table_size2, i; @@ -117,9 +118,9 @@ int qcow2_refcount_init(BlockDriverState *bs) ret = -ENOMEM; goto fail; } - BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_LOAD); - ret = bdrv_pread(bs->file, s->refcount_table_offset, - s->refcount_table, refcount_table_size2); + BLKDBG_CO_EVENT(bs->file, BLKDBG_REFTABLE_LOAD); + ret = bdrv_co_pread(bs->file, s->refcount_table_offset, + refcount_table_size2, s->refcount_table, 0); if (ret < 0) { goto fail; } @@ -228,9 +229,9 @@ static void set_refcount_ro6(void *refcount_array, uint64_t index, } -static int load_refcount_block(BlockDriverState *bs, - int64_t refcount_block_offset, - void **refcount_block) +static int GRAPH_RDLOCK +load_refcount_block(BlockDriverState *bs, int64_t refcount_block_offset, + void **refcount_block) { BDRVQcow2State *s = bs->opaque; @@ -301,8 +302,9 @@ static int in_same_refcount_block(BDRVQcow2State *s, uint64_t offset_a, * * Returns 0 on success or -errno in error case */ -static int alloc_refcount_block(BlockDriverState *bs, - int64_t cluster_index, void **refcount_block) +static int GRAPH_RDLOCK +alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index, + void **refcount_block) { BDRVQcow2State *s = bs->opaque; unsigned int refcount_table_index; @@ -439,7 +441,7 @@ static int alloc_refcount_block(BlockDriverState *bs, BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_HOOKUP); ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset + refcount_table_index * REFTABLE_ENTRY_SIZE, - &data64, sizeof(data64)); + sizeof(data64), &data64, 0); if (ret < 0) { goto fail; } @@ -684,8 +686,8 @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, } BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE); - ret = bdrv_pwrite_sync(bs->file, table_offset, new_table, - table_size * REFTABLE_ENTRY_SIZE); + ret = bdrv_pwrite_sync(bs->file, table_offset, + table_size * REFTABLE_ENTRY_SIZE, new_table, 0); if (ret < 0) { goto fail; } @@ -704,7 +706,7 @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, refcount_table_offset), - &data, sizeof(data)); + sizeof(data), &data, 0); if (ret < 0) { goto fail; } @@ -805,12 +807,9 @@ found: /* XXX: cache several refcount block clusters ? */ /* @addend is the absolute value of the addend; if @decrease is set, @addend * will be subtracted from the current refcount, otherwise it will be added */ -static int update_refcount(BlockDriverState *bs, - int64_t offset, - int64_t length, - uint64_t addend, - bool decrease, - enum qcow2_discard_type type) +static int GRAPH_RDLOCK +update_refcount(BlockDriverState *bs, int64_t offset, int64_t length, + uint64_t addend, bool decrease, enum qcow2_discard_type type) { BDRVQcow2State *s = bs->opaque; int64_t start, last, cluster_offset; @@ -966,8 +965,8 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, /* return < 0 if error */ -static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size, - uint64_t max) +static int64_t GRAPH_RDLOCK +alloc_clusters_noref(BlockDriverState *bs, uint64_t size, uint64_t max) { BDRVQcow2State *s = bs->opaque; uint64_t i, nb_clusters, refcount; @@ -1029,8 +1028,8 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) return offset; } -int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, - int64_t nb_clusters) +int64_t coroutine_fn qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, + int64_t nb_clusters) { BDRVQcow2State *s = bs->opaque; uint64_t cluster_index, refcount; @@ -1068,14 +1067,14 @@ int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, /* only used to allocate compressed sectors. We try to allocate contiguous sectors. size must be <= cluster_size */ -int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) +int64_t coroutine_fn GRAPH_RDLOCK qcow2_alloc_bytes(BlockDriverState *bs, int size) { BDRVQcow2State *s = bs->opaque; int64_t offset; size_t free_in_cluster; int ret; - BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_BYTES); + BLKDBG_CO_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_BYTES); assert(size > 0 && size <= s->cluster_size); assert(!s->free_byte_offset || offset_into_cluster(s, s->free_byte_offset)); @@ -1206,7 +1205,7 @@ void qcow2_free_any_cluster(BlockDriverState *bs, uint64_t l2_entry, } } -int coroutine_fn qcow2_write_caches(BlockDriverState *bs) +int qcow2_write_caches(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; int ret; @@ -1226,7 +1225,7 @@ int coroutine_fn qcow2_write_caches(BlockDriverState *bs) return 0; } -int coroutine_fn qcow2_flush_caches(BlockDriverState *bs) +int qcow2_flush_caches(BlockDriverState *bs) { int ret = qcow2_write_caches(bs); if (ret < 0) { @@ -1274,7 +1273,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, } l1_allocated = true; - ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2); + ret = bdrv_pread(bs->file, l1_table_offset, l1_size2, l1_table, 0); if (ret < 0) { goto fail; } @@ -1435,8 +1434,8 @@ fail: cpu_to_be64s(&l1_table[i]); } - ret = bdrv_pwrite_sync(bs->file, l1_table_offset, - l1_table, l1_size2); + ret = bdrv_pwrite_sync(bs->file, l1_table_offset, l1_size2, l1_table, + 0); for (i = 0; i < l1_size; i++) { be64_to_cpus(&l1_table[i]); @@ -1523,10 +1522,11 @@ static int realloc_refcount_array(BDRVQcow2State *s, void **array, * * Modifies the number of errors in res. */ -int qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res, - void **refcount_table, - int64_t *refcount_table_size, - int64_t offset, int64_t size) +int coroutine_fn GRAPH_RDLOCK +qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res, + void **refcount_table, + int64_t *refcount_table_size, + int64_t offset, int64_t size) { BDRVQcow2State *s = bs->opaque; uint64_t start, last, cluster_offset, k, refcount; @@ -1537,7 +1537,7 @@ int qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res, return 0; } - file_len = bdrv_getlength(bs->file->bs); + file_len = bdrv_co_getlength(bs->file->bs); if (file_len < 0) { return file_len; } @@ -1599,10 +1599,11 @@ enum { * * On failure in-memory @l2_table may be modified. */ -static int fix_l2_entry_by_zero(BlockDriverState *bs, BdrvCheckResult *res, - uint64_t l2_offset, - uint64_t *l2_table, int l2_index, bool active, - bool *metadata_overlap) +static int coroutine_fn GRAPH_RDLOCK +fix_l2_entry_by_zero(BlockDriverState *bs, BdrvCheckResult *res, + uint64_t l2_offset, uint64_t *l2_table, + int l2_index, bool active, + bool *metadata_overlap) { BDRVQcow2State *s = bs->opaque; int ret; @@ -1633,8 +1634,8 @@ static int fix_l2_entry_by_zero(BlockDriverState *bs, BdrvCheckResult *res, goto fail; } - ret = bdrv_pwrite_sync(bs->file, l2e_offset, &l2_table[idx], - l2_entry_size(s)); + ret = bdrv_co_pwrite_sync(bs->file, l2e_offset, l2_entry_size(s), + &l2_table[idx], 0); if (ret < 0) { fprintf(stderr, "ERROR: Failed to overwrite L2 " "table entry: %s\n", strerror(-ret)); @@ -1658,10 +1659,11 @@ fail: * Returns the number of errors found by the checks or -errno if an internal * error occurred. */ -static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, - void **refcount_table, - int64_t *refcount_table_size, int64_t l2_offset, - int flags, BdrvCheckMode fix, bool active) +static int coroutine_fn GRAPH_RDLOCK +check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, + void **refcount_table, + int64_t *refcount_table_size, int64_t l2_offset, + int flags, BdrvCheckMode fix, bool active) { BDRVQcow2State *s = bs->opaque; uint64_t l2_entry, l2_bitmap; @@ -1672,7 +1674,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, bool metadata_overlap; /* Read L2 table from disk */ - ret = bdrv_pread(bs->file, l2_offset, l2_table, l2_size_bytes); + ret = bdrv_co_pread(bs->file, l2_offset, l2_size_bytes, l2_table, 0); if (ret < 0) { fprintf(stderr, "ERROR: I/O error in check_refcounts_l2\n"); res->check_errors++; @@ -1857,12 +1859,11 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, * Returns the number of errors found by the checks or -errno if an internal * error occurred. */ -static int check_refcounts_l1(BlockDriverState *bs, - BdrvCheckResult *res, - void **refcount_table, - int64_t *refcount_table_size, - int64_t l1_table_offset, int l1_size, - int flags, BdrvCheckMode fix, bool active) +static int coroutine_fn GRAPH_RDLOCK +check_refcounts_l1(BlockDriverState *bs, BdrvCheckResult *res, + void **refcount_table, int64_t *refcount_table_size, + int64_t l1_table_offset, int l1_size, + int flags, BdrvCheckMode fix, bool active) { BDRVQcow2State *s = bs->opaque; size_t l1_size_bytes = l1_size * L1E_SIZE; @@ -1888,7 +1889,7 @@ static int check_refcounts_l1(BlockDriverState *bs, } /* Read L1 table entries from disk */ - ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size_bytes); + ret = bdrv_co_pread(bs->file, l1_table_offset, l1_size_bytes, l1_table, 0); if (ret < 0) { fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); res->check_errors++; @@ -1948,8 +1949,8 @@ static int check_refcounts_l1(BlockDriverState *bs, * have been already detected and sufficiently signaled by the calling function * (qcow2_check_refcounts) by the time this function is called). */ -static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, - BdrvCheckMode fix) +static int coroutine_fn GRAPH_RDLOCK +check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) { BDRVQcow2State *s = bs->opaque; uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size); @@ -2004,8 +2005,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, } } - ret = bdrv_pread(bs->file, l2_offset, l2_table, - s->l2_size * l2_entry_size(s)); + ret = bdrv_co_pread(bs->file, l2_offset, s->l2_size * l2_entry_size(s), + l2_table, 0); if (ret < 0) { fprintf(stderr, "ERROR: Could not read L2 table: %s\n", strerror(-ret)); @@ -2058,8 +2059,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, goto fail; } - ret = bdrv_pwrite(bs->file, l2_offset, l2_table, - s->cluster_size); + ret = bdrv_co_pwrite(bs->file, l2_offset, s->cluster_size, l2_table, 0); if (ret < 0) { fprintf(stderr, "ERROR: Could not write L2 table: %s\n", strerror(-ret)); @@ -2082,9 +2082,10 @@ fail: * Checks consistency of refblocks and accounts for each refblock in * *refcount_table. */ -static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, - BdrvCheckMode fix, bool *rebuild, - void **refcount_table, int64_t *nb_clusters) +static int coroutine_fn GRAPH_RDLOCK +check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix, bool *rebuild, + void **refcount_table, int64_t *nb_clusters) { BDRVQcow2State *s = bs->opaque; int64_t i, size; @@ -2126,13 +2127,13 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, goto resize_fail; } - ret = bdrv_truncate(bs->file, offset + s->cluster_size, false, - PREALLOC_MODE_OFF, 0, &local_err); + ret = bdrv_co_truncate(bs->file, offset + s->cluster_size, false, + PREALLOC_MODE_OFF, 0, &local_err); if (ret < 0) { error_report_err(local_err); goto resize_fail; } - size = bdrv_getlength(bs->file->bs); + size = bdrv_co_getlength(bs->file->bs); if (size < 0) { ret = size; goto resize_fail; @@ -2196,9 +2197,10 @@ resize_fail: /* * Calculates an in-memory refcount table. */ -static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, - BdrvCheckMode fix, bool *rebuild, - void **refcount_table, int64_t *nb_clusters) +static int coroutine_fn GRAPH_RDLOCK +calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix, bool *rebuild, + void **refcount_table, int64_t *nb_clusters) { BDRVQcow2State *s = bs->opaque; int64_t i; @@ -2298,10 +2300,11 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, * Compares the actual reference count for each cluster in the image against the * refcount as reported by the refcount structures on-disk. */ -static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, - BdrvCheckMode fix, bool *rebuild, - int64_t *highest_cluster, - void *refcount_table, int64_t nb_clusters) +static void coroutine_fn GRAPH_RDLOCK +compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix, bool *rebuild, + int64_t *highest_cluster, + void *refcount_table, int64_t nb_clusters) { BDRVQcow2State *s = bs->opaque; int64_t i; @@ -2462,7 +2465,8 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, * Return whether the on-disk reftable array was resized (true/false), * or -errno on error. */ -static int rebuild_refcounts_write_refblocks( +static int coroutine_fn GRAPH_RDLOCK +rebuild_refcounts_write_refblocks( BlockDriverState *bs, void **refcount_table, int64_t *nb_clusters, int64_t first_cluster, int64_t end_cluster, uint64_t **on_disk_reftable_ptr, uint32_t *on_disk_reftable_entries_ptr, @@ -2577,8 +2581,8 @@ static int rebuild_refcounts_write_refblocks( on_disk_refblock = (void *)((char *) *refcount_table + refblock_index * s->cluster_size); - ret = bdrv_pwrite(bs->file, refblock_offset, on_disk_refblock, - s->cluster_size); + ret = bdrv_co_pwrite(bs->file, refblock_offset, s->cluster_size, + on_disk_refblock, 0); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR writing refblock"); return ret; @@ -2600,11 +2604,10 @@ static int rebuild_refcounts_write_refblocks( * On success, the old refcount structure is leaked (it will be covered by the * new refcount structure). */ -static int rebuild_refcount_structure(BlockDriverState *bs, - BdrvCheckResult *res, - void **refcount_table, - int64_t *nb_clusters, - Error **errp) +static int coroutine_fn GRAPH_RDLOCK +rebuild_refcount_structure(BlockDriverState *bs, BdrvCheckResult *res, + void **refcount_table, int64_t *nb_clusters, + Error **errp) { BDRVQcow2State *s = bs->opaque; int64_t reftable_offset = -1; @@ -2640,7 +2643,7 @@ static int rebuild_refcount_structure(BlockDriverState *bs, * repeat all this until the reftable stops growing. * * (This loop will terminate, because with every cluster the - * reftable grows, it can accomodate a multitude of more refcounts, + * reftable grows, it can accommodate a multitude of more refcounts, * so that at some point this must be able to cover the reftable * and all refblocks describing it.) * @@ -2733,8 +2736,8 @@ static int rebuild_refcount_structure(BlockDriverState *bs, } assert(reftable_length < INT_MAX); - ret = bdrv_pwrite(bs->file, reftable_offset, on_disk_reftable, - reftable_length); + ret = bdrv_co_pwrite(bs->file, reftable_offset, reftable_length, + on_disk_reftable, 0); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR writing reftable"); goto fail; @@ -2744,10 +2747,10 @@ static int rebuild_refcount_structure(BlockDriverState *bs, reftable_offset_and_clusters.reftable_offset = cpu_to_be64(reftable_offset); reftable_offset_and_clusters.reftable_clusters = cpu_to_be32(reftable_clusters); - ret = bdrv_pwrite_sync(bs->file, - offsetof(QCowHeader, refcount_table_offset), - &reftable_offset_and_clusters, - sizeof(reftable_offset_and_clusters)); + ret = bdrv_co_pwrite_sync(bs->file, + offsetof(QCowHeader, refcount_table_offset), + sizeof(reftable_offset_and_clusters), + &reftable_offset_and_clusters, 0); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR setting reftable"); goto fail; @@ -2776,8 +2779,8 @@ fail: * Returns 0 if no errors are found, the number of errors in case the image is * detected as corrupted, and -errno when an internal error occurred. */ -int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, - BdrvCheckMode fix) +int coroutine_fn GRAPH_RDLOCK +qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) { BDRVQcow2State *s = bs->opaque; BdrvCheckResult pre_compare_res; @@ -2786,7 +2789,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, bool rebuild = false; int ret; - size = bdrv_getlength(bs->file->bs); + size = bdrv_co_getlength(bs->file->bs); if (size < 0) { res->check_errors++; return size; @@ -3009,7 +3012,7 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, return -ENOMEM; } - ret = bdrv_pread(bs->file, l1_ofs, l1, l1_sz2); + ret = bdrv_pread(bs->file, l1_ofs, l1_sz2, l1, 0); if (ret < 0) { g_free(l1); return ret; @@ -3098,20 +3101,22 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, * * @allocated should be set to true if a new cluster has been allocated. */ -typedef int (RefblockFinishOp)(BlockDriverState *bs, uint64_t **reftable, - uint64_t reftable_index, uint64_t *reftable_size, - void *refblock, bool refblock_empty, - bool *allocated, Error **errp); +typedef int /* GRAPH_RDLOCK_PTR */ + (RefblockFinishOp)(BlockDriverState *bs, uint64_t **reftable, + uint64_t reftable_index, uint64_t *reftable_size, + void *refblock, bool refblock_empty, + bool *allocated, Error **errp); /** * This "operation" for walk_over_reftable() allocates the refblock on disk (if * it is not empty) and inserts its offset into the new reftable. The size of * this new reftable is increased as required. */ -static int alloc_refblock(BlockDriverState *bs, uint64_t **reftable, - uint64_t reftable_index, uint64_t *reftable_size, - void *refblock, bool refblock_empty, bool *allocated, - Error **errp) +static int GRAPH_RDLOCK +alloc_refblock(BlockDriverState *bs, uint64_t **reftable, + uint64_t reftable_index, uint64_t *reftable_size, + void *refblock, bool refblock_empty, bool *allocated, + Error **errp) { BDRVQcow2State *s = bs->opaque; int64_t offset; @@ -3161,10 +3166,11 @@ static int alloc_refblock(BlockDriverState *bs, uint64_t **reftable, * offset specified by the new reftable's entry. It does not modify the new * reftable or change any refcounts. */ -static int flush_refblock(BlockDriverState *bs, uint64_t **reftable, - uint64_t reftable_index, uint64_t *reftable_size, - void *refblock, bool refblock_empty, bool *allocated, - Error **errp) +static int GRAPH_RDLOCK +flush_refblock(BlockDriverState *bs, uint64_t **reftable, + uint64_t reftable_index, uint64_t *reftable_size, + void *refblock, bool refblock_empty, bool *allocated, + Error **errp) { BDRVQcow2State *s = bs->opaque; int64_t offset; @@ -3180,7 +3186,7 @@ static int flush_refblock(BlockDriverState *bs, uint64_t **reftable, return ret; } - ret = bdrv_pwrite(bs->file, offset, refblock, s->cluster_size); + ret = bdrv_pwrite(bs->file, offset, s->cluster_size, refblock, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write refblock"); return ret; @@ -3205,16 +3211,17 @@ static int flush_refblock(BlockDriverState *bs, uint64_t **reftable, * * @allocated is set to true if a new cluster has been allocated. */ -static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable, - uint64_t *new_reftable_index, - uint64_t *new_reftable_size, - void *new_refblock, int new_refblock_size, - int new_refcount_bits, - RefblockFinishOp *operation, bool *allocated, - Qcow2SetRefcountFunc *new_set_refcount, - BlockDriverAmendStatusCB *status_cb, - void *cb_opaque, int index, int total, - Error **errp) +static int GRAPH_RDLOCK +walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable, + uint64_t *new_reftable_index, + uint64_t *new_reftable_size, + void *new_refblock, int new_refblock_size, + int new_refcount_bits, + RefblockFinishOp *operation, bool *allocated, + Qcow2SetRefcountFunc *new_set_refcount, + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque, int index, int total, + Error **errp) { BDRVQcow2State *s = bs->opaque; uint64_t reftable_index; @@ -3452,8 +3459,9 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, cpu_to_be64s(&new_reftable[i]); } - ret = bdrv_pwrite(bs->file, new_reftable_offset, new_reftable, - new_reftable_size * REFTABLE_ENTRY_SIZE); + ret = bdrv_pwrite(bs->file, new_reftable_offset, + new_reftable_size * REFTABLE_ENTRY_SIZE, new_reftable, + 0); for (i = 0; i < new_reftable_size; i++) { be64_to_cpus(&new_reftable[i]); @@ -3539,7 +3547,8 @@ done: return ret; } -static int64_t get_refblock_offset(BlockDriverState *bs, uint64_t offset) +static int64_t coroutine_fn GRAPH_RDLOCK +get_refblock_offset(BlockDriverState *bs, uint64_t offset) { BDRVQcow2State *s = bs->opaque; uint32_t index = offset_to_reftable_index(s, offset); @@ -3558,8 +3567,8 @@ static int64_t get_refblock_offset(BlockDriverState *bs, uint64_t offset) return covering_refblock_offset; } -static int qcow2_discard_refcount_block(BlockDriverState *bs, - uint64_t discard_block_offs) +static int coroutine_fn GRAPH_RDLOCK +qcow2_discard_refcount_block(BlockDriverState *bs, uint64_t discard_block_offs) { BDRVQcow2State *s = bs->opaque; int64_t refblock_offs; @@ -3615,7 +3624,7 @@ static int qcow2_discard_refcount_block(BlockDriverState *bs, return 0; } -int qcow2_shrink_reftable(BlockDriverState *bs) +int coroutine_fn qcow2_shrink_reftable(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; uint64_t *reftable_tmp = @@ -3656,8 +3665,9 @@ int qcow2_shrink_reftable(BlockDriverState *bs) reftable_tmp[i] = unused_block ? 0 : cpu_to_be64(s->refcount_table[i]); } - ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset, reftable_tmp, - s->refcount_table_size * REFTABLE_ENTRY_SIZE); + ret = bdrv_co_pwrite_sync(bs->file, s->refcount_table_offset, + s->refcount_table_size * REFTABLE_ENTRY_SIZE, + reftable_tmp, 0); /* * If the write in the reftable failed the image may contain a partially * overwritten reftable. In this case it would be better to clear the @@ -3682,7 +3692,7 @@ out: return ret; } -int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size) +int64_t coroutine_fn qcow2_get_last_cluster(BlockDriverState *bs, int64_t size) { BDRVQcow2State *s = bs->opaque; int64_t i; @@ -3704,7 +3714,8 @@ int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size) return -EIO; } -int qcow2_detect_metadata_preallocation(BlockDriverState *bs) +int coroutine_fn GRAPH_RDLOCK +qcow2_detect_metadata_preallocation(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; int64_t i, end_cluster, cluster_count = 0, threshold; @@ -3712,12 +3723,12 @@ int qcow2_detect_metadata_preallocation(BlockDriverState *bs) qemu_co_mutex_assert_locked(&s->lock); - file_length = bdrv_getlength(bs->file->bs); + file_length = bdrv_co_getlength(bs->file->bs); if (file_length < 0) { return file_length; } - real_allocation = bdrv_get_allocated_file_size(bs->file->bs); + real_allocation = bdrv_co_get_allocated_file_size(bs->file->bs); if (real_allocation < 0) { return real_allocation; } diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 075269a023..92e47978bf 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -77,10 +77,11 @@ void qcow2_free_snapshots(BlockDriverState *bs) * qcow2_check_refcounts() does not do anything with snapshots' * extra data.) */ -static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, - int *nb_clusters_reduced, - int *extra_data_dropped, - Error **errp) +static coroutine_fn GRAPH_RDLOCK +int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, + int *nb_clusters_reduced, + int *extra_data_dropped, + Error **errp) { BDRVQcow2State *s = bs->opaque; QCowSnapshotHeader h; @@ -108,7 +109,7 @@ static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, /* Read statically sized part of the snapshot header */ offset = ROUND_UP(offset, 8); - ret = bdrv_pread(bs->file, offset, &h, sizeof(h)); + ret = bdrv_co_pread(bs->file, offset, sizeof(h), &h, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read snapshot table"); goto fail; @@ -146,8 +147,8 @@ static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, } /* Read known extra data */ - ret = bdrv_pread(bs->file, offset, &extra, - MIN(sizeof(extra), sn->extra_data_size)); + ret = bdrv_co_pread(bs->file, offset, + MIN(sizeof(extra), sn->extra_data_size), &extra, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read snapshot table"); goto fail; @@ -184,8 +185,8 @@ static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, /* Store unknown extra data */ unknown_extra_data_size = sn->extra_data_size - sizeof(extra); sn->unknown_extra_data = g_malloc(unknown_extra_data_size); - ret = bdrv_pread(bs->file, offset, sn->unknown_extra_data, - unknown_extra_data_size); + ret = bdrv_co_pread(bs->file, offset, unknown_extra_data_size, + sn->unknown_extra_data, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read snapshot table"); @@ -196,7 +197,7 @@ static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, /* Read snapshot ID */ sn->id_str = g_malloc(id_str_size + 1); - ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size); + ret = bdrv_co_pread(bs->file, offset, id_str_size, sn->id_str, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read snapshot table"); goto fail; @@ -206,7 +207,7 @@ static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, /* Read snapshot name */ sn->name = g_malloc(name_size + 1); - ret = bdrv_pread(bs->file, offset, sn->name, name_size); + ret = bdrv_co_pread(bs->file, offset, name_size, sn->name, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read snapshot table"); goto fail; @@ -261,7 +262,7 @@ fail: return ret; } -int qcow2_read_snapshots(BlockDriverState *bs, Error **errp) +int coroutine_fn qcow2_read_snapshots(BlockDriverState *bs, Error **errp) { return qcow2_do_read_snapshots(bs, false, NULL, NULL, errp); } @@ -349,13 +350,13 @@ int qcow2_write_snapshots(BlockDriverState *bs) h.name_size = cpu_to_be16(name_size); offset = ROUND_UP(offset, 8); - ret = bdrv_pwrite(bs->file, offset, &h, sizeof(h)); + ret = bdrv_pwrite(bs->file, offset, sizeof(h), &h, 0); if (ret < 0) { goto fail; } offset += sizeof(h); - ret = bdrv_pwrite(bs->file, offset, &extra, sizeof(extra)); + ret = bdrv_pwrite(bs->file, offset, sizeof(extra), &extra, 0); if (ret < 0) { goto fail; } @@ -369,21 +370,21 @@ int qcow2_write_snapshots(BlockDriverState *bs) assert(unknown_extra_data_size <= BDRV_REQUEST_MAX_BYTES); assert(sn->unknown_extra_data); - ret = bdrv_pwrite(bs->file, offset, sn->unknown_extra_data, - unknown_extra_data_size); + ret = bdrv_pwrite(bs->file, offset, unknown_extra_data_size, + sn->unknown_extra_data, 0); if (ret < 0) { goto fail; } offset += unknown_extra_data_size; } - ret = bdrv_pwrite(bs->file, offset, sn->id_str, id_str_size); + ret = bdrv_pwrite(bs->file, offset, id_str_size, sn->id_str, 0); if (ret < 0) { goto fail; } offset += id_str_size; - ret = bdrv_pwrite(bs->file, offset, sn->name, name_size); + ret = bdrv_pwrite(bs->file, offset, name_size, sn->name, 0); if (ret < 0) { goto fail; } @@ -406,7 +407,7 @@ int qcow2_write_snapshots(BlockDriverState *bs) header_data.snapshots_offset = cpu_to_be64(snapshots_offset); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), - &header_data, sizeof(header_data)); + sizeof(header_data), &header_data, 0); if (ret < 0) { goto fail; } @@ -441,8 +442,9 @@ int coroutine_fn qcow2_check_read_snapshot_table(BlockDriverState *bs, } QEMU_PACKED snapshot_table_pointer; /* qcow2_do_open() discards this information in check mode */ - ret = bdrv_pread(bs->file, offsetof(QCowHeader, nb_snapshots), - &snapshot_table_pointer, sizeof(snapshot_table_pointer)); + ret = bdrv_co_pread(bs->file, offsetof(QCowHeader, nb_snapshots), + sizeof(snapshot_table_pointer), &snapshot_table_pointer, + 0); if (ret < 0) { result->check_errors++; fprintf(stderr, "ERROR failed to read the snapshot table pointer from " @@ -511,9 +513,9 @@ int coroutine_fn qcow2_check_read_snapshot_table(BlockDriverState *bs, assert(fix & BDRV_FIX_ERRORS); snapshot_table_pointer.nb_snapshots = cpu_to_be32(s->nb_snapshots); - ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), - &snapshot_table_pointer.nb_snapshots, - sizeof(snapshot_table_pointer.nb_snapshots)); + ret = bdrv_co_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), + sizeof(snapshot_table_pointer.nb_snapshots), + &snapshot_table_pointer.nb_snapshots, 0); if (ret < 0) { result->check_errors++; fprintf(stderr, "ERROR failed to update the snapshot count in the " @@ -693,8 +695,8 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) goto fail; } - ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table, - s->l1_size * L1E_SIZE); + ret = bdrv_pwrite(bs->file, sn->l1_table_offset, s->l1_size * L1E_SIZE, + l1_table, 0); if (ret < 0) { goto fail; } @@ -829,8 +831,8 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) goto fail; } - ret = bdrv_pread(bs->file, sn->l1_table_offset, - sn_l1_table, sn_l1_bytes); + ret = bdrv_pread(bs->file, sn->l1_table_offset, sn_l1_bytes, sn_l1_table, + 0); if (ret < 0) { goto fail; } @@ -848,8 +850,8 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) goto fail; } - ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, sn_l1_table, - cur_l1_bytes); + ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, cur_l1_bytes, + sn_l1_table, 0); if (ret < 0) { goto fail; } @@ -1051,8 +1053,8 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, return -ENOMEM; } - ret = bdrv_pread(bs->file, sn->l1_table_offset, - new_l1_table, new_l1_bytes); + ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_bytes, + new_l1_table, 0); if (ret < 0) { error_setg(errp, "Failed to read l1 table for snapshot"); qemu_vfree(new_l1_table); diff --git a/block/qcow2-threads.c b/block/qcow2-threads.c index 1914baf456..d6071a1eae 100644 --- a/block/qcow2-threads.c +++ b/block/qcow2-threads.c @@ -34,6 +34,7 @@ #endif #include "qcow2.h" +#include "block/block-io.h" #include "block/thread-pool.h" #include "crypto.h" @@ -42,7 +43,6 @@ qcow2_co_process(BlockDriverState *bs, ThreadPoolFunc *func, void *arg) { int ret; BDRVQcow2State *s = bs->opaque; - ThreadPool *pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); qemu_co_mutex_lock(&s->lock); while (s->nb_threads >= QCOW2_MAX_THREADS) { @@ -51,7 +51,7 @@ qcow2_co_process(BlockDriverState *bs, ThreadPoolFunc *func, void *arg) s->nb_threads++; qemu_co_mutex_unlock(&s->lock); - ret = thread_pool_submit_co(pool, func, arg); + ret = thread_pool_submit_co(func, arg); qemu_co_mutex_lock(&s->lock); s->nb_threads--; diff --git a/block/qcow2.c b/block/qcow2.c index 4f5e6440fb..956128b409 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -43,6 +43,7 @@ #include "qapi/qapi-visit-block-core.h" #include "crypto.h" #include "block/aio_task.h" +#include "block/dirty-bitmap.h" /* Differences with QCOW: @@ -94,9 +95,10 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) } -static ssize_t qcow2_crypto_hdr_read_func(QCryptoBlock *block, size_t offset, - uint8_t *buf, size_t buflen, - void *opaque, Error **errp) +static int GRAPH_RDLOCK +qcow2_crypto_hdr_read_func(QCryptoBlock *block, size_t offset, + uint8_t *buf, size_t buflen, + void *opaque, Error **errp) { BlockDriverState *bs = opaque; BDRVQcow2State *s = bs->opaque; @@ -107,18 +109,19 @@ static ssize_t qcow2_crypto_hdr_read_func(QCryptoBlock *block, size_t offset, return -1; } - ret = bdrv_pread(bs->file, - s->crypto_header.offset + offset, buf, buflen); + ret = bdrv_pread(bs->file, s->crypto_header.offset + offset, buflen, buf, + 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read encryption header"); return -1; } - return ret; + return 0; } -static ssize_t qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, - void *opaque, Error **errp) +static int coroutine_fn GRAPH_RDLOCK +qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, void *opaque, + Error **errp) { BlockDriverState *bs = opaque; BDRVQcow2State *s = bs->opaque; @@ -143,21 +146,21 @@ static ssize_t qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, */ 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, - clusterlen, 0); + ret = bdrv_co_pwrite_zeroes(bs->file, ret, clusterlen, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not zero fill encryption header"); return -1; } - return ret; + return 0; } -static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset, - const uint8_t *buf, size_t buflen, - void *opaque, Error **errp) +/* The graph lock must be held when called in coroutine context */ +static int coroutine_mixed_fn GRAPH_RDLOCK +qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset, + const uint8_t *buf, size_t buflen, + void *opaque, Error **errp) { BlockDriverState *bs = opaque; BDRVQcow2State *s = bs->opaque; @@ -168,13 +171,13 @@ static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset, return -1; } - ret = bdrv_pwrite(bs->file, - s->crypto_header.offset + offset, buf, buflen); + ret = bdrv_pwrite(bs->file, s->crypto_header.offset + offset, buflen, buf, + 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read encryption header"); return -1; } - return ret; + return 0; } static QDict* @@ -198,10 +201,10 @@ qcow2_extract_crypto_opts(QemuOpts *opts, const char *fmt, Error **errp) * unknown magic is skipped (future extension this version knows nothing about) * return 0 upon success, non-0 otherwise */ -static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, - uint64_t end_offset, void **p_feature_table, - int flags, bool *need_update_header, - Error **errp) +static int coroutine_fn GRAPH_RDLOCK +qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, + uint64_t end_offset, void **p_feature_table, + int flags, bool *need_update_header, Error **errp) { BDRVQcow2State *s = bs->opaque; QCowExtension ext; @@ -227,7 +230,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, printf("attempting to read extended header in offset %lu\n", offset); #endif - ret = bdrv_pread(bs->file, offset, &ext, sizeof(ext)); + ret = bdrv_co_pread(bs->file, offset, sizeof(ext), &ext, 0); if (ret < 0) { error_setg_errno(errp, -ret, "qcow2_read_extension: ERROR: " "pread fail from offset %" PRIu64, offset); @@ -255,7 +258,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, sizeof(bs->backing_format)); return 2; } - ret = bdrv_pread(bs->file, offset, bs->backing_format, ext.len); + ret = bdrv_co_pread(bs->file, offset, ext.len, bs->backing_format, 0); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: ext_backing_format: " "Could not read format name"); @@ -271,10 +274,11 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, case QCOW2_EXT_MAGIC_FEATURE_TABLE: if (p_feature_table != NULL) { void *feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); - ret = bdrv_pread(bs->file, offset , feature_table, ext.len); + ret = bdrv_co_pread(bs->file, offset, ext.len, feature_table, 0); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: ext_feature_table: " "Could not read table"); + g_free(feature_table); return ret; } @@ -296,7 +300,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, return -EINVAL; } - ret = bdrv_pread(bs->file, offset, &s->crypto_header, ext.len); + ret = bdrv_co_pread(bs->file, offset, ext.len, &s->crypto_header, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Unable to read CRYPTO header extension"); @@ -352,7 +356,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, break; } - ret = bdrv_pread(bs->file, offset, &bitmaps_ext, ext.len); + ret = bdrv_co_pread(bs->file, offset, ext.len, &bitmaps_ext, 0); if (ret < 0) { error_setg_errno(errp, -ret, "bitmaps_ext: " "Could not read ext header"); @@ -416,7 +420,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, case QCOW2_EXT_MAGIC_DATA_FILE: { s->image_data_file = g_malloc0(ext.len + 1); - ret = bdrv_pread(bs->file, offset, s->image_data_file, ext.len); + ret = bdrv_co_pread(bs->file, offset, ext.len, s->image_data_file, 0); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: Could not read data file name"); @@ -440,7 +444,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, uext->len = ext.len; QLIST_INSERT_HEAD(&s->unknown_header_ext, uext, next); - ret = bdrv_pread(bs->file, offset , uext->data, uext->len); + ret = bdrv_co_pread(bs->file, offset, uext->len, uext->data, 0); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: unknown extension: " "Could not read data"); @@ -516,12 +520,9 @@ int qcow2_mark_dirty(BlockDriverState *bs) } val = cpu_to_be64(s->incompatible_features | QCOW2_INCOMPAT_DIRTY); - ret = bdrv_pwrite(bs->file, offsetof(QCowHeader, incompatible_features), - &val, sizeof(val)); - if (ret < 0) { - return ret; - } - ret = bdrv_flush(bs->file->bs); + ret = bdrv_pwrite_sync(bs->file, + offsetof(QCowHeader, incompatible_features), + sizeof(val), &val, 0); if (ret < 0) { return ret; } @@ -536,7 +537,7 @@ int qcow2_mark_dirty(BlockDriverState *bs) * function when there are no pending requests, it does not guard against * concurrent requests dirtying the image. */ -static int qcow2_mark_clean(BlockDriverState *bs) +static int GRAPH_RDLOCK qcow2_mark_clean(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; @@ -570,7 +571,8 @@ int qcow2_mark_corrupt(BlockDriverState *bs) * Marks the image as consistent, i.e., unsets the corrupt bit, and flushes * before if necessary. */ -int qcow2_mark_consistent(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK +qcow2_mark_consistent(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; @@ -602,9 +604,9 @@ static void qcow2_add_check_result(BdrvCheckResult *out, } } -static int coroutine_fn qcow2_co_check_locked(BlockDriverState *bs, - BdrvCheckResult *result, - BdrvCheckMode fix) +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_check_locked(BlockDriverState *bs, BdrvCheckResult *result, + BdrvCheckMode fix) { BdrvCheckResult snapshot_res = {}; BdrvCheckResult refcount_res = {}; @@ -641,9 +643,9 @@ static int coroutine_fn qcow2_co_check_locked(BlockDriverState *bs, return ret; } -static int coroutine_fn qcow2_co_check(BlockDriverState *bs, - BdrvCheckResult *result, - BdrvCheckMode fix) +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_check(BlockDriverState *bs, BdrvCheckResult *result, + BdrvCheckMode fix) { BDRVQcow2State *s = bs->opaque; int ret; @@ -682,6 +684,7 @@ static const char *const mutable_opts[] = { QCOW2_OPT_DISCARD_REQUEST, QCOW2_OPT_DISCARD_SNAPSHOT, QCOW2_OPT_DISCARD_OTHER, + QCOW2_OPT_DISCARD_NO_UNREF, QCOW2_OPT_OVERLAP, QCOW2_OPT_OVERLAP_TEMPLATE, QCOW2_OPT_OVERLAP_MAIN_HEADER, @@ -726,6 +729,11 @@ static QemuOptsList qcow2_runtime_opts = { .type = QEMU_OPT_BOOL, .help = "Generate discard requests when other clusters are freed", }, + { + .name = QCOW2_OPT_DISCARD_NO_UNREF, + .type = QEMU_OPT_BOOL, + .help = "Do not unreference discarded clusters", + }, { .name = QCOW2_OPT_OVERLAP, .type = QEMU_OPT_STRING, @@ -969,14 +977,14 @@ typedef struct Qcow2ReopenState { bool use_lazy_refcounts; int overlap_check; bool discard_passthrough[QCOW2_DISCARD_MAX]; + bool discard_no_unref; uint64_t cache_clean_interval; QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ } Qcow2ReopenState; -static int qcow2_update_options_prepare(BlockDriverState *bs, - Qcow2ReopenState *r, - QDict *options, int flags, - Error **errp) +static int GRAPH_RDLOCK +qcow2_update_options_prepare(BlockDriverState *bs, Qcow2ReopenState *r, + QDict *options, int flags, Error **errp) { BDRVQcow2State *s = bs->opaque; QemuOpts *opts = NULL; @@ -1140,6 +1148,15 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, r->discard_passthrough[QCOW2_DISCARD_OTHER] = qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false); + r->discard_no_unref = qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_NO_UNREF, + false); + if (r->discard_no_unref && s->qcow_version < 3) { + error_setg(errp, + "discard-no-unref is only supported since qcow2 version 3"); + ret = -EINVAL; + goto fail; + } + switch (s->crypt_method_header) { case QCOW_CRYPT_NONE: if (encryptfmt) { @@ -1220,6 +1237,8 @@ static void qcow2_update_options_commit(BlockDriverState *bs, s->discard_passthrough[i] = r->discard_passthrough[i]; } + s->discard_no_unref = r->discard_no_unref; + if (s->cache_clean_interval != r->cache_clean_interval) { cache_clean_timer_del(bs); s->cache_clean_interval = r->cache_clean_interval; @@ -1242,8 +1261,9 @@ static void qcow2_update_options_abort(BlockDriverState *bs, qapi_free_QCryptoBlockOpenOptions(r->crypto_opts); } -static int qcow2_update_options(BlockDriverState *bs, QDict *options, - int flags, Error **errp) +static int coroutine_fn GRAPH_RDLOCK +qcow2_update_options(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { Qcow2ReopenState r = {}; int ret; @@ -1295,9 +1315,9 @@ static int validate_compression_type(BDRVQcow2State *s, Error **errp) } /* Called with s->lock held. */ -static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, - int flags, bool open_data_file, - Error **errp) +static int coroutine_fn GRAPH_RDLOCK +qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, + bool open_data_file, Error **errp) { ERRP_GUARD(); BDRVQcow2State *s = bs->opaque; @@ -1308,7 +1328,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, uint64_t l1_vm_state_index; bool update_header = false; - ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); + ret = bdrv_co_pread(bs->file, 0, sizeof(header), &header, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read qcow2 header"); goto fail; @@ -1384,8 +1404,9 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, if (header.header_length > sizeof(header)) { s->unknown_header_fields_size = header.header_length - sizeof(header); s->unknown_header_fields = g_malloc(s->unknown_header_fields_size); - ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields, - s->unknown_header_fields_size); + ret = bdrv_co_pread(bs->file, sizeof(header), + s->unknown_header_fields_size, + s->unknown_header_fields, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read unknown qcow2 header " "fields"); @@ -1580,8 +1601,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, ret = -ENOMEM; goto fail; } - ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, - s->l1_size * L1E_SIZE); + ret = bdrv_co_pread(bs->file, s->l1_table_offset, s->l1_size * L1E_SIZE, + s->l1_table, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read L1 table"); goto fail; @@ -1617,9 +1638,11 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, if (open_data_file) { /* Open external data file */ - s->data_file = bdrv_open_child(NULL, options, "data-file", bs, - &child_of_bds, BDRV_CHILD_DATA, - true, errp); + bdrv_graph_co_rdunlock(); + s->data_file = bdrv_co_open_child(NULL, options, "data-file", bs, + &child_of_bds, BDRV_CHILD_DATA, + true, errp); + bdrv_graph_co_rdlock(); if (*errp) { ret = -EINVAL; goto fail; @@ -1627,9 +1650,12 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) { if (!s->data_file && s->image_data_file) { - s->data_file = bdrv_open_child(s->image_data_file, options, - "data-file", bs, &child_of_bds, - BDRV_CHILD_DATA, false, errp); + bdrv_graph_co_rdunlock(); + s->data_file = bdrv_co_open_child(s->image_data_file, options, + "data-file", bs, + &child_of_bds, + BDRV_CHILD_DATA, false, errp); + bdrv_graph_co_rdlock(); if (!s->data_file) { ret = -EINVAL; goto fail; @@ -1698,16 +1724,27 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, ret = -EINVAL; goto fail; } - ret = bdrv_pread(bs->file, header.backing_file_offset, - bs->auto_backing_file, len); + + s->image_backing_file = g_malloc(len + 1); + ret = bdrv_co_pread(bs->file, header.backing_file_offset, len, + s->image_backing_file, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read backing file name"); goto fail; } - bs->auto_backing_file[len] = '\0'; - pstrcpy(bs->backing_file, sizeof(bs->backing_file), - bs->auto_backing_file); - s->image_backing_file = g_strdup(bs->auto_backing_file); + s->image_backing_file[len] = '\0'; + + /* + * Update only when something has changed. This function is called by + * qcow2_co_invalidate_cache(), and we do not want to reset + * auto_backing_file unless necessary. + */ + if (!g_str_equal(s->image_backing_file, bs->backing_file)) { + pstrcpy(bs->backing_file, sizeof(bs->backing_file), + s->image_backing_file); + pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file), + s->image_backing_file); + } } /* @@ -1843,7 +1880,9 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, fail: g_free(s->image_data_file); if (open_data_file && has_data_file(bs)) { - bdrv_unref_child(bs, s->data_file); + bdrv_graph_co_rdunlock(); + bdrv_co_unref_child(bs, s->data_file); + bdrv_graph_co_rdlock(); s->data_file = NULL; } g_free(s->unknown_header_fields); @@ -1878,10 +1917,14 @@ static void coroutine_fn qcow2_open_entry(void *opaque) QCow2OpenCo *qoc = opaque; BDRVQcow2State *s = qoc->bs->opaque; + GRAPH_RDLOCK_GUARD(); + qemu_co_mutex_lock(&s->lock); qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, true, qoc->errp); qemu_co_mutex_unlock(&s->lock); + + aio_wait_kick(); } static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, @@ -1895,24 +1938,23 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, .errp = errp, .ret = -EINPROGRESS }; + int ret; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_IMAGE, false, errp); - if (!bs->file) { - return -EINVAL; + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; } /* Initialise locks */ qemu_co_mutex_init(&s->lock); - if (qemu_in_coroutine()) { - /* From bdrv_co_create. */ - qcow2_open_entry(&qoc); - } else { - assert(qemu_get_current_aio_context() == qemu_get_aio_context()); - qemu_coroutine_enter(qemu_coroutine_create(qcow2_open_entry, &qoc)); - BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS); - } + assert(!qemu_in_coroutine()); + assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + + aio_co_enter(bdrv_get_aio_context(bs), + qemu_coroutine_create(qcow2_open_entry, &qoc)); + AIO_WAIT_WHILE_UNLOCKED(NULL, qoc.ret == -EINPROGRESS); + return qoc.ret; } @@ -1928,13 +1970,17 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.pdiscard_alignment = s->cluster_size; } -static int qcow2_reopen_prepare(BDRVReopenState *state, - BlockReopenQueue *queue, Error **errp) +static int GRAPH_UNLOCKED +qcow2_reopen_prepare(BDRVReopenState *state,BlockReopenQueue *queue, + Error **errp) { BDRVQcow2State *s = state->bs->opaque; Qcow2ReopenState *r; int ret; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + r = g_new0(Qcow2ReopenState, 1); state->opaque = r; @@ -1984,6 +2030,8 @@ static void qcow2_reopen_commit(BDRVReopenState *state) { BDRVQcow2State *s = state->bs->opaque; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + qcow2_update_options_commit(state->bs, state->opaque); if (!s->data_file) { /* @@ -1997,6 +2045,8 @@ static void qcow2_reopen_commit(BDRVReopenState *state) static void qcow2_reopen_commit_post(BDRVReopenState *state) { + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (state->flags & BDRV_O_RDWR) { Error *local_err = NULL; @@ -2017,6 +2067,8 @@ static void qcow2_reopen_abort(BDRVReopenState *state) { BDRVQcow2State *s = state->bs->opaque; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!s->data_file) { /* * If we don't have an external data file, s->data_file was cleared by @@ -2074,11 +2126,10 @@ static void qcow2_join_options(QDict *options, QDict *old_options) } } -static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, - bool want_zero, - int64_t offset, int64_t count, - int64_t *pnum, int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, + int64_t count, int64_t *pnum, int64_t *map, + BlockDriverState **file) { BDRVQcow2State *s = bs->opaque; uint64_t host_offset; @@ -2122,12 +2173,14 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, { status |= BDRV_BLOCK_RECURSE; } + if (type == QCOW2_SUBCLUSTER_COMPRESSED) { + status |= BDRV_BLOCK_COMPRESSED; + } return status; } -static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs, - QCowL2Meta **pl2meta, - bool link_l2) +static int coroutine_fn GRAPH_RDLOCK +qcow2_handle_l2meta(BlockDriverState *bs, QCowL2Meta **pl2meta, bool link_l2) { int ret = 0; QCowL2Meta *l2meta = *pl2meta; @@ -2158,7 +2211,7 @@ out: return ret; } -static coroutine_fn int +static int coroutine_fn GRAPH_RDLOCK qcow2_co_preadv_encrypted(BlockDriverState *bs, uint64_t host_offset, uint64_t offset, @@ -2186,7 +2239,7 @@ qcow2_co_preadv_encrypted(BlockDriverState *bs, return -ENOMEM; } - BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); + BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_AIO); ret = bdrv_co_pread(s->data_file, host_offset, bytes, buf, 0); if (ret < 0) { goto fail; @@ -2259,12 +2312,10 @@ static coroutine_fn int qcow2_add_task(BlockDriverState *bs, return 0; } -static coroutine_fn int qcow2_co_preadv_task(BlockDriverState *bs, - QCow2SubclusterType subc_type, - uint64_t host_offset, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, - size_t qiov_offset) +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_preadv_task(BlockDriverState *bs, QCow2SubclusterType subc_type, + uint64_t host_offset, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset) { BDRVQcow2State *s = bs->opaque; @@ -2278,7 +2329,7 @@ static coroutine_fn int qcow2_co_preadv_task(BlockDriverState *bs, case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: assert(bs->backing); /* otherwise handled in qcow2_co_preadv_part */ - BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); + BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); return bdrv_co_preadv_part(bs->backing, offset, bytes, qiov, qiov_offset, 0); @@ -2292,7 +2343,7 @@ static coroutine_fn int qcow2_co_preadv_task(BlockDriverState *bs, offset, bytes, qiov, qiov_offset); } - BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); + BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_AIO); return bdrv_co_preadv_part(s->data_file, host_offset, bytes, qiov, qiov_offset, 0); @@ -2303,7 +2354,11 @@ static coroutine_fn int qcow2_co_preadv_task(BlockDriverState *bs, g_assert_not_reached(); } -static coroutine_fn int qcow2_co_preadv_task_entry(AioTask *task) +/* + * This function can count as GRAPH_RDLOCK because qcow2_co_preadv_part() holds + * the graph lock and keeps it until this coroutine has terminated. + */ +static int coroutine_fn GRAPH_RDLOCK qcow2_co_preadv_task_entry(AioTask *task) { Qcow2AioTask *t = container_of(task, Qcow2AioTask, task); @@ -2314,11 +2369,10 @@ static coroutine_fn int qcow2_co_preadv_task_entry(AioTask *task) t->qiov, t->qiov_offset); } -static coroutine_fn int qcow2_co_preadv_part(BlockDriverState *bs, - int64_t offset, int64_t bytes, - QEMUIOVector *qiov, - size_t qiov_offset, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_preadv_part(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) { BDRVQcow2State *s = bs->opaque; int ret = 0; @@ -2438,7 +2492,8 @@ static bool merge_cow(uint64_t offset, unsigned bytes, * Return 1 if the COW regions read as zeroes, 0 if not, < 0 on error. * Note that returning 0 does not guarantee non-zero data. */ -static int is_zero_cow(BlockDriverState *bs, QCowL2Meta *m) +static int coroutine_fn GRAPH_RDLOCK +is_zero_cow(BlockDriverState *bs, QCowL2Meta *m) { /* * This check is designed for optimization shortcut so it must be @@ -2456,7 +2511,8 @@ static int is_zero_cow(BlockDriverState *bs, QCowL2Meta *m) m->cow_end.nb_bytes); } -static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) +static int coroutine_fn GRAPH_RDLOCK +handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) { BDRVQcow2State *s = bs->opaque; QCowL2Meta *m; @@ -2497,7 +2553,7 @@ static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) return ret; } - BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_SPACE); + BLKDBG_CO_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_SPACE); ret = bdrv_co_pwrite_zeroes(s->data_file, start_offset, nb_bytes, BDRV_REQ_NO_FALLBACK); if (ret < 0) { @@ -2519,12 +2575,10 @@ static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) * l2meta - if not NULL, qcow2_co_pwritev_task() will consume it. Caller must * not use it somehow after qcow2_co_pwritev_task() call */ -static coroutine_fn int qcow2_co_pwritev_task(BlockDriverState *bs, - uint64_t host_offset, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, - uint64_t qiov_offset, - QCowL2Meta *l2meta) +static coroutine_fn GRAPH_RDLOCK +int qcow2_co_pwritev_task(BlockDriverState *bs, uint64_t host_offset, + uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, + uint64_t qiov_offset, QCowL2Meta *l2meta) { int ret; BDRVQcow2State *s = bs->opaque; @@ -2564,7 +2618,7 @@ static coroutine_fn int qcow2_co_pwritev_task(BlockDriverState *bs, * guest data now. */ if (!merge_cow(offset, bytes, qiov, qiov_offset, l2meta)) { - BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); + BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); trace_qcow2_writev_data(qemu_coroutine_self(), host_offset); ret = bdrv_co_pwritev_part(s->data_file, host_offset, bytes, qiov, qiov_offset, 0); @@ -2590,7 +2644,11 @@ out_locked: return ret; } -static coroutine_fn int qcow2_co_pwritev_task_entry(AioTask *task) +/* + * This function can count as GRAPH_RDLOCK because qcow2_co_pwritev_part() holds + * the graph lock and keeps it until this coroutine has terminated. + */ +static coroutine_fn GRAPH_RDLOCK int qcow2_co_pwritev_task_entry(AioTask *task) { Qcow2AioTask *t = container_of(task, Qcow2AioTask, task); @@ -2601,9 +2659,10 @@ static coroutine_fn int qcow2_co_pwritev_task_entry(AioTask *task) t->l2meta); } -static coroutine_fn int qcow2_co_pwritev_part( - BlockDriverState *bs, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) { BDRVQcow2State *s = bs->opaque; int offset_in_cluster; @@ -2683,7 +2742,7 @@ fail_nometa: return ret; } -static int qcow2_inactivate(BlockDriverState *bs) +static int GRAPH_RDLOCK qcow2_inactivate(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; int ret, result = 0; @@ -2718,7 +2777,8 @@ static int qcow2_inactivate(BlockDriverState *bs) return result; } -static void qcow2_do_close(BlockDriverState *bs, bool close_data_file) +static void coroutine_mixed_fn GRAPH_RDLOCK +qcow2_do_close(BlockDriverState *bs, bool close_data_file) { BDRVQcow2State *s = bs->opaque; qemu_vfree(s->l1_table); @@ -2745,21 +2805,29 @@ static void qcow2_do_close(BlockDriverState *bs, bool close_data_file) g_free(s->image_backing_format); if (close_data_file && has_data_file(bs)) { + GLOBAL_STATE_CODE(); + bdrv_graph_rdunlock_main_loop(); + bdrv_graph_wrlock(); bdrv_unref_child(bs, s->data_file); + bdrv_graph_wrunlock(); s->data_file = NULL; + bdrv_graph_rdlock_main_loop(); } qcow2_refcount_close(bs); qcow2_free_snapshots(bs); } -static void qcow2_close(BlockDriverState *bs) +static void GRAPH_UNLOCKED qcow2_close(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + qcow2_do_close(bs, true); } -static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, - Error **errp) +static void coroutine_fn GRAPH_RDLOCK +qcow2_co_invalidate_cache(BlockDriverState *bs, Error **errp) { ERRP_GUARD(); BDRVQcow2State *s = bs->opaque; @@ -3081,7 +3149,7 @@ int qcow2_update_header(BlockDriverState *bs) } /* Write the new header */ - ret = bdrv_pwrite(bs->file, 0, header, s->cluster_size); + ret = bdrv_pwrite(bs->file, 0, s->cluster_size, header, 0); if (ret < 0) { goto fail; } @@ -3092,8 +3160,9 @@ fail: return ret; } -static int qcow2_change_backing_file(BlockDriverState *bs, - const char *backing_file, const char *backing_fmt) +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_change_backing_file(BlockDriverState *bs, const char *backing_file, + const char *backing_fmt) { BDRVQcow2State *s = bs->opaque; @@ -3121,9 +3190,10 @@ static int qcow2_change_backing_file(BlockDriverState *bs, return qcow2_update_header(bs); } -static int qcow2_set_up_encryption(BlockDriverState *bs, - QCryptoBlockCreateOptions *cryptoopts, - Error **errp) +static int coroutine_fn GRAPH_RDLOCK +qcow2_set_up_encryption(BlockDriverState *bs, + QCryptoBlockCreateOptions *cryptoopts, + Error **errp) { BDRVQcow2State *s = bs->opaque; QCryptoBlock *crypto = NULL; @@ -3146,7 +3216,7 @@ static int qcow2_set_up_encryption(BlockDriverState *bs, crypto = qcrypto_block_create(cryptoopts, "encrypt.", qcow2_crypto_hdr_init_func, qcow2_crypto_hdr_write_func, - bs, errp); + bs, 0, errp); if (!crypto) { return -EINVAL; } @@ -3170,9 +3240,9 @@ static int qcow2_set_up_encryption(BlockDriverState *bs, * * Returns: 0 on success, -errno on failure. */ -static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, - uint64_t new_length, PreallocMode mode, - Error **errp) +static int coroutine_fn GRAPH_RDLOCK +preallocate_co(BlockDriverState *bs, uint64_t offset, uint64_t new_length, + PreallocMode mode, Error **errp) { BDRVQcow2State *s = bs->opaque; uint64_t bytes; @@ -3215,7 +3285,7 @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, * all of the allocated clusters (otherwise we get failing reads after * EOF). Extend the image to the last allocated sector. */ - file_length = bdrv_getlength(s->data_file->bs); + file_length = bdrv_co_getlength(s->data_file->bs); if (file_length < 0) { error_setg_errno(errp, -file_length, "Could not get file size"); ret = file_length; @@ -3410,9 +3480,10 @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version, return refcount_bits; } -static int coroutine_fn +static int coroutine_fn GRAPH_UNLOCKED qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) { + ERRP_GUARD(); BlockdevCreateOptionsQcow2 *qcow2_opts; QDict *options; @@ -3442,7 +3513,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) assert(create_options->driver == BLOCKDEV_DRIVER_QCOW2); qcow2_opts = &create_options->u.qcow2; - bs = bdrv_open_blockdev_ref(qcow2_opts->file, errp); + bs = bdrv_co_open_blockdev_ref(qcow2_opts->file, errp); if (bs == NULL) { return -EIO; } @@ -3497,7 +3568,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) if (!qcow2_opts->has_preallocation) { qcow2_opts->preallocation = PREALLOC_MODE_OFF; } - if (qcow2_opts->has_backing_file && + if (qcow2_opts->backing_file && qcow2_opts->preallocation != PREALLOC_MODE_OFF && !qcow2_opts->extended_l2) { @@ -3506,7 +3577,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) ret = -EINVAL; goto out; } - if (qcow2_opts->has_backing_fmt && !qcow2_opts->has_backing_file) { + if (qcow2_opts->has_backing_fmt && !qcow2_opts->backing_file) { error_setg(errp, "Backing format cannot be used without backing file"); ret = -EINVAL; goto out; @@ -3547,7 +3618,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) ret = -EINVAL; goto out; } - if (qcow2_opts->data_file_raw && qcow2_opts->has_backing_file) { + if (qcow2_opts->data_file_raw && qcow2_opts->backing_file) { error_setg(errp, "Backing file and data-file-raw cannot be used at " "the same time"); ret = -EINVAL; @@ -3573,7 +3644,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) * backing file when specifying data_file_raw is an error * anyway. */ - assert(!qcow2_opts->has_backing_file); + assert(!qcow2_opts->backing_file); } if (qcow2_opts->data_file) { @@ -3584,7 +3655,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) ret = -EINVAL; goto out; } - data_bs = bdrv_open_blockdev_ref(qcow2_opts->data_file, errp); + data_bs = bdrv_co_open_blockdev_ref(qcow2_opts->data_file, errp); if (data_bs == NULL) { ret = -EIO; goto out; @@ -3617,8 +3688,8 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) } /* Create BlockBackend to write to the image */ - blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, - errp); + blk = blk_co_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, + errp); if (!blk) { ret = -EPERM; goto out; @@ -3668,7 +3739,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) cpu_to_be64(QCOW2_INCOMPAT_EXTL2); } - ret = blk_pwrite(blk, 0, header, cluster_size, 0); + ret = blk_co_pwrite(blk, 0, cluster_size, header, 0); g_free(header); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write qcow2 header"); @@ -3678,7 +3749,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) /* Write a refcount table with one refcount block */ refcount_table = g_malloc0(2 * cluster_size); refcount_table[0] = cpu_to_be64(2 * cluster_size); - ret = blk_pwrite(blk, cluster_size, refcount_table, 2 * cluster_size, 0); + ret = blk_co_pwrite(blk, cluster_size, 2 * cluster_size, refcount_table, 0); g_free(refcount_table); if (ret < 0) { @@ -3686,7 +3757,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) goto out; } - blk_unref(blk); + blk_co_unref(blk); blk = NULL; /* @@ -3700,16 +3771,18 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) if (data_bs) { qdict_put_str(options, "data-file", data_bs->node_name); } - blk = blk_new_open(NULL, NULL, options, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH, - errp); + blk = blk_co_new_open(NULL, NULL, options, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH, + errp); if (blk == NULL) { ret = -EIO; goto out; } + bdrv_graph_co_rdlock(); ret = qcow2_alloc_clusters(blk_bs(blk), 3 * cluster_size); if (ret < 0) { + bdrv_graph_co_rdunlock(); error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 " "header and refcount table"); goto out; @@ -3727,29 +3800,34 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) /* Create a full header (including things like feature table) */ ret = qcow2_update_header(blk_bs(blk)); + bdrv_graph_co_rdunlock(); + if (ret < 0) { error_setg_errno(errp, -ret, "Could not update qcow2 header"); goto out; } /* Okay, now that we have a valid image, let's give it the right size */ - ret = blk_truncate(blk, qcow2_opts->size, false, qcow2_opts->preallocation, - 0, errp); + ret = blk_co_truncate(blk, qcow2_opts->size, false, + qcow2_opts->preallocation, 0, errp); if (ret < 0) { error_prepend(errp, "Could not resize image: "); goto out; } /* Want a backing file? There you go. */ - if (qcow2_opts->has_backing_file) { + if (qcow2_opts->backing_file) { const char *backing_format = NULL; if (qcow2_opts->has_backing_fmt) { backing_format = BlockdevDriver_str(qcow2_opts->backing_fmt); } - ret = bdrv_change_backing_file(blk_bs(blk), qcow2_opts->backing_file, - backing_format, false); + bdrv_graph_co_rdlock(); + ret = bdrv_co_change_backing_file(blk_bs(blk), qcow2_opts->backing_file, + backing_format, false); + bdrv_graph_co_rdunlock(); + if (ret < 0) { error_setg_errno(errp, -ret, "Could not assign backing file '%s' " "with format '%s'", qcow2_opts->backing_file, @@ -3759,14 +3837,17 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) } /* Want encryption? There you go. */ - if (qcow2_opts->has_encrypt) { + if (qcow2_opts->encrypt) { + bdrv_graph_co_rdlock(); ret = qcow2_set_up_encryption(blk_bs(blk), qcow2_opts->encrypt, errp); + bdrv_graph_co_rdunlock(); + if (ret < 0) { goto out; } } - blk_unref(blk); + blk_co_unref(blk); blk = NULL; /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning. @@ -3781,9 +3862,9 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) if (data_bs) { qdict_put_str(options, "data-file", data_bs->node_name); } - blk = blk_new_open(NULL, NULL, options, - BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO, - errp); + blk = blk_co_new_open(NULL, NULL, options, + BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO, + errp); if (blk == NULL) { ret = -EIO; goto out; @@ -3791,16 +3872,15 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) ret = 0; out: - blk_unref(blk); - bdrv_unref(bs); - bdrv_unref(data_bs); + blk_co_unref(blk); + bdrv_co_unref(bs); + bdrv_co_unref(data_bs); return ret; } -static int coroutine_fn qcow2_co_create_opts(BlockDriver *drv, - const char *filename, - QemuOpts *opts, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts, + Error **errp) { BlockdevCreateOptions *create_options = NULL; QDict *qdict; @@ -3860,13 +3940,13 @@ static int coroutine_fn qcow2_co_create_opts(BlockDriver *drv, } /* Create and open the file (protocol layer) */ - ret = bdrv_create_file(filename, opts, errp); + ret = bdrv_co_create_file(filename, opts, errp); if (ret < 0) { goto finish; } - bs = bdrv_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); + bs = bdrv_co_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); if (bs == NULL) { ret = -EIO; goto finish; @@ -3875,14 +3955,14 @@ static int coroutine_fn qcow2_co_create_opts(BlockDriver *drv, /* Create and open an external data file (protocol layer) */ val = qdict_get_try_str(qdict, BLOCK_OPT_DATA_FILE); if (val) { - ret = bdrv_create_file(val, opts, errp); + ret = bdrv_co_create_file(val, opts, errp); if (ret < 0) { goto finish; } - data_bs = bdrv_open(val, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, - errp); + data_bs = bdrv_co_open(val, NULL, NULL, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, + errp); if (data_bs == NULL) { ret = -EIO; goto finish; @@ -3918,21 +3998,24 @@ static int coroutine_fn qcow2_co_create_opts(BlockDriver *drv, ret = qcow2_co_create(create_options, errp); finish: if (ret < 0) { + bdrv_graph_co_rdlock(); bdrv_co_delete_file_noerr(bs); bdrv_co_delete_file_noerr(data_bs); + bdrv_graph_co_rdunlock(); } else { ret = 0; } qobject_unref(qdict); - bdrv_unref(bs); - bdrv_unref(data_bs); + bdrv_co_unref(bs); + bdrv_co_unref(data_bs); qapi_free_BlockdevCreateOptions(create_options); return ret; } -static bool is_zero(BlockDriverState *bs, int64_t offset, int64_t bytes) +static bool coroutine_fn GRAPH_RDLOCK +is_zero(BlockDriverState *bs, int64_t offset, int64_t bytes) { int64_t nr; int res; @@ -3953,7 +4036,7 @@ static bool is_zero(BlockDriverState *bs, int64_t offset, int64_t bytes) * backing file. So, we need a loop. */ do { - res = bdrv_block_status_above(bs, NULL, offset, bytes, &nr, NULL, NULL); + res = bdrv_co_block_status_above(bs, NULL, offset, bytes, &nr, NULL, NULL); offset += nr; bytes -= nr; } while (res >= 0 && (res & BDRV_BLOCK_ZERO) && nr && bytes); @@ -3961,8 +4044,9 @@ static bool is_zero(BlockDriverState *bs, int64_t offset, int64_t bytes) return res >= 0 && (res & BDRV_BLOCK_ZERO) && bytes == 0; } -static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int64_t bytes, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + BdrvRequestFlags flags) { int ret; BDRVQcow2State *s = bs->opaque; @@ -4016,8 +4100,8 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs, return ret; } -static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs, - int64_t offset, int64_t bytes) +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { int ret; BDRVQcow2State *s = bs->opaque; @@ -4045,7 +4129,7 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs, return ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK qcow2_co_copy_range_from(BlockDriverState *bs, BdrvChild *src, int64_t src_offset, BdrvChild *dst, int64_t dst_offset, @@ -4078,7 +4162,7 @@ qcow2_co_copy_range_from(BlockDriverState *bs, case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: if (bs->backing && bs->backing->bs) { - int64_t backing_length = bdrv_getlength(bs->backing->bs); + int64_t backing_length = bdrv_co_getlength(bs->backing->bs); if (src_offset >= backing_length) { cur_write_flags |= BDRV_REQ_ZERO_WRITE; } else { @@ -4128,7 +4212,7 @@ out: return ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK qcow2_co_copy_range_to(BlockDriverState *bs, BdrvChild *src, int64_t src_offset, BdrvChild *dst, int64_t dst_offset, @@ -4196,10 +4280,11 @@ fail: return ret; } -static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, - bool exact, PreallocMode prealloc, - BdrvRequestFlags flags, Error **errp) +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, + PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) { + ERRP_GUARD(); BDRVQcow2State *s = bs->opaque; uint64_t old_length; int64_t new_l1_size; @@ -4273,7 +4358,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, goto fail; } - old_file_size = bdrv_getlength(bs->file->bs); + old_file_size = bdrv_co_getlength(bs->file->bs); if (old_file_size < 0) { error_setg_errno(errp, -old_file_size, "Failed to inquire current file length"); @@ -4366,7 +4451,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, break; } - old_file_size = bdrv_getlength(bs->file->bs); + old_file_size = bdrv_co_getlength(bs->file->bs); if (old_file_size < 0) { error_setg_errno(errp, -old_file_size, "Failed to inquire current file length"); @@ -4550,8 +4635,8 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, /* write updated header.size */ offset = cpu_to_be64(offset); - ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size), - &offset, sizeof(offset)); + ret = bdrv_co_pwrite_sync(bs->file, offsetof(QCowHeader, size), + sizeof(offset), &offset, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to update the image size"); goto fail; @@ -4572,7 +4657,7 @@ fail: return ret; } -static coroutine_fn int +static int coroutine_fn GRAPH_RDLOCK qcow2_co_pwritev_compressed_task(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, size_t qiov_offset) @@ -4623,7 +4708,7 @@ qcow2_co_pwritev_compressed_task(BlockDriverState *bs, goto fail; } - BLKDBG_EVENT(s->data_file, BLKDBG_WRITE_COMPRESSED); + BLKDBG_CO_EVENT(s->data_file, BLKDBG_WRITE_COMPRESSED); ret = bdrv_co_pwrite(s->data_file, cluster_offset, out_len, out_buf, 0); if (ret < 0) { goto fail; @@ -4636,7 +4721,13 @@ fail: return ret; } -static coroutine_fn int qcow2_co_pwritev_compressed_task_entry(AioTask *task) +/* + * This function can count as GRAPH_RDLOCK because + * qcow2_co_pwritev_compressed_part() holds the graph lock and keeps it until + * this coroutine has terminated. + */ +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_pwritev_compressed_task_entry(AioTask *task) { Qcow2AioTask *t = container_of(task, Qcow2AioTask, task); @@ -4650,7 +4741,7 @@ static coroutine_fn int qcow2_co_pwritev_compressed_task_entry(AioTask *task) * XXX: put compressed sectors first, then all the cluster aligned * tables to avoid losing bytes in alignment */ -static coroutine_fn int +static int coroutine_fn GRAPH_RDLOCK qcow2_co_pwritev_compressed_part(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset) @@ -4668,7 +4759,7 @@ qcow2_co_pwritev_compressed_part(BlockDriverState *bs, * align end of file to a sector boundary to ease reading with * sector based I/Os */ - int64_t len = bdrv_getlength(bs->file->bs); + int64_t len = bdrv_co_getlength(bs->file->bs); if (len < 0) { return len; } @@ -4713,7 +4804,7 @@ qcow2_co_pwritev_compressed_part(BlockDriverState *bs, return ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK qcow2_co_preadv_compressed(BlockDriverState *bs, uint64_t l2_entry, uint64_t offset, @@ -4736,7 +4827,7 @@ qcow2_co_preadv_compressed(BlockDriverState *bs, out_buf = qemu_blockalign(bs, s->cluster_size); - BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); + BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_COMPRESSED); ret = bdrv_co_pread(bs->file, coffset, csize, buf, 0); if (ret < 0) { goto fail; @@ -4756,7 +4847,7 @@ fail: return ret; } -static int make_completely_empty(BlockDriverState *bs) +static int GRAPH_RDLOCK make_completely_empty(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; Error *local_err = NULL; @@ -4828,7 +4919,7 @@ static int make_completely_empty(BlockDriverState *bs) l1_ofs_rt_ofs_cls.reftable_offset = cpu_to_be64(s->cluster_size); l1_ofs_rt_ofs_cls.reftable_clusters = cpu_to_be32(1); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_table_offset), - &l1_ofs_rt_ofs_cls, sizeof(l1_ofs_rt_ofs_cls)); + sizeof(l1_ofs_rt_ofs_cls), &l1_ofs_rt_ofs_cls, 0); if (ret < 0) { goto fail_broken_refcounts; } @@ -4859,8 +4950,8 @@ static int make_completely_empty(BlockDriverState *bs) /* Enter the first refblock into the reftable */ rt_entry = cpu_to_be64(2 * s->cluster_size); - ret = bdrv_pwrite_sync(bs->file, s->cluster_size, - &rt_entry, sizeof(rt_entry)); + ret = bdrv_pwrite_sync(bs->file, s->cluster_size, sizeof(rt_entry), + &rt_entry, 0); if (ret < 0) { goto fail_broken_refcounts; } @@ -4907,7 +4998,7 @@ fail: return ret; } -static int qcow2_make_empty(BlockDriverState *bs) +static int GRAPH_RDLOCK qcow2_make_empty(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; uint64_t offset, end_offset; @@ -4951,7 +5042,7 @@ static int qcow2_make_empty(BlockDriverState *bs) return ret; } -static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) +static coroutine_fn GRAPH_RDLOCK int qcow2_co_flush_to_os(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; int ret; @@ -5131,17 +5222,19 @@ err: return NULL; } -static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +qcow2_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVQcow2State *s = bs->opaque; bdi->cluster_size = s->cluster_size; + bdi->subcluster_size = s->subcluster_size; bdi->vm_state_offset = qcow2_vm_state_offset(s); bdi->is_dirty = s->incompatible_features & QCOW2_INCOMPAT_DIRTY; return 0; } -static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, - Error **errp) +static ImageInfoSpecific * GRAPH_RDLOCK +qcow2_get_specific_info(BlockDriverState *bs, Error **errp) { BDRVQcow2State *s = bs->opaque; ImageInfoSpecific *spec_info; @@ -5184,7 +5277,6 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, .refcount_bits = s->refcount_bits, .has_bitmaps = !!bitmaps, .bitmaps = bitmaps, - .has_data_file = !!s->image_data_file, .data_file = g_strdup(s->image_data_file), .has_data_file_raw = has_data_file(bs), .data_file_raw = data_file_is_raw(bs), @@ -5215,14 +5307,14 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, memset(&encrypt_info->u, 0, sizeof(encrypt_info->u)); qapi_free_QCryptoBlockInfo(encrypt_info); - spec_info->u.qcow2.data->has_encrypt = true; spec_info->u.qcow2.data->encrypt = qencrypt; } return spec_info; } -static int qcow2_has_zero_init(BlockDriverState *bs) +static int coroutine_mixed_fn GRAPH_RDLOCK +qcow2_has_zero_init(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; bool preallocated; @@ -5276,31 +5368,31 @@ static int64_t qcow2_check_vmstate_request(BlockDriverState *bs, return pos; } -static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, - int64_t pos) +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) { int64_t offset = qcow2_check_vmstate_request(bs, qiov, pos); if (offset < 0) { return offset; } - BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE); + BLKDBG_CO_EVENT(bs->file, BLKDBG_VMSTATE_SAVE); return bs->drv->bdrv_co_pwritev_part(bs, offset, qiov->size, qiov, 0, 0); } -static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, - int64_t pos) +static int coroutine_fn GRAPH_RDLOCK +qcow2_co_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) { int64_t offset = qcow2_check_vmstate_request(bs, qiov, pos); if (offset < 0) { return offset; } - BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_LOAD); + BLKDBG_CO_EVENT(bs->file, BLKDBG_VMSTATE_LOAD); return bs->drv->bdrv_co_preadv_part(bs, offset, qiov->size, qiov, 0, 0); } -static int qcow2_has_compressed_clusters(BlockDriverState *bs) +static int GRAPH_RDLOCK qcow2_has_compressed_clusters(BlockDriverState *bs) { int64_t offset = 0; int64_t bytes = bdrv_getlength(bs); @@ -5336,9 +5428,10 @@ static int qcow2_has_compressed_clusters(BlockDriverState *bs) * Downgrades an image's version. To achieve this, any incompatible features * have to be removed. */ -static int qcow2_downgrade(BlockDriverState *bs, int target_version, - BlockDriverAmendStatusCB *status_cb, void *cb_opaque, - Error **errp) +static int GRAPH_RDLOCK +qcow2_downgrade(BlockDriverState *bs, int target_version, + BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + Error **errp) { BDRVQcow2State *s = bs->opaque; int current_version = s->qcow_version; @@ -5446,9 +5539,10 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, * features of older versions, some things may have to be presented * differently. */ -static int qcow2_upgrade(BlockDriverState *bs, int target_version, - BlockDriverAmendStatusCB *status_cb, void *cb_opaque, - Error **errp) +static int GRAPH_RDLOCK +qcow2_upgrade(BlockDriverState *bs, int target_version, + BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + Error **errp) { BDRVQcow2State *s = bs->opaque; bool need_snapshot_update; @@ -5574,11 +5668,10 @@ static void qcow2_amend_helper_cb(BlockDriverState *bs, info->original_cb_opaque); } -static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb, - void *cb_opaque, - bool force, - Error **errp) +static int GRAPH_RDLOCK +qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + bool force, Error **errp) { BDRVQcow2State *s = bs->opaque; int old_version = s->qcow_version, new_version = old_version; @@ -5835,7 +5928,7 @@ static int coroutine_fn qcow2_co_amend(BlockDriverState *bs, BDRVQcow2State *s = bs->opaque; int ret = 0; - if (qopts->has_encrypt) { + if (qopts->encrypt) { if (!s->crypto) { error_setg(errp, "image is not encrypted, can't amend"); return -EOPNOTSUPP; @@ -5900,7 +5993,7 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, node_name = bdrv_get_node_name(bs); qapi_event_send_block_image_corrupted(bdrv_get_device_name(bs), - *node_name != '\0', node_name, + *node_name ? node_name : NULL, message, offset >= 0, offset, size >= 0, size, fatal); @@ -6033,64 +6126,64 @@ static const char *const qcow2_strong_runtime_opts[] = { }; BlockDriver bdrv_qcow2 = { - .format_name = "qcow2", - .instance_size = sizeof(BDRVQcow2State), - .bdrv_probe = qcow2_probe, - .bdrv_open = qcow2_open, - .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_default_perms, - .bdrv_co_create_opts = qcow2_co_create_opts, - .bdrv_co_create = qcow2_co_create, - .bdrv_has_zero_init = qcow2_has_zero_init, - .bdrv_co_block_status = qcow2_co_block_status, + .format_name = "qcow2", + .instance_size = sizeof(BDRVQcow2State), + .bdrv_probe = qcow2_probe, + .bdrv_open = qcow2_open, + .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_default_perms, + .bdrv_co_create_opts = qcow2_co_create_opts, + .bdrv_co_create = qcow2_co_create, + .bdrv_has_zero_init = qcow2_has_zero_init, + .bdrv_co_block_status = qcow2_co_block_status, - .bdrv_co_preadv_part = qcow2_co_preadv_part, - .bdrv_co_pwritev_part = qcow2_co_pwritev_part, - .bdrv_co_flush_to_os = qcow2_co_flush_to_os, + .bdrv_co_preadv_part = qcow2_co_preadv_part, + .bdrv_co_pwritev_part = qcow2_co_pwritev_part, + .bdrv_co_flush_to_os = qcow2_co_flush_to_os, - .bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes, - .bdrv_co_pdiscard = qcow2_co_pdiscard, - .bdrv_co_copy_range_from = qcow2_co_copy_range_from, - .bdrv_co_copy_range_to = qcow2_co_copy_range_to, - .bdrv_co_truncate = qcow2_co_truncate, - .bdrv_co_pwritev_compressed_part = qcow2_co_pwritev_compressed_part, - .bdrv_make_empty = qcow2_make_empty, + .bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes, + .bdrv_co_pdiscard = qcow2_co_pdiscard, + .bdrv_co_copy_range_from = qcow2_co_copy_range_from, + .bdrv_co_copy_range_to = qcow2_co_copy_range_to, + .bdrv_co_truncate = qcow2_co_truncate, + .bdrv_co_pwritev_compressed_part = qcow2_co_pwritev_compressed_part, + .bdrv_make_empty = qcow2_make_empty, - .bdrv_snapshot_create = qcow2_snapshot_create, - .bdrv_snapshot_goto = qcow2_snapshot_goto, - .bdrv_snapshot_delete = qcow2_snapshot_delete, - .bdrv_snapshot_list = qcow2_snapshot_list, - .bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp, - .bdrv_measure = qcow2_measure, - .bdrv_get_info = qcow2_get_info, - .bdrv_get_specific_info = qcow2_get_specific_info, + .bdrv_snapshot_create = qcow2_snapshot_create, + .bdrv_snapshot_goto = qcow2_snapshot_goto, + .bdrv_snapshot_delete = qcow2_snapshot_delete, + .bdrv_snapshot_list = qcow2_snapshot_list, + .bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp, + .bdrv_measure = qcow2_measure, + .bdrv_co_get_info = qcow2_co_get_info, + .bdrv_get_specific_info = qcow2_get_specific_info, - .bdrv_save_vmstate = qcow2_save_vmstate, - .bdrv_load_vmstate = qcow2_load_vmstate, + .bdrv_co_save_vmstate = qcow2_co_save_vmstate, + .bdrv_co_load_vmstate = qcow2_co_load_vmstate, - .is_format = true, - .supports_backing = true, - .bdrv_change_backing_file = qcow2_change_backing_file, + .is_format = true, + .supports_backing = true, + .bdrv_co_change_backing_file = qcow2_co_change_backing_file, - .bdrv_refresh_limits = qcow2_refresh_limits, - .bdrv_co_invalidate_cache = qcow2_co_invalidate_cache, - .bdrv_inactivate = qcow2_inactivate, + .bdrv_refresh_limits = qcow2_refresh_limits, + .bdrv_co_invalidate_cache = qcow2_co_invalidate_cache, + .bdrv_inactivate = qcow2_inactivate, - .create_opts = &qcow2_create_opts, - .amend_opts = &qcow2_amend_opts, - .strong_runtime_opts = qcow2_strong_runtime_opts, - .mutable_opts = mutable_opts, - .bdrv_co_check = qcow2_co_check, - .bdrv_amend_options = qcow2_amend_options, - .bdrv_co_amend = qcow2_co_amend, + .create_opts = &qcow2_create_opts, + .amend_opts = &qcow2_amend_opts, + .strong_runtime_opts = qcow2_strong_runtime_opts, + .mutable_opts = mutable_opts, + .bdrv_co_check = qcow2_co_check, + .bdrv_amend_options = qcow2_amend_options, + .bdrv_co_amend = qcow2_co_amend, - .bdrv_detach_aio_context = qcow2_detach_aio_context, - .bdrv_attach_aio_context = qcow2_attach_aio_context, + .bdrv_detach_aio_context = qcow2_detach_aio_context, + .bdrv_attach_aio_context = qcow2_attach_aio_context, .bdrv_supports_persistent_dirty_bitmap = qcow2_supports_persistent_dirty_bitmap, diff --git a/block/qcow2.h b/block/qcow2.h index ba436a8d0d..a9e3481c6e 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -133,6 +133,7 @@ #define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request" #define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot" #define QCOW2_OPT_DISCARD_OTHER "pass-discard-other" +#define QCOW2_OPT_DISCARD_NO_UNREF "discard-no-unref" #define QCOW2_OPT_OVERLAP "overlap-check" #define QCOW2_OPT_OVERLAP_TEMPLATE "overlap-check.template" #define QCOW2_OPT_OVERLAP_MAIN_HEADER "overlap-check.main-header" @@ -385,6 +386,8 @@ typedef struct BDRVQcow2State { bool discard_passthrough[QCOW2_DISCARD_MAX]; + bool discard_no_unref; + int overlap_check; /* bitmask of Qcow2MetadataOverlap values */ bool signaled_corruption; @@ -638,7 +641,7 @@ static inline void set_l2_bitmap(BDRVQcow2State *s, uint64_t *l2_slice, l2_slice[idx + 1] = cpu_to_be64(bitmap); } -static inline bool has_data_file(BlockDriverState *bs) +static inline bool GRAPH_RDLOCK has_data_file(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; return (s->data_file != bs->file); @@ -706,8 +709,8 @@ static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s) return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); } -static inline QCow2ClusterType qcow2_get_cluster_type(BlockDriverState *bs, - uint64_t l2_entry) +static inline QCow2ClusterType GRAPH_RDLOCK +qcow2_get_cluster_type(BlockDriverState *bs, uint64_t l2_entry) { BDRVQcow2State *s = bs->opaque; @@ -740,7 +743,7 @@ static inline QCow2ClusterType qcow2_get_cluster_type(BlockDriverState *bs, * (this checks the whole entry and bitmap, not only the bits related * to subcluster @sc_index). */ -static inline +static inline GRAPH_RDLOCK QCow2SubclusterType qcow2_get_subcluster_type(BlockDriverState *bs, uint64_t l2_entry, uint64_t l2_bitmap, @@ -831,14 +834,14 @@ int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size, int refcount_order, bool generous_increase, uint64_t *refblock_count); -int qcow2_mark_dirty(BlockDriverState *bs); -int qcow2_mark_corrupt(BlockDriverState *bs); -int qcow2_mark_consistent(BlockDriverState *bs); -int qcow2_update_header(BlockDriverState *bs); +int GRAPH_RDLOCK qcow2_mark_dirty(BlockDriverState *bs); +int GRAPH_RDLOCK qcow2_mark_corrupt(BlockDriverState *bs); +int GRAPH_RDLOCK qcow2_update_header(BlockDriverState *bs); -void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, - int64_t size, const char *message_format, ...) - G_GNUC_PRINTF(5, 6); +void GRAPH_RDLOCK +qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, + int64_t size, const char *message_format, ...) + G_GNUC_PRINTF(5, 6); int qcow2_validate_table(BlockDriverState *bs, uint64_t offset, uint64_t entries, size_t entry_len, @@ -846,157 +849,211 @@ int qcow2_validate_table(BlockDriverState *bs, uint64_t offset, Error **errp); /* qcow2-refcount.c functions */ -int qcow2_refcount_init(BlockDriverState *bs); +int coroutine_fn GRAPH_RDLOCK qcow2_refcount_init(BlockDriverState *bs); void qcow2_refcount_close(BlockDriverState *bs); -int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, - uint64_t *refcount); +int GRAPH_RDLOCK qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, + uint64_t *refcount); -int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, - uint64_t addend, bool decrease, - enum qcow2_discard_type type); +int GRAPH_RDLOCK +qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, + uint64_t addend, bool decrease, + enum qcow2_discard_type type); -int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t offset, - uint64_t additional_clusters, bool exact_size, - int new_refblock_index, - uint64_t new_refblock_offset); +int64_t GRAPH_RDLOCK +qcow2_refcount_area(BlockDriverState *bs, uint64_t offset, + uint64_t additional_clusters, bool exact_size, + int new_refblock_index, + uint64_t new_refblock_offset); -int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size); -int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, - int64_t nb_clusters); -int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size); -void qcow2_free_clusters(BlockDriverState *bs, - int64_t offset, int64_t size, - enum qcow2_discard_type type); -void qcow2_free_any_cluster(BlockDriverState *bs, uint64_t l2_entry, - enum qcow2_discard_type type); +int64_t GRAPH_RDLOCK +qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size); -int qcow2_update_snapshot_refcount(BlockDriverState *bs, - int64_t l1_table_offset, int l1_size, int addend); +int64_t GRAPH_RDLOCK coroutine_fn +qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, + int64_t nb_clusters); -int coroutine_fn qcow2_flush_caches(BlockDriverState *bs); -int coroutine_fn qcow2_write_caches(BlockDriverState *bs); -int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, - BdrvCheckMode fix); +int64_t coroutine_fn GRAPH_RDLOCK qcow2_alloc_bytes(BlockDriverState *bs, int size); +void GRAPH_RDLOCK qcow2_free_clusters(BlockDriverState *bs, + int64_t offset, int64_t size, + enum qcow2_discard_type type); +void GRAPH_RDLOCK +qcow2_free_any_cluster(BlockDriverState *bs, uint64_t l2_entry, + enum qcow2_discard_type type); -void qcow2_process_discards(BlockDriverState *bs, int ret); +int GRAPH_RDLOCK +qcow2_update_snapshot_refcount(BlockDriverState *bs, int64_t l1_table_offset, + int l1_size, int addend); -int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, - int64_t size); -int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, - int64_t size, bool data_file); -int qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res, - void **refcount_table, - int64_t *refcount_table_size, - int64_t offset, int64_t size); +int GRAPH_RDLOCK qcow2_flush_caches(BlockDriverState *bs); +int GRAPH_RDLOCK qcow2_write_caches(BlockDriverState *bs); +int coroutine_fn qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix); -int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, - BlockDriverAmendStatusCB *status_cb, - void *cb_opaque, Error **errp); -int qcow2_shrink_reftable(BlockDriverState *bs); -int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size); -int qcow2_detect_metadata_preallocation(BlockDriverState *bs); +void GRAPH_RDLOCK qcow2_process_discards(BlockDriverState *bs, int ret); + +int GRAPH_RDLOCK +qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, + int64_t size); +int GRAPH_RDLOCK +qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, + int64_t size, bool data_file); + +int coroutine_fn qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res, + void **refcount_table, + int64_t *refcount_table_size, + int64_t offset, int64_t size); + +int GRAPH_RDLOCK +qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque, Error **errp); +int coroutine_fn GRAPH_RDLOCK qcow2_shrink_reftable(BlockDriverState *bs); + +int64_t coroutine_fn GRAPH_RDLOCK +qcow2_get_last_cluster(BlockDriverState *bs, int64_t size); + +int coroutine_fn GRAPH_RDLOCK +qcow2_detect_metadata_preallocation(BlockDriverState *bs); /* qcow2-cluster.c functions */ -int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, - bool exact_size); -int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t max_size); -int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); +int GRAPH_RDLOCK +qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, bool exact_size); + +int coroutine_fn GRAPH_RDLOCK +qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t max_size); + +int GRAPH_RDLOCK qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, uint8_t *buf, int nb_sectors, bool enc, Error **errp); -int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, - unsigned int *bytes, uint64_t *host_offset, - QCow2SubclusterType *subcluster_type); -int qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, - unsigned int *bytes, uint64_t *host_offset, - QCowL2Meta **m); -int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, - uint64_t offset, - int compressed_size, - uint64_t *host_offset); -void qcow2_parse_compressed_l2_entry(BlockDriverState *bs, uint64_t l2_entry, - uint64_t *coffset, int *csize); +int GRAPH_RDLOCK +qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, + unsigned int *bytes, uint64_t *host_offset, + QCow2SubclusterType *subcluster_type); -int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); -void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m); -int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, enum qcow2_discard_type type, - bool full_discard); -int qcow2_subcluster_zeroize(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, int flags); +int coroutine_fn GRAPH_RDLOCK +qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, + unsigned int *bytes, uint64_t *host_offset, + QCowL2Meta **m); -int qcow2_expand_zero_clusters(BlockDriverState *bs, - BlockDriverAmendStatusCB *status_cb, - void *cb_opaque); +int coroutine_fn GRAPH_RDLOCK +qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset, + int compressed_size, uint64_t *host_offset); +void GRAPH_RDLOCK +qcow2_parse_compressed_l2_entry(BlockDriverState *bs, uint64_t l2_entry, + uint64_t *coffset, int *csize); + +int coroutine_fn GRAPH_RDLOCK +qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); + +void coroutine_fn GRAPH_RDLOCK +qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m); + +int GRAPH_RDLOCK +qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, uint64_t bytes, + enum qcow2_discard_type type, bool full_discard); + +int coroutine_fn GRAPH_RDLOCK +qcow2_subcluster_zeroize(BlockDriverState *bs, uint64_t offset, uint64_t bytes, + int flags); + +int GRAPH_RDLOCK +qcow2_expand_zero_clusters(BlockDriverState *bs, + BlockDriverAmendStatusCB *status_cb, + void *cb_opaque); /* qcow2-snapshot.c functions */ -int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); -int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); -int qcow2_snapshot_delete(BlockDriverState *bs, - const char *snapshot_id, - const char *name, - Error **errp); -int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); -int qcow2_snapshot_load_tmp(BlockDriverState *bs, - const char *snapshot_id, - const char *name, - Error **errp); +int GRAPH_RDLOCK +qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); + +int GRAPH_RDLOCK +qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); + +int GRAPH_RDLOCK +qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id, + const char *name, Error **errp); + +int GRAPH_RDLOCK +qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); + +int GRAPH_RDLOCK +qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_id, + const char *name, Error **errp); void qcow2_free_snapshots(BlockDriverState *bs); -int qcow2_read_snapshots(BlockDriverState *bs, Error **errp); -int qcow2_write_snapshots(BlockDriverState *bs); +int coroutine_fn GRAPH_RDLOCK +qcow2_read_snapshots(BlockDriverState *bs, Error **errp); +int GRAPH_RDLOCK qcow2_write_snapshots(BlockDriverState *bs); -int coroutine_fn qcow2_check_read_snapshot_table(BlockDriverState *bs, - BdrvCheckResult *result, - BdrvCheckMode fix); -int coroutine_fn qcow2_check_fix_snapshot_table(BlockDriverState *bs, - BdrvCheckResult *result, - BdrvCheckMode fix); +int coroutine_fn GRAPH_RDLOCK +qcow2_check_read_snapshot_table(BlockDriverState *bs, BdrvCheckResult *result, + BdrvCheckMode fix); + +int coroutine_fn GRAPH_RDLOCK +qcow2_check_fix_snapshot_table(BlockDriverState *bs, BdrvCheckResult *result, + BdrvCheckMode fix); /* qcow2-cache.c functions */ -Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, - unsigned table_size); +Qcow2Cache * GRAPH_RDLOCK +qcow2_cache_create(BlockDriverState *bs, int num_tables, unsigned table_size); + int qcow2_cache_destroy(Qcow2Cache *c); void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); -int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); -int qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c); -int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, - Qcow2Cache *dependency); +int GRAPH_RDLOCK qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); +int GRAPH_RDLOCK qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c); +int GRAPH_RDLOCK qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, + Qcow2Cache *dependency); void qcow2_cache_depends_on_flush(Qcow2Cache *c); void qcow2_cache_clean_unused(Qcow2Cache *c); -int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c); +int GRAPH_RDLOCK qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c); + +int GRAPH_RDLOCK +qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table); + +int GRAPH_RDLOCK +qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table); -int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, - void **table); -int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, - void **table); void qcow2_cache_put(Qcow2Cache *c, void **table); void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64_t offset); void qcow2_cache_discard(Qcow2Cache *c, void *table); /* qcow2-bitmap.c functions */ -int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, - void **refcount_table, - int64_t *refcount_table_size); -bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, - Error **errp); -bool qcow2_get_bitmap_info_list(BlockDriverState *bs, - Qcow2BitmapInfoList **info_list, Error **errp); -int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); -int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp); -bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, - bool release_stored, Error **errp); -int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); -bool qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs, - const char *name, - uint32_t granularity, - Error **errp); -int qcow2_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, - const char *name, - Error **errp); +int coroutine_fn GRAPH_RDLOCK +qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, + void **refcount_table, + int64_t *refcount_table_size); + +bool coroutine_fn GRAPH_RDLOCK +qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, + Error **errp); + +bool GRAPH_RDLOCK +qcow2_get_bitmap_info_list(BlockDriverState *bs, + Qcow2BitmapInfoList **info_list, Error **errp); + +int GRAPH_RDLOCK qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); +int GRAPH_RDLOCK qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); + +int coroutine_fn GRAPH_RDLOCK +qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp); + +bool GRAPH_RDLOCK +qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, bool release_stored, + Error **errp); + +bool coroutine_fn GRAPH_RDLOCK +qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, + uint32_t granularity, Error **errp); + +int coroutine_fn GRAPH_RDLOCK +qcow2_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, + Error **errp); + bool qcow2_supports_persistent_dirty_bitmap(BlockDriverState *bs); uint64_t qcow2_get_persistent_dirty_bitmap_size(BlockDriverState *bs, uint32_t cluster_size); diff --git a/block/qed-check.c b/block/qed-check.c index 418033ee24..6a01b94f9c 100644 --- a/block/qed-check.c +++ b/block/qed-check.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" +#include "block/block-io.h" #include "qed.h" typedef struct { @@ -106,7 +107,8 @@ static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table) /** * Descend tables and check each cluster is referenced once only */ -static int coroutine_fn qed_check_l1_table(QEDCheck *check, QEDTable *table) +static int coroutine_fn GRAPH_RDLOCK +qed_check_l1_table(QEDCheck *check, QEDTable *table) { BDRVQEDState *s = check->s; unsigned int i, num_invalid_l1 = 0; @@ -198,7 +200,8 @@ static void qed_check_for_leaks(QEDCheck *check) /** * Mark an image clean once it passes check or has been repaired */ -static void qed_check_mark_clean(BDRVQEDState *s, BdrvCheckResult *result) +static void coroutine_fn GRAPH_RDLOCK +qed_check_mark_clean(BDRVQEDState *s, BdrvCheckResult *result) { /* Skip if there were unfixable corruptions or I/O errors */ if (result->corruptions > 0 || result->check_errors > 0) { @@ -211,7 +214,7 @@ static void qed_check_mark_clean(BDRVQEDState *s, BdrvCheckResult *result) } /* Ensure fixes reach storage before clearing check bit */ - bdrv_flush(s->bs); + bdrv_co_flush(s->bs); s->header.features &= ~QED_F_NEED_CHECK; qed_write_header_sync(s); diff --git a/block/qed-table.c b/block/qed-table.c index 1cc844b1a5..f04520d4c8 100644 --- a/block/qed-table.c +++ b/block/qed-table.c @@ -13,6 +13,7 @@ */ #include "qemu/osdep.h" +#include "block/block-io.h" #include "trace.h" #include "qemu/sockets.h" /* for EINPROGRESS on Windows */ #include "qed.h" @@ -20,8 +21,8 @@ #include "qemu/memalign.h" /* Called with table_lock held. */ -static int coroutine_fn qed_read_table(BDRVQEDState *s, uint64_t offset, - QEDTable *table) +static int coroutine_fn GRAPH_RDLOCK +qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table) { unsigned int bytes = s->header.cluster_size * s->header.table_size; @@ -62,9 +63,9 @@ out: * * Called with table_lock held. */ -static int coroutine_fn qed_write_table(BDRVQEDState *s, uint64_t offset, - QEDTable *table, unsigned int index, - unsigned int n, bool flush) +static int coroutine_fn GRAPH_RDLOCK +qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, + unsigned int index, unsigned int n, bool flush) { unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1; unsigned int start, end, i; @@ -100,7 +101,7 @@ static int coroutine_fn qed_write_table(BDRVQEDState *s, uint64_t offset, } if (flush) { - ret = bdrv_flush(s->bs); + ret = bdrv_co_flush(s->bs); if (ret < 0) { goto out; } @@ -121,7 +122,7 @@ int coroutine_fn qed_read_l1_table_sync(BDRVQEDState *s) int coroutine_fn qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n) { - BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE); + BLKDBG_CO_EVENT(s->bs->file, BLKDBG_L1_UPDATE); return qed_write_table(s, s->header.l1_table_offset, s->l1_table, index, n, false); } @@ -149,7 +150,7 @@ int coroutine_fn qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, request->l2_table = qed_alloc_l2_cache_entry(&s->l2_cache); request->l2_table->table = qed_alloc_table(s); - BLKDBG_EVENT(s->bs->file, BLKDBG_L2_LOAD); + BLKDBG_CO_EVENT(s->bs->file, BLKDBG_L2_LOAD); ret = qed_read_table(s, offset, request->l2_table->table); if (ret) { @@ -182,7 +183,7 @@ int coroutine_fn qed_write_l2_table(BDRVQEDState *s, QEDRequest *request, unsigned int index, unsigned int n, bool flush) { - BLKDBG_EVENT(s->bs->file, BLKDBG_L2_UPDATE); + BLKDBG_CO_EVENT(s->bs->file, BLKDBG_L2_UPDATE); return qed_write_table(s, request->l2_table->offset, request->l2_table->table, index, n, flush); } diff --git a/block/qed.c b/block/qed.c index f34d9a3ac1..fa5bc11085 100644 --- a/block/qed.c +++ b/block/qed.c @@ -87,14 +87,9 @@ static void qed_header_cpu_to_le(const QEDHeader *cpu, QEDHeader *le) int qed_write_header_sync(BDRVQEDState *s) { QEDHeader le; - int ret; qed_header_cpu_to_le(&s->header, &le); - ret = bdrv_pwrite(s->bs->file, 0, &le, sizeof(le)); - if (ret != sizeof(le)) { - return ret; - } - return 0; + return bdrv_pwrite(s->bs->file, 0, sizeof(le), &le, 0); } /** @@ -105,7 +100,7 @@ int qed_write_header_sync(BDRVQEDState *s) * * No new allocating reqs can start while this function runs. */ -static int coroutine_fn qed_write_header(BDRVQEDState *s) +static int coroutine_fn GRAPH_RDLOCK qed_write_header(BDRVQEDState *s) { /* We must write full sectors for O_DIRECT but cannot necessarily generate * the data following the header if an unrecognized compat feature is @@ -200,14 +195,15 @@ static bool qed_is_image_size_valid(uint64_t image_size, uint32_t cluster_size, * * The string is NUL-terminated. */ -static int qed_read_string(BdrvChild *file, uint64_t offset, size_t n, - char *buf, size_t buflen) +static int coroutine_fn GRAPH_RDLOCK +qed_read_string(BdrvChild *file, uint64_t offset, + size_t n, char *buf, size_t buflen) { int ret; if (n >= buflen) { return -EINVAL; } - ret = bdrv_pread(file, offset, buf, n); + ret = bdrv_co_pread(file, offset, n, buf, 0); if (ret < 0) { return ret; } @@ -259,7 +255,7 @@ static CachedL2Table *qed_new_l2_table(BDRVQEDState *s) return l2_table; } -static bool qed_plug_allocating_write_reqs(BDRVQEDState *s) +static bool coroutine_fn qed_plug_allocating_write_reqs(BDRVQEDState *s) { qemu_co_mutex_lock(&s->table_lock); @@ -267,7 +263,7 @@ static bool qed_plug_allocating_write_reqs(BDRVQEDState *s) assert(!s->allocating_write_reqs_plugged); if (s->allocating_acb != NULL) { /* Another allocating write came concurrently. This cannot happen - * from bdrv_qed_co_drain_begin, but it can happen when the timer runs. + * from bdrv_qed_drain_begin, but it can happen when the timer runs. */ qemu_co_mutex_unlock(&s->table_lock); return false; @@ -278,7 +274,7 @@ static bool qed_plug_allocating_write_reqs(BDRVQEDState *s) return true; } -static void qed_unplug_allocating_write_reqs(BDRVQEDState *s) +static void coroutine_fn qed_unplug_allocating_write_reqs(BDRVQEDState *s) { qemu_co_mutex_lock(&s->table_lock); assert(s->allocating_write_reqs_plugged); @@ -287,12 +283,12 @@ static void qed_unplug_allocating_write_reqs(BDRVQEDState *s) qemu_co_mutex_unlock(&s->table_lock); } -static void coroutine_fn qed_need_check_timer_entry(void *opaque) +static void coroutine_fn GRAPH_RDLOCK qed_need_check_timer(BDRVQEDState *s) { - BDRVQEDState *s = opaque; int ret; trace_qed_need_check_timer_cb(s); + assert_bdrv_graph_readable(); if (!qed_plug_allocating_write_reqs(s)) { return; @@ -315,9 +311,21 @@ static void coroutine_fn qed_need_check_timer_entry(void *opaque) (void) ret; } +static void coroutine_fn qed_need_check_timer_entry(void *opaque) +{ + BDRVQEDState *s = opaque; + GRAPH_RDLOCK_GUARD(); + + qed_need_check_timer(opaque); + bdrv_dec_in_flight(s->bs); +} + static void qed_need_check_timer_cb(void *opaque) { + BDRVQEDState *s = opaque; Coroutine *co = qemu_coroutine_create(qed_need_check_timer_entry, opaque); + + bdrv_inc_in_flight(s->bs); qemu_coroutine_enter(co); } @@ -360,7 +368,7 @@ static void bdrv_qed_attach_aio_context(BlockDriverState *bs, } } -static void coroutine_fn bdrv_qed_co_drain_begin(BlockDriverState *bs) +static void bdrv_qed_drain_begin(BlockDriverState *bs) { BDRVQEDState *s = bs->opaque; @@ -368,8 +376,12 @@ static void coroutine_fn bdrv_qed_co_drain_begin(BlockDriverState *bs) * header is flushed. */ if (s->need_check_timer && timer_pending(s->need_check_timer)) { + Coroutine *co; + qed_cancel_need_check_timer(s); - qed_need_check_timer_entry(s); + co = qemu_coroutine_create(qed_need_check_timer_entry, s); + bdrv_inc_in_flight(bs); + aio_co_enter(bdrv_get_aio_context(bs), co); } } @@ -384,15 +396,15 @@ static void bdrv_qed_init_state(BlockDriverState *bs) } /* Called with table_lock held. */ -static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, - int flags, Error **errp) +static int coroutine_fn GRAPH_RDLOCK +bdrv_qed_do_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVQEDState *s = bs->opaque; QEDHeader le_header; int64_t file_size; int ret; - ret = bdrv_pread(bs->file, 0, &le_header, sizeof(le_header)); + ret = bdrv_co_pread(bs->file, 0, sizeof(le_header), &le_header, 0); if (ret < 0) { error_setg(errp, "Failed to read QED header"); return ret; @@ -415,7 +427,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, } /* Round down file size to the last cluster */ - file_size = bdrv_getlength(bs->file->bs); + file_size = bdrv_co_getlength(bs->file->bs); if (file_size < 0) { error_setg(errp, "Failed to get file length"); return file_size; @@ -450,6 +462,8 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, } if ((s->header.features & QED_F_BACKING_FILE)) { + g_autofree char *backing_file_str = NULL; + if ((uint64_t)s->header.backing_filename_offset + s->header.backing_filename_size > s->header.cluster_size * s->header.header_size) { @@ -457,16 +471,21 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, return -EINVAL; } + backing_file_str = g_malloc(sizeof(bs->backing_file)); ret = qed_read_string(bs->file, s->header.backing_filename_offset, s->header.backing_filename_size, - bs->auto_backing_file, - sizeof(bs->auto_backing_file)); + backing_file_str, sizeof(bs->backing_file)); if (ret < 0) { error_setg(errp, "Failed to read backing filename"); return ret; } - pstrcpy(bs->backing_file, sizeof(bs->backing_file), - bs->auto_backing_file); + + if (!g_str_equal(backing_file_str, bs->backing_file)) { + pstrcpy(bs->backing_file, sizeof(bs->backing_file), + backing_file_str); + pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file), + backing_file_str); + } if (s->header.features & QED_F_BACKING_FORMAT_NO_PROBE) { pstrcpy(bs->backing_format, sizeof(bs->backing_format), "raw"); @@ -490,7 +509,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, } /* From here on only known autoclear feature bits are valid */ - bdrv_flush(bs->file->bs); + bdrv_co_flush(bs->file->bs); } s->l1_table = qed_alloc_table(s); @@ -544,13 +563,15 @@ static void coroutine_fn bdrv_qed_open_entry(void *opaque) QEDOpenCo *qoc = opaque; BDRVQEDState *s = qoc->bs->opaque; + GRAPH_RDLOCK_GUARD(); + qemu_co_mutex_lock(&s->table_lock); qoc->ret = bdrv_qed_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp); qemu_co_mutex_unlock(&s->table_lock); } -static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +static int coroutine_mixed_fn bdrv_qed_open(BlockDriverState *bs, QDict *options, + int flags, Error **errp) { QEDOpenCo qoc = { .bs = bs, @@ -559,22 +580,19 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, .errp = errp, .ret = -EINPROGRESS }; + int ret; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_IMAGE, false, errp); - if (!bs->file) { - return -EINVAL; + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; } bdrv_qed_init_state(bs); - if (qemu_in_coroutine()) { - bdrv_qed_open_entry(&qoc); - } else { - assert(qemu_get_current_aio_context() == qemu_get_aio_context()); - qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc)); - BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS); - } + assert(!qemu_in_coroutine()); + assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc)); BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS); + return qoc.ret; } @@ -594,7 +612,7 @@ static int bdrv_qed_reopen_prepare(BDRVReopenState *state, return 0; } -static void bdrv_qed_close(BlockDriverState *bs) +static void GRAPH_RDLOCK bdrv_qed_do_close(BlockDriverState *bs) { BDRVQEDState *s = bs->opaque; @@ -613,8 +631,16 @@ static void bdrv_qed_close(BlockDriverState *bs) qemu_vfree(s->l1_table); } -static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts, - Error **errp) +static void GRAPH_UNLOCKED bdrv_qed_close(BlockDriverState *bs) +{ + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + bdrv_qed_do_close(bs); +} + +static int coroutine_fn GRAPH_UNLOCKED +bdrv_qed_co_create(BlockdevCreateOptions *opts, Error **errp) { BlockdevCreateOptionsQed *qed_opts; BlockBackend *blk = NULL; @@ -660,13 +686,13 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts, } /* Create BlockBackend to write to the image */ - bs = bdrv_open_blockdev_ref(qed_opts->file, errp); + bs = bdrv_co_open_blockdev_ref(qed_opts->file, errp); if (bs == NULL) { return -EIO; } - blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, - errp); + blk = blk_co_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, + errp); if (!blk) { ret = -EPERM; goto out; @@ -691,12 +717,12 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts, * The QED format associates file length with allocation status, * so a new file (which is empty) must have a length of 0. */ - ret = blk_truncate(blk, 0, true, PREALLOC_MODE_OFF, 0, errp); + ret = blk_co_truncate(blk, 0, true, PREALLOC_MODE_OFF, 0, errp); if (ret < 0) { goto out; } - if (qed_opts->has_backing_file) { + if (qed_opts->backing_file) { header.features |= QED_F_BACKING_FILE; header.backing_filename_offset = sizeof(le_header); header.backing_filename_size = strlen(qed_opts->backing_file); @@ -710,18 +736,18 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts, } qed_header_cpu_to_le(&header, &le_header); - ret = blk_pwrite(blk, 0, &le_header, sizeof(le_header), 0); + ret = blk_co_pwrite(blk, 0, sizeof(le_header), &le_header, 0); if (ret < 0) { goto out; } - ret = blk_pwrite(blk, sizeof(le_header), qed_opts->backing_file, - header.backing_filename_size, 0); + ret = blk_co_pwrite(blk, sizeof(le_header), header.backing_filename_size, + qed_opts->backing_file, 0); if (ret < 0) { goto out; } l1_table = g_malloc0(l1_size); - ret = blk_pwrite(blk, header.l1_table_offset, l1_table, l1_size, 0); + ret = blk_co_pwrite(blk, header.l1_table_offset, l1_size, l1_table, 0); if (ret < 0) { goto out; } @@ -729,15 +755,14 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts, ret = 0; /* success */ out: g_free(l1_table); - blk_unref(blk); - bdrv_unref(bs); + blk_co_unref(blk); + bdrv_co_unref(bs); return ret; } -static int coroutine_fn bdrv_qed_co_create_opts(BlockDriver *drv, - const char *filename, - QemuOpts *opts, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +bdrv_qed_co_create_opts(BlockDriver *drv, const char *filename, + QemuOpts *opts, Error **errp) { BlockdevCreateOptions *create_options = NULL; QDict *qdict; @@ -762,13 +787,13 @@ static int coroutine_fn bdrv_qed_co_create_opts(BlockDriver *drv, } /* Create and open the file (protocol layer) */ - ret = bdrv_create_file(filename, opts, errp); + ret = bdrv_co_create_file(filename, opts, errp); if (ret < 0) { goto fail; } - bs = bdrv_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); + bs = bdrv_co_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); if (bs == NULL) { ret = -EIO; goto fail; @@ -801,16 +826,15 @@ static int coroutine_fn bdrv_qed_co_create_opts(BlockDriver *drv, fail: qobject_unref(qdict); - bdrv_unref(bs); + bdrv_co_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; } -static int coroutine_fn bdrv_qed_co_block_status(BlockDriverState *bs, - bool want_zero, - int64_t pos, int64_t bytes, - int64_t *pnum, int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +bdrv_qed_co_block_status(BlockDriverState *bs, bool want_zero, int64_t pos, + int64_t bytes, int64_t *pnum, int64_t *map, + BlockDriverState **file) { BDRVQEDState *s = bs->opaque; size_t len = MIN(bytes, SIZE_MAX); @@ -863,11 +887,11 @@ static BDRVQEDState *acb_to_s(QEDAIOCB *acb) * This function reads qiov->size bytes starting at pos from the backing file. * If there is no backing file then zeroes are read. */ -static int coroutine_fn qed_read_backing_file(BDRVQEDState *s, uint64_t pos, - QEMUIOVector *qiov) +static int coroutine_fn GRAPH_RDLOCK +qed_read_backing_file(BDRVQEDState *s, uint64_t pos, QEMUIOVector *qiov) { if (s->bs->backing) { - BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO); + BLKDBG_CO_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO); return bdrv_co_preadv(s->bs->backing, pos, qiov->size, qiov, 0); } qemu_iovec_memset(qiov, 0, 0, qiov->size); @@ -882,9 +906,9 @@ static int coroutine_fn qed_read_backing_file(BDRVQEDState *s, uint64_t pos, * @len: Number of bytes * @offset: Byte offset in image file */ -static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s, - uint64_t pos, uint64_t len, - uint64_t offset) +static int coroutine_fn GRAPH_RDLOCK +qed_copy_from_backing_file(BDRVQEDState *s, uint64_t pos, uint64_t len, + uint64_t offset) { QEMUIOVector qiov; int ret; @@ -902,7 +926,7 @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s, goto out; } - BLKDBG_EVENT(s->bs->file, BLKDBG_COW_WRITE); + BLKDBG_CO_EVENT(s->bs->file, BLKDBG_COW_WRITE); ret = bdrv_co_pwritev(s->bs->file, offset, qiov.size, &qiov, 0); if (ret < 0) { goto out; @@ -977,7 +1001,7 @@ static void coroutine_fn qed_aio_complete(QEDAIOCB *acb) * * Called with table_lock held. */ -static int coroutine_fn qed_aio_write_l1_update(QEDAIOCB *acb) +static int coroutine_fn GRAPH_RDLOCK qed_aio_write_l1_update(QEDAIOCB *acb) { BDRVQEDState *s = acb_to_s(acb); CachedL2Table *l2_table = acb->request.l2_table; @@ -1007,7 +1031,8 @@ static int coroutine_fn qed_aio_write_l1_update(QEDAIOCB *acb) * * Called with table_lock held. */ -static int coroutine_fn qed_aio_write_l2_update(QEDAIOCB *acb, uint64_t offset) +static int coroutine_fn GRAPH_RDLOCK +qed_aio_write_l2_update(QEDAIOCB *acb, uint64_t offset) { BDRVQEDState *s = acb_to_s(acb); bool need_alloc = acb->find_cluster_ret == QED_CLUSTER_L1; @@ -1045,7 +1070,7 @@ static int coroutine_fn qed_aio_write_l2_update(QEDAIOCB *acb, uint64_t offset) * * Called with table_lock *not* held. */ -static int coroutine_fn qed_aio_write_main(QEDAIOCB *acb) +static int coroutine_fn GRAPH_RDLOCK qed_aio_write_main(QEDAIOCB *acb) { BDRVQEDState *s = acb_to_s(acb); uint64_t offset = acb->cur_cluster + @@ -1053,7 +1078,7 @@ static int coroutine_fn qed_aio_write_main(QEDAIOCB *acb) trace_qed_aio_write_main(s, acb, 0, offset, acb->cur_qiov.size); - BLKDBG_EVENT(s->bs->file, BLKDBG_WRITE_AIO); + BLKDBG_CO_EVENT(s->bs->file, BLKDBG_WRITE_AIO); return bdrv_co_pwritev(s->bs->file, offset, acb->cur_qiov.size, &acb->cur_qiov, 0); } @@ -1063,7 +1088,7 @@ static int coroutine_fn qed_aio_write_main(QEDAIOCB *acb) * * Called with table_lock held. */ -static int coroutine_fn qed_aio_write_cow(QEDAIOCB *acb) +static int coroutine_fn GRAPH_RDLOCK qed_aio_write_cow(QEDAIOCB *acb) { BDRVQEDState *s = acb_to_s(acb); uint64_t start, len, offset; @@ -1121,7 +1146,7 @@ out: /** * Check if the QED_F_NEED_CHECK bit should be set during allocating write */ -static bool qed_should_set_need_check(BDRVQEDState *s) +static bool GRAPH_RDLOCK qed_should_set_need_check(BDRVQEDState *s) { /* The flush before L2 update path ensures consistency */ if (s->bs->backing) { @@ -1141,7 +1166,8 @@ static bool qed_should_set_need_check(BDRVQEDState *s) * * Called with table_lock held. */ -static int coroutine_fn qed_aio_write_alloc(QEDAIOCB *acb, size_t len) +static int coroutine_fn GRAPH_RDLOCK +qed_aio_write_alloc(QEDAIOCB *acb, size_t len) { BDRVQEDState *s = acb_to_s(acb); int ret; @@ -1204,8 +1230,8 @@ static int coroutine_fn qed_aio_write_alloc(QEDAIOCB *acb, size_t len) * * Called with table_lock held. */ -static int coroutine_fn qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, - size_t len) +static int coroutine_fn GRAPH_RDLOCK +qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len) { BDRVQEDState *s = acb_to_s(acb); int r; @@ -1247,8 +1273,8 @@ out: * * Called with table_lock held. */ -static int coroutine_fn qed_aio_write_data(void *opaque, int ret, - uint64_t offset, size_t len) +static int coroutine_fn GRAPH_RDLOCK +qed_aio_write_data(void *opaque, int ret, uint64_t offset, size_t len) { QEDAIOCB *acb = opaque; @@ -1280,8 +1306,8 @@ static int coroutine_fn qed_aio_write_data(void *opaque, int ret, * * Called with table_lock held. */ -static int coroutine_fn qed_aio_read_data(void *opaque, int ret, - uint64_t offset, size_t len) +static int coroutine_fn GRAPH_RDLOCK +qed_aio_read_data(void *opaque, int ret, uint64_t offset, size_t len) { QEDAIOCB *acb = opaque; BDRVQEDState *s = acb_to_s(acb); @@ -1306,7 +1332,7 @@ static int coroutine_fn qed_aio_read_data(void *opaque, int ret, } else if (ret != QED_CLUSTER_FOUND) { r = qed_read_backing_file(s, acb->cur_pos, &acb->cur_qiov); } else { - BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); + BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_AIO); r = bdrv_co_preadv(bs->file, offset, acb->cur_qiov.size, &acb->cur_qiov, 0); } @@ -1318,7 +1344,7 @@ static int coroutine_fn qed_aio_read_data(void *opaque, int ret, /** * Begin next I/O or complete the request */ -static int coroutine_fn qed_aio_next_io(QEDAIOCB *acb) +static int coroutine_fn GRAPH_RDLOCK qed_aio_next_io(QEDAIOCB *acb) { BDRVQEDState *s = acb_to_s(acb); uint64_t offset; @@ -1363,9 +1389,9 @@ static int coroutine_fn qed_aio_next_io(QEDAIOCB *acb) return ret; } -static int coroutine_fn qed_co_request(BlockDriverState *bs, int64_t sector_num, - QEMUIOVector *qiov, int nb_sectors, - int flags) +static int coroutine_fn GRAPH_RDLOCK +qed_co_request(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, + int nb_sectors, int flags) { QEDAIOCB acb = { .bs = bs, @@ -1382,25 +1408,23 @@ static int coroutine_fn qed_co_request(BlockDriverState *bs, int64_t sector_num, return qed_aio_next_io(&acb); } -static int coroutine_fn bdrv_qed_co_readv(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, - QEMUIOVector *qiov) +static int coroutine_fn GRAPH_RDLOCK +bdrv_qed_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + QEMUIOVector *qiov) { return qed_co_request(bs, sector_num, qiov, nb_sectors, 0); } -static int coroutine_fn bdrv_qed_co_writev(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, - QEMUIOVector *qiov, int flags) +static int coroutine_fn GRAPH_RDLOCK +bdrv_qed_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + QEMUIOVector *qiov, int flags) { - assert(!flags); return qed_co_request(bs, sector_num, qiov, nb_sectors, QED_AIOCB_WRITE); } -static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, - int64_t bytes, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + BdrvRequestFlags flags) { BDRVQEDState *s = bs->opaque; @@ -1427,12 +1451,10 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, QED_AIOCB_WRITE | QED_AIOCB_ZERO); } -static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs, - int64_t offset, - bool exact, - PreallocMode prealloc, - BdrvRequestFlags flags, - Error **errp) +static int coroutine_fn GRAPH_RDLOCK +bdrv_qed_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, + PreallocMode prealloc, BdrvRequestFlags flags, + Error **errp) { BDRVQEDState *s = bs->opaque; uint64_t old_image_size; @@ -1465,13 +1487,14 @@ static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs, return ret; } -static int64_t bdrv_qed_getlength(BlockDriverState *bs) +static int64_t coroutine_fn bdrv_qed_co_getlength(BlockDriverState *bs) { BDRVQEDState *s = bs->opaque; return s->header.image_size; } -static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +bdrv_qed_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVQEDState *s = bs->opaque; @@ -1481,9 +1504,9 @@ static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return 0; } -static int bdrv_qed_change_backing_file(BlockDriverState *bs, - const char *backing_file, - const char *backing_fmt) +static int coroutine_fn GRAPH_RDLOCK +bdrv_qed_co_change_backing_file(BlockDriverState *bs, const char *backing_file, + const char *backing_fmt) { BDRVQEDState *s = bs->opaque; QEDHeader new_header, le_header; @@ -1545,7 +1568,7 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs, } /* Write new header */ - ret = bdrv_pwrite_sync(bs->file, 0, buffer, buffer_len); + ret = bdrv_co_pwrite_sync(bs->file, 0, buffer_len, buffer, 0); g_free(buffer); if (ret == 0) { memcpy(&s->header, &new_header, sizeof(new_header)); @@ -1553,13 +1576,14 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs, return ret; } -static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs, - Error **errp) +static void coroutine_fn GRAPH_RDLOCK +bdrv_qed_co_invalidate_cache(BlockDriverState *bs, Error **errp) { + ERRP_GUARD(); BDRVQEDState *s = bs->opaque; int ret; - bdrv_qed_close(bs); + bdrv_qed_do_close(bs); bdrv_qed_init_state(bs); qemu_co_mutex_lock(&s->table_lock); @@ -1570,9 +1594,9 @@ static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs, } } -static int coroutine_fn bdrv_qed_co_check(BlockDriverState *bs, - BdrvCheckResult *result, - BdrvCheckMode fix) +static int coroutine_fn GRAPH_RDLOCK +bdrv_qed_co_check(BlockDriverState *bs, BdrvCheckResult *result, + BdrvCheckMode fix) { BDRVQEDState *s = bs->opaque; int ret; @@ -1619,34 +1643,34 @@ static QemuOptsList qed_create_opts = { }; static BlockDriver bdrv_qed = { - .format_name = "qed", - .instance_size = sizeof(BDRVQEDState), - .create_opts = &qed_create_opts, - .is_format = true, - .supports_backing = true, + .format_name = "qed", + .instance_size = sizeof(BDRVQEDState), + .create_opts = &qed_create_opts, + .is_format = true, + .supports_backing = true, - .bdrv_probe = bdrv_qed_probe, - .bdrv_open = bdrv_qed_open, - .bdrv_close = bdrv_qed_close, - .bdrv_reopen_prepare = bdrv_qed_reopen_prepare, - .bdrv_child_perm = bdrv_default_perms, - .bdrv_co_create = bdrv_qed_co_create, - .bdrv_co_create_opts = bdrv_qed_co_create_opts, - .bdrv_has_zero_init = bdrv_has_zero_init_1, - .bdrv_co_block_status = bdrv_qed_co_block_status, - .bdrv_co_readv = bdrv_qed_co_readv, - .bdrv_co_writev = bdrv_qed_co_writev, - .bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes, - .bdrv_co_truncate = bdrv_qed_co_truncate, - .bdrv_getlength = bdrv_qed_getlength, - .bdrv_get_info = bdrv_qed_get_info, - .bdrv_refresh_limits = bdrv_qed_refresh_limits, - .bdrv_change_backing_file = bdrv_qed_change_backing_file, - .bdrv_co_invalidate_cache = bdrv_qed_co_invalidate_cache, - .bdrv_co_check = bdrv_qed_co_check, - .bdrv_detach_aio_context = bdrv_qed_detach_aio_context, - .bdrv_attach_aio_context = bdrv_qed_attach_aio_context, - .bdrv_co_drain_begin = bdrv_qed_co_drain_begin, + .bdrv_probe = bdrv_qed_probe, + .bdrv_open = bdrv_qed_open, + .bdrv_close = bdrv_qed_close, + .bdrv_reopen_prepare = bdrv_qed_reopen_prepare, + .bdrv_child_perm = bdrv_default_perms, + .bdrv_co_create = bdrv_qed_co_create, + .bdrv_co_create_opts = bdrv_qed_co_create_opts, + .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_co_block_status = bdrv_qed_co_block_status, + .bdrv_co_readv = bdrv_qed_co_readv, + .bdrv_co_writev = bdrv_qed_co_writev, + .bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes, + .bdrv_co_truncate = bdrv_qed_co_truncate, + .bdrv_co_getlength = bdrv_qed_co_getlength, + .bdrv_co_get_info = bdrv_qed_co_get_info, + .bdrv_refresh_limits = bdrv_qed_refresh_limits, + .bdrv_co_change_backing_file = bdrv_qed_co_change_backing_file, + .bdrv_co_invalidate_cache = bdrv_qed_co_invalidate_cache, + .bdrv_co_check = bdrv_qed_co_check, + .bdrv_detach_aio_context = bdrv_qed_detach_aio_context, + .bdrv_attach_aio_context = bdrv_qed_attach_aio_context, + .bdrv_drain_begin = bdrv_qed_drain_begin, }; static void bdrv_qed_init(void) diff --git a/block/qed.h b/block/qed.h index 3d12bf78d4..26d4bf038c 100644 --- a/block/qed.h +++ b/block/qed.h @@ -185,7 +185,7 @@ enum { /** * Header functions */ -int qed_write_header_sync(BDRVQEDState *s); +int GRAPH_RDLOCK qed_write_header_sync(BDRVQEDState *s); /** * L2 cache functions @@ -200,33 +200,40 @@ void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table); /** * Table I/O functions */ -int coroutine_fn qed_read_l1_table_sync(BDRVQEDState *s); -int coroutine_fn qed_write_l1_table(BDRVQEDState *s, unsigned int index, - unsigned int n); -int coroutine_fn qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index, - unsigned int n); -int coroutine_fn qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, - uint64_t offset); -int coroutine_fn qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, - uint64_t offset); -int coroutine_fn qed_write_l2_table(BDRVQEDState *s, QEDRequest *request, - unsigned int index, unsigned int n, - bool flush); -int coroutine_fn qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request, - unsigned int index, unsigned int n, - bool flush); +int coroutine_fn GRAPH_RDLOCK qed_read_l1_table_sync(BDRVQEDState *s); + +int coroutine_fn GRAPH_RDLOCK +qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n); + +int coroutine_fn GRAPH_RDLOCK +qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index, unsigned int n); + +int coroutine_fn GRAPH_RDLOCK +qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset); + +int coroutine_fn GRAPH_RDLOCK +qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset); + +int coroutine_fn GRAPH_RDLOCK +qed_write_l2_table(BDRVQEDState *s, QEDRequest *request, unsigned int index, + unsigned int n, bool flush); + +int coroutine_fn GRAPH_RDLOCK +qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request, + unsigned int index, unsigned int n, bool flush); /** * Cluster functions */ -int coroutine_fn qed_find_cluster(BDRVQEDState *s, QEDRequest *request, - uint64_t pos, size_t *len, - uint64_t *img_offset); +int coroutine_fn GRAPH_RDLOCK +qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos, + size_t *len, uint64_t *img_offset); /** * Consistency check */ -int coroutine_fn qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix); +int coroutine_fn GRAPH_RDLOCK +qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix); QEDTable *qed_alloc_table(BDRVQEDState *s); diff --git a/block/quorum.c b/block/quorum.c index f33f30d36b..db8fe891c4 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -161,11 +161,10 @@ static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b) return a->l == b->l; } -static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs, - QEMUIOVector *qiov, - uint64_t offset, - uint64_t bytes, - int flags) +static QuorumAIOCB *coroutine_fn quorum_aio_get(BlockDriverState *bs, + QEMUIOVector *qiov, + uint64_t offset, uint64_t bytes, + int flags) { BDRVQuorumState *s = bs->opaque; QuorumAIOCB *acb = g_new(QuorumAIOCB, 1); @@ -203,11 +202,11 @@ static void quorum_report_bad(QuorumOpType type, uint64_t offset, msg = strerror(-ret); } - qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name, start_sector, + qapi_event_send_quorum_report_bad(type, msg, node_name, start_sector, end_sector - start_sector); } -static void quorum_report_failure(QuorumAIOCB *acb) +static void GRAPH_RDLOCK quorum_report_failure(QuorumAIOCB *acb) { const char *reference = bdrv_get_device_or_node_name(acb->bs); int64_t start_sector = acb->offset / BDRV_SECTOR_SIZE; @@ -220,7 +219,7 @@ static void quorum_report_failure(QuorumAIOCB *acb) static int quorum_vote_error(QuorumAIOCB *acb); -static bool quorum_has_too_much_io_failed(QuorumAIOCB *acb) +static bool GRAPH_RDLOCK quorum_has_too_much_io_failed(QuorumAIOCB *acb) { BDRVQuorumState *s = acb->bs->opaque; @@ -233,8 +232,6 @@ static bool quorum_has_too_much_io_failed(QuorumAIOCB *acb) return false; } -static int read_fifo_child(QuorumAIOCB *acb); - static void quorum_copy_qiov(QEMUIOVector *dest, QEMUIOVector *source) { int i; @@ -273,7 +270,11 @@ static void quorum_report_bad_versions(BDRVQuorumState *s, } } -static void quorum_rewrite_entry(void *opaque) +/* + * This function can count as GRAPH_RDLOCK because read_quorum_children() holds + * the graph lock and keeps it until this coroutine has terminated. + */ +static void coroutine_fn GRAPH_RDLOCK quorum_rewrite_entry(void *opaque) { QuorumCo *co = opaque; QuorumAIOCB *acb = co->acb; @@ -293,8 +294,8 @@ static void quorum_rewrite_entry(void *opaque) } } -static bool quorum_rewrite_bad_versions(QuorumAIOCB *acb, - QuorumVoteValue *value) +static bool coroutine_fn GRAPH_RDLOCK +quorum_rewrite_bad_versions(QuorumAIOCB *acb, QuorumVoteValue *value) { QuorumVoteVersion *version; QuorumVoteItem *item; @@ -494,7 +495,7 @@ static int quorum_vote_error(QuorumAIOCB *acb) return ret; } -static void quorum_vote(QuorumAIOCB *acb) +static void coroutine_fn GRAPH_RDLOCK quorum_vote(QuorumAIOCB *acb) { bool quorum = true; int i, j, ret; @@ -574,7 +575,11 @@ free_exit: quorum_free_vote_list(&acb->votes); } -static void read_quorum_children_entry(void *opaque) +/* + * This function can count as GRAPH_RDLOCK because read_quorum_children() holds + * the graph lock and keeps it until this coroutine has terminated. + */ +static void coroutine_fn GRAPH_RDLOCK read_quorum_children_entry(void *opaque) { QuorumCo *co = opaque; QuorumAIOCB *acb = co->acb; @@ -602,7 +607,7 @@ static void read_quorum_children_entry(void *opaque) } } -static int read_quorum_children(QuorumAIOCB *acb) +static int coroutine_fn GRAPH_RDLOCK read_quorum_children(QuorumAIOCB *acb) { BDRVQuorumState *s = acb->bs->opaque; int i; @@ -643,7 +648,7 @@ static int read_quorum_children(QuorumAIOCB *acb) return acb->vote_ret; } -static int read_fifo_child(QuorumAIOCB *acb) +static int coroutine_fn GRAPH_RDLOCK read_fifo_child(QuorumAIOCB *acb) { BDRVQuorumState *s = acb->bs->opaque; int n, ret; @@ -664,8 +669,9 @@ static int read_fifo_child(QuorumAIOCB *acb) return ret; } -static int quorum_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, - QEMUIOVector *qiov, BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +quorum_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVQuorumState *s = bs->opaque; QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags); @@ -684,7 +690,11 @@ static int quorum_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, return ret; } -static void write_quorum_entry(void *opaque) +/* + * This function can count as GRAPH_RDLOCK because quorum_co_pwritev() holds the + * graph lock and keeps it until this coroutine has terminated. + */ +static void coroutine_fn GRAPH_RDLOCK write_quorum_entry(void *opaque) { QuorumCo *co = opaque; QuorumAIOCB *acb = co->acb; @@ -715,9 +725,9 @@ static void write_quorum_entry(void *opaque) } } -static int quorum_co_pwritev(BlockDriverState *bs, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +quorum_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVQuorumState *s = bs->opaque; QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags); @@ -746,27 +756,28 @@ static int quorum_co_pwritev(BlockDriverState *bs, int64_t offset, return ret; } -static int quorum_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, - int64_t bytes, BdrvRequestFlags flags) - +static int coroutine_fn GRAPH_RDLOCK +quorum_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + BdrvRequestFlags flags) { return quorum_co_pwritev(bs, offset, bytes, NULL, flags | BDRV_REQ_ZERO_WRITE); } -static int64_t quorum_getlength(BlockDriverState *bs) +static int64_t coroutine_fn GRAPH_RDLOCK +quorum_co_getlength(BlockDriverState *bs) { BDRVQuorumState *s = bs->opaque; int64_t result; int i; /* check that all file have the same length */ - result = bdrv_getlength(s->children[0]->bs); + result = bdrv_co_getlength(s->children[0]->bs); if (result < 0) { return result; } for (i = 1; i < s->num_children; i++) { - int64_t value = bdrv_getlength(s->children[i]->bs); + int64_t value = bdrv_co_getlength(s->children[i]->bs); if (value < 0) { return value; } @@ -778,7 +789,7 @@ static int64_t quorum_getlength(BlockDriverState *bs) return result; } -static coroutine_fn int quorum_co_flush(BlockDriverState *bs) +static coroutine_fn GRAPH_RDLOCK int quorum_co_flush(BlockDriverState *bs) { BDRVQuorumState *s = bs->opaque; QuorumVoteVersion *winner = NULL; @@ -814,8 +825,8 @@ static coroutine_fn int quorum_co_flush(BlockDriverState *bs) return result; } -static bool quorum_recurse_can_replace(BlockDriverState *bs, - BlockDriverState *to_replace) +static bool GRAPH_RDLOCK +quorum_recurse_can_replace(BlockDriverState *bs, BlockDriverState *to_replace) { BDRVQuorumState *s = bs->opaque; int i; @@ -1026,12 +1037,14 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, close_exit: /* cleanup on error */ + bdrv_graph_wrlock(); for (i = 0; i < s->num_children; i++) { if (!opened[i]) { continue; } bdrv_unref_child(bs, s->children[i]); } + bdrv_graph_wrunlock(); g_free(s->children); g_free(opened); exit: @@ -1044,15 +1057,17 @@ static void quorum_close(BlockDriverState *bs) BDRVQuorumState *s = bs->opaque; int i; + bdrv_graph_wrlock(); for (i = 0; i < s->num_children; i++) { bdrv_unref_child(bs, s->children[i]); } + bdrv_graph_wrunlock(); g_free(s->children); } -static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, - Error **errp) +static void GRAPH_WRLOCK +quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, Error **errp) { BDRVQuorumState *s = bs->opaque; BdrvChild *child; @@ -1078,8 +1093,6 @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, } s->next_child_index++; - bdrv_drained_begin(bs); - /* We can safely add the child now */ bdrv_ref(child_bs); @@ -1087,18 +1100,15 @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, BDRV_CHILD_DATA, errp); if (child == NULL) { s->next_child_index--; - goto out; + return; } s->children = g_renew(BdrvChild *, s->children, s->num_children + 1); s->children[s->num_children++] = child; quorum_refresh_flags(bs); - -out: - bdrv_drained_end(bs); } -static void quorum_del_child(BlockDriverState *bs, BdrvChild *child, - Error **errp) +static void GRAPH_WRLOCK +quorum_del_child(BlockDriverState *bs, BdrvChild *child, Error **errp) { BDRVQuorumState *s = bs->opaque; char indexstr[INDEXSTR_LEN]; @@ -1128,16 +1138,14 @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child, s->next_child_index--; } - bdrv_drained_begin(bs); - /* We can safely remove this child now */ memmove(&s->children[i], &s->children[i + 1], (s->num_children - i - 1) * sizeof(BdrvChild *)); s->children = g_renew(BdrvChild *, s->children, --s->num_children); + bdrv_unref_child(bs, child); quorum_refresh_flags(bs); - bdrv_drained_end(bs); } static void quorum_gather_child_options(BlockDriverState *bs, QDict *target, @@ -1217,11 +1225,10 @@ static void quorum_child_perm(BlockDriverState *bs, BdrvChild *c, * return BDRV_BLOCK_ZERO if *all* children agree that a certain * region contains zeroes, and BDRV_BLOCK_DATA otherwise. */ -static int coroutine_fn quorum_co_block_status(BlockDriverState *bs, - bool want_zero, - int64_t offset, int64_t count, - int64_t *pnum, int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +quorum_co_block_status(BlockDriverState *bs, bool want_zero, + int64_t offset, int64_t count, + int64_t *pnum, int64_t *map, BlockDriverState **file) { BDRVQuorumState *s = bs->opaque; int i, ret; @@ -1283,7 +1290,7 @@ static BlockDriver bdrv_quorum = { .bdrv_co_flush = quorum_co_flush, - .bdrv_getlength = quorum_getlength, + .bdrv_co_getlength = quorum_co_getlength, .bdrv_co_preadv = quorum_co_preadv, .bdrv_co_pwritev = quorum_co_pwritev, diff --git a/block/raw-format.c b/block/raw-format.c index 69fd650eaf..ac7e8495f6 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -27,6 +27,7 @@ */ #include "qemu/osdep.h" +#include "block/block-io.h" #include "block/block_int.h" #include "qapi/error.h" #include "qemu/module.h" @@ -94,9 +95,9 @@ end: return ret; } -static int raw_apply_options(BlockDriverState *bs, BDRVRawState *s, - uint64_t offset, bool has_size, uint64_t size, - Error **errp) +static int GRAPH_RDLOCK +raw_apply_options(BlockDriverState *bs, BDRVRawState *s, uint64_t offset, + bool has_size, uint64_t size, Error **errp) { int64_t real_size = 0; @@ -144,6 +145,9 @@ static int raw_reopen_prepare(BDRVReopenState *reopen_state, uint64_t offset, size; int ret; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + assert(reopen_state != NULL); assert(reopen_state->bs != NULL); @@ -202,9 +206,9 @@ static inline int raw_adjust_offset(BlockDriverState *bs, int64_t *offset, return 0; } -static int coroutine_fn raw_co_preadv(BlockDriverState *bs, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +raw_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { int ret; @@ -213,13 +217,13 @@ static int coroutine_fn raw_co_preadv(BlockDriverState *bs, int64_t offset, return ret; } - BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); + BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_AIO); return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); } -static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +raw_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { void *buf = NULL; BlockDriver *drv; @@ -258,6 +262,8 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset, qemu_iovec_add(&local_qiov, buf, 512); qemu_iovec_concat(&local_qiov, qiov, 512, qiov->size - 512); qiov = &local_qiov; + + flags &= ~BDRV_REQ_REGISTERED_BUF; } ret = raw_adjust_offset(bs, &offset, bytes, true); @@ -265,7 +271,7 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset, goto fail; } - BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); + BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); fail: @@ -276,11 +282,10 @@ fail: return ret; } -static int coroutine_fn raw_co_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, - int64_t bytes, int64_t *pnum, - int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +raw_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, + int64_t bytes, int64_t *pnum, int64_t *map, + BlockDriverState **file) { BDRVRawState *s = bs->opaque; *pnum = bytes; @@ -289,9 +294,9 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs, return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID; } -static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int64_t bytes, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +raw_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + BdrvRequestFlags flags) { int ret; @@ -302,8 +307,8 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs, return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); } -static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, - int64_t offset, int64_t bytes) +static int coroutine_fn GRAPH_RDLOCK +raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { int ret; @@ -314,14 +319,37 @@ static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, return bdrv_co_pdiscard(bs->file, offset, bytes); } -static int64_t raw_getlength(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK +raw_co_zone_report(BlockDriverState *bs, int64_t offset, + unsigned int *nr_zones, + BlockZoneDescriptor *zones) +{ + return bdrv_co_zone_report(bs->file->bs, offset, nr_zones, zones); +} + +static int coroutine_fn GRAPH_RDLOCK +raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, + int64_t offset, int64_t len) +{ + return bdrv_co_zone_mgmt(bs->file->bs, op, offset, len); +} + +static int coroutine_fn GRAPH_RDLOCK +raw_co_zone_append(BlockDriverState *bs,int64_t *offset, QEMUIOVector *qiov, + BdrvRequestFlags flags) +{ + return bdrv_co_zone_append(bs->file->bs, offset, qiov, flags); +} + +static int64_t coroutine_fn GRAPH_RDLOCK +raw_co_getlength(BlockDriverState *bs) { int64_t len; BDRVRawState *s = bs->opaque; /* Update size. It should not change unless the file was externally * modified. */ - len = bdrv_getlength(bs->file->bs); + len = bdrv_co_getlength(bs->file->bs); if (len < 0) { return len; } @@ -365,13 +393,16 @@ static BlockMeasureInfo *raw_measure(QemuOpts *opts, BlockDriverState *in_bs, return info; } -static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn GRAPH_RDLOCK +raw_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { - return bdrv_get_info(bs->file->bs, bdi); + return bdrv_co_get_info(bs->file->bs, bdi); } -static void raw_refresh_limits(BlockDriverState *bs, Error **errp) +static void GRAPH_RDLOCK raw_refresh_limits(BlockDriverState *bs, Error **errp) { + bs->bl.has_variable_length = bs->file->bs->bl.has_variable_length; + if (bs->probed) { /* To make it easier to protect the first sector, any probed * image is restricted to read-modify-write on sub-sector @@ -380,9 +411,9 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) } } -static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, - bool exact, PreallocMode prealloc, - BdrvRequestFlags flags, Error **errp) +static int coroutine_fn GRAPH_RDLOCK +raw_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, + PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) { BDRVRawState *s = bs->opaque; @@ -401,17 +432,20 @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, return bdrv_co_truncate(bs->file, offset, exact, prealloc, flags, errp); } -static void raw_eject(BlockDriverState *bs, bool eject_flag) +static void coroutine_fn GRAPH_RDLOCK +raw_co_eject(BlockDriverState *bs, bool eject_flag) { - bdrv_eject(bs->file->bs, eject_flag); + bdrv_co_eject(bs->file->bs, eject_flag); } -static void raw_lock_medium(BlockDriverState *bs, bool locked) +static void coroutine_fn GRAPH_RDLOCK +raw_co_lock_medium(BlockDriverState *bs, bool locked) { - bdrv_lock_medium(bs->file->bs, locked); + bdrv_co_lock_medium(bs->file->bs, locked); } -static int raw_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) +static int coroutine_fn GRAPH_RDLOCK +raw_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) { BDRVRawState *s = bs->opaque; if (s->offset || s->has_size) { @@ -420,17 +454,16 @@ static int raw_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) return bdrv_co_ioctl(bs->file->bs, req, buf); } -static int raw_has_zero_init(BlockDriverState *bs) +static int GRAPH_RDLOCK raw_has_zero_init(BlockDriverState *bs) { return bdrv_has_zero_init(bs->file->bs); } -static int coroutine_fn raw_co_create_opts(BlockDriver *drv, - const char *filename, - QemuOpts *opts, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +raw_co_create_opts(BlockDriver *drv, const char *filename, + QemuOpts *opts, Error **errp) { - return bdrv_create_file(filename, opts, errp); + return bdrv_co_create_file(filename, opts, errp); } static int raw_open(BlockDriverState *bs, QDict *options, int flags, @@ -442,6 +475,8 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, BdrvChildRole file_role; int ret; + GLOBAL_STATE_CODE(); + ret = raw_read_options(options, &offset, &has_size, &size, errp); if (ret < 0) { return ret; @@ -457,13 +492,15 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, file_role = BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY; } - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - file_role, false, errp); + bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + file_role, false, errp); + + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!bs->file) { return -EINVAL; } - bs->sg = bs->file->bs->sg; + bs->sg = bdrv_is_sg(bs->file->bs); 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 | @@ -489,7 +526,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, return ret; } - if (bs->sg && (s->offset || s->has_size)) { + if (bdrv_is_sg(bs) && (s->offset || s->has_size)) { error_setg(errp, "Cannot use offset/size with SCSI generic devices"); return -EINVAL; } @@ -505,7 +542,8 @@ static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) return 1; } -static int raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) +static int GRAPH_RDLOCK +raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) { BDRVRawState *s = bs->opaque; int ret; @@ -522,7 +560,8 @@ static int raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) return 0; } -static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) +static int GRAPH_RDLOCK +raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) { BDRVRawState *s = bs->opaque; if (s->offset || s->has_size) { @@ -531,14 +570,12 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) return bdrv_probe_geometry(bs->file->bs, geo); } -static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, - BdrvChild *src, - int64_t src_offset, - BdrvChild *dst, - int64_t dst_offset, - int64_t bytes, - BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags) +static int coroutine_fn GRAPH_RDLOCK +raw_co_copy_range_from(BlockDriverState *bs, + BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { int ret; @@ -550,14 +587,12 @@ static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, bytes, read_flags, write_flags); } -static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, - BdrvChild *src, - int64_t src_offset, - BdrvChild *dst, - int64_t dst_offset, - int64_t bytes, - BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags) +static int coroutine_fn GRAPH_RDLOCK +raw_co_copy_range_to(BlockDriverState *bs, + BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) { int ret; @@ -576,7 +611,7 @@ static const char *const raw_strong_runtime_opts[] = { NULL }; -static void raw_cancel_in_flight(BlockDriverState *bs) +static void GRAPH_RDLOCK raw_cancel_in_flight(BlockDriverState *bs) { bdrv_cancel_in_flight(bs->file->bs); } @@ -603,6 +638,7 @@ static void raw_child_perm(BlockDriverState *bs, BdrvChild *c, BlockDriver bdrv_raw = { .format_name = "raw", .instance_size = sizeof(BDRVRawState), + .supports_zoned_children = true, .bdrv_probe = &raw_probe, .bdrv_reopen_prepare = &raw_reopen_prepare, .bdrv_reopen_commit = &raw_reopen_commit, @@ -614,20 +650,22 @@ BlockDriver bdrv_raw = { .bdrv_co_pwritev = &raw_co_pwritev, .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes, .bdrv_co_pdiscard = &raw_co_pdiscard, + .bdrv_co_zone_report = &raw_co_zone_report, + .bdrv_co_zone_mgmt = &raw_co_zone_mgmt, + .bdrv_co_zone_append = &raw_co_zone_append, .bdrv_co_block_status = &raw_co_block_status, .bdrv_co_copy_range_from = &raw_co_copy_range_from, .bdrv_co_copy_range_to = &raw_co_copy_range_to, .bdrv_co_truncate = &raw_co_truncate, - .bdrv_getlength = &raw_getlength, + .bdrv_co_getlength = &raw_co_getlength, .is_format = true, - .has_variable_length = true, .bdrv_measure = &raw_measure, - .bdrv_get_info = &raw_get_info, + .bdrv_co_get_info = &raw_co_get_info, .bdrv_refresh_limits = &raw_refresh_limits, .bdrv_probe_blocksizes = &raw_probe_blocksizes, .bdrv_probe_geometry = &raw_probe_geometry, - .bdrv_eject = &raw_eject, - .bdrv_lock_medium = &raw_lock_medium, + .bdrv_co_eject = &raw_co_eject, + .bdrv_co_lock_medium = &raw_co_lock_medium, .bdrv_co_ioctl = &raw_co_ioctl, .create_opts = &raw_create_opts, .bdrv_has_zero_init = &raw_has_zero_init, diff --git a/block/rbd.c b/block/rbd.c index 6caf35cbba..84bb2fa5d7 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -18,6 +18,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/option.h" +#include "block/block-io.h" #include "block/block_int.h" #include "block/qdict.h" #include "crypto/secret.h" @@ -71,6 +72,16 @@ static const char rbd_luks2_header_verification[ 'L', 'U', 'K', 'S', 0xBA, 0xBE, 0, 2 }; +static const char rbd_layered_luks_header_verification[ + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = { + 'R', 'B', 'D', 'L', 0xBA, 0xBE, 0, 1 +}; + +static const char rbd_layered_luks2_header_verification[ + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = { + 'R', 'B', 'D', 'L', 0xBA, 0xBE, 0, 2 +}; + typedef enum { RBD_AIO_READ, RBD_AIO_WRITE, @@ -385,7 +396,6 @@ static int qemu_rbd_encryption_format(rbd_image_t image, { int r = 0; g_autofree char *passphrase = NULL; - size_t passphrase_len; rbd_encryption_format_t format; rbd_encryption_options_t opts; rbd_encryption_luks1_format_options_t luks_opts; @@ -407,12 +417,12 @@ static int qemu_rbd_encryption_format(rbd_image_t image, opts_size = sizeof(luks_opts); r = qemu_rbd_convert_luks_create_options( qapi_RbdEncryptionCreateOptionsLUKS_base(&encrypt->u.luks), - &luks_opts.alg, &passphrase, &passphrase_len, errp); + &luks_opts.alg, &passphrase, &luks_opts.passphrase_size, + errp); if (r < 0) { return r; } luks_opts.passphrase = passphrase; - luks_opts.passphrase_size = passphrase_len; break; } case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2: { @@ -423,12 +433,12 @@ static int qemu_rbd_encryption_format(rbd_image_t image, r = qemu_rbd_convert_luks_create_options( qapi_RbdEncryptionCreateOptionsLUKS2_base( &encrypt->u.luks2), - &luks2_opts.alg, &passphrase, &passphrase_len, errp); + &luks2_opts.alg, &passphrase, &luks2_opts.passphrase_size, + errp); if (r < 0) { return r; } luks2_opts.passphrase = passphrase; - luks2_opts.passphrase_size = passphrase_len; break; } default: { @@ -467,9 +477,11 @@ static int qemu_rbd_encryption_load(rbd_image_t image, { int r = 0; g_autofree char *passphrase = NULL; - size_t passphrase_len; rbd_encryption_luks1_format_options_t luks_opts; rbd_encryption_luks2_format_options_t luks2_opts; +#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2 + rbd_encryption_luks_format_options_t luks_any_opts; +#endif rbd_encryption_format_t format; rbd_encryption_options_t opts; size_t opts_size; @@ -482,12 +494,11 @@ static int qemu_rbd_encryption_load(rbd_image_t image, opts_size = sizeof(luks_opts); r = qemu_rbd_convert_luks_options( qapi_RbdEncryptionOptionsLUKS_base(&encrypt->u.luks), - &passphrase, &passphrase_len, errp); + &passphrase, &luks_opts.passphrase_size, errp); if (r < 0) { return r; } luks_opts.passphrase = passphrase; - luks_opts.passphrase_size = passphrase_len; break; } case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2: { @@ -497,14 +508,29 @@ static int qemu_rbd_encryption_load(rbd_image_t image, opts_size = sizeof(luks2_opts); r = qemu_rbd_convert_luks_options( qapi_RbdEncryptionOptionsLUKS2_base(&encrypt->u.luks2), - &passphrase, &passphrase_len, errp); + &passphrase, &luks2_opts.passphrase_size, errp); if (r < 0) { return r; } luks2_opts.passphrase = passphrase; - luks2_opts.passphrase_size = passphrase_len; break; } +#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2 + case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY: { + memset(&luks_any_opts, 0, sizeof(luks_any_opts)); + format = RBD_ENCRYPTION_FORMAT_LUKS; + opts = &luks_any_opts; + opts_size = sizeof(luks_any_opts); + r = qemu_rbd_convert_luks_options( + qapi_RbdEncryptionOptionsLUKSAny_base(&encrypt->u.luks_any), + &passphrase, &luks_any_opts.passphrase_size, errp); + if (r < 0) { + return r; + } + luks_any_opts.passphrase = passphrase; + break; + } +#endif default: { r = -ENOTSUP; error_setg_errno( @@ -522,6 +548,128 @@ static int qemu_rbd_encryption_load(rbd_image_t image, return 0; } + +#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2 +static int qemu_rbd_encryption_load2(rbd_image_t image, + RbdEncryptionOptions *encrypt, + Error **errp) +{ + int r = 0; + int encrypt_count = 1; + int i; + RbdEncryptionOptions *curr_encrypt; + rbd_encryption_spec_t *specs; + rbd_encryption_luks1_format_options_t *luks_opts; + rbd_encryption_luks2_format_options_t *luks2_opts; + rbd_encryption_luks_format_options_t *luks_any_opts; + + /* count encryption options */ + for (curr_encrypt = encrypt->parent; curr_encrypt; + curr_encrypt = curr_encrypt->parent) { + ++encrypt_count; + } + + specs = g_new0(rbd_encryption_spec_t, encrypt_count); + + curr_encrypt = encrypt; + for (i = 0; i < encrypt_count; ++i) { + switch (curr_encrypt->format) { + case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS: { + specs[i].format = RBD_ENCRYPTION_FORMAT_LUKS1; + + luks_opts = g_new0(rbd_encryption_luks1_format_options_t, 1); + specs[i].opts = luks_opts; + specs[i].opts_size = sizeof(*luks_opts); + + r = qemu_rbd_convert_luks_options( + qapi_RbdEncryptionOptionsLUKS_base( + &curr_encrypt->u.luks), + (char **)&luks_opts->passphrase, + &luks_opts->passphrase_size, + errp); + break; + } + case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2: { + specs[i].format = RBD_ENCRYPTION_FORMAT_LUKS2; + + luks2_opts = g_new0(rbd_encryption_luks2_format_options_t, 1); + specs[i].opts = luks2_opts; + specs[i].opts_size = sizeof(*luks2_opts); + + r = qemu_rbd_convert_luks_options( + qapi_RbdEncryptionOptionsLUKS2_base( + &curr_encrypt->u.luks2), + (char **)&luks2_opts->passphrase, + &luks2_opts->passphrase_size, + errp); + break; + } + case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY: { + specs[i].format = RBD_ENCRYPTION_FORMAT_LUKS; + + luks_any_opts = g_new0(rbd_encryption_luks_format_options_t, 1); + specs[i].opts = luks_any_opts; + specs[i].opts_size = sizeof(*luks_any_opts); + + r = qemu_rbd_convert_luks_options( + qapi_RbdEncryptionOptionsLUKSAny_base( + &curr_encrypt->u.luks_any), + (char **)&luks_any_opts->passphrase, + &luks_any_opts->passphrase_size, + errp); + break; + } + default: { + r = -ENOTSUP; + error_setg_errno( + errp, -r, "unknown image encryption format: %u", + curr_encrypt->format); + } + } + + if (r < 0) { + goto exit; + } + + curr_encrypt = curr_encrypt->parent; + } + + r = rbd_encryption_load2(image, specs, encrypt_count); + if (r < 0) { + error_setg_errno(errp, -r, "layered encryption load fail"); + goto exit; + } + +exit: + for (i = 0; i < encrypt_count; ++i) { + if (!specs[i].opts) { + break; + } + + switch (specs[i].format) { + case RBD_ENCRYPTION_FORMAT_LUKS1: { + luks_opts = specs[i].opts; + g_free((void *)luks_opts->passphrase); + break; + } + case RBD_ENCRYPTION_FORMAT_LUKS2: { + luks2_opts = specs[i].opts; + g_free((void *)luks2_opts->passphrase); + break; + } + case RBD_ENCRYPTION_FORMAT_LUKS: { + luks_any_opts = specs[i].opts; + g_free((void *)luks_any_opts->passphrase); + break; + } + } + + g_free(specs[i].opts); + } + g_free(specs); + return r; +} +#endif #endif /* FIXME Deprecate and remove keypairs or make it available in QMP. */ @@ -536,13 +684,13 @@ static int qemu_rbd_do_create(BlockdevCreateOptions *options, int ret; assert(options->driver == BLOCKDEV_DRIVER_RBD); - if (opts->location->has_snapshot) { + if (opts->location->snapshot) { error_setg(errp, "Can't use snapshot name for image creation"); return -EINVAL; } #ifndef LIBRBD_SUPPORTS_ENCRYPTION - if (opts->has_encrypt) { + if (opts->encrypt) { error_setg(errp, "RBD library does not support image encryption"); return -ENOTSUP; } @@ -574,7 +722,7 @@ static int qemu_rbd_do_create(BlockdevCreateOptions *options, } #ifdef LIBRBD_SUPPORTS_ENCRYPTION - if (opts->has_encrypt) { + if (opts->encrypt) { rbd_image_t image; ret = rbd_open(io_ctx, opts->location->image, &image, NULL); @@ -686,7 +834,6 @@ static int coroutine_fn qemu_rbd_co_create_opts(BlockDriver *drv, goto exit; } rbd_opts->encrypt = encrypt; - rbd_opts->has_encrypt = !!encrypt; /* * Caution: while qdict_get_try_str() is fine, getting non-string @@ -697,11 +844,8 @@ static int coroutine_fn qemu_rbd_co_create_opts(BlockDriver *drv, 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->q_namespace = g_strdup(qdict_get_try_str(options, "namespace")); - loc->has_q_namespace = !!loc->q_namespace; loc->image = g_strdup(qdict_get_try_str(options, "image")); keypairs = qdict_get_try_str(options, "=keyvalue-pairs"); @@ -767,7 +911,6 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, return -EINVAL; } opts->key_secret = g_strdup(secretid); - opts->has_key_secret = true; } mon_host = qemu_rbd_mon_host(opts, &local_err); @@ -785,7 +928,7 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, /* try default location when conf=NULL, but ignore failure */ r = rados_conf_read_file(*cluster, opts->conf); - if (opts->has_conf && r < 0) { + if (opts->conf && r < 0) { error_setg_errno(errp, -r, "error reading conf file %s", opts->conf); goto failed_shutdown; } @@ -831,6 +974,26 @@ 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; } + +#ifdef HAVE_RBD_NAMESPACE_EXISTS + if (opts->q_namespace && strlen(opts->q_namespace) > 0) { + bool exists; + + r = rbd_namespace_exists(*io_ctx, opts->q_namespace, &exists); + if (r < 0) { + error_setg_errno(errp, -r, "error checking namespace"); + goto failed_ioctx_destroy; + } + + if (!exists) { + error_setg(errp, "namespace '%s' does not exist", + opts->q_namespace); + r = -ENOENT; + goto failed_ioctx_destroy; + } + } +#endif + /* * Set the namespace after opening the io context on the pool, * if nspace == NULL or if nspace == "", it is just as we did nothing @@ -840,6 +1003,10 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, r = 0; goto out; +#ifdef HAVE_RBD_NAMESPACE_EXISTS +failed_ioctx_destroy: + rados_ioctx_destroy(*io_ctx); +#endif failed_shutdown: rados_shutdown(*cluster); out: @@ -967,9 +1134,18 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, goto failed_open; } - if (opts->has_encrypt) { + if (opts->encrypt) { #ifdef LIBRBD_SUPPORTS_ENCRYPTION - r = qemu_rbd_encryption_load(s->image, opts->encrypt, errp); + if (opts->encrypt->parent) { +#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2 + r = qemu_rbd_encryption_load2(s->image, opts->encrypt, errp); +#else + r = -ENOTSUP; + error_setg(errp, "RBD library does not support layered encryption"); +#endif + } else { + r = qemu_rbd_encryption_load(s->image, opts->encrypt, errp); + } if (r < 0) { goto failed_post_open; } @@ -992,7 +1168,9 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, /* If we are using an rbd snapshot, we must be r/o, otherwise * leave as-is */ if (s->snap != NULL) { + bdrv_graph_rdlock_main_loop(); r = bdrv_apply_auto_read_only(bs, "rbd snapshots are read-only", errp); + bdrv_graph_rdunlock_main_loop(); if (r < 0) { goto failed_post_open; } @@ -1032,6 +1210,8 @@ static int qemu_rbd_reopen_prepare(BDRVReopenState *state, BDRVRBDState *s = state->bs->opaque; int ret = 0; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (s->snap && state->flags & BDRV_O_RDWR) { error_setg(errp, "Cannot change node '%s' to r/w when using RBD snapshot", @@ -1114,7 +1294,7 @@ static int coroutine_fn qemu_rbd_start_co(BlockDriverState *bs, * operations that exceed the current size. */ if (offset + bytes > s->image_size) { - int r = qemu_rbd_resize(bs, offset + bytes); + r = qemu_rbd_resize(bs, offset + bytes); if (r < 0) { return r; } @@ -1220,7 +1400,8 @@ coroutine_fn qemu_rbd_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, } #endif -static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +qemu_rbd_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVRBDState *s = bs->opaque; bdi->cluster_size = s->object_size; @@ -1260,6 +1441,16 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs, spec_info->u.rbd.data->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; spec_info->u.rbd.data->has_encryption_format = true; + } else if (memcmp(buf, rbd_layered_luks_header_verification, + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { + spec_info->u.rbd.data->encryption_format = + RBD_IMAGE_ENCRYPTION_FORMAT_LUKS; + spec_info->u.rbd.data->has_encryption_format = true; + } else if (memcmp(buf, rbd_layered_luks2_header_verification, + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { + spec_info->u.rbd.data->encryption_format = + RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; + spec_info->u.rbd.data->has_encryption_format = true; } else { spec_info->u.rbd.data->has_encryption_format = false; } @@ -1410,7 +1601,7 @@ static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs, return status; } -static int64_t qemu_rbd_getlength(BlockDriverState *bs) +static int64_t coroutine_fn qemu_rbd_co_getlength(BlockDriverState *bs) { BDRVRBDState *s = bs->opaque; int r; @@ -1631,10 +1822,10 @@ static BlockDriver bdrv_rbd = { .bdrv_co_create = qemu_rbd_co_create, .bdrv_co_create_opts = qemu_rbd_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, - .bdrv_get_info = qemu_rbd_getinfo, + .bdrv_co_get_info = qemu_rbd_co_get_info, .bdrv_get_specific_info = qemu_rbd_get_specific_info, .create_opts = &qemu_rbd_create_opts, - .bdrv_getlength = qemu_rbd_getlength, + .bdrv_co_getlength = qemu_rbd_co_getlength, .bdrv_co_truncate = qemu_rbd_co_truncate, .protocol_name = "rbd", diff --git a/block/replication.c b/block/replication.c index 55c8f894aa..ca6bd0a720 100644 --- a/block/replication.c +++ b/block/replication.c @@ -88,11 +88,9 @@ static int replication_open(BlockDriverState *bs, QDict *options, const char *mode; const char *top_id; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, errp); - if (!bs->file) { - return -EINVAL; + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; } ret = -EINVAL; @@ -142,6 +140,7 @@ static void replication_close(BlockDriverState *bs) { BDRVReplicationState *s = bs->opaque; Job *commit_job; + GLOBAL_STATE_CODE(); if (s->stage == BLOCK_REPLICATION_RUNNING) { replication_stop(s->rs, false, NULL); @@ -180,9 +179,10 @@ static void replication_child_perm(BlockDriverState *bs, BdrvChild *c, return; } -static int64_t replication_getlength(BlockDriverState *bs) +static int64_t coroutine_fn GRAPH_RDLOCK +replication_co_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + return bdrv_co_getlength(bs->file->bs); } static int replication_get_io_status(BDRVReplicationState *s) @@ -221,10 +221,9 @@ static int replication_return_value(BDRVReplicationState *s, int ret) return ret; } -static coroutine_fn int replication_co_readv(BlockDriverState *bs, - int64_t sector_num, - int remaining_sectors, - QEMUIOVector *qiov) +static int coroutine_fn GRAPH_RDLOCK +replication_co_readv(BlockDriverState *bs, int64_t sector_num, + int remaining_sectors, QEMUIOVector *qiov) { BDRVReplicationState *s = bs->opaque; int ret; @@ -245,11 +244,9 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs, return replication_return_value(s, ret); } -static coroutine_fn int replication_co_writev(BlockDriverState *bs, - int64_t sector_num, - int remaining_sectors, - QEMUIOVector *qiov, - int flags) +static int coroutine_fn GRAPH_RDLOCK +replication_co_writev(BlockDriverState *bs, int64_t sector_num, + int remaining_sectors, QEMUIOVector *qiov, int flags) { BDRVReplicationState *s = bs->opaque; QEMUIOVector hd_qiov; @@ -260,7 +257,6 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs, int ret; int64_t n; - assert(!flags); ret = replication_get_io_status(s); if (ret < 0) { goto out; @@ -280,10 +276,10 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs, while (remaining_sectors > 0) { int64_t count; - ret = bdrv_is_allocated_above(top->bs, base->bs, false, - sector_num * BDRV_SECTOR_SIZE, - remaining_sectors * BDRV_SECTOR_SIZE, - &count); + ret = bdrv_co_is_allocated_above(top->bs, base->bs, false, + sector_num * BDRV_SECTOR_SIZE, + remaining_sectors * BDRV_SECTOR_SIZE, + &count); if (ret < 0) { goto out1; } @@ -311,13 +307,16 @@ out: return ret; } -static void secondary_do_checkpoint(BlockDriverState *bs, Error **errp) +static void GRAPH_UNLOCKED +secondary_do_checkpoint(BlockDriverState *bs, Error **errp) { BDRVReplicationState *s = bs->opaque; - BdrvChild *active_disk = bs->file; + BdrvChild *active_disk; Error *local_err = NULL; int ret; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!s->backup_job) { error_setg(errp, "Backup job was cancelled unexpectedly"); return; @@ -329,6 +328,7 @@ static void secondary_do_checkpoint(BlockDriverState *bs, Error **errp) return; } + active_disk = bs->file; if (!active_disk->bs->drv) { error_setg(errp, "Active disk %s is ejected", active_disk->bs->node_name); @@ -364,6 +364,9 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable, BdrvChild *hidden_disk, *secondary_disk; BlockReopenQueue *reopen_queue = NULL; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + /* * s->hidden_disk and s->secondary_disk may not be set yet, as they will * only be set after the children are writable. @@ -376,9 +379,6 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable, s->orig_secondary_read_only = bdrv_is_read_only(secondary_disk->bs); } - bdrv_subtree_drained_begin(hidden_disk->bs); - bdrv_subtree_drained_begin(secondary_disk->bs); - if (s->orig_hidden_read_only) { QDict *opts = qdict_new(); qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable); @@ -394,18 +394,8 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable, } if (reopen_queue) { - AioContext *ctx = bdrv_get_aio_context(bs); - if (ctx != qemu_get_aio_context()) { - aio_context_release(ctx); - } bdrv_reopen_multiple(reopen_queue, errp); - if (ctx != qemu_get_aio_context()) { - aio_context_acquire(ctx); - } } - - bdrv_subtree_drained_end(hidden_disk->bs); - bdrv_subtree_drained_end(secondary_disk->bs); } static void backup_job_cleanup(BlockDriverState *bs) @@ -437,7 +427,8 @@ static void backup_job_completed(void *opaque, int ret) backup_job_cleanup(bs); } -static bool check_top_bs(BlockDriverState *top_bs, BlockDriverState *bs) +static bool GRAPH_RDLOCK +check_top_bs(BlockDriverState *top_bs, BlockDriverState *bs) { BdrvChild *child; @@ -464,12 +455,11 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, BlockDriverState *top_bs; BdrvChild *active_disk, *hidden_disk, *secondary_disk; int64_t active_length, hidden_length, disk_length; - AioContext *aio_context; Error *local_err = NULL; BackupPerf perf = { .use_copy_range = true, .max_workers = 1 }; - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); + GLOBAL_STATE_CODE(); + s = bs->opaque; if (s->stage == BLOCK_REPLICATION_DONE || @@ -479,20 +469,17 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, * 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); return; } if (s->mode != mode) { error_setg(errp, "The parameter mode's value is invalid, needs %d," " but got %d", s->mode, mode); - aio_context_release(aio_context); return; } @@ -500,26 +487,28 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, case REPLICATION_MODE_PRIMARY: break; case REPLICATION_MODE_SECONDARY: + bdrv_graph_rdlock_main_loop(); active_disk = bs->file; if (!active_disk || !active_disk->bs || !active_disk->bs->backing) { error_setg(errp, "Active disk doesn't have backing file"); - aio_context_release(aio_context); + bdrv_graph_rdunlock_main_loop(); return; } hidden_disk = active_disk->bs->backing; if (!hidden_disk->bs || !hidden_disk->bs->backing) { error_setg(errp, "Hidden disk doesn't have backing file"); - aio_context_release(aio_context); + bdrv_graph_rdunlock_main_loop(); return; } secondary_disk = hidden_disk->bs->backing; if (!secondary_disk->bs || !bdrv_has_blk(secondary_disk->bs)) { error_setg(errp, "The secondary disk doesn't have block backend"); - aio_context_release(aio_context); + bdrv_graph_rdunlock_main_loop(); return; } + bdrv_graph_rdunlock_main_loop(); /* verify the length */ active_length = bdrv_getlength(active_disk->bs); @@ -529,36 +518,38 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, active_length != hidden_length || hidden_length != disk_length) { error_setg(errp, "Active disk, hidden disk, secondary disk's length" " are not the same"); - aio_context_release(aio_context); return; } /* Must be true, or the bdrv_getlength() calls would have failed */ assert(active_disk->bs->drv && hidden_disk->bs->drv); + bdrv_graph_rdlock_main_loop(); if (!active_disk->bs->drv->bdrv_make_empty || !hidden_disk->bs->drv->bdrv_make_empty) { error_setg(errp, "Active disk or hidden disk doesn't support make_empty"); - aio_context_release(aio_context); + bdrv_graph_rdunlock_main_loop(); return; } + bdrv_graph_rdunlock_main_loop(); /* reopen the backing file in r/w mode */ reopen_backing_file(bs, true, &local_err); if (local_err) { error_propagate(errp, local_err); - aio_context_release(aio_context); return; } + bdrv_graph_wrlock(); + bdrv_ref(hidden_disk->bs); s->hidden_disk = bdrv_attach_child(bs, hidden_disk->bs, "hidden disk", &child_of_bds, BDRV_CHILD_DATA, &local_err); if (local_err) { error_propagate(errp, local_err); - aio_context_release(aio_context); + bdrv_graph_wrunlock(); return; } @@ -568,7 +559,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, BDRV_CHILD_DATA, &local_err); if (local_err) { error_propagate(errp, local_err); - aio_context_release(aio_context); + bdrv_graph_wrunlock(); return; } @@ -580,13 +571,15 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, if (!top_bs || !bdrv_is_root_node(top_bs) || !check_top_bs(top_bs, bs)) { error_setg(errp, "No top_bs or it is invalid"); + bdrv_graph_wrunlock(); reopen_backing_file(bs, false, NULL); - aio_context_release(aio_context); return; } bdrv_op_block_all(top_bs, s->blocker); bdrv_op_unblock(top_bs, BLOCK_OP_TYPE_DATAPLANE, s->blocker); + bdrv_graph_wrunlock(); + s->backup_job = backup_job_create( NULL, s->secondary_disk->bs, s->hidden_disk->bs, 0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, NULL, @@ -597,13 +590,11 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, if (local_err) { error_propagate(errp, local_err); backup_job_cleanup(bs); - aio_context_release(aio_context); return; } job_start(&s->backup_job->job); break; default: - aio_context_release(aio_context); abort(); } @@ -614,18 +605,12 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, } s->error = 0; - aio_context_release(aio_context); } static void replication_do_checkpoint(ReplicationState *rs, Error **errp) { BlockDriverState *bs = rs->opaque; - BDRVReplicationState *s; - AioContext *aio_context; - - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - s = bs->opaque; + BDRVReplicationState *s = bs->opaque; if (s->stage == BLOCK_REPLICATION_DONE || s->stage == BLOCK_REPLICATION_FAILOVER) { @@ -634,38 +619,28 @@ static void replication_do_checkpoint(ReplicationState *rs, Error **errp) * 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(bs, errp); } - aio_context_release(aio_context); } static void replication_get_error(ReplicationState *rs, Error **errp) { BlockDriverState *bs = rs->opaque; - BDRVReplicationState *s; - AioContext *aio_context; - - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - s = bs->opaque; + BDRVReplicationState *s = bs->opaque; if (s->stage == BLOCK_REPLICATION_NONE) { error_setg(errp, "Block replication is not running"); - aio_context_release(aio_context); return; } if (s->error) { error_setg(errp, "I/O error occurred"); - aio_context_release(aio_context); return; } - aio_context_release(aio_context); } static void replication_done(void *opaque, int ret) @@ -676,10 +651,13 @@ static void replication_done(void *opaque, int ret) if (ret == 0) { s->stage = BLOCK_REPLICATION_DONE; + bdrv_graph_wrlock(); bdrv_unref_child(bs, s->secondary_disk); s->secondary_disk = NULL; bdrv_unref_child(bs, s->hidden_disk); s->hidden_disk = NULL; + bdrv_graph_wrunlock(); + s->error = 0; } else { s->stage = BLOCK_REPLICATION_FAILOVER_FAILED; @@ -690,12 +668,7 @@ static void replication_done(void *opaque, int ret) static void replication_stop(ReplicationState *rs, bool failover, Error **errp) { BlockDriverState *bs = rs->opaque; - BDRVReplicationState *s; - AioContext *aio_context; - - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - s = bs->opaque; + BDRVReplicationState *s = bs->opaque; if (s->stage == BLOCK_REPLICATION_DONE || s->stage == BLOCK_REPLICATION_FAILOVER) { @@ -704,13 +677,11 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) * 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); return; } @@ -732,21 +703,20 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) if (!failover) { secondary_do_checkpoint(bs, errp); s->stage = BLOCK_REPLICATION_DONE; - aio_context_release(aio_context); return; } + bdrv_graph_rdlock_main_loop(); s->stage = BLOCK_REPLICATION_FAILOVER; s->commit_job = commit_active_start( NULL, bs->file->bs, s->secondary_disk->bs, JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, NULL, replication_done, bs, true, errp); + bdrv_graph_rdunlock_main_loop(); break; default: - aio_context_release(aio_context); abort(); } - aio_context_release(aio_context); } static const char *const replication_strong_runtime_opts[] = { @@ -764,13 +734,12 @@ static BlockDriver bdrv_replication = { .bdrv_close = replication_close, .bdrv_child_perm = replication_child_perm, - .bdrv_getlength = replication_getlength, + .bdrv_co_getlength = replication_co_getlength, .bdrv_co_readv = replication_co_readv, .bdrv_co_writev = replication_co_writev, .is_filter = true, - .has_variable_length = true, .strong_runtime_opts = replication_strong_runtime_opts, }; diff --git a/block/snapshot-access.c b/block/snapshot-access.c index 77b87c1946..84d0d13f86 100644 --- a/block/snapshot-access.c +++ b/block/snapshot-access.c @@ -26,7 +26,7 @@ #include "qemu/cutils.h" #include "block/block_int.h" -static coroutine_fn int +static int coroutine_fn GRAPH_RDLOCK snapshot_access_co_preadv_part(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, @@ -39,7 +39,7 @@ snapshot_access_co_preadv_part(BlockDriverState *bs, return bdrv_co_preadv_snapshot(bs->file, offset, bytes, qiov, qiov_offset); } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK snapshot_access_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, @@ -49,8 +49,8 @@ snapshot_access_co_block_status(BlockDriverState *bs, bytes, pnum, map, file); } -static int coroutine_fn snapshot_access_co_pdiscard(BlockDriverState *bs, - int64_t offset, int64_t bytes) +static int coroutine_fn GRAPH_RDLOCK +snapshot_access_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { return bdrv_co_pdiscard_snapshot(bs->file->bs, offset, bytes); } @@ -73,7 +73,7 @@ snapshot_access_co_pwritev_part(BlockDriverState *bs, } -static void snapshot_access_refresh_filename(BlockDriverState *bs) +static void GRAPH_RDLOCK snapshot_access_refresh_filename(BlockDriverState *bs) { pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->file->bs->filename); @@ -82,9 +82,12 @@ static void snapshot_access_refresh_filename(BlockDriverState *bs) static int snapshot_access_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY, - false, errp); + bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY, + false, errp); + + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!bs->file) { return -EINVAL; } @@ -108,7 +111,7 @@ static void snapshot_access_child_perm(BlockDriverState *bs, BdrvChild *c, *nshared = BLK_PERM_ALL; } -BlockDriver bdrv_snapshot_access_drv = { +static BlockDriver bdrv_snapshot_access_drv = { .format_name = "snapshot-access", .bdrv_open = snapshot_access_open, diff --git a/block/snapshot.c b/block/snapshot.c index d6f53c3065..8242b4abac 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -151,41 +151,33 @@ bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, } /** - * Return a pointer to the child BDS pointer to which we can fall + * Return a pointer to child of given BDS to which we can fall * back if the given BDS does not support snapshots. * Return NULL if there is no BDS to (safely) fall back to. - * - * We need to return an indirect pointer because bdrv_snapshot_goto() - * has to modify the BdrvChild pointer. */ -static BdrvChild **bdrv_snapshot_fallback_ptr(BlockDriverState *bs) +static BdrvChild * GRAPH_RDLOCK +bdrv_snapshot_fallback_child(BlockDriverState *bs) { - BdrvChild **fallback; + BdrvChild *fallback = bdrv_primary_child(bs); BdrvChild *child; - /* - * The only BdrvChild pointers that are safe to modify (and which - * we can thus return a reference to) are bs->file and - * bs->backing. - */ - fallback = &bs->file; - if (!*fallback && bs->drv && bs->drv->is_filter) { - fallback = &bs->backing; - } + GLOBAL_STATE_CODE(); + assert_bdrv_graph_readable(); - if (!*fallback) { + /* We allow fallback only to primary child */ + if (!fallback) { return NULL; } /* * Check that there are no other children that would need to be * snapshotted. If there are, it is not safe to fall back to - * *fallback. + * fallback. */ QLIST_FOREACH(child, &bs->children, next) { if (child->role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA | BDRV_CHILD_FILTERED) && - child != *fallback) + child != fallback) { return NULL; } @@ -194,17 +186,20 @@ static BdrvChild **bdrv_snapshot_fallback_ptr(BlockDriverState *bs) return fallback; } -static BlockDriverState *bdrv_snapshot_fallback(BlockDriverState *bs) +static BlockDriverState * GRAPH_RDLOCK +bdrv_snapshot_fallback(BlockDriverState *bs) { - BdrvChild **child_ptr = bdrv_snapshot_fallback_ptr(bs); - return child_ptr ? (*child_ptr)->bs : NULL; + GLOBAL_STATE_CODE(); + return child_bs(bdrv_snapshot_fallback_child(bs)); } int bdrv_can_snapshot(BlockDriverState *bs) { BlockDriver *drv = bs->drv; + GLOBAL_STATE_CODE(); - if (!drv || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { + + if (!drv || !bdrv_is_inserted(bs) || !bdrv_is_writable(bs)) { return 0; } @@ -244,7 +239,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, Error **errp) { BlockDriver *drv = bs->drv; - BdrvChild **fallback_ptr; + BdrvChild *fallback; int ret, open_ret; GLOBAL_STATE_CODE(); @@ -267,13 +262,16 @@ int bdrv_snapshot_goto(BlockDriverState *bs, return ret; } - fallback_ptr = bdrv_snapshot_fallback_ptr(bs); - if (fallback_ptr) { + bdrv_graph_rdlock_main_loop(); + fallback = bdrv_snapshot_fallback_child(bs); + bdrv_graph_rdunlock_main_loop(); + + if (fallback) { QDict *options; QDict *file_options; Error *local_err = NULL; - BlockDriverState *fallback_bs = (*fallback_ptr)->bs; - char *subqdict_prefix = g_strdup_printf("%s.", (*fallback_ptr)->name); + BlockDriverState *fallback_bs = fallback->bs; + char *subqdict_prefix = g_strdup_printf("%s.", fallback->name); options = qdict_clone_shallow(bs->options); @@ -284,8 +282,8 @@ int bdrv_snapshot_goto(BlockDriverState *bs, qobject_unref(file_options); g_free(subqdict_prefix); - /* Force .bdrv_open() below to re-attach fallback_bs on *fallback_ptr */ - qdict_put_str(options, (*fallback_ptr)->name, + /* Force .bdrv_open() below to re-attach fallback_bs on fallback */ + qdict_put_str(options, fallback->name, bdrv_get_node_name(fallback_bs)); /* Now close bs, apply the snapshot on fallback_bs, and re-open bs */ @@ -294,8 +292,9 @@ int bdrv_snapshot_goto(BlockDriverState *bs, } /* .bdrv_open() will re-attach it */ - bdrv_unref_child(bs, *fallback_ptr); - *fallback_ptr = NULL; + bdrv_graph_wrlock(); + bdrv_unref_child(bs, fallback); + bdrv_graph_wrunlock(); ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp); open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err); @@ -309,15 +308,15 @@ int bdrv_snapshot_goto(BlockDriverState *bs, } /* - * fallback_ptr is &bs->file or &bs->backing. *fallback_ptr - * was closed above and set to NULL, but the .bdrv_open() call - * has opened it again, because we set the respective option - * (with the qdict_put_str() call above). - * Assert that .bdrv_open() has attached some child on - * *fallback_ptr, and that it has attached the one we wanted - * it to (i.e., fallback_bs). + * fallback was a primary child. It was closed above and set to NULL, + * but the .bdrv_open() call has opened it again, because we set the + * respective option (with the qdict_put_str() call above). + * Assert that .bdrv_open() has attached the right BDS as primary child. */ - assert(*fallback_ptr && fallback_bs == (*fallback_ptr)->bs); + bdrv_graph_rdlock_main_loop(); + assert(bdrv_primary_bs(bs) == fallback_bs); + bdrv_graph_rdunlock_main_loop(); + bdrv_unref(fallback_bs); return ret; } @@ -389,10 +388,12 @@ int bdrv_snapshot_delete(BlockDriverState *bs, int bdrv_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_info) { + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + BlockDriver *drv = bs->drv; BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); - GLOBAL_STATE_CODE(); if (!drv) { return -ENOMEDIUM; } @@ -433,6 +434,7 @@ int bdrv_snapshot_load_tmp(BlockDriverState *bs, BlockDriver *drv = bs->drv; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!drv) { error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); @@ -477,9 +479,9 @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, } -static int bdrv_all_get_snapshot_devices(bool has_devices, strList *devices, - GList **all_bdrvs, - Error **errp) +static int GRAPH_RDLOCK +bdrv_all_get_snapshot_devices(bool has_devices, strList *devices, + GList **all_bdrvs, Error **errp) { g_autoptr(GList) bdrvs = NULL; @@ -511,8 +513,11 @@ static int bdrv_all_get_snapshot_devices(bool has_devices, strList *devices, } -static bool bdrv_all_snapshots_includes_bs(BlockDriverState *bs) +static bool GRAPH_RDLOCK bdrv_all_snapshots_includes_bs(BlockDriverState *bs) { + GLOBAL_STATE_CODE(); + assert_bdrv_graph_readable(); + if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { return false; } @@ -522,9 +527,7 @@ static bool bdrv_all_snapshots_includes_bs(BlockDriverState *bs) return bdrv_has_blk(bs) || QLIST_EMPTY(&bs->parents); } -/* Group operations. All block drivers are involved. - * These functions will properly handle dataplane (take aio_context_acquire - * when appropriate for appropriate block drivers) */ +/* Group operations. All block drivers are involved. */ bool bdrv_all_can_snapshot(bool has_devices, strList *devices, Error **errp) @@ -533,6 +536,7 @@ bool bdrv_all_can_snapshot(bool has_devices, strList *devices, GList *iterbdrvs; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { return false; @@ -541,14 +545,11 @@ bool bdrv_all_can_snapshot(bool has_devices, strList *devices, iterbdrvs = bdrvs; while (iterbdrvs) { BlockDriverState *bs = iterbdrvs->data; - AioContext *ctx = bdrv_get_aio_context(bs); bool ok = true; - aio_context_acquire(ctx); if (devices || bdrv_all_snapshots_includes_bs(bs)) { ok = bdrv_can_snapshot(bs); } - aio_context_release(ctx); if (!ok) { error_setg(errp, "Device '%s' is writable but does not support " "snapshots", bdrv_get_device_or_node_name(bs)); @@ -565,10 +566,12 @@ int bdrv_all_delete_snapshot(const char *name, bool has_devices, strList *devices, Error **errp) { + ERRP_GUARD(); g_autoptr(GList) bdrvs = NULL; GList *iterbdrvs; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { return -1; @@ -577,18 +580,15 @@ int bdrv_all_delete_snapshot(const char *name, iterbdrvs = bdrvs; while (iterbdrvs) { BlockDriverState *bs = iterbdrvs->data; - AioContext *ctx = bdrv_get_aio_context(bs); QEMUSnapshotInfo sn1, *snapshot = &sn1; int ret = 0; - aio_context_acquire(ctx); if ((devices || bdrv_all_snapshots_includes_bs(bs)) && bdrv_snapshot_find(bs, snapshot, name) >= 0) { ret = bdrv_snapshot_delete(bs, snapshot->id_str, snapshot->name, errp); } - aio_context_release(ctx); if (ret < 0) { error_prepend(errp, "Could not delete snapshot '%s' on '%s': ", name, bdrv_get_device_or_node_name(bs)); @@ -606,29 +606,37 @@ int bdrv_all_goto_snapshot(const char *name, bool has_devices, strList *devices, Error **errp) { + ERRP_GUARD(); g_autoptr(GList) bdrvs = NULL; GList *iterbdrvs; + int ret; GLOBAL_STATE_CODE(); - if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { + bdrv_graph_rdlock_main_loop(); + ret = bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp); + bdrv_graph_rdunlock_main_loop(); + + if (ret < 0) { return -1; } iterbdrvs = bdrvs; while (iterbdrvs) { BlockDriverState *bs = iterbdrvs->data; - AioContext *ctx = bdrv_get_aio_context(bs); - int ret = 0; + bool all_snapshots_includes_bs; - aio_context_acquire(ctx); - if (devices || bdrv_all_snapshots_includes_bs(bs)) { - ret = bdrv_snapshot_goto(bs, name, errp); - } - aio_context_release(ctx); + bdrv_graph_rdlock_main_loop(); + all_snapshots_includes_bs = bdrv_all_snapshots_includes_bs(bs); + bdrv_graph_rdunlock_main_loop(); + + ret = (devices || all_snapshots_includes_bs) ? + bdrv_snapshot_goto(bs, name, errp) : 0; if (ret < 0) { + bdrv_graph_rdlock_main_loop(); error_prepend(errp, "Could not load snapshot '%s' on '%s': ", name, bdrv_get_device_or_node_name(bs)); + bdrv_graph_rdunlock_main_loop(); return -1; } @@ -646,6 +654,7 @@ int bdrv_all_has_snapshot(const char *name, GList *iterbdrvs; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { return -1; @@ -654,15 +663,12 @@ int bdrv_all_has_snapshot(const char *name, iterbdrvs = bdrvs; while (iterbdrvs) { BlockDriverState *bs = iterbdrvs->data; - AioContext *ctx = bdrv_get_aio_context(bs); QEMUSnapshotInfo sn; int ret = 0; - aio_context_acquire(ctx); if (devices || bdrv_all_snapshots_includes_bs(bs)) { ret = bdrv_snapshot_find(bs, &sn, name); } - aio_context_release(ctx); if (ret < 0) { if (ret == -ENOENT) { return 0; @@ -688,7 +694,9 @@ int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, { g_autoptr(GList) bdrvs = NULL; GList *iterbdrvs; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { return -1; @@ -697,10 +705,8 @@ int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, iterbdrvs = bdrvs; while (iterbdrvs) { BlockDriverState *bs = iterbdrvs->data; - AioContext *ctx = bdrv_get_aio_context(bs); int ret = 0; - aio_context_acquire(ctx); if (bs == vm_state_bs) { sn->vm_state_size = vm_state_size; ret = bdrv_snapshot_create(bs, sn); @@ -708,7 +714,6 @@ int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, sn->vm_state_size = 0; ret = bdrv_snapshot_create(bs, sn); } - aio_context_release(ctx); if (ret < 0) { error_setg(errp, "Could not create snapshot '%s' on '%s'", sn->name, bdrv_get_device_or_node_name(bs)); @@ -730,6 +735,7 @@ BlockDriverState *bdrv_all_find_vmstate_bs(const char *vmstate_bs, GList *iterbdrvs; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { return NULL; @@ -738,13 +744,10 @@ BlockDriverState *bdrv_all_find_vmstate_bs(const char *vmstate_bs, iterbdrvs = bdrvs; while (iterbdrvs) { BlockDriverState *bs = iterbdrvs->data; - AioContext *ctx = bdrv_get_aio_context(bs); bool found = false; - aio_context_acquire(ctx); found = (devices || bdrv_all_snapshots_includes_bs(bs)) && bdrv_can_snapshot(bs); - aio_context_release(ctx); if (vmstate_bs) { if (g_str_equal(vmstate_bs, diff --git a/block/ssh.c b/block/ssh.c index a2dc646536..2748253d4a 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -27,6 +27,7 @@ #include #include +#include "block/block-io.h" #include "block/block_int.h" #include "block/qdict.h" #include "qapi/error.h" @@ -643,7 +644,7 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts, unsigned int port = 0; int new_sock = -1; - if (opts->has_user) { + if (opts->user) { s->user = g_strdup(opts->user); } else { s->user = g_strdup(g_get_user_name()); @@ -1018,7 +1019,7 @@ static void restart_coroutine(void *opaque) AioContext *ctx = bdrv_get_aio_context(bs); trace_ssh_restart_coroutine(restart->co); - aio_set_fd_handler(ctx, s->sock, false, NULL, NULL, NULL, NULL, NULL); + aio_set_fd_handler(ctx, s->sock, NULL, NULL, NULL, NULL, NULL); aio_co_wake(restart->co); } @@ -1048,7 +1049,7 @@ static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs) trace_ssh_co_yield(s->sock, rd_handler, wr_handler); aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, - false, rd_handler, wr_handler, NULL, NULL, &restart); + rd_handler, wr_handler, NULL, NULL, &restart); qemu_coroutine_yield(); trace_ssh_co_yield_back(s->sock); } @@ -1129,9 +1130,9 @@ static coroutine_fn int ssh_co_readv(BlockDriverState *bs, return ret; } -static int ssh_write(BDRVSSHState *s, BlockDriverState *bs, - int64_t offset, size_t size, - QEMUIOVector *qiov) +static coroutine_fn int ssh_write(BDRVSSHState *s, BlockDriverState *bs, + int64_t offset, size_t size, + QEMUIOVector *qiov) { ssize_t r; size_t written; @@ -1196,7 +1197,6 @@ static coroutine_fn int ssh_co_writev(BlockDriverState *bs, BDRVSSHState *s = bs->opaque; int ret; - assert(!flags); qemu_co_mutex_lock(&s->lock); ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE, nb_sectors * BDRV_SECTOR_SIZE, qiov); @@ -1253,7 +1253,7 @@ static coroutine_fn int ssh_co_flush(BlockDriverState *bs) return ret; } -static int64_t ssh_getlength(BlockDriverState *bs) +static int64_t coroutine_fn ssh_co_getlength(BlockDriverState *bs) { BDRVSSHState *s = bs->opaque; int64_t length; @@ -1364,7 +1364,7 @@ static BlockDriver bdrv_ssh = { .bdrv_has_zero_init = ssh_has_zero_init, .bdrv_co_readv = ssh_co_readv, .bdrv_co_writev = ssh_co_writev, - .bdrv_getlength = ssh_getlength, + .bdrv_co_getlength = ssh_co_getlength, .bdrv_co_truncate = ssh_co_truncate, .bdrv_co_flush_to_disk = ssh_co_flush, .bdrv_refresh_filename = ssh_refresh_filename, diff --git a/block/stream.c b/block/stream.c index 694709bd25..7031eef12b 100644 --- a/block/stream.c +++ b/block/stream.c @@ -16,7 +16,6 @@ #include "block/block_int.h" #include "block/blockjob_int.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qapi/qmp/qdict.h" #include "qemu/ratelimit.h" #include "sysemu/block-backend.h" @@ -40,6 +39,7 @@ typedef struct StreamBlockJob { BlockDriverState *target_bs; BlockdevOnError on_error; char *backing_file_str; + bool backing_mask_protocol; bool bs_read_only; } StreamBlockJob; @@ -54,35 +54,66 @@ static int coroutine_fn stream_populate(BlockBackend *blk, static int stream_prepare(Job *job) { StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); - BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs); + BlockDriverState *unfiltered_bs; + BlockDriverState *unfiltered_bs_cow; BlockDriverState *base; BlockDriverState *unfiltered_base; Error *local_err = NULL; int ret = 0; + GLOBAL_STATE_CODE(); + + bdrv_graph_rdlock_main_loop(); + unfiltered_bs = bdrv_skip_filters(s->target_bs); + unfiltered_bs_cow = bdrv_cow_bs(unfiltered_bs); + bdrv_graph_rdunlock_main_loop(); + /* We should drop filter at this point, as filter hold the backing chain */ bdrv_cor_filter_drop(s->cor_filter_bs); s->cor_filter_bs = NULL; - bdrv_subtree_drained_begin(s->above_base); - - base = bdrv_filter_or_cow_bs(s->above_base); - if (base) { - bdrv_ref(base); + /* + * bdrv_set_backing_hd() requires that the unfiltered_bs and the COW child + * of unfiltered_bs is drained. Drain already here and use + * bdrv_set_backing_hd_drained() instead because the polling during + * drained_begin() might change the graph, and if we do this only later, we + * may end up working with the wrong base node (or it might even have gone + * away by the time we want to use it). + */ + bdrv_drained_begin(unfiltered_bs); + if (unfiltered_bs_cow) { + bdrv_ref(unfiltered_bs_cow); + bdrv_drained_begin(unfiltered_bs_cow); } + bdrv_graph_rdlock_main_loop(); + base = bdrv_filter_or_cow_bs(s->above_base); unfiltered_base = bdrv_skip_filters(base); + bdrv_graph_rdunlock_main_loop(); - if (bdrv_cow_child(unfiltered_bs)) { + if (unfiltered_bs_cow) { const char *base_id = NULL, *base_fmt = NULL; if (unfiltered_base) { base_id = s->backing_file_str ?: unfiltered_base->filename; if (unfiltered_base->drv) { - base_fmt = unfiltered_base->drv->format_name; + if (s->backing_mask_protocol && + unfiltered_base->drv->protocol_name) { + base_fmt = "raw"; + } else { + base_fmt = unfiltered_base->drv->format_name; + } } } - bdrv_set_backing_hd(unfiltered_bs, base, &local_err); + bdrv_graph_wrlock(); + bdrv_set_backing_hd_drained(unfiltered_bs, base, &local_err); + bdrv_graph_wrunlock(); + + /* + * This call will do I/O, so the graph can change again from here on. + * We have already completed the graph change, so we are not in danger + * of operating on the wrong node any more if this happens. + */ ret = bdrv_change_backing_file(unfiltered_bs, base_id, base_fmt, false); if (local_err) { error_report_err(local_err); @@ -92,10 +123,11 @@ static int stream_prepare(Job *job) } out: - if (base) { - bdrv_unref(base); + if (unfiltered_bs_cow) { + bdrv_drained_end(unfiltered_bs_cow); + bdrv_unref(unfiltered_bs_cow); } - bdrv_subtree_drained_end(s->above_base); + bdrv_drained_end(unfiltered_bs); return ret; } @@ -123,21 +155,23 @@ static void stream_clean(Job *job) static int coroutine_fn stream_run(Job *job, Error **errp) { StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); - BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs); + BlockDriverState *unfiltered_bs; int64_t len; int64_t offset = 0; - uint64_t delay_ns = 0; int error = 0; int64_t n = 0; /* bytes */ - if (unfiltered_bs == s->base_overlay) { - /* Nothing to stream */ - return 0; - } + WITH_GRAPH_RDLOCK_GUARD() { + unfiltered_bs = bdrv_skip_filters(s->target_bs); + if (unfiltered_bs == s->base_overlay) { + /* Nothing to stream */ + return 0; + } - len = bdrv_getlength(s->target_bs); - if (len < 0) { - return len; + len = bdrv_co_getlength(s->target_bs); + if (len < 0) { + return len; + } } job_progress_set_remaining(&s->common.job, len); @@ -148,28 +182,32 @@ static int coroutine_fn stream_run(Job *job, Error **errp) /* 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. */ - job_sleep_ns(&s->common.job, delay_ns); + block_job_ratelimit_sleep(&s->common); if (job_is_cancelled(&s->common.job)) { break; } copy = false; - ret = bdrv_is_allocated(unfiltered_bs, offset, STREAM_CHUNK, &n); - if (ret == 1) { - /* Allocated in the top, no need to copy. */ - } else if (ret >= 0) { - /* Copy if allocated in the intermediate images. Limit to the - * known-unallocated area [offset, offset+n*BDRV_SECTOR_SIZE). */ - ret = bdrv_is_allocated_above(bdrv_cow_bs(unfiltered_bs), - s->base_overlay, true, - offset, n, &n); - /* Finish early if end of backing file has been reached */ - if (ret == 0 && n == 0) { - n = len - offset; - } + WITH_GRAPH_RDLOCK_GUARD() { + ret = bdrv_co_is_allocated(unfiltered_bs, offset, STREAM_CHUNK, &n); + if (ret == 1) { + /* Allocated in the top, no need to copy. */ + } else if (ret >= 0) { + /* + * Copy if allocated in the intermediate images. Limit to the + * known-unallocated area [offset, offset+n*BDRV_SECTOR_SIZE). + */ + ret = bdrv_co_is_allocated_above(bdrv_cow_bs(unfiltered_bs), + s->base_overlay, true, + offset, n, &n); + /* Finish early if end of backing file has been reached */ + if (ret == 0 && n == 0) { + n = len - offset; + } - copy = (ret > 0); + copy = (ret > 0); + } } trace_stream_one_iteration(s, offset, n, ret); if (copy) { @@ -193,9 +231,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp) /* Publish progress */ job_progress_update(&s->common.job, n); if (copy) { - delay_ns = block_job_ratelimit_get_delay(&s->common, n); - } else { - delay_ns = 0; + block_job_ratelimit_processed_bytes(&s->common, n); } } @@ -217,6 +253,7 @@ static const BlockJobDriver stream_job_driver = { void stream_start(const char *job_id, BlockDriverState *bs, BlockDriverState *base, const char *backing_file_str, + bool backing_mask_protocol, BlockDriverState *bottom, int creation_flags, int64_t speed, BlockdevOnError on_error, @@ -238,6 +275,8 @@ void stream_start(const char *job_id, BlockDriverState *bs, assert(!(base && bottom)); assert(!(backing_file_str && bottom)); + bdrv_graph_rdlock_main_loop(); + if (bottom) { /* * New simple interface. The code is written in terms of old interface @@ -254,7 +293,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, if (!base_overlay) { error_setg(errp, "'%s' is not in the backing chain of '%s'", base->node_name, bs->node_name); - return; + goto out_rdlock; } /* @@ -274,10 +313,9 @@ void stream_start(const char *job_id, BlockDriverState *bs, /* Make sure that the image is opened in read-write mode */ bs_read_only = bdrv_is_read_only(bs); if (bs_read_only) { - int ret; /* Hold the chain during reopen */ if (bdrv_freeze_backing_chain(bs, above_base, errp) < 0) { - return; + goto out_rdlock; } ret = bdrv_reopen_set_read_only(bs, false, errp); @@ -286,10 +324,12 @@ void stream_start(const char *job_id, BlockDriverState *bs, bdrv_unfreeze_backing_chain(bs, above_base); if (ret < 0) { - return; + goto out_rdlock; } } + bdrv_graph_rdunlock_main_loop(); + opts = qdict_new(); qdict_put_str(opts, "driver", "copy-on-read"); @@ -333,8 +373,10 @@ void stream_start(const char *job_id, BlockDriverState *bs, * already have our own plans. Also don't allow resize as the image size is * queried only at the job start and then cached. */ + bdrv_graph_wrlock(); if (block_job_add_bdrv(&s->common, "active node", bs, 0, basic_flags | BLK_PERM_WRITE, errp)) { + bdrv_graph_wrunlock(); goto fail; } @@ -354,13 +396,16 @@ void stream_start(const char *job_id, BlockDriverState *bs, ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, basic_flags, errp); if (ret < 0) { + bdrv_graph_wrunlock(); goto fail; } } + bdrv_graph_wrunlock(); s->base_overlay = base_overlay; s->above_base = above_base; s->backing_file_str = g_strdup(backing_file_str); + s->backing_mask_protocol = backing_mask_protocol; s->cor_filter_bs = cor_filter_bs; s->target_bs = bs; s->bs_read_only = bs_read_only; @@ -380,4 +425,8 @@ fail: if (bs_read_only) { bdrv_reopen_set_read_only(bs, true, NULL); } + return; + +out_rdlock: + bdrv_graph_rdunlock_main_loop(); } diff --git a/block/throttle-groups.c b/block/throttle-groups.c index fb203c3ced..f5c0fac581 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -37,7 +37,7 @@ static void throttle_group_obj_init(Object *obj); static void throttle_group_obj_complete(UserCreatable *obj, Error **errp); -static void timer_cb(ThrottleGroupMember *tgm, bool is_write); +static void timer_cb(ThrottleGroupMember *tgm, ThrottleDirection direction); /* The ThrottleGroup structure (with its ThrottleState) is shared * among different ThrottleGroupMembers and it's independent from @@ -73,8 +73,8 @@ struct ThrottleGroup { QemuMutex lock; /* This lock protects the following four fields */ ThrottleState ts; QLIST_HEAD(, ThrottleGroupMember) head; - ThrottleGroupMember *tokens[2]; - bool any_timer_armed[2]; + ThrottleGroupMember *tokens[THROTTLE_MAX]; + bool any_timer_armed[THROTTLE_MAX]; QEMUClockType clock_type; /* This field is protected by the global QEMU mutex */ @@ -197,13 +197,13 @@ static ThrottleGroupMember *throttle_group_next_tgm(ThrottleGroupMember *tgm) * This assumes that tg->lock is held. * * @tgm: the ThrottleGroupMember - * @is_write: the type of operation (read/write) + * @direction: the ThrottleDirection * @ret: whether the ThrottleGroupMember has pending requests. */ static inline bool tgm_has_pending_reqs(ThrottleGroupMember *tgm, - bool is_write) + ThrottleDirection direction) { - return tgm->pending_reqs[is_write]; + return tgm->pending_reqs[direction]; } /* Return the next ThrottleGroupMember in the round-robin sequence with pending @@ -212,12 +212,12 @@ static inline bool tgm_has_pending_reqs(ThrottleGroupMember *tgm, * This assumes that tg->lock is held. * * @tgm: the current ThrottleGroupMember - * @is_write: the type of operation (read/write) + * @direction: the ThrottleDirection * @ret: the next ThrottleGroupMember with pending requests, or tgm if * there is none. */ static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm, - bool is_write) + ThrottleDirection direction) { ThrottleState *ts = tgm->throttle_state; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); @@ -227,16 +227,16 @@ static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm, * it's being drained. Skip the round-robin search and return tgm * immediately if it has pending requests. Otherwise we could be * forcing it to wait for other member's throttled requests. */ - if (tgm_has_pending_reqs(tgm, is_write) && + if (tgm_has_pending_reqs(tgm, direction) && qatomic_read(&tgm->io_limits_disabled)) { return tgm; } - start = token = tg->tokens[is_write]; + start = token = tg->tokens[direction]; /* get next bs round in round robin style */ token = throttle_group_next_tgm(token); - while (token != start && !tgm_has_pending_reqs(token, is_write)) { + while (token != start && !tgm_has_pending_reqs(token, direction)) { token = throttle_group_next_tgm(token); } @@ -244,12 +244,12 @@ static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm, * then decide the token is the current tgm because chances are * the current tgm got the current request queued. */ - if (token == start && !tgm_has_pending_reqs(token, is_write)) { + if (token == start && !tgm_has_pending_reqs(token, direction)) { token = tgm; } /* Either we return the original TGM, or one with pending requests */ - assert(token == tgm || tgm_has_pending_reqs(token, is_write)); + assert(token == tgm || tgm_has_pending_reqs(token, direction)); return token; } @@ -261,11 +261,11 @@ static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm, * This assumes that tg->lock is held. * * @tgm: the current ThrottleGroupMember - * @is_write: the type of operation (read/write) + * @direction: the ThrottleDirection * @ret: whether the I/O request needs to be throttled or not */ static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm, - bool is_write) + ThrottleDirection direction) { ThrottleState *ts = tgm->throttle_state; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); @@ -277,16 +277,16 @@ static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm, } /* Check if any of the timers in this group is already armed */ - if (tg->any_timer_armed[is_write]) { + if (tg->any_timer_armed[direction]) { return true; } - must_wait = throttle_schedule_timer(ts, tt, is_write); + must_wait = throttle_schedule_timer(ts, tt, direction); /* If a timer just got armed, set tgm as the current token */ if (must_wait) { - tg->tokens[is_write] = tgm; - tg->any_timer_armed[is_write] = true; + tg->tokens[direction] = tgm; + tg->any_timer_armed[direction] = true; } return must_wait; @@ -296,15 +296,15 @@ static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm, * any request was actually pending. * * @tgm: the current ThrottleGroupMember - * @is_write: the type of operation (read/write) + * @direction: the ThrottleDirection */ static bool coroutine_fn throttle_group_co_restart_queue(ThrottleGroupMember *tgm, - bool is_write) + ThrottleDirection direction) { bool ret; qemu_co_mutex_lock(&tgm->throttled_reqs_lock); - ret = qemu_co_queue_next(&tgm->throttled_reqs[is_write]); + ret = qemu_co_queue_next(&tgm->throttled_reqs[direction]); qemu_co_mutex_unlock(&tgm->throttled_reqs_lock); return ret; @@ -315,9 +315,10 @@ static bool coroutine_fn throttle_group_co_restart_queue(ThrottleGroupMember *tg * This assumes that tg->lock is held. * * @tgm: the current ThrottleGroupMember - * @is_write: the type of operation (read/write) + * @direction: the ThrottleDirection */ -static void schedule_next_request(ThrottleGroupMember *tgm, bool is_write) +static void coroutine_mixed_fn schedule_next_request(ThrottleGroupMember *tgm, + ThrottleDirection direction) { ThrottleState *ts = tgm->throttle_state; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); @@ -325,27 +326,27 @@ static void schedule_next_request(ThrottleGroupMember *tgm, bool is_write) ThrottleGroupMember *token; /* Check if there's any pending request to schedule next */ - token = next_throttle_token(tgm, is_write); - if (!tgm_has_pending_reqs(token, is_write)) { + token = next_throttle_token(tgm, direction); + if (!tgm_has_pending_reqs(token, direction)) { return; } /* Set a timer for the request if it needs to be throttled */ - must_wait = throttle_group_schedule_timer(token, is_write); + must_wait = throttle_group_schedule_timer(token, direction); /* If it doesn't have to wait, queue it for immediate execution */ if (!must_wait) { /* Give preference to requests from the current tgm */ if (qemu_in_coroutine() && - throttle_group_co_restart_queue(tgm, is_write)) { + throttle_group_co_restart_queue(tgm, direction)) { token = tgm; } else { ThrottleTimers *tt = &token->throttle_timers; int64_t now = qemu_clock_get_ns(tg->clock_type); - timer_mod(tt->timers[is_write], now); - tg->any_timer_armed[is_write] = true; + timer_mod(tt->timers[direction], now); + tg->any_timer_armed[direction] = true; } - tg->tokens[is_write] = token; + tg->tokens[direction] = token; } } @@ -355,48 +356,49 @@ static void schedule_next_request(ThrottleGroupMember *tgm, bool is_write) * * @tgm: the current ThrottleGroupMember * @bytes: the number of bytes for this I/O - * @is_write: the type of operation (read/write) + * @direction: the ThrottleDirection */ void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm, int64_t bytes, - bool is_write) + ThrottleDirection direction) { bool must_wait; ThrottleGroupMember *token; ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts); assert(bytes >= 0); + assert(direction < THROTTLE_MAX); qemu_mutex_lock(&tg->lock); /* First we check if this I/O has to be throttled. */ - token = next_throttle_token(tgm, is_write); - must_wait = throttle_group_schedule_timer(token, is_write); + token = next_throttle_token(tgm, direction); + must_wait = throttle_group_schedule_timer(token, direction); /* Wait if there's a timer set or queued requests of this type */ - if (must_wait || tgm->pending_reqs[is_write]) { - tgm->pending_reqs[is_write]++; + if (must_wait || tgm->pending_reqs[direction]) { + tgm->pending_reqs[direction]++; qemu_mutex_unlock(&tg->lock); qemu_co_mutex_lock(&tgm->throttled_reqs_lock); - qemu_co_queue_wait(&tgm->throttled_reqs[is_write], + qemu_co_queue_wait(&tgm->throttled_reqs[direction], &tgm->throttled_reqs_lock); qemu_co_mutex_unlock(&tgm->throttled_reqs_lock); qemu_mutex_lock(&tg->lock); - tgm->pending_reqs[is_write]--; + tgm->pending_reqs[direction]--; } /* The I/O will be executed, so do the accounting */ - throttle_account(tgm->throttle_state, is_write, bytes); + throttle_account(tgm->throttle_state, direction, bytes); /* Schedule the next request */ - schedule_next_request(tgm, is_write); + schedule_next_request(tgm, direction); qemu_mutex_unlock(&tg->lock); } typedef struct { ThrottleGroupMember *tgm; - bool is_write; + ThrottleDirection direction; } RestartData; static void coroutine_fn throttle_group_restart_queue_entry(void *opaque) @@ -405,16 +407,16 @@ static void coroutine_fn throttle_group_restart_queue_entry(void *opaque) ThrottleGroupMember *tgm = data->tgm; ThrottleState *ts = tgm->throttle_state; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); - bool is_write = data->is_write; + ThrottleDirection direction = data->direction; bool empty_queue; - empty_queue = !throttle_group_co_restart_queue(tgm, is_write); + empty_queue = !throttle_group_co_restart_queue(tgm, direction); /* If the request queue was empty then we have to take care of * scheduling the next one */ if (empty_queue) { qemu_mutex_lock(&tg->lock); - schedule_next_request(tgm, is_write); + schedule_next_request(tgm, direction); qemu_mutex_unlock(&tg->lock); } @@ -424,18 +426,19 @@ static void coroutine_fn throttle_group_restart_queue_entry(void *opaque) aio_wait_kick(); } -static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write) +static void throttle_group_restart_queue(ThrottleGroupMember *tgm, + ThrottleDirection direction) { Coroutine *co; RestartData *rd = g_new0(RestartData, 1); rd->tgm = tgm; - rd->is_write = is_write; + rd->direction = direction; /* This function is called when a timer is fired or when * throttle_group_restart_tgm() is called. Either way, there can * be no timer pending on this tgm at this point */ - assert(!timer_pending(tgm->throttle_timers.timers[is_write])); + assert(!timer_pending(tgm->throttle_timers.timers[direction])); qatomic_inc(&tgm->restart_pending); @@ -445,18 +448,18 @@ static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write void throttle_group_restart_tgm(ThrottleGroupMember *tgm) { - int i; + ThrottleDirection dir; if (tgm->throttle_state) { - for (i = 0; i < 2; i++) { - QEMUTimer *t = tgm->throttle_timers.timers[i]; + for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) { + QEMUTimer *t = tgm->throttle_timers.timers[dir]; if (timer_pending(t)) { /* If there's a pending timer on this tgm, fire it now */ timer_del(t); - timer_cb(tgm, i); + timer_cb(tgm, dir); } else { /* Else run the next request from the queue manually */ - throttle_group_restart_queue(tgm, i); + throttle_group_restart_queue(tgm, dir); } } } @@ -500,30 +503,30 @@ void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg) * because it had been throttled. * * @tgm: the ThrottleGroupMember whose request had been throttled - * @is_write: the type of operation (read/write) + * @direction: the ThrottleDirection */ -static void timer_cb(ThrottleGroupMember *tgm, bool is_write) +static void timer_cb(ThrottleGroupMember *tgm, ThrottleDirection direction) { ThrottleState *ts = tgm->throttle_state; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); /* The timer has just been fired, so we can update the flag */ qemu_mutex_lock(&tg->lock); - tg->any_timer_armed[is_write] = false; + tg->any_timer_armed[direction] = false; qemu_mutex_unlock(&tg->lock); /* Run the request that was waiting for this timer */ - throttle_group_restart_queue(tgm, is_write); + throttle_group_restart_queue(tgm, direction); } static void read_timer_cb(void *opaque) { - timer_cb(opaque, false); + timer_cb(opaque, THROTTLE_READ); } static void write_timer_cb(void *opaque) { - timer_cb(opaque, true); + timer_cb(opaque, THROTTLE_WRITE); } /* Register a ThrottleGroupMember from the throttling group, also initializing @@ -541,7 +544,7 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm, const char *groupname, AioContext *ctx) { - int i; + ThrottleDirection dir; ThrottleState *ts = throttle_group_incref(groupname); ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); @@ -551,10 +554,11 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm, QEMU_LOCK_GUARD(&tg->lock); /* If the ThrottleGroup is new set this ThrottleGroupMember as the token */ - for (i = 0; i < 2; i++) { - if (!tg->tokens[i]) { - tg->tokens[i] = tgm; + for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) { + if (!tg->tokens[dir]) { + tg->tokens[dir] = tgm; } + qemu_co_queue_init(&tgm->throttled_reqs[dir]); } QLIST_INSERT_HEAD(&tg->head, tgm, round_robin); @@ -566,8 +570,6 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm, write_timer_cb, tgm); qemu_co_mutex_init(&tgm->throttled_reqs_lock); - qemu_co_queue_init(&tgm->throttled_reqs[0]); - qemu_co_queue_init(&tgm->throttled_reqs[1]); } /* Unregister a ThrottleGroupMember from its group, removing it from the list, @@ -585,7 +587,7 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm) ThrottleState *ts = tgm->throttle_state; ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); ThrottleGroupMember *token; - int i; + ThrottleDirection dir; if (!ts) { /* Discard already unregistered tgm */ @@ -596,17 +598,17 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm) AIO_WAIT_WHILE(tgm->aio_context, qatomic_read(&tgm->restart_pending) > 0); WITH_QEMU_LOCK_GUARD(&tg->lock) { - for (i = 0; i < 2; i++) { - assert(tgm->pending_reqs[i] == 0); - assert(qemu_co_queue_empty(&tgm->throttled_reqs[i])); - assert(!timer_pending(tgm->throttle_timers.timers[i])); - if (tg->tokens[i] == tgm) { + for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) { + assert(tgm->pending_reqs[dir] == 0); + assert(qemu_co_queue_empty(&tgm->throttled_reqs[dir])); + assert(!timer_pending(tgm->throttle_timers.timers[dir])); + if (tg->tokens[dir] == tgm) { token = throttle_group_next_tgm(tgm); /* Take care of the case where this is the last tgm in the group */ if (token == tgm) { token = NULL; } - tg->tokens[i] = token; + tg->tokens[dir] = token; } } @@ -631,19 +633,20 @@ void throttle_group_detach_aio_context(ThrottleGroupMember *tgm) { ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts); ThrottleTimers *tt = &tgm->throttle_timers; - int i; + ThrottleDirection dir; /* Requests must have been drained */ - assert(tgm->pending_reqs[0] == 0 && tgm->pending_reqs[1] == 0); - assert(qemu_co_queue_empty(&tgm->throttled_reqs[0])); - assert(qemu_co_queue_empty(&tgm->throttled_reqs[1])); + for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) { + assert(tgm->pending_reqs[dir] == 0); + assert(qemu_co_queue_empty(&tgm->throttled_reqs[dir])); + } /* Kick off next ThrottleGroupMember, if necessary */ WITH_QEMU_LOCK_GUARD(&tg->lock) { - for (i = 0; i < 2; i++) { - if (timer_pending(tt->timers[i])) { - tg->any_timer_armed[i] = false; - schedule_next_request(tgm, i); + for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) { + if (timer_pending(tt->timers[dir])) { + tg->any_timer_armed[dir] = false; + schedule_next_request(tgm, dir); } } } diff --git a/block/throttle.c b/block/throttle.c index 6e8d52fa24..97972d1f15 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -18,6 +18,8 @@ */ #include "qemu/osdep.h" +#include "block/block-io.h" +#include "block/block_int.h" #include "block/throttle-groups.h" #include "qemu/module.h" #include "qemu/option.h" @@ -78,12 +80,13 @@ static int throttle_open(BlockDriverState *bs, QDict *options, char *group; int ret; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, errp); - if (!bs->file) { - return -EINVAL; + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; } + + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bs->supported_write_flags = bs->file->bs->supported_write_flags | BDRV_REQ_WRITE_UNCHANGED; bs->supported_zero_flags = bs->file->bs->supported_zero_flags | @@ -106,63 +109,61 @@ static void throttle_close(BlockDriverState *bs) } -static int64_t throttle_getlength(BlockDriverState *bs) +static int64_t coroutine_fn GRAPH_RDLOCK +throttle_co_getlength(BlockDriverState *bs) { - return bdrv_getlength(bs->file->bs); + return bdrv_co_getlength(bs->file->bs); } -static int coroutine_fn throttle_co_preadv(BlockDriverState *bs, - int64_t offset, int64_t bytes, - QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +throttle_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { ThrottleGroupMember *tgm = bs->opaque; - throttle_group_co_io_limits_intercept(tgm, bytes, false); + throttle_group_co_io_limits_intercept(tgm, bytes, THROTTLE_READ); return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); } -static int coroutine_fn throttle_co_pwritev(BlockDriverState *bs, - int64_t offset, int64_t bytes, - QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +throttle_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { ThrottleGroupMember *tgm = bs->opaque; - throttle_group_co_io_limits_intercept(tgm, bytes, true); + throttle_group_co_io_limits_intercept(tgm, bytes, THROTTLE_WRITE); return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); } -static int coroutine_fn throttle_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int64_t bytes, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +throttle_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + BdrvRequestFlags flags) { ThrottleGroupMember *tgm = bs->opaque; - throttle_group_co_io_limits_intercept(tgm, bytes, true); + throttle_group_co_io_limits_intercept(tgm, bytes, THROTTLE_WRITE); return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); } -static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs, - int64_t offset, int64_t bytes) +static int coroutine_fn GRAPH_RDLOCK +throttle_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { ThrottleGroupMember *tgm = bs->opaque; - throttle_group_co_io_limits_intercept(tgm, bytes, true); + throttle_group_co_io_limits_intercept(tgm, bytes, THROTTLE_WRITE); return bdrv_co_pdiscard(bs->file, offset, bytes); } -static int coroutine_fn throttle_co_pwritev_compressed(BlockDriverState *bs, - int64_t offset, - int64_t bytes, - QEMUIOVector *qiov) +static int coroutine_fn GRAPH_RDLOCK +throttle_co_pwritev_compressed(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov) { return throttle_co_pwritev(bs, offset, bytes, qiov, BDRV_REQ_WRITE_COMPRESSED); } -static int throttle_co_flush(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK throttle_co_flush(BlockDriverState *bs) { return bdrv_co_flush(bs->file->bs); } @@ -216,7 +217,7 @@ static void throttle_reopen_abort(BDRVReopenState *reopen_state) reopen_state->opaque = NULL; } -static void coroutine_fn throttle_co_drain_begin(BlockDriverState *bs) +static void throttle_drain_begin(BlockDriverState *bs) { ThrottleGroupMember *tgm = bs->opaque; if (qatomic_fetch_inc(&tgm->io_limits_disabled) == 0) { @@ -224,7 +225,7 @@ static void coroutine_fn throttle_co_drain_begin(BlockDriverState *bs) } } -static void coroutine_fn throttle_co_drain_end(BlockDriverState *bs) +static void throttle_drain_end(BlockDriverState *bs) { ThrottleGroupMember *tgm = bs->opaque; assert(tgm->io_limits_disabled); @@ -247,7 +248,7 @@ static BlockDriver bdrv_throttle = { .bdrv_child_perm = bdrv_default_perms, - .bdrv_getlength = throttle_getlength, + .bdrv_co_getlength = throttle_co_getlength, .bdrv_co_preadv = throttle_co_preadv, .bdrv_co_pwritev = throttle_co_pwritev, @@ -263,8 +264,8 @@ static BlockDriver bdrv_throttle = { .bdrv_reopen_commit = throttle_reopen_commit, .bdrv_reopen_abort = throttle_reopen_abort, - .bdrv_co_drain_begin = throttle_co_drain_begin, - .bdrv_co_drain_end = throttle_co_drain_end, + .bdrv_drain_begin = throttle_drain_begin, + .bdrv_drain_end = throttle_drain_end, .is_filter = true, .strong_runtime_opts = throttle_strong_runtime_opts, diff --git a/block/trace-events b/block/trace-events index 549090d453..8e789e1f12 100644 --- a/block/trace-events +++ b/block/trace-events @@ -64,9 +64,8 @@ file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) " # 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_unplug_fn(void *s, int blocked, int queued, int inflight) "LuringState %p blocked %d queued %d inflight %d" +luring_do_submit(void *s, int blocked, int queued, int inflight) "LuringState %p blocked %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" @@ -141,7 +140,6 @@ nvme_kick(void *s, unsigned q_index) "s %p q #%u" nvme_dma_flush_queue_wait(void *s) "s %p" nvme_error(int cmd_specific, int sq_head, int sqid, int cid, int status) "cmd_specific %d sq_head %d sqid %d cid %d status 0x%x" nvme_process_completion(void *s, unsigned q_index, int inflight) "s %p q #%u inflight %d" -nvme_process_completion_queue_plugged(void *s, unsigned q_index) "s %p q #%u" nvme_complete_command(void *s, unsigned q_index, int cid) "s %p q #%u cid %d" nvme_submit_command(void *s, unsigned q_index, int cid) "s %p q #%u cid %d" nvme_submit_command_raw(int c0, int c1, int c2, int c3, int c4, int c5, int c6, int c7) "%02x %02x %02x %02x %02x %02x %02x %02x" @@ -168,10 +166,13 @@ iscsi_xcopy(void *src_lun, uint64_t src_off, void *dst_lun, uint64_t dst_off, ui # nbd.c nbd_parse_blockstatus_compliance(const char *err) "ignoring extra data from non-compliant server: %s" nbd_structured_read_compliance(const char *type) "server sent non-compliant unaligned read %s chunk" +nbd_extended_headers_compliance(const char *type) "server sent non-compliant %s chunk not matching choice of extended headers" nbd_read_reply_entry_fail(int ret, const char *err) "ret = %d, err: %s" -nbd_co_request_fail(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name, int ret, const char *err) "Request failed { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) } ret = %d, err: %s" +nbd_co_request_fail(uint64_t from, uint64_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name, int ret, const char *err) "Request failed { .from = %" PRIu64", .len = %" PRIu64 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) } ret = %d, err: %s" nbd_client_handshake(const char *export_name) "export '%s'" nbd_client_handshake_success(const char *export_name) "export '%s'" +nbd_reconnect_attempt(unsigned in_flight) "in_flight %u" +nbd_reconnect_attempt_result(int ret, unsigned in_flight) "ret %d in_flight %u" # ssh.c ssh_restart_coroutine(void *co) "co=%p" @@ -207,6 +208,10 @@ file_FindEjectableOpticalMedia(const char *media) "Matching using %s" file_setup_cdrom(const char *partition) "Using %s as optical disc" file_hdev_is_sg(int type, int version) "SG device found: type=%d, version=%d" file_flush_fdatasync_failed(int err) "errno %d" +zbd_zone_report(void *bs, unsigned int nr_zones, int64_t sector) "bs %p report %d zones starting at sector offset 0x%" PRIx64 "" +zbd_zone_mgmt(void *bs, const char *op_name, int64_t sector, int64_t len) "bs %p %s starts at sector offset 0x%" PRIx64 " over a range of 0x%" PRIx64 " sectors" +zbd_zone_append(void *bs, int64_t sector) "bs %p append at sector offset 0x%" PRIx64 "" +zbd_zone_append_complete(void *bs, int64_t sector) "bs %p returns append sector 0x%" PRIx64 "" # ssh.c sftp_error(const char *op, const char *ssh_err, int ssh_err_code, int sftp_err_code) "%s failed: %s (libssh error code: %d, sftp error code: %d)" diff --git a/block/vdi.c b/block/vdi.c index cca3a3a356..6363da08ce 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -239,7 +239,7 @@ static void vdi_header_to_le(VdiHeader *header) static void vdi_header_print(VdiHeader *header) { - char uuidstr[37]; + char uuidstr[UUID_STR_LEN]; QemuUUID uuid; logout("text %s", header->text); logout("signature 0x%08x\n", header->signature); @@ -327,9 +327,10 @@ static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res, return 0; } -static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +vdi_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { - /* TODO: vdi_get_info would be needed for machine snapshots. + /* TODO: vdi_co_get_info would be needed for machine snapshots. vm_state_offset is still missing. */ BDRVVdiState *s = (BDRVVdiState *)bs->opaque; logout("\n"); @@ -377,15 +378,16 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, int ret; QemuUUID uuid_link, uuid_parent; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_IMAGE, false, errp); - if (!bs->file) { - return -EINVAL; + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + logout("\n"); - ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); + ret = bdrv_pread(bs->file, 0, sizeof(header), &header, 0); if (ret < 0) { goto fail; } @@ -485,8 +487,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file, header.offset_bmap, s->bmap, - bmap_size * SECTOR_SIZE); + ret = bdrv_pread(bs->file, header.offset_bmap, bmap_size * SECTOR_SIZE, + s->bmap, 0); if (ret < 0) { goto fail_free_bmap; } @@ -495,9 +497,9 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, error_setg(&s->migration_blocker, "The vdi format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - ret = migrate_add_blocker(s->migration_blocker, errp); + + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { - error_free(s->migration_blocker); goto fail_free_bmap; } @@ -518,11 +520,10 @@ static int vdi_reopen_prepare(BDRVReopenState *state, return 0; } -static int coroutine_fn vdi_co_block_status(BlockDriverState *bs, - bool want_zero, - int64_t offset, int64_t bytes, - int64_t *pnum, int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +vdi_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, + int64_t bytes, int64_t *pnum, int64_t *map, + BlockDriverState **file) { BDRVVdiState *s = (BDRVVdiState *)bs->opaque; size_t bmap_index = offset / s->block_size; @@ -544,7 +545,7 @@ static int coroutine_fn vdi_co_block_status(BlockDriverState *bs, (s->header.image_type == VDI_TYPE_STATIC ? BDRV_BLOCK_RECURSE : 0); } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK vdi_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -600,7 +601,7 @@ vdi_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, return ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK vdi_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -634,7 +635,6 @@ vdi_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, bmap_entry = le32_to_cpu(s->bmap[block_index]); if (!VDI_IS_ALLOCATED(bmap_entry)) { /* Allocate new block and write to it. */ - uint64_t data_offset; qemu_co_rwlock_upgrade(&s->bmap_lock); bmap_entry = le32_to_cpu(s->bmap[block_index]); if (VDI_IS_ALLOCATED(bmap_entry)) { @@ -664,7 +664,8 @@ vdi_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, * so this full-cluster write does not overlap a partial write * of the same cluster, issued from the "else" branch. */ - ret = bdrv_pwrite(bs->file, data_offset, block, s->block_size); + ret = bdrv_co_pwrite(bs->file, data_offset, s->block_size, block, + 0); qemu_co_rwlock_unlock(&s->bmap_lock); } else { nonallocating_write: @@ -699,7 +700,7 @@ nonallocating_write: /* One or more new blocks were allocated. */ VdiHeader *header; uint8_t *base; - uint64_t offset; + uint64_t bmap_offset; uint32_t n_sectors; g_free(block); @@ -709,7 +710,7 @@ nonallocating_write: assert(VDI_IS_ALLOCATED(bmap_first)); *header = s->header; vdi_header_to_le(header); - ret = bdrv_pwrite(bs->file, 0, header, sizeof(*header)); + ret = bdrv_co_pwrite(bs->file, 0, sizeof(*header), header, 0); g_free(header); if (ret < 0) { @@ -722,20 +723,22 @@ nonallocating_write: bmap_first /= (SECTOR_SIZE / sizeof(uint32_t)); bmap_last /= (SECTOR_SIZE / sizeof(uint32_t)); n_sectors = bmap_last - bmap_first + 1; - offset = s->bmap_sector + bmap_first; + bmap_offset = s->bmap_sector + bmap_first; base = ((uint8_t *)&s->bmap[0]) + bmap_first * SECTOR_SIZE; logout("will write %u block map sectors starting from entry %u\n", n_sectors, bmap_first); - ret = bdrv_pwrite(bs->file, offset * SECTOR_SIZE, base, - n_sectors * SECTOR_SIZE); + ret = bdrv_co_pwrite(bs->file, bmap_offset * SECTOR_SIZE, + n_sectors * SECTOR_SIZE, base, 0); } - return ret < 0 ? ret : 0; + return ret; } -static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, - size_t block_size, Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +vdi_co_do_create(BlockdevCreateOptions *create_options, size_t block_size, + Error **errp) { + ERRP_GUARD(); BlockdevCreateOptionsVdi *vdi_opts; int ret = 0; uint64_t bytes = 0; @@ -799,14 +802,14 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, } /* Create BlockBackend to write to the image */ - bs_file = bdrv_open_blockdev_ref(vdi_opts->file, errp); + bs_file = bdrv_co_open_blockdev_ref(vdi_opts->file, errp); if (!bs_file) { ret = -EIO; goto exit; } - blk = blk_new_with_bs(bs_file, BLK_PERM_WRITE | BLK_PERM_RESIZE, - BLK_PERM_ALL, errp); + blk = blk_co_new_with_bs(bs_file, BLK_PERM_WRITE | BLK_PERM_RESIZE, + BLK_PERM_ALL, errp); if (!blk) { ret = -EPERM; goto exit; @@ -845,7 +848,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, vdi_header_print(&header); } vdi_header_to_le(&header); - ret = blk_pwrite(blk, offset, &header, sizeof(header), 0); + ret = blk_co_pwrite(blk, offset, sizeof(header), &header, 0); if (ret < 0) { error_setg(errp, "Error writing header"); goto exit; @@ -866,7 +869,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, bmap[i] = VDI_UNALLOCATED; } } - ret = blk_pwrite(blk, offset, bmap, bmap_size, 0); + ret = blk_co_pwrite(blk, offset, bmap_size, bmap, 0); if (ret < 0) { error_setg(errp, "Error writing bmap"); goto exit; @@ -875,8 +878,8 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, } if (image_type == VDI_TYPE_STATIC) { - ret = blk_truncate(blk, offset + blocks * block_size, false, - PREALLOC_MODE_OFF, 0, errp); + ret = blk_co_truncate(blk, offset + blocks * block_size, false, + PREALLOC_MODE_OFF, 0, errp); if (ret < 0) { error_prepend(errp, "Failed to statically allocate file"); goto exit; @@ -885,22 +888,21 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, ret = 0; exit: - blk_unref(blk); - bdrv_unref(bs_file); + blk_co_unref(blk); + bdrv_co_unref(bs_file); g_free(bmap); return ret; } -static int coroutine_fn vdi_co_create(BlockdevCreateOptions *create_options, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +vdi_co_create(BlockdevCreateOptions *create_options, Error **errp) { return vdi_co_do_create(create_options, DEFAULT_CLUSTER_SIZE, errp); } -static int coroutine_fn vdi_co_create_opts(BlockDriver *drv, - const char *filename, - QemuOpts *opts, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +vdi_co_create_opts(BlockDriver *drv, const char *filename, + QemuOpts *opts, Error **errp) { QDict *qdict = NULL; BlockdevCreateOptions *create_options = NULL; @@ -934,13 +936,13 @@ static int coroutine_fn vdi_co_create_opts(BlockDriver *drv, qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true); /* Create and open the file (protocol layer) */ - ret = bdrv_create_file(filename, opts, errp); + ret = bdrv_co_create_file(filename, opts, errp); if (ret < 0) { goto done; } - bs_file = bdrv_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); + bs_file = bdrv_co_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); if (!bs_file) { ret = -EIO; goto done; @@ -975,7 +977,7 @@ static int coroutine_fn vdi_co_create_opts(BlockDriver *drv, done: qobject_unref(qdict); qapi_free_BlockdevCreateOptions(create_options); - bdrv_unref(bs_file); + bdrv_co_unref(bs_file); return ret; } @@ -985,11 +987,10 @@ static void vdi_close(BlockDriverState *bs) qemu_vfree(s->bmap); - migrate_del_blocker(s->migration_blocker); - error_free(s->migration_blocker); + migrate_del_blocker(&s->migration_blocker); } -static int vdi_has_zero_init(BlockDriverState *bs) +static int GRAPH_RDLOCK vdi_has_zero_init(BlockDriverState *bs) { BDRVVdiState *s = bs->opaque; @@ -1049,7 +1050,7 @@ static BlockDriver bdrv_vdi = { .bdrv_co_pwritev = vdi_co_pwritev, #endif - .bdrv_get_info = vdi_get_info, + .bdrv_co_get_info = vdi_co_get_info, .is_format = true, .create_opts = &vdi_create_opts, diff --git a/block/vhdx-log.c b/block/vhdx-log.c index ff0d4e0da0..4385a2d4f6 100644 --- a/block/vhdx-log.c +++ b/block/vhdx-log.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "block/block-io.h" #include "block/block_int.h" #include "qemu/error-report.h" #include "qemu/bswap.h" @@ -54,8 +55,9 @@ static const MSGUID zero_guid = { 0 }; /* Allow peeking at the hdr entry at the beginning of the current * read index, without advancing the read index */ -static int vhdx_log_peek_hdr(BlockDriverState *bs, VHDXLogEntries *log, - VHDXLogEntryHeader *hdr) +static int GRAPH_RDLOCK +vhdx_log_peek_hdr(BlockDriverState *bs, VHDXLogEntries *log, + VHDXLogEntryHeader *hdr) { int ret = 0; uint64_t offset; @@ -84,7 +86,7 @@ static int vhdx_log_peek_hdr(BlockDriverState *bs, VHDXLogEntries *log, offset = log->offset + read; - ret = bdrv_pread(bs->file, offset, hdr, sizeof(VHDXLogEntryHeader)); + ret = bdrv_pread(bs->file, offset, sizeof(VHDXLogEntryHeader), hdr, 0); if (ret < 0) { goto exit; } @@ -106,7 +108,7 @@ static int vhdx_log_inc_idx(uint32_t idx, uint64_t length) /* Reset the log to empty */ -static void vhdx_log_reset(BlockDriverState *bs, BDRVVHDXState *s) +static void GRAPH_RDLOCK vhdx_log_reset(BlockDriverState *bs, BDRVVHDXState *s) { MSGUID guid = { 0 }; s->log.read = s->log.write = 0; @@ -126,9 +128,10 @@ static void vhdx_log_reset(BlockDriverState *bs, BDRVVHDXState *s) * not modified. * * 0 is returned on success, -errno otherwise. */ -static int vhdx_log_read_sectors(BlockDriverState *bs, VHDXLogEntries *log, - uint32_t *sectors_read, void *buffer, - uint32_t num_sectors, bool peek) +static int GRAPH_RDLOCK +vhdx_log_read_sectors(BlockDriverState *bs, VHDXLogEntries *log, + uint32_t *sectors_read, void *buffer, + uint32_t num_sectors, bool peek) { int ret = 0; uint64_t offset; @@ -144,7 +147,7 @@ static int vhdx_log_read_sectors(BlockDriverState *bs, VHDXLogEntries *log, } offset = log->offset + read; - ret = bdrv_pread(bs->file, offset, buffer, VHDX_LOG_SECTOR_SIZE); + ret = bdrv_pread(bs->file, offset, VHDX_LOG_SECTOR_SIZE, buffer, 0); if (ret < 0) { goto exit; } @@ -168,9 +171,10 @@ exit: * It is assumed that 'buffer' is at least 4096*num_sectors large. * * 0 is returned on success, -errno otherwise */ -static int vhdx_log_write_sectors(BlockDriverState *bs, VHDXLogEntries *log, - uint32_t *sectors_written, void *buffer, - uint32_t num_sectors) +static int coroutine_fn GRAPH_RDLOCK +vhdx_log_write_sectors(BlockDriverState *bs, VHDXLogEntries *log, + uint32_t *sectors_written, void *buffer, + uint32_t num_sectors) { int ret = 0; uint64_t offset; @@ -194,8 +198,7 @@ static int vhdx_log_write_sectors(BlockDriverState *bs, VHDXLogEntries *log, /* full */ break; } - ret = bdrv_pwrite(bs->file, offset, buffer_tmp, - VHDX_LOG_SECTOR_SIZE); + ret = bdrv_co_pwrite(bs->file, offset, VHDX_LOG_SECTOR_SIZE, buffer_tmp, 0); if (ret < 0) { goto exit; } @@ -332,9 +335,9 @@ static int vhdx_compute_desc_sectors(uint32_t desc_cnt) * will allocate all the space for buffer, which must be NULL when * passed into this function. Each descriptor will also be validated, * and error returned if any are invalid. */ -static int vhdx_log_read_desc(BlockDriverState *bs, BDRVVHDXState *s, - VHDXLogEntries *log, VHDXLogDescEntries **buffer, - bool convert_endian) +static int GRAPH_RDLOCK +vhdx_log_read_desc(BlockDriverState *bs, BDRVVHDXState *s, VHDXLogEntries *log, + VHDXLogDescEntries **buffer, bool convert_endian) { int ret = 0; uint32_t desc_sectors; @@ -411,8 +414,9 @@ exit: * For a zero descriptor, it may describe multiple sectors to fill with zeroes. * In this case, it should be noted that zeroes are written to disk, and the * image file is not extended as a sparse file. */ -static int vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc, - VHDXLogDataSector *data) +static int GRAPH_RDLOCK +vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc, + VHDXLogDataSector *data) { int ret = 0; uint64_t seq, file_offset; @@ -466,8 +470,8 @@ static int vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc, /* count is only > 1 if we are writing zeroes */ for (i = 0; i < count; i++) { - ret = bdrv_pwrite_sync(bs->file, file_offset, buffer, - VHDX_LOG_SECTOR_SIZE); + ret = bdrv_pwrite_sync(bs->file, file_offset, VHDX_LOG_SECTOR_SIZE, + buffer, 0); if (ret < 0) { goto exit; } @@ -483,8 +487,8 @@ exit: * file, and then set the log to 'empty' status once complete. * * The log entries should be validate prior to flushing */ -static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s, - VHDXLogSequence *logs) +static int GRAPH_RDLOCK +vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s, VHDXLogSequence *logs) { int ret = 0; int i; @@ -583,9 +587,10 @@ exit: return ret; } -static int vhdx_validate_log_entry(BlockDriverState *bs, BDRVVHDXState *s, - VHDXLogEntries *log, uint64_t seq, - bool *valid, VHDXLogEntryHeader *entry) +static int GRAPH_RDLOCK +vhdx_validate_log_entry(BlockDriverState *bs, BDRVVHDXState *s, + VHDXLogEntries *log, uint64_t seq, + bool *valid, VHDXLogEntryHeader *entry) { int ret = 0; VHDXLogEntryHeader hdr; @@ -662,8 +667,8 @@ free_and_exit: /* Search through the log circular buffer, and find the valid, active * log sequence, if any exists * */ -static int vhdx_log_search(BlockDriverState *bs, BDRVVHDXState *s, - VHDXLogSequence *logs) +static int GRAPH_RDLOCK +vhdx_log_search(BlockDriverState *bs, BDRVVHDXState *s, VHDXLogSequence *logs) { int ret = 0; uint32_t tail; @@ -852,8 +857,9 @@ static void vhdx_log_raw_to_le_sector(VHDXLogDescriptor *desc, } -static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s, - void *data, uint32_t length, uint64_t offset) +static int coroutine_fn GRAPH_RDLOCK +vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s, + void *data, uint32_t length, uint64_t offset) { int ret = 0; void *buffer = NULL; @@ -923,7 +929,7 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s, sectors += partial_sectors; - file_length = bdrv_getlength(bs->file->bs); + file_length = bdrv_co_getlength(bs->file->bs); if (file_length < 0) { ret = file_length; goto exit; @@ -970,8 +976,8 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s, if (i == 0 && leading_length) { /* partial sector at the front of the buffer */ - ret = bdrv_pread(bs->file, file_offset, merged_sector, - VHDX_LOG_SECTOR_SIZE); + ret = bdrv_co_pread(bs->file, file_offset, VHDX_LOG_SECTOR_SIZE, + merged_sector, 0); if (ret < 0) { goto exit; } @@ -980,10 +986,9 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s, sector_write = merged_sector; } else if (i == sectors - 1 && trailing_length) { /* partial sector at the end of the buffer */ - ret = bdrv_pread(bs->file, - file_offset, - merged_sector + trailing_length, - VHDX_LOG_SECTOR_SIZE - trailing_length); + ret = bdrv_co_pread(bs->file, file_offset + trailing_length, + VHDX_LOG_SECTOR_SIZE - trailing_length, + merged_sector + trailing_length, 0); if (ret < 0) { goto exit; } @@ -1036,8 +1041,9 @@ exit: } /* Perform a log write, and then immediately flush the entire log */ -int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s, - void *data, uint32_t length, uint64_t offset) +int coroutine_fn +vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s, + void *data, uint32_t length, uint64_t offset) { int ret = 0; VHDXLogSequence logs = { .valid = true, @@ -1047,7 +1053,7 @@ int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s, /* Make sure data written (new and/or changed blocks) is stable * on disk, before creating log entry */ - ret = bdrv_flush(bs); + ret = bdrv_co_flush(bs); if (ret < 0) { goto exit; } @@ -1059,7 +1065,7 @@ int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s, logs.log = s->log; /* Make sure log is stable on disk */ - ret = bdrv_flush(bs); + ret = bdrv_co_flush(bs); if (ret < 0) { goto exit; } diff --git a/block/vhdx.c b/block/vhdx.c index 410c6f9610..5aa1a13506 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -326,7 +326,7 @@ static int vhdx_write_header(BdrvChild *file, VHDXHeader *hdr, buffer = qemu_blockalign(bs_file, VHDX_HEADER_SIZE); if (read) { /* if true, we can't assume the extra reserved bytes are 0 */ - ret = bdrv_pread(file, offset, buffer, VHDX_HEADER_SIZE); + ret = bdrv_pread(file, offset, VHDX_HEADER_SIZE, buffer, 0); if (ret < 0) { goto exit; } @@ -340,7 +340,7 @@ static int vhdx_write_header(BdrvChild *file, VHDXHeader *hdr, vhdx_header_le_export(hdr, header_le); vhdx_update_checksum(buffer, VHDX_HEADER_SIZE, offsetof(VHDXHeader, checksum)); - ret = bdrv_pwrite_sync(file, offset, header_le, sizeof(VHDXHeader)); + ret = bdrv_pwrite_sync(file, offset, sizeof(VHDXHeader), header_le, 0); exit: qemu_vfree(buffer); @@ -353,8 +353,9 @@ exit: * * - non-current header is updated with largest sequence number */ -static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s, - bool generate_data_write_guid, MSGUID *log_guid) +static int GRAPH_RDLOCK +vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s, + bool generate_data_write_guid, MSGUID *log_guid) { int ret = 0; int hdr_idx = 0; @@ -416,8 +417,8 @@ int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s, } /* opens the specified header block from the VHDX file header section */ -static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, - Error **errp) +static void GRAPH_RDLOCK +vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, Error **errp) { int ret; VHDXHeader *header1; @@ -440,8 +441,8 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, /* We have to read the whole VHDX_HEADER_SIZE instead of * sizeof(VHDXHeader), because the checksum is over the whole * region */ - ret = bdrv_pread(bs->file, VHDX_HEADER1_OFFSET, buffer, - VHDX_HEADER_SIZE); + ret = bdrv_pread(bs->file, VHDX_HEADER1_OFFSET, VHDX_HEADER_SIZE, buffer, + 0); if (ret < 0) { goto fail; } @@ -457,8 +458,8 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, } } - ret = bdrv_pread(bs->file, VHDX_HEADER2_OFFSET, buffer, - VHDX_HEADER_SIZE); + ret = bdrv_pread(bs->file, VHDX_HEADER2_OFFSET, VHDX_HEADER_SIZE, buffer, + 0); if (ret < 0) { goto fail; } @@ -517,7 +518,8 @@ exit: } -static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s) +static int GRAPH_RDLOCK +vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s) { int ret = 0; uint8_t *buffer; @@ -531,8 +533,8 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s) * whole block */ buffer = qemu_blockalign(bs, VHDX_HEADER_BLOCK_SIZE); - ret = bdrv_pread(bs->file, VHDX_REGION_TABLE_OFFSET, buffer, - VHDX_HEADER_BLOCK_SIZE); + ret = bdrv_pread(bs->file, VHDX_REGION_TABLE_OFFSET, + VHDX_HEADER_BLOCK_SIZE, buffer, 0); if (ret < 0) { goto fail; } @@ -634,7 +636,8 @@ fail: * Also, if the File Parameters indicate this is a differencing file, * we must also look for the Parent Locator metadata item. */ -static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) +static int GRAPH_RDLOCK +vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) { int ret = 0; uint8_t *buffer; @@ -644,8 +647,8 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) buffer = qemu_blockalign(bs, VHDX_METADATA_TABLE_MAX_SIZE); - ret = bdrv_pread(bs->file, s->metadata_rt.file_offset, buffer, - VHDX_METADATA_TABLE_MAX_SIZE); + ret = bdrv_pread(bs->file, s->metadata_rt.file_offset, + VHDX_METADATA_TABLE_MAX_SIZE, buffer, 0); if (ret < 0) { goto exit; } @@ -750,8 +753,9 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) ret = bdrv_pread(bs->file, s->metadata_entries.file_parameters_entry.offset + s->metadata_rt.file_offset, + sizeof(s->params), &s->params, - sizeof(s->params)); + 0); if (ret < 0) { goto exit; @@ -785,24 +789,27 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) ret = bdrv_pread(bs->file, s->metadata_entries.virtual_disk_size_entry.offset + s->metadata_rt.file_offset, + sizeof(uint64_t), &s->virtual_disk_size, - sizeof(uint64_t)); + 0); if (ret < 0) { goto exit; } ret = bdrv_pread(bs->file, s->metadata_entries.logical_sector_size_entry.offset + s->metadata_rt.file_offset, + sizeof(uint32_t), &s->logical_sector_size, - sizeof(uint32_t)); + 0); if (ret < 0) { goto exit; } ret = bdrv_pread(bs->file, s->metadata_entries.phys_sector_size_entry.offset + s->metadata_rt.file_offset, + sizeof(uint32_t), &s->physical_sector_size, - sizeof(uint32_t)); + 0); if (ret < 0) { goto exit; } @@ -881,7 +888,8 @@ static void vhdx_calc_bat_entries(BDRVVHDXState *s) } -static int vhdx_check_bat_entries(BlockDriverState *bs, int *errcnt) +static int coroutine_mixed_fn GRAPH_RDLOCK +vhdx_check_bat_entries(BlockDriverState *bs, int *errcnt) { BDRVVHDXState *s = bs->opaque; int64_t image_file_size = bdrv_getlength(bs->file->bs); @@ -981,8 +989,7 @@ static void vhdx_close(BlockDriverState *bs) s->bat = NULL; qemu_vfree(s->parent_entries); s->parent_entries = NULL; - migrate_del_blocker(s->migration_blocker); - error_free(s->migration_blocker); + migrate_del_blocker(&s->migration_blocker); qemu_vfree(s->log.hdr); s->log.hdr = NULL; vhdx_region_unregister_all(s); @@ -997,12 +1004,15 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, uint64_t signature; Error *local_err = NULL; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_IMAGE, false, errp); - if (!bs->file) { - return -EINVAL; + GLOBAL_STATE_CODE(); + + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + s->bat = NULL; s->first_visible_write = true; @@ -1010,7 +1020,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, QLIST_INIT(&s->regions); /* validate the file signature */ - ret = bdrv_pread(bs->file, 0, &signature, sizeof(uint64_t)); + ret = bdrv_pread(bs->file, 0, sizeof(uint64_t), &signature, 0); if (ret < 0) { goto fail; } @@ -1069,12 +1079,12 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file, s->bat_offset, s->bat, s->bat_rt.length); + ret = bdrv_pread(bs->file, s->bat_offset, s->bat_rt.length, s->bat, 0); if (ret < 0) { goto fail; } - /* endian convert populated BAT field entires */ + /* endian convert populated BAT field entries */ for (i = 0; i < s->bat_entries; i++) { s->bat[i] = le64_to_cpu(s->bat[i]); } @@ -1090,9 +1100,8 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, error_setg(&s->migration_blocker, "The vhdx format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - ret = migrate_add_blocker(s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { - error_free(s->migration_blocker); goto fail; } @@ -1158,7 +1167,8 @@ static void vhdx_block_translate(BDRVVHDXState *s, int64_t sector_num, } -static int vhdx_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +vhdx_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVVHDXState *s = bs->opaque; @@ -1168,8 +1178,9 @@ static int vhdx_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) } -static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) +static int coroutine_fn GRAPH_RDLOCK +vhdx_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + QEMUIOVector *qiov) { BDRVVHDXState *s = bs->opaque; int ret = 0; @@ -1245,12 +1256,13 @@ exit: * * Returns the file offset start of the new payload block */ -static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s, - uint64_t *new_offset, bool *need_zero) +static int coroutine_fn GRAPH_RDLOCK +vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s, + uint64_t *new_offset, bool *need_zero) { int64_t current_len; - current_len = bdrv_getlength(bs->file->bs); + current_len = bdrv_co_getlength(bs->file->bs); if (current_len < 0) { return current_len; } @@ -1266,16 +1278,16 @@ static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s, if (*need_zero) { int ret; - ret = bdrv_truncate(bs->file, *new_offset + s->block_size, false, - PREALLOC_MODE_OFF, BDRV_REQ_ZERO_WRITE, NULL); + ret = bdrv_co_truncate(bs->file, *new_offset + s->block_size, false, + PREALLOC_MODE_OFF, BDRV_REQ_ZERO_WRITE, NULL); if (ret != -ENOTSUP) { *need_zero = false; return ret; } } - return bdrv_truncate(bs->file, *new_offset + s->block_size, false, - PREALLOC_MODE_OFF, 0, NULL); + return bdrv_co_truncate(bs->file, *new_offset + s->block_size, false, + PREALLOC_MODE_OFF, 0, NULL); } /* @@ -1320,9 +1332,9 @@ int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s) return ret; } -static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov, - int flags) +static int coroutine_fn GRAPH_RDLOCK +vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + QEMUIOVector *qiov, int flags) { int ret = -ENOTSUP; BDRVVHDXState *s = bs->opaque; @@ -1338,7 +1350,6 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, uint64_t bat_prior_offset = 0; bool bat_update = false; - assert(!flags); qemu_iovec_init(&hd_qiov, qiov->niov); qemu_co_mutex_lock(&s->lock); @@ -1502,14 +1513,17 @@ exit: * There are 2 headers, and the highest sequence number will represent * the active header */ -static int vhdx_create_new_headers(BlockBackend *blk, uint64_t image_size, - uint32_t log_size) +static int coroutine_fn GRAPH_UNLOCKED +vhdx_create_new_headers(BlockBackend *blk, uint64_t image_size, + uint32_t log_size) { BlockDriverState *bs = blk_bs(blk); BdrvChild *child; int ret = 0; VHDXHeader *hdr = NULL; + GRAPH_RDLOCK_GUARD(); + hdr = g_new0(VHDXHeader, 1); hdr->signature = VHDX_HEADER_SIGNATURE; @@ -1565,12 +1579,10 @@ exit: * The first 64KB of the Metadata section is reserved for the metadata * header and entries; beyond that, the metadata items themselves reside. */ -static int vhdx_create_new_metadata(BlockBackend *blk, - uint64_t image_size, - uint32_t block_size, - uint32_t sector_size, - uint64_t metadata_offset, - VHDXImageType type) +static int coroutine_fn +vhdx_create_new_metadata(BlockBackend *blk, uint64_t image_size, + uint32_t block_size, uint32_t sector_size, + uint64_t metadata_offset, VHDXImageType type) { int ret = 0; uint32_t offset = 0; @@ -1661,13 +1673,13 @@ static int vhdx_create_new_metadata(BlockBackend *blk, VHDX_META_FLAGS_IS_VIRTUAL_DISK; vhdx_metadata_entry_le_export(&md_table_entry[4]); - ret = blk_pwrite(blk, metadata_offset, buffer, VHDX_HEADER_BLOCK_SIZE, 0); + ret = blk_co_pwrite(blk, metadata_offset, VHDX_HEADER_BLOCK_SIZE, buffer, 0); if (ret < 0) { goto exit; } - ret = blk_pwrite(blk, metadata_offset + (64 * KiB), entry_buffer, - VHDX_METADATA_ENTRY_BUFFER_SIZE, 0); + ret = blk_co_pwrite(blk, metadata_offset + (64 * KiB), + VHDX_METADATA_ENTRY_BUFFER_SIZE, entry_buffer, 0); if (ret < 0) { goto exit; } @@ -1687,10 +1699,11 @@ exit: * Fixed images: default state of the BAT is fully populated, with * file offsets and state PAYLOAD_BLOCK_FULLY_PRESENT. */ -static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, - uint64_t image_size, VHDXImageType type, - bool use_zero_blocks, uint64_t file_offset, - uint32_t length, Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, + uint64_t image_size, VHDXImageType type, + bool use_zero_blocks, uint64_t file_offset, + uint32_t length, Error **errp) { int ret = 0; uint64_t data_file_offset; @@ -1699,6 +1712,7 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, uint64_t unused; int block_state; VHDXSectorInfo sinfo; + bool has_zero_init; assert(s->bat == NULL); @@ -1711,14 +1725,14 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, if (type == VHDX_TYPE_DYNAMIC) { /* All zeroes, so we can just extend the file - the end of the BAT * is the furthest thing we have written yet */ - ret = blk_truncate(blk, data_file_offset, false, PREALLOC_MODE_OFF, - 0, errp); + ret = blk_co_truncate(blk, data_file_offset, false, PREALLOC_MODE_OFF, + 0, errp); if (ret < 0) { goto exit; } } else if (type == VHDX_TYPE_FIXED) { - ret = blk_truncate(blk, data_file_offset + image_size, false, - PREALLOC_MODE_OFF, 0, errp); + ret = blk_co_truncate(blk, data_file_offset + image_size, false, + PREALLOC_MODE_OFF, 0, errp); if (ret < 0) { goto exit; } @@ -1728,9 +1742,13 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, goto exit; } + bdrv_graph_co_rdlock(); + has_zero_init = bdrv_has_zero_init(blk_bs(blk)); + bdrv_graph_co_rdunlock(); + if (type == VHDX_TYPE_FIXED || use_zero_blocks || - bdrv_has_zero_init(blk_bs(blk)) == 0) { + has_zero_init == 0) { /* for a fixed file, the default BAT entry is not zero */ s->bat = g_try_malloc0(length); if (length && s->bat == NULL) { @@ -1752,7 +1770,7 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, s->bat[sinfo.bat_idx] = cpu_to_le64(s->bat[sinfo.bat_idx]); sector_num += s->sectors_per_block; } - ret = blk_pwrite(blk, file_offset, s->bat, length, 0); + ret = blk_co_pwrite(blk, file_offset, length, s->bat, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write the BAT"); goto exit; @@ -1773,15 +1791,12 @@ exit: * to create the BAT itself, we will also cause the BAT to be * created. */ -static int vhdx_create_new_region_table(BlockBackend *blk, - uint64_t image_size, - uint32_t block_size, - uint32_t sector_size, - uint32_t log_size, - bool use_zero_blocks, - VHDXImageType type, - uint64_t *metadata_offset, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +vhdx_create_new_region_table(BlockBackend *blk, uint64_t image_size, + uint32_t block_size, uint32_t sector_size, + uint32_t log_size, bool use_zero_blocks, + VHDXImageType type, uint64_t *metadata_offset, + Error **errp) { int ret = 0; uint32_t offset = 0; @@ -1856,15 +1871,15 @@ static int vhdx_create_new_region_table(BlockBackend *blk, } /* Now write out the region headers to disk */ - ret = blk_pwrite(blk, VHDX_REGION_TABLE_OFFSET, buffer, - VHDX_HEADER_BLOCK_SIZE, 0); + ret = blk_co_pwrite(blk, VHDX_REGION_TABLE_OFFSET, VHDX_HEADER_BLOCK_SIZE, + buffer, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write first region table"); goto exit; } - ret = blk_pwrite(blk, VHDX_REGION_TABLE2_OFFSET, buffer, - VHDX_HEADER_BLOCK_SIZE, 0); + ret = blk_co_pwrite(blk, VHDX_REGION_TABLE2_OFFSET, VHDX_HEADER_BLOCK_SIZE, + buffer, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write second region table"); goto exit; @@ -1893,8 +1908,8 @@ exit: * .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------. * 1MB */ -static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +vhdx_co_create(BlockdevCreateOptions *opts, Error **errp) { BlockdevCreateOptionsVhdx *vhdx_opts; BlockBackend *blk = NULL; @@ -1988,13 +2003,13 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, } /* Create BlockBackend to write to the image */ - bs = bdrv_open_blockdev_ref(vhdx_opts->file, errp); + bs = bdrv_co_open_blockdev_ref(vhdx_opts->file, errp); if (bs == NULL) { return -EIO; } - blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, - errp); + blk = blk_co_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, + errp); if (!blk) { ret = -EPERM; goto delete_and_exit; @@ -2008,15 +2023,15 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, creator = g_utf8_to_utf16("QEMU v" QEMU_VERSION, -1, NULL, &creator_items, NULL); signature = cpu_to_le64(VHDX_FILE_SIGNATURE); - ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature), - 0); + ret = blk_co_pwrite(blk, VHDX_FILE_ID_OFFSET, sizeof(signature), &signature, + 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write file signature"); goto delete_and_exit; } if (creator) { - ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET + sizeof(signature), - creator, creator_items * sizeof(gunichar2), 0); + ret = blk_co_pwrite(blk, VHDX_FILE_ID_OFFSET + sizeof(signature), + creator_items * sizeof(gunichar2), creator, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write creator field"); goto delete_and_exit; @@ -2049,16 +2064,15 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, ret = 0; delete_and_exit: - blk_unref(blk); - bdrv_unref(bs); + blk_co_unref(blk); + bdrv_co_unref(bs); g_free(creator); return ret; } -static int coroutine_fn vhdx_co_create_opts(BlockDriver *drv, - const char *filename, - QemuOpts *opts, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +vhdx_co_create_opts(BlockDriver *drv, const char *filename, + QemuOpts *opts, Error **errp) { BlockdevCreateOptions *create_options = NULL; QDict *qdict; @@ -2082,13 +2096,13 @@ static int coroutine_fn vhdx_co_create_opts(BlockDriver *drv, } /* Create and open the file (protocol layer) */ - ret = bdrv_create_file(filename, opts, errp); + ret = bdrv_co_create_file(filename, opts, errp); if (ret < 0) { goto fail; } - bs = bdrv_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); + bs = bdrv_co_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); if (bs == NULL) { ret = -EIO; goto fail; @@ -2141,7 +2155,7 @@ static int coroutine_fn vhdx_co_create_opts(BlockDriver *drv, fail: qobject_unref(qdict); - bdrv_unref(bs); + bdrv_co_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; } @@ -2153,9 +2167,9 @@ fail: * r/w and any log has already been replayed, so there is nothing (currently) * for us to do here */ -static int coroutine_fn vhdx_co_check(BlockDriverState *bs, - BdrvCheckResult *result, - BdrvCheckMode fix) +static int coroutine_fn GRAPH_RDLOCK +vhdx_co_check(BlockDriverState *bs, BdrvCheckResult *result, + BdrvCheckMode fix) { BDRVVHDXState *s = bs->opaque; @@ -2168,7 +2182,7 @@ static int coroutine_fn vhdx_co_check(BlockDriverState *bs, return 0; } -static int vhdx_has_zero_init(BlockDriverState *bs) +static int GRAPH_RDLOCK vhdx_has_zero_init(BlockDriverState *bs) { BDRVVHDXState *s = bs->opaque; int state; @@ -2243,7 +2257,7 @@ static BlockDriver bdrv_vhdx = { .bdrv_co_writev = vhdx_co_writev, .bdrv_co_create = vhdx_co_create, .bdrv_co_create_opts = vhdx_co_create_opts, - .bdrv_get_info = vhdx_get_info, + .bdrv_co_get_info = vhdx_co_get_info, .bdrv_co_check = vhdx_co_check, .bdrv_has_zero_init = vhdx_has_zero_init, diff --git a/block/vhdx.h b/block/vhdx.h index 0b74924cee..c6dd4d6040 100644 --- a/block/vhdx.h +++ b/block/vhdx.h @@ -212,7 +212,7 @@ typedef struct QEMU_PACKED VHDXLogDataSector { uint32_t sequence_high; /* 4 MSB of 8 byte sequence_number */ uint8_t data[4084]; /* raw data, bytes 8-4091 (inclusive). see the data descriptor field for the - other mising bytes */ + other missing bytes */ uint32_t sequence_low; /* 4 LSB of 8 byte sequence_number */ } VHDXLogDataSector; @@ -257,7 +257,7 @@ typedef struct QEMU_PACKED VHDXMetadataTableHeader { #define VHDX_META_FLAGS_IS_USER 0x01 /* max 1024 entries */ #define VHDX_META_FLAGS_IS_VIRTUAL_DISK 0x02 /* virtual disk metadata if set, - otherwise file metdata */ + otherwise file metadata */ #define VHDX_META_FLAGS_IS_REQUIRED 0x04 /* parse must understand this entry to open the file */ typedef struct QEMU_PACKED VHDXMetadataTableEntry { @@ -401,8 +401,9 @@ typedef struct BDRVVHDXState { void vhdx_guid_generate(MSGUID *guid); -int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s, bool rw, - MSGUID *log_guid); +int GRAPH_RDLOCK +vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s, bool rw, + MSGUID *log_guid); uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset); uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size, @@ -410,11 +411,13 @@ uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size, bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset); -int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed, - Error **errp); +int GRAPH_RDLOCK +vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed, + Error **errp); -int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s, - void *data, uint32_t length, uint64_t offset); +int coroutine_fn GRAPH_RDLOCK +vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s, + void *data, uint32_t length, uint64_t offset); static inline void leguid_to_cpus(MSGUID *guid) { @@ -446,6 +449,8 @@ void vhdx_metadata_header_le_import(VHDXMetadataTableHeader *hdr); void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr); void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e); void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e); -int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s); + +int GRAPH_RDLOCK +vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s); #endif diff --git a/block/vmdk.c b/block/vmdk.c index 38e5ab3806..3b82979fdf 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -272,6 +272,7 @@ static void vmdk_free_extents(BlockDriverState *bs) BDRVVmdkState *s = bs->opaque; VmdkExtent *e; + bdrv_graph_wrlock(); for (i = 0; i < s->num_extents; i++) { e = &s->extents[i]; g_free(e->l1_table); @@ -282,6 +283,8 @@ static void vmdk_free_extents(BlockDriverState *bs) bdrv_unref_child(bs, e->file); } } + bdrv_graph_wrunlock(); + g_free(s->extents); } @@ -297,7 +300,8 @@ static void vmdk_free_last_extent(BlockDriverState *bs) } /* Return -ve errno, or 0 on success and write CID into *pcid. */ -static int vmdk_read_cid(BlockDriverState *bs, int parent, uint32_t *pcid) +static int GRAPH_RDLOCK +vmdk_read_cid(BlockDriverState *bs, int parent, uint32_t *pcid) { char *desc; uint32_t cid; @@ -307,7 +311,7 @@ static int vmdk_read_cid(BlockDriverState *bs, int parent, uint32_t *pcid) int ret; desc = g_malloc0(DESC_SIZE); - ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); + ret = bdrv_pread(bs->file, s->desc_offset, DESC_SIZE, desc, 0); if (ret < 0) { goto out; } @@ -339,36 +343,49 @@ out: return ret; } -static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) +static int coroutine_fn GRAPH_RDLOCK +vmdk_write_cid(BlockDriverState *bs, uint32_t cid) { char *desc, *tmp_desc; char *p_name, *tmp_str; BDRVVmdkState *s = bs->opaque; int ret = 0; - desc = g_malloc0(DESC_SIZE); - tmp_desc = g_malloc0(DESC_SIZE); - ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); + size_t desc_buf_size; + + if (s->desc_offset == 0) { + desc_buf_size = bdrv_getlength(bs->file->bs); + if (desc_buf_size > 16ULL << 20) { + error_report("VMDK description file too big"); + return -EFBIG; + } + } else { + desc_buf_size = DESC_SIZE; + } + + desc = g_malloc0(desc_buf_size); + tmp_desc = g_malloc0(desc_buf_size); + ret = bdrv_co_pread(bs->file, s->desc_offset, desc_buf_size, desc, 0); if (ret < 0) { goto out; } - desc[DESC_SIZE - 1] = '\0'; + desc[desc_buf_size - 1] = '\0'; tmp_str = strstr(desc, "parentCID"); if (tmp_str == NULL) { ret = -EINVAL; goto out; } - pstrcpy(tmp_desc, DESC_SIZE, tmp_str); + pstrcpy(tmp_desc, desc_buf_size, tmp_str); p_name = strstr(desc, "CID"); if (p_name != NULL) { p_name += sizeof("CID"); - snprintf(p_name, DESC_SIZE - (p_name - desc), "%" PRIx32 "\n", cid); - pstrcat(desc, DESC_SIZE, tmp_desc); + snprintf(p_name, desc_buf_size - (p_name - desc), "%" PRIx32 "\n", cid); + pstrcat(desc, desc_buf_size, tmp_desc); } - ret = bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE); + ret = bdrv_co_pwrite_sync(bs->file, s->desc_offset, desc_buf_size, desc, 0); out: g_free(desc); @@ -376,7 +393,7 @@ out: return ret; } -static int vmdk_is_cid_valid(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK vmdk_is_cid_valid(BlockDriverState *bs) { BDRVVmdkState *s = bs->opaque; uint32_t cur_pcid; @@ -411,6 +428,9 @@ static int vmdk_reopen_prepare(BDRVReopenState *state, BDRVVmdkReopenState *rs; int i; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + assert(state != NULL); assert(state->bs != NULL); assert(state->opaque == NULL); @@ -447,6 +467,9 @@ static void vmdk_reopen_commit(BDRVReopenState *state) BDRVVmdkReopenState *rs = state->opaque; int i; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + for (i = 0; i < s->num_extents; i++) { if (rs->extents_using_bs_file[i]) { s->extents[i].file = state->bs->file; @@ -461,7 +484,7 @@ static void vmdk_reopen_abort(BDRVReopenState *state) vmdk_reopen_clean(state); } -static int vmdk_parent_open(BlockDriverState *bs) +static int GRAPH_RDLOCK vmdk_parent_open(BlockDriverState *bs) { char *p_name; char *desc; @@ -469,11 +492,10 @@ static int vmdk_parent_open(BlockDriverState *bs) int ret; desc = g_malloc0(DESC_SIZE + 1); - ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); + ret = bdrv_pread(bs->file, s->desc_offset, DESC_SIZE, desc, 0); if (ret < 0) { goto out; } - ret = 0; p_name = strstr(desc, "parentFileNameHint"); if (p_name != NULL) { @@ -575,8 +597,8 @@ static int vmdk_add_extent(BlockDriverState *bs, return 0; } -static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, - Error **errp) +static int GRAPH_RDLOCK +vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, Error **errp) { int ret; size_t l1_size; @@ -589,10 +611,8 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, return -ENOMEM; } - ret = bdrv_pread(extent->file, - extent->l1_table_offset, - extent->l1_table, - l1_size); + ret = bdrv_pread(extent->file, extent->l1_table_offset, l1_size, + extent->l1_table, 0); if (ret < 0) { bdrv_refresh_filename(extent->file->bs); error_setg_errno(errp, -ret, @@ -616,10 +636,8 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, ret = -ENOMEM; goto fail_l1; } - ret = bdrv_pread(extent->file, - extent->l1_backup_table_offset, - extent->l1_backup_table, - l1_size); + ret = bdrv_pread(extent->file, extent->l1_backup_table_offset, + l1_size, extent->l1_backup_table, 0); if (ret < 0) { bdrv_refresh_filename(extent->file->bs); error_setg_errno(errp, -ret, @@ -642,16 +660,16 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, return ret; } -static int vmdk_open_vmfs_sparse(BlockDriverState *bs, - BdrvChild *file, - int flags, Error **errp) +static int GRAPH_RDLOCK +vmdk_open_vmfs_sparse(BlockDriverState *bs, BdrvChild *file, int flags, + Error **errp) { int ret; uint32_t magic; VMDK3Header header; VmdkExtent *extent = NULL; - ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); + ret = bdrv_pread(file, sizeof(magic), sizeof(header), &header, 0); if (ret < 0) { bdrv_refresh_filename(file->bs); error_setg_errno(errp, -ret, @@ -798,9 +816,9 @@ static int check_se_sparse_volatile_header(VMDKSESparseVolatileHeader *header, return 0; } -static int vmdk_open_se_sparse(BlockDriverState *bs, - BdrvChild *file, - int flags, Error **errp) +static int GRAPH_RDLOCK +vmdk_open_se_sparse(BlockDriverState *bs, BdrvChild *file, int flags, + Error **errp) { int ret; VMDKSESparseConstHeader const_header; @@ -815,7 +833,7 @@ static int vmdk_open_se_sparse(BlockDriverState *bs, assert(sizeof(const_header) == SECTOR_SIZE); - ret = bdrv_pread(file, 0, &const_header, sizeof(const_header)); + ret = bdrv_pread(file, 0, sizeof(const_header), &const_header, 0); if (ret < 0) { bdrv_refresh_filename(file->bs); error_setg_errno(errp, -ret, @@ -832,9 +850,8 @@ static int vmdk_open_se_sparse(BlockDriverState *bs, assert(sizeof(volatile_header) == SECTOR_SIZE); - ret = bdrv_pread(file, - const_header.volatile_header_offset * SECTOR_SIZE, - &volatile_header, sizeof(volatile_header)); + ret = bdrv_pread(file, const_header.volatile_header_offset * SECTOR_SIZE, + sizeof(volatile_header), &volatile_header, 0); if (ret < 0) { bdrv_refresh_filename(file->bs); error_setg_errno(errp, -ret, @@ -904,20 +921,20 @@ static char *vmdk_read_desc(BdrvChild *file, uint64_t desc_offset, Error **errp) size = MIN(size, (1 << 20) - 1); /* avoid unbounded allocation */ buf = g_malloc(size + 1); - ret = bdrv_pread(file, desc_offset, buf, size); + ret = bdrv_pread(file, desc_offset, size, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read from file"); g_free(buf); return NULL; } - buf[ret] = 0; + buf[size] = 0; return buf; } -static int vmdk_open_vmdk4(BlockDriverState *bs, - BdrvChild *file, - int flags, QDict *options, Error **errp) +static int GRAPH_RDLOCK +vmdk_open_vmdk4(BlockDriverState *bs, BdrvChild *file, int flags, + QDict *options, Error **errp) { int ret; uint32_t magic; @@ -928,7 +945,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, int64_t l1_backup_offset = 0; bool compressed; - ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); + ret = bdrv_pread(file, sizeof(magic), sizeof(header), &header, 0); if (ret < 0) { bdrv_refresh_filename(file->bs); error_setg_errno(errp, -ret, @@ -979,9 +996,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, } QEMU_PACKED eos_marker; } QEMU_PACKED footer; - ret = bdrv_pread(file, - bs->file->bs->total_sectors * 512 - 1536, - &footer, sizeof(footer)); + ret = bdrv_pread(file, bs->file->bs->total_sectors * 512 - 1536, + sizeof(footer), &footer, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to read footer"); return ret; @@ -1098,8 +1114,9 @@ static int vmdk_parse_description(const char *desc, const char *opt_name, } /* Open an extent file and append to bs array */ -static int vmdk_open_sparse(BlockDriverState *bs, BdrvChild *file, int flags, - char *buf, QDict *options, Error **errp) +static int GRAPH_RDLOCK +vmdk_open_sparse(BlockDriverState *bs, BdrvChild *file, int flags, + char *buf, QDict *options, Error **errp) { uint32_t magic; @@ -1126,9 +1143,11 @@ static const char *next_line(const char *s) return s; } -static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, - QDict *options, Error **errp) +static int GRAPH_RDLOCK +vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options, + Error **errp) { + ERRP_GUARD(); int ret; int matches; char access[11]; @@ -1146,6 +1165,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, char extent_opt_prefix[32]; Error *local_err = NULL; + GLOBAL_STATE_CODE(); + for (p = desc; *p; p = next_line(p)) { /* parse extent line in one of below formats: * @@ -1213,7 +1234,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, bs, &child_of_bds, extent_role, false, &local_err); g_free(extent_path); - if (local_err) { + if (!extent_file) { error_propagate(errp, local_err); ret = -EINVAL; goto out; @@ -1226,7 +1247,11 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, ret = vmdk_add_extent(bs, extent_file, true, sectors, 0, 0, 0, 0, 0, &extent, errp); if (ret < 0) { + bdrv_graph_rdunlock_main_loop(); + bdrv_graph_wrlock(); bdrv_unref_child(bs, extent_file); + bdrv_graph_wrunlock(); + bdrv_graph_rdlock_main_loop(); goto out; } extent->flat_start_offset = flat_offset << 9; @@ -1241,20 +1266,32 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, } g_free(buf); if (ret) { + bdrv_graph_rdunlock_main_loop(); + bdrv_graph_wrlock(); bdrv_unref_child(bs, extent_file); + bdrv_graph_wrunlock(); + bdrv_graph_rdlock_main_loop(); goto out; } extent = &s->extents[s->num_extents - 1]; } else if (!strcmp(type, "SESPARSE")) { ret = vmdk_open_se_sparse(bs, extent_file, bs->open_flags, errp); if (ret) { + bdrv_graph_rdunlock_main_loop(); + bdrv_graph_wrlock(); bdrv_unref_child(bs, extent_file); + bdrv_graph_wrunlock(); + bdrv_graph_rdlock_main_loop(); goto out; } extent = &s->extents[s->num_extents - 1]; } else { error_setg(errp, "Unsupported extent type '%s'", type); + bdrv_graph_rdunlock_main_loop(); + bdrv_graph_wrlock(); bdrv_unref_child(bs, extent_file); + bdrv_graph_wrunlock(); + bdrv_graph_rdlock_main_loop(); ret = -ENOTSUP; goto out; } @@ -1278,8 +1315,9 @@ out: return ret; } -static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf, - QDict *options, Error **errp) +static int GRAPH_RDLOCK +vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf, QDict *options, + Error **errp) { int ret; char ct[128]; @@ -1315,10 +1353,11 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, BDRVVmdkState *s = bs->opaque; uint32_t magic; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_IMAGE, false, errp); - if (!bs->file) { - return -EINVAL; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; } buf = vmdk_read_desc(bs->file, 0, errp); @@ -1367,9 +1406,8 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, error_setg(&s->migration_blocker, "The vmdk format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - ret = migrate_add_blocker(s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { - error_free(s->migration_blocker); goto fail; } @@ -1411,13 +1449,11 @@ static void vmdk_refresh_limits(BlockDriverState *bs, Error **errp) * [@skip_start_sector, @skip_end_sector) is not copied or written, and leave * it for call to write user data in the request. */ -static int get_whole_cluster(BlockDriverState *bs, - VmdkExtent *extent, - uint64_t cluster_offset, - uint64_t offset, - uint64_t skip_start_bytes, - uint64_t skip_end_bytes, - bool zeroed) +static int coroutine_fn GRAPH_RDLOCK +get_whole_cluster(BlockDriverState *bs, VmdkExtent *extent, + uint64_t cluster_offset, uint64_t offset, + uint64_t skip_start_bytes, uint64_t skip_end_bytes, + bool zeroed) { int ret = VMDK_OK; int64_t cluster_bytes; @@ -1447,17 +1483,17 @@ static int get_whole_cluster(BlockDriverState *bs, if (skip_start_bytes > 0) { if (copy_from_backing) { /* qcow2 emits this on bs->file instead of bs->backing */ - BLKDBG_EVENT(extent->file, BLKDBG_COW_READ); - ret = bdrv_pread(bs->backing, offset, whole_grain, - skip_start_bytes); + BLKDBG_CO_EVENT(extent->file, BLKDBG_COW_READ); + ret = bdrv_co_pread(bs->backing, offset, skip_start_bytes, + whole_grain, 0); if (ret < 0) { ret = VMDK_ERROR; goto exit; } } - BLKDBG_EVENT(extent->file, BLKDBG_COW_WRITE); - ret = bdrv_pwrite(extent->file, cluster_offset, whole_grain, - skip_start_bytes); + BLKDBG_CO_EVENT(extent->file, BLKDBG_COW_WRITE); + ret = bdrv_co_pwrite(extent->file, cluster_offset, skip_start_bytes, + whole_grain, 0); if (ret < 0) { ret = VMDK_ERROR; goto exit; @@ -1467,19 +1503,19 @@ static int get_whole_cluster(BlockDriverState *bs, if (skip_end_bytes < cluster_bytes) { if (copy_from_backing) { /* qcow2 emits this on bs->file instead of bs->backing */ - BLKDBG_EVENT(extent->file, BLKDBG_COW_READ); - ret = bdrv_pread(bs->backing, offset + skip_end_bytes, - whole_grain + skip_end_bytes, - cluster_bytes - skip_end_bytes); + BLKDBG_CO_EVENT(extent->file, BLKDBG_COW_READ); + ret = bdrv_co_pread(bs->backing, offset + skip_end_bytes, + cluster_bytes - skip_end_bytes, + whole_grain + skip_end_bytes, 0); if (ret < 0) { ret = VMDK_ERROR; goto exit; } } - BLKDBG_EVENT(extent->file, BLKDBG_COW_WRITE); - ret = bdrv_pwrite(extent->file, cluster_offset + skip_end_bytes, - whole_grain + skip_end_bytes, - cluster_bytes - skip_end_bytes); + BLKDBG_CO_EVENT(extent->file, BLKDBG_COW_WRITE); + ret = bdrv_co_pwrite(extent->file, cluster_offset + skip_end_bytes, + cluster_bytes - skip_end_bytes, + whole_grain + skip_end_bytes, 0); if (ret < 0) { ret = VMDK_ERROR; goto exit; @@ -1492,29 +1528,29 @@ exit: return ret; } -static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, - uint32_t offset) +static int coroutine_fn GRAPH_RDLOCK +vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, uint32_t offset) { offset = cpu_to_le32(offset); /* update L2 table */ - BLKDBG_EVENT(extent->file, BLKDBG_L2_UPDATE); - if (bdrv_pwrite(extent->file, - ((int64_t)m_data->l2_offset * 512) - + (m_data->l2_index * sizeof(offset)), - &offset, sizeof(offset)) < 0) { + BLKDBG_CO_EVENT(extent->file, BLKDBG_L2_UPDATE); + if (bdrv_co_pwrite(extent->file, + ((int64_t)m_data->l2_offset * 512) + + (m_data->l2_index * sizeof(offset)), + sizeof(offset), &offset, 0) < 0) { return VMDK_ERROR; } /* update backup L2 table */ if (extent->l1_backup_table_offset != 0) { m_data->l2_offset = extent->l1_backup_table[m_data->l1_index]; - if (bdrv_pwrite(extent->file, - ((int64_t)m_data->l2_offset * 512) - + (m_data->l2_index * sizeof(offset)), - &offset, sizeof(offset)) < 0) { + if (bdrv_co_pwrite(extent->file, + ((int64_t)m_data->l2_offset * 512) + + (m_data->l2_index * sizeof(offset)), + sizeof(offset), &offset, 0) < 0) { return VMDK_ERROR; } } - if (bdrv_flush(extent->file->bs) < 0) { + if (bdrv_co_flush(extent->file->bs) < 0) { return VMDK_ERROR; } if (m_data->l2_cache_entry) { @@ -1544,14 +1580,11 @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, * VMDK_UNALLOC if cluster is not mapped and @allocate is false. * VMDK_ERROR if failed. */ -static int get_cluster_offset(BlockDriverState *bs, - VmdkExtent *extent, - VmdkMetaData *m_data, - uint64_t offset, - bool allocate, - uint64_t *cluster_offset, - uint64_t skip_start_bytes, - uint64_t skip_end_bytes) +static int coroutine_fn GRAPH_RDLOCK +get_cluster_offset(BlockDriverState *bs, VmdkExtent *extent, + VmdkMetaData *m_data, uint64_t offset, bool allocate, + uint64_t *cluster_offset, uint64_t skip_start_bytes, + uint64_t skip_end_bytes) { unsigned int l1_index, l2_offset, l2_index; int min_index, i, j; @@ -1630,12 +1663,12 @@ static int get_cluster_offset(BlockDriverState *bs, } } l2_table = (char *)extent->l2_cache + (min_index * l2_size_bytes); - BLKDBG_EVENT(extent->file, BLKDBG_L2_LOAD); - if (bdrv_pread(extent->file, + BLKDBG_CO_EVENT(extent->file, BLKDBG_L2_LOAD); + if (bdrv_co_pread(extent->file, (int64_t)l2_offset * 512, - l2_table, - l2_size_bytes - ) != l2_size_bytes) { + l2_size_bytes, + l2_table, 0 + ) < 0) { return VMDK_ERROR; } @@ -1744,11 +1777,10 @@ static inline uint64_t vmdk_find_offset_in_cluster(VmdkExtent *extent, return extent_relative_offset % cluster_size; } -static int coroutine_fn vmdk_co_block_status(BlockDriverState *bs, - bool want_zero, - int64_t offset, int64_t bytes, - int64_t *pnum, int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +vmdk_co_block_status(BlockDriverState *bs, bool want_zero, + int64_t offset, int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file) { BDRVVmdkState *s = bs->opaque; int64_t index_in_cluster, n, ret; @@ -1783,6 +1815,8 @@ static int coroutine_fn vmdk_co_block_status(BlockDriverState *bs, if (extent->flat) { ret |= BDRV_BLOCK_RECURSE; } + } else { + ret |= BDRV_BLOCK_COMPRESSED; } *file = extent->file->bs; break; @@ -1793,10 +1827,11 @@ static int coroutine_fn vmdk_co_block_status(BlockDriverState *bs, return ret; } -static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset, - int64_t offset_in_cluster, QEMUIOVector *qiov, - uint64_t qiov_offset, uint64_t n_bytes, - uint64_t offset) +static int coroutine_fn GRAPH_RDLOCK +vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset, + int64_t offset_in_cluster, QEMUIOVector *qiov, + uint64_t qiov_offset, uint64_t n_bytes, + uint64_t offset) { int ret; VmdkGrainMarker *data = NULL; @@ -1841,12 +1876,12 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset, n_bytes = buf_len + sizeof(VmdkGrainMarker); qemu_iovec_init_buf(&local_qiov, data, n_bytes); - BLKDBG_EVENT(extent->file, BLKDBG_WRITE_COMPRESSED); + BLKDBG_CO_EVENT(extent->file, BLKDBG_WRITE_COMPRESSED); } else { qemu_iovec_init(&local_qiov, qiov->niov); qemu_iovec_concat(&local_qiov, qiov, qiov_offset, n_bytes); - BLKDBG_EVENT(extent->file, BLKDBG_WRITE_AIO); + BLKDBG_CO_EVENT(extent->file, BLKDBG_WRITE_AIO); } write_offset = cluster_offset + offset_in_cluster; @@ -1874,9 +1909,9 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset, return ret; } -static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset, - int64_t offset_in_cluster, QEMUIOVector *qiov, - int bytes) +static int coroutine_fn GRAPH_RDLOCK +vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset, + int64_t offset_in_cluster, QEMUIOVector *qiov, int bytes) { int ret; int cluster_bytes, buf_bytes; @@ -1888,7 +1923,7 @@ static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset, if (!extent->compressed) { - BLKDBG_EVENT(extent->file, BLKDBG_READ_AIO); + BLKDBG_CO_EVENT(extent->file, BLKDBG_READ_AIO); ret = bdrv_co_preadv(extent->file, cluster_offset + offset_in_cluster, bytes, qiov, 0); @@ -1902,10 +1937,9 @@ static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset, buf_bytes = cluster_bytes * 2; cluster_buf = g_malloc(buf_bytes); uncomp_buf = g_malloc(cluster_bytes); - BLKDBG_EVENT(extent->file, BLKDBG_READ_COMPRESSED); - ret = bdrv_pread(extent->file, - cluster_offset, - cluster_buf, buf_bytes); + BLKDBG_CO_EVENT(extent->file, BLKDBG_READ_COMPRESSED); + ret = bdrv_co_pread(extent->file, cluster_offset, buf_bytes, cluster_buf, + 0); if (ret < 0) { goto out; } @@ -1941,7 +1975,7 @@ static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset, return ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK vmdk_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -1981,7 +2015,7 @@ vmdk_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, qemu_iovec_concat(&local_qiov, qiov, bytes_done, n_bytes); /* qcow2 emits this on bs->file instead of bs->backing */ - BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); + BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); ret = bdrv_co_preadv(bs->backing, offset, n_bytes, &local_qiov, 0); if (ret < 0) { @@ -2023,9 +2057,9 @@ fail: * * Returns: error code with 0 for success. */ -static int vmdk_pwritev(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, - bool zeroed, bool zero_dry_run) +static int coroutine_fn GRAPH_RDLOCK +vmdk_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, bool zeroed, bool zero_dry_run) { BDRVVmdkState *s = bs->opaque; VmdkExtent *extent = NULL; @@ -2121,7 +2155,7 @@ static int vmdk_pwritev(BlockDriverState *bs, uint64_t offset, return 0; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK vmdk_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -2133,7 +2167,7 @@ vmdk_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, return ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK vmdk_co_pwritev_compressed(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov) { @@ -2145,13 +2179,13 @@ vmdk_co_pwritev_compressed(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t length; for (i = 0; i < s->num_extents; i++) { - length = bdrv_getlength(s->extents[i].file->bs); + length = bdrv_co_getlength(s->extents[i].file->bs); if (length < 0) { return length; } length = QEMU_ALIGN_UP(length, BDRV_SECTOR_SIZE); - ret = bdrv_truncate(s->extents[i].file, length, false, - PREALLOC_MODE_OFF, 0, NULL); + ret = bdrv_co_truncate(s->extents[i].file, length, false, + PREALLOC_MODE_OFF, 0, NULL); if (ret < 0) { return ret; } @@ -2161,10 +2195,9 @@ vmdk_co_pwritev_compressed(BlockDriverState *bs, int64_t offset, int64_t bytes, return vmdk_co_pwritev(bs, offset, bytes, qiov, 0); } -static int coroutine_fn vmdk_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, - int64_t bytes, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +vmdk_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + BdrvRequestFlags flags) { int ret; BDRVVmdkState *s = bs->opaque; @@ -2180,10 +2213,9 @@ static int coroutine_fn vmdk_co_pwrite_zeroes(BlockDriverState *bs, return ret; } -static int vmdk_init_extent(BlockBackend *blk, - int64_t filesize, bool flat, - bool compress, bool zeroed_grain, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +vmdk_init_extent(BlockBackend *blk, int64_t filesize, bool flat, bool compress, + bool zeroed_grain, Error **errp) { int ret, i; VMDK4Header header; @@ -2192,7 +2224,7 @@ static int vmdk_init_extent(BlockBackend *blk, int gd_buf_size; if (flat) { - ret = blk_truncate(blk, filesize, false, PREALLOC_MODE_OFF, 0, errp); + ret = blk_co_truncate(blk, filesize, false, PREALLOC_MODE_OFF, 0, errp); goto exit; } magic = cpu_to_be32(VMDK4_MAGIC); @@ -2244,19 +2276,19 @@ static int vmdk_init_extent(BlockBackend *blk, header.check_bytes[3] = 0xa; /* write all the data */ - ret = blk_pwrite(blk, 0, &magic, sizeof(magic), 0); + ret = blk_co_pwrite(blk, 0, sizeof(magic), &magic, 0); if (ret < 0) { error_setg(errp, QERR_IO_ERROR); goto exit; } - ret = blk_pwrite(blk, sizeof(magic), &header, sizeof(header), 0); + ret = blk_co_pwrite(blk, sizeof(magic), sizeof(header), &header, 0); if (ret < 0) { error_setg(errp, QERR_IO_ERROR); goto exit; } - ret = blk_truncate(blk, le64_to_cpu(header.grain_offset) << 9, false, - PREALLOC_MODE_OFF, 0, errp); + ret = blk_co_truncate(blk, le64_to_cpu(header.grain_offset) << 9, false, + PREALLOC_MODE_OFF, 0, errp); if (ret < 0) { goto exit; } @@ -2268,8 +2300,8 @@ static int vmdk_init_extent(BlockBackend *blk, i < gt_count; i++, tmp += gt_size) { gd_buf[i] = cpu_to_le32(tmp); } - ret = blk_pwrite(blk, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE, - gd_buf, gd_buf_size, 0); + ret = blk_co_pwrite(blk, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE, + gd_buf_size, gd_buf, 0); if (ret < 0) { error_setg(errp, QERR_IO_ERROR); goto exit; @@ -2280,8 +2312,8 @@ static int vmdk_init_extent(BlockBackend *blk, i < gt_count; i++, tmp += gt_size) { gd_buf[i] = cpu_to_le32(tmp); } - ret = blk_pwrite(blk, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE, - gd_buf, gd_buf_size, 0); + ret = blk_co_pwrite(blk, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE, + gd_buf_size, gd_buf, 0); if (ret < 0) { error_setg(errp, QERR_IO_ERROR); } @@ -2292,22 +2324,22 @@ exit: return ret; } -static int vmdk_create_extent(const char *filename, int64_t filesize, - bool flat, bool compress, bool zeroed_grain, - BlockBackend **pbb, - QemuOpts *opts, Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +vmdk_create_extent(const char *filename, int64_t filesize, bool flat, + bool compress, bool zeroed_grain, BlockBackend **pbb, + QemuOpts *opts, Error **errp) { int ret; BlockBackend *blk = NULL; - ret = bdrv_create_file(filename, opts, errp); + ret = bdrv_co_create_file(filename, opts, errp); if (ret < 0) { goto exit; } - blk = blk_new_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, - errp); + blk = blk_co_new_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, + errp); if (blk == NULL) { ret = -EIO; goto exit; @@ -2321,7 +2353,7 @@ exit: if (pbb) { *pbb = blk; } else { - blk_unref(blk); + blk_co_unref(blk); blk = NULL; } } @@ -2373,14 +2405,10 @@ static int filename_decompose(const char *filename, char *path, char *prefix, * non-split format. * idx >= 1: get the n-th extent if in a split subformat */ -typedef BlockBackend *(*vmdk_create_extent_fn)(int64_t size, - int idx, - bool flat, - bool split, - bool compress, - bool zeroed_grain, - void *opaque, - Error **errp); +typedef BlockBackend * coroutine_fn GRAPH_UNLOCKED_PTR + (*vmdk_create_extent_fn)(int64_t size, int idx, bool flat, bool split, + bool compress, bool zeroed_grain, void *opaque, + Error **errp); static void vmdk_desc_add_extent(GString *desc, const char *extent_line_fmt, @@ -2393,17 +2421,18 @@ static void vmdk_desc_add_extent(GString *desc, g_free(basename); } -static int coroutine_fn vmdk_co_do_create(int64_t size, - BlockdevVmdkSubformat subformat, - BlockdevVmdkAdapterType adapter_type, - const char *backing_file, - const char *hw_version, - const char *toolsversion, - bool compat6, - bool zeroed_grain, - vmdk_create_extent_fn extent_fn, - void *opaque, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +vmdk_co_do_create(int64_t size, + BlockdevVmdkSubformat subformat, + BlockdevVmdkAdapterType adapter_type, + const char *backing_file, + const char *hw_version, + const char *toolsversion, + bool compat6, + bool zeroed_grain, + vmdk_create_extent_fn extent_fn, + void *opaque, + Error **errp) { int extent_idx; BlockBackend *blk = NULL; @@ -2524,8 +2553,8 @@ static int coroutine_fn vmdk_co_do_create(int64_t size, } assert(full_backing); - backing = blk_new_open(full_backing, NULL, NULL, - BDRV_O_NO_BACKING, errp); + backing = blk_co_new_open(full_backing, NULL, NULL, + BDRV_O_NO_BACKING, errp); g_free(full_backing); if (backing == NULL) { ret = -EIO; @@ -2534,12 +2563,15 @@ static int coroutine_fn vmdk_co_do_create(int64_t size, if (strcmp(blk_bs(backing)->drv->format_name, "vmdk")) { error_setg(errp, "Invalid backing file format: %s. Must be vmdk", blk_bs(backing)->drv->format_name); - blk_unref(backing); + blk_co_unref(backing); ret = -EINVAL; goto exit; } + + bdrv_graph_co_rdlock(); ret = vmdk_read_cid(blk_bs(backing), 0, &parent_cid); - blk_unref(backing); + bdrv_graph_co_rdunlock(); + blk_co_unref(backing); if (ret) { error_setg(errp, "Failed to read parent CID"); goto exit; @@ -2560,14 +2592,14 @@ static int coroutine_fn vmdk_co_do_create(int64_t size, blk_bs(extent_blk)->filename); created_size += cur_size; extent_idx++; - blk_unref(extent_blk); + blk_co_unref(extent_blk); } /* Check whether we got excess extents */ extent_blk = extent_fn(-1, extent_idx, flat, split, compress, zeroed_grain, opaque, NULL); if (extent_blk) { - blk_unref(extent_blk); + blk_co_unref(extent_blk); error_setg(errp, "List of extents contains unused extents"); ret = -EINVAL; goto exit; @@ -2592,7 +2624,7 @@ static int coroutine_fn vmdk_co_do_create(int64_t size, desc_offset = 0x200; } - ret = blk_pwrite(blk, desc_offset, desc, desc_len, 0); + ret = blk_co_pwrite(blk, desc_offset, desc_len, desc, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write description"); goto exit; @@ -2600,7 +2632,7 @@ static int coroutine_fn vmdk_co_do_create(int64_t size, /* bdrv_pwrite write padding zeros to align to sector, we don't need that * for description file */ if (desc_offset == 0) { - ret = blk_truncate(blk, desc_len, false, PREALLOC_MODE_OFF, 0, errp); + ret = blk_co_truncate(blk, desc_len, false, PREALLOC_MODE_OFF, 0, errp); if (ret < 0) { goto exit; } @@ -2608,7 +2640,7 @@ static int coroutine_fn vmdk_co_do_create(int64_t size, ret = 0; exit: if (blk) { - blk_unref(blk); + blk_co_unref(blk); } g_free(desc); g_free(parent_desc_line); @@ -2623,10 +2655,10 @@ typedef struct { QemuOpts *opts; } VMDKCreateOptsData; -static BlockBackend *vmdk_co_create_opts_cb(int64_t size, int idx, - bool flat, bool split, bool compress, - bool zeroed_grain, void *opaque, - Error **errp) +static BlockBackend * coroutine_fn GRAPH_UNLOCKED +vmdk_co_create_opts_cb(int64_t size, int idx, bool flat, bool split, + bool compress, bool zeroed_grain, void *opaque, + Error **errp) { BlockBackend *blk = NULL; BlockDriverState *bs = NULL; @@ -2659,16 +2691,15 @@ static BlockBackend *vmdk_co_create_opts_cb(int64_t size, int idx, errp)) { goto exit; } - bdrv_unref(bs); + bdrv_co_unref(bs); exit: g_free(ext_filename); return blk; } -static int coroutine_fn vmdk_co_create_opts(BlockDriver *drv, - const char *filename, - QemuOpts *opts, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +vmdk_co_create_opts(BlockDriver *drv, const char *filename, + QemuOpts *opts, Error **errp) { Error *local_err = NULL; char *desc = NULL; @@ -2775,10 +2806,9 @@ exit: return ret; } -static BlockBackend *vmdk_co_create_cb(int64_t size, int idx, - bool flat, bool split, bool compress, - bool zeroed_grain, void *opaque, - Error **errp) +static BlockBackend * coroutine_fn GRAPH_UNLOCKED +vmdk_co_create_cb(int64_t size, int idx, bool flat, bool split, bool compress, + bool zeroed_grain, void *opaque, Error **errp) { int ret; BlockDriverState *bs; @@ -2786,7 +2816,7 @@ static BlockBackend *vmdk_co_create_cb(int64_t size, int idx, BlockdevCreateOptionsVmdk *opts = opaque; if (idx == 0) { - bs = bdrv_open_blockdev_ref(opts->file, errp); + bs = bdrv_co_open_blockdev_ref(opts->file, errp); } else { int i; BlockdevRefList *list = opts->extents; @@ -2801,34 +2831,35 @@ static BlockBackend *vmdk_co_create_cb(int64_t size, int idx, error_setg(errp, "Extent [%d] not specified", idx - 1); return NULL; } - bs = bdrv_open_blockdev_ref(list->value, errp); + bs = bdrv_co_open_blockdev_ref(list->value, errp); } if (!bs) { return NULL; } - blk = blk_new_with_bs(bs, - BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE, - BLK_PERM_ALL, errp); + blk = blk_co_new_with_bs(bs, + BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | + BLK_PERM_RESIZE, + BLK_PERM_ALL, + errp); if (!blk) { return NULL; } blk_set_allow_write_beyond_eof(blk, true); - bdrv_unref(bs); + bdrv_co_unref(bs); if (size != -1) { ret = vmdk_init_extent(blk, size, flat, compress, zeroed_grain, errp); if (ret) { - blk_unref(blk); + blk_co_unref(blk); blk = NULL; } } return blk; } -static int coroutine_fn vmdk_co_create(BlockdevCreateOptions *create_options, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +vmdk_co_create(BlockdevCreateOptions *create_options, Error **errp) { - int ret; BlockdevCreateOptionsVmdk *opts; opts = &create_options->u.vmdk; @@ -2836,24 +2867,19 @@ static int coroutine_fn vmdk_co_create(BlockdevCreateOptions *create_options, /* Validate options */ if (!QEMU_IS_ALIGNED(opts->size, BDRV_SECTOR_SIZE)) { error_setg(errp, "Image size must be a multiple of 512 bytes"); - ret = -EINVAL; - goto out; + return -EINVAL; } - ret = vmdk_co_do_create(opts->size, - opts->subformat, - opts->adapter_type, - opts->backing_file, - opts->hwversion, - opts->toolsversion, - false, - opts->zeroed_grain, - vmdk_co_create_cb, - opts, errp); - return ret; - -out: - return ret; + return vmdk_co_do_create(opts->size, + opts->subformat, + opts->adapter_type, + opts->backing_file, + opts->hwversion, + opts->toolsversion, + false, + opts->zeroed_grain, + vmdk_co_create_cb, + opts, errp); } static void vmdk_close(BlockDriverState *bs) @@ -2863,18 +2889,18 @@ static void vmdk_close(BlockDriverState *bs) vmdk_free_extents(bs); g_free(s->create_type); - migrate_del_blocker(s->migration_blocker); - error_free(s->migration_blocker); + migrate_del_blocker(&s->migration_blocker); } -static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs) +static int64_t coroutine_fn GRAPH_RDLOCK +vmdk_co_get_allocated_file_size(BlockDriverState *bs) { int i; int64_t ret = 0; int64_t r; BDRVVmdkState *s = bs->opaque; - ret = bdrv_get_allocated_file_size(bs->file->bs); + ret = bdrv_co_get_allocated_file_size(bs->file->bs); if (ret < 0) { return ret; } @@ -2882,7 +2908,7 @@ static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs) if (s->extents[i].file == bs->file) { continue; } - r = bdrv_get_allocated_file_size(s->extents[i].file->bs); + r = bdrv_co_get_allocated_file_size(s->extents[i].file->bs); if (r < 0) { return r; } @@ -2891,7 +2917,7 @@ static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs) return ret; } -static int vmdk_has_zero_init(BlockDriverState *bs) +static int GRAPH_RDLOCK vmdk_has_zero_init(BlockDriverState *bs) { int i; BDRVVmdkState *s = bs->opaque; @@ -2908,12 +2934,12 @@ static int vmdk_has_zero_init(BlockDriverState *bs) return 1; } -static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent) +static VmdkExtentInfo * GRAPH_RDLOCK vmdk_get_extent_info(VmdkExtent *extent) { - ImageInfo *info = g_new0(ImageInfo, 1); + VmdkExtentInfo *info = g_new0(VmdkExtentInfo, 1); bdrv_refresh_filename(extent->file->bs); - *info = (ImageInfo){ + *info = (VmdkExtentInfo){ .filename = g_strdup(extent->file->bs->filename), .format = g_strdup(extent->type), .virtual_size = extent->sectors * BDRV_SECTOR_SIZE, @@ -2926,14 +2952,13 @@ static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent) return info; } -static int coroutine_fn vmdk_co_check(BlockDriverState *bs, - BdrvCheckResult *result, - BdrvCheckMode fix) +static int coroutine_fn GRAPH_RDLOCK +vmdk_co_check(BlockDriverState *bs, BdrvCheckResult *result, BdrvCheckMode fix) { BDRVVmdkState *s = bs->opaque; VmdkExtent *extent = NULL; int64_t sector_num = 0; - int64_t total_sectors = bdrv_nb_sectors(bs); + int64_t total_sectors = bdrv_co_nb_sectors(bs); int ret; uint64_t cluster_offset; @@ -2963,7 +2988,7 @@ static int coroutine_fn vmdk_co_check(BlockDriverState *bs, break; } if (ret == VMDK_OK) { - int64_t extent_len = bdrv_getlength(extent->file->bs); + int64_t extent_len = bdrv_co_getlength(extent->file->bs); if (extent_len < 0) { fprintf(stderr, "ERROR: could not get extent file length for sector %" @@ -2986,13 +3011,13 @@ static int coroutine_fn vmdk_co_check(BlockDriverState *bs, return ret; } -static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs, - Error **errp) +static ImageInfoSpecific * GRAPH_RDLOCK +vmdk_get_specific_info(BlockDriverState *bs, Error **errp) { int i; BDRVVmdkState *s = bs->opaque; ImageInfoSpecific *spec_info = g_new0(ImageInfoSpecific, 1); - ImageInfoList **tail; + VmdkExtentInfoList **tail; *spec_info = (ImageInfoSpecific){ .type = IMAGE_INFO_SPECIFIC_KIND_VMDK, @@ -3022,7 +3047,8 @@ static bool vmdk_extents_type_eq(const VmdkExtent *a, const VmdkExtent *b) (a->flat || a->cluster_sectors == b->cluster_sectors); } -static int vmdk_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +vmdk_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { int i; BDRVVmdkState *s = bs->opaque; @@ -3041,8 +3067,9 @@ static int vmdk_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return 0; } -static void vmdk_gather_child_options(BlockDriverState *bs, QDict *target, - bool backing_overridden) +static void GRAPH_RDLOCK +vmdk_gather_child_options(BlockDriverState *bs, QDict *target, + bool backing_overridden) { /* No children but file and backing can be explicitly specified (TODO) */ qdict_put(target, "file", @@ -3135,11 +3162,11 @@ static BlockDriver bdrv_vmdk = { .bdrv_co_create_opts = vmdk_co_create_opts, .bdrv_co_create = vmdk_co_create, .bdrv_co_block_status = vmdk_co_block_status, - .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size, + .bdrv_co_get_allocated_file_size = vmdk_co_get_allocated_file_size, .bdrv_has_zero_init = vmdk_has_zero_init, .bdrv_get_specific_info = vmdk_get_specific_info, .bdrv_refresh_limits = vmdk_refresh_limits, - .bdrv_get_info = vmdk_get_info, + .bdrv_co_get_info = vmdk_co_get_info, .bdrv_gather_child_options = vmdk_gather_child_options, .is_format = true, diff --git a/block/vpc.c b/block/vpc.c index 4d8f16e199..d95a204612 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -233,12 +233,13 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, int ret; int64_t bs_size; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, - BDRV_CHILD_IMAGE, false, errp); - if (!bs->file) { - return -EINVAL; + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); + if (ret < 0) { + return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + opts = qemu_opts_create(&vpc_runtime_opts, NULL, 0, &error_abort); if (!qemu_opts_absorb_qdict(opts, options, errp)) { ret = -EINVAL; @@ -252,7 +253,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file, 0, &s->footer, sizeof(s->footer)); + ret = bdrv_pread(bs->file, 0, sizeof(s->footer), &s->footer, 0); if (ret < 0) { error_setg(errp, "Unable to read VHD header"); goto fail; @@ -272,8 +273,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, } /* If a fixed disk, the footer is found only at the end of the file */ - ret = bdrv_pread(bs->file, offset - sizeof(*footer), - footer, sizeof(*footer)); + ret = bdrv_pread(bs->file, offset - sizeof(*footer), sizeof(*footer), + footer, 0); if (ret < 0) { goto fail; } @@ -347,7 +348,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, if (disk_type == VHD_DYNAMIC) { ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), - &dyndisk_header, sizeof(dyndisk_header)); + sizeof(dyndisk_header), &dyndisk_header, 0); if (ret < 0) { error_setg(errp, "Error reading dynamic VHD header"); goto fail; @@ -401,8 +402,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, s->bat_offset = be64_to_cpu(dyndisk_header.table_offset); - ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, - pagetable_size); + ret = bdrv_pread(bs->file, s->bat_offset, pagetable_size, + s->pagetable, 0); if (ret < 0) { error_setg(errp, "Error reading pagetable"); goto fail; @@ -450,9 +451,9 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, error_setg(&s->migration_blocker, "The vpc format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - ret = migrate_add_blocker(s->migration_blocker, errp); + + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { - error_free(s->migration_blocker); goto fail; } @@ -487,8 +488,8 @@ static int vpc_reopen_prepare(BDRVReopenState *state, * operation (the block bitmaps is updated then), 0 otherwise. * If write is true then err must not be NULL. */ -static inline int64_t get_image_offset(BlockDriverState *bs, uint64_t offset, - bool write, int *err) +static int64_t coroutine_fn GRAPH_RDLOCK +get_image_offset(BlockDriverState *bs, uint64_t offset, bool write, int *err) { BDRVVPCState *s = bs->opaque; uint64_t bitmap_offset, block_offset; @@ -511,12 +512,12 @@ static inline int64_t get_image_offset(BlockDriverState *bs, uint64_t offset, miss sparse read optimization, but it's not a problem in terms of correctness. */ if (write && (s->last_bitmap_offset != bitmap_offset)) { - uint8_t bitmap[s->bitmap_size]; + g_autofree uint8_t *bitmap = g_malloc(s->bitmap_size); int r; s->last_bitmap_offset = bitmap_offset; memset(bitmap, 0xff, s->bitmap_size); - r = bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size); + r = bdrv_co_pwrite_sync(bs->file, bitmap_offset, s->bitmap_size, bitmap, 0); if (r < 0) { *err = r; return -2; @@ -532,13 +533,13 @@ static inline int64_t get_image_offset(BlockDriverState *bs, uint64_t offset, * * Returns 0 on success and < 0 on error */ -static int rewrite_footer(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK rewrite_footer(BlockDriverState *bs) { int ret; BDRVVPCState *s = bs->opaque; int64_t offset = s->free_data_block_offset; - ret = bdrv_pwrite_sync(bs->file, offset, &s->footer, sizeof(s->footer)); + ret = bdrv_co_pwrite_sync(bs->file, offset, sizeof(s->footer), &s->footer, 0); if (ret < 0) return ret; @@ -552,13 +553,14 @@ static int rewrite_footer(BlockDriverState *bs) * * Returns the sectors' offset in the image file on success and < 0 on error */ -static int64_t alloc_block(BlockDriverState *bs, int64_t offset) +static int64_t coroutine_fn GRAPH_RDLOCK +alloc_block(BlockDriverState *bs, int64_t offset) { BDRVVPCState *s = bs->opaque; int64_t bat_offset; uint32_t index, bat_value; int ret; - uint8_t bitmap[s->bitmap_size]; + g_autofree uint8_t *bitmap = g_malloc(s->bitmap_size); /* Check if sector_num is valid */ if ((offset < 0) || (offset > bs->total_sectors * BDRV_SECTOR_SIZE)) { @@ -572,8 +574,8 @@ static int64_t alloc_block(BlockDriverState *bs, int64_t offset) /* Initialize the block's bitmap */ memset(bitmap, 0xff, s->bitmap_size); - ret = bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, - s->bitmap_size); + ret = bdrv_co_pwrite_sync(bs->file, s->free_data_block_offset, + s->bitmap_size, bitmap, 0); if (ret < 0) { return ret; } @@ -587,7 +589,7 @@ static int64_t alloc_block(BlockDriverState *bs, int64_t offset) /* Write BAT entry to disk */ bat_offset = s->bat_offset + (4 * index); bat_value = cpu_to_be32(s->pagetable[index]); - ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4); + ret = bdrv_co_pwrite_sync(bs->file, bat_offset, 4, &bat_value, 0); if (ret < 0) goto fail; @@ -598,7 +600,8 @@ fail: return ret; } -static int vpc_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +vpc_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVVPCState *s = (BDRVVPCState *)bs->opaque; @@ -609,7 +612,7 @@ static int vpc_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return 0; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK vpc_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -659,7 +662,7 @@ fail: return ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK vpc_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -717,11 +720,11 @@ fail: return ret; } -static int coroutine_fn vpc_co_block_status(BlockDriverState *bs, - bool want_zero, - int64_t offset, int64_t bytes, - int64_t *pnum, int64_t *map, - BlockDriverState **file) +static int coroutine_fn GRAPH_RDLOCK +vpc_co_block_status(BlockDriverState *bs, bool want_zero, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, + BlockDriverState **file) { BDRVVPCState *s = bs->opaque; int64_t image_offset; @@ -819,8 +822,8 @@ static int calculate_geometry(int64_t total_sectors, uint16_t *cyls, return 0; } -static int create_dynamic_disk(BlockBackend *blk, VHDFooter *footer, - int64_t total_sectors) +static int coroutine_fn create_dynamic_disk(BlockBackend *blk, VHDFooter *footer, + int64_t total_sectors) { VHDDynDiskHeader dyndisk_header; uint8_t bat_sector[512]; @@ -833,13 +836,13 @@ static int create_dynamic_disk(BlockBackend *blk, VHDFooter *footer, block_size = 0x200000; num_bat_entries = DIV_ROUND_UP(total_sectors, block_size / 512); - ret = blk_pwrite(blk, offset, footer, sizeof(*footer), 0); + ret = blk_co_pwrite(blk, offset, sizeof(*footer), footer, 0); if (ret < 0) { goto fail; } offset = 1536 + ((num_bat_entries * 4 + 511) & ~511); - ret = blk_pwrite(blk, offset, footer, sizeof(*footer), 0); + ret = blk_co_pwrite(blk, offset, sizeof(*footer), footer, 0); if (ret < 0) { goto fail; } @@ -849,7 +852,7 @@ static int create_dynamic_disk(BlockBackend *blk, VHDFooter *footer, memset(bat_sector, 0xFF, 512); for (i = 0; i < DIV_ROUND_UP(num_bat_entries * 4, 512); i++) { - ret = blk_pwrite(blk, offset, bat_sector, 512, 0); + ret = blk_co_pwrite(blk, offset, 512, bat_sector, 0); if (ret < 0) { goto fail; } @@ -877,7 +880,7 @@ static int create_dynamic_disk(BlockBackend *blk, VHDFooter *footer, /* Write the header */ offset = 512; - ret = blk_pwrite(blk, offset, &dyndisk_header, sizeof(dyndisk_header), 0); + ret = blk_co_pwrite(blk, offset, sizeof(dyndisk_header), &dyndisk_header, 0); if (ret < 0) { goto fail; } @@ -887,21 +890,21 @@ static int create_dynamic_disk(BlockBackend *blk, VHDFooter *footer, return ret; } -static int create_fixed_disk(BlockBackend *blk, VHDFooter *footer, - int64_t total_size, Error **errp) +static int coroutine_fn create_fixed_disk(BlockBackend *blk, VHDFooter *footer, + int64_t total_size, Error **errp) { int ret; /* Add footer to total size */ total_size += sizeof(*footer); - ret = blk_truncate(blk, total_size, false, PREALLOC_MODE_OFF, 0, errp); + ret = blk_co_truncate(blk, total_size, false, PREALLOC_MODE_OFF, 0, errp); if (ret < 0) { return ret; } - ret = blk_pwrite(blk, total_size - sizeof(*footer), - footer, sizeof(*footer), 0); + ret = blk_co_pwrite(blk, total_size - sizeof(*footer), sizeof(*footer), + footer, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Unable to write VHD header"); return ret; @@ -966,8 +969,8 @@ static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts, return 0; } -static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +vpc_co_create(BlockdevCreateOptions *opts, Error **errp) { BlockdevCreateOptionsVpc *vpc_opts; BlockBackend *blk = NULL; @@ -1004,13 +1007,13 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts, } /* Create BlockBackend to write to the image */ - bs = bdrv_open_blockdev_ref(vpc_opts->file, errp); + bs = bdrv_co_open_blockdev_ref(vpc_opts->file, errp); if (bs == NULL) { return -EIO; } - blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, - errp); + blk = blk_co_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, + errp); if (!blk) { ret = -EPERM; goto out; @@ -1081,15 +1084,14 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts, } out: - blk_unref(blk); - bdrv_unref(bs); + blk_co_unref(blk); + bdrv_co_unref(bs); return ret; } -static int coroutine_fn vpc_co_create_opts(BlockDriver *drv, - const char *filename, - QemuOpts *opts, - Error **errp) +static int coroutine_fn GRAPH_UNLOCKED +vpc_co_create_opts(BlockDriver *drv, const char *filename, + QemuOpts *opts, Error **errp) { BlockdevCreateOptions *create_options = NULL; QDict *qdict; @@ -1111,13 +1113,13 @@ static int coroutine_fn vpc_co_create_opts(BlockDriver *drv, } /* Create and open the file (protocol layer) */ - ret = bdrv_create_file(filename, opts, errp); + ret = bdrv_co_create_file(filename, opts, errp); if (ret < 0) { goto fail; } - bs = bdrv_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); + bs = bdrv_co_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); if (bs == NULL) { ret = -EIO; goto fail; @@ -1162,13 +1164,13 @@ static int coroutine_fn vpc_co_create_opts(BlockDriver *drv, fail: qobject_unref(qdict); - bdrv_unref(bs); + bdrv_co_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; } -static int vpc_has_zero_init(BlockDriverState *bs) +static int GRAPH_RDLOCK vpc_has_zero_init(BlockDriverState *bs) { BDRVVPCState *s = bs->opaque; @@ -1187,8 +1189,7 @@ static void vpc_close(BlockDriverState *bs) g_free(s->pageentry_u8); #endif - migrate_del_blocker(s->migration_blocker); - error_free(s->migration_blocker); + migrate_del_blocker(&s->migration_blocker); } static QemuOptsList vpc_create_opts = { @@ -1240,7 +1241,7 @@ static BlockDriver bdrv_vpc = { .bdrv_co_pwritev = vpc_co_pwritev, .bdrv_co_block_status = vpc_co_block_status, - .bdrv_get_info = vpc_get_info, + .bdrv_co_get_info = vpc_co_get_info, .is_format = true, .create_opts = &vpc_create_opts, diff --git a/block/vvfat.c b/block/vvfat.c index b2b58d93b8..9d050ba3ae 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -25,7 +25,9 @@ #include "qemu/osdep.h" #include +#include #include "qapi/error.h" +#include "block/block-io.h" #include "block/block_int.h" #include "block/qdict.h" #include "qemu/module.h" @@ -499,7 +501,7 @@ static bool valid_filename(const unsigned char *name) (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c > 127 || - strchr("$%'-_@~`!(){}^#&.+,;=[]", c) != NULL)) + strchr(" $%'-_@~`!(){}^#&.+,;=[]", c) != NULL)) { return false; } @@ -775,7 +777,6 @@ static int read_directory(BDRVVVFATState* s, int mapping_index) while((entry=readdir(dir))) { unsigned int length=strlen(dirname)+2+strlen(entry->d_name); char* buffer; - direntry_t* direntry; struct stat st; int is_dot=!strcmp(entry->d_name,"."); int is_dotdot=!strcmp(entry->d_name,".."); @@ -855,7 +856,7 @@ static int read_directory(BDRVVVFATState* s, int mapping_index) /* fill with zeroes up to the end of the cluster */ while(s->directory.next%(0x10*s->sectors_per_cluster)) { - direntry_t* direntry=array_get_next(&(s->directory)); + direntry = array_get_next(&(s->directory)); memset(direntry,0,sizeof(direntry_t)); } @@ -1051,7 +1052,7 @@ static BDRVVVFATState *vvv = NULL; #endif static int enable_write_target(BlockDriverState *bs, Error **errp); -static int is_consistent(BDRVVVFATState *s); +static int coroutine_fn is_consistent(BDRVVVFATState *s); static QemuOptsList runtime_opts = { .name = "vvfat", @@ -1143,6 +1144,8 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, QemuOpts *opts; int ret; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + #ifdef DEBUG vvv = s; #endif @@ -1265,9 +1268,8 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, "The vvfat (rw) format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - ret = migrate_add_blocker(s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { - error_free(s->migration_blocker); goto fail; } } @@ -1467,8 +1469,8 @@ static void print_mapping(const mapping_t* mapping) } #endif -static int vvfat_read(BlockDriverState *bs, int64_t sector_num, - uint8_t *buf, int nb_sectors) +static int coroutine_fn GRAPH_RDLOCK +vvfat_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) { BDRVVVFATState *s = bs->opaque; int i; @@ -1479,8 +1481,8 @@ static int vvfat_read(BlockDriverState *bs, int64_t sector_num, if (s->qcow) { int64_t n; int ret; - ret = bdrv_is_allocated(s->qcow->bs, sector_num * BDRV_SECTOR_SIZE, - (nb_sectors - i) * BDRV_SECTOR_SIZE, &n); + ret = bdrv_co_is_allocated(s->qcow->bs, sector_num * BDRV_SECTOR_SIZE, + (nb_sectors - i) * BDRV_SECTOR_SIZE, &n); if (ret < 0) { return ret; } @@ -1488,8 +1490,8 @@ static int vvfat_read(BlockDriverState *bs, int64_t sector_num, DLOG(fprintf(stderr, "sectors %" PRId64 "+%" PRId64 " allocated\n", sector_num, n >> BDRV_SECTOR_BITS)); - if (bdrv_pread(s->qcow, sector_num * BDRV_SECTOR_SIZE, - buf + i * 0x200, n) < 0) { + if (bdrv_co_pread(s->qcow, sector_num * BDRV_SECTOR_SIZE, n, + buf + i * 0x200, 0) < 0) { return -1; } i += (n >> BDRV_SECTOR_BITS) - 1; @@ -1530,7 +1532,7 @@ static int vvfat_read(BlockDriverState *bs, int64_t sector_num, return 0; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK vvfat_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -1794,8 +1796,8 @@ static inline uint32_t modified_fat_get(BDRVVVFATState* s, } } -static inline bool cluster_was_modified(BDRVVVFATState *s, - uint32_t cluster_num) +static inline bool coroutine_fn GRAPH_RDLOCK +cluster_was_modified(BDRVVVFATState *s, uint32_t cluster_num) { int was_modified = 0; int i; @@ -1805,10 +1807,10 @@ static inline bool cluster_was_modified(BDRVVVFATState *s, } for (i = 0; !was_modified && i < s->sectors_per_cluster; i++) { - was_modified = bdrv_is_allocated(s->qcow->bs, - (cluster2sector(s, cluster_num) + - i) * BDRV_SECTOR_SIZE, - BDRV_SECTOR_SIZE, NULL); + was_modified = bdrv_co_is_allocated(s->qcow->bs, + (cluster2sector(s, cluster_num) + + i) * BDRV_SECTOR_SIZE, + BDRV_SECTOR_SIZE, NULL); } /* @@ -1850,8 +1852,8 @@ typedef enum { * Further, the files/directories handled by this function are * assumed to be *not* deleted (and *only* those). */ -static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, - direntry_t* direntry, const char* path) +static uint32_t coroutine_fn GRAPH_RDLOCK +get_cluster_count_for_direntry(BDRVVVFATState* s, direntry_t* direntry, const char* path) { /* * This is a little bit tricky: @@ -1960,25 +1962,26 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, * This is horribly inefficient, but that is okay, since * it is rarely executed, if at all. */ - int64_t offset = cluster2sector(s, cluster_num); + int64_t offs = cluster2sector(s, cluster_num); vvfat_close_current_file(s); for (i = 0; i < s->sectors_per_cluster; i++) { int res; - res = bdrv_is_allocated(s->qcow->bs, - (offset + i) * BDRV_SECTOR_SIZE, - BDRV_SECTOR_SIZE, NULL); + res = bdrv_co_is_allocated(s->qcow->bs, + (offs + i) * BDRV_SECTOR_SIZE, + BDRV_SECTOR_SIZE, NULL); if (res < 0) { return -1; } if (!res) { - res = vvfat_read(s->bs, offset, s->cluster_buffer, 1); + res = vvfat_read(s->bs, offs, s->cluster_buffer, 1); if (res) { return -1; } - res = bdrv_pwrite(s->qcow, offset * BDRV_SECTOR_SIZE, - s->cluster_buffer, BDRV_SECTOR_SIZE); + res = bdrv_co_pwrite(s->qcow, offs * BDRV_SECTOR_SIZE, + BDRV_SECTOR_SIZE, s->cluster_buffer, + 0); if (res < 0) { return -2; } @@ -2008,8 +2011,8 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, * It returns 0 upon inconsistency or error, and the number of clusters * used by the directory, its subdirectories and their files. */ -static int check_directory_consistency(BDRVVVFATState *s, - int cluster_num, const char* path) +static int coroutine_fn GRAPH_RDLOCK +check_directory_consistency(BDRVVVFATState *s, int cluster_num, const char* path) { int ret = 0; unsigned char* cluster = g_malloc(s->cluster_size); @@ -2135,7 +2138,8 @@ DLOG(fprintf(stderr, "check direntry %d:\n", i); print_direntry(direntries + i)) } /* returns 1 on success */ -static int is_consistent(BDRVVVFATState* s) +static int coroutine_fn GRAPH_RDLOCK +is_consistent(BDRVVVFATState* s) { int i, check; int used_clusters_count = 0; @@ -2411,8 +2415,8 @@ static int commit_mappings(BDRVVVFATState* s, return 0; } -static int commit_direntries(BDRVVVFATState* s, - int dir_index, int parent_mapping_index) +static int coroutine_fn GRAPH_RDLOCK +commit_direntries(BDRVVVFATState* s, int dir_index, int parent_mapping_index) { direntry_t* direntry = array_get(&(s->directory), dir_index); uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry); @@ -2463,8 +2467,9 @@ static int commit_direntries(BDRVVVFATState* s, for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) { direntry_t *first_direntry; - void* direntry = array_get(&(s->directory), current_dir_index); - int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry, + + direntry = array_get(&(s->directory), current_dir_index); + ret = vvfat_read(s->bs, cluster2sector(s, c), (uint8_t *)direntry, s->sectors_per_cluster); if (ret) return ret; @@ -2501,8 +2506,8 @@ static int commit_direntries(BDRVVVFATState* s, /* commit one file (adjust contents, adjust mapping), return first_mapping_index */ -static int commit_one_file(BDRVVVFATState* s, - int dir_index, uint32_t offset) +static int coroutine_fn GRAPH_RDLOCK +commit_one_file(BDRVVVFATState* s, int dir_index, uint32_t offset) { direntry_t* direntry = array_get(&(s->directory), dir_index); uint32_t c = begin_of_direntry(direntry); @@ -2686,12 +2691,12 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s) direntry_t* direntry = array_get(&(s->directory), mapping->info.dir.first_dir_index); uint32_t c = mapping->begin; - int i = 0; + int j = 0; /* recurse */ while (!fat_eof(s, c)) { do { - direntry_t* d = direntry + i; + direntry_t *d = direntry + j; if (is_file(d) || (is_directory(d) && !is_dot(d))) { int l; @@ -2712,8 +2717,8 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s) schedule_rename(s, m->begin, new_path); } - i++; - } while((i % (0x10 * s->sectors_per_cluster)) != 0); + j++; + } while (j % (0x10 * s->sectors_per_cluster) != 0); c = fat_get(s, c); } } @@ -2725,13 +2730,9 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s) mapping_t* mapping; int j, parent_path_len; -#ifdef __MINGW32__ - if (mkdir(commit->path)) + if (g_mkdir(commit->path, 0755)) { return -5; -#else - if (mkdir(commit->path, 0755)) - return -5; -#endif + } mapping = insert_mapping(s, commit->param.mkdir.cluster, commit->param.mkdir.cluster + 1); @@ -2771,7 +2772,7 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s) /* * TODO: make sure that the short name is not matching *another* file */ -static int handle_commits(BDRVVVFATState* s) +static int coroutine_fn GRAPH_RDLOCK handle_commits(BDRVVVFATState* s) { int i, fail = 0; @@ -2785,13 +2786,10 @@ static int handle_commits(BDRVVVFATState* s) fail = -2; break; case ACTION_WRITEOUT: { -#ifndef NDEBUG - /* these variables are only used by assert() below */ direntry_t* entry = array_get(&(s->directory), commit->param.writeout.dir_index); uint32_t begin = begin_of_direntry(entry); mapping_t* mapping = find_mapping_for_cluster(s, begin); -#endif assert(mapping); assert(mapping->begin == begin); @@ -2807,16 +2805,16 @@ static int handle_commits(BDRVVVFATState* s) int begin = commit->param.new_file.first_cluster; mapping_t* mapping = find_mapping_for_cluster(s, begin); direntry_t* entry; - int i; + int j; /* find direntry */ - for (i = 0; i < s->directory.next; i++) { - entry = array_get(&(s->directory), i); + for (j = 0; j < s->directory.next; j++) { + entry = array_get(&(s->directory), j); if (is_file(entry) && begin_of_direntry(entry) == begin) break; } - if (i >= s->directory.next) { + if (j >= s->directory.next) { fail = -6; continue; } @@ -2836,8 +2834,9 @@ static int handle_commits(BDRVVVFATState* s) mapping->mode = MODE_NORMAL; mapping->info.file.offset = 0; - if (commit_one_file(s, i, 0)) + if (commit_one_file(s, j, 0)) { fail = -7; + } break; } @@ -2917,7 +2916,7 @@ static int handle_deletes(BDRVVVFATState* s) * - recurse direntries from root (using bs->bdrv_pread) * - delete files corresponding to mappings marked as deleted */ -static int do_commit(BDRVVVFATState* s) +static int coroutine_fn GRAPH_RDLOCK do_commit(BDRVVVFATState* s) { int ret = 0; @@ -2967,7 +2966,7 @@ DLOG(checkpoint()); return 0; } -static int try_commit(BDRVVVFATState* s) +static int coroutine_fn GRAPH_RDLOCK try_commit(BDRVVVFATState* s) { vvfat_close_current_file(s); DLOG(checkpoint()); @@ -2976,8 +2975,9 @@ DLOG(checkpoint()); return do_commit(s); } -static int vvfat_write(BlockDriverState *bs, int64_t sector_num, - const uint8_t *buf, int nb_sectors) +static int coroutine_fn GRAPH_RDLOCK +vvfat_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) { BDRVVVFATState *s = bs->opaque; int i, ret; @@ -2992,11 +2992,35 @@ DLOG(checkpoint()); vvfat_close_current_file(s); + if (sector_num == s->offset_to_bootsector && nb_sectors == 1) { + /* + * Write on bootsector. Allow only changing the reserved1 field, + * used to mark volume dirtiness + */ + unsigned char *bootsector = s->first_sectors + + s->offset_to_bootsector * 0x200; + /* + * LATER TODO: if FAT32, this is wrong (see init_directories(), + * which always creates a FAT16 bootsector) + */ + const int reserved1_offset = offsetof(bootsector_t, u.fat16.reserved1); + + for (i = 0; i < 0x200; i++) { + if (i != reserved1_offset && bootsector[i] != buf[i]) { + fprintf(stderr, "Tried to write to protected bootsector\n"); + return -1; + } + } + + /* Update bootsector with the only updatable byte, and return success */ + bootsector[reserved1_offset] = buf[reserved1_offset]; + return 0; + } + /* * Some sanity checks: * - do not allow writing to the boot sector */ - if (sector_num < s->offset_to_fat) return -1; @@ -3062,8 +3086,8 @@ DLOG(checkpoint()); * Use qcow backend. Commit later. */ DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors)); - ret = bdrv_pwrite(s->qcow, sector_num * BDRV_SECTOR_SIZE, buf, - nb_sectors * BDRV_SECTOR_SIZE); + ret = bdrv_co_pwrite(s->qcow, sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE, buf, 0); if (ret < 0) { fprintf(stderr, "Error writing to qcow backend\n"); return ret; @@ -3083,7 +3107,7 @@ DLOG(checkpoint()); return 0; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK vvfat_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -3145,10 +3169,9 @@ static int enable_write_target(BlockDriverState *bs, Error **errp) array_init(&(s->commits), sizeof(commit_t)); - s->qcow_filename = g_malloc(PATH_MAX); - ret = get_tmp_filename(s->qcow_filename, PATH_MAX); - if (ret < 0) { - error_setg_errno(errp, -ret, "can't create temporary file"); + s->qcow_filename = create_tmp_file(errp); + if (!s->qcow_filename) { + ret = -ENOENT; goto err; } @@ -3215,8 +3238,7 @@ static void vvfat_close(BlockDriverState *bs) g_free(s->cluster_buffer); if (s->qcow) { - migrate_del_blocker(s->migration_blocker); - error_free(s->migration_blocker); + migrate_del_blocker(&s->migration_blocker); } } diff --git a/block/win32-aio.c b/block/win32-aio.c index aadc7b1bc3..6327861e1d 100644 --- a/block/win32-aio.c +++ b/block/win32-aio.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qemu/timer.h" +#include "block/block-io.h" #include "block/block_int.h" #include "block/aio.h" #include "block/raw-aio.h" @@ -173,7 +174,7 @@ int win32_aio_attach(QEMUWin32AIOState *aio, HANDLE hfile) void win32_aio_detach_aio_context(QEMUWin32AIOState *aio, AioContext *old_context) { - aio_set_event_notifier(old_context, &aio->e, false, NULL, NULL, NULL); + aio_set_event_notifier(old_context, &aio->e, NULL, NULL, NULL); aio->aio_ctx = NULL; } @@ -181,8 +182,8 @@ void win32_aio_attach_aio_context(QEMUWin32AIOState *aio, AioContext *new_context) { aio->aio_ctx = new_context; - aio_set_event_notifier(new_context, &aio->e, false, - win32_aio_completion_cb, NULL, NULL); + aio_set_event_notifier(new_context, &aio->e, win32_aio_completion_cb, + NULL, NULL); } QEMUWin32AIOState *win32_aio_init(void) diff --git a/block/write-threshold.c b/block/write-threshold.c index 35cafbc22d..56fe88de81 100644 --- a/block/write-threshold.c +++ b/block/write-threshold.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "block/block-io.h" #include "block/block_int.h" #include "block/write-threshold.h" #include "qapi/error.h" @@ -32,7 +33,6 @@ void qmp_block_set_write_threshold(const char *node_name, Error **errp) { BlockDriverState *bs; - AioContext *aio_context; bs = bdrv_find_node(node_name); if (!bs) { @@ -40,12 +40,7 @@ void qmp_block_set_write_threshold(const char *node_name, return; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - bdrv_write_threshold_set(bs, threshold_bytes); - - aio_context_release(aio_context); } void bdrv_write_threshold_check_write(BlockDriverState *bs, int64_t offset, diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 012256bb02..213012435f 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -173,8 +173,8 @@ void nbd_server_start_options(NbdServerOptions *arg, Error **errp) } void qmp_nbd_server_start(SocketAddressLegacy *addr, - bool has_tls_creds, const char *tls_creds, - bool has_tls_authz, const char *tls_authz, + const char *tls_creds, + const char *tls_authz, bool has_max_connections, uint32_t max_connections, Error **errp) { @@ -200,8 +200,7 @@ void qmp_nbd_server_add(NbdServerAddOptions *arg, Error **errp) * block-export-add would default to the node-name, but we may have to use * the device name as a default here for compatibility. */ - if (!arg->has_name) { - arg->has_name = true; + if (!arg->name) { arg->name = g_strdup(arg->device); } @@ -215,7 +214,7 @@ void qmp_nbd_server_add(NbdServerAddOptions *arg, Error **errp) }; QAPI_CLONE_MEMBERS(BlockExportOptionsNbdBase, &export_opts->u.nbd, qapi_NbdServerAddOptions_base(arg)); - if (arg->has_bitmap) { + if (arg->bitmap) { BlockDirtyBitmapOrStr *el = g_new(BlockDirtyBitmapOrStr, 1); *el = (BlockDirtyBitmapOrStr) { diff --git a/blockdev.c b/blockdev.c index 9230888e34..057601dcf0 100644 --- a/blockdev.c +++ b/blockdev.c @@ -35,6 +35,7 @@ #include "sysemu/blockdev.h" #include "hw/block/block.h" #include "block/blockjob.h" +#include "block/dirty-bitmap.h" #include "block/qdict.h" #include "block/throttle-groups.h" #include "monitor/monitor.h" @@ -150,16 +151,24 @@ void blockdev_mark_auto_del(BlockBackend *blk) return; } - for (job = block_job_next(NULL); job; job = block_job_next(job)) { - if (block_job_has_bdrv(job, blk_bs(blk))) { - AioContext *aio_context = job->job.aio_context; - aio_context_acquire(aio_context); + JOB_LOCK_GUARD(); - job_cancel(&job->job, false); - - aio_context_release(aio_context); + do { + job = block_job_next_locked(NULL); + while (job && (job->job.cancelled || + job->job.deferred_to_main_loop || + !block_job_has_bdrv(job, blk_bs(blk)))) + { + job = block_job_next_locked(job); } - } + if (job) { + /* + * This drops the job lock temporarily and polls, so we need to + * restart processing the list from the start after this. + */ + job_cancel_locked(&job->job, false); + } + } while (job); dinfo->auto_del = 1; } @@ -246,13 +255,13 @@ void drive_check_orphaned(void) * Ignore default drives, because we create certain default * drives unconditionally, then leave them unclaimed. Not the * users fault. - * Ignore IF_VIRTIO, because it gets desugared into -device, - * so we can leave failing to -device. + * Ignore IF_VIRTIO or IF_XEN, because it gets desugared into + * -device, so we can leave failing to -device. * Ignore IF_NONE, because leaving unclaimed IF_NONE remains * available for device_add is a feature. */ if (dinfo->is_default || dinfo->type == IF_VIRTIO - || dinfo->type == IF_NONE) { + || dinfo->type == IF_XEN || dinfo->type == IF_NONE) { continue; } if (!blk_get_attached_dev(blk)) { @@ -332,10 +341,10 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals, switch (qobject_type(entry->value)) { case QTYPE_QSTRING: { - unsigned long long length; + uint64_t length; const char *str = qstring_get_str(qobject_to(QString, entry->value)); - if (parse_uint_full(str, &length, 10) == 0 && + if (parse_uint_full(str, 10, &length) == 0 && length > 0 && length <= UINT_MAX) { block_acct_add_interval(stats, (unsigned) length); } else { @@ -455,6 +464,17 @@ static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags, } } +static OnOffAuto account_get_opt(QemuOpts *opts, const char *name) +{ + if (!qemu_opt_find(opts, name)) { + return ON_OFF_AUTO_AUTO; + } + if (qemu_opt_get_bool(opts, name, true)) { + return ON_OFF_AUTO_ON; + } + return ON_OFF_AUTO_OFF; +} + /* Takes the ownership of bs_opts */ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, Error **errp) @@ -462,7 +482,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, const char *buf; int bdrv_flags = 0; int on_read_error, on_write_error; - bool account_invalid, account_failed; + OnOffAuto account_invalid, account_failed; bool writethrough, read_only; BlockBackend *blk; BlockDriverState *bs; @@ -496,8 +516,8 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, /* extract parameters */ snapshot = qemu_opt_get_bool(opts, "snapshot", 0); - account_invalid = qemu_opt_get_bool(opts, "stats-account-invalid", true); - account_failed = qemu_opt_get_bool(opts, "stats-account-failed", true); + account_invalid = account_get_opt(opts, "stats-account-invalid"); + account_failed = account_get_opt(opts, "stats-account-failed"); writethrough = !qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true); @@ -665,11 +685,7 @@ void blockdev_close_all_bdrv_states(void) GLOBAL_STATE_CODE(); QTAILQ_FOREACH_SAFE(bs, &monitor_bdrv_states, monitor_list, next_bs) { - AioContext *ctx = bdrv_get_aio_context(bs); - - aio_context_acquire(ctx); bdrv_unref(bs); - aio_context_release(ctx); } } @@ -952,6 +968,15 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type, qemu_opt_set(devopts, "driver", "virtio-blk", &error_abort); qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), &error_abort); + } else if (type == IF_XEN) { + QemuOpts *devopts; + devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, + &error_abort); + qemu_opt_set(devopts, "driver", + (media == MEDIA_CDROM) ? "xen-cdrom" : "xen-disk", + &error_abort); + qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), + &error_abort); } filename = qemu_opt_get(legacy_opts, "file"); @@ -1015,6 +1040,8 @@ static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) { BlockDriverState *bs; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bs = bdrv_lookup_bs(name, name, errp); if (bs == NULL) { return NULL; @@ -1027,7 +1054,7 @@ static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) if (!bdrv_is_inserted(bs)) { error_setg(errp, "Device has no medium"); - return NULL; + bs = NULL; } return bs; @@ -1039,26 +1066,20 @@ static void blockdev_do_action(TransactionAction *action, Error **errp) list.value = action; list.next = NULL; - qmp_transaction(&list, false, NULL, errp); + qmp_transaction(&list, NULL, errp); } -void qmp_blockdev_snapshot_sync(bool has_device, const char *device, - bool has_node_name, const char *node_name, +void qmp_blockdev_snapshot_sync(const char *device, const char *node_name, const char *snapshot_file, - bool has_snapshot_node_name, const char *snapshot_node_name, - bool has_format, const char *format, + const char *format, bool has_mode, NewImageMode mode, Error **errp) { BlockdevSnapshotSync snapshot = { - .has_device = has_device, .device = (char *) device, - .has_node_name = has_node_name, .node_name = (char *) node_name, .snapshot_file = (char *) snapshot_file, - .has_snapshot_node_name = has_snapshot_node_name, .snapshot_node_name = (char *) snapshot_node_name, - .has_format = has_format, .format = (char *) format, .has_mode = has_mode, .mode = mode, @@ -1100,64 +1121,52 @@ void qmp_blockdev_snapshot_internal_sync(const char *device, } SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, - bool has_id, const char *id, - bool has_name, const char *name, Error **errp) { BlockDriverState *bs; - AioContext *aio_context; QEMUSnapshotInfo sn; Error *local_err = NULL; SnapshotInfo *info = NULL; int ret; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bs = qmp_get_root_bs(device, errp); if (!bs) { return NULL; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - - if (!has_id) { - id = NULL; - } - - if (!has_name) { - name = NULL; - } if (!id && !name) { error_setg(errp, "Name or id must be provided"); - goto out_aio_context; + return NULL; } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) { - goto out_aio_context; + return NULL; } ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err); if (local_err) { error_propagate(errp, local_err); - goto out_aio_context; + return NULL; } if (!ret) { error_setg(errp, "Snapshot with id '%s' and name '%s' does not exist on " "device '%s'", STR_OR_NULL(id), STR_OR_NULL(name), device); - goto out_aio_context; + return NULL; } bdrv_snapshot_delete(bs, id, name, &local_err); if (local_err) { error_propagate(errp, local_err); - goto out_aio_context; + return NULL; } - aio_context_release(aio_context); - info = g_new0(SnapshotInfo, 1); info->id = g_strdup(sn.id_str); info->name = g_strdup(sn.name); @@ -1172,85 +1181,24 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, } return info; - -out_aio_context: - aio_context_release(aio_context); - return NULL; } -/* New and old BlockDriverState structs for atomic group operations */ - -typedef struct BlkActionState BlkActionState; - -/** - * BlkActionOps: - * Table of operations that define an Action. - * - * @instance_size: Size of state struct, in bytes. - * @prepare: Prepare the work, must NOT be NULL. - * @commit: Commit the changes, can be NULL. - * @abort: Abort the changes on fail, can be NULL. - * @clean: Clean up resources after all transaction actions have called - * commit() or abort(). Can be NULL. - * - * Only prepare() may fail. In a single transaction, only one of commit() or - * abort() will be called. clean() will always be called if it is present. - * - * Always run under BQL. - */ -typedef struct BlkActionOps { - size_t instance_size; - void (*prepare)(BlkActionState *common, Error **errp); - void (*commit)(BlkActionState *common); - void (*abort)(BlkActionState *common); - void (*clean)(BlkActionState *common); -} BlkActionOps; - -/** - * BlkActionState: - * Describes one Action's state within a Transaction. - * - * @action: QAPI-defined enum identifying which Action to perform. - * @ops: Table of ActionOps this Action can perform. - * @block_job_txn: Transaction which this action belongs to. - * @entry: List membership for all Actions in this Transaction. - * - * This structure must be arranged as first member in a subclassed type, - * assuming that the compiler will also arrange it to the same offsets as the - * base class. - */ -struct BlkActionState { - TransactionAction *action; - const BlkActionOps *ops; - JobTxn *block_job_txn; - TransactionProperties *txn_props; - QTAILQ_ENTRY(BlkActionState) entry; -}; - /* internal snapshot private data */ typedef struct InternalSnapshotState { - BlkActionState common; BlockDriverState *bs; QEMUSnapshotInfo sn; bool created; } InternalSnapshotState; +static void internal_snapshot_abort(void *opaque); +static void internal_snapshot_clean(void *opaque); +TransactionActionDrv internal_snapshot_drv = { + .abort = internal_snapshot_abort, + .clean = internal_snapshot_clean, +}; -static int action_check_completion_mode(BlkActionState *s, Error **errp) -{ - if (s->txn_props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { - error_setg(errp, - "Action '%s' does not support Transaction property " - "completion-mode = %s", - TransactionActionKind_str(s->action->type), - ActionCompletionMode_str(s->txn_props->completion_mode)); - return -1; - } - return 0; -} - -static void internal_snapshot_prepare(BlkActionState *common, - Error **errp) +static void internal_snapshot_action(BlockdevSnapshotInternal *internal, + Transaction *tran, Error **errp) { Error *local_err = NULL; const char *device; @@ -1259,57 +1207,46 @@ static void internal_snapshot_prepare(BlkActionState *common, QEMUSnapshotInfo old_sn, *sn; bool ret; int64_t rt; - BlockdevSnapshotInternal *internal; - InternalSnapshotState *state; - AioContext *aio_context; + InternalSnapshotState *state = g_new0(InternalSnapshotState, 1); int ret1; - g_assert(common->action->type == - TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC); - internal = common->action->u.blockdev_snapshot_internal_sync.data; - state = DO_UPCAST(InternalSnapshotState, common, common); + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + tran_add(tran, &internal_snapshot_drv, state); - /* 1. parse input */ device = internal->device; name = internal->name; - /* 2. check for validation */ - if (action_check_completion_mode(common, errp) < 0) { - return; - } - bs = qmp_get_root_bs(device, errp); if (!bs) { return; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - state->bs = bs; /* Paired with .clean() */ bdrv_drained_begin(bs); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) { - goto out; + return; } if (bdrv_is_read_only(bs)) { error_setg(errp, "Device '%s' is read only", device); - goto out; + return; } if (!bdrv_can_snapshot(bs)) { error_setg(errp, "Block format '%s' used by device '%s' " "does not support internal snapshots", bs->drv->format_name, device); - goto out; + return; } if (!strlen(name)) { error_setg(errp, "Name is empty"); - goto out; + return; } /* check whether a snapshot with name exist */ @@ -1317,12 +1254,12 @@ static void internal_snapshot_prepare(BlkActionState *common, &local_err); if (local_err) { error_propagate(errp, local_err); - goto out; + return; } else if (ret) { error_setg(errp, "Snapshot with name '%s' already exists on device '%s'", name, device); - goto out; + return; } /* 3. take the snapshot */ @@ -1343,32 +1280,27 @@ static void internal_snapshot_prepare(BlkActionState *common, error_setg_errno(errp, -ret1, "Failed to create snapshot '%s' on device '%s'", name, device); - goto out; + return; } /* 4. succeed, mark a snapshot is created */ state->created = true; - -out: - aio_context_release(aio_context); } -static void internal_snapshot_abort(BlkActionState *common) +static void internal_snapshot_abort(void *opaque) { - InternalSnapshotState *state = - DO_UPCAST(InternalSnapshotState, common, common); + InternalSnapshotState *state = opaque; BlockDriverState *bs = state->bs; QEMUSnapshotInfo *sn = &state->sn; - AioContext *aio_context; Error *local_error = NULL; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!state->created) { return; } - aio_context = bdrv_get_aio_context(state->bs); - aio_context_acquire(aio_context); - if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) { error_reportf_err(local_error, "Failed to delete snapshot with id '%s' and " @@ -1376,38 +1308,37 @@ static void internal_snapshot_abort(BlkActionState *common) sn->id_str, sn->name, bdrv_get_device_name(bs)); } - - aio_context_release(aio_context); } -static void internal_snapshot_clean(BlkActionState *common) +static void internal_snapshot_clean(void *opaque) { - InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState, - common, common); - AioContext *aio_context; + g_autofree InternalSnapshotState *state = opaque; if (!state->bs) { return; } - aio_context = bdrv_get_aio_context(state->bs); - aio_context_acquire(aio_context); - bdrv_drained_end(state->bs); - - aio_context_release(aio_context); } /* external snapshot private data */ typedef struct ExternalSnapshotState { - BlkActionState common; BlockDriverState *old_bs; BlockDriverState *new_bs; bool overlay_appended; } ExternalSnapshotState; -static void external_snapshot_prepare(BlkActionState *common, - Error **errp) +static void external_snapshot_commit(void *opaque); +static void external_snapshot_abort(void *opaque); +static void external_snapshot_clean(void *opaque); +TransactionActionDrv external_snapshot_drv = { + .commit = external_snapshot_commit, + .abort = external_snapshot_abort, + .clean = external_snapshot_clean, +}; + +static void external_snapshot_action(TransactionAction *action, + Transaction *tran, Error **errp) { int ret; int flags = 0; @@ -1420,12 +1351,14 @@ static void external_snapshot_prepare(BlkActionState *common, const char *snapshot_ref; /* File name of the new image (for 'blockdev-snapshot-sync') */ const char *new_image_file; - ExternalSnapshotState *state = - DO_UPCAST(ExternalSnapshotState, common, common); - TransactionAction *action = common->action; - AioContext *aio_context; + ExternalSnapshotState *state = g_new0(ExternalSnapshotState, 1); uint64_t perm, shared; + /* TODO We'll eventually have to take a writer lock in this function */ + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + tran_add(tran, &external_snapshot_drv, state); + /* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar * purpose but a different set of parameters */ switch (action->type) { @@ -1441,8 +1374,8 @@ static void external_snapshot_prepare(BlkActionState *common, case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC: { BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data; - device = s->has_device ? s->device : NULL; - node_name = s->has_node_name ? s->node_name : NULL; + device = s->device; + node_name = s->node_name; new_image_file = s->snapshot_file; snapshot_ref = NULL; } @@ -1452,54 +1385,48 @@ static void external_snapshot_prepare(BlkActionState *common, } /* start processing */ - if (action_check_completion_mode(common, errp) < 0) { - return; - } state->old_bs = bdrv_lookup_bs(device, node_name, errp); if (!state->old_bs) { return; } - aio_context = bdrv_get_aio_context(state->old_bs); - aio_context_acquire(aio_context); - /* Paired with .clean() */ bdrv_drained_begin(state->old_bs); if (!bdrv_is_inserted(state->old_bs)) { - error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); - goto out; + error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, + bdrv_get_device_or_node_name(state->old_bs)); + return; } if (bdrv_op_is_blocked(state->old_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) { - goto out; + return; } if (!bdrv_is_read_only(state->old_bs)) { if (bdrv_flush(state->old_bs)) { error_setg(errp, QERR_IO_ERROR); - goto out; + return; } } 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"; + const char *format = s->format ?: "qcow2"; enum NewImageMode mode; - const char *snapshot_node_name = - s->has_snapshot_node_name ? s->snapshot_node_name : NULL; + const char *snapshot_node_name = s->snapshot_node_name; if (node_name && !snapshot_node_name) { error_setg(errp, "New overlay node-name missing"); - goto out; + return; } if (snapshot_node_name && bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) { error_setg(errp, "New overlay node-name already in use"); - goto out; + return; } flags = state->old_bs->open_flags; @@ -1512,16 +1439,18 @@ static void external_snapshot_prepare(BlkActionState *common, int64_t size = bdrv_getlength(state->old_bs); if (size < 0) { error_setg_errno(errp, -size, "bdrv_getlength failed"); - goto out; + return; } bdrv_refresh_filename(state->old_bs); + bdrv_img_create(new_image_file, format, state->old_bs->filename, state->old_bs->drv->format_name, NULL, size, flags, false, &local_err); + if (local_err) { error_propagate(errp, local_err); - goto out; + return; } } @@ -1534,9 +1463,10 @@ static void external_snapshot_prepare(BlkActionState *common, state->new_bs = bdrv_open(new_image_file, snapshot_ref, options, flags, errp); + /* We will manually add the backing_hd field to the bs later */ if (!state->new_bs) { - goto out; + return; } /* @@ -1547,42 +1477,34 @@ static void external_snapshot_prepare(BlkActionState *common, 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; + return; } if (state->new_bs->drv->is_filter) { error_setg(errp, "Filters cannot be used as overlays"); - goto out; + return; } if (bdrv_cow_child(state->new_bs)) { error_setg(errp, "The overlay already has a backing image"); - goto out; + return; } if (!state->new_bs->drv->supports_backing) { error_setg(errp, "The overlay does not support backing images"); - goto out; + return; } ret = bdrv_append(state->new_bs, state->old_bs, errp); if (ret < 0) { - goto out; + return; } state->overlay_appended = true; - -out: - aio_context_release(aio_context); } -static void external_snapshot_commit(BlkActionState *common) +static void external_snapshot_commit(void *opaque) { - ExternalSnapshotState *state = - DO_UPCAST(ExternalSnapshotState, common, common); - AioContext *aio_context; - - aio_context = bdrv_get_aio_context(state->old_bs); - aio_context_acquire(aio_context); + ExternalSnapshotState *state = opaque; /* We don't need (or want) to use the transactional * bdrv_reopen_multiple() across all the entries at once, because we @@ -1590,14 +1512,11 @@ static void external_snapshot_commit(BlkActionState *common) if (!qatomic_read(&state->old_bs->copy_on_read)) { bdrv_reopen_set_read_only(state->old_bs, true, NULL); } - - aio_context_release(aio_context); } -static void external_snapshot_abort(BlkActionState *common) +static void external_snapshot_abort(void *opaque) { - ExternalSnapshotState *state = - DO_UPCAST(ExternalSnapshotState, common, common); + ExternalSnapshotState *state = opaque; if (state->new_bs) { if (state->overlay_appended) { AioContext *aio_context; @@ -1605,7 +1524,6 @@ static void external_snapshot_abort(BlkActionState *common) int ret; aio_context = bdrv_get_aio_context(state->old_bs); - aio_context_acquire(aio_context); bdrv_ref(state->old_bs); /* we can't let bdrv_set_backind_hd() close state->old_bs; we need it */ @@ -1618,46 +1536,35 @@ static void external_snapshot_abort(BlkActionState *common) */ 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); + ret = bdrv_try_change_aio_context(state->old_bs, + aio_context, NULL, NULL); assert(ret == 0); - - aio_context_release(tmp_context); - aio_context_acquire(aio_context); } + bdrv_drained_begin(state->new_bs); + bdrv_graph_wrlock(); bdrv_replace_node(state->new_bs, state->old_bs, &error_abort); - bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */ + bdrv_graph_wrunlock(); + bdrv_drained_end(state->new_bs); - aio_context_release(aio_context); + bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */ } } } -static void external_snapshot_clean(BlkActionState *common) +static void external_snapshot_clean(void *opaque) { - ExternalSnapshotState *state = - DO_UPCAST(ExternalSnapshotState, common, common); - AioContext *aio_context; + g_autofree ExternalSnapshotState *state = opaque; if (!state->old_bs) { return; } - aio_context = bdrv_get_aio_context(state->old_bs); - aio_context_acquire(aio_context); - bdrv_drained_end(state->old_bs); bdrv_unref(state->new_bs); - - aio_context_release(aio_context); } typedef struct DriveBackupState { - BlkActionState common; BlockDriverState *bs; BlockJob *job; } DriveBackupState; @@ -1668,15 +1575,25 @@ static BlockJob *do_backup_common(BackupCommon *backup, AioContext *aio_context, JobTxn *txn, Error **errp); -static void drive_backup_prepare(BlkActionState *common, Error **errp) +static void drive_backup_commit(void *opaque); +static void drive_backup_abort(void *opaque); +static void drive_backup_clean(void *opaque); +TransactionActionDrv drive_backup_drv = { + .commit = drive_backup_commit, + .abort = drive_backup_abort, + .clean = drive_backup_clean, +}; + +static void drive_backup_action(DriveBackup *backup, + JobTxn *block_job_txn, + Transaction *tran, Error **errp) { - DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); - DriveBackup *backup; + DriveBackupState *state = g_new0(DriveBackupState, 1); BlockDriverState *bs; BlockDriverState *target_bs; BlockDriverState *source = NULL; AioContext *aio_context; - AioContext *old_context; + const char *format; QDict *options; Error *local_err = NULL; int flags; @@ -1684,8 +1601,9 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp) bool set_backing_hd = false; int ret; - assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP); - backup = common->action->u.drive_backup.data; + GLOBAL_STATE_CODE(); + + tran_add(tran, &drive_backup_drv, state); if (!backup->has_mode) { backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; @@ -1702,20 +1620,21 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp) } aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); state->bs = bs; /* Paired with .clean() */ bdrv_drained_begin(bs); - if (!backup->has_format) { - backup->format = backup->mode == NEW_IMAGE_MODE_EXISTING ? - NULL : (char *) bs->drv->format_name; + format = backup->format; + if (!format && backup->mode != NEW_IMAGE_MODE_EXISTING) { + format = bs->drv->format_name; } /* Early check to avoid creating target */ + bdrv_graph_rdlock_main_loop(); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { - goto out; + bdrv_graph_rdunlock_main_loop(); + return; } flags = bs->open_flags | BDRV_O_RDWR; @@ -1741,62 +1660,57 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp) flags |= BDRV_O_NO_BACKING; set_backing_hd = true; } + bdrv_graph_rdunlock_main_loop(); size = bdrv_getlength(bs); if (size < 0) { error_setg_errno(errp, -size, "bdrv_getlength failed"); - goto out; + return; } if (backup->mode != NEW_IMAGE_MODE_EXISTING) { - assert(backup->format); + assert(format); if (source) { /* Implicit filters should not appear in the filename */ - BlockDriverState *explicit_backing = - bdrv_skip_implicit_filters(source); + BlockDriverState *explicit_backing; + bdrv_graph_rdlock_main_loop(); + explicit_backing = bdrv_skip_implicit_filters(source); bdrv_refresh_filename(explicit_backing); - bdrv_img_create(backup->target, backup->format, + bdrv_graph_rdunlock_main_loop(); + + bdrv_img_create(backup->target, format, explicit_backing->filename, explicit_backing->drv->format_name, NULL, size, flags, false, &local_err); } else { - bdrv_img_create(backup->target, backup->format, NULL, NULL, NULL, + bdrv_img_create(backup->target, format, NULL, NULL, NULL, size, flags, false, &local_err); } } if (local_err) { error_propagate(errp, local_err); - goto out; + return; } 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); + if (format) { + qdict_put_str(options, "driver", 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); + ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); + if (ret < 0) { + bdrv_unref(target_bs); + return; + } if (set_backing_hd) { if (bdrv_set_backing_hd(target_bs, source, errp) < 0) { @@ -1806,79 +1720,65 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp) state->job = do_backup_common(qapi_DriveBackup_base(backup), bs, target_bs, aio_context, - common->block_job_txn, errp); + block_job_txn, errp); unref: bdrv_unref(target_bs); -out: - aio_context_release(aio_context); } -static void drive_backup_commit(BlkActionState *common) +static void drive_backup_commit(void *opaque) { - DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); - AioContext *aio_context; - - aio_context = bdrv_get_aio_context(state->bs); - aio_context_acquire(aio_context); + DriveBackupState *state = opaque; assert(state->job); job_start(&state->job->job); - - aio_context_release(aio_context); } -static void drive_backup_abort(BlkActionState *common) +static void drive_backup_abort(void *opaque) { - DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); + DriveBackupState *state = opaque; if (state->job) { - AioContext *aio_context; - - aio_context = bdrv_get_aio_context(state->bs); - aio_context_acquire(aio_context); - job_cancel_sync(&state->job->job, true); - - aio_context_release(aio_context); } } -static void drive_backup_clean(BlkActionState *common) +static void drive_backup_clean(void *opaque) { - DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); - AioContext *aio_context; + g_autofree DriveBackupState *state = opaque; if (!state->bs) { return; } - aio_context = bdrv_get_aio_context(state->bs); - aio_context_acquire(aio_context); - bdrv_drained_end(state->bs); - - aio_context_release(aio_context); } typedef struct BlockdevBackupState { - BlkActionState common; BlockDriverState *bs; BlockJob *job; } BlockdevBackupState; -static void blockdev_backup_prepare(BlkActionState *common, Error **errp) +static void blockdev_backup_commit(void *opaque); +static void blockdev_backup_abort(void *opaque); +static void blockdev_backup_clean(void *opaque); +TransactionActionDrv blockdev_backup_drv = { + .commit = blockdev_backup_commit, + .abort = blockdev_backup_abort, + .clean = blockdev_backup_clean, +}; + +static void blockdev_backup_action(BlockdevBackup *backup, + JobTxn *block_job_txn, + Transaction *tran, Error **errp) { - BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); - BlockdevBackup *backup; + BlockdevBackupState *state = g_new0(BlockdevBackupState, 1); BlockDriverState *bs; BlockDriverState *target_bs; AioContext *aio_context; - AioContext *old_context; int ret; - assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); - backup = common->action->u.blockdev_backup.data; + tran_add(tran, &blockdev_backup_drv, state); bs = bdrv_lookup_bs(backup->device, backup->device, errp); if (!bs) { @@ -1890,19 +1790,14 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp) return; } - /* Honor bdrv_try_set_aio_context() context acquisition requirements. */ + /* Honor bdrv_try_change_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); + ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, 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() */ @@ -1910,80 +1805,58 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp) state->job = do_backup_common(qapi_BlockdevBackup_base(backup), bs, target_bs, aio_context, - common->block_job_txn, errp); - - aio_context_release(aio_context); + block_job_txn, errp); } -static void blockdev_backup_commit(BlkActionState *common) +static void blockdev_backup_commit(void *opaque) { - BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); - AioContext *aio_context; - - aio_context = bdrv_get_aio_context(state->bs); - aio_context_acquire(aio_context); + BlockdevBackupState *state = opaque; assert(state->job); job_start(&state->job->job); - - aio_context_release(aio_context); } -static void blockdev_backup_abort(BlkActionState *common) +static void blockdev_backup_abort(void *opaque) { - BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); + BlockdevBackupState *state = opaque; if (state->job) { - AioContext *aio_context; - - aio_context = bdrv_get_aio_context(state->bs); - aio_context_acquire(aio_context); - job_cancel_sync(&state->job->job, true); - - aio_context_release(aio_context); } } -static void blockdev_backup_clean(BlkActionState *common) +static void blockdev_backup_clean(void *opaque) { - BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); - AioContext *aio_context; + g_autofree BlockdevBackupState *state = opaque; if (!state->bs) { return; } - aio_context = bdrv_get_aio_context(state->bs); - aio_context_acquire(aio_context); - bdrv_drained_end(state->bs); - - aio_context_release(aio_context); } typedef struct BlockDirtyBitmapState { - BlkActionState common; BdrvDirtyBitmap *bitmap; BlockDriverState *bs; HBitmap *backup; - bool prepared; bool was_enabled; } BlockDirtyBitmapState; -static void block_dirty_bitmap_add_prepare(BlkActionState *common, - Error **errp) +static void block_dirty_bitmap_add_abort(void *opaque); +TransactionActionDrv block_dirty_bitmap_add_drv = { + .abort = block_dirty_bitmap_add_abort, + .clean = g_free, +}; + +static void block_dirty_bitmap_add_action(BlockDirtyBitmapAdd *action, + Transaction *tran, Error **errp) { Error *local_err = NULL; - BlockDirtyBitmapAdd *action; - BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, - common, common); + BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); - if (action_check_completion_mode(common, errp) < 0) { - return; - } + tran_add(tran, &block_dirty_bitmap_add_drv, state); - action = common->action->u.block_dirty_bitmap_add.data; /* AIO context taken and released within qmp_block_dirty_bitmap_add */ qmp_block_dirty_bitmap_add(action->node, action->name, action->has_granularity, action->granularity, @@ -1992,39 +1865,37 @@ static void block_dirty_bitmap_add_prepare(BlkActionState *common, &local_err); if (!local_err) { - state->prepared = true; + state->bitmap = block_dirty_bitmap_lookup(action->node, action->name, + NULL, &error_abort); } else { error_propagate(errp, local_err); } } -static void block_dirty_bitmap_add_abort(BlkActionState *common) +static void block_dirty_bitmap_add_abort(void *opaque) { - BlockDirtyBitmapAdd *action; - BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, - common, common); + BlockDirtyBitmapState *state = opaque; - action = common->action->u.block_dirty_bitmap_add.data; - /* Should not be able to fail: IF the bitmap was added via .prepare(), - * then the node reference and bitmap name must have been valid. - */ - if (state->prepared) { - qmp_block_dirty_bitmap_remove(action->node, action->name, &error_abort); + if (state->bitmap) { + bdrv_release_dirty_bitmap(state->bitmap); } } -static void block_dirty_bitmap_clear_prepare(BlkActionState *common, - Error **errp) +static void block_dirty_bitmap_restore(void *opaque); +static void block_dirty_bitmap_free_backup(void *opaque); +TransactionActionDrv block_dirty_bitmap_clear_drv = { + .abort = block_dirty_bitmap_restore, + .commit = block_dirty_bitmap_free_backup, + .clean = g_free, +}; + +static void block_dirty_bitmap_clear_action(BlockDirtyBitmap *action, + Transaction *tran, Error **errp) { - BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, - common, common); - BlockDirtyBitmap *action; + BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); - if (action_check_completion_mode(common, errp) < 0) { - return; - } + tran_add(tran, &block_dirty_bitmap_clear_drv, state); - action = common->action->u.block_dirty_bitmap_clear.data; state->bitmap = block_dirty_bitmap_lookup(action->node, action->name, &state->bs, @@ -2040,36 +1911,35 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common, bdrv_clear_dirty_bitmap(state->bitmap, &state->backup); } -static void block_dirty_bitmap_restore(BlkActionState *common) +static void block_dirty_bitmap_restore(void *opaque) { - BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, - common, common); + BlockDirtyBitmapState *state = opaque; if (state->backup) { bdrv_restore_dirty_bitmap(state->bitmap, state->backup); } } -static void block_dirty_bitmap_free_backup(BlkActionState *common) +static void block_dirty_bitmap_free_backup(void *opaque) { - BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, - common, common); + BlockDirtyBitmapState *state = opaque; hbitmap_free(state->backup); } -static void block_dirty_bitmap_enable_prepare(BlkActionState *common, - Error **errp) +static void block_dirty_bitmap_enable_abort(void *opaque); +TransactionActionDrv block_dirty_bitmap_enable_drv = { + .abort = block_dirty_bitmap_enable_abort, + .clean = g_free, +}; + +static void block_dirty_bitmap_enable_action(BlockDirtyBitmap *action, + Transaction *tran, Error **errp) { - BlockDirtyBitmap *action; - BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, - common, common); + BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); - if (action_check_completion_mode(common, errp) < 0) { - return; - } + tran_add(tran, &block_dirty_bitmap_enable_drv, state); - action = common->action->u.block_dirty_bitmap_enable.data; state->bitmap = block_dirty_bitmap_lookup(action->node, action->name, NULL, @@ -2086,28 +1956,28 @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common, bdrv_enable_dirty_bitmap(state->bitmap); } -static void block_dirty_bitmap_enable_abort(BlkActionState *common) +static void block_dirty_bitmap_enable_abort(void *opaque) { - BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, - common, common); + BlockDirtyBitmapState *state = opaque; if (!state->was_enabled) { bdrv_disable_dirty_bitmap(state->bitmap); } } -static void block_dirty_bitmap_disable_prepare(BlkActionState *common, - Error **errp) +static void block_dirty_bitmap_disable_abort(void *opaque); +TransactionActionDrv block_dirty_bitmap_disable_drv = { + .abort = block_dirty_bitmap_disable_abort, + .clean = g_free, +}; + +static void block_dirty_bitmap_disable_action(BlockDirtyBitmap *action, + Transaction *tran, Error **errp) { - BlockDirtyBitmap *action; - BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, - common, common); + BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); - if (action_check_completion_mode(common, errp) < 0) { - return; - } + tran_add(tran, &block_dirty_bitmap_disable_drv, state); - action = common->action->u.block_dirty_bitmap_disable.data; state->bitmap = block_dirty_bitmap_lookup(action->node, action->name, NULL, @@ -2124,46 +1994,48 @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common, bdrv_disable_dirty_bitmap(state->bitmap); } -static void block_dirty_bitmap_disable_abort(BlkActionState *common) +static void block_dirty_bitmap_disable_abort(void *opaque) { - BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, - common, common); + BlockDirtyBitmapState *state = opaque; if (state->was_enabled) { bdrv_enable_dirty_bitmap(state->bitmap); } } -static void block_dirty_bitmap_merge_prepare(BlkActionState *common, - Error **errp) +TransactionActionDrv block_dirty_bitmap_merge_drv = { + .commit = block_dirty_bitmap_free_backup, + .abort = block_dirty_bitmap_restore, + .clean = g_free, +}; + +static void block_dirty_bitmap_merge_action(BlockDirtyBitmapMerge *action, + Transaction *tran, Error **errp) { - BlockDirtyBitmapMerge *action; - BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, - common, common); + BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); - if (action_check_completion_mode(common, errp) < 0) { - return; - } - - action = common->action->u.block_dirty_bitmap_merge.data; + tran_add(tran, &block_dirty_bitmap_merge_drv, state); state->bitmap = block_dirty_bitmap_merge(action->node, action->target, action->bitmaps, &state->backup, errp); } -static void block_dirty_bitmap_remove_prepare(BlkActionState *common, - Error **errp) +static void block_dirty_bitmap_remove_commit(void *opaque); +static void block_dirty_bitmap_remove_abort(void *opaque); +TransactionActionDrv block_dirty_bitmap_remove_drv = { + .commit = block_dirty_bitmap_remove_commit, + .abort = block_dirty_bitmap_remove_abort, + .clean = g_free, +}; + +static void block_dirty_bitmap_remove_action(BlockDirtyBitmap *action, + Transaction *tran, Error **errp) { - BlockDirtyBitmap *action; - BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, - common, common); + BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); - if (action_check_completion_mode(common, errp) < 0) { - return; - } + tran_add(tran, &block_dirty_bitmap_remove_drv, state); - action = common->action->u.block_dirty_bitmap_remove.data; state->bitmap = block_dirty_bitmap_remove(action->node, action->name, false, &state->bs, errp); @@ -2173,10 +2045,9 @@ static void block_dirty_bitmap_remove_prepare(BlkActionState *common, } } -static void block_dirty_bitmap_remove_abort(BlkActionState *common) +static void block_dirty_bitmap_remove_abort(void *opaque) { - BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, - common, common); + BlockDirtyBitmapState *state = opaque; if (state->bitmap) { bdrv_dirty_bitmap_skip_store(state->bitmap, false); @@ -2184,210 +2055,156 @@ static void block_dirty_bitmap_remove_abort(BlkActionState *common) } } -static void block_dirty_bitmap_remove_commit(BlkActionState *common) +static void block_dirty_bitmap_remove_commit(void *opaque) { - BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, - common, common); + BlockDirtyBitmapState *state = opaque; bdrv_dirty_bitmap_set_busy(state->bitmap, false); bdrv_release_dirty_bitmap(state->bitmap); } -static void abort_prepare(BlkActionState *common, Error **errp) +static void abort_commit(void *opaque); +TransactionActionDrv abort_drv = { + .commit = abort_commit, +}; + +static void abort_action(Transaction *tran, Error **errp) { + tran_add(tran, &abort_drv, NULL); error_setg(errp, "Transaction aborted using Abort action"); } -static void abort_commit(BlkActionState *common) +static void abort_commit(void *opaque) { g_assert_not_reached(); /* this action never succeeds */ } -static const BlkActionOps actions[] = { - [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT] = { - .instance_size = sizeof(ExternalSnapshotState), - .prepare = external_snapshot_prepare, - .commit = external_snapshot_commit, - .abort = external_snapshot_abort, - .clean = external_snapshot_clean, - }, - [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = { - .instance_size = sizeof(ExternalSnapshotState), - .prepare = external_snapshot_prepare, - .commit = external_snapshot_commit, - .abort = external_snapshot_abort, - .clean = external_snapshot_clean, - }, - [TRANSACTION_ACTION_KIND_DRIVE_BACKUP] = { - .instance_size = sizeof(DriveBackupState), - .prepare = drive_backup_prepare, - .commit = drive_backup_commit, - .abort = drive_backup_abort, - .clean = drive_backup_clean, - }, - [TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = { - .instance_size = sizeof(BlockdevBackupState), - .prepare = blockdev_backup_prepare, - .commit = blockdev_backup_commit, - .abort = blockdev_backup_abort, - .clean = blockdev_backup_clean, - }, - [TRANSACTION_ACTION_KIND_ABORT] = { - .instance_size = sizeof(BlkActionState), - .prepare = abort_prepare, - .commit = abort_commit, - }, - [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC] = { - .instance_size = sizeof(InternalSnapshotState), - .prepare = internal_snapshot_prepare, - .abort = internal_snapshot_abort, - .clean = internal_snapshot_clean, - }, - [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ADD] = { - .instance_size = sizeof(BlockDirtyBitmapState), - .prepare = block_dirty_bitmap_add_prepare, - .abort = block_dirty_bitmap_add_abort, - }, - [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR] = { - .instance_size = sizeof(BlockDirtyBitmapState), - .prepare = block_dirty_bitmap_clear_prepare, - .commit = block_dirty_bitmap_free_backup, - .abort = block_dirty_bitmap_restore, - }, - [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ENABLE] = { - .instance_size = sizeof(BlockDirtyBitmapState), - .prepare = block_dirty_bitmap_enable_prepare, - .abort = block_dirty_bitmap_enable_abort, - }, - [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_DISABLE] = { - .instance_size = sizeof(BlockDirtyBitmapState), - .prepare = block_dirty_bitmap_disable_prepare, - .abort = block_dirty_bitmap_disable_abort, - }, - [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_MERGE] = { - .instance_size = sizeof(BlockDirtyBitmapState), - .prepare = block_dirty_bitmap_merge_prepare, - .commit = block_dirty_bitmap_free_backup, - .abort = block_dirty_bitmap_restore, - }, - [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_REMOVE] = { - .instance_size = sizeof(BlockDirtyBitmapState), - .prepare = block_dirty_bitmap_remove_prepare, - .commit = block_dirty_bitmap_remove_commit, - .abort = block_dirty_bitmap_remove_abort, - }, - /* Where are transactions for MIRROR, COMMIT and STREAM? +static void transaction_action(TransactionAction *act, JobTxn *block_job_txn, + Transaction *tran, Error **errp) +{ + switch (act->type) { + case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT: + case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC: + external_snapshot_action(act, tran, errp); + return; + case TRANSACTION_ACTION_KIND_DRIVE_BACKUP: + drive_backup_action(act->u.drive_backup.data, + block_job_txn, tran, errp); + return; + case TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP: + blockdev_backup_action(act->u.blockdev_backup.data, + block_job_txn, tran, errp); + return; + case TRANSACTION_ACTION_KIND_ABORT: + abort_action(tran, errp); + return; + case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC: + internal_snapshot_action(act->u.blockdev_snapshot_internal_sync.data, + tran, errp); + return; + case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ADD: + block_dirty_bitmap_add_action(act->u.block_dirty_bitmap_add.data, + tran, errp); + return; + case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR: + block_dirty_bitmap_clear_action(act->u.block_dirty_bitmap_clear.data, + tran, errp); + return; + case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ENABLE: + block_dirty_bitmap_enable_action(act->u.block_dirty_bitmap_enable.data, + tran, errp); + return; + case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_DISABLE: + block_dirty_bitmap_disable_action( + act->u.block_dirty_bitmap_disable.data, tran, errp); + return; + case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_MERGE: + block_dirty_bitmap_merge_action(act->u.block_dirty_bitmap_merge.data, + tran, errp); + return; + case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_REMOVE: + block_dirty_bitmap_remove_action(act->u.block_dirty_bitmap_remove.data, + tran, errp); + return; + /* + * Where are transactions for MIRROR, COMMIT and STREAM? * Although these blockjobs use transaction callbacks like the backup job, * these jobs do not necessarily adhere to transaction semantics. * These jobs may not fully undo all of their actions on abort, nor do they * necessarily work in transactions with more than one job in them. */ -}; - -/** - * Allocate a TransactionProperties structure if necessary, and fill - * that structure with desired defaults if they are unset. - */ -static TransactionProperties *get_transaction_properties( - TransactionProperties *props) -{ - if (!props) { - props = g_new0(TransactionProperties, 1); - } - - if (!props->has_completion_mode) { - props->has_completion_mode = true; - props->completion_mode = ACTION_COMPLETION_MODE_INDIVIDUAL; - } - - return props; + case TRANSACTION_ACTION_KIND__MAX: + default: + g_assert_not_reached(); + }; } + /* * 'Atomic' group operations. The operations are performed as a set, and if * any fail then we roll back all operations in the group. * * Always run under BQL. */ -void qmp_transaction(TransactionActionList *dev_list, - bool has_props, - struct TransactionProperties *props, +void qmp_transaction(TransactionActionList *actions, + struct TransactionProperties *properties, Error **errp) { - TransactionActionList *dev_entry = dev_list; + TransactionActionList *act; JobTxn *block_job_txn = NULL; - BlkActionState *state, *next; Error *local_err = NULL; + Transaction *tran; + ActionCompletionMode comp_mode = + properties ? properties->completion_mode : + ACTION_COMPLETION_MODE_INDIVIDUAL; GLOBAL_STATE_CODE(); - QTAILQ_HEAD(, BlkActionState) snap_bdrv_states; - QTAILQ_INIT(&snap_bdrv_states); - /* Does this transaction get canceled as a group on failure? * If not, we don't really need to make a JobTxn. */ - props = get_transaction_properties(props); - if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { + if (comp_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { + for (act = actions; act; act = act->next) { + TransactionActionKind type = act->value->type; + + if (type != TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP && + type != TRANSACTION_ACTION_KIND_DRIVE_BACKUP) + { + error_setg(errp, + "Action '%s' does not support transaction property " + "completion-mode = %s", + TransactionActionKind_str(type), + ActionCompletionMode_str(comp_mode)); + return; + } + } + block_job_txn = job_txn_new(); } /* drain all i/o before any operations */ bdrv_drain_all(); + tran = tran_new(); + /* We don't do anything in this loop that commits us to the operations */ - while (NULL != dev_entry) { - TransactionAction *dev_info = NULL; - const BlkActionOps *ops; - - dev_info = dev_entry->value; - dev_entry = dev_entry->next; - - assert(dev_info->type < ARRAY_SIZE(actions)); - - ops = &actions[dev_info->type]; - assert(ops->instance_size > 0); - - state = g_malloc0(ops->instance_size); - state->ops = ops; - state->action = dev_info; - state->block_job_txn = block_job_txn; - state->txn_props = props; - QTAILQ_INSERT_TAIL(&snap_bdrv_states, state, entry); - - state->ops->prepare(state, &local_err); + for (act = actions; act; act = act->next) { + transaction_action(act->value, block_job_txn, tran, &local_err); if (local_err) { error_propagate(errp, local_err); goto delete_and_fail; } } - QTAILQ_FOREACH(state, &snap_bdrv_states, entry) { - if (state->ops->commit) { - state->ops->commit(state); - } - } + tran_commit(tran); /* success */ goto exit; delete_and_fail: /* failure, and it is all-or-none; roll back all operations */ - QTAILQ_FOREACH_REVERSE(state, &snap_bdrv_states, entry) { - if (state->ops->abort) { - state->ops->abort(state); - } - } + tran_abort(tran); exit: - QTAILQ_FOREACH_SAFE(state, &snap_bdrv_states, entry, next) { - if (state->ops->clean) { - state->ops->clean(state); - } - g_free(state); - } - if (!has_props) { - qapi_free_TransactionProperties(props); - } job_txn_unref(block_job_txn); } @@ -2416,8 +2233,7 @@ BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, return ret; } -void coroutine_fn qmp_block_resize(bool has_device, const char *device, - bool has_node_name, const char *node_name, +void coroutine_fn qmp_block_resize(const char *device, const char *node_name, int64_t size, Error **errp) { Error *local_err = NULL; @@ -2425,9 +2241,7 @@ void coroutine_fn qmp_block_resize(bool has_device, const char *device, BlockDriverState *bs; AioContext *old_ctx; - bs = bdrv_lookup_bs(has_device ? device : NULL, - has_node_name ? node_name : NULL, - &local_err); + bs = bdrv_lookup_bs(device, node_name, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -2438,38 +2252,38 @@ void coroutine_fn qmp_block_resize(bool has_device, const char *device, return; } - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, NULL)) { - error_setg(errp, QERR_DEVICE_IN_USE, device); + bdrv_graph_co_rdlock(); + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, errp)) { + bdrv_graph_co_rdunlock(); return; } + bdrv_graph_co_rdunlock(); - blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, errp); + blk = blk_co_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, errp); if (!blk) { return; } - bdrv_co_lock(bs); bdrv_drained_begin(bs); - bdrv_co_unlock(bs); old_ctx = bdrv_co_enter(bs); - blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp); + blk_co_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp); bdrv_co_leave(bs, old_ctx); - bdrv_co_lock(bs); bdrv_drained_end(bs); - blk_unref(blk); - bdrv_co_unlock(bs); + blk_co_unref(blk); } -void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, - bool has_base, const char *base, - bool has_base_node, const char *base_node, - bool has_backing_file, const char *backing_file, - bool has_bottom, const char *bottom, +void qmp_block_stream(const char *job_id, const char *device, + const char *base, + const char *base_node, + const char *backing_file, + bool has_backing_mask_protocol, + bool backing_mask_protocol, + const char *bottom, bool has_speed, int64_t speed, bool has_on_error, BlockdevOnError on_error, - bool has_filter_node_name, const char *filter_node_name, + const char *filter_node_name, bool has_auto_finalize, bool auto_finalize, bool has_auto_dismiss, bool auto_dismiss, Error **errp) @@ -2481,24 +2295,30 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, Error *local_err = NULL; int job_flags = JOB_DEFAULT; - if (has_base && has_base_node) { + GLOBAL_STATE_CODE(); + + if (base && base_node) { error_setg(errp, "'base' and 'base-node' cannot be specified " "at the same time"); return; } - if (has_base && has_bottom) { + if (base && bottom) { error_setg(errp, "'base' and 'bottom' cannot be specified " "at the same time"); return; } - if (has_bottom && has_base_node) { + if (bottom && base_node) { error_setg(errp, "'bottom' and 'base-node' cannot be specified " "at the same time"); return; } + if (!has_backing_mask_protocol) { + backing_mask_protocol = false; + } + if (!has_on_error) { on_error = BLOCKDEV_ON_ERROR_REPORT; } @@ -2509,49 +2329,50 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, } aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - if (has_base) { + bdrv_graph_rdlock_main_loop(); + if (base) { base_bs = bdrv_find_backing_image(bs, base); if (base_bs == NULL) { error_setg(errp, "Can't find '%s' in the backing chain", base); - goto out; + goto out_rdlock; } assert(bdrv_get_aio_context(base_bs) == aio_context); } - if (has_base_node) { + if (base_node) { base_bs = bdrv_lookup_bs(NULL, base_node, errp); if (!base_bs) { - goto out; + goto out_rdlock; } if (bs == base_bs || !bdrv_chain_contains(bs, base_bs)) { error_setg(errp, "Node '%s' is not a backing image of '%s'", base_node, device); - goto out; + goto out_rdlock; } assert(bdrv_get_aio_context(base_bs) == aio_context); + bdrv_refresh_filename(base_bs); } - if (has_bottom) { + if (bottom) { bottom_bs = bdrv_lookup_bs(NULL, bottom, errp); if (!bottom_bs) { - goto out; + goto out_rdlock; } if (!bottom_bs->drv) { error_setg(errp, "Node '%s' is not open", bottom); - goto out; + goto out_rdlock; } if (bottom_bs->drv->is_filter) { error_setg(errp, "Node '%s' is a filter, use a non-filter node " "as 'bottom'", bottom); - goto out; + goto out_rdlock; } if (!bdrv_chain_contains(bs, bottom_bs)) { error_setg(errp, "Node '%s' is not in a chain starting from '%s'", bottom, device); - goto out; + goto out_rdlock; } assert(bdrv_get_aio_context(bottom_bs) == aio_context); } @@ -2559,21 +2380,22 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, /* * Check for op blockers in the whole chain between bs and base (or bottom) */ - iter_end = has_bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs; + iter_end = bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs; for (iter = bs; iter && iter != iter_end; iter = bdrv_filter_or_cow_bs(iter)) { if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_STREAM, errp)) { - goto out; + goto out_rdlock; } } + bdrv_graph_rdunlock_main_loop(); /* if we are streaming the entire chain, the result will have no backing * file, and specifying one is therefore an error */ - if (base_bs == NULL && has_backing_file) { + if (!base_bs && backing_file) { error_setg(errp, "backing file specified, but streaming the " "entire chain"); - goto out; + return; } if (has_auto_finalize && !auto_finalize) { @@ -2583,29 +2405,33 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, job_flags |= JOB_MANUAL_DISMISS; } - stream_start(has_job_id ? job_id : NULL, bs, base_bs, backing_file, + stream_start(job_id, bs, base_bs, backing_file, + backing_mask_protocol, bottom_bs, job_flags, has_speed ? speed : 0, on_error, filter_node_name, &local_err); if (local_err) { error_propagate(errp, local_err); - goto out; + return; } trace_qmp_block_stream(bs); + return; -out: - aio_context_release(aio_context); +out_rdlock: + bdrv_graph_rdunlock_main_loop(); } -void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, - bool has_base_node, const char *base_node, - bool has_base, const char *base, - bool has_top_node, const char *top_node, - bool has_top, const char *top, - bool has_backing_file, const char *backing_file, +void qmp_block_commit(const char *job_id, const char *device, + const char *base_node, + const char *base, + const char *top_node, + const char *top, + const char *backing_file, + bool has_backing_mask_protocol, + bool backing_mask_protocol, bool has_speed, int64_t speed, bool has_on_error, BlockdevOnError on_error, - bool has_filter_node_name, const char *filter_node_name, + const char *filter_node_name, bool has_auto_finalize, bool auto_finalize, bool has_auto_dismiss, bool auto_dismiss, Error **errp) @@ -2618,21 +2444,24 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, int job_flags = JOB_DEFAULT; uint64_t top_perm, top_shared; + /* TODO We'll eventually have to take a writer lock in this function */ + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!has_speed) { speed = 0; } if (!has_on_error) { on_error = BLOCKDEV_ON_ERROR_REPORT; } - if (!has_filter_node_name) { - filter_node_name = NULL; - } if (has_auto_finalize && !auto_finalize) { job_flags |= JOB_MANUAL_FINALIZE; } if (has_auto_dismiss && !auto_dismiss) { job_flags |= JOB_MANUAL_DISMISS; } + if (!has_backing_mask_protocol) { + backing_mask_protocol = false; + } /* Important Note: * libvirt relies on the DeviceNotFound error class in order to probe for @@ -2653,29 +2482,28 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, } aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, errp)) { - goto out; + return; } /* default top_bs is the active layer */ top_bs = bs; - if (has_top_node && has_top) { + if (top_node && top) { error_setg(errp, "'top-node' and 'top' are mutually exclusive"); - goto out; - } else if (has_top_node) { + return; + } else if (top_node) { top_bs = bdrv_lookup_bs(NULL, top_node, errp); if (top_bs == NULL) { - goto out; + return; } if (!bdrv_chain_contains(bs, top_bs)) { error_setg(errp, "'%s' is not in this backing file chain", top_node); - goto out; + return; } - } else if (has_top && top) { + } else if (top) { /* This strcmp() is just a shortcut, there is no need to * refresh @bs's filename. If it mismatches, * bdrv_find_backing_image() will do the refresh and may still @@ -2687,35 +2515,35 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, if (top_bs == NULL) { error_setg(errp, "Top image file %s not found", top ? top : "NULL"); - goto out; + return; } assert(bdrv_get_aio_context(top_bs) == aio_context); - if (has_base_node && has_base) { + if (base_node && base) { error_setg(errp, "'base-node' and 'base' are mutually exclusive"); - goto out; - } else if (has_base_node) { + return; + } else if (base_node) { base_bs = bdrv_lookup_bs(NULL, base_node, errp); if (base_bs == NULL) { - goto out; + return; } if (!bdrv_chain_contains(top_bs, base_bs)) { error_setg(errp, "'%s' is not in this backing file chain", base_node); - goto out; + return; } - } else if (has_base && base) { + } else if (base) { base_bs = bdrv_find_backing_image(top_bs, base); if (base_bs == NULL) { error_setg(errp, "Can't find '%s' in the backing chain", base); - goto out; + return; } } else { base_bs = bdrv_find_base(top_bs); if (base_bs == NULL) { error_setg(errp, "There is no backimg image"); - goto out; + return; } } @@ -2725,14 +2553,14 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, iter = bdrv_filter_or_cow_bs(iter)) { if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { - goto out; + return; } } /* Do not allow attempts to commit an image into itself */ if (top_bs == base_bs) { error_setg(errp, "cannot commit an image into itself"); - goto out; + return; } /* @@ -2747,7 +2575,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, if (top_perm & BLK_PERM_WRITE || bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs)) { - if (has_backing_file) { + if (backing_file) { if (bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs)) { error_setg(errp, "'backing-file' specified," " but 'top' is the active layer"); @@ -2755,9 +2583,9 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, error_setg(errp, "'backing-file' specified, but 'top' has a " "writer on it"); } - goto out; + return; } - if (!has_job_id) { + if (!job_id) { /* * Emulate here what block_job_create() does, because it * is possible that @bs != @top_bs (the block job should @@ -2771,19 +2599,17 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, } else { BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs); if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { - goto out; + return; } - commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, job_flags, - speed, on_error, has_backing_file ? backing_file : NULL, + commit_start(job_id, bs, base_bs, top_bs, job_flags, + speed, on_error, backing_file, + backing_mask_protocol, filter_node_name, &local_err); } if (local_err != NULL) { error_propagate(errp, local_err); - goto out; + return; } - -out: - aio_context_release(aio_context); } /* Common QMP interface for drive-backup and blockdev-backup */ @@ -2807,9 +2633,6 @@ static BlockJob *do_backup_common(BackupCommon *backup, if (!backup->has_on_target_error) { backup->on_target_error = BLOCKDEV_ON_ERROR_REPORT; } - if (!backup->has_job_id) { - backup->job_id = NULL; - } if (!backup->has_auto_finalize) { backup->auto_finalize = true; } @@ -2835,7 +2658,7 @@ static BlockJob *do_backup_common(BackupCommon *backup, if ((backup->sync == MIRROR_SYNC_MODE_BITMAP) || (backup->sync == MIRROR_SYNC_MODE_INCREMENTAL)) { /* done before desugaring 'incremental' to print the right message */ - if (!backup->has_bitmap) { + if (!backup->bitmap) { error_setg(errp, "must provide a valid bitmap name for " "'%s' sync mode", MirrorSyncMode_str(backup->sync)); return NULL; @@ -2856,7 +2679,7 @@ static BlockJob *do_backup_common(BackupCommon *backup, backup->bitmap_mode = BITMAP_SYNC_MODE_ON_SUCCESS; } - if (backup->has_bitmap) { + if (backup->bitmap) { bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap); if (!bmap) { error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap); @@ -2889,7 +2712,7 @@ static BlockJob *do_backup_common(BackupCommon *backup, } } - if (!backup->has_bitmap && backup->has_bitmap_mode) { + if (!backup->bitmap && backup->has_bitmap_mode) { error_setg(errp, "Cannot specify bitmap sync mode without a bitmap"); return NULL; } @@ -2932,6 +2755,8 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(bool has_flat, XDbgBlockGraph *qmp_x_debug_query_block_graph(Error **errp) { + GRAPH_RDLOCK_GUARD_MAINLOOP(); + return bdrv_get_xdbg_block_graph(errp); } @@ -2949,7 +2774,7 @@ void qmp_blockdev_backup(BlockdevBackup *backup, Error **errp) **/ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, BlockDriverState *target, - bool has_replaces, const char *replaces, + const char *replaces, enum MirrorSyncMode sync, BlockMirrorBackingMode backing_mode, bool zero_target, @@ -2961,7 +2786,6 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, bool has_on_target_error, BlockdevOnError on_target_error, bool has_unmap, bool unmap, - bool has_filter_node_name, const char *filter_node_name, bool has_copy_mode, MirrorCopyMode copy_mode, bool has_auto_finalize, bool auto_finalize, @@ -2971,6 +2795,9 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, BlockDriverState *unfiltered_bs; int job_flags = JOB_DEFAULT; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!has_speed) { speed = 0; } @@ -2989,9 +2816,6 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, if (!has_unmap) { unmap = true; } - if (!has_filter_node_name) { - filter_node_name = NULL; - } if (!has_copy_mode) { copy_mode = MIRROR_COPY_MODE_BACKGROUND; } @@ -3024,18 +2848,16 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, sync = MIRROR_SYNC_MODE_FULL; } - if (!has_replaces) { + if (!replaces) { /* We want to mirror from @bs, but keep implicit filters on top */ unfiltered_bs = bdrv_skip_implicit_filters(bs); if (unfiltered_bs != bs) { replaces = unfiltered_bs->node_name; - has_replaces = true; } } - if (has_replaces) { + if (replaces) { BlockDriverState *to_replace_bs; - AioContext *replace_aio_context; int64_t bs_size, replace_size; bs_size = bdrv_getlength(bs); @@ -3049,10 +2871,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, return; } - replace_aio_context = bdrv_get_aio_context(to_replace_bs); - aio_context_acquire(replace_aio_context); replace_size = bdrv_getlength(to_replace_bs); - aio_context_release(replace_aio_context); if (replace_size < 0) { error_setg_errno(errp, -replace_size, @@ -3070,7 +2889,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, * and will allow to check whether the node still exist at mirror completion */ mirror_start(job_id, bs, target, - has_replaces ? replaces : NULL, job_flags, + replaces, job_flags, speed, granularity, buf_size, sync, backing_mode, zero_target, on_source_error, on_target_error, unmap, filter_node_name, copy_mode, errp); @@ -3081,7 +2900,6 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) BlockDriverState *bs; BlockDriverState *target_backing_bs, *target_bs; AioContext *aio_context; - AioContext *old_context; BlockMirrorBackingMode backing_mode; Error *local_err = NULL; QDict *options = NULL; @@ -3097,18 +2915,19 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) } /* Early check to avoid creating target */ + bdrv_graph_rdlock_main_loop(); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) { + bdrv_graph_rdunlock_main_loop(); return; } aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); if (!arg->has_mode) { arg->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; } - if (!arg->has_format) { + if (!arg->format) { format = (arg->mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name); } @@ -3121,18 +2940,19 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) if (arg->sync == MIRROR_SYNC_MODE_NONE) { target_backing_bs = bs; } + bdrv_graph_rdunlock_main_loop(); size = bdrv_getlength(bs); if (size < 0) { error_setg_errno(errp, -size, "bdrv_getlength failed"); - goto out; + return; } - if (arg->has_replaces) { - if (!arg->has_node_name) { + if (arg->replaces) { + if (!arg->node_name) { error_setg(errp, "a node-name must be provided when replacing a" " named node of the graph"); - goto out; + return; } } @@ -3153,16 +2973,21 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) bdrv_img_create(arg->target, format, NULL, NULL, NULL, size, flags, false, &local_err); } else { - /* Implicit filters should not appear in the filename */ - BlockDriverState *explicit_backing = - bdrv_skip_implicit_filters(target_backing_bs); + BlockDriverState *explicit_backing; switch (arg->mode) { case NEW_IMAGE_MODE_EXISTING: break; case NEW_IMAGE_MODE_ABSOLUTE_PATHS: - /* create new image with backing file */ + /* + * Create new image with backing file. + * Implicit filters should not appear in the filename. + */ + bdrv_graph_rdlock_main_loop(); + explicit_backing = bdrv_skip_implicit_filters(target_backing_bs); bdrv_refresh_filename(explicit_backing); + bdrv_graph_rdunlock_main_loop(); + bdrv_img_create(arg->target, format, explicit_backing->filename, explicit_backing->drv->format_name, @@ -3175,11 +3000,11 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) if (local_err) { error_propagate(errp, local_err); - goto out; + return; } options = qdict_new(); - if (arg->has_node_name) { + if (arg->node_name) { qdict_put_str(options, "node-name", arg->node_name); } if (format) { @@ -3191,31 +3016,24 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) */ target_bs = bdrv_open(arg->target, NULL, options, flags, errp); if (!target_bs) { - goto out; - } - - zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL && - (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); - aio_context_release(old_context); return; } - aio_context_release(old_context); - aio_context_acquire(aio_context); + bdrv_graph_rdlock_main_loop(); + zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL && + (arg->mode == NEW_IMAGE_MODE_EXISTING || + !bdrv_has_zero_init(target_bs))); + bdrv_graph_rdunlock_main_loop(); - blockdev_mirror_common(arg->has_job_id ? arg->job_id : NULL, bs, target_bs, - arg->has_replaces, arg->replaces, arg->sync, + + ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); + if (ret < 0) { + bdrv_unref(target_bs); + return; + } + + blockdev_mirror_common(arg->job_id, bs, target_bs, + arg->replaces, arg->sync, backing_mode, zero_target, arg->has_speed, arg->speed, arg->has_granularity, arg->granularity, @@ -3223,19 +3041,17 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) arg->has_on_source_error, arg->on_source_error, arg->has_on_target_error, arg->on_target_error, arg->has_unmap, arg->unmap, - false, NULL, + NULL, arg->has_copy_mode, arg->copy_mode, arg->has_auto_finalize, arg->auto_finalize, arg->has_auto_dismiss, arg->auto_dismiss, errp); bdrv_unref(target_bs); -out: - aio_context_release(aio_context); } -void qmp_blockdev_mirror(bool has_job_id, const char *job_id, +void qmp_blockdev_mirror(const char *job_id, const char *device, const char *target, - bool has_replaces, const char *replaces, + const char *replaces, MirrorSyncMode sync, bool has_speed, int64_t speed, bool has_granularity, uint32_t granularity, @@ -3244,7 +3060,6 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id, BlockdevOnError on_source_error, bool has_on_target_error, BlockdevOnError on_target_error, - bool has_filter_node_name, const char *filter_node_name, bool has_copy_mode, MirrorCopyMode copy_mode, bool has_auto_finalize, bool auto_finalize, @@ -3254,7 +3069,6 @@ 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; bool zero_target; int ret; @@ -3271,48 +3085,37 @@ 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(old_context); - - ret = bdrv_try_set_aio_context(target_bs, aio_context, errp); - - aio_context_release(old_context); - aio_context_acquire(aio_context); + ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); if (ret < 0) { - goto out; + return; } - blockdev_mirror_common(has_job_id ? job_id : NULL, bs, target_bs, - has_replaces, replaces, sync, backing_mode, + blockdev_mirror_common(job_id, bs, target_bs, + replaces, sync, backing_mode, zero_target, has_speed, speed, has_granularity, granularity, has_buf_size, buf_size, has_on_source_error, on_source_error, has_on_target_error, on_target_error, - true, true, - has_filter_node_name, filter_node_name, + true, true, filter_node_name, has_copy_mode, copy_mode, has_auto_finalize, auto_finalize, has_auto_dismiss, auto_dismiss, errp); -out: - aio_context_release(aio_context); } -/* Get a block job using its ID and acquire its AioContext */ -static BlockJob *find_block_job(const char *id, AioContext **aio_context, - Error **errp) +/* + * Get a block job using its ID. Called with job_mutex held. + */ +static BlockJob *find_block_job_locked(const char *id, Error **errp) { BlockJob *job; assert(id != NULL); - *aio_context = NULL; - - job = block_job_get(id); + job = block_job_get_locked(id); if (!job) { error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE, @@ -3320,30 +3123,30 @@ static BlockJob *find_block_job(const char *id, AioContext **aio_context, return NULL; } - *aio_context = block_job_get_aio_context(job); - aio_context_acquire(*aio_context); - return job; } void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp) { - AioContext *aio_context; - BlockJob *job = find_block_job(device, &aio_context, errp); + BlockJob *job; + + JOB_LOCK_GUARD(); + job = find_block_job_locked(device, errp); if (!job) { return; } - block_job_set_speed(job, speed, errp); - aio_context_release(aio_context); + block_job_set_speed_locked(job, speed, errp); } void qmp_block_job_cancel(const char *device, bool has_force, bool force, Error **errp) { - AioContext *aio_context; - BlockJob *job = find_block_job(device, &aio_context, errp); + BlockJob *job; + + JOB_LOCK_GUARD(); + job = find_block_job_locked(device, errp); if (!job) { return; @@ -3353,97 +3156,108 @@ void qmp_block_job_cancel(const char *device, force = false; } - if (job_user_paused(&job->job) && !force) { + if (job_user_paused_locked(&job->job) && !force) { error_setg(errp, "The block job for device '%s' is currently paused", device); - goto out; + return; } trace_qmp_block_job_cancel(job); - job_user_cancel(&job->job, force, errp); -out: - aio_context_release(aio_context); + job_user_cancel_locked(&job->job, force, errp); } void qmp_block_job_pause(const char *device, Error **errp) { - AioContext *aio_context; - BlockJob *job = find_block_job(device, &aio_context, errp); + BlockJob *job; + + JOB_LOCK_GUARD(); + job = find_block_job_locked(device, errp); if (!job) { return; } trace_qmp_block_job_pause(job); - job_user_pause(&job->job, errp); - aio_context_release(aio_context); + job_user_pause_locked(&job->job, errp); } void qmp_block_job_resume(const char *device, Error **errp) { - AioContext *aio_context; - BlockJob *job = find_block_job(device, &aio_context, errp); + BlockJob *job; + + JOB_LOCK_GUARD(); + job = find_block_job_locked(device, errp); if (!job) { return; } trace_qmp_block_job_resume(job); - job_user_resume(&job->job, errp); - aio_context_release(aio_context); + job_user_resume_locked(&job->job, errp); } void qmp_block_job_complete(const char *device, Error **errp) { - AioContext *aio_context; - BlockJob *job = find_block_job(device, &aio_context, errp); + BlockJob *job; + + JOB_LOCK_GUARD(); + job = find_block_job_locked(device, errp); if (!job) { return; } trace_qmp_block_job_complete(job); - job_complete(&job->job, errp); - aio_context_release(aio_context); + job_complete_locked(&job->job, errp); } void qmp_block_job_finalize(const char *id, Error **errp) { - AioContext *aio_context; - BlockJob *job = find_block_job(id, &aio_context, errp); + BlockJob *job; + + JOB_LOCK_GUARD(); + job = find_block_job_locked(id, errp); if (!job) { return; } trace_qmp_block_job_finalize(job); - job_ref(&job->job); - job_finalize(&job->job, errp); + job_ref_locked(&job->job); + job_finalize_locked(&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 = block_job_get_aio_context(job); - job_unref(&job->job); - aio_context_release(aio_context); + job_unref_locked(&job->job); } void qmp_block_job_dismiss(const char *id, Error **errp) { - AioContext *aio_context; - BlockJob *bjob = find_block_job(id, &aio_context, errp); + BlockJob *bjob; Job *job; + JOB_LOCK_GUARD(); + bjob = find_block_job_locked(id, errp); + if (!bjob) { return; } trace_qmp_block_job_dismiss(bjob); job = &bjob->job; - job_dismiss(&job, errp); - aio_context_release(aio_context); + job_dismiss_locked(&job, errp); +} + +void qmp_block_job_change(BlockJobChangeOptions *opts, Error **errp) +{ + BlockJob *job; + + JOB_LOCK_GUARD(); + job = find_block_job_locked(opts->id, errp); + + if (!job) { + return; + } + + block_job_change_locked(job, opts, errp); } void qmp_change_backing_file(const char *device, @@ -3452,7 +3266,6 @@ void qmp_change_backing_file(const char *device, Error **errp) { BlockDriverState *bs = NULL; - AioContext *aio_context; BlockDriverState *image_bs = NULL; Error *local_err = NULL; bool ro; @@ -3463,45 +3276,45 @@ void qmp_change_backing_file(const char *device, return; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); + bdrv_graph_rdlock_main_loop(); image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err); if (local_err) { error_propagate(errp, local_err); - goto out; + goto out_rdlock; } if (!image_bs) { error_setg(errp, "image file not found"); - goto out; + goto out_rdlock; } if (bdrv_find_base(image_bs) == image_bs) { error_setg(errp, "not allowing backing file change on an image " "without a backing file"); - goto out; + goto out_rdlock; } /* even though we are not necessarily operating on bs, we need it to * determine if block ops are currently prohibited on the chain */ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_CHANGE, errp)) { - goto out; + goto out_rdlock; } /* final sanity check */ if (!bdrv_chain_contains(bs, image_bs)) { error_setg(errp, "'%s' and image file are not in the same chain", device); - goto out; + goto out_rdlock; } + bdrv_graph_rdunlock_main_loop(); /* if not r/w, reopen to make r/w */ ro = bdrv_is_read_only(image_bs); if (ro) { if (bdrv_reopen_set_read_only(image_bs, false, errp) != 0) { - goto out; + return; } } @@ -3519,9 +3332,10 @@ void qmp_change_backing_file(const char *device, if (ro) { bdrv_reopen_set_read_only(image_bs, true, errp); } + return; -out: - aio_context_release(aio_context); +out_rdlock: + bdrv_graph_rdunlock_main_loop(); } void qmp_blockdev_add(BlockdevOptions *options, Error **errp) @@ -3556,20 +3370,17 @@ fail: void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) { BlockReopenQueue *queue = NULL; - GSList *drained = NULL; - GSList *p; /* Add each one of the BDS that we want to reopen to the queue */ for (; reopen_list != NULL; reopen_list = reopen_list->next) { BlockdevOptions *options = reopen_list->value; BlockDriverState *bs; - AioContext *ctx; QObject *obj; Visitor *v; QDict *qdict; /* Check for the selected node name */ - if (!options->has_node_name) { + if (!options->node_name) { error_setg(errp, "node-name not specified"); goto fail; } @@ -3591,14 +3402,7 @@ void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) qdict_flatten(qdict); - ctx = bdrv_get_aio_context(bs); - aio_context_acquire(ctx); - - bdrv_subtree_drained_begin(bs); queue = bdrv_reopen_queue(queue, bs, qdict, false); - drained = g_slist_prepend(drained, bs); - - aio_context_release(ctx); } /* Perform the reopen operation */ @@ -3607,23 +3411,14 @@ void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) fail: bdrv_reopen_queue_free(queue); - for (p = drained; p; p = p->next) { - BlockDriverState *bs = p->data; - AioContext *ctx = bdrv_get_aio_context(bs); - - aio_context_acquire(ctx); - bdrv_subtree_drained_end(bs); - aio_context_release(ctx); - } - g_slist_free(drained); } void qmp_blockdev_del(const char *node_name, Error **errp) { - AioContext *aio_context; BlockDriverState *bs; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); bs = bdrv_find_node(node_name); if (!bs) { @@ -3634,34 +3429,29 @@ void qmp_blockdev_del(const char *node_name, Error **errp) error_setg(errp, "Node %s is in use", node_name); return; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, errp)) { - goto out; + return; } if (!QTAILQ_IN_USE(bs, monitor_list)) { error_setg(errp, "Node %s is not owned by the monitor", bs->node_name); - goto out; + return; } if (bs->refcnt > 1) { error_setg(errp, "Block device %s is in use", bdrv_get_device_or_node_name(bs)); - goto out; + return; } QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list); bdrv_unref(bs); - -out: - aio_context_release(aio_context); } -static BdrvChild *bdrv_find_child(BlockDriverState *parent_bs, - const char *child_name) +static BdrvChild * GRAPH_RDLOCK +bdrv_find_child(BlockDriverState *parent_bs, const char *child_name) { BdrvChild *child; @@ -3674,45 +3464,49 @@ static BdrvChild *bdrv_find_child(BlockDriverState *parent_bs, return NULL; } -void qmp_x_blockdev_change(const char *parent, bool has_child, - const char *child, bool has_node, +void qmp_x_blockdev_change(const char *parent, const char *child, const char *node, Error **errp) { BlockDriverState *parent_bs, *new_bs = NULL; BdrvChild *p_child; + bdrv_graph_wrlock(); + parent_bs = bdrv_lookup_bs(parent, parent, errp); if (!parent_bs) { - return; + goto out; } - if (has_child == has_node) { - if (has_child) { + if (!child == !node) { + if (child) { error_setg(errp, "The parameters child and node are in conflict"); } else { error_setg(errp, "Either child or node must be specified"); } - return; + goto out; } - if (has_child) { + if (child) { p_child = bdrv_find_child(parent_bs, child); if (!p_child) { error_setg(errp, "Node '%s' does not have child '%s'", parent, child); - return; + goto out; } bdrv_del_child(parent_bs, p_child, errp); } - if (has_node) { + if (node) { new_bs = bdrv_find_node(node); if (!new_bs) { error_setg(errp, "Node '%s' not found", node); - return; + goto out; } bdrv_add_child(parent_bs, new_bs, errp); } + +out: + bdrv_graph_wrunlock(); } BlockJobInfoList *qmp_query_block_jobs(Error **errp) @@ -3720,17 +3514,16 @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp) BlockJobInfoList *head = NULL, **tail = &head; BlockJob *job; - for (job = block_job_next(NULL); job; job = block_job_next(job)) { + JOB_LOCK_GUARD(); + + for (job = block_job_next_locked(NULL); job; + job = block_job_next_locked(job)) { BlockJobInfo *value; - AioContext *aio_context; if (block_job_is_internal(job)) { continue; } - aio_context = block_job_get_aio_context(job); - aio_context_acquire(aio_context); - value = block_job_query(job, errp); - aio_context_release(aio_context); + value = block_job_query_locked(job, errp); if (!value) { qapi_free_BlockJobInfoList(head); return NULL; @@ -3744,10 +3537,11 @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp) void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, bool has_force, bool force, Error **errp) { - AioContext *old_context; AioContext *new_context; BlockDriverState *bs; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + bs = bdrv_find_node(node_name); if (!bs) { error_setg(errp, "Failed to find node with node-name='%s'", node_name); @@ -3774,12 +3568,7 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, new_context = qemu_get_aio_context(); } - old_context = bdrv_get_aio_context(bs); - aio_context_acquire(old_context); - - bdrv_try_set_aio_context(bs, new_context, errp); - - aio_context_release(old_context); + bdrv_try_change_aio_context(bs, new_context, NULL, errp); } QemuOptsList qemu_common_drive_opts = { diff --git a/blockjob.c b/blockjob.c index 4868453d74..d5f29e14af 100644 --- a/blockjob.c +++ b/blockjob.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "block/aio-wait.h" #include "block/block.h" #include "block/blockjob_int.h" #include "block/block_int.h" @@ -32,25 +33,9 @@ #include "qapi/error.h" #include "qapi/qapi-events-block-core.h" #include "qapi/qmp/qerror.h" -#include "qemu/coroutine.h" #include "qemu/main-loop.h" #include "qemu/timer.h" -/* - * The block job API is composed of two categories of functions. - * - * The first includes functions used by the monitor. The monitor is - * peculiar in that it accesses the block job list with block_job_get, and - * therefore needs consistency across block_job_get and the actual operation - * (e.g. block_job_set_speed). The consistency is achieved with - * aio_context_acquire/release. These functions are declared in blockjob.h. - * - * The second includes functions used by the block job drivers and sometimes - * by the core block layer. These do not care about locking, because the - * whole coroutine runs under the AioContext lock, and are declared in - * blockjob_int.h. - */ - static bool is_block_job(Job *job) { return job_type(job) == JOB_TYPE_BACKUP || @@ -59,21 +44,21 @@ static bool is_block_job(Job *job) job_type(job) == JOB_TYPE_STREAM; } -BlockJob *block_job_next(BlockJob *bjob) +BlockJob *block_job_next_locked(BlockJob *bjob) { Job *job = bjob ? &bjob->job : NULL; GLOBAL_STATE_CODE(); do { - job = job_next(job); + job = job_next_locked(job); } while (job && !is_block_job(job)); return job ? container_of(job, BlockJob, job) : NULL; } -BlockJob *block_job_get(const char *id) +BlockJob *block_job_get_locked(const char *id) { - Job *job = job_get(id); + Job *job = job_get_locked(id); GLOBAL_STATE_CODE(); if (job && is_block_job(job)) { @@ -83,6 +68,12 @@ BlockJob *block_job_get(const char *id) } } +BlockJob *block_job_get(const char *id) +{ + JOB_LOCK_GUARD(); + return block_job_get_locked(id); +} + void block_job_free(Job *job) { BlockJob *bjob = container_of(job, BlockJob, job); @@ -114,8 +105,10 @@ static bool child_job_drained_poll(BdrvChild *c) /* An inactive or completed job doesn't have any pending requests. Jobs * with !job->busy are either already paused or have a pause point after * being reentered, so no job driver code will run before they pause. */ - if (!job->busy || job_is_completed(job)) { - return false; + WITH_JOB_LOCK_GUARD() { + if (!job->busy || job_is_completed_locked(job)) { + return false; + } } /* Otherwise, assume that it isn't fully stopped yet, but allow the job to @@ -127,48 +120,61 @@ static bool child_job_drained_poll(BdrvChild *c) } } -static void child_job_drained_end(BdrvChild *c, int *drained_end_counter) +static void child_job_drained_end(BdrvChild *c) { BlockJob *job = c->opaque; job_resume(&job->job); } -static bool child_job_can_set_aio_ctx(BdrvChild *c, AioContext *ctx, - GSList **ignore, Error **errp) +typedef struct BdrvStateChildJobContext { + AioContext *new_ctx; + BlockJob *job; +} BdrvStateChildJobContext; + +static void child_job_set_aio_ctx_commit(void *opaque) +{ + BdrvStateChildJobContext *s = opaque; + BlockJob *job = s->job; + + job_set_aio_context(&job->job, s->new_ctx); +} + +static TransactionActionDrv change_child_job_context = { + .commit = child_job_set_aio_ctx_commit, + .clean = g_free, +}; + +static bool child_job_change_aio_ctx(BdrvChild *c, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp) { BlockJob *job = c->opaque; + BdrvStateChildJobContext *s; GSList *l; for (l = job->nodes; l; l = l->next) { BdrvChild *sibling = l->data; - if (!bdrv_child_can_set_aio_context(sibling, ctx, ignore, errp)) { + if (!bdrv_child_change_aio_context(sibling, ctx, visited, + tran, errp)) { return false; } } + + s = g_new(BdrvStateChildJobContext, 1); + *s = (BdrvStateChildJobContext) { + .new_ctx = ctx, + .job = job, + }; + + tran_add(tran, &change_child_job_context, s); return true; } -static void child_job_set_aio_ctx(BdrvChild *c, AioContext *ctx, - GSList **ignore) -{ - BlockJob *job = c->opaque; - GSList *l; - - for (l = job->nodes; l; l = l->next) { - BdrvChild *sibling = l->data; - if (g_slist_find(*ignore, sibling)) { - continue; - } - *ignore = g_slist_prepend(*ignore, sibling); - bdrv_set_aio_context_ignore(sibling->bs, ctx, ignore); - } - - job->job.aio_context = ctx; -} - static AioContext *child_job_get_parent_aio_context(BdrvChild *c) { BlockJob *job = c->opaque; + IO_CODE(); + JOB_LOCK_GUARD(); return job->job.aio_context; } @@ -178,8 +184,7 @@ static const BdrvChildClass child_job = { .drained_begin = child_job_drained_begin, .drained_poll = child_job_drained_poll, .drained_end = child_job_drained_end, - .can_set_aio_ctx = child_job_can_set_aio_ctx, - .set_aio_ctx = child_job_set_aio_ctx, + .change_aio_ctx = child_job_change_aio_ctx, .stay_at_node = true, .get_parent_aio_context = child_job_get_parent_aio_context, }; @@ -193,6 +198,7 @@ void block_job_remove_all_bdrv(BlockJob *job) * one to make sure that such a concurrent access does not attempt * to process an already freed BdrvChild. */ + bdrv_graph_wrlock(); while (job->nodes) { GSList *l = job->nodes; BdrvChild *c = l->data; @@ -204,6 +210,7 @@ void block_job_remove_all_bdrv(BlockJob *job) g_slist_free_1(l); } + bdrv_graph_wrunlock(); } bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs) @@ -225,21 +232,12 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, Error **errp) { BdrvChild *c; - bool need_context_ops; GLOBAL_STATE_CODE(); bdrv_ref(bs); - need_context_ops = bdrv_get_aio_context(bs) != job->job.aio_context; - - if (need_context_ops && job->job.aio_context != qemu_get_aio_context()) { - aio_context_release(job->job.aio_context); - } c = bdrv_root_attach_child(bs, name, &child_job, 0, perm, shared_perm, job, errp); - if (need_context_ops && job->job.aio_context != qemu_get_aio_context()) { - aio_context_acquire(job->job.aio_context); - } if (c == NULL) { return -EPERM; } @@ -250,7 +248,8 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, return 0; } -static void block_job_on_idle(Notifier *n, void *opaque) +/* Called with job_mutex lock held. */ +static void block_job_on_idle_locked(Notifier *n, void *opaque) { aio_wait_kick(); } @@ -271,14 +270,14 @@ static bool job_timer_pending(Job *job) return timer_pending(&job->sleep_timer); } -bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) +bool block_job_set_speed_locked(BlockJob *job, int64_t speed, Error **errp) { const BlockJobDriver *drv = block_job_driver(job); int64_t old_speed = job->speed; GLOBAL_STATE_CODE(); - if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp) < 0) { + if (job_apply_verb_locked(&job->job, JOB_VERB_SET_SPEED, errp) < 0) { return false; } if (speed < 0) { @@ -292,7 +291,9 @@ bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) job->speed = speed; if (drv->set_speed) { + job_unlock(); drv->set_speed(job, speed); + job_lock(); } if (speed && speed <= old_speed) { @@ -300,21 +301,66 @@ bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) } /* kick only if a timer is pending */ - job_enter_cond(&job->job, job_timer_pending); + job_enter_cond_locked(&job->job, job_timer_pending); return true; } -int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) +static bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) { - IO_CODE(); - return ratelimit_calculate_delay(&job->limit, n); + JOB_LOCK_GUARD(); + return block_job_set_speed_locked(job, speed, errp); } -BlockJobInfo *block_job_query(BlockJob *job, Error **errp) +void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, + Error **errp) +{ + const BlockJobDriver *drv = block_job_driver(job); + + GLOBAL_STATE_CODE(); + + if (job_apply_verb_locked(&job->job, JOB_VERB_CHANGE, errp)) { + return; + } + + if (drv->change) { + job_unlock(); + drv->change(job, opts, errp); + job_lock(); + } else { + error_setg(errp, "Job type does not support change"); + } +} + +void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n) +{ + IO_CODE(); + ratelimit_calculate_delay(&job->limit, n); +} + +void block_job_ratelimit_sleep(BlockJob *job) +{ + uint64_t delay_ns; + + /* + * Sleep at least once. If the job is reentered early, keep waiting until + * we've waited for the full time that is necessary to keep the job at the + * right speed. + * + * Make sure to recalculate the delay after each (possibly interrupted) + * sleep because the speed can change while the job has yielded. + */ + do { + delay_ns = ratelimit_calculate_delay(&job->limit, 0); + job_sleep_ns(&job->job, delay_ns); + } while (delay_ns && !job_is_cancelled(&job->job)); +} + +BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp) { BlockJobInfo *info; uint64_t progress_current, progress_total; + const BlockJobDriver *drv = block_job_driver(job); GLOBAL_STATE_CODE(); @@ -327,28 +373,33 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) &progress_total); info = g_new0(BlockJobInfo, 1); - info->type = g_strdup(job_type_str(&job->job)); + info->type = job_type(&job->job); info->device = g_strdup(job->job.id); - info->busy = qatomic_read(&job->job.busy); + info->busy = job->job.busy; info->paused = job->job.pause_count > 0; info->offset = progress_current; info->len = progress_total; info->speed = job->speed; info->io_status = job->iostatus; - info->ready = job_is_ready(&job->job), + info->ready = job_is_ready_locked(&job->job), info->status = job->job.status; info->auto_finalize = job->job.auto_finalize; info->auto_dismiss = job->job.auto_dismiss; if (job->job.ret) { - info->has_error = true; info->error = job->job.err ? g_strdup(error_get_pretty(job->job.err)) : g_strdup(strerror(-job->job.ret)); } + if (drv->query) { + job_unlock(); + drv->query(job, info); + job_lock(); + } return info; } -static void block_job_iostatus_set_err(BlockJob *job, int error) +/* Called with job lock held */ +static void block_job_iostatus_set_err_locked(BlockJob *job, int error) { if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { job->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : @@ -356,7 +407,8 @@ static void block_job_iostatus_set_err(BlockJob *job, int error) } } -static void block_job_event_cancelled(Notifier *n, void *opaque) +/* Called with job_mutex lock held. */ +static void block_job_event_cancelled_locked(Notifier *n, void *opaque) { BlockJob *job = opaque; uint64_t progress_current, progress_total; @@ -375,7 +427,8 @@ static void block_job_event_cancelled(Notifier *n, void *opaque) job->speed); } -static void block_job_event_completed(Notifier *n, void *opaque) +/* Called with job_mutex lock held. */ +static void block_job_event_completed_locked(Notifier *n, void *opaque) { BlockJob *job = opaque; const char *msg = NULL; @@ -397,11 +450,11 @@ static void block_job_event_completed(Notifier *n, void *opaque) progress_total, progress_current, job->speed, - !!msg, msg); } -static void block_job_event_pending(Notifier *n, void *opaque) +/* Called with job_mutex lock held. */ +static void block_job_event_pending_locked(Notifier *n, void *opaque) { BlockJob *job = opaque; @@ -413,7 +466,8 @@ static void block_job_event_pending(Notifier *n, void *opaque) job->job.id); } -static void block_job_event_ready(Notifier *n, void *opaque) +/* Called with job_mutex lock held. */ +static void block_job_event_ready_locked(Notifier *n, void *opaque) { BlockJob *job = opaque; uint64_t progress_current, progress_total; @@ -433,11 +487,6 @@ static void block_job_event_ready(Notifier *n, void *opaque) } -/* - * API for block job drivers and the block layer. These functions are - * declared in blockjob_int.h. - */ - void *block_job_create(const char *job_id, const BlockJobDriver *driver, JobTxn *txn, BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, int64_t speed, int flags, @@ -447,6 +496,8 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, int ret; GLOBAL_STATE_CODE(); + bdrv_graph_wrlock(); + if (job_id == NULL && !(flags & JOB_INTERNAL)) { job_id = bdrv_get_device_name(bs); } @@ -454,6 +505,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, job = job_create(job_id, &driver->job_driver, txn, bdrv_get_aio_context(bs), flags, cb, opaque, errp); if (job == NULL) { + bdrv_graph_wrunlock(); return NULL; } @@ -463,19 +515,21 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, ratelimit_init(&job->limit); - job->finalize_cancelled_notifier.notify = block_job_event_cancelled; - job->finalize_completed_notifier.notify = block_job_event_completed; - job->pending_notifier.notify = block_job_event_pending; - job->ready_notifier.notify = block_job_event_ready; - job->idle_notifier.notify = block_job_on_idle; + job->finalize_cancelled_notifier.notify = block_job_event_cancelled_locked; + job->finalize_completed_notifier.notify = block_job_event_completed_locked; + job->pending_notifier.notify = block_job_event_pending_locked; + job->ready_notifier.notify = block_job_event_ready_locked; + job->idle_notifier.notify = block_job_on_idle_locked; - notifier_list_add(&job->job.on_finalize_cancelled, - &job->finalize_cancelled_notifier); - notifier_list_add(&job->job.on_finalize_completed, - &job->finalize_completed_notifier); - notifier_list_add(&job->job.on_pending, &job->pending_notifier); - notifier_list_add(&job->job.on_ready, &job->ready_notifier); - notifier_list_add(&job->job.on_idle, &job->idle_notifier); + WITH_JOB_LOCK_GUARD() { + notifier_list_add(&job->job.on_finalize_cancelled, + &job->finalize_cancelled_notifier); + notifier_list_add(&job->job.on_finalize_completed, + &job->finalize_completed_notifier); + notifier_list_add(&job->job.on_pending, &job->pending_notifier); + notifier_list_add(&job->job.on_ready, &job->ready_notifier); + notifier_list_add(&job->job.on_idle, &job->idle_notifier); + } error_setg(&job->blocker, "block device is in use by block job: %s", job_type_str(&job->job)); @@ -491,14 +545,16 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, goto fail; } + bdrv_graph_wrunlock(); return job; fail: + bdrv_graph_wrunlock(); job_early_fail(&job->job); return NULL; } -void block_job_iostatus_reset(BlockJob *job) +void block_job_iostatus_reset_locked(BlockJob *job) { GLOBAL_STATE_CODE(); if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { @@ -508,6 +564,12 @@ void block_job_iostatus_reset(BlockJob *job) job->iostatus = BLOCK_DEVICE_IO_STATUS_OK; } +static void block_job_iostatus_reset(BlockJob *job) +{ + JOB_LOCK_GUARD(); + block_job_iostatus_reset_locked(job); +} + void block_job_user_resume(Job *job) { BlockJob *bjob = container_of(job, BlockJob, job); @@ -546,12 +608,17 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, action); } if (action == BLOCK_ERROR_ACTION_STOP) { - if (!job->job.user_paused) { - job_pause(&job->job); - /* make the pause user visible, which will be resumed from QMP. */ - job->job.user_paused = true; + WITH_JOB_LOCK_GUARD() { + if (!job->job.user_paused) { + job_pause_locked(&job->job); + /* + * make the pause user visible, which will be + * resumed from QMP. + */ + job->job.user_paused = true; + } + block_job_iostatus_set_err_locked(job, error); } - block_job_iostatus_set_err(job, error); } return action; } diff --git a/bsd-user/arm/signal.c b/bsd-user/arm/signal.c index 2b1dd745d1..9734407543 100644 --- a/bsd-user/arm/signal.c +++ b/bsd-user/arm/signal.c @@ -17,6 +17,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu.h" /* diff --git a/bsd-user/arm/target_arch.h b/bsd-user/arm/target_arch.h index 561934bbd2..d80cb85c64 100644 --- a/bsd-user/arm/target_arch.h +++ b/bsd-user/arm/target_arch.h @@ -21,6 +21,7 @@ #define TARGET_ARCH_H #include "qemu.h" +#include "target/arm/cpu-features.h" void target_cpu_set_tls(CPUARMState *env, target_ulong newtls); target_ulong target_cpu_get_tls(CPUARMState *env); diff --git a/bsd-user/arm/target_arch_cpu.c b/bsd-user/arm/target_arch_cpu.c index 02bf9149d5..fe38ae2210 100644 --- a/bsd-user/arm/target_arch_cpu.c +++ b/bsd-user/arm/target_arch_cpu.c @@ -16,6 +16,8 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ + +#include "qemu/osdep.h" #include "target_arch.h" void target_cpu_set_tls(CPUARMState *env, target_ulong newtls) diff --git a/bsd-user/arm/target_arch_elf.h b/bsd-user/arm/target_arch_elf.h index 935bce347f..b1c0fd2b32 100644 --- a/bsd-user/arm/target_arch_elf.h +++ b/bsd-user/arm/target_arch_elf.h @@ -20,7 +20,6 @@ #ifndef TARGET_ARCH_ELF_H #define TARGET_ARCH_ELF_H -#define ELF_START_MMAP 0x80000000 #define ELF_ET_DYN_LOAD_ADDR 0x500000 #define elf_check_arch(x) ((x) == EM_ARM) diff --git a/bsd-user/bsd-file.h b/bsd-user/bsd-file.h index a6bff3b8c2..6fa2c30b4d 100644 --- a/bsd-user/bsd-file.h +++ b/bsd-user/bsd-file.h @@ -22,9 +22,919 @@ #include "qemu/path.h" -extern struct iovec *lock_iovec(int type, abi_ulong target_addr, int count, - int copy); -extern void unlock_iovec(struct iovec *vec, abi_ulong target_addr, int count, - int copy); +#define LOCK_PATH(p, arg) \ +do { \ + (p) = lock_user_string(arg); \ + if ((p) == NULL) { \ + return -TARGET_EFAULT; \ + } \ +} while (0) + +#define UNLOCK_PATH(p, arg) unlock_user(p, arg, 0) + +#define LOCK_PATH2(p1, arg1, p2, arg2) \ +do { \ + (p1) = lock_user_string(arg1); \ + if ((p1) == NULL) { \ + return -TARGET_EFAULT; \ + } \ + (p2) = lock_user_string(arg2); \ + if ((p2) == NULL) { \ + unlock_user(p1, arg1, 0); \ + return -TARGET_EFAULT; \ + } \ +} while (0) + +#define UNLOCK_PATH2(p1, arg1, p2, arg2) \ +do { \ + unlock_user(p2, arg2, 0); \ + unlock_user(p1, arg1, 0); \ +} while (0) + +struct iovec *lock_iovec(int type, abi_ulong target_addr, int count, int copy); +void unlock_iovec(struct iovec *vec, abi_ulong target_addr, int count, int copy); + +int safe_open(const char *path, int flags, mode_t mode); +int safe_openat(int fd, const char *path, int flags, mode_t mode); + +ssize_t safe_read(int fd, void *buf, size_t nbytes); +ssize_t safe_pread(int fd, void *buf, size_t nbytes, off_t offset); +ssize_t safe_readv(int fd, const struct iovec *iov, int iovcnt); +ssize_t safe_preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset); + +ssize_t safe_write(int fd, void *buf, size_t nbytes); +ssize_t safe_pwrite(int fd, void *buf, size_t nbytes, off_t offset); +ssize_t safe_writev(int fd, const struct iovec *iov, int iovcnt); +ssize_t safe_pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset); + +/* read(2) */ +static abi_long do_bsd_read(abi_long arg1, abi_long arg2, abi_long arg3) +{ + abi_long ret; + void *p; + + p = lock_user(VERIFY_WRITE, arg2, arg3, 0); + if (p == NULL) { + return -TARGET_EFAULT; + } + ret = get_errno(safe_read(arg1, p, arg3)); + unlock_user(p, arg2, ret); + + return ret; +} + +/* pread(2) */ +static abi_long do_bsd_pread(void *cpu_env, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) +{ + abi_long ret; + void *p; + + p = lock_user(VERIFY_WRITE, arg2, arg3, 0); + if (p == NULL) { + return -TARGET_EFAULT; + } + if (regpairs_aligned(cpu_env) != 0) { + arg4 = arg5; + arg5 = arg6; + } + ret = get_errno(safe_pread(arg1, p, arg3, target_arg64(arg4, arg5))); + unlock_user(p, arg2, ret); + + return ret; +} + +/* readv(2) */ +static abi_long do_bsd_readv(abi_long arg1, abi_long arg2, abi_long arg3) +{ + abi_long ret; + struct iovec *vec = lock_iovec(VERIFY_WRITE, arg2, arg3, 0); + + if (vec != NULL) { + ret = get_errno(safe_readv(arg1, vec, arg3)); + unlock_iovec(vec, arg2, arg3, 1); + } else { + ret = -host_to_target_errno(errno); + } + + return ret; +} + +/* preadv(2) */ +static abi_long do_bsd_preadv(void *cpu_env, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) +{ + abi_long ret; + struct iovec *vec = lock_iovec(VERIFY_WRITE, arg2, arg3, 1); + + if (vec != NULL) { + if (regpairs_aligned(cpu_env) != 0) { + arg4 = arg5; + arg5 = arg6; + } + ret = get_errno(safe_preadv(arg1, vec, arg3, target_arg64(arg4, arg5))); + unlock_iovec(vec, arg2, arg3, 0); + } else { + ret = -host_to_target_errno(errno); + } + + return ret; +} + +/* write(2) */ +static abi_long do_bsd_write(abi_long arg1, abi_long arg2, abi_long arg3) +{ + abi_long nbytes, ret; + void *p; + + /* nbytes < 0 implies that it was larger than SIZE_MAX. */ + nbytes = arg3; + if (nbytes < 0) { + return -TARGET_EINVAL; + } + p = lock_user(VERIFY_READ, arg2, nbytes, 1); + if (p == NULL) { + return -TARGET_EFAULT; + } + ret = get_errno(safe_write(arg1, p, arg3)); + unlock_user(p, arg2, 0); + + return ret; +} + +/* pwrite(2) */ +static abi_long do_bsd_pwrite(void *cpu_env, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) +{ + abi_long ret; + void *p; + + p = lock_user(VERIFY_READ, arg2, arg3, 1); + if (p == NULL) { + return -TARGET_EFAULT; + } + if (regpairs_aligned(cpu_env) != 0) { + arg4 = arg5; + arg5 = arg6; + } + ret = get_errno(safe_pwrite(arg1, p, arg3, target_arg64(arg4, arg5))); + unlock_user(p, arg2, 0); + + return ret; +} + +/* writev(2) */ +static abi_long do_bsd_writev(abi_long arg1, abi_long arg2, abi_long arg3) +{ + abi_long ret; + struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1); + + if (vec != NULL) { + ret = get_errno(safe_writev(arg1, vec, arg3)); + unlock_iovec(vec, arg2, arg3, 0); + } else { + ret = -host_to_target_errno(errno); + } + + return ret; +} + +/* pwritev(2) */ +static abi_long do_bsd_pwritev(void *cpu_env, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) +{ + abi_long ret; + struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1); + + if (vec != NULL) { + if (regpairs_aligned(cpu_env) != 0) { + arg4 = arg5; + arg5 = arg6; + } + ret = get_errno(safe_pwritev(arg1, vec, arg3, target_arg64(arg4, arg5))); + unlock_iovec(vec, arg2, arg3, 0); + } else { + ret = -host_to_target_errno(errno); + } + + return ret; +} + +/* open(2) */ +static abi_long do_bsd_open(abi_long arg1, abi_long arg2, abi_long arg3) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(safe_open(path(p), target_to_host_bitmask(arg2, + fcntl_flags_tbl), arg3)); + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* openat(2) */ +static abi_long do_bsd_openat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(safe_openat(arg1, path(p), + target_to_host_bitmask(arg3, fcntl_flags_tbl), arg4)); + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* close(2) */ +static abi_long do_bsd_close(abi_long arg1) +{ + return get_errno(close(arg1)); +} + +/* fdatasync(2) */ +static abi_long do_bsd_fdatasync(abi_long arg1) +{ + return get_errno(fdatasync(arg1)); +} + +/* fsync(2) */ +static abi_long do_bsd_fsync(abi_long arg1) +{ + return get_errno(fsync(arg1)); +} + +/* closefrom(2) */ +static abi_long do_bsd_closefrom(abi_long arg1) +{ + closefrom(arg1); /* returns void */ + return get_errno(0); +} + +/* revoke(2) */ +static abi_long do_bsd_revoke(abi_long arg1) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(revoke(p)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* access(2) */ +static abi_long do_bsd_access(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(access(path(p), arg2)); + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* eaccess(2) */ +static abi_long do_bsd_eaccess(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(eaccess(path(p), arg2)); + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* faccessat(2) */ +static abi_long do_bsd_faccessat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(faccessat(arg1, p, arg3, arg4)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* chdir(2) */ +static abi_long do_bsd_chdir(abi_long arg1) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(chdir(p)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* fchdir(2) */ +static abi_long do_bsd_fchdir(abi_long arg1) +{ + return get_errno(fchdir(arg1)); +} + +/* rename(2) */ +static abi_long do_bsd_rename(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH2(p1, arg1, p2, arg2); + ret = get_errno(rename(p1, p2)); /* XXX path(p1), path(p2) */ + UNLOCK_PATH2(p1, arg1, p2, arg2); + + return ret; +} + +/* renameat(2) */ +static abi_long do_bsd_renameat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH2(p1, arg2, p2, arg4); + ret = get_errno(renameat(arg1, p1, arg3, p2)); + UNLOCK_PATH2(p1, arg2, p2, arg4); + + return ret; +} + +/* link(2) */ +static abi_long do_bsd_link(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH2(p1, arg1, p2, arg2); + ret = get_errno(link(p1, p2)); /* XXX path(p1), path(p2) */ + UNLOCK_PATH2(p1, arg1, p2, arg2); + + return ret; +} + +/* linkat(2) */ +static abi_long do_bsd_linkat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH2(p1, arg2, p2, arg4); + ret = get_errno(linkat(arg1, p1, arg3, p2, arg5)); + UNLOCK_PATH2(p1, arg2, p2, arg4); + + return ret; +} + +/* unlink(2) */ +static abi_long do_bsd_unlink(abi_long arg1) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(unlink(p)); /* XXX path(p) */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* unlinkat(2) */ +static abi_long do_bsd_unlinkat(abi_long arg1, abi_long arg2, + abi_long arg3) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(unlinkat(arg1, p, arg3)); /* XXX path(p) */ + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* mkdir(2) */ +static abi_long do_bsd_mkdir(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(mkdir(p, arg2)); /* XXX path(p) */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* mkdirat(2) */ +static abi_long do_bsd_mkdirat(abi_long arg1, abi_long arg2, + abi_long arg3) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(mkdirat(arg1, p, arg3)); + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* rmdir(2) */ +static abi_long do_bsd_rmdir(abi_long arg1) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(rmdir(p)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* undocumented __getcwd(char *buf, size_t len) system call */ +static abi_long do_bsd___getcwd(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + p = lock_user(VERIFY_WRITE, arg1, arg2, 0); + if (p == NULL) { + return -TARGET_EFAULT; + } + ret = safe_syscall(SYS___getcwd, p, arg2); + unlock_user(p, arg1, ret == 0 ? strlen(p) + 1 : 0); + + return get_errno(ret); +} + +/* dup(2) */ +static abi_long do_bsd_dup(abi_long arg1) +{ + return get_errno(dup(arg1)); +} + +/* dup2(2) */ +static abi_long do_bsd_dup2(abi_long arg1, abi_long arg2) +{ + return get_errno(dup2(arg1, arg2)); +} + +/* truncate(2) */ +static abi_long do_bsd_truncate(void *cpu_env, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + if (regpairs_aligned(cpu_env) != 0) { + arg2 = arg3; + arg3 = arg4; + } + ret = get_errno(truncate(p, target_arg64(arg2, arg3))); + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* ftruncate(2) */ +static abi_long do_bsd_ftruncate(void *cpu_env, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4) +{ + if (regpairs_aligned(cpu_env) != 0) { + arg2 = arg3; + arg3 = arg4; + } + return get_errno(ftruncate(arg1, target_arg64(arg2, arg3))); +} + +/* acct(2) */ +static abi_long do_bsd_acct(abi_long arg1) +{ + abi_long ret; + void *p; + + if (arg1 == 0) { + ret = get_errno(acct(NULL)); + } else { + LOCK_PATH(p, arg1); + ret = get_errno(acct(path(p))); + UNLOCK_PATH(p, arg1); + } + return ret; +} + +/* sync(2) */ +static abi_long do_bsd_sync(void) +{ + sync(); + return 0; +} + +/* mount(2) */ +static abi_long do_bsd_mount(abi_long arg1, abi_long arg2, abi_long arg3, + abi_long arg4) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH2(p1, arg1, p2, arg2); + /* + * XXX arg4 should be locked, but it isn't clear how to do that since it may + * be not be a NULL-terminated string. + */ + if (arg4 == 0) { + ret = get_errno(mount(p1, p2, arg3, NULL)); /* XXX path(p2)? */ + } else { + ret = get_errno(mount(p1, p2, arg3, g2h_untagged(arg4))); /* XXX path(p2)? */ + } + UNLOCK_PATH2(p1, arg1, p2, arg2); + + return ret; +} + +/* unmount(2) */ +static abi_long do_bsd_unmount(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(unmount(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* nmount(2) */ +static abi_long do_bsd_nmount(abi_long arg1, abi_long count, + abi_long flags) +{ + abi_long ret; + struct iovec *vec = lock_iovec(VERIFY_READ, arg1, count, 1); + + if (vec != NULL) { + ret = get_errno(nmount(vec, count, flags)); + unlock_iovec(vec, arg1, count, 0); + } else { + return -TARGET_EFAULT; + } + + return ret; +} + +/* symlink(2) */ +static abi_long do_bsd_symlink(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH2(p1, arg1, p2, arg2); + ret = get_errno(symlink(p1, p2)); /* XXX path(p1), path(p2) */ + UNLOCK_PATH2(p1, arg1, p2, arg2); + + return ret; +} + +/* symlinkat(2) */ +static abi_long do_bsd_symlinkat(abi_long arg1, abi_long arg2, + abi_long arg3) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH2(p1, arg1, p2, arg3); + ret = get_errno(symlinkat(p1, arg2, p2)); /* XXX path(p1), path(p2) */ + UNLOCK_PATH2(p1, arg1, p2, arg3); + + return ret; +} + +/* readlink(2) */ +static abi_long do_bsd_readlink(CPUArchState *env, abi_long arg1, + abi_long arg2, abi_long arg3) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH(p1, arg1); + p2 = lock_user(VERIFY_WRITE, arg2, arg3, 0); + if (p2 == NULL) { + UNLOCK_PATH(p1, arg1); + return -TARGET_EFAULT; + } + if (strcmp(p1, "/proc/curproc/file") == 0) { + CPUState *cpu = env_cpu(env); + TaskState *ts = get_task_state(cpu); + strncpy(p2, ts->bprm->fullpath, arg3); + ret = MIN((abi_long)strlen(ts->bprm->fullpath), arg3); + } else { + ret = get_errno(readlink(path(p1), p2, arg3)); + } + unlock_user(p2, arg2, ret); + UNLOCK_PATH(p1, arg1); + + return ret; +} + +/* readlinkat(2) */ +static abi_long do_bsd_readlinkat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p1, *p2; + + LOCK_PATH(p1, arg2); + p2 = lock_user(VERIFY_WRITE, arg3, arg4, 0); + if (p2 == NULL) { + UNLOCK_PATH(p1, arg2); + return -TARGET_EFAULT; + } + ret = get_errno(readlinkat(arg1, p1, p2, arg4)); + unlock_user(p2, arg3, ret); + UNLOCK_PATH(p1, arg2); + + return ret; +} + +/* chmod(2) */ +static abi_long do_bsd_chmod(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(chmod(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* fchmod(2) */ +static abi_long do_bsd_fchmod(abi_long arg1, abi_long arg2) +{ + return get_errno(fchmod(arg1, arg2)); +} + +/* lchmod(2) */ +static abi_long do_bsd_lchmod(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(lchmod(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* fchmodat(2) */ +static abi_long do_bsd_fchmodat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(fchmodat(arg1, p, arg3, arg4)); + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* pre-ino64 mknod(2) */ +static abi_long do_bsd_freebsd11_mknod(abi_long arg1, abi_long arg2, abi_long arg3) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(syscall(SYS_freebsd11_mknod, p, arg2, arg3)); + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* pre-ino64 mknodat(2) */ +static abi_long do_bsd_freebsd11_mknodat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(syscall(SYS_freebsd11_mknodat, arg1, p, arg3, arg4)); + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* post-ino64 mknodat(2) */ +static abi_long do_bsd_mknodat(void *cpu_env, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, + abi_long arg6) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + /* 32-bit arch's use two 32 registers for 64 bit return value */ + if (regpairs_aligned(cpu_env) != 0) { + ret = get_errno(mknodat(arg1, p, arg3, target_arg64(arg5, arg6))); + } else { + ret = get_errno(mknodat(arg1, p, arg3, target_arg64(arg4, arg5))); + } + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* chown(2) */ +static abi_long do_bsd_chown(abi_long arg1, abi_long arg2, abi_long arg3) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(chown(p, arg2, arg3)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* fchown(2) */ +static abi_long do_bsd_fchown(abi_long arg1, abi_long arg2, + abi_long arg3) +{ + return get_errno(fchown(arg1, arg2, arg3)); +} + +/* lchown(2) */ +static abi_long do_bsd_lchown(abi_long arg1, abi_long arg2, + abi_long arg3) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(lchown(p, arg2, arg3)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* fchownat(2) */ +static abi_long do_bsd_fchownat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(fchownat(arg1, p, arg3, arg4, arg5)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* chflags(2) */ +static abi_long do_bsd_chflags(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(chflags(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* lchflags(2) */ +static abi_long do_bsd_lchflags(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(lchflags(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* fchflags(2) */ +static abi_long do_bsd_fchflags(abi_long arg1, abi_long arg2) +{ + return get_errno(fchflags(arg1, arg2)); +} + +/* chroot(2) */ +static abi_long do_bsd_chroot(abi_long arg1) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(chroot(p)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* flock(2) */ +static abi_long do_bsd_flock(abi_long arg1, abi_long arg2) +{ + return get_errno(flock(arg1, arg2)); +} + +/* mkfifo(2) */ +static abi_long do_bsd_mkfifo(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(mkfifo(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* mkfifoat(2) */ +static abi_long do_bsd_mkfifoat(abi_long arg1, abi_long arg2, + abi_long arg3) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg2); + ret = get_errno(mkfifoat(arg1, p, arg3)); + UNLOCK_PATH(p, arg2); + + return ret; +} + +/* pathconf(2) */ +static abi_long do_bsd_pathconf(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(pathconf(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* lpathconf(2) */ +static abi_long do_bsd_lpathconf(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(lpathconf(p, arg2)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} + +/* fpathconf(2) */ +static abi_long do_bsd_fpathconf(abi_long arg1, abi_long arg2) +{ + return get_errno(fpathconf(arg1, arg2)); +} + +/* undelete(2) */ +static abi_long do_bsd_undelete(abi_long arg1) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, arg1); + ret = get_errno(undelete(p)); /* XXX path(p)? */ + UNLOCK_PATH(p, arg1); + + return ret; +} #endif /* BSD_FILE_H */ diff --git a/bsd-user/bsd-mem.c b/bsd-user/bsd-mem.c new file mode 100644 index 0000000000..2ab1334b70 --- /dev/null +++ b/bsd-user/bsd-mem.c @@ -0,0 +1,104 @@ +/* + * memory management system conversion routines + * + * Copyright (c) 2013 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "qemu-bsd.h" + +struct bsd_shm_regions bsd_shm_regions[N_BSD_SHM_REGIONS]; + +abi_ulong target_brk; +abi_ulong initial_target_brk; + +void target_set_brk(abi_ulong new_brk) +{ + target_brk = TARGET_PAGE_ALIGN(new_brk); + initial_target_brk = target_brk; +} + +void target_to_host_ipc_perm__locked(struct ipc_perm *host_ip, + struct target_ipc_perm *target_ip) +{ + __get_user(host_ip->cuid, &target_ip->cuid); + __get_user(host_ip->cgid, &target_ip->cgid); + __get_user(host_ip->uid, &target_ip->uid); + __get_user(host_ip->gid, &target_ip->gid); + __get_user(host_ip->mode, &target_ip->mode); + __get_user(host_ip->seq, &target_ip->seq); + __get_user(host_ip->key, &target_ip->key); +} + +abi_long target_to_host_shmid_ds(struct shmid_ds *host_sd, + abi_ulong target_addr) +{ + struct target_shmid_ds *target_sd; + + if (!lock_user_struct(VERIFY_READ, target_sd, target_addr, 1)) { + return -TARGET_EFAULT; + } + + target_to_host_ipc_perm__locked(&(host_sd->shm_perm), + &(target_sd->shm_perm)); + + __get_user(host_sd->shm_segsz, &target_sd->shm_segsz); + __get_user(host_sd->shm_lpid, &target_sd->shm_lpid); + __get_user(host_sd->shm_cpid, &target_sd->shm_cpid); + __get_user(host_sd->shm_nattch, &target_sd->shm_nattch); + __get_user(host_sd->shm_atime, &target_sd->shm_atime); + __get_user(host_sd->shm_dtime, &target_sd->shm_dtime); + __get_user(host_sd->shm_ctime, &target_sd->shm_ctime); + unlock_user_struct(target_sd, target_addr, 0); + + return 0; +} + +void host_to_target_ipc_perm__locked(struct target_ipc_perm *target_ip, + struct ipc_perm *host_ip) +{ + __put_user(host_ip->cuid, &target_ip->cuid); + __put_user(host_ip->cgid, &target_ip->cgid); + __put_user(host_ip->uid, &target_ip->uid); + __put_user(host_ip->gid, &target_ip->gid); + __put_user(host_ip->mode, &target_ip->mode); + __put_user(host_ip->seq, &target_ip->seq); + __put_user(host_ip->key, &target_ip->key); +} + +abi_long host_to_target_shmid_ds(abi_ulong target_addr, + struct shmid_ds *host_sd) +{ + struct target_shmid_ds *target_sd; + + if (!lock_user_struct(VERIFY_WRITE, target_sd, target_addr, 0)) { + return -TARGET_EFAULT; + } + + host_to_target_ipc_perm__locked(&(target_sd->shm_perm), + &(host_sd->shm_perm)); + + __put_user(host_sd->shm_segsz, &target_sd->shm_segsz); + __put_user(host_sd->shm_lpid, &target_sd->shm_lpid); + __put_user(host_sd->shm_cpid, &target_sd->shm_cpid); + __put_user(host_sd->shm_nattch, &target_sd->shm_nattch); + __put_user(host_sd->shm_atime, &target_sd->shm_atime); + __put_user(host_sd->shm_dtime, &target_sd->shm_dtime); + __put_user(host_sd->shm_ctime, &target_sd->shm_ctime); + unlock_user_struct(target_sd, target_addr, 1); + + return 0; +} diff --git a/bsd-user/bsd-mem.h b/bsd-user/bsd-mem.h new file mode 100644 index 0000000000..21d9bab889 --- /dev/null +++ b/bsd-user/bsd-mem.h @@ -0,0 +1,452 @@ +/* + * memory management system call shims and definitions + * + * Copyright (c) 2013-15 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. 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. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#ifndef BSD_USER_BSD_MEM_H +#define BSD_USER_BSD_MEM_H + +#include +#include +#include +#include +#include + +#include "qemu-bsd.h" + +extern struct bsd_shm_regions bsd_shm_regions[]; +extern abi_ulong target_brk; +extern abi_ulong initial_target_brk; + +/* mmap(2) */ +static inline abi_long do_bsd_mmap(void *cpu_env, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6, abi_long arg7, + abi_long arg8) +{ + if (regpairs_aligned(cpu_env) != 0) { + arg6 = arg7; + arg7 = arg8; + } + return get_errno(target_mmap(arg1, arg2, arg3, + target_to_host_bitmask(arg4, mmap_flags_tbl), + arg5, target_arg64(arg6, arg7))); +} + +/* munmap(2) */ +static inline abi_long do_bsd_munmap(abi_long arg1, abi_long arg2) +{ + return get_errno(target_munmap(arg1, arg2)); +} + +/* mprotect(2) */ +static inline abi_long do_bsd_mprotect(abi_long arg1, abi_long arg2, + abi_long arg3) +{ + return get_errno(target_mprotect(arg1, arg2, arg3)); +} + +/* msync(2) */ +static inline abi_long do_bsd_msync(abi_long addr, abi_long len, abi_long flags) +{ + if (!guest_range_valid_untagged(addr, len)) { + /* It seems odd, but POSIX wants this to be ENOMEM */ + return -TARGET_ENOMEM; + } + + return get_errno(msync(g2h_untagged(addr), len, flags)); +} + +/* mlock(2) */ +static inline abi_long do_bsd_mlock(abi_long arg1, abi_long arg2) +{ + if (!guest_range_valid_untagged(arg1, arg2)) { + return -TARGET_EINVAL; + } + return get_errno(mlock(g2h_untagged(arg1), arg2)); +} + +/* munlock(2) */ +static inline abi_long do_bsd_munlock(abi_long arg1, abi_long arg2) +{ + if (!guest_range_valid_untagged(arg1, arg2)) { + return -TARGET_EINVAL; + } + return get_errno(munlock(g2h_untagged(arg1), arg2)); +} + +/* mlockall(2) */ +static inline abi_long do_bsd_mlockall(abi_long arg1) +{ + return get_errno(mlockall(arg1)); +} + +/* munlockall(2) */ +static inline abi_long do_bsd_munlockall(void) +{ + return get_errno(munlockall()); +} + +/* madvise(2) */ +static inline abi_long do_bsd_madvise(abi_long arg1, abi_long arg2, + abi_long arg3) +{ + abi_ulong len; + int ret = 0; + abi_long start = arg1; + abi_long len_in = arg2; + abi_long advice = arg3; + + if (start & ~TARGET_PAGE_MASK) { + return -TARGET_EINVAL; + } + if (len_in == 0) { + return 0; + } + len = TARGET_PAGE_ALIGN(len_in); + if (len == 0 || !guest_range_valid_untagged(start, len)) { + return -TARGET_EINVAL; + } + + /* + * Most advice values are hints, so ignoring and returning success is ok. + * + * However, some advice values such as MADV_DONTNEED, are not hints and + * need to be emulated. + * + * A straight passthrough for those may not be safe because qemu sometimes + * turns private file-backed mappings into anonymous mappings. + * If all guest pages have PAGE_PASSTHROUGH set, mappings have the + * same semantics for the host as for the guest. + * + * MADV_DONTNEED is passed through, if possible. + * If passthrough isn't possible, we nevertheless (wrongly!) return + * success, which is broken but some userspace programs fail to work + * otherwise. Completely implementing such emulation is quite complicated + * though. + */ + mmap_lock(); + switch (advice) { + case MADV_DONTNEED: + if (page_check_range(start, len, PAGE_PASSTHROUGH)) { + ret = get_errno(madvise(g2h_untagged(start), len, advice)); + if (ret == 0) { + page_reset_target_data(start, start + len - 1); + } + } + } + mmap_unlock(); + + return ret; +} + +/* minherit(2) */ +static inline abi_long do_bsd_minherit(abi_long addr, abi_long len, + abi_long inherit) +{ + return get_errno(minherit(g2h_untagged(addr), len, inherit)); +} + +/* mincore(2) */ +static inline abi_long do_bsd_mincore(abi_ulong target_addr, abi_ulong len, + abi_ulong target_vec) +{ + abi_long ret; + void *p; + abi_ulong vec_len = DIV_ROUND_UP(len, TARGET_PAGE_SIZE); + + if (!guest_range_valid_untagged(target_addr, len) + || !page_check_range(target_addr, len, PAGE_VALID)) { + return -TARGET_EFAULT; + } + + p = lock_user(VERIFY_WRITE, target_vec, vec_len, 0); + if (p == NULL) { + return -TARGET_EFAULT; + } + ret = get_errno(mincore(g2h_untagged(target_addr), len, p)); + unlock_user(p, target_vec, vec_len); + + return ret; +} + +/* do_brk() must return target values and target errnos. */ +static inline abi_long do_obreak(abi_ulong brk_val) +{ + abi_long mapped_addr; + abi_ulong new_brk; + abi_ulong old_brk; + + /* brk pointers are always untagged */ + + /* do not allow to shrink below initial brk value */ + if (brk_val < initial_target_brk) { + return target_brk; + } + + new_brk = TARGET_PAGE_ALIGN(brk_val); + old_brk = TARGET_PAGE_ALIGN(target_brk); + + /* new and old target_brk might be on the same page */ + if (new_brk == old_brk) { + target_brk = brk_val; + return target_brk; + } + + /* Release heap if necessary */ + if (new_brk < old_brk) { + target_munmap(new_brk, old_brk - new_brk); + + target_brk = brk_val; + return target_brk; + } + + mapped_addr = target_mmap(old_brk, new_brk - old_brk, + PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_EXCL | MAP_ANON | MAP_PRIVATE, + -1, 0); + + if (mapped_addr == old_brk) { + target_brk = brk_val; + return target_brk; + } + + /* For everything else, return the previous break. */ + return target_brk; +} + +/* shm_open(2) */ +static inline abi_long do_bsd_shm_open(abi_ulong arg1, abi_long arg2, + abi_long arg3) +{ + int ret; + void *p; + + if (arg1 == (uintptr_t)SHM_ANON) { + p = SHM_ANON; + } else { + p = lock_user_string(arg1); + if (p == NULL) { + return -TARGET_EFAULT; + } + } + ret = get_errno(shm_open(p, target_to_host_bitmask(arg2, fcntl_flags_tbl), + arg3)); + + if (p != SHM_ANON) { + unlock_user(p, arg1, 0); + } + + return ret; +} + +/* shm_unlink(2) */ +static inline abi_long do_bsd_shm_unlink(abi_ulong arg1) +{ + int ret; + void *p; + + p = lock_user_string(arg1); + if (p == NULL) { + return -TARGET_EFAULT; + } + ret = get_errno(shm_unlink(p)); /* XXX path(p)? */ + unlock_user(p, arg1, 0); + + return ret; +} + +/* shmget(2) */ +static inline abi_long do_bsd_shmget(abi_long arg1, abi_ulong arg2, + abi_long arg3) +{ + return get_errno(shmget(arg1, arg2, arg3)); +} + +/* shmctl(2) */ +static inline abi_long do_bsd_shmctl(abi_long shmid, abi_long cmd, + abi_ulong buff) +{ + struct shmid_ds dsarg; + abi_long ret = -TARGET_EINVAL; + + cmd &= 0xff; + + switch (cmd) { + case IPC_STAT: + if (target_to_host_shmid_ds(&dsarg, buff)) { + return -TARGET_EFAULT; + } + ret = get_errno(shmctl(shmid, cmd, &dsarg)); + if (host_to_target_shmid_ds(buff, &dsarg)) { + return -TARGET_EFAULT; + } + break; + + case IPC_SET: + if (target_to_host_shmid_ds(&dsarg, buff)) { + return -TARGET_EFAULT; + } + ret = get_errno(shmctl(shmid, cmd, &dsarg)); + break; + + case IPC_RMID: + ret = get_errno(shmctl(shmid, cmd, NULL)); + break; + + default: + ret = -TARGET_EINVAL; + break; + } + + return ret; +} + +/* shmat(2) */ +static inline abi_long do_bsd_shmat(int shmid, abi_ulong shmaddr, int shmflg) +{ + abi_ulong raddr; + abi_long ret; + struct shmid_ds shm_info; + + /* Find out the length of the shared memory segment. */ + ret = get_errno(shmctl(shmid, IPC_STAT, &shm_info)); + if (is_error(ret)) { + /* Can't get the length */ + return ret; + } + + if (!guest_range_valid_untagged(shmaddr, shm_info.shm_segsz)) { + return -TARGET_EINVAL; + } + + WITH_MMAP_LOCK_GUARD() { + void *host_raddr; + + if (shmaddr) { + host_raddr = shmat(shmid, (void *)g2h_untagged(shmaddr), shmflg); + } else { + abi_ulong mmap_start; + + mmap_start = mmap_find_vma(0, shm_info.shm_segsz); + + if (mmap_start == -1) { + return -TARGET_ENOMEM; + } + host_raddr = shmat(shmid, g2h_untagged(mmap_start), + shmflg | SHM_REMAP); + } + + if (host_raddr == (void *)-1) { + return get_errno(-1); + } + raddr = h2g(host_raddr); + + page_set_flags(raddr, raddr + shm_info.shm_segsz - 1, + PAGE_VALID | PAGE_RESET | PAGE_READ | + (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE)); + + for (int i = 0; i < N_BSD_SHM_REGIONS; i++) { + if (bsd_shm_regions[i].start == 0) { + bsd_shm_regions[i].start = raddr; + bsd_shm_regions[i].size = shm_info.shm_segsz; + break; + } + } + } + + return raddr; +} + +/* shmdt(2) */ +static inline abi_long do_bsd_shmdt(abi_ulong shmaddr) +{ + abi_long ret; + + WITH_MMAP_LOCK_GUARD() { + int i; + + for (i = 0; i < N_BSD_SHM_REGIONS; ++i) { + if (bsd_shm_regions[i].start == shmaddr) { + break; + } + } + + if (i == N_BSD_SHM_REGIONS) { + return -TARGET_EINVAL; + } + + ret = get_errno(shmdt(g2h_untagged(shmaddr))); + if (ret == 0) { + abi_ulong size = bsd_shm_regions[i].size; + + bsd_shm_regions[i].start = 0; + page_set_flags(shmaddr, shmaddr + size - 1, 0); + mmap_reserve(shmaddr, size); + } + } + + return ret; +} + +static inline abi_long do_bsd_vadvise(void) +{ + /* See sys_ovadvise() in vm_unix.c */ + return -TARGET_EINVAL; +} + +static inline abi_long do_bsd_sbrk(void) +{ + /* see sys_sbrk() in vm_mmap.c */ + return -TARGET_EOPNOTSUPP; +} + +static inline abi_long do_bsd_sstk(void) +{ + /* see sys_sstk() in vm_mmap.c */ + return -TARGET_EOPNOTSUPP; +} + +#endif /* BSD_USER_BSD_MEM_H */ diff --git a/bsd-user/bsd-proc.c b/bsd-user/bsd-proc.c new file mode 100644 index 0000000000..ca3c1bf94f --- /dev/null +++ b/bsd-user/bsd-proc.c @@ -0,0 +1,145 @@ +/* + * BSD process related system call helpers + * + * Copyright (c) 2013-14 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#include "qemu/osdep.h" + +#include +#include +#include +#include +#include + +#include "qemu.h" +#include "qemu-bsd.h" +#include "signal-common.h" + +#include "bsd-proc.h" + +/* + * resource/rusage conversion + */ +int target_to_host_resource(int code) +{ + return code; +} + +rlim_t target_to_host_rlim(abi_llong target_rlim) +{ + return tswap64(target_rlim); +} + +abi_llong host_to_target_rlim(rlim_t rlim) +{ + return tswap64(rlim); +} + +void h2g_rusage(const struct rusage *rusage, + struct target_freebsd_rusage *target_rusage) +{ + __put_user(rusage->ru_utime.tv_sec, &target_rusage->ru_utime.tv_sec); + __put_user(rusage->ru_utime.tv_usec, &target_rusage->ru_utime.tv_usec); + + __put_user(rusage->ru_stime.tv_sec, &target_rusage->ru_stime.tv_sec); + __put_user(rusage->ru_stime.tv_usec, &target_rusage->ru_stime.tv_usec); + + __put_user(rusage->ru_maxrss, &target_rusage->ru_maxrss); + __put_user(rusage->ru_idrss, &target_rusage->ru_idrss); + __put_user(rusage->ru_idrss, &target_rusage->ru_idrss); + __put_user(rusage->ru_isrss, &target_rusage->ru_isrss); + __put_user(rusage->ru_minflt, &target_rusage->ru_minflt); + __put_user(rusage->ru_majflt, &target_rusage->ru_majflt); + __put_user(rusage->ru_nswap, &target_rusage->ru_nswap); + __put_user(rusage->ru_inblock, &target_rusage->ru_inblock); + __put_user(rusage->ru_oublock, &target_rusage->ru_oublock); + __put_user(rusage->ru_msgsnd, &target_rusage->ru_msgsnd); + __put_user(rusage->ru_msgrcv, &target_rusage->ru_msgrcv); + __put_user(rusage->ru_nsignals, &target_rusage->ru_nsignals); + __put_user(rusage->ru_nvcsw, &target_rusage->ru_nvcsw); + __put_user(rusage->ru_nivcsw, &target_rusage->ru_nivcsw); +} + +abi_long host_to_target_rusage(abi_ulong target_addr, + const struct rusage *rusage) +{ + struct target_freebsd_rusage *target_rusage; + + if (!lock_user_struct(VERIFY_WRITE, target_rusage, target_addr, 0)) { + return -TARGET_EFAULT; + } + h2g_rusage(rusage, target_rusage); + unlock_user_struct(target_rusage, target_addr, 1); + + return 0; +} + +abi_long host_to_target_wrusage(abi_ulong target_addr, + const struct __wrusage *wrusage) +{ + struct target_freebsd__wrusage *target_wrusage; + + if (!lock_user_struct(VERIFY_WRITE, target_wrusage, target_addr, 0)) { + return -TARGET_EFAULT; + } + h2g_rusage(&wrusage->wru_self, &target_wrusage->wru_self); + h2g_rusage(&wrusage->wru_children, &target_wrusage->wru_children); + unlock_user_struct(target_wrusage, target_addr, 1); + + return 0; +} + +/* + * wait status conversion. + * + * Map host to target signal numbers for the wait family of syscalls. + * Assume all other status bits are the same. + */ +int host_to_target_waitstatus(int status) +{ + if (WIFSIGNALED(status)) { + return host_to_target_signal(WTERMSIG(status)) | (status & ~0x7f); + } + if (WIFSTOPPED(status)) { + return (host_to_target_signal(WSTOPSIG(status)) << 8) | (status & 0xff); + } + return status; +} + +int bsd_get_ncpu(void) +{ + int ncpu = -1; + cpuset_t mask; + + CPU_ZERO(&mask); + + if (cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(mask), + &mask) == 0) { + ncpu = CPU_COUNT(&mask); + } + + if (ncpu == -1) { + ncpu = sysconf(_SC_NPROCESSORS_ONLN); + } + + if (ncpu == -1) { + gemu_log("XXX Missing bsd_get_ncpu() implementation\n"); + ncpu = 1; + } + + return ncpu; +} + diff --git a/bsd-user/bsd-proc.h b/bsd-user/bsd-proc.h new file mode 100644 index 0000000000..8b1c2deea3 --- /dev/null +++ b/bsd-user/bsd-proc.h @@ -0,0 +1,414 @@ +/* + * process related system call shims and definitions + * + * Copyright (c) 2013-2014 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef BSD_PROC_H_ +#define BSD_PROC_H_ + +#include + +#include "qemu-bsd.h" +#include "gdbstub/syscalls.h" +#include "qemu/plugin.h" + +extern int _getlogin(char*, int); +int bsd_get_ncpu(void); + +/* exit(2) */ +static inline abi_long do_bsd_exit(void *cpu_env, abi_long arg1) +{ + gdb_exit(arg1); + qemu_plugin_user_exit(); + _exit(arg1); + + return 0; +} + +/* getgroups(2) */ +static inline abi_long do_bsd_getgroups(abi_long gidsetsize, abi_long arg2) +{ + abi_long ret; + uint32_t *target_grouplist; + g_autofree gid_t *grouplist; + int i; + + grouplist = g_try_new(gid_t, gidsetsize); + ret = get_errno(getgroups(gidsetsize, grouplist)); + if (gidsetsize != 0) { + if (!is_error(ret)) { + target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * 2, 0); + if (!target_grouplist) { + return -TARGET_EFAULT; + } + for (i = 0; i < ret; i++) { + target_grouplist[i] = tswap32(grouplist[i]); + } + unlock_user(target_grouplist, arg2, gidsetsize * 2); + } + } + return ret; +} + +/* setgroups(2) */ +static inline abi_long do_bsd_setgroups(abi_long gidsetsize, abi_long arg2) +{ + uint32_t *target_grouplist; + g_autofree gid_t *grouplist; + int i; + + grouplist = g_try_new(gid_t, gidsetsize); + target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * 2, 1); + if (!target_grouplist) { + return -TARGET_EFAULT; + } + for (i = 0; i < gidsetsize; i++) { + grouplist[i] = tswap32(target_grouplist[i]); + } + unlock_user(target_grouplist, arg2, 0); + return get_errno(setgroups(gidsetsize, grouplist)); +} + +/* umask(2) */ +static inline abi_long do_bsd_umask(abi_long arg1) +{ + return get_errno(umask(arg1)); +} + +/* setlogin(2) */ +static inline abi_long do_bsd_setlogin(abi_long arg1) +{ + abi_long ret; + void *p; + + p = lock_user_string(arg1); + if (p == NULL) { + return -TARGET_EFAULT; + } + ret = get_errno(setlogin(p)); + unlock_user(p, arg1, 0); + + return ret; +} + +/* getlogin(2) */ +static inline abi_long do_bsd_getlogin(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + + p = lock_user(VERIFY_WRITE, arg1, arg2, 0); + if (p == NULL) { + return -TARGET_EFAULT; + } + ret = get_errno(_getlogin(p, arg2)); + unlock_user(p, arg1, arg2); + + return ret; +} + +/* getrusage(2) */ +static inline abi_long do_bsd_getrusage(abi_long who, abi_ulong target_addr) +{ + abi_long ret; + struct rusage rusage; + + ret = get_errno(getrusage(who, &rusage)); + if (!is_error(ret)) { + host_to_target_rusage(target_addr, &rusage); + } + return ret; +} + +/* getrlimit(2) */ +static inline abi_long do_bsd_getrlimit(abi_long arg1, abi_ulong arg2) +{ + abi_long ret; + int resource = target_to_host_resource(arg1); + struct target_rlimit *target_rlim; + struct rlimit rlim; + + switch (resource) { + case RLIMIT_STACK: + rlim.rlim_cur = target_dflssiz; + rlim.rlim_max = target_maxssiz; + ret = 0; + break; + + case RLIMIT_DATA: + rlim.rlim_cur = target_dfldsiz; + rlim.rlim_max = target_maxdsiz; + ret = 0; + break; + + default: + ret = get_errno(getrlimit(resource, &rlim)); + break; + } + if (!is_error(ret)) { + if (!lock_user_struct(VERIFY_WRITE, target_rlim, arg2, 0)) { + return -TARGET_EFAULT; + } + target_rlim->rlim_cur = host_to_target_rlim(rlim.rlim_cur); + target_rlim->rlim_max = host_to_target_rlim(rlim.rlim_max); + unlock_user_struct(target_rlim, arg2, 1); + } + return ret; +} + +/* setrlimit(2) */ +static inline abi_long do_bsd_setrlimit(abi_long arg1, abi_ulong arg2) +{ + abi_long ret; + int resource = target_to_host_resource(arg1); + struct target_rlimit *target_rlim; + struct rlimit rlim; + + if (RLIMIT_STACK == resource) { + /* XXX We should, maybe, allow the stack size to shrink */ + ret = -TARGET_EPERM; + } else { + if (!lock_user_struct(VERIFY_READ, target_rlim, arg2, 1)) { + return -TARGET_EFAULT; + } + rlim.rlim_cur = target_to_host_rlim(target_rlim->rlim_cur); + rlim.rlim_max = target_to_host_rlim(target_rlim->rlim_max); + unlock_user_struct(target_rlim, arg2, 0); + ret = get_errno(setrlimit(resource, &rlim)); + } + return ret; +} + +/* getpid(2) */ +static inline abi_long do_bsd_getpid(void) +{ + return get_errno(getpid()); +} + +/* getppid(2) */ +static inline abi_long do_bsd_getppid(void) +{ + return get_errno(getppid()); +} + +/* getuid(2) */ +static inline abi_long do_bsd_getuid(void) +{ + return get_errno(getuid()); +} + +/* geteuid(2) */ +static inline abi_long do_bsd_geteuid(void) +{ + return get_errno(geteuid()); +} + +/* getgid(2) */ +static inline abi_long do_bsd_getgid(void) +{ + return get_errno(getgid()); +} + +/* getegid(2) */ +static inline abi_long do_bsd_getegid(void) +{ + return get_errno(getegid()); +} + +/* setuid(2) */ +static inline abi_long do_bsd_setuid(abi_long arg1) +{ + return get_errno(setuid(arg1)); +} + +/* seteuid(2) */ +static inline abi_long do_bsd_seteuid(abi_long arg1) +{ + return get_errno(seteuid(arg1)); +} + +/* setgid(2) */ +static inline abi_long do_bsd_setgid(abi_long arg1) +{ + return get_errno(setgid(arg1)); +} + +/* setegid(2) */ +static inline abi_long do_bsd_setegid(abi_long arg1) +{ + return get_errno(setegid(arg1)); +} + +/* getpgid(2) */ +static inline abi_long do_bsd_getpgid(pid_t pid) +{ + return get_errno(getpgid(pid)); +} + +/* setpgid(2) */ +static inline abi_long do_bsd_setpgid(int pid, int pgrp) +{ + return get_errno(setpgid(pid, pgrp)); +} + +/* getpgrp(2) */ +static inline abi_long do_bsd_getpgrp(void) +{ + return get_errno(getpgrp()); +} + +/* setreuid(2) */ +static inline abi_long do_bsd_setreuid(abi_long arg1, abi_long arg2) +{ + return get_errno(setreuid(arg1, arg2)); +} + +/* setregid(2) */ +static inline abi_long do_bsd_setregid(abi_long arg1, abi_long arg2) +{ + return get_errno(setregid(arg1, arg2)); +} + +/* setresgid(2) */ +static inline abi_long do_bsd_setresgid(gid_t rgid, gid_t egid, gid_t sgid) +{ + return get_errno(setresgid(rgid, egid, sgid)); +} + +/* setresuid(2) */ +static inline abi_long do_bsd_setresuid(uid_t ruid, uid_t euid, uid_t suid) +{ + return get_errno(setresuid(ruid, euid, suid)); +} + +/* getresuid(2) */ +static inline abi_long do_bsd_getresuid(abi_ulong arg1, abi_ulong arg2, + abi_ulong arg3) +{ + abi_long ret; + uid_t ruid, euid, suid; + + ret = get_errno(getresuid(&ruid, &euid, &suid)); + if (is_error(ret)) { + return ret; + } + if (put_user_s32(ruid, arg1)) { + return -TARGET_EFAULT; + } + if (put_user_s32(euid, arg2)) { + return -TARGET_EFAULT; + } + if (put_user_s32(suid, arg3)) { + return -TARGET_EFAULT; + } + return ret; +} + +/* getresgid(2) */ +static inline abi_long do_bsd_getresgid(abi_ulong arg1, abi_ulong arg2, + abi_ulong arg3) +{ + abi_long ret; + uid_t ruid, euid, suid; + + ret = get_errno(getresgid(&ruid, &euid, &suid)); + if (is_error(ret)) { + return ret; + } + if (put_user_s32(ruid, arg1)) { + return -TARGET_EFAULT; + } + if (put_user_s32(euid, arg2)) { + return -TARGET_EFAULT; + } + if (put_user_s32(suid, arg3)) { + return -TARGET_EFAULT; + } + return ret; +} + +/* getsid(2) */ +static inline abi_long do_bsd_getsid(abi_long arg1) +{ + return get_errno(getsid(arg1)); +} + +/* setsid(2) */ +static inline abi_long do_bsd_setsid(void) +{ + return get_errno(setsid()); +} + +/* issetugid(2) */ +static inline abi_long do_bsd_issetugid(void) +{ + return get_errno(issetugid()); +} + +/* profil(2) */ +static inline abi_long do_bsd_profil(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + return -TARGET_ENOSYS; +} + +/* ktrace(2) */ +static inline abi_long do_bsd_ktrace(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + return -TARGET_ENOSYS; +} + +/* utrace(2) */ +static inline abi_long do_bsd_utrace(abi_long arg1, abi_long arg2) +{ + return -TARGET_ENOSYS; +} + + +/* ptrace(2) */ +static inline abi_long do_bsd_ptrace(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + return -TARGET_ENOSYS; +} + +/* getpriority(2) */ +static inline abi_long do_bsd_getpriority(abi_long which, abi_long who) +{ + abi_long ret; + /* + * Note that negative values are valid for getpriority, so we must + * differentiate based on errno settings. + */ + errno = 0; + ret = getpriority(which, who); + if (ret == -1 && errno != 0) { + return -host_to_target_errno(errno); + } + + return ret; +} + +/* setpriority(2) */ +static inline abi_long do_bsd_setpriority(abi_long which, abi_long who, + abi_long prio) +{ + return get_errno(setpriority(which, who, prio)); +} + +#endif /* !BSD_PROC_H_ */ diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c index f8edb22f2a..baf2f63d2f 100644 --- a/bsd-user/elfload.c +++ b/bsd-user/elfload.c @@ -156,7 +156,7 @@ static abi_ulong copy_elf_strings(int argc, char **argv, void **page, --p; --tmp; --len; if (--offset < 0) { offset = p % TARGET_PAGE_SIZE; - pag = (char *)page[p / TARGET_PAGE_SIZE]; + pag = page[p / TARGET_PAGE_SIZE]; if (!pag) { pag = g_try_malloc0(TARGET_PAGE_SIZE); page[p / TARGET_PAGE_SIZE] = pag; @@ -352,9 +352,10 @@ static abi_ulong load_elf_interp(struct elfhdr *interp_elf_ex, static int symfind(const void *s0, const void *s1) { - target_ulong addr = *(target_ulong *)s0; struct elf_sym *sym = (struct elf_sym *)s1; + __typeof(sym->st_value) addr = *(uint64_t *)s0; int result = 0; + if (addr < sym->st_value) { result = -1; } else if (addr >= sym->st_value + sym->st_size) { @@ -363,7 +364,7 @@ static int symfind(const void *s0, const void *s1) return result; } -static const char *lookup_symbolxx(struct syminfo *s, target_ulong orig_addr) +static const char *lookup_symbolxx(struct syminfo *s, uint64_t orig_addr) { #if ELF_CLASS == ELFCLASS32 struct elf_sym *syms = s->disas_symtab.elf32; @@ -737,8 +738,6 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs, /* OK, This is the point of no return */ info->end_data = 0; info->end_code = 0; - info->start_mmap = (abi_ulong)ELF_START_MMAP; - info->mmap = 0; elf_entry = (abi_ulong) elf_ex.e_entry; /* XXX Join this with PT_INTERP search? */ @@ -812,7 +811,7 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs, bprm->stringp, &elf_ex, load_addr, et_dyn_addr, interp_load_addr, info); info->load_addr = reloc_func_desc; - info->start_brk = info->brk = elf_brk; + info->brk = elf_brk; info->start_stack = bprm->p; info->load_bias = 0; diff --git a/bsd-user/errno_defs.h b/bsd-user/errno_defs.h index f3e8ac3488..abe70119d9 100644 --- a/bsd-user/errno_defs.h +++ b/bsd-user/errno_defs.h @@ -149,7 +149,7 @@ #define TARGET_ELAST 90 /* Must be equal largest errno */ /* Internal errors: */ -#define TARGET_EJUSTRETURN 254 /* Just return without modifing regs */ +#define TARGET_EJUSTRETURN 254 /* Just return without modifying regs */ #define TARGET_ERESTART 255 /* Restart syscall */ #include "special-errno.h" diff --git a/bsd-user/freebsd/meson.build b/bsd-user/freebsd/meson.build index f87c788e84..8fd6c7cfb8 100644 --- a/bsd-user/freebsd/meson.build +++ b/bsd-user/freebsd/meson.build @@ -1,4 +1,6 @@ bsd_user_ss.add(files( + 'os-stat.c', + 'os-proc.c', 'os-sys.c', 'os-syscall.c', )) diff --git a/bsd-user/freebsd/os-misc.h b/bsd-user/freebsd/os-misc.h new file mode 100644 index 0000000000..71145764a4 --- /dev/null +++ b/bsd-user/freebsd/os-misc.h @@ -0,0 +1,98 @@ +/* + * miscellaneous FreeBSD system call shims + * + * Copyright (c) 2013-14 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef OS_MISC_H +#define OS_MISC_H + +#include +#include +#include + +/* + * shm_open2 isn't exported, but the __sys_ alias is. We can use either for the + * static version, but to dynamically link we have to use the sys version. + */ +int __sys_shm_open2(const char *path, int flags, mode_t mode, int shmflags, + const char *); + +#if defined(__FreeBSD_version) && __FreeBSD_version >= 1300048 +/* shm_open2(2) */ +static inline abi_long do_freebsd_shm_open2(abi_ulong pathptr, abi_ulong flags, + abi_long mode, abi_ulong shmflags, abi_ulong nameptr) +{ + int ret; + void *uname, *upath; + + if (pathptr == (uintptr_t)SHM_ANON) { + upath = SHM_ANON; + } else { + upath = lock_user_string(pathptr); + if (upath == NULL) { + return -TARGET_EFAULT; + } + } + + uname = NULL; + if (nameptr != 0) { + uname = lock_user_string(nameptr); + if (uname == NULL) { + unlock_user(upath, pathptr, 0); + return -TARGET_EFAULT; + } + } + ret = get_errno(__sys_shm_open2(upath, + target_to_host_bitmask(flags, fcntl_flags_tbl), mode, + target_to_host_bitmask(shmflags, shmflag_flags_tbl), uname)); + + if (upath != SHM_ANON) { + unlock_user(upath, pathptr, 0); + } + if (uname != NULL) { + unlock_user(uname, nameptr, 0); + } + return ret; +} +#endif /* __FreeBSD_version >= 1300048 */ + +#if defined(__FreeBSD_version) && __FreeBSD_version >= 1300049 +/* shm_rename(2) */ +static inline abi_long do_freebsd_shm_rename(abi_ulong fromptr, abi_ulong toptr, + abi_ulong flags) +{ + int ret; + void *ufrom, *uto; + + ufrom = lock_user_string(fromptr); + if (ufrom == NULL) { + return -TARGET_EFAULT; + } + uto = lock_user_string(toptr); + if (uto == NULL) { + unlock_user(ufrom, fromptr, 0); + return -TARGET_EFAULT; + } + ret = get_errno(shm_rename(ufrom, uto, flags)); + unlock_user(ufrom, fromptr, 0); + unlock_user(uto, toptr, 0); + + return ret; +} +#endif /* __FreeBSD_version >= 1300049 */ + +#endif /* OS_MISC_H */ diff --git a/bsd-user/freebsd/os-proc.c b/bsd-user/freebsd/os-proc.c new file mode 100644 index 0000000000..e0203e259b --- /dev/null +++ b/bsd-user/freebsd/os-proc.c @@ -0,0 +1,480 @@ +/* + * FreeBSD process related emulation code + * + * Copyright (c) 2013-15 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#include "qemu/osdep.h" + +#include +#include +#include +struct kinfo_proc; +#include + +#include "qemu.h" + +/* + * Get the filename for the given file descriptor. + * Note that this may return NULL (fail) if no longer cached in the kernel. + */ +static char * +get_filename_from_fd(pid_t pid, int fd, char *filename, size_t len) +{ + char *ret = NULL; + unsigned int cnt; + struct procstat *procstat = NULL; + struct kinfo_proc *kp = NULL; + struct filestat_list *head = NULL; + struct filestat *fst; + + procstat = procstat_open_sysctl(); + if (procstat == NULL) { + goto out; + } + + kp = procstat_getprocs(procstat, KERN_PROC_PID, pid, &cnt); + if (kp == NULL) { + goto out; + } + + head = procstat_getfiles(procstat, kp, 0); + if (head == NULL) { + goto out; + } + + STAILQ_FOREACH(fst, head, next) { + if (fd == fst->fs_fd) { + if (fst->fs_path != NULL) { + (void)strlcpy(filename, fst->fs_path, len); + ret = filename; + } + break; + } + } + +out: + if (head != NULL) { + procstat_freefiles(procstat, head); + } + if (kp != NULL) { + procstat_freeprocs(procstat, kp); + } + if (procstat != NULL) { + procstat_close(procstat); + } + return ret; +} + +/* + * execve/fexecve + */ +abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp, + abi_ulong guest_envp, int do_fexec) +{ + char **argp, **envp, **qargp, **qarg1, **qarg0, **qargend; + int argc, envc; + abi_ulong gp; + abi_ulong addr; + char **q; + int total_size = 0; + void *p; + abi_long ret; + + argc = 0; + for (gp = guest_argp; gp; gp += sizeof(abi_ulong)) { + if (get_user_ual(addr, gp)) { + return -TARGET_EFAULT; + } + if (!addr) { + break; + } + argc++; + } + envc = 0; + for (gp = guest_envp; gp; gp += sizeof(abi_ulong)) { + if (get_user_ual(addr, gp)) { + return -TARGET_EFAULT; + } + if (!addr) { + break; + } + envc++; + } + + qarg0 = argp = g_new0(char *, argc + 9); + /* save the first argument for the emulator */ + *argp++ = (char *)getprogname(); + qargp = argp; + *argp++ = (char *)getprogname(); + qarg1 = argp; + envp = g_new0(char *, envc + 1); + for (gp = guest_argp, q = argp; gp; gp += sizeof(abi_ulong), q++) { + if (get_user_ual(addr, gp)) { + ret = -TARGET_EFAULT; + goto execve_end; + } + if (!addr) { + break; + } + *q = lock_user_string(addr); + if (*q == NULL) { + ret = -TARGET_EFAULT; + goto execve_end; + } + total_size += strlen(*q) + 1; + } + *q++ = NULL; + qargend = q; + + for (gp = guest_envp, q = envp; gp; gp += sizeof(abi_ulong), q++) { + if (get_user_ual(addr, gp)) { + ret = -TARGET_EFAULT; + goto execve_end; + } + if (!addr) { + break; + } + *q = lock_user_string(addr); + if (*q == NULL) { + ret = -TARGET_EFAULT; + goto execve_end; + } + total_size += strlen(*q) + 1; + } + *q = NULL; + + /* + * This case will not be caught by the host's execve() if its + * page size is bigger than the target's. + */ + if (total_size > MAX_ARG_PAGES * TARGET_PAGE_SIZE) { + ret = -TARGET_E2BIG; + goto execve_end; + } + + if (do_fexec) { + if (((int)path_or_fd > 0 && + is_target_elf_binary((int)path_or_fd)) == 1) { + char execpath[PATH_MAX]; + + /* + * The executable is an elf binary for the target + * arch. execve() it using the emulator if we can + * determine the filename path from the fd. + */ + if (get_filename_from_fd(getpid(), (int)path_or_fd, execpath, + sizeof(execpath)) != NULL) { + memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1)); + qarg1[1] = qarg1[0]; + qarg1[0] = (char *)"-0"; + qarg1 += 2; + qargend += 2; + *qarg1 = execpath; +#ifndef DONT_INHERIT_INTERP_PREFIX + memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1)); + *qarg1++ = (char *)"-L"; + *qarg1++ = (char *)interp_prefix; +#endif + ret = get_errno(execve(qemu_proc_pathname, qargp, envp)); + } else { + /* Getting the filename path failed. */ + ret = -TARGET_EBADF; + goto execve_end; + } + } else { + ret = get_errno(fexecve((int)path_or_fd, argp, envp)); + } + } else { + int fd; + + p = lock_user_string(path_or_fd); + if (p == NULL) { + ret = -TARGET_EFAULT; + goto execve_end; + } + + /* + * Check the header and see if it a target elf binary. If so + * then execute using qemu user mode emulator. + */ + fd = open(p, O_RDONLY | O_CLOEXEC); + if (fd > 0 && is_target_elf_binary(fd) == 1) { + close(fd); + /* execve() as a target binary using emulator. */ + memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1)); + qarg1[1] = qarg1[0]; + qarg1[0] = (char *)"-0"; + qarg1 += 2; + qargend += 2; + *qarg1 = (char *)p; +#ifndef DONT_INHERIT_INTERP_PREFIX + memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1)); + *qarg1++ = (char *)"-L"; + *qarg1++ = (char *)interp_prefix; +#endif + ret = get_errno(execve(qemu_proc_pathname, qargp, envp)); + } else { + close(fd); + /* Execve() as a host native binary. */ + ret = get_errno(execve(p, argp, envp)); + } + unlock_user(p, path_or_fd, 0); + } + +execve_end: + for (gp = guest_argp, q = argp; *q; gp += sizeof(abi_ulong), q++) { + if (get_user_ual(addr, gp) || !addr) { + break; + } + unlock_user(*q, addr, 0); + } + + for (gp = guest_envp, q = envp; *q; gp += sizeof(abi_ulong), q++) { + if (get_user_ual(addr, gp) || !addr) { + break; + } + unlock_user(*q, addr, 0); + } + + g_free(qarg0); + g_free(envp); + + return ret; +} + +#include + +static abi_long +t2h_procctl_cmd(int target_cmd, int *host_cmd) +{ + switch (target_cmd) { + case TARGET_PROC_SPROTECT: + *host_cmd = PROC_SPROTECT; + break; + + case TARGET_PROC_REAP_ACQUIRE: + *host_cmd = PROC_REAP_ACQUIRE; + break; + + case TARGET_PROC_REAP_RELEASE: + *host_cmd = PROC_REAP_RELEASE; + break; + + case TARGET_PROC_REAP_STATUS: + *host_cmd = PROC_REAP_STATUS; + break; + + case TARGET_PROC_REAP_KILL: + *host_cmd = PROC_REAP_KILL; + break; + + default: + return -TARGET_EINVAL; + } + + return 0; +} + +static abi_long +h2t_reaper_status(struct procctl_reaper_status *host_rs, + abi_ulong target_rs_addr) +{ + struct target_procctl_reaper_status *target_rs; + + if (!lock_user_struct(VERIFY_WRITE, target_rs, target_rs_addr, 0)) { + return -TARGET_EFAULT; + } + __put_user(host_rs->rs_flags, &target_rs->rs_flags); + __put_user(host_rs->rs_children, &target_rs->rs_children); + __put_user(host_rs->rs_descendants, &target_rs->rs_descendants); + __put_user(host_rs->rs_reaper, &target_rs->rs_reaper); + __put_user(host_rs->rs_pid, &target_rs->rs_pid); + unlock_user_struct(target_rs, target_rs_addr, 1); + + return 0; +} + +static abi_long +t2h_reaper_kill(abi_ulong target_rk_addr, struct procctl_reaper_kill *host_rk) +{ + struct target_procctl_reaper_kill *target_rk; + + if (!lock_user_struct(VERIFY_READ, target_rk, target_rk_addr, 1)) { + return -TARGET_EFAULT; + } + __get_user(host_rk->rk_sig, &target_rk->rk_sig); + __get_user(host_rk->rk_flags, &target_rk->rk_flags); + __get_user(host_rk->rk_subtree, &target_rk->rk_subtree); + __get_user(host_rk->rk_killed, &target_rk->rk_killed); + __get_user(host_rk->rk_fpid, &target_rk->rk_fpid); + unlock_user_struct(target_rk, target_rk_addr, 0); + + return 0; +} + +static abi_long +h2t_reaper_kill(struct procctl_reaper_kill *host_rk, abi_ulong target_rk_addr) +{ + struct target_procctl_reaper_kill *target_rk; + + if (!lock_user_struct(VERIFY_WRITE, target_rk, target_rk_addr, 0)) { + return -TARGET_EFAULT; + } + __put_user(host_rk->rk_sig, &target_rk->rk_sig); + __put_user(host_rk->rk_flags, &target_rk->rk_flags); + __put_user(host_rk->rk_subtree, &target_rk->rk_subtree); + __put_user(host_rk->rk_killed, &target_rk->rk_killed); + __put_user(host_rk->rk_fpid, &target_rk->rk_fpid); + unlock_user_struct(target_rk, target_rk_addr, 1); + + return 0; +} + +static abi_long +h2t_procctl_reaper_pidinfo(struct procctl_reaper_pidinfo *host_pi, + abi_ulong target_pi_addr) +{ + struct target_procctl_reaper_pidinfo *target_pi; + + if (!lock_user_struct(VERIFY_WRITE, target_pi, target_pi_addr, 0)) { + return -TARGET_EFAULT; + } + __put_user(host_pi->pi_pid, &target_pi->pi_pid); + __put_user(host_pi->pi_subtree, &target_pi->pi_subtree); + __put_user(host_pi->pi_flags, &target_pi->pi_flags); + unlock_user_struct(target_pi, target_pi_addr, 1); + + return 0; +} + +abi_long +do_freebsd_procctl(void *cpu_env, int idtype, abi_ulong arg2, abi_ulong arg3, + abi_ulong arg4, abi_ulong arg5, abi_ulong arg6) +{ + abi_long error = 0, target_rp_pids; + void *data; + int host_cmd, flags; + uint32_t u, target_rp_count; + g_autofree union { + struct procctl_reaper_status rs; + struct procctl_reaper_pids rp; + struct procctl_reaper_kill rk; + } host; + struct target_procctl_reaper_pids *target_rp; + id_t id; /* 64-bit */ + int target_cmd; + abi_ulong target_arg; + +#if TARGET_ABI_BITS == 32 + /* See if we need to align the register pairs. */ + if (regpairs_aligned(cpu_env)) { + id = (id_t)target_arg64(arg3, arg4); + target_cmd = (int)arg5; + target_arg = arg6; + } else { + id = (id_t)target_arg64(arg2, arg3); + target_cmd = (int)arg4; + target_arg = arg5; + } +#else + id = (id_t)arg2; + target_cmd = (int)arg3; + target_arg = arg4; +#endif + + error = t2h_procctl_cmd(target_cmd, &host_cmd); + if (error) { + return error; + } + switch (host_cmd) { + case PROC_SPROTECT: + data = &flags; + break; + + case PROC_REAP_ACQUIRE: + case PROC_REAP_RELEASE: + if (target_arg == 0) { + data = NULL; + } else { + error = -TARGET_EINVAL; + } + break; + + case PROC_REAP_STATUS: + data = &host.rs; + break; + + case PROC_REAP_GETPIDS: + if (!lock_user_struct(VERIFY_READ, target_rp, target_arg, 1)) { + return -TARGET_EFAULT; + } + __get_user(target_rp_count, &target_rp->rp_count); + __get_user(target_rp_pids, &target_rp->rp_pids); + unlock_user_struct(target_rp, target_arg, 0); + host.rp.rp_count = target_rp_count; + host.rp.rp_pids = g_try_new(struct procctl_reaper_pidinfo, + target_rp_count); + + if (host.rp.rp_pids == NULL) { + error = -TARGET_ENOMEM; + } else { + data = &host.rp; + } + break; + + case PROC_REAP_KILL: + error = t2h_reaper_kill(target_arg, &host.rk); + break; + } + + if (error) { + return error; + } + error = get_errno(procctl(idtype, id, host_cmd, data)); + + if (error) { + return error; + } + switch (host_cmd) { + case PROC_SPROTECT: + if (put_user_s32(flags, target_arg)) { + return -TARGET_EFAULT; + } + break; + + case PROC_REAP_STATUS: + error = h2t_reaper_status(&host.rs, target_arg); + break; + + case PROC_REAP_GETPIDS: + /* copyout reaper pidinfo */ + for (u = 0; u < target_rp_count; u++) { + error = h2t_procctl_reaper_pidinfo(&host.rp.rp_pids[u], + target_rp_pids + + (u * sizeof(struct target_procctl_reaper_pidinfo))); + if (error) { + break; + } + } + break; + + case PROC_REAP_KILL: + error = h2t_reaper_kill(&host.rk, target_arg); + break; + } + + return error; +} diff --git a/bsd-user/freebsd/os-proc.h b/bsd-user/freebsd/os-proc.h new file mode 100644 index 0000000000..3003c8cb63 --- /dev/null +++ b/bsd-user/freebsd/os-proc.h @@ -0,0 +1,293 @@ +/* + * process related system call shims and definitions + * + * Copyright (c) 2013-14 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef BSD_USER_FREEBSD_OS_PROC_H +#define BSD_USER_FREEBSD_OS_PROC_H + +#include +#include +#include +#include +#include +#include +#include + +#include "target_arch_cpu.h" + +pid_t safe_wait4(pid_t wpid, int *status, int options, struct rusage *rusage); +pid_t safe_wait6(idtype_t idtype, id_t id, int *status, int options, + struct __wrusage *wrusage, siginfo_t *infop); + +extern int __setugid(int flag); + +/* execve(2) */ +static inline abi_long do_freebsd_execve(abi_ulong path_or_fd, abi_ulong argp, + abi_ulong envp) +{ + + return freebsd_exec_common(path_or_fd, argp, envp, 0); +} + +/* fexecve(2) */ +static inline abi_long do_freebsd_fexecve(abi_ulong path_or_fd, abi_ulong argp, + abi_ulong envp) +{ + + return freebsd_exec_common(path_or_fd, argp, envp, 1); +} + +/* wait4(2) */ +static inline abi_long do_freebsd_wait4(abi_long arg1, abi_ulong target_status, + abi_long arg3, abi_ulong target_rusage) +{ + abi_long ret; + int status; + struct rusage rusage, *rusage_ptr = NULL; + + if (target_rusage) { + rusage_ptr = &rusage; + } + ret = get_errno(safe_wait4(arg1, &status, arg3, rusage_ptr)); + + if (ret < 0) { + return ret; + } + if (target_status != 0) { + status = host_to_target_waitstatus(status); + if (put_user_s32(status, target_status) != 0) { + return -TARGET_EFAULT; + } + } + if (target_rusage != 0) { + host_to_target_rusage(target_rusage, &rusage); + } + return ret; +} + +/* wait6(2) */ +static inline abi_long do_freebsd_wait6(void *cpu_env, abi_long idtype, + abi_long id1, abi_long id2, + abi_ulong target_status, abi_long options, abi_ulong target_wrusage, + abi_ulong target_infop, abi_ulong pad1) +{ + abi_long ret; + int status; + struct __wrusage wrusage, *wrusage_ptr = NULL; + siginfo_t info; + void *p; + + if (regpairs_aligned(cpu_env) != 0) { + /* printf("shifting args\n"); */ + /* 64-bit id is aligned, so shift all the arguments over by one */ + id1 = id2; + id2 = target_status; + target_status = options; + options = target_wrusage; + target_wrusage = target_infop; + target_infop = pad1; + } + + if (target_wrusage) { + wrusage_ptr = &wrusage; + } + ret = get_errno(safe_wait6(idtype, target_arg64(id1, id2), + &status, options, wrusage_ptr, &info)); + + if (ret < 0) { + return ret; + } + if (target_status != 0) { + status = host_to_target_waitstatus(status); + if (put_user_s32(status, target_status) != 0) { + return -TARGET_EFAULT; + } + } + if (target_wrusage != 0) { + host_to_target_wrusage(target_wrusage, &wrusage); + } + if (target_infop != 0) { + p = lock_user(VERIFY_WRITE, target_infop, sizeof(target_siginfo_t), 0); + if (p == NULL) { + return -TARGET_EFAULT; + } + host_to_target_siginfo(p, &info); + unlock_user(p, target_infop, sizeof(target_siginfo_t)); + } + return ret; +} + +/* setloginclass(2) */ +static inline abi_long do_freebsd_setloginclass(abi_ulong arg1) +{ + abi_long ret; + void *p; + + p = lock_user_string(arg1); + if (p == NULL) { + return -TARGET_EFAULT; + } + ret = get_errno(setloginclass(p)); + unlock_user(p, arg1, 0); + + return ret; +} + +/* getloginclass(2) */ +static inline abi_long do_freebsd_getloginclass(abi_ulong arg1, abi_ulong arg2) +{ + abi_long ret; + void *p; + + p = lock_user(VERIFY_WRITE, arg1, arg2, 0); + if (p == NULL) { + return -TARGET_EFAULT; + } + ret = get_errno(getloginclass(p, arg2)); + unlock_user(p, arg1, arg2); + + return ret; +} + +/* pdgetpid(2) */ +static inline abi_long do_freebsd_pdgetpid(abi_long fd, abi_ulong target_pidp) +{ + abi_long ret; + pid_t pid; + + ret = get_errno(pdgetpid(fd, &pid)); + if (!is_error(ret)) { + if (put_user_u32(pid, target_pidp)) { + return -TARGET_EFAULT; + } + } + return ret; +} + +/* undocumented __setugid */ +static inline abi_long do_freebsd___setugid(abi_long arg1) +{ + return -TARGET_ENOSYS; +} + +/* fork(2) */ +static inline abi_long do_freebsd_fork(void *cpu_env) +{ + abi_long ret; + abi_ulong child_flag; + + fork_start(); + ret = fork(); + if (ret == 0) { + /* child */ + child_flag = 1; + target_cpu_clone_regs(cpu_env, 0); + } else { + /* parent */ + child_flag = 0; + } + + /* + * The fork system call sets a child flag in the second return + * value: 0 for parent process, 1 for child process. + */ + set_second_rval(cpu_env, child_flag); + + fork_end(ret); + + return ret; +} + +/* vfork(2) */ +static inline abi_long do_freebsd_vfork(void *cpu_env) +{ + return do_freebsd_fork(cpu_env); +} + +/* rfork(2) */ +static inline abi_long do_freebsd_rfork(void *cpu_env, abi_long flags) +{ + abi_long ret; + abi_ulong child_flag; + + /* + * XXX We need to handle RFMEM here, as well. Neither are safe to execute + * as-is on x86 hosts because they'll split memory but not the stack, + * wreaking havoc on host architectures that use the stack to store the + * return address as both threads try to pop it off. Rejecting RFSPAWN + * entirely for now is ok, the only consumer at the moment is posix_spawn + * and it will fall back to classic vfork(2) if we return EINVAL. + */ + if ((flags & TARGET_RFSPAWN) != 0) { + return -TARGET_EINVAL; + } + fork_start(); + ret = rfork(flags); + if (ret == 0) { + /* child */ + child_flag = 1; + target_cpu_clone_regs(cpu_env, 0); + } else { + /* parent */ + child_flag = 0; + } + + /* + * The fork system call sets a child flag in the second return + * value: 0 for parent process, 1 for child process. + */ + set_second_rval(cpu_env, child_flag); + fork_end(ret); + + return ret; + +} + +/* pdfork(2) */ +static inline abi_long do_freebsd_pdfork(void *cpu_env, abi_ulong target_fdp, + abi_long flags) +{ + abi_long ret; + abi_ulong child_flag; + int fd; + + fork_start(); + ret = pdfork(&fd, flags); + if (ret == 0) { + /* child */ + child_flag = 1; + target_cpu_clone_regs(cpu_env, 0); + } else { + /* parent */ + child_flag = 0; + if (put_user_s32(fd, target_fdp)) { + return -TARGET_EFAULT; + } + } + + /* + * The fork system call sets a child flag in the second return + * value: 0 for parent process, 1 for child process. + */ + set_second_rval(cpu_env, child_flag); + fork_end(ret); + + return ret; +} + +#endif /* BSD_USER_FREEBSD_OS_PROC_H */ diff --git a/bsd-user/freebsd/os-stat.c b/bsd-user/freebsd/os-stat.c new file mode 100644 index 0000000000..f0f9e609c3 --- /dev/null +++ b/bsd-user/freebsd/os-stat.c @@ -0,0 +1,262 @@ +/* + * FreeBSD stat related conversion routines + * + * Copyright (c) 2013 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#include "qemu/osdep.h" + +#include "qemu.h" + +/* + * stat conversion + */ +abi_long h2t_freebsd11_stat(abi_ulong target_addr, + struct freebsd11_stat *host_st) +{ + struct target_freebsd11_stat *target_st; + + if (!lock_user_struct(VERIFY_WRITE, target_st, target_addr, 0)) { + return -TARGET_EFAULT; + } + memset(target_st, 0, sizeof(*target_st)); + __put_user(host_st->st_dev, &target_st->st_dev); + __put_user(host_st->st_ino, &target_st->st_ino); + __put_user(host_st->st_mode, &target_st->st_mode); + __put_user(host_st->st_nlink, &target_st->st_nlink); + __put_user(host_st->st_uid, &target_st->st_uid); + __put_user(host_st->st_gid, &target_st->st_gid); + __put_user(host_st->st_rdev, &target_st->st_rdev); + __put_user(host_st->st_atim.tv_sec, &target_st->st_atim.tv_sec); + __put_user(host_st->st_atim.tv_nsec, &target_st->st_atim.tv_nsec); + __put_user(host_st->st_mtim.tv_sec, &target_st->st_mtim.tv_sec); + __put_user(host_st->st_mtim.tv_nsec, &target_st->st_mtim.tv_nsec); + __put_user(host_st->st_ctim.tv_sec, &target_st->st_ctim.tv_sec); + __put_user(host_st->st_ctim.tv_nsec, &target_st->st_ctim.tv_nsec); + __put_user(host_st->st_size, &target_st->st_size); + __put_user(host_st->st_blocks, &target_st->st_blocks); + __put_user(host_st->st_blksize, &target_st->st_blksize); + __put_user(host_st->st_flags, &target_st->st_flags); + __put_user(host_st->st_gen, &target_st->st_gen); + /* st_lspare not used */ + __put_user(host_st->st_birthtim.tv_sec, &target_st->st_birthtim.tv_sec); + __put_user(host_st->st_birthtim.tv_nsec, &target_st->st_birthtim.tv_nsec); + unlock_user_struct(target_st, target_addr, 1); + + return 0; +} + +abi_long h2t_freebsd_stat(abi_ulong target_addr, + struct stat *host_st) +{ + struct target_stat *target_st; + + if (!lock_user_struct(VERIFY_WRITE, target_st, target_addr, 0)) { + return -TARGET_EFAULT; + } + memset(target_st, 0, sizeof(*target_st)); + __put_user(host_st->st_dev, &target_st->st_dev); + __put_user(host_st->st_ino, &target_st->st_ino); + __put_user(host_st->st_nlink, &target_st->st_nlink); + __put_user(host_st->st_mode, &target_st->st_mode); + __put_user(host_st->st_uid, &target_st->st_uid); + __put_user(host_st->st_gid, &target_st->st_gid); + __put_user(host_st->st_rdev, &target_st->st_rdev); + __put_user(host_st->st_atim.tv_sec, &target_st->st_atim.tv_sec); + __put_user(host_st->st_atim.tv_nsec, &target_st->st_atim.tv_nsec); +#ifdef TARGET_HAS_STAT_TIME_T_EXT +/* __put_user(host_st->st_mtim_ext, &target_st->st_mtim_ext); XXX */ +#endif + __put_user(host_st->st_mtim.tv_sec, &target_st->st_mtim.tv_sec); + __put_user(host_st->st_mtim.tv_nsec, &target_st->st_mtim.tv_nsec); +#ifdef TARGET_HAS_STAT_TIME_T_EXT +/* __put_user(host_st->st_ctim_ext, &target_st->st_ctim_ext); XXX */ +#endif + __put_user(host_st->st_ctim.tv_sec, &target_st->st_ctim.tv_sec); + __put_user(host_st->st_ctim.tv_nsec, &target_st->st_ctim.tv_nsec); +#ifdef TARGET_HAS_STAT_TIME_T_EXT +/* __put_user(host_st->st_birthtim_ext, &target_st->st_birthtim_ext); XXX */ +#endif + __put_user(host_st->st_birthtim.tv_sec, &target_st->st_birthtim.tv_sec); + __put_user(host_st->st_birthtim.tv_nsec, &target_st->st_birthtim.tv_nsec); + + __put_user(host_st->st_size, &target_st->st_size); + __put_user(host_st->st_blocks, &target_st->st_blocks); + __put_user(host_st->st_blksize, &target_st->st_blksize); + __put_user(host_st->st_flags, &target_st->st_flags); + __put_user(host_st->st_gen, &target_st->st_gen); + unlock_user_struct(target_st, target_addr, 1); + + return 0; +} + +abi_long h2t_freebsd11_nstat(abi_ulong target_addr, + struct freebsd11_stat *host_st) +{ + struct target_freebsd11_nstat *target_st; + + if (!lock_user_struct(VERIFY_WRITE, target_st, target_addr, 0)) { + return -TARGET_EFAULT; + } + memset(target_st, 0, sizeof(*target_st)); + __put_user(host_st->st_dev, &target_st->st_dev); + __put_user(host_st->st_ino, &target_st->st_ino); + __put_user(host_st->st_mode, &target_st->st_mode); + __put_user(host_st->st_nlink, &target_st->st_nlink); + __put_user(host_st->st_uid, &target_st->st_uid); + __put_user(host_st->st_gid, &target_st->st_gid); + __put_user(host_st->st_rdev, &target_st->st_rdev); + __put_user(host_st->st_atim.tv_sec, &target_st->st_atim.tv_sec); + __put_user(host_st->st_atim.tv_nsec, &target_st->st_atim.tv_nsec); + __put_user(host_st->st_mtim.tv_sec, &target_st->st_mtim.tv_sec); + __put_user(host_st->st_mtim.tv_nsec, &target_st->st_mtim.tv_nsec); + __put_user(host_st->st_ctim.tv_sec, &target_st->st_ctim.tv_sec); + __put_user(host_st->st_ctim.tv_nsec, &target_st->st_ctim.tv_nsec); + __put_user(host_st->st_size, &target_st->st_size); + __put_user(host_st->st_blocks, &target_st->st_blocks); + __put_user(host_st->st_blksize, &target_st->st_blksize); + __put_user(host_st->st_flags, &target_st->st_flags); + __put_user(host_st->st_gen, &target_st->st_gen); + __put_user(host_st->st_birthtim.tv_sec, &target_st->st_birthtim.tv_sec); + __put_user(host_st->st_birthtim.tv_nsec, &target_st->st_birthtim.tv_nsec); + unlock_user_struct(target_st, target_addr, 1); + + return 0; +} + +/* + * file handle conversion + */ +abi_long t2h_freebsd_fhandle(fhandle_t *host_fh, abi_ulong target_addr) +{ + target_freebsd_fhandle_t *target_fh; + + if (!lock_user_struct(VERIFY_READ, target_fh, target_addr, 1)) { + return -TARGET_EFAULT; + } + __get_user(host_fh->fh_fsid.val[0], &target_fh->fh_fsid.val[0]); + __get_user(host_fh->fh_fsid.val[1], &target_fh->fh_fsid.val[0]); + __get_user(host_fh->fh_fid.fid_len, &target_fh->fh_fid.fid_len); + /* u_short fid_data0; */ + memcpy(host_fh->fh_fid.fid_data, target_fh->fh_fid.fid_data, + TARGET_MAXFIDSZ); + unlock_user_struct(target_fh, target_addr, 0); + return 0; +} + +abi_long h2t_freebsd_fhandle(abi_ulong target_addr, fhandle_t *host_fh) +{ + target_freebsd_fhandle_t *target_fh; + + if (!lock_user_struct(VERIFY_WRITE, target_fh, target_addr, 0)) { + return -TARGET_EFAULT; + } + __put_user(host_fh->fh_fsid.val[0], &target_fh->fh_fsid.val[0]); + __put_user(host_fh->fh_fsid.val[1], &target_fh->fh_fsid.val[0]); + __put_user(host_fh->fh_fid.fid_len, &target_fh->fh_fid.fid_len); + /* u_short fid_data0; */ + memcpy(target_fh->fh_fid.fid_data, host_fh->fh_fid.fid_data, + TARGET_MAXFIDSZ); + unlock_user_struct(target_fh, target_addr, 1); + return 0; +} + +/* + * file system stat + */ +abi_long h2t_freebsd11_statfs(abi_ulong target_addr, + struct freebsd11_statfs *host_statfs) +{ + struct target_freebsd11_statfs *target_statfs; + + if (!lock_user_struct(VERIFY_WRITE, target_statfs, target_addr, 0)) { + return -TARGET_EFAULT; + } + __put_user(host_statfs->f_version, &target_statfs->f_version); + __put_user(host_statfs->f_type, &target_statfs->f_type); + __put_user(host_statfs->f_flags, &target_statfs->f_flags); + __put_user(host_statfs->f_bsize, &target_statfs->f_bsize); + __put_user(host_statfs->f_iosize, &target_statfs->f_iosize); + __put_user(host_statfs->f_blocks, &target_statfs->f_blocks); + __put_user(host_statfs->f_bfree, &target_statfs->f_bfree); + __put_user(host_statfs->f_bavail, &target_statfs->f_bavail); + __put_user(host_statfs->f_files, &target_statfs->f_files); + __put_user(host_statfs->f_ffree, &target_statfs->f_ffree); + __put_user(host_statfs->f_syncwrites, &target_statfs->f_syncwrites); + __put_user(host_statfs->f_asyncwrites, &target_statfs->f_asyncwrites); + __put_user(host_statfs->f_syncreads, &target_statfs->f_syncreads); + __put_user(host_statfs->f_asyncreads, &target_statfs->f_asyncreads); + /* uint64_t f_spare[10]; */ + __put_user(host_statfs->f_namemax, &target_statfs->f_namemax); + __put_user(host_statfs->f_owner, &target_statfs->f_owner); + __put_user(host_statfs->f_fsid.val[0], &target_statfs->f_fsid.val[0]); + __put_user(host_statfs->f_fsid.val[1], &target_statfs->f_fsid.val[1]); + /* char f_charspace[80]; */ + strncpy(target_statfs->f_fstypename, host_statfs->f_fstypename, + sizeof(target_statfs->f_fstypename)); + strncpy(target_statfs->f_mntfromname, host_statfs->f_mntfromname, + sizeof(target_statfs->f_mntfromname)); + strncpy(target_statfs->f_mntonname, host_statfs->f_mntonname, + sizeof(target_statfs->f_mntonname)); + unlock_user_struct(target_statfs, target_addr, 1); + return 0; +} + +abi_long h2t_freebsd_statfs(abi_ulong target_addr, + struct statfs *host_statfs) +{ + struct target_statfs *target_statfs; + + if (!lock_user_struct(VERIFY_WRITE, target_statfs, target_addr, 0)) { + return -TARGET_EFAULT; + } + __put_user(host_statfs->f_version, &target_statfs->f_version); + __put_user(host_statfs->f_type, &target_statfs->f_type); + __put_user(host_statfs->f_flags, &target_statfs->f_flags); + __put_user(host_statfs->f_bsize, &target_statfs->f_bsize); + __put_user(host_statfs->f_iosize, &target_statfs->f_iosize); + __put_user(host_statfs->f_blocks, &target_statfs->f_blocks); + __put_user(host_statfs->f_bfree, &target_statfs->f_bfree); + __put_user(host_statfs->f_bavail, &target_statfs->f_bavail); + __put_user(host_statfs->f_files, &target_statfs->f_files); + __put_user(host_statfs->f_ffree, &target_statfs->f_ffree); + __put_user(host_statfs->f_syncwrites, &target_statfs->f_syncwrites); + __put_user(host_statfs->f_asyncwrites, &target_statfs->f_asyncwrites); + __put_user(host_statfs->f_syncreads, &target_statfs->f_syncreads); + __put_user(host_statfs->f_asyncreads, &target_statfs->f_asyncreads); + /* uint64_t f_spare[10]; */ + __put_user(host_statfs->f_namemax, &target_statfs->f_namemax); + __put_user(host_statfs->f_owner, &target_statfs->f_owner); + __put_user(host_statfs->f_fsid.val[0], &target_statfs->f_fsid.val[0]); + __put_user(host_statfs->f_fsid.val[1], &target_statfs->f_fsid.val[1]); + /* char f_charspace[80]; */ + strncpy(target_statfs->f_fstypename, host_statfs->f_fstypename, + sizeof(target_statfs->f_fstypename)); + strncpy(target_statfs->f_mntfromname, host_statfs->f_mntfromname, + sizeof(target_statfs->f_mntfromname)); + strncpy(target_statfs->f_mntonname, host_statfs->f_mntonname, + sizeof(target_statfs->f_mntonname)); + unlock_user_struct(target_statfs, target_addr, 1); + return 0; +} + +/* + * fcntl cmd conversion + */ +abi_long target_to_host_fcntl_cmd(int cmd) +{ + return cmd; +} + diff --git a/bsd-user/freebsd/os-stat.h b/bsd-user/freebsd/os-stat.h new file mode 100644 index 0000000000..3bdc66aa98 --- /dev/null +++ b/bsd-user/freebsd/os-stat.h @@ -0,0 +1,663 @@ +/* + * stat related system call shims and definitions + * + * Copyright (c) 2013 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef BSD_USER_FREEBSD_OS_STAT_H +#define BSD_USER_FREEBSD_OS_STAT_H + +int freebsd11_stat(const char *path, struct freebsd11_stat *stat); +__sym_compat(stat, freebsd11_stat, FBSD_1.0); +int freebsd11_lstat(const char *path, struct freebsd11_stat *stat); +__sym_compat(lstat, freebsd11_lstat, FBSD_1.0); +int freebsd11_fstat(int fd, struct freebsd11_stat *stat); +__sym_compat(fstat, freebsd11_fstat, FBSD_1.0); +int freebsd11_fstatat(int fd, const char *path, struct freebsd11_stat *stat, + int flag); +__sym_compat(fstatat, freebsd11_fstatat, FBSD_1.1); + +int freebsd11_fhstat(const fhandle_t *fhandle, struct freebsd11_stat *stat); +__sym_compat(fhstat, freebsd11_fhstat, FBSD_1.0); +int freebsd11_getfsstat(struct freebsd11_statfs *buf, long bufsize, int mode); +__sym_compat(getfsstat, freebsd11_getfsstat, FBSD_1.0); +int freebsd11_fhstatfs(const fhandle_t *fhandle, struct freebsd11_statfs * buf); +__sym_compat(fhstatfs, freebsd11_fhstatfs, FBSD_1.0); +int freebsd11_statfs(const char *path, struct freebsd11_statfs *buf); +__sym_compat(statfs, freebsd11_statfs, FBSD_1.0); +int freebsd11_fstatfs(int fd, struct freebsd11_statfs *buf); +__sym_compat(fstatfs, freebsd11_fstatfs, FBSD_1.0); + +ssize_t freebsd11_getdirentries(int fd, char *buf, size_t nbytes, off_t *basep); +__sym_compat(getdirentries, freebsd11_getdirentries, FBSD_1.0); +ssize_t freebsd11_getdents(int fd, char *buf, size_t nbytes); +__sym_compat(getdents, freebsd11_getdents, FBSD_1.0); + +/* undocumented nstat system calls */ +int freebsd11_nstat(const char *path, struct freebsd11_stat *sb); +__sym_compat(nstat, freebsd11_nstat, FBSD_1.0); +int freebsd11_nlstat(const char *path, struct freebsd11_stat *sb); +__sym_compat(nlstat, freebsd11_nlstat, FBSD_1.0); +int freebsd11_nfstat(int fd, struct freebsd11_stat *sb); +__sym_compat(nfstat, freebsd11_nfstat, FBSD_1.0); + +/* stat(2) */ +static inline abi_long do_freebsd11_stat(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + struct freebsd11_stat st; + + LOCK_PATH(p, arg1); + ret = get_errno(freebsd11_stat(path(p), &st)); + UNLOCK_PATH(p, arg1); + if (!is_error(ret)) { + ret = h2t_freebsd11_stat(arg2, &st); + } + return ret; +} + +/* lstat(2) */ +static inline abi_long do_freebsd11_lstat(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + struct freebsd11_stat st; + + LOCK_PATH(p, arg1); + ret = get_errno(freebsd11_lstat(path(p), &st)); + UNLOCK_PATH(p, arg1); + if (!is_error(ret)) { + ret = h2t_freebsd11_stat(arg2, &st); + } + return ret; +} + +/* fstat(2) */ +static inline abi_long do_freebsd11_fstat(abi_long arg1, abi_long arg2) +{ + abi_long ret; + struct freebsd11_stat st; + + ret = get_errno(freebsd11_fstat(arg1, &st)); + if (!is_error(ret)) { + ret = h2t_freebsd11_stat(arg2, &st); + } + return ret; +} + +/* fstat(2) */ +static inline abi_long do_freebsd_fstat(abi_long arg1, abi_long arg2) +{ + abi_long ret; + struct stat st; + + ret = get_errno(fstat(arg1, &st)); + if (!is_error(ret)) { + ret = h2t_freebsd_stat(arg2, &st); + } + return ret; +} + +/* fstatat(2) */ +static inline abi_long do_freebsd11_fstatat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p; + struct freebsd11_stat st; + + LOCK_PATH(p, arg2); + ret = get_errno(freebsd11_fstatat(arg1, p, &st, arg4)); + UNLOCK_PATH(p, arg2); + if (!is_error(ret) && arg3) { + ret = h2t_freebsd11_stat(arg3, &st); + } + return ret; +} + +/* fstatat(2) */ +static inline abi_long do_freebsd_fstatat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4) +{ + abi_long ret; + void *p; + struct stat st; + + LOCK_PATH(p, arg2); + ret = get_errno(fstatat(arg1, p, &st, arg4)); + UNLOCK_PATH(p, arg2); + if (!is_error(ret) && arg3) { + ret = h2t_freebsd_stat(arg3, &st); + } + return ret; +} + +/* undocumented nstat(char *path, struct nstat *ub) syscall */ +static abi_long do_freebsd11_nstat(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + struct freebsd11_stat st; + + LOCK_PATH(p, arg1); + ret = get_errno(freebsd11_nstat(path(p), &st)); + UNLOCK_PATH(p, arg1); + if (!is_error(ret)) { + ret = h2t_freebsd11_nstat(arg2, &st); + } + return ret; +} + +/* undocumented nfstat(int fd, struct nstat *sb) syscall */ +static abi_long do_freebsd11_nfstat(abi_long arg1, abi_long arg2) +{ + abi_long ret; + struct freebsd11_stat st; + + ret = get_errno(freebsd11_nfstat(arg1, &st)); + if (!is_error(ret)) { + ret = h2t_freebsd11_nstat(arg2, &st); + } + return ret; +} + +/* undocumented nlstat(char *path, struct nstat *ub) syscall */ +static abi_long do_freebsd11_nlstat(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + struct freebsd11_stat st; + + LOCK_PATH(p, arg1); + ret = get_errno(freebsd11_nlstat(path(p), &st)); + UNLOCK_PATH(p, arg1); + if (!is_error(ret)) { + ret = h2t_freebsd11_nstat(arg2, &st); + } + return ret; +} + +/* getfh(2) */ +static abi_long do_freebsd_getfh(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + fhandle_t host_fh; + + LOCK_PATH(p, arg1); + ret = get_errno(getfh(path(p), &host_fh)); + UNLOCK_PATH(p, arg1); + if (is_error(ret)) { + return ret; + } + return h2t_freebsd_fhandle(arg2, &host_fh); +} + +/* lgetfh(2) */ +static inline abi_long do_freebsd_lgetfh(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + fhandle_t host_fh; + + LOCK_PATH(p, arg1); + ret = get_errno(lgetfh(path(p), &host_fh)); + UNLOCK_PATH(p, arg1); + if (is_error(ret)) { + return ret; + } + return h2t_freebsd_fhandle(arg2, &host_fh); +} + +/* fhopen(2) */ +static inline abi_long do_freebsd_fhopen(abi_long arg1, abi_long arg2) +{ + abi_long ret; + fhandle_t host_fh; + + ret = t2h_freebsd_fhandle(&host_fh, arg1); + if (is_error(ret)) { + return ret; + } + + return get_errno(fhopen(&host_fh, arg2)); +} + +/* fhstat(2) */ +static inline abi_long do_freebsd11_fhstat(abi_long arg1, abi_long arg2) +{ + abi_long ret; + fhandle_t host_fh; + struct freebsd11_stat host_sb; + + ret = t2h_freebsd_fhandle(&host_fh, arg1); + if (is_error(ret)) { + return ret; + } + ret = get_errno(freebsd11_fhstat(&host_fh, &host_sb)); + if (is_error(ret)) { + return ret; + } + return h2t_freebsd11_stat(arg2, &host_sb); +} + +/* fhstat(2) */ +static inline abi_long do_freebsd_fhstat(abi_long arg1, abi_long arg2) +{ + abi_long ret; + fhandle_t host_fh; + struct stat host_sb; + + ret = t2h_freebsd_fhandle(&host_fh, arg1); + if (is_error(ret)) { + return ret; + } + ret = get_errno(fhstat(&host_fh, &host_sb)); + if (is_error(ret)) { + return ret; + } + return h2t_freebsd_stat(arg2, &host_sb); +} + +/* fhstatfs(2) */ +static inline abi_long do_freebsd11_fhstatfs(abi_ulong target_fhp_addr, + abi_ulong target_stfs_addr) +{ + abi_long ret; + fhandle_t host_fh; + struct freebsd11_statfs host_stfs; + + ret = t2h_freebsd_fhandle(&host_fh, target_fhp_addr); + if (is_error(ret)) { + return ret; + } + ret = get_errno(freebsd11_fhstatfs(&host_fh, &host_stfs)); + if (is_error(ret)) { + return ret; + } + return h2t_freebsd11_statfs(target_stfs_addr, &host_stfs); +} + +/* fhstatfs(2) */ +static inline abi_long do_freebsd_fhstatfs(abi_ulong target_fhp_addr, + abi_ulong target_stfs_addr) +{ + abi_long ret; + fhandle_t host_fh; + struct statfs host_stfs; + + ret = t2h_freebsd_fhandle(&host_fh, target_fhp_addr); + if (is_error(ret)) { + return ret; + } + ret = get_errno(fhstatfs(&host_fh, &host_stfs)); + if (is_error(ret)) { + return ret; + } + return h2t_freebsd_statfs(target_stfs_addr, &host_stfs); +} + +/* statfs(2) */ +static inline abi_long do_freebsd11_statfs(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + struct freebsd11_statfs host_stfs; + + LOCK_PATH(p, arg1); + ret = get_errno(freebsd11_statfs(path(p), &host_stfs)); + UNLOCK_PATH(p, arg1); + if (is_error(ret)) { + return ret; + } + + return h2t_freebsd11_statfs(arg2, &host_stfs); +} + +/* statfs(2) */ +static inline abi_long do_freebsd_statfs(abi_long arg1, abi_long arg2) +{ + abi_long ret; + void *p; + struct statfs host_stfs; + + LOCK_PATH(p, arg1); + ret = get_errno(statfs(path(p), &host_stfs)); + UNLOCK_PATH(p, arg1); + if (is_error(ret)) { + return ret; + } + + return h2t_freebsd_statfs(arg2, &host_stfs); +} + +/* fstatfs(2) */ +static inline abi_long do_freebsd11_fstatfs(abi_long fd, abi_ulong target_addr) +{ + abi_long ret; + struct freebsd11_statfs host_stfs; + + ret = get_errno(freebsd11_fstatfs(fd, &host_stfs)); + if (is_error(ret)) { + return ret; + } + + return h2t_freebsd11_statfs(target_addr, &host_stfs); +} + +/* fstatfs(2) */ +static inline abi_long do_freebsd_fstatfs(abi_long fd, abi_ulong target_addr) +{ + abi_long ret; + struct statfs host_stfs; + + ret = get_errno(fstatfs(fd, &host_stfs)); + if (is_error(ret)) { + return ret; + } + + return h2t_freebsd_statfs(target_addr, &host_stfs); +} + +/* getfsstat(2) */ +static inline abi_long do_freebsd11_getfsstat(abi_ulong target_addr, + abi_long bufsize, abi_long flags) +{ + abi_long ret; + struct freebsd11_statfs *host_stfs; + int count; + long host_bufsize; + + count = bufsize / sizeof(struct target_freebsd11_statfs); + + /* if user buffer is NULL then return number of mounted FS's */ + if (target_addr == 0 || count == 0) { + return get_errno(freebsd11_getfsstat(NULL, 0, flags)); + } + + /* XXX check count to be reasonable */ + host_bufsize = sizeof(struct freebsd11_statfs) * count; + host_stfs = alloca(host_bufsize); + if (!host_stfs) { + return -TARGET_EINVAL; + } + + ret = count = get_errno(freebsd11_getfsstat(host_stfs, host_bufsize, flags)); + if (is_error(ret)) { + return ret; + } + + while (count--) { + if (h2t_freebsd11_statfs((target_addr + + (count * sizeof(struct target_freebsd11_statfs))), + &host_stfs[count])) { + return -TARGET_EFAULT; + } + } + return ret; +} + +/* getfsstat(2) */ +static inline abi_long do_freebsd_getfsstat(abi_ulong target_addr, + abi_long bufsize, abi_long flags) +{ + abi_long ret; + struct statfs *host_stfs; + int count; + long host_bufsize; + + count = bufsize / sizeof(struct target_statfs); + + /* if user buffer is NULL then return number of mounted FS's */ + if (target_addr == 0 || count == 0) { + return get_errno(freebsd11_getfsstat(NULL, 0, flags)); + } + + /* XXX check count to be reasonable */ + host_bufsize = sizeof(struct statfs) * count; + host_stfs = alloca(host_bufsize); + if (!host_stfs) { + return -TARGET_EINVAL; + } + + ret = count = get_errno(getfsstat(host_stfs, host_bufsize, flags)); + if (is_error(ret)) { + return ret; + } + + while (count--) { + if (h2t_freebsd_statfs((target_addr + + (count * sizeof(struct target_statfs))), + &host_stfs[count])) { + return -TARGET_EFAULT; + } + } + return ret; +} + +/* getdents(2) */ +static inline abi_long do_freebsd11_getdents(abi_long arg1, + abi_ulong arg2, abi_long nbytes) +{ + abi_long ret; + struct freebsd11_dirent *dirp; + + dirp = lock_user(VERIFY_WRITE, arg2, nbytes, 0); + if (dirp == NULL) { + return -TARGET_EFAULT; + } + ret = get_errno(freebsd11_getdents(arg1, (char *)dirp, nbytes)); + if (!is_error(ret)) { + struct freebsd11_dirent *de; + int len = ret; + int reclen; + + de = dirp; + while (len > 0) { + reclen = de->d_reclen; + if (reclen > len) { + return -TARGET_EFAULT; + } + de->d_reclen = tswap16(reclen); + de->d_fileno = tswap32(de->d_fileno); + len -= reclen; + } + } + return ret; +} + +/* getdirecentries(2) */ +static inline abi_long do_freebsd11_getdirentries(abi_long arg1, + abi_ulong arg2, abi_long nbytes, abi_ulong arg4) +{ + abi_long ret; + struct freebsd11_dirent *dirp; + long basep; + + dirp = lock_user(VERIFY_WRITE, arg2, nbytes, 0); + if (dirp == NULL) { + return -TARGET_EFAULT; + } + ret = get_errno(freebsd11_getdirentries(arg1, (char *)dirp, nbytes, &basep)); + if (!is_error(ret)) { + struct freebsd11_dirent *de; + int len = ret; + int reclen; + + de = dirp; + while (len > 0) { + reclen = de->d_reclen; + if (reclen > len) { + return -TARGET_EFAULT; + } + de->d_reclen = tswap16(reclen); + de->d_fileno = tswap32(de->d_fileno); + len -= reclen; + de = (struct freebsd11_dirent *)((void *)de + reclen); + } + } + unlock_user(dirp, arg2, ret); + if (arg4) { + if (put_user(basep, arg4, abi_ulong)) { + return -TARGET_EFAULT; + } + } + return ret; +} + +/* getdirecentries(2) */ +static inline abi_long do_freebsd_getdirentries(abi_long arg1, + abi_ulong arg2, abi_long nbytes, abi_ulong arg4) +{ + abi_long ret; + struct dirent *dirp; + long basep; + + dirp = lock_user(VERIFY_WRITE, arg2, nbytes, 0); + if (dirp == NULL) { + return -TARGET_EFAULT; + } + ret = get_errno(getdirentries(arg1, (char *)dirp, nbytes, &basep)); + if (!is_error(ret)) { + struct dirent *de; + int len = ret; + int reclen; + + de = dirp; + while (len > 0) { + reclen = de->d_reclen; + if (reclen > len) { + return -TARGET_EFAULT; + } + de->d_fileno = tswap64(de->d_fileno); + de->d_off = tswap64(de->d_off); + de->d_reclen = tswap16(de->d_reclen); + de->d_namlen = tswap16(de->d_namlen); + len -= reclen; + de = (struct dirent *)((void *)de + reclen); + } + } + unlock_user(dirp, arg2, ret); + if (arg4) { + if (put_user(basep, arg4, abi_ulong)) { + return -TARGET_EFAULT; + } + } + return ret; +} + +/* fcntl(2) */ +static inline abi_long do_freebsd_fcntl(abi_long arg1, abi_long arg2, + abi_ulong arg3) +{ + abi_long ret; + int host_cmd; + struct flock fl; + struct target_freebsd_flock *target_fl; + + host_cmd = target_to_host_fcntl_cmd(arg2); + if (host_cmd < 0) { + return host_cmd; + } + switch (arg2) { + case TARGET_F_GETLK: + if (!lock_user_struct(VERIFY_READ, target_fl, arg3, 1)) { + return -TARGET_EFAULT; + } + __get_user(fl.l_type, &target_fl->l_type); + __get_user(fl.l_whence, &target_fl->l_whence); + __get_user(fl.l_start, &target_fl->l_start); + __get_user(fl.l_len, &target_fl->l_len); + __get_user(fl.l_pid, &target_fl->l_pid); + __get_user(fl.l_sysid, &target_fl->l_sysid); + unlock_user_struct(target_fl, arg3, 0); + ret = get_errno(safe_fcntl(arg1, host_cmd, &fl)); + if (!is_error(ret)) { + if (!lock_user_struct(VERIFY_WRITE, target_fl, arg3, 0)) { + return -TARGET_EFAULT; + } + __put_user(fl.l_type, &target_fl->l_type); + __put_user(fl.l_whence, &target_fl->l_whence); + __put_user(fl.l_start, &target_fl->l_start); + __put_user(fl.l_len, &target_fl->l_len); + __put_user(fl.l_pid, &target_fl->l_pid); + __put_user(fl.l_sysid, &target_fl->l_sysid); + unlock_user_struct(target_fl, arg3, 1); + } + break; + + case TARGET_F_SETLK: + case TARGET_F_SETLKW: + if (!lock_user_struct(VERIFY_READ, target_fl, arg3, 1)) { + return -TARGET_EFAULT; + } + __get_user(fl.l_type, &target_fl->l_type); + __get_user(fl.l_whence, &target_fl->l_whence); + __get_user(fl.l_start, &target_fl->l_start); + __get_user(fl.l_len, &target_fl->l_len); + __get_user(fl.l_pid, &target_fl->l_pid); + __get_user(fl.l_sysid, &target_fl->l_sysid); + unlock_user_struct(target_fl, arg3, 0); + ret = get_errno(safe_fcntl(arg1, host_cmd, &fl)); + break; + + case TARGET_F_DUPFD: + case TARGET_F_DUP2FD: + case TARGET_F_GETOWN: + case TARGET_F_SETOWN: + case TARGET_F_GETFD: + case TARGET_F_SETFD: + case TARGET_F_GETFL: + case TARGET_F_SETFL: + case TARGET_F_READAHEAD: + case TARGET_F_RDAHEAD: + case TARGET_F_ADD_SEALS: + case TARGET_F_GET_SEALS: + default: + ret = get_errno(safe_fcntl(arg1, host_cmd, arg3)); + break; + } + return ret; +} + +#if defined(__FreeBSD_version) && __FreeBSD_version >= 1300080 +extern int __realpathat(int fd, const char *path, char *buf, size_t size, + int flags); +/* https://svnweb.freebsd.org/base?view=revision&revision=358172 */ +/* no man page */ +static inline abi_long do_freebsd_realpathat(abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + abi_long ret; + void *p, *b; + + LOCK_PATH(p, arg2); + b = lock_user(VERIFY_WRITE, arg3, arg4, 0); + if (b == NULL) { + UNLOCK_PATH(p, arg2); + return -TARGET_EFAULT; + } + + ret = get_errno(__realpathat(arg1, p, b, arg4, arg5)); + UNLOCK_PATH(p, arg2); + unlock_user(b, arg3, ret); + + return ret; +} +#endif + +#endif /* BSD_USER_FREEBSD_OS_STAT_H */ diff --git a/bsd-user/freebsd/os-sys.c b/bsd-user/freebsd/os-sys.c index 309e27b9d6..df31706558 100644 --- a/bsd-user/freebsd/os-sys.c +++ b/bsd-user/freebsd/os-sys.c @@ -17,9 +17,581 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu.h" #include "target_arch_sysarch.h" +#include + +/* + * Length for the fixed length types. + * 0 means variable length for strings and structures + * Compare with sys/kern_sysctl.c ctl_size + * Note: Not all types appear to be used in-tree. + */ +static const int guest_ctl_size[CTLTYPE + 1] = { + [CTLTYPE_INT] = sizeof(abi_int), + [CTLTYPE_UINT] = sizeof(abi_uint), + [CTLTYPE_LONG] = sizeof(abi_long), + [CTLTYPE_ULONG] = sizeof(abi_ulong), + [CTLTYPE_S8] = sizeof(int8_t), + [CTLTYPE_S16] = sizeof(int16_t), + [CTLTYPE_S32] = sizeof(int32_t), + [CTLTYPE_S64] = sizeof(int64_t), + [CTLTYPE_U8] = sizeof(uint8_t), + [CTLTYPE_U16] = sizeof(uint16_t), + [CTLTYPE_U32] = sizeof(uint32_t), + [CTLTYPE_U64] = sizeof(uint64_t), +}; + +static const int host_ctl_size[CTLTYPE + 1] = { + [CTLTYPE_INT] = sizeof(int), + [CTLTYPE_UINT] = sizeof(u_int), + [CTLTYPE_LONG] = sizeof(long), + [CTLTYPE_ULONG] = sizeof(u_long), + [CTLTYPE_S8] = sizeof(int8_t), + [CTLTYPE_S16] = sizeof(int16_t), + [CTLTYPE_S32] = sizeof(int32_t), + [CTLTYPE_S64] = sizeof(int64_t), + [CTLTYPE_U8] = sizeof(uint8_t), + [CTLTYPE_U16] = sizeof(uint16_t), + [CTLTYPE_U32] = sizeof(uint32_t), + [CTLTYPE_U64] = sizeof(uint64_t), +}; + +#ifdef TARGET_ABI32 +/* + * Limit the amount of available memory to be most of the 32-bit address + * space. 0x100c000 was arrived at through trial and error as a good + * definition of 'most'. + */ +static const abi_ulong guest_max_mem = UINT32_MAX - 0x100c000 + 1; + +static abi_ulong cap_memory(uint64_t mem) +{ + return MIN(guest_max_mem, mem); +} +#endif + +static abi_ulong scale_to_guest_pages(uint64_t pages) +{ + /* Scale pages from host to guest */ + pages = muldiv64(pages, qemu_real_host_page_size(), TARGET_PAGE_SIZE); +#ifdef TARGET_ABI32 + /* cap pages if need be */ + pages = MIN(pages, guest_max_mem / (abi_ulong)TARGET_PAGE_SIZE); +#endif + return pages; +} + +#ifdef TARGET_ABI32 +/* Used only for TARGET_ABI32 */ +static abi_long h2g_long_sat(long l) +{ + if (l > INT32_MAX) { + l = INT32_MAX; + } else if (l < INT32_MIN) { + l = INT32_MIN; + } + return l; +} + +static abi_ulong h2g_ulong_sat(u_long ul) +{ + return MIN(ul, UINT32_MAX); +} +#endif + +/* + * placeholder until bsd-user downstream upstreams this with its thread support + */ +#define bsd_get_ncpu() 1 + +/* + * This uses the undocumented oidfmt interface to find the kind of a requested + * sysctl, see /sys/kern/kern_sysctl.c:sysctl_sysctl_oidfmt() (compare to + * src/sbin/sysctl/sysctl.c) + */ +static int oidfmt(int *oid, int len, char *fmt, uint32_t *kind) +{ + int qoid[CTL_MAXNAME + 2]; + uint8_t buf[BUFSIZ]; + int i; + size_t j; + + qoid[0] = CTL_SYSCTL; + qoid[1] = CTL_SYSCTL_OIDFMT; + memcpy(qoid + 2, oid, len * sizeof(int)); + + j = sizeof(buf); + i = sysctl(qoid, len + 2, buf, &j, 0, 0); + if (i) { + return i; + } + + if (kind) { + *kind = *(uint32_t *)buf; + } + + if (fmt) { + strcpy(fmt, (char *)(buf + sizeof(uint32_t))); + } + return 0; +} + +/* + * Convert the old value from host to guest. + * + * For LONG and ULONG on ABI32, we need to 'down convert' the 8 byte quantities + * to 4 bytes. The caller setup a buffer in host memory to get this data from + * the kernel and pass it to us. We do the down conversion and adjust the length + * so the caller knows what to write as the returned length into the target when + * it copies the down converted values into the target. + * + * For normal integral types, we just need to byte swap. No size changes. + * + * For strings and node data, there's no conversion needed. + * + * For opaque data, per sysctl OID converts take care of it. + */ +static void h2g_old_sysctl(void *holdp, size_t *holdlen, uint32_t kind) +{ + size_t len; + int hlen, glen; + uint8_t *hp, *gp; + + /* + * Although rare, we can have arrays of sysctl. Both sysctl_old_ddb in + * kern_sysctl.c and show_var in sbin/sysctl/sysctl.c have code that loops + * this way. *holdlen has been set by the kernel to the host's length. + * Only LONG and ULONG on ABI32 have different sizes: see below. + */ + gp = hp = (uint8_t *)holdp; + len = 0; + hlen = host_ctl_size[kind & CTLTYPE]; + glen = guest_ctl_size[kind & CTLTYPE]; + + /* + * hlen == 0 for CTLTYPE_STRING and CTLTYPE_NODE, which need no conversion + * as well as CTLTYPE_OPAQUE, which needs special converters. + */ + if (hlen == 0) { + return; + } + + while (len < *holdlen) { + if (hlen == glen) { + switch (hlen) { + case 1: + /* Nothing needed: no byteswapping and assigning in place */ + break; + case 2: + *(uint16_t *)gp = tswap16(*(uint16_t *)hp); + break; + case 4: + *(uint32_t *)gp = tswap32(*(uint32_t *)hp); + break; + case 8: + *(uint64_t *)gp = tswap64(*(uint64_t *)hp); + break; + default: + g_assert_not_reached(); + } + } else { +#ifdef TARGET_ABI32 + /* + * Saturating assignment for the only two types that differ between + * 32-bit and 64-bit machines. All other integral types have the + * same, fixed size and will be converted w/o loss of precision + * in the above switch. + */ + switch (kind & CTLTYPE) { + case CTLTYPE_LONG: + *(abi_long *)gp = tswap32(h2g_long_sat(*(long *)hp)); + break; + case CTLTYPE_ULONG: + *(abi_ulong *)gp = tswap32(h2g_ulong_sat(*(u_long *)hp)); + break; + default: + g_assert_not_reached(); + } +#else + g_assert_not_reached(); +#endif + } + gp += glen; + hp += hlen; + len += hlen; + } +#ifdef TARGET_ABI32 + if (hlen != glen) { + *holdlen = (*holdlen / hlen) * glen; + } +#endif +} + +/* + * Convert the undocmented name2oid sysctl data for the target. + */ +static inline void sysctl_name2oid(uint32_t *holdp, size_t holdlen) +{ + size_t i, num = holdlen / sizeof(uint32_t); + + for (i = 0; i < num; i++) { + holdp[i] = tswap32(holdp[i]); + } +} + +static inline void sysctl_oidfmt(uint32_t *holdp) +{ + /* byte swap the kind */ + holdp[0] = tswap32(holdp[0]); +} + +static abi_long do_freebsd_sysctl_oid(CPUArchState *env, int32_t *snamep, + int32_t namelen, void *holdp, size_t *holdlenp, void *hnewp, + size_t newlen) +{ + uint32_t kind = 0; + abi_long ret; + size_t holdlen, oldlen; +#ifdef TARGET_ABI32 + void *old_holdp; +#endif + + holdlen = oldlen = *holdlenp; + oidfmt(snamep, namelen, NULL, &kind); + + /* Handle some arch/emulator dependent sysctl()'s here. */ + switch (snamep[0]) { + case CTL_KERN: + switch (snamep[1]) { + case KERN_USRSTACK: + if (oldlen) { + (*(abi_ulong *)holdp) = tswapal(TARGET_USRSTACK); + } + holdlen = sizeof(abi_ulong); + ret = 0; + goto out; + + case KERN_PS_STRINGS: + if (oldlen) { + (*(abi_ulong *)holdp) = tswapal(TARGET_PS_STRINGS); + } + holdlen = sizeof(abi_ulong); + ret = 0; + goto out; + + default: + break; + } + break; + + case CTL_HW: + switch (snamep[1]) { + case HW_MACHINE: + holdlen = sizeof(TARGET_HW_MACHINE); + if (holdp) { + strlcpy(holdp, TARGET_HW_MACHINE, oldlen); + } + ret = 0; + goto out; + + case HW_MACHINE_ARCH: + { + holdlen = sizeof(TARGET_HW_MACHINE_ARCH); + if (holdp) { + strlcpy(holdp, TARGET_HW_MACHINE_ARCH, oldlen); + } + ret = 0; + goto out; + } + case HW_NCPU: + if (oldlen) { + (*(abi_int *)holdp) = tswap32(bsd_get_ncpu()); + } + holdlen = sizeof(int32_t); + ret = 0; + goto out; +#if defined(TARGET_ARM) + case HW_FLOATINGPT: + if (oldlen) { + ARMCPU *cpu = env_archcpu(env); + *(abi_int *)holdp = cpu_isar_feature(aa32_vfp, cpu); + } + holdlen = sizeof(abi_int); + ret = 0; + goto out; +#endif + + +#ifdef TARGET_ABI32 + case HW_PHYSMEM: + case HW_USERMEM: + case HW_REALMEM: + holdlen = sizeof(abi_ulong); + ret = 0; + + if (oldlen) { + int mib[2] = {snamep[0], snamep[1]}; + unsigned long lvalue; + size_t len = sizeof(lvalue); + + if (sysctl(mib, 2, &lvalue, &len, NULL, 0) == -1) { + ret = -1; + } else { + lvalue = cap_memory(lvalue); + (*(abi_ulong *)holdp) = tswapal((abi_ulong)lvalue); + } + } + goto out; +#endif + + default: + { + static int oid_hw_availpages; + static int oid_hw_pagesizes; + + if (!oid_hw_availpages) { + int real_oid[CTL_MAXNAME + 2]; + size_t len = sizeof(real_oid) / sizeof(int); + + if (sysctlnametomib("hw.availpages", real_oid, &len) >= 0) { + oid_hw_availpages = real_oid[1]; + } + } + if (!oid_hw_pagesizes) { + int real_oid[CTL_MAXNAME + 2]; + size_t len = sizeof(real_oid) / sizeof(int); + + if (sysctlnametomib("hw.pagesizes", real_oid, &len) >= 0) { + oid_hw_pagesizes = real_oid[1]; + } + } + + if (oid_hw_availpages && snamep[1] == oid_hw_availpages) { + long lvalue; + size_t len = sizeof(lvalue); + + if (sysctlbyname("hw.availpages", &lvalue, &len, NULL, 0) == -1) { + ret = -1; + } else { + if (oldlen) { + lvalue = scale_to_guest_pages(lvalue); + (*(abi_ulong *)holdp) = tswapal((abi_ulong)lvalue); + } + holdlen = sizeof(abi_ulong); + ret = 0; + } + goto out; + } + + if (oid_hw_pagesizes && snamep[1] == oid_hw_pagesizes) { + if (oldlen) { + (*(abi_ulong *)holdp) = tswapal((abi_ulong)TARGET_PAGE_SIZE); + ((abi_ulong *)holdp)[1] = 0; + } + holdlen = sizeof(abi_ulong) * 2; + ret = 0; + goto out; + } + break; + } + } + break; + + default: + break; + } + +#ifdef TARGET_ABI32 + /* + * For long and ulong with a 64-bit host and a 32-bit target we have to do + * special things. holdlen here is the length provided by the target to the + * system call. So we allocate a buffer twice as large because longs are + * twice as big on the host which will be writing them. In h2g_old_sysctl + * we'll adjust them and adjust the length. + */ + if (kind == CTLTYPE_LONG || kind == CTLTYPE_ULONG) { + old_holdp = holdp; + holdlen = holdlen * 2; + holdp = g_malloc(holdlen); + } +#endif + + ret = get_errno(sysctl(snamep, namelen, holdp, &holdlen, hnewp, newlen)); + if (!ret && (holdp != 0)) { + + if (snamep[0] == CTL_SYSCTL) { + switch (snamep[1]) { + case CTL_SYSCTL_NEXT: + case CTL_SYSCTL_NAME2OID: + case CTL_SYSCTL_NEXTNOSKIP: + /* + * All of these return an OID array, so we need to convert to + * target. + */ + sysctl_name2oid(holdp, holdlen); + break; + + case CTL_SYSCTL_OIDFMT: + /* Handle oidfmt */ + sysctl_oidfmt(holdp); + break; + case CTL_SYSCTL_OIDDESCR: + case CTL_SYSCTL_OIDLABEL: + default: + /* Handle it based on the type */ + h2g_old_sysctl(holdp, &holdlen, kind); + /* NB: None of these are LONG or ULONG */ + break; + } + } else { + /* + * Need to convert from host to target. All the weird special cases + * are handled above. + */ + h2g_old_sysctl(holdp, &holdlen, kind); +#ifdef TARGET_ABI32 + /* + * For the 32-bit on 64-bit case, for longs we need to copy the + * now-converted buffer to the target and free the buffer. + */ + if (kind == CTLTYPE_LONG || kind == CTLTYPE_ULONG) { + memcpy(old_holdp, holdp, holdlen); + g_free(holdp); + holdp = old_holdp; + } +#endif + } + } + +out: + *holdlenp = holdlen; + return ret; +} + +/* + * This syscall was created to make sysctlbyname(3) more efficient, but we can't + * really provide it in bsd-user. Notably, we must always translate the names + * independently since some sysctl values have to be faked for the target + * environment, so it still has to break down to two syscalls for the underlying + * implementation. + */ +abi_long do_freebsd_sysctlbyname(CPUArchState *env, abi_ulong namep, + int32_t namelen, abi_ulong oldp, abi_ulong oldlenp, abi_ulong newp, + abi_ulong newlen) +{ + abi_long ret = -TARGET_EFAULT; + void *holdp = NULL, *hnewp = NULL; + char *snamep = NULL; + int oid[CTL_MAXNAME + 2]; + size_t holdlen, oidplen; + abi_ulong oldlen = 0; + + /* oldlenp is read/write, pre-check here for write */ + if (oldlenp) { + if (!access_ok(VERIFY_WRITE, oldlenp, sizeof(abi_ulong)) || + get_user_ual(oldlen, oldlenp)) { + goto out; + } + } + snamep = lock_user_string(namep); + if (snamep == NULL) { + goto out; + } + if (newp) { + hnewp = lock_user(VERIFY_READ, newp, newlen, 1); + if (hnewp == NULL) { + goto out; + } + } + if (oldp) { + holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0); + if (holdp == NULL) { + goto out; + } + } + holdlen = oldlen; + + oidplen = ARRAY_SIZE(oid); + if (sysctlnametomib(snamep, oid, &oidplen) != 0) { + ret = -TARGET_EINVAL; + goto out; + } + + ret = do_freebsd_sysctl_oid(env, oid, oidplen, holdp, &holdlen, hnewp, + newlen); + + /* + * writeability pre-checked above. __sysctl(2) returns ENOMEM and updates + * oldlenp for the proper size to use. + */ + if (oldlenp && (ret == 0 || ret == -TARGET_ENOMEM)) { + put_user_ual(holdlen, oldlenp); + } +out: + unlock_user(snamep, namep, 0); + unlock_user(holdp, oldp, ret == 0 ? holdlen : 0); + unlock_user(hnewp, newp, 0); + + return ret; +} + +abi_long do_freebsd_sysctl(CPUArchState *env, abi_ulong namep, int32_t namelen, + abi_ulong oldp, abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen) +{ + abi_long ret = -TARGET_EFAULT; + void *hnamep, *holdp = NULL, *hnewp = NULL; + size_t holdlen; + abi_ulong oldlen = 0; + int32_t *snamep = g_malloc(sizeof(int32_t) * namelen), *p, *q, i; + + /* oldlenp is read/write, pre-check here for write */ + if (oldlenp) { + if (!access_ok(VERIFY_WRITE, oldlenp, sizeof(abi_ulong)) || + get_user_ual(oldlen, oldlenp)) { + goto out; + } + } + hnamep = lock_user(VERIFY_READ, namep, namelen, 1); + if (hnamep == NULL) { + goto out; + } + if (newp) { + hnewp = lock_user(VERIFY_READ, newp, newlen, 1); + if (hnewp == NULL) { + goto out; + } + } + if (oldp) { + holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0); + if (holdp == NULL) { + goto out; + } + } + holdlen = oldlen; + for (p = hnamep, q = snamep, i = 0; i < namelen; p++, i++, q++) { + *q = tswap32(*p); + } + + ret = do_freebsd_sysctl_oid(env, snamep, namelen, holdp, &holdlen, hnewp, + newlen); + + /* + * writeability pre-checked above. __sysctl(2) returns ENOMEM and updates + * oldlenp for the proper size to use. + */ + if (oldlenp && (ret == 0 || ret == -TARGET_ENOMEM)) { + put_user_ual(holdlen, oldlenp); + } + unlock_user(hnamep, namep, 0); + unlock_user(holdp, oldp, ret == 0 ? holdlen : 0); +out: + g_free(snamep); + return ret; +} + /* sysarch() is architecture dependent. */ abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2) { diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c index d272478e7b..ca2f6fdb66 100644 --- a/bsd-user/freebsd/os-syscall.c +++ b/bsd-user/freebsd/os-syscall.c @@ -17,34 +17,56 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ - -/* - * We need the FreeBSD "legacy" definitions. Rust needs the FreeBSD 11 system - * calls since it doesn't use libc at all, so we have to emulate that despite - * FreeBSD 11 being EOL'd. - */ -#define _WANT_FREEBSD11_STAT -#define _WANT_FREEBSD11_STATFS -#define _WANT_FREEBSD11_DIRENT -#define _WANT_KERNEL_ERRNO -#define _WANT_SEMUN #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/path.h" #include +#include #include +#include #include #include +#include "include/gdbstub/syscalls.h" + #include "qemu.h" #include "signal-common.h" #include "user/syscall-trace.h" +/* BSD independent syscall shims */ #include "bsd-file.h" +#include "bsd-mem.h" +#include "bsd-proc.h" -void target_set_brk(abi_ulong new_brk) -{ -} +/* BSD dependent syscall shims */ +#include "os-stat.h" +#include "os-proc.h" +#include "os-misc.h" + +/* I/O */ +safe_syscall3(int, open, const char *, path, int, flags, mode_t, mode); +safe_syscall4(int, openat, int, fd, const char *, path, int, flags, mode_t, + mode); + +safe_syscall3(ssize_t, read, int, fd, void *, buf, size_t, nbytes); +safe_syscall4(ssize_t, pread, int, fd, void *, buf, size_t, nbytes, off_t, + offset); +safe_syscall3(ssize_t, readv, int, fd, const struct iovec *, iov, int, iovcnt); +safe_syscall4(ssize_t, preadv, int, fd, const struct iovec *, iov, int, iovcnt, + off_t, offset); + +safe_syscall3(ssize_t, write, int, fd, void *, buf, size_t, nbytes); +safe_syscall4(ssize_t, pwrite, int, fd, void *, buf, size_t, nbytes, off_t, + offset); +safe_syscall3(ssize_t, writev, int, fd, const struct iovec *, iov, int, iovcnt); +safe_syscall4(ssize_t, pwritev, int, fd, const struct iovec *, iov, int, iovcnt, + off_t, offset); + +/* used in os-proc */ +safe_syscall4(pid_t, wait4, pid_t, wpid, int *, status, int, options, + struct rusage *, rusage); +safe_syscall6(pid_t, wait6, idtype_t, idtype, id_t, id, int *, status, int, + options, struct __wrusage *, wrusage, siginfo_t *, infop); /* * errno conversion. @@ -74,16 +96,856 @@ bool is_error(abi_long ret) } /* - * do_syscall() should always have a single exit point at the end so that - * actions, such as logging of syscall results, can be performed. All errnos - * that do_syscall() returns must be -TARGET_. + * Unlocks a iovec. Unlike unlock_iovec, it assumes the tvec array itself is + * already locked from target_addr. It will be unlocked as well as all the iovec + * elements. + */ +static void helper_unlock_iovec(struct target_iovec *target_vec, + abi_ulong target_addr, struct iovec *vec, + int count, int copy) +{ + for (int i = 0; i < count; i++) { + abi_ulong base = tswapal(target_vec[i].iov_base); + + if (vec[i].iov_base) { + unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0); + } + } + unlock_user(target_vec, target_addr, 0); +} + +struct iovec *lock_iovec(int type, abi_ulong target_addr, + int count, int copy) +{ + struct target_iovec *target_vec; + struct iovec *vec; + abi_ulong total_len, max_len; + int i; + int err = 0; + + if (count == 0) { + errno = 0; + return NULL; + } + if (count < 0 || count > IOV_MAX) { + errno = EINVAL; + return NULL; + } + + vec = g_try_new0(struct iovec, count); + if (vec == NULL) { + errno = ENOMEM; + return NULL; + } + + target_vec = lock_user(VERIFY_READ, target_addr, + count * sizeof(struct target_iovec), 1); + if (target_vec == NULL) { + err = EFAULT; + goto fail2; + } + + max_len = 0x7fffffff & MIN(TARGET_PAGE_MASK, PAGE_MASK); + total_len = 0; + + for (i = 0; i < count; i++) { + abi_ulong base = tswapal(target_vec[i].iov_base); + abi_long len = tswapal(target_vec[i].iov_len); + + if (len < 0) { + err = EINVAL; + goto fail; + } else if (len == 0) { + /* Zero length pointer is ignored. */ + vec[i].iov_base = 0; + } else { + vec[i].iov_base = lock_user(type, base, len, copy); + /* + * If the first buffer pointer is bad, this is a fault. But + * subsequent bad buffers will result in a partial write; this is + * realized by filling the vector with null pointers and zero + * lengths. + */ + if (!vec[i].iov_base) { + if (i == 0) { + err = EFAULT; + goto fail; + } else { + /* + * Fail all the subsequent addresses, they are already + * zero'd. + */ + goto out; + } + } + if (len > max_len - total_len) { + len = max_len - total_len; + } + } + vec[i].iov_len = len; + total_len += len; + } +out: + unlock_user(target_vec, target_addr, 0); + return vec; + +fail: + helper_unlock_iovec(target_vec, target_addr, vec, i, copy); +fail2: + g_free(vec); + errno = err; + return NULL; +} + +void unlock_iovec(struct iovec *vec, abi_ulong target_addr, + int count, int copy) +{ + struct target_iovec *target_vec; + + target_vec = lock_user(VERIFY_READ, target_addr, + count * sizeof(struct target_iovec), 1); + if (target_vec) { + helper_unlock_iovec(target_vec, target_addr, vec, count, copy); + } + + g_free(vec); +} + +/* + * All errnos that freebsd_syscall() returns must be -TARGET_. + */ +static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5, abi_long arg6, abi_long arg7, + abi_long arg8) +{ + abi_long ret; + + switch (num) { + /* + * process system calls + */ + case TARGET_FREEBSD_NR_fork: /* fork(2) */ + ret = do_freebsd_fork(cpu_env); + break; + + case TARGET_FREEBSD_NR_vfork: /* vfork(2) */ + ret = do_freebsd_vfork(cpu_env); + break; + + case TARGET_FREEBSD_NR_rfork: /* rfork(2) */ + ret = do_freebsd_rfork(cpu_env, arg1); + break; + + case TARGET_FREEBSD_NR_pdfork: /* pdfork(2) */ + ret = do_freebsd_pdfork(cpu_env, arg1, arg2); + break; + + case TARGET_FREEBSD_NR_execve: /* execve(2) */ + ret = do_freebsd_execve(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_fexecve: /* fexecve(2) */ + ret = do_freebsd_fexecve(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_wait4: /* wait4(2) */ + ret = do_freebsd_wait4(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_wait6: /* wait6(2) */ + ret = do_freebsd_wait6(cpu_env, arg1, arg2, arg3, + arg4, arg5, arg6, arg7, arg8); + break; + + case TARGET_FREEBSD_NR_exit: /* exit(2) */ + ret = do_bsd_exit(cpu_env, arg1); + break; + + case TARGET_FREEBSD_NR_getgroups: /* getgroups(2) */ + ret = do_bsd_getgroups(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_setgroups: /* setgroups(2) */ + ret = do_bsd_setgroups(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_umask: /* umask(2) */ + ret = do_bsd_umask(arg1); + break; + + case TARGET_FREEBSD_NR_setlogin: /* setlogin(2) */ + ret = do_bsd_setlogin(arg1); + break; + + case TARGET_FREEBSD_NR_getlogin: /* getlogin(2) */ + ret = do_bsd_getlogin(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_getrusage: /* getrusage(2) */ + ret = do_bsd_getrusage(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_getrlimit: /* getrlimit(2) */ + ret = do_bsd_getrlimit(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_setrlimit: /* setrlimit(2) */ + ret = do_bsd_setrlimit(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_getpid: /* getpid(2) */ + ret = do_bsd_getpid(); + break; + + case TARGET_FREEBSD_NR_getppid: /* getppid(2) */ + ret = do_bsd_getppid(); + break; + + case TARGET_FREEBSD_NR_getuid: /* getuid(2) */ + ret = do_bsd_getuid(); + break; + + case TARGET_FREEBSD_NR_geteuid: /* geteuid(2) */ + ret = do_bsd_geteuid(); + break; + + case TARGET_FREEBSD_NR_getgid: /* getgid(2) */ + ret = do_bsd_getgid(); + break; + + case TARGET_FREEBSD_NR_getegid: /* getegid(2) */ + ret = do_bsd_getegid(); + break; + + case TARGET_FREEBSD_NR_setuid: /* setuid(2) */ + ret = do_bsd_setuid(arg1); + break; + + case TARGET_FREEBSD_NR_seteuid: /* seteuid(2) */ + ret = do_bsd_seteuid(arg1); + break; + + case TARGET_FREEBSD_NR_setgid: /* setgid(2) */ + ret = do_bsd_setgid(arg1); + break; + + case TARGET_FREEBSD_NR_setegid: /* setegid(2) */ + ret = do_bsd_setegid(arg1); + break; + + case TARGET_FREEBSD_NR_getpgrp: /* getpgrp(2) */ + ret = do_bsd_getpgrp(); + break; + + case TARGET_FREEBSD_NR_getpgid: /* getpgid(2) */ + ret = do_bsd_getpgid(arg1); + break; + + case TARGET_FREEBSD_NR_setpgid: /* setpgid(2) */ + ret = do_bsd_setpgid(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_setreuid: /* setreuid(2) */ + ret = do_bsd_setreuid(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_setregid: /* setregid(2) */ + ret = do_bsd_setregid(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_getresuid: /* getresuid(2) */ + ret = do_bsd_getresuid(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_getresgid: /* getresgid(2) */ + ret = do_bsd_getresgid(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_setresuid: /* setresuid(2) */ + ret = do_bsd_setresuid(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_setresgid: /* setresgid(2) */ + ret = do_bsd_setresgid(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_getsid: /* getsid(2) */ + ret = do_bsd_getsid(arg1); + break; + + case TARGET_FREEBSD_NR_setsid: /* setsid(2) */ + ret = do_bsd_setsid(); + break; + + case TARGET_FREEBSD_NR_issetugid: /* issetugid(2) */ + ret = do_bsd_issetugid(); + break; + + case TARGET_FREEBSD_NR_profil: /* profil(2) */ + ret = do_bsd_profil(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_ktrace: /* ktrace(2) */ + ret = do_bsd_ktrace(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_setloginclass: /* setloginclass(2) */ + ret = do_freebsd_setloginclass(arg1); + break; + + case TARGET_FREEBSD_NR_getloginclass: /* getloginclass(2) */ + ret = do_freebsd_getloginclass(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_pdgetpid: /* pdgetpid(2) */ + ret = do_freebsd_pdgetpid(arg1, arg2); + break; + + case TARGET_FREEBSD_NR___setugid: /* undocumented */ + ret = do_freebsd___setugid(arg1); + break; + + case TARGET_FREEBSD_NR_utrace: /* utrace(2) */ + ret = do_bsd_utrace(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_ptrace: /* ptrace(2) */ + ret = do_bsd_ptrace(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_getpriority: /* getpriority(2) */ + ret = do_bsd_getpriority(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_setpriority: /* setpriority(2) */ + ret = do_bsd_setpriority(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_procctl: /* procctl(2) */ + ret = do_freebsd_procctl(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6); + break; + + /* + * File system calls. + */ + case TARGET_FREEBSD_NR_read: /* read(2) */ + ret = do_bsd_read(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_pread: /* pread(2) */ + ret = do_bsd_pread(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6); + break; + + case TARGET_FREEBSD_NR_readv: /* readv(2) */ + ret = do_bsd_readv(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_preadv: /* preadv(2) */ + ret = do_bsd_preadv(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6); + break; + + case TARGET_FREEBSD_NR_write: /* write(2) */ + ret = do_bsd_write(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_pwrite: /* pwrite(2) */ + ret = do_bsd_pwrite(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6); + break; + + case TARGET_FREEBSD_NR_writev: /* writev(2) */ + ret = do_bsd_writev(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_pwritev: /* pwritev(2) */ + ret = do_bsd_pwritev(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6); + break; + + case TARGET_FREEBSD_NR_open: /* open(2) */ + ret = do_bsd_open(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_openat: /* openat(2) */ + ret = do_bsd_openat(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_close: /* close(2) */ + ret = do_bsd_close(arg1); + break; + + case TARGET_FREEBSD_NR_fdatasync: /* fdatasync(2) */ + ret = do_bsd_fdatasync(arg1); + break; + + case TARGET_FREEBSD_NR_fsync: /* fsync(2) */ + ret = do_bsd_fsync(arg1); + break; + + case TARGET_FREEBSD_NR_freebsd12_closefrom: /* closefrom(2) */ + ret = do_bsd_closefrom(arg1); + break; + + case TARGET_FREEBSD_NR_revoke: /* revoke(2) */ + ret = do_bsd_revoke(arg1); + break; + + case TARGET_FREEBSD_NR_access: /* access(2) */ + ret = do_bsd_access(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_eaccess: /* eaccess(2) */ + ret = do_bsd_eaccess(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_faccessat: /* faccessat(2) */ + ret = do_bsd_faccessat(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_chdir: /* chdir(2) */ + ret = do_bsd_chdir(arg1); + break; + + case TARGET_FREEBSD_NR_fchdir: /* fchdir(2) */ + ret = do_bsd_fchdir(arg1); + break; + + case TARGET_FREEBSD_NR_rename: /* rename(2) */ + ret = do_bsd_rename(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_renameat: /* renameat(2) */ + ret = do_bsd_renameat(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_link: /* link(2) */ + ret = do_bsd_link(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_linkat: /* linkat(2) */ + ret = do_bsd_linkat(arg1, arg2, arg3, arg4, arg5); + break; + + case TARGET_FREEBSD_NR_unlink: /* unlink(2) */ + ret = do_bsd_unlink(arg1); + break; + + case TARGET_FREEBSD_NR_unlinkat: /* unlinkat(2) */ + ret = do_bsd_unlinkat(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_mkdir: /* mkdir(2) */ + ret = do_bsd_mkdir(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_mkdirat: /* mkdirat(2) */ + ret = do_bsd_mkdirat(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_rmdir: /* rmdir(2) (XXX no rmdirat()?) */ + ret = do_bsd_rmdir(arg1); + break; + + case TARGET_FREEBSD_NR___getcwd: /* undocumented __getcwd() */ + ret = do_bsd___getcwd(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_dup: /* dup(2) */ + ret = do_bsd_dup(arg1); + break; + + case TARGET_FREEBSD_NR_dup2: /* dup2(2) */ + ret = do_bsd_dup2(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_truncate: /* truncate(2) */ + ret = do_bsd_truncate(cpu_env, arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_ftruncate: /* ftruncate(2) */ + ret = do_bsd_ftruncate(cpu_env, arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_acct: /* acct(2) */ + ret = do_bsd_acct(arg1); + break; + + case TARGET_FREEBSD_NR_sync: /* sync(2) */ + ret = do_bsd_sync(); + break; + + case TARGET_FREEBSD_NR_mount: /* mount(2) */ + ret = do_bsd_mount(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_unmount: /* unmount(2) */ + ret = do_bsd_unmount(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_nmount: /* nmount(2) */ + ret = do_bsd_nmount(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_symlink: /* symlink(2) */ + ret = do_bsd_symlink(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_symlinkat: /* symlinkat(2) */ + ret = do_bsd_symlinkat(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_readlink: /* readlink(2) */ + ret = do_bsd_readlink(cpu_env, arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_readlinkat: /* readlinkat(2) */ + ret = do_bsd_readlinkat(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_chmod: /* chmod(2) */ + ret = do_bsd_chmod(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fchmod: /* fchmod(2) */ + ret = do_bsd_fchmod(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_lchmod: /* lchmod(2) */ + ret = do_bsd_lchmod(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fchmodat: /* fchmodat(2) */ + ret = do_bsd_fchmodat(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_freebsd11_mknod: /* mknod(2) */ + ret = do_bsd_freebsd11_mknod(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_freebsd11_mknodat: /* mknodat(2) */ + ret = do_bsd_freebsd11_mknodat(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_mknodat: /* mknodat(2) */ + ret = do_bsd_mknodat(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6); + break; + + case TARGET_FREEBSD_NR_chown: /* chown(2) */ + ret = do_bsd_chown(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_fchown: /* fchown(2) */ + ret = do_bsd_fchown(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_lchown: /* lchown(2) */ + ret = do_bsd_lchown(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_fchownat: /* fchownat(2) */ + ret = do_bsd_fchownat(arg1, arg2, arg3, arg4, arg5); + break; + + case TARGET_FREEBSD_NR_chflags: /* chflags(2) */ + ret = do_bsd_chflags(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_lchflags: /* lchflags(2) */ + ret = do_bsd_lchflags(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fchflags: /* fchflags(2) */ + ret = do_bsd_fchflags(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_chroot: /* chroot(2) */ + ret = do_bsd_chroot(arg1); + break; + + case TARGET_FREEBSD_NR_flock: /* flock(2) */ + ret = do_bsd_flock(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_mkfifo: /* mkfifo(2) */ + ret = do_bsd_mkfifo(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_mkfifoat: /* mkfifoat(2) */ + ret = do_bsd_mkfifoat(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_pathconf: /* pathconf(2) */ + ret = do_bsd_pathconf(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_lpathconf: /* lpathconf(2) */ + ret = do_bsd_lpathconf(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fpathconf: /* fpathconf(2) */ + ret = do_bsd_fpathconf(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_undelete: /* undelete(2) */ + ret = do_bsd_undelete(arg1); + break; + + /* + * stat system calls + */ + case TARGET_FREEBSD_NR_freebsd11_stat: /* stat(2) */ + ret = do_freebsd11_stat(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_freebsd11_lstat: /* lstat(2) */ + ret = do_freebsd11_lstat(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_freebsd11_fstat: /* fstat(2) */ + ret = do_freebsd11_fstat(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fstat: /* fstat(2) */ + ret = do_freebsd_fstat(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_freebsd11_fstatat: /* fstatat(2) */ + ret = do_freebsd11_fstatat(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_fstatat: /* fstatat(2) */ + ret = do_freebsd_fstatat(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_freebsd11_nstat: /* undocumented */ + ret = do_freebsd11_nstat(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_freebsd11_nfstat: /* undocumented */ + ret = do_freebsd11_nfstat(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_freebsd11_nlstat: /* undocumented */ + ret = do_freebsd11_nlstat(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_getfh: /* getfh(2) */ + ret = do_freebsd_getfh(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_lgetfh: /* lgetfh(2) */ + ret = do_freebsd_lgetfh(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fhopen: /* fhopen(2) */ + ret = do_freebsd_fhopen(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_freebsd11_fhstat: /* fhstat(2) */ + ret = do_freebsd11_fhstat(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fhstat: /* fhstat(2) */ + ret = do_freebsd_fhstat(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_freebsd11_fhstatfs: /* fhstatfs(2) */ + ret = do_freebsd11_fhstatfs(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fhstatfs: /* fhstatfs(2) */ + ret = do_freebsd_fhstatfs(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_freebsd11_statfs: /* statfs(2) */ + ret = do_freebsd11_statfs(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_statfs: /* statfs(2) */ + ret = do_freebsd_statfs(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_freebsd11_fstatfs: /* fstatfs(2) */ + ret = do_freebsd11_fstatfs(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fstatfs: /* fstatfs(2) */ + ret = do_freebsd_fstatfs(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_freebsd11_getfsstat: /* getfsstat(2) */ + ret = do_freebsd11_getfsstat(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_getfsstat: /* getfsstat(2) */ + ret = do_freebsd_getfsstat(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_freebsd11_getdents: /* getdents(2) */ + ret = do_freebsd11_getdents(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_getdirentries: /* getdirentries(2) */ + ret = do_freebsd_getdirentries(arg1, arg2, arg3, arg4); + break; + + case TARGET_FREEBSD_NR_freebsd11_getdirentries: /* getdirentries(2) */ + ret = do_freebsd11_getdirentries(arg1, arg2, arg3, arg4); + break; + case TARGET_FREEBSD_NR_fcntl: /* fcntl(2) */ + ret = do_freebsd_fcntl(arg1, arg2, arg3); + break; + + /* + * Memory management system calls. + */ + case TARGET_FREEBSD_NR_mmap: /* mmap(2) */ + ret = do_bsd_mmap(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8); + break; + + case TARGET_FREEBSD_NR_munmap: /* munmap(2) */ + ret = do_bsd_munmap(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_mprotect: /* mprotect(2) */ + ret = do_bsd_mprotect(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_msync: /* msync(2) */ + ret = do_bsd_msync(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_mlock: /* mlock(2) */ + ret = do_bsd_mlock(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_munlock: /* munlock(2) */ + ret = do_bsd_munlock(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_mlockall: /* mlockall(2) */ + ret = do_bsd_mlockall(arg1); + break; + + case TARGET_FREEBSD_NR_munlockall: /* munlockall(2) */ + ret = do_bsd_munlockall(); + break; + + case TARGET_FREEBSD_NR_madvise: /* madvise(2) */ + ret = do_bsd_madvise(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_minherit: /* minherit(2) */ + ret = do_bsd_minherit(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_mincore: /* mincore(2) */ + ret = do_bsd_mincore(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_freebsd12_shm_open: /* shm_open(2) */ + ret = do_bsd_shm_open(arg1, arg2, arg3); + break; + +#if defined(__FreeBSD_version) && __FreeBSD_version >= 1300048 + case TARGET_FREEBSD_NR_shm_open2: /* shm_open2(2) */ + ret = do_freebsd_shm_open2(arg1, arg2, arg3, arg4, arg5); + break; +#endif + +#if defined(__FreeBSD_version) && __FreeBSD_version >= 1300049 + case TARGET_FREEBSD_NR_shm_rename: /* shm_rename(2) */ + ret = do_freebsd_shm_rename(arg1, arg2, arg3); + break; +#endif + + case TARGET_FREEBSD_NR_shm_unlink: /* shm_unlink(2) */ + ret = do_bsd_shm_unlink(arg1); + break; + + case TARGET_FREEBSD_NR_shmget: /* shmget(2) */ + ret = do_bsd_shmget(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_shmctl: /* shmctl(2) */ + ret = do_bsd_shmctl(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_shmat: /* shmat(2) */ + ret = do_bsd_shmat(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_shmdt: /* shmdt(2) */ + ret = do_bsd_shmdt(arg1); + break; + + case TARGET_FREEBSD_NR_freebsd11_vadvise: + ret = do_bsd_vadvise(); + break; + + case TARGET_FREEBSD_NR_sbrk: + ret = do_bsd_sbrk(); + break; + + case TARGET_FREEBSD_NR_sstk: + ret = do_bsd_sstk(); + break; + + /* + * Misc + */ + case TARGET_FREEBSD_NR_break: + ret = do_obreak(arg1); + break; + + /* + * sys{ctl, arch, call} + */ + case TARGET_FREEBSD_NR___sysctl: /* sysctl(3) */ + ret = do_freebsd_sysctl(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6); + break; + + case TARGET_FREEBSD_NR___sysctlbyname: /* sysctlbyname(2) */ + ret = do_freebsd_sysctlbyname(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6); + break; + + case TARGET_FREEBSD_NR_sysarch: /* sysarch(2) */ + ret = do_freebsd_sysarch(cpu_env, arg1, arg2); + break; + + default: + qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num); + ret = -TARGET_ENOSYS; + break; + } + + return ret; +} + +/* + * do_freebsd_syscall() should always have a single exit point at the end so + * that actions, such as logging of syscall results, can be performed. This + * as a wrapper around freebsd_syscall() so that actually happens. Since + * that is a singleton, modern compilers will inline it anyway... */ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6, abi_long arg7, abi_long arg8) { - return 0; + abi_long ret; + + if (do_strace) { + print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); + } + + ret = freebsd_syscall(cpu_env, num, arg1, arg2, arg3, arg4, arg5, arg6, + arg7, arg8); + if (do_strace) { + print_freebsd_syscall_ret(num, ret); + } + + return ret; } void syscall_init(void) diff --git a/bsd-user/freebsd/qemu-os.h b/bsd-user/freebsd/qemu-os.h new file mode 100644 index 0000000000..12adc50928 --- /dev/null +++ b/bsd-user/freebsd/qemu-os.h @@ -0,0 +1,50 @@ +/* + * FreeBSD conversion extern declarations + * + * Copyright (c) 2013 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef QEMU_OS_H +#define QEMU_OS_H + +/* qemu/osdep.h pulls in the rest */ + +#include +#include +#include +#include +#include +#include +#include + +struct freebsd11_stat; + +/* os-stat.c */ +abi_long h2t_freebsd11_stat(abi_ulong target_addr, + struct freebsd11_stat *host_st); +abi_long h2t_freebsd11_nstat(abi_ulong target_addr, + struct freebsd11_stat *host_st); +abi_long t2h_freebsd_fhandle(fhandle_t *host_fh, abi_ulong target_addr); +abi_long h2t_freebsd_fhandle(abi_ulong target_addr, fhandle_t *host_fh); +abi_long h2t_freebsd11_statfs(abi_ulong target_addr, + struct freebsd11_statfs *host_statfs); +abi_long target_to_host_fcntl_cmd(int cmd); +abi_long h2t_freebsd_stat(abi_ulong target_addr, + struct stat *host_st); +abi_long h2t_freebsd_statfs(abi_ulong target_addr, + struct statfs *host_statfs); + +#endif /* QEMU_OS_H */ diff --git a/bsd-user/freebsd/target_os_siginfo.h b/bsd-user/freebsd/target_os_siginfo.h index 4573738752..6c282d8502 100644 --- a/bsd-user/freebsd/target_os_siginfo.h +++ b/bsd-user/freebsd/target_os_siginfo.h @@ -72,7 +72,7 @@ typedef struct target_siginfo { int32_t _mqd; } _mesgp; - /* SIGPOLL -- Not really genreated in FreeBSD ??? */ + /* SIGPOLL -- Not really generated in FreeBSD ??? */ struct { int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ } _poll; diff --git a/bsd-user/freebsd/target_os_stack.h b/bsd-user/freebsd/target_os_stack.h index 0590133291..d15fc3263f 100644 --- a/bsd-user/freebsd/target_os_stack.h +++ b/bsd-user/freebsd/target_os_stack.h @@ -25,7 +25,7 @@ #include "qemu/guest-random.h" /* - * The inital FreeBSD stack is as follows: + * The initial FreeBSD stack is as follows: * (see kern/kern_exec.c exec_copyout_strings() ) * * Hi Address -> char **ps_argvstr (struct ps_strings for ps, w, etc.) @@ -59,7 +59,7 @@ static inline int setup_initial_stack(struct bsd_binprm *bprm, /* Save some space for ps_strings. */ p -= sizeof(struct target_ps_strings); - /* Add machine depedent sigcode. */ + /* Add machine dependent sigcode. */ p -= TARGET_SZSIGCODE; if (setup_sigtramp(p, (unsigned)offsetof(struct target_sigframe, sf_uc), TARGET_FREEBSD_NR_sigreturn)) { diff --git a/bsd-user/freebsd/target_os_user.h b/bsd-user/freebsd/target_os_user.h index f036a32343..1ca7b5ab17 100644 --- a/bsd-user/freebsd/target_os_user.h +++ b/bsd-user/freebsd/target_os_user.h @@ -26,7 +26,7 @@ struct target_priority { uint8_t pri_class; /* Scheduling class. */ uint8_t pri_level; /* Normal priority level. */ - uint8_t pri_native; /* Priority before propogation. */ + uint8_t pri_native; /* Priority before propagation. */ uint8_t pri_user; /* User priority based on p_cpu and p_nice. */ }; diff --git a/bsd-user/host/i386/host-signal.h b/bsd-user/host/i386/host-signal.h index 169e61b154..ffdfaba534 100644 --- a/bsd-user/host/i386/host-signal.h +++ b/bsd-user/host/i386/host-signal.h @@ -9,6 +9,7 @@ #ifndef I386_HOST_SIGNAL_H #define I386_HOST_SIGNAL_H +#include #include #include #include diff --git a/bsd-user/host/x86_64/host-signal.h b/bsd-user/host/x86_64/host-signal.h index 47ca19f881..32ac4e4180 100644 --- a/bsd-user/host/x86_64/host-signal.h +++ b/bsd-user/host/x86_64/host-signal.h @@ -9,6 +9,7 @@ #ifndef X86_64_HOST_SIGNAL_H #define X86_64_HOST_SIGNAL_H +#include #include #include #include diff --git a/bsd-user/i386/signal.c b/bsd-user/i386/signal.c index 5dd975ce56..a3131047b8 100644 --- a/bsd-user/i386/signal.c +++ b/bsd-user/i386/signal.c @@ -17,6 +17,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu.h" /* diff --git a/bsd-user/i386/target_arch_cpu.c b/bsd-user/i386/target_arch_cpu.c index d349e45299..2a3af2ddef 100644 --- a/bsd-user/i386/target_arch_cpu.c +++ b/bsd-user/i386/target_arch_cpu.c @@ -17,9 +17,8 @@ * along with this program; if not, see . */ -#include - #include "qemu/osdep.h" + #include "cpu.h" #include "qemu.h" #include "qemu/timer.h" diff --git a/bsd-user/i386/target_arch_cpu.h b/bsd-user/i386/target_arch_cpu.h index d792dc720f..9bf2c4244b 100644 --- a/bsd-user/i386/target_arch_cpu.h +++ b/bsd-user/i386/target_arch_cpu.h @@ -164,6 +164,10 @@ static inline void target_cpu_loop(CPUX86State *env) } break; + case EXCP_SYSCALL: + /* doesn't do anything */ + break; + case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; diff --git a/bsd-user/i386/target_arch_elf.h b/bsd-user/i386/target_arch_elf.h index cbcd1f08e2..4ac27b02e7 100644 --- a/bsd-user/i386/target_arch_elf.h +++ b/bsd-user/i386/target_arch_elf.h @@ -20,7 +20,6 @@ #ifndef TARGET_ARCH_ELF_H #define TARGET_ARCH_ELF_H -#define ELF_START_MMAP 0x80000000 #define ELF_ET_DYN_LOAD_ADDR 0x01001000 #define elf_check_arch(x) (((x) == EM_386) || ((x) == EM_486)) diff --git a/bsd-user/main.c b/bsd-user/main.c index 6f09180d65..01b313756e 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -18,12 +18,10 @@ * along with this program; if not, see . */ -#include -#include +#include "qemu/osdep.h" #include #include -#include "qemu/osdep.h" #include "qemu/help-texts.h" #include "qemu/units.h" #include "qemu/accel.h" @@ -38,7 +36,7 @@ #include "qemu/help_option.h" #include "qemu/module.h" #include "exec/exec-all.h" -#include "tcg/tcg.h" +#include "tcg/startup.h" #include "qemu/timer.h" #include "qemu/envlist.h" #include "qemu/cutils.h" @@ -46,11 +44,19 @@ #include "trace/control.h" #include "crypto/init.h" #include "qemu/guest-random.h" +#include "gdbstub/user.h" #include "host-os.h" #include "target_arch_cpu.h" -int singlestep; + +/* + * TODO: Remove these and rely only on qemu_real_host_page_size(). + */ +uintptr_t qemu_host_page_size; +intptr_t qemu_host_page_mask; + +static bool opt_one_insn_per_tb; uintptr_t guest_base; bool have_guest_base; /* @@ -69,13 +75,9 @@ bool have_guest_base; # if HOST_LONG_BITS > TARGET_VIRT_ADDR_SPACE_BITS # if TARGET_VIRT_ADDR_SPACE_BITS == 32 && \ (TARGET_LONG_BITS == 32 || defined(TARGET_ABI32)) -/* - * There are a number of places where we assign reserved_va to a variable - * of type abi_ulong and expect it to fit. Avoid the last page. - */ -# define MAX_RESERVED_VA (0xfffffffful & TARGET_PAGE_MASK) +# define MAX_RESERVED_VA 0xfffffffful # else -# define MAX_RESERVED_VA (1ul << TARGET_VIRT_ADDR_SPACE_BITS) +# define MAX_RESERVED_VA ((1ul << TARGET_VIRT_ADDR_SPACE_BITS) - 1) # endif # else # define MAX_RESERVED_VA 0 @@ -93,7 +95,7 @@ unsigned long reserved_va = MAX_RESERVED_VA; unsigned long reserved_va; #endif -static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; +const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; const char *qemu_uname_release; char qemu_proc_pathname[PATH_MAX]; /* full path to exeutable */ @@ -111,10 +113,13 @@ void fork_start(void) start_exclusive(); cpu_list_lock(); mmap_fork_start(); + gdbserver_fork_start(); } -void fork_end(int child) +void fork_end(pid_t pid) { + bool child = pid == 0; + if (child) { CPUState *cpu, *next_cpu; /* @@ -123,7 +128,7 @@ void fork_end(int child) */ CPU_FOREACH_SAFE(cpu, next_cpu) { if (cpu != thread_cpu) { - QTAILQ_REMOVE_RCU(&cpus, cpu, node); + QTAILQ_REMOVE_RCU(&cpus_queue, cpu, node); } } mmap_fork_end(child); @@ -132,10 +137,12 @@ void fork_end(int child) * state, so we don't need to end_exclusive() here. */ qemu_init_cpu_list(); - gdbserver_fork(thread_cpu); + get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id(); + gdbserver_fork_end(thread_cpu, pid); } else { mmap_fork_end(child); cpu_list_unlock(); + gdbserver_fork_end(thread_cpu, pid); end_exclusive(); } } @@ -167,7 +174,7 @@ static void usage(void) "-d item1[,...] enable logging of specified items\n" " (use '-d help' for a list of log items)\n" "-D logfile write logs to 'logfile' (default stderr)\n" - "-singlestep always run in singlestep mode\n" + "-one-insn-per-tb run with one guest instruction per emulated TB\n" "-strace log system calls\n" "-trace [[enable=]][,events=][,file=]\n" " specify tracing options\n" @@ -299,11 +306,22 @@ int main(int argc, char **argv) envlist = envlist_create(); - /* add current environment into the list */ + /* + * add current environment into the list + * envlist_setenv adds to the front of the list; to preserve environ + * order add from back to front + */ for (wrk = environ; *wrk != NULL; wrk++) { + continue; + } + while (wrk != environ) { + wrk--; (void) envlist_setenv(envlist, *wrk); } + qemu_host_page_size = getpagesize(); + qemu_host_page_size = MAX(qemu_host_page_size, TARGET_PAGE_SIZE); + cpu_model = NULL; qemu_add_opts(&qemu_trace_opts); @@ -361,11 +379,12 @@ int main(int argc, char **argv) } else if (!strcmp(r, "L")) { interp_prefix = argv[optind++]; } else if (!strcmp(r, "p")) { - qemu_host_page_size = atoi(argv[optind++]); - if (qemu_host_page_size == 0 || - (qemu_host_page_size & (qemu_host_page_size - 1)) != 0) { - fprintf(stderr, "page size must be a power of two\n"); - exit(1); + unsigned size, want = qemu_real_host_page_size(); + + r = argv[optind++]; + if (qemu_strtoui(r, NULL, 10, &size) || size != want) { + warn_report("Deprecated page size option cannot " + "change host page size (%u)", want); } } else if (!strcmp(r, "g")) { gdbstub = g_strdup(argv[optind++]); @@ -374,10 +393,7 @@ int main(int argc, char **argv) } else if (!strcmp(r, "cpu")) { cpu_model = argv[optind++]; if (is_help_option(cpu_model)) { - /* XXX: implement xxx_cpu_list for targets that still miss it */ -#if defined(cpu_list) - cpu_list(); -#endif + list_cpus(); exit(1); } } else if (!strcmp(r, "B")) { @@ -390,8 +406,8 @@ int main(int argc, char **argv) (void) envlist_unsetenv(envlist, "LD_PRELOAD"); } else if (!strcmp(r, "seed")) { seed_optarg = optarg; - } else if (!strcmp(r, "singlestep")) { - singlestep = 1; + } else if (!strcmp(r, "one-insn-per-tb")) { + opt_one_insn_per_tb = true; } else if (!strcmp(r, "strace")) { do_strace = 1; } else if (!strcmp(r, "trace")) { @@ -403,6 +419,8 @@ int main(int argc, char **argv) } } + qemu_host_page_mask = -qemu_host_page_size; + /* init debug */ { int mask = 0; @@ -449,13 +467,16 @@ int main(int argc, char **argv) /* init tcg before creating CPUs and to get qemu_host_page_size */ { - AccelClass *ac = ACCEL_GET_CLASS(current_accel()); + AccelState *accel = current_accel(); + AccelClass *ac = ACCEL_GET_CLASS(accel); accel_init_interfaces(ac); + object_property_set_bool(OBJECT(accel), "one-insn-per-tb", + opt_one_insn_per_tb, &error_abort); ac->init_machine(NULL); } cpu = cpu_create(cpu_type); - env = cpu->env_ptr; + env = cpu_env(cpu); cpu_reset(cpu); thread_cpu = cpu; @@ -466,10 +487,6 @@ int main(int argc, char **argv) target_environ = envlist_to_environ(envlist, NULL); envlist_free(envlist); - if (reserved_va) { - mmap_next_start = reserved_va; - } - { Error *err = NULL; if (seed_optarg != NULL) { @@ -487,7 +504,49 @@ int main(int argc, char **argv) * Now that page sizes are configured we can do * proper page alignment for guest_base. */ - guest_base = HOST_PAGE_ALIGN(guest_base); + if (have_guest_base) { + if (guest_base & ~qemu_host_page_mask) { + error_report("Selected guest base not host page aligned"); + exit(1); + } + } + + /* + * If reserving host virtual address space, do so now. + * Combined with '-B', ensure that the chosen range is free. + */ + if (reserved_va) { + void *p; + + if (have_guest_base) { + p = mmap((void *)guest_base, reserved_va + 1, PROT_NONE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_EXCL, -1, 0); + } else { + p = mmap(NULL, reserved_va + 1, PROT_NONE, + MAP_ANON | MAP_PRIVATE, -1, 0); + } + if (p == MAP_FAILED) { + const char *err = strerror(errno); + char *sz = size_to_str(reserved_va + 1); + + if (have_guest_base) { + error_report("Cannot allocate %s bytes at -B %p for guest " + "address space: %s", sz, (void *)guest_base, err); + } else { + error_report("Cannot allocate %s bytes for guest " + "address space: %s", sz, err); + } + exit(1); + } + guest_base = (uintptr_t)p; + have_guest_base = true; + + /* Ensure that mmap_next_start is within range. */ + if (reserved_va <= mmap_next_start) { + mmap_next_start = (reserved_va / 4 * 3) + & TARGET_PAGE_MASK & qemu_host_page_mask; + } + } if (loader_exec(filename, argv + optind, target_environ, regs, info, &bprm) != 0) { @@ -508,8 +567,6 @@ int main(int argc, char **argv) fprintf(f, "page layout changed following binary load\n"); page_dump(f); - fprintf(f, "start_brk 0x" TARGET_ABI_FMT_lx "\n", - info->start_brk); fprintf(f, "end_code 0x" TARGET_ABI_FMT_lx "\n", info->end_code); fprintf(f, "start_code 0x" TARGET_ABI_FMT_lx "\n", @@ -543,13 +600,13 @@ int main(int argc, char **argv) * generating the prologue until now so that the prologue can take * the real value of GUEST_BASE into account. */ - tcg_prologue_init(tcg_ctx); + tcg_prologue_init(); target_cpu_init(env, regs); if (gdbstub) { gdbserver_start(gdbstub); - gdb_handlesig(cpu, 0); + gdb_handlesig(cpu, 0, NULL, NULL, 0); } cpu_loop(env); /* never exits */ diff --git a/bsd-user/meson.build b/bsd-user/meson.build index 5243122fc5..39bad0ae33 100644 --- a/bsd-user/meson.build +++ b/bsd-user/meson.build @@ -7,6 +7,8 @@ bsd_user_ss = ss.source_set() common_user_inc += include_directories('include') bsd_user_ss.add(files( + 'bsd-mem.c', + 'bsd-proc.c', 'bsdload.c', 'elfload.c', 'main.c', @@ -16,7 +18,12 @@ bsd_user_ss.add(files( 'uaccess.c', )) +elf = cc.find_library('elf', required: true) +procstat = cc.find_library('procstat', required: true) +kvm = cc.find_library('kvm', required: true) +bsd_user_ss.add(elf, procstat, kvm) + # Pull in the OS-specific build glue, if any -subdir(targetos) +subdir(host_os) specific_ss.add_all(when: 'CONFIG_BSD_USER', if_true: bsd_user_ss) diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index e54e26de17..3ef11b2807 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -32,6 +32,7 @@ void mmap_lock(void) void mmap_unlock(void) { + assert(mmap_lock_count > 0); if (--mmap_lock_count == 0) { pthread_mutex_unlock(&mmap_mutex); } @@ -118,7 +119,7 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot) if (ret != 0) goto error; } - page_set_flags(start, start + len, prot | PAGE_VALID); + page_set_flags(start, start + len - 1, prot | PAGE_VALID); mmap_unlock(); return 0; error: @@ -213,8 +214,6 @@ static int mmap_frag(abi_ulong real_start, #endif abi_ulong mmap_next_start = TASK_UNMAPPED_BASE; -unsigned long last_brk; - /* * Subroutine of mmap_find_vma, used when we have pre-allocated a chunk of guest * address space. @@ -222,50 +221,16 @@ unsigned long last_brk; static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size, abi_ulong alignment) { - abi_ulong addr; - abi_ulong end_addr; - int prot; - int looped = 0; + abi_ulong ret; - if (size > reserved_va) { - return (abi_ulong)-1; + ret = page_find_range_empty(start, reserved_va, size, alignment); + if (ret == -1 && start > TARGET_PAGE_SIZE) { + /* Restart at the beginning of the address space. */ + ret = page_find_range_empty(TARGET_PAGE_SIZE, start - 1, + size, alignment); } - size = HOST_PAGE_ALIGN(size) + alignment; - end_addr = start + size; - if (end_addr > reserved_va) { - end_addr = reserved_va; - } - addr = end_addr - qemu_host_page_size; - - while (1) { - if (addr > end_addr) { - if (looped) { - return (abi_ulong)-1; - } - end_addr = reserved_va; - addr = end_addr - qemu_host_page_size; - looped = 1; - continue; - } - prot = page_get_flags(addr); - if (prot) { - end_addr = addr; - } - if (end_addr - addr >= size) { - break; - } - addr -= qemu_host_page_size; - } - - if (start == mmap_next_start) { - mmap_next_start = addr; - } - /* addr is sufficiently low to align it up */ - if (alignment != 0) { - addr = (addr + alignment) & ~(alignment - 1); - } - return addr; + return ret; } /* @@ -293,7 +258,8 @@ static abi_ulong mmap_find_vma_aligned(abi_ulong start, abi_ulong size, if (reserved_va) { return mmap_find_vma_reserved(start, size, - (alignment != 0 ? 1 << alignment : 0)); + (alignment != 0 ? 1 << alignment : + MAX(qemu_host_page_size, TARGET_PAGE_SIZE))); } addr = start; @@ -609,7 +575,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, } /* Reject the mapping if any page within the range is mapped */ - if ((flags & MAP_EXCL) && page_check_range(start, len, 0) < 0) { + if ((flags & MAP_EXCL) && !page_check_range_empty(start, end - 1)) { errno = EINVAL; goto fail; } @@ -656,14 +622,13 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, } } the_end1: - page_set_flags(start, start + len, prot | PAGE_VALID); + page_set_flags(start, start + len - 1, prot | PAGE_VALID); the_end: #ifdef DEBUG_MMAP printf("ret=0x" TARGET_ABI_FMT_lx "\n", start); page_dump(stdout); printf("\n"); #endif - tb_invalidate_phys_range(start, start + len); mmap_unlock(); return start; fail: @@ -671,7 +636,7 @@ fail: return -1; } -static void mmap_reserve(abi_ulong start, abi_ulong size) +void mmap_reserve(abi_ulong start, abi_ulong size) { abi_ulong real_start; abi_ulong real_end; @@ -768,8 +733,7 @@ int target_munmap(abi_ulong start, abi_ulong len) } if (ret == 0) { - page_set_flags(start, start + len, 0); - tb_invalidate_phys_range(start, start + len); + page_set_flags(start, start + len - 1, 0); } mmap_unlock(); return ret; diff --git a/bsd-user/qemu-bsd.h b/bsd-user/qemu-bsd.h new file mode 100644 index 0000000000..ffc64bb244 --- /dev/null +++ b/bsd-user/qemu-bsd.h @@ -0,0 +1,58 @@ +/* + * BSD conversion extern declarations + * + * Copyright (c) 2013 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef QEMU_BSD_H +#define QEMU_BSD_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* bsd-proc.c */ +int target_to_host_resource(int code); +rlim_t target_to_host_rlim(abi_llong target_rlim); +abi_llong host_to_target_rlim(rlim_t rlim); +abi_long host_to_target_rusage(abi_ulong target_addr, + const struct rusage *rusage); +abi_long host_to_target_wrusage(abi_ulong target_addr, + const struct __wrusage *wrusage); +int host_to_target_waitstatus(int status); +void h2g_rusage(const struct rusage *rusage, + struct target_freebsd_rusage *target_rusage); + +/* bsd-mem.c */ +void target_to_host_ipc_perm__locked(struct ipc_perm *host_ip, + struct target_ipc_perm *target_ip); +void host_to_target_ipc_perm__locked(struct target_ipc_perm *target_ip, + struct ipc_perm *host_ip); +abi_long target_to_host_shmid_ds(struct shmid_ds *host_sd, + abi_ulong target_addr); +abi_long host_to_target_shmid_ds(abi_ulong target_addr, + struct shmid_ds *host_sd); + +#endif /* QEMU_BSD_H */ diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index be6105385e..1b0a591d2d 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -17,7 +17,6 @@ #ifndef QEMU_H #define QEMU_H -#include "qemu/osdep.h" #include "cpu.h" #include "qemu/units.h" #include "exec/cpu_ldst.h" @@ -37,6 +36,15 @@ extern char **environ; #include "target_os_signal.h" #include "target.h" #include "exec/gdbstub.h" +#include "qemu/clang-tsa.h" + +#include "qemu-os.h" +/* + * TODO: Remove these and rely only on qemu_real_host_page_size(). + */ +extern uintptr_t qemu_host_page_size; +extern intptr_t qemu_host_page_mask; +#define HOST_PAGE_ALIGN(addr) ROUND_UP((addr), qemu_host_page_size) /* * This struct is used to hold certain information about the image. Basically, @@ -50,10 +58,7 @@ struct image_info { abi_ulong end_code; abi_ulong start_data; abi_ulong end_data; - abi_ulong start_brk; abi_ulong brk; - abi_ulong start_mmap; - abi_ulong mmap; abi_ulong rss; abi_ulong start_stack; abi_ulong entry; @@ -112,13 +117,19 @@ typedef struct TaskState { struct target_sigaltstack sigaltstack_used; } __attribute__((aligned(16))) TaskState; +static inline TaskState *get_task_state(CPUState *cs) +{ + return cs->opaque; +} + void stop_all_tasks(void); +extern const char *interp_prefix; extern const char *qemu_uname_release; /* * TARGET_ARG_MAX defines the number of bytes allocated for arguments * and envelope for the new program. 256k should suffice for a reasonable - * maxiumum env+arg in 32-bit environments, bump it up to 512k for !ILP32 + * maximum env+arg in 32-bit environments, bump it up to 512k for !ILP32 * platforms. */ #if TARGET_ABI_BITS > 32 @@ -181,7 +192,7 @@ void cpu_loop(CPUArchState *env); char *target_strerror(int err); int get_osversion(void); void fork_start(void); -void fork_end(int child); +void fork_end(pid_t pid); #include "qemu/log.h" @@ -232,11 +243,11 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, abi_ulong new_size, unsigned long flags, abi_ulong new_addr); int target_msync(abi_ulong start, abi_ulong len, int flags); -extern unsigned long last_brk; extern abi_ulong mmap_next_start; abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size); -void mmap_fork_start(void); -void mmap_fork_end(int child); +void mmap_reserve(abi_ulong start, abi_ulong size); +void TSA_NO_TSA mmap_fork_start(void); +void TSA_NO_TSA mmap_fork_end(int child); /* main.c */ extern char qemu_proc_pathname[]; @@ -252,7 +263,18 @@ abi_long get_errno(abi_long ret); bool is_error(abi_long ret); int host_to_target_errno(int err); +/* os-proc.c */ +abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp, + abi_ulong guest_envp, int do_fexec); +abi_long do_freebsd_procctl(void *cpu_env, int idtype, abi_ulong arg2, + abi_ulong arg3, abi_ulong arg4, abi_ulong arg5, abi_ulong arg6); + /* os-sys.c */ +abi_long do_freebsd_sysctl(CPUArchState *env, abi_ulong namep, int32_t namelen, + abi_ulong oldp, abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen); +abi_long do_freebsd_sysctlbyname(CPUArchState *env, abi_ulong namep, + int32_t namelen, abi_ulong oldp, abi_ulong oldlenp, abi_ulong newp, + abi_ulong newlen); abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2); /* user access */ @@ -262,7 +284,7 @@ abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2); static inline bool access_ok(int type, abi_ulong addr, abi_ulong size) { - return page_check_range((target_ulong)addr, size, type) == 0; + return page_check_range((target_ulong)addr, size, type); } /* @@ -271,50 +293,64 @@ static inline bool access_ok(int type, abi_ulong addr, abi_ulong size) * These are usually used to access struct data members once the struct has been * locked - usually with lock_user_struct(). */ -#define __put_user(x, hptr)\ -({\ - int size = sizeof(*hptr);\ - switch (size) {\ - case 1:\ - *(uint8_t *)(hptr) = (uint8_t)(typeof(*hptr))(x);\ - break;\ - case 2:\ - *(uint16_t *)(hptr) = tswap16((typeof(*hptr))(x));\ - break;\ - case 4:\ - *(uint32_t *)(hptr) = tswap32((typeof(*hptr))(x));\ - break;\ - case 8:\ - *(uint64_t *)(hptr) = tswap64((typeof(*hptr))(x));\ - break;\ - default:\ - abort();\ - } \ - 0;\ -}) -#define __get_user(x, hptr) \ -({\ - int size = sizeof(*hptr);\ - switch (size) {\ - case 1:\ - x = (typeof(*hptr))*(uint8_t *)(hptr);\ - break;\ - case 2:\ - x = (typeof(*hptr))tswap16(*(uint16_t *)(hptr));\ - break;\ - case 4:\ - x = (typeof(*hptr))tswap32(*(uint32_t *)(hptr));\ - break;\ - case 8:\ - x = (typeof(*hptr))tswap64(*(uint64_t *)(hptr));\ - break;\ - default:\ - x = 0;\ - abort();\ - } \ - 0;\ -}) +/* + * Tricky points: + * - Use __builtin_choose_expr to avoid type promotion from ?:, + * - Invalid sizes result in a compile time error stemming from + * the fact that abort has no parameters. + * - It's easier to use the endian-specific unaligned load/store + * functions than host-endian unaligned load/store plus tswapN. + * - The pragmas are necessary only to silence a clang false-positive + * warning: see https://bugs.llvm.org/show_bug.cgi?id=39113 . + * - gcc has bugs in its _Pragma() support in some versions, eg + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83256 -- so we only + * include the warning-suppression pragmas for clang + */ +#if defined(__clang__) && __has_warning("-Waddress-of-packed-member") +#define PRAGMA_DISABLE_PACKED_WARNING \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Waddress-of-packed-member\"") + +#define PRAGMA_REENABLE_PACKED_WARNING \ + _Pragma("GCC diagnostic pop") + +#else +#define PRAGMA_DISABLE_PACKED_WARNING +#define PRAGMA_REENABLE_PACKED_WARNING +#endif + +#define __put_user_e(x, hptr, e) \ + do { \ + PRAGMA_DISABLE_PACKED_WARNING; \ + (__builtin_choose_expr(sizeof(*(hptr)) == 1, stb_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 2, stw_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 4, stl_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 8, stq_##e##_p, abort)))) \ + ((hptr), (x)), (void)0); \ + PRAGMA_REENABLE_PACKED_WARNING; \ + } while (0) + +#define __get_user_e(x, hptr, e) \ + do { \ + PRAGMA_DISABLE_PACKED_WARNING; \ + ((x) = (typeof(*hptr))( \ + __builtin_choose_expr(sizeof(*(hptr)) == 1, ldub_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 2, lduw_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 4, ldl_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 8, ldq_##e##_p, abort)))) \ + (hptr)), (void)0); \ + PRAGMA_REENABLE_PACKED_WARNING; \ + } while (0) + + +#if TARGET_BIG_ENDIAN +# define __put_user(x, hptr) __put_user_e(x, hptr, be) +# define __get_user(x, hptr) __get_user_e(x, hptr, be) +#else +# define __put_user(x, hptr) __put_user_e(x, hptr, le) +# define __get_user(x, hptr) __get_user_e(x, hptr, le) +#endif /* * put_user()/get_user() take a guest address and check access @@ -327,10 +363,10 @@ static inline bool access_ok(int type, abi_ulong addr, abi_ulong size) ({ \ abi_ulong __gaddr = (gaddr); \ target_type *__hptr; \ - abi_long __ret; \ + abi_long __ret = 0; \ __hptr = lock_user(VERIFY_WRITE, __gaddr, sizeof(target_type), 0); \ if (__hptr) { \ - __ret = __put_user((x), __hptr); \ + __put_user((x), __hptr); \ unlock_user(__hptr, __gaddr, sizeof(target_type)); \ } else \ __ret = -TARGET_EFAULT; \ @@ -341,10 +377,10 @@ static inline bool access_ok(int type, abi_ulong addr, abi_ulong size) ({ \ abi_ulong __gaddr = (gaddr); \ target_type *__hptr; \ - abi_long __ret; \ + abi_long __ret = 0; \ __hptr = lock_user(VERIFY_READ, __gaddr, sizeof(target_type), 1); \ if (__hptr) { \ - __ret = __get_user((x), __hptr); \ + __get_user((x), __hptr); \ unlock_user(__hptr, __gaddr, 0); \ } else { \ (x) = 0; \ diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index 6f90345bb2..77d7c7a78b 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -35,6 +35,7 @@ int do_sigaction(int sig, const struct target_sigaction *act, abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); long do_sigreturn(CPUArchState *env, abi_ulong addr); void force_sig_fault(int sig, int code, abi_ulong addr); +void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info); int host_to_target_signal(int sig); void host_to_target_sigset(target_sigset_t *d, const sigset_t *s); void process_pending_signals(CPUArchState *env); @@ -49,11 +50,11 @@ void target_to_host_sigset(sigset_t *d, const target_sigset_t *s); * union in target_siginfo is valid. This only applies between * host_to_target_siginfo_noswap() and tswap_siginfo(); it does not appear * either within host siginfo_t or in target_siginfo structures which we get - * from the guest userspace program. Linux kenrels use this internally, but BSD + * from the guest userspace program. Linux kernels use this internally, but BSD * kernels don't do this, but its a useful abstraction. * * The linux-user version of this uses the top 16 bits, but FreeBSD's SI_USER - * and other signal indepenent SI_ codes have bit 16 set, so we only use the top + * and other signal independent SI_ codes have bit 16 set, so we only use the top * byte instead. * * For FreeBSD, we have si_pid, si_uid, si_status, and si_addr always. Linux and diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 58a5386395..e5a773ddde 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -21,11 +21,15 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu.h" +#include "gdbstub/user.h" #include "signal-common.h" #include "trace.h" #include "hw/core/tcg-cpu-ops.h" #include "host-signal.h" +/* target_siginfo_t must fit in gdbstub's siginfo save area. */ +QEMU_BUILD_BUG_ON(sizeof(target_siginfo_t) > MAX_SIGINFO_LENGTH); + static struct target_sigaction sigact_table[TARGET_NSIG]; static void host_signal_handler(int host_sig, siginfo_t *info, void *puc); static void target_to_host_sigset_internal(sigset_t *d, @@ -43,7 +47,7 @@ static inline int sas_ss_flags(TaskState *ts, unsigned long sp) } /* - * The BSD ABIs use the same singal numbers across all the CPU architectures, so + * The BSD ABIs use the same signal numbers across all the CPU architectures, so * (unlike Linux) these functions are just the identity mapping. This might not * be true for XyzBSD running on AbcBSD, which doesn't currently work. */ @@ -240,7 +244,7 @@ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, #endif /* * Unsure that this can actually be generated, and our support for - * capsicum is somewhere between weak and non-existant, but if we get + * capsicum is somewhere between weak and non-existent, but if we get * one, then we know what to save. */ #ifdef QEMU_SI_CAPSICUM @@ -310,15 +314,21 @@ static void tswap_siginfo(target_siginfo_t *tinfo, const target_siginfo_t *info) } } +void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info) +{ + host_to_target_siginfo_noswap(tinfo, info); + tswap_siginfo(tinfo, tinfo); +} + int block_signals(void) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); sigset_t set; /* * It's OK to block everything including SIGSEGV, because we won't run any * further guest code before unblocking signals in - * process_pending_signals(). We depend on the FreeBSD behaivor here where + * process_pending_signals(). We depend on the FreeBSD behavior here where * this will only affect this thread's signal mask. We don't use * pthread_sigmask which might seem more correct because that routine also * does odd things with SIGCANCEL to implement pthread_cancel(). @@ -350,9 +360,9 @@ static int core_dump_signal(int sig) static G_NORETURN void dump_core_and_abort(int target_sig) { - CPUArchState *env = thread_cpu->env_ptr; - CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + CPUState *cpu = thread_cpu; + CPUArchState *env = cpu_env(cpu); + TaskState *ts = get_task_state(cpu); int core_dumped = 0; int host_sig; struct sigaction act; @@ -414,7 +424,7 @@ void queue_signal(CPUArchState *env, int sig, int si_type, target_siginfo_t *info) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); trace_user_queue_signal(env, sig); @@ -456,21 +466,19 @@ static int fatal_signal(int sig) void force_sig_fault(int sig, int code, abi_ulong addr) { CPUState *cpu = thread_cpu; - CPUArchState *env = cpu->env_ptr; target_siginfo_t info = {}; info.si_signo = sig; info.si_errno = 0; info.si_code = code; info.si_addr = addr; - queue_signal(env, sig, QEMU_SI_FAULT, &info); + queue_signal(cpu_env(cpu), sig, QEMU_SI_FAULT, &info); } static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) { - CPUArchState *env = thread_cpu->env_ptr; - CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + CPUState *cpu = thread_cpu; + TaskState *ts = get_task_state(cpu); target_siginfo_t tinfo; ucontext_t *uc = puc; struct emulated_sigtable *k; @@ -579,7 +587,7 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) /* compare to kern/kern_sig.c sys_sigaltstack() and kern_sigaltstack() */ abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); int ret; target_stack_t oss; @@ -708,7 +716,7 @@ int do_sigaction(int sig, const struct target_sigaction *act, static inline abi_ulong get_sigframe(struct target_sigaction *ka, CPUArchState *env, size_t frame_size) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); abi_ulong sp; /* Use default user stack */ @@ -783,13 +791,10 @@ static int reset_signal_mask(target_ucontext_t *ucontext) int i; sigset_t blocked; target_sigset_t target_set; - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); for (i = 0; i < TARGET_NSIG_WORDS; i++) { - if (__get_user(target_set.__bits[i], - &ucontext->uc_sigmask.__bits[i])) { - return -TARGET_EFAULT; - } + __get_user(target_set.__bits[i], &ucontext->uc_sigmask.__bits[i]); } target_to_host_sigset_internal(&blocked, &target_set); ts->signal_mask = blocked; @@ -836,7 +841,7 @@ badframe: void signal_init(void) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); struct sigaction act; struct sigaction oact; int i; @@ -850,11 +855,6 @@ void signal_init(void) act.sa_flags = SA_SIGINFO; for (i = 1; i <= TARGET_NSIG; i++) { -#ifdef CONFIG_GPROF - if (i == TARGET_SIGPROF) { - continue; - } -#endif host_sig = target_to_host_signal(i); sigaction(host_sig, NULL, &oact); if (oact.sa_sigaction == (void *)SIG_IGN) { @@ -880,7 +880,7 @@ static void handle_pending_signal(CPUArchState *env, int sig, struct emulated_sigtable *k) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); struct target_sigaction *sa; int code; sigset_t set; @@ -892,7 +892,7 @@ static void handle_pending_signal(CPUArchState *env, int sig, k->pending = 0; - sig = gdb_handlesig(cpu, sig); + sig = gdb_handlesig(cpu, sig, NULL, &k->info, sizeof(k->info)); if (!sig) { sa = NULL; handler = TARGET_SIG_IGN; @@ -969,7 +969,7 @@ void process_pending_signals(CPUArchState *env) int sig; sigset_t *blocked_set, set; struct emulated_sigtable *k; - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); while (qatomic_read(&ts->signal_pending)) { sigfillset(&set); @@ -1024,7 +1024,7 @@ void process_pending_signals(CPUArchState *env) void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, MMUAccessType access_type, bool maperr, uintptr_t ra) { - const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + const TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; if (tcg_ops->record_sigsegv) { tcg_ops->record_sigsegv(cpu, addr, access_type, maperr, ra); @@ -1040,7 +1040,7 @@ void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, MMUAccessType access_type, uintptr_t ra) { - const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + const TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; if (tcg_ops->record_sigbus) { tcg_ops->record_sigbus(cpu, addr, access_type, ra); diff --git a/bsd-user/strace.c b/bsd-user/strace.c index a77d10dd6b..96499751eb 100644 --- a/bsd-user/strace.c +++ b/bsd-user/strace.c @@ -20,7 +20,6 @@ #include #include #include -#include #include "qemu.h" diff --git a/bsd-user/syscall_defs.h b/bsd-user/syscall_defs.h index f5797b28e3..52f84d5dd1 100644 --- a/bsd-user/syscall_defs.h +++ b/bsd-user/syscall_defs.h @@ -45,9 +45,9 @@ * */ #if (!defined(TARGET_I386)) -typedef int64_t target_freebsd_time_t; +typedef int64_t target_time_t; #else -typedef int32_t target_freebsd_time_t; +typedef int32_t target_time_t; #endif struct target_iovec { @@ -55,9 +55,48 @@ struct target_iovec { abi_long iov_len; /* Number of bytes */ }; +/* + * sys/ipc.h + */ +struct target_ipc_perm { + uint32_t cuid; /* creator user id */ + uint32_t cgid; /* creator group id */ + uint32_t uid; /* user id */ + uint32_t gid; /* group id */ + uint16_t mode; /* r/w permission */ + uint16_t seq; /* sequence # */ + abi_long key; /* user specified msg/sem/shm key */ +}; + +#define TARGET_IPC_RMID 0 /* remove identifier */ +#define TARGET_IPC_SET 1 /* set options */ +#define TARGET_IPC_STAT 2 /* get options */ + +/* + * sys/shm.h + */ +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* peration permission structure */ + abi_ulong shm_segsz; /* size of segment in bytes */ + int32_t shm_lpid; /* process ID of last shared memory op */ + int32_t shm_cpid; /* process ID of creator */ + int32_t shm_nattch; /* number of current attaches */ + target_time_t shm_atime; /* time of last shmat() */ + target_time_t shm_dtime; /* time of last shmdt() */ + target_time_t shm_ctime; /* time of last change by shmctl() */ +}; + +#define N_BSD_SHM_REGIONS 32 +struct bsd_shm_regions { + abi_long start; + abi_long size; +}; + /* * sys/mman.h */ +#define TARGET_MADV_DONTNEED 4 /* dont need these pages */ + #define TARGET_FREEBSD_MAP_RESERVED0080 0x0080 /* previously misimplemented */ /* MAP_INHERIT */ #define TARGET_FREEBSD_MAP_RESERVED0100 0x0100 /* previously unimplemented */ @@ -102,7 +141,7 @@ typedef abi_long target_freebsd_suseconds_t; /* compare to sys/timespec.h */ struct target_freebsd_timespec { - target_freebsd_time_t tv_sec; /* seconds */ + target_time_t tv_sec; /* seconds */ abi_long tv_nsec; /* and nanoseconds */ #if !defined(TARGET_I386) && TARGET_ABI_BITS == 32 abi_long _pad; @@ -120,7 +159,7 @@ struct target_freebsd__umtx_time { }; struct target_freebsd_timeval { - target_freebsd_time_t tv_sec; /* seconds */ + target_time_t tv_sec; /* seconds */ target_freebsd_suseconds_t tv_usec;/* and microseconds */ #if !defined(TARGET_I386) && TARGET_ABI_BITS == 32 abi_long _pad; @@ -130,11 +169,7 @@ struct target_freebsd_timeval { /* * sys/resource.h */ -#if defined(__FreeBSD__) #define TARGET_RLIM_INFINITY RLIM_INFINITY -#else -#define TARGET_RLIM_INFINITY ((abi_ulong)-1) -#endif #define TARGET_RLIMIT_CPU 0 #define TARGET_RLIMIT_FSIZE 1 @@ -179,6 +214,263 @@ struct target_freebsd__wrusage { struct target_freebsd_rusage wru_children; }; +/* + * sys/stat.h + */ +struct target_freebsd11_stat { + uint32_t st_dev; /* inode's device */ + uint32_t st_ino; /* inode's number */ + int16_t st_mode; /* inode protection mode */ + int16_t st_nlink; /* number of hard links */ + uint32_t st_uid; /* user ID of the file's owner */ + uint32_t st_gid; /* group ID of the file's group */ + uint32_t st_rdev; /* device type */ + struct target_freebsd_timespec st_atim; /* time last accessed */ + struct target_freebsd_timespec st_mtim; /* time last data modification */ + struct target_freebsd_timespec st_ctim; /* time last file status change */ + int64_t st_size; /* file size, in bytes */ + int64_t st_blocks; /* blocks allocated for file */ + uint32_t st_blksize; /* optimal blocksize for I/O */ + uint32_t st_flags; /* user defined flags for file */ + uint32_t st_gen; /* file generation number */ + int32_t st_lspare; + struct target_freebsd_timespec st_birthtim; /* time of file creation */ + /* + * Explicitly pad st_birthtim to 16 bytes so that the size of + * struct stat is backwards compatible. We use bitfields instead + * of an array of chars so that this doesn't require a C99 compiler + * to compile if the size of the padding is 0. We use 2 bitfields + * to cover up to 64 bits on 32-bit machines. We assume that + * CHAR_BIT is 8... + */ + unsigned int:(8 / 2) * (16 - (int)sizeof(struct target_freebsd_timespec)); + unsigned int:(8 / 2) * (16 - (int)sizeof(struct target_freebsd_timespec)); +} __packed; + +#if defined(__i386__) +#define TARGET_HAS_STAT_TIME_T_EXT 1 +#endif + +struct target_stat { + uint64_t st_dev; /* inode's device */ + uint64_t st_ino; /* inode's number */ + uint64_t st_nlink; /* number of hard links */ + int16_t st_mode; /* inode protection mode */ + int16_t st_padding0; + uint32_t st_uid; /* user ID of the file's owner */ + uint32_t st_gid; /* group ID of the file's group */ + int32_t st_padding1; + uint64_t st_rdev; /* device type */ +#ifdef TARGET_HAS_STAT_TIME_T_EXT + int32_t st_atim_ext; +#endif + struct target_freebsd_timespec st_atim; /* time of last access */ +#ifdef TARGET_HAS_STAT_TIME_T_EXT + int32_t st_mtim_ext; +#endif + struct target_freebsd_timespec st_mtim; /* time of last data modification */ +#ifdef TARGET_HAS_STAT_TIME_T_EXT + int32_t st_ctim_ext; +#endif + struct target_freebsd_timespec st_ctim;/* time of last file status change */ +#ifdef TARGET_HAS_STAT_TIME_T_EXT + int32_t st_btim_ext; +#endif + struct target_freebsd_timespec st_birthtim; /* time of file creation */ + int64_t st_size; /* file size, in bytes */ + int64_t st_blocks; /* blocks allocated for file */ + uint32_t st_blksize; /* optimal blocksize for I/O */ + uint32_t st_flags; /* user defined flags for file */ + uint64_t st_gen; /* file generation number */ + uint64_t st_spare[10]; +}; + + +/* struct nstat is the same as stat above but without the st_lspare field */ +struct target_freebsd11_nstat { + uint32_t st_dev; /* inode's device */ + uint32_t st_ino; /* inode's number */ + int16_t st_mode; /* inode protection mode */ + int16_t st_nlink; /* number of hard links */ + uint32_t st_uid; /* user ID of the file's owner */ + uint32_t st_gid; /* group ID of the file's group */ + uint32_t st_rdev; /* device type */ + struct target_freebsd_timespec st_atim; /* time last accessed */ + struct target_freebsd_timespec st_mtim; /* time last data modification */ + struct target_freebsd_timespec st_ctim; /* time last file status change */ + int64_t st_size; /* file size, in bytes */ + int64_t st_blocks; /* blocks allocated for file */ + uint32_t st_blksize; /* optimal blocksize for I/O */ + uint32_t st_flags; /* user defined flags for file */ + uint32_t st_gen; /* file generation number */ + struct target_freebsd_timespec st_birthtim; /* time of file creation */ + /* + * Explicitly pad st_birthtim to 16 bytes so that the size of + * struct stat is backwards compatible. We use bitfields instead + * of an array of chars so that this doesn't require a C99 compiler + * to compile if the size of the padding is 0. We use 2 bitfields + * to cover up to 64 bits on 32-bit machines. We assume that + * CHAR_BIT is 8... + */ + unsigned int:(8 / 2) * (16 - (int)sizeof(struct target_freebsd_timespec)); + unsigned int:(8 / 2) * (16 - (int)sizeof(struct target_freebsd_timespec)); +} __packed; + +/* + * sys/mount.h + */ + +/* filesystem id type */ +typedef struct target_freebsd_fsid { int32_t val[2]; } target_freebsd_fsid_t; + +/* filesystem statistics */ +struct target_freebsd11_statfs { + uint32_t f_version; /* structure version number */ + uint32_t f_type; /* type of filesystem */ + uint64_t f_flags; /* copy of mount exported flags */ + uint64_t f_bsize; /* filesystem fragment size */ + uint64_t f_iosize; /* optimal transfer block size */ + uint64_t f_blocks; /* total data blocks in filesystem */ + uint64_t f_bfree; /* free blocks in filesystem */ + int64_t f_bavail; /* free blocks avail to non-superuser */ + uint64_t f_files; /* total file nodes in filesystem */ + int64_t f_ffree; /* free nodes avail to non-superuser */ + uint64_t f_syncwrites; /* count of sync writes since mount */ + uint64_t f_asyncwrites; /* count of async writes since mount */ + uint64_t f_syncreads; /* count of sync reads since mount */ + uint64_t f_asyncreads; /* count of async reads since mount */ + uint64_t f_spare[10]; /* unused spare */ + uint32_t f_namemax; /* maximum filename length */ + uint32_t f_owner; /* user that mounted the filesystem */ + target_freebsd_fsid_t f_fsid; /* filesystem id */ + char f_charspare[80]; /* spare string space */ + char f_fstypename[16]; /* filesys type name */ + char f_mntfromname[88]; /* mount filesystem */ + char f_mntonname[88]; /* dir on which mounted*/ +}; + +struct target_statfs { + uint32_t f_version; /* structure version number */ + uint32_t f_type; /* type of filesystem */ + uint64_t f_flags; /* copy of mount exported flags */ + uint64_t f_bsize; /* filesystem fragment size */ + uint64_t f_iosize; /* optimal transfer block size */ + uint64_t f_blocks; /* total data blocks in filesystem */ + uint64_t f_bfree; /* free blocks in filesystem */ + int64_t f_bavail; /* free blocks avail to non-superuser */ + uint64_t f_files; /* total file nodes in filesystem */ + int64_t f_ffree; /* free nodes avail to non-superuser */ + uint64_t f_syncwrites; /* count of sync writes since mount */ + uint64_t f_asyncwrites; /* count of async writes since mount */ + uint64_t f_syncreads; /* count of sync reads since mount */ + uint64_t f_asyncreads; /* count of async reads since mount */ + uint64_t f_spare[10]; /* unused spare */ + uint32_t f_namemax; /* maximum filename length */ + uint32_t f_owner; /* user that mounted the filesystem */ + target_freebsd_fsid_t f_fsid; /* filesystem id */ + char f_charspare[80]; /* spare string space */ + char f_fstypename[16]; /* filesystem type name */ + char f_mntfromname[1024]; /* mounted filesystem */ + char f_mntonname[1024]; /* directory on which mounted */ +}; + +/* File identifier. These are unique per filesystem on a single machine. */ +#define TARGET_MAXFIDSZ 16 + +struct target_freebsd_fid { + uint16_t fid_len; /* len of data in bytes */ + uint16_t fid_data0; /* force longword align */ + char fid_data[TARGET_MAXFIDSZ]; /* data (variable len) */ +}; + +/* Generic file handle */ +struct target_freebsd_fhandle { + target_freebsd_fsid_t fh_fsid; /* Filesystem id of mount point */ + struct target_freebsd_fid fh_fid; /* Filesys specific id */ +}; +typedef struct target_freebsd_fhandle target_freebsd_fhandle_t; + +/* + * sys/fcntl.h + */ +#define TARGET_F_DUPFD 0 +#define TARGET_F_GETFD 1 +#define TARGET_F_SETFD 2 +#define TARGET_F_GETFL 3 +#define TARGET_F_SETFL 4 +#define TARGET_F_GETOWN 5 +#define TARGET_F_SETOWN 6 +#define TARGET_F_OGETLK 7 +#define TARGET_F_OSETLK 8 +#define TARGET_F_OSETLKW 9 +#define TARGET_F_DUP2FD 10 +#define TARGET_F_GETLK 11 +#define TARGET_F_SETLK 12 +#define TARGET_F_SETLKW 13 +#define TARGET_F_SETLK_REMOTE 14 +#define TARGET_F_READAHEAD 15 +#define TARGET_F_RDAHEAD 16 +#define TARGET_F_DUPFD_CLOEXEC 17 +#define TARGET_F_DUP2FD_CLOEXEC 18 +/* FreeBSD-specific */ +#define TARGET_F_ADD_SEALS 19 +#define TARGET_F_GET_SEALS 20 + +struct target_freebsd_flock { + int64_t l_start; + int64_t l_len; + int32_t l_pid; + int16_t l_type; + int16_t l_whence; + int32_t l_sysid; +} QEMU_PACKED; + +/* sys/unistd.h */ +/* user: vfork(2) semantics, clear signals */ +#define TARGET_RFSPAWN (1U << 31) + +/* + * from sys/procctl.h + */ +#define TARGET_PROC_SPROTECT 1 +#define TARGET_PROC_REAP_ACQUIRE 2 +#define TARGET_PROC_REAP_RELEASE 3 +#define TARGET_PROC_REAP_STATUS 4 +#define TARGET_PROC_REAP_GETPIDS 5 +#define TARGET_PROC_REAP_KILL 6 + +struct target_procctl_reaper_status { + uint32_t rs_flags; + uint32_t rs_children; + uint32_t rs_descendants; + uint32_t rs_reaper; + uint32_t rs_pid; + uint32_t rs_pad0[15]; +}; + +struct target_procctl_reaper_pidinfo { + uint32_t pi_pid; + uint32_t pi_subtree; + uint32_t pi_flags; + uint32_t pi_pad0[15]; +}; + +struct target_procctl_reaper_pids { + uint32_t rp_count; + uint32_t rp_pad0[15]; + abi_ulong rp_pids; +}; + +struct target_procctl_reaper_kill { + int32_t rk_sig; + uint32_t rk_flags; + uint32_t rk_subtree; + uint32_t rk_killed; + uint32_t rk_fpid; + uint32_t rk_pad0[15]; +}; + + #define safe_syscall0(type, name) \ type safe_##name(void) \ { \ @@ -226,4 +518,12 @@ type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ return safe_syscall(SYS_##name, arg1, arg2, arg3, arg4, arg5, arg6); \ } +#define safe_fcntl(...) safe_syscall(SYS_fcntl, __VA_ARGS__) + +/* So far all target and host bitmasks are the same */ +#undef target_to_host_bitmask +#define target_to_host_bitmask(x, tbl) (x) +#undef host_to_target_bitmask +#define host_to_target_bitmask(x, tbl) (x) + #endif /* SYSCALL_DEFS_H */ diff --git a/bsd-user/trace-events b/bsd-user/trace-events index 843896f627..2c1cb66726 100644 --- a/bsd-user/trace-events +++ b/bsd-user/trace-events @@ -1,4 +1,4 @@ -# See docs/tracing.txt for syntax documentation. +# See docs/devel/tracing.rst for syntax documentation. # bsd-user/signal.c user_setup_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64 diff --git a/bsd-user/x86_64/signal.c b/bsd-user/x86_64/signal.c index c3875bc4c6..46cb865180 100644 --- a/bsd-user/x86_64/signal.c +++ b/bsd-user/x86_64/signal.c @@ -16,6 +16,7 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu.h" /* diff --git a/bsd-user/x86_64/target_arch_cpu.c b/bsd-user/x86_64/target_arch_cpu.c index be7bd10720..1d32f18907 100644 --- a/bsd-user/x86_64/target_arch_cpu.c +++ b/bsd-user/x86_64/target_arch_cpu.c @@ -17,9 +17,8 @@ * along with this program; if not, see . */ -#include - #include "qemu/osdep.h" + #include "cpu.h" #include "qemu.h" #include "qemu/timer.h" diff --git a/bsd-user/x86_64/target_arch_elf.h b/bsd-user/x86_64/target_arch_elf.h index b244711888..e51c2faf08 100644 --- a/bsd-user/x86_64/target_arch_elf.h +++ b/bsd-user/x86_64/target_arch_elf.h @@ -20,7 +20,6 @@ #ifndef TARGET_ARCH_ELF_H #define TARGET_ARCH_ELF_H -#define ELF_START_MMAP 0x2aaaaab000ULL #define ELF_ET_DYN_LOAD_ADDR 0x01021000 #define elf_check_arch(x) (((x) == ELF_ARCH)) diff --git a/chardev/baum.c b/chardev/baum.c index 79d618e350..a1d9784d92 100644 --- a/chardev/baum.c +++ b/chardev/baum.c @@ -44,39 +44,39 @@ #define ESC 0x1B -#define BAUM_REQ_DisplayData 0x01 -#define BAUM_REQ_GetVersionNumber 0x05 -#define BAUM_REQ_GetKeys 0x08 -#define BAUM_REQ_SetMode 0x12 -#define BAUM_REQ_SetProtocol 0x15 -#define BAUM_REQ_GetDeviceIdentity 0x84 -#define BAUM_REQ_GetSerialNumber 0x8A +#define BAUM_REQ_DisplayData 0x01 +#define BAUM_REQ_GetVersionNumber 0x05 +#define BAUM_REQ_GetKeys 0x08 +#define BAUM_REQ_SetMode 0x12 +#define BAUM_REQ_SetProtocol 0x15 +#define BAUM_REQ_GetDeviceIdentity 0x84 +#define BAUM_REQ_GetSerialNumber 0x8A -#define BAUM_RSP_CellCount 0x01 -#define BAUM_RSP_VersionNumber 0x05 -#define BAUM_RSP_ModeSetting 0x11 -#define BAUM_RSP_CommunicationChannel 0x16 -#define BAUM_RSP_PowerdownSignal 0x17 -#define BAUM_RSP_HorizontalSensors 0x20 -#define BAUM_RSP_VerticalSensors 0x21 -#define BAUM_RSP_RoutingKeys 0x22 -#define BAUM_RSP_Switches 0x23 -#define BAUM_RSP_TopKeys 0x24 -#define BAUM_RSP_HorizontalSensor 0x25 -#define BAUM_RSP_VerticalSensor 0x26 -#define BAUM_RSP_RoutingKey 0x27 -#define BAUM_RSP_FrontKeys6 0x28 -#define BAUM_RSP_BackKeys6 0x29 -#define BAUM_RSP_CommandKeys 0x2B -#define BAUM_RSP_FrontKeys10 0x2C -#define BAUM_RSP_BackKeys10 0x2D -#define BAUM_RSP_EntryKeys 0x33 -#define BAUM_RSP_JoyStick 0x34 -#define BAUM_RSP_ErrorCode 0x40 -#define BAUM_RSP_InfoBlock 0x42 -#define BAUM_RSP_DeviceIdentity 0x84 -#define BAUM_RSP_SerialNumber 0x8A -#define BAUM_RSP_BluetoothName 0x8C +#define BAUM_RSP_CellCount 0x01 +#define BAUM_RSP_VersionNumber 0x05 +#define BAUM_RSP_ModeSetting 0x11 +#define BAUM_RSP_CommunicationChannel 0x16 +#define BAUM_RSP_PowerdownSignal 0x17 +#define BAUM_RSP_HorizontalSensors 0x20 +#define BAUM_RSP_VerticalSensors 0x21 +#define BAUM_RSP_RoutingKeys 0x22 +#define BAUM_RSP_Switches 0x23 +#define BAUM_RSP_TopKeys 0x24 +#define BAUM_RSP_HorizontalSensor 0x25 +#define BAUM_RSP_VerticalSensor 0x26 +#define BAUM_RSP_RoutingKey 0x27 +#define BAUM_RSP_FrontKeys6 0x28 +#define BAUM_RSP_BackKeys6 0x29 +#define BAUM_RSP_CommandKeys 0x2B +#define BAUM_RSP_FrontKeys10 0x2C +#define BAUM_RSP_BackKeys10 0x2D +#define BAUM_RSP_EntryKeys 0x33 +#define BAUM_RSP_JoyStick 0x34 +#define BAUM_RSP_ErrorCode 0x40 +#define BAUM_RSP_InfoBlock 0x42 +#define BAUM_RSP_DeviceIdentity 0x84 +#define BAUM_RSP_SerialNumber 0x8A +#define BAUM_RSP_BluetoothName 0x8C #define BAUM_TL1 0x01 #define BAUM_TL2 0x02 @@ -87,6 +87,9 @@ #define BUF_SIZE 256 +#define X_MAX 84 +#define Y_MAX 1 + struct BaumChardev { Chardev parent; @@ -244,11 +247,11 @@ static int baum_deferred_init(BaumChardev *baum) brlapi_perror("baum: brlapi__getDisplaySize"); return 0; } - if (baum->y > 1) { - baum->y = 1; + if (baum->y > Y_MAX) { + baum->y = Y_MAX; } - if (baum->x > 84) { - baum->x = 84; + if (baum->x > X_MAX) { + baum->x = X_MAX; } con = qemu_console_lookup_by_index(0); @@ -296,7 +299,8 @@ static void baum_chr_accept_input(struct Chardev *chr) static void baum_write_packet(BaumChardev *baum, const uint8_t *buf, int len) { Chardev *chr = CHARDEV(baum); - uint8_t io_buf[1 + 2 * len], *cur = io_buf; + g_autofree uint8_t *io_buf = g_malloc(1 + 2 * len); + uint8_t *cur = io_buf; int room; *cur++ = ESC; while (len--) @@ -380,9 +384,9 @@ static int baum_eat_packet(BaumChardev *baum, const uint8_t *buf, int len) switch (req) { case BAUM_REQ_DisplayData: { - uint8_t cells[baum->x * baum->y], c; - uint8_t text[baum->x * baum->y]; - uint8_t zero[baum->x * baum->y]; + uint8_t cells[X_MAX * Y_MAX], c; + uint8_t text[X_MAX * Y_MAX]; + uint8_t zero[X_MAX * Y_MAX]; int cursor = BRLAPI_CURSOR_OFF; int i; @@ -405,7 +409,7 @@ static int baum_eat_packet(BaumChardev *baum, const uint8_t *buf, int len) } timer_del(baum->cellCount_timer); - memset(zero, 0, sizeof(zero)); + memset(zero, 0, baum->x * baum->y); brlapi_writeArguments_t wa = { .displayNumber = BRLAPI_DISPLAY_DEFAULT, diff --git a/chardev/char-fd.c b/chardev/char-fd.c index cf78454841..d2c4923359 100644 --- a/chardev/char-fd.c +++ b/chardev/char-fd.c @@ -198,7 +198,7 @@ int qmp_chardev_open_file_source(char *src, int flags, Error **errp) { int fd = -1; - TFR(fd = qemu_open_old(src, flags, 0666)); + fd = RETRY_ON_EINTR(qemu_open_old(src, flags, 0666)); if (fd == -1) { error_setg_file_open(errp, errno, src); } diff --git a/chardev/char-fe.c b/chardev/char-fe.c index 7789f7be9c..66cee8475a 100644 --- a/chardev/char-fe.c +++ b/chardev/char-fe.c @@ -199,26 +199,27 @@ bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp) MuxChardev *d = MUX_CHARDEV(s); if (d->mux_cnt >= MAX_MUX) { - goto unavailable; + error_setg(errp, + "too many uses of multiplexed chardev '%s'" + " (maximum is " stringify(MAX_MUX) ")", + s->label); + return false; } d->backends[d->mux_cnt] = b; tag = d->mux_cnt++; } else if (s->be) { - goto unavailable; + error_setg(errp, "chardev '%s' is already in use", s->label); + return false; } else { s->be = b; } } - b->fe_open = false; + b->fe_is_open = false; b->tag = tag; b->chr = s; return true; - -unavailable: - error_setg(errp, QERR_DEVICE_IN_USE, s->label); - return false; } void qemu_chr_fe_deinit(CharBackend *b, bool del) @@ -257,7 +258,7 @@ void qemu_chr_fe_set_handlers_full(CharBackend *b, bool sync_state) { Chardev *s; - int fe_open; + bool fe_open; s = b->chr; if (!s) { @@ -265,10 +266,10 @@ void qemu_chr_fe_set_handlers_full(CharBackend *b, } if (!opaque && !fd_can_read && !fd_read && !fd_event) { - fe_open = 0; + fe_open = false; remove_fd_in_watch(s); } else { - fe_open = 1; + fe_open = true; } b->chr_can_read = fd_can_read; b->chr_read = fd_read; @@ -336,7 +337,7 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo) } } -void qemu_chr_fe_set_open(CharBackend *be, int fe_open) +void qemu_chr_fe_set_open(CharBackend *be, bool is_open) { Chardev *chr = be->chr; @@ -344,12 +345,12 @@ void qemu_chr_fe_set_open(CharBackend *be, int fe_open) return; } - if (be->fe_open == fe_open) { + if (be->fe_is_open == is_open) { return; } - be->fe_open = fe_open; + be->fe_is_open = is_open; if (CHARDEV_GET_CLASS(chr)->chr_set_fe_open) { - CHARDEV_GET_CLASS(chr)->chr_set_fe_open(chr, fe_open); + CHARDEV_GET_CLASS(chr)->chr_set_fe_open(chr, is_open); } } diff --git a/chardev/char-file.c b/chardev/char-file.c index 2fd80707e5..263e6da563 100644 --- a/chardev/char-file.c +++ b/chardev/char-file.c @@ -45,7 +45,7 @@ static void qmp_chardev_open_file(Chardev *chr, DWORD accessmode; DWORD flags; - if (file->has_in) { + if (file->in) { error_setg(errp, "input file not supported"); return; } @@ -83,7 +83,7 @@ static void qmp_chardev_open_file(Chardev *chr, return; } - if (file->has_in) { + if (file->in) { flags = O_RDONLY; in = qmp_chardev_open_file_source(file->in, flags, errp); if (in < 0) { @@ -100,6 +100,7 @@ static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend, Error **errp) { const char *path = qemu_opt_get(opts, "path"); + const char *inpath = qemu_opt_get(opts, "input-path"); ChardevFile *file; backend->type = CHARDEV_BACKEND_KIND_FILE; @@ -107,9 +108,16 @@ static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend, error_setg(errp, "chardev: file: no filename given"); return; } +#ifdef _WIN32 + if (inpath) { + error_setg(errp, "chardev: file: input-path not supported on Windows"); + return; + } +#endif file = backend->u.file.data = g_new0(ChardevFile, 1); qemu_chr_parse_common(opts, qapi_ChardevFile_base(file)); file->out = g_strdup(path); + file->in = g_strdup(inpath); file->has_append = true; file->append = qemu_opt_get_bool(opts, "append", false); diff --git a/chardev/char-hmp-cmds.c b/chardev/char-hmp-cmds.c new file mode 100644 index 0000000000..287c2b1bcd --- /dev/null +++ b/chardev/char-hmp-cmds.c @@ -0,0 +1,220 @@ +/* + * HMP commands related to character devices + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "chardev/char.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-char.h" +#include "qapi/qmp/qdict.h" +#include "qemu/config-file.h" +#include "qemu/option.h" + +void hmp_info_chardev(Monitor *mon, const QDict *qdict) +{ + ChardevInfoList *char_info, *info; + + char_info = qmp_query_chardev(NULL); + for (info = char_info; info; info = info->next) { + monitor_printf(mon, "%s: filename=%s\n", info->value->label, + info->value->filename); + } + + qapi_free_ChardevInfoList(char_info); +} + +void hmp_ringbuf_write(Monitor *mon, const QDict *qdict) +{ + const char *chardev = qdict_get_str(qdict, "device"); + const char *data = qdict_get_str(qdict, "data"); + Error *err = NULL; + + qmp_ringbuf_write(chardev, data, false, 0, &err); + + hmp_handle_error(mon, err); +} + +void hmp_ringbuf_read(Monitor *mon, const QDict *qdict) +{ + uint32_t size = qdict_get_int(qdict, "size"); + const char *chardev = qdict_get_str(qdict, "device"); + char *data; + Error *err = NULL; + int i; + + data = qmp_ringbuf_read(chardev, size, false, 0, &err); + if (hmp_handle_error(mon, err)) { + return; + } + + for (i = 0; data[i]; i++) { + unsigned char ch = data[i]; + + if (ch == '\\') { + monitor_printf(mon, "\\\\"); + } else if ((ch < 0x20 && ch != '\n' && ch != '\t') || ch == 0x7F) { + monitor_printf(mon, "\\u%04X", ch); + } else { + monitor_printf(mon, "%c", ch); + } + + } + monitor_printf(mon, "\n"); + g_free(data); +} + +void hmp_chardev_add(Monitor *mon, const QDict *qdict) +{ + const char *args = qdict_get_str(qdict, "args"); + Error *err = NULL; + QemuOpts *opts; + + opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), args, true); + if (opts == NULL) { + error_setg(&err, "Parsing chardev args failed"); + } else { + qemu_chr_new_from_opts(opts, NULL, &err); + qemu_opts_del(opts); + } + hmp_handle_error(mon, err); +} + +void hmp_chardev_change(Monitor *mon, const QDict *qdict) +{ + const char *args = qdict_get_str(qdict, "args"); + const char *id; + Error *err = NULL; + ChardevBackend *backend = NULL; + ChardevReturn *ret = NULL; + QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), args, + true); + if (!opts) { + error_setg(&err, "Parsing chardev args failed"); + goto end; + } + + id = qdict_get_str(qdict, "id"); + if (qemu_opts_id(opts)) { + error_setg(&err, "Unexpected 'id' parameter"); + goto end; + } + + backend = qemu_chr_parse_opts(opts, &err); + if (!backend) { + goto end; + } + + ret = qmp_chardev_change(id, backend, &err); + +end: + qapi_free_ChardevReturn(ret); + qapi_free_ChardevBackend(backend); + qemu_opts_del(opts); + hmp_handle_error(mon, err); +} + +void hmp_chardev_remove(Monitor *mon, const QDict *qdict) +{ + Error *local_err = NULL; + + qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err); + hmp_handle_error(mon, local_err); +} + +void hmp_chardev_send_break(Monitor *mon, const QDict *qdict) +{ + Error *local_err = NULL; + + qmp_chardev_send_break(qdict_get_str(qdict, "id"), &local_err); + hmp_handle_error(mon, local_err); +} + +void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + ChardevBackendInfoList *list, *start; + + if (nb_args != 2) { + return; + } + len = strlen(str); + readline_set_completion_index(rs, len); + + start = list = qmp_query_chardev_backends(NULL); + while (list) { + const char *chr_name = list->value->name; + + if (!strncmp(chr_name, str, len)) { + readline_add_completion(rs, chr_name); + } + list = list->next; + } + qapi_free_ChardevBackendInfoList(start); +} + +void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + ChardevInfoList *list, *start; + + if (nb_args != 2) { + return; + } + len = strlen(str); + readline_set_completion_index(rs, len); + + start = list = qmp_query_chardev(NULL); + while (list) { + ChardevInfo *chr = list->value; + + if (!strncmp(chr->label, str, len)) { + readline_add_completion(rs, chr->label); + } + list = list->next; + } + qapi_free_ChardevInfoList(start); +} + +static void ringbuf_completion(ReadLineState *rs, const char *str) +{ + size_t len; + ChardevInfoList *list, *start; + + len = strlen(str); + readline_set_completion_index(rs, len); + + start = list = qmp_query_chardev(NULL); + while (list) { + ChardevInfo *chr_info = list->value; + + if (!strncmp(chr_info->label, str, len)) { + Chardev *chr = qemu_chr_find(chr_info->label); + if (chr && CHARDEV_IS_RINGBUF(chr)) { + readline_add_completion(rs, chr_info->label); + } + } + list = list->next; + } + qapi_free_ChardevInfoList(start); +} + +void ringbuf_write_completion(ReadLineState *rs, int nb_args, const char *str) +{ + if (nb_args != 2) { + return; + } + ringbuf_completion(rs, str); +} diff --git a/chardev/char-io.c b/chardev/char-io.c index 4451128cba..dab77b112e 100644 --- a/chardev/char-io.c +++ b/chardev/char-io.c @@ -33,6 +33,7 @@ typedef struct IOWatchPoll { IOCanReadHandler *fd_can_read; GSourceFunc fd_read; void *opaque; + GMainContext *context; } IOWatchPoll; static IOWatchPoll *io_watch_poll_from_source(GSource *source) @@ -50,28 +51,59 @@ static gboolean io_watch_poll_prepare(GSource *source, return FALSE; } + /* + * We do not register the QIOChannel watch as a child GSource. + * The 'prepare' function on the parent GSource will be + * skipped if a child GSource's 'prepare' function indicates + * readiness. We need this prepare function be guaranteed + * to run on *every* iteration of the main loop, because + * it is critical to ensure we remove the QIOChannel watch + * if 'fd_can_read' indicates the frontend cannot receive + * more data. + */ if (now_active) { iwp->src = qio_channel_create_watch( iwp->ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL); g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL); - g_source_add_child_source(source, iwp->src); - g_source_unref(iwp->src); + g_source_attach(iwp->src, iwp->context); } else { - g_source_remove_child_source(source, iwp->src); + g_source_destroy(iwp->src); + g_source_unref(iwp->src); iwp->src = NULL; } return FALSE; } +static gboolean io_watch_poll_check(GSource *source) +{ + return FALSE; +} + static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { - return G_SOURCE_CONTINUE; + abort(); +} + +static void io_watch_poll_finalize(GSource *source) +{ + /* + * Due to a glib bug, removing the last reference to a source + * inside a finalize callback causes recursive locking (and a + * deadlock). This is not a problem inside other callbacks, + * including dispatch callbacks, so we call io_remove_watch_poll + * to remove this source. At this point, iwp->src must + * be NULL, or we would leak it. + */ + IOWatchPoll *iwp = io_watch_poll_from_source(source); + assert(iwp->src == NULL); } static GSourceFuncs io_watch_poll_funcs = { .prepare = io_watch_poll_prepare, + .check = io_watch_poll_check, .dispatch = io_watch_poll_dispatch, + .finalize = io_watch_poll_finalize, }; GSource *io_add_watch_poll(Chardev *chr, @@ -91,6 +123,7 @@ GSource *io_add_watch_poll(Chardev *chr, iwp->ioc = ioc; iwp->fd_read = (GSourceFunc) fd_read; iwp->src = NULL; + iwp->context = context; name = g_strdup_printf("chardev-iowatch-%s", chr->label); g_source_set_name((GSource *)iwp, name); @@ -101,10 +134,23 @@ GSource *io_add_watch_poll(Chardev *chr, return (GSource *)iwp; } +static void io_remove_watch_poll(GSource *source) +{ + IOWatchPoll *iwp; + + iwp = io_watch_poll_from_source(source); + if (iwp->src) { + g_source_destroy(iwp->src); + g_source_unref(iwp->src); + iwp->src = NULL; + } + g_source_destroy(&iwp->parent); +} + void remove_fd_in_watch(Chardev *chr) { if (chr->gsource) { - g_source_destroy(chr->gsource); + io_remove_watch_poll(chr->gsource); chr->gsource = NULL; } } diff --git a/chardev/char-parallel.c b/chardev/char-parallel.c index 05e7efbd6c..78697d7522 100644 --- a/chardev/char-parallel.c +++ b/chardev/char-parallel.c @@ -164,13 +164,13 @@ static void qemu_chr_open_pp_fd(Chardev *chr, { ParallelChardev *drv = PARALLEL_CHARDEV(chr); + drv->fd = fd; + if (ioctl(fd, PPCLAIM) < 0) { error_setg_errno(errp, errno, "not a parallel port"); - close(fd); return; } - drv->fd = fd; drv->mode = IEEE1284_MODE_COMPAT; } #endif /* __linux__ */ @@ -238,7 +238,7 @@ static void qemu_chr_open_pp_fd(Chardev *chr, } #endif -#ifdef HAVE_CHARDEV_PARPORT +#ifdef HAVE_CHARDEV_PARALLEL static void qmp_chardev_open_parallel(Chardev *chr, ChardevBackend *backend, bool *be_opened, @@ -276,29 +276,21 @@ static void char_parallel_class_init(ObjectClass *oc, void *data) cc->parse = qemu_chr_parse_parallel; cc->open = qmp_chardev_open_parallel; -#if defined(__linux__) cc->chr_ioctl = pp_ioctl; -#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ - defined(__DragonFly__) - cc->chr_ioctl = pp_ioctl; -#endif } static void char_parallel_finalize(Object *obj) { -#if defined(__linux__) Chardev *chr = CHARDEV(obj); ParallelChardev *drv = PARALLEL_CHARDEV(chr); int fd = drv->fd; +#if defined(__linux__) pp_hw_mode(drv, IEEE1284_MODE_COMPAT); ioctl(fd, PPRELEASE); +#endif close(fd); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); -#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ - defined(__DragonFly__) - /* FIXME: close fd? */ -#endif } static const TypeInfo char_parallel_type_info = { @@ -316,4 +308,4 @@ static void register_types(void) type_init(register_types); -#endif +#endif /* HAVE_CHARDEV_PARALLEL */ diff --git a/chardev/char-pipe.c b/chardev/char-pipe.c index 66d3b85091..5ad30bcc59 100644 --- a/chardev/char-pipe.c +++ b/chardev/char-pipe.c @@ -131,8 +131,8 @@ static void qemu_chr_open_pipe(Chardev *chr, filename_in = g_strdup_printf("%s.in", filename); filename_out = g_strdup_printf("%s.out", filename); - TFR(fd_in = qemu_open_old(filename_in, O_RDWR | O_BINARY)); - TFR(fd_out = qemu_open_old(filename_out, O_RDWR | O_BINARY)); + fd_in = RETRY_ON_EINTR(qemu_open_old(filename_in, O_RDWR | O_BINARY)); + fd_out = RETRY_ON_EINTR(qemu_open_old(filename_out, O_RDWR | O_BINARY)); g_free(filename_in); g_free(filename_out); if (fd_in < 0 || fd_out < 0) { @@ -142,7 +142,9 @@ static void qemu_chr_open_pipe(Chardev *chr, if (fd_out >= 0) { close(fd_out); } - TFR(fd_in = fd_out = qemu_open_old(filename, O_RDWR | O_BINARY)); + fd_in = fd_out = RETRY_ON_EINTR( + qemu_open_old(filename, O_RDWR | O_BINARY) + ); if (fd_in < 0) { error_setg_file_open(errp, errno, filename); return; diff --git a/chardev/char-pty.c b/chardev/char-pty.c index 53f25c6bbd..cc2f7617fe 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -93,9 +93,7 @@ static void pty_chr_update_read_handler(Chardev *chr) pfd.fd = fioc->fd; pfd.events = G_IO_OUT; pfd.revents = 0; - do { - rc = g_poll(&pfd, 1, 0); - } while (rc == -1 && errno == EINTR); + rc = RETRY_ON_EINTR(g_poll(&pfd, 1, 0)); assert(rc >= 0); if (pfd.revents & G_IO_HUP) { @@ -108,11 +106,27 @@ static void pty_chr_update_read_handler(Chardev *chr) static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len) { PtyChardev *s = PTY_CHARDEV(chr); + GPollFD pfd; + int rc; - if (!s->connected) { - return len; + if (s->connected) { + return io_channel_send(s->ioc, buf, len); } - return io_channel_send(s->ioc, buf, len); + + /* + * The other side might already be re-connected, but the timer might + * not have fired yet. So let's check here whether we can write again: + */ + pfd.fd = QIO_CHANNEL_FILE(s->ioc)->fd; + pfd.events = G_IO_OUT; + pfd.revents = 0; + rc = RETRY_ON_EINTR(g_poll(&pfd, 1, 0)); + g_assert(rc >= 0); + if (!(pfd.revents & G_IO_HUP) && (pfd.revents & G_IO_OUT)) { + io_channel_send(s->ioc, buf, len); + } + + return len; } static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond) @@ -336,7 +350,7 @@ static void char_pty_open(Chardev *chr, s = PTY_CHARDEV(chr); s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd)); name = g_strdup_printf("chardev-pty-%s", chr->label); - qio_channel_set_name(QIO_CHANNEL(s->ioc), name); + qio_channel_set_name(s->ioc, name); g_free(name); s->timer_src = NULL; *be_opened = false; diff --git a/chardev/char-socket.c b/chardev/char-socket.c index dc4e218eeb..812d7aa38a 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -283,11 +283,11 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len) if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) { ret = qio_channel_readv_full(s->ioc, &iov, 1, &msgfds, &msgfds_num, - NULL); + 0, NULL); } else { ret = qio_channel_readv_full(s->ioc, &iov, 1, NULL, NULL, - NULL); + 0, NULL); } if (msgfds_num) { @@ -378,6 +378,10 @@ static void tcp_chr_free_connection(Chardev *chr) char_socket_yank_iochannel, QIO_CHANNEL(s->sioc)); } + + if (s->ioc) { + qio_channel_close(s->ioc, NULL); + } object_unref(OBJECT(s->sioc)); s->sioc = NULL; object_unref(OBJECT(s->ioc)); @@ -557,12 +561,10 @@ static char *qemu_chr_compute_filename(SocketChardev *s) const char *left = "", *right = ""; switch (ss->ss_family) { -#ifndef _WIN32 case AF_UNIX: return g_strdup_printf("unix:%s%s", ((struct sockaddr_un *)(ss))->sun_path, s->is_listen ? ",server=on" : ""); -#endif case AF_INET6: left = "["; right = "]"; @@ -599,6 +601,22 @@ static void update_ioc_handlers(SocketChardev *s) remove_hup_source(s); s->hup_source = qio_channel_create_watch(s->ioc, G_IO_HUP); + /* + * poll() is liable to return POLLHUP even when there is + * still incoming data available to read on the FD. If + * we have the hup_source at the same priority as the + * main io_add_watch_poll GSource, then we might end up + * processing the POLLHUP event first, closing the FD, + * and as a result silently discard data we should have + * read. + * + * By setting the hup_source to G_PRIORITY_DEFAULT + 1, + * we ensure that io_add_watch_poll GSource will always + * be dispatched first, thus guaranteeing we will be + * able to process all incoming data before closing the + * FD + */ + g_source_set_priority(s->hup_source, G_PRIORITY_DEFAULT + 1); g_source_set_callback(s->hup_source, (GSourceFunc)tcp_chr_hup, chr, NULL); g_source_attach(s->hup_source, chr->gcontext); @@ -712,7 +730,7 @@ static void tcp_chr_telnet_init(Chardev *chr) if (!s->is_tn3270) { init->buflen = 12; - /* Prep the telnet negotion to put telnet in binary, + /* Prep the telnet negotiation to put telnet in binary, * no echo, single char mode */ IACSET(init->buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */ IACSET(init->buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */ @@ -720,7 +738,7 @@ static void tcp_chr_telnet_init(Chardev *chr) IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */ } else { init->buflen = 21; - /* Prep the TN3270 negotion based on RFC1576 */ + /* Prep the TN3270 negotiation based on RFC1576 */ IACSET(init->buf, 0xff, 0xfd, 0x19); /* IAC DO EOR */ IACSET(init->buf, 0xff, 0xfb, 0x19); /* IAC WILL EOR */ IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO BINARY */ @@ -744,8 +762,12 @@ static void tcp_chr_websock_handshake(QIOTask *task, gpointer user_data) { Chardev *chr = user_data; SocketChardev *s = user_data; + Error *err = NULL; - if (qio_task_propagate_error(task, NULL)) { + if (qio_task_propagate_error(task, &err)) { + error_reportf_err(err, + "websock handshake of character device %s failed: ", + chr->label); tcp_chr_disconnect(chr); } else { if (s->do_telnetopt) { @@ -780,8 +802,12 @@ static void tcp_chr_tls_handshake(QIOTask *task, { Chardev *chr = user_data; SocketChardev *s = user_data; + Error *err = NULL; - if (qio_task_propagate_error(task, NULL)) { + if (qio_task_propagate_error(task, &err)) { + error_reportf_err(err, + "TLS handshake of character device %s failed: ", + chr->label); tcp_chr_disconnect(chr); } else { if (s->is_websock) { @@ -1067,6 +1093,7 @@ static void char_socket_finalize(Object *obj) qio_net_listener_set_client_func_full(s->listener, NULL, NULL, NULL, chr->gcontext); object_unref(OBJECT(s->listener)); + s->listener = NULL; } if (s->tls_creds) { object_unref(OBJECT(s->tls_creds)); @@ -1253,7 +1280,7 @@ static bool qmp_chardev_validate_socket(ChardevSocket *sock, "'fd' address type"); return false; } - if (sock->has_tls_creds && + if (sock->tls_creds && !(sock->has_server && sock->server)) { error_setg(errp, "'tls_creds' option is incompatible with " @@ -1263,7 +1290,7 @@ static bool qmp_chardev_validate_socket(ChardevSocket *sock, break; case SOCKET_ADDRESS_TYPE_UNIX: - if (sock->has_tls_creds) { + if (sock->tls_creds) { error_setg(errp, "'tls_creds' option is incompatible with " "'unix' address type"); @@ -1275,7 +1302,7 @@ static bool qmp_chardev_validate_socket(ChardevSocket *sock, break; case SOCKET_ADDRESS_TYPE_VSOCK: - if (sock->has_tls_creds) { + if (sock->tls_creds) { error_setg(errp, "'tls_creds' option is incompatible with " "'vsock' address type"); @@ -1286,12 +1313,12 @@ static bool qmp_chardev_validate_socket(ChardevSocket *sock, break; } - if (sock->has_tls_authz && !sock->has_tls_creds) { + if (sock->tls_authz && !sock->tls_creds) { error_setg(errp, "'tls_authz' option requires 'tls_creds' option"); return false; } - /* Validate any options which have a dependancy on client vs server */ + /* Validate any options which have a dependency on client vs server */ if (!sock->has_server || sock->server) { if (sock->has_reconnect) { error_setg(errp, @@ -1372,10 +1399,12 @@ static void qmp_chardev_open_socket(Chardev *chr, } qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE); +#ifndef _WIN32 /* TODO SOCKET_ADDRESS_FD where fd has AF_UNIX */ if (addr->type == SOCKET_ADDRESS_TYPE_UNIX) { qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS); } +#endif /* * In the chardev-change special-case, we shouldn't register a new yank @@ -1465,9 +1494,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, sock->wait = qemu_opt_get_bool(opts, "wait", true); sock->has_reconnect = qemu_opt_find(opts, "reconnect"); sock->reconnect = qemu_opt_get_number(opts, "reconnect", 0); - sock->has_tls_creds = qemu_opt_get(opts, "tls-creds"); sock->tls_creds = g_strdup(qemu_opt_get(opts, "tls-creds")); - sock->has_tls_authz = qemu_opt_get(opts, "tls-authz"); sock->tls_authz = g_strdup(qemu_opt_get(opts, "tls-authz")); addr = g_new0(SocketAddressLegacy, 1); @@ -1497,7 +1524,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, }; } else { addr->type = SOCKET_ADDRESS_TYPE_FD; - addr->u.fd.data = g_new(String, 1); + addr->u.fd.data = g_new(FdSocketAddress, 1); addr->u.fd.data->str = g_strdup(fd); } sock->addr = addr; diff --git a/chardev/char-udp.c b/chardev/char-udp.c index 6756e69924..3d9a2d5e77 100644 --- a/chardev/char-udp.c +++ b/chardev/char-udp.c @@ -178,7 +178,6 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend, udp->remote = addr; if (has_local) { - udp->has_local = true; addr = g_new0(SocketAddressLegacy, 1); addr->type = SOCKET_ADDRESS_TYPE_INET; addr->u.inet.data = g_new(InetSocketAddress, 1); diff --git a/chardev/char-win-stdio.c b/chardev/char-win-stdio.c index a4771ab82e..1a18999e78 100644 --- a/chardev/char-win-stdio.c +++ b/chardev/char-win-stdio.c @@ -146,6 +146,8 @@ static void qemu_chr_open_stdio(Chardev *chr, bool *be_opened, Error **errp) { + ChardevStdio *opts = backend->u.stdio.data; + bool stdio_allow_signal = !opts->has_signal || opts->signal; WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr); DWORD dwMode; int is_console = 0; @@ -188,12 +190,16 @@ static void qemu_chr_open_stdio(Chardev *chr, } } - dwMode |= ENABLE_LINE_INPUT; + dwMode |= ENABLE_LINE_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT; if (is_console) { /* set the terminal in raw mode */ /* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */ - dwMode |= ENABLE_PROCESSED_INPUT; + if (stdio_allow_signal) { + dwMode |= ENABLE_PROCESSED_INPUT; + } else { + dwMode &= ~ENABLE_PROCESSED_INPUT; + } } SetConsoleMode(stdio->hStdIn, dwMode); diff --git a/chardev/char.c b/chardev/char.c index 0169d8dde4..3c43fb1278 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "monitor/monitor.h" +#include "monitor/qmp-helpers.h" #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" @@ -170,6 +171,18 @@ int qemu_chr_write(Chardev *s, const uint8_t *buf, int len, bool write_all) return res; } + if (replay_mode == REPLAY_MODE_RECORD) { + /* + * When recording we don't want temporary conditions to + * perturb the result. By ensuring we write everything we can + * while recording we avoid playback being out of sync if it + * doesn't encounter the same temporary conditions (usually + * triggered by external programs not reading the chardev fast + * enough and pipes filling up). + */ + write_all = true; + } + res = qemu_chr_write_buffer(s, buf, len, &offset, write_all); if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) { @@ -193,7 +206,7 @@ int qemu_chr_be_can_write(Chardev *s) return be->chr_can_read(be->opaque); } -void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len) +void qemu_chr_be_write_impl(Chardev *s, const uint8_t *buf, int len) { CharBackend *be = s->be; @@ -202,7 +215,7 @@ void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len) } } -void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len) +void qemu_chr_be_write(Chardev *s, const uint8_t *buf, int len) { if (qemu_chr_replay(s)) { if (replay_mode == REPLAY_MODE_PLAY) { @@ -240,7 +253,7 @@ static void qemu_char_open(Chardev *chr, ChardevBackend *backend, /* Any ChardevCommon member would work */ ChardevCommon *common = backend ? backend->u.null.data : NULL; - if (common && common->has_logfile) { + if (common && common->logfile) { int flags = O_WRONLY; if (common->has_logappend && common->logappend) { @@ -496,9 +509,7 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend) { const char *logfile = qemu_opt_get(opts, "logfile"); - backend->has_logfile = logfile != NULL; backend->logfile = g_strdup(logfile); - backend->has_logappend = true; backend->logappend = qemu_opt_get_bool(opts, "logappend", false); } @@ -519,7 +530,7 @@ static const ChardevClass *char_get_class(const char *driver, Error **errp) if (object_class_is_abstract(oc)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver", - "an abstract device type"); + "a non-abstract device type"); return NULL; } @@ -532,19 +543,6 @@ static const ChardevClass *char_get_class(const char *driver, Error **errp) return cc; } -static struct ChardevAlias { - const char *typename; - const char *alias; - bool deprecation_warning_printed; -} chardev_alias_table[] = { -#ifdef HAVE_CHARDEV_PARPORT - { "parallel", "parport" }, -#endif -#ifdef HAVE_CHARDEV_SERIAL - { "serial", "tty" }, -#endif -}; - typedef struct ChadevClassFE { void (*fn)(const char *name, void *opaque); void *opaque; @@ -580,28 +578,12 @@ help_string_append(const char *name, void *opaque) g_string_append_printf(str, "\n %s", name); } -static const char *chardev_alias_translate(const char *name) -{ - int i; - for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) { - if (g_strcmp0(chardev_alias_table[i].alias, name) == 0) { - if (!chardev_alias_table[i].deprecation_warning_printed) { - warn_report("The alias '%s' is deprecated, use '%s' instead", - name, chardev_alias_table[i].typename); - chardev_alias_table[i].deprecation_warning_printed = true; - } - return chardev_alias_table[i].typename; - } - } - return name; -} - ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp) { Error *local_err = NULL; const ChardevClass *cc; ChardevBackend *backend = NULL; - const char *name = chardev_alias_translate(qemu_opt_get(opts, "backend")); + const char *name = qemu_opt_get(opts, "backend"); if (name == NULL) { error_setg(errp, "chardev: \"%s\" missing backend", @@ -639,7 +621,7 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context, const ChardevClass *cc; Chardev *chr = NULL; ChardevBackend *backend = NULL; - const char *name = chardev_alias_translate(qemu_opt_get(opts, "backend")); + const char *name = qemu_opt_get(opts, "backend"); const char *id = qemu_opts_id(opts); char *bid = NULL; @@ -780,7 +762,7 @@ static int qmp_query_chardev_foreach(Object *obj, void *data) value->label = g_strdup(chr->label); value->filename = g_strdup(chr->filename); - value->frontend_open = chr->be && chr->be->fe_open; + value->frontend_open = chr->be && chr->be->fe_is_open; QAPI_LIST_PREPEND(*list, value); @@ -835,6 +817,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "path", .type = QEMU_OPT_STRING, + },{ + .name = "input-path", + .type = QEMU_OPT_STRING, },{ .name = "host", .type = QEMU_OPT_STRING, @@ -1057,7 +1042,6 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, ret = g_new0(ChardevReturn, 1); if (CHARDEV_IS_PTY(chr)) { ret->pty = g_strdup(chr->filename + 4); - ret->has_pty = true; } return ret; @@ -1143,7 +1127,7 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend, return NULL; } - /* change successfull, clean up */ + /* change successful, clean up */ chr_new->handover_yank_instance = false; /* @@ -1160,7 +1144,6 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend, ret = g_new0(ChardevReturn, 1); if (CHARDEV_IS_PTY(chr_new)) { ret->pty = g_strdup(chr_new->filename + 4); - ret->has_pty = true; } return ret; @@ -1199,6 +1182,23 @@ void qmp_chardev_send_break(const char *id, Error **errp) qemu_chr_be_event(chr, CHR_EVENT_BREAK); } +bool qmp_add_client_char(int fd, bool has_skipauth, bool skipauth, + bool has_tls, bool tls, const char *protocol, + Error **errp) +{ + Chardev *s = qemu_chr_find(protocol); + + if (!s) { + error_setg(errp, "protocol '%s' is invalid", protocol); + return false; + } + if (qemu_chr_add_client(s, fd) < 0) { + error_setg(errp, "failed to add client"); + return false; + } + return true; +} + /* * Add a timeout callback for the chardev (in milliseconds), return * the GSource object created. Please use this to add timeout hook for diff --git a/chardev/meson.build b/chardev/meson.build index 664f77b887..70070a8279 100644 --- a/chardev/meson.build +++ b/chardev/meson.build @@ -12,20 +12,27 @@ chardev_ss.add(files( 'char-udp.c', 'char.c', )) -chardev_ss.add(when: 'CONFIG_POSIX', if_true: [files( - 'char-fd.c', - 'char-parallel.c', - 'char-pty.c', -), util]) -chardev_ss.add(when: 'CONFIG_WIN32', if_true: files( - 'char-console.c', - 'char-win-stdio.c', - 'char-win.c', -)) +if host_os == 'windows' + chardev_ss.add(files( + 'char-console.c', + 'char-win-stdio.c', + 'char-win.c', + )) +else + chardev_ss.add(files( + 'char-fd.c', + 'char-parallel.c', + 'char-pty.c', + ), util) +endif -chardev_ss = chardev_ss.apply(config_host, strict: false) +chardev_ss = chardev_ss.apply({}) -softmmu_ss.add(files('msmouse.c', 'wctablet.c', 'testdev.c')) +system_ss.add(files( + 'char-hmp-cmds.c', + 'msmouse.c', + 'wctablet.c', + 'testdev.c')) chardev_modules = {} diff --git a/chardev/msmouse.c b/chardev/msmouse.c index eb9231dcdb..a774c397b4 100644 --- a/chardev/msmouse.c +++ b/chardev/msmouse.c @@ -24,23 +24,45 @@ #include "qemu/osdep.h" #include "qemu/module.h" +#include "qemu/fifo8.h" #include "chardev/char.h" +#include "chardev/char-serial.h" #include "ui/console.h" #include "ui/input.h" #include "qom/object.h" -#define MSMOUSE_LO6(n) ((n) & 0x3f) -#define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6) +#define MSMOUSE_LO6(n) ((n) & 0x3f) +#define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6) +#define MSMOUSE_PWR(cm) (cm & (CHR_TIOCM_RTS | CHR_TIOCM_DTR)) + +/* Serial PnP for 6 bit devices/mice sends all ASCII chars - 0x20 */ +#define M(c) (c - 0x20) +/* Serial fifo size. */ +#define MSMOUSE_BUF_SZ 64 + +/* Mouse ID: Send "M3" cause we behave like a 3 button logitech mouse. */ +const uint8_t mouse_id[] = {'M', '3'}; +/* + * PnP start "(", PnP version (1.0), vendor ID, product ID, '\\', + * serial ID (omitted), '\\', MS class name, '\\', driver ID (omitted), '\\', + * product description, checksum, ")" + * Missing parts are inserted later. + */ +const uint8_t pnp_data[] = {M('('), 1, '$', M('Q'), M('M'), M('U'), + M('0'), M('0'), M('0'), M('1'), + M('\\'), M('\\'), + M('M'), M('O'), M('U'), M('S'), M('E'), + M('\\'), M('\\')}; struct MouseChardev { Chardev parent; QemuInputHandlerState *hs; + int tiocm; int axis[INPUT_AXIS__MAX]; bool btns[INPUT_BUTTON__MAX]; bool btnc[INPUT_BUTTON__MAX]; - uint8_t outbuf[32]; - int outlen; + Fifo8 outbuf; }; typedef struct MouseChardev MouseChardev; @@ -51,20 +73,18 @@ DECLARE_INSTANCE_CHECKER(MouseChardev, MOUSE_CHARDEV, static void msmouse_chr_accept_input(Chardev *chr) { MouseChardev *mouse = MOUSE_CHARDEV(chr); - int len; + uint32_t len, avail; len = qemu_chr_be_can_write(chr); - if (len > mouse->outlen) { - len = mouse->outlen; - } - if (!len) { - return; - } + avail = fifo8_num_used(&mouse->outbuf); + while (len > 0 && avail > 0) { + const uint8_t *buf; + uint32_t size; - qemu_chr_be_write(chr, mouse->outbuf, len); - mouse->outlen -= len; - if (mouse->outlen) { - memmove(mouse->outbuf, mouse->outbuf + len, mouse->outlen); + buf = fifo8_pop_buf(&mouse->outbuf, MIN(len, avail), &size); + qemu_chr_be_write(chr, buf, size); + len = qemu_chr_be_can_write(chr); + avail -= size; } } @@ -91,12 +111,11 @@ static void msmouse_queue_event(MouseChardev *mouse) mouse->btnc[INPUT_BUTTON_MIDDLE]) { bytes[3] |= (mouse->btns[INPUT_BUTTON_MIDDLE] ? 0x20 : 0x00); mouse->btnc[INPUT_BUTTON_MIDDLE] = false; - count = 4; + count++; } - if (mouse->outlen <= sizeof(mouse->outbuf) - count) { - memcpy(mouse->outbuf + mouse->outlen, bytes, count); - mouse->outlen += count; + if (fifo8_num_free(&mouse->outbuf) >= count) { + fifo8_push_all(&mouse->outbuf, bytes, count); } else { /* queue full -> drop event */ } @@ -109,6 +128,11 @@ static void msmouse_input_event(DeviceState *dev, QemuConsole *src, InputMoveEvent *move; InputBtnEvent *btn; + /* Ignore events if serial mouse powered down. */ + if (!MSMOUSE_PWR(mouse->tiocm)) { + return; + } + switch (evt->type) { case INPUT_EVENT_KIND_REL: move = evt->u.rel.data; @@ -132,6 +156,11 @@ static void msmouse_input_sync(DeviceState *dev) MouseChardev *mouse = MOUSE_CHARDEV(dev); Chardev *chr = CHARDEV(dev); + /* Ignore events if serial mouse powered down. */ + if (!MSMOUSE_PWR(mouse->tiocm)) { + return; + } + msmouse_queue_event(mouse); msmouse_chr_accept_input(chr); } @@ -142,20 +171,88 @@ static int msmouse_chr_write(struct Chardev *s, const uint8_t *buf, int len) return len; } -static void char_msmouse_finalize(Object *obj) -{ - MouseChardev *mouse = MOUSE_CHARDEV(obj); - - qemu_input_handler_unregister(mouse->hs); -} - -static QemuInputHandler msmouse_handler = { +static const QemuInputHandler msmouse_handler = { .name = "QEMU Microsoft Mouse", .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL, .event = msmouse_input_event, .sync = msmouse_input_sync, }; +static int msmouse_ioctl(Chardev *chr, int cmd, void *arg) +{ + MouseChardev *mouse = MOUSE_CHARDEV(chr); + int c, i, j; + uint8_t bytes[MSMOUSE_BUF_SZ / 2]; + int *targ = (int *)arg; + const uint8_t hexchr[16] = {M('0'), M('1'), M('2'), M('3'), M('4'), M('5'), + M('6'), M('7'), M('8'), M('9'), M('A'), M('B'), + M('C'), M('D'), M('E'), M('F')}; + + switch (cmd) { + case CHR_IOCTL_SERIAL_SET_TIOCM: + c = mouse->tiocm; + mouse->tiocm = *(int *)arg; + if (MSMOUSE_PWR(mouse->tiocm)) { + if (!MSMOUSE_PWR(c)) { + /* + * Power on after reset: Send ID and PnP data + * No need to check fifo space as it is empty at this point. + */ + fifo8_push_all(&mouse->outbuf, mouse_id, sizeof(mouse_id)); + /* Add PnP data: */ + fifo8_push_all(&mouse->outbuf, pnp_data, sizeof(pnp_data)); + /* + * Add device description from qemu handler name. + * Make sure this all fits into the queue beforehand! + */ + c = M(')'); + for (i = 0; msmouse_handler.name[i]; i++) { + bytes[i] = M(msmouse_handler.name[i]); + c += bytes[i]; + } + /* Calc more of checksum */ + for (j = 0; j < sizeof(pnp_data); j++) { + c += pnp_data[j]; + } + c &= 0xff; + bytes[i++] = hexchr[c >> 4]; + bytes[i++] = hexchr[c & 0x0f]; + bytes[i++] = M(')'); + fifo8_push_all(&mouse->outbuf, bytes, i); + /* Start sending data to serial. */ + msmouse_chr_accept_input(chr); + } + break; + } + /* + * Reset mouse buffers on power down. + * Mouse won't send anything without power. + */ + fifo8_reset(&mouse->outbuf); + memset(mouse->axis, 0, sizeof(mouse->axis)); + memset(mouse->btns, false, sizeof(mouse->btns)); + memset(mouse->btnc, false, sizeof(mouse->btns)); + break; + case CHR_IOCTL_SERIAL_GET_TIOCM: + /* Remember line control status. */ + *targ = mouse->tiocm; + break; + default: + return -ENOTSUP; + } + return 0; +} + +static void char_msmouse_finalize(Object *obj) +{ + MouseChardev *mouse = MOUSE_CHARDEV(obj); + + if (mouse->hs) { + qemu_input_handler_unregister(mouse->hs); + } + fifo8_destroy(&mouse->outbuf); +} + static void msmouse_chr_open(Chardev *chr, ChardevBackend *backend, bool *be_opened, @@ -166,6 +263,8 @@ static void msmouse_chr_open(Chardev *chr, *be_opened = false; mouse->hs = qemu_input_handler_register((DeviceState *)mouse, &msmouse_handler); + mouse->tiocm = 0; + fifo8_create(&mouse->outbuf, MSMOUSE_BUF_SZ); } static void char_msmouse_class_init(ObjectClass *oc, void *data) @@ -175,6 +274,7 @@ static void char_msmouse_class_init(ObjectClass *oc, void *data) cc->open = msmouse_chr_open; cc->chr_write = msmouse_chr_write; cc->chr_accept_input = msmouse_chr_accept_input; + cc->chr_ioctl = msmouse_ioctl; } static const TypeInfo char_msmouse_type_info = { diff --git a/chardev/spice.c b/chardev/spice.c index bbffef4913..e843d961a7 100644 --- a/chardev/spice.c +++ b/chardev/spice.c @@ -98,9 +98,7 @@ static SpiceCharDeviceInterface vmc_interface = { .write = vmc_write, .read = vmc_read, .event = vmc_event, -#if SPICE_SERVER_VERSION >= 0x000c06 .flags = SPICE_CHAR_DEVICE_NOTIFY_WRITABLE, -#endif }; diff --git a/chardev/wctablet.c b/chardev/wctablet.c index e8b292c43c..f4008bf35b 100644 --- a/chardev/wctablet.c +++ b/chardev/wctablet.c @@ -178,7 +178,7 @@ static void wctablet_input_sync(DeviceState *dev) } } -static QemuInputHandler wctablet_handler = { +static const QemuInputHandler wctablet_handler = { .name = "QEMU Wacom Pen Tablet", .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, .event = wctablet_input_event, @@ -319,7 +319,9 @@ static void wctablet_chr_finalize(Object *obj) { TabletChardev *tablet = WCTABLET_CHARDEV(obj); - qemu_input_handler_unregister(tablet->hs); + if (tablet->hs) { + qemu_input_handler_unregister(tablet->hs); + } } static void wctablet_chr_open(Chardev *chr, diff --git a/common-user/host/ppc/safe-syscall.inc.S b/common-user/host/ppc/safe-syscall.inc.S new file mode 100644 index 0000000000..0851f6c0b8 --- /dev/null +++ b/common-user/host/ppc/safe-syscall.inc.S @@ -0,0 +1,107 @@ +/* + * safe-syscall.inc.S : host-specific assembly fragment + * to handle signals occurring at the same time as system calls. + * This is intended to be included by common-user/safe-syscall.S + * + * Copyright (C) 2022 Linaro, Ltd. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * Standardize on the _CALL_FOO symbols used by GCC: + * Apple XCode does not define _CALL_DARWIN. + * Clang defines _CALL_ELF (64-bit) but not _CALL_SYSV (32-bit). + */ +#if !defined(_CALL_SYSV) && \ + !defined(_CALL_DARWIN) && \ + !defined(_CALL_AIX) && \ + !defined(_CALL_ELF) +# if defined(__APPLE__) +# define _CALL_DARWIN +# elif defined(__ELF__) && TCG_TARGET_REG_BITS == 32 +# define _CALL_SYSV +# else +# error "Unknown ABI" +# endif +#endif + +#ifndef _CALL_SYSV +# error "Unsupported ABI" +#endif + + + .global safe_syscall_base + .global safe_syscall_start + .global safe_syscall_end + .type safe_syscall_base, @function + + .text + + /* + * This is the entry point for making a system call. The calling + * convention here is that of a C varargs function with the + * first argument an 'int *' to the signal_pending flag, the + * second one the system call number (as a 'long'), and all further + * arguments being syscall arguments (also 'long'). + */ +safe_syscall_base: + .cfi_startproc + stwu 1, -8(1) + .cfi_def_cfa_offset 8 + stw 30, 4(1) + .cfi_offset 30, -4 + + /* + * We enter with r3 == &signal_pending + * r4 == syscall number + * r5 ... r10 == syscall arguments + * and return the result in r3 + * and the syscall instruction needs + * r0 == syscall number + * r3 ... r8 == syscall arguments + * and returns the result in r3 + * Shuffle everything around appropriately. + */ + mr 30, 3 /* signal_pending */ + mr 0, 4 /* syscall number */ + mr 3, 5 /* syscall arguments */ + mr 4, 6 + mr 5, 7 + mr 6, 8 + mr 7, 9 + mr 8, 10 + + /* + * This next sequence of code works in conjunction with the + * rewind_if_safe_syscall_function(). If a signal is taken + * and the interrupted PC is anywhere between 'safe_syscall_start' + * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'. + * The code sequence must therefore be able to cope with this, and + * the syscall instruction must be the final one in the sequence. + */ +safe_syscall_start: + /* if signal_pending is non-zero, don't do the call */ + lwz 12, 0(30) + cmpwi 0, 12, 0 + bne- 2f + sc +safe_syscall_end: + /* code path when we did execute the syscall */ + lwz 30, 4(1) /* restore r30 */ + addi 1, 1, 8 /* restore stack */ + .cfi_restore 30 + .cfi_def_cfa_offset 0 + bnslr+ /* return on success */ + b safe_syscall_set_errno_tail + + /* code path when we didn't execute the syscall */ +2: lwz 30, 4(1) + addi 1, 1, 8 + addi 3, 0, QEMU_ERESTARTSYS + b safe_syscall_set_errno_tail + + .cfi_endproc + + .size safe_syscall_base, .-safe_syscall_base diff --git a/common-user/meson.build b/common-user/meson.build index 26212dda5c..ac9de5b9e3 100644 --- a/common-user/meson.build +++ b/common-user/meson.build @@ -1,3 +1,7 @@ +if not have_user + subdir_done() +endif + common_user_inc += include_directories('host/' / host_arch) user_ss.add(files( diff --git a/configs/devices/aarch64-softmmu/default.mak b/configs/devices/aarch64-softmmu/default.mak index cf43ac8da1..f82a04c27d 100644 --- a/configs/devices/aarch64-softmmu/default.mak +++ b/configs/devices/aarch64-softmmu/default.mak @@ -3,6 +3,8 @@ # We support all the 32 bit boards so need all their config include ../arm-softmmu/default.mak -CONFIG_XLNX_ZYNQMP_ARM=y -CONFIG_XLNX_VERSAL=y -CONFIG_SBSA_REF=y +# These are selected by default when TCG is enabled, uncomment them to +# keep out of the build. +# CONFIG_XLNX_ZYNQMP_ARM=n +# CONFIG_XLNX_VERSAL=n +# CONFIG_SBSA_REF=n diff --git a/configs/devices/arm-softmmu/default.mak b/configs/devices/arm-softmmu/default.mak index 6985a25377..6ee31bc1ab 100644 --- a/configs/devices/arm-softmmu/default.mak +++ b/configs/devices/arm-softmmu/default.mak @@ -4,41 +4,45 @@ # CONFIG_TEST_DEVICES=n CONFIG_ARM_VIRT=y -CONFIG_CUBIEBOARD=y -CONFIG_EXYNOS4=y -CONFIG_HIGHBANK=y -CONFIG_INTEGRATOR=y -CONFIG_FSL_IMX31=y -CONFIG_MUSICPAL=y -CONFIG_MUSCA=y -CONFIG_CHEETAH=y -CONFIG_SX1=y -CONFIG_NSERIES=y -CONFIG_STELLARIS=y -CONFIG_STM32VLDISCOVERY=y -CONFIG_REALVIEW=y -CONFIG_VERSATILE=y -CONFIG_VEXPRESS=y -CONFIG_ZYNQ=y -CONFIG_MAINSTONE=y -CONFIG_GUMSTIX=y -CONFIG_SPITZ=y -CONFIG_TOSA=y -CONFIG_Z2=y -CONFIG_NPCM7XX=y -CONFIG_COLLIE=y -CONFIG_ASPEED_SOC=y -CONFIG_NETDUINO2=y -CONFIG_NETDUINOPLUS2=y -CONFIG_MPS2=y -CONFIG_RASPI=y -CONFIG_DIGIC=y -CONFIG_SABRELITE=y -CONFIG_EMCRAFT_SF2=y -CONFIG_MICROBIT=y -CONFIG_FSL_IMX25=y -CONFIG_FSL_IMX7=y -CONFIG_FSL_IMX6UL=y -CONFIG_SEMIHOSTING=y -CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y -CONFIG_ALLWINNER_H3=y + +# These are selected by default when TCG is enabled, uncomment them to +# keep out of the build. +# CONFIG_CUBIEBOARD=n +# CONFIG_EXYNOS4=n +# CONFIG_HIGHBANK=n +# CONFIG_INTEGRATOR=n +# CONFIG_FSL_IMX31=n +# CONFIG_MUSICPAL=n +# CONFIG_MPS3R=n +# CONFIG_MUSCA=n +# CONFIG_CHEETAH=n +# CONFIG_SX1=n +# CONFIG_NSERIES=n +# CONFIG_STELLARIS=n +# CONFIG_STM32VLDISCOVERY=n +# CONFIG_B_L475E_IOT01A=n +# CONFIG_REALVIEW=n +# CONFIG_VERSATILE=n +# CONFIG_VEXPRESS=n +# CONFIG_ZYNQ=n +# CONFIG_MAINSTONE=n +# CONFIG_GUMSTIX=n +# CONFIG_SPITZ=n +# CONFIG_TOSA=n +# CONFIG_Z2=n +# CONFIG_NPCM7XX=n +# CONFIG_COLLIE=n +# CONFIG_ASPEED_SOC=n +# CONFIG_NETDUINO2=n +# CONFIG_NETDUINOPLUS2=n +# CONFIG_OLIMEX_STM32_H405=n +# CONFIG_MPS2=n +# CONFIG_RASPI=n +# CONFIG_DIGIC=n +# CONFIG_SABRELITE=n +# CONFIG_EMCRAFT_SF2=n +# CONFIG_MICROBIT=n +# CONFIG_FSL_IMX25=n +# CONFIG_FSL_IMX7=n +# CONFIG_FSL_IMX6UL=n +# CONFIG_ALLWINNER_H3=n diff --git a/configs/devices/m68k-softmmu/default.mak b/configs/devices/m68k-softmmu/default.mak index 7f8619e427..8dcaa28ed3 100644 --- a/configs/devices/m68k-softmmu/default.mak +++ b/configs/devices/m68k-softmmu/default.mak @@ -1,7 +1,5 @@ # Default configuration for m68k-softmmu -CONFIG_SEMIHOSTING=y - # Boards: # CONFIG_AN5206=y diff --git a/configs/devices/mips-softmmu/common.mak b/configs/devices/mips-softmmu/common.mak index d2202c839e..416a5d353e 100644 --- a/configs/devices/mips-softmmu/common.mak +++ b/configs/devices/mips-softmmu/common.mak @@ -1,38 +1,8 @@ # Common mips*-softmmu CONFIG defines -# CONFIG_SEMIHOSTING is always required on this architecture -CONFIG_SEMIHOSTING=y +# Uncomment the following lines to disable these optional devices: +# CONFIG_PCI_DEVICES=n +# CONFIG_TEST_DEVICES=n -CONFIG_ISA_BUS=y -CONFIG_PCI=y -CONFIG_PCI_DEVICES=y -CONFIG_VGA_ISA=y -CONFIG_VGA_MMIO=y -CONFIG_VGA_CIRRUS=y -CONFIG_VMWARE_VGA=y -CONFIG_SERIAL=y -CONFIG_SERIAL_ISA=y -CONFIG_PARALLEL=y -CONFIG_I8254=y -CONFIG_PCSPK=y -CONFIG_PCKBD=y -CONFIG_FDC=y -CONFIG_ACPI=y -CONFIG_ACPI_PIIX4=y -CONFIG_APM=y -CONFIG_I8257=y -CONFIG_PIIX4=y -CONFIG_IDE_ISA=y -CONFIG_IDE_PIIX=y -CONFIG_PFLASH_CFI01=y -CONFIG_I8259=y -CONFIG_MC146818RTC=y -CONFIG_EMPTY_SLOT=y -CONFIG_MIPS_CPS=y -CONFIG_MIPS_ITU=y CONFIG_MALTA=y -CONFIG_PCNET_PCI=y CONFIG_MIPSSIM=y -CONFIG_ACPI_SMBUS=y -CONFIG_SMBUS_EEPROM=y -CONFIG_TEST_DEVICES=y diff --git a/configs/devices/mips64el-softmmu/default.mak b/configs/devices/mips64el-softmmu/default.mak index c610749ac1..88a37cf27f 100644 --- a/configs/devices/mips64el-softmmu/default.mak +++ b/configs/devices/mips64el-softmmu/default.mak @@ -1,11 +1,7 @@ # Default configuration for mips64el-softmmu include ../mips-softmmu/common.mak -CONFIG_IDE_VIA=y CONFIG_FULOONG=y CONFIG_LOONGSON3V=y -CONFIG_ATI_VGA=y -CONFIG_RTL8139_PCI=y CONFIG_JAZZ=y -CONFIG_VT82C686=y CONFIG_MIPS_BOSTON=y diff --git a/configs/devices/nios2-softmmu/default.mak b/configs/devices/nios2-softmmu/default.mak index 1bc4082ea9..e130d024e6 100644 --- a/configs/devices/nios2-softmmu/default.mak +++ b/configs/devices/nios2-softmmu/default.mak @@ -1,7 +1,5 @@ # Default configuration for nios2-softmmu -CONFIG_SEMIHOSTING=y - # Boards: # CONFIG_NIOS2_10M50=y diff --git a/configs/devices/or1k-softmmu/default.mak b/configs/devices/or1k-softmmu/default.mak index 168101c39a..89c39e3123 100644 --- a/configs/devices/or1k-softmmu/default.mak +++ b/configs/devices/or1k-softmmu/default.mak @@ -3,3 +3,4 @@ # Boards: # CONFIG_OR1K_SIM=y +CONFIG_OR1K_VIRT=y diff --git a/configs/devices/ppc-softmmu/default.mak b/configs/devices/ppc-softmmu/default.mak index 658a454426..b85fd2bcd7 100644 --- a/configs/devices/ppc-softmmu/default.mak +++ b/configs/devices/ppc-softmmu/default.mak @@ -1,7 +1,8 @@ # Default configuration for ppc-softmmu # For embedded PPCs: -CONFIG_E500=y +CONFIG_E500PLAT=y +CONFIG_MPC8544DS=y CONFIG_PPC405=y CONFIG_PPC440=y CONFIG_VIRTEX=y @@ -13,6 +14,7 @@ CONFIG_SAM460EX=y CONFIG_MAC_OLDWORLD=y CONFIG_MAC_NEWWORLD=y +CONFIG_AMIGAONE=y CONFIG_PEGASOS2=y # For PReP diff --git a/configs/devices/riscv32-softmmu/default.mak b/configs/devices/riscv32-softmmu/default.mak index d847bd5692..94a236c9c2 100644 --- a/configs/devices/riscv32-softmmu/default.mak +++ b/configs/devices/riscv32-softmmu/default.mak @@ -3,8 +3,6 @@ # Uncomment the following lines to disable these optional devices: # #CONFIG_PCI_DEVICES=n -CONFIG_SEMIHOSTING=y -CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y # Boards: # diff --git a/configs/devices/riscv64-softmmu/default.mak b/configs/devices/riscv64-softmmu/default.mak index bc69301fa4..3f68059448 100644 --- a/configs/devices/riscv64-softmmu/default.mak +++ b/configs/devices/riscv64-softmmu/default.mak @@ -3,8 +3,6 @@ # Uncomment the following lines to disable these optional devices: # #CONFIG_PCI_DEVICES=n -CONFIG_SEMIHOSTING=y -CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y # Boards: # diff --git a/configs/devices/s390x-softmmu/default.mak b/configs/devices/s390x-softmmu/default.mak index f2287a133f..6d87bc8b4b 100644 --- a/configs/devices/s390x-softmmu/default.mak +++ b/configs/devices/s390x-softmmu/default.mak @@ -7,6 +7,7 @@ #CONFIG_VFIO_CCW=n #CONFIG_VIRTIO_PCI=n #CONFIG_WDT_DIAG288=n +#CONFIG_PCIE_DEVICES=n # Boards: # diff --git a/configs/devices/xtensa-softmmu/default.mak b/configs/devices/xtensa-softmmu/default.mak index 4fe1bf00c9..49e4c9da88 100644 --- a/configs/devices/xtensa-softmmu/default.mak +++ b/configs/devices/xtensa-softmmu/default.mak @@ -1,7 +1,5 @@ # Default configuration for Xtensa -CONFIG_SEMIHOSTING=y - # Boards: # CONFIG_XTENSA_SIM=y diff --git a/configs/meson/windows.txt b/configs/meson/windows.txt new file mode 100644 index 0000000000..55b192e71b --- /dev/null +++ b/configs/meson/windows.txt @@ -0,0 +1,9 @@ +# target-specific defaults, can still be overridden on +# the command line + +[built-in options] +bindir = '' +prefix = '/qemu' + +[project options] +qemu_suffix = '' diff --git a/configs/targets/aarch64-linux-user.mak b/configs/targets/aarch64-linux-user.mak index d0c603c54e..ba8bc5fe3f 100644 --- a/configs/targets/aarch64-linux-user.mak +++ b/configs/targets/aarch64-linux-user.mak @@ -1,5 +1,6 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm -TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml +TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/aarch64-pauth.xml TARGET_HAS_BFLT=y +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/aarch64-softmmu.mak b/configs/targets/aarch64-softmmu.mak index d489e6da83..b4338e9568 100644 --- a/configs/targets/aarch64-softmmu.mak +++ b/configs/targets/aarch64-softmmu.mak @@ -1,5 +1,5 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm TARGET_SUPPORTS_MTTCG=y -TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml +TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml gdb-xml/aarch64-pauth.xml TARGET_NEED_FDT=y diff --git a/configs/targets/aarch64_be-linux-user.mak b/configs/targets/aarch64_be-linux-user.mak index 7794424745..acb5620cdb 100644 --- a/configs/targets/aarch64_be-linux-user.mak +++ b/configs/targets/aarch64_be-linux-user.mak @@ -1,6 +1,7 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm TARGET_BIG_ENDIAN=y -TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml +TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/aarch64-pauth.xml TARGET_HAS_BFLT=y +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/alpha-linux-user.mak b/configs/targets/alpha-linux-user.mak index 7e62fd796a..f7d3fb4afa 100644 --- a/configs/targets/alpha-linux-user.mak +++ b/configs/targets/alpha-linux-user.mak @@ -1,4 +1,3 @@ TARGET_ARCH=alpha TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl -TARGET_ALIGNED_ONLY=y diff --git a/configs/targets/alpha-softmmu.mak b/configs/targets/alpha-softmmu.mak index e4b874a19e..9dbe160740 100644 --- a/configs/targets/alpha-softmmu.mak +++ b/configs/targets/alpha-softmmu.mak @@ -1,3 +1,2 @@ TARGET_ARCH=alpha -TARGET_ALIGNED_ONLY=y TARGET_SUPPORTS_MTTCG=y diff --git a/configs/targets/arm-linux-user.mak b/configs/targets/arm-linux-user.mak index 3e10d6b15d..7f5d65794c 100644 --- a/configs/targets/arm-linux-user.mak +++ b/configs/targets/arm-linux-user.mak @@ -3,4 +3,5 @@ TARGET_SYSTBL_ABI=common,oabi TARGET_SYSTBL=syscall.tbl TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml TARGET_HAS_BFLT=y +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/armeb-linux-user.mak b/configs/targets/armeb-linux-user.mak index a249cc2e29..943d0d87bf 100644 --- a/configs/targets/armeb-linux-user.mak +++ b/configs/targets/armeb-linux-user.mak @@ -4,4 +4,5 @@ TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml TARGET_HAS_BFLT=y +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/hexagon-linux-user.mak b/configs/targets/hexagon-linux-user.mak index 003ed0a408..2765a4c563 100644 --- a/configs/targets/hexagon-linux-user.mak +++ b/configs/targets/hexagon-linux-user.mak @@ -1 +1,2 @@ TARGET_ARCH=hexagon +TARGET_XML_FILES=gdb-xml/hexagon-core.xml gdb-xml/hexagon-hvx.xml diff --git a/configs/targets/hppa-linux-user.mak b/configs/targets/hppa-linux-user.mak index db873a8796..8e0a80492f 100644 --- a/configs/targets/hppa-linux-user.mak +++ b/configs/targets/hppa-linux-user.mak @@ -1,5 +1,5 @@ TARGET_ARCH=hppa +TARGET_ABI32=y TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl -TARGET_ALIGNED_ONLY=y TARGET_BIG_ENDIAN=y diff --git a/configs/targets/hppa-softmmu.mak b/configs/targets/hppa-softmmu.mak index 44f07b0332..a41662aa99 100644 --- a/configs/targets/hppa-softmmu.mak +++ b/configs/targets/hppa-softmmu.mak @@ -1,4 +1,3 @@ TARGET_ARCH=hppa -TARGET_ALIGNED_ONLY=y TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y diff --git a/configs/targets/loongarch64-linux-user.mak b/configs/targets/loongarch64-linux-user.mak new file mode 100644 index 0000000000..d878e5a113 --- /dev/null +++ b/configs/targets/loongarch64-linux-user.mak @@ -0,0 +1,4 @@ +# Default configuration for loongarch64-linux-user +TARGET_ARCH=loongarch64 +TARGET_BASE_ARCH=loongarch +TARGET_XML_FILES=gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak index 7bc06c850c..f23780fdd8 100644 --- a/configs/targets/loongarch64-softmmu.mak +++ b/configs/targets/loongarch64-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=loongarch64 TARGET_BASE_ARCH=loongarch TARGET_SUPPORTS_MTTCG=y -TARGET_XML_FILES= gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu64.xml +TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml +TARGET_NEED_FDT=y diff --git a/configs/targets/microblaze-linux-user.mak b/configs/targets/microblaze-linux-user.mak index 4249a37f65..0a2322c249 100644 --- a/configs/targets/microblaze-linux-user.mak +++ b/configs/targets/microblaze-linux-user.mak @@ -3,3 +3,4 @@ TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y TARGET_HAS_BFLT=y +TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml diff --git a/configs/targets/microblaze-softmmu.mak b/configs/targets/microblaze-softmmu.mak index 8385e2d333..e84c0cc728 100644 --- a/configs/targets/microblaze-softmmu.mak +++ b/configs/targets/microblaze-softmmu.mak @@ -2,3 +2,4 @@ TARGET_ARCH=microblaze TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y TARGET_NEED_FDT=y +TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml diff --git a/configs/targets/microblazeel-linux-user.mak b/configs/targets/microblazeel-linux-user.mak index d0e775d840..270743156a 100644 --- a/configs/targets/microblazeel-linux-user.mak +++ b/configs/targets/microblazeel-linux-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=microblaze TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl TARGET_HAS_BFLT=y +TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml diff --git a/configs/targets/microblazeel-softmmu.mak b/configs/targets/microblazeel-softmmu.mak index af40391f2f..9b688036bd 100644 --- a/configs/targets/microblazeel-softmmu.mak +++ b/configs/targets/microblazeel-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=microblaze TARGET_SUPPORTS_MTTCG=y TARGET_NEED_FDT=y +TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml diff --git a/configs/targets/mips-linux-user.mak b/configs/targets/mips-linux-user.mak index 71fa77d464..b4569a9893 100644 --- a/configs/targets/mips-linux-user.mak +++ b/configs/targets/mips-linux-user.mak @@ -2,5 +2,4 @@ TARGET_ARCH=mips TARGET_ABI_MIPSO32=y TARGET_SYSTBL_ABI=o32 TARGET_SYSTBL=syscall_o32.tbl -TARGET_ALIGNED_ONLY=y TARGET_BIG_ENDIAN=y diff --git a/configs/targets/mips-softmmu.mak b/configs/targets/mips-softmmu.mak index 7787a4d94c..d34b4083fc 100644 --- a/configs/targets/mips-softmmu.mak +++ b/configs/targets/mips-softmmu.mak @@ -1,4 +1,3 @@ TARGET_ARCH=mips -TARGET_ALIGNED_ONLY=y TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y diff --git a/configs/targets/mips64-linux-user.mak b/configs/targets/mips64-linux-user.mak index 5a4771f22d..d2ff509a11 100644 --- a/configs/targets/mips64-linux-user.mak +++ b/configs/targets/mips64-linux-user.mak @@ -3,5 +3,4 @@ TARGET_ABI_MIPSN64=y TARGET_BASE_ARCH=mips TARGET_SYSTBL_ABI=n64 TARGET_SYSTBL=syscall_n64.tbl -TARGET_ALIGNED_ONLY=y TARGET_BIG_ENDIAN=y diff --git a/configs/targets/mips64-softmmu.mak b/configs/targets/mips64-softmmu.mak index 568d66650c..12d9483bf0 100644 --- a/configs/targets/mips64-softmmu.mak +++ b/configs/targets/mips64-softmmu.mak @@ -1,4 +1,3 @@ TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips -TARGET_ALIGNED_ONLY=y TARGET_BIG_ENDIAN=y diff --git a/configs/targets/mips64el-linux-user.mak b/configs/targets/mips64el-linux-user.mak index f348f35997..f9efeec8ea 100644 --- a/configs/targets/mips64el-linux-user.mak +++ b/configs/targets/mips64el-linux-user.mak @@ -3,4 +3,3 @@ TARGET_ABI_MIPSN64=y TARGET_BASE_ARCH=mips TARGET_SYSTBL_ABI=n64 TARGET_SYSTBL=syscall_n64.tbl -TARGET_ALIGNED_ONLY=y diff --git a/configs/targets/mips64el-softmmu.mak b/configs/targets/mips64el-softmmu.mak index 5a52aa4b64..8d9ab3ddc4 100644 --- a/configs/targets/mips64el-softmmu.mak +++ b/configs/targets/mips64el-softmmu.mak @@ -1,4 +1,3 @@ TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips -TARGET_ALIGNED_ONLY=y TARGET_NEED_FDT=y diff --git a/configs/targets/mipsel-linux-user.mak b/configs/targets/mipsel-linux-user.mak index e23793070c..e8d7241d31 100644 --- a/configs/targets/mipsel-linux-user.mak +++ b/configs/targets/mipsel-linux-user.mak @@ -2,4 +2,3 @@ TARGET_ARCH=mips TARGET_ABI_MIPSO32=y TARGET_SYSTBL_ABI=o32 TARGET_SYSTBL=syscall_o32.tbl -TARGET_ALIGNED_ONLY=y diff --git a/configs/targets/mipsel-softmmu.mak b/configs/targets/mipsel-softmmu.mak index c7c41f4fb7..0829659fc2 100644 --- a/configs/targets/mipsel-softmmu.mak +++ b/configs/targets/mipsel-softmmu.mak @@ -1,3 +1,2 @@ TARGET_ARCH=mips -TARGET_ALIGNED_ONLY=y TARGET_SUPPORTS_MTTCG=y diff --git a/configs/targets/mipsn32-linux-user.mak b/configs/targets/mipsn32-linux-user.mak index 1e80b302fc..206095da64 100644 --- a/configs/targets/mipsn32-linux-user.mak +++ b/configs/targets/mipsn32-linux-user.mak @@ -4,5 +4,4 @@ TARGET_ABI32=y TARGET_BASE_ARCH=mips TARGET_SYSTBL_ABI=n32 TARGET_SYSTBL=syscall_n32.tbl -TARGET_ALIGNED_ONLY=y TARGET_BIG_ENDIAN=y diff --git a/configs/targets/mipsn32el-linux-user.mak b/configs/targets/mipsn32el-linux-user.mak index f31a9c394b..ca2a3ed753 100644 --- a/configs/targets/mipsn32el-linux-user.mak +++ b/configs/targets/mipsn32el-linux-user.mak @@ -4,4 +4,3 @@ TARGET_ABI32=y TARGET_BASE_ARCH=mips TARGET_SYSTBL_ABI=n32 TARGET_SYSTBL=syscall_n32.tbl -TARGET_ALIGNED_ONLY=y diff --git a/configs/targets/nios2-softmmu.mak b/configs/targets/nios2-softmmu.mak index 1e93b54cd1..c99ae3777e 100644 --- a/configs/targets/nios2-softmmu.mak +++ b/configs/targets/nios2-softmmu.mak @@ -1,2 +1,2 @@ TARGET_ARCH=nios2 -TARGET_ALIGNED_ONLY=y +TARGET_NEED_FDT=y diff --git a/configs/targets/or1k-softmmu.mak b/configs/targets/or1k-softmmu.mak index 263e970870..432f855a30 100644 --- a/configs/targets/or1k-softmmu.mak +++ b/configs/targets/or1k-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=openrisc +TARGET_SUPPORTS_MTTCG=y TARGET_BIG_ENDIAN=y TARGET_NEED_FDT=y diff --git a/configs/targets/riscv32-linux-user.mak b/configs/targets/riscv32-linux-user.mak index bd2f1fd497..9761618e67 100644 --- a/configs/targets/riscv32-linux-user.mak +++ b/configs/targets/riscv32-linux-user.mak @@ -2,4 +2,5 @@ TARGET_ARCH=riscv32 TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-virtual.xml +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/riscv64-linux-user.mak b/configs/targets/riscv64-linux-user.mak index 4aca7662ce..cfd1fd382f 100644 --- a/configs/targets/riscv64-linux-user.mak +++ b/configs/targets/riscv64-linux-user.mak @@ -2,4 +2,5 @@ TARGET_ARCH=riscv64 TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml +CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/configs/targets/s390x-linux-user.mak b/configs/targets/s390x-linux-user.mak index e2978248ed..24c04c8589 100644 --- a/configs/targets/s390x-linux-user.mak +++ b/configs/targets/s390x-linux-user.mak @@ -2,4 +2,4 @@ TARGET_ARCH=s390x TARGET_SYSTBL_ABI=common,64 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y -TARGET_XML_FILES= gdb-xml/s390x-core64.xml gdb-xml/s390-acr.xml gdb-xml/s390-fpr.xml gdb-xml/s390-vx.xml gdb-xml/s390-cr.xml gdb-xml/s390-virt.xml gdb-xml/s390-gs.xml +TARGET_XML_FILES= gdb-xml/s390x-core64.xml gdb-xml/s390-acr.xml gdb-xml/s390-fpr.xml gdb-xml/s390-vx.xml gdb-xml/s390-cr.xml gdb-xml/s390-virt.xml gdb-xml/s390-virt-kvm.xml gdb-xml/s390-gs.xml diff --git a/configs/targets/s390x-softmmu.mak b/configs/targets/s390x-softmmu.mak index 258b4cf358..70d2f9f0ba 100644 --- a/configs/targets/s390x-softmmu.mak +++ b/configs/targets/s390x-softmmu.mak @@ -1,4 +1,4 @@ TARGET_ARCH=s390x TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y -TARGET_XML_FILES= gdb-xml/s390x-core64.xml gdb-xml/s390-acr.xml gdb-xml/s390-fpr.xml gdb-xml/s390-vx.xml gdb-xml/s390-cr.xml gdb-xml/s390-virt.xml gdb-xml/s390-gs.xml +TARGET_XML_FILES= gdb-xml/s390x-core64.xml gdb-xml/s390-acr.xml gdb-xml/s390-fpr.xml gdb-xml/s390-vx.xml gdb-xml/s390-cr.xml gdb-xml/s390-virt.xml gdb-xml/s390-virt-kvm.xml gdb-xml/s390-gs.xml diff --git a/configs/targets/sh4-linux-user.mak b/configs/targets/sh4-linux-user.mak index 0152d6621e..9908887566 100644 --- a/configs/targets/sh4-linux-user.mak +++ b/configs/targets/sh4-linux-user.mak @@ -1,5 +1,4 @@ TARGET_ARCH=sh4 TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl -TARGET_ALIGNED_ONLY=y TARGET_HAS_BFLT=y diff --git a/configs/targets/sh4-softmmu.mak b/configs/targets/sh4-softmmu.mak index 95896376c4..f9d62d91e4 100644 --- a/configs/targets/sh4-softmmu.mak +++ b/configs/targets/sh4-softmmu.mak @@ -1,2 +1 @@ TARGET_ARCH=sh4 -TARGET_ALIGNED_ONLY=y diff --git a/configs/targets/sh4eb-linux-user.mak b/configs/targets/sh4eb-linux-user.mak index 6724165efe..9db6b3609c 100644 --- a/configs/targets/sh4eb-linux-user.mak +++ b/configs/targets/sh4eb-linux-user.mak @@ -1,6 +1,5 @@ TARGET_ARCH=sh4 TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl -TARGET_ALIGNED_ONLY=y TARGET_BIG_ENDIAN=y TARGET_HAS_BFLT=y diff --git a/configs/targets/sh4eb-softmmu.mak b/configs/targets/sh4eb-softmmu.mak index dc8b30bf7a..226b1fc698 100644 --- a/configs/targets/sh4eb-softmmu.mak +++ b/configs/targets/sh4eb-softmmu.mak @@ -1,3 +1,2 @@ TARGET_ARCH=sh4 -TARGET_ALIGNED_ONLY=y TARGET_BIG_ENDIAN=y diff --git a/configs/targets/sparc-linux-user.mak b/configs/targets/sparc-linux-user.mak index 00e7bc1f07..abcfb8fc62 100644 --- a/configs/targets/sparc-linux-user.mak +++ b/configs/targets/sparc-linux-user.mak @@ -1,5 +1,4 @@ TARGET_ARCH=sparc TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl -TARGET_ALIGNED_ONLY=y TARGET_BIG_ENDIAN=y diff --git a/configs/targets/sparc-softmmu.mak b/configs/targets/sparc-softmmu.mak index a849190f01..a5d9200382 100644 --- a/configs/targets/sparc-softmmu.mak +++ b/configs/targets/sparc-softmmu.mak @@ -1,3 +1,3 @@ TARGET_ARCH=sparc -TARGET_ALIGNED_ONLY=y TARGET_BIG_ENDIAN=y +TARGET_SUPPORTS_MTTCG=y diff --git a/configs/targets/sparc32plus-linux-user.mak b/configs/targets/sparc32plus-linux-user.mak index a65c0951a1..6cc8fa516b 100644 --- a/configs/targets/sparc32plus-linux-user.mak +++ b/configs/targets/sparc32plus-linux-user.mak @@ -4,5 +4,4 @@ TARGET_BASE_ARCH=sparc TARGET_ABI_DIR=sparc TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl -TARGET_ALIGNED_ONLY=y TARGET_BIG_ENDIAN=y diff --git a/configs/targets/sparc64-linux-user.mak b/configs/targets/sparc64-linux-user.mak index 20fcb93fa4..52f05ec000 100644 --- a/configs/targets/sparc64-linux-user.mak +++ b/configs/targets/sparc64-linux-user.mak @@ -3,5 +3,4 @@ TARGET_BASE_ARCH=sparc TARGET_ABI_DIR=sparc TARGET_SYSTBL_ABI=common,64 TARGET_SYSTBL=syscall.tbl -TARGET_ALIGNED_ONLY=y TARGET_BIG_ENDIAN=y diff --git a/configs/targets/sparc64-softmmu.mak b/configs/targets/sparc64-softmmu.mak index c626ac3eae..36ca64ec41 100644 --- a/configs/targets/sparc64-softmmu.mak +++ b/configs/targets/sparc64-softmmu.mak @@ -1,4 +1,4 @@ TARGET_ARCH=sparc64 TARGET_BASE_ARCH=sparc -TARGET_ALIGNED_ONLY=y TARGET_BIG_ENDIAN=y +TARGET_SUPPORTS_MTTCG=y diff --git a/configure b/configure index af92336fd6..34836e388b 100755 --- a/configure +++ b/configure @@ -4,9 +4,8 @@ # # Unset some variables known to interfere with behavior of common tools, -# just as autoconf does. -CLICOLOR_FORCE= GREP_OPTIONS= -unset CLICOLOR_FORCE GREP_OPTIONS +# just as autoconf does. Unlike autoconf, we assume that unset exists. +unset CLICOLOR_FORCE GREP_OPTIONS BASH_ENV ENV MAIL MAILPATH CDPATH # Don't allow CCACHE, if present, to use cached results of compile tests! export CCACHE_RECACHE=yes @@ -31,19 +30,18 @@ then fi fi - mkdir build - touch $MARKER + if ! mkdir build || ! touch $MARKER + then + echo "ERROR: Could not create ./build directory. Check the permissions on" + echo "your source directory, or try doing an out-of-tree build." + exit 1 + fi cat > GNUmakefile <<'EOF' # This file is auto-generated by configure to support in-source tree # 'make' command invocation -ifeq ($(MAKECMDGOALS),) -recurse: all -endif - -.NOTPARALLEL: % -%: force +build: @echo 'changing dir to build for $(MAKE) "$(MAKECMDGOALS)"...' @$(MAKE) -C build -f Makefile $(MAKECMDGOALS) @if test "$(MAKECMDGOALS)" = "distclean" && \ @@ -51,13 +49,14 @@ endif then \ rm -rf build GNUmakefile ; \ fi -force: ; -.PHONY: force +%: build + @ +.PHONY: build GNUmakefile: ; EOF cd build - exec $source_path/configure "$@" + exec "$source_path/configure" "$@" fi # Temporary directory used for files created while @@ -67,8 +66,7 @@ fi # it when configure exits.) TMPDIR1="config-temp" rm -rf "${TMPDIR1}" -mkdir -p "${TMPDIR1}" -if [ $? -ne 0 ]; then +if ! mkdir -p "${TMPDIR1}"; then echo "ERROR: failed to create temporary directory" exit 1 fi @@ -76,8 +74,6 @@ fi TMPB="qemu-conf" TMPC="${TMPDIR1}/${TMPB}.c" TMPO="${TMPDIR1}/${TMPB}.o" -TMPCXX="${TMPDIR1}/${TMPB}.cxx" -TMPM="${TMPDIR1}/${TMPB}.m" TMPE="${TMPDIR1}/${TMPB}.exe" rm -f config.log @@ -85,15 +81,16 @@ rm -f config.log # Print a helpful header at the top of config.log echo "# QEMU configure log $(date)" >> config.log printf "# Configured with:" >> config.log -printf " '%s'" "$0" "$@" >> config.log -echo >> config.log -echo "#" >> config.log +# repeat the invocation to log and stdout for CI +invoke=$(printf " '%s'" "$0" "$@") +test -n "$GITLAB_CI" && echo "configuring with: $invoke" +{ echo "$invoke"; echo; echo "#"; } >> config.log quote_sh() { printf "%s" "$1" | sed "s,','\\\\'',g; s,.*,'&'," } -print_error() { +error_exit() { (echo echo "ERROR: $1" while test -n "$2"; do @@ -101,17 +98,13 @@ print_error() { shift done echo) >&2 -} - -error_exit() { - print_error "$@" exit 1 } do_compiler() { # Run the compiler, capturing its output to the log. First argument # is compiler binary to execute. - local compiler="$1" + compiler="$1" shift if test -n "$BASH_VERSION"; then eval ' echo >>config.log " @@ -122,84 +115,20 @@ lines: ${BASH_LINENO[*]}" $compiler "$@" >> config.log 2>&1 || return $? } -do_compiler_werror() { - # Run the compiler, capturing its output to the log. First argument - # is compiler binary to execute. - compiler="$1" - shift - if test -n "$BASH_VERSION"; then eval ' - echo >>config.log " -funcs: ${FUNCNAME[*]} -lines: ${BASH_LINENO[*]}" - '; fi - echo $compiler "$@" >> config.log - $compiler "$@" >> config.log 2>&1 || return $? - # Test passed. If this is an --enable-werror build, rerun - # the test with -Werror and bail out if it fails. This - # makes warning-generating-errors in configure test code - # obvious to developers. - if test "$werror" != "yes"; then - return 0 - fi - # Don't bother rerunning the compile if we were already using -Werror - case "$*" in - *-Werror*) - return 0 - ;; - esac - echo $compiler -Werror "$@" >> config.log - $compiler -Werror "$@" >> config.log 2>&1 && return $? - error_exit "configure test passed without -Werror but failed with -Werror." \ - "This is probably a bug in the configure script. The failing command" \ - "will be at the bottom of config.log." \ - "You can run configure with --disable-werror to bypass this check." -} - do_cc() { - do_compiler_werror "$cc" $CPU_CFLAGS "$@" -} - -do_cxx() { - do_compiler_werror "$cxx" $CPU_CFLAGS "$@" -} - -do_objc() { - do_compiler_werror "$objcc" $CPU_CFLAGS "$@" -} - -# Append $2 to the variable named $1, with space separation -add_to() { - eval $1=\${$1:+\"\$$1 \"}\$2 -} - -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="-D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS" - CONFIGURE_CXXFLAGS=$(echo "$CONFIGURE_CFLAGS" | sed s/-std=gnu11/-std=gnu++11/) - for arg in $QEMU_CFLAGS; do - case $arg in - -Wstrict-prototypes|-Wmissing-prototypes|-Wnested-externs|\ - -Wold-style-declaration|-Wold-style-definition|-Wredundant-decls) - ;; - *) - QEMU_CXXFLAGS=${QEMU_CXXFLAGS:+$QEMU_CXXFLAGS }$arg - ;; - esac - done + do_compiler "$cc" $CPU_CFLAGS "$@" } compile_object() { local_cflags="$1" - do_cc $CFLAGS $EXTRA_CFLAGS $CONFIGURE_CFLAGS $QEMU_CFLAGS $local_cflags -c -o $TMPO $TMPC + do_cc $CFLAGS $EXTRA_CFLAGS $local_cflags -c -o $TMPO $TMPC } compile_prog() { local_cflags="$1" local_ldflags="$2" - do_cc $CFLAGS $EXTRA_CFLAGS $CONFIGURE_CFLAGS $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC \ - $LDFLAGS $EXTRA_LDFLAGS $CONFIGURE_LDFLAGS $QEMU_LDFLAGS $local_ldflags + do_cc $CFLAGS $EXTRA_CFLAGS $local_cflags -o $TMPE $TMPC \ + $LDFLAGS $EXTRA_LDFLAGS $local_ldflags } # symbolically link $1 to $2. Portable version of "ln -sf". @@ -234,39 +163,24 @@ version_ge () { done } -glob() { - eval test -z '"${1#'"$2"'}"' -} - if printf %s\\n "$source_path" "$PWD" | grep -q "[[:space:]:]"; then error_exit "main directory cannot contain spaces nor colons" fi +# parse CC options first; some compiler tests are used to establish +# some defaults, based on the host environment + # default parameters +container_engine="auto" cpu="" -static="no" cross_compile="no" cross_prefix="" host_cc="cc" -stack_protector="" -safe_stack="" -use_containers="yes" -gdb_bin=$(command -v "gdb-multiarch" || command -v "gdb") - -if test -e "$source_path/.git" -then - git_submodules_action="update" -else - git_submodules_action="ignore" -fi - -git_submodules="ui/keycodemapdb" -git="git" - -# Don't accept a target_list environment variable. -unset target_list -unset target_list_exclude +EXTRA_CFLAGS="" +EXTRA_CXXFLAGS="" +EXTRA_OBJCFLAGS="" +EXTRA_LDFLAGS="" # Default value for a variable defining feature "foo". # * foo="no" feature will only be used if --enable-foo arg is given @@ -279,59 +193,8 @@ unset target_list_exclude # Always add --enable-foo and --disable-foo command line args. # 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. - default_feature="" -# parse CC options second -for opt do - optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)') - case "$opt" in - --without-default-features) - default_feature="no" - ;; - esac -done -EXTRA_CFLAGS="" -EXTRA_CXXFLAGS="" -EXTRA_OBJCFLAGS="" -EXTRA_LDFLAGS="" - -debug_tcg="no" -sanitizers="no" -tsan="no" -fortify_source="yes" -EXESUF="" -modules="no" -prefix="/usr/local" -qemu_suffix="qemu" -softmmu="yes" -linux_user="" -bsd_user="" -pie="" -coroutine="" -plugins="$default_feature" -meson="" -meson_args="" -ninja="" -bindir="bin" -skip_meson=no - -# The following Meson options are handled manually (still they -# are included in the automatically generated help message) - -# 1. Track which submodules are needed -if test "$default_feature" = no ; then - slirp="disabled" -else - slirp="auto" -fi -fdt="auto" - -# 2. Automatically enable/disable other options -tcg="enabled" -cfi="false" - -# parse CC options second for opt do optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)') case "$opt" in @@ -342,6 +205,8 @@ for opt do ;; --cxx=*) CXX="$optarg" ;; + --objcc=*) objcc="$optarg" + ;; --cpu=*) cpu="$optarg" ;; --extra-cflags=*) @@ -368,18 +233,50 @@ for opt do --cross-prefix-*) cc_arch=${opt#--cross-prefix-}; cc_arch=${cc_arch%%=*} eval "cross_prefix_${cc_arch}=\$optarg" ;; + --without-default-features) default_feature="no" + ;; esac done -# OS specific -# Using uname is really, really broken. Once we have the right set of checks -# we can eliminate its usage altogether. + +default_cflags='-O2 -g' +git_submodules_action="update" +docs="auto" +EXESUF="" +system="yes" +linux_user="" +bsd_user="" +plugins="$default_feature" +subdirs="" +ninja="" +python= +download="enabled" +skip_meson=no +use_containers="yes" +gdb_bin=$(command -v "gdb-multiarch" || command -v "gdb") +gdb_arches="" + +# Don't accept a target_list environment variable. +unset target_list +unset target_list_exclude + +# The following Meson options are handled manually (still they +# are included in the automatically generated help message) +# because they automatically enable/disable other options +tcg="auto" +cfi="false" + +# Meson has PIE as a boolean rather than enabled/disabled/auto, +# and we also need to check for -static-pie before Meson runs +# which requires knowing whether --static is enabled. +pie="" +static="no" # Preferred compiler: # ${CC} (if set) # ${cross_prefix}gcc (if cross-prefix specified) # system compiler if test -z "${CC}${cross_prefix}"; then - cc="$host_cc" + cc="cc" else cc="${CC-${cross_prefix}gcc}" fi @@ -390,41 +287,36 @@ else cxx="${CXX-${cross_prefix}g++}" fi +# Preferred ObjC compiler: +# $objcc (if set, i.e. via --objcc option) +# ${cross_prefix}clang (if cross-prefix specified) +# clang (if available) +# $cc +if test -z "${objcc}${cross_prefix}"; then + if has clang; then + objcc=clang + else + objcc="$cc" + fi +else + objcc="${objcc-${cross_prefix}clang}" +fi + ar="${AR-${cross_prefix}ar}" as="${AS-${cross_prefix}as}" ccas="${CCAS-$cc}" +dlltool="${DLLTOOL-${cross_prefix}dlltool}" objcopy="${OBJCOPY-${cross_prefix}objcopy}" ld="${LD-${cross_prefix}ld}" ranlib="${RANLIB-${cross_prefix}ranlib}" nm="${NM-${cross_prefix}nm}" -smbd="$SMBD" strip="${STRIP-${cross_prefix}strip}" widl="${WIDL-${cross_prefix}widl}" windres="${WINDRES-${cross_prefix}windres}" -pkg_config_exe="${PKG_CONFIG-${cross_prefix}pkg-config}" -query_pkg_config() { - "${pkg_config_exe}" ${QEMU_PKG_CONFIG_FLAGS} "$@" -} -pkg_config=query_pkg_config +windmc="${WINDMC-${cross_prefix}windmc}" +pkg_config="${PKG_CONFIG-${cross_prefix}pkg-config}" sdl2_config="${SDL2_CONFIG-${cross_prefix}sdl2-config}" -# default flags for all hosts -# We use -fwrapv to tell the compiler that we require a C dialect where -# left shift of signed integers is well defined and has the expected -# 2s-complement style results. (Both clang and gcc agree that it -# provides these semantics.) -QEMU_CFLAGS="-fno-strict-aliasing -fno-common -fwrapv" -QEMU_CFLAGS="-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_LDFLAGS= - -# Flags that are needed during configure but later taken care of by Meson -CONFIGURE_CFLAGS="-std=gnu11 -Wall" -CONFIGURE_LDFLAGS= - - check_define() { cat > $TMPC < $TMPC < -int main(void) { return 0; } -EOF - compile_object -} - write_c_skeleton() { cat > $TMPC <= 3.8. + # NB: a True python conditional creates a non-zero return code (Failure) + "$1" -c 'import sys; sys.exit(sys.version_info < (3,8))' +} + +first_python= +if test -z "${PYTHON}"; then + # A bare 'python' is traditionally python 2.x, but some distros + # have it as python 3.x, so check in both places. + for binary in python3 python python3.12 python3.11 \ + python3.10 python3.9 python3.8; do + if has "$binary"; then + python=$(command -v "$binary") + if check_py_version "$python"; then + # This one is good. + first_python= + break + else + first_python=$python + fi + fi + done +else + # Same as above, but only check the environment variable. + has "${PYTHON}" || error_exit "The PYTHON environment variable does not point to an executable" + python=$(command -v "$PYTHON") + if check_py_version "$python"; then + # This one is good. + first_python= + else + first_python=$first_python fi -done - +fi # Check for ancillary tools used in testing genisoimage= @@ -656,30 +574,34 @@ do fi done -# Default objcc to clang if available, otherwise use CC -if has clang; then - objcc=clang -else - objcc="$cc" -fi - -if test "$mingw32" = "yes" ; then +if test "$host_os" = "windows" ; then EXESUF=".exe" - # MinGW needs -mthreads for TLS and macro _MT. - CONFIGURE_CFLAGS="-mthreads $CONFIGURE_CFLAGS" - write_c_skeleton; - prefix="/qemu" - bindir="" - qemu_suffix="" fi -werror="" +meson_option_build_array() { + printf '[' + (if test "$host_os" = windows; then + IFS=\; + else + IFS=: + fi + for e in $1; do + printf '"""' + # backslash escape any '\' and '"' characters + printf "%s" "$e" | sed -e 's/\([\"]\)/\\\1/g' + printf '""",' + done) + printf ']\n' +} -. $source_path/scripts/meson-buildoptions.sh +. "$source_path/scripts/meson-buildoptions.sh" meson_options= meson_option_add() { - meson_options="$meson_options $(quote_sh "$1")" + local arg + for arg; do + meson_options="$meson_options $(quote_sh "$arg")" + done } meson_option_parse() { meson_options="$meson_options $(_meson_option_parse "$@")" @@ -690,14 +612,20 @@ meson_option_parse() { fi } +meson_add_machine_file() { + if test "$cross_compile" = "yes"; then + meson_option_add --cross-file "$1" + else + meson_option_add --native-file "$1" + fi +} + for opt do optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)') case "$opt" in --help|-h) show_help=yes ;; - --version|-V) exec cat $source_path/VERSION - ;; - --prefix=*) prefix="$optarg" + --version|-V) exec cat "$source_path/VERSION" ;; --cross-prefix=*) ;; @@ -707,22 +635,18 @@ for opt do ;; --cxx=*) ;; - --objcc=*) objcc="$optarg" + --objcc=*) ;; - --make=*) make="$optarg" + --make=*) ;; --install=*) ;; - --python=*) python="$optarg" ; explicit_python=yes + --python=*) python="$optarg" ;; --skip-meson) skip_meson=yes ;; - --meson=*) meson="$optarg" - ;; --ninja=*) ninja="$optarg" ;; - --smbd=*) smbd="$optarg" - ;; --extra-cflags=*) ;; --extra-cxxflags=*) @@ -735,15 +659,9 @@ for opt do ;; --cross-prefix-*) ;; - --enable-debug-info) meson_option_add -Ddebug=true + --enable-docs) docs=enabled ;; - --disable-debug-info) meson_option_add -Ddebug=false - ;; - --enable-modules) - modules="yes" - ;; - --disable-modules) - modules="no" + --disable-docs) docs=disabled ;; --cpu=*) ;; @@ -775,13 +693,7 @@ for opt do ;; --without-default-features) # processed above ;; - --static) - static="yes" - QEMU_PKG_CONFIG_FLAGS="--static $QEMU_PKG_CONFIG_FLAGS" - ;; - --bindir=*) bindir="$optarg" - ;; - --with-suffix=*) qemu_suffix="$optarg" + --static) static="yes" ;; --host=*|--build=*|\ --disable-dependency-tracking|\ @@ -793,41 +705,21 @@ for opt do # configure to be used by RPM and similar macros that set # lots of directory switches by default. ;; - --enable-debug-tcg) debug_tcg="yes" - ;; - --disable-debug-tcg) debug_tcg="no" - ;; --enable-debug) # Enable debugging options that aren't excessively noisy - debug_tcg="yes" + meson_option_parse --enable-debug-tcg "" + meson_option_parse --enable-debug-graph-lock "" meson_option_parse --enable-debug-mutex "" meson_option_add -Doptimization=0 - fortify_source="no" - ;; - --enable-sanitizers) sanitizers="yes" - ;; - --disable-sanitizers) sanitizers="no" - ;; - --enable-tsan) tsan="yes" - ;; - --disable-tsan) tsan="no" - ;; - --disable-slirp) slirp="disabled" - ;; - --enable-slirp) slirp="enabled" - ;; - --enable-slirp=git) slirp="internal" - ;; - --enable-slirp=*) slirp="$optarg" + default_cflags='-O0 -g' ;; --disable-tcg) tcg="disabled" - plugins="no" ;; --enable-tcg) tcg="enabled" ;; - --disable-system) softmmu="no" + --disable-system) system="no" ;; - --enable-system) softmmu="yes" + --enable-system) system="yes" ;; --disable-user) linux_user="no" ; @@ -846,55 +738,15 @@ for opt do ;; --disable-pie) pie="no" ;; - --enable-werror) werror="yes" + --enable-cfi) cfi=true ;; - --disable-werror) werror="no" + --disable-cfi) cfi=false ;; - --enable-stack-protector) stack_protector="yes" + --disable-download) download="disabled"; git_submodules_action=validate; ;; - --disable-stack-protector) stack_protector="no" + --enable-download) download="enabled"; git_submodules_action=update; ;; - --enable-safe-stack) safe_stack="yes" - ;; - --disable-safe-stack) safe_stack="no" - ;; - --enable-cfi) - cfi="true"; - meson_option_add -Db_lto=true - ;; - --disable-cfi) cfi="false" - ;; - --disable-fdt) fdt="disabled" - ;; - --enable-fdt) fdt="enabled" - ;; - --enable-fdt=git) fdt="internal" - ;; - --enable-fdt=*) fdt="$optarg" - ;; - --with-coroutine=*) coroutine="$optarg" - ;; - --disable-zlib-test) - ;; - --disable-virtio-blk-data-plane|--enable-virtio-blk-data-plane) - echo "$0: $opt is obsolete, virtio-blk data-plane is always on" >&2 - ;; - --enable-vhdx|--disable-vhdx) - echo "$0: $opt is obsolete, VHDX driver is always built" >&2 - ;; - --enable-uuid|--disable-uuid) - echo "$0: $opt is obsolete, UUID support is always built" >&2 - ;; - --with-git=*) git="$optarg" - ;; - --with-git-submodules=*) - git_submodules_action="$optarg" - ;; - --enable-plugins) if test "$mingw32" = "yes"; then - error_exit "TCG plugins not currently supported on Windows platforms" - else - plugins="yes" - fi + --enable-plugins) plugins="yes" ;; --disable-plugins) plugins="no" ;; @@ -902,92 +754,83 @@ for opt do ;; --disable-containers) use_containers="no" ;; + --container-engine=*) container_engine="$optarg" + ;; --gdb=*) gdb_bin="$optarg" ;; - # backwards compatibility options - --enable-trace-backend=*) meson_option_parse "--enable-trace-backends=$optarg" "$optarg" - ;; - --disable-blobs) meson_option_parse --disable-install-blobs "" - ;; - --enable-tcmalloc) meson_option_parse --enable-malloc=tcmalloc tcmalloc - ;; - --enable-jemalloc) meson_option_parse --enable-malloc=jemalloc jemalloc - ;; # everything else has the same name in configure and meson --*) meson_option_parse "$opt" "$optarg" ;; + # Pass through -Dxxxx options to meson + -D*) meson_options="$meson_options $opt" + ;; esac done -# test for any invalid configuration combinations -if test "$plugins" = "yes" -a "$tcg" = "disabled"; then - error_exit "Can't enable plugins on non-TCG builds" +if ! test -e "$source_path/.git" +then + git_submodules_action="validate" fi -case $git_submodules_action in - update|validate) - if test ! -e "$source_path/.git"; then - echo "ERROR: cannot $git_submodules_action git submodules without .git" - exit 1 - fi - ;; - ignore) - if ! test -f "$source_path/ui/keycodemapdb/README" - then - echo - echo "ERROR: missing GIT submodules" - echo - if test -e "$source_path/.git"; then - echo "--with-git-submodules=ignore specified but submodules were not" - echo "checked out. Please initialize and update submodules." - else - echo "This is not a GIT checkout but module content appears to" - echo "be missing. Do not use 'git archive' or GitHub download links" - echo "to acquire QEMU source archives. Non-GIT builds are only" - echo "supported with source archives linked from:" - echo - echo " https://www.qemu.org/download/#source" - echo - echo "Developers working with GIT can use scripts/archive-source.sh" - echo "if they need to create valid source archives." - fi - echo - exit 1 - fi - ;; - *) - echo "ERROR: invalid --with-git-submodules= value '$git_submodules_action'" - exit 1 - ;; -esac +if ! test -f "$source_path/subprojects/keycodemapdb/README" \ + && test "$download" = disabled +then + echo + echo "ERROR: missing subprojects" + echo + if test -e "$source_path/.git"; then + echo "--disable-download specified but subprojects were not" + echo 'checked out. Please invoke "meson subprojects download"' + echo "before configuring QEMU, or remove --disable-download" + echo "from the command line." + else + echo "This is not a GIT checkout but subproject content appears to" + echo "be missing. Do not use 'git archive' or GitHub download links" + echo "to acquire QEMU source archives. Non-GIT builds are only" + echo "supported with source archives linked from:" + echo + echo " https://www.qemu.org/download/#source" + echo + echo "Developers working with GIT can use scripts/archive-source.sh" + echo "if they need to create valid source archives." + fi + echo + exit 1 +fi default_target_list="" mak_wilds="" -if [ "$linux_user" != no ]; then - if [ "$targetos" = linux ] && [ -d $source_path/linux-user/include/host/$cpu ]; then - linux_user=yes - elif [ "$linux_user" = yes ]; then - error_exit "linux-user not supported on this architecture" +if [ -n "$host_arch" ] && [ -d "$source_path/common-user/host/$host_arch" ]; then + if [ "$linux_user" != no ]; then + if [ "$host_os" = linux ]; then + linux_user=yes + elif [ "$linux_user" = yes ]; then + error_exit "linux-user not supported on this architecture" + fi + if [ "$linux_user" = "yes" ]; then + mak_wilds="${mak_wilds} $source_path/configs/targets/*-linux-user.mak" + fi + fi + if [ "$bsd_user" != no ]; then + if [ "$bsd_user" = "" ]; then + test $host_os = freebsd && bsd_user=yes + fi + if [ "$bsd_user" = yes ] && ! [ -d "$source_path/bsd-user/$host_os" ]; then + error_exit "bsd-user not supported on this host OS" + fi + if [ "$bsd_user" = "yes" ]; then + mak_wilds="${mak_wilds} $source_path/configs/targets/*-bsd-user.mak" + fi + fi +else + if [ "$linux_user" = yes ] || [ "$bsd_user" = yes ]; then + error_exit "user mode emulation not supported on this architecture" fi fi -if [ "$bsd_user" != no ]; then - if [ "$bsd_user" = "" ]; then - test $targetos = freebsd && bsd_user=yes - fi - if [ "$bsd_user" = yes ] && ! [ -d $source_path/bsd-user/$targetos ]; then - error_exit "bsd-user not supported on this host OS" - fi -fi -if [ "$softmmu" = "yes" ]; then +if [ "$system" = "yes" ]; then mak_wilds="${mak_wilds} $source_path/configs/targets/*-softmmu.mak" fi -if [ "$linux_user" = "yes" ]; then - mak_wilds="${mak_wilds} $source_path/configs/targets/*-linux-user.mak" -fi -if [ "$bsd_user" = "yes" ]; then - mak_wilds="${mak_wilds} $source_path/configs/targets/*-bsd-user.mak" -fi for config in $mak_wilds; do target="$(basename "$config" .mak)" @@ -1004,17 +847,17 @@ Options: [defaults in brackets after descriptions] Standard options: --help print this message - --prefix=PREFIX install in PREFIX [$prefix] --target-list=LIST set target list (default: build all) $(echo Available targets: $default_target_list | \ fold -s -w 53 | sed -e 's/^/ /') --target-list-exclude=LIST exclude a set of targets from the default target-list Advanced options (experts only): + -Dmesonoptname=val passthrough option to meson unmodified --cross-prefix=PREFIX use PREFIX for compile tools, PREFIX can be blank [$cross_prefix] --cc=CC use C compiler CC [$cc] - --host-cc=CC use C compiler CC [$host_cc] for code run at - build time + --host-cc=CC when cross compiling, use C compiler CC for code run + at build time [$host_cc] --cxx=CXX use C++ compiler CXX [$cxx] --objcc=OBJCC use Objective-C compiler OBJCC [$objcc] --extra-cflags=CFLAGS append extra C compiler flags CFLAGS @@ -1024,34 +867,18 @@ Advanced options (experts only): --cross-cc-ARCH=CC use compiler when building ARCH guest test cases --cross-cc-cflags-ARCH= use compiler flags when building ARCH guest tests --cross-prefix-ARCH=PREFIX cross compiler prefix when building ARCH guest test cases - --make=MAKE use specified make [$make] --python=PYTHON use specified python [$python] - --meson=MESON use specified meson [$meson] --ninja=NINJA use specified ninja [$ninja] - --smbd=SMBD use specified smbd [$smbd] - --with-git=GIT use specified git [$git] - --with-git-submodules=update update git submodules (default if .git dir exists) - --with-git-submodules=validate fail if git submodules are not up to date - --with-git-submodules=ignore do not update or check git submodules (default if no .git dir) --static enable static build [$static] - --bindir=PATH install binaries in PATH - --with-suffix=SUFFIX suffix for QEMU data inside datadir/libdir/sysconfdir/docdir [$qemu_suffix] --without-default-features default all --enable-* options to "disabled" --without-default-devices do not include any device that is not needed to start the emulator (only use if you are including desired devices in configs/devices/) --with-devices-ARCH=NAME override default configs/devices --enable-debug enable common debug build options - --enable-sanitizers enable default sanitizers - --enable-tsan enable thread sanitizer - --disable-werror disable compilation abort on warning - --disable-stack-protector disable compiler-provided stack protection --cpu=CPU Build for host CPU [$cpu] - --with-coroutine=BACKEND coroutine backend. Supported options: - ucontext, sigaltstack, windows - --enable-plugins - enable plugins via shared library loading --disable-containers don't use containers for cross-building + --container-engine=TYPE which container engine to use [$container_engine] --gdb=GDB-path gdb to use for gdbstub tests [$gdb_bin] EOF meson_options_help @@ -1061,11 +888,6 @@ cat << EOF linux-user all linux usermode emulation targets bsd-user all BSD usermode emulation targets pie Position Independent Executables - modules modules support (non-Windows) - debug-tcg TCG debugging (default is disabled) - debug-info debugging information - safe-stack SafeStack Stack Smash Protection. Depends on - clang/llvm >= 3.7 and requires coroutine backend ucontext. NOTE: The object files are built at the place where configure is launched EOF @@ -1073,70 +895,96 @@ exit 0 fi # Remove old dependency files to make sure that they get properly regenerated -rm -f */config-devices.mak.d +rm -f ./*/config-devices.mak.d if test -z "$python" then - error_exit "Python not found. Use --python=/path/to/python" -fi -if ! has "$make" -then - error_exit "GNU make ($make) not found" + # If first_python is set, there was a binary somewhere even though + # it was not suitable. Use it for the error message. + if test -n "$first_python"; then + error_exit "Cannot use '$first_python', Python >= 3.8 is required." \ + "Use --python=/path/to/python to specify a supported Python." + else + error_exit "Python not found. Use --python=/path/to/python" + fi 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,6))'; then - error_exit "Cannot use '$python', Python >= 3.6 is required." \ - "Use --python=/path/to/python to specify a supported Python." +if ! check_py_version "$python"; then + error_exit "Cannot use '$python', Python >= 3.8 is required." \ + "Use --python=/path/to/python to specify a supported Python." \ + "Maybe try:" \ + " openSUSE Leap 15.3+: zypper install python39" \ + " CentOS 8: dnf install python38" fi -# Preserve python version since some functionality is dependent on it -python_version=$($python -c 'import sys; print("%d.%d.%d" % (sys.version_info[0], sys.version_info[1], sys.version_info[2]))' 2>/dev/null) +# Resolve PATH +python="$(command -v "$python")" + +# Create a Python virtual environment using our configured python. +# The stdout of this script will be the location of a symlink that +# points to the configured Python. +# Entry point scripts for pip, meson, and sphinx are generated if those +# packages are present. + +# Defaults assumed for now: +# - venv is cleared if it exists already; +# - venv is allowed to use system packages; +# - all setup can be performed offline; +# - missing packages may be fetched from PyPI, +# unless --disable-download is passed. +# - pip is not installed into the venv when possible, +# but ensurepip is called as a fallback when necessary. + +echo "python determined to be '$python'" +echo "python version: $($python --version)" + +python="$($python -B "${source_path}/python/scripts/mkvenv.py" create pyvenv)" +if test "$?" -ne 0 ; then + error_exit "python venv creation failed" +fi # Suppress writing compiled files python="$python -B" +mkvenv="$python ${source_path}/python/scripts/mkvenv.py" -if test -z "$meson"; then - if test "$explicit_python" = no && has meson && version_ge "$(meson --version)" 0.59.3; then - meson=meson - elif test $git_submodules_action != 'ignore' ; then - meson=git - elif test -e "${source_path}/meson/meson.py" ; then - meson=internal - else - if test "$explicit_python" = yes; then - error_exit "--python requires using QEMU's embedded Meson distribution, but it was not found." - else - error_exit "Meson not found. Use --meson=/path/to/meson" +# Finish preparing the virtual environment using vendored .whl files + +if $python -c 'import sys; sys.exit(sys.version_info >= (3,11))'; then + $mkvenv ensure --dir "${source_path}/python/wheels" \ + 'tomli>=1.2.0' || exit 1 +fi +$mkvenv ensuregroup --dir "${source_path}/python/wheels" \ + ${source_path}/pythondeps.toml meson || exit 1 + +# At this point, we expect Meson to be installed and available. +# We expect mkvenv or pip to have created pyvenv/bin/meson for us. +# We ignore PATH completely here: we want to use the venv's Meson +# *exclusively*. + +meson="$(cd pyvenv/bin; pwd)/meson" + +# Conditionally ensure Sphinx is installed. + +mkvenv_online_flag="" +if test "$download" = "enabled" ; then + mkvenv_online_flag=" --online" +fi + +if test "$docs" != "disabled" ; then + if ! $mkvenv ensuregroup \ + $(test "$docs" = "enabled" && echo "$mkvenv_online_flag") \ + ${source_path}/pythondeps.toml docs; + then + if test "$docs" = "enabled" ; then + exit 1 fi - fi -else - # Meson uses its own Python interpreter to invoke other Python scripts, - # but the user wants to use the one they specified with --python. - # - # We do not want to override the distro Python interpreter (and sometimes - # cannot: for example in Homebrew /usr/bin/meson is a bash script), so - # just require --meson=git|internal together with --python. - if test "$explicit_python" = yes; then - case "$meson" in - git | internal) ;; - *) error_exit "--python requires using QEMU's embedded Meson distribution." ;; - esac + echo "Sphinx not found/usable, disabling docs." + docs=disabled + else + docs=enabled fi fi -if test "$meson" = git; then - git_submodules="${git_submodules} meson" -fi - -case "$meson" in - git | internal) - meson="$python ${source_path}/meson/meson.py" - ;; - *) meson=$(command -v "$meson") ;; -esac - # Probe for ninja if test -z "$ninja"; then @@ -1151,32 +999,7 @@ if test -z "$ninja"; then fi fi -# Check that the C compiler works. Doing this here before testing -# the host CPU ensures that we had a valid CC to autodetect the -# $cpu var (and we should bail right here if that's not the case). -# It also allows the help message to be printed without a CC. -write_c_skeleton; -if compile_object ; then - : C compiler works ok -else - error_exit "\"$cc\" either does not exist or does not work" -fi -if ! compile_prog ; then - error_exit "\"$cc\" cannot build an executable (is your linker broken?)" -fi - -# Consult white-list to determine whether to enable werror -# by default. Only enable by default for git builds -if test -z "$werror" ; then - if test "$git_submodules_action" != "ignore" && \ - { test "$linux" = "yes" || test "$mingw32" = "yes"; }; then - werror="yes" - else - werror="no" - fi -fi - -if test "$targetos" = "bogus"; then +if test "$host_os" = "bogus"; then # Now that we know that we're not printing the help and that # the compiler works (so the results of the check_defines we used # to identify the OS are reliable), if we didn't recognize the @@ -1184,156 +1007,29 @@ if test "$targetos" = "bogus"; then error_exit "Unrecognized host OS (uname -s reports '$(uname -s)')" fi -# Check whether the compiler matches our minimum requirements: -cat > $TMPC << EOF -#if defined(__clang_major__) && defined(__clang_minor__) -# ifdef __apple_build_version__ -# if __clang_major__ < 10 || (__clang_major__ == 10 && __clang_minor__ < 0) -# error You need at least XCode Clang v10.0 to compile QEMU -# endif -# else -# if __clang_major__ < 6 || (__clang_major__ == 6 && __clang_minor__ < 0) -# error You need at least Clang v6.0 to compile QEMU -# endif -# endif -#elif defined(__GNUC__) && defined(__GNUC_MINOR__) -# if __GNUC__ < 7 || (__GNUC__ == 7 && __GNUC_MINOR__ < 4) -# error You need at least GCC v7.4.0 to compile QEMU -# endif -#else -# error You either need GCC or Clang to compiler QEMU -#endif -int main (void) { return 0; } -EOF -if ! compile_prog "" "" ; then - error_exit "You need at least GCC v7.4 or Clang v6.0 (or XCode Clang v10.0)" -fi - -# Accumulate -Wfoo and -Wno-bar separately. -# We will list all of the enable flags first, and the disable flags second. -# Note that we do not add -Werror, because that would enable it for all -# configure tests. If a configure test failed due to -Werror this would -# just silently disable some features, so it's too error prone. - -warn_flags= -add_to warn_flags -Wold-style-declaration -add_to warn_flags -Wold-style-definition -add_to warn_flags -Wtype-limits -add_to warn_flags -Wformat-security -add_to warn_flags -Wformat-y2k -add_to warn_flags -Winit-self -add_to warn_flags -Wignored-qualifiers -add_to warn_flags -Wempty-body -add_to warn_flags -Wnested-externs -add_to warn_flags -Wendif-labels -add_to warn_flags -Wexpansion-to-defined -add_to warn_flags -Wimplicit-fallthrough=2 - -nowarn_flags= -add_to nowarn_flags -Wno-initializer-overrides -add_to nowarn_flags -Wno-missing-include-dirs -add_to nowarn_flags -Wno-shift-negative-value -add_to nowarn_flags -Wno-string-plus-int -add_to nowarn_flags -Wno-typedef-redefinition -add_to nowarn_flags -Wno-tautological-type-limit-compare -add_to nowarn_flags -Wno-psabi - -gcc_flags="$warn_flags $nowarn_flags" - -cc_has_warning_flag() { - write_c_skeleton; - - # Use the positive sense of the flag when testing for -Wno-wombat - # support (gcc will happily accept the -Wno- form of unknown - # warning options). - optflag="$(echo $1 | sed -e 's/^-Wno-/-W/')" - compile_prog "-Werror $optflag" "" -} - -objcc_has_warning_flag() { - cat > $TMPM < $TMPC << EOF -int main(int argc, char *argv[]) -{ - char arr[64], *p = arr, *c = argv[0]; - while (*c) { - *p++ = *c++; - } - return 0; -} -EOF - gcc_flags="-fstack-protector-strong -fstack-protector-all" - sp_on=0 - for flag in $gcc_flags; do - # We need to check both a compile and a link, since some compiler - # setups fail only on a .c->.o compile and some only at link time - if compile_object "-Werror $flag" && - compile_prog "-Werror $flag" ""; then - QEMU_CFLAGS="$QEMU_CFLAGS $flag" - QEMU_LDFLAGS="$QEMU_LDFLAGS $flag" - sp_on=1 - break - fi - done - if test "$stack_protector" = yes; then - if test $sp_on = 0; then - error_exit "Stack protector not supported" - fi +# test for any invalid configuration combinations +if test "$host_os" = "windows" && ! has "$dlltool"; then + if test "$plugins" = "yes"; then + error_exit "TCG plugins requires dlltool to build on Windows platforms" fi + plugins="no" fi - -# Disable -Wmissing-braces on older compilers that warn even for -# the "universal" C zero initializer {0}. -cat > $TMPC << EOF -struct { - int a[2]; -} x = {0}; -EOF -if compile_object "-Werror" "" ; then - : -else - QEMU_CFLAGS="$QEMU_CFLAGS -Wno-missing-braces" +if test "$tcg" = "disabled" ; then + if test "$plugins" = "yes"; then + error_exit "Can't enable plugins on non-TCG builds" + fi + plugins="no" fi - -# Our module code doesn't support Windows -if test "$modules" = "yes" && test "$mingw32" = "yes" ; then - error_exit "Modules are not available for Windows" -fi - -# Static linking is not possible with plugins, modules or PIE if test "$static" = "yes" ; then - if test "$modules" = "yes" ; then - error_exit "static and modules are mutually incompatible" - fi if test "$plugins" = "yes"; then error_exit "static and plugins are mutually incompatible" - else - plugins="no" fi + plugins="no" +fi +if test "$plugins" != "no"; then + plugins=yes + subdirs="$subdirs contrib/plugins" fi -test "$plugins" = "" && plugins=yes cat > $TMPC << EOF @@ -1346,73 +1042,30 @@ static THREAD int tls_var; int main(void) { return tls_var; } EOF -# Check we support -fno-pie and -no-pie first; we will need the former for -# building ROMs, and both for everything if --disable-pie is passed. -if compile_prog "-Werror -fno-pie" "-no-pie"; then - CFLAGS_NOPIE="-fno-pie" - LDFLAGS_NOPIE="-no-pie" +if test "$host_os" = windows || test "$host_os" = haiku; then + if test "$pie" = "yes"; then + error_exit "PIE not available due to missing OS support" + fi + pie=no fi -if test "$static" = "yes"; then - if test "$pie" != "no" && compile_prog "-Werror -fPIE -DPIE" "-static-pie"; then - CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS" - QEMU_LDFLAGS="-static-pie $QEMU_LDFLAGS" +if test "$pie" != "no"; then + if test "$static" = "yes"; then + pie_ldflags=-static-pie + else + pie_ldflags=-pie + fi + if compile_prog "-Werror -fPIE -DPIE" "$pie_ldflags"; then pie="yes" elif test "$pie" = "yes"; then error_exit "-static-pie not available due to missing toolchain support" else - QEMU_LDFLAGS="-static $QEMU_LDFLAGS" + echo "Disabling PIE due to missing toolchain support" pie="no" fi -elif test "$pie" = "no"; then - CONFIGURE_CFLAGS="$CFLAGS_NOPIE $CONFIGURE_CFLAGS" - CONFIGURE_LDFLAGS="$LDFLAGS_NOPIE $CONFIGURE_LDFLAGS" -elif compile_prog "-Werror -fPIE -DPIE" "-pie"; then - CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS" - CONFIGURE_LDFLAGS="-pie $CONFIGURE_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 ########################################## -# __sync_fetch_and_and requires at least -march=i486. Many toolchains -# use i686 as default anyway, but for those that don't, an explicit -# specification is necessary - -if test "$cpu" = "i386"; then - cat > $TMPC << EOF -static int sfaa(int *ptr) -{ - return __sync_fetch_and_and(ptr, 0); -} - -int main(void) -{ - int val = 42; - val = __sync_val_compare_and_swap(&val, 0, 1); - sfaa(&val); - return val; -} -EOF - if ! compile_prog "" "" ; then - QEMU_CFLAGS="-march=i486 $QEMU_CFLAGS" - fi -fi - -if test "$tcg" = "enabled"; then - git_submodules="$git_submodules tests/fp/berkeley-testfloat-3" - git_submodules="$git_submodules tests/fp/berkeley-softfloat-3" -fi if test -z "${target_list+xxx}" ; then default_targets=yes @@ -1436,391 +1089,67 @@ else done fi -# see if system emulation was really requested -case " $target_list " in - *"-softmmu "*) softmmu=yes - ;; - *) softmmu=no - ;; -esac +if test "$tcg" = "auto"; then + if test -z "$target_list"; then + tcg="disabled" + else + tcg="enabled" + fi +fi -feature_not_found() { - feature=$1 - remedy=$2 +######################################### +# gdb test - error_exit "User requested feature $feature" \ - "configure was not able to find it." \ - "$remedy" -} +if test -n "$gdb_bin"; then + gdb_version=$($gdb_bin --version | head -n 1) + if version_ge ${gdb_version##* } 9.1; then + gdb_arches=$($python "$source_path/scripts/probe-gdb-support.py" $gdb_bin) + else + gdb_bin="" + fi +fi -# --- +########################################## # big/little endian test cat > $TMPC << EOF -#include -short big_endian[] = { 0x4269, 0x4765, 0x4e64, 0x4961, 0x4e00, 0, }; -short little_endian[] = { 0x694c, 0x7454, 0x654c, 0x6e45, 0x6944, 0x6e41, 0, }; -int main(int argc, char *argv[]) -{ - return printf("%s %s\n", (char *)big_endian, (char *)little_endian); -} -EOF - -if compile_prog ; then - if strings -a $TMPE | grep -q BiGeNdIaN ; then - bigendian="yes" - elif strings -a $TMPE | grep -q LiTtLeEnDiAn ; then - bigendian="no" - else - echo big/little test failed - exit 1 - fi -else - echo big/little test failed - exit 1 -fi - -########################################## -# pkg-config probe - -if ! has "$pkg_config_exe"; then - error_exit "pkg-config binary '$pkg_config_exe' not found" -fi - -########################################## -# glib support probe - -# When bumping glib_req_ver, please check also whether we should increase -# the _WIN32_WINNT setting in osdep.h according to the value from glib -glib_req_ver=2.56 -glib_modules=gthread-2.0 -if test "$modules" = yes; then - glib_modules="$glib_modules gmodule-export-2.0" -elif test "$plugins" = "yes"; then - glib_modules="$glib_modules gmodule-no-export-2.0" -fi - -for i in $glib_modules; do - if $pkg_config --atleast-version=$glib_req_ver $i; then - glib_cflags=$($pkg_config --cflags $i) - glib_libs=$($pkg_config --libs $i) - else - error_exit "glib-$glib_req_ver $i is required to compile QEMU" - fi -done - -glib_bindir="$($pkg_config --variable=bindir glib-2.0)" -if test -z "$glib_bindir" ; then - glib_bindir="$($pkg_config --variable=prefix glib-2.0)"/bin -fi - -# This workaround is required due to a bug in pkg-config file for glib as it -# doesn't define GLIB_STATIC_COMPILATION for pkg-config --static - -if test "$static" = yes && test "$mingw32" = yes; then - glib_cflags="-DGLIB_STATIC_COMPILATION $glib_cflags" -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 -# 32-bit QEMU while pointing at 64-bit glib headers -cat > $TMPC < -#include - -#define QEMU_BUILD_BUG_ON(x) \ - typedef char qemu_build_bug_on[(x)?-1:1] __attribute__((unused)); - -int main(void) { - QEMU_BUILD_BUG_ON(sizeof(size_t) != GLIB_SIZEOF_SIZE_T); - return 0; -} -EOF - -if ! compile_prog "$glib_cflags" "$glib_libs" ; then - error_exit "sizeof(size_t) doesn't match GLIB_SIZEOF_SIZE_T."\ - "You probably need to set PKG_CONFIG_LIBDIR"\ - "to point to the right pkg-config files for your"\ - "build target" -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) +#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# error LITTLE +#endif 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" - CONFIGURE_CFLAGS="$CONFIGURE_CFLAGS -Wno-unused-function" - fi -fi -########################################## -# fdt probe - -case "$fdt" in - auto | enabled | internal) - # Simpler to always update submodule, even if not needed. - git_submodules="${git_submodules} dtc" - ;; -esac - -########################################## -# check and set a backend for coroutine - -# We prefer ucontext, but it's not always possible. The fallback -# is sigcontext. On Windows the only valid backend is the Windows -# specific one. - -ucontext_works=no -if test "$darwin" != "yes"; then - cat > $TMPC << EOF -#include -#ifdef __stub_makecontext -#error Ignoring glibc stub makecontext which will always fail -#endif -int main(void) { makecontext(0, 0, 0); return 0; } -EOF - if compile_prog "" "" ; then - ucontext_works=yes - fi -fi - -if test "$coroutine" = ""; then - if test "$mingw32" = "yes"; then - coroutine=win32 - elif test "$ucontext_works" = "yes"; then - coroutine=ucontext - else - coroutine=sigaltstack - fi +if ! compile_prog ; then + bigendian="no" else - case $coroutine in - windows) - if test "$mingw32" != "yes"; then - error_exit "'windows' coroutine backend only valid for Windows" - fi - # Unfortunately the user visible backend name doesn't match the - # coroutine-*.c filename for this case, so we have to adjust it here. - coroutine=win32 - ;; - ucontext) - if test "$ucontext_works" != "yes"; then - feature_not_found "ucontext" - fi - ;; - sigaltstack) - if test "$mingw32" = "yes"; then - error_exit "only the 'windows' coroutine backend is valid for Windows" - fi - ;; - *) - error_exit "unknown coroutine backend $coroutine" - ;; - esac -fi - -################################################## -# SafeStack - - -if test "$safe_stack" = "yes"; then -cat > $TMPC << EOF -int main(int argc, char *argv[]) -{ -#if ! __has_feature(safe_stack) -#error SafeStack Disabled -#endif - return 0; -} -EOF - flag="-fsanitize=safe-stack" - # Check that safe-stack is supported and enabled. - if compile_prog "-Werror $flag" "$flag"; then - # Flag needed both at compilation and at linking - QEMU_CFLAGS="$QEMU_CFLAGS $flag" - QEMU_LDFLAGS="$QEMU_LDFLAGS $flag" - else - error_exit "SafeStack not supported by your compiler" - fi - if test "$coroutine" != "ucontext"; then - error_exit "SafeStack is only supported by the coroutine backend ucontext" - fi -else -cat > $TMPC << EOF -int main(int argc, char *argv[]) -{ -#if defined(__has_feature) -#if __has_feature(safe_stack) -#error SafeStack Enabled -#endif -#endif - return 0; -} -EOF -if test "$safe_stack" = "no"; then - # Make sure that safe-stack is disabled - if ! compile_prog "-Werror" ""; then - # SafeStack was already enabled, try to explicitly remove the feature - flag="-fno-sanitize=safe-stack" - if ! compile_prog "-Werror $flag" "$flag"; then - error_exit "Configure cannot disable SafeStack" - fi - QEMU_CFLAGS="$QEMU_CFLAGS $flag" - QEMU_LDFLAGS="$QEMU_LDFLAGS $flag" - fi -else # "$safe_stack" = "" - # Set safe_stack to yes or no based on pre-existing flags - if compile_prog "-Werror" ""; then - safe_stack="no" - else - safe_stack="yes" - if test "$coroutine" != "ucontext"; then - error_exit "SafeStack is only supported by the coroutine backend ucontext" - fi - fi -fi -fi - -######################################## -# check if ccache is interfering with -# semantic analysis of macros - -unset CCACHE_CPP2 -ccache_cpp2=no -cat > $TMPC << EOF -static const int Z = 1; -#define fn() ({ Z; }) -#define TAUT(X) ((X) == Z) -#define PAREN(X, Y) (X == Y) -#define ID(X) (X) -int main(int argc, char *argv[]) -{ - int x = 0, y = 0; - x = ID(x); - x = fn(); - fn(); - if (PAREN(x, y)) return 0; - if (TAUT(Z)) return 0; - return 0; -} -EOF - -if ! compile_object "-Werror"; then - ccache_cpp2=yes -fi - -################################################# -# clang does not support glibc + FORTIFY_SOURCE. - -if test "$fortify_source" != "no"; then - if echo | $cc -dM -E - | grep __clang__ > /dev/null 2>&1 ; then - fortify_source="no"; - elif test -n "$cxx" && has $cxx && - echo | $cxx -dM -E - | grep __clang__ >/dev/null 2>&1 ; then - fortify_source="no"; - else - fortify_source="yes" - fi -fi - -########################################## -# checks for sanitizers - -have_asan=no -have_ubsan=no -have_asan_iface_h=no -have_asan_iface_fiber=no - -if test "$sanitizers" = "yes" ; then - write_c_skeleton - if compile_prog "$CPU_CFLAGS -Werror -fsanitize=address" ""; then - have_asan=yes - fi - - # we could use a simple skeleton for flags checks, but this also - # detect the static linking issue of ubsan, see also: - # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84285 cat > $TMPC << EOF -#include -int main(void) { - void *tmp = malloc(10); - if (tmp != NULL) { - return *(int *)(tmp + 2); - } - return 1; -} +#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# error BIG +#endif +int main(void) { return 0; } EOF - if compile_prog "$CPU_CFLAGS -Werror -fsanitize=undefined" ""; then - have_ubsan=yes - fi - if check_include "sanitizer/asan_interface.h" ; then - have_asan_iface_h=yes - fi - - cat > $TMPC << EOF -#include -int main(void) { - __sanitizer_start_switch_fiber(0, 0, 0); - return 0; -} -EOF - if compile_prog "$CPU_CFLAGS -Werror -fsanitize=address" "" ; then - have_asan_iface_fiber=yes + if ! compile_prog ; then + bigendian="yes" + else + echo big/little test failed + exit 1 fi fi -# Thread sanitizer is, for now, much noisier than the other sanitizers; -# keep it separate until that is not the case. -if test "$tsan" = "yes" && test "$sanitizers" = "yes"; then - error_exit "TSAN is not supported with other sanitiziers." -fi -have_tsan=no -have_tsan_iface_fiber=no -if test "$tsan" = "yes" ; then - write_c_skeleton - if compile_prog "$CPU_CFLAGS -Werror -fsanitize=thread" "" ; then - have_tsan=yes - fi - cat > $TMPC << EOF -#include -int main(void) { - __tsan_create_fiber(0); - return 0; -} -EOF - if compile_prog "$CPU_CFLAGS -Werror -fsanitize=thread" "" ; then - have_tsan_iface_fiber=yes - fi -fi - -########################################## -# check for slirp - -case "$slirp" in - auto | enabled | internal) - # Simpler to always update submodule, even if not needed. - git_submodules="${git_submodules} slirp" - ;; -esac - ########################################## # functions to probe cross compilers container="no" -if test $use_containers = "yes"; then - if has "docker" || has "podman"; then - container=$($python $source_path/tests/docker/docker.py probe) +runc="" +if test $use_containers = "yes" && (has "docker" || has "podman"); then + case $($python "$source_path"/tests/docker/docker.py --engine "$container_engine" probe) in + *docker) container=docker ;; + podman) container=podman ;; + no) container=no ;; + esac + if test "$container" != "no"; then + docker_py="$python $source_path/tests/docker/docker.py --engine $container" + runc=$container fi fi @@ -1849,6 +1178,7 @@ fi : ${cross_prefix_sh4="sh4-linux-gnu-"} : ${cross_prefix_sparc64="sparc64-linux-gnu-"} : ${cross_prefix_sparc="$cross_prefix_sparc64"} +: ${cross_prefix_tricore="tricore-"} : ${cross_prefix_x86_64="x86_64-linux-gnu-"} : ${cross_cc_aarch64_be="$cross_cc_aarch64"} @@ -1856,9 +1186,9 @@ fi : ${cross_cc_armeb="$cross_cc_arm"} : ${cross_cc_cflags_armeb="-mbig-endian"} : ${cross_cc_hexagon="hexagon-unknown-linux-musl-clang"} -: ${cross_cc_cflags_hexagon="-mv67 -O2 -static"} +: ${cross_cc_cflags_hexagon="-mv73 -O2 -static"} : ${cross_cc_cflags_i386="-m32"} -: ${cross_cc_cflags_ppc="-m32"} +: ${cross_cc_cflags_ppc="-m32 -mbig-endian"} : ${cross_cc_cflags_ppc64="-m64 -mbig-endian"} : ${cross_cc_ppc64le="$cross_cc_ppc64"} : ${cross_cc_cflags_ppc64le="-m64 -mlittle-endian"} @@ -1870,6 +1200,7 @@ fi : ${cross_cc_e2k_ignore_checks="yes"} compute_target_variable() { + eval "$2=" if eval test -n "\"\${cross_prefix_$1}\""; then if eval has "\"\${cross_prefix_$1}\$3\""; then eval "$2=\"\${cross_prefix_$1}\$3\"" @@ -1877,10 +1208,33 @@ compute_target_variable() { fi } +have_target() { + for i; do + case " $target_list " in + *" $i "*) return 0;; + *) ;; + esac + done + return 1 +} + +# probe_target_compiler TARGET +# +# Look for a compiler for the given target, either native or cross. +# Set variables target_* if a compiler is found, and container_cross_* +# if a Docker-based cross-compiler image is known for the target. +# Set got_cross_cc to yes/no depending on whether a non-container-based +# compiler was found. +# +# If TARGET is a user-mode emulation target, also set build_static to +# "y" if static linking is possible. +# probe_target_compiler() { # reset all output variables + got_cross_cc=no container_image= container_hosts= + container_cross_prefix= container_cross_cc= container_cross_ar= container_cross_as= @@ -1889,16 +1243,9 @@ probe_target_compiler() { container_cross_objcopy= container_cross_ranlib= container_cross_strip= - target_cc= - target_ar= - target_as= - target_ld= - target_nm= - target_objcopy= - target_ranlib= - target_strip= - case $1 in + target_arch=${1%%-*} + case $target_arch in aarch64) container_hosts="x86_64 aarch64" ;; alpha) container_hosts=x86_64 ;; arm) container_hosts="x86_64 aarch64" ;; @@ -1906,6 +1253,7 @@ probe_target_compiler() { hexagon) container_hosts=x86_64 ;; hppa) container_hosts=x86_64 ;; i386) container_hosts=x86_64 ;; + loongarch64) container_hosts=x86_64 ;; m68k) container_hosts=x86_64 ;; microblaze) container_hosts=x86_64 ;; mips64el) container_hosts=x86_64 ;; @@ -1920,23 +1268,41 @@ probe_target_compiler() { sh4) container_hosts=x86_64 ;; sparc64) container_hosts=x86_64 ;; tricore) container_hosts=x86_64 ;; - x86_64) container_hosts="aarch64 ppc64el x86_64" ;; + x86_64) container_hosts="aarch64 ppc64le x86_64" ;; xtensa*) container_hosts=x86_64 ;; esac for host in $container_hosts; do test "$container" != no || continue test "$host" = "$cpu" || continue - case $1 in + case $target_arch in + # debian-all-test-cross architectures + + hppa|m68k|mips|riscv64|sparc64) + container_image=debian-all-test-cross + ;; + mips64) + container_image=debian-all-test-cross + container_cross_prefix=mips64-linux-gnuabi64- + ;; + ppc|ppc64|ppc64le) + container_image=debian-all-test-cross + container_cross_prefix=powerpc${target_arch#ppc}-linux-gnu- + ;; + + # debian-legacy-test-cross architectures (need Debian 11) + # - libc6.1-dev-alpha-cross: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1054412 + # - sh4-linux-user: binaries don't run with bookworm compiler + + alpha|sh4) + container_image=debian-legacy-test-cross + ;; + + # architectures with individual containers + aarch64) # We don't have any bigendian build tools so we only use this for AArch64 container_image=debian-arm64-cross - container_cross_prefix=aarch64-linux-gnu- - container_cross_cc=${container_cross_prefix}gcc-10 - ;; - alpha) - container_image=debian-alpha-cross - container_cross_prefix=alpha-linux-gnu- ;; arm) # We don't have any bigendian build tools so we only use this for ARM @@ -1945,95 +1311,43 @@ probe_target_compiler() { ;; cris) container_image=fedora-cris-cross - container_cross_prefix=cris-linux-gnu- ;; hexagon) - container_image=debian-hexagon-cross container_cross_prefix=hexagon-unknown-linux-musl- container_cross_cc=${container_cross_prefix}clang ;; - hppa) - container_image=debian-hppa-cross - container_cross_prefix=hppa-linux-gnu- - ;; i386) - container_image=fedora-i386-cross - container_cross_prefix= + container_image=debian-i686-cross + container_cross_prefix=i686-linux-gnu- ;; - m68k) - container_image=debian-m68k-cross - container_cross_prefix=m68k-linux-gnu- + loongarch64) + container_image=debian-loongarch-cross + container_cross_prefix=loongarch64-unknown-linux-gnu- ;; microblaze) - container_image=debian-microblaze-cross container_cross_prefix=microblaze-linux-musl- ;; mips64el) container_image=debian-mips64el-cross container_cross_prefix=mips64el-linux-gnuabi64- ;; - mips64) - container_image=debian-mips64-cross - container_cross_prefix=mips64-linux-gnuabi64- - ;; - mipsel) - container_image=debian-mipsel-cross - container_cross_prefix=mipsel-linux-gnu- - ;; - mips) - container_image=debian-mips-cross - container_cross_prefix=mips-linux-gnu- - ;; - nios2) - container_image=debian-nios2-cross - container_cross_prefix=nios2-linux-gnu- - ;; - ppc) - container_image=debian-powerpc-test-cross - container_cross_prefix=powerpc-linux-gnu- - container_cross_cc=${container_cross_prefix}gcc-10 - ;; - ppc64|ppc64le) - container_image=debian-powerpc-test-cross - container_cross_prefix=powerpc${1#ppc}-linux-gnu- - container_cross_cc=${container_cross_prefix}gcc-10 - ;; - riscv64) - container_image=debian-riscv64-test-cross - container_cross_prefix=riscv64-linux-gnu- - ;; - s390x) - container_image=debian-s390x-cross - container_cross_prefix=s390x-linux-gnu- - ;; - sh4) - container_image=debian-sh4-cross - container_cross_prefix=sh4-linux-gnu- - ;; - sparc64) - container_image=debian-sparc64-cross - container_cross_prefix=sparc64-linux-gnu- - ;; tricore) container_image=debian-tricore-cross container_cross_prefix=tricore- - container_cross_as=tricore-as - container_cross_ld=tricore-ld - break ;; x86_64) container_image=debian-amd64-cross - container_cross_prefix=x86_64-linux-gnu- ;; xtensa*) - # FIXME: xtensa-linux-user? - container_hosts=x86_64 container_image=debian-xtensa-cross # default to the dc232b cpu container_cross_prefix=/opt/2020.07/xtensa-dc232b-elf/bin/xtensa-dc232b-elf- ;; esac + # Debian and GNU architecture names usually match + : ${container_image:=debian-$target_arch-cross} + : ${container_cross_prefix:=$target_arch-linux-gnu-} : ${container_cross_cc:=${container_cross_prefix}gcc} : ${container_cross_ar:=${container_cross_prefix}ar} : ${container_cross_as:=${container_cross_prefix}as} @@ -2044,161 +1358,160 @@ probe_target_compiler() { : ${container_cross_strip:=${container_cross_prefix}strip} done - eval "target_cflags=\${cross_cc_cflags_$1}" - if eval test -n "\"\${cross_cc_$1}\""; then - if eval has "\"\${cross_cc_$1}\""; then - eval "target_cc=\"\${cross_cc_$1}\"" + try=cross + # For softmmu/roms also look for a bi-endian or multilib-enabled host compiler + if [ "${1%softmmu}" != "$1" ] || test "$target_arch" = "$cpu"; then + case "$target_arch:$cpu" in + aarch64_be:aarch64 | \ + armeb:arm | \ + i386:x86_64 | \ + mips*:mips64 | \ + ppc*:ppc64 | \ + sparc:sparc64 | \ + "$cpu:$cpu") + try='native cross' ;; + esac + fi + eval "target_cflags=\${cross_cc_cflags_$target_arch}" + for thistry in $try; do + case $thistry in + native) + target_cc=$cc + target_ccas=$ccas + target_ar=$ar + target_as=$as + target_ld=$ld + target_nm=$nm + target_objcopy=$objcopy + target_ranlib=$ranlib + target_strip=$strip + ;; + cross) + target_cc= + if eval test -n "\"\${cross_cc_$target_arch}\""; then + if eval has "\"\${cross_cc_$target_arch}\""; then + eval "target_cc=\"\${cross_cc_$target_arch}\"" + fi + else + compute_target_variable $target_arch target_cc gcc + fi + target_ccas=$target_cc + compute_target_variable $target_arch target_ar ar + compute_target_variable $target_arch target_as as + compute_target_variable $target_arch target_ld ld + compute_target_variable $target_arch target_nm nm + compute_target_variable $target_arch target_objcopy objcopy + compute_target_variable $target_arch target_ranlib ranlib + compute_target_variable $target_arch target_strip strip + ;; + esac + + if test -n "$target_cc"; then + case $target_arch in + i386|x86_64) + if $target_cc --version | grep -qi "clang"; then + continue + fi + ;; + esac + elif test -n "$target_as" && test -n "$target_ld"; then + # Special handling for assembler only targets + case $target in + tricore-softmmu) + build_static= + got_cross_cc=yes + break + ;; + *) + continue + ;; + esac + else + continue fi - else - compute_target_variable $1 target_cc gcc - fi - target_ccas=$target_cc - compute_target_variable $1 target_ar ar - compute_target_variable $1 target_as as - compute_target_variable $1 target_ld ld - compute_target_variable $1 target_nm nm - compute_target_variable $1 target_objcopy objcopy - compute_target_variable $1 target_ranlib ranlib - compute_target_variable $1 target_strip strip - if test "$1" = $cpu; then - : ${target_cc:=$cc} - : ${target_ccas:=$ccas} - : ${target_as:=$as} - : ${target_ld:=$ld} - : ${target_ar:=$ar} - : ${target_as:=$as} - : ${target_ld:=$ld} - : ${target_nm:=$nm} - : ${target_objcopy:=$objcopy} - : ${target_ranlib:=$ranlib} - : ${target_strip:=$strip} - fi - if test -n "$target_cc"; then + + write_c_skeleton case $1 in - i386|x86_64) - if $target_cc --version | grep -qi "clang"; then - unset target_cc + *-softmmu) + if do_compiler "$target_cc" $target_cflags -o $TMPO -c $TMPC && + do_compiler "$target_cc" $target_cflags -r -nostdlib -o "${TMPDIR1}/${TMPB}2.o" "$TMPO" -lgcc; then + got_cross_cc=yes + break + fi + ;; + *) + if do_compiler "$target_cc" $target_cflags -o $TMPE $TMPC -static ; then + build_static=y + got_cross_cc=yes + break + fi + if do_compiler "$target_cc" $target_cflags -o $TMPE $TMPC ; then + build_static= + got_cross_cc=yes + break fi ;; esac - fi -} - -probe_target_compilers() { - for i; do - probe_target_compiler $i - test -n "$target_cc" && return 0 done + if test $got_cross_cc != yes; then + build_static= + target_cc= + target_ccas= + target_ar= + target_as= + target_ld= + target_nm= + target_objcopy= + target_ranlib= + target_strip= + fi + test -n "$target_cc" } write_target_makefile() { - if test -n "$target_cc"; then - echo "CC=$target_cc" - echo "CCAS=$target_ccas" - fi - if test -n "$target_ar"; then - echo "AR=$target_ar" - fi - if test -n "$target_as"; then - echo "AS=$target_as" - fi - if test -n "$target_ld"; then - echo "LD=$target_ld" - fi - if test -n "$target_nm"; then - echo "NM=$target_nm" - fi - if test -n "$target_objcopy"; then - echo "OBJCOPY=$target_objcopy" - fi - if test -n "$target_ranlib"; then - echo "RANLIB=$target_ranlib" - fi - if test -n "$target_strip"; then - echo "STRIP=$target_strip" - fi -} - -write_container_target_makefile() { - if test -n "$container_cross_cc"; then - echo "CC=\$(DOCKER_SCRIPT) cc --cc $container_cross_cc -i qemu/$container_image -s $source_path --" - echo "CCAS=\$(DOCKER_SCRIPT) cc --cc $container_cross_cc -i qemu/$container_image -s $source_path --" - fi - echo "AR=\$(DOCKER_SCRIPT) cc --cc $container_cross_ar -i qemu/$container_image -s $source_path --" - echo "AS=\$(DOCKER_SCRIPT) cc --cc $container_cross_as -i qemu/$container_image -s $source_path --" - echo "LD=\$(DOCKER_SCRIPT) cc --cc $container_cross_ld -i qemu/$container_image -s $source_path --" - echo "NM=\$(DOCKER_SCRIPT) cc --cc $container_cross_nm -i qemu/$container_image -s $source_path --" - echo "OBJCOPY=\$(DOCKER_SCRIPT) cc --cc $container_cross_objcopy -i qemu/$container_image -s $source_path --" - echo "RANLIB=\$(DOCKER_SCRIPT) cc --cc $container_cross_ranlib -i qemu/$container_image -s $source_path --" - echo "STRIP=\$(DOCKER_SCRIPT) cc --cc $container_cross_strip -i qemu/$container_image -s $source_path --" -} - - - -########################################## -# End of CC checks -# After here, no more $cc or $ld runs - -write_c_skeleton - -if test "$fortify_source" = "yes" ; then - QEMU_CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $QEMU_CFLAGS" -fi - -case "$ARCH" in -alpha) - # Ensure there's only a single GP - QEMU_CFLAGS="-msmall-data $QEMU_CFLAGS" -;; -esac - -if test "$have_asan" = "yes"; then - 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." - elif test "$have_asan_iface_fiber" = "no" ; then - echo "ASAN build enabled, but ASAN header is too old." \ - "Without code annotation, the report may be inferior." - fi -fi -if test "$have_tsan" = "yes" ; then - if test "$have_tsan_iface_fiber" = "yes" ; then - QEMU_CFLAGS="-fsanitize=thread $QEMU_CFLAGS" - QEMU_LDFLAGS="-fsanitize=thread $QEMU_LDFLAGS" - else - error_exit "Cannot enable TSAN due to missing fiber annotation interface." - fi -elif test "$tsan" = "yes" ; then - error_exit "Cannot enable TSAN due to missing sanitize thread interface." -fi -if test "$have_ubsan" = "yes"; then - QEMU_CFLAGS="-fsanitize=undefined $QEMU_CFLAGS" - QEMU_LDFLAGS="-fsanitize=undefined $QEMU_LDFLAGS" -fi - -########################################## - -# Exclude --warn-common with TSan to suppress warnings from the TSan libraries. -if test "$solaris" = "no" && test "$tsan" = "no"; then - if $ld --version 2>/dev/null | grep "GNU ld" >/dev/null 2>/dev/null ; then - QEMU_LDFLAGS="-Wl,--warn-common $QEMU_LDFLAGS" + echo "EXTRA_CFLAGS=$target_cflags" + if test -z "$target_cc" && test -z "$target_as"; then + test -z "$container_image" && error_exit "Internal error: could not find cross compiler for $1?" + echo "$1: docker-image-$container_image" >> Makefile.prereqs + if test -n "$container_cross_cc"; then + echo "CC=$docker_py cc --cc $container_cross_cc -i qemu/$container_image -s $source_path --" + echo "CCAS=$docker_py cc --cc $container_cross_cc -i qemu/$container_image -s $source_path --" fi -fi - -# Guest agent Windows MSI package - -if test "$QEMU_GA_MANUFACTURER" = ""; then - QEMU_GA_MANUFACTURER=QEMU -fi -if test "$QEMU_GA_DISTRO" = ""; then - QEMU_GA_DISTRO=Linux -fi -if test "$QEMU_GA_VERSION" = ""; then - QEMU_GA_VERSION=$(cat $source_path/VERSION) -fi - + echo "AR=$docker_py cc --cc $container_cross_ar -i qemu/$container_image -s $source_path --" + echo "AS=$docker_py cc --cc $container_cross_as -i qemu/$container_image -s $source_path --" + echo "LD=$docker_py cc --cc $container_cross_ld -i qemu/$container_image -s $source_path --" + echo "NM=$docker_py cc --cc $container_cross_nm -i qemu/$container_image -s $source_path --" + echo "OBJCOPY=$docker_py cc --cc $container_cross_objcopy -i qemu/$container_image -s $source_path --" + echo "RANLIB=$docker_py cc --cc $container_cross_ranlib -i qemu/$container_image -s $source_path --" + echo "STRIP=$docker_py cc --cc $container_cross_strip -i qemu/$container_image -s $source_path --" + else + if test -n "$target_cc"; then + echo "CC=$target_cc" + echo "CCAS=$target_ccas" + fi + if test -n "$target_ar"; then + echo "AR=$target_ar" + fi + if test -n "$target_as"; then + echo "AS=$target_as" + fi + if test -n "$target_ld"; then + echo "LD=$target_ld" + fi + if test -n "$target_nm"; then + echo "NM=$target_nm" + fi + if test -n "$target_objcopy"; then + echo "OBJCOPY=$target_objcopy" + fi + if test -n "$target_ranlib"; then + echo "RANLIB=$target_ranlib" + fi + if test -n "$target_strip"; then + echo "STRIP=$target_strip" + fi + fi +} ####################################### # cross-compiled firmware targets @@ -2212,50 +1525,43 @@ fi # tests might fail. Prefer to keep the relevant files in their own # directory and symlink the directory instead. LINKS="Makefile" -LINKS="$LINKS tests/tcg/Makefile.target" +LINKS="$LINKS docs/config" LINKS="$LINKS pc-bios/optionrom/Makefile" LINKS="$LINKS pc-bios/s390-ccw/Makefile" LINKS="$LINKS pc-bios/vof/Makefile" LINKS="$LINKS .gdbinit scripts" # scripts needed by relative path in .gdbinit LINKS="$LINKS tests/avocado tests/data" -LINKS="$LINKS tests/qemu-iotests/check" +LINKS="$LINKS tests/qemu-iotests/check tests/qemu-iotests/Makefile" LINKS="$LINKS python" LINKS="$LINKS contrib/plugins/Makefile " for f in $LINKS ; do if [ -e "$source_path/$f" ]; then - mkdir -p `dirname ./$f` symlink "$source_path/$f" "$f" fi done -# Mac OS X ships with a broken assembler -roms= -probe_target_compilers i386 x86_64 -if test -n "$target_cc" && - test "$targetos" != "darwin" && test "$targetos" != "sunos" && \ - test "$targetos" != "haiku" && test "$softmmu" = yes ; then - # Different host OS linkers have different ideas about the name of the ELF - # emulation. Linux and OpenBSD/amd64 use 'elf_i386'; FreeBSD uses the _fbsd - # variant; OpenBSD/i386 uses the _obsd variant; and Windows uses i386pe. - for emu in elf_i386 elf_i386_fbsd elf_i386_obsd i386pe; do - if "$target_ld" -verbose 2>&1 | grep -q "^[[:space:]]*$emu[[:space:]]*$"; then - ld_i386_emulation="$emu" - break - fi - done - if test -n "$ld_i386_emulation"; then - roms="optionrom" - config_mak=pc-bios/optionrom/config.mak - echo "# Automatically generated by configure - do not modify" > $config_mak - echo "TOPSRC_DIR=$source_path" >> $config_mak - echo "LD_I386_EMULATION=$ld_i386_emulation" >> $config_mak - write_target_makefile >> $config_mak - fi +# use included Linux headers for KVM architectures +if test "$host_os" = "linux" && test -n "$linux_arch"; then + symlink "$source_path/linux-headers/asm-$linux_arch" linux-headers/asm fi -probe_target_compilers ppc ppc64 -if test -n "$target_cc" && test "$softmmu" = yes; then - roms="$roms vof" +echo "# Automatically generated by configure - do not modify" > Makefile.prereqs + +# Mac OS X ships with a broken assembler +if have_target i386-softmmu x86_64-softmmu && \ + test "$host_os" != "darwin" && test "$host_os" != "sunos" && \ + test "$host_os" != "haiku" && \ + probe_target_compiler i386-softmmu; then + subdirs="$subdirs pc-bios/optionrom" + config_mak=pc-bios/optionrom/config.mak + echo "# Automatically generated by configure - do not modify" > $config_mak + echo "TOPSRC_DIR=$source_path" >> $config_mak + write_target_makefile >> $config_mak +fi + +if have_target ppc-softmmu ppc64-softmmu && \ + probe_target_compiler ppc-softmmu; then + subdirs="$subdirs pc-bios/vof" config_mak=pc-bios/vof/config.mak echo "# Automatically generated by configure - do not modify" > $config_mak echo "SRC_DIR=$source_path/pc-bios/vof" >> $config_mak @@ -2264,8 +1570,8 @@ fi # Only build s390-ccw bios if the compiler has -march=z900 or -march=z10 # (which is the lowest architecture level that Clang supports) -probe_target_compiler s390x -if test -n "$target_cc" && test "$softmmu" = yes; then +if have_target s390x-softmmu && probe_target_compiler s390x-softmmu && \ + GIT=git "$source_path/scripts/git-submodule.sh" "$git_submodules_action" roms/SLOF >> config.log 2>&1; then write_c_skeleton do_compiler "$target_cc" $target_cc_cflags -march=z900 -o $TMPO -c $TMPC has_z900=$? @@ -2274,217 +1580,77 @@ if test -n "$target_cc" && test "$softmmu" = yes; then echo "WARNING: Your compiler does not support the z900!" echo " The s390-ccw bios will only work with guest CPUs >= z10." fi - roms="$roms s390-ccw" + subdirs="$subdirs pc-bios/s390-ccw" config_mak=pc-bios/s390-ccw/config-host.mak echo "# Automatically generated by configure - do not modify" > $config_mak echo "SRC_PATH=$source_path/pc-bios/s390-ccw" >> $config_mak + echo "GIT_SUBMODULES_ACTION=$git_submodules_action" >> $config_mak write_target_makefile >> $config_mak - # SLOF is required for building the s390-ccw firmware on s390x, - # since it is using the libnet code from SLOF for network booting. - git_submodules="${git_submodules} roms/SLOF" fi fi ####################################### # generate config-host.mak -# 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 - cat > $TMPC < $TMPCXX < $config_host_mak echo >> $config_host_mak echo all: >> $config_host_mak -echo "GIT=$git" >> $config_host_mak -echo "GIT_SUBMODULES=$git_submodules" >> $config_host_mak -echo "GIT_SUBMODULES_ACTION=$git_submodules_action" >> $config_host_mak -if test "$debug_tcg" = "yes" ; then - echo "CONFIG_DEBUG_TCG=y" >> $config_host_mak -fi -if test "$mingw32" = "yes" ; then - echo "CONFIG_WIN32=y" >> $config_host_mak - echo "QEMU_GA_MANUFACTURER=${QEMU_GA_MANUFACTURER}" >> $config_host_mak - echo "QEMU_GA_DISTRO=${QEMU_GA_DISTRO}" >> $config_host_mak - echo "QEMU_GA_VERSION=${QEMU_GA_VERSION}" >> $config_host_mak -else - echo "CONFIG_POSIX=y" >> $config_host_mak -fi - -if test "$linux" = "yes" ; then - echo "CONFIG_LINUX=y" >> $config_host_mak -fi - -if test "$darwin" = "yes" ; then - echo "CONFIG_DARWIN=y" >> $config_host_mak -fi - -if test "$solaris" = "yes" ; then - echo "CONFIG_SOLARIS=y" >> $config_host_mak -fi -if test "$static" = "yes" ; then - echo "CONFIG_STATIC=y" >> $config_host_mak -fi echo "SRC_PATH=$source_path" >> $config_host_mak echo "TARGET_DIRS=$target_list" >> $config_host_mak -if test "$modules" = "yes"; then - echo "CONFIG_MODULES=y" >> $config_host_mak +echo "GDB=$gdb_bin" >> $config_host_mak +if test "$container" != no; then + echo "RUNC=$runc" >> $config_host_mak fi - -# XXX: suppress that -if [ "$bsd" = "yes" ] ; then - echo "CONFIG_BSD=y" >> $config_host_mak -fi - -echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak - -if test "$have_asan_iface_fiber" = "yes" ; then - echo "CONFIG_ASAN_IFACE_FIBER=y" >> $config_host_mak -fi - -if test "$have_tsan" = "yes" && test "$have_tsan_iface_fiber" = "yes" ; then - echo "CONFIG_TSAN=y" >> $config_host_mak -fi - -if test "$plugins" = "yes" ; then - echo "CONFIG_PLUGIN=y" >> $config_host_mak -fi - -if test -n "$gdb_bin"; then - gdb_version=$($gdb_bin --version | head -n 1) - if version_ge ${gdb_version##* } 9.1; then - echo "HAVE_GDB_BIN=$gdb_bin" >> $config_host_mak - fi -fi - -echo "ROMS=$roms" >> $config_host_mak -echo "MAKE=$make" >> $config_host_mak +echo "SUBDIRS=$subdirs" >> $config_host_mak echo "PYTHON=$python" >> $config_host_mak +echo "MKVENV_ENSUREGROUP=$mkvenv ensuregroup $mkvenv_online_flag" >> $config_host_mak echo "GENISOIMAGE=$genisoimage" >> $config_host_mak echo "MESON=$meson" >> $config_host_mak echo "NINJA=$ninja" >> $config_host_mak -echo "CC=$cc" >> $config_host_mak -echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak -echo "QEMU_CXXFLAGS=$QEMU_CXXFLAGS" >> $config_host_mak -echo "QEMU_OBJCFLAGS=$QEMU_OBJCFLAGS" >> $config_host_mak -echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak -echo "GLIB_LIBS=$glib_libs" >> $config_host_mak -echo "GLIB_BINDIR=$glib_bindir" >> $config_host_mak -echo "GLIB_VERSION=$(pkg-config --modversion glib-2.0)" >> $config_host_mak -echo "QEMU_LDFLAGS=$QEMU_LDFLAGS" >> $config_host_mak echo "EXESUF=$EXESUF" >> $config_host_mak - -# use included Linux headers -if test "$linux" = "yes" ; then - mkdir -p linux-headers - case "$cpu" in - i386|x86_64) - linux_arch=x86 - ;; - ppc|ppc64) - linux_arch=powerpc - ;; - s390x) - linux_arch=s390 - ;; - aarch64) - linux_arch=arm64 - ;; - loongarch*) - linux_arch=loongarch - ;; - mips64) - linux_arch=mips - ;; - *) - # For most CPUs the kernel architecture name and QEMU CPU name match. - linux_arch="$cpu" - ;; - esac - # For non-KVM architectures we will not have asm headers - if [ -e "$source_path/linux-headers/asm-$linux_arch" ]; then - symlink "$source_path/linux-headers/asm-$linux_arch" linux-headers/asm - fi -fi - -for target in $target_list; do - target_dir="$target" - target_name=$(echo $target | cut -d '-' -f 1)$EXESUF - mkdir -p $target_dir - case $target in - *-user) symlink "../qemu-$target_name" "$target_dir/qemu-$target_name" ;; - *) symlink "../qemu-system-$target_name" "$target_dir/qemu-system-$target_name" ;; - esac -done - if test "$default_targets" = "yes"; then echo "CONFIG_DEFAULT_TARGETS=y" >> $config_host_mak fi -if test "$ccache_cpp2" = "yes"; then - echo "export CCACHE_CPP2=y" >> $config_host_mak +# contrib/plugins configuration +echo "# Automatically generated by configure - do not modify" > contrib/plugins/$config_host_mak +echo "SRC_PATH=$source_path/contrib/plugins" >> contrib/plugins/$config_host_mak +echo "PKG_CONFIG=${pkg_config}" >> contrib/plugins/$config_host_mak +echo "CC=$cc $CPU_CFLAGS" >> contrib/plugins/$config_host_mak +echo "CFLAGS=${CFLAGS-$default_cflags} $EXTRA_CFLAGS" >> contrib/plugins/$config_host_mak +if test "$host_os" = windows; then + echo "DLLTOOL=$dlltool" >> contrib/plugins/$config_host_mak fi - -if test "$safe_stack" = "yes"; then - echo "CONFIG_SAFESTACK=y" >> $config_host_mak +if test "$host_os" = darwin; then + echo "CONFIG_DARWIN=y" >> contrib/plugins/$config_host_mak +fi +if test "$host_os" = windows; then + echo "CONFIG_WIN32=y" >> contrib/plugins/$config_host_mak fi # tests/tcg configuration -(makefile=tests/tcg/Makefile.prereqs -echo "# Automatically generated by configure - do not modify" > $makefile - -config_host_mak=tests/tcg/config-host.mak -echo "# Automatically generated by configure - do not modify" > $config_host_mak -echo "SRC_PATH=$source_path" >> $config_host_mak -echo "HOST_CC=$host_cc" >> $config_host_mak +mkdir -p tests/tcg +echo "# Automatically generated by configure - do not modify" > tests/tcg/$config_host_mak +echo "SRC_PATH=$source_path" >> tests/tcg/$config_host_mak +if test "$plugins" = "yes" ; then + echo "CONFIG_PLUGIN=y" >> tests/tcg/$config_host_mak +fi tcg_tests_targets= for target in $target_list; do arch=${target%%-*} - probe_target_compiler ${arch} - config_target_mak=tests/tcg/config-$target.mak - - echo "# Automatically generated by configure - do not modify" > $config_target_mak - echo "TARGET_NAME=$arch" >> $config_target_mak case $target in + xtensa*-linux-user) + # the toolchain is not complete with headers, only build system tests + continue + ;; *-softmmu) - test -f $source_path/tests/tcg/$arch/Makefile.softmmu-target || continue + test -f "$source_path/tests/tcg/$arch/Makefile.softmmu-target" || continue qemu="qemu-system-$arch" ;; *-linux-user|*-bsd-user) @@ -2492,108 +1658,31 @@ for target in $target_list; do ;; esac - got_cross_cc=no - unset build_static + if probe_target_compiler $target || test -n "$container_image"; then + test -n "$container_image" && build_static=y + mkdir -p "tests/tcg/$target" + config_target_mak=tests/tcg/$target/config-target.mak + ln -sf "$source_path/tests/tcg/Makefile.target" "tests/tcg/$target/Makefile" + echo "# Automatically generated by configure - do not modify" > "$config_target_mak" + echo "TARGET_NAME=$arch" >> "$config_target_mak" + echo "TARGET=$target" >> "$config_target_mak" + write_target_makefile "build-tcg-tests-$target" >> "$config_target_mak" + echo "BUILD_STATIC=$build_static" >> "$config_target_mak" + echo "QEMU=$PWD/$qemu" >> "$config_target_mak" - if test -n "$target_cc"; then - write_c_skeleton - if ! do_compiler "$target_cc" $target_cflags \ - -o $TMPE $TMPC -static ; then - # For host systems we might get away with building without -static - if do_compiler "$target_cc" $target_cflags \ - -o $TMPE $TMPC ; then - got_cross_cc=yes - fi - else - got_cross_cc=yes - build_static=y + # will GDB work with these binaries? + if test "${gdb_arches#*$arch}" != "$gdb_arches"; then + echo "GDB=$gdb_bin" >> $config_target_mak fi - elif test -n "$target_as" && test -n "$target_ld"; then - # Special handling for assembler only tests - case $target in - tricore-softmmu) got_cross_cc=yes ;; - esac - fi - if test $got_cross_cc = yes; then - # 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. - - echo "BUILD_STATIC=$build_static" >> $config_target_mak - write_target_makefile >> $config_target_mak - case $target in - aarch64-*) - if do_compiler "$target_cc" $target_cflags \ - -march=armv8.1-a+sve -o $TMPE $TMPC; then - echo "CROSS_CC_HAS_SVE=y" >> $config_target_mak - fi - if do_compiler "$target_cc" $target_cflags \ - -march=armv8.1-a+sve2 -o $TMPE $TMPC; then - echo "CROSS_CC_HAS_SVE2=y" >> $config_target_mak - fi - if do_compiler "$target_cc" $target_cflags \ - -march=armv8.3-a -o $TMPE $TMPC; then - echo "CROSS_CC_HAS_ARMV8_3=y" >> $config_target_mak - fi - if do_compiler "$target_cc" $target_cflags \ - -mbranch-protection=standard -o $TMPE $TMPC; then - echo "CROSS_CC_HAS_ARMV8_BTI=y" >> $config_target_mak - fi - if do_compiler "$target_cc" $target_cflags \ - -march=armv8.5-a+memtag -o $TMPE $TMPC; then - echo "CROSS_CC_HAS_ARMV8_MTE=y" >> $config_target_mak - fi - ;; - ppc*) - if do_compiler "$target_cc" $target_cflags \ - -mpower8-vector -o $TMPE $TMPC; then - echo "CROSS_CC_HAS_POWER8_VECTOR=y" >> $config_target_mak - fi - if do_compiler "$target_cc" $target_cflags \ - -mpower10 -o $TMPE $TMPC; then - echo "CROSS_CC_HAS_POWER10=y" >> $config_target_mak - fi - ;; - i386-linux-user) - if do_compiler "$target_cc" $target_cflags \ - -Werror -fno-pie -o $TMPE $TMPC; then - echo "CROSS_CC_HAS_I386_NOPIE=y" >> $config_target_mak - fi - ;; - esac - elif test -n "$container_image"; then - echo "build-tcg-tests-$target: docker-image-$container_image" >> $makefile - echo "BUILD_STATIC=y" >> $config_target_mak - write_container_target_makefile >> $config_target_mak - case $target in - aarch64-*) - echo "CROSS_CC_HAS_SVE=y" >> $config_target_mak - echo "CROSS_CC_HAS_SVE2=y" >> $config_target_mak - echo "CROSS_CC_HAS_ARMV8_3=y" >> $config_target_mak - echo "CROSS_CC_HAS_ARMV8_BTI=y" >> $config_target_mak - echo "CROSS_CC_HAS_ARMV8_MTE=y" >> $config_target_mak - ;; - ppc*) - echo "CROSS_CC_HAS_POWER8_VECTOR=y" >> $config_target_mak - echo "CROSS_CC_HAS_POWER10=y" >> $config_target_mak - ;; - i386-linux-user) - echo "CROSS_CC_HAS_I386_NOPIE=y" >> $config_target_mak - ;; - esac - got_cross_cc=yes - fi - if test $got_cross_cc = yes; then - mkdir -p tests/tcg/$target - echo "QEMU=$PWD/$qemu" >> $config_target_mak - echo "EXTRA_CFLAGS=$target_cflags" >> $config_target_mak - echo "run-tcg-tests-$target: $qemu\$(EXESUF)" >> $makefile + echo "run-tcg-tests-$target: $qemu\$(EXESUF)" >> Makefile.prereqs tcg_tests_targets="$tcg_tests_targets $target" fi done -echo "TCG_TESTS_TARGETS=$tcg_tests_targets" >> $makefile) + +if test "$tcg" = "enabled"; then + echo "TCG_TESTS_TARGETS=$tcg_tests_targets" >> $config_host_mak +fi if test "$skip_meson" = no; then cross="config-meson.cross.new" @@ -2611,20 +1700,44 @@ if test "$skip_meson" = no; then echo "${a}-softmmu = '$c'" >> $cross done - test -z "$cxx" && echo "link_language = 'c'" >> $cross echo "[built-in options]" >> $cross echo "c_args = [$(meson_quote $CFLAGS $EXTRA_CFLAGS)]" >> $cross echo "cpp_args = [$(meson_quote $CXXFLAGS $EXTRA_CXXFLAGS)]" >> $cross test -n "$objcc" && echo "objc_args = [$(meson_quote $OBJCFLAGS $EXTRA_OBJCFLAGS)]" >> $cross echo "c_link_args = [$(meson_quote $CFLAGS $LDFLAGS $EXTRA_CFLAGS $EXTRA_LDFLAGS)]" >> $cross echo "cpp_link_args = [$(meson_quote $CXXFLAGS $LDFLAGS $EXTRA_CXXFLAGS $EXTRA_LDFLAGS)]" >> $cross + + # Only enable by default for git builds and on select OSes + echo "# environment defaults, can still be overridden on " >> $cross + echo "# the command line" >> $cross + if test -e "$source_path/.git" && \ + { test "$host_os" = linux || test "$host_os" = "windows"; }; then + echo 'werror = true' >> $cross + fi + echo "[project options]" >> $cross + if test "$SMBD" != ''; then + echo "smbd = $(meson_quote "$SMBD")" >> $cross + fi + if test "${QEMU_GA_MANUFACTURER}" != ''; then + echo "qemu_ga_manufacturer = $(meson_quote "${QEMU_GA_MANUFACTURER}")" >> $cross + fi + if test "${QEMU_GA_DISTRO}" != ''; then + echo "qemu_ga_distro = $(meson_quote "${QEMU_GA_DISTRO}")" >> $cross + fi + if test "${QEMU_GA_VERSION}" != ''; then + echo "qemu_ga_version = $(meson_quote "${QEMU_GA_VERSION}")" >> $cross + fi + + echo >> $cross echo "[binaries]" >> $cross echo "c = [$(meson_quote $cc $CPU_CFLAGS)]" >> $cross test -n "$cxx" && echo "cpp = [$(meson_quote $cxx $CPU_CFLAGS)]" >> $cross test -n "$objcc" && echo "objc = [$(meson_quote $objcc $CPU_CFLAGS)]" >> $cross echo "ar = [$(meson_quote $ar)]" >> $cross + echo "dlltool = [$(meson_quote $dlltool)]" >> $cross echo "nm = [$(meson_quote $nm)]" >> $cross - echo "pkgconfig = [$(meson_quote $pkg_config_exe)]" >> $cross + echo "pkgconfig = [$(meson_quote $pkg_config)]" >> $cross + echo "pkg-config = [$(meson_quote $pkg_config)]" >> $cross echo "ranlib = [$(meson_quote $ranlib)]" >> $cross if has $sdl2_config; then echo "sdl2-config = [$(meson_quote $sdl2_config)]" >> $cross @@ -2632,10 +1745,10 @@ if test "$skip_meson" = no; then echo "strip = [$(meson_quote $strip)]" >> $cross echo "widl = [$(meson_quote $widl)]" >> $cross echo "windres = [$(meson_quote $windres)]" >> $cross + echo "windmc = [$(meson_quote $windmc)]" >> $cross if test "$cross_compile" = "yes"; then - cross_arg="--cross-file config-meson.cross" echo "[host_machine]" >> $cross - echo "system = '$targetos'" >> $cross + echo "system = '$host_os'" >> $cross case "$cpu" in i386) echo "cpu_family = 'x86'" >> $cross @@ -2650,42 +1763,48 @@ if test "$skip_meson" = no; then else echo "endian = 'little'" >> $cross fi - else - cross_arg="--native-file config-meson.cross" + + native="config-meson.native.new" + echo "# Automatically generated by configure - do not modify" > $native + echo "[binaries]" >> $native + echo "c = [$(meson_quote $host_cc)]" >> $native + mv $native config-meson.native + meson_option_add --native-file + meson_option_add config-meson.native fi mv $cross config-meson.cross + meson_add_machine_file config-meson.cross + if test -f "$source_path/configs/meson/$host_os.txt"; then + meson_add_machine_file $source_path/configs/meson/$host_os.txt + fi rm -rf meson-private meson-info meson-logs - # Built-in options - test "$bindir" != "bin" && meson_option_add "-Dbindir=$bindir" + test "$download" = "disabled" && meson_option_add "--wrap-mode=nodownload" test "$default_feature" = no && meson_option_add -Dauto_features=disabled + test "$static" = yes && meson_option_add -Dprefer_static=true test "$pie" = no && meson_option_add -Db_pie=false - test "$werror" = yes && meson_option_add -Dwerror=true # QEMU options - test "$cfi" != false && meson_option_add "-Dcfi=$cfi" - test "$fdt" != auto && meson_option_add "-Dfdt=$fdt" + test "$cfi" != false && meson_option_add "-Dcfi=$cfi" "-Db_lto=$cfi" + test "$docs" != auto && meson_option_add "-Ddocs=$docs" test -n "${LIB_FUZZING_ENGINE+xxx}" && meson_option_add "-Dfuzzing_engine=$LIB_FUZZING_ENGINE" - test "$qemu_suffix" != qemu && meson_option_add "-Dqemu_suffix=$qemu_suffix" - test "$slirp" != auto && meson_option_add "-Dslirp=$slirp" - test "$smbd" != '' && meson_option_add "-Dsmbd=$smbd" + test "$plugins" = yes && meson_option_add "-Dplugins=true" test "$tcg" != enabled && meson_option_add "-Dtcg=$tcg" run_meson() { - NINJA=$ninja $meson setup --prefix "$prefix" "$@" $cross_arg "$PWD" "$source_path" + NINJA=$ninja $meson setup "$@" "$PWD" "$source_path" } eval run_meson $meson_options if test "$?" -ne 0 ; then error_exit "meson setup failed" fi + echo "$meson" > build.ninja.stamp else if test -f meson-private/cmd_line.txt; then - # Adjust old command line options whose type was changed - # Avoids having to use "setup --wipe" when Meson is upgraded + # Adjust old command line options that were removed + # sed -i is not portable perl -i -ne ' - s/^gettext = true$/gettext = auto/; - s/^gettext = false$/gettext = disabled/; - /^b_staticpic/ && next; + /^sphinx_build/ && next; print;' meson-private/cmd_line.txt fi fi @@ -2721,24 +1840,27 @@ preserve_env CC preserve_env CFLAGS preserve_env CXX preserve_env CXXFLAGS -preserve_env INSTALL +preserve_env DLLTOOL preserve_env LD preserve_env LDFLAGS preserve_env LD_LIBRARY_PATH -preserve_env LIBTOOL -preserve_env MAKE preserve_env NM +preserve_env OBJCFLAGS preserve_env OBJCOPY preserve_env PATH preserve_env PKG_CONFIG preserve_env PKG_CONFIG_LIBDIR preserve_env PKG_CONFIG_PATH preserve_env PYTHON +preserve_env QEMU_GA_MANUFACTURER +preserve_env QEMU_GA_DISTRO +preserve_env QEMU_GA_VERSION preserve_env SDL2_CONFIG preserve_env SMBD preserve_env STRIP preserve_env WIDL preserve_env WINDRES +preserve_env WINDMC printf "exec" >>config.status for i in "$0" "$@"; do diff --git a/contrib/elf2dmp/addrspace.c b/contrib/elf2dmp/addrspace.c index 53ded17061..81295a1153 100644 --- a/contrib/elf2dmp/addrspace.c +++ b/contrib/elf2dmp/addrspace.c @@ -11,9 +11,10 @@ static struct pa_block *pa_space_find_block(struct pa_space *ps, uint64_t pa) { size_t i; + for (i = 0; i < ps->block_nr; i++) { if (ps->block[i].paddr <= pa && - pa <= ps->block[i].paddr + ps->block[i].size) { + pa < ps->block[i].paddr + ps->block[i].size) { return ps->block + i; } } @@ -21,7 +22,7 @@ static struct pa_block *pa_space_find_block(struct pa_space *ps, uint64_t pa) return NULL; } -static uint8_t *pa_space_resolve(struct pa_space *ps, uint64_t pa) +static void *pa_space_resolve(struct pa_space *ps, uint64_t pa) { struct pa_block *block = pa_space_find_block(ps, pa); @@ -32,7 +33,44 @@ static uint8_t *pa_space_resolve(struct pa_space *ps, uint64_t pa) return block->addr + (pa - block->paddr); } -int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf) +static bool pa_space_read64(struct pa_space *ps, uint64_t pa, uint64_t *value) +{ + uint64_t *resolved = pa_space_resolve(ps, pa); + + if (!resolved) { + return false; + } + + *value = *resolved; + + return true; +} + +static void pa_block_align(struct pa_block *b) +{ + uint64_t low_align = ((b->paddr - 1) | ELF2DMP_PAGE_MASK) + 1 - b->paddr; + uint64_t high_align = (b->paddr + b->size) & ELF2DMP_PAGE_MASK; + + if (low_align == 0 && high_align == 0) { + return; + } + + if (low_align + high_align < b->size) { + printf("Block 0x%"PRIx64"+:0x%"PRIx64" will be aligned to " + "0x%"PRIx64"+:0x%"PRIx64"\n", b->paddr, b->size, + b->paddr + low_align, b->size - low_align - high_align); + b->size -= low_align + high_align; + } else { + printf("Block 0x%"PRIx64"+:0x%"PRIx64" is too small to align\n", + b->paddr, b->size); + b->size = 0; + } + + b->addr += low_align; + b->paddr += low_align; +} + +void pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf) { Elf64_Half phdr_nr = elf_getphdrnum(qemu_elf->map); Elf64_Phdr *phdr = elf64_getphdr(qemu_elf->map); @@ -47,29 +85,28 @@ int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf) } } - ps->block = malloc(sizeof(*ps->block) * ps->block_nr); - if (!ps->block) { - return 1; - } + ps->block = g_new(struct pa_block, ps->block_nr); for (i = 0; i < phdr_nr; i++) { - if (phdr[i].p_type == PT_LOAD) { + if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset < qemu_elf->size) { ps->block[block_i] = (struct pa_block) { .addr = (uint8_t *)qemu_elf->map + phdr[i].p_offset, .paddr = phdr[i].p_paddr, - .size = phdr[i].p_filesz, + .size = MIN(phdr[i].p_filesz, + qemu_elf->size - phdr[i].p_offset), }; - block_i++; + pa_block_align(&ps->block[block_i]); + block_i = ps->block[block_i].size ? (block_i + 1) : block_i; } } - return 0; + ps->block_nr = block_i; } void pa_space_destroy(struct pa_space *ps) { ps->block_nr = 0; - free(ps->block); + g_free(ps->block); } void va_space_set_dtb(struct va_space *vs, uint64_t dtb) @@ -83,19 +120,20 @@ void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb) va_space_set_dtb(vs, dtb); } -static uint64_t get_pml4e(struct va_space *vs, uint64_t va) +static bool get_pml4e(struct va_space *vs, uint64_t va, uint64_t *value) { uint64_t pa = (vs->dtb & 0xffffffffff000) | ((va & 0xff8000000000) >> 36); - return *(uint64_t *)pa_space_resolve(vs->ps, pa); + return pa_space_read64(vs->ps, pa, value); } -static uint64_t get_pdpi(struct va_space *vs, uint64_t va, uint64_t pml4e) +static bool get_pdpi(struct va_space *vs, uint64_t va, uint64_t pml4e, + uint64_t *value) { uint64_t pdpte_paddr = (pml4e & 0xffffffffff000) | ((va & 0x7FC0000000) >> 27); - return *(uint64_t *)pa_space_resolve(vs->ps, pdpte_paddr); + return pa_space_read64(vs->ps, pdpte_paddr, value); } static uint64_t pde_index(uint64_t va) @@ -108,11 +146,12 @@ static uint64_t pdba_base(uint64_t pdpe) return pdpe & 0xFFFFFFFFFF000; } -static uint64_t get_pgd(struct va_space *vs, uint64_t va, uint64_t pdpe) +static bool get_pgd(struct va_space *vs, uint64_t va, uint64_t pdpe, + uint64_t *value) { uint64_t pgd_entry = pdba_base(pdpe) + pde_index(va) * 8; - return *(uint64_t *)pa_space_resolve(vs->ps, pgd_entry); + return pa_space_read64(vs->ps, pgd_entry, value); } static uint64_t pte_index(uint64_t va) @@ -125,11 +164,12 @@ static uint64_t ptba_base(uint64_t pde) return pde & 0xFFFFFFFFFF000; } -static uint64_t get_pte(struct va_space *vs, uint64_t va, uint64_t pgd) +static bool get_pte(struct va_space *vs, uint64_t va, uint64_t pgd, + uint64_t *value) { uint64_t pgd_val = ptba_base(pgd) + pte_index(va) * 8; - return *(uint64_t *)pa_space_resolve(vs->ps, pgd_val); + return pa_space_read64(vs->ps, pgd_val, value); } static uint64_t get_paddr(uint64_t va, uint64_t pte) @@ -161,13 +201,11 @@ static uint64_t va_space_va2pa(struct va_space *vs, uint64_t va) { uint64_t pml4e, pdpe, pgd, pte; - pml4e = get_pml4e(vs, va); - if (!is_present(pml4e)) { + if (!get_pml4e(vs, va, &pml4e) || !is_present(pml4e)) { return INVALID_PA; } - pdpe = get_pdpi(vs, va, pml4e); - if (!is_present(pdpe)) { + if (!get_pdpi(vs, va, pml4e, &pdpe) || !is_present(pdpe)) { return INVALID_PA; } @@ -175,8 +213,7 @@ static uint64_t va_space_va2pa(struct va_space *vs, uint64_t va) return get_1GB_paddr(va, pdpe); } - pgd = get_pgd(vs, va, pdpe); - if (!is_present(pgd)) { + if (!get_pgd(vs, va, pdpe, &pgd) || !is_present(pgd)) { return INVALID_PA; } @@ -184,8 +221,7 @@ static uint64_t va_space_va2pa(struct va_space *vs, uint64_t va) return get_2MB_paddr(va, pgd); } - pte = get_pte(vs, va, pgd); - if (!is_present(pte)) { + if (!get_pte(vs, va, pgd, &pte) || !is_present(pte)) { return INVALID_PA; } @@ -203,8 +239,8 @@ void *va_space_resolve(struct va_space *vs, uint64_t va) return pa_space_resolve(vs->ps, pa); } -int va_space_rw(struct va_space *vs, uint64_t addr, - void *buf, size_t size, int is_write) +bool va_space_rw(struct va_space *vs, uint64_t addr, + void *buf, size_t size, int is_write) { while (size) { uint64_t page = addr & ELF2DMP_PFN_MASK; @@ -215,7 +251,7 @@ int va_space_rw(struct va_space *vs, uint64_t addr, ptr = va_space_resolve(vs, addr); if (!ptr) { - return 1; + return false; } if (is_write) { @@ -229,5 +265,5 @@ int va_space_rw(struct va_space *vs, uint64_t addr, addr += s; } - return 0; + return true; } diff --git a/contrib/elf2dmp/addrspace.h b/contrib/elf2dmp/addrspace.h index 00b44c1218..2ad30a9da4 100644 --- a/contrib/elf2dmp/addrspace.h +++ b/contrib/elf2dmp/addrspace.h @@ -12,6 +12,7 @@ #define ELF2DMP_PAGE_BITS 12 #define ELF2DMP_PAGE_SIZE (1ULL << ELF2DMP_PAGE_BITS) +#define ELF2DMP_PAGE_MASK (ELF2DMP_PAGE_SIZE - 1) #define ELF2DMP_PFN_MASK (~(ELF2DMP_PAGE_SIZE - 1)) #define INVALID_PA UINT64_MAX @@ -32,13 +33,13 @@ struct va_space { struct pa_space *ps; }; -int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf); +void pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf); void pa_space_destroy(struct pa_space *ps); void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb); void va_space_set_dtb(struct va_space *vs, uint64_t dtb); void *va_space_resolve(struct va_space *vs, uint64_t va); -int va_space_rw(struct va_space *vs, uint64_t addr, - void *buf, size_t size, int is_write); +bool va_space_rw(struct va_space *vs, uint64_t addr, + void *buf, size_t size, int is_write); #endif /* ADDRSPACE_H */ diff --git a/contrib/elf2dmp/download.c b/contrib/elf2dmp/download.c index bd7650a7a2..21306b3fd4 100644 --- a/contrib/elf2dmp/download.c +++ b/contrib/elf2dmp/download.c @@ -9,19 +9,18 @@ #include #include "download.h" -int download_url(const char *name, const char *url) +bool download_url(const char *name, const char *url) { - int err = 0; + bool success = false; FILE *file; CURL *curl = curl_easy_init(); if (!curl) { - return 1; + return false; } file = fopen(name, "wb"); if (!file) { - err = 1; goto out_curl; } @@ -33,13 +32,12 @@ int download_url(const char *name, const char *url) || curl_easy_perform(curl) != CURLE_OK) { unlink(name); fclose(file); - err = 1; } else { - err = fclose(file); + success = !fclose(file); } out_curl: curl_easy_cleanup(curl); - return err; + return success; } diff --git a/contrib/elf2dmp/download.h b/contrib/elf2dmp/download.h index 5c274925f7..f65adb5d08 100644 --- a/contrib/elf2dmp/download.h +++ b/contrib/elf2dmp/download.h @@ -8,6 +8,6 @@ #ifndef DOWNLOAD_H #define DOWNLOAD_H -int download_url(const char *name, const char *url); +bool download_url(const char *name, const char *url); #endif /* DOWNLOAD_H */ diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c index b9fc6d230c..d046a72ae6 100644 --- a/contrib/elf2dmp/main.c +++ b/contrib/elf2dmp/main.c @@ -6,6 +6,7 @@ */ #include "qemu/osdep.h" +#include "qemu/bitops.h" #include "err.h" #include "addrspace.h" @@ -17,8 +18,10 @@ #define SYM_URL_BASE "https://msdl.microsoft.com/download/symbols/" #define PDB_NAME "ntkrnlmp.pdb" +#define PE_NAME "ntoskrnl.exe" #define INITIAL_MXCSR 0x1f80 +#define MAX_NUMBER_OF_RUNS 42 typedef struct idt_desc { uint16_t offset1; /* offset bits 0..15 */ @@ -45,11 +48,6 @@ static const uint64_t SharedUserData = 0xfffff78000000000; s ? printf(#s" = 0x%016"PRIx64"\n", s) :\ eprintf("Failed to resolve "#s"\n"), s) -static uint64_t rol(uint64_t x, uint64_t y) -{ - return (x << y) | (x >> (64 - y)); -} - /* * Decoding algorithm can be found in Volatility project */ @@ -62,7 +60,7 @@ static void kdbg_decode(uint64_t *dst, uint64_t *src, size_t size, uint64_t block; block = src[i]; - block = rol(block ^ kwn, (uint8_t)kwn); + block = rol64(block ^ kwn, kwn); block = __builtin_bswap64(block ^ kdbe) ^ kwa; dst[i] = block; } @@ -77,9 +75,9 @@ static KDDEBUGGER_DATA64 *get_kdbg(uint64_t KernBase, struct pdb_reader *pdb, bool decode = false; uint64_t kwn, kwa, KdpDataBlockEncoded; - if (va_space_rw(vs, - KdDebuggerDataBlock + offsetof(KDDEBUGGER_DATA64, Header), - &kdbg_hdr, sizeof(kdbg_hdr), 0)) { + if (!va_space_rw(vs, + KdDebuggerDataBlock + offsetof(KDDEBUGGER_DATA64, Header), + &kdbg_hdr, sizeof(kdbg_hdr), 0)) { eprintf("Failed to extract KDBG header\n"); return NULL; } @@ -95,8 +93,8 @@ static KDDEBUGGER_DATA64 *get_kdbg(uint64_t KernBase, struct pdb_reader *pdb, return NULL; } - if (va_space_rw(vs, KiWaitNever, &kwn, sizeof(kwn), 0) || - va_space_rw(vs, KiWaitAlways, &kwa, sizeof(kwa), 0)) { + if (!va_space_rw(vs, KiWaitNever, &kwn, sizeof(kwn), 0) || + !va_space_rw(vs, KiWaitAlways, &kwa, sizeof(kwa), 0)) { return NULL; } @@ -118,13 +116,11 @@ static KDDEBUGGER_DATA64 *get_kdbg(uint64_t KernBase, struct pdb_reader *pdb, } } - kdbg = malloc(kdbg_hdr.Size); - if (!kdbg) { - return NULL; - } + kdbg = g_malloc(kdbg_hdr.Size); - if (va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 0)) { + if (!va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 0)) { eprintf("Failed to extract entire KDBG\n"); + g_free(kdbg); return NULL; } @@ -186,13 +182,13 @@ static void win_context_init_from_qemu_cpu_state(WinContext64 *ctx, * Finds paging-structure hierarchy base, * if previously set doesn't give access to kernel structures */ -static int fix_dtb(struct va_space *vs, QEMU_Elf *qe) +static bool fix_dtb(struct va_space *vs, QEMU_Elf *qe) { /* * Firstly, test previously set DTB. */ if (va_space_resolve(vs, SharedUserData)) { - return 0; + return true; } /* @@ -206,7 +202,7 @@ static int fix_dtb(struct va_space *vs, QEMU_Elf *qe) va_space_set_dtb(vs, s->cr[3]); printf("DTB 0x%016"PRIx64" has been found from CPU #%zu" " as system task CR3\n", vs->dtb, i); - return !(va_space_resolve(vs, SharedUserData)); + return va_space_resolve(vs, SharedUserData); } } @@ -220,21 +216,58 @@ static int fix_dtb(struct va_space *vs, QEMU_Elf *qe) uint64_t *cr3 = va_space_resolve(vs, Prcb + 0x7000); if (!cr3) { - return 1; + return false; } va_space_set_dtb(vs, *cr3); printf("DirectoryTableBase = 0x%016"PRIx64" has been found from CPU #0" " as interrupt handling CR3\n", vs->dtb); - return !(va_space_resolve(vs, SharedUserData)); + return va_space_resolve(vs, SharedUserData); } - return 1; + return true; } -static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps, - struct va_space *vs, uint64_t KdDebuggerDataBlock, - KDDEBUGGER_DATA64 *kdbg, uint64_t KdVersionBlock, int nr_cpus) +static void try_merge_runs(struct pa_space *ps, + WinDumpPhyMemDesc64 *PhysicalMemoryBlock) +{ + unsigned int merge_cnt = 0, run_idx = 0; + + PhysicalMemoryBlock->NumberOfRuns = 0; + + for (size_t idx = 0; idx < ps->block_nr; idx++) { + struct pa_block *blk = ps->block + idx; + struct pa_block *next = blk + 1; + + PhysicalMemoryBlock->NumberOfPages += blk->size / ELF2DMP_PAGE_SIZE; + + if (idx + 1 != ps->block_nr && blk->paddr + blk->size == next->paddr) { + printf("Block #%zu 0x%"PRIx64"+:0x%"PRIx64" and %u previous will be" + " merged\n", idx, blk->paddr, blk->size, merge_cnt); + merge_cnt++; + } else { + struct pa_block *first_merged = blk - merge_cnt; + + printf("Block #%zu 0x%"PRIx64"+:0x%"PRIx64" and %u previous will be" + " merged to 0x%"PRIx64"+:0x%"PRIx64" (run #%u)\n", + idx, blk->paddr, blk->size, merge_cnt, first_merged->paddr, + blk->paddr + blk->size - first_merged->paddr, run_idx); + PhysicalMemoryBlock->Run[run_idx] = (WinDumpPhyMemRun64) { + .BasePage = first_merged->paddr / ELF2DMP_PAGE_SIZE, + .PageCount = (blk->paddr + blk->size - first_merged->paddr) / + ELF2DMP_PAGE_SIZE, + }; + PhysicalMemoryBlock->NumberOfRuns++; + run_idx++; + merge_cnt = 0; + } + } +} + +static bool fill_header(WinDumpHeader64 *hdr, struct pa_space *ps, + struct va_space *vs, uint64_t KdDebuggerDataBlock, + KDDEBUGGER_DATA64 *kdbg, uint64_t KdVersionBlock, + int nr_cpus) { uint32_t *suite_mask = va_space_resolve(vs, SharedUserData + KUSD_OFFSET_SUITE_MASK); @@ -242,18 +275,17 @@ static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps, KUSD_OFFSET_PRODUCT_TYPE); DBGKD_GET_VERSION64 kvb; WinDumpHeader64 h; - size_t i; QEMU_BUILD_BUG_ON(KUSD_OFFSET_SUITE_MASK >= ELF2DMP_PAGE_SIZE); QEMU_BUILD_BUG_ON(KUSD_OFFSET_PRODUCT_TYPE >= ELF2DMP_PAGE_SIZE); if (!suite_mask || !product_type) { - return 1; + return false; } - if (va_space_rw(vs, KdVersionBlock, &kvb, sizeof(kvb), 0)) { + if (!va_space_rw(vs, KdVersionBlock, &kvb, sizeof(kvb), 0)) { eprintf("Failed to extract KdVersionBlock\n"); - return 1; + return false; } h = (WinDumpHeader64) { @@ -280,64 +312,116 @@ static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps, .RequiredDumpSpace = sizeof(h), }; - for (i = 0; i < ps->block_nr; i++) { - h.PhysicalMemoryBlock.NumberOfPages += ps->block[i].size / ELF2DMP_PAGE_SIZE; - h.PhysicalMemoryBlock.Run[i] = (WinDumpPhyMemRun64) { - .BasePage = ps->block[i].paddr / ELF2DMP_PAGE_SIZE, - .PageCount = ps->block[i].size / ELF2DMP_PAGE_SIZE, - }; + if (h.PhysicalMemoryBlock.NumberOfRuns <= MAX_NUMBER_OF_RUNS) { + for (size_t idx = 0; idx < ps->block_nr; idx++) { + h.PhysicalMemoryBlock.NumberOfPages += + ps->block[idx].size / ELF2DMP_PAGE_SIZE; + h.PhysicalMemoryBlock.Run[idx] = (WinDumpPhyMemRun64) { + .BasePage = ps->block[idx].paddr / ELF2DMP_PAGE_SIZE, + .PageCount = ps->block[idx].size / ELF2DMP_PAGE_SIZE, + }; + } + } else { + try_merge_runs(ps, &h.PhysicalMemoryBlock); } - h.RequiredDumpSpace += h.PhysicalMemoryBlock.NumberOfPages << ELF2DMP_PAGE_BITS; + h.RequiredDumpSpace += + h.PhysicalMemoryBlock.NumberOfPages << ELF2DMP_PAGE_BITS; *hdr = h; - return 0; + return true; } -static int fill_context(KDDEBUGGER_DATA64 *kdbg, - struct va_space *vs, QEMU_Elf *qe) +/* + * fill_context() continues even if it fails to fill contexts of some CPUs. + * A dump may still contain valuable information even if it lacks contexts of + * some CPUs due to dump corruption or a failure before starting CPUs. + */ +static void fill_context(KDDEBUGGER_DATA64 *kdbg, + struct va_space *vs, QEMU_Elf *qe) { - int i; + int i; + for (i = 0; i < qe->state_nr; i++) { uint64_t Prcb; uint64_t Context; WinContext64 ctx; QEMUCPUState *s = qe->state[i]; - if (va_space_rw(vs, kdbg->KiProcessorBlock + sizeof(Prcb) * i, - &Prcb, sizeof(Prcb), 0)) { + if (!va_space_rw(vs, kdbg->KiProcessorBlock + sizeof(Prcb) * i, + &Prcb, sizeof(Prcb), 0)) { eprintf("Failed to read CPU #%d PRCB location\n", i); - return 1; + continue; } - if (va_space_rw(vs, Prcb + kdbg->OffsetPrcbContext, - &Context, sizeof(Context), 0)) { + if (!Prcb) { + eprintf("Context for CPU #%d is missing\n", i); + continue; + } + + if (!va_space_rw(vs, Prcb + kdbg->OffsetPrcbContext, + &Context, sizeof(Context), 0)) { eprintf("Failed to read CPU #%d ContextFrame location\n", i); - return 1; + continue; } printf("Filling context for CPU #%d...\n", i); win_context_init_from_qemu_cpu_state(&ctx, s); - if (va_space_rw(vs, Context, &ctx, sizeof(ctx), 1)) { + if (!va_space_rw(vs, Context, &ctx, sizeof(ctx), 1)) { eprintf("Failed to fill CPU #%d context\n", i); - return 1; + continue; } } - - return 0; } -static int write_dump(struct pa_space *ps, - WinDumpHeader64 *hdr, const char *name) +static bool pe_get_data_dir_entry(uint64_t base, void *start_addr, int idx, + void *entry, size_t size, struct va_space *vs) +{ + const char e_magic[2] = "MZ"; + const char Signature[4] = "PE\0\0"; + IMAGE_DOS_HEADER *dos_hdr = start_addr; + IMAGE_NT_HEADERS64 nt_hdrs; + IMAGE_FILE_HEADER *file_hdr = &nt_hdrs.FileHeader; + IMAGE_OPTIONAL_HEADER64 *opt_hdr = &nt_hdrs.OptionalHeader; + IMAGE_DATA_DIRECTORY *data_dir = nt_hdrs.OptionalHeader.DataDirectory; + + QEMU_BUILD_BUG_ON(sizeof(*dos_hdr) >= ELF2DMP_PAGE_SIZE); + + if (memcmp(&dos_hdr->e_magic, e_magic, sizeof(e_magic))) { + return false; + } + + if (!va_space_rw(vs, base + dos_hdr->e_lfanew, + &nt_hdrs, sizeof(nt_hdrs), 0)) { + return false; + } + + if (memcmp(&nt_hdrs.Signature, Signature, sizeof(Signature)) || + file_hdr->Machine != 0x8664 || opt_hdr->Magic != 0x020b) { + return false; + } + + if (!va_space_rw(vs, base + data_dir[idx].VirtualAddress, entry, size, 0)) { + return false; + } + + printf("Data directory entry #%d: RVA = 0x%08"PRIx32"\n", idx, + (uint32_t)data_dir[idx].VirtualAddress); + + return true; +} + +static bool write_dump(struct pa_space *ps, + WinDumpHeader64 *hdr, const char *name) { FILE *dmp_file = fopen(name, "wb"); size_t i; if (!dmp_file) { eprintf("Failed to open output file \'%s\'\n", name); - return 1; + return false; } printf("Writing header to file...\n"); @@ -345,118 +429,86 @@ static int write_dump(struct pa_space *ps, if (fwrite(hdr, sizeof(*hdr), 1, dmp_file) != 1) { eprintf("Failed to write dump header\n"); fclose(dmp_file); - return 1; + return false; } for (i = 0; i < ps->block_nr; i++) { struct pa_block *b = &ps->block[i]; - printf("Writing block #%zu/%zu to file...\n", i, ps->block_nr); + printf("Writing block #%zu/%zu of %"PRIu64" bytes to file...\n", i, + ps->block_nr, b->size); if (fwrite(b->addr, b->size, 1, dmp_file) != 1) { - eprintf("Failed to write dump header\n"); + eprintf("Failed to write block\n"); fclose(dmp_file); - return 1; + return false; } } - return fclose(dmp_file); + return !fclose(dmp_file); } -static int pe_get_pdb_symstore_hash(uint64_t base, void *start_addr, - char *hash, struct va_space *vs) +static bool pe_check_pdb_name(uint64_t base, void *start_addr, + struct va_space *vs, OMFSignatureRSDS *rsds) { - const char e_magic[2] = "MZ"; - const char Signature[4] = "PE\0\0"; const char sign_rsds[4] = "RSDS"; - IMAGE_DOS_HEADER *dos_hdr = start_addr; - IMAGE_NT_HEADERS64 nt_hdrs; - IMAGE_FILE_HEADER *file_hdr = &nt_hdrs.FileHeader; - IMAGE_OPTIONAL_HEADER64 *opt_hdr = &nt_hdrs.OptionalHeader; - IMAGE_DATA_DIRECTORY *data_dir = nt_hdrs.OptionalHeader.DataDirectory; IMAGE_DEBUG_DIRECTORY debug_dir; - OMFSignatureRSDS rsds; - char *pdb_name; - size_t pdb_name_sz; - size_t i; + char pdb_name[sizeof(PDB_NAME)]; - QEMU_BUILD_BUG_ON(sizeof(*dos_hdr) >= ELF2DMP_PAGE_SIZE); - - if (memcmp(&dos_hdr->e_magic, e_magic, sizeof(e_magic))) { - return 1; - } - - if (va_space_rw(vs, base + dos_hdr->e_lfanew, - &nt_hdrs, sizeof(nt_hdrs), 0)) { - return 1; - } - - if (memcmp(&nt_hdrs.Signature, Signature, sizeof(Signature)) || - file_hdr->Machine != 0x8664 || opt_hdr->Magic != 0x020b) { - return 1; - } - - printf("Debug Directory RVA = 0x%08"PRIx32"\n", - (uint32_t)data_dir[IMAGE_FILE_DEBUG_DIRECTORY].VirtualAddress); - - if (va_space_rw(vs, - base + data_dir[IMAGE_FILE_DEBUG_DIRECTORY].VirtualAddress, - &debug_dir, sizeof(debug_dir), 0)) { - return 1; + if (!pe_get_data_dir_entry(base, start_addr, IMAGE_FILE_DEBUG_DIRECTORY, + &debug_dir, sizeof(debug_dir), vs)) { + eprintf("Failed to get Debug Directory\n"); + return false; } if (debug_dir.Type != IMAGE_DEBUG_TYPE_CODEVIEW) { - return 1; + eprintf("Debug Directory type is not CodeView\n"); + return false; } - if (va_space_rw(vs, - base + debug_dir.AddressOfRawData, - &rsds, sizeof(rsds), 0)) { - return 1; + if (!va_space_rw(vs, base + debug_dir.AddressOfRawData, + rsds, sizeof(*rsds), 0)) { + eprintf("Failed to resolve OMFSignatureRSDS\n"); + return false; } - printf("CodeView signature is \'%.4s\'\n", rsds.Signature); - - if (memcmp(&rsds.Signature, sign_rsds, sizeof(sign_rsds))) { - return 1; + if (memcmp(&rsds->Signature, sign_rsds, sizeof(sign_rsds))) { + eprintf("CodeView signature is \'%.4s\', \'%.4s\' expected\n", + rsds->Signature, sign_rsds); + return false; } - pdb_name_sz = debug_dir.SizeOfData - sizeof(rsds); - pdb_name = malloc(pdb_name_sz); - if (!pdb_name) { - return 1; + if (debug_dir.SizeOfData - sizeof(*rsds) != sizeof(PDB_NAME)) { + eprintf("PDB name size doesn't match\n"); + return false; } - if (va_space_rw(vs, base + debug_dir.AddressOfRawData + - offsetof(OMFSignatureRSDS, name), pdb_name, pdb_name_sz, 0)) { - free(pdb_name); - return 1; + if (!va_space_rw(vs, base + debug_dir.AddressOfRawData + + offsetof(OMFSignatureRSDS, name), + pdb_name, sizeof(PDB_NAME), 0)) { + eprintf("Failed to resolve PDB name\n"); + return false; } printf("PDB name is \'%s\', \'%s\' expected\n", pdb_name, PDB_NAME); - if (strcmp(pdb_name, PDB_NAME)) { - eprintf("Unexpected PDB name, it seems the kernel isn't found\n"); - free(pdb_name); - return 1; - } + return !strcmp(pdb_name, PDB_NAME); +} - free(pdb_name); - - sprintf(hash, "%.08x%.04x%.04x%.02x%.02x", rsds.guid.a, rsds.guid.b, - rsds.guid.c, rsds.guid.d[0], rsds.guid.d[1]); +static void pe_get_pdb_symstore_hash(OMFSignatureRSDS *rsds, char *hash) +{ + sprintf(hash, "%.08x%.04x%.04x%.02x%.02x", rsds->guid.a, rsds->guid.b, + rsds->guid.c, rsds->guid.d[0], rsds->guid.d[1]); hash += 20; - for (i = 0; i < 6; i++, hash += 2) { - sprintf(hash, "%.02x", rsds.guid.e[i]); + for (unsigned int i = 0; i < 6; i++, hash += 2) { + sprintf(hash, "%.02x", rsds->guid.e[i]); } - sprintf(hash, "%.01x", rsds.age); - - return 0; + sprintf(hash, "%.01x", rsds->age); } int main(int argc, char *argv[]) { - int err = 0; + int err = 1; QEMU_Elf qemu_elf; struct pa_space ps; struct va_space vs; @@ -472,39 +524,35 @@ int main(int argc, char *argv[]) uint64_t KdDebuggerDataBlock; KDDEBUGGER_DATA64 *kdbg; uint64_t KdVersionBlock; + bool kernel_found = false; + OMFSignatureRSDS rsds; if (argc != 3) { eprintf("usage:\n\t%s elf_file dmp_file\n", argv[0]); return 1; } - if (QEMU_Elf_init(&qemu_elf, argv[1])) { + if (!QEMU_Elf_init(&qemu_elf, argv[1])) { eprintf("Failed to initialize QEMU ELF dump\n"); return 1; } - if (pa_space_create(&ps, &qemu_elf)) { - eprintf("Failed to initialize physical address space\n"); - err = 1; - goto out_elf; - } + pa_space_create(&ps, &qemu_elf); state = qemu_elf.state[0]; printf("CPU #0 CR3 is 0x%016"PRIx64"\n", state->cr[3]); va_space_create(&vs, &ps, state->cr[3]); - if (fix_dtb(&vs, &qemu_elf)) { + if (!fix_dtb(&vs, &qemu_elf)) { eprintf("Failed to find paging base\n"); - err = 1; - goto out_elf; + goto out_ps; } printf("CPU #0 IDT is at 0x%016"PRIx64"\n", state->idt.base); - if (va_space_rw(&vs, state->idt.base, - &first_idt_desc, sizeof(first_idt_desc), 0)) { + if (!va_space_rw(&vs, state->idt.base, + &first_idt_desc, sizeof(first_idt_desc), 0)) { eprintf("Failed to get CPU #0 IDT[0]\n"); - err = 1; goto out_ps; } printf("CPU #0 IDT[0] -> 0x%016"PRIx64"\n", idt_desc_addr(first_idt_desc)); @@ -519,78 +567,69 @@ int main(int argc, char *argv[]) } if (*(uint16_t *)nt_start_addr == 0x5a4d) { /* MZ */ - break; + printf("Checking candidate KernBase = 0x%016"PRIx64"\n", KernBase); + if (pe_check_pdb_name(KernBase, nt_start_addr, &vs, &rsds)) { + kernel_found = true; + break; + } } } - if (!nt_start_addr) { + if (!kernel_found) { eprintf("Failed to find NT kernel image\n"); - err = 1; goto out_ps; } printf("KernBase = 0x%016"PRIx64", signature is \'%.2s\'\n", KernBase, (char *)nt_start_addr); - if (pe_get_pdb_symstore_hash(KernBase, nt_start_addr, pdb_hash, &vs)) { - eprintf("Failed to get PDB symbol store hash\n"); - err = 1; - goto out_ps; - } + pe_get_pdb_symstore_hash(&rsds, pdb_hash); sprintf(pdb_url, "%s%s/%s/%s", SYM_URL_BASE, PDB_NAME, pdb_hash, PDB_NAME); printf("PDB URL is %s\n", pdb_url); - if (download_url(PDB_NAME, pdb_url)) { + if (!download_url(PDB_NAME, pdb_url)) { eprintf("Failed to download PDB file\n"); - err = 1; goto out_ps; } - if (pdb_init_from_file(PDB_NAME, &pdb)) { + if (!pdb_init_from_file(PDB_NAME, &pdb)) { eprintf("Failed to initialize PDB reader\n"); - err = 1; goto out_pdb_file; } if (!SYM_RESOLVE(KernBase, &pdb, KdDebuggerDataBlock) || !SYM_RESOLVE(KernBase, &pdb, KdVersionBlock)) { - err = 1; goto out_pdb; } kdbg = get_kdbg(KernBase, &pdb, &vs, KdDebuggerDataBlock); if (!kdbg) { - err = 1; goto out_pdb; } - if (fill_header(&header, &ps, &vs, KdDebuggerDataBlock, kdbg, - KdVersionBlock, qemu_elf.state_nr)) { - err = 1; + if (!fill_header(&header, &ps, &vs, KdDebuggerDataBlock, kdbg, + KdVersionBlock, qemu_elf.state_nr)) { goto out_kdbg; } - if (fill_context(kdbg, &vs, &qemu_elf)) { - err = 1; - goto out_kdbg; - } + fill_context(kdbg, &vs, &qemu_elf); - if (write_dump(&ps, &header, argv[2])) { + if (!write_dump(&ps, &header, argv[2])) { eprintf("Failed to save dump\n"); - err = 1; goto out_kdbg; } + err = 0; + out_kdbg: - free(kdbg); + g_free(kdbg); out_pdb: pdb_exit(&pdb); out_pdb_file: unlink(PDB_NAME); out_ps: pa_space_destroy(&ps); -out_elf: QEMU_Elf_exit(&qemu_elf); return err; diff --git a/contrib/elf2dmp/pdb.c b/contrib/elf2dmp/pdb.c index adcfa7e154..492aca4434 100644 --- a/contrib/elf2dmp/pdb.c +++ b/contrib/elf2dmp/pdb.c @@ -19,12 +19,17 @@ */ #include "qemu/osdep.h" +#include "qemu/bswap.h" #include "pdb.h" #include "err.h" static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx) { + if (idx >= r->ds.toc->num_files) { + return 0; + } + return r->ds.toc->file_size[idx]; } @@ -90,18 +95,18 @@ uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name) static void pdb_reader_ds_exit(struct pdb_reader *r) { - free(r->ds.toc); + g_free(r->ds.toc); } static void pdb_exit_symbols(struct pdb_reader *r) { - free(r->modimage); - free(r->symbols); + g_free(r->modimage); + g_free(r->symbols); } static void pdb_exit_segments(struct pdb_reader *r) { - free(r->segs); + g_free(r->segs); } static void *pdb_ds_read(const PDB_DS_HEADER *header, @@ -116,10 +121,7 @@ static void *pdb_ds_read(const PDB_DS_HEADER *header, nBlocks = (size + header->block_size - 1) / header->block_size; - buffer = malloc(nBlocks * header->block_size); - if (!buffer) { - return NULL; - } + buffer = g_malloc(nBlocks * header->block_size); for (i = 0; i < nBlocks; i++) { memcpy(buffer + i * header->block_size, (const char *)header + @@ -157,66 +159,58 @@ static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number) return pdb_ds_read(r->ds.header, block_list, file_size[file_number]); } -static int pdb_init_segments(struct pdb_reader *r) +static bool pdb_init_segments(struct pdb_reader *r) { - char *segs; - unsigned stream_idx = r->sidx.segments; + unsigned stream_idx = r->segments; - segs = pdb_ds_read_file(r, stream_idx); - if (!segs) { - return 1; + r->segs = pdb_ds_read_file(r, stream_idx); + if (!r->segs) { + return false; } - r->segs = segs; r->segs_size = pdb_get_file_size(r, stream_idx); + if (!r->segs_size) { + return false; + } - return 0; + return true; } -static int pdb_init_symbols(struct pdb_reader *r) +static bool pdb_init_symbols(struct pdb_reader *r) { - int err = 0; PDB_SYMBOLS *symbols; - PDB_STREAM_INDEXES *sidx = &r->sidx; - - memset(sidx, -1, sizeof(*sidx)); symbols = pdb_ds_read_file(r, 3); if (!symbols) { - return 1; + return false; } r->symbols = symbols; - if (symbols->stream_index_size != sizeof(PDB_STREAM_INDEXES)) { - err = 1; - goto out_symbols; - } - - memcpy(sidx, (const char *)symbols + sizeof(PDB_SYMBOLS) + + r->segments = lduw_le_p((const char *)symbols + sizeof(PDB_SYMBOLS) + symbols->module_size + symbols->offset_size + symbols->hash_size + symbols->srcmodule_size + - symbols->pdbimport_size + symbols->unknown2_size, sizeof(*sidx)); + symbols->pdbimport_size + symbols->unknown2_size + + offsetof(PDB_STREAM_INDEXES, segments)); /* Read global symbol table */ r->modimage = pdb_ds_read_file(r, symbols->gsym_file); if (!r->modimage) { - err = 1; goto out_symbols; } - return 0; + return true; out_symbols: - free(symbols); + g_free(symbols); - return err; + return false; } -static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr) +static bool pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr) { if (hdr->block_size == 0) { - return 1; + return false; } memset(r->file_used, 0, sizeof(r->file_used)); @@ -225,87 +219,81 @@ static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr) hdr->toc_page * hdr->block_size), hdr->toc_size); if (!r->ds.toc) { - return 1; + return false; } - return 0; + return true; } -static int pdb_reader_init(struct pdb_reader *r, void *data) +static bool pdb_reader_init(struct pdb_reader *r, void *data) { - int err = 0; const char pdb7[] = "Microsoft C/C++ MSF 7.00"; if (memcmp(data, pdb7, sizeof(pdb7) - 1)) { - return 1; + return false; } - if (pdb_reader_ds_init(r, data)) { - return 1; + if (!pdb_reader_ds_init(r, data)) { + return false; } r->ds.root = pdb_ds_read_file(r, 1); if (!r->ds.root) { - err = 1; goto out_ds; } - if (pdb_init_symbols(r)) { - err = 1; + if (!pdb_init_symbols(r)) { goto out_root; } - if (pdb_init_segments(r)) { - err = 1; + if (!pdb_init_segments(r)) { goto out_sym; } - return 0; + return true; out_sym: pdb_exit_symbols(r); out_root: - free(r->ds.root); + g_free(r->ds.root); out_ds: pdb_reader_ds_exit(r); - return err; + return false; } static void pdb_reader_exit(struct pdb_reader *r) { pdb_exit_segments(r); pdb_exit_symbols(r); - free(r->ds.root); + g_free(r->ds.root); pdb_reader_ds_exit(r); } -int pdb_init_from_file(const char *name, struct pdb_reader *reader) +bool pdb_init_from_file(const char *name, struct pdb_reader *reader) { GError *gerr = NULL; - int err = 0; void *map; reader->gmf = g_mapped_file_new(name, TRUE, &gerr); if (gerr) { eprintf("Failed to map PDB file \'%s\'\n", name); g_error_free(gerr); - return 1; + return false; } reader->file_size = g_mapped_file_get_length(reader->gmf); map = g_mapped_file_get_contents(reader->gmf); - if (pdb_reader_init(reader, map)) { - err = 1; + if (!pdb_reader_init(reader, map)) { goto out_unmap; } - return 0; + return true; out_unmap: g_mapped_file_unref(reader->gmf); - return err; + return false; } void pdb_exit(struct pdb_reader *reader) diff --git a/contrib/elf2dmp/pdb.h b/contrib/elf2dmp/pdb.h index 4ea8925ee8..feddf1862f 100644 --- a/contrib/elf2dmp/pdb.h +++ b/contrib/elf2dmp/pdb.h @@ -227,13 +227,13 @@ struct pdb_reader { } ds; uint32_t file_used[1024]; PDB_SYMBOLS *symbols; - PDB_STREAM_INDEXES sidx; + uint16_t segments; uint8_t *modimage; char *segs; size_t segs_size; }; -int pdb_init_from_file(const char *name, struct pdb_reader *reader); +bool pdb_init_from_file(const char *name, struct pdb_reader *reader); void pdb_exit(struct pdb_reader *reader); uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name); uint64_t pdb_find_public_v3_symbol(struct pdb_reader *reader, const char *name); diff --git a/contrib/elf2dmp/pe.h b/contrib/elf2dmp/pe.h index c2a4a6ba7c..71126af1ac 100644 --- a/contrib/elf2dmp/pe.h +++ b/contrib/elf2dmp/pe.h @@ -33,75 +33,90 @@ typedef struct IMAGE_DOS_HEADER { } __attribute__ ((packed)) IMAGE_DOS_HEADER; typedef struct IMAGE_FILE_HEADER { - uint16_t Machine; - uint16_t NumberOfSections; - uint32_t TimeDateStamp; - uint32_t PointerToSymbolTable; - uint32_t NumberOfSymbols; - uint16_t SizeOfOptionalHeader; - uint16_t Characteristics; + uint16_t Machine; + uint16_t NumberOfSections; + uint32_t TimeDateStamp; + uint32_t PointerToSymbolTable; + uint32_t NumberOfSymbols; + uint16_t SizeOfOptionalHeader; + uint16_t Characteristics; } __attribute__ ((packed)) IMAGE_FILE_HEADER; typedef struct IMAGE_DATA_DIRECTORY { - uint32_t VirtualAddress; - uint32_t Size; + uint32_t VirtualAddress; + uint32_t Size; } __attribute__ ((packed)) IMAGE_DATA_DIRECTORY; #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 typedef struct IMAGE_OPTIONAL_HEADER64 { - uint16_t Magic; /* 0x20b */ - uint8_t MajorLinkerVersion; - uint8_t MinorLinkerVersion; - uint32_t SizeOfCode; - uint32_t SizeOfInitializedData; - uint32_t SizeOfUninitializedData; - uint32_t AddressOfEntryPoint; - uint32_t BaseOfCode; - uint64_t ImageBase; - uint32_t SectionAlignment; - uint32_t FileAlignment; - uint16_t MajorOperatingSystemVersion; - uint16_t MinorOperatingSystemVersion; - uint16_t MajorImageVersion; - uint16_t MinorImageVersion; - uint16_t MajorSubsystemVersion; - uint16_t MinorSubsystemVersion; - uint32_t Win32VersionValue; - uint32_t SizeOfImage; - uint32_t SizeOfHeaders; - uint32_t CheckSum; - uint16_t Subsystem; - uint16_t DllCharacteristics; - uint64_t SizeOfStackReserve; - uint64_t SizeOfStackCommit; - uint64_t SizeOfHeapReserve; - uint64_t SizeOfHeapCommit; - uint32_t LoaderFlags; - uint32_t NumberOfRvaAndSizes; - IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; + uint16_t Magic; /* 0x20b */ + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint64_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint64_t SizeOfStackReserve; + uint64_t SizeOfStackCommit; + uint64_t SizeOfHeapReserve; + uint64_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } __attribute__ ((packed)) IMAGE_OPTIONAL_HEADER64; typedef struct IMAGE_NT_HEADERS64 { - uint32_t Signature; - IMAGE_FILE_HEADER FileHeader; - IMAGE_OPTIONAL_HEADER64 OptionalHeader; + uint32_t Signature; + IMAGE_FILE_HEADER FileHeader; + IMAGE_OPTIONAL_HEADER64 OptionalHeader; } __attribute__ ((packed)) IMAGE_NT_HEADERS64; +typedef struct IMAGE_EXPORT_DIRECTORY { + uint32_t Characteristics; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t Name; + uint32_t Base; + uint32_t NumberOfFunctions; + uint32_t NumberOfNames; + uint32_t AddressOfFunctions; + uint32_t AddressOfNames; + uint32_t AddressOfNameOrdinals; +} __attribute__ ((packed)) IMAGE_EXPORT_DIRECTORY; + typedef struct IMAGE_DEBUG_DIRECTORY { - uint32_t Characteristics; - uint32_t TimeDateStamp; - uint16_t MajorVersion; - uint16_t MinorVersion; - uint32_t Type; - uint32_t SizeOfData; - uint32_t AddressOfRawData; - uint32_t PointerToRawData; + uint32_t Characteristics; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t Type; + uint32_t SizeOfData; + uint32_t AddressOfRawData; + uint32_t PointerToRawData; } __attribute__ ((packed)) IMAGE_DEBUG_DIRECTORY; #define IMAGE_DEBUG_TYPE_CODEVIEW 2 #endif +#define IMAGE_FILE_EXPORT_DIRECTORY 0 #define IMAGE_FILE_DEBUG_DIRECTORY 6 typedef struct guid_t { diff --git a/contrib/elf2dmp/qemu_elf.c b/contrib/elf2dmp/qemu_elf.c index ebda60dcb8..c9bad6e82c 100644 --- a/contrib/elf2dmp/qemu_elf.c +++ b/contrib/elf2dmp/qemu_elf.c @@ -6,6 +6,7 @@ */ #include "qemu/osdep.h" +#include "qemu/host-utils.h" #include "err.h" #include "qemu_elf.h" @@ -15,36 +16,11 @@ #define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d))) #endif -#ifndef DIV_ROUND_UP -#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) -#endif - -#define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \ - ((DIV_ROUND_UP((hdr_size), 4) + \ - DIV_ROUND_UP((name_size), 4) + \ - DIV_ROUND_UP((desc_size), 4)) * 4) - int is_system(QEMUCPUState *s) { return s->gs.base >> 63; } -static char *nhdr_get_name(Elf64_Nhdr *nhdr) -{ - return (char *)nhdr + ROUND_UP(sizeof(*nhdr), 4); -} - -static void *nhdr_get_desc(Elf64_Nhdr *nhdr) -{ - return nhdr_get_name(nhdr) + ROUND_UP(nhdr->n_namesz, 4); -} - -static Elf64_Nhdr *nhdr_get_next(Elf64_Nhdr *nhdr) -{ - return (void *)((uint8_t *)nhdr + ELF_NOTE_SIZE(sizeof(*nhdr), - nhdr->n_namesz, nhdr->n_descsz)); -} - Elf64_Phdr *elf64_getphdr(void *map) { Elf64_Ehdr *ehdr = map; @@ -60,67 +36,103 @@ Elf64_Half elf_getphdrnum(void *map) return ehdr->e_phnum; } -static int init_states(QEMU_Elf *qe) +static bool advance_note_offset(uint64_t *offsetp, uint64_t size, uint64_t end) +{ + uint64_t offset = *offsetp; + + if (uadd64_overflow(offset, size, &offset) || offset > UINT64_MAX - 3) { + return false; + } + + offset = ROUND_UP(offset, 4); + + if (offset > end) { + return false; + } + + *offsetp = offset; + + return true; +} + +static bool init_states(QEMU_Elf *qe) { Elf64_Phdr *phdr = elf64_getphdr(qe->map); - Elf64_Nhdr *start = (void *)((uint8_t *)qe->map + phdr[0].p_offset); - Elf64_Nhdr *end = (void *)((uint8_t *)start + phdr[0].p_memsz); Elf64_Nhdr *nhdr; - size_t cpu_nr = 0; + GPtrArray *states; + QEMUCPUState *state; + uint32_t state_size; + uint64_t offset; + uint64_t end_offset; + char *name; if (phdr[0].p_type != PT_NOTE) { eprintf("Failed to find PT_NOTE\n"); - return 1; + return false; } qe->has_kernel_gs_base = 1; + offset = phdr[0].p_offset; + states = g_ptr_array_new(); - for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) { - if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) { - QEMUCPUState *state = nhdr_get_desc(nhdr); + if (uadd64_overflow(offset, phdr[0].p_memsz, &end_offset) || + end_offset > qe->size) { + end_offset = qe->size; + } - if (state->size < sizeof(*state)) { - eprintf("CPU #%zu: QEMU CPU state size %u doesn't match\n", - cpu_nr, state->size); + while (offset < end_offset) { + nhdr = (void *)((uint8_t *)qe->map + offset); + + if (!advance_note_offset(&offset, sizeof(*nhdr), end_offset)) { + break; + } + + name = (char *)qe->map + offset; + + if (!advance_note_offset(&offset, nhdr->n_namesz, end_offset)) { + break; + } + + state = (void *)((uint8_t *)qe->map + offset); + + if (!advance_note_offset(&offset, nhdr->n_descsz, end_offset)) { + break; + } + + if (!strcmp(name, QEMU_NOTE_NAME) && + nhdr->n_descsz >= offsetof(QEMUCPUState, kernel_gs_base)) { + state_size = MIN(state->size, nhdr->n_descsz); + + if (state_size < sizeof(*state)) { + eprintf("CPU #%u: QEMU CPU state size %u doesn't match\n", + states->len, state_size); /* * We assume either every QEMU CPU state has KERNEL_GS_BASE or * no one has. */ qe->has_kernel_gs_base = 0; } - cpu_nr++; + g_ptr_array_add(states, state); } } - printf("%zu CPU states has been found\n", cpu_nr); + printf("%u CPU states has been found\n", states->len); - qe->state = malloc(sizeof(*qe->state) * cpu_nr); - if (!qe->state) { - return 1; - } + qe->state_nr = states->len; + qe->state = (void *)g_ptr_array_free(states, FALSE); - cpu_nr = 0; - - for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) { - if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) { - qe->state[cpu_nr] = nhdr_get_desc(nhdr); - cpu_nr++; - } - } - - qe->state_nr = cpu_nr; - - return 0; + return true; } static void exit_states(QEMU_Elf *qe) { - free(qe->state); + g_free(qe->state); } static bool check_ehdr(QEMU_Elf *qe) { Elf64_Ehdr *ehdr = qe->map; + uint64_t phendoff; if (sizeof(Elf64_Ehdr) > qe->size) { eprintf("Invalid input dump file size\n"); @@ -162,46 +174,97 @@ static bool check_ehdr(QEMU_Elf *qe) return false; } + if (umul64_overflow(ehdr->e_phnum, sizeof(Elf64_Phdr), &phendoff) || + uadd64_overflow(phendoff, ehdr->e_phoff, &phendoff) || + phendoff > qe->size) { + eprintf("phdrs do not fit in file\n"); + return false; + } + return true; } -int QEMU_Elf_init(QEMU_Elf *qe, const char *filename) +static bool QEMU_Elf_map(QEMU_Elf *qe, const char *filename) { +#ifdef CONFIG_LINUX + struct stat st; + int fd; + + printf("Using Linux mmap\n"); + + fd = open(filename, O_RDONLY, 0); + if (fd == -1) { + eprintf("Failed to open ELF dump file \'%s\'\n", filename); + return false; + } + + if (fstat(fd, &st)) { + eprintf("Failed to get size of ELF dump file\n"); + close(fd); + return false; + } + qe->size = st.st_size; + + qe->map = mmap(NULL, qe->size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_NORESERVE, fd, 0); + if (qe->map == MAP_FAILED) { + eprintf("Failed to map ELF file\n"); + close(fd); + return false; + } + + close(fd); +#else GError *gerr = NULL; - int err = 0; + + printf("Using GLib mmap\n"); qe->gmf = g_mapped_file_new(filename, TRUE, &gerr); if (gerr) { eprintf("Failed to map ELF dump file \'%s\'\n", filename); g_error_free(gerr); - return 1; + return false; } qe->map = g_mapped_file_get_contents(qe->gmf); qe->size = g_mapped_file_get_length(qe->gmf); +#endif + + return true; +} + +static void QEMU_Elf_unmap(QEMU_Elf *qe) +{ +#ifdef CONFIG_LINUX + munmap(qe->map, qe->size); +#else + g_mapped_file_unref(qe->gmf); +#endif +} + +bool QEMU_Elf_init(QEMU_Elf *qe, const char *filename) +{ + if (!QEMU_Elf_map(qe, filename)) { + return false; + } if (!check_ehdr(qe)) { eprintf("Input file has the wrong format\n"); - err = 1; - goto out_unmap; + QEMU_Elf_unmap(qe); + return false; } - if (init_states(qe)) { + if (!init_states(qe)) { eprintf("Failed to extract QEMU CPU states\n"); - err = 1; - goto out_unmap; + QEMU_Elf_unmap(qe); + return false; } - return 0; - -out_unmap: - g_mapped_file_unref(qe->gmf); - - return err; + return true; } void QEMU_Elf_exit(QEMU_Elf *qe) { exit_states(qe); - g_mapped_file_unref(qe->gmf); + QEMU_Elf_unmap(qe); } diff --git a/contrib/elf2dmp/qemu_elf.h b/contrib/elf2dmp/qemu_elf.h index b2f0d9cbc9..adc50238b4 100644 --- a/contrib/elf2dmp/qemu_elf.h +++ b/contrib/elf2dmp/qemu_elf.h @@ -32,7 +32,9 @@ typedef struct QEMUCPUState { int is_system(QEMUCPUState *s); typedef struct QEMU_Elf { +#ifndef CONFIG_LINUX GMappedFile *gmf; +#endif size_t size; void *map; QEMUCPUState **state; @@ -40,7 +42,7 @@ typedef struct QEMU_Elf { int has_kernel_gs_base; } QEMU_Elf; -int QEMU_Elf_init(QEMU_Elf *qe, const char *filename); +bool QEMU_Elf_init(QEMU_Elf *qe, const char *filename); void QEMU_Elf_exit(QEMU_Elf *qe); Elf64_Phdr *elf64_getphdr(void *map); diff --git a/contrib/gitdm/domain-map b/contrib/gitdm/domain-map index 2800d9f986..bf1dce03fd 100644 --- a/contrib/gitdm/domain-map +++ b/contrib/gitdm/domain-map @@ -4,21 +4,32 @@ # This maps email domains to nice easy to read company names # +linux.alibaba.com Alibaba +amazon.com Amazon +amazon.co.uk Amazon +amazon.de Amazon amd.com AMD +aspeedtech.com ASPEED Technology Inc. baidu.com Baidu bytedance.com ByteDance +cestc.cn Cestc cmss.chinamobile.com China Mobile citrix.com Citrix crudebyte.com Crudebyte +chinatelecom.cn China Telecom +daynix.com Daynix eldorado.org.br Instituto de Pesquisas Eldorado +fb.com Facebook fujitsu.com Fujitsu google.com Google greensocs.com GreenSocs +hisilicon.com Huawei huawei.com Huawei ibm.com IBM igalia.com Igalia intel.com Intel linaro.org Linaro +loongson.cn Loongson Technology lwn.net LWN microsoft.com Microsoft mvista.com MontaVista @@ -29,15 +40,19 @@ oracle.com Oracle proxmox.com Proxmox quicinc.com Qualcomm Innovation Center redhat.com Red Hat +rev.ng rev.ng Labs +rivosinc.com Rivos Inc rt-rk.com RT-RK samsung.com Samsung siemens.com Siemens sifive.com SiFive suse.com SUSE suse.de SUSE +syrmia.com SYRMIA +ventanamicro.com Ventana Micro Systems virtuozzo.com Virtuozzo +vrull.eu VRULL wdc.com Western Digital windriver.com Wind River -xilinx.com Xilinx yadro.com YADRO yandex-team.ru Yandex diff --git a/contrib/gitdm/filetypes.txt b/contrib/gitdm/filetypes.txt index d2d6f6db8d..b1d01c0992 100644 --- a/contrib/gitdm/filetypes.txt +++ b/contrib/gitdm/filetypes.txt @@ -12,8 +12,7 @@ # GNU Library General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# along with this program. If not, see . # # Authors : Gregorio Robles # Authors : Germán Póo-Caamaño diff --git a/contrib/gitdm/group-map-academics b/contrib/gitdm/group-map-academics index 44745ca85b..082458e1bd 100644 --- a/contrib/gitdm/group-map-academics +++ b/contrib/gitdm/group-map-academics @@ -19,3 +19,9 @@ edu.cn # Boston University bu.edu + +# Institute of Software Chinese Academy of Sciences +iscas.ac.cn + +# Université Grenoble Alpes +univ-grenoble-alpes.fr diff --git a/contrib/gitdm/group-map-alibaba b/contrib/gitdm/group-map-alibaba new file mode 100644 index 0000000000..4c34446d34 --- /dev/null +++ b/contrib/gitdm/group-map-alibaba @@ -0,0 +1,7 @@ +# +# Alibaba contributors including its subsidiaries +# + +# c-sky.com, now part of T-Head, wholly-owned entity of Alibaba Group +ren_guo@c-sky.com +zhiwei_liu@c-sky.com diff --git a/contrib/gitdm/group-map-amd b/contrib/gitdm/group-map-amd new file mode 100644 index 0000000000..bda4239a8a --- /dev/null +++ b/contrib/gitdm/group-map-amd @@ -0,0 +1,8 @@ +# AMD acquired Xilinx and contributors have been slowly updating emails + +edgar.iglesias@xilinx.com +fnu.vikram@xilinx.com +francisco.iglesias@xilinx.com +sai.pavan.boddu@xilinx.com +stefano.stabellini@xilinx.com +tong.ho@xilinx.com diff --git a/contrib/gitdm/group-map-facebook b/contrib/gitdm/group-map-facebook new file mode 100644 index 0000000000..38589f8fb9 --- /dev/null +++ b/contrib/gitdm/group-map-facebook @@ -0,0 +1,5 @@ +# +# Some Facebook contributors also occasionally use personal email addresses. +# + +peter@pjd.dev diff --git a/contrib/gitdm/group-map-ibm b/contrib/gitdm/group-map-ibm index da62fa3f44..24d8dc1b86 100644 --- a/contrib/gitdm/group-map-ibm +++ b/contrib/gitdm/group-map-ibm @@ -12,3 +12,4 @@ jcfaracco@gmail.com joel@jms.id.au sjitindarsingh@gmail.com tommusta@gmail.com +idan.horowitz@gmail.com diff --git a/contrib/gitdm/group-map-individuals b/contrib/gitdm/group-map-individuals index f816aa8770..d7116f5444 100644 --- a/contrib/gitdm/group-map-individuals +++ b/contrib/gitdm/group-map-individuals @@ -34,3 +34,11 @@ bmeng.cn@gmail.com liq3ea@gmail.com chetan4windows@gmail.com akihiko.odaki@gmail.com +paul@nowt.org +git@xen0n.name +simon@simonsafar.com +research_trasio@irq.a4lg.com +shentey@gmail.com +bmeng@tinylab.org +strahinja.p.jankovic@gmail.com +Jason@zx2c4.com diff --git a/contrib/ivshmem-client/meson.build b/contrib/ivshmem-client/meson.build index ce8dcca84d..3c8b09af4b 100644 --- a/contrib/ivshmem-client/meson.build +++ b/contrib/ivshmem-client/meson.build @@ -1,4 +1,4 @@ executable('ivshmem-client', files('ivshmem-client.c', 'main.c'), genh, dependencies: glib, - build_by_default: targetos == 'linux', + build_by_default: host_os == 'linux', install: false) diff --git a/contrib/ivshmem-server/main.c b/contrib/ivshmem-server/main.c index 224dbeb547..5901f17707 100644 --- a/contrib/ivshmem-server/main.c +++ b/contrib/ivshmem-server/main.c @@ -69,7 +69,7 @@ static void ivshmem_server_parse_args(IvshmemServerArgs *args, int argc, char *argv[]) { int c; - unsigned long long v; + uint64_t v; Error *err = NULL; while ((c = getopt(argc, argv, "hvFp:S:m:M:l:n:")) != -1) { @@ -112,7 +112,7 @@ ivshmem_server_parse_args(IvshmemServerArgs *args, int argc, char *argv[]) break; case 'n': /* number of vectors */ - if (parse_uint_full(optarg, &v, 0) < 0) { + if (parse_uint_full(optarg, 0, &v) < 0) { fprintf(stderr, "cannot parse n_vectors\n"); ivshmem_server_help(argv[0]); exit(1); diff --git a/contrib/ivshmem-server/meson.build b/contrib/ivshmem-server/meson.build index c6c3c82e89..1c8fea6594 100644 --- a/contrib/ivshmem-server/meson.build +++ b/contrib/ivshmem-server/meson.build @@ -1,4 +1,4 @@ executable('ivshmem-server', files('ivshmem-server.c', 'main.c'), genh, dependencies: [qemuutil, rt], - build_by_default: targetos == 'linux', + build_by_default: host_os == 'linux', install: false) diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile index df3499f4f2..0b64d2c1e3 100644 --- a/contrib/plugins/Makefile +++ b/contrib/plugins/Makefile @@ -3,44 +3,66 @@ # This Makefile example is fairly independent from the main makefile # so users can take and adapt it for their build. We only really # include config-host.mak so we don't have to repeat probing for -# cflags that the main configure has already done for us. +# programs that the main configure has already done for us. # -BUILD_DIR := $(CURDIR)/../.. +include config-host.mak -include $(BUILD_DIR)/config-host.mak +TOP_SRC_PATH = $(SRC_PATH)/../.. -VPATH += $(SRC_PATH)/contrib/plugins +VPATH += $(SRC_PATH) NAMES := NAMES += execlog NAMES += hotblocks NAMES += hotpages NAMES += howvec + +# The lockstep example communicates using unix sockets, +# and can't be easily made to work on windows. +ifneq ($(CONFIG_WIN32),y) NAMES += lockstep +endif + NAMES += hwprofile NAMES += cache NAMES += drcov -SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES))) +ifeq ($(CONFIG_WIN32),y) +SO_SUFFIX := .dll +LDLIBS += $(shell $(PKG_CONFIG) --libs glib-2.0) +else +SO_SUFFIX := .so +endif + +SONAMES := $(addsuffix $(SO_SUFFIX),$(addprefix lib,$(NAMES))) # The main QEMU uses Glib extensively so it's perfectly fine to use it # in plugins (which many example do). -CFLAGS = $(GLIB_CFLAGS) -CFLAGS += -fPIC -Wall $(filter -W%, $(QEMU_CFLAGS)) -CFLAGS += $(if $(findstring no-psabi,$(QEMU_CFLAGS)),-Wpsabi) -CFLAGS += -I$(SRC_PATH)/include/qemu +PLUGIN_CFLAGS := $(shell $(PKG_CONFIG) --cflags glib-2.0) +PLUGIN_CFLAGS += -fPIC -Wall +PLUGIN_CFLAGS += -I$(TOP_SRC_PATH)/include/qemu all: $(SONAMES) %.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< + $(CC) $(CFLAGS) $(PLUGIN_CFLAGS) -c -o $@ $< + +ifeq ($(CONFIG_WIN32),y) +lib%$(SO_SUFFIX): %.o win32_linker.o ../../plugins/libqemu_plugin_api.a + $(CC) -shared -o $@ $^ $(LDLIBS) +else ifeq ($(CONFIG_DARWIN),y) +lib%$(SO_SUFFIX): %.o + $(CC) -bundle -Wl,-undefined,dynamic_lookup -o $@ $^ $(LDLIBS) +else +lib%$(SO_SUFFIX): %.o + $(CC) -shared -o $@ $^ $(LDLIBS) +endif -lib%.so: %.o - $(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDLIBS) clean: - rm -f *.o *.so *.d + rm -f *.o *$(SO_SUFFIX) *.d rm -Rf .libs .PHONY: all clean +.SECONDARY: diff --git a/contrib/plugins/cache.c b/contrib/plugins/cache.c index b9226e7c40..c5c8ac75a9 100644 --- a/contrib/plugins/cache.c +++ b/contrib/plugins/cache.c @@ -38,7 +38,7 @@ enum EvictionPolicy policy; * put in any of the blocks inside the set. The number of block per set is * called the associativity (assoc). * - * Each block contains the the stored tag and a valid bit. Since this is not + * Each block contains the stored tag and a valid bit. Since this is not * a functional simulator, the data itself is not stored. We only identify * whether a block is in the cache or not by searching for its tag. * @@ -350,7 +350,7 @@ static int in_cache(Cache *cache, uint64_t addr) * @cache: The cache under simulation * @addr: The address of the requested memory location * - * Returns true if the requsted data is hit in the cache and false when missed. + * Returns true if the requested data is hit in the cache and false when missed. * The cache is updated on miss for the next access. */ static bool access_cache(Cache *cache, uint64_t addr) @@ -405,7 +405,7 @@ static void vcpu_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info, g_mutex_lock(&l1_dcache_locks[cache_idx]); hit_in_l1 = access_cache(l1_dcaches[cache_idx], effective_addr); if (!hit_in_l1) { - insn = (InsnData *) userdata; + insn = userdata; __atomic_fetch_add(&insn->l1_dmisses, 1, __ATOMIC_SEQ_CST); l1_dcaches[cache_idx]->misses++; } @@ -419,7 +419,7 @@ static void vcpu_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info, g_mutex_lock(&l2_ucache_locks[cache_idx]); if (!access_cache(l2_ucaches[cache_idx], effective_addr)) { - insn = (InsnData *) userdata; + insn = userdata; __atomic_fetch_add(&insn->l2_misses, 1, __ATOMIC_SEQ_CST); l2_ucaches[cache_idx]->misses++; } @@ -440,7 +440,7 @@ static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata) g_mutex_lock(&l1_icache_locks[cache_idx]); hit_in_l1 = access_cache(l1_icaches[cache_idx], insn_addr); if (!hit_in_l1) { - insn = (InsnData *) userdata; + insn = userdata; __atomic_fetch_add(&insn->l1_imisses, 1, __ATOMIC_SEQ_CST); l1_icaches[cache_idx]->misses++; } @@ -454,7 +454,7 @@ static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata) g_mutex_lock(&l2_ucache_locks[cache_idx]); if (!access_cache(l2_ucaches[cache_idx], insn_addr)) { - insn = (InsnData *) userdata; + insn = userdata; __atomic_fetch_add(&insn->l2_misses, 1, __ATOMIC_SEQ_CST); l2_ucaches[cache_idx]->misses++; } @@ -535,18 +535,16 @@ static void caches_free(Cache **caches) } } -static void append_stats_line(GString *line, uint64_t l1_daccess, - uint64_t l1_dmisses, uint64_t l1_iaccess, - uint64_t l1_imisses, uint64_t l2_access, - uint64_t l2_misses) +static void append_stats_line(GString *line, + uint64_t l1_daccess, uint64_t l1_dmisses, + uint64_t l1_iaccess, uint64_t l1_imisses, + uint64_t l2_access, uint64_t l2_misses) { - double l1_dmiss_rate, l1_imiss_rate, l2_miss_rate; + double l1_dmiss_rate = ((double) l1_dmisses) / (l1_daccess) * 100.0; + double l1_imiss_rate = ((double) l1_imisses) / (l1_iaccess) * 100.0; - l1_dmiss_rate = ((double) l1_dmisses) / (l1_daccess) * 100.0; - l1_imiss_rate = ((double) l1_imisses) / (l1_iaccess) * 100.0; - - g_string_append_printf(line, "%-14lu %-12lu %9.4lf%% %-14lu %-12lu" - " %9.4lf%%", + g_string_append_printf(line, "%-14" PRIu64 " %-12" PRIu64 " %9.4lf%%" + " %-14" PRIu64 " %-12" PRIu64 " %9.4lf%%", l1_daccess, l1_dmisses, l1_daccess ? l1_dmiss_rate : 0.0, @@ -554,9 +552,10 @@ static void append_stats_line(GString *line, uint64_t l1_daccess, l1_imisses, l1_iaccess ? l1_imiss_rate : 0.0); - if (use_l2) { - l2_miss_rate = ((double) l2_misses) / (l2_access) * 100.0; - g_string_append_printf(line, " %-12lu %-11lu %10.4lf%%", + if (l2_access && l2_misses) { + double l2_miss_rate = ((double) l2_misses) / (l2_access) * 100.0; + g_string_append_printf(line, + " %-12" PRIu64 " %-11" PRIu64 " %10.4lf%%", l2_access, l2_misses, l2_access ? l2_miss_rate : 0.0); @@ -662,8 +661,8 @@ static void log_top_insns(void) if (insn->symbol) { g_string_append_printf(rep, " (%s)", insn->symbol); } - g_string_append_printf(rep, ", %ld, %s\n", insn->l1_dmisses, - insn->disas_str); + g_string_append_printf(rep, ", %" PRId64 ", %s\n", + insn->l1_dmisses, insn->disas_str); } miss_insns = g_list_sort(miss_insns, icmp); @@ -675,8 +674,8 @@ static void log_top_insns(void) if (insn->symbol) { g_string_append_printf(rep, " (%s)", insn->symbol); } - g_string_append_printf(rep, ", %ld, %s\n", insn->l1_imisses, - insn->disas_str); + g_string_append_printf(rep, ", %" PRId64 ", %s\n", + insn->l1_imisses, insn->disas_str); } if (!use_l2) { @@ -692,8 +691,8 @@ static void log_top_insns(void) if (insn->symbol) { g_string_append_printf(rep, " (%s)", insn->symbol); } - g_string_append_printf(rep, ", %ld, %s\n", insn->l2_misses, - insn->disas_str); + g_string_append_printf(rep, ", %" PRId64 ", %s\n", + insn->l2_misses, insn->disas_str); } finish: @@ -768,11 +767,11 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, policy = LRU; - cores = sys ? qemu_plugin_n_vcpus() : 1; + cores = sys ? info->system.smp_vcpus : 1; for (i = 0; i < argc; i++) { char *opt = argv[i]; - g_autofree char **tokens = g_strsplit(opt, "=", 2); + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); if (g_strcmp0(tokens[0], "iblksize") == 0) { l1_iblksize = STRTOLL(tokens[1]); diff --git a/contrib/plugins/drcov.c b/contrib/plugins/drcov.c index b4a855adaf..5edc94dcaf 100644 --- a/contrib/plugins/drcov.c +++ b/contrib/plugins/drcov.c @@ -48,7 +48,7 @@ static void printf_header(unsigned long count) uint64_t start_code = qemu_plugin_start_code(); uint64_t end_code = qemu_plugin_end_code(); uint64_t entry = qemu_plugin_entry_code(); - fprintf(fp, "0, 0x%lx, 0x%lx, 0x%lx, %s\n", + fprintf(fp, "0, 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", %s\n", start_code, end_code, entry, path); fprintf(fp, "BB Table: %ld bbs\n", count); } @@ -148,7 +148,7 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { for (int i = 0; i < argc; i++) { - g_autofree char **tokens = g_strsplit(argv[i], "=", 2); + g_auto(GStrv) tokens = g_strsplit(argv[i], "=", 2); if (g_strcmp0(tokens[0], "filename") == 0) { file_name = g_strdup(tokens[1]); } diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c index a5275dcc15..fab18113d4 100644 --- a/contrib/plugins/execlog.c +++ b/contrib/plugins/execlog.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2021, Alexandre Iooss * - * Log instruction execution with memory access. + * Log instruction execution with memory access and register changes * * License: GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -15,10 +15,41 @@ #include +typedef struct { + struct qemu_plugin_register *handle; + GByteArray *last; + GByteArray *new; + const char *name; +} Register; + +typedef struct CPU { + /* Store last executed instruction on each vCPU as a GString */ + GString *last_exec; + /* Ptr array of Register */ + GPtrArray *registers; +} CPU; + QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; -/* Store last executed instruction on each vCPU as a GString */ -GArray *last_exec; +static GArray *cpus; +static GRWLock expand_array_lock; + +static GPtrArray *imatches; +static GArray *amatches; +static GPtrArray *rmatches; +static bool disas_assist; +static GMutex add_reg_name_lock; +static GPtrArray *all_reg_names; + +static CPU *get_cpu(int vcpu_index) +{ + CPU *c; + g_rw_lock_reader_lock(&expand_array_lock); + c = &g_array_index(cpus, CPU, vcpu_index); + g_rw_lock_reader_unlock(&expand_array_lock); + + return c; +} /** * Add memory read or write information to current instruction log @@ -26,11 +57,10 @@ GArray *last_exec; static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t info, uint64_t vaddr, void *udata) { - GString *s; + CPU *c = get_cpu(cpu_index); + GString *s = c->last_exec; /* Find vCPU in array */ - g_assert(cpu_index < last_exec->len); - s = g_array_index(last_exec, GString *, cpu_index); /* Indicate type of memory access */ if (qemu_plugin_mem_is_store(info)) { @@ -51,29 +81,91 @@ static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t info, } /** - * Log instruction execution + * Log instruction execution, outputting the last one. + * + * vcpu_insn_exec() is a copy and paste of vcpu_insn_exec_with_regs() + * without the checking of register values when we've attempted to + * optimise with disas_assist. */ -static void vcpu_insn_exec(unsigned int cpu_index, void *udata) +static void insn_check_regs(CPU *cpu) { - GString *s; + for (int n = 0; n < cpu->registers->len; n++) { + Register *reg = cpu->registers->pdata[n]; + int sz; - /* Find or create vCPU in array */ - while (cpu_index >= last_exec->len) { - s = g_string_new(NULL); - g_array_append_val(last_exec, s); + g_byte_array_set_size(reg->new, 0); + sz = qemu_plugin_read_register(reg->handle, reg->new); + g_assert(sz == reg->last->len); + + if (memcmp(reg->last->data, reg->new->data, sz)) { + GByteArray *temp = reg->last; + g_string_append_printf(cpu->last_exec, ", %s -> 0x", reg->name); + /* TODO: handle BE properly */ + for (int i = sz; i >= 0; i--) { + g_string_append_printf(cpu->last_exec, "%02x", + reg->new->data[i]); + } + reg->last = reg->new; + reg->new = temp; + } } - s = g_array_index(last_exec, GString *, cpu_index); +} + +/* Log last instruction while checking registers */ +static void vcpu_insn_exec_with_regs(unsigned int cpu_index, void *udata) +{ + CPU *cpu = get_cpu(cpu_index); /* Print previous instruction in cache */ - if (s->len) { - qemu_plugin_outs(s->str); + if (cpu->last_exec->len) { + if (cpu->registers) { + insn_check_regs(cpu); + } + + qemu_plugin_outs(cpu->last_exec->str); qemu_plugin_outs("\n"); } /* Store new instruction in cache */ /* vcpu_mem will add memory access information to last_exec */ - g_string_printf(s, "%u, ", cpu_index); - g_string_append(s, (char *)udata); + g_string_printf(cpu->last_exec, "%u, ", cpu_index); + g_string_append(cpu->last_exec, (char *)udata); +} + +/* Log last instruction while checking registers, ignore next */ +static void vcpu_insn_exec_only_regs(unsigned int cpu_index, void *udata) +{ + CPU *cpu = get_cpu(cpu_index); + + /* Print previous instruction in cache */ + if (cpu->last_exec->len) { + if (cpu->registers) { + insn_check_regs(cpu); + } + + qemu_plugin_outs(cpu->last_exec->str); + qemu_plugin_outs("\n"); + } + + /* reset */ + cpu->last_exec->len = 0; +} + +/* Log last instruction without checking regs, setup next */ +static void vcpu_insn_exec(unsigned int cpu_index, void *udata) +{ + CPU *cpu = get_cpu(cpu_index); + + /* Print previous instruction in cache */ + if (cpu->last_exec->len) { + qemu_plugin_outs(cpu->last_exec->str); + qemu_plugin_outs("\n"); + } + + /* Store new instruction in cache */ + /* vcpu_mem will add memory access information to last_exec */ + g_string_printf(cpu->last_exec, "%u, ", cpu_index); + g_string_append(cpu->last_exec, (char *)udata); } /** @@ -85,12 +177,15 @@ static void vcpu_insn_exec(unsigned int cpu_index, void *udata) static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { struct qemu_plugin_insn *insn; - uint64_t insn_vaddr; - uint32_t insn_opcode; - char *insn_disas; + bool skip = (imatches || amatches); + bool check_regs_this = rmatches; + bool check_regs_next = false; size_t n = qemu_plugin_tb_n_insns(tb); for (size_t i = 0; i < n; i++) { + char *insn_disas; + uint64_t insn_vaddr; + /* * `insn` is shared between translations in QEMU, copy needed data here. * `output` is never freed as it might be used multiple times during @@ -99,37 +194,248 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) * a limitation for CISC architectures. */ insn = qemu_plugin_tb_get_insn(tb, i); - insn_vaddr = qemu_plugin_insn_vaddr(insn); - insn_opcode = *((uint32_t *)qemu_plugin_insn_data(insn)); insn_disas = qemu_plugin_insn_disas(insn); - char *output = g_strdup_printf("0x%"PRIx64", 0x%"PRIx32", \"%s\"", - insn_vaddr, insn_opcode, insn_disas); + insn_vaddr = qemu_plugin_insn_vaddr(insn); - /* Register callback on memory read or write */ - qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem, - QEMU_PLUGIN_CB_NO_REGS, - QEMU_PLUGIN_MEM_RW, NULL); + /* + * If we are filtering we better check out if we have any + * hits. The skip "latches" so we can track memory accesses + * after the instruction we care about. Also enable register + * checking on the next instruction. + */ + if (skip && imatches) { + int j; + for (j = 0; j < imatches->len && skip; j++) { + char *m = g_ptr_array_index(imatches, j); + if (g_str_has_prefix(insn_disas, m)) { + skip = false; + check_regs_next = rmatches; + } + } + } - /* Register callback on instruction */ - qemu_plugin_register_vcpu_insn_exec_cb(insn, vcpu_insn_exec, - QEMU_PLUGIN_CB_NO_REGS, output); + if (skip && amatches) { + int j; + for (j = 0; j < amatches->len && skip; j++) { + uint64_t v = g_array_index(amatches, uint64_t, j); + if (v == insn_vaddr) { + skip = false; + } + } + } + + /* + * Check the disassembly to see if a register we care about + * will be affected by this instruction. This relies on the + * dissembler doing something sensible for the registers we + * care about. + */ + if (disas_assist && rmatches) { + check_regs_next = false; + gchar *args = g_strstr_len(insn_disas, -1, " "); + for (int n = 0; n < all_reg_names->len; n++) { + gchar *reg = g_ptr_array_index(all_reg_names, n); + if (g_strrstr(args, reg)) { + check_regs_next = true; + skip = false; + } + } + } + + /* + * We now have 3 choices: + * + * - Log insn + * - Log insn while checking registers + * - Don't log this insn but check if last insn changed registers + */ + + if (skip) { + if (check_regs_this) { + qemu_plugin_register_vcpu_insn_exec_cb(insn, + vcpu_insn_exec_only_regs, + QEMU_PLUGIN_CB_R_REGS, + NULL); + } + } else { + uint32_t insn_opcode; + insn_opcode = *((uint32_t *)qemu_plugin_insn_data(insn)); + char *output = g_strdup_printf("0x%"PRIx64", 0x%"PRIx32", \"%s\"", + insn_vaddr, insn_opcode, insn_disas); + + /* Register callback on memory read or write */ + qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem, + QEMU_PLUGIN_CB_NO_REGS, + QEMU_PLUGIN_MEM_RW, NULL); + + /* Register callback on instruction */ + if (check_regs_this) { + qemu_plugin_register_vcpu_insn_exec_cb( + insn, vcpu_insn_exec_with_regs, + QEMU_PLUGIN_CB_R_REGS, + output); + } else { + qemu_plugin_register_vcpu_insn_exec_cb( + insn, vcpu_insn_exec, + QEMU_PLUGIN_CB_NO_REGS, + output); + } + + /* reset skip */ + skip = (imatches || amatches); + } + + /* set regs for next */ + if (disas_assist && rmatches) { + check_regs_this = check_regs_next; + } + + g_free(insn_disas); } } +static Register *init_vcpu_register(qemu_plugin_reg_descriptor *desc) +{ + Register *reg = g_new0(Register, 1); + g_autofree gchar *lower = g_utf8_strdown(desc->name, -1); + int r; + + reg->handle = desc->handle; + reg->name = g_intern_string(lower); + reg->last = g_byte_array_new(); + reg->new = g_byte_array_new(); + + /* read the initial value */ + r = qemu_plugin_read_register(reg->handle, reg->last); + g_assert(r > 0); + return reg; +} + +/* + * g_pattern_match_string has been deprecated in Glib since 2.70 and + * will complain about it if you try to use it. Fortunately the + * signature of both functions is the same making it easy to work + * around. + */ +static inline +gboolean g_pattern_spec_match_string_qemu(GPatternSpec *pspec, + const gchar *string) +{ +#if GLIB_CHECK_VERSION(2, 70, 0) + return g_pattern_spec_match_string(pspec, string); +#else + return g_pattern_match_string(pspec, string); +#endif +}; +#define g_pattern_spec_match_string(p, s) g_pattern_spec_match_string_qemu(p, s) + +static GPtrArray *registers_init(int vcpu_index) +{ + g_autoptr(GPtrArray) registers = g_ptr_array_new(); + g_autoptr(GArray) reg_list = qemu_plugin_get_registers(); + + if (rmatches && reg_list->len) { + /* + * Go through each register in the complete list and + * see if we want to track it. + */ + for (int r = 0; r < reg_list->len; r++) { + qemu_plugin_reg_descriptor *rd = &g_array_index( + reg_list, qemu_plugin_reg_descriptor, r); + for (int p = 0; p < rmatches->len; p++) { + g_autoptr(GPatternSpec) pat = g_pattern_spec_new(rmatches->pdata[p]); + g_autofree gchar *rd_lower = g_utf8_strdown(rd->name, -1); + if (g_pattern_spec_match_string(pat, rd->name) || + g_pattern_spec_match_string(pat, rd_lower)) { + Register *reg = init_vcpu_register(rd); + g_ptr_array_add(registers, reg); + + /* we need a list of regnames at TB translation time */ + if (disas_assist) { + g_mutex_lock(&add_reg_name_lock); + if (!g_ptr_array_find(all_reg_names, reg->name, NULL)) { + g_ptr_array_add(all_reg_names, (gpointer)reg->name); + } + g_mutex_unlock(&add_reg_name_lock); + } + } + } + } + } + + return registers->len ? g_steal_pointer(®isters) : NULL; +} + +/* + * Initialise a new vcpu/thread with: + * - last_exec tracking data + * - list of tracked registers + * - initial value of registers + * + * As we could have multiple threads trying to do this we need to + * serialise the expansion under a lock. + */ +static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) +{ + CPU *c; + + g_rw_lock_writer_lock(&expand_array_lock); + if (vcpu_index >= cpus->len) { + g_array_set_size(cpus, vcpu_index + 1); + } + g_rw_lock_writer_unlock(&expand_array_lock); + + c = get_cpu(vcpu_index); + c->last_exec = g_string_new(NULL); + c->registers = registers_init(vcpu_index); +} + /** * On plugin exit, print last instruction in cache */ static void plugin_exit(qemu_plugin_id_t id, void *p) { guint i; - GString *s; - for (i = 0; i < last_exec->len; i++) { - s = g_array_index(last_exec, GString *, i); - if (s->str) { - qemu_plugin_outs(s->str); + g_rw_lock_reader_lock(&expand_array_lock); + for (i = 0; i < cpus->len; i++) { + CPU *c = get_cpu(i); + if (c->last_exec && c->last_exec->str) { + qemu_plugin_outs(c->last_exec->str); qemu_plugin_outs("\n"); } } + g_rw_lock_reader_unlock(&expand_array_lock); +} + +/* Add a match to the array of matches */ +static void parse_insn_match(char *match) +{ + if (!imatches) { + imatches = g_ptr_array_new(); + } + g_ptr_array_add(imatches, g_strdup(match)); +} + +static void parse_vaddr_match(char *match) +{ + uint64_t v = g_ascii_strtoull(match, NULL, 16); + + if (!amatches) { + amatches = g_array_new(false, true, sizeof(uint64_t)); + } + g_array_append_val(amatches, v); +} + +/* + * We have to wait until vCPUs are started before we can check the + * patterns find anything. + */ +static void add_regpat(char *regpat) +{ + if (!rmatches) { + rmatches = g_ptr_array_new(); + } + g_ptr_array_add(rmatches, g_strdup(regpat)); } /** @@ -143,9 +449,32 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, * Initialize dynamic array to cache vCPU instruction. In user mode * we don't know the size before emulation. */ - last_exec = g_array_new(FALSE, FALSE, sizeof(GString *)); + cpus = g_array_sized_new(true, true, sizeof(CPU), + info->system_emulation ? info->system.max_vcpus : 1); - /* Register translation block and exit callbacks */ + for (int i = 0; i < argc; i++) { + char *opt = argv[i]; + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "ifilter") == 0) { + parse_insn_match(tokens[1]); + } else if (g_strcmp0(tokens[0], "afilter") == 0) { + parse_vaddr_match(tokens[1]); + } else if (g_strcmp0(tokens[0], "reg") == 0) { + add_regpat(tokens[1]); + } else if (g_strcmp0(tokens[0], "rdisas") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &disas_assist)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + all_reg_names = g_ptr_array_new(); + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + /* Register init, translation block and exit callbacks */ + qemu_plugin_register_vcpu_init_cb(id, vcpu_init); qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); diff --git a/contrib/plugins/hotblocks.c b/contrib/plugins/hotblocks.c index 062200a7a4..02bc5078bd 100644 --- a/contrib/plugins/hotblocks.c +++ b/contrib/plugins/hotblocks.c @@ -34,8 +34,8 @@ static guint64 limit = 20; */ typedef struct { uint64_t start_addr; - uint64_t exec_count; - int trans_count; + struct qemu_plugin_scoreboard *exec_count; + int trans_count; unsigned long insns; } ExecCount; @@ -43,7 +43,17 @@ static gint cmp_exec_count(gconstpointer a, gconstpointer b) { ExecCount *ea = (ExecCount *) a; ExecCount *eb = (ExecCount *) b; - return ea->exec_count > eb->exec_count ? -1 : 1; + uint64_t count_a = + qemu_plugin_u64_sum(qemu_plugin_scoreboard_u64(ea->exec_count)); + uint64_t count_b = + qemu_plugin_u64_sum(qemu_plugin_scoreboard_u64(eb->exec_count)); + return count_a > count_b ? -1 : 1; +} + +static void exec_count_free(gpointer key, gpointer value, gpointer user_data) +{ + ExecCount *cnt = value; + qemu_plugin_scoreboard_free(cnt->exec_count); } static void plugin_exit(qemu_plugin_id_t id, void *p) @@ -52,7 +62,6 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) GList *counts, *it; int i; - g_mutex_lock(&lock); g_string_append_printf(report, "%d entries in the hash table\n", g_hash_table_size(hotblocks)); counts = g_hash_table_get_values(hotblocks); @@ -63,16 +72,21 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) for (i = 0; i < limit && it->next; i++, it = it->next) { ExecCount *rec = (ExecCount *) it->data; - g_string_append_printf(report, "0x%016"PRIx64", %d, %ld, %"PRId64"\n", - rec->start_addr, rec->trans_count, - rec->insns, rec->exec_count); + g_string_append_printf( + report, "0x%016"PRIx64", %d, %ld, %"PRId64"\n", + rec->start_addr, rec->trans_count, + rec->insns, + qemu_plugin_u64_sum( + qemu_plugin_scoreboard_u64(rec->exec_count))); } g_list_free(it); - g_mutex_unlock(&lock); } qemu_plugin_outs(report->str); + + g_hash_table_foreach(hotblocks, exec_count_free, NULL); + g_hash_table_destroy(hotblocks); } static void plugin_init(void) @@ -82,15 +96,9 @@ static void plugin_init(void) static void vcpu_tb_exec(unsigned int cpu_index, void *udata) { - ExecCount *cnt; - uint64_t hash = (uint64_t) udata; - - g_mutex_lock(&lock); - cnt = (ExecCount *) g_hash_table_lookup(hotblocks, (gconstpointer) hash); - /* should always succeed */ - g_assert(cnt); - cnt->exec_count++; - g_mutex_unlock(&lock); + ExecCount *cnt = (ExecCount *)udata; + qemu_plugin_u64_add(qemu_plugin_scoreboard_u64(cnt->exec_count), + cpu_index, 1); } /* @@ -114,18 +122,20 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) cnt->start_addr = pc; cnt->trans_count = 1; cnt->insns = insns; + cnt->exec_count = qemu_plugin_scoreboard_new(sizeof(uint64_t)); g_hash_table_insert(hotblocks, (gpointer) hash, (gpointer) cnt); } g_mutex_unlock(&lock); if (do_inline) { - qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64, - &cnt->exec_count, 1); + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, + qemu_plugin_scoreboard_u64(cnt->exec_count), 1); } else { qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, - (void *)hash); + (void *)cnt); } } @@ -135,7 +145,7 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, { for (int i = 0; i < argc; i++) { char *opt = argv[i]; - g_autofree char **tokens = g_strsplit(opt, "=", 2); + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); if (g_strcmp0(tokens[0], "inline") == 0) { if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) { fprintf(stderr, "boolean argument parsing failed: %s\n", opt); diff --git a/contrib/plugins/hotpages.c b/contrib/plugins/hotpages.c index 0d12910af6..8316ae50c7 100644 --- a/contrib/plugins/hotpages.c +++ b/contrib/plugins/hotpages.c @@ -169,7 +169,7 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, for (i = 0; i < argc; i++) { char *opt = argv[i]; - g_autofree char **tokens = g_strsplit(opt, "=", -1); + g_auto(GStrv) tokens = g_strsplit(opt, "=", -1); if (g_strcmp0(tokens[0], "sortby") == 0) { if (g_strcmp0(tokens[1], "reads") == 0) { diff --git a/contrib/plugins/howvec.c b/contrib/plugins/howvec.c index 4a5ec3d936..94bbc53820 100644 --- a/contrib/plugins/howvec.c +++ b/contrib/plugins/howvec.c @@ -43,13 +43,13 @@ typedef struct { uint32_t mask; uint32_t pattern; CountType what; - uint64_t count; + qemu_plugin_u64 count; } InsnClassExecCount; typedef struct { char *insn; uint32_t opcode; - uint64_t count; + qemu_plugin_u64 count; InsnClassExecCount *class; } InsnExecCount; @@ -159,12 +159,15 @@ static gint cmp_exec_count(gconstpointer a, gconstpointer b) { InsnExecCount *ea = (InsnExecCount *) a; InsnExecCount *eb = (InsnExecCount *) b; - return ea->count > eb->count ? -1 : 1; + uint64_t count_a = qemu_plugin_u64_sum(ea->count); + uint64_t count_b = qemu_plugin_u64_sum(eb->count); + return count_a > count_b ? -1 : 1; } static void free_record(gpointer data) { InsnExecCount *rec = (InsnExecCount *) data; + qemu_plugin_scoreboard_free(rec->count.score); g_free(rec->insn); g_free(rec); } @@ -173,6 +176,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) { g_autoptr(GString) report = g_string_new("Instruction Classes:\n"); int i; + uint64_t total_count; GList *counts; InsnClassExecCount *class = NULL; @@ -180,10 +184,12 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) class = &class_table[i]; switch (class->what) { case COUNT_CLASS: - if (class->count || verbose) { - g_string_append_printf(report, "Class: %-24s\t(%ld hits)\n", + total_count = qemu_plugin_u64_sum(class->count); + if (total_count || verbose) { + g_string_append_printf(report, + "Class: %-24s\t(%" PRId64 " hits)\n", class->class, - class->count); + total_count); } break; case COUNT_INDIVIDUAL: @@ -208,9 +214,10 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) i++, counts = g_list_next(counts)) { InsnExecCount *rec = (InsnExecCount *) counts->data; g_string_append_printf(report, - "Instr: %-24s\t(%ld hits)\t(op=0x%08x/%s)\n", + "Instr: %-24s\t(%" PRId64 " hits)" + "\t(op=0x%08x/%s)\n", rec->insn, - rec->count, + qemu_plugin_u64_sum(rec->count), rec->opcode, rec->class ? rec->class->class : "un-categorised"); @@ -219,6 +226,12 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) } g_hash_table_destroy(insns); + for (i = 0; i < ARRAY_SIZE(class_tables); i++) { + for (int j = 0; j < class_tables[i].table_sz; ++j) { + qemu_plugin_scoreboard_free(class_tables[i].table[j].count.score); + } + } + qemu_plugin_outs(report->str); } @@ -230,11 +243,12 @@ static void plugin_init(void) static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata) { - uint64_t *count = (uint64_t *) udata; - (*count)++; + struct qemu_plugin_scoreboard *score = udata; + qemu_plugin_u64_add(qemu_plugin_scoreboard_u64(score), cpu_index, 1); } -static uint64_t *find_counter(struct qemu_plugin_insn *insn) +static struct qemu_plugin_scoreboard *find_counter( + struct qemu_plugin_insn *insn) { int i; uint64_t *cnt = NULL; @@ -263,7 +277,7 @@ static uint64_t *find_counter(struct qemu_plugin_insn *insn) case COUNT_NONE: return NULL; case COUNT_CLASS: - return &class->count; + return class->count.score; case COUNT_INDIVIDUAL: { InsnExecCount *icount; @@ -277,13 +291,16 @@ static uint64_t *find_counter(struct qemu_plugin_insn *insn) icount->opcode = opcode; icount->insn = qemu_plugin_insn_disas(insn); icount->class = class; + struct qemu_plugin_scoreboard *score = + qemu_plugin_scoreboard_new(sizeof(uint64_t)); + icount->count = qemu_plugin_scoreboard_u64(score); g_hash_table_insert(insns, GUINT_TO_POINTER(opcode), (gpointer) icount); } g_mutex_unlock(&lock); - return &icount->count; + return icount->count.score; } default: g_assert_not_reached(); @@ -298,14 +315,14 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) size_t i; for (i = 0; i < n; i++) { - uint64_t *cnt; struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); - cnt = find_counter(insn); + struct qemu_plugin_scoreboard *cnt = find_counter(insn); if (cnt) { if (do_inline) { - qemu_plugin_register_vcpu_insn_exec_inline( - insn, QEMU_PLUGIN_INLINE_ADD_U64, cnt, 1); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_ADD_U64, + qemu_plugin_scoreboard_u64(cnt), 1); } else { qemu_plugin_register_vcpu_insn_exec_cb( insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, cnt); @@ -320,6 +337,14 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, { int i; + for (i = 0; i < ARRAY_SIZE(class_tables); i++) { + for (int j = 0; j < class_tables[i].table_sz; ++j) { + struct qemu_plugin_scoreboard *score = + qemu_plugin_scoreboard_new(sizeof(uint64_t)); + class_tables[i].table[j].count = qemu_plugin_scoreboard_u64(score); + } + } + /* Select a class table appropriate to the guest architecture */ for (i = 0; i < ARRAY_SIZE(class_tables); i++) { ClassSelector *entry = &class_tables[i]; @@ -333,7 +358,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, for (i = 0; i < argc; i++) { char *p = argv[i]; - g_autofree char **tokens = g_strsplit(p, "=", -1); + g_auto(GStrv) tokens = g_strsplit(p, "=", -1); if (g_strcmp0(tokens[0], "inline") == 0) { if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) { fprintf(stderr, "boolean argument parsing failed: %s\n", p); diff --git a/contrib/plugins/hwprofile.c b/contrib/plugins/hwprofile.c index 691d4edb0c..739ac0c66b 100644 --- a/contrib/plugins/hwprofile.c +++ b/contrib/plugins/hwprofile.c @@ -263,7 +263,7 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, for (i = 0; i < argc; i++) { char *opt = argv[i]; - g_autofree char **tokens = g_strsplit(opt, "=", 2); + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); if (g_strcmp0(tokens[0], "track") == 0) { if (g_strcmp0(tokens[1], "read") == 0) { diff --git a/contrib/plugins/lockstep.c b/contrib/plugins/lockstep.c index a41ffe83fa..237543b43a 100644 --- a/contrib/plugins/lockstep.c +++ b/contrib/plugins/lockstep.c @@ -108,7 +108,7 @@ static void report_divergance(ExecState *us, ExecState *them) /* * If we have diverged before did we get back on track or are we - * totally loosing it? + * totally losing it? */ if (divergence_log) { DivergeState *last = (DivergeState *) divergence_log->data; @@ -130,11 +130,13 @@ static void report_divergance(ExecState *us, ExecState *them) } } divergence_log = g_slist_prepend(divergence_log, - g_memdup(&divrec, sizeof(divrec))); + g_memdup2(&divrec, sizeof(divrec))); /* Output short log entry of going out of sync... */ if (verbose || divrec.distance == 1 || diverged) { - g_string_printf(out, "@ 0x%016lx vs 0x%016lx (%d/%d since last)\n", + g_string_printf(out, + "@ 0x%016" PRIx64 " vs 0x%016" PRIx64 + " (%d/%d since last)\n", us->pc, them->pc, g_slist_length(divergence_log), divrec.distance); qemu_plugin_outs(out->str); @@ -144,7 +146,9 @@ static void report_divergance(ExecState *us, ExecState *them) int i; GSList *entry; - g_string_printf(out, "Δ insn_count @ 0x%016lx (%ld) vs 0x%016lx (%ld)\n", + g_string_printf(out, + "Δ insn_count @ 0x%016" PRIx64 + " (%ld) vs 0x%016" PRIx64 " (%ld)\n", us->pc, us->insn_count, them->pc, them->insn_count); for (entry = log, i = 0; @@ -152,7 +156,8 @@ static void report_divergance(ExecState *us, ExecState *them) entry = g_slist_next(entry), i++) { ExecInfo *prev = (ExecInfo *) entry->data; g_string_append_printf(out, - " previously @ 0x%016lx/%ld (%ld insns)\n", + " previously @ 0x%016" PRIx64 "/%" PRId64 + " (%ld insns)\n", prev->block->pc, prev->block->insns, prev->insn_count); } @@ -240,6 +245,7 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) static bool setup_socket(const char *path) { struct sockaddr_un sockaddr; + const gsize pathlen = sizeof(sockaddr.sun_path) - 1; int fd; fd = socket(AF_UNIX, SOCK_STREAM, 0); @@ -249,7 +255,12 @@ static bool setup_socket(const char *path) } sockaddr.sun_family = AF_UNIX; - g_strlcpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1); + if (g_strlcpy(sockaddr.sun_path, path, pathlen) >= pathlen) { + perror("bad path"); + close(fd); + return false; + } + if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) { perror("bind socket"); close(fd); @@ -282,6 +293,7 @@ static bool connect_socket(const char *path) { int fd; struct sockaddr_un sockaddr; + const gsize pathlen = sizeof(sockaddr.sun_path) - 1; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { @@ -290,7 +302,11 @@ static bool connect_socket(const char *path) } sockaddr.sun_family = AF_UNIX; - g_strlcpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1); + if (g_strlcpy(sockaddr.sun_path, path, pathlen) >= pathlen) { + perror("bad path"); + close(fd); + return false; + } if (connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) { perror("failed to connect"); @@ -323,7 +339,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, for (i = 0; i < argc; i++) { char *p = argv[i]; - g_autofree char **tokens = g_strsplit(p, "=", 2); + g_auto(GStrv) tokens = g_strsplit(p, "=", 2); if (g_strcmp0(tokens[0], "verbose") == 0) { if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &verbose)) { diff --git a/contrib/plugins/win32_linker.c b/contrib/plugins/win32_linker.c new file mode 100644 index 0000000000..7534b2b8bf --- /dev/null +++ b/contrib/plugins/win32_linker.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023, Greg Manning + * + * This hook, __pfnDliFailureHook2, is documented in the microsoft documentation here: + * https://learn.microsoft.com/en-us/cpp/build/reference/error-handling-and-notification + * It gets called when a delay-loaded DLL encounters various errors. + * We handle the specific case of a DLL looking for a "qemu.exe", + * and give it the running executable (regardless of what it is named). + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include +#include + +FARPROC WINAPI dll_failure_hook(unsigned dliNotify, PDelayLoadInfo pdli); + + +PfnDliHook __pfnDliFailureHook2 = dll_failure_hook; + +FARPROC WINAPI dll_failure_hook(unsigned dliNotify, PDelayLoadInfo pdli) { + if (dliNotify == dliFailLoadLib) { + /* If the failing request was for qemu.exe, ... */ + if (strcmp(pdli->szDll, "qemu.exe") == 0) { + /* Then pass back a pointer to the top level module. */ + HMODULE top = GetModuleHandle(NULL); + return (FARPROC) top; + } + } + /* Otherwise we can't do anything special. */ + return 0; +} + diff --git a/contrib/vhost-user-blk/meson.build b/contrib/vhost-user-blk/meson.build index 601ea15ef5..ac1eece37a 100644 --- a/contrib/vhost-user-blk/meson.build +++ b/contrib/vhost-user-blk/meson.build @@ -1,5 +1,4 @@ -# FIXME: broken on 32-bit architectures executable('vhost-user-blk', files('vhost-user-blk.c'), dependencies: [qemuutil, vhost_user], - build_by_default: false, + build_by_default: host_os == 'linux', install: false) diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c index cd4a5d7335..89e5f11a64 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -106,10 +106,7 @@ static void vub_req_complete(VubReq *req) req->size + 1); vu_queue_notify(vu_dev, req->vq); - if (req->elem) { - free(req->elem); - } - + g_free(req->elem); g_free(req); } @@ -146,7 +143,7 @@ vub_readv(VubReq *req, struct iovec *iov, uint32_t iovcnt) req->size = vub_iov_size(iov, iovcnt); rc = preadv(vdev_blk->blk_fd, iov, iovcnt, req->sector_num * 512); if (rc < 0) { - fprintf(stderr, "%s, Sector %"PRIu64", Size %lu failed with %s\n", + fprintf(stderr, "%s, Sector %"PRIu64", Size %zu failed with %s\n", vdev_blk->blk_name, req->sector_num, req->size, strerror(errno)); return -1; @@ -169,7 +166,7 @@ vub_writev(VubReq *req, struct iovec *iov, uint32_t iovcnt) req->size = vub_iov_size(iov, iovcnt); rc = pwritev(vdev_blk->blk_fd, iov, iovcnt, req->sector_num * 512); if (rc < 0) { - fprintf(stderr, "%s, Sector %"PRIu64", Size %lu failed with %s\n", + fprintf(stderr, "%s, Sector %"PRIu64", Size %zu failed with %s\n", vdev_blk->blk_name, req->sector_num, req->size, strerror(errno)); return -1; @@ -188,7 +185,7 @@ vub_discard_write_zeroes(VubReq *req, struct iovec *iov, uint32_t iovcnt, size = vub_iov_size(iov, iovcnt); if (size != sizeof(*desc)) { - fprintf(stderr, "Invalid size %ld, expect %ld\n", size, sizeof(*desc)); + fprintf(stderr, "Invalid size %zd, expect %zd\n", size, sizeof(*desc)); return -1; } buf = g_new0(char, size); @@ -196,7 +193,7 @@ vub_discard_write_zeroes(VubReq *req, struct iovec *iov, uint32_t iovcnt, #if defined(__linux__) && defined(BLKDISCARD) && defined(BLKZEROOUT) VubDev *vdev_blk = req->vdev_blk; - desc = (struct virtio_blk_discard_write_zeroes *)buf; + desc = buf; uint64_t range[2] = { le64toh(desc->sector) << 9, le32toh(desc->num_sectors) << 9 }; if (type == VIRTIO_BLK_T_DISCARD) { @@ -243,7 +240,7 @@ static int vub_virtio_process_req(VubDev *vdev_blk, /* refer to hw/block/virtio_blk.c */ if (elem->out_num < 1 || elem->in_num < 1) { fprintf(stderr, "virtio-blk request missing headers\n"); - free(elem); + g_free(elem); return -1; } @@ -325,7 +322,7 @@ static int vub_virtio_process_req(VubDev *vdev_blk, return 0; err: - free(elem); + g_free(elem); g_free(req); return -1; } @@ -424,7 +421,7 @@ vub_set_config(VuDev *vu_dev, const uint8_t *data, int fd; /* don't support live migration */ - if (flags != VHOST_SET_CONFIG_TYPE_MASTER) { + if (flags != VHOST_SET_CONFIG_TYPE_FRONTEND) { return -1; } @@ -535,9 +532,9 @@ vub_get_blocksize(int fd) static void vub_initialize_config(int fd, struct virtio_blk_config *config) { - off64_t capacity; + off_t capacity; - capacity = lseek64(fd, 0, SEEK_END); + capacity = lseek(fd, 0, SEEK_END); config->capacity = capacity >> 9; config->blk_size = vub_get_blocksize(fd); config->size_max = 65536; diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index bfb8d93cf8..bb41758e34 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -303,6 +303,53 @@ vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) cmd->state = VG_CMD_STATE_PENDING; } +static gboolean +get_edid_cb(gint fd, GIOCondition condition, gpointer user_data) +{ + struct virtio_gpu_resp_edid resp_edid; + VuGpu *vg = user_data; + struct virtio_gpu_ctrl_command *cmd = QTAILQ_LAST(&vg->fenceq); + + g_debug("get edid cb"); + assert(cmd->cmd_hdr.type == VIRTIO_GPU_CMD_GET_EDID); + if (!vg_recv_msg(vg, VHOST_USER_GPU_GET_EDID, + sizeof(resp_edid), &resp_edid)) { + return G_SOURCE_CONTINUE; + } + + QTAILQ_REMOVE(&vg->fenceq, cmd, next); + vg_ctrl_response(vg, cmd, &resp_edid.hdr, sizeof(resp_edid)); + + vg->wait_in = 0; + vg_handle_ctrl(&vg->dev.parent, 0); + + return G_SOURCE_REMOVE; +} + +void +vg_get_edid(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_cmd_get_edid get_edid; + + VUGPU_FILL_CMD(get_edid); + virtio_gpu_bswap_32(&get_edid, sizeof(get_edid)); + + VhostUserGpuMsg msg = { + .request = VHOST_USER_GPU_GET_EDID, + .size = sizeof(VhostUserGpuEdidRequest), + .payload.edid_req = { + .scanout_id = get_edid.scanout, + }, + }; + + assert(vg->wait_in == 0); + + vg_send_msg(vg, &msg, -1); + vg->wait_in = g_unix_fd_add(vg->sock_fd, G_IO_IN | G_IO_HUP, + get_edid_cb, vg); + cmd->state = VG_CMD_STATE_PENDING; +} + static void vg_resource_create_2d(VuGpu *g, struct virtio_gpu_ctrl_command *cmd) @@ -787,7 +834,7 @@ vg_resource_flush(VuGpu *g, .width = width, .height = height, }; - pixman_image_t *i = + pixman_image_t *img = pixman_image_create_bits(pixman_image_get_format(res->image), msg->payload.update.width, msg->payload.update.height, @@ -795,11 +842,11 @@ vg_resource_flush(VuGpu *g, payload.update.data), width * bpp); pixman_image_composite(PIXMAN_OP_SRC, - res->image, NULL, i, + res->image, NULL, img, extents->x1, extents->y1, 0, 0, 0, 0, width, height); - pixman_image_unref(i); + pixman_image_unref(img); vg_send_msg(g, msg, -1); g_free(msg); } @@ -837,8 +884,9 @@ vg_process_cmd(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: vg_resource_detach_backing(vg, cmd); break; - /* case VIRTIO_GPU_CMD_GET_EDID: */ - /* break */ + case VIRTIO_GPU_CMD_GET_EDID: + vg_get_edid(vg, cmd); + break; default: g_warning("TODO handle ctrl %x\n", cmd->cmd_hdr.type); cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; @@ -1022,26 +1070,39 @@ vg_queue_set_started(VuDev *dev, int qidx, bool started) static gboolean protocol_features_cb(gint fd, GIOCondition condition, gpointer user_data) { + const uint64_t protocol_edid = (1 << VHOST_USER_GPU_PROTOCOL_F_EDID); + const uint64_t protocol_dmabuf2 = (1 << VHOST_USER_GPU_PROTOCOL_F_DMABUF2); VuGpu *g = user_data; - uint64_t u64; + uint64_t protocol_features; VhostUserGpuMsg msg = { .request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES }; - if (!vg_recv_msg(g, msg.request, sizeof(u64), &u64)) { + if (!vg_recv_msg(g, msg.request, + sizeof(protocol_features), &protocol_features)) { return G_SOURCE_CONTINUE; } + protocol_features &= (protocol_edid | protocol_dmabuf2); + msg = (VhostUserGpuMsg) { .request = VHOST_USER_GPU_SET_PROTOCOL_FEATURES, .size = sizeof(uint64_t), - .payload.u64 = 0 + .payload.u64 = protocol_features, }; vg_send_msg(g, &msg, -1); g->wait_in = 0; vg_handle_ctrl(&g->dev.parent, 0); + if (g->edid_inited && !(protocol_features & protocol_edid)) { + g_printerr("EDID feature set by the frontend but it does not support " + "the EDID vhost-user-gpu protocol.\n"); + exit(EXIT_FAILURE); + } + + g->use_modifiers = !!(protocol_features & protocol_dmabuf2); + return G_SOURCE_REMOVE; } @@ -1049,7 +1110,7 @@ static void set_gpu_protocol_features(VuGpu *g) { VhostUserGpuMsg msg = { - .request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES + .request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES, }; vg_send_msg(g, &msg, -1); @@ -1086,6 +1147,7 @@ vg_get_features(VuDev *dev) if (opt_virgl) { features |= 1 << VIRTIO_GPU_F_VIRGL; } + features |= 1 << VIRTIO_GPU_F_EDID; return features; } @@ -1103,6 +1165,8 @@ vg_set_features(VuDev *dev, uint64_t features) g->virgl_inited = true; } + g->edid_inited = !!(features & (1 << VIRTIO_GPU_F_EDID)); + g->virgl = virgl; } diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c index 3e45e1bd33..51da0e3667 100644 --- a/contrib/vhost-user-gpu/virgl.c +++ b/contrib/vhost-user-gpu/virgl.c @@ -318,6 +318,37 @@ virgl_resource_detach_backing(VuGpu *g, vg_cleanup_mapping_iov(g, res_iovs, num_iovs); } +static int +virgl_get_resource_info_modifiers(uint32_t resource_id, + struct virgl_renderer_resource_info *info, + uint64_t *modifiers) +{ + int ret; +#ifdef VIRGL_RENDERER_RESOURCE_INFO_EXT_VERSION + struct virgl_renderer_resource_info_ext info_ext; + ret = virgl_renderer_resource_get_info_ext(resource_id, &info_ext); + if (ret) { + return ret; + } + + *info = info_ext.base; + *modifiers = info_ext.modifiers; +#else + ret = virgl_renderer_resource_get_info(resource_id, info); + if (ret) { + return ret; + } + + /* + * Before virgl_renderer_resource_get_info_ext, + * getting the modifiers was not possible. + */ + *modifiers = 0; +#endif + + return 0; +} + static void virgl_cmd_set_scanout(VuGpu *g, struct virtio_gpu_ctrl_command *cmd) @@ -338,8 +369,10 @@ virgl_cmd_set_scanout(VuGpu *g, memset(&info, 0, sizeof(info)); if (ss.resource_id && ss.r.width && ss.r.height) { - ret = virgl_renderer_resource_get_info(ss.resource_id, &info); - if (ret == -1) { + uint64_t modifiers = 0; + ret = virgl_get_resource_info_modifiers(ss.resource_id, &info, + &modifiers); + if (ret) { g_critical("%s: illegal resource specified %d\n", __func__, ss.resource_id); cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; @@ -354,8 +387,6 @@ virgl_cmd_set_scanout(VuGpu *g, } assert(fd >= 0); VhostUserGpuMsg msg = { - .request = VHOST_USER_GPU_DMABUF_SCANOUT, - .size = sizeof(VhostUserGpuDMABUFScanout), .payload.dmabuf_scanout.scanout_id = ss.scanout_id, .payload.dmabuf_scanout.x = ss.r.x, .payload.dmabuf_scanout.y = ss.r.y, @@ -367,6 +398,20 @@ virgl_cmd_set_scanout(VuGpu *g, .payload.dmabuf_scanout.fd_flags = info.flags, .payload.dmabuf_scanout.fd_drm_fourcc = info.drm_fourcc }; + + if (g->use_modifiers) { + /* + * The message uses all the fields set in dmabuf_scanout plus + * modifiers which is appended after VhostUserGpuDMABUFScanout. + */ + msg.request = VHOST_USER_GPU_DMABUF_SCANOUT2; + msg.size = sizeof(VhostUserGpuDMABUFScanout2); + msg.payload.dmabuf_scanout2.modifier = modifiers; + } else { + msg.request = VHOST_USER_GPU_DMABUF_SCANOUT; + msg.size = sizeof(VhostUserGpuDMABUFScanout); + } + vg_send_msg(g, &msg, fd); close(fd); } else { @@ -495,6 +540,9 @@ void vg_virgl_process_cmd(VuGpu *g, struct virtio_gpu_ctrl_command *cmd) case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: vg_get_display_info(g, cmd); break; + case VIRTIO_GPU_CMD_GET_EDID: + vg_get_edid(g, cmd); + break; default: g_debug("TODO handle ctrl %x\n", cmd->cmd_hdr.type); cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h index e2864bba68..654c392fbb 100644 --- a/contrib/vhost-user-gpu/vugpu.h +++ b/contrib/vhost-user-gpu/vugpu.h @@ -36,6 +36,8 @@ typedef enum VhostUserGpuRequest { VHOST_USER_GPU_UPDATE, VHOST_USER_GPU_DMABUF_SCANOUT, VHOST_USER_GPU_DMABUF_UPDATE, + VHOST_USER_GPU_GET_EDID, + VHOST_USER_GPU_DMABUF_SCANOUT2, } VhostUserGpuRequest; typedef struct VhostUserGpuDisplayInfoReply { @@ -83,6 +85,15 @@ typedef struct VhostUserGpuDMABUFScanout { int fd_drm_fourcc; } QEMU_PACKED VhostUserGpuDMABUFScanout; +typedef struct VhostUserGpuDMABUFScanout2 { + struct VhostUserGpuDMABUFScanout dmabuf_scanout; + uint64_t modifier; +} QEMU_PACKED VhostUserGpuDMABUFScanout2; + +typedef struct VhostUserGpuEdidRequest { + uint32_t scanout_id; +} QEMU_PACKED VhostUserGpuEdidRequest; + typedef struct VhostUserGpuMsg { uint32_t request; /* VhostUserGpuRequest */ uint32_t flags; @@ -93,6 +104,9 @@ typedef struct VhostUserGpuMsg { VhostUserGpuScanout scanout; VhostUserGpuUpdate update; VhostUserGpuDMABUFScanout dmabuf_scanout; + VhostUserGpuDMABUFScanout2 dmabuf_scanout2; + VhostUserGpuEdidRequest edid_req; + struct virtio_gpu_resp_edid resp_edid; struct virtio_gpu_resp_display_info display_info; uint64_t u64; } payload; @@ -104,6 +118,9 @@ static VhostUserGpuMsg m __attribute__ ((unused)); #define VHOST_USER_GPU_MSG_FLAG_REPLY 0x4 +#define VHOST_USER_GPU_PROTOCOL_F_EDID 0 +#define VHOST_USER_GPU_PROTOCOL_F_DMABUF2 1 + struct virtio_gpu_scanout { uint32_t width, height; int x, y; @@ -122,6 +139,8 @@ typedef struct VuGpu { bool virgl; bool virgl_inited; + bool edid_inited; + bool use_modifiers; uint32_t inflight; struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS]; @@ -145,12 +164,12 @@ struct virtio_gpu_ctrl_command { }; #define VUGPU_FILL_CMD(out) do { \ - size_t s; \ - s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \ + size_t vugpufillcmd_s_ = \ + iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \ &out, sizeof(out)); \ - if (s != sizeof(out)) { \ + if (vugpufillcmd_s_ != sizeof(out)) { \ g_critical("%s: command size incorrect %zu vs %zu", \ - __func__, s, sizeof(out)); \ + __func__, vugpufillcmd_s_, sizeof(out)); \ return; \ } \ } while (0) @@ -171,6 +190,7 @@ int vg_create_mapping_iov(VuGpu *g, struct iovec **iov); void vg_cleanup_mapping_iov(VuGpu *g, struct iovec *iov, uint32_t count); void vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd); +void vg_get_edid(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd); void vg_wait_ok(VuGpu *g); diff --git a/contrib/vhost-user-input/meson.build b/contrib/vhost-user-input/meson.build index 21a9ed4f15..840d866594 100644 --- a/contrib/vhost-user-input/meson.build +++ b/contrib/vhost-user-input/meson.build @@ -1,4 +1,4 @@ executable('vhost-user-input', files('main.c'), dependencies: [qemuutil, vhost_user], - build_by_default: targetos == 'linux', + build_by_default: host_os == 'linux', install: false) diff --git a/contrib/vhost-user-scsi/meson.build b/contrib/vhost-user-scsi/meson.build index cc893f6f20..44be04853e 100644 --- a/contrib/vhost-user-scsi/meson.build +++ b/contrib/vhost-user-scsi/meson.build @@ -1,6 +1,6 @@ if libiscsi.found() executable('vhost-user-scsi', files('vhost-user-scsi.c'), dependencies: [qemuutil, libiscsi, vhost_user], - build_by_default: targetos == 'linux', + build_by_default: host_os == 'linux', install: false) endif diff --git a/cpus-common.c b/cpu-common.c similarity index 79% rename from cpus-common.c rename to cpu-common.c index db459b41ce..ce78273af5 100644 --- a/cpus-common.c +++ b/cpu-common.c @@ -23,8 +23,9 @@ #include "hw/core/cpu.h" #include "sysemu/cpus.h" #include "qemu/lockable.h" +#include "trace/trace-root.h" -static QemuMutex qemu_cpu_list_lock; +QemuMutex qemu_cpu_list_lock; static QemuCond exclusive_cond; static QemuCond exclusive_resume; static QemuCond qemu_work_cond; @@ -72,7 +73,13 @@ static int cpu_get_free_index(void) return max_cpu_index; } -CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus); +CPUTailQ cpus_queue = QTAILQ_HEAD_INITIALIZER(cpus_queue); +static unsigned int cpu_list_generation_id; + +unsigned int cpu_list_generation_id_get(void) +{ + return cpu_list_generation_id; +} void cpu_list_add(CPUState *cpu) { @@ -83,7 +90,8 @@ void cpu_list_add(CPUState *cpu) } else { assert(!cpu_index_auto_assigned); } - QTAILQ_INSERT_TAIL_RCU(&cpus, cpu, node); + QTAILQ_INSERT_TAIL_RCU(&cpus_queue, cpu, node); + cpu_list_generation_id++; } void cpu_list_remove(CPUState *cpu) @@ -94,8 +102,9 @@ void cpu_list_remove(CPUState *cpu) return; } - QTAILQ_REMOVE_RCU(&cpus, cpu, node); + QTAILQ_REMOVE_RCU(&cpus_queue, cpu, node); cpu->cpu_index = UNASSIGNED_CPU_INDEX; + cpu_list_generation_id++; } CPUState *qemu_get_cpu(int index) @@ -148,7 +157,7 @@ void do_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data, wi.exclusive = false; queue_work_on_cpu(cpu, &wi); - while (!qatomic_mb_read(&wi.done)) { + while (!qatomic_load_acquire(&wi.done)) { CPUState *self_cpu = current_cpu; qemu_cond_wait(&qemu_work_cond, mutex); @@ -184,6 +193,11 @@ void start_exclusive(void) CPUState *other_cpu; int running_cpus; + if (current_cpu->exclusive_context_count) { + current_cpu->exclusive_context_count++; + return; + } + qemu_mutex_lock(&qemu_cpu_list_lock); exclusive_idle(); @@ -211,13 +225,16 @@ void start_exclusive(void) */ qemu_mutex_unlock(&qemu_cpu_list_lock); - current_cpu->in_exclusive_context = true; + current_cpu->exclusive_context_count = 1; } /* Finish an exclusive operation. */ void end_exclusive(void) { - current_cpu->in_exclusive_context = false; + current_cpu->exclusive_context_count--; + if (current_cpu->exclusive_context_count) { + return; + } qemu_mutex_lock(&qemu_cpu_list_lock); qatomic_set(&pending_cpus, 0); @@ -334,11 +351,11 @@ void process_queued_cpu_work(CPUState *cpu) * BQL, so it goes to sleep; start_exclusive() is sleeping too, so * neither CPU can proceed. */ - qemu_mutex_unlock_iothread(); + bql_unlock(); start_exclusive(); wi->func(cpu, wi->data); end_exclusive(); - qemu_mutex_lock_iothread(); + bql_lock(); } else { wi->func(cpu, wi->data); } @@ -346,9 +363,80 @@ void process_queued_cpu_work(CPUState *cpu) if (wi->free) { g_free(wi); } else { - qatomic_mb_set(&wi->done, true); + qatomic_store_release(&wi->done, true); } } qemu_mutex_unlock(&cpu->work_mutex); qemu_cond_broadcast(&qemu_work_cond); } + +/* Add a breakpoint. */ +int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags, + CPUBreakpoint **breakpoint) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + CPUBreakpoint *bp; + + if (cc->gdb_adjust_breakpoint) { + pc = cc->gdb_adjust_breakpoint(cpu, pc); + } + + bp = g_malloc(sizeof(*bp)); + + bp->pc = pc; + bp->flags = flags; + + /* keep all GDB-injected breakpoints in front */ + if (flags & BP_GDB) { + QTAILQ_INSERT_HEAD(&cpu->breakpoints, bp, entry); + } else { + QTAILQ_INSERT_TAIL(&cpu->breakpoints, bp, entry); + } + + if (breakpoint) { + *breakpoint = bp; + } + + trace_breakpoint_insert(cpu->cpu_index, pc, flags); + return 0; +} + +/* Remove a specific breakpoint. */ +int cpu_breakpoint_remove(CPUState *cpu, vaddr pc, int flags) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + CPUBreakpoint *bp; + + if (cc->gdb_adjust_breakpoint) { + pc = cc->gdb_adjust_breakpoint(cpu, pc); + } + + QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { + if (bp->pc == pc && bp->flags == flags) { + cpu_breakpoint_remove_by_ref(cpu, bp); + return 0; + } + } + return -ENOENT; +} + +/* Remove a specific breakpoint by reference. */ +void cpu_breakpoint_remove_by_ref(CPUState *cpu, CPUBreakpoint *bp) +{ + QTAILQ_REMOVE(&cpu->breakpoints, bp, entry); + + trace_breakpoint_remove(cpu->cpu_index, bp->pc, bp->flags); + g_free(bp); +} + +/* Remove all matching breakpoints. */ +void cpu_breakpoint_remove_all(CPUState *cpu, int mask) +{ + CPUBreakpoint *bp, *next; + + QTAILQ_FOREACH_SAFE(bp, &cpu->breakpoints, entry, next) { + if (bp->flags & mask) { + cpu_breakpoint_remove_by_ref(cpu, bp); + } + } +} diff --git a/cpu.c b/cpu-target.c similarity index 66% rename from cpu.c rename to cpu-target.c index 584ac78baf..4c0621bf33 100644 --- a/cpu.c +++ b/cpu-target.c @@ -24,27 +24,27 @@ #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" +#include "qemu/qemu-print.h" #include "migration/vmstate.h" #ifdef CONFIG_USER_ONLY #include "qemu.h" #else #include "hw/core/sysemu-cpu-ops.h" #include "exec/address-spaces.h" +#include "exec/memory.h" #endif +#include "sysemu/cpus.h" #include "sysemu/tcg.h" -#include "sysemu/kvm.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" #include "exec/cpu-common.h" #include "exec/exec-all.h" +#include "exec/tb-flush.h" #include "exec/translate-all.h" #include "exec/log.h" #include "hw/core/accel-cpu.h" #include "trace/trace-root.h" #include "qemu/accel.h" -uintptr_t qemu_host_page_size; -intptr_t qemu_host_page_mask; - #ifndef CONFIG_USER_ONLY static int cpu_common_post_load(void *opaque, int version_id) { @@ -86,7 +86,7 @@ static const VMStateDescription vmstate_cpu_common_exception_index = { .version_id = 1, .minimum_version_id = 1, .needed = cpu_common_exception_index_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(exception_index, CPUState), VMSTATE_END_OF_LIST() } @@ -104,7 +104,7 @@ static const VMStateDescription vmstate_cpu_common_crash_occurred = { .version_id = 1, .minimum_version_id = 1, .needed = cpu_common_crash_occurred_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(crash_occurred, CPUState), VMSTATE_END_OF_LIST() } @@ -116,12 +116,12 @@ const VMStateDescription vmstate_cpu_common = { .minimum_version_id = 1, .pre_load = cpu_common_pre_load, .post_load = cpu_common_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(halted, CPUState), VMSTATE_UINT32(interrupt_request, CPUState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_cpu_common_exception_index, &vmstate_cpu_common_crash_occurred, NULL @@ -129,20 +129,17 @@ const VMStateDescription vmstate_cpu_common = { }; #endif -void cpu_exec_realizefn(CPUState *cpu, Error **errp) +bool cpu_exec_realizefn(CPUState *cpu, Error **errp) { -#ifndef CONFIG_USER_ONLY - CPUClass *cc = CPU_GET_CLASS(cpu); -#endif + /* cache the cpu class for the hotpath */ + cpu->cc = CPU_GET_CLASS(cpu); + if (!accel_cpu_common_realize(cpu, errp)) { + return false; + } + + /* Wait until cpu initialization complete before exposing cpu. */ cpu_list_add(cpu); - if (!accel_cpu_realizefn(cpu, errp)) { - return; - } - /* NB: errp parameter is unused currently */ - if (tcg_enabled()) { - tcg_exec_realizefn(cpu, errp); - } #ifdef CONFIG_USER_ONLY assert(qdev_get_vmsd(DEVICE(cpu)) == NULL || @@ -151,10 +148,12 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp) if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { vmstate_register(NULL, cpu->cpu_index, &vmstate_cpu_common, cpu); } - if (cc->sysemu_ops->legacy_vmsd != NULL) { - vmstate_register(NULL, cpu->cpu_index, cc->sysemu_ops->legacy_vmsd, cpu); + if (cpu->cc->sysemu_ops->legacy_vmsd != NULL) { + vmstate_register(NULL, cpu->cpu_index, cpu->cc->sysemu_ops->legacy_vmsd, cpu); } #endif /* CONFIG_USER_ONLY */ + + return true; } void cpu_exec_unrealizefn(CPUState *cpu) @@ -169,11 +168,13 @@ void cpu_exec_unrealizefn(CPUState *cpu) vmstate_unregister(NULL, &vmstate_cpu_common, cpu); } #endif - if (tcg_enabled()) { - tcg_exec_unrealizefn(cpu); - } cpu_list_remove(cpu); + /* + * Now that the vCPU has been removed from the RCU list, we can call + * accel_cpu_common_unrealize, which may free fields using call_rcu. + */ + accel_cpu_common_unrealize(cpu); } /* @@ -191,7 +192,7 @@ static Property cpu_common_props[] = { prctl_unalign_sigbus, false), #else /* - * Create a memory property for softmmu CPU object, so users can + * Create a memory property for system CPU object, so users can * wire up its memory. The default if no link is set up is to use * the system address space. */ @@ -201,6 +202,7 @@ static Property cpu_common_props[] = { DEFINE_PROP_END_OF_LIST(), }; +#ifndef CONFIG_USER_ONLY static bool cpu_get_start_powered_off(Object *obj, Error **errp) { CPUState *cpu = CPU(obj); @@ -212,12 +214,13 @@ static void cpu_set_start_powered_off(Object *obj, bool value, Error **errp) CPUState *cpu = CPU(obj); cpu->start_powered_off = value; } +#endif void cpu_class_init_props(DeviceClass *dc) { +#ifndef CONFIG_USER_ONLY ObjectClass *oc = OBJECT_CLASS(dc); - device_class_set_props(dc, cpu_common_props); /* * We can't use DEFINE_PROP_BOOL in the Property array for this * property, because we want this to be settable after realize. @@ -225,6 +228,9 @@ void cpu_class_init_props(DeviceClass *dc) object_class_property_add_bool(oc, "start-powered-off", cpu_get_start_powered_off, cpu_set_start_powered_off); +#endif + + device_class_set_props(dc, cpu_common_props); } void cpu_exec_initfn(CPUState *cpu) @@ -239,6 +245,21 @@ void cpu_exec_initfn(CPUState *cpu) #endif } +char *cpu_model_from_type(const char *typename) +{ + const char *suffix = "-" CPU_RESOLVING_TYPE; + + if (!object_class_by_name(typename)) { + return NULL; + } + + if (g_str_has_suffix(typename, suffix)) { + return g_strndup(typename, strlen(typename) - strlen(suffix)); + } + + return g_strdup(typename); +} + const char *parse_cpu_option(const char *cpu_option) { ObjectClass *oc; @@ -266,112 +287,34 @@ const char *parse_cpu_option(const char *cpu_option) return cpu_type; } -void list_cpus(const char *optarg) +#ifndef cpu_list +static void cpu_list_entry(gpointer data, gpointer user_data) { - /* XXX: implement xxx_cpu_list for targets that still miss it */ -#if defined(cpu_list) - cpu_list(); -#endif -} + CPUClass *cc = CPU_CLASS(OBJECT_CLASS(data)); + const char *typename = object_class_get_name(OBJECT_CLASS(data)); + g_autofree char *model = cpu_model_from_type(typename); -#if defined(CONFIG_USER_ONLY) -void tb_invalidate_phys_addr(target_ulong addr) -{ - mmap_lock(); - tb_invalidate_phys_page_range(addr, addr + 1); - mmap_unlock(); -} -#else -void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) -{ - ram_addr_t ram_addr; - MemoryRegion *mr; - hwaddr l = 1; - - if (!tcg_enabled()) { - return; - } - - RCU_READ_LOCK_GUARD(); - mr = address_space_translate(as, addr, &addr, &l, false, attrs); - if (!(memory_region_is_ram(mr) - || memory_region_is_romd(mr))) { - return; - } - ram_addr = memory_region_get_ram_addr(mr) + addr; - tb_invalidate_phys_page_range(ram_addr, ram_addr + 1); -} -#endif - -/* Add a breakpoint. */ -int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags, - CPUBreakpoint **breakpoint) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - CPUBreakpoint *bp; - - if (cc->gdb_adjust_breakpoint) { - pc = cc->gdb_adjust_breakpoint(cpu, pc); - } - - bp = g_malloc(sizeof(*bp)); - - bp->pc = pc; - bp->flags = flags; - - /* keep all GDB-injected breakpoints in front */ - if (flags & BP_GDB) { - QTAILQ_INSERT_HEAD(&cpu->breakpoints, bp, entry); + if (cc->deprecation_note) { + qemu_printf(" %s (deprecated)\n", model); } else { - QTAILQ_INSERT_TAIL(&cpu->breakpoints, bp, entry); + qemu_printf(" %s\n", model); } - - if (breakpoint) { - *breakpoint = bp; - } - - trace_breakpoint_insert(cpu->cpu_index, pc, flags); - return 0; } -/* Remove a specific breakpoint. */ -int cpu_breakpoint_remove(CPUState *cpu, vaddr pc, int flags) +static void cpu_list(void) { - CPUClass *cc = CPU_GET_CLASS(cpu); - CPUBreakpoint *bp; + GSList *list; - if (cc->gdb_adjust_breakpoint) { - pc = cc->gdb_adjust_breakpoint(cpu, pc); - } - - QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { - if (bp->pc == pc && bp->flags == flags) { - cpu_breakpoint_remove_by_ref(cpu, bp); - return 0; - } - } - return -ENOENT; + list = object_class_get_list_sorted(TYPE_CPU, false); + qemu_printf("Available CPUs:\n"); + g_slist_foreach(list, cpu_list_entry, NULL); + g_slist_free(list); } +#endif -/* Remove a specific breakpoint by reference. */ -void cpu_breakpoint_remove_by_ref(CPUState *cpu, CPUBreakpoint *bp) +void list_cpus(void) { - QTAILQ_REMOVE(&cpu->breakpoints, bp, entry); - - trace_breakpoint_remove(cpu->cpu_index, bp->pc, bp->flags); - g_free(bp); -} - -/* Remove all matching breakpoints. */ -void cpu_breakpoint_remove_all(CPUState *cpu, int mask) -{ - CPUBreakpoint *bp, *next; - - QTAILQ_FOREACH_SAFE(bp, &cpu->breakpoints, entry, next) { - if (bp->flags & mask) { - cpu_breakpoint_remove_by_ref(cpu, bp); - } - } + cpu_list(); } /* enable or disable single step mode. EXCP_DEBUG is returned by the @@ -380,9 +323,14 @@ void cpu_single_step(CPUState *cpu, int enabled) { if (cpu->singlestep_enabled != enabled) { cpu->singlestep_enabled = enabled; - if (kvm_enabled()) { - kvm_update_guest_debug(cpu, 0); + +#if !defined(CONFIG_USER_ONLY) + const AccelOpsClass *ops = cpus_get_accel(); + if (ops->update_guest_debug) { + ops->update_guest_debug(cpu); } +#endif + trace_breakpoint_singlestep(cpu->cpu_index, enabled); } } @@ -432,6 +380,9 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, vaddr l, page; void * p; uint8_t *buf = ptr; + ssize_t written; + int ret = -1; + int fd = -1; while (len > 0) { page = addr & TARGET_PAGE_MASK; @@ -439,51 +390,84 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, if (l > len) l = len; flags = page_get_flags(page); - if (!(flags & PAGE_VALID)) - return -1; + if (!(flags & PAGE_VALID)) { + goto out_close; + } if (is_write) { - if (!(flags & PAGE_WRITE)) - return -1; + if (flags & PAGE_WRITE) { + /* XXX: this code should not depend on lock_user */ + p = lock_user(VERIFY_WRITE, addr, l, 0); + if (!p) { + goto out_close; + } + memcpy(p, buf, l); + unlock_user(p, addr, l); + } else { + /* Bypass the host page protection using ptrace. */ + if (fd == -1) { + fd = open("/proc/self/mem", O_WRONLY); + if (fd == -1) { + goto out; + } + } + /* + * If there is a TranslationBlock and we weren't bypassing the + * host page protection, the memcpy() above would SEGV, + * ultimately leading to page_unprotect(). So invalidate the + * translations manually. Both invalidation and pwrite() must + * be under mmap_lock() in order to prevent the creation of + * another TranslationBlock in between. + */ + mmap_lock(); + tb_invalidate_phys_range(addr, addr + l - 1); + written = pwrite(fd, buf, l, + (off_t)(uintptr_t)g2h_untagged(addr)); + mmap_unlock(); + if (written != l) { + goto out_close; + } + } + } else if (flags & PAGE_READ) { /* XXX: this code should not depend on lock_user */ - if (!(p = lock_user(VERIFY_WRITE, addr, l, 0))) - return -1; - memcpy(p, buf, l); - unlock_user(p, addr, l); - } else { - if (!(flags & PAGE_READ)) - return -1; - /* XXX: this code should not depend on lock_user */ - if (!(p = lock_user(VERIFY_READ, addr, l, 1))) - return -1; + p = lock_user(VERIFY_READ, addr, l, 1); + if (!p) { + goto out_close; + } memcpy(buf, p, l); unlock_user(p, addr, 0); + } else { + /* Bypass the host page protection using ptrace. */ + if (fd == -1) { + fd = open("/proc/self/mem", O_RDONLY); + if (fd == -1) { + goto out; + } + } + if (pread(fd, buf, l, + (off_t)(uintptr_t)g2h_untagged(addr)) != l) { + goto out_close; + } } len -= l; buf += l; addr += l; } - return 0; + ret = 0; +out_close: + if (fd != -1) { + close(fd); + } +out: + return ret; } #endif bool target_words_bigendian(void) { -#if TARGET_BIG_ENDIAN - return true; -#else - return false; -#endif + return TARGET_BIG_ENDIAN; } -void page_size_init(void) +const char *target_name(void) { - /* NOTE: we can always suppose that qemu_host_page_size >= - TARGET_PAGE_SIZE */ - if (qemu_host_page_size == 0) { - qemu_host_page_size = qemu_real_host_page_size(); - } - if (qemu_host_page_size < TARGET_PAGE_SIZE) { - qemu_host_page_size = TARGET_PAGE_SIZE; - } - qemu_host_page_mask = -(intptr_t)qemu_host_page_size; + return TARGET_NAME; } diff --git a/crypto/aes.c b/crypto/aes.c index af72ff7779..df4362ac60 100644 --- a/crypto/aes.c +++ b/crypto/aes.c @@ -28,7 +28,10 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "qemu/bitops.h" #include "crypto/aes.h" +#include "crypto/aes-round.h" typedef uint32_t u32; typedef uint8_t u8; @@ -108,278 +111,152 @@ const uint8_t AES_isbox[256] = { 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D, }; -const uint8_t AES_shifts[16] = { - 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 +/* AES ShiftRows, for complete unrolling. */ +#define AES_SH(X) (((X) * 5) & 15) + +/* AES InvShiftRows, for complete unrolling. */ +#define AES_ISH(X) (((X) * 13) & 15) + +/* + * MixColumns lookup table, for use with rot32. + */ +static const uint32_t AES_mc_rot[256] = { + 0x00000000, 0x03010102, 0x06020204, 0x05030306, + 0x0c040408, 0x0f05050a, 0x0a06060c, 0x0907070e, + 0x18080810, 0x1b090912, 0x1e0a0a14, 0x1d0b0b16, + 0x140c0c18, 0x170d0d1a, 0x120e0e1c, 0x110f0f1e, + 0x30101020, 0x33111122, 0x36121224, 0x35131326, + 0x3c141428, 0x3f15152a, 0x3a16162c, 0x3917172e, + 0x28181830, 0x2b191932, 0x2e1a1a34, 0x2d1b1b36, + 0x241c1c38, 0x271d1d3a, 0x221e1e3c, 0x211f1f3e, + 0x60202040, 0x63212142, 0x66222244, 0x65232346, + 0x6c242448, 0x6f25254a, 0x6a26264c, 0x6927274e, + 0x78282850, 0x7b292952, 0x7e2a2a54, 0x7d2b2b56, + 0x742c2c58, 0x772d2d5a, 0x722e2e5c, 0x712f2f5e, + 0x50303060, 0x53313162, 0x56323264, 0x55333366, + 0x5c343468, 0x5f35356a, 0x5a36366c, 0x5937376e, + 0x48383870, 0x4b393972, 0x4e3a3a74, 0x4d3b3b76, + 0x443c3c78, 0x473d3d7a, 0x423e3e7c, 0x413f3f7e, + 0xc0404080, 0xc3414182, 0xc6424284, 0xc5434386, + 0xcc444488, 0xcf45458a, 0xca46468c, 0xc947478e, + 0xd8484890, 0xdb494992, 0xde4a4a94, 0xdd4b4b96, + 0xd44c4c98, 0xd74d4d9a, 0xd24e4e9c, 0xd14f4f9e, + 0xf05050a0, 0xf35151a2, 0xf65252a4, 0xf55353a6, + 0xfc5454a8, 0xff5555aa, 0xfa5656ac, 0xf95757ae, + 0xe85858b0, 0xeb5959b2, 0xee5a5ab4, 0xed5b5bb6, + 0xe45c5cb8, 0xe75d5dba, 0xe25e5ebc, 0xe15f5fbe, + 0xa06060c0, 0xa36161c2, 0xa66262c4, 0xa56363c6, + 0xac6464c8, 0xaf6565ca, 0xaa6666cc, 0xa96767ce, + 0xb86868d0, 0xbb6969d2, 0xbe6a6ad4, 0xbd6b6bd6, + 0xb46c6cd8, 0xb76d6dda, 0xb26e6edc, 0xb16f6fde, + 0x907070e0, 0x937171e2, 0x967272e4, 0x957373e6, + 0x9c7474e8, 0x9f7575ea, 0x9a7676ec, 0x997777ee, + 0x887878f0, 0x8b7979f2, 0x8e7a7af4, 0x8d7b7bf6, + 0x847c7cf8, 0x877d7dfa, 0x827e7efc, 0x817f7ffe, + 0x9b80801b, 0x98818119, 0x9d82821f, 0x9e83831d, + 0x97848413, 0x94858511, 0x91868617, 0x92878715, + 0x8388880b, 0x80898909, 0x858a8a0f, 0x868b8b0d, + 0x8f8c8c03, 0x8c8d8d01, 0x898e8e07, 0x8a8f8f05, + 0xab90903b, 0xa8919139, 0xad92923f, 0xae93933d, + 0xa7949433, 0xa4959531, 0xa1969637, 0xa2979735, + 0xb398982b, 0xb0999929, 0xb59a9a2f, 0xb69b9b2d, + 0xbf9c9c23, 0xbc9d9d21, 0xb99e9e27, 0xba9f9f25, + 0xfba0a05b, 0xf8a1a159, 0xfda2a25f, 0xfea3a35d, + 0xf7a4a453, 0xf4a5a551, 0xf1a6a657, 0xf2a7a755, + 0xe3a8a84b, 0xe0a9a949, 0xe5aaaa4f, 0xe6abab4d, + 0xefacac43, 0xecadad41, 0xe9aeae47, 0xeaafaf45, + 0xcbb0b07b, 0xc8b1b179, 0xcdb2b27f, 0xceb3b37d, + 0xc7b4b473, 0xc4b5b571, 0xc1b6b677, 0xc2b7b775, + 0xd3b8b86b, 0xd0b9b969, 0xd5baba6f, 0xd6bbbb6d, + 0xdfbcbc63, 0xdcbdbd61, 0xd9bebe67, 0xdabfbf65, + 0x5bc0c09b, 0x58c1c199, 0x5dc2c29f, 0x5ec3c39d, + 0x57c4c493, 0x54c5c591, 0x51c6c697, 0x52c7c795, + 0x43c8c88b, 0x40c9c989, 0x45caca8f, 0x46cbcb8d, + 0x4fcccc83, 0x4ccdcd81, 0x49cece87, 0x4acfcf85, + 0x6bd0d0bb, 0x68d1d1b9, 0x6dd2d2bf, 0x6ed3d3bd, + 0x67d4d4b3, 0x64d5d5b1, 0x61d6d6b7, 0x62d7d7b5, + 0x73d8d8ab, 0x70d9d9a9, 0x75dadaaf, 0x76dbdbad, + 0x7fdcdca3, 0x7cdddda1, 0x79dedea7, 0x7adfdfa5, + 0x3be0e0db, 0x38e1e1d9, 0x3de2e2df, 0x3ee3e3dd, + 0x37e4e4d3, 0x34e5e5d1, 0x31e6e6d7, 0x32e7e7d5, + 0x23e8e8cb, 0x20e9e9c9, 0x25eaeacf, 0x26ebebcd, + 0x2fececc3, 0x2cededc1, 0x29eeeec7, 0x2aefefc5, + 0x0bf0f0fb, 0x08f1f1f9, 0x0df2f2ff, 0x0ef3f3fd, + 0x07f4f4f3, 0x04f5f5f1, 0x01f6f6f7, 0x02f7f7f5, + 0x13f8f8eb, 0x10f9f9e9, 0x15fafaef, 0x16fbfbed, + 0x1ffcfce3, 0x1cfdfde1, 0x19fefee7, 0x1affffe5, }; -const uint8_t AES_ishifts[16] = { - 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 +/* + * Inverse MixColumns lookup table, for use with rot32. + */ +static const uint32_t AES_imc_rot[256] = { + 0x00000000, 0x0b0d090e, 0x161a121c, 0x1d171b12, + 0x2c342438, 0x27392d36, 0x3a2e3624, 0x31233f2a, + 0x58684870, 0x5365417e, 0x4e725a6c, 0x457f5362, + 0x745c6c48, 0x7f516546, 0x62467e54, 0x694b775a, + 0xb0d090e0, 0xbbdd99ee, 0xa6ca82fc, 0xadc78bf2, + 0x9ce4b4d8, 0x97e9bdd6, 0x8afea6c4, 0x81f3afca, + 0xe8b8d890, 0xe3b5d19e, 0xfea2ca8c, 0xf5afc382, + 0xc48cfca8, 0xcf81f5a6, 0xd296eeb4, 0xd99be7ba, + 0x7bbb3bdb, 0x70b632d5, 0x6da129c7, 0x66ac20c9, + 0x578f1fe3, 0x5c8216ed, 0x41950dff, 0x4a9804f1, + 0x23d373ab, 0x28de7aa5, 0x35c961b7, 0x3ec468b9, + 0x0fe75793, 0x04ea5e9d, 0x19fd458f, 0x12f04c81, + 0xcb6bab3b, 0xc066a235, 0xdd71b927, 0xd67cb029, + 0xe75f8f03, 0xec52860d, 0xf1459d1f, 0xfa489411, + 0x9303e34b, 0x980eea45, 0x8519f157, 0x8e14f859, + 0xbf37c773, 0xb43ace7d, 0xa92dd56f, 0xa220dc61, + 0xf66d76ad, 0xfd607fa3, 0xe07764b1, 0xeb7a6dbf, + 0xda595295, 0xd1545b9b, 0xcc434089, 0xc74e4987, + 0xae053edd, 0xa50837d3, 0xb81f2cc1, 0xb31225cf, + 0x82311ae5, 0x893c13eb, 0x942b08f9, 0x9f2601f7, + 0x46bde64d, 0x4db0ef43, 0x50a7f451, 0x5baafd5f, + 0x6a89c275, 0x6184cb7b, 0x7c93d069, 0x779ed967, + 0x1ed5ae3d, 0x15d8a733, 0x08cfbc21, 0x03c2b52f, + 0x32e18a05, 0x39ec830b, 0x24fb9819, 0x2ff69117, + 0x8dd64d76, 0x86db4478, 0x9bcc5f6a, 0x90c15664, + 0xa1e2694e, 0xaaef6040, 0xb7f87b52, 0xbcf5725c, + 0xd5be0506, 0xdeb30c08, 0xc3a4171a, 0xc8a91e14, + 0xf98a213e, 0xf2872830, 0xef903322, 0xe49d3a2c, + 0x3d06dd96, 0x360bd498, 0x2b1ccf8a, 0x2011c684, + 0x1132f9ae, 0x1a3ff0a0, 0x0728ebb2, 0x0c25e2bc, + 0x656e95e6, 0x6e639ce8, 0x737487fa, 0x78798ef4, + 0x495ab1de, 0x4257b8d0, 0x5f40a3c2, 0x544daacc, + 0xf7daec41, 0xfcd7e54f, 0xe1c0fe5d, 0xeacdf753, + 0xdbeec879, 0xd0e3c177, 0xcdf4da65, 0xc6f9d36b, + 0xafb2a431, 0xa4bfad3f, 0xb9a8b62d, 0xb2a5bf23, + 0x83868009, 0x888b8907, 0x959c9215, 0x9e919b1b, + 0x470a7ca1, 0x4c0775af, 0x51106ebd, 0x5a1d67b3, + 0x6b3e5899, 0x60335197, 0x7d244a85, 0x7629438b, + 0x1f6234d1, 0x146f3ddf, 0x097826cd, 0x02752fc3, + 0x335610e9, 0x385b19e7, 0x254c02f5, 0x2e410bfb, + 0x8c61d79a, 0x876cde94, 0x9a7bc586, 0x9176cc88, + 0xa055f3a2, 0xab58faac, 0xb64fe1be, 0xbd42e8b0, + 0xd4099fea, 0xdf0496e4, 0xc2138df6, 0xc91e84f8, + 0xf83dbbd2, 0xf330b2dc, 0xee27a9ce, 0xe52aa0c0, + 0x3cb1477a, 0x37bc4e74, 0x2aab5566, 0x21a65c68, + 0x10856342, 0x1b886a4c, 0x069f715e, 0x0d927850, + 0x64d90f0a, 0x6fd40604, 0x72c31d16, 0x79ce1418, + 0x48ed2b32, 0x43e0223c, 0x5ef7392e, 0x55fa3020, + 0x01b79aec, 0x0aba93e2, 0x17ad88f0, 0x1ca081fe, + 0x2d83bed4, 0x268eb7da, 0x3b99acc8, 0x3094a5c6, + 0x59dfd29c, 0x52d2db92, 0x4fc5c080, 0x44c8c98e, + 0x75ebf6a4, 0x7ee6ffaa, 0x63f1e4b8, 0x68fcedb6, + 0xb1670a0c, 0xba6a0302, 0xa77d1810, 0xac70111e, + 0x9d532e34, 0x965e273a, 0x8b493c28, 0x80443526, + 0xe90f427c, 0xe2024b72, 0xff155060, 0xf418596e, + 0xc53b6644, 0xce366f4a, 0xd3217458, 0xd82c7d56, + 0x7a0ca137, 0x7101a839, 0x6c16b32b, 0x671bba25, + 0x5638850f, 0x5d358c01, 0x40229713, 0x4b2f9e1d, + 0x2264e947, 0x2969e049, 0x347efb5b, 0x3f73f255, + 0x0e50cd7f, 0x055dc471, 0x184adf63, 0x1347d66d, + 0xcadc31d7, 0xc1d138d9, 0xdcc623cb, 0xd7cb2ac5, + 0xe6e815ef, 0xede51ce1, 0xf0f207f3, 0xfbff0efd, + 0x92b479a7, 0x99b970a9, 0x84ae6bbb, 0x8fa362b5, + 0xbe805d9f, 0xb58d5491, 0xa89a4f83, 0xa397468d, }; -/* AES_imc[x][0] = [x].[0e, 09, 0d, 0b]; */ -/* AES_imc[x][1] = [x].[0b, 0e, 09, 0d]; */ -/* AES_imc[x][2] = [x].[0d, 0b, 0e, 09]; */ -/* AES_imc[x][3] = [x].[09, 0d, 0b, 0e]; */ -const uint32_t AES_imc[256][4] = { - { 0x00000000, 0x00000000, 0x00000000, 0x00000000, }, /* x=00 */ - { 0x0E090D0B, 0x0B0E090D, 0x0D0B0E09, 0x090D0B0E, }, /* x=01 */ - { 0x1C121A16, 0x161C121A, 0x1A161C12, 0x121A161C, }, /* x=02 */ - { 0x121B171D, 0x1D121B17, 0x171D121B, 0x1B171D12, }, /* x=03 */ - { 0x3824342C, 0x2C382434, 0x342C3824, 0x24342C38, }, /* x=04 */ - { 0x362D3927, 0x27362D39, 0x3927362D, 0x2D392736, }, /* x=05 */ - { 0x24362E3A, 0x3A24362E, 0x2E3A2436, 0x362E3A24, }, /* x=06 */ - { 0x2A3F2331, 0x312A3F23, 0x23312A3F, 0x3F23312A, }, /* x=07 */ - { 0x70486858, 0x58704868, 0x68587048, 0x48685870, }, /* x=08 */ - { 0x7E416553, 0x537E4165, 0x65537E41, 0x4165537E, }, /* x=09 */ - { 0x6C5A724E, 0x4E6C5A72, 0x724E6C5A, 0x5A724E6C, }, /* x=0A */ - { 0x62537F45, 0x4562537F, 0x7F456253, 0x537F4562, }, /* x=0B */ - { 0x486C5C74, 0x74486C5C, 0x5C74486C, 0x6C5C7448, }, /* x=0C */ - { 0x4665517F, 0x7F466551, 0x517F4665, 0x65517F46, }, /* x=0D */ - { 0x547E4662, 0x62547E46, 0x4662547E, 0x7E466254, }, /* x=0E */ - { 0x5A774B69, 0x695A774B, 0x4B695A77, 0x774B695A, }, /* x=0F */ - { 0xE090D0B0, 0xB0E090D0, 0xD0B0E090, 0x90D0B0E0, }, /* x=10 */ - { 0xEE99DDBB, 0xBBEE99DD, 0xDDBBEE99, 0x99DDBBEE, }, /* x=11 */ - { 0xFC82CAA6, 0xA6FC82CA, 0xCAA6FC82, 0x82CAA6FC, }, /* x=12 */ - { 0xF28BC7AD, 0xADF28BC7, 0xC7ADF28B, 0x8BC7ADF2, }, /* x=13 */ - { 0xD8B4E49C, 0x9CD8B4E4, 0xE49CD8B4, 0xB4E49CD8, }, /* x=14 */ - { 0xD6BDE997, 0x97D6BDE9, 0xE997D6BD, 0xBDE997D6, }, /* x=15 */ - { 0xC4A6FE8A, 0x8AC4A6FE, 0xFE8AC4A6, 0xA6FE8AC4, }, /* x=16 */ - { 0xCAAFF381, 0x81CAAFF3, 0xF381CAAF, 0xAFF381CA, }, /* x=17 */ - { 0x90D8B8E8, 0xE890D8B8, 0xB8E890D8, 0xD8B8E890, }, /* x=18 */ - { 0x9ED1B5E3, 0xE39ED1B5, 0xB5E39ED1, 0xD1B5E39E, }, /* x=19 */ - { 0x8CCAA2FE, 0xFE8CCAA2, 0xA2FE8CCA, 0xCAA2FE8C, }, /* x=1A */ - { 0x82C3AFF5, 0xF582C3AF, 0xAFF582C3, 0xC3AFF582, }, /* x=1B */ - { 0xA8FC8CC4, 0xC4A8FC8C, 0x8CC4A8FC, 0xFC8CC4A8, }, /* x=1C */ - { 0xA6F581CF, 0xCFA6F581, 0x81CFA6F5, 0xF581CFA6, }, /* x=1D */ - { 0xB4EE96D2, 0xD2B4EE96, 0x96D2B4EE, 0xEE96D2B4, }, /* x=1E */ - { 0xBAE79BD9, 0xD9BAE79B, 0x9BD9BAE7, 0xE79BD9BA, }, /* x=1F */ - { 0xDB3BBB7B, 0x7BDB3BBB, 0xBB7BDB3B, 0x3BBB7BDB, }, /* x=20 */ - { 0xD532B670, 0x70D532B6, 0xB670D532, 0x32B670D5, }, /* x=21 */ - { 0xC729A16D, 0x6DC729A1, 0xA16DC729, 0x29A16DC7, }, /* x=22 */ - { 0xC920AC66, 0x66C920AC, 0xAC66C920, 0x20AC66C9, }, /* x=23 */ - { 0xE31F8F57, 0x57E31F8F, 0x8F57E31F, 0x1F8F57E3, }, /* x=24 */ - { 0xED16825C, 0x5CED1682, 0x825CED16, 0x16825CED, }, /* x=25 */ - { 0xFF0D9541, 0x41FF0D95, 0x9541FF0D, 0x0D9541FF, }, /* x=26 */ - { 0xF104984A, 0x4AF10498, 0x984AF104, 0x04984AF1, }, /* x=27 */ - { 0xAB73D323, 0x23AB73D3, 0xD323AB73, 0x73D323AB, }, /* x=28 */ - { 0xA57ADE28, 0x28A57ADE, 0xDE28A57A, 0x7ADE28A5, }, /* x=29 */ - { 0xB761C935, 0x35B761C9, 0xC935B761, 0x61C935B7, }, /* x=2A */ - { 0xB968C43E, 0x3EB968C4, 0xC43EB968, 0x68C43EB9, }, /* x=2B */ - { 0x9357E70F, 0x0F9357E7, 0xE70F9357, 0x57E70F93, }, /* x=2C */ - { 0x9D5EEA04, 0x049D5EEA, 0xEA049D5E, 0x5EEA049D, }, /* x=2D */ - { 0x8F45FD19, 0x198F45FD, 0xFD198F45, 0x45FD198F, }, /* x=2E */ - { 0x814CF012, 0x12814CF0, 0xF012814C, 0x4CF01281, }, /* x=2F */ - { 0x3BAB6BCB, 0xCB3BAB6B, 0x6BCB3BAB, 0xAB6BCB3B, }, /* x=30 */ - { 0x35A266C0, 0xC035A266, 0x66C035A2, 0xA266C035, }, /* x=31 */ - { 0x27B971DD, 0xDD27B971, 0x71DD27B9, 0xB971DD27, }, /* x=32 */ - { 0x29B07CD6, 0xD629B07C, 0x7CD629B0, 0xB07CD629, }, /* x=33 */ - { 0x038F5FE7, 0xE7038F5F, 0x5FE7038F, 0x8F5FE703, }, /* x=34 */ - { 0x0D8652EC, 0xEC0D8652, 0x52EC0D86, 0x8652EC0D, }, /* x=35 */ - { 0x1F9D45F1, 0xF11F9D45, 0x45F11F9D, 0x9D45F11F, }, /* x=36 */ - { 0x119448FA, 0xFA119448, 0x48FA1194, 0x9448FA11, }, /* x=37 */ - { 0x4BE30393, 0x934BE303, 0x03934BE3, 0xE303934B, }, /* x=38 */ - { 0x45EA0E98, 0x9845EA0E, 0x0E9845EA, 0xEA0E9845, }, /* x=39 */ - { 0x57F11985, 0x8557F119, 0x198557F1, 0xF1198557, }, /* x=3A */ - { 0x59F8148E, 0x8E59F814, 0x148E59F8, 0xF8148E59, }, /* x=3B */ - { 0x73C737BF, 0xBF73C737, 0x37BF73C7, 0xC737BF73, }, /* x=3C */ - { 0x7DCE3AB4, 0xB47DCE3A, 0x3AB47DCE, 0xCE3AB47D, }, /* x=3D */ - { 0x6FD52DA9, 0xA96FD52D, 0x2DA96FD5, 0xD52DA96F, }, /* x=3E */ - { 0x61DC20A2, 0xA261DC20, 0x20A261DC, 0xDC20A261, }, /* x=3F */ - { 0xAD766DF6, 0xF6AD766D, 0x6DF6AD76, 0x766DF6AD, }, /* x=40 */ - { 0xA37F60FD, 0xFDA37F60, 0x60FDA37F, 0x7F60FDA3, }, /* x=41 */ - { 0xB16477E0, 0xE0B16477, 0x77E0B164, 0x6477E0B1, }, /* x=42 */ - { 0xBF6D7AEB, 0xEBBF6D7A, 0x7AEBBF6D, 0x6D7AEBBF, }, /* x=43 */ - { 0x955259DA, 0xDA955259, 0x59DA9552, 0x5259DA95, }, /* x=44 */ - { 0x9B5B54D1, 0xD19B5B54, 0x54D19B5B, 0x5B54D19B, }, /* x=45 */ - { 0x894043CC, 0xCC894043, 0x43CC8940, 0x4043CC89, }, /* x=46 */ - { 0x87494EC7, 0xC787494E, 0x4EC78749, 0x494EC787, }, /* x=47 */ - { 0xDD3E05AE, 0xAEDD3E05, 0x05AEDD3E, 0x3E05AEDD, }, /* x=48 */ - { 0xD33708A5, 0xA5D33708, 0x08A5D337, 0x3708A5D3, }, /* x=49 */ - { 0xC12C1FB8, 0xB8C12C1F, 0x1FB8C12C, 0x2C1FB8C1, }, /* x=4A */ - { 0xCF2512B3, 0xB3CF2512, 0x12B3CF25, 0x2512B3CF, }, /* x=4B */ - { 0xE51A3182, 0x82E51A31, 0x3182E51A, 0x1A3182E5, }, /* x=4C */ - { 0xEB133C89, 0x89EB133C, 0x3C89EB13, 0x133C89EB, }, /* x=4D */ - { 0xF9082B94, 0x94F9082B, 0x2B94F908, 0x082B94F9, }, /* x=4E */ - { 0xF701269F, 0x9FF70126, 0x269FF701, 0x01269FF7, }, /* x=4F */ - { 0x4DE6BD46, 0x464DE6BD, 0xBD464DE6, 0xE6BD464D, }, /* x=50 */ - { 0x43EFB04D, 0x4D43EFB0, 0xB04D43EF, 0xEFB04D43, }, /* x=51 */ - { 0x51F4A750, 0x5051F4A7, 0xA75051F4, 0xF4A75051, }, /* x=52 */ - { 0x5FFDAA5B, 0x5B5FFDAA, 0xAA5B5FFD, 0xFDAA5B5F, }, /* x=53 */ - { 0x75C2896A, 0x6A75C289, 0x896A75C2, 0xC2896A75, }, /* x=54 */ - { 0x7BCB8461, 0x617BCB84, 0x84617BCB, 0xCB84617B, }, /* x=55 */ - { 0x69D0937C, 0x7C69D093, 0x937C69D0, 0xD0937C69, }, /* x=56 */ - { 0x67D99E77, 0x7767D99E, 0x9E7767D9, 0xD99E7767, }, /* x=57 */ - { 0x3DAED51E, 0x1E3DAED5, 0xD51E3DAE, 0xAED51E3D, }, /* x=58 */ - { 0x33A7D815, 0x1533A7D8, 0xD81533A7, 0xA7D81533, }, /* x=59 */ - { 0x21BCCF08, 0x0821BCCF, 0xCF0821BC, 0xBCCF0821, }, /* x=5A */ - { 0x2FB5C203, 0x032FB5C2, 0xC2032FB5, 0xB5C2032F, }, /* x=5B */ - { 0x058AE132, 0x32058AE1, 0xE132058A, 0x8AE13205, }, /* x=5C */ - { 0x0B83EC39, 0x390B83EC, 0xEC390B83, 0x83EC390B, }, /* x=5D */ - { 0x1998FB24, 0x241998FB, 0xFB241998, 0x98FB2419, }, /* x=5E */ - { 0x1791F62F, 0x2F1791F6, 0xF62F1791, 0x91F62F17, }, /* x=5F */ - { 0x764DD68D, 0x8D764DD6, 0xD68D764D, 0x4DD68D76, }, /* x=60 */ - { 0x7844DB86, 0x867844DB, 0xDB867844, 0x44DB8678, }, /* x=61 */ - { 0x6A5FCC9B, 0x9B6A5FCC, 0xCC9B6A5F, 0x5FCC9B6A, }, /* x=62 */ - { 0x6456C190, 0x906456C1, 0xC1906456, 0x56C19064, }, /* x=63 */ - { 0x4E69E2A1, 0xA14E69E2, 0xE2A14E69, 0x69E2A14E, }, /* x=64 */ - { 0x4060EFAA, 0xAA4060EF, 0xEFAA4060, 0x60EFAA40, }, /* x=65 */ - { 0x527BF8B7, 0xB7527BF8, 0xF8B7527B, 0x7BF8B752, }, /* x=66 */ - { 0x5C72F5BC, 0xBC5C72F5, 0xF5BC5C72, 0x72F5BC5C, }, /* x=67 */ - { 0x0605BED5, 0xD50605BE, 0xBED50605, 0x05BED506, }, /* x=68 */ - { 0x080CB3DE, 0xDE080CB3, 0xB3DE080C, 0x0CB3DE08, }, /* x=69 */ - { 0x1A17A4C3, 0xC31A17A4, 0xA4C31A17, 0x17A4C31A, }, /* x=6A */ - { 0x141EA9C8, 0xC8141EA9, 0xA9C8141E, 0x1EA9C814, }, /* x=6B */ - { 0x3E218AF9, 0xF93E218A, 0x8AF93E21, 0x218AF93E, }, /* x=6C */ - { 0x302887F2, 0xF2302887, 0x87F23028, 0x2887F230, }, /* x=6D */ - { 0x223390EF, 0xEF223390, 0x90EF2233, 0x3390EF22, }, /* x=6E */ - { 0x2C3A9DE4, 0xE42C3A9D, 0x9DE42C3A, 0x3A9DE42C, }, /* x=6F */ - { 0x96DD063D, 0x3D96DD06, 0x063D96DD, 0xDD063D96, }, /* x=70 */ - { 0x98D40B36, 0x3698D40B, 0x0B3698D4, 0xD40B3698, }, /* x=71 */ - { 0x8ACF1C2B, 0x2B8ACF1C, 0x1C2B8ACF, 0xCF1C2B8A, }, /* x=72 */ - { 0x84C61120, 0x2084C611, 0x112084C6, 0xC6112084, }, /* x=73 */ - { 0xAEF93211, 0x11AEF932, 0x3211AEF9, 0xF93211AE, }, /* x=74 */ - { 0xA0F03F1A, 0x1AA0F03F, 0x3F1AA0F0, 0xF03F1AA0, }, /* x=75 */ - { 0xB2EB2807, 0x07B2EB28, 0x2807B2EB, 0xEB2807B2, }, /* x=76 */ - { 0xBCE2250C, 0x0CBCE225, 0x250CBCE2, 0xE2250CBC, }, /* x=77 */ - { 0xE6956E65, 0x65E6956E, 0x6E65E695, 0x956E65E6, }, /* x=78 */ - { 0xE89C636E, 0x6EE89C63, 0x636EE89C, 0x9C636EE8, }, /* x=79 */ - { 0xFA877473, 0x73FA8774, 0x7473FA87, 0x877473FA, }, /* x=7A */ - { 0xF48E7978, 0x78F48E79, 0x7978F48E, 0x8E7978F4, }, /* x=7B */ - { 0xDEB15A49, 0x49DEB15A, 0x5A49DEB1, 0xB15A49DE, }, /* x=7C */ - { 0xD0B85742, 0x42D0B857, 0x5742D0B8, 0xB85742D0, }, /* x=7D */ - { 0xC2A3405F, 0x5FC2A340, 0x405FC2A3, 0xA3405FC2, }, /* x=7E */ - { 0xCCAA4D54, 0x54CCAA4D, 0x4D54CCAA, 0xAA4D54CC, }, /* x=7F */ - { 0x41ECDAF7, 0xF741ECDA, 0xDAF741EC, 0xECDAF741, }, /* x=80 */ - { 0x4FE5D7FC, 0xFC4FE5D7, 0xD7FC4FE5, 0xE5D7FC4F, }, /* x=81 */ - { 0x5DFEC0E1, 0xE15DFEC0, 0xC0E15DFE, 0xFEC0E15D, }, /* x=82 */ - { 0x53F7CDEA, 0xEA53F7CD, 0xCDEA53F7, 0xF7CDEA53, }, /* x=83 */ - { 0x79C8EEDB, 0xDB79C8EE, 0xEEDB79C8, 0xC8EEDB79, }, /* x=84 */ - { 0x77C1E3D0, 0xD077C1E3, 0xE3D077C1, 0xC1E3D077, }, /* x=85 */ - { 0x65DAF4CD, 0xCD65DAF4, 0xF4CD65DA, 0xDAF4CD65, }, /* x=86 */ - { 0x6BD3F9C6, 0xC66BD3F9, 0xF9C66BD3, 0xD3F9C66B, }, /* x=87 */ - { 0x31A4B2AF, 0xAF31A4B2, 0xB2AF31A4, 0xA4B2AF31, }, /* x=88 */ - { 0x3FADBFA4, 0xA43FADBF, 0xBFA43FAD, 0xADBFA43F, }, /* x=89 */ - { 0x2DB6A8B9, 0xB92DB6A8, 0xA8B92DB6, 0xB6A8B92D, }, /* x=8A */ - { 0x23BFA5B2, 0xB223BFA5, 0xA5B223BF, 0xBFA5B223, }, /* x=8B */ - { 0x09808683, 0x83098086, 0x86830980, 0x80868309, }, /* x=8C */ - { 0x07898B88, 0x8807898B, 0x8B880789, 0x898B8807, }, /* x=8D */ - { 0x15929C95, 0x9515929C, 0x9C951592, 0x929C9515, }, /* x=8E */ - { 0x1B9B919E, 0x9E1B9B91, 0x919E1B9B, 0x9B919E1B, }, /* x=8F */ - { 0xA17C0A47, 0x47A17C0A, 0x0A47A17C, 0x7C0A47A1, }, /* x=90 */ - { 0xAF75074C, 0x4CAF7507, 0x074CAF75, 0x75074CAF, }, /* x=91 */ - { 0xBD6E1051, 0x51BD6E10, 0x1051BD6E, 0x6E1051BD, }, /* x=92 */ - { 0xB3671D5A, 0x5AB3671D, 0x1D5AB367, 0x671D5AB3, }, /* x=93 */ - { 0x99583E6B, 0x6B99583E, 0x3E6B9958, 0x583E6B99, }, /* x=94 */ - { 0x97513360, 0x60975133, 0x33609751, 0x51336097, }, /* x=95 */ - { 0x854A247D, 0x7D854A24, 0x247D854A, 0x4A247D85, }, /* x=96 */ - { 0x8B432976, 0x768B4329, 0x29768B43, 0x4329768B, }, /* x=97 */ - { 0xD134621F, 0x1FD13462, 0x621FD134, 0x34621FD1, }, /* x=98 */ - { 0xDF3D6F14, 0x14DF3D6F, 0x6F14DF3D, 0x3D6F14DF, }, /* x=99 */ - { 0xCD267809, 0x09CD2678, 0x7809CD26, 0x267809CD, }, /* x=9A */ - { 0xC32F7502, 0x02C32F75, 0x7502C32F, 0x2F7502C3, }, /* x=9B */ - { 0xE9105633, 0x33E91056, 0x5633E910, 0x105633E9, }, /* x=9C */ - { 0xE7195B38, 0x38E7195B, 0x5B38E719, 0x195B38E7, }, /* x=9D */ - { 0xF5024C25, 0x25F5024C, 0x4C25F502, 0x024C25F5, }, /* x=9E */ - { 0xFB0B412E, 0x2EFB0B41, 0x412EFB0B, 0x0B412EFB, }, /* x=9F */ - { 0x9AD7618C, 0x8C9AD761, 0x618C9AD7, 0xD7618C9A, }, /* x=A0 */ - { 0x94DE6C87, 0x8794DE6C, 0x6C8794DE, 0xDE6C8794, }, /* x=A1 */ - { 0x86C57B9A, 0x9A86C57B, 0x7B9A86C5, 0xC57B9A86, }, /* x=A2 */ - { 0x88CC7691, 0x9188CC76, 0x769188CC, 0xCC769188, }, /* x=A3 */ - { 0xA2F355A0, 0xA0A2F355, 0x55A0A2F3, 0xF355A0A2, }, /* x=A4 */ - { 0xACFA58AB, 0xABACFA58, 0x58ABACFA, 0xFA58ABAC, }, /* x=A5 */ - { 0xBEE14FB6, 0xB6BEE14F, 0x4FB6BEE1, 0xE14FB6BE, }, /* x=A6 */ - { 0xB0E842BD, 0xBDB0E842, 0x42BDB0E8, 0xE842BDB0, }, /* x=A7 */ - { 0xEA9F09D4, 0xD4EA9F09, 0x09D4EA9F, 0x9F09D4EA, }, /* x=A8 */ - { 0xE49604DF, 0xDFE49604, 0x04DFE496, 0x9604DFE4, }, /* x=A9 */ - { 0xF68D13C2, 0xC2F68D13, 0x13C2F68D, 0x8D13C2F6, }, /* x=AA */ - { 0xF8841EC9, 0xC9F8841E, 0x1EC9F884, 0x841EC9F8, }, /* x=AB */ - { 0xD2BB3DF8, 0xF8D2BB3D, 0x3DF8D2BB, 0xBB3DF8D2, }, /* x=AC */ - { 0xDCB230F3, 0xF3DCB230, 0x30F3DCB2, 0xB230F3DC, }, /* x=AD */ - { 0xCEA927EE, 0xEECEA927, 0x27EECEA9, 0xA927EECE, }, /* x=AE */ - { 0xC0A02AE5, 0xE5C0A02A, 0x2AE5C0A0, 0xA02AE5C0, }, /* x=AF */ - { 0x7A47B13C, 0x3C7A47B1, 0xB13C7A47, 0x47B13C7A, }, /* x=B0 */ - { 0x744EBC37, 0x37744EBC, 0xBC37744E, 0x4EBC3774, }, /* x=B1 */ - { 0x6655AB2A, 0x2A6655AB, 0xAB2A6655, 0x55AB2A66, }, /* x=B2 */ - { 0x685CA621, 0x21685CA6, 0xA621685C, 0x5CA62168, }, /* x=B3 */ - { 0x42638510, 0x10426385, 0x85104263, 0x63851042, }, /* x=B4 */ - { 0x4C6A881B, 0x1B4C6A88, 0x881B4C6A, 0x6A881B4C, }, /* x=B5 */ - { 0x5E719F06, 0x065E719F, 0x9F065E71, 0x719F065E, }, /* x=B6 */ - { 0x5078920D, 0x0D507892, 0x920D5078, 0x78920D50, }, /* x=B7 */ - { 0x0A0FD964, 0x640A0FD9, 0xD9640A0F, 0x0FD9640A, }, /* x=B8 */ - { 0x0406D46F, 0x6F0406D4, 0xD46F0406, 0x06D46F04, }, /* x=B9 */ - { 0x161DC372, 0x72161DC3, 0xC372161D, 0x1DC37216, }, /* x=BA */ - { 0x1814CE79, 0x791814CE, 0xCE791814, 0x14CE7918, }, /* x=BB */ - { 0x322BED48, 0x48322BED, 0xED48322B, 0x2BED4832, }, /* x=BC */ - { 0x3C22E043, 0x433C22E0, 0xE0433C22, 0x22E0433C, }, /* x=BD */ - { 0x2E39F75E, 0x5E2E39F7, 0xF75E2E39, 0x39F75E2E, }, /* x=BE */ - { 0x2030FA55, 0x552030FA, 0xFA552030, 0x30FA5520, }, /* x=BF */ - { 0xEC9AB701, 0x01EC9AB7, 0xB701EC9A, 0x9AB701EC, }, /* x=C0 */ - { 0xE293BA0A, 0x0AE293BA, 0xBA0AE293, 0x93BA0AE2, }, /* x=C1 */ - { 0xF088AD17, 0x17F088AD, 0xAD17F088, 0x88AD17F0, }, /* x=C2 */ - { 0xFE81A01C, 0x1CFE81A0, 0xA01CFE81, 0x81A01CFE, }, /* x=C3 */ - { 0xD4BE832D, 0x2DD4BE83, 0x832DD4BE, 0xBE832DD4, }, /* x=C4 */ - { 0xDAB78E26, 0x26DAB78E, 0x8E26DAB7, 0xB78E26DA, }, /* x=C5 */ - { 0xC8AC993B, 0x3BC8AC99, 0x993BC8AC, 0xAC993BC8, }, /* x=C6 */ - { 0xC6A59430, 0x30C6A594, 0x9430C6A5, 0xA59430C6, }, /* x=C7 */ - { 0x9CD2DF59, 0x599CD2DF, 0xDF599CD2, 0xD2DF599C, }, /* x=C8 */ - { 0x92DBD252, 0x5292DBD2, 0xD25292DB, 0xDBD25292, }, /* x=C9 */ - { 0x80C0C54F, 0x4F80C0C5, 0xC54F80C0, 0xC0C54F80, }, /* x=CA */ - { 0x8EC9C844, 0x448EC9C8, 0xC8448EC9, 0xC9C8448E, }, /* x=CB */ - { 0xA4F6EB75, 0x75A4F6EB, 0xEB75A4F6, 0xF6EB75A4, }, /* x=CC */ - { 0xAAFFE67E, 0x7EAAFFE6, 0xE67EAAFF, 0xFFE67EAA, }, /* x=CD */ - { 0xB8E4F163, 0x63B8E4F1, 0xF163B8E4, 0xE4F163B8, }, /* x=CE */ - { 0xB6EDFC68, 0x68B6EDFC, 0xFC68B6ED, 0xEDFC68B6, }, /* x=CF */ - { 0x0C0A67B1, 0xB10C0A67, 0x67B10C0A, 0x0A67B10C, }, /* x=D0 */ - { 0x02036ABA, 0xBA02036A, 0x6ABA0203, 0x036ABA02, }, /* x=D1 */ - { 0x10187DA7, 0xA710187D, 0x7DA71018, 0x187DA710, }, /* x=D2 */ - { 0x1E1170AC, 0xAC1E1170, 0x70AC1E11, 0x1170AC1E, }, /* x=D3 */ - { 0x342E539D, 0x9D342E53, 0x539D342E, 0x2E539D34, }, /* x=D4 */ - { 0x3A275E96, 0x963A275E, 0x5E963A27, 0x275E963A, }, /* x=D5 */ - { 0x283C498B, 0x8B283C49, 0x498B283C, 0x3C498B28, }, /* x=D6 */ - { 0x26354480, 0x80263544, 0x44802635, 0x35448026, }, /* x=D7 */ - { 0x7C420FE9, 0xE97C420F, 0x0FE97C42, 0x420FE97C, }, /* x=D8 */ - { 0x724B02E2, 0xE2724B02, 0x02E2724B, 0x4B02E272, }, /* x=D9 */ - { 0x605015FF, 0xFF605015, 0x15FF6050, 0x5015FF60, }, /* x=DA */ - { 0x6E5918F4, 0xF46E5918, 0x18F46E59, 0x5918F46E, }, /* x=DB */ - { 0x44663BC5, 0xC544663B, 0x3BC54466, 0x663BC544, }, /* x=DC */ - { 0x4A6F36CE, 0xCE4A6F36, 0x36CE4A6F, 0x6F36CE4A, }, /* x=DD */ - { 0x587421D3, 0xD3587421, 0x21D35874, 0x7421D358, }, /* x=DE */ - { 0x567D2CD8, 0xD8567D2C, 0x2CD8567D, 0x7D2CD856, }, /* x=DF */ - { 0x37A10C7A, 0x7A37A10C, 0x0C7A37A1, 0xA10C7A37, }, /* x=E0 */ - { 0x39A80171, 0x7139A801, 0x017139A8, 0xA8017139, }, /* x=E1 */ - { 0x2BB3166C, 0x6C2BB316, 0x166C2BB3, 0xB3166C2B, }, /* x=E2 */ - { 0x25BA1B67, 0x6725BA1B, 0x1B6725BA, 0xBA1B6725, }, /* x=E3 */ - { 0x0F853856, 0x560F8538, 0x38560F85, 0x8538560F, }, /* x=E4 */ - { 0x018C355D, 0x5D018C35, 0x355D018C, 0x8C355D01, }, /* x=E5 */ - { 0x13972240, 0x40139722, 0x22401397, 0x97224013, }, /* x=E6 */ - { 0x1D9E2F4B, 0x4B1D9E2F, 0x2F4B1D9E, 0x9E2F4B1D, }, /* x=E7 */ - { 0x47E96422, 0x2247E964, 0x642247E9, 0xE9642247, }, /* x=E8 */ - { 0x49E06929, 0x2949E069, 0x692949E0, 0xE0692949, }, /* x=E9 */ - { 0x5BFB7E34, 0x345BFB7E, 0x7E345BFB, 0xFB7E345B, }, /* x=EA */ - { 0x55F2733F, 0x3F55F273, 0x733F55F2, 0xF2733F55, }, /* x=EB */ - { 0x7FCD500E, 0x0E7FCD50, 0x500E7FCD, 0xCD500E7F, }, /* x=EC */ - { 0x71C45D05, 0x0571C45D, 0x5D0571C4, 0xC45D0571, }, /* x=ED */ - { 0x63DF4A18, 0x1863DF4A, 0x4A1863DF, 0xDF4A1863, }, /* x=EE */ - { 0x6DD64713, 0x136DD647, 0x47136DD6, 0xD647136D, }, /* x=EF */ - { 0xD731DCCA, 0xCAD731DC, 0xDCCAD731, 0x31DCCAD7, }, /* x=F0 */ - { 0xD938D1C1, 0xC1D938D1, 0xD1C1D938, 0x38D1C1D9, }, /* x=F1 */ - { 0xCB23C6DC, 0xDCCB23C6, 0xC6DCCB23, 0x23C6DCCB, }, /* x=F2 */ - { 0xC52ACBD7, 0xD7C52ACB, 0xCBD7C52A, 0x2ACBD7C5, }, /* x=F3 */ - { 0xEF15E8E6, 0xE6EF15E8, 0xE8E6EF15, 0x15E8E6EF, }, /* x=F4 */ - { 0xE11CE5ED, 0xEDE11CE5, 0xE5EDE11C, 0x1CE5EDE1, }, /* x=F5 */ - { 0xF307F2F0, 0xF0F307F2, 0xF2F0F307, 0x07F2F0F3, }, /* x=F6 */ - { 0xFD0EFFFB, 0xFBFD0EFF, 0xFFFBFD0E, 0x0EFFFBFD, }, /* x=F7 */ - { 0xA779B492, 0x92A779B4, 0xB492A779, 0x79B492A7, }, /* x=F8 */ - { 0xA970B999, 0x99A970B9, 0xB999A970, 0x70B999A9, }, /* x=F9 */ - { 0xBB6BAE84, 0x84BB6BAE, 0xAE84BB6B, 0x6BAE84BB, }, /* x=FA */ - { 0xB562A38F, 0x8FB562A3, 0xA38FB562, 0x62A38FB5, }, /* x=FB */ - { 0x9F5D80BE, 0xBE9F5D80, 0x80BE9F5D, 0x5D80BE9F, }, /* x=FC */ - { 0x91548DB5, 0xB591548D, 0x8DB59154, 0x548DB591, }, /* x=FD */ - { 0x834F9AA8, 0xA8834F9A, 0x9AA8834F, 0x4F9AA883, }, /* x=FE */ - { 0x8D4697A3, 0xA38D4697, 0x97A38D46, 0x4697A38D, }, /* x=FF */ -}; - - /* AES_Te0[x] = S [x].[02, 01, 01, 03]; @@ -461,7 +338,8 @@ const uint32_t AES_Te0[256] = { 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, }; -const uint32_t AES_Te1[256] = { + +static const uint32_t AES_Te1[256] = { 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, @@ -527,7 +405,8 @@ const uint32_t AES_Te1[256] = { 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, }; -const uint32_t AES_Te2[256] = { + +static const uint32_t AES_Te2[256] = { 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, @@ -593,8 +472,8 @@ const uint32_t AES_Te2[256] = { 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, }; -const uint32_t AES_Te3[256] = { +static const uint32_t AES_Te3[256] = { 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, @@ -660,7 +539,8 @@ const uint32_t AES_Te3[256] = { 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, }; -const uint32_t AES_Te4[256] = { + +static const uint32_t AES_Te4[256] = { 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, @@ -726,6 +606,7 @@ const uint32_t AES_Te4[256] = { 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, }; + const uint32_t AES_Td0[256] = { 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, @@ -792,7 +673,8 @@ const uint32_t AES_Td0[256] = { 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, }; -const uint32_t AES_Td1[256] = { + +static const uint32_t AES_Td1[256] = { 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, @@ -858,7 +740,8 @@ const uint32_t AES_Td1[256] = { 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, }; -const uint32_t AES_Td2[256] = { + +static const uint32_t AES_Td2[256] = { 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, @@ -925,7 +808,8 @@ const uint32_t AES_Td2[256] = { 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, }; -const uint32_t AES_Td3[256] = { + +static const uint32_t AES_Td3[256] = { 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, @@ -991,7 +875,8 @@ const uint32_t AES_Td3[256] = { 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, }; -const uint32_t AES_Td4[256] = { + +static const uint32_t AES_Td4[256] = { 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, @@ -1057,12 +942,351 @@ const uint32_t AES_Td4[256] = { 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, }; + static const u32 rcon[] = { 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ }; +/* + * Perform MixColumns. + */ +static inline void +aesenc_MC_swap(AESState *r, const AESState *st, bool swap) +{ + int swap_b = swap * 0xf; + int swap_w = swap * 0x3; + bool be = HOST_BIG_ENDIAN ^ swap; + uint32_t t; + + /* Note that AES_mc_rot is encoded for little-endian. */ + t = ( AES_mc_rot[st->b[swap_b ^ 0x0]] ^ + rol32(AES_mc_rot[st->b[swap_b ^ 0x1]], 8) ^ + rol32(AES_mc_rot[st->b[swap_b ^ 0x2]], 16) ^ + rol32(AES_mc_rot[st->b[swap_b ^ 0x3]], 24)); + if (be) { + t = bswap32(t); + } + r->w[swap_w ^ 0] = t; + + t = ( AES_mc_rot[st->b[swap_b ^ 0x4]] ^ + rol32(AES_mc_rot[st->b[swap_b ^ 0x5]], 8) ^ + rol32(AES_mc_rot[st->b[swap_b ^ 0x6]], 16) ^ + rol32(AES_mc_rot[st->b[swap_b ^ 0x7]], 24)); + if (be) { + t = bswap32(t); + } + r->w[swap_w ^ 1] = t; + + t = ( AES_mc_rot[st->b[swap_b ^ 0x8]] ^ + rol32(AES_mc_rot[st->b[swap_b ^ 0x9]], 8) ^ + rol32(AES_mc_rot[st->b[swap_b ^ 0xA]], 16) ^ + rol32(AES_mc_rot[st->b[swap_b ^ 0xB]], 24)); + if (be) { + t = bswap32(t); + } + r->w[swap_w ^ 2] = t; + + t = ( AES_mc_rot[st->b[swap_b ^ 0xC]] ^ + rol32(AES_mc_rot[st->b[swap_b ^ 0xD]], 8) ^ + rol32(AES_mc_rot[st->b[swap_b ^ 0xE]], 16) ^ + rol32(AES_mc_rot[st->b[swap_b ^ 0xF]], 24)); + if (be) { + t = bswap32(t); + } + r->w[swap_w ^ 3] = t; +} + +void aesenc_MC_gen(AESState *r, const AESState *st) +{ + aesenc_MC_swap(r, st, false); +} + +void aesenc_MC_genrev(AESState *r, const AESState *st) +{ + aesenc_MC_swap(r, st, true); +} + +/* + * Perform SubBytes + ShiftRows + AddRoundKey. + */ +static inline void +aesenc_SB_SR_AK_swap(AESState *ret, const AESState *st, + const AESState *rk, bool swap) +{ + const int swap_b = swap ? 15 : 0; + AESState t; + + t.b[swap_b ^ 0x0] = AES_sbox[st->b[swap_b ^ AES_SH(0x0)]]; + t.b[swap_b ^ 0x1] = AES_sbox[st->b[swap_b ^ AES_SH(0x1)]]; + t.b[swap_b ^ 0x2] = AES_sbox[st->b[swap_b ^ AES_SH(0x2)]]; + t.b[swap_b ^ 0x3] = AES_sbox[st->b[swap_b ^ AES_SH(0x3)]]; + t.b[swap_b ^ 0x4] = AES_sbox[st->b[swap_b ^ AES_SH(0x4)]]; + t.b[swap_b ^ 0x5] = AES_sbox[st->b[swap_b ^ AES_SH(0x5)]]; + t.b[swap_b ^ 0x6] = AES_sbox[st->b[swap_b ^ AES_SH(0x6)]]; + t.b[swap_b ^ 0x7] = AES_sbox[st->b[swap_b ^ AES_SH(0x7)]]; + t.b[swap_b ^ 0x8] = AES_sbox[st->b[swap_b ^ AES_SH(0x8)]]; + t.b[swap_b ^ 0x9] = AES_sbox[st->b[swap_b ^ AES_SH(0x9)]]; + t.b[swap_b ^ 0xa] = AES_sbox[st->b[swap_b ^ AES_SH(0xA)]]; + t.b[swap_b ^ 0xb] = AES_sbox[st->b[swap_b ^ AES_SH(0xB)]]; + t.b[swap_b ^ 0xc] = AES_sbox[st->b[swap_b ^ AES_SH(0xC)]]; + t.b[swap_b ^ 0xd] = AES_sbox[st->b[swap_b ^ AES_SH(0xD)]]; + t.b[swap_b ^ 0xe] = AES_sbox[st->b[swap_b ^ AES_SH(0xE)]]; + t.b[swap_b ^ 0xf] = AES_sbox[st->b[swap_b ^ AES_SH(0xF)]]; + + /* + * Perform the AddRoundKey with generic vectors. + * This may be expanded to either host integer or host vector code. + * The key and output endianness match, so no bswap required. + */ + ret->v = t.v ^ rk->v; +} + +void aesenc_SB_SR_AK_gen(AESState *r, const AESState *s, const AESState *k) +{ + aesenc_SB_SR_AK_swap(r, s, k, false); +} + +void aesenc_SB_SR_AK_genrev(AESState *r, const AESState *s, const AESState *k) +{ + aesenc_SB_SR_AK_swap(r, s, k, true); +} + +/* + * Perform SubBytes + ShiftRows + MixColumns + AddRoundKey. + */ +static inline void +aesenc_SB_SR_MC_AK_swap(AESState *r, const AESState *st, + const AESState *rk, bool swap) +{ + int swap_b = swap * 0xf; + int swap_w = swap * 0x3; + bool be = HOST_BIG_ENDIAN ^ swap; + uint32_t w0, w1, w2, w3; + + w0 = (AES_Te0[st->b[swap_b ^ AES_SH(0x0)]] ^ + AES_Te1[st->b[swap_b ^ AES_SH(0x1)]] ^ + AES_Te2[st->b[swap_b ^ AES_SH(0x2)]] ^ + AES_Te3[st->b[swap_b ^ AES_SH(0x3)]]); + + w1 = (AES_Te0[st->b[swap_b ^ AES_SH(0x4)]] ^ + AES_Te1[st->b[swap_b ^ AES_SH(0x5)]] ^ + AES_Te2[st->b[swap_b ^ AES_SH(0x6)]] ^ + AES_Te3[st->b[swap_b ^ AES_SH(0x7)]]); + + w2 = (AES_Te0[st->b[swap_b ^ AES_SH(0x8)]] ^ + AES_Te1[st->b[swap_b ^ AES_SH(0x9)]] ^ + AES_Te2[st->b[swap_b ^ AES_SH(0xA)]] ^ + AES_Te3[st->b[swap_b ^ AES_SH(0xB)]]); + + w3 = (AES_Te0[st->b[swap_b ^ AES_SH(0xC)]] ^ + AES_Te1[st->b[swap_b ^ AES_SH(0xD)]] ^ + AES_Te2[st->b[swap_b ^ AES_SH(0xE)]] ^ + AES_Te3[st->b[swap_b ^ AES_SH(0xF)]]); + + /* Note that AES_TeX is encoded for big-endian. */ + if (!be) { + w0 = bswap32(w0); + w1 = bswap32(w1); + w2 = bswap32(w2); + w3 = bswap32(w3); + } + + r->w[swap_w ^ 0] = rk->w[swap_w ^ 0] ^ w0; + r->w[swap_w ^ 1] = rk->w[swap_w ^ 1] ^ w1; + r->w[swap_w ^ 2] = rk->w[swap_w ^ 2] ^ w2; + r->w[swap_w ^ 3] = rk->w[swap_w ^ 3] ^ w3; +} + +void aesenc_SB_SR_MC_AK_gen(AESState *r, const AESState *st, + const AESState *rk) +{ + aesenc_SB_SR_MC_AK_swap(r, st, rk, false); +} + +void aesenc_SB_SR_MC_AK_genrev(AESState *r, const AESState *st, + const AESState *rk) +{ + aesenc_SB_SR_MC_AK_swap(r, st, rk, true); +} + +/* + * Perform InvMixColumns. + */ +static inline void +aesdec_IMC_swap(AESState *r, const AESState *st, bool swap) +{ + int swap_b = swap * 0xf; + int swap_w = swap * 0x3; + bool be = HOST_BIG_ENDIAN ^ swap; + uint32_t t; + + /* Note that AES_imc_rot is encoded for little-endian. */ + t = ( AES_imc_rot[st->b[swap_b ^ 0x0]] ^ + rol32(AES_imc_rot[st->b[swap_b ^ 0x1]], 8) ^ + rol32(AES_imc_rot[st->b[swap_b ^ 0x2]], 16) ^ + rol32(AES_imc_rot[st->b[swap_b ^ 0x3]], 24)); + if (be) { + t = bswap32(t); + } + r->w[swap_w ^ 0] = t; + + t = ( AES_imc_rot[st->b[swap_b ^ 0x4]] ^ + rol32(AES_imc_rot[st->b[swap_b ^ 0x5]], 8) ^ + rol32(AES_imc_rot[st->b[swap_b ^ 0x6]], 16) ^ + rol32(AES_imc_rot[st->b[swap_b ^ 0x7]], 24)); + if (be) { + t = bswap32(t); + } + r->w[swap_w ^ 1] = t; + + t = ( AES_imc_rot[st->b[swap_b ^ 0x8]] ^ + rol32(AES_imc_rot[st->b[swap_b ^ 0x9]], 8) ^ + rol32(AES_imc_rot[st->b[swap_b ^ 0xA]], 16) ^ + rol32(AES_imc_rot[st->b[swap_b ^ 0xB]], 24)); + if (be) { + t = bswap32(t); + } + r->w[swap_w ^ 2] = t; + + t = ( AES_imc_rot[st->b[swap_b ^ 0xC]] ^ + rol32(AES_imc_rot[st->b[swap_b ^ 0xD]], 8) ^ + rol32(AES_imc_rot[st->b[swap_b ^ 0xE]], 16) ^ + rol32(AES_imc_rot[st->b[swap_b ^ 0xF]], 24)); + if (be) { + t = bswap32(t); + } + r->w[swap_w ^ 3] = t; +} + +void aesdec_IMC_gen(AESState *r, const AESState *st) +{ + aesdec_IMC_swap(r, st, false); +} + +void aesdec_IMC_genrev(AESState *r, const AESState *st) +{ + aesdec_IMC_swap(r, st, true); +} + +/* + * Perform InvSubBytes + InvShiftRows + AddRoundKey. + */ +static inline void +aesdec_ISB_ISR_AK_swap(AESState *ret, const AESState *st, + const AESState *rk, bool swap) +{ + const int swap_b = swap ? 15 : 0; + AESState t; + + t.b[swap_b ^ 0x0] = AES_isbox[st->b[swap_b ^ AES_ISH(0x0)]]; + t.b[swap_b ^ 0x1] = AES_isbox[st->b[swap_b ^ AES_ISH(0x1)]]; + t.b[swap_b ^ 0x2] = AES_isbox[st->b[swap_b ^ AES_ISH(0x2)]]; + t.b[swap_b ^ 0x3] = AES_isbox[st->b[swap_b ^ AES_ISH(0x3)]]; + t.b[swap_b ^ 0x4] = AES_isbox[st->b[swap_b ^ AES_ISH(0x4)]]; + t.b[swap_b ^ 0x5] = AES_isbox[st->b[swap_b ^ AES_ISH(0x5)]]; + t.b[swap_b ^ 0x6] = AES_isbox[st->b[swap_b ^ AES_ISH(0x6)]]; + t.b[swap_b ^ 0x7] = AES_isbox[st->b[swap_b ^ AES_ISH(0x7)]]; + t.b[swap_b ^ 0x8] = AES_isbox[st->b[swap_b ^ AES_ISH(0x8)]]; + t.b[swap_b ^ 0x9] = AES_isbox[st->b[swap_b ^ AES_ISH(0x9)]]; + t.b[swap_b ^ 0xa] = AES_isbox[st->b[swap_b ^ AES_ISH(0xA)]]; + t.b[swap_b ^ 0xb] = AES_isbox[st->b[swap_b ^ AES_ISH(0xB)]]; + t.b[swap_b ^ 0xc] = AES_isbox[st->b[swap_b ^ AES_ISH(0xC)]]; + t.b[swap_b ^ 0xd] = AES_isbox[st->b[swap_b ^ AES_ISH(0xD)]]; + t.b[swap_b ^ 0xe] = AES_isbox[st->b[swap_b ^ AES_ISH(0xE)]]; + t.b[swap_b ^ 0xf] = AES_isbox[st->b[swap_b ^ AES_ISH(0xF)]]; + + /* + * Perform the AddRoundKey with generic vectors. + * This may be expanded to either host integer or host vector code. + * The key and output endianness match, so no bswap required. + */ + ret->v = t.v ^ rk->v; +} + +void aesdec_ISB_ISR_AK_gen(AESState *r, const AESState *s, const AESState *k) +{ + aesdec_ISB_ISR_AK_swap(r, s, k, false); +} + +void aesdec_ISB_ISR_AK_genrev(AESState *r, const AESState *s, const AESState *k) +{ + aesdec_ISB_ISR_AK_swap(r, s, k, true); +} + +/* + * Perform InvSubBytes + InvShiftRows + InvMixColumns + AddRoundKey. + */ +static inline void +aesdec_ISB_ISR_IMC_AK_swap(AESState *r, const AESState *st, + const AESState *rk, bool swap) +{ + int swap_b = swap * 0xf; + int swap_w = swap * 0x3; + bool be = HOST_BIG_ENDIAN ^ swap; + uint32_t w0, w1, w2, w3; + + w0 = (AES_Td0[st->b[swap_b ^ AES_ISH(0x0)]] ^ + AES_Td1[st->b[swap_b ^ AES_ISH(0x1)]] ^ + AES_Td2[st->b[swap_b ^ AES_ISH(0x2)]] ^ + AES_Td3[st->b[swap_b ^ AES_ISH(0x3)]]); + + w1 = (AES_Td0[st->b[swap_b ^ AES_ISH(0x4)]] ^ + AES_Td1[st->b[swap_b ^ AES_ISH(0x5)]] ^ + AES_Td2[st->b[swap_b ^ AES_ISH(0x6)]] ^ + AES_Td3[st->b[swap_b ^ AES_ISH(0x7)]]); + + w2 = (AES_Td0[st->b[swap_b ^ AES_ISH(0x8)]] ^ + AES_Td1[st->b[swap_b ^ AES_ISH(0x9)]] ^ + AES_Td2[st->b[swap_b ^ AES_ISH(0xA)]] ^ + AES_Td3[st->b[swap_b ^ AES_ISH(0xB)]]); + + w3 = (AES_Td0[st->b[swap_b ^ AES_ISH(0xC)]] ^ + AES_Td1[st->b[swap_b ^ AES_ISH(0xD)]] ^ + AES_Td2[st->b[swap_b ^ AES_ISH(0xE)]] ^ + AES_Td3[st->b[swap_b ^ AES_ISH(0xF)]]); + + /* Note that AES_TdX is encoded for big-endian. */ + if (!be) { + w0 = bswap32(w0); + w1 = bswap32(w1); + w2 = bswap32(w2); + w3 = bswap32(w3); + } + + r->w[swap_w ^ 0] = rk->w[swap_w ^ 0] ^ w0; + r->w[swap_w ^ 1] = rk->w[swap_w ^ 1] ^ w1; + r->w[swap_w ^ 2] = rk->w[swap_w ^ 2] ^ w2; + r->w[swap_w ^ 3] = rk->w[swap_w ^ 3] ^ w3; +} + +void aesdec_ISB_ISR_IMC_AK_gen(AESState *r, const AESState *st, + const AESState *rk) +{ + aesdec_ISB_ISR_IMC_AK_swap(r, st, rk, false); +} + +void aesdec_ISB_ISR_IMC_AK_genrev(AESState *r, const AESState *st, + const AESState *rk) +{ + aesdec_ISB_ISR_IMC_AK_swap(r, st, rk, true); +} + +void aesdec_ISB_ISR_AK_IMC_gen(AESState *ret, const AESState *st, + const AESState *rk) +{ + aesdec_ISB_ISR_AK_gen(ret, st, rk); + aesdec_IMC_gen(ret, ret); +} + +void aesdec_ISB_ISR_AK_IMC_genrev(AESState *ret, const AESState *st, + const AESState *rk) +{ + aesdec_ISB_ISR_AK_genrev(ret, st, rk); + aesdec_IMC_genrev(ret, ret); +} + /** * Expand the cipher key into the encryption key schedule. */ diff --git a/crypto/afalg.c b/crypto/afalg.c index 10046bb0ae..52a491dbb5 100644 --- a/crypto/afalg.c +++ b/crypto/afalg.c @@ -59,7 +59,7 @@ qcrypto_afalg_socket_bind(const char *type, const char *name, if (bind(sbind, (const struct sockaddr *)&salg, sizeof(salg)) != 0) { error_setg_errno(errp, errno, "Failed to bind socket"); - closesocket(sbind); + close(sbind); return -1; } @@ -73,7 +73,7 @@ qcrypto_afalg_comm_alloc(const char *type, const char *name, QCryptoAFAlg *afalg; afalg = g_new0(QCryptoAFAlg, 1); - /* initilize crypto API socket */ + /* initialize crypto API socket */ afalg->opfd = -1; afalg->tfmfd = qcrypto_afalg_socket_bind(type, name, errp); if (afalg->tfmfd == -1) { @@ -105,11 +105,11 @@ void qcrypto_afalg_comm_free(QCryptoAFAlg *afalg) } if (afalg->tfmfd != -1) { - closesocket(afalg->tfmfd); + close(afalg->tfmfd); } if (afalg->opfd != -1) { - closesocket(afalg->opfd); + close(afalg->opfd); } g_free(afalg); diff --git a/crypto/akcipher.c b/crypto/akcipher.c index ad88379c1e..e4bbc6e5f1 100644 --- a/crypto/akcipher.c +++ b/crypto/akcipher.c @@ -22,6 +22,8 @@ #include "qemu/osdep.h" #include "crypto/akcipher.h" #include "akcipherpriv.h" +#include "der.h" +#include "rsakey.h" #if defined(CONFIG_GCRYPT) #include "akcipher-gcrypt.c.inc" @@ -106,3 +108,19 @@ void qcrypto_akcipher_free(QCryptoAkCipher *akcipher) drv->free(akcipher); } + +int qcrypto_akcipher_export_p8info(const QCryptoAkCipherOptions *opts, + uint8_t *key, size_t keylen, + uint8_t **dst, size_t *dst_len, + Error **errp) +{ + switch (opts->alg) { + case QCRYPTO_AKCIPHER_ALG_RSA: + qcrypto_akcipher_rsakey_export_p8info(key, keylen, dst, dst_len); + return 0; + + default: + error_setg(errp, "Unsupported algorithm: %u", opts->alg); + return -1; + } +} diff --git a/crypto/block-luks-priv.h b/crypto/block-luks-priv.h new file mode 100644 index 0000000000..8fc967afcb --- /dev/null +++ b/crypto/block-luks-priv.h @@ -0,0 +1,141 @@ +/* + * QEMU Crypto block device encryption LUKS format + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "qapi/error.h" +#include "qemu/bswap.h" + +#include "block-luks.h" + +#include "crypto/hash.h" +#include "crypto/afsplit.h" +#include "crypto/pbkdf.h" +#include "crypto/secret.h" +#include "crypto/random.h" +#include "qemu/uuid.h" + +#include "qemu/bitmap.h" + +/* + * Reference for the LUKS format implemented here is + * + * docs/on-disk-format.pdf + * + * in 'cryptsetup' package source code + * + * This file implements the 1.2.1 specification, dated + * Oct 16, 2011. + */ + +typedef struct QCryptoBlockLUKSHeader QCryptoBlockLUKSHeader; +typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot; + + +/* The following constants are all defined by the LUKS spec */ +#define QCRYPTO_BLOCK_LUKS_VERSION 1 + +#define QCRYPTO_BLOCK_LUKS_MAGIC_LEN 6 +#define QCRYPTO_BLOCK_LUKS_CIPHER_NAME_LEN 32 +#define QCRYPTO_BLOCK_LUKS_CIPHER_MODE_LEN 32 +#define QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN 32 +#define QCRYPTO_BLOCK_LUKS_DIGEST_LEN 20 +#define QCRYPTO_BLOCK_LUKS_SALT_LEN 32 +#define QCRYPTO_BLOCK_LUKS_UUID_LEN 40 +#define QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS 8 +#define QCRYPTO_BLOCK_LUKS_STRIPES 4000 +#define QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS 1000 +#define QCRYPTO_BLOCK_LUKS_MIN_MASTER_KEY_ITERS 1000 +#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET 4096 + +#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED 0x0000DEAD +#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED 0x00AC71F3 + +#define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL + +#define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS 2000 +#define QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS 40 + +static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = { + 'L', 'U', 'K', 'S', 0xBA, 0xBE +}; + +/* + * This struct is written to disk in big-endian format, + * but operated upon in native-endian format. + */ +struct QCryptoBlockLUKSKeySlot { + /* state of keyslot, enabled/disable */ + uint32_t active; + /* iterations for PBKDF2 */ + uint32_t iterations; + /* salt for PBKDF2 */ + uint8_t salt[QCRYPTO_BLOCK_LUKS_SALT_LEN]; + /* start sector of key material */ + uint32_t key_offset_sector; + /* number of anti-forensic stripes */ + uint32_t stripes; +}; + +/* + * This struct is written to disk in big-endian format, + * but operated upon in native-endian format. + */ +struct QCryptoBlockLUKSHeader { + /* 'L', 'U', 'K', 'S', '0xBA', '0xBE' */ + char magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN]; + + /* LUKS version, currently 1 */ + uint16_t version; + + /* cipher name specification (aes, etc) */ + char cipher_name[QCRYPTO_BLOCK_LUKS_CIPHER_NAME_LEN]; + + /* cipher mode specification (cbc-plain, xts-essiv:sha256, etc) */ + char cipher_mode[QCRYPTO_BLOCK_LUKS_CIPHER_MODE_LEN]; + + /* hash specification (sha256, etc) */ + char hash_spec[QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN]; + + /* start offset of the volume data (in 512 byte sectors) */ + uint32_t payload_offset_sector; + + /* Number of key bytes */ + uint32_t master_key_len; + + /* master key checksum after PBKDF2 */ + uint8_t master_key_digest[QCRYPTO_BLOCK_LUKS_DIGEST_LEN]; + + /* salt for master key PBKDF2 */ + uint8_t master_key_salt[QCRYPTO_BLOCK_LUKS_SALT_LEN]; + + /* iterations for master key PBKDF2 */ + uint32_t master_key_iterations; + + /* UUID of the partition in standard ASCII representation */ + uint8_t uuid[QCRYPTO_BLOCK_LUKS_UUID_LEN]; + + /* key slots */ + QCryptoBlockLUKSKeySlot key_slots[QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS]; +}; + + +void +qcrypto_block_luks_to_disk_endian(QCryptoBlockLUKSHeader *hdr); +void +qcrypto_block_luks_from_disk_endian(QCryptoBlockLUKSHeader *hdr); diff --git a/crypto/block-luks.c b/crypto/block-luks.c index fe8f04ffb2..3ee928fb5a 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -23,6 +23,7 @@ #include "qemu/bswap.h" #include "block-luks.h" +#include "block-luks-priv.h" #include "crypto/hash.h" #include "crypto/afsplit.h" @@ -31,7 +32,6 @@ #include "crypto/random.h" #include "qemu/uuid.h" -#include "qemu/coroutine.h" #include "qemu/bitmap.h" /* @@ -46,37 +46,6 @@ */ typedef struct QCryptoBlockLUKS QCryptoBlockLUKS; -typedef struct QCryptoBlockLUKSHeader QCryptoBlockLUKSHeader; -typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot; - - -/* The following constants are all defined by the LUKS spec */ -#define QCRYPTO_BLOCK_LUKS_VERSION 1 - -#define QCRYPTO_BLOCK_LUKS_MAGIC_LEN 6 -#define QCRYPTO_BLOCK_LUKS_CIPHER_NAME_LEN 32 -#define QCRYPTO_BLOCK_LUKS_CIPHER_MODE_LEN 32 -#define QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN 32 -#define QCRYPTO_BLOCK_LUKS_DIGEST_LEN 20 -#define QCRYPTO_BLOCK_LUKS_SALT_LEN 32 -#define QCRYPTO_BLOCK_LUKS_UUID_LEN 40 -#define QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS 8 -#define QCRYPTO_BLOCK_LUKS_STRIPES 4000 -#define QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS 1000 -#define QCRYPTO_BLOCK_LUKS_MIN_MASTER_KEY_ITERS 1000 -#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET 4096 - -#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED 0x0000DEAD -#define QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED 0x00AC71F3 - -#define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL - -#define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS 2000 -#define QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS 40 - -static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = { - 'L', 'U', 'K', 'S', 0xBA, 0xBE -}; typedef struct QCryptoBlockLUKSNameMap QCryptoBlockLUKSNameMap; struct QCryptoBlockLUKSNameMap { @@ -126,77 +95,26 @@ qcrypto_block_luks_cipher_size_map_twofish[] = { { 0, 0 }, }; +#ifdef CONFIG_CRYPTO_SM4 +static const QCryptoBlockLUKSCipherSizeMap +qcrypto_block_luks_cipher_size_map_sm4[] = { + { 16, QCRYPTO_CIPHER_ALG_SM4}, + { 0, 0 }, +}; +#endif + static const QCryptoBlockLUKSCipherNameMap qcrypto_block_luks_cipher_name_map[] = { { "aes", qcrypto_block_luks_cipher_size_map_aes }, { "cast5", qcrypto_block_luks_cipher_size_map_cast5 }, { "serpent", qcrypto_block_luks_cipher_size_map_serpent }, { "twofish", qcrypto_block_luks_cipher_size_map_twofish }, -}; - - -/* - * This struct is written to disk in big-endian format, - * but operated upon in native-endian format. - */ -struct QCryptoBlockLUKSKeySlot { - /* state of keyslot, enabled/disable */ - uint32_t active; - /* iterations for PBKDF2 */ - uint32_t iterations; - /* salt for PBKDF2 */ - uint8_t salt[QCRYPTO_BLOCK_LUKS_SALT_LEN]; - /* start sector of key material */ - uint32_t key_offset_sector; - /* number of anti-forensic stripes */ - uint32_t stripes; +#ifdef CONFIG_CRYPTO_SM4 + { "sm4", qcrypto_block_luks_cipher_size_map_sm4}, +#endif }; QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSKeySlot) != 48); - - -/* - * This struct is written to disk in big-endian format, - * but operated upon in native-endian format. - */ -struct QCryptoBlockLUKSHeader { - /* 'L', 'U', 'K', 'S', '0xBA', '0xBE' */ - char magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN]; - - /* LUKS version, currently 1 */ - uint16_t version; - - /* cipher name specification (aes, etc) */ - char cipher_name[QCRYPTO_BLOCK_LUKS_CIPHER_NAME_LEN]; - - /* cipher mode specification (cbc-plain, xts-essiv:sha256, etc) */ - char cipher_mode[QCRYPTO_BLOCK_LUKS_CIPHER_MODE_LEN]; - - /* hash specification (sha256, etc) */ - char hash_spec[QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN]; - - /* start offset of the volume data (in 512 byte sectors) */ - uint32_t payload_offset_sector; - - /* Number of key bytes */ - uint32_t master_key_len; - - /* master key checksum after PBKDF2 */ - uint8_t master_key_digest[QCRYPTO_BLOCK_LUKS_DIGEST_LEN]; - - /* salt for master key PBKDF2 */ - uint8_t master_key_salt[QCRYPTO_BLOCK_LUKS_SALT_LEN]; - - /* iterations for master key PBKDF2 */ - uint32_t master_key_iterations; - - /* UUID of the partition in standard ASCII representation */ - uint8_t uuid[QCRYPTO_BLOCK_LUKS_UUID_LEN]; - - /* key slots */ - QCryptoBlockLUKSKeySlot key_slots[QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS]; -}; - QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSHeader) != 592); @@ -254,7 +172,7 @@ static int qcrypto_block_luks_cipher_name_lookup(const char *name, } } - error_setg(errp, "Algorithm %s with key size %d bytes not supported", + error_setg(errp, "Algorithm '%s' with key size %d bytes not supported", name, key_bytes); return 0; } @@ -290,7 +208,7 @@ static int qcrypto_block_luks_name_lookup(const char *name, int ret = qapi_enum_parse(map, name, -1, NULL); if (ret < 0) { - error_setg(errp, "%s %s not supported", type, name); + error_setg(errp, "%s '%s' not supported", type, name); return 0; } return ret; @@ -337,7 +255,7 @@ qcrypto_block_luks_has_format(const uint8_t *buf, * * When calculating ESSIV IVs, the cipher length used by ESSIV * may be different from the cipher length used for the block - * encryption, becauses dm-crypt uses the hash digest length + * encryption, because dm-crypt uses the hash digest length * as the key size. ie, if you have AES 128 as the block cipher * and SHA 256 as ESSIV hash, then ESSIV will use AES 256 as * the cipher since that gets a key length matching the digest @@ -440,8 +358,53 @@ qcrypto_block_luks_splitkeylen_sectors(const QCryptoBlockLUKS *luks, return ROUND_UP(splitkeylen_sectors, header_sectors); } + +void +qcrypto_block_luks_to_disk_endian(QCryptoBlockLUKSHeader *hdr) +{ + size_t i; + + /* + * Everything on disk uses Big Endian (tm), so flip header fields + * before writing them + */ + cpu_to_be16s(&hdr->version); + cpu_to_be32s(&hdr->payload_offset_sector); + cpu_to_be32s(&hdr->master_key_len); + cpu_to_be32s(&hdr->master_key_iterations); + + for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { + cpu_to_be32s(&hdr->key_slots[i].active); + cpu_to_be32s(&hdr->key_slots[i].iterations); + cpu_to_be32s(&hdr->key_slots[i].key_offset_sector); + cpu_to_be32s(&hdr->key_slots[i].stripes); + } +} + +void +qcrypto_block_luks_from_disk_endian(QCryptoBlockLUKSHeader *hdr) +{ + size_t i; + + /* + * The header is always stored in big-endian format, so + * convert everything to native + */ + be16_to_cpus(&hdr->version); + be32_to_cpus(&hdr->payload_offset_sector); + be32_to_cpus(&hdr->master_key_len); + be32_to_cpus(&hdr->master_key_iterations); + + for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { + be32_to_cpus(&hdr->key_slots[i].active); + be32_to_cpus(&hdr->key_slots[i].iterations); + be32_to_cpus(&hdr->key_slots[i].key_offset_sector); + be32_to_cpus(&hdr->key_slots[i].stripes); + } +} + /* - * Stores the main LUKS header, taking care of endianess + * Stores the main LUKS header, taking care of endianness */ static int qcrypto_block_luks_store_header(QCryptoBlock *block, @@ -451,28 +414,13 @@ qcrypto_block_luks_store_header(QCryptoBlock *block, { const QCryptoBlockLUKS *luks = block->opaque; Error *local_err = NULL; - size_t i; g_autofree QCryptoBlockLUKSHeader *hdr_copy = NULL; /* Create a copy of the header */ hdr_copy = g_new0(QCryptoBlockLUKSHeader, 1); memcpy(hdr_copy, &luks->header, sizeof(QCryptoBlockLUKSHeader)); - /* - * Everything on disk uses Big Endian (tm), so flip header fields - * before writing them - */ - cpu_to_be16s(&hdr_copy->version); - cpu_to_be32s(&hdr_copy->payload_offset_sector); - cpu_to_be32s(&hdr_copy->master_key_len); - cpu_to_be32s(&hdr_copy->master_key_iterations); - - for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { - cpu_to_be32s(&hdr_copy->key_slots[i].active); - cpu_to_be32s(&hdr_copy->key_slots[i].iterations); - cpu_to_be32s(&hdr_copy->key_slots[i].key_offset_sector); - cpu_to_be32s(&hdr_copy->key_slots[i].stripes); - } + qcrypto_block_luks_to_disk_endian(hdr_copy); /* Write out the partition header and key slot headers */ writefunc(block, 0, (const uint8_t *)hdr_copy, sizeof(*hdr_copy), @@ -486,7 +434,7 @@ qcrypto_block_luks_store_header(QCryptoBlock *block, } /* - * Loads the main LUKS header,and byteswaps it to native endianess + * Loads the main LUKS header, and byteswaps it to native endianness * And run basic sanity checks on it */ static int @@ -495,8 +443,7 @@ qcrypto_block_luks_load_header(QCryptoBlock *block, void *opaque, Error **errp) { - ssize_t rv; - size_t i; + int rv; QCryptoBlockLUKS *luks = block->opaque; /* @@ -512,21 +459,7 @@ qcrypto_block_luks_load_header(QCryptoBlock *block, return rv; } - /* - * The header is always stored in big-endian format, so - * convert everything to native - */ - be16_to_cpus(&luks->header.version); - be32_to_cpus(&luks->header.payload_offset_sector); - be32_to_cpus(&luks->header.master_key_len); - be32_to_cpus(&luks->header.master_key_iterations); - - for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { - be32_to_cpus(&luks->header.key_slots[i].active); - be32_to_cpus(&luks->header.key_slots[i].iterations); - be32_to_cpus(&luks->header.key_slots[i].key_offset_sector); - be32_to_cpus(&luks->header.key_slots[i].stripes); - } + qcrypto_block_luks_from_disk_endian(&luks->header); return 0; } @@ -535,12 +468,15 @@ qcrypto_block_luks_load_header(QCryptoBlock *block, * Does basic sanity checks on the LUKS header */ static int -qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp) +qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, + unsigned int flags, + Error **errp) { size_t i, j; unsigned int header_sectors = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET / QCRYPTO_BLOCK_LUKS_SECTOR_SIZE; + bool detached = flags & QCRYPTO_BLOCK_OPEN_DETACHED; if (memcmp(luks->header.magic, qcrypto_block_luks_magic, QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) { @@ -554,6 +490,36 @@ qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp) return -1; } + if (!memchr(luks->header.cipher_name, '\0', + sizeof(luks->header.cipher_name))) { + error_setg(errp, "LUKS header cipher name is not NUL terminated"); + return -1; + } + + if (!memchr(luks->header.cipher_mode, '\0', + sizeof(luks->header.cipher_mode))) { + error_setg(errp, "LUKS header cipher mode is not NUL terminated"); + return -1; + } + + if (!memchr(luks->header.hash_spec, '\0', + sizeof(luks->header.hash_spec))) { + error_setg(errp, "LUKS header hash spec is not NUL terminated"); + return -1; + } + + if (!detached && luks->header.payload_offset_sector < + DIV_ROUND_UP(QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET, + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) { + error_setg(errp, "LUKS payload is overlapping with the header"); + return -1; + } + + if (luks->header.master_key_iterations == 0) { + error_setg(errp, "LUKS key iteration count is zero"); + return -1; + } + /* Check all keyslots for corruption */ for (i = 0 ; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; i++) { @@ -564,8 +530,9 @@ qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp) header_sectors, slot1->stripes); - if (slot1->stripes == 0) { - error_setg(errp, "Keyslot %zu is corrupted (stripes == 0)", i); + if (slot1->stripes != QCRYPTO_BLOCK_LUKS_STRIPES) { + error_setg(errp, "Keyslot %zu is corrupted (stripes %d != %d)", + i, slot1->stripes, QCRYPTO_BLOCK_LUKS_STRIPES); return -1; } @@ -576,7 +543,21 @@ qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp) return -1; } - if (start1 + len1 > luks->header.payload_offset_sector) { + if (slot1->active == QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED && + slot1->iterations == 0) { + error_setg(errp, "Keyslot %zu iteration count is zero", i); + return -1; + } + + if (start1 < DIV_ROUND_UP(QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET, + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) { + error_setg(errp, + "Keyslot %zu is overlapping with the LUKS header", + i); + return -1; + } + + if (!detached && start1 + len1 > luks->header.payload_offset_sector) { error_setg(errp, "Keyslot %zu is overlapping with the encrypted payload", i); @@ -624,7 +605,7 @@ qcrypto_block_luks_parse_header(QCryptoBlockLUKS *luks, Error **errp) */ ivgen_name = strchr(cipher_mode, '-'); if (!ivgen_name) { - error_setg(errp, "Unexpected cipher mode string format %s", + error_setg(errp, "Unexpected cipher mode string format '%s'", luks->header.cipher_mode); return -1; } @@ -739,14 +720,14 @@ qcrypto_block_luks_store_key(QCryptoBlock *block, assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS); slot = &luks->header.key_slots[slot_idx]; + splitkeylen = luks->header.master_key_len * slot->stripes; + if (qcrypto_random_bytes(slot->salt, QCRYPTO_BLOCK_LUKS_SALT_LEN, errp) < 0) { goto cleanup; } - splitkeylen = luks->header.master_key_len * slot->stripes; - /* * Determine how many iterations are required to * hash the user password while consuming 1 second of compute @@ -856,7 +837,7 @@ qcrypto_block_luks_store_key(QCryptoBlock *block, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, splitkey, splitkeylen, opaque, - errp) != splitkeylen) { + errp) < 0) { goto cleanup; } @@ -903,7 +884,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block, g_autofree uint8_t *splitkey = NULL; size_t splitkeylen; g_autofree uint8_t *possiblekey = NULL; - ssize_t rv; + int rv; g_autoptr(QCryptoCipher) cipher = NULL; uint8_t keydigest[QCRYPTO_BLOCK_LUKS_DIGEST_LEN]; g_autoptr(QCryptoIVGen) ivgen = NULL; @@ -1193,7 +1174,7 @@ qcrypto_block_luks_erase_key(QCryptoBlock *block, garbagesplitkey, splitkeylen, opaque, - &local_err) != splitkeylen) { + &local_err) < 0) { error_propagate(errp, local_err); return -1; } @@ -1236,7 +1217,7 @@ qcrypto_block_luks_open(QCryptoBlock *block, goto fail; } - if (qcrypto_block_luks_check_header(luks, errp) < 0) { + if (qcrypto_block_luks_check_header(luks, flags, errp) < 0) { goto fail; } @@ -1290,6 +1271,7 @@ qcrypto_block_luks_open(QCryptoBlock *block, block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE; block->payload_offset = luks->header.payload_offset_sector * block->sector_size; + block->detached_header = (block->payload_offset == 0) ? true : false; return 0; @@ -1334,6 +1316,7 @@ qcrypto_block_luks_create(QCryptoBlock *block, const char *hash_alg; g_autofree char *cipher_mode_spec = NULL; uint64_t iters; + uint64_t detached_header_size; memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts)); if (!luks_opts.has_iter_time) { @@ -1562,19 +1545,32 @@ qcrypto_block_luks_create(QCryptoBlock *block, slot->stripes = QCRYPTO_BLOCK_LUKS_STRIPES; } - /* The total size of the LUKS headers is the partition header + key - * slot headers, rounded up to the nearest sector, combined with - * the size of each master key material region, also rounded up - * to the nearest sector */ - luks->header.payload_offset_sector = header_sectors + - QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS * split_key_sectors; + if (block->detached_header) { + /* + * For a detached LUKS header image, set the payload_offset_sector + * to 0 to specify the starting point for read/write + */ + luks->header.payload_offset_sector = 0; + } else { + /* + * The total size of the LUKS headers is the partition header + key + * slot headers, rounded up to the nearest sector, combined with + * the size of each master key material region, also rounded up + * to the nearest sector + */ + luks->header.payload_offset_sector = header_sectors + + QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS * split_key_sectors; + } block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE; block->payload_offset = luks->header.payload_offset_sector * block->sector_size; + detached_header_size = + (header_sectors + QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS * + split_key_sectors) * block->sector_size; /* Reserve header space to match payload offset */ - initfunc(block, block->payload_offset, opaque, &local_err); + initfunc(block, detached_header_size, opaque, &local_err); if (local_err) { error_propagate(errp, local_err); goto error; @@ -1629,13 +1625,13 @@ qcrypto_block_luks_amend_add_keyslot(QCryptoBlock *block, g_autofree char *new_password = NULL; g_autofree uint8_t *master_key = NULL; - char *secret = opts_luks->has_secret ? opts_luks->secret : luks->secret; + char *secret = opts_luks->secret ?: luks->secret; - if (!opts_luks->has_new_secret) { + if (!opts_luks->new_secret) { error_setg(errp, "'new-secret' is required to activate a keyslot"); return -1; } - if (opts_luks->has_old_secret) { + if (opts_luks->old_secret) { error_setg(errp, "'old-secret' must not be given when activating keyslots"); return -1; @@ -1709,7 +1705,7 @@ qcrypto_block_luks_amend_erase_keyslots(QCryptoBlock *block, g_autofree uint8_t *tmpkey = NULL; g_autofree char *old_password = NULL; - if (opts_luks->has_new_secret) { + if (opts_luks->new_secret) { error_setg(errp, "'new-secret' must not be given when erasing keyslots"); return -1; @@ -1719,14 +1715,14 @@ qcrypto_block_luks_amend_erase_keyslots(QCryptoBlock *block, "'iter-time' must not be given when erasing keyslots"); return -1; } - if (opts_luks->has_secret) { + if (opts_luks->secret) { error_setg(errp, "'secret' must not be given when erasing keyslots"); return -1; } /* Load the old password if given */ - if (opts_luks->has_old_secret) { + if (opts_luks->old_secret) { old_password = qcrypto_secret_lookup_as_utf8(opts_luks->old_secret, errp); if (!old_password) { @@ -1751,7 +1747,7 @@ qcrypto_block_luks_amend_erase_keyslots(QCryptoBlock *block, return -1; } - if (opts_luks->has_old_secret) { + if (opts_luks->old_secret) { int rv = qcrypto_block_luks_load_key(block, keyslot, old_password, @@ -1793,7 +1789,7 @@ qcrypto_block_luks_amend_erase_keyslots(QCryptoBlock *block, } /* Erase all keyslots that match the given old password */ - } else if (opts_luks->has_old_secret) { + } else if (opts_luks->old_secret) { unsigned long slots_to_erase_bitmap = 0; size_t i; @@ -1900,6 +1896,7 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block, info->u.luks.master_key_iters = luks->header.master_key_iterations; info->u.luks.uuid = g_strndup((const char *)luks->header.uuid, sizeof(luks->header.uuid)); + info->u.luks.detached_header = block->detached_header; for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { slot = g_new0(QCryptoBlockInfoLUKSSlot, 1); diff --git a/crypto/block.c b/crypto/block.c index eb057948b5..506ea1d1a3 100644 --- a/crypto/block.c +++ b/crypto/block.c @@ -87,6 +87,7 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, QCryptoBlockInitFunc initfunc, QCryptoBlockWriteFunc writefunc, void *opaque, + unsigned int flags, Error **errp) { QCryptoBlock *block = g_new0(QCryptoBlock, 1); @@ -102,6 +103,7 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, } block->driver = qcrypto_block_drivers[options->format]; + block->detached_header = flags & QCRYPTO_BLOCK_CREATE_DETACHED; if (block->driver->create(block, options, optprefix, initfunc, writefunc, opaque, errp) < 0) { @@ -115,7 +117,7 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, } -static ssize_t qcrypto_block_headerlen_hdr_init_func(QCryptoBlock *block, +static int qcrypto_block_headerlen_hdr_init_func(QCryptoBlock *block, size_t headerlen, void *opaque, Error **errp) { size_t *headerlenp = opaque; @@ -126,12 +128,12 @@ static ssize_t qcrypto_block_headerlen_hdr_init_func(QCryptoBlock *block, } -static ssize_t qcrypto_block_headerlen_hdr_write_func(QCryptoBlock *block, +static int 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; + return 0; } @@ -146,7 +148,7 @@ qcrypto_block_calculate_payload_offset(QCryptoBlockCreateOptions *create_opts, qcrypto_block_create(create_opts, optprefix, qcrypto_block_headerlen_hdr_init_func, qcrypto_block_headerlen_hdr_write_func, - len, errp); + len, 0, errp); return crypto != NULL; } diff --git a/crypto/blockpriv.h b/crypto/blockpriv.h index 3c7ccea504..836f3b4726 100644 --- a/crypto/blockpriv.h +++ b/crypto/blockpriv.h @@ -42,6 +42,8 @@ struct QCryptoBlock { size_t niv; uint64_t payload_offset; /* In bytes */ uint64_t sector_size; /* In bytes */ + + bool detached_header; /* True if disk has a detached LUKS header */ }; struct QCryptoBlockDriver { diff --git a/crypto/cipher-gcrypt.c.inc b/crypto/cipher-gcrypt.c.inc index a6a0117717..4a8314746d 100644 --- a/crypto/cipher-gcrypt.c.inc +++ b/crypto/cipher-gcrypt.c.inc @@ -20,6 +20,56 @@ #include +static int qcrypto_cipher_alg_to_gcry_alg(QCryptoCipherAlgorithm alg) +{ + switch (alg) { + case QCRYPTO_CIPHER_ALG_DES: + return GCRY_CIPHER_DES; + case QCRYPTO_CIPHER_ALG_3DES: + return GCRY_CIPHER_3DES; + case QCRYPTO_CIPHER_ALG_AES_128: + return GCRY_CIPHER_AES128; + case QCRYPTO_CIPHER_ALG_AES_192: + return GCRY_CIPHER_AES192; + case QCRYPTO_CIPHER_ALG_AES_256: + return GCRY_CIPHER_AES256; + case QCRYPTO_CIPHER_ALG_CAST5_128: + return GCRY_CIPHER_CAST5; + case QCRYPTO_CIPHER_ALG_SERPENT_128: + return GCRY_CIPHER_SERPENT128; + case QCRYPTO_CIPHER_ALG_SERPENT_192: + return GCRY_CIPHER_SERPENT192; + case QCRYPTO_CIPHER_ALG_SERPENT_256: + return GCRY_CIPHER_SERPENT256; + case QCRYPTO_CIPHER_ALG_TWOFISH_128: + return GCRY_CIPHER_TWOFISH128; + case QCRYPTO_CIPHER_ALG_TWOFISH_256: + return GCRY_CIPHER_TWOFISH; +#ifdef CONFIG_CRYPTO_SM4 + case QCRYPTO_CIPHER_ALG_SM4: + return GCRY_CIPHER_SM4; +#endif + default: + return GCRY_CIPHER_NONE; + } +} + +static int qcrypto_cipher_mode_to_gcry_mode(QCryptoCipherMode mode) +{ + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + return GCRY_CIPHER_MODE_ECB; + case QCRYPTO_CIPHER_MODE_XTS: + return GCRY_CIPHER_MODE_XTS; + case QCRYPTO_CIPHER_MODE_CBC: + return GCRY_CIPHER_MODE_CBC; + case QCRYPTO_CIPHER_MODE_CTR: + return GCRY_CIPHER_MODE_CTR; + default: + return GCRY_CIPHER_MODE_NONE; + } +} + bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, QCryptoCipherMode mode) { @@ -35,11 +85,19 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, case QCRYPTO_CIPHER_ALG_SERPENT_256: case QCRYPTO_CIPHER_ALG_TWOFISH_128: case QCRYPTO_CIPHER_ALG_TWOFISH_256: +#ifdef CONFIG_CRYPTO_SM4 + case QCRYPTO_CIPHER_ALG_SM4: +#endif break; default: return false; } + if (gcry_cipher_algo_info(qcrypto_cipher_alg_to_gcry_alg(alg), + GCRYCTL_TEST_ALGO, NULL, NULL) != 0) { + return false; + } + switch (mode) { case QCRYPTO_CIPHER_MODE_ECB: case QCRYPTO_CIPHER_MODE_CBC: @@ -185,67 +243,26 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, return NULL; } - switch (alg) { - case QCRYPTO_CIPHER_ALG_DES: - gcryalg = GCRY_CIPHER_DES; - break; - case QCRYPTO_CIPHER_ALG_3DES: - gcryalg = GCRY_CIPHER_3DES; - break; - case QCRYPTO_CIPHER_ALG_AES_128: - gcryalg = GCRY_CIPHER_AES128; - break; - case QCRYPTO_CIPHER_ALG_AES_192: - gcryalg = GCRY_CIPHER_AES192; - break; - case QCRYPTO_CIPHER_ALG_AES_256: - gcryalg = GCRY_CIPHER_AES256; - break; - case QCRYPTO_CIPHER_ALG_CAST5_128: - gcryalg = GCRY_CIPHER_CAST5; - break; - case QCRYPTO_CIPHER_ALG_SERPENT_128: - gcryalg = GCRY_CIPHER_SERPENT128; - break; - case QCRYPTO_CIPHER_ALG_SERPENT_192: - gcryalg = GCRY_CIPHER_SERPENT192; - break; - case QCRYPTO_CIPHER_ALG_SERPENT_256: - gcryalg = GCRY_CIPHER_SERPENT256; - break; - case QCRYPTO_CIPHER_ALG_TWOFISH_128: - gcryalg = GCRY_CIPHER_TWOFISH128; - break; - case QCRYPTO_CIPHER_ALG_TWOFISH_256: - gcryalg = GCRY_CIPHER_TWOFISH; - break; - default: + gcryalg = qcrypto_cipher_alg_to_gcry_alg(alg); + if (gcryalg == GCRY_CIPHER_NONE) { error_setg(errp, "Unsupported cipher algorithm %s", QCryptoCipherAlgorithm_str(alg)); return NULL; } - drv = &qcrypto_gcrypt_driver; - switch (mode) { - case QCRYPTO_CIPHER_MODE_ECB: - gcrymode = GCRY_CIPHER_MODE_ECB; - break; - case QCRYPTO_CIPHER_MODE_XTS: - gcrymode = GCRY_CIPHER_MODE_XTS; - break; - case QCRYPTO_CIPHER_MODE_CBC: - gcrymode = GCRY_CIPHER_MODE_CBC; - break; - case QCRYPTO_CIPHER_MODE_CTR: - drv = &qcrypto_gcrypt_ctr_driver; - gcrymode = GCRY_CIPHER_MODE_CTR; - break; - default: + gcrymode = qcrypto_cipher_mode_to_gcry_mode(mode); + if (gcrymode == GCRY_CIPHER_MODE_NONE) { error_setg(errp, "Unsupported cipher mode %s", QCryptoCipherMode_str(mode)); return NULL; } + if (mode == QCRYPTO_CIPHER_MODE_CTR) { + drv = &qcrypto_gcrypt_ctr_driver; + } else { + drv = &qcrypto_gcrypt_driver; + } + ctx = g_new0(QCryptoCipherGcrypt, 1); ctx->base.driver = drv; diff --git a/crypto/cipher-gnutls.c.inc b/crypto/cipher-gnutls.c.inc index 501e4e07a5..d3e231c13c 100644 --- a/crypto/cipher-gnutls.c.inc +++ b/crypto/cipher-gnutls.c.inc @@ -113,7 +113,7 @@ qcrypto_gnutls_cipher_encrypt(QCryptoCipher *cipher, while (len) { gnutls_cipher_hd_t handle; gnutls_datum_t gkey = { (unsigned char *)ctx->key, ctx->nkey }; - int err = gnutls_cipher_init(&handle, ctx->galg, &gkey, NULL); + err = gnutls_cipher_init(&handle, ctx->galg, &gkey, NULL); if (err != 0) { error_setg(errp, "Cannot initialize cipher: %s", gnutls_strerror(err)); @@ -174,7 +174,7 @@ qcrypto_gnutls_cipher_decrypt(QCryptoCipher *cipher, while (len) { gnutls_cipher_hd_t handle; gnutls_datum_t gkey = { (unsigned char *)ctx->key, ctx->nkey }; - int err = gnutls_cipher_init(&handle, ctx->galg, &gkey, NULL); + err = gnutls_cipher_init(&handle, ctx->galg, &gkey, NULL); if (err != 0) { error_setg(errp, "Cannot initialize cipher: %s", gnutls_strerror(err)); diff --git a/crypto/cipher-nettle.c.inc b/crypto/cipher-nettle.c.inc index 24cc61f87b..42b39e18a2 100644 --- a/crypto/cipher-nettle.c.inc +++ b/crypto/cipher-nettle.c.inc @@ -33,6 +33,9 @@ #ifndef CONFIG_QEMU_PRIVATE_XTS #include #endif +#ifdef CONFIG_CRYPTO_SM4 +#include +#endif static inline bool qcrypto_length_check(size_t len, size_t blocksize, Error **errp) @@ -426,6 +429,30 @@ DEFINE_ECB_CBC_CTR_XTS(qcrypto_nettle_twofish, QCryptoNettleTwofish, TWOFISH_BLOCK_SIZE, twofish_encrypt_native, twofish_decrypt_native) +#ifdef CONFIG_CRYPTO_SM4 +typedef struct QCryptoNettleSm4 { + QCryptoCipher base; + struct sm4_ctx key[2]; +} QCryptoNettleSm4; + +static void sm4_encrypt_native(void *ctx, size_t length, + uint8_t *dst, const uint8_t *src) +{ + struct sm4_ctx *keys = ctx; + sm4_crypt(&keys[0], length, dst, src); +} + +static void sm4_decrypt_native(void *ctx, size_t length, + uint8_t *dst, const uint8_t *src) +{ + struct sm4_ctx *keys = ctx; + sm4_crypt(&keys[1], length, dst, src); +} + +DEFINE_ECB(qcrypto_nettle_sm4, + QCryptoNettleSm4, SM4_BLOCK_SIZE, + sm4_encrypt_native, sm4_decrypt_native) +#endif bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, QCryptoCipherMode mode) @@ -443,6 +470,9 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, case QCRYPTO_CIPHER_ALG_TWOFISH_128: case QCRYPTO_CIPHER_ALG_TWOFISH_192: case QCRYPTO_CIPHER_ALG_TWOFISH_256: +#ifdef CONFIG_CRYPTO_SM4 + case QCRYPTO_CIPHER_ALG_SM4: +#endif break; default: return false; @@ -701,6 +731,25 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, return &ctx->base; } +#ifdef CONFIG_CRYPTO_SM4 + case QCRYPTO_CIPHER_ALG_SM4: + { + QCryptoNettleSm4 *ctx = g_new0(QCryptoNettleSm4, 1); + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + ctx->base.driver = &qcrypto_nettle_sm4_driver_ecb; + break; + default: + goto bad_cipher_mode; + } + + sm4_set_encrypt_key(&ctx->key[0], key); + sm4_set_decrypt_key(&ctx->key[1], key); + + return &ctx->base; + } +#endif default: error_setg(errp, "Unsupported cipher algorithm %s", diff --git a/crypto/cipher.c b/crypto/cipher.c index 74b09a5b26..5f512768ea 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -38,6 +38,9 @@ static const size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = { [QCRYPTO_CIPHER_ALG_TWOFISH_128] = 16, [QCRYPTO_CIPHER_ALG_TWOFISH_192] = 24, [QCRYPTO_CIPHER_ALG_TWOFISH_256] = 32, +#ifdef CONFIG_CRYPTO_SM4 + [QCRYPTO_CIPHER_ALG_SM4] = 16, +#endif }; static const size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = { @@ -53,6 +56,9 @@ static const size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = { [QCRYPTO_CIPHER_ALG_TWOFISH_128] = 16, [QCRYPTO_CIPHER_ALG_TWOFISH_192] = 16, [QCRYPTO_CIPHER_ALG_TWOFISH_256] = 16, +#ifdef CONFIG_CRYPTO_SM4 + [QCRYPTO_CIPHER_ALG_SM4] = 16, +#endif }; static const bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX] = { diff --git a/crypto/clmul.c b/crypto/clmul.c new file mode 100644 index 0000000000..9e3e61a77d --- /dev/null +++ b/crypto/clmul.c @@ -0,0 +1,111 @@ +/* + * Carry-less multiply operations. + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (C) 2023 Linaro, Ltd. + */ + +#include "qemu/osdep.h" +#include "crypto/clmul.h" + +uint64_t clmul_8x8_low(uint64_t n, uint64_t m) +{ + uint64_t r = 0; + + for (int i = 0; i < 8; ++i) { + uint64_t mask = (n & 0x0101010101010101ull) * 0xff; + r ^= m & mask; + m = (m << 1) & 0xfefefefefefefefeull; + n >>= 1; + } + return r; +} + +static uint64_t clmul_8x4_even_int(uint64_t n, uint64_t m) +{ + uint64_t r = 0; + + for (int i = 0; i < 8; ++i) { + uint64_t mask = (n & 0x0001000100010001ull) * 0xffff; + r ^= m & mask; + n >>= 1; + m <<= 1; + } + return r; +} + +uint64_t clmul_8x4_even(uint64_t n, uint64_t m) +{ + n &= 0x00ff00ff00ff00ffull; + m &= 0x00ff00ff00ff00ffull; + return clmul_8x4_even_int(n, m); +} + +uint64_t clmul_8x4_odd(uint64_t n, uint64_t m) +{ + return clmul_8x4_even(n >> 8, m >> 8); +} + +static uint64_t unpack_8_to_16(uint64_t x) +{ + return (x & 0x000000ff) + | ((x & 0x0000ff00) << 8) + | ((x & 0x00ff0000) << 16) + | ((x & 0xff000000) << 24); +} + +uint64_t clmul_8x4_packed(uint32_t n, uint32_t m) +{ + return clmul_8x4_even_int(unpack_8_to_16(n), unpack_8_to_16(m)); +} + +uint64_t clmul_16x2_even(uint64_t n, uint64_t m) +{ + uint64_t r = 0; + + n &= 0x0000ffff0000ffffull; + m &= 0x0000ffff0000ffffull; + + for (int i = 0; i < 16; ++i) { + uint64_t mask = (n & 0x0000000100000001ull) * 0xffffffffull; + r ^= m & mask; + n >>= 1; + m <<= 1; + } + return r; +} + +uint64_t clmul_16x2_odd(uint64_t n, uint64_t m) +{ + return clmul_16x2_even(n >> 16, m >> 16); +} + +uint64_t clmul_32(uint32_t n, uint32_t m32) +{ + uint64_t r = 0; + uint64_t m = m32; + + for (int i = 0; i < 32; ++i) { + r ^= n & 1 ? m : 0; + n >>= 1; + m <<= 1; + } + return r; +} + +Int128 clmul_64_gen(uint64_t n, uint64_t m) +{ + uint64_t rl = 0, rh = 0; + + /* Bit 0 can only influence the low 64-bit result. */ + if (n & 1) { + rl = m; + } + + for (int i = 1; i < 64; ++i) { + uint64_t mask = -((n >> i) & 1); + rl ^= (m << i) & mask; + rh ^= (m >> (64 - i)) & mask; + } + return int128_make128(rl, rh); +} diff --git a/crypto/der.c b/crypto/der.c index f877390bbb..ebbecfc3fe 100644 --- a/crypto/der.c +++ b/crypto/der.c @@ -22,20 +22,93 @@ #include "qemu/osdep.h" #include "crypto/der.h" +typedef struct QCryptoDerEncodeNode { + uint8_t tag; + struct QCryptoDerEncodeNode *parent; + struct QCryptoDerEncodeNode *next; + /* for constructed type, data is null */ + const uint8_t *data; + size_t dlen; +} QCryptoDerEncodeNode; + +typedef struct QCryptoEncodeContext { + QCryptoDerEncodeNode root; + QCryptoDerEncodeNode *current_parent; + QCryptoDerEncodeNode *tail; +} QCryptoEncodeContext; + enum QCryptoDERTypeTag { QCRYPTO_DER_TYPE_TAG_BOOL = 0x1, QCRYPTO_DER_TYPE_TAG_INT = 0x2, QCRYPTO_DER_TYPE_TAG_BIT_STR = 0x3, QCRYPTO_DER_TYPE_TAG_OCT_STR = 0x4, - QCRYPTO_DER_TYPE_TAG_OCT_NULL = 0x5, - QCRYPTO_DER_TYPE_TAG_OCT_OID = 0x6, + QCRYPTO_DER_TYPE_TAG_NULL = 0x5, + QCRYPTO_DER_TYPE_TAG_OID = 0x6, QCRYPTO_DER_TYPE_TAG_SEQ = 0x10, QCRYPTO_DER_TYPE_TAG_SET = 0x11, }; -#define QCRYPTO_DER_CONSTRUCTED_MASK 0x20 +enum QCryptoDERTagClass { + QCRYPTO_DER_TAG_CLASS_UNIV = 0x0, + QCRYPTO_DER_TAG_CLASS_APPL = 0x1, + QCRYPTO_DER_TAG_CLASS_CONT = 0x2, + QCRYPTO_DER_TAG_CLASS_PRIV = 0x3, +}; + +enum QCryptoDERTagEnc { + QCRYPTO_DER_TAG_ENC_PRIM = 0x0, + QCRYPTO_DER_TAG_ENC_CONS = 0x1, +}; + +#define QCRYPTO_DER_TAG_ENC_MASK 0x20 +#define QCRYPTO_DER_TAG_ENC_SHIFT 5 + +#define QCRYPTO_DER_TAG_CLASS_MASK 0xc0 +#define QCRYPTO_DER_TAG_CLASS_SHIFT 6 + +#define QCRYPTO_DER_TAG_VAL_MASK 0x1f #define QCRYPTO_DER_SHORT_LEN_MASK 0x80 +#define QCRYPTO_DER_TAG(class, enc, val) \ + (((class) << QCRYPTO_DER_TAG_CLASS_SHIFT) | \ + ((enc) << QCRYPTO_DER_TAG_ENC_SHIFT) | (val)) + +/** + * qcrypto_der_encode_length: + * @src_len: the length of source data + * @dst: destination to save the encoded 'length', if dst is NULL, only compute + * the expected buffer size in bytes. + * @dst_len: output parameter, indicates how many bytes wrote. + * + * Encode the 'length' part of TLV tuple. + */ +static void qcrypto_der_encode_length(size_t src_len, + uint8_t *dst, size_t *dst_len) +{ + size_t max_length = 0xFF; + uint8_t length_bytes = 0, header_byte; + + if (src_len < QCRYPTO_DER_SHORT_LEN_MASK) { + header_byte = src_len; + *dst_len = 1; + } else { + for (length_bytes = 1; max_length < src_len; length_bytes++) { + max_length = (max_length << 8) + max_length; + } + header_byte = length_bytes; + header_byte |= QCRYPTO_DER_SHORT_LEN_MASK; + *dst_len = length_bytes + 1; + } + if (!dst) { + return; + } + *dst++ = header_byte; + /* Bigendian length bytes */ + for (; length_bytes > 0; length_bytes--) { + *dst++ = ((src_len >> (length_bytes - 1) * 8) & 0xFF); + } +} + static uint8_t qcrypto_der_peek_byte(const uint8_t **data, size_t *dlen) { return **data; @@ -150,40 +223,230 @@ static int qcrypto_der_extract_data(const uint8_t **data, size_t *dlen, return qcrypto_der_extract_definite_data(data, dlen, cb, ctx, errp); } -int qcrypto_der_decode_int(const uint8_t **data, size_t *dlen, - QCryptoDERDecodeCb cb, void *ctx, Error **errp) +static int qcrypto_der_decode_tlv(const uint8_t expected_tag, + const uint8_t **data, size_t *dlen, + QCryptoDERDecodeCb cb, + void *ctx, Error **errp) { + const uint8_t *saved_data = *data; + size_t saved_dlen = *dlen; uint8_t tag; + int data_length; + if (*dlen < 1) { error_setg(errp, "Need more data"); return -1; } tag = qcrypto_der_cut_byte(data, dlen); - - /* INTEGER must encoded in primitive-form */ - if (tag != QCRYPTO_DER_TYPE_TAG_INT) { - error_setg(errp, "Invalid integer type tag: %u", tag); - return -1; + if (tag != expected_tag) { + error_setg(errp, "Unexpected tag: expected: %u, actual: %u", + expected_tag, tag); + goto error; } - return qcrypto_der_extract_data(data, dlen, cb, ctx, errp); + data_length = qcrypto_der_extract_data(data, dlen, cb, ctx, errp); + if (data_length < 0) { + goto error; + } + return data_length; + +error: + *data = saved_data; + *dlen = saved_dlen; + return -1; +} + +int qcrypto_der_decode_int(const uint8_t **data, size_t *dlen, + QCryptoDERDecodeCb cb, void *ctx, Error **errp) +{ + const uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV, + QCRYPTO_DER_TAG_ENC_PRIM, + QCRYPTO_DER_TYPE_TAG_INT); + return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp); } int qcrypto_der_decode_seq(const uint8_t **data, size_t *dlen, QCryptoDERDecodeCb cb, void *ctx, Error **errp) { - uint8_t tag; - if (*dlen < 1) { - error_setg(errp, "Need more data"); - return -1; - } - tag = qcrypto_der_cut_byte(data, dlen); - - /* SEQUENCE must use constructed form */ - if (tag != (QCRYPTO_DER_TYPE_TAG_SEQ | QCRYPTO_DER_CONSTRUCTED_MASK)) { - error_setg(errp, "Invalid type sequence tag: %u", tag); - return -1; - } - - return qcrypto_der_extract_data(data, dlen, cb, ctx, errp); + uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV, + QCRYPTO_DER_TAG_ENC_CONS, + QCRYPTO_DER_TYPE_TAG_SEQ); + return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp); +} + +int qcrypto_der_decode_octet_str(const uint8_t **data, size_t *dlen, + QCryptoDERDecodeCb cb, void *ctx, Error **errp) +{ + uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV, + QCRYPTO_DER_TAG_ENC_PRIM, + QCRYPTO_DER_TYPE_TAG_OCT_STR); + return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp); +} + +int qcrypto_der_decode_bit_str(const uint8_t **data, size_t *dlen, + QCryptoDERDecodeCb cb, void *ctx, Error **errp) +{ + uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV, + QCRYPTO_DER_TAG_ENC_PRIM, + QCRYPTO_DER_TYPE_TAG_BIT_STR); + return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp); +} + +int qcrypto_der_decode_oid(const uint8_t **data, size_t *dlen, + QCryptoDERDecodeCb cb, void *ctx, Error **errp) +{ + uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV, + QCRYPTO_DER_TAG_ENC_PRIM, + QCRYPTO_DER_TYPE_TAG_OID); + return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp); +} + +int qcrypto_der_decode_ctx_tag(const uint8_t **data, size_t *dlen, int tag_id, + QCryptoDERDecodeCb cb, void *ctx, Error **errp) +{ + uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_CONT, + QCRYPTO_DER_TAG_ENC_CONS, + tag_id); + return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp); +} + +static void qcrypto_der_encode_prim(QCryptoEncodeContext *ctx, uint8_t tag, + const uint8_t *data, size_t dlen) +{ + QCryptoDerEncodeNode *node = g_new0(QCryptoDerEncodeNode, 1); + size_t nbytes_len; + + node->tag = tag; + node->data = data; + node->dlen = dlen; + node->parent = ctx->current_parent; + + qcrypto_der_encode_length(dlen, NULL, &nbytes_len); + /* 1 byte for Tag, nbyte_len for Length, and dlen for Value */ + node->parent->dlen += 1 + nbytes_len + dlen; + + ctx->tail->next = node; + ctx->tail = node; +} + +QCryptoEncodeContext *qcrypto_der_encode_ctx_new(void) +{ + QCryptoEncodeContext *ctx = g_new0(QCryptoEncodeContext, 1); + ctx->current_parent = &ctx->root; + ctx->tail = &ctx->root; + return ctx; +} + +static void qcrypto_der_encode_cons_begin(QCryptoEncodeContext *ctx, + uint8_t tag) +{ + QCryptoDerEncodeNode *node = g_new0(QCryptoDerEncodeNode, 1); + + node->tag = tag; + node->parent = ctx->current_parent; + ctx->current_parent = node; + ctx->tail->next = node; + ctx->tail = node; +} + +static void qcrypto_der_encode_cons_end(QCryptoEncodeContext *ctx) +{ + QCryptoDerEncodeNode *cons_node = ctx->current_parent; + size_t nbytes_len; + + qcrypto_der_encode_length(cons_node->dlen, NULL, &nbytes_len); + /* 1 byte for Tag, nbyte_len for Length, and dlen for Value */ + cons_node->parent->dlen += 1 + nbytes_len + cons_node->dlen; + ctx->current_parent = cons_node->parent; +} + +void qcrypto_der_encode_seq_begin(QCryptoEncodeContext *ctx) +{ + uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV, + QCRYPTO_DER_TAG_ENC_CONS, + QCRYPTO_DER_TYPE_TAG_SEQ); + qcrypto_der_encode_cons_begin(ctx, tag); +} + +void qcrypto_der_encode_seq_end(QCryptoEncodeContext *ctx) +{ + qcrypto_der_encode_cons_end(ctx); +} + +void qcrypto_der_encode_oid(QCryptoEncodeContext *ctx, + const uint8_t *src, size_t src_len) +{ + uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV, + QCRYPTO_DER_TAG_ENC_PRIM, + QCRYPTO_DER_TYPE_TAG_OID); + qcrypto_der_encode_prim(ctx, tag, src, src_len); +} + +void qcrypto_der_encode_int(QCryptoEncodeContext *ctx, + const uint8_t *src, size_t src_len) +{ + uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV, + QCRYPTO_DER_TAG_ENC_PRIM, + QCRYPTO_DER_TYPE_TAG_INT); + qcrypto_der_encode_prim(ctx, tag, src, src_len); +} + +void qcrypto_der_encode_null(QCryptoEncodeContext *ctx) +{ + uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV, + QCRYPTO_DER_TAG_ENC_PRIM, + QCRYPTO_DER_TYPE_TAG_NULL); + qcrypto_der_encode_prim(ctx, tag, NULL, 0); +} + +void qcrypto_der_encode_octet_str(QCryptoEncodeContext *ctx, + const uint8_t *src, size_t src_len) +{ + uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV, + QCRYPTO_DER_TAG_ENC_PRIM, + QCRYPTO_DER_TYPE_TAG_OCT_STR); + qcrypto_der_encode_prim(ctx, tag, src, src_len); +} + +void qcrypto_der_encode_octet_str_begin(QCryptoEncodeContext *ctx) +{ + uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV, + QCRYPTO_DER_TAG_ENC_PRIM, + QCRYPTO_DER_TYPE_TAG_OCT_STR); + qcrypto_der_encode_cons_begin(ctx, tag); +} + +void qcrypto_der_encode_octet_str_end(QCryptoEncodeContext *ctx) +{ + qcrypto_der_encode_cons_end(ctx); +} + +size_t qcrypto_der_encode_ctx_buffer_len(QCryptoEncodeContext *ctx) +{ + return ctx->root.dlen; +} + +void qcrypto_der_encode_ctx_flush_and_free(QCryptoEncodeContext *ctx, + uint8_t *dst) +{ + QCryptoDerEncodeNode *node, *prev; + size_t len; + + for (prev = &ctx->root; + (node = prev->next) && (prev->next = node->next, 1);) { + /* Tag */ + *dst++ = node->tag; + + /* Length */ + qcrypto_der_encode_length(node->dlen, dst, &len); + dst += len; + + /* Value */ + if (node->data) { + memcpy(dst, node->data, node->dlen); + dst += node->dlen; + } + g_free(node); + } + g_free(ctx); } diff --git a/crypto/der.h b/crypto/der.h index e3d3aeacdc..f4ba6da28a 100644 --- a/crypto/der.h +++ b/crypto/der.h @@ -22,6 +22,11 @@ #include "qapi/error.h" +typedef struct QCryptoEncodeContext QCryptoEncodeContext; + +/* rsaEncryption: 1.2.840.113549.1.1.1 */ +#define QCRYPTO_OID_rsaEncryption "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01" + /* Simple decoder used to parse DER encoded rsa keys. */ /** @@ -47,14 +52,13 @@ typedef int (*QCryptoDERDecodeCb) (void *opaque, const uint8_t *value, * will be set to the rest length of data, if cb is not NULL, must * return 0 to make decode success, at last, the length of the data * part of the decoded INTEGER will be returned. Otherwise, -1 is - * returned. + * returned and the valued of *data and *dlen keep unchanged. */ int qcrypto_der_decode_int(const uint8_t **data, size_t *dlen, QCryptoDERDecodeCb cb, void *opaque, Error **errp); - /** * qcrypto_der_decode_seq: * @@ -70,7 +74,7 @@ int qcrypto_der_decode_int(const uint8_t **data, * will be set to the rest length of data, if cb is not NULL, must * return 0 to make decode success, at last, the length of the data * part of the decoded SEQUENCE will be returned. Otherwise, -1 is - * returned. + * returned and the valued of *data and *dlen keep unchanged. */ int qcrypto_der_decode_seq(const uint8_t **data, size_t *dlen, @@ -78,4 +82,205 @@ int qcrypto_der_decode_seq(const uint8_t **data, void *opaque, Error **errp); +/** + * qcrypto_der_decode_oid: + * + * Decode OID from DER-encoded data, similar with der_decode_int. + * + * @data: pointer to address of input data + * @dlen: pointer to length of input data + * @cb: callback invoked when decode succeed, if cb equals NULL, no + * callback will be invoked + * @opaque: parameter passed to cb + * + * Returns: On success, *data points to rest data, and *dlen + * will be set to the rest length of data, if cb is not NULL, must + * return 0 to make decode success, at last, the length of the data + * part of the decoded OID will be returned. Otherwise, -1 is + * returned and the valued of *data and *dlen keep unchanged. + */ +int qcrypto_der_decode_oid(const uint8_t **data, + size_t *dlen, + QCryptoDERDecodeCb cb, + void *opaque, + Error **errp); + +/** + * qcrypto_der_decode_octet_str: + * + * Decode OCTET STRING from DER-encoded data, similar with der_decode_int. + * + * @data: pointer to address of input data + * @dlen: pointer to length of input data + * @cb: callback invoked when decode succeed, if cb equals NULL, no + * callback will be invoked + * @opaque: parameter passed to cb + * + * Returns: On success, *data points to rest data, and *dlen + * will be set to the rest length of data, if cb is not NULL, must + * return 0 to make decode success, at last, the length of the data + * part of the decoded OCTET STRING will be returned. Otherwise, -1 is + * returned and the valued of *data and *dlen keep unchanged. + */ +int qcrypto_der_decode_octet_str(const uint8_t **data, + size_t *dlen, + QCryptoDERDecodeCb cb, + void *opaque, + Error **errp); + +/** + * qcrypto_der_decode_bit_str: + * + * Decode BIT STRING from DER-encoded data, similar with der_decode_int. + * + * @data: pointer to address of input data + * @dlen: pointer to length of input data + * @cb: callback invoked when decode succeed, if cb equals NULL, no + * callback will be invoked + * @opaque: parameter passed to cb + * + * Returns: On success, *data points to rest data, and *dlen + * will be set to the rest length of data, if cb is not NULL, must + * return 0 to make decode success, at last, the length of the data + * part of the decoded BIT STRING will be returned. Otherwise, -1 is + * returned and the valued of *data and *dlen keep unchanged. + */ +int qcrypto_der_decode_bit_str(const uint8_t **data, + size_t *dlen, + QCryptoDERDecodeCb cb, + void *opaque, + Error **errp); + + +/** + * qcrypto_der_decode_ctx_tag: + * + * Decode context specific tag + * + * @data: pointer to address of input data + * @dlen: pointer to length of input data + * @tag: expected value of context specific tag + * @cb: callback invoked when decode succeed, if cb equals NULL, no + * callback will be invoked + * @opaque: parameter passed to cb + * + * Returns: On success, *data points to rest data, and *dlen + * will be set to the rest length of data, if cb is not NULL, must + * return 0 to make decode success, at last, the length of the data + * part of the decoded BIT STRING will be returned. Otherwise, -1 is + * returned and the valued of *data and *dlen keep unchanged. + */ +int qcrypto_der_decode_ctx_tag(const uint8_t **data, + size_t *dlen, int tag_id, + QCryptoDERDecodeCb cb, + void *opaque, + Error **errp); + +/** + * qcrypto_der_encode_ctx_new: + * + * Allocate a context used for der encoding. + */ +QCryptoEncodeContext *qcrypto_der_encode_ctx_new(void); + +/** + * qcrypto_der_encode_seq_begin: + * @ctx: the encode context. + * + * Start encoding a SEQUENCE for ctx. + * + */ +void qcrypto_der_encode_seq_begin(QCryptoEncodeContext *ctx); + +/** + * qcrypto_der_encode_seq_begin: + * @ctx: the encode context. + * + * Finish uencoding a SEQUENCE for ctx. + * + */ +void qcrypto_der_encode_seq_end(QCryptoEncodeContext *ctx); + + +/** + * qcrypto_der_encode_oid: + * @ctx: the encode context. + * @src: the source data of oid, note it should be already encoded, this + * function only add tag and length part for it. + * + * Encode an oid into ctx. + */ +void qcrypto_der_encode_oid(QCryptoEncodeContext *ctx, + const uint8_t *src, size_t src_len); + +/** + * qcrypto_der_encode_int: + * @ctx: the encode context. + * @src: the source data of integer, note it should be already encoded, this + * function only add tag and length part for it. + * + * Encode an integer into ctx. + */ +void qcrypto_der_encode_int(QCryptoEncodeContext *ctx, + const uint8_t *src, size_t src_len); + +/** + * qcrypto_der_encode_null: + * @ctx: the encode context. + * + * Encode a null into ctx. + */ +void qcrypto_der_encode_null(QCryptoEncodeContext *ctx); + +/** + * qcrypto_der_encode_octet_str: + * @ctx: the encode context. + * @src: the source data of the octet string. + * + * Encode a octet string into ctx. + */ +void qcrypto_der_encode_octet_str(QCryptoEncodeContext *ctx, + const uint8_t *src, size_t src_len); + +/** + * qcrypto_der_encode_octet_str_begin: + * @ctx: the encode context. + * + * Start encoding a octet string, All fields between + * qcrypto_der_encode_octet_str_begin and qcrypto_der_encode_octet_str_end + * are encoded as an octet string. This is useful when we need to encode a + * encoded SEQUENCE as OCTET STRING. + */ +void qcrypto_der_encode_octet_str_begin(QCryptoEncodeContext *ctx); + +/** + * qcrypto_der_encode_octet_str_end: + * @ctx: the encode context. + * + * Finish encoding a octet string, All fields between + * qcrypto_der_encode_octet_str_begin and qcrypto_der_encode_octet_str_end + * are encoded as an octet string. This is useful when we need to encode a + * encoded SEQUENCE as OCTET STRING. + */ +void qcrypto_der_encode_octet_str_end(QCryptoEncodeContext *ctx); + +/** + * qcrypto_der_encode_ctx_buffer_len: + * @ctx: the encode context. + * + * Compute the expected buffer size to save all encoded things. + */ +size_t qcrypto_der_encode_ctx_buffer_len(QCryptoEncodeContext *ctx); + +/** + * qcrypto_der_encode_ctx_flush_and_free: + * @ctx: the encode context. + * @dst: the destination to save the encoded data, the length of dst should + * not less than qcrypto_der_encode_cxt_buffer_len + * + * Flush all encoded data into dst, then free ctx. + */ +void qcrypto_der_encode_ctx_flush_and_free(QCryptoEncodeContext *ctx, + uint8_t *dst); + #endif /* QCRYPTO_ASN1_DECODER_H */ diff --git a/crypto/hmacpriv.h b/crypto/hmacpriv.h index 4387ca2587..62dfe8257a 100644 --- a/crypto/hmacpriv.h +++ b/crypto/hmacpriv.h @@ -28,19 +28,18 @@ struct QCryptoHmacDriver { void (*hmac_free)(QCryptoHmac *hmac); }; -extern void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, - const uint8_t *key, size_t nkey, - Error **errp); +void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, + const uint8_t *key, size_t nkey, + Error **errp); extern QCryptoHmacDriver qcrypto_hmac_lib_driver; #ifdef CONFIG_AF_ALG #include "afalgpriv.h" -extern QCryptoAFAlg * -qcrypto_afalg_hmac_ctx_new(QCryptoHashAlgorithm alg, - const uint8_t *key, size_t nkey, - Error **errp); +QCryptoAFAlg *qcrypto_afalg_hmac_ctx_new(QCryptoHashAlgorithm alg, + const uint8_t *key, size_t nkey, + Error **errp); extern QCryptoHmacDriver qcrypto_hmac_afalg_driver; #endif diff --git a/crypto/meson.build b/crypto/meson.build index 5f03a30d34..c46f9c22a7 100644 --- a/crypto/meson.build +++ b/crypto/meson.build @@ -46,11 +46,15 @@ endif if have_afalg crypto_ss.add(if_true: files('afalg.c', 'cipher-afalg.c', 'hash-afalg.c')) endif -crypto_ss.add(when: gnutls, if_true: files('tls-cipher-suites.c')) -util_ss.add(files('sm4.c')) -util_ss.add(files('aes.c')) -util_ss.add(files('init.c')) +system_ss.add(when: gnutls, if_true: files('tls-cipher-suites.c')) + +util_ss.add(files( + 'aes.c', + 'clmul.c', + 'init.c', + 'sm4.c', +)) if gnutls.found() util_ss.add(gnutls) endif diff --git a/crypto/pbkdf.c b/crypto/pbkdf.c index 3775ddc6c5..8d198c152c 100644 --- a/crypto/pbkdf.c +++ b/crypto/pbkdf.c @@ -24,6 +24,11 @@ #ifndef _WIN32 #include #endif +#ifdef CONFIG_DARWIN +#include +#include +#include +#endif static int qcrypto_pbkdf2_get_thread_cpu(unsigned long long *val_ms, @@ -45,6 +50,24 @@ static int qcrypto_pbkdf2_get_thread_cpu(unsigned long long *val_ms, /* QuadPart is units of 100ns and we want ms as unit */ *val_ms = thread_time.QuadPart / 10000ll; return 0; +#elif defined(CONFIG_DARWIN) + mach_port_t thread; + kern_return_t kr; + mach_msg_type_number_t count; + thread_basic_info_data_t info; + + thread = mach_thread_self(); + count = THREAD_BASIC_INFO_COUNT; + kr = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&info, &count); + mach_port_deallocate(mach_task_self(), thread); + if (kr != KERN_SUCCESS || (info.flags & TH_FLAGS_IDLE) != 0) { + error_setg_errno(errp, errno, "Unable to get thread CPU usage"); + return -1; + } + + *val_ms = ((info.user_time.seconds * 1000ll) + + (info.user_time.microseconds / 1000)); + return 0; #elif defined(RUSAGE_THREAD) struct rusage ru; if (getrusage(RUSAGE_THREAD, &ru) < 0) { diff --git a/crypto/rsakey-builtin.c.inc b/crypto/rsakey-builtin.c.inc index aeeacc8f9b..46cc7afe87 100644 --- a/crypto/rsakey-builtin.c.inc +++ b/crypto/rsakey-builtin.c.inc @@ -88,15 +88,13 @@ static QCryptoAkCipherRSAKey *qcrypto_builtin_rsa_public_key_parse( goto error; } if (seq_length != 0) { + error_setg(errp, "Invalid RSA public key"); goto error; } return rsa; error: - if (errp && !*errp) { - error_setg(errp, "Invalid RSA public key"); - } qcrypto_akcipher_rsakey_free(rsa); return NULL; } @@ -169,15 +167,13 @@ static QCryptoAkCipherRSAKey *qcrypto_builtin_rsa_private_key_parse( return rsa; } if (seq_length != 0) { + error_setg(errp, "Invalid RSA private key"); goto error; } return rsa; error: - if (errp && !*errp) { - error_setg(errp, "Invalid RSA private key"); - } qcrypto_akcipher_rsakey_free(rsa); return NULL; } diff --git a/crypto/rsakey.c b/crypto/rsakey.c index cc40e072f0..7d6f273aef 100644 --- a/crypto/rsakey.c +++ b/crypto/rsakey.c @@ -19,6 +19,8 @@ * */ +#include "qemu/osdep.h" +#include "der.h" #include "rsakey.h" void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *rsa_key) @@ -37,6 +39,46 @@ void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *rsa_key) g_free(rsa_key); } +/** + * PKCS#8 private key info for RSA + * + * PrivateKeyInfo ::= SEQUENCE { + * version INTEGER, + * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + * privateKey OCTET STRING, + * attributes [0] IMPLICIT Attributes OPTIONAL + * } + */ +void qcrypto_akcipher_rsakey_export_p8info(const uint8_t *key, + size_t keylen, + uint8_t **dst, + size_t *dlen) +{ + QCryptoEncodeContext *ctx = qcrypto_der_encode_ctx_new(); + uint8_t version = 0; + + qcrypto_der_encode_seq_begin(ctx); + + /* version */ + qcrypto_der_encode_int(ctx, &version, sizeof(version)); + + /* algorithm identifier */ + qcrypto_der_encode_seq_begin(ctx); + qcrypto_der_encode_oid(ctx, (uint8_t *)QCRYPTO_OID_rsaEncryption, + sizeof(QCRYPTO_OID_rsaEncryption) - 1); + qcrypto_der_encode_null(ctx); + qcrypto_der_encode_seq_end(ctx); + + /* RSA private key */ + qcrypto_der_encode_octet_str(ctx, key, keylen); + + qcrypto_der_encode_seq_end(ctx); + + *dlen = qcrypto_der_encode_ctx_buffer_len(ctx); + *dst = g_malloc(*dlen); + qcrypto_der_encode_ctx_flush_and_free(ctx, *dst); +} + #if defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED) #include "rsakey-nettle.c.inc" #else diff --git a/crypto/rsakey.h b/crypto/rsakey.h index 974b76f659..00b3eccec7 100644 --- a/crypto/rsakey.h +++ b/crypto/rsakey.h @@ -22,7 +22,6 @@ #ifndef QCRYPTO_RSAKEY_H #define QCRYPTO_RSAKEY_H -#include "qemu/osdep.h" #include "qemu/host-utils.h" #include "crypto/akcipher.h" @@ -84,6 +83,16 @@ QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse( QCryptoAkCipherKeyType type, const uint8_t *key, size_t keylen, Error **errp); +/** + * qcrypto_akcipher_rsakey_export_as_p8info: + * + * Export RSA private key to PKCS#8 private key info. + */ +void qcrypto_akcipher_rsakey_export_p8info(const uint8_t *key, + size_t keylen, + uint8_t **dst, + size_t *dlen); + void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *key); G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipherRSAKey, diff --git a/crypto/sm4.c b/crypto/sm4.c index 9f0cd452c7..2987306cf7 100644 --- a/crypto/sm4.c +++ b/crypto/sm4.c @@ -47,3 +47,13 @@ uint8_t const sm4_sbox[] = { 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48, }; +uint32_t const sm4_ck[] = { + 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269, + 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9, + 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249, + 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9, + 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229, + 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299, + 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, + 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279 +}; diff --git a/crypto/tls-cipher-suites.c b/crypto/tls-cipher-suites.c index 5e4f597464..d0df4badc0 100644 --- a/crypto/tls-cipher-suites.c +++ b/crypto/tls-cipher-suites.c @@ -52,7 +52,6 @@ GByteArray *qcrypto_tls_cipher_suites_get_data(QCryptoTLSCipherSuites *obj, byte_array = g_byte_array_new(); for (i = 0;; i++) { - int ret; unsigned idx; const char *name; IANA_TLS_CIPHER cipher; diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c index a4f9891274..546cad1c5a 100644 --- a/crypto/tlscredspsk.c +++ b/crypto/tlscredspsk.c @@ -109,7 +109,12 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds, goto cleanup; } - gnutls_psk_set_server_credentials_file(creds->data.server, pskfile); + ret = gnutls_psk_set_server_credentials_file(creds->data.server, pskfile); + if (ret < 0) { + error_setg(errp, "Cannot set PSK server credentials: %s", + gnutls_strerror(ret)); + goto cleanup; + } gnutls_psk_set_server_dh_params(creds->data.server, creds->parent_obj.dh_params); } else { @@ -135,8 +140,13 @@ qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds, goto cleanup; } - gnutls_psk_set_client_credentials(creds->data.client, - username, &key, GNUTLS_PSK_KEY_HEX); + ret = gnutls_psk_set_client_credentials(creds->data.client, + username, &key, GNUTLS_PSK_KEY_HEX); + if (ret < 0) { + error_setg(errp, "Cannot set PSK client credentials: %s", + gnutls_strerror(ret)); + goto cleanup; + } } rv = 0; diff --git a/crypto/tlssession.c b/crypto/tlssession.c index b302d835d2..1e98f44e0d 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -493,6 +493,13 @@ qcrypto_tls_session_read(QCryptoTLSSession *session, } +size_t +qcrypto_tls_session_check_pending(QCryptoTLSSession *session) +{ + return gnutls_record_check_pending(session->handle); +} + + int qcrypto_tls_session_handshake(QCryptoTLSSession *session, Error **errp) @@ -615,6 +622,13 @@ qcrypto_tls_session_read(QCryptoTLSSession *sess, } +size_t +qcrypto_tls_session_check_pending(QCryptoTLSSession *session) +{ + return 0; +} + + int qcrypto_tls_session_handshake(QCryptoTLSSession *sess, Error **errp) diff --git a/disas/arm-a64.cc b/disas/arm-a64.cc deleted file mode 100644 index a1402a2e07..0000000000 --- a/disas/arm-a64.cc +++ /dev/null @@ -1,101 +0,0 @@ -/* - * ARM A64 disassembly output wrapper to libvixl - * Copyright (c) 2013 Linaro Limited - * Written by Claudio Fontana - * - * 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 "disas/dis-asm.h" - -#include "vixl/a64/disasm-a64.h" - -using namespace vixl; - -static Decoder *vixl_decoder = NULL; -static Disassembler *vixl_disasm = NULL; - -/* We don't use libvixl's PrintDisassembler because its output - * is a little unhelpful (trailing newlines, for example). - * Instead we use our own very similar variant so we have - * control over the format. - */ -class QEMUDisassembler : public Disassembler { -public: - QEMUDisassembler() : printf_(NULL), stream_(NULL) { } - ~QEMUDisassembler() { } - - void SetStream(FILE *stream) { - stream_ = stream; - } - - void SetPrintf(fprintf_function printf_fn) { - printf_ = printf_fn; - } - -protected: - virtual void ProcessOutput(const Instruction *instr) { - printf_(stream_, "%08" PRIx32 " %s", - instr->InstructionBits(), GetOutput()); - } - -private: - fprintf_function printf_; - FILE *stream_; -}; - -static int vixl_is_initialized(void) -{ - return vixl_decoder != NULL; -} - -static void vixl_init() { - vixl_decoder = new Decoder(); - vixl_disasm = new QEMUDisassembler(); - vixl_decoder->AppendVisitor(vixl_disasm); -} - -#define INSN_SIZE 4 - -/* Disassemble ARM A64 instruction. This is our only entry - * point from QEMU's C code. - */ -int print_insn_arm_a64(uint64_t addr, disassemble_info *info) -{ - uint8_t bytes[INSN_SIZE]; - uint32_t instrval; - const Instruction *instr; - int status; - - status = info->read_memory_func(addr, bytes, INSN_SIZE, info); - if (status != 0) { - info->memory_error_func(status, addr, info); - return -1; - } - - if (!vixl_is_initialized()) { - vixl_init(); - } - - ((QEMUDisassembler *)vixl_disasm)->SetPrintf(info->fprintf_func); - ((QEMUDisassembler *)vixl_disasm)->SetStream(info->stream); - - instrval = bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24; - instr = reinterpret_cast(&instrval); - vixl_disasm->MapCodeAddress(addr, instr); - vixl_decoder->Decode(instr); - - return INSN_SIZE; -} diff --git a/disas/capstone.c b/disas/capstone.c index 20bc8f9669..fe3efb0d3c 100644 --- a/disas/capstone.c +++ b/disas/capstone.c @@ -191,37 +191,43 @@ bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size) size_t tsize = MIN(sizeof(cap_buf) - csize, size); const uint8_t *cbuf = cap_buf; - info->read_memory_func(pc + csize, cap_buf + csize, tsize, info); - csize += tsize; - size -= tsize; + if (info->read_memory_func(pc + csize, cap_buf + csize, tsize, info) == 0) { + csize += tsize; + size -= tsize; - while (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) { - cap_dump_insn(info, insn); - } + while (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) { + cap_dump_insn(info, insn); + } + + /* If the target memory is not consumed, go back for more... */ + if (size != 0) { + /* + * ... taking care to move any remaining fractional insn + * to the beginning of the buffer. + */ + if (csize != 0) { + memmove(cap_buf, cbuf, csize); + } + continue; + } - /* If the target memory is not consumed, go back for more... */ - if (size != 0) { /* - * ... taking care to move any remaining fractional insn - * to the beginning of the buffer. + * Since the target memory is consumed, we should not have + * a remaining fractional insn. */ if (csize != 0) { - memmove(cap_buf, cbuf, csize); + info->fprintf_func(info->stream, + "Disassembler disagrees with translator " + "over instruction decoding\n" + "Please report this to qemu-devel@nongnu.org\n"); } - continue; - } + break; - /* - * Since the target memory is consumed, we should not have - * a remaining fractional insn. - */ - if (csize != 0) { + } else { info->fprintf_func(info->stream, - "Disassembler disagrees with translator " - "over instruction decoding\n" - "Please report this to qemu-devel@nongnu.org\n"); + "0x%08" PRIx64 ": unable to read memory\n", pc); + break; } - break; } cs_close(&handle); @@ -286,16 +292,23 @@ bool cap_disas_monitor(disassemble_info *info, uint64_t pc, int count) /* Make certain that we can make progress. */ assert(tsize != 0); - info->read_memory_func(pc + csize, cap_buf + csize, tsize, info); - csize += tsize; + if (info->read_memory_func(pc + csize, cap_buf + csize, + tsize, info) == 0) + { + csize += tsize; - if (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) { - cap_dump_insn(info, insn); - if (--count <= 0) { - break; + if (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) { + cap_dump_insn(info, insn); + if (--count <= 0) { + break; + } } + memmove(cap_buf, cbuf, csize); + } else { + info->fprintf_func(info->stream, + "0x%08" PRIx64 ": unable to read memory\n", pc); + break; } - memmove(cap_buf, cbuf, csize); } cs_close(&handle); diff --git a/disas/cris.c b/disas/cris.c index 0b0a3fb916..409a224c5d 100644 --- a/disas/cris.c +++ b/disas/cris.c @@ -1731,10 +1731,10 @@ format_hex (unsigned long number, unsigned (== 0). */ static char * -format_dec (long number, char *outbuffer, int signedp) +format_dec (long number, char *outbuffer, size_t outsize, int signedp) { last_immediate = number; - sprintf (outbuffer, signedp ? "%ld" : "%lu", number); + snprintf (outbuffer, outsize, signedp ? "%ld" : "%lu", number); return outbuffer + strlen (outbuffer); } @@ -1876,6 +1876,12 @@ print_flags (struct cris_disasm_data *disdata, unsigned int insn, char *cp) return cp; } +#define FORMAT_DEC(number, tp, signedp) \ + format_dec (number, tp, ({ \ + assert(tp >= temp && tp <= temp + sizeof(temp)); \ + temp + sizeof(temp) - tp; \ + }), signedp) + /* Print out an insn with its operands, and update the info->insn_type fields. The prefix_opcodep and the rest hold a prefix insn that is supposed to be output as an address mode. */ @@ -2105,7 +2111,7 @@ print_with_operands (const struct cris_opcode *opcodep, if ((*cs == 'z' && (insn & 0x20)) || (opcodep->match == BDAP_QUICK_OPCODE && (nbytes <= 2 || buffer[1 + nbytes] == 0))) - tp = format_dec (number, tp, signedp); + tp = FORMAT_DEC (number, tp, signedp); else { unsigned int highbyte = (number >> 24) & 0xff; @@ -2241,7 +2247,7 @@ print_with_operands (const struct cris_opcode *opcodep, with_reg_prefix); if (number >= 0) *tp++ = '+'; - tp = format_dec (number, tp, 1); + tp = FORMAT_DEC (number, tp, 1); info->flags |= CRIS_DIS_FLAG_MEM_TARGET_IS_REG; info->target = (prefix_insn >> 12) & 15; @@ -2340,7 +2346,7 @@ print_with_operands (const struct cris_opcode *opcodep, { if (number >= 0) *tp++ = '+'; - tp = format_dec (number, tp, 1); + tp = FORMAT_DEC (number, tp, 1); } } else @@ -2397,7 +2403,7 @@ print_with_operands (const struct cris_opcode *opcodep, break; case 'I': - tp = format_dec (insn & 63, tp, 0); + tp = FORMAT_DEC (insn & 63, tp, 0); break; case 'b': @@ -2426,11 +2432,11 @@ print_with_operands (const struct cris_opcode *opcodep, break; case 'c': - tp = format_dec (insn & 31, tp, 0); + tp = FORMAT_DEC (insn & 31, tp, 0); break; case 'C': - tp = format_dec (insn & 15, tp, 0); + tp = FORMAT_DEC (insn & 15, tp, 0); break; case 'o': @@ -2463,7 +2469,7 @@ print_with_operands (const struct cris_opcode *opcodep, if (number > 127) number = number - 256; - tp = format_dec (number, tp, 1); + tp = FORMAT_DEC (number, tp, 1); *tp++ = ','; tp = format_reg (disdata, (insn >> 12) & 15, tp, with_reg_prefix); } @@ -2474,7 +2480,7 @@ print_with_operands (const struct cris_opcode *opcodep, break; case 'i': - tp = format_dec ((insn & 32) ? (insn & 31) | ~31L : insn & 31, tp, 1); + tp = FORMAT_DEC ((insn & 32) ? (insn & 31) | ~31L : insn & 31, tp, 1); break; case 'P': diff --git a/disas/disas-internal.h b/disas/disas-internal.h new file mode 100644 index 0000000000..84a01f126f --- /dev/null +++ b/disas/disas-internal.h @@ -0,0 +1,21 @@ +/* + * Definitions used internally in the disassembly code + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DISAS_INTERNAL_H +#define DISAS_INTERNAL_H + +#include "disas/dis-asm.h" + +typedef struct CPUDebug { + struct disassemble_info info; + CPUState *cpu; +} CPUDebug; + +void disas_initialize_debug_target(CPUDebug *s, CPUState *cpu); +int disas_gstring_printf(FILE *stream, const char *fmt, ...) + G_GNUC_PRINTF(2, 3); + +#endif diff --git a/disas/disas-mon.c b/disas/disas-mon.c new file mode 100644 index 0000000000..5d6d9aa02d --- /dev/null +++ b/disas/disas-mon.c @@ -0,0 +1,66 @@ +/* + * Functions related to disassembly from the monitor + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "disas-internal.h" +#include "disas/disas.h" +#include "exec/memory.h" +#include "hw/core/cpu.h" +#include "monitor/monitor.h" + +static int +physical_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info) +{ + CPUDebug *s = container_of(info, CPUDebug, info); + MemTxResult res; + + res = address_space_read(s->cpu->as, memaddr, MEMTXATTRS_UNSPECIFIED, + myaddr, length); + return res == MEMTX_OK ? 0 : EIO; +} + +/* Disassembler for the monitor. */ +void monitor_disas(Monitor *mon, CPUState *cpu, uint64_t pc, + int nb_insn, bool is_physical) +{ + int count, i; + CPUDebug s; + g_autoptr(GString) ds = g_string_new(""); + + disas_initialize_debug_target(&s, cpu); + s.info.fprintf_func = disas_gstring_printf; + s.info.stream = (FILE *)ds; /* abuse this slot */ + s.info.show_opcodes = true; + + if (is_physical) { + s.info.read_memory_func = physical_read_memory; + } + s.info.buffer_vma = pc; + + if (s.info.cap_arch >= 0 && cap_disas_monitor(&s.info, pc, nb_insn)) { + monitor_puts(mon, ds->str); + return; + } + + if (!s.info.print_insn) { + monitor_printf(mon, "0x%08" PRIx64 + ": Asm output not supported on this arch\n", pc); + return; + } + + for (i = 0; i < nb_insn; i++) { + g_string_append_printf(ds, "0x%08" PRIx64 ": ", pc); + count = s.info.print_insn(pc, &s.info); + g_string_append_c(ds, '\n'); + if (count < 0) { + break; + } + pc += count; + } + + monitor_puts(mon, ds->str); +} diff --git a/disas.c b/disas/disas.c similarity index 76% rename from disas.c rename to disas/disas.c index 11d4ce08a8..bf92eaee29 100644 --- a/disas.c +++ b/disas/disas.c @@ -1,16 +1,12 @@ /* General "disassemble this chunk" code. Used for debugging. */ #include "qemu/osdep.h" -#include "disas/dis-asm.h" +#include "disas/disas-internal.h" #include "elf.h" #include "qemu/qemu-print.h" - #include "disas/disas.h" #include "disas/capstone.h" - -typedef struct CPUDebug { - struct disassemble_info info; - CPUState *cpu; -} CPUDebug; +#include "hw/core/cpu.h" +#include "exec/memory.h" /* Filled in by elfload.c. Simplistic, but will do for now. */ struct syminfo *syminfos = NULL; @@ -83,18 +79,18 @@ static int print_insn_objdump(bfd_vma pc, disassemble_info *info, const char *prefix) { int i, n = info->buffer_length; - uint8_t *buf = g_malloc(n); + g_autofree uint8_t *buf = g_malloc(n); - info->read_memory_func(pc, buf, n, info); - - for (i = 0; i < n; ++i) { - if (i % 32 == 0) { - info->fprintf_func(info->stream, "\n%s: ", prefix); + if (info->read_memory_func(pc, buf, n, info) == 0) { + for (i = 0; i < n; ++i) { + if (i % 32 == 0) { + info->fprintf_func(info->stream, "\n%s: ", prefix); + } + info->fprintf_func(info->stream, "%02x", buf[i]); } - info->fprintf_func(info->stream, "%02x", buf[i]); + } else { + info->fprintf_func(info->stream, "unable to read memory"); } - - g_free(buf); return n; } @@ -119,18 +115,18 @@ static void initialize_debug(CPUDebug *s) s->info.symbol_at_address_func = symbol_at_address; } -static void initialize_debug_target(CPUDebug *s, CPUState *cpu) +void disas_initialize_debug_target(CPUDebug *s, CPUState *cpu) { initialize_debug(s); s->cpu = cpu; s->info.read_memory_func = target_read_memory; s->info.print_address_func = print_address; -#if TARGET_BIG_ENDIAN - s->info.endian = BFD_ENDIAN_BIG; -#else - s->info.endian = BFD_ENDIAN_LITTLE; -#endif + if (target_words_bigendian()) { + s->info.endian = BFD_ENDIAN_BIG; + } else { + s->info.endian = BFD_ENDIAN_LITTLE; + } CPUClass *cc = CPU_GET_CLASS(cpu); if (cc->disas_set_info) { @@ -168,7 +164,7 @@ static void initialize_debug_host(CPUDebug *s) # ifdef _ARCH_PPC64 s->info.cap_mode = CS_MODE_64; # endif -#elif defined(__riscv) && defined(CONFIG_RISCV_DIS) +#elif defined(__riscv) #if defined(_ILP32) || (__riscv_xlen == 32) s->info.print_insn = print_insn_riscv32; #elif defined(_LP64) @@ -178,9 +174,6 @@ static void initialize_debug_host(CPUDebug *s) #endif #elif defined(__aarch64__) s->info.cap_arch = CS_ARCH_ARM64; -# ifdef CONFIG_ARM_A64_DIS - s->info.print_insn = print_insn_arm_a64; -# endif #elif defined(__alpha__) s->info.print_insn = print_insn_alpha; #elif defined(__sparc__) @@ -201,24 +194,26 @@ static void initialize_debug_host(CPUDebug *s) s->info.cap_insn_split = 6; #elif defined(__hppa__) s->info.print_insn = print_insn_hppa; +#elif defined(__loongarch__) + s->info.print_insn = print_insn_loongarch; #elif defined(__e2k__) s->info.print_insn = print_insn_e2k; #endif } /* Disassemble this for me please... (debugging). */ -void target_disas(FILE *out, CPUState *cpu, target_ulong code, - target_ulong size) +void target_disas(FILE *out, CPUState *cpu, uint64_t code, size_t size) { - target_ulong pc; + uint64_t pc; int count; CPUDebug s; - initialize_debug_target(&s, cpu); + disas_initialize_debug_target(&s, cpu); s.info.fprintf_func = fprintf; s.info.stream = out; s.info.buffer_vma = code; s.info.buffer_length = size; + s.info.show_opcodes = true; if (s.info.cap_arch >= 0 && cap_disas_target(&s.info, code, size)) { return; @@ -229,11 +224,12 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code, } for (pc = code; size > 0; pc += count, size -= count) { - fprintf(out, "0x" TARGET_FMT_lx ": ", pc); - count = s.info.print_insn(pc, &s.info); - fprintf(out, "\n"); - if (count < 0) - break; + fprintf(out, "0x%08" PRIx64 ": ", pc); + count = s.info.print_insn(pc, &s.info); + fprintf(out, "\n"); + if (count < 0) { + break; + } if (size < count) { fprintf(out, "Disassembler disagrees with translator over instruction " @@ -244,7 +240,7 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code, } } -static int plugin_printf(FILE *stream, const char *fmt, ...) +int disas_gstring_printf(FILE *stream, const char *fmt, ...) { /* We abuse the FILE parameter to pass a GString. */ GString *s = (GString *)stream; @@ -274,8 +270,8 @@ char *plugin_disas(CPUState *cpu, uint64_t addr, size_t size) CPUDebug s; GString *ds = g_string_new(NULL); - initialize_debug_target(&s, cpu); - s.info.fprintf_func = plugin_printf; + disas_initialize_debug_target(&s, cpu); + s.info.fprintf_func = disas_gstring_printf; s.info.stream = (FILE *)ds; /* abuse this slot */ s.info.buffer_vma = addr; s.info.buffer_length = size; @@ -294,7 +290,7 @@ char *plugin_disas(CPUState *cpu, uint64_t addr, size_t size) } /* Disassemble this for me please... (debugging). */ -void disas(FILE *out, const void *code, unsigned long size) +void disas(FILE *out, const void *code, size_t size) { uintptr_t pc; int count; @@ -306,6 +302,7 @@ void disas(FILE *out, const void *code, unsigned long size) s.info.buffer = code; s.info.buffer_vma = (uintptr_t)code; s.info.buffer_length = size; + s.info.show_opcodes = true; if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size)) { return; @@ -326,7 +323,7 @@ void disas(FILE *out, const void *code, unsigned long size) } /* Look up symbol for debugging purpose. Returns "" if unknown. */ -const char *lookup_symbol(target_ulong orig_addr) +const char *lookup_symbol(uint64_t orig_addr) { const char *symbol = ""; struct syminfo *s; @@ -340,54 +337,3 @@ const char *lookup_symbol(target_ulong orig_addr) return symbol; } - -#if !defined(CONFIG_USER_ONLY) - -#include "monitor/monitor.h" - -static int -physical_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, - struct disassemble_info *info) -{ - CPUDebug *s = container_of(info, CPUDebug, info); - MemTxResult res; - - res = address_space_read(s->cpu->as, memaddr, MEMTXATTRS_UNSPECIFIED, - myaddr, length); - return res == MEMTX_OK ? 0 : EIO; -} - -/* Disassembler for the monitor. */ -void monitor_disas(Monitor *mon, CPUState *cpu, - target_ulong pc, int nb_insn, int is_physical) -{ - int count, i; - CPUDebug s; - - initialize_debug_target(&s, cpu); - s.info.fprintf_func = qemu_fprintf; - if (is_physical) { - s.info.read_memory_func = physical_read_memory; - } - s.info.buffer_vma = pc; - - if (s.info.cap_arch >= 0 && cap_disas_monitor(&s.info, pc, nb_insn)) { - return; - } - - if (!s.info.print_insn) { - monitor_printf(mon, "0x" TARGET_FMT_lx - ": Asm output not supported on this arch\n", pc); - return; - } - - for(i = 0; i < nb_insn; i++) { - monitor_printf(mon, "0x" TARGET_FMT_lx ": ", pc); - count = s.info.print_insn(pc, &s.info); - monitor_printf(mon, "\n"); - if (count < 0) - break; - pc += count; - } -} -#endif diff --git a/disas/hppa.c b/disas/hppa.c index dcf9a47f34..49e2231ae6 100644 --- a/disas/hppa.c +++ b/disas/hppa.c @@ -1609,6 +1609,10 @@ static const struct pa_opcode pa_opcodes[] = { "call", 0xe800a000, 0xffe0e000, "nW", pa10, FLAG_STRICT}, { "ret", 0xe840d000, 0xfffffffd, "n", pa20, FLAG_STRICT}, +/* Opcodes assigned to QEMU, used by SeaBIOS firmware and Linux kernel */ +{ "HALT QEMU", 0xfffdead0, 0xfffffffd, "n", pa10, FLAG_STRICT}, +{ "RESET QEMU", 0xfffdead1, 0xfffffffd, "n", pa10, FLAG_STRICT}, +{ "RESTORE SHR",0xfffdead2, 0xfffffffd, "n", pa10, FLAG_STRICT}, }; #define NUMOPCODES ((sizeof pa_opcodes)/(sizeof pa_opcodes[0])) @@ -1968,6 +1972,12 @@ print_insn_hppa (bfd_vma memaddr, disassemble_info *info) insn = bfd_getb32 (buffer); + if (info->show_opcodes) { + info->fprintf_func(info->stream, " %02x %02x %02x %02x ", + (insn >> 24) & 0xff, (insn >> 16) & 0xff, + (insn >> 8) & 0xff, insn & 0xff); + } + for (i = 0; i < NUMOPCODES; ++i) { const struct pa_opcode *opcode = &pa_opcodes[i]; @@ -2826,6 +2836,6 @@ print_insn_hppa (bfd_vma memaddr, disassemble_info *info) return sizeof (insn); } } - (*info->fprintf_func) (info->stream, "#%8x", insn); + info->fprintf_func(info->stream, ""); return sizeof (insn); } diff --git a/disas/libvixl/LICENCE b/disas/libvixl/LICENCE deleted file mode 100644 index b7e160a3f5..0000000000 --- a/disas/libvixl/LICENCE +++ /dev/null @@ -1,30 +0,0 @@ -LICENCE -======= - -The software in this repository is covered by the following licence. - -// Copyright 2013, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. diff --git a/disas/libvixl/README b/disas/libvixl/README deleted file mode 100644 index 932a41adf7..0000000000 --- a/disas/libvixl/README +++ /dev/null @@ -1,11 +0,0 @@ - -The code in this directory is a subset of libvixl: - https://github.com/armvixl/vixl -(specifically, it is the set of files needed for disassembly only, -taken from libvixl 1.12). -Bugfixes should preferably be sent upstream initially. - -The disassembler does not currently support the entire A64 instruction -set. Notably: - * Limited support for system instructions. - * A few miscellaneous integer and floating point instructions are missing. diff --git a/disas/libvixl/meson.build b/disas/libvixl/meson.build deleted file mode 100644 index 5e2eb33e8e..0000000000 --- a/disas/libvixl/meson.build +++ /dev/null @@ -1,7 +0,0 @@ -libvixl_ss.add(files( - 'vixl/a64/decoder-a64.cc', - 'vixl/a64/disasm-a64.cc', - 'vixl/a64/instructions-a64.cc', - 'vixl/compiler-intrinsics.cc', - 'vixl/utils.cc', -)) diff --git a/disas/libvixl/vixl/a64/assembler-a64.h b/disas/libvixl/vixl/a64/assembler-a64.h deleted file mode 100644 index fda5ccc6c7..0000000000 --- a/disas/libvixl/vixl/a64/assembler-a64.h +++ /dev/null @@ -1,4624 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#ifndef VIXL_A64_ASSEMBLER_A64_H_ -#define VIXL_A64_ASSEMBLER_A64_H_ - - -#include "vixl/globals.h" -#include "vixl/invalset.h" -#include "vixl/utils.h" -#include "vixl/code-buffer.h" -#include "vixl/a64/instructions-a64.h" - -namespace vixl { - -typedef uint64_t RegList; -static const int kRegListSizeInBits = sizeof(RegList) * 8; - - -// Registers. - -// Some CPURegister methods can return Register or VRegister types, so we need -// to declare them in advance. -class Register; -class VRegister; - -class CPURegister { - public: - enum RegisterType { - // The kInvalid value is used to detect uninitialized static instances, - // which are always zero-initialized before any constructors are called. - kInvalid = 0, - kRegister, - kVRegister, - kFPRegister = kVRegister, - kNoRegister - }; - - CPURegister() : code_(0), size_(0), type_(kNoRegister) { - VIXL_ASSERT(!IsValid()); - VIXL_ASSERT(IsNone()); - } - - CPURegister(unsigned code, unsigned size, RegisterType type) - : code_(code), size_(size), type_(type) { - VIXL_ASSERT(IsValidOrNone()); - } - - unsigned code() const { - VIXL_ASSERT(IsValid()); - return code_; - } - - RegisterType type() const { - VIXL_ASSERT(IsValidOrNone()); - return type_; - } - - RegList Bit() const { - VIXL_ASSERT(code_ < (sizeof(RegList) * 8)); - return IsValid() ? (static_cast(1) << code_) : 0; - } - - unsigned size() const { - VIXL_ASSERT(IsValid()); - return size_; - } - - int SizeInBytes() const { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(size() % 8 == 0); - return size_ / 8; - } - - int SizeInBits() const { - VIXL_ASSERT(IsValid()); - return size_; - } - - bool Is8Bits() const { - VIXL_ASSERT(IsValid()); - return size_ == 8; - } - - bool Is16Bits() const { - VIXL_ASSERT(IsValid()); - return size_ == 16; - } - - bool Is32Bits() const { - VIXL_ASSERT(IsValid()); - return size_ == 32; - } - - bool Is64Bits() const { - VIXL_ASSERT(IsValid()); - return size_ == 64; - } - - bool Is128Bits() const { - VIXL_ASSERT(IsValid()); - return size_ == 128; - } - - bool IsValid() const { - if (IsValidRegister() || IsValidVRegister()) { - VIXL_ASSERT(!IsNone()); - return true; - } else { - // This assert is hit when the register has not been properly initialized. - // One cause for this can be an initialisation order fiasco. See - // https://isocpp.org/wiki/faq/ctors#static-init-order for some details. - VIXL_ASSERT(IsNone()); - return false; - } - } - - bool IsValidRegister() const { - return IsRegister() && - ((size_ == kWRegSize) || (size_ == kXRegSize)) && - ((code_ < kNumberOfRegisters) || (code_ == kSPRegInternalCode)); - } - - bool IsValidVRegister() const { - return IsVRegister() && - ((size_ == kBRegSize) || (size_ == kHRegSize) || - (size_ == kSRegSize) || (size_ == kDRegSize) || - (size_ == kQRegSize)) && - (code_ < kNumberOfVRegisters); - } - - bool IsValidFPRegister() const { - return IsFPRegister() && (code_ < kNumberOfVRegisters); - } - - bool IsNone() const { - // kNoRegister types should always have size 0 and code 0. - VIXL_ASSERT((type_ != kNoRegister) || (code_ == 0)); - VIXL_ASSERT((type_ != kNoRegister) || (size_ == 0)); - - return type_ == kNoRegister; - } - - bool Aliases(const CPURegister& other) const { - VIXL_ASSERT(IsValidOrNone() && other.IsValidOrNone()); - return (code_ == other.code_) && (type_ == other.type_); - } - - bool Is(const CPURegister& other) const { - VIXL_ASSERT(IsValidOrNone() && other.IsValidOrNone()); - return Aliases(other) && (size_ == other.size_); - } - - bool IsZero() const { - VIXL_ASSERT(IsValid()); - return IsRegister() && (code_ == kZeroRegCode); - } - - bool IsSP() const { - VIXL_ASSERT(IsValid()); - return IsRegister() && (code_ == kSPRegInternalCode); - } - - bool IsRegister() const { - return type_ == kRegister; - } - - bool IsVRegister() const { - return type_ == kVRegister; - } - - bool IsFPRegister() const { - return IsS() || IsD(); - } - - bool IsW() const { return IsValidRegister() && Is32Bits(); } - bool IsX() const { return IsValidRegister() && Is64Bits(); } - - // These assertions ensure that the size and type of the register are as - // described. They do not consider the number of lanes that make up a vector. - // So, for example, Is8B() implies IsD(), and Is1D() implies IsD, but IsD() - // does not imply Is1D() or Is8B(). - // Check the number of lanes, ie. the format of the vector, using methods such - // as Is8B(), Is1D(), etc. in the VRegister class. - bool IsV() const { return IsVRegister(); } - bool IsB() const { return IsV() && Is8Bits(); } - bool IsH() const { return IsV() && Is16Bits(); } - bool IsS() const { return IsV() && Is32Bits(); } - bool IsD() const { return IsV() && Is64Bits(); } - bool IsQ() const { return IsV() && Is128Bits(); } - - const Register& W() const; - const Register& X() const; - const VRegister& V() const; - const VRegister& B() const; - const VRegister& H() const; - const VRegister& S() const; - const VRegister& D() const; - const VRegister& Q() const; - - bool IsSameSizeAndType(const CPURegister& other) const { - return (size_ == other.size_) && (type_ == other.type_); - } - - protected: - unsigned code_; - unsigned size_; - RegisterType type_; - - private: - bool IsValidOrNone() const { - return IsValid() || IsNone(); - } -}; - - -class Register : public CPURegister { - public: - Register() : CPURegister() {} - explicit Register(const CPURegister& other) - : CPURegister(other.code(), other.size(), other.type()) { - VIXL_ASSERT(IsValidRegister()); - } - Register(unsigned code, unsigned size) - : CPURegister(code, size, kRegister) {} - - bool IsValid() const { - VIXL_ASSERT(IsRegister() || IsNone()); - return IsValidRegister(); - } - - static const Register& WRegFromCode(unsigned code); - static const Register& XRegFromCode(unsigned code); - - private: - static const Register wregisters[]; - static const Register xregisters[]; -}; - - -class VRegister : public CPURegister { - public: - VRegister() : CPURegister(), lanes_(1) {} - explicit VRegister(const CPURegister& other) - : CPURegister(other.code(), other.size(), other.type()), lanes_(1) { - VIXL_ASSERT(IsValidVRegister()); - VIXL_ASSERT(IsPowerOf2(lanes_) && (lanes_ <= 16)); - } - VRegister(unsigned code, unsigned size, unsigned lanes = 1) - : CPURegister(code, size, kVRegister), lanes_(lanes) { - VIXL_ASSERT(IsPowerOf2(lanes_) && (lanes_ <= 16)); - } - VRegister(unsigned code, VectorFormat format) - : CPURegister(code, RegisterSizeInBitsFromFormat(format), kVRegister), - lanes_(IsVectorFormat(format) ? LaneCountFromFormat(format) : 1) { - VIXL_ASSERT(IsPowerOf2(lanes_) && (lanes_ <= 16)); - } - - bool IsValid() const { - VIXL_ASSERT(IsVRegister() || IsNone()); - return IsValidVRegister(); - } - - static const VRegister& BRegFromCode(unsigned code); - static const VRegister& HRegFromCode(unsigned code); - static const VRegister& SRegFromCode(unsigned code); - static const VRegister& DRegFromCode(unsigned code); - static const VRegister& QRegFromCode(unsigned code); - static const VRegister& VRegFromCode(unsigned code); - - VRegister V8B() const { return VRegister(code_, kDRegSize, 8); } - VRegister V16B() const { return VRegister(code_, kQRegSize, 16); } - VRegister V4H() const { return VRegister(code_, kDRegSize, 4); } - VRegister V8H() const { return VRegister(code_, kQRegSize, 8); } - VRegister V2S() const { return VRegister(code_, kDRegSize, 2); } - VRegister V4S() const { return VRegister(code_, kQRegSize, 4); } - VRegister V2D() const { return VRegister(code_, kQRegSize, 2); } - VRegister V1D() const { return VRegister(code_, kDRegSize, 1); } - - bool Is8B() const { return (Is64Bits() && (lanes_ == 8)); } - bool Is16B() const { return (Is128Bits() && (lanes_ == 16)); } - bool Is4H() const { return (Is64Bits() && (lanes_ == 4)); } - bool Is8H() const { return (Is128Bits() && (lanes_ == 8)); } - bool Is2S() const { return (Is64Bits() && (lanes_ == 2)); } - bool Is4S() const { return (Is128Bits() && (lanes_ == 4)); } - bool Is1D() const { return (Is64Bits() && (lanes_ == 1)); } - bool Is2D() const { return (Is128Bits() && (lanes_ == 2)); } - - // For consistency, we assert the number of lanes of these scalar registers, - // even though there are no vectors of equivalent total size with which they - // could alias. - bool Is1B() const { - VIXL_ASSERT(!(Is8Bits() && IsVector())); - return Is8Bits(); - } - bool Is1H() const { - VIXL_ASSERT(!(Is16Bits() && IsVector())); - return Is16Bits(); - } - bool Is1S() const { - VIXL_ASSERT(!(Is32Bits() && IsVector())); - return Is32Bits(); - } - - bool IsLaneSizeB() const { return LaneSizeInBits() == kBRegSize; } - bool IsLaneSizeH() const { return LaneSizeInBits() == kHRegSize; } - bool IsLaneSizeS() const { return LaneSizeInBits() == kSRegSize; } - bool IsLaneSizeD() const { return LaneSizeInBits() == kDRegSize; } - - int lanes() const { - return lanes_; - } - - bool IsScalar() const { - return lanes_ == 1; - } - - bool IsVector() const { - return lanes_ > 1; - } - - bool IsSameFormat(const VRegister& other) const { - return (size_ == other.size_) && (lanes_ == other.lanes_); - } - - unsigned LaneSizeInBytes() const { - return SizeInBytes() / lanes_; - } - - unsigned LaneSizeInBits() const { - return LaneSizeInBytes() * 8; - } - - private: - static const VRegister bregisters[]; - static const VRegister hregisters[]; - static const VRegister sregisters[]; - static const VRegister dregisters[]; - static const VRegister qregisters[]; - static const VRegister vregisters[]; - int lanes_; -}; - - -// Backward compatibility for FPRegisters. -typedef VRegister FPRegister; - -// No*Reg is used to indicate an unused argument, or an error case. Note that -// these all compare equal (using the Is() method). The Register and VRegister -// variants are provided for convenience. -const Register NoReg; -const VRegister NoVReg; -const FPRegister NoFPReg; // For backward compatibility. -const CPURegister NoCPUReg; - - -#define DEFINE_REGISTERS(N) \ -const Register w##N(N, kWRegSize); \ -const Register x##N(N, kXRegSize); -REGISTER_CODE_LIST(DEFINE_REGISTERS) -#undef DEFINE_REGISTERS -const Register wsp(kSPRegInternalCode, kWRegSize); -const Register sp(kSPRegInternalCode, kXRegSize); - - -#define DEFINE_VREGISTERS(N) \ -const VRegister b##N(N, kBRegSize); \ -const VRegister h##N(N, kHRegSize); \ -const VRegister s##N(N, kSRegSize); \ -const VRegister d##N(N, kDRegSize); \ -const VRegister q##N(N, kQRegSize); \ -const VRegister v##N(N, kQRegSize); -REGISTER_CODE_LIST(DEFINE_VREGISTERS) -#undef DEFINE_VREGISTERS - - -// Registers aliases. -const Register ip0 = x16; -const Register ip1 = x17; -const Register lr = x30; -const Register xzr = x31; -const Register wzr = w31; - - -// AreAliased returns true if any of the named registers overlap. Arguments -// set to NoReg are ignored. The system stack pointer may be specified. -bool AreAliased(const CPURegister& reg1, - const CPURegister& reg2, - const CPURegister& reg3 = NoReg, - const CPURegister& reg4 = NoReg, - const CPURegister& reg5 = NoReg, - const CPURegister& reg6 = NoReg, - const CPURegister& reg7 = NoReg, - const CPURegister& reg8 = NoReg); - - -// AreSameSizeAndType returns true if all of the specified registers have the -// same size, and are of the same type. The system stack pointer may be -// specified. Arguments set to NoReg are ignored, as are any subsequent -// arguments. At least one argument (reg1) must be valid (not NoCPUReg). -bool AreSameSizeAndType(const CPURegister& reg1, - const CPURegister& reg2, - const CPURegister& reg3 = NoCPUReg, - const CPURegister& reg4 = NoCPUReg, - const CPURegister& reg5 = NoCPUReg, - const CPURegister& reg6 = NoCPUReg, - const CPURegister& reg7 = NoCPUReg, - const CPURegister& reg8 = NoCPUReg); - - -// AreSameFormat returns true if all of the specified VRegisters have the same -// vector format. Arguments set to NoReg are ignored, as are any subsequent -// arguments. At least one argument (reg1) must be valid (not NoVReg). -bool AreSameFormat(const VRegister& reg1, - const VRegister& reg2, - const VRegister& reg3 = NoVReg, - const VRegister& reg4 = NoVReg); - - -// AreConsecutive returns true if all of the specified VRegisters are -// consecutive in the register file. Arguments set to NoReg are ignored, as are -// any subsequent arguments. At least one argument (reg1) must be valid -// (not NoVReg). -bool AreConsecutive(const VRegister& reg1, - const VRegister& reg2, - const VRegister& reg3 = NoVReg, - const VRegister& reg4 = NoVReg); - - -// Lists of registers. -class CPURegList { - public: - explicit CPURegList(CPURegister reg1, - CPURegister reg2 = NoCPUReg, - CPURegister reg3 = NoCPUReg, - CPURegister reg4 = NoCPUReg) - : list_(reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit()), - size_(reg1.size()), type_(reg1.type()) { - VIXL_ASSERT(AreSameSizeAndType(reg1, reg2, reg3, reg4)); - VIXL_ASSERT(IsValid()); - } - - CPURegList(CPURegister::RegisterType type, unsigned size, RegList list) - : list_(list), size_(size), type_(type) { - VIXL_ASSERT(IsValid()); - } - - CPURegList(CPURegister::RegisterType type, unsigned size, - unsigned first_reg, unsigned last_reg) - : size_(size), type_(type) { - VIXL_ASSERT(((type == CPURegister::kRegister) && - (last_reg < kNumberOfRegisters)) || - ((type == CPURegister::kVRegister) && - (last_reg < kNumberOfVRegisters))); - VIXL_ASSERT(last_reg >= first_reg); - list_ = (UINT64_C(1) << (last_reg + 1)) - 1; - list_ &= ~((UINT64_C(1) << first_reg) - 1); - VIXL_ASSERT(IsValid()); - } - - CPURegister::RegisterType type() const { - VIXL_ASSERT(IsValid()); - return type_; - } - - // Combine another CPURegList into this one. Registers that already exist in - // this list are left unchanged. The type and size of the registers in the - // 'other' list must match those in this list. - void Combine(const CPURegList& other) { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(other.type() == type_); - VIXL_ASSERT(other.RegisterSizeInBits() == size_); - list_ |= other.list(); - } - - // Remove every register in the other CPURegList from this one. Registers that - // do not exist in this list are ignored. The type and size of the registers - // in the 'other' list must match those in this list. - void Remove(const CPURegList& other) { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(other.type() == type_); - VIXL_ASSERT(other.RegisterSizeInBits() == size_); - list_ &= ~other.list(); - } - - // Variants of Combine and Remove which take a single register. - void Combine(const CPURegister& other) { - VIXL_ASSERT(other.type() == type_); - VIXL_ASSERT(other.size() == size_); - Combine(other.code()); - } - - void Remove(const CPURegister& other) { - VIXL_ASSERT(other.type() == type_); - VIXL_ASSERT(other.size() == size_); - Remove(other.code()); - } - - // Variants of Combine and Remove which take a single register by its code; - // the type and size of the register is inferred from this list. - void Combine(int code) { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); - list_ |= (UINT64_C(1) << code); - } - - void Remove(int code) { - VIXL_ASSERT(IsValid()); - VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); - list_ &= ~(UINT64_C(1) << code); - } - - static CPURegList Union(const CPURegList& list_1, const CPURegList& list_2) { - VIXL_ASSERT(list_1.type_ == list_2.type_); - VIXL_ASSERT(list_1.size_ == list_2.size_); - return CPURegList(list_1.type_, list_1.size_, list_1.list_ | list_2.list_); - } - static CPURegList Union(const CPURegList& list_1, - const CPURegList& list_2, - const CPURegList& list_3); - static CPURegList Union(const CPURegList& list_1, - const CPURegList& list_2, - const CPURegList& list_3, - const CPURegList& list_4); - - static CPURegList Intersection(const CPURegList& list_1, - const CPURegList& list_2) { - VIXL_ASSERT(list_1.type_ == list_2.type_); - VIXL_ASSERT(list_1.size_ == list_2.size_); - return CPURegList(list_1.type_, list_1.size_, list_1.list_ & list_2.list_); - } - static CPURegList Intersection(const CPURegList& list_1, - const CPURegList& list_2, - const CPURegList& list_3); - static CPURegList Intersection(const CPURegList& list_1, - const CPURegList& list_2, - const CPURegList& list_3, - const CPURegList& list_4); - - bool Overlaps(const CPURegList& other) const { - return (type_ == other.type_) && ((list_ & other.list_) != 0); - } - - RegList list() const { - VIXL_ASSERT(IsValid()); - return list_; - } - - void set_list(RegList new_list) { - VIXL_ASSERT(IsValid()); - list_ = new_list; - } - - // Remove all callee-saved registers from the list. This can be useful when - // preparing registers for an AAPCS64 function call, for example. - void RemoveCalleeSaved(); - - CPURegister PopLowestIndex(); - CPURegister PopHighestIndex(); - - // AAPCS64 callee-saved registers. - static CPURegList GetCalleeSaved(unsigned size = kXRegSize); - static CPURegList GetCalleeSavedV(unsigned size = kDRegSize); - - // AAPCS64 caller-saved registers. Note that this includes lr. - // TODO(all): Determine how we handle d8-d15 being callee-saved, but the top - // 64-bits being caller-saved. - static CPURegList GetCallerSaved(unsigned size = kXRegSize); - static CPURegList GetCallerSavedV(unsigned size = kDRegSize); - - bool IsEmpty() const { - VIXL_ASSERT(IsValid()); - return list_ == 0; - } - - bool IncludesAliasOf(const CPURegister& other) const { - VIXL_ASSERT(IsValid()); - return (type_ == other.type()) && ((other.Bit() & list_) != 0); - } - - bool IncludesAliasOf(int code) const { - VIXL_ASSERT(IsValid()); - return ((code & list_) != 0); - } - - int Count() const { - VIXL_ASSERT(IsValid()); - return CountSetBits(list_); - } - - unsigned RegisterSizeInBits() const { - VIXL_ASSERT(IsValid()); - return size_; - } - - unsigned RegisterSizeInBytes() const { - int size_in_bits = RegisterSizeInBits(); - VIXL_ASSERT((size_in_bits % 8) == 0); - return size_in_bits / 8; - } - - unsigned TotalSizeInBytes() const { - VIXL_ASSERT(IsValid()); - return RegisterSizeInBytes() * Count(); - } - - private: - RegList list_; - unsigned size_; - CPURegister::RegisterType type_; - - bool IsValid() const; -}; - - -// AAPCS64 callee-saved registers. -extern const CPURegList kCalleeSaved; -extern const CPURegList kCalleeSavedV; - - -// AAPCS64 caller-saved registers. Note that this includes lr. -extern const CPURegList kCallerSaved; -extern const CPURegList kCallerSavedV; - - -// Operand. -class Operand { - public: - // # - // where is int64_t. - // This is allowed to be an implicit constructor because Operand is - // a wrapper class that doesn't normally perform any type conversion. - Operand(int64_t immediate = 0); // NOLINT(runtime/explicit) - - // rm, { #} - // where is one of {LSL, LSR, ASR, ROR}. - // is uint6_t. - // This is allowed to be an implicit constructor because Operand is - // a wrapper class that doesn't normally perform any type conversion. - Operand(Register reg, - Shift shift = LSL, - unsigned shift_amount = 0); // NOLINT(runtime/explicit) - - // rm, { {#}} - // where is one of {UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW, SXTX}. - // is uint2_t. - explicit Operand(Register reg, Extend extend, unsigned shift_amount = 0); - - bool IsImmediate() const; - bool IsShiftedRegister() const; - bool IsExtendedRegister() const; - bool IsZero() const; - - // This returns an LSL shift (<= 4) operand as an equivalent extend operand, - // which helps in the encoding of instructions that use the stack pointer. - Operand ToExtendedRegister() const; - - int64_t immediate() const { - VIXL_ASSERT(IsImmediate()); - return immediate_; - } - - Register reg() const { - VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister()); - return reg_; - } - - Shift shift() const { - VIXL_ASSERT(IsShiftedRegister()); - return shift_; - } - - Extend extend() const { - VIXL_ASSERT(IsExtendedRegister()); - return extend_; - } - - unsigned shift_amount() const { - VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister()); - return shift_amount_; - } - - private: - int64_t immediate_; - Register reg_; - Shift shift_; - Extend extend_; - unsigned shift_amount_; -}; - - -// MemOperand represents the addressing mode of a load or store instruction. -class MemOperand { - public: - explicit MemOperand(Register base, - int64_t offset = 0, - AddrMode addrmode = Offset); - MemOperand(Register base, - Register regoffset, - Shift shift = LSL, - unsigned shift_amount = 0); - MemOperand(Register base, - Register regoffset, - Extend extend, - unsigned shift_amount = 0); - MemOperand(Register base, - const Operand& offset, - AddrMode addrmode = Offset); - - const Register& base() const { return base_; } - const Register& regoffset() const { return regoffset_; } - int64_t offset() const { return offset_; } - AddrMode addrmode() const { return addrmode_; } - Shift shift() const { return shift_; } - Extend extend() const { return extend_; } - unsigned shift_amount() const { return shift_amount_; } - bool IsImmediateOffset() const; - bool IsRegisterOffset() const; - bool IsPreIndex() const; - bool IsPostIndex() const; - - void AddOffset(int64_t offset); - - private: - Register base_; - Register regoffset_; - int64_t offset_; - AddrMode addrmode_; - Shift shift_; - Extend extend_; - unsigned shift_amount_; -}; - - -class LabelTestHelper; // Forward declaration. - - -class Label { - public: - Label() : location_(kLocationUnbound) {} - ~Label() { - // If the label has been linked to, it needs to be bound to a target. - VIXL_ASSERT(!IsLinked() || IsBound()); - } - - bool IsBound() const { return location_ >= 0; } - bool IsLinked() const { return !links_.empty(); } - - ptrdiff_t location() const { return location_; } - - static const int kNPreallocatedLinks = 4; - static const ptrdiff_t kInvalidLinkKey = PTRDIFF_MAX; - static const size_t kReclaimFrom = 512; - static const size_t kReclaimFactor = 2; - - typedef InvalSet LinksSetBase; - typedef InvalSetIterator LabelLinksIteratorBase; - - private: - class LinksSet : public LinksSetBase { - public: - LinksSet() : LinksSetBase() {} - }; - - // Allows iterating over the links of a label. The behaviour is undefined if - // the list of links is modified in any way while iterating. - class LabelLinksIterator : public LabelLinksIteratorBase { - public: - explicit LabelLinksIterator(Label* label) - : LabelLinksIteratorBase(&label->links_) {} - }; - - void Bind(ptrdiff_t location) { - // Labels can only be bound once. - VIXL_ASSERT(!IsBound()); - location_ = location; - } - - void AddLink(ptrdiff_t instruction) { - // If a label is bound, the assembler already has the information it needs - // to write the instruction, so there is no need to add it to links_. - VIXL_ASSERT(!IsBound()); - links_.insert(instruction); - } - - void DeleteLink(ptrdiff_t instruction) { - links_.erase(instruction); - } - - void ClearAllLinks() { - links_.clear(); - } - - // TODO: The comment below considers average case complexity for our - // usual use-cases. The elements of interest are: - // - Branches to a label are emitted in order: branch instructions to a label - // are generated at an offset in the code generation buffer greater than any - // other branch to that same label already generated. As an example, this can - // be broken when an instruction is patched to become a branch. Note that the - // code will still work, but the complexity considerations below may locally - // not apply any more. - // - Veneers are generated in order: for multiple branches of the same type - // branching to the same unbound label going out of range, veneers are - // generated in growing order of the branch instruction offset from the start - // of the buffer. - // - // When creating a veneer for a branch going out of range, the link for this - // branch needs to be removed from this `links_`. Since all branches are - // tracked in one underlying InvalSet, the complexity for this deletion is the - // same as for finding the element, ie. O(n), where n is the number of links - // in the set. - // This could be reduced to O(1) by using the same trick as used when tracking - // branch information for veneers: split the container to use one set per type - // of branch. With that setup, when a veneer is created and the link needs to - // be deleted, if the two points above hold, it must be the minimum element of - // the set for its type of branch, and that minimum element will be accessible - // in O(1). - - // The offsets of the instructions that have linked to this label. - LinksSet links_; - // The label location. - ptrdiff_t location_; - - static const ptrdiff_t kLocationUnbound = -1; - - // It is not safe to copy labels, so disable the copy constructor and operator - // by declaring them private (without an implementation). - Label(const Label&); - void operator=(const Label&); - - // The Assembler class is responsible for binding and linking labels, since - // the stored offsets need to be consistent with the Assembler's buffer. - friend class Assembler; - // The MacroAssembler and VeneerPool handle resolution of branches to distant - // targets. - friend class MacroAssembler; - friend class VeneerPool; -}; - - -// Required InvalSet template specialisations. -#define INVAL_SET_TEMPLATE_PARAMETERS \ - ptrdiff_t, \ - Label::kNPreallocatedLinks, \ - ptrdiff_t, \ - Label::kInvalidLinkKey, \ - Label::kReclaimFrom, \ - Label::kReclaimFactor -template<> -inline ptrdiff_t InvalSet::Key( - const ptrdiff_t& element) { - return element; -} -template<> -inline void InvalSet::SetKey( - ptrdiff_t* element, ptrdiff_t key) { - *element = key; -} -#undef INVAL_SET_TEMPLATE_PARAMETERS - - -class Assembler; -class LiteralPool; - -// A literal is a 32-bit or 64-bit piece of data stored in the instruction -// stream and loaded through a pc relative load. The same literal can be -// referred to by multiple instructions but a literal can only reside at one -// place in memory. A literal can be used by a load before or after being -// placed in memory. -// -// Internally an offset of 0 is associated with a literal which has been -// neither used nor placed. Then two possibilities arise: -// 1) the label is placed, the offset (stored as offset + 1) is used to -// resolve any subsequent load using the label. -// 2) the label is not placed and offset is the offset of the last load using -// the literal (stored as -offset -1). If multiple loads refer to this -// literal then the last load holds the offset of the preceding load and -// all loads form a chain. Once the offset is placed all the loads in the -// chain are resolved and future loads fall back to possibility 1. -class RawLiteral { - public: - enum DeletionPolicy { - kDeletedOnPlacementByPool, - kDeletedOnPoolDestruction, - kManuallyDeleted - }; - - RawLiteral(size_t size, - LiteralPool* literal_pool, - DeletionPolicy deletion_policy = kManuallyDeleted); - - // The literal pool only sees and deletes `RawLiteral*` pointers, but they are - // actually pointing to `Literal` objects. - virtual ~RawLiteral() {} - - size_t size() { - VIXL_STATIC_ASSERT(kDRegSizeInBytes == kXRegSizeInBytes); - VIXL_STATIC_ASSERT(kSRegSizeInBytes == kWRegSizeInBytes); - VIXL_ASSERT((size_ == kXRegSizeInBytes) || - (size_ == kWRegSizeInBytes) || - (size_ == kQRegSizeInBytes)); - return size_; - } - uint64_t raw_value128_low64() { - VIXL_ASSERT(size_ == kQRegSizeInBytes); - return low64_; - } - uint64_t raw_value128_high64() { - VIXL_ASSERT(size_ == kQRegSizeInBytes); - return high64_; - } - uint64_t raw_value64() { - VIXL_ASSERT(size_ == kXRegSizeInBytes); - VIXL_ASSERT(high64_ == 0); - return low64_; - } - uint32_t raw_value32() { - VIXL_ASSERT(size_ == kWRegSizeInBytes); - VIXL_ASSERT(high64_ == 0); - VIXL_ASSERT(is_uint32(low64_) || is_int32(low64_)); - return static_cast(low64_); - } - bool IsUsed() { return offset_ < 0; } - bool IsPlaced() { return offset_ > 0; } - - LiteralPool* GetLiteralPool() const { - return literal_pool_; - } - - ptrdiff_t offset() { - VIXL_ASSERT(IsPlaced()); - return offset_ - 1; - } - - protected: - void set_offset(ptrdiff_t offset) { - VIXL_ASSERT(offset >= 0); - VIXL_ASSERT(IsWordAligned(offset)); - VIXL_ASSERT(!IsPlaced()); - offset_ = offset + 1; - } - ptrdiff_t last_use() { - VIXL_ASSERT(IsUsed()); - return -offset_ - 1; - } - void set_last_use(ptrdiff_t offset) { - VIXL_ASSERT(offset >= 0); - VIXL_ASSERT(IsWordAligned(offset)); - VIXL_ASSERT(!IsPlaced()); - offset_ = -offset - 1; - } - - size_t size_; - ptrdiff_t offset_; - uint64_t low64_; - uint64_t high64_; - - private: - LiteralPool* literal_pool_; - DeletionPolicy deletion_policy_; - - friend class Assembler; - friend class LiteralPool; -}; - - -template -class Literal : public RawLiteral { - public: - explicit Literal(T value, - LiteralPool* literal_pool = NULL, - RawLiteral::DeletionPolicy ownership = kManuallyDeleted) - : RawLiteral(sizeof(value), literal_pool, ownership) { - VIXL_STATIC_ASSERT(sizeof(value) <= kXRegSizeInBytes); - UpdateValue(value); - } - - Literal(T high64, T low64, - LiteralPool* literal_pool = NULL, - RawLiteral::DeletionPolicy ownership = kManuallyDeleted) - : RawLiteral(kQRegSizeInBytes, literal_pool, ownership) { - VIXL_STATIC_ASSERT(sizeof(low64) == (kQRegSizeInBytes / 2)); - UpdateValue(high64, low64); - } - - virtual ~Literal() {} - - // Update the value of this literal, if necessary by rewriting the value in - // the pool. - // If the literal has already been placed in a literal pool, the address of - // the start of the code buffer must be provided, as the literal only knows it - // offset from there. This also allows patching the value after the code has - // been moved in memory. - void UpdateValue(T new_value, uint8_t* code_buffer = NULL) { - VIXL_ASSERT(sizeof(new_value) == size_); - memcpy(&low64_, &new_value, sizeof(new_value)); - if (IsPlaced()) { - VIXL_ASSERT(code_buffer != NULL); - RewriteValueInCode(code_buffer); - } - } - - void UpdateValue(T high64, T low64, uint8_t* code_buffer = NULL) { - VIXL_ASSERT(sizeof(low64) == size_ / 2); - memcpy(&low64_, &low64, sizeof(low64)); - memcpy(&high64_, &high64, sizeof(high64)); - if (IsPlaced()) { - VIXL_ASSERT(code_buffer != NULL); - RewriteValueInCode(code_buffer); - } - } - - void UpdateValue(T new_value, const Assembler* assembler); - void UpdateValue(T high64, T low64, const Assembler* assembler); - - private: - void RewriteValueInCode(uint8_t* code_buffer) { - VIXL_ASSERT(IsPlaced()); - VIXL_STATIC_ASSERT(sizeof(T) <= kXRegSizeInBytes); - switch (size()) { - case kSRegSizeInBytes: - *reinterpret_cast(code_buffer + offset()) = raw_value32(); - break; - case kDRegSizeInBytes: - *reinterpret_cast(code_buffer + offset()) = raw_value64(); - break; - default: - VIXL_ASSERT(size() == kQRegSizeInBytes); - uint64_t* base_address = - reinterpret_cast(code_buffer + offset()); - *base_address = raw_value128_low64(); - *(base_address + 1) = raw_value128_high64(); - } - } -}; - - -// Control whether or not position-independent code should be emitted. -enum PositionIndependentCodeOption { - // All code generated will be position-independent; all branches and - // references to labels generated with the Label class will use PC-relative - // addressing. - PositionIndependentCode, - - // Allow VIXL to generate code that refers to absolute addresses. With this - // option, it will not be possible to copy the code buffer and run it from a - // different address; code must be generated in its final location. - PositionDependentCode, - - // Allow VIXL to assume that the bottom 12 bits of the address will be - // constant, but that the top 48 bits may change. This allows `adrp` to - // function in systems which copy code between pages, but otherwise maintain - // 4KB page alignment. - PageOffsetDependentCode -}; - - -// Control how scaled- and unscaled-offset loads and stores are generated. -enum LoadStoreScalingOption { - // Prefer scaled-immediate-offset instructions, but emit unscaled-offset, - // register-offset, pre-index or post-index instructions if necessary. - PreferScaledOffset, - - // Prefer unscaled-immediate-offset instructions, but emit scaled-offset, - // register-offset, pre-index or post-index instructions if necessary. - PreferUnscaledOffset, - - // Require scaled-immediate-offset instructions. - RequireScaledOffset, - - // Require unscaled-immediate-offset instructions. - RequireUnscaledOffset -}; - - -// Assembler. -class Assembler { - public: - Assembler(size_t capacity, - PositionIndependentCodeOption pic = PositionIndependentCode); - Assembler(byte* buffer, size_t capacity, - PositionIndependentCodeOption pic = PositionIndependentCode); - - // The destructor asserts that one of the following is true: - // * The Assembler object has not been used. - // * Nothing has been emitted since the last Reset() call. - // * Nothing has been emitted since the last FinalizeCode() call. - ~Assembler(); - - // System functions. - - // Start generating code from the beginning of the buffer, discarding any code - // and data that has already been emitted into the buffer. - void Reset(); - - // Finalize a code buffer of generated instructions. This function must be - // called before executing or copying code from the buffer. - void FinalizeCode(); - - // Label. - // Bind a label to the current PC. - void bind(Label* label); - - // Bind a label to a specified offset from the start of the buffer. - void BindToOffset(Label* label, ptrdiff_t offset); - - // Place a literal at the current PC. - void place(RawLiteral* literal); - - ptrdiff_t CursorOffset() const { - return buffer_->CursorOffset(); - } - - ptrdiff_t BufferEndOffset() const { - return static_cast(buffer_->capacity()); - } - - // Return the address of an offset in the buffer. - template - T GetOffsetAddress(ptrdiff_t offset) const { - VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); - return buffer_->GetOffsetAddress(offset); - } - - // Return the address of a bound label. - template - T GetLabelAddress(const Label * label) const { - VIXL_ASSERT(label->IsBound()); - VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); - return GetOffsetAddress(label->location()); - } - - // Return the address of the cursor. - template - T GetCursorAddress() const { - VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); - return GetOffsetAddress(CursorOffset()); - } - - // Return the address of the start of the buffer. - template - T GetStartAddress() const { - VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t)); - return GetOffsetAddress(0); - } - - Instruction* InstructionAt(ptrdiff_t instruction_offset) { - return GetOffsetAddress(instruction_offset); - } - - ptrdiff_t InstructionOffset(Instruction* instruction) { - VIXL_STATIC_ASSERT(sizeof(*instruction) == 1); - ptrdiff_t offset = instruction - GetStartAddress(); - VIXL_ASSERT((0 <= offset) && - (offset < static_cast(BufferCapacity()))); - return offset; - } - - // Instruction set functions. - - // Branch / Jump instructions. - // Branch to register. - void br(const Register& xn); - - // Branch with link to register. - void blr(const Register& xn); - - // Branch to register with return hint. - void ret(const Register& xn = lr); - - // Unconditional branch to label. - void b(Label* label); - - // Conditional branch to label. - void b(Label* label, Condition cond); - - // Unconditional branch to PC offset. - void b(int imm26); - - // Conditional branch to PC offset. - void b(int imm19, Condition cond); - - // Branch with link to label. - void bl(Label* label); - - // Branch with link to PC offset. - void bl(int imm26); - - // Compare and branch to label if zero. - void cbz(const Register& rt, Label* label); - - // Compare and branch to PC offset if zero. - void cbz(const Register& rt, int imm19); - - // Compare and branch to label if not zero. - void cbnz(const Register& rt, Label* label); - - // Compare and branch to PC offset if not zero. - void cbnz(const Register& rt, int imm19); - - // Table lookup from one register. - void tbl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Table lookup from two registers. - void tbl(const VRegister& vd, - const VRegister& vn, - const VRegister& vn2, - const VRegister& vm); - - // Table lookup from three registers. - void tbl(const VRegister& vd, - const VRegister& vn, - const VRegister& vn2, - const VRegister& vn3, - const VRegister& vm); - - // Table lookup from four registers. - void tbl(const VRegister& vd, - const VRegister& vn, - const VRegister& vn2, - const VRegister& vn3, - const VRegister& vn4, - const VRegister& vm); - - // Table lookup extension from one register. - void tbx(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Table lookup extension from two registers. - void tbx(const VRegister& vd, - const VRegister& vn, - const VRegister& vn2, - const VRegister& vm); - - // Table lookup extension from three registers. - void tbx(const VRegister& vd, - const VRegister& vn, - const VRegister& vn2, - const VRegister& vn3, - const VRegister& vm); - - // Table lookup extension from four registers. - void tbx(const VRegister& vd, - const VRegister& vn, - const VRegister& vn2, - const VRegister& vn3, - const VRegister& vn4, - const VRegister& vm); - - // Test bit and branch to label if zero. - void tbz(const Register& rt, unsigned bit_pos, Label* label); - - // Test bit and branch to PC offset if zero. - void tbz(const Register& rt, unsigned bit_pos, int imm14); - - // Test bit and branch to label if not zero. - void tbnz(const Register& rt, unsigned bit_pos, Label* label); - - // Test bit and branch to PC offset if not zero. - void tbnz(const Register& rt, unsigned bit_pos, int imm14); - - // Address calculation instructions. - // Calculate a PC-relative address. Unlike for branches the offset in adr is - // unscaled (i.e. the result can be unaligned). - - // Calculate the address of a label. - void adr(const Register& rd, Label* label); - - // Calculate the address of a PC offset. - void adr(const Register& rd, int imm21); - - // Calculate the page address of a label. - void adrp(const Register& rd, Label* label); - - // Calculate the page address of a PC offset. - void adrp(const Register& rd, int imm21); - - // Data Processing instructions. - // Add. - void add(const Register& rd, - const Register& rn, - const Operand& operand); - - // Add and update status flags. - void adds(const Register& rd, - const Register& rn, - const Operand& operand); - - // Compare negative. - void cmn(const Register& rn, const Operand& operand); - - // Subtract. - void sub(const Register& rd, - const Register& rn, - const Operand& operand); - - // Subtract and update status flags. - void subs(const Register& rd, - const Register& rn, - const Operand& operand); - - // Compare. - void cmp(const Register& rn, const Operand& operand); - - // Negate. - void neg(const Register& rd, - const Operand& operand); - - // Negate and update status flags. - void negs(const Register& rd, - const Operand& operand); - - // Add with carry bit. - void adc(const Register& rd, - const Register& rn, - const Operand& operand); - - // Add with carry bit and update status flags. - void adcs(const Register& rd, - const Register& rn, - const Operand& operand); - - // Subtract with carry bit. - void sbc(const Register& rd, - const Register& rn, - const Operand& operand); - - // Subtract with carry bit and update status flags. - void sbcs(const Register& rd, - const Register& rn, - const Operand& operand); - - // Negate with carry bit. - void ngc(const Register& rd, - const Operand& operand); - - // Negate with carry bit and update status flags. - void ngcs(const Register& rd, - const Operand& operand); - - // Logical instructions. - // Bitwise and (A & B). - void and_(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bitwise and (A & B) and update status flags. - void ands(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bit test and set flags. - void tst(const Register& rn, const Operand& operand); - - // Bit clear (A & ~B). - void bic(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bit clear (A & ~B) and update status flags. - void bics(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bitwise or (A | B). - void orr(const Register& rd, const Register& rn, const Operand& operand); - - // Bitwise nor (A | ~B). - void orn(const Register& rd, const Register& rn, const Operand& operand); - - // Bitwise eor/xor (A ^ B). - void eor(const Register& rd, const Register& rn, const Operand& operand); - - // Bitwise enor/xnor (A ^ ~B). - void eon(const Register& rd, const Register& rn, const Operand& operand); - - // Logical shift left by variable. - void lslv(const Register& rd, const Register& rn, const Register& rm); - - // Logical shift right by variable. - void lsrv(const Register& rd, const Register& rn, const Register& rm); - - // Arithmetic shift right by variable. - void asrv(const Register& rd, const Register& rn, const Register& rm); - - // Rotate right by variable. - void rorv(const Register& rd, const Register& rn, const Register& rm); - - // Bitfield instructions. - // Bitfield move. - void bfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms); - - // Signed bitfield move. - void sbfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms); - - // Unsigned bitfield move. - void ubfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms); - - // Bfm aliases. - // Bitfield insert. - void bfi(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - bfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); - } - - // Bitfield extract and insert low. - void bfxil(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - bfm(rd, rn, lsb, lsb + width - 1); - } - - // Sbfm aliases. - // Arithmetic shift right. - void asr(const Register& rd, const Register& rn, unsigned shift) { - VIXL_ASSERT(shift < rd.size()); - sbfm(rd, rn, shift, rd.size() - 1); - } - - // Signed bitfield insert with zero at right. - void sbfiz(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - sbfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); - } - - // Signed bitfield extract. - void sbfx(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - sbfm(rd, rn, lsb, lsb + width - 1); - } - - // Signed extend byte. - void sxtb(const Register& rd, const Register& rn) { - sbfm(rd, rn, 0, 7); - } - - // Signed extend halfword. - void sxth(const Register& rd, const Register& rn) { - sbfm(rd, rn, 0, 15); - } - - // Signed extend word. - void sxtw(const Register& rd, const Register& rn) { - sbfm(rd, rn, 0, 31); - } - - // Ubfm aliases. - // Logical shift left. - void lsl(const Register& rd, const Register& rn, unsigned shift) { - unsigned reg_size = rd.size(); - VIXL_ASSERT(shift < reg_size); - ubfm(rd, rn, (reg_size - shift) % reg_size, reg_size - shift - 1); - } - - // Logical shift right. - void lsr(const Register& rd, const Register& rn, unsigned shift) { - VIXL_ASSERT(shift < rd.size()); - ubfm(rd, rn, shift, rd.size() - 1); - } - - // Unsigned bitfield insert with zero at right. - void ubfiz(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - ubfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); - } - - // Unsigned bitfield extract. - void ubfx(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - VIXL_ASSERT(width >= 1); - VIXL_ASSERT(lsb + width <= rn.size()); - ubfm(rd, rn, lsb, lsb + width - 1); - } - - // Unsigned extend byte. - void uxtb(const Register& rd, const Register& rn) { - ubfm(rd, rn, 0, 7); - } - - // Unsigned extend halfword. - void uxth(const Register& rd, const Register& rn) { - ubfm(rd, rn, 0, 15); - } - - // Unsigned extend word. - void uxtw(const Register& rd, const Register& rn) { - ubfm(rd, rn, 0, 31); - } - - // Extract. - void extr(const Register& rd, - const Register& rn, - const Register& rm, - unsigned lsb); - - // Conditional select: rd = cond ? rn : rm. - void csel(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional select increment: rd = cond ? rn : rm + 1. - void csinc(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional select inversion: rd = cond ? rn : ~rm. - void csinv(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional select negation: rd = cond ? rn : -rm. - void csneg(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional set: rd = cond ? 1 : 0. - void cset(const Register& rd, Condition cond); - - // Conditional set mask: rd = cond ? -1 : 0. - void csetm(const Register& rd, Condition cond); - - // Conditional increment: rd = cond ? rn + 1 : rn. - void cinc(const Register& rd, const Register& rn, Condition cond); - - // Conditional invert: rd = cond ? ~rn : rn. - void cinv(const Register& rd, const Register& rn, Condition cond); - - // Conditional negate: rd = cond ? -rn : rn. - void cneg(const Register& rd, const Register& rn, Condition cond); - - // Rotate right. - void ror(const Register& rd, const Register& rs, unsigned shift) { - extr(rd, rs, rs, shift); - } - - // Conditional comparison. - // Conditional compare negative. - void ccmn(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond); - - // Conditional compare. - void ccmp(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond); - - // CRC-32 checksum from byte. - void crc32b(const Register& rd, - const Register& rn, - const Register& rm); - - // CRC-32 checksum from half-word. - void crc32h(const Register& rd, - const Register& rn, - const Register& rm); - - // CRC-32 checksum from word. - void crc32w(const Register& rd, - const Register& rn, - const Register& rm); - - // CRC-32 checksum from double word. - void crc32x(const Register& rd, - const Register& rn, - const Register& rm); - - // CRC-32 C checksum from byte. - void crc32cb(const Register& rd, - const Register& rn, - const Register& rm); - - // CRC-32 C checksum from half-word. - void crc32ch(const Register& rd, - const Register& rn, - const Register& rm); - - // CRC-32 C checksum from word. - void crc32cw(const Register& rd, - const Register& rn, - const Register& rm); - - // CRC-32C checksum from double word. - void crc32cx(const Register& rd, - const Register& rn, - const Register& rm); - - // Multiply. - void mul(const Register& rd, const Register& rn, const Register& rm); - - // Negated multiply. - void mneg(const Register& rd, const Register& rn, const Register& rm); - - // Signed long multiply: 32 x 32 -> 64-bit. - void smull(const Register& rd, const Register& rn, const Register& rm); - - // Signed multiply high: 64 x 64 -> 64-bit <127:64>. - void smulh(const Register& xd, const Register& xn, const Register& xm); - - // Multiply and accumulate. - void madd(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Multiply and subtract. - void msub(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Signed long multiply and accumulate: 32 x 32 + 64 -> 64-bit. - void smaddl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Unsigned long multiply and accumulate: 32 x 32 + 64 -> 64-bit. - void umaddl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Unsigned long multiply: 32 x 32 -> 64-bit. - void umull(const Register& rd, - const Register& rn, - const Register& rm) { - umaddl(rd, rn, rm, xzr); - } - - // Unsigned multiply high: 64 x 64 -> 64-bit <127:64>. - void umulh(const Register& xd, - const Register& xn, - const Register& xm); - - // Signed long multiply and subtract: 64 - (32 x 32) -> 64-bit. - void smsubl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Unsigned long multiply and subtract: 64 - (32 x 32) -> 64-bit. - void umsubl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Signed integer divide. - void sdiv(const Register& rd, const Register& rn, const Register& rm); - - // Unsigned integer divide. - void udiv(const Register& rd, const Register& rn, const Register& rm); - - // Bit reverse. - void rbit(const Register& rd, const Register& rn); - - // Reverse bytes in 16-bit half words. - void rev16(const Register& rd, const Register& rn); - - // Reverse bytes in 32-bit words. - void rev32(const Register& rd, const Register& rn); - - // Reverse bytes. - void rev(const Register& rd, const Register& rn); - - // Count leading zeroes. - void clz(const Register& rd, const Register& rn); - - // Count leading sign bits. - void cls(const Register& rd, const Register& rn); - - // Memory instructions. - // Load integer or FP register. - void ldr(const CPURegister& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Store integer or FP register. - void str(const CPURegister& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load word with sign extension. - void ldrsw(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load byte. - void ldrb(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Store byte. - void strb(const Register& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load byte with sign extension. - void ldrsb(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load half-word. - void ldrh(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Store half-word. - void strh(const Register& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load half-word with sign extension. - void ldrsh(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferScaledOffset); - - // Load integer or FP register (with unscaled offset). - void ldur(const CPURegister& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Store integer or FP register (with unscaled offset). - void stur(const CPURegister& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load word with sign extension. - void ldursw(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load byte (with unscaled offset). - void ldurb(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Store byte (with unscaled offset). - void sturb(const Register& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load byte with sign extension (and unscaled offset). - void ldursb(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load half-word (with unscaled offset). - void ldurh(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Store half-word (with unscaled offset). - void sturh(const Register& rt, const MemOperand& dst, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load half-word with sign extension (and unscaled offset). - void ldursh(const Register& rt, const MemOperand& src, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Load integer or FP register pair. - void ldp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& src); - - // Store integer or FP register pair. - void stp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& dst); - - // Load word pair with sign extension. - void ldpsw(const Register& rt, const Register& rt2, const MemOperand& src); - - // Load integer or FP register pair, non-temporal. - void ldnp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& src); - - // Store integer or FP register pair, non-temporal. - void stnp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& dst); - - // Load integer or FP register from literal pool. - void ldr(const CPURegister& rt, RawLiteral* literal); - - // Load word with sign extension from literal pool. - void ldrsw(const Register& rt, RawLiteral* literal); - - // Load integer or FP register from pc + imm19 << 2. - void ldr(const CPURegister& rt, int imm19); - - // Load word with sign extension from pc + imm19 << 2. - void ldrsw(const Register& rt, int imm19); - - // Store exclusive byte. - void stxrb(const Register& rs, const Register& rt, const MemOperand& dst); - - // Store exclusive half-word. - void stxrh(const Register& rs, const Register& rt, const MemOperand& dst); - - // Store exclusive register. - void stxr(const Register& rs, const Register& rt, const MemOperand& dst); - - // Load exclusive byte. - void ldxrb(const Register& rt, const MemOperand& src); - - // Load exclusive half-word. - void ldxrh(const Register& rt, const MemOperand& src); - - // Load exclusive register. - void ldxr(const Register& rt, const MemOperand& src); - - // Store exclusive register pair. - void stxp(const Register& rs, - const Register& rt, - const Register& rt2, - const MemOperand& dst); - - // Load exclusive register pair. - void ldxp(const Register& rt, const Register& rt2, const MemOperand& src); - - // Store-release exclusive byte. - void stlxrb(const Register& rs, const Register& rt, const MemOperand& dst); - - // Store-release exclusive half-word. - void stlxrh(const Register& rs, const Register& rt, const MemOperand& dst); - - // Store-release exclusive register. - void stlxr(const Register& rs, const Register& rt, const MemOperand& dst); - - // Load-acquire exclusive byte. - void ldaxrb(const Register& rt, const MemOperand& src); - - // Load-acquire exclusive half-word. - void ldaxrh(const Register& rt, const MemOperand& src); - - // Load-acquire exclusive register. - void ldaxr(const Register& rt, const MemOperand& src); - - // Store-release exclusive register pair. - void stlxp(const Register& rs, - const Register& rt, - const Register& rt2, - const MemOperand& dst); - - // Load-acquire exclusive register pair. - void ldaxp(const Register& rt, const Register& rt2, const MemOperand& src); - - // Store-release byte. - void stlrb(const Register& rt, const MemOperand& dst); - - // Store-release half-word. - void stlrh(const Register& rt, const MemOperand& dst); - - // Store-release register. - void stlr(const Register& rt, const MemOperand& dst); - - // Load-acquire byte. - void ldarb(const Register& rt, const MemOperand& src); - - // Load-acquire half-word. - void ldarh(const Register& rt, const MemOperand& src); - - // Load-acquire register. - void ldar(const Register& rt, const MemOperand& src); - - // Prefetch memory. - void prfm(PrefetchOperation op, const MemOperand& addr, - LoadStoreScalingOption option = PreferScaledOffset); - - // Prefetch memory (with unscaled offset). - void prfum(PrefetchOperation op, const MemOperand& addr, - LoadStoreScalingOption option = PreferUnscaledOffset); - - // Prefetch memory in the literal pool. - void prfm(PrefetchOperation op, RawLiteral* literal); - - // Prefetch from pc + imm19 << 2. - void prfm(PrefetchOperation op, int imm19); - - // Move instructions. The default shift of -1 indicates that the move - // instruction will calculate an appropriate 16-bit immediate and left shift - // that is equal to the 64-bit immediate argument. If an explicit left shift - // is specified (0, 16, 32 or 48), the immediate must be a 16-bit value. - // - // For movk, an explicit shift can be used to indicate which half word should - // be overwritten, eg. movk(x0, 0, 0) will overwrite the least-significant - // half word with zero, whereas movk(x0, 0, 48) will overwrite the - // most-significant. - - // Move immediate and keep. - void movk(const Register& rd, uint64_t imm, int shift = -1) { - MoveWide(rd, imm, shift, MOVK); - } - - // Move inverted immediate. - void movn(const Register& rd, uint64_t imm, int shift = -1) { - MoveWide(rd, imm, shift, MOVN); - } - - // Move immediate. - void movz(const Register& rd, uint64_t imm, int shift = -1) { - MoveWide(rd, imm, shift, MOVZ); - } - - // Misc instructions. - // Monitor debug-mode breakpoint. - void brk(int code); - - // Halting debug-mode breakpoint. - void hlt(int code); - - // Generate exception targeting EL1. - void svc(int code); - - // Move register to register. - void mov(const Register& rd, const Register& rn); - - // Move inverted operand to register. - void mvn(const Register& rd, const Operand& operand); - - // System instructions. - // Move to register from system register. - void mrs(const Register& rt, SystemRegister sysreg); - - // Move from register to system register. - void msr(SystemRegister sysreg, const Register& rt); - - // System instruction. - void sys(int op1, int crn, int crm, int op2, const Register& rt = xzr); - - // System instruction with pre-encoded op (op1:crn:crm:op2). - void sys(int op, const Register& rt = xzr); - - // System data cache operation. - void dc(DataCacheOp op, const Register& rt); - - // System instruction cache operation. - void ic(InstructionCacheOp op, const Register& rt); - - // System hint. - void hint(SystemHint code); - - // Clear exclusive monitor. - void clrex(int imm4 = 0xf); - - // Data memory barrier. - void dmb(BarrierDomain domain, BarrierType type); - - // Data synchronization barrier. - void dsb(BarrierDomain domain, BarrierType type); - - // Instruction synchronization barrier. - void isb(); - - // Alias for system instructions. - // No-op. - void nop() { - hint(NOP); - } - - // FP and NEON instructions. - // Move double precision immediate to FP register. - void fmov(const VRegister& vd, double imm); - - // Move single precision immediate to FP register. - void fmov(const VRegister& vd, float imm); - - // Move FP register to register. - void fmov(const Register& rd, const VRegister& fn); - - // Move register to FP register. - void fmov(const VRegister& vd, const Register& rn); - - // Move FP register to FP register. - void fmov(const VRegister& vd, const VRegister& fn); - - // Move 64-bit register to top half of 128-bit FP register. - void fmov(const VRegister& vd, int index, const Register& rn); - - // Move top half of 128-bit FP register to 64-bit register. - void fmov(const Register& rd, const VRegister& vn, int index); - - // FP add. - void fadd(const VRegister& vd, const VRegister& vn, const VRegister& vm); - - // FP subtract. - void fsub(const VRegister& vd, const VRegister& vn, const VRegister& vm); - - // FP multiply. - void fmul(const VRegister& vd, const VRegister& vn, const VRegister& vm); - - // FP fused multiply-add. - void fmadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - const VRegister& va); - - // FP fused multiply-subtract. - void fmsub(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - const VRegister& va); - - // FP fused multiply-add and negate. - void fnmadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - const VRegister& va); - - // FP fused multiply-subtract and negate. - void fnmsub(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - const VRegister& va); - - // FP multiply-negate scalar. - void fnmul(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP reciprocal exponent scalar. - void frecpx(const VRegister& vd, - const VRegister& vn); - - // FP divide. - void fdiv(const VRegister& vd, const VRegister& fn, const VRegister& vm); - - // FP maximum. - void fmax(const VRegister& vd, const VRegister& fn, const VRegister& vm); - - // FP minimum. - void fmin(const VRegister& vd, const VRegister& fn, const VRegister& vm); - - // FP maximum number. - void fmaxnm(const VRegister& vd, const VRegister& fn, const VRegister& vm); - - // FP minimum number. - void fminnm(const VRegister& vd, const VRegister& fn, const VRegister& vm); - - // FP absolute. - void fabs(const VRegister& vd, const VRegister& vn); - - // FP negate. - void fneg(const VRegister& vd, const VRegister& vn); - - // FP square root. - void fsqrt(const VRegister& vd, const VRegister& vn); - - // FP round to integer, nearest with ties to away. - void frinta(const VRegister& vd, const VRegister& vn); - - // FP round to integer, implicit rounding. - void frinti(const VRegister& vd, const VRegister& vn); - - // FP round to integer, toward minus infinity. - void frintm(const VRegister& vd, const VRegister& vn); - - // FP round to integer, nearest with ties to even. - void frintn(const VRegister& vd, const VRegister& vn); - - // FP round to integer, toward plus infinity. - void frintp(const VRegister& vd, const VRegister& vn); - - // FP round to integer, exact, implicit rounding. - void frintx(const VRegister& vd, const VRegister& vn); - - // FP round to integer, towards zero. - void frintz(const VRegister& vd, const VRegister& vn); - - void FPCompareMacro(const VRegister& vn, - double value, - FPTrapFlags trap); - - void FPCompareMacro(const VRegister& vn, - const VRegister& vm, - FPTrapFlags trap); - - // FP compare registers. - void fcmp(const VRegister& vn, const VRegister& vm); - - // FP compare immediate. - void fcmp(const VRegister& vn, double value); - - void FPCCompareMacro(const VRegister& vn, - const VRegister& vm, - StatusFlags nzcv, - Condition cond, - FPTrapFlags trap); - - // FP conditional compare. - void fccmp(const VRegister& vn, - const VRegister& vm, - StatusFlags nzcv, - Condition cond); - - // FP signaling compare registers. - void fcmpe(const VRegister& vn, const VRegister& vm); - - // FP signaling compare immediate. - void fcmpe(const VRegister& vn, double value); - - // FP conditional signaling compare. - void fccmpe(const VRegister& vn, - const VRegister& vm, - StatusFlags nzcv, - Condition cond); - - // FP conditional select. - void fcsel(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - Condition cond); - - // Common FP Convert functions. - void NEONFPConvertToInt(const Register& rd, - const VRegister& vn, - Instr op); - void NEONFPConvertToInt(const VRegister& vd, - const VRegister& vn, - Instr op); - - // FP convert between precisions. - void fcvt(const VRegister& vd, const VRegister& vn); - - // FP convert to higher precision. - void fcvtl(const VRegister& vd, const VRegister& vn); - - // FP convert to higher precision (second part). - void fcvtl2(const VRegister& vd, const VRegister& vn); - - // FP convert to lower precision. - void fcvtn(const VRegister& vd, const VRegister& vn); - - // FP convert to lower prevision (second part). - void fcvtn2(const VRegister& vd, const VRegister& vn); - - // FP convert to lower precision, rounding to odd. - void fcvtxn(const VRegister& vd, const VRegister& vn); - - // FP convert to lower precision, rounding to odd (second part). - void fcvtxn2(const VRegister& vd, const VRegister& vn); - - // FP convert to signed integer, nearest with ties to away. - void fcvtas(const Register& rd, const VRegister& vn); - - // FP convert to unsigned integer, nearest with ties to away. - void fcvtau(const Register& rd, const VRegister& vn); - - // FP convert to signed integer, nearest with ties to away. - void fcvtas(const VRegister& vd, const VRegister& vn); - - // FP convert to unsigned integer, nearest with ties to away. - void fcvtau(const VRegister& vd, const VRegister& vn); - - // FP convert to signed integer, round towards -infinity. - void fcvtms(const Register& rd, const VRegister& vn); - - // FP convert to unsigned integer, round towards -infinity. - void fcvtmu(const Register& rd, const VRegister& vn); - - // FP convert to signed integer, round towards -infinity. - void fcvtms(const VRegister& vd, const VRegister& vn); - - // FP convert to unsigned integer, round towards -infinity. - void fcvtmu(const VRegister& vd, const VRegister& vn); - - // FP convert to signed integer, nearest with ties to even. - void fcvtns(const Register& rd, const VRegister& vn); - - // FP convert to unsigned integer, nearest with ties to even. - void fcvtnu(const Register& rd, const VRegister& vn); - - // FP convert to signed integer, nearest with ties to even. - void fcvtns(const VRegister& rd, const VRegister& vn); - - // FP convert to unsigned integer, nearest with ties to even. - void fcvtnu(const VRegister& rd, const VRegister& vn); - - // FP convert to signed integer or fixed-point, round towards zero. - void fcvtzs(const Register& rd, const VRegister& vn, int fbits = 0); - - // FP convert to unsigned integer or fixed-point, round towards zero. - void fcvtzu(const Register& rd, const VRegister& vn, int fbits = 0); - - // FP convert to signed integer or fixed-point, round towards zero. - void fcvtzs(const VRegister& vd, const VRegister& vn, int fbits = 0); - - // FP convert to unsigned integer or fixed-point, round towards zero. - void fcvtzu(const VRegister& vd, const VRegister& vn, int fbits = 0); - - // FP convert to signed integer, round towards +infinity. - void fcvtps(const Register& rd, const VRegister& vn); - - // FP convert to unsigned integer, round towards +infinity. - void fcvtpu(const Register& rd, const VRegister& vn); - - // FP convert to signed integer, round towards +infinity. - void fcvtps(const VRegister& vd, const VRegister& vn); - - // FP convert to unsigned integer, round towards +infinity. - void fcvtpu(const VRegister& vd, const VRegister& vn); - - // Convert signed integer or fixed point to FP. - void scvtf(const VRegister& fd, const Register& rn, int fbits = 0); - - // Convert unsigned integer or fixed point to FP. - void ucvtf(const VRegister& fd, const Register& rn, int fbits = 0); - - // Convert signed integer or fixed-point to FP. - void scvtf(const VRegister& fd, const VRegister& vn, int fbits = 0); - - // Convert unsigned integer or fixed-point to FP. - void ucvtf(const VRegister& fd, const VRegister& vn, int fbits = 0); - - // Unsigned absolute difference. - void uabd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed absolute difference. - void sabd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned absolute difference and accumulate. - void uaba(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed absolute difference and accumulate. - void saba(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Add. - void add(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Subtract. - void sub(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned halving add. - void uhadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed halving add. - void shadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned rounding halving add. - void urhadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed rounding halving add. - void srhadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned halving sub. - void uhsub(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed halving sub. - void shsub(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned saturating add. - void uqadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating add. - void sqadd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned saturating subtract. - void uqsub(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating subtract. - void sqsub(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Add pairwise. - void addp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Add pair of elements scalar. - void addp(const VRegister& vd, - const VRegister& vn); - - // Multiply-add to accumulator. - void mla(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Multiply-subtract to accumulator. - void mls(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Multiply. - void mul(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Multiply by scalar element. - void mul(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Multiply-add by scalar element. - void mla(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Multiply-subtract by scalar element. - void mls(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed long multiply-add by scalar element. - void smlal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed long multiply-add by scalar element (second part). - void smlal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Unsigned long multiply-add by scalar element. - void umlal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Unsigned long multiply-add by scalar element (second part). - void umlal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed long multiply-sub by scalar element. - void smlsl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed long multiply-sub by scalar element (second part). - void smlsl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Unsigned long multiply-sub by scalar element. - void umlsl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Unsigned long multiply-sub by scalar element (second part). - void umlsl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed long multiply by scalar element. - void smull(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed long multiply by scalar element (second part). - void smull2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Unsigned long multiply by scalar element. - void umull(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Unsigned long multiply by scalar element (second part). - void umull2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed saturating double long multiply by element. - void sqdmull(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed saturating double long multiply by element (second part). - void sqdmull2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed saturating doubling long multiply-add by element. - void sqdmlal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed saturating doubling long multiply-add by element (second part). - void sqdmlal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed saturating doubling long multiply-sub by element. - void sqdmlsl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed saturating doubling long multiply-sub by element (second part). - void sqdmlsl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Compare equal. - void cmeq(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Compare signed greater than or equal. - void cmge(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Compare signed greater than. - void cmgt(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Compare unsigned higher. - void cmhi(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Compare unsigned higher or same. - void cmhs(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Compare bitwise test bits nonzero. - void cmtst(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Compare bitwise to zero. - void cmeq(const VRegister& vd, - const VRegister& vn, - int value); - - // Compare signed greater than or equal to zero. - void cmge(const VRegister& vd, - const VRegister& vn, - int value); - - // Compare signed greater than zero. - void cmgt(const VRegister& vd, - const VRegister& vn, - int value); - - // Compare signed less than or equal to zero. - void cmle(const VRegister& vd, - const VRegister& vn, - int value); - - // Compare signed less than zero. - void cmlt(const VRegister& vd, - const VRegister& vn, - int value); - - // Signed shift left by register. - void sshl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned shift left by register. - void ushl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating shift left by register. - void sqshl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned saturating shift left by register. - void uqshl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed rounding shift left by register. - void srshl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned rounding shift left by register. - void urshl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating rounding shift left by register. - void sqrshl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned saturating rounding shift left by register. - void uqrshl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bitwise and. - void and_(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bitwise or. - void orr(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bitwise or immediate. - void orr(const VRegister& vd, - const int imm8, - const int left_shift = 0); - - // Move register to register. - void mov(const VRegister& vd, - const VRegister& vn); - - // Bitwise orn. - void orn(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bitwise eor. - void eor(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bit clear immediate. - void bic(const VRegister& vd, - const int imm8, - const int left_shift = 0); - - // Bit clear. - void bic(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bitwise insert if false. - void bif(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bitwise insert if true. - void bit(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Bitwise select. - void bsl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Polynomial multiply. - void pmul(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Vector move immediate. - void movi(const VRegister& vd, - const uint64_t imm, - Shift shift = LSL, - const int shift_amount = 0); - - // Bitwise not. - void mvn(const VRegister& vd, - const VRegister& vn); - - // Vector move inverted immediate. - void mvni(const VRegister& vd, - const int imm8, - Shift shift = LSL, - const int shift_amount = 0); - - // Signed saturating accumulate of unsigned value. - void suqadd(const VRegister& vd, - const VRegister& vn); - - // Unsigned saturating accumulate of signed value. - void usqadd(const VRegister& vd, - const VRegister& vn); - - // Absolute value. - void abs(const VRegister& vd, - const VRegister& vn); - - // Signed saturating absolute value. - void sqabs(const VRegister& vd, - const VRegister& vn); - - // Negate. - void neg(const VRegister& vd, - const VRegister& vn); - - // Signed saturating negate. - void sqneg(const VRegister& vd, - const VRegister& vn); - - // Bitwise not. - void not_(const VRegister& vd, - const VRegister& vn); - - // Extract narrow. - void xtn(const VRegister& vd, - const VRegister& vn); - - // Extract narrow (second part). - void xtn2(const VRegister& vd, - const VRegister& vn); - - // Signed saturating extract narrow. - void sqxtn(const VRegister& vd, - const VRegister& vn); - - // Signed saturating extract narrow (second part). - void sqxtn2(const VRegister& vd, - const VRegister& vn); - - // Unsigned saturating extract narrow. - void uqxtn(const VRegister& vd, - const VRegister& vn); - - // Unsigned saturating extract narrow (second part). - void uqxtn2(const VRegister& vd, - const VRegister& vn); - - // Signed saturating extract unsigned narrow. - void sqxtun(const VRegister& vd, - const VRegister& vn); - - // Signed saturating extract unsigned narrow (second part). - void sqxtun2(const VRegister& vd, - const VRegister& vn); - - // Extract vector from pair of vectors. - void ext(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int index); - - // Duplicate vector element to vector or scalar. - void dup(const VRegister& vd, - const VRegister& vn, - int vn_index); - - // Move vector element to scalar. - void mov(const VRegister& vd, - const VRegister& vn, - int vn_index); - - // Duplicate general-purpose register to vector. - void dup(const VRegister& vd, - const Register& rn); - - // Insert vector element from another vector element. - void ins(const VRegister& vd, - int vd_index, - const VRegister& vn, - int vn_index); - - // Move vector element to another vector element. - void mov(const VRegister& vd, - int vd_index, - const VRegister& vn, - int vn_index); - - // Insert vector element from general-purpose register. - void ins(const VRegister& vd, - int vd_index, - const Register& rn); - - // Move general-purpose register to a vector element. - void mov(const VRegister& vd, - int vd_index, - const Register& rn); - - // Unsigned move vector element to general-purpose register. - void umov(const Register& rd, - const VRegister& vn, - int vn_index); - - // Move vector element to general-purpose register. - void mov(const Register& rd, - const VRegister& vn, - int vn_index); - - // Signed move vector element to general-purpose register. - void smov(const Register& rd, - const VRegister& vn, - int vn_index); - - // One-element structure load to one register. - void ld1(const VRegister& vt, - const MemOperand& src); - - // One-element structure load to two registers. - void ld1(const VRegister& vt, - const VRegister& vt2, - const MemOperand& src); - - // One-element structure load to three registers. - void ld1(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const MemOperand& src); - - // One-element structure load to four registers. - void ld1(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const VRegister& vt4, - const MemOperand& src); - - // One-element single structure load to one lane. - void ld1(const VRegister& vt, - int lane, - const MemOperand& src); - - // One-element single structure load to all lanes. - void ld1r(const VRegister& vt, - const MemOperand& src); - - // Two-element structure load. - void ld2(const VRegister& vt, - const VRegister& vt2, - const MemOperand& src); - - // Two-element single structure load to one lane. - void ld2(const VRegister& vt, - const VRegister& vt2, - int lane, - const MemOperand& src); - - // Two-element single structure load to all lanes. - void ld2r(const VRegister& vt, - const VRegister& vt2, - const MemOperand& src); - - // Three-element structure load. - void ld3(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const MemOperand& src); - - // Three-element single structure load to one lane. - void ld3(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - int lane, - const MemOperand& src); - - // Three-element single structure load to all lanes. - void ld3r(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const MemOperand& src); - - // Four-element structure load. - void ld4(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const VRegister& vt4, - const MemOperand& src); - - // Four-element single structure load to one lane. - void ld4(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const VRegister& vt4, - int lane, - const MemOperand& src); - - // Four-element single structure load to all lanes. - void ld4r(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const VRegister& vt4, - const MemOperand& src); - - // Count leading sign bits. - void cls(const VRegister& vd, - const VRegister& vn); - - // Count leading zero bits (vector). - void clz(const VRegister& vd, - const VRegister& vn); - - // Population count per byte. - void cnt(const VRegister& vd, - const VRegister& vn); - - // Reverse bit order. - void rbit(const VRegister& vd, - const VRegister& vn); - - // Reverse elements in 16-bit halfwords. - void rev16(const VRegister& vd, - const VRegister& vn); - - // Reverse elements in 32-bit words. - void rev32(const VRegister& vd, - const VRegister& vn); - - // Reverse elements in 64-bit doublewords. - void rev64(const VRegister& vd, - const VRegister& vn); - - // Unsigned reciprocal square root estimate. - void ursqrte(const VRegister& vd, - const VRegister& vn); - - // Unsigned reciprocal estimate. - void urecpe(const VRegister& vd, - const VRegister& vn); - - // Signed pairwise long add. - void saddlp(const VRegister& vd, - const VRegister& vn); - - // Unsigned pairwise long add. - void uaddlp(const VRegister& vd, - const VRegister& vn); - - // Signed pairwise long add and accumulate. - void sadalp(const VRegister& vd, - const VRegister& vn); - - // Unsigned pairwise long add and accumulate. - void uadalp(const VRegister& vd, - const VRegister& vn); - - // Shift left by immediate. - void shl(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating shift left by immediate. - void sqshl(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating shift left unsigned by immediate. - void sqshlu(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned saturating shift left by immediate. - void uqshl(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed shift left long by immediate. - void sshll(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed shift left long by immediate (second part). - void sshll2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed extend long. - void sxtl(const VRegister& vd, - const VRegister& vn); - - // Signed extend long (second part). - void sxtl2(const VRegister& vd, - const VRegister& vn); - - // Unsigned shift left long by immediate. - void ushll(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned shift left long by immediate (second part). - void ushll2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Shift left long by element size. - void shll(const VRegister& vd, - const VRegister& vn, - int shift); - - // Shift left long by element size (second part). - void shll2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned extend long. - void uxtl(const VRegister& vd, - const VRegister& vn); - - // Unsigned extend long (second part). - void uxtl2(const VRegister& vd, - const VRegister& vn); - - // Shift left by immediate and insert. - void sli(const VRegister& vd, - const VRegister& vn, - int shift); - - // Shift right by immediate and insert. - void sri(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed maximum. - void smax(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed pairwise maximum. - void smaxp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Add across vector. - void addv(const VRegister& vd, - const VRegister& vn); - - // Signed add long across vector. - void saddlv(const VRegister& vd, - const VRegister& vn); - - // Unsigned add long across vector. - void uaddlv(const VRegister& vd, - const VRegister& vn); - - // FP maximum number across vector. - void fmaxnmv(const VRegister& vd, - const VRegister& vn); - - // FP maximum across vector. - void fmaxv(const VRegister& vd, - const VRegister& vn); - - // FP minimum number across vector. - void fminnmv(const VRegister& vd, - const VRegister& vn); - - // FP minimum across vector. - void fminv(const VRegister& vd, - const VRegister& vn); - - // Signed maximum across vector. - void smaxv(const VRegister& vd, - const VRegister& vn); - - // Signed minimum. - void smin(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed minimum pairwise. - void sminp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed minimum across vector. - void sminv(const VRegister& vd, - const VRegister& vn); - - // One-element structure store from one register. - void st1(const VRegister& vt, - const MemOperand& src); - - // One-element structure store from two registers. - void st1(const VRegister& vt, - const VRegister& vt2, - const MemOperand& src); - - // One-element structure store from three registers. - void st1(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const MemOperand& src); - - // One-element structure store from four registers. - void st1(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const VRegister& vt4, - const MemOperand& src); - - // One-element single structure store from one lane. - void st1(const VRegister& vt, - int lane, - const MemOperand& src); - - // Two-element structure store from two registers. - void st2(const VRegister& vt, - const VRegister& vt2, - const MemOperand& src); - - // Two-element single structure store from two lanes. - void st2(const VRegister& vt, - const VRegister& vt2, - int lane, - const MemOperand& src); - - // Three-element structure store from three registers. - void st3(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const MemOperand& src); - - // Three-element single structure store from three lanes. - void st3(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - int lane, - const MemOperand& src); - - // Four-element structure store from four registers. - void st4(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const VRegister& vt4, - const MemOperand& src); - - // Four-element single structure store from four lanes. - void st4(const VRegister& vt, - const VRegister& vt2, - const VRegister& vt3, - const VRegister& vt4, - int lane, - const MemOperand& src); - - // Unsigned add long. - void uaddl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned add long (second part). - void uaddl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned add wide. - void uaddw(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned add wide (second part). - void uaddw2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed add long. - void saddl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed add long (second part). - void saddl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed add wide. - void saddw(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed add wide (second part). - void saddw2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned subtract long. - void usubl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned subtract long (second part). - void usubl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned subtract wide. - void usubw(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned subtract wide (second part). - void usubw2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed subtract long. - void ssubl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed subtract long (second part). - void ssubl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed integer subtract wide. - void ssubw(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed integer subtract wide (second part). - void ssubw2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned maximum. - void umax(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned pairwise maximum. - void umaxp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned maximum across vector. - void umaxv(const VRegister& vd, - const VRegister& vn); - - // Unsigned minimum. - void umin(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned pairwise minimum. - void uminp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned minimum across vector. - void uminv(const VRegister& vd, - const VRegister& vn); - - // Transpose vectors (primary). - void trn1(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Transpose vectors (secondary). - void trn2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unzip vectors (primary). - void uzp1(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unzip vectors (secondary). - void uzp2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Zip vectors (primary). - void zip1(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Zip vectors (secondary). - void zip2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed shift right by immediate. - void sshr(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned shift right by immediate. - void ushr(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed rounding shift right by immediate. - void srshr(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned rounding shift right by immediate. - void urshr(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed shift right by immediate and accumulate. - void ssra(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned shift right by immediate and accumulate. - void usra(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed rounding shift right by immediate and accumulate. - void srsra(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned rounding shift right by immediate and accumulate. - void ursra(const VRegister& vd, - const VRegister& vn, - int shift); - - // Shift right narrow by immediate. - void shrn(const VRegister& vd, - const VRegister& vn, - int shift); - - // Shift right narrow by immediate (second part). - void shrn2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Rounding shift right narrow by immediate. - void rshrn(const VRegister& vd, - const VRegister& vn, - int shift); - - // Rounding shift right narrow by immediate (second part). - void rshrn2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned saturating shift right narrow by immediate. - void uqshrn(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned saturating shift right narrow by immediate (second part). - void uqshrn2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned saturating rounding shift right narrow by immediate. - void uqrshrn(const VRegister& vd, - const VRegister& vn, - int shift); - - // Unsigned saturating rounding shift right narrow by immediate (second part). - void uqrshrn2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating shift right narrow by immediate. - void sqshrn(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating shift right narrow by immediate (second part). - void sqshrn2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating rounded shift right narrow by immediate. - void sqrshrn(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating rounded shift right narrow by immediate (second part). - void sqrshrn2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating shift right unsigned narrow by immediate. - void sqshrun(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed saturating shift right unsigned narrow by immediate (second part). - void sqshrun2(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed sat rounded shift right unsigned narrow by immediate. - void sqrshrun(const VRegister& vd, - const VRegister& vn, - int shift); - - // Signed sat rounded shift right unsigned narrow by immediate (second part). - void sqrshrun2(const VRegister& vd, - const VRegister& vn, - int shift); - - // FP reciprocal step. - void frecps(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP reciprocal estimate. - void frecpe(const VRegister& vd, - const VRegister& vn); - - // FP reciprocal square root estimate. - void frsqrte(const VRegister& vd, - const VRegister& vn); - - // FP reciprocal square root step. - void frsqrts(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed absolute difference and accumulate long. - void sabal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed absolute difference and accumulate long (second part). - void sabal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned absolute difference and accumulate long. - void uabal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned absolute difference and accumulate long (second part). - void uabal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed absolute difference long. - void sabdl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed absolute difference long (second part). - void sabdl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned absolute difference long. - void uabdl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned absolute difference long (second part). - void uabdl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Polynomial multiply long. - void pmull(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Polynomial multiply long (second part). - void pmull2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed long multiply-add. - void smlal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed long multiply-add (second part). - void smlal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned long multiply-add. - void umlal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned long multiply-add (second part). - void umlal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed long multiply-sub. - void smlsl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed long multiply-sub (second part). - void smlsl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned long multiply-sub. - void umlsl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned long multiply-sub (second part). - void umlsl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed long multiply. - void smull(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed long multiply (second part). - void smull2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling long multiply-add. - void sqdmlal(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling long multiply-add (second part). - void sqdmlal2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling long multiply-subtract. - void sqdmlsl(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling long multiply-subtract (second part). - void sqdmlsl2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling long multiply. - void sqdmull(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling long multiply (second part). - void sqdmull2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling multiply returning high half. - void sqdmulh(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating rounding doubling multiply returning high half. - void sqrdmulh(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Signed saturating doubling multiply element returning high half. - void sqdmulh(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Signed saturating rounding doubling multiply element returning high half. - void sqrdmulh(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // Unsigned long multiply long. - void umull(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Unsigned long multiply (second part). - void umull2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Add narrow returning high half. - void addhn(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Add narrow returning high half (second part). - void addhn2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Rounding add narrow returning high half. - void raddhn(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Rounding add narrow returning high half (second part). - void raddhn2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Subtract narrow returning high half. - void subhn(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Subtract narrow returning high half (second part). - void subhn2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Rounding subtract narrow returning high half. - void rsubhn(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // Rounding subtract narrow returning high half (second part). - void rsubhn2(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP vector multiply accumulate. - void fmla(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP vector multiply subtract. - void fmls(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP vector multiply extended. - void fmulx(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP absolute greater than or equal. - void facge(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP absolute greater than. - void facgt(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP multiply by element. - void fmul(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // FP fused multiply-add to accumulator by element. - void fmla(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // FP fused multiply-sub from accumulator by element. - void fmls(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // FP multiply extended by element. - void fmulx(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index); - - // FP compare equal. - void fcmeq(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP greater than. - void fcmgt(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP greater than or equal. - void fcmge(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP compare equal to zero. - void fcmeq(const VRegister& vd, - const VRegister& vn, - double imm); - - // FP greater than zero. - void fcmgt(const VRegister& vd, - const VRegister& vn, - double imm); - - // FP greater than or equal to zero. - void fcmge(const VRegister& vd, - const VRegister& vn, - double imm); - - // FP less than or equal to zero. - void fcmle(const VRegister& vd, - const VRegister& vn, - double imm); - - // FP less than to zero. - void fcmlt(const VRegister& vd, - const VRegister& vn, - double imm); - - // FP absolute difference. - void fabd(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP pairwise add vector. - void faddp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP pairwise add scalar. - void faddp(const VRegister& vd, - const VRegister& vn); - - // FP pairwise maximum vector. - void fmaxp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP pairwise maximum scalar. - void fmaxp(const VRegister& vd, - const VRegister& vn); - - // FP pairwise minimum vector. - void fminp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP pairwise minimum scalar. - void fminp(const VRegister& vd, - const VRegister& vn); - - // FP pairwise maximum number vector. - void fmaxnmp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP pairwise maximum number scalar. - void fmaxnmp(const VRegister& vd, - const VRegister& vn); - - // FP pairwise minimum number vector. - void fminnmp(const VRegister& vd, - const VRegister& vn, - const VRegister& vm); - - // FP pairwise minimum number scalar. - void fminnmp(const VRegister& vd, - const VRegister& vn); - - // Emit generic instructions. - // Emit raw instructions into the instruction stream. - void dci(Instr raw_inst) { Emit(raw_inst); } - - // Emit 32 bits of data into the instruction stream. - void dc32(uint32_t data) { - VIXL_ASSERT(buffer_monitor_ > 0); - buffer_->Emit32(data); - } - - // Emit 64 bits of data into the instruction stream. - void dc64(uint64_t data) { - VIXL_ASSERT(buffer_monitor_ > 0); - buffer_->Emit64(data); - } - - // Copy a string into the instruction stream, including the terminating NULL - // character. The instruction pointer is then aligned correctly for - // subsequent instructions. - void EmitString(const char * string) { - VIXL_ASSERT(string != NULL); - VIXL_ASSERT(buffer_monitor_ > 0); - - buffer_->EmitString(string); - buffer_->Align(); - } - - // Code generation helpers. - - // Register encoding. - static Instr Rd(CPURegister rd) { - VIXL_ASSERT(rd.code() != kSPRegInternalCode); - return rd.code() << Rd_offset; - } - - static Instr Rn(CPURegister rn) { - VIXL_ASSERT(rn.code() != kSPRegInternalCode); - return rn.code() << Rn_offset; - } - - static Instr Rm(CPURegister rm) { - VIXL_ASSERT(rm.code() != kSPRegInternalCode); - return rm.code() << Rm_offset; - } - - static Instr RmNot31(CPURegister rm) { - VIXL_ASSERT(rm.code() != kSPRegInternalCode); - VIXL_ASSERT(!rm.IsZero()); - return Rm(rm); - } - - static Instr Ra(CPURegister ra) { - VIXL_ASSERT(ra.code() != kSPRegInternalCode); - return ra.code() << Ra_offset; - } - - static Instr Rt(CPURegister rt) { - VIXL_ASSERT(rt.code() != kSPRegInternalCode); - return rt.code() << Rt_offset; - } - - static Instr Rt2(CPURegister rt2) { - VIXL_ASSERT(rt2.code() != kSPRegInternalCode); - return rt2.code() << Rt2_offset; - } - - static Instr Rs(CPURegister rs) { - VIXL_ASSERT(rs.code() != kSPRegInternalCode); - return rs.code() << Rs_offset; - } - - // These encoding functions allow the stack pointer to be encoded, and - // disallow the zero register. - static Instr RdSP(Register rd) { - VIXL_ASSERT(!rd.IsZero()); - return (rd.code() & kRegCodeMask) << Rd_offset; - } - - static Instr RnSP(Register rn) { - VIXL_ASSERT(!rn.IsZero()); - return (rn.code() & kRegCodeMask) << Rn_offset; - } - - // Flags encoding. - static Instr Flags(FlagsUpdate S) { - if (S == SetFlags) { - return 1 << FlagsUpdate_offset; - } else if (S == LeaveFlags) { - return 0 << FlagsUpdate_offset; - } - VIXL_UNREACHABLE(); - return 0; - } - - static Instr Cond(Condition cond) { - return cond << Condition_offset; - } - - // PC-relative address encoding. - static Instr ImmPCRelAddress(int imm21) { - VIXL_ASSERT(is_int21(imm21)); - Instr imm = static_cast(truncate_to_int21(imm21)); - Instr immhi = (imm >> ImmPCRelLo_width) << ImmPCRelHi_offset; - Instr immlo = imm << ImmPCRelLo_offset; - return (immhi & ImmPCRelHi_mask) | (immlo & ImmPCRelLo_mask); - } - - // Branch encoding. - static Instr ImmUncondBranch(int imm26) { - VIXL_ASSERT(is_int26(imm26)); - return truncate_to_int26(imm26) << ImmUncondBranch_offset; - } - - static Instr ImmCondBranch(int imm19) { - VIXL_ASSERT(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmCondBranch_offset; - } - - static Instr ImmCmpBranch(int imm19) { - VIXL_ASSERT(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmCmpBranch_offset; - } - - static Instr ImmTestBranch(int imm14) { - VIXL_ASSERT(is_int14(imm14)); - return truncate_to_int14(imm14) << ImmTestBranch_offset; - } - - static Instr ImmTestBranchBit(unsigned bit_pos) { - VIXL_ASSERT(is_uint6(bit_pos)); - // Subtract five from the shift offset, as we need bit 5 from bit_pos. - unsigned b5 = bit_pos << (ImmTestBranchBit5_offset - 5); - unsigned b40 = bit_pos << ImmTestBranchBit40_offset; - b5 &= ImmTestBranchBit5_mask; - b40 &= ImmTestBranchBit40_mask; - return b5 | b40; - } - - // Data Processing encoding. - static Instr SF(Register rd) { - return rd.Is64Bits() ? SixtyFourBits : ThirtyTwoBits; - } - - static Instr ImmAddSub(int imm) { - VIXL_ASSERT(IsImmAddSub(imm)); - if (is_uint12(imm)) { // No shift required. - imm <<= ImmAddSub_offset; - } else { - imm = ((imm >> 12) << ImmAddSub_offset) | (1 << ShiftAddSub_offset); - } - return imm; - } - - static Instr ImmS(unsigned imms, unsigned reg_size) { - VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(imms)) || - ((reg_size == kWRegSize) && is_uint5(imms))); - USE(reg_size); - return imms << ImmS_offset; - } - - static Instr ImmR(unsigned immr, unsigned reg_size) { - VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || - ((reg_size == kWRegSize) && is_uint5(immr))); - USE(reg_size); - VIXL_ASSERT(is_uint6(immr)); - return immr << ImmR_offset; - } - - static Instr ImmSetBits(unsigned imms, unsigned reg_size) { - VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - VIXL_ASSERT(is_uint6(imms)); - VIXL_ASSERT((reg_size == kXRegSize) || is_uint6(imms + 3)); - USE(reg_size); - return imms << ImmSetBits_offset; - } - - static Instr ImmRotate(unsigned immr, unsigned reg_size) { - VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || - ((reg_size == kWRegSize) && is_uint5(immr))); - USE(reg_size); - return immr << ImmRotate_offset; - } - - static Instr ImmLLiteral(int imm19) { - VIXL_ASSERT(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmLLiteral_offset; - } - - static Instr BitN(unsigned bitn, unsigned reg_size) { - VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - VIXL_ASSERT((reg_size == kXRegSize) || (bitn == 0)); - USE(reg_size); - return bitn << BitN_offset; - } - - static Instr ShiftDP(Shift shift) { - VIXL_ASSERT(shift == LSL || shift == LSR || shift == ASR || shift == ROR); - return shift << ShiftDP_offset; - } - - static Instr ImmDPShift(unsigned amount) { - VIXL_ASSERT(is_uint6(amount)); - return amount << ImmDPShift_offset; - } - - static Instr ExtendMode(Extend extend) { - return extend << ExtendMode_offset; - } - - static Instr ImmExtendShift(unsigned left_shift) { - VIXL_ASSERT(left_shift <= 4); - return left_shift << ImmExtendShift_offset; - } - - static Instr ImmCondCmp(unsigned imm) { - VIXL_ASSERT(is_uint5(imm)); - return imm << ImmCondCmp_offset; - } - - static Instr Nzcv(StatusFlags nzcv) { - return ((nzcv >> Flags_offset) & 0xf) << Nzcv_offset; - } - - // MemOperand offset encoding. - static Instr ImmLSUnsigned(int imm12) { - VIXL_ASSERT(is_uint12(imm12)); - return imm12 << ImmLSUnsigned_offset; - } - - static Instr ImmLS(int imm9) { - VIXL_ASSERT(is_int9(imm9)); - return truncate_to_int9(imm9) << ImmLS_offset; - } - - static Instr ImmLSPair(int imm7, unsigned access_size) { - VIXL_ASSERT(((imm7 >> access_size) << access_size) == imm7); - int scaled_imm7 = imm7 >> access_size; - VIXL_ASSERT(is_int7(scaled_imm7)); - return truncate_to_int7(scaled_imm7) << ImmLSPair_offset; - } - - static Instr ImmShiftLS(unsigned shift_amount) { - VIXL_ASSERT(is_uint1(shift_amount)); - return shift_amount << ImmShiftLS_offset; - } - - static Instr ImmPrefetchOperation(int imm5) { - VIXL_ASSERT(is_uint5(imm5)); - return imm5 << ImmPrefetchOperation_offset; - } - - static Instr ImmException(int imm16) { - VIXL_ASSERT(is_uint16(imm16)); - return imm16 << ImmException_offset; - } - - static Instr ImmSystemRegister(int imm15) { - VIXL_ASSERT(is_uint15(imm15)); - return imm15 << ImmSystemRegister_offset; - } - - static Instr ImmHint(int imm7) { - VIXL_ASSERT(is_uint7(imm7)); - return imm7 << ImmHint_offset; - } - - static Instr CRm(int imm4) { - VIXL_ASSERT(is_uint4(imm4)); - return imm4 << CRm_offset; - } - - static Instr CRn(int imm4) { - VIXL_ASSERT(is_uint4(imm4)); - return imm4 << CRn_offset; - } - - static Instr SysOp(int imm14) { - VIXL_ASSERT(is_uint14(imm14)); - return imm14 << SysOp_offset; - } - - static Instr ImmSysOp1(int imm3) { - VIXL_ASSERT(is_uint3(imm3)); - return imm3 << SysOp1_offset; - } - - static Instr ImmSysOp2(int imm3) { - VIXL_ASSERT(is_uint3(imm3)); - return imm3 << SysOp2_offset; - } - - static Instr ImmBarrierDomain(int imm2) { - VIXL_ASSERT(is_uint2(imm2)); - return imm2 << ImmBarrierDomain_offset; - } - - static Instr ImmBarrierType(int imm2) { - VIXL_ASSERT(is_uint2(imm2)); - return imm2 << ImmBarrierType_offset; - } - - // Move immediates encoding. - static Instr ImmMoveWide(uint64_t imm) { - VIXL_ASSERT(is_uint16(imm)); - return static_cast(imm << ImmMoveWide_offset); - } - - static Instr ShiftMoveWide(int64_t shift) { - VIXL_ASSERT(is_uint2(shift)); - return static_cast(shift << ShiftMoveWide_offset); - } - - // FP Immediates. - static Instr ImmFP32(float imm); - static Instr ImmFP64(double imm); - - // FP register type. - static Instr FPType(FPRegister fd) { - return fd.Is64Bits() ? FP64 : FP32; - } - - static Instr FPScale(unsigned scale) { - VIXL_ASSERT(is_uint6(scale)); - return scale << FPScale_offset; - } - - // Immediate field checking helpers. - static bool IsImmAddSub(int64_t immediate); - static bool IsImmConditionalCompare(int64_t immediate); - static bool IsImmFP32(float imm); - static bool IsImmFP64(double imm); - static bool IsImmLogical(uint64_t value, - unsigned width, - unsigned* n = NULL, - unsigned* imm_s = NULL, - unsigned* imm_r = NULL); - static bool IsImmLSPair(int64_t offset, unsigned access_size); - static bool IsImmLSScaled(int64_t offset, unsigned access_size); - static bool IsImmLSUnscaled(int64_t offset); - static bool IsImmMovn(uint64_t imm, unsigned reg_size); - static bool IsImmMovz(uint64_t imm, unsigned reg_size); - - // Instruction bits for vector format in data processing operations. - static Instr VFormat(VRegister vd) { - if (vd.Is64Bits()) { - switch (vd.lanes()) { - case 2: return NEON_2S; - case 4: return NEON_4H; - case 8: return NEON_8B; - default: return 0xffffffff; - } - } else { - VIXL_ASSERT(vd.Is128Bits()); - switch (vd.lanes()) { - case 2: return NEON_2D; - case 4: return NEON_4S; - case 8: return NEON_8H; - case 16: return NEON_16B; - default: return 0xffffffff; - } - } - } - - // Instruction bits for vector format in floating point data processing - // operations. - static Instr FPFormat(VRegister vd) { - if (vd.lanes() == 1) { - // Floating point scalar formats. - VIXL_ASSERT(vd.Is32Bits() || vd.Is64Bits()); - return vd.Is64Bits() ? FP64 : FP32; - } - - // Two lane floating point vector formats. - if (vd.lanes() == 2) { - VIXL_ASSERT(vd.Is64Bits() || vd.Is128Bits()); - return vd.Is128Bits() ? NEON_FP_2D : NEON_FP_2S; - } - - // Four lane floating point vector format. - VIXL_ASSERT((vd.lanes() == 4) && vd.Is128Bits()); - return NEON_FP_4S; - } - - // Instruction bits for vector format in load and store operations. - static Instr LSVFormat(VRegister vd) { - if (vd.Is64Bits()) { - switch (vd.lanes()) { - case 1: return LS_NEON_1D; - case 2: return LS_NEON_2S; - case 4: return LS_NEON_4H; - case 8: return LS_NEON_8B; - default: return 0xffffffff; - } - } else { - VIXL_ASSERT(vd.Is128Bits()); - switch (vd.lanes()) { - case 2: return LS_NEON_2D; - case 4: return LS_NEON_4S; - case 8: return LS_NEON_8H; - case 16: return LS_NEON_16B; - default: return 0xffffffff; - } - } - } - - // Instruction bits for scalar format in data processing operations. - static Instr SFormat(VRegister vd) { - VIXL_ASSERT(vd.lanes() == 1); - switch (vd.SizeInBytes()) { - case 1: return NEON_B; - case 2: return NEON_H; - case 4: return NEON_S; - case 8: return NEON_D; - default: return 0xffffffff; - } - } - - static Instr ImmNEONHLM(int index, int num_bits) { - int h, l, m; - if (num_bits == 3) { - VIXL_ASSERT(is_uint3(index)); - h = (index >> 2) & 1; - l = (index >> 1) & 1; - m = (index >> 0) & 1; - } else if (num_bits == 2) { - VIXL_ASSERT(is_uint2(index)); - h = (index >> 1) & 1; - l = (index >> 0) & 1; - m = 0; - } else { - VIXL_ASSERT(is_uint1(index) && (num_bits == 1)); - h = (index >> 0) & 1; - l = 0; - m = 0; - } - return (h << NEONH_offset) | (l << NEONL_offset) | (m << NEONM_offset); - } - - static Instr ImmNEONExt(int imm4) { - VIXL_ASSERT(is_uint4(imm4)); - return imm4 << ImmNEONExt_offset; - } - - static Instr ImmNEON5(Instr format, int index) { - VIXL_ASSERT(is_uint4(index)); - int s = LaneSizeInBytesLog2FromFormat(static_cast(format)); - int imm5 = (index << (s + 1)) | (1 << s); - return imm5 << ImmNEON5_offset; - } - - static Instr ImmNEON4(Instr format, int index) { - VIXL_ASSERT(is_uint4(index)); - int s = LaneSizeInBytesLog2FromFormat(static_cast(format)); - int imm4 = index << s; - return imm4 << ImmNEON4_offset; - } - - static Instr ImmNEONabcdefgh(int imm8) { - VIXL_ASSERT(is_uint8(imm8)); - Instr instr; - instr = ((imm8 >> 5) & 7) << ImmNEONabc_offset; - instr |= (imm8 & 0x1f) << ImmNEONdefgh_offset; - return instr; - } - - static Instr NEONCmode(int cmode) { - VIXL_ASSERT(is_uint4(cmode)); - return cmode << NEONCmode_offset; - } - - static Instr NEONModImmOp(int op) { - VIXL_ASSERT(is_uint1(op)); - return op << NEONModImmOp_offset; - } - - // Size of the code generated since label to the current position. - size_t SizeOfCodeGeneratedSince(Label* label) const { - VIXL_ASSERT(label->IsBound()); - return buffer_->OffsetFrom(label->location()); - } - - size_t SizeOfCodeGenerated() const { - return buffer_->CursorOffset(); - } - - size_t BufferCapacity() const { return buffer_->capacity(); } - - size_t RemainingBufferSpace() const { return buffer_->RemainingBytes(); } - - void EnsureSpaceFor(size_t amount) { - if (buffer_->RemainingBytes() < amount) { - size_t capacity = buffer_->capacity(); - size_t size = buffer_->CursorOffset(); - do { - // TODO(all): refine. - capacity *= 2; - } while ((capacity - size) < amount); - buffer_->Grow(capacity); - } - } - -#ifdef VIXL_DEBUG - void AcquireBuffer() { - VIXL_ASSERT(buffer_monitor_ >= 0); - buffer_monitor_++; - } - - void ReleaseBuffer() { - buffer_monitor_--; - VIXL_ASSERT(buffer_monitor_ >= 0); - } -#endif - - PositionIndependentCodeOption pic() const { - return pic_; - } - - bool AllowPageOffsetDependentCode() const { - return (pic() == PageOffsetDependentCode) || - (pic() == PositionDependentCode); - } - - static const Register& AppropriateZeroRegFor(const CPURegister& reg) { - return reg.Is64Bits() ? xzr : wzr; - } - - - protected: - void LoadStore(const CPURegister& rt, - const MemOperand& addr, - LoadStoreOp op, - LoadStoreScalingOption option = PreferScaledOffset); - - void LoadStorePair(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& addr, - LoadStorePairOp op); - void LoadStoreStruct(const VRegister& vt, - const MemOperand& addr, - NEONLoadStoreMultiStructOp op); - void LoadStoreStruct1(const VRegister& vt, - int reg_count, - const MemOperand& addr); - void LoadStoreStructSingle(const VRegister& vt, - uint32_t lane, - const MemOperand& addr, - NEONLoadStoreSingleStructOp op); - void LoadStoreStructSingleAllLanes(const VRegister& vt, - const MemOperand& addr, - NEONLoadStoreSingleStructOp op); - void LoadStoreStructVerify(const VRegister& vt, - const MemOperand& addr, - Instr op); - - void Prefetch(PrefetchOperation op, - const MemOperand& addr, - LoadStoreScalingOption option = PreferScaledOffset); - - // TODO(all): The third parameter should be passed by reference but gcc 4.8.2 - // reports a bogus uninitialised warning then. - void Logical(const Register& rd, - const Register& rn, - const Operand operand, - LogicalOp op); - void LogicalImmediate(const Register& rd, - const Register& rn, - unsigned n, - unsigned imm_s, - unsigned imm_r, - LogicalOp op); - - void ConditionalCompare(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond, - ConditionalCompareOp op); - - void AddSubWithCarry(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - AddSubWithCarryOp op); - - - // Functions for emulating operands not directly supported by the instruction - // set. - void EmitShift(const Register& rd, - const Register& rn, - Shift shift, - unsigned amount); - void EmitExtendShift(const Register& rd, - const Register& rn, - Extend extend, - unsigned left_shift); - - void AddSub(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - AddSubOp op); - - void NEONTable(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - NEONTableOp op); - - // Find an appropriate LoadStoreOp or LoadStorePairOp for the specified - // registers. Only simple loads are supported; sign- and zero-extension (such - // as in LDPSW_x or LDRB_w) are not supported. - static LoadStoreOp LoadOpFor(const CPURegister& rt); - static LoadStorePairOp LoadPairOpFor(const CPURegister& rt, - const CPURegister& rt2); - static LoadStoreOp StoreOpFor(const CPURegister& rt); - static LoadStorePairOp StorePairOpFor(const CPURegister& rt, - const CPURegister& rt2); - static LoadStorePairNonTemporalOp LoadPairNonTemporalOpFor( - const CPURegister& rt, const CPURegister& rt2); - static LoadStorePairNonTemporalOp StorePairNonTemporalOpFor( - const CPURegister& rt, const CPURegister& rt2); - static LoadLiteralOp LoadLiteralOpFor(const CPURegister& rt); - - - private: - static uint32_t FP32ToImm8(float imm); - static uint32_t FP64ToImm8(double imm); - - // Instruction helpers. - void MoveWide(const Register& rd, - uint64_t imm, - int shift, - MoveWideImmediateOp mov_op); - void DataProcShiftedRegister(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - Instr op); - void DataProcExtendedRegister(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - Instr op); - void LoadStorePairNonTemporal(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& addr, - LoadStorePairNonTemporalOp op); - void LoadLiteral(const CPURegister& rt, uint64_t imm, LoadLiteralOp op); - void ConditionalSelect(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond, - ConditionalSelectOp op); - void DataProcessing1Source(const Register& rd, - const Register& rn, - DataProcessing1SourceOp op); - void DataProcessing3Source(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra, - DataProcessing3SourceOp op); - void FPDataProcessing1Source(const VRegister& fd, - const VRegister& fn, - FPDataProcessing1SourceOp op); - void FPDataProcessing3Source(const VRegister& fd, - const VRegister& fn, - const VRegister& fm, - const VRegister& fa, - FPDataProcessing3SourceOp op); - void NEONAcrossLanesL(const VRegister& vd, - const VRegister& vn, - NEONAcrossLanesOp op); - void NEONAcrossLanes(const VRegister& vd, - const VRegister& vn, - NEONAcrossLanesOp op); - void NEONModifiedImmShiftLsl(const VRegister& vd, - const int imm8, - const int left_shift, - NEONModifiedImmediateOp op); - void NEONModifiedImmShiftMsl(const VRegister& vd, - const int imm8, - const int shift_amount, - NEONModifiedImmediateOp op); - void NEONFP2Same(const VRegister& vd, - const VRegister& vn, - Instr vop); - void NEON3Same(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - NEON3SameOp vop); - void NEONFP3Same(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - Instr op); - void NEON3DifferentL(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - NEON3DifferentOp vop); - void NEON3DifferentW(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - NEON3DifferentOp vop); - void NEON3DifferentHN(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - NEON3DifferentOp vop); - void NEONFP2RegMisc(const VRegister& vd, - const VRegister& vn, - NEON2RegMiscOp vop, - double value = 0.0); - void NEON2RegMisc(const VRegister& vd, - const VRegister& vn, - NEON2RegMiscOp vop, - int value = 0); - void NEONFP2RegMisc(const VRegister& vd, - const VRegister& vn, - Instr op); - void NEONAddlp(const VRegister& vd, - const VRegister& vn, - NEON2RegMiscOp op); - void NEONPerm(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - NEONPermOp op); - void NEONFPByElement(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index, - NEONByIndexedElementOp op); - void NEONByElement(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index, - NEONByIndexedElementOp op); - void NEONByElementL(const VRegister& vd, - const VRegister& vn, - const VRegister& vm, - int vm_index, - NEONByIndexedElementOp op); - void NEONShiftImmediate(const VRegister& vd, - const VRegister& vn, - NEONShiftImmediateOp op, - int immh_immb); - void NEONShiftLeftImmediate(const VRegister& vd, - const VRegister& vn, - int shift, - NEONShiftImmediateOp op); - void NEONShiftRightImmediate(const VRegister& vd, - const VRegister& vn, - int shift, - NEONShiftImmediateOp op); - void NEONShiftImmediateL(const VRegister& vd, - const VRegister& vn, - int shift, - NEONShiftImmediateOp op); - void NEONShiftImmediateN(const VRegister& vd, - const VRegister& vn, - int shift, - NEONShiftImmediateOp op); - void NEONXtn(const VRegister& vd, - const VRegister& vn, - NEON2RegMiscOp vop); - - Instr LoadStoreStructAddrModeField(const MemOperand& addr); - - // Encode the specified MemOperand for the specified access size and scaling - // preference. - Instr LoadStoreMemOperand(const MemOperand& addr, - unsigned access_size, - LoadStoreScalingOption option); - - // Link the current (not-yet-emitted) instruction to the specified label, then - // return an offset to be encoded in the instruction. If the label is not yet - // bound, an offset of 0 is returned. - ptrdiff_t LinkAndGetByteOffsetTo(Label * label); - ptrdiff_t LinkAndGetInstructionOffsetTo(Label * label); - ptrdiff_t LinkAndGetPageOffsetTo(Label * label); - - // A common implementation for the LinkAndGetOffsetTo helpers. - template - ptrdiff_t LinkAndGetOffsetTo(Label* label); - - // Literal load offset are in words (32-bit). - ptrdiff_t LinkAndGetWordOffsetTo(RawLiteral* literal); - - // Emit the instruction in buffer_. - void Emit(Instr instruction) { - VIXL_STATIC_ASSERT(sizeof(instruction) == kInstructionSize); - VIXL_ASSERT(buffer_monitor_ > 0); - buffer_->Emit32(instruction); - } - - // Buffer where the code is emitted. - CodeBuffer* buffer_; - PositionIndependentCodeOption pic_; - -#ifdef VIXL_DEBUG - int64_t buffer_monitor_; -#endif -}; - - -// All Assembler emits MUST acquire/release the underlying code buffer. The -// helper scope below will do so and optionally ensure the buffer is big enough -// to receive the emit. It is possible to request the scope not to perform any -// checks (kNoCheck) if for example it is known in advance the buffer size is -// adequate or there is some other size checking mechanism in place. -class CodeBufferCheckScope { - public: - // Tell whether or not the scope needs to ensure the associated CodeBuffer - // has enough space for the requested size. - enum CheckPolicy { - kNoCheck, - kCheck - }; - - // Tell whether or not the scope should assert the amount of code emitted - // within the scope is consistent with the requested amount. - enum AssertPolicy { - kNoAssert, // No assert required. - kExactSize, // The code emitted must be exactly size bytes. - kMaximumSize // The code emitted must be at most size bytes. - }; - - CodeBufferCheckScope(Assembler* assm, - size_t size, - CheckPolicy check_policy = kCheck, - AssertPolicy assert_policy = kMaximumSize) - : assm_(assm) { - if (check_policy == kCheck) assm->EnsureSpaceFor(size); -#ifdef VIXL_DEBUG - assm->bind(&start_); - size_ = size; - assert_policy_ = assert_policy; - assm->AcquireBuffer(); -#else - USE(assert_policy); -#endif - } - - // This is a shortcut for CodeBufferCheckScope(assm, 0, kNoCheck, kNoAssert). - explicit CodeBufferCheckScope(Assembler* assm) : assm_(assm) { -#ifdef VIXL_DEBUG - size_ = 0; - assert_policy_ = kNoAssert; - assm->AcquireBuffer(); -#endif - } - - ~CodeBufferCheckScope() { -#ifdef VIXL_DEBUG - assm_->ReleaseBuffer(); - switch (assert_policy_) { - case kNoAssert: break; - case kExactSize: - VIXL_ASSERT(assm_->SizeOfCodeGeneratedSince(&start_) == size_); - break; - case kMaximumSize: - VIXL_ASSERT(assm_->SizeOfCodeGeneratedSince(&start_) <= size_); - break; - default: - VIXL_UNREACHABLE(); - } -#endif - } - - protected: - Assembler* assm_; -#ifdef VIXL_DEBUG - Label start_; - size_t size_; - AssertPolicy assert_policy_; -#endif -}; - - -template -void Literal::UpdateValue(T new_value, const Assembler* assembler) { - return UpdateValue(new_value, assembler->GetStartAddress()); -} - - -template -void Literal::UpdateValue(T high64, T low64, const Assembler* assembler) { - return UpdateValue(high64, low64, assembler->GetStartAddress()); -} - - -} // namespace vixl - -#endif // VIXL_A64_ASSEMBLER_A64_H_ diff --git a/disas/libvixl/vixl/a64/constants-a64.h b/disas/libvixl/vixl/a64/constants-a64.h deleted file mode 100644 index 2caa73af87..0000000000 --- a/disas/libvixl/vixl/a64/constants-a64.h +++ /dev/null @@ -1,2116 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#ifndef VIXL_A64_CONSTANTS_A64_H_ -#define VIXL_A64_CONSTANTS_A64_H_ - -namespace vixl { - -const unsigned kNumberOfRegisters = 32; -const unsigned kNumberOfVRegisters = 32; -const unsigned kNumberOfFPRegisters = kNumberOfVRegisters; -// Callee saved registers are x21-x30(lr). -const int kNumberOfCalleeSavedRegisters = 10; -const int kFirstCalleeSavedRegisterIndex = 21; -// Callee saved FP registers are d8-d15. -const int kNumberOfCalleeSavedFPRegisters = 8; -const int kFirstCalleeSavedFPRegisterIndex = 8; - -#define REGISTER_CODE_LIST(R) \ -R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \ -R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \ -R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \ -R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31) - -#define INSTRUCTION_FIELDS_LIST(V_) \ -/* Register fields */ \ -V_(Rd, 4, 0, Bits) /* Destination register. */ \ -V_(Rn, 9, 5, Bits) /* First source register. */ \ -V_(Rm, 20, 16, Bits) /* Second source register. */ \ -V_(Ra, 14, 10, Bits) /* Third source register. */ \ -V_(Rt, 4, 0, Bits) /* Load/store register. */ \ -V_(Rt2, 14, 10, Bits) /* Load/store second register. */ \ -V_(Rs, 20, 16, Bits) /* Exclusive access status. */ \ - \ -/* Common bits */ \ -V_(SixtyFourBits, 31, 31, Bits) \ -V_(FlagsUpdate, 29, 29, Bits) \ - \ -/* PC relative addressing */ \ -V_(ImmPCRelHi, 23, 5, SignedBits) \ -V_(ImmPCRelLo, 30, 29, Bits) \ - \ -/* Add/subtract/logical shift register */ \ -V_(ShiftDP, 23, 22, Bits) \ -V_(ImmDPShift, 15, 10, Bits) \ - \ -/* Add/subtract immediate */ \ -V_(ImmAddSub, 21, 10, Bits) \ -V_(ShiftAddSub, 23, 22, Bits) \ - \ -/* Add/substract extend */ \ -V_(ImmExtendShift, 12, 10, Bits) \ -V_(ExtendMode, 15, 13, Bits) \ - \ -/* Move wide */ \ -V_(ImmMoveWide, 20, 5, Bits) \ -V_(ShiftMoveWide, 22, 21, Bits) \ - \ -/* Logical immediate, bitfield and extract */ \ -V_(BitN, 22, 22, Bits) \ -V_(ImmRotate, 21, 16, Bits) \ -V_(ImmSetBits, 15, 10, Bits) \ -V_(ImmR, 21, 16, Bits) \ -V_(ImmS, 15, 10, Bits) \ - \ -/* Test and branch immediate */ \ -V_(ImmTestBranch, 18, 5, SignedBits) \ -V_(ImmTestBranchBit40, 23, 19, Bits) \ -V_(ImmTestBranchBit5, 31, 31, Bits) \ - \ -/* Conditionals */ \ -V_(Condition, 15, 12, Bits) \ -V_(ConditionBranch, 3, 0, Bits) \ -V_(Nzcv, 3, 0, Bits) \ -V_(ImmCondCmp, 20, 16, Bits) \ -V_(ImmCondBranch, 23, 5, SignedBits) \ - \ -/* Floating point */ \ -V_(FPType, 23, 22, Bits) \ -V_(ImmFP, 20, 13, Bits) \ -V_(FPScale, 15, 10, Bits) \ - \ -/* Load Store */ \ -V_(ImmLS, 20, 12, SignedBits) \ -V_(ImmLSUnsigned, 21, 10, Bits) \ -V_(ImmLSPair, 21, 15, SignedBits) \ -V_(ImmShiftLS, 12, 12, Bits) \ -V_(LSOpc, 23, 22, Bits) \ -V_(LSVector, 26, 26, Bits) \ -V_(LSSize, 31, 30, Bits) \ -V_(ImmPrefetchOperation, 4, 0, Bits) \ -V_(PrefetchHint, 4, 3, Bits) \ -V_(PrefetchTarget, 2, 1, Bits) \ -V_(PrefetchStream, 0, 0, Bits) \ - \ -/* Other immediates */ \ -V_(ImmUncondBranch, 25, 0, SignedBits) \ -V_(ImmCmpBranch, 23, 5, SignedBits) \ -V_(ImmLLiteral, 23, 5, SignedBits) \ -V_(ImmException, 20, 5, Bits) \ -V_(ImmHint, 11, 5, Bits) \ -V_(ImmBarrierDomain, 11, 10, Bits) \ -V_(ImmBarrierType, 9, 8, Bits) \ - \ -/* System (MRS, MSR, SYS) */ \ -V_(ImmSystemRegister, 19, 5, Bits) \ -V_(SysO0, 19, 19, Bits) \ -V_(SysOp, 18, 5, Bits) \ -V_(SysOp1, 18, 16, Bits) \ -V_(SysOp2, 7, 5, Bits) \ -V_(CRn, 15, 12, Bits) \ -V_(CRm, 11, 8, Bits) \ - \ -/* Load-/store-exclusive */ \ -V_(LdStXLoad, 22, 22, Bits) \ -V_(LdStXNotExclusive, 23, 23, Bits) \ -V_(LdStXAcquireRelease, 15, 15, Bits) \ -V_(LdStXSizeLog2, 31, 30, Bits) \ -V_(LdStXPair, 21, 21, Bits) \ - \ -/* NEON generic fields */ \ -V_(NEONQ, 30, 30, Bits) \ -V_(NEONSize, 23, 22, Bits) \ -V_(NEONLSSize, 11, 10, Bits) \ -V_(NEONS, 12, 12, Bits) \ -V_(NEONL, 21, 21, Bits) \ -V_(NEONM, 20, 20, Bits) \ -V_(NEONH, 11, 11, Bits) \ -V_(ImmNEONExt, 14, 11, Bits) \ -V_(ImmNEON5, 20, 16, Bits) \ -V_(ImmNEON4, 14, 11, Bits) \ - \ -/* NEON Modified Immediate fields */ \ -V_(ImmNEONabc, 18, 16, Bits) \ -V_(ImmNEONdefgh, 9, 5, Bits) \ -V_(NEONModImmOp, 29, 29, Bits) \ -V_(NEONCmode, 15, 12, Bits) \ - \ -/* NEON Shift Immediate fields */ \ -V_(ImmNEONImmhImmb, 22, 16, Bits) \ -V_(ImmNEONImmh, 22, 19, Bits) \ -V_(ImmNEONImmb, 18, 16, Bits) - -#define SYSTEM_REGISTER_FIELDS_LIST(V_, M_) \ -/* NZCV */ \ -V_(Flags, 31, 28, Bits) \ -V_(N, 31, 31, Bits) \ -V_(Z, 30, 30, Bits) \ -V_(C, 29, 29, Bits) \ -V_(V, 28, 28, Bits) \ -M_(NZCV, Flags_mask) \ -/* FPCR */ \ -V_(AHP, 26, 26, Bits) \ -V_(DN, 25, 25, Bits) \ -V_(FZ, 24, 24, Bits) \ -V_(RMode, 23, 22, Bits) \ -M_(FPCR, AHP_mask | DN_mask | FZ_mask | RMode_mask) - -// Fields offsets. -#define DECLARE_FIELDS_OFFSETS(Name, HighBit, LowBit, X) \ -const int Name##_offset = LowBit; \ -const int Name##_width = HighBit - LowBit + 1; \ -const uint32_t Name##_mask = ((1 << Name##_width) - 1) << LowBit; -#define NOTHING(A, B) -INSTRUCTION_FIELDS_LIST(DECLARE_FIELDS_OFFSETS) -SYSTEM_REGISTER_FIELDS_LIST(DECLARE_FIELDS_OFFSETS, NOTHING) -#undef NOTHING -#undef DECLARE_FIELDS_BITS - -// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), formed -// from ImmPCRelLo and ImmPCRelHi. -const int ImmPCRel_mask = ImmPCRelLo_mask | ImmPCRelHi_mask; - -// Condition codes. -enum Condition { - eq = 0, // Z set Equal. - ne = 1, // Z clear Not equal. - cs = 2, // C set Carry set. - cc = 3, // C clear Carry clear. - mi = 4, // N set Negative. - pl = 5, // N clear Positive or zero. - vs = 6, // V set Overflow. - vc = 7, // V clear No overflow. - hi = 8, // C set, Z clear Unsigned higher. - ls = 9, // C clear or Z set Unsigned lower or same. - ge = 10, // N == V Greater or equal. - lt = 11, // N != V Less than. - gt = 12, // Z clear, N == V Greater than. - le = 13, // Z set or N != V Less then or equal - al = 14, // Always. - nv = 15, // Behaves as always/al. - - // Aliases. - hs = cs, // C set Unsigned higher or same. - lo = cc // C clear Unsigned lower. -}; - -inline Condition InvertCondition(Condition cond) { - // Conditions al and nv behave identically, as "always true". They can't be - // inverted, because there is no "always false" condition. - VIXL_ASSERT((cond != al) && (cond != nv)); - return static_cast(cond ^ 1); -} - -enum FPTrapFlags { - EnableTrap = 1, - DisableTrap = 0 -}; - -enum FlagsUpdate { - SetFlags = 1, - LeaveFlags = 0 -}; - -enum StatusFlags { - NoFlag = 0, - - // Derive the flag combinations from the system register bit descriptions. - NFlag = N_mask, - ZFlag = Z_mask, - CFlag = C_mask, - VFlag = V_mask, - NZFlag = NFlag | ZFlag, - NCFlag = NFlag | CFlag, - NVFlag = NFlag | VFlag, - ZCFlag = ZFlag | CFlag, - ZVFlag = ZFlag | VFlag, - CVFlag = CFlag | VFlag, - NZCFlag = NFlag | ZFlag | CFlag, - NZVFlag = NFlag | ZFlag | VFlag, - NCVFlag = NFlag | CFlag | VFlag, - ZCVFlag = ZFlag | CFlag | VFlag, - NZCVFlag = NFlag | ZFlag | CFlag | VFlag, - - // Floating-point comparison results. - FPEqualFlag = ZCFlag, - FPLessThanFlag = NFlag, - FPGreaterThanFlag = CFlag, - FPUnorderedFlag = CVFlag -}; - -enum Shift { - NO_SHIFT = -1, - LSL = 0x0, - LSR = 0x1, - ASR = 0x2, - ROR = 0x3, - MSL = 0x4 -}; - -enum Extend { - NO_EXTEND = -1, - UXTB = 0, - UXTH = 1, - UXTW = 2, - UXTX = 3, - SXTB = 4, - SXTH = 5, - SXTW = 6, - SXTX = 7 -}; - -enum SystemHint { - NOP = 0, - YIELD = 1, - WFE = 2, - WFI = 3, - SEV = 4, - SEVL = 5 -}; - -enum BarrierDomain { - OuterShareable = 0, - NonShareable = 1, - InnerShareable = 2, - FullSystem = 3 -}; - -enum BarrierType { - BarrierOther = 0, - BarrierReads = 1, - BarrierWrites = 2, - BarrierAll = 3 -}; - -enum PrefetchOperation { - PLDL1KEEP = 0x00, - PLDL1STRM = 0x01, - PLDL2KEEP = 0x02, - PLDL2STRM = 0x03, - PLDL3KEEP = 0x04, - PLDL3STRM = 0x05, - - PLIL1KEEP = 0x08, - PLIL1STRM = 0x09, - PLIL2KEEP = 0x0a, - PLIL2STRM = 0x0b, - PLIL3KEEP = 0x0c, - PLIL3STRM = 0x0d, - - PSTL1KEEP = 0x10, - PSTL1STRM = 0x11, - PSTL2KEEP = 0x12, - PSTL2STRM = 0x13, - PSTL3KEEP = 0x14, - PSTL3STRM = 0x15 -}; - -// System/special register names. -// This information is not encoded as one field but as the concatenation of -// multiple fields (Op0<0>, Op1, Crn, Crm, Op2). -enum SystemRegister { - NZCV = ((0x1 << SysO0_offset) | - (0x3 << SysOp1_offset) | - (0x4 << CRn_offset) | - (0x2 << CRm_offset) | - (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset, - FPCR = ((0x1 << SysO0_offset) | - (0x3 << SysOp1_offset) | - (0x4 << CRn_offset) | - (0x4 << CRm_offset) | - (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset -}; - -enum InstructionCacheOp { - IVAU = ((0x3 << SysOp1_offset) | - (0x7 << CRn_offset) | - (0x5 << CRm_offset) | - (0x1 << SysOp2_offset)) >> SysOp_offset -}; - -enum DataCacheOp { - CVAC = ((0x3 << SysOp1_offset) | - (0x7 << CRn_offset) | - (0xa << CRm_offset) | - (0x1 << SysOp2_offset)) >> SysOp_offset, - CVAU = ((0x3 << SysOp1_offset) | - (0x7 << CRn_offset) | - (0xb << CRm_offset) | - (0x1 << SysOp2_offset)) >> SysOp_offset, - CIVAC = ((0x3 << SysOp1_offset) | - (0x7 << CRn_offset) | - (0xe << CRm_offset) | - (0x1 << SysOp2_offset)) >> SysOp_offset, - ZVA = ((0x3 << SysOp1_offset) | - (0x7 << CRn_offset) | - (0x4 << CRm_offset) | - (0x1 << SysOp2_offset)) >> SysOp_offset -}; - -// Instruction enumerations. -// -// These are the masks that define a class of instructions, and the list of -// instructions within each class. Each enumeration has a Fixed, FMask and -// Mask value. -// -// Fixed: The fixed bits in this instruction class. -// FMask: The mask used to extract the fixed bits in the class. -// Mask: The mask used to identify the instructions within a class. -// -// The enumerations can be used like this: -// -// VIXL_ASSERT(instr->Mask(PCRelAddressingFMask) == PCRelAddressingFixed); -// switch(instr->Mask(PCRelAddressingMask)) { -// case ADR: Format("adr 'Xd, 'AddrPCRelByte"); break; -// case ADRP: Format("adrp 'Xd, 'AddrPCRelPage"); break; -// default: printf("Unknown instruction\n"); -// } - - -// Generic fields. -enum GenericInstrField { - SixtyFourBits = 0x80000000, - ThirtyTwoBits = 0x00000000, - FP32 = 0x00000000, - FP64 = 0x00400000 -}; - -enum NEONFormatField { - NEONFormatFieldMask = 0x40C00000, - NEON_Q = 0x40000000, - NEON_8B = 0x00000000, - NEON_16B = NEON_8B | NEON_Q, - NEON_4H = 0x00400000, - NEON_8H = NEON_4H | NEON_Q, - NEON_2S = 0x00800000, - NEON_4S = NEON_2S | NEON_Q, - NEON_1D = 0x00C00000, - NEON_2D = 0x00C00000 | NEON_Q -}; - -enum NEONFPFormatField { - NEONFPFormatFieldMask = 0x40400000, - NEON_FP_2S = FP32, - NEON_FP_4S = FP32 | NEON_Q, - NEON_FP_2D = FP64 | NEON_Q -}; - -enum NEONLSFormatField { - NEONLSFormatFieldMask = 0x40000C00, - LS_NEON_8B = 0x00000000, - LS_NEON_16B = LS_NEON_8B | NEON_Q, - LS_NEON_4H = 0x00000400, - LS_NEON_8H = LS_NEON_4H | NEON_Q, - LS_NEON_2S = 0x00000800, - LS_NEON_4S = LS_NEON_2S | NEON_Q, - LS_NEON_1D = 0x00000C00, - LS_NEON_2D = LS_NEON_1D | NEON_Q -}; - -enum NEONScalarFormatField { - NEONScalarFormatFieldMask = 0x00C00000, - NEONScalar = 0x10000000, - NEON_B = 0x00000000, - NEON_H = 0x00400000, - NEON_S = 0x00800000, - NEON_D = 0x00C00000 -}; - -// PC relative addressing. -enum PCRelAddressingOp { - PCRelAddressingFixed = 0x10000000, - PCRelAddressingFMask = 0x1F000000, - PCRelAddressingMask = 0x9F000000, - ADR = PCRelAddressingFixed | 0x00000000, - ADRP = PCRelAddressingFixed | 0x80000000 -}; - -// Add/sub (immediate, shifted and extended.) -const int kSFOffset = 31; -enum AddSubOp { - AddSubOpMask = 0x60000000, - AddSubSetFlagsBit = 0x20000000, - ADD = 0x00000000, - ADDS = ADD | AddSubSetFlagsBit, - SUB = 0x40000000, - SUBS = SUB | AddSubSetFlagsBit -}; - -#define ADD_SUB_OP_LIST(V) \ - V(ADD), \ - V(ADDS), \ - V(SUB), \ - V(SUBS) - -enum AddSubImmediateOp { - AddSubImmediateFixed = 0x11000000, - AddSubImmediateFMask = 0x1F000000, - AddSubImmediateMask = 0xFF000000, - #define ADD_SUB_IMMEDIATE(A) \ - A##_w_imm = AddSubImmediateFixed | A, \ - A##_x_imm = AddSubImmediateFixed | A | SixtyFourBits - ADD_SUB_OP_LIST(ADD_SUB_IMMEDIATE) - #undef ADD_SUB_IMMEDIATE -}; - -enum AddSubShiftedOp { - AddSubShiftedFixed = 0x0B000000, - AddSubShiftedFMask = 0x1F200000, - AddSubShiftedMask = 0xFF200000, - #define ADD_SUB_SHIFTED(A) \ - A##_w_shift = AddSubShiftedFixed | A, \ - A##_x_shift = AddSubShiftedFixed | A | SixtyFourBits - ADD_SUB_OP_LIST(ADD_SUB_SHIFTED) - #undef ADD_SUB_SHIFTED -}; - -enum AddSubExtendedOp { - AddSubExtendedFixed = 0x0B200000, - AddSubExtendedFMask = 0x1F200000, - AddSubExtendedMask = 0xFFE00000, - #define ADD_SUB_EXTENDED(A) \ - A##_w_ext = AddSubExtendedFixed | A, \ - A##_x_ext = AddSubExtendedFixed | A | SixtyFourBits - ADD_SUB_OP_LIST(ADD_SUB_EXTENDED) - #undef ADD_SUB_EXTENDED -}; - -// Add/sub with carry. -enum AddSubWithCarryOp { - AddSubWithCarryFixed = 0x1A000000, - AddSubWithCarryFMask = 0x1FE00000, - AddSubWithCarryMask = 0xFFE0FC00, - ADC_w = AddSubWithCarryFixed | ADD, - ADC_x = AddSubWithCarryFixed | ADD | SixtyFourBits, - ADC = ADC_w, - ADCS_w = AddSubWithCarryFixed | ADDS, - ADCS_x = AddSubWithCarryFixed | ADDS | SixtyFourBits, - SBC_w = AddSubWithCarryFixed | SUB, - SBC_x = AddSubWithCarryFixed | SUB | SixtyFourBits, - SBC = SBC_w, - SBCS_w = AddSubWithCarryFixed | SUBS, - SBCS_x = AddSubWithCarryFixed | SUBS | SixtyFourBits -}; - - -// Logical (immediate and shifted register). -enum LogicalOp { - LogicalOpMask = 0x60200000, - NOT = 0x00200000, - AND = 0x00000000, - BIC = AND | NOT, - ORR = 0x20000000, - ORN = ORR | NOT, - EOR = 0x40000000, - EON = EOR | NOT, - ANDS = 0x60000000, - BICS = ANDS | NOT -}; - -// Logical immediate. -enum LogicalImmediateOp { - LogicalImmediateFixed = 0x12000000, - LogicalImmediateFMask = 0x1F800000, - LogicalImmediateMask = 0xFF800000, - AND_w_imm = LogicalImmediateFixed | AND, - AND_x_imm = LogicalImmediateFixed | AND | SixtyFourBits, - ORR_w_imm = LogicalImmediateFixed | ORR, - ORR_x_imm = LogicalImmediateFixed | ORR | SixtyFourBits, - EOR_w_imm = LogicalImmediateFixed | EOR, - EOR_x_imm = LogicalImmediateFixed | EOR | SixtyFourBits, - ANDS_w_imm = LogicalImmediateFixed | ANDS, - ANDS_x_imm = LogicalImmediateFixed | ANDS | SixtyFourBits -}; - -// Logical shifted register. -enum LogicalShiftedOp { - LogicalShiftedFixed = 0x0A000000, - LogicalShiftedFMask = 0x1F000000, - LogicalShiftedMask = 0xFF200000, - AND_w = LogicalShiftedFixed | AND, - AND_x = LogicalShiftedFixed | AND | SixtyFourBits, - AND_shift = AND_w, - BIC_w = LogicalShiftedFixed | BIC, - BIC_x = LogicalShiftedFixed | BIC | SixtyFourBits, - BIC_shift = BIC_w, - ORR_w = LogicalShiftedFixed | ORR, - ORR_x = LogicalShiftedFixed | ORR | SixtyFourBits, - ORR_shift = ORR_w, - ORN_w = LogicalShiftedFixed | ORN, - ORN_x = LogicalShiftedFixed | ORN | SixtyFourBits, - ORN_shift = ORN_w, - EOR_w = LogicalShiftedFixed | EOR, - EOR_x = LogicalShiftedFixed | EOR | SixtyFourBits, - EOR_shift = EOR_w, - EON_w = LogicalShiftedFixed | EON, - EON_x = LogicalShiftedFixed | EON | SixtyFourBits, - EON_shift = EON_w, - ANDS_w = LogicalShiftedFixed | ANDS, - ANDS_x = LogicalShiftedFixed | ANDS | SixtyFourBits, - ANDS_shift = ANDS_w, - BICS_w = LogicalShiftedFixed | BICS, - BICS_x = LogicalShiftedFixed | BICS | SixtyFourBits, - BICS_shift = BICS_w -}; - -// Move wide immediate. -enum MoveWideImmediateOp { - MoveWideImmediateFixed = 0x12800000, - MoveWideImmediateFMask = 0x1F800000, - MoveWideImmediateMask = 0xFF800000, - MOVN = 0x00000000, - MOVZ = 0x40000000, - MOVK = 0x60000000, - MOVN_w = MoveWideImmediateFixed | MOVN, - MOVN_x = MoveWideImmediateFixed | MOVN | SixtyFourBits, - MOVZ_w = MoveWideImmediateFixed | MOVZ, - MOVZ_x = MoveWideImmediateFixed | MOVZ | SixtyFourBits, - MOVK_w = MoveWideImmediateFixed | MOVK, - MOVK_x = MoveWideImmediateFixed | MOVK | SixtyFourBits -}; - -// Bitfield. -const int kBitfieldNOffset = 22; -enum BitfieldOp { - BitfieldFixed = 0x13000000, - BitfieldFMask = 0x1F800000, - BitfieldMask = 0xFF800000, - SBFM_w = BitfieldFixed | 0x00000000, - SBFM_x = BitfieldFixed | 0x80000000, - SBFM = SBFM_w, - BFM_w = BitfieldFixed | 0x20000000, - BFM_x = BitfieldFixed | 0xA0000000, - BFM = BFM_w, - UBFM_w = BitfieldFixed | 0x40000000, - UBFM_x = BitfieldFixed | 0xC0000000, - UBFM = UBFM_w - // Bitfield N field. -}; - -// Extract. -enum ExtractOp { - ExtractFixed = 0x13800000, - ExtractFMask = 0x1F800000, - ExtractMask = 0xFFA00000, - EXTR_w = ExtractFixed | 0x00000000, - EXTR_x = ExtractFixed | 0x80000000, - EXTR = EXTR_w -}; - -// Unconditional branch. -enum UnconditionalBranchOp { - UnconditionalBranchFixed = 0x14000000, - UnconditionalBranchFMask = 0x7C000000, - UnconditionalBranchMask = 0xFC000000, - B = UnconditionalBranchFixed | 0x00000000, - BL = UnconditionalBranchFixed | 0x80000000 -}; - -// Unconditional branch to register. -enum UnconditionalBranchToRegisterOp { - UnconditionalBranchToRegisterFixed = 0xD6000000, - UnconditionalBranchToRegisterFMask = 0xFE000000, - UnconditionalBranchToRegisterMask = 0xFFFFFC1F, - BR = UnconditionalBranchToRegisterFixed | 0x001F0000, - BLR = UnconditionalBranchToRegisterFixed | 0x003F0000, - RET = UnconditionalBranchToRegisterFixed | 0x005F0000 -}; - -// Compare and branch. -enum CompareBranchOp { - CompareBranchFixed = 0x34000000, - CompareBranchFMask = 0x7E000000, - CompareBranchMask = 0xFF000000, - CBZ_w = CompareBranchFixed | 0x00000000, - CBZ_x = CompareBranchFixed | 0x80000000, - CBZ = CBZ_w, - CBNZ_w = CompareBranchFixed | 0x01000000, - CBNZ_x = CompareBranchFixed | 0x81000000, - CBNZ = CBNZ_w -}; - -// Test and branch. -enum TestBranchOp { - TestBranchFixed = 0x36000000, - TestBranchFMask = 0x7E000000, - TestBranchMask = 0x7F000000, - TBZ = TestBranchFixed | 0x00000000, - TBNZ = TestBranchFixed | 0x01000000 -}; - -// Conditional branch. -enum ConditionalBranchOp { - ConditionalBranchFixed = 0x54000000, - ConditionalBranchFMask = 0xFE000000, - ConditionalBranchMask = 0xFF000010, - B_cond = ConditionalBranchFixed | 0x00000000 -}; - -// System. -// System instruction encoding is complicated because some instructions use op -// and CR fields to encode parameters. To handle this cleanly, the system -// instructions are split into more than one enum. - -enum SystemOp { - SystemFixed = 0xD5000000, - SystemFMask = 0xFFC00000 -}; - -enum SystemSysRegOp { - SystemSysRegFixed = 0xD5100000, - SystemSysRegFMask = 0xFFD00000, - SystemSysRegMask = 0xFFF00000, - MRS = SystemSysRegFixed | 0x00200000, - MSR = SystemSysRegFixed | 0x00000000 -}; - -enum SystemHintOp { - SystemHintFixed = 0xD503201F, - SystemHintFMask = 0xFFFFF01F, - SystemHintMask = 0xFFFFF01F, - HINT = SystemHintFixed | 0x00000000 -}; - -enum SystemSysOp { - SystemSysFixed = 0xD5080000, - SystemSysFMask = 0xFFF80000, - SystemSysMask = 0xFFF80000, - SYS = SystemSysFixed | 0x00000000 -}; - -// Exception. -enum ExceptionOp { - ExceptionFixed = 0xD4000000, - ExceptionFMask = 0xFF000000, - ExceptionMask = 0xFFE0001F, - HLT = ExceptionFixed | 0x00400000, - BRK = ExceptionFixed | 0x00200000, - SVC = ExceptionFixed | 0x00000001, - HVC = ExceptionFixed | 0x00000002, - SMC = ExceptionFixed | 0x00000003, - DCPS1 = ExceptionFixed | 0x00A00001, - DCPS2 = ExceptionFixed | 0x00A00002, - DCPS3 = ExceptionFixed | 0x00A00003 -}; - -enum MemBarrierOp { - MemBarrierFixed = 0xD503309F, - MemBarrierFMask = 0xFFFFF09F, - MemBarrierMask = 0xFFFFF0FF, - DSB = MemBarrierFixed | 0x00000000, - DMB = MemBarrierFixed | 0x00000020, - ISB = MemBarrierFixed | 0x00000040 -}; - -enum SystemExclusiveMonitorOp { - SystemExclusiveMonitorFixed = 0xD503305F, - SystemExclusiveMonitorFMask = 0xFFFFF0FF, - SystemExclusiveMonitorMask = 0xFFFFF0FF, - CLREX = SystemExclusiveMonitorFixed -}; - -// Any load or store. -enum LoadStoreAnyOp { - LoadStoreAnyFMask = 0x0a000000, - LoadStoreAnyFixed = 0x08000000 -}; - -// Any load pair or store pair. -enum LoadStorePairAnyOp { - LoadStorePairAnyFMask = 0x3a000000, - LoadStorePairAnyFixed = 0x28000000 -}; - -#define LOAD_STORE_PAIR_OP_LIST(V) \ - V(STP, w, 0x00000000), \ - V(LDP, w, 0x00400000), \ - V(LDPSW, x, 0x40400000), \ - V(STP, x, 0x80000000), \ - V(LDP, x, 0x80400000), \ - V(STP, s, 0x04000000), \ - V(LDP, s, 0x04400000), \ - V(STP, d, 0x44000000), \ - V(LDP, d, 0x44400000), \ - V(STP, q, 0x84000000), \ - V(LDP, q, 0x84400000) - -// Load/store pair (post, pre and offset.) -enum LoadStorePairOp { - LoadStorePairMask = 0xC4400000, - LoadStorePairLBit = 1 << 22, - #define LOAD_STORE_PAIR(A, B, C) \ - A##_##B = C - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR) - #undef LOAD_STORE_PAIR -}; - -enum LoadStorePairPostIndexOp { - LoadStorePairPostIndexFixed = 0x28800000, - LoadStorePairPostIndexFMask = 0x3B800000, - LoadStorePairPostIndexMask = 0xFFC00000, - #define LOAD_STORE_PAIR_POST_INDEX(A, B, C) \ - A##_##B##_post = LoadStorePairPostIndexFixed | A##_##B - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_POST_INDEX) - #undef LOAD_STORE_PAIR_POST_INDEX -}; - -enum LoadStorePairPreIndexOp { - LoadStorePairPreIndexFixed = 0x29800000, - LoadStorePairPreIndexFMask = 0x3B800000, - LoadStorePairPreIndexMask = 0xFFC00000, - #define LOAD_STORE_PAIR_PRE_INDEX(A, B, C) \ - A##_##B##_pre = LoadStorePairPreIndexFixed | A##_##B - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_PRE_INDEX) - #undef LOAD_STORE_PAIR_PRE_INDEX -}; - -enum LoadStorePairOffsetOp { - LoadStorePairOffsetFixed = 0x29000000, - LoadStorePairOffsetFMask = 0x3B800000, - LoadStorePairOffsetMask = 0xFFC00000, - #define LOAD_STORE_PAIR_OFFSET(A, B, C) \ - A##_##B##_off = LoadStorePairOffsetFixed | A##_##B - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_OFFSET) - #undef LOAD_STORE_PAIR_OFFSET -}; - -enum LoadStorePairNonTemporalOp { - LoadStorePairNonTemporalFixed = 0x28000000, - LoadStorePairNonTemporalFMask = 0x3B800000, - LoadStorePairNonTemporalMask = 0xFFC00000, - LoadStorePairNonTemporalLBit = 1 << 22, - STNP_w = LoadStorePairNonTemporalFixed | STP_w, - LDNP_w = LoadStorePairNonTemporalFixed | LDP_w, - STNP_x = LoadStorePairNonTemporalFixed | STP_x, - LDNP_x = LoadStorePairNonTemporalFixed | LDP_x, - STNP_s = LoadStorePairNonTemporalFixed | STP_s, - LDNP_s = LoadStorePairNonTemporalFixed | LDP_s, - STNP_d = LoadStorePairNonTemporalFixed | STP_d, - LDNP_d = LoadStorePairNonTemporalFixed | LDP_d, - STNP_q = LoadStorePairNonTemporalFixed | STP_q, - LDNP_q = LoadStorePairNonTemporalFixed | LDP_q -}; - -// Load literal. -enum LoadLiteralOp { - LoadLiteralFixed = 0x18000000, - LoadLiteralFMask = 0x3B000000, - LoadLiteralMask = 0xFF000000, - LDR_w_lit = LoadLiteralFixed | 0x00000000, - LDR_x_lit = LoadLiteralFixed | 0x40000000, - LDRSW_x_lit = LoadLiteralFixed | 0x80000000, - PRFM_lit = LoadLiteralFixed | 0xC0000000, - LDR_s_lit = LoadLiteralFixed | 0x04000000, - LDR_d_lit = LoadLiteralFixed | 0x44000000, - LDR_q_lit = LoadLiteralFixed | 0x84000000 -}; - -#define LOAD_STORE_OP_LIST(V) \ - V(ST, RB, w, 0x00000000), \ - V(ST, RH, w, 0x40000000), \ - V(ST, R, w, 0x80000000), \ - V(ST, R, x, 0xC0000000), \ - V(LD, RB, w, 0x00400000), \ - V(LD, RH, w, 0x40400000), \ - V(LD, R, w, 0x80400000), \ - V(LD, R, x, 0xC0400000), \ - V(LD, RSB, x, 0x00800000), \ - V(LD, RSH, x, 0x40800000), \ - V(LD, RSW, x, 0x80800000), \ - V(LD, RSB, w, 0x00C00000), \ - V(LD, RSH, w, 0x40C00000), \ - V(ST, R, b, 0x04000000), \ - V(ST, R, h, 0x44000000), \ - V(ST, R, s, 0x84000000), \ - V(ST, R, d, 0xC4000000), \ - V(ST, R, q, 0x04800000), \ - V(LD, R, b, 0x04400000), \ - V(LD, R, h, 0x44400000), \ - V(LD, R, s, 0x84400000), \ - V(LD, R, d, 0xC4400000), \ - V(LD, R, q, 0x04C00000) - -// Load/store (post, pre, offset and unsigned.) -enum LoadStoreOp { - LoadStoreMask = 0xC4C00000, - LoadStoreVMask = 0x04000000, - #define LOAD_STORE(A, B, C, D) \ - A##B##_##C = D - LOAD_STORE_OP_LIST(LOAD_STORE), - #undef LOAD_STORE - PRFM = 0xC0800000 -}; - -// Load/store unscaled offset. -enum LoadStoreUnscaledOffsetOp { - LoadStoreUnscaledOffsetFixed = 0x38000000, - LoadStoreUnscaledOffsetFMask = 0x3B200C00, - LoadStoreUnscaledOffsetMask = 0xFFE00C00, - PRFUM = LoadStoreUnscaledOffsetFixed | PRFM, - #define LOAD_STORE_UNSCALED(A, B, C, D) \ - A##U##B##_##C = LoadStoreUnscaledOffsetFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_UNSCALED) - #undef LOAD_STORE_UNSCALED -}; - -// Load/store post index. -enum LoadStorePostIndex { - LoadStorePostIndexFixed = 0x38000400, - LoadStorePostIndexFMask = 0x3B200C00, - LoadStorePostIndexMask = 0xFFE00C00, - #define LOAD_STORE_POST_INDEX(A, B, C, D) \ - A##B##_##C##_post = LoadStorePostIndexFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_POST_INDEX) - #undef LOAD_STORE_POST_INDEX -}; - -// Load/store pre index. -enum LoadStorePreIndex { - LoadStorePreIndexFixed = 0x38000C00, - LoadStorePreIndexFMask = 0x3B200C00, - LoadStorePreIndexMask = 0xFFE00C00, - #define LOAD_STORE_PRE_INDEX(A, B, C, D) \ - A##B##_##C##_pre = LoadStorePreIndexFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_PRE_INDEX) - #undef LOAD_STORE_PRE_INDEX -}; - -// Load/store unsigned offset. -enum LoadStoreUnsignedOffset { - LoadStoreUnsignedOffsetFixed = 0x39000000, - LoadStoreUnsignedOffsetFMask = 0x3B000000, - LoadStoreUnsignedOffsetMask = 0xFFC00000, - PRFM_unsigned = LoadStoreUnsignedOffsetFixed | PRFM, - #define LOAD_STORE_UNSIGNED_OFFSET(A, B, C, D) \ - A##B##_##C##_unsigned = LoadStoreUnsignedOffsetFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_UNSIGNED_OFFSET) - #undef LOAD_STORE_UNSIGNED_OFFSET -}; - -// Load/store register offset. -enum LoadStoreRegisterOffset { - LoadStoreRegisterOffsetFixed = 0x38200800, - LoadStoreRegisterOffsetFMask = 0x3B200C00, - LoadStoreRegisterOffsetMask = 0xFFE00C00, - PRFM_reg = LoadStoreRegisterOffsetFixed | PRFM, - #define LOAD_STORE_REGISTER_OFFSET(A, B, C, D) \ - A##B##_##C##_reg = LoadStoreRegisterOffsetFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_REGISTER_OFFSET) - #undef LOAD_STORE_REGISTER_OFFSET -}; - -enum LoadStoreExclusive { - LoadStoreExclusiveFixed = 0x08000000, - LoadStoreExclusiveFMask = 0x3F000000, - LoadStoreExclusiveMask = 0xFFE08000, - STXRB_w = LoadStoreExclusiveFixed | 0x00000000, - STXRH_w = LoadStoreExclusiveFixed | 0x40000000, - STXR_w = LoadStoreExclusiveFixed | 0x80000000, - STXR_x = LoadStoreExclusiveFixed | 0xC0000000, - LDXRB_w = LoadStoreExclusiveFixed | 0x00400000, - LDXRH_w = LoadStoreExclusiveFixed | 0x40400000, - LDXR_w = LoadStoreExclusiveFixed | 0x80400000, - LDXR_x = LoadStoreExclusiveFixed | 0xC0400000, - STXP_w = LoadStoreExclusiveFixed | 0x80200000, - STXP_x = LoadStoreExclusiveFixed | 0xC0200000, - LDXP_w = LoadStoreExclusiveFixed | 0x80600000, - LDXP_x = LoadStoreExclusiveFixed | 0xC0600000, - STLXRB_w = LoadStoreExclusiveFixed | 0x00008000, - STLXRH_w = LoadStoreExclusiveFixed | 0x40008000, - STLXR_w = LoadStoreExclusiveFixed | 0x80008000, - STLXR_x = LoadStoreExclusiveFixed | 0xC0008000, - LDAXRB_w = LoadStoreExclusiveFixed | 0x00408000, - LDAXRH_w = LoadStoreExclusiveFixed | 0x40408000, - LDAXR_w = LoadStoreExclusiveFixed | 0x80408000, - LDAXR_x = LoadStoreExclusiveFixed | 0xC0408000, - STLXP_w = LoadStoreExclusiveFixed | 0x80208000, - STLXP_x = LoadStoreExclusiveFixed | 0xC0208000, - LDAXP_w = LoadStoreExclusiveFixed | 0x80608000, - LDAXP_x = LoadStoreExclusiveFixed | 0xC0608000, - STLRB_w = LoadStoreExclusiveFixed | 0x00808000, - STLRH_w = LoadStoreExclusiveFixed | 0x40808000, - STLR_w = LoadStoreExclusiveFixed | 0x80808000, - STLR_x = LoadStoreExclusiveFixed | 0xC0808000, - LDARB_w = LoadStoreExclusiveFixed | 0x00C08000, - LDARH_w = LoadStoreExclusiveFixed | 0x40C08000, - LDAR_w = LoadStoreExclusiveFixed | 0x80C08000, - LDAR_x = LoadStoreExclusiveFixed | 0xC0C08000 -}; - -// Conditional compare. -enum ConditionalCompareOp { - ConditionalCompareMask = 0x60000000, - CCMN = 0x20000000, - CCMP = 0x60000000 -}; - -// Conditional compare register. -enum ConditionalCompareRegisterOp { - ConditionalCompareRegisterFixed = 0x1A400000, - ConditionalCompareRegisterFMask = 0x1FE00800, - ConditionalCompareRegisterMask = 0xFFE00C10, - CCMN_w = ConditionalCompareRegisterFixed | CCMN, - CCMN_x = ConditionalCompareRegisterFixed | SixtyFourBits | CCMN, - CCMP_w = ConditionalCompareRegisterFixed | CCMP, - CCMP_x = ConditionalCompareRegisterFixed | SixtyFourBits | CCMP -}; - -// Conditional compare immediate. -enum ConditionalCompareImmediateOp { - ConditionalCompareImmediateFixed = 0x1A400800, - ConditionalCompareImmediateFMask = 0x1FE00800, - ConditionalCompareImmediateMask = 0xFFE00C10, - CCMN_w_imm = ConditionalCompareImmediateFixed | CCMN, - CCMN_x_imm = ConditionalCompareImmediateFixed | SixtyFourBits | CCMN, - CCMP_w_imm = ConditionalCompareImmediateFixed | CCMP, - CCMP_x_imm = ConditionalCompareImmediateFixed | SixtyFourBits | CCMP -}; - -// Conditional select. -enum ConditionalSelectOp { - ConditionalSelectFixed = 0x1A800000, - ConditionalSelectFMask = 0x1FE00000, - ConditionalSelectMask = 0xFFE00C00, - CSEL_w = ConditionalSelectFixed | 0x00000000, - CSEL_x = ConditionalSelectFixed | 0x80000000, - CSEL = CSEL_w, - CSINC_w = ConditionalSelectFixed | 0x00000400, - CSINC_x = ConditionalSelectFixed | 0x80000400, - CSINC = CSINC_w, - CSINV_w = ConditionalSelectFixed | 0x40000000, - CSINV_x = ConditionalSelectFixed | 0xC0000000, - CSINV = CSINV_w, - CSNEG_w = ConditionalSelectFixed | 0x40000400, - CSNEG_x = ConditionalSelectFixed | 0xC0000400, - CSNEG = CSNEG_w -}; - -// Data processing 1 source. -enum DataProcessing1SourceOp { - DataProcessing1SourceFixed = 0x5AC00000, - DataProcessing1SourceFMask = 0x5FE00000, - DataProcessing1SourceMask = 0xFFFFFC00, - RBIT = DataProcessing1SourceFixed | 0x00000000, - RBIT_w = RBIT, - RBIT_x = RBIT | SixtyFourBits, - REV16 = DataProcessing1SourceFixed | 0x00000400, - REV16_w = REV16, - REV16_x = REV16 | SixtyFourBits, - REV = DataProcessing1SourceFixed | 0x00000800, - REV_w = REV, - REV32_x = REV | SixtyFourBits, - REV_x = DataProcessing1SourceFixed | SixtyFourBits | 0x00000C00, - CLZ = DataProcessing1SourceFixed | 0x00001000, - CLZ_w = CLZ, - CLZ_x = CLZ | SixtyFourBits, - CLS = DataProcessing1SourceFixed | 0x00001400, - CLS_w = CLS, - CLS_x = CLS | SixtyFourBits -}; - -// Data processing 2 source. -enum DataProcessing2SourceOp { - DataProcessing2SourceFixed = 0x1AC00000, - DataProcessing2SourceFMask = 0x5FE00000, - DataProcessing2SourceMask = 0xFFE0FC00, - UDIV_w = DataProcessing2SourceFixed | 0x00000800, - UDIV_x = DataProcessing2SourceFixed | 0x80000800, - UDIV = UDIV_w, - SDIV_w = DataProcessing2SourceFixed | 0x00000C00, - SDIV_x = DataProcessing2SourceFixed | 0x80000C00, - SDIV = SDIV_w, - LSLV_w = DataProcessing2SourceFixed | 0x00002000, - LSLV_x = DataProcessing2SourceFixed | 0x80002000, - LSLV = LSLV_w, - LSRV_w = DataProcessing2SourceFixed | 0x00002400, - LSRV_x = DataProcessing2SourceFixed | 0x80002400, - LSRV = LSRV_w, - ASRV_w = DataProcessing2SourceFixed | 0x00002800, - ASRV_x = DataProcessing2SourceFixed | 0x80002800, - ASRV = ASRV_w, - RORV_w = DataProcessing2SourceFixed | 0x00002C00, - RORV_x = DataProcessing2SourceFixed | 0x80002C00, - RORV = RORV_w, - CRC32B = DataProcessing2SourceFixed | 0x00004000, - CRC32H = DataProcessing2SourceFixed | 0x00004400, - CRC32W = DataProcessing2SourceFixed | 0x00004800, - CRC32X = DataProcessing2SourceFixed | SixtyFourBits | 0x00004C00, - CRC32CB = DataProcessing2SourceFixed | 0x00005000, - CRC32CH = DataProcessing2SourceFixed | 0x00005400, - CRC32CW = DataProcessing2SourceFixed | 0x00005800, - CRC32CX = DataProcessing2SourceFixed | SixtyFourBits | 0x00005C00 -}; - -// Data processing 3 source. -enum DataProcessing3SourceOp { - DataProcessing3SourceFixed = 0x1B000000, - DataProcessing3SourceFMask = 0x1F000000, - DataProcessing3SourceMask = 0xFFE08000, - MADD_w = DataProcessing3SourceFixed | 0x00000000, - MADD_x = DataProcessing3SourceFixed | 0x80000000, - MADD = MADD_w, - MSUB_w = DataProcessing3SourceFixed | 0x00008000, - MSUB_x = DataProcessing3SourceFixed | 0x80008000, - MSUB = MSUB_w, - SMADDL_x = DataProcessing3SourceFixed | 0x80200000, - SMSUBL_x = DataProcessing3SourceFixed | 0x80208000, - SMULH_x = DataProcessing3SourceFixed | 0x80400000, - UMADDL_x = DataProcessing3SourceFixed | 0x80A00000, - UMSUBL_x = DataProcessing3SourceFixed | 0x80A08000, - UMULH_x = DataProcessing3SourceFixed | 0x80C00000 -}; - -// Floating point compare. -enum FPCompareOp { - FPCompareFixed = 0x1E202000, - FPCompareFMask = 0x5F203C00, - FPCompareMask = 0xFFE0FC1F, - FCMP_s = FPCompareFixed | 0x00000000, - FCMP_d = FPCompareFixed | FP64 | 0x00000000, - FCMP = FCMP_s, - FCMP_s_zero = FPCompareFixed | 0x00000008, - FCMP_d_zero = FPCompareFixed | FP64 | 0x00000008, - FCMP_zero = FCMP_s_zero, - FCMPE_s = FPCompareFixed | 0x00000010, - FCMPE_d = FPCompareFixed | FP64 | 0x00000010, - FCMPE = FCMPE_s, - FCMPE_s_zero = FPCompareFixed | 0x00000018, - FCMPE_d_zero = FPCompareFixed | FP64 | 0x00000018, - FCMPE_zero = FCMPE_s_zero -}; - -// Floating point conditional compare. -enum FPConditionalCompareOp { - FPConditionalCompareFixed = 0x1E200400, - FPConditionalCompareFMask = 0x5F200C00, - FPConditionalCompareMask = 0xFFE00C10, - FCCMP_s = FPConditionalCompareFixed | 0x00000000, - FCCMP_d = FPConditionalCompareFixed | FP64 | 0x00000000, - FCCMP = FCCMP_s, - FCCMPE_s = FPConditionalCompareFixed | 0x00000010, - FCCMPE_d = FPConditionalCompareFixed | FP64 | 0x00000010, - FCCMPE = FCCMPE_s -}; - -// Floating point conditional select. -enum FPConditionalSelectOp { - FPConditionalSelectFixed = 0x1E200C00, - FPConditionalSelectFMask = 0x5F200C00, - FPConditionalSelectMask = 0xFFE00C00, - FCSEL_s = FPConditionalSelectFixed | 0x00000000, - FCSEL_d = FPConditionalSelectFixed | FP64 | 0x00000000, - FCSEL = FCSEL_s -}; - -// Floating point immediate. -enum FPImmediateOp { - FPImmediateFixed = 0x1E201000, - FPImmediateFMask = 0x5F201C00, - FPImmediateMask = 0xFFE01C00, - FMOV_s_imm = FPImmediateFixed | 0x00000000, - FMOV_d_imm = FPImmediateFixed | FP64 | 0x00000000 -}; - -// Floating point data processing 1 source. -enum FPDataProcessing1SourceOp { - FPDataProcessing1SourceFixed = 0x1E204000, - FPDataProcessing1SourceFMask = 0x5F207C00, - FPDataProcessing1SourceMask = 0xFFFFFC00, - FMOV_s = FPDataProcessing1SourceFixed | 0x00000000, - FMOV_d = FPDataProcessing1SourceFixed | FP64 | 0x00000000, - FMOV = FMOV_s, - FABS_s = FPDataProcessing1SourceFixed | 0x00008000, - FABS_d = FPDataProcessing1SourceFixed | FP64 | 0x00008000, - FABS = FABS_s, - FNEG_s = FPDataProcessing1SourceFixed | 0x00010000, - FNEG_d = FPDataProcessing1SourceFixed | FP64 | 0x00010000, - FNEG = FNEG_s, - FSQRT_s = FPDataProcessing1SourceFixed | 0x00018000, - FSQRT_d = FPDataProcessing1SourceFixed | FP64 | 0x00018000, - FSQRT = FSQRT_s, - FCVT_ds = FPDataProcessing1SourceFixed | 0x00028000, - FCVT_sd = FPDataProcessing1SourceFixed | FP64 | 0x00020000, - FCVT_hs = FPDataProcessing1SourceFixed | 0x00038000, - FCVT_hd = FPDataProcessing1SourceFixed | FP64 | 0x00038000, - FCVT_sh = FPDataProcessing1SourceFixed | 0x00C20000, - FCVT_dh = FPDataProcessing1SourceFixed | 0x00C28000, - FRINTN_s = FPDataProcessing1SourceFixed | 0x00040000, - FRINTN_d = FPDataProcessing1SourceFixed | FP64 | 0x00040000, - FRINTN = FRINTN_s, - FRINTP_s = FPDataProcessing1SourceFixed | 0x00048000, - FRINTP_d = FPDataProcessing1SourceFixed | FP64 | 0x00048000, - FRINTP = FRINTP_s, - FRINTM_s = FPDataProcessing1SourceFixed | 0x00050000, - FRINTM_d = FPDataProcessing1SourceFixed | FP64 | 0x00050000, - FRINTM = FRINTM_s, - FRINTZ_s = FPDataProcessing1SourceFixed | 0x00058000, - FRINTZ_d = FPDataProcessing1SourceFixed | FP64 | 0x00058000, - FRINTZ = FRINTZ_s, - FRINTA_s = FPDataProcessing1SourceFixed | 0x00060000, - FRINTA_d = FPDataProcessing1SourceFixed | FP64 | 0x00060000, - FRINTA = FRINTA_s, - FRINTX_s = FPDataProcessing1SourceFixed | 0x00070000, - FRINTX_d = FPDataProcessing1SourceFixed | FP64 | 0x00070000, - FRINTX = FRINTX_s, - FRINTI_s = FPDataProcessing1SourceFixed | 0x00078000, - FRINTI_d = FPDataProcessing1SourceFixed | FP64 | 0x00078000, - FRINTI = FRINTI_s -}; - -// Floating point data processing 2 source. -enum FPDataProcessing2SourceOp { - FPDataProcessing2SourceFixed = 0x1E200800, - FPDataProcessing2SourceFMask = 0x5F200C00, - FPDataProcessing2SourceMask = 0xFFE0FC00, - FMUL = FPDataProcessing2SourceFixed | 0x00000000, - FMUL_s = FMUL, - FMUL_d = FMUL | FP64, - FDIV = FPDataProcessing2SourceFixed | 0x00001000, - FDIV_s = FDIV, - FDIV_d = FDIV | FP64, - FADD = FPDataProcessing2SourceFixed | 0x00002000, - FADD_s = FADD, - FADD_d = FADD | FP64, - FSUB = FPDataProcessing2SourceFixed | 0x00003000, - FSUB_s = FSUB, - FSUB_d = FSUB | FP64, - FMAX = FPDataProcessing2SourceFixed | 0x00004000, - FMAX_s = FMAX, - FMAX_d = FMAX | FP64, - FMIN = FPDataProcessing2SourceFixed | 0x00005000, - FMIN_s = FMIN, - FMIN_d = FMIN | FP64, - FMAXNM = FPDataProcessing2SourceFixed | 0x00006000, - FMAXNM_s = FMAXNM, - FMAXNM_d = FMAXNM | FP64, - FMINNM = FPDataProcessing2SourceFixed | 0x00007000, - FMINNM_s = FMINNM, - FMINNM_d = FMINNM | FP64, - FNMUL = FPDataProcessing2SourceFixed | 0x00008000, - FNMUL_s = FNMUL, - FNMUL_d = FNMUL | FP64 -}; - -// Floating point data processing 3 source. -enum FPDataProcessing3SourceOp { - FPDataProcessing3SourceFixed = 0x1F000000, - FPDataProcessing3SourceFMask = 0x5F000000, - FPDataProcessing3SourceMask = 0xFFE08000, - FMADD_s = FPDataProcessing3SourceFixed | 0x00000000, - FMSUB_s = FPDataProcessing3SourceFixed | 0x00008000, - FNMADD_s = FPDataProcessing3SourceFixed | 0x00200000, - FNMSUB_s = FPDataProcessing3SourceFixed | 0x00208000, - FMADD_d = FPDataProcessing3SourceFixed | 0x00400000, - FMSUB_d = FPDataProcessing3SourceFixed | 0x00408000, - FNMADD_d = FPDataProcessing3SourceFixed | 0x00600000, - FNMSUB_d = FPDataProcessing3SourceFixed | 0x00608000 -}; - -// Conversion between floating point and integer. -enum FPIntegerConvertOp { - FPIntegerConvertFixed = 0x1E200000, - FPIntegerConvertFMask = 0x5F20FC00, - FPIntegerConvertMask = 0xFFFFFC00, - FCVTNS = FPIntegerConvertFixed | 0x00000000, - FCVTNS_ws = FCVTNS, - FCVTNS_xs = FCVTNS | SixtyFourBits, - FCVTNS_wd = FCVTNS | FP64, - FCVTNS_xd = FCVTNS | SixtyFourBits | FP64, - FCVTNU = FPIntegerConvertFixed | 0x00010000, - FCVTNU_ws = FCVTNU, - FCVTNU_xs = FCVTNU | SixtyFourBits, - FCVTNU_wd = FCVTNU | FP64, - FCVTNU_xd = FCVTNU | SixtyFourBits | FP64, - FCVTPS = FPIntegerConvertFixed | 0x00080000, - FCVTPS_ws = FCVTPS, - FCVTPS_xs = FCVTPS | SixtyFourBits, - FCVTPS_wd = FCVTPS | FP64, - FCVTPS_xd = FCVTPS | SixtyFourBits | FP64, - FCVTPU = FPIntegerConvertFixed | 0x00090000, - FCVTPU_ws = FCVTPU, - FCVTPU_xs = FCVTPU | SixtyFourBits, - FCVTPU_wd = FCVTPU | FP64, - FCVTPU_xd = FCVTPU | SixtyFourBits | FP64, - FCVTMS = FPIntegerConvertFixed | 0x00100000, - FCVTMS_ws = FCVTMS, - FCVTMS_xs = FCVTMS | SixtyFourBits, - FCVTMS_wd = FCVTMS | FP64, - FCVTMS_xd = FCVTMS | SixtyFourBits | FP64, - FCVTMU = FPIntegerConvertFixed | 0x00110000, - FCVTMU_ws = FCVTMU, - FCVTMU_xs = FCVTMU | SixtyFourBits, - FCVTMU_wd = FCVTMU | FP64, - FCVTMU_xd = FCVTMU | SixtyFourBits | FP64, - FCVTZS = FPIntegerConvertFixed | 0x00180000, - FCVTZS_ws = FCVTZS, - FCVTZS_xs = FCVTZS | SixtyFourBits, - FCVTZS_wd = FCVTZS | FP64, - FCVTZS_xd = FCVTZS | SixtyFourBits | FP64, - FCVTZU = FPIntegerConvertFixed | 0x00190000, - FCVTZU_ws = FCVTZU, - FCVTZU_xs = FCVTZU | SixtyFourBits, - FCVTZU_wd = FCVTZU | FP64, - FCVTZU_xd = FCVTZU | SixtyFourBits | FP64, - SCVTF = FPIntegerConvertFixed | 0x00020000, - SCVTF_sw = SCVTF, - SCVTF_sx = SCVTF | SixtyFourBits, - SCVTF_dw = SCVTF | FP64, - SCVTF_dx = SCVTF | SixtyFourBits | FP64, - UCVTF = FPIntegerConvertFixed | 0x00030000, - UCVTF_sw = UCVTF, - UCVTF_sx = UCVTF | SixtyFourBits, - UCVTF_dw = UCVTF | FP64, - UCVTF_dx = UCVTF | SixtyFourBits | FP64, - FCVTAS = FPIntegerConvertFixed | 0x00040000, - FCVTAS_ws = FCVTAS, - FCVTAS_xs = FCVTAS | SixtyFourBits, - FCVTAS_wd = FCVTAS | FP64, - FCVTAS_xd = FCVTAS | SixtyFourBits | FP64, - FCVTAU = FPIntegerConvertFixed | 0x00050000, - FCVTAU_ws = FCVTAU, - FCVTAU_xs = FCVTAU | SixtyFourBits, - FCVTAU_wd = FCVTAU | FP64, - FCVTAU_xd = FCVTAU | SixtyFourBits | FP64, - FMOV_ws = FPIntegerConvertFixed | 0x00060000, - FMOV_sw = FPIntegerConvertFixed | 0x00070000, - FMOV_xd = FMOV_ws | SixtyFourBits | FP64, - FMOV_dx = FMOV_sw | SixtyFourBits | FP64, - FMOV_d1_x = FPIntegerConvertFixed | SixtyFourBits | 0x008F0000, - FMOV_x_d1 = FPIntegerConvertFixed | SixtyFourBits | 0x008E0000 -}; - -// Conversion between fixed point and floating point. -enum FPFixedPointConvertOp { - FPFixedPointConvertFixed = 0x1E000000, - FPFixedPointConvertFMask = 0x5F200000, - FPFixedPointConvertMask = 0xFFFF0000, - FCVTZS_fixed = FPFixedPointConvertFixed | 0x00180000, - FCVTZS_ws_fixed = FCVTZS_fixed, - FCVTZS_xs_fixed = FCVTZS_fixed | SixtyFourBits, - FCVTZS_wd_fixed = FCVTZS_fixed | FP64, - FCVTZS_xd_fixed = FCVTZS_fixed | SixtyFourBits | FP64, - FCVTZU_fixed = FPFixedPointConvertFixed | 0x00190000, - FCVTZU_ws_fixed = FCVTZU_fixed, - FCVTZU_xs_fixed = FCVTZU_fixed | SixtyFourBits, - FCVTZU_wd_fixed = FCVTZU_fixed | FP64, - FCVTZU_xd_fixed = FCVTZU_fixed | SixtyFourBits | FP64, - SCVTF_fixed = FPFixedPointConvertFixed | 0x00020000, - SCVTF_sw_fixed = SCVTF_fixed, - SCVTF_sx_fixed = SCVTF_fixed | SixtyFourBits, - SCVTF_dw_fixed = SCVTF_fixed | FP64, - SCVTF_dx_fixed = SCVTF_fixed | SixtyFourBits | FP64, - UCVTF_fixed = FPFixedPointConvertFixed | 0x00030000, - UCVTF_sw_fixed = UCVTF_fixed, - UCVTF_sx_fixed = UCVTF_fixed | SixtyFourBits, - UCVTF_dw_fixed = UCVTF_fixed | FP64, - UCVTF_dx_fixed = UCVTF_fixed | SixtyFourBits | FP64 -}; - -// Crypto - two register SHA. -enum Crypto2RegSHAOp { - Crypto2RegSHAFixed = 0x5E280800, - Crypto2RegSHAFMask = 0xFF3E0C00 -}; - -// Crypto - three register SHA. -enum Crypto3RegSHAOp { - Crypto3RegSHAFixed = 0x5E000000, - Crypto3RegSHAFMask = 0xFF208C00 -}; - -// Crypto - AES. -enum CryptoAESOp { - CryptoAESFixed = 0x4E280800, - CryptoAESFMask = 0xFF3E0C00 -}; - -// NEON instructions with two register operands. -enum NEON2RegMiscOp { - NEON2RegMiscFixed = 0x0E200800, - NEON2RegMiscFMask = 0x9F3E0C00, - NEON2RegMiscMask = 0xBF3FFC00, - NEON2RegMiscUBit = 0x20000000, - NEON_REV64 = NEON2RegMiscFixed | 0x00000000, - NEON_REV32 = NEON2RegMiscFixed | 0x20000000, - NEON_REV16 = NEON2RegMiscFixed | 0x00001000, - NEON_SADDLP = NEON2RegMiscFixed | 0x00002000, - NEON_UADDLP = NEON_SADDLP | NEON2RegMiscUBit, - NEON_SUQADD = NEON2RegMiscFixed | 0x00003000, - NEON_USQADD = NEON_SUQADD | NEON2RegMiscUBit, - NEON_CLS = NEON2RegMiscFixed | 0x00004000, - NEON_CLZ = NEON2RegMiscFixed | 0x20004000, - NEON_CNT = NEON2RegMiscFixed | 0x00005000, - NEON_RBIT_NOT = NEON2RegMiscFixed | 0x20005000, - NEON_SADALP = NEON2RegMiscFixed | 0x00006000, - NEON_UADALP = NEON_SADALP | NEON2RegMiscUBit, - NEON_SQABS = NEON2RegMiscFixed | 0x00007000, - NEON_SQNEG = NEON2RegMiscFixed | 0x20007000, - NEON_CMGT_zero = NEON2RegMiscFixed | 0x00008000, - NEON_CMGE_zero = NEON2RegMiscFixed | 0x20008000, - NEON_CMEQ_zero = NEON2RegMiscFixed | 0x00009000, - NEON_CMLE_zero = NEON2RegMiscFixed | 0x20009000, - NEON_CMLT_zero = NEON2RegMiscFixed | 0x0000A000, - NEON_ABS = NEON2RegMiscFixed | 0x0000B000, - NEON_NEG = NEON2RegMiscFixed | 0x2000B000, - NEON_XTN = NEON2RegMiscFixed | 0x00012000, - NEON_SQXTUN = NEON2RegMiscFixed | 0x20012000, - NEON_SHLL = NEON2RegMiscFixed | 0x20013000, - NEON_SQXTN = NEON2RegMiscFixed | 0x00014000, - NEON_UQXTN = NEON_SQXTN | NEON2RegMiscUBit, - - NEON2RegMiscOpcode = 0x0001F000, - NEON_RBIT_NOT_opcode = NEON_RBIT_NOT & NEON2RegMiscOpcode, - NEON_NEG_opcode = NEON_NEG & NEON2RegMiscOpcode, - NEON_XTN_opcode = NEON_XTN & NEON2RegMiscOpcode, - NEON_UQXTN_opcode = NEON_UQXTN & NEON2RegMiscOpcode, - - // These instructions use only one bit of the size field. The other bit is - // used to distinguish between instructions. - NEON2RegMiscFPMask = NEON2RegMiscMask | 0x00800000, - NEON_FABS = NEON2RegMiscFixed | 0x0080F000, - NEON_FNEG = NEON2RegMiscFixed | 0x2080F000, - NEON_FCVTN = NEON2RegMiscFixed | 0x00016000, - NEON_FCVTXN = NEON2RegMiscFixed | 0x20016000, - NEON_FCVTL = NEON2RegMiscFixed | 0x00017000, - NEON_FRINTN = NEON2RegMiscFixed | 0x00018000, - NEON_FRINTA = NEON2RegMiscFixed | 0x20018000, - NEON_FRINTP = NEON2RegMiscFixed | 0x00818000, - NEON_FRINTM = NEON2RegMiscFixed | 0x00019000, - NEON_FRINTX = NEON2RegMiscFixed | 0x20019000, - NEON_FRINTZ = NEON2RegMiscFixed | 0x00819000, - NEON_FRINTI = NEON2RegMiscFixed | 0x20819000, - NEON_FCVTNS = NEON2RegMiscFixed | 0x0001A000, - NEON_FCVTNU = NEON_FCVTNS | NEON2RegMiscUBit, - NEON_FCVTPS = NEON2RegMiscFixed | 0x0081A000, - NEON_FCVTPU = NEON_FCVTPS | NEON2RegMiscUBit, - NEON_FCVTMS = NEON2RegMiscFixed | 0x0001B000, - NEON_FCVTMU = NEON_FCVTMS | NEON2RegMiscUBit, - NEON_FCVTZS = NEON2RegMiscFixed | 0x0081B000, - NEON_FCVTZU = NEON_FCVTZS | NEON2RegMiscUBit, - NEON_FCVTAS = NEON2RegMiscFixed | 0x0001C000, - NEON_FCVTAU = NEON_FCVTAS | NEON2RegMiscUBit, - NEON_FSQRT = NEON2RegMiscFixed | 0x2081F000, - NEON_SCVTF = NEON2RegMiscFixed | 0x0001D000, - NEON_UCVTF = NEON_SCVTF | NEON2RegMiscUBit, - NEON_URSQRTE = NEON2RegMiscFixed | 0x2081C000, - NEON_URECPE = NEON2RegMiscFixed | 0x0081C000, - NEON_FRSQRTE = NEON2RegMiscFixed | 0x2081D000, - NEON_FRECPE = NEON2RegMiscFixed | 0x0081D000, - NEON_FCMGT_zero = NEON2RegMiscFixed | 0x0080C000, - NEON_FCMGE_zero = NEON2RegMiscFixed | 0x2080C000, - NEON_FCMEQ_zero = NEON2RegMiscFixed | 0x0080D000, - NEON_FCMLE_zero = NEON2RegMiscFixed | 0x2080D000, - NEON_FCMLT_zero = NEON2RegMiscFixed | 0x0080E000, - - NEON_FCVTL_opcode = NEON_FCVTL & NEON2RegMiscOpcode, - NEON_FCVTN_opcode = NEON_FCVTN & NEON2RegMiscOpcode -}; - -// NEON instructions with three same-type operands. -enum NEON3SameOp { - NEON3SameFixed = 0x0E200400, - NEON3SameFMask = 0x9F200400, - NEON3SameMask = 0xBF20FC00, - NEON3SameUBit = 0x20000000, - NEON_ADD = NEON3SameFixed | 0x00008000, - NEON_ADDP = NEON3SameFixed | 0x0000B800, - NEON_SHADD = NEON3SameFixed | 0x00000000, - NEON_SHSUB = NEON3SameFixed | 0x00002000, - NEON_SRHADD = NEON3SameFixed | 0x00001000, - NEON_CMEQ = NEON3SameFixed | NEON3SameUBit | 0x00008800, - NEON_CMGE = NEON3SameFixed | 0x00003800, - NEON_CMGT = NEON3SameFixed | 0x00003000, - NEON_CMHI = NEON3SameFixed | NEON3SameUBit | NEON_CMGT, - NEON_CMHS = NEON3SameFixed | NEON3SameUBit | NEON_CMGE, - NEON_CMTST = NEON3SameFixed | 0x00008800, - NEON_MLA = NEON3SameFixed | 0x00009000, - NEON_MLS = NEON3SameFixed | 0x20009000, - NEON_MUL = NEON3SameFixed | 0x00009800, - NEON_PMUL = NEON3SameFixed | 0x20009800, - NEON_SRSHL = NEON3SameFixed | 0x00005000, - NEON_SQSHL = NEON3SameFixed | 0x00004800, - NEON_SQRSHL = NEON3SameFixed | 0x00005800, - NEON_SSHL = NEON3SameFixed | 0x00004000, - NEON_SMAX = NEON3SameFixed | 0x00006000, - NEON_SMAXP = NEON3SameFixed | 0x0000A000, - NEON_SMIN = NEON3SameFixed | 0x00006800, - NEON_SMINP = NEON3SameFixed | 0x0000A800, - NEON_SABD = NEON3SameFixed | 0x00007000, - NEON_SABA = NEON3SameFixed | 0x00007800, - NEON_UABD = NEON3SameFixed | NEON3SameUBit | NEON_SABD, - NEON_UABA = NEON3SameFixed | NEON3SameUBit | NEON_SABA, - NEON_SQADD = NEON3SameFixed | 0x00000800, - NEON_SQSUB = NEON3SameFixed | 0x00002800, - NEON_SUB = NEON3SameFixed | NEON3SameUBit | 0x00008000, - NEON_UHADD = NEON3SameFixed | NEON3SameUBit | NEON_SHADD, - NEON_UHSUB = NEON3SameFixed | NEON3SameUBit | NEON_SHSUB, - NEON_URHADD = NEON3SameFixed | NEON3SameUBit | NEON_SRHADD, - NEON_UMAX = NEON3SameFixed | NEON3SameUBit | NEON_SMAX, - NEON_UMAXP = NEON3SameFixed | NEON3SameUBit | NEON_SMAXP, - NEON_UMIN = NEON3SameFixed | NEON3SameUBit | NEON_SMIN, - NEON_UMINP = NEON3SameFixed | NEON3SameUBit | NEON_SMINP, - NEON_URSHL = NEON3SameFixed | NEON3SameUBit | NEON_SRSHL, - NEON_UQADD = NEON3SameFixed | NEON3SameUBit | NEON_SQADD, - NEON_UQRSHL = NEON3SameFixed | NEON3SameUBit | NEON_SQRSHL, - NEON_UQSHL = NEON3SameFixed | NEON3SameUBit | NEON_SQSHL, - NEON_UQSUB = NEON3SameFixed | NEON3SameUBit | NEON_SQSUB, - NEON_USHL = NEON3SameFixed | NEON3SameUBit | NEON_SSHL, - NEON_SQDMULH = NEON3SameFixed | 0x0000B000, - NEON_SQRDMULH = NEON3SameFixed | 0x2000B000, - - // NEON floating point instructions with three same-type operands. - NEON3SameFPFixed = NEON3SameFixed | 0x0000C000, - NEON3SameFPFMask = NEON3SameFMask | 0x0000C000, - NEON3SameFPMask = NEON3SameMask | 0x00800000, - NEON_FADD = NEON3SameFixed | 0x0000D000, - NEON_FSUB = NEON3SameFixed | 0x0080D000, - NEON_FMUL = NEON3SameFixed | 0x2000D800, - NEON_FDIV = NEON3SameFixed | 0x2000F800, - NEON_FMAX = NEON3SameFixed | 0x0000F000, - NEON_FMAXNM = NEON3SameFixed | 0x0000C000, - NEON_FMAXP = NEON3SameFixed | 0x2000F000, - NEON_FMAXNMP = NEON3SameFixed | 0x2000C000, - NEON_FMIN = NEON3SameFixed | 0x0080F000, - NEON_FMINNM = NEON3SameFixed | 0x0080C000, - NEON_FMINP = NEON3SameFixed | 0x2080F000, - NEON_FMINNMP = NEON3SameFixed | 0x2080C000, - NEON_FMLA = NEON3SameFixed | 0x0000C800, - NEON_FMLS = NEON3SameFixed | 0x0080C800, - NEON_FMULX = NEON3SameFixed | 0x0000D800, - NEON_FRECPS = NEON3SameFixed | 0x0000F800, - NEON_FRSQRTS = NEON3SameFixed | 0x0080F800, - NEON_FABD = NEON3SameFixed | 0x2080D000, - NEON_FADDP = NEON3SameFixed | 0x2000D000, - NEON_FCMEQ = NEON3SameFixed | 0x0000E000, - NEON_FCMGE = NEON3SameFixed | 0x2000E000, - NEON_FCMGT = NEON3SameFixed | 0x2080E000, - NEON_FACGE = NEON3SameFixed | 0x2000E800, - NEON_FACGT = NEON3SameFixed | 0x2080E800, - - // NEON logical instructions with three same-type operands. - NEON3SameLogicalFixed = NEON3SameFixed | 0x00001800, - NEON3SameLogicalFMask = NEON3SameFMask | 0x0000F800, - NEON3SameLogicalMask = 0xBFE0FC00, - NEON3SameLogicalFormatMask = NEON_Q, - NEON_AND = NEON3SameLogicalFixed | 0x00000000, - NEON_ORR = NEON3SameLogicalFixed | 0x00A00000, - NEON_ORN = NEON3SameLogicalFixed | 0x00C00000, - NEON_EOR = NEON3SameLogicalFixed | 0x20000000, - NEON_BIC = NEON3SameLogicalFixed | 0x00400000, - NEON_BIF = NEON3SameLogicalFixed | 0x20C00000, - NEON_BIT = NEON3SameLogicalFixed | 0x20800000, - NEON_BSL = NEON3SameLogicalFixed | 0x20400000 -}; - -// NEON instructions with three different-type operands. -enum NEON3DifferentOp { - NEON3DifferentFixed = 0x0E200000, - NEON3DifferentFMask = 0x9F200C00, - NEON3DifferentMask = 0xFF20FC00, - NEON_ADDHN = NEON3DifferentFixed | 0x00004000, - NEON_ADDHN2 = NEON_ADDHN | NEON_Q, - NEON_PMULL = NEON3DifferentFixed | 0x0000E000, - NEON_PMULL2 = NEON_PMULL | NEON_Q, - NEON_RADDHN = NEON3DifferentFixed | 0x20004000, - NEON_RADDHN2 = NEON_RADDHN | NEON_Q, - NEON_RSUBHN = NEON3DifferentFixed | 0x20006000, - NEON_RSUBHN2 = NEON_RSUBHN | NEON_Q, - NEON_SABAL = NEON3DifferentFixed | 0x00005000, - NEON_SABAL2 = NEON_SABAL | NEON_Q, - NEON_SABDL = NEON3DifferentFixed | 0x00007000, - NEON_SABDL2 = NEON_SABDL | NEON_Q, - NEON_SADDL = NEON3DifferentFixed | 0x00000000, - NEON_SADDL2 = NEON_SADDL | NEON_Q, - NEON_SADDW = NEON3DifferentFixed | 0x00001000, - NEON_SADDW2 = NEON_SADDW | NEON_Q, - NEON_SMLAL = NEON3DifferentFixed | 0x00008000, - NEON_SMLAL2 = NEON_SMLAL | NEON_Q, - NEON_SMLSL = NEON3DifferentFixed | 0x0000A000, - NEON_SMLSL2 = NEON_SMLSL | NEON_Q, - NEON_SMULL = NEON3DifferentFixed | 0x0000C000, - NEON_SMULL2 = NEON_SMULL | NEON_Q, - NEON_SSUBL = NEON3DifferentFixed | 0x00002000, - NEON_SSUBL2 = NEON_SSUBL | NEON_Q, - NEON_SSUBW = NEON3DifferentFixed | 0x00003000, - NEON_SSUBW2 = NEON_SSUBW | NEON_Q, - NEON_SQDMLAL = NEON3DifferentFixed | 0x00009000, - NEON_SQDMLAL2 = NEON_SQDMLAL | NEON_Q, - NEON_SQDMLSL = NEON3DifferentFixed | 0x0000B000, - NEON_SQDMLSL2 = NEON_SQDMLSL | NEON_Q, - NEON_SQDMULL = NEON3DifferentFixed | 0x0000D000, - NEON_SQDMULL2 = NEON_SQDMULL | NEON_Q, - NEON_SUBHN = NEON3DifferentFixed | 0x00006000, - NEON_SUBHN2 = NEON_SUBHN | NEON_Q, - NEON_UABAL = NEON_SABAL | NEON3SameUBit, - NEON_UABAL2 = NEON_UABAL | NEON_Q, - NEON_UABDL = NEON_SABDL | NEON3SameUBit, - NEON_UABDL2 = NEON_UABDL | NEON_Q, - NEON_UADDL = NEON_SADDL | NEON3SameUBit, - NEON_UADDL2 = NEON_UADDL | NEON_Q, - NEON_UADDW = NEON_SADDW | NEON3SameUBit, - NEON_UADDW2 = NEON_UADDW | NEON_Q, - NEON_UMLAL = NEON_SMLAL | NEON3SameUBit, - NEON_UMLAL2 = NEON_UMLAL | NEON_Q, - NEON_UMLSL = NEON_SMLSL | NEON3SameUBit, - NEON_UMLSL2 = NEON_UMLSL | NEON_Q, - NEON_UMULL = NEON_SMULL | NEON3SameUBit, - NEON_UMULL2 = NEON_UMULL | NEON_Q, - NEON_USUBL = NEON_SSUBL | NEON3SameUBit, - NEON_USUBL2 = NEON_USUBL | NEON_Q, - NEON_USUBW = NEON_SSUBW | NEON3SameUBit, - NEON_USUBW2 = NEON_USUBW | NEON_Q -}; - -// NEON instructions operating across vectors. -enum NEONAcrossLanesOp { - NEONAcrossLanesFixed = 0x0E300800, - NEONAcrossLanesFMask = 0x9F3E0C00, - NEONAcrossLanesMask = 0xBF3FFC00, - NEON_ADDV = NEONAcrossLanesFixed | 0x0001B000, - NEON_SADDLV = NEONAcrossLanesFixed | 0x00003000, - NEON_UADDLV = NEONAcrossLanesFixed | 0x20003000, - NEON_SMAXV = NEONAcrossLanesFixed | 0x0000A000, - NEON_SMINV = NEONAcrossLanesFixed | 0x0001A000, - NEON_UMAXV = NEONAcrossLanesFixed | 0x2000A000, - NEON_UMINV = NEONAcrossLanesFixed | 0x2001A000, - - // NEON floating point across instructions. - NEONAcrossLanesFPFixed = NEONAcrossLanesFixed | 0x0000C000, - NEONAcrossLanesFPFMask = NEONAcrossLanesFMask | 0x0000C000, - NEONAcrossLanesFPMask = NEONAcrossLanesMask | 0x00800000, - - NEON_FMAXV = NEONAcrossLanesFPFixed | 0x2000F000, - NEON_FMINV = NEONAcrossLanesFPFixed | 0x2080F000, - NEON_FMAXNMV = NEONAcrossLanesFPFixed | 0x2000C000, - NEON_FMINNMV = NEONAcrossLanesFPFixed | 0x2080C000 -}; - -// NEON instructions with indexed element operand. -enum NEONByIndexedElementOp { - NEONByIndexedElementFixed = 0x0F000000, - NEONByIndexedElementFMask = 0x9F000400, - NEONByIndexedElementMask = 0xBF00F400, - NEON_MUL_byelement = NEONByIndexedElementFixed | 0x00008000, - NEON_MLA_byelement = NEONByIndexedElementFixed | 0x20000000, - NEON_MLS_byelement = NEONByIndexedElementFixed | 0x20004000, - NEON_SMULL_byelement = NEONByIndexedElementFixed | 0x0000A000, - NEON_SMLAL_byelement = NEONByIndexedElementFixed | 0x00002000, - NEON_SMLSL_byelement = NEONByIndexedElementFixed | 0x00006000, - NEON_UMULL_byelement = NEONByIndexedElementFixed | 0x2000A000, - NEON_UMLAL_byelement = NEONByIndexedElementFixed | 0x20002000, - NEON_UMLSL_byelement = NEONByIndexedElementFixed | 0x20006000, - NEON_SQDMULL_byelement = NEONByIndexedElementFixed | 0x0000B000, - NEON_SQDMLAL_byelement = NEONByIndexedElementFixed | 0x00003000, - NEON_SQDMLSL_byelement = NEONByIndexedElementFixed | 0x00007000, - NEON_SQDMULH_byelement = NEONByIndexedElementFixed | 0x0000C000, - NEON_SQRDMULH_byelement = NEONByIndexedElementFixed | 0x0000D000, - - // Floating point instructions. - NEONByIndexedElementFPFixed = NEONByIndexedElementFixed | 0x00800000, - NEONByIndexedElementFPMask = NEONByIndexedElementMask | 0x00800000, - NEON_FMLA_byelement = NEONByIndexedElementFPFixed | 0x00001000, - NEON_FMLS_byelement = NEONByIndexedElementFPFixed | 0x00005000, - NEON_FMUL_byelement = NEONByIndexedElementFPFixed | 0x00009000, - NEON_FMULX_byelement = NEONByIndexedElementFPFixed | 0x20009000 -}; - -// NEON register copy. -enum NEONCopyOp { - NEONCopyFixed = 0x0E000400, - NEONCopyFMask = 0x9FE08400, - NEONCopyMask = 0x3FE08400, - NEONCopyInsElementMask = NEONCopyMask | 0x40000000, - NEONCopyInsGeneralMask = NEONCopyMask | 0x40007800, - NEONCopyDupElementMask = NEONCopyMask | 0x20007800, - NEONCopyDupGeneralMask = NEONCopyDupElementMask, - NEONCopyUmovMask = NEONCopyMask | 0x20007800, - NEONCopySmovMask = NEONCopyMask | 0x20007800, - NEON_INS_ELEMENT = NEONCopyFixed | 0x60000000, - NEON_INS_GENERAL = NEONCopyFixed | 0x40001800, - NEON_DUP_ELEMENT = NEONCopyFixed | 0x00000000, - NEON_DUP_GENERAL = NEONCopyFixed | 0x00000800, - NEON_SMOV = NEONCopyFixed | 0x00002800, - NEON_UMOV = NEONCopyFixed | 0x00003800 -}; - -// NEON extract. -enum NEONExtractOp { - NEONExtractFixed = 0x2E000000, - NEONExtractFMask = 0xBF208400, - NEONExtractMask = 0xBFE08400, - NEON_EXT = NEONExtractFixed | 0x00000000 -}; - -enum NEONLoadStoreMultiOp { - NEONLoadStoreMultiL = 0x00400000, - NEONLoadStoreMulti1_1v = 0x00007000, - NEONLoadStoreMulti1_2v = 0x0000A000, - NEONLoadStoreMulti1_3v = 0x00006000, - NEONLoadStoreMulti1_4v = 0x00002000, - NEONLoadStoreMulti2 = 0x00008000, - NEONLoadStoreMulti3 = 0x00004000, - NEONLoadStoreMulti4 = 0x00000000 -}; - -// NEON load/store multiple structures. -enum NEONLoadStoreMultiStructOp { - NEONLoadStoreMultiStructFixed = 0x0C000000, - NEONLoadStoreMultiStructFMask = 0xBFBF0000, - NEONLoadStoreMultiStructMask = 0xBFFFF000, - NEONLoadStoreMultiStructStore = NEONLoadStoreMultiStructFixed, - NEONLoadStoreMultiStructLoad = NEONLoadStoreMultiStructFixed | - NEONLoadStoreMultiL, - NEON_LD1_1v = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti1_1v, - NEON_LD1_2v = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti1_2v, - NEON_LD1_3v = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti1_3v, - NEON_LD1_4v = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti1_4v, - NEON_LD2 = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti2, - NEON_LD3 = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti3, - NEON_LD4 = NEONLoadStoreMultiStructLoad | NEONLoadStoreMulti4, - NEON_ST1_1v = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti1_1v, - NEON_ST1_2v = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti1_2v, - NEON_ST1_3v = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti1_3v, - NEON_ST1_4v = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti1_4v, - NEON_ST2 = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti2, - NEON_ST3 = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti3, - NEON_ST4 = NEONLoadStoreMultiStructStore | NEONLoadStoreMulti4 -}; - -// NEON load/store multiple structures with post-index addressing. -enum NEONLoadStoreMultiStructPostIndexOp { - NEONLoadStoreMultiStructPostIndexFixed = 0x0C800000, - NEONLoadStoreMultiStructPostIndexFMask = 0xBFA00000, - NEONLoadStoreMultiStructPostIndexMask = 0xBFE0F000, - NEONLoadStoreMultiStructPostIndex = 0x00800000, - NEON_LD1_1v_post = NEON_LD1_1v | NEONLoadStoreMultiStructPostIndex, - NEON_LD1_2v_post = NEON_LD1_2v | NEONLoadStoreMultiStructPostIndex, - NEON_LD1_3v_post = NEON_LD1_3v | NEONLoadStoreMultiStructPostIndex, - NEON_LD1_4v_post = NEON_LD1_4v | NEONLoadStoreMultiStructPostIndex, - NEON_LD2_post = NEON_LD2 | NEONLoadStoreMultiStructPostIndex, - NEON_LD3_post = NEON_LD3 | NEONLoadStoreMultiStructPostIndex, - NEON_LD4_post = NEON_LD4 | NEONLoadStoreMultiStructPostIndex, - NEON_ST1_1v_post = NEON_ST1_1v | NEONLoadStoreMultiStructPostIndex, - NEON_ST1_2v_post = NEON_ST1_2v | NEONLoadStoreMultiStructPostIndex, - NEON_ST1_3v_post = NEON_ST1_3v | NEONLoadStoreMultiStructPostIndex, - NEON_ST1_4v_post = NEON_ST1_4v | NEONLoadStoreMultiStructPostIndex, - NEON_ST2_post = NEON_ST2 | NEONLoadStoreMultiStructPostIndex, - NEON_ST3_post = NEON_ST3 | NEONLoadStoreMultiStructPostIndex, - NEON_ST4_post = NEON_ST4 | NEONLoadStoreMultiStructPostIndex -}; - -enum NEONLoadStoreSingleOp { - NEONLoadStoreSingle1 = 0x00000000, - NEONLoadStoreSingle2 = 0x00200000, - NEONLoadStoreSingle3 = 0x00002000, - NEONLoadStoreSingle4 = 0x00202000, - NEONLoadStoreSingleL = 0x00400000, - NEONLoadStoreSingle_b = 0x00000000, - NEONLoadStoreSingle_h = 0x00004000, - NEONLoadStoreSingle_s = 0x00008000, - NEONLoadStoreSingle_d = 0x00008400, - NEONLoadStoreSingleAllLanes = 0x0000C000, - NEONLoadStoreSingleLenMask = 0x00202000 -}; - -// NEON load/store single structure. -enum NEONLoadStoreSingleStructOp { - NEONLoadStoreSingleStructFixed = 0x0D000000, - NEONLoadStoreSingleStructFMask = 0xBF9F0000, - NEONLoadStoreSingleStructMask = 0xBFFFE000, - NEONLoadStoreSingleStructStore = NEONLoadStoreSingleStructFixed, - NEONLoadStoreSingleStructLoad = NEONLoadStoreSingleStructFixed | - NEONLoadStoreSingleL, - NEONLoadStoreSingleStructLoad1 = NEONLoadStoreSingle1 | - NEONLoadStoreSingleStructLoad, - NEONLoadStoreSingleStructLoad2 = NEONLoadStoreSingle2 | - NEONLoadStoreSingleStructLoad, - NEONLoadStoreSingleStructLoad3 = NEONLoadStoreSingle3 | - NEONLoadStoreSingleStructLoad, - NEONLoadStoreSingleStructLoad4 = NEONLoadStoreSingle4 | - NEONLoadStoreSingleStructLoad, - NEONLoadStoreSingleStructStore1 = NEONLoadStoreSingle1 | - NEONLoadStoreSingleStructFixed, - NEONLoadStoreSingleStructStore2 = NEONLoadStoreSingle2 | - NEONLoadStoreSingleStructFixed, - NEONLoadStoreSingleStructStore3 = NEONLoadStoreSingle3 | - NEONLoadStoreSingleStructFixed, - NEONLoadStoreSingleStructStore4 = NEONLoadStoreSingle4 | - NEONLoadStoreSingleStructFixed, - NEON_LD1_b = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingle_b, - NEON_LD1_h = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingle_h, - NEON_LD1_s = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingle_s, - NEON_LD1_d = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingle_d, - NEON_LD1R = NEONLoadStoreSingleStructLoad1 | NEONLoadStoreSingleAllLanes, - NEON_ST1_b = NEONLoadStoreSingleStructStore1 | NEONLoadStoreSingle_b, - NEON_ST1_h = NEONLoadStoreSingleStructStore1 | NEONLoadStoreSingle_h, - NEON_ST1_s = NEONLoadStoreSingleStructStore1 | NEONLoadStoreSingle_s, - NEON_ST1_d = NEONLoadStoreSingleStructStore1 | NEONLoadStoreSingle_d, - - NEON_LD2_b = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingle_b, - NEON_LD2_h = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingle_h, - NEON_LD2_s = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingle_s, - NEON_LD2_d = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingle_d, - NEON_LD2R = NEONLoadStoreSingleStructLoad2 | NEONLoadStoreSingleAllLanes, - NEON_ST2_b = NEONLoadStoreSingleStructStore2 | NEONLoadStoreSingle_b, - NEON_ST2_h = NEONLoadStoreSingleStructStore2 | NEONLoadStoreSingle_h, - NEON_ST2_s = NEONLoadStoreSingleStructStore2 | NEONLoadStoreSingle_s, - NEON_ST2_d = NEONLoadStoreSingleStructStore2 | NEONLoadStoreSingle_d, - - NEON_LD3_b = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingle_b, - NEON_LD3_h = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingle_h, - NEON_LD3_s = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingle_s, - NEON_LD3_d = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingle_d, - NEON_LD3R = NEONLoadStoreSingleStructLoad3 | NEONLoadStoreSingleAllLanes, - NEON_ST3_b = NEONLoadStoreSingleStructStore3 | NEONLoadStoreSingle_b, - NEON_ST3_h = NEONLoadStoreSingleStructStore3 | NEONLoadStoreSingle_h, - NEON_ST3_s = NEONLoadStoreSingleStructStore3 | NEONLoadStoreSingle_s, - NEON_ST3_d = NEONLoadStoreSingleStructStore3 | NEONLoadStoreSingle_d, - - NEON_LD4_b = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingle_b, - NEON_LD4_h = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingle_h, - NEON_LD4_s = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingle_s, - NEON_LD4_d = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingle_d, - NEON_LD4R = NEONLoadStoreSingleStructLoad4 | NEONLoadStoreSingleAllLanes, - NEON_ST4_b = NEONLoadStoreSingleStructStore4 | NEONLoadStoreSingle_b, - NEON_ST4_h = NEONLoadStoreSingleStructStore4 | NEONLoadStoreSingle_h, - NEON_ST4_s = NEONLoadStoreSingleStructStore4 | NEONLoadStoreSingle_s, - NEON_ST4_d = NEONLoadStoreSingleStructStore4 | NEONLoadStoreSingle_d -}; - -// NEON load/store single structure with post-index addressing. -enum NEONLoadStoreSingleStructPostIndexOp { - NEONLoadStoreSingleStructPostIndexFixed = 0x0D800000, - NEONLoadStoreSingleStructPostIndexFMask = 0xBF800000, - NEONLoadStoreSingleStructPostIndexMask = 0xBFE0E000, - NEONLoadStoreSingleStructPostIndex = 0x00800000, - NEON_LD1_b_post = NEON_LD1_b | NEONLoadStoreSingleStructPostIndex, - NEON_LD1_h_post = NEON_LD1_h | NEONLoadStoreSingleStructPostIndex, - NEON_LD1_s_post = NEON_LD1_s | NEONLoadStoreSingleStructPostIndex, - NEON_LD1_d_post = NEON_LD1_d | NEONLoadStoreSingleStructPostIndex, - NEON_LD1R_post = NEON_LD1R | NEONLoadStoreSingleStructPostIndex, - NEON_ST1_b_post = NEON_ST1_b | NEONLoadStoreSingleStructPostIndex, - NEON_ST1_h_post = NEON_ST1_h | NEONLoadStoreSingleStructPostIndex, - NEON_ST1_s_post = NEON_ST1_s | NEONLoadStoreSingleStructPostIndex, - NEON_ST1_d_post = NEON_ST1_d | NEONLoadStoreSingleStructPostIndex, - - NEON_LD2_b_post = NEON_LD2_b | NEONLoadStoreSingleStructPostIndex, - NEON_LD2_h_post = NEON_LD2_h | NEONLoadStoreSingleStructPostIndex, - NEON_LD2_s_post = NEON_LD2_s | NEONLoadStoreSingleStructPostIndex, - NEON_LD2_d_post = NEON_LD2_d | NEONLoadStoreSingleStructPostIndex, - NEON_LD2R_post = NEON_LD2R | NEONLoadStoreSingleStructPostIndex, - NEON_ST2_b_post = NEON_ST2_b | NEONLoadStoreSingleStructPostIndex, - NEON_ST2_h_post = NEON_ST2_h | NEONLoadStoreSingleStructPostIndex, - NEON_ST2_s_post = NEON_ST2_s | NEONLoadStoreSingleStructPostIndex, - NEON_ST2_d_post = NEON_ST2_d | NEONLoadStoreSingleStructPostIndex, - - NEON_LD3_b_post = NEON_LD3_b | NEONLoadStoreSingleStructPostIndex, - NEON_LD3_h_post = NEON_LD3_h | NEONLoadStoreSingleStructPostIndex, - NEON_LD3_s_post = NEON_LD3_s | NEONLoadStoreSingleStructPostIndex, - NEON_LD3_d_post = NEON_LD3_d | NEONLoadStoreSingleStructPostIndex, - NEON_LD3R_post = NEON_LD3R | NEONLoadStoreSingleStructPostIndex, - NEON_ST3_b_post = NEON_ST3_b | NEONLoadStoreSingleStructPostIndex, - NEON_ST3_h_post = NEON_ST3_h | NEONLoadStoreSingleStructPostIndex, - NEON_ST3_s_post = NEON_ST3_s | NEONLoadStoreSingleStructPostIndex, - NEON_ST3_d_post = NEON_ST3_d | NEONLoadStoreSingleStructPostIndex, - - NEON_LD4_b_post = NEON_LD4_b | NEONLoadStoreSingleStructPostIndex, - NEON_LD4_h_post = NEON_LD4_h | NEONLoadStoreSingleStructPostIndex, - NEON_LD4_s_post = NEON_LD4_s | NEONLoadStoreSingleStructPostIndex, - NEON_LD4_d_post = NEON_LD4_d | NEONLoadStoreSingleStructPostIndex, - NEON_LD4R_post = NEON_LD4R | NEONLoadStoreSingleStructPostIndex, - NEON_ST4_b_post = NEON_ST4_b | NEONLoadStoreSingleStructPostIndex, - NEON_ST4_h_post = NEON_ST4_h | NEONLoadStoreSingleStructPostIndex, - NEON_ST4_s_post = NEON_ST4_s | NEONLoadStoreSingleStructPostIndex, - NEON_ST4_d_post = NEON_ST4_d | NEONLoadStoreSingleStructPostIndex -}; - -// NEON modified immediate. -enum NEONModifiedImmediateOp { - NEONModifiedImmediateFixed = 0x0F000400, - NEONModifiedImmediateFMask = 0x9FF80400, - NEONModifiedImmediateOpBit = 0x20000000, - NEONModifiedImmediate_MOVI = NEONModifiedImmediateFixed | 0x00000000, - NEONModifiedImmediate_MVNI = NEONModifiedImmediateFixed | 0x20000000, - NEONModifiedImmediate_ORR = NEONModifiedImmediateFixed | 0x00001000, - NEONModifiedImmediate_BIC = NEONModifiedImmediateFixed | 0x20001000 -}; - -// NEON shift immediate. -enum NEONShiftImmediateOp { - NEONShiftImmediateFixed = 0x0F000400, - NEONShiftImmediateFMask = 0x9F800400, - NEONShiftImmediateMask = 0xBF80FC00, - NEONShiftImmediateUBit = 0x20000000, - NEON_SHL = NEONShiftImmediateFixed | 0x00005000, - NEON_SSHLL = NEONShiftImmediateFixed | 0x0000A000, - NEON_USHLL = NEONShiftImmediateFixed | 0x2000A000, - NEON_SLI = NEONShiftImmediateFixed | 0x20005000, - NEON_SRI = NEONShiftImmediateFixed | 0x20004000, - NEON_SHRN = NEONShiftImmediateFixed | 0x00008000, - NEON_RSHRN = NEONShiftImmediateFixed | 0x00008800, - NEON_UQSHRN = NEONShiftImmediateFixed | 0x20009000, - NEON_UQRSHRN = NEONShiftImmediateFixed | 0x20009800, - NEON_SQSHRN = NEONShiftImmediateFixed | 0x00009000, - NEON_SQRSHRN = NEONShiftImmediateFixed | 0x00009800, - NEON_SQSHRUN = NEONShiftImmediateFixed | 0x20008000, - NEON_SQRSHRUN = NEONShiftImmediateFixed | 0x20008800, - NEON_SSHR = NEONShiftImmediateFixed | 0x00000000, - NEON_SRSHR = NEONShiftImmediateFixed | 0x00002000, - NEON_USHR = NEONShiftImmediateFixed | 0x20000000, - NEON_URSHR = NEONShiftImmediateFixed | 0x20002000, - NEON_SSRA = NEONShiftImmediateFixed | 0x00001000, - NEON_SRSRA = NEONShiftImmediateFixed | 0x00003000, - NEON_USRA = NEONShiftImmediateFixed | 0x20001000, - NEON_URSRA = NEONShiftImmediateFixed | 0x20003000, - NEON_SQSHLU = NEONShiftImmediateFixed | 0x20006000, - NEON_SCVTF_imm = NEONShiftImmediateFixed | 0x0000E000, - NEON_UCVTF_imm = NEONShiftImmediateFixed | 0x2000E000, - NEON_FCVTZS_imm = NEONShiftImmediateFixed | 0x0000F800, - NEON_FCVTZU_imm = NEONShiftImmediateFixed | 0x2000F800, - NEON_SQSHL_imm = NEONShiftImmediateFixed | 0x00007000, - NEON_UQSHL_imm = NEONShiftImmediateFixed | 0x20007000 -}; - -// NEON table. -enum NEONTableOp { - NEONTableFixed = 0x0E000000, - NEONTableFMask = 0xBF208C00, - NEONTableExt = 0x00001000, - NEONTableMask = 0xBF20FC00, - NEON_TBL_1v = NEONTableFixed | 0x00000000, - NEON_TBL_2v = NEONTableFixed | 0x00002000, - NEON_TBL_3v = NEONTableFixed | 0x00004000, - NEON_TBL_4v = NEONTableFixed | 0x00006000, - NEON_TBX_1v = NEON_TBL_1v | NEONTableExt, - NEON_TBX_2v = NEON_TBL_2v | NEONTableExt, - NEON_TBX_3v = NEON_TBL_3v | NEONTableExt, - NEON_TBX_4v = NEON_TBL_4v | NEONTableExt -}; - -// NEON perm. -enum NEONPermOp { - NEONPermFixed = 0x0E000800, - NEONPermFMask = 0xBF208C00, - NEONPermMask = 0x3F20FC00, - NEON_UZP1 = NEONPermFixed | 0x00001000, - NEON_TRN1 = NEONPermFixed | 0x00002000, - NEON_ZIP1 = NEONPermFixed | 0x00003000, - NEON_UZP2 = NEONPermFixed | 0x00005000, - NEON_TRN2 = NEONPermFixed | 0x00006000, - NEON_ZIP2 = NEONPermFixed | 0x00007000 -}; - -// NEON scalar instructions with two register operands. -enum NEONScalar2RegMiscOp { - NEONScalar2RegMiscFixed = 0x5E200800, - NEONScalar2RegMiscFMask = 0xDF3E0C00, - NEONScalar2RegMiscMask = NEON_Q | NEONScalar | NEON2RegMiscMask, - NEON_CMGT_zero_scalar = NEON_Q | NEONScalar | NEON_CMGT_zero, - NEON_CMEQ_zero_scalar = NEON_Q | NEONScalar | NEON_CMEQ_zero, - NEON_CMLT_zero_scalar = NEON_Q | NEONScalar | NEON_CMLT_zero, - NEON_CMGE_zero_scalar = NEON_Q | NEONScalar | NEON_CMGE_zero, - NEON_CMLE_zero_scalar = NEON_Q | NEONScalar | NEON_CMLE_zero, - NEON_ABS_scalar = NEON_Q | NEONScalar | NEON_ABS, - NEON_SQABS_scalar = NEON_Q | NEONScalar | NEON_SQABS, - NEON_NEG_scalar = NEON_Q | NEONScalar | NEON_NEG, - NEON_SQNEG_scalar = NEON_Q | NEONScalar | NEON_SQNEG, - NEON_SQXTN_scalar = NEON_Q | NEONScalar | NEON_SQXTN, - NEON_UQXTN_scalar = NEON_Q | NEONScalar | NEON_UQXTN, - NEON_SQXTUN_scalar = NEON_Q | NEONScalar | NEON_SQXTUN, - NEON_SUQADD_scalar = NEON_Q | NEONScalar | NEON_SUQADD, - NEON_USQADD_scalar = NEON_Q | NEONScalar | NEON_USQADD, - - NEONScalar2RegMiscOpcode = NEON2RegMiscOpcode, - NEON_NEG_scalar_opcode = NEON_NEG_scalar & NEONScalar2RegMiscOpcode, - - NEONScalar2RegMiscFPMask = NEONScalar2RegMiscMask | 0x00800000, - NEON_FRSQRTE_scalar = NEON_Q | NEONScalar | NEON_FRSQRTE, - NEON_FRECPE_scalar = NEON_Q | NEONScalar | NEON_FRECPE, - NEON_SCVTF_scalar = NEON_Q | NEONScalar | NEON_SCVTF, - NEON_UCVTF_scalar = NEON_Q | NEONScalar | NEON_UCVTF, - NEON_FCMGT_zero_scalar = NEON_Q | NEONScalar | NEON_FCMGT_zero, - NEON_FCMEQ_zero_scalar = NEON_Q | NEONScalar | NEON_FCMEQ_zero, - NEON_FCMLT_zero_scalar = NEON_Q | NEONScalar | NEON_FCMLT_zero, - NEON_FCMGE_zero_scalar = NEON_Q | NEONScalar | NEON_FCMGE_zero, - NEON_FCMLE_zero_scalar = NEON_Q | NEONScalar | NEON_FCMLE_zero, - NEON_FRECPX_scalar = NEONScalar2RegMiscFixed | 0x0081F000, - NEON_FCVTNS_scalar = NEON_Q | NEONScalar | NEON_FCVTNS, - NEON_FCVTNU_scalar = NEON_Q | NEONScalar | NEON_FCVTNU, - NEON_FCVTPS_scalar = NEON_Q | NEONScalar | NEON_FCVTPS, - NEON_FCVTPU_scalar = NEON_Q | NEONScalar | NEON_FCVTPU, - NEON_FCVTMS_scalar = NEON_Q | NEONScalar | NEON_FCVTMS, - NEON_FCVTMU_scalar = NEON_Q | NEONScalar | NEON_FCVTMU, - NEON_FCVTZS_scalar = NEON_Q | NEONScalar | NEON_FCVTZS, - NEON_FCVTZU_scalar = NEON_Q | NEONScalar | NEON_FCVTZU, - NEON_FCVTAS_scalar = NEON_Q | NEONScalar | NEON_FCVTAS, - NEON_FCVTAU_scalar = NEON_Q | NEONScalar | NEON_FCVTAU, - NEON_FCVTXN_scalar = NEON_Q | NEONScalar | NEON_FCVTXN -}; - -// NEON scalar instructions with three same-type operands. -enum NEONScalar3SameOp { - NEONScalar3SameFixed = 0x5E200400, - NEONScalar3SameFMask = 0xDF200400, - NEONScalar3SameMask = 0xFF20FC00, - NEON_ADD_scalar = NEON_Q | NEONScalar | NEON_ADD, - NEON_CMEQ_scalar = NEON_Q | NEONScalar | NEON_CMEQ, - NEON_CMGE_scalar = NEON_Q | NEONScalar | NEON_CMGE, - NEON_CMGT_scalar = NEON_Q | NEONScalar | NEON_CMGT, - NEON_CMHI_scalar = NEON_Q | NEONScalar | NEON_CMHI, - NEON_CMHS_scalar = NEON_Q | NEONScalar | NEON_CMHS, - NEON_CMTST_scalar = NEON_Q | NEONScalar | NEON_CMTST, - NEON_SUB_scalar = NEON_Q | NEONScalar | NEON_SUB, - NEON_UQADD_scalar = NEON_Q | NEONScalar | NEON_UQADD, - NEON_SQADD_scalar = NEON_Q | NEONScalar | NEON_SQADD, - NEON_UQSUB_scalar = NEON_Q | NEONScalar | NEON_UQSUB, - NEON_SQSUB_scalar = NEON_Q | NEONScalar | NEON_SQSUB, - NEON_USHL_scalar = NEON_Q | NEONScalar | NEON_USHL, - NEON_SSHL_scalar = NEON_Q | NEONScalar | NEON_SSHL, - NEON_UQSHL_scalar = NEON_Q | NEONScalar | NEON_UQSHL, - NEON_SQSHL_scalar = NEON_Q | NEONScalar | NEON_SQSHL, - NEON_URSHL_scalar = NEON_Q | NEONScalar | NEON_URSHL, - NEON_SRSHL_scalar = NEON_Q | NEONScalar | NEON_SRSHL, - NEON_UQRSHL_scalar = NEON_Q | NEONScalar | NEON_UQRSHL, - NEON_SQRSHL_scalar = NEON_Q | NEONScalar | NEON_SQRSHL, - NEON_SQDMULH_scalar = NEON_Q | NEONScalar | NEON_SQDMULH, - NEON_SQRDMULH_scalar = NEON_Q | NEONScalar | NEON_SQRDMULH, - - // NEON floating point scalar instructions with three same-type operands. - NEONScalar3SameFPFixed = NEONScalar3SameFixed | 0x0000C000, - NEONScalar3SameFPFMask = NEONScalar3SameFMask | 0x0000C000, - NEONScalar3SameFPMask = NEONScalar3SameMask | 0x00800000, - NEON_FACGE_scalar = NEON_Q | NEONScalar | NEON_FACGE, - NEON_FACGT_scalar = NEON_Q | NEONScalar | NEON_FACGT, - NEON_FCMEQ_scalar = NEON_Q | NEONScalar | NEON_FCMEQ, - NEON_FCMGE_scalar = NEON_Q | NEONScalar | NEON_FCMGE, - NEON_FCMGT_scalar = NEON_Q | NEONScalar | NEON_FCMGT, - NEON_FMULX_scalar = NEON_Q | NEONScalar | NEON_FMULX, - NEON_FRECPS_scalar = NEON_Q | NEONScalar | NEON_FRECPS, - NEON_FRSQRTS_scalar = NEON_Q | NEONScalar | NEON_FRSQRTS, - NEON_FABD_scalar = NEON_Q | NEONScalar | NEON_FABD -}; - -// NEON scalar instructions with three different-type operands. -enum NEONScalar3DiffOp { - NEONScalar3DiffFixed = 0x5E200000, - NEONScalar3DiffFMask = 0xDF200C00, - NEONScalar3DiffMask = NEON_Q | NEONScalar | NEON3DifferentMask, - NEON_SQDMLAL_scalar = NEON_Q | NEONScalar | NEON_SQDMLAL, - NEON_SQDMLSL_scalar = NEON_Q | NEONScalar | NEON_SQDMLSL, - NEON_SQDMULL_scalar = NEON_Q | NEONScalar | NEON_SQDMULL -}; - -// NEON scalar instructions with indexed element operand. -enum NEONScalarByIndexedElementOp { - NEONScalarByIndexedElementFixed = 0x5F000000, - NEONScalarByIndexedElementFMask = 0xDF000400, - NEONScalarByIndexedElementMask = 0xFF00F400, - NEON_SQDMLAL_byelement_scalar = NEON_Q | NEONScalar | NEON_SQDMLAL_byelement, - NEON_SQDMLSL_byelement_scalar = NEON_Q | NEONScalar | NEON_SQDMLSL_byelement, - NEON_SQDMULL_byelement_scalar = NEON_Q | NEONScalar | NEON_SQDMULL_byelement, - NEON_SQDMULH_byelement_scalar = NEON_Q | NEONScalar | NEON_SQDMULH_byelement, - NEON_SQRDMULH_byelement_scalar - = NEON_Q | NEONScalar | NEON_SQRDMULH_byelement, - - // Floating point instructions. - NEONScalarByIndexedElementFPFixed - = NEONScalarByIndexedElementFixed | 0x00800000, - NEONScalarByIndexedElementFPMask - = NEONScalarByIndexedElementMask | 0x00800000, - NEON_FMLA_byelement_scalar = NEON_Q | NEONScalar | NEON_FMLA_byelement, - NEON_FMLS_byelement_scalar = NEON_Q | NEONScalar | NEON_FMLS_byelement, - NEON_FMUL_byelement_scalar = NEON_Q | NEONScalar | NEON_FMUL_byelement, - NEON_FMULX_byelement_scalar = NEON_Q | NEONScalar | NEON_FMULX_byelement -}; - -// NEON scalar register copy. -enum NEONScalarCopyOp { - NEONScalarCopyFixed = 0x5E000400, - NEONScalarCopyFMask = 0xDFE08400, - NEONScalarCopyMask = 0xFFE0FC00, - NEON_DUP_ELEMENT_scalar = NEON_Q | NEONScalar | NEON_DUP_ELEMENT -}; - -// NEON scalar pairwise instructions. -enum NEONScalarPairwiseOp { - NEONScalarPairwiseFixed = 0x5E300800, - NEONScalarPairwiseFMask = 0xDF3E0C00, - NEONScalarPairwiseMask = 0xFFB1F800, - NEON_ADDP_scalar = NEONScalarPairwiseFixed | 0x0081B000, - NEON_FMAXNMP_scalar = NEONScalarPairwiseFixed | 0x2000C000, - NEON_FMINNMP_scalar = NEONScalarPairwiseFixed | 0x2080C000, - NEON_FADDP_scalar = NEONScalarPairwiseFixed | 0x2000D000, - NEON_FMAXP_scalar = NEONScalarPairwiseFixed | 0x2000F000, - NEON_FMINP_scalar = NEONScalarPairwiseFixed | 0x2080F000 -}; - -// NEON scalar shift immediate. -enum NEONScalarShiftImmediateOp { - NEONScalarShiftImmediateFixed = 0x5F000400, - NEONScalarShiftImmediateFMask = 0xDF800400, - NEONScalarShiftImmediateMask = 0xFF80FC00, - NEON_SHL_scalar = NEON_Q | NEONScalar | NEON_SHL, - NEON_SLI_scalar = NEON_Q | NEONScalar | NEON_SLI, - NEON_SRI_scalar = NEON_Q | NEONScalar | NEON_SRI, - NEON_SSHR_scalar = NEON_Q | NEONScalar | NEON_SSHR, - NEON_USHR_scalar = NEON_Q | NEONScalar | NEON_USHR, - NEON_SRSHR_scalar = NEON_Q | NEONScalar | NEON_SRSHR, - NEON_URSHR_scalar = NEON_Q | NEONScalar | NEON_URSHR, - NEON_SSRA_scalar = NEON_Q | NEONScalar | NEON_SSRA, - NEON_USRA_scalar = NEON_Q | NEONScalar | NEON_USRA, - NEON_SRSRA_scalar = NEON_Q | NEONScalar | NEON_SRSRA, - NEON_URSRA_scalar = NEON_Q | NEONScalar | NEON_URSRA, - NEON_UQSHRN_scalar = NEON_Q | NEONScalar | NEON_UQSHRN, - NEON_UQRSHRN_scalar = NEON_Q | NEONScalar | NEON_UQRSHRN, - NEON_SQSHRN_scalar = NEON_Q | NEONScalar | NEON_SQSHRN, - NEON_SQRSHRN_scalar = NEON_Q | NEONScalar | NEON_SQRSHRN, - NEON_SQSHRUN_scalar = NEON_Q | NEONScalar | NEON_SQSHRUN, - NEON_SQRSHRUN_scalar = NEON_Q | NEONScalar | NEON_SQRSHRUN, - NEON_SQSHLU_scalar = NEON_Q | NEONScalar | NEON_SQSHLU, - NEON_SQSHL_imm_scalar = NEON_Q | NEONScalar | NEON_SQSHL_imm, - NEON_UQSHL_imm_scalar = NEON_Q | NEONScalar | NEON_UQSHL_imm, - NEON_SCVTF_imm_scalar = NEON_Q | NEONScalar | NEON_SCVTF_imm, - NEON_UCVTF_imm_scalar = NEON_Q | NEONScalar | NEON_UCVTF_imm, - NEON_FCVTZS_imm_scalar = NEON_Q | NEONScalar | NEON_FCVTZS_imm, - NEON_FCVTZU_imm_scalar = NEON_Q | NEONScalar | NEON_FCVTZU_imm -}; - -// Unimplemented and unallocated instructions. These are defined to make fixed -// bit assertion easier. -enum UnimplementedOp { - UnimplementedFixed = 0x00000000, - UnimplementedFMask = 0x00000000 -}; - -enum UnallocatedOp { - UnallocatedFixed = 0x00000000, - UnallocatedFMask = 0x00000000 -}; - -} // namespace vixl - -#endif // VIXL_A64_CONSTANTS_A64_H_ diff --git a/disas/libvixl/vixl/a64/cpu-a64.h b/disas/libvixl/vixl/a64/cpu-a64.h deleted file mode 100644 index cdf09a6af1..0000000000 --- a/disas/libvixl/vixl/a64/cpu-a64.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2014, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#ifndef VIXL_CPU_A64_H -#define VIXL_CPU_A64_H - -#include "vixl/globals.h" -#include "vixl/a64/instructions-a64.h" - -namespace vixl { - -class CPU { - public: - // Initialise CPU support. - static void SetUp(); - - // Ensures the data at a given address and with a given size is the same for - // the I and D caches. I and D caches are not automatically coherent on ARM - // so this operation is required before any dynamically generated code can - // safely run. - static void EnsureIAndDCacheCoherency(void *address, size_t length); - - // Handle tagged pointers. - template - static T SetPointerTag(T pointer, uint64_t tag) { - VIXL_ASSERT(is_uintn(kAddressTagWidth, tag)); - - // Use C-style casts to get static_cast behaviour for integral types (T), - // and reinterpret_cast behaviour for other types. - - uint64_t raw = (uint64_t)pointer; - VIXL_STATIC_ASSERT(sizeof(pointer) == sizeof(raw)); - - raw = (raw & ~kAddressTagMask) | (tag << kAddressTagOffset); - return (T)raw; - } - - template - static uint64_t GetPointerTag(T pointer) { - // Use C-style casts to get static_cast behaviour for integral types (T), - // and reinterpret_cast behaviour for other types. - - uint64_t raw = (uint64_t)pointer; - VIXL_STATIC_ASSERT(sizeof(pointer) == sizeof(raw)); - - return (raw & kAddressTagMask) >> kAddressTagOffset; - } - - private: - // Return the content of the cache type register. - static uint32_t GetCacheType(); - - // I and D cache line size in bytes. - static unsigned icache_line_size_; - static unsigned dcache_line_size_; -}; - -} // namespace vixl - -#endif // VIXL_CPU_A64_H diff --git a/disas/libvixl/vixl/a64/decoder-a64.cc b/disas/libvixl/vixl/a64/decoder-a64.cc deleted file mode 100644 index 5ba2d3ce04..0000000000 --- a/disas/libvixl/vixl/a64/decoder-a64.cc +++ /dev/null @@ -1,877 +0,0 @@ -// Copyright 2014, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#include "vixl/globals.h" -#include "vixl/utils.h" -#include "vixl/a64/decoder-a64.h" - -namespace vixl { - -void Decoder::DecodeInstruction(const Instruction *instr) { - if (instr->Bits(28, 27) == 0) { - VisitUnallocated(instr); - } else { - switch (instr->Bits(27, 24)) { - // 0: PC relative addressing. - case 0x0: DecodePCRelAddressing(instr); break; - - // 1: Add/sub immediate. - case 0x1: DecodeAddSubImmediate(instr); break; - - // A: Logical shifted register. - // Add/sub with carry. - // Conditional compare register. - // Conditional compare immediate. - // Conditional select. - // Data processing 1 source. - // Data processing 2 source. - // B: Add/sub shifted register. - // Add/sub extended register. - // Data processing 3 source. - case 0xA: - case 0xB: DecodeDataProcessing(instr); break; - - // 2: Logical immediate. - // Move wide immediate. - case 0x2: DecodeLogical(instr); break; - - // 3: Bitfield. - // Extract. - case 0x3: DecodeBitfieldExtract(instr); break; - - // 4: Unconditional branch immediate. - // Exception generation. - // Compare and branch immediate. - // 5: Compare and branch immediate. - // Conditional branch. - // System. - // 6,7: Unconditional branch. - // Test and branch immediate. - case 0x4: - case 0x5: - case 0x6: - case 0x7: DecodeBranchSystemException(instr); break; - - // 8,9: Load/store register pair post-index. - // Load register literal. - // Load/store register unscaled immediate. - // Load/store register immediate post-index. - // Load/store register immediate pre-index. - // Load/store register offset. - // Load/store exclusive. - // C,D: Load/store register pair offset. - // Load/store register pair pre-index. - // Load/store register unsigned immediate. - // Advanced SIMD. - case 0x8: - case 0x9: - case 0xC: - case 0xD: DecodeLoadStore(instr); break; - - // E: FP fixed point conversion. - // FP integer conversion. - // FP data processing 1 source. - // FP compare. - // FP immediate. - // FP data processing 2 source. - // FP conditional compare. - // FP conditional select. - // Advanced SIMD. - // F: FP data processing 3 source. - // Advanced SIMD. - case 0xE: - case 0xF: DecodeFP(instr); break; - } - } -} - -void Decoder::AppendVisitor(DecoderVisitor* new_visitor) { - visitors_.push_back(new_visitor); -} - - -void Decoder::PrependVisitor(DecoderVisitor* new_visitor) { - visitors_.push_front(new_visitor); -} - - -void Decoder::InsertVisitorBefore(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor) { - std::list::iterator it; - for (it = visitors_.begin(); it != visitors_.end(); it++) { - if (*it == registered_visitor) { - visitors_.insert(it, new_visitor); - return; - } - } - // We reached the end of the list. The last element must be - // registered_visitor. - VIXL_ASSERT(*it == registered_visitor); - visitors_.insert(it, new_visitor); -} - - -void Decoder::InsertVisitorAfter(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor) { - std::list::iterator it; - for (it = visitors_.begin(); it != visitors_.end(); it++) { - if (*it == registered_visitor) { - it++; - visitors_.insert(it, new_visitor); - return; - } - } - // We reached the end of the list. The last element must be - // registered_visitor. - VIXL_ASSERT(*it == registered_visitor); - visitors_.push_back(new_visitor); -} - - -void Decoder::RemoveVisitor(DecoderVisitor* visitor) { - visitors_.remove(visitor); -} - - -void Decoder::DecodePCRelAddressing(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(27, 24) == 0x0); - // We know bit 28 is set, as = 0 is filtered out at the top level - // decode. - VIXL_ASSERT(instr->Bit(28) == 0x1); - VisitPCRelAddressing(instr); -} - - -void Decoder::DecodeBranchSystemException(const Instruction* instr) { - VIXL_ASSERT((instr->Bits(27, 24) == 0x4) || - (instr->Bits(27, 24) == 0x5) || - (instr->Bits(27, 24) == 0x6) || - (instr->Bits(27, 24) == 0x7) ); - - switch (instr->Bits(31, 29)) { - case 0: - case 4: { - VisitUnconditionalBranch(instr); - break; - } - case 1: - case 5: { - if (instr->Bit(25) == 0) { - VisitCompareBranch(instr); - } else { - VisitTestBranch(instr); - } - break; - } - case 2: { - if (instr->Bit(25) == 0) { - if ((instr->Bit(24) == 0x1) || - (instr->Mask(0x01000010) == 0x00000010)) { - VisitUnallocated(instr); - } else { - VisitConditionalBranch(instr); - } - } else { - VisitUnallocated(instr); - } - break; - } - case 6: { - if (instr->Bit(25) == 0) { - if (instr->Bit(24) == 0) { - if ((instr->Bits(4, 2) != 0) || - (instr->Mask(0x00E0001D) == 0x00200001) || - (instr->Mask(0x00E0001D) == 0x00400001) || - (instr->Mask(0x00E0001E) == 0x00200002) || - (instr->Mask(0x00E0001E) == 0x00400002) || - (instr->Mask(0x00E0001C) == 0x00600000) || - (instr->Mask(0x00E0001C) == 0x00800000) || - (instr->Mask(0x00E0001F) == 0x00A00000) || - (instr->Mask(0x00C0001C) == 0x00C00000)) { - VisitUnallocated(instr); - } else { - VisitException(instr); - } - } else { - if (instr->Bits(23, 22) == 0) { - const Instr masked_003FF0E0 = instr->Mask(0x003FF0E0); - if ((instr->Bits(21, 19) == 0x4) || - (masked_003FF0E0 == 0x00033000) || - (masked_003FF0E0 == 0x003FF020) || - (masked_003FF0E0 == 0x003FF060) || - (masked_003FF0E0 == 0x003FF0E0) || - (instr->Mask(0x00388000) == 0x00008000) || - (instr->Mask(0x0038E000) == 0x00000000) || - (instr->Mask(0x0039E000) == 0x00002000) || - (instr->Mask(0x003AE000) == 0x00002000) || - (instr->Mask(0x003CE000) == 0x00042000) || - (instr->Mask(0x003FFFC0) == 0x000320C0) || - (instr->Mask(0x003FF100) == 0x00032100) || - (instr->Mask(0x003FF200) == 0x00032200) || - (instr->Mask(0x003FF400) == 0x00032400) || - (instr->Mask(0x003FF800) == 0x00032800) || - (instr->Mask(0x0038F000) == 0x00005000) || - (instr->Mask(0x0038E000) == 0x00006000)) { - VisitUnallocated(instr); - } else { - VisitSystem(instr); - } - } else { - VisitUnallocated(instr); - } - } - } else { - if ((instr->Bit(24) == 0x1) || - (instr->Bits(20, 16) != 0x1F) || - (instr->Bits(15, 10) != 0) || - (instr->Bits(4, 0) != 0) || - (instr->Bits(24, 21) == 0x3) || - (instr->Bits(24, 22) == 0x3)) { - VisitUnallocated(instr); - } else { - VisitUnconditionalBranchToRegister(instr); - } - } - break; - } - case 3: - case 7: { - VisitUnallocated(instr); - break; - } - } -} - - -void Decoder::DecodeLoadStore(const Instruction* instr) { - VIXL_ASSERT((instr->Bits(27, 24) == 0x8) || - (instr->Bits(27, 24) == 0x9) || - (instr->Bits(27, 24) == 0xC) || - (instr->Bits(27, 24) == 0xD) ); - // TODO(all): rearrange the tree to integrate this branch. - if ((instr->Bit(28) == 0) && (instr->Bit(29) == 0) && (instr->Bit(26) == 1)) { - DecodeNEONLoadStore(instr); - return; - } - - if (instr->Bit(24) == 0) { - if (instr->Bit(28) == 0) { - if (instr->Bit(29) == 0) { - if (instr->Bit(26) == 0) { - VisitLoadStoreExclusive(instr); - } else { - VIXL_UNREACHABLE(); - } - } else { - if ((instr->Bits(31, 30) == 0x3) || - (instr->Mask(0xC4400000) == 0x40000000)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(23) == 0) { - if (instr->Mask(0xC4400000) == 0xC0400000) { - VisitUnallocated(instr); - } else { - VisitLoadStorePairNonTemporal(instr); - } - } else { - VisitLoadStorePairPostIndex(instr); - } - } - } - } else { - if (instr->Bit(29) == 0) { - if (instr->Mask(0xC4000000) == 0xC4000000) { - VisitUnallocated(instr); - } else { - VisitLoadLiteral(instr); - } - } else { - if ((instr->Mask(0x84C00000) == 0x80C00000) || - (instr->Mask(0x44800000) == 0x44800000) || - (instr->Mask(0x84800000) == 0x84800000)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(21) == 0) { - switch (instr->Bits(11, 10)) { - case 0: { - VisitLoadStoreUnscaledOffset(instr); - break; - } - case 1: { - if (instr->Mask(0xC4C00000) == 0xC0800000) { - VisitUnallocated(instr); - } else { - VisitLoadStorePostIndex(instr); - } - break; - } - case 2: { - // TODO: VisitLoadStoreRegisterOffsetUnpriv. - VisitUnimplemented(instr); - break; - } - case 3: { - if (instr->Mask(0xC4C00000) == 0xC0800000) { - VisitUnallocated(instr); - } else { - VisitLoadStorePreIndex(instr); - } - break; - } - } - } else { - if (instr->Bits(11, 10) == 0x2) { - if (instr->Bit(14) == 0) { - VisitUnallocated(instr); - } else { - VisitLoadStoreRegisterOffset(instr); - } - } else { - VisitUnallocated(instr); - } - } - } - } - } - } else { - if (instr->Bit(28) == 0) { - if (instr->Bit(29) == 0) { - VisitUnallocated(instr); - } else { - if ((instr->Bits(31, 30) == 0x3) || - (instr->Mask(0xC4400000) == 0x40000000)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(23) == 0) { - VisitLoadStorePairOffset(instr); - } else { - VisitLoadStorePairPreIndex(instr); - } - } - } - } else { - if (instr->Bit(29) == 0) { - VisitUnallocated(instr); - } else { - if ((instr->Mask(0x84C00000) == 0x80C00000) || - (instr->Mask(0x44800000) == 0x44800000) || - (instr->Mask(0x84800000) == 0x84800000)) { - VisitUnallocated(instr); - } else { - VisitLoadStoreUnsignedOffset(instr); - } - } - } - } -} - - -void Decoder::DecodeLogical(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(27, 24) == 0x2); - - if (instr->Mask(0x80400000) == 0x00400000) { - VisitUnallocated(instr); - } else { - if (instr->Bit(23) == 0) { - VisitLogicalImmediate(instr); - } else { - if (instr->Bits(30, 29) == 0x1) { - VisitUnallocated(instr); - } else { - VisitMoveWideImmediate(instr); - } - } - } -} - - -void Decoder::DecodeBitfieldExtract(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(27, 24) == 0x3); - - if ((instr->Mask(0x80400000) == 0x80000000) || - (instr->Mask(0x80400000) == 0x00400000) || - (instr->Mask(0x80008000) == 0x00008000)) { - VisitUnallocated(instr); - } else if (instr->Bit(23) == 0) { - if ((instr->Mask(0x80200000) == 0x00200000) || - (instr->Mask(0x60000000) == 0x60000000)) { - VisitUnallocated(instr); - } else { - VisitBitfield(instr); - } - } else { - if ((instr->Mask(0x60200000) == 0x00200000) || - (instr->Mask(0x60000000) != 0x00000000)) { - VisitUnallocated(instr); - } else { - VisitExtract(instr); - } - } -} - - -void Decoder::DecodeAddSubImmediate(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(27, 24) == 0x1); - if (instr->Bit(23) == 1) { - VisitUnallocated(instr); - } else { - VisitAddSubImmediate(instr); - } -} - - -void Decoder::DecodeDataProcessing(const Instruction* instr) { - VIXL_ASSERT((instr->Bits(27, 24) == 0xA) || - (instr->Bits(27, 24) == 0xB)); - - if (instr->Bit(24) == 0) { - if (instr->Bit(28) == 0) { - if (instr->Mask(0x80008000) == 0x00008000) { - VisitUnallocated(instr); - } else { - VisitLogicalShifted(instr); - } - } else { - switch (instr->Bits(23, 21)) { - case 0: { - if (instr->Mask(0x0000FC00) != 0) { - VisitUnallocated(instr); - } else { - VisitAddSubWithCarry(instr); - } - break; - } - case 2: { - if ((instr->Bit(29) == 0) || - (instr->Mask(0x00000410) != 0)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(11) == 0) { - VisitConditionalCompareRegister(instr); - } else { - VisitConditionalCompareImmediate(instr); - } - } - break; - } - case 4: { - if (instr->Mask(0x20000800) != 0x00000000) { - VisitUnallocated(instr); - } else { - VisitConditionalSelect(instr); - } - break; - } - case 6: { - if (instr->Bit(29) == 0x1) { - VisitUnallocated(instr); - VIXL_FALLTHROUGH(); - } else { - if (instr->Bit(30) == 0) { - if ((instr->Bit(15) == 0x1) || - (instr->Bits(15, 11) == 0) || - (instr->Bits(15, 12) == 0x1) || - (instr->Bits(15, 12) == 0x3) || - (instr->Bits(15, 13) == 0x3) || - (instr->Mask(0x8000EC00) == 0x00004C00) || - (instr->Mask(0x8000E800) == 0x80004000) || - (instr->Mask(0x8000E400) == 0x80004000)) { - VisitUnallocated(instr); - } else { - VisitDataProcessing2Source(instr); - } - } else { - if ((instr->Bit(13) == 1) || - (instr->Bits(20, 16) != 0) || - (instr->Bits(15, 14) != 0) || - (instr->Mask(0xA01FFC00) == 0x00000C00) || - (instr->Mask(0x201FF800) == 0x00001800)) { - VisitUnallocated(instr); - } else { - VisitDataProcessing1Source(instr); - } - } - break; - } - } - case 1: - case 3: - case 5: - case 7: VisitUnallocated(instr); break; - } - } - } else { - if (instr->Bit(28) == 0) { - if (instr->Bit(21) == 0) { - if ((instr->Bits(23, 22) == 0x3) || - (instr->Mask(0x80008000) == 0x00008000)) { - VisitUnallocated(instr); - } else { - VisitAddSubShifted(instr); - } - } else { - if ((instr->Mask(0x00C00000) != 0x00000000) || - (instr->Mask(0x00001400) == 0x00001400) || - (instr->Mask(0x00001800) == 0x00001800)) { - VisitUnallocated(instr); - } else { - VisitAddSubExtended(instr); - } - } - } else { - if ((instr->Bit(30) == 0x1) || - (instr->Bits(30, 29) == 0x1) || - (instr->Mask(0xE0600000) == 0x00200000) || - (instr->Mask(0xE0608000) == 0x00400000) || - (instr->Mask(0x60608000) == 0x00408000) || - (instr->Mask(0x60E00000) == 0x00E00000) || - (instr->Mask(0x60E00000) == 0x00800000) || - (instr->Mask(0x60E00000) == 0x00600000)) { - VisitUnallocated(instr); - } else { - VisitDataProcessing3Source(instr); - } - } - } -} - - -void Decoder::DecodeFP(const Instruction* instr) { - VIXL_ASSERT((instr->Bits(27, 24) == 0xE) || - (instr->Bits(27, 24) == 0xF)); - if (instr->Bit(28) == 0) { - DecodeNEONVectorDataProcessing(instr); - } else { - if (instr->Bits(31, 30) == 0x3) { - VisitUnallocated(instr); - } else if (instr->Bits(31, 30) == 0x1) { - DecodeNEONScalarDataProcessing(instr); - } else { - if (instr->Bit(29) == 0) { - if (instr->Bit(24) == 0) { - if (instr->Bit(21) == 0) { - if ((instr->Bit(23) == 1) || - (instr->Bit(18) == 1) || - (instr->Mask(0x80008000) == 0x00000000) || - (instr->Mask(0x000E0000) == 0x00000000) || - (instr->Mask(0x000E0000) == 0x000A0000) || - (instr->Mask(0x00160000) == 0x00000000) || - (instr->Mask(0x00160000) == 0x00120000)) { - VisitUnallocated(instr); - } else { - VisitFPFixedPointConvert(instr); - } - } else { - if (instr->Bits(15, 10) == 32) { - VisitUnallocated(instr); - } else if (instr->Bits(15, 10) == 0) { - if ((instr->Bits(23, 22) == 0x3) || - (instr->Mask(0x000E0000) == 0x000A0000) || - (instr->Mask(0x000E0000) == 0x000C0000) || - (instr->Mask(0x00160000) == 0x00120000) || - (instr->Mask(0x00160000) == 0x00140000) || - (instr->Mask(0x20C40000) == 0x00800000) || - (instr->Mask(0x20C60000) == 0x00840000) || - (instr->Mask(0xA0C60000) == 0x80060000) || - (instr->Mask(0xA0C60000) == 0x00860000) || - (instr->Mask(0xA0C60000) == 0x00460000) || - (instr->Mask(0xA0CE0000) == 0x80860000) || - (instr->Mask(0xA0CE0000) == 0x804E0000) || - (instr->Mask(0xA0CE0000) == 0x000E0000) || - (instr->Mask(0xA0D60000) == 0x00160000) || - (instr->Mask(0xA0D60000) == 0x80560000) || - (instr->Mask(0xA0D60000) == 0x80960000)) { - VisitUnallocated(instr); - } else { - VisitFPIntegerConvert(instr); - } - } else if (instr->Bits(14, 10) == 16) { - const Instr masked_A0DF8000 = instr->Mask(0xA0DF8000); - if ((instr->Mask(0x80180000) != 0) || - (masked_A0DF8000 == 0x00020000) || - (masked_A0DF8000 == 0x00030000) || - (masked_A0DF8000 == 0x00068000) || - (masked_A0DF8000 == 0x00428000) || - (masked_A0DF8000 == 0x00430000) || - (masked_A0DF8000 == 0x00468000) || - (instr->Mask(0xA0D80000) == 0x00800000) || - (instr->Mask(0xA0DE0000) == 0x00C00000) || - (instr->Mask(0xA0DF0000) == 0x00C30000) || - (instr->Mask(0xA0DC0000) == 0x00C40000)) { - VisitUnallocated(instr); - } else { - VisitFPDataProcessing1Source(instr); - } - } else if (instr->Bits(13, 10) == 8) { - if ((instr->Bits(15, 14) != 0) || - (instr->Bits(2, 0) != 0) || - (instr->Mask(0x80800000) != 0x00000000)) { - VisitUnallocated(instr); - } else { - VisitFPCompare(instr); - } - } else if (instr->Bits(12, 10) == 4) { - if ((instr->Bits(9, 5) != 0) || - (instr->Mask(0x80800000) != 0x00000000)) { - VisitUnallocated(instr); - } else { - VisitFPImmediate(instr); - } - } else { - if (instr->Mask(0x80800000) != 0x00000000) { - VisitUnallocated(instr); - } else { - switch (instr->Bits(11, 10)) { - case 1: { - VisitFPConditionalCompare(instr); - break; - } - case 2: { - if ((instr->Bits(15, 14) == 0x3) || - (instr->Mask(0x00009000) == 0x00009000) || - (instr->Mask(0x0000A000) == 0x0000A000)) { - VisitUnallocated(instr); - } else { - VisitFPDataProcessing2Source(instr); - } - break; - } - case 3: { - VisitFPConditionalSelect(instr); - break; - } - default: VIXL_UNREACHABLE(); - } - } - } - } - } else { - // Bit 30 == 1 has been handled earlier. - VIXL_ASSERT(instr->Bit(30) == 0); - if (instr->Mask(0xA0800000) != 0) { - VisitUnallocated(instr); - } else { - VisitFPDataProcessing3Source(instr); - } - } - } else { - VisitUnallocated(instr); - } - } - } -} - - -void Decoder::DecodeNEONLoadStore(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(29, 25) == 0x6); - if (instr->Bit(31) == 0) { - if ((instr->Bit(24) == 0) && (instr->Bit(21) == 1)) { - VisitUnallocated(instr); - return; - } - - if (instr->Bit(23) == 0) { - if (instr->Bits(20, 16) == 0) { - if (instr->Bit(24) == 0) { - VisitNEONLoadStoreMultiStruct(instr); - } else { - VisitNEONLoadStoreSingleStruct(instr); - } - } else { - VisitUnallocated(instr); - } - } else { - if (instr->Bit(24) == 0) { - VisitNEONLoadStoreMultiStructPostIndex(instr); - } else { - VisitNEONLoadStoreSingleStructPostIndex(instr); - } - } - } else { - VisitUnallocated(instr); - } -} - - -void Decoder::DecodeNEONVectorDataProcessing(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(28, 25) == 0x7); - if (instr->Bit(31) == 0) { - if (instr->Bit(24) == 0) { - if (instr->Bit(21) == 0) { - if (instr->Bit(15) == 0) { - if (instr->Bit(10) == 0) { - if (instr->Bit(29) == 0) { - if (instr->Bit(11) == 0) { - VisitNEONTable(instr); - } else { - VisitNEONPerm(instr); - } - } else { - VisitNEONExtract(instr); - } - } else { - if (instr->Bits(23, 22) == 0) { - VisitNEONCopy(instr); - } else { - VisitUnallocated(instr); - } - } - } else { - VisitUnallocated(instr); - } - } else { - if (instr->Bit(10) == 0) { - if (instr->Bit(11) == 0) { - VisitNEON3Different(instr); - } else { - if (instr->Bits(18, 17) == 0) { - if (instr->Bit(20) == 0) { - if (instr->Bit(19) == 0) { - VisitNEON2RegMisc(instr); - } else { - if (instr->Bits(30, 29) == 0x2) { - VisitCryptoAES(instr); - } else { - VisitUnallocated(instr); - } - } - } else { - if (instr->Bit(19) == 0) { - VisitNEONAcrossLanes(instr); - } else { - VisitUnallocated(instr); - } - } - } else { - VisitUnallocated(instr); - } - } - } else { - VisitNEON3Same(instr); - } - } - } else { - if (instr->Bit(10) == 0) { - VisitNEONByIndexedElement(instr); - } else { - if (instr->Bit(23) == 0) { - if (instr->Bits(22, 19) == 0) { - VisitNEONModifiedImmediate(instr); - } else { - VisitNEONShiftImmediate(instr); - } - } else { - VisitUnallocated(instr); - } - } - } - } else { - VisitUnallocated(instr); - } -} - - -void Decoder::DecodeNEONScalarDataProcessing(const Instruction* instr) { - VIXL_ASSERT(instr->Bits(28, 25) == 0xF); - if (instr->Bit(24) == 0) { - if (instr->Bit(21) == 0) { - if (instr->Bit(15) == 0) { - if (instr->Bit(10) == 0) { - if (instr->Bit(29) == 0) { - if (instr->Bit(11) == 0) { - VisitCrypto3RegSHA(instr); - } else { - VisitUnallocated(instr); - } - } else { - VisitUnallocated(instr); - } - } else { - if (instr->Bits(23, 22) == 0) { - VisitNEONScalarCopy(instr); - } else { - VisitUnallocated(instr); - } - } - } else { - VisitUnallocated(instr); - } - } else { - if (instr->Bit(10) == 0) { - if (instr->Bit(11) == 0) { - VisitNEONScalar3Diff(instr); - } else { - if (instr->Bits(18, 17) == 0) { - if (instr->Bit(20) == 0) { - if (instr->Bit(19) == 0) { - VisitNEONScalar2RegMisc(instr); - } else { - if (instr->Bit(29) == 0) { - VisitCrypto2RegSHA(instr); - } else { - VisitUnallocated(instr); - } - } - } else { - if (instr->Bit(19) == 0) { - VisitNEONScalarPairwise(instr); - } else { - VisitUnallocated(instr); - } - } - } else { - VisitUnallocated(instr); - } - } - } else { - VisitNEONScalar3Same(instr); - } - } - } else { - if (instr->Bit(10) == 0) { - VisitNEONScalarByIndexedElement(instr); - } else { - if (instr->Bit(23) == 0) { - VisitNEONScalarShiftImmediate(instr); - } else { - VisitUnallocated(instr); - } - } - } -} - - -#define DEFINE_VISITOR_CALLERS(A) \ - void Decoder::Visit##A(const Instruction *instr) { \ - VIXL_ASSERT(instr->Mask(A##FMask) == A##Fixed); \ - std::list::iterator it; \ - for (it = visitors_.begin(); it != visitors_.end(); it++) { \ - (*it)->Visit##A(instr); \ - } \ - } -VISITOR_LIST(DEFINE_VISITOR_CALLERS) -#undef DEFINE_VISITOR_CALLERS -} // namespace vixl diff --git a/disas/libvixl/vixl/a64/decoder-a64.h b/disas/libvixl/vixl/a64/decoder-a64.h deleted file mode 100644 index b3f04f68fc..0000000000 --- a/disas/libvixl/vixl/a64/decoder-a64.h +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2014, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#ifndef VIXL_A64_DECODER_A64_H_ -#define VIXL_A64_DECODER_A64_H_ - -#include - -#include "vixl/globals.h" -#include "vixl/a64/instructions-a64.h" - - -// List macro containing all visitors needed by the decoder class. - -#define VISITOR_LIST_THAT_RETURN(V) \ - V(PCRelAddressing) \ - V(AddSubImmediate) \ - V(LogicalImmediate) \ - V(MoveWideImmediate) \ - V(Bitfield) \ - V(Extract) \ - V(UnconditionalBranch) \ - V(UnconditionalBranchToRegister) \ - V(CompareBranch) \ - V(TestBranch) \ - V(ConditionalBranch) \ - V(System) \ - V(Exception) \ - V(LoadStorePairPostIndex) \ - V(LoadStorePairOffset) \ - V(LoadStorePairPreIndex) \ - V(LoadStorePairNonTemporal) \ - V(LoadLiteral) \ - V(LoadStoreUnscaledOffset) \ - V(LoadStorePostIndex) \ - V(LoadStorePreIndex) \ - V(LoadStoreRegisterOffset) \ - V(LoadStoreUnsignedOffset) \ - V(LoadStoreExclusive) \ - V(LogicalShifted) \ - V(AddSubShifted) \ - V(AddSubExtended) \ - V(AddSubWithCarry) \ - V(ConditionalCompareRegister) \ - V(ConditionalCompareImmediate) \ - V(ConditionalSelect) \ - V(DataProcessing1Source) \ - V(DataProcessing2Source) \ - V(DataProcessing3Source) \ - V(FPCompare) \ - V(FPConditionalCompare) \ - V(FPConditionalSelect) \ - V(FPImmediate) \ - V(FPDataProcessing1Source) \ - V(FPDataProcessing2Source) \ - V(FPDataProcessing3Source) \ - V(FPIntegerConvert) \ - V(FPFixedPointConvert) \ - V(Crypto2RegSHA) \ - V(Crypto3RegSHA) \ - V(CryptoAES) \ - V(NEON2RegMisc) \ - V(NEON3Different) \ - V(NEON3Same) \ - V(NEONAcrossLanes) \ - V(NEONByIndexedElement) \ - V(NEONCopy) \ - V(NEONExtract) \ - V(NEONLoadStoreMultiStruct) \ - V(NEONLoadStoreMultiStructPostIndex) \ - V(NEONLoadStoreSingleStruct) \ - V(NEONLoadStoreSingleStructPostIndex) \ - V(NEONModifiedImmediate) \ - V(NEONScalar2RegMisc) \ - V(NEONScalar3Diff) \ - V(NEONScalar3Same) \ - V(NEONScalarByIndexedElement) \ - V(NEONScalarCopy) \ - V(NEONScalarPairwise) \ - V(NEONScalarShiftImmediate) \ - V(NEONShiftImmediate) \ - V(NEONTable) \ - V(NEONPerm) \ - -#define VISITOR_LIST_THAT_DONT_RETURN(V) \ - V(Unallocated) \ - V(Unimplemented) \ - -#define VISITOR_LIST(V) \ - VISITOR_LIST_THAT_RETURN(V) \ - VISITOR_LIST_THAT_DONT_RETURN(V) \ - -namespace vixl { - -// The Visitor interface. Disassembler and simulator (and other tools) -// must provide implementations for all of these functions. -class DecoderVisitor { - public: - enum VisitorConstness { - kConstVisitor, - kNonConstVisitor - }; - explicit DecoderVisitor(VisitorConstness constness = kConstVisitor) - : constness_(constness) {} - - virtual ~DecoderVisitor() {} - - #define DECLARE(A) virtual void Visit##A(const Instruction* instr) = 0; - VISITOR_LIST(DECLARE) - #undef DECLARE - - bool IsConstVisitor() const { return constness_ == kConstVisitor; } - Instruction* MutableInstruction(const Instruction* instr) { - VIXL_ASSERT(!IsConstVisitor()); - return const_cast(instr); - } - - private: - const VisitorConstness constness_; -}; - - -class Decoder { - public: - Decoder() {} - - // Top-level wrappers around the actual decoding function. - void Decode(const Instruction* instr) { - std::list::iterator it; - for (it = visitors_.begin(); it != visitors_.end(); it++) { - VIXL_ASSERT((*it)->IsConstVisitor()); - } - DecodeInstruction(instr); - } - void Decode(Instruction* instr) { - DecodeInstruction(const_cast(instr)); - } - - // Register a new visitor class with the decoder. - // Decode() will call the corresponding visitor method from all registered - // visitor classes when decoding reaches the leaf node of the instruction - // decode tree. - // Visitors are called in order. - // A visitor can be registered multiple times. - // - // d.AppendVisitor(V1); - // d.AppendVisitor(V2); - // d.PrependVisitor(V2); - // d.AppendVisitor(V3); - // - // d.Decode(i); - // - // will call in order visitor methods in V2, V1, V2, V3. - void AppendVisitor(DecoderVisitor* visitor); - void PrependVisitor(DecoderVisitor* visitor); - // These helpers register `new_visitor` before or after the first instance of - // `registered_visiter` in the list. - // So if - // V1, V2, V1, V2 - // are registered in this order in the decoder, calls to - // d.InsertVisitorAfter(V3, V1); - // d.InsertVisitorBefore(V4, V2); - // will yield the order - // V1, V3, V4, V2, V1, V2 - // - // For more complex modifications of the order of registered visitors, one can - // directly access and modify the list of visitors via the `visitors()' - // accessor. - void InsertVisitorBefore(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor); - void InsertVisitorAfter(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor); - - // Remove all instances of a previously registered visitor class from the list - // of visitors stored by the decoder. - void RemoveVisitor(DecoderVisitor* visitor); - - #define DECLARE(A) void Visit##A(const Instruction* instr); - VISITOR_LIST(DECLARE) - #undef DECLARE - - - std::list* visitors() { return &visitors_; } - - private: - // Decodes an instruction and calls the visitor functions registered with the - // Decoder class. - void DecodeInstruction(const Instruction* instr); - - // Decode the PC relative addressing instruction, and call the corresponding - // visitors. - // On entry, instruction bits 27:24 = 0x0. - void DecodePCRelAddressing(const Instruction* instr); - - // Decode the add/subtract immediate instruction, and call the correspoding - // visitors. - // On entry, instruction bits 27:24 = 0x1. - void DecodeAddSubImmediate(const Instruction* instr); - - // Decode the branch, system command, and exception generation parts of - // the instruction tree, and call the corresponding visitors. - // On entry, instruction bits 27:24 = {0x4, 0x5, 0x6, 0x7}. - void DecodeBranchSystemException(const Instruction* instr); - - // Decode the load and store parts of the instruction tree, and call - // the corresponding visitors. - // On entry, instruction bits 27:24 = {0x8, 0x9, 0xC, 0xD}. - void DecodeLoadStore(const Instruction* instr); - - // Decode the logical immediate and move wide immediate parts of the - // instruction tree, and call the corresponding visitors. - // On entry, instruction bits 27:24 = 0x2. - void DecodeLogical(const Instruction* instr); - - // Decode the bitfield and extraction parts of the instruction tree, - // and call the corresponding visitors. - // On entry, instruction bits 27:24 = 0x3. - void DecodeBitfieldExtract(const Instruction* instr); - - // Decode the data processing parts of the instruction tree, and call the - // corresponding visitors. - // On entry, instruction bits 27:24 = {0x1, 0xA, 0xB}. - void DecodeDataProcessing(const Instruction* instr); - - // Decode the floating point parts of the instruction tree, and call the - // corresponding visitors. - // On entry, instruction bits 27:24 = {0xE, 0xF}. - void DecodeFP(const Instruction* instr); - - // Decode the Advanced SIMD (NEON) load/store part of the instruction tree, - // and call the corresponding visitors. - // On entry, instruction bits 29:25 = 0x6. - void DecodeNEONLoadStore(const Instruction* instr); - - // Decode the Advanced SIMD (NEON) vector data processing part of the - // instruction tree, and call the corresponding visitors. - // On entry, instruction bits 28:25 = 0x7. - void DecodeNEONVectorDataProcessing(const Instruction* instr); - - // Decode the Advanced SIMD (NEON) scalar data processing part of the - // instruction tree, and call the corresponding visitors. - // On entry, instruction bits 28:25 = 0xF. - void DecodeNEONScalarDataProcessing(const Instruction* instr); - - private: - // Visitors are registered in a list. - std::list visitors_; -}; - -} // namespace vixl - -#endif // VIXL_A64_DECODER_A64_H_ diff --git a/disas/libvixl/vixl/a64/disasm-a64.cc b/disas/libvixl/vixl/a64/disasm-a64.cc deleted file mode 100644 index f34d1d68da..0000000000 --- a/disas/libvixl/vixl/a64/disasm-a64.cc +++ /dev/null @@ -1,3495 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#include -#include "vixl/a64/disasm-a64.h" - -namespace vixl { - -Disassembler::Disassembler() { - buffer_size_ = 256; - buffer_ = reinterpret_cast(malloc(buffer_size_)); - buffer_pos_ = 0; - own_buffer_ = true; - code_address_offset_ = 0; -} - - -Disassembler::Disassembler(char* text_buffer, int buffer_size) { - buffer_size_ = buffer_size; - buffer_ = text_buffer; - buffer_pos_ = 0; - own_buffer_ = false; - code_address_offset_ = 0; -} - - -Disassembler::~Disassembler() { - if (own_buffer_) { - free(buffer_); - } -} - - -char* Disassembler::GetOutput() { - return buffer_; -} - - -void Disassembler::VisitAddSubImmediate(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool stack_op = (rd_is_zr || RnIsZROrSP(instr)) && - (instr->ImmAddSub() == 0) ? true : false; - const char *mnemonic = ""; - const char *form = "'Rds, 'Rns, 'IAddSub"; - const char *form_cmp = "'Rns, 'IAddSub"; - const char *form_mov = "'Rds, 'Rns"; - - switch (instr->Mask(AddSubImmediateMask)) { - case ADD_w_imm: - case ADD_x_imm: { - mnemonic = "add"; - if (stack_op) { - mnemonic = "mov"; - form = form_mov; - } - break; - } - case ADDS_w_imm: - case ADDS_x_imm: { - mnemonic = "adds"; - if (rd_is_zr) { - mnemonic = "cmn"; - form = form_cmp; - } - break; - } - case SUB_w_imm: - case SUB_x_imm: mnemonic = "sub"; break; - case SUBS_w_imm: - case SUBS_x_imm: { - mnemonic = "subs"; - if (rd_is_zr) { - mnemonic = "cmp"; - form = form_cmp; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitAddSubShifted(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm'NDP"; - const char *form_cmp = "'Rn, 'Rm'NDP"; - const char *form_neg = "'Rd, 'Rm'NDP"; - - switch (instr->Mask(AddSubShiftedMask)) { - case ADD_w_shift: - case ADD_x_shift: mnemonic = "add"; break; - case ADDS_w_shift: - case ADDS_x_shift: { - mnemonic = "adds"; - if (rd_is_zr) { - mnemonic = "cmn"; - form = form_cmp; - } - break; - } - case SUB_w_shift: - case SUB_x_shift: { - mnemonic = "sub"; - if (rn_is_zr) { - mnemonic = "neg"; - form = form_neg; - } - break; - } - case SUBS_w_shift: - case SUBS_x_shift: { - mnemonic = "subs"; - if (rd_is_zr) { - mnemonic = "cmp"; - form = form_cmp; - } else if (rn_is_zr) { - mnemonic = "negs"; - form = form_neg; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitAddSubExtended(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - const char *mnemonic = ""; - Extend mode = static_cast(instr->ExtendMode()); - const char *form = ((mode == UXTX) || (mode == SXTX)) ? - "'Rds, 'Rns, 'Xm'Ext" : "'Rds, 'Rns, 'Wm'Ext"; - const char *form_cmp = ((mode == UXTX) || (mode == SXTX)) ? - "'Rns, 'Xm'Ext" : "'Rns, 'Wm'Ext"; - - switch (instr->Mask(AddSubExtendedMask)) { - case ADD_w_ext: - case ADD_x_ext: mnemonic = "add"; break; - case ADDS_w_ext: - case ADDS_x_ext: { - mnemonic = "adds"; - if (rd_is_zr) { - mnemonic = "cmn"; - form = form_cmp; - } - break; - } - case SUB_w_ext: - case SUB_x_ext: mnemonic = "sub"; break; - case SUBS_w_ext: - case SUBS_x_ext: { - mnemonic = "subs"; - if (rd_is_zr) { - mnemonic = "cmp"; - form = form_cmp; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitAddSubWithCarry(const Instruction* instr) { - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm"; - const char *form_neg = "'Rd, 'Rm"; - - switch (instr->Mask(AddSubWithCarryMask)) { - case ADC_w: - case ADC_x: mnemonic = "adc"; break; - case ADCS_w: - case ADCS_x: mnemonic = "adcs"; break; - case SBC_w: - case SBC_x: { - mnemonic = "sbc"; - if (rn_is_zr) { - mnemonic = "ngc"; - form = form_neg; - } - break; - } - case SBCS_w: - case SBCS_x: { - mnemonic = "sbcs"; - if (rn_is_zr) { - mnemonic = "ngcs"; - form = form_neg; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLogicalImmediate(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rds, 'Rn, 'ITri"; - - if (instr->ImmLogical() == 0) { - // The immediate encoded in the instruction is not in the expected format. - Format(instr, "unallocated", "(LogicalImmediate)"); - return; - } - - switch (instr->Mask(LogicalImmediateMask)) { - case AND_w_imm: - case AND_x_imm: mnemonic = "and"; break; - case ORR_w_imm: - case ORR_x_imm: { - mnemonic = "orr"; - unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize - : kWRegSize; - if (rn_is_zr && !IsMovzMovnImm(reg_size, instr->ImmLogical())) { - mnemonic = "mov"; - form = "'Rds, 'ITri"; - } - break; - } - case EOR_w_imm: - case EOR_x_imm: mnemonic = "eor"; break; - case ANDS_w_imm: - case ANDS_x_imm: { - mnemonic = "ands"; - if (rd_is_zr) { - mnemonic = "tst"; - form = "'Rn, 'ITri"; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -bool Disassembler::IsMovzMovnImm(unsigned reg_size, uint64_t value) { - VIXL_ASSERT((reg_size == kXRegSize) || - ((reg_size == kWRegSize) && (value <= 0xffffffff))); - - // Test for movz: 16 bits set at positions 0, 16, 32 or 48. - if (((value & UINT64_C(0xffffffffffff0000)) == 0) || - ((value & UINT64_C(0xffffffff0000ffff)) == 0) || - ((value & UINT64_C(0xffff0000ffffffff)) == 0) || - ((value & UINT64_C(0x0000ffffffffffff)) == 0)) { - return true; - } - - // Test for movn: NOT(16 bits set at positions 0, 16, 32 or 48). - if ((reg_size == kXRegSize) && - (((~value & UINT64_C(0xffffffffffff0000)) == 0) || - ((~value & UINT64_C(0xffffffff0000ffff)) == 0) || - ((~value & UINT64_C(0xffff0000ffffffff)) == 0) || - ((~value & UINT64_C(0x0000ffffffffffff)) == 0))) { - return true; - } - if ((reg_size == kWRegSize) && - (((value & 0xffff0000) == 0xffff0000) || - ((value & 0x0000ffff) == 0x0000ffff))) { - return true; - } - return false; -} - - -void Disassembler::VisitLogicalShifted(const Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm'NLo"; - - switch (instr->Mask(LogicalShiftedMask)) { - case AND_w: - case AND_x: mnemonic = "and"; break; - case BIC_w: - case BIC_x: mnemonic = "bic"; break; - case EOR_w: - case EOR_x: mnemonic = "eor"; break; - case EON_w: - case EON_x: mnemonic = "eon"; break; - case BICS_w: - case BICS_x: mnemonic = "bics"; break; - case ANDS_w: - case ANDS_x: { - mnemonic = "ands"; - if (rd_is_zr) { - mnemonic = "tst"; - form = "'Rn, 'Rm'NLo"; - } - break; - } - case ORR_w: - case ORR_x: { - mnemonic = "orr"; - if (rn_is_zr && (instr->ImmDPShift() == 0) && (instr->ShiftDP() == LSL)) { - mnemonic = "mov"; - form = "'Rd, 'Rm"; - } - break; - } - case ORN_w: - case ORN_x: { - mnemonic = "orn"; - if (rn_is_zr) { - mnemonic = "mvn"; - form = "'Rd, 'Rm'NLo"; - } - break; - } - default: VIXL_UNREACHABLE(); - } - - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitConditionalCompareRegister(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rn, 'Rm, 'INzcv, 'Cond"; - - switch (instr->Mask(ConditionalCompareRegisterMask)) { - case CCMN_w: - case CCMN_x: mnemonic = "ccmn"; break; - case CCMP_w: - case CCMP_x: mnemonic = "ccmp"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitConditionalCompareImmediate(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rn, 'IP, 'INzcv, 'Cond"; - - switch (instr->Mask(ConditionalCompareImmediateMask)) { - case CCMN_w_imm: - case CCMN_x_imm: mnemonic = "ccmn"; break; - case CCMP_w_imm: - case CCMP_x_imm: mnemonic = "ccmp"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitConditionalSelect(const Instruction* instr) { - bool rnm_is_zr = (RnIsZROrSP(instr) && RmIsZROrSP(instr)); - bool rn_is_rm = (instr->Rn() == instr->Rm()); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm, 'Cond"; - const char *form_test = "'Rd, 'CInv"; - const char *form_update = "'Rd, 'Rn, 'CInv"; - - Condition cond = static_cast(instr->Condition()); - bool invertible_cond = (cond != al) && (cond != nv); - - switch (instr->Mask(ConditionalSelectMask)) { - case CSEL_w: - case CSEL_x: mnemonic = "csel"; break; - case CSINC_w: - case CSINC_x: { - mnemonic = "csinc"; - if (rnm_is_zr && invertible_cond) { - mnemonic = "cset"; - form = form_test; - } else if (rn_is_rm && invertible_cond) { - mnemonic = "cinc"; - form = form_update; - } - break; - } - case CSINV_w: - case CSINV_x: { - mnemonic = "csinv"; - if (rnm_is_zr && invertible_cond) { - mnemonic = "csetm"; - form = form_test; - } else if (rn_is_rm && invertible_cond) { - mnemonic = "cinv"; - form = form_update; - } - break; - } - case CSNEG_w: - case CSNEG_x: { - mnemonic = "csneg"; - if (rn_is_rm && invertible_cond) { - mnemonic = "cneg"; - form = form_update; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitBitfield(const Instruction* instr) { - unsigned s = instr->ImmS(); - unsigned r = instr->ImmR(); - unsigned rd_size_minus_1 = - ((instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize) - 1; - const char *mnemonic = ""; - const char *form = ""; - const char *form_shift_right = "'Rd, 'Rn, 'IBr"; - const char *form_extend = "'Rd, 'Wn"; - const char *form_bfiz = "'Rd, 'Rn, 'IBZ-r, 'IBs+1"; - const char *form_bfx = "'Rd, 'Rn, 'IBr, 'IBs-r+1"; - const char *form_lsl = "'Rd, 'Rn, 'IBZ-r"; - - switch (instr->Mask(BitfieldMask)) { - case SBFM_w: - case SBFM_x: { - mnemonic = "sbfx"; - form = form_bfx; - if (r == 0) { - form = form_extend; - if (s == 7) { - mnemonic = "sxtb"; - } else if (s == 15) { - mnemonic = "sxth"; - } else if ((s == 31) && (instr->SixtyFourBits() == 1)) { - mnemonic = "sxtw"; - } else { - form = form_bfx; - } - } else if (s == rd_size_minus_1) { - mnemonic = "asr"; - form = form_shift_right; - } else if (s < r) { - mnemonic = "sbfiz"; - form = form_bfiz; - } - break; - } - case UBFM_w: - case UBFM_x: { - mnemonic = "ubfx"; - form = form_bfx; - if (r == 0) { - form = form_extend; - if (s == 7) { - mnemonic = "uxtb"; - } else if (s == 15) { - mnemonic = "uxth"; - } else { - form = form_bfx; - } - } - if (s == rd_size_minus_1) { - mnemonic = "lsr"; - form = form_shift_right; - } else if (r == s + 1) { - mnemonic = "lsl"; - form = form_lsl; - } else if (s < r) { - mnemonic = "ubfiz"; - form = form_bfiz; - } - break; - } - case BFM_w: - case BFM_x: { - mnemonic = "bfxil"; - form = form_bfx; - if (s < r) { - mnemonic = "bfi"; - form = form_bfiz; - } - } - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitExtract(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm, 'IExtract"; - - switch (instr->Mask(ExtractMask)) { - case EXTR_w: - case EXTR_x: { - if (instr->Rn() == instr->Rm()) { - mnemonic = "ror"; - form = "'Rd, 'Rn, 'IExtract"; - } else { - mnemonic = "extr"; - } - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitPCRelAddressing(const Instruction* instr) { - switch (instr->Mask(PCRelAddressingMask)) { - case ADR: Format(instr, "adr", "'Xd, 'AddrPCRelByte"); break; - case ADRP: Format(instr, "adrp", "'Xd, 'AddrPCRelPage"); break; - default: Format(instr, "unimplemented", "(PCRelAddressing)"); - } -} - - -void Disassembler::VisitConditionalBranch(const Instruction* instr) { - switch (instr->Mask(ConditionalBranchMask)) { - case B_cond: Format(instr, "b.'CBrn", "'TImmCond"); break; - default: VIXL_UNREACHABLE(); - } -} - - -void Disassembler::VisitUnconditionalBranchToRegister( - const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Xn"; - - switch (instr->Mask(UnconditionalBranchToRegisterMask)) { - case BR: mnemonic = "br"; break; - case BLR: mnemonic = "blr"; break; - case RET: { - mnemonic = "ret"; - if (instr->Rn() == kLinkRegCode) { - form = NULL; - } - break; - } - default: form = "(UnconditionalBranchToRegister)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitUnconditionalBranch(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'TImmUncn"; - - switch (instr->Mask(UnconditionalBranchMask)) { - case B: mnemonic = "b"; break; - case BL: mnemonic = "bl"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitDataProcessing1Source(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn"; - - switch (instr->Mask(DataProcessing1SourceMask)) { - #define FORMAT(A, B) \ - case A##_w: \ - case A##_x: mnemonic = B; break; - FORMAT(RBIT, "rbit"); - FORMAT(REV16, "rev16"); - FORMAT(REV, "rev"); - FORMAT(CLZ, "clz"); - FORMAT(CLS, "cls"); - #undef FORMAT - case REV32_x: mnemonic = "rev32"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitDataProcessing2Source(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Rd, 'Rn, 'Rm"; - const char *form_wwx = "'Wd, 'Wn, 'Xm"; - - switch (instr->Mask(DataProcessing2SourceMask)) { - #define FORMAT(A, B) \ - case A##_w: \ - case A##_x: mnemonic = B; break; - FORMAT(UDIV, "udiv"); - FORMAT(SDIV, "sdiv"); - FORMAT(LSLV, "lsl"); - FORMAT(LSRV, "lsr"); - FORMAT(ASRV, "asr"); - FORMAT(RORV, "ror"); - #undef FORMAT - case CRC32B: mnemonic = "crc32b"; break; - case CRC32H: mnemonic = "crc32h"; break; - case CRC32W: mnemonic = "crc32w"; break; - case CRC32X: mnemonic = "crc32x"; form = form_wwx; break; - case CRC32CB: mnemonic = "crc32cb"; break; - case CRC32CH: mnemonic = "crc32ch"; break; - case CRC32CW: mnemonic = "crc32cw"; break; - case CRC32CX: mnemonic = "crc32cx"; form = form_wwx; break; - default: form = "(DataProcessing2Source)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitDataProcessing3Source(const Instruction* instr) { - bool ra_is_zr = RaIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Xd, 'Wn, 'Wm, 'Xa"; - const char *form_rrr = "'Rd, 'Rn, 'Rm"; - const char *form_rrrr = "'Rd, 'Rn, 'Rm, 'Ra"; - const char *form_xww = "'Xd, 'Wn, 'Wm"; - const char *form_xxx = "'Xd, 'Xn, 'Xm"; - - switch (instr->Mask(DataProcessing3SourceMask)) { - case MADD_w: - case MADD_x: { - mnemonic = "madd"; - form = form_rrrr; - if (ra_is_zr) { - mnemonic = "mul"; - form = form_rrr; - } - break; - } - case MSUB_w: - case MSUB_x: { - mnemonic = "msub"; - form = form_rrrr; - if (ra_is_zr) { - mnemonic = "mneg"; - form = form_rrr; - } - break; - } - case SMADDL_x: { - mnemonic = "smaddl"; - if (ra_is_zr) { - mnemonic = "smull"; - form = form_xww; - } - break; - } - case SMSUBL_x: { - mnemonic = "smsubl"; - if (ra_is_zr) { - mnemonic = "smnegl"; - form = form_xww; - } - break; - } - case UMADDL_x: { - mnemonic = "umaddl"; - if (ra_is_zr) { - mnemonic = "umull"; - form = form_xww; - } - break; - } - case UMSUBL_x: { - mnemonic = "umsubl"; - if (ra_is_zr) { - mnemonic = "umnegl"; - form = form_xww; - } - break; - } - case SMULH_x: { - mnemonic = "smulh"; - form = form_xxx; - break; - } - case UMULH_x: { - mnemonic = "umulh"; - form = form_xxx; - break; - } - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitCompareBranch(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rt, 'TImmCmpa"; - - switch (instr->Mask(CompareBranchMask)) { - case CBZ_w: - case CBZ_x: mnemonic = "cbz"; break; - case CBNZ_w: - case CBNZ_x: mnemonic = "cbnz"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitTestBranch(const Instruction* instr) { - const char *mnemonic = ""; - // If the top bit of the immediate is clear, the tested register is - // disassembled as Wt, otherwise Xt. As the top bit of the immediate is - // encoded in bit 31 of the instruction, we can reuse the Rt form, which - // uses bit 31 (normally "sf") to choose the register size. - const char *form = "'Rt, 'IS, 'TImmTest"; - - switch (instr->Mask(TestBranchMask)) { - case TBZ: mnemonic = "tbz"; break; - case TBNZ: mnemonic = "tbnz"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitMoveWideImmediate(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'IMoveImm"; - - // Print the shift separately for movk, to make it clear which half word will - // be overwritten. Movn and movz print the computed immediate, which includes - // shift calculation. - switch (instr->Mask(MoveWideImmediateMask)) { - case MOVN_w: - case MOVN_x: - if ((instr->ImmMoveWide()) || (instr->ShiftMoveWide() == 0)) { - if ((instr->SixtyFourBits() == 0) && (instr->ImmMoveWide() == 0xffff)) { - mnemonic = "movn"; - } else { - mnemonic = "mov"; - form = "'Rd, 'IMoveNeg"; - } - } else { - mnemonic = "movn"; - } - break; - case MOVZ_w: - case MOVZ_x: - if ((instr->ImmMoveWide()) || (instr->ShiftMoveWide() == 0)) - mnemonic = "mov"; - else - mnemonic = "movz"; - break; - case MOVK_w: - case MOVK_x: mnemonic = "movk"; form = "'Rd, 'IMoveLSL"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -#define LOAD_STORE_LIST(V) \ - V(STRB_w, "strb", "'Wt") \ - V(STRH_w, "strh", "'Wt") \ - V(STR_w, "str", "'Wt") \ - V(STR_x, "str", "'Xt") \ - V(LDRB_w, "ldrb", "'Wt") \ - V(LDRH_w, "ldrh", "'Wt") \ - V(LDR_w, "ldr", "'Wt") \ - V(LDR_x, "ldr", "'Xt") \ - V(LDRSB_x, "ldrsb", "'Xt") \ - V(LDRSH_x, "ldrsh", "'Xt") \ - V(LDRSW_x, "ldrsw", "'Xt") \ - V(LDRSB_w, "ldrsb", "'Wt") \ - V(LDRSH_w, "ldrsh", "'Wt") \ - V(STR_b, "str", "'Bt") \ - V(STR_h, "str", "'Ht") \ - V(STR_s, "str", "'St") \ - V(STR_d, "str", "'Dt") \ - V(LDR_b, "ldr", "'Bt") \ - V(LDR_h, "ldr", "'Ht") \ - V(LDR_s, "ldr", "'St") \ - V(LDR_d, "ldr", "'Dt") \ - V(STR_q, "str", "'Qt") \ - V(LDR_q, "ldr", "'Qt") - -void Disassembler::VisitLoadStorePreIndex(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePreIndex)"; - - switch (instr->Mask(LoadStorePreIndexMask)) { - #define LS_PREINDEX(A, B, C) \ - case A##_pre: mnemonic = B; form = C ", ['Xns'ILS]!"; break; - LOAD_STORE_LIST(LS_PREINDEX) - #undef LS_PREINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePostIndex(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePostIndex)"; - - switch (instr->Mask(LoadStorePostIndexMask)) { - #define LS_POSTINDEX(A, B, C) \ - case A##_post: mnemonic = B; form = C ", ['Xns]'ILS"; break; - LOAD_STORE_LIST(LS_POSTINDEX) - #undef LS_POSTINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreUnsignedOffset(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStoreUnsignedOffset)"; - - switch (instr->Mask(LoadStoreUnsignedOffsetMask)) { - #define LS_UNSIGNEDOFFSET(A, B, C) \ - case A##_unsigned: mnemonic = B; form = C ", ['Xns'ILU]"; break; - LOAD_STORE_LIST(LS_UNSIGNEDOFFSET) - #undef LS_UNSIGNEDOFFSET - case PRFM_unsigned: mnemonic = "prfm"; form = "'PrefOp, ['Xns'ILU]"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreRegisterOffset(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStoreRegisterOffset)"; - - switch (instr->Mask(LoadStoreRegisterOffsetMask)) { - #define LS_REGISTEROFFSET(A, B, C) \ - case A##_reg: mnemonic = B; form = C ", ['Xns, 'Offsetreg]"; break; - LOAD_STORE_LIST(LS_REGISTEROFFSET) - #undef LS_REGISTEROFFSET - case PRFM_reg: mnemonic = "prfm"; form = "'PrefOp, ['Xns, 'Offsetreg]"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreUnscaledOffset(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Wt, ['Xns'ILS]"; - const char *form_x = "'Xt, ['Xns'ILS]"; - const char *form_b = "'Bt, ['Xns'ILS]"; - const char *form_h = "'Ht, ['Xns'ILS]"; - const char *form_s = "'St, ['Xns'ILS]"; - const char *form_d = "'Dt, ['Xns'ILS]"; - const char *form_q = "'Qt, ['Xns'ILS]"; - const char *form_prefetch = "'PrefOp, ['Xns'ILS]"; - - switch (instr->Mask(LoadStoreUnscaledOffsetMask)) { - case STURB_w: mnemonic = "sturb"; break; - case STURH_w: mnemonic = "sturh"; break; - case STUR_w: mnemonic = "stur"; break; - case STUR_x: mnemonic = "stur"; form = form_x; break; - case STUR_b: mnemonic = "stur"; form = form_b; break; - case STUR_h: mnemonic = "stur"; form = form_h; break; - case STUR_s: mnemonic = "stur"; form = form_s; break; - case STUR_d: mnemonic = "stur"; form = form_d; break; - case STUR_q: mnemonic = "stur"; form = form_q; break; - case LDURB_w: mnemonic = "ldurb"; break; - case LDURH_w: mnemonic = "ldurh"; break; - case LDUR_w: mnemonic = "ldur"; break; - case LDUR_x: mnemonic = "ldur"; form = form_x; break; - case LDUR_b: mnemonic = "ldur"; form = form_b; break; - case LDUR_h: mnemonic = "ldur"; form = form_h; break; - case LDUR_s: mnemonic = "ldur"; form = form_s; break; - case LDUR_d: mnemonic = "ldur"; form = form_d; break; - case LDUR_q: mnemonic = "ldur"; form = form_q; break; - case LDURSB_x: form = form_x; VIXL_FALLTHROUGH(); - case LDURSB_w: mnemonic = "ldursb"; break; - case LDURSH_x: form = form_x; VIXL_FALLTHROUGH(); - case LDURSH_w: mnemonic = "ldursh"; break; - case LDURSW_x: mnemonic = "ldursw"; form = form_x; break; - case PRFUM: mnemonic = "prfum"; form = form_prefetch; break; - default: form = "(LoadStoreUnscaledOffset)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadLiteral(const Instruction* instr) { - const char *mnemonic = "ldr"; - const char *form = "(LoadLiteral)"; - - switch (instr->Mask(LoadLiteralMask)) { - case LDR_w_lit: form = "'Wt, 'ILLiteral 'LValue"; break; - case LDR_x_lit: form = "'Xt, 'ILLiteral 'LValue"; break; - case LDR_s_lit: form = "'St, 'ILLiteral 'LValue"; break; - case LDR_d_lit: form = "'Dt, 'ILLiteral 'LValue"; break; - case LDR_q_lit: form = "'Qt, 'ILLiteral 'LValue"; break; - case LDRSW_x_lit: { - mnemonic = "ldrsw"; - form = "'Xt, 'ILLiteral 'LValue"; - break; - } - case PRFM_lit: { - mnemonic = "prfm"; - form = "'PrefOp, 'ILLiteral 'LValue"; - break; - } - default: mnemonic = "unimplemented"; - } - Format(instr, mnemonic, form); -} - - -#define LOAD_STORE_PAIR_LIST(V) \ - V(STP_w, "stp", "'Wt, 'Wt2", "2") \ - V(LDP_w, "ldp", "'Wt, 'Wt2", "2") \ - V(LDPSW_x, "ldpsw", "'Xt, 'Xt2", "2") \ - V(STP_x, "stp", "'Xt, 'Xt2", "3") \ - V(LDP_x, "ldp", "'Xt, 'Xt2", "3") \ - V(STP_s, "stp", "'St, 'St2", "2") \ - V(LDP_s, "ldp", "'St, 'St2", "2") \ - V(STP_d, "stp", "'Dt, 'Dt2", "3") \ - V(LDP_d, "ldp", "'Dt, 'Dt2", "3") \ - V(LDP_q, "ldp", "'Qt, 'Qt2", "4") \ - V(STP_q, "stp", "'Qt, 'Qt2", "4") - -void Disassembler::VisitLoadStorePairPostIndex(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePairPostIndex)"; - - switch (instr->Mask(LoadStorePairPostIndexMask)) { - #define LSP_POSTINDEX(A, B, C, D) \ - case A##_post: mnemonic = B; form = C ", ['Xns]'ILP" D; break; - LOAD_STORE_PAIR_LIST(LSP_POSTINDEX) - #undef LSP_POSTINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePairPreIndex(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePairPreIndex)"; - - switch (instr->Mask(LoadStorePairPreIndexMask)) { - #define LSP_PREINDEX(A, B, C, D) \ - case A##_pre: mnemonic = B; form = C ", ['Xns'ILP" D "]!"; break; - LOAD_STORE_PAIR_LIST(LSP_PREINDEX) - #undef LSP_PREINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePairOffset(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePairOffset)"; - - switch (instr->Mask(LoadStorePairOffsetMask)) { - #define LSP_OFFSET(A, B, C, D) \ - case A##_off: mnemonic = B; form = C ", ['Xns'ILP" D "]"; break; - LOAD_STORE_PAIR_LIST(LSP_OFFSET) - #undef LSP_OFFSET - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePairNonTemporal(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form; - - switch (instr->Mask(LoadStorePairNonTemporalMask)) { - case STNP_w: mnemonic = "stnp"; form = "'Wt, 'Wt2, ['Xns'ILP2]"; break; - case LDNP_w: mnemonic = "ldnp"; form = "'Wt, 'Wt2, ['Xns'ILP2]"; break; - case STNP_x: mnemonic = "stnp"; form = "'Xt, 'Xt2, ['Xns'ILP3]"; break; - case LDNP_x: mnemonic = "ldnp"; form = "'Xt, 'Xt2, ['Xns'ILP3]"; break; - case STNP_s: mnemonic = "stnp"; form = "'St, 'St2, ['Xns'ILP2]"; break; - case LDNP_s: mnemonic = "ldnp"; form = "'St, 'St2, ['Xns'ILP2]"; break; - case STNP_d: mnemonic = "stnp"; form = "'Dt, 'Dt2, ['Xns'ILP3]"; break; - case LDNP_d: mnemonic = "ldnp"; form = "'Dt, 'Dt2, ['Xns'ILP3]"; break; - case STNP_q: mnemonic = "stnp"; form = "'Qt, 'Qt2, ['Xns'ILP4]"; break; - case LDNP_q: mnemonic = "ldnp"; form = "'Qt, 'Qt2, ['Xns'ILP4]"; break; - default: form = "(LoadStorePairNonTemporal)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreExclusive(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form; - - switch (instr->Mask(LoadStoreExclusiveMask)) { - case STXRB_w: mnemonic = "stxrb"; form = "'Ws, 'Wt, ['Xns]"; break; - case STXRH_w: mnemonic = "stxrh"; form = "'Ws, 'Wt, ['Xns]"; break; - case STXR_w: mnemonic = "stxr"; form = "'Ws, 'Wt, ['Xns]"; break; - case STXR_x: mnemonic = "stxr"; form = "'Ws, 'Xt, ['Xns]"; break; - case LDXRB_w: mnemonic = "ldxrb"; form = "'Wt, ['Xns]"; break; - case LDXRH_w: mnemonic = "ldxrh"; form = "'Wt, ['Xns]"; break; - case LDXR_w: mnemonic = "ldxr"; form = "'Wt, ['Xns]"; break; - case LDXR_x: mnemonic = "ldxr"; form = "'Xt, ['Xns]"; break; - case STXP_w: mnemonic = "stxp"; form = "'Ws, 'Wt, 'Wt2, ['Xns]"; break; - case STXP_x: mnemonic = "stxp"; form = "'Ws, 'Xt, 'Xt2, ['Xns]"; break; - case LDXP_w: mnemonic = "ldxp"; form = "'Wt, 'Wt2, ['Xns]"; break; - case LDXP_x: mnemonic = "ldxp"; form = "'Xt, 'Xt2, ['Xns]"; break; - case STLXRB_w: mnemonic = "stlxrb"; form = "'Ws, 'Wt, ['Xns]"; break; - case STLXRH_w: mnemonic = "stlxrh"; form = "'Ws, 'Wt, ['Xns]"; break; - case STLXR_w: mnemonic = "stlxr"; form = "'Ws, 'Wt, ['Xns]"; break; - case STLXR_x: mnemonic = "stlxr"; form = "'Ws, 'Xt, ['Xns]"; break; - case LDAXRB_w: mnemonic = "ldaxrb"; form = "'Wt, ['Xns]"; break; - case LDAXRH_w: mnemonic = "ldaxrh"; form = "'Wt, ['Xns]"; break; - case LDAXR_w: mnemonic = "ldaxr"; form = "'Wt, ['Xns]"; break; - case LDAXR_x: mnemonic = "ldaxr"; form = "'Xt, ['Xns]"; break; - case STLXP_w: mnemonic = "stlxp"; form = "'Ws, 'Wt, 'Wt2, ['Xns]"; break; - case STLXP_x: mnemonic = "stlxp"; form = "'Ws, 'Xt, 'Xt2, ['Xns]"; break; - case LDAXP_w: mnemonic = "ldaxp"; form = "'Wt, 'Wt2, ['Xns]"; break; - case LDAXP_x: mnemonic = "ldaxp"; form = "'Xt, 'Xt2, ['Xns]"; break; - case STLRB_w: mnemonic = "stlrb"; form = "'Wt, ['Xns]"; break; - case STLRH_w: mnemonic = "stlrh"; form = "'Wt, ['Xns]"; break; - case STLR_w: mnemonic = "stlr"; form = "'Wt, ['Xns]"; break; - case STLR_x: mnemonic = "stlr"; form = "'Xt, ['Xns]"; break; - case LDARB_w: mnemonic = "ldarb"; form = "'Wt, ['Xns]"; break; - case LDARH_w: mnemonic = "ldarh"; form = "'Wt, ['Xns]"; break; - case LDAR_w: mnemonic = "ldar"; form = "'Wt, ['Xns]"; break; - case LDAR_x: mnemonic = "ldar"; form = "'Xt, ['Xns]"; break; - default: form = "(LoadStoreExclusive)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPCompare(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Fn, 'Fm"; - const char *form_zero = "'Fn, #0.0"; - - switch (instr->Mask(FPCompareMask)) { - case FCMP_s_zero: - case FCMP_d_zero: form = form_zero; VIXL_FALLTHROUGH(); - case FCMP_s: - case FCMP_d: mnemonic = "fcmp"; break; - case FCMPE_s_zero: - case FCMPE_d_zero: form = form_zero; VIXL_FALLTHROUGH(); - case FCMPE_s: - case FCMPE_d: mnemonic = "fcmpe"; break; - default: form = "(FPCompare)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPConditionalCompare(const Instruction* instr) { - const char *mnemonic = "unmplemented"; - const char *form = "'Fn, 'Fm, 'INzcv, 'Cond"; - - switch (instr->Mask(FPConditionalCompareMask)) { - case FCCMP_s: - case FCCMP_d: mnemonic = "fccmp"; break; - case FCCMPE_s: - case FCCMPE_d: mnemonic = "fccmpe"; break; - default: form = "(FPConditionalCompare)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPConditionalSelect(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Fd, 'Fn, 'Fm, 'Cond"; - - switch (instr->Mask(FPConditionalSelectMask)) { - case FCSEL_s: - case FCSEL_d: mnemonic = "fcsel"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPDataProcessing1Source(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Fd, 'Fn"; - - switch (instr->Mask(FPDataProcessing1SourceMask)) { - #define FORMAT(A, B) \ - case A##_s: \ - case A##_d: mnemonic = B; break; - FORMAT(FMOV, "fmov"); - FORMAT(FABS, "fabs"); - FORMAT(FNEG, "fneg"); - FORMAT(FSQRT, "fsqrt"); - FORMAT(FRINTN, "frintn"); - FORMAT(FRINTP, "frintp"); - FORMAT(FRINTM, "frintm"); - FORMAT(FRINTZ, "frintz"); - FORMAT(FRINTA, "frinta"); - FORMAT(FRINTX, "frintx"); - FORMAT(FRINTI, "frinti"); - #undef FORMAT - case FCVT_ds: mnemonic = "fcvt"; form = "'Dd, 'Sn"; break; - case FCVT_sd: mnemonic = "fcvt"; form = "'Sd, 'Dn"; break; - case FCVT_hs: mnemonic = "fcvt"; form = "'Hd, 'Sn"; break; - case FCVT_sh: mnemonic = "fcvt"; form = "'Sd, 'Hn"; break; - case FCVT_dh: mnemonic = "fcvt"; form = "'Dd, 'Hn"; break; - case FCVT_hd: mnemonic = "fcvt"; form = "'Hd, 'Dn"; break; - default: form = "(FPDataProcessing1Source)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPDataProcessing2Source(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Fd, 'Fn, 'Fm"; - - switch (instr->Mask(FPDataProcessing2SourceMask)) { - #define FORMAT(A, B) \ - case A##_s: \ - case A##_d: mnemonic = B; break; - FORMAT(FMUL, "fmul"); - FORMAT(FDIV, "fdiv"); - FORMAT(FADD, "fadd"); - FORMAT(FSUB, "fsub"); - FORMAT(FMAX, "fmax"); - FORMAT(FMIN, "fmin"); - FORMAT(FMAXNM, "fmaxnm"); - FORMAT(FMINNM, "fminnm"); - FORMAT(FNMUL, "fnmul"); - #undef FORMAT - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPDataProcessing3Source(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Fd, 'Fn, 'Fm, 'Fa"; - - switch (instr->Mask(FPDataProcessing3SourceMask)) { - #define FORMAT(A, B) \ - case A##_s: \ - case A##_d: mnemonic = B; break; - FORMAT(FMADD, "fmadd"); - FORMAT(FMSUB, "fmsub"); - FORMAT(FNMADD, "fnmadd"); - FORMAT(FNMSUB, "fnmsub"); - #undef FORMAT - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPImmediate(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "(FPImmediate)"; - - switch (instr->Mask(FPImmediateMask)) { - case FMOV_s_imm: mnemonic = "fmov"; form = "'Sd, 'IFPSingle"; break; - case FMOV_d_imm: mnemonic = "fmov"; form = "'Dd, 'IFPDouble"; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPIntegerConvert(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(FPIntegerConvert)"; - const char *form_rf = "'Rd, 'Fn"; - const char *form_fr = "'Fd, 'Rn"; - - switch (instr->Mask(FPIntegerConvertMask)) { - case FMOV_ws: - case FMOV_xd: mnemonic = "fmov"; form = form_rf; break; - case FMOV_sw: - case FMOV_dx: mnemonic = "fmov"; form = form_fr; break; - case FMOV_d1_x: mnemonic = "fmov"; form = "'Vd.D[1], 'Rn"; break; - case FMOV_x_d1: mnemonic = "fmov"; form = "'Rd, 'Vn.D[1]"; break; - case FCVTAS_ws: - case FCVTAS_xs: - case FCVTAS_wd: - case FCVTAS_xd: mnemonic = "fcvtas"; form = form_rf; break; - case FCVTAU_ws: - case FCVTAU_xs: - case FCVTAU_wd: - case FCVTAU_xd: mnemonic = "fcvtau"; form = form_rf; break; - case FCVTMS_ws: - case FCVTMS_xs: - case FCVTMS_wd: - case FCVTMS_xd: mnemonic = "fcvtms"; form = form_rf; break; - case FCVTMU_ws: - case FCVTMU_xs: - case FCVTMU_wd: - case FCVTMU_xd: mnemonic = "fcvtmu"; form = form_rf; break; - case FCVTNS_ws: - case FCVTNS_xs: - case FCVTNS_wd: - case FCVTNS_xd: mnemonic = "fcvtns"; form = form_rf; break; - case FCVTNU_ws: - case FCVTNU_xs: - case FCVTNU_wd: - case FCVTNU_xd: mnemonic = "fcvtnu"; form = form_rf; break; - case FCVTZU_xd: - case FCVTZU_ws: - case FCVTZU_wd: - case FCVTZU_xs: mnemonic = "fcvtzu"; form = form_rf; break; - case FCVTZS_xd: - case FCVTZS_wd: - case FCVTZS_xs: - case FCVTZS_ws: mnemonic = "fcvtzs"; form = form_rf; break; - case FCVTPU_xd: - case FCVTPU_ws: - case FCVTPU_wd: - case FCVTPU_xs: mnemonic = "fcvtpu"; form = form_rf; break; - case FCVTPS_xd: - case FCVTPS_wd: - case FCVTPS_xs: - case FCVTPS_ws: mnemonic = "fcvtps"; form = form_rf; break; - case SCVTF_sw: - case SCVTF_sx: - case SCVTF_dw: - case SCVTF_dx: mnemonic = "scvtf"; form = form_fr; break; - case UCVTF_sw: - case UCVTF_sx: - case UCVTF_dw: - case UCVTF_dx: mnemonic = "ucvtf"; form = form_fr; break; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPFixedPointConvert(const Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'Fn, 'IFPFBits"; - const char *form_fr = "'Fd, 'Rn, 'IFPFBits"; - - switch (instr->Mask(FPFixedPointConvertMask)) { - case FCVTZS_ws_fixed: - case FCVTZS_xs_fixed: - case FCVTZS_wd_fixed: - case FCVTZS_xd_fixed: mnemonic = "fcvtzs"; break; - case FCVTZU_ws_fixed: - case FCVTZU_xs_fixed: - case FCVTZU_wd_fixed: - case FCVTZU_xd_fixed: mnemonic = "fcvtzu"; break; - case SCVTF_sw_fixed: - case SCVTF_sx_fixed: - case SCVTF_dw_fixed: - case SCVTF_dx_fixed: mnemonic = "scvtf"; form = form_fr; break; - case UCVTF_sw_fixed: - case UCVTF_sx_fixed: - case UCVTF_dw_fixed: - case UCVTF_dx_fixed: mnemonic = "ucvtf"; form = form_fr; break; - default: VIXL_UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitSystem(const Instruction* instr) { - // Some system instructions hijack their Op and Cp fields to represent a - // range of immediates instead of indicating a different instruction. This - // makes the decoding tricky. - const char *mnemonic = "unimplemented"; - const char *form = "(System)"; - - if (instr->Mask(SystemExclusiveMonitorFMask) == SystemExclusiveMonitorFixed) { - switch (instr->Mask(SystemExclusiveMonitorMask)) { - case CLREX: { - mnemonic = "clrex"; - form = (instr->CRm() == 0xf) ? NULL : "'IX"; - break; - } - } - } else if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { - switch (instr->Mask(SystemSysRegMask)) { - case MRS: { - mnemonic = "mrs"; - switch (instr->ImmSystemRegister()) { - case NZCV: form = "'Xt, nzcv"; break; - case FPCR: form = "'Xt, fpcr"; break; - default: form = "'Xt, (unknown)"; break; - } - break; - } - case MSR: { - mnemonic = "msr"; - switch (instr->ImmSystemRegister()) { - case NZCV: form = "nzcv, 'Xt"; break; - case FPCR: form = "fpcr, 'Xt"; break; - default: form = "(unknown), 'Xt"; break; - } - break; - } - } - } else if (instr->Mask(SystemHintFMask) == SystemHintFixed) { - switch (instr->ImmHint()) { - case NOP: { - mnemonic = "nop"; - form = NULL; - break; - } - } - } else if (instr->Mask(MemBarrierFMask) == MemBarrierFixed) { - switch (instr->Mask(MemBarrierMask)) { - case DMB: { - mnemonic = "dmb"; - form = "'M"; - break; - } - case DSB: { - mnemonic = "dsb"; - form = "'M"; - break; - } - case ISB: { - mnemonic = "isb"; - form = NULL; - break; - } - } - } else if (instr->Mask(SystemSysFMask) == SystemSysFixed) { - switch (instr->SysOp()) { - case IVAU: - mnemonic = "ic"; - form = "ivau, 'Xt"; - break; - case CVAC: - mnemonic = "dc"; - form = "cvac, 'Xt"; - break; - case CVAU: - mnemonic = "dc"; - form = "cvau, 'Xt"; - break; - case CIVAC: - mnemonic = "dc"; - form = "civac, 'Xt"; - break; - case ZVA: - mnemonic = "dc"; - form = "zva, 'Xt"; - break; - default: - mnemonic = "sys"; - if (instr->Rt() == 31) { - form = "'G1, 'Kn, 'Km, 'G2"; - } else { - form = "'G1, 'Kn, 'Km, 'G2, 'Xt"; - } - break; - } - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitException(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'IDebug"; - - switch (instr->Mask(ExceptionMask)) { - case HLT: mnemonic = "hlt"; break; - case BRK: mnemonic = "brk"; break; - case SVC: mnemonic = "svc"; break; - case HVC: mnemonic = "hvc"; break; - case SMC: mnemonic = "smc"; break; - case DCPS1: mnemonic = "dcps1"; form = "{'IDebug}"; break; - case DCPS2: mnemonic = "dcps2"; form = "{'IDebug}"; break; - case DCPS3: mnemonic = "dcps3"; form = "{'IDebug}"; break; - default: form = "(Exception)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitCrypto2RegSHA(const Instruction* instr) { - VisitUnimplemented(instr); -} - - -void Disassembler::VisitCrypto3RegSHA(const Instruction* instr) { - VisitUnimplemented(instr); -} - - -void Disassembler::VisitCryptoAES(const Instruction* instr) { - VisitUnimplemented(instr); -} - - -void Disassembler::VisitNEON2RegMisc(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Vd.%s, 'Vn.%s"; - const char *form_cmp_zero = "'Vd.%s, 'Vn.%s, #0"; - const char *form_fcmp_zero = "'Vd.%s, 'Vn.%s, #0.0"; - NEONFormatDecoder nfd(instr); - - static const NEONFormatMap map_lp_ta = { - {23, 22, 30}, {NF_4H, NF_8H, NF_2S, NF_4S, NF_1D, NF_2D} - }; - - static const NEONFormatMap map_cvt_ta = { - {22}, {NF_4S, NF_2D} - }; - - static const NEONFormatMap map_cvt_tb = { - {22, 30}, {NF_4H, NF_8H, NF_2S, NF_4S} - }; - - if (instr->Mask(NEON2RegMiscOpcode) <= NEON_NEG_opcode) { - // These instructions all use a two bit size field, except NOT and RBIT, - // which use the field to encode the operation. - switch (instr->Mask(NEON2RegMiscMask)) { - case NEON_REV64: mnemonic = "rev64"; break; - case NEON_REV32: mnemonic = "rev32"; break; - case NEON_REV16: mnemonic = "rev16"; break; - case NEON_SADDLP: - mnemonic = "saddlp"; - nfd.SetFormatMap(0, &map_lp_ta); - break; - case NEON_UADDLP: - mnemonic = "uaddlp"; - nfd.SetFormatMap(0, &map_lp_ta); - break; - case NEON_SUQADD: mnemonic = "suqadd"; break; - case NEON_USQADD: mnemonic = "usqadd"; break; - case NEON_CLS: mnemonic = "cls"; break; - case NEON_CLZ: mnemonic = "clz"; break; - case NEON_CNT: mnemonic = "cnt"; break; - case NEON_SADALP: - mnemonic = "sadalp"; - nfd.SetFormatMap(0, &map_lp_ta); - break; - case NEON_UADALP: - mnemonic = "uadalp"; - nfd.SetFormatMap(0, &map_lp_ta); - break; - case NEON_SQABS: mnemonic = "sqabs"; break; - case NEON_SQNEG: mnemonic = "sqneg"; break; - case NEON_CMGT_zero: mnemonic = "cmgt"; form = form_cmp_zero; break; - case NEON_CMGE_zero: mnemonic = "cmge"; form = form_cmp_zero; break; - case NEON_CMEQ_zero: mnemonic = "cmeq"; form = form_cmp_zero; break; - case NEON_CMLE_zero: mnemonic = "cmle"; form = form_cmp_zero; break; - case NEON_CMLT_zero: mnemonic = "cmlt"; form = form_cmp_zero; break; - case NEON_ABS: mnemonic = "abs"; break; - case NEON_NEG: mnemonic = "neg"; break; - case NEON_RBIT_NOT: - switch (instr->FPType()) { - case 0: mnemonic = "mvn"; break; - case 1: mnemonic = "rbit"; break; - default: form = "(NEON2RegMisc)"; - } - nfd.SetFormatMaps(nfd.LogicalFormatMap()); - break; - } - } else { - // These instructions all use a one bit size field, except XTN, SQXTUN, - // SHLL, SQXTN and UQXTN, which use a two bit size field. - nfd.SetFormatMaps(nfd.FPFormatMap()); - switch (instr->Mask(NEON2RegMiscFPMask)) { - case NEON_FABS: mnemonic = "fabs"; break; - case NEON_FNEG: mnemonic = "fneg"; break; - case NEON_FCVTN: - mnemonic = instr->Mask(NEON_Q) ? "fcvtn2" : "fcvtn"; - nfd.SetFormatMap(0, &map_cvt_tb); - nfd.SetFormatMap(1, &map_cvt_ta); - break; - case NEON_FCVTXN: - mnemonic = instr->Mask(NEON_Q) ? "fcvtxn2" : "fcvtxn"; - nfd.SetFormatMap(0, &map_cvt_tb); - nfd.SetFormatMap(1, &map_cvt_ta); - break; - case NEON_FCVTL: - mnemonic = instr->Mask(NEON_Q) ? "fcvtl2" : "fcvtl"; - nfd.SetFormatMap(0, &map_cvt_ta); - nfd.SetFormatMap(1, &map_cvt_tb); - break; - case NEON_FRINTN: mnemonic = "frintn"; break; - case NEON_FRINTA: mnemonic = "frinta"; break; - case NEON_FRINTP: mnemonic = "frintp"; break; - case NEON_FRINTM: mnemonic = "frintm"; break; - case NEON_FRINTX: mnemonic = "frintx"; break; - case NEON_FRINTZ: mnemonic = "frintz"; break; - case NEON_FRINTI: mnemonic = "frinti"; break; - case NEON_FCVTNS: mnemonic = "fcvtns"; break; - case NEON_FCVTNU: mnemonic = "fcvtnu"; break; - case NEON_FCVTPS: mnemonic = "fcvtps"; break; - case NEON_FCVTPU: mnemonic = "fcvtpu"; break; - case NEON_FCVTMS: mnemonic = "fcvtms"; break; - case NEON_FCVTMU: mnemonic = "fcvtmu"; break; - case NEON_FCVTZS: mnemonic = "fcvtzs"; break; - case NEON_FCVTZU: mnemonic = "fcvtzu"; break; - case NEON_FCVTAS: mnemonic = "fcvtas"; break; - case NEON_FCVTAU: mnemonic = "fcvtau"; break; - case NEON_FSQRT: mnemonic = "fsqrt"; break; - case NEON_SCVTF: mnemonic = "scvtf"; break; - case NEON_UCVTF: mnemonic = "ucvtf"; break; - case NEON_URSQRTE: mnemonic = "ursqrte"; break; - case NEON_URECPE: mnemonic = "urecpe"; break; - case NEON_FRSQRTE: mnemonic = "frsqrte"; break; - case NEON_FRECPE: mnemonic = "frecpe"; break; - case NEON_FCMGT_zero: mnemonic = "fcmgt"; form = form_fcmp_zero; break; - case NEON_FCMGE_zero: mnemonic = "fcmge"; form = form_fcmp_zero; break; - case NEON_FCMEQ_zero: mnemonic = "fcmeq"; form = form_fcmp_zero; break; - case NEON_FCMLE_zero: mnemonic = "fcmle"; form = form_fcmp_zero; break; - case NEON_FCMLT_zero: mnemonic = "fcmlt"; form = form_fcmp_zero; break; - default: - if ((NEON_XTN_opcode <= instr->Mask(NEON2RegMiscOpcode)) && - (instr->Mask(NEON2RegMiscOpcode) <= NEON_UQXTN_opcode)) { - nfd.SetFormatMap(0, nfd.IntegerFormatMap()); - nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); - - switch (instr->Mask(NEON2RegMiscMask)) { - case NEON_XTN: mnemonic = "xtn"; break; - case NEON_SQXTN: mnemonic = "sqxtn"; break; - case NEON_UQXTN: mnemonic = "uqxtn"; break; - case NEON_SQXTUN: mnemonic = "sqxtun"; break; - case NEON_SHLL: - mnemonic = "shll"; - nfd.SetFormatMap(0, nfd.LongIntegerFormatMap()); - nfd.SetFormatMap(1, nfd.IntegerFormatMap()); - switch (instr->NEONSize()) { - case 0: form = "'Vd.%s, 'Vn.%s, #8"; break; - case 1: form = "'Vd.%s, 'Vn.%s, #16"; break; - case 2: form = "'Vd.%s, 'Vn.%s, #32"; break; - default: form = "(NEON2RegMisc)"; - } - } - Format(instr, nfd.Mnemonic(mnemonic), nfd.Substitute(form)); - return; - } else { - form = "(NEON2RegMisc)"; - } - } - } - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEON3Same(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s"; - NEONFormatDecoder nfd(instr); - - if (instr->Mask(NEON3SameLogicalFMask) == NEON3SameLogicalFixed) { - switch (instr->Mask(NEON3SameLogicalMask)) { - case NEON_AND: mnemonic = "and"; break; - case NEON_ORR: - mnemonic = "orr"; - if (instr->Rm() == instr->Rn()) { - mnemonic = "mov"; - form = "'Vd.%s, 'Vn.%s"; - } - break; - case NEON_ORN: mnemonic = "orn"; break; - case NEON_EOR: mnemonic = "eor"; break; - case NEON_BIC: mnemonic = "bic"; break; - case NEON_BIF: mnemonic = "bif"; break; - case NEON_BIT: mnemonic = "bit"; break; - case NEON_BSL: mnemonic = "bsl"; break; - default: form = "(NEON3Same)"; - } - nfd.SetFormatMaps(nfd.LogicalFormatMap()); - } else { - static const char *mnemonics[] = { - "shadd", "uhadd", "shadd", "uhadd", - "sqadd", "uqadd", "sqadd", "uqadd", - "srhadd", "urhadd", "srhadd", "urhadd", - NULL, NULL, NULL, NULL, // Handled by logical cases above. - "shsub", "uhsub", "shsub", "uhsub", - "sqsub", "uqsub", "sqsub", "uqsub", - "cmgt", "cmhi", "cmgt", "cmhi", - "cmge", "cmhs", "cmge", "cmhs", - "sshl", "ushl", "sshl", "ushl", - "sqshl", "uqshl", "sqshl", "uqshl", - "srshl", "urshl", "srshl", "urshl", - "sqrshl", "uqrshl", "sqrshl", "uqrshl", - "smax", "umax", "smax", "umax", - "smin", "umin", "smin", "umin", - "sabd", "uabd", "sabd", "uabd", - "saba", "uaba", "saba", "uaba", - "add", "sub", "add", "sub", - "cmtst", "cmeq", "cmtst", "cmeq", - "mla", "mls", "mla", "mls", - "mul", "pmul", "mul", "pmul", - "smaxp", "umaxp", "smaxp", "umaxp", - "sminp", "uminp", "sminp", "uminp", - "sqdmulh", "sqrdmulh", "sqdmulh", "sqrdmulh", - "addp", "unallocated", "addp", "unallocated", - "fmaxnm", "fmaxnmp", "fminnm", "fminnmp", - "fmla", "unallocated", "fmls", "unallocated", - "fadd", "faddp", "fsub", "fabd", - "fmulx", "fmul", "unallocated", "unallocated", - "fcmeq", "fcmge", "unallocated", "fcmgt", - "unallocated", "facge", "unallocated", "facgt", - "fmax", "fmaxp", "fmin", "fminp", - "frecps", "fdiv", "frsqrts", "unallocated"}; - - // Operation is determined by the opcode bits (15-11), the top bit of - // size (23) and the U bit (29). - unsigned index = (instr->Bits(15, 11) << 2) | (instr->Bit(23) << 1) | - instr->Bit(29); - VIXL_ASSERT(index < (sizeof(mnemonics) / sizeof(mnemonics[0]))); - mnemonic = mnemonics[index]; - // Assert that index is not one of the previously handled logical - // instructions. - VIXL_ASSERT(mnemonic != NULL); - - if (instr->Mask(NEON3SameFPFMask) == NEON3SameFPFixed) { - nfd.SetFormatMaps(nfd.FPFormatMap()); - } - } - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEON3Different(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s"; - - NEONFormatDecoder nfd(instr); - nfd.SetFormatMap(0, nfd.LongIntegerFormatMap()); - - // Ignore the Q bit. Appending a "2" suffix is handled later. - switch (instr->Mask(NEON3DifferentMask) & ~NEON_Q) { - case NEON_PMULL: mnemonic = "pmull"; break; - case NEON_SABAL: mnemonic = "sabal"; break; - case NEON_SABDL: mnemonic = "sabdl"; break; - case NEON_SADDL: mnemonic = "saddl"; break; - case NEON_SMLAL: mnemonic = "smlal"; break; - case NEON_SMLSL: mnemonic = "smlsl"; break; - case NEON_SMULL: mnemonic = "smull"; break; - case NEON_SSUBL: mnemonic = "ssubl"; break; - case NEON_SQDMLAL: mnemonic = "sqdmlal"; break; - case NEON_SQDMLSL: mnemonic = "sqdmlsl"; break; - case NEON_SQDMULL: mnemonic = "sqdmull"; break; - case NEON_UABAL: mnemonic = "uabal"; break; - case NEON_UABDL: mnemonic = "uabdl"; break; - case NEON_UADDL: mnemonic = "uaddl"; break; - case NEON_UMLAL: mnemonic = "umlal"; break; - case NEON_UMLSL: mnemonic = "umlsl"; break; - case NEON_UMULL: mnemonic = "umull"; break; - case NEON_USUBL: mnemonic = "usubl"; break; - case NEON_SADDW: - mnemonic = "saddw"; - nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); - break; - case NEON_SSUBW: - mnemonic = "ssubw"; - nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); - break; - case NEON_UADDW: - mnemonic = "uaddw"; - nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); - break; - case NEON_USUBW: - mnemonic = "usubw"; - nfd.SetFormatMap(1, nfd.LongIntegerFormatMap()); - break; - case NEON_ADDHN: - mnemonic = "addhn"; - nfd.SetFormatMaps(nfd.LongIntegerFormatMap()); - nfd.SetFormatMap(0, nfd.IntegerFormatMap()); - break; - case NEON_RADDHN: - mnemonic = "raddhn"; - nfd.SetFormatMaps(nfd.LongIntegerFormatMap()); - nfd.SetFormatMap(0, nfd.IntegerFormatMap()); - break; - case NEON_RSUBHN: - mnemonic = "rsubhn"; - nfd.SetFormatMaps(nfd.LongIntegerFormatMap()); - nfd.SetFormatMap(0, nfd.IntegerFormatMap()); - break; - case NEON_SUBHN: - mnemonic = "subhn"; - nfd.SetFormatMaps(nfd.LongIntegerFormatMap()); - nfd.SetFormatMap(0, nfd.IntegerFormatMap()); - break; - default: form = "(NEON3Different)"; - } - Format(instr, nfd.Mnemonic(mnemonic), nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONAcrossLanes(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "%sd, 'Vn.%s"; - - NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap(), - NEONFormatDecoder::IntegerFormatMap()); - - if (instr->Mask(NEONAcrossLanesFPFMask) == NEONAcrossLanesFPFixed) { - nfd.SetFormatMap(0, nfd.FPScalarFormatMap()); - nfd.SetFormatMap(1, nfd.FPFormatMap()); - switch (instr->Mask(NEONAcrossLanesFPMask)) { - case NEON_FMAXV: mnemonic = "fmaxv"; break; - case NEON_FMINV: mnemonic = "fminv"; break; - case NEON_FMAXNMV: mnemonic = "fmaxnmv"; break; - case NEON_FMINNMV: mnemonic = "fminnmv"; break; - default: form = "(NEONAcrossLanes)"; break; - } - } else if (instr->Mask(NEONAcrossLanesFMask) == NEONAcrossLanesFixed) { - switch (instr->Mask(NEONAcrossLanesMask)) { - case NEON_ADDV: mnemonic = "addv"; break; - case NEON_SMAXV: mnemonic = "smaxv"; break; - case NEON_SMINV: mnemonic = "sminv"; break; - case NEON_UMAXV: mnemonic = "umaxv"; break; - case NEON_UMINV: mnemonic = "uminv"; break; - case NEON_SADDLV: - mnemonic = "saddlv"; - nfd.SetFormatMap(0, nfd.LongScalarFormatMap()); - break; - case NEON_UADDLV: - mnemonic = "uaddlv"; - nfd.SetFormatMap(0, nfd.LongScalarFormatMap()); - break; - default: form = "(NEONAcrossLanes)"; break; - } - } - Format(instr, mnemonic, nfd.Substitute(form, - NEONFormatDecoder::kPlaceholder, NEONFormatDecoder::kFormat)); -} - - -void Disassembler::VisitNEONByIndexedElement(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - bool l_instr = false; - bool fp_instr = false; - - const char *form = "'Vd.%s, 'Vn.%s, 'Ve.%s['IVByElemIndex]"; - - static const NEONFormatMap map_ta = { - {23, 22}, {NF_UNDEF, NF_4S, NF_2D} - }; - NEONFormatDecoder nfd(instr, &map_ta, - NEONFormatDecoder::IntegerFormatMap(), - NEONFormatDecoder::ScalarFormatMap()); - - switch (instr->Mask(NEONByIndexedElementMask)) { - case NEON_SMULL_byelement: mnemonic = "smull"; l_instr = true; break; - case NEON_UMULL_byelement: mnemonic = "umull"; l_instr = true; break; - case NEON_SMLAL_byelement: mnemonic = "smlal"; l_instr = true; break; - case NEON_UMLAL_byelement: mnemonic = "umlal"; l_instr = true; break; - case NEON_SMLSL_byelement: mnemonic = "smlsl"; l_instr = true; break; - case NEON_UMLSL_byelement: mnemonic = "umlsl"; l_instr = true; break; - case NEON_SQDMULL_byelement: mnemonic = "sqdmull"; l_instr = true; break; - case NEON_SQDMLAL_byelement: mnemonic = "sqdmlal"; l_instr = true; break; - case NEON_SQDMLSL_byelement: mnemonic = "sqdmlsl"; l_instr = true; break; - case NEON_MUL_byelement: mnemonic = "mul"; break; - case NEON_MLA_byelement: mnemonic = "mla"; break; - case NEON_MLS_byelement: mnemonic = "mls"; break; - case NEON_SQDMULH_byelement: mnemonic = "sqdmulh"; break; - case NEON_SQRDMULH_byelement: mnemonic = "sqrdmulh"; break; - default: - switch (instr->Mask(NEONByIndexedElementFPMask)) { - case NEON_FMUL_byelement: mnemonic = "fmul"; fp_instr = true; break; - case NEON_FMLA_byelement: mnemonic = "fmla"; fp_instr = true; break; - case NEON_FMLS_byelement: mnemonic = "fmls"; fp_instr = true; break; - case NEON_FMULX_byelement: mnemonic = "fmulx"; fp_instr = true; break; - } - } - - if (l_instr) { - Format(instr, nfd.Mnemonic(mnemonic), nfd.Substitute(form)); - } else if (fp_instr) { - nfd.SetFormatMap(0, nfd.FPFormatMap()); - Format(instr, mnemonic, nfd.Substitute(form)); - } else { - nfd.SetFormatMap(0, nfd.IntegerFormatMap()); - Format(instr, mnemonic, nfd.Substitute(form)); - } -} - - -void Disassembler::VisitNEONCopy(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONCopy)"; - - NEONFormatDecoder nfd(instr, NEONFormatDecoder::TriangularFormatMap(), - NEONFormatDecoder::TriangularScalarFormatMap()); - - if (instr->Mask(NEONCopyInsElementMask) == NEON_INS_ELEMENT) { - mnemonic = "mov"; - nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap()); - form = "'Vd.%s['IVInsIndex1], 'Vn.%s['IVInsIndex2]"; - } else if (instr->Mask(NEONCopyInsGeneralMask) == NEON_INS_GENERAL) { - mnemonic = "mov"; - nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap()); - if (nfd.GetVectorFormat() == kFormatD) { - form = "'Vd.%s['IVInsIndex1], 'Xn"; - } else { - form = "'Vd.%s['IVInsIndex1], 'Wn"; - } - } else if (instr->Mask(NEONCopyUmovMask) == NEON_UMOV) { - if (instr->Mask(NEON_Q) || ((instr->ImmNEON5() & 7) == 4)) { - mnemonic = "mov"; - } else { - mnemonic = "umov"; - } - nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap()); - if (nfd.GetVectorFormat() == kFormatD) { - form = "'Xd, 'Vn.%s['IVInsIndex1]"; - } else { - form = "'Wd, 'Vn.%s['IVInsIndex1]"; - } - } else if (instr->Mask(NEONCopySmovMask) == NEON_SMOV) { - mnemonic = "smov"; - nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap()); - form = "'Rdq, 'Vn.%s['IVInsIndex1]"; - } else if (instr->Mask(NEONCopyDupElementMask) == NEON_DUP_ELEMENT) { - mnemonic = "dup"; - form = "'Vd.%s, 'Vn.%s['IVInsIndex1]"; - } else if (instr->Mask(NEONCopyDupGeneralMask) == NEON_DUP_GENERAL) { - mnemonic = "dup"; - if (nfd.GetVectorFormat() == kFormat2D) { - form = "'Vd.%s, 'Xn"; - } else { - form = "'Vd.%s, 'Wn"; - } - } - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONExtract(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONExtract)"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::LogicalFormatMap()); - if (instr->Mask(NEONExtractMask) == NEON_EXT) { - mnemonic = "ext"; - form = "'Vd.%s, 'Vn.%s, 'Vm.%s, 'IVExtract"; - } - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONLoadStoreMultiStruct(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONLoadStoreMultiStruct)"; - const char *form_1v = "{'Vt.%1$s}, ['Xns]"; - const char *form_2v = "{'Vt.%1$s, 'Vt2.%1$s}, ['Xns]"; - const char *form_3v = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s}, ['Xns]"; - const char *form_4v = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns]"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap()); - - switch (instr->Mask(NEONLoadStoreMultiStructMask)) { - case NEON_LD1_1v: mnemonic = "ld1"; form = form_1v; break; - case NEON_LD1_2v: mnemonic = "ld1"; form = form_2v; break; - case NEON_LD1_3v: mnemonic = "ld1"; form = form_3v; break; - case NEON_LD1_4v: mnemonic = "ld1"; form = form_4v; break; - case NEON_LD2: mnemonic = "ld2"; form = form_2v; break; - case NEON_LD3: mnemonic = "ld3"; form = form_3v; break; - case NEON_LD4: mnemonic = "ld4"; form = form_4v; break; - case NEON_ST1_1v: mnemonic = "st1"; form = form_1v; break; - case NEON_ST1_2v: mnemonic = "st1"; form = form_2v; break; - case NEON_ST1_3v: mnemonic = "st1"; form = form_3v; break; - case NEON_ST1_4v: mnemonic = "st1"; form = form_4v; break; - case NEON_ST2: mnemonic = "st2"; form = form_2v; break; - case NEON_ST3: mnemonic = "st3"; form = form_3v; break; - case NEON_ST4: mnemonic = "st4"; form = form_4v; break; - default: break; - } - - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONLoadStoreMultiStructPostIndex( - const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONLoadStoreMultiStructPostIndex)"; - const char *form_1v = "{'Vt.%1$s}, ['Xns], 'Xmr1"; - const char *form_2v = "{'Vt.%1$s, 'Vt2.%1$s}, ['Xns], 'Xmr2"; - const char *form_3v = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s}, ['Xns], 'Xmr3"; - const char *form_4v = - "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns], 'Xmr4"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap()); - - switch (instr->Mask(NEONLoadStoreMultiStructPostIndexMask)) { - case NEON_LD1_1v_post: mnemonic = "ld1"; form = form_1v; break; - case NEON_LD1_2v_post: mnemonic = "ld1"; form = form_2v; break; - case NEON_LD1_3v_post: mnemonic = "ld1"; form = form_3v; break; - case NEON_LD1_4v_post: mnemonic = "ld1"; form = form_4v; break; - case NEON_LD2_post: mnemonic = "ld2"; form = form_2v; break; - case NEON_LD3_post: mnemonic = "ld3"; form = form_3v; break; - case NEON_LD4_post: mnemonic = "ld4"; form = form_4v; break; - case NEON_ST1_1v_post: mnemonic = "st1"; form = form_1v; break; - case NEON_ST1_2v_post: mnemonic = "st1"; form = form_2v; break; - case NEON_ST1_3v_post: mnemonic = "st1"; form = form_3v; break; - case NEON_ST1_4v_post: mnemonic = "st1"; form = form_4v; break; - case NEON_ST2_post: mnemonic = "st2"; form = form_2v; break; - case NEON_ST3_post: mnemonic = "st3"; form = form_3v; break; - case NEON_ST4_post: mnemonic = "st4"; form = form_4v; break; - default: break; - } - - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONLoadStoreSingleStruct(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONLoadStoreSingleStruct)"; - - const char *form_1b = "{'Vt.b}['IVLSLane0], ['Xns]"; - const char *form_1h = "{'Vt.h}['IVLSLane1], ['Xns]"; - const char *form_1s = "{'Vt.s}['IVLSLane2], ['Xns]"; - const char *form_1d = "{'Vt.d}['IVLSLane3], ['Xns]"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap()); - - switch (instr->Mask(NEONLoadStoreSingleStructMask)) { - case NEON_LD1_b: mnemonic = "ld1"; form = form_1b; break; - case NEON_LD1_h: mnemonic = "ld1"; form = form_1h; break; - case NEON_LD1_s: - mnemonic = "ld1"; - VIXL_STATIC_ASSERT((NEON_LD1_s | (1 << NEONLSSize_offset)) == NEON_LD1_d); - form = ((instr->NEONLSSize() & 1) == 0) ? form_1s : form_1d; - break; - case NEON_ST1_b: mnemonic = "st1"; form = form_1b; break; - case NEON_ST1_h: mnemonic = "st1"; form = form_1h; break; - case NEON_ST1_s: - mnemonic = "st1"; - VIXL_STATIC_ASSERT((NEON_ST1_s | (1 << NEONLSSize_offset)) == NEON_ST1_d); - form = ((instr->NEONLSSize() & 1) == 0) ? form_1s : form_1d; - break; - case NEON_LD1R: - mnemonic = "ld1r"; - form = "{'Vt.%s}, ['Xns]"; - break; - case NEON_LD2_b: - case NEON_ST2_b: - mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; - form = "{'Vt.b, 'Vt2.b}['IVLSLane0], ['Xns]"; - break; - case NEON_LD2_h: - case NEON_ST2_h: - mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; - form = "{'Vt.h, 'Vt2.h}['IVLSLane1], ['Xns]"; - break; - case NEON_LD2_s: - case NEON_ST2_s: - VIXL_STATIC_ASSERT((NEON_ST2_s | (1 << NEONLSSize_offset)) == NEON_ST2_d); - VIXL_STATIC_ASSERT((NEON_LD2_s | (1 << NEONLSSize_offset)) == NEON_LD2_d); - mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; - if ((instr->NEONLSSize() & 1) == 0) - form = "{'Vt.s, 'Vt2.s}['IVLSLane2], ['Xns]"; - else - form = "{'Vt.d, 'Vt2.d}['IVLSLane3], ['Xns]"; - break; - case NEON_LD2R: - mnemonic = "ld2r"; - form = "{'Vt.%s, 'Vt2.%s}, ['Xns]"; - break; - case NEON_LD3_b: - case NEON_ST3_b: - mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; - form = "{'Vt.b, 'Vt2.b, 'Vt3.b}['IVLSLane0], ['Xns]"; - break; - case NEON_LD3_h: - case NEON_ST3_h: - mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; - form = "{'Vt.h, 'Vt2.h, 'Vt3.h}['IVLSLane1], ['Xns]"; - break; - case NEON_LD3_s: - case NEON_ST3_s: - mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; - if ((instr->NEONLSSize() & 1) == 0) - form = "{'Vt.s, 'Vt2.s, 'Vt3.s}['IVLSLane2], ['Xns]"; - else - form = "{'Vt.d, 'Vt2.d, 'Vt3.d}['IVLSLane3], ['Xns]"; - break; - case NEON_LD3R: - mnemonic = "ld3r"; - form = "{'Vt.%s, 'Vt2.%s, 'Vt3.%s}, ['Xns]"; - break; - case NEON_LD4_b: - case NEON_ST4_b: - mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; - form = "{'Vt.b, 'Vt2.b, 'Vt3.b, 'Vt4.b}['IVLSLane0], ['Xns]"; - break; - case NEON_LD4_h: - case NEON_ST4_h: - mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; - form = "{'Vt.h, 'Vt2.h, 'Vt3.h, 'Vt4.h}['IVLSLane1], ['Xns]"; - break; - case NEON_LD4_s: - case NEON_ST4_s: - VIXL_STATIC_ASSERT((NEON_LD4_s | (1 << NEONLSSize_offset)) == NEON_LD4_d); - VIXL_STATIC_ASSERT((NEON_ST4_s | (1 << NEONLSSize_offset)) == NEON_ST4_d); - mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; - if ((instr->NEONLSSize() & 1) == 0) - form = "{'Vt.s, 'Vt2.s, 'Vt3.s, 'Vt4.s}['IVLSLane2], ['Xns]"; - else - form = "{'Vt.d, 'Vt2.d, 'Vt3.d, 'Vt4.d}['IVLSLane3], ['Xns]"; - break; - case NEON_LD4R: - mnemonic = "ld4r"; - form = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns]"; - break; - default: break; - } - - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONLoadStoreSingleStructPostIndex( - const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONLoadStoreSingleStructPostIndex)"; - - const char *form_1b = "{'Vt.b}['IVLSLane0], ['Xns], 'Xmb1"; - const char *form_1h = "{'Vt.h}['IVLSLane1], ['Xns], 'Xmb2"; - const char *form_1s = "{'Vt.s}['IVLSLane2], ['Xns], 'Xmb4"; - const char *form_1d = "{'Vt.d}['IVLSLane3], ['Xns], 'Xmb8"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap()); - - switch (instr->Mask(NEONLoadStoreSingleStructPostIndexMask)) { - case NEON_LD1_b_post: mnemonic = "ld1"; form = form_1b; break; - case NEON_LD1_h_post: mnemonic = "ld1"; form = form_1h; break; - case NEON_LD1_s_post: - mnemonic = "ld1"; - VIXL_STATIC_ASSERT((NEON_LD1_s | (1 << NEONLSSize_offset)) == NEON_LD1_d); - form = ((instr->NEONLSSize() & 1) == 0) ? form_1s : form_1d; - break; - case NEON_ST1_b_post: mnemonic = "st1"; form = form_1b; break; - case NEON_ST1_h_post: mnemonic = "st1"; form = form_1h; break; - case NEON_ST1_s_post: - mnemonic = "st1"; - VIXL_STATIC_ASSERT((NEON_ST1_s | (1 << NEONLSSize_offset)) == NEON_ST1_d); - form = ((instr->NEONLSSize() & 1) == 0) ? form_1s : form_1d; - break; - case NEON_LD1R_post: - mnemonic = "ld1r"; - form = "{'Vt.%s}, ['Xns], 'Xmz1"; - break; - case NEON_LD2_b_post: - case NEON_ST2_b_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; - form = "{'Vt.b, 'Vt2.b}['IVLSLane0], ['Xns], 'Xmb2"; - break; - case NEON_ST2_h_post: - case NEON_LD2_h_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; - form = "{'Vt.h, 'Vt2.h}['IVLSLane1], ['Xns], 'Xmb4"; - break; - case NEON_LD2_s_post: - case NEON_ST2_s_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld2" : "st2"; - if ((instr->NEONLSSize() & 1) == 0) - form = "{'Vt.s, 'Vt2.s}['IVLSLane2], ['Xns], 'Xmb8"; - else - form = "{'Vt.d, 'Vt2.d}['IVLSLane3], ['Xns], 'Xmb16"; - break; - case NEON_LD2R_post: - mnemonic = "ld2r"; - form = "{'Vt.%s, 'Vt2.%s}, ['Xns], 'Xmz2"; - break; - case NEON_LD3_b_post: - case NEON_ST3_b_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; - form = "{'Vt.b, 'Vt2.b, 'Vt3.b}['IVLSLane0], ['Xns], 'Xmb3"; - break; - case NEON_LD3_h_post: - case NEON_ST3_h_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; - form = "{'Vt.h, 'Vt2.h, 'Vt3.h}['IVLSLane1], ['Xns], 'Xmb6"; - break; - case NEON_LD3_s_post: - case NEON_ST3_s_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld3" : "st3"; - if ((instr->NEONLSSize() & 1) == 0) - form = "{'Vt.s, 'Vt2.s, 'Vt3.s}['IVLSLane2], ['Xns], 'Xmb12"; - else - form = "{'Vt.d, 'Vt2.d, 'Vt3.d}['IVLSLane3], ['Xns], 'Xmr3"; - break; - case NEON_LD3R_post: - mnemonic = "ld3r"; - form = "{'Vt.%s, 'Vt2.%s, 'Vt3.%s}, ['Xns], 'Xmz3"; - break; - case NEON_LD4_b_post: - case NEON_ST4_b_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; - form = "{'Vt.b, 'Vt2.b, 'Vt3.b, 'Vt4.b}['IVLSLane0], ['Xns], 'Xmb4"; - break; - case NEON_LD4_h_post: - case NEON_ST4_h_post: - mnemonic = (instr->LdStXLoad()) == 1 ? "ld4" : "st4"; - form = "{'Vt.h, 'Vt2.h, 'Vt3.h, 'Vt4.h}['IVLSLane1], ['Xns], 'Xmb8"; - break; - case NEON_LD4_s_post: - case NEON_ST4_s_post: - mnemonic = (instr->LdStXLoad() == 1) ? "ld4" : "st4"; - if ((instr->NEONLSSize() & 1) == 0) - form = "{'Vt.s, 'Vt2.s, 'Vt3.s, 'Vt4.s}['IVLSLane2], ['Xns], 'Xmb16"; - else - form = "{'Vt.d, 'Vt2.d, 'Vt3.d, 'Vt4.d}['IVLSLane3], ['Xns], 'Xmb32"; - break; - case NEON_LD4R_post: - mnemonic = "ld4r"; - form = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns], 'Xmz4"; - break; - default: break; - } - - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONModifiedImmediate(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Vt.%s, 'IVMIImm8, lsl 'IVMIShiftAmt1"; - - int cmode = instr->NEONCmode(); - int cmode_3 = (cmode >> 3) & 1; - int cmode_2 = (cmode >> 2) & 1; - int cmode_1 = (cmode >> 1) & 1; - int cmode_0 = cmode & 1; - int q = instr->NEONQ(); - int op = instr->NEONModImmOp(); - - static const NEONFormatMap map_b = { {30}, {NF_8B, NF_16B} }; - static const NEONFormatMap map_h = { {30}, {NF_4H, NF_8H} }; - static const NEONFormatMap map_s = { {30}, {NF_2S, NF_4S} }; - NEONFormatDecoder nfd(instr, &map_b); - - if (cmode_3 == 0) { - if (cmode_0 == 0) { - mnemonic = (op == 1) ? "mvni" : "movi"; - } else { // cmode<0> == '1'. - mnemonic = (op == 1) ? "bic" : "orr"; - } - nfd.SetFormatMap(0, &map_s); - } else { // cmode<3> == '1'. - if (cmode_2 == 0) { - if (cmode_0 == 0) { - mnemonic = (op == 1) ? "mvni" : "movi"; - } else { // cmode<0> == '1'. - mnemonic = (op == 1) ? "bic" : "orr"; - } - nfd.SetFormatMap(0, &map_h); - } else { // cmode<2> == '1'. - if (cmode_1 == 0) { - mnemonic = (op == 1) ? "mvni" : "movi"; - form = "'Vt.%s, 'IVMIImm8, msl 'IVMIShiftAmt2"; - nfd.SetFormatMap(0, &map_s); - } else { // cmode<1> == '1'. - if (cmode_0 == 0) { - mnemonic = "movi"; - if (op == 0) { - form = "'Vt.%s, 'IVMIImm8"; - } else { - form = (q == 0) ? "'Dd, 'IVMIImm" : "'Vt.2d, 'IVMIImm"; - } - } else { // cmode<0> == '1' - mnemonic = "fmov"; - if (op == 0) { - form = "'Vt.%s, 'IVMIImmFPSingle"; - nfd.SetFormatMap(0, &map_s); - } else { - if (q == 1) { - form = "'Vt.2d, 'IVMIImmFPDouble"; - } - } - } - } - } - } - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONScalar2RegMisc(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "%sd, %sn"; - const char *form_0 = "%sd, %sn, #0"; - const char *form_fp0 = "%sd, %sn, #0.0"; - - NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap()); - - if (instr->Mask(NEON2RegMiscOpcode) <= NEON_NEG_scalar_opcode) { - // These instructions all use a two bit size field, except NOT and RBIT, - // which use the field to encode the operation. - switch (instr->Mask(NEONScalar2RegMiscMask)) { - case NEON_CMGT_zero_scalar: mnemonic = "cmgt"; form = form_0; break; - case NEON_CMGE_zero_scalar: mnemonic = "cmge"; form = form_0; break; - case NEON_CMLE_zero_scalar: mnemonic = "cmle"; form = form_0; break; - case NEON_CMLT_zero_scalar: mnemonic = "cmlt"; form = form_0; break; - case NEON_CMEQ_zero_scalar: mnemonic = "cmeq"; form = form_0; break; - case NEON_NEG_scalar: mnemonic = "neg"; break; - case NEON_SQNEG_scalar: mnemonic = "sqneg"; break; - case NEON_ABS_scalar: mnemonic = "abs"; break; - case NEON_SQABS_scalar: mnemonic = "sqabs"; break; - case NEON_SUQADD_scalar: mnemonic = "suqadd"; break; - case NEON_USQADD_scalar: mnemonic = "usqadd"; break; - default: form = "(NEONScalar2RegMisc)"; - } - } else { - // These instructions all use a one bit size field, except SQXTUN, SQXTN - // and UQXTN, which use a two bit size field. - nfd.SetFormatMaps(nfd.FPScalarFormatMap()); - switch (instr->Mask(NEONScalar2RegMiscFPMask)) { - case NEON_FRSQRTE_scalar: mnemonic = "frsqrte"; break; - case NEON_FRECPE_scalar: mnemonic = "frecpe"; break; - case NEON_SCVTF_scalar: mnemonic = "scvtf"; break; - case NEON_UCVTF_scalar: mnemonic = "ucvtf"; break; - case NEON_FCMGT_zero_scalar: mnemonic = "fcmgt"; form = form_fp0; break; - case NEON_FCMGE_zero_scalar: mnemonic = "fcmge"; form = form_fp0; break; - case NEON_FCMLE_zero_scalar: mnemonic = "fcmle"; form = form_fp0; break; - case NEON_FCMLT_zero_scalar: mnemonic = "fcmlt"; form = form_fp0; break; - case NEON_FCMEQ_zero_scalar: mnemonic = "fcmeq"; form = form_fp0; break; - case NEON_FRECPX_scalar: mnemonic = "frecpx"; break; - case NEON_FCVTNS_scalar: mnemonic = "fcvtns"; break; - case NEON_FCVTNU_scalar: mnemonic = "fcvtnu"; break; - case NEON_FCVTPS_scalar: mnemonic = "fcvtps"; break; - case NEON_FCVTPU_scalar: mnemonic = "fcvtpu"; break; - case NEON_FCVTMS_scalar: mnemonic = "fcvtms"; break; - case NEON_FCVTMU_scalar: mnemonic = "fcvtmu"; break; - case NEON_FCVTZS_scalar: mnemonic = "fcvtzs"; break; - case NEON_FCVTZU_scalar: mnemonic = "fcvtzu"; break; - case NEON_FCVTAS_scalar: mnemonic = "fcvtas"; break; - case NEON_FCVTAU_scalar: mnemonic = "fcvtau"; break; - case NEON_FCVTXN_scalar: - nfd.SetFormatMap(0, nfd.LongScalarFormatMap()); - mnemonic = "fcvtxn"; - break; - default: - nfd.SetFormatMap(0, nfd.ScalarFormatMap()); - nfd.SetFormatMap(1, nfd.LongScalarFormatMap()); - switch (instr->Mask(NEONScalar2RegMiscMask)) { - case NEON_SQXTN_scalar: mnemonic = "sqxtn"; break; - case NEON_UQXTN_scalar: mnemonic = "uqxtn"; break; - case NEON_SQXTUN_scalar: mnemonic = "sqxtun"; break; - default: form = "(NEONScalar2RegMisc)"; - } - } - } - Format(instr, mnemonic, nfd.SubstitutePlaceholders(form)); -} - - -void Disassembler::VisitNEONScalar3Diff(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "%sd, %sn, %sm"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::LongScalarFormatMap(), - NEONFormatDecoder::ScalarFormatMap()); - - switch (instr->Mask(NEONScalar3DiffMask)) { - case NEON_SQDMLAL_scalar : mnemonic = "sqdmlal"; break; - case NEON_SQDMLSL_scalar : mnemonic = "sqdmlsl"; break; - case NEON_SQDMULL_scalar : mnemonic = "sqdmull"; break; - default: form = "(NEONScalar3Diff)"; - } - Format(instr, mnemonic, nfd.SubstitutePlaceholders(form)); -} - - -void Disassembler::VisitNEONScalar3Same(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "%sd, %sn, %sm"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap()); - - if (instr->Mask(NEONScalar3SameFPFMask) == NEONScalar3SameFPFixed) { - nfd.SetFormatMaps(nfd.FPScalarFormatMap()); - switch (instr->Mask(NEONScalar3SameFPMask)) { - case NEON_FACGE_scalar: mnemonic = "facge"; break; - case NEON_FACGT_scalar: mnemonic = "facgt"; break; - case NEON_FCMEQ_scalar: mnemonic = "fcmeq"; break; - case NEON_FCMGE_scalar: mnemonic = "fcmge"; break; - case NEON_FCMGT_scalar: mnemonic = "fcmgt"; break; - case NEON_FMULX_scalar: mnemonic = "fmulx"; break; - case NEON_FRECPS_scalar: mnemonic = "frecps"; break; - case NEON_FRSQRTS_scalar: mnemonic = "frsqrts"; break; - case NEON_FABD_scalar: mnemonic = "fabd"; break; - default: form = "(NEONScalar3Same)"; - } - } else { - switch (instr->Mask(NEONScalar3SameMask)) { - case NEON_ADD_scalar: mnemonic = "add"; break; - case NEON_SUB_scalar: mnemonic = "sub"; break; - case NEON_CMEQ_scalar: mnemonic = "cmeq"; break; - case NEON_CMGE_scalar: mnemonic = "cmge"; break; - case NEON_CMGT_scalar: mnemonic = "cmgt"; break; - case NEON_CMHI_scalar: mnemonic = "cmhi"; break; - case NEON_CMHS_scalar: mnemonic = "cmhs"; break; - case NEON_CMTST_scalar: mnemonic = "cmtst"; break; - case NEON_UQADD_scalar: mnemonic = "uqadd"; break; - case NEON_SQADD_scalar: mnemonic = "sqadd"; break; - case NEON_UQSUB_scalar: mnemonic = "uqsub"; break; - case NEON_SQSUB_scalar: mnemonic = "sqsub"; break; - case NEON_USHL_scalar: mnemonic = "ushl"; break; - case NEON_SSHL_scalar: mnemonic = "sshl"; break; - case NEON_UQSHL_scalar: mnemonic = "uqshl"; break; - case NEON_SQSHL_scalar: mnemonic = "sqshl"; break; - case NEON_URSHL_scalar: mnemonic = "urshl"; break; - case NEON_SRSHL_scalar: mnemonic = "srshl"; break; - case NEON_UQRSHL_scalar: mnemonic = "uqrshl"; break; - case NEON_SQRSHL_scalar: mnemonic = "sqrshl"; break; - case NEON_SQDMULH_scalar: mnemonic = "sqdmulh"; break; - case NEON_SQRDMULH_scalar: mnemonic = "sqrdmulh"; break; - default: form = "(NEONScalar3Same)"; - } - } - Format(instr, mnemonic, nfd.SubstitutePlaceholders(form)); -} - - -void Disassembler::VisitNEONScalarByIndexedElement(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "%sd, %sn, 'Ve.%s['IVByElemIndex]"; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap()); - bool long_instr = false; - - switch (instr->Mask(NEONScalarByIndexedElementMask)) { - case NEON_SQDMULL_byelement_scalar: - mnemonic = "sqdmull"; - long_instr = true; - break; - case NEON_SQDMLAL_byelement_scalar: - mnemonic = "sqdmlal"; - long_instr = true; - break; - case NEON_SQDMLSL_byelement_scalar: - mnemonic = "sqdmlsl"; - long_instr = true; - break; - case NEON_SQDMULH_byelement_scalar: - mnemonic = "sqdmulh"; - break; - case NEON_SQRDMULH_byelement_scalar: - mnemonic = "sqrdmulh"; - break; - default: - nfd.SetFormatMap(0, nfd.FPScalarFormatMap()); - switch (instr->Mask(NEONScalarByIndexedElementFPMask)) { - case NEON_FMUL_byelement_scalar: mnemonic = "fmul"; break; - case NEON_FMLA_byelement_scalar: mnemonic = "fmla"; break; - case NEON_FMLS_byelement_scalar: mnemonic = "fmls"; break; - case NEON_FMULX_byelement_scalar: mnemonic = "fmulx"; break; - default: form = "(NEONScalarByIndexedElement)"; - } - } - - if (long_instr) { - nfd.SetFormatMap(0, nfd.LongScalarFormatMap()); - } - - Format(instr, mnemonic, nfd.Substitute( - form, nfd.kPlaceholder, nfd.kPlaceholder, nfd.kFormat)); -} - - -void Disassembler::VisitNEONScalarCopy(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONScalarCopy)"; - - NEONFormatDecoder nfd(instr, NEONFormatDecoder::TriangularScalarFormatMap()); - - if (instr->Mask(NEONScalarCopyMask) == NEON_DUP_ELEMENT_scalar) { - mnemonic = "mov"; - form = "%sd, 'Vn.%s['IVInsIndex1]"; - } - - Format(instr, mnemonic, nfd.Substitute(form, nfd.kPlaceholder, nfd.kFormat)); -} - - -void Disassembler::VisitNEONScalarPairwise(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "%sd, 'Vn.%s"; - NEONFormatMap map = { {22}, {NF_2S, NF_2D} }; - NEONFormatDecoder nfd(instr, NEONFormatDecoder::FPScalarFormatMap(), &map); - - switch (instr->Mask(NEONScalarPairwiseMask)) { - case NEON_ADDP_scalar: mnemonic = "addp"; break; - case NEON_FADDP_scalar: mnemonic = "faddp"; break; - case NEON_FMAXP_scalar: mnemonic = "fmaxp"; break; - case NEON_FMAXNMP_scalar: mnemonic = "fmaxnmp"; break; - case NEON_FMINP_scalar: mnemonic = "fminp"; break; - case NEON_FMINNMP_scalar: mnemonic = "fminnmp"; break; - default: form = "(NEONScalarPairwise)"; - } - Format(instr, mnemonic, nfd.Substitute(form, - NEONFormatDecoder::kPlaceholder, NEONFormatDecoder::kFormat)); -} - - -void Disassembler::VisitNEONScalarShiftImmediate(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "%sd, %sn, 'Is1"; - const char *form_2 = "%sd, %sn, 'Is2"; - - static const NEONFormatMap map_shift = { - {22, 21, 20, 19}, - {NF_UNDEF, NF_B, NF_H, NF_H, NF_S, NF_S, NF_S, NF_S, - NF_D, NF_D, NF_D, NF_D, NF_D, NF_D, NF_D, NF_D} - }; - static const NEONFormatMap map_shift_narrow = { - {21, 20, 19}, - {NF_UNDEF, NF_H, NF_S, NF_S, NF_D, NF_D, NF_D, NF_D} - }; - NEONFormatDecoder nfd(instr, &map_shift); - - if (instr->ImmNEONImmh()) { // immh has to be non-zero. - switch (instr->Mask(NEONScalarShiftImmediateMask)) { - case NEON_FCVTZU_imm_scalar: mnemonic = "fcvtzu"; break; - case NEON_FCVTZS_imm_scalar: mnemonic = "fcvtzs"; break; - case NEON_SCVTF_imm_scalar: mnemonic = "scvtf"; break; - case NEON_UCVTF_imm_scalar: mnemonic = "ucvtf"; break; - case NEON_SRI_scalar: mnemonic = "sri"; break; - case NEON_SSHR_scalar: mnemonic = "sshr"; break; - case NEON_USHR_scalar: mnemonic = "ushr"; break; - case NEON_SRSHR_scalar: mnemonic = "srshr"; break; - case NEON_URSHR_scalar: mnemonic = "urshr"; break; - case NEON_SSRA_scalar: mnemonic = "ssra"; break; - case NEON_USRA_scalar: mnemonic = "usra"; break; - case NEON_SRSRA_scalar: mnemonic = "srsra"; break; - case NEON_URSRA_scalar: mnemonic = "ursra"; break; - case NEON_SHL_scalar: mnemonic = "shl"; form = form_2; break; - case NEON_SLI_scalar: mnemonic = "sli"; form = form_2; break; - case NEON_SQSHLU_scalar: mnemonic = "sqshlu"; form = form_2; break; - case NEON_SQSHL_imm_scalar: mnemonic = "sqshl"; form = form_2; break; - case NEON_UQSHL_imm_scalar: mnemonic = "uqshl"; form = form_2; break; - case NEON_UQSHRN_scalar: - mnemonic = "uqshrn"; - nfd.SetFormatMap(1, &map_shift_narrow); - break; - case NEON_UQRSHRN_scalar: - mnemonic = "uqrshrn"; - nfd.SetFormatMap(1, &map_shift_narrow); - break; - case NEON_SQSHRN_scalar: - mnemonic = "sqshrn"; - nfd.SetFormatMap(1, &map_shift_narrow); - break; - case NEON_SQRSHRN_scalar: - mnemonic = "sqrshrn"; - nfd.SetFormatMap(1, &map_shift_narrow); - break; - case NEON_SQSHRUN_scalar: - mnemonic = "sqshrun"; - nfd.SetFormatMap(1, &map_shift_narrow); - break; - case NEON_SQRSHRUN_scalar: - mnemonic = "sqrshrun"; - nfd.SetFormatMap(1, &map_shift_narrow); - break; - default: - form = "(NEONScalarShiftImmediate)"; - } - } else { - form = "(NEONScalarShiftImmediate)"; - } - Format(instr, mnemonic, nfd.SubstitutePlaceholders(form)); -} - - -void Disassembler::VisitNEONShiftImmediate(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Vd.%s, 'Vn.%s, 'Is1"; - const char *form_shift_2 = "'Vd.%s, 'Vn.%s, 'Is2"; - const char *form_xtl = "'Vd.%s, 'Vn.%s"; - - // 0001->8H, 001x->4S, 01xx->2D, all others undefined. - static const NEONFormatMap map_shift_ta = { - {22, 21, 20, 19}, - {NF_UNDEF, NF_8H, NF_4S, NF_4S, NF_2D, NF_2D, NF_2D, NF_2D} - }; - - // 00010->8B, 00011->16B, 001x0->4H, 001x1->8H, - // 01xx0->2S, 01xx1->4S, 1xxx1->2D, all others undefined. - static const NEONFormatMap map_shift_tb = { - {22, 21, 20, 19, 30}, - {NF_UNDEF, NF_UNDEF, NF_8B, NF_16B, NF_4H, NF_8H, NF_4H, NF_8H, - NF_2S, NF_4S, NF_2S, NF_4S, NF_2S, NF_4S, NF_2S, NF_4S, - NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, - NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, NF_UNDEF, NF_2D, NF_UNDEF, NF_2D} - }; - - NEONFormatDecoder nfd(instr, &map_shift_tb); - - if (instr->ImmNEONImmh()) { // immh has to be non-zero. - switch (instr->Mask(NEONShiftImmediateMask)) { - case NEON_SQSHLU: mnemonic = "sqshlu"; form = form_shift_2; break; - case NEON_SQSHL_imm: mnemonic = "sqshl"; form = form_shift_2; break; - case NEON_UQSHL_imm: mnemonic = "uqshl"; form = form_shift_2; break; - case NEON_SHL: mnemonic = "shl"; form = form_shift_2; break; - case NEON_SLI: mnemonic = "sli"; form = form_shift_2; break; - case NEON_SCVTF_imm: mnemonic = "scvtf"; break; - case NEON_UCVTF_imm: mnemonic = "ucvtf"; break; - case NEON_FCVTZU_imm: mnemonic = "fcvtzu"; break; - case NEON_FCVTZS_imm: mnemonic = "fcvtzs"; break; - case NEON_SRI: mnemonic = "sri"; break; - case NEON_SSHR: mnemonic = "sshr"; break; - case NEON_USHR: mnemonic = "ushr"; break; - case NEON_SRSHR: mnemonic = "srshr"; break; - case NEON_URSHR: mnemonic = "urshr"; break; - case NEON_SSRA: mnemonic = "ssra"; break; - case NEON_USRA: mnemonic = "usra"; break; - case NEON_SRSRA: mnemonic = "srsra"; break; - case NEON_URSRA: mnemonic = "ursra"; break; - case NEON_SHRN: - mnemonic = instr->Mask(NEON_Q) ? "shrn2" : "shrn"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_RSHRN: - mnemonic = instr->Mask(NEON_Q) ? "rshrn2" : "rshrn"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_UQSHRN: - mnemonic = instr->Mask(NEON_Q) ? "uqshrn2" : "uqshrn"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_UQRSHRN: - mnemonic = instr->Mask(NEON_Q) ? "uqrshrn2" : "uqrshrn"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_SQSHRN: - mnemonic = instr->Mask(NEON_Q) ? "sqshrn2" : "sqshrn"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_SQRSHRN: - mnemonic = instr->Mask(NEON_Q) ? "sqrshrn2" : "sqrshrn"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_SQSHRUN: - mnemonic = instr->Mask(NEON_Q) ? "sqshrun2" : "sqshrun"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_SQRSHRUN: - mnemonic = instr->Mask(NEON_Q) ? "sqrshrun2" : "sqrshrun"; - nfd.SetFormatMap(1, &map_shift_ta); - break; - case NEON_SSHLL: - nfd.SetFormatMap(0, &map_shift_ta); - if (instr->ImmNEONImmb() == 0 && - CountSetBits(instr->ImmNEONImmh(), 32) == 1) { // sxtl variant. - form = form_xtl; - mnemonic = instr->Mask(NEON_Q) ? "sxtl2" : "sxtl"; - } else { // sshll variant. - form = form_shift_2; - mnemonic = instr->Mask(NEON_Q) ? "sshll2" : "sshll"; - } - break; - case NEON_USHLL: - nfd.SetFormatMap(0, &map_shift_ta); - if (instr->ImmNEONImmb() == 0 && - CountSetBits(instr->ImmNEONImmh(), 32) == 1) { // uxtl variant. - form = form_xtl; - mnemonic = instr->Mask(NEON_Q) ? "uxtl2" : "uxtl"; - } else { // ushll variant. - form = form_shift_2; - mnemonic = instr->Mask(NEON_Q) ? "ushll2" : "ushll"; - } - break; - default: form = "(NEONShiftImmediate)"; - } - } else { - form = "(NEONShiftImmediate)"; - } - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitNEONTable(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(NEONTable)"; - const char form_1v[] = "'Vd.%%s, {'Vn.16b}, 'Vm.%%s"; - const char form_2v[] = "'Vd.%%s, {'Vn.16b, v%d.16b}, 'Vm.%%s"; - const char form_3v[] = "'Vd.%%s, {'Vn.16b, v%d.16b, v%d.16b}, 'Vm.%%s"; - const char form_4v[] = - "'Vd.%%s, {'Vn.16b, v%d.16b, v%d.16b, v%d.16b}, 'Vm.%%s"; - static const NEONFormatMap map_b = { {30}, {NF_8B, NF_16B} }; - NEONFormatDecoder nfd(instr, &map_b); - - switch (instr->Mask(NEONTableMask)) { - case NEON_TBL_1v: mnemonic = "tbl"; form = form_1v; break; - case NEON_TBL_2v: mnemonic = "tbl"; form = form_2v; break; - case NEON_TBL_3v: mnemonic = "tbl"; form = form_3v; break; - case NEON_TBL_4v: mnemonic = "tbl"; form = form_4v; break; - case NEON_TBX_1v: mnemonic = "tbx"; form = form_1v; break; - case NEON_TBX_2v: mnemonic = "tbx"; form = form_2v; break; - case NEON_TBX_3v: mnemonic = "tbx"; form = form_3v; break; - case NEON_TBX_4v: mnemonic = "tbx"; form = form_4v; break; - default: break; - } - - char re_form[sizeof(form_4v) + 6]; - int reg_num = instr->Rn(); - snprintf(re_form, sizeof(re_form), form, - (reg_num + 1) % kNumberOfVRegisters, - (reg_num + 2) % kNumberOfVRegisters, - (reg_num + 3) % kNumberOfVRegisters); - - Format(instr, mnemonic, nfd.Substitute(re_form)); -} - - -void Disassembler::VisitNEONPerm(const Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s"; - NEONFormatDecoder nfd(instr); - - switch (instr->Mask(NEONPermMask)) { - case NEON_TRN1: mnemonic = "trn1"; break; - case NEON_TRN2: mnemonic = "trn2"; break; - case NEON_UZP1: mnemonic = "uzp1"; break; - case NEON_UZP2: mnemonic = "uzp2"; break; - case NEON_ZIP1: mnemonic = "zip1"; break; - case NEON_ZIP2: mnemonic = "zip2"; break; - default: form = "(NEONPerm)"; - } - Format(instr, mnemonic, nfd.Substitute(form)); -} - - -void Disassembler::VisitUnimplemented(const Instruction* instr) { - Format(instr, "unimplemented", "(Unimplemented)"); -} - - -void Disassembler::VisitUnallocated(const Instruction* instr) { - Format(instr, "unallocated", "(Unallocated)"); -} - - -void Disassembler::ProcessOutput(const Instruction* /*instr*/) { - // The base disasm does nothing more than disassembling into a buffer. -} - - -void Disassembler::AppendRegisterNameToOutput(const Instruction* instr, - const CPURegister& reg) { - USE(instr); - VIXL_ASSERT(reg.IsValid()); - char reg_char; - - if (reg.IsRegister()) { - reg_char = reg.Is64Bits() ? 'x' : 'w'; - } else { - VIXL_ASSERT(reg.IsVRegister()); - switch (reg.SizeInBits()) { - case kBRegSize: reg_char = 'b'; break; - case kHRegSize: reg_char = 'h'; break; - case kSRegSize: reg_char = 's'; break; - case kDRegSize: reg_char = 'd'; break; - default: - VIXL_ASSERT(reg.Is128Bits()); - reg_char = 'q'; - } - } - - if (reg.IsVRegister() || !(reg.Aliases(sp) || reg.Aliases(xzr))) { - // A core or scalar/vector register: [wx]0 - 30, [bhsdq]0 - 31. - AppendToOutput("%c%d", reg_char, reg.code()); - } else if (reg.Aliases(sp)) { - // Disassemble w31/x31 as stack pointer wsp/sp. - AppendToOutput("%s", reg.Is64Bits() ? "sp" : "wsp"); - } else { - // Disassemble w31/x31 as zero register wzr/xzr. - AppendToOutput("%czr", reg_char); - } -} - - -void Disassembler::AppendPCRelativeOffsetToOutput(const Instruction* instr, - int64_t offset) { - USE(instr); - uint64_t abs_offset = offset; - char sign = (offset < 0) ? '-' : '+'; - if (offset < 0) { - abs_offset = -abs_offset; - } - AppendToOutput("#%c0x%" PRIx64, sign, abs_offset); -} - - -void Disassembler::AppendAddressToOutput(const Instruction* instr, - const void* addr) { - USE(instr); - AppendToOutput("(addr 0x%" PRIxPTR ")", reinterpret_cast(addr)); -} - - -void Disassembler::AppendCodeAddressToOutput(const Instruction* instr, - const void* addr) { - AppendAddressToOutput(instr, addr); -} - - -void Disassembler::AppendDataAddressToOutput(const Instruction* instr, - const void* addr) { - AppendAddressToOutput(instr, addr); -} - - -void Disassembler::AppendCodeRelativeAddressToOutput(const Instruction* instr, - const void* addr) { - USE(instr); - int64_t rel_addr = CodeRelativeAddress(addr); - if (rel_addr >= 0) { - AppendToOutput("(addr 0x%" PRIx64 ")", rel_addr); - } else { - AppendToOutput("(addr -0x%" PRIx64 ")", -rel_addr); - } -} - - -void Disassembler::AppendCodeRelativeCodeAddressToOutput( - const Instruction* instr, const void* addr) { - AppendCodeRelativeAddressToOutput(instr, addr); -} - - -void Disassembler::AppendCodeRelativeDataAddressToOutput( - const Instruction* instr, const void* addr) { - AppendCodeRelativeAddressToOutput(instr, addr); -} - - -void Disassembler::MapCodeAddress(int64_t base_address, - const Instruction* instr_address) { - set_code_address_offset( - base_address - reinterpret_cast(instr_address)); -} -int64_t Disassembler::CodeRelativeAddress(const void* addr) { - return reinterpret_cast(addr) + code_address_offset(); -} - - -void Disassembler::Format(const Instruction* instr, const char* mnemonic, - const char* format) { - VIXL_ASSERT(mnemonic != NULL); - ResetOutput(); - Substitute(instr, mnemonic); - if (format != NULL) { - VIXL_ASSERT(buffer_pos_ < buffer_size_); - buffer_[buffer_pos_++] = ' '; - Substitute(instr, format); - } - VIXL_ASSERT(buffer_pos_ < buffer_size_); - buffer_[buffer_pos_] = 0; - ProcessOutput(instr); -} - - -void Disassembler::Substitute(const Instruction* instr, const char* string) { - char chr = *string++; - while (chr != '\0') { - if (chr == '\'') { - string += SubstituteField(instr, string); - } else { - VIXL_ASSERT(buffer_pos_ < buffer_size_); - buffer_[buffer_pos_++] = chr; - } - chr = *string++; - } -} - - -int Disassembler::SubstituteField(const Instruction* instr, - const char* format) { - switch (format[0]) { - // NB. The remaining substitution prefix characters are: GJKUZ. - case 'R': // Register. X or W, selected by sf bit. - case 'F': // FP register. S or D, selected by type field. - case 'V': // Vector register, V, vector format. - case 'W': - case 'X': - case 'B': - case 'H': - case 'S': - case 'D': - case 'Q': return SubstituteRegisterField(instr, format); - case 'I': return SubstituteImmediateField(instr, format); - case 'L': return SubstituteLiteralField(instr, format); - case 'N': return SubstituteShiftField(instr, format); - case 'P': return SubstitutePrefetchField(instr, format); - case 'C': return SubstituteConditionField(instr, format); - case 'E': return SubstituteExtendField(instr, format); - case 'A': return SubstitutePCRelAddressField(instr, format); - case 'T': return SubstituteBranchTargetField(instr, format); - case 'O': return SubstituteLSRegOffsetField(instr, format); - case 'M': return SubstituteBarrierField(instr, format); - case 'K': return SubstituteCrField(instr, format); - case 'G': return SubstituteSysOpField(instr, format); - default: { - VIXL_UNREACHABLE(); - return 1; - } - } -} - - -int Disassembler::SubstituteRegisterField(const Instruction* instr, - const char* format) { - char reg_prefix = format[0]; - unsigned reg_num = 0; - unsigned field_len = 2; - - switch (format[1]) { - case 'd': - reg_num = instr->Rd(); - if (format[2] == 'q') { - reg_prefix = instr->NEONQ() ? 'X' : 'W'; - field_len = 3; - } - break; - case 'n': reg_num = instr->Rn(); break; - case 'm': - reg_num = instr->Rm(); - switch (format[2]) { - // Handle registers tagged with b (bytes), z (instruction), or - // r (registers), used for address updates in - // NEON load/store instructions. - case 'r': - case 'b': - case 'z': { - field_len = 3; - char* eimm; - int imm = static_cast(strtol(&format[3], &eimm, 10)); - field_len += eimm - &format[3]; - if (reg_num == 31) { - switch (format[2]) { - case 'z': - imm *= (1 << instr->NEONLSSize()); - break; - case 'r': - imm *= (instr->NEONQ() == 0) ? kDRegSizeInBytes - : kQRegSizeInBytes; - break; - case 'b': - break; - } - AppendToOutput("#%d", imm); - return field_len; - } - break; - } - } - break; - case 'e': - // This is register Rm, but using a 4-bit specifier. Used in NEON - // by-element instructions. - reg_num = (instr->Rm() & 0xf); - break; - case 'a': reg_num = instr->Ra(); break; - case 's': reg_num = instr->Rs(); break; - case 't': - reg_num = instr->Rt(); - if (format[0] == 'V') { - if ((format[2] >= '2') && (format[2] <= '4')) { - // Handle consecutive vector register specifiers Vt2, Vt3 and Vt4. - reg_num = (reg_num + format[2] - '1') % 32; - field_len = 3; - } - } else { - if (format[2] == '2') { - // Handle register specifier Rt2. - reg_num = instr->Rt2(); - field_len = 3; - } - } - break; - default: VIXL_UNREACHABLE(); - } - - // Increase field length for registers tagged as stack. - if (format[2] == 's') { - field_len = 3; - } - - CPURegister::RegisterType reg_type = CPURegister::kRegister; - unsigned reg_size = kXRegSize; - - if (reg_prefix == 'R') { - reg_prefix = instr->SixtyFourBits() ? 'X' : 'W'; - } else if (reg_prefix == 'F') { - reg_prefix = ((instr->FPType() & 1) == 0) ? 'S' : 'D'; - } - - switch (reg_prefix) { - case 'W': - reg_type = CPURegister::kRegister; reg_size = kWRegSize; break; - case 'X': - reg_type = CPURegister::kRegister; reg_size = kXRegSize; break; - case 'B': - reg_type = CPURegister::kVRegister; reg_size = kBRegSize; break; - case 'H': - reg_type = CPURegister::kVRegister; reg_size = kHRegSize; break; - case 'S': - reg_type = CPURegister::kVRegister; reg_size = kSRegSize; break; - case 'D': - reg_type = CPURegister::kVRegister; reg_size = kDRegSize; break; - case 'Q': - reg_type = CPURegister::kVRegister; reg_size = kQRegSize; break; - case 'V': - AppendToOutput("v%d", reg_num); - return field_len; - default: - VIXL_UNREACHABLE(); - } - - if ((reg_type == CPURegister::kRegister) && - (reg_num == kZeroRegCode) && (format[2] == 's')) { - reg_num = kSPRegInternalCode; - } - - AppendRegisterNameToOutput(instr, CPURegister(reg_num, reg_size, reg_type)); - - return field_len; -} - - -int Disassembler::SubstituteImmediateField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'I'); - - switch (format[1]) { - case 'M': { // IMoveImm, IMoveNeg or IMoveLSL. - if (format[5] == 'L') { - AppendToOutput("#0x%" PRIx32, instr->ImmMoveWide()); - if (instr->ShiftMoveWide() > 0) { - AppendToOutput(", lsl #%" PRId32, 16 * instr->ShiftMoveWide()); - } - } else { - VIXL_ASSERT((format[5] == 'I') || (format[5] == 'N')); - uint64_t imm = static_cast(instr->ImmMoveWide()) << - (16 * instr->ShiftMoveWide()); - if (format[5] == 'N') - imm = ~imm; - if (!instr->SixtyFourBits()) - imm &= UINT64_C(0xffffffff); - AppendToOutput("#0x%" PRIx64, imm); - } - return 8; - } - case 'L': { - switch (format[2]) { - case 'L': { // ILLiteral - Immediate Load Literal. - AppendToOutput("pc%+" PRId32, - instr->ImmLLiteral() << kLiteralEntrySizeLog2); - return 9; - } - case 'S': { // ILS - Immediate Load/Store. - if (instr->ImmLS() != 0) { - AppendToOutput(", #%" PRId32, instr->ImmLS()); - } - return 3; - } - case 'P': { // ILPx - Immediate Load/Store Pair, x = access size. - if (instr->ImmLSPair() != 0) { - // format[3] is the scale value. Convert to a number. - int scale = 1 << (format[3] - '0'); - AppendToOutput(", #%" PRId32, instr->ImmLSPair() * scale); - } - return 4; - } - case 'U': { // ILU - Immediate Load/Store Unsigned. - if (instr->ImmLSUnsigned() != 0) { - int shift = instr->SizeLS(); - AppendToOutput(", #%" PRId32, instr->ImmLSUnsigned() << shift); - } - return 3; - } - default: { - VIXL_UNIMPLEMENTED(); - return 0; - } - } - } - case 'C': { // ICondB - Immediate Conditional Branch. - int64_t offset = instr->ImmCondBranch() << 2; - AppendPCRelativeOffsetToOutput(instr, offset); - return 6; - } - case 'A': { // IAddSub. - VIXL_ASSERT(instr->ShiftAddSub() <= 1); - int64_t imm = instr->ImmAddSub() << (12 * instr->ShiftAddSub()); - AppendToOutput("#0x%" PRIx64 " (%" PRId64 ")", imm, imm); - return 7; - } - case 'F': { // IFPSingle, IFPDouble or IFPFBits. - if (format[3] == 'F') { // IFPFbits. - AppendToOutput("#%" PRId32, 64 - instr->FPScale()); - return 8; - } else { - AppendToOutput("#0x%" PRIx32 " (%.4f)", instr->ImmFP(), - format[3] == 'S' ? instr->ImmFP32() : instr->ImmFP64()); - return 9; - } - } - case 'T': { // ITri - Immediate Triangular Encoded. - AppendToOutput("#0x%" PRIx64, instr->ImmLogical()); - return 4; - } - case 'N': { // INzcv. - int nzcv = (instr->Nzcv() << Flags_offset); - AppendToOutput("#%c%c%c%c", ((nzcv & NFlag) == 0) ? 'n' : 'N', - ((nzcv & ZFlag) == 0) ? 'z' : 'Z', - ((nzcv & CFlag) == 0) ? 'c' : 'C', - ((nzcv & VFlag) == 0) ? 'v' : 'V'); - return 5; - } - case 'P': { // IP - Conditional compare. - AppendToOutput("#%" PRId32, instr->ImmCondCmp()); - return 2; - } - case 'B': { // Bitfields. - return SubstituteBitfieldImmediateField(instr, format); - } - case 'E': { // IExtract. - AppendToOutput("#%" PRId32, instr->ImmS()); - return 8; - } - case 'S': { // IS - Test and branch bit. - AppendToOutput("#%" PRId32, (instr->ImmTestBranchBit5() << 5) | - instr->ImmTestBranchBit40()); - return 2; - } - case 's': { // Is - Shift (immediate). - switch (format[2]) { - case '1': { // Is1 - SSHR. - int shift = 16 << HighestSetBitPosition(instr->ImmNEONImmh()); - shift -= instr->ImmNEONImmhImmb(); - AppendToOutput("#%d", shift); - return 3; - } - case '2': { // Is2 - SLI. - int shift = instr->ImmNEONImmhImmb(); - shift -= 8 << HighestSetBitPosition(instr->ImmNEONImmh()); - AppendToOutput("#%d", shift); - return 3; - } - default: { - VIXL_UNIMPLEMENTED(); - return 0; - } - } - } - case 'D': { // IDebug - HLT and BRK instructions. - AppendToOutput("#0x%" PRIx32, instr->ImmException()); - return 6; - } - case 'V': { // Immediate Vector. - switch (format[2]) { - case 'E': { // IVExtract. - AppendToOutput("#%" PRId32, instr->ImmNEONExt()); - return 9; - } - case 'B': { // IVByElemIndex. - int vm_index = (instr->NEONH() << 1) | instr->NEONL(); - if (instr->NEONSize() == 1) { - vm_index = (vm_index << 1) | instr->NEONM(); - } - AppendToOutput("%d", vm_index); - return strlen("IVByElemIndex"); - } - case 'I': { // INS element. - if (strncmp(format, "IVInsIndex", strlen("IVInsIndex")) == 0) { - int rd_index, rn_index; - int imm5 = instr->ImmNEON5(); - int imm4 = instr->ImmNEON4(); - int tz = CountTrailingZeros(imm5, 32); - rd_index = imm5 >> (tz + 1); - rn_index = imm4 >> tz; - if (strncmp(format, "IVInsIndex1", strlen("IVInsIndex1")) == 0) { - AppendToOutput("%d", rd_index); - return strlen("IVInsIndex1"); - } else if (strncmp(format, "IVInsIndex2", - strlen("IVInsIndex2")) == 0) { - AppendToOutput("%d", rn_index); - return strlen("IVInsIndex2"); - } else { - VIXL_UNIMPLEMENTED(); - return 0; - } - } - VIXL_FALLTHROUGH(); - } - case 'L': { // IVLSLane[0123] - suffix indicates access size shift. - AppendToOutput("%d", instr->NEONLSIndex(format[8] - '0')); - return 9; - } - case 'M': { // Modified Immediate cases. - if (strncmp(format, - "IVMIImmFPSingle", - strlen("IVMIImmFPSingle")) == 0) { - AppendToOutput("#0x%" PRIx32 " (%.4f)", instr->ImmNEONabcdefgh(), - instr->ImmNEONFP32()); - return strlen("IVMIImmFPSingle"); - } else if (strncmp(format, - "IVMIImmFPDouble", - strlen("IVMIImmFPDouble")) == 0) { - AppendToOutput("#0x%" PRIx32 " (%.4f)", instr->ImmNEONabcdefgh(), - instr->ImmNEONFP64()); - return strlen("IVMIImmFPDouble"); - } else if (strncmp(format, "IVMIImm8", strlen("IVMIImm8")) == 0) { - uint64_t imm8 = instr->ImmNEONabcdefgh(); - AppendToOutput("#0x%" PRIx64, imm8); - return strlen("IVMIImm8"); - } else if (strncmp(format, "IVMIImm", strlen("IVMIImm")) == 0) { - uint64_t imm8 = instr->ImmNEONabcdefgh(); - uint64_t imm = 0; - for (int i = 0; i < 8; ++i) { - if (imm8 & (1 << i)) { - imm |= (UINT64_C(0xff) << (8 * i)); - } - } - AppendToOutput("#0x%" PRIx64, imm); - return strlen("IVMIImm"); - } else if (strncmp(format, "IVMIShiftAmt1", - strlen("IVMIShiftAmt1")) == 0) { - int cmode = instr->NEONCmode(); - int shift_amount = 8 * ((cmode >> 1) & 3); - AppendToOutput("#%d", shift_amount); - return strlen("IVMIShiftAmt1"); - } else if (strncmp(format, "IVMIShiftAmt2", - strlen("IVMIShiftAmt2")) == 0) { - int cmode = instr->NEONCmode(); - int shift_amount = 8 << (cmode & 1); - AppendToOutput("#%d", shift_amount); - return strlen("IVMIShiftAmt2"); - } else { - VIXL_UNIMPLEMENTED(); - return 0; - } - } - default: { - VIXL_UNIMPLEMENTED(); - return 0; - } - } - } - case 'X': { // IX - CLREX instruction. - AppendToOutput("#0x%" PRIx32, instr->CRm()); - return 2; - } - default: { - VIXL_UNIMPLEMENTED(); - return 0; - } - } -} - - -int Disassembler::SubstituteBitfieldImmediateField(const Instruction* instr, - const char* format) { - VIXL_ASSERT((format[0] == 'I') && (format[1] == 'B')); - unsigned r = instr->ImmR(); - unsigned s = instr->ImmS(); - - switch (format[2]) { - case 'r': { // IBr. - AppendToOutput("#%d", r); - return 3; - } - case 's': { // IBs+1 or IBs-r+1. - if (format[3] == '+') { - AppendToOutput("#%d", s + 1); - return 5; - } else { - VIXL_ASSERT(format[3] == '-'); - AppendToOutput("#%d", s - r + 1); - return 7; - } - } - case 'Z': { // IBZ-r. - VIXL_ASSERT((format[3] == '-') && (format[4] == 'r')); - unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize; - AppendToOutput("#%d", reg_size - r); - return 5; - } - default: { - VIXL_UNREACHABLE(); - return 0; - } - } -} - - -int Disassembler::SubstituteLiteralField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(strncmp(format, "LValue", 6) == 0); - USE(format); - - const void * address = instr->LiteralAddress(); - switch (instr->Mask(LoadLiteralMask)) { - case LDR_w_lit: - case LDR_x_lit: - case LDRSW_x_lit: - case LDR_s_lit: - case LDR_d_lit: - case LDR_q_lit: - AppendCodeRelativeDataAddressToOutput(instr, address); - break; - case PRFM_lit: { - // Use the prefetch hint to decide how to print the address. - switch (instr->PrefetchHint()) { - case 0x0: // PLD: prefetch for load. - case 0x2: // PST: prepare for store. - AppendCodeRelativeDataAddressToOutput(instr, address); - break; - case 0x1: // PLI: preload instructions. - AppendCodeRelativeCodeAddressToOutput(instr, address); - break; - case 0x3: // Unallocated hint. - AppendCodeRelativeAddressToOutput(instr, address); - break; - } - break; - } - default: - VIXL_UNREACHABLE(); - } - - return 6; -} - - -int Disassembler::SubstituteShiftField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'N'); - VIXL_ASSERT(instr->ShiftDP() <= 0x3); - - switch (format[1]) { - case 'D': { // HDP. - VIXL_ASSERT(instr->ShiftDP() != ROR); - VIXL_FALLTHROUGH(); - } - case 'L': { // HLo. - if (instr->ImmDPShift() != 0) { - const char* shift_type[] = {"lsl", "lsr", "asr", "ror"}; - AppendToOutput(", %s #%" PRId32, shift_type[instr->ShiftDP()], - instr->ImmDPShift()); - } - return 3; - } - default: - VIXL_UNIMPLEMENTED(); - return 0; - } -} - - -int Disassembler::SubstituteConditionField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'C'); - const char* condition_code[] = { "eq", "ne", "hs", "lo", - "mi", "pl", "vs", "vc", - "hi", "ls", "ge", "lt", - "gt", "le", "al", "nv" }; - int cond; - switch (format[1]) { - case 'B': cond = instr->ConditionBranch(); break; - case 'I': { - cond = InvertCondition(static_cast(instr->Condition())); - break; - } - default: cond = instr->Condition(); - } - AppendToOutput("%s", condition_code[cond]); - return 4; -} - - -int Disassembler::SubstitutePCRelAddressField(const Instruction* instr, - const char* format) { - VIXL_ASSERT((strcmp(format, "AddrPCRelByte") == 0) || // Used by `adr`. - (strcmp(format, "AddrPCRelPage") == 0)); // Used by `adrp`. - - int64_t offset = instr->ImmPCRel(); - - // Compute the target address based on the effective address (after applying - // code_address_offset). This is required for correct behaviour of adrp. - const Instruction* base = instr + code_address_offset(); - if (format[9] == 'P') { - offset *= kPageSize; - base = AlignDown(base, kPageSize); - } - // Strip code_address_offset before printing, so we can use the - // semantically-correct AppendCodeRelativeAddressToOutput. - const void* target = - reinterpret_cast(base + offset - code_address_offset()); - - AppendPCRelativeOffsetToOutput(instr, offset); - AppendToOutput(" "); - AppendCodeRelativeAddressToOutput(instr, target); - return 13; -} - - -int Disassembler::SubstituteBranchTargetField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(strncmp(format, "TImm", 4) == 0); - - int64_t offset = 0; - switch (format[5]) { - // BImmUncn - unconditional branch immediate. - case 'n': offset = instr->ImmUncondBranch(); break; - // BImmCond - conditional branch immediate. - case 'o': offset = instr->ImmCondBranch(); break; - // BImmCmpa - compare and branch immediate. - case 'm': offset = instr->ImmCmpBranch(); break; - // BImmTest - test and branch immediate. - case 'e': offset = instr->ImmTestBranch(); break; - default: VIXL_UNIMPLEMENTED(); - } - offset <<= kInstructionSizeLog2; - const void* target_address = reinterpret_cast(instr + offset); - VIXL_STATIC_ASSERT(sizeof(*instr) == 1); - - AppendPCRelativeOffsetToOutput(instr, offset); - AppendToOutput(" "); - AppendCodeRelativeCodeAddressToOutput(instr, target_address); - - return 8; -} - - -int Disassembler::SubstituteExtendField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(strncmp(format, "Ext", 3) == 0); - VIXL_ASSERT(instr->ExtendMode() <= 7); - USE(format); - - const char* extend_mode[] = { "uxtb", "uxth", "uxtw", "uxtx", - "sxtb", "sxth", "sxtw", "sxtx" }; - - // If rd or rn is SP, uxtw on 32-bit registers and uxtx on 64-bit - // registers becomes lsl. - if (((instr->Rd() == kZeroRegCode) || (instr->Rn() == kZeroRegCode)) && - (((instr->ExtendMode() == UXTW) && (instr->SixtyFourBits() == 0)) || - (instr->ExtendMode() == UXTX))) { - if (instr->ImmExtendShift() > 0) { - AppendToOutput(", lsl #%" PRId32, instr->ImmExtendShift()); - } - } else { - AppendToOutput(", %s", extend_mode[instr->ExtendMode()]); - if (instr->ImmExtendShift() > 0) { - AppendToOutput(" #%" PRId32, instr->ImmExtendShift()); - } - } - return 3; -} - - -int Disassembler::SubstituteLSRegOffsetField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(strncmp(format, "Offsetreg", 9) == 0); - const char* extend_mode[] = { "undefined", "undefined", "uxtw", "lsl", - "undefined", "undefined", "sxtw", "sxtx" }; - USE(format); - - unsigned shift = instr->ImmShiftLS(); - Extend ext = static_cast(instr->ExtendMode()); - char reg_type = ((ext == UXTW) || (ext == SXTW)) ? 'w' : 'x'; - - unsigned rm = instr->Rm(); - if (rm == kZeroRegCode) { - AppendToOutput("%czr", reg_type); - } else { - AppendToOutput("%c%d", reg_type, rm); - } - - // Extend mode UXTX is an alias for shift mode LSL here. - if (!((ext == UXTX) && (shift == 0))) { - AppendToOutput(", %s", extend_mode[ext]); - if (shift != 0) { - AppendToOutput(" #%d", instr->SizeLS()); - } - } - return 9; -} - - -int Disassembler::SubstitutePrefetchField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'P'); - USE(format); - - static const char* hints[] = {"ld", "li", "st"}; - static const char* stream_options[] = {"keep", "strm"}; - - unsigned hint = instr->PrefetchHint(); - unsigned target = instr->PrefetchTarget() + 1; - unsigned stream = instr->PrefetchStream(); - - if ((hint >= (sizeof(hints) / sizeof(hints[0]))) || (target > 3)) { - // Unallocated prefetch operations. - int prefetch_mode = instr->ImmPrefetchOperation(); - AppendToOutput("#0b%c%c%c%c%c", - (prefetch_mode & (1 << 4)) ? '1' : '0', - (prefetch_mode & (1 << 3)) ? '1' : '0', - (prefetch_mode & (1 << 2)) ? '1' : '0', - (prefetch_mode & (1 << 1)) ? '1' : '0', - (prefetch_mode & (1 << 0)) ? '1' : '0'); - } else { - VIXL_ASSERT(stream < (sizeof(stream_options) / sizeof(stream_options[0]))); - AppendToOutput("p%sl%d%s", hints[hint], target, stream_options[stream]); - } - return 6; -} - -int Disassembler::SubstituteBarrierField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'M'); - USE(format); - - static const char* options[4][4] = { - { "sy (0b0000)", "oshld", "oshst", "osh" }, - { "sy (0b0100)", "nshld", "nshst", "nsh" }, - { "sy (0b1000)", "ishld", "ishst", "ish" }, - { "sy (0b1100)", "ld", "st", "sy" } - }; - int domain = instr->ImmBarrierDomain(); - int type = instr->ImmBarrierType(); - - AppendToOutput("%s", options[domain][type]); - return 1; -} - -int Disassembler::SubstituteSysOpField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'G'); - int op = -1; - switch (format[1]) { - case '1': op = instr->SysOp1(); break; - case '2': op = instr->SysOp2(); break; - default: - VIXL_UNREACHABLE(); - } - AppendToOutput("#%d", op); - return 2; -} - -int Disassembler::SubstituteCrField(const Instruction* instr, - const char* format) { - VIXL_ASSERT(format[0] == 'K'); - int cr = -1; - switch (format[1]) { - case 'n': cr = instr->CRn(); break; - case 'm': cr = instr->CRm(); break; - default: - VIXL_UNREACHABLE(); - } - AppendToOutput("C%d", cr); - return 2; -} - -void Disassembler::ResetOutput() { - buffer_pos_ = 0; - buffer_[buffer_pos_] = 0; -} - - -void Disassembler::AppendToOutput(const char* format, ...) { - va_list args; - va_start(args, format); - buffer_pos_ += vsnprintf(&buffer_[buffer_pos_], buffer_size_ - buffer_pos_, - format, args); - va_end(args); -} - - -void PrintDisassembler::ProcessOutput(const Instruction* instr) { - fprintf(stream_, "0x%016" PRIx64 " %08" PRIx32 "\t\t%s\n", - reinterpret_cast(instr), - instr->InstructionBits(), - GetOutput()); -} - -} // namespace vixl diff --git a/disas/libvixl/vixl/a64/disasm-a64.h b/disas/libvixl/vixl/a64/disasm-a64.h deleted file mode 100644 index 930df6ea6a..0000000000 --- a/disas/libvixl/vixl/a64/disasm-a64.h +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#ifndef VIXL_A64_DISASM_A64_H -#define VIXL_A64_DISASM_A64_H - -#include "vixl/globals.h" -#include "vixl/utils.h" -#include "vixl/a64/instructions-a64.h" -#include "vixl/a64/decoder-a64.h" -#include "vixl/a64/assembler-a64.h" - -namespace vixl { - -class Disassembler: public DecoderVisitor { - public: - Disassembler(); - Disassembler(char* text_buffer, int buffer_size); - virtual ~Disassembler(); - char* GetOutput(); - - // Declare all Visitor functions. - #define DECLARE(A) virtual void Visit##A(const Instruction* instr); - VISITOR_LIST(DECLARE) - #undef DECLARE - - protected: - virtual void ProcessOutput(const Instruction* instr); - - // Default output functions. The functions below implement a default way of - // printing elements in the disassembly. A sub-class can override these to - // customize the disassembly output. - - // Prints the name of a register. - // TODO: This currently doesn't allow renaming of V registers. - virtual void AppendRegisterNameToOutput(const Instruction* instr, - const CPURegister& reg); - - // Prints a PC-relative offset. This is used for example when disassembling - // branches to immediate offsets. - virtual void AppendPCRelativeOffsetToOutput(const Instruction* instr, - int64_t offset); - - // Prints an address, in the general case. It can be code or data. This is - // used for example to print the target address of an ADR instruction. - virtual void AppendCodeRelativeAddressToOutput(const Instruction* instr, - const void* addr); - - // Prints the address of some code. - // This is used for example to print the target address of a branch to an - // immediate offset. - // A sub-class can for example override this method to lookup the address and - // print an appropriate name. - virtual void AppendCodeRelativeCodeAddressToOutput(const Instruction* instr, - const void* addr); - - // Prints the address of some data. - // This is used for example to print the source address of a load literal - // instruction. - virtual void AppendCodeRelativeDataAddressToOutput(const Instruction* instr, - const void* addr); - - // Same as the above, but for addresses that are not relative to the code - // buffer. They are currently not used by VIXL. - virtual void AppendAddressToOutput(const Instruction* instr, - const void* addr); - virtual void AppendCodeAddressToOutput(const Instruction* instr, - const void* addr); - virtual void AppendDataAddressToOutput(const Instruction* instr, - const void* addr); - - public: - // Get/Set the offset that should be added to code addresses when printing - // code-relative addresses in the AppendCodeRelativeAddressToOutput() - // helpers. - // Below is an example of how a branch immediate instruction in memory at - // address 0xb010200 would disassemble with different offsets. - // Base address | Disassembly - // 0x0 | 0xb010200: b #+0xcc (addr 0xb0102cc) - // 0x10000 | 0xb000200: b #+0xcc (addr 0xb0002cc) - // 0xb010200 | 0x0: b #+0xcc (addr 0xcc) - void MapCodeAddress(int64_t base_address, const Instruction* instr_address); - int64_t CodeRelativeAddress(const void* instr); - - private: - void Format( - const Instruction* instr, const char* mnemonic, const char* format); - void Substitute(const Instruction* instr, const char* string); - int SubstituteField(const Instruction* instr, const char* format); - int SubstituteRegisterField(const Instruction* instr, const char* format); - int SubstituteImmediateField(const Instruction* instr, const char* format); - int SubstituteLiteralField(const Instruction* instr, const char* format); - int SubstituteBitfieldImmediateField( - const Instruction* instr, const char* format); - int SubstituteShiftField(const Instruction* instr, const char* format); - int SubstituteExtendField(const Instruction* instr, const char* format); - int SubstituteConditionField(const Instruction* instr, const char* format); - int SubstitutePCRelAddressField(const Instruction* instr, const char* format); - int SubstituteBranchTargetField(const Instruction* instr, const char* format); - int SubstituteLSRegOffsetField(const Instruction* instr, const char* format); - int SubstitutePrefetchField(const Instruction* instr, const char* format); - int SubstituteBarrierField(const Instruction* instr, const char* format); - int SubstituteSysOpField(const Instruction* instr, const char* format); - int SubstituteCrField(const Instruction* instr, const char* format); - bool RdIsZROrSP(const Instruction* instr) const { - return (instr->Rd() == kZeroRegCode); - } - - bool RnIsZROrSP(const Instruction* instr) const { - return (instr->Rn() == kZeroRegCode); - } - - bool RmIsZROrSP(const Instruction* instr) const { - return (instr->Rm() == kZeroRegCode); - } - - bool RaIsZROrSP(const Instruction* instr) const { - return (instr->Ra() == kZeroRegCode); - } - - bool IsMovzMovnImm(unsigned reg_size, uint64_t value); - - int64_t code_address_offset() const { return code_address_offset_; } - - protected: - void ResetOutput(); - void AppendToOutput(const char* string, ...) PRINTF_CHECK(2, 3); - - void set_code_address_offset(int64_t code_address_offset) { - code_address_offset_ = code_address_offset; - } - - char* buffer_; - uint32_t buffer_pos_; - uint32_t buffer_size_; - bool own_buffer_; - - int64_t code_address_offset_; -}; - - -class PrintDisassembler: public Disassembler { - public: - explicit PrintDisassembler(FILE* stream) : stream_(stream) { } - - protected: - virtual void ProcessOutput(const Instruction* instr); - - private: - FILE *stream_; -}; -} // namespace vixl - -#endif // VIXL_A64_DISASM_A64_H diff --git a/disas/libvixl/vixl/a64/instructions-a64.cc b/disas/libvixl/vixl/a64/instructions-a64.cc deleted file mode 100644 index 33992f88a4..0000000000 --- a/disas/libvixl/vixl/a64/instructions-a64.cc +++ /dev/null @@ -1,622 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#include "vixl/a64/instructions-a64.h" -#include "vixl/a64/assembler-a64.h" - -namespace vixl { - - -// Floating-point infinity values. -const float16 kFP16PositiveInfinity = 0x7c00; -const float16 kFP16NegativeInfinity = 0xfc00; -const float kFP32PositiveInfinity = rawbits_to_float(0x7f800000); -const float kFP32NegativeInfinity = rawbits_to_float(0xff800000); -const double kFP64PositiveInfinity = - rawbits_to_double(UINT64_C(0x7ff0000000000000)); -const double kFP64NegativeInfinity = - rawbits_to_double(UINT64_C(0xfff0000000000000)); - - -// The default NaN values (for FPCR.DN=1). -const double kFP64DefaultNaN = rawbits_to_double(UINT64_C(0x7ff8000000000000)); -const float kFP32DefaultNaN = rawbits_to_float(0x7fc00000); -const float16 kFP16DefaultNaN = 0x7e00; - - -static uint64_t RotateRight(uint64_t value, - unsigned int rotate, - unsigned int width) { - VIXL_ASSERT(width <= 64); - rotate &= 63; - return ((value & ((UINT64_C(1) << rotate) - 1)) << - (width - rotate)) | (value >> rotate); -} - - -static uint64_t RepeatBitsAcrossReg(unsigned reg_size, - uint64_t value, - unsigned width) { - VIXL_ASSERT((width == 2) || (width == 4) || (width == 8) || (width == 16) || - (width == 32)); - VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - uint64_t result = value & ((UINT64_C(1) << width) - 1); - for (unsigned i = width; i < reg_size; i *= 2) { - result |= (result << i); - } - return result; -} - - -bool Instruction::IsLoad() const { - if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) { - return false; - } - - if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) { - return Mask(LoadStorePairLBit) != 0; - } else { - LoadStoreOp op = static_cast(Mask(LoadStoreMask)); - switch (op) { - case LDRB_w: - case LDRH_w: - case LDR_w: - case LDR_x: - case LDRSB_w: - case LDRSB_x: - case LDRSH_w: - case LDRSH_x: - case LDRSW_x: - case LDR_b: - case LDR_h: - case LDR_s: - case LDR_d: - case LDR_q: return true; - default: return false; - } - } -} - - -bool Instruction::IsStore() const { - if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) { - return false; - } - - if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) { - return Mask(LoadStorePairLBit) == 0; - } else { - LoadStoreOp op = static_cast(Mask(LoadStoreMask)); - switch (op) { - case STRB_w: - case STRH_w: - case STR_w: - case STR_x: - case STR_b: - case STR_h: - case STR_s: - case STR_d: - case STR_q: return true; - default: return false; - } - } -} - - -// Logical immediates can't encode zero, so a return value of zero is used to -// indicate a failure case. Specifically, where the constraints on imm_s are -// not met. -uint64_t Instruction::ImmLogical() const { - unsigned reg_size = SixtyFourBits() ? kXRegSize : kWRegSize; - int32_t n = BitN(); - int32_t imm_s = ImmSetBits(); - int32_t imm_r = ImmRotate(); - - // An integer is constructed from the n, imm_s and imm_r bits according to - // the following table: - // - // N imms immr size S R - // 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr) - // 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr) - // 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr) - // 0 110sss xxxrrr 8 UInt(sss) UInt(rrr) - // 0 1110ss xxxxrr 4 UInt(ss) UInt(rr) - // 0 11110s xxxxxr 2 UInt(s) UInt(r) - // (s bits must not be all set) - // - // A pattern is constructed of size bits, where the least significant S+1 - // bits are set. The pattern is rotated right by R, and repeated across a - // 32 or 64-bit value, depending on destination register width. - // - - if (n == 1) { - if (imm_s == 0x3f) { - return 0; - } - uint64_t bits = (UINT64_C(1) << (imm_s + 1)) - 1; - return RotateRight(bits, imm_r, 64); - } else { - if ((imm_s >> 1) == 0x1f) { - return 0; - } - for (int width = 0x20; width >= 0x2; width >>= 1) { - if ((imm_s & width) == 0) { - int mask = width - 1; - if ((imm_s & mask) == mask) { - return 0; - } - uint64_t bits = (UINT64_C(1) << ((imm_s & mask) + 1)) - 1; - return RepeatBitsAcrossReg(reg_size, - RotateRight(bits, imm_r & mask, width), - width); - } - } - } - VIXL_UNREACHABLE(); - return 0; -} - - -uint32_t Instruction::ImmNEONabcdefgh() const { - return ImmNEONabc() << 5 | ImmNEONdefgh(); -} - - -float Instruction::Imm8ToFP32(uint32_t imm8) { - // Imm8: abcdefgh (8 bits) - // Single: aBbb.bbbc.defg.h000.0000.0000.0000.0000 (32 bits) - // where B is b ^ 1 - uint32_t bits = imm8; - uint32_t bit7 = (bits >> 7) & 0x1; - uint32_t bit6 = (bits >> 6) & 0x1; - uint32_t bit5_to_0 = bits & 0x3f; - uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19); - - return rawbits_to_float(result); -} - - -float Instruction::ImmFP32() const { - return Imm8ToFP32(ImmFP()); -} - - -double Instruction::Imm8ToFP64(uint32_t imm8) { - // Imm8: abcdefgh (8 bits) - // Double: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 - // 0000.0000.0000.0000.0000.0000.0000.0000 (64 bits) - // where B is b ^ 1 - uint32_t bits = imm8; - uint64_t bit7 = (bits >> 7) & 0x1; - uint64_t bit6 = (bits >> 6) & 0x1; - uint64_t bit5_to_0 = bits & 0x3f; - uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48); - - return rawbits_to_double(result); -} - - -double Instruction::ImmFP64() const { - return Imm8ToFP64(ImmFP()); -} - - -float Instruction::ImmNEONFP32() const { - return Imm8ToFP32(ImmNEONabcdefgh()); -} - - -double Instruction::ImmNEONFP64() const { - return Imm8ToFP64(ImmNEONabcdefgh()); -} - - -unsigned CalcLSDataSize(LoadStoreOp op) { - VIXL_ASSERT((LSSize_offset + LSSize_width) == (kInstructionSize * 8)); - unsigned size = static_cast(op) >> LSSize_offset; - if ((op & LSVector_mask) != 0) { - // Vector register memory operations encode the access size in the "size" - // and "opc" fields. - if ((size == 0) && ((op & LSOpc_mask) >> LSOpc_offset) >= 2) { - size = kQRegSizeInBytesLog2; - } - } - return size; -} - - -unsigned CalcLSPairDataSize(LoadStorePairOp op) { - VIXL_STATIC_ASSERT(kXRegSizeInBytes == kDRegSizeInBytes); - VIXL_STATIC_ASSERT(kWRegSizeInBytes == kSRegSizeInBytes); - switch (op) { - case STP_q: - case LDP_q: return kQRegSizeInBytesLog2; - case STP_x: - case LDP_x: - case STP_d: - case LDP_d: return kXRegSizeInBytesLog2; - default: return kWRegSizeInBytesLog2; - } -} - - -int Instruction::ImmBranchRangeBitwidth(ImmBranchType branch_type) { - switch (branch_type) { - case UncondBranchType: - return ImmUncondBranch_width; - case CondBranchType: - return ImmCondBranch_width; - case CompareBranchType: - return ImmCmpBranch_width; - case TestBranchType: - return ImmTestBranch_width; - default: - VIXL_UNREACHABLE(); - return 0; - } -} - - -int32_t Instruction::ImmBranchForwardRange(ImmBranchType branch_type) { - int32_t encoded_max = 1 << (ImmBranchRangeBitwidth(branch_type) - 1); - return encoded_max * kInstructionSize; -} - - -bool Instruction::IsValidImmPCOffset(ImmBranchType branch_type, - int64_t offset) { - return is_intn(ImmBranchRangeBitwidth(branch_type), offset); -} - - -const Instruction* Instruction::ImmPCOffsetTarget() const { - const Instruction * base = this; - ptrdiff_t offset; - if (IsPCRelAddressing()) { - // ADR and ADRP. - offset = ImmPCRel(); - if (Mask(PCRelAddressingMask) == ADRP) { - base = AlignDown(base, kPageSize); - offset *= kPageSize; - } else { - VIXL_ASSERT(Mask(PCRelAddressingMask) == ADR); - } - } else { - // All PC-relative branches. - VIXL_ASSERT(BranchType() != UnknownBranchType); - // Relative branch offsets are instruction-size-aligned. - offset = ImmBranch() << kInstructionSizeLog2; - } - return base + offset; -} - - -int Instruction::ImmBranch() const { - switch (BranchType()) { - case CondBranchType: return ImmCondBranch(); - case UncondBranchType: return ImmUncondBranch(); - case CompareBranchType: return ImmCmpBranch(); - case TestBranchType: return ImmTestBranch(); - default: VIXL_UNREACHABLE(); - } - return 0; -} - - -void Instruction::SetImmPCOffsetTarget(const Instruction* target) { - if (IsPCRelAddressing()) { - SetPCRelImmTarget(target); - } else { - SetBranchImmTarget(target); - } -} - - -void Instruction::SetPCRelImmTarget(const Instruction* target) { - ptrdiff_t imm21; - if ((Mask(PCRelAddressingMask) == ADR)) { - imm21 = target - this; - } else { - VIXL_ASSERT(Mask(PCRelAddressingMask) == ADRP); - uintptr_t this_page = reinterpret_cast(this) / kPageSize; - uintptr_t target_page = reinterpret_cast(target) / kPageSize; - imm21 = target_page - this_page; - } - Instr imm = Assembler::ImmPCRelAddress(static_cast(imm21)); - - SetInstructionBits(Mask(~ImmPCRel_mask) | imm); -} - - -void Instruction::SetBranchImmTarget(const Instruction* target) { - VIXL_ASSERT(((target - this) & 3) == 0); - Instr branch_imm = 0; - uint32_t imm_mask = 0; - int offset = static_cast((target - this) >> kInstructionSizeLog2); - switch (BranchType()) { - case CondBranchType: { - branch_imm = Assembler::ImmCondBranch(offset); - imm_mask = ImmCondBranch_mask; - break; - } - case UncondBranchType: { - branch_imm = Assembler::ImmUncondBranch(offset); - imm_mask = ImmUncondBranch_mask; - break; - } - case CompareBranchType: { - branch_imm = Assembler::ImmCmpBranch(offset); - imm_mask = ImmCmpBranch_mask; - break; - } - case TestBranchType: { - branch_imm = Assembler::ImmTestBranch(offset); - imm_mask = ImmTestBranch_mask; - break; - } - default: VIXL_UNREACHABLE(); - } - SetInstructionBits(Mask(~imm_mask) | branch_imm); -} - - -void Instruction::SetImmLLiteral(const Instruction* source) { - VIXL_ASSERT(IsWordAligned(source)); - ptrdiff_t offset = (source - this) >> kLiteralEntrySizeLog2; - Instr imm = Assembler::ImmLLiteral(static_cast(offset)); - Instr mask = ImmLLiteral_mask; - - SetInstructionBits(Mask(~mask) | imm); -} - - -VectorFormat VectorFormatHalfWidth(const VectorFormat vform) { - VIXL_ASSERT(vform == kFormat8H || vform == kFormat4S || vform == kFormat2D || - vform == kFormatH || vform == kFormatS || vform == kFormatD); - switch (vform) { - case kFormat8H: return kFormat8B; - case kFormat4S: return kFormat4H; - case kFormat2D: return kFormat2S; - case kFormatH: return kFormatB; - case kFormatS: return kFormatH; - case kFormatD: return kFormatS; - default: VIXL_UNREACHABLE(); return kFormatUndefined; - } -} - - -VectorFormat VectorFormatDoubleWidth(const VectorFormat vform) { - VIXL_ASSERT(vform == kFormat8B || vform == kFormat4H || vform == kFormat2S || - vform == kFormatB || vform == kFormatH || vform == kFormatS); - switch (vform) { - case kFormat8B: return kFormat8H; - case kFormat4H: return kFormat4S; - case kFormat2S: return kFormat2D; - case kFormatB: return kFormatH; - case kFormatH: return kFormatS; - case kFormatS: return kFormatD; - default: VIXL_UNREACHABLE(); return kFormatUndefined; - } -} - - -VectorFormat VectorFormatFillQ(const VectorFormat vform) { - switch (vform) { - case kFormatB: - case kFormat8B: - case kFormat16B: return kFormat16B; - case kFormatH: - case kFormat4H: - case kFormat8H: return kFormat8H; - case kFormatS: - case kFormat2S: - case kFormat4S: return kFormat4S; - case kFormatD: - case kFormat1D: - case kFormat2D: return kFormat2D; - default: VIXL_UNREACHABLE(); return kFormatUndefined; - } -} - -VectorFormat VectorFormatHalfWidthDoubleLanes(const VectorFormat vform) { - switch (vform) { - case kFormat4H: return kFormat8B; - case kFormat8H: return kFormat16B; - case kFormat2S: return kFormat4H; - case kFormat4S: return kFormat8H; - case kFormat1D: return kFormat2S; - case kFormat2D: return kFormat4S; - default: VIXL_UNREACHABLE(); return kFormatUndefined; - } -} - -VectorFormat VectorFormatDoubleLanes(const VectorFormat vform) { - VIXL_ASSERT(vform == kFormat8B || vform == kFormat4H || vform == kFormat2S); - switch (vform) { - case kFormat8B: return kFormat16B; - case kFormat4H: return kFormat8H; - case kFormat2S: return kFormat4S; - default: VIXL_UNREACHABLE(); return kFormatUndefined; - } -} - - -VectorFormat VectorFormatHalfLanes(const VectorFormat vform) { - VIXL_ASSERT(vform == kFormat16B || vform == kFormat8H || vform == kFormat4S); - switch (vform) { - case kFormat16B: return kFormat8B; - case kFormat8H: return kFormat4H; - case kFormat4S: return kFormat2S; - default: VIXL_UNREACHABLE(); return kFormatUndefined; - } -} - - -VectorFormat ScalarFormatFromLaneSize(int laneSize) { - switch (laneSize) { - case 8: return kFormatB; - case 16: return kFormatH; - case 32: return kFormatS; - case 64: return kFormatD; - default: VIXL_UNREACHABLE(); return kFormatUndefined; - } -} - - -unsigned RegisterSizeInBitsFromFormat(VectorFormat vform) { - VIXL_ASSERT(vform != kFormatUndefined); - switch (vform) { - case kFormatB: return kBRegSize; - case kFormatH: return kHRegSize; - case kFormatS: return kSRegSize; - case kFormatD: return kDRegSize; - case kFormat8B: - case kFormat4H: - case kFormat2S: - case kFormat1D: return kDRegSize; - default: return kQRegSize; - } -} - - -unsigned RegisterSizeInBytesFromFormat(VectorFormat vform) { - return RegisterSizeInBitsFromFormat(vform) / 8; -} - - -unsigned LaneSizeInBitsFromFormat(VectorFormat vform) { - VIXL_ASSERT(vform != kFormatUndefined); - switch (vform) { - case kFormatB: - case kFormat8B: - case kFormat16B: return 8; - case kFormatH: - case kFormat4H: - case kFormat8H: return 16; - case kFormatS: - case kFormat2S: - case kFormat4S: return 32; - case kFormatD: - case kFormat1D: - case kFormat2D: return 64; - default: VIXL_UNREACHABLE(); return 0; - } -} - - -int LaneSizeInBytesFromFormat(VectorFormat vform) { - return LaneSizeInBitsFromFormat(vform) / 8; -} - - -int LaneSizeInBytesLog2FromFormat(VectorFormat vform) { - VIXL_ASSERT(vform != kFormatUndefined); - switch (vform) { - case kFormatB: - case kFormat8B: - case kFormat16B: return 0; - case kFormatH: - case kFormat4H: - case kFormat8H: return 1; - case kFormatS: - case kFormat2S: - case kFormat4S: return 2; - case kFormatD: - case kFormat1D: - case kFormat2D: return 3; - default: VIXL_UNREACHABLE(); return 0; - } -} - - -int LaneCountFromFormat(VectorFormat vform) { - VIXL_ASSERT(vform != kFormatUndefined); - switch (vform) { - case kFormat16B: return 16; - case kFormat8B: - case kFormat8H: return 8; - case kFormat4H: - case kFormat4S: return 4; - case kFormat2S: - case kFormat2D: return 2; - case kFormat1D: - case kFormatB: - case kFormatH: - case kFormatS: - case kFormatD: return 1; - default: VIXL_UNREACHABLE(); return 0; - } -} - - -int MaxLaneCountFromFormat(VectorFormat vform) { - VIXL_ASSERT(vform != kFormatUndefined); - switch (vform) { - case kFormatB: - case kFormat8B: - case kFormat16B: return 16; - case kFormatH: - case kFormat4H: - case kFormat8H: return 8; - case kFormatS: - case kFormat2S: - case kFormat4S: return 4; - case kFormatD: - case kFormat1D: - case kFormat2D: return 2; - default: VIXL_UNREACHABLE(); return 0; - } -} - - -// Does 'vform' indicate a vector format or a scalar format? -bool IsVectorFormat(VectorFormat vform) { - VIXL_ASSERT(vform != kFormatUndefined); - switch (vform) { - case kFormatB: - case kFormatH: - case kFormatS: - case kFormatD: return false; - default: return true; - } -} - - -int64_t MaxIntFromFormat(VectorFormat vform) { - return INT64_MAX >> (64 - LaneSizeInBitsFromFormat(vform)); -} - - -int64_t MinIntFromFormat(VectorFormat vform) { - return INT64_MIN >> (64 - LaneSizeInBitsFromFormat(vform)); -} - - -uint64_t MaxUintFromFormat(VectorFormat vform) { - return UINT64_MAX >> (64 - LaneSizeInBitsFromFormat(vform)); -} -} // namespace vixl - diff --git a/disas/libvixl/vixl/a64/instructions-a64.h b/disas/libvixl/vixl/a64/instructions-a64.h deleted file mode 100644 index 7e0dbae36a..0000000000 --- a/disas/libvixl/vixl/a64/instructions-a64.h +++ /dev/null @@ -1,757 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#ifndef VIXL_A64_INSTRUCTIONS_A64_H_ -#define VIXL_A64_INSTRUCTIONS_A64_H_ - -#include "vixl/globals.h" -#include "vixl/utils.h" -#include "vixl/a64/constants-a64.h" - -namespace vixl { -// ISA constants. -------------------------------------------------------------- - -typedef uint32_t Instr; -const unsigned kInstructionSize = 4; -const unsigned kInstructionSizeLog2 = 2; -const unsigned kLiteralEntrySize = 4; -const unsigned kLiteralEntrySizeLog2 = 2; -const unsigned kMaxLoadLiteralRange = 1 * MBytes; - -// This is the nominal page size (as used by the adrp instruction); the actual -// size of the memory pages allocated by the kernel is likely to differ. -const unsigned kPageSize = 4 * KBytes; -const unsigned kPageSizeLog2 = 12; - -const unsigned kBRegSize = 8; -const unsigned kBRegSizeLog2 = 3; -const unsigned kBRegSizeInBytes = kBRegSize / 8; -const unsigned kBRegSizeInBytesLog2 = kBRegSizeLog2 - 3; -const unsigned kHRegSize = 16; -const unsigned kHRegSizeLog2 = 4; -const unsigned kHRegSizeInBytes = kHRegSize / 8; -const unsigned kHRegSizeInBytesLog2 = kHRegSizeLog2 - 3; -const unsigned kWRegSize = 32; -const unsigned kWRegSizeLog2 = 5; -const unsigned kWRegSizeInBytes = kWRegSize / 8; -const unsigned kWRegSizeInBytesLog2 = kWRegSizeLog2 - 3; -const unsigned kXRegSize = 64; -const unsigned kXRegSizeLog2 = 6; -const unsigned kXRegSizeInBytes = kXRegSize / 8; -const unsigned kXRegSizeInBytesLog2 = kXRegSizeLog2 - 3; -const unsigned kSRegSize = 32; -const unsigned kSRegSizeLog2 = 5; -const unsigned kSRegSizeInBytes = kSRegSize / 8; -const unsigned kSRegSizeInBytesLog2 = kSRegSizeLog2 - 3; -const unsigned kDRegSize = 64; -const unsigned kDRegSizeLog2 = 6; -const unsigned kDRegSizeInBytes = kDRegSize / 8; -const unsigned kDRegSizeInBytesLog2 = kDRegSizeLog2 - 3; -const unsigned kQRegSize = 128; -const unsigned kQRegSizeLog2 = 7; -const unsigned kQRegSizeInBytes = kQRegSize / 8; -const unsigned kQRegSizeInBytesLog2 = kQRegSizeLog2 - 3; -const uint64_t kWRegMask = UINT64_C(0xffffffff); -const uint64_t kXRegMask = UINT64_C(0xffffffffffffffff); -const uint64_t kSRegMask = UINT64_C(0xffffffff); -const uint64_t kDRegMask = UINT64_C(0xffffffffffffffff); -const uint64_t kSSignMask = UINT64_C(0x80000000); -const uint64_t kDSignMask = UINT64_C(0x8000000000000000); -const uint64_t kWSignMask = UINT64_C(0x80000000); -const uint64_t kXSignMask = UINT64_C(0x8000000000000000); -const uint64_t kByteMask = UINT64_C(0xff); -const uint64_t kHalfWordMask = UINT64_C(0xffff); -const uint64_t kWordMask = UINT64_C(0xffffffff); -const uint64_t kXMaxUInt = UINT64_C(0xffffffffffffffff); -const uint64_t kWMaxUInt = UINT64_C(0xffffffff); -const int64_t kXMaxInt = INT64_C(0x7fffffffffffffff); -const int64_t kXMinInt = INT64_C(0x8000000000000000); -const int32_t kWMaxInt = INT32_C(0x7fffffff); -const int32_t kWMinInt = INT32_C(0x80000000); -const unsigned kLinkRegCode = 30; -const unsigned kZeroRegCode = 31; -const unsigned kSPRegInternalCode = 63; -const unsigned kRegCodeMask = 0x1f; - -const unsigned kAddressTagOffset = 56; -const unsigned kAddressTagWidth = 8; -const uint64_t kAddressTagMask = - ((UINT64_C(1) << kAddressTagWidth) - 1) << kAddressTagOffset; -VIXL_STATIC_ASSERT(kAddressTagMask == UINT64_C(0xff00000000000000)); - -// AArch64 floating-point specifics. These match IEEE-754. -const unsigned kDoubleMantissaBits = 52; -const unsigned kDoubleExponentBits = 11; -const unsigned kFloatMantissaBits = 23; -const unsigned kFloatExponentBits = 8; -const unsigned kFloat16MantissaBits = 10; -const unsigned kFloat16ExponentBits = 5; - -// Floating-point infinity values. -extern const float16 kFP16PositiveInfinity; -extern const float16 kFP16NegativeInfinity; -extern const float kFP32PositiveInfinity; -extern const float kFP32NegativeInfinity; -extern const double kFP64PositiveInfinity; -extern const double kFP64NegativeInfinity; - -// The default NaN values (for FPCR.DN=1). -extern const float16 kFP16DefaultNaN; -extern const float kFP32DefaultNaN; -extern const double kFP64DefaultNaN; - -unsigned CalcLSDataSize(LoadStoreOp op); -unsigned CalcLSPairDataSize(LoadStorePairOp op); - -enum ImmBranchType { - UnknownBranchType = 0, - CondBranchType = 1, - UncondBranchType = 2, - CompareBranchType = 3, - TestBranchType = 4 -}; - -enum AddrMode { - Offset, - PreIndex, - PostIndex -}; - -enum FPRounding { - // The first four values are encodable directly by FPCR. - FPTieEven = 0x0, - FPPositiveInfinity = 0x1, - FPNegativeInfinity = 0x2, - FPZero = 0x3, - - // The final rounding modes are only available when explicitly specified by - // the instruction (such as with fcvta). It cannot be set in FPCR. - FPTieAway, - FPRoundOdd -}; - -enum Reg31Mode { - Reg31IsStackPointer, - Reg31IsZeroRegister -}; - -// Instructions. --------------------------------------------------------------- - -class Instruction { - public: - Instr InstructionBits() const { - return *(reinterpret_cast(this)); - } - - void SetInstructionBits(Instr new_instr) { - *(reinterpret_cast(this)) = new_instr; - } - - int Bit(int pos) const { - return (InstructionBits() >> pos) & 1; - } - - uint32_t Bits(int msb, int lsb) const { - return unsigned_bitextract_32(msb, lsb, InstructionBits()); - } - - int32_t SignedBits(int msb, int lsb) const { - int32_t bits = *(reinterpret_cast(this)); - return signed_bitextract_32(msb, lsb, bits); - } - - Instr Mask(uint32_t mask) const { - return InstructionBits() & mask; - } - - #define DEFINE_GETTER(Name, HighBit, LowBit, Func) \ - int32_t Name() const { return Func(HighBit, LowBit); } - INSTRUCTION_FIELDS_LIST(DEFINE_GETTER) - #undef DEFINE_GETTER - - // ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), - // formed from ImmPCRelLo and ImmPCRelHi. - int ImmPCRel() const { - int offset = - static_cast((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo()); - int width = ImmPCRelLo_width + ImmPCRelHi_width; - return signed_bitextract_32(width - 1, 0, offset); - } - - uint64_t ImmLogical() const; - unsigned ImmNEONabcdefgh() const; - float ImmFP32() const; - double ImmFP64() const; - float ImmNEONFP32() const; - double ImmNEONFP64() const; - - unsigned SizeLS() const { - return CalcLSDataSize(static_cast(Mask(LoadStoreMask))); - } - - unsigned SizeLSPair() const { - return CalcLSPairDataSize( - static_cast(Mask(LoadStorePairMask))); - } - - int NEONLSIndex(int access_size_shift) const { - int64_t q = NEONQ(); - int64_t s = NEONS(); - int64_t size = NEONLSSize(); - int64_t index = (q << 3) | (s << 2) | size; - return static_cast(index >> access_size_shift); - } - - // Helpers. - bool IsCondBranchImm() const { - return Mask(ConditionalBranchFMask) == ConditionalBranchFixed; - } - - bool IsUncondBranchImm() const { - return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed; - } - - bool IsCompareBranch() const { - return Mask(CompareBranchFMask) == CompareBranchFixed; - } - - bool IsTestBranch() const { - return Mask(TestBranchFMask) == TestBranchFixed; - } - - bool IsImmBranch() const { - return BranchType() != UnknownBranchType; - } - - bool IsPCRelAddressing() const { - return Mask(PCRelAddressingFMask) == PCRelAddressingFixed; - } - - bool IsLogicalImmediate() const { - return Mask(LogicalImmediateFMask) == LogicalImmediateFixed; - } - - bool IsAddSubImmediate() const { - return Mask(AddSubImmediateFMask) == AddSubImmediateFixed; - } - - bool IsAddSubExtended() const { - return Mask(AddSubExtendedFMask) == AddSubExtendedFixed; - } - - bool IsLoadOrStore() const { - return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed; - } - - bool IsLoad() const; - bool IsStore() const; - - bool IsLoadLiteral() const { - // This includes PRFM_lit. - return Mask(LoadLiteralFMask) == LoadLiteralFixed; - } - - bool IsMovn() const { - return (Mask(MoveWideImmediateMask) == MOVN_x) || - (Mask(MoveWideImmediateMask) == MOVN_w); - } - - static int ImmBranchRangeBitwidth(ImmBranchType branch_type); - static int32_t ImmBranchForwardRange(ImmBranchType branch_type); - static bool IsValidImmPCOffset(ImmBranchType branch_type, int64_t offset); - - // Indicate whether Rd can be the stack pointer or the zero register. This - // does not check that the instruction actually has an Rd field. - Reg31Mode RdMode() const { - // The following instructions use sp or wsp as Rd: - // Add/sub (immediate) when not setting the flags. - // Add/sub (extended) when not setting the flags. - // Logical (immediate) when not setting the flags. - // Otherwise, r31 is the zero register. - if (IsAddSubImmediate() || IsAddSubExtended()) { - if (Mask(AddSubSetFlagsBit)) { - return Reg31IsZeroRegister; - } else { - return Reg31IsStackPointer; - } - } - if (IsLogicalImmediate()) { - // Of the logical (immediate) instructions, only ANDS (and its aliases) - // can set the flags. The others can all write into sp. - // Note that some logical operations are not available to - // immediate-operand instructions, so we have to combine two masks here. - if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) { - return Reg31IsZeroRegister; - } else { - return Reg31IsStackPointer; - } - } - return Reg31IsZeroRegister; - } - - // Indicate whether Rn can be the stack pointer or the zero register. This - // does not check that the instruction actually has an Rn field. - Reg31Mode RnMode() const { - // The following instructions use sp or wsp as Rn: - // All loads and stores. - // Add/sub (immediate). - // Add/sub (extended). - // Otherwise, r31 is the zero register. - if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) { - return Reg31IsStackPointer; - } - return Reg31IsZeroRegister; - } - - ImmBranchType BranchType() const { - if (IsCondBranchImm()) { - return CondBranchType; - } else if (IsUncondBranchImm()) { - return UncondBranchType; - } else if (IsCompareBranch()) { - return CompareBranchType; - } else if (IsTestBranch()) { - return TestBranchType; - } else { - return UnknownBranchType; - } - } - - // Find the target of this instruction. 'this' may be a branch or a - // PC-relative addressing instruction. - const Instruction* ImmPCOffsetTarget() const; - - // Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or - // a PC-relative addressing instruction. - void SetImmPCOffsetTarget(const Instruction* target); - // Patch a literal load instruction to load from 'source'. - void SetImmLLiteral(const Instruction* source); - - // The range of a load literal instruction, expressed as 'instr +- range'. - // The range is actually the 'positive' range; the branch instruction can - // target [instr - range - kInstructionSize, instr + range]. - static const int kLoadLiteralImmBitwidth = 19; - static const int kLoadLiteralRange = - (1 << kLoadLiteralImmBitwidth) / 2 - kInstructionSize; - - // Calculate the address of a literal referred to by a load-literal - // instruction, and return it as the specified type. - // - // The literal itself is safely mutable only if the backing buffer is safely - // mutable. - template - T LiteralAddress() const { - uint64_t base_raw = reinterpret_cast(this); - int64_t offset = ImmLLiteral() << kLiteralEntrySizeLog2; - uint64_t address_raw = base_raw + offset; - - // Cast the address using a C-style cast. A reinterpret_cast would be - // appropriate, but it can't cast one integral type to another. - T address = (T)(address_raw); - - // Assert that the address can be represented by the specified type. - VIXL_ASSERT((uint64_t)(address) == address_raw); - - return address; - } - - uint32_t Literal32() const { - uint32_t literal; - memcpy(&literal, LiteralAddress(), sizeof(literal)); - return literal; - } - - uint64_t Literal64() const { - uint64_t literal; - memcpy(&literal, LiteralAddress(), sizeof(literal)); - return literal; - } - - float LiteralFP32() const { - return rawbits_to_float(Literal32()); - } - - double LiteralFP64() const { - return rawbits_to_double(Literal64()); - } - - const Instruction* NextInstruction() const { - return this + kInstructionSize; - } - - const Instruction* InstructionAtOffset(int64_t offset) const { - VIXL_ASSERT(IsWordAligned(this + offset)); - return this + offset; - } - - template static Instruction* Cast(T src) { - return reinterpret_cast(src); - } - - template static const Instruction* CastConst(T src) { - return reinterpret_cast(src); - } - - private: - int ImmBranch() const; - - static float Imm8ToFP32(uint32_t imm8); - static double Imm8ToFP64(uint32_t imm8); - - void SetPCRelImmTarget(const Instruction* target); - void SetBranchImmTarget(const Instruction* target); -}; - - -// Functions for handling NEON vector format information. -enum VectorFormat { - kFormatUndefined = 0xffffffff, - kFormat8B = NEON_8B, - kFormat16B = NEON_16B, - kFormat4H = NEON_4H, - kFormat8H = NEON_8H, - kFormat2S = NEON_2S, - kFormat4S = NEON_4S, - kFormat1D = NEON_1D, - kFormat2D = NEON_2D, - - // Scalar formats. We add the scalar bit to distinguish between scalar and - // vector enumerations; the bit is always set in the encoding of scalar ops - // and always clear for vector ops. Although kFormatD and kFormat1D appear - // to be the same, their meaning is subtly different. The first is a scalar - // operation, the second a vector operation that only affects one lane. - kFormatB = NEON_B | NEONScalar, - kFormatH = NEON_H | NEONScalar, - kFormatS = NEON_S | NEONScalar, - kFormatD = NEON_D | NEONScalar -}; - -VectorFormat VectorFormatHalfWidth(const VectorFormat vform); -VectorFormat VectorFormatDoubleWidth(const VectorFormat vform); -VectorFormat VectorFormatDoubleLanes(const VectorFormat vform); -VectorFormat VectorFormatHalfLanes(const VectorFormat vform); -VectorFormat ScalarFormatFromLaneSize(int lanesize); -VectorFormat VectorFormatHalfWidthDoubleLanes(const VectorFormat vform); -VectorFormat VectorFormatFillQ(const VectorFormat vform); -unsigned RegisterSizeInBitsFromFormat(VectorFormat vform); -unsigned RegisterSizeInBytesFromFormat(VectorFormat vform); -// TODO: Make the return types of these functions consistent. -unsigned LaneSizeInBitsFromFormat(VectorFormat vform); -int LaneSizeInBytesFromFormat(VectorFormat vform); -int LaneSizeInBytesLog2FromFormat(VectorFormat vform); -int LaneCountFromFormat(VectorFormat vform); -int MaxLaneCountFromFormat(VectorFormat vform); -bool IsVectorFormat(VectorFormat vform); -int64_t MaxIntFromFormat(VectorFormat vform); -int64_t MinIntFromFormat(VectorFormat vform); -uint64_t MaxUintFromFormat(VectorFormat vform); - - -enum NEONFormat { - NF_UNDEF = 0, - NF_8B = 1, - NF_16B = 2, - NF_4H = 3, - NF_8H = 4, - NF_2S = 5, - NF_4S = 6, - NF_1D = 7, - NF_2D = 8, - NF_B = 9, - NF_H = 10, - NF_S = 11, - NF_D = 12 -}; - -static const unsigned kNEONFormatMaxBits = 6; - -struct NEONFormatMap { - // The bit positions in the instruction to consider. - uint8_t bits[kNEONFormatMaxBits]; - - // Mapping from concatenated bits to format. - NEONFormat map[1 << kNEONFormatMaxBits]; -}; - -class NEONFormatDecoder { - public: - enum SubstitutionMode { - kPlaceholder, - kFormat - }; - - // Construct a format decoder with increasingly specific format maps for each - // subsitution. If no format map is specified, the default is the integer - // format map. - explicit NEONFormatDecoder(const Instruction* instr) { - instrbits_ = instr->InstructionBits(); - SetFormatMaps(IntegerFormatMap()); - } - NEONFormatDecoder(const Instruction* instr, - const NEONFormatMap* format) { - instrbits_ = instr->InstructionBits(); - SetFormatMaps(format); - } - NEONFormatDecoder(const Instruction* instr, - const NEONFormatMap* format0, - const NEONFormatMap* format1) { - instrbits_ = instr->InstructionBits(); - SetFormatMaps(format0, format1); - } - NEONFormatDecoder(const Instruction* instr, - const NEONFormatMap* format0, - const NEONFormatMap* format1, - const NEONFormatMap* format2) { - instrbits_ = instr->InstructionBits(); - SetFormatMaps(format0, format1, format2); - } - - // Set the format mapping for all or individual substitutions. - void SetFormatMaps(const NEONFormatMap* format0, - const NEONFormatMap* format1 = NULL, - const NEONFormatMap* format2 = NULL) { - VIXL_ASSERT(format0 != NULL); - formats_[0] = format0; - formats_[1] = (format1 == NULL) ? formats_[0] : format1; - formats_[2] = (format2 == NULL) ? formats_[1] : format2; - } - void SetFormatMap(unsigned index, const NEONFormatMap* format) { - VIXL_ASSERT(index <= (sizeof(formats_) / sizeof(formats_[0]))); - VIXL_ASSERT(format != NULL); - formats_[index] = format; - } - - // Substitute %s in the input string with the placeholder string for each - // register, ie. "'B", "'H", etc. - const char* SubstitutePlaceholders(const char* string) { - return Substitute(string, kPlaceholder, kPlaceholder, kPlaceholder); - } - - // Substitute %s in the input string with a new string based on the - // substitution mode. - const char* Substitute(const char* string, - SubstitutionMode mode0 = kFormat, - SubstitutionMode mode1 = kFormat, - SubstitutionMode mode2 = kFormat) { - snprintf(form_buffer_, sizeof(form_buffer_), string, - GetSubstitute(0, mode0), - GetSubstitute(1, mode1), - GetSubstitute(2, mode2)); - return form_buffer_; - } - - // Append a "2" to a mnemonic string based of the state of the Q bit. - const char* Mnemonic(const char* mnemonic) { - if ((instrbits_ & NEON_Q) != 0) { - snprintf(mne_buffer_, sizeof(mne_buffer_), "%s2", mnemonic); - return mne_buffer_; - } - return mnemonic; - } - - VectorFormat GetVectorFormat(int format_index = 0) { - return GetVectorFormat(formats_[format_index]); - } - - VectorFormat GetVectorFormat(const NEONFormatMap* format_map) { - static const VectorFormat vform[] = { - kFormatUndefined, - kFormat8B, kFormat16B, kFormat4H, kFormat8H, - kFormat2S, kFormat4S, kFormat1D, kFormat2D, - kFormatB, kFormatH, kFormatS, kFormatD - }; - VIXL_ASSERT(GetNEONFormat(format_map) < (sizeof(vform) / sizeof(vform[0]))); - return vform[GetNEONFormat(format_map)]; - } - - // Built in mappings for common cases. - - // The integer format map uses three bits (Q, size<1:0>) to encode the - // "standard" set of NEON integer vector formats. - static const NEONFormatMap* IntegerFormatMap() { - static const NEONFormatMap map = { - {23, 22, 30}, - {NF_8B, NF_16B, NF_4H, NF_8H, NF_2S, NF_4S, NF_UNDEF, NF_2D} - }; - return ↦ - } - - // The long integer format map uses two bits (size<1:0>) to encode the - // long set of NEON integer vector formats. These are used in narrow, wide - // and long operations. - static const NEONFormatMap* LongIntegerFormatMap() { - static const NEONFormatMap map = { - {23, 22}, {NF_8H, NF_4S, NF_2D} - }; - return ↦ - } - - // The FP format map uses two bits (Q, size<0>) to encode the NEON FP vector - // formats: NF_2S, NF_4S, NF_2D. - static const NEONFormatMap* FPFormatMap() { - // The FP format map assumes two bits (Q, size<0>) are used to encode the - // NEON FP vector formats: NF_2S, NF_4S, NF_2D. - static const NEONFormatMap map = { - {22, 30}, {NF_2S, NF_4S, NF_UNDEF, NF_2D} - }; - return ↦ - } - - // The load/store format map uses three bits (Q, 11, 10) to encode the - // set of NEON vector formats. - static const NEONFormatMap* LoadStoreFormatMap() { - static const NEONFormatMap map = { - {11, 10, 30}, - {NF_8B, NF_16B, NF_4H, NF_8H, NF_2S, NF_4S, NF_1D, NF_2D} - }; - return ↦ - } - - // The logical format map uses one bit (Q) to encode the NEON vector format: - // NF_8B, NF_16B. - static const NEONFormatMap* LogicalFormatMap() { - static const NEONFormatMap map = { - {30}, {NF_8B, NF_16B} - }; - return ↦ - } - - // The triangular format map uses between two and five bits to encode the NEON - // vector format: - // xxx10->8B, xxx11->16B, xx100->4H, xx101->8H - // x1000->2S, x1001->4S, 10001->2D, all others undefined. - static const NEONFormatMap* TriangularFormatMap() { - static const NEONFormatMap map = { - {19, 18, 17, 16, 30}, - {NF_UNDEF, NF_UNDEF, NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, NF_2S, - NF_4S, NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, NF_UNDEF, NF_2D, - NF_8B, NF_16B, NF_4H, NF_8H, NF_8B, NF_16B, NF_2S, NF_4S, NF_8B, NF_16B, - NF_4H, NF_8H, NF_8B, NF_16B} - }; - return ↦ - } - - // The scalar format map uses two bits (size<1:0>) to encode the NEON scalar - // formats: NF_B, NF_H, NF_S, NF_D. - static const NEONFormatMap* ScalarFormatMap() { - static const NEONFormatMap map = { - {23, 22}, {NF_B, NF_H, NF_S, NF_D} - }; - return ↦ - } - - // The long scalar format map uses two bits (size<1:0>) to encode the longer - // NEON scalar formats: NF_H, NF_S, NF_D. - static const NEONFormatMap* LongScalarFormatMap() { - static const NEONFormatMap map = { - {23, 22}, {NF_H, NF_S, NF_D} - }; - return ↦ - } - - // The FP scalar format map assumes one bit (size<0>) is used to encode the - // NEON FP scalar formats: NF_S, NF_D. - static const NEONFormatMap* FPScalarFormatMap() { - static const NEONFormatMap map = { - {22}, {NF_S, NF_D} - }; - return ↦ - } - - // The triangular scalar format map uses between one and four bits to encode - // the NEON FP scalar formats: - // xxx1->B, xx10->H, x100->S, 1000->D, all others undefined. - static const NEONFormatMap* TriangularScalarFormatMap() { - static const NEONFormatMap map = { - {19, 18, 17, 16}, - {NF_UNDEF, NF_B, NF_H, NF_B, NF_S, NF_B, NF_H, NF_B, - NF_D, NF_B, NF_H, NF_B, NF_S, NF_B, NF_H, NF_B} - }; - return ↦ - } - - private: - // Get a pointer to a string that represents the format or placeholder for - // the specified substitution index, based on the format map and instruction. - const char* GetSubstitute(int index, SubstitutionMode mode) { - if (mode == kFormat) { - return NEONFormatAsString(GetNEONFormat(formats_[index])); - } - VIXL_ASSERT(mode == kPlaceholder); - return NEONFormatAsPlaceholder(GetNEONFormat(formats_[index])); - } - - // Get the NEONFormat enumerated value for bits obtained from the - // instruction based on the specified format mapping. - NEONFormat GetNEONFormat(const NEONFormatMap* format_map) { - return format_map->map[PickBits(format_map->bits)]; - } - - // Convert a NEONFormat into a string. - static const char* NEONFormatAsString(NEONFormat format) { - static const char* formats[] = { - "undefined", - "8b", "16b", "4h", "8h", "2s", "4s", "1d", "2d", - "b", "h", "s", "d" - }; - VIXL_ASSERT(format < (sizeof(formats) / sizeof(formats[0]))); - return formats[format]; - } - - // Convert a NEONFormat into a register placeholder string. - static const char* NEONFormatAsPlaceholder(NEONFormat format) { - VIXL_ASSERT((format == NF_B) || (format == NF_H) || - (format == NF_S) || (format == NF_D) || - (format == NF_UNDEF)); - static const char* formats[] = { - "undefined", - "undefined", "undefined", "undefined", "undefined", - "undefined", "undefined", "undefined", "undefined", - "'B", "'H", "'S", "'D" - }; - return formats[format]; - } - - // Select bits from instrbits_ defined by the bits array, concatenate them, - // and return the value. - uint8_t PickBits(const uint8_t bits[]) { - uint8_t result = 0; - for (unsigned b = 0; b < kNEONFormatMaxBits; b++) { - if (bits[b] == 0) break; - result <<= 1; - result |= ((instrbits_ & (1 << bits[b])) == 0) ? 0 : 1; - } - return result; - } - - Instr instrbits_; - const NEONFormatMap* formats_[3]; - char form_buffer_[64]; - char mne_buffer_[16]; -}; -} // namespace vixl - -#endif // VIXL_A64_INSTRUCTIONS_A64_H_ diff --git a/disas/libvixl/vixl/code-buffer.h b/disas/libvixl/vixl/code-buffer.h deleted file mode 100644 index b95babbdee..0000000000 --- a/disas/libvixl/vixl/code-buffer.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2014, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#ifndef VIXL_CODE_BUFFER_H -#define VIXL_CODE_BUFFER_H - -#include -#include "vixl/globals.h" - -namespace vixl { - -class CodeBuffer { - public: - explicit CodeBuffer(size_t capacity = 4 * KBytes); - CodeBuffer(void* buffer, size_t capacity); - ~CodeBuffer(); - - void Reset(); - - ptrdiff_t OffsetFrom(ptrdiff_t offset) const { - ptrdiff_t cursor_offset = cursor_ - buffer_; - VIXL_ASSERT((offset >= 0) && (offset <= cursor_offset)); - return cursor_offset - offset; - } - - ptrdiff_t CursorOffset() const { - return OffsetFrom(0); - } - - template - T GetOffsetAddress(ptrdiff_t offset) const { - VIXL_ASSERT((offset >= 0) && (offset <= (cursor_ - buffer_))); - return reinterpret_cast(buffer_ + offset); - } - - size_t RemainingBytes() const { - VIXL_ASSERT((cursor_ >= buffer_) && (cursor_ <= (buffer_ + capacity_))); - return (buffer_ + capacity_) - cursor_; - } - - // A code buffer can emit: - // * 32-bit data: instruction and constant. - // * 64-bit data: constant. - // * string: debug info. - void Emit32(uint32_t data) { Emit(data); } - - void Emit64(uint64_t data) { Emit(data); } - - void EmitString(const char* string); - - // Align to kInstructionSize. - void Align(); - - size_t capacity() const { return capacity_; } - - bool IsManaged() const { return managed_; } - - void Grow(size_t new_capacity); - - bool IsDirty() const { return dirty_; } - - void SetClean() { dirty_ = false; } - - private: - template - void Emit(T value) { - VIXL_ASSERT(RemainingBytes() >= sizeof(value)); - dirty_ = true; - memcpy(cursor_, &value, sizeof(value)); - cursor_ += sizeof(value); - } - - // Backing store of the buffer. - byte* buffer_; - // If true the backing store is allocated and deallocated by the buffer. The - // backing store can then grow on demand. If false the backing store is - // provided by the user and cannot be resized internally. - bool managed_; - // Pointer to the next location to be written. - byte* cursor_; - // True if there has been any write since the buffer was created or cleaned. - bool dirty_; - // Capacity in bytes of the backing store. - size_t capacity_; -}; - -} // namespace vixl - -#endif // VIXL_CODE_BUFFER_H - diff --git a/disas/libvixl/vixl/compiler-intrinsics.cc b/disas/libvixl/vixl/compiler-intrinsics.cc deleted file mode 100644 index fd551faeb1..0000000000 --- a/disas/libvixl/vixl/compiler-intrinsics.cc +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#include "compiler-intrinsics.h" - -namespace vixl { - - -int CountLeadingSignBitsFallBack(int64_t value, int width) { - VIXL_ASSERT(IsPowerOf2(width) && (width <= 64)); - if (value >= 0) { - return CountLeadingZeros(value, width) - 1; - } else { - return CountLeadingZeros(~value, width) - 1; - } -} - - -int CountLeadingZerosFallBack(uint64_t value, int width) { - VIXL_ASSERT(IsPowerOf2(width) && (width <= 64)); - if (value == 0) { - return width; - } - int count = 0; - value = value << (64 - width); - if ((value & UINT64_C(0xffffffff00000000)) == 0) { - count += 32; - value = value << 32; - } - if ((value & UINT64_C(0xffff000000000000)) == 0) { - count += 16; - value = value << 16; - } - if ((value & UINT64_C(0xff00000000000000)) == 0) { - count += 8; - value = value << 8; - } - if ((value & UINT64_C(0xf000000000000000)) == 0) { - count += 4; - value = value << 4; - } - if ((value & UINT64_C(0xc000000000000000)) == 0) { - count += 2; - value = value << 2; - } - if ((value & UINT64_C(0x8000000000000000)) == 0) { - count += 1; - } - count += (value == 0); - return count; -} - - -int CountSetBitsFallBack(uint64_t value, int width) { - VIXL_ASSERT(IsPowerOf2(width) && (width <= 64)); - - // Mask out unused bits to ensure that they are not counted. - value &= (UINT64_C(0xffffffffffffffff) >> (64 - width)); - - // Add up the set bits. - // The algorithm works by adding pairs of bit fields together iteratively, - // where the size of each bit field doubles each time. - // An example for an 8-bit value: - // Bits: h g f e d c b a - // \ | \ | \ | \ | - // value = h+g f+e d+c b+a - // \ | \ | - // value = h+g+f+e d+c+b+a - // \ | - // value = h+g+f+e+d+c+b+a - const uint64_t kMasks[] = { - UINT64_C(0x5555555555555555), - UINT64_C(0x3333333333333333), - UINT64_C(0x0f0f0f0f0f0f0f0f), - UINT64_C(0x00ff00ff00ff00ff), - UINT64_C(0x0000ffff0000ffff), - UINT64_C(0x00000000ffffffff), - }; - - for (unsigned i = 0; i < (sizeof(kMasks) / sizeof(kMasks[0])); i++) { - int shift = 1 << i; - value = ((value >> shift) & kMasks[i]) + (value & kMasks[i]); - } - - return static_cast(value); -} - - -int CountTrailingZerosFallBack(uint64_t value, int width) { - VIXL_ASSERT(IsPowerOf2(width) && (width <= 64)); - int count = 0; - value = value << (64 - width); - if ((value & UINT64_C(0xffffffff)) == 0) { - count += 32; - value = value >> 32; - } - if ((value & 0xffff) == 0) { - count += 16; - value = value >> 16; - } - if ((value & 0xff) == 0) { - count += 8; - value = value >> 8; - } - if ((value & 0xf) == 0) { - count += 4; - value = value >> 4; - } - if ((value & 0x3) == 0) { - count += 2; - value = value >> 2; - } - if ((value & 0x1) == 0) { - count += 1; - } - count += (value == 0); - return count - (64 - width); -} - - -} // namespace vixl diff --git a/disas/libvixl/vixl/compiler-intrinsics.h b/disas/libvixl/vixl/compiler-intrinsics.h deleted file mode 100644 index 9431beddb9..0000000000 --- a/disas/libvixl/vixl/compiler-intrinsics.h +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - - -#ifndef VIXL_COMPILER_INTRINSICS_H -#define VIXL_COMPILER_INTRINSICS_H - -#include "globals.h" - -namespace vixl { - -// Helper to check whether the version of GCC used is greater than the specified -// requirement. -#define MAJOR 1000000 -#define MINOR 1000 -#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) -#define GCC_VERSION_OR_NEWER(major, minor, patchlevel) \ - ((__GNUC__ * MAJOR + __GNUC_MINOR__ * MINOR + __GNUC_PATCHLEVEL__) >= \ - ((major) * MAJOR + (minor) * MINOR + (patchlevel))) -#elif defined(__GNUC__) && defined(__GNUC_MINOR__) -#define GCC_VERSION_OR_NEWER(major, minor, patchlevel) \ - ((__GNUC__ * MAJOR + __GNUC_MINOR__ * MINOR) >= \ - ((major) * MAJOR + (minor) * MINOR + (patchlevel))) -#else -#define GCC_VERSION_OR_NEWER(major, minor, patchlevel) 0 -#endif - - -#if defined(__clang__) && !defined(VIXL_NO_COMPILER_BUILTINS) - -#define COMPILER_HAS_BUILTIN_CLRSB (__has_builtin(__builtin_clrsb)) -#define COMPILER_HAS_BUILTIN_CLZ (__has_builtin(__builtin_clz)) -#define COMPILER_HAS_BUILTIN_CTZ (__has_builtin(__builtin_ctz)) -#define COMPILER_HAS_BUILTIN_FFS (__has_builtin(__builtin_ffs)) -#define COMPILER_HAS_BUILTIN_POPCOUNT (__has_builtin(__builtin_popcount)) - -#elif defined(__GNUC__) && !defined(VIXL_NO_COMPILER_BUILTINS) -// The documentation for these builtins is available at: -// https://gcc.gnu.org/onlinedocs/gcc-$MAJOR.$MINOR.$PATCHLEVEL/gcc//Other-Builtins.html - -# define COMPILER_HAS_BUILTIN_CLRSB (GCC_VERSION_OR_NEWER(4, 7, 0)) -# define COMPILER_HAS_BUILTIN_CLZ (GCC_VERSION_OR_NEWER(3, 4, 0)) -# define COMPILER_HAS_BUILTIN_CTZ (GCC_VERSION_OR_NEWER(3, 4, 0)) -# define COMPILER_HAS_BUILTIN_FFS (GCC_VERSION_OR_NEWER(3, 4, 0)) -# define COMPILER_HAS_BUILTIN_POPCOUNT (GCC_VERSION_OR_NEWER(3, 4, 0)) - -#else -// One can define VIXL_NO_COMPILER_BUILTINS to force using the manually -// implemented C++ methods. - -#define COMPILER_HAS_BUILTIN_BSWAP false -#define COMPILER_HAS_BUILTIN_CLRSB false -#define COMPILER_HAS_BUILTIN_CLZ false -#define COMPILER_HAS_BUILTIN_CTZ false -#define COMPILER_HAS_BUILTIN_FFS false -#define COMPILER_HAS_BUILTIN_POPCOUNT false - -#endif - - -template -inline bool IsPowerOf2(V value) { - return (value != 0) && ((value & (value - 1)) == 0); -} - - -// Declaration of fallback functions. -int CountLeadingSignBitsFallBack(int64_t value, int width); -int CountLeadingZerosFallBack(uint64_t value, int width); -int CountSetBitsFallBack(uint64_t value, int width); -int CountTrailingZerosFallBack(uint64_t value, int width); - - -// Implementation of intrinsics functions. -// TODO: The implementations could be improved for sizes different from 32bit -// and 64bit: we could mask the values and call the appropriate builtin. - -template -inline int CountLeadingSignBits(V value, int width = (sizeof(V) * 8)) { -#if COMPILER_HAS_BUILTIN_CLRSB - if (width == 32) { - return __builtin_clrsb(value); - } else if (width == 64) { - return __builtin_clrsbll(value); - } -#endif - return CountLeadingSignBitsFallBack(value, width); -} - - -template -inline int CountLeadingZeros(V value, int width = (sizeof(V) * 8)) { -#if COMPILER_HAS_BUILTIN_CLZ - if (width == 32) { - return (value == 0) ? 32 : __builtin_clz(static_cast(value)); - } else if (width == 64) { - return (value == 0) ? 64 : __builtin_clzll(value); - } -#endif - return CountLeadingZerosFallBack(value, width); -} - - -template -inline int CountSetBits(V value, int width = (sizeof(V) * 8)) { -#if COMPILER_HAS_BUILTIN_POPCOUNT - if (width == 32) { - return __builtin_popcount(static_cast(value)); - } else if (width == 64) { - return __builtin_popcountll(value); - } -#endif - return CountSetBitsFallBack(value, width); -} - - -template -inline int CountTrailingZeros(V value, int width = (sizeof(V) * 8)) { -#if COMPILER_HAS_BUILTIN_CTZ - if (width == 32) { - return (value == 0) ? 32 : __builtin_ctz(static_cast(value)); - } else if (width == 64) { - return (value == 0) ? 64 : __builtin_ctzll(value); - } -#endif - return CountTrailingZerosFallBack(value, width); -} - -} // namespace vixl - -#endif // VIXL_COMPILER_INTRINSICS_H - diff --git a/disas/libvixl/vixl/globals.h b/disas/libvixl/vixl/globals.h deleted file mode 100644 index 3a71942f1e..0000000000 --- a/disas/libvixl/vixl/globals.h +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#ifndef VIXL_GLOBALS_H -#define VIXL_GLOBALS_H - -// Get standard C99 macros for integer types. -#ifndef __STDC_CONSTANT_MACROS -#define __STDC_CONSTANT_MACROS -#endif - -#ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS -#endif - -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS -#endif - -extern "C" { -#include -#include -} - -#include -#include -#include -#include -#include - -#include "vixl/platform.h" - - -typedef uint8_t byte; - -// Type for half-precision (16 bit) floating point numbers. -typedef uint16_t float16; - -const int KBytes = 1024; -const int MBytes = 1024 * KBytes; - -#define VIXL_ABORT() \ - do { printf("in %s, line %i", __FILE__, __LINE__); abort(); } while (false) -#ifdef VIXL_DEBUG - #define VIXL_ASSERT(condition) assert(condition) - #define VIXL_CHECK(condition) VIXL_ASSERT(condition) - #define VIXL_UNIMPLEMENTED() \ - do { fprintf(stderr, "UNIMPLEMENTED\t"); VIXL_ABORT(); } while (false) - #define VIXL_UNREACHABLE() \ - do { fprintf(stderr, "UNREACHABLE\t"); VIXL_ABORT(); } while (false) -#else - #define VIXL_ASSERT(condition) ((void) 0) - #define VIXL_CHECK(condition) assert(condition) - #define VIXL_UNIMPLEMENTED() ((void) 0) - #define VIXL_UNREACHABLE() ((void) 0) -#endif -// This is not as powerful as template based assertions, but it is simple. -// It assumes that the descriptions are unique. If this starts being a problem, -// we can switch to a different implemention. -#define VIXL_CONCAT(a, b) a##b -#define VIXL_STATIC_ASSERT_LINE(line, condition) \ - typedef char VIXL_CONCAT(STATIC_ASSERT_LINE_, line)[(condition) ? 1 : -1] \ - __attribute__((unused)) -#define VIXL_STATIC_ASSERT(condition) \ - VIXL_STATIC_ASSERT_LINE(__LINE__, condition) - -template -inline void USE(T1) {} - -template -inline void USE(T1, T2) {} - -template -inline void USE(T1, T2, T3) {} - -template -inline void USE(T1, T2, T3, T4) {} - -#define VIXL_ALIGNMENT_EXCEPTION() \ - do { fprintf(stderr, "ALIGNMENT EXCEPTION\t"); VIXL_ABORT(); } while (0) - -// The clang::fallthrough attribute is used along with the Wimplicit-fallthrough -// argument to annotate intentional fall-through between switch labels. -// For more information please refer to: -// http://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough -#ifndef __has_warning - #define __has_warning(x) 0 -#endif - -// Fallthrough annotation for Clang and C++11(201103L). -#if __has_warning("-Wimplicit-fallthrough") && __cplusplus >= 201103L - #define VIXL_FALLTHROUGH() [[clang::fallthrough]] //NOLINT -// Fallthrough annotation for GCC >= 7. -#elif __GNUC__ >= 7 - #define VIXL_FALLTHROUGH() __attribute__((fallthrough)) -#else - #define VIXL_FALLTHROUGH() do {} while (0) -#endif - -#if __cplusplus >= 201103L - #define VIXL_NO_RETURN [[noreturn]] //NOLINT -#else - #define VIXL_NO_RETURN __attribute__((noreturn)) -#endif - -// Some functions might only be marked as "noreturn" for the DEBUG build. This -// macro should be used for such cases (for more details see what -// VIXL_UNREACHABLE expands to). -#ifdef VIXL_DEBUG - #define VIXL_DEBUG_NO_RETURN VIXL_NO_RETURN -#else - #define VIXL_DEBUG_NO_RETURN -#endif - -#ifdef VIXL_INCLUDE_SIMULATOR -#ifndef VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE - #define VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE 1 -#endif -#else -#ifndef VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE - #define VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE 0 -#endif -#if VIXL_GENERATE_SIMULATOR_INSTRUCTIONS_VALUE - #warning "Generating Simulator instructions without Simulator support." -#endif -#endif - -#ifdef USE_SIMULATOR - #error "Please see the release notes for USE_SIMULATOR." -#endif - -#endif // VIXL_GLOBALS_H diff --git a/disas/libvixl/vixl/invalset.h b/disas/libvixl/vixl/invalset.h deleted file mode 100644 index 2e0871f8c3..0000000000 --- a/disas/libvixl/vixl/invalset.h +++ /dev/null @@ -1,775 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#ifndef VIXL_INVALSET_H_ -#define VIXL_INVALSET_H_ - -#include - -#include -#include - -#include "vixl/globals.h" - -namespace vixl { - -// We define a custom data structure template and its iterator as `std` -// containers do not fit the performance requirements for some of our use cases. -// -// The structure behaves like an iterable unordered set with special properties -// and restrictions. "InvalSet" stands for "Invalidatable Set". -// -// Restrictions and requirements: -// - Adding an element already present in the set is illegal. In debug mode, -// this is checked at insertion time. -// - The templated class `ElementType` must provide comparison operators so that -// `std::sort()` can be used. -// - A key must be available to represent invalid elements. -// - Elements with an invalid key must compare higher or equal to any other -// element. -// -// Use cases and performance considerations: -// Our use cases present two specificities that allow us to design this -// structure to provide fast insertion *and* fast search and deletion -// operations: -// - Elements are (generally) inserted in order (sorted according to their key). -// - A key is available to mark elements as invalid (deleted). -// The backing `std::vector` allows for fast insertions. When -// searching for an element we ensure the elements are sorted (this is generally -// the case) and perform a binary search. When deleting an element we do not -// free the associated memory immediately. Instead, an element to be deleted is -// marked with the 'invalid' key. Other methods of the container take care of -// ignoring entries marked as invalid. -// To avoid the overhead of the `std::vector` container when only few entries -// are used, a number of elements are preallocated. - -// 'ElementType' and 'KeyType' are respectively the types of the elements and -// their key. The structure only reclaims memory when safe to do so, if the -// number of elements that can be reclaimed is greater than `RECLAIM_FROM` and -// greater than ` / RECLAIM_FACTOR. -#define TEMPLATE_INVALSET_P_DECL \ - class ElementType, \ - unsigned N_PREALLOCATED_ELEMENTS, \ - class KeyType, \ - KeyType INVALID_KEY, \ - size_t RECLAIM_FROM, \ - unsigned RECLAIM_FACTOR - -#define TEMPLATE_INVALSET_P_DEF \ -ElementType, N_PREALLOCATED_ELEMENTS, \ -KeyType, INVALID_KEY, RECLAIM_FROM, RECLAIM_FACTOR - -template class InvalSetIterator; // Forward declaration. - -template class InvalSet { - public: - InvalSet(); - ~InvalSet(); - - static const size_t kNPreallocatedElements = N_PREALLOCATED_ELEMENTS; - static const KeyType kInvalidKey = INVALID_KEY; - - // It is illegal to insert an element already present in the set. - void insert(const ElementType& element); - - // Looks for the specified element in the set and - if found - deletes it. - void erase(const ElementType& element); - - // This indicates the number of (valid) elements stored in this set. - size_t size() const; - - // Returns true if no elements are stored in the set. - // Note that this does not mean the the backing storage is empty: it can still - // contain invalid elements. - bool empty() const; - - void clear(); - - const ElementType min_element(); - - // This returns the key of the minimum element in the set. - KeyType min_element_key(); - - static bool IsValid(const ElementType& element); - static KeyType Key(const ElementType& element); - static void SetKey(ElementType* element, KeyType key); - - protected: - // Returns a pointer to the element in vector_ if it was found, or NULL - // otherwise. - ElementType* Search(const ElementType& element); - - // The argument *must* point to an element stored in *this* set. - // This function is not allowed to move elements in the backing vector - // storage. - void EraseInternal(ElementType* element); - - // The elements in the range searched must be sorted. - ElementType* BinarySearch(const ElementType& element, - ElementType* start, - ElementType* end) const; - - // Sort the elements. - enum SortType { - // The 'hard' version guarantees that invalid elements are moved to the end - // of the container. - kHardSort, - // The 'soft' version only guarantees that the elements will be sorted. - // Invalid elements may still be present anywhere in the set. - kSoftSort - }; - void Sort(SortType sort_type); - - // Delete the elements that have an invalid key. The complexity is linear - // with the size of the vector. - void Clean(); - - const ElementType Front() const; - const ElementType Back() const; - - // Delete invalid trailing elements and return the last valid element in the - // set. - const ElementType CleanBack(); - - // Returns a pointer to the start or end of the backing storage. - const ElementType* StorageBegin() const; - const ElementType* StorageEnd() const; - ElementType* StorageBegin(); - ElementType* StorageEnd(); - - // Returns the index of the element within the backing storage. The element - // must belong to the backing storage. - size_t ElementIndex(const ElementType* element) const; - - // Returns the element at the specified index in the backing storage. - const ElementType* ElementAt(size_t index) const; - ElementType* ElementAt(size_t index); - - static const ElementType* FirstValidElement(const ElementType* from, - const ElementType* end); - - void CacheMinElement(); - const ElementType CachedMinElement() const; - - bool ShouldReclaimMemory() const; - void ReclaimMemory(); - - bool IsUsingVector() const { return vector_ != NULL; } - void set_sorted(bool sorted) { sorted_ = sorted; } - - // We cache some data commonly required by users to improve performance. - // We cannot cache pointers to elements as we do not control the backing - // storage. - bool valid_cached_min_; - size_t cached_min_index_; // Valid iff `valid_cached_min_` is true. - KeyType cached_min_key_; // Valid iff `valid_cached_min_` is true. - - // Indicates whether the elements are sorted. - bool sorted_; - - // This represents the number of (valid) elements in this set. - size_t size_; - - // The backing storage is either the array of preallocated elements or the - // vector. The structure starts by using the preallocated elements, and - // transitions (permanently) to using the vector once more than - // kNPreallocatedElements are used. - // Elements are only invalidated when using the vector. The preallocated - // storage always only contains valid elements. - ElementType preallocated_[kNPreallocatedElements]; - std::vector* vector_; - -#ifdef VIXL_DEBUG - // Iterators acquire and release this monitor. While a set is acquired, - // certain operations are illegal to ensure that the iterator will - // correctly iterate over the elements in the set. - int monitor_; - int monitor() const { return monitor_; } - void Acquire() { monitor_++; } - void Release() { - monitor_--; - VIXL_ASSERT(monitor_ >= 0); - } -#endif - - friend class InvalSetIterator >; - typedef ElementType _ElementType; - typedef KeyType _KeyType; -}; - - -template class InvalSetIterator { - private: - // Redefine types to mirror the associated set types. - typedef typename S::_ElementType ElementType; - typedef typename S::_KeyType KeyType; - - public: - explicit InvalSetIterator(S* inval_set); - ~InvalSetIterator(); - - ElementType* Current() const; - void Advance(); - bool Done() const; - - // Mark this iterator as 'done'. - void Finish(); - - // Delete the current element and advance the iterator to point to the next - // element. - void DeleteCurrentAndAdvance(); - - static bool IsValid(const ElementType& element); - static KeyType Key(const ElementType& element); - - protected: - void MoveToValidElement(); - - // Indicates if the iterator is looking at the vector or at the preallocated - // elements. - const bool using_vector_; - // Used when looking at the preallocated elements, or in debug mode when using - // the vector to track how many times the iterator has advanced. - size_t index_; - typename std::vector::iterator iterator_; - S* inval_set_; -}; - - -template -InvalSet::InvalSet() - : valid_cached_min_(false), - sorted_(true), size_(0), vector_(NULL) { -#ifdef VIXL_DEBUG - monitor_ = 0; -#endif -} - - -template -InvalSet::~InvalSet() { - VIXL_ASSERT(monitor_ == 0); - delete vector_; -} - - -template -void InvalSet::insert(const ElementType& element) { - VIXL_ASSERT(monitor() == 0); - VIXL_ASSERT(IsValid(element)); - VIXL_ASSERT(Search(element) == NULL); - set_sorted(empty() || (sorted_ && (element > CleanBack()))); - if (IsUsingVector()) { - vector_->push_back(element); - } else { - if (size_ < kNPreallocatedElements) { - preallocated_[size_] = element; - } else { - // Transition to using the vector. - vector_ = new std::vector(preallocated_, - preallocated_ + size_); - vector_->push_back(element); - } - } - size_++; - - if (valid_cached_min_ && (element < min_element())) { - cached_min_index_ = IsUsingVector() ? vector_->size() - 1 : size_ - 1; - cached_min_key_ = Key(element); - valid_cached_min_ = true; - } - - if (ShouldReclaimMemory()) { - ReclaimMemory(); - } -} - - -template -void InvalSet::erase(const ElementType& element) { - VIXL_ASSERT(monitor() == 0); - VIXL_ASSERT(IsValid(element)); - ElementType* local_element = Search(element); - if (local_element != NULL) { - EraseInternal(local_element); - } -} - - -template -ElementType* InvalSet::Search( - const ElementType& element) { - VIXL_ASSERT(monitor() == 0); - if (empty()) { - return NULL; - } - if (ShouldReclaimMemory()) { - ReclaimMemory(); - } - if (!sorted_) { - Sort(kHardSort); - } - if (!valid_cached_min_) { - CacheMinElement(); - } - return BinarySearch(element, ElementAt(cached_min_index_), StorageEnd()); -} - - -template -size_t InvalSet::size() const { - return size_; -} - - -template -bool InvalSet::empty() const { - return size_ == 0; -} - - -template -void InvalSet::clear() { - VIXL_ASSERT(monitor() == 0); - size_ = 0; - if (IsUsingVector()) { - vector_->clear(); - } - set_sorted(true); - valid_cached_min_ = false; -} - - -template -const ElementType InvalSet::min_element() { - VIXL_ASSERT(monitor() == 0); - VIXL_ASSERT(!empty()); - CacheMinElement(); - return *ElementAt(cached_min_index_); -} - - -template -KeyType InvalSet::min_element_key() { - VIXL_ASSERT(monitor() == 0); - if (valid_cached_min_) { - return cached_min_key_; - } else { - return Key(min_element()); - } -} - - -template -bool InvalSet::IsValid(const ElementType& element) { - return Key(element) != kInvalidKey; -} - - -template -void InvalSet::EraseInternal(ElementType* element) { - // Note that this function must be safe even while an iterator has acquired - // this set. - VIXL_ASSERT(element != NULL); - size_t deleted_index = ElementIndex(element); - if (IsUsingVector()) { - VIXL_ASSERT((&(vector_->front()) <= element) && - (element <= &(vector_->back()))); - SetKey(element, kInvalidKey); - } else { - VIXL_ASSERT((preallocated_ <= element) && - (element < (preallocated_ + kNPreallocatedElements))); - ElementType* end = preallocated_ + kNPreallocatedElements; - size_t copy_size = sizeof(*element) * (end - element - 1); - memmove(element, element + 1, copy_size); - } - size_--; - - if (valid_cached_min_ && - (deleted_index == cached_min_index_)) { - if (sorted_ && !empty()) { - const ElementType* min = FirstValidElement(element, StorageEnd()); - cached_min_index_ = ElementIndex(min); - cached_min_key_ = Key(*min); - valid_cached_min_ = true; - } else { - valid_cached_min_ = false; - } - } -} - - -template -ElementType* InvalSet::BinarySearch( - const ElementType& element, ElementType* start, ElementType* end) const { - if (start == end) { - return NULL; - } - VIXL_ASSERT(sorted_); - VIXL_ASSERT(start < end); - VIXL_ASSERT(!empty()); - - // Perform a binary search through the elements while ignoring invalid - // elements. - ElementType* elements = start; - size_t low = 0; - size_t high = (end - start) - 1; - while (low < high) { - // Find valid bounds. - while (!IsValid(elements[low]) && (low < high)) ++low; - while (!IsValid(elements[high]) && (low < high)) --high; - VIXL_ASSERT(low <= high); - // Avoid overflow when computing the middle index. - size_t middle = low / 2 + high / 2 + (low & high & 1); - if ((middle == low) || (middle == high)) { - break; - } - while (!IsValid(elements[middle]) && (middle < high - 1)) ++middle; - while (!IsValid(elements[middle]) && (low + 1 < middle)) --middle; - if (!IsValid(elements[middle])) { - break; - } - if (elements[middle] < element) { - low = middle; - } else { - high = middle; - } - } - - if (elements[low] == element) return &elements[low]; - if (elements[high] == element) return &elements[high]; - return NULL; -} - - -template -void InvalSet::Sort(SortType sort_type) { - VIXL_ASSERT(monitor() == 0); - if (sort_type == kSoftSort) { - if (sorted_) { - return; - } - } - if (empty()) { - return; - } - - Clean(); - std::sort(StorageBegin(), StorageEnd()); - - set_sorted(true); - cached_min_index_ = 0; - cached_min_key_ = Key(Front()); - valid_cached_min_ = true; -} - - -template -void InvalSet::Clean() { - VIXL_ASSERT(monitor() == 0); - if (empty() || !IsUsingVector()) { - return; - } - // Manually iterate through the vector storage to discard invalid elements. - ElementType* start = &(vector_->front()); - ElementType* end = start + vector_->size(); - ElementType* c = start; - ElementType* first_invalid; - ElementType* first_valid; - ElementType* next_invalid; - - while (c < end && IsValid(*c)) { c++; } - first_invalid = c; - - while (c < end) { - while (c < end && !IsValid(*c)) { c++; } - first_valid = c; - while (c < end && IsValid(*c)) { c++; } - next_invalid = c; - - ptrdiff_t n_moved_elements = (next_invalid - first_valid); - memmove(first_invalid, first_valid, n_moved_elements * sizeof(*c)); - first_invalid = first_invalid + n_moved_elements; - c = next_invalid; - } - - // Delete the trailing invalid elements. - vector_->erase(vector_->begin() + (first_invalid - start), vector_->end()); - VIXL_ASSERT(vector_->size() == size_); - - if (sorted_) { - valid_cached_min_ = true; - cached_min_index_ = 0; - cached_min_key_ = Key(*ElementAt(0)); - } else { - valid_cached_min_ = false; - } -} - - -template -const ElementType InvalSet::Front() const { - VIXL_ASSERT(!empty()); - return IsUsingVector() ? vector_->front() : preallocated_[0]; -} - - -template -const ElementType InvalSet::Back() const { - VIXL_ASSERT(!empty()); - return IsUsingVector() ? vector_->back() : preallocated_[size_ - 1]; -} - - -template -const ElementType InvalSet::CleanBack() { - VIXL_ASSERT(monitor() == 0); - if (IsUsingVector()) { - // Delete the invalid trailing elements. - typename std::vector::reverse_iterator it = vector_->rbegin(); - while (!IsValid(*it)) { - it++; - } - vector_->erase(it.base(), vector_->end()); - } - return Back(); -} - - -template -const ElementType* InvalSet::StorageBegin() const { - return IsUsingVector() ? &(vector_->front()) : preallocated_; -} - - -template -const ElementType* InvalSet::StorageEnd() const { - return IsUsingVector() ? &(vector_->back()) + 1 : preallocated_ + size_; -} - - -template -ElementType* InvalSet::StorageBegin() { - return IsUsingVector() ? &(vector_->front()) : preallocated_; -} - - -template -ElementType* InvalSet::StorageEnd() { - return IsUsingVector() ? &(vector_->back()) + 1 : preallocated_ + size_; -} - - -template -size_t InvalSet::ElementIndex( - const ElementType* element) const { - VIXL_ASSERT((StorageBegin() <= element) && (element < StorageEnd())); - return element - StorageBegin(); -} - - -template -const ElementType* InvalSet::ElementAt( - size_t index) const { - VIXL_ASSERT( - (IsUsingVector() && (index < vector_->size())) || (index < size_)); - return StorageBegin() + index; -} - -template -ElementType* InvalSet::ElementAt(size_t index) { - VIXL_ASSERT( - (IsUsingVector() && (index < vector_->size())) || (index < size_)); - return StorageBegin() + index; -} - -template -const ElementType* InvalSet::FirstValidElement( - const ElementType* from, const ElementType* end) { - while ((from < end) && !IsValid(*from)) { - from++; - } - return from; -} - - -template -void InvalSet::CacheMinElement() { - VIXL_ASSERT(monitor() == 0); - VIXL_ASSERT(!empty()); - - if (valid_cached_min_) { - return; - } - - if (sorted_) { - const ElementType* min = FirstValidElement(StorageBegin(), StorageEnd()); - cached_min_index_ = ElementIndex(min); - cached_min_key_ = Key(*min); - valid_cached_min_ = true; - } else { - Sort(kHardSort); - } - VIXL_ASSERT(valid_cached_min_); -} - - -template -bool InvalSet::ShouldReclaimMemory() const { - if (!IsUsingVector()) { - return false; - } - size_t n_invalid_elements = vector_->size() - size_; - return (n_invalid_elements > RECLAIM_FROM) && - (n_invalid_elements > vector_->size() / RECLAIM_FACTOR); -} - - -template -void InvalSet::ReclaimMemory() { - VIXL_ASSERT(monitor() == 0); - Clean(); -} - - -template -InvalSetIterator::InvalSetIterator(S* inval_set) - : using_vector_((inval_set != NULL) && inval_set->IsUsingVector()), - index_(0), - inval_set_(inval_set) { - if (inval_set != NULL) { - inval_set->Sort(S::kSoftSort); -#ifdef VIXL_DEBUG - inval_set->Acquire(); -#endif - if (using_vector_) { - iterator_ = typename std::vector::iterator( - inval_set_->vector_->begin()); - } - MoveToValidElement(); - } -} - - -template -InvalSetIterator::~InvalSetIterator() { -#ifdef VIXL_DEBUG - if (inval_set_ != NULL) { - inval_set_->Release(); - } -#endif -} - - -template -typename S::_ElementType* InvalSetIterator::Current() const { - VIXL_ASSERT(!Done()); - if (using_vector_) { - return &(*iterator_); - } else { - return &(inval_set_->preallocated_[index_]); - } -} - - -template -void InvalSetIterator::Advance() { - VIXL_ASSERT(!Done()); - if (using_vector_) { - iterator_++; -#ifdef VIXL_DEBUG - index_++; -#endif - MoveToValidElement(); - } else { - index_++; - } -} - - -template -bool InvalSetIterator::Done() const { - if (using_vector_) { - bool done = (iterator_ == inval_set_->vector_->end()); - VIXL_ASSERT(done == (index_ == inval_set_->size())); - return done; - } else { - return index_ == inval_set_->size(); - } -} - - -template -void InvalSetIterator::Finish() { - VIXL_ASSERT(inval_set_->sorted_); - if (using_vector_) { - iterator_ = inval_set_->vector_->end(); - } - index_ = inval_set_->size(); -} - - -template -void InvalSetIterator::DeleteCurrentAndAdvance() { - if (using_vector_) { - inval_set_->EraseInternal(&(*iterator_)); - MoveToValidElement(); - } else { - inval_set_->EraseInternal(inval_set_->preallocated_ + index_); - } -} - - -template -bool InvalSetIterator::IsValid(const ElementType& element) { - return S::IsValid(element); -} - - -template -typename S::_KeyType InvalSetIterator::Key(const ElementType& element) { - return S::Key(element); -} - - -template -void InvalSetIterator::MoveToValidElement() { - if (using_vector_) { - while ((iterator_ != inval_set_->vector_->end()) && !IsValid(*iterator_)) { - iterator_++; - } - } else { - VIXL_ASSERT(inval_set_->empty() || IsValid(inval_set_->preallocated_[0])); - // Nothing to do. - } -} - -#undef TEMPLATE_INVALSET_P_DECL -#undef TEMPLATE_INVALSET_P_DEF - -} // namespace vixl - -#endif // VIXL_INVALSET_H_ diff --git a/disas/libvixl/vixl/platform.h b/disas/libvixl/vixl/platform.h deleted file mode 100644 index 26a74de81b..0000000000 --- a/disas/libvixl/vixl/platform.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2014, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#ifndef PLATFORM_H -#define PLATFORM_H - -// Define platform specific functionalities. -extern "C" { -#include -} - -namespace vixl { -inline void HostBreakpoint() { raise(SIGINT); } -} // namespace vixl - -#endif diff --git a/disas/libvixl/vixl/utils.cc b/disas/libvixl/vixl/utils.cc deleted file mode 100644 index 69304d266d..0000000000 --- a/disas/libvixl/vixl/utils.cc +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#include "vixl/utils.h" -#include - -namespace vixl { - -uint32_t float_to_rawbits(float value) { - uint32_t bits = 0; - memcpy(&bits, &value, 4); - return bits; -} - - -uint64_t double_to_rawbits(double value) { - uint64_t bits = 0; - memcpy(&bits, &value, 8); - return bits; -} - - -float rawbits_to_float(uint32_t bits) { - float value = 0.0; - memcpy(&value, &bits, 4); - return value; -} - - -double rawbits_to_double(uint64_t bits) { - double value = 0.0; - memcpy(&value, &bits, 8); - return value; -} - - -uint32_t float_sign(float val) { - uint32_t rawbits = float_to_rawbits(val); - return unsigned_bitextract_32(31, 31, rawbits); -} - - -uint32_t float_exp(float val) { - uint32_t rawbits = float_to_rawbits(val); - return unsigned_bitextract_32(30, 23, rawbits); -} - - -uint32_t float_mantissa(float val) { - uint32_t rawbits = float_to_rawbits(val); - return unsigned_bitextract_32(22, 0, rawbits); -} - - -uint32_t double_sign(double val) { - uint64_t rawbits = double_to_rawbits(val); - return static_cast(unsigned_bitextract_64(63, 63, rawbits)); -} - - -uint32_t double_exp(double val) { - uint64_t rawbits = double_to_rawbits(val); - return static_cast(unsigned_bitextract_64(62, 52, rawbits)); -} - - -uint64_t double_mantissa(double val) { - uint64_t rawbits = double_to_rawbits(val); - return unsigned_bitextract_64(51, 0, rawbits); -} - - -float float_pack(uint32_t sign, uint32_t exp, uint32_t mantissa) { - uint32_t bits = (sign << 31) | (exp << 23) | mantissa; - return rawbits_to_float(bits); -} - - -double double_pack(uint64_t sign, uint64_t exp, uint64_t mantissa) { - uint64_t bits = (sign << 63) | (exp << 52) | mantissa; - return rawbits_to_double(bits); -} - - -int float16classify(float16 value) { - uint16_t exponent_max = (1 << 5) - 1; - uint16_t exponent_mask = exponent_max << 10; - uint16_t mantissa_mask = (1 << 10) - 1; - - uint16_t exponent = (value & exponent_mask) >> 10; - uint16_t mantissa = value & mantissa_mask; - if (exponent == 0) { - if (mantissa == 0) { - return FP_ZERO; - } - return FP_SUBNORMAL; - } else if (exponent == exponent_max) { - if (mantissa == 0) { - return FP_INFINITE; - } - return FP_NAN; - } - return FP_NORMAL; -} - - -unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size) { - VIXL_ASSERT((reg_size % 8) == 0); - int count = 0; - for (unsigned i = 0; i < (reg_size / 16); i++) { - if ((imm & 0xffff) == 0) { - count++; - } - imm >>= 16; - } - return count; -} - -} // namespace vixl diff --git a/disas/libvixl/vixl/utils.h b/disas/libvixl/vixl/utils.h deleted file mode 100644 index ecb0f1014a..0000000000 --- a/disas/libvixl/vixl/utils.h +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2015, ARM Limited -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of ARM Limited nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - -#ifndef VIXL_UTILS_H -#define VIXL_UTILS_H - -#include -#include -#include "vixl/globals.h" -#include "vixl/compiler-intrinsics.h" - -namespace vixl { - -// Macros for compile-time format checking. -#if GCC_VERSION_OR_NEWER(4, 4, 0) -#define PRINTF_CHECK(format_index, varargs_index) \ - __attribute__((format(gnu_printf, format_index, varargs_index))) -#else -#define PRINTF_CHECK(format_index, varargs_index) -#endif - -// Check number width. -inline bool is_intn(unsigned n, int64_t x) { - VIXL_ASSERT((0 < n) && (n < 64)); - int64_t limit = INT64_C(1) << (n - 1); - return (-limit <= x) && (x < limit); -} - -inline bool is_uintn(unsigned n, int64_t x) { - VIXL_ASSERT((0 < n) && (n < 64)); - return !(x >> n); -} - -inline uint32_t truncate_to_intn(unsigned n, int64_t x) { - VIXL_ASSERT((0 < n) && (n < 64)); - return static_cast(x & ((INT64_C(1) << n) - 1)); -} - -#define INT_1_TO_63_LIST(V) \ -V(1) V(2) V(3) V(4) V(5) V(6) V(7) V(8) \ -V(9) V(10) V(11) V(12) V(13) V(14) V(15) V(16) \ -V(17) V(18) V(19) V(20) V(21) V(22) V(23) V(24) \ -V(25) V(26) V(27) V(28) V(29) V(30) V(31) V(32) \ -V(33) V(34) V(35) V(36) V(37) V(38) V(39) V(40) \ -V(41) V(42) V(43) V(44) V(45) V(46) V(47) V(48) \ -V(49) V(50) V(51) V(52) V(53) V(54) V(55) V(56) \ -V(57) V(58) V(59) V(60) V(61) V(62) V(63) - -#define DECLARE_IS_INT_N(N) \ -inline bool is_int##N(int64_t x) { return is_intn(N, x); } -#define DECLARE_IS_UINT_N(N) \ -inline bool is_uint##N(int64_t x) { return is_uintn(N, x); } -#define DECLARE_TRUNCATE_TO_INT_N(N) \ -inline uint32_t truncate_to_int##N(int x) { return truncate_to_intn(N, x); } -INT_1_TO_63_LIST(DECLARE_IS_INT_N) -INT_1_TO_63_LIST(DECLARE_IS_UINT_N) -INT_1_TO_63_LIST(DECLARE_TRUNCATE_TO_INT_N) -#undef DECLARE_IS_INT_N -#undef DECLARE_IS_UINT_N -#undef DECLARE_TRUNCATE_TO_INT_N - -// Bit field extraction. -inline uint32_t unsigned_bitextract_32(int msb, int lsb, uint32_t x) { - return (x >> lsb) & ((1 << (1 + msb - lsb)) - 1); -} - -inline uint64_t unsigned_bitextract_64(int msb, int lsb, uint64_t x) { - return (x >> lsb) & ((static_cast(1) << (1 + msb - lsb)) - 1); -} - -inline int32_t signed_bitextract_32(int msb, int lsb, int32_t x) { - return (x << (31 - msb)) >> (lsb + 31 - msb); -} - -inline int64_t signed_bitextract_64(int msb, int lsb, int64_t x) { - return (x << (63 - msb)) >> (lsb + 63 - msb); -} - -// Floating point representation. -uint32_t float_to_rawbits(float value); -uint64_t double_to_rawbits(double value); -float rawbits_to_float(uint32_t bits); -double rawbits_to_double(uint64_t bits); - -uint32_t float_sign(float val); -uint32_t float_exp(float val); -uint32_t float_mantissa(float val); -uint32_t double_sign(double val); -uint32_t double_exp(double val); -uint64_t double_mantissa(double val); - -float float_pack(uint32_t sign, uint32_t exp, uint32_t mantissa); -double double_pack(uint64_t sign, uint64_t exp, uint64_t mantissa); - -// An fpclassify() function for 16-bit half-precision floats. -int float16classify(float16 value); - -// NaN tests. -inline bool IsSignallingNaN(double num) { - const uint64_t kFP64QuietNaNMask = UINT64_C(0x0008000000000000); - uint64_t raw = double_to_rawbits(num); - if (std::isnan(num) && ((raw & kFP64QuietNaNMask) == 0)) { - return true; - } - return false; -} - - -inline bool IsSignallingNaN(float num) { - const uint32_t kFP32QuietNaNMask = 0x00400000; - uint32_t raw = float_to_rawbits(num); - if (std::isnan(num) && ((raw & kFP32QuietNaNMask) == 0)) { - return true; - } - return false; -} - - -inline bool IsSignallingNaN(float16 num) { - const uint16_t kFP16QuietNaNMask = 0x0200; - return (float16classify(num) == FP_NAN) && - ((num & kFP16QuietNaNMask) == 0); -} - - -template -inline bool IsQuietNaN(T num) { - return std::isnan(num) && !IsSignallingNaN(num); -} - - -// Convert the NaN in 'num' to a quiet NaN. -inline double ToQuietNaN(double num) { - const uint64_t kFP64QuietNaNMask = UINT64_C(0x0008000000000000); - VIXL_ASSERT(std::isnan(num)); - return rawbits_to_double(double_to_rawbits(num) | kFP64QuietNaNMask); -} - - -inline float ToQuietNaN(float num) { - const uint32_t kFP32QuietNaNMask = 0x00400000; - VIXL_ASSERT(std::isnan(num)); - return rawbits_to_float(float_to_rawbits(num) | kFP32QuietNaNMask); -} - - -// Fused multiply-add. -inline double FusedMultiplyAdd(double op1, double op2, double a) { - return fma(op1, op2, a); -} - - -inline float FusedMultiplyAdd(float op1, float op2, float a) { - return fmaf(op1, op2, a); -} - - -inline uint64_t LowestSetBit(uint64_t value) { - return value & -value; -} - - -template -inline int HighestSetBitPosition(T value) { - VIXL_ASSERT(value != 0); - return (sizeof(value) * 8 - 1) - CountLeadingZeros(value); -} - - -template -inline int WhichPowerOf2(V value) { - VIXL_ASSERT(IsPowerOf2(value)); - return CountTrailingZeros(value); -} - - -unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size); - - -template -T ReverseBits(T value) { - VIXL_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) || - (sizeof(value) == 4) || (sizeof(value) == 8)); - T result = 0; - for (unsigned i = 0; i < (sizeof(value) * 8); i++) { - result = (result << 1) | (value & 1); - value >>= 1; - } - return result; -} - - -template -T ReverseBytes(T value, int block_bytes_log2) { - VIXL_ASSERT((sizeof(value) == 4) || (sizeof(value) == 8)); - VIXL_ASSERT((1U << block_bytes_log2) <= sizeof(value)); - // Split the 64-bit value into an 8-bit array, where b[0] is the least - // significant byte, and b[7] is the most significant. - uint8_t bytes[8]; - uint64_t mask = UINT64_C(0xff00000000000000); - for (int i = 7; i >= 0; i--) { - bytes[i] = (static_cast(value) & mask) >> (i * 8); - mask >>= 8; - } - - // Permutation tables for REV instructions. - // permute_table[0] is used by REV16_x, REV16_w - // permute_table[1] is used by REV32_x, REV_w - // permute_table[2] is used by REV_x - VIXL_ASSERT((0 < block_bytes_log2) && (block_bytes_log2 < 4)); - static const uint8_t permute_table[3][8] = { {6, 7, 4, 5, 2, 3, 0, 1}, - {4, 5, 6, 7, 0, 1, 2, 3}, - {0, 1, 2, 3, 4, 5, 6, 7} }; - T result = 0; - for (int i = 0; i < 8; i++) { - result <<= 8; - result |= bytes[permute_table[block_bytes_log2 - 1][i]]; - } - return result; -} - - -// Pointer alignment -// TODO: rename/refactor to make it specific to instructions. -template -bool IsWordAligned(T pointer) { - VIXL_ASSERT(sizeof(pointer) == sizeof(intptr_t)); // NOLINT(runtime/sizeof) - return ((intptr_t)(pointer) & 3) == 0; -} - -// Increment a pointer (up to 64 bits) until it has the specified alignment. -template -T AlignUp(T pointer, size_t alignment) { - // Use C-style casts to get static_cast behaviour for integral types (T), and - // reinterpret_cast behaviour for other types. - - uint64_t pointer_raw = (uint64_t)pointer; - VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw)); - - size_t align_step = (alignment - pointer_raw) % alignment; - VIXL_ASSERT((pointer_raw + align_step) % alignment == 0); - - return (T)(pointer_raw + align_step); -} - -// Decrement a pointer (up to 64 bits) until it has the specified alignment. -template -T AlignDown(T pointer, size_t alignment) { - // Use C-style casts to get static_cast behaviour for integral types (T), and - // reinterpret_cast behaviour for other types. - - uint64_t pointer_raw = (uint64_t)pointer; - VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw)); - - size_t align_step = pointer_raw % alignment; - VIXL_ASSERT((pointer_raw - align_step) % alignment == 0); - - return (T)(pointer_raw - align_step); -} - -} // namespace vixl - -#endif // VIXL_UTILS_H diff --git a/disas/m68k.c b/disas/m68k.c index aefaecfbd6..1f16e295ab 100644 --- a/disas/m68k.c +++ b/disas/m68k.c @@ -1632,10 +1632,10 @@ print_insn_arg (const char *d, case '2': case '3': { - int val = fetch_arg (buffer, place, 5, info); + int reg = fetch_arg (buffer, place, 5, info); const char *name = 0; - switch (val) + switch (reg) { case 2: name = "%tt0"; break; case 3: name = "%tt1"; break; @@ -1655,12 +1655,12 @@ print_insn_arg (const char *d, int break_reg = ((buffer[3] >> 2) & 7); (*info->fprintf_func) - (info->stream, val == 0x1c ? "%%bad%d" : "%%bac%d", + (info->stream, reg == 0x1c ? "%%bad%d" : "%%bac%d", break_reg); } break; default: - (*info->fprintf_func) (info->stream, "", val); + (*info->fprintf_func) (info->stream, "", reg); } if (name) (*info->fprintf_func) (info->stream, "%s", name); diff --git a/disas/meson.build b/disas/meson.build index 16780fe05e..0e2aa4a913 100644 --- a/disas/meson.build +++ b/disas/meson.build @@ -1,20 +1,22 @@ -libvixl_ss = ss.source_set() -subdir('libvixl') - common_ss.add(when: 'CONFIG_ALPHA_DIS', if_true: files('alpha.c')) -common_ss.add(when: 'CONFIG_ARM_A64_DIS', if_true: files('arm-a64.cc')) -common_ss.add_all(when: 'CONFIG_ARM_A64_DIS', if_true: libvixl_ss) common_ss.add(when: 'CONFIG_CRIS_DIS', if_true: files('cris.c')) common_ss.add(when: 'CONFIG_HEXAGON_DIS', if_true: files('hexagon.c')) common_ss.add(when: 'CONFIG_E2K_DIS', if_true: files('e2k.c')) common_ss.add(when: 'CONFIG_HPPA_DIS', if_true: files('hppa.c')) common_ss.add(when: 'CONFIG_M68K_DIS', if_true: files('m68k.c')) common_ss.add(when: 'CONFIG_MICROBLAZE_DIS', if_true: files('microblaze.c')) -common_ss.add(when: 'CONFIG_MIPS_DIS', if_true: files('mips.c')) -common_ss.add(when: 'CONFIG_NANOMIPS_DIS', if_true: files('nanomips.cpp')) +common_ss.add(when: 'CONFIG_MIPS_DIS', if_true: files('mips.c', 'nanomips.c')) common_ss.add(when: 'CONFIG_NIOS2_DIS', if_true: files('nios2.c')) -common_ss.add(when: 'CONFIG_RISCV_DIS', if_true: files('riscv.c')) +common_ss.add(when: 'CONFIG_RISCV_DIS', if_true: files( + 'riscv.c', + 'riscv-xthead.c', + 'riscv-xventana.c' +)) common_ss.add(when: 'CONFIG_SH4_DIS', if_true: files('sh4.c')) common_ss.add(when: 'CONFIG_SPARC_DIS', if_true: files('sparc.c')) common_ss.add(when: 'CONFIG_XTENSA_DIS', if_true: files('xtensa.c')) -common_ss.add(when: capstone, if_true: files('capstone.c')) +common_ss.add(when: capstone, if_true: [files('capstone.c'), capstone]) +common_ss.add(files('disas.c')) + +system_ss.add(files('disas-mon.c')) +specific_ss.add(capstone) diff --git a/disas/mips.c b/disas/mips.c index b9a5204304..5aacacb2c8 100644 --- a/disas/mips.c +++ b/disas/mips.c @@ -20,6 +20,7 @@ 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 "disas/dis-asm.h" /* mips.h. Mips opcode list for GDB, the GNU debugger. @@ -1334,9 +1335,9 @@ const struct mips_opcode mips_builtin_opcodes[] = {"balc", "+p", 0xe8000000, 0xfc000000, UBD|WR_31, 0, I32R6}, {"bc", "+p", 0xc8000000, 0xfc000000, UBD|WR_31, 0, I32R6}, {"jic", "t,o", 0xd8000000, 0xffe00000, UBD|RD_t, 0, I32R6}, -{"beqzc", "s,+p", 0xd8000000, 0xfc000000, CBD|RD_s, 0, I32R6}, +{"beqzc", "s,+q", 0xd8000000, 0xfc000000, CBD|RD_s, 0, I32R6}, {"jialc", "t,o", 0xf8000000, 0xffe00000, UBD|RD_t, 0, I32R6}, -{"bnezc", "s,+p", 0xf8000000, 0xfc000000, CBD|RD_s, 0, I32R6}, +{"bnezc", "s,+q", 0xf8000000, 0xfc000000, CBD|RD_s, 0, I32R6}, {"beqzalc", "s,t,p", 0x20000000, 0xffe00000, CBD|RD_s|RD_t, 0, I32R6}, {"bovc", "s,t,p", 0x20000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, {"beqc", "s,t,p", 0x20000000, 0xfc000000, CBD|RD_s|RD_t, 0, I32R6}, @@ -4462,6 +4463,13 @@ print_insn_args (const char *d, (*info->print_address_func) (info->target, info); break; + case 'q': + /* Sign extend the displacement with 21 bits. */ + delta = sextract32(l, OP_SH_DELTA, 21); + info->target = (delta << 2) + pc + INSNLEN; + (*info->print_address_func) (info->target, info); + break; + case 't': /* Coprocessor 0 reg name */ (*info->fprintf_func) (info->stream, "%s", mips_cp0_names[(l >> OP_SH_RT) & diff --git a/disas/nanomips.cpp b/disas/nanomips.c similarity index 72% rename from disas/nanomips.cpp rename to disas/nanomips.c index 9be8df75dd..db0c297b8d 100644 --- a/disas/nanomips.cpp +++ b/disas/nanomips.c @@ -30,274 +30,67 @@ #include "qemu/osdep.h" #include "disas/dis-asm.h" -#include -#include -#include -#include -#include +typedef int64_t int64; +typedef uint64_t uint64; +typedef uint32_t uint32; +typedef uint16_t uint16; +typedef uint64_t img_address; -#include "nanomips.h" +typedef struct Dis_info { + img_address m_pc; + fprintf_function fprintf_func; + FILE *stream; + sigjmp_buf buf; +} Dis_info; #define IMGASSERTONCE(test) -int nanomips_dis(char *buf, - unsigned address, - unsigned short one, - unsigned short two, - unsigned short three) +static char * G_GNUC_PRINTF(1, 2) img_format(const char *format, ...) { - std::string disasm; - uint16 bits[3] = {one, two, three}; - - NMD::TABLE_ENTRY_TYPE type; - NMD d(address, NMD::ALL_ATTRIBUTES); - int size = d.Disassemble(bits, disasm, type); - - strcpy(buf, disasm.c_str()); - return size; -} - -int print_insn_nanomips(bfd_vma memaddr, struct disassemble_info *info) -{ - int status; - bfd_byte buffer[2]; - uint16_t insn1 = 0, insn2 = 0, insn3 = 0; - char buf[200]; - - info->bytes_per_chunk = 2; - info->display_endian = info->endian; - info->insn_info_valid = 1; - info->branch_delay_insns = 0; - info->data_size = 0; - info->insn_type = dis_nonbranch; - info->target = 0; - info->target2 = 0; - - status = (*info->read_memory_func)(memaddr, buffer, 2, info); - if (status != 0) { - (*info->memory_error_func)(status, memaddr, info); - return -1; - } - - if (info->endian == BFD_ENDIAN_BIG) { - insn1 = bfd_getb16(buffer); - } else { - insn1 = bfd_getl16(buffer); - } - (*info->fprintf_func)(info->stream, "%04x ", insn1); - - /* Handle 32-bit opcodes. */ - if ((insn1 & 0x1000) == 0) { - status = (*info->read_memory_func)(memaddr + 2, buffer, 2, info); - if (status != 0) { - (*info->memory_error_func)(status, memaddr + 2, info); - return -1; - } - - if (info->endian == BFD_ENDIAN_BIG) { - insn2 = bfd_getb16(buffer); - } else { - insn2 = bfd_getl16(buffer); - } - (*info->fprintf_func)(info->stream, "%04x ", insn2); - } else { - (*info->fprintf_func)(info->stream, " "); - } - /* Handle 48-bit opcodes. */ - if ((insn1 >> 10) == 0x18) { - status = (*info->read_memory_func)(memaddr + 4, buffer, 2, info); - if (status != 0) { - (*info->memory_error_func)(status, memaddr + 4, info); - return -1; - } - - if (info->endian == BFD_ENDIAN_BIG) { - insn3 = bfd_getb16(buffer); - } else { - insn3 = bfd_getl16(buffer); - } - (*info->fprintf_func)(info->stream, "%04x ", insn3); - } else { - (*info->fprintf_func)(info->stream, " "); - } - - int length = nanomips_dis(buf, memaddr, insn1, insn2, insn3); - - /* FIXME: Should probably use a hash table on the major opcode here. */ - - (*info->fprintf_func) (info->stream, "%s", buf); - if (length > 0) { - return length / 8; - } - - info->insn_type = dis_noninsn; - - return insn3 ? 6 : insn2 ? 4 : 2; -} - - -namespace img -{ - address addr32(address a) - { - return a; - } - - std::string format(const char *format, ...) - { - char buffer[256]; - va_list args; - va_start(args, format); - int err = vsprintf(buffer, format, args); - if (err < 0) { - perror(buffer); - } - va_end(args); - return buffer; - } - - std::string format(const char *format, - std::string s) - { - char buffer[256]; - - sprintf(buffer, format, s.c_str()); - - return buffer; - } - - std::string format(const char *format, - std::string s1, - std::string s2) - { - char buffer[256]; - - sprintf(buffer, format, s1.c_str(), s2.c_str()); - - return buffer; - } - - std::string format(const char *format, - std::string s1, - std::string s2, - std::string s3) - { - char buffer[256]; - - sprintf(buffer, format, s1.c_str(), s2.c_str(), s3.c_str()); - - return buffer; - } - - std::string format(const char *format, - std::string s1, - std::string s2, - std::string s3, - std::string s4) - { - char buffer[256]; - - sprintf(buffer, format, s1.c_str(), s2.c_str(), s3.c_str(), - s4.c_str()); - - return buffer; - } - - std::string format(const char *format, - std::string s1, - std::string s2, - std::string s3, - std::string s4, - std::string s5) - { - char buffer[256]; - - sprintf(buffer, format, s1.c_str(), s2.c_str(), s3.c_str(), - s4.c_str(), s5.c_str()); - - return buffer; - } - - std::string format(const char *format, - uint64 d, - std::string s2) - { - char buffer[256]; - - sprintf(buffer, format, d, s2.c_str()); - - return buffer; - } - - std::string format(const char *format, - std::string s1, - uint64 d, - std::string s2) - { - char buffer[256]; - - sprintf(buffer, format, s1.c_str(), d, s2.c_str()); - - return buffer; - } - - std::string format(const char *format, - std::string s1, - std::string s2, - uint64 d) - { - char buffer[256]; - - sprintf(buffer, format, s1.c_str(), s2.c_str(), d); - - return buffer; - } - - char as_char(int c) - { - return static_cast(c); - } -}; - - -std::string to_string(img::address a) -{ - char buffer[256]; - sprintf(buffer, "0x%" PRIx64, a); + char *buffer; + va_list args; + va_start(args, format); + buffer = g_strdup_vprintf(format, args); + va_end(args); return buffer; } -uint64 extract_bits(uint64 data, uint32 bit_offset, uint32 bit_size) +static char *to_string(img_address a) +{ + return g_strdup_printf("0x%" PRIx64, a); +} + + +static uint64 extract_bits(uint64 data, uint32 bit_offset, uint32 bit_size) { return (data << (64 - (bit_size + bit_offset))) >> (64 - bit_size); } -int64 sign_extend(int64 data, int msb) +static int64 sign_extend(int64 data, int msb) { uint64 shift = 63 - msb; return (data << shift) >> shift; } -uint64 NMD::renumber_registers(uint64 index, uint64 *register_list, - size_t register_list_size) +static uint64 renumber_registers(uint64 index, uint64 *register_list, + size_t register_list_size, Dis_info *info) { if (index < register_list_size) { return register_list[index]; } - throw std::runtime_error(img::format( - "Invalid register mapping index %" PRIu64 - ", size of list = %zu", - index, register_list_size)); + info->fprintf_func(info->stream, "Invalid register mapping index %" PRIu64 + ", size of list = %zu", index, register_list_size); + siglongjmp(info->buf, 1); } /* - * NMD::decode_gpr_gpr4() - decoder for 'gpr4' gpr encoding type + * decode_gpr_gpr4() - decoder for 'gpr4' gpr encoding type * * Map a 4-bit code to the 5-bit register space according to this pattern: * @@ -322,17 +115,17 @@ uint64 NMD::renumber_registers(uint64 index, uint64 *register_list, * - MUL[4X4] * - SW[4X4] */ -uint64 NMD::decode_gpr_gpr4(uint64 d) +static uint64 decode_gpr_gpr4(uint64 d, Dis_info *info) { static uint64 register_list[] = { 8, 9, 10, 11, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23 }; return renumber_registers(d, register_list, - sizeof(register_list) / sizeof(register_list[0])); + sizeof(register_list) / sizeof(register_list[0]), info); } /* - * NMD::decode_gpr_gpr4_zero() - decoder for 'gpr4.zero' gpr encoding type + * decode_gpr_gpr4_zero() - decoder for 'gpr4.zero' gpr encoding type * * Map a 4-bit code to the 5-bit register space according to this pattern: * @@ -358,17 +151,17 @@ uint64 NMD::decode_gpr_gpr4(uint64 d) * - MOVEP * - SW[4X4] */ -uint64 NMD::decode_gpr_gpr4_zero(uint64 d) +static uint64 decode_gpr_gpr4_zero(uint64 d, Dis_info *info) { static uint64 register_list[] = { 8, 9, 10, 0, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23 }; return renumber_registers(d, register_list, - sizeof(register_list) / sizeof(register_list[0])); + sizeof(register_list) / sizeof(register_list[0]), info); } /* - * NMD::decode_gpr_gpr3() - decoder for 'gpr3' gpr encoding type + * decode_gpr_gpr3() - decoder for 'gpr3' gpr encoding type * * Map a 3-bit code to the 5-bit register space according to this pattern: * @@ -417,16 +210,16 @@ uint64 NMD::decode_gpr_gpr4_zero(uint64 d) * - SW[16] * - XOR[16] */ -uint64 NMD::decode_gpr_gpr3(uint64 d) +static uint64 decode_gpr_gpr3(uint64 d, Dis_info *info) { static uint64 register_list[] = { 16, 17, 18, 19, 4, 5, 6, 7 }; return renumber_registers(d, register_list, - sizeof(register_list) / sizeof(register_list[0])); + sizeof(register_list) / sizeof(register_list[0]), info); } /* - * NMD::decode_gpr_gpr3_src_store() - decoder for 'gpr3.src.store' gpr encoding + * decode_gpr_gpr3_src_store() - decoder for 'gpr3.src.store' gpr encoding * type * * Map a 3-bit code to the 5-bit register space according to this pattern: @@ -457,16 +250,16 @@ uint64 NMD::decode_gpr_gpr3(uint64 d) * - SW[16] * - SW[GP16] */ -uint64 NMD::decode_gpr_gpr3_src_store(uint64 d) +static uint64 decode_gpr_gpr3_src_store(uint64 d, Dis_info *info) { static uint64 register_list[] = { 0, 17, 18, 19, 4, 5, 6, 7 }; return renumber_registers(d, register_list, - sizeof(register_list) / sizeof(register_list[0])); + sizeof(register_list) / sizeof(register_list[0]), info); } /* - * NMD::decode_gpr_gpr2_reg1() - decoder for 'gpr2.reg1' gpr encoding type + * decode_gpr_gpr2_reg1() - decoder for 'gpr2.reg1' gpr encoding type * * Map a 2-bit code to the 5-bit register space according to this pattern: * @@ -487,16 +280,16 @@ uint64 NMD::decode_gpr_gpr3_src_store(uint64 d) * - MOVEP * - MOVEP[REV] */ -uint64 NMD::decode_gpr_gpr2_reg1(uint64 d) +static uint64 decode_gpr_gpr2_reg1(uint64 d, Dis_info *info) { static uint64 register_list[] = { 4, 5, 6, 7 }; return renumber_registers(d, register_list, - sizeof(register_list) / sizeof(register_list[0])); + sizeof(register_list) / sizeof(register_list[0]), info); } /* - * NMD::decode_gpr_gpr2_reg2() - decoder for 'gpr2.reg2' gpr encoding type + * decode_gpr_gpr2_reg2() - decoder for 'gpr2.reg2' gpr encoding type * * Map a 2-bit code to the 5-bit register space according to this pattern: * @@ -517,16 +310,16 @@ uint64 NMD::decode_gpr_gpr2_reg1(uint64 d) * - MOVEP * - MOVEP[REV] */ -uint64 NMD::decode_gpr_gpr2_reg2(uint64 d) +static uint64 decode_gpr_gpr2_reg2(uint64 d, Dis_info *info) { static uint64 register_list[] = { 5, 6, 7, 8 }; return renumber_registers(d, register_list, - sizeof(register_list) / sizeof(register_list[0])); + sizeof(register_list) / sizeof(register_list[0]), info); } /* - * NMD::decode_gpr_gpr1() - decoder for 'gpr1' gpr encoding type + * decode_gpr_gpr1() - decoder for 'gpr1' gpr encoding type * * Map a 1-bit code to the 5-bit register space according to this pattern: * @@ -546,81 +339,28 @@ uint64 NMD::decode_gpr_gpr2_reg2(uint64 d) * * - MOVE.BALC */ -uint64 NMD::decode_gpr_gpr1(uint64 d) +static uint64 decode_gpr_gpr1(uint64 d, Dis_info *info) { static uint64 register_list[] = { 4, 5 }; return renumber_registers(d, register_list, - sizeof(register_list) / sizeof(register_list[0])); + sizeof(register_list) / sizeof(register_list[0]), info); } -uint64 NMD::copy(uint64 d) -{ - return d; -} - - -int64 NMD::copy(int64 d) -{ - return d; -} - - -int64 NMD::neg_copy(uint64 d) +static int64 neg_copy(uint64 d) { return 0ll - d; } -int64 NMD::neg_copy(int64 d) -{ - return -d; -} - - -/* strange wrapper around gpr3 */ -uint64 NMD::encode_rs3_and_check_rs3_ge_rt3(uint64 d) -{ -return decode_gpr_gpr3(d); -} - - -/* strange wrapper around gpr3 */ -uint64 NMD::encode_rs3_and_check_rs3_lt_rt3(uint64 d) -{ - return decode_gpr_gpr3(d); -} - - -/* nop - done by extraction function */ -uint64 NMD::encode_s_from_address(uint64 d) -{ - return d; -} - - -/* nop - done by extraction function */ -uint64 NMD::encode_u_from_address(uint64 d) -{ - return d; -} - - -/* nop - done by extraction function */ -uint64 NMD::encode_s_from_s_hi(uint64 d) -{ - return d; -} - - -uint64 NMD::encode_count3_from_count(uint64 d) +static uint64 encode_count3_from_count(uint64 d) { IMGASSERTONCE(d < 8); return d == 0ull ? 8ull : d; } -uint64 NMD::encode_shift3_from_shift(uint64 d) +static uint64 encode_shift3_from_shift(uint64 d) { IMGASSERTONCE(d < 8); return d == 0ull ? 8ull : d; @@ -628,21 +368,21 @@ uint64 NMD::encode_shift3_from_shift(uint64 d) /* special value for load literal */ -int64 NMD::encode_eu_from_s_li16(uint64 d) +static int64 encode_eu_from_s_li16(uint64 d) { IMGASSERTONCE(d < 128); return d == 127 ? -1 : (int64)d; } -uint64 NMD::encode_msbd_from_size(uint64 d) +static uint64 encode_msbd_from_size(uint64 d) { IMGASSERTONCE(d < 32); return d + 1; } -uint64 NMD::encode_eu_from_u_andi16(uint64 d) +static uint64 encode_eu_from_u_andi16(uint64 d) { IMGASSERTONCE(d < 16); if (d == 12) { @@ -655,42 +395,14 @@ uint64 NMD::encode_eu_from_u_andi16(uint64 d) } -uint64 NMD::encode_msbd_from_pos_and_size(uint64 d) -{ - IMGASSERTONCE(0); - return d; -} - - /* save16 / restore16 ???? */ -uint64 NMD::encode_rt1_from_rt(uint64 d) +static uint64 encode_rt1_from_rt(uint64 d) { return d ? 31 : 30; } -/* ? */ -uint64 NMD::encode_lsb_from_pos_and_size(uint64 d) -{ - return d; -} - - -std::string NMD::save_restore_list(uint64 rt, uint64 count, uint64 gp) -{ - std::string str; - - for (uint64 counter = 0; counter != count; counter++) { - bool use_gp = gp && (counter == count - 1); - uint64 this_rt = use_gp ? 28 : ((rt & 0x10) | (rt + counter)) & 0x1f; - str += img::format(",%s", GPR(this_rt)); - } - - return str; -} - - -std::string NMD::GPR(uint64 reg) +static const char *GPR(uint64 reg, Dis_info *info) { static const char *gpr_reg[32] = { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", @@ -703,12 +415,32 @@ std::string NMD::GPR(uint64 reg) return gpr_reg[reg]; } - throw std::runtime_error(img::format("Invalid GPR register index %" PRIu64, - reg)); + info->fprintf_func(info->stream, "Invalid GPR register index %" PRIu64, + reg); + siglongjmp(info->buf, 1); } -std::string NMD::FPR(uint64 reg) +static char *save_restore_list(uint64 rt, uint64 count, uint64 gp, + Dis_info *info) +{ + char *reg_list[34]; + reg_list[0] = (char *)""; + + assert(count <= 32); + for (uint64 counter = 0; counter != count; counter++) { + bool use_gp = gp && (counter == count - 1); + uint64 this_rt = use_gp ? 28 : ((rt & 0x10) | (rt + counter)) & 0x1f; + /* glib usage below requires casting away const */ + reg_list[counter + 1] = (char *)GPR(this_rt, info); + } + reg_list[count + 1] = NULL; + + return g_strjoinv(",", reg_list); +} + + +static const char *FPR(uint64 reg, Dis_info *info) { static const char *fpr_reg[32] = { "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", @@ -721,12 +453,13 @@ std::string NMD::FPR(uint64 reg) return fpr_reg[reg]; } - throw std::runtime_error(img::format("Invalid FPR register index %" PRIu64, - reg)); + info->fprintf_func(info->stream, "Invalid FPR register index %" PRIu64, + reg); + siglongjmp(info->buf, 1); } -std::string NMD::AC(uint64 reg) +static const char *AC(uint64 reg, Dis_info *info) { static const char *ac_reg[4] = { "ac0", "ac1", "ac2", "ac3" @@ -736,42 +469,22 @@ std::string NMD::AC(uint64 reg) return ac_reg[reg]; } - throw std::runtime_error(img::format("Invalid AC register index %" PRIu64, - reg)); + info->fprintf_func(info->stream, "Invalid AC register index %" PRIu64, + reg); + siglongjmp(info->buf, 1); } -std::string NMD::IMMEDIATE(uint64 value) -{ - return img::format("0x%" PRIx64, value); -} - - -std::string NMD::IMMEDIATE(int64 value) -{ - return img::format("%" PRId64, value); -} - - -std::string NMD::CPR(uint64 reg) -{ - /* needs more work */ - return img::format("CP%" PRIu64, reg); -} - - -std::string NMD::ADDRESS(uint64 value, int instruction_size) +static char *ADDRESS(uint64 value, int instruction_size, Dis_info *info) { /* token for string replace */ - /* const char TOKEN_REPLACE = (char)0xa2; */ - img::address address = m_pc + value + instruction_size; + img_address address = info->m_pc + value + instruction_size; /* symbol replacement */ - /* return img::as_char(TOKEN_REPLACE) + to_string(address); */ return to_string(address); } -uint64 NMD::extract_op_code_value(const uint16 * data, int size) +static uint64 extract_op_code_value(const uint16 *data, int size) { switch (size) { case 16: @@ -786,92 +499,7 @@ uint64 NMD::extract_op_code_value(const uint16 * data, int size) } -int NMD::Disassemble(const uint16 * data, std::string & dis, - NMD::TABLE_ENTRY_TYPE & type) -{ - return Disassemble(data, dis, type, MAJOR, 2); -} - - -/* - * Recurse through tables until the instruction is found then return - * the string and size - * - * inputs: - * pointer to a word stream, - * disassember table and size - * returns: - * instruction size - negative is error - * disassembly string - on error will constain error string - */ -int NMD::Disassemble(const uint16 * data, std::string & dis, - NMD::TABLE_ENTRY_TYPE & type, const Pool *table, - int table_size) -{ - try - { - for (int i = 0; i < table_size; i++) { - uint64 op_code = extract_op_code_value(data, - table[i].instructions_size); - if ((op_code & table[i].mask) == table[i].value) { - /* possible match */ - conditional_function cond = table[i].condition; - if ((cond == 0) || (this->*cond)(op_code)) { - try - { - if (table[i].type == pool) { - return Disassemble(data, dis, type, - table[i].next_table, - table[i].next_table_size); - } else if ((table[i].type == instruction) || - (table[i].type == call_instruction) || - (table[i].type == branch_instruction) || - (table[i].type == return_instruction)) { - if ((table[i].attributes != 0) && - (m_requested_instruction_categories & - table[i].attributes) == 0) { - /* - * failed due to instruction having - * an ASE attribute and the requested version - * not having that attribute - */ - dis = "ASE attribute mismatch"; - return -5; - } - disassembly_function dis_fn = table[i].disassembly; - if (dis_fn == 0) { - dis = "disassembler failure - bad table entry"; - return -6; - } - type = table[i].type; - dis = (this->*dis_fn)(op_code); - return table[i].instructions_size; - } else { - dis = "reserved instruction"; - return -2; - } - } - catch (std::runtime_error & e) - { - dis = e.what(); - return -3; /* runtime error */ - } - } - } - } - } - catch (std::exception & e) - { - dis = e.what(); - return -4; /* runtime error */ - } - - dis = "failed to disassemble"; - return -1; /* failed to disassemble */ -} - - -uint64 NMD::extract_code_18_to_0(uint64 instruction) +static uint64 extract_code_18_to_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 19); @@ -879,7 +507,7 @@ uint64 NMD::extract_code_18_to_0(uint64 instruction) } -uint64 NMD::extract_shift3_2_1_0(uint64 instruction) +static uint64 extract_shift3_2_1_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 3); @@ -887,7 +515,7 @@ uint64 NMD::extract_shift3_2_1_0(uint64 instruction) } -uint64 NMD::extract_u_11_10_9_8_7_6_5_4_3__s3(uint64 instruction) +static uint64 extract_u_11_10_9_8_7_6_5_4_3__s3(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 3, 9) << 3; @@ -895,7 +523,7 @@ uint64 NMD::extract_u_11_10_9_8_7_6_5_4_3__s3(uint64 instruction) } -uint64 NMD::extract_count_3_2_1_0(uint64 instruction) +static uint64 extract_count_3_2_1_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 4); @@ -903,7 +531,7 @@ uint64 NMD::extract_count_3_2_1_0(uint64 instruction) } -uint64 NMD::extract_rtz3_9_8_7(uint64 instruction) +static uint64 extract_rtz3_9_8_7(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 7, 3); @@ -911,7 +539,7 @@ uint64 NMD::extract_rtz3_9_8_7(uint64 instruction) } -uint64 NMD::extract_u_17_to_1__s1(uint64 instruction) +static uint64 extract_u_17_to_1__s1(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 1, 17) << 1; @@ -919,7 +547,7 @@ uint64 NMD::extract_u_17_to_1__s1(uint64 instruction) } -int64 NMD::extract_s__se9_20_19_18_17_16_15_14_13_12_11(uint64 instruction) +static int64 extract_s__se9_20_19_18_17_16_15_14_13_12_11(uint64 instruction) { int64 value = 0; value |= extract_bits(instruction, 11, 10); @@ -928,7 +556,7 @@ int64 NMD::extract_s__se9_20_19_18_17_16_15_14_13_12_11(uint64 instruction) } -int64 NMD::extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(uint64 instruction) +static int64 extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(uint64 instruction) { int64 value = 0; value |= extract_bits(instruction, 0, 1) << 11; @@ -938,7 +566,7 @@ int64 NMD::extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(uint64 instruction) } -uint64 NMD::extract_u_10(uint64 instruction) +static uint64 extract_u_10(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 10, 1); @@ -946,7 +574,7 @@ uint64 NMD::extract_u_10(uint64 instruction) } -uint64 NMD::extract_rtz4_27_26_25_23_22_21(uint64 instruction) +static uint64 extract_rtz4_27_26_25_23_22_21(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 21, 3); @@ -955,7 +583,7 @@ uint64 NMD::extract_rtz4_27_26_25_23_22_21(uint64 instruction) } -uint64 NMD::extract_sa_15_14_13_12_11(uint64 instruction) +static uint64 extract_sa_15_14_13_12_11(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 11, 5); @@ -963,7 +591,7 @@ uint64 NMD::extract_sa_15_14_13_12_11(uint64 instruction) } -uint64 NMD::extract_shift_4_3_2_1_0(uint64 instruction) +static uint64 extract_shift_4_3_2_1_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 5); @@ -971,7 +599,7 @@ uint64 NMD::extract_shift_4_3_2_1_0(uint64 instruction) } -uint64 NMD::extract_shiftx_10_9_8_7__s1(uint64 instruction) +static uint64 extract_shiftx_10_9_8_7__s1(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 7, 4) << 1; @@ -979,7 +607,7 @@ uint64 NMD::extract_shiftx_10_9_8_7__s1(uint64 instruction) } -uint64 NMD::extract_hint_25_24_23_22_21(uint64 instruction) +static uint64 extract_hint_25_24_23_22_21(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 21, 5); @@ -987,7 +615,7 @@ uint64 NMD::extract_hint_25_24_23_22_21(uint64 instruction) } -uint64 NMD::extract_count3_14_13_12(uint64 instruction) +static uint64 extract_count3_14_13_12(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 12, 3); @@ -995,7 +623,7 @@ uint64 NMD::extract_count3_14_13_12(uint64 instruction) } -int64 NMD::extract_s__se31_0_11_to_2_20_to_12_s12(uint64 instruction) +static int64 extract_s__se31_0_11_to_2_20_to_12_s12(uint64 instruction) { int64 value = 0; value |= extract_bits(instruction, 0, 1) << 31; @@ -1006,7 +634,7 @@ int64 NMD::extract_s__se31_0_11_to_2_20_to_12_s12(uint64 instruction) } -int64 NMD::extract_s__se7_0_6_5_4_3_2_1_s1(uint64 instruction) +static int64 extract_s__se7_0_6_5_4_3_2_1_s1(uint64 instruction) { int64 value = 0; value |= extract_bits(instruction, 0, 1) << 7; @@ -1016,7 +644,7 @@ int64 NMD::extract_s__se7_0_6_5_4_3_2_1_s1(uint64 instruction) } -uint64 NMD::extract_u2_10_9(uint64 instruction) +static uint64 extract_u2_10_9(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 9, 2); @@ -1024,7 +652,7 @@ uint64 NMD::extract_u2_10_9(uint64 instruction) } -uint64 NMD::extract_code_25_24_23_22_21_20_19_18_17_16(uint64 instruction) +static uint64 extract_code_25_24_23_22_21_20_19_18_17_16(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 16, 10); @@ -1032,7 +660,7 @@ uint64 NMD::extract_code_25_24_23_22_21_20_19_18_17_16(uint64 instruction) } -uint64 NMD::extract_rs_20_19_18_17_16(uint64 instruction) +static uint64 extract_rs_20_19_18_17_16(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 16, 5); @@ -1040,7 +668,7 @@ uint64 NMD::extract_rs_20_19_18_17_16(uint64 instruction) } -uint64 NMD::extract_u_2_1__s1(uint64 instruction) +static uint64 extract_u_2_1__s1(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 1, 2) << 1; @@ -1048,7 +676,7 @@ uint64 NMD::extract_u_2_1__s1(uint64 instruction) } -uint64 NMD::extract_stripe_6(uint64 instruction) +static uint64 extract_stripe_6(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 6, 1); @@ -1056,7 +684,7 @@ uint64 NMD::extract_stripe_6(uint64 instruction) } -uint64 NMD::extract_ac_15_14(uint64 instruction) +static uint64 extract_ac_15_14(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 14, 2); @@ -1064,7 +692,7 @@ uint64 NMD::extract_ac_15_14(uint64 instruction) } -uint64 NMD::extract_shift_20_19_18_17_16(uint64 instruction) +static uint64 extract_shift_20_19_18_17_16(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 16, 5); @@ -1072,7 +700,7 @@ uint64 NMD::extract_shift_20_19_18_17_16(uint64 instruction) } -uint64 NMD::extract_rdl_25_24(uint64 instruction) +static uint64 extract_rdl_25_24(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 24, 1); @@ -1080,7 +708,7 @@ uint64 NMD::extract_rdl_25_24(uint64 instruction) } -int64 NMD::extract_s__se10_0_9_8_7_6_5_4_3_2_1_s1(uint64 instruction) +static int64 extract_s__se10_0_9_8_7_6_5_4_3_2_1_s1(uint64 instruction) { int64 value = 0; value |= extract_bits(instruction, 0, 1) << 10; @@ -1090,7 +718,7 @@ int64 NMD::extract_s__se10_0_9_8_7_6_5_4_3_2_1_s1(uint64 instruction) } -uint64 NMD::extract_eu_6_5_4_3_2_1_0(uint64 instruction) +static uint64 extract_eu_6_5_4_3_2_1_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 7); @@ -1098,7 +726,7 @@ uint64 NMD::extract_eu_6_5_4_3_2_1_0(uint64 instruction) } -uint64 NMD::extract_shift_5_4_3_2_1_0(uint64 instruction) +static uint64 extract_shift_5_4_3_2_1_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 6); @@ -1106,7 +734,7 @@ uint64 NMD::extract_shift_5_4_3_2_1_0(uint64 instruction) } -uint64 NMD::extract_count_19_18_17_16(uint64 instruction) +static uint64 extract_count_19_18_17_16(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 16, 4); @@ -1114,7 +742,7 @@ uint64 NMD::extract_count_19_18_17_16(uint64 instruction) } -uint64 NMD::extract_code_2_1_0(uint64 instruction) +static uint64 extract_code_2_1_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 3); @@ -1122,7 +750,7 @@ uint64 NMD::extract_code_2_1_0(uint64 instruction) } -uint64 NMD::extract_u_11_10_9_8_7_6_5_4_3_2_1_0(uint64 instruction) +static uint64 extract_u_11_10_9_8_7_6_5_4_3_2_1_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 12); @@ -1130,7 +758,7 @@ uint64 NMD::extract_u_11_10_9_8_7_6_5_4_3_2_1_0(uint64 instruction) } -uint64 NMD::extract_rs_4_3_2_1_0(uint64 instruction) +static uint64 extract_rs_4_3_2_1_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 5); @@ -1138,7 +766,7 @@ uint64 NMD::extract_rs_4_3_2_1_0(uint64 instruction) } -uint64 NMD::extract_u_20_to_3__s3(uint64 instruction) +static uint64 extract_u_20_to_3__s3(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 3, 18) << 3; @@ -1146,7 +774,7 @@ uint64 NMD::extract_u_20_to_3__s3(uint64 instruction) } -uint64 NMD::extract_u_3_2_1_0__s2(uint64 instruction) +static uint64 extract_u_3_2_1_0__s2(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 4) << 2; @@ -1154,7 +782,7 @@ uint64 NMD::extract_u_3_2_1_0__s2(uint64 instruction) } -uint64 NMD::extract_cofun_25_24_23(uint64 instruction) +static uint64 extract_cofun_25_24_23(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 3, 23); @@ -1162,7 +790,7 @@ uint64 NMD::extract_cofun_25_24_23(uint64 instruction) } -uint64 NMD::extract_u_2_1_0__s2(uint64 instruction) +static uint64 extract_u_2_1_0__s2(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 3) << 2; @@ -1170,7 +798,7 @@ uint64 NMD::extract_u_2_1_0__s2(uint64 instruction) } -uint64 NMD::extract_rd3_3_2_1(uint64 instruction) +static uint64 extract_rd3_3_2_1(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 1, 3); @@ -1178,7 +806,7 @@ uint64 NMD::extract_rd3_3_2_1(uint64 instruction) } -uint64 NMD::extract_sa_15_14_13_12(uint64 instruction) +static uint64 extract_sa_15_14_13_12(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 12, 4); @@ -1186,7 +814,7 @@ uint64 NMD::extract_sa_15_14_13_12(uint64 instruction) } -uint64 NMD::extract_rt_25_24_23_22_21(uint64 instruction) +static uint64 extract_rt_25_24_23_22_21(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 21, 5); @@ -1194,7 +822,7 @@ uint64 NMD::extract_rt_25_24_23_22_21(uint64 instruction) } -uint64 NMD::extract_ru_7_6_5_4_3(uint64 instruction) +static uint64 extract_ru_7_6_5_4_3(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 3, 5); @@ -1202,7 +830,7 @@ uint64 NMD::extract_ru_7_6_5_4_3(uint64 instruction) } -uint64 NMD::extract_u_17_to_0(uint64 instruction) +static uint64 extract_u_17_to_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 18); @@ -1210,7 +838,7 @@ uint64 NMD::extract_u_17_to_0(uint64 instruction) } -uint64 NMD::extract_rsz4_4_2_1_0(uint64 instruction) +static uint64 extract_rsz4_4_2_1_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 3); @@ -1219,7 +847,7 @@ uint64 NMD::extract_rsz4_4_2_1_0(uint64 instruction) } -int64 NMD::extract_s__se21_0_20_to_1_s1(uint64 instruction) +static int64 extract_s__se21_0_20_to_1_s1(uint64 instruction) { int64 value = 0; value |= extract_bits(instruction, 0, 1) << 21; @@ -1229,7 +857,7 @@ int64 NMD::extract_s__se21_0_20_to_1_s1(uint64 instruction) } -uint64 NMD::extract_op_25_to_3(uint64 instruction) +static uint64 extract_op_25_to_3(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 3, 23); @@ -1237,7 +865,7 @@ uint64 NMD::extract_op_25_to_3(uint64 instruction) } -uint64 NMD::extract_rs4_4_2_1_0(uint64 instruction) +static uint64 extract_rs4_4_2_1_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 3); @@ -1246,7 +874,7 @@ uint64 NMD::extract_rs4_4_2_1_0(uint64 instruction) } -uint64 NMD::extract_bit_23_22_21(uint64 instruction) +static uint64 extract_bit_23_22_21(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 21, 3); @@ -1254,7 +882,7 @@ uint64 NMD::extract_bit_23_22_21(uint64 instruction) } -uint64 NMD::extract_rt_41_40_39_38_37(uint64 instruction) +static uint64 extract_rt_41_40_39_38_37(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 37, 5); @@ -1262,7 +890,7 @@ uint64 NMD::extract_rt_41_40_39_38_37(uint64 instruction) } -int64 NMD::extract_shift__se5_21_20_19_18_17_16(uint64 instruction) +static int64 extract_shift__se5_21_20_19_18_17_16(uint64 instruction) { int64 value = 0; value |= extract_bits(instruction, 16, 6); @@ -1271,7 +899,7 @@ int64 NMD::extract_shift__se5_21_20_19_18_17_16(uint64 instruction) } -uint64 NMD::extract_rd2_3_8(uint64 instruction) +static uint64 extract_rd2_3_8(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 3, 1) << 1; @@ -1280,7 +908,7 @@ uint64 NMD::extract_rd2_3_8(uint64 instruction) } -uint64 NMD::extract_code_17_to_0(uint64 instruction) +static uint64 extract_code_17_to_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 18); @@ -1288,7 +916,7 @@ uint64 NMD::extract_code_17_to_0(uint64 instruction) } -uint64 NMD::extract_size_20_19_18_17_16(uint64 instruction) +static uint64 extract_size_20_19_18_17_16(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 16, 5); @@ -1296,7 +924,7 @@ uint64 NMD::extract_size_20_19_18_17_16(uint64 instruction) } -int64 NMD::extract_s__se8_15_7_6_5_4_3_2_s2(uint64 instruction) +static int64 extract_s__se8_15_7_6_5_4_3_2_s2(uint64 instruction) { int64 value = 0; value |= extract_bits(instruction, 2, 6) << 2; @@ -1306,7 +934,7 @@ int64 NMD::extract_s__se8_15_7_6_5_4_3_2_s2(uint64 instruction) } -uint64 NMD::extract_u_15_to_0(uint64 instruction) +static uint64 extract_u_15_to_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 16); @@ -1314,7 +942,7 @@ uint64 NMD::extract_u_15_to_0(uint64 instruction) } -uint64 NMD::extract_fs_20_19_18_17_16(uint64 instruction) +static uint64 extract_fs_20_19_18_17_16(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 16, 5); @@ -1322,7 +950,7 @@ uint64 NMD::extract_fs_20_19_18_17_16(uint64 instruction) } -int64 NMD::extract_s__se8_15_7_6_5_4_3_2_1_0(uint64 instruction) +static int64 extract_s__se8_15_7_6_5_4_3_2_1_0(uint64 instruction) { int64 value = 0; value |= extract_bits(instruction, 0, 8); @@ -1332,7 +960,7 @@ int64 NMD::extract_s__se8_15_7_6_5_4_3_2_1_0(uint64 instruction) } -uint64 NMD::extract_stype_20_19_18_17_16(uint64 instruction) +static uint64 extract_stype_20_19_18_17_16(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 16, 5); @@ -1340,7 +968,7 @@ uint64 NMD::extract_stype_20_19_18_17_16(uint64 instruction) } -uint64 NMD::extract_rtl_11(uint64 instruction) +static uint64 extract_rtl_11(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 9, 1); @@ -1348,7 +976,7 @@ uint64 NMD::extract_rtl_11(uint64 instruction) } -uint64 NMD::extract_hs_20_19_18_17_16(uint64 instruction) +static uint64 extract_hs_20_19_18_17_16(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 16, 5); @@ -1356,7 +984,7 @@ uint64 NMD::extract_hs_20_19_18_17_16(uint64 instruction) } -uint64 NMD::extract_sel_13_12_11(uint64 instruction) +static uint64 extract_sel_13_12_11(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 11, 3); @@ -1364,7 +992,7 @@ uint64 NMD::extract_sel_13_12_11(uint64 instruction) } -uint64 NMD::extract_lsb_4_3_2_1_0(uint64 instruction) +static uint64 extract_lsb_4_3_2_1_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 5); @@ -1372,7 +1000,7 @@ uint64 NMD::extract_lsb_4_3_2_1_0(uint64 instruction) } -uint64 NMD::extract_gp_2(uint64 instruction) +static uint64 extract_gp_2(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 2, 1); @@ -1380,7 +1008,7 @@ uint64 NMD::extract_gp_2(uint64 instruction) } -uint64 NMD::extract_rt3_9_8_7(uint64 instruction) +static uint64 extract_rt3_9_8_7(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 7, 3); @@ -1388,7 +1016,7 @@ uint64 NMD::extract_rt3_9_8_7(uint64 instruction) } -uint64 NMD::extract_ft_25_24_23_22_21(uint64 instruction) +static uint64 extract_ft_25_24_23_22_21(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 21, 5); @@ -1396,7 +1024,7 @@ uint64 NMD::extract_ft_25_24_23_22_21(uint64 instruction) } -uint64 NMD::extract_u_17_16_15_14_13_12_11(uint64 instruction) +static uint64 extract_u_17_16_15_14_13_12_11(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 11, 7); @@ -1404,7 +1032,7 @@ uint64 NMD::extract_u_17_16_15_14_13_12_11(uint64 instruction) } -uint64 NMD::extract_cs_20_19_18_17_16(uint64 instruction) +static uint64 extract_cs_20_19_18_17_16(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 16, 5); @@ -1412,7 +1040,7 @@ uint64 NMD::extract_cs_20_19_18_17_16(uint64 instruction) } -uint64 NMD::extract_rt4_9_7_6_5(uint64 instruction) +static uint64 extract_rt4_9_7_6_5(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 5, 3); @@ -1421,7 +1049,7 @@ uint64 NMD::extract_rt4_9_7_6_5(uint64 instruction) } -uint64 NMD::extract_msbt_10_9_8_7_6(uint64 instruction) +static uint64 extract_msbt_10_9_8_7_6(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 6, 5); @@ -1429,7 +1057,7 @@ uint64 NMD::extract_msbt_10_9_8_7_6(uint64 instruction) } -uint64 NMD::extract_u_5_4_3_2_1_0__s2(uint64 instruction) +static uint64 extract_u_5_4_3_2_1_0__s2(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 6) << 2; @@ -1437,7 +1065,7 @@ uint64 NMD::extract_u_5_4_3_2_1_0__s2(uint64 instruction) } -uint64 NMD::extract_sa_15_14_13(uint64 instruction) +static uint64 extract_sa_15_14_13(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 13, 3); @@ -1445,7 +1073,7 @@ uint64 NMD::extract_sa_15_14_13(uint64 instruction) } -int64 NMD::extract_s__se14_0_13_to_1_s1(uint64 instruction) +static int64 extract_s__se14_0_13_to_1_s1(uint64 instruction) { int64 value = 0; value |= extract_bits(instruction, 0, 1) << 14; @@ -1455,7 +1083,7 @@ int64 NMD::extract_s__se14_0_13_to_1_s1(uint64 instruction) } -uint64 NMD::extract_rs3_6_5_4(uint64 instruction) +static uint64 extract_rs3_6_5_4(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 4, 3); @@ -1463,7 +1091,7 @@ uint64 NMD::extract_rs3_6_5_4(uint64 instruction) } -uint64 NMD::extract_u_31_to_0__s32(uint64 instruction) +static uint64 extract_u_31_to_0__s32(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 32) << 32; @@ -1471,7 +1099,7 @@ uint64 NMD::extract_u_31_to_0__s32(uint64 instruction) } -uint64 NMD::extract_shift_10_9_8_7_6(uint64 instruction) +static uint64 extract_shift_10_9_8_7_6(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 6, 5); @@ -1479,7 +1107,7 @@ uint64 NMD::extract_shift_10_9_8_7_6(uint64 instruction) } -uint64 NMD::extract_cs_25_24_23_22_21(uint64 instruction) +static uint64 extract_cs_25_24_23_22_21(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 21, 5); @@ -1487,7 +1115,7 @@ uint64 NMD::extract_cs_25_24_23_22_21(uint64 instruction) } -uint64 NMD::extract_shiftx_11_10_9_8_7_6(uint64 instruction) +static uint64 extract_shiftx_11_10_9_8_7_6(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 6, 6); @@ -1495,7 +1123,7 @@ uint64 NMD::extract_shiftx_11_10_9_8_7_6(uint64 instruction) } -uint64 NMD::extract_rt_9_8_7_6_5(uint64 instruction) +static uint64 extract_rt_9_8_7_6_5(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 5, 5); @@ -1503,7 +1131,7 @@ uint64 NMD::extract_rt_9_8_7_6_5(uint64 instruction) } -uint64 NMD::extract_op_25_24_23_22_21(uint64 instruction) +static uint64 extract_op_25_24_23_22_21(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 21, 5); @@ -1511,7 +1139,7 @@ uint64 NMD::extract_op_25_24_23_22_21(uint64 instruction) } -uint64 NMD::extract_u_6_5_4_3_2_1_0__s2(uint64 instruction) +static uint64 extract_u_6_5_4_3_2_1_0__s2(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 7) << 2; @@ -1519,7 +1147,7 @@ uint64 NMD::extract_u_6_5_4_3_2_1_0__s2(uint64 instruction) } -uint64 NMD::extract_bit_16_15_14_13_12_11(uint64 instruction) +static uint64 extract_bit_16_15_14_13_12_11(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 11, 6); @@ -1527,7 +1155,7 @@ uint64 NMD::extract_bit_16_15_14_13_12_11(uint64 instruction) } -uint64 NMD::extract_mask_20_19_18_17_16_15_14(uint64 instruction) +static uint64 extract_mask_20_19_18_17_16_15_14(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 14, 7); @@ -1535,7 +1163,7 @@ uint64 NMD::extract_mask_20_19_18_17_16_15_14(uint64 instruction) } -uint64 NMD::extract_eu_3_2_1_0(uint64 instruction) +static uint64 extract_eu_3_2_1_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 4); @@ -1543,7 +1171,7 @@ uint64 NMD::extract_eu_3_2_1_0(uint64 instruction) } -uint64 NMD::extract_u_7_6_5_4__s4(uint64 instruction) +static uint64 extract_u_7_6_5_4__s4(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 4, 4) << 4; @@ -1551,7 +1179,7 @@ uint64 NMD::extract_u_7_6_5_4__s4(uint64 instruction) } -int64 NMD::extract_s__se8_15_7_6_5_4_3_s3(uint64 instruction) +static int64 extract_s__se8_15_7_6_5_4_3_s3(uint64 instruction) { int64 value = 0; value |= extract_bits(instruction, 3, 5) << 3; @@ -1561,7 +1189,7 @@ int64 NMD::extract_s__se8_15_7_6_5_4_3_s3(uint64 instruction) } -uint64 NMD::extract_ft_15_14_13_12_11(uint64 instruction) +static uint64 extract_ft_15_14_13_12_11(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 11, 5); @@ -1569,7 +1197,7 @@ uint64 NMD::extract_ft_15_14_13_12_11(uint64 instruction) } -int64 NMD::extract_s__se31_15_to_0_31_to_16(uint64 instruction) +static int64 extract_s__se31_15_to_0_31_to_16(uint64 instruction) { int64 value = 0; value |= extract_bits(instruction, 0, 16) << 16; @@ -1579,7 +1207,7 @@ int64 NMD::extract_s__se31_15_to_0_31_to_16(uint64 instruction) } -uint64 NMD::extract_u_20_19_18_17_16_15_14_13(uint64 instruction) +static uint64 extract_u_20_19_18_17_16_15_14_13(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 13, 8); @@ -1587,7 +1215,7 @@ uint64 NMD::extract_u_20_19_18_17_16_15_14_13(uint64 instruction) } -uint64 NMD::extract_u_17_to_2__s2(uint64 instruction) +static uint64 extract_u_17_to_2__s2(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 2, 16) << 2; @@ -1595,7 +1223,7 @@ uint64 NMD::extract_u_17_to_2__s2(uint64 instruction) } -uint64 NMD::extract_rd_15_14_13_12_11(uint64 instruction) +static uint64 extract_rd_15_14_13_12_11(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 11, 5); @@ -1603,7 +1231,7 @@ uint64 NMD::extract_rd_15_14_13_12_11(uint64 instruction) } -uint64 NMD::extract_c0s_20_19_18_17_16(uint64 instruction) +static uint64 extract_c0s_20_19_18_17_16(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 16, 5); @@ -1611,7 +1239,7 @@ uint64 NMD::extract_c0s_20_19_18_17_16(uint64 instruction) } -uint64 NMD::extract_code_1_0(uint64 instruction) +static uint64 extract_code_1_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 2); @@ -1619,7 +1247,7 @@ uint64 NMD::extract_code_1_0(uint64 instruction) } -int64 NMD::extract_s__se25_0_24_to_1_s1(uint64 instruction) +static int64 extract_s__se25_0_24_to_1_s1(uint64 instruction) { int64 value = 0; value |= extract_bits(instruction, 0, 1) << 25; @@ -1629,7 +1257,7 @@ int64 NMD::extract_s__se25_0_24_to_1_s1(uint64 instruction) } -uint64 NMD::extract_u_1_0(uint64 instruction) +static uint64 extract_u_1_0(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 2); @@ -1637,7 +1265,7 @@ uint64 NMD::extract_u_1_0(uint64 instruction) } -uint64 NMD::extract_u_3_8__s2(uint64 instruction) +static uint64 extract_u_3_8__s2(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 3, 1) << 3; @@ -1646,7 +1274,7 @@ uint64 NMD::extract_u_3_8__s2(uint64 instruction) } -uint64 NMD::extract_fd_15_14_13_12_11(uint64 instruction) +static uint64 extract_fd_15_14_13_12_11(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 11, 5); @@ -1654,7 +1282,7 @@ uint64 NMD::extract_fd_15_14_13_12_11(uint64 instruction) } -uint64 NMD::extract_u_4_3_2_1_0__s2(uint64 instruction) +static uint64 extract_u_4_3_2_1_0__s2(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 5) << 2; @@ -1662,7 +1290,7 @@ uint64 NMD::extract_u_4_3_2_1_0__s2(uint64 instruction) } -uint64 NMD::extract_rtz4_9_7_6_5(uint64 instruction) +static uint64 extract_rtz4_9_7_6_5(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 5, 3); @@ -1671,7 +1299,7 @@ uint64 NMD::extract_rtz4_9_7_6_5(uint64 instruction) } -uint64 NMD::extract_sel_15_14_13_12_11(uint64 instruction) +static uint64 extract_sel_15_14_13_12_11(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 11, 5); @@ -1679,7 +1307,7 @@ uint64 NMD::extract_sel_15_14_13_12_11(uint64 instruction) } -uint64 NMD::extract_ct_25_24_23_22_21(uint64 instruction) +static uint64 extract_ct_25_24_23_22_21(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 21, 5); @@ -1687,7 +1315,7 @@ uint64 NMD::extract_ct_25_24_23_22_21(uint64 instruction) } -uint64 NMD::extract_u_20_to_2__s2(uint64 instruction) +static uint64 extract_u_20_to_2__s2(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 2, 19) << 2; @@ -1695,7 +1323,7 @@ uint64 NMD::extract_u_20_to_2__s2(uint64 instruction) } -int64 NMD::extract_s__se3_4_2_1_0(uint64 instruction) +static int64 extract_s__se3_4_2_1_0(uint64 instruction) { int64 value = 0; value |= extract_bits(instruction, 0, 3); @@ -1705,7 +1333,7 @@ int64 NMD::extract_s__se3_4_2_1_0(uint64 instruction) } -uint64 NMD::extract_u_3_2_1_0__s1(uint64 instruction) +static uint64 extract_u_3_2_1_0__s1(uint64 instruction) { uint64 value = 0; value |= extract_bits(instruction, 0, 4) << 1; @@ -1714,28 +1342,28 @@ uint64 NMD::extract_u_3_2_1_0__s1(uint64 instruction) -bool NMD::ADDIU_32__cond(uint64 instruction) +static bool ADDIU_32__cond(uint64 instruction) { uint64 rt = extract_rt_25_24_23_22_21(instruction); return rt != 0; } -bool NMD::ADDIU_RS5__cond(uint64 instruction) +static bool ADDIU_RS5__cond(uint64 instruction) { uint64 rt = extract_rt_9_8_7_6_5(instruction); return rt != 0; } -bool NMD::BALRSC_cond(uint64 instruction) +static bool BALRSC_cond(uint64 instruction) { uint64 rt = extract_rt_25_24_23_22_21(instruction); return rt != 0; } -bool NMD::BEQC_16__cond(uint64 instruction) +static bool BEQC_16__cond(uint64 instruction) { uint64 rs3 = extract_rs3_6_5_4(instruction); uint64 rt3 = extract_rt3_9_8_7(instruction); @@ -1744,7 +1372,7 @@ bool NMD::BEQC_16__cond(uint64 instruction) } -bool NMD::BNEC_16__cond(uint64 instruction) +static bool BNEC_16__cond(uint64 instruction) { uint64 rs3 = extract_rs3_6_5_4(instruction); uint64 rt3 = extract_rt3_9_8_7(instruction); @@ -1753,35 +1381,35 @@ bool NMD::BNEC_16__cond(uint64 instruction) } -bool NMD::MOVE_cond(uint64 instruction) +static bool MOVE_cond(uint64 instruction) { uint64 rt = extract_rt_9_8_7_6_5(instruction); return rt != 0; } -bool NMD::P16_BR1_cond(uint64 instruction) +static bool P16_BR1_cond(uint64 instruction) { uint64 u = extract_u_3_2_1_0__s1(instruction); return u != 0; } -bool NMD::PREF_S9__cond(uint64 instruction) +static bool PREF_S9__cond(uint64 instruction) { uint64 hint = extract_hint_25_24_23_22_21(instruction); return hint != 31; } -bool NMD::PREFE_cond(uint64 instruction) +static bool PREFE_cond(uint64 instruction) { uint64 hint = extract_hint_25_24_23_22_21(instruction); return hint != 31; } -bool NMD::SLTU_cond(uint64 instruction) +static bool SLTU_cond(uint64 instruction) { uint64 rd = extract_rd_15_14_13_12_11(instruction); return rd != 0; @@ -1799,15 +1427,15 @@ bool NMD::SLTU_cond(uint64 instruction) * fs ----- * fd ----- */ -std::string NMD::ABS_D(uint64 instruction) +static char *ABS_D(uint64 instruction, Dis_info *info) { uint64 fd_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string fs = FPR(copy(fs_value)); - std::string fd = FPR(copy(fd_value)); + const char *fs = FPR(fs_value, info); + const char *fd = FPR(fd_value, info); - return img::format("ABS.D %s, %s", fd, fs); + return img_format("ABS.D %s, %s", fd, fs); } @@ -1821,15 +1449,15 @@ std::string NMD::ABS_D(uint64 instruction) * fd ----- * fs ----- */ -std::string NMD::ABS_S(uint64 instruction) +static char *ABS_S(uint64 instruction, Dis_info *info) { uint64 fd_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string fs = FPR(copy(fs_value)); - std::string fd = FPR(copy(fd_value)); + const char *fs = FPR(fs_value, info); + const char *fd = FPR(fd_value, info); - return img::format("ABS.S %s, %s", fd, fs); + return img_format("ABS.S %s, %s", fd, fs); } @@ -1843,15 +1471,15 @@ std::string NMD::ABS_S(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::ABSQ_S_PH(uint64 instruction) +static char *ABSQ_S_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("ABSQ_S.PH %s, %s", rt, rs); + return img_format("ABSQ_S.PH %s, %s", rt, rs); } @@ -1865,15 +1493,15 @@ std::string NMD::ABSQ_S_PH(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::ABSQ_S_QB(uint64 instruction) +static char *ABSQ_S_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("ABSQ_S.QB %s, %s", rt, rs); + return img_format("ABSQ_S.QB %s, %s", rt, rs); } @@ -1887,15 +1515,15 @@ std::string NMD::ABSQ_S_QB(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::ABSQ_S_W(uint64 instruction) +static char *ABSQ_S_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("ABSQ_S.W %s, %s", rt, rs); + return img_format("ABSQ_S.W %s, %s", rt, rs); } @@ -1908,17 +1536,16 @@ std::string NMD::ABSQ_S_W(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::ACLR(uint64 instruction) +static char *ACLR(uint64 instruction, Dis_info *info) { uint64 bit_value = extract_bit_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string bit = IMMEDIATE(copy(bit_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rs = GPR(rs_value, info); - return img::format("ACLR %s, %s(%s)", bit, s, rs); + return img_format("ACLR 0x%" PRIx64 ", %" PRId64 "(%s)", + bit_value, s_value, rs); } @@ -1931,17 +1558,17 @@ std::string NMD::ACLR(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::ADD(uint64 instruction) +static char *ADD(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADD %s, %s, %s", rd, rs, rt); + return img_format("ADD %s, %s, %s", rd, rs, rt); } @@ -1956,17 +1583,17 @@ std::string NMD::ADD(uint64 instruction) * fs ----- * fd ----- */ -std::string NMD::ADD_D(uint64 instruction) +static char *ADD_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); - std::string fd = FPR(copy(fd_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); + const char *fd = FPR(fd_value, info); - return img::format("ADD.D %s, %s, %s", fd, fs, ft); + return img_format("ADD.D %s, %s, %s", fd, fs, ft); } @@ -1981,17 +1608,17 @@ std::string NMD::ADD_D(uint64 instruction) * fs ----- * fd ----- */ -std::string NMD::ADD_S(uint64 instruction) +static char *ADD_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); - std::string fd = FPR(copy(fd_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); + const char *fd = FPR(fd_value, info); - return img::format("ADD.S %s, %s, %s", fd, fs, ft); + return img_format("ADD.S %s, %s, %s", fd, fs, ft); } @@ -2004,17 +1631,16 @@ std::string NMD::ADD_S(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::ADDIU_32_(uint64 instruction) +static char *ADDIU_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_15_to_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("ADDIU %s, %s, %s", rt, rs, u); + return img_format("ADDIU %s, %s, 0x%" PRIx64, rt, rs, u_value); } @@ -2027,15 +1653,14 @@ std::string NMD::ADDIU_32_(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::ADDIU_48_(uint64 instruction) +static char *ADDIU_48_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_41_40_39_38_37(instruction); int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); + const char *rt = GPR(rt_value, info); - return img::format("ADDIU %s, %s", rt, s); + return img_format("ADDIU %s, %" PRId64, rt, s_value); } @@ -2048,15 +1673,14 @@ std::string NMD::ADDIU_48_(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::ADDIU_GP48_(uint64 instruction) +static char *ADDIU_GP48_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_41_40_39_38_37(instruction); int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); + const char *rt = GPR(rt_value, info); - return img::format("ADDIU %s, $%d, %s", rt, 28, s); + return img_format("ADDIU %s, $%d, %" PRId64, rt, 28, s_value); } @@ -2069,15 +1693,14 @@ std::string NMD::ADDIU_GP48_(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::ADDIU_GP_B_(uint64 instruction) +static char *ADDIU_GP_B_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_to_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("ADDIU %s, $%d, %s", rt, 28, u); + return img_format("ADDIU %s, $%d, 0x%" PRIx64, rt, 28, u_value); } @@ -2090,15 +1713,14 @@ std::string NMD::ADDIU_GP_B_(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::ADDIU_GP_W_(uint64 instruction) +static char *ADDIU_GP_W_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_20_to_2__s2(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("ADDIU %s, $%d, %s", rt, 28, u); + return img_format("ADDIU %s, $%d, 0x%" PRIx64, rt, 28, u_value); } @@ -2111,17 +1733,17 @@ std::string NMD::ADDIU_GP_W_(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::ADDIU_NEG_(uint64 instruction) +static char *ADDIU_NEG_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string u = IMMEDIATE(neg_copy(u_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); + int64 u = neg_copy(u_value); - return img::format("ADDIU %s, %s, %s", rt, rs, u); + return img_format("ADDIU %s, %s, %" PRId64, rt, rs, u); } @@ -2134,15 +1756,14 @@ std::string NMD::ADDIU_NEG_(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::ADDIU_R1_SP_(uint64 instruction) +static char *ADDIU_R1_SP_(uint64 instruction, Dis_info *info) { uint64 u_value = extract_u_5_4_3_2_1_0__s2(instruction); uint64 rt3_value = extract_rt3_9_8_7(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); - return img::format("ADDIU %s, $%d, %s", rt3, 29, u); + return img_format("ADDIU %s, $%d, 0x%" PRIx64, rt3, 29, u_value); } @@ -2155,17 +1776,16 @@ std::string NMD::ADDIU_R1_SP_(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::ADDIU_R2_(uint64 instruction) +static char *ADDIU_R2_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 u_value = extract_u_2_1_0__s2(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); - return img::format("ADDIU %s, %s, %s", rt3, rs3, u); + return img_format("ADDIU %s, %s, 0x%" PRIx64, rt3, rs3, u_value); } @@ -2177,15 +1797,14 @@ std::string NMD::ADDIU_R2_(uint64 instruction) * rt ----- * s - --- */ -std::string NMD::ADDIU_RS5_(uint64 instruction) +static char *ADDIU_RS5_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_9_8_7_6_5(instruction); int64 s_value = extract_s__se3_4_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); + const char *rt = GPR(rt_value, info); - return img::format("ADDIU %s, %s", rt, s); + return img_format("ADDIU %s, %" PRId64, rt, s_value); } @@ -2199,15 +1818,15 @@ std::string NMD::ADDIU_RS5_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDIUPC_32_(uint64 instruction) +static char *ADDIUPC_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); int64 s_value = extract_s__se21_0_20_to_1_s1(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("ADDIUPC %s, %s", rt, s); + return img_format("ADDIUPC %s, %s", rt, s); } @@ -2221,15 +1840,15 @@ std::string NMD::ADDIUPC_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDIUPC_48_(uint64 instruction) +static char *ADDIUPC_48_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_41_40_39_38_37(instruction); int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 6); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 6, info); - return img::format("ADDIUPC %s, %s", rt, s); + return img_format("ADDIUPC %s, %s", rt, s); } @@ -2243,17 +1862,17 @@ std::string NMD::ADDIUPC_48_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDQ_PH(uint64 instruction) +static char *ADDQ_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDQ.PH %s, %s, %s", rd, rs, rt); + return img_format("ADDQ.PH %s, %s, %s", rd, rs, rt); } @@ -2268,17 +1887,17 @@ std::string NMD::ADDQ_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDQ_S_PH(uint64 instruction) +static char *ADDQ_S_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDQ_S.PH %s, %s, %s", rd, rs, rt); + return img_format("ADDQ_S.PH %s, %s, %s", rd, rs, rt); } @@ -2292,17 +1911,17 @@ std::string NMD::ADDQ_S_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDQ_S_W(uint64 instruction) +static char *ADDQ_S_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDQ_S.W %s, %s, %s", rd, rs, rt); + return img_format("ADDQ_S.W %s, %s, %s", rd, rs, rt); } @@ -2317,17 +1936,17 @@ std::string NMD::ADDQ_S_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDQH_PH(uint64 instruction) +static char *ADDQH_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDQH.PH %s, %s, %s", rd, rs, rt); + return img_format("ADDQH.PH %s, %s, %s", rd, rs, rt); } @@ -2342,17 +1961,17 @@ std::string NMD::ADDQH_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDQH_R_PH(uint64 instruction) +static char *ADDQH_R_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDQH_R.PH %s, %s, %s", rd, rs, rt); + return img_format("ADDQH_R.PH %s, %s, %s", rd, rs, rt); } @@ -2367,17 +1986,17 @@ std::string NMD::ADDQH_R_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDQH_R_W(uint64 instruction) +static char *ADDQH_R_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDQH_R.W %s, %s, %s", rd, rs, rt); + return img_format("ADDQH_R.W %s, %s, %s", rd, rs, rt); } @@ -2392,17 +2011,17 @@ std::string NMD::ADDQH_R_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDQH_W(uint64 instruction) +static char *ADDQH_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDQH.W %s, %s, %s", rd, rs, rt); + return img_format("ADDQH.W %s, %s, %s", rd, rs, rt); } @@ -2416,17 +2035,17 @@ std::string NMD::ADDQH_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDSC(uint64 instruction) +static char *ADDSC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDSC %s, %s, %s", rd, rs, rt); + return img_format("ADDSC %s, %s, %s", rd, rs, rt); } @@ -2439,17 +2058,17 @@ std::string NMD::ADDSC(uint64 instruction) * rs3 --- * rd3 --- */ -std::string NMD::ADDU_16_(uint64 instruction) +static char *ADDU_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 rd3_value = extract_rd3_3_2_1(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); - std::string rd3 = GPR(decode_gpr_gpr3(rd3_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); + const char *rd3 = GPR(decode_gpr_gpr3(rd3_value, info), info); - return img::format("ADDU %s, %s, %s", rd3, rs3, rt3); + return img_format("ADDU %s, %s, %s", rd3, rs3, rt3); } @@ -2463,17 +2082,17 @@ std::string NMD::ADDU_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDU_32_(uint64 instruction) +static char *ADDU_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDU %s, %s, %s", rd, rs, rt); + return img_format("ADDU %s, %s, %s", rd, rs, rt); } @@ -2487,15 +2106,15 @@ std::string NMD::ADDU_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDU_4X4_(uint64 instruction) +static char *ADDU_4X4_(uint64 instruction, Dis_info *info) { uint64 rt4_value = extract_rt4_9_7_6_5(instruction); uint64 rs4_value = extract_rs4_4_2_1_0(instruction); - std::string rs4 = GPR(decode_gpr_gpr4(rs4_value)); - std::string rt4 = GPR(decode_gpr_gpr4(rt4_value)); + const char *rs4 = GPR(decode_gpr_gpr4(rs4_value, info), info); + const char *rt4 = GPR(decode_gpr_gpr4(rt4_value, info), info); - return img::format("ADDU %s, %s", rs4, rt4); + return img_format("ADDU %s, %s", rs4, rt4); } @@ -2509,17 +2128,17 @@ std::string NMD::ADDU_4X4_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDU_PH(uint64 instruction) +static char *ADDU_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDU.PH %s, %s, %s", rd, rs, rt); + return img_format("ADDU.PH %s, %s, %s", rd, rs, rt); } @@ -2533,17 +2152,17 @@ std::string NMD::ADDU_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDU_QB(uint64 instruction) +static char *ADDU_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDU.QB %s, %s, %s", rd, rs, rt); + return img_format("ADDU.QB %s, %s, %s", rd, rs, rt); } @@ -2558,17 +2177,17 @@ std::string NMD::ADDU_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDU_S_PH(uint64 instruction) +static char *ADDU_S_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDU_S.PH %s, %s, %s", rd, rs, rt); + return img_format("ADDU_S.PH %s, %s, %s", rd, rs, rt); } @@ -2582,17 +2201,17 @@ std::string NMD::ADDU_S_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDU_S_QB(uint64 instruction) +static char *ADDU_S_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDU_S.QB %s, %s, %s", rd, rs, rt); + return img_format("ADDU_S.QB %s, %s, %s", rd, rs, rt); } @@ -2607,17 +2226,17 @@ std::string NMD::ADDU_S_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDUH_QB(uint64 instruction) +static char *ADDUH_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDUH.QB %s, %s, %s", rd, rs, rt); + return img_format("ADDUH.QB %s, %s, %s", rd, rs, rt); } @@ -2632,17 +2251,17 @@ std::string NMD::ADDUH_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDUH_R_QB(uint64 instruction) +static char *ADDUH_R_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDUH_R.QB %s, %s, %s", rd, rs, rt); + return img_format("ADDUH_R.QB %s, %s, %s", rd, rs, rt); } /* @@ -2655,17 +2274,17 @@ std::string NMD::ADDUH_R_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ADDWC(uint64 instruction) +static char *ADDWC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ADDWC %s, %s, %s", rd, rs, rt); + return img_format("ADDWC %s, %s, %s", rd, rs, rt); } @@ -2679,15 +2298,15 @@ std::string NMD::ADDWC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ALUIPC(uint64 instruction) +static char *ALUIPC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); int64 s_value = extract_s__se31_0_11_to_2_20_to_12_s12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("ALUIPC %s, %%pcrel_hi(%s)", rt, s); + return img_format("ALUIPC %s, %%pcrel_hi(%s)", rt, s); } @@ -2700,15 +2319,15 @@ std::string NMD::ALUIPC(uint64 instruction) * rs3 --- * eu ---- */ -std::string NMD::AND_16_(uint64 instruction) +static char *AND_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); - return img::format("AND %s, %s", rs3, rt3); + return img_format("AND %s, %s", rs3, rt3); } @@ -2722,17 +2341,17 @@ std::string NMD::AND_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::AND_32_(uint64 instruction) +static char *AND_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("AND %s, %s, %s", rd, rs, rt); + return img_format("AND %s, %s, %s", rd, rs, rt); } @@ -2745,17 +2364,17 @@ std::string NMD::AND_32_(uint64 instruction) * rs3 --- * eu ---- */ -std::string NMD::ANDI_16_(uint64 instruction) +static char *ANDI_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 eu_value = extract_eu_3_2_1_0(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); - std::string eu = IMMEDIATE(encode_eu_from_u_andi16(eu_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); + uint64 eu = encode_eu_from_u_andi16(eu_value); - return img::format("ANDI %s, %s, %s", rt3, rs3, eu); + return img_format("ANDI %s, %s, 0x%" PRIx64, rt3, rs3, eu); } @@ -2769,17 +2388,16 @@ std::string NMD::ANDI_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ANDI_32_(uint64 instruction) +static char *ANDI_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("ANDI %s, %s, %s", rt, rs, u); + return img_format("ANDI %s, %s, 0x%" PRIx64, rt, rs, u_value); } @@ -2793,17 +2411,16 @@ std::string NMD::ANDI_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::APPEND(uint64 instruction) +static char *APPEND(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("APPEND %s, %s, %s", rt, rs, sa); + return img_format("APPEND %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -2817,17 +2434,16 @@ std::string NMD::APPEND(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ASET(uint64 instruction) +static char *ASET(uint64 instruction, Dis_info *info) { uint64 bit_value = extract_bit_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string bit = IMMEDIATE(copy(bit_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rs = GPR(rs_value, info); - return img::format("ASET %s, %s(%s)", bit, s, rs); + return img_format("ASET 0x%" PRIx64 ", %" PRId64 "(%s)", + bit_value, s_value, rs); } @@ -2841,13 +2457,13 @@ std::string NMD::ASET(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BALC_16_(uint64 instruction) +static char *BALC_16_(uint64 instruction, Dis_info *info) { int64 s_value = extract_s__se10_0_9_8_7_6_5_4_3_2_1_s1(instruction); - std::string s = ADDRESS(encode_s_from_address(s_value), 2); + g_autofree char *s = ADDRESS(s_value, 2, info); - return img::format("BALC %s", s); + return img_format("BALC %s", s); } @@ -2861,13 +2477,13 @@ std::string NMD::BALC_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BALC_32_(uint64 instruction) +static char *BALC_32_(uint64 instruction, Dis_info *info) { int64 s_value = extract_s__se25_0_24_to_1_s1(instruction); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BALC %s", s); + return img_format("BALC %s", s); } @@ -2881,15 +2497,15 @@ std::string NMD::BALC_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BALRSC(uint64 instruction) +static char *BALRSC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("BALRSC %s, %s", rt, rs); + return img_format("BALRSC %s, %s", rt, rs); } @@ -2903,17 +2519,16 @@ std::string NMD::BALRSC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BBEQZC(uint64 instruction) +static char *BBEQZC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 bit_value = extract_bit_16_15_14_13_12_11(instruction); int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); - std::string rt = GPR(copy(rt_value)); - std::string bit = IMMEDIATE(copy(bit_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BBEQZC %s, %s, %s", rt, bit, s); + return img_format("BBEQZC %s, 0x%" PRIx64 ", %s", rt, bit_value, s); } @@ -2927,17 +2542,16 @@ std::string NMD::BBEQZC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BBNEZC(uint64 instruction) +static char *BBNEZC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 bit_value = extract_bit_16_15_14_13_12_11(instruction); int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); - std::string rt = GPR(copy(rt_value)); - std::string bit = IMMEDIATE(copy(bit_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BBNEZC %s, %s, %s", rt, bit, s); + return img_format("BBNEZC %s, 0x%" PRIx64 ", %s", rt, bit_value, s); } @@ -2951,13 +2565,13 @@ std::string NMD::BBNEZC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BC_16_(uint64 instruction) +static char *BC_16_(uint64 instruction, Dis_info *info) { int64 s_value = extract_s__se10_0_9_8_7_6_5_4_3_2_1_s1(instruction); - std::string s = ADDRESS(encode_s_from_address(s_value), 2); + g_autofree char *s = ADDRESS(s_value, 2, info); - return img::format("BC %s", s); + return img_format("BC %s", s); } @@ -2971,13 +2585,13 @@ std::string NMD::BC_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BC_32_(uint64 instruction) +static char *BC_32_(uint64 instruction, Dis_info *info) { int64 s_value = extract_s__se25_0_24_to_1_s1(instruction); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BC %s", s); + return img_format("BC %s", s); } @@ -2991,15 +2605,15 @@ std::string NMD::BC_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BC1EQZC(uint64 instruction) +static char *BC1EQZC(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); - std::string ft = FPR(copy(ft_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *ft = FPR(ft_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BC1EQZC %s, %s", ft, s); + return img_format("BC1EQZC %s, %s", ft, s); } @@ -3013,15 +2627,15 @@ std::string NMD::BC1EQZC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BC1NEZC(uint64 instruction) +static char *BC1NEZC(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); - std::string ft = FPR(copy(ft_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *ft = FPR(ft_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BC1NEZC %s, %s", ft, s); + return img_format("BC1NEZC %s, %s", ft, s); } @@ -3035,15 +2649,14 @@ std::string NMD::BC1NEZC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BC2EQZC(uint64 instruction) +static char *BC2EQZC(uint64 instruction, Dis_info *info) { uint64 ct_value = extract_ct_25_24_23_22_21(instruction); int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); - std::string ct = CPR(copy(ct_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BC2EQZC %s, %s", ct, s); + return img_format("BC2EQZC CP%" PRIu64 ", %s", ct_value, s); } @@ -3057,15 +2670,14 @@ std::string NMD::BC2EQZC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BC2NEZC(uint64 instruction) +static char *BC2NEZC(uint64 instruction, Dis_info *info) { uint64 ct_value = extract_ct_25_24_23_22_21(instruction); int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); - std::string ct = CPR(copy(ct_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BC2NEZC %s, %s", ct, s); + return img_format("BC2NEZC CP%" PRIu64 ", %s", ct_value, s); } @@ -3079,17 +2691,17 @@ std::string NMD::BC2NEZC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BEQC_16_(uint64 instruction) +static char *BEQC_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 u_value = extract_u_3_2_1_0__s1(instruction); - std::string rs3 = GPR(encode_rs3_and_check_rs3_lt_rt3(rs3_value)); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string u = ADDRESS(encode_u_from_address(u_value), 2); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + g_autofree char *u = ADDRESS(u_value, 2, info); - return img::format("BEQC %s, %s, %s", rs3, rt3, u); + return img_format("BEQC %s, %s, %s", rs3, rt3, u); } @@ -3103,17 +2715,17 @@ std::string NMD::BEQC_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BEQC_32_(uint64 instruction) +static char *BEQC_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BEQC %s, %s, %s", rs, rt, s); + return img_format("BEQC %s, %s, %s", rs, rt, s); } @@ -3127,17 +2739,16 @@ std::string NMD::BEQC_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BEQIC(uint64 instruction) +static char *BEQIC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_16_15_14_13_12_11(instruction); int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BEQIC %s, %s, %s", rt, u, s); + return img_format("BEQIC %s, 0x%" PRIx64 ", %s", rt, u_value, s); } @@ -3151,15 +2762,15 @@ std::string NMD::BEQIC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BEQZC_16_(uint64 instruction) +static char *BEQZC_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); int64 s_value = extract_s__se7_0_6_5_4_3_2_1_s1(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 2); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + g_autofree char *s = ADDRESS(s_value, 2, info); - return img::format("BEQZC %s, %s", rt3, s); + return img_format("BEQZC %s, %s", rt3, s); } @@ -3173,17 +2784,17 @@ std::string NMD::BEQZC_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BGEC(uint64 instruction) +static char *BGEC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BGEC %s, %s, %s", rs, rt, s); + return img_format("BGEC %s, %s, %s", rs, rt, s); } @@ -3197,17 +2808,16 @@ std::string NMD::BGEC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BGEIC(uint64 instruction) +static char *BGEIC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_16_15_14_13_12_11(instruction); int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BGEIC %s, %s, %s", rt, u, s); + return img_format("BGEIC %s, 0x%" PRIx64 ", %s", rt, u_value, s); } @@ -3221,17 +2831,16 @@ std::string NMD::BGEIC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BGEIUC(uint64 instruction) +static char *BGEIUC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_16_15_14_13_12_11(instruction); int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BGEIUC %s, %s, %s", rt, u, s); + return img_format("BGEIUC %s, 0x%" PRIx64 ", %s", rt, u_value, s); } @@ -3245,17 +2854,17 @@ std::string NMD::BGEIUC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BGEUC(uint64 instruction) +static char *BGEUC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BGEUC %s, %s, %s", rs, rt, s); + return img_format("BGEUC %s, %s, %s", rs, rt, s); } @@ -3269,17 +2878,17 @@ std::string NMD::BGEUC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BLTC(uint64 instruction) +static char *BLTC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BLTC %s, %s, %s", rs, rt, s); + return img_format("BLTC %s, %s, %s", rs, rt, s); } @@ -3293,17 +2902,16 @@ std::string NMD::BLTC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BLTIC(uint64 instruction) +static char *BLTIC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_16_15_14_13_12_11(instruction); int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BLTIC %s, %s, %s", rt, u, s); + return img_format("BLTIC %s, 0x%" PRIx64 ", %s", rt, u_value, s); } @@ -3317,17 +2925,16 @@ std::string NMD::BLTIC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BLTIUC(uint64 instruction) +static char *BLTIUC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_16_15_14_13_12_11(instruction); int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BLTIUC %s, %s, %s", rt, u, s); + return img_format("BLTIUC %s, 0x%" PRIx64 ", %s", rt, u_value, s); } @@ -3341,17 +2948,17 @@ std::string NMD::BLTIUC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BLTUC(uint64 instruction) +static char *BLTUC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BLTUC %s, %s, %s", rs, rt, s); + return img_format("BLTUC %s, %s, %s", rs, rt, s); } @@ -3365,17 +2972,17 @@ std::string NMD::BLTUC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BNEC_16_(uint64 instruction) +static char *BNEC_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 u_value = extract_u_3_2_1_0__s1(instruction); - std::string rs3 = GPR(encode_rs3_and_check_rs3_ge_rt3(rs3_value)); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string u = ADDRESS(encode_u_from_address(u_value), 2); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + g_autofree char *u = ADDRESS(u_value, 2, info); - return img::format("BNEC %s, %s, %s", rs3, rt3, u); + return img_format("BNEC %s, %s, %s", rs3, rt3, u); } @@ -3389,17 +2996,17 @@ std::string NMD::BNEC_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BNEC_32_(uint64 instruction) +static char *BNEC_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BNEC %s, %s, %s", rs, rt, s); + return img_format("BNEC %s, %s, %s", rs, rt, s); } @@ -3413,17 +3020,16 @@ std::string NMD::BNEC_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BNEIC(uint64 instruction) +static char *BNEIC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_16_15_14_13_12_11(instruction); int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BNEIC %s, %s, %s", rt, u, s); + return img_format("BNEIC %s, 0x%" PRIx64 ", %s", rt, u_value, s); } @@ -3437,15 +3043,15 @@ std::string NMD::BNEIC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BNEZC_16_(uint64 instruction) +static char *BNEZC_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); int64 s_value = extract_s__se7_0_6_5_4_3_2_1_s1(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 2); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + g_autofree char *s = ADDRESS(s_value, 2, info); - return img::format("BNEZC %s, %s", rt3, s); + return img_format("BNEZC %s, %s", rt3, s); } @@ -3459,13 +3065,13 @@ std::string NMD::BNEZC_16_(uint64 instruction) * s[13:1] ------------- * s[14] - */ -std::string NMD::BPOSGE32C(uint64 instruction) +static char *BPOSGE32C(uint64 instruction, Dis_info *info) { int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("BPOSGE32C %s", s); + return img_format("BPOSGE32C %s", s); } @@ -3479,13 +3085,12 @@ std::string NMD::BPOSGE32C(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BREAK_16_(uint64 instruction) +static char *BREAK_16_(uint64 instruction, Dis_info *info) { uint64 code_value = extract_code_2_1_0(instruction); - std::string code = IMMEDIATE(copy(code_value)); - return img::format("BREAK %s", code); + return img_format("BREAK 0x%" PRIx64, code_value); } @@ -3499,13 +3104,12 @@ std::string NMD::BREAK_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BREAK_32_(uint64 instruction) +static char *BREAK_32_(uint64 instruction, Dis_info *info) { uint64 code_value = extract_code_18_to_0(instruction); - std::string code = IMMEDIATE(copy(code_value)); - return img::format("BREAK %s", code); + return img_format("BREAK 0x%" PRIx64, code_value); } @@ -3519,13 +3123,13 @@ std::string NMD::BREAK_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::BRSC(uint64 instruction) +static char *BRSC(uint64 instruction, Dis_info *info) { uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rs = GPR(copy(rs_value)); + const char *rs = GPR(rs_value, info); - return img::format("BRSC %s", rs); + return img_format("BRSC %s", rs); } @@ -3539,17 +3143,16 @@ std::string NMD::BRSC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CACHE(uint64 instruction) +static char *CACHE(uint64 instruction, Dis_info *info) { uint64 op_value = extract_op_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string op = IMMEDIATE(copy(op_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rs = GPR(rs_value, info); - return img::format("CACHE %s, %s(%s)", op, s, rs); + return img_format("CACHE 0x%" PRIx64 ", %" PRId64 "(%s)", + op_value, s_value, rs); } @@ -3563,17 +3166,16 @@ std::string NMD::CACHE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CACHEE(uint64 instruction) +static char *CACHEE(uint64 instruction, Dis_info *info) { uint64 op_value = extract_op_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string op = IMMEDIATE(copy(op_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rs = GPR(rs_value, info); - return img::format("CACHEE %s, %s(%s)", op, s, rs); + return img_format("CACHEE 0x%" PRIx64 ", %" PRId64 "(%s)", + op_value, s_value, rs); } @@ -3587,15 +3189,15 @@ std::string NMD::CACHEE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CEIL_L_D(uint64 instruction) +static char *CEIL_L_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CEIL.L.D %s, %s", ft, fs); + return img_format("CEIL.L.D %s, %s", ft, fs); } @@ -3609,15 +3211,15 @@ std::string NMD::CEIL_L_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CEIL_L_S(uint64 instruction) +static char *CEIL_L_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CEIL.L.S %s, %s", ft, fs); + return img_format("CEIL.L.S %s, %s", ft, fs); } @@ -3631,15 +3233,15 @@ std::string NMD::CEIL_L_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CEIL_W_D(uint64 instruction) +static char *CEIL_W_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CEIL.W.D %s, %s", ft, fs); + return img_format("CEIL.W.D %s, %s", ft, fs); } @@ -3653,15 +3255,15 @@ std::string NMD::CEIL_W_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CEIL_W_S(uint64 instruction) +static char *CEIL_W_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CEIL.W.S %s, %s", ft, fs); + return img_format("CEIL.W.S %s, %s", ft, fs); } @@ -3675,15 +3277,14 @@ std::string NMD::CEIL_W_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CFC1(uint64 instruction) +static char *CFC1(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 cs_value = extract_cs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string cs = CPR(copy(cs_value)); + const char *rt = GPR(rt_value, info); - return img::format("CFC1 %s, %s", rt, cs); + return img_format("CFC1 %s, CP%" PRIu64, rt, cs_value); } @@ -3697,15 +3298,14 @@ std::string NMD::CFC1(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CFC2(uint64 instruction) +static char *CFC2(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 cs_value = extract_cs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string cs = CPR(copy(cs_value)); + const char *rt = GPR(rt_value, info); - return img::format("CFC2 %s, %s", rt, cs); + return img_format("CFC2 %s, CP%" PRIu64, rt, cs_value); } @@ -3719,15 +3319,15 @@ std::string NMD::CFC2(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CLASS_D(uint64 instruction) +static char *CLASS_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CLASS.D %s, %s", ft, fs); + return img_format("CLASS.D %s, %s", ft, fs); } @@ -3741,15 +3341,15 @@ std::string NMD::CLASS_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CLASS_S(uint64 instruction) +static char *CLASS_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CLASS.S %s, %s", ft, fs); + return img_format("CLASS.S %s, %s", ft, fs); } @@ -3763,15 +3363,15 @@ std::string NMD::CLASS_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CLO(uint64 instruction) +static char *CLO(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("CLO %s, %s", rt, rs); + return img_format("CLO %s, %s", rt, rs); } @@ -3785,15 +3385,15 @@ std::string NMD::CLO(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CLZ(uint64 instruction) +static char *CLZ(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("CLZ %s, %s", rt, rs); + return img_format("CLZ %s, %s", rt, rs); } @@ -3807,17 +3407,17 @@ std::string NMD::CLZ(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_AF_D(uint64 instruction) +static char *CMP_AF_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.AF.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.AF.D %s, %s, %s", fd, fs, ft); } @@ -3831,17 +3431,17 @@ std::string NMD::CMP_AF_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_AF_S(uint64 instruction) +static char *CMP_AF_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.AF.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.AF.S %s, %s, %s", fd, fs, ft); } @@ -3855,17 +3455,17 @@ std::string NMD::CMP_AF_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_EQ_D(uint64 instruction) +static char *CMP_EQ_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.EQ.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.EQ.D %s, %s, %s", fd, fs, ft); } @@ -3878,15 +3478,15 @@ std::string NMD::CMP_EQ_D(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::CMP_EQ_PH(uint64 instruction) +static char *CMP_EQ_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("CMP.EQ.PH %s, %s", rs, rt); + return img_format("CMP.EQ.PH %s, %s", rs, rt); } @@ -3900,17 +3500,17 @@ std::string NMD::CMP_EQ_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_EQ_S(uint64 instruction) +static char *CMP_EQ_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.EQ.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.EQ.S %s, %s, %s", fd, fs, ft); } @@ -3924,17 +3524,17 @@ std::string NMD::CMP_EQ_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_LE_D(uint64 instruction) +static char *CMP_LE_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.LE.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.LE.D %s, %s, %s", fd, fs, ft); } @@ -3947,15 +3547,15 @@ std::string NMD::CMP_LE_D(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::CMP_LE_PH(uint64 instruction) +static char *CMP_LE_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("CMP.LE.PH %s, %s", rs, rt); + return img_format("CMP.LE.PH %s, %s", rs, rt); } @@ -3969,17 +3569,17 @@ std::string NMD::CMP_LE_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_LE_S(uint64 instruction) +static char *CMP_LE_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.LE.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.LE.S %s, %s, %s", fd, fs, ft); } @@ -3993,17 +3593,17 @@ std::string NMD::CMP_LE_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_LT_D(uint64 instruction) +static char *CMP_LT_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.LT.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.LT.D %s, %s, %s", fd, fs, ft); } @@ -4016,15 +3616,15 @@ std::string NMD::CMP_LT_D(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::CMP_LT_PH(uint64 instruction) +static char *CMP_LT_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("CMP.LT.PH %s, %s", rs, rt); + return img_format("CMP.LT.PH %s, %s", rs, rt); } @@ -4038,17 +3638,17 @@ std::string NMD::CMP_LT_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_LT_S(uint64 instruction) +static char *CMP_LT_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.LT.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.LT.S %s, %s, %s", fd, fs, ft); } @@ -4062,17 +3662,17 @@ std::string NMD::CMP_LT_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_NE_D(uint64 instruction) +static char *CMP_NE_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.NE.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.NE.D %s, %s, %s", fd, fs, ft); } @@ -4086,17 +3686,17 @@ std::string NMD::CMP_NE_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_NE_S(uint64 instruction) +static char *CMP_NE_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.NE.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.NE.S %s, %s, %s", fd, fs, ft); } @@ -4110,17 +3710,17 @@ std::string NMD::CMP_NE_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_OR_D(uint64 instruction) +static char *CMP_OR_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.OR.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.OR.D %s, %s, %s", fd, fs, ft); } @@ -4134,17 +3734,17 @@ std::string NMD::CMP_OR_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_OR_S(uint64 instruction) +static char *CMP_OR_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.OR.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.OR.S %s, %s, %s", fd, fs, ft); } @@ -4158,17 +3758,17 @@ std::string NMD::CMP_OR_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SAF_D(uint64 instruction) +static char *CMP_SAF_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SAF.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.SAF.D %s, %s, %s", fd, fs, ft); } @@ -4182,17 +3782,17 @@ std::string NMD::CMP_SAF_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SAF_S(uint64 instruction) +static char *CMP_SAF_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SAF.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.SAF.S %s, %s, %s", fd, fs, ft); } @@ -4206,17 +3806,17 @@ std::string NMD::CMP_SAF_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SEQ_D(uint64 instruction) +static char *CMP_SEQ_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SEQ.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.SEQ.D %s, %s, %s", fd, fs, ft); } @@ -4230,17 +3830,17 @@ std::string NMD::CMP_SEQ_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SEQ_S(uint64 instruction) +static char *CMP_SEQ_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SEQ.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.SEQ.S %s, %s, %s", fd, fs, ft); } @@ -4254,17 +3854,17 @@ std::string NMD::CMP_SEQ_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SLE_D(uint64 instruction) +static char *CMP_SLE_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SLE.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.SLE.D %s, %s, %s", fd, fs, ft); } @@ -4278,17 +3878,17 @@ std::string NMD::CMP_SLE_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SLE_S(uint64 instruction) +static char *CMP_SLE_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SLE.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.SLE.S %s, %s, %s", fd, fs, ft); } @@ -4302,17 +3902,17 @@ std::string NMD::CMP_SLE_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SLT_D(uint64 instruction) +static char *CMP_SLT_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SLT.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.SLT.D %s, %s, %s", fd, fs, ft); } @@ -4326,17 +3926,17 @@ std::string NMD::CMP_SLT_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SLT_S(uint64 instruction) +static char *CMP_SLT_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SLT.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.SLT.S %s, %s, %s", fd, fs, ft); } @@ -4350,17 +3950,17 @@ std::string NMD::CMP_SLT_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SNE_D(uint64 instruction) +static char *CMP_SNE_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SNE.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.SNE.D %s, %s, %s", fd, fs, ft); } @@ -4374,17 +3974,17 @@ std::string NMD::CMP_SNE_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SNE_S(uint64 instruction) +static char *CMP_SNE_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SNE.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.SNE.S %s, %s, %s", fd, fs, ft); } @@ -4398,17 +3998,17 @@ std::string NMD::CMP_SNE_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SOR_D(uint64 instruction) +static char *CMP_SOR_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SOR.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.SOR.D %s, %s, %s", fd, fs, ft); } @@ -4422,17 +4022,17 @@ std::string NMD::CMP_SOR_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SOR_S(uint64 instruction) +static char *CMP_SOR_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SOR.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.SOR.S %s, %s, %s", fd, fs, ft); } @@ -4446,17 +4046,17 @@ std::string NMD::CMP_SOR_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SUEQ_D(uint64 instruction) +static char *CMP_SUEQ_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SUEQ.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.SUEQ.D %s, %s, %s", fd, fs, ft); } @@ -4470,17 +4070,17 @@ std::string NMD::CMP_SUEQ_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SUEQ_S(uint64 instruction) +static char *CMP_SUEQ_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SUEQ.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.SUEQ.S %s, %s, %s", fd, fs, ft); } @@ -4494,17 +4094,17 @@ std::string NMD::CMP_SUEQ_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SULE_D(uint64 instruction) +static char *CMP_SULE_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SULE.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.SULE.D %s, %s, %s", fd, fs, ft); } @@ -4518,17 +4118,17 @@ std::string NMD::CMP_SULE_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SULE_S(uint64 instruction) +static char *CMP_SULE_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SULE.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.SULE.S %s, %s, %s", fd, fs, ft); } @@ -4542,17 +4142,17 @@ std::string NMD::CMP_SULE_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SULT_D(uint64 instruction) +static char *CMP_SULT_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SULT.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.SULT.D %s, %s, %s", fd, fs, ft); } @@ -4566,17 +4166,17 @@ std::string NMD::CMP_SULT_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SULT_S(uint64 instruction) +static char *CMP_SULT_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SULT.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.SULT.S %s, %s, %s", fd, fs, ft); } @@ -4590,17 +4190,17 @@ std::string NMD::CMP_SULT_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SUN_D(uint64 instruction) +static char *CMP_SUN_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SUN.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.SUN.D %s, %s, %s", fd, fs, ft); } @@ -4614,17 +4214,17 @@ std::string NMD::CMP_SUN_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SUNE_D(uint64 instruction) +static char *CMP_SUNE_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SUNE.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.SUNE.D %s, %s, %s", fd, fs, ft); } @@ -4638,17 +4238,17 @@ std::string NMD::CMP_SUNE_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SUNE_S(uint64 instruction) +static char *CMP_SUNE_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SUNE.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.SUNE.S %s, %s, %s", fd, fs, ft); } @@ -4662,17 +4262,17 @@ std::string NMD::CMP_SUNE_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_SUN_S(uint64 instruction) +static char *CMP_SUN_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.SUN.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.SUN.S %s, %s, %s", fd, fs, ft); } @@ -4686,17 +4286,17 @@ std::string NMD::CMP_SUN_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_UEQ_D(uint64 instruction) +static char *CMP_UEQ_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.UEQ.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.UEQ.D %s, %s, %s", fd, fs, ft); } @@ -4710,17 +4310,17 @@ std::string NMD::CMP_UEQ_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_UEQ_S(uint64 instruction) +static char *CMP_UEQ_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.UEQ.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.UEQ.S %s, %s, %s", fd, fs, ft); } @@ -4734,17 +4334,17 @@ std::string NMD::CMP_UEQ_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_ULE_D(uint64 instruction) +static char *CMP_ULE_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.ULE.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.ULE.D %s, %s, %s", fd, fs, ft); } @@ -4758,17 +4358,17 @@ std::string NMD::CMP_ULE_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_ULE_S(uint64 instruction) +static char *CMP_ULE_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.ULE.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.ULE.S %s, %s, %s", fd, fs, ft); } @@ -4782,17 +4382,17 @@ std::string NMD::CMP_ULE_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_ULT_D(uint64 instruction) +static char *CMP_ULT_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.ULT.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.ULT.D %s, %s, %s", fd, fs, ft); } @@ -4806,17 +4406,17 @@ std::string NMD::CMP_ULT_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_ULT_S(uint64 instruction) +static char *CMP_ULT_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.ULT.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.ULT.S %s, %s, %s", fd, fs, ft); } @@ -4830,17 +4430,17 @@ std::string NMD::CMP_ULT_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_UN_D(uint64 instruction) +static char *CMP_UN_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.UN.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.UN.D %s, %s, %s", fd, fs, ft); } @@ -4854,17 +4454,17 @@ std::string NMD::CMP_UN_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_UNE_D(uint64 instruction) +static char *CMP_UNE_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.UNE.D %s, %s, %s", fd, fs, ft); + return img_format("CMP.UNE.D %s, %s, %s", fd, fs, ft); } @@ -4878,17 +4478,17 @@ std::string NMD::CMP_UNE_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_UNE_S(uint64 instruction) +static char *CMP_UNE_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.UNE.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.UNE.S %s, %s, %s", fd, fs, ft); } @@ -4902,17 +4502,17 @@ std::string NMD::CMP_UNE_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMP_UN_S(uint64 instruction) +static char *CMP_UN_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("CMP.UN.S %s, %s, %s", fd, fs, ft); + return img_format("CMP.UN.S %s, %s, %s", fd, fs, ft); } @@ -4927,17 +4527,17 @@ std::string NMD::CMP_UN_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMPGDU_EQ_QB(uint64 instruction) +static char *CMPGDU_EQ_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("CMPGDU.EQ.QB %s, %s, %s", rd, rs, rt); + return img_format("CMPGDU.EQ.QB %s, %s, %s", rd, rs, rt); } @@ -4952,17 +4552,17 @@ std::string NMD::CMPGDU_EQ_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMPGDU_LE_QB(uint64 instruction) +static char *CMPGDU_LE_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("CMPGDU.LE.QB %s, %s, %s", rd, rs, rt); + return img_format("CMPGDU.LE.QB %s, %s, %s", rd, rs, rt); } @@ -4977,17 +4577,17 @@ std::string NMD::CMPGDU_LE_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMPGDU_LT_QB(uint64 instruction) +static char *CMPGDU_LT_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("CMPGDU.LT.QB %s, %s, %s", rd, rs, rt); + return img_format("CMPGDU.LT.QB %s, %s, %s", rd, rs, rt); } @@ -5002,17 +4602,17 @@ std::string NMD::CMPGDU_LT_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMPGU_EQ_QB(uint64 instruction) +static char *CMPGU_EQ_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("CMPGU.EQ.QB %s, %s, %s", rd, rs, rt); + return img_format("CMPGU.EQ.QB %s, %s, %s", rd, rs, rt); } @@ -5027,17 +4627,17 @@ std::string NMD::CMPGU_EQ_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMPGU_LE_QB(uint64 instruction) +static char *CMPGU_LE_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("CMPGU.LE.QB %s, %s, %s", rd, rs, rt); + return img_format("CMPGU.LE.QB %s, %s, %s", rd, rs, rt); } @@ -5052,17 +4652,17 @@ std::string NMD::CMPGU_LE_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CMPGU_LT_QB(uint64 instruction) +static char *CMPGU_LT_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("CMPGU.LT.QB %s, %s, %s", rd, rs, rt); + return img_format("CMPGU.LT.QB %s, %s, %s", rd, rs, rt); } @@ -5076,15 +4676,15 @@ std::string NMD::CMPGU_LT_QB(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::CMPU_EQ_QB(uint64 instruction) +static char *CMPU_EQ_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("CMPU.EQ.QB %s, %s", rs, rt); + return img_format("CMPU.EQ.QB %s, %s", rs, rt); } @@ -5098,15 +4698,15 @@ std::string NMD::CMPU_EQ_QB(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::CMPU_LE_QB(uint64 instruction) +static char *CMPU_LE_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("CMPU.LE.QB %s, %s", rs, rt); + return img_format("CMPU.LE.QB %s, %s", rs, rt); } @@ -5120,15 +4720,15 @@ std::string NMD::CMPU_LE_QB(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::CMPU_LT_QB(uint64 instruction) +static char *CMPU_LT_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("CMPU.LT.QB %s, %s", rs, rt); + return img_format("CMPU.LT.QB %s, %s", rs, rt); } @@ -5142,13 +4742,12 @@ std::string NMD::CMPU_LT_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::COP2_1(uint64 instruction) +static char *COP2_1(uint64 instruction, Dis_info *info) { uint64 cofun_value = extract_cofun_25_24_23(instruction); - std::string cofun = IMMEDIATE(copy(cofun_value)); - return img::format("COP2_1 %s", cofun); + return img_format("COP2_1 0x%" PRIx64, cofun_value); } @@ -5162,15 +4761,14 @@ std::string NMD::COP2_1(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CTC1(uint64 instruction) +static char *CTC1(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 cs_value = extract_cs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string cs = CPR(copy(cs_value)); + const char *rt = GPR(rt_value, info); - return img::format("CTC1 %s, %s", rt, cs); + return img_format("CTC1 %s, CP%" PRIu64, rt, cs_value); } @@ -5184,15 +4782,14 @@ std::string NMD::CTC1(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CTC2(uint64 instruction) +static char *CTC2(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 cs_value = extract_cs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string cs = CPR(copy(cs_value)); + const char *rt = GPR(rt_value, info); - return img::format("CTC2 %s, %s", rt, cs); + return img_format("CTC2 %s, CP%" PRIu64, rt, cs_value); } @@ -5206,15 +4803,15 @@ std::string NMD::CTC2(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CVT_D_L(uint64 instruction) +static char *CVT_D_L(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CVT.D.L %s, %s", ft, fs); + return img_format("CVT.D.L %s, %s", ft, fs); } @@ -5228,15 +4825,15 @@ std::string NMD::CVT_D_L(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CVT_D_S(uint64 instruction) +static char *CVT_D_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CVT.D.S %s, %s", ft, fs); + return img_format("CVT.D.S %s, %s", ft, fs); } @@ -5250,15 +4847,15 @@ std::string NMD::CVT_D_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CVT_D_W(uint64 instruction) +static char *CVT_D_W(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CVT.D.W %s, %s", ft, fs); + return img_format("CVT.D.W %s, %s", ft, fs); } @@ -5272,15 +4869,15 @@ std::string NMD::CVT_D_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CVT_L_D(uint64 instruction) +static char *CVT_L_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CVT.L.D %s, %s", ft, fs); + return img_format("CVT.L.D %s, %s", ft, fs); } @@ -5294,15 +4891,15 @@ std::string NMD::CVT_L_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CVT_L_S(uint64 instruction) +static char *CVT_L_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CVT.L.S %s, %s", ft, fs); + return img_format("CVT.L.S %s, %s", ft, fs); } @@ -5316,15 +4913,15 @@ std::string NMD::CVT_L_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CVT_S_D(uint64 instruction) +static char *CVT_S_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CVT.S.D %s, %s", ft, fs); + return img_format("CVT.S.D %s, %s", ft, fs); } @@ -5338,15 +4935,15 @@ std::string NMD::CVT_S_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CVT_S_L(uint64 instruction) +static char *CVT_S_L(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CVT.S.L %s, %s", ft, fs); + return img_format("CVT.S.L %s, %s", ft, fs); } @@ -5360,15 +4957,15 @@ std::string NMD::CVT_S_L(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CVT_S_PL(uint64 instruction) +static char *CVT_S_PL(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CVT.S.PL %s, %s", ft, fs); + return img_format("CVT.S.PL %s, %s", ft, fs); } @@ -5382,15 +4979,15 @@ std::string NMD::CVT_S_PL(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CVT_S_PU(uint64 instruction) +static char *CVT_S_PU(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CVT.S.PU %s, %s", ft, fs); + return img_format("CVT.S.PU %s, %s", ft, fs); } @@ -5404,15 +5001,15 @@ std::string NMD::CVT_S_PU(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CVT_S_W(uint64 instruction) +static char *CVT_S_W(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CVT.S.W %s, %s", ft, fs); + return img_format("CVT.S.W %s, %s", ft, fs); } @@ -5426,15 +5023,15 @@ std::string NMD::CVT_S_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CVT_W_D(uint64 instruction) +static char *CVT_W_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CVT.W.D %s, %s", ft, fs); + return img_format("CVT.W.D %s, %s", ft, fs); } @@ -5448,15 +5045,15 @@ std::string NMD::CVT_W_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::CVT_W_S(uint64 instruction) +static char *CVT_W_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("CVT.W.S %s, %s", ft, fs); + return img_format("CVT.W.S %s, %s", ft, fs); } @@ -5470,15 +5067,14 @@ std::string NMD::CVT_W_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DADDIU_48_(uint64 instruction) +static char *DADDIU_48_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_41_40_39_38_37(instruction); int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); + const char *rt = GPR(rt_value, info); - return img::format("DADDIU %s, %s", rt, s); + return img_format("DADDIU %s, %" PRId64, rt, s_value); } @@ -5492,17 +5088,17 @@ std::string NMD::DADDIU_48_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DADDIU_NEG_(uint64 instruction) +static char *DADDIU_NEG_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string u = IMMEDIATE(neg_copy(u_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); + int64 u = neg_copy(u_value); - return img::format("DADDIU %s, %s, %s", rt, rs, u); + return img_format("DADDIU %s, %s, %" PRId64, rt, rs, u); } @@ -5516,17 +5112,16 @@ std::string NMD::DADDIU_NEG_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DADDIU_U12_(uint64 instruction) +static char *DADDIU_U12_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("DADDIU %s, %s, %s", rt, rs, u); + return img_format("DADDIU %s, %s, 0x%" PRIx64, rt, rs, u_value); } @@ -5540,17 +5135,17 @@ std::string NMD::DADDIU_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DADD(uint64 instruction) +static char *DADD(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DADD %s, %s, %s", rd, rs, rt); + return img_format("DADD %s, %s, %s", rd, rs, rt); } @@ -5564,17 +5159,17 @@ std::string NMD::DADD(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DADDU(uint64 instruction) +static char *DADDU(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DADDU %s, %s, %s", rd, rs, rt); + return img_format("DADDU %s, %s, %s", rd, rs, rt); } @@ -5588,15 +5183,15 @@ std::string NMD::DADDU(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DCLO(uint64 instruction) +static char *DCLO(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("DCLO %s, %s", rt, rs); + return img_format("DCLO %s, %s", rt, rs); } @@ -5610,15 +5205,15 @@ std::string NMD::DCLO(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DCLZ(uint64 instruction) +static char *DCLZ(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("DCLZ %s, %s", rt, rs); + return img_format("DCLZ %s, %s", rt, rs); } @@ -5632,17 +5227,17 @@ std::string NMD::DCLZ(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DDIV(uint64 instruction) +static char *DDIV(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DDIV %s, %s, %s", rd, rs, rt); + return img_format("DDIV %s, %s, %s", rd, rs, rt); } @@ -5656,17 +5251,17 @@ std::string NMD::DDIV(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DDIVU(uint64 instruction) +static char *DDIVU(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DDIVU %s, %s, %s", rd, rs, rt); + return img_format("DDIVU %s, %s, %s", rd, rs, rt); } @@ -5680,11 +5275,11 @@ std::string NMD::DDIVU(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DERET(uint64 instruction) +static char *DERET(uint64 instruction, Dis_info *info) { (void)instruction; - return "DERET "; + return g_strdup("DERET "); } @@ -5698,19 +5293,19 @@ std::string NMD::DERET(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DEXTM(uint64 instruction) +static char *DEXTM(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string lsb = IMMEDIATE(copy(lsb_value)); - std::string msbd = IMMEDIATE(encode_msbd_from_size(msbd_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); + uint64 msbd = encode_msbd_from_size(msbd_value); - return img::format("DEXTM %s, %s, %s, %s", rt, rs, lsb, msbd); + return img_format("DEXTM %s, %s, 0x%" PRIx64 ", 0x%" PRIx64, + rt, rs, lsb_value, msbd); } @@ -5724,19 +5319,19 @@ std::string NMD::DEXTM(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DEXT(uint64 instruction) +static char *DEXT(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string lsb = IMMEDIATE(copy(lsb_value)); - std::string msbd = IMMEDIATE(encode_msbd_from_size(msbd_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); + uint64 msbd = encode_msbd_from_size(msbd_value); - return img::format("DEXT %s, %s, %s, %s", rt, rs, lsb, msbd); + return img_format("DEXT %s, %s, 0x%" PRIx64 ", 0x%" PRIx64, + rt, rs, lsb_value, msbd); } @@ -5750,19 +5345,19 @@ std::string NMD::DEXT(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DEXTU(uint64 instruction) +static char *DEXTU(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string lsb = IMMEDIATE(copy(lsb_value)); - std::string msbd = IMMEDIATE(encode_msbd_from_size(msbd_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); + uint64 msbd = encode_msbd_from_size(msbd_value); - return img::format("DEXTU %s, %s, %s, %s", rt, rs, lsb, msbd); + return img_format("DEXTU %s, %s, 0x%" PRIx64 ", 0x%" PRIx64, + rt, rs, lsb_value, msbd); } @@ -5776,20 +5371,19 @@ std::string NMD::DEXTU(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DINSM(uint64 instruction) +static char *DINSM(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string pos = IMMEDIATE(encode_lsb_from_pos_and_size(lsb_value)); - std::string size = IMMEDIATE(encode_lsb_from_pos_and_size(msbd_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); /* !!!!!!!!!! - no conversion function */ - return img::format("DINSM %s, %s, %s, %s", rt, rs, pos, size); + return img_format("DINSM %s, %s, 0x%" PRIx64 ", 0x%" PRIx64, + rt, rs, lsb_value, msbd_value); /* hand edited */ } @@ -5804,20 +5398,19 @@ std::string NMD::DINSM(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DINS(uint64 instruction) +static char *DINS(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string pos = IMMEDIATE(encode_lsb_from_pos_and_size(lsb_value)); - std::string size = IMMEDIATE(encode_lsb_from_pos_and_size(msbd_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); /* !!!!!!!!!! - no conversion function */ - return img::format("DINS %s, %s, %s, %s", rt, rs, pos, size); + return img_format("DINS %s, %s, 0x%" PRIx64 ", 0x%" PRIx64, + rt, rs, lsb_value, msbd_value); /* hand edited */ } @@ -5832,20 +5425,19 @@ std::string NMD::DINS(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DINSU(uint64 instruction) +static char *DINSU(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string pos = IMMEDIATE(encode_lsb_from_pos_and_size(lsb_value)); - std::string size = IMMEDIATE(encode_lsb_from_pos_and_size(msbd_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); /* !!!!!!!!!! - no conversion function */ - return img::format("DINSU %s, %s, %s, %s", rt, rs, pos, size); + return img_format("DINSU %s, %s, 0x%" PRIx64 ", 0x%" PRIx64, + rt, rs, lsb_value, msbd_value); /* hand edited */ } @@ -5860,13 +5452,13 @@ std::string NMD::DINSU(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DI(uint64 instruction) +static char *DI(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); - std::string rt = GPR(copy(rt_value)); + const char *rt = GPR(rt_value, info); - return img::format("DI %s", rt); + return img_format("DI %s", rt); } @@ -5880,17 +5472,17 @@ std::string NMD::DI(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DIV(uint64 instruction) +static char *DIV(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DIV %s, %s, %s", rd, rs, rt); + return img_format("DIV %s, %s, %s", rd, rs, rt); } @@ -5904,17 +5496,17 @@ std::string NMD::DIV(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DIV_D(uint64 instruction) +static char *DIV_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("DIV.D %s, %s, %s", fd, fs, ft); + return img_format("DIV.D %s, %s, %s", fd, fs, ft); } @@ -5928,17 +5520,17 @@ std::string NMD::DIV_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DIV_S(uint64 instruction) +static char *DIV_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("DIV.S %s, %s, %s", fd, fs, ft); + return img_format("DIV.S %s, %s, %s", fd, fs, ft); } @@ -5952,17 +5544,17 @@ std::string NMD::DIV_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DIVU(uint64 instruction) +static char *DIVU(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DIVU %s, %s, %s", rd, rs, rt); + return img_format("DIVU %s, %s, %s", rd, rs, rt); } @@ -5976,19 +5568,18 @@ std::string NMD::DIVU(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DLSA(uint64 instruction) +static char *DLSA(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); uint64 u2_value = extract_u2_10_9(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); - std::string u2 = IMMEDIATE(copy(u2_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DLSA %s, %s, %s, %s", rd, rs, rt, u2); + return img_format("DLSA %s, %s, %s, 0x%" PRIx64, rd, rs, rt, u2_value); } @@ -6002,15 +5593,14 @@ std::string NMD::DLSA(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DLUI_48_(uint64 instruction) +static char *DLUI_48_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_41_40_39_38_37(instruction); uint64 u_value = extract_u_31_to_0__s32(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("DLUI %s, %s", rt, u); + return img_format("DLUI %s, 0x%" PRIx64, rt, u_value); } @@ -6024,17 +5614,16 @@ std::string NMD::DLUI_48_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMFC0(uint64 instruction) +static char *DMFC0(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = CPR(copy(c0s_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("DMFC0 %s, %s, %s", rt, c0s, sel); + return img_format("DMFC0 %s, CP%" PRIu64 ", 0x%" PRIx64, + rt, c0s_value, sel_value); } @@ -6048,15 +5637,15 @@ std::string NMD::DMFC0(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMFC1(uint64 instruction) +static char *DMFC1(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string fs = FPR(copy(fs_value)); + const char *rt = GPR(rt_value, info); + const char *fs = FPR(fs_value, info); - return img::format("DMFC1 %s, %s", rt, fs); + return img_format("DMFC1 %s, %s", rt, fs); } @@ -6070,15 +5659,14 @@ std::string NMD::DMFC1(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMFC2(uint64 instruction) +static char *DMFC2(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 cs_value = extract_cs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string cs = CPR(copy(cs_value)); + const char *rt = GPR(rt_value, info); - return img::format("DMFC2 %s, %s", rt, cs); + return img_format("DMFC2 %s, CP%" PRIu64, rt, cs_value); } @@ -6092,17 +5680,16 @@ std::string NMD::DMFC2(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMFGC0(uint64 instruction) +static char *DMFGC0(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = CPR(copy(c0s_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("DMFGC0 %s, %s, %s", rt, c0s, sel); + return img_format("DMFGC0 %s, CP%" PRIu64 ", 0x%" PRIx64, + rt, c0s_value, sel_value); } @@ -6116,17 +5703,17 @@ std::string NMD::DMFGC0(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMOD(uint64 instruction) +static char *DMOD(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DMOD %s, %s, %s", rd, rs, rt); + return img_format("DMOD %s, %s, %s", rd, rs, rt); } @@ -6140,17 +5727,17 @@ std::string NMD::DMOD(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMODU(uint64 instruction) +static char *DMODU(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DMODU %s, %s, %s", rd, rs, rt); + return img_format("DMODU %s, %s, %s", rd, rs, rt); } @@ -6164,17 +5751,16 @@ std::string NMD::DMODU(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMTC0(uint64 instruction) +static char *DMTC0(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = CPR(copy(c0s_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("DMTC0 %s, %s, %s", rt, c0s, sel); + return img_format("DMTC0 %s, CP%" PRIu64 ", 0x%" PRIx64, + rt, c0s_value, sel_value); } @@ -6188,15 +5774,15 @@ std::string NMD::DMTC0(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMTC1(uint64 instruction) +static char *DMTC1(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string fs = FPR(copy(fs_value)); + const char *rt = GPR(rt_value, info); + const char *fs = FPR(fs_value, info); - return img::format("DMTC1 %s, %s", rt, fs); + return img_format("DMTC1 %s, %s", rt, fs); } @@ -6210,15 +5796,14 @@ std::string NMD::DMTC1(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMTC2(uint64 instruction) +static char *DMTC2(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 cs_value = extract_cs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string cs = CPR(copy(cs_value)); + const char *rt = GPR(rt_value, info); - return img::format("DMTC2 %s, %s", rt, cs); + return img_format("DMTC2 %s, CP%" PRIu64, rt, cs_value); } @@ -6232,17 +5817,16 @@ std::string NMD::DMTC2(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMTGC0(uint64 instruction) +static char *DMTGC0(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = CPR(copy(c0s_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("DMTGC0 %s, %s, %s", rt, c0s, sel); + return img_format("DMTGC0 %s, CP%" PRIu64 ", 0x%" PRIx64, + rt, c0s_value, sel_value); } @@ -6256,13 +5840,13 @@ std::string NMD::DMTGC0(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMT(uint64 instruction) +static char *DMT(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); - std::string rt = GPR(copy(rt_value)); + const char *rt = GPR(rt_value, info); - return img::format("DMT %s", rt); + return img_format("DMT %s", rt); } @@ -6276,17 +5860,17 @@ std::string NMD::DMT(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMUH(uint64 instruction) +static char *DMUH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DMUH %s, %s, %s", rd, rs, rt); + return img_format("DMUH %s, %s, %s", rd, rs, rt); } @@ -6300,17 +5884,17 @@ std::string NMD::DMUH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMUHU(uint64 instruction) +static char *DMUHU(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DMUHU %s, %s, %s", rd, rs, rt); + return img_format("DMUHU %s, %s, %s", rd, rs, rt); } @@ -6324,17 +5908,17 @@ std::string NMD::DMUHU(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMUL(uint64 instruction) +static char *DMUL(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DMUL %s, %s, %s", rd, rs, rt); + return img_format("DMUL %s, %s, %s", rd, rs, rt); } @@ -6348,17 +5932,17 @@ std::string NMD::DMUL(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DMULU(uint64 instruction) +static char *DMULU(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DMULU %s, %s, %s", rd, rs, rt); + return img_format("DMULU %s, %s, %s", rd, rs, rt); } @@ -6373,17 +5957,17 @@ std::string NMD::DMULU(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::DPA_W_PH(uint64 instruction) +static char *DPA_W_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPA.W.PH %s, %s, %s", ac, rs, rt); + return img_format("DPA.W.PH %s, %s, %s", ac, rs, rt); } @@ -6397,17 +5981,17 @@ std::string NMD::DPA_W_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPAQ_SA_L_W(uint64 instruction) +static char *DPAQ_SA_L_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPAQ_SA.L.W %s, %s, %s", ac, rs, rt); + return img_format("DPAQ_SA.L.W %s, %s, %s", ac, rs, rt); } @@ -6421,17 +6005,17 @@ std::string NMD::DPAQ_SA_L_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPAQ_S_W_PH(uint64 instruction) +static char *DPAQ_S_W_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPAQ_S.W.PH %s, %s, %s", ac, rs, rt); + return img_format("DPAQ_S.W.PH %s, %s, %s", ac, rs, rt); } @@ -6445,17 +6029,17 @@ std::string NMD::DPAQ_S_W_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPAQX_SA_W_PH(uint64 instruction) +static char *DPAQX_SA_W_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPAQX_SA.W.PH %s, %s, %s", ac, rs, rt); + return img_format("DPAQX_SA.W.PH %s, %s, %s", ac, rs, rt); } @@ -6469,17 +6053,17 @@ std::string NMD::DPAQX_SA_W_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPAQX_S_W_PH(uint64 instruction) +static char *DPAQX_S_W_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPAQX_S.W.PH %s, %s, %s", ac, rs, rt); + return img_format("DPAQX_S.W.PH %s, %s, %s", ac, rs, rt); } @@ -6493,17 +6077,17 @@ std::string NMD::DPAQX_S_W_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPAU_H_QBL(uint64 instruction) +static char *DPAU_H_QBL(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPAU.H.QBL %s, %s, %s", ac, rs, rt); + return img_format("DPAU.H.QBL %s, %s, %s", ac, rs, rt); } @@ -6517,17 +6101,17 @@ std::string NMD::DPAU_H_QBL(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPAU_H_QBR(uint64 instruction) +static char *DPAU_H_QBR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPAU.H.QBR %s, %s, %s", ac, rs, rt); + return img_format("DPAU.H.QBR %s, %s, %s", ac, rs, rt); } @@ -6541,17 +6125,17 @@ std::string NMD::DPAU_H_QBR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPAX_W_PH(uint64 instruction) +static char *DPAX_W_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPAX.W.PH %s, %s, %s", ac, rs, rt); + return img_format("DPAX.W.PH %s, %s, %s", ac, rs, rt); } @@ -6565,17 +6149,17 @@ std::string NMD::DPAX_W_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPS_W_PH(uint64 instruction) +static char *DPS_W_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPS.W.PH %s, %s, %s", ac, rs, rt); + return img_format("DPS.W.PH %s, %s, %s", ac, rs, rt); } @@ -6589,17 +6173,17 @@ std::string NMD::DPS_W_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPSQ_SA_L_W(uint64 instruction) +static char *DPSQ_SA_L_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPSQ_SA.L.W %s, %s, %s", ac, rs, rt); + return img_format("DPSQ_SA.L.W %s, %s, %s", ac, rs, rt); } @@ -6613,17 +6197,17 @@ std::string NMD::DPSQ_SA_L_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPSQ_S_W_PH(uint64 instruction) +static char *DPSQ_S_W_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPSQ_S.W.PH %s, %s, %s", ac, rs, rt); + return img_format("DPSQ_S.W.PH %s, %s, %s", ac, rs, rt); } @@ -6637,17 +6221,17 @@ std::string NMD::DPSQ_S_W_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPSQX_SA_W_PH(uint64 instruction) +static char *DPSQX_SA_W_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPSQX_SA.W.PH %s, %s, %s", ac, rs, rt); + return img_format("DPSQX_SA.W.PH %s, %s, %s", ac, rs, rt); } @@ -6661,17 +6245,17 @@ std::string NMD::DPSQX_SA_W_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPSQX_S_W_PH(uint64 instruction) +static char *DPSQX_S_W_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPSQX_S.W.PH %s, %s, %s", ac, rs, rt); + return img_format("DPSQX_S.W.PH %s, %s, %s", ac, rs, rt); } @@ -6685,17 +6269,17 @@ std::string NMD::DPSQX_S_W_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPSU_H_QBL(uint64 instruction) +static char *DPSU_H_QBL(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPSU.H.QBL %s, %s, %s", ac, rs, rt); + return img_format("DPSU.H.QBL %s, %s, %s", ac, rs, rt); } @@ -6709,17 +6293,17 @@ std::string NMD::DPSU_H_QBL(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPSU_H_QBR(uint64 instruction) +static char *DPSU_H_QBR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPSU.H.QBR %s, %s, %s", ac, rs, rt); + return img_format("DPSU.H.QBR %s, %s, %s", ac, rs, rt); } @@ -6733,17 +6317,17 @@ std::string NMD::DPSU_H_QBR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DPSX_W_PH(uint64 instruction) +static char *DPSX_W_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DPSX.W.PH %s, %s, %s", ac, rs, rt); + return img_format("DPSX.W.PH %s, %s, %s", ac, rs, rt); } @@ -6757,17 +6341,16 @@ std::string NMD::DPSX_W_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DROTR(uint64 instruction) +static char *DROTR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 shift_value = extract_shift_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("DROTR %s, %s, %s", rt, rs, shift); + return img_format("DROTR %s, %s, 0x%" PRIx64, rt, rs, shift_value); } @@ -6781,17 +6364,16 @@ std::string NMD::DROTR(uint64 instruction) * rs ----- * shift ----- */ -std::string NMD::DROTR32(uint64 instruction) +static char *DROTR32(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 shift_value = extract_shift_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("DROTR32 %s, %s, %s", rt, rs, shift); + return img_format("DROTR32 %s, %s, 0x%" PRIx64, rt, rs, shift_value); } @@ -6805,17 +6387,17 @@ std::string NMD::DROTR32(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DROTRV(uint64 instruction) +static char *DROTRV(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DROTRV %s, %s, %s", rd, rs, rt); + return img_format("DROTRV %s, %s, %s", rd, rs, rt); } @@ -6829,19 +6411,18 @@ std::string NMD::DROTRV(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DROTX(uint64 instruction) +static char *DROTX(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 shiftx_value = extract_shiftx_11_10_9_8_7_6(instruction); uint64 shift_value = extract_shift_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string shift = IMMEDIATE(copy(shift_value)); - std::string shiftx = IMMEDIATE(copy(shiftx_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("DROTX %s, %s, %s, %s", rt, rs, shift, shiftx); + return img_format("DROTX %s, %s, 0x%" PRIx64 ", 0x%" PRIx64, + rt, rs, shift_value, shiftx_value); } @@ -6855,17 +6436,16 @@ std::string NMD::DROTX(uint64 instruction) * rs ----- * shift ----- */ -std::string NMD::DSLL(uint64 instruction) +static char *DSLL(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 shift_value = extract_shift_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("DSLL %s, %s, %s", rt, rs, shift); + return img_format("DSLL %s, %s, 0x%" PRIx64, rt, rs, shift_value); } @@ -6879,17 +6459,16 @@ std::string NMD::DSLL(uint64 instruction) * rs ----- * shift ----- */ -std::string NMD::DSLL32(uint64 instruction) +static char *DSLL32(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 shift_value = extract_shift_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("DSLL32 %s, %s, %s", rt, rs, shift); + return img_format("DSLL32 %s, %s, 0x%" PRIx64, rt, rs, shift_value); } @@ -6903,17 +6482,17 @@ std::string NMD::DSLL32(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DSLLV(uint64 instruction) +static char *DSLLV(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DSLLV %s, %s, %s", rd, rs, rt); + return img_format("DSLLV %s, %s, %s", rd, rs, rt); } @@ -6927,17 +6506,16 @@ std::string NMD::DSLLV(uint64 instruction) * rs ----- * shift ----- */ -std::string NMD::DSRA(uint64 instruction) +static char *DSRA(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 shift_value = extract_shift_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("DSRA %s, %s, %s", rt, rs, shift); + return img_format("DSRA %s, %s, 0x%" PRIx64, rt, rs, shift_value); } @@ -6951,17 +6529,16 @@ std::string NMD::DSRA(uint64 instruction) * rs ----- * shift ----- */ -std::string NMD::DSRA32(uint64 instruction) +static char *DSRA32(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 shift_value = extract_shift_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("DSRA32 %s, %s, %s", rt, rs, shift); + return img_format("DSRA32 %s, %s, 0x%" PRIx64, rt, rs, shift_value); } @@ -6975,17 +6552,17 @@ std::string NMD::DSRA32(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DSRAV(uint64 instruction) +static char *DSRAV(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DSRAV %s, %s, %s", rd, rs, rt); + return img_format("DSRAV %s, %s, %s", rd, rs, rt); } @@ -6999,17 +6576,16 @@ std::string NMD::DSRAV(uint64 instruction) * rs ----- * shift ----- */ -std::string NMD::DSRL(uint64 instruction) +static char *DSRL(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 shift_value = extract_shift_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("DSRL %s, %s, %s", rt, rs, shift); + return img_format("DSRL %s, %s, 0x%" PRIx64, rt, rs, shift_value); } @@ -7023,17 +6599,16 @@ std::string NMD::DSRL(uint64 instruction) * rs ----- * shift ----- */ -std::string NMD::DSRL32(uint64 instruction) +static char *DSRL32(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 shift_value = extract_shift_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("DSRL32 %s, %s, %s", rt, rs, shift); + return img_format("DSRL32 %s, %s, 0x%" PRIx64, rt, rs, shift_value); } @@ -7047,17 +6622,17 @@ std::string NMD::DSRL32(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DSRLV(uint64 instruction) +static char *DSRLV(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DSRLV %s, %s, %s", rd, rs, rt); + return img_format("DSRLV %s, %s, %s", rd, rs, rt); } @@ -7071,17 +6646,17 @@ std::string NMD::DSRLV(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DSUB(uint64 instruction) +static char *DSUB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DSUB %s, %s, %s", rd, rs, rt); + return img_format("DSUB %s, %s, %s", rd, rs, rt); } @@ -7095,17 +6670,17 @@ std::string NMD::DSUB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DSUBU(uint64 instruction) +static char *DSUBU(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("DSUBU %s, %s, %s", rd, rs, rt); + return img_format("DSUBU %s, %s, %s", rd, rs, rt); } @@ -7119,13 +6694,13 @@ std::string NMD::DSUBU(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DVPE(uint64 instruction) +static char *DVPE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); - std::string rt = GPR(copy(rt_value)); + const char *rt = GPR(rt_value, info); - return img::format("DVPE %s", rt); + return img_format("DVPE %s", rt); } @@ -7139,13 +6714,13 @@ std::string NMD::DVPE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::DVP(uint64 instruction) +static char *DVP(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); - std::string rt = GPR(copy(rt_value)); + const char *rt = GPR(rt_value, info); - return img::format("DVP %s", rt); + return img_format("DVP %s", rt); } @@ -7159,11 +6734,11 @@ std::string NMD::DVP(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::EHB(uint64 instruction) +static char *EHB(uint64 instruction, Dis_info *info) { (void)instruction; - return "EHB "; + return g_strdup("EHB "); } @@ -7177,13 +6752,13 @@ std::string NMD::EHB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::EI(uint64 instruction) +static char *EI(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); - std::string rt = GPR(copy(rt_value)); + const char *rt = GPR(rt_value, info); - return img::format("EI %s", rt); + return img_format("EI %s", rt); } @@ -7197,13 +6772,13 @@ std::string NMD::EI(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::EMT(uint64 instruction) +static char *EMT(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); - std::string rt = GPR(copy(rt_value)); + const char *rt = GPR(rt_value, info); - return img::format("EMT %s", rt); + return img_format("EMT %s", rt); } @@ -7217,11 +6792,11 @@ std::string NMD::EMT(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ERET(uint64 instruction) +static char *ERET(uint64 instruction, Dis_info *info) { (void)instruction; - return "ERET "; + return g_strdup("ERET "); } @@ -7235,11 +6810,11 @@ std::string NMD::ERET(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ERETNC(uint64 instruction) +static char *ERETNC(uint64 instruction, Dis_info *info) { (void)instruction; - return "ERETNC "; + return g_strdup("ERETNC "); } @@ -7253,13 +6828,13 @@ std::string NMD::ERETNC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::EVP(uint64 instruction) +static char *EVP(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); - std::string rt = GPR(copy(rt_value)); + const char *rt = GPR(rt_value, info); - return img::format("EVP %s", rt); + return img_format("EVP %s", rt); } @@ -7273,13 +6848,13 @@ std::string NMD::EVP(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::EVPE(uint64 instruction) +static char *EVPE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); - std::string rt = GPR(copy(rt_value)); + const char *rt = GPR(rt_value, info); - return img::format("EVPE %s", rt); + return img_format("EVPE %s", rt); } @@ -7293,19 +6868,19 @@ std::string NMD::EVPE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::EXT(uint64 instruction) +static char *EXT(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string lsb = IMMEDIATE(copy(lsb_value)); - std::string msbd = IMMEDIATE(encode_msbd_from_size(msbd_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); + uint64 msbd = encode_msbd_from_size(msbd_value); - return img::format("EXT %s, %s, %s, %s", rt, rs, lsb, msbd); + return img_format("EXT %s, %s, 0x%" PRIx64 ", 0x%" PRIx64, + rt, rs, lsb_value, msbd); } @@ -7319,19 +6894,18 @@ std::string NMD::EXT(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::EXTD(uint64 instruction) +static char *EXTD(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); uint64 shift_value = extract_shift_10_9_8_7_6(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("EXTD %s, %s, %s, %s", rd, rs, rt, shift); + return img_format("EXTD %s, %s, %s, 0x%" PRIx64, rd, rs, rt, shift_value); } @@ -7345,19 +6919,18 @@ std::string NMD::EXTD(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::EXTD32(uint64 instruction) +static char *EXTD32(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); uint64 shift_value = extract_shift_10_9_8_7_6(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("EXTD32 %s, %s, %s, %s", rd, rs, rt, shift); + return img_format("EXTD32 %s, %s, %s, 0x%" PRIx64, rd, rs, rt, shift_value); } @@ -7371,17 +6944,16 @@ std::string NMD::EXTD32(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::EXTPDP(uint64 instruction) +static char *EXTPDP(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 size_value = extract_size_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ac = AC(copy(ac_value)); - std::string size = IMMEDIATE(copy(size_value)); + const char *rt = GPR(rt_value, info); + const char *ac = AC(ac_value, info); - return img::format("EXTPDP %s, %s, %s", rt, ac, size); + return img_format("EXTPDP %s, %s, 0x%" PRIx64, rt, ac, size_value); } @@ -7395,17 +6967,17 @@ std::string NMD::EXTPDP(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::EXTPDPV(uint64 instruction) +static char *EXTPDPV(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); - return img::format("EXTPDPV %s, %s, %s", rt, ac, rs); + return img_format("EXTPDPV %s, %s, %s", rt, ac, rs); } @@ -7419,17 +6991,16 @@ std::string NMD::EXTPDPV(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::EXTP(uint64 instruction) +static char *EXTP(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 size_value = extract_size_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ac = AC(copy(ac_value)); - std::string size = IMMEDIATE(copy(size_value)); + const char *rt = GPR(rt_value, info); + const char *ac = AC(ac_value, info); - return img::format("EXTP %s, %s, %s", rt, ac, size); + return img_format("EXTP %s, %s, 0x%" PRIx64, rt, ac, size_value); } @@ -7443,17 +7014,17 @@ std::string NMD::EXTP(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::EXTPV(uint64 instruction) +static char *EXTPV(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); - return img::format("EXTPV %s, %s, %s", rt, ac, rs); + return img_format("EXTPV %s, %s, %s", rt, ac, rs); } @@ -7468,17 +7039,16 @@ std::string NMD::EXTPV(uint64 instruction) * shift ----- * ac -- */ -std::string NMD::EXTR_RS_W(uint64 instruction) +static char *EXTR_RS_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 shift_value = extract_shift_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ac = AC(copy(ac_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *ac = AC(ac_value, info); - return img::format("EXTR_RS.W %s, %s, %s", rt, ac, shift); + return img_format("EXTR_RS.W %s, %s, 0x%" PRIx64, rt, ac, shift_value); } @@ -7493,17 +7063,16 @@ std::string NMD::EXTR_RS_W(uint64 instruction) * shift ----- * ac -- */ -std::string NMD::EXTR_R_W(uint64 instruction) +static char *EXTR_R_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 shift_value = extract_shift_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ac = AC(copy(ac_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *ac = AC(ac_value, info); - return img::format("EXTR_R.W %s, %s, %s", rt, ac, shift); + return img_format("EXTR_R.W %s, %s, 0x%" PRIx64, rt, ac, shift_value); } @@ -7518,17 +7087,16 @@ std::string NMD::EXTR_R_W(uint64 instruction) * shift ----- * ac -- */ -std::string NMD::EXTR_S_H(uint64 instruction) +static char *EXTR_S_H(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 shift_value = extract_shift_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ac = AC(copy(ac_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *ac = AC(ac_value, info); - return img::format("EXTR_S.H %s, %s, %s", rt, ac, shift); + return img_format("EXTR_S.H %s, %s, 0x%" PRIx64, rt, ac, shift_value); } @@ -7543,17 +7111,16 @@ std::string NMD::EXTR_S_H(uint64 instruction) * shift ----- * ac -- */ -std::string NMD::EXTR_W(uint64 instruction) +static char *EXTR_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 shift_value = extract_shift_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ac = AC(copy(ac_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *ac = AC(ac_value, info); - return img::format("EXTR.W %s, %s, %s", rt, ac, shift); + return img_format("EXTR.W %s, %s, 0x%" PRIx64, rt, ac, shift_value); } @@ -7568,17 +7135,17 @@ std::string NMD::EXTR_W(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::EXTRV_RS_W(uint64 instruction) +static char *EXTRV_RS_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); - return img::format("EXTRV_RS.W %s, %s, %s", rt, ac, rs); + return img_format("EXTRV_RS.W %s, %s, %s", rt, ac, rs); } @@ -7593,17 +7160,17 @@ std::string NMD::EXTRV_RS_W(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::EXTRV_R_W(uint64 instruction) +static char *EXTRV_R_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); - return img::format("EXTRV_R.W %s, %s, %s", rt, ac, rs); + return img_format("EXTRV_R.W %s, %s, %s", rt, ac, rs); } @@ -7618,17 +7185,17 @@ std::string NMD::EXTRV_R_W(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::EXTRV_S_H(uint64 instruction) +static char *EXTRV_S_H(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); - return img::format("EXTRV_S.H %s, %s, %s", rt, ac, rs); + return img_format("EXTRV_S.H %s, %s, %s", rt, ac, rs); } @@ -7643,17 +7210,17 @@ std::string NMD::EXTRV_S_H(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::EXTRV_W(uint64 instruction) +static char *EXTRV_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); - return img::format("EXTRV.W %s, %s, %s", rt, ac, rs); + return img_format("EXTRV.W %s, %s, %s", rt, ac, rs); } @@ -7668,19 +7235,18 @@ std::string NMD::EXTRV_W(uint64 instruction) * rd ----- * shift ----- */ -std::string NMD::EXTW(uint64 instruction) +static char *EXTW(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); uint64 shift_value = extract_shift_10_9_8_7_6(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("EXTW %s, %s, %s, %s", rd, rs, rt, shift); + return img_format("EXTW %s, %s, %s, 0x%" PRIx64, rd, rs, rt, shift_value); } @@ -7694,15 +7260,15 @@ std::string NMD::EXTW(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::FLOOR_L_D(uint64 instruction) +static char *FLOOR_L_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("FLOOR.L.D %s, %s", ft, fs); + return img_format("FLOOR.L.D %s, %s", ft, fs); } @@ -7716,15 +7282,15 @@ std::string NMD::FLOOR_L_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::FLOOR_L_S(uint64 instruction) +static char *FLOOR_L_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("FLOOR.L.S %s, %s", ft, fs); + return img_format("FLOOR.L.S %s, %s", ft, fs); } @@ -7738,15 +7304,15 @@ std::string NMD::FLOOR_L_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::FLOOR_W_D(uint64 instruction) +static char *FLOOR_W_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("FLOOR.W.D %s, %s", ft, fs); + return img_format("FLOOR.W.D %s, %s", ft, fs); } @@ -7760,15 +7326,15 @@ std::string NMD::FLOOR_W_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::FLOOR_W_S(uint64 instruction) +static char *FLOOR_W_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("FLOOR.W.S %s, %s", ft, fs); + return img_format("FLOOR.W.S %s, %s", ft, fs); } @@ -7782,17 +7348,17 @@ std::string NMD::FLOOR_W_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::FORK(uint64 instruction) +static char *FORK(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("FORK %s, %s, %s", rd, rs, rt); + return img_format("FORK %s, %s, %s", rd, rs, rt); } @@ -7806,13 +7372,12 @@ std::string NMD::FORK(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::HYPCALL(uint64 instruction) +static char *HYPCALL(uint64 instruction, Dis_info *info) { uint64 code_value = extract_code_17_to_0(instruction); - std::string code = IMMEDIATE(copy(code_value)); - return img::format("HYPCALL %s", code); + return img_format("HYPCALL 0x%" PRIx64, code_value); } @@ -7826,13 +7391,12 @@ std::string NMD::HYPCALL(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::HYPCALL_16_(uint64 instruction) +static char *HYPCALL_16_(uint64 instruction, Dis_info *info) { uint64 code_value = extract_code_1_0(instruction); - std::string code = IMMEDIATE(copy(code_value)); - return img::format("HYPCALL %s", code); + return img_format("HYPCALL 0x%" PRIx64, code_value); } @@ -7846,20 +7410,19 @@ std::string NMD::HYPCALL_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::INS(uint64 instruction) +static char *INS(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string pos = IMMEDIATE(encode_lsb_from_pos_and_size(lsb_value)); - std::string size = IMMEDIATE(encode_lsb_from_pos_and_size(msbd_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); /* !!!!!!!!!! - no conversion function */ - return img::format("INS %s, %s, %s, %s", rt, rs, pos, size); + return img_format("INS %s, %s, 0x%" PRIx64 ", 0x%" PRIx64, + rt, rs, lsb_value, msbd_value); /* hand edited */ } @@ -7873,15 +7436,15 @@ std::string NMD::INS(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::INSV(uint64 instruction) +static char *INSV(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("INSV %s, %s", rt, rs); + return img_format("INSV %s, %s", rt, rs); } @@ -7895,11 +7458,11 @@ std::string NMD::INSV(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::IRET(uint64 instruction) +static char *IRET(uint64 instruction, Dis_info *info) { (void)instruction; - return "IRET "; + return g_strdup("IRET "); } @@ -7913,13 +7476,13 @@ std::string NMD::IRET(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::JALRC_16_(uint64 instruction) +static char *JALRC_16_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_9_8_7_6_5(instruction); - std::string rt = GPR(copy(rt_value)); + const char *rt = GPR(rt_value, info); - return img::format("JALRC $%d, %s", 31, rt); + return img_format("JALRC $%d, %s", 31, rt); } @@ -7933,15 +7496,15 @@ std::string NMD::JALRC_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::JALRC_32_(uint64 instruction) +static char *JALRC_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("JALRC %s, %s", rt, rs); + return img_format("JALRC %s, %s", rt, rs); } @@ -7955,15 +7518,15 @@ std::string NMD::JALRC_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::JALRC_HB(uint64 instruction) +static char *JALRC_HB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("JALRC.HB %s, %s", rt, rs); + return img_format("JALRC.HB %s, %s", rt, rs); } @@ -7977,13 +7540,13 @@ std::string NMD::JALRC_HB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::JRC(uint64 instruction) +static char *JRC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_9_8_7_6_5(instruction); - std::string rt = GPR(copy(rt_value)); + const char *rt = GPR(rt_value, info); - return img::format("JRC %s", rt); + return img_format("JRC %s", rt); } @@ -7997,17 +7560,16 @@ std::string NMD::JRC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LB_16_(uint64 instruction) +static char *LB_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 u_value = extract_u_1_0(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); - return img::format("LB %s, %s(%s)", rt3, u, rs3); + return img_format("LB %s, 0x%" PRIx64 "(%s)", rt3, u_value, rs3); } @@ -8021,15 +7583,14 @@ std::string NMD::LB_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LB_GP_(uint64 instruction) +static char *LB_GP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_to_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("LB %s, %s($%d)", rt, u, 28); + return img_format("LB %s, 0x%" PRIx64 "($%d)", rt, u_value, 28); } @@ -8043,17 +7604,16 @@ std::string NMD::LB_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LB_S9_(uint64 instruction) +static char *LB_S9_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LB %s, %s(%s)", rt, s, rs); + return img_format("LB %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -8067,17 +7627,16 @@ std::string NMD::LB_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LB_U12_(uint64 instruction) +static char *LB_U12_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LB %s, %s(%s)", rt, u, rs); + return img_format("LB %s, 0x%" PRIx64 "(%s)", rt, u_value, rs); } @@ -8091,17 +7650,16 @@ std::string NMD::LB_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LBE(uint64 instruction) +static char *LBE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LBE %s, %s(%s)", rt, s, rs); + return img_format("LBE %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -8115,17 +7673,16 @@ std::string NMD::LBE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LBU_16_(uint64 instruction) +static char *LBU_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 u_value = extract_u_1_0(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); - return img::format("LBU %s, %s(%s)", rt3, u, rs3); + return img_format("LBU %s, 0x%" PRIx64 "(%s)", rt3, u_value, rs3); } @@ -8139,15 +7696,14 @@ std::string NMD::LBU_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LBU_GP_(uint64 instruction) +static char *LBU_GP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_to_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("LBU %s, %s($%d)", rt, u, 28); + return img_format("LBU %s, 0x%" PRIx64 "($%d)", rt, u_value, 28); } @@ -8161,17 +7717,16 @@ std::string NMD::LBU_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LBU_S9_(uint64 instruction) +static char *LBU_S9_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LBU %s, %s(%s)", rt, s, rs); + return img_format("LBU %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -8185,17 +7740,16 @@ std::string NMD::LBU_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LBU_U12_(uint64 instruction) +static char *LBU_U12_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LBU %s, %s(%s)", rt, u, rs); + return img_format("LBU %s, 0x%" PRIx64 "(%s)", rt, u_value, rs); } @@ -8209,17 +7763,16 @@ std::string NMD::LBU_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LBUE(uint64 instruction) +static char *LBUE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LBUE %s, %s(%s)", rt, s, rs); + return img_format("LBUE %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -8233,17 +7786,17 @@ std::string NMD::LBUE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LBUX(uint64 instruction) +static char *LBUX(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LBUX %s, %s(%s)", rd, rs, rt); + return img_format("LBUX %s, %s(%s)", rd, rs, rt); } @@ -8257,17 +7810,17 @@ std::string NMD::LBUX(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LBX(uint64 instruction) +static char *LBX(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LBX %s, %s(%s)", rd, rs, rt); + return img_format("LBX %s, %s(%s)", rd, rs, rt); } @@ -8281,15 +7834,14 @@ std::string NMD::LBX(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LD_GP_(uint64 instruction) +static char *LD_GP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_20_to_3__s3(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("LD %s, %s($%d)", rt, u, 28); + return img_format("LD %s, 0x%" PRIx64 "($%d)", rt, u_value, 28); } @@ -8303,17 +7855,16 @@ std::string NMD::LD_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LD_S9_(uint64 instruction) +static char *LD_S9_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LD %s, %s(%s)", rt, s, rs); + return img_format("LD %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -8327,17 +7878,16 @@ std::string NMD::LD_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LD_U12_(uint64 instruction) +static char *LD_U12_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LD %s, %s(%s)", rt, u, rs); + return img_format("LD %s, 0x%" PRIx64 "(%s)", rt, u_value, rs); } @@ -8351,15 +7901,14 @@ std::string NMD::LD_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LDC1_GP_(uint64 instruction) +static char *LDC1_GP_(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_to_2__s2(instruction); - std::string ft = FPR(copy(ft_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *ft = FPR(ft_value, info); - return img::format("LDC1 %s, %s($%d)", ft, u, 28); + return img_format("LDC1 %s, 0x%" PRIx64 "($%d)", ft, u_value, 28); } @@ -8373,17 +7922,16 @@ std::string NMD::LDC1_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LDC1_S9_(uint64 instruction) +static char *LDC1_S9_(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string ft = FPR(copy(ft_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LDC1 %s, %s(%s)", ft, s, rs); + return img_format("LDC1 %s, %" PRId64 "(%s)", ft, s_value, rs); } @@ -8397,17 +7945,16 @@ std::string NMD::LDC1_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LDC1_U12_(uint64 instruction) +static char *LDC1_U12_(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string ft = FPR(copy(ft_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LDC1 %s, %s(%s)", ft, u, rs); + return img_format("LDC1 %s, 0x%" PRIx64 "(%s)", ft, u_value, rs); } @@ -8421,17 +7968,17 @@ std::string NMD::LDC1_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LDC1XS(uint64 instruction) +static char *LDC1XS(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ft_value = extract_ft_15_14_13_12_11(instruction); - std::string ft = FPR(copy(ft_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LDC1XS %s, %s(%s)", ft, rs, rt); + return img_format("LDC1XS %s, %s(%s)", ft, rs, rt); } @@ -8445,17 +7992,17 @@ std::string NMD::LDC1XS(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LDC1X(uint64 instruction) +static char *LDC1X(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ft_value = extract_ft_15_14_13_12_11(instruction); - std::string ft = FPR(copy(ft_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LDC1X %s, %s(%s)", ft, rs, rt); + return img_format("LDC1X %s, %s(%s)", ft, rs, rt); } @@ -8469,17 +8016,16 @@ std::string NMD::LDC1X(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LDC2(uint64 instruction) +static char *LDC2(uint64 instruction, Dis_info *info) { uint64 ct_value = extract_ct_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string ct = CPR(copy(ct_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rs = GPR(rs_value, info); - return img::format("LDC2 %s, %s(%s)", ct, s, rs); + return img_format("LDC2 CP%" PRIu64 ", %" PRId64 "(%s)", + ct_value, s_value, rs); } @@ -8493,19 +8039,19 @@ std::string NMD::LDC2(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LDM(uint64 instruction) +static char *LDM(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); uint64 count3_value = extract_count3_14_13_12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); - std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); + uint64 count3 = encode_count3_from_count(count3_value); - return img::format("LDM %s, %s(%s), %s", rt, s, rs, count3); + return img_format("LDM %s, %" PRId64 "(%s), 0x%" PRIx64, + rt, s_value, rs, count3); } @@ -8519,15 +8065,15 @@ std::string NMD::LDM(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LDPC_48_(uint64 instruction) +static char *LDPC_48_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_41_40_39_38_37(instruction); int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 6); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 6, info); - return img::format("LDPC %s, %s", rt, s); + return img_format("LDPC %s, %s", rt, s); } @@ -8541,17 +8087,17 @@ std::string NMD::LDPC_48_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LDX(uint64 instruction) +static char *LDX(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LDX %s, %s(%s)", rd, rs, rt); + return img_format("LDX %s, %s(%s)", rd, rs, rt); } @@ -8565,17 +8111,17 @@ std::string NMD::LDX(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LDXS(uint64 instruction) +static char *LDXS(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LDXS %s, %s(%s)", rd, rs, rt); + return img_format("LDXS %s, %s(%s)", rd, rs, rt); } @@ -8589,17 +8135,16 @@ std::string NMD::LDXS(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LH_16_(uint64 instruction) +static char *LH_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 u_value = extract_u_2_1__s1(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); - return img::format("LH %s, %s(%s)", rt3, u, rs3); + return img_format("LH %s, 0x%" PRIx64 "(%s)", rt3, u_value, rs3); } @@ -8613,15 +8158,14 @@ std::string NMD::LH_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LH_GP_(uint64 instruction) +static char *LH_GP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_to_1__s1(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("LH %s, %s($%d)", rt, u, 28); + return img_format("LH %s, 0x%" PRIx64 "($%d)", rt, u_value, 28); } @@ -8635,17 +8179,16 @@ std::string NMD::LH_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LH_S9_(uint64 instruction) +static char *LH_S9_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LH %s, %s(%s)", rt, s, rs); + return img_format("LH %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -8659,17 +8202,16 @@ std::string NMD::LH_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LH_U12_(uint64 instruction) +static char *LH_U12_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LH %s, %s(%s)", rt, u, rs); + return img_format("LH %s, 0x%" PRIx64 "(%s)", rt, u_value, rs); } @@ -8683,17 +8225,16 @@ std::string NMD::LH_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LHE(uint64 instruction) +static char *LHE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LHE %s, %s(%s)", rt, s, rs); + return img_format("LHE %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -8707,17 +8248,16 @@ std::string NMD::LHE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LHU_16_(uint64 instruction) +static char *LHU_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 u_value = extract_u_2_1__s1(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); - return img::format("LHU %s, %s(%s)", rt3, u, rs3); + return img_format("LHU %s, 0x%" PRIx64 "(%s)", rt3, u_value, rs3); } @@ -8731,15 +8271,14 @@ std::string NMD::LHU_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LHU_GP_(uint64 instruction) +static char *LHU_GP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_to_1__s1(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("LHU %s, %s($%d)", rt, u, 28); + return img_format("LHU %s, 0x%" PRIx64 "($%d)", rt, u_value, 28); } @@ -8753,17 +8292,16 @@ std::string NMD::LHU_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LHU_S9_(uint64 instruction) +static char *LHU_S9_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LHU %s, %s(%s)", rt, s, rs); + return img_format("LHU %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -8777,17 +8315,16 @@ std::string NMD::LHU_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LHU_U12_(uint64 instruction) +static char *LHU_U12_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LHU %s, %s(%s)", rt, u, rs); + return img_format("LHU %s, 0x%" PRIx64 "(%s)", rt, u_value, rs); } @@ -8801,17 +8338,16 @@ std::string NMD::LHU_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LHUE(uint64 instruction) +static char *LHUE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LHUE %s, %s(%s)", rt, s, rs); + return img_format("LHUE %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -8825,17 +8361,17 @@ std::string NMD::LHUE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LHUX(uint64 instruction) +static char *LHUX(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LHUX %s, %s(%s)", rd, rs, rt); + return img_format("LHUX %s, %s(%s)", rd, rs, rt); } @@ -8849,17 +8385,17 @@ std::string NMD::LHUX(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LHUXS(uint64 instruction) +static char *LHUXS(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LHUXS %s, %s(%s)", rd, rs, rt); + return img_format("LHUXS %s, %s(%s)", rd, rs, rt); } @@ -8873,17 +8409,17 @@ std::string NMD::LHUXS(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LHXS(uint64 instruction) +static char *LHXS(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LHXS %s, %s(%s)", rd, rs, rt); + return img_format("LHXS %s, %s(%s)", rd, rs, rt); } @@ -8897,17 +8433,17 @@ std::string NMD::LHXS(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LHX(uint64 instruction) +static char *LHX(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LHX %s, %s(%s)", rd, rs, rt); + return img_format("LHX %s, %s(%s)", rd, rs, rt); } @@ -8921,15 +8457,15 @@ std::string NMD::LHX(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LI_16_(uint64 instruction) +static char *LI_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 eu_value = extract_eu_6_5_4_3_2_1_0(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string eu = IMMEDIATE(encode_eu_from_s_li16(eu_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + int64 eu = encode_eu_from_s_li16(eu_value); - return img::format("LI %s, %s", rt3, eu); + return img_format("LI %s, %" PRId64, rt3, eu); } @@ -8943,15 +8479,14 @@ std::string NMD::LI_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LI_48_(uint64 instruction) +static char *LI_48_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_41_40_39_38_37(instruction); int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); + const char *rt = GPR(rt_value, info); - return img::format("LI %s, %s", rt, s); + return img_format("LI %s, %" PRId64, rt, s_value); } @@ -8965,17 +8500,16 @@ std::string NMD::LI_48_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LL(uint64 instruction) +static char *LL(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_s2(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LL %s, %s(%s)", rt, s, rs); + return img_format("LL %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -8989,17 +8523,16 @@ std::string NMD::LL(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LLD(uint64 instruction) +static char *LLD(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_s3(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LLD %s, %s(%s)", rt, s, rs); + return img_format("LLD %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -9013,17 +8546,17 @@ std::string NMD::LLD(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LLDP(uint64 instruction) +static char *LLDP(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ru_value = extract_ru_7_6_5_4_3(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ru = GPR(copy(ru_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *ru = GPR(ru_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LLDP %s, %s, (%s)", rt, ru, rs); + return img_format("LLDP %s, %s, (%s)", rt, ru, rs); } @@ -9037,17 +8570,16 @@ std::string NMD::LLDP(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LLE(uint64 instruction) +static char *LLE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_s2(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LLE %s, %s(%s)", rt, s, rs); + return img_format("LLE %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -9061,17 +8593,17 @@ std::string NMD::LLE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LLWP(uint64 instruction) +static char *LLWP(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ru_value = extract_ru_7_6_5_4_3(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ru = GPR(copy(ru_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *ru = GPR(ru_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LLWP %s, %s, (%s)", rt, ru, rs); + return img_format("LLWP %s, %s, (%s)", rt, ru, rs); } @@ -9085,17 +8617,17 @@ std::string NMD::LLWP(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LLWPE(uint64 instruction) +static char *LLWPE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ru_value = extract_ru_7_6_5_4_3(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ru = GPR(copy(ru_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *ru = GPR(ru_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LLWPE %s, %s, (%s)", rt, ru, rs); + return img_format("LLWPE %s, %s, (%s)", rt, ru, rs); } @@ -9109,19 +8641,18 @@ std::string NMD::LLWPE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LSA(uint64 instruction) +static char *LSA(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); uint64 u2_value = extract_u2_10_9(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); - std::string u2 = IMMEDIATE(copy(u2_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LSA %s, %s, %s, %s", rd, rs, rt, u2); + return img_format("LSA %s, %s, %s, 0x%" PRIx64, rd, rs, rt, u2_value); } @@ -9135,15 +8666,14 @@ std::string NMD::LSA(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LUI(uint64 instruction) +static char *LUI(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); int64 s_value = extract_s__se31_0_11_to_2_20_to_12_s12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); + const char *rt = GPR(rt_value, info); - return img::format("LUI %s, %%hi(%s)", rt, s); + return img_format("LUI %s, %%hi(%" PRId64 ")", rt, s_value); } @@ -9157,17 +8687,16 @@ std::string NMD::LUI(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LW_16_(uint64 instruction) +static char *LW_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 u_value = extract_u_3_2_1_0__s2(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); - return img::format("LW %s, %s(%s)", rt3, u, rs3); + return img_format("LW %s, 0x%" PRIx64 "(%s)", rt3, u_value, rs3); } @@ -9181,17 +8710,16 @@ std::string NMD::LW_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LW_4X4_(uint64 instruction) +static char *LW_4X4_(uint64 instruction, Dis_info *info) { uint64 rt4_value = extract_rt4_9_7_6_5(instruction); uint64 rs4_value = extract_rs4_4_2_1_0(instruction); uint64 u_value = extract_u_3_8__s2(instruction); - std::string rt4 = GPR(decode_gpr_gpr4(rt4_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs4 = GPR(decode_gpr_gpr4(rs4_value)); + const char *rt4 = GPR(decode_gpr_gpr4(rt4_value, info), info); + const char *rs4 = GPR(decode_gpr_gpr4(rs4_value, info), info); - return img::format("LW %s, %s(%s)", rt4, u, rs4); + return img_format("LW %s, 0x%" PRIx64 "(%s)", rt4, u_value, rs4); } @@ -9205,15 +8733,14 @@ std::string NMD::LW_4X4_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LW_GP_(uint64 instruction) +static char *LW_GP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_20_to_2__s2(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("LW %s, %s($%d)", rt, u, 28); + return img_format("LW %s, 0x%" PRIx64 "($%d)", rt, u_value, 28); } @@ -9227,15 +8754,14 @@ std::string NMD::LW_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LW_GP16_(uint64 instruction) +static char *LW_GP16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 u_value = extract_u_6_5_4_3_2_1_0__s2(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); - return img::format("LW %s, %s($%d)", rt3, u, 28); + return img_format("LW %s, 0x%" PRIx64 "($%d)", rt3, u_value, 28); } @@ -9249,17 +8775,16 @@ std::string NMD::LW_GP16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LW_S9_(uint64 instruction) +static char *LW_S9_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LW %s, %s(%s)", rt, s, rs); + return img_format("LW %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -9273,15 +8798,14 @@ std::string NMD::LW_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LW_SP_(uint64 instruction) +static char *LW_SP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_9_8_7_6_5(instruction); uint64 u_value = extract_u_4_3_2_1_0__s2(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("LW %s, %s($%d)", rt, u, 29); + return img_format("LW %s, 0x%" PRIx64 "($%d)", rt, u_value, 29); } @@ -9295,17 +8819,16 @@ std::string NMD::LW_SP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LW_U12_(uint64 instruction) +static char *LW_U12_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LW %s, %s(%s)", rt, u, rs); + return img_format("LW %s, 0x%" PRIx64 "(%s)", rt, u_value, rs); } @@ -9319,15 +8842,14 @@ std::string NMD::LW_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWC1_GP_(uint64 instruction) +static char *LWC1_GP_(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_to_2__s2(instruction); - std::string ft = FPR(copy(ft_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *ft = FPR(ft_value, info); - return img::format("LWC1 %s, %s($%d)", ft, u, 28); + return img_format("LWC1 %s, 0x%" PRIx64 "($%d)", ft, u_value, 28); } @@ -9341,17 +8863,16 @@ std::string NMD::LWC1_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWC1_S9_(uint64 instruction) +static char *LWC1_S9_(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string ft = FPR(copy(ft_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LWC1 %s, %s(%s)", ft, s, rs); + return img_format("LWC1 %s, %" PRId64 "(%s)", ft, s_value, rs); } @@ -9365,17 +8886,16 @@ std::string NMD::LWC1_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWC1_U12_(uint64 instruction) +static char *LWC1_U12_(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string ft = FPR(copy(ft_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LWC1 %s, %s(%s)", ft, u, rs); + return img_format("LWC1 %s, 0x%" PRIx64 "(%s)", ft, u_value, rs); } @@ -9389,17 +8909,17 @@ std::string NMD::LWC1_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWC1X(uint64 instruction) +static char *LWC1X(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ft_value = extract_ft_15_14_13_12_11(instruction); - std::string ft = FPR(copy(ft_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LWC1X %s, %s(%s)", ft, rs, rt); + return img_format("LWC1X %s, %s(%s)", ft, rs, rt); } @@ -9413,17 +8933,17 @@ std::string NMD::LWC1X(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWC1XS(uint64 instruction) +static char *LWC1XS(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ft_value = extract_ft_15_14_13_12_11(instruction); - std::string ft = FPR(copy(ft_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LWC1XS %s, %s(%s)", ft, rs, rt); + return img_format("LWC1XS %s, %s(%s)", ft, rs, rt); } @@ -9437,17 +8957,16 @@ std::string NMD::LWC1XS(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWC2(uint64 instruction) +static char *LWC2(uint64 instruction, Dis_info *info) { uint64 ct_value = extract_ct_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string ct = CPR(copy(ct_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rs = GPR(rs_value, info); - return img::format("LWC2 %s, %s(%s)", ct, s, rs); + return img_format("LWC2 CP%" PRIu64 ", %" PRId64 "(%s)", + ct_value, s_value, rs); } @@ -9461,17 +8980,16 @@ std::string NMD::LWC2(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWE(uint64 instruction) +static char *LWE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LWE %s, %s(%s)", rt, s, rs); + return img_format("LWE %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -9485,19 +9003,19 @@ std::string NMD::LWE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWM(uint64 instruction) +static char *LWM(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); uint64 count3_value = extract_count3_14_13_12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); - std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); + uint64 count3 = encode_count3_from_count(count3_value); - return img::format("LWM %s, %s(%s), %s", rt, s, rs, count3); + return img_format("LWM %s, %" PRId64 "(%s), 0x%" PRIx64, + rt, s_value, rs, count3); } @@ -9511,15 +9029,15 @@ std::string NMD::LWM(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWPC_48_(uint64 instruction) +static char *LWPC_48_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_41_40_39_38_37(instruction); int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 6); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 6, info); - return img::format("LWPC %s, %s", rt, s); + return img_format("LWPC %s, %s", rt, s); } @@ -9533,15 +9051,14 @@ std::string NMD::LWPC_48_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWU_GP_(uint64 instruction) +static char *LWU_GP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_to_2__s2(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("LWU %s, %s($%d)", rt, u, 28); + return img_format("LWU %s, 0x%" PRIx64 "($%d)", rt, u_value, 28); } @@ -9555,17 +9072,16 @@ std::string NMD::LWU_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWU_S9_(uint64 instruction) +static char *LWU_S9_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LWU %s, %s(%s)", rt, s, rs); + return img_format("LWU %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -9579,17 +9095,16 @@ std::string NMD::LWU_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWU_U12_(uint64 instruction) +static char *LWU_U12_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("LWU %s, %s(%s)", rt, u, rs); + return img_format("LWU %s, 0x%" PRIx64 "(%s)", rt, u_value, rs); } @@ -9603,17 +9118,17 @@ std::string NMD::LWU_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWUX(uint64 instruction) +static char *LWUX(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LWUX %s, %s(%s)", rd, rs, rt); + return img_format("LWUX %s, %s(%s)", rd, rs, rt); } @@ -9627,17 +9142,17 @@ std::string NMD::LWUX(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWUXS(uint64 instruction) +static char *LWUXS(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LWUXS %s, %s(%s)", rd, rs, rt); + return img_format("LWUXS %s, %s(%s)", rd, rs, rt); } @@ -9651,17 +9166,17 @@ std::string NMD::LWUXS(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWX(uint64 instruction) +static char *LWX(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LWX %s, %s(%s)", rd, rs, rt); + return img_format("LWX %s, %s(%s)", rd, rs, rt); } @@ -9675,17 +9190,17 @@ std::string NMD::LWX(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWXS_16_(uint64 instruction) +static char *LWXS_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 rd3_value = extract_rd3_3_2_1(instruction); - std::string rd3 = GPR(decode_gpr_gpr3(rd3_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); - std::string rt3 = IMMEDIATE(decode_gpr_gpr3(rt3_value)); + const char *rd3 = GPR(decode_gpr_gpr3(rd3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); + uint64 rt3 = decode_gpr_gpr3(rt3_value, info); - return img::format("LWXS %s, %s(%s)", rd3, rs3, rt3); + return img_format("LWXS %s, %s(0x%" PRIx64 ")", rd3, rs3, rt3); } @@ -9699,17 +9214,17 @@ std::string NMD::LWXS_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::LWXS_32_(uint64 instruction) +static char *LWXS_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("LWXS %s, %s(%s)", rd, rs, rt); + return img_format("LWXS %s, %s(%s)", rd, rs, rt); } @@ -9724,17 +9239,17 @@ std::string NMD::LWXS_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MADD_DSP_(uint64 instruction) +static char *MADD_DSP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MADD %s, %s, %s", ac, rs, rt); + return img_format("MADD %s, %s, %s", ac, rs, rt); } @@ -9748,17 +9263,17 @@ std::string NMD::MADD_DSP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MADDF_D(uint64 instruction) +static char *MADDF_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("MADDF.D %s, %s, %s", fd, fs, ft); + return img_format("MADDF.D %s, %s, %s", fd, fs, ft); } @@ -9772,17 +9287,17 @@ std::string NMD::MADDF_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MADDF_S(uint64 instruction) +static char *MADDF_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("MADDF.S %s, %s, %s", fd, fs, ft); + return img_format("MADDF.S %s, %s, %s", fd, fs, ft); } @@ -9797,17 +9312,17 @@ std::string NMD::MADDF_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MADDU_DSP_(uint64 instruction) +static char *MADDU_DSP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MADDU %s, %s, %s", ac, rs, rt); + return img_format("MADDU %s, %s, %s", ac, rs, rt); } @@ -9822,17 +9337,17 @@ std::string NMD::MADDU_DSP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MAQ_S_W_PHL(uint64 instruction) +static char *MAQ_S_W_PHL(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MAQ_S.W.PHL %s, %s, %s", ac, rs, rt); + return img_format("MAQ_S.W.PHL %s, %s, %s", ac, rs, rt); } @@ -9847,17 +9362,17 @@ std::string NMD::MAQ_S_W_PHL(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MAQ_S_W_PHR(uint64 instruction) +static char *MAQ_S_W_PHR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MAQ_S.W.PHR %s, %s, %s", ac, rs, rt); + return img_format("MAQ_S.W.PHR %s, %s, %s", ac, rs, rt); } @@ -9872,17 +9387,17 @@ std::string NMD::MAQ_S_W_PHR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MAQ_SA_W_PHL(uint64 instruction) +static char *MAQ_SA_W_PHL(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MAQ_SA.W.PHL %s, %s, %s", ac, rs, rt); + return img_format("MAQ_SA.W.PHL %s, %s, %s", ac, rs, rt); } @@ -9897,17 +9412,17 @@ std::string NMD::MAQ_SA_W_PHL(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MAQ_SA_W_PHR(uint64 instruction) +static char *MAQ_SA_W_PHR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MAQ_SA.W.PHR %s, %s, %s", ac, rs, rt); + return img_format("MAQ_SA.W.PHR %s, %s, %s", ac, rs, rt); } @@ -9921,17 +9436,17 @@ std::string NMD::MAQ_SA_W_PHR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MAX_D(uint64 instruction) +static char *MAX_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("MAX.D %s, %s, %s", fd, fs, ft); + return img_format("MAX.D %s, %s, %s", fd, fs, ft); } @@ -9945,17 +9460,17 @@ std::string NMD::MAX_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MAX_S(uint64 instruction) +static char *MAX_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("MAX.S %s, %s, %s", fd, fs, ft); + return img_format("MAX.S %s, %s, %s", fd, fs, ft); } @@ -9969,17 +9484,17 @@ std::string NMD::MAX_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MAXA_D(uint64 instruction) +static char *MAXA_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("MAXA.D %s, %s, %s", fd, fs, ft); + return img_format("MAXA.D %s, %s, %s", fd, fs, ft); } @@ -9993,17 +9508,17 @@ std::string NMD::MAXA_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MAXA_S(uint64 instruction) +static char *MAXA_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("MAXA.S %s, %s, %s", fd, fs, ft); + return img_format("MAXA.S %s, %s, %s", fd, fs, ft); } @@ -10017,17 +9532,16 @@ std::string NMD::MAXA_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MFC0(uint64 instruction) +static char *MFC0(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = CPR(copy(c0s_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("MFC0 %s, %s, %s", rt, c0s, sel); + return img_format("MFC0 %s, CP%" PRIu64 ", 0x%" PRIx64, + rt, c0s_value, sel_value); } @@ -10041,15 +9555,15 @@ std::string NMD::MFC0(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MFC1(uint64 instruction) +static char *MFC1(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string fs = FPR(copy(fs_value)); + const char *rt = GPR(rt_value, info); + const char *fs = FPR(fs_value, info); - return img::format("MFC1 %s, %s", rt, fs); + return img_format("MFC1 %s, %s", rt, fs); } @@ -10063,15 +9577,14 @@ std::string NMD::MFC1(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MFC2(uint64 instruction) +static char *MFC2(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 cs_value = extract_cs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string cs = CPR(copy(cs_value)); + const char *rt = GPR(rt_value, info); - return img::format("MFC2 %s, %s", rt, cs); + return img_format("MFC2 %s, CP%" PRIu64, rt, cs_value); } @@ -10085,17 +9598,16 @@ std::string NMD::MFC2(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MFGC0(uint64 instruction) +static char *MFGC0(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = CPR(copy(c0s_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("MFGC0 %s, %s, %s", rt, c0s, sel); + return img_format("MFGC0 %s, CP%" PRIu64 ", 0x%" PRIx64, + rt, c0s_value, sel_value); } @@ -10109,17 +9621,16 @@ std::string NMD::MFGC0(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MFHC0(uint64 instruction) +static char *MFHC0(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = CPR(copy(c0s_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("MFHC0 %s, %s, %s", rt, c0s, sel); + return img_format("MFHC0 %s, CP%" PRIu64 ", 0x%" PRIx64, + rt, c0s_value, sel_value); } @@ -10133,15 +9644,15 @@ std::string NMD::MFHC0(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MFHC1(uint64 instruction) +static char *MFHC1(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string fs = FPR(copy(fs_value)); + const char *rt = GPR(rt_value, info); + const char *fs = FPR(fs_value, info); - return img::format("MFHC1 %s, %s", rt, fs); + return img_format("MFHC1 %s, %s", rt, fs); } @@ -10155,15 +9666,14 @@ std::string NMD::MFHC1(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MFHC2(uint64 instruction) +static char *MFHC2(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 cs_value = extract_cs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string cs = CPR(copy(cs_value)); + const char *rt = GPR(rt_value, info); - return img::format("MFHC2 %s, %s", rt, cs); + return img_format("MFHC2 %s, CP%" PRIu64, rt, cs_value); } @@ -10177,17 +9687,16 @@ std::string NMD::MFHC2(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MFHGC0(uint64 instruction) +static char *MFHGC0(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = CPR(copy(c0s_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("MFHGC0 %s, %s, %s", rt, c0s, sel); + return img_format("MFHGC0 %s, CP%" PRIu64 ", 0x%" PRIx64, + rt, c0s_value, sel_value); } @@ -10200,15 +9709,15 @@ std::string NMD::MFHGC0(uint64 instruction) * rt ----- * ac -- */ -std::string NMD::MFHI_DSP_(uint64 instruction) +static char *MFHI_DSP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ac = AC(copy(ac_value)); + const char *rt = GPR(rt_value, info); + const char *ac = AC(ac_value, info); - return img::format("MFHI %s, %s", rt, ac); + return img_format("MFHI %s, %s", rt, ac); } @@ -10222,19 +9731,17 @@ std::string NMD::MFHI_DSP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MFHTR(uint64 instruction) +static char *MFHTR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); uint64 u_value = extract_u_10(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = IMMEDIATE(copy(c0s_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("MFHTR %s, %s, %s, %s", rt, c0s, u, sel); + return img_format("MFHTR %s, 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64, + rt, c0s_value, u_value, sel_value); } @@ -10247,15 +9754,15 @@ std::string NMD::MFHTR(uint64 instruction) * rt ----- * ac -- */ -std::string NMD::MFLO_DSP_(uint64 instruction) +static char *MFLO_DSP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ac = AC(copy(ac_value)); + const char *rt = GPR(rt_value, info); + const char *ac = AC(ac_value, info); - return img::format("MFLO %s, %s", rt, ac); + return img_format("MFLO %s, %s", rt, ac); } @@ -10269,19 +9776,17 @@ std::string NMD::MFLO_DSP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MFTR(uint64 instruction) +static char *MFTR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); uint64 u_value = extract_u_10(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = IMMEDIATE(copy(c0s_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("MFTR %s, %s, %s, %s", rt, c0s, u, sel); + return img_format("MFTR %s, 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64, + rt, c0s_value, u_value, sel_value); } @@ -10295,17 +9800,17 @@ std::string NMD::MFTR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MIN_D(uint64 instruction) +static char *MIN_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("MIN.D %s, %s, %s", fd, fs, ft); + return img_format("MIN.D %s, %s, %s", fd, fs, ft); } @@ -10319,17 +9824,17 @@ std::string NMD::MIN_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MIN_S(uint64 instruction) +static char *MIN_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("MIN.S %s, %s, %s", fd, fs, ft); + return img_format("MIN.S %s, %s, %s", fd, fs, ft); } @@ -10343,17 +9848,17 @@ std::string NMD::MIN_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MINA_D(uint64 instruction) +static char *MINA_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("MINA.D %s, %s, %s", fd, fs, ft); + return img_format("MINA.D %s, %s, %s", fd, fs, ft); } @@ -10367,17 +9872,17 @@ std::string NMD::MINA_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MINA_S(uint64 instruction) +static char *MINA_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("MINA.S %s, %s, %s", fd, fs, ft); + return img_format("MINA.S %s, %s, %s", fd, fs, ft); } @@ -10391,17 +9896,17 @@ std::string NMD::MINA_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MOD(uint64 instruction) +static char *MOD(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MOD %s, %s, %s", rd, rs, rt); + return img_format("MOD %s, %s, %s", rd, rs, rt); } @@ -10415,17 +9920,17 @@ std::string NMD::MOD(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MODSUB(uint64 instruction) +static char *MODSUB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MODSUB %s, %s, %s", rd, rs, rt); + return img_format("MODSUB %s, %s, %s", rd, rs, rt); } @@ -10439,17 +9944,17 @@ std::string NMD::MODSUB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MODU(uint64 instruction) +static char *MODU(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MODU %s, %s, %s", rd, rs, rt); + return img_format("MODU %s, %s, %s", rd, rs, rt); } @@ -10463,15 +9968,15 @@ std::string NMD::MODU(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MOV_D(uint64 instruction) +static char *MOV_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("MOV.D %s, %s", ft, fs); + return img_format("MOV.D %s, %s", ft, fs); } @@ -10485,15 +9990,15 @@ std::string NMD::MOV_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MOV_S(uint64 instruction) +static char *MOV_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("MOV.S %s, %s", ft, fs); + return img_format("MOV.S %s, %s", ft, fs); } @@ -10507,17 +10012,17 @@ std::string NMD::MOV_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MOVE_BALC(uint64 instruction) +static char *MOVE_BALC(uint64 instruction, Dis_info *info) { uint64 rtz4_value = extract_rtz4_27_26_25_23_22_21(instruction); uint64 rd1_value = extract_rdl_25_24(instruction); int64 s_value = extract_s__se21_0_20_to_1_s1(instruction); - std::string rd1 = GPR(decode_gpr_gpr1(rd1_value)); - std::string rtz4 = GPR(decode_gpr_gpr4_zero(rtz4_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 4); + const char *rd1 = GPR(decode_gpr_gpr1(rd1_value, info), info); + const char *rtz4 = GPR(decode_gpr_gpr4_zero(rtz4_value, info), info); + g_autofree char *s = ADDRESS(s_value, 4, info); - return img::format("MOVE.BALC %s, %s, %s", rd1, rtz4, s); + return img_format("MOVE.BALC %s, %s, %s", rd1, rtz4, s); } @@ -10531,19 +10036,19 @@ std::string NMD::MOVE_BALC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MOVEP(uint64 instruction) +static char *MOVEP(uint64 instruction, Dis_info *info) { uint64 rtz4_value = extract_rtz4_9_7_6_5(instruction); uint64 rd2_value = extract_rd2_3_8(instruction); uint64 rsz4_value = extract_rsz4_4_2_1_0(instruction); - std::string rd2 = GPR(decode_gpr_gpr2_reg1(rd2_value)); - std::string re2 = GPR(decode_gpr_gpr2_reg2(rd2_value)); + const char *rd2 = GPR(decode_gpr_gpr2_reg1(rd2_value, info), info); + const char *re2 = GPR(decode_gpr_gpr2_reg2(rd2_value, info), info); /* !!!!!!!!!! - no conversion function */ - std::string rsz4 = GPR(decode_gpr_gpr4_zero(rsz4_value)); - std::string rtz4 = GPR(decode_gpr_gpr4_zero(rtz4_value)); + const char *rsz4 = GPR(decode_gpr_gpr4_zero(rsz4_value, info), info); + const char *rtz4 = GPR(decode_gpr_gpr4_zero(rtz4_value, info), info); - return img::format("MOVEP %s, %s, %s, %s", rd2, re2, rsz4, rtz4); + return img_format("MOVEP %s, %s, %s, %s", rd2, re2, rsz4, rtz4); /* hand edited */ } @@ -10558,19 +10063,19 @@ std::string NMD::MOVEP(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MOVEP_REV_(uint64 instruction) +static char *MOVEP_REV_(uint64 instruction, Dis_info *info) { uint64 rt4_value = extract_rt4_9_7_6_5(instruction); uint64 rd2_value = extract_rd2_3_8(instruction); uint64 rs4_value = extract_rs4_4_2_1_0(instruction); - std::string rs4 = GPR(decode_gpr_gpr4(rs4_value)); - std::string rt4 = GPR(decode_gpr_gpr4(rt4_value)); - std::string rd2 = GPR(decode_gpr_gpr2_reg1(rd2_value)); - std::string rs2 = GPR(decode_gpr_gpr2_reg2(rd2_value)); + const char *rs4 = GPR(decode_gpr_gpr4(rs4_value, info), info); + const char *rt4 = GPR(decode_gpr_gpr4(rt4_value, info), info); + const char *rd2 = GPR(decode_gpr_gpr2_reg1(rd2_value, info), info); + const char *rs2 = GPR(decode_gpr_gpr2_reg2(rd2_value, info), info); /* !!!!!!!!!! - no conversion function */ - return img::format("MOVEP %s, %s, %s, %s", rs4, rt4, rd2, rs2); + return img_format("MOVEP %s, %s, %s, %s", rs4, rt4, rd2, rs2); /* hand edited */ } @@ -10585,15 +10090,15 @@ std::string NMD::MOVEP_REV_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MOVE(uint64 instruction) +static char *MOVE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_9_8_7_6_5(instruction); uint64 rs_value = extract_rs_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("MOVE %s, %s", rt, rs); + return img_format("MOVE %s, %s", rt, rs); } @@ -10607,17 +10112,17 @@ std::string NMD::MOVE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MOVN(uint64 instruction) +static char *MOVN(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MOVN %s, %s, %s", rd, rs, rt); + return img_format("MOVN %s, %s, %s", rd, rs, rt); } @@ -10631,17 +10136,17 @@ std::string NMD::MOVN(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MOVZ(uint64 instruction) +static char *MOVZ(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MOVZ %s, %s, %s", rd, rs, rt); + return img_format("MOVZ %s, %s, %s", rd, rs, rt); } @@ -10655,17 +10160,17 @@ std::string NMD::MOVZ(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::MSUB_DSP_(uint64 instruction) +static char *MSUB_DSP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MSUB %s, %s, %s", ac, rs, rt); + return img_format("MSUB %s, %s, %s", ac, rs, rt); } @@ -10679,17 +10184,17 @@ std::string NMD::MSUB_DSP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MSUBF_D(uint64 instruction) +static char *MSUBF_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("MSUBF.D %s, %s, %s", fd, fs, ft); + return img_format("MSUBF.D %s, %s, %s", fd, fs, ft); } @@ -10703,17 +10208,17 @@ std::string NMD::MSUBF_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MSUBF_S(uint64 instruction) +static char *MSUBF_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("MSUBF.S %s, %s, %s", fd, fs, ft); + return img_format("MSUBF.S %s, %s, %s", fd, fs, ft); } @@ -10727,17 +10232,17 @@ std::string NMD::MSUBF_S(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::MSUBU_DSP_(uint64 instruction) +static char *MSUBU_DSP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MSUBU %s, %s, %s", ac, rs, rt); + return img_format("MSUBU %s, %s, %s", ac, rs, rt); } @@ -10751,17 +10256,16 @@ std::string NMD::MSUBU_DSP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MTC0(uint64 instruction) +static char *MTC0(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = CPR(copy(c0s_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("MTC0 %s, %s, %s", rt, c0s, sel); + return img_format("MTC0 %s, CP%" PRIu64 ", 0x%" PRIx64, + rt, c0s_value, sel_value); } @@ -10775,15 +10279,15 @@ std::string NMD::MTC0(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MTC1(uint64 instruction) +static char *MTC1(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string fs = FPR(copy(fs_value)); + const char *rt = GPR(rt_value, info); + const char *fs = FPR(fs_value, info); - return img::format("MTC1 %s, %s", rt, fs); + return img_format("MTC1 %s, %s", rt, fs); } @@ -10797,15 +10301,14 @@ std::string NMD::MTC1(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MTC2(uint64 instruction) +static char *MTC2(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 cs_value = extract_cs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string cs = CPR(copy(cs_value)); + const char *rt = GPR(rt_value, info); - return img::format("MTC2 %s, %s", rt, cs); + return img_format("MTC2 %s, CP%" PRIu64, rt, cs_value); } @@ -10819,17 +10322,16 @@ std::string NMD::MTC2(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MTGC0(uint64 instruction) +static char *MTGC0(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = CPR(copy(c0s_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("MTGC0 %s, %s, %s", rt, c0s, sel); + return img_format("MTGC0 %s, CP%" PRIu64 ", 0x%" PRIx64, + rt, c0s_value, sel_value); } @@ -10843,17 +10345,16 @@ std::string NMD::MTGC0(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MTHC0(uint64 instruction) +static char *MTHC0(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = CPR(copy(c0s_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("MTHC0 %s, %s, %s", rt, c0s, sel); + return img_format("MTHC0 %s, CP%" PRIu64 ", 0x%" PRIx64, + rt, c0s_value, sel_value); } @@ -10867,15 +10368,15 @@ std::string NMD::MTHC0(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MTHC1(uint64 instruction) +static char *MTHC1(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string fs = FPR(copy(fs_value)); + const char *rt = GPR(rt_value, info); + const char *fs = FPR(fs_value, info); - return img::format("MTHC1 %s, %s", rt, fs); + return img_format("MTHC1 %s, %s", rt, fs); } @@ -10889,15 +10390,14 @@ std::string NMD::MTHC1(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MTHC2(uint64 instruction) +static char *MTHC2(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 cs_value = extract_cs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string cs = CPR(copy(cs_value)); + const char *rt = GPR(rt_value, info); - return img::format("MTHC2 %s, %s", rt, cs); + return img_format("MTHC2 %s, CP%" PRIu64, rt, cs_value); } @@ -10911,17 +10411,16 @@ std::string NMD::MTHC2(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MTHGC0(uint64 instruction) +static char *MTHGC0(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = CPR(copy(c0s_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("MTHGC0 %s, %s, %s", rt, c0s, sel); + return img_format("MTHGC0 %s, CP%" PRIu64 ", 0x%" PRIx64, + rt, c0s_value, sel_value); } @@ -10934,15 +10433,15 @@ std::string NMD::MTHGC0(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::MTHI_DSP_(uint64 instruction) +static char *MTHI_DSP_(uint64 instruction, Dis_info *info) { uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rs = GPR(copy(rs_value)); - std::string ac = AC(copy(ac_value)); + const char *rs = GPR(rs_value, info); + const char *ac = AC(ac_value, info); - return img::format("MTHI %s, %s", rs, ac); + return img_format("MTHI %s, %s", rs, ac); } @@ -10955,15 +10454,15 @@ std::string NMD::MTHI_DSP_(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::MTHLIP(uint64 instruction) +static char *MTHLIP(uint64 instruction, Dis_info *info) { uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rs = GPR(copy(rs_value)); - std::string ac = AC(copy(ac_value)); + const char *rs = GPR(rs_value, info); + const char *ac = AC(ac_value, info); - return img::format("MTHLIP %s, %s", rs, ac); + return img_format("MTHLIP %s, %s", rs, ac); } @@ -10977,19 +10476,17 @@ std::string NMD::MTHLIP(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MTHTR(uint64 instruction) +static char *MTHTR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); uint64 u_value = extract_u_10(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = IMMEDIATE(copy(c0s_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("MTHTR %s, %s, %s, %s", rt, c0s, u, sel); + return img_format("MTHTR %s, 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64, + rt, c0s_value, u_value, sel_value); } @@ -11002,15 +10499,15 @@ std::string NMD::MTHTR(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::MTLO_DSP_(uint64 instruction) +static char *MTLO_DSP_(uint64 instruction, Dis_info *info) { uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rs = GPR(copy(rs_value)); - std::string ac = AC(copy(ac_value)); + const char *rs = GPR(rs_value, info); + const char *ac = AC(ac_value, info); - return img::format("MTLO %s, %s", rs, ac); + return img_format("MTLO %s, %s", rs, ac); } @@ -11024,19 +10521,17 @@ std::string NMD::MTLO_DSP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MTTR(uint64 instruction) +static char *MTTR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_15_14_13_12_11(instruction); uint64 u_value = extract_u_10(instruction); - std::string rt = GPR(copy(rt_value)); - std::string c0s = IMMEDIATE(copy(c0s_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("MTTR %s, %s, %s, %s", rt, c0s, u, sel); + return img_format("MTTR %s, 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64, + rt, c0s_value, u_value, sel_value); } @@ -11050,17 +10545,17 @@ std::string NMD::MTTR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MUH(uint64 instruction) +static char *MUH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MUH %s, %s, %s", rd, rs, rt); + return img_format("MUH %s, %s, %s", rd, rs, rt); } @@ -11074,17 +10569,17 @@ std::string NMD::MUH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MUHU(uint64 instruction) +static char *MUHU(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MUHU %s, %s, %s", rd, rs, rt); + return img_format("MUHU %s, %s, %s", rd, rs, rt); } @@ -11098,17 +10593,17 @@ std::string NMD::MUHU(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MUL_32_(uint64 instruction) +static char *MUL_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MUL %s, %s, %s", rd, rs, rt); + return img_format("MUL %s, %s, %s", rd, rs, rt); } @@ -11122,15 +10617,15 @@ std::string NMD::MUL_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MUL_4X4_(uint64 instruction) +static char *MUL_4X4_(uint64 instruction, Dis_info *info) { uint64 rt4_value = extract_rt4_9_7_6_5(instruction); uint64 rs4_value = extract_rs4_4_2_1_0(instruction); - std::string rs4 = GPR(decode_gpr_gpr4(rs4_value)); - std::string rt4 = GPR(decode_gpr_gpr4(rt4_value)); + const char *rs4 = GPR(decode_gpr_gpr4(rs4_value, info), info); + const char *rt4 = GPR(decode_gpr_gpr4(rt4_value, info), info); - return img::format("MUL %s, %s", rs4, rt4); + return img_format("MUL %s, %s", rs4, rt4); } @@ -11144,17 +10639,17 @@ std::string NMD::MUL_4X4_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MUL_D(uint64 instruction) +static char *MUL_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("MUL.D %s, %s, %s", fd, fs, ft); + return img_format("MUL.D %s, %s, %s", fd, fs, ft); } @@ -11169,17 +10664,17 @@ std::string NMD::MUL_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MUL_PH(uint64 instruction) +static char *MUL_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MUL.PH %s, %s, %s", rd, rs, rt); + return img_format("MUL.PH %s, %s, %s", rd, rs, rt); } @@ -11194,17 +10689,17 @@ std::string NMD::MUL_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MUL_S_PH(uint64 instruction) +static char *MUL_S_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MUL_S.PH %s, %s, %s", rd, rs, rt); + return img_format("MUL_S.PH %s, %s, %s", rd, rs, rt); } @@ -11218,17 +10713,17 @@ std::string NMD::MUL_S_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MUL_S(uint64 instruction) +static char *MUL_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("MUL.S %s, %s, %s", fd, fs, ft); + return img_format("MUL.S %s, %s, %s", fd, fs, ft); } @@ -11243,17 +10738,17 @@ std::string NMD::MUL_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MULEQ_S_W_PHL(uint64 instruction) +static char *MULEQ_S_W_PHL(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MULEQ_S.W.PHL %s, %s, %s", rd, rs, rt); + return img_format("MULEQ_S.W.PHL %s, %s, %s", rd, rs, rt); } @@ -11268,17 +10763,17 @@ std::string NMD::MULEQ_S_W_PHL(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MULEQ_S_W_PHR(uint64 instruction) +static char *MULEQ_S_W_PHR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MULEQ_S.W.PHR %s, %s, %s", rd, rs, rt); + return img_format("MULEQ_S.W.PHR %s, %s, %s", rd, rs, rt); } @@ -11293,17 +10788,17 @@ std::string NMD::MULEQ_S_W_PHR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MULEU_S_PH_QBL(uint64 instruction) +static char *MULEU_S_PH_QBL(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MULEU_S.PH.QBL %s, %s, %s", rd, rs, rt); + return img_format("MULEU_S.PH.QBL %s, %s, %s", rd, rs, rt); } @@ -11318,17 +10813,17 @@ std::string NMD::MULEU_S_PH_QBL(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MULEU_S_PH_QBR(uint64 instruction) +static char *MULEU_S_PH_QBR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MULEU_S.PH.QBR %s, %s, %s", rd, rs, rt); + return img_format("MULEU_S.PH.QBR %s, %s, %s", rd, rs, rt); } @@ -11343,17 +10838,17 @@ std::string NMD::MULEU_S_PH_QBR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MULQ_RS_PH(uint64 instruction) +static char *MULQ_RS_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MULQ_RS.PH %s, %s, %s", rd, rs, rt); + return img_format("MULQ_RS.PH %s, %s, %s", rd, rs, rt); } @@ -11368,17 +10863,17 @@ std::string NMD::MULQ_RS_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MULQ_RS_W(uint64 instruction) +static char *MULQ_RS_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MULQ_RS.W %s, %s, %s", rd, rs, rt); + return img_format("MULQ_RS.W %s, %s, %s", rd, rs, rt); } @@ -11393,17 +10888,17 @@ std::string NMD::MULQ_RS_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MULQ_S_PH(uint64 instruction) +static char *MULQ_S_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MULQ_S.PH %s, %s, %s", rd, rs, rt); + return img_format("MULQ_S.PH %s, %s, %s", rd, rs, rt); } @@ -11418,17 +10913,17 @@ std::string NMD::MULQ_S_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MULQ_S_W(uint64 instruction) +static char *MULQ_S_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MULQ_S.W %s, %s, %s", rd, rs, rt); + return img_format("MULQ_S.W %s, %s, %s", rd, rs, rt); } @@ -11443,17 +10938,17 @@ std::string NMD::MULQ_S_W(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::MULSA_W_PH(uint64 instruction) +static char *MULSA_W_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MULSA.W.PH %s, %s, %s", ac, rs, rt); + return img_format("MULSA.W.PH %s, %s, %s", ac, rs, rt); } @@ -11468,17 +10963,17 @@ std::string NMD::MULSA_W_PH(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::MULSAQ_S_W_PH(uint64 instruction) +static char *MULSAQ_S_W_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MULSAQ_S.W.PH %s, %s, %s", ac, rs, rt); + return img_format("MULSAQ_S.W.PH %s, %s, %s", ac, rs, rt); } @@ -11492,17 +10987,17 @@ std::string NMD::MULSAQ_S_W_PH(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::MULT_DSP_(uint64 instruction) +static char *MULT_DSP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MULT %s, %s, %s", ac, rs, rt); + return img_format("MULT %s, %s, %s", ac, rs, rt); } @@ -11516,17 +11011,17 @@ std::string NMD::MULT_DSP_(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::MULTU_DSP_(uint64 instruction) +static char *MULTU_DSP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string ac = AC(copy(ac_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ac = AC(ac_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MULTU %s, %s, %s", ac, rs, rt); + return img_format("MULTU %s, %s, %s", ac, rs, rt); } @@ -11540,17 +11035,17 @@ std::string NMD::MULTU_DSP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::MULU(uint64 instruction) +static char *MULU(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("MULU %s, %s, %s", rd, rs, rt); + return img_format("MULU %s, %s, %s", rd, rs, rt); } @@ -11564,15 +11059,15 @@ std::string NMD::MULU(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::NEG_D(uint64 instruction) +static char *NEG_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("NEG.D %s, %s", ft, fs); + return img_format("NEG.D %s, %s", ft, fs); } @@ -11586,15 +11081,15 @@ std::string NMD::NEG_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::NEG_S(uint64 instruction) +static char *NEG_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("NEG.S %s, %s", ft, fs); + return img_format("NEG.S %s, %s", ft, fs); } @@ -11608,11 +11103,11 @@ std::string NMD::NEG_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::NOP_16_(uint64 instruction) +static char *NOP_16_(uint64 instruction, Dis_info *info) { (void)instruction; - return "NOP "; + return g_strdup("NOP "); } @@ -11626,11 +11121,11 @@ std::string NMD::NOP_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::NOP_32_(uint64 instruction) +static char *NOP_32_(uint64 instruction, Dis_info *info) { (void)instruction; - return "NOP "; + return g_strdup("NOP "); } @@ -11644,17 +11139,17 @@ std::string NMD::NOP_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::NOR(uint64 instruction) +static char *NOR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("NOR %s, %s, %s", rd, rs, rt); + return img_format("NOR %s, %s, %s", rd, rs, rt); } @@ -11668,15 +11163,15 @@ std::string NMD::NOR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::NOT_16_(uint64 instruction) +static char *NOT_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); - return img::format("NOT %s, %s", rt3, rs3); + return img_format("NOT %s, %s", rt3, rs3); } @@ -11690,15 +11185,15 @@ std::string NMD::NOT_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::OR_16_(uint64 instruction) +static char *OR_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); - return img::format("OR %s, %s", rs3, rt3); + return img_format("OR %s, %s", rs3, rt3); } @@ -11712,17 +11207,17 @@ std::string NMD::OR_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::OR_32_(uint64 instruction) +static char *OR_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("OR %s, %s, %s", rd, rs, rt); + return img_format("OR %s, %s, %s", rd, rs, rt); } @@ -11736,17 +11231,16 @@ std::string NMD::OR_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ORI(uint64 instruction) +static char *ORI(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("ORI %s, %s, %s", rt, rs, u); + return img_format("ORI %s, %s, 0x%" PRIx64, rt, rs, u_value); } @@ -11761,17 +11255,17 @@ std::string NMD::ORI(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PACKRL_PH(uint64 instruction) +static char *PACKRL_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("PACKRL.PH %s, %s, %s", rd, rs, rt); + return img_format("PACKRL.PH %s, %s, %s", rd, rs, rt); } @@ -11785,11 +11279,11 @@ std::string NMD::PACKRL_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PAUSE(uint64 instruction) +static char *PAUSE(uint64 instruction, Dis_info *info) { (void)instruction; - return "PAUSE "; + return g_strdup("PAUSE "); } @@ -11804,17 +11298,17 @@ std::string NMD::PAUSE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PICK_PH(uint64 instruction) +static char *PICK_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("PICK.PH %s, %s, %s", rd, rs, rt); + return img_format("PICK.PH %s, %s, %s", rd, rs, rt); } @@ -11829,17 +11323,17 @@ std::string NMD::PICK_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PICK_QB(uint64 instruction) +static char *PICK_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("PICK.QB %s, %s, %s", rd, rs, rt); + return img_format("PICK.QB %s, %s, %s", rd, rs, rt); } @@ -11854,15 +11348,15 @@ std::string NMD::PICK_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECEQ_W_PHL(uint64 instruction) +static char *PRECEQ_W_PHL(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("PRECEQ.W.PHL %s, %s", rt, rs); + return img_format("PRECEQ.W.PHL %s, %s", rt, rs); } @@ -11877,15 +11371,15 @@ std::string NMD::PRECEQ_W_PHL(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECEQ_W_PHR(uint64 instruction) +static char *PRECEQ_W_PHR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("PRECEQ.W.PHR %s, %s", rt, rs); + return img_format("PRECEQ.W.PHR %s, %s", rt, rs); } @@ -11900,15 +11394,15 @@ std::string NMD::PRECEQ_W_PHR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECEQU_PH_QBLA(uint64 instruction) +static char *PRECEQU_PH_QBLA(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("PRECEQU.PH.QBLA %s, %s", rt, rs); + return img_format("PRECEQU.PH.QBLA %s, %s", rt, rs); } @@ -11923,15 +11417,15 @@ std::string NMD::PRECEQU_PH_QBLA(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECEQU_PH_QBL(uint64 instruction) +static char *PRECEQU_PH_QBL(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("PRECEQU.PH.QBL %s, %s", rt, rs); + return img_format("PRECEQU.PH.QBL %s, %s", rt, rs); } @@ -11946,15 +11440,15 @@ std::string NMD::PRECEQU_PH_QBL(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECEQU_PH_QBRA(uint64 instruction) +static char *PRECEQU_PH_QBRA(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("PRECEQU.PH.QBRA %s, %s", rt, rs); + return img_format("PRECEQU.PH.QBRA %s, %s", rt, rs); } @@ -11969,15 +11463,15 @@ std::string NMD::PRECEQU_PH_QBRA(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECEQU_PH_QBR(uint64 instruction) +static char *PRECEQU_PH_QBR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("PRECEQU.PH.QBR %s, %s", rt, rs); + return img_format("PRECEQU.PH.QBR %s, %s", rt, rs); } @@ -11993,15 +11487,15 @@ std::string NMD::PRECEQU_PH_QBR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECEU_PH_QBLA(uint64 instruction) +static char *PRECEU_PH_QBLA(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("PRECEU.PH.QBLA %s, %s", rt, rs); + return img_format("PRECEU.PH.QBLA %s, %s", rt, rs); } @@ -12016,15 +11510,15 @@ std::string NMD::PRECEU_PH_QBLA(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECEU_PH_QBL(uint64 instruction) +static char *PRECEU_PH_QBL(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("PRECEU.PH.QBL %s, %s", rt, rs); + return img_format("PRECEU.PH.QBL %s, %s", rt, rs); } @@ -12040,15 +11534,15 @@ std::string NMD::PRECEU_PH_QBL(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECEU_PH_QBRA(uint64 instruction) +static char *PRECEU_PH_QBRA(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("PRECEU.PH.QBRA %s, %s", rt, rs); + return img_format("PRECEU.PH.QBRA %s, %s", rt, rs); } @@ -12063,15 +11557,15 @@ std::string NMD::PRECEU_PH_QBRA(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECEU_PH_QBR(uint64 instruction) +static char *PRECEU_PH_QBR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("PRECEU.PH.QBR %s, %s", rt, rs); + return img_format("PRECEU.PH.QBR %s, %s", rt, rs); } @@ -12086,17 +11580,17 @@ std::string NMD::PRECEU_PH_QBR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECR_QB_PH(uint64 instruction) +static char *PRECR_QB_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("PRECR.QB.PH %s, %s, %s", rd, rs, rt); + return img_format("PRECR.QB.PH %s, %s, %s", rd, rs, rt); } @@ -12111,17 +11605,16 @@ std::string NMD::PRECR_QB_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECR_SRA_PH_W(uint64 instruction) +static char *PRECR_SRA_PH_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("PRECR_SRA.PH.W %s, %s, %s", rt, rs, sa); + return img_format("PRECR_SRA.PH.W %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -12136,17 +11629,16 @@ std::string NMD::PRECR_SRA_PH_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECR_SRA_R_PH_W(uint64 instruction) +static char *PRECR_SRA_R_PH_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("PRECR_SRA_R.PH.W %s, %s, %s", rt, rs, sa); + return img_format("PRECR_SRA_R.PH.W %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -12161,17 +11653,17 @@ std::string NMD::PRECR_SRA_R_PH_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECRQ_PH_W(uint64 instruction) +static char *PRECRQ_PH_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("PRECRQ.PH.W %s, %s, %s", rd, rs, rt); + return img_format("PRECRQ.PH.W %s, %s, %s", rd, rs, rt); } @@ -12186,17 +11678,17 @@ std::string NMD::PRECRQ_PH_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECRQ_QB_PH(uint64 instruction) +static char *PRECRQ_QB_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("PRECRQ.QB.PH %s, %s, %s", rd, rs, rt); + return img_format("PRECRQ.QB.PH %s, %s, %s", rd, rs, rt); } @@ -12211,17 +11703,17 @@ std::string NMD::PRECRQ_QB_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECRQ_RS_PH_W(uint64 instruction) +static char *PRECRQ_RS_PH_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("PRECRQ_RS.PH.W %s, %s, %s", rd, rs, rt); + return img_format("PRECRQ_RS.PH.W %s, %s, %s", rd, rs, rt); } @@ -12236,17 +11728,17 @@ std::string NMD::PRECRQ_RS_PH_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PRECRQU_S_QB_PH(uint64 instruction) +static char *PRECRQU_S_QB_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("PRECRQU_S.QB.PH %s, %s, %s", rd, rs, rt); + return img_format("PRECRQU_S.QB.PH %s, %s, %s", rd, rs, rt); } @@ -12260,17 +11752,16 @@ std::string NMD::PRECRQU_S_QB_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PREF_S9_(uint64 instruction) +static char *PREF_S9_(uint64 instruction, Dis_info *info) { uint64 hint_value = extract_hint_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string hint = IMMEDIATE(copy(hint_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rs = GPR(rs_value, info); - return img::format("PREF %s, %s(%s)", hint, s, rs); + return img_format("PREF 0x%" PRIx64 ", %" PRId64 "(%s)", + hint_value, s_value, rs); } @@ -12284,17 +11775,16 @@ std::string NMD::PREF_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PREF_U12_(uint64 instruction) +static char *PREF_U12_(uint64 instruction, Dis_info *info) { uint64 hint_value = extract_hint_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string hint = IMMEDIATE(copy(hint_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *rs = GPR(rs_value, info); - return img::format("PREF %s, %s(%s)", hint, u, rs); + return img_format("PREF 0x%" PRIx64 ", 0x%" PRIx64 "(%s)", + hint_value, u_value, rs); } @@ -12308,17 +11798,16 @@ std::string NMD::PREF_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PREFE(uint64 instruction) +static char *PREFE(uint64 instruction, Dis_info *info) { uint64 hint_value = extract_hint_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string hint = IMMEDIATE(copy(hint_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rs = GPR(rs_value, info); - return img::format("PREFE %s, %s(%s)", hint, s, rs); + return img_format("PREFE 0x%" PRIx64 ", %" PRId64 "(%s)", + hint_value, s_value, rs); } @@ -12332,17 +11821,16 @@ std::string NMD::PREFE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::PREPEND(uint64 instruction) +static char *PREPEND(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("PREPEND %s, %s, %s", rt, rs, sa); + return img_format("PREPEND %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -12355,15 +11843,15 @@ std::string NMD::PREPEND(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::RADDU_W_QB(uint64 instruction) +static char *RADDU_W_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("RADDU.W.QB %s, %s", rt, rs); + return img_format("RADDU.W.QB %s, %s", rt, rs); } @@ -12376,15 +11864,14 @@ std::string NMD::RADDU_W_QB(uint64 instruction) * rt ----- * mask ------- */ -std::string NMD::RDDSP(uint64 instruction) +static char *RDDSP(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 mask_value = extract_mask_20_19_18_17_16_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string mask = IMMEDIATE(copy(mask_value)); + const char *rt = GPR(rt_value, info); - return img::format("RDDSP %s, %s", rt, mask); + return img_format("RDDSP %s, 0x%" PRIx64, rt, mask_value); } @@ -12398,17 +11885,16 @@ std::string NMD::RDDSP(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::RDHWR(uint64 instruction) +static char *RDHWR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 hs_value = extract_hs_20_19_18_17_16(instruction); uint64 sel_value = extract_sel_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string hs = CPR(copy(hs_value)); - std::string sel = IMMEDIATE(copy(sel_value)); + const char *rt = GPR(rt_value, info); - return img::format("RDHWR %s, %s, %s", rt, hs, sel); + return img_format("RDHWR %s, CP%" PRIu64 ", 0x%" PRIx64, + rt, hs_value, sel_value); } @@ -12422,15 +11908,15 @@ std::string NMD::RDHWR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::RDPGPR(uint64 instruction) +static char *RDPGPR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("RDPGPR %s, %s", rt, rs); + return img_format("RDPGPR %s, %s", rt, rs); } @@ -12444,15 +11930,15 @@ std::string NMD::RDPGPR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::RECIP_D(uint64 instruction) +static char *RECIP_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("RECIP.D %s, %s", ft, fs); + return img_format("RECIP.D %s, %s", ft, fs); } @@ -12466,15 +11952,15 @@ std::string NMD::RECIP_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::RECIP_S(uint64 instruction) +static char *RECIP_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("RECIP.S %s, %s", ft, fs); + return img_format("RECIP.S %s, %s", ft, fs); } @@ -12488,15 +11974,14 @@ std::string NMD::RECIP_S(uint64 instruction) * rt ----- * s ---------- */ -std::string NMD::REPL_PH(uint64 instruction) +static char *REPL_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); int64 s_value = extract_s__se9_20_19_18_17_16_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); + const char *rt = GPR(rt_value, info); - return img::format("REPL.PH %s, %s", rt, s); + return img_format("REPL.PH %s, %" PRId64, rt, s_value); } @@ -12510,15 +11995,14 @@ std::string NMD::REPL_PH(uint64 instruction) * rt ----- * u -------- */ -std::string NMD::REPL_QB(uint64 instruction) +static char *REPL_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_20_19_18_17_16_15_14_13(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("REPL.QB %s, %s", rt, u); + return img_format("REPL.QB %s, 0x%" PRIx64, rt, u_value); } @@ -12532,15 +12016,15 @@ std::string NMD::REPL_QB(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::REPLV_PH(uint64 instruction) +static char *REPLV_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("REPLV.PH %s, %s", rt, rs); + return img_format("REPLV.PH %s, %s", rt, rs); } @@ -12553,15 +12037,15 @@ std::string NMD::REPLV_PH(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::REPLV_QB(uint64 instruction) +static char *REPLV_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("REPLV.QB %s, %s", rt, rs); + return img_format("REPLV.QB %s, %s", rt, rs); } @@ -12575,16 +12059,16 @@ std::string NMD::REPLV_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::RESTORE_32_(uint64 instruction) +static char *RESTORE_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 count_value = extract_count_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3__s3(instruction); uint64 gp_value = extract_gp_2(instruction); - std::string u = IMMEDIATE(copy(u_value)); - return img::format("RESTORE %s%s", u, - save_restore_list(rt_value, count_value, gp_value)); + g_autofree char *save_restore_str = save_restore_list( + rt_value, count_value, gp_value, info); + return img_format("RESTORE 0x%" PRIx64 "%s", u_value, save_restore_str); } @@ -12598,15 +12082,15 @@ std::string NMD::RESTORE_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::RESTORE_JRC_16_(uint64 instruction) +static char *RESTORE_JRC_16_(uint64 instruction, Dis_info *info) { uint64 rt1_value = extract_rtl_11(instruction); uint64 u_value = extract_u_7_6_5_4__s4(instruction); uint64 count_value = extract_count_3_2_1_0(instruction); - std::string u = IMMEDIATE(copy(u_value)); - return img::format("RESTORE.JRC %s%s", u, - save_restore_list(encode_rt1_from_rt(rt1_value), count_value, 0)); + g_autofree char *save_restore_str = save_restore_list( + encode_rt1_from_rt(rt1_value), count_value, 0, info); + return img_format("RESTORE.JRC 0x%" PRIx64 "%s", u_value, save_restore_str); } @@ -12620,16 +12104,17 @@ std::string NMD::RESTORE_JRC_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::RESTORE_JRC_32_(uint64 instruction) +static char *RESTORE_JRC_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 count_value = extract_count_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3__s3(instruction); uint64 gp_value = extract_gp_2(instruction); - std::string u = IMMEDIATE(copy(u_value)); - return img::format("RESTORE.JRC %s%s", u, - save_restore_list(rt_value, count_value, gp_value)); + g_autofree char *save_restore_str = save_restore_list( + rt_value, count_value, gp_value, info); + return img_format("RESTORE.JRC 0x%" PRIx64 "%s", u_value, + save_restore_str); } @@ -12643,15 +12128,14 @@ std::string NMD::RESTORE_JRC_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::RESTOREF(uint64 instruction) +static char *RESTOREF(uint64 instruction, Dis_info *info) { uint64 count_value = extract_count_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3__s3(instruction); - std::string u = IMMEDIATE(copy(u_value)); - std::string count = IMMEDIATE(copy(count_value)); - return img::format("RESTOREF %s, %s", u, count); + return img_format("RESTOREF 0x%" PRIx64 ", 0x%" PRIx64, + u_value, count_value); } @@ -12665,15 +12149,15 @@ std::string NMD::RESTOREF(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::RINT_D(uint64 instruction) +static char *RINT_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("RINT.D %s, %s", ft, fs); + return img_format("RINT.D %s, %s", ft, fs); } @@ -12687,15 +12171,15 @@ std::string NMD::RINT_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::RINT_S(uint64 instruction) +static char *RINT_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("RINT.S %s, %s", ft, fs); + return img_format("RINT.S %s, %s", ft, fs); } @@ -12709,17 +12193,16 @@ std::string NMD::RINT_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ROTR(uint64 instruction) +static char *ROTR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 shift_value = extract_shift_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("ROTR %s, %s, %s", rt, rs, shift); + return img_format("ROTR %s, %s, 0x%" PRIx64, rt, rs, shift_value); } @@ -12733,17 +12216,17 @@ std::string NMD::ROTR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ROTRV(uint64 instruction) +static char *ROTRV(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("ROTRV %s, %s, %s", rd, rs, rt); + return img_format("ROTRV %s, %s, %s", rd, rs, rt); } @@ -12757,7 +12240,7 @@ std::string NMD::ROTRV(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ROTX(uint64 instruction) +static char *ROTX(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); @@ -12765,14 +12248,11 @@ std::string NMD::ROTX(uint64 instruction) uint64 stripe_value = extract_stripe_6(instruction); uint64 shift_value = extract_shift_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string shift = IMMEDIATE(copy(shift_value)); - std::string shiftx = IMMEDIATE(copy(shiftx_value)); - std::string stripe = IMMEDIATE(copy(stripe_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("ROTX %s, %s, %s, %s, %s", - rt, rs, shift, shiftx, stripe); + return img_format("ROTX %s, %s, 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64, + rt, rs, shift_value, shiftx_value, stripe_value); } @@ -12786,15 +12266,15 @@ std::string NMD::ROTX(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ROUND_L_D(uint64 instruction) +static char *ROUND_L_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("ROUND.L.D %s, %s", ft, fs); + return img_format("ROUND.L.D %s, %s", ft, fs); } @@ -12808,15 +12288,15 @@ std::string NMD::ROUND_L_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ROUND_L_S(uint64 instruction) +static char *ROUND_L_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("ROUND.L.S %s, %s", ft, fs); + return img_format("ROUND.L.S %s, %s", ft, fs); } @@ -12830,15 +12310,15 @@ std::string NMD::ROUND_L_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ROUND_W_D(uint64 instruction) +static char *ROUND_W_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("ROUND.W.D %s, %s", ft, fs); + return img_format("ROUND.W.D %s, %s", ft, fs); } @@ -12852,15 +12332,15 @@ std::string NMD::ROUND_W_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::ROUND_W_S(uint64 instruction) +static char *ROUND_W_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("ROUND.W.S %s, %s", ft, fs); + return img_format("ROUND.W.S %s, %s", ft, fs); } @@ -12874,15 +12354,15 @@ std::string NMD::ROUND_W_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::RSQRT_D(uint64 instruction) +static char *RSQRT_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("RSQRT.D %s, %s", ft, fs); + return img_format("RSQRT.D %s, %s", ft, fs); } @@ -12896,15 +12376,15 @@ std::string NMD::RSQRT_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::RSQRT_S(uint64 instruction) +static char *RSQRT_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("RSQRT.S %s, %s", ft, fs); + return img_format("RSQRT.S %s, %s", ft, fs); } @@ -12918,15 +12398,15 @@ std::string NMD::RSQRT_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SAVE_16_(uint64 instruction) +static char *SAVE_16_(uint64 instruction, Dis_info *info) { uint64 rt1_value = extract_rtl_11(instruction); uint64 u_value = extract_u_7_6_5_4__s4(instruction); uint64 count_value = extract_count_3_2_1_0(instruction); - std::string u = IMMEDIATE(copy(u_value)); - return img::format("SAVE %s%s", u, - save_restore_list(encode_rt1_from_rt(rt1_value), count_value, 0)); + g_autofree char *save_restore_str = save_restore_list( + encode_rt1_from_rt(rt1_value), count_value, 0, info); + return img_format("SAVE 0x%" PRIx64 "%s", u_value, save_restore_str); } @@ -12940,16 +12420,16 @@ std::string NMD::SAVE_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SAVE_32_(uint64 instruction) +static char *SAVE_32_(uint64 instruction, Dis_info *info) { uint64 count_value = extract_count_19_18_17_16(instruction); uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3__s3(instruction); uint64 gp_value = extract_gp_2(instruction); - std::string u = IMMEDIATE(copy(u_value)); - return img::format("SAVE %s%s", u, - save_restore_list(rt_value, count_value, gp_value)); + g_autofree char *save_restore_str = save_restore_list( + rt_value, count_value, gp_value, info); + return img_format("SAVE 0x%" PRIx64 "%s", u_value, save_restore_str); } @@ -12963,15 +12443,13 @@ std::string NMD::SAVE_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SAVEF(uint64 instruction) +static char *SAVEF(uint64 instruction, Dis_info *info) { uint64 count_value = extract_count_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3__s3(instruction); - std::string u = IMMEDIATE(copy(u_value)); - std::string count = IMMEDIATE(copy(count_value)); - return img::format("SAVEF %s, %s", u, count); + return img_format("SAVEF 0x%" PRIx64 ", 0x%" PRIx64, u_value, count_value); } @@ -12985,17 +12463,16 @@ std::string NMD::SAVEF(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SB_16_(uint64 instruction) +static char *SB_16_(uint64 instruction, Dis_info *info) { uint64 rtz3_value = extract_rtz3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 u_value = extract_u_1_0(instruction); - std::string rtz3 = GPR(decode_gpr_gpr3_src_store(rtz3_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + const char *rtz3 = GPR(decode_gpr_gpr3_src_store(rtz3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); - return img::format("SB %s, %s(%s)", rtz3, u, rs3); + return img_format("SB %s, 0x%" PRIx64 "(%s)", rtz3, u_value, rs3); } @@ -13009,15 +12486,14 @@ std::string NMD::SB_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SB_GP_(uint64 instruction) +static char *SB_GP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_to_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("SB %s, %s($%d)", rt, u, 28); + return img_format("SB %s, 0x%" PRIx64 "($%d)", rt, u_value, 28); } @@ -13031,17 +12507,16 @@ std::string NMD::SB_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SB_S9_(uint64 instruction) +static char *SB_S9_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SB %s, %s(%s)", rt, s, rs); + return img_format("SB %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -13055,17 +12530,16 @@ std::string NMD::SB_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SB_U12_(uint64 instruction) +static char *SB_U12_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SB %s, %s(%s)", rt, u, rs); + return img_format("SB %s, 0x%" PRIx64 "(%s)", rt, u_value, rs); } @@ -13079,17 +12553,16 @@ std::string NMD::SB_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SBE(uint64 instruction) +static char *SBE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SBE %s, %s(%s)", rt, s, rs); + return img_format("SBE %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -13103,17 +12576,17 @@ std::string NMD::SBE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SBX(uint64 instruction) +static char *SBX(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SBX %s, %s(%s)", rd, rs, rt); + return img_format("SBX %s, %s(%s)", rd, rs, rt); } @@ -13127,17 +12600,16 @@ std::string NMD::SBX(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SC(uint64 instruction) +static char *SC(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_s2(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SC %s, %s(%s)", rt, s, rs); + return img_format("SC %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -13151,17 +12623,16 @@ std::string NMD::SC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SCD(uint64 instruction) +static char *SCD(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_s3(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SCD %s, %s(%s)", rt, s, rs); + return img_format("SCD %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -13175,17 +12646,17 @@ std::string NMD::SCD(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SCDP(uint64 instruction) +static char *SCDP(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ru_value = extract_ru_7_6_5_4_3(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ru = GPR(copy(ru_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *ru = GPR(ru_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SCDP %s, %s, (%s)", rt, ru, rs); + return img_format("SCDP %s, %s, (%s)", rt, ru, rs); } @@ -13199,17 +12670,16 @@ std::string NMD::SCDP(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SCE(uint64 instruction) +static char *SCE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_s2(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SCE %s, %s(%s)", rt, s, rs); + return img_format("SCE %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -13223,17 +12693,17 @@ std::string NMD::SCE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SCWP(uint64 instruction) +static char *SCWP(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ru_value = extract_ru_7_6_5_4_3(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ru = GPR(copy(ru_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *ru = GPR(ru_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SCWP %s, %s, (%s)", rt, ru, rs); + return img_format("SCWP %s, %s, (%s)", rt, ru, rs); } @@ -13247,17 +12717,17 @@ std::string NMD::SCWP(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SCWPE(uint64 instruction) +static char *SCWPE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ru_value = extract_ru_7_6_5_4_3(instruction); - std::string rt = GPR(copy(rt_value)); - std::string ru = GPR(copy(ru_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *ru = GPR(ru_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SCWPE %s, %s, (%s)", rt, ru, rs); + return img_format("SCWPE %s, %s, (%s)", rt, ru, rs); } @@ -13271,15 +12741,14 @@ std::string NMD::SCWPE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SD_GP_(uint64 instruction) +static char *SD_GP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_20_to_3__s3(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("SD %s, %s($%d)", rt, u, 28); + return img_format("SD %s, 0x%" PRIx64 "($%d)", rt, u_value, 28); } @@ -13293,17 +12762,16 @@ std::string NMD::SD_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SD_S9_(uint64 instruction) +static char *SD_S9_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SD %s, %s(%s)", rt, s, rs); + return img_format("SD %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -13317,17 +12785,16 @@ std::string NMD::SD_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SD_U12_(uint64 instruction) +static char *SD_U12_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SD %s, %s(%s)", rt, u, rs); + return img_format("SD %s, 0x%" PRIx64 "(%s)", rt, u_value, rs); } @@ -13341,13 +12808,12 @@ std::string NMD::SD_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SDBBP_16_(uint64 instruction) +static char *SDBBP_16_(uint64 instruction, Dis_info *info) { uint64 code_value = extract_code_2_1_0(instruction); - std::string code = IMMEDIATE(copy(code_value)); - return img::format("SDBBP %s", code); + return img_format("SDBBP 0x%" PRIx64, code_value); } @@ -13361,13 +12827,12 @@ std::string NMD::SDBBP_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SDBBP_32_(uint64 instruction) +static char *SDBBP_32_(uint64 instruction, Dis_info *info) { uint64 code_value = extract_code_18_to_0(instruction); - std::string code = IMMEDIATE(copy(code_value)); - return img::format("SDBBP %s", code); + return img_format("SDBBP 0x%" PRIx64, code_value); } @@ -13381,15 +12846,14 @@ std::string NMD::SDBBP_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SDC1_GP_(uint64 instruction) +static char *SDC1_GP_(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_to_2__s2(instruction); - std::string ft = FPR(copy(ft_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *ft = FPR(ft_value, info); - return img::format("SDC1 %s, %s($%d)", ft, u, 28); + return img_format("SDC1 %s, 0x%" PRIx64 "($%d)", ft, u_value, 28); } @@ -13403,17 +12867,16 @@ std::string NMD::SDC1_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SDC1_S9_(uint64 instruction) +static char *SDC1_S9_(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string ft = FPR(copy(ft_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SDC1 %s, %s(%s)", ft, s, rs); + return img_format("SDC1 %s, %" PRId64 "(%s)", ft, s_value, rs); } @@ -13427,17 +12890,16 @@ std::string NMD::SDC1_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SDC1_U12_(uint64 instruction) +static char *SDC1_U12_(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string ft = FPR(copy(ft_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SDC1 %s, %s(%s)", ft, u, rs); + return img_format("SDC1 %s, 0x%" PRIx64 "(%s)", ft, u_value, rs); } @@ -13451,17 +12913,17 @@ std::string NMD::SDC1_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SDC1X(uint64 instruction) +static char *SDC1X(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ft_value = extract_ft_15_14_13_12_11(instruction); - std::string ft = FPR(copy(ft_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SDC1X %s, %s(%s)", ft, rs, rt); + return img_format("SDC1X %s, %s(%s)", ft, rs, rt); } @@ -13475,17 +12937,17 @@ std::string NMD::SDC1X(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SDC1XS(uint64 instruction) +static char *SDC1XS(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ft_value = extract_ft_15_14_13_12_11(instruction); - std::string ft = FPR(copy(ft_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SDC1XS %s, %s(%s)", ft, rs, rt); + return img_format("SDC1XS %s, %s(%s)", ft, rs, rt); } @@ -13499,17 +12961,16 @@ std::string NMD::SDC1XS(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SDC2(uint64 instruction) +static char *SDC2(uint64 instruction, Dis_info *info) { uint64 cs_value = extract_cs_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string cs = CPR(copy(cs_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rs = GPR(rs_value, info); - return img::format("SDC2 %s, %s(%s)", cs, s, rs); + return img_format("SDC2 CP%" PRIu64 ", %" PRId64 "(%s)", + cs_value, s_value, rs); } @@ -13523,19 +12984,19 @@ std::string NMD::SDC2(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SDM(uint64 instruction) +static char *SDM(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); uint64 count3_value = extract_count3_14_13_12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); - std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); + uint64 count3 = encode_count3_from_count(count3_value); - return img::format("SDM %s, %s(%s), %s", rt, s, rs, count3); + return img_format("SDM %s, %" PRId64 "(%s), 0x%" PRIx64, + rt, s_value, rs, count3); } @@ -13549,15 +13010,15 @@ std::string NMD::SDM(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SDPC_48_(uint64 instruction) +static char *SDPC_48_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_41_40_39_38_37(instruction); int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 6); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 6, info); - return img::format("SDPC %s, %s", rt, s); + return img_format("SDPC %s, %s", rt, s); } @@ -13571,17 +13032,17 @@ std::string NMD::SDPC_48_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SDXS(uint64 instruction) +static char *SDXS(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SDXS %s, %s(%s)", rd, rs, rt); + return img_format("SDXS %s, %s(%s)", rd, rs, rt); } @@ -13595,17 +13056,17 @@ std::string NMD::SDXS(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SDX(uint64 instruction) +static char *SDX(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SDX %s, %s(%s)", rd, rs, rt); + return img_format("SDX %s, %s(%s)", rd, rs, rt); } @@ -13619,15 +13080,15 @@ std::string NMD::SDX(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SEB(uint64 instruction) +static char *SEB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SEB %s, %s", rt, rs); + return img_format("SEB %s, %s", rt, rs); } @@ -13641,15 +13102,15 @@ std::string NMD::SEB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SEH(uint64 instruction) +static char *SEH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SEH %s, %s", rt, rs); + return img_format("SEH %s, %s", rt, rs); } @@ -13663,17 +13124,17 @@ std::string NMD::SEH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SEL_D(uint64 instruction) +static char *SEL_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("SEL.D %s, %s, %s", fd, fs, ft); + return img_format("SEL.D %s, %s, %s", fd, fs, ft); } @@ -13687,17 +13148,17 @@ std::string NMD::SEL_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SEL_S(uint64 instruction) +static char *SEL_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("SEL.S %s, %s, %s", fd, fs, ft); + return img_format("SEL.S %s, %s, %s", fd, fs, ft); } @@ -13711,17 +13172,17 @@ std::string NMD::SEL_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SELEQZ_D(uint64 instruction) +static char *SELEQZ_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("SELEQZ.D %s, %s, %s", fd, fs, ft); + return img_format("SELEQZ.D %s, %s, %s", fd, fs, ft); } @@ -13735,17 +13196,17 @@ std::string NMD::SELEQZ_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SELEQZ_S(uint64 instruction) +static char *SELEQZ_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("SELEQZ.S %s, %s, %s", fd, fs, ft); + return img_format("SELEQZ.S %s, %s, %s", fd, fs, ft); } @@ -13759,17 +13220,17 @@ std::string NMD::SELEQZ_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SELNEZ_D(uint64 instruction) +static char *SELNEZ_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("SELNEZ.D %s, %s, %s", fd, fs, ft); + return img_format("SELNEZ.D %s, %s, %s", fd, fs, ft); } @@ -13783,17 +13244,17 @@ std::string NMD::SELNEZ_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SELNEZ_S(uint64 instruction) +static char *SELNEZ_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("SELNEZ.S %s, %s, %s", fd, fs, ft); + return img_format("SELNEZ.S %s, %s, %s", fd, fs, ft); } @@ -13807,17 +13268,16 @@ std::string NMD::SELNEZ_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SEQI(uint64 instruction) +static char *SEQI(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SEQI %s, %s, %s", rt, rs, u); + return img_format("SEQI %s, %s, 0x%" PRIx64, rt, rs, u_value); } @@ -13831,17 +13291,16 @@ std::string NMD::SEQI(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SH_16_(uint64 instruction) +static char *SH_16_(uint64 instruction, Dis_info *info) { uint64 rtz3_value = extract_rtz3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 u_value = extract_u_2_1__s1(instruction); - std::string rtz3 = GPR(decode_gpr_gpr3_src_store(rtz3_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + const char *rtz3 = GPR(decode_gpr_gpr3_src_store(rtz3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); - return img::format("SH %s, %s(%s)", rtz3, u, rs3); + return img_format("SH %s, 0x%" PRIx64 "(%s)", rtz3, u_value, rs3); } @@ -13855,15 +13314,14 @@ std::string NMD::SH_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SH_GP_(uint64 instruction) +static char *SH_GP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_to_1__s1(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("SH %s, %s($%d)", rt, u, 28); + return img_format("SH %s, 0x%" PRIx64 "($%d)", rt, u_value, 28); } @@ -13877,17 +13335,16 @@ std::string NMD::SH_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SH_S9_(uint64 instruction) +static char *SH_S9_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SH %s, %s(%s)", rt, s, rs); + return img_format("SH %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -13901,17 +13358,16 @@ std::string NMD::SH_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SH_U12_(uint64 instruction) +static char *SH_U12_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SH %s, %s(%s)", rt, u, rs); + return img_format("SH %s, 0x%" PRIx64 "(%s)", rt, u_value, rs); } @@ -13925,17 +13381,16 @@ std::string NMD::SH_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHE(uint64 instruction) +static char *SHE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHE %s, %s(%s)", rt, s, rs); + return img_format("SHE %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -13949,15 +13404,14 @@ std::string NMD::SHE(uint64 instruction) * shift ------ * ac -- */ -std::string NMD::SHILO(uint64 instruction) +static char *SHILO(uint64 instruction, Dis_info *info) { int64 shift_value = extract_shift__se5_21_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string shift = IMMEDIATE(copy(shift_value)); - std::string ac = AC(copy(ac_value)); + const char *ac = AC(ac_value, info); - return img::format("SHILO %s, %s", ac, shift); + return img_format("SHILO %s, 0x%" PRIx64, ac, shift_value); } @@ -13971,15 +13425,15 @@ std::string NMD::SHILO(uint64 instruction) * rs ----- * ac -- */ -std::string NMD::SHILOV(uint64 instruction) +static char *SHILOV(uint64 instruction, Dis_info *info) { uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ac_value = extract_ac_15_14(instruction); - std::string rs = GPR(copy(rs_value)); - std::string ac = AC(copy(ac_value)); + const char *rs = GPR(rs_value, info); + const char *ac = AC(ac_value, info); - return img::format("SHILOV %s, %s", ac, rs); + return img_format("SHILOV %s, %s", ac, rs); } @@ -13993,17 +13447,16 @@ std::string NMD::SHILOV(uint64 instruction) * rs ----- * sa ---- */ -std::string NMD::SHLL_PH(uint64 instruction) +static char *SHLL_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13_12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHLL.PH %s, %s, %s", rt, rs, sa); + return img_format("SHLL.PH %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -14017,17 +13470,16 @@ std::string NMD::SHLL_PH(uint64 instruction) * rs ----- * sa --- */ -std::string NMD::SHLL_QB(uint64 instruction) +static char *SHLL_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHLL.QB %s, %s, %s", rt, rs, sa); + return img_format("SHLL.QB %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -14042,17 +13494,16 @@ std::string NMD::SHLL_QB(uint64 instruction) * rs ----- * sa ---- */ -std::string NMD::SHLL_S_PH(uint64 instruction) +static char *SHLL_S_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13_12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHLL_S.PH %s, %s, %s", rt, rs, sa); + return img_format("SHLL_S.PH %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -14066,17 +13517,16 @@ std::string NMD::SHLL_S_PH(uint64 instruction) * rs ----- * sa ----- */ -std::string NMD::SHLL_S_W(uint64 instruction) +static char *SHLL_S_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHLL_S.W %s, %s, %s", rt, rs, sa); + return img_format("SHLL_S.W %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -14091,17 +13541,17 @@ std::string NMD::SHLL_S_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHLLV_PH(uint64 instruction) +static char *SHLLV_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rd = GPR(rd_value, info); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHLLV.PH %s, %s, %s", rd, rt, rs); + return img_format("SHLLV.PH %s, %s, %s", rd, rt, rs); } @@ -14115,17 +13565,17 @@ std::string NMD::SHLLV_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHLLV_QB(uint64 instruction) +static char *SHLLV_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rd = GPR(rd_value, info); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHLLV.QB %s, %s, %s", rd, rt, rs); + return img_format("SHLLV.QB %s, %s, %s", rd, rt, rs); } @@ -14140,17 +13590,17 @@ std::string NMD::SHLLV_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHLLV_S_PH(uint64 instruction) +static char *SHLLV_S_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rd = GPR(rd_value, info); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHLLV_S.PH %s, %s, %s", rd, rt, rs); + return img_format("SHLLV_S.PH %s, %s, %s", rd, rt, rs); } @@ -14164,17 +13614,17 @@ std::string NMD::SHLLV_S_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHLLV_S_W(uint64 instruction) +static char *SHLLV_S_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rd = GPR(rd_value, info); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHLLV_S.W %s, %s, %s", rd, rt, rs); + return img_format("SHLLV_S.W %s, %s, %s", rd, rt, rs); } @@ -14188,17 +13638,16 @@ std::string NMD::SHLLV_S_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHRA_PH(uint64 instruction) +static char *SHRA_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13_12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHRA.PH %s, %s, %s", rt, rs, sa); + return img_format("SHRA.PH %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -14212,17 +13661,16 @@ std::string NMD::SHRA_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHRA_QB(uint64 instruction) +static char *SHRA_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHRA.QB %s, %s, %s", rt, rs, sa); + return img_format("SHRA.QB %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -14236,17 +13684,16 @@ std::string NMD::SHRA_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHRA_R_PH(uint64 instruction) +static char *SHRA_R_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13_12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHRA_R.PH %s, %s, %s", rt, rs, sa); + return img_format("SHRA_R.PH %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -14260,17 +13707,16 @@ std::string NMD::SHRA_R_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHRA_R_QB(uint64 instruction) +static char *SHRA_R_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHRA_R.QB %s, %s, %s", rt, rs, sa); + return img_format("SHRA_R.QB %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -14284,17 +13730,16 @@ std::string NMD::SHRA_R_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHRA_R_W(uint64 instruction) +static char *SHRA_R_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13_12_11(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHRA_R.W %s, %s, %s", rt, rs, sa); + return img_format("SHRA_R.W %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -14308,17 +13753,17 @@ std::string NMD::SHRA_R_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHRAV_PH(uint64 instruction) +static char *SHRAV_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rd = GPR(rd_value, info); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHRAV.PH %s, %s, %s", rd, rt, rs); + return img_format("SHRAV.PH %s, %s, %s", rd, rt, rs); } @@ -14332,17 +13777,17 @@ std::string NMD::SHRAV_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHRAV_QB(uint64 instruction) +static char *SHRAV_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rd = GPR(rd_value, info); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHRAV.QB %s, %s, %s", rd, rt, rs); + return img_format("SHRAV.QB %s, %s, %s", rd, rt, rs); } @@ -14356,17 +13801,17 @@ std::string NMD::SHRAV_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHRAV_R_PH(uint64 instruction) +static char *SHRAV_R_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rd = GPR(rd_value, info); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHRAV_R.PH %s, %s, %s", rd, rt, rs); + return img_format("SHRAV_R.PH %s, %s, %s", rd, rt, rs); } @@ -14380,17 +13825,17 @@ std::string NMD::SHRAV_R_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHRAV_R_QB(uint64 instruction) +static char *SHRAV_R_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rd = GPR(rd_value, info); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHRAV_R.QB %s, %s, %s", rd, rt, rs); + return img_format("SHRAV_R.QB %s, %s, %s", rd, rt, rs); } @@ -14404,17 +13849,17 @@ std::string NMD::SHRAV_R_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHRAV_R_W(uint64 instruction) +static char *SHRAV_R_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rd = GPR(rd_value, info); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHRAV_R.W %s, %s, %s", rd, rt, rs); + return img_format("SHRAV_R.W %s, %s, %s", rd, rt, rs); } @@ -14428,17 +13873,16 @@ std::string NMD::SHRAV_R_W(uint64 instruction) * rs ----- * sa ---- */ -std::string NMD::SHRL_PH(uint64 instruction) +static char *SHRL_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13_12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHRL.PH %s, %s, %s", rt, rs, sa); + return img_format("SHRL.PH %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -14452,17 +13896,16 @@ std::string NMD::SHRL_PH(uint64 instruction) * rs ----- * sa --- */ -std::string NMD::SHRL_QB(uint64 instruction) +static char *SHRL_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 sa_value = extract_sa_15_14_13(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string sa = IMMEDIATE(copy(sa_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHRL.QB %s, %s, %s", rt, rs, sa); + return img_format("SHRL.QB %s, %s, 0x%" PRIx64, rt, rs, sa_value); } @@ -14477,17 +13920,17 @@ std::string NMD::SHRL_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHRLV_PH(uint64 instruction) +static char *SHRLV_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rd = GPR(rd_value, info); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHRLV.PH %s, %s, %s", rd, rt, rs); + return img_format("SHRLV.PH %s, %s, %s", rd, rt, rs); } @@ -14501,17 +13944,17 @@ std::string NMD::SHRLV_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHRLV_QB(uint64 instruction) +static char *SHRLV_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rd = GPR(rd_value, info); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SHRLV.QB %s, %s, %s", rd, rt, rs); + return img_format("SHRLV.QB %s, %s, %s", rd, rt, rs); } @@ -14525,17 +13968,17 @@ std::string NMD::SHRLV_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHX(uint64 instruction) +static char *SHX(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SHX %s, %s(%s)", rd, rs, rt); + return img_format("SHX %s, %s(%s)", rd, rs, rt); } @@ -14549,17 +13992,17 @@ std::string NMD::SHX(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SHXS(uint64 instruction) +static char *SHXS(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SHXS %s, %s(%s)", rd, rs, rt); + return img_format("SHXS %s, %s(%s)", rd, rs, rt); } @@ -14573,13 +14016,12 @@ std::string NMD::SHXS(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SIGRIE(uint64 instruction) +static char *SIGRIE(uint64 instruction, Dis_info *info) { uint64 code_value = extract_code_18_to_0(instruction); - std::string code = IMMEDIATE(copy(code_value)); - return img::format("SIGRIE %s", code); + return img_format("SIGRIE 0x%" PRIx64, code_value); } @@ -14593,17 +14035,17 @@ std::string NMD::SIGRIE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SLL_16_(uint64 instruction) +static char *SLL_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 shift3_value = extract_shift3_2_1_0(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); - std::string shift3 = IMMEDIATE(encode_shift3_from_shift(shift3_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); + uint64 shift3 = encode_shift3_from_shift(shift3_value); - return img::format("SLL %s, %s, %s", rt3, rs3, shift3); + return img_format("SLL %s, %s, 0x%" PRIx64, rt3, rs3, shift3); } @@ -14617,17 +14059,16 @@ std::string NMD::SLL_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SLL_32_(uint64 instruction) +static char *SLL_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 shift_value = extract_shift_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SLL %s, %s, %s", rt, rs, shift); + return img_format("SLL %s, %s, 0x%" PRIx64, rt, rs, shift_value); } @@ -14641,17 +14082,17 @@ std::string NMD::SLL_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SLLV(uint64 instruction) +static char *SLLV(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SLLV %s, %s, %s", rd, rs, rt); + return img_format("SLLV %s, %s, %s", rd, rs, rt); } @@ -14665,17 +14106,17 @@ std::string NMD::SLLV(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SLT(uint64 instruction) +static char *SLT(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SLT %s, %s, %s", rd, rs, rt); + return img_format("SLT %s, %s, %s", rd, rs, rt); } @@ -14689,17 +14130,16 @@ std::string NMD::SLT(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SLTI(uint64 instruction) +static char *SLTI(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SLTI %s, %s, %s", rt, rs, u); + return img_format("SLTI %s, %s, 0x%" PRIx64, rt, rs, u_value); } @@ -14713,17 +14153,16 @@ std::string NMD::SLTI(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SLTIU(uint64 instruction) +static char *SLTIU(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SLTIU %s, %s, %s", rt, rs, u); + return img_format("SLTIU %s, %s, 0x%" PRIx64, rt, rs, u_value); } @@ -14737,17 +14176,17 @@ std::string NMD::SLTIU(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SLTU(uint64 instruction) +static char *SLTU(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SLTU %s, %s, %s", rd, rs, rt); + return img_format("SLTU %s, %s, %s", rd, rs, rt); } @@ -14761,17 +14200,17 @@ std::string NMD::SLTU(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SOV(uint64 instruction) +static char *SOV(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SOV %s, %s, %s", rd, rs, rt); + return img_format("SOV %s, %s, %s", rd, rs, rt); } @@ -14785,13 +14224,12 @@ std::string NMD::SOV(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SPECIAL2(uint64 instruction) +static char *SPECIAL2(uint64 instruction, Dis_info *info) { uint64 op_value = extract_op_25_to_3(instruction); - std::string op = IMMEDIATE(copy(op_value)); - return img::format("SPECIAL2 %s", op); + return img_format("SPECIAL2 0x%" PRIx64, op_value); } @@ -14805,15 +14243,15 @@ std::string NMD::SPECIAL2(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SQRT_D(uint64 instruction) +static char *SQRT_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("SQRT.D %s, %s", ft, fs); + return img_format("SQRT.D %s, %s", ft, fs); } @@ -14827,15 +14265,15 @@ std::string NMD::SQRT_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SQRT_S(uint64 instruction) +static char *SQRT_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("SQRT.S %s, %s", ft, fs); + return img_format("SQRT.S %s, %s", ft, fs); } @@ -14849,17 +14287,16 @@ std::string NMD::SQRT_S(uint64 instruction) * rd ----- * sa ----- */ -std::string NMD::SRA(uint64 instruction) +static char *SRA(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 shift_value = extract_shift_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SRA %s, %s, %s", rt, rs, shift); + return img_format("SRA %s, %s, 0x%" PRIx64, rt, rs, shift_value); } @@ -14873,17 +14310,17 @@ std::string NMD::SRA(uint64 instruction) * rt ----- * rd ----- */ -std::string NMD::SRAV(uint64 instruction) +static char *SRAV(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SRAV %s, %s, %s", rd, rs, rt); + return img_format("SRAV %s, %s, %s", rd, rs, rt); } @@ -14897,17 +14334,17 @@ std::string NMD::SRAV(uint64 instruction) * rt ----- * rd ----- */ -std::string NMD::SRL_16_(uint64 instruction) +static char *SRL_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 shift3_value = extract_shift3_2_1_0(instruction); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); - std::string shift3 = IMMEDIATE(encode_shift3_from_shift(shift3_value)); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); + uint64 shift3 = encode_shift3_from_shift(shift3_value); - return img::format("SRL %s, %s, %s", rt3, rs3, shift3); + return img_format("SRL %s, %s, 0x%" PRIx64, rt3, rs3, shift3); } @@ -14921,17 +14358,16 @@ std::string NMD::SRL_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SRL_32_(uint64 instruction) +static char *SRL_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 shift_value = extract_shift_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string shift = IMMEDIATE(copy(shift_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SRL %s, %s, %s", rt, rs, shift); + return img_format("SRL %s, %s, 0x%" PRIx64, rt, rs, shift_value); } @@ -14945,17 +14381,17 @@ std::string NMD::SRL_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SRLV(uint64 instruction) +static char *SRLV(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SRLV %s, %s, %s", rd, rs, rt); + return img_format("SRLV %s, %s, %s", rd, rs, rt); } @@ -14969,17 +14405,17 @@ std::string NMD::SRLV(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUB(uint64 instruction) +static char *SUB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUB %s, %s, %s", rd, rs, rt); + return img_format("SUB %s, %s, %s", rd, rs, rt); } @@ -14993,17 +14429,17 @@ std::string NMD::SUB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUB_D(uint64 instruction) +static char *SUB_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("SUB.D %s, %s, %s", fd, fs, ft); + return img_format("SUB.D %s, %s, %s", fd, fs, ft); } @@ -15017,17 +14453,17 @@ std::string NMD::SUB_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUB_S(uint64 instruction) +static char *SUB_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); uint64 fd_value = extract_fd_15_14_13_12_11(instruction); - std::string fd = FPR(copy(fd_value)); - std::string fs = FPR(copy(fs_value)); - std::string ft = FPR(copy(ft_value)); + const char *fd = FPR(fd_value, info); + const char *fs = FPR(fs_value, info); + const char *ft = FPR(ft_value, info); - return img::format("SUB.S %s, %s, %s", fd, fs, ft); + return img_format("SUB.S %s, %s, %s", fd, fs, ft); } @@ -15041,17 +14477,17 @@ std::string NMD::SUB_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBQ_PH(uint64 instruction) +static char *SUBQ_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUBQ.PH %s, %s, %s", rd, rs, rt); + return img_format("SUBQ.PH %s, %s, %s", rd, rs, rt); } @@ -15066,17 +14502,17 @@ std::string NMD::SUBQ_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBQ_S_PH(uint64 instruction) +static char *SUBQ_S_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUBQ_S.PH %s, %s, %s", rd, rs, rt); + return img_format("SUBQ_S.PH %s, %s, %s", rd, rs, rt); } @@ -15091,17 +14527,17 @@ std::string NMD::SUBQ_S_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBQ_S_W(uint64 instruction) +static char *SUBQ_S_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUBQ_S.W %s, %s, %s", rd, rs, rt); + return img_format("SUBQ_S.W %s, %s, %s", rd, rs, rt); } @@ -15116,17 +14552,17 @@ std::string NMD::SUBQ_S_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBQH_PH(uint64 instruction) +static char *SUBQH_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUBQH.PH %s, %s, %s", rd, rs, rt); + return img_format("SUBQH.PH %s, %s, %s", rd, rs, rt); } @@ -15141,17 +14577,17 @@ std::string NMD::SUBQH_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBQH_R_PH(uint64 instruction) +static char *SUBQH_R_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUBQH_R.PH %s, %s, %s", rd, rs, rt); + return img_format("SUBQH_R.PH %s, %s, %s", rd, rs, rt); } @@ -15166,17 +14602,17 @@ std::string NMD::SUBQH_R_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBQH_R_W(uint64 instruction) +static char *SUBQH_R_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUBQH_R.W %s, %s, %s", rd, rs, rt); + return img_format("SUBQH_R.W %s, %s, %s", rd, rs, rt); } @@ -15191,17 +14627,17 @@ std::string NMD::SUBQH_R_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBQH_W(uint64 instruction) +static char *SUBQH_W(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUBQH.W %s, %s, %s", rd, rs, rt); + return img_format("SUBQH.W %s, %s, %s", rd, rs, rt); } @@ -15215,17 +14651,17 @@ std::string NMD::SUBQH_W(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBU_16_(uint64 instruction) +static char *SUBU_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 rd3_value = extract_rd3_3_2_1(instruction); - std::string rd3 = GPR(decode_gpr_gpr3(rd3_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + const char *rd3 = GPR(decode_gpr_gpr3(rd3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); - return img::format("SUBU %s, %s, %s", rd3, rs3, rt3); + return img_format("SUBU %s, %s, %s", rd3, rs3, rt3); } @@ -15239,17 +14675,17 @@ std::string NMD::SUBU_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBU_32_(uint64 instruction) +static char *SUBU_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUBU %s, %s, %s", rd, rs, rt); + return img_format("SUBU %s, %s, %s", rd, rs, rt); } @@ -15263,17 +14699,17 @@ std::string NMD::SUBU_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBU_PH(uint64 instruction) +static char *SUBU_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUBU.PH %s, %s, %s", rd, rs, rt); + return img_format("SUBU.PH %s, %s, %s", rd, rs, rt); } @@ -15287,17 +14723,17 @@ std::string NMD::SUBU_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBU_QB(uint64 instruction) +static char *SUBU_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUBU.QB %s, %s, %s", rd, rs, rt); + return img_format("SUBU.QB %s, %s, %s", rd, rs, rt); } @@ -15312,17 +14748,17 @@ std::string NMD::SUBU_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBU_S_PH(uint64 instruction) +static char *SUBU_S_PH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUBU_S.PH %s, %s, %s", rd, rs, rt); + return img_format("SUBU_S.PH %s, %s, %s", rd, rs, rt); } @@ -15337,17 +14773,17 @@ std::string NMD::SUBU_S_PH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBU_S_QB(uint64 instruction) +static char *SUBU_S_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUBU_S.QB %s, %s, %s", rd, rs, rt); + return img_format("SUBU_S.QB %s, %s, %s", rd, rs, rt); } @@ -15362,17 +14798,17 @@ std::string NMD::SUBU_S_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBUH_QB(uint64 instruction) +static char *SUBUH_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUBUH.QB %s, %s, %s", rd, rs, rt); + return img_format("SUBUH.QB %s, %s, %s", rd, rs, rt); } @@ -15387,17 +14823,17 @@ std::string NMD::SUBUH_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SUBUH_R_QB(uint64 instruction) +static char *SUBUH_R_QB(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SUBUH_R.QB %s, %s, %s", rd, rs, rt); + return img_format("SUBUH_R.QB %s, %s, %s", rd, rs, rt); } @@ -15411,17 +14847,16 @@ std::string NMD::SUBUH_R_QB(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SW_16_(uint64 instruction) +static char *SW_16_(uint64 instruction, Dis_info *info) { uint64 rtz3_value = extract_rtz3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); uint64 u_value = extract_u_3_2_1_0__s2(instruction); - std::string rtz3 = GPR(decode_gpr_gpr3_src_store(rtz3_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + const char *rtz3 = GPR(decode_gpr_gpr3_src_store(rtz3_value, info), info); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); - return img::format("SW %s, %s(%s)", rtz3, u, rs3); + return img_format("SW %s, 0x%" PRIx64 "(%s)", rtz3, u_value, rs3); } @@ -15435,17 +14870,16 @@ std::string NMD::SW_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SW_4X4_(uint64 instruction) +static char *SW_4X4_(uint64 instruction, Dis_info *info) { uint64 rtz4_value = extract_rtz4_9_7_6_5(instruction); uint64 rs4_value = extract_rs4_4_2_1_0(instruction); uint64 u_value = extract_u_3_8__s2(instruction); - std::string rtz4 = GPR(decode_gpr_gpr4_zero(rtz4_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs4 = GPR(decode_gpr_gpr4(rs4_value)); + const char *rtz4 = GPR(decode_gpr_gpr4_zero(rtz4_value, info), info); + const char *rs4 = GPR(decode_gpr_gpr4(rs4_value, info), info); - return img::format("SW %s, %s(%s)", rtz4, u, rs4); + return img_format("SW %s, 0x%" PRIx64 "(%s)", rtz4, u_value, rs4); } @@ -15459,15 +14893,14 @@ std::string NMD::SW_4X4_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SW_GP16_(uint64 instruction) +static char *SW_GP16_(uint64 instruction, Dis_info *info) { uint64 u_value = extract_u_6_5_4_3_2_1_0__s2(instruction); uint64 rtz3_value = extract_rtz3_9_8_7(instruction); - std::string rtz3 = GPR(decode_gpr_gpr3_src_store(rtz3_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rtz3 = GPR(decode_gpr_gpr3_src_store(rtz3_value, info), info); - return img::format("SW %s, %s($%d)", rtz3, u, 28); + return img_format("SW %s, 0x%" PRIx64 "($%d)", rtz3, u_value, 28); } @@ -15481,15 +14914,14 @@ std::string NMD::SW_GP16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SW_GP_(uint64 instruction) +static char *SW_GP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 u_value = extract_u_20_to_2__s2(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("SW %s, %s($%d)", rt, u, 28); + return img_format("SW %s, 0x%" PRIx64 "($%d)", rt, u_value, 28); } @@ -15503,17 +14935,16 @@ std::string NMD::SW_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SW_S9_(uint64 instruction) +static char *SW_S9_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SW %s, %s(%s)", rt, s, rs); + return img_format("SW %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -15527,15 +14958,14 @@ std::string NMD::SW_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SW_SP_(uint64 instruction) +static char *SW_SP_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_9_8_7_6_5(instruction); uint64 u_value = extract_u_4_3_2_1_0__s2(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); - return img::format("SW %s, %s($%d)", rt, u, 29); + return img_format("SW %s, 0x%" PRIx64 "($%d)", rt, u_value, 29); } @@ -15549,17 +14979,16 @@ std::string NMD::SW_SP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SW_U12_(uint64 instruction) +static char *SW_U12_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SW %s, %s(%s)", rt, u, rs); + return img_format("SW %s, 0x%" PRIx64 "(%s)", rt, u_value, rs); } @@ -15573,15 +15002,14 @@ std::string NMD::SW_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SWC1_GP_(uint64 instruction) +static char *SWC1_GP_(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 u_value = extract_u_17_to_2__s2(instruction); - std::string ft = FPR(copy(ft_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *ft = FPR(ft_value, info); - return img::format("SWC1 %s, %s($%d)", ft, u, 28); + return img_format("SWC1 %s, 0x%" PRIx64 "($%d)", ft, u_value, 28); } @@ -15595,17 +15023,16 @@ std::string NMD::SWC1_GP_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SWC1_S9_(uint64 instruction) +static char *SWC1_S9_(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string ft = FPR(copy(ft_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SWC1 %s, %s(%s)", ft, s, rs); + return img_format("SWC1 %s, %" PRId64 "(%s)", ft, s_value, rs); } @@ -15619,17 +15046,16 @@ std::string NMD::SWC1_S9_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SWC1_U12_(uint64 instruction) +static char *SWC1_U12_(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string ft = FPR(copy(ft_value)); - std::string u = IMMEDIATE(copy(u_value)); - std::string rs = GPR(copy(rs_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SWC1 %s, %s(%s)", ft, u, rs); + return img_format("SWC1 %s, 0x%" PRIx64 "(%s)", ft, u_value, rs); } @@ -15643,17 +15069,17 @@ std::string NMD::SWC1_U12_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SWC1X(uint64 instruction) +static char *SWC1X(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ft_value = extract_ft_15_14_13_12_11(instruction); - std::string ft = FPR(copy(ft_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SWC1X %s, %s(%s)", ft, rs, rt); + return img_format("SWC1X %s, %s(%s)", ft, rs, rt); } @@ -15667,17 +15093,17 @@ std::string NMD::SWC1X(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SWC1XS(uint64 instruction) +static char *SWC1XS(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 ft_value = extract_ft_15_14_13_12_11(instruction); - std::string ft = FPR(copy(ft_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *ft = FPR(ft_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SWC1XS %s, %s(%s)", ft, rs, rt); + return img_format("SWC1XS %s, %s(%s)", ft, rs, rt); } @@ -15691,17 +15117,16 @@ std::string NMD::SWC1XS(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SWC2(uint64 instruction) +static char *SWC2(uint64 instruction, Dis_info *info) { uint64 cs_value = extract_cs_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string cs = CPR(copy(cs_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rs = GPR(rs_value, info); - return img::format("SWC2 %s, %s(%s)", cs, s, rs); + return img_format("SWC2 CP%" PRIu64 ", %" PRId64 "(%s)", + cs_value, s_value, rs); } @@ -15715,17 +15140,16 @@ std::string NMD::SWC2(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SWE(uint64 instruction) +static char *SWE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("SWE %s, %s(%s)", rt, s, rs); + return img_format("SWE %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -15739,19 +15163,19 @@ std::string NMD::SWE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SWM(uint64 instruction) +static char *SWM(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); uint64 count3_value = extract_count3_14_13_12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); - std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); + uint64 count3 = encode_count3_from_count(count3_value); - return img::format("SWM %s, %s(%s), %s", rt, s, rs, count3); + return img_format("SWM %s, %" PRId64 "(%s), 0x%" PRIx64, + rt, s_value, rs, count3); } @@ -15765,15 +15189,15 @@ std::string NMD::SWM(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SWPC_48_(uint64 instruction) +static char *SWPC_48_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_41_40_39_38_37(instruction); int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = ADDRESS(encode_s_from_address(s_value), 6); + const char *rt = GPR(rt_value, info); + g_autofree char *s = ADDRESS(s_value, 6, info); - return img::format("SWPC %s, %s", rt, s); + return img_format("SWPC %s, %s", rt, s); } @@ -15787,17 +15211,17 @@ std::string NMD::SWPC_48_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SWX(uint64 instruction) +static char *SWX(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SWX %s, %s(%s)", rd, rs, rt); + return img_format("SWX %s, %s(%s)", rd, rs, rt); } @@ -15811,17 +15235,17 @@ std::string NMD::SWX(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SWXS(uint64 instruction) +static char *SWXS(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("SWXS %s, %s(%s)", rd, rs, rt); + return img_format("SWXS %s, %s(%s)", rd, rs, rt); } @@ -15835,13 +15259,12 @@ std::string NMD::SWXS(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SYNC(uint64 instruction) +static char *SYNC(uint64 instruction, Dis_info *info) { uint64 stype_value = extract_stype_20_19_18_17_16(instruction); - std::string stype = IMMEDIATE(copy(stype_value)); - return img::format("SYNC %s", stype); + return img_format("SYNC 0x%" PRIx64, stype_value); } @@ -15855,15 +15278,14 @@ std::string NMD::SYNC(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SYNCI(uint64 instruction) +static char *SYNCI(uint64 instruction, Dis_info *info) { uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rs = GPR(rs_value, info); - return img::format("SYNCI %s(%s)", s, rs); + return img_format("SYNCI %" PRId64 "(%s)", s_value, rs); } @@ -15877,15 +15299,14 @@ std::string NMD::SYNCI(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SYNCIE(uint64 instruction) +static char *SYNCIE(uint64 instruction, Dis_info *info) { uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rs = GPR(rs_value, info); - return img::format("SYNCIE %s(%s)", s, rs); + return img_format("SYNCIE %" PRId64 "(%s)", s_value, rs); } @@ -15899,13 +15320,12 @@ std::string NMD::SYNCIE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::SYSCALL_16_(uint64 instruction) +static char *SYSCALL_16_(uint64 instruction, Dis_info *info) { uint64 code_value = extract_code_1_0(instruction); - std::string code = IMMEDIATE(copy(code_value)); - return img::format("SYSCALL %s", code); + return img_format("SYSCALL 0x%" PRIx64, code_value); } @@ -15917,13 +15337,12 @@ std::string NMD::SYSCALL_16_(uint64 instruction) * 00000000000010 * code ------------------ */ -std::string NMD::SYSCALL_32_(uint64 instruction) +static char *SYSCALL_32_(uint64 instruction, Dis_info *info) { uint64 code_value = extract_code_17_to_0(instruction); - std::string code = IMMEDIATE(copy(code_value)); - return img::format("SYSCALL %s", code); + return img_format("SYSCALL 0x%" PRIx64, code_value); } @@ -15937,15 +15356,15 @@ std::string NMD::SYSCALL_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TEQ(uint64 instruction) +static char *TEQ(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("TEQ %s, %s", rs, rt); + return img_format("TEQ %s, %s", rs, rt); } @@ -15959,11 +15378,11 @@ std::string NMD::TEQ(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TLBGINV(uint64 instruction) +static char *TLBGINV(uint64 instruction, Dis_info *info) { (void)instruction; - return "TLBGINV "; + return g_strdup("TLBGINV "); } @@ -15977,11 +15396,11 @@ std::string NMD::TLBGINV(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TLBGINVF(uint64 instruction) +static char *TLBGINVF(uint64 instruction, Dis_info *info) { (void)instruction; - return "TLBGINVF "; + return g_strdup("TLBGINVF "); } @@ -15995,11 +15414,11 @@ std::string NMD::TLBGINVF(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TLBGP(uint64 instruction) +static char *TLBGP(uint64 instruction, Dis_info *info) { (void)instruction; - return "TLBGP "; + return g_strdup("TLBGP "); } @@ -16013,11 +15432,11 @@ std::string NMD::TLBGP(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TLBGR(uint64 instruction) +static char *TLBGR(uint64 instruction, Dis_info *info) { (void)instruction; - return "TLBGR "; + return g_strdup("TLBGR "); } @@ -16031,11 +15450,11 @@ std::string NMD::TLBGR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TLBGWI(uint64 instruction) +static char *TLBGWI(uint64 instruction, Dis_info *info) { (void)instruction; - return "TLBGWI "; + return g_strdup("TLBGWI "); } @@ -16049,11 +15468,11 @@ std::string NMD::TLBGWI(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TLBGWR(uint64 instruction) +static char *TLBGWR(uint64 instruction, Dis_info *info) { (void)instruction; - return "TLBGWR "; + return g_strdup("TLBGWR "); } @@ -16067,11 +15486,11 @@ std::string NMD::TLBGWR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TLBINV(uint64 instruction) +static char *TLBINV(uint64 instruction, Dis_info *info) { (void)instruction; - return "TLBINV "; + return g_strdup("TLBINV "); } @@ -16085,11 +15504,11 @@ std::string NMD::TLBINV(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TLBINVF(uint64 instruction) +static char *TLBINVF(uint64 instruction, Dis_info *info) { (void)instruction; - return "TLBINVF "; + return g_strdup("TLBINVF "); } @@ -16103,11 +15522,11 @@ std::string NMD::TLBINVF(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TLBP(uint64 instruction) +static char *TLBP(uint64 instruction, Dis_info *info) { (void)instruction; - return "TLBP "; + return g_strdup("TLBP "); } @@ -16121,11 +15540,11 @@ std::string NMD::TLBP(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TLBR(uint64 instruction) +static char *TLBR(uint64 instruction, Dis_info *info) { (void)instruction; - return "TLBR "; + return g_strdup("TLBR "); } @@ -16139,11 +15558,11 @@ std::string NMD::TLBR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TLBWI(uint64 instruction) +static char *TLBWI(uint64 instruction, Dis_info *info) { (void)instruction; - return "TLBWI "; + return g_strdup("TLBWI "); } @@ -16157,11 +15576,11 @@ std::string NMD::TLBWI(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TLBWR(uint64 instruction) +static char *TLBWR(uint64 instruction, Dis_info *info) { (void)instruction; - return "TLBWR "; + return g_strdup("TLBWR "); } @@ -16175,15 +15594,15 @@ std::string NMD::TLBWR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TNE(uint64 instruction) +static char *TNE(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("TNE %s, %s", rs, rt); + return img_format("TNE %s, %s", rs, rt); } @@ -16197,15 +15616,15 @@ std::string NMD::TNE(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TRUNC_L_D(uint64 instruction) +static char *TRUNC_L_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("TRUNC.L.D %s, %s", ft, fs); + return img_format("TRUNC.L.D %s, %s", ft, fs); } @@ -16219,15 +15638,15 @@ std::string NMD::TRUNC_L_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TRUNC_L_S(uint64 instruction) +static char *TRUNC_L_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("TRUNC.L.S %s, %s", ft, fs); + return img_format("TRUNC.L.S %s, %s", ft, fs); } @@ -16241,15 +15660,15 @@ std::string NMD::TRUNC_L_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TRUNC_W_D(uint64 instruction) +static char *TRUNC_W_D(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("TRUNC.W.D %s, %s", ft, fs); + return img_format("TRUNC.W.D %s, %s", ft, fs); } @@ -16263,15 +15682,15 @@ std::string NMD::TRUNC_W_D(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::TRUNC_W_S(uint64 instruction) +static char *TRUNC_W_S(uint64 instruction, Dis_info *info) { uint64 ft_value = extract_ft_25_24_23_22_21(instruction); uint64 fs_value = extract_fs_20_19_18_17_16(instruction); - std::string ft = FPR(copy(ft_value)); - std::string fs = FPR(copy(fs_value)); + const char *ft = FPR(ft_value, info); + const char *fs = FPR(fs_value, info); - return img::format("TRUNC.W.S %s, %s", ft, fs); + return img_format("TRUNC.W.S %s, %s", ft, fs); } @@ -16285,19 +15704,19 @@ std::string NMD::TRUNC_W_S(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::UALDM(uint64 instruction) +static char *UALDM(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); uint64 count3_value = extract_count3_14_13_12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); - std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); + uint64 count3 = encode_count3_from_count(count3_value); - return img::format("UALDM %s, %s(%s), %s", rt, s, rs, count3); + return img_format("UALDM %s, %" PRId64 "(%s), 0x%" PRIx64, + rt, s_value, rs, count3); } @@ -16311,17 +15730,16 @@ std::string NMD::UALDM(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::UALH(uint64 instruction) +static char *UALH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("UALH %s, %s(%s)", rt, s, rs); + return img_format("UALH %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -16335,19 +15753,19 @@ std::string NMD::UALH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::UALWM(uint64 instruction) +static char *UALWM(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); uint64 count3_value = extract_count3_14_13_12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); - std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); + uint64 count3 = encode_count3_from_count(count3_value); - return img::format("UALWM %s, %s(%s), %s", rt, s, rs, count3); + return img_format("UALWM %s, %" PRId64 "(%s), 0x%" PRIx64, + rt, s_value, rs, count3); } @@ -16361,19 +15779,19 @@ std::string NMD::UALWM(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::UASDM(uint64 instruction) +static char *UASDM(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); uint64 count3_value = extract_count3_14_13_12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); - std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); + uint64 count3 = encode_count3_from_count(count3_value); - return img::format("UASDM %s, %s(%s), %s", rt, s, rs, count3); + return img_format("UASDM %s, %" PRId64 "(%s), 0x%" PRIx64, + rt, s_value, rs, count3); } @@ -16387,17 +15805,16 @@ std::string NMD::UASDM(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::UASH(uint64 instruction) +static char *UASH(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("UASH %s, %s(%s)", rt, s, rs); + return img_format("UASH %s, %" PRId64 "(%s)", rt, s_value, rs); } @@ -16411,19 +15828,19 @@ std::string NMD::UASH(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::UASWM(uint64 instruction) +static char *UASWM(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); uint64 count3_value = extract_count3_14_13_12(instruction); - std::string rt = GPR(copy(rt_value)); - std::string s = IMMEDIATE(copy(s_value)); - std::string rs = GPR(copy(rs_value)); - std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); + uint64 count3 = encode_count3_from_count(count3_value); - return img::format("UASWM %s, %s(%s), %s", rt, s, rs, count3); + return img_format("UASWM %s, %" PRId64 "(%s), 0x%" PRIx64, + rt, s_value, rs, count3); } @@ -16437,13 +15854,12 @@ std::string NMD::UASWM(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::UDI(uint64 instruction) +static char *UDI(uint64 instruction, Dis_info *info) { uint64 op_value = extract_op_25_to_3(instruction); - std::string op = IMMEDIATE(copy(op_value)); - return img::format("UDI %s", op); + return img_format("UDI 0x%" PRIx64, op_value); } @@ -16455,13 +15871,12 @@ std::string NMD::UDI(uint64 instruction) * 001000 1100001101111111 * code ---------- */ -std::string NMD::WAIT(uint64 instruction) +static char *WAIT(uint64 instruction, Dis_info *info) { uint64 code_value = extract_code_25_24_23_22_21_20_19_18_17_16(instruction); - std::string code = IMMEDIATE(copy(code_value)); - return img::format("WAIT %s", code); + return img_format("WAIT 0x%" PRIx64, code_value); } @@ -16475,15 +15890,14 @@ std::string NMD::WAIT(uint64 instruction) * rt ----- * mask ------- */ -std::string NMD::WRDSP(uint64 instruction) +static char *WRDSP(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 mask_value = extract_mask_20_19_18_17_16_15_14(instruction); - std::string rt = GPR(copy(rt_value)); - std::string mask = IMMEDIATE(copy(mask_value)); + const char *rt = GPR(rt_value, info); - return img::format("WRDSP %s, %s", rt, mask); + return img_format("WRDSP %s, 0x%" PRIx64, rt, mask_value); } @@ -16497,15 +15911,15 @@ std::string NMD::WRDSP(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::WRPGPR(uint64 instruction) +static char *WRPGPR(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("WRPGPR %s, %s", rt, rs); + return img_format("WRPGPR %s, %s", rt, rs); } @@ -16519,15 +15933,15 @@ std::string NMD::WRPGPR(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::XOR_16_(uint64 instruction) +static char *XOR_16_(uint64 instruction, Dis_info *info) { uint64 rt3_value = extract_rt3_9_8_7(instruction); uint64 rs3_value = extract_rs3_6_5_4(instruction); - std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); - std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + const char *rs3 = GPR(decode_gpr_gpr3(rs3_value, info), info); + const char *rt3 = GPR(decode_gpr_gpr3(rt3_value, info), info); - return img::format("XOR %s, %s", rs3, rt3); + return img_format("XOR %s, %s", rs3, rt3); } @@ -16541,17 +15955,17 @@ std::string NMD::XOR_16_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::XOR_32_(uint64 instruction) +static char *XOR_32_(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 rd_value = extract_rd_15_14_13_12_11(instruction); - std::string rd = GPR(copy(rd_value)); - std::string rs = GPR(copy(rs_value)); - std::string rt = GPR(copy(rt_value)); + const char *rd = GPR(rd_value, info); + const char *rs = GPR(rs_value, info); + const char *rt = GPR(rt_value, info); - return img::format("XOR %s, %s, %s", rd, rs, rt); + return img_format("XOR %s, %s, %s", rd, rs, rt); } @@ -16565,17 +15979,16 @@ std::string NMD::XOR_32_(uint64 instruction) * rs ----- * rd ----- */ -std::string NMD::XORI(uint64 instruction) +static char *XORI(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); - std::string u = IMMEDIATE(copy(u_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("XORI %s, %s, %s", rt, rs, u); + return img_format("XORI %s, %s, 0x%" PRIx64, rt, rs, u_value); } @@ -16588,15 +16001,15 @@ std::string NMD::XORI(uint64 instruction) * rt ----- * rs ----- */ -std::string NMD::YIELD(uint64 instruction) +static char *YIELD(uint64 instruction, Dis_info *info) { uint64 rt_value = extract_rt_25_24_23_22_21(instruction); uint64 rs_value = extract_rs_20_19_18_17_16(instruction); - std::string rt = GPR(copy(rt_value)); - std::string rs = GPR(copy(rs_value)); + const char *rt = GPR(rt_value, info); + const char *rs = GPR(rs_value, info); - return img::format("YIELD %s, %s", rt, rs); + return img_format("YIELD %s, %s", rt, rs); } @@ -16703,83 +16116,128 @@ std::string NMD::YIELD(uint64 instruction) * */ -NMD::Pool NMD::P_SYSCALL[2] = { +typedef enum { + instruction, + call_instruction, + branch_instruction, + return_instruction, + reserved_block, + pool, +} TABLE_ENTRY_TYPE; + +typedef enum { + MIPS64_ = 0x00000001, + XNP_ = 0x00000002, + XMMS_ = 0x00000004, + EVA_ = 0x00000008, + DSP_ = 0x00000010, + MT_ = 0x00000020, + EJTAG_ = 0x00000040, + TLBINV_ = 0x00000080, + CP0_ = 0x00000100, + CP1_ = 0x00000200, + CP2_ = 0x00000400, + UDI_ = 0x00000800, + MCU_ = 0x00001000, + VZ_ = 0x00002000, + TLB_ = 0x00004000, + MVH_ = 0x00008000, + ALL_ATTRIBUTES = 0xffffffffull, +} TABLE_ATTRIBUTE_TYPE; + +typedef bool (*conditional_function)(uint64 instruction); +typedef char * (*disassembly_function)(uint64 instruction, + Dis_info *info); + +typedef struct Pool { + TABLE_ENTRY_TYPE type; + const struct Pool *next_table; + int next_table_size; + int instructions_size; + uint64 mask; + uint64 value; + disassembly_function disassembly; + conditional_function condition; + uint64 attributes; +} Pool; + +static const Pool P_SYSCALL[2] = { { instruction , 0 , 0 , 32, - 0xfffc0000, 0x00080000, &NMD::SYSCALL_32_ , 0, + 0xfffc0000, 0x00080000, &SYSCALL_32_ , 0, 0x0 }, /* SYSCALL[32] */ { instruction , 0 , 0 , 32, - 0xfffc0000, 0x000c0000, &NMD::HYPCALL , 0, + 0xfffc0000, 0x000c0000, &HYPCALL , 0, CP0_ | VZ_ }, /* HYPCALL */ }; -NMD::Pool NMD::P_RI[4] = { +static const Pool P_RI[4] = { { instruction , 0 , 0 , 32, - 0xfff80000, 0x00000000, &NMD::SIGRIE , 0, + 0xfff80000, 0x00000000, &SIGRIE , 0, 0x0 }, /* SIGRIE */ { pool , P_SYSCALL , 2 , 32, 0xfff80000, 0x00080000, 0 , 0, 0x0 }, /* P.SYSCALL */ { instruction , 0 , 0 , 32, - 0xfff80000, 0x00100000, &NMD::BREAK_32_ , 0, + 0xfff80000, 0x00100000, &BREAK_32_ , 0, 0x0 }, /* BREAK[32] */ { instruction , 0 , 0 , 32, - 0xfff80000, 0x00180000, &NMD::SDBBP_32_ , 0, + 0xfff80000, 0x00180000, &SDBBP_32_ , 0, EJTAG_ }, /* SDBBP[32] */ }; -NMD::Pool NMD::P_ADDIU[2] = { +static const Pool P_ADDIU[2] = { { pool , P_RI , 4 , 32, 0xffe00000, 0x00000000, 0 , 0, 0x0 }, /* P.RI */ { instruction , 0 , 0 , 32, - 0xfc000000, 0x00000000, &NMD::ADDIU_32_ , &NMD::ADDIU_32__cond , + 0xfc000000, 0x00000000, &ADDIU_32_ , &ADDIU_32__cond , 0x0 }, /* ADDIU[32] */ }; -NMD::Pool NMD::P_TRAP[2] = { +static const Pool P_TRAP[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000000, &NMD::TEQ , 0, + 0xfc0007ff, 0x20000000, &TEQ , 0, XMMS_ }, /* TEQ */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000400, &NMD::TNE , 0, + 0xfc0007ff, 0x20000400, &TNE , 0, XMMS_ }, /* TNE */ }; -NMD::Pool NMD::P_CMOVE[2] = { +static const Pool P_CMOVE[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000210, &NMD::MOVZ , 0, + 0xfc0007ff, 0x20000210, &MOVZ , 0, 0x0 }, /* MOVZ */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000610, &NMD::MOVN , 0, + 0xfc0007ff, 0x20000610, &MOVN , 0, 0x0 }, /* MOVN */ }; -NMD::Pool NMD::P_D_MT_VPE[2] = { +static const Pool P_D_MT_VPE[2] = { { instruction , 0 , 0 , 32, - 0xfc1f3fff, 0x20010ab0, &NMD::DMT , 0, + 0xfc1f3fff, 0x20010ab0, &DMT , 0, MT_ }, /* DMT */ { instruction , 0 , 0 , 32, - 0xfc1f3fff, 0x20000ab0, &NMD::DVPE , 0, + 0xfc1f3fff, 0x20000ab0, &DVPE , 0, MT_ }, /* DVPE */ }; -NMD::Pool NMD::P_E_MT_VPE[2] = { +static const Pool P_E_MT_VPE[2] = { { instruction , 0 , 0 , 32, - 0xfc1f3fff, 0x20010eb0, &NMD::EMT , 0, + 0xfc1f3fff, 0x20010eb0, &EMT , 0, MT_ }, /* EMT */ { instruction , 0 , 0 , 32, - 0xfc1f3fff, 0x20000eb0, &NMD::EVPE , 0, + 0xfc1f3fff, 0x20000eb0, &EVPE , 0, MT_ }, /* EVPE */ }; -NMD::Pool NMD::_P_MT_VPE[2] = { +static const Pool _P_MT_VPE[2] = { { pool , P_D_MT_VPE , 2 , 32, 0xfc003fff, 0x20000ab0, 0 , 0, 0x0 }, /* P.D_MT_VPE */ @@ -16789,7 +16247,7 @@ NMD::Pool NMD::_P_MT_VPE[2] = { }; -NMD::Pool NMD::P_MT_VPE[8] = { +static const Pool P_MT_VPE[8] = { { reserved_block , 0 , 0 , 32, 0xfc003bff, 0x200002b0, 0 , 0, 0x0 }, /* P.MT_VPE~*(0) */ @@ -16817,38 +16275,38 @@ NMD::Pool NMD::P_MT_VPE[8] = { }; -NMD::Pool NMD::P_DVP[2] = { +static const Pool P_DVP[2] = { { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x20000390, &NMD::DVP , 0, + 0xfc00ffff, 0x20000390, &DVP , 0, 0x0 }, /* DVP */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x20000790, &NMD::EVP , 0, + 0xfc00ffff, 0x20000790, &EVP , 0, 0x0 }, /* EVP */ }; -NMD::Pool NMD::P_SLTU[2] = { +static const Pool P_SLTU[2] = { { pool , P_DVP , 2 , 32, 0xfc00fbff, 0x20000390, 0 , 0, 0x0 }, /* P.DVP */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000390, &NMD::SLTU , &NMD::SLTU_cond , + 0xfc0003ff, 0x20000390, &SLTU , &SLTU_cond , 0x0 }, /* SLTU */ }; -NMD::Pool NMD::_POOL32A0[128] = { +static const Pool _POOL32A0[128] = { { pool , P_TRAP , 2 , 32, 0xfc0003ff, 0x20000000, 0 , 0, 0x0 }, /* P.TRAP */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000008, &NMD::SEB , 0, + 0xfc0003ff, 0x20000008, &SEB , 0, XMMS_ }, /* SEB */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000010, &NMD::SLLV , 0, + 0xfc0003ff, 0x20000010, &SLLV , 0, 0x0 }, /* SLLV */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000018, &NMD::MUL_32_ , 0, + 0xfc0003ff, 0x20000018, &MUL_32_ , 0, 0x0 }, /* MUL[32] */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000020, 0 , 0, @@ -16857,22 +16315,22 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x20000028, 0 , 0, 0x0 }, /* _POOL32A0~*(5) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000030, &NMD::MFC0 , 0, + 0xfc0003ff, 0x20000030, &MFC0 , 0, 0x0 }, /* MFC0 */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000038, &NMD::MFHC0 , 0, + 0xfc0003ff, 0x20000038, &MFHC0 , 0, CP0_ | MVH_ }, /* MFHC0 */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000040, 0 , 0, 0x0 }, /* _POOL32A0~*(8) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000048, &NMD::SEH , 0, + 0xfc0003ff, 0x20000048, &SEH , 0, 0x0 }, /* SEH */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000050, &NMD::SRLV , 0, + 0xfc0003ff, 0x20000050, &SRLV , 0, 0x0 }, /* SRLV */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000058, &NMD::MUH , 0, + 0xfc0003ff, 0x20000058, &MUH , 0, 0x0 }, /* MUH */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000060, 0 , 0, @@ -16881,10 +16339,10 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x20000068, 0 , 0, 0x0 }, /* _POOL32A0~*(13) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000070, &NMD::MTC0 , 0, + 0xfc0003ff, 0x20000070, &MTC0 , 0, CP0_ }, /* MTC0 */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000078, &NMD::MTHC0 , 0, + 0xfc0003ff, 0x20000078, &MTHC0 , 0, CP0_ | MVH_ }, /* MTHC0 */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000080, 0 , 0, @@ -16893,10 +16351,10 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x20000088, 0 , 0, 0x0 }, /* _POOL32A0~*(17) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000090, &NMD::SRAV , 0, + 0xfc0003ff, 0x20000090, &SRAV , 0, 0x0 }, /* SRAV */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000098, &NMD::MULU , 0, + 0xfc0003ff, 0x20000098, &MULU , 0, 0x0 }, /* MULU */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200000a0, 0 , 0, @@ -16905,10 +16363,10 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x200000a8, 0 , 0, 0x0 }, /* _POOL32A0~*(21) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200000b0, &NMD::MFGC0 , 0, + 0xfc0003ff, 0x200000b0, &MFGC0 , 0, CP0_ | VZ_ }, /* MFGC0 */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200000b8, &NMD::MFHGC0 , 0, + 0xfc0003ff, 0x200000b8, &MFHGC0 , 0, CP0_ | VZ_ | MVH_ }, /* MFHGC0 */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200000c0, 0 , 0, @@ -16917,10 +16375,10 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x200000c8, 0 , 0, 0x0 }, /* _POOL32A0~*(25) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200000d0, &NMD::ROTRV , 0, + 0xfc0003ff, 0x200000d0, &ROTRV , 0, 0x0 }, /* ROTRV */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200000d8, &NMD::MUHU , 0, + 0xfc0003ff, 0x200000d8, &MUHU , 0, 0x0 }, /* MUHU */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200000e0, 0 , 0, @@ -16929,10 +16387,10 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x200000e8, 0 , 0, 0x0 }, /* _POOL32A0~*(29) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200000f0, &NMD::MTGC0 , 0, + 0xfc0003ff, 0x200000f0, &MTGC0 , 0, CP0_ | VZ_ }, /* MTGC0 */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200000f8, &NMD::MTHGC0 , 0, + 0xfc0003ff, 0x200000f8, &MTHGC0 , 0, CP0_ | VZ_ | MVH_ }, /* MTHGC0 */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000100, 0 , 0, @@ -16941,10 +16399,10 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x20000108, 0 , 0, 0x0 }, /* _POOL32A0~*(33) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000110, &NMD::ADD , 0, + 0xfc0003ff, 0x20000110, &ADD , 0, XMMS_ }, /* ADD */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000118, &NMD::DIV , 0, + 0xfc0003ff, 0x20000118, &DIV , 0, 0x0 }, /* DIV */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000120, 0 , 0, @@ -16953,7 +16411,7 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x20000128, 0 , 0, 0x0 }, /* _POOL32A0~*(37) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000130, &NMD::DMFC0 , 0, + 0xfc0003ff, 0x20000130, &DMFC0 , 0, CP0_ | MIPS64_ }, /* DMFC0 */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000138, 0 , 0, @@ -16965,10 +16423,10 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x20000148, 0 , 0, 0x0 }, /* _POOL32A0~*(41) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000150, &NMD::ADDU_32_ , 0, + 0xfc0003ff, 0x20000150, &ADDU_32_ , 0, 0x0 }, /* ADDU[32] */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000158, &NMD::MOD , 0, + 0xfc0003ff, 0x20000158, &MOD , 0, 0x0 }, /* MOD */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000160, 0 , 0, @@ -16977,7 +16435,7 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x20000168, 0 , 0, 0x0 }, /* _POOL32A0~*(45) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000170, &NMD::DMTC0 , 0, + 0xfc0003ff, 0x20000170, &DMTC0 , 0, CP0_ | MIPS64_ }, /* DMTC0 */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000178, 0 , 0, @@ -16989,10 +16447,10 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x20000188, 0 , 0, 0x0 }, /* _POOL32A0~*(49) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000190, &NMD::SUB , 0, + 0xfc0003ff, 0x20000190, &SUB , 0, XMMS_ }, /* SUB */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000198, &NMD::DIVU , 0, + 0xfc0003ff, 0x20000198, &DIVU , 0, 0x0 }, /* DIVU */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200001a0, 0 , 0, @@ -17001,22 +16459,22 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x200001a8, 0 , 0, 0x0 }, /* _POOL32A0~*(53) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200001b0, &NMD::DMFGC0 , 0, + 0xfc0003ff, 0x200001b0, &DMFGC0 , 0, CP0_ | MIPS64_ | VZ_}, /* DMFGC0 */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200001b8, 0 , 0, 0x0 }, /* _POOL32A0~*(55) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200001c0, &NMD::RDHWR , 0, + 0xfc0003ff, 0x200001c0, &RDHWR , 0, XMMS_ }, /* RDHWR */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200001c8, 0 , 0, 0x0 }, /* _POOL32A0~*(57) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200001d0, &NMD::SUBU_32_ , 0, + 0xfc0003ff, 0x200001d0, &SUBU_32_ , 0, 0x0 }, /* SUBU[32] */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200001d8, &NMD::MODU , 0, + 0xfc0003ff, 0x200001d8, &MODU , 0, 0x0 }, /* MODU */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200001e0, 0 , 0, @@ -17025,7 +16483,7 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x200001e8, 0 , 0, 0x0 }, /* _POOL32A0~*(61) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200001f0, &NMD::DMTGC0 , 0, + 0xfc0003ff, 0x200001f0, &DMTGC0 , 0, CP0_ | MIPS64_ | VZ_}, /* DMTGC0 */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200001f8, 0 , 0, @@ -17046,13 +16504,13 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x20000220, 0 , 0, 0x0 }, /* _POOL32A0~*(68) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000228, &NMD::FORK , 0, + 0xfc0003ff, 0x20000228, &FORK , 0, MT_ }, /* FORK */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000230, &NMD::MFTR , 0, + 0xfc0003ff, 0x20000230, &MFTR , 0, MT_ }, /* MFTR */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000238, &NMD::MFHTR , 0, + 0xfc0003ff, 0x20000238, &MFHTR , 0, MT_ }, /* MFHTR */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000240, 0 , 0, @@ -17061,7 +16519,7 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x20000248, 0 , 0, 0x0 }, /* _POOL32A0~*(73) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000250, &NMD::AND_32_ , 0, + 0xfc0003ff, 0x20000250, &AND_32_ , 0, 0x0 }, /* AND[32] */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000258, 0 , 0, @@ -17070,13 +16528,13 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x20000260, 0 , 0, 0x0 }, /* _POOL32A0~*(76) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000268, &NMD::YIELD , 0, + 0xfc0003ff, 0x20000268, &YIELD , 0, MT_ }, /* YIELD */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000270, &NMD::MTTR , 0, + 0xfc0003ff, 0x20000270, &MTTR , 0, MT_ }, /* MTTR */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000278, &NMD::MTHTR , 0, + 0xfc0003ff, 0x20000278, &MTHTR , 0, MT_ }, /* MTHTR */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000280, 0 , 0, @@ -17085,7 +16543,7 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x20000288, 0 , 0, 0x0 }, /* _POOL32A0~*(81) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000290, &NMD::OR_32_ , 0, + 0xfc0003ff, 0x20000290, &OR_32_ , 0, 0x0 }, /* OR[32] */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000298, 0 , 0, @@ -17109,7 +16567,7 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x200002c8, 0 , 0, 0x0 }, /* _POOL32A0~*(89) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200002d0, &NMD::NOR , 0, + 0xfc0003ff, 0x200002d0, &NOR , 0, 0x0 }, /* NOR */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200002d8, 0 , 0, @@ -17133,7 +16591,7 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x20000308, 0 , 0, 0x0 }, /* _POOL32A0~*(97) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000310, &NMD::XOR_32_ , 0, + 0xfc0003ff, 0x20000310, &XOR_32_ , 0, 0x0 }, /* XOR[32] */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000318, 0 , 0, @@ -17157,7 +16615,7 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x20000348, 0 , 0, 0x0 }, /* _POOL32A0~*(105) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000350, &NMD::SLT , 0, + 0xfc0003ff, 0x20000350, &SLT , 0, 0x0 }, /* SLT */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000358, 0 , 0, @@ -17205,7 +16663,7 @@ NMD::Pool NMD::_POOL32A0[128] = { 0xfc0003ff, 0x200003c8, 0 , 0, 0x0 }, /* _POOL32A0~*(121) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200003d0, &NMD::SOV , 0, + 0xfc0003ff, 0x200003d0, &SOV , 0, 0x0 }, /* SOV */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200003d8, 0 , 0, @@ -17225,185 +16683,185 @@ NMD::Pool NMD::_POOL32A0[128] = { }; -NMD::Pool NMD::ADDQ__S__PH[2] = { +static const Pool ADDQ__S__PH[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000000d, &NMD::ADDQ_PH , 0, + 0xfc0007ff, 0x2000000d, &ADDQ_PH , 0, DSP_ }, /* ADDQ.PH */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000040d, &NMD::ADDQ_S_PH , 0, + 0xfc0007ff, 0x2000040d, &ADDQ_S_PH , 0, DSP_ }, /* ADDQ_S.PH */ }; -NMD::Pool NMD::MUL__S__PH[2] = { +static const Pool MUL__S__PH[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000002d, &NMD::MUL_PH , 0, + 0xfc0007ff, 0x2000002d, &MUL_PH , 0, DSP_ }, /* MUL.PH */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000042d, &NMD::MUL_S_PH , 0, + 0xfc0007ff, 0x2000042d, &MUL_S_PH , 0, DSP_ }, /* MUL_S.PH */ }; -NMD::Pool NMD::ADDQH__R__PH[2] = { +static const Pool ADDQH__R__PH[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000004d, &NMD::ADDQH_PH , 0, + 0xfc0007ff, 0x2000004d, &ADDQH_PH , 0, DSP_ }, /* ADDQH.PH */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000044d, &NMD::ADDQH_R_PH , 0, + 0xfc0007ff, 0x2000044d, &ADDQH_R_PH , 0, DSP_ }, /* ADDQH_R.PH */ }; -NMD::Pool NMD::ADDQH__R__W[2] = { +static const Pool ADDQH__R__W[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000008d, &NMD::ADDQH_W , 0, + 0xfc0007ff, 0x2000008d, &ADDQH_W , 0, DSP_ }, /* ADDQH.W */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000048d, &NMD::ADDQH_R_W , 0, + 0xfc0007ff, 0x2000048d, &ADDQH_R_W , 0, DSP_ }, /* ADDQH_R.W */ }; -NMD::Pool NMD::ADDU__S__QB[2] = { +static const Pool ADDU__S__QB[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x200000cd, &NMD::ADDU_QB , 0, + 0xfc0007ff, 0x200000cd, &ADDU_QB , 0, DSP_ }, /* ADDU.QB */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x200004cd, &NMD::ADDU_S_QB , 0, + 0xfc0007ff, 0x200004cd, &ADDU_S_QB , 0, DSP_ }, /* ADDU_S.QB */ }; -NMD::Pool NMD::ADDU__S__PH[2] = { +static const Pool ADDU__S__PH[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000010d, &NMD::ADDU_PH , 0, + 0xfc0007ff, 0x2000010d, &ADDU_PH , 0, DSP_ }, /* ADDU.PH */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000050d, &NMD::ADDU_S_PH , 0, + 0xfc0007ff, 0x2000050d, &ADDU_S_PH , 0, DSP_ }, /* ADDU_S.PH */ }; -NMD::Pool NMD::ADDUH__R__QB[2] = { +static const Pool ADDUH__R__QB[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000014d, &NMD::ADDUH_QB , 0, + 0xfc0007ff, 0x2000014d, &ADDUH_QB , 0, DSP_ }, /* ADDUH.QB */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000054d, &NMD::ADDUH_R_QB , 0, + 0xfc0007ff, 0x2000054d, &ADDUH_R_QB , 0, DSP_ }, /* ADDUH_R.QB */ }; -NMD::Pool NMD::SHRAV__R__PH[2] = { +static const Pool SHRAV__R__PH[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000018d, &NMD::SHRAV_PH , 0, + 0xfc0007ff, 0x2000018d, &SHRAV_PH , 0, DSP_ }, /* SHRAV.PH */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000058d, &NMD::SHRAV_R_PH , 0, + 0xfc0007ff, 0x2000058d, &SHRAV_R_PH , 0, DSP_ }, /* SHRAV_R.PH */ }; -NMD::Pool NMD::SHRAV__R__QB[2] = { +static const Pool SHRAV__R__QB[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x200001cd, &NMD::SHRAV_QB , 0, + 0xfc0007ff, 0x200001cd, &SHRAV_QB , 0, DSP_ }, /* SHRAV.QB */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x200005cd, &NMD::SHRAV_R_QB , 0, + 0xfc0007ff, 0x200005cd, &SHRAV_R_QB , 0, DSP_ }, /* SHRAV_R.QB */ }; -NMD::Pool NMD::SUBQ__S__PH[2] = { +static const Pool SUBQ__S__PH[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000020d, &NMD::SUBQ_PH , 0, + 0xfc0007ff, 0x2000020d, &SUBQ_PH , 0, DSP_ }, /* SUBQ.PH */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000060d, &NMD::SUBQ_S_PH , 0, + 0xfc0007ff, 0x2000060d, &SUBQ_S_PH , 0, DSP_ }, /* SUBQ_S.PH */ }; -NMD::Pool NMD::SUBQH__R__PH[2] = { +static const Pool SUBQH__R__PH[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000024d, &NMD::SUBQH_PH , 0, + 0xfc0007ff, 0x2000024d, &SUBQH_PH , 0, DSP_ }, /* SUBQH.PH */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000064d, &NMD::SUBQH_R_PH , 0, + 0xfc0007ff, 0x2000064d, &SUBQH_R_PH , 0, DSP_ }, /* SUBQH_R.PH */ }; -NMD::Pool NMD::SUBQH__R__W[2] = { +static const Pool SUBQH__R__W[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000028d, &NMD::SUBQH_W , 0, + 0xfc0007ff, 0x2000028d, &SUBQH_W , 0, DSP_ }, /* SUBQH.W */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000068d, &NMD::SUBQH_R_W , 0, + 0xfc0007ff, 0x2000068d, &SUBQH_R_W , 0, DSP_ }, /* SUBQH_R.W */ }; -NMD::Pool NMD::SUBU__S__QB[2] = { +static const Pool SUBU__S__QB[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x200002cd, &NMD::SUBU_QB , 0, + 0xfc0007ff, 0x200002cd, &SUBU_QB , 0, DSP_ }, /* SUBU.QB */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x200006cd, &NMD::SUBU_S_QB , 0, + 0xfc0007ff, 0x200006cd, &SUBU_S_QB , 0, DSP_ }, /* SUBU_S.QB */ }; -NMD::Pool NMD::SUBU__S__PH[2] = { +static const Pool SUBU__S__PH[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000030d, &NMD::SUBU_PH , 0, + 0xfc0007ff, 0x2000030d, &SUBU_PH , 0, DSP_ }, /* SUBU.PH */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000070d, &NMD::SUBU_S_PH , 0, + 0xfc0007ff, 0x2000070d, &SUBU_S_PH , 0, DSP_ }, /* SUBU_S.PH */ }; -NMD::Pool NMD::SHRA__R__PH[2] = { +static const Pool SHRA__R__PH[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000335, &NMD::SHRA_PH , 0, + 0xfc0007ff, 0x20000335, &SHRA_PH , 0, DSP_ }, /* SHRA.PH */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000735, &NMD::SHRA_R_PH , 0, + 0xfc0007ff, 0x20000735, &SHRA_R_PH , 0, DSP_ }, /* SHRA_R.PH */ }; -NMD::Pool NMD::SUBUH__R__QB[2] = { +static const Pool SUBUH__R__QB[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000034d, &NMD::SUBUH_QB , 0, + 0xfc0007ff, 0x2000034d, &SUBUH_QB , 0, DSP_ }, /* SUBUH.QB */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000074d, &NMD::SUBUH_R_QB , 0, + 0xfc0007ff, 0x2000074d, &SUBUH_R_QB , 0, DSP_ }, /* SUBUH_R.QB */ }; -NMD::Pool NMD::SHLLV__S__PH[2] = { +static const Pool SHLLV__S__PH[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000038d, &NMD::SHLLV_PH , 0, + 0xfc0007ff, 0x2000038d, &SHLLV_PH , 0, DSP_ }, /* SHLLV.PH */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x2000078d, &NMD::SHLLV_S_PH , 0, + 0xfc0007ff, 0x2000078d, &SHLLV_S_PH , 0, DSP_ }, /* SHLLV_S.PH */ }; -NMD::Pool NMD::SHLL__S__PH[4] = { +static const Pool SHLL__S__PH[4] = { { instruction , 0 , 0 , 32, - 0xfc000fff, 0x200003b5, &NMD::SHLL_PH , 0, + 0xfc000fff, 0x200003b5, &SHLL_PH , 0, DSP_ }, /* SHLL.PH */ { reserved_block , 0 , 0 , 32, 0xfc000fff, 0x200007b5, 0 , 0, 0x0 }, /* SHLL[_S].PH~*(1) */ { instruction , 0 , 0 , 32, - 0xfc000fff, 0x20000bb5, &NMD::SHLL_S_PH , 0, + 0xfc000fff, 0x20000bb5, &SHLL_S_PH , 0, DSP_ }, /* SHLL_S.PH */ { reserved_block , 0 , 0 , 32, 0xfc000fff, 0x20000fb5, 0 , 0, @@ -17411,19 +16869,19 @@ NMD::Pool NMD::SHLL__S__PH[4] = { }; -NMD::Pool NMD::PRECR_SRA__R__PH_W[2] = { +static const Pool PRECR_SRA__R__PH_W[2] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x200003cd, &NMD::PRECR_SRA_PH_W , 0, + 0xfc0007ff, 0x200003cd, &PRECR_SRA_PH_W , 0, DSP_ }, /* PRECR_SRA.PH.W */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x200007cd, &NMD::PRECR_SRA_R_PH_W , 0, + 0xfc0007ff, 0x200007cd, &PRECR_SRA_R_PH_W , 0, DSP_ }, /* PRECR_SRA_R.PH.W */ }; -NMD::Pool NMD::_POOL32A5[128] = { +static const Pool _POOL32A5[128] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000005, &NMD::CMP_EQ_PH , 0, + 0xfc0003ff, 0x20000005, &CMP_EQ_PH , 0, DSP_ }, /* CMP.EQ.PH */ { pool , ADDQ__S__PH , 2 , 32, 0xfc0003ff, 0x2000000d, 0 , 0, @@ -17432,10 +16890,10 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x20000015, 0 , 0, 0x0 }, /* _POOL32A5~*(2) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x2000001d, &NMD::SHILO , 0, + 0xfc0003ff, 0x2000001d, &SHILO , 0, DSP_ }, /* SHILO */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000025, &NMD::MULEQ_S_W_PHL , 0, + 0xfc0003ff, 0x20000025, &MULEQ_S_W_PHL , 0, DSP_ }, /* MULEQ_S.W.PHL */ { pool , MUL__S__PH , 2 , 32, 0xfc0003ff, 0x2000002d, 0 , 0, @@ -17444,10 +16902,10 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x20000035, 0 , 0, 0x0 }, /* _POOL32A5~*(6) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x2000003d, &NMD::REPL_PH , 0, + 0xfc0003ff, 0x2000003d, &REPL_PH , 0, DSP_ }, /* REPL.PH */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000045, &NMD::CMP_LT_PH , 0, + 0xfc0003ff, 0x20000045, &CMP_LT_PH , 0, DSP_ }, /* CMP.LT.PH */ { pool , ADDQH__R__PH , 2 , 32, 0xfc0003ff, 0x2000004d, 0 , 0, @@ -17459,10 +16917,10 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x2000005d, 0 , 0, 0x0 }, /* _POOL32A5~*(11) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000065, &NMD::MULEQ_S_W_PHR , 0, + 0xfc0003ff, 0x20000065, &MULEQ_S_W_PHR , 0, DSP_ }, /* MULEQ_S.W.PHR */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x2000006d, &NMD::PRECR_QB_PH , 0, + 0xfc0003ff, 0x2000006d, &PRECR_QB_PH , 0, DSP_ }, /* PRECR.QB.PH */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000075, 0 , 0, @@ -17471,13 +16929,13 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x2000007d, 0 , 0, 0x0 }, /* _POOL32A5~*(15) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000085, &NMD::CMP_LE_PH , 0, + 0xfc0003ff, 0x20000085, &CMP_LE_PH , 0, DSP_ }, /* CMP.LE.PH */ { pool , ADDQH__R__W , 2 , 32, 0xfc0003ff, 0x2000008d, 0 , 0, 0x0 }, /* ADDQH[_R].W */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000095, &NMD::MULEU_S_PH_QBL , 0, + 0xfc0003ff, 0x20000095, &MULEU_S_PH_QBL , 0, DSP_ }, /* MULEU_S.PH.QBL */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x2000009d, 0 , 0, @@ -17486,7 +16944,7 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x200000a5, 0 , 0, 0x0 }, /* _POOL32A5~*(20) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200000ad, &NMD::PRECRQ_QB_PH , 0, + 0xfc0003ff, 0x200000ad, &PRECRQ_QB_PH , 0, DSP_ }, /* PRECRQ.QB.PH */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200000b5, 0 , 0, @@ -17495,13 +16953,13 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x200000bd, 0 , 0, 0x0 }, /* _POOL32A5~*(23) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200000c5, &NMD::CMPGU_EQ_QB , 0, + 0xfc0003ff, 0x200000c5, &CMPGU_EQ_QB , 0, DSP_ }, /* CMPGU.EQ.QB */ { pool , ADDU__S__QB , 2 , 32, 0xfc0003ff, 0x200000cd, 0 , 0, 0x0 }, /* ADDU[_S].QB */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200000d5, &NMD::MULEU_S_PH_QBR , 0, + 0xfc0003ff, 0x200000d5, &MULEU_S_PH_QBR , 0, DSP_ }, /* MULEU_S.PH.QBR */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200000dd, 0 , 0, @@ -17510,7 +16968,7 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x200000e5, 0 , 0, 0x0 }, /* _POOL32A5~*(28) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200000ed, &NMD::PRECRQ_PH_W , 0, + 0xfc0003ff, 0x200000ed, &PRECRQ_PH_W , 0, DSP_ }, /* PRECRQ.PH.W */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200000f5, 0 , 0, @@ -17519,13 +16977,13 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x200000fd, 0 , 0, 0x0 }, /* _POOL32A5~*(31) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000105, &NMD::CMPGU_LT_QB , 0, + 0xfc0003ff, 0x20000105, &CMPGU_LT_QB , 0, DSP_ }, /* CMPGU.LT.QB */ { pool , ADDU__S__PH , 2 , 32, 0xfc0003ff, 0x2000010d, 0 , 0, 0x0 }, /* ADDU[_S].PH */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000115, &NMD::MULQ_RS_PH , 0, + 0xfc0003ff, 0x20000115, &MULQ_RS_PH , 0, DSP_ }, /* MULQ_RS.PH */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x2000011d, 0 , 0, @@ -17534,7 +16992,7 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x20000125, 0 , 0, 0x0 }, /* _POOL32A5~*(36) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x2000012d, &NMD::PRECRQ_RS_PH_W , 0, + 0xfc0003ff, 0x2000012d, &PRECRQ_RS_PH_W , 0, DSP_ }, /* PRECRQ_RS.PH.W */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000135, 0 , 0, @@ -17543,13 +17001,13 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x2000013d, 0 , 0, 0x0 }, /* _POOL32A5~*(39) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000145, &NMD::CMPGU_LE_QB , 0, + 0xfc0003ff, 0x20000145, &CMPGU_LE_QB , 0, DSP_ }, /* CMPGU.LE.QB */ { pool , ADDUH__R__QB , 2 , 32, 0xfc0003ff, 0x2000014d, 0 , 0, 0x0 }, /* ADDUH[_R].QB */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000155, &NMD::MULQ_S_PH , 0, + 0xfc0003ff, 0x20000155, &MULQ_S_PH , 0, DSP_ }, /* MULQ_S.PH */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x2000015d, 0 , 0, @@ -17558,7 +17016,7 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x20000165, 0 , 0, 0x0 }, /* _POOL32A5~*(44) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x2000016d, &NMD::PRECRQU_S_QB_PH , 0, + 0xfc0003ff, 0x2000016d, &PRECRQU_S_QB_PH , 0, DSP_ }, /* PRECRQU_S.QB.PH */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000175, 0 , 0, @@ -17567,13 +17025,13 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x2000017d, 0 , 0, 0x0 }, /* _POOL32A5~*(47) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000185, &NMD::CMPGDU_EQ_QB , 0, + 0xfc0003ff, 0x20000185, &CMPGDU_EQ_QB , 0, DSP_ }, /* CMPGDU.EQ.QB */ { pool , SHRAV__R__PH , 2 , 32, 0xfc0003ff, 0x2000018d, 0 , 0, 0x0 }, /* SHRAV[_R].PH */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000195, &NMD::MULQ_RS_W , 0, + 0xfc0003ff, 0x20000195, &MULQ_RS_W , 0, DSP_ }, /* MULQ_RS.W */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x2000019d, 0 , 0, @@ -17582,7 +17040,7 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x200001a5, 0 , 0, 0x0 }, /* _POOL32A5~*(52) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200001ad, &NMD::PACKRL_PH , 0, + 0xfc0003ff, 0x200001ad, &PACKRL_PH , 0, DSP_ }, /* PACKRL.PH */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200001b5, 0 , 0, @@ -17591,13 +17049,13 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x200001bd, 0 , 0, 0x0 }, /* _POOL32A5~*(55) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200001c5, &NMD::CMPGDU_LT_QB , 0, + 0xfc0003ff, 0x200001c5, &CMPGDU_LT_QB , 0, DSP_ }, /* CMPGDU.LT.QB */ { pool , SHRAV__R__QB , 2 , 32, 0xfc0003ff, 0x200001cd, 0 , 0, 0x0 }, /* SHRAV[_R].QB */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200001d5, &NMD::MULQ_S_W , 0, + 0xfc0003ff, 0x200001d5, &MULQ_S_W , 0, DSP_ }, /* MULQ_S.W */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200001dd, 0 , 0, @@ -17606,7 +17064,7 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x200001e5, 0 , 0, 0x0 }, /* _POOL32A5~*(60) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200001ed, &NMD::PICK_QB , 0, + 0xfc0003ff, 0x200001ed, &PICK_QB , 0, DSP_ }, /* PICK.QB */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200001f5, 0 , 0, @@ -17615,13 +17073,13 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x200001fd, 0 , 0, 0x0 }, /* _POOL32A5~*(63) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000205, &NMD::CMPGDU_LE_QB , 0, + 0xfc0003ff, 0x20000205, &CMPGDU_LE_QB , 0, DSP_ }, /* CMPGDU.LE.QB */ { pool , SUBQ__S__PH , 2 , 32, 0xfc0003ff, 0x2000020d, 0 , 0, 0x0 }, /* SUBQ[_S].PH */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000215, &NMD::APPEND , 0, + 0xfc0003ff, 0x20000215, &APPEND , 0, DSP_ }, /* APPEND */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x2000021d, 0 , 0, @@ -17630,7 +17088,7 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x20000225, 0 , 0, 0x0 }, /* _POOL32A5~*(68) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x2000022d, &NMD::PICK_PH , 0, + 0xfc0003ff, 0x2000022d, &PICK_PH , 0, DSP_ }, /* PICK.PH */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x20000235, 0 , 0, @@ -17639,13 +17097,13 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x2000023d, 0 , 0, 0x0 }, /* _POOL32A5~*(71) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000245, &NMD::CMPU_EQ_QB , 0, + 0xfc0003ff, 0x20000245, &CMPU_EQ_QB , 0, DSP_ }, /* CMPU.EQ.QB */ { pool , SUBQH__R__PH , 2 , 32, 0xfc0003ff, 0x2000024d, 0 , 0, 0x0 }, /* SUBQH[_R].PH */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000255, &NMD::PREPEND , 0, + 0xfc0003ff, 0x20000255, &PREPEND , 0, DSP_ }, /* PREPEND */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x2000025d, 0 , 0, @@ -17663,13 +17121,13 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x2000027d, 0 , 0, 0x0 }, /* _POOL32A5~*(79) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000285, &NMD::CMPU_LT_QB , 0, + 0xfc0003ff, 0x20000285, &CMPU_LT_QB , 0, DSP_ }, /* CMPU.LT.QB */ { pool , SUBQH__R__W , 2 , 32, 0xfc0003ff, 0x2000028d, 0 , 0, 0x0 }, /* SUBQH[_R].W */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000295, &NMD::MODSUB , 0, + 0xfc0003ff, 0x20000295, &MODSUB , 0, DSP_ }, /* MODSUB */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x2000029d, 0 , 0, @@ -17687,13 +17145,13 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x200002bd, 0 , 0, 0x0 }, /* _POOL32A5~*(87) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200002c5, &NMD::CMPU_LE_QB , 0, + 0xfc0003ff, 0x200002c5, &CMPU_LE_QB , 0, DSP_ }, /* CMPU.LE.QB */ { pool , SUBU__S__QB , 2 , 32, 0xfc0003ff, 0x200002cd, 0 , 0, 0x0 }, /* SUBU[_S].QB */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200002d5, &NMD::SHRAV_R_W , 0, + 0xfc0003ff, 0x200002d5, &SHRAV_R_W , 0, DSP_ }, /* SHRAV_R.W */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200002dd, 0 , 0, @@ -17705,19 +17163,19 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x200002ed, 0 , 0, 0x0 }, /* _POOL32A5~*(93) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200002f5, &NMD::SHRA_R_W , 0, + 0xfc0003ff, 0x200002f5, &SHRA_R_W , 0, DSP_ }, /* SHRA_R.W */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200002fd, 0 , 0, 0x0 }, /* _POOL32A5~*(95) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000305, &NMD::ADDQ_S_W , 0, + 0xfc0003ff, 0x20000305, &ADDQ_S_W , 0, DSP_ }, /* ADDQ_S.W */ { pool , SUBU__S__PH , 2 , 32, 0xfc0003ff, 0x2000030d, 0 , 0, 0x0 }, /* SUBU[_S].PH */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000315, &NMD::SHRLV_PH , 0, + 0xfc0003ff, 0x20000315, &SHRLV_PH , 0, DSP_ }, /* SHRLV.PH */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x2000031d, 0 , 0, @@ -17735,13 +17193,13 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x2000033d, 0 , 0, 0x0 }, /* _POOL32A5~*(103) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000345, &NMD::SUBQ_S_W , 0, + 0xfc0003ff, 0x20000345, &SUBQ_S_W , 0, DSP_ }, /* SUBQ_S.W */ { pool , SUBUH__R__QB , 2 , 32, 0xfc0003ff, 0x2000034d, 0 , 0, 0x0 }, /* SUBUH[_R].QB */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000355, &NMD::SHRLV_QB , 0, + 0xfc0003ff, 0x20000355, &SHRLV_QB , 0, DSP_ }, /* SHRLV.QB */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x2000035d, 0 , 0, @@ -17759,13 +17217,13 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x2000037d, 0 , 0, 0x0 }, /* _POOL32A5~*(111) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000385, &NMD::ADDSC , 0, + 0xfc0003ff, 0x20000385, &ADDSC , 0, DSP_ }, /* ADDSC */ { pool , SHLLV__S__PH , 2 , 32, 0xfc0003ff, 0x2000038d, 0 , 0, 0x0 }, /* SHLLV[_S].PH */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x20000395, &NMD::SHLLV_QB , 0, + 0xfc0003ff, 0x20000395, &SHLLV_QB , 0, DSP_ }, /* SHLLV.QB */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x2000039d, 0 , 0, @@ -17783,13 +17241,13 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x200003bd, 0 , 0, 0x0 }, /* _POOL32A5~*(119) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200003c5, &NMD::ADDWC , 0, + 0xfc0003ff, 0x200003c5, &ADDWC , 0, DSP_ }, /* ADDWC */ { pool , PRECR_SRA__R__PH_W , 2 , 32, 0xfc0003ff, 0x200003cd, 0 , 0, 0x0 }, /* PRECR_SRA[_R].PH.W */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200003d5, &NMD::SHLLV_S_W , 0, + 0xfc0003ff, 0x200003d5, &SHLLV_S_W , 0, DSP_ }, /* SHLLV_S.W */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200003dd, 0 , 0, @@ -17801,7 +17259,7 @@ NMD::Pool NMD::_POOL32A5[128] = { 0xfc0003ff, 0x200003ed, 0 , 0, 0x0 }, /* _POOL32A5~*(125) */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0x200003f5, &NMD::SHLL_S_W , 0, + 0xfc0003ff, 0x200003f5, &SHLL_S_W , 0, DSP_ }, /* SHLL_S.W */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0x200003fd, 0 , 0, @@ -17809,59 +17267,59 @@ NMD::Pool NMD::_POOL32A5[128] = { }; -NMD::Pool NMD::PP_LSX[16] = { +static const Pool PP_LSX[16] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000007, &NMD::LBX , 0, + 0xfc0007ff, 0x20000007, &LBX , 0, 0x0 }, /* LBX */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000087, &NMD::SBX , 0, + 0xfc0007ff, 0x20000087, &SBX , 0, XMMS_ }, /* SBX */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000107, &NMD::LBUX , 0, + 0xfc0007ff, 0x20000107, &LBUX , 0, 0x0 }, /* LBUX */ { reserved_block , 0 , 0 , 32, 0xfc0007ff, 0x20000187, 0 , 0, 0x0 }, /* PP.LSX~*(3) */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000207, &NMD::LHX , 0, + 0xfc0007ff, 0x20000207, &LHX , 0, 0x0 }, /* LHX */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000287, &NMD::SHX , 0, + 0xfc0007ff, 0x20000287, &SHX , 0, XMMS_ }, /* SHX */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000307, &NMD::LHUX , 0, + 0xfc0007ff, 0x20000307, &LHUX , 0, 0x0 }, /* LHUX */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000387, &NMD::LWUX , 0, + 0xfc0007ff, 0x20000387, &LWUX , 0, MIPS64_ }, /* LWUX */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000407, &NMD::LWX , 0, + 0xfc0007ff, 0x20000407, &LWX , 0, 0x0 }, /* LWX */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000487, &NMD::SWX , 0, + 0xfc0007ff, 0x20000487, &SWX , 0, XMMS_ }, /* SWX */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000507, &NMD::LWC1X , 0, + 0xfc0007ff, 0x20000507, &LWC1X , 0, CP1_ }, /* LWC1X */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000587, &NMD::SWC1X , 0, + 0xfc0007ff, 0x20000587, &SWC1X , 0, CP1_ }, /* SWC1X */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000607, &NMD::LDX , 0, + 0xfc0007ff, 0x20000607, &LDX , 0, MIPS64_ }, /* LDX */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000687, &NMD::SDX , 0, + 0xfc0007ff, 0x20000687, &SDX , 0, MIPS64_ }, /* SDX */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000707, &NMD::LDC1X , 0, + 0xfc0007ff, 0x20000707, &LDC1X , 0, CP1_ }, /* LDC1X */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000787, &NMD::SDC1X , 0, + 0xfc0007ff, 0x20000787, &SDC1X , 0, CP1_ }, /* SDC1X */ }; -NMD::Pool NMD::PP_LSXS[16] = { +static const Pool PP_LSXS[16] = { { reserved_block , 0 , 0 , 32, 0xfc0007ff, 0x20000047, 0 , 0, 0x0 }, /* PP.LSXS~*(0) */ @@ -17875,45 +17333,45 @@ NMD::Pool NMD::PP_LSXS[16] = { 0xfc0007ff, 0x200001c7, 0 , 0, 0x0 }, /* PP.LSXS~*(3) */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000247, &NMD::LHXS , 0, + 0xfc0007ff, 0x20000247, &LHXS , 0, 0x0 }, /* LHXS */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x200002c7, &NMD::SHXS , 0, + 0xfc0007ff, 0x200002c7, &SHXS , 0, XMMS_ }, /* SHXS */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000347, &NMD::LHUXS , 0, + 0xfc0007ff, 0x20000347, &LHUXS , 0, 0x0 }, /* LHUXS */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x200003c7, &NMD::LWUXS , 0, + 0xfc0007ff, 0x200003c7, &LWUXS , 0, MIPS64_ }, /* LWUXS */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000447, &NMD::LWXS_32_ , 0, + 0xfc0007ff, 0x20000447, &LWXS_32_ , 0, 0x0 }, /* LWXS[32] */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x200004c7, &NMD::SWXS , 0, + 0xfc0007ff, 0x200004c7, &SWXS , 0, XMMS_ }, /* SWXS */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000547, &NMD::LWC1XS , 0, + 0xfc0007ff, 0x20000547, &LWC1XS , 0, CP1_ }, /* LWC1XS */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x200005c7, &NMD::SWC1XS , 0, + 0xfc0007ff, 0x200005c7, &SWC1XS , 0, CP1_ }, /* SWC1XS */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000647, &NMD::LDXS , 0, + 0xfc0007ff, 0x20000647, &LDXS , 0, MIPS64_ }, /* LDXS */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x200006c7, &NMD::SDXS , 0, + 0xfc0007ff, 0x200006c7, &SDXS , 0, MIPS64_ }, /* SDXS */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x20000747, &NMD::LDC1XS , 0, + 0xfc0007ff, 0x20000747, &LDC1XS , 0, CP1_ }, /* LDC1XS */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0x200007c7, &NMD::SDC1XS , 0, + 0xfc0007ff, 0x200007c7, &SDC1XS , 0, CP1_ }, /* SDC1XS */ }; -NMD::Pool NMD::P_LSX[2] = { +static const Pool P_LSX[2] = { { pool , PP_LSX , 16 , 32, 0xfc00007f, 0x20000007, 0 , 0, 0x0 }, /* PP.LSX */ @@ -17923,28 +17381,28 @@ NMD::Pool NMD::P_LSX[2] = { }; -NMD::Pool NMD::POOL32Axf_1_0[4] = { +static const Pool POOL32Axf_1_0[4] = { { instruction , 0 , 0 , 32, - 0xfc003fff, 0x2000007f, &NMD::MFHI_DSP_ , 0, + 0xfc003fff, 0x2000007f, &MFHI_DSP_ , 0, DSP_ }, /* MFHI[DSP] */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x2000107f, &NMD::MFLO_DSP_ , 0, + 0xfc003fff, 0x2000107f, &MFLO_DSP_ , 0, DSP_ }, /* MFLO[DSP] */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x2000207f, &NMD::MTHI_DSP_ , 0, + 0xfc003fff, 0x2000207f, &MTHI_DSP_ , 0, DSP_ }, /* MTHI[DSP] */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x2000307f, &NMD::MTLO_DSP_ , 0, + 0xfc003fff, 0x2000307f, &MTLO_DSP_ , 0, DSP_ }, /* MTLO[DSP] */ }; -NMD::Pool NMD::POOL32Axf_1_1[4] = { +static const Pool POOL32Axf_1_1[4] = { { instruction , 0 , 0 , 32, - 0xfc003fff, 0x2000027f, &NMD::MTHLIP , 0, + 0xfc003fff, 0x2000027f, &MTHLIP , 0, DSP_ }, /* MTHLIP */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x2000127f, &NMD::SHILOV , 0, + 0xfc003fff, 0x2000127f, &SHILOV , 0, DSP_ }, /* SHILOV */ { reserved_block , 0 , 0 , 32, 0xfc003fff, 0x2000227f, 0 , 0, @@ -17955,53 +17413,53 @@ NMD::Pool NMD::POOL32Axf_1_1[4] = { }; -NMD::Pool NMD::POOL32Axf_1_3[4] = { +static const Pool POOL32Axf_1_3[4] = { { instruction , 0 , 0 , 32, - 0xfc003fff, 0x2000067f, &NMD::RDDSP , 0, + 0xfc003fff, 0x2000067f, &RDDSP , 0, DSP_ }, /* RDDSP */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x2000167f, &NMD::WRDSP , 0, + 0xfc003fff, 0x2000167f, &WRDSP , 0, DSP_ }, /* WRDSP */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x2000267f, &NMD::EXTP , 0, + 0xfc003fff, 0x2000267f, &EXTP , 0, DSP_ }, /* EXTP */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x2000367f, &NMD::EXTPDP , 0, + 0xfc003fff, 0x2000367f, &EXTPDP , 0, DSP_ }, /* EXTPDP */ }; -NMD::Pool NMD::POOL32Axf_1_4[2] = { +static const Pool POOL32Axf_1_4[2] = { { instruction , 0 , 0 , 32, - 0xfc001fff, 0x2000087f, &NMD::SHLL_QB , 0, + 0xfc001fff, 0x2000087f, &SHLL_QB , 0, DSP_ }, /* SHLL.QB */ { instruction , 0 , 0 , 32, - 0xfc001fff, 0x2000187f, &NMD::SHRL_QB , 0, + 0xfc001fff, 0x2000187f, &SHRL_QB , 0, DSP_ }, /* SHRL.QB */ }; -NMD::Pool NMD::MAQ_S_A__W_PHR[2] = { +static const Pool MAQ_S_A__W_PHR[2] = { { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20000a7f, &NMD::MAQ_S_W_PHR , 0, + 0xfc003fff, 0x20000a7f, &MAQ_S_W_PHR , 0, DSP_ }, /* MAQ_S.W.PHR */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20002a7f, &NMD::MAQ_SA_W_PHR , 0, + 0xfc003fff, 0x20002a7f, &MAQ_SA_W_PHR , 0, DSP_ }, /* MAQ_SA.W.PHR */ }; -NMD::Pool NMD::MAQ_S_A__W_PHL[2] = { +static const Pool MAQ_S_A__W_PHL[2] = { { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20001a7f, &NMD::MAQ_S_W_PHL , 0, + 0xfc003fff, 0x20001a7f, &MAQ_S_W_PHL , 0, DSP_ }, /* MAQ_S.W.PHL */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20003a7f, &NMD::MAQ_SA_W_PHL , 0, + 0xfc003fff, 0x20003a7f, &MAQ_SA_W_PHL , 0, DSP_ }, /* MAQ_SA.W.PHL */ }; -NMD::Pool NMD::POOL32Axf_1_5[2] = { +static const Pool POOL32Axf_1_5[2] = { { pool , MAQ_S_A__W_PHR , 2 , 32, 0xfc001fff, 0x20000a7f, 0 , 0, 0x0 }, /* MAQ_S[A].W.PHR */ @@ -18011,23 +17469,23 @@ NMD::Pool NMD::POOL32Axf_1_5[2] = { }; -NMD::Pool NMD::POOL32Axf_1_7[4] = { +static const Pool POOL32Axf_1_7[4] = { { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20000e7f, &NMD::EXTR_W , 0, + 0xfc003fff, 0x20000e7f, &EXTR_W , 0, DSP_ }, /* EXTR.W */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20001e7f, &NMD::EXTR_R_W , 0, + 0xfc003fff, 0x20001e7f, &EXTR_R_W , 0, DSP_ }, /* EXTR_R.W */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20002e7f, &NMD::EXTR_RS_W , 0, + 0xfc003fff, 0x20002e7f, &EXTR_RS_W , 0, DSP_ }, /* EXTR_RS.W */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20003e7f, &NMD::EXTR_S_H , 0, + 0xfc003fff, 0x20003e7f, &EXTR_S_H , 0, DSP_ }, /* EXTR_S.H */ }; -NMD::Pool NMD::POOL32Axf_1[8] = { +static const Pool POOL32Axf_1[8] = { { pool , POOL32Axf_1_0 , 4 , 32, 0xfc000fff, 0x2000007f, 0 , 0, 0x0 }, /* POOL32Axf_1_0 */ @@ -18055,119 +17513,119 @@ NMD::Pool NMD::POOL32Axf_1[8] = { }; -NMD::Pool NMD::POOL32Axf_2_DSP__0_7[8] = { +static const Pool POOL32Axf_2_DSP__0_7[8] = { { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200000bf, &NMD::DPA_W_PH , 0, + 0xfc003fff, 0x200000bf, &DPA_W_PH , 0, DSP_ }, /* DPA.W.PH */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200002bf, &NMD::DPAQ_S_W_PH , 0, + 0xfc003fff, 0x200002bf, &DPAQ_S_W_PH , 0, DSP_ }, /* DPAQ_S.W.PH */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200004bf, &NMD::DPS_W_PH , 0, + 0xfc003fff, 0x200004bf, &DPS_W_PH , 0, DSP_ }, /* DPS.W.PH */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200006bf, &NMD::DPSQ_S_W_PH , 0, + 0xfc003fff, 0x200006bf, &DPSQ_S_W_PH , 0, DSP_ }, /* DPSQ_S.W.PH */ { reserved_block , 0 , 0 , 32, 0xfc003fff, 0x200008bf, 0 , 0, 0x0 }, /* POOL32Axf_2(DSP)_0_7~*(4) */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20000abf, &NMD::MADD_DSP_ , 0, + 0xfc003fff, 0x20000abf, &MADD_DSP_ , 0, DSP_ }, /* MADD[DSP] */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20000cbf, &NMD::MULT_DSP_ , 0, + 0xfc003fff, 0x20000cbf, &MULT_DSP_ , 0, DSP_ }, /* MULT[DSP] */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20000ebf, &NMD::EXTRV_W , 0, + 0xfc003fff, 0x20000ebf, &EXTRV_W , 0, DSP_ }, /* EXTRV.W */ }; -NMD::Pool NMD::POOL32Axf_2_DSP__8_15[8] = { +static const Pool POOL32Axf_2_DSP__8_15[8] = { { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200010bf, &NMD::DPAX_W_PH , 0, + 0xfc003fff, 0x200010bf, &DPAX_W_PH , 0, DSP_ }, /* DPAX.W.PH */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200012bf, &NMD::DPAQ_SA_L_W , 0, + 0xfc003fff, 0x200012bf, &DPAQ_SA_L_W , 0, DSP_ }, /* DPAQ_SA.L.W */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200014bf, &NMD::DPSX_W_PH , 0, + 0xfc003fff, 0x200014bf, &DPSX_W_PH , 0, DSP_ }, /* DPSX.W.PH */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200016bf, &NMD::DPSQ_SA_L_W , 0, + 0xfc003fff, 0x200016bf, &DPSQ_SA_L_W , 0, DSP_ }, /* DPSQ_SA.L.W */ { reserved_block , 0 , 0 , 32, 0xfc003fff, 0x200018bf, 0 , 0, 0x0 }, /* POOL32Axf_2(DSP)_8_15~*(4) */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20001abf, &NMD::MADDU_DSP_ , 0, + 0xfc003fff, 0x20001abf, &MADDU_DSP_ , 0, DSP_ }, /* MADDU[DSP] */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20001cbf, &NMD::MULTU_DSP_ , 0, + 0xfc003fff, 0x20001cbf, &MULTU_DSP_ , 0, DSP_ }, /* MULTU[DSP] */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20001ebf, &NMD::EXTRV_R_W , 0, + 0xfc003fff, 0x20001ebf, &EXTRV_R_W , 0, DSP_ }, /* EXTRV_R.W */ }; -NMD::Pool NMD::POOL32Axf_2_DSP__16_23[8] = { +static const Pool POOL32Axf_2_DSP__16_23[8] = { { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200020bf, &NMD::DPAU_H_QBL , 0, + 0xfc003fff, 0x200020bf, &DPAU_H_QBL , 0, DSP_ }, /* DPAU.H.QBL */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200022bf, &NMD::DPAQX_S_W_PH , 0, + 0xfc003fff, 0x200022bf, &DPAQX_S_W_PH , 0, DSP_ }, /* DPAQX_S.W.PH */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200024bf, &NMD::DPSU_H_QBL , 0, + 0xfc003fff, 0x200024bf, &DPSU_H_QBL , 0, DSP_ }, /* DPSU.H.QBL */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200026bf, &NMD::DPSQX_S_W_PH , 0, + 0xfc003fff, 0x200026bf, &DPSQX_S_W_PH , 0, DSP_ }, /* DPSQX_S.W.PH */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200028bf, &NMD::EXTPV , 0, + 0xfc003fff, 0x200028bf, &EXTPV , 0, DSP_ }, /* EXTPV */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20002abf, &NMD::MSUB_DSP_ , 0, + 0xfc003fff, 0x20002abf, &MSUB_DSP_ , 0, DSP_ }, /* MSUB[DSP] */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20002cbf, &NMD::MULSA_W_PH , 0, + 0xfc003fff, 0x20002cbf, &MULSA_W_PH , 0, DSP_ }, /* MULSA.W.PH */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20002ebf, &NMD::EXTRV_RS_W , 0, + 0xfc003fff, 0x20002ebf, &EXTRV_RS_W , 0, DSP_ }, /* EXTRV_RS.W */ }; -NMD::Pool NMD::POOL32Axf_2_DSP__24_31[8] = { +static const Pool POOL32Axf_2_DSP__24_31[8] = { { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200030bf, &NMD::DPAU_H_QBR , 0, + 0xfc003fff, 0x200030bf, &DPAU_H_QBR , 0, DSP_ }, /* DPAU.H.QBR */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200032bf, &NMD::DPAQX_SA_W_PH , 0, + 0xfc003fff, 0x200032bf, &DPAQX_SA_W_PH , 0, DSP_ }, /* DPAQX_SA.W.PH */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200034bf, &NMD::DPSU_H_QBR , 0, + 0xfc003fff, 0x200034bf, &DPSU_H_QBR , 0, DSP_ }, /* DPSU.H.QBR */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200036bf, &NMD::DPSQX_SA_W_PH , 0, + 0xfc003fff, 0x200036bf, &DPSQX_SA_W_PH , 0, DSP_ }, /* DPSQX_SA.W.PH */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x200038bf, &NMD::EXTPDPV , 0, + 0xfc003fff, 0x200038bf, &EXTPDPV , 0, DSP_ }, /* EXTPDPV */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20003abf, &NMD::MSUBU_DSP_ , 0, + 0xfc003fff, 0x20003abf, &MSUBU_DSP_ , 0, DSP_ }, /* MSUBU[DSP] */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20003cbf, &NMD::MULSAQ_S_W_PH , 0, + 0xfc003fff, 0x20003cbf, &MULSAQ_S_W_PH , 0, DSP_ }, /* MULSAQ_S.W.PH */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0x20003ebf, &NMD::EXTRV_S_H , 0, + 0xfc003fff, 0x20003ebf, &EXTRV_S_H , 0, DSP_ }, /* EXTRV_S.H */ }; -NMD::Pool NMD::POOL32Axf_2[4] = { +static const Pool POOL32Axf_2[4] = { { pool , POOL32Axf_2_DSP__0_7, 8 , 32, 0xfc0031ff, 0x200000bf, 0 , 0, 0x0 }, /* POOL32Axf_2(DSP)_0_7 */ @@ -18183,12 +17641,12 @@ NMD::Pool NMD::POOL32Axf_2[4] = { }; -NMD::Pool NMD::POOL32Axf_4[128] = { +static const Pool POOL32Axf_4[128] = { { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000013f, &NMD::ABSQ_S_QB , 0, + 0xfc00ffff, 0x2000013f, &ABSQ_S_QB , 0, DSP_ }, /* ABSQ_S.QB */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000033f, &NMD::REPLV_PH , 0, + 0xfc00ffff, 0x2000033f, &REPLV_PH , 0, DSP_ }, /* REPLV.PH */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000053f, 0 , 0, @@ -18209,10 +17667,10 @@ NMD::Pool NMD::POOL32Axf_4[128] = { 0xfc00ffff, 0x20000f3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(7) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000113f, &NMD::ABSQ_S_PH , 0, + 0xfc00ffff, 0x2000113f, &ABSQ_S_PH , 0, DSP_ }, /* ABSQ_S.PH */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000133f, &NMD::REPLV_QB , 0, + 0xfc00ffff, 0x2000133f, &REPLV_QB , 0, DSP_ }, /* REPLV.QB */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000153f, 0 , 0, @@ -18233,7 +17691,7 @@ NMD::Pool NMD::POOL32Axf_4[128] = { 0xfc00ffff, 0x20001f3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(15) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000213f, &NMD::ABSQ_S_W , 0, + 0xfc00ffff, 0x2000213f, &ABSQ_S_W , 0, DSP_ }, /* ABSQ_S.W */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000233f, 0 , 0, @@ -18281,7 +17739,7 @@ NMD::Pool NMD::POOL32Axf_4[128] = { 0xfc00ffff, 0x20003f3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(31) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000413f, &NMD::INSV , 0, + 0xfc00ffff, 0x2000413f, &INSV , 0, DSP_ }, /* INSV */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000433f, 0 , 0, @@ -18296,16 +17754,16 @@ NMD::Pool NMD::POOL32Axf_4[128] = { 0xfc00ffff, 0x2000493f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(36) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x20004b3f, &NMD::CLO , 0, + 0xfc00ffff, 0x20004b3f, &CLO , 0, XMMS_ }, /* CLO */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x20004d3f, &NMD::MFC2 , 0, + 0xfc00ffff, 0x20004d3f, &MFC2 , 0, CP2_ }, /* MFC2 */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x20004f3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(39) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000513f, &NMD::PRECEQ_W_PHL , 0, + 0xfc00ffff, 0x2000513f, &PRECEQ_W_PHL , 0, DSP_ }, /* PRECEQ.W.PHL */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000533f, 0 , 0, @@ -18320,16 +17778,16 @@ NMD::Pool NMD::POOL32Axf_4[128] = { 0xfc00ffff, 0x2000593f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(44) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x20005b3f, &NMD::CLZ , 0, + 0xfc00ffff, 0x20005b3f, &CLZ , 0, XMMS_ }, /* CLZ */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x20005d3f, &NMD::MTC2 , 0, + 0xfc00ffff, 0x20005d3f, &MTC2 , 0, CP2_ }, /* MTC2 */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x20005f3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(47) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000613f, &NMD::PRECEQ_W_PHR , 0, + 0xfc00ffff, 0x2000613f, &PRECEQ_W_PHR , 0, DSP_ }, /* PRECEQ.W.PHR */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000633f, 0 , 0, @@ -18347,16 +17805,16 @@ NMD::Pool NMD::POOL32Axf_4[128] = { 0xfc00ffff, 0x20006b3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(53) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x20006d3f, &NMD::DMFC2 , 0, + 0xfc00ffff, 0x20006d3f, &DMFC2 , 0, CP2_ }, /* DMFC2 */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x20006f3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(55) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000713f, &NMD::PRECEQU_PH_QBL , 0, + 0xfc00ffff, 0x2000713f, &PRECEQU_PH_QBL , 0, DSP_ }, /* PRECEQU.PH.QBL */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000733f, &NMD::PRECEQU_PH_QBLA , 0, + 0xfc00ffff, 0x2000733f, &PRECEQU_PH_QBLA , 0, DSP_ }, /* PRECEQU.PH.QBLA */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000753f, 0 , 0, @@ -18371,7 +17829,7 @@ NMD::Pool NMD::POOL32Axf_4[128] = { 0xfc00ffff, 0x20007b3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(61) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x20007d3f, &NMD::DMTC2 , 0, + 0xfc00ffff, 0x20007d3f, &DMTC2 , 0, CP2_ }, /* DMTC2 */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x20007f3f, 0 , 0, @@ -18395,16 +17853,16 @@ NMD::Pool NMD::POOL32Axf_4[128] = { 0xfc00ffff, 0x20008b3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(69) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x20008d3f, &NMD::MFHC2 , 0, + 0xfc00ffff, 0x20008d3f, &MFHC2 , 0, CP2_ }, /* MFHC2 */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x20008f3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(71) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000913f, &NMD::PRECEQU_PH_QBR , 0, + 0xfc00ffff, 0x2000913f, &PRECEQU_PH_QBR , 0, DSP_ }, /* PRECEQU.PH.QBR */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000933f, &NMD::PRECEQU_PH_QBRA , 0, + 0xfc00ffff, 0x2000933f, &PRECEQU_PH_QBRA , 0, DSP_ }, /* PRECEQU.PH.QBRA */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000953f, 0 , 0, @@ -18419,7 +17877,7 @@ NMD::Pool NMD::POOL32Axf_4[128] = { 0xfc00ffff, 0x20009b3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(77) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x20009d3f, &NMD::MTHC2 , 0, + 0xfc00ffff, 0x20009d3f, &MTHC2 , 0, CP2_ }, /* MTHC2 */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x20009f3f, 0 , 0, @@ -18449,10 +17907,10 @@ NMD::Pool NMD::POOL32Axf_4[128] = { 0xfc00ffff, 0x2000af3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(87) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000b13f, &NMD::PRECEU_PH_QBL , 0, + 0xfc00ffff, 0x2000b13f, &PRECEU_PH_QBL , 0, DSP_ }, /* PRECEU.PH.QBL */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000b33f, &NMD::PRECEU_PH_QBLA , 0, + 0xfc00ffff, 0x2000b33f, &PRECEU_PH_QBLA , 0, DSP_ }, /* PRECEU.PH.QBLA */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000b53f, 0 , 0, @@ -18491,16 +17949,16 @@ NMD::Pool NMD::POOL32Axf_4[128] = { 0xfc00ffff, 0x2000cb3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(101) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000cd3f, &NMD::CFC2 , 0, + 0xfc00ffff, 0x2000cd3f, &CFC2 , 0, CP2_ }, /* CFC2 */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000cf3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(103) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000d13f, &NMD::PRECEU_PH_QBR , 0, + 0xfc00ffff, 0x2000d13f, &PRECEU_PH_QBR , 0, DSP_ }, /* PRECEU.PH.QBR */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000d33f, &NMD::PRECEU_PH_QBRA , 0, + 0xfc00ffff, 0x2000d33f, &PRECEU_PH_QBRA , 0, DSP_ }, /* PRECEU.PH.QBRA */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000d53f, 0 , 0, @@ -18515,7 +17973,7 @@ NMD::Pool NMD::POOL32Axf_4[128] = { 0xfc00ffff, 0x2000db3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(109) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000dd3f, &NMD::CTC2 , 0, + 0xfc00ffff, 0x2000dd3f, &CTC2 , 0, CP2_ }, /* CTC2 */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000df3f, 0 , 0, @@ -18545,7 +18003,7 @@ NMD::Pool NMD::POOL32Axf_4[128] = { 0xfc00ffff, 0x2000ef3f, 0 , 0, 0x0 }, /* POOL32Axf_4~*(119) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000f13f, &NMD::RADDU_W_QB , 0, + 0xfc00ffff, 0x2000f13f, &RADDU_W_QB , 0, DSP_ }, /* RADDU.W.QB */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000f33f, 0 , 0, @@ -18571,18 +18029,18 @@ NMD::Pool NMD::POOL32Axf_4[128] = { }; -NMD::Pool NMD::POOL32Axf_5_group0[32] = { +static const Pool POOL32Axf_5_group0[32] = { { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000017f, &NMD::TLBGP , 0, + 0xfc00ffff, 0x2000017f, &TLBGP , 0, CP0_ | VZ_ | TLB_ }, /* TLBGP */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000037f, &NMD::TLBP , 0, + 0xfc00ffff, 0x2000037f, &TLBP , 0, CP0_ | TLB_ }, /* TLBP */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000057f, &NMD::TLBGINV , 0, + 0xfc00ffff, 0x2000057f, &TLBGINV , 0, CP0_ | VZ_ | TLB_ | TLBINV_}, /* TLBGINV */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000077f, &NMD::TLBINV , 0, + 0xfc00ffff, 0x2000077f, &TLBINV , 0, CP0_ | TLB_ | TLBINV_}, /* TLBINV */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000097f, 0 , 0, @@ -18597,16 +18055,16 @@ NMD::Pool NMD::POOL32Axf_5_group0[32] = { 0xfc00ffff, 0x20000f7f, 0 , 0, 0x0 }, /* POOL32Axf_5_group0~*(7) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000117f, &NMD::TLBGR , 0, + 0xfc00ffff, 0x2000117f, &TLBGR , 0, CP0_ | VZ_ | TLB_ }, /* TLBGR */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000137f, &NMD::TLBR , 0, + 0xfc00ffff, 0x2000137f, &TLBR , 0, CP0_ | TLB_ }, /* TLBR */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000157f, &NMD::TLBGINVF , 0, + 0xfc00ffff, 0x2000157f, &TLBGINVF , 0, CP0_ | VZ_ | TLB_ | TLBINV_}, /* TLBGINVF */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000177f, &NMD::TLBINVF , 0, + 0xfc00ffff, 0x2000177f, &TLBINVF , 0, CP0_ | TLB_ | TLBINV_}, /* TLBINVF */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000197f, 0 , 0, @@ -18621,10 +18079,10 @@ NMD::Pool NMD::POOL32Axf_5_group0[32] = { 0xfc00ffff, 0x20001f7f, 0 , 0, 0x0 }, /* POOL32Axf_5_group0~*(15) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000217f, &NMD::TLBGWI , 0, + 0xfc00ffff, 0x2000217f, &TLBGWI , 0, CP0_ | VZ_ | TLB_ }, /* TLBGWI */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000237f, &NMD::TLBWI , 0, + 0xfc00ffff, 0x2000237f, &TLBWI , 0, CP0_ | TLB_ }, /* TLBWI */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000257f, 0 , 0, @@ -18645,10 +18103,10 @@ NMD::Pool NMD::POOL32Axf_5_group0[32] = { 0xfc00ffff, 0x20002f7f, 0 , 0, 0x0 }, /* POOL32Axf_5_group0~*(23) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000317f, &NMD::TLBGWR , 0, + 0xfc00ffff, 0x2000317f, &TLBGWR , 0, CP0_ | VZ_ | TLB_ }, /* TLBGWR */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000337f, &NMD::TLBWR , 0, + 0xfc00ffff, 0x2000337f, &TLBWR , 0, CP0_ | TLB_ }, /* TLBWR */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000357f, 0 , 0, @@ -18671,7 +18129,7 @@ NMD::Pool NMD::POOL32Axf_5_group0[32] = { }; -NMD::Pool NMD::POOL32Axf_5_group1[32] = { +static const Pool POOL32Axf_5_group1[32] = { { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000417f, 0 , 0, 0x0 }, /* POOL32Axf_5_group1~*(0) */ @@ -18682,7 +18140,7 @@ NMD::Pool NMD::POOL32Axf_5_group1[32] = { 0xfc00ffff, 0x2000457f, 0 , 0, 0x0 }, /* POOL32Axf_5_group1~*(2) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000477f, &NMD::DI , 0, + 0xfc00ffff, 0x2000477f, &DI , 0, 0x0 }, /* DI */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000497f, 0 , 0, @@ -18706,7 +18164,7 @@ NMD::Pool NMD::POOL32Axf_5_group1[32] = { 0xfc00ffff, 0x2000557f, 0 , 0, 0x0 }, /* POOL32Axf_5_group1~*(10) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000577f, &NMD::EI , 0, + 0xfc00ffff, 0x2000577f, &EI , 0, 0x0 }, /* EI */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000597f, 0 , 0, @@ -18771,22 +18229,22 @@ NMD::Pool NMD::POOL32Axf_5_group1[32] = { }; -NMD::Pool NMD::ERETx[2] = { +static const Pool ERETx[2] = { { instruction , 0 , 0 , 32, - 0xfc01ffff, 0x2000f37f, &NMD::ERET , 0, + 0xfc01ffff, 0x2000f37f, &ERET , 0, 0x0 }, /* ERET */ { instruction , 0 , 0 , 32, - 0xfc01ffff, 0x2001f37f, &NMD::ERETNC , 0, + 0xfc01ffff, 0x2001f37f, &ERETNC , 0, 0x0 }, /* ERETNC */ }; -NMD::Pool NMD::POOL32Axf_5_group3[32] = { +static const Pool POOL32Axf_5_group3[32] = { { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000c17f, 0 , 0, 0x0 }, /* POOL32Axf_5_group3~*(0) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000c37f, &NMD::WAIT , 0, + 0xfc00ffff, 0x2000c37f, &WAIT , 0, 0x0 }, /* WAIT */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000c57f, 0 , 0, @@ -18810,7 +18268,7 @@ NMD::Pool NMD::POOL32Axf_5_group3[32] = { 0xfc00ffff, 0x2000d17f, 0 , 0, 0x0 }, /* POOL32Axf_5_group3~*(8) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000d37f, &NMD::IRET , 0, + 0xfc00ffff, 0x2000d37f, &IRET , 0, MCU_ }, /* IRET */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000d57f, 0 , 0, @@ -18831,10 +18289,10 @@ NMD::Pool NMD::POOL32Axf_5_group3[32] = { 0xfc00ffff, 0x2000df7f, 0 , 0, 0x0 }, /* POOL32Axf_5_group3~*(15) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000e17f, &NMD::RDPGPR , 0, + 0xfc00ffff, 0x2000e17f, &RDPGPR , 0, CP0_ }, /* RDPGPR */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000e37f, &NMD::DERET , 0, + 0xfc00ffff, 0x2000e37f, &DERET , 0, EJTAG_ }, /* DERET */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0x2000e57f, 0 , 0, @@ -18855,7 +18313,7 @@ NMD::Pool NMD::POOL32Axf_5_group3[32] = { 0xfc00ffff, 0x2000ef7f, 0 , 0, 0x0 }, /* POOL32Axf_5_group3~*(23) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0x2000f17f, &NMD::WRPGPR , 0, + 0xfc00ffff, 0x2000f17f, &WRPGPR , 0, CP0_ }, /* WRPGPR */ { pool , ERETx , 2 , 32, 0xfc00ffff, 0x2000f37f, 0 , 0, @@ -18881,7 +18339,7 @@ NMD::Pool NMD::POOL32Axf_5_group3[32] = { }; -NMD::Pool NMD::POOL32Axf_5[4] = { +static const Pool POOL32Axf_5[4] = { { pool , POOL32Axf_5_group0 , 32 , 32, 0xfc00c1ff, 0x2000017f, 0 , 0, 0x0 }, /* POOL32Axf_5_group0 */ @@ -18897,25 +18355,25 @@ NMD::Pool NMD::POOL32Axf_5[4] = { }; -NMD::Pool NMD::SHRA__R__QB[2] = { +static const Pool SHRA__R__QB[2] = { { instruction , 0 , 0 , 32, - 0xfc001fff, 0x200001ff, &NMD::SHRA_QB , 0, + 0xfc001fff, 0x200001ff, &SHRA_QB , 0, DSP_ }, /* SHRA.QB */ { instruction , 0 , 0 , 32, - 0xfc001fff, 0x200011ff, &NMD::SHRA_R_QB , 0, + 0xfc001fff, 0x200011ff, &SHRA_R_QB , 0, DSP_ }, /* SHRA_R.QB */ }; -NMD::Pool NMD::POOL32Axf_7[8] = { +static const Pool POOL32Axf_7[8] = { { pool , SHRA__R__QB , 2 , 32, 0xfc000fff, 0x200001ff, 0 , 0, 0x0 }, /* SHRA[_R].QB */ { instruction , 0 , 0 , 32, - 0xfc000fff, 0x200003ff, &NMD::SHRL_PH , 0, + 0xfc000fff, 0x200003ff, &SHRL_PH , 0, DSP_ }, /* SHRL.PH */ { instruction , 0 , 0 , 32, - 0xfc000fff, 0x200005ff, &NMD::REPL_QB , 0, + 0xfc000fff, 0x200005ff, &REPL_QB , 0, DSP_ }, /* REPL.QB */ { reserved_block , 0 , 0 , 32, 0xfc000fff, 0x200007ff, 0 , 0, @@ -18935,7 +18393,7 @@ NMD::Pool NMD::POOL32Axf_7[8] = { }; -NMD::Pool NMD::POOL32Axf[8] = { +static const Pool POOL32Axf[8] = { { reserved_block , 0 , 0 , 32, 0xfc0001ff, 0x2000003f, 0 , 0, 0x0 }, /* POOL32Axf~*(0) */ @@ -18963,18 +18421,18 @@ NMD::Pool NMD::POOL32Axf[8] = { }; -NMD::Pool NMD::_POOL32A7[8] = { +static const Pool _POOL32A7[8] = { { pool , P_LSX , 2 , 32, 0xfc00003f, 0x20000007, 0 , 0, 0x0 }, /* P.LSX */ { instruction , 0 , 0 , 32, - 0xfc00003f, 0x2000000f, &NMD::LSA , 0, + 0xfc00003f, 0x2000000f, &LSA , 0, 0x0 }, /* LSA */ { reserved_block , 0 , 0 , 32, 0xfc00003f, 0x20000017, 0 , 0, 0x0 }, /* _POOL32A7~*(2) */ { instruction , 0 , 0 , 32, - 0xfc00003f, 0x2000001f, &NMD::EXTW , 0, + 0xfc00003f, 0x2000001f, &EXTW , 0, 0x0 }, /* EXTW */ { reserved_block , 0 , 0 , 32, 0xfc00003f, 0x20000027, 0 , 0, @@ -18991,18 +18449,18 @@ NMD::Pool NMD::_POOL32A7[8] = { }; -NMD::Pool NMD::P32A[8] = { +static const Pool P32A[8] = { { pool , _POOL32A0 , 128 , 32, 0xfc000007, 0x20000000, 0 , 0, 0x0 }, /* _POOL32A0 */ { instruction , 0 , 0 , 32, - 0xfc000007, 0x20000001, &NMD::SPECIAL2 , 0, + 0xfc000007, 0x20000001, &SPECIAL2 , 0, UDI_ }, /* SPECIAL2 */ { instruction , 0 , 0 , 32, - 0xfc000007, 0x20000002, &NMD::COP2_1 , 0, + 0xfc000007, 0x20000002, &COP2_1 , 0, CP2_ }, /* COP2_1 */ { instruction , 0 , 0 , 32, - 0xfc000007, 0x20000003, &NMD::UDI , 0, + 0xfc000007, 0x20000003, &UDI , 0, UDI_ }, /* UDI */ { reserved_block , 0 , 0 , 32, 0xfc000007, 0x20000004, 0 , 0, @@ -19019,44 +18477,44 @@ NMD::Pool NMD::P32A[8] = { }; -NMD::Pool NMD::P_GP_D[2] = { +static const Pool P_GP_D[2] = { { instruction , 0 , 0 , 32, - 0xfc000007, 0x40000001, &NMD::LD_GP_ , 0, + 0xfc000007, 0x40000001, &LD_GP_ , 0, MIPS64_ }, /* LD[GP] */ { instruction , 0 , 0 , 32, - 0xfc000007, 0x40000005, &NMD::SD_GP_ , 0, + 0xfc000007, 0x40000005, &SD_GP_ , 0, MIPS64_ }, /* SD[GP] */ }; -NMD::Pool NMD::P_GP_W[4] = { +static const Pool P_GP_W[4] = { { instruction , 0 , 0 , 32, - 0xfc000003, 0x40000000, &NMD::ADDIU_GP_W_ , 0, + 0xfc000003, 0x40000000, &ADDIU_GP_W_ , 0, 0x0 }, /* ADDIU[GP.W] */ { pool , P_GP_D , 2 , 32, 0xfc000003, 0x40000001, 0 , 0, 0x0 }, /* P.GP.D */ { instruction , 0 , 0 , 32, - 0xfc000003, 0x40000002, &NMD::LW_GP_ , 0, + 0xfc000003, 0x40000002, &LW_GP_ , 0, 0x0 }, /* LW[GP] */ { instruction , 0 , 0 , 32, - 0xfc000003, 0x40000003, &NMD::SW_GP_ , 0, + 0xfc000003, 0x40000003, &SW_GP_ , 0, 0x0 }, /* SW[GP] */ }; -NMD::Pool NMD::POOL48I[32] = { +static const Pool POOL48I[32] = { { instruction , 0 , 0 , 48, - 0xfc1f00000000ull, 0x600000000000ull, &NMD::LI_48_ , 0, + 0xfc1f00000000ull, 0x600000000000ull, &LI_48_ , 0, XMMS_ }, /* LI[48] */ { instruction , 0 , 0 , 48, - 0xfc1f00000000ull, 0x600100000000ull, &NMD::ADDIU_48_ , 0, + 0xfc1f00000000ull, 0x600100000000ull, &ADDIU_48_ , 0, XMMS_ }, /* ADDIU[48] */ { instruction , 0 , 0 , 48, - 0xfc1f00000000ull, 0x600200000000ull, &NMD::ADDIU_GP48_ , 0, + 0xfc1f00000000ull, 0x600200000000ull, &ADDIU_GP48_ , 0, XMMS_ }, /* ADDIU[GP48] */ { instruction , 0 , 0 , 48, - 0xfc1f00000000ull, 0x600300000000ull, &NMD::ADDIUPC_48_ , 0, + 0xfc1f00000000ull, 0x600300000000ull, &ADDIUPC_48_ , 0, XMMS_ }, /* ADDIUPC[48] */ { reserved_block , 0 , 0 , 48, 0xfc1f00000000ull, 0x600400000000ull, 0 , 0, @@ -19080,7 +18538,7 @@ NMD::Pool NMD::POOL48I[32] = { 0xfc1f00000000ull, 0x600a00000000ull, 0 , 0, 0x0 }, /* POOL48I~*(10) */ { instruction , 0 , 0 , 48, - 0xfc1f00000000ull, 0x600b00000000ull, &NMD::LWPC_48_ , 0, + 0xfc1f00000000ull, 0x600b00000000ull, &LWPC_48_ , 0, XMMS_ }, /* LWPC[48] */ { reserved_block , 0 , 0 , 48, 0xfc1f00000000ull, 0x600c00000000ull, 0 , 0, @@ -19092,13 +18550,13 @@ NMD::Pool NMD::POOL48I[32] = { 0xfc1f00000000ull, 0x600e00000000ull, 0 , 0, 0x0 }, /* POOL48I~*(14) */ { instruction , 0 , 0 , 48, - 0xfc1f00000000ull, 0x600f00000000ull, &NMD::SWPC_48_ , 0, + 0xfc1f00000000ull, 0x600f00000000ull, &SWPC_48_ , 0, XMMS_ }, /* SWPC[48] */ { reserved_block , 0 , 0 , 48, 0xfc1f00000000ull, 0x601000000000ull, 0 , 0, 0x0 }, /* POOL48I~*(16) */ { instruction , 0 , 0 , 48, - 0xfc1f00000000ull, 0x601100000000ull, &NMD::DADDIU_48_ , 0, + 0xfc1f00000000ull, 0x601100000000ull, &DADDIU_48_ , 0, MIPS64_ }, /* DADDIU[48] */ { reserved_block , 0 , 0 , 48, 0xfc1f00000000ull, 0x601200000000ull, 0 , 0, @@ -19107,7 +18565,7 @@ NMD::Pool NMD::POOL48I[32] = { 0xfc1f00000000ull, 0x601300000000ull, 0 , 0, 0x0 }, /* POOL48I~*(19) */ { instruction , 0 , 0 , 48, - 0xfc1f00000000ull, 0x601400000000ull, &NMD::DLUI_48_ , 0, + 0xfc1f00000000ull, 0x601400000000ull, &DLUI_48_ , 0, MIPS64_ }, /* DLUI[48] */ { reserved_block , 0 , 0 , 48, 0xfc1f00000000ull, 0x601500000000ull, 0 , 0, @@ -19128,7 +18586,7 @@ NMD::Pool NMD::POOL48I[32] = { 0xfc1f00000000ull, 0x601a00000000ull, 0 , 0, 0x0 }, /* POOL48I~*(26) */ { instruction , 0 , 0 , 48, - 0xfc1f00000000ull, 0x601b00000000ull, &NMD::LDPC_48_ , 0, + 0xfc1f00000000ull, 0x601b00000000ull, &LDPC_48_ , 0, MIPS64_ }, /* LDPC[48] */ { reserved_block , 0 , 0 , 48, 0xfc1f00000000ull, 0x601c00000000ull, 0 , 0, @@ -19140,33 +18598,33 @@ NMD::Pool NMD::POOL48I[32] = { 0xfc1f00000000ull, 0x601e00000000ull, 0 , 0, 0x0 }, /* POOL48I~*(30) */ { instruction , 0 , 0 , 48, - 0xfc1f00000000ull, 0x601f00000000ull, &NMD::SDPC_48_ , 0, + 0xfc1f00000000ull, 0x601f00000000ull, &SDPC_48_ , 0, MIPS64_ }, /* SDPC[48] */ }; -NMD::Pool NMD::PP_SR[4] = { +static const Pool PP_SR[4] = { { instruction , 0 , 0 , 32, - 0xfc10f003, 0x80003000, &NMD::SAVE_32_ , 0, + 0xfc10f003, 0x80003000, &SAVE_32_ , 0, 0x0 }, /* SAVE[32] */ { reserved_block , 0 , 0 , 32, 0xfc10f003, 0x80003001, 0 , 0, 0x0 }, /* PP.SR~*(1) */ { instruction , 0 , 0 , 32, - 0xfc10f003, 0x80003002, &NMD::RESTORE_32_ , 0, + 0xfc10f003, 0x80003002, &RESTORE_32_ , 0, 0x0 }, /* RESTORE[32] */ { return_instruction , 0 , 0 , 32, - 0xfc10f003, 0x80003003, &NMD::RESTORE_JRC_32_ , 0, + 0xfc10f003, 0x80003003, &RESTORE_JRC_32_ , 0, 0x0 }, /* RESTORE.JRC[32] */ }; -NMD::Pool NMD::P_SR_F[8] = { +static const Pool P_SR_F[8] = { { instruction , 0 , 0 , 32, - 0xfc10f007, 0x80103000, &NMD::SAVEF , 0, + 0xfc10f007, 0x80103000, &SAVEF , 0, CP1_ }, /* SAVEF */ { instruction , 0 , 0 , 32, - 0xfc10f007, 0x80103001, &NMD::RESTOREF , 0, + 0xfc10f007, 0x80103001, &RESTOREF , 0, CP1_ }, /* RESTOREF */ { reserved_block , 0 , 0 , 32, 0xfc10f007, 0x80103002, 0 , 0, @@ -19189,7 +18647,7 @@ NMD::Pool NMD::P_SR_F[8] = { }; -NMD::Pool NMD::P_SR[2] = { +static const Pool P_SR[2] = { { pool , PP_SR , 4 , 32, 0xfc10f000, 0x80003000, 0 , 0, 0x0 }, /* PP.SR */ @@ -19199,26 +18657,26 @@ NMD::Pool NMD::P_SR[2] = { }; -NMD::Pool NMD::P_SLL[5] = { +static const Pool P_SLL[5] = { { instruction , 0 , 0 , 32, - 0xffe0f1ff, 0x8000c000, &NMD::NOP_32_ , 0, + 0xffe0f1ff, 0x8000c000, &NOP_32_ , 0, 0x0 }, /* NOP[32] */ { instruction , 0 , 0 , 32, - 0xffe0f1ff, 0x8000c003, &NMD::EHB , 0, + 0xffe0f1ff, 0x8000c003, &EHB , 0, 0x0 }, /* EHB */ { instruction , 0 , 0 , 32, - 0xffe0f1ff, 0x8000c005, &NMD::PAUSE , 0, + 0xffe0f1ff, 0x8000c005, &PAUSE , 0, 0x0 }, /* PAUSE */ { instruction , 0 , 0 , 32, - 0xffe0f1ff, 0x8000c006, &NMD::SYNC , 0, + 0xffe0f1ff, 0x8000c006, &SYNC , 0, 0x0 }, /* SYNC */ { instruction , 0 , 0 , 32, - 0xfc00f1e0, 0x8000c000, &NMD::SLL_32_ , 0, + 0xfc00f1e0, 0x8000c000, &SLL_32_ , 0, 0x0 }, /* SLL[32] */ }; -NMD::Pool NMD::P_SHIFT[16] = { +static const Pool P_SHIFT[16] = { { pool , P_SLL , 5 , 32, 0xfc00f1e0, 0x8000c000, 0 , 0, 0x0 }, /* P.SLL */ @@ -19226,53 +18684,53 @@ NMD::Pool NMD::P_SHIFT[16] = { 0xfc00f1e0, 0x8000c020, 0 , 0, 0x0 }, /* P.SHIFT~*(1) */ { instruction , 0 , 0 , 32, - 0xfc00f1e0, 0x8000c040, &NMD::SRL_32_ , 0, + 0xfc00f1e0, 0x8000c040, &SRL_32_ , 0, 0x0 }, /* SRL[32] */ { reserved_block , 0 , 0 , 32, 0xfc00f1e0, 0x8000c060, 0 , 0, 0x0 }, /* P.SHIFT~*(3) */ { instruction , 0 , 0 , 32, - 0xfc00f1e0, 0x8000c080, &NMD::SRA , 0, + 0xfc00f1e0, 0x8000c080, &SRA , 0, 0x0 }, /* SRA */ { reserved_block , 0 , 0 , 32, 0xfc00f1e0, 0x8000c0a0, 0 , 0, 0x0 }, /* P.SHIFT~*(5) */ { instruction , 0 , 0 , 32, - 0xfc00f1e0, 0x8000c0c0, &NMD::ROTR , 0, + 0xfc00f1e0, 0x8000c0c0, &ROTR , 0, 0x0 }, /* ROTR */ { reserved_block , 0 , 0 , 32, 0xfc00f1e0, 0x8000c0e0, 0 , 0, 0x0 }, /* P.SHIFT~*(7) */ { instruction , 0 , 0 , 32, - 0xfc00f1e0, 0x8000c100, &NMD::DSLL , 0, + 0xfc00f1e0, 0x8000c100, &DSLL , 0, MIPS64_ }, /* DSLL */ { instruction , 0 , 0 , 32, - 0xfc00f1e0, 0x8000c120, &NMD::DSLL32 , 0, + 0xfc00f1e0, 0x8000c120, &DSLL32 , 0, MIPS64_ }, /* DSLL32 */ { instruction , 0 , 0 , 32, - 0xfc00f1e0, 0x8000c140, &NMD::DSRL , 0, + 0xfc00f1e0, 0x8000c140, &DSRL , 0, MIPS64_ }, /* DSRL */ { instruction , 0 , 0 , 32, - 0xfc00f1e0, 0x8000c160, &NMD::DSRL32 , 0, + 0xfc00f1e0, 0x8000c160, &DSRL32 , 0, MIPS64_ }, /* DSRL32 */ { instruction , 0 , 0 , 32, - 0xfc00f1e0, 0x8000c180, &NMD::DSRA , 0, + 0xfc00f1e0, 0x8000c180, &DSRA , 0, MIPS64_ }, /* DSRA */ { instruction , 0 , 0 , 32, - 0xfc00f1e0, 0x8000c1a0, &NMD::DSRA32 , 0, + 0xfc00f1e0, 0x8000c1a0, &DSRA32 , 0, MIPS64_ }, /* DSRA32 */ { instruction , 0 , 0 , 32, - 0xfc00f1e0, 0x8000c1c0, &NMD::DROTR , 0, + 0xfc00f1e0, 0x8000c1c0, &DROTR , 0, MIPS64_ }, /* DROTR */ { instruction , 0 , 0 , 32, - 0xfc00f1e0, 0x8000c1e0, &NMD::DROTR32 , 0, + 0xfc00f1e0, 0x8000c1e0, &DROTR32 , 0, MIPS64_ }, /* DROTR32 */ }; -NMD::Pool NMD::P_ROTX[4] = { +static const Pool P_ROTX[4] = { { instruction , 0 , 0 , 32, - 0xfc00f820, 0x8000d000, &NMD::ROTX , 0, + 0xfc00f820, 0x8000d000, &ROTX , 0, XMMS_ }, /* ROTX */ { reserved_block , 0 , 0 , 32, 0xfc00f820, 0x8000d020, 0 , 0, @@ -19286,74 +18744,74 @@ NMD::Pool NMD::P_ROTX[4] = { }; -NMD::Pool NMD::P_INS[4] = { +static const Pool P_INS[4] = { { instruction , 0 , 0 , 32, - 0xfc00f820, 0x8000e000, &NMD::INS , 0, + 0xfc00f820, 0x8000e000, &INS , 0, XMMS_ }, /* INS */ { instruction , 0 , 0 , 32, - 0xfc00f820, 0x8000e020, &NMD::DINSU , 0, + 0xfc00f820, 0x8000e020, &DINSU , 0, MIPS64_ }, /* DINSU */ { instruction , 0 , 0 , 32, - 0xfc00f820, 0x8000e800, &NMD::DINSM , 0, + 0xfc00f820, 0x8000e800, &DINSM , 0, MIPS64_ }, /* DINSM */ { instruction , 0 , 0 , 32, - 0xfc00f820, 0x8000e820, &NMD::DINS , 0, + 0xfc00f820, 0x8000e820, &DINS , 0, MIPS64_ }, /* DINS */ }; -NMD::Pool NMD::P_EXT[4] = { +static const Pool P_EXT[4] = { { instruction , 0 , 0 , 32, - 0xfc00f820, 0x8000f000, &NMD::EXT , 0, + 0xfc00f820, 0x8000f000, &EXT , 0, XMMS_ }, /* EXT */ { instruction , 0 , 0 , 32, - 0xfc00f820, 0x8000f020, &NMD::DEXTU , 0, + 0xfc00f820, 0x8000f020, &DEXTU , 0, MIPS64_ }, /* DEXTU */ { instruction , 0 , 0 , 32, - 0xfc00f820, 0x8000f800, &NMD::DEXTM , 0, + 0xfc00f820, 0x8000f800, &DEXTM , 0, MIPS64_ }, /* DEXTM */ { instruction , 0 , 0 , 32, - 0xfc00f820, 0x8000f820, &NMD::DEXT , 0, + 0xfc00f820, 0x8000f820, &DEXT , 0, MIPS64_ }, /* DEXT */ }; -NMD::Pool NMD::P_U12[16] = { +static const Pool P_U12[16] = { { instruction , 0 , 0 , 32, - 0xfc00f000, 0x80000000, &NMD::ORI , 0, + 0xfc00f000, 0x80000000, &ORI , 0, 0x0 }, /* ORI */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x80001000, &NMD::XORI , 0, + 0xfc00f000, 0x80001000, &XORI , 0, 0x0 }, /* XORI */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x80002000, &NMD::ANDI_32_ , 0, + 0xfc00f000, 0x80002000, &ANDI_32_ , 0, 0x0 }, /* ANDI[32] */ { pool , P_SR , 2 , 32, 0xfc00f000, 0x80003000, 0 , 0, 0x0 }, /* P.SR */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x80004000, &NMD::SLTI , 0, + 0xfc00f000, 0x80004000, &SLTI , 0, 0x0 }, /* SLTI */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x80005000, &NMD::SLTIU , 0, + 0xfc00f000, 0x80005000, &SLTIU , 0, 0x0 }, /* SLTIU */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x80006000, &NMD::SEQI , 0, + 0xfc00f000, 0x80006000, &SEQI , 0, 0x0 }, /* SEQI */ { reserved_block , 0 , 0 , 32, 0xfc00f000, 0x80007000, 0 , 0, 0x0 }, /* P.U12~*(7) */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x80008000, &NMD::ADDIU_NEG_ , 0, + 0xfc00f000, 0x80008000, &ADDIU_NEG_ , 0, 0x0 }, /* ADDIU[NEG] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x80009000, &NMD::DADDIU_U12_ , 0, + 0xfc00f000, 0x80009000, &DADDIU_U12_ , 0, MIPS64_ }, /* DADDIU[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x8000a000, &NMD::DADDIU_NEG_ , 0, + 0xfc00f000, 0x8000a000, &DADDIU_NEG_ , 0, MIPS64_ }, /* DADDIU[NEG] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x8000b000, &NMD::DROTX , 0, + 0xfc00f000, 0x8000b000, &DROTX , 0, MIPS64_ }, /* DROTX */ { pool , P_SHIFT , 16 , 32, 0xfc00f000, 0x8000c000, 0 , 0, @@ -19370,19 +18828,19 @@ NMD::Pool NMD::P_U12[16] = { }; -NMD::Pool NMD::RINT_fmt[2] = { +static const Pool RINT_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa0000020, &NMD::RINT_S , 0, + 0xfc0003ff, 0xa0000020, &RINT_S , 0, CP1_ }, /* RINT.S */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa0000220, &NMD::RINT_D , 0, + 0xfc0003ff, 0xa0000220, &RINT_D , 0, CP1_ }, /* RINT.D */ }; -NMD::Pool NMD::ADD_fmt0[2] = { +static const Pool ADD_fmt0[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa0000030, &NMD::ADD_S , 0, + 0xfc0003ff, 0xa0000030, &ADD_S , 0, CP1_ }, /* ADD.S */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0xa0000230, 0 , 0, @@ -19390,29 +18848,29 @@ NMD::Pool NMD::ADD_fmt0[2] = { }; -NMD::Pool NMD::SELEQZ_fmt[2] = { +static const Pool SELEQZ_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa0000038, &NMD::SELEQZ_S , 0, + 0xfc0003ff, 0xa0000038, &SELEQZ_S , 0, CP1_ }, /* SELEQZ.S */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa0000238, &NMD::SELEQZ_D , 0, + 0xfc0003ff, 0xa0000238, &SELEQZ_D , 0, CP1_ }, /* SELEQZ.D */ }; -NMD::Pool NMD::CLASS_fmt[2] = { +static const Pool CLASS_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa0000060, &NMD::CLASS_S , 0, + 0xfc0003ff, 0xa0000060, &CLASS_S , 0, CP1_ }, /* CLASS.S */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa0000260, &NMD::CLASS_D , 0, + 0xfc0003ff, 0xa0000260, &CLASS_D , 0, CP1_ }, /* CLASS.D */ }; -NMD::Pool NMD::SUB_fmt0[2] = { +static const Pool SUB_fmt0[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa0000070, &NMD::SUB_S , 0, + 0xfc0003ff, 0xa0000070, &SUB_S , 0, CP1_ }, /* SUB.S */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0xa0000270, 0 , 0, @@ -19420,19 +18878,19 @@ NMD::Pool NMD::SUB_fmt0[2] = { }; -NMD::Pool NMD::SELNEZ_fmt[2] = { +static const Pool SELNEZ_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa0000078, &NMD::SELNEZ_S , 0, + 0xfc0003ff, 0xa0000078, &SELNEZ_S , 0, CP1_ }, /* SELNEZ.S */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa0000278, &NMD::SELNEZ_D , 0, + 0xfc0003ff, 0xa0000278, &SELNEZ_D , 0, CP1_ }, /* SELNEZ.D */ }; -NMD::Pool NMD::MUL_fmt0[2] = { +static const Pool MUL_fmt0[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa00000b0, &NMD::MUL_S , 0, + 0xfc0003ff, 0xa00000b0, &MUL_S , 0, CP1_ }, /* MUL.S */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0xa00002b0, 0 , 0, @@ -19440,19 +18898,19 @@ NMD::Pool NMD::MUL_fmt0[2] = { }; -NMD::Pool NMD::SEL_fmt[2] = { +static const Pool SEL_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa00000b8, &NMD::SEL_S , 0, + 0xfc0003ff, 0xa00000b8, &SEL_S , 0, CP1_ }, /* SEL.S */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa00002b8, &NMD::SEL_D , 0, + 0xfc0003ff, 0xa00002b8, &SEL_D , 0, CP1_ }, /* SEL.D */ }; -NMD::Pool NMD::DIV_fmt0[2] = { +static const Pool DIV_fmt0[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa00000f0, &NMD::DIV_S , 0, + 0xfc0003ff, 0xa00000f0, &DIV_S , 0, CP1_ }, /* DIV.S */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0xa00002f0, 0 , 0, @@ -19460,9 +18918,9 @@ NMD::Pool NMD::DIV_fmt0[2] = { }; -NMD::Pool NMD::ADD_fmt1[2] = { +static const Pool ADD_fmt1[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa0000130, &NMD::ADD_D , 0, + 0xfc0003ff, 0xa0000130, &ADD_D , 0, CP1_ }, /* ADD.D */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0xa0000330, 0 , 0, @@ -19470,9 +18928,9 @@ NMD::Pool NMD::ADD_fmt1[2] = { }; -NMD::Pool NMD::SUB_fmt1[2] = { +static const Pool SUB_fmt1[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa0000170, &NMD::SUB_D , 0, + 0xfc0003ff, 0xa0000170, &SUB_D , 0, CP1_ }, /* SUB.D */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0xa0000370, 0 , 0, @@ -19480,9 +18938,9 @@ NMD::Pool NMD::SUB_fmt1[2] = { }; -NMD::Pool NMD::MUL_fmt1[2] = { +static const Pool MUL_fmt1[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa00001b0, &NMD::MUL_D , 0, + 0xfc0003ff, 0xa00001b0, &MUL_D , 0, CP1_ }, /* MUL.D */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0xa00003b0, 0 , 0, @@ -19490,19 +18948,19 @@ NMD::Pool NMD::MUL_fmt1[2] = { }; -NMD::Pool NMD::MADDF_fmt[2] = { +static const Pool MADDF_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa00001b8, &NMD::MADDF_S , 0, + 0xfc0003ff, 0xa00001b8, &MADDF_S , 0, CP1_ }, /* MADDF.S */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa00003b8, &NMD::MADDF_D , 0, + 0xfc0003ff, 0xa00003b8, &MADDF_D , 0, CP1_ }, /* MADDF.D */ }; -NMD::Pool NMD::DIV_fmt1[2] = { +static const Pool DIV_fmt1[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa00001f0, &NMD::DIV_D , 0, + 0xfc0003ff, 0xa00001f0, &DIV_D , 0, CP1_ }, /* DIV.D */ { reserved_block , 0 , 0 , 32, 0xfc0003ff, 0xa00003f0, 0 , 0, @@ -19510,17 +18968,17 @@ NMD::Pool NMD::DIV_fmt1[2] = { }; -NMD::Pool NMD::MSUBF_fmt[2] = { +static const Pool MSUBF_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa00001f8, &NMD::MSUBF_S , 0, + 0xfc0003ff, 0xa00001f8, &MSUBF_S , 0, CP1_ }, /* MSUBF.S */ { instruction , 0 , 0 , 32, - 0xfc0003ff, 0xa00003f8, &NMD::MSUBF_D , 0, + 0xfc0003ff, 0xa00003f8, &MSUBF_D , 0, CP1_ }, /* MSUBF.D */ }; -NMD::Pool NMD::POOL32F_0[64] = { +static const Pool POOL32F_0[64] = { { reserved_block , 0 , 0 , 32, 0xfc0001ff, 0xa0000000, 0 , 0, CP1_ }, /* POOL32F_0~*(0) */ @@ -19716,177 +19174,177 @@ NMD::Pool NMD::POOL32F_0[64] = { }; -NMD::Pool NMD::MIN_fmt[2] = { +static const Pool MIN_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc00023f, 0xa0000003, &NMD::MIN_S , 0, + 0xfc00023f, 0xa0000003, &MIN_S , 0, CP1_ }, /* MIN.S */ { instruction , 0 , 0 , 32, - 0xfc00023f, 0xa0000203, &NMD::MIN_D , 0, + 0xfc00023f, 0xa0000203, &MIN_D , 0, CP1_ }, /* MIN.D */ }; -NMD::Pool NMD::MAX_fmt[2] = { +static const Pool MAX_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc00023f, 0xa000000b, &NMD::MAX_S , 0, + 0xfc00023f, 0xa000000b, &MAX_S , 0, CP1_ }, /* MAX.S */ { instruction , 0 , 0 , 32, - 0xfc00023f, 0xa000020b, &NMD::MAX_D , 0, + 0xfc00023f, 0xa000020b, &MAX_D , 0, CP1_ }, /* MAX.D */ }; -NMD::Pool NMD::MINA_fmt[2] = { +static const Pool MINA_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc00023f, 0xa0000023, &NMD::MINA_S , 0, + 0xfc00023f, 0xa0000023, &MINA_S , 0, CP1_ }, /* MINA.S */ { instruction , 0 , 0 , 32, - 0xfc00023f, 0xa0000223, &NMD::MINA_D , 0, + 0xfc00023f, 0xa0000223, &MINA_D , 0, CP1_ }, /* MINA.D */ }; -NMD::Pool NMD::MAXA_fmt[2] = { +static const Pool MAXA_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc00023f, 0xa000002b, &NMD::MAXA_S , 0, + 0xfc00023f, 0xa000002b, &MAXA_S , 0, CP1_ }, /* MAXA.S */ { instruction , 0 , 0 , 32, - 0xfc00023f, 0xa000022b, &NMD::MAXA_D , 0, + 0xfc00023f, 0xa000022b, &MAXA_D , 0, CP1_ }, /* MAXA.D */ }; -NMD::Pool NMD::CVT_L_fmt[2] = { +static const Pool CVT_L_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000013b, &NMD::CVT_L_S , 0, + 0xfc007fff, 0xa000013b, &CVT_L_S , 0, CP1_ }, /* CVT.L.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000413b, &NMD::CVT_L_D , 0, + 0xfc007fff, 0xa000413b, &CVT_L_D , 0, CP1_ }, /* CVT.L.D */ }; -NMD::Pool NMD::RSQRT_fmt[2] = { +static const Pool RSQRT_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000023b, &NMD::RSQRT_S , 0, + 0xfc007fff, 0xa000023b, &RSQRT_S , 0, CP1_ }, /* RSQRT.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000423b, &NMD::RSQRT_D , 0, + 0xfc007fff, 0xa000423b, &RSQRT_D , 0, CP1_ }, /* RSQRT.D */ }; -NMD::Pool NMD::FLOOR_L_fmt[2] = { +static const Pool FLOOR_L_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000033b, &NMD::FLOOR_L_S , 0, + 0xfc007fff, 0xa000033b, &FLOOR_L_S , 0, CP1_ }, /* FLOOR.L.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000433b, &NMD::FLOOR_L_D , 0, + 0xfc007fff, 0xa000433b, &FLOOR_L_D , 0, CP1_ }, /* FLOOR.L.D */ }; -NMD::Pool NMD::CVT_W_fmt[2] = { +static const Pool CVT_W_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000093b, &NMD::CVT_W_S , 0, + 0xfc007fff, 0xa000093b, &CVT_W_S , 0, CP1_ }, /* CVT.W.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000493b, &NMD::CVT_W_D , 0, + 0xfc007fff, 0xa000493b, &CVT_W_D , 0, CP1_ }, /* CVT.W.D */ }; -NMD::Pool NMD::SQRT_fmt[2] = { +static const Pool SQRT_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0000a3b, &NMD::SQRT_S , 0, + 0xfc007fff, 0xa0000a3b, &SQRT_S , 0, CP1_ }, /* SQRT.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0004a3b, &NMD::SQRT_D , 0, + 0xfc007fff, 0xa0004a3b, &SQRT_D , 0, CP1_ }, /* SQRT.D */ }; -NMD::Pool NMD::FLOOR_W_fmt[2] = { +static const Pool FLOOR_W_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0000b3b, &NMD::FLOOR_W_S , 0, + 0xfc007fff, 0xa0000b3b, &FLOOR_W_S , 0, CP1_ }, /* FLOOR.W.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0004b3b, &NMD::FLOOR_W_D , 0, + 0xfc007fff, 0xa0004b3b, &FLOOR_W_D , 0, CP1_ }, /* FLOOR.W.D */ }; -NMD::Pool NMD::RECIP_fmt[2] = { +static const Pool RECIP_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000123b, &NMD::RECIP_S , 0, + 0xfc007fff, 0xa000123b, &RECIP_S , 0, CP1_ }, /* RECIP.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000523b, &NMD::RECIP_D , 0, + 0xfc007fff, 0xa000523b, &RECIP_D , 0, CP1_ }, /* RECIP.D */ }; -NMD::Pool NMD::CEIL_L_fmt[2] = { +static const Pool CEIL_L_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000133b, &NMD::CEIL_L_S , 0, + 0xfc007fff, 0xa000133b, &CEIL_L_S , 0, CP1_ }, /* CEIL.L.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000533b, &NMD::CEIL_L_D , 0, + 0xfc007fff, 0xa000533b, &CEIL_L_D , 0, CP1_ }, /* CEIL.L.D */ }; -NMD::Pool NMD::CEIL_W_fmt[2] = { +static const Pool CEIL_W_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0001b3b, &NMD::CEIL_W_S , 0, + 0xfc007fff, 0xa0001b3b, &CEIL_W_S , 0, CP1_ }, /* CEIL.W.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0005b3b, &NMD::CEIL_W_D , 0, + 0xfc007fff, 0xa0005b3b, &CEIL_W_D , 0, CP1_ }, /* CEIL.W.D */ }; -NMD::Pool NMD::TRUNC_L_fmt[2] = { +static const Pool TRUNC_L_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000233b, &NMD::TRUNC_L_S , 0, + 0xfc007fff, 0xa000233b, &TRUNC_L_S , 0, CP1_ }, /* TRUNC.L.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000633b, &NMD::TRUNC_L_D , 0, + 0xfc007fff, 0xa000633b, &TRUNC_L_D , 0, CP1_ }, /* TRUNC.L.D */ }; -NMD::Pool NMD::TRUNC_W_fmt[2] = { +static const Pool TRUNC_W_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0002b3b, &NMD::TRUNC_W_S , 0, + 0xfc007fff, 0xa0002b3b, &TRUNC_W_S , 0, CP1_ }, /* TRUNC.W.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0006b3b, &NMD::TRUNC_W_D , 0, + 0xfc007fff, 0xa0006b3b, &TRUNC_W_D , 0, CP1_ }, /* TRUNC.W.D */ }; -NMD::Pool NMD::ROUND_L_fmt[2] = { +static const Pool ROUND_L_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000333b, &NMD::ROUND_L_S , 0, + 0xfc007fff, 0xa000333b, &ROUND_L_S , 0, CP1_ }, /* ROUND.L.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000733b, &NMD::ROUND_L_D , 0, + 0xfc007fff, 0xa000733b, &ROUND_L_D , 0, CP1_ }, /* ROUND.L.D */ }; -NMD::Pool NMD::ROUND_W_fmt[2] = { +static const Pool ROUND_W_fmt[2] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0003b3b, &NMD::ROUND_W_S , 0, + 0xfc007fff, 0xa0003b3b, &ROUND_W_S , 0, CP1_ }, /* ROUND.W.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0007b3b, &NMD::ROUND_W_D , 0, + 0xfc007fff, 0xa0007b3b, &ROUND_W_D , 0, CP1_ }, /* ROUND.W.D */ }; -NMD::Pool NMD::POOL32Fxf_0[64] = { +static const Pool POOL32Fxf_0[64] = { { reserved_block , 0 , 0 , 32, 0xfc003fff, 0xa000003b, 0 , 0, CP1_ }, /* POOL32Fxf_0~*(0) */ @@ -19936,7 +19394,7 @@ NMD::Pool NMD::POOL32Fxf_0[64] = { 0xfc003fff, 0xa0000f3b, 0 , 0, CP1_ }, /* POOL32Fxf_0~*(15) */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0xa000103b, &NMD::CFC1 , 0, + 0xfc003fff, 0xa000103b, &CFC1 , 0, CP1_ }, /* CFC1 */ { reserved_block , 0 , 0 , 32, 0xfc003fff, 0xa000113b, 0 , 0, @@ -19960,7 +19418,7 @@ NMD::Pool NMD::POOL32Fxf_0[64] = { 0xfc003fff, 0xa000173b, 0 , 0, CP1_ }, /* POOL32Fxf_0~*(23) */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0xa000183b, &NMD::CTC1 , 0, + 0xfc003fff, 0xa000183b, &CTC1 , 0, CP1_ }, /* CTC1 */ { reserved_block , 0 , 0 , 32, 0xfc003fff, 0xa000193b, 0 , 0, @@ -19984,10 +19442,10 @@ NMD::Pool NMD::POOL32Fxf_0[64] = { 0xfc003fff, 0xa0001f3b, 0 , 0, CP1_ }, /* POOL32Fxf_0~*(31) */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0xa000203b, &NMD::MFC1 , 0, + 0xfc003fff, 0xa000203b, &MFC1 , 0, CP1_ }, /* MFC1 */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0xa000213b, &NMD::CVT_S_PL , 0, + 0xfc003fff, 0xa000213b, &CVT_S_PL , 0, CP1_ }, /* CVT.S.PL */ { reserved_block , 0 , 0 , 32, 0xfc003fff, 0xa000223b, 0 , 0, @@ -19996,7 +19454,7 @@ NMD::Pool NMD::POOL32Fxf_0[64] = { 0xfc003fff, 0xa000233b, 0 , 0, CP1_ }, /* TRUNC.L.fmt */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0xa000243b, &NMD::DMFC1 , 0, + 0xfc003fff, 0xa000243b, &DMFC1 , 0, CP1_ | MIPS64_ }, /* DMFC1 */ { reserved_block , 0 , 0 , 32, 0xfc003fff, 0xa000253b, 0 , 0, @@ -20008,10 +19466,10 @@ NMD::Pool NMD::POOL32Fxf_0[64] = { 0xfc003fff, 0xa000273b, 0 , 0, CP1_ }, /* POOL32Fxf_0~*(39) */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0xa000283b, &NMD::MTC1 , 0, + 0xfc003fff, 0xa000283b, &MTC1 , 0, CP1_ }, /* MTC1 */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0xa000293b, &NMD::CVT_S_PU , 0, + 0xfc003fff, 0xa000293b, &CVT_S_PU , 0, CP1_ }, /* CVT.S.PU */ { reserved_block , 0 , 0 , 32, 0xfc003fff, 0xa0002a3b, 0 , 0, @@ -20020,7 +19478,7 @@ NMD::Pool NMD::POOL32Fxf_0[64] = { 0xfc003fff, 0xa0002b3b, 0 , 0, CP1_ }, /* TRUNC.W.fmt */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0xa0002c3b, &NMD::DMTC1 , 0, + 0xfc003fff, 0xa0002c3b, &DMTC1 , 0, CP1_ | MIPS64_ }, /* DMTC1 */ { reserved_block , 0 , 0 , 32, 0xfc003fff, 0xa0002d3b, 0 , 0, @@ -20032,7 +19490,7 @@ NMD::Pool NMD::POOL32Fxf_0[64] = { 0xfc003fff, 0xa0002f3b, 0 , 0, CP1_ }, /* POOL32Fxf_0~*(47) */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0xa000303b, &NMD::MFHC1 , 0, + 0xfc003fff, 0xa000303b, &MFHC1 , 0, CP1_ }, /* MFHC1 */ { reserved_block , 0 , 0 , 32, 0xfc003fff, 0xa000313b, 0 , 0, @@ -20056,7 +19514,7 @@ NMD::Pool NMD::POOL32Fxf_0[64] = { 0xfc003fff, 0xa000373b, 0 , 0, CP1_ }, /* POOL32Fxf_0~*(55) */ { instruction , 0 , 0 , 32, - 0xfc003fff, 0xa000383b, &NMD::MTHC1 , 0, + 0xfc003fff, 0xa000383b, &MTHC1 , 0, CP1_ }, /* MTHC1 */ { reserved_block , 0 , 0 , 32, 0xfc003fff, 0xa000393b, 0 , 0, @@ -20082,12 +19540,12 @@ NMD::Pool NMD::POOL32Fxf_0[64] = { }; -NMD::Pool NMD::MOV_fmt[4] = { +static const Pool MOV_fmt[4] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000007b, &NMD::MOV_S , 0, + 0xfc007fff, 0xa000007b, &MOV_S , 0, CP1_ }, /* MOV.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000207b, &NMD::MOV_D , 0, + 0xfc007fff, 0xa000207b, &MOV_D , 0, CP1_ }, /* MOV.D */ { reserved_block , 0 , 0 , 32, 0xfc007fff, 0xa000407b, 0 , 0, @@ -20098,12 +19556,12 @@ NMD::Pool NMD::MOV_fmt[4] = { }; -NMD::Pool NMD::ABS_fmt[4] = { +static const Pool ABS_fmt[4] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000037b, &NMD::ABS_S , 0, + 0xfc007fff, 0xa000037b, &ABS_S , 0, CP1_ }, /* ABS.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000237b, &NMD::ABS_D , 0, + 0xfc007fff, 0xa000237b, &ABS_D , 0, CP1_ }, /* ABS.D */ { reserved_block , 0 , 0 , 32, 0xfc007fff, 0xa000437b, 0 , 0, @@ -20114,12 +19572,12 @@ NMD::Pool NMD::ABS_fmt[4] = { }; -NMD::Pool NMD::NEG_fmt[4] = { +static const Pool NEG_fmt[4] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0000b7b, &NMD::NEG_S , 0, + 0xfc007fff, 0xa0000b7b, &NEG_S , 0, CP1_ }, /* NEG.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0002b7b, &NMD::NEG_D , 0, + 0xfc007fff, 0xa0002b7b, &NEG_D , 0, CP1_ }, /* NEG.D */ { reserved_block , 0 , 0 , 32, 0xfc007fff, 0xa0004b7b, 0 , 0, @@ -20130,15 +19588,15 @@ NMD::Pool NMD::NEG_fmt[4] = { }; -NMD::Pool NMD::CVT_D_fmt[4] = { +static const Pool CVT_D_fmt[4] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000137b, &NMD::CVT_D_S , 0, + 0xfc007fff, 0xa000137b, &CVT_D_S , 0, CP1_ }, /* CVT.D.S */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000337b, &NMD::CVT_D_W , 0, + 0xfc007fff, 0xa000337b, &CVT_D_W , 0, CP1_ }, /* CVT.D.W */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa000537b, &NMD::CVT_D_L , 0, + 0xfc007fff, 0xa000537b, &CVT_D_L , 0, CP1_ }, /* CVT.D.L */ { reserved_block , 0 , 0 , 32, 0xfc007fff, 0xa000737b, 0 , 0, @@ -20146,15 +19604,15 @@ NMD::Pool NMD::CVT_D_fmt[4] = { }; -NMD::Pool NMD::CVT_S_fmt[4] = { +static const Pool CVT_S_fmt[4] = { { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0001b7b, &NMD::CVT_S_D , 0, + 0xfc007fff, 0xa0001b7b, &CVT_S_D , 0, CP1_ }, /* CVT.S.D */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0003b7b, &NMD::CVT_S_W , 0, + 0xfc007fff, 0xa0003b7b, &CVT_S_W , 0, CP1_ }, /* CVT.S.W */ { instruction , 0 , 0 , 32, - 0xfc007fff, 0xa0005b7b, &NMD::CVT_S_L , 0, + 0xfc007fff, 0xa0005b7b, &CVT_S_L , 0, CP1_ }, /* CVT.S.L */ { reserved_block , 0 , 0 , 32, 0xfc007fff, 0xa0007b7b, 0 , 0, @@ -20162,7 +19620,7 @@ NMD::Pool NMD::CVT_S_fmt[4] = { }; -NMD::Pool NMD::POOL32Fxf_1[32] = { +static const Pool POOL32Fxf_1[32] = { { pool , MOV_fmt , 4 , 32, 0xfc001fff, 0xa000007b, 0 , 0, CP1_ }, /* MOV.fmt */ @@ -20262,7 +19720,7 @@ NMD::Pool NMD::POOL32Fxf_1[32] = { }; -NMD::Pool NMD::POOL32Fxf[4] = { +static const Pool POOL32Fxf[4] = { { pool , POOL32Fxf_0 , 64 , 32, 0xfc0000ff, 0xa000003b, 0 , 0, CP1_ }, /* POOL32Fxf_0 */ @@ -20278,7 +19736,7 @@ NMD::Pool NMD::POOL32Fxf[4] = { }; -NMD::Pool NMD::POOL32F_3[8] = { +static const Pool POOL32F_3[8] = { { pool , MIN_fmt , 2 , 32, 0xfc00003f, 0xa0000003, 0 , 0, CP1_ }, /* MIN.fmt */ @@ -20306,66 +19764,66 @@ NMD::Pool NMD::POOL32F_3[8] = { }; -NMD::Pool NMD::CMP_condn_S[32] = { +static const Pool CMP_condn_S[32] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000005, &NMD::CMP_AF_S , 0, + 0xfc0007ff, 0xa0000005, &CMP_AF_S , 0, CP1_ }, /* CMP.AF.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000045, &NMD::CMP_UN_S , 0, + 0xfc0007ff, 0xa0000045, &CMP_UN_S , 0, CP1_ }, /* CMP.UN.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000085, &NMD::CMP_EQ_S , 0, + 0xfc0007ff, 0xa0000085, &CMP_EQ_S , 0, CP1_ }, /* CMP.EQ.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa00000c5, &NMD::CMP_UEQ_S , 0, + 0xfc0007ff, 0xa00000c5, &CMP_UEQ_S , 0, CP1_ }, /* CMP.UEQ.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000105, &NMD::CMP_LT_S , 0, + 0xfc0007ff, 0xa0000105, &CMP_LT_S , 0, CP1_ }, /* CMP.LT.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000145, &NMD::CMP_ULT_S , 0, + 0xfc0007ff, 0xa0000145, &CMP_ULT_S , 0, CP1_ }, /* CMP.ULT.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000185, &NMD::CMP_LE_S , 0, + 0xfc0007ff, 0xa0000185, &CMP_LE_S , 0, CP1_ }, /* CMP.LE.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa00001c5, &NMD::CMP_ULE_S , 0, + 0xfc0007ff, 0xa00001c5, &CMP_ULE_S , 0, CP1_ }, /* CMP.ULE.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000205, &NMD::CMP_SAF_S , 0, + 0xfc0007ff, 0xa0000205, &CMP_SAF_S , 0, CP1_ }, /* CMP.SAF.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000245, &NMD::CMP_SUN_S , 0, + 0xfc0007ff, 0xa0000245, &CMP_SUN_S , 0, CP1_ }, /* CMP.SUN.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000285, &NMD::CMP_SEQ_S , 0, + 0xfc0007ff, 0xa0000285, &CMP_SEQ_S , 0, CP1_ }, /* CMP.SEQ.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa00002c5, &NMD::CMP_SUEQ_S , 0, + 0xfc0007ff, 0xa00002c5, &CMP_SUEQ_S , 0, CP1_ }, /* CMP.SUEQ.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000305, &NMD::CMP_SLT_S , 0, + 0xfc0007ff, 0xa0000305, &CMP_SLT_S , 0, CP1_ }, /* CMP.SLT.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000345, &NMD::CMP_SULT_S , 0, + 0xfc0007ff, 0xa0000345, &CMP_SULT_S , 0, CP1_ }, /* CMP.SULT.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000385, &NMD::CMP_SLE_S , 0, + 0xfc0007ff, 0xa0000385, &CMP_SLE_S , 0, CP1_ }, /* CMP.SLE.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa00003c5, &NMD::CMP_SULE_S , 0, + 0xfc0007ff, 0xa00003c5, &CMP_SULE_S , 0, CP1_ }, /* CMP.SULE.S */ { reserved_block , 0 , 0 , 32, 0xfc0007ff, 0xa0000405, 0 , 0, CP1_ }, /* CMP.condn.S~*(16) */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000445, &NMD::CMP_OR_S , 0, + 0xfc0007ff, 0xa0000445, &CMP_OR_S , 0, CP1_ }, /* CMP.OR.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000485, &NMD::CMP_UNE_S , 0, + 0xfc0007ff, 0xa0000485, &CMP_UNE_S , 0, CP1_ }, /* CMP.UNE.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa00004c5, &NMD::CMP_NE_S , 0, + 0xfc0007ff, 0xa00004c5, &CMP_NE_S , 0, CP1_ }, /* CMP.NE.S */ { reserved_block , 0 , 0 , 32, 0xfc0007ff, 0xa0000505, 0 , 0, @@ -20383,13 +19841,13 @@ NMD::Pool NMD::CMP_condn_S[32] = { 0xfc0007ff, 0xa0000605, 0 , 0, CP1_ }, /* CMP.condn.S~*(24) */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000645, &NMD::CMP_SOR_S , 0, + 0xfc0007ff, 0xa0000645, &CMP_SOR_S , 0, CP1_ }, /* CMP.SOR.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000685, &NMD::CMP_SUNE_S , 0, + 0xfc0007ff, 0xa0000685, &CMP_SUNE_S , 0, CP1_ }, /* CMP.SUNE.S */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa00006c5, &NMD::CMP_SNE_S , 0, + 0xfc0007ff, 0xa00006c5, &CMP_SNE_S , 0, CP1_ }, /* CMP.SNE.S */ { reserved_block , 0 , 0 , 32, 0xfc0007ff, 0xa0000705, 0 , 0, @@ -20406,66 +19864,66 @@ NMD::Pool NMD::CMP_condn_S[32] = { }; -NMD::Pool NMD::CMP_condn_D[32] = { +static const Pool CMP_condn_D[32] = { { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000015, &NMD::CMP_AF_D , 0, + 0xfc0007ff, 0xa0000015, &CMP_AF_D , 0, CP1_ }, /* CMP.AF.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000055, &NMD::CMP_UN_D , 0, + 0xfc0007ff, 0xa0000055, &CMP_UN_D , 0, CP1_ }, /* CMP.UN.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000095, &NMD::CMP_EQ_D , 0, + 0xfc0007ff, 0xa0000095, &CMP_EQ_D , 0, CP1_ }, /* CMP.EQ.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa00000d5, &NMD::CMP_UEQ_D , 0, + 0xfc0007ff, 0xa00000d5, &CMP_UEQ_D , 0, CP1_ }, /* CMP.UEQ.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000115, &NMD::CMP_LT_D , 0, + 0xfc0007ff, 0xa0000115, &CMP_LT_D , 0, CP1_ }, /* CMP.LT.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000155, &NMD::CMP_ULT_D , 0, + 0xfc0007ff, 0xa0000155, &CMP_ULT_D , 0, CP1_ }, /* CMP.ULT.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000195, &NMD::CMP_LE_D , 0, + 0xfc0007ff, 0xa0000195, &CMP_LE_D , 0, CP1_ }, /* CMP.LE.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa00001d5, &NMD::CMP_ULE_D , 0, + 0xfc0007ff, 0xa00001d5, &CMP_ULE_D , 0, CP1_ }, /* CMP.ULE.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000215, &NMD::CMP_SAF_D , 0, + 0xfc0007ff, 0xa0000215, &CMP_SAF_D , 0, CP1_ }, /* CMP.SAF.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000255, &NMD::CMP_SUN_D , 0, + 0xfc0007ff, 0xa0000255, &CMP_SUN_D , 0, CP1_ }, /* CMP.SUN.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000295, &NMD::CMP_SEQ_D , 0, + 0xfc0007ff, 0xa0000295, &CMP_SEQ_D , 0, CP1_ }, /* CMP.SEQ.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa00002d5, &NMD::CMP_SUEQ_D , 0, + 0xfc0007ff, 0xa00002d5, &CMP_SUEQ_D , 0, CP1_ }, /* CMP.SUEQ.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000315, &NMD::CMP_SLT_D , 0, + 0xfc0007ff, 0xa0000315, &CMP_SLT_D , 0, CP1_ }, /* CMP.SLT.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000355, &NMD::CMP_SULT_D , 0, + 0xfc0007ff, 0xa0000355, &CMP_SULT_D , 0, CP1_ }, /* CMP.SULT.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000395, &NMD::CMP_SLE_D , 0, + 0xfc0007ff, 0xa0000395, &CMP_SLE_D , 0, CP1_ }, /* CMP.SLE.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa00003d5, &NMD::CMP_SULE_D , 0, + 0xfc0007ff, 0xa00003d5, &CMP_SULE_D , 0, CP1_ }, /* CMP.SULE.D */ { reserved_block , 0 , 0 , 32, 0xfc0007ff, 0xa0000415, 0 , 0, CP1_ }, /* CMP.condn.D~*(16) */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000455, &NMD::CMP_OR_D , 0, + 0xfc0007ff, 0xa0000455, &CMP_OR_D , 0, CP1_ }, /* CMP.OR.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000495, &NMD::CMP_UNE_D , 0, + 0xfc0007ff, 0xa0000495, &CMP_UNE_D , 0, CP1_ }, /* CMP.UNE.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa00004d5, &NMD::CMP_NE_D , 0, + 0xfc0007ff, 0xa00004d5, &CMP_NE_D , 0, CP1_ }, /* CMP.NE.D */ { reserved_block , 0 , 0 , 32, 0xfc0007ff, 0xa0000515, 0 , 0, @@ -20483,13 +19941,13 @@ NMD::Pool NMD::CMP_condn_D[32] = { 0xfc0007ff, 0xa0000615, 0 , 0, CP1_ }, /* CMP.condn.D~*(24) */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000655, &NMD::CMP_SOR_D , 0, + 0xfc0007ff, 0xa0000655, &CMP_SOR_D , 0, CP1_ }, /* CMP.SOR.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa0000695, &NMD::CMP_SUNE_D , 0, + 0xfc0007ff, 0xa0000695, &CMP_SUNE_D , 0, CP1_ }, /* CMP.SUNE.D */ { instruction , 0 , 0 , 32, - 0xfc0007ff, 0xa00006d5, &NMD::CMP_SNE_D , 0, + 0xfc0007ff, 0xa00006d5, &CMP_SNE_D , 0, CP1_ }, /* CMP.SNE.D */ { reserved_block , 0 , 0 , 32, 0xfc0007ff, 0xa0000715, 0 , 0, @@ -20506,7 +19964,7 @@ NMD::Pool NMD::CMP_condn_D[32] = { }; -NMD::Pool NMD::POOL32F_5[8] = { +static const Pool POOL32F_5[8] = { { pool , CMP_condn_S , 32 , 32, 0xfc00003f, 0xa0000005, 0 , 0, CP1_ }, /* CMP.condn.S */ @@ -20534,7 +19992,7 @@ NMD::Pool NMD::POOL32F_5[8] = { }; -NMD::Pool NMD::POOL32F[8] = { +static const Pool POOL32F[8] = { { pool , POOL32F_0 , 64 , 32, 0xfc000007, 0xa0000000, 0 , 0, CP1_ }, /* POOL32F_0 */ @@ -20562,18 +20020,18 @@ NMD::Pool NMD::POOL32F[8] = { }; -NMD::Pool NMD::POOL32S_0[64] = { +static const Pool POOL32S_0[64] = { { reserved_block , 0 , 0 , 32, 0xfc0001ff, 0xc0000000, 0 , 0, 0x0 }, /* POOL32S_0~*(0) */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc0000008, &NMD::DLSA , 0, + 0xfc0001ff, 0xc0000008, &DLSA , 0, MIPS64_ }, /* DLSA */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc0000010, &NMD::DSLLV , 0, + 0xfc0001ff, 0xc0000010, &DSLLV , 0, MIPS64_ }, /* DSLLV */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc0000018, &NMD::DMUL , 0, + 0xfc0001ff, 0xc0000018, &DMUL , 0, MIPS64_ }, /* DMUL */ { reserved_block , 0 , 0 , 32, 0xfc0001ff, 0xc0000020, 0 , 0, @@ -20594,10 +20052,10 @@ NMD::Pool NMD::POOL32S_0[64] = { 0xfc0001ff, 0xc0000048, 0 , 0, 0x0 }, /* POOL32S_0~*(9) */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc0000050, &NMD::DSRLV , 0, + 0xfc0001ff, 0xc0000050, &DSRLV , 0, MIPS64_ }, /* DSRLV */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc0000058, &NMD::DMUH , 0, + 0xfc0001ff, 0xc0000058, &DMUH , 0, MIPS64_ }, /* DMUH */ { reserved_block , 0 , 0 , 32, 0xfc0001ff, 0xc0000060, 0 , 0, @@ -20618,10 +20076,10 @@ NMD::Pool NMD::POOL32S_0[64] = { 0xfc0001ff, 0xc0000088, 0 , 0, 0x0 }, /* POOL32S_0~*(17) */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc0000090, &NMD::DSRAV , 0, + 0xfc0001ff, 0xc0000090, &DSRAV , 0, MIPS64_ }, /* DSRAV */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc0000098, &NMD::DMULU , 0, + 0xfc0001ff, 0xc0000098, &DMULU , 0, MIPS64_ }, /* DMULU */ { reserved_block , 0 , 0 , 32, 0xfc0001ff, 0xc00000a0, 0 , 0, @@ -20642,10 +20100,10 @@ NMD::Pool NMD::POOL32S_0[64] = { 0xfc0001ff, 0xc00000c8, 0 , 0, 0x0 }, /* POOL32S_0~*(25) */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc00000d0, &NMD::DROTRV , 0, + 0xfc0001ff, 0xc00000d0, &DROTRV , 0, MIPS64_ }, /* DROTRV */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc00000d8, &NMD::DMUHU , 0, + 0xfc0001ff, 0xc00000d8, &DMUHU , 0, MIPS64_ }, /* DMUHU */ { reserved_block , 0 , 0 , 32, 0xfc0001ff, 0xc00000e0, 0 , 0, @@ -20666,10 +20124,10 @@ NMD::Pool NMD::POOL32S_0[64] = { 0xfc0001ff, 0xc0000108, 0 , 0, 0x0 }, /* POOL32S_0~*(33) */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc0000110, &NMD::DADD , 0, + 0xfc0001ff, 0xc0000110, &DADD , 0, MIPS64_ }, /* DADD */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc0000118, &NMD::DDIV , 0, + 0xfc0001ff, 0xc0000118, &DDIV , 0, MIPS64_ }, /* DDIV */ { reserved_block , 0 , 0 , 32, 0xfc0001ff, 0xc0000120, 0 , 0, @@ -20690,10 +20148,10 @@ NMD::Pool NMD::POOL32S_0[64] = { 0xfc0001ff, 0xc0000148, 0 , 0, 0x0 }, /* POOL32S_0~*(41) */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc0000150, &NMD::DADDU , 0, + 0xfc0001ff, 0xc0000150, &DADDU , 0, MIPS64_ }, /* DADDU */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc0000158, &NMD::DMOD , 0, + 0xfc0001ff, 0xc0000158, &DMOD , 0, MIPS64_ }, /* DMOD */ { reserved_block , 0 , 0 , 32, 0xfc0001ff, 0xc0000160, 0 , 0, @@ -20714,10 +20172,10 @@ NMD::Pool NMD::POOL32S_0[64] = { 0xfc0001ff, 0xc0000188, 0 , 0, 0x0 }, /* POOL32S_0~*(49) */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc0000190, &NMD::DSUB , 0, + 0xfc0001ff, 0xc0000190, &DSUB , 0, MIPS64_ }, /* DSUB */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc0000198, &NMD::DDIVU , 0, + 0xfc0001ff, 0xc0000198, &DDIVU , 0, MIPS64_ }, /* DDIVU */ { reserved_block , 0 , 0 , 32, 0xfc0001ff, 0xc00001a0, 0 , 0, @@ -20738,10 +20196,10 @@ NMD::Pool NMD::POOL32S_0[64] = { 0xfc0001ff, 0xc00001c8, 0 , 0, 0x0 }, /* POOL32S_0~*(57) */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc00001d0, &NMD::DSUBU , 0, + 0xfc0001ff, 0xc00001d0, &DSUBU , 0, MIPS64_ }, /* DSUBU */ { instruction , 0 , 0 , 32, - 0xfc0001ff, 0xc00001d8, &NMD::DMODU , 0, + 0xfc0001ff, 0xc00001d8, &DMODU , 0, MIPS64_ }, /* DMODU */ { reserved_block , 0 , 0 , 32, 0xfc0001ff, 0xc00001e0, 0 , 0, @@ -20758,7 +20216,7 @@ NMD::Pool NMD::POOL32S_0[64] = { }; -NMD::Pool NMD::POOL32Sxf_4[128] = { +static const Pool POOL32Sxf_4[128] = { { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0xc000013c, 0 , 0, 0x0 }, /* POOL32Sxf_4~*(0) */ @@ -20871,7 +20329,7 @@ NMD::Pool NMD::POOL32Sxf_4[128] = { 0xfc00ffff, 0xc000493c, 0 , 0, 0x0 }, /* POOL32Sxf_4~*(36) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0xc0004b3c, &NMD::DCLO , 0, + 0xfc00ffff, 0xc0004b3c, &DCLO , 0, MIPS64_ }, /* DCLO */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0xc0004d3c, 0 , 0, @@ -20895,7 +20353,7 @@ NMD::Pool NMD::POOL32Sxf_4[128] = { 0xfc00ffff, 0xc000593c, 0 , 0, 0x0 }, /* POOL32Sxf_4~*(44) */ { instruction , 0 , 0 , 32, - 0xfc00ffff, 0xc0005b3c, &NMD::DCLZ , 0, + 0xfc00ffff, 0xc0005b3c, &DCLZ , 0, MIPS64_ }, /* DCLZ */ { reserved_block , 0 , 0 , 32, 0xfc00ffff, 0xc0005d3c, 0 , 0, @@ -21146,7 +20604,7 @@ NMD::Pool NMD::POOL32Sxf_4[128] = { }; -NMD::Pool NMD::POOL32Sxf[8] = { +static const Pool POOL32Sxf[8] = { { reserved_block , 0 , 0 , 32, 0xfc0001ff, 0xc000003c, 0 , 0, 0x0 }, /* POOL32Sxf~*(0) */ @@ -21174,12 +20632,12 @@ NMD::Pool NMD::POOL32Sxf[8] = { }; -NMD::Pool NMD::POOL32S_4[8] = { +static const Pool POOL32S_4[8] = { { instruction , 0 , 0 , 32, - 0xfc00003f, 0xc0000004, &NMD::EXTD , 0, + 0xfc00003f, 0xc0000004, &EXTD , 0, MIPS64_ }, /* EXTD */ { instruction , 0 , 0 , 32, - 0xfc00003f, 0xc000000c, &NMD::EXTD32 , 0, + 0xfc00003f, 0xc000000c, &EXTD32 , 0, MIPS64_ }, /* EXTD32 */ { reserved_block , 0 , 0 , 32, 0xfc00003f, 0xc0000014, 0 , 0, @@ -21202,7 +20660,7 @@ NMD::Pool NMD::POOL32S_4[8] = { }; -NMD::Pool NMD::POOL32S[8] = { +static const Pool POOL32S[8] = { { pool , POOL32S_0 , 64 , 32, 0xfc000007, 0xc0000000, 0 , 0, 0x0 }, /* POOL32S_0 */ @@ -21230,29 +20688,29 @@ NMD::Pool NMD::POOL32S[8] = { }; -NMD::Pool NMD::P_LUI[2] = { +static const Pool P_LUI[2] = { { instruction , 0 , 0 , 32, - 0xfc000002, 0xe0000000, &NMD::LUI , 0, + 0xfc000002, 0xe0000000, &LUI , 0, 0x0 }, /* LUI */ { instruction , 0 , 0 , 32, - 0xfc000002, 0xe0000002, &NMD::ALUIPC , 0, + 0xfc000002, 0xe0000002, &ALUIPC , 0, 0x0 }, /* ALUIPC */ }; -NMD::Pool NMD::P_GP_LH[2] = { +static const Pool P_GP_LH[2] = { { instruction , 0 , 0 , 32, - 0xfc1c0001, 0x44100000, &NMD::LH_GP_ , 0, + 0xfc1c0001, 0x44100000, &LH_GP_ , 0, 0x0 }, /* LH[GP] */ { instruction , 0 , 0 , 32, - 0xfc1c0001, 0x44100001, &NMD::LHU_GP_ , 0, + 0xfc1c0001, 0x44100001, &LHU_GP_ , 0, 0x0 }, /* LHU[GP] */ }; -NMD::Pool NMD::P_GP_SH[2] = { +static const Pool P_GP_SH[2] = { { instruction , 0 , 0 , 32, - 0xfc1c0001, 0x44140000, &NMD::SH_GP_ , 0, + 0xfc1c0001, 0x44140000, &SH_GP_ , 0, 0x0 }, /* SH[GP] */ { reserved_block , 0 , 0 , 32, 0xfc1c0001, 0x44140001, 0 , 0, @@ -21260,25 +20718,25 @@ NMD::Pool NMD::P_GP_SH[2] = { }; -NMD::Pool NMD::P_GP_CP1[4] = { +static const Pool P_GP_CP1[4] = { { instruction , 0 , 0 , 32, - 0xfc1c0003, 0x44180000, &NMD::LWC1_GP_ , 0, + 0xfc1c0003, 0x44180000, &LWC1_GP_ , 0, CP1_ }, /* LWC1[GP] */ { instruction , 0 , 0 , 32, - 0xfc1c0003, 0x44180001, &NMD::SWC1_GP_ , 0, + 0xfc1c0003, 0x44180001, &SWC1_GP_ , 0, CP1_ }, /* SWC1[GP] */ { instruction , 0 , 0 , 32, - 0xfc1c0003, 0x44180002, &NMD::LDC1_GP_ , 0, + 0xfc1c0003, 0x44180002, &LDC1_GP_ , 0, CP1_ }, /* LDC1[GP] */ { instruction , 0 , 0 , 32, - 0xfc1c0003, 0x44180003, &NMD::SDC1_GP_ , 0, + 0xfc1c0003, 0x44180003, &SDC1_GP_ , 0, CP1_ }, /* SDC1[GP] */ }; -NMD::Pool NMD::P_GP_M64[4] = { +static const Pool P_GP_M64[4] = { { instruction , 0 , 0 , 32, - 0xfc1c0003, 0x441c0000, &NMD::LWU_GP_ , 0, + 0xfc1c0003, 0x441c0000, &LWU_GP_ , 0, MIPS64_ }, /* LWU[GP] */ { reserved_block , 0 , 0 , 32, 0xfc1c0003, 0x441c0001, 0 , 0, @@ -21292,18 +20750,18 @@ NMD::Pool NMD::P_GP_M64[4] = { }; -NMD::Pool NMD::P_GP_BH[8] = { +static const Pool P_GP_BH[8] = { { instruction , 0 , 0 , 32, - 0xfc1c0000, 0x44000000, &NMD::LB_GP_ , 0, + 0xfc1c0000, 0x44000000, &LB_GP_ , 0, 0x0 }, /* LB[GP] */ { instruction , 0 , 0 , 32, - 0xfc1c0000, 0x44040000, &NMD::SB_GP_ , 0, + 0xfc1c0000, 0x44040000, &SB_GP_ , 0, 0x0 }, /* SB[GP] */ { instruction , 0 , 0 , 32, - 0xfc1c0000, 0x44080000, &NMD::LBU_GP_ , 0, + 0xfc1c0000, 0x44080000, &LBU_GP_ , 0, 0x0 }, /* LBU[GP] */ { instruction , 0 , 0 , 32, - 0xfc1c0000, 0x440c0000, &NMD::ADDIU_GP_B_ , 0, + 0xfc1c0000, 0x440c0000, &ADDIU_GP_B_ , 0, 0x0 }, /* ADDIU[GP.B] */ { pool , P_GP_LH , 2 , 32, 0xfc1c0000, 0x44100000, 0 , 0, @@ -21320,136 +20778,136 @@ NMD::Pool NMD::P_GP_BH[8] = { }; -NMD::Pool NMD::P_LS_U12[16] = { +static const Pool P_LS_U12[16] = { { instruction , 0 , 0 , 32, - 0xfc00f000, 0x84000000, &NMD::LB_U12_ , 0, + 0xfc00f000, 0x84000000, &LB_U12_ , 0, 0x0 }, /* LB[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x84001000, &NMD::SB_U12_ , 0, + 0xfc00f000, 0x84001000, &SB_U12_ , 0, 0x0 }, /* SB[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x84002000, &NMD::LBU_U12_ , 0, + 0xfc00f000, 0x84002000, &LBU_U12_ , 0, 0x0 }, /* LBU[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x84003000, &NMD::PREF_U12_ , 0, + 0xfc00f000, 0x84003000, &PREF_U12_ , 0, 0x0 }, /* PREF[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x84004000, &NMD::LH_U12_ , 0, + 0xfc00f000, 0x84004000, &LH_U12_ , 0, 0x0 }, /* LH[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x84005000, &NMD::SH_U12_ , 0, + 0xfc00f000, 0x84005000, &SH_U12_ , 0, 0x0 }, /* SH[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x84006000, &NMD::LHU_U12_ , 0, + 0xfc00f000, 0x84006000, &LHU_U12_ , 0, 0x0 }, /* LHU[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x84007000, &NMD::LWU_U12_ , 0, + 0xfc00f000, 0x84007000, &LWU_U12_ , 0, MIPS64_ }, /* LWU[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x84008000, &NMD::LW_U12_ , 0, + 0xfc00f000, 0x84008000, &LW_U12_ , 0, 0x0 }, /* LW[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x84009000, &NMD::SW_U12_ , 0, + 0xfc00f000, 0x84009000, &SW_U12_ , 0, 0x0 }, /* SW[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x8400a000, &NMD::LWC1_U12_ , 0, + 0xfc00f000, 0x8400a000, &LWC1_U12_ , 0, CP1_ }, /* LWC1[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x8400b000, &NMD::SWC1_U12_ , 0, + 0xfc00f000, 0x8400b000, &SWC1_U12_ , 0, CP1_ }, /* SWC1[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x8400c000, &NMD::LD_U12_ , 0, + 0xfc00f000, 0x8400c000, &LD_U12_ , 0, MIPS64_ }, /* LD[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x8400d000, &NMD::SD_U12_ , 0, + 0xfc00f000, 0x8400d000, &SD_U12_ , 0, MIPS64_ }, /* SD[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x8400e000, &NMD::LDC1_U12_ , 0, + 0xfc00f000, 0x8400e000, &LDC1_U12_ , 0, CP1_ }, /* LDC1[U12] */ { instruction , 0 , 0 , 32, - 0xfc00f000, 0x8400f000, &NMD::SDC1_U12_ , 0, + 0xfc00f000, 0x8400f000, &SDC1_U12_ , 0, CP1_ }, /* SDC1[U12] */ }; -NMD::Pool NMD::P_PREF_S9_[2] = { +static const Pool P_PREF_S9_[2] = { { instruction , 0 , 0 , 32, - 0xffe07f00, 0xa7e01800, &NMD::SYNCI , 0, + 0xffe07f00, 0xa7e01800, &SYNCI , 0, 0x0 }, /* SYNCI */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4001800, &NMD::PREF_S9_ , &NMD::PREF_S9__cond , + 0xfc007f00, 0xa4001800, &PREF_S9_ , &PREF_S9__cond , 0x0 }, /* PREF[S9] */ }; -NMD::Pool NMD::P_LS_S0[16] = { +static const Pool P_LS_S0[16] = { { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4000000, &NMD::LB_S9_ , 0, + 0xfc007f00, 0xa4000000, &LB_S9_ , 0, 0x0 }, /* LB[S9] */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4000800, &NMD::SB_S9_ , 0, + 0xfc007f00, 0xa4000800, &SB_S9_ , 0, 0x0 }, /* SB[S9] */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4001000, &NMD::LBU_S9_ , 0, + 0xfc007f00, 0xa4001000, &LBU_S9_ , 0, 0x0 }, /* LBU[S9] */ { pool , P_PREF_S9_ , 2 , 32, 0xfc007f00, 0xa4001800, 0 , 0, 0x0 }, /* P.PREF[S9] */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4002000, &NMD::LH_S9_ , 0, + 0xfc007f00, 0xa4002000, &LH_S9_ , 0, 0x0 }, /* LH[S9] */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4002800, &NMD::SH_S9_ , 0, + 0xfc007f00, 0xa4002800, &SH_S9_ , 0, 0x0 }, /* SH[S9] */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4003000, &NMD::LHU_S9_ , 0, + 0xfc007f00, 0xa4003000, &LHU_S9_ , 0, 0x0 }, /* LHU[S9] */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4003800, &NMD::LWU_S9_ , 0, + 0xfc007f00, 0xa4003800, &LWU_S9_ , 0, MIPS64_ }, /* LWU[S9] */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4004000, &NMD::LW_S9_ , 0, + 0xfc007f00, 0xa4004000, &LW_S9_ , 0, 0x0 }, /* LW[S9] */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4004800, &NMD::SW_S9_ , 0, + 0xfc007f00, 0xa4004800, &SW_S9_ , 0, 0x0 }, /* SW[S9] */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4005000, &NMD::LWC1_S9_ , 0, + 0xfc007f00, 0xa4005000, &LWC1_S9_ , 0, CP1_ }, /* LWC1[S9] */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4005800, &NMD::SWC1_S9_ , 0, + 0xfc007f00, 0xa4005800, &SWC1_S9_ , 0, CP1_ }, /* SWC1[S9] */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4006000, &NMD::LD_S9_ , 0, + 0xfc007f00, 0xa4006000, &LD_S9_ , 0, MIPS64_ }, /* LD[S9] */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4006800, &NMD::SD_S9_ , 0, + 0xfc007f00, 0xa4006800, &SD_S9_ , 0, MIPS64_ }, /* SD[S9] */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4007000, &NMD::LDC1_S9_ , 0, + 0xfc007f00, 0xa4007000, &LDC1_S9_ , 0, CP1_ }, /* LDC1[S9] */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4007800, &NMD::SDC1_S9_ , 0, + 0xfc007f00, 0xa4007800, &SDC1_S9_ , 0, CP1_ }, /* SDC1[S9] */ }; -NMD::Pool NMD::ASET_ACLR[2] = { +static const Pool ASET_ACLR[2] = { { instruction , 0 , 0 , 32, - 0xfe007f00, 0xa4001100, &NMD::ASET , 0, + 0xfe007f00, 0xa4001100, &ASET , 0, MCU_ }, /* ASET */ { instruction , 0 , 0 , 32, - 0xfe007f00, 0xa6001100, &NMD::ACLR , 0, + 0xfe007f00, 0xa6001100, &ACLR , 0, MCU_ }, /* ACLR */ }; -NMD::Pool NMD::P_LL[4] = { +static const Pool P_LL[4] = { { instruction , 0 , 0 , 32, - 0xfc007f03, 0xa4005100, &NMD::LL , 0, + 0xfc007f03, 0xa4005100, &LL , 0, 0x0 }, /* LL */ { instruction , 0 , 0 , 32, - 0xfc007f03, 0xa4005101, &NMD::LLWP , 0, + 0xfc007f03, 0xa4005101, &LLWP , 0, XNP_ }, /* LLWP */ { reserved_block , 0 , 0 , 32, 0xfc007f03, 0xa4005102, 0 , 0, @@ -21460,12 +20918,12 @@ NMD::Pool NMD::P_LL[4] = { }; -NMD::Pool NMD::P_SC[4] = { +static const Pool P_SC[4] = { { instruction , 0 , 0 , 32, - 0xfc007f03, 0xa4005900, &NMD::SC , 0, + 0xfc007f03, 0xa4005900, &SC , 0, 0x0 }, /* SC */ { instruction , 0 , 0 , 32, - 0xfc007f03, 0xa4005901, &NMD::SCWP , 0, + 0xfc007f03, 0xa4005901, &SCWP , 0, XNP_ }, /* SCWP */ { reserved_block , 0 , 0 , 32, 0xfc007f03, 0xa4005902, 0 , 0, @@ -21476,12 +20934,12 @@ NMD::Pool NMD::P_SC[4] = { }; -NMD::Pool NMD::P_LLD[8] = { +static const Pool P_LLD[8] = { { instruction , 0 , 0 , 32, - 0xfc007f07, 0xa4007100, &NMD::LLD , 0, + 0xfc007f07, 0xa4007100, &LLD , 0, MIPS64_ }, /* LLD */ { instruction , 0 , 0 , 32, - 0xfc007f07, 0xa4007101, &NMD::LLDP , 0, + 0xfc007f07, 0xa4007101, &LLDP , 0, MIPS64_ }, /* LLDP */ { reserved_block , 0 , 0 , 32, 0xfc007f07, 0xa4007102, 0 , 0, @@ -21504,12 +20962,12 @@ NMD::Pool NMD::P_LLD[8] = { }; -NMD::Pool NMD::P_SCD[8] = { +static const Pool P_SCD[8] = { { instruction , 0 , 0 , 32, - 0xfc007f07, 0xa4007900, &NMD::SCD , 0, + 0xfc007f07, 0xa4007900, &SCD , 0, MIPS64_ }, /* SCD */ { instruction , 0 , 0 , 32, - 0xfc007f07, 0xa4007901, &NMD::SCDP , 0, + 0xfc007f07, 0xa4007901, &SCDP , 0, MIPS64_ }, /* SCDP */ { reserved_block , 0 , 0 , 32, 0xfc007f07, 0xa4007902, 0 , 0, @@ -21532,7 +20990,7 @@ NMD::Pool NMD::P_SCD[8] = { }; -NMD::Pool NMD::P_LS_S1[16] = { +static const Pool P_LS_S1[16] = { { reserved_block , 0 , 0 , 32, 0xfc007f00, 0xa4000100, 0 , 0, 0x0 }, /* P.LS.S1~*(0) */ @@ -21546,22 +21004,22 @@ NMD::Pool NMD::P_LS_S1[16] = { 0xfc007f00, 0xa4001900, 0 , 0, 0x0 }, /* P.LS.S1~*(3) */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4002100, &NMD::UALH , 0, + 0xfc007f00, 0xa4002100, &UALH , 0, XMMS_ }, /* UALH */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4002900, &NMD::UASH , 0, + 0xfc007f00, 0xa4002900, &UASH , 0, XMMS_ }, /* UASH */ { reserved_block , 0 , 0 , 32, 0xfc007f00, 0xa4003100, 0 , 0, 0x0 }, /* P.LS.S1~*(6) */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4003900, &NMD::CACHE , 0, + 0xfc007f00, 0xa4003900, &CACHE , 0, CP0_ }, /* CACHE */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4004100, &NMD::LWC2 , 0, + 0xfc007f00, 0xa4004100, &LWC2 , 0, CP2_ }, /* LWC2 */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4004900, &NMD::SWC2 , 0, + 0xfc007f00, 0xa4004900, &SWC2 , 0, CP2_ }, /* SWC2 */ { pool , P_LL , 4 , 32, 0xfc007f00, 0xa4005100, 0 , 0, @@ -21570,10 +21028,10 @@ NMD::Pool NMD::P_LS_S1[16] = { 0xfc007f00, 0xa4005900, 0 , 0, 0x0 }, /* P.SC */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4006100, &NMD::LDC2 , 0, + 0xfc007f00, 0xa4006100, &LDC2 , 0, CP2_ }, /* LDC2 */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4006900, &NMD::SDC2 , 0, + 0xfc007f00, 0xa4006900, &SDC2 , 0, CP2_ }, /* SDC2 */ { pool , P_LLD , 8 , 32, 0xfc007f00, 0xa4007100, 0 , 0, @@ -21584,22 +21042,22 @@ NMD::Pool NMD::P_LS_S1[16] = { }; -NMD::Pool NMD::P_PREFE[2] = { +static const Pool P_PREFE[2] = { { instruction , 0 , 0 , 32, - 0xffe07f00, 0xa7e01a00, &NMD::SYNCIE , 0, + 0xffe07f00, 0xa7e01a00, &SYNCIE , 0, CP0_ | EVA_ }, /* SYNCIE */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4001a00, &NMD::PREFE , &NMD::PREFE_cond , + 0xfc007f00, 0xa4001a00, &PREFE , &PREFE_cond , CP0_ | EVA_ }, /* PREFE */ }; -NMD::Pool NMD::P_LLE[4] = { +static const Pool P_LLE[4] = { { instruction , 0 , 0 , 32, - 0xfc007f03, 0xa4005200, &NMD::LLE , 0, + 0xfc007f03, 0xa4005200, &LLE , 0, CP0_ | EVA_ }, /* LLE */ { instruction , 0 , 0 , 32, - 0xfc007f03, 0xa4005201, &NMD::LLWPE , 0, + 0xfc007f03, 0xa4005201, &LLWPE , 0, CP0_ | EVA_ }, /* LLWPE */ { reserved_block , 0 , 0 , 32, 0xfc007f03, 0xa4005202, 0 , 0, @@ -21610,12 +21068,12 @@ NMD::Pool NMD::P_LLE[4] = { }; -NMD::Pool NMD::P_SCE[4] = { +static const Pool P_SCE[4] = { { instruction , 0 , 0 , 32, - 0xfc007f03, 0xa4005a00, &NMD::SCE , 0, + 0xfc007f03, 0xa4005a00, &SCE , 0, CP0_ | EVA_ }, /* SCE */ { instruction , 0 , 0 , 32, - 0xfc007f03, 0xa4005a01, &NMD::SCWPE , 0, + 0xfc007f03, 0xa4005a01, &SCWPE , 0, CP0_ | EVA_ }, /* SCWPE */ { reserved_block , 0 , 0 , 32, 0xfc007f03, 0xa4005a02, 0 , 0, @@ -21626,36 +21084,36 @@ NMD::Pool NMD::P_SCE[4] = { }; -NMD::Pool NMD::P_LS_E0[16] = { +static const Pool P_LS_E0[16] = { { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4000200, &NMD::LBE , 0, + 0xfc007f00, 0xa4000200, &LBE , 0, CP0_ | EVA_ }, /* LBE */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4000a00, &NMD::SBE , 0, + 0xfc007f00, 0xa4000a00, &SBE , 0, CP0_ | EVA_ }, /* SBE */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4001200, &NMD::LBUE , 0, + 0xfc007f00, 0xa4001200, &LBUE , 0, CP0_ | EVA_ }, /* LBUE */ { pool , P_PREFE , 2 , 32, 0xfc007f00, 0xa4001a00, 0 , 0, 0x0 }, /* P.PREFE */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4002200, &NMD::LHE , 0, + 0xfc007f00, 0xa4002200, &LHE , 0, CP0_ | EVA_ }, /* LHE */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4002a00, &NMD::SHE , 0, + 0xfc007f00, 0xa4002a00, &SHE , 0, CP0_ | EVA_ }, /* SHE */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4003200, &NMD::LHUE , 0, + 0xfc007f00, 0xa4003200, &LHUE , 0, CP0_ | EVA_ }, /* LHUE */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4003a00, &NMD::CACHEE , 0, + 0xfc007f00, 0xa4003a00, &CACHEE , 0, CP0_ | EVA_ }, /* CACHEE */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4004200, &NMD::LWE , 0, + 0xfc007f00, 0xa4004200, &LWE , 0, CP0_ | EVA_ }, /* LWE */ { instruction , 0 , 0 , 32, - 0xfc007f00, 0xa4004a00, &NMD::SWE , 0, + 0xfc007f00, 0xa4004a00, &SWE , 0, CP0_ | EVA_ }, /* SWE */ { pool , P_LLE , 4 , 32, 0xfc007f00, 0xa4005200, 0 , 0, @@ -21678,47 +21136,47 @@ NMD::Pool NMD::P_LS_E0[16] = { }; -NMD::Pool NMD::P_LS_WM[2] = { +static const Pool P_LS_WM[2] = { { instruction , 0 , 0 , 32, - 0xfc000f00, 0xa4000400, &NMD::LWM , 0, + 0xfc000f00, 0xa4000400, &LWM , 0, XMMS_ }, /* LWM */ { instruction , 0 , 0 , 32, - 0xfc000f00, 0xa4000c00, &NMD::SWM , 0, + 0xfc000f00, 0xa4000c00, &SWM , 0, XMMS_ }, /* SWM */ }; -NMD::Pool NMD::P_LS_UAWM[2] = { +static const Pool P_LS_UAWM[2] = { { instruction , 0 , 0 , 32, - 0xfc000f00, 0xa4000500, &NMD::UALWM , 0, + 0xfc000f00, 0xa4000500, &UALWM , 0, XMMS_ }, /* UALWM */ { instruction , 0 , 0 , 32, - 0xfc000f00, 0xa4000d00, &NMD::UASWM , 0, + 0xfc000f00, 0xa4000d00, &UASWM , 0, XMMS_ }, /* UASWM */ }; -NMD::Pool NMD::P_LS_DM[2] = { +static const Pool P_LS_DM[2] = { { instruction , 0 , 0 , 32, - 0xfc000f00, 0xa4000600, &NMD::LDM , 0, + 0xfc000f00, 0xa4000600, &LDM , 0, MIPS64_ }, /* LDM */ { instruction , 0 , 0 , 32, - 0xfc000f00, 0xa4000e00, &NMD::SDM , 0, + 0xfc000f00, 0xa4000e00, &SDM , 0, MIPS64_ }, /* SDM */ }; -NMD::Pool NMD::P_LS_UADM[2] = { +static const Pool P_LS_UADM[2] = { { instruction , 0 , 0 , 32, - 0xfc000f00, 0xa4000700, &NMD::UALDM , 0, + 0xfc000f00, 0xa4000700, &UALDM , 0, MIPS64_ }, /* UALDM */ { instruction , 0 , 0 , 32, - 0xfc000f00, 0xa4000f00, &NMD::UASDM , 0, + 0xfc000f00, 0xa4000f00, &UASDM , 0, MIPS64_ }, /* UASDM */ }; -NMD::Pool NMD::P_LS_S9[8] = { +static const Pool P_LS_S9[8] = { { pool , P_LS_S0 , 16 , 32, 0xfc000700, 0xa4000000, 0 , 0, 0x0 }, /* P.LS.S0 */ @@ -21746,32 +21204,32 @@ NMD::Pool NMD::P_LS_S9[8] = { }; -NMD::Pool NMD::P_BAL[2] = { +static const Pool P_BAL[2] = { { branch_instruction , 0 , 0 , 32, - 0xfe000000, 0x28000000, &NMD::BC_32_ , 0, + 0xfe000000, 0x28000000, &BC_32_ , 0, 0x0 }, /* BC[32] */ { call_instruction , 0 , 0 , 32, - 0xfe000000, 0x2a000000, &NMD::BALC_32_ , 0, + 0xfe000000, 0x2a000000, &BALC_32_ , 0, 0x0 }, /* BALC[32] */ }; -NMD::Pool NMD::P_BALRSC[2] = { +static const Pool P_BALRSC[2] = { { branch_instruction , 0 , 0 , 32, - 0xffe0f000, 0x48008000, &NMD::BRSC , 0, + 0xffe0f000, 0x48008000, &BRSC , 0, 0x0 }, /* BRSC */ { call_instruction , 0 , 0 , 32, - 0xfc00f000, 0x48008000, &NMD::BALRSC , &NMD::BALRSC_cond , + 0xfc00f000, 0x48008000, &BALRSC , &BALRSC_cond , 0x0 }, /* BALRSC */ }; -NMD::Pool NMD::P_J[16] = { +static const Pool P_J[16] = { { call_instruction , 0 , 0 , 32, - 0xfc00f000, 0x48000000, &NMD::JALRC_32_ , 0, + 0xfc00f000, 0x48000000, &JALRC_32_ , 0, 0x0 }, /* JALRC[32] */ { call_instruction , 0 , 0 , 32, - 0xfc00f000, 0x48001000, &NMD::JALRC_HB , 0, + 0xfc00f000, 0x48001000, &JALRC_HB , 0, 0x0 }, /* JALRC.HB */ { reserved_block , 0 , 0 , 32, 0xfc00f000, 0x48002000, 0 , 0, @@ -21818,21 +21276,21 @@ NMD::Pool NMD::P_J[16] = { }; -NMD::Pool NMD::P_BR3A[32] = { +static const Pool P_BR3A[32] = { { branch_instruction , 0 , 0 , 32, - 0xfc1fc000, 0x88004000, &NMD::BC1EQZC , 0, + 0xfc1fc000, 0x88004000, &BC1EQZC , 0, CP1_ }, /* BC1EQZC */ { branch_instruction , 0 , 0 , 32, - 0xfc1fc000, 0x88014000, &NMD::BC1NEZC , 0, + 0xfc1fc000, 0x88014000, &BC1NEZC , 0, CP1_ }, /* BC1NEZC */ { branch_instruction , 0 , 0 , 32, - 0xfc1fc000, 0x88024000, &NMD::BC2EQZC , 0, + 0xfc1fc000, 0x88024000, &BC2EQZC , 0, CP2_ }, /* BC2EQZC */ { branch_instruction , 0 , 0 , 32, - 0xfc1fc000, 0x88034000, &NMD::BC2NEZC , 0, + 0xfc1fc000, 0x88034000, &BC2NEZC , 0, CP2_ }, /* BC2NEZC */ { branch_instruction , 0 , 0 , 32, - 0xfc1fc000, 0x88044000, &NMD::BPOSGE32C , 0, + 0xfc1fc000, 0x88044000, &BPOSGE32C , 0, DSP_ }, /* BPOSGE32C */ { reserved_block , 0 , 0 , 32, 0xfc1fc000, 0x88054000, 0 , 0, @@ -21918,67 +21376,67 @@ NMD::Pool NMD::P_BR3A[32] = { }; -NMD::Pool NMD::P_BR1[4] = { +static const Pool P_BR1[4] = { { branch_instruction , 0 , 0 , 32, - 0xfc00c000, 0x88000000, &NMD::BEQC_32_ , 0, + 0xfc00c000, 0x88000000, &BEQC_32_ , 0, 0x0 }, /* BEQC[32] */ { pool , P_BR3A , 32 , 32, 0xfc00c000, 0x88004000, 0 , 0, 0x0 }, /* P.BR3A */ { branch_instruction , 0 , 0 , 32, - 0xfc00c000, 0x88008000, &NMD::BGEC , 0, + 0xfc00c000, 0x88008000, &BGEC , 0, 0x0 }, /* BGEC */ { branch_instruction , 0 , 0 , 32, - 0xfc00c000, 0x8800c000, &NMD::BGEUC , 0, + 0xfc00c000, 0x8800c000, &BGEUC , 0, 0x0 }, /* BGEUC */ }; -NMD::Pool NMD::P_BR2[4] = { +static const Pool P_BR2[4] = { { branch_instruction , 0 , 0 , 32, - 0xfc00c000, 0xa8000000, &NMD::BNEC_32_ , 0, + 0xfc00c000, 0xa8000000, &BNEC_32_ , 0, 0x0 }, /* BNEC[32] */ { reserved_block , 0 , 0 , 32, 0xfc00c000, 0xa8004000, 0 , 0, 0x0 }, /* P.BR2~*(1) */ { branch_instruction , 0 , 0 , 32, - 0xfc00c000, 0xa8008000, &NMD::BLTC , 0, + 0xfc00c000, 0xa8008000, &BLTC , 0, 0x0 }, /* BLTC */ { branch_instruction , 0 , 0 , 32, - 0xfc00c000, 0xa800c000, &NMD::BLTUC , 0, + 0xfc00c000, 0xa800c000, &BLTUC , 0, 0x0 }, /* BLTUC */ }; -NMD::Pool NMD::P_BRI[8] = { +static const Pool P_BRI[8] = { { branch_instruction , 0 , 0 , 32, - 0xfc1c0000, 0xc8000000, &NMD::BEQIC , 0, + 0xfc1c0000, 0xc8000000, &BEQIC , 0, 0x0 }, /* BEQIC */ { branch_instruction , 0 , 0 , 32, - 0xfc1c0000, 0xc8040000, &NMD::BBEQZC , 0, + 0xfc1c0000, 0xc8040000, &BBEQZC , 0, XMMS_ }, /* BBEQZC */ { branch_instruction , 0 , 0 , 32, - 0xfc1c0000, 0xc8080000, &NMD::BGEIC , 0, + 0xfc1c0000, 0xc8080000, &BGEIC , 0, 0x0 }, /* BGEIC */ { branch_instruction , 0 , 0 , 32, - 0xfc1c0000, 0xc80c0000, &NMD::BGEIUC , 0, + 0xfc1c0000, 0xc80c0000, &BGEIUC , 0, 0x0 }, /* BGEIUC */ { branch_instruction , 0 , 0 , 32, - 0xfc1c0000, 0xc8100000, &NMD::BNEIC , 0, + 0xfc1c0000, 0xc8100000, &BNEIC , 0, 0x0 }, /* BNEIC */ { branch_instruction , 0 , 0 , 32, - 0xfc1c0000, 0xc8140000, &NMD::BBNEZC , 0, + 0xfc1c0000, 0xc8140000, &BBNEZC , 0, XMMS_ }, /* BBNEZC */ { branch_instruction , 0 , 0 , 32, - 0xfc1c0000, 0xc8180000, &NMD::BLTIC , 0, + 0xfc1c0000, 0xc8180000, &BLTIC , 0, 0x0 }, /* BLTIC */ { branch_instruction , 0 , 0 , 32, - 0xfc1c0000, 0xc81c0000, &NMD::BLTIUC , 0, + 0xfc1c0000, 0xc81c0000, &BLTIUC , 0, 0x0 }, /* BLTIUC */ }; -NMD::Pool NMD::P32[32] = { +static const Pool P32[32] = { { pool , P_ADDIU , 2 , 32, 0xfc000000, 0x00000000, 0 , 0, 0x0 }, /* P.ADDIU */ @@ -22004,7 +21462,7 @@ NMD::Pool NMD::P32[32] = { 0xfc000000, 0xe0000000, 0 , 0, 0x0 }, /* P.LUI */ { instruction , 0 , 0 , 32, - 0xfc000000, 0x04000000, &NMD::ADDIUPC_32_ , 0, + 0xfc000000, 0x04000000, &ADDIUPC_32_ , 0, 0x0 }, /* ADDIUPC[32] */ { reserved_block , 0 , 0 , 32, 0xfc000000, 0x24000000, 0 , 0, @@ -22028,7 +21486,7 @@ NMD::Pool NMD::P32[32] = { 0xfc000000, 0xe4000000, 0 , 0, 0x0 }, /* P32~*(29) */ { call_instruction , 0 , 0 , 32, - 0xfc000000, 0x08000000, &NMD::MOVE_BALC , 0, + 0xfc000000, 0x08000000, &MOVE_BALC , 0, XMMS_ }, /* MOVE.BALC */ { pool , P_BAL , 2 , 32, 0xfc000000, 0x28000000, 0 , 0, @@ -22078,17 +21536,17 @@ NMD::Pool NMD::P32[32] = { }; -NMD::Pool NMD::P16_SYSCALL[2] = { +static const Pool P16_SYSCALL[2] = { { instruction , 0 , 0 , 16, - 0xfffc , 0x1008 , &NMD::SYSCALL_16_ , 0, + 0xfffc , 0x1008 , &SYSCALL_16_ , 0, 0x0 }, /* SYSCALL[16] */ { instruction , 0 , 0 , 16, - 0xfffc , 0x100c , &NMD::HYPCALL_16_ , 0, + 0xfffc , 0x100c , &HYPCALL_16_ , 0, CP0_ | VZ_ }, /* HYPCALL[16] */ }; -NMD::Pool NMD::P16_RI[4] = { +static const Pool P16_RI[4] = { { reserved_block , 0 , 0 , 16, 0xfff8 , 0x1000 , 0 , 0, 0x0 }, /* P16.RI~*(0) */ @@ -22096,51 +21554,51 @@ NMD::Pool NMD::P16_RI[4] = { 0xfff8 , 0x1008 , 0 , 0, 0x0 }, /* P16.SYSCALL */ { instruction , 0 , 0 , 16, - 0xfff8 , 0x1010 , &NMD::BREAK_16_ , 0, + 0xfff8 , 0x1010 , &BREAK_16_ , 0, 0x0 }, /* BREAK[16] */ { instruction , 0 , 0 , 16, - 0xfff8 , 0x1018 , &NMD::SDBBP_16_ , 0, + 0xfff8 , 0x1018 , &SDBBP_16_ , 0, EJTAG_ }, /* SDBBP[16] */ }; -NMD::Pool NMD::P16_MV[2] = { +static const Pool P16_MV[2] = { { pool , P16_RI , 4 , 16, 0xffe0 , 0x1000 , 0 , 0, 0x0 }, /* P16.RI */ { instruction , 0 , 0 , 16, - 0xfc00 , 0x1000 , &NMD::MOVE , &NMD::MOVE_cond , + 0xfc00 , 0x1000 , &MOVE , &MOVE_cond , 0x0 }, /* MOVE */ }; -NMD::Pool NMD::P16_SHIFT[2] = { +static const Pool P16_SHIFT[2] = { { instruction , 0 , 0 , 16, - 0xfc08 , 0x3000 , &NMD::SLL_16_ , 0, + 0xfc08 , 0x3000 , &SLL_16_ , 0, 0x0 }, /* SLL[16] */ { instruction , 0 , 0 , 16, - 0xfc08 , 0x3008 , &NMD::SRL_16_ , 0, + 0xfc08 , 0x3008 , &SRL_16_ , 0, 0x0 }, /* SRL[16] */ }; -NMD::Pool NMD::POOL16C_00[4] = { +static const Pool POOL16C_00[4] = { { instruction , 0 , 0 , 16, - 0xfc0f , 0x5000 , &NMD::NOT_16_ , 0, + 0xfc0f , 0x5000 , &NOT_16_ , 0, 0x0 }, /* NOT[16] */ { instruction , 0 , 0 , 16, - 0xfc0f , 0x5004 , &NMD::XOR_16_ , 0, + 0xfc0f , 0x5004 , &XOR_16_ , 0, 0x0 }, /* XOR[16] */ { instruction , 0 , 0 , 16, - 0xfc0f , 0x5008 , &NMD::AND_16_ , 0, + 0xfc0f , 0x5008 , &AND_16_ , 0, 0x0 }, /* AND[16] */ { instruction , 0 , 0 , 16, - 0xfc0f , 0x500c , &NMD::OR_16_ , 0, + 0xfc0f , 0x500c , &OR_16_ , 0, 0x0 }, /* OR[16] */ }; -NMD::Pool NMD::POOL16C_0[2] = { +static const Pool POOL16C_0[2] = { { pool , POOL16C_00 , 4 , 16, 0xfc03 , 0x5000 , 0 , 0, 0x0 }, /* POOL16C_00 */ @@ -22150,39 +21608,39 @@ NMD::Pool NMD::POOL16C_0[2] = { }; -NMD::Pool NMD::P16C[2] = { +static const Pool P16C[2] = { { pool , POOL16C_0 , 2 , 16, 0xfc01 , 0x5000 , 0 , 0, 0x0 }, /* POOL16C_0 */ { instruction , 0 , 0 , 16, - 0xfc01 , 0x5001 , &NMD::LWXS_16_ , 0, + 0xfc01 , 0x5001 , &LWXS_16_ , 0, 0x0 }, /* LWXS[16] */ }; -NMD::Pool NMD::P16_A1[2] = { +static const Pool P16_A1[2] = { { reserved_block , 0 , 0 , 16, 0xfc40 , 0x7000 , 0 , 0, 0x0 }, /* P16.A1~*(0) */ { instruction , 0 , 0 , 16, - 0xfc40 , 0x7040 , &NMD::ADDIU_R1_SP_ , 0, + 0xfc40 , 0x7040 , &ADDIU_R1_SP_ , 0, 0x0 }, /* ADDIU[R1.SP] */ }; -NMD::Pool NMD::P_ADDIU_RS5_[2] = { +static const Pool P_ADDIU_RS5_[2] = { { instruction , 0 , 0 , 16, - 0xffe8 , 0x9008 , &NMD::NOP_16_ , 0, + 0xffe8 , 0x9008 , &NOP_16_ , 0, 0x0 }, /* NOP[16] */ { instruction , 0 , 0 , 16, - 0xfc08 , 0x9008 , &NMD::ADDIU_RS5_ , &NMD::ADDIU_RS5__cond , + 0xfc08 , 0x9008 , &ADDIU_RS5_ , &ADDIU_RS5__cond , 0x0 }, /* ADDIU[RS5] */ }; -NMD::Pool NMD::P16_A2[2] = { +static const Pool P16_A2[2] = { { instruction , 0 , 0 , 16, - 0xfc08 , 0x9000 , &NMD::ADDIU_R2_ , 0, + 0xfc08 , 0x9000 , &ADDIU_R2_ , 0, 0x0 }, /* ADDIU[R2] */ { pool , P_ADDIU_RS5_ , 2 , 16, 0xfc08 , 0x9008 , 0 , 0, @@ -22190,62 +21648,62 @@ NMD::Pool NMD::P16_A2[2] = { }; -NMD::Pool NMD::P16_ADDU[2] = { +static const Pool P16_ADDU[2] = { { instruction , 0 , 0 , 16, - 0xfc01 , 0xb000 , &NMD::ADDU_16_ , 0, + 0xfc01 , 0xb000 , &ADDU_16_ , 0, 0x0 }, /* ADDU[16] */ { instruction , 0 , 0 , 16, - 0xfc01 , 0xb001 , &NMD::SUBU_16_ , 0, + 0xfc01 , 0xb001 , &SUBU_16_ , 0, 0x0 }, /* SUBU[16] */ }; -NMD::Pool NMD::P16_JRC[2] = { +static const Pool P16_JRC[2] = { { branch_instruction , 0 , 0 , 16, - 0xfc1f , 0xd800 , &NMD::JRC , 0, + 0xfc1f , 0xd800 , &JRC , 0, 0x0 }, /* JRC */ { call_instruction , 0 , 0 , 16, - 0xfc1f , 0xd810 , &NMD::JALRC_16_ , 0, + 0xfc1f , 0xd810 , &JALRC_16_ , 0, 0x0 }, /* JALRC[16] */ }; -NMD::Pool NMD::P16_BR1[2] = { +static const Pool P16_BR1[2] = { { branch_instruction , 0 , 0 , 16, - 0xfc00 , 0xd800 , &NMD::BEQC_16_ , &NMD::BEQC_16__cond , + 0xfc00 , 0xd800 , &BEQC_16_ , &BEQC_16__cond , XMMS_ }, /* BEQC[16] */ { branch_instruction , 0 , 0 , 16, - 0xfc00 , 0xd800 , &NMD::BNEC_16_ , &NMD::BNEC_16__cond , + 0xfc00 , 0xd800 , &BNEC_16_ , &BNEC_16__cond , XMMS_ }, /* BNEC[16] */ }; -NMD::Pool NMD::P16_BR[2] = { +static const Pool P16_BR[2] = { { pool , P16_JRC , 2 , 16, 0xfc0f , 0xd800 , 0 , 0, 0x0 }, /* P16.JRC */ { pool , P16_BR1 , 2 , 16, - 0xfc00 , 0xd800 , 0 , &NMD::P16_BR1_cond , + 0xfc00 , 0xd800 , 0 , &P16_BR1_cond , 0x0 }, /* P16.BR1 */ }; -NMD::Pool NMD::P16_SR[2] = { +static const Pool P16_SR[2] = { { instruction , 0 , 0 , 16, - 0xfd00 , 0x1c00 , &NMD::SAVE_16_ , 0, + 0xfd00 , 0x1c00 , &SAVE_16_ , 0, 0x0 }, /* SAVE[16] */ { return_instruction , 0 , 0 , 16, - 0xfd00 , 0x1d00 , &NMD::RESTORE_JRC_16_ , 0, + 0xfd00 , 0x1d00 , &RESTORE_JRC_16_ , 0, 0x0 }, /* RESTORE.JRC[16] */ }; -NMD::Pool NMD::P16_4X4[4] = { +static const Pool P16_4X4[4] = { { instruction , 0 , 0 , 16, - 0xfd08 , 0x3c00 , &NMD::ADDU_4X4_ , 0, + 0xfd08 , 0x3c00 , &ADDU_4X4_ , 0, XMMS_ }, /* ADDU[4X4] */ { instruction , 0 , 0 , 16, - 0xfd08 , 0x3c08 , &NMD::MUL_4X4_ , 0, + 0xfd08 , 0x3c08 , &MUL_4X4_ , 0, XMMS_ }, /* MUL[4X4] */ { reserved_block , 0 , 0 , 16, 0xfd08 , 0x3d00 , 0 , 0, @@ -22256,15 +21714,15 @@ NMD::Pool NMD::P16_4X4[4] = { }; -NMD::Pool NMD::P16_LB[4] = { +static const Pool P16_LB[4] = { { instruction , 0 , 0 , 16, - 0xfc0c , 0x5c00 , &NMD::LB_16_ , 0, + 0xfc0c , 0x5c00 , &LB_16_ , 0, 0x0 }, /* LB[16] */ { instruction , 0 , 0 , 16, - 0xfc0c , 0x5c04 , &NMD::SB_16_ , 0, + 0xfc0c , 0x5c04 , &SB_16_ , 0, 0x0 }, /* SB[16] */ { instruction , 0 , 0 , 16, - 0xfc0c , 0x5c08 , &NMD::LBU_16_ , 0, + 0xfc0c , 0x5c08 , &LBU_16_ , 0, 0x0 }, /* LBU[16] */ { reserved_block , 0 , 0 , 16, 0xfc0c , 0x5c0c , 0 , 0, @@ -22272,15 +21730,15 @@ NMD::Pool NMD::P16_LB[4] = { }; -NMD::Pool NMD::P16_LH[4] = { +static const Pool P16_LH[4] = { { instruction , 0 , 0 , 16, - 0xfc09 , 0x7c00 , &NMD::LH_16_ , 0, + 0xfc09 , 0x7c00 , &LH_16_ , 0, 0x0 }, /* LH[16] */ { instruction , 0 , 0 , 16, - 0xfc09 , 0x7c01 , &NMD::SH_16_ , 0, + 0xfc09 , 0x7c01 , &SH_16_ , 0, 0x0 }, /* SH[16] */ { instruction , 0 , 0 , 16, - 0xfc09 , 0x7c08 , &NMD::LHU_16_ , 0, + 0xfc09 , 0x7c08 , &LHU_16_ , 0, 0x0 }, /* LHU[16] */ { reserved_block , 0 , 0 , 16, 0xfc09 , 0x7c09 , 0 , 0, @@ -22288,7 +21746,7 @@ NMD::Pool NMD::P16_LH[4] = { }; -NMD::Pool NMD::P16[32] = { +static const Pool P16[32] = { { pool , P16_MV , 2 , 16, 0xfc00 , 0x1000 , 0 , 0, 0x0 }, /* P16.MV */ @@ -22308,40 +21766,40 @@ NMD::Pool NMD::P16[32] = { 0xfc00 , 0xb000 , 0 , 0, 0x0 }, /* P16.ADDU */ { instruction , 0 , 0 , 16, - 0xfc00 , 0xd000 , &NMD::LI_16_ , 0, + 0xfc00 , 0xd000 , &LI_16_ , 0, 0x0 }, /* LI[16] */ { instruction , 0 , 0 , 16, - 0xfc00 , 0xf000 , &NMD::ANDI_16_ , 0, + 0xfc00 , 0xf000 , &ANDI_16_ , 0, 0x0 }, /* ANDI[16] */ { instruction , 0 , 0 , 16, - 0xfc00 , 0x1400 , &NMD::LW_16_ , 0, + 0xfc00 , 0x1400 , &LW_16_ , 0, 0x0 }, /* LW[16] */ { instruction , 0 , 0 , 16, - 0xfc00 , 0x3400 , &NMD::LW_SP_ , 0, + 0xfc00 , 0x3400 , &LW_SP_ , 0, 0x0 }, /* LW[SP] */ { instruction , 0 , 0 , 16, - 0xfc00 , 0x5400 , &NMD::LW_GP16_ , 0, + 0xfc00 , 0x5400 , &LW_GP16_ , 0, 0x0 }, /* LW[GP16] */ { instruction , 0 , 0 , 16, - 0xfc00 , 0x7400 , &NMD::LW_4X4_ , 0, + 0xfc00 , 0x7400 , &LW_4X4_ , 0, XMMS_ }, /* LW[4X4] */ { instruction , 0 , 0 , 16, - 0xfc00 , 0x9400 , &NMD::SW_16_ , 0, + 0xfc00 , 0x9400 , &SW_16_ , 0, 0x0 }, /* SW[16] */ { instruction , 0 , 0 , 16, - 0xfc00 , 0xb400 , &NMD::SW_SP_ , 0, + 0xfc00 , 0xb400 , &SW_SP_ , 0, 0x0 }, /* SW[SP] */ { instruction , 0 , 0 , 16, - 0xfc00 , 0xd400 , &NMD::SW_GP16_ , 0, + 0xfc00 , 0xd400 , &SW_GP16_ , 0, 0x0 }, /* SW[GP16] */ { instruction , 0 , 0 , 16, - 0xfc00 , 0xf400 , &NMD::SW_4X4_ , 0, + 0xfc00 , 0xf400 , &SW_4X4_ , 0, XMMS_ }, /* SW[4X4] */ { branch_instruction , 0 , 0 , 16, - 0xfc00 , 0x1800 , &NMD::BC_16_ , 0, + 0xfc00 , 0x1800 , &BC_16_ , 0, 0x0 }, /* BC[16] */ { call_instruction , 0 , 0 , 16, - 0xfc00 , 0x3800 , &NMD::BALC_16_ , 0, + 0xfc00 , 0x3800 , &BALC_16_ , 0, 0x0 }, /* BALC[16] */ { reserved_block , 0 , 0 , 16, 0xfc00 , 0x5800 , 0 , 0, @@ -22350,10 +21808,10 @@ NMD::Pool NMD::P16[32] = { 0xfc00 , 0x7800 , 0 , 0, 0x0 }, /* P16~*(14) */ { branch_instruction , 0 , 0 , 16, - 0xfc00 , 0x9800 , &NMD::BEQZC_16_ , 0, + 0xfc00 , 0x9800 , &BEQZC_16_ , 0, 0x0 }, /* BEQZC[16] */ { branch_instruction , 0 , 0 , 16, - 0xfc00 , 0xb800 , &NMD::BNEZC_16_ , 0, + 0xfc00 , 0xb800 , &BNEZC_16_ , 0, 0x0 }, /* BNEZC[16] */ { pool , P16_BR , 2 , 16, 0xfc00 , 0xd800 , 0 , 0, @@ -22377,18 +21835,18 @@ NMD::Pool NMD::P16[32] = { 0xfc00 , 0x9c00 , 0 , 0, 0x0 }, /* P16~*(19) */ { instruction , 0 , 0 , 16, - 0xfc00 , 0xbc00 , &NMD::MOVEP , 0, + 0xfc00 , 0xbc00 , &MOVEP , 0, XMMS_ }, /* MOVEP */ { reserved_block , 0 , 0 , 16, 0xfc00 , 0xdc00 , 0 , 0, 0x0 }, /* P16~*(27) */ { instruction , 0 , 0 , 16, - 0xfc00 , 0xfc00 , &NMD::MOVEP_REV_ , 0, + 0xfc00 , 0xfc00 , &MOVEP_REV_ , 0, XMMS_ }, /* MOVEP[REV] */ }; -NMD::Pool NMD::MAJOR[2] = { +static const Pool MAJOR[2] = { { pool , P32 , 32 , 32, 0x10000000, 0x00000000, 0 , 0, 0x0 }, /* P32 */ @@ -22396,3 +21854,137 @@ NMD::Pool NMD::MAJOR[2] = { 0x1000 , 0x1000 , 0 , 0, 0x0 }, /* P16 */ }; + +/* + * Recurse through tables until the instruction is found then return + * the string and size + * + * inputs: + * pointer to a word stream, + * disassember table and size + * returns: + * instruction size - negative is error + * disassembly string - on error will constain error string + */ +static int Disassemble(const uint16 *data, char **dis, + TABLE_ENTRY_TYPE *type, const Pool *table, + int table_size, Dis_info *info) +{ + for (int i = 0; i < table_size; i++) { + uint64 op_code = extract_op_code_value(data, + table[i].instructions_size); + if ((op_code & table[i].mask) == table[i].value) { + /* possible match */ + conditional_function cond = table[i].condition; + if ((cond == NULL) || cond(op_code)) { + if (table[i].type == pool) { + return Disassemble(data, dis, type, + table[i].next_table, + table[i].next_table_size, + info); + } else if ((table[i].type == instruction) || + (table[i].type == call_instruction) || + (table[i].type == branch_instruction) || + (table[i].type == return_instruction)) { + disassembly_function dis_fn = table[i].disassembly; + if (dis_fn == 0) { + *dis = g_strdup( + "disassembler failure - bad table entry"); + return -6; + } + *type = table[i].type; + *dis = dis_fn(op_code, info); + return table[i].instructions_size; + } else { + *dis = g_strdup("reserved instruction"); + return -2; + } + } + } + } + *dis = g_strdup("failed to disassemble"); + return -1; /* failed to disassemble */ +} + + +static bool nanomips_dis(const uint16_t *data, char **buf, Dis_info *info) +{ + TABLE_ENTRY_TYPE type; + + /* Handle runtime errors. */ + if (unlikely(sigsetjmp(info->buf, 0) != 0)) { + return false; + } + return Disassemble(data, buf, &type, MAJOR, ARRAY_SIZE(MAJOR), info) >= 0; +} + +static bool read_u16(uint16_t *ret, bfd_vma memaddr, + struct disassemble_info *info) +{ + int status = (*info->read_memory_func)(memaddr, (bfd_byte *)ret, 2, info); + if (status != 0) { + (*info->memory_error_func)(status, memaddr, info); + return false; + } + + if ((info->endian == BFD_ENDIAN_BIG) != HOST_BIG_ENDIAN) { + bswap16s(ret); + } + return true; +} + +int print_insn_nanomips(bfd_vma memaddr, struct disassemble_info *info) +{ + int length; + uint16_t words[3] = { }; + g_autofree char *buf = NULL; + + info->bytes_per_chunk = 2; + info->display_endian = info->endian; + info->insn_info_valid = 1; + info->branch_delay_insns = 0; + info->data_size = 0; + info->insn_type = dis_nonbranch; + info->target = 0; + info->target2 = 0; + + Dis_info disassm_info; + disassm_info.m_pc = memaddr; + disassm_info.fprintf_func = info->fprintf_func; + disassm_info.stream = info->stream; + + if (!read_u16(&words[0], memaddr, info)) { + return -1; + } + length = 2; + + /* Handle 32-bit opcodes. */ + if ((words[0] & 0x1000) == 0) { + if (!read_u16(&words[1], memaddr + 2, info)) { + return -1; + } + length = 4; + + /* Handle 48-bit opcodes. */ + if ((words[0] >> 10) == 0x18) { + if (!read_u16(&words[1], memaddr + 4, info)) { + return -1; + } + length = 6; + } + } + + for (int i = 0; i < ARRAY_SIZE(words); i++) { + if (i * 2 < length) { + (*info->fprintf_func)(info->stream, "%04x ", words[i]); + } else { + (*info->fprintf_func)(info->stream, " "); + } + } + + if (nanomips_dis(words, &buf, &disassm_info)) { + (*info->fprintf_func) (info->stream, "%s", buf); + } + + return length; +} diff --git a/disas/nanomips.h b/disas/nanomips.h deleted file mode 100644 index a0a2225301..0000000000 --- a/disas/nanomips.h +++ /dev/null @@ -1,1076 +0,0 @@ -/* - * Header file for nanoMIPS disassembler component of QEMU - * - * Copyright (C) 2018 Wave Computing, Inc. - * Copyright (C) 2018 Matthew Fortune - * Copyright (C) 2018 Aleksandar Markovic - * - * 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 DISAS_NANOMIPS_H -#define DISAS_NANOMIPS_H - -#include - -typedef int64_t int64; -typedef uint64_t uint64; -typedef uint32_t uint32; -typedef uint16_t uint16; - -namespace img -{ - typedef uint64_t address; -} - - -class NMD -{ -public: - - enum TABLE_ENTRY_TYPE { - instruction, - call_instruction, - branch_instruction, - return_instruction, - reserved_block, - pool, - }; - - enum TABLE_ATTRIBUTE_TYPE { - MIPS64_ = 0x00000001, - XNP_ = 0x00000002, - XMMS_ = 0x00000004, - EVA_ = 0x00000008, - DSP_ = 0x00000010, - MT_ = 0x00000020, - EJTAG_ = 0x00000040, - TLBINV_ = 0x00000080, - CP0_ = 0x00000100, - CP1_ = 0x00000200, - CP2_ = 0x00000400, - UDI_ = 0x00000800, - MCU_ = 0x00001000, - VZ_ = 0x00002000, - TLB_ = 0x00004000, - MVH_ = 0x00008000, - ALL_ATTRIBUTES = 0xffffffffull, - }; - - - NMD(img::address pc, TABLE_ATTRIBUTE_TYPE requested_instruction_categories) - : m_pc(pc) - , m_requested_instruction_categories(requested_instruction_categories) - { - } - - int Disassemble(const uint16 *data, std::string & dis, - TABLE_ENTRY_TYPE & type); - -private: - - img::address m_pc; - TABLE_ATTRIBUTE_TYPE m_requested_instruction_categories; - - typedef std::string(NMD:: *disassembly_function)(uint64 instruction); - typedef bool(NMD:: *conditional_function)(uint64 instruction); - - struct Pool { - TABLE_ENTRY_TYPE type; - struct Pool *next_table; - int next_table_size; - int instructions_size; - uint64 mask; - uint64 value; - disassembly_function disassembly; - conditional_function condition; - uint64 attributes; - }; - - uint64 extract_op_code_value(const uint16 *data, int size); - int Disassemble(const uint16 *data, std::string & dis, - TABLE_ENTRY_TYPE & type, const Pool *table, int table_size); - - uint64 renumber_registers(uint64 index, uint64 *register_list, - size_t register_list_size); - - uint64 decode_gpr_gpr4(uint64 d); - uint64 decode_gpr_gpr4_zero(uint64 d); - uint64 decode_gpr_gpr3(uint64 d); - uint64 decode_gpr_gpr3_src_store(uint64 d); - uint64 decode_gpr_gpr2_reg1(uint64 d); - uint64 decode_gpr_gpr2_reg2(uint64 d); - uint64 decode_gpr_gpr1(uint64 d); - - uint64 copy(uint64 d); - int64 copy(int64 d); - int64 neg_copy(uint64 d); - int64 neg_copy(int64 d); - uint64 encode_rs3_and_check_rs3_ge_rt3(uint64 d); - uint64 encode_rs3_and_check_rs3_lt_rt3(uint64 d); - uint64 encode_s_from_address(uint64 d); - uint64 encode_u_from_address(uint64 d); - uint64 encode_s_from_s_hi(uint64 d); - uint64 encode_count3_from_count(uint64 d); - uint64 encode_shift3_from_shift(uint64 d); - int64 encode_eu_from_s_li16(uint64 d); - uint64 encode_msbd_from_size(uint64 d); - uint64 encode_eu_from_u_andi16(uint64 d); - - uint64 encode_msbd_from_pos_and_size(uint64 d); - - uint64 encode_rt1_from_rt(uint64 d); - uint64 encode_lsb_from_pos_and_size(uint64 d); - - std::string save_restore_list(uint64 rt, uint64 count, uint64 gp); - - std::string GPR(uint64 reg); - std::string FPR(uint64 reg); - std::string AC(uint64 reg); - std::string IMMEDIATE(uint64 value); - std::string IMMEDIATE(int64 value); - std::string CPR(uint64 reg); - std::string ADDRESS(uint64 value, int instruction_size); - - int64 extract_s__se3_4_2_1_0(uint64 instruction); - int64 extract_s__se7_0_6_5_4_3_2_1_s1(uint64 instruction); - int64 extract_s__se8_15_7_6_5_4_3_s3(uint64 instruction); - int64 extract_s__se8_15_7_6_5_4_3_2_s2(uint64 instruction); - int64 extract_s__se8_15_7_6_5_4_3_2_1_0(uint64 instruction); - int64 extract_s__se9_20_19_18_17_16_15_14_13_12_11(uint64 instruction); - int64 extract_s__se10_0_9_8_7_6_5_4_3_2_1_s1(uint64 instruction); - int64 extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(uint64 instruction); - int64 extract_s__se14_0_13_to_1_s1(uint64 instruction); - int64 extract_s__se21_0_20_to_1_s1(uint64 instruction); - int64 extract_s__se25_0_24_to_1_s1(uint64 instruction); - int64 extract_s__se31_15_to_0_31_to_16(uint64 instruction); - int64 extract_s__se31_0_11_to_2_20_to_12_s12(uint64 instruction); - int64 extract_shift__se5_21_20_19_18_17_16(uint64 instruction); - - uint64 extract_ac_15_14(uint64 instruction); - uint64 extract_bit_16_15_14_13_12_11(uint64 instruction); - uint64 extract_bit_23_22_21(uint64 instruction); - uint64 extract_c0s_20_19_18_17_16(uint64 instruction); - uint64 extract_code_17_to_0(uint64 instruction); - uint64 extract_code_18_to_0(uint64 instruction); - uint64 extract_code_1_0(uint64 instruction); - uint64 extract_code_2_1_0(uint64 instruction); - uint64 extract_code_25_24_23_22_21_20_19_18_17_16(uint64 instruction); - uint64 extract_cofun_25_24_23(uint64 instruction); - uint64 extract_count3_14_13_12(uint64 instruction); - uint64 extract_count_3_2_1_0(uint64 instruction); - uint64 extract_count_19_18_17_16(uint64 instruction); - uint64 extract_cs_20_19_18_17_16(uint64 instruction); - uint64 extract_cs_25_24_23_22_21(uint64 instruction); - uint64 extract_ct_25_24_23_22_21(uint64 instruction); - uint64 extract_eu_3_2_1_0(uint64 instruction); - uint64 extract_eu_6_5_4_3_2_1_0(uint64 instruction); - uint64 extract_fd_15_14_13_12_11(uint64 instruction); - uint64 extract_fs_20_19_18_17_16(uint64 instruction); - uint64 extract_ft_15_14_13_12_11(uint64 instruction); - uint64 extract_ft_25_24_23_22_21(uint64 instruction); - uint64 extract_gp_2(uint64 instruction); - uint64 extract_hint_25_24_23_22_21(uint64 instruction); - uint64 extract_hs_20_19_18_17_16(uint64 instruction); - uint64 extract_lsb_4_3_2_1_0(uint64 instruction); - uint64 extract_mask_20_19_18_17_16_15_14(uint64 instruction); - uint64 extract_msbt_10_9_8_7_6(uint64 instruction); - uint64 extract_op_25_24_23_22_21(uint64 instruction); - uint64 extract_op_25_to_3(uint64 instruction); - uint64 extract_rdl_25_24(uint64 instruction); - uint64 extract_rd2_3_8(uint64 instruction); - uint64 extract_rd3_3_2_1(uint64 instruction); - uint64 extract_rd_15_14_13_12_11(uint64 instruction); - uint64 extract_rs3_6_5_4(uint64 instruction); - uint64 extract_rs4_4_2_1_0(uint64 instruction); - uint64 extract_rs_4_3_2_1_0(uint64 instruction); - uint64 extract_rs_20_19_18_17_16(uint64 instruction); - uint64 extract_rsz4_4_2_1_0(uint64 instruction); - uint64 extract_rtl_11(uint64 instruction); - uint64 extract_rt3_9_8_7(uint64 instruction); - uint64 extract_rt4_9_7_6_5(uint64 instruction); - uint64 extract_rt_25_24_23_22_21(uint64 instruction); - uint64 extract_rt_41_40_39_38_37(uint64 instruction); - uint64 extract_rt_9_8_7_6_5(uint64 instruction); - uint64 extract_rtz3_9_8_7(uint64 instruction); - uint64 extract_rtz4_27_26_25_23_22_21(uint64 instruction); - uint64 extract_rtz4_9_7_6_5(uint64 instruction); - uint64 extract_ru_7_6_5_4_3(uint64 instruction); - uint64 extract_sa_15_14_13_12_11(uint64 instruction); - uint64 extract_sa_15_14_13_12(uint64 instruction); - uint64 extract_sa_15_14_13(uint64 instruction); - uint64 extract_sel_13_12_11(uint64 instruction); - uint64 extract_sel_15_14_13_12_11(uint64 instruction); - uint64 extract_shift3_2_1_0(uint64 instruction); - uint64 extract_shift_4_3_2_1_0(uint64 instruction); - uint64 extract_shift_5_4_3_2_1_0(uint64 instruction); - uint64 extract_shift_20_19_18_17_16(uint64 instruction); - uint64 extract_shift_10_9_8_7_6(uint64 instruction); - uint64 extract_shiftx_11_10_9_8_7_6(uint64 instruction); - uint64 extract_shiftx_10_9_8_7__s1(uint64 instruction); - uint64 extract_size_20_19_18_17_16(uint64 instruction); - uint64 extract_stripe_6(uint64 instruction); - uint64 extract_stype_20_19_18_17_16(uint64 instruction); - uint64 extract_u2_10_9(uint64 instruction); - uint64 extract_u_11_10_9_8_7_6_5_4_3_2_1_0(uint64 instruction); - uint64 extract_u_15_to_0(uint64 instruction); - uint64 extract_u_17_to_0(uint64 instruction); - uint64 extract_u_1_0(uint64 instruction); - uint64 extract_u_3_2_1_0__s1(uint64 instruction); - uint64 extract_u_2_1_0__s2(uint64 instruction); - uint64 extract_u_3_2_1_0__s2(uint64 instruction); - uint64 extract_u_4_3_2_1_0__s2(uint64 instruction); - uint64 extract_u_5_4_3_2_1_0__s2(uint64 instruction); - uint64 extract_u_6_5_4_3_2_1_0__s2(uint64 instruction); - uint64 extract_u_31_to_0__s32(uint64 instruction); - uint64 extract_u_10(uint64 instruction); - uint64 extract_u_17_16_15_14_13_12_11(uint64 instruction); - uint64 extract_u_20_19_18_17_16_15_14_13(uint64 instruction); - uint64 extract_u_17_to_1__s1(uint64 instruction); - uint64 extract_u_2_1__s1(uint64 instruction); - uint64 extract_u_17_to_2__s2(uint64 instruction); - uint64 extract_u_20_to_2__s2(uint64 instruction); - uint64 extract_u_20_to_3__s3(uint64 instruction); - uint64 extract_u_3_8__s2(uint64 instruction); - uint64 extract_u_11_10_9_8_7_6_5_4_3__s3(uint64 instruction); - uint64 extract_u_7_6_5_4__s4(uint64 instruction); - - bool ADDIU_32__cond(uint64 instruction); - bool ADDIU_RS5__cond(uint64 instruction); - bool BALRSC_cond(uint64 instruction); - bool BEQC_16__cond(uint64 instruction); - bool BNEC_16__cond(uint64 instruction); - bool MOVE_cond(uint64 instruction); - bool P16_BR1_cond(uint64 instruction); - bool PREF_S9__cond(uint64 instruction); - bool PREFE_cond(uint64 instruction); - bool SLTU_cond(uint64 instruction); - - std::string ABS_D(uint64 instruction); - std::string ABS_S(uint64 instruction); - std::string ABSQ_S_PH(uint64 instruction); - std::string ABSQ_S_QB(uint64 instruction); - std::string ABSQ_S_W(uint64 instruction); - std::string ACLR(uint64 instruction); - std::string ADD(uint64 instruction); - std::string ADD_D(uint64 instruction); - std::string ADD_S(uint64 instruction); - std::string ADDIU_32_(uint64 instruction); - std::string ADDIU_48_(uint64 instruction); - std::string ADDIU_GP48_(uint64 instruction); - std::string ADDIU_GP_B_(uint64 instruction); - std::string ADDIU_GP_W_(uint64 instruction); - std::string ADDIU_NEG_(uint64 instruction); - std::string ADDIU_R1_SP_(uint64 instruction); - std::string ADDIU_R2_(uint64 instruction); - std::string ADDIU_RS5_(uint64 instruction); - std::string ADDIUPC_32_(uint64 instruction); - std::string ADDIUPC_48_(uint64 instruction); - std::string ADDQ_PH(uint64 instruction); - std::string ADDQ_S_PH(uint64 instruction); - std::string ADDQ_S_W(uint64 instruction); - std::string ADDQH_PH(uint64 instruction); - std::string ADDQH_R_PH(uint64 instruction); - std::string ADDQH_R_W(uint64 instruction); - std::string ADDQH_W(uint64 instruction); - std::string ADDSC(uint64 instruction); - std::string ADDU_16_(uint64 instruction); - std::string ADDU_32_(uint64 instruction); - std::string ADDU_4X4_(uint64 instruction); - std::string ADDU_PH(uint64 instruction); - std::string ADDU_QB(uint64 instruction); - std::string ADDU_S_PH(uint64 instruction); - std::string ADDU_S_QB(uint64 instruction); - std::string ADDUH_QB(uint64 instruction); - std::string ADDUH_R_QB(uint64 instruction); - std::string ADDWC(uint64 instruction); - std::string ALUIPC(uint64 instruction); - std::string AND_16_(uint64 instruction); - std::string AND_32_(uint64 instruction); - std::string ANDI_16_(uint64 instruction); - std::string ANDI_32_(uint64 instruction); - std::string APPEND(uint64 instruction); - std::string ASET(uint64 instruction); - std::string BALC_16_(uint64 instruction); - std::string BALC_32_(uint64 instruction); - std::string BALRSC(uint64 instruction); - std::string BBEQZC(uint64 instruction); - std::string BBNEZC(uint64 instruction); - std::string BC_16_(uint64 instruction); - std::string BC_32_(uint64 instruction); - std::string BC1EQZC(uint64 instruction); - std::string BC1NEZC(uint64 instruction); - std::string BC2EQZC(uint64 instruction); - std::string BC2NEZC(uint64 instruction); - std::string BEQC_16_(uint64 instruction); - std::string BEQC_32_(uint64 instruction); - std::string BEQIC(uint64 instruction); - std::string BEQZC_16_(uint64 instruction); - std::string BGEC(uint64 instruction); - std::string BGEIC(uint64 instruction); - std::string BGEIUC(uint64 instruction); - std::string BGEUC(uint64 instruction); - std::string BLTC(uint64 instruction); - std::string BLTIC(uint64 instruction); - std::string BLTIUC(uint64 instruction); - std::string BLTUC(uint64 instruction); - std::string BNEC_16_(uint64 instruction); - std::string BNEC_32_(uint64 instruction); - std::string BNEIC(uint64 instruction); - std::string BNEZC_16_(uint64 instruction); - std::string BPOSGE32C(uint64 instruction); - std::string BREAK_16_(uint64 instruction); - std::string BREAK_32_(uint64 instruction); - std::string BRSC(uint64 instruction); - std::string CACHE(uint64 instruction); - std::string CACHEE(uint64 instruction); - std::string CEIL_L_D(uint64 instruction); - std::string CEIL_L_S(uint64 instruction); - std::string CEIL_W_D(uint64 instruction); - std::string CEIL_W_S(uint64 instruction); - std::string CFC1(uint64 instruction); - std::string CFC2(uint64 instruction); - std::string CLASS_D(uint64 instruction); - std::string CLASS_S(uint64 instruction); - std::string CLO(uint64 instruction); - std::string CLZ(uint64 instruction); - std::string CMP_AF_D(uint64 instruction); - std::string CMP_AF_S(uint64 instruction); - std::string CMP_EQ_D(uint64 instruction); - std::string CMP_EQ_PH(uint64 instruction); - std::string CMP_EQ_S(uint64 instruction); - std::string CMP_LE_D(uint64 instruction); - std::string CMP_LE_PH(uint64 instruction); - std::string CMP_LE_S(uint64 instruction); - std::string CMP_LT_D(uint64 instruction); - std::string CMP_LT_PH(uint64 instruction); - std::string CMP_LT_S(uint64 instruction); - std::string CMP_NE_D(uint64 instruction); - std::string CMP_NE_S(uint64 instruction); - std::string CMP_OR_D(uint64 instruction); - std::string CMP_OR_S(uint64 instruction); - std::string CMP_SAF_D(uint64 instruction); - std::string CMP_SAF_S(uint64 instruction); - std::string CMP_SEQ_D(uint64 instruction); - std::string CMP_SEQ_S(uint64 instruction); - std::string CMP_SLE_D(uint64 instruction); - std::string CMP_SLE_S(uint64 instruction); - std::string CMP_SLT_D(uint64 instruction); - std::string CMP_SLT_S(uint64 instruction); - std::string CMP_SNE_D(uint64 instruction); - std::string CMP_SNE_S(uint64 instruction); - std::string CMP_SOR_D(uint64 instruction); - std::string CMP_SOR_S(uint64 instruction); - std::string CMP_SUEQ_D(uint64 instruction); - std::string CMP_SUEQ_S(uint64 instruction); - std::string CMP_SULE_D(uint64 instruction); - std::string CMP_SULE_S(uint64 instruction); - std::string CMP_SULT_D(uint64 instruction); - std::string CMP_SULT_S(uint64 instruction); - std::string CMP_SUN_D(uint64 instruction); - std::string CMP_SUN_S(uint64 instruction); - std::string CMP_SUNE_D(uint64 instruction); - std::string CMP_SUNE_S(uint64 instruction); - std::string CMP_UEQ_D(uint64 instruction); - std::string CMP_UEQ_S(uint64 instruction); - std::string CMP_ULE_D(uint64 instruction); - std::string CMP_ULE_S(uint64 instruction); - std::string CMP_ULT_D(uint64 instruction); - std::string CMP_ULT_S(uint64 instruction); - std::string CMP_UN_D(uint64 instruction); - std::string CMP_UN_S(uint64 instruction); - std::string CMP_UNE_D(uint64 instruction); - std::string CMP_UNE_S(uint64 instruction); - std::string CMPGDU_EQ_QB(uint64 instruction); - std::string CMPGDU_LE_QB(uint64 instruction); - std::string CMPGDU_LT_QB(uint64 instruction); - std::string CMPGU_EQ_QB(uint64 instruction); - std::string CMPGU_LE_QB(uint64 instruction); - std::string CMPGU_LT_QB(uint64 instruction); - std::string CMPU_EQ_QB(uint64 instruction); - std::string CMPU_LE_QB(uint64 instruction); - std::string CMPU_LT_QB(uint64 instruction); - std::string COP2_1(uint64 instruction); - std::string CTC1(uint64 instruction); - std::string CTC2(uint64 instruction); - std::string CVT_D_L(uint64 instruction); - std::string CVT_D_S(uint64 instruction); - std::string CVT_D_W(uint64 instruction); - std::string CVT_L_D(uint64 instruction); - std::string CVT_L_S(uint64 instruction); - std::string CVT_S_D(uint64 instruction); - std::string CVT_S_L(uint64 instruction); - std::string CVT_S_PL(uint64 instruction); - std::string CVT_S_PU(uint64 instruction); - std::string CVT_S_W(uint64 instruction); - std::string CVT_W_D(uint64 instruction); - std::string CVT_W_S(uint64 instruction); - std::string DADDIU_48_(uint64 instruction); - std::string DADDIU_NEG_(uint64 instruction); - std::string DADDIU_U12_(uint64 instruction); - std::string DADD(uint64 instruction); - std::string DADDU(uint64 instruction); - std::string DCLO(uint64 instruction); - std::string DCLZ(uint64 instruction); - std::string DDIV(uint64 instruction); - std::string DDIVU(uint64 instruction); - std::string DERET(uint64 instruction); - std::string DEXTM(uint64 instruction); - std::string DEXT(uint64 instruction); - std::string DEXTU(uint64 instruction); - std::string DINSM(uint64 instruction); - std::string DINS(uint64 instruction); - std::string DINSU(uint64 instruction); - std::string DI(uint64 instruction); - std::string DIV(uint64 instruction); - std::string DIV_D(uint64 instruction); - std::string DIV_S(uint64 instruction); - std::string DIVU(uint64 instruction); - std::string DLSA(uint64 instruction); - std::string DLUI_48_(uint64 instruction); - std::string DMFC0(uint64 instruction); - std::string DMFC1(uint64 instruction); - std::string DMFC2(uint64 instruction); - std::string DMFGC0(uint64 instruction); - std::string DMOD(uint64 instruction); - std::string DMODU(uint64 instruction); - std::string DMTC0(uint64 instruction); - std::string DMTC1(uint64 instruction); - std::string DMTC2(uint64 instruction); - std::string DMTGC0(uint64 instruction); - std::string DMT(uint64 instruction); - std::string DMUH(uint64 instruction); - std::string DMUHU(uint64 instruction); - std::string DMUL(uint64 instruction); - std::string DMULU(uint64 instruction); - std::string DPAQ_S_W_PH(uint64 instruction); - std::string DPAQ_SA_L_W(uint64 instruction); - std::string DPAQX_S_W_PH(uint64 instruction); - std::string DPAQX_SA_W_PH(uint64 instruction); - std::string DPAU_H_QBL(uint64 instruction); - std::string DPAU_H_QBR(uint64 instruction); - std::string DPA_W_PH(uint64 instruction); - std::string DPAX_W_PH(uint64 instruction); - std::string DPS_W_PH(uint64 instruction); - std::string DPSQ_SA_L_W(uint64 instruction); - std::string DPSQ_S_W_PH(uint64 instruction); - std::string DPSQX_SA_W_PH(uint64 instruction); - std::string DPSQX_S_W_PH(uint64 instruction); - std::string DPSU_H_QBL(uint64 instruction); - std::string DPSU_H_QBR(uint64 instruction); - std::string DPSX_W_PH(uint64 instruction); - std::string DROTR(uint64 instruction); - std::string DROTR32(uint64 instruction); - std::string DROTRV(uint64 instruction); - std::string DROTX(uint64 instruction); - std::string DSLL(uint64 instruction); - std::string DSLL32(uint64 instruction); - std::string DSLLV(uint64 instruction); - std::string DSRA(uint64 instruction); - std::string DSRA32(uint64 instruction); - std::string DSRAV(uint64 instruction); - std::string DSRL32(uint64 instruction); - std::string DSRL(uint64 instruction); - std::string DSRLV(uint64 instruction); - std::string DSUB(uint64 instruction); - std::string DSUBU(uint64 instruction); - std::string DVP(uint64 instruction); - std::string DVPE(uint64 instruction); - std::string EHB(uint64 instruction); - std::string EI(uint64 instruction); - std::string EMT(uint64 instruction); - std::string ERET(uint64 instruction); - std::string ERETNC(uint64 instruction); - std::string EVP(uint64 instruction); - std::string EVPE(uint64 instruction); - std::string EXT(uint64 instruction); - std::string EXTD(uint64 instruction); - std::string EXTD32(uint64 instruction); - std::string EXTP(uint64 instruction); - std::string EXTPDP(uint64 instruction); - std::string EXTPDPV(uint64 instruction); - std::string EXTPV(uint64 instruction); - std::string EXTR_RS_W(uint64 instruction); - std::string EXTR_R_W(uint64 instruction); - std::string EXTR_S_H(uint64 instruction); - std::string EXTR_W(uint64 instruction); - std::string EXTRV_R_W(uint64 instruction); - std::string EXTRV_RS_W(uint64 instruction); - std::string EXTRV_S_H(uint64 instruction); - std::string EXTRV_W(uint64 instruction); - std::string EXTW(uint64 instruction); - std::string FLOOR_L_D(uint64 instruction); - std::string FLOOR_L_S(uint64 instruction); - std::string FLOOR_W_D(uint64 instruction); - std::string FLOOR_W_S(uint64 instruction); - std::string FORK(uint64 instruction); - std::string HYPCALL(uint64 instruction); - std::string HYPCALL_16_(uint64 instruction); - std::string INS(uint64 instruction); - std::string INSV(uint64 instruction); - std::string IRET(uint64 instruction); - std::string JALRC_16_(uint64 instruction); - std::string JALRC_32_(uint64 instruction); - std::string JALRC_HB(uint64 instruction); - std::string JRC(uint64 instruction); - std::string LB_16_(uint64 instruction); - std::string LB_GP_(uint64 instruction); - std::string LB_S9_(uint64 instruction); - std::string LB_U12_(uint64 instruction); - std::string LBE(uint64 instruction); - std::string LBU_16_(uint64 instruction); - std::string LBU_GP_(uint64 instruction); - std::string LBU_S9_(uint64 instruction); - std::string LBU_U12_(uint64 instruction); - std::string LBUE(uint64 instruction); - std::string LBUX(uint64 instruction); - std::string LBX(uint64 instruction); - std::string LD_GP_(uint64 instruction); - std::string LD_S9_(uint64 instruction); - std::string LD_U12_(uint64 instruction); - std::string LDC1_GP_(uint64 instruction); - std::string LDC1_S9_(uint64 instruction); - std::string LDC1_U12_(uint64 instruction); - std::string LDC1X(uint64 instruction); - std::string LDC1XS(uint64 instruction); - std::string LDC2(uint64 instruction); - std::string LDM(uint64 instruction); - std::string LDPC_48_(uint64 instruction); - std::string LDX(uint64 instruction); - std::string LDXS(uint64 instruction); - std::string LH_16_(uint64 instruction); - std::string LH_GP_(uint64 instruction); - std::string LH_S9_(uint64 instruction); - std::string LH_U12_(uint64 instruction); - std::string LHE(uint64 instruction); - std::string LHU_16_(uint64 instruction); - std::string LHU_GP_(uint64 instruction); - std::string LHU_S9_(uint64 instruction); - std::string LHU_U12_(uint64 instruction); - std::string LHUE(uint64 instruction); - std::string LHUX(uint64 instruction); - std::string LHUXS(uint64 instruction); - std::string LHX(uint64 instruction); - std::string LHXS(uint64 instruction); - std::string LI_16_(uint64 instruction); - std::string LI_48_(uint64 instruction); - std::string LL(uint64 instruction); - std::string LLD(uint64 instruction); - std::string LLDP(uint64 instruction); - std::string LLE(uint64 instruction); - std::string LLWP(uint64 instruction); - std::string LLWPE(uint64 instruction); - std::string LSA(uint64 instruction); - std::string LUI(uint64 instruction); - std::string LW_16_(uint64 instruction); - std::string LW_4X4_(uint64 instruction); - std::string LWC1_GP_(uint64 instruction); - std::string LWC1_S9_(uint64 instruction); - std::string LWC1_U12_(uint64 instruction); - std::string LWC1X(uint64 instruction); - std::string LWC1XS(uint64 instruction); - std::string LWC2(uint64 instruction); - std::string LWE(uint64 instruction); - std::string LW_GP_(uint64 instruction); - std::string LW_GP16_(uint64 instruction); - std::string LWM(uint64 instruction); - std::string LWPC_48_(uint64 instruction); - std::string LW_S9_(uint64 instruction); - std::string LW_SP_(uint64 instruction); - std::string LW_U12_(uint64 instruction); - std::string LWU_GP_(uint64 instruction); - std::string LWU_S9_(uint64 instruction); - std::string LWU_U12_(uint64 instruction); - std::string LWUX(uint64 instruction); - std::string LWUXS(uint64 instruction); - std::string LWX(uint64 instruction); - std::string LWXS_16_(uint64 instruction); - std::string LWXS_32_(uint64 instruction); - std::string MADD_DSP_(uint64 instruction); - std::string MADDF_D(uint64 instruction); - std::string MADDF_S(uint64 instruction); - std::string MADDU_DSP_(uint64 instruction); - std::string MAQ_S_W_PHL(uint64 instruction); - std::string MAQ_S_W_PHR(uint64 instruction); - std::string MAQ_SA_W_PHL(uint64 instruction); - std::string MAQ_SA_W_PHR(uint64 instruction); - std::string MAX_D(uint64 instruction); - std::string MAX_S(uint64 instruction); - std::string MAXA_D(uint64 instruction); - std::string MAXA_S(uint64 instruction); - std::string MFC0(uint64 instruction); - std::string MFC1(uint64 instruction); - std::string MFC2(uint64 instruction); - std::string MFGC0(uint64 instruction); - std::string MFHC0(uint64 instruction); - std::string MFHC1(uint64 instruction); - std::string MFHC2(uint64 instruction); - std::string MFHGC0(uint64 instruction); - std::string MFHI_DSP_(uint64 instruction); - std::string MFHTR(uint64 instruction); - std::string MFLO_DSP_(uint64 instruction); - std::string MFTR(uint64 instruction); - std::string MIN_D(uint64 instruction); - std::string MIN_S(uint64 instruction); - std::string MINA_D(uint64 instruction); - std::string MINA_S(uint64 instruction); - std::string MOD(uint64 instruction); - std::string MODSUB(uint64 instruction); - std::string MODU(uint64 instruction); - std::string MOV_D(uint64 instruction); - std::string MOV_S(uint64 instruction); - std::string MOVE_BALC(uint64 instruction); - std::string MOVEP(uint64 instruction); - std::string MOVEP_REV_(uint64 instruction); - std::string MOVE(uint64 instruction); - std::string MOVN(uint64 instruction); - std::string MOVZ(uint64 instruction); - std::string MSUB_DSP_(uint64 instruction); - std::string MSUBF_D(uint64 instruction); - std::string MSUBF_S(uint64 instruction); - std::string MSUBU_DSP_(uint64 instruction); - std::string MTC0(uint64 instruction); - std::string MTC1(uint64 instruction); - std::string MTC2(uint64 instruction); - std::string MTGC0(uint64 instruction); - std::string MTHC0(uint64 instruction); - std::string MTHC1(uint64 instruction); - std::string MTHC2(uint64 instruction); - std::string MTHGC0(uint64 instruction); - std::string MTHI_DSP_(uint64 instruction); - std::string MTHLIP(uint64 instruction); - std::string MTHTR(uint64 instruction); - std::string MTLO_DSP_(uint64 instruction); - std::string MTTR(uint64 instruction); - std::string MUH(uint64 instruction); - std::string MUHU(uint64 instruction); - std::string MUL_32_(uint64 instruction); - std::string MUL_4X4_(uint64 instruction); - std::string MUL_D(uint64 instruction); - std::string MUL_PH(uint64 instruction); - std::string MUL_S(uint64 instruction); - std::string MUL_S_PH(uint64 instruction); - std::string MULEQ_S_W_PHL(uint64 instruction); - std::string MULEQ_S_W_PHR(uint64 instruction); - std::string MULEU_S_PH_QBL(uint64 instruction); - std::string MULEU_S_PH_QBR(uint64 instruction); - std::string MULQ_RS_PH(uint64 instruction); - std::string MULQ_RS_W(uint64 instruction); - std::string MULQ_S_PH(uint64 instruction); - std::string MULQ_S_W(uint64 instruction); - std::string MULSA_W_PH(uint64 instruction); - std::string MULSAQ_S_W_PH(uint64 instruction); - std::string MULT_DSP_(uint64 instruction); - std::string MULTU_DSP_(uint64 instruction); - std::string MULU(uint64 instruction); - std::string NEG_D(uint64 instruction); - std::string NEG_S(uint64 instruction); - std::string NOP_16_(uint64 instruction); - std::string NOP_32_(uint64 instruction); - std::string NOR(uint64 instruction); - std::string NOT_16_(uint64 instruction); - std::string OR_16_(uint64 instruction); - std::string OR_32_(uint64 instruction); - std::string ORI(uint64 instruction); - std::string PACKRL_PH(uint64 instruction); - std::string PAUSE(uint64 instruction); - std::string PICK_PH(uint64 instruction); - std::string PICK_QB(uint64 instruction); - std::string PRECEQ_W_PHL(uint64 instruction); - std::string PRECEQ_W_PHR(uint64 instruction); - std::string PRECEQU_PH_QBL(uint64 instruction); - std::string PRECEQU_PH_QBLA(uint64 instruction); - std::string PRECEQU_PH_QBR(uint64 instruction); - std::string PRECEQU_PH_QBRA(uint64 instruction); - std::string PRECEU_PH_QBL(uint64 instruction); - std::string PRECEU_PH_QBLA(uint64 instruction); - std::string PRECEU_PH_QBR(uint64 instruction); - std::string PRECEU_PH_QBRA(uint64 instruction); - std::string PRECR_QB_PH(uint64 instruction); - std::string PRECR_SRA_PH_W(uint64 instruction); - std::string PRECR_SRA_R_PH_W(uint64 instruction); - std::string PRECRQ_PH_W(uint64 instruction); - std::string PRECRQ_QB_PH(uint64 instruction); - std::string PRECRQ_RS_PH_W(uint64 instruction); - std::string PRECRQU_S_QB_PH(uint64 instruction); - std::string PREF_S9_(uint64 instruction); - std::string PREF_U12_(uint64 instruction); - std::string PREFE(uint64 instruction); - std::string PREPEND(uint64 instruction); - std::string RADDU_W_QB(uint64 instruction); - std::string RDDSP(uint64 instruction); - std::string RDHWR(uint64 instruction); - std::string RDPGPR(uint64 instruction); - std::string RECIP_D(uint64 instruction); - std::string RECIP_S(uint64 instruction); - std::string REPL_PH(uint64 instruction); - std::string REPL_QB(uint64 instruction); - std::string REPLV_PH(uint64 instruction); - std::string REPLV_QB(uint64 instruction); - std::string RESTORE_32_(uint64 instruction); - std::string RESTORE_JRC_16_(uint64 instruction); - std::string RESTORE_JRC_32_(uint64 instruction); - std::string RESTOREF(uint64 instruction); - std::string RINT_D(uint64 instruction); - std::string RINT_S(uint64 instruction); - std::string ROTR(uint64 instruction); - std::string ROTRV(uint64 instruction); - std::string ROTX(uint64 instruction); - std::string ROUND_L_D(uint64 instruction); - std::string ROUND_L_S(uint64 instruction); - std::string ROUND_W_D(uint64 instruction); - std::string ROUND_W_S(uint64 instruction); - std::string RSQRT_D(uint64 instruction); - std::string RSQRT_S(uint64 instruction); - std::string SAVE_16_(uint64 instruction); - std::string SAVE_32_(uint64 instruction); - std::string SAVEF(uint64 instruction); - std::string SB_16_(uint64 instruction); - std::string SB_GP_(uint64 instruction); - std::string SB_S9_(uint64 instruction); - std::string SB_U12_(uint64 instruction); - std::string SBE(uint64 instruction); - std::string SBX(uint64 instruction); - std::string SC(uint64 instruction); - std::string SCD(uint64 instruction); - std::string SCDP(uint64 instruction); - std::string SCE(uint64 instruction); - std::string SCWP(uint64 instruction); - std::string SCWPE(uint64 instruction); - std::string SD_GP_(uint64 instruction); - std::string SD_S9_(uint64 instruction); - std::string SD_U12_(uint64 instruction); - std::string SDBBP_16_(uint64 instruction); - std::string SDBBP_32_(uint64 instruction); - std::string SDC1_GP_(uint64 instruction); - std::string SDC1_S9_(uint64 instruction); - std::string SDC1_U12_(uint64 instruction); - std::string SDC1X(uint64 instruction); - std::string SDC1XS(uint64 instruction); - std::string SDC2(uint64 instruction); - std::string SDM(uint64 instruction); - std::string SDPC_48_(uint64 instruction); - std::string SDX(uint64 instruction); - std::string SDXS(uint64 instruction); - std::string SEB(uint64 instruction); - std::string SEH(uint64 instruction); - std::string SEL_D(uint64 instruction); - std::string SEL_S(uint64 instruction); - std::string SELEQZ_D(uint64 instruction); - std::string SELEQZ_S(uint64 instruction); - std::string SELNEZ_D(uint64 instruction); - std::string SELNEZ_S(uint64 instruction); - std::string SEQI(uint64 instruction); - std::string SH_16_(uint64 instruction); - std::string SH_GP_(uint64 instruction); - std::string SH_S9_(uint64 instruction); - std::string SH_U12_(uint64 instruction); - std::string SHE(uint64 instruction); - std::string SHILO(uint64 instruction); - std::string SHILOV(uint64 instruction); - std::string SHLL_PH(uint64 instruction); - std::string SHLL_QB(uint64 instruction); - std::string SHLL_S_PH(uint64 instruction); - std::string SHLL_S_W(uint64 instruction); - std::string SHLLV_PH(uint64 instruction); - std::string SHLLV_QB(uint64 instruction); - std::string SHLLV_S_PH(uint64 instruction); - std::string SHLLV_S_W(uint64 instruction); - std::string SHRA_PH(uint64 instruction); - std::string SHRA_QB(uint64 instruction); - std::string SHRA_R_PH(uint64 instruction); - std::string SHRA_R_QB(uint64 instruction); - std::string SHRA_R_W(uint64 instruction); - std::string SHRAV_PH(uint64 instruction); - std::string SHRAV_QB(uint64 instruction); - std::string SHRAV_R_PH(uint64 instruction); - std::string SHRAV_R_QB(uint64 instruction); - std::string SHRAV_R_W(uint64 instruction); - std::string SHRL_PH(uint64 instruction); - std::string SHRL_QB(uint64 instruction); - std::string SHRLV_PH(uint64 instruction); - std::string SHRLV_QB(uint64 instruction); - std::string SHX(uint64 instruction); - std::string SHXS(uint64 instruction); - std::string SIGRIE(uint64 instruction); - std::string SLL_16_(uint64 instruction); - std::string SLL_32_(uint64 instruction); - std::string SLLV(uint64 instruction); - std::string SLT(uint64 instruction); - std::string SLTI(uint64 instruction); - std::string SLTIU(uint64 instruction); - std::string SLTU(uint64 instruction); - std::string SOV(uint64 instruction); - std::string SPECIAL2(uint64 instruction); - std::string SQRT_D(uint64 instruction); - std::string SQRT_S(uint64 instruction); - std::string SRA(uint64 instruction); - std::string SRAV(uint64 instruction); - std::string SRL_16_(uint64 instruction); - std::string SRL_32_(uint64 instruction); - std::string SRLV(uint64 instruction); - std::string SUB(uint64 instruction); - std::string SUB_D(uint64 instruction); - std::string SUB_S(uint64 instruction); - std::string SUBQ_PH(uint64 instruction); - std::string SUBQ_S_PH(uint64 instruction); - std::string SUBQ_S_W(uint64 instruction); - std::string SUBQH_PH(uint64 instruction); - std::string SUBQH_R_PH(uint64 instruction); - std::string SUBQH_R_W(uint64 instruction); - std::string SUBQH_W(uint64 instruction); - std::string SUBU_16_(uint64 instruction); - std::string SUBU_32_(uint64 instruction); - std::string SUBU_PH(uint64 instruction); - std::string SUBU_QB(uint64 instruction); - std::string SUBU_S_PH(uint64 instruction); - std::string SUBU_S_QB(uint64 instruction); - std::string SUBUH_QB(uint64 instruction); - std::string SUBUH_R_QB(uint64 instruction); - std::string SW_16_(uint64 instruction); - std::string SW_4X4_(uint64 instruction); - std::string SW_GP16_(uint64 instruction); - std::string SW_GP_(uint64 instruction); - std::string SW_S9_(uint64 instruction); - std::string SW_SP_(uint64 instruction); - std::string SW_U12_(uint64 instruction); - std::string SWC1_GP_(uint64 instruction); - std::string SWC1_S9_(uint64 instruction); - std::string SWC1_U12_(uint64 instruction); - std::string SWC1X(uint64 instruction); - std::string SWC1XS(uint64 instruction); - std::string SWC2(uint64 instruction); - std::string SWE(uint64 instruction); - std::string SWM(uint64 instruction); - std::string SWPC_48_(uint64 instruction); - std::string SWX(uint64 instruction); - std::string SWXS(uint64 instruction); - std::string SYNC(uint64 instruction); - std::string SYNCI(uint64 instruction); - std::string SYNCIE(uint64 instruction); - std::string SYSCALL_16_(uint64 instruction); - std::string SYSCALL_32_(uint64 instruction); - std::string TEQ(uint64 instruction); - std::string TLBGINV(uint64 instruction); - std::string TLBGINVF(uint64 instruction); - std::string TLBGP(uint64 instruction); - std::string TLBGR(uint64 instruction); - std::string TLBGWI(uint64 instruction); - std::string TLBGWR(uint64 instruction); - std::string TLBINV(uint64 instruction); - std::string TLBINVF(uint64 instruction); - std::string TLBP(uint64 instruction); - std::string TLBR(uint64 instruction); - std::string TLBWI(uint64 instruction); - std::string TLBWR(uint64 instruction); - std::string TNE(uint64 instruction); - std::string TRUNC_L_D(uint64 instruction); - std::string TRUNC_L_S(uint64 instruction); - std::string TRUNC_W_D(uint64 instruction); - std::string TRUNC_W_S(uint64 instruction); - std::string UALDM(uint64 instruction); - std::string UALH(uint64 instruction); - std::string UALWM(uint64 instruction); - std::string UASDM(uint64 instruction); - std::string UASH(uint64 instruction); - std::string UASWM(uint64 instruction); - std::string UDI(uint64 instruction); - std::string WAIT(uint64 instruction); - std::string WRDSP(uint64 instruction); - std::string WRPGPR(uint64 instruction); - std::string XOR_16_(uint64 instruction); - std::string XOR_32_(uint64 instruction); - std::string XORI(uint64 instruction); - std::string YIELD(uint64 instruction); - - static Pool P_SYSCALL[2]; - static Pool P_RI[4]; - static Pool P_ADDIU[2]; - static Pool P_TRAP[2]; - static Pool P_CMOVE[2]; - static Pool P_D_MT_VPE[2]; - static Pool P_E_MT_VPE[2]; - static Pool _P_MT_VPE[2]; - static Pool P_MT_VPE[8]; - static Pool P_DVP[2]; - static Pool P_SLTU[2]; - static Pool _POOL32A0[128]; - static Pool ADDQ__S__PH[2]; - static Pool MUL__S__PH[2]; - static Pool ADDQH__R__PH[2]; - static Pool ADDQH__R__W[2]; - static Pool ADDU__S__QB[2]; - static Pool ADDU__S__PH[2]; - static Pool ADDUH__R__QB[2]; - static Pool SHRAV__R__PH[2]; - static Pool SHRAV__R__QB[2]; - static Pool SUBQ__S__PH[2]; - static Pool SUBQH__R__PH[2]; - static Pool SUBQH__R__W[2]; - static Pool SUBU__S__QB[2]; - static Pool SUBU__S__PH[2]; - static Pool SHRA__R__PH[2]; - static Pool SUBUH__R__QB[2]; - static Pool SHLLV__S__PH[2]; - static Pool SHLL__S__PH[4]; - static Pool PRECR_SRA__R__PH_W[2]; - static Pool _POOL32A5[128]; - static Pool PP_LSX[16]; - static Pool PP_LSXS[16]; - static Pool P_LSX[2]; - static Pool POOL32Axf_1_0[4]; - static Pool POOL32Axf_1_1[4]; - static Pool POOL32Axf_1_3[4]; - static Pool POOL32Axf_1_4[2]; - static Pool MAQ_S_A__W_PHR[2]; - static Pool MAQ_S_A__W_PHL[2]; - static Pool POOL32Axf_1_5[2]; - static Pool POOL32Axf_1_7[4]; - static Pool POOL32Axf_1[8]; - static Pool POOL32Axf_2_DSP__0_7[8]; - static Pool POOL32Axf_2_DSP__8_15[8]; - static Pool POOL32Axf_2_DSP__16_23[8]; - static Pool POOL32Axf_2_DSP__24_31[8]; - static Pool POOL32Axf_2[4]; - static Pool POOL32Axf_4[128]; - static Pool POOL32Axf_5_group0[32]; - static Pool POOL32Axf_5_group1[32]; - static Pool ERETx[2]; - static Pool POOL32Axf_5_group3[32]; - static Pool POOL32Axf_5[4]; - static Pool SHRA__R__QB[2]; - static Pool POOL32Axf_7[8]; - static Pool POOL32Axf[8]; - static Pool _POOL32A7[8]; - static Pool P32A[8]; - static Pool P_GP_D[2]; - static Pool P_GP_W[4]; - static Pool POOL48I[32]; - static Pool PP_SR[4]; - static Pool P_SR_F[8]; - static Pool P_SR[2]; - static Pool P_SLL[5]; - static Pool P_SHIFT[16]; - static Pool P_ROTX[4]; - static Pool P_INS[4]; - static Pool P_EXT[4]; - static Pool P_U12[16]; - static Pool RINT_fmt[2]; - static Pool ADD_fmt0[2]; - static Pool SELEQZ_fmt[2]; - static Pool CLASS_fmt[2]; - static Pool SUB_fmt0[2]; - static Pool SELNEZ_fmt[2]; - static Pool MUL_fmt0[2]; - static Pool SEL_fmt[2]; - static Pool DIV_fmt0[2]; - static Pool ADD_fmt1[2]; - static Pool SUB_fmt1[2]; - static Pool MUL_fmt1[2]; - static Pool MADDF_fmt[2]; - static Pool DIV_fmt1[2]; - static Pool MSUBF_fmt[2]; - static Pool POOL32F_0[64]; - static Pool MIN_fmt[2]; - static Pool MAX_fmt[2]; - static Pool MINA_fmt[2]; - static Pool MAXA_fmt[2]; - static Pool CVT_L_fmt[2]; - static Pool RSQRT_fmt[2]; - static Pool FLOOR_L_fmt[2]; - static Pool CVT_W_fmt[2]; - static Pool SQRT_fmt[2]; - static Pool FLOOR_W_fmt[2]; - static Pool RECIP_fmt[2]; - static Pool CEIL_L_fmt[2]; - static Pool CEIL_W_fmt[2]; - static Pool TRUNC_L_fmt[2]; - static Pool TRUNC_W_fmt[2]; - static Pool ROUND_L_fmt[2]; - static Pool ROUND_W_fmt[2]; - static Pool POOL32Fxf_0[64]; - static Pool MOV_fmt[4]; - static Pool ABS_fmt[4]; - static Pool NEG_fmt[4]; - static Pool CVT_D_fmt[4]; - static Pool CVT_S_fmt[4]; - static Pool POOL32Fxf_1[32]; - static Pool POOL32Fxf[4]; - static Pool POOL32F_3[8]; - static Pool CMP_condn_S[32]; - static Pool CMP_condn_D[32]; - static Pool POOL32F_5[8]; - static Pool POOL32F[8]; - static Pool POOL32S_0[64]; - static Pool POOL32Sxf_4[128]; - static Pool POOL32Sxf[8]; - static Pool POOL32S_4[8]; - static Pool POOL32S[8]; - static Pool P_LUI[2]; - static Pool P_GP_LH[2]; - static Pool P_GP_SH[2]; - static Pool P_GP_CP1[4]; - static Pool P_GP_M64[4]; - static Pool P_GP_BH[8]; - static Pool P_LS_U12[16]; - static Pool P_PREF_S9_[2]; - static Pool P_LS_S0[16]; - static Pool ASET_ACLR[2]; - static Pool P_LL[4]; - static Pool P_SC[4]; - static Pool P_LLD[8]; - static Pool P_SCD[8]; - static Pool P_LS_S1[16]; - static Pool P_PREFE[2]; - static Pool P_LLE[4]; - static Pool P_SCE[4]; - static Pool P_LS_E0[16]; - static Pool P_LS_WM[2]; - static Pool P_LS_UAWM[2]; - static Pool P_LS_DM[2]; - static Pool P_LS_UADM[2]; - static Pool P_LS_S9[8]; - static Pool P_BAL[2]; - static Pool P_BALRSC[2]; - static Pool P_J[16]; - static Pool P_BR3A[32]; - static Pool P_BR1[4]; - static Pool P_BR2[4]; - static Pool P_BRI[8]; - static Pool P32[32]; - static Pool P16_SYSCALL[2]; - static Pool P16_RI[4]; - static Pool P16_MV[2]; - static Pool P16_SHIFT[2]; - static Pool POOL16C_00[4]; - static Pool POOL16C_0[2]; - static Pool P16C[2]; - static Pool P16_A1[2]; - static Pool P_ADDIU_RS5_[2]; - static Pool P16_A2[2]; - static Pool P16_ADDU[2]; - static Pool P16_JRC[2]; - static Pool P16_BR1[2]; - static Pool P16_BR[2]; - static Pool P16_SR[2]; - static Pool P16_4X4[4]; - static Pool P16_LB[4]; - static Pool P16_LH[4]; - static Pool P16[32]; - static Pool MAJOR[2]; - -}; - -#endif diff --git a/disas/riscv-xthead.c b/disas/riscv-xthead.c new file mode 100644 index 0000000000..fcca326d1c --- /dev/null +++ b/disas/riscv-xthead.c @@ -0,0 +1,708 @@ +/* + * QEMU RISC-V Disassembler for xthead. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "disas/riscv.h" +#include "disas/riscv-xthead.h" + +typedef enum { + /* 0 is reserved for rv_op_illegal. */ + /* XTheadBa */ + rv_op_th_addsl = 1, + /* XTheadBb */ + rv_op_th_srri, + rv_op_th_srriw, + rv_op_th_ext, + rv_op_th_extu, + rv_op_th_ff0, + rv_op_th_ff1, + rv_op_th_rev, + rv_op_th_revw, + rv_op_th_tstnbz, + /* XTheadBs */ + rv_op_th_tst, + /* XTheadCmo */ + rv_op_th_dcache_call, + rv_op_th_dcache_ciall, + rv_op_th_dcache_iall, + rv_op_th_dcache_cpa, + rv_op_th_dcache_cipa, + rv_op_th_dcache_ipa, + rv_op_th_dcache_cva, + rv_op_th_dcache_civa, + rv_op_th_dcache_iva, + rv_op_th_dcache_csw, + rv_op_th_dcache_cisw, + rv_op_th_dcache_isw, + rv_op_th_dcache_cpal1, + rv_op_th_dcache_cval1, + rv_op_th_icache_iall, + rv_op_th_icache_ialls, + rv_op_th_icache_ipa, + rv_op_th_icache_iva, + rv_op_th_l2cache_call, + rv_op_th_l2cache_ciall, + rv_op_th_l2cache_iall, + /* XTheadCondMov */ + rv_op_th_mveqz, + rv_op_th_mvnez, + /* XTheadFMemIdx */ + rv_op_th_flrd, + rv_op_th_flrw, + rv_op_th_flurd, + rv_op_th_flurw, + rv_op_th_fsrd, + rv_op_th_fsrw, + rv_op_th_fsurd, + rv_op_th_fsurw, + /* XTheadFmv */ + rv_op_th_fmv_hw_x, + rv_op_th_fmv_x_hw, + /* XTheadMac */ + rv_op_th_mula, + rv_op_th_mulah, + rv_op_th_mulaw, + rv_op_th_muls, + rv_op_th_mulsw, + rv_op_th_mulsh, + /* XTheadMemIdx */ + rv_op_th_lbia, + rv_op_th_lbib, + rv_op_th_lbuia, + rv_op_th_lbuib, + rv_op_th_lhia, + rv_op_th_lhib, + rv_op_th_lhuia, + rv_op_th_lhuib, + rv_op_th_lwia, + rv_op_th_lwib, + rv_op_th_lwuia, + rv_op_th_lwuib, + rv_op_th_ldia, + rv_op_th_ldib, + rv_op_th_sbia, + rv_op_th_sbib, + rv_op_th_shia, + rv_op_th_shib, + rv_op_th_swia, + rv_op_th_swib, + rv_op_th_sdia, + rv_op_th_sdib, + rv_op_th_lrb, + rv_op_th_lrbu, + rv_op_th_lrh, + rv_op_th_lrhu, + rv_op_th_lrw, + rv_op_th_lrwu, + rv_op_th_lrd, + rv_op_th_srb, + rv_op_th_srh, + rv_op_th_srw, + rv_op_th_srd, + rv_op_th_lurb, + rv_op_th_lurbu, + rv_op_th_lurh, + rv_op_th_lurhu, + rv_op_th_lurw, + rv_op_th_lurwu, + rv_op_th_lurd, + rv_op_th_surb, + rv_op_th_surh, + rv_op_th_surw, + rv_op_th_surd, + /* XTheadMemPair */ + rv_op_th_ldd, + rv_op_th_lwd, + rv_op_th_lwud, + rv_op_th_sdd, + rv_op_th_swd, + /* XTheadSync */ + rv_op_th_sfence_vmas, + rv_op_th_sync, + rv_op_th_sync_i, + rv_op_th_sync_is, + rv_op_th_sync_s, +} rv_xthead_op; + +const rv_opcode_data xthead_opcode_data[] = { + { "th.illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, + /* XTheadBa */ + { "th.addsl", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + /* XTheadBb */ + { "th.srri", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "th.srriw", rv_codec_r2_imm5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "th.ext", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 }, + { "th.extu", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 }, + { "th.ff0", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "th.ff1", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "th.rev", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "th.revw", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "th.tstnbz", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + /* XTheadBs */ + { "th.tst", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + /* XTheadCmo */ + { "th.dcache.call", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.dcache.ciall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.dcache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.dcache.cpa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.cipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.ipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.cva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.civa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.iva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.csw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.cisw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.isw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.cpal1", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.dcache.cval1", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.icache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.icache.ialls", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.icache.ipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.icache.iva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "th.l2cache.call", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.l2cache.ciall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.l2cache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + /* XTheadCondMov */ + { "th.mveqz", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "th.mvnez", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + /* XTheadFMemIdx */ + { "th.flrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.flrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.flurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.flurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.fsrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.fsrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.fsurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.fsurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + /* XTheadFmv */ + { "th.fmv.hw.x", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "th.fmv.x.hw", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + /* XTheadMac */ + { "th.mula", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "th.mulaw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "th.mulah", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "th.muls", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "th.mulsw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "th.mulsh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + /* XTheadMemIdx */ + { "th.lbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 }, + { "th.lbuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lbuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lhia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lhib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lhuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lhuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lwia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lwib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lwuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lwuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.ldia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.ldib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.sbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.sbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.shia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.shib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.swia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.swib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.sdia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.sdib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, + { "th.lrb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lrbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lrh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lrhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lrw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lrwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lrd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.srb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.srh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.srw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.srd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lurb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lurbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lurh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lurhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lurw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lurwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lurd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.surb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.surh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.surw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.surd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + /* XTheadMemPair */ + { "th.ldd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, + { "th.lwd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, + { "th.lwud", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, + { "th.sdd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, + { "th.swd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, + /* XTheadSync */ + { "th.sfence.vmas", rv_codec_r, rv_fmt_rs1_rs2, NULL, 0, 0, 0 }, + { "th.sync", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.sync.i", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.sync.is", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.sync.s", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, +}; + +void decode_xtheadba(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 1: + switch ((inst >> 25) & 0b1111111) { + case 0b0000000: + case 0b0000001: + case 0b0000010: + case 0b0000011: op = rv_op_th_addsl; break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadbb(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 1: + switch ((inst >> 25) & 0b1111111) { + case 0b0001010: op = rv_op_th_srriw; break; + case 0b1000000: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_tstnbz; + } + break; + case 0b1000001: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_rev; + } + break; + case 0b1000010: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_ff0; + } + break; + case 0b1000011: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_ff1; + } + break; + case 0b1000100: + case 0b1001000: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_revw; + } + break; + case 0b0000100: + case 0b0000101: op = rv_op_th_srri; break; + } + break; + case 2: op = rv_op_th_ext; break; + case 3: op = rv_op_th_extu; break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadbs(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 1: + switch ((inst >> 26) & 0b111111) { + case 0b100010: op = rv_op_th_tst; break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadcmo(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 0: + switch ((inst >> 20 & 0b111111111111)) { + case 0b000000000001: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_dcache_call; + } + break; + case 0b000000000011: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_dcache_ciall; + } + break; + case 0b000000000010: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_dcache_iall; + } + break; + case 0b000000101001: op = rv_op_th_dcache_cpa; break; + case 0b000000101011: op = rv_op_th_dcache_cipa; break; + case 0b000000101010: op = rv_op_th_dcache_ipa; break; + case 0b000000100101: op = rv_op_th_dcache_cva; break; + case 0b000000100111: op = rv_op_th_dcache_civa; break; + case 0b000000100110: op = rv_op_th_dcache_iva; break; + case 0b000000100001: op = rv_op_th_dcache_csw; break; + case 0b000000100011: op = rv_op_th_dcache_cisw; break; + case 0b000000100010: op = rv_op_th_dcache_isw; break; + case 0b000000101000: op = rv_op_th_dcache_cpal1; break; + case 0b000000100100: op = rv_op_th_dcache_cval1; break; + case 0b000000010000: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_icache_iall; + } + break; + case 0b000000010001: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_icache_ialls; + } + break; + case 0b000000111000: op = rv_op_th_icache_ipa; break; + case 0b000000110000: op = rv_op_th_icache_iva; break; + case 0b000000010101: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_l2cache_call; + } + break; + case 0b000000010111: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_l2cache_ciall; + } + break; + case 0b000000010110: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_l2cache_iall; + } + break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadcondmov(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 1: + switch ((inst >> 25) & 0b1111111) { + case 0b0100000: op = rv_op_th_mveqz; break; + case 0b0100001: op = rv_op_th_mvnez; break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadfmemidx(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 6: + switch ((inst >> 27) & 0b11111) { + case 8: op = rv_op_th_flrw; break; + case 10: op = rv_op_th_flurw; break; + case 12: op = rv_op_th_flrd; break; + case 14: op = rv_op_th_flurd; break; + } + break; + case 7: + switch ((inst >> 27) & 0b11111) { + case 8: op = rv_op_th_fsrw; break; + case 10: op = rv_op_th_fsurw; break; + case 12: op = rv_op_th_fsrd; break; + case 14: op = rv_op_th_fsurd; break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadfmv(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 1: + switch ((inst >> 25) & 0b1111111) { + case 0b1010000: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_fmv_hw_x; + } + break; + case 0b1100000: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_th_fmv_x_hw; + } + break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadmac(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 1: + switch ((inst >> 25) & 0b1111111) { + case 0b0010000: op = rv_op_th_mula; break; + case 0b0010001: op = rv_op_th_muls; break; + case 0b0010010: op = rv_op_th_mulaw; break; + case 0b0010011: op = rv_op_th_mulsw; break; + case 0b0010100: op = rv_op_th_mulah; break; + case 0b0010101: op = rv_op_th_mulsh; break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadmemidx(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 4: + switch ((inst >> 27) & 0b11111) { + case 0: op = rv_op_th_lrb; break; + case 1: op = rv_op_th_lbib; break; + case 2: op = rv_op_th_lurb; break; + case 3: op = rv_op_th_lbia; break; + case 4: op = rv_op_th_lrh; break; + case 5: op = rv_op_th_lhib; break; + case 6: op = rv_op_th_lurh; break; + case 7: op = rv_op_th_lhia; break; + case 8: op = rv_op_th_lrw; break; + case 9: op = rv_op_th_lwib; break; + case 10: op = rv_op_th_lurw; break; + case 11: op = rv_op_th_lwia; break; + case 12: op = rv_op_th_lrd; break; + case 13: op = rv_op_th_ldib; break; + case 14: op = rv_op_th_lurd; break; + case 15: op = rv_op_th_ldia; break; + case 16: op = rv_op_th_lrbu; break; + case 17: op = rv_op_th_lbuib; break; + case 18: op = rv_op_th_lurbu; break; + case 19: op = rv_op_th_lbuia; break; + case 20: op = rv_op_th_lrhu; break; + case 21: op = rv_op_th_lhuib; break; + case 22: op = rv_op_th_lurhu; break; + case 23: op = rv_op_th_lhuia; break; + case 24: op = rv_op_th_lrwu; break; + case 25: op = rv_op_th_lwuib; break; + case 26: op = rv_op_th_lurwu; break; + case 27: op = rv_op_th_lwuia; break; + } + break; + case 5: + switch ((inst >> 27) & 0b11111) { + case 0: op = rv_op_th_srb; break; + case 1: op = rv_op_th_sbib; break; + case 2: op = rv_op_th_surb; break; + case 3: op = rv_op_th_sbia; break; + case 4: op = rv_op_th_srh; break; + case 5: op = rv_op_th_shib; break; + case 6: op = rv_op_th_surh; break; + case 7: op = rv_op_th_shia; break; + case 8: op = rv_op_th_srw; break; + case 9: op = rv_op_th_swib; break; + case 10: op = rv_op_th_surw; break; + case 11: op = rv_op_th_swia; break; + case 12: op = rv_op_th_srd; break; + case 13: op = rv_op_th_sdib; break; + case 14: op = rv_op_th_surd; break; + case 15: op = rv_op_th_sdia; break; + } + break; + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadmempair(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 4: + switch ((inst >> 27) & 0b11111) { + case 28: op = rv_op_th_lwd; break; + case 30: op = rv_op_th_lwud; break; + case 31: op = rv_op_th_ldd; break; + } + break; + case 5: + switch ((inst >> 27) & 0b11111) { + case 28: op = rv_op_th_swd; break; + case 31: op = rv_op_th_sdd; break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} + +void decode_xtheadsync(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 2: + /* custom-0 */ + switch ((inst >> 12) & 0b111) { + case 0: + switch ((inst >> 25) & 0b1111111) { + case 0b0000010: op = rv_op_th_sfence_vmas; break; + case 0b0000000: + switch ((inst >> 20) & 0b11111) { + case 0b11000: op = rv_op_th_sync; break; + case 0b11010: op = rv_op_th_sync_i; break; + case 0b11011: op = rv_op_th_sync_is; break; + case 0b11001: op = rv_op_th_sync_s; break; + } + break; + } + break; + } + break; + /* custom-0 */ + } + break; + } + + dec->op = op; +} diff --git a/disas/riscv-xthead.h b/disas/riscv-xthead.h new file mode 100644 index 0000000000..fcd42746e7 --- /dev/null +++ b/disas/riscv-xthead.h @@ -0,0 +1,28 @@ +/* + * QEMU disassembler -- RISC-V specific header (xthead*). + * + * Copyright (c) 2023 VRULL GmbH + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DISAS_RISCV_XTHEAD_H +#define DISAS_RISCV_XTHEAD_H + +#include "disas/riscv.h" + +extern const rv_opcode_data xthead_opcode_data[]; + +void decode_xtheadba(rv_decode *, rv_isa); +void decode_xtheadbb(rv_decode *, rv_isa); +void decode_xtheadbs(rv_decode *, rv_isa); +void decode_xtheadcmo(rv_decode *, rv_isa); +void decode_xtheadcondmov(rv_decode *, rv_isa); +void decode_xtheadfmemidx(rv_decode *, rv_isa); +void decode_xtheadfmv(rv_decode *, rv_isa); +void decode_xtheadmac(rv_decode *, rv_isa); +void decode_xtheadmemidx(rv_decode *, rv_isa); +void decode_xtheadmempair(rv_decode *, rv_isa); +void decode_xtheadsync(rv_decode *, rv_isa); + +#endif /* DISAS_RISCV_XTHEAD_H */ diff --git a/disas/riscv-xventana.c b/disas/riscv-xventana.c new file mode 100644 index 0000000000..cd694f15f3 --- /dev/null +++ b/disas/riscv-xventana.c @@ -0,0 +1,42 @@ +/* + * QEMU RISC-V Disassembler for xventana. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "disas/riscv.h" +#include "disas/riscv-xventana.h" + +typedef enum { + /* 0 is reserved for rv_op_illegal. */ + ventana_op_vt_maskc = 1, + ventana_op_vt_maskcn = 2, +} rv_ventana_op; + +const rv_opcode_data ventana_opcode_data[] = { + { "vt.illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, + { "vt.maskc", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "vt.maskcn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, +}; + +void decode_xventanacondops(rv_decode *dec, rv_isa isa) +{ + rv_inst inst = dec->inst; + rv_opcode op = rv_op_illegal; + + switch (((inst >> 0) & 0b11)) { + case 3: + switch (((inst >> 2) & 0b11111)) { + case 30: + switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { + case 6: op = ventana_op_vt_maskc; break; + case 7: op = ventana_op_vt_maskcn; break; + } + break; + } + break; + } + + dec->op = op; +} diff --git a/disas/riscv-xventana.h b/disas/riscv-xventana.h new file mode 100644 index 0000000000..72be9ffa16 --- /dev/null +++ b/disas/riscv-xventana.h @@ -0,0 +1,18 @@ +/* + * QEMU disassembler -- RISC-V specific header (xventana*). + * + * Copyright (c) 2023 VRULL GmbH + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DISAS_RISCV_XVENTANA_H +#define DISAS_RISCV_XVENTANA_H + +#include "disas/riscv.h" + +extern const rv_opcode_data ventana_opcode_data[]; + +void decode_xventanacondops(rv_decode*, rv_isa); + +#endif /* DISAS_RISCV_XVENTANA_H */ diff --git a/disas/riscv.c b/disas/riscv.c index 7af6afc8fa..e236c8b5b7 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -18,150 +18,17 @@ */ #include "qemu/osdep.h" +#include "qemu/bitops.h" #include "disas/dis-asm.h" +#include "target/riscv/cpu_cfg.h" +#include "disas/riscv.h" - -/* types */ - -typedef uint64_t rv_inst; -typedef uint16_t rv_opcode; - -/* enums */ +/* Vendor extensions */ +#include "disas/riscv-xthead.h" +#include "disas/riscv-xventana.h" typedef enum { - rv32, - rv64, - rv128 -} rv_isa; - -typedef enum { - rv_rm_rne = 0, - rv_rm_rtz = 1, - rv_rm_rdn = 2, - rv_rm_rup = 3, - rv_rm_rmm = 4, - rv_rm_dyn = 7, -} rv_rm; - -typedef enum { - rv_fence_i = 8, - rv_fence_o = 4, - rv_fence_r = 2, - rv_fence_w = 1, -} rv_fence; - -typedef enum { - rv_ireg_zero, - rv_ireg_ra, - rv_ireg_sp, - rv_ireg_gp, - rv_ireg_tp, - rv_ireg_t0, - rv_ireg_t1, - rv_ireg_t2, - rv_ireg_s0, - rv_ireg_s1, - rv_ireg_a0, - rv_ireg_a1, - rv_ireg_a2, - rv_ireg_a3, - rv_ireg_a4, - rv_ireg_a5, - rv_ireg_a6, - rv_ireg_a7, - rv_ireg_s2, - rv_ireg_s3, - rv_ireg_s4, - rv_ireg_s5, - rv_ireg_s6, - rv_ireg_s7, - rv_ireg_s8, - rv_ireg_s9, - rv_ireg_s10, - rv_ireg_s11, - rv_ireg_t3, - rv_ireg_t4, - rv_ireg_t5, - rv_ireg_t6, -} rv_ireg; - -typedef enum { - rvc_end, - rvc_rd_eq_ra, - rvc_rd_eq_x0, - rvc_rs1_eq_x0, - rvc_rs2_eq_x0, - rvc_rs2_eq_rs1, - rvc_rs1_eq_ra, - rvc_imm_eq_zero, - rvc_imm_eq_n1, - rvc_imm_eq_p1, - rvc_csr_eq_0x001, - rvc_csr_eq_0x002, - rvc_csr_eq_0x003, - rvc_csr_eq_0xc00, - rvc_csr_eq_0xc01, - rvc_csr_eq_0xc02, - rvc_csr_eq_0xc80, - rvc_csr_eq_0xc81, - rvc_csr_eq_0xc82, -} rvc_constraint; - -typedef enum { - rv_codec_illegal, - rv_codec_none, - rv_codec_u, - rv_codec_uj, - rv_codec_i, - rv_codec_i_sh5, - rv_codec_i_sh6, - rv_codec_i_sh7, - rv_codec_i_csr, - rv_codec_s, - rv_codec_sb, - rv_codec_r, - rv_codec_r_m, - rv_codec_r4_m, - rv_codec_r_a, - rv_codec_r_l, - rv_codec_r_f, - rv_codec_cb, - rv_codec_cb_imm, - rv_codec_cb_sh5, - rv_codec_cb_sh6, - rv_codec_ci, - rv_codec_ci_sh5, - rv_codec_ci_sh6, - rv_codec_ci_16sp, - rv_codec_ci_lwsp, - rv_codec_ci_ldsp, - rv_codec_ci_lqsp, - rv_codec_ci_li, - rv_codec_ci_lui, - rv_codec_ci_none, - rv_codec_ciw_4spn, - rv_codec_cj, - rv_codec_cj_jal, - rv_codec_cl_lw, - rv_codec_cl_ld, - rv_codec_cl_lq, - rv_codec_cr, - rv_codec_cr_mv, - rv_codec_cr_jalr, - rv_codec_cr_jr, - rv_codec_cs, - rv_codec_cs_sw, - rv_codec_cs_sd, - rv_codec_cs_sq, - rv_codec_css_swsp, - rv_codec_css_sdsp, - rv_codec_css_sqsp, - rv_codec_k_bs, - rv_codec_k_rnum, -} rv_codec; - -typedef enum { - rv_op_illegal = 0, + /* 0 is reserved for rv_op_illegal. */ rv_op_lui = 1, rv_op_auipc = 2, rv_op_jal = 3, @@ -560,49 +427,487 @@ typedef enum { rv_op_zip = 396, rv_op_xperm4 = 397, rv_op_xperm8 = 398, + rv_op_vle8_v = 399, + rv_op_vle16_v = 400, + rv_op_vle32_v = 401, + rv_op_vle64_v = 402, + rv_op_vse8_v = 403, + rv_op_vse16_v = 404, + rv_op_vse32_v = 405, + rv_op_vse64_v = 406, + rv_op_vlm_v = 407, + rv_op_vsm_v = 408, + rv_op_vlse8_v = 409, + rv_op_vlse16_v = 410, + rv_op_vlse32_v = 411, + rv_op_vlse64_v = 412, + rv_op_vsse8_v = 413, + rv_op_vsse16_v = 414, + rv_op_vsse32_v = 415, + rv_op_vsse64_v = 416, + rv_op_vluxei8_v = 417, + rv_op_vluxei16_v = 418, + rv_op_vluxei32_v = 419, + rv_op_vluxei64_v = 420, + rv_op_vloxei8_v = 421, + rv_op_vloxei16_v = 422, + rv_op_vloxei32_v = 423, + rv_op_vloxei64_v = 424, + rv_op_vsuxei8_v = 425, + rv_op_vsuxei16_v = 426, + rv_op_vsuxei32_v = 427, + rv_op_vsuxei64_v = 428, + rv_op_vsoxei8_v = 429, + rv_op_vsoxei16_v = 430, + rv_op_vsoxei32_v = 431, + rv_op_vsoxei64_v = 432, + rv_op_vle8ff_v = 433, + rv_op_vle16ff_v = 434, + rv_op_vle32ff_v = 435, + rv_op_vle64ff_v = 436, + rv_op_vl1re8_v = 437, + rv_op_vl1re16_v = 438, + rv_op_vl1re32_v = 439, + rv_op_vl1re64_v = 440, + rv_op_vl2re8_v = 441, + rv_op_vl2re16_v = 442, + rv_op_vl2re32_v = 443, + rv_op_vl2re64_v = 444, + rv_op_vl4re8_v = 445, + rv_op_vl4re16_v = 446, + rv_op_vl4re32_v = 447, + rv_op_vl4re64_v = 448, + rv_op_vl8re8_v = 449, + rv_op_vl8re16_v = 450, + rv_op_vl8re32_v = 451, + rv_op_vl8re64_v = 452, + rv_op_vs1r_v = 453, + rv_op_vs2r_v = 454, + rv_op_vs4r_v = 455, + rv_op_vs8r_v = 456, + rv_op_vadd_vv = 457, + rv_op_vadd_vx = 458, + rv_op_vadd_vi = 459, + rv_op_vsub_vv = 460, + rv_op_vsub_vx = 461, + rv_op_vrsub_vx = 462, + rv_op_vrsub_vi = 463, + rv_op_vwaddu_vv = 464, + rv_op_vwaddu_vx = 465, + rv_op_vwadd_vv = 466, + rv_op_vwadd_vx = 467, + rv_op_vwsubu_vv = 468, + rv_op_vwsubu_vx = 469, + rv_op_vwsub_vv = 470, + rv_op_vwsub_vx = 471, + rv_op_vwaddu_wv = 472, + rv_op_vwaddu_wx = 473, + rv_op_vwadd_wv = 474, + rv_op_vwadd_wx = 475, + rv_op_vwsubu_wv = 476, + rv_op_vwsubu_wx = 477, + rv_op_vwsub_wv = 478, + rv_op_vwsub_wx = 479, + rv_op_vadc_vvm = 480, + rv_op_vadc_vxm = 481, + rv_op_vadc_vim = 482, + rv_op_vmadc_vvm = 483, + rv_op_vmadc_vxm = 484, + rv_op_vmadc_vim = 485, + rv_op_vsbc_vvm = 486, + rv_op_vsbc_vxm = 487, + rv_op_vmsbc_vvm = 488, + rv_op_vmsbc_vxm = 489, + rv_op_vand_vv = 490, + rv_op_vand_vx = 491, + rv_op_vand_vi = 492, + rv_op_vor_vv = 493, + rv_op_vor_vx = 494, + rv_op_vor_vi = 495, + rv_op_vxor_vv = 496, + rv_op_vxor_vx = 497, + rv_op_vxor_vi = 498, + rv_op_vsll_vv = 499, + rv_op_vsll_vx = 500, + rv_op_vsll_vi = 501, + rv_op_vsrl_vv = 502, + rv_op_vsrl_vx = 503, + rv_op_vsrl_vi = 504, + rv_op_vsra_vv = 505, + rv_op_vsra_vx = 506, + rv_op_vsra_vi = 507, + rv_op_vnsrl_wv = 508, + rv_op_vnsrl_wx = 509, + rv_op_vnsrl_wi = 510, + rv_op_vnsra_wv = 511, + rv_op_vnsra_wx = 512, + rv_op_vnsra_wi = 513, + rv_op_vmseq_vv = 514, + rv_op_vmseq_vx = 515, + rv_op_vmseq_vi = 516, + rv_op_vmsne_vv = 517, + rv_op_vmsne_vx = 518, + rv_op_vmsne_vi = 519, + rv_op_vmsltu_vv = 520, + rv_op_vmsltu_vx = 521, + rv_op_vmslt_vv = 522, + rv_op_vmslt_vx = 523, + rv_op_vmsleu_vv = 524, + rv_op_vmsleu_vx = 525, + rv_op_vmsleu_vi = 526, + rv_op_vmsle_vv = 527, + rv_op_vmsle_vx = 528, + rv_op_vmsle_vi = 529, + rv_op_vmsgtu_vx = 530, + rv_op_vmsgtu_vi = 531, + rv_op_vmsgt_vx = 532, + rv_op_vmsgt_vi = 533, + rv_op_vminu_vv = 534, + rv_op_vminu_vx = 535, + rv_op_vmin_vv = 536, + rv_op_vmin_vx = 537, + rv_op_vmaxu_vv = 538, + rv_op_vmaxu_vx = 539, + rv_op_vmax_vv = 540, + rv_op_vmax_vx = 541, + rv_op_vmul_vv = 542, + rv_op_vmul_vx = 543, + rv_op_vmulh_vv = 544, + rv_op_vmulh_vx = 545, + rv_op_vmulhu_vv = 546, + rv_op_vmulhu_vx = 547, + rv_op_vmulhsu_vv = 548, + rv_op_vmulhsu_vx = 549, + rv_op_vdivu_vv = 550, + rv_op_vdivu_vx = 551, + rv_op_vdiv_vv = 552, + rv_op_vdiv_vx = 553, + rv_op_vremu_vv = 554, + rv_op_vremu_vx = 555, + rv_op_vrem_vv = 556, + rv_op_vrem_vx = 557, + rv_op_vwmulu_vv = 558, + rv_op_vwmulu_vx = 559, + rv_op_vwmulsu_vv = 560, + rv_op_vwmulsu_vx = 561, + rv_op_vwmul_vv = 562, + rv_op_vwmul_vx = 563, + rv_op_vmacc_vv = 564, + rv_op_vmacc_vx = 565, + rv_op_vnmsac_vv = 566, + rv_op_vnmsac_vx = 567, + rv_op_vmadd_vv = 568, + rv_op_vmadd_vx = 569, + rv_op_vnmsub_vv = 570, + rv_op_vnmsub_vx = 571, + rv_op_vwmaccu_vv = 572, + rv_op_vwmaccu_vx = 573, + rv_op_vwmacc_vv = 574, + rv_op_vwmacc_vx = 575, + rv_op_vwmaccsu_vv = 576, + rv_op_vwmaccsu_vx = 577, + rv_op_vwmaccus_vx = 578, + rv_op_vmv_v_v = 579, + rv_op_vmv_v_x = 580, + rv_op_vmv_v_i = 581, + rv_op_vmerge_vvm = 582, + rv_op_vmerge_vxm = 583, + rv_op_vmerge_vim = 584, + rv_op_vsaddu_vv = 585, + rv_op_vsaddu_vx = 586, + rv_op_vsaddu_vi = 587, + rv_op_vsadd_vv = 588, + rv_op_vsadd_vx = 589, + rv_op_vsadd_vi = 590, + rv_op_vssubu_vv = 591, + rv_op_vssubu_vx = 592, + rv_op_vssub_vv = 593, + rv_op_vssub_vx = 594, + rv_op_vaadd_vv = 595, + rv_op_vaadd_vx = 596, + rv_op_vaaddu_vv = 597, + rv_op_vaaddu_vx = 598, + rv_op_vasub_vv = 599, + rv_op_vasub_vx = 600, + rv_op_vasubu_vv = 601, + rv_op_vasubu_vx = 602, + rv_op_vsmul_vv = 603, + rv_op_vsmul_vx = 604, + rv_op_vssrl_vv = 605, + rv_op_vssrl_vx = 606, + rv_op_vssrl_vi = 607, + rv_op_vssra_vv = 608, + rv_op_vssra_vx = 609, + rv_op_vssra_vi = 610, + rv_op_vnclipu_wv = 611, + rv_op_vnclipu_wx = 612, + rv_op_vnclipu_wi = 613, + rv_op_vnclip_wv = 614, + rv_op_vnclip_wx = 615, + rv_op_vnclip_wi = 616, + rv_op_vfadd_vv = 617, + rv_op_vfadd_vf = 618, + rv_op_vfsub_vv = 619, + rv_op_vfsub_vf = 620, + rv_op_vfrsub_vf = 621, + rv_op_vfwadd_vv = 622, + rv_op_vfwadd_vf = 623, + rv_op_vfwadd_wv = 624, + rv_op_vfwadd_wf = 625, + rv_op_vfwsub_vv = 626, + rv_op_vfwsub_vf = 627, + rv_op_vfwsub_wv = 628, + rv_op_vfwsub_wf = 629, + rv_op_vfmul_vv = 630, + rv_op_vfmul_vf = 631, + rv_op_vfdiv_vv = 632, + rv_op_vfdiv_vf = 633, + rv_op_vfrdiv_vf = 634, + rv_op_vfwmul_vv = 635, + rv_op_vfwmul_vf = 636, + rv_op_vfmacc_vv = 637, + rv_op_vfmacc_vf = 638, + rv_op_vfnmacc_vv = 639, + rv_op_vfnmacc_vf = 640, + rv_op_vfmsac_vv = 641, + rv_op_vfmsac_vf = 642, + rv_op_vfnmsac_vv = 643, + rv_op_vfnmsac_vf = 644, + rv_op_vfmadd_vv = 645, + rv_op_vfmadd_vf = 646, + rv_op_vfnmadd_vv = 647, + rv_op_vfnmadd_vf = 648, + rv_op_vfmsub_vv = 649, + rv_op_vfmsub_vf = 650, + rv_op_vfnmsub_vv = 651, + rv_op_vfnmsub_vf = 652, + rv_op_vfwmacc_vv = 653, + rv_op_vfwmacc_vf = 654, + rv_op_vfwnmacc_vv = 655, + rv_op_vfwnmacc_vf = 656, + rv_op_vfwmsac_vv = 657, + rv_op_vfwmsac_vf = 658, + rv_op_vfwnmsac_vv = 659, + rv_op_vfwnmsac_vf = 660, + rv_op_vfsqrt_v = 661, + rv_op_vfrsqrt7_v = 662, + rv_op_vfrec7_v = 663, + rv_op_vfmin_vv = 664, + rv_op_vfmin_vf = 665, + rv_op_vfmax_vv = 666, + rv_op_vfmax_vf = 667, + rv_op_vfsgnj_vv = 668, + rv_op_vfsgnj_vf = 669, + rv_op_vfsgnjn_vv = 670, + rv_op_vfsgnjn_vf = 671, + rv_op_vfsgnjx_vv = 672, + rv_op_vfsgnjx_vf = 673, + rv_op_vfslide1up_vf = 674, + rv_op_vfslide1down_vf = 675, + rv_op_vmfeq_vv = 676, + rv_op_vmfeq_vf = 677, + rv_op_vmfne_vv = 678, + rv_op_vmfne_vf = 679, + rv_op_vmflt_vv = 680, + rv_op_vmflt_vf = 681, + rv_op_vmfle_vv = 682, + rv_op_vmfle_vf = 683, + rv_op_vmfgt_vf = 684, + rv_op_vmfge_vf = 685, + rv_op_vfclass_v = 686, + rv_op_vfmerge_vfm = 687, + rv_op_vfmv_v_f = 688, + rv_op_vfcvt_xu_f_v = 689, + rv_op_vfcvt_x_f_v = 690, + rv_op_vfcvt_f_xu_v = 691, + rv_op_vfcvt_f_x_v = 692, + rv_op_vfcvt_rtz_xu_f_v = 693, + rv_op_vfcvt_rtz_x_f_v = 694, + rv_op_vfwcvt_xu_f_v = 695, + rv_op_vfwcvt_x_f_v = 696, + rv_op_vfwcvt_f_xu_v = 697, + rv_op_vfwcvt_f_x_v = 698, + rv_op_vfwcvt_f_f_v = 699, + rv_op_vfwcvt_rtz_xu_f_v = 700, + rv_op_vfwcvt_rtz_x_f_v = 701, + rv_op_vfncvt_xu_f_w = 702, + rv_op_vfncvt_x_f_w = 703, + rv_op_vfncvt_f_xu_w = 704, + rv_op_vfncvt_f_x_w = 705, + rv_op_vfncvt_f_f_w = 706, + rv_op_vfncvt_rod_f_f_w = 707, + rv_op_vfncvt_rtz_xu_f_w = 708, + rv_op_vfncvt_rtz_x_f_w = 709, + rv_op_vredsum_vs = 710, + rv_op_vredand_vs = 711, + rv_op_vredor_vs = 712, + rv_op_vredxor_vs = 713, + rv_op_vredminu_vs = 714, + rv_op_vredmin_vs = 715, + rv_op_vredmaxu_vs = 716, + rv_op_vredmax_vs = 717, + rv_op_vwredsumu_vs = 718, + rv_op_vwredsum_vs = 719, + rv_op_vfredusum_vs = 720, + rv_op_vfredosum_vs = 721, + rv_op_vfredmin_vs = 722, + rv_op_vfredmax_vs = 723, + rv_op_vfwredusum_vs = 724, + rv_op_vfwredosum_vs = 725, + rv_op_vmand_mm = 726, + rv_op_vmnand_mm = 727, + rv_op_vmandn_mm = 728, + rv_op_vmxor_mm = 729, + rv_op_vmor_mm = 730, + rv_op_vmnor_mm = 731, + rv_op_vmorn_mm = 732, + rv_op_vmxnor_mm = 733, + rv_op_vcpop_m = 734, + rv_op_vfirst_m = 735, + rv_op_vmsbf_m = 736, + rv_op_vmsif_m = 737, + rv_op_vmsof_m = 738, + rv_op_viota_m = 739, + rv_op_vid_v = 740, + rv_op_vmv_x_s = 741, + rv_op_vmv_s_x = 742, + rv_op_vfmv_f_s = 743, + rv_op_vfmv_s_f = 744, + rv_op_vslideup_vx = 745, + rv_op_vslideup_vi = 746, + rv_op_vslide1up_vx = 747, + rv_op_vslidedown_vx = 748, + rv_op_vslidedown_vi = 749, + rv_op_vslide1down_vx = 750, + rv_op_vrgather_vv = 751, + rv_op_vrgatherei16_vv = 752, + rv_op_vrgather_vx = 753, + rv_op_vrgather_vi = 754, + rv_op_vcompress_vm = 755, + rv_op_vmv1r_v = 756, + rv_op_vmv2r_v = 757, + rv_op_vmv4r_v = 758, + rv_op_vmv8r_v = 759, + rv_op_vzext_vf2 = 760, + rv_op_vzext_vf4 = 761, + rv_op_vzext_vf8 = 762, + rv_op_vsext_vf2 = 763, + rv_op_vsext_vf4 = 764, + rv_op_vsext_vf8 = 765, + rv_op_vsetvli = 766, + rv_op_vsetivli = 767, + rv_op_vsetvl = 768, + rv_op_c_zext_b = 769, + rv_op_c_sext_b = 770, + rv_op_c_zext_h = 771, + rv_op_c_sext_h = 772, + rv_op_c_zext_w = 773, + rv_op_c_not = 774, + rv_op_c_mul = 775, + rv_op_c_lbu = 776, + rv_op_c_lhu = 777, + rv_op_c_lh = 778, + rv_op_c_sb = 779, + rv_op_c_sh = 780, + rv_op_cm_push = 781, + rv_op_cm_pop = 782, + rv_op_cm_popret = 783, + rv_op_cm_popretz = 784, + rv_op_cm_mva01s = 785, + rv_op_cm_mvsa01 = 786, + rv_op_cm_jt = 787, + rv_op_cm_jalt = 788, + rv_op_czero_eqz = 789, + rv_op_czero_nez = 790, + rv_op_fcvt_bf16_s = 791, + rv_op_fcvt_s_bf16 = 792, + rv_op_vfncvtbf16_f_f_w = 793, + rv_op_vfwcvtbf16_f_f_v = 794, + rv_op_vfwmaccbf16_vv = 795, + rv_op_vfwmaccbf16_vf = 796, + rv_op_flh = 797, + rv_op_fsh = 798, + rv_op_fmv_h_x = 799, + rv_op_fmv_x_h = 800, + rv_op_fli_s = 801, + rv_op_fli_d = 802, + rv_op_fli_q = 803, + rv_op_fli_h = 804, + rv_op_fminm_s = 805, + rv_op_fmaxm_s = 806, + rv_op_fminm_d = 807, + rv_op_fmaxm_d = 808, + rv_op_fminm_q = 809, + rv_op_fmaxm_q = 810, + rv_op_fminm_h = 811, + rv_op_fmaxm_h = 812, + rv_op_fround_s = 813, + rv_op_froundnx_s = 814, + rv_op_fround_d = 815, + rv_op_froundnx_d = 816, + rv_op_fround_q = 817, + rv_op_froundnx_q = 818, + rv_op_fround_h = 819, + rv_op_froundnx_h = 820, + rv_op_fcvtmod_w_d = 821, + rv_op_fmvh_x_d = 822, + rv_op_fmvp_d_x = 823, + rv_op_fmvh_x_q = 824, + rv_op_fmvp_q_x = 825, + rv_op_fleq_s = 826, + rv_op_fltq_s = 827, + rv_op_fleq_d = 828, + rv_op_fltq_d = 829, + rv_op_fleq_q = 830, + rv_op_fltq_q = 831, + rv_op_fleq_h = 832, + rv_op_fltq_h = 833, + rv_op_vaesdf_vv = 834, + rv_op_vaesdf_vs = 835, + rv_op_vaesdm_vv = 836, + rv_op_vaesdm_vs = 837, + rv_op_vaesef_vv = 838, + rv_op_vaesef_vs = 839, + rv_op_vaesem_vv = 840, + rv_op_vaesem_vs = 841, + rv_op_vaeskf1_vi = 842, + rv_op_vaeskf2_vi = 843, + rv_op_vaesz_vs = 844, + rv_op_vandn_vv = 845, + rv_op_vandn_vx = 846, + rv_op_vbrev_v = 847, + rv_op_vbrev8_v = 848, + rv_op_vclmul_vv = 849, + rv_op_vclmul_vx = 850, + rv_op_vclmulh_vv = 851, + rv_op_vclmulh_vx = 852, + rv_op_vclz_v = 853, + rv_op_vcpop_v = 854, + rv_op_vctz_v = 855, + rv_op_vghsh_vv = 856, + rv_op_vgmul_vv = 857, + rv_op_vrev8_v = 858, + rv_op_vrol_vv = 859, + rv_op_vrol_vx = 860, + rv_op_vror_vv = 861, + rv_op_vror_vx = 862, + rv_op_vror_vi = 863, + rv_op_vsha2ch_vv = 864, + rv_op_vsha2cl_vv = 865, + rv_op_vsha2ms_vv = 866, + rv_op_vsm3c_vi = 867, + rv_op_vsm3me_vv = 868, + rv_op_vsm4k_vi = 869, + rv_op_vsm4r_vv = 870, + rv_op_vsm4r_vs = 871, + rv_op_vwsll_vv = 872, + rv_op_vwsll_vx = 873, + rv_op_vwsll_vi = 874, + rv_op_amocas_w = 875, + rv_op_amocas_d = 876, + rv_op_amocas_q = 877, } rv_op; -/* structures */ - -typedef struct { - uint64_t pc; - uint64_t inst; - int32_t imm; - uint16_t op; - uint8_t codec; - uint8_t rd; - uint8_t rs1; - uint8_t rs2; - uint8_t rs3; - uint8_t rm; - uint8_t pred; - uint8_t succ; - uint8_t aq; - uint8_t rl; - uint8_t bs; - uint8_t rnum; -} rv_decode; - -typedef struct { - const int op; - const rvc_constraint *constraints; -} rv_comp_data; - -enum { - rvcd_imm_nz = 0x1 -}; - -typedef struct { - const char * const name; - const rv_codec codec; - const char * const format; - const rv_comp_data *pseudo; - const short decomp_rv32; - const short decomp_rv64; - const short decomp_rv128; - const short decomp_data; -} rv_opcode_data; - /* register names */ static const char rv_ireg_name_sym[32][5] = { @@ -619,51 +924,37 @@ static const char rv_freg_name_sym[32][5] = { "fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11", }; -/* instruction formats */ +static const char rv_vreg_name_sym[32][4] = { + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", + "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", + "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", + "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31" +}; -#define rv_fmt_none "O\t" -#define rv_fmt_rs1 "O\t1" -#define rv_fmt_offset "O\to" -#define rv_fmt_pred_succ "O\tp,s" -#define rv_fmt_rs1_rs2 "O\t1,2" -#define rv_fmt_rd_imm "O\t0,i" -#define rv_fmt_rd_offset "O\t0,o" -#define rv_fmt_rd_rs1_rs2 "O\t0,1,2" -#define rv_fmt_frd_rs1 "O\t3,1" -#define rv_fmt_rd_frs1 "O\t0,4" -#define rv_fmt_rd_frs1_frs2 "O\t0,4,5" -#define rv_fmt_frd_frs1_frs2 "O\t3,4,5" -#define rv_fmt_rm_frd_frs1 "O\tr,3,4" -#define rv_fmt_rm_frd_rs1 "O\tr,3,1" -#define rv_fmt_rm_rd_frs1 "O\tr,0,4" -#define rv_fmt_rm_frd_frs1_frs2 "O\tr,3,4,5" -#define rv_fmt_rm_frd_frs1_frs2_frs3 "O\tr,3,4,5,6" -#define rv_fmt_rd_rs1_imm "O\t0,1,i" -#define rv_fmt_rd_rs1_offset "O\t0,1,i" -#define rv_fmt_rd_offset_rs1 "O\t0,i(1)" -#define rv_fmt_frd_offset_rs1 "O\t3,i(1)" -#define rv_fmt_rd_csr_rs1 "O\t0,c,1" -#define rv_fmt_rd_csr_zimm "O\t0,c,7" -#define rv_fmt_rs2_offset_rs1 "O\t2,i(1)" -#define rv_fmt_frs2_offset_rs1 "O\t5,i(1)" -#define rv_fmt_rs1_rs2_offset "O\t1,2,o" -#define rv_fmt_rs2_rs1_offset "O\t2,1,o" -#define rv_fmt_aqrl_rd_rs2_rs1 "OAR\t0,2,(1)" -#define rv_fmt_aqrl_rd_rs1 "OAR\t0,(1)" -#define rv_fmt_rd "O\t0" -#define rv_fmt_rd_zimm "O\t0,7" -#define rv_fmt_rd_rs1 "O\t0,1" -#define rv_fmt_rd_rs2 "O\t0,2" -#define rv_fmt_rs1_offset "O\t1,o" -#define rv_fmt_rs2_offset "O\t2,o" -#define rv_fmt_rs1_rs2_bs "O\t1,2,b" -#define rv_fmt_rd_rs1_rnum "O\t0,1,n" +/* The FLI.[HSDQ] numeric constants (0.0 for symbolic constants). + * The constants use the hex floating-point literal representation + * that is printed when using the printf %a format specifier, + * which matches the output that is generated by the disassembler. + */ +static const char rv_fli_name_const[32][9] = +{ + "0x1p+0", "min", "0x1p-16", "0x1p-15", + "0x1p-8", "0x1p-7", "0x1p-4", "0x1p-3", + "0x1p-2", "0x1.4p-2", "0x1.8p-2", "0x1.cp-2", + "0x1p-1", "0x1.4p-1", "0x1.8p-1", "0x1.cp-1", + "0x1p+0", "0x1.4p+0", "0x1.8p+0", "0x1.cp+0", + "0x1p+1", "0x1.4p+1", "0x1.8p+1", "0x1p+2", + "0x1p+3", "0x1p+4", "0x1p+7", "0x1p+8", + "0x1p+15", "0x1p+16", "inf", "nan" +}; /* pseudo-instruction constraints */ static const rvc_constraint rvcc_jal[] = { rvc_rd_eq_ra, rvc_end }; -static const rvc_constraint rvcc_jalr[] = { rvc_rd_eq_ra, rvc_imm_eq_zero, rvc_end }; -static const rvc_constraint rvcc_nop[] = { rvc_rd_eq_x0, rvc_rs1_eq_x0, rvc_imm_eq_zero, rvc_end }; +static const rvc_constraint rvcc_jalr[] = { rvc_rd_eq_ra, rvc_imm_eq_zero, + rvc_end }; +static const rvc_constraint rvcc_nop[] = { rvc_rd_eq_x0, rvc_rs1_eq_x0, + rvc_imm_eq_zero, rvc_end }; static const rvc_constraint rvcc_mv[] = { rvc_imm_eq_zero, rvc_end }; static const rvc_constraint rvcc_not[] = { rvc_imm_eq_n1, rvc_end }; static const rvc_constraint rvcc_neg[] = { rvc_rs1_eq_x0, rvc_end }; @@ -693,18 +984,28 @@ static const rvc_constraint rvcc_bleu[] = { rvc_end }; static const rvc_constraint rvcc_bgt[] = { rvc_end }; static const rvc_constraint rvcc_bgtu[] = { rvc_end }; static const rvc_constraint rvcc_j[] = { rvc_rd_eq_x0, rvc_end }; -static const rvc_constraint rvcc_ret[] = { rvc_rd_eq_x0, rvc_rs1_eq_ra, rvc_end }; -static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, rvc_end }; -static const rvc_constraint rvcc_rdcycle[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc00, rvc_end }; -static const rvc_constraint rvcc_rdtime[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc01, rvc_end }; -static const rvc_constraint rvcc_rdinstret[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc02, rvc_end }; -static const rvc_constraint rvcc_rdcycleh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end }; -static const rvc_constraint rvcc_rdtimeh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc81, rvc_end }; +static const rvc_constraint rvcc_ret[] = { rvc_rd_eq_x0, rvc_rs1_eq_ra, + rvc_end }; +static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, + rvc_end }; +static const rvc_constraint rvcc_rdcycle[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc00, + rvc_end }; +static const rvc_constraint rvcc_rdtime[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc01, + rvc_end }; +static const rvc_constraint rvcc_rdinstret[] = { rvc_rs1_eq_x0, + rvc_csr_eq_0xc02, rvc_end }; +static const rvc_constraint rvcc_rdcycleh[] = { rvc_rs1_eq_x0, + rvc_csr_eq_0xc80, rvc_end }; +static const rvc_constraint rvcc_rdtimeh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc81, + rvc_end }; static const rvc_constraint rvcc_rdinstreth[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc82, rvc_end }; -static const rvc_constraint rvcc_frcsr[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x003, rvc_end }; -static const rvc_constraint rvcc_frrm[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x002, rvc_end }; -static const rvc_constraint rvcc_frflags[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x001, rvc_end }; +static const rvc_constraint rvcc_frcsr[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x003, + rvc_end }; +static const rvc_constraint rvcc_frrm[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x002, + rvc_end }; +static const rvc_constraint rvcc_frflags[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x001, + rvc_end }; static const rvc_constraint rvcc_fscsr[] = { rvc_csr_eq_0x003, rvc_end }; static const rvc_constraint rvcc_fsrm[] = { rvc_csr_eq_0x002, rvc_end }; static const rvc_constraint rvcc_fsflags[] = { rvc_csr_eq_0x001, rvc_end }; @@ -876,10 +1177,10 @@ static const rv_comp_data rvcp_fsgnjx_q[] = { /* instruction metadata */ -const rv_opcode_data opcode_data[] = { +const rv_opcode_data rvi_opcode_data[] = { { "illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, - { "lui", rv_codec_u, rv_fmt_rd_imm, NULL, 0, 0, 0 }, - { "auipc", rv_codec_u, rv_fmt_rd_offset, NULL, 0, 0, 0 }, + { "lui", rv_codec_u, rv_fmt_rd_uimm, NULL, 0, 0, 0 }, + { "auipc", rv_codec_u, rv_fmt_rd_uoffset, NULL, 0, 0, 0 }, { "jal", rv_codec_uj, rv_fmt_rd_offset, rvcp_jal, 0, 0, 0 }, { "jalr", rv_codec_i, rv_fmt_rd_rs1_offset, rvcp_jalr, 0, 0, 0 }, { "beq", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_beq, 0, 0, 0 }, @@ -1106,20 +1407,26 @@ const rv_opcode_data opcode_data[] = { { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi, rvcd_imm_nz }, - { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, 0 }, - { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw }, + { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, + rv_op_fld, 0 }, + { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, + rv_op_lw }, { "c.flw", rv_codec_cl_lw, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 }, - { "c.fsd", rv_codec_cs_sd, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, rv_op_fsd, 0 }, - { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw }, + { "c.fsd", rv_codec_cs_sd, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, + rv_op_fsd, 0 }, + { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, + rv_op_sw }, { "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 }, - { "c.nop", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, + { "c.nop", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_addi, rv_op_addi, + rv_op_addi }, { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi, rvcd_imm_nz }, { "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, NULL, rv_op_jal, 0, 0 }, - { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, + { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, + rv_op_addi }, { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi, rvcd_imm_nz }, - { "c.lui", rv_codec_ci_lui, rv_fmt_rd_imm, NULL, rv_op_lui, rv_op_lui, + { "c.lui", rv_codec_ci_lui, rv_fmt_rd_uimm, NULL, rv_op_lui, rv_op_lui, rv_op_lui, rvcd_imm_nz }, { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli, rv_op_srli, rv_op_srli, rvcd_imm_nz }, @@ -1127,37 +1434,63 @@ const rv_opcode_data opcode_data[] = { rv_op_srai, rv_op_srai, rvcd_imm_nz }, { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, rv_op_andi, rv_op_andi }, - { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, rv_op_sub }, - { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor, rv_op_xor, rv_op_xor }, - { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or, rv_op_or, rv_op_or }, - { "c.and", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_and, rv_op_and, rv_op_and }, - { "c.subw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_subw, rv_op_subw, rv_op_subw }, - { "c.addw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_addw, rv_op_addw, rv_op_addw }, - { "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal, rv_op_jal, rv_op_jal }, - { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq, rv_op_beq, rv_op_beq }, - { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne, rv_op_bne, rv_op_bne }, + { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, + rv_op_sub }, + { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor, rv_op_xor, + rv_op_xor }, + { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or, rv_op_or, + rv_op_or }, + { "c.and", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_and, rv_op_and, + rv_op_and }, + { "c.subw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_subw, rv_op_subw, + rv_op_subw }, + { "c.addw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_addw, rv_op_addw, + rv_op_addw }, + { "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal, rv_op_jal, + rv_op_jal }, + { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq, rv_op_beq, + rv_op_beq }, + { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne, rv_op_bne, + rv_op_bne }, { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli, rv_op_slli, rv_op_slli, rvcd_imm_nz }, - { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, rv_op_fld }, - { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw }, - { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 }, - { "c.jr", rv_codec_cr_jr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, rv_op_jalr, rv_op_jalr }, - { "c.mv", rv_codec_cr_mv, rv_fmt_rd_rs1_rs2, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, - { "c.ebreak", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_ebreak, rv_op_ebreak, rv_op_ebreak }, - { "c.jalr", rv_codec_cr_jalr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, rv_op_jalr, rv_op_jalr }, - { "c.add", rv_codec_cr, rv_fmt_rd_rs1_rs2, NULL, rv_op_add, rv_op_add, rv_op_add }, - { "c.fsdsp", rv_codec_css_sdsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, rv_op_fsd, rv_op_fsd }, - { "c.swsp", rv_codec_css_swsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw }, - { "c.fswsp", rv_codec_css_swsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 }, - { "c.ld", rv_codec_cl_ld, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, rv_op_ld }, - { "c.sd", rv_codec_cs_sd, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, rv_op_sd }, - { "c.addiw", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, 0, rv_op_addiw, rv_op_addiw }, - { "c.ldsp", rv_codec_ci_ldsp, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, rv_op_ld }, - { "c.sdsp", rv_codec_css_sdsp, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, rv_op_sd }, + { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, + rv_op_fld, rv_op_fld }, + { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, + rv_op_lw, rv_op_lw }, + { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, + 0 }, + { "c.jr", rv_codec_cr_jr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, + rv_op_jalr, rv_op_jalr }, + { "c.mv", rv_codec_cr_mv, rv_fmt_rd_rs1_rs2, NULL, rv_op_addi, rv_op_addi, + rv_op_addi }, + { "c.ebreak", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_ebreak, + rv_op_ebreak, rv_op_ebreak }, + { "c.jalr", rv_codec_cr_jalr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, + rv_op_jalr, rv_op_jalr }, + { "c.add", rv_codec_cr, rv_fmt_rd_rs1_rs2, NULL, rv_op_add, rv_op_add, + rv_op_add }, + { "c.fsdsp", rv_codec_css_sdsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, + rv_op_fsd, rv_op_fsd }, + { "c.swsp", rv_codec_css_swsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, + rv_op_sw, rv_op_sw }, + { "c.fswsp", rv_codec_css_swsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, + 0 }, + { "c.ld", rv_codec_cl_ld, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, + rv_op_ld }, + { "c.sd", rv_codec_cs_sd, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, + rv_op_sd }, + { "c.addiw", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, 0, rv_op_addiw, + rv_op_addiw }, + { "c.ldsp", rv_codec_ci_ldsp, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, + rv_op_ld }, + { "c.sdsp", rv_codec_css_sdsp, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, + rv_op_sd }, { "c.lq", rv_codec_cl_lq, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq }, { "c.sq", rv_codec_cs_sq, rv_fmt_rs2_offset_rs1, NULL, 0, 0, rv_op_sq }, { "c.lqsp", rv_codec_ci_lqsp, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq }, - { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, NULL, 0, 0, rv_op_sq }, + { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, NULL, 0, 0, + rv_op_sq }, { "nop", rv_codec_i, rv_fmt_none, NULL, 0, 0, 0 }, { "mv", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, { "not", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, @@ -1168,15 +1501,15 @@ const rv_opcode_data opcode_data[] = { { "snez", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, { "sltz", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, { "sgtz", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, - { "fmv.s", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "fabs.s", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "fneg.s", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "fmv.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "fabs.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "fneg.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "fmv.q", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "fabs.q", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "fneg.q", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "fmv.s", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, + { "fabs.s", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, + { "fneg.s", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, + { "fmv.d", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, + { "fabs.d", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, + { "fneg.d", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, + { "fmv.q", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, + { "fabs.q", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, + { "fneg.q", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, { "beqz", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 }, { "bnez", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 }, { "blez", rv_codec_sb, rv_fmt_rs2_offset, NULL, 0, 0, 0 }, @@ -1214,9 +1547,9 @@ const rv_opcode_data opcode_data[] = { { "cpop", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, { "sext.h", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, { "sext.b", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "xnor", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "orn", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "andn", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "xnor", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "orn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "andn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "rol", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "ror", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "sh1add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, @@ -1233,9 +1566,9 @@ const rv_opcode_data opcode_data[] = { { "max", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "maxu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "clzw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "clzw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "ctzw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, { "cpopw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "slli.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "slli.uw", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, { "add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "rolw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "rorw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, @@ -1283,7 +1616,486 @@ const rv_opcode_data opcode_data[] = { { "unzip", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, { "zip", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, { "xperm4", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "xperm8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 } + { "xperm8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "vle8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vle16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vle32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vle64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vse8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vse16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vse32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vse64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vlm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vsm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vlse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vlse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vlse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vlse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vsse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vsse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vsse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vsse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, + { "vluxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vluxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vluxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vluxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vloxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vloxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vloxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vloxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsuxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsuxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsuxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsuxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsoxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsoxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsoxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vsoxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vle8ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vle16ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vle32ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vle64ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl1re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl1re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl1re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl1re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl2re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl2re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl2re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl2re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl4re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl4re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl4re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl4re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl8re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl8re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl8re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vl8re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vs1r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vs2r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vs4r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vs8r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, + { "vadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vrsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vrsub.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vwaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwsubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwsubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwaddu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwaddu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwadd.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwsubu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwsubu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwsub.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, + { "vadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, + { "vadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, 0, 0, 0 }, + { "vmadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, + { "vmadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, + { "vmadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, 0, 0, 0 }, + { "vsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, + { "vsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, + { "vmsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, + { "vmsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, + { "vand.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vand.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vand.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vxor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vxor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vxor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vsll.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vsrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vsrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vsrl.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vsra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vsra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vsra.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnsrl.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vnsrl.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vnsrl.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnsra.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vnsra.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vnsra.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vmseq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmseq.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmseq.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vmsne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmsne.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmsne.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vmsltu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmsltu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmslt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmslt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmsleu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmsleu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmsleu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vmsle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmsle.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmsle.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vmsgtu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmsgtu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vmsgt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmsgt.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vminu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vminu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmin.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmaxu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmaxu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmax.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmulhu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmulhu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmulhsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmulhsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vdivu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vdivu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vdiv.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vremu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vremu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vrem.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vrem.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwmulu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwmulu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwmulsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwmulsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vnmsac.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vmadd.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vnmsub.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vwmaccu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vwmaccu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vwmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vwmaccsu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vwmaccsu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vwmaccus.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, + { "vmv.v.v", rv_codec_v_r, rv_fmt_vd_vs1, NULL, 0, 0, 0 }, + { "vmv.v.x", rv_codec_v_r, rv_fmt_vd_rs1, NULL, 0, 0, 0 }, + { "vmv.v.i", rv_codec_v_i, rv_fmt_vd_imm, NULL, 0, 0, 0 }, + { "vmerge.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, + { "vmerge.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, + { "vmerge.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, 0, 0, 0 }, + { "vsaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vsaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vsaddu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vsadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vsadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vsadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, + { "vssubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vssubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vssub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vssub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vaadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vaadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vaaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vaaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vasub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vasub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vasubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vasubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vsmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vsmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vssrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vssrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vssrl.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vssra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vssra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vssra.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnclipu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vnclipu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vnclipu.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnclip.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vnclip.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vnclip.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vfadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfrsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfwadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfwadd.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfwsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfwsub.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfrdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfwmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfnmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfsqrt.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vfrsqrt7.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vfrec7.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vfmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfmin.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfmax.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfsgnj.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfsgnj.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfsgnjn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfsgnjn.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfsgnjx.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfsgnjx.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfslide1up.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfslide1down.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vmfeq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmfeq.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vmfne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmfne.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vmflt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmflt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vmfle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmfle.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vmfgt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vmfge.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, + { "vfclass.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfmerge.vfm", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vl, NULL, 0, 0, 0 }, + { "vfmv.v.f", rv_codec_v_r, rv_fmt_vd_fs1, NULL, 0, 0, 0 }, + { "vfcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvt.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.f.xu.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.f.x.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.rod.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.rtz.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfncvt.rtz.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vredand.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vredor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vredxor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vredminu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vredmaxu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwredsumu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfwredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vfwredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmnand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmandn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmxor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmorn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vmxnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vcpop.m", rv_codec_v_r, rv_fmt_rd_vs2_vm, NULL, 0, 0, 0 }, + { "vfirst.m", rv_codec_v_r, rv_fmt_rd_vs2_vm, NULL, 0, 0, 0 }, + { "vmsbf.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vmsif.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vmsof.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "viota.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vid.v", rv_codec_v_r, rv_fmt_vd_vm, NULL, 0, 0, 0 }, + { "vmv.x.s", rv_codec_v_r, rv_fmt_rd_vs2, NULL, 0, 0, 0 }, + { "vmv.s.x", rv_codec_v_r, rv_fmt_vd_rs1, NULL, 0, 0, 0 }, + { "vfmv.f.s", rv_codec_v_r, rv_fmt_fd_vs2, NULL, 0, 0, 0 }, + { "vfmv.s.f", rv_codec_v_r, rv_fmt_vd_fs1, NULL, 0, 0, 0 }, + { "vslideup.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vslideup.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vslide1up.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vslidedown.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vslidedown.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vslide1down.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vrgather.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vrgatherei16.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vrgather.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vrgather.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vcompress.vm", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, + { "vmv1r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vmv2r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vmv4r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vmv8r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vzext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vzext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vzext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vsext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vsext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vsext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vsetvli", rv_codec_vsetvli, rv_fmt_vsetvli, NULL, 0, 0, 0 }, + { "vsetivli", rv_codec_vsetivli, rv_fmt_vsetivli, NULL, 0, 0, 0 }, + { "vsetvl", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "c.zext.b", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, + { "c.sext.b", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, + { "c.zext.h", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, + { "c.sext.h", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, + { "c.zext.w", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, + { "c.not", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, + { "c.mul", rv_codec_zcb_mul, rv_fmt_rd_rs2, NULL, 0, 0 }, + { "c.lbu", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, + { "c.lhu", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, + { "c.lh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, + { "c.sb", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, + { "c.sh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, + { "cm.push", rv_codec_zcmp_cm_pushpop, rv_fmt_push_rlist, NULL, 0, 0 }, + { "cm.pop", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist, NULL, 0, 0 }, + { "cm.popret", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist, NULL, 0, 0, 0 }, + { "cm.popretz", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist, NULL, 0, 0 }, + { "cm.mva01s", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, + { "cm.mvsa01", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, + { "cm.jt", rv_codec_zcmt_jt, rv_fmt_zcmt_index, NULL, 0 }, + { "cm.jalt", rv_codec_zcmt_jt, rv_fmt_zcmt_index, NULL, 0 }, + { "czero.eqz", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "czero.nez", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "fcvt.bf16.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fcvt.s.bf16", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "vfncvtbf16.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwcvtbf16.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vfwmaccbf16.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, + { "vfwmaccbf16.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, + { "flh", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 }, + { "fsh", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 }, + { "fmv.h.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, + { "fmv.x.h", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "fli.s", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, + { "fli.d", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, + { "fli.q", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, + { "fli.h", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, + { "fminm.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fmaxm.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fminm.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fmaxm.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fminm.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fmaxm.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fminm.h", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fmaxm.h", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, + { "fround.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "froundnx.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fround.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "froundnx.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fround.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "froundnx.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fround.h", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "froundnx.h", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, + { "fcvtmod.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, + { "fmvh.x.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "fmvp.d.x", rv_codec_r, rv_fmt_frd_rs1_rs2, NULL, 0, 0, 0 }, + { "fmvh.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "fmvp.q.x", rv_codec_r, rv_fmt_frd_rs1_rs2, NULL, 0, 0, 0 }, + { "fleq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fltq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fleq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fltq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fleq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fltq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fleq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "fltq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, + { "vaesdf.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaesdf.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaesdm.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaesdm.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaesef.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaesef.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaesem.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaesem.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vaeskf1.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vaeskf2.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vaesz.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vandn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vandn.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vbrev.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vbrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vclmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vclmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vclmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vclmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vclz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vcpop.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vctz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vghsh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, + { "vgmul.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, + { "vrol.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vrol.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vror.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vror.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vror.vi", rv_codec_vror_vi, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vsha2ch.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, + { "vsha2cl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, + { "vsha2ms.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, + { "vsm3c.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vsm3me.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, + { "vsm4k.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vsm4r.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vsm4r.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, + { "vwsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, + { "vwsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, + { "vwsll.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "amocas.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, }; /* CSR names */ @@ -1297,15 +2109,18 @@ static const char *csr_name(int csrno) case 0x0003: return "fcsr"; case 0x0004: return "uie"; case 0x0005: return "utvec"; + case 0x0008: return "vstart"; + case 0x0009: return "vxsat"; + case 0x000a: return "vxrm"; + case 0x000f: return "vcsr"; case 0x0015: return "seed"; + case 0x0017: return "jvt"; case 0x0040: return "uscratch"; case 0x0041: return "uepc"; case 0x0042: return "ucause"; case 0x0043: return "utval"; case 0x0044: return "uip"; case 0x0100: return "sstatus"; - case 0x0102: return "sedeleg"; - case 0x0103: return "sideleg"; case 0x0104: return "sie"; case 0x0105: return "stvec"; case 0x0106: return "scounteren"; @@ -1389,8 +2204,8 @@ static const char *csr_name(int csrno) case 0x03ba: return "pmpaddr10"; case 0x03bb: return "pmpaddr11"; case 0x03bc: return "pmpaddr12"; - case 0x03bd: return "pmpaddr14"; - case 0x03be: return "pmpaddr13"; + case 0x03bd: return "pmpaddr13"; + case 0x03be: return "pmpaddr14"; case 0x03bf: return "pmpaddr15"; case 0x0780: return "mtohost"; case 0x0781: return "mfromhost"; @@ -1471,6 +2286,9 @@ static const char *csr_name(int csrno) case 0x0c00: return "cycle"; case 0x0c01: return "time"; case 0x0c02: return "instret"; + case 0x0c20: return "vl"; + case 0x0c21: return "vtype"; + case 0x0c22: return "vlenb"; case 0x0c80: return "cycleh"; case 0x0c81: return "timeh"; case 0x0c82: return "instreth"; @@ -1500,9 +2318,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; - switch (((inst >> 0) & 0b11)) { + switch ((inst >> 0) & 0b11) { case 0: - switch (((inst >> 13) & 0b111)) { + switch ((inst >> 13) & 0b111) { case 0: op = rv_op_c_addi4spn; break; case 1: if (isa == rv128) { @@ -1519,6 +2337,24 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) op = rv_op_c_ld; } break; + case 4: + switch ((inst >> 10) & 0b111) { + case 0: op = rv_op_c_lbu; break; + case 1: + if (((inst >> 6) & 1) == 0) { + op = rv_op_c_lhu; + } else { + op = rv_op_c_lh; + } + break; + case 2: op = rv_op_c_sb; break; + case 3: + if (((inst >> 6) & 1) == 0) { + op = rv_op_c_sh; + } + break; + } + break; case 5: if (isa == rv128) { op = rv_op_c_sq; @@ -1537,9 +2373,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 1: - switch (((inst >> 13) & 0b111)) { + switch ((inst >> 13) & 0b111) { case 0: - switch (((inst >> 2) & 0b11111111111)) { + switch ((inst >> 2) & 0b11111111111) { case 0: op = rv_op_c_nop; break; default: op = rv_op_c_addi; break; } @@ -1553,13 +2389,13 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 2: op = rv_op_c_li; break; case 3: - switch (((inst >> 7) & 0b11111)) { + switch ((inst >> 7) & 0b11111) { case 2: op = rv_op_c_addi16sp; break; default: op = rv_op_c_lui; break; } break; case 4: - switch (((inst >> 10) & 0b11)) { + switch ((inst >> 10) & 0b11) { case 0: op = rv_op_c_srli; break; @@ -1575,6 +2411,17 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 3: op = rv_op_c_and; break; case 4: op = rv_op_c_subw; break; case 5: op = rv_op_c_addw; break; + case 6: op = rv_op_c_mul; break; + case 7: + switch ((inst >> 2) & 0b111) { + case 0: op = rv_op_c_zext_b; break; + case 1: op = rv_op_c_sext_b; break; + case 2: op = rv_op_c_zext_h; break; + case 3: op = rv_op_c_sext_h; break; + case 4: op = rv_op_c_zext_w; break; + case 5: op = rv_op_c_not; break; + } + break; } break; } @@ -1585,7 +2432,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 2: - switch (((inst >> 13) & 0b111)) { + switch ((inst >> 13) & 0b111) { case 0: op = rv_op_c_slli; break; @@ -1605,17 +2452,17 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 4: - switch (((inst >> 12) & 0b1)) { + switch ((inst >> 12) & 0b1) { case 0: - switch (((inst >> 2) & 0b11111)) { + switch ((inst >> 2) & 0b11111) { case 0: op = rv_op_c_jr; break; default: op = rv_op_c_mv; break; } break; case 1: - switch (((inst >> 2) & 0b11111)) { + switch ((inst >> 2) & 0b11111) { case 0: - switch (((inst >> 7) & 0b11111)) { + switch ((inst >> 7) & 0b11111) { case 0: op = rv_op_c_ebreak; break; default: op = rv_op_c_jalr; break; } @@ -1630,6 +2477,52 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) op = rv_op_c_sqsp; } else { op = rv_op_c_fsdsp; + if (dec->cfg->ext_zcmp && ((inst >> 12) & 0b01)) { + switch ((inst >> 8) & 0b01111) { + case 8: + if (((inst >> 4) & 0b01111) >= 4) { + op = rv_op_cm_push; + } + break; + case 10: + if (((inst >> 4) & 0b01111) >= 4) { + op = rv_op_cm_pop; + } + break; + case 12: + if (((inst >> 4) & 0b01111) >= 4) { + op = rv_op_cm_popretz; + } + break; + case 14: + if (((inst >> 4) & 0b01111) >= 4) { + op = rv_op_cm_popret; + } + break; + } + } else { + switch ((inst >> 10) & 0b011) { + case 0: + if (!dec->cfg->ext_zcmt) { + break; + } + if (((inst >> 2) & 0xFF) >= 32) { + op = rv_op_cm_jalt; + } else { + op = rv_op_cm_jt; + } + break; + case 3: + if (!dec->cfg->ext_zcmp) { + break; + } + switch ((inst >> 5) & 0b011) { + case 1: op = rv_op_cm_mvsa01; break; + case 3: op = rv_op_cm_mva01s; break; + } + break; + } + } } break; case 6: op = rv_op_c_swsp; break; @@ -1643,9 +2536,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 3: - switch (((inst >> 2) & 0b11111)) { + switch ((inst >> 2) & 0b11111) { case 0: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_lb; break; case 1: op = rv_op_lh; break; case 2: op = rv_op_lw; break; @@ -1657,32 +2550,110 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 1: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { + case 0: + switch ((inst >> 20) & 0b111111111111) { + case 40: op = rv_op_vl1re8_v; break; + case 552: op = rv_op_vl2re8_v; break; + case 1576: op = rv_op_vl4re8_v; break; + case 3624: op = rv_op_vl8re8_v; break; + } + switch ((inst >> 26) & 0b111) { + case 0: + switch ((inst >> 20) & 0b11111) { + case 0: op = rv_op_vle8_v; break; + case 11: op = rv_op_vlm_v; break; + case 16: op = rv_op_vle8ff_v; break; + } + break; + case 1: op = rv_op_vluxei8_v; break; + case 2: op = rv_op_vlse8_v; break; + case 3: op = rv_op_vloxei8_v; break; + } + break; + case 1: op = rv_op_flh; break; case 2: op = rv_op_flw; break; case 3: op = rv_op_fld; break; case 4: op = rv_op_flq; break; + case 5: + switch ((inst >> 20) & 0b111111111111) { + case 40: op = rv_op_vl1re16_v; break; + case 552: op = rv_op_vl2re16_v; break; + case 1576: op = rv_op_vl4re16_v; break; + case 3624: op = rv_op_vl8re16_v; break; + } + switch ((inst >> 26) & 0b111) { + case 0: + switch ((inst >> 20) & 0b11111) { + case 0: op = rv_op_vle16_v; break; + case 16: op = rv_op_vle16ff_v; break; + } + break; + case 1: op = rv_op_vluxei16_v; break; + case 2: op = rv_op_vlse16_v; break; + case 3: op = rv_op_vloxei16_v; break; + } + break; + case 6: + switch ((inst >> 20) & 0b111111111111) { + case 40: op = rv_op_vl1re32_v; break; + case 552: op = rv_op_vl2re32_v; break; + case 1576: op = rv_op_vl4re32_v; break; + case 3624: op = rv_op_vl8re32_v; break; + } + switch ((inst >> 26) & 0b111) { + case 0: + switch ((inst >> 20) & 0b11111) { + case 0: op = rv_op_vle32_v; break; + case 16: op = rv_op_vle32ff_v; break; + } + break; + case 1: op = rv_op_vluxei32_v; break; + case 2: op = rv_op_vlse32_v; break; + case 3: op = rv_op_vloxei32_v; break; + } + break; + case 7: + switch ((inst >> 20) & 0b111111111111) { + case 40: op = rv_op_vl1re64_v; break; + case 552: op = rv_op_vl2re64_v; break; + case 1576: op = rv_op_vl4re64_v; break; + case 3624: op = rv_op_vl8re64_v; break; + } + switch ((inst >> 26) & 0b111) { + case 0: + switch ((inst >> 20) & 0b11111) { + case 0: op = rv_op_vle64_v; break; + case 16: op = rv_op_vle64ff_v; break; + } + break; + case 1: op = rv_op_vluxei64_v; break; + case 2: op = rv_op_vlse64_v; break; + case 3: op = rv_op_vloxei64_v; break; + } + break; } break; case 3: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fence; break; case 1: op = rv_op_fence_i; break; case 2: op = rv_op_lq; break; } break; case 4: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_addi; break; case 1: - switch (((inst >> 27) & 0b11111)) { + switch ((inst >> 27) & 0b11111) { case 0b00000: op = rv_op_slli; break; case 0b00001: - switch (((inst >> 20) & 0b1111111)) { + switch ((inst >> 20) & 0b1111111) { case 0b0001111: op = rv_op_zip; break; } break; case 0b00010: - switch (((inst >> 20) & 0b1111111)) { + switch ((inst >> 20) & 0b1111111) { case 0b0000000: op = rv_op_sha256sum0; break; case 0b0000001: op = rv_op_sha256sum1; break; case 0b0000010: op = rv_op_sha256sig0; break; @@ -1697,7 +2668,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 0b00101: op = rv_op_bseti; break; case 0b00110: - switch (((inst >> 20) & 0b1111111)) { + switch ((inst >> 20) & 0b1111111) { case 0b0000000: op = rv_op_aes64im; break; default: if (((inst >> 24) & 0b0111) == 0b001) { @@ -1709,7 +2680,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 0b01001: op = rv_op_bclri; break; case 0b01101: op = rv_op_binvi; break; case 0b01100: - switch (((inst >> 20) & 0b1111111)) { + switch ((inst >> 20) & 0b1111111) { case 0b0000000: op = rv_op_clz; break; case 0b0000001: op = rv_op_ctz; break; case 0b0000010: op = rv_op_cpop; break; @@ -1724,10 +2695,10 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 3: op = rv_op_sltiu; break; case 4: op = rv_op_xori; break; case 5: - switch (((inst >> 27) & 0b11111)) { + switch ((inst >> 27) & 0b11111) { case 0b00000: op = rv_op_srli; break; case 0b00001: - switch (((inst >> 20) & 0b1111111)) { + switch ((inst >> 20) & 0b1111111) { case 0b0001111: op = rv_op_unzip; break; } break; @@ -1750,13 +2721,13 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 5: op = rv_op_auipc; break; case 6: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_addiw; break; case 1: - switch (((inst >> 25) & 0b1111111)) { + switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_slliw; break; - case 4: op = rv_op_slli_uw; break; - case 48: + case 2: op = rv_op_slli_uw; break; + case 24: switch ((inst >> 20) & 0b11111) { case 0b00000: op = rv_op_clzw; break; case 0b00001: op = rv_op_ctzw; break; @@ -1766,7 +2737,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 5: - switch (((inst >> 25) & 0b1111111)) { + switch ((inst >> 25) & 0b1111111) { case 0: op = rv_op_srliw; break; case 32: op = rv_op_sraiw; break; case 48: op = rv_op_roriw; break; @@ -1775,7 +2746,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 8: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_sb; break; case 1: op = rv_op_sh; break; case 2: op = rv_op_sw; break; @@ -1784,14 +2755,71 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 9: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { + case 0: + switch ((inst >> 20) & 0b111111111111) { + case 40: op = rv_op_vs1r_v; break; + case 552: op = rv_op_vs2r_v; break; + case 1576: op = rv_op_vs4r_v; break; + case 3624: op = rv_op_vs8r_v; break; + } + switch ((inst >> 26) & 0b111) { + case 0: + switch ((inst >> 20) & 0b11111) { + case 0: op = rv_op_vse8_v; break; + case 11: op = rv_op_vsm_v; break; + } + break; + case 1: op = rv_op_vsuxei8_v; break; + case 2: op = rv_op_vsse8_v; break; + case 3: op = rv_op_vsoxei8_v; break; + } + break; + case 1: op = rv_op_fsh; break; case 2: op = rv_op_fsw; break; case 3: op = rv_op_fsd; break; case 4: op = rv_op_fsq; break; + case 5: + switch ((inst >> 26) & 0b111) { + case 0: + switch ((inst >> 20) & 0b11111) { + case 0: op = rv_op_vse16_v; break; + } + break; + case 1: op = rv_op_vsuxei16_v; break; + case 2: op = rv_op_vsse16_v; break; + case 3: op = rv_op_vsoxei16_v; break; + } + break; + case 6: + switch ((inst >> 26) & 0b111) { + case 0: + switch ((inst >> 20) & 0b11111) { + case 0: op = rv_op_vse32_v; break; + } + break; + case 1: op = rv_op_vsuxei32_v; break; + case 2: op = rv_op_vsse32_v; break; + case 3: op = rv_op_vsoxei32_v; break; + } + break; + case 7: + switch ((inst >> 26) & 0b111) { + case 0: + switch ((inst >> 20) & 0b11111) { + case 0: op = rv_op_vse64_v; break; + } + break; + case 1: op = rv_op_vsuxei64_v; break; + case 2: op = rv_op_vsse64_v; break; + case 3: op = rv_op_vsoxei64_v; break; + } + break; } break; case 11: - switch (((inst >> 24) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 24) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { case 2: op = rv_op_amoadd_w; break; case 3: op = rv_op_amoadd_d; break; case 4: op = rv_op_amoadd_q; break; @@ -1799,17 +2827,17 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 11: op = rv_op_amoswap_d; break; case 12: op = rv_op_amoswap_q; break; case 18: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_lr_w; break; } break; case 19: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_lr_d; break; } break; case 20: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_lr_q; break; } break; @@ -1819,6 +2847,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 34: op = rv_op_amoxor_w; break; case 35: op = rv_op_amoxor_d; break; case 36: op = rv_op_amoxor_q; break; + case 42: op = rv_op_amocas_w; break; + case 43: op = rv_op_amocas_d; break; + case 44: op = rv_op_amocas_q; break; case 66: op = rv_op_amoor_w; break; case 67: op = rv_op_amoor_d; break; case 68: op = rv_op_amoor_q; break; @@ -1840,7 +2871,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 12: - switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { + switch (((inst >> 22) & 0b1111111000) | + ((inst >> 12) & 0b0000000111)) { case 0: op = rv_op_add; break; case 1: op = rv_op_sll; break; case 2: op = rv_op_slt; break; @@ -1872,6 +2904,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 45: op = rv_op_minu; break; case 46: op = rv_op_max; break; case 47: op = rv_op_maxu; break; + case 075: op = rv_op_czero_eqz; break; + case 077: op = rv_op_czero_nez; break; case 130: op = rv_op_sh1add; break; case 132: op = rv_op_sh2add; break; case 134: op = rv_op_sh3add; break; @@ -1911,7 +2945,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 13: op = rv_op_lui; break; case 14: - switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { + switch (((inst >> 22) & 0b1111111000) | + ((inst >> 12) & 0b0000000111)) { case 0: op = rv_op_addw; break; case 1: op = rv_op_sllw; break; case 5: op = rv_op_srlw; break; @@ -1937,35 +2972,35 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 16: - switch (((inst >> 25) & 0b11)) { + switch ((inst >> 25) & 0b11) { case 0: op = rv_op_fmadd_s; break; case 1: op = rv_op_fmadd_d; break; case 3: op = rv_op_fmadd_q; break; } break; case 17: - switch (((inst >> 25) & 0b11)) { + switch ((inst >> 25) & 0b11) { case 0: op = rv_op_fmsub_s; break; case 1: op = rv_op_fmsub_d; break; case 3: op = rv_op_fmsub_q; break; } break; case 18: - switch (((inst >> 25) & 0b11)) { + switch ((inst >> 25) & 0b11) { case 0: op = rv_op_fnmsub_s; break; case 1: op = rv_op_fnmsub_d; break; case 3: op = rv_op_fnmsub_q; break; } break; case 19: - switch (((inst >> 25) & 0b11)) { + switch ((inst >> 25) & 0b11) { case 0: op = rv_op_fnmadd_s; break; case 1: op = rv_op_fnmadd_d; break; case 3: op = rv_op_fnmadd_q; break; } break; case 20: - switch (((inst >> 25) & 0b1111111)) { + switch ((inst >> 25) & 0b1111111) { case 0: op = rv_op_fadd_s; break; case 1: op = rv_op_fadd_d; break; case 3: op = rv_op_fadd_q; break; @@ -1979,100 +3014,148 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 13: op = rv_op_fdiv_d; break; case 15: op = rv_op_fdiv_q; break; case 16: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fsgnj_s; break; case 1: op = rv_op_fsgnjn_s; break; case 2: op = rv_op_fsgnjx_s; break; } break; case 17: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fsgnj_d; break; case 1: op = rv_op_fsgnjn_d; break; case 2: op = rv_op_fsgnjx_d; break; } break; case 19: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fsgnj_q; break; case 1: op = rv_op_fsgnjn_q; break; case 2: op = rv_op_fsgnjx_q; break; } break; case 20: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fmin_s; break; case 1: op = rv_op_fmax_s; break; + case 2: op = rv_op_fminm_s; break; + case 3: op = rv_op_fmaxm_s; break; } break; case 21: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fmin_d; break; case 1: op = rv_op_fmax_d; break; + case 2: op = rv_op_fminm_d; break; + case 3: op = rv_op_fmaxm_d; break; + } + break; + case 22: + switch (((inst >> 12) & 0b111)) { + case 2: op = rv_op_fminm_h; break; + case 3: op = rv_op_fmaxm_h; break; } break; case 23: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fmin_q; break; case 1: op = rv_op_fmax_q; break; + case 2: op = rv_op_fminm_q; break; + case 3: op = rv_op_fmaxm_q; break; } break; case 32: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 1: op = rv_op_fcvt_s_d; break; case 3: op = rv_op_fcvt_s_q; break; + case 4: op = rv_op_fround_s; break; + case 5: op = rv_op_froundnx_s; break; + case 6: op = rv_op_fcvt_s_bf16; break; } break; case 33: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_d_s; break; case 3: op = rv_op_fcvt_d_q; break; + case 4: op = rv_op_fround_d; break; + case 5: op = rv_op_froundnx_d; break; + } + break; + case 34: + switch (((inst >> 20) & 0b11111)) { + case 4: op = rv_op_fround_h; break; + case 5: op = rv_op_froundnx_h; break; + case 8: op = rv_op_fcvt_bf16_s; break; } break; case 35: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_q_s; break; case 1: op = rv_op_fcvt_q_d; break; + case 4: op = rv_op_fround_q; break; + case 5: op = rv_op_froundnx_q; break; } break; case 44: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fsqrt_s; break; } break; case 45: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fsqrt_d; break; } break; case 47: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fsqrt_q; break; } break; case 80: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fle_s; break; case 1: op = rv_op_flt_s; break; case 2: op = rv_op_feq_s; break; + case 4: op = rv_op_fleq_s; break; + case 5: op = rv_op_fltq_s; break; } break; case 81: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fle_d; break; case 1: op = rv_op_flt_d; break; case 2: op = rv_op_feq_d; break; + case 4: op = rv_op_fleq_d; break; + case 5: op = rv_op_fltq_d; break; + } + break; + case 82: + switch (((inst >> 12) & 0b111)) { + case 4: op = rv_op_fleq_h; break; + case 5: op = rv_op_fltq_h; break; } break; case 83: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_fle_q; break; case 1: op = rv_op_flt_q; break; case 2: op = rv_op_feq_q; break; + case 4: op = rv_op_fleq_q; break; + case 5: op = rv_op_fltq_q; break; + } + break; + case 89: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_fmvp_d_x; break; + } + break; + case 91: + switch (((inst >> 12) & 0b111)) { + case 0: op = rv_op_fmvp_q_x; break; } break; case 96: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_w_s; break; case 1: op = rv_op_fcvt_wu_s; break; case 2: op = rv_op_fcvt_l_s; break; @@ -2080,15 +3163,16 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 97: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_w_d; break; case 1: op = rv_op_fcvt_wu_d; break; case 2: op = rv_op_fcvt_l_d; break; case 3: op = rv_op_fcvt_lu_d; break; + case 8: op = rv_op_fcvtmod_w_d; break; } break; case 99: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_w_q; break; case 1: op = rv_op_fcvt_wu_q; break; case 2: op = rv_op_fcvt_l_q; break; @@ -2096,7 +3180,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 104: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_s_w; break; case 1: op = rv_op_fcvt_s_wu; break; case 2: op = rv_op_fcvt_s_l; break; @@ -2104,7 +3188,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 105: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_d_w; break; case 1: op = rv_op_fcvt_d_wu; break; case 2: op = rv_op_fcvt_d_l; break; @@ -2112,7 +3196,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 107: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_fcvt_q_w; break; case 1: op = rv_op_fcvt_q_wu; break; case 2: op = rv_op_fcvt_q_l; break; @@ -2120,50 +3204,524 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 112: - switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_x_s; break; case 1: op = rv_op_fclass_s; break; } break; case 113: - switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_x_d; break; case 1: op = rv_op_fclass_d; break; + case 8: op = rv_op_fmvh_x_d; break; + } + break; + case 114: + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { + case 0: op = rv_op_fmv_x_h; break; } break; case 115: - switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_x_q; break; case 1: op = rv_op_fclass_q; break; + case 8: op = rv_op_fmvh_x_q; break; } break; case 120: - switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_s_x; break; + case 8: op = rv_op_fli_s; break; } break; case 121: - switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_d_x; break; + case 8: op = rv_op_fli_d; break; + } + break; + case 122: + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { + case 0: op = rv_op_fmv_h_x; break; + case 8: op = rv_op_fli_h; break; } break; case 123: - switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 17) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { case 0: op = rv_op_fmv_q_x; break; + case 8: op = rv_op_fli_q; break; + } + break; + } + break; + case 21: + switch ((inst >> 12) & 0b111) { + case 0: + switch ((inst >> 26) & 0b111111) { + case 0: op = rv_op_vadd_vv; break; + case 1: op = rv_op_vandn_vv; break; + case 2: op = rv_op_vsub_vv; break; + case 4: op = rv_op_vminu_vv; break; + case 5: op = rv_op_vmin_vv; break; + case 6: op = rv_op_vmaxu_vv; break; + case 7: op = rv_op_vmax_vv; break; + case 9: op = rv_op_vand_vv; break; + case 10: op = rv_op_vor_vv; break; + case 11: op = rv_op_vxor_vv; break; + case 12: op = rv_op_vrgather_vv; break; + case 14: op = rv_op_vrgatherei16_vv; break; + case 16: + if (((inst >> 25) & 1) == 0) { + op = rv_op_vadc_vvm; + } + break; + case 17: op = rv_op_vmadc_vvm; break; + case 18: + if (((inst >> 25) & 1) == 0) { + op = rv_op_vsbc_vvm; + } + break; + case 19: op = rv_op_vmsbc_vvm; break; + case 20: op = rv_op_vror_vv; break; + case 21: op = rv_op_vrol_vv; break; + case 23: + if (((inst >> 20) & 0b111111) == 32) + op = rv_op_vmv_v_v; + else if (((inst >> 25) & 1) == 0) + op = rv_op_vmerge_vvm; + break; + case 24: op = rv_op_vmseq_vv; break; + case 25: op = rv_op_vmsne_vv; break; + case 26: op = rv_op_vmsltu_vv; break; + case 27: op = rv_op_vmslt_vv; break; + case 28: op = rv_op_vmsleu_vv; break; + case 29: op = rv_op_vmsle_vv; break; + case 32: op = rv_op_vsaddu_vv; break; + case 33: op = rv_op_vsadd_vv; break; + case 34: op = rv_op_vssubu_vv; break; + case 35: op = rv_op_vssub_vv; break; + case 37: op = rv_op_vsll_vv; break; + case 39: op = rv_op_vsmul_vv; break; + case 40: op = rv_op_vsrl_vv; break; + case 41: op = rv_op_vsra_vv; break; + case 42: op = rv_op_vssrl_vv; break; + case 43: op = rv_op_vssra_vv; break; + case 44: op = rv_op_vnsrl_wv; break; + case 45: op = rv_op_vnsra_wv; break; + case 46: op = rv_op_vnclipu_wv; break; + case 47: op = rv_op_vnclip_wv; break; + case 48: op = rv_op_vwredsumu_vs; break; + case 49: op = rv_op_vwredsum_vs; break; + case 53: op = rv_op_vwsll_vv; break; + } + break; + case 1: + switch ((inst >> 26) & 0b111111) { + case 0: op = rv_op_vfadd_vv; break; + case 1: op = rv_op_vfredusum_vs; break; + case 2: op = rv_op_vfsub_vv; break; + case 3: op = rv_op_vfredosum_vs; break; + case 4: op = rv_op_vfmin_vv; break; + case 5: op = rv_op_vfredmin_vs; break; + case 6: op = rv_op_vfmax_vv; break; + case 7: op = rv_op_vfredmax_vs; break; + case 8: op = rv_op_vfsgnj_vv; break; + case 9: op = rv_op_vfsgnjn_vv; break; + case 10: op = rv_op_vfsgnjx_vv; break; + case 16: + switch ((inst >> 15) & 0b11111) { + case 0: if ((inst >> 25) & 1) op = rv_op_vfmv_f_s; break; + } + break; + case 18: + switch ((inst >> 15) & 0b11111) { + case 0: op = rv_op_vfcvt_xu_f_v; break; + case 1: op = rv_op_vfcvt_x_f_v; break; + case 2: op = rv_op_vfcvt_f_xu_v; break; + case 3: op = rv_op_vfcvt_f_x_v; break; + case 6: op = rv_op_vfcvt_rtz_xu_f_v; break; + case 7: op = rv_op_vfcvt_rtz_x_f_v; break; + case 8: op = rv_op_vfwcvt_xu_f_v; break; + case 9: op = rv_op_vfwcvt_x_f_v; break; + case 10: op = rv_op_vfwcvt_f_xu_v; break; + case 11: op = rv_op_vfwcvt_f_x_v; break; + case 12: op = rv_op_vfwcvt_f_f_v; break; + case 13: op = rv_op_vfwcvtbf16_f_f_v; break; + case 14: op = rv_op_vfwcvt_rtz_xu_f_v; break; + case 15: op = rv_op_vfwcvt_rtz_x_f_v; break; + case 16: op = rv_op_vfncvt_xu_f_w; break; + case 17: op = rv_op_vfncvt_x_f_w; break; + case 18: op = rv_op_vfncvt_f_xu_w; break; + case 19: op = rv_op_vfncvt_f_x_w; break; + case 20: op = rv_op_vfncvt_f_f_w; break; + case 21: op = rv_op_vfncvt_rod_f_f_w; break; + case 22: op = rv_op_vfncvt_rtz_xu_f_w; break; + case 23: op = rv_op_vfncvt_rtz_x_f_w; break; + case 29: op = rv_op_vfncvtbf16_f_f_w; break; + } + break; + case 19: + switch ((inst >> 15) & 0b11111) { + case 0: op = rv_op_vfsqrt_v; break; + case 4: op = rv_op_vfrsqrt7_v; break; + case 5: op = rv_op_vfrec7_v; break; + case 16: op = rv_op_vfclass_v; break; + } + break; + case 24: op = rv_op_vmfeq_vv; break; + case 25: op = rv_op_vmfle_vv; break; + case 27: op = rv_op_vmflt_vv; break; + case 28: op = rv_op_vmfne_vv; break; + case 32: op = rv_op_vfdiv_vv; break; + case 36: op = rv_op_vfmul_vv; break; + case 40: op = rv_op_vfmadd_vv; break; + case 41: op = rv_op_vfnmadd_vv; break; + case 42: op = rv_op_vfmsub_vv; break; + case 43: op = rv_op_vfnmsub_vv; break; + case 44: op = rv_op_vfmacc_vv; break; + case 45: op = rv_op_vfnmacc_vv; break; + case 46: op = rv_op_vfmsac_vv; break; + case 47: op = rv_op_vfnmsac_vv; break; + case 48: op = rv_op_vfwadd_vv; break; + case 49: op = rv_op_vfwredusum_vs; break; + case 50: op = rv_op_vfwsub_vv; break; + case 51: op = rv_op_vfwredosum_vs; break; + case 52: op = rv_op_vfwadd_wv; break; + case 54: op = rv_op_vfwsub_wv; break; + case 56: op = rv_op_vfwmul_vv; break; + case 59: op = rv_op_vfwmaccbf16_vv; break; + case 60: op = rv_op_vfwmacc_vv; break; + case 61: op = rv_op_vfwnmacc_vv; break; + case 62: op = rv_op_vfwmsac_vv; break; + case 63: op = rv_op_vfwnmsac_vv; break; + } + break; + case 2: + switch ((inst >> 26) & 0b111111) { + case 0: op = rv_op_vredsum_vs; break; + case 1: op = rv_op_vredand_vs; break; + case 2: op = rv_op_vredor_vs; break; + case 3: op = rv_op_vredxor_vs; break; + case 4: op = rv_op_vredminu_vs; break; + case 5: op = rv_op_vredmin_vs; break; + case 6: op = rv_op_vredmaxu_vs; break; + case 7: op = rv_op_vredmax_vs; break; + case 8: op = rv_op_vaaddu_vv; break; + case 9: op = rv_op_vaadd_vv; break; + case 10: op = rv_op_vasubu_vv; break; + case 11: op = rv_op_vasub_vv; break; + case 12: op = rv_op_vclmul_vv; break; + case 13: op = rv_op_vclmulh_vv; break; + case 16: + switch ((inst >> 15) & 0b11111) { + case 0: if ((inst >> 25) & 1) op = rv_op_vmv_x_s; break; + case 16: op = rv_op_vcpop_m; break; + case 17: op = rv_op_vfirst_m; break; + } + break; + case 18: + switch ((inst >> 15) & 0b11111) { + case 2: op = rv_op_vzext_vf8; break; + case 3: op = rv_op_vsext_vf8; break; + case 4: op = rv_op_vzext_vf4; break; + case 5: op = rv_op_vsext_vf4; break; + case 6: op = rv_op_vzext_vf2; break; + case 7: op = rv_op_vsext_vf2; break; + case 8: op = rv_op_vbrev8_v; break; + case 9: op = rv_op_vrev8_v; break; + case 10: op = rv_op_vbrev_v; break; + case 12: op = rv_op_vclz_v; break; + case 13: op = rv_op_vctz_v; break; + case 14: op = rv_op_vcpop_v; break; + } + break; + case 20: + switch ((inst >> 15) & 0b11111) { + case 1: op = rv_op_vmsbf_m; break; + case 2: op = rv_op_vmsof_m; break; + case 3: op = rv_op_vmsif_m; break; + case 16: op = rv_op_viota_m; break; + case 17: + if (((inst >> 20) & 0b11111) == 0) { + op = rv_op_vid_v; + } + break; + } + break; + case 23: if ((inst >> 25) & 1) op = rv_op_vcompress_vm; break; + case 24: if ((inst >> 25) & 1) op = rv_op_vmandn_mm; break; + case 25: if ((inst >> 25) & 1) op = rv_op_vmand_mm; break; + case 26: if ((inst >> 25) & 1) op = rv_op_vmor_mm; break; + case 27: if ((inst >> 25) & 1) op = rv_op_vmxor_mm; break; + case 28: if ((inst >> 25) & 1) op = rv_op_vmorn_mm; break; + case 29: if ((inst >> 25) & 1) op = rv_op_vmnand_mm; break; + case 30: if ((inst >> 25) & 1) op = rv_op_vmnor_mm; break; + case 31: if ((inst >> 25) & 1) op = rv_op_vmxnor_mm; break; + case 32: op = rv_op_vdivu_vv; break; + case 33: op = rv_op_vdiv_vv; break; + case 34: op = rv_op_vremu_vv; break; + case 35: op = rv_op_vrem_vv; break; + case 36: op = rv_op_vmulhu_vv; break; + case 37: op = rv_op_vmul_vv; break; + case 38: op = rv_op_vmulhsu_vv; break; + case 39: op = rv_op_vmulh_vv; break; + case 41: op = rv_op_vmadd_vv; break; + case 43: op = rv_op_vnmsub_vv; break; + case 45: op = rv_op_vmacc_vv; break; + case 47: op = rv_op_vnmsac_vv; break; + case 48: op = rv_op_vwaddu_vv; break; + case 49: op = rv_op_vwadd_vv; break; + case 50: op = rv_op_vwsubu_vv; break; + case 51: op = rv_op_vwsub_vv; break; + case 52: op = rv_op_vwaddu_wv; break; + case 53: op = rv_op_vwadd_wv; break; + case 54: op = rv_op_vwsubu_wv; break; + case 55: op = rv_op_vwsub_wv; break; + case 56: op = rv_op_vwmulu_vv; break; + case 58: op = rv_op_vwmulsu_vv; break; + case 59: op = rv_op_vwmul_vv; break; + case 60: op = rv_op_vwmaccu_vv; break; + case 61: op = rv_op_vwmacc_vv; break; + case 63: op = rv_op_vwmaccsu_vv; break; + } + break; + case 3: + switch ((inst >> 26) & 0b111111) { + case 0: op = rv_op_vadd_vi; break; + case 3: op = rv_op_vrsub_vi; break; + case 9: op = rv_op_vand_vi; break; + case 10: op = rv_op_vor_vi; break; + case 11: op = rv_op_vxor_vi; break; + case 12: op = rv_op_vrgather_vi; break; + case 14: op = rv_op_vslideup_vi; break; + case 15: op = rv_op_vslidedown_vi; break; + case 16: + if (((inst >> 25) & 1) == 0) { + op = rv_op_vadc_vim; + } + break; + case 17: op = rv_op_vmadc_vim; break; + case 20: case 21: op = rv_op_vror_vi; break; + case 23: + if (((inst >> 20) & 0b111111) == 32) + op = rv_op_vmv_v_i; + else if (((inst >> 25) & 1) == 0) + op = rv_op_vmerge_vim; + break; + case 24: op = rv_op_vmseq_vi; break; + case 25: op = rv_op_vmsne_vi; break; + case 28: op = rv_op_vmsleu_vi; break; + case 29: op = rv_op_vmsle_vi; break; + case 30: op = rv_op_vmsgtu_vi; break; + case 31: op = rv_op_vmsgt_vi; break; + case 32: op = rv_op_vsaddu_vi; break; + case 33: op = rv_op_vsadd_vi; break; + case 37: op = rv_op_vsll_vi; break; + case 39: + switch ((inst >> 15) & 0b11111) { + case 0: op = rv_op_vmv1r_v; break; + case 1: op = rv_op_vmv2r_v; break; + case 3: op = rv_op_vmv4r_v; break; + case 7: op = rv_op_vmv8r_v; break; + } + break; + case 40: op = rv_op_vsrl_vi; break; + case 41: op = rv_op_vsra_vi; break; + case 42: op = rv_op_vssrl_vi; break; + case 43: op = rv_op_vssra_vi; break; + case 44: op = rv_op_vnsrl_wi; break; + case 45: op = rv_op_vnsra_wi; break; + case 46: op = rv_op_vnclipu_wi; break; + case 47: op = rv_op_vnclip_wi; break; + case 53: op = rv_op_vwsll_vi; break; + } + break; + case 4: + switch ((inst >> 26) & 0b111111) { + case 0: op = rv_op_vadd_vx; break; + case 1: op = rv_op_vandn_vx; break; + case 2: op = rv_op_vsub_vx; break; + case 3: op = rv_op_vrsub_vx; break; + case 4: op = rv_op_vminu_vx; break; + case 5: op = rv_op_vmin_vx; break; + case 6: op = rv_op_vmaxu_vx; break; + case 7: op = rv_op_vmax_vx; break; + case 9: op = rv_op_vand_vx; break; + case 10: op = rv_op_vor_vx; break; + case 11: op = rv_op_vxor_vx; break; + case 12: op = rv_op_vrgather_vx; break; + case 14: op = rv_op_vslideup_vx; break; + case 15: op = rv_op_vslidedown_vx; break; + case 16: + if (((inst >> 25) & 1) == 0) { + op = rv_op_vadc_vxm; + } + break; + case 17: op = rv_op_vmadc_vxm; break; + case 18: + if (((inst >> 25) & 1) == 0) { + op = rv_op_vsbc_vxm; + } + break; + case 19: op = rv_op_vmsbc_vxm; break; + case 20: op = rv_op_vror_vx; break; + case 21: op = rv_op_vrol_vx; break; + case 23: + if (((inst >> 20) & 0b111111) == 32) + op = rv_op_vmv_v_x; + else if (((inst >> 25) & 1) == 0) + op = rv_op_vmerge_vxm; + break; + case 24: op = rv_op_vmseq_vx; break; + case 25: op = rv_op_vmsne_vx; break; + case 26: op = rv_op_vmsltu_vx; break; + case 27: op = rv_op_vmslt_vx; break; + case 28: op = rv_op_vmsleu_vx; break; + case 29: op = rv_op_vmsle_vx; break; + case 30: op = rv_op_vmsgtu_vx; break; + case 31: op = rv_op_vmsgt_vx; break; + case 32: op = rv_op_vsaddu_vx; break; + case 33: op = rv_op_vsadd_vx; break; + case 34: op = rv_op_vssubu_vx; break; + case 35: op = rv_op_vssub_vx; break; + case 37: op = rv_op_vsll_vx; break; + case 39: op = rv_op_vsmul_vx; break; + case 40: op = rv_op_vsrl_vx; break; + case 41: op = rv_op_vsra_vx; break; + case 42: op = rv_op_vssrl_vx; break; + case 43: op = rv_op_vssra_vx; break; + case 44: op = rv_op_vnsrl_wx; break; + case 45: op = rv_op_vnsra_wx; break; + case 46: op = rv_op_vnclipu_wx; break; + case 47: op = rv_op_vnclip_wx; break; + case 53: op = rv_op_vwsll_vx; break; + } + break; + case 5: + switch ((inst >> 26) & 0b111111) { + case 0: op = rv_op_vfadd_vf; break; + case 2: op = rv_op_vfsub_vf; break; + case 4: op = rv_op_vfmin_vf; break; + case 6: op = rv_op_vfmax_vf; break; + case 8: op = rv_op_vfsgnj_vf; break; + case 9: op = rv_op_vfsgnjn_vf; break; + case 10: op = rv_op_vfsgnjx_vf; break; + case 14: op = rv_op_vfslide1up_vf; break; + case 15: op = rv_op_vfslide1down_vf; break; + case 16: + switch ((inst >> 20) & 0b11111) { + case 0: if ((inst >> 25) & 1) op = rv_op_vfmv_s_f; break; + } + break; + case 23: + if (((inst >> 25) & 1) == 0) + op = rv_op_vfmerge_vfm; + else if (((inst >> 20) & 0b111111) == 32) + op = rv_op_vfmv_v_f; + break; + case 24: op = rv_op_vmfeq_vf; break; + case 25: op = rv_op_vmfle_vf; break; + case 27: op = rv_op_vmflt_vf; break; + case 28: op = rv_op_vmfne_vf; break; + case 29: op = rv_op_vmfgt_vf; break; + case 31: op = rv_op_vmfge_vf; break; + case 32: op = rv_op_vfdiv_vf; break; + case 33: op = rv_op_vfrdiv_vf; break; + case 36: op = rv_op_vfmul_vf; break; + case 39: op = rv_op_vfrsub_vf; break; + case 40: op = rv_op_vfmadd_vf; break; + case 41: op = rv_op_vfnmadd_vf; break; + case 42: op = rv_op_vfmsub_vf; break; + case 43: op = rv_op_vfnmsub_vf; break; + case 44: op = rv_op_vfmacc_vf; break; + case 45: op = rv_op_vfnmacc_vf; break; + case 46: op = rv_op_vfmsac_vf; break; + case 47: op = rv_op_vfnmsac_vf; break; + case 48: op = rv_op_vfwadd_vf; break; + case 50: op = rv_op_vfwsub_vf; break; + case 52: op = rv_op_vfwadd_wf; break; + case 54: op = rv_op_vfwsub_wf; break; + case 56: op = rv_op_vfwmul_vf; break; + case 59: op = rv_op_vfwmaccbf16_vf; break; + case 60: op = rv_op_vfwmacc_vf; break; + case 61: op = rv_op_vfwnmacc_vf; break; + case 62: op = rv_op_vfwmsac_vf; break; + case 63: op = rv_op_vfwnmsac_vf; break; + } + break; + case 6: + switch ((inst >> 26) & 0b111111) { + case 8: op = rv_op_vaaddu_vx; break; + case 9: op = rv_op_vaadd_vx; break; + case 10: op = rv_op_vasubu_vx; break; + case 11: op = rv_op_vasub_vx; break; + case 12: op = rv_op_vclmul_vx; break; + case 13: op = rv_op_vclmulh_vx; break; + case 14: op = rv_op_vslide1up_vx; break; + case 15: op = rv_op_vslide1down_vx; break; + case 16: + switch ((inst >> 20) & 0b11111) { + case 0: if ((inst >> 25) & 1) op = rv_op_vmv_s_x; break; + } + break; + case 32: op = rv_op_vdivu_vx; break; + case 33: op = rv_op_vdiv_vx; break; + case 34: op = rv_op_vremu_vx; break; + case 35: op = rv_op_vrem_vx; break; + case 36: op = rv_op_vmulhu_vx; break; + case 37: op = rv_op_vmul_vx; break; + case 38: op = rv_op_vmulhsu_vx; break; + case 39: op = rv_op_vmulh_vx; break; + case 41: op = rv_op_vmadd_vx; break; + case 43: op = rv_op_vnmsub_vx; break; + case 45: op = rv_op_vmacc_vx; break; + case 47: op = rv_op_vnmsac_vx; break; + case 48: op = rv_op_vwaddu_vx; break; + case 49: op = rv_op_vwadd_vx; break; + case 50: op = rv_op_vwsubu_vx; break; + case 51: op = rv_op_vwsub_vx; break; + case 52: op = rv_op_vwaddu_wx; break; + case 53: op = rv_op_vwadd_wx; break; + case 54: op = rv_op_vwsubu_wx; break; + case 55: op = rv_op_vwsub_wx; break; + case 56: op = rv_op_vwmulu_vx; break; + case 58: op = rv_op_vwmulsu_vx; break; + case 59: op = rv_op_vwmul_vx; break; + case 60: op = rv_op_vwmaccu_vx; break; + case 61: op = rv_op_vwmacc_vx; break; + case 62: op = rv_op_vwmaccus_vx; break; + case 63: op = rv_op_vwmaccsu_vx; break; + } + break; + case 7: + if (((inst >> 31) & 1) == 0) { + op = rv_op_vsetvli; + } else if ((inst >> 30) & 1) { + op = rv_op_vsetivli; + } else if (((inst >> 25) & 0b11111) == 0) { + op = rv_op_vsetvl; } break; } break; case 22: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_addid; break; case 1: - switch (((inst >> 26) & 0b111111)) { + switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_sllid; break; } break; case 5: - switch (((inst >> 26) & 0b111111)) { + switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_srlid; break; case 16: op = rv_op_sraid; break; } @@ -2171,7 +3729,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 24: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_beq; break; case 1: op = rv_op_bne; break; case 4: op = rv_op_blt; break; @@ -2181,32 +3739,33 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 25: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: op = rv_op_jalr; break; } break; case 27: op = rv_op_jal; break; case 28: - switch (((inst >> 12) & 0b111)) { + switch ((inst >> 12) & 0b111) { case 0: - switch (((inst >> 20) & 0b111111100000) | ((inst >> 7) & 0b000000011111)) { + switch (((inst >> 20) & 0b111111100000) | + ((inst >> 7) & 0b000000011111)) { case 0: - switch (((inst >> 15) & 0b1111111111)) { + switch ((inst >> 15) & 0b1111111111) { case 0: op = rv_op_ecall; break; case 32: op = rv_op_ebreak; break; case 64: op = rv_op_uret; break; } break; case 256: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 2: - switch (((inst >> 15) & 0b11111)) { + switch ((inst >> 15) & 0b11111) { case 0: op = rv_op_sret; break; } break; case 4: op = rv_op_sfence_vm; break; case 5: - switch (((inst >> 15) & 0b11111)) { + switch ((inst >> 15) & 0b11111) { case 0: op = rv_op_wfi; break; } break; @@ -2214,17 +3773,17 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 288: op = rv_op_sfence_vma; break; case 512: - switch (((inst >> 15) & 0b1111111111)) { + switch ((inst >> 15) & 0b1111111111) { case 64: op = rv_op_hret; break; } break; case 768: - switch (((inst >> 15) & 0b1111111111)) { + switch ((inst >> 15) & 0b1111111111) { case 64: op = rv_op_mret; break; } break; case 1952: - switch (((inst >> 15) & 0b1111111111)) { + switch ((inst >> 15) & 0b1111111111) { case 576: op = rv_op_dret; break; } break; @@ -2238,8 +3797,44 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 7: op = rv_op_csrrci; break; } break; + case 29: + if (((inst >> 25) & 1) == 1 && ((inst >> 12) & 0b111) == 2) { + switch ((inst >> 26) & 0b111111) { + case 32: op = rv_op_vsm3me_vv; break; + case 33: op = rv_op_vsm4k_vi; break; + case 34: op = rv_op_vaeskf1_vi; break; + case 40: + switch ((inst >> 15) & 0b11111) { + case 0: op = rv_op_vaesdm_vv; break; + case 1: op = rv_op_vaesdf_vv; break; + case 2: op = rv_op_vaesem_vv; break; + case 3: op = rv_op_vaesef_vv; break; + case 16: op = rv_op_vsm4r_vv; break; + case 17: op = rv_op_vgmul_vv; break; + } + break; + case 41: + switch ((inst >> 15) & 0b11111) { + case 0: op = rv_op_vaesdm_vs; break; + case 1: op = rv_op_vaesdf_vs; break; + case 2: op = rv_op_vaesem_vs; break; + case 3: op = rv_op_vaesef_vs; break; + case 7: op = rv_op_vaesz_vs; break; + case 16: op = rv_op_vsm4r_vs; break; + } + break; + case 42: op = rv_op_vaeskf2_vi; break; + case 43: op = rv_op_vsm3c_vi; break; + case 44: op = rv_op_vghsh_vv; break; + case 45: op = rv_op_vsha2ms_vv; break; + case 46: op = rv_op_vsha2ch_vv; break; + case 47: op = rv_op_vsha2cl_vv; break; + } + } + break; case 30: - switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { + switch (((inst >> 22) & 0b1111111000) | + ((inst >> 12) & 0b0000000111)) { case 0: op = rv_op_addd; break; case 1: op = rv_op_slld; break; case 5: op = rv_op_srld; break; @@ -2340,6 +3935,21 @@ static uint32_t operand_crs2q(rv_inst inst) return (inst << 59) >> 61; } +static uint32_t calculate_xreg(uint32_t sreg) +{ + return sreg < 2 ? sreg + 8 : sreg + 16; +} + +static uint32_t operand_sreg1(rv_inst inst) +{ + return calculate_xreg((inst << 54) >> 61); +} + +static uint32_t operand_sreg2(rv_inst inst) +{ + return calculate_xreg((inst << 59) >> 61); +} + static uint32_t operand_crd(rv_inst inst) { return (inst << 52) >> 59; @@ -2402,10 +4012,25 @@ static int32_t operand_sbimm12(rv_inst inst) ((inst << 56) >> 63) << 11; } -static uint32_t operand_cimmsh6(rv_inst inst) +static uint32_t operand_cimmshl6(rv_inst inst, rv_isa isa) { - return ((inst << 51) >> 63) << 5 | + int imm = ((inst << 51) >> 63) << 5 | (inst << 57) >> 59; + if (isa == rv128) { + imm = imm ? imm : 64; + } + return imm; +} + +static uint32_t operand_cimmshr6(rv_inst inst, rv_isa isa) +{ + int imm = ((inst << 51) >> 63) << 5 | + (inst << 57) >> 59; + if (isa == rv128) { + imm = imm | (imm & 32) << 1; + imm = imm ? imm : 64; + } + return imm; } static int32_t operand_cimmi(rv_inst inst) @@ -2517,6 +4142,27 @@ static uint32_t operand_cimmq(rv_inst inst) ((inst << 57) >> 62) << 6; } +static uint32_t operand_vimm(rv_inst inst) +{ + return (int64_t)(inst << 44) >> 59; +} + +static uint32_t operand_vzimm11(rv_inst inst) +{ + return (inst << 33) >> 53; +} + +static uint32_t operand_vzimm10(rv_inst inst) +{ + return (inst << 34) >> 54; +} + +static uint32_t operand_vzimm6(rv_inst inst) +{ + return ((inst << 37) >> 63) << 5 | + ((inst << 44) >> 59); +} + static uint32_t operand_bs(rv_inst inst) { return (inst << 32) >> 62; @@ -2527,10 +4173,76 @@ static uint32_t operand_rnum(rv_inst inst) return (inst << 40) >> 60; } +static uint32_t operand_vm(rv_inst inst) +{ + return (inst << 38) >> 63; +} + +static uint32_t operand_uimm_c_lb(rv_inst inst) +{ + return (((inst << 58) >> 63) << 1) | + ((inst << 57) >> 63); +} + +static uint32_t operand_uimm_c_lh(rv_inst inst) +{ + return (((inst << 58) >> 63) << 1); +} + +static uint32_t operand_zcmp_spimm(rv_inst inst) +{ + return ((inst << 60) >> 62) << 4; +} + +static uint32_t operand_zcmp_rlist(rv_inst inst) +{ + return ((inst << 56) >> 60); +} + +static uint32_t operand_imm6(rv_inst inst) +{ + return (inst << 38) >> 60; +} + +static uint32_t operand_imm2(rv_inst inst) +{ + return (inst << 37) >> 62; +} + +static uint32_t operand_immh(rv_inst inst) +{ + return (inst << 32) >> 58; +} + +static uint32_t operand_imml(rv_inst inst) +{ + return (inst << 38) >> 58; +} + +static uint32_t calculate_stack_adj(rv_isa isa, uint32_t rlist, uint32_t spimm) +{ + int xlen_bytes_log2 = isa == rv64 ? 3 : 2; + int regs = rlist == 15 ? 13 : rlist - 3; + uint32_t stack_adj_base = ROUND_UP(regs << xlen_bytes_log2, 16); + return stack_adj_base + spimm; +} + +static uint32_t operand_zcmp_stack_adj(rv_inst inst, rv_isa isa) +{ + return calculate_stack_adj(isa, operand_zcmp_rlist(inst), + operand_zcmp_spimm(inst)); +} + +static uint32_t operand_tbl_index(rv_inst inst) +{ + return ((inst << 54) >> 56); +} + /* decode operands */ -static void decode_inst_operands(rv_decode *dec) +static void decode_inst_operands(rv_decode *dec, rv_isa isa) { + const rv_opcode_data *opcode_data = dec->opcode_data; rv_inst inst = dec->inst; dec->codec = opcode_data[dec->op].codec; switch (dec->codec) { @@ -2652,7 +4364,7 @@ static void decode_inst_operands(rv_decode *dec) case rv_codec_cb_sh6: dec->rd = dec->rs1 = operand_crs1rdq(inst) + 8; dec->rs2 = rv_ireg_zero; - dec->imm = operand_cimmsh6(inst); + dec->imm = operand_cimmshr6(inst, isa); break; case rv_codec_ci: dec->rd = dec->rs1 = operand_crs1rd(inst); @@ -2667,7 +4379,7 @@ static void decode_inst_operands(rv_decode *dec) case rv_codec_ci_sh6: dec->rd = dec->rs1 = operand_crs1rd(inst); dec->rs2 = rv_ireg_zero; - dec->imm = operand_cimmsh6(inst); + dec->imm = operand_cimmshl6(inst, isa); break; case rv_codec_ci_16sp: dec->rd = rv_ireg_sp; @@ -2816,6 +4528,103 @@ static void decode_inst_operands(rv_decode *dec) dec->rs1 = operand_rs1(inst); dec->rnum = operand_rnum(inst); break; + case rv_codec_v_r: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = operand_rs2(inst); + dec->vm = operand_vm(inst); + break; + case rv_codec_v_ldst: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->vm = operand_vm(inst); + break; + case rv_codec_v_i: + dec->rd = operand_rd(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = operand_vimm(inst); + dec->vm = operand_vm(inst); + break; + case rv_codec_vror_vi: + dec->rd = operand_rd(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = operand_vzimm6(inst); + dec->vm = operand_vm(inst); + break; + case rv_codec_vsetvli: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->vzimm = operand_vzimm11(inst); + break; + case rv_codec_vsetivli: + dec->rd = operand_rd(inst); + dec->imm = operand_vimm(inst); + dec->vzimm = operand_vzimm10(inst); + break; + case rv_codec_zcb_lb: + dec->rs1 = operand_crs1q(inst) + 8; + dec->rs2 = operand_crs2q(inst) + 8; + dec->imm = operand_uimm_c_lb(inst); + break; + case rv_codec_zcb_lh: + dec->rs1 = operand_crs1q(inst) + 8; + dec->rs2 = operand_crs2q(inst) + 8; + dec->imm = operand_uimm_c_lh(inst); + break; + case rv_codec_zcb_ext: + dec->rd = operand_crs1q(inst) + 8; + break; + case rv_codec_zcb_mul: + dec->rd = operand_crs1rdq(inst) + 8; + dec->rs2 = operand_crs2q(inst) + 8; + break; + case rv_codec_zcmp_cm_pushpop: + dec->imm = operand_zcmp_stack_adj(inst, isa); + dec->rlist = operand_zcmp_rlist(inst); + break; + case rv_codec_zcmp_cm_mv: + dec->rd = operand_sreg1(inst); + dec->rs2 = operand_sreg2(inst); + break; + case rv_codec_zcmt_jt: + dec->imm = operand_tbl_index(inst); + break; + case rv_codec_fli: + dec->rd = operand_rd(inst); + dec->imm = operand_rs1(inst); + break; + case rv_codec_r2_imm5: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->imm = operand_rs2(inst); + break; + case rv_codec_r2: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + break; + case rv_codec_r2_imm6: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->imm = operand_imm6(inst); + break; + case rv_codec_r_imm2: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = operand_imm2(inst); + break; + case rv_codec_r2_immhl: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->imm = operand_immh(inst); + dec->imm1 = operand_imml(inst); + break; + case rv_codec_r2_imm2_imm5: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->imm = sextract32(operand_rs2(inst), 0, 5); + dec->imm1 = operand_imm2(inst); + break; }; } @@ -2930,7 +4739,8 @@ static size_t inst_length(rv_inst inst) { /* NOTE: supports maximum instruction size of 64-bits */ - /* instruction length coding + /* + * instruction length coding * * aa - 16 bit aa != 11 * bbb11 - 32 bit bbb != 111 @@ -2957,6 +4767,7 @@ static void append(char *s1, const char *s2, size_t n) static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) { + const rv_opcode_data *opcode_data = dec->opcode_data; char tmp[64]; const char *fmt; @@ -2975,6 +4786,9 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) case ')': append(buf, ")", buflen); break; + case '-': + append(buf, "-", buflen); + break; case 'b': snprintf(tmp, sizeof(tmp), "%d", dec->bs); append(buf, tmp, buflen); @@ -2993,16 +4807,24 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) append(buf, rv_ireg_name_sym[dec->rs2], buflen); break; case '3': - append(buf, rv_freg_name_sym[dec->rd], buflen); + append(buf, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rd] : + rv_freg_name_sym[dec->rd], + buflen); break; case '4': - append(buf, rv_freg_name_sym[dec->rs1], buflen); + append(buf, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rs1] : + rv_freg_name_sym[dec->rs1], + buflen); break; case '5': - append(buf, rv_freg_name_sym[dec->rs2], buflen); + append(buf, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rs2] : + rv_freg_name_sym[dec->rs2], + buflen); break; case '6': - append(buf, rv_freg_name_sym[dec->rs3], buflen); + append(buf, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rs3] : + rv_freg_name_sym[dec->rs3], + buflen); break; case '7': snprintf(tmp, sizeof(tmp), "%d", dec->rs1); @@ -3012,6 +4834,14 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) snprintf(tmp, sizeof(tmp), "%d", dec->imm); append(buf, tmp, buflen); break; + case 'u': + snprintf(tmp, sizeof(tmp), "%u", ((uint32_t)dec->imm & 0b111111)); + append(buf, tmp, buflen); + break; + case 'j': + snprintf(tmp, sizeof(tmp), "%d", dec->imm1); + append(buf, tmp, buflen); + break; case 'o': snprintf(tmp, sizeof(tmp), "%d", dec->imm); append(buf, tmp, buflen); @@ -3022,6 +4852,19 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) dec->pc + dec->imm); append(buf, tmp, buflen); break; + case 'U': + fmt++; + snprintf(tmp, sizeof(tmp), "%d", dec->imm >> 12); + append(buf, tmp, buflen); + if (*fmt == 'o') { + while (strlen(buf) < tab * 2) { + append(buf, " ", buflen); + } + snprintf(tmp, sizeof(tmp), "# 0x%" PRIx64, + dec->pc + dec->imm); + append(buf, tmp, buflen); + } + break; case 'c': { const char *name = csr_name(dec->imm & 0xfff); if (name) { @@ -3100,6 +4943,81 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) append(buf, ".rl", buflen); } break; + case 'l': + append(buf, ",v0", buflen); + break; + case 'm': + if (dec->vm == 0) { + append(buf, ",v0.t", buflen); + } + break; + case 'D': + append(buf, rv_vreg_name_sym[dec->rd], buflen); + break; + case 'E': + append(buf, rv_vreg_name_sym[dec->rs1], buflen); + break; + case 'F': + append(buf, rv_vreg_name_sym[dec->rs2], buflen); + break; + case 'G': + append(buf, rv_vreg_name_sym[dec->rs3], buflen); + break; + case 'v': { + char nbuf[32] = {0}; + const int sew = 1 << (((dec->vzimm >> 3) & 0b111) + 3); + sprintf(nbuf, "%d", sew); + const int lmul = dec->vzimm & 0b11; + const int flmul = (dec->vzimm >> 2) & 1; + const char *vta = (dec->vzimm >> 6) & 1 ? "ta" : "tu"; + const char *vma = (dec->vzimm >> 7) & 1 ? "ma" : "mu"; + append(buf, "e", buflen); + append(buf, nbuf, buflen); + append(buf, ",m", buflen); + if (flmul) { + switch (lmul) { + case 3: + sprintf(nbuf, "f2"); + break; + case 2: + sprintf(nbuf, "f4"); + break; + case 1: + sprintf(nbuf, "f8"); + break; + } + append(buf, nbuf, buflen); + } else { + sprintf(nbuf, "%d", 1 << lmul); + append(buf, nbuf, buflen); + } + append(buf, ",", buflen); + append(buf, vta, buflen); + append(buf, ",", buflen); + append(buf, vma, buflen); + break; + } + case 'x': { + switch (dec->rlist) { + case 4: + snprintf(tmp, sizeof(tmp), "{ra}"); + break; + case 5: + snprintf(tmp, sizeof(tmp), "{ra, s0}"); + break; + case 15: + snprintf(tmp, sizeof(tmp), "{ra, s0-s11}"); + break; + default: + snprintf(tmp, sizeof(tmp), "{ra, s0-s%d}", dec->rlist - 5); + break; + } + append(buf, tmp, buflen); + break; + } + case 'h': + append(buf, rv_fli_name_const[dec->imm], buflen); + break; default: break; } @@ -3111,6 +5029,7 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) static void decode_inst_lift_pseudo(rv_decode *dec) { + const rv_opcode_data *opcode_data = dec->opcode_data; const rv_comp_data *comp_data = opcode_data[dec->op].pseudo; if (!comp_data) { return; @@ -3129,6 +5048,7 @@ static void decode_inst_lift_pseudo(rv_decode *dec) static void decode_inst_decompress_rv32(rv_decode *dec) { + const rv_opcode_data *opcode_data = dec->opcode_data; int decomp_op = opcode_data[dec->op].decomp_rv32; if (decomp_op != rv_op_illegal) { if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) @@ -3143,6 +5063,7 @@ static void decode_inst_decompress_rv32(rv_decode *dec) static void decode_inst_decompress_rv64(rv_decode *dec) { + const rv_opcode_data *opcode_data = dec->opcode_data; int decomp_op = opcode_data[dec->op].decomp_rv64; if (decomp_op != rv_op_illegal) { if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) @@ -3157,6 +5078,7 @@ static void decode_inst_decompress_rv64(rv_decode *dec) static void decode_inst_decompress_rv128(rv_decode *dec) { + const rv_opcode_data *opcode_data = dec->opcode_data; int decomp_op = opcode_data[dec->op].decomp_rv128; if (decomp_op != rv_op_illegal) { if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) @@ -3187,16 +5109,55 @@ static void decode_inst_decompress(rv_decode *dec, rv_isa isa) /* disassemble instruction */ static void -disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst) +disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst, + RISCVCPUConfig *cfg) { rv_decode dec = { 0 }; dec.pc = pc; dec.inst = inst; - decode_inst_opcode(&dec, isa); - decode_inst_operands(&dec); + dec.cfg = cfg; + + static const struct { + bool (*guard_func)(const RISCVCPUConfig *); + const rv_opcode_data *opcode_data; + void (*decode_func)(rv_decode *, rv_isa); + } decoders[] = { + { always_true_p, rvi_opcode_data, decode_inst_opcode }, + { has_xtheadba_p, xthead_opcode_data, decode_xtheadba }, + { has_xtheadbb_p, xthead_opcode_data, decode_xtheadbb }, + { has_xtheadbs_p, xthead_opcode_data, decode_xtheadbs }, + { has_xtheadcmo_p, xthead_opcode_data, decode_xtheadcmo }, + { has_xtheadcondmov_p, xthead_opcode_data, decode_xtheadcondmov }, + { has_xtheadfmemidx_p, xthead_opcode_data, decode_xtheadfmemidx }, + { has_xtheadfmv_p, xthead_opcode_data, decode_xtheadfmv }, + { has_xtheadmac_p, xthead_opcode_data, decode_xtheadmac }, + { has_xtheadmemidx_p, xthead_opcode_data, decode_xtheadmemidx }, + { has_xtheadmempair_p, xthead_opcode_data, decode_xtheadmempair }, + { has_xtheadsync_p, xthead_opcode_data, decode_xtheadsync }, + { has_XVentanaCondOps_p, ventana_opcode_data, decode_xventanacondops }, + }; + + for (size_t i = 0; i < ARRAY_SIZE(decoders); i++) { + bool (*guard_func)(const RISCVCPUConfig *) = decoders[i].guard_func; + const rv_opcode_data *opcode_data = decoders[i].opcode_data; + void (*decode_func)(rv_decode *, rv_isa) = decoders[i].decode_func; + + if (guard_func(cfg)) { + dec.opcode_data = opcode_data; + decode_func(&dec, isa); + if (dec.op != rv_op_illegal) + break; + } + } + + if (dec.op == rv_op_illegal) { + dec.opcode_data = rvi_opcode_data; + } + + decode_inst_operands(&dec, isa); decode_inst_decompress(&dec, isa); decode_inst_lift_pseudo(&dec); - format_inst(buf, buflen, 16, &dec); + format_inst(buf, buflen, 24, &dec); } #define INST_FMT_2 "%04" PRIx64 " " @@ -3231,22 +5192,25 @@ print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) } } - switch (len) { - case 2: - (*info->fprintf_func)(info->stream, INST_FMT_2, inst); - break; - case 4: - (*info->fprintf_func)(info->stream, INST_FMT_4, inst); - break; - case 6: - (*info->fprintf_func)(info->stream, INST_FMT_6, inst); - break; - default: - (*info->fprintf_func)(info->stream, INST_FMT_8, inst); - break; + if (info->show_opcodes) { + switch (len) { + case 2: + (*info->fprintf_func)(info->stream, INST_FMT_2, inst); + break; + case 4: + (*info->fprintf_func)(info->stream, INST_FMT_4, inst); + break; + case 6: + (*info->fprintf_func)(info->stream, INST_FMT_6, inst); + break; + default: + (*info->fprintf_func)(info->stream, INST_FMT_8, inst); + break; + } } - disasm_inst(buf, sizeof(buf), isa, memaddr, inst); + disasm_inst(buf, sizeof(buf), isa, memaddr, inst, + (RISCVCPUConfig *)info->target_info); (*info->fprintf_func)(info->stream, "%s", buf); return len; diff --git a/disas/riscv.h b/disas/riscv.h new file mode 100644 index 0000000000..16a08e4895 --- /dev/null +++ b/disas/riscv.h @@ -0,0 +1,305 @@ +/* + * QEMU disassembler -- RISC-V specific header. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DISAS_RISCV_H +#define DISAS_RISCV_H + +#include "target/riscv/cpu_cfg.h" + +/* types */ + +typedef uint64_t rv_inst; +typedef uint16_t rv_opcode; + +/* enums */ + +typedef enum { + rv32, + rv64, + rv128 +} rv_isa; + +typedef enum { + rv_rm_rne = 0, + rv_rm_rtz = 1, + rv_rm_rdn = 2, + rv_rm_rup = 3, + rv_rm_rmm = 4, + rv_rm_dyn = 7, +} rv_rm; + +typedef enum { + rv_fence_i = 8, + rv_fence_o = 4, + rv_fence_r = 2, + rv_fence_w = 1, +} rv_fence; + +typedef enum { + rv_ireg_zero, + rv_ireg_ra, + rv_ireg_sp, + rv_ireg_gp, + rv_ireg_tp, + rv_ireg_t0, + rv_ireg_t1, + rv_ireg_t2, + rv_ireg_s0, + rv_ireg_s1, + rv_ireg_a0, + rv_ireg_a1, + rv_ireg_a2, + rv_ireg_a3, + rv_ireg_a4, + rv_ireg_a5, + rv_ireg_a6, + rv_ireg_a7, + rv_ireg_s2, + rv_ireg_s3, + rv_ireg_s4, + rv_ireg_s5, + rv_ireg_s6, + rv_ireg_s7, + rv_ireg_s8, + rv_ireg_s9, + rv_ireg_s10, + rv_ireg_s11, + rv_ireg_t3, + rv_ireg_t4, + rv_ireg_t5, + rv_ireg_t6, +} rv_ireg; + +typedef enum { + rvc_end, + rvc_rd_eq_ra, + rvc_rd_eq_x0, + rvc_rs1_eq_x0, + rvc_rs2_eq_x0, + rvc_rs2_eq_rs1, + rvc_rs1_eq_ra, + rvc_imm_eq_zero, + rvc_imm_eq_n1, + rvc_imm_eq_p1, + rvc_csr_eq_0x001, + rvc_csr_eq_0x002, + rvc_csr_eq_0x003, + rvc_csr_eq_0xc00, + rvc_csr_eq_0xc01, + rvc_csr_eq_0xc02, + rvc_csr_eq_0xc80, + rvc_csr_eq_0xc81, + rvc_csr_eq_0xc82, +} rvc_constraint; + +typedef enum { + rv_codec_illegal, + rv_codec_none, + rv_codec_u, + rv_codec_uj, + rv_codec_i, + rv_codec_i_sh5, + rv_codec_i_sh6, + rv_codec_i_sh7, + rv_codec_i_csr, + rv_codec_s, + rv_codec_sb, + rv_codec_r, + rv_codec_r_m, + rv_codec_r4_m, + rv_codec_r_a, + rv_codec_r_l, + rv_codec_r_f, + rv_codec_cb, + rv_codec_cb_imm, + rv_codec_cb_sh5, + rv_codec_cb_sh6, + rv_codec_ci, + rv_codec_ci_sh5, + rv_codec_ci_sh6, + rv_codec_ci_16sp, + rv_codec_ci_lwsp, + rv_codec_ci_ldsp, + rv_codec_ci_lqsp, + rv_codec_ci_li, + rv_codec_ci_lui, + rv_codec_ci_none, + rv_codec_ciw_4spn, + rv_codec_cj, + rv_codec_cj_jal, + rv_codec_cl_lw, + rv_codec_cl_ld, + rv_codec_cl_lq, + rv_codec_cr, + rv_codec_cr_mv, + rv_codec_cr_jalr, + rv_codec_cr_jr, + rv_codec_cs, + rv_codec_cs_sw, + rv_codec_cs_sd, + rv_codec_cs_sq, + rv_codec_css_swsp, + rv_codec_css_sdsp, + rv_codec_css_sqsp, + rv_codec_k_bs, + rv_codec_k_rnum, + rv_codec_v_r, + rv_codec_v_ldst, + rv_codec_v_i, + rv_codec_vsetvli, + rv_codec_vsetivli, + rv_codec_vror_vi, + rv_codec_zcb_ext, + rv_codec_zcb_mul, + rv_codec_zcb_lb, + rv_codec_zcb_lh, + rv_codec_zcmp_cm_pushpop, + rv_codec_zcmp_cm_mv, + rv_codec_zcmt_jt, + rv_codec_r2_imm5, + rv_codec_r2, + rv_codec_r2_imm6, + rv_codec_r_imm2, + rv_codec_r2_immhl, + rv_codec_r2_imm2_imm5, + rv_codec_fli, +} rv_codec; + +/* structures */ + +typedef struct { + const int op; + const rvc_constraint *constraints; +} rv_comp_data; + +typedef struct { + const char * const name; + const rv_codec codec; + const char * const format; + const rv_comp_data *pseudo; + const short decomp_rv32; + const short decomp_rv64; + const short decomp_rv128; + const short decomp_data; +} rv_opcode_data; + +typedef struct { + RISCVCPUConfig *cfg; + uint64_t pc; + uint64_t inst; + const rv_opcode_data *opcode_data; + int32_t imm; + int32_t imm1; + uint16_t op; + uint8_t codec; + uint8_t rd; + uint8_t rs1; + uint8_t rs2; + uint8_t rs3; + uint8_t rm; + uint8_t pred; + uint8_t succ; + uint8_t aq; + uint8_t rl; + uint8_t bs; + uint8_t rnum; + uint8_t vm; + uint32_t vzimm; + uint8_t rlist; +} rv_decode; + +enum { + rv_op_illegal = 0 +}; + +enum { + rvcd_imm_nz = 0x1 +}; + +/* instruction formats */ + +#define rv_fmt_none "O\t" +#define rv_fmt_rs1 "O\t1" +#define rv_fmt_offset "O\to" +#define rv_fmt_pred_succ "O\tp,s" +#define rv_fmt_rs1_rs2 "O\t1,2" +#define rv_fmt_rd_imm "O\t0,i" +#define rv_fmt_rd_uimm "O\t0,Ui" +#define rv_fmt_rd_offset "O\t0,o" +#define rv_fmt_rd_uoffset "O\t0,Uo" +#define rv_fmt_rd_rs1_rs2 "O\t0,1,2" +#define rv_fmt_frd_rs1 "O\t3,1" +#define rv_fmt_frd_rs1_rs2 "O\t3,1,2" +#define rv_fmt_frd_frs1 "O\t3,4" +#define rv_fmt_rd_frs1 "O\t0,4" +#define rv_fmt_rd_frs1_frs2 "O\t0,4,5" +#define rv_fmt_frd_frs1_frs2 "O\t3,4,5" +#define rv_fmt_rm_frd_frs1 "O\tr,3,4" +#define rv_fmt_rm_frd_rs1 "O\tr,3,1" +#define rv_fmt_rm_rd_frs1 "O\tr,0,4" +#define rv_fmt_rm_frd_frs1_frs2 "O\tr,3,4,5" +#define rv_fmt_rm_frd_frs1_frs2_frs3 "O\tr,3,4,5,6" +#define rv_fmt_rd_rs1_imm "O\t0,1,i" +#define rv_fmt_rd_rs1_offset "O\t0,1,i" +#define rv_fmt_rd_offset_rs1 "O\t0,i(1)" +#define rv_fmt_frd_offset_rs1 "O\t3,i(1)" +#define rv_fmt_rd_csr_rs1 "O\t0,c,1" +#define rv_fmt_rd_csr_zimm "O\t0,c,7" +#define rv_fmt_rs2_offset_rs1 "O\t2,i(1)" +#define rv_fmt_frs2_offset_rs1 "O\t5,i(1)" +#define rv_fmt_rs1_rs2_offset "O\t1,2,o" +#define rv_fmt_rs2_rs1_offset "O\t2,1,o" +#define rv_fmt_aqrl_rd_rs2_rs1 "OAR\t0,2,(1)" +#define rv_fmt_aqrl_rd_rs1 "OAR\t0,(1)" +#define rv_fmt_rd "O\t0" +#define rv_fmt_rd_zimm "O\t0,7" +#define rv_fmt_rd_rs1 "O\t0,1" +#define rv_fmt_rd_rs2 "O\t0,2" +#define rv_fmt_rs1_offset "O\t1,o" +#define rv_fmt_rs2_offset "O\t2,o" +#define rv_fmt_rs1_rs2_bs "O\t1,2,b" +#define rv_fmt_rd_rs1_rnum "O\t0,1,n" +#define rv_fmt_ldst_vd_rs1_vm "O\tD,(1)m" +#define rv_fmt_ldst_vd_rs1_rs2_vm "O\tD,(1),2m" +#define rv_fmt_ldst_vd_rs1_vs2_vm "O\tD,(1),Fm" +#define rv_fmt_vd_vs2_vs1 "O\tD,F,E" +#define rv_fmt_vd_vs2_vs1_vl "O\tD,F,El" +#define rv_fmt_vd_vs2_vs1_vm "O\tD,F,Em" +#define rv_fmt_vd_vs2_rs1_vl "O\tD,F,1l" +#define rv_fmt_vd_vs2_fs1_vl "O\tD,F,4l" +#define rv_fmt_vd_vs2_rs1_vm "O\tD,F,1m" +#define rv_fmt_vd_vs2_fs1_vm "O\tD,F,4m" +#define rv_fmt_vd_vs2_imm_vl "O\tD,F,il" +#define rv_fmt_vd_vs2_imm_vm "O\tD,F,im" +#define rv_fmt_vd_vs2_uimm "O\tD,F,u" +#define rv_fmt_vd_vs2_uimm_vm "O\tD,F,um" +#define rv_fmt_vd_vs1_vs2_vm "O\tD,E,Fm" +#define rv_fmt_vd_rs1_vs2_vm "O\tD,1,Fm" +#define rv_fmt_vd_fs1_vs2_vm "O\tD,4,Fm" +#define rv_fmt_vd_vs1 "O\tD,E" +#define rv_fmt_vd_rs1 "O\tD,1" +#define rv_fmt_vd_fs1 "O\tD,4" +#define rv_fmt_vd_imm "O\tD,i" +#define rv_fmt_vd_vs2 "O\tD,F" +#define rv_fmt_vd_vs2_vm "O\tD,Fm" +#define rv_fmt_rd_vs2_vm "O\t0,Fm" +#define rv_fmt_rd_vs2 "O\t0,F" +#define rv_fmt_fd_vs2 "O\t3,F" +#define rv_fmt_vd_vm "O\tDm" +#define rv_fmt_vsetvli "O\t0,1,v" +#define rv_fmt_vsetivli "O\t0,u,v" +#define rv_fmt_rs1_rs2_zce_ldst "O\t2,i(1)" +#define rv_fmt_push_rlist "O\tx,-i" +#define rv_fmt_pop_rlist "O\tx,i" +#define rv_fmt_zcmt_index "O\ti" +#define rv_fmt_rd_rs1_rs2_imm "O\t0,1,2,i" +#define rv_fmt_frd_rs1_rs2_imm "O\t3,1,2,i" +#define rv_fmt_rd_rs1_immh_imml "O\t0,1,i,j" +#define rv_fmt_rd_rs1_immh_imml_addr "O\t0,(1),i,j" +#define rv_fmt_rd2_imm "O\t0,2,(1),i" +#define rv_fmt_fli "O\t3,h" + +#endif /* DISAS_RISCV_H */ diff --git a/docs/COLO-FT.txt b/docs/COLO-FT.txt index 8ec653f81c..2e760a4aee 100644 --- a/docs/COLO-FT.txt +++ b/docs/COLO-FT.txt @@ -210,6 +210,7 @@ children.0=childs0 \ 3. On Secondary VM's QEMU monitor, issue command {"execute":"qmp_capabilities"} +{"execute": "migrate-set-capabilities", "arguments": {"capabilities": [ {"capability": "x-colo", "state": 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 } } diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index 1958edb430..8fd7da140a 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -41,18 +41,18 @@ Those hosts are officially supported, with various accelerators: - Accelerators * - Arm - kvm (64 bit only), tcg, xen - * - MIPS + * - MIPS (little endian only) - kvm, tcg * - PPC - kvm, tcg * - RISC-V - - tcg + - kvm, tcg * - s390x - kvm, tcg * - SPARC - tcg * - x86 - - hax, hvf (64 bit only), kvm, nvmm, tcg, whpx (64 bit only), xen + - hvf (64 bit only), kvm, nvmm, tcg, whpx (64 bit only), xen Other host architectures are not supported. It is possible to build QEMU system emulation on an unsupported host architecture using the configure @@ -67,11 +67,15 @@ Non-supported architectures may be removed in the future following the Linux OS, macOS, FreeBSD, NetBSD, OpenBSD ----------------------------------------- -The project aims to support the most recent major version at all times. Support +The project aims to support the most recent major version at all times for +up to five years after its initial release. Support for the previous major version will be dropped 2 years after the new major version is released or when the vendor itself drops support, whichever comes first. In this context, third-party efforts to extend the lifetime of a distro -are not considered, even when they are endorsed by the vendor (eg. Debian LTS). +are not considered, even when they are endorsed by the vendor (eg. Debian LTS); +the same is true of repositories that contain packages backported from later +releases (e.g. Debian backports). Within each major release, only the most +recent minor release is considered. For the purposes of identifying supported software versions available on Linux, the project will look at CentOS, Debian, Fedora, openSUSE, RHEL, SLES and @@ -83,19 +87,61 @@ respective ports repository, while NetBSD will use the pkgsrc repository. For macOS, `Homebrew`_ will be used, although `MacPorts`_ is expected to carry similar versions. +Some build dependencies may follow less conservative rules: + +Python runtime + Distributions with long-term support often provide multiple versions + of the Python runtime. While QEMU will initially aim to support the + distribution's default runtime, it may later increase its minimum version + to any newer python that is available as an option from the vendor. + In this case, it will be necessary to use the ``--python`` command line + option of the ``configure`` script to point QEMU to a supported + version of the Python runtime. + + As of QEMU |version|, the minimum supported version of Python is 3.7. + +Python build dependencies + Some of QEMU's build dependencies are written in Python. Usually these + are only packaged by distributions for the default Python runtime. + If QEMU bumps its minimum Python version and a non-default runtime is + required, it may be necessary to fetch python modules from the Python + Package Index (PyPI) via ``pip``, in order to build QEMU. + +Optional build dependencies + Build components whose absence does not affect the ability to build + QEMU may not be available in distros, or may be too old for QEMU's + requirements. Many of these, such as the Avocado testing framework + or various linters, are written in Python and therefore can also + be installed using ``pip``. Cross compilers are another example + of optional build-time dependency; in this case it is possible to + download them from repositories such as EPEL, to use container-based + cross compilation using ``docker`` or ``podman``, or to use pre-built + binaries distributed with QEMU. + + Windows ------- The project aims to support the two most recent versions of Windows that are still supported by the vendor. The minimum Windows API that is currently -targeted is "Windows 7", so theoretically the QEMU binaries can still be run +targeted is "Windows 8", so theoretically the QEMU binaries can still be run on older versions of Windows, too. However, such old versions of Windows are not tested anymore, so it is recommended to use one of the latest versions of Windows instead. The project supports building QEMU with current versions of the MinGW -toolchain, either hosted on Linux (Debian/Fedora) or via MSYS2 on Windows. +toolchain, either hosted on Linux (Debian/Fedora) or via `MSYS2`_ on Windows. +A more recent Windows version is always preferred as it is less likely to have +problems with building via MSYS2. The building process of QEMU involves some +Python scripts that call os.symlink() which needs special attention for the +build process to successfully complete. On newer versions of Windows 10, +unprivileged accounts can create symlinks if Developer Mode is enabled. +When Developer Mode is not available/enabled, the SeCreateSymbolicLinkPrivilege +privilege is required, or the process must be run as an administrator. + +Only 64-bit Windows is supported. .. _Homebrew: https://brew.sh/ .. _MacPorts: https://www.macports.org/ +.. _MSYS2: https://www.msys2.org/ .. _Repology: https://repology.org/ diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index aa2e320207..7b548519b5 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -23,28 +23,6 @@ deprecated. System emulator command line arguments -------------------------------------- -``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=`` property if you plan to -transmit audio through the VNC protocol. - -``-chardev`` backend aliases ``tty`` and ``parport`` (since 6.0) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -``tty`` and ``parport`` are aliases that will be removed. Instead, the -actual backend names ``serial`` and ``parallel`` should be used. - Short-form boolean options (since 6.0) '''''''''''''''''''''''''''''''''''''' @@ -58,50 +36,6 @@ and will cause a warning. The replacement for the ``nodelay`` short-form boolean option is ``nodelay=on`` rather than ``delay=off``. -Userspace local APIC with KVM (x86, since 6.0) -'''''''''''''''''''''''''''''''''''''''''''''' - -Using ``-M kernel-irqchip=off`` with x86 machine types that include a local -APIC is deprecated. The ``split`` setting is supported, as is using -``-M kernel-irqchip=off`` with the ISA PC machine type. - -hexadecimal sizes with scaling multipliers (since 6.0) -'''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Input parameters that take a size value should only use a size suffix -(such as 'k' or 'M') when the base is written in decimal, and not when -the value is hexadecimal. That is, '0x20M' is deprecated, and should -be written either as '32M' or as '0x2000000'. - -``-spice password=string`` (since 6.0) -'''''''''''''''''''''''''''''''''''''' - -This option is insecure because the SPICE password remains visible in -the process listing. This is replaced by the new ``password-secret`` -option which lets the password be securely provided on the command -line using a ``secret`` object instance. - -``-watchdog`` (since 6.2) -''''''''''''''''''''''''' - -Use ``-device`` instead. - -``-smp`` ("parameter=0" SMP configurations) (since 6.2) -''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Specified CPU topology parameters must be greater than zero. - -In the SMP configuration, users should either provide a CPU topology -parameter with a reasonable value (greater than zero) or just omit it -and QEMU will compute the missing value. - -However, historically it was implicitly allowed for users to provide -a parameter with zero value, which is meaningless and could also possibly -cause unexpected results in the -smp parsing. So support for this kind of -configurations (e.g. -smp 8,sockets=0) is deprecated since 6.2 and will -be removed in the near future, users have to ensure that all the topology -members described with -smp are greater than zero. - Plugin argument passing through ``arg=`` (since 6.1) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -113,12 +47,29 @@ as short-form boolean values, and passed to plugins as ``arg_name=on``. However, short-form booleans are deprecated and full explicit ``arg_name=on`` form is preferred. -``-drive if=none`` for the sifive_u OTP device (since 6.2) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``-smp`` (Unsupported "parameter=1" SMP configurations) (since 9.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -Using ``-drive if=none`` to configure the OTP device of the sifive_u -RISC-V machine is deprecated. Use ``-drive if=pflash`` instead. +Specified CPU topology parameters must be supported by the machine. +In the SMP configuration, users should provide the CPU topology parameters that +are supported by the target machine. + +However, historically it was allowed for users to specify the unsupported +topology parameter as "1", which is meaningless. So support for this kind of +configurations (e.g. -smp drawers=1,books=1,clusters=1 for x86 PC machine) is +marked deprecated since 9.0, users have to ensure that all the topology members +described with -smp are supported by the target machine. + +User-mode emulator command line arguments +----------------------------------------- + +``-p`` (since 9.0) +'''''''''''''''''' + +The ``-p`` option pretends to control the host page size. However, +it is not possible to change the host page size, and using the +option only causes failures. QEMU Machine Protocol (QMP) commands ------------------------------------ @@ -191,67 +142,123 @@ accepted incorrect commands will return an error. Users should make sure that all arguments passed to ``device_add`` are consistent with the documented property types. -``query-sgx`` return value member ``section-size`` (since 7.0) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Member ``section-size`` in return value elements with meta-type ``uint64`` is -deprecated. Use ``sections`` instead. - - -``query-sgx-capabilities`` return value member ``section-size`` (since 7.0) -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Member ``section-size`` in return value elements with meta-type ``uint64`` is -deprecated. Use ``sections`` instead. - -System accelerators -------------------- - -MIPS ``Trap-and-Emul`` KVM support (since 6.0) -'''''''''''''''''''''''''''''''''''''''''''''' - -The MIPS ``Trap-and-Emul`` KVM host and guest support has been removed -from Linux upstream kernel, declare it deprecated. - -System emulator CPUS --------------------- - -MIPS ``I7200`` CPU Model (since 5.2) -'''''''''''''''''''''''''''''''''''' - -The ``I7200`` guest CPU relies on the nanoMIPS ISA, which is deprecated -(the ISA has never been upstreamed to a compiler toolchain). Therefore -this CPU is also deprecated. - - -QEMU API (QAPI) events ----------------------- +QEMU Machine Protocol (QMP) events +---------------------------------- ``MEM_UNPLUG_ERROR`` (since 6.2) '''''''''''''''''''''''''''''''''''''''''''''''''''''''' Use the more generic event ``DEVICE_UNPLUG_GUEST_ERROR`` instead. +``vcpu`` trace events (since 8.1) +''''''''''''''''''''''''''''''''' + +The ability to instrument QEMU helper functions with vCPU-aware trace +points was removed in 7.0. However QMP still exposed the vcpu +parameter. This argument has now been deprecated and the remaining +remaining trace points that used it are selected just by name. + +Host Architectures +------------------ + +BE MIPS (since 7.2) +''''''''''''''''''' + +As Debian 10 ("Buster") moved into LTS the big endian 32 bit version of +MIPS moved out of support making it hard to maintain our +cross-compilation CI tests of the architecture. As we no longer have +CI coverage support may bitrot away before the deprecation process +completes. The little endian variants of MIPS (both 32 and 64 bit) are +still a supported host architecture. + +System emulation on 32-bit x86 hosts (since 8.0) +'''''''''''''''''''''''''''''''''''''''''''''''' + +Support for 32-bit x86 host deployments is increasingly uncommon in mainstream +OS distributions given the widespread availability of 64-bit x86 hardware. +The QEMU project no longer considers 32-bit x86 support for system emulation to +be an effective use of its limited resources, and thus intends to discontinue +it. Since all recent x86 hardware from the past >10 years is capable of the +64-bit x86 extensions, a corresponding 64-bit OS should be used instead. + + +System emulator CPUs +-------------------- + +Nios II CPU (since 8.2) +''''''''''''''''''''''' + +The Nios II architecture is orphan. The ``nios2`` guest CPU support is +deprecated and will be removed in a future version of QEMU. + +``power5+`` and ``power7+`` CPU names (since 9.0) +''''''''''''''''''''''''''''''''''''''''''''''''' + +The character "+" in device (and thus also CPU) names is not allowed +in the QEMU object model anymore. ``power5+``, ``power5+_v2.1``, +``power7+`` and ``power7+_v2.1`` are currently still supported via +an alias, but for consistency these will get removed in a future +release, too. Use ``power5p_v2.1`` and ``power7p_v2.1`` instead. + +CRIS CPU architecture (since 9.0) +''''''''''''''''''''''''''''''''' + +The CRIS architecture was pulled from Linux in 4.17 and the compiler +is no longer packaged in any distro making it harder to run the +``check-tcg`` tests. Unless we can improve the testing situation there +is a chance the code will bitrot without anyone noticing. System emulator machines ------------------------ -PPC 405 ``taihu`` machine (since 7.0) -''''''''''''''''''''''''''''''''''''' +Arm ``virt`` machine ``dtb-kaslr-seed`` property (since 7.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -The PPC 405 CPU is a system-on-a-chip, so all 405 machines are very similar, -except for some external periphery. However, the periphery of the ``taihu`` -machine is hardly emulated at all (e.g. neither the LCD nor the USB part had -been implemented), so there is not much value added by this board. Use the -``ref405ep`` machine instead. +The ``dtb-kaslr-seed`` property on the ``virt`` board has been +deprecated; use the new name ``dtb-randomness`` instead. The new name +better reflects the way this property affects all random data within +the device tree blob, not just the ``kaslr-seed`` node. -``pc-i440fx-1.4`` up to ``pc-i440fx-1.7`` (since 7.0) +``pc-i440fx-2.0`` up to ``pc-i440fx-2.3`` (since 8.2) ''''''''''''''''''''''''''''''''''''''''''''''''''''' These old machine types are quite neglected nowadays and thus might have various pitfalls with regards to live migration. Use a newer machine type instead. +Nios II ``10m50-ghrd`` and ``nios2-generic-nommu`` machines (since 8.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The Nios II architecture is orphan. + +``shix`` (since 9.0) +'''''''''''''''''''' + +The machine is no longer in existence and has been long unmaintained +in QEMU. This also holds for the TC51828 16MiB flash that it uses. + +``pseries-2.1`` up to ``pseries-2.12`` (since 9.0) +'''''''''''''''''''''''''''''''''''''''''''''''''' + +Older pseries machines before version 3.0 have undergone many changes +to correct issues, mostly regarding migration compatibility. These are +no longer maintained and removing them will make the code easier to +read and maintain. Use versions 3.0 and above as a replacement. + +Arm machines ``akita``, ``borzoi``, ``cheetah``, ``connex``, ``mainstone``, ``n800``, ``n810``, ``spitz``, ``terrier``, ``tosa``, ``verdex``, ``z2`` (since 9.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +QEMU includes models of some machine types where the QEMU code that +emulates their SoCs is very old and unmaintained. This code is now +blocking our ability to move forward with various changes across +the codebase, and over many years nobody has been interested in +trying to modernise it. We don't expect any of these machines to have +a large number of users, because they're all modelling hardware that +has now passed away into history. We are therefore dropping support +for all machine types using the PXA2xx and OMAP2 SoCs. We are also +dropping the ``cheetah`` OMAP1 board, because we don't have any +test images for it and don't know of anybody who does; the ``sx1`` +and ``sx1-v1`` OMAP1 machines remain supported for now. Backend options --------------- @@ -287,20 +294,11 @@ 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. -``-device sga`` (since 6.2) -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ``sga`` device loads an option ROM for x86 targets which enables -SeaBIOS to send messages to the serial console. SeaBIOS 1.11.0 onwards -contains native support for this feature and thus use of the option -ROM approach is obsolete. The native SeaBIOS support can be activated -by using ``-machine graphics=off``. - ``-device nvme-ns,eui64-default=on|off`` (since 7.1) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In QEMU versions 6.1, 6.2 and 7.0, the ``nvme-ns`` generates an EUI-64 -identifer that is not globally unique. If an EUI-64 identifer is required, the +identifier that is not globally unique. If an EUI-64 identifier is required, the user must set it explicitly using the ``nvme-ns`` device parameter ``eui64``. ``-device nvme,use-intel-id=on|off`` (since 7.1) @@ -311,6 +309,81 @@ from Intel that was not properly allocated. Since version 5.2, the controller has used a properly allocated identifier. Deprecate the ``use-intel-id`` machine compatibility parameter. +``-device cxl-type3,memdev=xxxx`` (since 8.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``cxl-type3`` device initially only used a single memory backend. With +the addition of volatile memory support, it is now necessary to distinguish +between persistent and volatile memory backends. As such, memdev is deprecated +in favor of persistent-memdev. + +``-fsdev proxy`` and ``-virtfs proxy`` (since 8.1) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The 9p ``proxy`` filesystem backend driver has been deprecated and will be +removed (along with its proxy helper daemon) in a future version of QEMU. Please +use ``-fsdev local`` or ``-virtfs local`` for using the 9p ``local`` filesystem +backend, or alternatively consider deploying virtiofsd instead. + +The 9p ``proxy`` backend was originally developed as an alternative to the 9p +``local`` backend. The idea was to enhance security by dispatching actual low +level filesystem operations from 9p server (QEMU process) over to a separate +process (the virtfs-proxy-helper binary). However this alternative never gained +momentum. The proxy backend is much slower than the local backend, hasn't seen +any development in years, and showed to be less secure, especially due to the +fact that its helper daemon must be run as root, whereas with the local backend +QEMU is typically run as unprivileged user and allows to tighten behaviour by +mapping permissions et al by using its 'mapped' security model option. + +Nowadays it would make sense to reimplement the ``proxy`` backend by using +QEMU's ``vhost`` feature, which would eliminate the high latency costs under +which the 9p ``proxy`` backend currently suffers. However as of to date nobody +has indicated plans for such kind of reimplementation unfortunately. + +RISC-V 'any' CPU type ``-cpu any`` (since 8.2) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The 'any' CPU type was introduced back in 2018 and has been around since the +initial RISC-V QEMU port. Its usage has always been unclear: users don't know +what to expect from a CPU called 'any', and in fact the CPU does not do anything +special that isn't already done by the default CPUs rv32/rv64. + +After the introduction of the 'max' CPU type, RISC-V now has a good coverage +of generic CPUs: rv32 and rv64 as default CPUs and 'max' as a feature complete +CPU for both 32 and 64 bit builds. Users are then discouraged to use the 'any' +CPU type starting in 8.2. + +RISC-V CPU properties which start with capital 'Z' (since 8.2) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +All RISC-V CPU properties which start with capital 'Z' are being deprecated +starting in 8.2. The reason is that they were wrongly added with capital 'Z' +in the past. CPU properties were later added with lower-case names, which +is the format we want to use from now on. + +Users which try to use these deprecated properties will receive a warning +recommending to switch to their stable counterparts: + +- "Zifencei" should be replaced with "zifencei" +- "Zicsr" should be replaced with "zicsr" +- "Zihintntl" should be replaced with "zihintntl" +- "Zihintpause" should be replaced with "zihintpause" +- "Zawrs" should be replaced with "zawrs" +- "Zfa" should be replaced with "zfa" +- "Zfh" should be replaced with "zfh" +- "Zfhmin" should be replaced with "zfhmin" +- "Zve32f" should be replaced with "zve32f" +- "Zve64f" should be replaced with "zve64f" +- "Zve64d" should be replaced with "zve64d" + +``-device pvrdma`` and the rdma subsystem (since 8.2) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The pvrdma device and the whole rdma subsystem are in a bad shape and +without active maintenance. The QEMU project intends to remove this +device and subsystem from the code base in a future release without +replacement unless somebody steps up and improves the situation. + Block device options '''''''''''''''''''' @@ -337,15 +410,33 @@ The above, converted to the current supported format:: json:{"file.driver":"rbd", "file.pool":"rbd", "file.image":"name"} -linux-user mode CPUs --------------------- +``iscsi,password=xxx`` (since 8.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -MIPS ``I7200`` CPU (since 5.2) -'''''''''''''''''''''''''''''' +Specifying the iSCSI password in plain text on the command line using the +``password`` option is insecure. The ``password-secret`` option should be +used instead, to refer to a ``--object secret...`` instance that provides +a password via a file, or encrypted. + +Character device options +'''''''''''''''''''''''' + +Backend ``memory`` (since 9.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``memory`` is a deprecated synonym for ``ringbuf``. + +CPU device properties +''''''''''''''''''''' + +``pmu-num=n`` on RISC-V CPUs (since 8.2) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In order to support more flexible counter configurations this has been replaced +by a ``pmu-mask`` property. If set of counters is continuous then the mask can +be calculated with ``((2 ^ n) - 1) << 3``. The least significant three bits +must be left clear. -The ``I7200`` guest CPU relies on the nanoMIPS ISA, which is deprecated -(the ISA has never been upstreamed to a compiler toolchain). Therefore -this CPU is also deprecated. Backwards compatibility ----------------------- @@ -376,28 +467,65 @@ 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. -Guest Emulator ISAs -------------------- +QEMU guest agent +---------------- -nanoMIPS ISA -'''''''''''' +``--blacklist`` command line option (since 7.2) +''''''''''''''''''''''''''''''''''''''''''''''' -The ``nanoMIPS`` ISA has never been upstreamed to any compiler toolchain. -As it is hard to generate binaries for it, declare it deprecated. +``--blacklist`` has been replaced by ``--block-rpcs`` (which is a better +wording for what this option does). The short form ``-b`` still stays +the same and thus is the preferred way for scripts that should run with +both, older and future versions of QEMU. -Tools ------ +``blacklist`` config file option (since 7.2) +'''''''''''''''''''''''''''''''''''''''''''' -virtiofsd -''''''''' +The ``blacklist`` config file option has been renamed to ``block-rpcs`` +(to be in sync with the renaming of the corresponding command line +option). -There is a new Rust implementation of ``virtiofsd`` at -``https://gitlab.com/virtio-fs/virtiofsd``; -since this is now marked stable, new development should be done on that -rather than the existing C version in the QEMU tree. -The C version will still accept fixes and patches that -are already in development for the moment, but will eventually -be deleted from this tree. -New deployments should use the Rust version, and existing systems -should consider moving to it. The command line and feature set -is very close and moving should be simple. +Migration +--------- + +``skipped`` MigrationStats field (since 8.1) +'''''''''''''''''''''''''''''''''''''''''''' + +``skipped`` field in Migration stats has been deprecated. It hasn't +been used for more than 10 years. + +``inc`` migrate command option (since 8.2) +'''''''''''''''''''''''''''''''''''''''''' + +Use blockdev-mirror with NBD instead. + +As an intermediate step the ``inc`` functionality can be achieved by +setting the ``block-incremental`` migration parameter to ``true``. +But this parameter is also deprecated. + +``blk`` migrate command option (since 8.2) +'''''''''''''''''''''''''''''''''''''''''' + +Use blockdev-mirror with NBD instead. + +As an intermediate step the ``blk`` functionality can be achieved by +setting the ``block`` migration capability to ``true``. But this +capability is also deprecated. + +block migration (since 8.2) +''''''''''''''''''''''''''' + +Block migration is too inflexible. It needs to migrate all block +devices or none. + +Please see "QMP invocation for live storage migration with +``blockdev-mirror`` + NBD" in docs/interop/live-block-operations.rst +for a detailed explanation. + +old compression method (since 8.2) +'''''''''''''''''''''''''''''''''' + +Compression method fails too much. Too many races. We are going to +remove it if nobody fixes it. For starters, migration-test +compression tests are disabled because they fail randomly. If you need +compression, use multifd compression methods. diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst new file mode 100644 index 0000000000..a2eefe3f3f --- /dev/null +++ b/docs/about/emulation.rst @@ -0,0 +1,191 @@ +Emulation +========= + +QEMU's Tiny Code Generator (TCG) provides the ability to emulate a +number of CPU architectures on any supported host platform. Both +:ref:`System Emulation` and :ref:`User Mode Emulation` are supported +depending on the guest architecture. + +.. list-table:: Supported Guest Architectures for Emulation + :widths: 30 10 10 50 + :header-rows: 1 + + * - Architecture (qemu name) + - System + - User + - Notes + * - Alpha + - Yes + - Yes + - Legacy 64 bit RISC ISA developed by DEC + * - Arm (arm, aarch64) + - :ref:`Yes` + - Yes + - Wide range of features, see :ref:`Arm Emulation` for details + * - AVR + - :ref:`Yes` + - No + - 8 bit micro controller, often used in maker projects + * - Cris + - Yes + - Yes + - Embedded RISC chip developed by AXIS + * - Hexagon + - No + - Yes + - Family of DSPs by Qualcomm + * - PA-RISC (hppa) + - Yes + - Yes + - A legacy RISC system used in HP's old minicomputers + * - x86 (i386, x86_64) + - :ref:`Yes` + - Yes + - The ubiquitous desktop PC CPU architecture, 32 and 64 bit. + * - Loongarch + - Yes + - Yes + - A MIPS-like 64bit RISC architecture developed in China + * - m68k + - :ref:`Yes` + - Yes + - Motorola 68000 variants and ColdFire + * - Microblaze + - Yes + - Yes + - RISC based soft-core by Xilinx + * - MIPS (mips*) + - :ref:`Yes` + - Yes + - Venerable RISC architecture originally out of Stanford University + * - Nios2 + - Yes + - Yes + - 32 bit embedded soft-core by Altera + * - OpenRISC + - :ref:`Yes` + - Yes + - Open source RISC architecture developed by the OpenRISC community + * - Power (ppc, ppc64) + - :ref:`Yes` + - Yes + - A general purpose RISC architecture now managed by IBM + * - RISC-V + - :ref:`Yes` + - Yes + - An open standard RISC ISA maintained by RISC-V International + * - RX + - :ref:`Yes` + - No + - A 32 bit micro controller developed by Renesas + * - s390x + - :ref:`Yes` + - Yes + - A 64 bit CPU found in IBM's System Z mainframes + * - sh4 + - Yes + - Yes + - A 32 bit RISC embedded CPU developed by Hitachi + * - SPARC (sparc, sparc64) + - :ref:`Yes` + - Yes + - A RISC ISA originally developed by Sun Microsystems + * - Tricore + - Yes + - No + - A 32 bit RISC/uController/DSP developed by Infineon + * - Xtensa + - :ref:`Yes` + - Yes + - A configurable 32 bit soft core now owned by Cadence + +A number of features are only available when running under +emulation including :ref:`Record/Replay` and :ref:`TCG Plugins`. + +.. _Semihosting: + +Semihosting +----------- + +Semihosting is a feature defined by the owner of the architecture to +allow programs to interact with a debugging host system. On real +hardware this is usually provided by an In-circuit emulator (ICE) +hooked directly to the board. QEMU's implementation allows for +semihosting calls to be passed to the host system or via the +``gdbstub``. + +Generally semihosting makes it easier to bring up low level code before a +more fully functional operating system has been enabled. On QEMU it +also allows for embedded micro-controller code which typically doesn't +have a full libc to be run as "bare-metal" code under QEMU's user-mode +emulation. It is also useful for writing test cases and indeed a +number of compiler suites as well as QEMU itself use semihosting calls +to exit test code while reporting the success state. + +Semihosting is only available using TCG emulation. This is because the +instructions to trigger a semihosting call are typically reserved +causing most hypervisors to trap and fault on them. + +.. warning:: + Semihosting inherently bypasses any isolation there may be between + the guest and the host. As a result a program using semihosting can + happily trash your host system. Some semihosting calls (e.g. + ``SYS_READC``) can block execution indefinitely. You should only + ever run trusted code with semihosting enabled. + +Redirection +~~~~~~~~~~~ + +Semihosting calls can be re-directed to a (potentially remote) gdb +during debugging via the :ref:`gdbstub`. Output to the +semihosting console is configured as a ``chardev`` so can be +redirected to a file, pipe or socket like any other ``chardev`` +device. + +Supported Targets +~~~~~~~~~~~~~~~~~ + +Most targets offer similar semihosting implementations with some +minor changes to define the appropriate instruction to encode the +semihosting call and which registers hold the parameters. They tend to +presents a simple POSIX-like API which allows your program to read and +write files, access the console and some other basic interactions. + +For full details of the ABI for a particular target, and the set of +calls it provides, you should consult the semihosting specification +for that architecture. + +.. note:: + QEMU makes an implementation decision to implement all file + access in ``O_BINARY`` mode. The user-visible effect of this is + regardless of the text/binary mode the program sets QEMU will + always select a binary mode ensuring no line-terminator conversion + is performed on input or output. This is because gdb semihosting + support doesn't make the distinction between the modes and + magically processing line endings can be confusing. + +.. list-table:: Guest Architectures supporting Semihosting + :widths: 10 10 80 + :header-rows: 1 + + * - Architecture + - Modes + - Specification + * - Arm + - System and User-mode + - https://github.com/ARM-software/abi-aa/blob/main/semihosting/semihosting.rst + * - m68k + - System + - https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=libgloss/m68k/m68k-semi.txt;hb=HEAD + * - MIPS + - System + - Unified Hosting Interface (MD01069) + * - Nios II + - System + - https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/nios2/nios2-semi.txt;hb=HEAD + * - RISC-V + - System and User-mode + - https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc + * - Xtensa + - System + - Tensilica ISS SIMCALL diff --git a/docs/about/index.rst b/docs/about/index.rst index 5bea653c07..4f96ab5d91 100644 --- a/docs/about/index.rst +++ b/docs/about/index.rst @@ -5,24 +5,25 @@ About QEMU QEMU is a generic and open source machine emulator and virtualizer. QEMU can be used in several different ways. The most common is for -"system emulation", where it provides a virtual model of an +:ref:`System Emulation`, where it provides a virtual model of an entire machine (CPU, memory and emulated devices) to run a guest OS. -In this mode the CPU may be fully emulated, or it may work with -a hypervisor such as KVM, Xen, Hax or Hypervisor.Framework to -allow the guest to run directly on the host CPU. +In this mode the CPU may be fully emulated, or it may work with a +hypervisor such as KVM, Xen or Hypervisor.Framework to allow the +guest to run directly on the host CPU. -The second supported way to use QEMU is "user mode emulation", +The second supported way to use QEMU is :ref:`User Mode Emulation`, where QEMU can launch processes compiled for one CPU on another CPU. In this mode the CPU is always emulated. -QEMU also provides a number of standalone commandline utilities, -such as the ``qemu-img`` disk image utility that allows you to create, -convert and modify disk images. +QEMU also provides a number of standalone :ref:`command line +utilities`, such as the ``qemu-img`` disk image utility that +allows you to create, convert and modify disk images. .. toctree:: :maxdepth: 2 build-platforms + emulation deprecated removed-features license diff --git a/docs/about/license.rst b/docs/about/license.rst index cde3d2d25d..303c55d61b 100644 --- a/docs/about/license.rst +++ b/docs/about/license.rst @@ -8,4 +8,4 @@ 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 `__. +`LICENSE `__. diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index c7b9dadd5d..f9cf874f7b 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -396,6 +396,125 @@ Use ``-display sdl`` instead. Use ``-display curses`` instead. +Creating sound card devices using ``-soundhw`` (removed in 7.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Sound card devices should be created using ``-device`` or ``-audio``. +The exception is ``pcspk`` which can be activated using ``-machine +pcspk-audiodev=``. + +``-watchdog`` (since 7.2) +''''''''''''''''''''''''' + +Use ``-device`` instead. + +Hexadecimal sizes with scaling multipliers (since 8.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Input parameters that take a size value should only use a size suffix +(such as 'k' or 'M') when the base is written in decimal, and not when +the value is hexadecimal. That is, '0x20M' should be written either as +'32M' or as '0x2000000'. + +``-chardev`` backend aliases ``tty`` and ``parport`` (removed in 8.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +``tty`` and ``parport`` used to be aliases for ``serial`` and ``parallel`` +respectively. The actual backend names should be used instead. + +``-drive if=none`` for the sifive_u OTP device (removed in 8.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``-drive if=pflash`` to configure the OTP device of the sifive_u +RISC-V machine instead. + +``-spice password=string`` (removed in 8.0) +''''''''''''''''''''''''''''''''''''''''''' + +This option was insecure because the SPICE password remained visible in +the process listing. This was replaced by the new ``password-secret`` +option which lets the password be securely provided on the command +line using a ``secret`` object instance. + +``QEMU_AUDIO_`` environment variables and ``-audio-help`` (removed in 8.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``-audiodev`` and ``-audio`` command line options are now the only +way to specify audio backend settings. + +Using ``-audiodev`` to define the default audio backend (removed in 8.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +If no audiodev property is specified, previous versions would use the +first ``-audiodev`` command line option as a fallback. Starting with +version 8.2, audio backends created with ``-audiodev`` will only be +used by clients (sound cards, machines with embedded sound hardware, VNC) +that refer to it in an ``audiodev=`` property. + +In order to configure a default audio backend, use the ``-audio`` +command line option without specifying a ``model``; while previous +versions of QEMU required a model, starting with version 8.2 +QEMU does not require a model and will not create any sound card +in this case. + +Note that the default audio backend must be configured on the command +line if the ``-nodefaults`` options is used. + +``-no-hpet`` (removed in 9.0) +''''''''''''''''''''''''''''' + +The HPET setting has been turned into a machine property. +Use ``-machine hpet=off`` instead. + +``-no-acpi`` (removed in 9.0) +''''''''''''''''''''''''''''' + +The ``-no-acpi`` setting has been turned into a machine property. +Use ``-machine acpi=off`` instead. + +``-async-teardown`` (removed in 9.0) +'''''''''''''''''''''''''''''''''''' + +Use ``-run-with async-teardown=on`` instead. + +``-chroot`` (removed in 9.0) +'''''''''''''''''''''''''''' + +Use ``-run-with chroot=dir`` instead. + +``-singlestep`` (removed in 9.0) +'''''''''''''''''''''''''''''''' + +The ``-singlestep`` option has been turned into an accelerator property, +and given a name that better reflects what it actually does. +Use ``-accel tcg,one-insn-per-tb=on`` instead. + +``-smp`` ("parameter=0" SMP configurations) (removed in 9.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Specified CPU topology parameters must be greater than zero. + +In the SMP configuration, users should either provide a CPU topology +parameter with a reasonable value (greater than zero) or just omit it +and QEMU will compute the missing value. + +However, historically it was implicitly allowed for users to provide +a parameter with zero value, which is meaningless and could also possibly +cause unexpected results in the -smp parsing. So support for this kind of +configurations (e.g. -smp 8,sockets=0) is removed since 9.0, users have +to ensure that all the topology members described with -smp are greater +than zero. + +User-mode emulator command line arguments +----------------------------------------- + +``-singlestep`` (removed in 9.0) +'''''''''''''''''''''''''''''''' + +The ``-singlestep`` option has been given a name that better reflects +what it actually does. For both linux-user and bsd-user, use the +``-one-insn-per-tb`` option instead. + QEMU Machine Protocol (QMP) commands ------------------------------------ @@ -482,6 +601,19 @@ type of array items in query-named-block-nodes. Specify the properties for the object as top-level arguments instead. +``query-sgx`` return value member ``section-size`` (removed in 8.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Member ``section-size`` in the return value of ``query-sgx`` +was superseded by ``sections``. + + +``query-sgx-capabilities`` return value member ``section-size`` (removed in 8.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Member ``section-size`` in the return value of ``query-sgx-capabilities`` +was superseded by ``sections``. + Human Monitor Protocol (HMP) commands ------------------------------------- @@ -536,6 +668,27 @@ Use ``migrate-set-parameters`` instead. This command didn't produce any output already. Removed with no replacement. +``singlestep`` (removed in 9.0) +''''''''''''''''''''''''''''''' + +The ``singlestep`` command has been replaced by the ``one-insn-per-tb`` +command, which has the same behaviour but a less misleading name. + +Host Architectures +------------------ + +System emulation on 32-bit Windows hosts (removed in 9.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Windows 11 has no support for 32-bit host installs, and Windows 10 did +not support new 32-bit installs, only upgrades. 32-bit Windows support +has now been dropped by the MSYS2 project. QEMU also is deprecating +and dropping support for 32-bit x86 host deployments in +general. 32-bit Windows is therefore no longer a supported host for +QEMU. Since all recent x86 hardware from the past >10 years is +capable of the 64-bit x86 extensions, a corresponding 64-bit OS should +be used instead. + Guest Emulator ISAs ------------------- @@ -553,9 +706,8 @@ KVM guest support on 32-bit Arm hosts (removed in 5.2) '''''''''''''''''''''''''''''''''''''''''''''''''''''' 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. +to host KVM guests as of the 5.7 kernel, and was thus removed from QEMU +as well. Running 32-bit guests on a 64-bit Arm host remains supported. RISC-V ISA Specific CPUs (removed in 5.1) ''''''''''''''''''''''''''''''''''''''''' @@ -605,6 +757,28 @@ x86 ``Icelake-Client`` CPU (removed in 7.1) There isn't ever Icelake Client CPU, it is some wrong and imaginary one. Use ``Icelake-Server`` instead. +System accelerators +------------------- + +Userspace local APIC with KVM (x86, removed in 8.0) +''''''''''''''''''''''''''''''''''''''''''''''''''' + +``-M kernel-irqchip=off`` cannot be used on KVM if the CPU model includes +a local APIC. The ``split`` setting is supported, as is using ``-M +kernel-irqchip=off`` when the CPU does not have a local APIC. + +HAXM (``-accel hax``) (removed in 8.2) +'''''''''''''''''''''''''''''''''''''' + +The HAXM project has been retired (see https://github.com/intel/haxm#status). +Use "whpx" (on Windows) or "hvf" (on macOS) instead. + +MIPS "Trap-and-Emulate" KVM support (removed in 8.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''' + +The MIPS "Trap-and-Emulate" KVM host and guest support was removed +from Linux in 2021, and is not supported anymore by QEMU either. + System emulator machines ------------------------ @@ -642,8 +816,8 @@ mips ``fulong2e`` machine alias (removed in 6.0) This machine has been renamed ``fuloong2e``. -``pc-0.10`` up to ``pc-1.3`` (removed in 4.0 up to 6.0) -''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``pc-0.10`` up to ``pc-i440fx-1.7`` (removed in 4.0 up to 8.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' These machine types were very old and likely could not be used for live migration from old QEMU versions anymore. Use a newer machine type instead. @@ -661,6 +835,12 @@ Aspeed ``swift-bmc`` machine (removed in 7.0) This machine was removed because it was unused. Alternative AST2500 based OpenPOWER machines are ``witherspoon-bmc`` and ``romulus-bmc``. +ppc ``taihu`` machine (removed in 7.2) +''''''''''''''''''''''''''''''''''''''''''''' + +This machine was removed because it was partially emulated and 405 +machines are very similar. Use the ``ref405ep`` machine instead. + linux-user mode CPUs -------------------- @@ -681,13 +861,6 @@ tripped up the CI testing and was suspected to be quite broken. For that reason the maintainers strongly suspected no one actually used it. -Creating sound card devices using ``-soundhw`` (removed in 7.1) -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Sound card devices should be created using ``-device`` or ``-audio``. -The exception is ``pcspk`` which can be activated using ``-machine -pcspk-audiodev=``. - TCG introspection features -------------------------- @@ -727,6 +900,16 @@ The 'ide-drive' device has been removed. Users should use 'ide-hd' or The 'scsi-disk' device has been removed. Users should use 'scsi-hd' or 'scsi-cd' as appropriate to get a SCSI hard disk or CD-ROM as needed. +``sga`` (removed in 8.0) +'''''''''''''''''''''''' + +The ``sga`` device loaded an option ROM for x86 targets which enabled +SeaBIOS to send messages to the serial console. SeaBIOS 1.11.0 onwards +contains native support for this feature and thus use of the option +ROM approach was obsolete. The native SeaBIOS support can be activated +by using ``-machine graphics=off``. + + Related binaries ---------------- @@ -810,3 +993,16 @@ The VXHS code did not compile since v2.12.0. It was removed in 5.1. The corresponding upstream server project is no longer maintained. Users are recommended to switch to an alternative distributed block device driver such as RBD. + +Tools +----- + +virtiofsd (removed in 8.0) +'''''''''''''''''''''''''' + +There is a newer Rust implementation of ``virtiofsd`` at +``https://gitlab.com/virtio-fs/virtiofsd``; this has been +stable for some time and is now widely used. +The command line and feature set is very close to the removed +C implementation. + diff --git a/docs/colo-proxy.txt b/docs/colo-proxy.txt index 1fc38aed1b..e712c883db 100644 --- a/docs/colo-proxy.txt +++ b/docs/colo-proxy.txt @@ -162,7 +162,7 @@ Here is an example using demonstration IP and port addresses to more clearly describe the usage. Primary(ip:3.3.3.3): --netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown +-netdev tap,id=hn0,vhost=off -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=on,wait=off -chardev socket,id=compare1,host=3.3.3.3,port=9004,server=on,wait=off @@ -177,7 +177,7 @@ Primary(ip:3.3.3.3): -object colo-compare,id=comp0,primary_in=compare0-0,secondary_in=compare1,outdev=compare_out0,iothread=iothread1 Secondary(ip:3.3.3.8): --netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,down script=/etc/qemu-ifdown +-netdev tap,id=hn0,vhost=off -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 @@ -202,7 +202,7 @@ Primary(ip:3.3.3.3): -object colo-compare,id=comp0,primary_in=compare0-0,secondary_in=compare1,outdev=compare_out0,vnet_hdr_support Secondary(ip:3.3.3.8): --netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,down script=/etc/qemu-ifdown +-netdev tap,id=hn0,vhost=off -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 diff --git a/docs/conf.py b/docs/conf.py index 49dab44cca..aae0304ac6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,18 +29,8 @@ import os import sys import sphinx -from distutils.version import LooseVersion 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,6): - raise ConfigError( - "QEMU requires a Sphinx that uses Python 3.6 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. # This is always the absolute path of the docs/ directory in the source tree. @@ -98,7 +88,7 @@ default_role = 'any' # General information about the project. project = u'QEMU' -copyright = u'2022, The QEMU Project Developers' +copyright = u'2024, The QEMU Project Developers' author = u'The QEMU Project Developers' # The version info for the project you're documenting, acts as replacement for @@ -126,7 +116,7 @@ finally: # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -174,11 +164,10 @@ html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -if LooseVersion(sphinx_rtd_theme.__version__) >= LooseVersion("0.4.3"): - html_theme_options = { - "style_nav_header_background": "#802400", - "navigation_with_keys": True, - } +html_theme_options = { + "style_nav_header_background": "#802400", + "navigation_with_keys": True, +} html_logo = os.path.join(qemu_docdir, "../ui/icons/qemu_128x128.png") @@ -290,26 +279,9 @@ man_pages = [ ('tools/virtfs-proxy-helper', 'virtfs-proxy-helper', 'QEMU 9p virtfs proxy filesystem helper', ['M. Mohan Kumar'], 1), - ('tools/virtiofsd', 'virtiofsd', - 'QEMU virtio-fs shared file system daemon', - ['Stefan Hajnoczi ', - 'Masayoshi Mizuma '], 1), ] man_make_section_directory = False -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'QEMU', u'QEMU Documentation', - author, 'QEMU', 'One line description of project.', - 'Miscellaneous'), -] - - - # We use paths starting from qemu_docdir here so that you can run # sphinx-build from anywhere and the kerneldoc extension can still # find everything. diff --git a/docs/config/mach-virt-graphical.cfg b/docs/config/mach-virt-graphical.cfg index d6d31b17f5..eba76eb198 100644 --- a/docs/config/mach-virt-graphical.cfg +++ b/docs/config/mach-virt-graphical.cfg @@ -56,9 +56,11 @@ [machine] type = "virt" - accel = "kvm" gic-version = "host" +[accel] + accel = "kvm" + [memory] size = "1024" diff --git a/docs/config/mach-virt-serial.cfg b/docs/config/mach-virt-serial.cfg index 18a7c83731..324b0542ff 100644 --- a/docs/config/mach-virt-serial.cfg +++ b/docs/config/mach-virt-serial.cfg @@ -62,9 +62,11 @@ [machine] type = "virt" - accel = "kvm" gic-version = "host" +[accel] + accel = "kvm" + [memory] size = "1024" diff --git a/docs/config/q35-emulated.cfg b/docs/config/q35-emulated.cfg index 99ac918e78..b4bd7e858a 100644 --- a/docs/config/q35-emulated.cfg +++ b/docs/config/q35-emulated.cfg @@ -61,6 +61,8 @@ [machine] type = "q35" + +[accel] accel = "kvm" [memory] @@ -286,3 +288,7 @@ driver = "hda-duplex" bus = "ich9-hda-audio.0" cad = "0" + audiodev = "audiodev0" + +[audiodev "audiodev0"] + driver = "none" # CHANGE ME diff --git a/docs/config/q35-virtio-graphical.cfg b/docs/config/q35-virtio-graphical.cfg index 4207f11e4f..820860aefe 100644 --- a/docs/config/q35-virtio-graphical.cfg +++ b/docs/config/q35-virtio-graphical.cfg @@ -55,6 +55,8 @@ [machine] type = "q35" + +[accel] accel = "kvm" [memory] @@ -246,3 +248,7 @@ driver = "hda-duplex" bus = "sound.0" cad = "0" + audiodev = "audiodev0" + +[audiodev "audiodev0"] + driver = "none" # CHANGE ME diff --git a/docs/config/q35-virtio-serial.cfg b/docs/config/q35-virtio-serial.cfg index d2830aec5e..023291390e 100644 --- a/docs/config/q35-virtio-serial.cfg +++ b/docs/config/q35-virtio-serial.cfg @@ -60,6 +60,8 @@ [machine] type = "q35" + +[accel] accel = "kvm" [memory] diff --git a/docs/devel/acpi-bits.rst b/docs/devel/acpi-bits.rst new file mode 100644 index 0000000000..1ec394f5fb --- /dev/null +++ b/docs/devel/acpi-bits.rst @@ -0,0 +1,167 @@ +============================================================================= +ACPI/SMBIOS avocado tests using biosbits +============================================================================= +************ +Introduction +************ +Biosbits is a software written by Josh Triplett that can be downloaded +from https://biosbits.org/. The github codebase can be found +`here `__. It is a software that +executes the bios components such as acpi and smbios tables directly through +acpica bios interpreter (a freely available C based library written by Intel, +downloadable from https://acpica.org/ and is included with biosbits) without an +operating system getting involved in between. Bios-bits has python integration +with grub so actual routines that executes bios components can be written in +python instead of bash-ish (grub's native scripting language). +There are several advantages to directly testing the bios in a real physical +machine or in a VM as opposed to indirectly discovering bios issues through the +operating system (the OS). Operating systems tend to bypass bios problems and +hide them from the end user. We have more control of what we wanted to test and +how by being as close to the bios on a running system as possible without a +complicated software component such as an operating system coming in between. +Another issue is that we cannot exercise bios components such as ACPI and +SMBIOS without being in the highest hardware privilege level, ring 0 for +example in case of x86. Since the OS executes from ring 0 whereas normal user +land software resides in unprivileged ring 3, operating system must be modified +in order to write our test routines that exercise and test the bios. This is +not possible in all cases. Lastly, test frameworks and routines are preferably +written using a high level scripting language such as python. OSes and +OS modules are generally written using low level languages such as C and +low level assembly machine language. Writing test routines in a low level +language makes things more cumbersome. These and other reasons makes using +bios-bits very attractive for testing bioses. More details on the inspiration +for developing biosbits and its real life uses can be found in [#a]_ and [#b]_. + +For QEMU, we maintain a fork of bios bits in gitlab along with all the +dependent submodules `here `__. +This fork contains numerous fixes, a newer acpica and changes specific to +running this avocado QEMU tests using bits. The author of this document +is the sole maintainer of the QEMU fork of bios bits repository. For more +information, please see author's `FOSDEM talk on this bios-bits based test +framework `__. + +********************************* +Description of the test framework +********************************* + +Under the directory ``tests/avocado/``, ``acpi-bits.py`` is a QEMU avocado +test that drives all this. + +A brief description of the various test files follows. + +Under ``tests/avocado/`` as the root we have: + +:: + + ├── acpi-bits + │ ├── bits-config + │ │ └── bits-cfg.txt + │ ├── bits-tests + │ ├── smbios.py2 + │ ├── testacpi.py2 + │ └── testcpuid.py2 + ├── acpi-bits.py + +* ``tests/avocado``: + + ``acpi-bits.py``: + This is the main python avocado test script that generates a + biosbits iso. It then spawns a QEMU VM with it, collects the log and reports + test failures. This is the script one would be interested in if they wanted + to add or change some component of the log parsing, add a new command line + to alter how QEMU is spawned etc. Test writers typically would not need to + modify this script unless they wanted to enhance or change the log parsing + for their tests. In order to enable debugging, you can set **V=1** + environment variable. This enables verbose mode for the test and also dumps + the entire log from bios bits and more information in case failure happens. + You can also set **BITS_DEBUG=1** to turn on debug mode. It will enable + verbose logs and also retain the temporary work directory the test used for + you to inspect and run the specific commands manually. + + In order to run this test, please perform the following steps from the QEMU + build directory: + :: + + $ make check-venv (needed only the first time to create the venv) + $ ./pyvenv/bin/avocado run -t acpi tests/avocado + + The above will run all acpi avocado tests including this one. + In order to run the individual tests, perform the following: + :: + + $ ./pyvenv/bin/avocado run tests/avocado/acpi-bits.py --tap - + + The above will produce output in tap format. You can omit "--tap -" in the + end and it will produce output like the following: + :: + + $ ./pyvenv/bin/avocado run tests/avocado/acpi-bits.py + Fetching asset from tests/avocado/acpi-bits.py:AcpiBitsTest.test_acpi_smbios_bits + JOB ID : eab225724da7b64c012c65705dc2fa14ab1defef + JOB LOG : /home/anisinha/avocado/job-results/job-2022-10-10T17.58-eab2257/job.log + (1/1) tests/avocado/acpi-bits.py:AcpiBitsTest.test_acpi_smbios_bits: PASS (33.09 s) + RESULTS : PASS 1 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0 + JOB TIME : 39.22 s + + You can inspect the log file for more information about the run or in order + to diagnoze issues. If you pass V=1 in the environment, more diagnostic logs + would be found in the test log. + +* ``tests/avocado/acpi-bits/bits-config``: + + This location contains biosbits configuration files that determine how the + software runs the tests. + + ``bits-config.txt``: + This is the biosbits config file that determines what tests + or actions are performed by bits. The description of the config options are + provided in the file itself. + +* ``tests/avocado/acpi-bits/bits-tests``: + + This directory contains biosbits python based tests that are run from within + the biosbits environment in the spawned VM. New additions of test cases can + be made in the appropriate test file. For example, new acpi tests can go + into testacpi.py2 and one would call testsuite.add_test() to register the new + test so that it gets executed as a part of the ACPI tests. + It might be occasionally necessary to disable some subtests or add a new + test that belongs to a test suite not already present in this directory. To + do this, please clone the bits source from + https://gitlab.com/qemu-project/biosbits-bits/-/tree/qemu-bits. + Note that this is the "qemu-bits" branch and not the "bits" branch of the + repository. "qemu-bits" is the branch where we have made all the QEMU + specific enhancements and we must use the source from this branch only. + Copy the test suite/script that needs modification (addition of new tests + or disabling them) from python directory into this directory. For + example, in order to change cpuid related tests, copy the following + file into this directory and rename it with .py2 extension: + https://gitlab.com/qemu-project/biosbits-bits/-/blob/qemu-bits/python/testcpuid.py + Then make your additions and changes here. Therefore, the steps are: + + (a) Copy unmodified test script to this directory from bits source. + (b) Add a SPDX license header. + (c) Perform modifications to the test. + + Commits (a), (b) and (c) preferably should go under separate commits so that + the original test script and the changes we have made are separated and + clear. (a) and (b) can sometimes be combined into a single step. + + The test framework will then use your modified test script to run the test. + No further changes would be needed. Please check the logs to make sure that + appropriate changes have taken effect. + + The tests have an extension .py2 in order to indicate that: + + (a) They are python2.7 based scripts and not python 3 scripts. + (b) They are run from within the bios bits VM and is not subjected to QEMU + build/test python script maintenance and dependency resolutions. + (c) They need not be loaded by avocado framework when running tests. + + +Author: Ani Sinha + +References: +----------- +.. [#a] https://blog.linuxplumbersconf.org/2011/ocw/system/presentations/867/original/bits.pdf +.. [#b] https://www.youtube.com/watch?v=36QIepyUuhg +.. [#c] https://fosdem.org/2024/schedule/event/fosdem-2024-2262-exercising-qemu-generated-acpi-smbios-tables-using-biosbits-from-within-a-guest-vm-/ diff --git a/docs/devel/atomics.rst b/docs/devel/atomics.rst index 52baa0736d..b77c6e13e1 100644 --- a/docs/devel/atomics.rst +++ b/docs/devel/atomics.rst @@ -1,3 +1,5 @@ +.. _atomics-ref: + ========================= Atomic operations in QEMU ========================= @@ -25,7 +27,8 @@ provides macros that fall in three camps: - weak atomic access and manual memory barriers: ``qatomic_read()``, ``qatomic_set()``, ``smp_rmb()``, ``smp_wmb()``, ``smp_mb()``, - ``smp_mb_acquire()``, ``smp_mb_release()``, ``smp_read_barrier_depends()``; + ``smp_mb_acquire()``, ``smp_mb_release()``, ``smp_read_barrier_depends()``, + ``smp_mb__before_rmw()``, ``smp_mb__after_rmw()``; - sequentially consistent atomic access: everything else. @@ -99,28 +102,10 @@ Similar operations return the new value of ``*ptr``:: typeof(*ptr) qatomic_or_fetch(ptr, val) typeof(*ptr) qatomic_xor_fetch(ptr, val) -``qemu/atomic.h`` also provides loads and stores that cannot be reordered -with each other:: +``qemu/atomic.h`` also provides an optimized shortcut for +``qatomic_set`` followed by ``smp_mb``:: - typeof(*ptr) qatomic_mb_read(ptr) - void qatomic_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 (``qatomic_rcu_read``, ``qatomic_rcu_set``) when publishing - or accessing a new version of a data structure - -- other atomic accesses: ``qatomic_read`` and ``qatomic_load_acquire`` for - loads, ``qatomic_set`` and ``qatomic_store_release`` for stores, ``smp_mb`` - to forbid reordering subsequent loads before a store. + void qatomic_set_mb(ptr, val) Weak atomic access and manual memory barriers @@ -134,7 +119,7 @@ The only guarantees that you can rely upon in this case are: 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. + accesses, or perform other similar optimizations. - acquire operations will appear to happen, with respect to the other components of the system, before all the LOAD or STORE operations @@ -217,10 +202,9 @@ They come in six kinds: 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()``. + Therefore, unlike ``smp_rmb()`` or ``qatomic_load_acquire()``, + ``smp_read_barrier_depends()`` can be just a compiler barrier on + weakly-ordered architectures such as Arm or PPC[#]_. 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 @@ -228,6 +212,10 @@ They come in six kinds: than actually loading the address itself, then it's a _control_ dependency and a full read barrier or better is required. +.. [#] The DEC Alpha is an exception, because ``smp_read_barrier_depends()`` + needs a processor barrier. On strongly-ordered architectures such + as x86 or s390, ``smp_rmb()`` and ``qatomic_load_acquire()`` can + also be compiler barriers only. Memory barriers and ``qatomic_load_acquire``/``qatomic_store_release`` are mostly used when a data structure has one thread that is always a writer @@ -466,13 +454,19 @@ and memory barriers, and the equivalents in QEMU: 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. + a different set of memory barriers. In QEMU, all of them enforce + sequential consistency: there is a single order in which the + program sees them happen. -- in QEMU, ``qatomic_read()`` and ``qatomic_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: +- however, according to the C11 memory model that QEMU uses, this order + does not propagate to other memory accesses on either side of the + read-modify-write operation. As far as those are concerned, the + operation consist of just a load-acquire followed by a store-release. + Stores that precede the RMW operation, and loads that follow it, can + still be reordered and will happen *in the middle* of the read-modify-write + operation! + + Therefore, the following example is correct in Linux but not in QEMU: +----------------------------------+--------------------------------+ | Linux (correct) | QEMU (incorrect) | @@ -486,9 +480,24 @@ and memory barriers, and the equivalents in QEMU: 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: + Fixing this requires a full memory barrier between the write of ``x`` and + the read of ``y``. QEMU provides ``smp_mb__before_rmw()`` and + ``smp_mb__after_rmw()``; they act both as an optimization, + avoiding the memory barrier on processors where it is unnecessary, + and as a clarification of this corner case of the C11 memory model: + + +--------------------------------+ + | QEMU (correct) | + +================================+ + | :: | + | | + | a = qatomic_fetch_add(&x, 2);| + | smp_mb__after_rmw(); | + | b = qatomic_read(&y); | + +--------------------------------+ + + In the common case where only one thread writes ``x``, it is also possible + to write it like this: +--------------------------------+ | QEMU (correct) | @@ -496,8 +505,7 @@ and memory barriers, and the equivalents in QEMU: | :: | | | | a = qatomic_read(&x); | - | qatomic_set(&x, a + 2); | - | smp_mb(); | + | qatomic_set_mb(&x, a + 2); | | b = qatomic_read(&y); | +--------------------------------+ diff --git a/docs/devel/block-coroutine-wrapper.rst b/docs/devel/block-coroutine-wrapper.rst index 412851986b..6dd2cdcab3 100644 --- a/docs/devel/block-coroutine-wrapper.rst +++ b/docs/devel/block-coroutine-wrapper.rst @@ -26,12 +26,12 @@ called ``bdrv_foo()``. In this case the script can help. To trigger the generation: 1. You need ``bdrv_foo`` declaration somewhere (for example, in - ``block/coroutines.h``) with the ``generated_co_wrapper`` mark, + ``block/coroutines.h``) with the ``co_wrapper`` mark, like this: .. code-block:: c - int generated_co_wrapper bdrv_foo(); + int co_wrapper bdrv_foo(); 2. You need to feed this declaration to block-coroutine-wrapper script. For this, add the .h (or .c) file with the declaration to the @@ -46,7 +46,7 @@ Links 1. The script location is ``scripts/block-coroutine-wrapper.py``. -2. Generic place for private ``generated_co_wrapper`` declarations is +2. Generic place for private ``co_wrapper`` declarations is ``block/coroutines.h``, for public declarations: ``include/block/block.h`` diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index 431caba7aa..09caf2f8e1 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -4,30 +4,14 @@ The QEMU build system architecture This document aims to help developers understand the architecture of the QEMU build system. As with projects using GNU autotools, the QEMU build -system has two stages, first the developer runs the "configure" script +system has two stages; first the developer runs the "configure" script to determine the local build environment characteristics, then they run -"make" to build the project. There is about where the similarities with +"make" to build the project. This is about where the similarities with GNU autotools end, so try to forget what you know about them. +The two general ways to perform a build are as follows: -Stage 1: configure -================== - -The QEMU configure script is written directly in shell, and should be -compatible with any POSIX shell, hence it uses #!/bin/sh. An important -implication of this is that it is important to avoid using bash-isms on -development platforms where bash is the primary host. - -In contrast to autoconf scripts, QEMU's configure is expected to be -silent while it is checking for features. It will only display output -when an error occurs, or to show the final feature enablement summary -on completion. - -Because QEMU uses the Meson build system under the hood, only VPATH -builds are supported. There are two general ways to invoke configure & -perform a build: - - - VPATH, build artifacts outside of QEMU source tree entirely:: + - build artifacts outside of QEMU source tree entirely:: cd ../ mkdir build @@ -35,93 +19,190 @@ perform a build: ../qemu/configure make - - VPATH, build artifacts in a subdir of QEMU source tree:: + - build artifacts in a subdir of QEMU source tree:: mkdir build cd build ../configure make -The configure script automatically recognizes -command line options for which a same-named Meson option exists; -dashes in the command line are replaced with underscores. +Most of the actual build process uses Meson under the hood, therefore +build artifacts cannot be placed in the source tree itself. -Many checks on the compilation environment are still found in configure -rather than ``meson.build``, but new checks should be added directly to -``meson.build``. -Patches are also welcome to move existing checks from the configure -phase to ``meson.build``. When doing so, ensure that ``meson.build`` does -not use anymore the keys that you have removed from ``config-host.mak``. -Typically these will be replaced in ``meson.build`` by boolean variables, -``get_option('optname')`` invocations, or ``dep.found()`` expressions. -In general, the remaining checks have little or no interdependencies, -so they can be moved one by one. +Stage 1: configure +================== -Helper functions ----------------- +The configure script has five tasks: -The configure script provides a variety of helper functions to assist -developers in checking for system features: + - detect the host architecture -``do_cc $ARGS...`` - Attempt to run the system C compiler passing it $ARGS... + - list the targets for which to build emulators; the list of + targets also affects which firmware binaries and tests to build -``do_cxx $ARGS...`` - Attempt to run the system C++ compiler passing it $ARGS... + - find the compilers (native and cross) used to build executables, + firmware and tests. The results are written as either Makefile + fragments (``config-host.mak``) or a Meson machine file + (``config-meson.cross``) -``compile_object $CFLAGS`` - Attempt to compile a test program with the system C compiler using - $CFLAGS. The test program must have been previously written to a file - called $TMPC. The replacement in Meson is the compiler object ``cc``, - which has methods such as ``cc.compiles()``, - ``cc.check_header()``, ``cc.has_function()``. + - create a virtual environment in which all Python code runs during + the build, and possibly install packages into it from PyPI -``compile_prog $CFLAGS $LDFLAGS`` - Attempt to compile a test program with the system C compiler using - $CFLAGS and link it with the system linker using $LDFLAGS. The test - program must have been previously written to a file called $TMPC. - The replacement in Meson is ``cc.find_library()`` and ``cc.links()``. + - invoke Meson in the virtual environment, to perform the actual + configuration step for the emulator build + +The configure script automatically recognizes command line options for +which a same-named Meson option exists; dashes in the command line are +replaced with underscores. + +Almost all QEMU developers that need to modify the build system will +only be concerned with Meson, and therefore can skip the rest of this +section. + + +Modifying ``configure`` +----------------------- + +``configure`` is a shell script; it uses ``#!/bin/sh`` and therefore +should be compatible with any POSIX shell. It is important to avoid +using bash-isms to avoid breaking development platforms where bash is +the primary host. + +The configure script provides a variety of functions to help writing +portable shell code and providing consistent behavior across architectures +and operating systems: + +``error_exit $MESSAGE $MORE...`` + Print $MESSAGE to stderr, followed by $MORE... and then exit from the + configure script with non-zero status. ``has $COMMAND`` Determine if $COMMAND exists in the current environment, either as a shell builtin, or executable binary, returning 0 on success. The replacement in Meson is ``find_program()``. -``check_define $NAME`` - Determine if the macro $NAME is defined by the system C compiler +``probe_target_compiler $TARGET`` + Detect a cross compiler and cross tools for the QEMU target $TARGET (e.g., + ``$CPU-softmmu``, ``$CPU-linux-user``, ``$CPU-bsd-user``). If a working + compiler is present, return success and set variables ``$target_cc``, + ``$target_ar``, etc. to non-empty values. -``check_include $NAME`` - Determine if the include $NAME file is available to the system C - compiler. The replacement in Meson is ``cc.has_header()``. +``write_target_makefile`` + Write a Makefile fragment to stdout, exposing the result of the most + ``probe_target_compiler`` call as the usual Make variables (``CC``, + ``AR``, ``LD``, etc.). + + +Configure does not generally perform tests for compiler options beyond +basic checks to detect the host platform and ensure the compiler is +functioning. These are performed using a few more helper functions: + +``compile_object $CFLAGS`` + Attempt to compile a test program with the system C compiler using + $CFLAGS. The test program must have been previously written to a file + called $TMPC. + +``compile_prog $CFLAGS $LDFLAGS`` + Attempt to compile a test program with the system C compiler using + $CFLAGS and link it with the system linker using $LDFLAGS. The test + program must have been previously written to a file called $TMPC. + +``check_define $NAME`` + Determine if the macro $NAME is defined by the system C compiler. + +``do_compiler $CC $ARGS...`` + Attempt to run the C compiler $CC, passing it $ARGS... This function + does not use flags passed via options such as ``--extra-cflags``, and + therefore can be used to check for cross compilers. However, most + such checks are done at ``make`` time instead (see for example the + ``cc-option`` macro in ``pc-bios/option-rom/Makefile``). ``write_c_skeleton`` Write a minimal C program main() function to the temporary file - indicated by $TMPC + indicated by $TMPC. -``feature_not_found $NAME $REMEDY`` - Print a message to stderr that the feature $NAME was not available - on the system, suggesting the user try $REMEDY to address the - problem. -``error_exit $MESSAGE $MORE...`` - Print $MESSAGE to stderr, followed by $MORE... and then exit from the - configure script with non-zero status +Python virtual environments and the build process +------------------------------------------------- -``query_pkg_config $ARGS...`` - Run pkg-config passing it $ARGS. If QEMU is doing a static build, - then --static will be automatically added to $ARGS +An important step in ``configure`` is to create a Python virtual +environment (venv) during the configuration phase. The Python interpreter +comes from the ``--python`` command line option, the ``$PYTHON`` variable +from the environment, or the system PATH, in this order. The venv resides +in the ``pyvenv`` directory in the build tree, and provides consistency +in how the build process runs Python code. + +At this stage, ``configure`` also queries the chosen Python interpreter +about QEMU's build dependencies. Note that the build process does *not* +look for ``meson``, ``sphinx-build`` or ``avocado`` binaries in the PATH; +likewise, there are no options such as ``--meson`` or ``--sphinx-build``. +This avoids a potential mismatch, where Meson and Sphinx binaries on the +PATH might operate in a different Python environment than the one chosen +by the user during the build process. On the other hand, it introduces +a potential source of confusion where the user installs a dependency but +``configure`` is not able to find it. When this happens, the dependency +was installed in the ``site-packages`` directory of another interpreter, +or with the wrong ``pip`` program. + +If a package is available for the chosen interpreter, ``configure`` +prepares a small script that invokes it from the venv itself[#distlib]_. +If not, ``configure`` can also optionally install dependencies in the +virtual environment with ``pip``, either from wheels in ``python/wheels`` +or by downloading the package with PyPI. Downloading can be disabled with +``--disable-download``; and anyway, it only happens when a ``configure`` +option (currently, only ``--enable-docs``) is explicitly enabled but +the dependencies are not present[#pip]_. + +.. [#distlib] The scripts are created based on the package's metadata, + specifically the ``console_script`` entry points. This is the + same mechanism that ``pip`` uses when installing a package. + Currently, in all cases it would be possible to use ``python -m`` + instead of an entry point script, which makes this approach a + bit overkill. On the other hand, creating the scripts is + future proof and it makes the contents of the ``pyvenv/bin`` + directory more informative. Portability is also not an issue, + because the Python Packaging Authority provides a package + ``distlib.scripts`` to perform this task. + +.. [#pip] ``pip`` might also be used when running ``make check-avocado`` + if downloading is enabled, to ensure that Avocado is + available. + +The required versions of the packages are stored in a configuration file +``pythondeps.toml``. The format is custom to QEMU, but it is documented +at the top of the file itself and it should be easy to understand. The +requirements should make it possible to use the version that is packaged +that is provided by supported distros. + +When dependencies are downloaded, instead, ``configure`` uses a "known +good" version that is also listed in ``pythondeps.toml``. In this +scenario, ``pythondeps.toml`` behaves like the "lock file" used by +``cargo``, ``poetry`` or other dependency management systems. + + +Bundled Python packages +----------------------- + +Python packages that are **mandatory** dependencies to build QEMU, +but are not available in all supported distros, are bundled with the +QEMU sources. Currently this includes Meson (outdated in CentOS 8 +and derivatives, Ubuntu 20.04 and 22.04, and openSUSE Leap) and tomli +(absent in Ubuntu 20.04). + +If you need to update these, please do so by modifying and rerunning +``python/scripts/vendor.py``. This script embeds the sha256 hash of +package sources and checks it. The pypi.org web site provides an easy +way to retrieve the sha256 hash of the sources. Stage 2: Meson ============== -The Meson build system is currently used to describe the build -process for: +The Meson build system describes the build and install process for: 1) executables, which include: - - Tools - ``qemu-img``, ``qemu-nbd``, ``qga`` (guest agent), etc + - Tools - ``qemu-img``, ``qemu-nbd``, ``qemu-ga`` (guest agent), etc - System emulators - ``qemu-system-$ARCH`` @@ -131,7 +212,8 @@ process for: 2) documentation -3) ROMs, which can be either installed as binary blobs or compiled +3) ROMs, whether provided as binary blobs in the QEMU distributions + or cross compiled under the direction of the configure script 4) other data files, such as icons or desktop files @@ -169,26 +251,11 @@ Target-independent emulator sourcesets: This includes error handling infrastructure, standard data structures, platform portability wrapper functions, etc. - Target-independent code lives in the ``common_ss``, ``softmmu_ss`` and + Target-independent code lives in the ``common_ss``, ``system_ss`` and ``user_ss`` sourcesets. ``common_ss`` is linked into all emulators, - ``softmmu_ss`` only in system emulators, ``user_ss`` only in user-mode + ``system_ss`` only in system emulators, ``user_ss`` only in user-mode emulators. - Target-independent sourcesets must exercise particular care when using - ``if_false`` rules. The ``if_false`` rule will be used correctly when linking - emulator binaries; however, when *compiling* target-independent files - into .o files, Meson may need to pick *both* the ``if_true`` and - ``if_false`` sides to cater for targets that want either side. To - achieve that, you can add a special rule using the ``CONFIG_ALL`` - symbol:: - - # Some targets have CONFIG_ACPI, some don't, so this is not enough - softmmu_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi.c'), - if_false: files('acpi-stub.c')) - - # This is required as well: - softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c')) - Target-dependent emulator sourcesets: In the target-dependent set lives CPU emulation, some device emulation and much glue code. This sometimes also has to be compiled multiple times, @@ -211,20 +278,20 @@ Target-dependent emulator sourcesets: The sourceset is only used for system emulators. Each subdirectory in ``target/`` instead should add one sourceset to each - of the ``target_arch`` and ``target_softmmu_arch``, which are used respectively + of the ``target_arch`` and ``target_system_arch``, which are used respectively for all emulators and for system emulators only. For example:: arm_ss = ss.source_set() - arm_softmmu_ss = ss.source_set() + arm_system_ss = ss.source_set() ... target_arch += {'arm': arm_ss} - target_softmmu_arch += {'arm': arm_softmmu_ss} + target_system_arch += {'arm': arm_system_ss} Module sourcesets: There are two dictionaries for modules: ``modules`` is used for target-independent modules and ``target_modules`` is used for target-dependent modules. When modules are disabled the ``module`` - source sets are added to ``softmmu_ss`` and the ``target_modules`` + source sets are added to ``system_ss`` and the ``target_modules`` source sets are added to ``specific_ss``. Both dictionaries are nested. One dictionary is created per @@ -286,8 +353,7 @@ system/userspace emulation target Adding checks ------------- -New checks should be added to Meson. Compiler checks can be as simple as -the following:: +Compiler checks can be as simple as the following:: config_host_data.set('HAVE_BTRFS_H', cc.has_header('linux/btrfs.h')) @@ -316,8 +382,7 @@ dependency will be used:: sdl_image = not_found if not get_option('sdl_image').auto() or have_system sdl_image = dependency('SDL2_image', required: get_option('sdl_image'), - method: 'pkg-config', - static: enable_static) + method: 'pkg-config') endif This avoids warnings on static builds of user-mode emulators, for example. @@ -364,23 +429,40 @@ This is needed to obey the --python= option passed to the configure script, which may point to something other than the first python3 binary on the path. +By the time Meson runs, Python dependencies are available in the virtual +environment and should be invoked through the scripts that ``configure`` +places under ``pyvenv``. One way to do so is as follows, using Meson's +``find_program`` function:: -Stage 3: makefiles -================== + sphinx_build = find_program( + fs.parent(python.full_path()) / 'sphinx-build', + required: get_option('docs')) -The use of GNU make is required with the QEMU build system. -The output of Meson is a build.ninja file, which is used with the Ninja -build system. QEMU uses a different approach, where Makefile rules are -synthesized from the build.ninja file. The main Makefile includes these -rules and wraps them so that e.g. submodules are built before QEMU. -The resulting build system is largely non-recursive in nature, in -contrast to common practices seen with automake. +Stage 3: Make +============= -Tests are also ran by the Makefile with the traditional ``make check`` -phony target, while benchmarks are run with ``make bench``. Meson test -suites such as ``unit`` can be ran with ``make check-unit`` too. It is also -possible to run tests defined in meson.build with ``meson test``. +The next step in building QEMU is to invoke make. GNU Make is required +to build QEMU, and may be installed as ``gmake`` on some hosts. + +The output of Meson is a ``build.ninja`` file, which is used with the +Ninja build tool. However, QEMU's build comprises other components than +just the emulators (namely firmware and the tests in ``tests/tcg``) which +need different cross compilers. The QEMU Makefile wraps both Ninja and +the smaller build systems for firmware and tests; it also takes care of +running ``configure`` again when the script changes. Apart from invoking +these sub-Makefiles, the resulting build is largely non-recursive. + +Tests, whether defined in ``meson.build`` or not, are also ran by the +Makefile with the traditional ``make check`` phony target, while benchmarks +are run with ``make bench``. Meson test suites such as ``unit`` can be ran +with ``make check-unit``, and ``make check-tcg`` builds and runs "non-Meson" +tests for all targets. + +If desired, it is also possible to use ``ninja`` and ``meson test``, +respectively to build emulators and run tests defined in meson.build. +The main difference is that ``make`` needs the ``-jN`` flag in order to +enable parallel builds or tests. Useful make targets ------------------- @@ -392,6 +474,7 @@ Useful make targets Print the value of the variable VAR. Useful for debugging the build system. + Important files for the build system ==================================== @@ -405,8 +488,7 @@ number of dynamically created files listed later. ``Makefile`` The main entry point used when invoking make to build all the components of QEMU. The default 'all' target will naturally result in the build of - every component. Makefile takes care of recursively building submodules - directly via a non-recursive set of rules. + every component. ``*/meson.build`` The meson.build file in the root directory is the main entry point for the @@ -414,60 +496,94 @@ number of dynamically created files listed later. executables. Build rules for various subdirectories are included in other meson.build files spread throughout the QEMU source tree. +``python/scripts/mkvenv.py`` + A wrapper for the Python ``venv`` and ``distlib.scripts`` packages. + It handles creating the virtual environment, creating scripts in + ``pyvenv/bin``, and calling ``pip`` to install dependencies. + ``tests/Makefile.include`` - Rules for external test harnesses. These include the TCG tests, - ``qemu-iotests`` and the Avocado-based integration tests. + Rules for external test harnesses. These include the TCG tests + and the Avocado-based integration tests. ``tests/docker/Makefile.include`` - Rules for Docker tests. Like tests/Makefile, this file is included - directly by the top level Makefile, anything defined in this file will - influence the entire build system. + Rules for Docker tests. Like ``tests/Makefile.include``, this file is + included directly by the top level Makefile, anything defined in this + file will influence the entire build system. ``tests/vm/Makefile.include`` - Rules for VM-based tests. Like tests/Makefile, this file is included - directly by the top level Makefile, anything defined in this file will - influence the entire build system. + Rules for VM-based tests. Like ``tests/Makefile.include``, this file is + included directly by the top level Makefile, anything defined in this + file will influence the entire build system. Dynamically created files ------------------------- -The following files are generated dynamically by configure in order to -control the behaviour of the statically defined makefiles. This avoids -the need for QEMU makefiles to go through any pre-processing as seen -with autotools, where Makefile.am generates Makefile.in which generates -Makefile. +The following files are generated at run-time in order to control the +behaviour of the Makefiles. This avoids the need for QEMU makefiles to +go through any pre-processing as seen with autotools, where configure +generates ``Makefile`` from ``Makefile.in``. Built by configure: ``config-host.mak`` When configure has determined the characteristics of the build host it - will write a long list of variables to config-host.mak file. This - provides the various install directories, compiler / linker flags and a - variety of ``CONFIG_*`` variables related to optionally enabled features. - This is imported by the top level Makefile and meson.build in order to - tailor the build output. + will write the paths to various tools to this file, for use in ``Makefile`` + and to a smaller extent ``meson.build``. - config-host.mak is also used as a dependency checking mechanism. If make + ``config-host.mak`` is also used as a dependency checking mechanism. If make sees that the modification timestamp on configure is newer than that on - config-host.mak, then configure will be re-run. + ``config-host.mak``, then configure will be re-run. - The variables defined here are those which are applicable to all QEMU - build outputs. Variables which are potentially different for each - emulator target are defined by the next file... +``config-meson.cross`` + A Meson "cross file" (or native file) used to communicate the paths to + the toolchain and other configuration options. + +``config.status`` + + A small shell script that will invoke configure again with the same + environment variables that were set during the first run. It's used to + rerun configure after changes to the source code, but it can also be + inspected manually to check the contents of the environment. + +``Makefile.prereqs`` + + A set of Makefile dependencies that order the build and execution of + firmware and tests after the container images and emulators that they + need. + +``pc-bios/*/config.mak``, ``tests/tcg/config-host.mak``, ``tests/tcg/*/config-target.mak`` + + Configuration variables used to build the firmware and TCG tests, + including paths to cross compilation toolchains. + +``pyvenv`` + + A Python virtual environment that is used for all Python code running + during the build. Using a virtual environment ensures that even code + that is run via ``sphinx-build``, ``meson`` etc. uses the same interpreter + and packages. Built by Meson: -``${TARGET-NAME}-config-devices.mak`` - TARGET-NAME is again the name of a system or userspace emulator. The - config-devices.mak file is automatically generated by make using the - scripts/make_device_config.sh program, feeding it the - default-configs/$TARGET-NAME file as input. +``config-host.h`` + Used by C code to determine the properties of the build environment + and the set of enabled features for the entire build. -``config-host.h``, ``$TARGET_NAME-config-target.h``, ``$TARGET_NAME-config-devices.h`` - These files are used by source code to determine what features are - enabled. They are generated from the contents of the corresponding - ``*.mak`` files using Meson's ``configure_file()`` function. +``${TARGET-NAME}-config-devices.mak`` + TARGET-NAME is the name of a system emulator. The file is + generated by Meson using files under ``configs/devices`` as input. + +``${TARGET-NAME}-config-target.mak`` + TARGET-NAME is the name of a system or usermode emulator. The file is + generated by Meson using files under ``configs/targets`` as input. + +``$TARGET_NAME-config-target.h``, ``$TARGET_NAME-config-devices.h`` + Used by C code to determine the properties and enabled + features for each target. enabled. They are generated from + the contents of the corresponding ``*.mak`` files using Meson's + ``configure_file()`` function; each target can include them using + the ``CONFIG_TARGET`` and ``CONFIG_DEVICES`` macro respectively. ``build.ninja`` The build rules. diff --git a/docs/devel/ci-jobs.rst.inc b/docs/devel/ci-jobs.rst.inc index 1f28fec0d0..be06322279 100644 --- a/docs/devel/ci-jobs.rst.inc +++ b/docs/devel/ci-jobs.rst.inc @@ -70,6 +70,17 @@ in a handful of namespaces repository CI settings, or as git push variables, to influence which jobs get run in a pipeline + * QEMU_CI_CONTAINER_TAG - the tag used to publish containers + in stage 1, for use by build jobs in stage 2. Defaults to + 'latest', but if running pipelines for different branches + concurrently, it should be overridden per pipeline. + + * QEMU_CI_UPSTREAM - gitlab namespace that is considered to be + the 'upstream'. This defaults to 'qemu-project'. Contributors + may choose to override this if they are modifying rules in + base.yml and need to validate how they will operate when in + an upstream context, as opposed to their fork context. + * nnn - other misc variables not falling into the above categories, or using different names for historical reasons and not yet converted. @@ -104,7 +115,7 @@ CI pipeline. QEMU_JOB_SKIPPED ~~~~~~~~~~~~~~~~ -The job is not reliably successsful in general, so is not +The job is not reliably successful in general, so is not currently suitable to be run by default. Ideally this should be a temporary marker until the problems can be addressed, or the job permanently removed. @@ -136,7 +147,7 @@ Set this variable to 1 to create the pipelines, but leave all the jobs to be manually started from the UI Set this variable to 2 to create the pipelines and run all -the jobs immediately, as was historicaly behaviour +the jobs immediately, as was the historical behaviour QEMU_CI_AVOCADO_TESTING ~~~~~~~~~~~~~~~~~~~~~~~ @@ -177,3 +188,10 @@ If you've got access to a CentOS Stream 8 x86_64 host that can be used as a gitlab-CI runner, you can set this variable to enable the tests that require this kind of host. The runner should be tagged with both "centos_stream_8" and "x86_64". + +CCACHE_DISABLE +~~~~~~~~~~~~~~ +The jobs are configured to use "ccache" by default since this typically +reduces compilation time, at the cost of increased storage. If the +use of "ccache" is suspected to be hurting the overall job execution +time, setting the "CCACHE_DISABLE=1" env variable to disable it. diff --git a/docs/devel/clocks.rst b/docs/devel/clocks.rst index 675fbeb6ab..177ee1c90d 100644 --- a/docs/devel/clocks.rst +++ b/docs/devel/clocks.rst @@ -279,6 +279,10 @@ You can change the multiplier and divider of a clock at runtime, so you can use this to model clock controller devices which have guest-programmable frequency multipliers or dividers. +Similarly to ``clock_set()``, ``clock_set_mul_div()`` returns ``true`` if +the clock state was modified; that is, if the multiplier or the diviser +or both were changed by the call. + Note that ``clock_set_mul_div()`` does not automatically call ``clock_propagate()``. If you make a runtime change to the multiplier or divider you must call clock_propagate() yourself. @@ -502,7 +506,7 @@ This is typically used to migrate an input clock state. For example: VMStateDescription my_device_vmstate = { .name = "my_device", - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { [...], /* other migrated fields */ VMSTATE_CLOCK(clk, MyDeviceState), VMSTATE_END_OF_LIST() diff --git a/docs/devel/code-of-conduct.rst b/docs/devel/code-of-conduct.rst index 195444d1b4..f734ed0317 100644 --- a/docs/devel/code-of-conduct.rst +++ b/docs/devel/code-of-conduct.rst @@ -1,3 +1,5 @@ +.. _code_of_conduct: + Code of Conduct =============== diff --git a/docs/devel/decodetree.rst b/docs/devel/decodetree.rst index 49ea50c2a7..e3392aa705 100644 --- a/docs/devel/decodetree.rst +++ b/docs/devel/decodetree.rst @@ -23,22 +23,42 @@ Fields Syntax:: - field_def := '%' identifier ( unnamed_field )* ( !function=identifier )? + field_def := '%' identifier ( field )* ( !function=identifier )? + field := unnamed_field | named_field unnamed_field := number ':' ( 's' ) number + named_field := identifier ':' ( 's' ) number For *unnamed_field*, the first number is the least-significant bit position of the field and the second number is the length of the field. If the 's' is -present, the field is considered signed. If multiple ``unnamed_fields`` are -present, they are concatenated. In this way one can define disjoint fields. +present, the field is considered signed. + +A *named_field* refers to some other field in the instruction pattern +or format. Regardless of the length of the other field where it is +defined, it will be inserted into this field with the specified +signedness and bit width. + +Field definitions that involve loops (i.e. where a field is defined +directly or indirectly in terms of itself) are errors. + +A format can include fields that refer to named fields that are +defined in the instruction pattern(s) that use the format. +Conversely, an instruction pattern can include fields that refer to +named fields that are defined in the format it uses. However you +cannot currently do both at once (i.e. pattern P uses format F; F has +a field A that refers to a named field B that is defined in P, and P +has a field C that refers to a named field D that is defined in F). + +If multiple ``fields`` are present, they are concatenated. +In this way one can define disjoint fields. If ``!function`` is specified, the concatenated result is passed through the named function, taking and returning an integral value. -One may use ``!function`` with zero ``unnamed_fields``. This case is called +One may use ``!function`` with zero ``fields``. This case is called a *parameter*, and the named function is only passed the ``DisasContext`` and returns an integral value extracted from there. -A field with no ``unnamed_fields`` and no ``!function`` is in error. +A field with no ``fields`` and no ``!function`` is in error. Field examples: @@ -56,6 +76,9 @@ Field examples: | %shimm8 5:s8 13:1 | expand_shimm8(sextract(i, 5, 8) << 1 | | | !function=expand_shimm8 | extract(i, 13, 1)) | +---------------------------+---------------------------------------------+ +| %sz_imm 10:2 sz:3 | expand_sz_imm(extract(i, 10, 2) << 3 | | +| !function=expand_sz_imm | extract(a->sz, 0, 3)) | ++---------------------------+---------------------------------------------+ Argument Sets ============= diff --git a/docs/devel/docs.rst b/docs/devel/docs.rst new file mode 100644 index 0000000000..a7768b5311 --- /dev/null +++ b/docs/devel/docs.rst @@ -0,0 +1,68 @@ + +================== +QEMU Documentation +================== + +QEMU's documentation is written in reStructuredText format and +built using the Sphinx documentation generator. We generate both +the HTML manual and the manpages from the some documentation sources. + +hxtool and .hx files +-------------------- + +The documentation for QEMU command line options and Human Monitor Protocol +(HMP) commands is written in files with the ``.hx`` suffix. These +are processed in two ways: + + * ``scripts/hxtool`` creates C header files from them, which are included + in QEMU to do things like handle the ``--help`` option output + * a Sphinx extension in ``docs/sphinx/hxtool.py`` generates rST output + to be included in the HTML or manpage documentation + +The syntax of these ``.hx`` files is simple. It is broadly an +alternation of C code put into the C output and rST format text +put into the documentation. A few special directives are recognised; +these are all-caps and must be at the beginning of the line. + +``HXCOMM`` is the comment marker. The line, including any arbitrary +text after the marker, is discarded and appears neither in the C output +nor the documentation output. + +``SRST`` starts a reStructuredText section. Following lines +are put into the documentation verbatim, and discarded from the C output. +The alternative form ``SRST()`` is used to define a label which can be +referenced from elsewhere in the rST documentation. The label will take +the form ````, where ``DOCNAME`` is the name of the +top level rST file, ``HXFILE`` is the filename of the .hx file without +the ``.hx`` extension, and ``LABEL`` is the text provided within the +``SRST()`` directive. For example, +````. + +``ERST`` ends the documentation section started with ``SRST``, +and switches back to a C code section. + +``DEFHEADING()`` defines a heading that should appear in both the +``--help`` output and in the documentation. This directive should +be in the C code block. If there is a string inside the brackets, +this is the heading to use. If this string is empty, it produces +a blank line in the ``--help`` output and is ignored for the rST +output. + +``ARCHHEADING()`` is a variant of ``DEFHEADING()`` which produces +the heading only if the specified guest architecture was compiled +into QEMU. This should be avoided in new documentation. + +Within C code sections, you should check the comments at the top +of the file to see what the expected usage is, because this +varies between files. For instance in ``qemu-options.hx`` we use +the ``DEF()`` macro to define each option and specify its ``--help`` +text, but in ``hmp-commands.hx`` the C code sections are elements +of an array of structs of type ``HMPCommand`` which define the +name, behaviour and help text for each monitor command. + +In the file ``qemu-options.hx``, do not try to explicitly define a +reStructuredText label within a documentation section. This file +is included into two separate Sphinx documents, and some +versions of Sphinx will complain about the duplicate label +that results. Use the ``SRST()`` directive documented above, to +emit an unambiguous label. diff --git a/docs/devel/fuzzing.rst b/docs/devel/fuzzing.rst index 784ecb99e6..3bfcb33fc4 100644 --- a/docs/devel/fuzzing.rst +++ b/docs/devel/fuzzing.rst @@ -19,11 +19,6 @@ 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 maps ~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). Here, enable-sanitizers, is optional but it allows us to reliably detect bugs @@ -287,8 +282,8 @@ 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 +After this, the ``vl.c:main`` is called to set up the guest. There are +target-specific hooks that can be called before and after main, for additional setup(e.g. PCI setup, or VM snapshotting). ``LLVMFuzzerTestOneInput``: Uses qtest/qos functions to act based on the fuzz @@ -296,10 +291,9 @@ input. It is also responsible for manually calling ``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: +be reset at the end of each run. For example, this can be done by rebooting the +VM, after each run. -- 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 @@ -308,15 +302,3 @@ options for resetting state: reboot. - *Example target*: ``i440fx-qtest-reboot-fuzz`` - -- 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-api.rst b/docs/devel/index-api.rst index 60c0d7459d..fe01b2b488 100644 --- a/docs/devel/index-api.rst +++ b/docs/devel/index-api.rst @@ -11,4 +11,8 @@ generated from in-code annotations to function prototypes. loads-stores memory modules + pci + qom-api + qdev-api ui + zoned-storage diff --git a/docs/devel/index-build.rst b/docs/devel/index-build.rst index 1002a533a6..90b406ca0e 100644 --- a/docs/devel/index-build.rst +++ b/docs/devel/index-build.rst @@ -10,7 +10,9 @@ the basics if you are adding new files and targets to the build. build-system kconfig + docs testing + acpi-bits qtest ci qapi-code-gen diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst index e1a93df263..5636e9cf1d 100644 --- a/docs/devel/index-internals.rst +++ b/docs/devel/index-internals.rst @@ -11,11 +11,12 @@ Details about QEMU's various subsystems including how to add features to them. block-coroutine-wrapper clocks ebpf_rss - migration + migration/index multi-process reset + s390-cpu-topology s390-dasd-ipl tracing - vfio-migration + vfio-iommufd writing-monitor-commands virtio-backends diff --git a/docs/devel/index-process.rst b/docs/devel/index-process.rst index d0d7a200fd..362f97ee30 100644 --- a/docs/devel/index-process.rst +++ b/docs/devel/index-process.rst @@ -1,3 +1,5 @@ +.. _development_process: + QEMU Community Processes ------------------------ @@ -8,6 +10,7 @@ Notes about how to interact with the community and how and where to submit patch code-of-conduct conflict-resolution + maintainers style submitting-a-patch trivial-patches diff --git a/docs/devel/index-tcg.rst b/docs/devel/index-tcg.rst index 7b9760b26f..a992844e5c 100644 --- a/docs/devel/index-tcg.rst +++ b/docs/devel/index-tcg.rst @@ -1,3 +1,5 @@ +.. _tcg: + TCG Emulation ------------- @@ -9,6 +11,7 @@ are only implementing things for HW accelerated hypervisors. :maxdepth: 2 tcg + tcg-ops decodetree multi-thread-tcg tcg-icount diff --git a/docs/devel/index.rst b/docs/devel/index.rst index 09cfb322be..abf60457c2 100644 --- a/docs/devel/index.rst +++ b/docs/devel/index.rst @@ -2,10 +2,30 @@ Developer Information --------------------- -This section of the manual documents various parts of the internals of QEMU. -You only need to read it if you are interested in reading or +This section of the manual documents various parts of the internals of +QEMU. You only need to read it if you are interested in reading or modifying QEMU's source code. +QEMU is a large and mature project with a number of complex subsystems +that can be overwhelming to understand. The development documentation +is not comprehensive but hopefully presents enough to get you started. +If there are areas that are unclear please reach out either via the +IRC channel or mailing list and hopefully we can improve the +documentation for future developers. + +All developers will want to familiarise themselves with +:ref:`development_process` and how the community interacts. Please pay +particular attention to the :ref:`coding-style` and +:ref:`submitting-a-patch` sections to avoid common pitfalls. + +If you wish to implement a new hardware model you will want to read +through the :ref:`qom` documentation to understand how QEMU's object +model works. + +Those wishing to enhance or add new CPU emulation capabilities will +want to read our :ref:`tcg` documentation, especially the overview of +the :ref:`tcg_internals`. + .. toctree:: :maxdepth: 1 diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst index 69674d008a..ccb9a46bd7 100644 --- a/docs/devel/kconfig.rst +++ b/docs/devel/kconfig.rst @@ -274,7 +274,7 @@ or commenting out lines in the second group. It is also possible to run QEMU's configure script with the ``--without-default-devices`` option. When this is done, everything defaults -to ``n`` unless it is ``select``ed or explicitly switched on in the +to ``n`` unless it is ``select``\ ed or explicitly switched on in the ``.mak`` files. In other words, ``default`` and ``imply`` directives are disabled. When QEMU is built with this option, the user will probably want to change some lines in the first group, for example like this:: @@ -282,9 +282,19 @@ want to change some lines in the first group, for example like this:: CONFIG_PCI_DEVICES=y #CONFIG_TEST_DEVICES=n -and/or pick a subset of the devices in those device groups. Right now -there is no single place that lists all the optional devices for -``CONFIG_PCI_DEVICES`` and ``CONFIG_TEST_DEVICES``. In the future, +and/or pick a subset of the devices in those device groups. Without +further modifications to ``configs/devices/``, a system emulator built +without default devices might not do much more than start an empty +machine, and even then only if ``--nodefaults`` is specified on the +command line. Starting a VM *without* ``--nodefaults`` is allowed to +fail, but should never abort. Failures in ``make check`` with +``--without-default-devices`` are considered bugs in the test code: +the tests should either use ``--nodefaults``, and should be skipped +if a necessary device is not present in the build. Such failures +should not be worked around with ``select`` directives. + +Right now there is no single place that lists all the optional devices +for ``CONFIG_PCI_DEVICES`` and ``CONFIG_TEST_DEVICES``. In the future, we expect that ``.mak`` files will be automatically generated, so that they will include all these symbols and some help text on what they do. @@ -306,6 +316,6 @@ variable:: host_kconfig = \ (have_tpm ? ['CONFIG_TPM=y'] : []) + \ - ('CONFIG_SPICE' in config_host ? ['CONFIG_SPICE=y'] : []) + \ + (host_os == 'linux' ? ['CONFIG_LINUX=y'] : []) + \ (have_ivshmem ? ['CONFIG_IVSHMEM=y'] : []) + \ ... diff --git a/docs/devel/loads-stores.rst b/docs/devel/loads-stores.rst index ad5dfe133e..ec627aa9c0 100644 --- a/docs/devel/loads-stores.rst +++ b/docs/devel/loads-stores.rst @@ -36,6 +36,7 @@ store: ``st{size}_{endian}_p(ptr, val)`` ``size`` - ``b`` : 8 bits - ``w`` : 16 bits + - ``24`` : 24 bits - ``l`` : 32 bits - ``q`` : 64 bits @@ -62,11 +63,12 @@ which stores ``val`` to ``ptr`` as an ``{endian}`` order value of size ``sz`` bytes. -Regexes for git grep +Regexes for git grep: - ``\`` - ``\`` - - ``\`` - - ``\`` + - ``\`` + - ``\`` + - ``\`` ``cpu_{ld,st}*_mmu`` ~~~~~~~~~~~~~~~~~~~~ @@ -119,8 +121,8 @@ store: ``cpu_st{size}{end}_mmu(env, ptr, val, oi, retaddr)`` - ``_le`` : little endian Regexes for git grep: - - ``\`` - - ``\`` + - ``\`` + - ``\`` ``cpu_{ld,st}*_mmuidx_ra`` @@ -153,8 +155,8 @@ store: ``cpu_st{size}{end}_mmuidx_ra(env, ptr, val, mmuidx, retaddr)`` - ``_le`` : little endian Regexes for git grep: - - ``\`` - - ``\`` + - ``\`` + - ``\`` ``cpu_{ld,st}*_data_ra`` ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -191,8 +193,8 @@ store: ``cpu_st{size}{end}_data_ra(env, ptr, val, ra)`` - ``_le`` : little endian Regexes for git grep: - - ``\`` - - ``\`` + - ``\`` + - ``\`` ``cpu_{ld,st}*_data`` ~~~~~~~~~~~~~~~~~~~~~ @@ -229,9 +231,9 @@ store: ``cpu_st{size}{end}_data(env, ptr, val)`` - ``_be`` : big endian - ``_le`` : little endian -Regexes for git grep - - ``\`` - - ``\`` +Regexes for git grep: + - ``\`` + - ``\`` ``cpu_ld*_code`` ~~~~~~~~~~~~~~~~ @@ -294,34 +296,23 @@ swap: ``translator_ld{sign}{size}_swap(env, ptr, swap)`` - ``l`` : 32 bits - ``q`` : 64 bits -Regexes for git grep +Regexes for git grep: - ``\`` -``helper_*_{ld,st}*_mmu`` +``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}_mmuidx_ra`` functions -they perform accesses by guest virtual address, with a given ``mmuidx``. +generated by the TCG backend. Like the ``cpu_{ld,st}_mmu`` functions +they perform accesses by guest virtual address, with a given ``MemOpIdx``. -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()``. +They differ from ``cpu_{ld,st}_mmu`` in that they take the endianness +of the operation only from the MemOpIdx, and loads extend the return +value to the size of a host general register (``tcg_target_ulong``). -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 -unwinding is required). +load: ``helper_ld{sign}{size}_mmu(env, addr, opindex, retaddr)`` -**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. The explicit endianness -is the only feature they have beyond ``*_mmuidx_ra``. - -load: ``helper_{endian}_ld{sign}{size}_mmu(env, addr, opindex, retaddr)`` - -store: ``helper_{endian}_st{size}_mmu(env, addr, val, opindex, retaddr)`` +store: ``helper_{size}_mmu(env, addr, val, opindex, retaddr)`` ``sign`` - (empty) : for 32 or 64 bit sizes @@ -334,14 +325,9 @@ store: ``helper_{endian}_st{size}_mmu(env, addr, val, opindex, retaddr)`` - ``l`` : 32 bits - ``q`` : 64 bits -``endian`` - - ``le`` : little endian - - ``be`` : big endian - - ``ret`` : target endianness - -Regexes for git grep - - ``\`` - - ``\`` +Regexes for git grep: + - ``\`` + - ``\`` ``address_space_*`` ~~~~~~~~~~~~~~~~~~~ @@ -396,7 +382,7 @@ succeeded using a MemTxResult return code. The ``_{endian}`` suffix is omitted for byte accesses. -Regexes for git grep +Regexes for git grep: - ``\`` - ``\`` - ``\`` @@ -414,7 +400,7 @@ Note that portions of the write which attempt to write data to a device will be silently ignored -- only real RAM and ROM will be written to. -Regexes for git grep +Regexes for git grep: - ``address_space_write_rom`` ``{ld,st}*_phys`` @@ -452,7 +438,7 @@ device doing the access has no way to report such an error. The ``_{endian}_`` infix is omitted for byte accesses. -Regexes for git grep +Regexes for git grep: - ``\`` - ``\`` @@ -476,7 +462,7 @@ For new code they are better avoided: ``cpu_physical_memory_rw`` -Regexes for git grep +Regexes for git grep: - ``\`` ``cpu_memory_rw_debug`` @@ -511,7 +497,7 @@ make sure our existing code is doing things correctly. ``dma_memory_rw`` -Regexes for git grep +Regexes for git grep: - ``\`` - ``\`` - ``\`` @@ -552,7 +538,7 @@ correct address space for that device. The ``_{endian}_`` infix is omitted for byte accesses. -Regexes for git grep +Regexes for git grep: - ``\`` - ``\`` - ``\`` diff --git a/docs/devel/maintainers.rst b/docs/devel/maintainers.rst new file mode 100644 index 0000000000..5c907d901c --- /dev/null +++ b/docs/devel/maintainers.rst @@ -0,0 +1,107 @@ +.. _maintainers: + +The Role of Maintainers +======================= + +Maintainers are a critical part of the project's contributor ecosystem. +They come from a wide range of backgrounds from unpaid hobbyists +working in their spare time to employees who work on the project as +part of their job. Maintainer activities include: + + - reviewing patches and suggesting changes + - collecting patches and preparing pull requests + - tending to the long term health of their area + - participating in other project activities + +They are also human and subject to the same pressures as everyone else +including overload and burnout. Like everyone else they are subject +to project's :ref:`code_of_conduct` and should also be exemplars of +excellent community collaborators. + +The MAINTAINERS file +-------------------- + +The `MAINTAINERS +`__ +file contains the canonical list of who is a maintainer. The file +is machine readable so an appropriately configured git (see +:ref:`cc_the_relevant_maintainer`) can automatically Cc them on +patches that touch their area of code. + +The file also describes the status of the area of code to give an idea +of how actively that section is maintained. + +.. list-table:: Meaning of support status in MAINTAINERS + :widths: 25 75 + :header-rows: 1 + + * - Status + - Meaning + * - Supported + - Someone is actually paid to look after this. + * - Maintained + - Someone actually looks after it. + * - Odd Fixes + - It has a maintainer but they don't have time to do + much other than throw the odd patch in. + * - Orphan + - No current maintainer. + * - Obsolete + - Old obsolete code, should use something else. + +Please bear in mind that even if someone is paid to support something +it does not mean they are paid to support you. This is open source and +the code comes with no warranty and the project makes no guarantees +about dealing with bugs or features requests. + + + +Becoming a reviewer +------------------- + +Most maintainers start by becoming subsystem reviewers. While anyone +is welcome to review code on the mailing list getting added to the +MAINTAINERS file with a line like:: + + R: Random Hacker + +marks you as a 'designated reviewer' - expected to provide regular +spontaneous feedback. This will ensure that patches touching a given +subsystem will automatically be CC'd to you. + +Becoming a maintainer +--------------------- + +Maintainers are volunteers who put themselves forward or have been +asked by others to keep an eye on an area of code. They have generally +demonstrated to the community, usually via contributions and code +reviews, that they have a good understanding of the subsystem. They +are also trusted to make a positive contribution to the project and +work well with the other contributors. + +The process is simple - simply send a patch to the list that updates +the ``MAINTAINERS`` file. Sometimes this is done as part of a larger +series when a new sub-system is being added to the code base. This can +also be done by a retiring maintainer who nominates their replacement +after discussion with other contributors. + +Once the patch is reviewed and merged the only other step is to make +sure your GPG key is signed. + +.. _maintainer_keys: + +Maintainer GPG Keys +~~~~~~~~~~~~~~~~~~~ + +GPG is used to sign pull requests so they can be identified as really +coming from the maintainer. If your key is not already signed by +members of the QEMU community, you should make arrangements to attend +a `KeySigningParty `__ (for +example at KVM Forum) or make alternative arrangements to have your +key signed by an attendee. Key signing requires meeting another +community member **in person** [#]_ so please make appropriate +arrangements. + +.. [#] In recent pandemic times we have had to exercise some + flexibility here. Maintainers still need to sign their pull + requests though. diff --git a/docs/devel/migration/CPR.rst b/docs/devel/migration/CPR.rst new file mode 100644 index 0000000000..63c36470cf --- /dev/null +++ b/docs/devel/migration/CPR.rst @@ -0,0 +1,147 @@ +CheckPoint and Restart (CPR) +============================ + +CPR is the umbrella name for a set of migration modes in which the +VM is migrated to a new QEMU instance on the same host. It is +intended for use when the goal is to update host software components +that run the VM, such as QEMU or even the host kernel. At this time, +cpr-reboot is the only available mode. + +Because QEMU is restarted on the same host, with access to the same +local devices, CPR is allowed in certain cases where normal migration +would be blocked. However, the user must not modify the contents of +guest block devices between quitting old QEMU and starting new QEMU. + +CPR unconditionally stops VM execution before memory is saved, and +thus does not depend on any form of dirty page tracking. + +cpr-reboot mode +--------------- + +In this mode, QEMU stops the VM, and writes VM state to the migration +URI, which will typically be a file. After quitting QEMU, the user +resumes by running QEMU with the ``-incoming`` option. Because the +old and new QEMU instances are not active concurrently, the URI cannot +be a type that streams data from one instance to the other. + +Guest RAM can be saved in place if backed by shared memory, or can be +copied to a file. The former is more efficient and is therefore +preferred. + +After state and memory are saved, the user may update userland host +software before restarting QEMU and resuming the VM. Further, if +the RAM is backed by persistent shared memory, such as a DAX device, +then the user may reboot to a new host kernel before restarting QEMU. + +This mode supports VFIO devices provided the user first puts the +guest in the suspended runstate, such as by issuing the +``guest-suspend-ram`` command to the QEMU guest agent. The agent +must be pre-installed in the guest, and the guest must support +suspend to RAM. Beware that suspension can take a few seconds, so +the user should poll to see the suspended state before proceeding +with the CPR operation. + +Usage +^^^^^ + +It is recommended that guest RAM be backed with some type of shared +memory, such as ``memory-backend-file,share=on``, and that the +``x-ignore-shared`` capability be set. This combination allows memory +to be saved in place. Otherwise, after QEMU stops the VM, all guest +RAM is copied to the migration URI. + +Outgoing: + * Set the migration mode parameter to ``cpr-reboot``. + * Set the ``x-ignore-shared`` capability if desired. + * Issue the ``migrate`` command. It is recommended the the URI be a + ``file`` type, but one can use other types such as ``exec``, + provided the command captures all the data from the outgoing side, + and provides all the data to the incoming side. + * Quit when QEMU reaches the postmigrate state. + +Incoming: + * Start QEMU with the ``-incoming defer`` option. + * Set the migration mode parameter to ``cpr-reboot``. + * Set the ``x-ignore-shared`` capability if desired. + * Issue the ``migrate-incoming`` command. + * If the VM was running when the outgoing ``migrate`` command was + issued, then QEMU automatically resumes VM execution. + +Example 1 +^^^^^^^^^ +:: + + # qemu-kvm -monitor stdio + -object memory-backend-file,id=ram0,size=4G,mem-path=/dev/dax0.0,align=2M,share=on -m 4G + ... + + (qemu) info status + VM status: running + (qemu) migrate_set_parameter mode cpr-reboot + (qemu) migrate_set_capability x-ignore-shared on + (qemu) migrate -d file:vm.state + (qemu) info status + VM status: paused (postmigrate) + (qemu) quit + + ### optionally update kernel and reboot + # systemctl kexec + kexec_core: Starting new kernel + ... + + # qemu-kvm ... -incoming defer + (qemu) info status + VM status: paused (inmigrate) + (qemu) migrate_set_parameter mode cpr-reboot + (qemu) migrate_set_capability x-ignore-shared on + (qemu) migrate_incoming file:vm.state + (qemu) info status + VM status: running + +Example 2: VFIO +^^^^^^^^^^^^^^^ +:: + + # qemu-kvm -monitor stdio + -object memory-backend-file,id=ram0,size=4G,mem-path=/dev/dax0.0,align=2M,share=on -m 4G + -device vfio-pci, ... + -chardev socket,id=qga0,path=qga.sock,server=on,wait=off + -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0 + ... + + (qemu) info status + VM status: running + + # echo '{"execute":"guest-suspend-ram"}' | ncat --send-only -U qga.sock + + (qemu) info status + VM status: paused (suspended) + (qemu) migrate_set_parameter mode cpr-reboot + (qemu) migrate_set_capability x-ignore-shared on + (qemu) migrate -d file:vm.state + (qemu) info status + VM status: paused (postmigrate) + (qemu) quit + + ### optionally update kernel and reboot + # systemctl kexec + kexec_core: Starting new kernel + ... + + # qemu-kvm ... -incoming defer + (qemu) info status + VM status: paused (inmigrate) + (qemu) migrate_set_parameter mode cpr-reboot + (qemu) migrate_set_capability x-ignore-shared on + (qemu) migrate_incoming file:vm.state + (qemu) info status + VM status: paused (suspended) + (qemu) system_wakeup + (qemu) info status + VM status: running + +Caveats +^^^^^^^ + +cpr-reboot mode may not be used with postcopy, background-snapshot, +or COLO. diff --git a/docs/devel/migration/best-practices.rst b/docs/devel/migration/best-practices.rst new file mode 100644 index 0000000000..d7c34a3014 --- /dev/null +++ b/docs/devel/migration/best-practices.rst @@ -0,0 +1,48 @@ +============== +Best practices +============== + +Debugging +========= + +The migration stream can be analyzed thanks to ``scripts/analyze-migration.py``. + +Example usage: + +.. code-block:: shell + + $ qemu-system-x86_64 -display none -monitor stdio + (qemu) migrate "exec:cat > mig" + (qemu) q + $ ./scripts/analyze-migration.py -f mig + { + "ram (3)": { + "section sizes": { + "pc.ram": "0x0000000008000000", + ... + +See also ``analyze-migration.py -h`` help for more options. + +Firmware +======== + +Migration migrates the copies of RAM and ROM, and thus when running +on the destination it includes the firmware from the source. Even after +resetting a VM, the old firmware is used. Only once QEMU has been restarted +is the new firmware in use. + +- Changes in firmware size can cause changes in the required RAMBlock size + to hold the firmware and thus migration can fail. In practice it's best + to pad firmware images to convenient powers of 2 with plenty of space + for growth. + +- Care should be taken with device emulation code so that newer + emulation code can work with older firmware to allow forward migration. + +- Care should be taken with newer firmware so that backward migration + to older systems with older device emulation code will work. + +In some cases it may be best to tie specific firmware versions to specific +versioned machine types to cut down on the combinations that will need +support. This is also useful when newer versions of firmware outgrow +the padding. diff --git a/docs/devel/migration/compatibility.rst b/docs/devel/migration/compatibility.rst new file mode 100644 index 0000000000..5a5417ef06 --- /dev/null +++ b/docs/devel/migration/compatibility.rst @@ -0,0 +1,517 @@ +Backwards compatibility +======================= + +How backwards compatibility works +--------------------------------- + +When we do migration, we have two QEMU processes: the source and the +target. There are two cases, they are the same version or they are +different versions. The easy case is when they are the same version. +The difficult one is when they are different versions. + +There are two things that are different, but they have very similar +names and sometimes get confused: + +- QEMU version +- machine type version + +Let's start with a practical example, we start with: + +- qemu-system-x86_64 (v5.2), from now on qemu-5.2. +- qemu-system-x86_64 (v5.1), from now on qemu-5.1. + +Related to this are the "latest" machine types defined on each of +them: + +- pc-q35-5.2 (newer one in qemu-5.2) from now on pc-5.2 +- pc-q35-5.1 (newer one in qemu-5.1) from now on pc-5.1 + +First of all, migration is only supposed to work if you use the same +machine type in both source and destination. The QEMU hardware +configuration needs to be the same also on source and destination. +Most aspects of the backend configuration can be changed at will, +except for a few cases where the backend features influence frontend +device feature exposure. But that is not relevant for this section. + +I am going to list the number of combinations that we can have. Let's +start with the trivial ones, QEMU is the same on source and +destination: + +1 - qemu-5.2 -M pc-5.2 -> migrates to -> qemu-5.2 -M pc-5.2 + + This is the latest QEMU with the latest machine type. + This have to work, and if it doesn't work it is a bug. + +2 - qemu-5.1 -M pc-5.1 -> migrates to -> qemu-5.1 -M pc-5.1 + + Exactly the same case than the previous one, but for 5.1. + Nothing to see here either. + +This are the easiest ones, we will not talk more about them in this +section. + +Now we start with the more interesting cases. Consider the case where +we have the same QEMU version in both sides (qemu-5.2) but we are using +the latest machine type for that version (pc-5.2) but one of an older +QEMU version, in this case pc-5.1. + +3 - qemu-5.2 -M pc-5.1 -> migrates to -> qemu-5.2 -M pc-5.1 + + It needs to use the definition of pc-5.1 and the devices as they + were configured on 5.1, but this should be easy in the sense that + both sides are the same QEMU and both sides have exactly the same + idea of what the pc-5.1 machine is. + +4 - qemu-5.1 -M pc-5.2 -> migrates to -> qemu-5.1 -M pc-5.2 + + This combination is not possible as the qemu-5.1 doesn't understand + pc-5.2 machine type. So nothing to worry here. + +Now it comes the interesting ones, when both QEMU processes are +different. Notice also that the machine type needs to be pc-5.1, +because we have the limitation than qemu-5.1 doesn't know pc-5.2. So +the possible cases are: + +5 - qemu-5.2 -M pc-5.1 -> migrates to -> qemu-5.1 -M pc-5.1 + + This migration is known as newer to older. We need to make sure + when we are developing 5.2 we need to take care about not to break + migration to qemu-5.1. Notice that we can't make updates to + qemu-5.1 to understand whatever qemu-5.2 decides to change, so it is + in qemu-5.2 side to make the relevant changes. + +6 - qemu-5.1 -M pc-5.1 -> migrates to -> qemu-5.2 -M pc-5.1 + + This migration is known as older to newer. We need to make sure + than we are able to receive migrations from qemu-5.1. The problem is + similar to the previous one. + +If qemu-5.1 and qemu-5.2 were the same, there will not be any +compatibility problems. But the reason that we create qemu-5.2 is to +get new features, devices, defaults, etc. + +If we get a device that has a new feature, or change a default value, +we have a problem when we try to migrate between different QEMU +versions. + +So we need a way to tell qemu-5.2 that when we are using machine type +pc-5.1, it needs to **not** use the feature, to be able to migrate to +real qemu-5.1. + +And the equivalent part when migrating from qemu-5.1 to qemu-5.2. +qemu-5.2 has to expect that it is not going to get data for the new +feature, because qemu-5.1 doesn't know about it. + +How do we tell QEMU about these device feature changes? In +hw/core/machine.c:hw_compat_X_Y arrays. + +If we change a default value, we need to put back the old value on +that array. And the device, during initialization needs to look at +that array to see what value it needs to get for that feature. And +what are we going to put in that array, the value of a property. + +To create a property for a device, we need to use one of the +DEFINE_PROP_*() macros. See include/hw/qdev-properties.h to find the +macros that exist. With it, we set the default value for that +property, and that is what it is going to get in the latest released +version. But if we want a different value for a previous version, we +can change that in the hw_compat_X_Y arrays. + +hw_compat_X_Y is an array of registers that have the format: + +- name_device +- name_property +- value + +Let's see a practical example. + +In qemu-5.2 virtio-blk-device got multi queue support. This is a +change that is not backward compatible. In qemu-5.1 it has one +queue. In qemu-5.2 it has the same number of queues as the number of +cpus in the system. + +When we are doing migration, if we migrate from a device that has 4 +queues to a device that have only one queue, we don't know where to +put the extra information for the other 3 queues, and we fail +migration. + +Similar problem when we migrate from qemu-5.1 that has only one queue +to qemu-5.2, we only sent information for one queue, but destination +has 4, and we have 3 queues that are not properly initialized and +anything can happen. + +So, how can we address this problem. Easy, just convince qemu-5.2 +that when it is running pc-5.1, it needs to set the number of queues +for virtio-blk-devices to 1. + +That way we fix the cases 5 and 6. + +5 - qemu-5.2 -M pc-5.1 -> migrates to -> qemu-5.1 -M pc-5.1 + + qemu-5.2 -M pc-5.1 sets number of queues to be 1. + qemu-5.1 -M pc-5.1 expects number of queues to be 1. + + correct. migration works. + +6 - qemu-5.1 -M pc-5.1 -> migrates to -> qemu-5.2 -M pc-5.1 + + qemu-5.1 -M pc-5.1 sets number of queues to be 1. + qemu-5.2 -M pc-5.1 expects number of queues to be 1. + + correct. migration works. + +And now the other interesting case, case 3. In this case we have: + +3 - qemu-5.2 -M pc-5.1 -> migrates to -> qemu-5.2 -M pc-5.1 + + Here we have the same QEMU in both sides. So it doesn't matter a + lot if we have set the number of queues to 1 or not, because + they are the same. + + WRONG! + + Think what happens if we do one of this double migrations: + + A -> migrates -> B -> migrates -> C + + where: + + A: qemu-5.1 -M pc-5.1 + B: qemu-5.2 -M pc-5.1 + C: qemu-5.2 -M pc-5.1 + + migration A -> B is case 6, so number of queues needs to be 1. + + migration B -> C is case 3, so we don't care. But actually we + care because we haven't started the guest in qemu-5.2, it came + migrated from qemu-5.1. So to be in the safe place, we need to + always use number of queues 1 when we are using pc-5.1. + +Now, how was this done in reality? The following commit shows how it +was done:: + + commit 9445e1e15e66c19e42bea942ba810db28052cd05 + Author: Stefan Hajnoczi + Date: Tue Aug 18 15:33:47 2020 +0100 + + virtio-blk-pci: default num_queues to -smp N + +The relevant parts for migration are:: + + @@ -1281,7 +1284,8 @@ static Property virtio_blk_properties[] = { + #endif + DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, + true), + - DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1), + + DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, + + VIRTIO_BLK_AUTO_NUM_QUEUES), + DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 256), + +It changes the default value of num_queues. But it fishes it for old +machine types to have the right value:: + + @@ -31,6 +31,7 @@ + GlobalProperty hw_compat_5_1[] = { + ... + + { "virtio-blk-device", "num-queues", "1"}, + ... + }; + +A device with different features on both sides +---------------------------------------------- + +Let's assume that we are using the same QEMU binary on both sides, +just to make the things easier. But we have a device that has +different features on both sides of the migration. That can be +because the devices are different, because the kernel driver of both +devices have different features, whatever. + +How can we get this to work with migration. The way to do that is +"theoretically" easy. You have to get the features that the device +has in the source of the migration. The features that the device has +on the target of the migration, you get the intersection of the +features of both sides, and that is the way that you should launch +QEMU. + +Notice that this is not completely related to QEMU. The most +important thing here is that this should be handled by the managing +application that launches QEMU. If QEMU is configured correctly, the +migration will succeed. + +That said, actually doing it is complicated. Almost all devices are +bad at being able to be launched with only some features enabled. +With one big exception: cpus. + +You can read the documentation for QEMU x86 cpu models here: + +https://qemu-project.gitlab.io/qemu/system/qemu-cpu-models.html + +See when they talk about migration they recommend that one chooses the +newest cpu model that is supported for all cpus. + +Let's say that we have: + +Host A: + +Device X has the feature Y + +Host B: + +Device X has not the feature Y + +If we try to migrate without any care from host A to host B, it will +fail because when migration tries to load the feature Y on +destination, it will find that the hardware is not there. + +Doing this would be the equivalent of doing with cpus: + +Host A: + +$ qemu-system-x86_64 -cpu host + +Host B: + +$ qemu-system-x86_64 -cpu host + +When both hosts have different cpu features this is guaranteed to +fail. Especially if Host B has less features than host A. If host A +has less features than host B, sometimes it works. Important word of +last sentence is "sometimes". + +So, forgetting about cpu models and continuing with the -cpu host +example, let's see that the differences of the cpus is that Host A and +B have the following features: + +Features: 'pcid' 'stibp' 'taa-no' +Host A: X X +Host B: X + +And we want to migrate between them, the way configure both QEMU cpu +will be: + +Host A: + +$ qemu-system-x86_64 -cpu host,pcid=off,stibp=off + +Host B: + +$ qemu-system-x86_64 -cpu host,taa-no=off + +And you would be able to migrate between them. It is responsibility +of the management application or of the user to make sure that the +configuration is correct. QEMU doesn't know how to look at this kind +of features in general. + +Notice that we don't recommend to use -cpu host for migration. It is +used in this example because it makes the example simpler. + +Other devices have worse control about individual features. If they +want to be able to migrate between hosts that show different features, +the device needs a way to configure which ones it is going to use. + +In this section we have considered that we are using the same QEMU +binary in both sides of the migration. If we use different QEMU +versions process, then we need to have into account all other +differences and the examples become even more complicated. + +How to mitigate when we have a backward compatibility error +----------------------------------------------------------- + +We broke migration for old machine types continuously during +development. But as soon as we find that there is a problem, we fix +it. The problem is what happens when we detect after we have done a +release that something has gone wrong. + +Let see how it worked with one example. + +After the release of qemu-8.0 we found a problem when doing migration +of the machine type pc-7.2. + +- $ qemu-7.2 -M pc-7.2 -> qemu-7.2 -M pc-7.2 + + This migration works + +- $ qemu-8.0 -M pc-7.2 -> qemu-8.0 -M pc-7.2 + + This migration works + +- $ qemu-8.0 -M pc-7.2 -> qemu-7.2 -M pc-7.2 + + This migration fails + +- $ qemu-7.2 -M pc-7.2 -> qemu-8.0 -M pc-7.2 + + This migration fails + +So clearly something fails when migration between qemu-7.2 and +qemu-8.0 with machine type pc-7.2. The error messages, and git bisect +pointed to this commit. + +In qemu-8.0 we got this commit:: + + commit 010746ae1db7f52700cb2e2c46eb94f299cfa0d2 + Author: Jonathan Cameron + Date: Thu Mar 2 13:37:02 2023 +0000 + + hw/pci/aer: Implement PCI_ERR_UNCOR_MASK register + + +The relevant bits of the commit for our example are this ones:: + + --- a/hw/pci/pcie_aer.c + +++ b/hw/pci/pcie_aer.c + @@ -112,6 +112,10 @@ int pcie_aer_init(PCIDevice *dev, + + pci_set_long(dev->w1cmask + offset + PCI_ERR_UNCOR_STATUS, + PCI_ERR_UNC_SUPPORTED); + + pci_set_long(dev->config + offset + PCI_ERR_UNCOR_MASK, + + PCI_ERR_UNC_MASK_DEFAULT); + + pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_MASK, + + PCI_ERR_UNC_SUPPORTED); + + pci_set_long(dev->config + offset + PCI_ERR_UNCOR_SEVER, + PCI_ERR_UNC_SEVERITY_DEFAULT); + +The patch changes how we configure PCI space for AER. But QEMU fails +when the PCI space configuration is different between source and +destination. + +The following commit shows how this got fixed:: + + commit 5ed3dabe57dd9f4c007404345e5f5bf0e347317f + Author: Leonardo Bras + Date: Tue May 2 21:27:02 2023 -0300 + + hw/pci: Disable PCI_ERR_UNCOR_MASK register for machine type < 8.0 + + [...] + +The relevant parts of the fix in QEMU are as follow: + +First, we create a new property for the device to be able to configure +the old behaviour or the new behaviour:: + + diff --git a/hw/pci/pci.c b/hw/pci/pci.c + index 8a87ccc8b0..5153ad63d6 100644 + --- a/hw/pci/pci.c + +++ b/hw/pci/pci.c + @@ -79,6 +79,8 @@ static Property pci_props[] = { + DEFINE_PROP_STRING("failover_pair_id", PCIDevice, + failover_pair_id), + DEFINE_PROP_UINT32("acpi-index", PCIDevice, acpi_index, 0), + + DEFINE_PROP_BIT("x-pcie-err-unc-mask", PCIDevice, cap_present, + + QEMU_PCIE_ERR_UNC_MASK_BITNR, true), + DEFINE_PROP_END_OF_LIST() + }; + +Notice that we enable the feature for new machine types. + +Now we see how the fix is done. This is going to depend on what kind +of breakage happens, but in this case it is quite simple:: + + diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c + index 103667c368..374d593ead 100644 + --- a/hw/pci/pcie_aer.c + +++ b/hw/pci/pcie_aer.c + @@ -112,10 +112,13 @@ int pcie_aer_init(PCIDevice *dev, uint8_t cap_ver, + uint16_t offset, + + pci_set_long(dev->w1cmask + offset + PCI_ERR_UNCOR_STATUS, + PCI_ERR_UNC_SUPPORTED); + - pci_set_long(dev->config + offset + PCI_ERR_UNCOR_MASK, + - PCI_ERR_UNC_MASK_DEFAULT); + - pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_MASK, + - PCI_ERR_UNC_SUPPORTED); + + + + if (dev->cap_present & QEMU_PCIE_ERR_UNC_MASK) { + + pci_set_long(dev->config + offset + PCI_ERR_UNCOR_MASK, + + PCI_ERR_UNC_MASK_DEFAULT); + + pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_MASK, + + PCI_ERR_UNC_SUPPORTED); + + } + + pci_set_long(dev->config + offset + PCI_ERR_UNCOR_SEVER, + PCI_ERR_UNC_SEVERITY_DEFAULT); + +I.e. If the property bit is enabled, we configure it as we did for +qemu-8.0. If the property bit is not set, we configure it as it was in 7.2. + +And now, everything that is missing is disabling the feature for old +machine types:: + + diff --git a/hw/core/machine.c b/hw/core/machine.c + index 47a34841a5..07f763eb2e 100644 + --- a/hw/core/machine.c + +++ b/hw/core/machine.c + @@ -48,6 +48,7 @@ GlobalProperty hw_compat_7_2[] = { + { "e1000e", "migrate-timadj", "off" }, + { "virtio-mem", "x-early-migration", "false" }, + { "migration", "x-preempt-pre-7-2", "true" }, + + { TYPE_PCI_DEVICE, "x-pcie-err-unc-mask", "off" }, + }; + const size_t hw_compat_7_2_len = G_N_ELEMENTS(hw_compat_7_2); + +And now, when qemu-8.0.1 is released with this fix, all combinations +are going to work as supposed. + +- $ qemu-7.2 -M pc-7.2 -> qemu-7.2 -M pc-7.2 (works) +- $ qemu-8.0.1 -M pc-7.2 -> qemu-8.0.1 -M pc-7.2 (works) +- $ qemu-8.0.1 -M pc-7.2 -> qemu-7.2 -M pc-7.2 (works) +- $ qemu-7.2 -M pc-7.2 -> qemu-8.0.1 -M pc-7.2 (works) + +So the normality has been restored and everything is ok, no? + +Not really, now our matrix is much bigger. We started with the easy +cases, migration from the same version to the same version always +works: + +- $ qemu-7.2 -M pc-7.2 -> qemu-7.2 -M pc-7.2 +- $ qemu-8.0 -M pc-7.2 -> qemu-8.0 -M pc-7.2 +- $ qemu-8.0.1 -M pc-7.2 -> qemu-8.0.1 -M pc-7.2 + +Now the interesting ones. When the QEMU processes versions are +different. For the 1st set, their fail and we can do nothing, both +versions are released and we can't change anything. + +- $ qemu-7.2 -M pc-7.2 -> qemu-8.0 -M pc-7.2 +- $ qemu-8.0 -M pc-7.2 -> qemu-7.2 -M pc-7.2 + +This two are the ones that work. The whole point of making the +change in qemu-8.0.1 release was to fix this issue: + +- $ qemu-7.2 -M pc-7.2 -> qemu-8.0.1 -M pc-7.2 +- $ qemu-8.0.1 -M pc-7.2 -> qemu-7.2 -M pc-7.2 + +But now we found that qemu-8.0 neither can migrate to qemu-7.2 not +qemu-8.0.1. + +- $ qemu-8.0 -M pc-7.2 -> qemu-8.0.1 -M pc-7.2 +- $ qemu-8.0.1 -M pc-7.2 -> qemu-8.0 -M pc-7.2 + +So, if we start a pc-7.2 machine in qemu-8.0 we can't migrate it to +anything except to qemu-8.0. + +Can we do better? + +Yeap. If we know that we are going to do this migration: + +- $ qemu-8.0 -M pc-7.2 -> qemu-8.0.1 -M pc-7.2 + +We can launch the appropriate devices with:: + + --device...,x-pci-e-err-unc-mask=on + +And now we can receive a migration from 8.0. And from now on, we can +do that migration to new machine types if we remember to enable that +property for pc-7.2. Notice that we need to remember, it is not +enough to know that the source of the migration is qemu-8.0. Think of +this example: + +$ qemu-8.0 -M pc-7.2 -> qemu-8.0.1 -M pc-7.2 -> qemu-8.2 -M pc-7.2 + +In the second migration, the source is not qemu-8.0, but we still have +that "problem" and have that property enabled. Notice that we need to +continue having this mark/property until we have this machine +rebooted. But it is not a normal reboot (that don't reload QEMU) we +need the machine to poweroff/poweron on a fixed QEMU. And from now +on we can use the proper real machine. diff --git a/docs/devel/migration/dirty-limit.rst b/docs/devel/migration/dirty-limit.rst new file mode 100644 index 0000000000..8f32329d5f --- /dev/null +++ b/docs/devel/migration/dirty-limit.rst @@ -0,0 +1,71 @@ +Dirty limit +=========== + +The dirty limit, short for dirty page rate upper limit, is a new capability +introduced in the 8.1 QEMU release that uses a new algorithm based on the KVM +dirty ring to throttle down the guest during live migration. + +The algorithm framework is as follows: + +:: + + ------------------------------------------------------------------------------ + main --------------> throttle thread ------------> PREPARE(1) <-------- + thread \ | | + \ | | + \ V | + -\ CALCULATE(2) | + \ | | + \ | | + \ V | + \ SET PENALTY(3) ----- + -\ | + \ | + \ V + -> virtual CPU thread -------> ACCEPT PENALTY(4) + ------------------------------------------------------------------------------ + +When the qmp command qmp_set_vcpu_dirty_limit is called for the first time, +the QEMU main thread starts the throttle thread. The throttle thread, once +launched, executes the loop, which consists of three steps: + + - PREPARE (1) + + The entire work of PREPARE (1) is preparation for the second stage, + CALCULATE(2), as the name implies. It involves preparing the dirty + page rate value and the corresponding upper limit of the VM: + The dirty page rate is calculated via the KVM dirty ring mechanism, + which tells QEMU how many dirty pages a virtual CPU has had since the + last KVM_EXIT_DIRTY_RING_FULL exception; The dirty page rate upper + limit is specified by caller, therefore fetch it directly. + + - CALCULATE (2) + + Calculate a suitable sleep period for each virtual CPU, which will be + used to determine the penalty for the target virtual CPU. The + computation must be done carefully in order to reduce the dirty page + rate progressively down to the upper limit without oscillation. To + achieve this, two strategies are provided: the first is to add or + subtract sleep time based on the ratio of the current dirty page rate + to the limit, which is used when the current dirty page rate is far + from the limit; the second is to add or subtract a fixed time when + the current dirty page rate is close to the limit. + + - SET PENALTY (3) + + Set the sleep time for each virtual CPU that should be penalized based + on the results of the calculation supplied by step CALCULATE (2). + +After completing the three above stages, the throttle thread loops back +to step PREPARE (1) until the dirty limit is reached. + +On the other hand, each virtual CPU thread reads the sleep duration and +sleeps in the path of the KVM_EXIT_DIRTY_RING_FULL exception handler, that +is ACCEPT PENALTY (4). Virtual CPUs tied with writing processes will +obviously exit to the path and get penalized, whereas virtual CPUs involved +with read processes will not. + +In summary, thanks to the KVM dirty ring technology, the dirty limit +algorithm will restrict virtual CPUs as needed to keep their dirty page +rate inside the limit. This leads to more steady reading performance during +live migration and can aid in improving large guest responsiveness. diff --git a/docs/devel/migration/features.rst b/docs/devel/migration/features.rst new file mode 100644 index 0000000000..d5ca7b86d5 --- /dev/null +++ b/docs/devel/migration/features.rst @@ -0,0 +1,14 @@ +Migration features +================== + +Migration has plenty of features to support different use cases. + +.. toctree:: + :maxdepth: 2 + + postcopy + dirty-limit + vfio + virtio + mapped-ram + CPR diff --git a/docs/devel/migration/index.rst b/docs/devel/migration/index.rst new file mode 100644 index 0000000000..2aa294d631 --- /dev/null +++ b/docs/devel/migration/index.rst @@ -0,0 +1,13 @@ +Migration +========= + +This is the main entry for QEMU migration documentations. It explains how +QEMU live migration works. + +.. toctree:: + :maxdepth: 2 + + main + features + compatibility + best-practices diff --git a/docs/devel/migration.rst b/docs/devel/migration/main.rst similarity index 60% rename from docs/devel/migration.rst rename to docs/devel/migration/main.rst index 3e9656d8e0..54385a23e5 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration/main.rst @@ -1,6 +1,6 @@ -========= -Migration -========= +=================== +Migration framework +=================== QEMU has code to load/save the state of the guest that it is running. These are two complementary operations. Saving the state just does @@ -28,6 +28,8 @@ the guest to be stopped. Typically the time that the guest is unresponsive during live migration is the low hundred of milliseconds (notice that this depends on a lot of things). +.. contents:: + Transports ========== @@ -39,6 +41,11 @@ over any transport. - exec migration: do the migration using the stdin/stdout through a process. - fd migration: do the migration using a file descriptor that is passed to QEMU. QEMU doesn't care how this file descriptor is opened. +- file migration: do the migration using a file that is passed to QEMU + by path. A file offset option is supported to allow a management + application to add its own metadata to the start of the file without + QEMU interference. Note that QEMU does not flush cached file + data/metadata at the end of migration. In addition, support is included for migration using RDMA, which transports the page data using ``RDMA``, where the hardware takes care of @@ -50,27 +57,6 @@ All these migration protocols use the same infrastructure to save/restore state devices. This infrastructure is shared with the savevm/loadvm functionality. -Debugging -========= - -The migration stream can be analyzed thanks to ``scripts/analyze-migration.py``. - -Example usage: - -.. code-block:: shell - - $ qemu-system-x86_64 -display none -monitor stdio - (qemu) migrate "exec:cat > mig" - (qemu) q - $ ./scripts/analyze-migration.py -f mig - { - "ram (3)": { - "section sizes": { - "pc.ram": "0x0000000008000000", - ... - -See also ``analyze-migration.py -h`` help for more options. - Common infrastructure ===================== @@ -156,7 +142,7 @@ An example (from hw/input/pckbd.c) .name = "pckbd", .version_id = 3, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(write_cmd, KBDState), VMSTATE_UINT8(status, KBDState), VMSTATE_UINT8(mode, KBDState), @@ -165,13 +151,17 @@ An example (from hw/input/pckbd.c) } }; -We are declaring the state with name "pckbd". -The ``version_id`` is 3, and the fields are 4 uint8_t in a KBDState structure. -We registered this with: +We are declaring the state with name "pckbd". The ``version_id`` is +3, and there are 4 uint8_t fields in the KBDState structure. We +registered this ``VMSTATEDescription`` with one of the following +functions. The first one will generate a device ``instance_id`` +different for each registration. Use the second one if you already +have an id that is different for each instance of the device: .. code:: c - vmstate_register(NULL, 0, &vmstate_kbd, s); + vmstate_register_any(NULL, &vmstate_kbd, s); + vmstate_register(NULL, instance_id, &vmstate_kbd, s); For devices that are ``qdev`` based, we can register the device in the class init function: @@ -288,7 +278,7 @@ Example: .pre_save = ide_drive_pio_pre_save, .post_load = ide_drive_pio_post_load, .needed = ide_drive_pio_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(req_nb_sectors, IDEState), VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1, vmstate_info_uint8, uint8_t), @@ -306,11 +296,11 @@ Example: .version_id = 3, .minimum_version_id = 0, .post_load = ide_drive_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { .... several fields .... VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_ide_drive_pio_state, NULL } @@ -446,10 +436,10 @@ data doesn't match the stored device data well; it allows an intermediate temporary structure to be populated with migration data and then transferred to the main structure. -If you use memory API functions that update memory layout outside +If you use memory or portio_list API functions that update memory layout outside initialization (i.e., in response to a guest action), this is a strong indication that you need to call these functions in a ``post_load`` callback. -Examples of such memory API functions are: +Examples of such API functions are: - memory_region_add_subregion() - memory_region_del_subregion() @@ -458,6 +448,8 @@ Examples of such memory API functions are: - memory_region_set_enabled() - memory_region_set_address() - memory_region_set_alias_offset() + - portio_list_set_address() + - portio_list_set_enabled() Iterative device migration -------------------------- @@ -482,15 +474,17 @@ An iterative device must provide: - A ``load_setup`` function that initialises the data structures on the destination. - - A ``save_live_pending`` function that is called repeatedly and must - indicate how much more data the iterative data must save. The core - migration code will use this to determine when to pause the CPUs - and complete the migration. + - A ``state_pending_exact`` function that indicates how much more + data we must save. The core migration code will use this to + determine when to pause the CPUs and complete the migration. - - A ``save_live_iterate`` function (called after ``save_live_pending`` - when there is significant data still to be sent). It should send - a chunk of data until the point that stream bandwidth limits tell it - to stop. Each call generates one section. + - A ``state_pending_estimate`` function that indicates how much more + data we must save. When the estimated amount is smaller than the + threshold, we call ``state_pending_exact``. + + - A ``save_live_iterate`` function should send a chunk of data until + the point that stream bandwidth limits tell it to stop. Each call + generates one section. - A ``save_live_complete_precopy`` function that must transmit the last section for the device containing any remaining data. @@ -586,292 +580,3 @@ path. Return path - opened by main thread, written by main thread AND postcopy thread (protected by rp_mutex) -Postcopy -======== - -'Postcopy' migration is a way to deal with migrations that refuse to converge -(or take too long to converge) its plus side is that there is an upper bound on -the amount of migration traffic and time it takes, the down side is that during -the postcopy phase, a failure of *either* side or the network connection causes -the guest to be lost. - -In postcopy the destination CPUs are started before all the memory has been -transferred, and accesses to pages that are yet to be transferred cause -a fault that's translated by QEMU into a request to the source QEMU. - -Postcopy can be combined with precopy (i.e. normal migration) so that if precopy -doesn't finish in a given time the switch is made to postcopy. - -Enabling postcopy ------------------ - -To enable postcopy, issue this command on the monitor (both source and -destination) prior to the start of migration: - -``migrate_set_capability postcopy-ram on`` - -The normal commands are then used to start a migration, which is still -started in precopy mode. Issuing: - -``migrate_start_postcopy`` - -will now cause the transition from precopy to postcopy. -It can be issued immediately after migration is started or any -time later on. Issuing it after the end of a migration is harmless. - -Blocktime is a postcopy live migration metric, intended to show how -long the vCPU was in state of interruptible sleep due to pagefault. -That metric is calculated both for all vCPUs as overlapped value, and -separately for each vCPU. These values are calculated on destination -side. To enable postcopy blocktime calculation, enter following -command on destination monitor: - -``migrate_set_capability postcopy-blocktime on`` - -Postcopy blocktime can be retrieved by query-migrate qmp command. -postcopy-blocktime value of qmp command will show overlapped blocking -time for all vCPU, postcopy-vcpu-blocktime will show list of blocking -time per vCPU. - -.. note:: - During the postcopy phase, the bandwidth limits set using - ``migrate_set_parameter`` is ignored (to avoid delaying requested pages that - the destination is waiting for). - -Postcopy device transfer ------------------------- - -Loading of device data may cause the device emulation to access guest RAM -that may trigger faults that have to be resolved by the source, as such -the migration stream has to be able to respond with page data *during* the -device load, and hence the device data has to be read from the stream completely -before the device load begins to free the stream up. This is achieved by -'packaging' the device data into a blob that's read in one go. - -Source behaviour ----------------- - -Until postcopy is entered the migration stream is identical to normal -precopy, except for the addition of a 'postcopy advise' command at -the beginning, to tell the destination that postcopy might happen. -When postcopy starts the source sends the page discard data and then -forms the 'package' containing: - - - Command: 'postcopy listen' - - The device state - - A series of sections, identical to the precopy streams device state stream - containing everything except postcopiable devices (i.e. RAM) - - Command: 'postcopy run' - -The 'package' is sent as the data part of a Command: ``CMD_PACKAGED``, and the -contents are formatted in the same way as the main migration stream. - -During postcopy the source scans the list of dirty pages and sends them -to the destination without being requested (in much the same way as precopy), -however when a page request is received from the destination, the dirty page -scanning restarts from the requested location. This causes requested pages -to be sent quickly, and also causes pages directly after the requested page -to be sent quickly in the hope that those pages are likely to be used -by the destination soon. - -Destination behaviour ---------------------- - -Initially the destination looks the same as precopy, with a single thread -reading the migration stream; the 'postcopy advise' and 'discard' commands -are processed to change the way RAM is managed, but don't affect the stream -processing. - -:: - - ------------------------------------------------------------------------------ - 1 2 3 4 5 6 7 - main -----DISCARD-CMD_PACKAGED ( LISTEN DEVICE DEVICE DEVICE RUN ) - thread | | - | (page request) - | \___ - v \ - listen thread: --- page -- page -- page -- page -- page -- - - a b c - ------------------------------------------------------------------------------ - -- On receipt of ``CMD_PACKAGED`` (1) - - All the data associated with the package - the ( ... ) section in the diagram - - is read into memory, and the main thread recurses into qemu_loadvm_state_main - to process the contents of the package (2) which contains commands (3,6) and - devices (4...) - -- On receipt of 'postcopy listen' - 3 -(i.e. the 1st command in the package) - - a new thread (a) is started that takes over servicing the migration stream, - while the main thread carries on loading the package. It loads normal - background page data (b) but if during a device load a fault happens (5) - the returned page (c) is loaded by the listen thread allowing the main - threads device load to carry on. - -- The last thing in the ``CMD_PACKAGED`` is a 'RUN' command (6) - - letting the destination CPUs start running. At the end of the - ``CMD_PACKAGED`` (7) the main thread returns to normal running behaviour and - is no longer used by migration, while the listen thread carries on servicing - page data until the end of migration. - -Postcopy states ---------------- - -Postcopy moves through a series of states (see postcopy_state) from -ADVISE->DISCARD->LISTEN->RUNNING->END - - - Advise - - Set at the start of migration if postcopy is enabled, even - if it hasn't had the start command; here the destination - checks that its OS has the support needed for postcopy, and performs - setup to ensure the RAM mappings are suitable for later postcopy. - The destination will fail early in migration at this point if the - required OS support is not present. - (Triggered by reception of POSTCOPY_ADVISE command) - - - Discard - - Entered on receipt of the first 'discard' command; prior to - the first Discard being performed, hugepages are switched off - (using madvise) to ensure that no new huge pages are created - during the postcopy phase, and to cause any huge pages that - have discards on them to be broken. - - - Listen - - The first command in the package, POSTCOPY_LISTEN, switches - the destination state to Listen, and starts a new thread - (the 'listen thread') which takes over the job of receiving - pages off the migration stream, while the main thread carries - on processing the blob. With this thread able to process page - reception, the destination now 'sensitises' the RAM to detect - any access to missing pages (on Linux using the 'userfault' - system). - - - Running - - POSTCOPY_RUN causes the destination to synchronise all - state and start the CPUs and IO devices running. The main - thread now finishes processing the migration package and - now carries on as it would for normal precopy migration - (although it can't do the cleanup it would do as it - finishes a normal migration). - - - End - - The listen thread can now quit, and perform the cleanup of migration - state, the migration is now complete. - -Source side page maps ---------------------- - -The source side keeps two bitmaps during postcopy; 'the migration bitmap' -and 'unsent map'. The 'migration bitmap' is basically the same as in -the precopy case, and holds a bit to indicate that page is 'dirty' - -i.e. needs sending. During the precopy phase this is updated as the CPU -dirties pages, however during postcopy the CPUs are stopped and nothing -should dirty anything any more. - -The 'unsent map' is used for the transition to postcopy. It is a bitmap that -has a bit cleared whenever a page is sent to the destination, however during -the transition to postcopy mode it is combined with the migration bitmap -to form a set of pages that: - - a) Have been sent but then redirtied (which must be discarded) - b) Have not yet been sent - which also must be discarded to cause any - transparent huge pages built during precopy to be broken. - -Note that the contents of the unsentmap are sacrificed during the calculation -of the discard set and thus aren't valid once in postcopy. The dirtymap -is still valid and is used to ensure that no page is sent more than once. Any -request for a page that has already been sent is ignored. Duplicate requests -such as this can happen as a page is sent at about the same time the -destination accesses it. - -Postcopy with hugepages ------------------------ - -Postcopy now works with hugetlbfs backed memory: - - a) The linux kernel on the destination must support userfault on hugepages. - b) The huge-page configuration on the source and destination VMs must be - identical; i.e. RAMBlocks on both sides must use the same page size. - c) Note that ``-mem-path /dev/hugepages`` will fall back to allocating normal - RAM if it doesn't have enough hugepages, triggering (b) to fail. - Using ``-mem-prealloc`` enforces the allocation using hugepages. - d) Care should be taken with the size of hugepage used; postcopy with 2MB - hugepages works well, however 1GB hugepages are likely to be problematic - since it takes ~1 second to transfer a 1GB hugepage across a 10Gbps link, - and until the full page is transferred the destination thread is blocked. - -Postcopy with shared memory ---------------------------- - -Postcopy migration with shared memory needs explicit support from the other -processes that share memory and from QEMU. There are restrictions on the type of -memory that userfault can support shared. - -The Linux kernel userfault support works on ``/dev/shm`` memory and on ``hugetlbfs`` -(although the kernel doesn't provide an equivalent to ``madvise(MADV_DONTNEED)`` -for hugetlbfs which may be a problem in some configurations). - -The vhost-user code in QEMU supports clients that have Postcopy support, -and the ``vhost-user-bridge`` (in ``tests/``) and the DPDK package have changes -to support postcopy. - -The client needs to open a userfaultfd and register the areas -of memory that it maps with userfault. The client must then pass the -userfaultfd back to QEMU together with a mapping table that allows -fault addresses in the clients address space to be converted back to -RAMBlock/offsets. The client's userfaultfd is added to the postcopy -fault-thread and page requests are made on behalf of the client by QEMU. -QEMU performs 'wake' operations on the client's userfaultfd to allow it -to continue after a page has arrived. - -.. note:: - There are two future improvements that would be nice: - a) Some way to make QEMU ignorant of the addresses in the clients - address space - b) Avoiding the need for QEMU to perform ufd-wake calls after the - pages have arrived - -Retro-fitting postcopy to existing clients is possible: - a) A mechanism is needed for the registration with userfault as above, - and the registration needs to be coordinated with the phases of - postcopy. In vhost-user extra messages are added to the existing - control channel. - b) Any thread that can block due to guest memory accesses must be - identified and the implication understood; for example if the - guest memory access is made while holding a lock then all other - threads waiting for that lock will also be blocked. - -Firmware -======== - -Migration migrates the copies of RAM and ROM, and thus when running -on the destination it includes the firmware from the source. Even after -resetting a VM, the old firmware is used. Only once QEMU has been restarted -is the new firmware in use. - -- Changes in firmware size can cause changes in the required RAMBlock size - to hold the firmware and thus migration can fail. In practice it's best - to pad firmware images to convenient powers of 2 with plenty of space - for growth. - -- Care should be taken with device emulation code so that newer - emulation code can work with older firmware to allow forward migration. - -- Care should be taken with newer firmware so that backward migration - to older systems with older device emulation code will work. - -In some cases it may be best to tie specific firmware versions to specific -versioned machine types to cut down on the combinations that will need -support. This is also useful when newer versions of firmware outgrow -the padding. - diff --git a/docs/devel/migration/mapped-ram.rst b/docs/devel/migration/mapped-ram.rst new file mode 100644 index 0000000000..fa4cefd9fc --- /dev/null +++ b/docs/devel/migration/mapped-ram.rst @@ -0,0 +1,138 @@ +Mapped-ram +========== + +Mapped-ram is a new stream format for the RAM section designed to +supplement the existing ``file:`` migration and make it compatible +with ``multifd``. This enables parallel migration of a guest's RAM to +a file. + +The core of the feature is to ensure that RAM pages are mapped +directly to offsets in the resulting migration file. This enables the +``multifd`` threads to write exclusively to those offsets even if the +guest is constantly dirtying pages (i.e. live migration). Another +benefit is that the resulting file will have a bounded size, since +pages which are dirtied multiple times will always go to a fixed +location in the file, rather than constantly being added to a +sequential stream. Having the pages at fixed offsets also allows the +usage of O_DIRECT for save/restore of the migration stream as the +pages are ensured to be written respecting O_DIRECT alignment +restrictions (direct-io support not yet implemented). + +Usage +----- + +On both source and destination, enable the ``multifd`` and +``mapped-ram`` capabilities: + + ``migrate_set_capability multifd on`` + + ``migrate_set_capability mapped-ram on`` + +Use a ``file:`` URL for migration: + + ``migrate file:/path/to/migration/file`` + +Mapped-ram migration is best done non-live, i.e. by stopping the VM on +the source side before migrating. + +Use-cases +--------- + +The mapped-ram feature was designed for use cases where the migration +stream will be directed to a file in the filesystem and not +immediately restored on the destination VM [#]_. These could be +thought of as snapshots. We can further categorize them into live and +non-live. + +- Non-live snapshot + +If the use case requires a VM to be stopped before taking a snapshot, +that's the ideal scenario for mapped-ram migration. Not having to +track dirty pages, the migration will write the RAM pages to the disk +as fast as it can. + +Note: if a snapshot is taken of a running VM, but the VM will be +stopped after the snapshot by the admin, then consider stopping it +right before the snapshot to take benefit of the performance gains +mentioned above. + +- Live snapshot + +If the use case requires that the VM keeps running during and after +the snapshot operation, then mapped-ram migration can still be used, +but will be less performant. Other strategies such as +background-snapshot should be evaluated as well. One benefit of +mapped-ram in this scenario is portability since background-snapshot +depends on async dirty tracking (KVM_GET_DIRTY_LOG) which is not +supported outside of Linux. + +.. [#] While this same effect could be obtained with the usage of + snapshots or the ``file:`` migration alone, mapped-ram provides + a performance increase for VMs with larger RAM sizes (10s to + 100s of GiBs), specially if the VM has been stopped beforehand. + +RAM section format +------------------ + +Instead of having a sequential stream of pages that follow the +RAMBlock headers, the dirty pages for a RAMBlock follow its header +instead. This ensures that each RAM page has a fixed offset in the +resulting migration file. + +A bitmap is introduced to track which pages have been written in the +migration file. Pages are written at a fixed location for every +ramblock. Zero pages are ignored as they'd be zero in the destination +migration as well. + +:: + + Without mapped-ram: With mapped-ram: + + --------------------- -------------------------------- + | ramblock 1 header | | ramblock 1 header | + --------------------- -------------------------------- + | ramblock 2 header | | ramblock 1 mapped-ram header | + --------------------- -------------------------------- + | ... | | padding to next 1MB boundary | + --------------------- | ... | + | ramblock n header | -------------------------------- + --------------------- | ramblock 1 pages | + | RAM_SAVE_FLAG_EOS | | ... | + --------------------- -------------------------------- + | stream of pages | | ramblock 2 header | + | (iter 1) | -------------------------------- + | ... | | ramblock 2 mapped-ram header | + --------------------- -------------------------------- + | RAM_SAVE_FLAG_EOS | | padding to next 1MB boundary | + --------------------- | ... | + | stream of pages | -------------------------------- + | (iter 2) | | ramblock 2 pages | + | ... | | ... | + --------------------- -------------------------------- + | ... | | ... | + --------------------- -------------------------------- + | RAM_SAVE_FLAG_EOS | + -------------------------------- + | ... | + -------------------------------- + +where: + - ramblock header: the generic information for a ramblock, such as + idstr, used_len, etc. + + - ramblock mapped-ram header: the information added by this feature: + bitmap of pages written, bitmap size and offset of pages in the + migration file. + +Restrictions +------------ + +Since pages are written to their relative offsets and out of order +(due to the memory dirtying patterns), streaming channels such as +sockets are not supported. A seekable channel such as a file is +required. This can be verified in the QIOChannel by the presence of +the QIO_CHANNEL_FEATURE_SEEKABLE. + +The improvements brought by this feature apply only to guest physical +RAM. Other types of memory such as VRAM are migrated as part of device +states. diff --git a/docs/devel/migration/postcopy.rst b/docs/devel/migration/postcopy.rst new file mode 100644 index 0000000000..6c51e96d79 --- /dev/null +++ b/docs/devel/migration/postcopy.rst @@ -0,0 +1,313 @@ +======== +Postcopy +======== + +.. contents:: + +'Postcopy' migration is a way to deal with migrations that refuse to converge +(or take too long to converge) its plus side is that there is an upper bound on +the amount of migration traffic and time it takes, the down side is that during +the postcopy phase, a failure of *either* side causes the guest to be lost. + +In postcopy the destination CPUs are started before all the memory has been +transferred, and accesses to pages that are yet to be transferred cause +a fault that's translated by QEMU into a request to the source QEMU. + +Postcopy can be combined with precopy (i.e. normal migration) so that if precopy +doesn't finish in a given time the switch is made to postcopy. + +Enabling postcopy +================= + +To enable postcopy, issue this command on the monitor (both source and +destination) prior to the start of migration: + +``migrate_set_capability postcopy-ram on`` + +The normal commands are then used to start a migration, which is still +started in precopy mode. Issuing: + +``migrate_start_postcopy`` + +will now cause the transition from precopy to postcopy. +It can be issued immediately after migration is started or any +time later on. Issuing it after the end of a migration is harmless. + +Blocktime is a postcopy live migration metric, intended to show how +long the vCPU was in state of interruptible sleep due to pagefault. +That metric is calculated both for all vCPUs as overlapped value, and +separately for each vCPU. These values are calculated on destination +side. To enable postcopy blocktime calculation, enter following +command on destination monitor: + +``migrate_set_capability postcopy-blocktime on`` + +Postcopy blocktime can be retrieved by query-migrate qmp command. +postcopy-blocktime value of qmp command will show overlapped blocking +time for all vCPU, postcopy-vcpu-blocktime will show list of blocking +time per vCPU. + +.. note:: + During the postcopy phase, the bandwidth limits set using + ``migrate_set_parameter`` is ignored (to avoid delaying requested pages that + the destination is waiting for). + +Postcopy internals +================== + +State machine +------------- + +Postcopy moves through a series of states (see postcopy_state) from +ADVISE->DISCARD->LISTEN->RUNNING->END + + - Advise + + Set at the start of migration if postcopy is enabled, even + if it hasn't had the start command; here the destination + checks that its OS has the support needed for postcopy, and performs + setup to ensure the RAM mappings are suitable for later postcopy. + The destination will fail early in migration at this point if the + required OS support is not present. + (Triggered by reception of POSTCOPY_ADVISE command) + + - Discard + + Entered on receipt of the first 'discard' command; prior to + the first Discard being performed, hugepages are switched off + (using madvise) to ensure that no new huge pages are created + during the postcopy phase, and to cause any huge pages that + have discards on them to be broken. + + - Listen + + The first command in the package, POSTCOPY_LISTEN, switches + the destination state to Listen, and starts a new thread + (the 'listen thread') which takes over the job of receiving + pages off the migration stream, while the main thread carries + on processing the blob. With this thread able to process page + reception, the destination now 'sensitises' the RAM to detect + any access to missing pages (on Linux using the 'userfault' + system). + + - Running + + POSTCOPY_RUN causes the destination to synchronise all + state and start the CPUs and IO devices running. The main + thread now finishes processing the migration package and + now carries on as it would for normal precopy migration + (although it can't do the cleanup it would do as it + finishes a normal migration). + + - Paused + + Postcopy can run into a paused state (normally on both sides when + happens), where all threads will be temporarily halted mostly due to + network errors. When reaching paused state, migration will make sure + the qemu binary on both sides maintain the data without corrupting + the VM. To continue the migration, the admin needs to fix the + migration channel using the QMP command 'migrate-recover' on the + destination node, then resume the migration using QMP command 'migrate' + again on source node, with resume=true flag set. + + - End + + The listen thread can now quit, and perform the cleanup of migration + state, the migration is now complete. + +Device transfer +--------------- + +Loading of device data may cause the device emulation to access guest RAM +that may trigger faults that have to be resolved by the source, as such +the migration stream has to be able to respond with page data *during* the +device load, and hence the device data has to be read from the stream completely +before the device load begins to free the stream up. This is achieved by +'packaging' the device data into a blob that's read in one go. + +Source behaviour +---------------- + +Until postcopy is entered the migration stream is identical to normal +precopy, except for the addition of a 'postcopy advise' command at +the beginning, to tell the destination that postcopy might happen. +When postcopy starts the source sends the page discard data and then +forms the 'package' containing: + + - Command: 'postcopy listen' + - The device state + + A series of sections, identical to the precopy streams device state stream + containing everything except postcopiable devices (i.e. RAM) + - Command: 'postcopy run' + +The 'package' is sent as the data part of a Command: ``CMD_PACKAGED``, and the +contents are formatted in the same way as the main migration stream. + +During postcopy the source scans the list of dirty pages and sends them +to the destination without being requested (in much the same way as precopy), +however when a page request is received from the destination, the dirty page +scanning restarts from the requested location. This causes requested pages +to be sent quickly, and also causes pages directly after the requested page +to be sent quickly in the hope that those pages are likely to be used +by the destination soon. + +Destination behaviour +--------------------- + +Initially the destination looks the same as precopy, with a single thread +reading the migration stream; the 'postcopy advise' and 'discard' commands +are processed to change the way RAM is managed, but don't affect the stream +processing. + +:: + + ------------------------------------------------------------------------------ + 1 2 3 4 5 6 7 + main -----DISCARD-CMD_PACKAGED ( LISTEN DEVICE DEVICE DEVICE RUN ) + thread | | + | (page request) + | \___ + v \ + listen thread: --- page -- page -- page -- page -- page -- + + a b c + ------------------------------------------------------------------------------ + +- On receipt of ``CMD_PACKAGED`` (1) + + All the data associated with the package - the ( ... ) section in the diagram - + is read into memory, and the main thread recurses into qemu_loadvm_state_main + to process the contents of the package (2) which contains commands (3,6) and + devices (4...) + +- On receipt of 'postcopy listen' - 3 -(i.e. the 1st command in the package) + + a new thread (a) is started that takes over servicing the migration stream, + while the main thread carries on loading the package. It loads normal + background page data (b) but if during a device load a fault happens (5) + the returned page (c) is loaded by the listen thread allowing the main + threads device load to carry on. + +- The last thing in the ``CMD_PACKAGED`` is a 'RUN' command (6) + + letting the destination CPUs start running. At the end of the + ``CMD_PACKAGED`` (7) the main thread returns to normal running behaviour and + is no longer used by migration, while the listen thread carries on servicing + page data until the end of migration. + +Source side page bitmap +----------------------- + +The 'migration bitmap' in postcopy is basically the same as in the precopy, +where each of the bit to indicate that page is 'dirty' - i.e. needs +sending. During the precopy phase this is updated as the CPU dirties +pages, however during postcopy the CPUs are stopped and nothing should +dirty anything any more. Instead, dirty bits are cleared when the relevant +pages are sent during postcopy. + +Postcopy features +================= + +Postcopy recovery +----------------- + +Comparing to precopy, postcopy is special on error handlings. When any +error happens (in this case, mostly network errors), QEMU cannot easily +fail a migration because VM data resides in both source and destination +QEMU instances. On the other hand, when issue happens QEMU on both sides +will go into a paused state. It'll need a recovery phase to continue a +paused postcopy migration. + +The recovery phase normally contains a few steps: + + - When network issue occurs, both QEMU will go into PAUSED state + + - When the network is recovered (or a new network is provided), the admin + can setup the new channel for migration using QMP command + 'migrate-recover' on destination node, preparing for a resume. + + - On source host, the admin can continue the interrupted postcopy + migration using QMP command 'migrate' with resume=true flag set. + + - After the connection is re-established, QEMU will continue the postcopy + migration on both sides. + +During a paused postcopy migration, the VM can logically still continue +running, and it will not be impacted from any page access to pages that +were already migrated to destination VM before the interruption happens. +However, if any of the missing pages got accessed on destination VM, the VM +thread will be halted waiting for the page to be migrated, it means it can +be halted until the recovery is complete. + +The impact of accessing missing pages can be relevant to different +configurations of the guest. For example, when with async page fault +enabled, logically the guest can proactively schedule out the threads +accessing missing pages. + +Postcopy with hugepages +----------------------- + +Postcopy now works with hugetlbfs backed memory: + + a) The linux kernel on the destination must support userfault on hugepages. + b) The huge-page configuration on the source and destination VMs must be + identical; i.e. RAMBlocks on both sides must use the same page size. + c) Note that ``-mem-path /dev/hugepages`` will fall back to allocating normal + RAM if it doesn't have enough hugepages, triggering (b) to fail. + Using ``-mem-prealloc`` enforces the allocation using hugepages. + d) Care should be taken with the size of hugepage used; postcopy with 2MB + hugepages works well, however 1GB hugepages are likely to be problematic + since it takes ~1 second to transfer a 1GB hugepage across a 10Gbps link, + and until the full page is transferred the destination thread is blocked. + +Postcopy with shared memory +--------------------------- + +Postcopy migration with shared memory needs explicit support from the other +processes that share memory and from QEMU. There are restrictions on the type of +memory that userfault can support shared. + +The Linux kernel userfault support works on ``/dev/shm`` memory and on ``hugetlbfs`` +(although the kernel doesn't provide an equivalent to ``madvise(MADV_DONTNEED)`` +for hugetlbfs which may be a problem in some configurations). + +The vhost-user code in QEMU supports clients that have Postcopy support, +and the ``vhost-user-bridge`` (in ``tests/``) and the DPDK package have changes +to support postcopy. + +The client needs to open a userfaultfd and register the areas +of memory that it maps with userfault. The client must then pass the +userfaultfd back to QEMU together with a mapping table that allows +fault addresses in the clients address space to be converted back to +RAMBlock/offsets. The client's userfaultfd is added to the postcopy +fault-thread and page requests are made on behalf of the client by QEMU. +QEMU performs 'wake' operations on the client's userfaultfd to allow it +to continue after a page has arrived. + +.. note:: + There are two future improvements that would be nice: + a) Some way to make QEMU ignorant of the addresses in the clients + address space + b) Avoiding the need for QEMU to perform ufd-wake calls after the + pages have arrived + +Retro-fitting postcopy to existing clients is possible: + a) A mechanism is needed for the registration with userfault as above, + and the registration needs to be coordinated with the phases of + postcopy. In vhost-user extra messages are added to the existing + control channel. + b) Any thread that can block due to guest memory accesses must be + identified and the implication understood; for example if the + guest memory access is made while holding a lock then all other + threads waiting for that lock will also be blocked. + +Postcopy preemption mode +------------------------ + +Postcopy preempt is a new capability introduced in 8.0 QEMU release, it +allows urgent pages (those got page fault requested from destination QEMU +explicitly) to be sent in a separate preempt channel, rather than queued in +the background migration channel. Anyone who cares about latencies of page +faults during a postcopy migration should enable this feature. By default, +it's not enabled. diff --git a/docs/devel/migration/vfio.rst b/docs/devel/migration/vfio.rst new file mode 100644 index 0000000000..c49482eab6 --- /dev/null +++ b/docs/devel/migration/vfio.rst @@ -0,0 +1,208 @@ +===================== +VFIO device migration +===================== + +Migration of virtual machine involves saving the state for each device that +the guest is running on source host and restoring this saved state on the +destination host. This document details how saving and restoring of VFIO +devices is done in QEMU. + +Migration of VFIO devices consists of two phases: the optional pre-copy phase, +and the stop-and-copy phase. The pre-copy phase is iterative and allows to +accommodate VFIO devices that have a large amount of data that needs to be +transferred. The iterative pre-copy phase of migration allows for the guest to +continue whilst the VFIO device state is transferred to the destination, this +helps to reduce the total downtime of the VM. VFIO devices opt-in to pre-copy +support by reporting the VFIO_MIGRATION_PRE_COPY flag in the +VFIO_DEVICE_FEATURE_MIGRATION ioctl. + +When pre-copy is supported, it's possible to further reduce downtime by +enabling "switchover-ack" migration capability. +VFIO migration uAPI defines "initial bytes" as part of its pre-copy data stream +and recommends that the initial bytes are sent and loaded in the destination +before stopping the source VM. Enabling this migration capability will +guarantee that and thus, can potentially reduce downtime even further. + +To support migration of multiple devices that might do P2P transactions between +themselves, VFIO migration uAPI defines an intermediate P2P quiescent state. +While in the P2P quiescent state, P2P DMA transactions cannot be initiated by +the device, but the device can respond to incoming ones. Additionally, all +outstanding P2P transactions are guaranteed to have been completed by the time +the device enters this state. + +All the devices that support P2P migration are first transitioned to the P2P +quiescent state and only then are they stopped or started. This makes migration +safe P2P-wise, since starting and stopping the devices is not done atomically +for all the devices together. + +Thus, multiple VFIO devices migration is allowed only if all the devices +support P2P migration. Single VFIO device migration is allowed regardless of +P2P migration support. + +A detailed description of the UAPI for VFIO device migration can be found in +the comment for the ``vfio_device_mig_state`` structure in the header file +linux-headers/linux/vfio.h. + +VFIO implements the device hooks for the iterative approach as follows: + +* A ``save_setup`` function that sets up migration on the source. + +* A ``load_setup`` function that sets the VFIO device on the destination in + _RESUMING state. + +* A ``state_pending_estimate`` function that reports an estimate of the + remaining pre-copy data that the vendor driver has yet to save for the VFIO + device. + +* A ``state_pending_exact`` function that reads pending_bytes from the vendor + driver, which indicates the amount of data that the vendor driver has yet to + save for the VFIO device. + +* An ``is_active_iterate`` function that indicates ``save_live_iterate`` is + active only when the VFIO device is in pre-copy states. + +* A ``save_live_iterate`` function that reads the VFIO device's data from the + vendor driver during iterative pre-copy phase. + +* A ``switchover_ack_needed`` function that checks if the VFIO device uses + "switchover-ack" migration capability when this capability is enabled. + +* A ``save_state`` function to save the device config space if it is present. + +* A ``save_live_complete_precopy`` function that sets the VFIO device in + _STOP_COPY state and iteratively copies the data for the VFIO device until + the vendor driver indicates that no data remains. + +* A ``load_state`` function that loads the config section and the data + sections that are generated by the save functions above. + +* ``cleanup`` functions for both save and load that perform any migration + related cleanup. + + +The VFIO migration code uses a VM state change handler to change the VFIO +device state when the VM state changes from running to not-running, and +vice versa. + +Similarly, a migration state change handler is used to trigger a transition of +the VFIO device state when certain changes of the migration state occur. For +example, the VFIO device state is transitioned back to _RUNNING in case a +migration failed or was canceled. + +System memory dirty pages tracking +---------------------------------- + +A ``log_global_start`` and ``log_global_stop`` memory listener callback informs +the VFIO dirty tracking module to start and stop dirty page tracking. A +``log_sync`` memory listener callback queries the dirty page bitmap from the +dirty tracking module and marks system memory pages which were DMA-ed by the +VFIO device as dirty. The dirty page bitmap is queried per container. + +Currently there are two ways dirty page tracking can be done: +(1) Device dirty tracking: +In this method the device is responsible to log and report its DMAs. This +method can be used only if the device is capable of tracking its DMAs. +Discovering device capability, starting and stopping dirty tracking, and +syncing the dirty bitmaps from the device are done using the DMA logging uAPI. +More info about the uAPI can be found in the comments of the +``vfio_device_feature_dma_logging_control`` and +``vfio_device_feature_dma_logging_report`` structures in the header file +linux-headers/linux/vfio.h. + +(2) VFIO IOMMU module: +In this method dirty tracking is done by IOMMU. However, there is currently no +IOMMU support for dirty page tracking. For this reason, all pages are +perpetually marked dirty, unless the device driver pins pages through external +APIs in which case only those pinned pages are perpetually marked dirty. + +If the above two methods are not supported, all pages are perpetually marked +dirty by QEMU. + +By default, dirty pages are tracked during pre-copy as well as stop-and-copy +phase. So, a page marked as dirty will be copied to the destination in both +phases. Copying dirty pages in pre-copy phase helps QEMU to predict if it can +achieve its downtime tolerances. If QEMU during pre-copy phase keeps finding +dirty pages continuously, then it understands that even in stop-and-copy phase, +it is likely to find dirty pages and can predict the downtime accordingly. + +QEMU also provides a per device opt-out option ``pre-copy-dirty-page-tracking`` +which disables querying the dirty bitmap during pre-copy phase. If it is set to +off, all dirty pages will be copied to the destination in stop-and-copy phase +only. + +System memory dirty pages tracking when vIOMMU is enabled +--------------------------------------------------------- + +With vIOMMU, an IO virtual address range can get unmapped while in pre-copy +phase of migration. In that case, the unmap ioctl returns any dirty pages in +that range and QEMU reports corresponding guest physical pages dirty. During +stop-and-copy phase, an IOMMU notifier is used to get a callback for mapped +pages and then dirty pages bitmap is fetched from VFIO IOMMU modules for those +mapped ranges. If device dirty tracking is enabled with vIOMMU, live migration +will be blocked. + +Flow of state changes during Live migration +=========================================== + +Below is the state change flow during live migration for a VFIO device that +supports both precopy and P2P migration. The flow for devices that don't +support it is similar, except that the relevant states for precopy and P2P are +skipped. +The values in the parentheses represent the VM state, the migration state, and +the VFIO device state, respectively. + +Live migration save path +------------------------ + +:: + + QEMU normal running state + (RUNNING, _NONE, _RUNNING) + | + migrate_init spawns migration_thread + Migration thread then calls each device's .save_setup() + (RUNNING, _SETUP, _PRE_COPY) + | + (RUNNING, _ACTIVE, _PRE_COPY) + If device is active, get pending_bytes by .state_pending_{estimate,exact}() + If total pending_bytes >= threshold_size, call .save_live_iterate() + Data of VFIO device for pre-copy phase is copied + Iterate till total pending bytes converge and are less than threshold + | + On migration completion, the vCPUs and the VFIO device are stopped + The VFIO device is first put in P2P quiescent state + (FINISH_MIGRATE, _ACTIVE, _PRE_COPY_P2P) + | + Then the VFIO device is put in _STOP_COPY state + (FINISH_MIGRATE, _ACTIVE, _STOP_COPY) + .save_live_complete_precopy() is called for each active device + For the VFIO device, iterate in .save_live_complete_precopy() until + pending data is 0 + | + (POSTMIGRATE, _COMPLETED, _STOP_COPY) + Migraton thread schedules cleanup bottom half and exits + | + .save_cleanup() is called + (POSTMIGRATE, _COMPLETED, _STOP) + +Live migration resume path +-------------------------- + +:: + + Incoming migration calls .load_setup() for each device + (RESTORE_VM, _ACTIVE, _STOP) + | + For each device, .load_state() is called for that device section data + (RESTORE_VM, _ACTIVE, _RESUMING) + | + At the end, .load_cleanup() is called for each device and vCPUs are started + The VFIO device is first put in P2P quiescent state + (RUNNING, _ACTIVE, _RUNNING_P2P) + | + (RUNNING, _NONE, _RUNNING) + +Postcopy +======== + +Postcopy migration is currently not supported for VFIO devices. diff --git a/docs/devel/migration/virtio.rst b/docs/devel/migration/virtio.rst new file mode 100644 index 0000000000..611a18b821 --- /dev/null +++ b/docs/devel/migration/virtio.rst @@ -0,0 +1,115 @@ +======================= +Virtio device migration +======================= + +Copyright 2015 IBM Corp. + +This work is licensed under the terms of the GNU GPL, version 2 or later. See +the COPYING file in the top-level directory. + +Saving and restoring the state of virtio devices is a bit of a twisty maze, +for several reasons: + +- state is distributed between several parts: + + - virtio core, for common fields like features, number of queues, ... + + - virtio transport (pci, ccw, ...), for the different proxy devices and + transport specific state (msix vectors, indicators, ...) + + - virtio device (net, blk, ...), for the different device types and their + state (mac address, request queue, ...) + +- most fields are saved via the stream interface; subsequently, subsections + have been added to make cross-version migration possible + +This file attempts to document the current procedure and point out some +caveats. + +Save state procedure +==================== + +:: + + virtio core virtio transport virtio device + ----------- ---------------- ------------- + + save() function registered + via VMState wrapper on + device class + virtio_save() <---------- + ------> save_config() + - save proxy device + - save transport-specific + device fields + - save common device + fields + - save common virtqueue + fields + ------> save_queue() + - save transport-specific + virtqueue fields + ------> save_device() + - save device-specific + fields + - save subsections + - device endianness, + if changed from + default endianness + - 64 bit features, if + any high feature bit + is set + - virtio-1 virtqueue + fields, if VERSION_1 + is set + +Load state procedure +==================== + +:: + + virtio core virtio transport virtio device + ----------- ---------------- ------------- + + load() function registered + via VMState wrapper on + device class + virtio_load() <---------- + ------> load_config() + - load proxy device + - load transport-specific + device fields + - load common device + fields + - load common virtqueue + fields + ------> load_queue() + - load transport-specific + virtqueue fields + - notify guest + ------> load_device() + - load device-specific + fields + - load subsections + - device endianness + - 64 bit features + - virtio-1 virtqueue + fields + - sanitize endianness + - sanitize features + - virtqueue index sanity + check + - feature-dependent setup + +Implications of this setup +========================== + +Devices need to be careful in their state processing during load: The +load_device() procedure is invoked by the core before subsections have +been loaded. Any code that depends on information transmitted in subsections +therefore has to be invoked in the device's load() function _after_ +virtio_load() returned (like e.g. code depending on features). + +Any extension of the state being migrated should be done in subsections +added to the core for compatibility reasons. If transport or device specific +state is added, core needs to invoke a callback from the new subsection. diff --git a/docs/devel/multi-process.rst b/docs/devel/multi-process.rst index e4801751f2..4ef539c0b0 100644 --- a/docs/devel/multi-process.rst +++ b/docs/devel/multi-process.rst @@ -409,8 +409,9 @@ the initial messages sent to the emulation process is a guest memory table. Each entry in this table consists of a file descriptor and size that the emulation process can ``mmap()`` to directly access guest memory, similar to ``vhost_user_set_mem_table()``. Note guest memory -must be backed by file descriptors, such as when QEMU is given the -*-mem-path* command line option. +must be backed by shared file-backed memory, for example, using +*-object memory-backend-file,share=on* and setting that memory backend +as RAM for the machine. IOMMU operations ^^^^^^^^^^^^^^^^ diff --git a/docs/devel/multi-thread-tcg.rst b/docs/devel/multi-thread-tcg.rst index c9541a7b20..1420789fff 100644 --- a/docs/devel/multi-thread-tcg.rst +++ b/docs/devel/multi-thread-tcg.rst @@ -109,6 +109,7 @@ including: - debugging operations (breakpoint insertion/removal) - some CPU helper functions - linux-user spawning its first thread + - operations related to TCG Plugins This is done with the async_safe_run_on_cpu() mechanism to ensure all vCPUs are quiescent when changes are being made to shared global @@ -226,10 +227,9 @@ instruction. This could be a future optimisation. Emulated hardware state ----------------------- -Currently thanks to KVM work any access to IO memory is automatically -protected by the global iothread mutex, also known as the BQL (Big -QEMU Lock). Any IO region that doesn't use global mutex is expected to -do its own locking. +Currently thanks to KVM work any access to IO memory is automatically protected +by the BQL (Big QEMU Lock). Any IO region that doesn't use the BQL is expected +to do its own locking. However IO memory isn't the only way emulated hardware state can be modified. Some architectures have model specific registers that diff --git a/docs/devel/multiple-iothreads.txt b/docs/devel/multiple-iothreads.txt index aeb997bed5..de85767b12 100644 --- a/docs/devel/multiple-iothreads.txt +++ b/docs/devel/multiple-iothreads.txt @@ -5,7 +5,7 @@ the COPYING file in the top-level directory. This document explains the IOThread feature and how to write code that runs -outside the QEMU global mutex. +outside the BQL. The main loop and IOThreads --------------------------- @@ -29,13 +29,13 @@ scalability bottleneck on hosts with many CPUs. Work can be spread across several IOThreads instead of just one main loop. When set up correctly this can improve I/O latency and reduce jitter seen by the guest. -The main loop is also deeply associated with the QEMU global mutex, which is a -scalability bottleneck in itself. vCPU threads and the main loop use the QEMU -global mutex to serialize execution of QEMU code. This mutex is necessary -because a lot of QEMU's code historically was not thread-safe. +The main loop is also deeply associated with the BQL, which is a +scalability bottleneck in itself. vCPU threads and the main loop use the BQL +to serialize execution of QEMU code. This mutex is necessary because a lot of +QEMU's code historically was not thread-safe. The fact that all I/O processing is done in a single main loop and that the -QEMU global mutex is contended by all vCPU threads and the main loop explain +BQL is contended by all vCPU threads and the main loop explain why it is desirable to place work into IOThreads. The experimental virtio-blk data-plane implementation has been benchmarked and @@ -61,19 +61,26 @@ There are several old APIs that use the main loop AioContext: * LEGACY qemu_aio_set_event_notifier() - monitor an event notifier * LEGACY timer_new_ms() - create a timer * LEGACY qemu_bh_new() - create a BH + * LEGACY qemu_bh_new_guarded() - create a BH with a device re-entrancy guard * LEGACY qemu_aio_wait() - run an event loop iteration Since they implicitly work on the main loop they cannot be used in code that runs in an IOThread. They might cause a crash or deadlock if called from an -IOThread since the QEMU global mutex is not held. +IOThread since the BQL is not held. Instead, use the AioContext functions directly (see include/block/aio.h): * aio_set_fd_handler() - monitor a file descriptor * aio_set_event_notifier() - monitor an event notifier * aio_timer_new() - create a timer * aio_bh_new() - create a BH + * aio_bh_new_guarded() - create a BH with a device re-entrancy guard * aio_poll() - run an event loop iteration +The qemu_bh_new_guarded/aio_bh_new_guarded APIs accept a "MemReentrancyGuard" +argument, which is used to check for and prevent re-entrancy problems. For +BHs associated with devices, the reentrancy-guard is contained in the +corresponding DeviceState and named "mem_reentrancy_guard". + The AioContext can be obtained from the IOThread using iothread_get_aio_context() or for the main loop using qemu_get_aio_context(). Code that takes an AioContext argument works both in IOThreads or the main @@ -81,27 +88,18 @@ loop, depending on which AioContext instance the caller passes in. How to synchronize with an IOThread ----------------------------------- -AioContext is not thread-safe so some rules must be followed when using file -descriptors, event notifiers, timers, or BHs across threads: +Variables that can be accessed by multiple threads require some form of +synchronization such as qemu_mutex_lock(), rcu_read_lock(), etc. -1. AioContext functions can always be called safely. They handle their -own locking internally. - -2. Other threads wishing to access the AioContext must use -aio_context_acquire()/aio_context_release() for mutual exclusion. Once the -context is acquired no other thread can access it or run event loop iterations -in this AioContext. - -Legacy code sometimes nests aio_context_acquire()/aio_context_release() calls. -Do not use nesting anymore, it is incompatible with the BDRV_POLL_WHILE() macro -used in the block layer and can lead to hangs. - -There is currently no lock ordering rule if a thread needs to acquire multiple -AioContexts simultaneously. Therefore, it is only safe for code holding the -QEMU global mutex to acquire other AioContexts. +AioContext functions like aio_set_fd_handler(), aio_set_event_notifier(), +aio_bh_new(), and aio_timer_new() are thread-safe. They can be used to trigger +activity in an IOThread. Side note: the best way to schedule a function call across threads is to call -aio_bh_schedule_oneshot(). No acquire/release or locking is needed. +aio_bh_schedule_oneshot(). + +The main loop thread can wait synchronously for a condition using +AIO_WAIT_WHILE(). AioContext and the block layer ------------------------------ @@ -109,7 +107,7 @@ The AioContext originates from the QEMU block layer, even though nowadays AioContext is a generic event loop that can be used by any QEMU subsystem. The block layer has support for AioContext integrated. Each BlockDriverState -is associated with an AioContext using bdrv_try_set_aio_context() and +is associated with an AioContext using bdrv_try_change_aio_context() and bdrv_get_aio_context(). This allows block layer code to process I/O inside the right AioContext. Other subsystems may wish to follow a similar approach. @@ -117,22 +115,16 @@ Block layer code must therefore expect to run in an IOThread and avoid using old APIs that implicitly use the main loop. See the "How to program for IOThreads" above for information on how to do that. -If main loop code such as a QMP function wishes to access a BlockDriverState -it must first call aio_context_acquire(bdrv_get_aio_context(bs)) to ensure -that callbacks in the IOThread do not run in parallel. - Code running in the monitor typically needs to ensure that past requests from the guest are completed. When a block device is running in an IOThread, the IOThread can also process requests from the guest (via ioeventfd). To achieve both objects, wrap the code between bdrv_drained_begin() and bdrv_drained_end(), thus creating a "drained -section". The functions must be called between aio_context_acquire() -and aio_context_release(). You can freely release and re-acquire the -AioContext within a drained section. +section". -Long-running jobs (usually in the form of coroutines) are best scheduled in -the BlockDriverState's AioContext to avoid the need to acquire/release around -each bdrv_*() call. The functions bdrv_add/remove_aio_context_notifier, -or alternatively blk_add/remove_aio_context_notifier if you use BlockBackends, -can be used to get a notification whenever bdrv_try_set_aio_context() moves a +Long-running jobs (usually in the form of coroutines) are often scheduled in +the BlockDriverState's AioContext. The functions +bdrv_add/remove_aio_context_notifier, or alternatively +blk_add/remove_aio_context_notifier if you use BlockBackends, can be used to +get a notification whenever bdrv_try_change_aio_context() moves a BlockDriverState to a different AioContext. diff --git a/docs/devel/nested-papr.txt b/docs/devel/nested-papr.txt new file mode 100644 index 0000000000..90943650db --- /dev/null +++ b/docs/devel/nested-papr.txt @@ -0,0 +1,119 @@ +Nested PAPR API (aka KVM on PowerVM) +==================================== + +This API aims at providing support to enable nested virtualization with +KVM on PowerVM. While the existing support for nested KVM on PowerNV was +introduced with cap-nested-hv option, however, with a slight design change, +to enable this on papr/pseries, a new cap-nested-papr option is added. eg: + + qemu-system-ppc64 -cpu POWER10 -machine pseries,cap-nested-papr=true ... + +Work by: + Michael Neuling + Vaibhav Jain + Jordan Niethe + Harsh Prateek Bora + Shivaprasad G Bhat + Kautuk Consul + +Below taken from the kernel documentation: + +Introduction +============ + +This document explains how a guest operating system can act as a +hypervisor and run nested guests through the use of hypercalls, if the +hypervisor has implemented them. The terms L0, L1, and L2 are used to +refer to different software entities. L0 is the hypervisor mode entity +that would normally be called the "host" or "hypervisor". L1 is a +guest virtual machine that is directly run under L0 and is initiated +and controlled by L0. L2 is a guest virtual machine that is initiated +and controlled by L1 acting as a hypervisor. A significant design change +wrt existing API is that now the entire L2 state is maintained within L0. + +Existing Nested-HV API +====================== + +Linux/KVM has had support for Nesting as an L0 or L1 since 2018 + +The L0 code was added:: + + commit 8e3f5fc1045dc49fd175b978c5457f5f51e7a2ce + Author: Paul Mackerras + Date: Mon Oct 8 16:31:03 2018 +1100 + KVM: PPC: Book3S HV: Framework and hcall stubs for nested virtualization + +The L1 code was added:: + + commit 360cae313702cdd0b90f82c261a8302fecef030a + Author: Paul Mackerras + Date: Mon Oct 8 16:31:04 2018 +1100 + KVM: PPC: Book3S HV: Nested guest entry via hypercall + +This API works primarily using a signal hcall h_enter_nested(). This +call made by the L1 to tell the L0 to start an L2 vCPU with the given +state. The L0 then starts this L2 and runs until an L2 exit condition +is reached. Once the L2 exits, the state of the L2 is given back to +the L1 by the L0. The full L2 vCPU state is always transferred from +and to L1 when the L2 is run. The L0 doesn't keep any state on the L2 +vCPU (except in the short sequence in the L0 on L1 -> L2 entry and L2 +-> L1 exit). + +The only state kept by the L0 is the partition table. The L1 registers +it's partition table using the h_set_partition_table() hcall. All +other state held by the L0 about the L2s is cached state (such as +shadow page tables). + +The L1 may run any L2 or vCPU without first informing the L0. It +simply starts the vCPU using h_enter_nested(). The creation of L2s and +vCPUs is done implicitly whenever h_enter_nested() is called. + +In this document, we call this existing API the v1 API. + +New PAPR API +=============== + +The new PAPR API changes from the v1 API such that the creating L2 and +associated vCPUs is explicit. In this document, we call this the v2 +API. + +h_enter_nested() is replaced with H_GUEST_VCPU_RUN(). Before this can +be called the L1 must explicitly create the L2 using h_guest_create() +and any associated vCPUs() created with h_guest_create_vCPU(). Getting +and setting vCPU state can also be performed using h_guest_{g|s}et +hcall. + +The basic execution flow is for an L1 to create an L2, run it, and +delete it is: + +- L1 and L0 negotiate capabilities with H_GUEST_{G,S}ET_CAPABILITIES() + (normally at L1 boot time). + +- L1 requests the L0 to create an L2 with H_GUEST_CREATE() and receives a token + +- L1 requests the L0 to create an L2 vCPU with H_GUEST_CREATE_VCPU() + +- L1 and L0 communicate the vCPU state using the H_GUEST_{G,S}ET() hcall + +- L1 requests the L0 to run the vCPU using H_GUEST_RUN_VCPU() hcall + +- L1 deletes L2 with H_GUEST_DELETE() + +For more details, please refer: + +[1] Linux Kernel documentation (upstream documentation commit): + +commit 476652297f94a2e5e5ef29e734b0da37ade94110 +Author: Michael Neuling +Date: Thu Sep 14 13:06:00 2023 +1000 + + docs: powerpc: Document nested KVM on POWER + + Document support for nested KVM on POWER using the existing API as well + as the new PAPR API. This includes the new HCALL interface and how it + used by KVM. + + Signed-off-by: Michael Neuling + Signed-off-by: Jordan Niethe + Signed-off-by: Michael Ellerman + Link: https://msgid.link/20230914030600.16993-12-jniethe5@gmail.com diff --git a/docs/devel/pci.rst b/docs/devel/pci.rst new file mode 100644 index 0000000000..68739334f3 --- /dev/null +++ b/docs/devel/pci.rst @@ -0,0 +1,8 @@ +============= +PCI subsystem +============= + +API Reference +------------- + +.. kernel-doc:: include/hw/pci/pci.h diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index cd9b544376..f453bd3546 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -167,6 +167,7 @@ Syntax:: '*doc-required': BOOL, '*command-name-exceptions': [ STRING, ... ], '*command-returns-exceptions': [ STRING, ... ], + '*documentation-exceptions': [ STRING, ... ], '*member-name-exceptions': [ STRING, ... ] } } The pragma directive lets you control optional generator behavior. @@ -183,6 +184,10 @@ may contain ``"_"`` instead of ``"-"``. Default is none. Pragma 'command-returns-exceptions' takes a list of commands that may violate the rules on permitted return types. Default is none. +Pragma 'documentation-exceptions' takes a list of types, commands, and +events whose members / arguments need not be documented. Default is +none. + Pragma 'member-name-exceptions' takes a list of types whose member names may contain uppercase letters, and ``"_"`` instead of ``"-"``. Default is none. @@ -545,7 +550,8 @@ Member 'allow-oob' declares whether the command supports out-of-band { 'command': 'migrate_recover', 'data': { 'uri': 'str' }, 'allow-oob': true } -See qmp-spec.txt for out-of-band execution syntax and semantics. +See the :doc:`/interop/qmp-spec` for out-of-band execution syntax +and semantics. Commands supporting out-of-band execution can still be executed in-band. @@ -593,7 +599,7 @@ blocking the guest and other background operations. Coroutine safety can be hard to prove, similar to thread safety. Common pitfalls are: -- The global mutex isn't held across ``qemu_coroutine_yield()``, so +- The BQL isn't held across ``qemu_coroutine_yield()``, so operations that used to assume that they execute atomically may have to be more careful to protect against changes in the global state. @@ -685,9 +691,10 @@ change in the QMP syntax (usually by allowing values or operations 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. Each list member can either be ``{ 'name': STRING, '*if': -COND }``, or STRING, which is shorthand for ``{ 'name': STRING }``. +For this purpose, a list of features can be specified for definitions, +enumeration values, and struct members. Each feature 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. @@ -735,9 +742,8 @@ Types, commands, and events share a common namespace. Therefore, generally speaking, type definitions should always use CamelCase for user-defined type names, while built-in types are lowercase. -Type names ending with ``Kind`` or ``List`` are reserved for the -generator, which uses them for implicit union enums and array types, -respectively. +Type names ending with ``List`` are reserved for the generator, which +uses them for array types. Command names, member names within a type, and feature names should be all lower case with words separated by a hyphen. However, some @@ -804,9 +810,8 @@ gets its generated code guarded like this:: ... generated code ... #endif /* defined(HAVE_BAR) && defined(CONFIG_FOO) */ -Individual members of complex types, commands arguments, and -event-specific data can also be made conditional. This requires the -longhand form of MEMBER. +Individual members of complex types can also be made conditional. +This requires the longhand form of MEMBER. Example: a struct type with unconditional member 'foo' and conditional member 'bar' :: @@ -817,8 +822,8 @@ member 'bar' :: A union's discriminator may not be conditional. -Likewise, individual enumeration values be conditional. This requires -the longhand form of ENUM-VALUE_. +Likewise, individual enumeration values may be conditional. This +requires the longhand form of ENUM-VALUE_. Example: an enum type with unconditional value 'foo' and conditional value 'bar' :: @@ -924,14 +929,17 @@ first character of the first line. The usual ****strong****, *\*emphasized\** and ````literal```` markup should be used. If you need a single literal ``*``, you will need to -backslash-escape it. As an extension beyond the usual rST syntax, you -can also use ``@foo`` to reference a name in the schema; this is rendered -the same way as ````foo````. +backslash-escape it. + +Use ``@foo`` to reference a name in the schema. This is an rST +extension. It is rendered the same way as ````foo````, but carries +additional meaning. Example:: ## # Some text foo with **bold** and *emphasis* + # # 1. with a list # 2. like that # @@ -944,6 +952,11 @@ Example:: # <- get that ## +For legibility, wrap text paragraphs so every line is at most 70 +characters long. + +Separate sentences with two spaces. + Definition documentation ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -960,57 +973,51 @@ commands and events), member (for structs and unions), branch (for alternates), or value (for enums), a description of each feature (if any), and finally optional tagged sections. -The description of an argument or feature 'name' starts with -'\@name:'. The description text can start on the line following the -'\@name:', in which case it must not be indented at all. It can also -start on the same line as the '\@name:'. In this case if it spans -multiple lines then second and subsequent lines must be indented to -line up with the first character of the first line of the -description:: +Descriptions start with '\@name:'. The description text must be +indented like this:: - # @argone: - # This is a two line description - # in the first style. - # - # @argtwo: This is a two line description - # in the second style. + # @name: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed + # do eiusmod tempor incididunt ut labore et dolore magna aliqua. -The number of spaces between the ':' and the text is not significant. +.. FIXME The parser accepts these things in almost any order. -.. admonition:: FIXME - - The parser accepts these things in almost any order. - -.. admonition:: FIXME - - union branches should be described, too. +.. FIXME union branches should be described, too. Extensions added after the definition was first released carry a -'(since x.y.z)' comment. +"(since x.y.z)" comment. -The feature descriptions must be preceded by a line "Features:", like -this:: +The feature descriptions must be preceded by a blank line and then a +line "Features:", like this:: + # # Features: + # # @feature: Description text -A tagged section starts with one of the following words: -"Note:"/"Notes:", "Since:", "Example"/"Examples", "Returns:", "TODO:". -The section ends with the start of a new section. +A tagged section begins with a paragraph that starts with one of the +following words: "Note:"/"Notes:", "Since:", "Example:"/"Examples:", +"Returns:", "Errors:", "TODO:". It ends with the start of a new +section. -The text of a section can start on a new line, in -which case it must not be indented at all. It can also start -on the same line as the 'Note:', 'Returns:', etc tag. In this -case if it spans multiple lines then second and subsequent -lines must be indented to match the first, in the same way as -multiline argument descriptions. +The second and subsequent lines of tagged sections must be indented +like this:: -A 'Since: x.y.z' tagged section lists the release that introduced the + # Note: Ut enim ad minim veniam, quis nostrud exercitation ullamco + # laboris nisi ut aliquip ex ea commodo consequat. + # + # Duis aute irure dolor in reprehenderit in voluptate velit esse + # cillum dolore eu fugiat nulla pariatur. + +"Returns" and "Errors" sections are only valid for commands. They +document the success and the error response, respectively. + +A "Since: x.y.z" tagged section lists the release that introduced the definition. -An 'Example' or 'Examples' section is automatically rendered -entirely as literal fixed-width text. In other sections, -the text is formatted, and rST markup can be used. +An "Example" or "Examples" section is rendered entirely +as literal fixed-width text. "TODO" sections are not rendered at all +(they are for developers, not users of QMP). In other sections, the +text is formatted, and rST markup can be used. For example:: @@ -1020,13 +1027,13 @@ For example:: # Statistics of a virtual block device or a block backing device. # # @device: If the stats are for a virtual block device, the name - # corresponding to the virtual block device. + # corresponding to the virtual block device. # - # @node-name: The node name of the device. (since 2.3) + # @node-name: The node name of the device. (Since 2.3) # # ... more members ... # - # Since: 0.14.0 + # Since: 0.14 ## { 'struct': 'BlockStats', 'data': {'*device': 'str', '*node-name': 'str', @@ -1037,26 +1044,85 @@ For example:: # # Query the @BlockStats for all virtual block devices. # - # @query-nodes: If true, the command will query all the - # block nodes ... explain, explain ... (since 2.3) + # @query-nodes: If true, the command will query all the block nodes + # ... explain, explain ... + # (Since 2.3) # # Returns: A list of @BlockStats for each virtual block devices. # - # Since: 0.14.0 + # Since: 0.14 # # Example: # - # -> { "execute": "query-blockstats" } - # <- { - # ... lots of output ... - # } - # + # -> { "execute": "query-blockstats" } + # <- { + # ... lots of output ... + # } ## { 'command': 'query-blockstats', 'data': { '*query-nodes': 'bool' }, 'returns': ['BlockStats'] } +Markup pitfalls +~~~~~~~~~~~~~~~ + +A blank line is required between list items and paragraphs. Without +it, the list may not be recognized, resulting in garbled output. Good +example:: + + # An event's state is modified if: + # + # - its name matches the @name pattern, and + # - if @vcpu is given, the event has the "vcpu" property. + +Without the blank line this would be a single paragraph. + +Indentation matters. Bad example:: + + # @none: None (no memory side cache in this proximity domain, + # or cache associativity unknown) + # (since 5.0) + +The last line's de-indent is wrong. The second and subsequent lines +need to line up with each other, like this:: + + # @none: None (no memory side cache in this proximity domain, + # or cache associativity unknown) + # (since 5.0) + +Section tags are case-sensitive and end with a colon. They are only +recognized after a blank line. Good example:: + + # + # Since: 7.1 + +Bad examples (all ordinary paragraphs):: + + # since: 7.1 + + # Since 7.1 + + # Since : 7.1 + +Likewise, member descriptions require a colon. Good example:: + + # @interface-id: Interface ID + +Bad examples (all ordinary paragraphs):: + + # @interface-id Interface ID + + # @interface-id : Interface ID + +Undocumented members are not flagged, yet. Instead, the generated +documentation describes them as "Not documented". Think twice before +adding more undocumented members. + +When you change documentation comments, please check the generated +documentation comes out as intended! + + Client JSON Protocol introspection ================================== @@ -1157,9 +1223,8 @@ Example: the SchemaInfo for EVENT_C from section Events_ :: Type "q_obj-EVENT_C-arg" is an implicitly defined object type with the two members from the event's definition. -The SchemaInfo for struct and union types has meta-type "object". - -The SchemaInfo for a struct type has variant member "members". +The SchemaInfo for struct and union types has meta-type "object" and +variant member "members". The SchemaInfo for a union type additionally has variant members "tag" and "variants". @@ -1357,7 +1422,7 @@ qmp_my_command(); everything else is produced by the generator. :: $ cat example-schema.json { 'struct': 'UserDefOne', - 'data': { 'integer': 'int', '*string': 'str' } } + 'data': { 'integer': 'int', '*string': 'str', '*flag': 'bool' } } { 'command': 'my-command', 'data': { 'arg1': ['UserDefOne'] }, @@ -1410,8 +1475,9 @@ Example:: struct UserDefOne { int64_t integer; - bool has_string; char *string; + bool has_flag; + bool flag; }; void qapi_free_UserDefOne(UserDefOne *obj); @@ -1523,14 +1589,21 @@ Example:: bool visit_type_UserDefOne_members(Visitor *v, UserDefOne *obj, Error **errp) { + bool has_string = !!obj->string; + if (!visit_type_int(v, "integer", &obj->integer, errp)) { return false; } - if (visit_optional(v, "string", &obj->has_string)) { + if (visit_optional(v, "string", &has_string)) { if (!visit_type_str(v, "string", &obj->string, errp)) { return false; } } + if (visit_optional(v, "flag", &obj->has_flag)) { + if (!visit_type_bool(v, "flag", &obj->flag, errp)) { + return false; + } + } return true; } @@ -1664,7 +1737,6 @@ Example:: $ cat qapi-generated/example-qapi-commands.c [Uninteresting stuff omitted...] - static void qmp_marshal_output_UserDefOne(UserDefOne *ret_in, QObject **ret_out, Error **errp) { @@ -1748,7 +1820,7 @@ Example:: QTAILQ_INIT(cmds); qmp_register_command(cmds, "my-command", - qmp_marshal_my_command, QCO_NO_OPTIONS); + qmp_marshal_my_command, 0, 0); } [Uninteresting stuff omitted...] @@ -1917,6 +1989,12 @@ Example:: { "type", QLIT_QSTR("str"), }, {} })), + QLIT_QDICT(((QLitDictEntry[]) { + { "default", QLIT_QNULL, }, + { "name", QLIT_QSTR("flag"), }, + { "type", QLIT_QSTR("bool"), }, + {} + })), {} })), }, { "meta-type", QLIT_QSTR("object"), }, @@ -1950,6 +2028,12 @@ Example:: { "name", QLIT_QSTR("str"), }, {} })), + QLIT_QDICT(((QLitDictEntry[]) { + { "json-type", QLIT_QSTR("boolean"), }, + { "meta-type", QLIT_QSTR("builtin"), }, + { "name", QLIT_QSTR("bool"), }, + {} + })), {} })); diff --git a/docs/devel/qdev-api.rst b/docs/devel/qdev-api.rst new file mode 100644 index 0000000000..3f35eea025 --- /dev/null +++ b/docs/devel/qdev-api.rst @@ -0,0 +1,7 @@ +.. _qdev-api: + +================================ +QEMU Device (qdev) API Reference +================================ + +.. kernel-doc:: include/hw/qdev-core.h diff --git a/docs/devel/qom-api.rst b/docs/devel/qom-api.rst new file mode 100644 index 0000000000..ed1f17e797 --- /dev/null +++ b/docs/devel/qom-api.rst @@ -0,0 +1,9 @@ +.. _qom-api: + +===================================== +QEMU Object Model (QOM) API Reference +===================================== + +This is the complete API documentation for :ref:`qom`. + +.. kernel-doc:: include/qom/object.h diff --git a/docs/devel/qom.rst b/docs/devel/qom.rst index e5fe3597cd..0889ca949c 100644 --- a/docs/devel/qom.rst +++ b/docs/devel/qom.rst @@ -1,3 +1,5 @@ +.. _qom: + =========================== The QEMU Object Model (QOM) =========================== @@ -11,6 +13,24 @@ features: - System for dynamically registering types - Support for single-inheritance of types - Multiple inheritance of stateless interfaces +- Mapping internal members to publicly exposed properties + +The root object class is TYPE_OBJECT which provides for the basic +object methods. + +The QOM tree +============ + +The QOM tree is a composition tree which represents all of the objects +that make up a QEMU "machine". You can view this tree by running +``info qom-tree`` in the :ref:`QEMU monitor`. It will contain both +objects created by the machine itself as well those created due to +user configuration. + +Creating a QOM class +==================== + +A simple minimal device implementation may look something like below: .. code-block:: c :caption: Creating a minimal type @@ -24,7 +44,7 @@ features: typedef DeviceClass MyDeviceClass; typedef struct MyDevice { - DeviceState parent; + DeviceState parent_obj; int reg0, reg1, reg2; } MyDevice; @@ -46,6 +66,12 @@ In the above example, we create a simple type that is described by #TypeInfo. #TypeInfo describes information about the type including what it inherits from, the instance and class size, and constructor/destructor hooks. +The TYPE_DEVICE class is the parent class for all modern devices +implemented in QEMU and adds some specific methods to handle QEMU +device model. This includes managing the lifetime of devices from +creation through to when they become visible to the guest and +eventually unrealized. + Alternatively several static types could be registered using helper macro DEFINE_TYPES() @@ -96,7 +122,7 @@ when the object is needed. module_obj(TYPE_MY_DEVICE); Class Initialization -==================== +-------------------- Before an object is initialized, the class for the object must be initialized. There is only one class object for all instance objects @@ -145,7 +171,7 @@ will also have a wrapper function to call it easily: typedef struct MyDeviceClass { - DeviceClass parent; + DeviceClass parent_class; void (*frobnicate) (MyDevice *obj); } MyDeviceClass; @@ -166,7 +192,7 @@ will also have a wrapper function to call it easily: } Interfaces -========== +---------- Interfaces allow a limited form of multiple inheritance. Instances are similar to normal types except for the fact that are only defined by @@ -180,7 +206,7 @@ an argument to a method on its corresponding SomethingIfClass, or to dynamically cast it to an object that implements the interface. Methods -======= +------- A *method* is a function within the namespace scope of a class. It usually operates on the object instance by passing it as a @@ -273,8 +299,8 @@ Alternatively, object_class_by_name() can be used to obtain the class and its non-overridden methods for a specific type. This would correspond to ``MyClass::method(...)`` in C++. -The first example of such a QOM method was #CPUClass.reset, -another example is #DeviceClass.realize. +One example of such methods is ``DeviceClass.reset``. More examples +can be found at :ref:`device-life-cycle`. Standard type declaration and definition macros =============================================== @@ -292,8 +318,7 @@ in the header file: .. code-block:: c :caption: Declaring a simple type - OBJECT_DECLARE_SIMPLE_TYPE(MyDevice, my_device, - MY_DEVICE, DEVICE) + OBJECT_DECLARE_SIMPLE_TYPE(MyDevice, MY_DEVICE) This is equivalent to the following: @@ -323,12 +348,14 @@ used. This does the same as OBJECT_DECLARE_SIMPLE_TYPE(), but without the 'struct MyDeviceClass' definition. To implement the type, the OBJECT_DEFINE macro family is available. -In the simple case the OBJECT_DEFINE_TYPE macro is suitable: +For the simplest case of a leaf class which doesn't need any of its +own virtual functions (i.e. which was declared with OBJECT_DECLARE_SIMPLE_TYPE) +the OBJECT_DEFINE_SIMPLE_TYPE macro is suitable: .. code-block:: c :caption: Defining a simple type - OBJECT_DEFINE_TYPE(MyDevice, my_device, MY_DEVICE, DEVICE) + OBJECT_DEFINE_SIMPLE_TYPE(MyDevice, my_device, MY_DEVICE, DEVICE) This is equivalent to the following: @@ -345,7 +372,6 @@ This is equivalent to the following: .instance_size = sizeof(MyDevice), .instance_init = my_device_init, .instance_finalize = my_device_finalize, - .class_size = sizeof(MyDeviceClass), .class_init = my_device_class_init, }; @@ -360,20 +386,43 @@ This is sufficient to get the type registered with the type system, and the three standard methods now need to be implemented along with any other logic required for the type. +If the class needs its own virtual methods, or has some other +per-class state it needs to store in its own class struct, +then you can use the OBJECT_DEFINE_TYPE macro. This does the +same thing as OBJECT_DEFINE_SIMPLE_TYPE, but it also sets the +class_size of the type to the size of the class struct. + +.. code-block:: c + :caption: Defining a type which needs a class struct + + OBJECT_DEFINE_TYPE(MyDevice, my_device, MY_DEVICE, DEVICE) + If the type needs to implement one or more interfaces, then the -OBJECT_DEFINE_TYPE_WITH_INTERFACES() macro can be used instead. -This accepts an array of interface type names. +OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES() and +OBJECT_DEFINE_TYPE_WITH_INTERFACES() macros can be used instead. +These accept an array of interface type names. The difference between +them is that the former is for simple leaf classes that don't need +a class struct, and the latter is for when you will be defining +a class struct. .. code-block:: c :caption: Defining a simple type implementing interfaces + OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(MyDevice, my_device, + MY_DEVICE, DEVICE, + { TYPE_USER_CREATABLE }, + { NULL }) + +.. code-block:: c + :caption: Defining a type implementing interfaces + OBJECT_DEFINE_TYPE_WITH_INTERFACES(MyDevice, my_device, MY_DEVICE, DEVICE, { TYPE_USER_CREATABLE }, { NULL }) -If the type is not intended to be instantiated, then then -the OBJECT_DEFINE_ABSTRACT_TYPE() macro can be used instead: +If the type is not intended to be instantiated, then the +OBJECT_DEFINE_ABSTRACT_TYPE() macro can be used instead: .. code-block:: c :caption: Defining a simple abstract type @@ -381,9 +430,32 @@ the OBJECT_DEFINE_ABSTRACT_TYPE() macro can be used instead: OBJECT_DEFINE_ABSTRACT_TYPE(MyDevice, my_device, MY_DEVICE, DEVICE) +.. _device-life-cycle: +Device Life-cycle +================= + +As class initialisation cannot fail devices have an two additional +methods to handle the creation of dynamic devices. The ``realize`` +function is called with ``Error **`` pointer which should be set if +the device cannot complete its setup. Otherwise on successful +completion of the ``realize`` method the device object is added to the +QOM tree and made visible to the guest. + +The reverse function is ``unrealize`` and should be were clean-up +code lives to tidy up after the system is done with the device. + +All devices can be instantiated by C code, however only some can +created dynamically via the command line or monitor. + +Likewise only some can be unplugged after creation and need an +explicit ``unrealize`` implementation. This is determined by the +``user_creatable`` variable in the root ``DeviceClass`` structure. +Devices can only be unplugged if their ``parent_bus`` has a registered +``HotplugHandler``. API Reference -------------- +============= -.. kernel-doc:: include/qom/object.h +See the :ref:`QOM API` and :ref:`QDEV API` +documents for the complete API description. diff --git a/docs/devel/qtest.rst b/docs/devel/qtest.rst index 0455aa06ab..c5b8546b3e 100644 --- a/docs/devel/qtest.rst +++ b/docs/devel/qtest.rst @@ -81,7 +81,7 @@ which you can run manually. QTest Protocol -------------- -.. kernel-doc:: softmmu/qtest.c +.. kernel-doc:: system/qtest.c :doc: QTest Protocol diff --git a/docs/devel/replay.rst b/docs/devel/replay.rst index 0244be8b9c..effd856f0c 100644 --- a/docs/devel/replay.rst +++ b/docs/devel/replay.rst @@ -184,7 +184,7 @@ modes. Reading and writing requests are created by CPU thread of QEMU. Later these requests proceed to block layer which creates "bottom halves". Bottom halves consist of callback and its parameters. They are processed when -main loop locks the global mutex. These locks are not synchronized with +main loop locks the BQL. These locks are not synchronized with replaying process because main loop also processes the events that do not affect the virtual machine state (like user interaction with monitor). diff --git a/docs/devel/reset.rst b/docs/devel/reset.rst index abea1102dc..2ea85e7779 100644 --- a/docs/devel/reset.rst +++ b/docs/devel/reset.rst @@ -11,15 +11,15 @@ 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. - +The main object types which implement this interface are DeviceClass +and BusClass. 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. +the BQL. 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 @@ -184,21 +184,20 @@ in reset. { 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); + resettable_class_set_parent_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 +``resettable_class_set_parent_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); + resettable_class_set_parent_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 @@ -210,9 +209,11 @@ 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. +An object is under reset from the beginning of the *enter* phase (before +either its children or its own enter method is called) to the *exit* +phase. During *enter* and *hold* phase only, the function will return that the +object is in reset. The state is changed after the *exit* is propagated to +its children and just before calling the object's own *exit* method. 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, @@ -287,3 +288,43 @@ There is currently 2 cases where this function is used: 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. + +Reset of the complete system +---------------------------- + +Reset of the complete system is a little complicated. The typical +flow is: + +1. Code which wishes to reset the entire system does so by calling + ``qemu_system_reset_request()``. This schedules a reset, but the + reset will happen asynchronously after the function returns. + That makes this safe to call from, for example, device models. + +2. The function which is called to make the reset happen is + ``qemu_system_reset()``. Generally only core system code should + call this directly. + +3. ``qemu_system_reset()`` calls the ``MachineClass::reset`` method of + the current machine, if it has one. That method must call + ``qemu_devices_reset()``. If the machine has no reset method, + ``qemu_system_reset()`` calls ``qemu_devices_reset()`` directly. + +4. ``qemu_devices_reset()`` performs a reset of the system, using + the three-phase mechanism listed above. It resets all objects + that were registered with it using ``qemu_register_resettable()``. + It also calls all the functions registered with it using + ``qemu_register_reset()``. Those functions are called during the + "hold" phase of this reset. + +5. The most important object that this reset resets is the + 'sysbus' bus. The sysbus bus is the root of the qbus tree. This + means that all devices on the sysbus are reset, and all their + child buses, and all the devices on those child buses. + +6. Devices which are not on the qbus tree are *not* automatically + reset! (The most obvious example of this is CPU objects, but + anything that directly inherits from ``TYPE_OBJECT`` or ``TYPE_DEVICE`` + rather than from ``TYPE_SYS_BUS_DEVICE`` or some other plugs-into-a-bus + type will be in this category.) You need to therefore arrange for these + to be reset in some other way (e.g. using ``qemu_register_resettable()`` + or ``qemu_register_reset()``). diff --git a/docs/devel/s390-cpu-topology.rst b/docs/devel/s390-cpu-topology.rst new file mode 100644 index 0000000000..48313b92d4 --- /dev/null +++ b/docs/devel/s390-cpu-topology.rst @@ -0,0 +1,170 @@ +QAPI interface for S390 CPU topology +==================================== + +The following sections will explain the QAPI interface for S390 CPU topology +with the help of exemplary output. +For this, let's assume that QEMU has been started with the following +command, defining 4 CPUs, where CPU[0] is defined by the -smp argument and will +have default values: + +.. code-block:: bash + + qemu-system-s390x \ + -enable-kvm \ + -cpu z14,ctop=on \ + -smp 1,drawers=3,books=3,sockets=2,cores=2,maxcpus=36 \ + -device z14-s390x-cpu,core-id=19,entitlement=high \ + -device z14-s390x-cpu,core-id=11,entitlement=low \ + -device z14-s390x-cpu,core-id=12,entitlement=high \ + ... + +Additions to query-cpus-fast +---------------------------- + +The command query-cpus-fast allows querying the topology tree and +modifiers for all configured vCPUs. + +.. code-block:: QMP + + { "execute": "query-cpus-fast" } + { + "return": [ + { + "dedicated": false, + "thread-id": 536993, + "props": { + "core-id": 0, + "socket-id": 0, + "drawer-id": 0, + "book-id": 0 + }, + "cpu-state": "operating", + "entitlement": "medium", + "qom-path": "/machine/unattached/device[0]", + "cpu-index": 0, + "target": "s390x" + }, + { + "dedicated": false, + "thread-id": 537003, + "props": { + "core-id": 19, + "socket-id": 1, + "drawer-id": 0, + "book-id": 2 + }, + "cpu-state": "operating", + "entitlement": "high", + "qom-path": "/machine/peripheral-anon/device[0]", + "cpu-index": 19, + "target": "s390x" + }, + { + "dedicated": false, + "thread-id": 537004, + "props": { + "core-id": 11, + "socket-id": 1, + "drawer-id": 0, + "book-id": 1 + }, + "cpu-state": "operating", + "entitlement": "low", + "qom-path": "/machine/peripheral-anon/device[1]", + "cpu-index": 11, + "target": "s390x" + }, + { + "dedicated": true, + "thread-id": 537005, + "props": { + "core-id": 12, + "socket-id": 0, + "drawer-id": 3, + "book-id": 2 + }, + "cpu-state": "operating", + "entitlement": "high", + "qom-path": "/machine/peripheral-anon/device[2]", + "cpu-index": 12, + "target": "s390x" + } + ] + } + + +QAPI command: set-cpu-topology +------------------------------ + +The command set-cpu-topology allows modifying the topology tree +or the topology modifiers of a vCPU in the configuration. + +.. code-block:: QMP + + { "execute": "set-cpu-topology", + "arguments": { + "core-id": 11, + "socket-id": 0, + "book-id": 0, + "drawer-id": 0, + "entitlement": "low", + "dedicated": false + } + } + {"return": {}} + +The core-id parameter is the only mandatory parameter and every +unspecified parameter keeps its previous value. + +QAPI event CPU_POLARIZATION_CHANGE +---------------------------------- + +When a guest requests a modification of the polarization, +QEMU sends a CPU_POLARIZATION_CHANGE event. + +When requesting the change, the guest only specifies horizontal or +vertical polarization. +It is the job of the entity administrating QEMU to set the dedication and fine +grained vertical entitlement in response to this event. + +Note that a vertical polarized dedicated vCPU can only have a high +entitlement, giving 6 possibilities for vCPU polarization: + +- Horizontal +- Horizontal dedicated +- Vertical low +- Vertical medium +- Vertical high +- Vertical high dedicated + +Example of the event received when the guest issues the CPU instruction +Perform Topology Function PTF(0) to request an horizontal polarization: + +.. code-block:: QMP + + { + "timestamp": { + "seconds": 1687870305, + "microseconds": 566299 + }, + "event": "CPU_POLARIZATION_CHANGE", + "data": { + "polarization": "horizontal" + } + } + +QAPI query command: query-s390x-cpu-polarization +------------------------------------------------ + +The query command query-s390x-cpu-polarization returns the current +CPU polarization of the machine. +In this case the guest previously issued a PTF(1) to request vertical polarization: + +.. code-block:: QMP + + { "execute": "query-s390x-cpu-polarization" } + { + "return": { + "polarization": "vertical" + } + } diff --git a/docs/devel/style.rst b/docs/devel/style.rst index 7ddd42b6c2..2f68b50079 100644 --- a/docs/devel/style.rst +++ b/docs/devel/style.rst @@ -204,7 +204,14 @@ Declarations Mixed declarations (interleaving statements and declarations within blocks) are generally not allowed; declarations should be at the beginning -of blocks. +of blocks. To avoid accidental re-use it is permissible to declare +loop variables inside for loops: + +.. code-block:: c + + for (int i = 0; i < ARRAY_SIZE(thing); i++) { + /* do something loopy */ + } Every now and then, an exception is made for declarations inside a #ifdef or #ifndef block: if the code looks nicer, such declarations can @@ -293,6 +300,27 @@ that QEMU depends on. Do not include "qemu/osdep.h" from header files since the .c file will have already included it. +Headers should normally include everything they need beyond osdep.h. +If exceptions are needed for some reason, they must be documented in +the header. If all that's needed from a header is typedefs, consider +putting those into qemu/typedefs.h instead of including the header. + +Cyclic inclusion is forbidden. + +Generative Includes +------------------- + +QEMU makes fairly extensive use of the macro pre-processor to +instantiate multiple similar functions. While such abuse of the macro +processor isn't discouraged it can make debugging and code navigation +harder. You should consider carefully if the same effect can be +achieved by making it easy for the compiler to constant fold or using +python scripting to generate grep friendly code. + +If you do use template header files they should be named with the +``.c.inc`` or ``.h.inc`` suffix to make it clear they are being +included for expansion. + C types ======= @@ -546,7 +574,8 @@ For example, instead of .. code-block:: c - int somefunc(void) { + int somefunc(void) + { int ret = -1; char *foo = g_strdup_printf("foo%", "wibble"); GList *bar = ..... @@ -567,7 +596,8 @@ Using g_autofree/g_autoptr enables the code to be written as: .. code-block:: c - int somefunc(void) { + int somefunc(void) + { g_autofree char *foo = g_strdup_printf("foo%", "wibble"); g_autoptr (GList) bar = ..... @@ -592,7 +622,8 @@ are still some caveats to beware of .. code-block:: c - char *somefunc(void) { + char *somefunc(void) + { g_autofree char *foo = g_strdup_printf("foo%", "wibble"); g_autoptr (GList) bar = ..... @@ -607,6 +638,97 @@ are still some caveats to beware of QEMU Specific Idioms ******************** +QEMU Object Model Declarations +============================== + +The QEMU Object Model (QOM) provides a framework for handling objects +in the base C language. The first declaration of a storage or class +structure should always be the parent and leave a visual space between +that declaration and the new code. It is also useful to separate +backing for properties (options driven by the user) and internal state +to make navigation easier. + +For a storage structure the first declaration should always be called +"parent_obj" and for a class structure the first member should always +be called "parent_class" as below: + +.. code-block:: c + + struct MyDeviceState { + DeviceState parent_obj; + + /* Properties */ + int prop_a; + char *prop_b; + /* Other stuff */ + int internal_state; + }; + + struct MyDeviceClass { + DeviceClass parent_class; + + void (*new_fn1)(void); + bool (*new_fn2)(CPUState *); + }; + +Note that there is no need to provide typedefs for QOM structures +since these are generated automatically by the QOM declaration macros. +See :ref:`qom` for more details. + +QEMU GUARD macros +================= + +QEMU provides a number of ``_GUARD`` macros intended to make the +handling of multiple exit paths easier. For example using +``QEMU_LOCK_GUARD`` to take a lock will ensure the lock is released on +exit from the function. + +.. code-block:: c + + static int my_critical_function(SomeState *s, void *data) + { + QEMU_LOCK_GUARD(&s->lock); + do_thing1(data); + if (check_state2(data)) { + return -1; + } + do_thing3(data); + return 0; + } + +will ensure s->lock is released however the function is exited. The +equivalent code without _GUARD macro makes us to carefully put +qemu_mutex_unlock() on all exit points: + +.. code-block:: c + + static int my_critical_function(SomeState *s, void *data) + { + qemu_mutex_lock(&s->lock); + do_thing1(data); + if (check_state2(data)) { + qemu_mutex_unlock(&s->lock); + return -1; + } + do_thing3(data); + qemu_mutex_unlock(&s->lock); + return 0; + } + +There are often ``WITH_`` forms of macros which more easily wrap +around a block inside a function. + +.. code-block:: c + + WITH_RCU_READ_LOCK_GUARD() { + QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) { + err = do_the_thing(kid->child); + if (err < 0) { + return err; + } + } + } + Error handling and reporting ============================ diff --git a/docs/devel/submitting-a-patch.rst b/docs/devel/submitting-a-patch.rst index d3876ec1b7..c641d948f1 100644 --- a/docs/devel/submitting-a-patch.rst +++ b/docs/devel/submitting-a-patch.rst @@ -3,34 +3,27 @@ Submitting a Patch ================== -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 submitting patches. If you follow these, you'll help -make our task of code review easier and your patch is likely to be -committed faster. +QEMU welcomes contributions to fix bugs, add functionality or improve +the documentation. However, we get a lot of patches, and so we have +some guidelines about submitting them. If you follow these, you'll +help make our task of contribution review easier and your change is +likely to be accepted and committed faster. This page seems very long, so if you are only trying to post a quick one-shot fix, the bare minimum we ask is that: -- You **must** provide a Signed-off-by: line (this is a hard - requirement because it's how you say "I'm legally okay to contribute - this and happy for it to go into QEMU", modeled after the `Linux kernel - `__ - policy.) ``git commit -s`` or ``git format-patch -s`` will add one. -- All contributions to QEMU must be **sent as patches** to the - qemu-devel `mailing list `__. Patch contributions - should not be posted on the bug tracker, posted on forums, or - externally hosted and linked to. (We have other mailing lists too, - but all patches must go to qemu-devel, possibly with a Cc: to another - list.) ``git send-email`` (`step-by-step setup - guide `__ and `hints and - tips `__) - works best for delivering the patch without mangling it, but - attachments can be used as a last resort on a first-time submission. -- You must read replies to your message, and be willing to act on them. - Note, however, that maintainers are often willing to manually fix up - first-time contributions, since there is a learning curve involved in - making an ideal patch submission. +.. list-table:: Minimal Checklist for Patches + :widths: 35 65 + :header-rows: 1 + + * - Check + - Reason + * - Patches contain Signed-off-by: Real Name + - States you are legally able to contribute the code. See :ref:`patch_emails_must_include_a_signed_off_by_line` + * - Sent as patch emails to ``qemu-devel@nongnu.org`` + - The project uses an email list based workflow. See :ref:`submitting_your_patches` + * - Be prepared to respond to review comments + - Code that doesn't pass review will not get merged. See :ref:`participating_in_code_review` You do not have to subscribe to post (list policy is to reply-to-all to preserve CCs and keep non-subscribers in the loop on the threads they @@ -39,7 +32,7 @@ ideas from other posts. If you do subscribe, be prepared for a high volume of email, often over one thousand messages in a week. The list is moderated; first-time posts from an email address (whether or not you subscribed) may be subject to some delay while waiting for a moderator -to whitelist your address. +to allow your address. The larger your contribution is, or if you plan on becoming a long-term contributor, then the more important the rest of this page becomes. @@ -229,6 +222,19 @@ bisection doesn't land on a known-broken state. Submitting your Patches ----------------------- +The QEMU project uses a public email based workflow for reviewing and +merging patches. As a result all contributions to QEMU must be **sent +as patches** to the qemu-devel `mailing list +`__. Patch +contributions should not be posted on the bug tracker, posted on +forums, or externally hosted and linked to. (We have other mailing +lists too, but all patches must go to qemu-devel, possibly with a Cc: +to another list.) ``git send-email`` (`step-by-step setup guide +`__ and `hints and tips +`__) +works best for delivering the patch without mangling it, but +attachments can be used as a last resort on a first-time submission. + .. _if_you_cannot_send_patch_emails: If you cannot send patch emails @@ -314,10 +320,12 @@ git repository to fetch the original commit. Patch emails must include a ``Signed-off-by:`` line ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For more information see `SubmittingPatches 1.12 -`__. -This is vital or we will not be able to apply your patch! Please use -your real name to sign a patch (not an alias or acronym). +Your patches **must** include a Signed-off-by: line. This is a hard +requirement because it's how you say "I'm legally okay to contribute +this and happy for it to go into QEMU". The process is modelled after +the `Linux kernel +`__ +policy. If you wrote the patch, make sure your "From:" and "Signed-off-by:" lines use the same spelling. It's okay if you subscribe or contribute to @@ -327,6 +335,11 @@ include a "From:" line in the body of the email (different from your envelope From:) that will give credit to the correct author; but again, that author's Signed-off-by: line is mandatory, with the same spelling. +There are various tooling options for automatically adding these tags +include using ``git commit -s`` or ``git format-patch -s``. For more +information see `SubmittingPatches 1.12 +`__. + .. _include_a_meaningful_cover_letter: Include a meaningful cover letter @@ -397,9 +410,19 @@ Participating in Code Review ---------------------------- All patches submitted to the QEMU project go through a code review -process before they are accepted. Some areas of code that are well -maintained may review patches quickly, lesser-loved areas of code may -have a longer delay. +process before they are accepted. This will often mean a series will +go through a number of iterations before being picked up by +:ref:`maintainers`. You therefore should be prepared to +read replies to your messages and be willing to act on them. + +Maintainers are often willing to manually fix up first-time +contributions, since there is a learning curve involved in making an +ideal patch submission. However for the best results you should +proactively respond to suggestions with changes or justifications for +your current approach. + +Some areas of code that are well maintained may review patches +quickly, lesser-loved areas of code may have a longer delay. .. _stay_around_to_fix_problems_raised_in_code_review: @@ -411,14 +434,20 @@ developers will identify bugs, or suggest a cleaner approach, or even just point out code style issues or commit message typos. You'll need to respond to these, and then send a second version of your patches with the issues fixed. This takes a little time and effort on your part, but -if you don't do it then your changes will never get into QEMU. It's also -just polite -- it is quite disheartening for a developer to spend time -reviewing your code and suggesting improvements, only to find that -you're not going to do anything further and it was all wasted effort. +if you don't do it then your changes will never get into QEMU. + +Remember that a maintainer is under no obligation to take your +patches. If someone has spent the time reviewing your code and +suggesting improvements and you simply re-post without either +addressing the comment directly or providing additional justification +for the change then it becomes wasted effort. You cannot demand others +merge and then fix up your code after the fact. When replying to comments on your patches **reply to all and not just the sender** -- keeping discussion on the mailing list means everybody -can follow it. +can follow it. Remember the spirit of the :ref:`code_of_conduct` and +keep discussions respectful and collaborative and avoid making +personal comments. .. _pay_attention_to_review_comments: diff --git a/docs/devel/submitting-a-pull-request.rst b/docs/devel/submitting-a-pull-request.rst index c9d1e8afd9..a4cd7ebbb6 100644 --- a/docs/devel/submitting-a-pull-request.rst +++ b/docs/devel/submitting-a-pull-request.rst @@ -53,14 +53,10 @@ series) and that "make check" passes before sending out the pull request. As a submaintainer you're one of QEMU's lines of defense against bad code, so double check the details. -**All pull requests must be signed**. If your key is not already signed -by members of the QEMU community, you should make arrangements to attend -a `KeySigningParty `__ (for -example at KVM Forum) or make alternative arrangements to have your key -signed by an attendee. Key signing requires meeting another community -member \*in person\* so please make appropriate arrangements. By -"signed" here we mean that the pullreq email should quote a tag which is -a GPG-signed tag (as created with 'gpg tag -s ...'). +**All pull requests must be signed**. By "signed" here we mean that +the pullreq email should quote a tag which is a GPG-signed tag (as +created with 'gpg tag -s ...'). See :ref:`maintainer_keys` for +details. **Pull requests not for master should say "not for master" and have "PULL SUBSYSTEM whatever" in the subject tag**. If your pull request is diff --git a/docs/devel/tcg-icount.rst b/docs/devel/tcg-icount.rst index 50c8e8dabc..7df883446a 100644 --- a/docs/devel/tcg-icount.rst +++ b/docs/devel/tcg-icount.rst @@ -62,12 +62,6 @@ To deal with this case, when an I/O access is made we: - re-compile a single [1]_ instruction block for the current PC - exit the cpu loop and execute the re-compiled block -The new block is created with the CF_LAST_IO compile flag which -ensures the final instruction translation starts with a call to -gen_io_start() so we don't enter a perpetual loop constantly -recompiling a single instruction block. For translators using the -common translator_loop this is done automatically. - .. [1] sometimes two instructions if dealing with delay slots Other I/O operations diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst new file mode 100644 index 0000000000..d46b625e0e --- /dev/null +++ b/docs/devel/tcg-ops.rst @@ -0,0 +1,979 @@ +.. _tcg-ops-ref: + +******************************* +TCG Intermediate Representation +******************************* + +Introduction +============ + +TCG (Tiny Code Generator) began as a generic backend for a C compiler. +It was simplified to be used in QEMU. It also has its roots in the +QOP code generator written by Paul Brook. + +Definitions +=========== + +The TCG *target* is the architecture for which we generate the code. +It is of course not the same as the "target" of QEMU which is the +emulated architecture. As TCG started as a generic C backend used +for cross compiling, the assumption was that TCG target might be +different from the host, although this is never the case for QEMU. + +In this document, we use *guest* to specify what architecture we are +emulating; *target* always means the TCG target, the machine on which +we are running QEMU. + +An operation with *undefined behavior* may result in a crash. + +An operation with *unspecified behavior* shall not crash. However, +the result may be one of several possibilities so may be considered +an *undefined result*. + +Basic Blocks +============ + +A TCG *basic block* is a single entry, multiple exit region which +corresponds to a list of instructions terminated by a label, or +any branch instruction. + +A TCG *extended basic block* is a single entry, multiple exit region +which corresponds to a list of instructions terminated by a label or +an unconditional branch. Specifically, an extended basic block is +a sequence of basic blocks connected by the fall-through paths of +zero or more conditional branch instructions. + +Operations +========== + +TCG instructions or *ops* operate on TCG *variables*, both of which +are strongly typed. Each instruction has a fixed number of output +variable operands, input variable operands and constant operands. +Vector instructions have a field specifying the element size within +the vector. The notable exception is the call instruction which has +a variable number of outputs and inputs. + +In the textual form, output operands usually come first, followed by +input operands, followed by constant operands. The output type is +included in the instruction name. Constants are prefixed with a '$'. + +.. code-block:: none + + add_i32 t0, t1, t2 /* (t0 <- t1 + t2) */ + +Variables +========= + +* ``TEMP_FIXED`` + + There is one TCG *fixed global* variable, ``cpu_env``, which is + live in all translation blocks, and holds a pointer to ``CPUArchState``. + This variable is held in a host cpu register at all times in all + translation blocks. + +* ``TEMP_GLOBAL`` + + A TCG *global* is a variable which is live in all translation blocks, + and corresponds to memory location that is within ``CPUArchState``. + These may be specified as an offset from ``cpu_env``, in which case + they are called *direct globals*, or may be specified as an offset + from a direct global, in which case they are called *indirect globals*. + Even indirect globals should still reference memory within + ``CPUArchState``. All TCG globals are defined during + ``TCGCPUOps.initialize``, before any translation blocks are generated. + +* ``TEMP_CONST`` + + A TCG *constant* is a variable which is live throughout the entire + translation block, and contains a constant value. These variables + are allocated on demand during translation and are hashed so that + there is exactly one variable holding a given value. + +* ``TEMP_TB`` + + A TCG *translation block temporary* is a variable which is live + throughout the entire translation block, but dies on any exit. + These temporaries are allocated explicitly during translation. + +* ``TEMP_EBB`` + + A TCG *extended basic block temporary* is a variable which is live + throughout an extended basic block, but dies on any exit. + These temporaries are allocated explicitly during translation. + +Types +===== + +* ``TCG_TYPE_I32`` + + A 32-bit integer. + +* ``TCG_TYPE_I64`` + + A 64-bit integer. For 32-bit hosts, such variables are split into a pair + of variables with ``type=TCG_TYPE_I32`` and ``base_type=TCG_TYPE_I64``. + The ``temp_subindex`` for each indicates where it falls within the + host-endian representation. + +* ``TCG_TYPE_PTR`` + + An alias for ``TCG_TYPE_I32`` or ``TCG_TYPE_I64``, depending on the size + of a pointer for the host. + +* ``TCG_TYPE_REG`` + + An alias for ``TCG_TYPE_I32`` or ``TCG_TYPE_I64``, depending on the size + of the integer registers for the host. This may be larger + than ``TCG_TYPE_PTR`` depending on the host ABI. + +* ``TCG_TYPE_I128`` + + A 128-bit integer. For all hosts, such variables are split into a number + of variables with ``type=TCG_TYPE_REG`` and ``base_type=TCG_TYPE_I128``. + The ``temp_subindex`` for each indicates where it falls within the + host-endian representation. + +* ``TCG_TYPE_V64`` + + A 64-bit vector. This type is valid only if the TCG target + sets ``TCG_TARGET_HAS_v64``. + +* ``TCG_TYPE_V128`` + + A 128-bit vector. This type is valid only if the TCG target + sets ``TCG_TARGET_HAS_v128``. + +* ``TCG_TYPE_V256`` + + A 256-bit vector. This type is valid only if the TCG target + sets ``TCG_TARGET_HAS_v256``. + +Helpers +======= + +Helpers are registered in a guest-specific ``helper.h``, +which is processed to generate ``tcg_gen_helper_*`` functions. +With these functions it is possible to call a function taking +i32, i64, i128 or pointer types. + +By default, before calling a helper, all globals are stored at their +canonical location. By default, the helper is allowed to modify the +CPU state (including the state represented by tcg globals) +or may raise an exception. This default can be overridden using the +following function modifiers: + +* ``TCG_CALL_NO_WRITE_GLOBALS`` + + The helper does not modify any globals, but may read them. + Globals will be saved to their canonical location before calling helpers, + but need not be reloaded afterwards. + +* ``TCG_CALL_NO_READ_GLOBALS`` + + The helper does not read globals, either directly or via an exception. + They will not be saved to their canonical locations before calling + the helper. This implies ``TCG_CALL_NO_WRITE_GLOBALS``. + +* ``TCG_CALL_NO_SIDE_EFFECTS`` + + The call to the helper function may be removed if the return value is + not used. This means that it may not modify any CPU state nor may it + raise an exception. + +Code Optimizations +================== + +When generating instructions, you can count on at least the following +optimizations: + +- Single instructions are simplified, e.g. + + .. code-block:: none + + and_i32 t0, t0, $0xffffffff + + is suppressed. + +- A liveness analysis is done at the basic block level. The + information is used to suppress moves from a dead variable to + another one. It is also used to remove instructions which compute + dead results. The later is especially useful for condition code + optimization in QEMU. + + In the following example: + + .. code-block:: none + + add_i32 t0, t1, t2 + add_i32 t0, t0, $1 + mov_i32 t0, $1 + + only the last instruction is kept. + + +Instruction Reference +===================== + +Function call +------------- + +.. list-table:: + + * - call ** ** ptr + + - | call function 'ptr' (pointer type) + | + | ** optional 32 bit or 64 bit return value + | ** optional 32 bit or 64 bit parameters + +Jumps/Labels +------------ + +.. list-table:: + + * - set_label $label + + - | Define label 'label' at the current program point. + + * - br $label + + - | Jump to label. + + * - brcond_i32/i64 *t0*, *t1*, *cond*, *label* + + - | Conditional jump if *t0* *cond* *t1* is true. *cond* can be: + | + | ``TCG_COND_EQ`` + | ``TCG_COND_NE`` + | ``TCG_COND_LT /* signed */`` + | ``TCG_COND_GE /* signed */`` + | ``TCG_COND_LE /* signed */`` + | ``TCG_COND_GT /* signed */`` + | ``TCG_COND_LTU /* unsigned */`` + | ``TCG_COND_GEU /* unsigned */`` + | ``TCG_COND_LEU /* unsigned */`` + | ``TCG_COND_GTU /* unsigned */`` + | ``TCG_COND_TSTEQ /* t1 & t2 == 0 */`` + | ``TCG_COND_TSTNE /* t1 & t2 != 0 */`` + +Arithmetic +---------- + +.. list-table:: + + * - add_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* + *t2* + + * - sub_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* - *t2* + + * - neg_i32/i64 *t0*, *t1* + + - | *t0* = -*t1* (two's complement) + + * - mul_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* * *t2* + + * - div_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* / *t2* (signed) + | Undefined behavior if division by zero or overflow. + + * - divu_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* / *t2* (unsigned) + | Undefined behavior if division by zero. + + * - rem_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* % *t2* (signed) + | Undefined behavior if division by zero or overflow. + + * - remu_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* % *t2* (unsigned) + | Undefined behavior if division by zero. + + +Logical +------- + +.. list-table:: + + * - and_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* & *t2* + + * - or_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* | *t2* + + * - xor_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* ^ *t2* + + * - not_i32/i64 *t0*, *t1* + + - | *t0* = ~\ *t1* + + * - andc_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* & ~\ *t2* + + * - eqv_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = ~(*t1* ^ *t2*), or equivalently, *t0* = *t1* ^ ~\ *t2* + + * - nand_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = ~(*t1* & *t2*) + + * - nor_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = ~(*t1* | *t2*) + + * - orc_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* | ~\ *t2* + + * - clz_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* ? clz(*t1*) : *t2* + + * - ctz_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* ? ctz(*t1*) : *t2* + + * - ctpop_i32/i64 *t0*, *t1* + + - | *t0* = number of bits set in *t1* + | + | With *ctpop* short for "count population", matching + | the function name used in ``include/qemu/host-utils.h``. + + +Shifts/Rotates +-------------- + +.. list-table:: + + * - shl_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* << *t2* + | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + + * - shr_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* >> *t2* (unsigned) + | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + + * - sar_i32/i64 *t0*, *t1*, *t2* + + - | *t0* = *t1* >> *t2* (signed) + | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + + * - rotl_i32/i64 *t0*, *t1*, *t2* + + - | Rotation of *t2* bits to the left + | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + + * - rotr_i32/i64 *t0*, *t1*, *t2* + + - | Rotation of *t2* bits to the right. + | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + + +Misc +---- + +.. list-table:: + + * - mov_i32/i64 *t0*, *t1* + + - | *t0* = *t1* + | Move *t1* to *t0* (both operands must have the same type). + + * - ext8s_i32/i64 *t0*, *t1* + + ext8u_i32/i64 *t0*, *t1* + + ext16s_i32/i64 *t0*, *t1* + + ext16u_i32/i64 *t0*, *t1* + + ext32s_i64 *t0*, *t1* + + ext32u_i64 *t0*, *t1* + + - | 8, 16 or 32 bit sign/zero extension (both operands must have the same type) + + * - bswap16_i32/i64 *t0*, *t1*, *flags* + + - | 16 bit byte swap on the low bits of a 32/64 bit input. + | + | If *flags* & ``TCG_BSWAP_IZ``, then *t1* is known to be zero-extended from bit 15. + | If *flags* & ``TCG_BSWAP_OZ``, then *t0* will be zero-extended from bit 15. + | If *flags* & ``TCG_BSWAP_OS``, then *t0* will be sign-extended from bit 15. + | + | If neither ``TCG_BSWAP_OZ`` nor ``TCG_BSWAP_OS`` are set, then the bits of *t0* above bit 15 may contain any value. + + * - bswap32_i64 *t0*, *t1*, *flags* + + - | 32 bit byte swap on a 64-bit value. The flags are the same as for bswap16, + except they apply from bit 31 instead of bit 15. + + * - bswap32_i32 *t0*, *t1*, *flags* + + bswap64_i64 *t0*, *t1*, *flags* + + - | 32/64 bit byte swap. The flags are ignored, but still present + for consistency with the other bswap opcodes. + + * - discard_i32/i64 *t0* + + - | Indicate that the value of *t0* won't be used later. It is useful to + force dead code elimination. + + * - deposit_i32/i64 *dest*, *t1*, *t2*, *pos*, *len* + + - | Deposit *t2* as a bitfield into *t1*, placing the result in *dest*. + | + | The bitfield is described by *pos*/*len*, which are immediate values: + | + | *len* - the length of the bitfield + | *pos* - the position of the first bit, counting from the LSB + | + | For example, "deposit_i32 dest, t1, t2, 8, 4" indicates a 4-bit field + at bit 8. This operation would be equivalent to + | + | *dest* = (*t1* & ~0x0f00) | ((*t2* << 8) & 0x0f00) + + * - extract_i32/i64 *dest*, *t1*, *pos*, *len* + + sextract_i32/i64 *dest*, *t1*, *pos*, *len* + + - | Extract a bitfield from *t1*, placing the result in *dest*. + | + | The bitfield is described by *pos*/*len*, which are immediate values, + as above for deposit. For extract_*, the result will be extended + to the left with zeros; for sextract_*, the result will be extended + to the left with copies of the bitfield sign bit at *pos* + *len* - 1. + | + | For example, "sextract_i32 dest, t1, 8, 4" indicates a 4-bit field + at bit 8. This operation would be equivalent to + | + | *dest* = (*t1* << 20) >> 28 + | + | (using an arithmetic right shift). + + * - extract2_i32/i64 *dest*, *t1*, *t2*, *pos* + + - | For N = {32,64}, extract an N-bit quantity from the concatenation + of *t2*:*t1*, beginning at *pos*. The tcg_gen_extract2_{i32,i64} expander + accepts 0 <= *pos* <= N as inputs. The backend code generator will + not see either 0 or N as inputs for these opcodes. + + * - extrl_i64_i32 *t0*, *t1* + + - | For 64-bit hosts only, extract the low 32-bits of input *t1* and place it + into 32-bit output *t0*. Depending on the host, this may be a simple move, + or may require additional canonicalization. + + * - extrh_i64_i32 *t0*, *t1* + + - | For 64-bit hosts only, extract the high 32-bits of input *t1* and place it + into 32-bit output *t0*. Depending on the host, this may be a simple shift, + or may require additional canonicalization. + + +Conditional moves +----------------- + +.. list-table:: + + * - setcond_i32/i64 *dest*, *t1*, *t2*, *cond* + + - | *dest* = (*t1* *cond* *t2*) + | + | Set *dest* to 1 if (*t1* *cond* *t2*) is true, otherwise set to 0. + + * - negsetcond_i32/i64 *dest*, *t1*, *t2*, *cond* + + - | *dest* = -(*t1* *cond* *t2*) + | + | Set *dest* to -1 if (*t1* *cond* *t2*) is true, otherwise set to 0. + + * - movcond_i32/i64 *dest*, *c1*, *c2*, *v1*, *v2*, *cond* + + - | *dest* = (*c1* *cond* *c2* ? *v1* : *v2*) + | + | Set *dest* to *v1* if (*c1* *cond* *c2*) is true, otherwise set to *v2*. + + +Type conversions +---------------- + +.. list-table:: + + * - ext_i32_i64 *t0*, *t1* + + - | Convert *t1* (32 bit) to *t0* (64 bit) and does sign extension + + * - extu_i32_i64 *t0*, *t1* + + - | Convert *t1* (32 bit) to *t0* (64 bit) and does zero extension + + * - trunc_i64_i32 *t0*, *t1* + + - | Truncate *t1* (64 bit) to *t0* (32 bit) + + * - concat_i32_i64 *t0*, *t1*, *t2* + + - | Construct *t0* (64-bit) taking the low half from *t1* (32 bit) and the high half + from *t2* (32 bit). + + * - concat32_i64 *t0*, *t1*, *t2* + + - | Construct *t0* (64-bit) taking the low half from *t1* (64 bit) and the high half + from *t2* (64 bit). + + +Load/Store +---------- + +.. list-table:: + + * - ld_i32/i64 *t0*, *t1*, *offset* + + ld8s_i32/i64 *t0*, *t1*, *offset* + + ld8u_i32/i64 *t0*, *t1*, *offset* + + ld16s_i32/i64 *t0*, *t1*, *offset* + + ld16u_i32/i64 *t0*, *t1*, *offset* + + ld32s_i64 t0, *t1*, *offset* + + ld32u_i64 t0, *t1*, *offset* + + - | *t0* = read(*t1* + *offset*) + | + | Load 8, 16, 32 or 64 bits with or without sign extension from host memory. + *offset* must be a constant. + + * - st_i32/i64 *t0*, *t1*, *offset* + + st8_i32/i64 *t0*, *t1*, *offset* + + st16_i32/i64 *t0*, *t1*, *offset* + + st32_i64 *t0*, *t1*, *offset* + + - | write(*t0*, *t1* + *offset*) + | + | Write 8, 16, 32 or 64 bits to host memory. + +All this opcodes assume that the pointed host memory doesn't correspond +to a global. In the latter case the behaviour is unpredictable. + + +Multiword arithmetic support +---------------------------- + +.. list-table:: + + * - add2_i32/i64 *t0_low*, *t0_high*, *t1_low*, *t1_high*, *t2_low*, *t2_high* + + sub2_i32/i64 *t0_low*, *t0_high*, *t1_low*, *t1_high*, *t2_low*, *t2_high* + + - | Similar to add/sub, except that the double-word inputs *t1* and *t2* are + formed from two single-word arguments, and the double-word output *t0* + is returned in two single-word outputs. + + * - mulu2_i32/i64 *t0_low*, *t0_high*, *t1*, *t2* + + - | Similar to mul, except two unsigned inputs *t1* and *t2* yielding the full + double-word product *t0*. The latter is returned in two single-word outputs. + + * - muls2_i32/i64 *t0_low*, *t0_high*, *t1*, *t2* + + - | Similar to mulu2, except the two inputs *t1* and *t2* are signed. + + * - mulsh_i32/i64 *t0*, *t1*, *t2* + + muluh_i32/i64 *t0*, *t1*, *t2* + + - | Provide the high part of a signed or unsigned multiply, respectively. + | + | If mulu2/muls2 are not provided by the backend, the tcg-op generator + can obtain the same results by emitting a pair of opcodes, mul + muluh/mulsh. + + +Memory Barrier support +---------------------- + +.. list-table:: + + * - mb *<$arg>* + + - | Generate a target memory barrier instruction to ensure memory ordering + as being enforced by a corresponding guest memory barrier instruction. + | + | The ordering enforced by the backend may be stricter than the ordering + required by the guest. It cannot be weaker. This opcode takes a constant + argument which is required to generate the appropriate barrier + instruction. The backend should take care to emit the target barrier + instruction only when necessary i.e., for SMP guests and when MTTCG is + enabled. + | + | The guest translators should generate this opcode for all guest instructions + which have ordering side effects. + | + | Please see :ref:`atomics-ref` for more information on memory barriers. + + +64-bit guest on 32-bit host support +----------------------------------- + +The following opcodes are internal to TCG. Thus they are to be implemented by +32-bit host code generators, but are not to be emitted by guest translators. +They are emitted as needed by inline functions within ``tcg-op.h``. + +.. list-table:: + + * - brcond2_i32 *t0_low*, *t0_high*, *t1_low*, *t1_high*, *cond*, *label* + + - | Similar to brcond, except that the 64-bit values *t0* and *t1* + are formed from two 32-bit arguments. + + * - setcond2_i32 *dest*, *t1_low*, *t1_high*, *t2_low*, *t2_high*, *cond* + + - | Similar to setcond, except that the 64-bit values *t1* and *t2* are + formed from two 32-bit arguments. The result is a 32-bit value. + + +QEMU specific operations +------------------------ + +.. list-table:: + + * - exit_tb *t0* + + - | Exit the current TB and return the value *t0* (word type). + + * - goto_tb *index* + + - | Exit the current TB and jump to the TB index *index* (constant) if the + current TB was linked to this TB. Otherwise execute the next + instructions. Only indices 0 and 1 are valid and tcg_gen_goto_tb may be issued + at most once with each slot index per TB. + + * - lookup_and_goto_ptr *tb_addr* + + - | Look up a TB address *tb_addr* and jump to it if valid. If not valid, + jump to the TCG epilogue to go back to the exec loop. + | + | This operation is optional. If the TCG backend does not implement the + goto_ptr opcode, emitting this op is equivalent to emitting exit_tb(0). + + * - qemu_ld_i32/i64/i128 *t0*, *t1*, *flags*, *memidx* + + qemu_st_i32/i64/i128 *t0*, *t1*, *flags*, *memidx* + + qemu_st8_i32 *t0*, *t1*, *flags*, *memidx* + + - | Load data at the guest address *t1* into *t0*, or store data in *t0* at guest + address *t1*. The _i32/_i64/_i128 size applies to the size of the input/output + register *t0* only. The address *t1* is always sized according to the guest, + and the width of the memory operation is controlled by *flags*. + | + | Both *t0* and *t1* may be split into little-endian ordered pairs of registers + if dealing with 64-bit quantities on a 32-bit host, or 128-bit quantities on + a 64-bit host. + | + | The *memidx* selects the qemu tlb index to use (e.g. user or kernel access). + The flags are the MemOp bits, selecting the sign, width, and endianness + of the memory access. + | + | For a 32-bit host, qemu_ld/st_i64 is guaranteed to only be used with a + 64-bit memory access specified in *flags*. + | + | For qemu_ld/st_i128, these are only supported for a 64-bit host. + | + | For i386, qemu_st8_i32 is exactly like qemu_st_i32, except the size of + the memory operation is known to be 8-bit. This allows the backend to + provide a different set of register constraints. + + +Host vector operations +---------------------- + +All of the vector ops have two parameters, ``TCGOP_VECL`` & ``TCGOP_VECE``. +The former specifies the length of the vector in log2 64-bit units; the +latter specifies the length of the element (if applicable) in log2 8-bit units. +E.g. VECL = 1 -> 64 << 1 -> v128, and VECE = 2 -> 1 << 2 -> i32. + +.. list-table:: + + * - mov_vec *v0*, *v1* + + ld_vec *v0*, *t1* + + st_vec *v0*, *t1* + + - | Move, load and store. + + * - dup_vec *v0*, *r1* + + - | Duplicate the low N bits of *r1* into VECL/VECE copies across *v0*. + + * - dupi_vec *v0*, *c* + + - | Similarly, for a constant. + | Smaller values will be replicated to host register size by the expanders. + + * - dup2_vec *v0*, *r1*, *r2* + + - | Duplicate *r2*:*r1* into VECL/64 copies across *v0*. This opcode is + only present for 32-bit hosts. + + * - add_vec *v0*, *v1*, *v2* + + - | *v0* = *v1* + *v2*, in elements across the vector. + + * - sub_vec *v0*, *v1*, *v2* + + - | Similarly, *v0* = *v1* - *v2*. + + * - mul_vec *v0*, *v1*, *v2* + + - | Similarly, *v0* = *v1* * *v2*. + + * - neg_vec *v0*, *v1* + + - | Similarly, *v0* = -*v1*. + + * - abs_vec *v0*, *v1* + + - | Similarly, *v0* = *v1* < 0 ? -*v1* : *v1*, in elements across the vector. + + * - smin_vec *v0*, *v1*, *v2* + + umin_vec *v0*, *v1*, *v2* + + - | Similarly, *v0* = MIN(*v1*, *v2*), for signed and unsigned element types. + + * - smax_vec *v0*, *v1*, *v2* + + umax_vec *v0*, *v1*, *v2* + + - | Similarly, *v0* = MAX(*v1*, *v2*), for signed and unsigned element types. + + * - ssadd_vec *v0*, *v1*, *v2* + + sssub_vec *v0*, *v1*, *v2* + + usadd_vec *v0*, *v1*, *v2* + + ussub_vec *v0*, *v1*, *v2* + + - | Signed and unsigned saturating addition and subtraction. + | + | If the true result is not representable within the element type, the + element is set to the minimum or maximum value for the type. + + * - and_vec *v0*, *v1*, *v2* + + or_vec *v0*, *v1*, *v2* + + xor_vec *v0*, *v1*, *v2* + + andc_vec *v0*, *v1*, *v2* + + orc_vec *v0*, *v1*, *v2* + + not_vec *v0*, *v1* + + - | Similarly, logical operations with and without complement. + | + | Note that VECE is unused. + + * - shli_vec *v0*, *v1*, *i2* + + shls_vec *v0*, *v1*, *s2* + + - | Shift all elements from v1 by a scalar *i2*/*s2*. I.e. + + .. code-block:: c + + for (i = 0; i < VECL/VECE; ++i) { + v0[i] = v1[i] << s2; + } + + * - shri_vec *v0*, *v1*, *i2* + + sari_vec *v0*, *v1*, *i2* + + rotli_vec *v0*, *v1*, *i2* + + shrs_vec *v0*, *v1*, *s2* + + sars_vec *v0*, *v1*, *s2* + + - | Similarly for logical and arithmetic right shift, and left rotate. + + * - shlv_vec *v0*, *v1*, *v2* + + - | Shift elements from *v1* by elements from *v2*. I.e. + + .. code-block:: c + + for (i = 0; i < VECL/VECE; ++i) { + v0[i] = v1[i] << v2[i]; + } + + * - shrv_vec *v0*, *v1*, *v2* + + sarv_vec *v0*, *v1*, *v2* + + rotlv_vec *v0*, *v1*, *v2* + + rotrv_vec *v0*, *v1*, *v2* + + - | Similarly for logical and arithmetic right shift, and rotates. + + * - cmp_vec *v0*, *v1*, *v2*, *cond* + + - | Compare vectors by element, storing -1 for true and 0 for false. + + * - bitsel_vec *v0*, *v1*, *v2*, *v3* + + - | Bitwise select, *v0* = (*v2* & *v1*) | (*v3* & ~\ *v1*), across the entire vector. + + * - cmpsel_vec *v0*, *c1*, *c2*, *v3*, *v4*, *cond* + + - | Select elements based on comparison results: + + .. code-block:: c + + for (i = 0; i < n; ++i) { + v0[i] = (c1[i] cond c2[i]) ? v3[i] : v4[i]. + } + +**Note 1**: Some shortcuts are defined when the last operand is known to be +a constant (e.g. addi for add, movi for mov). + +**Note 2**: When using TCG, the opcodes must never be generated directly +as some of them may not be available as "real" opcodes. Always use the +function tcg_gen_xxx(args). + + +Backend +======= + +``tcg-target.h`` contains the target specific definitions. ``tcg-target.c.inc`` +contains the target specific code; it is #included by ``tcg/tcg.c``, rather +than being a standalone C file. + +Assumptions +----------- + +The target word size (``TCG_TARGET_REG_BITS``) is expected to be 32 bit or +64 bit. It is expected that the pointer has the same size as the word. + +On a 32 bit target, all 64 bit operations are converted to 32 bits. A +few specific operations must be implemented to allow it (see add2_i32, +sub2_i32, brcond2_i32). + +On a 64 bit target, the values are transferred between 32 and 64-bit +registers using the following ops: + +- extrl_i64_i32 +- extrh_i64_i32 +- ext_i32_i64 +- extu_i32_i64 + +They ensure that the values are correctly truncated or extended when +moved from a 32-bit to a 64-bit register or vice-versa. Note that the +extrl_i64_i32 and extrh_i64_i32 are optional ops. It is not necessary +to implement them if all the following conditions are met: + +- 64-bit registers can hold 32-bit values +- 32-bit values in a 64-bit register do not need to stay zero or + sign extended +- all 32-bit TCG ops ignore the high part of 64-bit registers + +Floating point operations are not supported in this version. A +previous incarnation of the code generator had full support of them, +but it is better to concentrate on integer operations first. + +Constraints +---------------- + +GCC like constraints are used to define the constraints of every +instruction. Memory constraints are not supported in this +version. Aliases are specified in the input operands as for GCC. + +The same register may be used for both an input and an output, even when +they are not explicitly aliased. If an op expands to multiple target +instructions then care must be taken to avoid clobbering input values. +GCC style "early clobber" outputs are supported, with '``&``'. + +A target can define specific register or constant constraints. If an +operation uses a constant input constraint which does not allow all +constants, it must also accept registers in order to have a fallback. +The constraint '``i``' is defined generically to accept any constant. +The constraint '``r``' is not defined generically, but is consistently +used by each backend to indicate all registers. + +The movi_i32 and movi_i64 operations must accept any constants. + +The mov_i32 and mov_i64 operations must accept any registers of the +same type. + +The ld/st/sti instructions must accept signed 32 bit constant offsets. +This can be implemented by reserving a specific register in which to +compute the address if the offset is too big. + +The ld/st instructions must accept any destination (ld) or source (st) +register. + +The sti instruction may fail if it cannot store the given constant. + +Function call assumptions +------------------------- + +- The only supported types for parameters and return value are: 32 and + 64 bit integers and pointer. +- The stack grows downwards. +- The first N parameters are passed in registers. +- The next parameters are passed on the stack by storing them as words. +- Some registers are clobbered during the call. +- The function can return 0 or 1 value in registers. On a 32 bit + target, functions must be able to return 2 values in registers for + 64 bit return type. + + +Recommended coding rules for best performance +============================================= + +- Use globals to represent the parts of the QEMU CPU state which are + often modified, e.g. the integer registers and the condition + codes. TCG will be able to use host registers to store them. + +- Don't hesitate to use helpers for complicated or seldom used guest + instructions. There is little performance advantage in using TCG to + implement guest instructions taking more than about twenty TCG + instructions. Note that this rule of thumb is more applicable to + helpers doing complex logic or arithmetic, where the C compiler has + scope to do a good job of optimisation; it is less relevant where + the instruction is mostly doing loads and stores, and in those cases + inline TCG may still be faster for longer sequences. + +- Use the 'discard' instruction if you know that TCG won't be able to + prove that a given global is "dead" at a given program point. The + x86 guest uses it to improve the condition codes optimisation. diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index a7cc44aa20..9cc09d8c3d 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -3,6 +3,8 @@ Copyright (c) 2019, Linaro Limited Written by Emilio Cota and Alex Bennée +.. _TCG Plugins: + QEMU TCG Plugins ================ @@ -110,10 +112,54 @@ 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. -API -~~~ +However the following assumptions can be made: -.. kernel-doc:: include/qemu/qemu-plugin.h +Translation Blocks +++++++++++++++++++ + +All code will go through a translation phase although not all +translations will be necessarily be executed. You need to instrument +actual executions to track what is happening. + +It is quite normal to see the same address translated multiple times. +If you want to track the code in system emulation you should examine +the underlying physical address (``qemu_plugin_insn_haddr``) to take +into account the effects of virtual memory although if the system does +paging this will change too. + +Not all instructions in a block will always execute so if its +important to track individual instruction execution you need to +instrument them directly. However asynchronous interrupts will not +change control flow mid-block. + +Instructions +++++++++++++ + +Instruction instrumentation runs before the instruction executes. You +can be can be sure the instruction will be dispatched, but you can't +be sure it will complete. Generally this will be because of a +synchronous exception (e.g. SIGILL) triggered by the instruction +attempting to execute. If you want to be sure you will need to +instrument the next instruction as well. See the ``execlog.c`` plugin +for examples of how to track this and finalise details after execution. + +Memory Accesses ++++++++++++++++ + +Memory callbacks are called after a successful load or store. +Unsuccessful operations (i.e. faults) will not be visible to memory +instrumentation although the execution side effects can be observed +(e.g. entering a exception handler). + +System Idle and Resume States ++++++++++++++++++++++++++++++ + +The ``qemu_plugin_register_vcpu_idle_cb`` and +``qemu_plugin_register_vcpu_resume_cb`` functions can be used to track +when CPUs go into and return from sleep states when waiting for +external I/O. Be aware though that these may occur less frequently +than in real HW due to the inefficiencies of emulation giving less +chance for the CPU to idle. Internals --------- @@ -146,16 +192,145 @@ requested. The plugin isn't completely uninstalled until the safe work has executed while all vCPUs are quiescent. Example Plugins ---------------- +=============== There are a number of plugins included with QEMU and you are encouraged to contribute your own plugins plugins upstream. There is a -``contrib/plugins`` directory where they can go. +``contrib/plugins`` directory where they can go. There are also some +basic plugins that are used to test and exercise the API during the +``make check-tcg`` target in ``tests\plugins``. -- tests/plugins +- tests/plugins/empty.c -These are some basic plugins that are used to test and exercise the -API during the ``make check-tcg`` target. +Purely a test plugin for measuring the overhead of the plugins system +itself. Does no instrumentation. + +- tests/plugins/bb.c + +A very basic plugin which will measure execution in course terms as +each basic block is executed. By default the results are shown once +execution finishes:: + + $ qemu-aarch64 -plugin tests/plugin/libbb.so \ + -d plugin ./tests/tcg/aarch64-linux-user/sha1 + SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 + bb's: 2277338, insns: 158483046 + +Behaviour can be tweaked with the following arguments: + + * inline=true|false + + Use faster inline addition of a single counter. Not per-cpu and not + thread safe. + + * idle=true|false + + Dump the current execution stats whenever the guest vCPU idles + +- tests/plugins/insn.c + +This is a basic instruction level instrumentation which can count the +number of instructions executed on each core/thread:: + + $ qemu-aarch64 -plugin tests/plugin/libinsn.so \ + -d plugin ./tests/tcg/aarch64-linux-user/threadcount + Created 10 threads + Done + cpu 0 insns: 46765 + cpu 1 insns: 3694 + cpu 2 insns: 3694 + cpu 3 insns: 2994 + cpu 4 insns: 1497 + cpu 5 insns: 1497 + cpu 6 insns: 1497 + cpu 7 insns: 1497 + total insns: 63135 + +Behaviour can be tweaked with the following arguments: + + * inline=true|false + + Use faster inline addition of a single counter. Not per-cpu and not + thread safe. + + * sizes=true|false + + Give a summary of the instruction sizes for the execution + + * match= + + Only instrument instructions matching the string prefix. Will show + some basic stats including how many instructions have executed since + the last execution. For example:: + + $ qemu-aarch64 -plugin tests/plugin/libinsn.so,match=bl \ + -d plugin ./tests/tcg/aarch64-linux-user/sha512-vector + ... + 0x40069c, 'bl #0x4002b0', 10 hits, 1093 match hits, Δ+1257 since last match, 98 avg insns/match + 0x4006ac, 'bl #0x403690', 10 hits, 1094 match hits, Δ+47 since last match, 98 avg insns/match + 0x4037fc, 'bl #0x4002b0', 18 hits, 1095 match hits, Δ+22 since last match, 98 avg insns/match + 0x400720, 'bl #0x403690', 10 hits, 1096 match hits, Δ+58 since last match, 98 avg insns/match + 0x4037fc, 'bl #0x4002b0', 19 hits, 1097 match hits, Δ+22 since last match, 98 avg insns/match + 0x400730, 'bl #0x403690', 10 hits, 1098 match hits, Δ+33 since last match, 98 avg insns/match + 0x4037ac, 'bl #0x4002b0', 12 hits, 1099 match hits, Δ+20 since last match, 98 avg insns/match + ... + +For more detailed execution tracing see the ``execlog`` plugin for +other options. + +- tests/plugins/mem.c + +Basic instruction level memory instrumentation:: + + $ qemu-aarch64 -plugin tests/plugin/libmem.so,inline=true \ + -d plugin ./tests/tcg/aarch64-linux-user/sha1 + SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 + inline mem accesses: 79525013 + +Behaviour can be tweaked with the following arguments: + + * inline=true|false + + Use faster inline addition of a single counter. Not per-cpu and not + thread safe. + + * callback=true|false + + Use callbacks on each memory instrumentation. + + * hwaddr=true|false + + Count IO accesses (only for system emulation) + +- tests/plugins/syscall.c + +A basic syscall tracing plugin. This only works for user-mode. By +default it will give a summary of syscall stats at the end of the +run:: + + $ qemu-aarch64 -plugin tests/plugin/libsyscall \ + -d plugin ./tests/tcg/aarch64-linux-user/threadcount + Created 10 threads + Done + syscall no. calls errors + 226 12 0 + 99 11 11 + 115 11 0 + 222 11 0 + 93 10 0 + 220 10 0 + 233 10 0 + 215 8 0 + 214 4 0 + 134 2 0 + 64 2 0 + 96 1 0 + 94 1 0 + 80 1 0 + 261 1 0 + 78 1 0 + 160 1 0 + 135 1 0 - contrib/plugins/hotblocks.c @@ -172,7 +347,7 @@ slightly faster (but not thread safe) counters. Example:: - ./aarch64-linux-user/qemu-aarch64 \ + $ qemu-aarch64 \ -plugin contrib/plugins/libhotblocks.so -d plugin \ ./tests/tcg/aarch64-linux-user/sha1 SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 @@ -186,7 +361,7 @@ Example:: Similar to hotblocks but this time tracks memory accesses:: - ./aarch64-linux-user/qemu-aarch64 \ + $ qemu-aarch64 \ -plugin contrib/plugins/libhotpages.so -d plugin \ ./tests/tcg/aarch64-linux-user/sha1 SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 @@ -220,7 +395,7 @@ counted. You can give a value to the ``count`` argument for a class of instructions to break it down fully, so for example to see all the system registers accesses:: - ./aarch64-softmmu/qemu-system-aarch64 $(QEMU_ARGS) \ + $ qemu-system-aarch64 $(QEMU_ARGS) \ -append "root=/dev/sda2 systemd.unit=benchmark.service" \ -smp 4 -plugin ./contrib/plugins/libhowvec.so,count=sreg -d plugin @@ -288,10 +463,10 @@ for the plugin is a path for the socket the two instances will communicate over:: - ./sparc-softmmu/qemu-system-sparc -monitor none -parallel none \ + $ qemu-system-sparc -monitor none -parallel none \ -net none -M SS-20 -m 256 -kernel day11/zImage.elf \ -plugin ./contrib/plugins/liblockstep.so,sockpath=lockstep-sparc.sock \ - -d plugin,nochain + -d plugin,nochain which will eventually report:: @@ -346,9 +521,9 @@ The execlog tool traces executed instructions with memory access. It can be used for debugging and security analysis purposes. Please be aware that this will generate a lot of output. -The plugin takes no argument:: +The plugin needs default argument:: - qemu-system-arm $(QEMU_ARGS) \ + $ qemu-system-arm $(QEMU_ARGS) \ -plugin ./contrib/plugins/libexeclog.so -d plugin which will output an execution trace following this structure:: @@ -364,13 +539,36 @@ which will output an execution trace following this structure:: 0, 0xd34, 0xf9c8f000, "bl #0x10c8" 0, 0x10c8, 0xfff96c43, "ldr r3, [r0, #0x44]", load, 0x200000e4, RAM +the output can be filtered to only track certain instructions or +addresses using the ``ifilter`` or ``afilter`` options. You can stack the +arguments if required:: + + $ qemu-system-arm $(QEMU_ARGS) \ + -plugin ./contrib/plugins/libexeclog.so,ifilter=st1w,afilter=0x40001808 -d plugin + +This plugin can also dump registers when they change value. Specify the name of the +registers with multiple ``reg`` options. You can also use glob style matching if you wish:: + + $ qemu-system-arm $(QEMU_ARGS) \ + -plugin ./contrib/plugins/libexeclog.so,reg=\*_el2,reg=sp -d plugin + +Be aware that each additional register to check will slow down +execution quite considerably. You can optimise the number of register +checks done by using the rdisas option. This will only instrument +instructions that mention the registers in question in disassembly. +This is not foolproof as some instructions implicitly change +instructions. You can use the ifilter to catch these cases: + + $ qemu-system-arm $(QEMU_ARGS) \ + -plugin ./contrib/plugins/libexeclog.so,ifilter=msr,ifilter=blr,reg=x30,reg=\*_el1,rdisas=on + - contrib/plugins/cache.c Cache modelling plugin that measures the performance of a given L1 cache configuration, and optionally a unified L2 per-core cache when a given working set is run:: - qemu-x86_64 -plugin ./contrib/plugins/libcache.so \ + $ qemu-x86_64 -plugin ./contrib/plugins/libcache.so \ -d plugin -D cache.log ./tests/tcg/x86_64-linux-user/float_convs will report the following:: @@ -441,3 +639,12 @@ The plugin has a number of arguments, all of them are optional: associativity of the L2 cache, respectively. Setting any of the L2 configuration arguments implies ``l2=on``. (default: N = 2097152 (2MB), B = 64, A = 16) + +Plugin API +========== + +The following API is generated from the inline documentation in +``include/qemu/qemu-plugin.h``. Please ensure any updates to the API +include the full kernel-doc annotations. + +.. kernel-doc:: include/qemu/qemu-plugin.h diff --git a/docs/devel/tcg.rst b/docs/devel/tcg.rst index a65fb7b1c4..2786f2f679 100644 --- a/docs/devel/tcg.rst +++ b/docs/devel/tcg.rst @@ -1,3 +1,5 @@ +.. _tcg_internals: + ==================== Translator Internals ==================== @@ -9,7 +11,7 @@ which make it relatively easily portable and simple while achieving good performances. QEMU's dynamic translation backend is called TCG, for "Tiny Code -Generator". For more information, please take a look at ``tcg/README``. +Generator". For more information, please take a look at :ref:`tcg-ops-ref`. The following sections outline some notable features and implementation details of QEMU's dynamic translator. @@ -188,3 +190,26 @@ memory areas instead calls out to C code for device emulation. Finally, the MMU helps tracking dirty pages and pages pointed to by translation blocks. +Profiling JITted code +--------------------- + +The Linux ``perf`` tool will treat all JITted code as a single block as +unlike the main code it can't use debug information to link individual +program counter samples with larger functions. To overcome this +limitation you can use the ``-perfmap`` or the ``-jitdump`` option to generate +map files. ``-perfmap`` is lightweight and produces only guest-host mappings. +``-jitdump`` additionally saves JITed code and guest debug information (if +available); its output needs to be integrated with the ``perf.data`` file +before the final report can be viewed. + +.. code:: + + perf record $QEMU -perfmap $REMAINING_ARGS + perf report + + perf record -k 1 $QEMU -jitdump $REMAINING_ARGS + DEBUGINFOD_URLS= perf inject -j -i perf.data -o perf.data.jitted + perf report -i perf.data.jitted + +Note that qemu-system generates mappings only for ``-kernel`` files in ELF +format. diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 3f6ebd5073..fa28e3ecb2 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -81,6 +81,36 @@ QTest cases can be executed with make check-qtest +Writing portable test cases +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Both unit tests and qtests can run on POSIX hosts as well as Windows hosts. +Care must be taken when writing portable test cases that can be built and run +successfully on various hosts. The following list shows some best practices: + +* Use portable APIs from glib whenever necessary, e.g.: g_setenv(), + g_mkdtemp(), g_mkdir(). +* Avoid using hardcoded /tmp for temporary file directory. + Use g_get_tmp_dir() instead. +* Bear in mind that Windows has different special string representation for + stdin/stdout/stderr and null devices. For example if your test case uses + "/dev/fd/2" and "/dev/null" on Linux, remember to use "2" and "nul" on + Windows instead. Also IO redirection does not work on Windows, so avoid + using "2>nul" whenever necessary. +* If your test cases uses the blkdebug feature, use relative path to pass + the config and image file paths in the command line as Windows absolute + path contains the delimiter ":" which will confuse the blkdebug parser. +* Use double quotes in your extra QEMU command line in your test cases + instead of single quotes, as Windows does not drop single quotes when + passing the command line to QEMU. +* Windows opens a file in text mode by default, while a POSIX compliant + implementation treats text files and binary files the same. So if your + test cases opens a file to write some data and later wants to compare the + written data with the original one, be sure to pass the letter 'b' as + part of the mode string to fopen(), or O_BINARY flag for the open() call. +* If a certain test case can only run on POSIX or Linux hosts, use a proper + #ifdef in the codes. If the whole test suite cannot run on Windows, disable + the build in the meson.build file. + QAPI schema tests ~~~~~~~~~~~~~~~~~ @@ -297,7 +327,7 @@ build and test QEMU in predefined and widely accessible Linux environments. This makes it possible to expand the test coverage across distros, toolchain flavors and library versions. The support was originally written for Docker although we also support Podman as -an alternative container runtime. Although the many of the target +an alternative container runtime. Although many of the target names and scripts are prefixed with "docker" the system will automatically run on whichever is configured. @@ -375,7 +405,7 @@ locally by using the ``NOCACHE`` build option: .. code:: - make docker-image-debian10 NOCACHE=1 + make docker-image-debian-arm64-cross NOCACHE=1 Images ~~~~~~ @@ -399,49 +429,73 @@ using the ``lcitool`` program provided by the ``libvirt-ci`` project: https://gitlab.com/libvirt/libvirt-ci -In that project, there is a ``mappings.yml`` file defining the distro native -package names for a wide variety of third party projects. This is processed -in combination with a project defined list of build pre-requisites to determine -the list of native packages to install on each distribution. This can be used -to generate dockerfiles, VM package lists and Cirrus CI variables needed to -setup build environments across OS distributions with a consistent set of -packages present. - -When preparing a patch series that adds a new build pre-requisite to QEMU, -updates to various lcitool data files may be required. +``libvirt-ci`` contains an ``lcitool`` program as well as a list of +mappings to distribution package names for a wide variety of third +party projects. ``lcitool`` applies the mappings to a list of build +pre-requisites in ``tests/lcitool/projects/qemu.yml``, determines the +list of native packages to install on each distribution, and uses them +to generate build environments (dockerfiles and Cirrus CI variable files) +that are consistent across OS distribution. Adding new build pre-requisites ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +When preparing a patch series that adds a new build +pre-requisite to QEMU, the prerequisites should to be added to +``tests/lcitool/projects/qemu.yml`` in order to make the dependency +available in the CI build environments. + In the simple case where the pre-requisite is already known to ``libvirt-ci`` -the following steps are needed +the following steps are needed: * Edit ``tests/lcitool/projects/qemu.yml`` and add the pre-requisite * Run ``make lcitool-refresh`` to re-generate all relevant build environment manifests -In some cases ``libvirt-ci`` will not know about the build pre-requisite and -thus some extra preparation steps will be required first +It may be that ``libvirt-ci`` does not know about the new pre-requisite. +If that is the case, some extra preparation steps will be required +first to contribute the mapping to the ``libvirt-ci`` project: * Fork the ``libvirt-ci`` project on gitlab - * Edit the ``mappings.yml`` change to add an entry for the new build - prerequisite, listing its native package name on as many OS distros - as practical. + * Add an entry for the new build prerequisite to + ``lcitool/facts/mappings.yml``, listing its native package name on as + many OS distros as practical. Run ``python -m pytest --regenerate-output`` + and check that the changes are correct. - * Commit the ``mappings.yml`` change and submit a merge request to - the ``libvirt-ci`` project, noting in the description that this - is a new build pre-requisite desired for use with QEMU + * Commit the ``mappings.yml`` change together with the regenerated test + files, and submit a merge request to the ``libvirt-ci`` project. + Please note in the description that this is a new build pre-requisite + desired for use with QEMU. * CI pipeline will run to validate that the changes to ``mappings.yml`` are correct, by attempting to install the newly listed package on all OS distributions supported by ``libvirt-ci``. * Once the merge request is accepted, go back to QEMU and update - the ``libvirt-ci`` submodule to point to a commit that contains - the ``mappings.yml`` update. + the ``tests/lcitool/libvirt-ci`` submodule to point to a commit that + contains the ``mappings.yml`` update. Then add the prerequisite and + run ``make lcitool-refresh``. + + * Please also trigger gitlab container generation pipelines on your change + for as many OS distros as practical to make sure that there are no + obvious breakages when adding the new pre-requisite. Please see + `CI `__ documentation + page on how to trigger gitlab CI pipelines on your change. + + * Please also trigger gitlab container generation pipelines on your change + for as many OS distros as practical to make sure that there are no + obvious breakages when adding the new pre-requisite. Please see + `CI `__ documentation + page on how to trigger gitlab CI pipelines on your change. + +For enterprise distros that default to old, end-of-life versions of the +Python runtime, QEMU uses a separate set of mappings that work with more +recent versions. These can be found in ``tests/lcitool/mappings.yml``. +Modifying this file should not be necessary unless the new pre-requisite +is a Python library or tool. Adding new OS distros @@ -468,18 +522,20 @@ Assuming there is agreement to add a new OS distro then * Fork the ``libvirt-ci`` project on gitlab - * Add metadata under ``guests/lcitool/lcitool/ansible/group_vars/`` - for the new OS distro. There might be code changes required if - the OS distro uses a package format not currently known. The - ``libvirt-ci`` maintainers can advise on this when the issue - is file. + * Add metadata under ``lcitool/facts/targets/`` for the new OS + distro. There might be code changes required if the OS distro + uses a package format not currently known. The ``libvirt-ci`` + maintainers can advise on this when the issue is filed. - * Edit the ``mappings.yml`` change to update all the existing package - entries, providing details of the new OS distro + * Edit the ``lcitool/facts/mappings.yml`` change to add entries for + the new OS, listing the native package names for as many packages + as practical. Run ``python -m pytest --regenerate-output`` and + check that the changes are correct. - * Commit the ``mappings.yml`` change and submit a merge request to - the ``libvirt-ci`` project, noting in the description that this - is a new build pre-requisite desired for use with QEMU + * Commit the changes to ``lcitool/facts`` and the regenerated test + files, and submit a merge request to the ``libvirt-ci`` project. + Please note in the description that this is a new build pre-requisite + desired for use with QEMU * CI pipeline will run to validate that the changes to ``mappings.yml`` are correct, by attempting to install the newly listed package on @@ -508,7 +564,7 @@ When CI tasks, maintainers or yourself report a Docker test failure, follow the below steps to debug it: 1. Locally reproduce the failure with the reported command line. E.g. run - ``make docker-test-mingw@fedora J=8``. + ``make docker-test-mingw@fedora-win64-cross J=8``. 2. Add "V=1" to the command line, try again, to see the verbose output. 3. Further add "DEBUG=1" to the command line. This will pause in a shell prompt in the container right before testing starts. You could either manually @@ -544,13 +600,13 @@ https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual Thread Sanitizer in Docker ~~~~~~~~~~~~~~~~~~~~~~~~~~ -TSan is currently supported in the ubuntu2004 docker. +TSan is currently supported in the ubuntu2204 docker. The test-tsan test will build using TSan and then run make check. .. code:: - make docker-test-tsan@ubuntu2004 + make docker-test-tsan@ubuntu2204 TSan warnings under docker are placed in files located at build/tsan/. @@ -612,11 +668,11 @@ suppressing it. More information on the file format can be found here: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions -tests/tsan/blacklist.tsan - Has TSan warnings we wish to disable +tests/tsan/ignore.tsan - Has TSan warnings we wish to disable at compile time for test or debug. Add flags to configure to enable: -"--extra-cflags=-fsanitize-blacklist=/tests/tsan/blacklist.tsan" +"--extra-cflags=-fsanitize-blacklist=/tests/tsan/ignore.tsan" More information on the file format can be found here under "Blacklist Format": @@ -672,7 +728,7 @@ For example to setup the HPPA ports builds of Debian:: EXECUTABLE=(pwd)/qemu-hppa V=1 The ``DEB_`` variables are substitutions used by -``debian-boostrap.pre`` which is called to do the initial debootstrap +``debian-bootstrap.pre`` which is called to do the initial debootstrap of the rootfs before it is copied into the container. The second stage is run as part of the build. The final image will be tagged as ``qemu/debian-sid-hppa``. @@ -838,9 +894,9 @@ You can run the avocado tests simply by executing: make check-avocado -This involves the automatic creation of Python virtual environment -within the build tree (at ``tests/venv``) which will have all the -right dependencies, and will save tests results also within the +This involves the automatic installation, from PyPI, of all the +necessary avocado-framework dependencies into the QEMU venv within the +build tree (at ``./pyvenv``). Test results are also saved within the build tree (at ``tests/results``). Note: the build environment must be using a Python 3 stack, and have @@ -897,7 +953,7 @@ may be invoked by running: .. code:: - tests/venv/bin/avocado run $OPTION1 $OPTION2 tests/avocado/ + pyvenv/bin/avocado run $OPTION1 $OPTION2 tests/avocado/ Note that if ``make check-avocado`` was not executed before, it is possible to create the Python virtual environment with the dependencies @@ -912,20 +968,20 @@ a test file. To run tests from a single file within the build tree, use: .. code:: - tests/venv/bin/avocado run tests/avocado/$TESTFILE + pyvenv/bin/avocado run tests/avocado/$TESTFILE To run a single test within a test file, use: .. code:: - tests/venv/bin/avocado run tests/avocado/$TESTFILE:$TESTCLASS.$TESTNAME + pyvenv/bin/avocado run tests/avocado/$TESTFILE:$TESTCLASS.$TESTNAME Valid test names are visible in the output from any previous execution of Avocado or ``make check-avocado``, and can also be queried using: .. code:: - tests/venv/bin/avocado list tests/avocado + pyvenv/bin/avocado list tests/avocado Manual Installation ~~~~~~~~~~~~~~~~~~~ @@ -958,9 +1014,9 @@ class. Here's a simple usage example: """ def test_qmp_human_info_version(self): self.vm.launch() - res = self.vm.command('human-monitor-command', - command_line='info version') - self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)') + res = self.vm.cmd('human-monitor-command', + command_line='info version') + self.assertRegex(res, r'^(\d+\.\d+\.\d)') To execute your test, run: @@ -1009,19 +1065,19 @@ and hypothetical example follows: first_machine.launch() second_machine.launch() - first_res = first_machine.command( + first_res = first_machine.cmd( 'human-monitor-command', command_line='info version') - second_res = second_machine.command( + second_res = second_machine.cmd( 'human-monitor-command', command_line='info version') - third_res = self.get_vm(name='third_machine').command( + third_res = self.get_vm(name='third_machine').cmd( 'human-monitor-command', command_line='info version') - self.assertEquals(first_res, second_res, third_res) + self.assertEqual(first_res, second_res, third_res) At test "tear down", ``avocado_qemu.Test`` handles all the QEMUMachines shutdown. @@ -1290,6 +1346,17 @@ the environment. The definition of *large* is a bit arbitrary here, but it usually means an asset which occupies at least 1GB of size on disk when uncompressed. +SPEED +^^^^^ +Tests which have a long runtime will not be run unless ``SPEED=slow`` is +exported on the environment. + +The definition of *long* is a bit arbitrary here, and it depends on the +usefulness of the test too. A unique test is worth spending more time on, +small variations on existing tests perhaps less so. As a rough guide, +a test or set of similar tests which take more than 100 seconds to +complete. + AVOCADO_ALLOW_UNTRUSTED_CODE ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ There are tests which will boot a kernel image or firmware that can be @@ -1315,19 +1382,34 @@ conditions. For example, tests that take longer to execute when QEMU is compiled with debug flags. Therefore, the ``AVOCADO_TIMEOUT_EXPECTED`` variable has been used to determine whether those tests should run or not. -GITLAB_CI -^^^^^^^^^ -A number of tests are flagged to not run on the GitLab CI. Usually because -they proved to the flaky or there are constraints on the CI environment which -would make them fail. If you encounter a similar situation then use that -variable as shown on the code snippet below to skip the test: +QEMU_TEST_FLAKY_TESTS +^^^^^^^^^^^^^^^^^^^^^ +Some tests are not working reliably and thus are disabled by default. +This includes tests that don't run reliably on GitLab's CI which +usually expose real issues that are rarely seen on developer machines +due to the constraints of the CI environment. If you encounter a +similar situation then raise a bug and then mark the test as shown on +the code snippet below: .. code:: - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') + # See https://gitlab.com/qemu-project/qemu/-/issues/nnnn + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') def test(self): do_something() +You can also add ``:avocado: tags=flaky`` to the test meta-data so +only the flaky tests can be run as a group: + +.. code:: + + env QEMU_TEST_FLAKY_TESTS=1 ./pyvenv/bin/avocado \ + run tests/avocado -filter-by-tags=flaky + +Tests should not live in this state forever and should either be fixed +or eventually removed. + + Uninstalling Avocado ~~~~~~~~~~~~~~~~~~~~ @@ -1397,7 +1479,7 @@ TCG test dependencies ~~~~~~~~~~~~~~~~~~~~~ The TCG tests are deliberately very light on dependencies and are -either totally bare with minimal gcc lib support (for softmmu tests) +either totally bare with minimal gcc lib support (for system-mode tests) or just glibc (for linux-user tests). This is because getting a cross compiler to work with additional libraries can be challenging. diff --git a/docs/devel/tracing.rst b/docs/devel/tracing.rst index d288480db1..043bed7fd0 100644 --- a/docs/devel/tracing.rst +++ b/docs/devel/tracing.rst @@ -357,8 +357,7 @@ probes:: scripts/tracetool.py --backends=dtrace --format=stap \ --binary path/to/qemu-binary \ - --target-type system \ - --target-name x86_64 \ + --probe-prefix qemu.system.x86_64 \ --group=all \ trace-events-all \ qemu.stp diff --git a/docs/devel/vfio-iommufd.rst b/docs/devel/vfio-iommufd.rst new file mode 100644 index 0000000000..3d1c11f175 --- /dev/null +++ b/docs/devel/vfio-iommufd.rst @@ -0,0 +1,166 @@ +=============================== +IOMMUFD BACKEND usage with VFIO +=============================== + +(Same meaning for backend/container/BE) + +With the introduction of iommufd, the Linux kernel provides a generic +interface for user space drivers to propagate their DMA mappings to kernel +for assigned devices. While the legacy kernel interface is group-centric, +the new iommufd interface is device-centric, relying on device fd and iommufd. + +To support both interfaces in the QEMU VFIO device, introduce a base container +to abstract the common part of VFIO legacy and iommufd container. So that the +generic VFIO code can use either container. + +The base container implements generic functions such as memory_listener and +address space management whereas the derived container implements callbacks +specific to either legacy or iommufd. Each container has its own way to setup +secure context and dma management interface. The below diagram shows how it +looks like with both containers. + +:: + + VFIO AddressSpace/Memory + +-------+ +----------+ +-----+ +-----+ + | pci | | platform | | ap | | ccw | + +---+---+ +----+-----+ +--+--+ +--+--+ +----------------------+ + | | | | | AddressSpace | + | | | | +------------+---------+ + +---V-----------V-----------V--------V----+ / + | VFIOAddressSpace | <------------+ + | | | MemoryListener + | VFIOContainerBase list | + +-------+----------------------------+----+ + | | + | | + +-------V------+ +--------V----------+ + | iommufd | | vfio legacy | + | container | | container | + +-------+------+ +--------+----------+ + | | + | /dev/iommu | /dev/vfio/vfio + | /dev/vfio/devices/vfioX | /dev/vfio/$group_id + Userspace | | + ============+============================+=========================== + Kernel | device fd | + +---------------+ | group/container fd + | (BIND_IOMMUFD | | (SET_CONTAINER/SET_IOMMU) + | ATTACH_IOAS) | | device fd + | | | + | +-------V------------V-----------------+ + iommufd | | vfio | + (map/unmap | +---------+--------------------+-------+ + ioas_copy) | | | map/unmap + | | | + +------V------+ +-----V------+ +------V--------+ + | iommfd core | | device | | vfio iommu | + +-------------+ +------------+ +---------------+ + +* Secure Context setup + + - iommufd BE: uses device fd and iommufd to setup secure context + (bind_iommufd, attach_ioas) + - vfio legacy BE: uses group fd and container fd to setup secure context + (set_container, set_iommu) + +* Device access + + - iommufd BE: device fd is opened through ``/dev/vfio/devices/vfioX`` + - vfio legacy BE: device fd is retrieved from group fd ioctl + +* DMA Mapping flow + + 1. VFIOAddressSpace receives MemoryRegion add/del via MemoryListener + 2. VFIO populates DMA map/unmap via the container BEs + * iommufd BE: uses iommufd + * vfio legacy BE: uses container fd + +Example configuration +===================== + +Step 1: configure the host device +--------------------------------- + +It's exactly same as the VFIO device with legacy VFIO container. + +Step 2: configure QEMU +---------------------- + +Interactions with the ``/dev/iommu`` are abstracted by a new iommufd +object (compiled in with the ``CONFIG_IOMMUFD`` option). + +Any QEMU device (e.g. VFIO device) wishing to use ``/dev/iommu`` must +be linked with an iommufd object. It gets a new optional property +named iommufd which allows to pass an iommufd object. Take ``vfio-pci`` +device for example: + +.. code-block:: bash + + -object iommufd,id=iommufd0 + -device vfio-pci,host=0000:02:00.0,iommufd=iommufd0 + +Note the ``/dev/iommu`` and VFIO cdev can be externally opened by a +management layer. In such a case the fd is passed, the fd supports a +string naming the fd or a number, for example: + +.. code-block:: bash + + -object iommufd,id=iommufd0,fd=22 + -device vfio-pci,iommufd=iommufd0,fd=23 + +If the ``fd`` property is not passed, the fd is opened by QEMU. + +If no ``iommufd`` object is passed to the ``vfio-pci`` device, iommufd +is not used and the user gets the behavior based on the legacy VFIO +container: + +.. code-block:: bash + + -device vfio-pci,host=0000:02:00.0 + +Supported platform +================== + +Supports x86, ARM and s390x currently. + +Caveats +======= + +Dirty page sync +--------------- + +Dirty page sync with iommufd backend is unsupported yet, live migration is +disabled by default. But it can be force enabled like below, low efficient +though. + +.. code-block:: bash + + -object iommufd,id=iommufd0 + -device vfio-pci,host=0000:02:00.0,iommufd=iommufd0,enable-migration=on + +P2P DMA +------- + +PCI p2p DMA is unsupported as IOMMUFD doesn't support mapping hardware PCI +BAR region yet. Below warning shows for assigned PCI device, it's not a bug. + +.. code-block:: none + + qemu-system-x86_64: warning: IOMMU_IOAS_MAP failed: Bad address, PCI BAR? + qemu-system-x86_64: vfio_container_dma_map(0x560cb6cb1620, 0xe000000021000, 0x3000, 0x7f32ed55c000) = -14 (Bad address) + +FD passing with mdev +-------------------- + +``vfio-pci`` device checks sysfsdev property to decide if backend is a mdev. +If FD passing is used, there is no way to know that and the mdev is treated +like a real PCI device. There is an error as below if user wants to enable +RAM discarding for mdev. + +.. code-block:: none + + qemu-system-x86_64: -device vfio-pci,iommufd=iommufd0,x-balloon-allowed=on,fd=9: vfio VFIO_FD9: x-balloon-allowed only potentially compatible with mdev devices + +``vfio-ap`` and ``vfio-ccw`` devices don't have same issue as their backend +devices are always mdev and RAM discarding is force enabled. diff --git a/docs/devel/vfio-migration.rst b/docs/devel/vfio-migration.rst deleted file mode 100644 index 9ff6163c88..0000000000 --- a/docs/devel/vfio-migration.rst +++ /dev/null @@ -1,150 +0,0 @@ -===================== -VFIO device Migration -===================== - -Migration of virtual machine involves saving the state for each device that -the guest is running on source host and restoring this saved state on the -destination host. This document details how saving and restoring of VFIO -devices is done in QEMU. - -Migration of VFIO devices consists of two phases: the optional pre-copy phase, -and the stop-and-copy phase. The pre-copy phase is iterative and allows to -accommodate VFIO devices that have a large amount of data that needs to be -transferred. The iterative pre-copy phase of migration allows for the guest to -continue whilst the VFIO device state is transferred to the destination, this -helps to reduce the total downtime of the VM. VFIO devices can choose to skip -the pre-copy phase of migration by returning pending_bytes as zero during the -pre-copy phase. - -A detailed description of the UAPI for VFIO device migration can be found in -the comment for the ``vfio_device_migration_info`` structure in the header -file linux-headers/linux/vfio.h. - -VFIO implements the device hooks for the iterative approach as follows: - -* A ``save_setup`` function that sets up the migration region and sets _SAVING - flag in the VFIO device state. - -* A ``load_setup`` function that sets up the migration region on the - destination and sets _RESUMING flag in the VFIO device state. - -* A ``save_live_pending`` function that reads pending_bytes from the vendor - driver, which indicates the amount of data that the vendor driver has yet to - save for the VFIO device. - -* A ``save_live_iterate`` function that reads the VFIO device's data from the - vendor driver through the migration region during iterative phase. - -* A ``save_state`` function to save the device config space if it is present. - -* A ``save_live_complete_precopy`` function that resets _RUNNING flag from the - VFIO device state and iteratively copies the remaining data for the VFIO - device until the vendor driver indicates that no data remains (pending bytes - is zero). - -* A ``load_state`` function that loads the config section and the data - sections that are generated by the save functions above - -* ``cleanup`` functions for both save and load that perform any migration - related cleanup, including unmapping the migration region - - -The VFIO migration code uses a VM state change handler to change the VFIO -device state when the VM state changes from running to not-running, and -vice versa. - -Similarly, a migration state change handler is used to trigger a transition of -the VFIO device state when certain changes of the migration state occur. For -example, the VFIO device state is transitioned back to _RUNNING in case a -migration failed or was canceled. - -System memory dirty pages tracking ----------------------------------- - -A ``log_global_start`` and ``log_global_stop`` memory listener callback informs -the VFIO IOMMU module to start and stop dirty page tracking. A ``log_sync`` -memory listener callback marks those system memory pages as dirty which are -used for DMA by the VFIO device. The dirty pages bitmap is queried per -container. All pages pinned by the vendor driver through external APIs have to -be marked as dirty during migration. When there are CPU writes, CPU dirty page -tracking can identify dirtied pages, but any page pinned by the vendor driver -can also be written by the device. There is currently no device or IOMMU -support for dirty page tracking in hardware. - -By default, dirty pages are tracked when the device is in pre-copy as well as -stop-and-copy phase. So, a page pinned by the vendor driver will be copied to -the destination in both phases. Copying dirty pages in pre-copy phase helps -QEMU to predict if it can achieve its downtime tolerances. If QEMU during -pre-copy phase keeps finding dirty pages continuously, then it understands -that even in stop-and-copy phase, it is likely to find dirty pages and can -predict the downtime accordingly. - -QEMU also provides a per device opt-out option ``pre-copy-dirty-page-tracking`` -which disables querying the dirty bitmap during pre-copy phase. If it is set to -off, all dirty pages will be copied to the destination in stop-and-copy phase -only. - -System memory dirty pages tracking when vIOMMU is enabled ---------------------------------------------------------- - -With vIOMMU, an IO virtual address range can get unmapped while in pre-copy -phase of migration. In that case, the unmap ioctl returns any dirty pages in -that range and QEMU reports corresponding guest physical pages dirty. During -stop-and-copy phase, an IOMMU notifier is used to get a callback for mapped -pages and then dirty pages bitmap is fetched from VFIO IOMMU modules for those -mapped ranges. - -Flow of state changes during Live migration -=========================================== - -Below is the flow of state change during live migration. -The values in the brackets represent the VM state, the migration state, and -the VFIO device state, respectively. - -Live migration save path ------------------------- - -:: - - QEMU normal running state - (RUNNING, _NONE, _RUNNING) - | - migrate_init spawns migration_thread - Migration thread then calls each device's .save_setup() - (RUNNING, _SETUP, _RUNNING|_SAVING) - | - (RUNNING, _ACTIVE, _RUNNING|_SAVING) - If device is active, get pending_bytes by .save_live_pending() - If total pending_bytes >= threshold_size, call .save_live_iterate() - Data of VFIO device for pre-copy phase is copied - Iterate till total pending bytes converge and are less than threshold - | - On migration completion, vCPU stops and calls .save_live_complete_precopy for - each active device. The VFIO device is then transitioned into _SAVING state - (FINISH_MIGRATE, _DEVICE, _SAVING) - | - For the VFIO device, iterate in .save_live_complete_precopy until - pending data is 0 - (FINISH_MIGRATE, _DEVICE, _STOPPED) - | - (FINISH_MIGRATE, _COMPLETED, _STOPPED) - Migraton thread schedules cleanup bottom half and exits - -Live migration resume path --------------------------- - -:: - - Incoming migration calls .load_setup for each device - (RESTORE_VM, _ACTIVE, _STOPPED) - | - For each device, .load_state is called for that device section data - (RESTORE_VM, _ACTIVE, _RESUMING) - | - At the end, .load_cleanup is called for each device and vCPUs are started - (RUNNING, _NONE, _RUNNING) - -Postcopy -======== - -Postcopy migration is currently not supported for VFIO devices. diff --git a/docs/devel/virtio-migration.txt b/docs/devel/virtio-migration.txt deleted file mode 100644 index 98a6b0ffb5..0000000000 --- a/docs/devel/virtio-migration.txt +++ /dev/null @@ -1,108 +0,0 @@ -Virtio devices and migration -============================ - -Copyright 2015 IBM Corp. - -This work is licensed under the terms of the GNU GPL, version 2 or later. See -the COPYING file in the top-level directory. - -Saving and restoring the state of virtio devices is a bit of a twisty maze, -for several reasons: -- state is distributed between several parts: - - virtio core, for common fields like features, number of queues, ... - - virtio transport (pci, ccw, ...), for the different proxy devices and - transport specific state (msix vectors, indicators, ...) - - virtio device (net, blk, ...), for the different device types and their - state (mac address, request queue, ...) -- most fields are saved via the stream interface; subsequently, subsections - have been added to make cross-version migration possible - -This file attempts to document the current procedure and point out some -caveats. - - -Save state procedure -==================== - -virtio core virtio transport virtio device ------------ ---------------- ------------- - - save() function registered - via VMState wrapper on - device class -virtio_save() <---------- - ------> save_config() - - save proxy device - - save transport-specific - device fields -- save common device - fields -- save common virtqueue - fields - ------> save_queue() - - save transport-specific - virtqueue fields - ------> save_device() - - save device-specific - fields -- save subsections - - device endianness, - if changed from - default endianness - - 64 bit features, if - any high feature bit - is set - - virtio-1 virtqueue - fields, if VERSION_1 - is set - - -Load state procedure -==================== - -virtio core virtio transport virtio device ------------ ---------------- ------------- - - load() function registered - via VMState wrapper on - device class -virtio_load() <---------- - ------> load_config() - - load proxy device - - load transport-specific - device fields -- load common device - fields -- load common virtqueue - fields - ------> load_queue() - - load transport-specific - virtqueue fields -- notify guest - ------> load_device() - - load device-specific - fields -- load subsections - - device endianness - - 64 bit features - - virtio-1 virtqueue - fields -- sanitize endianness -- sanitize features -- virtqueue index sanity - check - - feature-dependent setup - - -Implications of this setup -========================== - -Devices need to be careful in their state processing during load: The -load_device() procedure is invoked by the core before subsections have -been loaded. Any code that depends on information transmitted in subsections -therefore has to be invoked in the device's load() function _after_ -virtio_load() returned (like e.g. code depending on features). - -Any extension of the state being migrated should be done in subsections -added to the core for compatibility reasons. If transport or device specific -state is added, core needs to invoke a callback from the new subsection. diff --git a/docs/devel/writing-monitor-commands.rst b/docs/devel/writing-monitor-commands.rst index 4aa2bb904d..930da5cd06 100644 --- a/docs/devel/writing-monitor-commands.rst +++ b/docs/devel/writing-monitor-commands.rst @@ -8,8 +8,8 @@ This document doesn't discuss QMP protocol level details, nor does it dive into the QAPI framework implementation. For an in-depth introduction to the QAPI framework, please refer to -docs/devel/qapi-code-gen.txt. For documentation about the QMP protocol, -start with docs/interop/qmp-intro.txt. +:doc:`qapi-code-gen`. For the QMP protocol, see the +:doc:`/interop/qmp-spec`. New commands may be implemented in QMP only. New HMP commands should be implemented on top of QMP. The typical HMP command wraps around an @@ -66,12 +66,13 @@ Then, in a different terminal:: "version": { "qemu": { "micro": 50, - "minor": 15, - "major": 0 + "minor": 2, + "major": 8 }, - "package": "" + "package": ... }, "capabilities": [ + "oob" ] } } @@ -107,10 +108,14 @@ The first step is defining the command in the appropriate QAPI schema module. We pick module qapi/misc.json, and add the following line at the bottom:: + ## + # @hello-world: + # + # Since: 9.0 + ## { 'command': 'hello-world' } -The "command" keyword defines a new QMP command. It's an JSON object. All -schema entries are JSON objects. The line above will instruct the QAPI to +The "command" keyword defines a new QMP command. It instructs QAPI to generate any prototypes and the necessary code to marshal and unmarshal protocol data. @@ -132,57 +137,70 @@ There are a few things to be noticed: 3. It takes an "Error \*\*" argument. This is required. Later we will see how to return errors and take additional arguments. The Error argument should not be touched if the command doesn't return errors -4. We won't add the function's prototype. That's automatically done by the QAPI +4. We won't add the function's prototype. That's automatically done by QAPI 5. Printing to the terminal is discouraged for QMP commands, we do it here because it's the easiest way to demonstrate a QMP command -You're done. Now build qemu, run it as suggested in the "Testing" section, +You're done. Now build QEMU, run it as suggested in the "Testing" section, and then type the following QMP command:: { "execute": "hello-world" } -Then check the terminal running qemu and look for the "Hello, world" string. If +Then check the terminal running QEMU and look for the "Hello, world" string. If you don't see it then something went wrong. Arguments ~~~~~~~~~ -Let's add an argument called "message" to our "hello-world" command. The new -argument will contain the string to be printed to stdout. It's an optional -argument, if it's not present we print our default "Hello, World" string. +Let's add arguments to our "hello-world" command. The first change we have to do is to modify the command specification in the schema file to the following:: - { 'command': 'hello-world', 'data': { '*message': 'str' } } + ## + # @hello-world: + # + # @message: message to be printed (default: "Hello, world!") + # + # @times: how many times to print the message (default: 1) + # + # Since: 9.0 + ## + { 'command': 'hello-world', + 'data': { '*message': 'str', '*times': 'int' } } -Notice the new 'data' member in the schema. It's an JSON object whose each -element is an argument to the command in question. Also notice the asterisk, -it's used to mark the argument optional (that means that you shouldn't use it -for mandatory arguments). Finally, 'str' is the argument's type, which -stands for "string". The QAPI also supports integers, booleans, enumerations -and user defined types. +Notice the new 'data' member in the schema. It specifies an argument +'message' of QAPI type 'str', and an argument 'times' of QAPI type +'int'. Also notice the asterisk, it's used to mark the argument +optional. Now, let's update our C implementation in monitor/qmp-cmds.c:: - void qmp_hello_world(bool has_message, const char *message, Error **errp) + void qmp_hello_world(const char *message, bool has_times, int64_t times, + Error **errp) { - if (has_message) { + if (!message) { + message = "Hello, world"; + } + if (!has_times) { + times = 1; + } + + for (int i = 0; i < times; i++) { printf("%s\n", message); - } else { - printf("Hello, world\n"); } } There are two important details to be noticed: -1. All optional arguments are accompanied by a 'has\_' boolean, which is set - if the optional argument is present or false otherwise +1. Optional arguments other than pointers are accompanied by a 'has\_' + boolean, which is set if the optional argument is present or false + otherwise 2. The C implementation signature must follow the schema's argument ordering, which is defined by the "data" member -Time to test our new version of the "hello-world" command. Build qemu, run it as +Time to test our new version of the "hello-world" command. Build QEMU, run it as described in the "Testing" section and then send two commands:: { "execute": "hello-world" } @@ -191,13 +209,13 @@ described in the "Testing" section and then send two commands:: } } - { "execute": "hello-world", "arguments": { "message": "We love qemu" } } + { "execute": "hello-world", "arguments": { "message": "We love QEMU" } } { "return": { } } -You should see "Hello, world" and "We love qemu" in the terminal running qemu, +You should see "Hello, world" and "We love QEMU" in the terminal running QEMU, if you don't see these strings, then something went wrong. @@ -210,9 +228,9 @@ file. Basically, most errors are set by calling the error_setg() function. Let's say we don't accept the string "message" to contain the word "love". If it does contain it, we want the "hello-world" command to return an error:: - void qmp_hello_world(bool has_message, const char *message, Error **errp) + void qmp_hello_world(const char *message, Error **errp) { - if (has_message) { + if (message) { if (strstr(message, "love")) { error_setg(errp, "the word 'love' is not allowed"); return; @@ -227,7 +245,7 @@ The first argument to the error_setg() function is the Error pointer to pointer, which is passed to all QMP functions. The next argument is a human description of the error, this is a free-form printf-like string. -Let's test the example above. Build qemu, run it as defined in the "Testing" +Let's test the example above. Build QEMU, run it as defined in the "Testing" section, and then issue the following command:: { "execute": "hello-world", "arguments": { "message": "all you need is love" } } @@ -254,44 +272,14 @@ If the failure you want to report falls into one of the two cases above, use error_set() with a second argument of an ErrorClass value. -Command Documentation -~~~~~~~~~~~~~~~~~~~~~ - -There's only one step missing to make "hello-world"'s implementation complete, -and that's its documentation in the schema file. - -There are many examples of such documentation in the schema file already, but -here goes "hello-world"'s new entry for qapi/misc.json:: - - ## - # @hello-world: - # - # Print a client provided string to the standard output stream. - # - # @message: string to be printed - # - # Returns: Nothing on success. - # - # Notes: if @message is not provided, the "Hello, world" string will - # be printed instead - # - # Since: - ## - { 'command': 'hello-world', 'data': { '*message': 'str' } } - -Please, note that the "Returns" clause is optional if a command doesn't return -any data nor any errors. - - Implementing the HMP command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Now that the QMP command is in place, we can also make it available in the human monitor (HMP). -With the introduction of the QAPI, HMP commands make QMP calls. Most of the -time HMP commands are simple wrappers. All HMP commands implementation exist in -the monitor/hmp-cmds.c file. +With the introduction of QAPI, HMP commands make QMP calls. Most of the +time HMP commands are simple wrappers. Here's the implementation of the "hello-world" HMP command:: @@ -306,18 +294,20 @@ Here's the implementation of the "hello-world" HMP command:: } } -Also, you have to add the function's prototype to the hmp.h file. +Add it to monitor/hmp-cmds.c. Also, add its prototype to +include/monitor/hmp.h. -There are three important points to be noticed: +There are four important points to be noticed: 1. The "mon" and "qdict" arguments are mandatory for all HMP functions. The former is the monitor object. The latter is how the monitor passes arguments entered by the user to the command implementation -2. hmp_hello_world() performs error checking. In this example we just call +2. We chose not to support the "times" argument in HMP +3. hmp_hello_world() performs error checking. In this example we just call hmp_handle_error() which prints a message to the user, but we could do more, like taking different actions depending on the error qmp_hello_world() returns -3. The "err" variable must be initialized to NULL before performing the +4. The "err" variable must be initialized to NULL before performing the QMP call There's one last step to actually make the command available to monitor users, @@ -340,17 +330,17 @@ To test this you have to open a user monitor and issue the "hello-world" command. It might be instructive to check the command's documentation with HMP's "help" command. -Please, check the "-monitor" command-line option to know how to open a user +Please check the "-monitor" command-line option to know how to open a user monitor. Writing more complex commands ----------------------------- -A QMP command is capable of returning any data the QAPI supports like integers, +A QMP command is capable of returning any data QAPI supports like integers, strings, booleans, enumerations and user defined types. -In this section we will focus on user defined types. Please, check the QAPI +In this section we will focus on user defined types. Please check the QAPI documentation for information about the other types. @@ -372,7 +362,7 @@ data, it is not expected that machines will need to parse the result. The overhead of defining a fine grained QAPI type for the data may not be justified by the potential benefit. In such cases, it is permitted to have a command return a simple string that contains formatted data, -however, it is mandatory for the command to use the 'x-' name prefix. +however, it is mandatory for the command to be marked unstable. This indicates that the command is not guaranteed to be long term stable / liable to change in future and is not following QAPI design best practices. An example where this approach is taken is the QMP @@ -386,302 +376,207 @@ an illustration. User Defined Types ~~~~~~~~~~~~~~~~~~ -FIXME This example needs to be redone after commit 6d32717 +For this example we will write the query-option-roms command, which +returns information about ROMs loaded into the option ROM space. For +more information about it, please check the "-option-rom" command-line +option. -For this example we will write the query-alarm-clock command, which returns -information about QEMU's timer alarm. For more information about it, please -check the "-clock" command-line option. - -We want to return two pieces of information. The first one is the alarm clock's -name. The second one is when the next alarm will fire. The former information is -returned as a string, the latter is an integer in nanoseconds (which is not -very useful in practice, as the timer has probably already fired when the -information reaches the client). - -The best way to return that data is to create a new QAPI type, as shown below:: +For each option ROM, we want to return two pieces of information: the +ROM image's file name, and its bootindex, if any. We need to create a +new QAPI type for that, as shown below:: ## - # @QemuAlarmClock + # @OptionRomInfo: # - # QEMU alarm clock information. + # @filename: option ROM image file name # - # @clock-name: The alarm clock method's name. + # @bootindex: option ROM's bootindex # - # @next-deadline: The time (in nanoseconds) the next alarm will fire. - # - # Since: 1.0 + # Since: 9.0 ## - { 'type': 'QemuAlarmClock', - 'data': { 'clock-name': 'str', '*next-deadline': 'int' } } + { 'struct': 'OptionRomInfo', + 'data': { 'filename': 'str', '*bootindex': 'int' } } -The "type" keyword defines a new QAPI type. Its "data" member contains the -type's members. In this example our members are the "clock-name" and the -"next-deadline" one, which is optional. +The "struct" keyword defines a new QAPI type. Its "data" member +contains the type's members. In this example our members are +"filename" and "bootindex". The latter is optional. -Now let's define the query-alarm-clock command:: +Now let's define the query-option-roms command:: ## - # @query-alarm-clock + # @query-option-roms: # - # Return information about QEMU's alarm clock. + # Query information on ROMs loaded into the option ROM space. # - # Returns a @QemuAlarmClock instance describing the alarm clock method - # being currently used by QEMU (this is usually set by the '-clock' - # command-line option). + # Returns: OptionRomInfo # - # Since: 1.0 + # Since: 9.0 ## - { 'command': 'query-alarm-clock', 'returns': 'QemuAlarmClock' } + { 'command': 'query-option-roms', + 'returns': ['OptionRomInfo'] } Notice the "returns" keyword. As its name suggests, it's used to define the data returned by a command. -It's time to implement the qmp_query_alarm_clock() function, you can put it -in the qemu-timer.c file:: +Notice the syntax ['OptionRomInfo']". This should be read as "returns +a list of OptionRomInfo". - QemuAlarmClock *qmp_query_alarm_clock(Error **errp) +It's time to implement the qmp_query_option_roms() function. Add to +monitor/qmp-cmds.c:: + + OptionRomInfoList *qmp_query_option_roms(Error **errp) { - QemuAlarmClock *clock; - int64_t deadline; + OptionRomInfoList *info_list = NULL; + OptionRomInfoList **tailp = &info_list; + OptionRomInfo *info; - clock = g_malloc0(sizeof(*clock)); - - deadline = qemu_next_alarm_deadline(); - if (deadline > 0) { - clock->has_next_deadline = true; - clock->next_deadline = deadline; + for (int i = 0; i < nb_option_roms; i++) { + info = g_malloc0(sizeof(*info)); + info->filename = g_strdup(option_rom[i].name); + info->has_bootindex = option_rom[i].bootindex >= 0; + if (info->has_bootindex) { + info->bootindex = option_rom[i].bootindex; + } + QAPI_LIST_APPEND(tailp, info); } - clock->clock_name = g_strdup(alarm_timer->name); - return clock; + return info_list; } There are a number of things to be noticed: -1. The QemuAlarmClock type is automatically generated by the QAPI framework, - its members correspond to the type's specification in the schema file -2. As specified in the schema file, the function returns a QemuAlarmClock - instance and takes no arguments (besides the "errp" one, which is mandatory - for all QMP functions) -3. The "clock" variable (which will point to our QAPI type instance) is - allocated by the regular g_malloc0() function. Note that we chose to - initialize the memory to zero. This is recommended for all QAPI types, as - it helps avoiding bad surprises (specially with booleans) -4. Remember that "next_deadline" is optional? All optional members have a - 'has_TYPE_NAME' member that should be properly set by the implementation, - as shown above -5. Even static strings, such as "alarm_timer->name", should be dynamically - allocated by the implementation. This is so because the QAPI also generates - a function to free its types and it cannot distinguish between dynamically - or statically allocated strings -6. You have to include "qapi/qapi-commands-misc.h" in qemu-timer.c +1. Type OptionRomInfo is automatically generated by the QAPI framework, + its members correspond to the type's specification in the schema + file +2. Type OptionRomInfoList is also generated. It's a singly linked + list. +3. As specified in the schema file, the function returns a + OptionRomInfoList, and takes no arguments (besides the "errp" one, + which is mandatory for all QMP functions) +4. The returned object is dynamically allocated +5. All strings are dynamically allocated. This is so because QAPI also + generates a function to free its types and it cannot distinguish + between dynamically or statically allocated strings +6. Remember that "bootindex" is optional? As a non-pointer optional + member, it comes with a 'has_bootindex' member that needs to be set + by the implementation, as shown above -Time to test the new command. Build qemu, run it as described in the "Testing" +Time to test the new command. Build QEMU, run it as described in the "Testing" section and try this:: - { "execute": "query-alarm-clock" } + { "execute": "query-option-rom" } { - "return": { - "next-deadline": 2368219, - "clock-name": "dynticks" - } + "return": [ + { + "filename": "kvmvapic.bin" + } + ] } The HMP command ~~~~~~~~~~~~~~~ -Here's the HMP counterpart of the query-alarm-clock command:: +Here's the HMP counterpart of the query-option-roms command:: - void hmp_info_alarm_clock(Monitor *mon) + void hmp_info_option_roms(Monitor *mon, const QDict *qdict) { - QemuAlarmClock *clock; Error *err = NULL; + OptionRomInfoList *info_list, *tail; + OptionRomInfo *info; - clock = qmp_query_alarm_clock(&err); + info_list = qmp_query_option_roms(&err); if (hmp_handle_error(mon, err)) { return; } - monitor_printf(mon, "Alarm clock method in use: '%s'\n", clock->clock_name); - if (clock->has_next_deadline) { - monitor_printf(mon, "Next alarm will fire in %" PRId64 " nanoseconds\n", - clock->next_deadline); - } - - qapi_free_QemuAlarmClock(clock); - } - -It's important to notice that hmp_info_alarm_clock() calls -qapi_free_QemuAlarmClock() to free the data returned by qmp_query_alarm_clock(). -For user defined types, the QAPI will generate a qapi_free_QAPI_TYPE_NAME() -function and that's what you have to use to free the types you define and -qapi_free_QAPI_TYPE_NAMEList() for list types (explained in the next section). -If the QMP call returns a string, then you should g_free() to free it. - -Also note that hmp_info_alarm_clock() performs error handling. That's not -strictly required if you're sure the QMP function doesn't return errors, but -it's good practice to always check for errors. - -Another important detail is that HMP's "info" commands don't go into the -hmp-commands.hx. Instead, they go into the info_cmds[] table, which is defined -in the monitor/misc.c file. The entry for the "info alarmclock" follows:: - - { - .name = "alarmclock", - .args_type = "", - .params = "", - .help = "show information about the alarm clock", - .cmd = hmp_info_alarm_clock, - }, - -To test this, run qemu and type "info alarmclock" in the user monitor. - - -Returning Lists -~~~~~~~~~~~~~~~ - -For this example, we're going to return all available methods for the timer -alarm, which is pretty much what the command-line option "-clock ?" does, -except that we're also going to inform which method is in use. - -This first step is to define a new type:: - - ## - # @TimerAlarmMethod - # - # Timer alarm method information. - # - # @method-name: The method's name. - # - # @current: true if this alarm method is currently in use, false otherwise - # - # Since: 1.0 - ## - { 'type': 'TimerAlarmMethod', - 'data': { 'method-name': 'str', 'current': 'bool' } } - -The command will be called "query-alarm-methods", here is its schema -specification:: - - ## - # @query-alarm-methods - # - # Returns information about available alarm methods. - # - # Returns: a list of @TimerAlarmMethod for each method - # - # Since: 1.0 - ## - { 'command': 'query-alarm-methods', 'returns': ['TimerAlarmMethod'] } - -Notice the syntax for returning lists "'returns': ['TimerAlarmMethod']", this -should be read as "returns a list of TimerAlarmMethod instances". - -The C implementation follows:: - - TimerAlarmMethodList *qmp_query_alarm_methods(Error **errp) - { - TimerAlarmMethodList *method_list = NULL; - const struct qemu_alarm_timer *p; - bool current = true; - - for (p = alarm_timers; p->name; p++) { - TimerAlarmMethod *value = g_malloc0(*value); - value->method_name = g_strdup(p->name); - value->current = current; - QAPI_LIST_PREPEND(method_list, value); - current = false; - } - - return method_list; - } - -The most important difference from the previous examples is the -TimerAlarmMethodList type, which is automatically generated by the QAPI from -the TimerAlarmMethod type. - -Each list node is represented by a TimerAlarmMethodList instance. We have to -allocate it, and that's done inside the for loop: the "info" pointer points to -an allocated node. We also have to allocate the node's contents, which is -stored in its "value" member. In our example, the "value" member is a pointer -to an TimerAlarmMethod instance. - -Notice that the "current" variable is used as "true" only in the first -iteration of the loop. That's because the alarm timer method in use is the -first element of the alarm_timers array. Also notice that QAPI lists are handled -by hand and we return the head of the list. - -Now Build qemu, run it as explained in the "Testing" section and try our new -command:: - - { "execute": "query-alarm-methods" } - { - "return": [ - { - "current": false, - "method-name": "unix" - }, - { - "current": true, - "method-name": "dynticks" + for (tail = info_list; tail; tail = tail->next) { + info = tail->value; + monitor_printf(mon, "%s", info->filename); + if (info->has_bootindex) { + monitor_printf(mon, " %" PRId64, info->bootindex); } - ] - } - -The HMP counterpart is a bit more complex than previous examples because it -has to traverse the list, it's shown below for reference:: - - void hmp_info_alarm_methods(Monitor *mon) - { - TimerAlarmMethodList *method_list, *method; - Error *err = NULL; - - method_list = qmp_query_alarm_methods(&err); - if (hmp_handle_error(mon, err)) { - return; + monitor_printf(mon, "\n"); } - for (method = method_list; method; method = method->next) { - monitor_printf(mon, "%c %s\n", method->value->current ? '*' : ' ', - method->value->method_name); - } - - qapi_free_TimerAlarmMethodList(method_list); + qapi_free_OptionRomInfoList(info_list); } +It's important to notice that hmp_info_option_roms() calls +qapi_free_OptionRomInfoList() to free the data returned by +qmp_query_option_roms(). For user defined types, QAPI will generate a +qapi_free_QAPI_TYPE_NAME() function, and that's what you have to use to +free the types you define and qapi_free_QAPI_TYPE_NAMEList() for list +types (explained in the next section). If the QMP function returns a +string, then you should g_free() to free it. + +Also note that hmp_info_option_roms() performs error handling. That's +not strictly required when you're sure the QMP function doesn't return +errors; you could instead pass it &error_abort then. + +Another important detail is that HMP's "info" commands go into +hmp-commands-info.hx, not hmp-commands.hx. The entry for the "info +option-roms" follows:: + + { + .name = "option-roms", + .args_type = "", + .params = "", + .help = "show roms", + .cmd = hmp_info_option_roms, + }, + SRST + ``info option-roms`` + Show the option ROMs. + ERST + +To test this, run QEMU and type "info option-roms" in the user monitor. + + Writing a debugging aid returning unstructured text --------------------------------------------------- As discussed in section `Modelling data in QAPI`_, it is required that commands expecting machine usage be using fine-grained QAPI data types. The exception to this rule applies when the command is solely intended -as a debugging aid and allows for returning unstructured text. This is -commonly needed for query commands that report aspects of QEMU's -internal state that are useful to human operators. +as a debugging aid and allows for returning unstructured text, such as +a query command that report aspects of QEMU's internal state that are +useful only to human operators. -In this example we will consider a simplified variant of the HMP -command ``info roms``. Following the earlier rules, this command will -need to live under the ``x-`` name prefix, so its QMP implementation -will be called ``x-query-roms``. It will have no parameters and will -return a single text string:: - - { 'struct': 'HumanReadableText', - 'data': { 'human-readable-text': 'str' } } +In this example we will consider the existing QMP command +``x-query-roms`` in qapi/machine.json. It has no parameters and +returns a ``HumanReadableText``:: + ## + # @x-query-roms: + # + # Query information on the registered ROMS + # + # Features: + # + # @unstable: This command is meant for debugging. + # + # Returns: registered ROMs + # + # Since: 6.2 + ## { 'command': 'x-query-roms', - 'returns': 'HumanReadableText' } + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ] } -The ``HumanReadableText`` struct is intended to be used for all -commands, under the ``x-`` name prefix that are returning unstructured -text targeted at humans. It should never be used for commands outside -the ``x-`` name prefix, as those should be using structured QAPI types. +The ``HumanReadableText`` struct is defined in qapi/common.json as a +struct with a string member. It is intended to be used for all +commands that are returning unstructured text targeted at +humans. These should all have feature 'unstable'. Note that the +feature's documentation states why the command is unstable. We +commonly use a ``x-`` command name prefix to make lack of stability +obvious to human users. Implementing the QMP command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The QMP implementation will typically involve creating a ``GString`` -object and printing formatted data into it:: +object and printing formatted data into it, like this:: HumanReadableText *qmp_x_query_roms(Error **errp) { @@ -698,6 +593,9 @@ object and printing formatted data into it:: return human_readable_text_from_str(buf); } +The actual implementation emits more information. You can find it in +hw/core/loader.c. + Implementing the HMP command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -706,7 +604,7 @@ Now that the QMP command is in place, we can also make it available in the human monitor (HMP) as shown in previous examples. The HMP implementations will all look fairly similar, as all they need do is invoke the QMP command and then print the resulting text or error -message. Here's the implementation of the "info roms" HMP command:: +message. Here's an implementation of the "info roms" HMP command:: void hmp_info_roms(Monitor *mon, const QDict *qdict) { @@ -716,7 +614,7 @@ message. Here's the implementation of the "info roms" HMP command:: if (hmp_handle_error(mon, err)) { return; } - monitor_printf(mon, "%s", info->human_readable_text); + monitor_puts(mon, info->human_readable_text); } Also, you have to add the function's prototype to the hmp.h file. @@ -746,3 +644,5 @@ field NULL:: .help = "show roms", .cmd_info_hrt = qmp_x_query_roms, }, + +This is how the actual HMP command is done. diff --git a/docs/devel/zoned-storage.rst b/docs/devel/zoned-storage.rst new file mode 100644 index 0000000000..30296d3c85 --- /dev/null +++ b/docs/devel/zoned-storage.rst @@ -0,0 +1,62 @@ +============= +zoned-storage +============= + +Zoned Block Devices (ZBDs) divide the LBA space into block regions called zones +that are larger than the LBA size. They can only allow sequential writes, which +can reduce write amplification in SSDs, and potentially lead to higher +throughput and increased capacity. More details about ZBDs can be found at: + +https://zonedstorage.io/docs/introduction/zoned-storage + +1. Block layer APIs for zoned storage +------------------------------------- +QEMU block layer supports three zoned storage models: +- BLK_Z_HM: The host-managed zoned model only allows sequential writes access +to zones. It supports ZBD-specific I/O commands that can be used by a host to +manage the zones of a device. +- BLK_Z_HA: The host-aware zoned model allows random write operations in +zones, making it backward compatible with regular block devices. +- BLK_Z_NONE: The non-zoned model has no zones support. It includes both +regular and drive-managed ZBD devices. ZBD-specific I/O commands are not +supported. + +The block device information resides inside BlockDriverState. QEMU uses +BlockLimits struct(BlockDriverState::bl) that is continuously accessed by the +block layer while processing I/O requests. A BlockBackend has a root pointer to +a BlockDriverState graph(for example, raw format on top of file-posix). The +zoned storage information can be propagated from the leaf BlockDriverState all +the way up to the BlockBackend. If the zoned storage model in file-posix is +set to BLK_Z_HM, then block drivers will declare support for zoned host device. + +The block layer APIs support commands needed for zoned storage devices, +including report zones, four zone operations, and zone append. + +2. Emulating zoned storage controllers +-------------------------------------- +When the BlockBackend's BlockLimits model reports a zoned storage device, users +like the virtio-blk emulation or the qemu-io-cmds.c utility can use block layer +APIs for zoned storage emulation or testing. + +For example, to test zone_report on a null_blk device using qemu-io is:: + + $ path/to/qemu-io --image-opts -n driver=host_device,filename=/dev/nullb0 -c "zrp offset nr_zones" + +To expose the host's zoned block device through virtio-blk, the command line +can be (includes the -device parameter):: + + -blockdev node-name=drive0,driver=host_device,filename=/dev/nullb0,cache.direct=on \ + -device virtio-blk-pci,drive=drive0 + +Or only use the -drive parameter:: + + -driver driver=host_device,file=/dev/nullb0,if=virtio,cache.direct=on + +Additionally, QEMU has several ways of supporting zoned storage, including: +(1) Using virtio-scsi: --device scsi-block allows for the passing through of +SCSI ZBC devices, enabling the attachment of ZBC or ZAC HDDs to QEMU. +(2) PCI device pass-through: While NVMe ZNS emulation is available for testing +purposes, it cannot yet pass through a zoned device from the host. To pass on +the NVMe ZNS device to the guest, use VFIO PCI pass the entire NVMe PCI adapter +through to the guest. Likewise, an HDD HBA can be passed on to QEMU all HDDs +attached to the HBA. diff --git a/docs/interop/bitmaps.rst b/docs/interop/bitmaps.rst index 1de46febdc..ddf8947d54 100644 --- a/docs/interop/bitmaps.rst +++ b/docs/interop/bitmaps.rst @@ -166,9 +166,9 @@ Basic QMP Usage --------------- The primary interface to manipulating bitmap objects is via the QMP -interface. If you are not familiar, see docs/interop/qmp-intro.txt for a broad -overview, and `qemu-qmp-ref `_ for a full reference of all -QMP commands. +interface. If you are not familiar, see the :doc:`qmp-spec` for the +protocol, and :doc:`qemu-qmp-ref` for a full reference of all QMP +commands. Supported Commands ~~~~~~~~~~~~~~~~~~ diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json index 4e049b1c7c..54a1fc6c10 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -113,13 +113,22 @@ # Virtualization, as specified in the AMD64 Architecture # Programmer's Manual. QEMU command line options related to # this feature are documented in -# "docs/amd-memory-encryption.txt". +# "docs/system/i386/amd-memory-encryption.rst". # # @amd-sev-es: The firmware supports running under AMD Secure Encrypted # Virtualization - Encrypted State, as specified in the AMD64 # Architecture Programmer's Manual. QEMU command line options # related to this feature are documented in -# "docs/amd-memory-encryption.txt". +# "docs/system/i386/amd-memory-encryption.rst". +# +# @amd-sev-snp: The firmware supports running under AMD Secure Encrypted +# Virtualization - Secure Nested Paging, as specified in the +# AMD64 Architecture Programmer's Manual. QEMU command line +# options related to this feature are documented in +# "docs/system/i386/amd-memory-encryption.rst". +# +# @intel-tdx: The firmware supports running under Intel Trust Domain +# Extensions (TDX). # # @enrolled-keys: The variable store (NVRAM) template associated with # the firmware binary has the UEFI Secure Boot @@ -185,9 +194,11 @@ # Since: 3.0 ## { 'enum' : 'FirmwareFeature', - 'data' : [ 'acpi-s3', 'acpi-s4', 'amd-sev', 'amd-sev-es', 'enrolled-keys', - 'requires-smm', 'secure-boot', 'verbose-dynamic', - 'verbose-static' ] } + 'data' : [ 'acpi-s3', 'acpi-s4', + 'amd-sev', 'amd-sev-es', 'amd-sev-snp', + 'intel-tdx', + 'enrolled-keys', 'requires-smm', 'secure-boot', + 'verbose-dynamic', 'verbose-static' ] } ## # @FirmwareFlashFile: @@ -212,7 +223,7 @@ ## -# @FirmwareFlashType: +# @FirmwareFlashMode: # # Describes how the firmware build handles code versus variable # persistence. @@ -247,7 +258,7 @@ # # @mode: Describes how the firmware build handles code versus variable # storage. If not present, it must be treated as if it was -# configured with value ``split``. Since: 7.0.0 +# configured with value @split. Since: 7.0.0 # # @executable: Identifies the firmware executable. The @mode # indicates whether there will be an associated @@ -256,13 +267,13 @@ # -drive if=none,id=pflash0,readonly=on,file=@executable.@filename,format=@executable.@format # -machine pflash0=pflash0 # or equivalent -blockdev instead of -drive. When -# @mode is ``combined`` the executable must be +# @mode is @combined the executable must be # cloned before use and configured with readonly=off. # With QEMU versions older than 4.0, you have to use # -drive if=pflash,unit=0,readonly=on,file=@executable.@filename,format=@executable.@format # # @nvram-template: Identifies the NVRAM template compatible with -# @executable, when @mode is set to ``split``, +# @executable, when @mode is set to @split, # otherwise it should not be present. # Management software instantiates an # individual copy -- a specific NVRAM file -- from @@ -424,203 +435,203 @@ # # Examples: # -# { -# "description": "SeaBIOS", -# "interface-types": [ -# "bios" -# ], -# "mapping": { -# "device": "memory", -# "filename": "/usr/share/seabios/bios-256k.bin" -# }, -# "targets": [ -# { -# "architecture": "i386", -# "machines": [ -# "pc-i440fx-*", -# "pc-q35-*" -# ] +# { +# "description": "SeaBIOS", +# "interface-types": [ +# "bios" +# ], +# "mapping": { +# "device": "memory", +# "filename": "/usr/share/seabios/bios-256k.bin" # }, -# { -# "architecture": "x86_64", -# "machines": [ -# "pc-i440fx-*", -# "pc-q35-*" -# ] -# } -# ], -# "features": [ -# "acpi-s3", -# "acpi-s4" -# ], -# "tags": [ -# "CONFIG_BOOTSPLASH=n", -# "CONFIG_ROM_SIZE=256", -# "CONFIG_USE_SMM=n" -# ] -# } +# "targets": [ +# { +# "architecture": "i386", +# "machines": [ +# "pc-i440fx-*", +# "pc-q35-*" +# ] +# }, +# { +# "architecture": "x86_64", +# "machines": [ +# "pc-i440fx-*", +# "pc-q35-*" +# ] +# } +# ], +# "features": [ +# "acpi-s3", +# "acpi-s4" +# ], +# "tags": [ +# "CONFIG_BOOTSPLASH=n", +# "CONFIG_ROM_SIZE=256", +# "CONFIG_USE_SMM=n" +# ] +# } # -# { -# "description": "OVMF with SB+SMM, empty varstore", -# "interface-types": [ -# "uefi" -# ], -# "mapping": { -# "device": "flash", -# "executable": { -# "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd", -# "format": "raw" +# { +# "description": "OVMF with SB+SMM, empty varstore", +# "interface-types": [ +# "uefi" +# ], +# "mapping": { +# "device": "flash", +# "executable": { +# "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd", +# "format": "raw" +# }, +# "nvram-template": { +# "filename": "/usr/share/OVMF/OVMF_VARS.fd", +# "format": "raw" +# } # }, -# "nvram-template": { -# "filename": "/usr/share/OVMF/OVMF_VARS.fd", -# "format": "raw" -# } -# }, -# "targets": [ -# { -# "architecture": "x86_64", -# "machines": [ -# "pc-q35-*" -# ] -# } -# ], -# "features": [ -# "acpi-s3", -# "amd-sev", -# "requires-smm", -# "secure-boot", -# "verbose-dynamic" -# ], -# "tags": [ -# "-a IA32", -# "-a X64", -# "-p OvmfPkg/OvmfPkgIa32X64.dsc", -# "-t GCC48", -# "-b DEBUG", -# "-D SMM_REQUIRE", -# "-D SECURE_BOOT_ENABLE", -# "-D FD_SIZE_4MB" -# ] -# } +# "targets": [ +# { +# "architecture": "x86_64", +# "machines": [ +# "pc-q35-*" +# ] +# } +# ], +# "features": [ +# "acpi-s3", +# "amd-sev", +# "requires-smm", +# "secure-boot", +# "verbose-dynamic" +# ], +# "tags": [ +# "-a IA32", +# "-a X64", +# "-p OvmfPkg/OvmfPkgIa32X64.dsc", +# "-t GCC48", +# "-b DEBUG", +# "-D SMM_REQUIRE", +# "-D SECURE_BOOT_ENABLE", +# "-D FD_SIZE_4MB" +# ] +# } # -# { -# "description": "OVMF with SB+SMM, SB enabled, MS certs enrolled", -# "interface-types": [ -# "uefi" -# ], -# "mapping": { -# "device": "flash", -# "executable": { -# "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd", -# "format": "raw" +# { +# "description": "OVMF with SB+SMM, SB enabled, MS certs enrolled", +# "interface-types": [ +# "uefi" +# ], +# "mapping": { +# "device": "flash", +# "executable": { +# "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd", +# "format": "raw" +# }, +# "nvram-template": { +# "filename": "/usr/share/OVMF/OVMF_VARS.secboot.fd", +# "format": "raw" +# } # }, -# "nvram-template": { -# "filename": "/usr/share/OVMF/OVMF_VARS.secboot.fd", -# "format": "raw" -# } -# }, -# "targets": [ -# { -# "architecture": "x86_64", -# "machines": [ -# "pc-q35-*" -# ] -# } -# ], -# "features": [ -# "acpi-s3", -# "amd-sev", -# "enrolled-keys", -# "requires-smm", -# "secure-boot", -# "verbose-dynamic" -# ], -# "tags": [ -# "-a IA32", -# "-a X64", -# "-p OvmfPkg/OvmfPkgIa32X64.dsc", -# "-t GCC48", -# "-b DEBUG", -# "-D SMM_REQUIRE", -# "-D SECURE_BOOT_ENABLE", -# "-D FD_SIZE_4MB" -# ] -# } +# "targets": [ +# { +# "architecture": "x86_64", +# "machines": [ +# "pc-q35-*" +# ] +# } +# ], +# "features": [ +# "acpi-s3", +# "amd-sev", +# "enrolled-keys", +# "requires-smm", +# "secure-boot", +# "verbose-dynamic" +# ], +# "tags": [ +# "-a IA32", +# "-a X64", +# "-p OvmfPkg/OvmfPkgIa32X64.dsc", +# "-t GCC48", +# "-b DEBUG", +# "-D SMM_REQUIRE", +# "-D SECURE_BOOT_ENABLE", +# "-D FD_SIZE_4MB" +# ] +# } # -# { -# "description": "OVMF with SEV-ES support", -# "interface-types": [ -# "uefi" -# ], -# "mapping": { -# "device": "flash", -# "executable": { -# "filename": "/usr/share/OVMF/OVMF_CODE.fd", -# "format": "raw" +# { +# "description": "OVMF with SEV-ES support", +# "interface-types": [ +# "uefi" +# ], +# "mapping": { +# "device": "flash", +# "executable": { +# "filename": "/usr/share/OVMF/OVMF_CODE.fd", +# "format": "raw" +# }, +# "nvram-template": { +# "filename": "/usr/share/OVMF/OVMF_VARS.fd", +# "format": "raw" +# } # }, -# "nvram-template": { -# "filename": "/usr/share/OVMF/OVMF_VARS.fd", -# "format": "raw" -# } -# }, -# "targets": [ -# { -# "architecture": "x86_64", -# "machines": [ -# "pc-q35-*" -# ] -# } -# ], -# "features": [ -# "acpi-s3", -# "amd-sev", -# "amd-sev-es", -# "verbose-dynamic" -# ], -# "tags": [ -# "-a X64", -# "-p OvmfPkg/OvmfPkgX64.dsc", -# "-t GCC48", -# "-b DEBUG", -# "-D FD_SIZE_4MB" -# ] -# } +# "targets": [ +# { +# "architecture": "x86_64", +# "machines": [ +# "pc-q35-*" +# ] +# } +# ], +# "features": [ +# "acpi-s3", +# "amd-sev", +# "amd-sev-es", +# "verbose-dynamic" +# ], +# "tags": [ +# "-a X64", +# "-p OvmfPkg/OvmfPkgX64.dsc", +# "-t GCC48", +# "-b DEBUG", +# "-D FD_SIZE_4MB" +# ] +# } # -# { -# "description": "UEFI firmware for ARM64 virtual machines", -# "interface-types": [ -# "uefi" -# ], -# "mapping": { -# "device": "flash", -# "executable": { -# "filename": "/usr/share/AAVMF/AAVMF_CODE.fd", -# "format": "raw" +# { +# "description": "UEFI firmware for ARM64 virtual machines", +# "interface-types": [ +# "uefi" +# ], +# "mapping": { +# "device": "flash", +# "executable": { +# "filename": "/usr/share/AAVMF/AAVMF_CODE.fd", +# "format": "raw" +# }, +# "nvram-template": { +# "filename": "/usr/share/AAVMF/AAVMF_VARS.fd", +# "format": "raw" +# } # }, -# "nvram-template": { -# "filename": "/usr/share/AAVMF/AAVMF_VARS.fd", -# "format": "raw" -# } -# }, -# "targets": [ -# { -# "architecture": "aarch64", -# "machines": [ -# "virt-*" -# ] -# } -# ], -# "features": [ +# "targets": [ +# { +# "architecture": "aarch64", +# "machines": [ +# "virt-*" +# ] +# } +# ], +# "features": [ # -# ], -# "tags": [ -# "-a AARCH64", -# "-p ArmVirtPkg/ArmVirtQemu.dsc", -# "-t GCC48", -# "-b DEBUG", -# "-D DEBUG_PRINT_ERROR_LEVEL=0x80000000" -# ] -# } +# ], +# "tags": [ +# "-a AARCH64", +# "-p ArmVirtPkg/ArmVirtQemu.dsc", +# "-t GCC48", +# "-b DEBUG", +# "-D DEBUG_PRINT_ERROR_LEVEL=0x80000000" +# ] +# } ## { 'struct' : 'Firmware', 'data' : { 'description' : 'str', diff --git a/docs/interop/index.rst b/docs/interop/index.rst index b7632acb7b..ed65395bfb 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -15,6 +15,7 @@ are useful for making QEMU interoperate with other software. dbus-display live-block-operations pr-helper + qmp-spec qemu-ga qemu-ga-ref qemu-qmp-ref @@ -23,3 +24,4 @@ are useful for making QEMU interoperate with other software. vhost-user-gpu vhost-vdpa virtio-balloon-stats + vnc-ledstate-pseudo-encoding diff --git a/docs/interop/live-block-operations.rst b/docs/interop/live-block-operations.rst index 39e62c9915..691429c7af 100644 --- a/docs/interop/live-block-operations.rst +++ b/docs/interop/live-block-operations.rst @@ -4,6 +4,8 @@ This work is licensed under the terms of the GNU GPL, version 2 or later. See the COPYING file in the top-level directory. +.. _Live Block Operations: + ============================ Live Block Device Operations ============================ @@ -53,7 +55,7 @@ files in a disk image backing chain: (1) Directional: 'base' and 'top'. Given the simple disk image chain above, image [A] can be referred to as 'base', and image [B] as - 'top'. (This terminology can be seen in in QAPI schema file, + 'top'. (This terminology can be seen in the QAPI schema file, block-core.json.) (2) Relational: 'backing file' and 'overlay'. Again, taking the same @@ -825,7 +827,7 @@ entire disk image chain, to a target, using ``blockdev-mirror`` would be: job ready to be completed (5) Gracefully complete the 'mirror' block device job, and notice the - the event ``BLOCK_JOB_COMPLETED`` + event ``BLOCK_JOB_COMPLETED`` (6) Shutdown the guest by issuing the QMP ``quit`` command so that caches are flushed diff --git a/docs/interop/nbd.txt b/docs/interop/nbd.txt index f5ca25174a..18efb251de 100644 --- a/docs/interop/nbd.txt +++ b/docs/interop/nbd.txt @@ -69,3 +69,4 @@ NBD_CMD_BLOCK_STATUS for "qemu:dirty-bitmap:", NBD_CMD_CACHE NBD_CMD_FLAG_FAST_ZERO * 5.2: NBD_CMD_BLOCK_STATUS for "qemu:allocation-depth" * 7.1: NBD_FLAG_CAN_MULTI_CONN for shareable writable exports +* 8.2: NBD_OPT_EXTENDED_HEADERS, NBD_FLAG_BLOCK_STATUS_PAYLOAD diff --git a/docs/interop/prl-xml.txt b/docs/interop/prl-xml.txt index 7031f8752c..cf9b3fba26 100644 --- a/docs/interop/prl-xml.txt +++ b/docs/interop/prl-xml.txt @@ -122,7 +122,7 @@ Each Image element has following child elements: * Type - image type of the element. It can be: "Plain" for raw files. "Compressed" for expanding disks. - * File - path to image file. Path can be relative to DiskDecriptor.xml or + * File - path to image file. Path can be relative to DiskDescriptor.xml or absolute. == Snapshots element == diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt index f7dc304ff6..2c4618375a 100644 --- a/docs/interop/qcow2.txt +++ b/docs/interop/qcow2.txt @@ -214,14 +214,19 @@ version 2. type. If the incompatible bit "Compression type" is set: the field - must be present and non-zero (which means non-zlib + must be present and non-zero (which means non-deflate compression type). Otherwise, this field must not be present - or must be zero (which means zlib). + or must be zero (which means deflate). Available compression type values: - 0: zlib + 0: deflate 1: zstd + The deflate compression type is called "zlib" + in QEMU. However, clusters with the + deflate compression type do not have zlib headers. + + 105 - 111: Padding, contents defined below. === Header padding === diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst index 3063357bb5..72fb75a6f5 100644 --- a/docs/interop/qemu-ga.rst +++ b/docs/interop/qemu-ga.rst @@ -79,10 +79,15 @@ Options Daemonize after startup (detach from terminal). -.. option:: -b, --blacklist=LIST +.. option:: -b, --block-rpcs=LIST - Comma-separated list of RPCs to disable (no spaces, ``?`` to list - available RPCs). + Comma-separated list of RPCs to disable (no spaces, use ``--block-rpcs=help`` + to list available RPCs). + +.. option:: -a, --allow-rpcs=LIST + + Comma-separated list of RPCs to enable (no spaces, use ``--allow-rpcs=help`` + to list available RPCs). .. option:: -D, --dump-conf @@ -125,7 +130,7 @@ pidfile string fsfreeze-hook string statedir string verbose boolean -blacklist string list +block-rpcs string list ============= =========== See also diff --git a/docs/interop/qemu-qmp-ref.rst b/docs/interop/qemu-qmp-ref.rst index 357effd64f..f94614a0b2 100644 --- a/docs/interop/qemu-qmp-ref.rst +++ b/docs/interop/qemu-qmp-ref.rst @@ -1,3 +1,5 @@ +.. _QMP Ref: + QEMU QMP Reference Manual ========================= diff --git a/docs/interop/qmp-intro.txt b/docs/interop/qmp-intro.txt deleted file mode 100644 index 1c745a7af0..0000000000 --- a/docs/interop/qmp-intro.txt +++ /dev/null @@ -1,88 +0,0 @@ - QEMU Machine Protocol - ===================== - -Introduction ------------- - -The QEMU Machine Protocol (QMP) allows applications to operate a -QEMU instance. - -QMP is JSON[1] based and features the following: - -- Lightweight, text-based, easy to parse data format -- Asynchronous messages support (ie. events) -- Capabilities Negotiation - -For detailed information on QMP's usage, please, refer to the following files: - -o qmp-spec.txt QEMU Machine Protocol current specification -o qemu-qmp-ref.html QEMU QMP commands and events (auto-generated at build-time) - -[1] https://www.json.org - -Usage ------ - -You can use the -qmp option to enable QMP. For example, the following -makes QMP available on localhost port 4444: - -$ qemu [...] -qmp tcp:localhost:4444,server=on,wait=off - -However, for more flexibility and to make use of more options, the -mon -command-line option should be used. For instance, the following example -creates one HMP instance (human monitor) on stdio and one QMP instance -on localhost port 4444: - -$ qemu [...] -chardev stdio,id=mon0 -mon chardev=mon0,mode=readline \ - -chardev socket,id=mon1,host=localhost,port=4444,server=on,wait=off \ - -mon chardev=mon1,mode=control,pretty=on - -Please, refer to QEMU's manpage for more information. - -Simple Testing --------------- - -To manually test QMP one can connect with telnet and issue commands by hand: - -$ telnet localhost 4444 -Trying 127.0.0.1... -Connected to localhost. -Escape character is '^]'. -{ - "QMP": { - "version": { - "qemu": { - "micro": 0, - "minor": 0, - "major": 3 - }, - "package": "v3.0.0" - }, - "capabilities": [ - "oob" - ] - } -} - -{ "execute": "qmp_capabilities" } -{ - "return": { - } -} - -{ "execute": "query-status" } -{ - "return": { - "status": "prelaunch", - "singlestep": false, - "running": false - } -} - -Please refer to docs/interop/qemu-qmp-ref.* for a complete command -reference, generated from qapi/qapi-schema.json. - -QMP wiki page -------------- - -https://wiki.qemu.org/QMP diff --git a/docs/interop/qmp-spec.txt b/docs/interop/qmp-spec.rst similarity index 56% rename from docs/interop/qmp-spec.txt rename to docs/interop/qmp-spec.rst index b0e8351d5b..563344160e 100644 --- a/docs/interop/qmp-spec.txt +++ b/docs/interop/qmp-spec.rst @@ -1,24 +1,26 @@ - QEMU Machine Protocol Specification +.. + Copyright (C) 2009-2016 Red Hat, Inc. -0. About This Document -====================== + This work is licensed under the terms of the GNU GPL, version 2 or + later. See the COPYING file in the top-level directory. -Copyright (C) 2009-2016 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. +=================================== +QEMU Machine Protocol Specification +=================================== -1. Introduction -=============== - -This document specifies the QEMU Machine Protocol (QMP), a JSON-based +The QEMU Machine Protocol (QMP) is a JSON-based protocol which is available for applications to operate QEMU at the machine-level. It is also in use by the QEMU Guest Agent (QGA), which is available for host applications to interact with the guest -operating system. +operating system. This page specifies the general format of +the protocol; details of the commands and data structures can +be found in the :doc:`qemu-qmp-ref` and the :doc:`qemu-ga-ref`. -2. Protocol Specification -========================= +.. contents:: + +Protocol Specification +====================== This section details the protocol format. For the purpose of this document, "Server" is either QEMU or the QEMU Guest Agent, and @@ -30,9 +32,7 @@ following format: json-DATA-STRUCTURE-NAME Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined -by the JSON standard: - -http://www.ietf.org/rfc/rfc8259.txt +by the `JSON standard `_. The server expects its input to be encoded in UTF-8, and sends its output encoded in ASCII. @@ -45,83 +45,89 @@ important unless specifically documented otherwise. Repeating a key within a json-object gives unpredictable results. Also for convenience, the server will accept an extension of -'single-quoted' strings in place of the usual "double-quoted" +``'single-quoted'`` strings in place of the usual ``"double-quoted"`` json-string, and both input forms of strings understand an additional -escape sequence of "\'" for a single quote. The server will only use +escape sequence of ``\'`` for a single quote. The server will only use double quoting on output. -2.1 General Definitions ------------------------ - -2.1.1 All interactions transmitted by the Server are json-objects, always - terminating with CRLF - -2.1.2 All json-objects members are mandatory when not specified otherwise - -2.2 Server Greeting +General Definitions ------------------- +All interactions transmitted by the Server are json-objects, always +terminating with CRLF. + +All json-objects members are mandatory when not specified otherwise. + +Server Greeting +--------------- + Right when connected the Server will issue a greeting message, which signals that the connection has been successfully established and that the Server is ready for capabilities negotiation (for more information refer to section -'4. Capabilities Negotiation'). +`Capabilities Negotiation`_). The greeting message format is: -{ "QMP": { "version": json-object, "capabilities": json-array } } +:: - Where, + { "QMP": { "version": json-object, "capabilities": json-array } } -- The "version" member contains the Server's version information (the format - is the same of the query-version command) -- The "capabilities" member specify the availability of features beyond the +Where: + +- The ``version`` member contains the Server's version information (the format + is the same as for the query-version command). +- The ``capabilities`` member specifies the availability of features beyond the baseline specification; the order of elements in this array has no particular significance. -2.2.1 Capabilities ------------------- +Capabilities +------------ Currently supported capabilities are: -- "oob": the QMP server supports "out-of-band" (OOB) command - execution, as described in section "2.3.1 Out-of-band execution". +``oob`` + the QMP server supports "out-of-band" (OOB) command + execution, as described in section `Out-of-band execution`_. -2.3 Issuing Commands --------------------- +Issuing Commands +---------------- The format for command execution is: -{ "execute": json-string, "arguments": json-object, "id": json-value } +:: + + { "execute": json-string, "arguments": json-object, "id": json-value } or -{ "exec-oob": json-string, "arguments": json-object, "id": json-value } +:: - Where, + { "exec-oob": json-string, "arguments": json-object, "id": json-value } -- The "execute" or "exec-oob" member identifies the command to be +Where: + +- The ``execute`` or ``exec-oob`` member identifies the command to be executed by the server. The latter requests out-of-band execution. -- The "arguments" member is used to pass any arguments required for the +- The ``arguments`` member is used to pass any arguments required for the execution of the command, it is optional when no arguments are required. Each command documents what contents will be considered - valid when handling the json-argument -- The "id" member is a transaction identification associated with the + valid when handling the json-argument. +- The ``id`` member is a transaction identification associated with the command execution, it is optional and will be part of the response - if provided. The "id" member can be any json-value. A json-number + if provided. The ``id`` member can be any json-value. A json-number incremented for each successive command works fine. -The actual commands are documented in the QEMU QMP reference manual -docs/interop/qemu-qmp-ref.{7,html,info,pdf,txt}. +The actual commands are documented in the :doc:`qemu-qmp-ref`. -2.3.1 Out-of-band execution ---------------------------- +Out-of-band execution +--------------------- The server normally reads, executes and responds to one command after the other. The client therefore receives command responses in issue order. -With out-of-band execution enabled via capability negotiation (section -4.), the server reads and queues commands as they arrive. It executes +With out-of-band execution enabled via `capabilities negotiation`_, +the server reads and queues commands as they arrive. It executes commands from the queue one after the other. Commands executed out-of-band jump the queue: the command get executed right away, possibly overtaking prior in-band commands. The client may therefore @@ -129,8 +135,8 @@ receive such a command's response before responses from prior in-band commands. To be able to match responses back to their commands, the client needs -to pass "id" with out-of-band commands. Passing it with all commands -is recommended for clients that accept capability "oob". +to pass ``id`` with out-of-band commands. Passing it with all commands +is recommended for clients that accept capability ``oob``. If the client sends in-band commands faster than the server can execute them, the server will stop reading requests until the request @@ -140,57 +146,61 @@ To ensure commands to be executed out-of-band get read and executed, the client should have at most eight in-band commands in flight. Only a few commands support out-of-band execution. The ones that do -have "allow-oob": true in output of query-qmp-schema. +have ``"allow-oob": true`` in the output of ``query-qmp-schema``. -2.4 Commands Responses ----------------------- +Commands Responses +------------------ There are two possible responses which the Server will issue as the result of a command execution: success or error. -As long as the commands were issued with a proper "id" field, then the -same "id" field will be attached in the corresponding response message +As long as the commands were issued with a proper ``id`` field, then the +same ``id`` field will be attached in the corresponding response message so that requests and responses can match. Clients should drop all the -responses that have an unknown "id" field. +responses that have an unknown ``id`` field. -2.4.1 success -------------- +Success +------- The format of a success response is: -{ "return": json-value, "id": json-value } +:: - Where, + { "return": json-value, "id": json-value } -- The "return" member contains the data returned by the command, which +Where: + +- The ``return`` member contains the data returned by the command, which is defined on a per-command basis (usually a json-object or json-array of json-objects, but sometimes a json-number, json-string, or json-array of json-strings); it is an empty json-object if the - command does not return data -- The "id" member contains the transaction identification associated - with the command execution if issued by the Client + command does not return data. +- The ``id`` member contains the transaction identification associated + with the command execution if issued by the Client. -2.4.2 error ------------ +Error +----- The format of an error response is: -{ "error": { "class": json-string, "desc": json-string }, "id": json-value } +:: - Where, + { "error": { "class": json-string, "desc": json-string }, "id": json-value } -- The "class" member contains the error class name (eg. "GenericError") -- The "desc" member is a human-readable error message. Clients should +Where: + +- The ``class`` member contains the error class name (eg. ``"GenericError"``). +- The ``desc`` member is a human-readable error message. Clients should not attempt to parse this message. -- The "id" member contains the transaction identification associated with - the command execution if issued by the Client +- The ``id`` member contains the transaction identification associated with + the command execution if issued by the Client. -NOTE: Some errors can occur before the Server is able to read the "id" member, -in these cases the "id" member will not be part of the error response, even +NOTE: Some errors can occur before the Server is able to read the ``id`` member; +in these cases the ``id`` member will not be part of the error response, even if provided by the client. -2.5 Asynchronous events ------------------------ +Asynchronous events +------------------- As a result of state changes, the Server may send messages unilaterally to the Client at any time, when not in the middle of any other @@ -198,44 +208,45 @@ response. They are called "asynchronous events". The format of asynchronous events is: -{ "event": json-string, "data": json-object, - "timestamp": { "seconds": json-number, "microseconds": json-number } } +:: - Where, + { "event": json-string, "data": json-object, + "timestamp": { "seconds": json-number, "microseconds": json-number } } -- The "event" member contains the event's name -- The "data" member contains event specific data, which is defined in a - per-event basis, it is optional -- The "timestamp" member contains the exact time of when the event +Where: + +- The ``event`` member contains the event's name. +- The ``data`` member contains event specific data, which is defined in a + per-event basis. It is optional. +- The ``timestamp`` member contains the exact time of when the event occurred in the Server. It is a fixed json-object with time in seconds and microseconds relative to the Unix Epoch (1 Jan 1970); if there is a failure to retrieve host time, both members of the timestamp will be set to -1. -The actual asynchronous events are documented in the QEMU QMP -reference manual docs/interop/qemu-qmp-ref.{7,html,info,pdf,txt}. +The actual asynchronous events are documented in the :doc:`qemu-qmp-ref`. Some events are rate-limited to at most one per second. If additional "similar" events arrive within one second, all but the last one are dropped, and the last one is delayed. "Similar" normally means same event type. -2.6 Forcing the JSON parser into known-good state -------------------------------------------------- +Forcing the JSON parser into known-good state +--------------------------------------------- Incomplete or invalid input can leave the server's JSON parser in a state where it can't parse additional commands. To get it back into known-good state, the client should provoke a lexical error. The cleanest way to do that is sending an ASCII control character -other than '\t' (horizontal tab), '\r' (carriage return), or '\n' (new -line). +other than ``\t`` (horizontal tab), ``\r`` (carriage return), or +``\n`` (new line). Sadly, older versions of QEMU can fail to flag this as an error. If a client needs to deal with them, it should send a 0xFF byte. -2.7 QGA Synchronization ------------------------ +QGA Synchronization +------------------- When a client connects to QGA over a transport lacking proper connection semantics such as virtio-serial, QGA may have read partial @@ -243,86 +254,106 @@ input from a previous client. The client needs to force QGA's parser into known-good state using the previous section's technique. Moreover, the client may receive output a previous client didn't read. To help with skipping that output, QGA provides the -'guest-sync-delimited' command. Refer to its documentation for +``guest-sync-delimited`` command. Refer to its documentation for details. -3. QMP Examples -=============== +QMP Examples +============ This section provides some examples of real QMP usage, in all of them -"C" stands for "Client" and "S" stands for "Server". +``->`` marks text sent by the Client and ``<-`` marks replies by the Server. -3.1 Server greeting -------------------- +.. admonition:: Example -S: { "QMP": {"version": {"qemu": {"micro": 0, "minor": 0, "major": 3}, - "package": "v3.0.0"}, "capabilities": ["oob"] } } + Server greeting -3.2 Capabilities negotiation ----------------------------- + .. code-block:: QMP -C: { "execute": "qmp_capabilities", "arguments": { "enable": ["oob"] } } -S: { "return": {}} + <- { "QMP": {"version": {"qemu": {"micro": 0, "minor": 0, "major": 3}, + "package": "v3.0.0"}, "capabilities": ["oob"] } } -3.3 Simple 'stop' execution ---------------------------- +.. admonition:: Example -C: { "execute": "stop" } -S: { "return": {} } + Capabilities negotiation -3.4 KVM information -------------------- + .. code-block:: QMP -C: { "execute": "query-kvm", "id": "example" } -S: { "return": { "enabled": true, "present": true }, "id": "example"} + -> { "execute": "qmp_capabilities", "arguments": { "enable": ["oob"] } } + <- { "return": {}} -3.5 Parsing error ------------------- +.. admonition:: Example -C: { "execute": } -S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } } + Simple 'stop' execution -3.6 Powerdown event -------------------- + .. code-block:: QMP -S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 }, - "event": "POWERDOWN" } + -> { "execute": "stop" } + <- { "return": {} } -3.7 Out-of-band execution -------------------------- +.. admonition:: Example -C: { "exec-oob": "migrate-pause", "id": 42 } -S: { "id": 42, - "error": { "class": "GenericError", - "desc": "migrate-pause is currently only supported during postcopy-active state" } } + KVM information + + .. code-block:: QMP + + -> { "execute": "query-kvm", "id": "example" } + <- { "return": { "enabled": true, "present": true }, "id": "example"} + +.. admonition:: Example + + Parsing error + + .. code-block:: QMP + + -> { "execute": } + <- { "error": { "class": "GenericError", "desc": "JSON parse error, expecting value" } } + +.. admonition:: Example + + Powerdown event + + .. code-block:: QMP + + <- { "timestamp": { "seconds": 1258551470, "microseconds": 802384 }, + "event": "POWERDOWN" } + +.. admonition:: Example + + Out-of-band execution + + .. code-block:: QMP + + -> { "exec-oob": "migrate-pause", "id": 42 } + <- { "id": 42, + "error": { "class": "GenericError", + "desc": "migrate-pause is currently only supported during postcopy-active state" } } -4. Capabilities Negotiation -=========================== +Capabilities Negotiation +======================== When a Client successfully establishes a connection, the Server is in Capabilities Negotiation mode. -In this mode only the qmp_capabilities command is allowed to run, all -other commands will return the CommandNotFound error. Asynchronous +In this mode only the ``qmp_capabilities`` command is allowed to run; all +other commands will return the ``CommandNotFound`` error. Asynchronous messages are not delivered either. -Clients should use the qmp_capabilities command to enable capabilities -advertised in the Server's greeting (section '2.2 Server Greeting') they -support. +Clients should use the ``qmp_capabilities`` command to enable capabilities +advertised in the `Server Greeting`_ which they support. -When the qmp_capabilities command is issued, and if it does not return an -error, the Server enters in Command mode where capabilities changes take -effect, all commands (except qmp_capabilities) are allowed and asynchronous +When the ``qmp_capabilities`` command is issued, and if it does not return an +error, the Server enters Command mode where capabilities changes take +effect, all commands (except ``qmp_capabilities``) are allowed and asynchronous messages are delivered. -5 Compatibility Considerations -============================== +Compatibility Considerations +============================ All protocol changes or new features which modify the protocol format in an incompatible way are disabled by default and will be advertised by the -capabilities array (section '2.2 Server Greeting'). Thus, Clients can check +capabilities array (in the `Server Greeting`_). Thus, Clients can check that array and enable the capabilities they support. The QMP Server performs a type check on the arguments to a command. It @@ -337,12 +368,12 @@ However, Clients must not assume any particular: - Length of json-arrays - Size of json-objects; in particular, future versions of QEMU may add - new keys and Clients should be able to ignore them. + new keys and Clients should be able to ignore them - Order of json-object members or json-array elements - Amount of errors generated by a command, that is, new errors can be added to any existing command in newer versions of the Server -Any command or member name beginning with "x-" is deemed experimental, +Any command or member name beginning with ``x-`` is deemed experimental, and may be withdrawn or changed in an incompatible manner in a future release. @@ -350,8 +381,8 @@ Of course, the Server does guarantee to send valid JSON. But apart from this, a Client should be "conservative in what they send, and liberal in what they accept". -6. Downstream extension of QMP -============================== +Downstream extension of QMP +=========================== We recommend that downstream consumers of QEMU do *not* modify QMP. Management tools should be able to support both upstream and downstream @@ -363,23 +394,25 @@ avoid modifying QMP. Both upstream and downstream need to take care to preserve long-term compatibility and interoperability. To help with that, QMP reserves JSON object member names beginning with -'__' (double underscore) for downstream use ("downstream names"). This +``__`` (double underscore) for downstream use ("downstream names"). This means upstream will never use any downstream names for its commands, arguments, errors, asynchronous events, and so forth. -Any new names downstream wishes to add must begin with '__'. To +Any new names downstream wishes to add must begin with ``__``. To ensure compatibility with other downstreams, it is strongly -recommended that you prefix your downstream names with '__RFQDN_' where +recommended that you prefix your downstream names with ``__RFQDN_`` where RFQDN is a valid, reverse fully qualified domain name which you control. For example, a qemu-kvm specific monitor command would be: +:: + (qemu) __org.linux-kvm_enable_irqchip -Downstream must not change the server greeting (section 2.2) other than +Downstream must not change the `server greeting`_ other than to offer additional capabilities. But see below for why even that is discouraged. -Section '5 Compatibility Considerations' applies to downstream as well +The section `Compatibility Considerations`_ applies to downstream as well as to upstream, obviously. It follows that downstream must behave exactly like upstream for any input not containing members with downstream names ("downstream members"), except it may add members diff --git a/docs/interop/vhost-user-gpu.rst b/docs/interop/vhost-user-gpu.rst index 1640553729..3035822d05 100644 --- a/docs/interop/vhost-user-gpu.rst +++ b/docs/interop/vhost-user-gpu.rst @@ -124,6 +124,29 @@ VhostUserGpuDMABUFScanout :fourcc: ``i32``, the DMABUF fourcc +VhostUserGpuEdidRequest +^^^^^^^^^^^^^^^^^^^^^^^ + ++------------+ +| scanout-id | ++------------+ + +:scanout-id: ``u32``, the scanout to get edid from + + +VhostUserGpuDMABUFScanout2 +^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++----------------+----------+ +| dmabuf_scanout | modifier | ++----------------+----------+ + +:dmabuf_scanout: ``VhostUserGpuDMABUFScanout``, filled as described in the + VhostUserGpuDMABUFScanout structure. + +:modifier: ``u64``, the DMABUF modifiers + + C structure ----------- @@ -141,6 +164,8 @@ In QEMU the vhost-user-gpu message is implemented with the following struct: VhostUserGpuScanout scanout; VhostUserGpuUpdate update; VhostUserGpuDMABUFScanout dmabuf_scanout; + VhostUserGpuEdidRequest edid_req; + struct virtio_gpu_resp_edid resp_edid; struct virtio_gpu_resp_display_info display_info; uint64_t u64; } payload; @@ -149,10 +174,12 @@ In QEMU the vhost-user-gpu message is implemented with the following struct: Protocol features ----------------- -None yet. +.. code:: c -As the protocol may need to evolve, new messages and communication -changes are negotiated thanks to preliminary + #define VHOST_USER_GPU_PROTOCOL_F_EDID 0 + #define VHOST_USER_GPU_PROTOCOL_F_DMABUF2 1 + +New messages and communication changes are negotiated thanks to the ``VHOST_USER_GPU_GET_PROTOCOL_FEATURES`` and ``VHOST_USER_GPU_SET_PROTOCOL_FEATURES`` requests. @@ -241,3 +268,22 @@ Message types Note: there is no data payload, since the scanout is shared thanks to DMABUF, that must have been set previously with ``VHOST_USER_GPU_DMABUF_SCANOUT``. + +``VHOST_USER_GPU_GET_EDID`` + :id: 11 + :request payload: ``struct VhostUserGpuEdidRequest`` + :reply payload: ``struct virtio_gpu_resp_edid`` (from virtio specification) + + Retrieve the EDID data for a given scanout. + This message requires the ``VHOST_USER_GPU_PROTOCOL_F_EDID`` protocol + feature to be supported. + +``VHOST_USER_GPU_DMABUF_SCANOUT2`` + :id: 12 + :request payload: ``VhostUserGpuDMABUFScanout2`` + :reply payload: N/A + + Same as VHOST_USER_GPU_DMABUF_SCANOUT, but also sends the dmabuf modifiers + appended to the message, which were not provided in the other message. + This message requires the ``VHOST_USER_GPU_PROTOCOL_F_DMABUF2`` protocol + feature to be supported. diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst index d7cf904f7f..d8419fd2f1 100644 --- a/docs/interop/vhost-user.rst +++ b/docs/interop/vhost-user.rst @@ -108,12 +108,49 @@ A vring state description :num: a 32-bit number +A vring descriptor index for split virtqueues +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++-------------+---------------------+ +| vring index | index in avail ring | ++-------------+---------------------+ + +:vring index: 32-bit index of the respective virtqueue + +:index in avail ring: 32-bit value, of which currently only the lower 16 + bits are used: + + - Bits 0–15: Index of the next *Available Ring* descriptor that the + back-end will process. This is a free-running index that is not + wrapped by the ring size. + - Bits 16–31: Reserved (set to zero) + +Vring descriptor indices for packed virtqueues +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++-------------+--------------------+ +| vring index | descriptor indices | ++-------------+--------------------+ + +:vring index: 32-bit index of the respective virtqueue + +:descriptor indices: 32-bit value: + + - Bits 0–14: Index of the next *Available Ring* descriptor that the + back-end will process. This is a free-running index that is not + wrapped by the ring size. + - Bit 15: Driver (Available) Ring Wrap Counter + - Bits 16–30: Index of the entry in the *Used Ring* where the back-end + will place the next descriptor. This is a free-running index that + is not wrapped by the ring size. + - Bit 31: Device (Used) Ring Wrap Counter + A vring address description ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -+-------+-------+------+------------+------+-----------+-----+ -| index | flags | size | descriptor | used | available | log | -+-------+-------+------+------------+------+-----------+-----+ ++-------+-------+------------+------+-----------+-----+ +| index | flags | descriptor | used | available | log | ++-------+-------+------------+------+-----------+-----+ :index: a 32-bit vring index @@ -130,18 +167,8 @@ A vring address description Note that a ring address is an IOVA if ``VIRTIO_F_IOMMU_PLATFORM`` has been negotiated. Otherwise it is a user address. -Memory regions description -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -+-------------+---------+---------+-----+---------+ -| num regions | padding | region0 | ... | region7 | -+-------------+---------+---------+-----+---------+ - -:num regions: a 32-bit number of regions - -:padding: 32-bit - -A region is: +Memory region description +^^^^^^^^^^^^^^^^^^^^^^^^^ +---------------+------+--------------+-------------+ | guest address | size | user address | mmap offset | @@ -155,22 +182,49 @@ A region is: :mmap offset: 64-bit offset where region starts in the mapped memory +When the ``VHOST_USER_PROTOCOL_F_XEN_MMAP`` protocol feature has been +successfully negotiated, the memory region description contains two extra +fields at the end. + ++---------------+------+--------------+-------------+----------------+-------+ +| guest address | size | user address | mmap offset | xen mmap flags | domid | ++---------------+------+--------------+-------------+----------------+-------+ + +:xen mmap flags: 32-bit bit field + +- Bit 0 is set for Xen foreign memory mapping. +- Bit 1 is set for Xen grant memory mapping. +- Bit 8 is set if the memory region can not be mapped in advance, and memory + areas within this region must be mapped / unmapped only when required by the + back-end. The back-end shouldn't try to map the entire region at once, as the + front-end may not allow it. The back-end should rather map only the required + amount of memory at once and unmap it after it is used. + +:domid: a 32-bit Xen hypervisor specific domain id. + Single memory region description ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -+---------+---------------+------+--------------+-------------+ -| padding | guest address | size | user address | mmap offset | -+---------+---------------+------+--------------+-------------+ ++---------+--------+ +| padding | region | ++---------+--------+ :padding: 64-bit -:guest address: a 64-bit guest address of the region +A region is represented by Memory region description. -:size: a 64-bit size +Multiple Memory regions description +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -:user address: a 64-bit user address ++-------------+---------+---------+-----+---------+ +| num regions | padding | region0 | ... | region7 | ++-------------+---------+---------+-----+---------+ -:mmap offset: 64-bit offset where region starts in the mapped memory +:num regions: a 32-bit number of regions + +:padding: 32-bit + +A region is represented by Memory region description. Log description ^^^^^^^^^^^^^^^ @@ -258,6 +312,42 @@ Inflight description :queue size: a 16-bit size of virtqueues +VhostUserShared +^^^^^^^^^^^^^^^ + ++------+ +| UUID | ++------+ + +:UUID: 16 bytes UUID, whose first three components (a 32-bit value, then + two 16-bit values) are stored in big endian. + +Device state transfer parameters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++--------------------+-----------------+ +| transfer direction | migration phase | ++--------------------+-----------------+ + +:transfer direction: a 32-bit enum, describing the direction in which + the state is transferred: + + - 0: Save: Transfer the state from the back-end to the front-end, + which happens on the source side of migration + - 1: Load: Transfer the state from the front-end to the back-end, + which happens on the destination side of migration + +:migration phase: a 32-bit enum, describing the state in which the VM + guest and devices are: + + - 0: Stopped (in the period after the transfer of memory-mapped + regions before switch-over to the destination): The VM guest is + stopped, and the vhost-user device is suspended (see + :ref:`Suspended device state `). + + In the future, additional phases might be added e.g. to allow + iterative migration while the device is running. + C structure ----------- @@ -315,8 +405,9 @@ in the ancillary data: * ``VHOST_USER_SET_VRING_KICK`` * ``VHOST_USER_SET_VRING_CALL`` * ``VHOST_USER_SET_VRING_ERR`` -* ``VHOST_USER_SET_SLAVE_REQ_FD`` +* ``VHOST_USER_SET_BACKEND_REQ_FD`` (previous name ``VHOST_USER_SET_SLAVE_REQ_FD``) * ``VHOST_USER_SET_INFLIGHT_FD`` (if ``VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD``) +* ``VHOST_USER_SET_DEVICE_STATE_FD`` If *front-end* is unable to send the full message or receives a wrong reply it will close the connection. An optional reconnection mechanism @@ -347,35 +438,50 @@ negotiation. Ring states ----------- -Rings can be in one of three states: +Rings have two independent states: started/stopped, and enabled/disabled. -* stopped: the back-end must not process the ring at all. +* While a ring is stopped, the back-end must not process the ring at + all, regardless of whether it is enabled or disabled. The + enabled/disabled state should still be tracked, though, so it can come + into effect once the ring is started. -* started but disabled: the back-end must process the ring without +* started and disabled: The back-end must process the ring without causing any side effects. For example, for a networking device, in the disabled state the back-end must not supply any new RX packets, but must process and discard any TX packets. -* started and enabled. +* started and enabled: The back-end must process the ring normally, i.e. + process all requests and execute them. -Each ring is initialized in a stopped state. The back-end 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`` -or receiving the in-band message ``VHOST_USER_VRING_KICK`` if negotiated, -and stop ring upon receiving ``VHOST_USER_GET_VRING_BASE``. +Each ring is initialized in a stopped and disabled state. The back-end +must start a ring upon receiving a kick (that is, detecting that file +descriptor is readable) on the descriptor specified by +``VHOST_USER_SET_VRING_KICK`` or receiving the in-band message +``VHOST_USER_VRING_KICK`` if negotiated, and stop a ring upon receiving +``VHOST_USER_GET_VRING_BASE``. Rings can be enabled or disabled by ``VHOST_USER_SET_VRING_ENABLE``. -If ``VHOST_USER_F_PROTOCOL_FEATURES`` has not been negotiated, the -ring starts directly in the enabled state. - -If ``VHOST_USER_F_PROTOCOL_FEATURES`` has been negotiated, the ring is -initialized in a disabled state and is enabled by -``VHOST_USER_SET_VRING_ENABLE`` with parameter 1. +In addition, upon receiving a ``VHOST_USER_SET_FEATURES`` message from +the front-end without ``VHOST_USER_F_PROTOCOL_FEATURES`` set, the +back-end must enable all rings immediately. While processing the rings (whether they are enabled or not), the back-end must support changing some configuration aspects on the fly. +.. _suspended_device_state: + +Suspended device state +^^^^^^^^^^^^^^^^^^^^^^ + +While all vrings are stopped, the device is *suspended*. In addition to +not processing any vring (because they are stopped), the device must: + +* not write to any guest memory regions, +* not send any notifications to the guest, +* not send any messages to the front-end, +* still process and reply to messages from the front-end. + Multiple queue support ---------------------- @@ -463,7 +569,8 @@ ancillary data, it may be used to inform the front-end that the log has been modified. Once the source has finished migration, rings will be stopped by the -source. No further update must be done before rings are restarted. +source (:ref:`Suspended device state `). No +further update must be done before rings are restarted. In postcopy migration the back-end is started before all the memory has been received from the source host, and care must be taken to avoid @@ -475,6 +582,80 @@ it performs WAKE ioctl's on the userfaultfd to wake the stalled back-end. The front-end indicates support for this via the ``VHOST_USER_PROTOCOL_F_PAGEFAULT`` feature. +.. _migrating_backend_state: + +Migrating back-end state +^^^^^^^^^^^^^^^^^^^^^^^^ + +Migrating device state involves transferring the state from one +back-end, called the source, to another back-end, called the +destination. After migration, the destination transparently resumes +operation without requiring the driver to re-initialize the device at +the VIRTIO level. If the migration fails, then the source can +transparently resume operation until another migration attempt is made. + +Generally, the front-end is connected to a virtual machine guest (which +contains the driver), which has its own state to transfer between source +and destination, and therefore will have an implementation-specific +mechanism to do so. The ``VHOST_USER_PROTOCOL_F_DEVICE_STATE`` feature +provides functionality to have the front-end include the back-end's +state in this transfer operation so the back-end does not need to +implement its own mechanism, and so the virtual machine may have its +complete state, including vhost-user devices' states, contained within a +single stream of data. + +To do this, the back-end state is transferred from back-end to front-end +on the source side, and vice versa on the destination side. This +transfer happens over a channel that is negotiated using the +``VHOST_USER_SET_DEVICE_STATE_FD`` message. This message has two +parameters: + +* Direction of transfer: On the source, the data is saved, transferring + it from the back-end to the front-end. On the destination, the data + is loaded, transferring it from the front-end to the back-end. + +* Migration phase: Currently, the only supported phase is the period + after the transfer of memory-mapped regions before switch-over to the + destination, when both the source and destination devices are + suspended (:ref:`Suspended device state `). + In the future, additional phases might be supported to allow iterative + migration while the device is running. + +The nature of the channel is implementation-defined, but it must +generally behave like a pipe: The writing end will write all the data it +has into it, signalling the end of data by closing its end. The reading +end must read all of this data (until encountering the end of file) and +process it. + +* When saving, the writing end is the source back-end, and the reading + end is the source front-end. After reading the state data from the + channel, the source front-end must transfer it to the destination + front-end through an implementation-defined mechanism. + +* When loading, the writing end is the destination front-end, and the + reading end is the destination back-end. After reading the state data + from the channel, the destination back-end must deserialize its + internal state from that data and set itself up to allow the driver to + seamlessly resume operation on the VIRTIO level. + +Seamlessly resuming operation means that the migration must be +transparent to the guest driver, which operates on the VIRTIO level. +This driver will not perform any re-initialization steps, but continue +to use the device as if no migration had occurred. The vhost-user +front-end, however, will re-initialize the vhost state on the +destination, following the usual protocol for establishing a connection +to a vhost-user back-end: This includes, for example, setting up memory +mappings and kick and call FDs as necessary, negotiating protocol +features, or setting the initial vring base indices (to the same value +as on the source side, so that operation can resume). + +Both on the source and on the destination side, after the respective +front-end has seen all data transferred (when the transfer FD has been +closed), it sends the ``VHOST_USER_CHECK_DEVICE_STATE`` message to +verify that data transfer was successful in the back-end, too. The +back-end responds once it knows whether the transfer and processing was +successful or not. + Memory access ------------- @@ -516,7 +697,7 @@ expected to reply with a zero payload, non-zero otherwise. The back-end relies on the back-end communication channel (see :ref:`Back-end communication ` section below) to send IOTLB miss -and access failure events, by sending ``VHOST_USER_SLAVE_IOTLB_MSG`` +and access failure events, by sending ``VHOST_USER_BACKEND_IOTLB_MSG`` requests to the front-end with a ``struct vhost_iotlb_msg`` as payload. For miss events, the iotlb payload has to be filled with the miss message type (1), the I/O virtual address and the permissions @@ -540,15 +721,15 @@ Back-end communication ---------------------- An optional communication channel is provided if the back-end declares -``VHOST_USER_PROTOCOL_F_SLAVE_REQ`` protocol feature, to allow the +``VHOST_USER_PROTOCOL_F_BACKEND_REQ`` protocol feature, to allow the back-end to make requests to the front-end. -The fd is provided via ``VHOST_USER_SET_SLAVE_REQ_FD`` ancillary data. +The fd is provided via ``VHOST_USER_SET_BACKEND_REQ_FD`` ancillary data. -A back-end may then send ``VHOST_USER_SLAVE_*`` messages to the front-end +A back-end may then send ``VHOST_USER_BACKEND_*`` messages to the front-end using this fd communication channel. -If ``VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD`` protocol feature is +If ``VHOST_USER_PROTOCOL_F_BACKEND_SEND_FD`` protocol feature is negotiated, back-end can send file descriptors (at most 8 descriptors in each message) to front-end via ancillary data using this fd communication channel. @@ -808,7 +989,7 @@ When reconnecting: #. If ``d.flags`` is not equal to the calculated flags value (means back-end has submitted the buffer to guest driver before crash, so - it has to commit the in-progres update), set ``old_free_head``, + it has to commit the in-progress update), set ``old_free_head``, ``old_used_idx``, ``old_used_wrap_counter`` to ``free_head``, ``used_idx``, ``used_wrap_counter`` @@ -835,7 +1016,7 @@ 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``, +``VHOST_USER_PROTOCOL_F_BACKEND_REQ`` and ``VHOST_USER_PROTOCOL_F_REPLY_ACK``, the former is necessary for getting a message channel from the back-end to the front-end, while the latter needs to be used with the in-band notification messages to block until they are processed, both to avoid @@ -855,18 +1036,21 @@ Protocol features #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_BACKEND_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_BACKEND_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 #define VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS 15 #define VHOST_USER_PROTOCOL_F_STATUS 16 + #define VHOST_USER_PROTOCOL_F_XEN_MMAP 17 + #define VHOST_USER_PROTOCOL_F_SHARED_OBJECT 18 + #define VHOST_USER_PROTOCOL_F_DEVICE_STATE 19 Front-end message types ----------------------- @@ -952,8 +1136,8 @@ Front-end message types ``VHOST_USER_SET_MEM_TABLE`` :id: 5 :equivalent ioctl: ``VHOST_SET_MEM_TABLE`` - :request payload: memory regions description - :reply payload: (postcopy only) memory regions description + :request payload: multiple memory regions description + :reply payload: (postcopy only) multiple memory regions description Sets the memory map regions on the back-end so it can translate the vring addresses. In the ancillary data there is an array of file @@ -1013,18 +1197,54 @@ Front-end message types ``VHOST_USER_SET_VRING_BASE`` :id: 10 :equivalent ioctl: ``VHOST_SET_VRING_BASE`` - :request payload: vring state description + :request payload: vring descriptor index/indices :reply payload: N/A - Sets the base offset in the available vring. + Sets the next index to use for descriptors in this vring: + + * For a split virtqueue, sets only the next descriptor index to + process in the *Available Ring*. The device is supposed to read the + next index in the *Used Ring* from the respective vring structure in + guest memory. + + * For a packed virtqueue, both indices are supplied, as they are not + explicitly available in memory. + + Consequently, the payload type is specific to the type of virt queue + (*a vring descriptor index for split virtqueues* vs. *vring descriptor + indices for packed virtqueues*). ``VHOST_USER_GET_VRING_BASE`` :id: 11 :equivalent ioctl: ``VHOST_USER_GET_VRING_BASE`` :request payload: vring state description - :reply payload: vring state description + :reply payload: vring descriptor index/indices - Get the available vring base offset. + Stops the vring and returns the current descriptor index or indices: + + * For a split virtqueue, returns only the 16-bit next descriptor + index to process in the *Available Ring*. Note that this may + differ from the available ring index in the vring structure in + memory, which points to where the driver will put new available + descriptors. For the *Used Ring*, the device only needs the next + descriptor index at which to put new descriptors, which is the + value in the vring structure in memory, so this value is not + covered by this message. + + * For a packed virtqueue, neither index is explicitly available to + read from memory, so both indices (as maintained by the device) are + returned. + + Consequently, the payload type is specific to the type of virt queue + (*a vring descriptor index for split virtqueues* vs. *vring descriptor + indices for packed virtqueues*). + + When and as long as all of a device’s vrings are stopped, it is + *suspended*, see :ref:`Suspended device state + `. + + The request payload’s *num* field is currently reserved and must be + set to 0. ``VHOST_USER_SET_VRING_KICK`` :id: 12 @@ -1059,8 +1279,8 @@ Front-end message types in the ancillary data. This signals that polling will be used 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 + ``VHOST_USER_PROTOCOL_F_BACKEND_REQ`` have been negotiated this message + isn't necessary as the ``VHOST_USER_BACKEND_VRING_CALL`` message can be used, it may however still be used to set an event file descriptor or to enable polling. @@ -1077,8 +1297,8 @@ Front-end message types invalid FD flag. This flag is set when there is no file descriptor 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 + ``VHOST_USER_PROTOCOL_F_BACKEND_REQ`` have been negotiated this message + isn't necessary as the ``VHOST_USER_BACKEND_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). @@ -1139,7 +1359,7 @@ Front-end message types respond with zero in case the specified MTU is valid, or non-zero otherwise. -``VHOST_USER_SET_SLAVE_REQ_FD`` +``VHOST_USER_SET_BACKEND_REQ_FD`` (previous name ``VHOST_USER_SET_SLAVE_REQ_FD``) :id: 21 :equivalent ioctl: N/A :request payload: N/A @@ -1150,7 +1370,7 @@ Front-end message types This request should be sent only when ``VHOST_USER_F_PROTOCOL_FEATURES`` has been negotiated, and protocol - feature bit ``VHOST_USER_PROTOCOL_F_SLAVE_REQ`` bit is present in + feature bit ``VHOST_USER_PROTOCOL_F_BACKEND_REQ`` bit is present in ``VHOST_USER_GET_PROTOCOL_FEATURES``. If ``VHOST_USER_PROTOCOL_F_REPLY_ACK`` is negotiated, the back-end must respond with zero for success, non-zero otherwise. @@ -1376,14 +1596,6 @@ Front-end message types For further details on postcopy, see ``VHOST_USER_SET_MEM_TABLE``. They apply to ``VHOST_USER_ADD_MEM_REG`` accordingly. - Exactly one file descriptor from which the memory is mapped is - passed in the ancillary data. - - In postcopy mode (see ``VHOST_USER_POSTCOPY_LISTEN``), the back-end - replies with the bases of the memory mapped region to the front-end. - For further details on postcopy, see ``VHOST_USER_SET_MEM_TABLE``. - They apply to ``VHOST_USER_ADD_MEM_REG`` accordingly. - ``VHOST_USER_REM_MEM_REG`` :id: 38 :equivalent ioctl: N/A @@ -1408,14 +1620,6 @@ Front-end message types accept messages with one file descriptor. If a file descriptor is passed, the back-end MUST close it without using it otherwise. - The memory region to be removed is identified by its guest address, - user address and size. The mmap offset is ignored. - - No file descriptors SHOULD be passed in the ancillary data. For - compatibility with existing incorrect implementations, the back-end MAY - accept messages with one file descriptor. If a file descriptor is - passed, the back-end MUST close it without using it otherwise. - ``VHOST_USER_SET_STATUS`` :id: 39 :equivalent ioctl: VHOST_VDPA_SET_STATUS @@ -1438,6 +1642,88 @@ Front-end message types query the back-end for its device status as defined in the Virtio specification. +``VHOST_USER_GET_SHARED_OBJECT`` + :id: 41 + :equivalent ioctl: N/A + :request payload: ``struct VhostUserShared`` + :reply payload: dmabuf fd + + When the ``VHOST_USER_PROTOCOL_F_SHARED_OBJECT`` protocol + feature has been successfully negotiated, and the UUID is found + in the exporters cache, this message is submitted by the front-end + to retrieve a given dma-buf fd from a given back-end, determined by + the requested UUID. Back-end will reply passing the fd when the operation + is successful, or no fd otherwise. + +``VHOST_USER_SET_DEVICE_STATE_FD`` + :id: 42 + :equivalent ioctl: N/A + :request payload: device state transfer parameters + :reply payload: ``u64`` + + Front-end and back-end negotiate a channel over which to transfer the + back-end’s internal state during migration. Either side (front-end or + back-end) may create the channel. The nature of this channel is not + restricted or defined in this document, but whichever side creates it + must create a file descriptor that is provided to the respectively + other side, allowing access to the channel. This FD must behave as + follows: + + * For the writing end, it must allow writing the whole back-end state + sequentially. Closing the file descriptor signals the end of + transfer. + + * For the reading end, it must allow reading the whole back-end state + sequentially. The end of file signals the end of the transfer. + + For example, the channel may be a pipe, in which case the two ends of + the pipe fulfill these requirements respectively. + + Initially, the front-end creates a channel along with such an FD. It + passes the FD to the back-end as ancillary data of a + ``VHOST_USER_SET_DEVICE_STATE_FD`` message. The back-end may create a + different transfer channel, passing the respective FD back to the + front-end as ancillary data of the reply. If so, the front-end must + then discard its channel and use the one provided by the back-end. + + Whether the back-end should decide to use its own channel is decided + based on efficiency: If the channel is a pipe, both ends will most + likely need to copy data into and out of it. Any channel that allows + for more efficient processing on at least one end, e.g. through + zero-copy, is considered more efficient and thus preferred. If the + back-end can provide such a channel, it should decide to use it. + + The request payload contains parameters for the subsequent data + transfer, as described in the :ref:`Migrating back-end state + ` section. + + The value returned is both an indication for success, and whether a + file descriptor for a back-end-provided channel is returned: Bits 0–7 + are 0 on success, and non-zero on error. Bit 8 is the invalid FD + flag; this flag is set when there is no file descriptor returned. + When this flag is not set, the front-end must use the returned file + descriptor as its end of the transfer channel. The back-end must not + both indicate an error and return a file descriptor. + + Using this function requires prior negotiation of the + ``VHOST_USER_PROTOCOL_F_DEVICE_STATE`` feature. + +``VHOST_USER_CHECK_DEVICE_STATE`` + :id: 43 + :equivalent ioctl: N/A + :request payload: N/A + :reply payload: ``u64`` + + After transferring the back-end’s internal state during migration (see + the :ref:`Migrating back-end state ` + section), check whether the back-end was able to successfully fully + process the state. + + The value returned indicates success or error; 0 is success, any + non-zero value is an error. + + Using this function requires prior negotiation of the + ``VHOST_USER_PROTOCOL_F_DEVICE_STATE`` feature. Back-end message types ---------------------- @@ -1445,7 +1731,7 @@ Back-end message types For this type of message, the request is sent by the back-end and the reply is sent by the front-end. -``VHOST_USER_SLAVE_IOTLB_MSG`` +``VHOST_USER_BACKEND_IOTLB_MSG`` (previous name ``VHOST_USER_SLAVE_IOTLB_MSG``) :id: 1 :equivalent ioctl: N/A (equivalent to ``VHOST_IOTLB_MSG`` message type) :request payload: ``struct vhost_iotlb_msg`` @@ -1460,7 +1746,7 @@ is sent by the front-end. ``VIRTIO_F_IOMMU_PLATFORM`` feature has been successfully negotiated. -``VHOST_USER_SLAVE_CONFIG_CHANGE_MSG`` +``VHOST_USER_BACKEND_CONFIG_CHANGE_MSG`` (previous name ``VHOST_USER_SLAVE_CONFIG_CHANGE_MSG``) :id: 2 :equivalent ioctl: N/A :request payload: N/A @@ -1475,7 +1761,7 @@ is sent by the front-end. ``VHOST_USER_NEED_REPLY`` flag, the front-end must respond with zero when operation is successfully completed, or non-zero otherwise. -``VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG`` +``VHOST_USER_BACKEND_VRING_HOST_NOTIFIER_MSG`` (previous name ``VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG``) :id: 3 :equivalent ioctl: N/A :request payload: vring area description @@ -1498,7 +1784,7 @@ is sent by the front-end. ``VHOST_USER_PROTOCOL_F_HOST_NOTIFIER`` protocol feature has been successfully negotiated. -``VHOST_USER_SLAVE_VRING_CALL`` +``VHOST_USER_BACKEND_VRING_CALL`` (previous name ``VHOST_USER_SLAVE_VRING_CALL``) :id: 4 :equivalent ioctl: N/A :request payload: vring state description @@ -1512,7 +1798,7 @@ is sent by the front-end. The state.num field is currently reserved and must be set to 0. -``VHOST_USER_SLAVE_VRING_ERR`` +``VHOST_USER_BACKEND_VRING_ERR`` (previous name ``VHOST_USER_SLAVE_VRING_ERR``) :id: 5 :equivalent ioctl: N/A :request payload: vring state description @@ -1526,6 +1812,53 @@ is sent by the front-end. The state.num field is currently reserved and must be set to 0. +``VHOST_USER_BACKEND_SHARED_OBJECT_ADD`` + :id: 6 + :equivalent ioctl: N/A + :request payload: ``struct VhostUserShared`` + :reply payload: N/A + + When the ``VHOST_USER_PROTOCOL_F_SHARED_OBJECT`` protocol + feature has been successfully negotiated, this message can be submitted + by the backends to add themselves as exporters to the virtio shared lookup + table. The back-end device gets associated with a UUID in the shared table. + The back-end is responsible of keeping its own table with exported dma-buf fds. + When another back-end tries to import the resource associated with the UUID, + it will send a message to the front-end, which will act as a proxy to the + exporter back-end. If ``VHOST_USER_PROTOCOL_F_REPLY_ACK`` is negotiated, and + the back-end sets the ``VHOST_USER_NEED_REPLY`` flag, the front-end must + respond with zero when operation is successfully completed, or non-zero + otherwise. + +``VHOST_USER_BACKEND_SHARED_OBJECT_REMOVE`` + :id: 7 + :equivalent ioctl: N/A + :request payload: ``struct VhostUserShared`` + :reply payload: N/A + + When the ``VHOST_USER_PROTOCOL_F_SHARED_OBJECT`` protocol + feature has been successfully negotiated, this message can be submitted + by the backend to remove themselves from to the virtio-dmabuf shared + table API. Only the back-end owning the entry (i.e., the one that first added + it) will have permission to remove it. Otherwise, the message is ignored. + The shared table will remove the back-end device associated with + the UUID. If ``VHOST_USER_PROTOCOL_F_REPLY_ACK`` is negotiated, and the + back-end sets the ``VHOST_USER_NEED_REPLY`` flag, the front-end must respond + with zero when operation is successfully completed, or non-zero otherwise. + +``VHOST_USER_BACKEND_SHARED_OBJECT_LOOKUP`` + :id: 8 + :equivalent ioctl: N/A + :request payload: ``struct VhostUserShared`` + :reply payload: dmabuf fd and ``u64`` + + When the ``VHOST_USER_PROTOCOL_F_SHARED_OBJECT`` protocol + feature has been successfully negotiated, this message can be submitted + by the backends to retrieve a given dma-buf fd from the virtio-dmabuf + shared table given a UUID. Frontend will reply passing the fd and a zero + when the operation is successful, or non-zero otherwise. Note that if the + operation fails, no fd is sent to the backend. + .. _reply_ack: VHOST_USER_PROTOCOL_F_REPLY_ACK diff --git a/docs/interop/vnc-ledstate-Pseudo-encoding.txt b/docs/interop/vnc-ledstate-pseudo-encoding.rst similarity index 100% rename from docs/interop/vnc-ledstate-Pseudo-encoding.txt rename to docs/interop/vnc-ledstate-pseudo-encoding.rst diff --git a/docs/meson.build b/docs/meson.build index 9136fed3b7..9040f860ae 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -1,10 +1,5 @@ -if get_option('sphinx_build') == '' - sphinx_build = find_program(['sphinx-build-3', 'sphinx-build'], - required: get_option('docs')) -else - sphinx_build = find_program(get_option('sphinx_build'), - required: get_option('docs')) -endif +sphinx_build = find_program(fs.parent(python.full_path()) / 'sphinx-build', + required: get_option('docs')) # Check if tools are available to build documentation. build_docs = false @@ -12,7 +7,19 @@ if sphinx_build.found() SPHINX_ARGS = ['env', 'CONFDIR=' + qemu_confdir, sphinx_build, '-q'] # If we're making warnings fatal, apply this to Sphinx runs as well if get_option('werror') - SPHINX_ARGS += [ '-W' ] + SPHINX_ARGS += [ '-W', '-Dkerneldoc_werror=1' ] + endif + + sphinx_version = run_command(SPHINX_ARGS + ['--version'], + check: true).stdout().split()[1] + if sphinx_version.version_compare('>=1.7.0') + SPHINX_ARGS += ['-j', 'auto'] + else + nproc = find_program('nproc') + if nproc.found() + jobs = run_command(nproc, check: true).stdout() + SPHINX_ARGS += ['-j', jobs] + endif endif # This is a bit awkward but works: create a trivial document and @@ -48,7 +55,6 @@ if build_docs 'qemu-storage-daemon.1': (have_tools ? 'man1' : ''), 'qemu-trace-stap.1': (stap.found() ? 'man1' : ''), 'virtfs-proxy-helper.1': (have_virtfs_proxy_helper ? 'man1' : ''), - 'virtiofsd.1': (have_virtiofsd ? 'man1' : ''), 'qemu.1': 'man1', 'qemu-block-drivers.7': 'man7', 'qemu-cpu-models.7': 'man7' diff --git a/docs/multi-thread-compression.txt b/docs/multi-thread-compression.txt index bb88c6bdf1..95b1556f67 100644 --- a/docs/multi-thread-compression.txt +++ b/docs/multi-thread-compression.txt @@ -117,13 +117,13 @@ to support the multiple thread compression migration: {qemu} migrate_set_capability compress on 3. Set the compression thread count on source: - {qemu} migrate_set_parameter compress_threads 12 + {qemu} migrate_set_parameter compress-threads 12 4. Set the compression level on the source: - {qemu} migrate_set_parameter compress_level 1 + {qemu} migrate_set_parameter compress-level 1 5. Set the decompression thread count on destination: - {qemu} migrate_set_parameter decompress_threads 3 + {qemu} migrate_set_parameter decompress-threads 3 6. Start outgoing migration: {qemu} migrate -d tcp:destination.host:4444 @@ -133,9 +133,9 @@ to support the multiple thread compression migration: The following are the default settings: compress: off - compress_threads: 8 - decompress_threads: 2 - compress_level: 1 (which means best speed) + compress-threads: 8 + decompress-threads: 2 + compress-level: 1 (which means best speed) So, only the first two steps are required to use the multiple thread compression in migration. You can do more if the default diff --git a/docs/pcie.txt b/docs/pcie.txt index 89e3502075..df49178311 100644 --- a/docs/pcie.txt +++ b/docs/pcie.txt @@ -48,8 +48,8 @@ Place only the following kinds of devices directly on the Root Complex: strangely when PCI Express devices are integrated with the Root Complex. - (2) PCI Express Root Ports (ioh3420), for starting exclusively PCI Express - hierarchies. + (2) PCI Express Root Ports (pcie-root-port), for starting exclusively + PCI Express hierarchies. (3) PCI Express to PCI Bridge (pcie-pci-bridge), for starting legacy PCI hierarchies. @@ -70,7 +70,7 @@ Place only the following kinds of devices directly on the Root Complex: -device pxb-pcie,id=pcie.1,bus_nr=x[,numa_node=y][,addr=z] PCI Express Root Ports and PCI Express to PCI bridges can be connected to the pcie.1 bus: - -device ioh3420,id=root_port1[,bus=pcie.1][,chassis=x][,slot=y][,addr=z] \ + -device pcie-root-port,id=root_port1[,bus=pcie.1][,chassis=x][,slot=y][,addr=z] \ -device pcie-pci-bridge,id=pcie_pci_bridge1,bus=pcie.1 @@ -112,14 +112,14 @@ Plug only PCI Express devices into PCI Express Ports. ------------ 2.2.1 Plugging a PCI Express device into a PCI Express Root Port: - -device ioh3420,id=root_port1,chassis=x,slot=y[,bus=pcie.0][,addr=z] \ + -device pcie-root-port,id=root_port1,chassis=x,slot=y[,bus=pcie.0][,addr=z] \ -device ,bus=root_port1 2.2.2 Using multi-function PCI Express Root Ports: - -device ioh3420,id=root_port1,multifunction=on,chassis=x,addr=z.0[,slot=y][,bus=pcie.0] \ - -device ioh3420,id=root_port2,chassis=x1,addr=z.1[,slot=y1][,bus=pcie.0] \ - -device ioh3420,id=root_port3,chassis=x2,addr=z.2[,slot=y2][,bus=pcie.0] \ + -device pcie-root-port,id=root_port1,multifunction=on,chassis=x,addr=z.0[,slot=y][,bus=pcie.0] \ + -device pcie-root-port,id=root_port2,chassis=x1,addr=z.1[,slot=y1][,bus=pcie.0] \ + -device pcie-root-port,id=root_port3,chassis=x2,addr=z.2[,slot=y2][,bus=pcie.0] \ 2.2.3 Plugging a PCI Express device into a Switch: - -device ioh3420,id=root_port1,chassis=x,slot=y[,bus=pcie.0][,addr=z] \ + -device pcie-root-port,id=root_port1,chassis=x,slot=y[,bus=pcie.0][,addr=z] \ -device x3130-upstream,id=upstream_port1,bus=root_port1[,addr=x] \ -device xio3130-downstream,id=downstream_port1,bus=upstream_port1,chassis=x1,slot=y1[,addr=z1]] \ -device ,bus=downstream_port1 diff --git a/docs/pcie_sriov.txt b/docs/pcie_sriov.txt index 11158dbf88..a47aad0bfa 100644 --- a/docs/pcie_sriov.txt +++ b/docs/pcie_sriov.txt @@ -9,10 +9,7 @@ virtual functions (VFs) for the main purpose of eliminating software overhead in I/O from virtual machines. QEMU now implements the basic common functionality to enable an emulated device -to support SR/IOV. Yet no fully implemented devices exists in QEMU, but a -proof-of-concept hack of the Intel igb can be found here: - -git://github.com/knuto/qemu.git sriov_patches_v5 +to support SR/IOV. Implementation ============== @@ -51,7 +48,7 @@ setting up a BAR for a VF. ... int ret = pcie_endpoint_cap_init(d, 0x70); ... - pcie_ari_init(d, 0x100, 1); + pcie_ari_init(d, 0x100); ... /* Add and initialize the SR/IOV capability */ @@ -81,7 +78,7 @@ setting up a BAR for a VF. ... int ret = pcie_endpoint_cap_init(d, 0x60); ... - pcie_ari_init(d, 0x100, 1); + pcie_ari_init(d, 0x100); ... memory_region_init(mr, ... ) pcie_sriov_vf_register_bar(d, bar_nr, mr); diff --git a/docs/qdev-device-use.txt b/docs/qdev-device-use.txt index 2408889334..c98c86d828 100644 --- a/docs/qdev-device-use.txt +++ b/docs/qdev-device-use.txt @@ -216,11 +216,11 @@ LEGACY-CHARDEV translates to -chardev HOST-OPTS... as follows: * unix:FNAME becomes -chardev socket,path=FNAME -* /dev/parportN becomes -chardev parport,file=/dev/parportN +* /dev/parportN becomes -chardev parallel,file=/dev/parportN * /dev/ppiN likewise -* Any other /dev/FNAME becomes -chardev tty,path=/dev/FNAME +* Any other /dev/FNAME becomes -chardev serial,path=/dev/FNAME * mon:LEGACY-CHARDEV is special: it multiplexes the monitor onto the character device defined by LEGACY-CHARDEV. -chardev provides more diff --git a/docs/rdma.txt b/docs/rdma.txt index 2b4cdea1d8..bd8dd799a9 100644 --- a/docs/rdma.txt +++ b/docs/rdma.txt @@ -89,7 +89,7 @@ RUNNING: First, set the migration speed to match your hardware's capabilities: QEMU Monitor Command: -$ migrate_set_parameter max_bandwidth 40g # or whatever is the MAX of your RDMA device +$ migrate_set_parameter max-bandwidth 40g # or whatever is the MAX of your RDMA device Next, on the destination machine, add the following to the QEMU command line: diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000000..691e5218ec --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +sphinx==5.3.0 +sphinx_rtd_theme==1.1.1 diff --git a/docs/specs/acpi_erst.rst b/docs/specs/acpi_erst.rst index a8a9d22d25..2339b60ad7 100644 --- a/docs/specs/acpi_erst.rst +++ b/docs/specs/acpi_erst.rst @@ -108,7 +108,7 @@ Slot 0 contains a backend storage header that identifies the contents as ERST and also facilitates efficient access to the records. Depending upon the size of the backend storage, additional slots will be designated to be a part of the slot 0 header. For example, at 8KiB, -the slot 0 header can accomodate 1021 records. Thus a storage size +the slot 0 header can accommodate 1021 records. Thus a storage size of 8MiB (8KiB * 1024) requires an additional slot for use by the header. In this scenario, slot 0 and slot 1 form the backend storage header, and records can be stored starting at slot 2. @@ -196,5 +196,5 @@ References [2] "Unified Extensible Firmware Interface Specification", version 2.1, October 2008. -[3] "Windows Hardware Error Architecture", specfically +[3] "Windows Hardware Error Architecture", specifically "Error Record Persistence Mechanism". diff --git a/docs/specs/edu.txt b/docs/specs/edu.rst similarity index 64% rename from docs/specs/edu.txt rename to docs/specs/edu.rst index 0876310809..ae72737dbb 100644 --- a/docs/specs/edu.txt +++ b/docs/specs/edu.rst @@ -2,9 +2,10 @@ EDU device ========== -Copyright (c) 2014-2015 Jiri Slaby +.. + Copyright (c) 2014-2015 Jiri Slaby -This document is licensed under the GPLv2 (or later). + This document is licensed under the GPLv2 (or later). This is an educational device for writing (kernel) drivers. Its original intention was to support the Linux kernel lectures taught at the Masaryk @@ -15,10 +16,11 @@ The devices behaves very similar to the PCI bridge present in the COMBO6 cards developed under the Liberouter wings. Both PCI device ID and PCI space is inherited from that device. -Command line switches: - -device edu[,dma_mask=mask] +Command line switches +--------------------- - dma_mask makes the virtual device work with DMA addresses with the given +``-device edu[,dma_mask=mask]`` + ``dma_mask`` makes the virtual device work with DMA addresses with the given mask. For educational purposes, the device supports only 28 bits (256 MiB) by default. Students shall set dma_mask for the device in the OS driver properly. @@ -26,7 +28,8 @@ Command line switches: PCI specs --------- -PCI ID: 1234:11e8 +PCI ID: + ``1234:11e8`` PCI Region 0: I/O memory, 1 MB in size. Users are supposed to communicate with the card @@ -35,24 +38,29 @@ PCI Region 0: MMIO area spec -------------- -Only size == 4 accesses are allowed for addresses < 0x80. size == 4 or -size == 8 for the rest. +Only ``size == 4`` accesses are allowed for addresses ``< 0x80``. +``size == 4`` or ``size == 8`` for the rest. -0x00 (RO) : identification (0xRRrr00edu) - RR -- major version - rr -- minor version +0x00 (RO) : identification + Value is in the form ``0xRRrr00edu`` where: + - ``RR`` -- major version + - ``rr`` -- minor version 0x04 (RW) : card liveness check - It is a simple value inversion (~ C operator). + It is a simple value inversion (``~`` C operator). 0x08 (RW) : factorial computation The stored value is taken and factorial of it is put back here. This happens only after factorial bit in the status register (0x20 below) is cleared. -0x20 (RW) : status register, bitwise OR - 0x01 -- computing factorial (RO) - 0x80 -- raise interrupt after finishing factorial computation +0x20 (RW) : status register + Bitwise OR of: + + 0x01 + computing factorial (RO) + 0x80 + raise interrupt after finishing factorial computation 0x24 (RO) : interrupt status register It contains values which raised the interrupt (see interrupt raise @@ -76,13 +84,19 @@ size == 8 for the rest. 0x90 (RW) : DMA transfer count The size of the area to perform the DMA on. -0x98 (RW) : DMA command register, bitwise OR - 0x01 -- start transfer - 0x02 -- direction (0: from RAM to EDU, 1: from EDU to RAM) - 0x04 -- raise interrupt 0x100 after finishing the DMA +0x98 (RW) : DMA command register + Bitwise OR of: + + 0x01 + start transfer + 0x02 + direction (0: from RAM to EDU, 1: from EDU to RAM) + 0x04 + raise interrupt 0x100 after finishing the DMA IRQ controller -------------- + An IRQ is generated when written to the interrupt raise register. The value appears in interrupt status register when the interrupt is raised and has to be written to the interrupt acknowledge register to lower it. @@ -94,22 +108,28 @@ routine. DMA controller -------------- + One has to specify, source, destination, size, and start the transfer. One 4096 bytes long buffer at offset 0x40000 is available in the EDU device. I.e. one can perform DMA to/from this space when programmed properly. Example of transferring a 100 byte block to and from the buffer using a given -PCI address 'addr': -addr -> DMA source address -0x40000 -> DMA destination address -100 -> DMA transfer count -1 -> DMA command register -while (DMA command register & 1) - ; +PCI address ``addr``: -0x40000 -> DMA source address -addr+100 -> DMA destination address -100 -> DMA transfer count -3 -> DMA command register -while (DMA command register & 1) - ; +:: + + addr -> DMA source address + 0x40000 -> DMA destination address + 100 -> DMA transfer count + 1 -> DMA command register + while (DMA command register & 1) + ; + +:: + + 0x40000 -> DMA source address + addr+100 -> DMA destination address + 100 -> DMA transfer count + 3 -> DMA command register + while (DMA command register & 1) + ; diff --git a/docs/specs/fsi.rst b/docs/specs/fsi.rst new file mode 100644 index 0000000000..af87822531 --- /dev/null +++ b/docs/specs/fsi.rst @@ -0,0 +1,122 @@ +====================================== +IBM's Flexible Service Interface (FSI) +====================================== + +The QEMU FSI emulation implements hardware interfaces between ASPEED SOC, FSI +master/slave and the end engine. + +FSI is a point-to-point two wire interface which is capable of supporting +distances of up to 4 meters. FSI interfaces have been used successfully for +many years in IBM servers to attach IBM Flexible Support Processors(FSP) to +CPUs and IBM ASICs. + +FSI allows a service processor access to the internal buses of a host POWER +processor to perform configuration or debugging. FSI has long existed in POWER +processes and so comes with some baggage, including how it has been integrated +into the ASPEED SoC. + +Working backwards from the POWER processor, the fundamental pieces of interest +for the implementation are: (see the `FSI specification`_ for more details) + +1. The Common FRU Access Macro (CFAM), an address space containing various + "engines" that drive accesses on buses internal and external to the POWER + chip. Examples include the SBEFIFO and I2C masters. The engines hang off of + an internal Local Bus (LBUS) which is described by the CFAM configuration + block. + +2. The FSI slave: The slave is the terminal point of the FSI bus for FSI + symbols addressed to it. Slaves can be cascaded off of one another. The + slave's configuration registers appear in address space of the CFAM to + which it is attached. + +3. The FSI master: A controller in the platform service processor (e.g. BMC) + driving CFAM engine accesses into the POWER chip. At the hardware level + FSI is a bit-based protocol supporting synchronous and DMA-driven accesses + of engines in a CFAM. + +4. The On-Chip Peripheral Bus (OPB): A low-speed bus typically found in POWER + processors. This now makes an appearance in the ASPEED SoC due to tight + integration of the FSI master IP with the OPB, mainly the existence of an + MMIO-mapping of the CFAM address straight onto a sub-region of the OPB + address space. + +5. An APB-to-OPB bridge enabling access to the OPB from the ARM core in the + AST2600. Hardware limitations prevent the OPB from being directly mapped + into APB, so all accesses are indirect through the bridge. + +The LBUS is modelled to maintain the qdev bus hierarchy and to take advantages +of the object model to automatically generate the CFAM configuration block. +The configuration block presents engines in the order they are attached to the +CFAM's LBUS. Engine implementations should subclass the LBusDevice and set the +'config' member of LBusDeviceClass to match the engine's type. + +CFAM designs offer a lot of flexibility, for instance it is possible for a +CFAM to be simultaneously driven from multiple FSI links. The modeling is not +so complete; it's assumed that each CFAM is attached to a single FSI slave (as +a consequence the CFAM subclasses the FSI slave). + +As for FSI, its symbols and wire-protocol are not modelled at all. This is not +necessary to get FSI off the ground thanks to the mapping of the CFAM address +space onto the OPB address space - the models follow this directly and map the +CFAM memory region into the OPB's memory region. + +The following commands start the ``rainier-bmc`` machine with built-in FSI +model. There are no model specific arguments. Please check this document to +learn more about Aspeed ``rainier-bmc`` machine: (:doc:`../../system/arm/aspeed`) + +.. code-block:: console + + qemu-system-arm -M rainier-bmc -nographic \ + -kernel fitImage-linux.bin \ + -dtb aspeed-bmc-ibm-rainier.dtb \ + -initrd obmc-phosphor-initramfs.rootfs.cpio.xz \ + -drive file=obmc-phosphor-image.rootfs.wic.qcow2,if=sd,index=2 \ + -append "rootwait console=ttyS4,115200n8 root=PARTLABEL=rofs-a" + +The implementation appears as following in the qemu device tree: + +.. code-block:: console + + (qemu) info qtree + bus: main-system-bus + type System + ... + dev: aspeed.apb2opb, id "" + gpio-out "sysbus-irq" 1 + mmio 000000001e79b000/0000000000001000 + bus: opb.1 + type opb + dev: fsi.master, id "" + bus: fsi.bus.1 + type fsi.bus + dev: cfam.config, id "" + dev: cfam, id "" + bus: lbus.1 + type lbus + dev: scratchpad, id "" + address = 0 (0x0) + bus: opb.0 + type opb + dev: fsi.master, id "" + bus: fsi.bus.0 + type fsi.bus + dev: cfam.config, id "" + dev: cfam, id "" + bus: lbus.0 + type lbus + dev: scratchpad, id "" + address = 0 (0x0) + +pdbg is a simple application to allow debugging of the host POWER processors +from the BMC. (see the `pdbg source repository`_ for more details) + +.. code-block:: console + + root@p10bmc:~# pdbg -a getcfam 0x0 + p0: 0x0 = 0xc0022d15 + +.. _FSI specification: + https://openpowerfoundation.org/specifications/fsi/ + +.. _pdbg source repository: + https://github.com/open-power/pdbg diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.rst similarity index 58% rename from docs/specs/fw_cfg.txt rename to docs/specs/fw_cfg.rst index 3e6d586f66..5ad47a901c 100644 --- a/docs/specs/fw_cfg.txt +++ b/docs/specs/fw_cfg.rst @@ -1,7 +1,9 @@ +=========================================== QEMU Firmware Configuration (fw_cfg) Device =========================================== -= Guest-side Hardware Interface = +Guest-side Hardware Interface +============================= This hardware interface allows the guest to retrieve various data items (blobs) that can influence how the firmware configures itself, or may @@ -9,7 +11,8 @@ contain tables to be installed for the guest OS. Examples include device boot order, ACPI and SMBIOS tables, virtual machine UUID, SMP and NUMA information, kernel/initrd images for direct (Linux) kernel booting, etc. -== Selector (Control) Register == +Selector (Control) Register +--------------------------- * Write only * Location: platform dependent (IOport or MMIO) @@ -30,10 +33,12 @@ of 1 means the item's data can be overwritten by writes to the data register. In other words, configuration write mode is enabled when the selector value is between 0x4000-0x7fff or 0xc000-0xffff. -NOTE: As of QEMU v2.4, writes to the fw_cfg data register are no +.. NOTE:: + As of QEMU v2.4, writes to the fw_cfg data register are no longer supported, and will be ignored (treated as no-ops)! -NOTE: As of QEMU v2.9, writes are reinstated, but only through the DMA +.. NOTE:: + As of QEMU v2.9, writes are reinstated, but only through the DMA interface (see below). Furthermore, writeability of any specific item is governed independently of Bit14 in the selector key value. @@ -45,17 +50,19 @@ items are accessed with a selector value between 0x0000-0x7fff, and architecture specific configuration items are accessed with a selector value between 0x8000-0xffff. -== Data Register == +Data Register +------------- * Read/Write (writes ignored as of QEMU v2.4, but see the DMA interface) -* Location: platform dependent (IOport [*] or MMIO) +* Location: platform dependent (IOport [#]_ or MMIO) * Width: 8-bit (if IOport), 8/16/32/64-bit (if MMIO) * Endianness: string-preserving -[*] On platforms where the data register is exposed as an IOport, its -port number will always be one greater than the port number of the -selector register. In other words, the two ports overlap, and can not -be mapped separately. +.. [#] + On platforms where the data register is exposed as an IOport, its + port number will always be one greater than the port number of the + selector register. In other words, the two ports overlap, and can not + be mapped separately. The data register allows access to an array of bytes for each firmware configuration data item. The specific item is selected by writing to @@ -74,91 +81,103 @@ An N-byte wide read of the data register will return the next available N bytes of the selected firmware configuration item, as a substring, in increasing address order, similar to memcpy(). -== Register Locations == +Register Locations +------------------ -=== x86, x86_64 Register Locations === +x86, x86_64 + * Selector Register IOport: 0x510 + * Data Register IOport: 0x511 + * DMA Address IOport: 0x514 -Selector Register IOport: 0x510 -Data Register IOport: 0x511 -DMA Address IOport: 0x514 +Arm + * Selector Register address: Base + 8 (2 bytes) + * Data Register address: Base + 0 (8 bytes) + * DMA Address address: Base + 16 (8 bytes) -=== Arm Register Locations === +ACPI Interface +-------------- -Selector Register address: Base + 8 (2 bytes) -Data Register address: Base + 0 (8 bytes) -DMA Address address: Base + 16 (8 bytes) - -== ACPI Interface == - -The fw_cfg device is defined with ACPI ID "QEMU0002". Since we expect +The fw_cfg device is defined with ACPI ID ``QEMU0002``. Since we expect ACPI tables to be passed into the guest through the fw_cfg device itself, the guest-side firmware can not use ACPI to find fw_cfg. However, once the firmware is finished setting up ACPI tables and hands control over to the guest kernel, the latter can use the fw_cfg ACPI node for a more accurate inventory of in-use IOport or MMIO regions. -== Firmware Configuration Items == +Firmware Configuration Items +---------------------------- -=== Signature (Key 0x0000, FW_CFG_SIGNATURE) === +Signature (Key 0x0000, ``FW_CFG_SIGNATURE``) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The presence of the fw_cfg selector and data registers can be verified -by selecting the "signature" item using key 0x0000 (FW_CFG_SIGNATURE), +by selecting the "signature" item using key 0x0000 (``FW_CFG_SIGNATURE``), and reading four bytes from the data register. If the fw_cfg device is -present, the four bytes read will contain the characters "QEMU". +present, the four bytes read will contain the characters ``QEMU``. If the DMA interface is available, then reading the DMA Address -Register returns 0x51454d5520434647 ("QEMU CFG" in big-endian format). +Register returns 0x51454d5520434647 (``QEMU CFG`` in big-endian format). -=== Revision / feature bitmap (Key 0x0001, FW_CFG_ID) === +Revision / feature bitmap (Key 0x0001, ``FW_CFG_ID``) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A 32-bit little-endian unsigned int, this item is used to check for enabled features. - - Bit 0: traditional interface. Always set. - - Bit 1: DMA interface. -=== File Directory (Key 0x0019, FW_CFG_FILE_DIR) === +- Bit 0: traditional interface. Always set. +- Bit 1: DMA interface. + +File Directory (Key 0x0019, ``FW_CFG_FILE_DIR``) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. highlight:: c Firmware configuration items stored at selector keys 0x0020 or higher -(FW_CFG_FILE_FIRST or higher) have an associated entry in a directory +(``FW_CFG_FILE_FIRST`` or higher) have an associated entry in a directory structure, which makes it easier for guest-side firmware to identify -and retrieve them. The format of this file directory (from fw_cfg.h in -the QEMU source tree) is shown here, slightly annotated for clarity: +and retrieve them. The format of this file directory (from ``fw_cfg.h`` in +the QEMU source tree) is shown here, slightly annotated for clarity:: -struct FWCfgFiles { /* the entire file directory fw_cfg item */ - uint32_t count; /* number of entries, in big-endian format */ - struct FWCfgFile f[]; /* array of file entries, see below */ -}; + struct FWCfgFiles { /* the entire file directory fw_cfg item */ + uint32_t count; /* number of entries, in big-endian format */ + struct FWCfgFile f[]; /* array of file entries, see below */ + }; -struct FWCfgFile { /* an individual file entry, 64 bytes total */ - uint32_t size; /* size of referenced fw_cfg item, big-endian */ - uint16_t select; /* selector key of fw_cfg item, big-endian */ - uint16_t reserved; - char name[56]; /* fw_cfg item name, NUL-terminated ascii */ -}; + struct FWCfgFile { /* an individual file entry, 64 bytes total */ + uint32_t size; /* size of referenced fw_cfg item, big-endian */ + uint16_t select; /* selector key of fw_cfg item, big-endian */ + uint16_t reserved; + char name[56]; /* fw_cfg item name, NUL-terminated ascii */ + }; -=== All Other Data Items === +All Other Data Items +~~~~~~~~~~~~~~~~~~~~ Please consult the QEMU source for the most up-to-date and authoritative list of selector keys and their respective items' purpose, format and writeability. -=== Ranges === +Ranges +~~~~~~ Theoretically, there may be up to 0x4000 generic firmware configuration items, and up to 0x4000 architecturally specific ones. +=============== =========== Selector Reg. Range Usage ---------------- ----------- +=============== =========== 0x0000 - 0x3fff Generic (0x0000 - 0x3fff, generally RO, possibly RW through - the DMA interface in QEMU v2.9+) + the DMA interface in QEMU v2.9+) 0x4000 - 0x7fff Generic (0x0000 - 0x3fff, RW, ignored in QEMU v2.4+) 0x8000 - 0xbfff Arch. Specific (0x0000 - 0x3fff, generally RO, possibly RW - through the DMA interface in QEMU v2.9+) + through the DMA interface in QEMU v2.9+) 0xc000 - 0xffff Arch. Specific (0x0000 - 0x3fff, RW, ignored in v2.4+) +=============== =========== In practice, the number of allowed firmware configuration items depends on the machine type/version. -= Guest-side DMA Interface = +Guest-side DMA Interface +======================== If bit 1 of the feature bitmap is set, the DMA interface is present. This does not replace the existing fw_cfg interface, it is an add-on. This interface @@ -171,68 +190,74 @@ addresses can be triggered with just one write, whereas operations with 64-bit addresses can be triggered with one 64-bit write or two 32-bit writes, starting with the most significant half (at offset 0). -In this register, the physical address of a FWCfgDmaAccess structure in RAM -should be written. This is the format of the FWCfgDmaAccess structure: +In this register, the physical address of a ``FWCfgDmaAccess`` structure in RAM +should be written. This is the format of the ``FWCfgDmaAccess`` structure:: -typedef struct FWCfgDmaAccess { - uint32_t control; - uint32_t length; - uint64_t address; -} FWCfgDmaAccess; + typedef struct FWCfgDmaAccess { + uint32_t control; + uint32_t length; + uint64_t address; + } FWCfgDmaAccess; The fields of the structure are in big endian mode, and the field at the lowest -address is the "control" field. +address is the ``control`` field. -The "control" field has the following bits: - - Bit 0: Error - - Bit 1: Read - - Bit 2: Skip - - Bit 3: Select. The upper 16 bits are the selected index. - - Bit 4: Write +The ``control`` field has the following bits: -When an operation is triggered, if the "control" field has bit 3 set, the +- Bit 0: Error +- Bit 1: Read +- Bit 2: Skip +- Bit 3: Select. The upper 16 bits are the selected index. +- Bit 4: Write + +When an operation is triggered, if the ``control`` field has bit 3 set, the upper 16 bits are interpreted as an index of a firmware configuration item. This has the same effect as writing the selector register. -If the "control" field has bit 1 set, a read operation will be performed. -"length" bytes for the current selector and offset will be copied into the -physical RAM address specified by the "address" field. +If the ``control`` field has bit 1 set, a read operation will be performed. +``length`` bytes for the current selector and offset will be copied into the +physical RAM address specified by the ``address`` field. -If the "control" field has bit 4 set (and not bit 1), a write operation will be -performed. "length" bytes will be copied from the physical RAM address -specified by the "address" field to the current selector and offset. QEMU +If the ``control`` field has bit 4 set (and not bit 1), a write operation will be +performed. ``length`` bytes will be copied from the physical RAM address +specified by the ``address`` field to the current selector and offset. QEMU prevents starting or finishing the write beyond the end of the item associated with the current selector (i.e., the item cannot be resized). Truncated writes are dropped entirely. Writes to read-only items are also rejected. All of these -write errors set bit 0 (the error bit) in the "control" field. +write errors set bit 0 (the error bit) in the ``control`` field. -If the "control" field has bit 2 set (and neither bit 1 nor bit 4), a skip +If the ``control`` field has bit 2 set (and neither bit 1 nor bit 4), a skip operation will be performed. The offset for the current selector will be -advanced "length" bytes. +advanced ``length`` bytes. -To check the result, read the "control" field: - error bit set -> something went wrong. - all bits cleared -> transfer finished successfully. - otherwise -> transfer still in progress (doesn't happen - today due to implementation not being async, - but may in the future). +To check the result, read the ``control`` field: -= Externally Provided Items = +Error bit set + Something went wrong. +All bits cleared + Transfer finished successfully. +Otherwise + Transfer still in progress + (doesn't happen today due to implementation not being async, + but may in the future). + +Externally Provided Items +========================= Since v2.4, "file" fw_cfg items (i.e., items with selector keys above -FW_CFG_FILE_FIRST, and with a corresponding entry in the fw_cfg file +``FW_CFG_FILE_FIRST``, and with a corresponding entry in the fw_cfg file directory structure) may be inserted via the QEMU command line, using -the following syntax: +the following syntax:: -fw_cfg [name=],file= -Or +Or:: -fw_cfg [name=],string= Since v5.1, QEMU allows some objects to generate fw_cfg-specific content, the content is then associated with a "file" item using the 'gen_id' option -in the command line, using the following syntax: +in the command line, using the following syntax:: -object ,id=,[generator-specific-options] \ -fw_cfg [name=],gen_id= @@ -241,24 +266,24 @@ See QEMU man page for more documentation. Using item_name with plain ASCII characters only is recommended. -Item names beginning with "opt/" are reserved for users. QEMU will +Item names beginning with ``opt/`` are reserved for users. QEMU will never create entries with such names unless explicitly ordered by the user. To avoid clashes among different users, it is strongly recommended -that you use names beginning with opt/RFQDN/, where RFQDN is a reverse +that you use names beginning with ``opt/RFQDN/``, where RFQDN is a reverse fully qualified domain name you control. For instance, if SeaBIOS -wanted to define additional names, the prefix "opt/org.seabios/" would +wanted to define additional names, the prefix ``opt/org.seabios/`` would be appropriate. -For historical reasons, "opt/ovmf/" is reserved for OVMF firmware. +For historical reasons, ``opt/ovmf/`` is reserved for OVMF firmware. -Prefix "opt/org.qemu/" is reserved for QEMU itself. +Prefix ``opt/org.qemu/`` is reserved for QEMU itself. -Use of names not beginning with "opt/" is potentially dangerous and +Use of names not beginning with ``opt/`` is potentially dangerous and entirely unsupported. QEMU will warn if you try. -Use of names not beginning with "opt/" is tolerated with 'gen_id' (that +Use of names not beginning with ``opt/`` is tolerated with 'gen_id' (that is, the warning is suppressed), but you must know exactly what you're doing. diff --git a/docs/specs/index.rst b/docs/specs/index.rst index e10684bf53..1484e3e760 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -8,6 +8,9 @@ guest hardware that is specific to QEMU. .. toctree:: :maxdepth: 2 + pci-ids + pci-serial + pci-testdev ppc-xive ppc-spapr-xive ppc-spapr-numa @@ -20,3 +23,13 @@ guest hardware that is specific to QEMU. acpi_nvdimm acpi_erst sev-guest-firmware + fw_cfg + fsi + vmw_pvscsi-spec + edu + ivshmem-spec + pvpanic + standard-vga + virt-ctlr + vmcoreinfo + vmgenid diff --git a/docs/specs/ivshmem-spec.txt b/docs/specs/ivshmem-spec.rst similarity index 88% rename from docs/specs/ivshmem-spec.txt rename to docs/specs/ivshmem-spec.rst index 1beb3a01ec..2d8e80055b 100644 --- a/docs/specs/ivshmem-spec.txt +++ b/docs/specs/ivshmem-spec.rst @@ -1,4 +1,6 @@ -= Device Specification for Inter-VM shared memory device = +====================================================== +Device Specification for Inter-VM shared memory device +====================================================== The Inter-VM shared memory device (ivshmem) is designed to share a memory region between multiple QEMU processes running different guests @@ -12,42 +14,17 @@ can obtain one from an ivshmem server. In the latter case, the device can additionally interrupt its peers, and get interrupted by its peers. +For information on configuring the ivshmem device on the QEMU +command line, see :doc:`../system/devices/ivshmem`. -== Configuring the ivshmem PCI device == - -There are two basic configurations: - -- Just shared memory: - - -device ivshmem-plain,memdev=HMB,... - - This uses host memory backend HMB. It should have option "share" - set. - -- Shared memory plus interrupts: - - -device ivshmem-doorbell,chardev=CHR,vectors=N,... - - An ivshmem server must already be running on the host. The device - connects to the server's UNIX domain socket via character device - CHR. - - Each peer gets assigned a unique ID by the server. IDs must be - between 0 and 65535. - - 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. - - -== The ivshmem PCI device's guest interface == +The ivshmem PCI device's guest interface +======================================== The device has vendor ID 1af4, device ID 1110, revision 1. Before QEMU 2.6.0, it had revision 0. -=== PCI BARs === +PCI BARs +-------- The ivshmem PCI device has two or three BARs: @@ -59,8 +36,7 @@ There are two ways to use this device: - If you only need the shared memory part, BAR2 suffices. This way, you have access to the shared memory in the guest and can use it as - you see fit. Memnic, for example, uses ivshmem this way from guest - user space (see http://dpdk.org/browse/memnic). + you see fit. - If you additionally need the capability for peers to interrupt each other, you need BAR0 and BAR1. You will most likely want to write a @@ -77,10 +53,13 @@ accessing BAR2. Revision 0 of the device is not capable to tell guest software whether it is configured for interrupts. -=== PCI device registers === +PCI device registers +-------------------- BAR 0 contains the following registers: +:: + Offset Size Access On reset Function 0 4 read/write 0 Interrupt Mask bit 0: peer interrupt (rev 0) @@ -145,18 +124,20 @@ With multiple MSI-X vectors, different vectors can be used to indicate different events have occurred. The semantics of interrupt vectors are left to the application. - -== Interrupt infrastructure == +Interrupt infrastructure +======================== When configured for interrupts, the peers share eventfd objects in addition to shared memory. The shared resources are managed by an ivshmem server. -=== The ivshmem server === +The ivshmem server +------------------ The server listens on a UNIX domain socket. For each new client that connects to the server, the server + - picks an ID, - creates eventfd file descriptors for the interrupt vectors, - sends the ID and the file descriptor for the shared memory to the @@ -189,7 +170,8 @@ vectors. A standalone client is in contrib/ivshmem-client/. It can be useful for debugging. -=== The ivshmem Client-Server Protocol === +The ivshmem Client-Server Protocol +---------------------------------- An ivshmem device configured for interrupts connects to an ivshmem server. This section details the protocol between the two. @@ -245,7 +227,8 @@ Known bugs: * The protocol is poorly designed. -=== The ivshmem Client-Client Protocol === +The ivshmem Client-Client Protocol +---------------------------------- An ivshmem device configured for interrupts receives eventfd file descriptors for interrupting peers and getting interrupted by peers diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst new file mode 100644 index 0000000000..c0a3dec2e7 --- /dev/null +++ b/docs/specs/pci-ids.rst @@ -0,0 +1,100 @@ +================ +PCI IDs for QEMU +================ + +Red Hat, Inc. donates a part of its device ID range to QEMU, to be used for +virtual devices. The vendor IDs are 1af4 (formerly Qumranet ID) and 1b36. + +Contact Gerd Hoffmann to get a device ID assigned +for your devices. + +1af4 vendor ID +-------------- + +The 1000 -> 10ff device ID range is used as follows for virtio-pci devices. +Note that this allocation is separate from the virtio device IDs, which are +maintained as part of the virtio specification. + +1af4:1000 + network device (legacy) +1af4:1001 + block device (legacy) +1af4:1002 + balloon device (legacy) +1af4:1003 + console device (legacy) +1af4:1004 + SCSI host bus adapter device (legacy) +1af4:1005 + entropy generator device (legacy) +1af4:1009 + 9p filesystem device (legacy) +1af4:1012 + vsock device (bug compatibility) + +1af4:1040 to 1af4:10ef + ID range for modern virtio devices. The PCI device + ID is calculated from the virtio device ID by adding the + 0x1040 offset. The virtio IDs are defined in the virtio + specification. The Linux kernel has a header file with + defines for all virtio IDs (``linux/virtio_ids.h``); QEMU has a + copy in ``include/standard-headers/``. + +1af4:10f0 to 1a4f:10ff + Available for experimental usage without registration. Must get + official ID when the code leaves the test lab (i.e. when seeking + upstream merge or shipping a distro/product) to avoid conflicts. + +1af4:1100 + Used as PCI Subsystem ID for existing hardware devices emulated + by QEMU. + +1af4:1110 + ivshmem device (:doc:`ivshmem-spec`) + +All other device IDs are reserved. + +1b36 vendor ID +-------------- + +The 0000 -> 00ff device ID range is used as follows for QEMU-specific +PCI devices (other than virtio): + +1b36:0001 + PCI-PCI bridge +1b36:0002 + PCI serial port (16550A) adapter (:doc:`pci-serial`) +1b36:0003 + PCI Dual-port 16550A adapter (:doc:`pci-serial`) +1b36:0004 + PCI Quad-port 16550A adapter (:doc:`pci-serial`) +1b36:0005 + PCI test device (:doc:`pci-testdev`) +1b36:0006 + PCI Rocker Ethernet switch device +1b36:0007 + PCI SD Card Host Controller Interface (SDHCI) +1b36:0008 + PCIe host bridge +1b36:0009 + PCI Expander Bridge (-device pxb) +1b36:000a + PCI-PCI bridge (multiseat) +1b36:000b + PCIe Expander Bridge (-device pxb-pcie) +1b36:000d + PCI xhci usb host adapter +1b36:000f + mdpy (mdev sample device), ``linux/samples/vfio-mdev/mdpy.c`` +1b36:0010 + PCIe NVMe device (``-device nvme``) +1b36:0011 + PCI PVPanic device (``-device pvpanic-pci``) +1b36:0012 + PCI ACPI ERST device (``-device acpi-erst``) +1b36:0013 + PCI UFS device (``-device ufs``) + +All these devices are documented in :doc:`index`. + +The 0100 device ID is used for the QXL video card device. diff --git a/docs/specs/pci-ids.txt b/docs/specs/pci-ids.txt deleted file mode 100644 index dd6859d039..0000000000 --- a/docs/specs/pci-ids.txt +++ /dev/null @@ -1,72 +0,0 @@ - -PCI IDs for qemu -================ - -Red Hat, Inc. donates a part of its device ID range to qemu, to be used for -virtual devices. The vendor IDs are 1af4 (formerly Qumranet ID) and 1b36. - -Contact Gerd Hoffmann to get a device ID assigned -for your devices. - -1af4 vendor ID --------------- - -The 1000 -> 10ff device ID range is used as follows for virtio-pci devices. -Note that this allocation separate from the virtio device IDs, which are -maintained as part of the virtio specification. - -1af4:1000 network device (legacy) -1af4:1001 block device (legacy) -1af4:1002 balloon device (legacy) -1af4:1003 console device (legacy) -1af4:1004 SCSI host bus adapter device (legacy) -1af4:1005 entropy generator device (legacy) -1af4:1009 9p filesystem device (legacy) - -1af4:1041 network device (modern) -1af4:1042 block device (modern) -1af4:1043 console device (modern) -1af4:1044 entropy generator device (modern) -1af4:1045 balloon device (modern) -1af4:1048 SCSI host bus adapter device (modern) -1af4:1049 9p filesystem device (modern) -1af4:1050 virtio gpu device (modern) -1af4:1052 virtio input device (modern) - -1af4:10f0 Available for experimental usage without registration. Must get - to official ID when the code leaves the test lab (i.e. when seeking -1af4:10ff upstream merge or shipping a distro/product) to avoid conflicts. - -1af4:1100 Used as PCI Subsystem ID for existing hardware devices emulated - by qemu. - -1af4:1110 ivshmem device (shared memory, docs/specs/ivshmem-spec.txt) - -All other device IDs are reserved. - -1b36 vendor ID --------------- - -The 0000 -> 00ff device ID range is used as follows for QEMU-specific -PCI devices (other than virtio): - -1b36:0001 PCI-PCI bridge -1b36:0002 PCI serial port (16550A) adapter (docs/specs/pci-serial.txt) -1b36:0003 PCI Dual-port 16550A adapter (docs/specs/pci-serial.txt) -1b36:0004 PCI Quad-port 16550A adapter (docs/specs/pci-serial.txt) -1b36:0005 PCI test device (docs/specs/pci-testdev.txt) -1b36:0006 PCI Rocker Ethernet switch device -1b36:0007 PCI SD Card Host Controller Interface (SDHCI) -1b36:0008 PCIe host bridge -1b36:0009 PCI Expander Bridge (-device pxb) -1b36:000a PCI-PCI bridge (multiseat) -1b36:000b PCIe Expander Bridge (-device pxb-pcie) -1b36:000d PCI xhci usb host adapter -1b36:000f mdpy (mdev sample device), linux/samples/vfio-mdev/mdpy.c -1b36:0010 PCIe NVMe device (-device nvme) -1b36:0011 PCI PVPanic device (-device pvpanic-pci) -1b36:0012 PCI ACPI ERST device (-device acpi-erst) - -All these devices are documented in docs/specs. - -The 0100 device ID is used for the QXL video card device. diff --git a/docs/specs/pci-serial.rst b/docs/specs/pci-serial.rst new file mode 100644 index 0000000000..8d916a3669 --- /dev/null +++ b/docs/specs/pci-serial.rst @@ -0,0 +1,37 @@ +======================= +QEMU PCI serial devices +======================= + +QEMU implements some PCI serial devices which are simple PCI +wrappers around one or more 16550 UARTs. + +There is one single-port variant and two multiport-variants. Linux +guests work out-of-the box with all cards. There is a Windows inf file +(``docs/qemupciserial.inf``) to set up the cards in Windows guests. + + +Single-port card +---------------- + +Name: + ``pci-serial`` +PCI ID: + 1b36:0002 +PCI Region 0: + IO bar, 8 bytes long, with the 16550 UART mapped to it. +Interrupt: + Wired to pin A. + + +Multiport cards +--------------- + +Name: + ``pci-serial-2x``, ``pci-serial-4x`` +PCI ID: + 1b36:0003 (``-2x``) and 1b36:0004 (``-4x``) +PCI Region 0: + IO bar, with two or four 16550 UARTs mapped after each other. + The first is at offset 0, the second at offset 8, and so on. +Interrupt: + Wired to pin A. diff --git a/docs/specs/pci-serial.txt b/docs/specs/pci-serial.txt deleted file mode 100644 index 66c761f2b4..0000000000 --- a/docs/specs/pci-serial.txt +++ /dev/null @@ -1,34 +0,0 @@ - -QEMU pci serial devices -======================= - -There is one single-port variant and two muliport-variants. Linux -guests out-of-the box with all cards. There is a Windows inf file -(docs/qemupciserial.inf) to setup the single-port card in Windows -guests. - - -single-port card ----------------- - -Name: pci-serial -PCI ID: 1b36:0002 - -PCI Region 0: - IO bar, 8 bytes long, with the 16550 uart mapped to it. - Interrupt is wired to pin A. - - -multiport cards ---------------- - -Name: pci-serial-2x -PCI ID: 1b36:0003 - -Name: pci-serial-4x -PCI ID: 1b36:0004 - -PCI Region 0: - IO bar, with two/four 16550 uart mapped after each other. - The first is at offset 0, second at offset 8, ... - Interrupt is wired to pin A. diff --git a/docs/specs/pci-testdev.rst b/docs/specs/pci-testdev.rst new file mode 100644 index 0000000000..4b6d36543b --- /dev/null +++ b/docs/specs/pci-testdev.rst @@ -0,0 +1,39 @@ +==================== +QEMU PCI test device +==================== + +``pci-testdev`` is a device used for testing low level IO. + +The device implements up to three BARs: BAR0, BAR1 and BAR2. +Each of BAR 0+1 can be memory or IO. Guests must detect +BAR types and act accordingly. + +BAR 0+1 size is up to 4K bytes each. +BAR 0+1 starts with the following header: + +.. code-block:: c + + typedef struct PCITestDevHdr { + uint8_t test; /* write-only, starts a given test number */ + uint8_t width_type; /* + * read-only, type and width of access for a given test. + * 1,2,4 for byte,word or long write. + * any other value if test not supported on this BAR + */ + uint8_t pad0[2]; + uint32_t offset; /* read-only, offset in this BAR for a given test */ + uint32_t data; /* read-only, data to use for a given test */ + uint32_t count; /* for debugging. number of writes detected. */ + uint8_t name[]; /* for debugging. 0-terminated ASCII string. */ + } PCITestDevHdr; + +All registers are little endian. + +The device is expected to always implement tests 0 to N on each BAR, and to add new +tests with higher numbers. In this way a guest can scan test numbers until it +detects an access type that it does not support on this BAR, then stop. + +BAR2 is a 64bit memory BAR, without backing storage. It is disabled +by default and can be enabled using the ``membar=`` property. This +can be used to test whether guests handle PCI BARs of a specific +(possibly quite large) size correctly. diff --git a/docs/specs/pci-testdev.txt b/docs/specs/pci-testdev.txt deleted file mode 100644 index 4280a1e73c..0000000000 --- a/docs/specs/pci-testdev.txt +++ /dev/null @@ -1,31 +0,0 @@ -pci-test is a device used for testing low level IO - -device implements up to three BARs: BAR0, BAR1 and BAR2. -Each of BAR 0+1 can be memory or IO. Guests must detect -BAR types and act accordingly. - -BAR 0+1 size is up to 4K bytes each. -BAR 0+1 starts with the following header: - -typedef struct PCITestDevHdr { - uint8_t test; <- write-only, starts a given test number - uint8_t width_type; <- read-only, type and width of access for a given test. - 1,2,4 for byte,word or long write. - any other value if test not supported on this BAR - uint8_t pad0[2]; - uint32_t offset; <- read-only, offset in this BAR for a given test - uint32_t data; <- read-only, data to use for a given test - uint32_t count; <- for debugging. number of writes detected. - uint8_t name[]; <- for debugging. 0-terminated ASCII string. -} PCITestDevHdr; - -All registers are little endian. - -device is expected to always implement tests 0 to N on each BAR, and to add new -tests with higher numbers. In this way a guest can scan test numbers until it -detects an access type that it does not support on this BAR, then stop. - -BAR2 is a 64bit memory bar, without backing storage. It is disabled -by default and can be enabled using the membar= property. This -can be used to test whether guests handle pci bars of a specific -(possibly quite large) size correctly. diff --git a/docs/specs/pvpanic.txt b/docs/specs/pvpanic.rst similarity index 62% rename from docs/specs/pvpanic.txt rename to docs/specs/pvpanic.rst index 8afcde11cc..b0f27860ec 100644 --- a/docs/specs/pvpanic.txt +++ b/docs/specs/pvpanic.rst @@ -21,18 +21,23 @@ recognize. On write, the bits not recognized by the device are ignored. Software should set only bits both itself and the device recognize. 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. +~~~~~~~~~~~~~~ + +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. +bit 2 (to be implemented) + a regular guest shutdown has happened and should be processed by the host PCI Interface ------------- The PCI interface is similar to the ISA interface except that it uses an MMIO address space provided by its BAR0, 1 byte long. Any machine with a PCI bus -can enable a pvpanic device by adding '-device pvpanic-pci' to the command +can enable a pvpanic device by adding ``-device pvpanic-pci`` to the command line. ACPI Interface @@ -40,15 +45,25 @@ ACPI Interface 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, with the same semantics as the I/O port - interface. +RDPT +~~~~ -WRPT: To send a guest panic event -Arguments: Arg0 is a byte to be written, with the same semantics as - the I/O interface. -Return: None +To determine whether guest panic notification is supported. + +Arguments + None +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 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 is modified. diff --git a/docs/specs/standard-vga.rst b/docs/specs/standard-vga.rst new file mode 100644 index 0000000000..992f429ced --- /dev/null +++ b/docs/specs/standard-vga.rst @@ -0,0 +1,94 @@ + +QEMU Standard VGA +================= + +Exists in two variants, for isa and pci. + +command line switches: + +``-vga std`` + picks isa for -M isapc, otherwise pci +``-device VGA`` + pci variant +``-device isa-vga`` + isa variant +``-device secondary-vga`` + legacy-free pci variant + + +PCI spec +-------- + +Applies to the pci variant only for obvious reasons. + +PCI ID + ``1234:1111`` + +PCI Region 0 + Framebuffer memory, 16 MB in size (by default). + Size is tunable via vga_mem_mb property. + +PCI Region 1 + Reserved (so we have the option to make the framebuffer bar 64bit). + +PCI Region 2 + MMIO bar, 4096 bytes in size (QEMU 1.3+) + +PCI ROM Region + Holds the vgabios (QEMU 0.14+). + + +The legacy-free variant has no ROM and has ``PCI_CLASS_DISPLAY_OTHER`` +instead of ``PCI_CLASS_DISPLAY_VGA``. + + +IO ports used +------------- + +Doesn't apply to the legacy-free pci variant, use the MMIO bar instead. + +``03c0 - 03df`` + standard vga ports +``01ce`` + bochs vbe interface index port +``01cf`` + bochs vbe interface data port (x86 only) +``01d0`` + bochs vbe interface data port + + +Memory regions used +------------------- + +``0xe0000000`` + Framebuffer memory, isa variant only. + +The pci variant used to mirror the framebuffer bar here, QEMU 0.14+ +stops doing that (except when in ``-M pc-$old`` compat mode). + + +MMIO area spec +-------------- + +Likewise applies to the pci variant only for obvious reasons. + +``0000 - 03ff`` + edid data blob. +``0400 - 041f`` + vga ioports (``0x3c0`` to ``0x3df``), remapped 1:1. Word access + is supported, bytes are written in little endian order (aka index + port first), so indexed registers can be updated with a single + mmio write (and thus only one vmexit). +``0500 - 0515`` + bochs dispi interface registers, mapped flat without index/data ports. + Use ``(index << 1)`` as offset for (16bit) register access. +``0600 - 0607`` + QEMU extended registers. QEMU 2.2+ only. + The pci revision is 2 (or greater) when these registers are present. + The registers are 32bit. +``0600`` + QEMU extended register region size, in bytes. +``0604`` + framebuffer endianness register. + - ``0xbebebebe`` indicates big endian. + - ``0x1e1e1e1e`` indicates little endian. diff --git a/docs/specs/standard-vga.txt b/docs/specs/standard-vga.txt deleted file mode 100644 index 18f75f1b30..0000000000 --- a/docs/specs/standard-vga.txt +++ /dev/null @@ -1,81 +0,0 @@ - -QEMU Standard VGA -================= - -Exists in two variants, for isa and pci. - -command line switches: - -vga std [ picks isa for -M isapc, otherwise pci ] - -device VGA [ pci variant ] - -device isa-vga [ isa variant ] - -device secondary-vga [ legacy-free pci variant ] - - -PCI spec --------- - -Applies to the pci variant only for obvious reasons. - -PCI ID: 1234:1111 - -PCI Region 0: - Framebuffer memory, 16 MB in size (by default). - Size is tunable via vga_mem_mb property. - -PCI Region 1: - Reserved (so we have the option to make the framebuffer bar 64bit). - -PCI Region 2: - MMIO bar, 4096 bytes in size (qemu 1.3+) - -PCI ROM Region: - Holds the vgabios (qemu 0.14+). - - -The legacy-free variant has no ROM and has PCI_CLASS_DISPLAY_OTHER -instead of PCI_CLASS_DISPLAY_VGA. - - -IO ports used -------------- - -Doesn't apply to the legacy-free pci variant, use the MMIO bar instead. - -03c0 - 03df : standard vga ports -01ce : bochs vbe interface index port -01cf : bochs vbe interface data port (x86 only) -01d0 : bochs vbe interface data port - - -Memory regions used -------------------- - -0xe0000000 : Framebuffer memory, isa variant only. - -The pci variant used to mirror the framebuffer bar here, qemu 0.14+ -stops doing that (except when in -M pc-$old compat mode). - - -MMIO area spec --------------- - -Likewise applies to the pci variant only for obvious reasons. - -0000 - 03ff : edid data blob. -0400 - 041f : vga ioports (0x3c0 -> 0x3df), remapped 1:1. - word access is supported, bytes are written - in little endia order (aka index port first), - so indexed registers can be updated with a - single mmio write (and thus only one vmexit). -0500 - 0515 : bochs dispi interface registers, mapped flat - without index/data ports. Use (index << 1) - as offset for (16bit) register access. - -0600 - 0607 : qemu extended registers. qemu 2.2+ only. - The pci revision is 2 (or greater) when - these registers are present. The registers - are 32bit. - 0600 : qemu extended register region size, in bytes. - 0604 : framebuffer endianness register. - - 0xbebebebe indicates big endian. - - 0x1e1e1e1e indicates little endian. diff --git a/docs/specs/tpm.rst b/docs/specs/tpm.rst index 3be190343a..68cb8cf7e6 100644 --- a/docs/specs/tpm.rst +++ b/docs/specs/tpm.rst @@ -1,3 +1,5 @@ +.. _tpm-device: + =============== QEMU TPM Device =============== @@ -21,12 +23,16 @@ 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_i2c.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. +An I2C device support is also provided which can be instantiated in the Arm +based emulation machines. This device only supports the TPM 2 protocol. + CRB interface ------------- @@ -250,24 +256,25 @@ hardware TPM ``/dev/tpm0``: 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: +available as a module (assuming a TPM 2 is passed through): .. 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) + [ 0.012560] ACPI: TPM2 0x000000000BFFD1900 00004C (v04 BOCHS \ + BXPC 0000001 BXPC 00000001) # ls -l /dev/tpm* - crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0 + crw-rw----. 1 tss root 10, 224 Sep 6 12:36 /dev/tpm0 + crw-rw----. 1 tss rss 253, 65536 Sep 6 12:36 /dev/tpmrm0 - # find /sys/devices/ | grep pcrs$ | xargs cat - PCR-00: 35 4E 3B CE 23 9F 38 59 ... + Starting with Linux 5.12 there are PCR entries for TPM 2 in sysfs: + # find /sys/devices/ -type f | grep pcr-sha + ... + /sys/devices/LNXSYSTEM:00/LNXSYBUS:00/MSFT0101:00/tpm/tpm0/pcr-sha256/1 + ... + /sys/devices/LNXSYSTEM:00/LNXSYBUS:00/MSFT0101:00/tpm/tpm0/pcr-sha256/9 ... - PCR-23: 00 00 00 00 00 00 00 00 ... The QEMU TPM emulator device ---------------------------- @@ -304,6 +311,7 @@ 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 \ + --tpm2 \ --log level=20 Command line to start QEMU with the TPM emulator device communicating @@ -335,9 +343,9 @@ 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 \ + qemu-system-aarch64 -machine virt,gic-version=3,acpi=off \ -cpu host -m 4G \ - -nographic -no-acpi \ + -nographic -accel kvm \ -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ -tpmdev emulator,id=tpm0,chardev=chrtpm \ -device tpm-tis-device,tpmdev=tpm0 \ @@ -346,6 +354,23 @@ In case an Arm virt machine is emulated, use the following command line: -drive if=pflash,format=raw,file=flash0.img,readonly=on \ -drive if=pflash,format=raw,file=flash1.img +In case a ast2600-evb bmc machine is emulated and you want to use a TPM device +attached to I2C bus, use the following command line: + +.. code-block:: console + + qemu-system-arm -M ast2600-evb -nographic \ + -kernel arch/arm/boot/zImage \ + -dtb arch/arm/boot/dts/aspeed-ast2600-evb.dtb \ + -initrd rootfs.cpio \ + -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis-i2c,tpmdev=tpm0,bus=aspeed.i2c.bus.12,address=0x2e + + For testing, use this command to load the driver to the correct address + + echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device + In case SeaBIOS is used as firmware, it should show the TPM menu item after entering the menu with 'ESC'. @@ -365,19 +390,20 @@ 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) + [ 0.012560] ACPI: TPM2 0x000000000BFFD1900 00004C (v04 BOCHS \ + BXPC 0000001 BXPC 00000001) # ls -l /dev/tpm* - crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0 + crw-rw----. 1 tss root 10, 224 Sep 6 12:36 /dev/tpm0 + crw-rw----. 1 tss rss 253, 65536 Sep 6 12:36 /dev/tpmrm0 - # find /sys/devices/ | grep pcrs$ | xargs cat - PCR-00: 35 4E 3B CE 23 9F 38 59 ... + Starting with Linux 5.12 there are PCR entries for TPM 2 in sysfs: + # find /sys/devices/ -type f | grep pcr-sha + ... + /sys/devices/LNXSYSTEM:00/LNXSYBUS:00/MSFT0101:00/tpm/tpm0/pcr-sha256/1 + ... + /sys/devices/LNXSYSTEM:00/LNXSYBUS:00/MSFT0101:00/tpm/tpm0/pcr-sha256/9 ... - PCR-23: 00 00 00 00 00 00 00 00 ... Migration with the TPM emulator =============================== @@ -398,7 +424,8 @@ 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 + --tpm2 \ + --log level=20 In a 2nd terminal start the VM: diff --git a/docs/specs/virt-ctlr.txt b/docs/specs/virt-ctlr.rst similarity index 70% rename from docs/specs/virt-ctlr.txt rename to docs/specs/virt-ctlr.rst index 24d38084f7..ad3edde82d 100644 --- a/docs/specs/virt-ctlr.txt +++ b/docs/specs/virt-ctlr.rst @@ -1,9 +1,9 @@ Virtual System Controller ========================= -This device is a simple interface defined for the pure virtual machine with no -hardware reference implementation to allow the guest kernel to send command -to the host hypervisor. +The ``virt-ctrl`` device is a simple interface defined for the pure +virtual machine with no hardware reference implementation to allow the +guest kernel to send command to the host hypervisor. The specification can evolve, the current state is defined as below. @@ -11,14 +11,12 @@ This is a MMIO mapped device using 256 bytes. Two 32bit registers are defined: -1- the features register (read-only, address 0x00) - +the features register (read-only, address 0x00) This register allows the device to report features supported by the controller. The only feature supported for the moment is power control (0x01). -2- the command register (write-only, address 0x04) - +the command register (write-only, address 0x04) This register allows the kernel to send the commands to the hypervisor. The implemented commands are part of the power control feature and are reset (1), halt (2) and panic (3). diff --git a/docs/specs/vmcoreinfo.rst b/docs/specs/vmcoreinfo.rst new file mode 100644 index 0000000000..6541aa116f --- /dev/null +++ b/docs/specs/vmcoreinfo.rst @@ -0,0 +1,54 @@ +================= +VMCoreInfo device +================= + +The ``-device vmcoreinfo`` will create a ``fw_cfg`` entry for a guest to +store dump details. + +``etc/vmcoreinfo`` +================== + +A guest may use this ``fw_cfg`` entry to add information details to QEMU +dumps. + +The entry of 16 bytes has the following layout, in little-endian:: + + #define VMCOREINFO_FORMAT_NONE 0x0 + #define VMCOREINFO_FORMAT_ELF 0x1 + + struct FWCfgVMCoreInfo { + uint16_t host_format; /* formats host supports */ + uint16_t guest_format; /* format guest supplies */ + uint32_t size; /* size of vmcoreinfo region */ + uint64_t paddr; /* physical address of vmcoreinfo region */ + }; + +Only full write (of 16 bytes) are considered valid for further +processing of entry values. + +A write of 0 in ``guest_format`` will disable further processing of +vmcoreinfo entry values & content. + +You may write a ``guest_format`` that is not supported by the host, in +which case the entry data can be ignored by QEMU (but you may still +access it through a debugger, via ``vmcoreinfo_realize::vmcoreinfo_state``). + +Format & content +================ + +As of QEMU 2.11, only ``VMCOREINFO_FORMAT_ELF`` is supported. + +The entry gives location and size of an ELF note that is appended in +qemu dumps. + +The note format/class must be of the target bitness and the size must +be less than 1Mb. + +If the ELF note name is ``VMCOREINFO``, it is expected to be the Linux +vmcoreinfo note (see `the kernel documentation for its format +`_). +In this case, qemu dump code will read the content +as a key=value text file, looking for ``NUMBER(phys_base)`` key +value. The value is expected to be more accurate than architecture +guess of the value. This is useful for KASLR-enabled guest with +ancient tools not handling the ``VMCOREINFO`` note. diff --git a/docs/specs/vmcoreinfo.txt b/docs/specs/vmcoreinfo.txt deleted file mode 100644 index bcbca6fe47..0000000000 --- a/docs/specs/vmcoreinfo.txt +++ /dev/null @@ -1,53 +0,0 @@ -================= -VMCoreInfo device -================= - -The `-device vmcoreinfo` will create a fw_cfg entry for a guest to -store dump details. - -etc/vmcoreinfo -************** - -A guest may use this fw_cfg entry to add information details to qemu -dumps. - -The entry of 16 bytes has the following layout, in little-endian:: - -#define VMCOREINFO_FORMAT_NONE 0x0 -#define VMCOREINFO_FORMAT_ELF 0x1 - - struct FWCfgVMCoreInfo { - uint16_t host_format; /* formats host supports */ - uint16_t guest_format; /* format guest supplies */ - uint32_t size; /* size of vmcoreinfo region */ - uint64_t paddr; /* physical address of vmcoreinfo region */ - }; - -Only full write (of 16 bytes) are considered valid for further -processing of entry values. - -A write of 0 in guest_format will disable further processing of -vmcoreinfo entry values & content. - -You may write a guest_format that is not supported by the host, in -which case the entry data can be ignored by qemu (but you may still -access it through a debugger, via vmcoreinfo_realize::vmcoreinfo_state). - -Format & content -**************** - -As of qemu 2.11, only VMCOREINFO_FORMAT_ELF is supported. - -The entry gives location and size of an ELF note that is appended in -qemu dumps. - -The note format/class must be of the target bitness and the size must -be less than 1Mb. - -If the ELF note name is "VMCOREINFO", it is expected to be the Linux -vmcoreinfo note (see Documentation/ABI/testing/sysfs-kernel-vmcoreinfo -in Linux source). In this case, qemu dump code will read the content -as a key=value text file, looking for "NUMBER(phys_base)" key -value. The value is expected to be more accurate than architecture -guess of the value. This is useful for KASLR-enabled guest with -ancient tools not handling the VMCOREINFO note. diff --git a/docs/specs/vmgenid.rst b/docs/specs/vmgenid.rst new file mode 100644 index 0000000000..9a3cefcd82 --- /dev/null +++ b/docs/specs/vmgenid.rst @@ -0,0 +1,246 @@ +Virtual Machine Generation ID Device +==================================== + +.. + Copyright (C) 2016 Red Hat, Inc. + Copyright (C) 2017 Skyport Systems, 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. + +The VM generation ID (``vmgenid``) device is an emulated device which +exposes a 128-bit, cryptographically random, integer value identifier, +referred to as a Globally Unique Identifier, or GUID. + +This allows management applications (e.g. libvirt) to notify the guest +operating system when the virtual machine is executed with a different +configuration (e.g. snapshot execution or creation from a template). The +guest operating system notices the change, and is then able to react as +appropriate by marking its copies of distributed databases as dirty, +re-initializing its random number generator etc. + + +Requirements +------------ + +These requirements are extracted from the "How to implement virtual machine +generation ID support in a virtualization platform" section of +`the Microsoft Virtual Machine Generation ID specification +`_ dated August 1, 2012. + +- **R1a** The generation ID shall live in an 8-byte aligned buffer. + +- **R1b** The buffer holding the generation ID shall be in guest RAM, + ROM, or device MMIO range. + +- **R1c** The buffer holding the generation ID shall be kept separate from + areas used by the operating system. + +- **R1d** The buffer shall not be covered by an AddressRangeMemory or + AddressRangeACPI entry in the E820 or UEFI memory map. + +- **R1e** The generation ID shall not live in a page frame that could be + mapped with caching disabled. (In other words, regardless of whether the + generation ID lives in RAM, ROM or MMIO, it shall only be mapped as + cacheable.) + +- **R2** to **R5** [These AML requirements are isolated well enough in the + Microsoft specification for us to simply refer to them here.] + +- **R6** The hypervisor shall expose a _HID (hardware identifier) object + in the VMGenId device's scope that is unique to the hypervisor vendor. + + +QEMU Implementation +------------------- + +The above-mentioned specification does not dictate which ACPI descriptor table +will contain the VM Generation ID device. Other implementations (Hyper-V and +Xen) put it in the main descriptor table (Differentiated System Description +Table or DSDT). For ease of debugging and implementation, we have decided to +put it in its own Secondary System Description Table, or SSDT. + +The following is a dump of the contents from a running system:: + + # iasl -p ./SSDT -d /sys/firmware/acpi/tables/SSDT + + Intel ACPI Component Architecture + ASL+ Optimizing Compiler version 20150717-64 + Copyright (c) 2000 - 2015 Intel Corporation + + Reading ACPI table from file /sys/firmware/acpi/tables/SSDT - Length + 00000198 (0x0000C6) + ACPI: SSDT 0x0000000000000000 0000C6 (v01 BOCHS VMGENID 00000001 BXPC 00000001) + Acpi table [SSDT] successfully installed and loaded + Pass 1 parse of [SSDT] + Pass 2 parse of [SSDT] + Parsing Deferred Opcodes (Methods/Buffers/Packages/Regions) + + Parsing completed + Disassembly completed + ASL Output: ./SSDT.dsl - 1631 bytes + # cat SSDT.dsl + /* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20150717-64 + * Copyright (c) 2000 - 2015 Intel Corporation + * + * Disassembling to symbolic ASL+ operators + * + * Disassembly of /sys/firmware/acpi/tables/SSDT, Sun Feb 5 00:19:37 2017 + * + * Original Table Header: + * Signature "SSDT" + * Length 0x000000CA (202) + * Revision 0x01 + * Checksum 0x4B + * OEM ID "BOCHS " + * OEM Table ID "VMGENID" + * OEM Revision 0x00000001 (1) + * Compiler ID "BXPC" + * Compiler Version 0x00000001 (1) + */ + DefinitionBlock ("/sys/firmware/acpi/tables/SSDT.aml", "SSDT", 1, "BOCHS ", "VMGENID", 0x00000001) + { + Name (VGIA, 0x07FFF000) + Scope (\_SB) + { + Device (VGEN) + { + Name (_HID, "QEMUVGID") // _HID: Hardware ID + Name (_CID, "VM_Gen_Counter") // _CID: Compatible ID + Name (_DDN, "VM_Gen_Counter") // _DDN: DOS Device Name + Method (_STA, 0, NotSerialized) // _STA: Status + { + Local0 = 0x0F + If ((VGIA == Zero)) + { + Local0 = Zero + } + + Return (Local0) + } + + Method (ADDR, 0, NotSerialized) + { + Local0 = Package (0x02) {} + Index (Local0, Zero) = (VGIA + 0x28) + Index (Local0, One) = Zero + Return (Local0) + } + } + } + + Method (\_GPE._E05, 0, NotSerialized) // _Exx: Edge-Triggered GPE + { + Notify (\_SB.VGEN, 0x80) // Status Change + } + } + + +Design Details: +--------------- + +Requirements R1a through R1e dictate that the memory holding the +VM Generation ID must be allocated and owned by the guest firmware, +in this case BIOS or UEFI. However, to be useful, QEMU must be able to +change the contents of the memory at runtime, specifically when starting a +backed-up or snapshotted image. In order to do this, QEMU must know the +address that has been allocated. + +The mechanism chosen for this memory sharing is writable fw_cfg blobs. +These are data object that are visible to both QEMU and guests, and are +addressable as sequential files. + +More information about fw_cfg can be found in :doc:`fw_cfg`. + +Two fw_cfg blobs are used in this case: + +``/etc/vmgenid_guid`` + +- contains the actual VM Generation ID GUID +- read-only to the guest + +``/etc/vmgenid_addr`` + +- contains the address of the downloaded vmgenid blob +- writable by the guest + + +QEMU sends the following commands to the guest at startup: + +1. Allocate memory for vmgenid_guid fw_cfg blob. +2. Write the address of vmgenid_guid into the SSDT (VGIA ACPI variable as + shown above in the iasl dump). Note that this change is not propagated + back to QEMU. +3. Write the address of vmgenid_guid back to QEMU's copy of vmgenid_addr + via the fw_cfg DMA interface. + +After step 3, QEMU is able to update the contents of vmgenid_guid at will. + +Since BIOS or UEFI does not necessarily run when we wish to change the GUID, +the value of VGIA is persisted via the VMState mechanism. + +As spelled out in the specification, any change to the GUID executes an +ACPI notification. The exact handler to use is not specified, so the vmgenid +device uses the first unused one: ``\_GPE._E05``. + + +Endian-ness Considerations: +--------------------------- + +Although not specified in Microsoft's document, it is assumed that the +device is expected to use little-endian format. + +All GUID passed in via command line or monitor are treated as big-endian. +GUID values displayed via monitor are shown in big-endian format. + + +GUID Storage Format: +-------------------- + +In order to implement an OVMF "SDT Header Probe Suppressor", the contents of +the vmgenid_guid fw_cfg blob are not simply a 128-bit GUID. There is also +significant padding in order to align and fill a memory page, as shown in the +following diagram:: + + +----------------------------------+ + | SSDT with OEM Table ID = VMGENID | + +----------------------------------+ + | ... | TOP OF PAGE + | VGIA dword object ---------------|-----> +---------------------------+ + | ... | | fw-allocated array for | + | _STA method referring to VGIA | | "etc/vmgenid_guid" | + | ... | +---------------------------+ + | ADDR method referring to VGIA | | 0: OVMF SDT Header probe | + | ... | | suppressor | + +----------------------------------+ | 36: padding for 8-byte | + | alignment | + | 40: GUID | + | 56: padding to page size | + +---------------------------+ + END OF PAGE + + +Device Usage: +------------- + +The device has one property, which may be only be set using the command line: + +``guid`` + sets the value of the GUID. A special value ``auto`` instructs + QEMU to generate a new random GUID. + +For example:: + + QEMU -device vmgenid,guid="324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87" + QEMU -device vmgenid,guid=auto + +The property may be queried via QMP/HMP:: + + (QEMU) query-vm-generation-id + {"return": {"guid": "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87"}} + +Setting of this parameter is intentionally left out from the QMP/HMP +interfaces. There are no known use cases for changing the GUID once QEMU is +running, and adding this capability would greatly increase the complexity. diff --git a/docs/specs/vmgenid.txt b/docs/specs/vmgenid.txt deleted file mode 100644 index 80ff69f31c..0000000000 --- a/docs/specs/vmgenid.txt +++ /dev/null @@ -1,245 +0,0 @@ -VIRTUAL MACHINE GENERATION ID -============================= - -Copyright (C) 2016 Red Hat, Inc. -Copyright (C) 2017 Skyport Systems, 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. - -=== - -The VM generation ID (vmgenid) device is an emulated device which -exposes a 128-bit, cryptographically random, integer value identifier, -referred to as a Globally Unique Identifier, or GUID. - -This allows management applications (e.g. libvirt) to notify the guest -operating system when the virtual machine is executed with a different -configuration (e.g. snapshot execution or creation from a template). The -guest operating system notices the change, and is then able to react as -appropriate by marking its copies of distributed databases as dirty, -re-initializing its random number generator etc. - - -Requirements ------------- - -These requirements are extracted from the "How to implement virtual machine -generation ID support in a virtualization platform" section of the -specification, dated August 1, 2012. - - -The document may be found on the web at: - http://go.microsoft.com/fwlink/?LinkId=260709 - -R1a. The generation ID shall live in an 8-byte aligned buffer. - -R1b. The buffer holding the generation ID shall be in guest RAM, ROM, or device - MMIO range. - -R1c. The buffer holding the generation ID shall be kept separate from areas - used by the operating system. - -R1d. The buffer shall not be covered by an AddressRangeMemory or - AddressRangeACPI entry in the E820 or UEFI memory map. - -R1e. The generation ID shall not live in a page frame that could be mapped with - caching disabled. (In other words, regardless of whether the generation ID - lives in RAM, ROM or MMIO, it shall only be mapped as cacheable.) - -R2 to R5. [These AML requirements are isolated well enough in the Microsoft - specification for us to simply refer to them here.] - -R6. The hypervisor shall expose a _HID (hardware identifier) object in the - VMGenId device's scope that is unique to the hypervisor vendor. - - -QEMU Implementation -------------------- - -The above-mentioned specification does not dictate which ACPI descriptor table -will contain the VM Generation ID device. Other implementations (Hyper-V and -Xen) put it in the main descriptor table (Differentiated System Description -Table or DSDT). For ease of debugging and implementation, we have decided to -put it in its own Secondary System Description Table, or SSDT. - -The following is a dump of the contents from a running system: - -# iasl -p ./SSDT -d /sys/firmware/acpi/tables/SSDT - -Intel ACPI Component Architecture -ASL+ Optimizing Compiler version 20150717-64 -Copyright (c) 2000 - 2015 Intel Corporation - -Reading ACPI table from file /sys/firmware/acpi/tables/SSDT - Length -00000198 (0x0000C6) -ACPI: SSDT 0x0000000000000000 0000C6 (v01 BOCHS VMGENID 00000001 BXPC -00000001) -Acpi table [SSDT] successfully installed and loaded -Pass 1 parse of [SSDT] -Pass 2 parse of [SSDT] -Parsing Deferred Opcodes (Methods/Buffers/Packages/Regions) - -Parsing completed -Disassembly completed -ASL Output: ./SSDT.dsl - 1631 bytes -# cat SSDT.dsl -/* - * Intel ACPI Component Architecture - * AML/ASL+ Disassembler version 20150717-64 - * Copyright (c) 2000 - 2015 Intel Corporation - * - * Disassembling to symbolic ASL+ operators - * - * Disassembly of /sys/firmware/acpi/tables/SSDT, Sun Feb 5 00:19:37 2017 - * - * Original Table Header: - * Signature "SSDT" - * Length 0x000000CA (202) - * Revision 0x01 - * Checksum 0x4B - * OEM ID "BOCHS " - * OEM Table ID "VMGENID" - * OEM Revision 0x00000001 (1) - * Compiler ID "BXPC" - * Compiler Version 0x00000001 (1) - */ -DefinitionBlock ("/sys/firmware/acpi/tables/SSDT.aml", "SSDT", 1, "BOCHS ", -"VMGENID", 0x00000001) -{ - Name (VGIA, 0x07FFF000) - Scope (\_SB) - { - Device (VGEN) - { - Name (_HID, "QEMUVGID") // _HID: Hardware ID - Name (_CID, "VM_Gen_Counter") // _CID: Compatible ID - Name (_DDN, "VM_Gen_Counter") // _DDN: DOS Device Name - Method (_STA, 0, NotSerialized) // _STA: Status - { - Local0 = 0x0F - If ((VGIA == Zero)) - { - Local0 = Zero - } - - Return (Local0) - } - - Method (ADDR, 0, NotSerialized) - { - Local0 = Package (0x02) {} - Index (Local0, Zero) = (VGIA + 0x28) - Index (Local0, One) = Zero - Return (Local0) - } - } - } - - Method (\_GPE._E05, 0, NotSerialized) // _Exx: Edge-Triggered GPE - { - Notify (\_SB.VGEN, 0x80) // Status Change - } -} - - -Design Details: ---------------- - -Requirements R1a through R1e dictate that the memory holding the -VM Generation ID must be allocated and owned by the guest firmware, -in this case BIOS or UEFI. However, to be useful, QEMU must be able to -change the contents of the memory at runtime, specifically when starting a -backed-up or snapshotted image. In order to do this, QEMU must know the -address that has been allocated. - -The mechanism chosen for this memory sharing is writable fw_cfg blobs. -These are data object that are visible to both QEMU and guests, and are -addressable as sequential files. - -More information about fw_cfg can be found in "docs/specs/fw_cfg.txt" - -Two fw_cfg blobs are used in this case: - -/etc/vmgenid_guid - contains the actual VM Generation ID GUID - - read-only to the guest -/etc/vmgenid_addr - contains the address of the downloaded vmgenid blob - - writable by the guest - - -QEMU sends the following commands to the guest at startup: - -1. Allocate memory for vmgenid_guid fw_cfg blob. -2. Write the address of vmgenid_guid into the SSDT (VGIA ACPI variable as - shown above in the iasl dump). Note that this change is not propagated - back to QEMU. -3. Write the address of vmgenid_guid back to QEMU's copy of vmgenid_addr - via the fw_cfg DMA interface. - -After step 3, QEMU is able to update the contents of vmgenid_guid at will. - -Since BIOS or UEFI does not necessarily run when we wish to change the GUID, -the value of VGIA is persisted via the VMState mechanism. - -As spelled out in the specification, any change to the GUID executes an -ACPI notification. The exact handler to use is not specified, so the vmgenid -device uses the first unused one: \_GPE._E05. - - -Endian-ness Considerations: ---------------------------- - -Although not specified in Microsoft's document, it is assumed that the -device is expected to use little-endian format. - -All GUID passed in via command line or monitor are treated as big-endian. -GUID values displayed via monitor are shown in big-endian format. - - -GUID Storage Format: --------------------- - -In order to implement an OVMF "SDT Header Probe Suppressor", the contents of -the vmgenid_guid fw_cfg blob are not simply a 128-bit GUID. There is also -significant padding in order to align and fill a memory page, as shown in the -following diagram: - -+----------------------------------+ -| SSDT with OEM Table ID = VMGENID | -+----------------------------------+ -| ... | TOP OF PAGE -| VGIA dword object ---------------|-----> +---------------------------+ -| ... | | fw-allocated array for | -| _STA method referring to VGIA | | "etc/vmgenid_guid" | -| ... | +---------------------------+ -| ADDR method referring to VGIA | | 0: OVMF SDT Header probe | -| ... | | suppressor | -+----------------------------------+ | 36: padding for 8-byte | - | alignment | - | 40: GUID | - | 56: padding to page size | - +---------------------------+ - END OF PAGE - - -Device Usage: -------------- - -The device has one property, which may be only be set using the command line: - - guid - sets the value of the GUID. A special value "auto" instructs - QEMU to generate a new random GUID. - -For example: - - QEMU -device vmgenid,guid="324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87" - QEMU -device vmgenid,guid=auto - -The property may be queried via QMP/HMP: - - (QEMU) query-vm-generation-id - {"return": {"guid": "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87"}} - -Setting of this parameter is intentionally left out from the QMP/HMP -interfaces. There are no known use cases for changing the GUID once QEMU is -running, and adding this capability would greatly increase the complexity. diff --git a/docs/specs/vmw_pvscsi-spec.rst b/docs/specs/vmw_pvscsi-spec.rst new file mode 100644 index 0000000000..b6f434a418 --- /dev/null +++ b/docs/specs/vmw_pvscsi-spec.rst @@ -0,0 +1,115 @@ +============================== +VMWare PVSCSI Device Interface +============================== + +.. + Created by Dmitry Fleytman (dmitry@daynix.com), Daynix Computing LTD. + +This document describes the VMWare PVSCSI device interface specification, +based on the source code of the PVSCSI Linux driver from kernel 3.0.4. + +Overview +======== + +The interface is based on a memory area shared between hypervisor and VM. +The memory area is obtained by driver as a device IO memory resource of +``PVSCSI_MEM_SPACE_SIZE`` length. +The shared memory consists of a registers area and a rings area. +The registers area is used to raise hypervisor interrupts and issue device +commands. The rings area is used to transfer data descriptors and SCSI +commands from VM to hypervisor and to transfer messages produced by +hypervisor to VM. Data itself is transferred via virtual scatter-gather DMA. + +PVSCSI Device Registers +======================= + +The length of the registers area is 1 page +(``PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES``). The structure of the +registers area is described by the ``PVSCSIRegOffset`` enum. There +are registers to issue device commands (with optional short data), +issue device interrupts, and control interrupt masking. + +PVSCSI Device Rings +=================== + +There are three rings in shared memory: + +Request ring (``struct PVSCSIRingReqDesc *req_ring``) + ring for OS to device requests + +Completion ring (``struct PVSCSIRingCmpDesc *cmp_ring``) + ring for device request completions + +Message ring (``struct PVSCSIRingMsgDesc *msg_ring``) + ring for messages from device. This ring is optional and the + guest might not configure it. + +There is a control area (``struct PVSCSIRingsState *rings_state``) +used to control rings operation. + +PVSCSI Device to Host Interrupts +================================ + +The following interrupt types are supported by the PVSCSI device: + +Completion interrupts (completion ring notifications): + +- ``PVSCSI_INTR_CMPL_0`` +- ``PVSCSI_INTR_CMPL_1`` + +Message interrupts (message ring notifications): + +- ``PVSCSI_INTR_MSG_0`` +- ``PVSCSI_INTR_MSG_1`` + +Interrupts are controlled via the ``PVSCSI_REG_OFFSET_INTR_MASK`` +register. If a bit is set it means the interrupt is enabled, and if +it is clear then the interrupt is disabled. + +The interrupt modes supported are legacy, MSI and MSI-X. +In the case of legacy interrupts, the ``PVSCSI_REG_OFFSET_INTR_STATUS`` +register is used to check which interrupt has arrived. Interrupts are +acknowledged when the corresponding bit is written to the interrupt +status register. + +PVSCSI Device Operation Sequences +================================= + +Startup sequence +---------------- + +a. Issue ``PVSCSI_CMD_ADAPTER_RESET`` command +b. Windows driver reads interrupt status register here +c. Issue ``PVSCSI_CMD_SETUP_MSG_RING`` command with no additional data, + check status and disable device messages if error returned + (Omitted if device messages disabled by driver configuration) +d. Issue ``PVSCSI_CMD_SETUP_RINGS`` command, provide rings configuration + as ``struct PVSCSICmdDescSetupRings`` +e. Issue ``PVSCSI_CMD_SETUP_MSG_RING`` command again, provide + rings configuration as ``struct PVSCSICmdDescSetupMsgRing`` +f. Unmask completion and message (if device messages enabled) interrupts + +Shutdown sequence +----------------- + +a. Mask interrupts +b. Flush request ring using ``PVSCSI_REG_OFFSET_KICK_NON_RW_IO`` +c. Issue ``PVSCSI_CMD_ADAPTER_RESET`` command + +Send request +------------ + +a. Fill next free request ring descriptor +b. Issue ``PVSCSI_REG_OFFSET_KICK_RW_IO`` for R/W operations + or ``PVSCSI_REG_OFFSET_KICK_NON_RW_IO`` for other operations + +Abort command +------------- + +a. Issue ``PVSCSI_CMD_ABORT_CMD`` command + +Request completion processing +----------------------------- + +a. Upon completion interrupt arrival process completion + and message (if enabled) rings diff --git a/docs/specs/vmw_pvscsi-spec.txt b/docs/specs/vmw_pvscsi-spec.txt deleted file mode 100644 index 49affb2a42..0000000000 --- a/docs/specs/vmw_pvscsi-spec.txt +++ /dev/null @@ -1,92 +0,0 @@ -General Description -=================== - -This document describes VMWare PVSCSI device interface specification. -Created by Dmitry Fleytman (dmitry@daynix.com), Daynix Computing LTD. -Based on source code of PVSCSI Linux driver from kernel 3.0.4 - -PVSCSI Device Interface Overview -================================ - -The interface is based on memory area shared between hypervisor and VM. -Memory area is obtained by driver as device IO memory resource of -PVSCSI_MEM_SPACE_SIZE length. -The shared memory consists of registers area and rings area. -The registers area is used to raise hypervisor interrupts and issue device -commands. The rings area is used to transfer data descriptors and SCSI -commands from VM to hypervisor and to transfer messages produced by -hypervisor to VM. Data itself is transferred via virtual scatter-gather DMA. - -PVSCSI Device Registers -======================= - -The length of the registers area is 1 page (PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES). -The structure of the registers area is described by the PVSCSIRegOffset enum. -There are registers to issue device command (with optional short data), -issue device interrupt, control interrupts masking. - -PVSCSI Device Rings -=================== - -There are three rings in shared memory: - - 1. Request ring (struct PVSCSIRingReqDesc *req_ring) - - ring for OS to device requests - 2. Completion ring (struct PVSCSIRingCmpDesc *cmp_ring) - - ring for device request completions - 3. Message ring (struct PVSCSIRingMsgDesc *msg_ring) - - ring for messages from device. - This ring is optional and the guest might not configure it. -There is a control area (struct PVSCSIRingsState *rings_state) used to control -rings operation. - -PVSCSI Device to Host Interrupts -================================ -There are following interrupt types supported by PVSCSI device: - 1. Completion interrupts (completion ring notifications): - PVSCSI_INTR_CMPL_0 - PVSCSI_INTR_CMPL_1 - 2. Message interrupts (message ring notifications): - PVSCSI_INTR_MSG_0 - PVSCSI_INTR_MSG_1 - -Interrupts are controlled via PVSCSI_REG_OFFSET_INTR_MASK register -Bit set means interrupt enabled, bit cleared - disabled - -Interrupt modes supported are legacy, MSI and MSI-X -In case of legacy interrupts, register PVSCSI_REG_OFFSET_INTR_STATUS -is used to check which interrupt has arrived. Interrupts are -acknowledged when the corresponding bit is written to the interrupt -status register. - -PVSCSI Device Operation Sequences -================================= - -1. Startup sequence: - a. Issue PVSCSI_CMD_ADAPTER_RESET command; - aa. Windows driver reads interrupt status register here; - b. Issue PVSCSI_CMD_SETUP_MSG_RING command with no additional data, - check status and disable device messages if error returned; - (Omitted if device messages disabled by driver configuration) - c. Issue PVSCSI_CMD_SETUP_RINGS command, provide rings configuration - as struct PVSCSICmdDescSetupRings; - d. Issue PVSCSI_CMD_SETUP_MSG_RING command again, provide - rings configuration as struct PVSCSICmdDescSetupMsgRing; - e. Unmask completion and message (if device messages enabled) interrupts. - -2. Shutdown sequences - a. Mask interrupts; - b. Flush request ring using PVSCSI_REG_OFFSET_KICK_NON_RW_IO; - c. Issue PVSCSI_CMD_ADAPTER_RESET command. - -3. Send request - a. Fill next free request ring descriptor; - b. Issue PVSCSI_REG_OFFSET_KICK_RW_IO for R/W operations; - or PVSCSI_REG_OFFSET_KICK_NON_RW_IO for other operations. - -4. Abort command - a. Issue PVSCSI_CMD_ABORT_CMD command; - -5. Request completion processing - a. Upon completion interrupt arrival process completion - and message (if enabled) rings. diff --git a/docs/sphinx/dbusdomain.py b/docs/sphinx/dbusdomain.py index 2ea95af623..9872fd5bf6 100644 --- a/docs/sphinx/dbusdomain.py +++ b/docs/sphinx/dbusdomain.py @@ -400,6 +400,10 @@ class DBusDomain(Domain): for refname, obj in self.objects.items(): yield (refname, refname, obj.objtype, obj.docname, obj.node_id, 1) + def merge_domaindata(self, docnames, otherdata): + for name, obj in otherdata['objects'].items(): + if obj.docname in docnames: + self.data['objects'][name] = obj def setup(app): app.add_domain(DBusDomain) diff --git a/docs/sphinx/fakedbusdoc.py b/docs/sphinx/fakedbusdoc.py index d2c5079046..2d2e6ef640 100644 --- a/docs/sphinx/fakedbusdoc.py +++ b/docs/sphinx/fakedbusdoc.py @@ -23,3 +23,8 @@ class FakeDBusDocDirective(Directive): def setup(app: Sphinx) -> Dict[str, Any]: """Register a fake dbus-doc directive with Sphinx""" app.add_directive("dbus-doc", FakeDBusDocDirective) + + return dict( + parallel_read_safe = True, + parallel_write_safe = True + ) diff --git a/docs/sphinx/hxtool.py b/docs/sphinx/hxtool.py index fb0649a3d5..3729084a36 100644 --- a/docs/sphinx/hxtool.py +++ b/docs/sphinx/hxtool.py @@ -49,7 +49,7 @@ def serror(file, lnum, errtext): def parse_directive(line): """Return first word of line, if any""" - return re.split('\W', line)[0] + return re.split(r'\W', line)[0] def parse_defheading(file, lnum, line): """Handle a DEFHEADING directive""" @@ -78,6 +78,14 @@ def parse_archheading(file, lnum, line): serror(file, lnum, "Invalid ARCHHEADING line") return match.group(1) +def parse_srst(file, lnum, line): + """Handle an SRST directive""" + # The input should be either "SRST", or "SRST(label)". + match = re.match(r'SRST(\((.*?)\))?', line) + if match is None: + serror(file, lnum, "Invalid SRST line") + return match.group(2) + class HxtoolDocDirective(Directive): """Extract rST fragments from the specified .hx file""" required_argument = 1 @@ -113,6 +121,14 @@ class HxtoolDocDirective(Directive): serror(hxfile, lnum, 'expected ERST, found SRST') else: state = HxState.RST + label = parse_srst(hxfile, lnum, line) + if label: + rstlist.append("", hxfile, lnum - 1) + # Build label as _DOCNAME-HXNAME-LABEL + hx = os.path.splitext(os.path.basename(hxfile))[0] + refline = ".. _" + env.docname + "-" + hx + \ + "-" + label + ":" + rstlist.append(refline, hxfile, lnum - 1) elif directive == 'ERST': if state == HxState.CTEXT: serror(hxfile, lnum, 'expected SRST, found ERST') diff --git a/docs/sphinx/kerneldoc.py b/docs/sphinx/kerneldoc.py index bf44215016..72c403a737 100644 --- a/docs/sphinx/kerneldoc.py +++ b/docs/sphinx/kerneldoc.py @@ -74,6 +74,10 @@ class KernelDocDirective(Directive): # Sphinx versions cmd += ['-sphinx-version', sphinx.__version__] + # Pass through the warnings-as-errors flag + if env.config.kerneldoc_werror: + cmd += ['-Werror'] + filename = env.config.kerneldoc_srctree + '/' + self.arguments[0] export_file_patterns = [] @@ -167,6 +171,7 @@ 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_config_value('kerneldoc_werror', 0, 'env') app.add_directive('kernel-doc', KernelDocDirective) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index d791b59492..8d428c64b0 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -168,12 +168,6 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): # TODO drop fallbacks when undocumented members are outlawed if section.text: defn = section.text - elif (variants and variants.tag_member == section.member - and not section.member.type.doc_type()): - values = section.member.type.member_names() - defn = [nodes.Text('One of ')] - defn.extend(intersperse([nodes.literal('', v) for v in values], - nodes.Text(', '))) else: defn = [nodes.Text('Not documented')] @@ -186,17 +180,13 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): if variants: for v in variants.variants: - if v.type.is_implicit(): - assert not v.type.base and not v.type.variants - for m in v.type.local_members: - term = self._nodes_for_one_member(m) - term.extend(self._nodes_for_variant_when(variants, v)) - dlnode += self._make_dlitem(term, None) - else: - term = [nodes.Text('The members of '), - nodes.literal('', v.type.doc_type())] - term.extend(self._nodes_for_variant_when(variants, v)) - dlnode += self._make_dlitem(term, None) + if v.type.name == 'q_empty': + continue + assert not v.type.is_implicit() + term = [nodes.Text('The members of '), + nodes.literal('', v.type.doc_type())] + term.extend(self._nodes_for_variant_when(variants, v)) + dlnode += self._make_dlitem(term, None) if not dlnode.children: return [] @@ -249,8 +239,8 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): seen_item = False dlnode = nodes.definition_list() for section in doc.features.values(): - dlnode += self._make_dlitem([nodes.literal('', section.name)], - section.text) + dlnode += self._make_dlitem( + [nodes.literal('', section.member.name)], section.text) seen_item = True if not seen_item: @@ -268,8 +258,11 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): """Return list of doctree nodes for additional sections""" nodelist = [] for section in doc.sections: - snode = self._make_section(section.name) - if section.name and section.name.startswith('Example'): + if section.tag and section.tag == 'TODO': + # Hide TODO: sections + continue + snode = self._make_section(section.tag) + if section.tag and section.tag.startswith('Example'): snode += self._nodes_for_example(section.text) else: self._parse_text_into_node(section.text, snode) @@ -512,7 +505,7 @@ class QAPIDocDirective(Directive): except QAPIError as err: # Launder QAPI parse errors into Sphinx extension errors # so they are displayed nicely to the user - raise ExtensionError(str(err)) + raise ExtensionError(str(err)) from err def do_parse(self, rstlist, node): """Parse rST source lines and add them to the specified node diff --git a/docs/sphinx/qmp_lexer.py b/docs/sphinx/qmp_lexer.py index f7e4c0e198..a59de8a079 100644 --- a/docs/sphinx/qmp_lexer.py +++ b/docs/sphinx/qmp_lexer.py @@ -41,3 +41,8 @@ def setup(sphinx): sphinx.add_lexer('QMP', QMPExampleLexer) except errors.VersionRequirementError: sphinx.add_lexer('QMP', QMPExampleLexer()) + + return dict( + parallel_read_safe = True, + parallel_write_safe = True + ) diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index 5d0a7865d3..b2dea54eed 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -24,6 +24,8 @@ AST2500 SoC based machines : - ``sonorapass-bmc`` OCP SonoraPass BMC - ``fp5280g2-bmc`` Inspur FP5280G2 BMC - ``g220a-bmc`` Bytedance G220A BMC +- ``yosemitev2-bmc`` Facebook YosemiteV2 BMC +- ``tiogapass-bmc`` Facebook Tiogapass BMC AST2600 SoC based machines : @@ -31,7 +33,10 @@ AST2600 SoC based machines : - ``tacoma-bmc`` OpenPOWER Witherspoon POWER9 AST2600 BMC - ``rainier-bmc`` IBM Rainier POWER10 BMC - ``fuji-bmc`` Facebook Fuji BMC +- ``bletchley-bmc`` Facebook Bletchley BMC - ``fby35-bmc`` Facebook fby35 BMC +- ``qcom-dc-scm-v1-bmc`` Qualcomm DC-SCM V1 BMC +- ``qcom-firework-bmc`` Qualcomm Firework BMC Supported devices ----------------- @@ -40,7 +45,7 @@ Supported devices * Interrupt Controller (VIC) * Timer Controller * RTC Controller - * I2C Controller + * I2C Controller, including the new register interface of the AST2600 * System Control Unit (SCU) * SRAM mapping * X-DMA Controller (basic interface) @@ -57,6 +62,10 @@ Supported devices * LPC Peripheral Controller (a subset of subdevices are supported) * Hash/Crypto Engine (HACE) - Hash support only. TODO: HMAC and RSA * ADC + * Secure Boot Controller (AST2600) + * eMMC Boot Controller (dummy) + * PECI Controller (minimal) + * I3C Controller Missing devices @@ -68,12 +77,10 @@ Missing devices * Super I/O Controller * PCI-Express 1 Controller * Graphic Display Controller - * PECI Controller * MCTP Controller * Mailbox Controller * Virtual UART * eSPI Controller - * I3C Controller Boot options ------------ @@ -97,7 +104,7 @@ To boot a kernel directly from a Linux build tree: -dtb arch/arm/boot/dts/aspeed-ast2600-evb.dtb \ -initrd rootfs.cpio -The image should be attached as an MTD drive. Run : +To boot the machine from the flash image, use an MTD drive : .. code-block:: bash @@ -110,18 +117,52 @@ Options specific to Aspeed machines are : device by using the FMC controller to load the instructions, and not simply from RAM. This takes a little longer. - * ``fmc-model`` to change the FMC Flash model. FW needs support for - the chip model to boot. + * ``fmc-model`` to change the default FMC Flash model. FW needs + support for the chip model to boot. - * ``spi-model`` to change the SPI Flash model. + * ``spi-model`` to change the default SPI Flash model. -For instance, to start the ``ast2500-evb`` machine with a different -FMC chip and a bigger (64M) SPI chip, use : + * ``bmc-console`` to change the default console device. Most of the + machines use the ``UART5`` device for a boot console, which is + mapped on ``/dev/ttyS4`` under Linux, but it is not always the + case. + +To use other flash models, for instance a different FMC chip and a +bigger (64M) SPI for the ``ast2500-evb`` machine, run : .. code-block:: bash -M ast2500-evb,fmc-model=mx25l25635e,spi-model=mx66u51235f +When more flexibility is needed to define the flash devices, to use +different flash models or define all flash devices (up to 8), the +``-nodefaults`` QEMU option can be used to avoid creating the default +flash devices. + +Flash devices should then be created from the command line and attached +to a block device : + +.. code-block:: bash + + $ qemu-system-arm -M ast2600-evb \ + -blockdev node-name=fmc0,driver=file,filename=/path/to/fmc0.img \ + -device mx66u51235f,bus=ssi.0,cs=0x0,drive=fmc0 \ + -blockdev node-name=fmc1,driver=file,filename=/path/to/fmc1.img \ + -device mx66u51235f,bus=ssi.0,cs=0x1,drive=fmc1 \ + -blockdev node-name=spi1,driver=file,filename=/path/to/spi1.img \ + -device mx66u51235f,cs=0x0,bus=ssi.1,drive=spi1 \ + -nographic -nodefaults + +In that case, the machine boots fetching instructions from the FMC0 +device. It is slower to start but closer to what HW does. Using the +machine option ``execute-in-place`` has a similar effect. + +To change the boot console and use device ``UART3`` (``/dev/ttyS2`` +under Linux), use : + +.. code-block:: bash + + -M ast2500-evb,bmc-console=uart3 Aspeed minibmc family boards (``ast1030-evb``) ================================================================== @@ -154,6 +195,8 @@ Supported devices * LPC Peripheral Controller (a subset of subdevices are supported) * Hash/Crypto Engine (HACE) - Hash support only. TODO: HMAC and RSA * ADC + * Secure Boot Controller + * PECI Controller (minimal) Missing devices @@ -161,7 +204,6 @@ Missing devices * PWM and Fan Controller * Slave GPIO Controller - * PECI Controller * Mailbox Controller * Virtual UART * eSPI Controller @@ -182,3 +224,51 @@ To boot a kernel directly from a Zephyr build tree: $ qemu-system-arm -M ast1030-evb -nographic \ -kernel zephyr.elf + +Facebook Yosemite v3.5 Platform and CraterLake Server (``fby35``) +================================================================== + +Facebook has a series of multi-node compute server designs named +Yosemite. The most recent version released was +`Yosemite v3 `__. + +Yosemite v3.5 is an iteration on this design, and is very similar: there's a +baseboard with a BMC, and 4 server slots. The new server board design termed +"CraterLake" includes a Bridge IC (BIC), with room for expansion boards to +include various compute accelerators (video, inferencing, etc). At the moment, +only the first server slot's BIC is included. + +Yosemite v3.5 is itself a sled which fits into a 40U chassis, and 3 sleds +can be fit into a chassis. See `here `__ +for an example. + +In this generation, the BMC is an AST2600 and each BIC is an AST1030. The BMC +runs `OpenBMC `__, and the BIC runs +`OpenBIC `__. + +Firmware images can be retrieved from the Github releases or built from the +source code, see the README's for instructions on that. This image uses the +"fby35" machine recipe from OpenBMC, and the "yv35-cl" target from OpenBIC. +Some reference images can also be found here: + +.. code-block:: bash + + $ wget https://github.com/facebook/openbmc/releases/download/openbmc-e2294ff5d31d/fby35.mtd + $ wget https://github.com/peterdelevoryas/OpenBIC/releases/download/oby35-cl-2022.13.01/Y35BCL.elf + +Since this machine has multiple SoC's, each with their own serial console, the +recommended way to run it is to allocate a pseudoterminal for each serial +console and let the monitor use stdio. Also, starting in a paused state is +useful because it allows you to attach to the pseudoterminals before the boot +process starts. + +.. code-block:: bash + + $ qemu-system-arm -machine fby35 \ + -drive file=fby35.mtd,format=raw,if=mtd \ + -device loader,file=Y35BCL.elf,addr=0,cpu-num=2 \ + -serial pty -serial pty -serial mon:stdio \ + -display none -S + $ screen /dev/tty0 # In a separate TMUX pane, terminal window, etc. + $ screen /dev/tty1 + $ (qemu) c # Start the boot process once screen is setup. diff --git a/docs/system/arm/b-l475e-iot01a.rst b/docs/system/arm/b-l475e-iot01a.rst new file mode 100644 index 0000000000..0afef8e4f4 --- /dev/null +++ b/docs/system/arm/b-l475e-iot01a.rst @@ -0,0 +1,45 @@ +B-L475E-IOT01A IoT Node (``b-l475e-iot01a``) +============================================ + +The B-L475E-IOT01A IoT Node uses the STM32L475VG SoC which is based on +ARM Cortex-M4F core. It is part of STMicroelectronics +:doc:`STM32 boards ` and more specifically the STM32L4 +ultra-low power series. The STM32L4x5 chip runs at up to 80 MHz and +integrates 128 KiB of SRAM and up to 1MiB of Flash. The B-L475E-IOT01A board +namely features 64 Mibit QSPI Flash, BT, WiFi and RF connectivity, +USART, I2C, SPI, CAN and USB OTG, as well as a variety of sensors. + +Supported devices +""""""""""""""""" + +Currently B-L475E-IOT01A machine's only supports the following devices: + +- Cortex-M4F based STM32L4x5 SoC +- STM32L4x5 EXTI (Extended interrupts and events controller) +- STM32L4x5 SYSCFG (System configuration controller) +- STM32L4x5 RCC (Reset and clock control) +- STM32L4x5 GPIOs (General-purpose I/Os) + +Missing devices +""""""""""""""" + +The B-L475E-IOT01A does *not* support the following devices: + +- Serial ports (UART) +- Analog to Digital Converter (ADC) +- SPI controller +- Timer controller (TIMER) + +See the complete list of unimplemented peripheral devices +in the STM32L4x5 module : ``./hw/arm/stm32l4x5_soc.c`` + +Boot options +"""""""""""" + +The B-L475E-IOT01A machine can be started using the ``-kernel`` +option to load a firmware. Example: + +.. code-block:: bash + + $ qemu-system-arm -M b-l475e-iot01a -kernel firmware.bin + diff --git a/docs/system/arm/bananapi_m2u.rst b/docs/system/arm/bananapi_m2u.rst new file mode 100644 index 0000000000..587b488655 --- /dev/null +++ b/docs/system/arm/bananapi_m2u.rst @@ -0,0 +1,140 @@ +Banana Pi BPI-M2U (``bpim2u``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Banana Pi BPI-M2 Ultra is a quad-core mini single board computer built with +Allwinner A40i/R40/V40 SoC. It features 2GB of RAM and 8GB eMMC. It also +has onboard WiFi and BT. On the ports side, the BPI-M2 Ultra has 2 USB A +2.0 ports, 1 USB OTG port, 1 HDMI port, 1 audio jack, a DC power port, +and last but not least, a SATA port. + +Supported devices +""""""""""""""""" + +The Banana Pi M2U machine supports the following devices: + + * SMP (Quad Core Cortex-A7) + * Generic Interrupt Controller configuration + * SRAM mappings + * SDRAM controller + * Timer device (re-used from Allwinner A10) + * UART + * SD/MMC storage controller + * EMAC ethernet + * GMAC ethernet + * Clock Control Unit + * SATA + * TWI (I2C) + * USB 2.0 + * Hardware Watchdog + +Limitations +""""""""""" + +Currently, Banana Pi M2U does *not* support the following features: + +- Graphical output via HDMI, GPU and/or the Display Engine +- Audio output +- Real Time Clock + +Also see the 'unimplemented' array in the Allwinner R40 SoC module +for a complete list of unimplemented I/O devices: ``./hw/arm/allwinner-r40.c`` + +Boot options +"""""""""""" + +The Banana Pi M2U machine can start using the standard -kernel functionality +for loading a Linux kernel or ELF executable. Additionally, the Banana Pi M2U +machine can also emulate the BootROM which is present on an actual Allwinner R40 +based SoC, which loads the bootloader from a SD card, specified via the -sd +argument to qemu-system-arm. + +Running mainline Linux +"""""""""""""""""""""" + +To build a Linux mainline kernel that can be booted by the Banana Pi M2U 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 boot the newly build linux kernel in QEMU with the Banana Pi M2U machine, use: + +.. code-block:: bash + + $ qemu-system-arm -M bpim2u -nographic \ + -kernel /path/to/linux/arch/arm/boot/zImage \ + -append 'console=ttyS0,115200' \ + -dtb /path/to/linux/arch/arm/boot/dts/sun8i-r40-bananapi-m2-ultra.dtb + +Banana Pi M2U images +"""""""""""""""""""" + +Note that the mainline kernel does not have a root filesystem. You can choose +to build you own image with buildroot using the bananapi_m2_ultra_defconfig. +Also see https://buildroot.org for more information. + +Another possibility is to run an OpenWrt image for Banana Pi M2U which +can be downloaded from: + + https://downloads.openwrt.org/releases/22.03.3/targets/sunxi/cortexa7/ + +When using an image as an SD card, it must be resized to a power of two. This can be +done with the ``qemu-img`` command. It is recommended to only increase the image size +instead of shrinking it to a power of two, to avoid loss of data. For example, +to prepare a downloaded Armbian image, first extract it and then increase +its size to one gigabyte as follows: + +.. code-block:: bash + + $ qemu-img resize \ + openwrt-22.03.3-sunxi-cortexa7-sinovoip_bananapi-m2-ultra-ext4-sdcard.img \ + 1G + +Instead of providing a custom Linux kernel via the -kernel command you may also +choose to let the Banana Pi M2U 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 bpim2u -nic user -nographic \ + -sd openwrt-22.03.3-sunxi-cortexa7-sinovoip_bananapi-m2-ultra-ext4-sdcard.img + +Running U-Boot +"""""""""""""" + +U-Boot mainline can be build and configured using the Bananapi_M2_Ultra_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 + +The BootROM of allwinner R40 loading u-boot from the 8KiB offset of sdcard. +Let's create an bootable disk image: + +.. code-block:: bash + + $ dd if=/dev/zero of=sd.img bs=32M count=1 + $ dd if=u-boot-sunxi-with-spl.bin of=sd.img bs=1k seek=8 conv=notrunc + +And then boot it. + +.. code-block:: bash + + $ qemu-system-arm -M bpim2u -nographic -sd sd.img + +Banana Pi M2U integration tests +""""""""""""""""""""""""""""""" + +The Banana Pi M2U machine has several integration tests included. +To run the whole set of tests, build QEMU from source and simply +provide the following command: + +.. code-block:: bash + + $ cd qemu-build-dir + $ AVOCADO_ALLOW_LARGE_STORAGE=yes tests/venv/bin/avocado \ + --verbose --show=app,console run -t machine:bpim2u \ + ../tests/avocado/boot_linux_console.py diff --git a/docs/system/arm/cpu-features.rst b/docs/system/arm/cpu-features.rst index 3e626c4b68..a5fb929243 100644 --- a/docs/system/arm/cpu-features.rst +++ b/docs/system/arm/cpu-features.rst @@ -177,39 +177,32 @@ 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). +``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. + 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. + Enable kvm-no-adjvtime to disable virtual time adjustment, also + restoring the legacy (pre-5.0) behavior. - kvm-steal-time Since v5.2, kvm-steal-time is enabled by - default when KVM is enabled, the feature is - supported, and the guest is 64-bit. +``kvm-steal-time`` + Since v5.2, kvm-steal-time is enabled by default when KVM is + enabled, the feature is supported, and the guest is 64-bit. - When kvm-steal-time is enabled a 64-bit guest - can account for time its CPUs were not running - due to the host not scheduling the corresponding - VCPU threads. The accounting statistics may - influence the guest scheduler behavior and/or be - exposed to the guest userspace. + When kvm-steal-time is enabled a 64-bit guest can account for time + its CPUs were not running due to the host not scheduling the + corresponding VCPU threads. The accounting statistics may influence + the guest scheduler behavior and/or be exposed to the guest + userspace. TCG VCPU Features ================= @@ -217,16 +210,20 @@ TCG VCPU Features TCG VCPU features are CPU features that are specific to TCG. Below is the list of TCG VCPU features and their descriptions. - pauth-impdef When ``FEAT_Pauth`` is enabled, either the - *impdef* (Implementation Defined) algorithm - is enabled or the *architected* QARMA algorithm - is enabled. By default the impdef algorithm - is disabled, and QARMA is enabled. +``pauth`` + Enable or disable ``FEAT_Pauth`` entirely. - The architected QARMA algorithm has good - cryptographic properties, but can be quite slow - to emulate. The impdef algorithm used by QEMU - is non-cryptographic but significantly faster. +``pauth-impdef`` + When ``pauth`` is enabled, select the QEMU implementation defined algorithm. + +``pauth-qarma3`` + When ``pauth`` is enabled, select the architected QARMA3 algorithm. + +Without either ``pauth-impdef`` or ``pauth-qarma3`` enabled, +the architected QARMA5 algorithm is used. The architected QARMA5 +and QARMA3 algorithms have good cryptographic properties, but can +be quite slow to emulate. The impdef algorithm used by QEMU is +non-cryptographic but significantly faster. SVE CPU Properties ================== @@ -284,7 +281,7 @@ SVE CPU Property Parsing Semantics CPU Property Dependencies and Constraints"). 4) If one or more vector lengths have been explicitly enabled and at - at least one of the dependency lengths of the maximum enabled length + least one of the dependency lengths of the maximum enabled length has been explicitly disabled, then an error is generated (see constraint (2) of "SVE CPU Property Dependencies and Constraints"). @@ -372,6 +369,31 @@ verbose command lines. However, the recommended way to select vector lengths is to explicitly enable each desired length. Therefore only example's (1), (4), and (6) exhibit recommended uses of the properties. +SME CPU Property Examples +------------------------- + + 1) Disable SME:: + + $ qemu-system-aarch64 -M virt -cpu max,sme=off + + 2) Implicitly enable all vector lengths for the ``max`` CPU type:: + + $ qemu-system-aarch64 -M virt -cpu max + + 3) Only enable the 256-bit vector length:: + + $ qemu-system-aarch64 -M virt -cpu max,sme256=on + + 3) Enable the 256-bit and 1024-bit vector lengths:: + + $ qemu-system-aarch64 -M virt -cpu max,sme256=on,sme1024=on + + 4) Disable the 512-bit vector length. This results in all the other + lengths supported by ``max`` defaulting to enabled + (128, 256, 1024 and 2048):: + + $ qemu-system-aarch64 -M virt -cpu max,sve512=off + SVE User-mode Default Vector Length Property -------------------------------------------- @@ -387,3 +409,57 @@ length supported by QEMU is 256. If this property is set to ``-1`` then the default vector length is set to the maximum possible length. + +SME CPU Properties +================== + +The SME CPU properties are much like the SVE properties: ``sme`` is +used to enable or disable the entire SME feature, and ``sme`` is +used to enable or disable specific vector lengths. Finally, +``sme_fa64`` is used to enable or disable ``FEAT_SME_FA64``, which +allows execution of the "full a64" instruction set while Streaming +SVE mode is enabled. + +SME is not supported by KVM at this time. + +At least one vector length must be enabled when ``sme`` is enabled, +and all vector lengths must be powers of 2. The maximum vector +length supported by qemu is 2048 bits. Otherwise, there are no +additional constraints on the set of vector lengths supported by SME. + +SME User-mode Default Vector Length Property +-------------------------------------------- + +For qemu-aarch64, the cpu property ``sme-default-vector-length=N`` is +defined to mirror the Linux kernel parameter file +``/proc/sys/abi/sme_default_vector_length``. The default length, ``N``, +is in units of bytes and must be between 16 and 8192. +If not specified, the default vector length is 32. + +As with ``sve-default-vector-length``, if the default length is larger +than the maximum vector length enabled, the actual vector length will +be reduced. If this property is set to ``-1`` then the default vector +length is set to the maximum possible length. + +RME CPU Properties +================== + +The status of RME support with QEMU is experimental. At this time we +only support RME within the CPU proper, not within the SMMU or GIC. +The feature is enabled by the CPU property ``x-rme``, with the ``x-`` +prefix present as a reminder of the experimental status, and defaults off. + +The method for enabling RME will change in some future QEMU release +without notice or backward compatibility. + +RME Level 0 GPT Size Property +----------------------------- + +To aid firmware developers in testing different possible CPU +configurations, ``x-l0gptsz=S`` may be used to specify the value +to encode into ``GPCCR_EL3.L0GPTSZ``, a read-only field that +specifies the size of the Level 0 Granule Protection Table. +Legal values for ``S`` are 30, 34, 36, and 39; the default is 30. + +As with ``x-rme``, the ``x-l0gptsz`` property may be renamed or +removed in some future QEMU release. diff --git a/docs/system/arm/cubieboard.rst b/docs/system/arm/cubieboard.rst index 344ff8cef9..58c4a2d3ea 100644 --- a/docs/system/arm/cubieboard.rst +++ b/docs/system/arm/cubieboard.rst @@ -14,3 +14,5 @@ Emulated devices: - SDHCI - USB controller - SATA controller +- TWI (I2C) controller +- Watchdog timer diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 83b4410065..2a7bbb82dc 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -1,3 +1,5 @@ +.. _Arm Emulation: + A-profile CPU architecture support ================================== @@ -12,6 +14,7 @@ the following architecture extensions: - FEAT_BBM at level 2 (Translation table break-before-make levels) - FEAT_BF16 (AArch64 BFloat16 instructions) - FEAT_BTI (Branch Target Identification) +- FEAT_CRC32 (CRC32 instructions) - FEAT_CSV2 (Cache speculation variant 2) - FEAT_CSV2_1p1 (Cache speculation variant 2, version 1.1) - FEAT_CSV2_1p2 (Cache speculation variant 2, version 1.2) @@ -24,14 +27,27 @@ the following architecture extensions: - FEAT_Debugv8p4 (Debug changes for v8.4) - FEAT_DotProd (Advanced SIMD dot product instructions) - FEAT_DoubleFault (Double Fault Extension) +- FEAT_E0PD (Preventing EL0 access to halves of address maps) +- FEAT_ECV (Enhanced Counter Virtualization) +- FEAT_EPAC (Enhanced pointer authentication) +- FEAT_ETS (Enhanced Translation Synchronization) +- FEAT_EVT (Enhanced Virtualization Traps) - FEAT_FCMA (Floating-point complex number instructions) +- FEAT_FGT (Fine-Grained Traps) - FEAT_FHM (Floating-point half-precision multiplication instructions) - FEAT_FP16 (Half-precision floating-point data processing) +- FEAT_FPAC (Faulting on AUT* instructions) +- FEAT_FPACCOMBINE (Faulting on combined pointer authentication instructions) - FEAT_FRINTTS (Floating-point to integer instructions) - FEAT_FlagM (Flag manipulation instructions v2) - FEAT_FlagM2 (Enhancements to flag manipulation instructions) +- FEAT_GTG (Guest translation granule size) +- FEAT_HAFDBS (Hardware management of the access flag and dirty bit state) +- FEAT_HBC (Hinted conditional branches) - FEAT_HCX (Support for the HCRX_EL2 register) - FEAT_HPDS (Hierarchical permission disables) +- FEAT_HPDS2 (Translation table page-based hardware attributes) +- FEAT_HPMN0 (Setting of MDCR_EL2.HPMN to zero) - FEAT_I8MM (AArch64 Int8 matrix multiplication instructions) - FEAT_IDST (ID space trap handling) - FEAT_IESB (Implicit error synchronization event) @@ -42,19 +58,30 @@ the following architecture extensions: - FEAT_LRCPC (Load-acquire RCpc instructions) - FEAT_LRCPC2 (Load-acquire RCpc instructions v2) - FEAT_LSE (Large System Extensions) +- FEAT_LSE2 (Large System Extensions v2) - FEAT_LVA (Large Virtual Address space) +- FEAT_MOPS (Standardization of memory operations) - FEAT_MTE (Memory Tagging Extension) - FEAT_MTE2 (Memory Tagging Extension) - FEAT_MTE3 (MTE Asymmetric Fault Handling) +- FEAT_NV (Nested Virtualization) +- FEAT_NV2 (Enhanced nested virtualization support) +- FEAT_PACIMP (Pointer authentication - IMPLEMENTATION DEFINED algorithm) +- FEAT_PACQARMA3 (Pointer authentication - QARMA3 algorithm) +- FEAT_PACQARMA5 (Pointer authentication - QARMA5 algorithm) - FEAT_PAN (Privileged access never) - FEAT_PAN2 (AT S1E1R and AT S1E1W instruction variants affected by PSTATE.PAN) +- FEAT_PAN3 (Support for SCTLR_ELx.EPAN) - FEAT_PAuth (Pointer authentication) +- FEAT_PAuth2 (Enhancements to pointer authentication) - FEAT_PMULL (PMULL, PMULL2 instructions) - FEAT_PMUv3p1 (PMU Extensions v3.1) - FEAT_PMUv3p4 (PMU Extensions v3.4) +- FEAT_PMUv3p5 (PMU Extensions v3.5) - FEAT_RAS (Reliability, availability, and serviceability) - FEAT_RASv1p1 (RAS Extension v1.1) - FEAT_RDM (Advanced SIMD rounding double multiply accumulate instructions) +- FEAT_RME (Realm Management Extension) (NB: support status in QEMU is experimental) - FEAT_RNG (Random number generator) - FEAT_S2FWB (Stage 2 forced Write-Back) - FEAT_SB (Speculation Barrier) @@ -65,8 +92,13 @@ the following architecture extensions: - FEAT_SHA512 (Advanced SIMD SHA512 instructions) - FEAT_SM3 (Advanced SIMD SM3 instructions) - FEAT_SM4 (Advanced SIMD SM4 instructions) +- FEAT_SME (Scalable Matrix Extension) +- FEAT_SME_FA64 (Full A64 instruction set in Streaming SVE mode) +- FEAT_SME_F64F64 (Double-precision floating-point outer product instructions) +- FEAT_SME_I16I64 (16-bit to 64-bit integer widening outer product instructions) - FEAT_SPECRES (Speculation restriction instructions) - FEAT_SSBS (Speculative Store Bypass Safe) +- FEAT_TIDCP1 (EL0 use of IMPLEMENTATION DEFINED functionality) - FEAT_TLBIOS (TLB invalidate instructions in Outer Shareable domain) - FEAT_TLBIRANGE (TLB invalidate range instructions) - FEAT_TTCNP (Translation table Common not private translations) diff --git a/docs/system/arm/mps2.rst b/docs/system/arm/mps2.rst index 8a75beb3a0..a305935cc4 100644 --- a/docs/system/arm/mps2.rst +++ b/docs/system/arm/mps2.rst @@ -1,7 +1,7 @@ -Arm MPS2 and MPS3 boards (``mps2-an385``, ``mps2-an386``, ``mps2-an500``, ``mps2-an505``, ``mps2-an511``, ``mps2-an521``, ``mps3-an524``, ``mps3-an547``) -========================================================================================================================================================= +Arm MPS2 and MPS3 boards (``mps2-an385``, ``mps2-an386``, ``mps2-an500``, ``mps2-an505``, ``mps2-an511``, ``mps2-an521``, ``mps3-an524``, ``mps3-an536``, ``mps3-an547``) +========================================================================================================================================================================= -These board models all use Arm M-profile CPUs. +These board models use Arm M-profile or R-profile CPUs. The Arm MPS2, MPS2+ and MPS3 dev boards are FPGA based (the 2+ has a bigger FPGA but is otherwise the same as the 2; the 3 has a bigger @@ -13,6 +13,8 @@ FPGA image. QEMU models the following FPGA images: +FPGA images using M-profile CPUs: + ``mps2-an385`` Cortex-M3 as documented in Arm Application Note AN385 ``mps2-an386`` @@ -30,6 +32,11 @@ QEMU models the following FPGA images: ``mps3-an547`` Cortex-M55 on an MPS3, as documented in Arm Application Note AN547 +FPGA images using R-profile CPUs: + +``mps3-an536`` + Dual Cortex-R52 on an MPS3, as documented in Arm Application Note AN536 + Differences between QEMU and real hardware: - AN385/AN386 remapping of low 16K of memory to either ZBT SSRAM1 or to @@ -45,6 +52,30 @@ Differences between QEMU and real hardware: flash, but only as simple ROM, so attempting to rewrite the flash from the guest will fail - QEMU does not model the USB controller in MPS3 boards +- AN536 does not support runtime control of CPU reset and halt via + the SCC CFG_REG0 register. +- AN536 does not support enabling or disabling the flash and ATCM + interfaces via the SCC CFG_REG1 register. +- AN536 does not support setting of the initial vector table + base address via the SCC CFG_REG6 and CFG_REG7 register config, + and does not provide a mechanism for specifying these values at + startup, so all guest images must be built to start from TCM + (i.e. to expect the interrupt vector base at 0 from reset). +- AN536 defaults to only creating a single CPU; this is the equivalent + of the way the real FPGA image usually runs with the second Cortex-R52 + held in halt via the initial SCC CFG_REG0 register setting. You can + create the second CPU with ``-smp 2``; both CPUs will then start + execution immediately on startup. + +Note that for the AN536 the first UART is accessible only by +CPU0, and the second UART is accessible only by CPU1. The +first UART accessible shared between both CPUs is the third +UART. Guest software might therefore be built to use either +the first UART or the third UART; if you don't see any output +from the UART you are looking at, try one of the others. +(Even if the AN536 machine is started with a single CPU and so +no "CPU1-only UART", the UART numbering remains the same, +with the third UART being the first of the shared ones.) Machine-specific options """""""""""""""""""""""" diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst index ef2792076a..0424cae4b0 100644 --- a/docs/system/arm/nuvoton.rst +++ b/docs/system/arm/nuvoton.rst @@ -49,6 +49,7 @@ Supported devices * SMBus controller (SMBF) * Ethernet controller (EMC) * Tachometer + * Peripheral SPI controller (PSPI) Missing devices --------------- @@ -64,7 +65,6 @@ Missing devices * Ethernet controller (GMAC) * USB device (USBD) - * Peripheral SPI controller (PSPI) * SD/MMC host * PECI interface * PCI and PCIe root complex and bridges @@ -82,9 +82,9 @@ Boot options The Nuvoton machines can boot from an OpenBMC firmware image, or directly into a kernel using the ``-kernel`` option. OpenBMC images for ``quanta-gsj`` and -possibly others can be downloaded from the OpenPOWER jenkins : +possibly others can be downloaded from the OpenBMC jenkins : - https://openpower.xyz/ + https://jenkins.openbmc.org/ The firmware image should be attached as an MTD drive. Example : diff --git a/docs/system/arm/orangepi.rst b/docs/system/arm/orangepi.rst index 83c7445197..9afa54213b 100644 --- a/docs/system/arm/orangepi.rst +++ b/docs/system/arm/orangepi.rst @@ -25,6 +25,8 @@ The Orange Pi PC machine supports the following devices: * Clock Control Unit * System Control module * Security Identifier device + * TWI (I2C) + * Watchdog timer Limitations """"""""""" diff --git a/docs/system/arm/palm.rst b/docs/system/arm/palm.rst index 47ff9b36d4..61bc8d34f4 100644 --- a/docs/system/arm/palm.rst +++ b/docs/system/arm/palm.rst @@ -14,7 +14,7 @@ following elements: - On-chip Real Time Clock - TI TSC2102i touchscreen controller / analog-digital converter / - Audio CODEC, connected through MicroWire and |I2S| busses + Audio CODEC, connected through MicroWire and |I2S| buses - GPIO-connected matrix keypad diff --git a/docs/system/arm/raspi.rst b/docs/system/arm/raspi.rst index 922fe375a6..fbec1da6a1 100644 --- a/docs/system/arm/raspi.rst +++ b/docs/system/arm/raspi.rst @@ -1,5 +1,5 @@ -Raspberry Pi boards (``raspi0``, ``raspi1ap``, ``raspi2b``, ``raspi3ap``, ``raspi3b``) -====================================================================================== +Raspberry Pi boards (``raspi0``, ``raspi1ap``, ``raspi2b``, ``raspi3ap``, ``raspi3b``, ``raspi4b``) +=================================================================================================== QEMU provides models of the following Raspberry Pi boards: @@ -12,12 +12,13 @@ QEMU provides models of the following Raspberry Pi boards: Cortex-A53 (4 cores), 512 MiB of RAM ``raspi3b`` Cortex-A53 (4 cores), 1 GiB of RAM - +``raspi4b`` + Cortex-A72 (4 cores), 2 GiB of RAM Implemented devices ------------------- - * ARM1176JZF-S, Cortex-A7 or Cortex-A53 CPU + * ARM1176JZF-S, Cortex-A7, Cortex-A53 or Cortex-A72 CPU * Interrupt controller * DMA controller * Clock and reset controller (CPRMAN) @@ -33,11 +34,13 @@ Implemented devices * USB2 host controller (DWC2 and MPHI) * MailBox controller (MBOX) * VideoCore firmware (property) - + * Peripheral SPI controller (SPI) + * Broadcom Serial Controller (I2C) Missing devices --------------- - * Peripheral SPI controller (SPI) * Analog to Digital Converter (ADC) * Pulse Width Modulation (PWM) + * PCIE Root Port (raspi4b) + * GENET Ethernet Controller (raspi4b) diff --git a/docs/system/arm/sbsa.rst b/docs/system/arm/sbsa.rst index b499d7e927..2bf22a1d0b 100644 --- a/docs/system/arm/sbsa.rst +++ b/docs/system/arm/sbsa.rst @@ -1,17 +1,16 @@ Arm Server Base System Architecture Reference board (``sbsa-ref``) ================================================================== -While the ``virt`` board is a generic board platform that doesn't match -any real hardware the ``sbsa-ref`` board intends to look like real -hardware. The `Server Base System Architecture -`_ defines a -minimum base line of hardware support and importantly how the firmware -reports that to any operating system. It is a static system that -reports a very minimal DT to the firmware for non-discoverable -information about components affected by the qemu command line (i.e. -cpus and memory). As a result it must have a firmware specifically -built to expect a certain hardware layout (as you would in a real -machine). +The ``sbsa-ref`` board intends to look like real hardware (while the ``virt`` +board is a generic board platform that doesn't match any real hardware). + +The hardware part is defined by two specifications: + + - `Base System Architecture `__ (BSA) + - `Server Base System Architecture `__ (SBSA) + +The `Arm Base Boot Requirements `__ (BBR) +specification defines how the firmware reports that to any operating system. It is intended to be a machine for developing firmware and testing standards compliance with operating systems. @@ -19,14 +18,73 @@ standards compliance with operating systems. Supported devices """"""""""""""""" -The sbsa-ref board supports: +The ``sbsa-ref`` board supports: - A configurable number of AArch64 CPUs - GIC version 3 - System bus AHCI controller - - System bus EHCI controller + - System bus XHCI controller - CDROM and hard disc on AHCI bus - E1000E ethernet card on PCIe bus - - VGA display adaptor on PCIe bus + - Bochs display adapter on PCIe bus - A generic SBSA watchdog device + +Board to firmware interface +""""""""""""""""""""""""""" + +``sbsa-ref`` is a static system that reports a very minimal devicetree to the +firmware for non-discoverable information about system components. This +includes both internal hardware and parts affected by the qemu command line +(i.e. CPUs and memory). As a result it must have a firmware specifically built +to expect a certain hardware layout (as you would in a real machine). + +Note +'''' + +QEMU provides the guest EL3 firmware with minimal information about hardware +platform using minimalistic devicetree. This is not a Linux devicetree. It is +not even a firmware devicetree. + +It is information passed from QEMU to describe the information a hardware +platform would have other mechanisms to discover at runtime, that are affected +by the QEMU command line. + +Ultimately this devicetree may be replaced by IPC calls to an emulated SCP. + +DeviceTree information +'''''''''''''''''''''' + +The devicetree reports: + + - CPUs + - memory + - platform version + - GIC addresses + - NUMA node id for CPUs and memory + +Platform version +'''''''''''''''' + +The platform version is only for informing platform firmware about +what kind of ``sbsa-ref`` board it is running on. It is neither +a QEMU versioned machine type nor a reflection of the level of the +SBSA/SystemReady SR support provided. + +The ``machine-version-major`` value is updated when changes breaking +fw compatibility are introduced. The ``machine-version-minor`` value +is updated when features are added that don't break fw compatibility. + +Platform version changes: + +0.0 + Devicetree holds information about CPUs, memory and platform version. + +0.1 + GIC information is present in devicetree. + +0.2 + GIC ITS information is present in devicetree. + +0.3 + The USB controller is an XHCI device, not EHCI. diff --git a/docs/system/arm/stm32.rst b/docs/system/arm/stm32.rst index 508b92cf86..3b640f3ee0 100644 --- a/docs/system/arm/stm32.rst +++ b/docs/system/arm/stm32.rst @@ -16,10 +16,13 @@ based on this chip : - ``netduino2`` Netduino 2 board with STM32F205RFT6 microcontroller -The STM32F4 series is based on ARM Cortex-M4F core. This series is pin-to-pin -compatible with STM32F2 series. The following machines are based on this chip : +The STM32F4 series is based on ARM Cortex-M4F core, as well as the STM32L4 +ultra-low-power series. The STM32F4 series is pin-to-pin compatible with STM32F2 series. +The following machines are based on this ARM Cortex-M4F chip : - ``netduinoplus2`` Netduino Plus 2 board with STM32F405RGT6 microcontroller +- ``olimex-stm32-h405`` Olimex STM32 H405 board with STM32F405RGT6 microcontroller +- ``b-l475e-iot01a`` :doc:`B-L475E-IOT01A IoT Node ` board with STM32L475VG microcontroller There are many other STM32 series that are currently not supported by QEMU. diff --git a/docs/system/arm/vexpress.rst b/docs/system/arm/vexpress.rst index 3e3839e923..38f29c73e7 100644 --- a/docs/system/arm/vexpress.rst +++ b/docs/system/arm/vexpress.rst @@ -58,6 +58,9 @@ Other differences between the hardware and the QEMU model: ``vexpress-a15``, and have IRQs from 40 upwards. If a dtb is provided on the command line then QEMU will edit it to include suitable entries describing these transports for the guest. +- QEMU does not currently support either dynamic or static remapping + of the area of memory at address 0: it is always mapped to alias + the first flash bank Booting a Linux kernel ---------------------- diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index 3d1058a80c..26fcba00b7 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -52,18 +52,36 @@ Supported guest CPU types: - ``cortex-a7`` (32-bit) - ``cortex-a15`` (32-bit; the default) +- ``cortex-a35`` (64-bit) - ``cortex-a53`` (64-bit) +- ``cortex-a55`` (64-bit) - ``cortex-a57`` (64-bit) - ``cortex-a72`` (64-bit) - ``cortex-a76`` (64-bit) +- ``cortex-a710`` (64-bit) - ``a64fx`` (64-bit) - ``host`` (with KVM only) - ``neoverse-n1`` (64-bit) +- ``neoverse-v1`` (64-bit) +- ``neoverse-n2`` (64-bit) - ``max`` (same as ``host`` for KVM; best possible emulation with TCG) Note that the default is ``cortex-a15``, so for an AArch64 guest you must specify a CPU type. +Also, please note that passing ``max`` CPU (i.e. ``-cpu max``) won't +enable all the CPU features for a given ``virt`` machine. Where a CPU +architectural feature requires support in both the CPU itself and in the +wider system (e.g. the MTE feature), it may not be enabled by default, +but instead requires a machine option to enable it. + +For example, MTE support must be enabled with ``-machine virt,mte=on``, +as well as by selecting an MTE-capable CPU (e.g., ``max``) with the +``-cpu`` option. + +See the machine-specific options below, or check them for a given machine +by passing the ``help`` suboption, like: ``-machine virt-9.0,help``. + Graphics output is available, but unlike the x86 PC machine types there is no default display device enabled: you should select one from the Display devices section of "-device help". The recommended option @@ -91,7 +109,30 @@ mte highmem Set ``on``/``off`` to enable/disable placing devices and RAM in physical address space above 32 bits. The default is ``on`` for machine types - later than ``virt-2.12``. + later than ``virt-2.12`` when the CPU supports an address space + bigger than 32 bits (i.e. 64-bit CPUs, and 32-bit CPUs with the + Large Physical Address Extension (LPAE) feature). If you want to + boot a 32-bit kernel which does not have ``CONFIG_LPAE`` enabled on + a CPU type which implements LPAE, you will need to manually set + this to ``off``; otherwise some devices, such as the PCI controller, + will not be accessible. + +compact-highmem + Set ``on``/``off`` to enable/disable the compact layout for high memory regions. + The default is ``on`` for machine types later than ``virt-7.2``. + +highmem-redists + Set ``on``/``off`` to enable/disable the high memory region for GICv3 or + GICv4 redistributor. The default is ``on``. Setting this to ``off`` will + limit the maximum number of CPUs when GICv3 or GICv4 is used. + +highmem-ecam + Set ``on``/``off`` to enable/disable the high memory region for PCI ECAM. + The default is ``on`` for machine types later than ``virt-3.0``. + +highmem-mmio + Set ``on``/``off`` to enable/disable the high memory region for PCI MMIO. + The default is ``on``. gic-version Specify the version of the Generic Interrupt Controller (GIC) to provide. @@ -126,13 +167,18 @@ ras Set ``on``/``off`` to enable/disable reporting host memory errors to a guest using ACPI and guest external abort exceptions. The default is off. +dtb-randomness + Set ``on``/``off`` to pass random seeds via the guest DTB + rng-seed and kaslr-seed nodes (in both "/chosen" and + "/secure-chosen") to use for features like the random number + generator and address space randomisation. The default is + ``on``. You will want to disable it if your trusted boot chain + will verify the DTB it is passed, since this option causes the + DTB to be non-deterministic. It would be the responsibility of + the firmware to come up with a seed and pass it on if it wants to. + dtb-kaslr-seed - Set ``on``/``off`` to pass a random seed via the guest dtb - kaslr-seed node (in both "/chosen" and /secure-chosen) to use - for features like address space randomisation. The default is - ``on``. You will want to disable it if your trusted boot chain will - verify the DTB it is passed. It would be the responsibility of the - firmware to come up with a seed and pass it on if it wants to. + A deprecated synonym for dtb-randomness. Linux guest kernel configuration """""""""""""""""""""""""""""""" diff --git a/docs/system/arm/xenpvh.rst b/docs/system/arm/xenpvh.rst new file mode 100644 index 0000000000..430ac2c02e --- /dev/null +++ b/docs/system/arm/xenpvh.rst @@ -0,0 +1,39 @@ +Xen Device Emulation Backend (``xenpvh``) +========================================= + +This machine is a little unusual compared to others as QEMU just acts +as an IOREQ server to register/connect with Xen Hypervisor. Control of +the VMs themselves is left to the Xen tooling. + +When TPM is enabled, this machine also creates a tpm-tis-device at a +user input tpm base address, adds a TPM emulator and connects to a +swtpm application running on host machine via chardev socket. This +enables xenpvh to support TPM functionalities for a guest domain. + +More information about TPM use and installing swtpm linux application +can be found in the :ref:`tpm-device` section. + +Example for starting swtpm on host machine: + +.. code-block:: console + + mkdir /tmp/vtpm2 + swtpm socket --tpmstate dir=/tmp/vtpm2 \ + --ctrl type=unixio,path=/tmp/vtpm2/swtpm-sock & + +Sample QEMU xenpvh commands for running and connecting with Xen: + +.. code-block:: console + + qemu-system-aarch64 -xen-domid 1 \ + -chardev socket,id=libxl-cmd,path=qmp-libxl-1,server=on,wait=off \ + -mon chardev=libxl-cmd,mode=control \ + -chardev socket,id=libxenstat-cmd,path=qmp-libxenstat-1,server=on,wait=off \ + -mon chardev=libxenstat-cmd,mode=control \ + -xen-attach -name guest0 -vnc none -display none -nographic \ + -machine xenpvh -m 1301 \ + -chardev socket,id=chrtpm,path=tmp/vtpm2/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm -machine tpm-base-addr=0x0C000000 + +In above QEMU command, last two lines are for connecting xenpvh QEMU to swtpm +via chardev socket. diff --git a/docs/system/arm/xlnx-versal-virt.rst b/docs/system/arm/xlnx-versal-virt.rst index 92ad10d2da..0bafc76469 100644 --- a/docs/system/arm/xlnx-versal-virt.rst +++ b/docs/system/arm/xlnx-versal-virt.rst @@ -34,6 +34,7 @@ Implemented devices: - DDR memory - BBRAM (36 bytes of Battery-backed RAM) - eFUSE (3072 bytes of one-time field-programmable bit array) +- 2 CANFDs QEMU does not yet model any other devices, including the PL and the AI Engine. @@ -193,7 +194,7 @@ To use a different index value, N, from default of 0, add: .. code-block:: bash - -global xlnx,bbram-ctrl.drive-index=N + -global driver=xlnx.bbram-ctrl,property=drive-index,value=N eFUSE File Backend """""""""""""""""" @@ -211,7 +212,7 @@ To use a different index value, N, from default of 1, add: .. code-block:: bash - -global xlnx,efuse.drive-index=N + -global xlnx-efuse.drive-index=N .. warning:: In actual physical Versal, BBRAM and eFUSE contain sensitive data. @@ -224,3 +225,33 @@ To use a different index value, N, from default of 1, add: Better yet, do not use actual product data when running guest image on this Xilinx Versal Virt board. + +Using CANFDs for Versal Virt +"""""""""""""""""""""""""""" +Versal CANFD controller is developed based on SocketCAN and QEMU CAN bus +implementation. Bus connection and socketCAN connection for each CAN module +can be set through command lines. + +To connect both CANFD0 and CANFD1 on the same bus: + +.. code-block:: bash + + -object can-bus,id=canbus -machine canbus0=canbus -machine canbus1=canbus + +To connect CANFD0 and CANFD1 to separate buses: + +.. code-block:: bash + + -object can-bus,id=canbus0 -object can-bus,id=canbus1 \ + -machine canbus0=canbus0 -machine canbus1=canbus1 + +The SocketCAN interface can connect to a Physical or a Virtual CAN interfaces on +the host machine. Please check this document to learn about CAN interface on +Linux: docs/system/devices/can.rst + +To connect CANFD0 and CANFD1 to host machine's CAN interface can0: + +.. code-block:: bash + + -object can-bus,id=canbus -machine canbus0=canbus -machine canbus1=canbus + -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus diff --git a/docs/system/arm/xscale.rst b/docs/system/arm/xscale.rst index d2d5949e10..e239136c3c 100644 --- a/docs/system/arm/xscale.rst +++ b/docs/system/arm/xscale.rst @@ -32,4 +32,4 @@ The clamshell PDA models emulation includes the following peripherals: - Three on-chip UARTs -- WM8750 audio CODEC on |I2C| and |I2S| busses +- WM8750 audio CODEC on |I2C| and |I2S| buses diff --git a/docs/system/cpu-models-x86-abi.csv b/docs/system/cpu-models-x86-abi.csv index f3f3b60be1..38b9bae310 100644 --- a/docs/system/cpu-models-x86-abi.csv +++ b/docs/system/cpu-models-x86-abi.csv @@ -8,27 +8,37 @@ Cascadelake-Server-v1,✅,✅,✅,✅ Cascadelake-Server-v2,✅,✅,✅,✅ Cascadelake-Server-v3,✅,✅,✅,✅ Cascadelake-Server-v4,✅,✅,✅,✅ +Cascadelake-Server-v5,✅,✅,✅,✅ Conroe-v1,✅,,, Cooperlake-v1,✅,✅,✅,✅ +Cooperlake-v2,✅,✅,✅,✅ Denverton-v1,✅,✅,, Denverton-v2,✅,✅,, +Denverton-v3,✅,✅,, Dhyana-v1,✅,✅,✅, +Dhyana-v2,✅,✅,✅, +EPYC-Genoa-v1,✅,✅,✅,✅ EPYC-Milan-v1,✅,✅,✅, +EPYC-Milan-v2,✅,✅,✅, EPYC-Rome-v1,✅,✅,✅, EPYC-Rome-v2,✅,✅,✅, +EPYC-Rome-v3,✅,✅,✅, +EPYC-Rome-v4,✅,✅,✅, EPYC-v1,✅,✅,✅, EPYC-v2,✅,✅,✅, EPYC-v3,✅,✅,✅, +EPYC-v4,✅,✅,✅, +GraniteRapids-v1,✅,✅,✅,✅ Haswell-v1,✅,✅,✅, Haswell-v2,✅,✅,✅, Haswell-v3,✅,✅,✅, Haswell-v4,✅,✅,✅, -Icelake-Client-v1,✅,✅,✅, -Icelake-Client-v2,✅,✅,✅, Icelake-Server-v1,✅,✅,✅,✅ Icelake-Server-v2,✅,✅,✅,✅ Icelake-Server-v3,✅,✅,✅,✅ Icelake-Server-v4,✅,✅,✅,✅ +Icelake-Server-v5,✅,✅,✅,✅ +Icelake-Server-v6,✅,✅,✅,✅ IvyBridge-v1,✅,✅,, IvyBridge-v2,✅,✅,, KnightsMill-v1,✅,✅,✅, @@ -42,15 +52,21 @@ Opteron_G5-v1,✅,✅,, Penryn-v1,✅,,, SandyBridge-v1,✅,✅,, SandyBridge-v2,✅,✅,, +SapphireRapids-v1,✅,✅,✅,✅ +SapphireRapids-v2,✅,✅,✅,✅ Skylake-Client-v1,✅,✅,✅, Skylake-Client-v2,✅,✅,✅, Skylake-Client-v3,✅,✅,✅, +Skylake-Client-v4,✅,✅,✅, Skylake-Server-v1,✅,✅,✅,✅ Skylake-Server-v2,✅,✅,✅,✅ Skylake-Server-v3,✅,✅,✅,✅ Skylake-Server-v4,✅,✅,✅,✅ +Skylake-Server-v5,✅,✅,✅,✅ Snowridge-v1,✅,✅,, Snowridge-v2,✅,✅,, +Snowridge-v3,✅,✅,, +Snowridge-v4,✅,✅,, Westmere-v1,✅,✅,, Westmere-v2,✅,✅,, athlon-v1,,,, diff --git a/docs/system/cpu-models-x86.rst.inc b/docs/system/cpu-models-x86.rst.inc index 7f6368f999..ba27b5683f 100644 --- a/docs/system/cpu-models-x86.rst.inc +++ b/docs/system/cpu-models-x86.rst.inc @@ -58,7 +58,7 @@ depending on the machine type is in use. .. csv-table:: x86-64 ABI compatibility levels :file: cpu-models-x86-abi.csv :widths: 40,15,15,15,15 - :header-rows: 2 + :header-rows: 1 Preferred CPU models for Intel x86 hosts diff --git a/docs/system/device-emulation.rst b/docs/system/device-emulation.rst index 3b729b920d..f19777411c 100644 --- a/docs/system/device-emulation.rst +++ b/docs/system/device-emulation.rst @@ -86,9 +86,16 @@ Emulated Devices devices/ccid.rst devices/cxl.rst devices/ivshmem.rst + devices/keyboard.rst devices/net.rst devices/nvme.rst devices/usb.rst devices/vhost-user.rst + devices/virtio-gpu.rst devices/virtio-pmem.rst + devices/virtio-snd.rst + devices/vhost-user-input.rst devices/vhost-user-rng.rst + devices/canokey.rst + devices/usb-u2f.rst + devices/igb.rst diff --git a/docs/system/devices/can.rst b/docs/system/devices/can.rst index fe37af8223..09121836fd 100644 --- a/docs/system/devices/can.rst +++ b/docs/system/devices/can.rst @@ -1,12 +1,12 @@ CAN Bus Emulation Support ========================= The CAN bus emulation provides mechanism to connect multiple -emulated CAN controller chips together by one or multiple CAN busses -(the controller device "canbus" parameter). The individual busses +emulated CAN controller chips together by one or multiple CAN buses +(the controller device "canbus" parameter). The individual buses can be connected to host system CAN API (at this time only Linux SocketCAN is supported). -The concept of busses is generic and different CAN controllers +The concept of buses is generic and different CAN controllers can be implemented. The initial submission implemented SJA1000 controller which @@ -169,8 +169,9 @@ and with bitrate switch:: cangen can0 -b -The test can be run viceversa, generate messages in the guest system and capture them -in the host one and much more combinations. +The test can also be run the other way around, generating messages in the +guest system and capturing them in the host system. Other combinations are +also possible. Links to other resources ------------------------ diff --git a/docs/system/devices/canokey.rst b/docs/system/devices/canokey.rst new file mode 100644 index 0000000000..7f3664963f --- /dev/null +++ b/docs/system/devices/canokey.rst @@ -0,0 +1,158 @@ +.. _canokey: + +CanoKey QEMU +------------ + +CanoKey [1]_ is an open-source secure key with supports of + +* U2F / FIDO2 with Ed25519 and HMAC-secret +* OpenPGP Card V3.4 with RSA4096, Ed25519 and more [2]_ +* PIV (NIST SP 800-73-4) +* HOTP / TOTP +* NDEF + +All these platform-independent features are in canokey-core [3]_. + +For different platforms, CanoKey has different implementations, +including both hardware implementations and virtual cards: + +* CanoKey STM32 [4]_ +* CanoKey Pigeon [5]_ +* (virt-card) CanoKey USB/IP +* (virt-card) CanoKey FunctionFS + +In QEMU, yet another CanoKey virt-card is implemented. +CanoKey QEMU exposes itself as a USB device to the guest OS. + +With the same software configuration as a hardware key, +the guest OS can use all the functionalities of a secure key as if +there was actually an hardware key plugged in. + +CanoKey QEMU provides much convenience for debugging: + +* libcanokey-qemu supports debugging output thus developers can + inspect what happens inside a secure key +* CanoKey QEMU supports trace event thus event +* QEMU USB stack supports pcap thus USB packet between the guest + and key can be captured and analysed + +Then for developers: + +* For developers on software with secure key support (e.g. FIDO2, OpenPGP), + they can see what happens inside the secure key +* For secure key developers, USB packets between guest OS and CanoKey + can be easily captured and analysed + +Also since this is a virtual card, it can be easily used in CI for testing +on code coping with secure key. + +Building +======== + +libcanokey-qemu is required to use CanoKey QEMU. + +.. code-block:: shell + + git clone https://github.com/canokeys/canokey-qemu + mkdir canokey-qemu/build + pushd canokey-qemu/build + +If you want to install libcanokey-qemu in a different place, +add ``-DCMAKE_INSTALL_PREFIX=/path/to/your/place`` to cmake below. + +.. code-block:: shell + + cmake .. + make + make install # may need sudo + popd + +Then configuring and building: + +.. code-block:: shell + + # depending on your env, lib/pkgconfig can be lib64/pkgconfig + export PKG_CONFIG_PATH=/path/to/your/place/lib/pkgconfig:$PKG_CONFIG_PATH + ./configure --enable-canokey && make + +Using CanoKey QEMU +================== + +CanoKey QEMU stores all its data on a file of the host specified by the argument +when invoking qemu. + +.. parsed-literal:: + + |qemu_system| -usb -device canokey,file=$HOME/.canokey-file + +Note: you should keep this file carefully as it may contain your private key! + +The first time when the file is used, it is created and initialized by CanoKey, +afterwards CanoKey QEMU would just read this file. + +After the guest OS boots, you can check that there is a USB device. + +For example, If the guest OS is an Linux machine. You may invoke lsusb +and find CanoKey QEMU there: + +.. code-block:: shell + + $ lsusb + Bus 001 Device 002: ID 20a0:42d4 Clay Logic CanoKey QEMU + +You may setup the key as guided in [6]_. The console for the key is at [7]_. + +Debugging +========= + +CanoKey QEMU consists of two parts, ``libcanokey-qemu.so`` and ``canokey.c``, +the latter of which resides in QEMU. The former provides core functionality +of a secure key while the latter provides platform-dependent functions: +USB packet handling. + +If you want to trace what happens inside the secure key, when compiling +libcanokey-qemu, you should add ``-DQEMU_DEBUG_OUTPUT=ON`` in cmake command +line: + +.. code-block:: shell + + cmake .. -DQEMU_DEBUG_OUTPUT=ON + +If you want to trace events happened in canokey.c, use + +.. parsed-literal:: + + |qemu_system| --trace "canokey_*" \\ + -usb -device canokey,file=$HOME/.canokey-file + +If you want to capture USB packets between the guest and the host, you can: + +.. parsed-literal:: + + |qemu_system| -usb -device canokey,file=$HOME/.canokey-file,pcap=key.pcap + +Limitations +=========== + +Currently libcanokey-qemu.so has dozens of global variables as it was originally +designed for embedded systems. Thus one qemu instance can not have +multiple CanoKey QEMU running, namely you can not + +.. parsed-literal:: + + |qemu_system| -usb -device canokey,file=$HOME/.canokey-file \\ + -device canokey,file=$HOME/.canokey-file2 + +Also, there is no lock on canokey-file, thus two CanoKey QEMU instance +can not read one canokey-file at the same time. + +References +========== + +.. [1] ``_ +.. [2] ``_ +.. [3] ``_ +.. [4] ``_ +.. [5] ``_ +.. [6] ``_ +.. [7] ``_ diff --git a/docs/system/devices/cxl.rst b/docs/system/devices/cxl.rst index 9293cbf01a..10a0e9bc9f 100644 --- a/docs/system/devices/cxl.rst +++ b/docs/system/devices/cxl.rst @@ -15,7 +15,7 @@ with CXL Host Bridges, which have CXL Root Ports which may be directly attached to CXL or PCI End Points. Alternatively there may be CXL Switches with CXL and PCI Endpoints attached below them. In many cases additional control and capabilities are exposed via PCI Express interfaces. -This sharing of interfaces and hence emulation code is is reflected +This sharing of interfaces and hence emulation code is reflected in how the devices are emulated in QEMU. In most cases the various CXL elements are built upon an equivalent PCIe devices. @@ -83,7 +83,7 @@ CXL Fixed Memory Windows (CFMW) A CFMW consists of a particular range of Host Physical Address space which is routed to particular CXL Host Bridges. At time of generic software initialization it will have a particularly interleaving -configuration and associated Quality of Serice Throtling Group (QTG). +configuration and associated Quality of Service Throttling Group (QTG). This information is available to system software, when making decisions about how to configure interleave across available CXL memory devices. It is provide as CFMW Structures (CFMWS) in @@ -98,7 +98,7 @@ specification defined register interface called CXL Host Bridge Component Registers (CHBCR). The location of this CHBCR MMIO space is described to system software via a CXL Host Bridge Structure (CHBS) in the CEDT ACPI table. The actual interfaces -are identical to those used for other parts of the CXL heirarchy +are identical to those used for other parts of the CXL hierarchy as CXL Component Registers in PCI BARs. Interfaces provided include: @@ -111,15 +111,13 @@ Interfaces provided include: CXL Root Ports (CXL RP) ~~~~~~~~~~~~~~~~~~~~~~~ -A CXL Root Port servers te same purpose as a PCIe Root Port. +A CXL Root Port serves the same purpose as a PCIe Root Port. There are a number of CXL specific Designated Vendor Specific Extended Capabilities (DVSEC) in PCIe Configuration Space and associated component register access via PCI bars. CXL Switch ~~~~~~~~~~ -Not yet implemented in QEMU. - Here we consider a simple CXL switch with only a single virtual hierarchy. Whilst more complex devices exist, their visibility to a particular host is generally the same as for @@ -137,11 +135,15 @@ BARs. The Upstream Port has the configuration interfaces for the HDM decoders which route incoming memory accesses to the appropriate downstream port. +A CXL switch is created in a similar fashion to PCI switches +by creating an upstream port (cxl-upstream) and a number of +downstream ports on the internal switch bus (cxl-downstream). + CXL Memory Devices - Type 3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ CXL type 3 devices use a PCI class code and are intended to be supported by a generic operating system driver. They have HDM decoders -though in these EP devices, the decoder is reponsible not for +though in these EP devices, the decoder is responsible not for routing but for translation of the incoming host physical address (HPA) into a Device Physical Address (DPA). @@ -155,12 +157,12 @@ responsible for allocating appropriate ranges from within the CFMWs and exposing those via normal memory configurations as would be done for system RAM. -Example system Topology. x marks the match in each decoder level:: +Example system topology. x marks the match in each decoder level:: |<------------------SYSTEM PHYSICAL ADDRESS MAP (1)----------------->| | __________ __________________________________ __________ | | | | | | | | | - | | CFMW 0 | | CXL Fixed Memory Window 1 | | CFMW 1 | | + | | CFMW 0 | | CXL Fixed Memory Window 1 | | CFMW 2 | | | | HB0 only | | Configured to interleave memory | | HB1 only | | | | | | memory accesses across HB0/HB1 | | | | | |__________| |_____x____________________________| |__________| | @@ -185,8 +187,8 @@ Example system Topology. x marks the match in each decoder level:: ___________|___ __________|__ __|_________ ___|_________ (3)| Root Port 0 | | Root Port 1 | | Root Port 2| | Root Port 3 | | Appears in | | Appears in | | Appears in | | Appear in | - | PCI topology | | PCI Topology| | PCI Topo | | PCI Topo | - | As 0c:00.0 | | as 0c:01.0 | | as de:00.0 | | as de:01.0 | + | PCI topology | | PCI topology| | PCI topo | | PCI topo | + | as 0c:00.0 | | as 0c:01.0 | | as de:00.0 | | as de:01.0 | |_______________| |_____________| |____________| |_____________| | | | | | | | | @@ -206,8 +208,8 @@ Notes: (1) **3 CXL Fixed Memory Windows (CFMW)** corresponding to different ranges of the system physical address map. Each CFMW has particular interleave setup across the CXL Host Bridges (HB) - CFMW0 provides uninterleaved access to HB0, CFW2 provides - uninterleaved acess to HB1. CFW1 provides interleaved memory access + CFMW0 provides uninterleaved access to HB0, CFMW2 provides + uninterleaved access to HB1. CFMW1 provides interleaved memory access across HB0 and HB1. (2) **Two CXL Host Bridges**. Each of these has 2 CXL Root Ports and @@ -240,24 +242,101 @@ Notes: they will take the Host Physical Addresses of accesses and map them to their own local Device Physical Address Space (DPA). +Example topology involving a switch:: + + |<------------------SYSTEM PHYSICAL ADDRESS MAP (1)----------------->| + | __________ __________________________________ __________ | + | | | | | | | | + | | CFMW 0 | | CXL Fixed Memory Window 1 | | CFMW 2 | | + | | HB0 only | | Configured to interleave memory | | HB1 only | | + | | | | memory accesses across HB0/HB1 | | | | + | |____x_____| |__________________________________| |__________| | + | | | | + | | | | + | | | + Interleave Decoder | | | + Matches this HB | | | + \_____________| |_____________/ + __________|__________ _____|_______________ + | | | | + | CXL HB 0 | | CXL HB 1 | + | HB IntLv Decoders | | HB IntLv Decoders | + | PCI/CXL Root Bus 0c | | PCI/CXL Root Bus 0d | + | | | | + |___x_________________| |_____________________| + | | | | + | + A HB 0 HDM Decoder + matches this Port + ___________|___ + | Root Port 0 | + | Appears in | + | PCI topology | + | as 0c:00.0 | + |___________x___| + | + | + \_____________________ + | + | + --------------------------------------------------- + | Switch 0 USP as PCI 0d:00.0 | + | USP has HDM decoder which direct traffic to | + | appropriate downstream port | + | Switch BUS appears as 0e | + |x__________________________________________________| + | | | | + | | | | + _____|_________ ______|______ ______|_____ ______|_______ + (4)| x | | | | | | | + | CXL Type3 0 | | CXL Type3 1 | | CXL type3 2| | CLX Type 3 3 | + | | | | | | | | + | PMEM0(Vol LSA)| | PMEM1 (...) | | PMEM2 (...)| | PMEM3 (...) | + | Decoder to go | | | | | | | + | from host PA | | PCI 10:00.0 | | PCI 11:00.0| | PCI 12:00.0 | + | to device PA | | | | | | | + | PCI as 0f:00.0| | | | | | | + |_______________| |_____________| |____________| |______________| + Example command lines --------------------- -A very simple setup with just one directly attached CXL Type 3 device:: +A very simple setup with just one directly attached CXL Type 3 Persistent Memory device:: - qemu-system-aarch64 -M virt,gic-version=3,cxl=on -m 4g,maxmem=8G,slots=8 -cpu max \ + qemu-system-x86_64 -M q35,cxl=on -m 4G,maxmem=8G,slots=8 -smp 4 \ ... -object memory-backend-file,id=cxl-mem1,share=on,mem-path=/tmp/cxltest.raw,size=256M \ -object memory-backend-file,id=cxl-lsa1,share=on,mem-path=/tmp/lsa.raw,size=256M \ -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \ -device cxl-rp,port=0,bus=cxl.1,id=root_port13,chassis=0,slot=2 \ - -device cxl-type3,bus=root_port13,memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem0 \ - -cxl-fixed-memory-window targets.0=cxl.1,size=4G + -device cxl-type3,bus=root_port13,persistent-memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem0 \ + -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G + +A very simple setup with just one directly attached CXL Type 3 Volatile Memory device:: + + qemu-system-x86_64 -M q35,cxl=on -m 4G,maxmem=8G,slots=8 -smp 4 \ + ... + -object memory-backend-ram,id=vmem0,share=on,size=256M \ + -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \ + -device cxl-rp,port=0,bus=cxl.1,id=root_port13,chassis=0,slot=2 \ + -device cxl-type3,bus=root_port13,volatile-memdev=vmem0,id=cxl-vmem0 \ + -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G + +The same volatile setup may optionally include an LSA region:: + + qemu-system-x86_64 -M q35,cxl=on -m 4G,maxmem=8G,slots=8 -smp 4 \ + ... + -object memory-backend-ram,id=vmem0,share=on,size=256M \ + -object memory-backend-file,id=cxl-lsa0,share=on,mem-path=/tmp/lsa.raw,size=256M \ + -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \ + -device cxl-rp,port=0,bus=cxl.1,id=root_port13,chassis=0,slot=2 \ + -device cxl-type3,bus=root_port13,volatile-memdev=vmem0,lsa=cxl-lsa0,id=cxl-vmem0 \ + -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G A setup suitable for 4 way interleave. Only one fixed window provided, to enable 2 way interleave across 2 CXL host bridges. Each host bridge has 2 CXL Root Ports, with the CXL Type3 device directly attached (no switches).:: - qemu-system-aarch64 -M virt,gic-version=3,cxl=on -m 4g,maxmem=8G,slots=8 -cpu max \ + qemu-system-x86_64 -M q35,cxl=on -m 4G,maxmem=8G,slots=8 -smp 4 \ ... -object memory-backend-file,id=cxl-mem1,share=on,mem-path=/tmp/cxltest.raw,size=256M \ -object memory-backend-file,id=cxl-mem2,share=on,mem-path=/tmp/cxltest2.raw,size=256M \ @@ -270,19 +349,53 @@ the CXL Type3 device directly attached (no switches).:: -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \ -device pxb-cxl,bus_nr=222,bus=pcie.0,id=cxl.2 \ -device cxl-rp,port=0,bus=cxl.1,id=root_port13,chassis=0,slot=2 \ - -device cxl-type3,bus=root_port13,memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem0 \ + -device cxl-type3,bus=root_port13,persistent-memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem0 \ -device cxl-rp,port=1,bus=cxl.1,id=root_port14,chassis=0,slot=3 \ - -device cxl-type3,bus=root_port14,memdev=cxl-mem2,lsa=cxl-lsa2,id=cxl-pmem1 \ + -device cxl-type3,bus=root_port14,persistent-memdev=cxl-mem2,lsa=cxl-lsa2,id=cxl-pmem1 \ -device cxl-rp,port=0,bus=cxl.2,id=root_port15,chassis=0,slot=5 \ - -device cxl-type3,bus=root_port15,memdev=cxl-mem3,lsa=cxl-lsa3,id=cxl-pmem2 \ + -device cxl-type3,bus=root_port15,persistent-memdev=cxl-mem3,lsa=cxl-lsa3,id=cxl-pmem2 \ -device cxl-rp,port=1,bus=cxl.2,id=root_port16,chassis=0,slot=6 \ - -device cxl-type3,bus=root_port16,memdev=cxl-mem4,lsa=cxl-lsa4,id=cxl-pmem3 \ - -cxl-fixed-memory-window targets.0=cxl.1,targets.1=cxl.2,size=4G,interleave-granularity=8k + -device cxl-type3,bus=root_port16,persistent-memdev=cxl-mem4,lsa=cxl-lsa4,id=cxl-pmem3 \ + -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.targets.1=cxl.2,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=8k + +An example of 4 devices below a switch suitable for 1, 2 or 4 way interleave:: + + qemu-system-x86_64 -M q35,cxl=on -m 4G,maxmem=8G,slots=8 -smp 4 \ + ... + -object memory-backend-file,id=cxl-mem0,share=on,mem-path=/tmp/cxltest.raw,size=256M \ + -object memory-backend-file,id=cxl-mem1,share=on,mem-path=/tmp/cxltest1.raw,size=256M \ + -object memory-backend-file,id=cxl-mem2,share=on,mem-path=/tmp/cxltest2.raw,size=256M \ + -object memory-backend-file,id=cxl-mem3,share=on,mem-path=/tmp/cxltest3.raw,size=256M \ + -object memory-backend-file,id=cxl-lsa0,share=on,mem-path=/tmp/lsa0.raw,size=256M \ + -object memory-backend-file,id=cxl-lsa1,share=on,mem-path=/tmp/lsa1.raw,size=256M \ + -object memory-backend-file,id=cxl-lsa2,share=on,mem-path=/tmp/lsa2.raw,size=256M \ + -object memory-backend-file,id=cxl-lsa3,share=on,mem-path=/tmp/lsa3.raw,size=256M \ + -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \ + -device cxl-rp,port=0,bus=cxl.1,id=root_port0,chassis=0,slot=0 \ + -device cxl-rp,port=1,bus=cxl.1,id=root_port1,chassis=0,slot=1 \ + -device cxl-upstream,bus=root_port0,id=us0 \ + -device cxl-downstream,port=0,bus=us0,id=swport0,chassis=0,slot=4 \ + -device cxl-type3,bus=swport0,persistent-memdev=cxl-mem0,lsa=cxl-lsa0,id=cxl-pmem0 \ + -device cxl-downstream,port=1,bus=us0,id=swport1,chassis=0,slot=5 \ + -device cxl-type3,bus=swport1,persistent-memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem1 \ + -device cxl-downstream,port=2,bus=us0,id=swport2,chassis=0,slot=6 \ + -device cxl-type3,bus=swport2,persistent-memdev=cxl-mem2,lsa=cxl-lsa2,id=cxl-pmem2 \ + -device cxl-downstream,port=3,bus=us0,id=swport3,chassis=0,slot=7 \ + -device cxl-type3,bus=swport3,persistent-memdev=cxl-mem3,lsa=cxl-lsa3,id=cxl-pmem3 \ + -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=4k + +Deprecations +------------ + +The Type 3 device [memdev] attribute has been deprecated in favor of the +[persistent-memdev] attributes. [memdev] will default to a persistent memory +device for backward compatibility and is incapable of being used in combination +with [persistent-memdev]. Kernel Configuration Options ---------------------------- -In Linux 5.18 the followings options are necessary to make use of +In Linux 5.18 the following options are necessary to make use of OS management of CXL memory devices as described here. * CONFIG_CXL_BUS @@ -298,5 +411,4 @@ References - Consortium website for specifications etc: http://www.computeexpresslink.org - - Compute Express link Revision 2 specification, October 2020 - - CEDT CFMWS & QTG _DSM ECN May 2021 + - Compute Express Link (CXL) Specification, Revision 3.1, August 2023 diff --git a/docs/system/devices/igb.rst b/docs/system/devices/igb.rst new file mode 100644 index 0000000000..04e79dfe54 --- /dev/null +++ b/docs/system/devices/igb.rst @@ -0,0 +1,73 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later +.. _igb: + +igb +--- + +igb is a family of Intel's gigabit ethernet controllers. In QEMU, 82576 +emulation is implemented in particular. Its datasheet is available at [1]_. + +This implementation is expected to be useful to test SR-IOV networking without +requiring physical hardware. + +Limitations +=========== + +This igb implementation was tested with Linux Test Project [2]_ and Windows HLK +[3]_ during the initial development. Later it was also tested with DPDK Test +Suite [4]_. The command used when testing with LTP is: + +.. code-block:: shell + + network.sh -6mta + +Be aware that this implementation lacks many functionalities available with the +actual hardware, and you may experience various failures if you try to use it +with a different operating system other than DPDK, Linux, and Windows or if you +try functionalities not covered by the tests. + +Using igb +========= + +Using igb should be nothing different from using another network device. See +:ref:`Network_emulation` in general. + +However, you may also need to perform additional steps to activate SR-IOV +feature on your guest. For Linux, refer to [5]_. + +Developing igb +============== + +igb is the successor of e1000e, and e1000e is the successor of e1000 in turn. +As these devices are very similar, if you make a change for igb and the same +change can be applied to e1000e and e1000, please do so. + +Please do not forget to run tests before submitting a change. As tests included +in QEMU is very minimal, run some application which is likely to be affected by +the change to confirm it works in an integrated system. + +Testing igb +=========== + +A qtest of the basic functionality is available. Run the below at the build +directory: + +.. code-block:: shell + + meson test qtest-x86_64/qos-test + +ethtool can test register accesses, interrupts, etc. It is automated as an +Avocado test and can be ran with the following command: + +.. code:: shell + + make check-avocado AVOCADO_TESTS=tests/avocado/netdev-ethtool.py + +References +========== + +.. [1] https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82576eb-gigabit-ethernet-controller-datasheet.pdf +.. [2] https://github.com/linux-test-project/ltp +.. [3] https://learn.microsoft.com/en-us/windows-hardware/test/hlk/ +.. [4] https://doc.dpdk.org/dts/gsg/ +.. [5] https://docs.kernel.org/PCI/pci-iov-howto.html diff --git a/docs/system/devices/ivshmem.rst b/docs/system/devices/ivshmem.rst index b03a48afa3..ce71e25663 100644 --- a/docs/system/devices/ivshmem.rst +++ b/docs/system/devices/ivshmem.rst @@ -1,5 +1,3 @@ -.. _pcsys_005fivshmem: - Inter-VM Shared Memory device ----------------------------- @@ -35,7 +33,7 @@ syntax when using the shared memory server is: 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). +:doc:`../../specs/ivshmem-spec`). Migration with ivshmem ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/system/devices/keyboard.rst b/docs/system/devices/keyboard.rst new file mode 100644 index 0000000000..a8f9fbebae --- /dev/null +++ b/docs/system/devices/keyboard.rst @@ -0,0 +1,129 @@ +.. _keyboard: + +Sparc32 keyboard +---------------- +SUN Type 4, 5 and 5c keyboards have dip switches to choose the language layout +of the keyboard. Solaris makes an ioctl to query the value of the dipswitches +and uses that value to select keyboard layout. Also the SUN bios like the one +in the file ss5.bin uses this value to support at least some keyboard layouts. +However, the OpenBIOS provided with qemu is hardcoded to always use an +US keyboard layout. + +With the escc.chnA-sunkbd-layout driver property it is possible to select +keyboard layout. Example: + +-global escc.chnA-sunkbd-layout=de + +Depending on type of keyboard, the keyboard can have 6 or 5 dip-switches to +select keyboard layout, giving up to 64 different layouts. Not all +combinations are supported by Solaris and even less by Sun OpenBoot BIOS. + +The dip switch settings can be given as hexadecimal number, decimal number +or in some cases as a language string. Examples: + +-global escc.chnA-sunkbd-layout=0x2b + +-global escc.chnA-sunkbd-layout=43 + +-global escc.chnA-sunkbd-layout=sv + +The above 3 examples all select a swedish keyboard layout. Table 3-15 at +https://docs.oracle.com/cd/E19683-01/806-6642/new-43/index.html explains which +keytable file is used for different dip switch settings. The information +in that table can be summarized in this table: + +.. list-table:: Language selection values for escc.chnA-sunkbd-layout + :widths: 10 10 10 + :header-rows: 1 + + * - Hexadecimal value + - Decimal value + - Language code + * - 0x21 + - 33 + - en-us + * - 0x23 + - 35 + - fr + * - 0x24 + - 36 + - da + * - 0x25 + - 37 + - de + * - 0x26 + - 38 + - it + * - 0x27 + - 39 + - nl + * - 0x28 + - 40 + - no + * - 0x29 + - 41 + - pt + * - 0x2a + - 42 + - es + * - 0x2b + - 43 + - sv + * - 0x2c + - 44 + - fr-ch + * - 0x2d + - 45 + - de-ch + * - 0x2e + - 46 + - en-gb + * - 0x2f + - 47 + - ko + * - 0x30 + - 48 + - tw + * - 0x31 + - 49 + - ja + * - 0x32 + - 50 + - fr-ca + * - 0x33 + - 51 + - hu + * - 0x34 + - 52 + - pl + * - 0x35 + - 53 + - cz + * - 0x36 + - 54 + - ru + * - 0x37 + - 55 + - lv + * - 0x38 + - 56 + - tr + * - 0x39 + - 57 + - gr + * - 0x3a + - 58 + - ar + * - 0x3b + - 59 + - lt + * - 0x3c + - 60 + - nl-be + * - 0x3c + - 60 + - be + +Not all dip switch values have a corresponding language code and both "be" and +"nl-be" correspond to the same dip switch value. By default, if no value is +given to escc.chnA-sunkbd-layout 0x21 (en-us) will be used. diff --git a/docs/system/devices/net.rst b/docs/system/devices/net.rst index 4b2640c448..2ab516d4b0 100644 --- a/docs/system/devices/net.rst +++ b/docs/system/devices/net.rst @@ -1,4 +1,4 @@ -.. _pcsys_005fnetwork: +.. _Network_Emulation: Network emulation ----------------- diff --git a/docs/system/devices/nvme.rst b/docs/system/devices/nvme.rst index b5acb2a9c1..d2b1ca9645 100644 --- a/docs/system/devices/nvme.rst +++ b/docs/system/devices/nvme.rst @@ -81,6 +81,13 @@ There are a number of parameters available: Set the UUID of the namespace. This will be reported as a "Namespace UUID" descriptor in the Namespace Identification Descriptor List. +``nguid`` + Set the NGUID of the namespace. This will be reported as a "Namespace Globally + Unique Identifier" descriptor in the Namespace Identification Descriptor List. + It is specified as a string of hexadecimal digits containing exactly 16 bytes + or "auto" for a random value. An optional '-' separator could be used to group + bytes. If not specified the NGUID will remain all zeros. + ``eui64`` Set the EUI-64 of the namespace. This will be reported as a "IEEE Extended Unique Identifier" descriptor in the Namespace Identification Descriptor List. @@ -104,8 +111,8 @@ multipath I/O. .. code-block:: console -device nvme-subsys,id=nvme-subsys-0,nqn=subsys0 - -device nvme,serial=a,subsys=nvme-subsys-0 - -device nvme,serial=b,subsys=nvme-subsys-0 + -device nvme,serial=deadbeef,subsys=nvme-subsys-0 + -device nvme,serial=deadbeef,subsys=nvme-subsys-0 This will create an NVM subsystem with two controllers. Having controllers linked to an ``nvme-subsys`` device allows additional ``nvme-ns`` parameters: @@ -212,6 +219,41 @@ The namespace may be configured with additional parameters the minimum memory page size (CAP.MPSMIN). The default value (``0``) has this property inherit the ``mdts`` value. +Flexible Data Placement +----------------------- + +The device may be configured to support TP4146 ("Flexible Data Placement") by +configuring it (``fdp=on``) on the subsystem:: + + -device nvme-subsys,id=nvme-subsys-0,nqn=subsys0,fdp=on,fdp.nruh=16 + +The subsystem emulates a single Endurance Group, on which Flexible Data +Placement will be supported. Also note that the device emulation deviates +slightly from the specification, by always enabling the "FDP Mode" feature on +the controller if the subsystems is configured for Flexible Data Placement. + +Enabling Flexible Data Placement on the subsyste enables the following +parameters: + +``fdp.nrg`` (default: ``1``) + Set the number of Reclaim Groups. + +``fdp.nruh`` (default: ``0``) + Set the number of Reclaim Unit Handles. This is a mandatory parameter and + must be non-zero. + +``fdp.runs`` (default: ``96M``) + Set the Reclaim Unit Nominal Size. Defaults to 96 MiB. + +Namespaces within this subsystem may requests Reclaim Unit Handles:: + + -device nvme-ns,drive=nvm-1,fdp.ruhs=RUHLIST + +The ``RUHLIST`` is a semicolon separated list (i.e. ``0;1;2;3``) and may +include ranges (i.e. ``0;8-15``). If no reclaim unit handle list is specified, +the controller will assign the controller-specified reclaim unit handle to +placement handle identifier 0. + Metadata -------- @@ -236,6 +278,94 @@ The virtual namespace device supports DIF- and DIX-based protection information ``pil=UINT8`` (default: ``0``) Controls the location of the protection information within the metadata. Set - to ``1`` to transfer protection information as the first eight bytes of - metadata. Otherwise, the protection information is transferred as the last - eight bytes. + to ``1`` to transfer protection information as the first bytes of metadata. + Otherwise, the protection information is transferred as the last bytes of + metadata. + +``pif=UINT8`` (default: ``0``) + By default, the namespace device uses 16 bit guard protection information + format (``pif=0``). Set to ``2`` to enable 64 bit guard protection + information format. This requires at least 16 bytes of metadata. Note that + ``pif=1`` (32 bit guards) is currently not supported. + +Virtualization Enhancements and SR-IOV (Experimental Support) +------------------------------------------------------------- + +The ``nvme`` device supports Single Root I/O Virtualization and Sharing +along with Virtualization Enhancements. The controller has to be linked to +an NVM Subsystem device (``nvme-subsys``) for use with SR-IOV. + +A number of parameters are present (**please note, that they may be +subject to change**): + +``sriov_max_vfs`` (default: ``0``) + Indicates the maximum number of PCIe virtual functions supported + by the controller. Specifying a non-zero value enables reporting of both + SR-IOV and ARI (Alternative Routing-ID Interpretation) capabilities + by the NVMe device. Virtual function controllers will not report SR-IOV. + +``sriov_vq_flexible`` + Indicates the total number of flexible queue resources assignable to all + the secondary controllers. Implicitly sets the number of primary + controller's private resources to ``(max_ioqpairs - sriov_vq_flexible)``. + +``sriov_vi_flexible`` + Indicates the total number of flexible interrupt resources assignable to + all the secondary controllers. Implicitly sets the number of primary + controller's private resources to ``(msix_qsize - sriov_vi_flexible)``. + +``sriov_max_vi_per_vf`` (default: ``0``) + Indicates the maximum number of virtual interrupt resources assignable + to a secondary controller. The default ``0`` resolves to + ``(sriov_vi_flexible / sriov_max_vfs)`` + +``sriov_max_vq_per_vf`` (default: ``0``) + Indicates the maximum number of virtual queue resources assignable to + a secondary controller. The default ``0`` resolves to + ``(sriov_vq_flexible / sriov_max_vfs)`` + +The simplest possible invocation enables the capability to set up one VF +controller and assign an admin queue, an IO queue, and a MSI-X interrupt. + +.. code-block:: console + + -device nvme-subsys,id=subsys0 + -device nvme,serial=deadbeef,subsys=subsys0,sriov_max_vfs=1, + sriov_vq_flexible=2,sriov_vi_flexible=1 + +The minimum steps required to configure a functional NVMe secondary +controller are: + + * unbind flexible resources from the primary controller + +.. code-block:: console + + nvme virt-mgmt /dev/nvme0 -c 0 -r 1 -a 1 -n 0 + nvme virt-mgmt /dev/nvme0 -c 0 -r 0 -a 1 -n 0 + + * perform a Function Level Reset on the primary controller to actually + release the resources + +.. code-block:: console + + echo 1 > /sys/bus/pci/devices/0000:01:00.0/reset + + * enable VF + +.. code-block:: console + + echo 1 > /sys/bus/pci/devices/0000:01:00.0/sriov_numvfs + + * assign the flexible resources to the VF and set it ONLINE + +.. code-block:: console + + nvme virt-mgmt /dev/nvme0 -c 1 -r 1 -a 8 -n 1 + nvme virt-mgmt /dev/nvme0 -c 1 -r 0 -a 8 -n 2 + nvme virt-mgmt /dev/nvme0 -c 1 -r 0 -a 9 -n 0 + + * bind the NVMe driver to the VF + +.. code-block:: console + + echo 0000:01:00.1 > /sys/bus/pci/drivers/nvme/bind diff --git a/docs/system/devices/usb-u2f.rst b/docs/system/devices/usb-u2f.rst new file mode 100644 index 0000000000..4f57d5c8c3 --- /dev/null +++ b/docs/system/devices/usb-u2f.rst @@ -0,0 +1,93 @@ +Universal Second Factor (U2F) USB Key Device +============================================ + +U2F is an open authentication standard that enables relying parties +exposed to the internet to offer a strong second factor option for end +user authentication. + +The second factor is provided by a device implementing the U2F +protocol. In case of a USB U2F security key, it is a USB HID device +that implements the U2F protocol. + +QEMU supports both pass-through of a host U2F key device to a VM, +and software emulation of a U2F key. + +``u2f-passthru`` +---------------- + +The ``u2f-passthru`` device allows you to connect a real hardware +U2F key on your host to a guest VM. All requests made from the guest +are passed through to the physical security key connected to the +host machine and vice versa. + +In addition, the dedicated pass-through allows you to share a single +U2F security key with several guest VMs, which is not possible with a +simple host device assignment pass-through. + +You can specify the host U2F key to use with the ``hidraw`` +option, which takes the host path to a Linux ``/dev/hidrawN`` device: + +.. parsed-literal:: + |qemu_system| -usb -device u2f-passthru,hidraw=/dev/hidraw0 + +If you don't specify the device, the ``u2f-passthru`` device will +autoscan to take the first U2F device it finds on the host (this +requires a working libudev): + +.. parsed-literal:: + |qemu_system| -usb -device u2f-passthru + +``u2f-emulated`` +---------------- + +``u2f-emulated`` is a completely software emulated U2F device. +It uses `libu2f-emu `__ +for the U2F key emulation. libu2f-emu +provides a complete implementation of the U2F protocol device part for +all specified transports given by the FIDO Alliance. + +To work, an emulated U2F device must have four elements: + + * ec x509 certificate + * ec private key + * counter (four bytes value) + * 48 bytes of entropy (random bits) + +To use this type of device, these have to be configured, and these +four elements must be passed one way or another. + +Assuming that you have a working libu2f-emu installed on the host, +there are three possible ways to configure the ``u2f-emulated`` device: + + * ephemeral + * setup directory + * manual + +Ephemeral is the simplest way to configure; it lets the device generate +all the elements it needs for a single use of the lifetime of the device. +It is the default if you do not pass any other options to the device. + +.. parsed-literal:: + |qemu_system| -usb -device u2f-emulated + +You can pass the device the path of a setup directory on the host +using the ``dir`` option; the directory must contain these four files: + + * ``certificate.pem``: ec x509 certificate + * ``private-key.pem``: ec private key + * ``counter``: counter value + * ``entropy``: 48 bytes of entropy + +.. parsed-literal:: + |qemu_system| -usb -device u2f-emulated,dir=$dir + +You can also manually pass the device the paths to each of these files, +if you don't want them all to be in the same directory, using the options + + * ``cert`` + * ``priv`` + * ``counter`` + * ``entropy`` + +.. parsed-literal:: + |qemu_system| -usb -device u2f-emulated,cert=$DIR1/$FILE1,priv=$DIR2/$FILE2,counter=$DIR3/$FILE3,entropy=$DIR4/$FILE4 diff --git a/docs/system/devices/usb.rst b/docs/system/devices/usb.rst index afb7d6c226..a6ca7b0c37 100644 --- a/docs/system/devices/usb.rst +++ b/docs/system/devices/usb.rst @@ -1,5 +1,3 @@ -.. _pcsys_005fusb: - USB emulation ------------- @@ -178,8 +176,20 @@ option or the ``device_add`` monitor command. Available devices are: 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. + Braille device. This emulates a Baum Braille device USB port. id has to + specify a character device defined with ``-chardev …,id=id``. One will + normally use BrlAPI to display the braille output on a BRLTTY-supported + device with + + .. parsed-literal:: + + |qemu_system| [...] -chardev braille,id=brl -device usb-braille,chardev=brl + + or alternatively, use the following equivalent shortcut: + + .. parsed-literal:: + + |qemu_system| [...] -usbdevice braille ``usb-net[,netdev=id]`` Network adapter that supports CDC ethernet and RNDIS protocols. id @@ -197,7 +207,11 @@ option or the ``device_add`` monitor command. Available devices are: USB audio device ``u2f-{emulated,passthru}`` - Universal Second Factor device + :doc:`usb-u2f` + +``canokey`` + An Open-source Secure Key implementing FIDO2, OpenPGP, PIV and more. + For more information, see :ref:`canokey`. Physical port addressing ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -349,3 +363,44 @@ and also assign it to the correct USB bus in QEMU like this: -device usb-ehci,id=ehci \\ -device usb-host,bus=usb-bus.0,hostbus=3,hostport=1 \\ -device usb-host,bus=ehci.0,hostbus=1,hostport=1 + +``usb-host`` properties for reset behavior +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``guest-reset`` and ``guest-reset-all`` properties control +whenever the guest is allowed to reset the physical usb device on the +host. There are three cases: + +``guest-reset=false`` + The guest is not allowed to reset the (physical) usb device. + +``guest-reset=true,guest-resets-all=false`` + The guest is allowed to reset the device when it is not yet + initialized (aka no usb bus address assigned). Usually this results + in one guest reset being allowed. This is the default behavior. + +``guest-reset=true,guest-resets-all=true`` + The guest is allowed to reset the device as it pleases. + +The reason for this existing are broken usb devices. In theory one +should be able to reset (and re-initialize) usb devices at any time. +In practice that may result in shitty usb device firmware crashing and +the device not responding any more until you power-cycle (aka un-plug +and re-plug) it. + +What works best pretty much depends on the behavior of the specific +usb device at hand, so it's a trial-and-error game. If the default +doesn't work, try another option and see whenever the situation +improves. + +record usb transfers +^^^^^^^^^^^^^^^^^^^^ + +All usb devices have support for recording the usb traffic. This can +be enabled using the ``pcap=`` property, for example: + +``-device usb-mouse,pcap=mouse.pcap`` + +The pcap files are compatible with the linux kernels usbmon. Many +tools, including ``wireshark``, can decode and inspect these trace +files. diff --git a/docs/system/devices/vhost-user-input.rst b/docs/system/devices/vhost-user-input.rst new file mode 100644 index 0000000000..118eb78101 --- /dev/null +++ b/docs/system/devices/vhost-user-input.rst @@ -0,0 +1,45 @@ +.. _vhost_user_input: + +QEMU vhost-user-input - Input emulation +======================================= + +This document describes the setup and usage of the Virtio input device. +The Virtio input device is a paravirtualized device for input events. + +Description +----------- + +The vhost-user-input device implementation was designed to work with a daemon +polling on input devices and passes input events to the guest. + +QEMU provides a backend implementation in contrib/vhost-user-input. + +Linux kernel support +-------------------- + +Virtio input requires a guest Linux kernel built with the +``CONFIG_VIRTIO_INPUT`` option. + +Examples +-------- + +The backend daemon should be started first: + +:: + + host# vhost-user-input --socket-path=input.sock \ + --evdev-path=/dev/input/event17 + +The QEMU invocation needs to create a chardev socket to communicate with the +backend daemon and access the VirtIO queues with the guest over the +:ref:`shared memory `. + +:: + + host# qemu-system \ + -chardev socket,path=/tmp/input.sock,id=mouse0 \ + -device vhost-user-input-pci,chardev=mouse0 \ + -m 4096 \ + -object memory-backend-file,id=mem,size=4G,mem-path=/dev/shm,share=on \ + -numa node,memdev=mem \ + ... diff --git a/docs/system/devices/vhost-user-rng.rst b/docs/system/devices/vhost-user-rng.rst index a145d4105c..ead1405326 100644 --- a/docs/system/devices/vhost-user-rng.rst +++ b/docs/system/devices/vhost-user-rng.rst @@ -1,3 +1,5 @@ +.. _vhost_user_rng: + QEMU vhost-user-rng - RNG emulation =================================== diff --git a/docs/system/devices/vhost-user.rst b/docs/system/devices/vhost-user.rst index 86128114fa..9b2da106ce 100644 --- a/docs/system/devices/vhost-user.rst +++ b/docs/system/devices/vhost-user.rst @@ -8,13 +8,81 @@ outside of QEMU itself. To do this there are a number of things required. vhost-user device -=================== +================= These are simple stub devices that ensure the VirtIO device is visible to the guest. The code is mostly boilerplate although each device has a ``chardev`` option which specifies the ID of the ``--chardev`` device that connects via a socket to the vhost-user *daemon*. +Each device will have an virtio-mmio and virtio-pci variant. See your +platform details for what sort of virtio bus to use. + +.. list-table:: vhost-user devices + :widths: 20 20 60 + :header-rows: 1 + + * - Device + - Type + - Notes + * - vhost-user-blk + - Block storage + - See contrib/vhost-user-blk + * - vhost-user-fs + - File based storage driver + - See https://gitlab.com/virtio-fs/virtiofsd + * - vhost-user-gpio + - Proxy gpio pins to host + - See https://github.com/rust-vmm/vhost-device + * - vhost-user-gpu + - GPU driver + - See contrib/vhost-user-gpu + * - vhost-user-i2c + - Proxy i2c devices to host + - See https://github.com/rust-vmm/vhost-device + * - vhost-user-input + - Generic input driver + - :ref:`vhost_user_input` + * - vhost-user-rng + - Entropy driver + - :ref:`vhost_user_rng` + * - vhost-user-scmi + - System Control and Management Interface + - See https://github.com/rust-vmm/vhost-device + * - vhost-user-snd + - Audio device + - See https://github.com/rust-vmm/vhost-device/staging + * - vhost-user-scsi + - SCSI based storage + - See contrib/vhost-user-scsi + * - vhost-user-vsock + - Socket based communication + - See https://github.com/rust-vmm/vhost-device + +The referenced *daemons* are not exhaustive, any conforming backend +implementing the device and using the vhost-user protocol should work. + +vhost-user-device +^^^^^^^^^^^^^^^^^ + +The vhost-user-device is a generic development device intended for +expert use while developing new backends. The user needs to specify +all the required parameters including: + + - Device ``virtio-id`` + - The ``num_vqs`` it needs and their ``vq_size`` + - The ``config_size`` if needed + +.. note:: + To prevent user confusion you cannot currently instantiate + vhost-user-device without first patching out:: + + /* Reason: stop inexperienced users confusing themselves */ + dc->user_creatable = false; + + in ``vhost-user-device.c`` and ``vhost-user-device-pci.c`` file and + rebuilding. + vhost-user daemon ================= @@ -23,6 +91,8 @@ following the :ref:`vhost_user_proto`. There are a number of daemons that can be built when enabled by the project although any daemon that meets the specification for a given device can be used. +.. _shared_memory_object: + Shared memory object ==================== @@ -38,13 +108,13 @@ system memory as defined by the ``-m`` argument. Example ======= -First start you daemon. +First start your daemon. .. parsed-literal:: $ virtio-foo --socket-path=/var/run/foo.sock $OTHER_ARGS -The you start your QEMU instance specifying the device, chardev and +Then you start your QEMU instance specifying the device, chardev and memory objects. .. parsed-literal:: diff --git a/docs/system/devices/virtio-gpu.rst b/docs/system/devices/virtio-gpu.rst new file mode 100644 index 0000000000..cb73dd7998 --- /dev/null +++ b/docs/system/devices/virtio-gpu.rst @@ -0,0 +1,112 @@ +.. + SPDX-License-Identifier: GPL-2.0-or-later + +virtio-gpu +========== + +This document explains the setup and usage of the virtio-gpu device. +The virtio-gpu device paravirtualizes the GPU and display controller. + +Linux kernel support +-------------------- + +virtio-gpu requires a guest Linux kernel built with the +``CONFIG_DRM_VIRTIO_GPU`` option. + +QEMU virtio-gpu variants +------------------------ + +QEMU virtio-gpu device variants come in the following form: + + * ``virtio-vga[-BACKEND]`` + * ``virtio-gpu[-BACKEND][-INTERFACE]`` + * ``vhost-user-vga`` + * ``vhost-user-pci`` + +**Backends:** QEMU provides a 2D virtio-gpu backend, and two accelerated +backends: virglrenderer ('gl' device label) and rutabaga_gfx ('rutabaga' +device label). There is a vhost-user backend that runs the graphics stack +in a separate process for improved isolation. + +**Interfaces:** QEMU further categorizes virtio-gpu device variants based +on the interface exposed to the guest. The interfaces can be classified +into VGA and non-VGA variants. The VGA ones are prefixed with virtio-vga +or vhost-user-vga while the non-VGA ones are prefixed with virtio-gpu or +vhost-user-gpu. + +The VGA ones always use the PCI interface, but for the non-VGA ones, the +user can further pick between MMIO or PCI. For MMIO, the user can suffix +the device name with -device, though vhost-user-gpu does not support MMIO. +For PCI, the user can suffix it with -pci. Without these suffixes, the +platform default will be chosen. + +virtio-gpu 2d +------------- + +The default 2D backend only performs 2D operations. The guest needs to +employ a software renderer for 3D graphics. + +Typically, the software renderer is provided by `Mesa`_ or `SwiftShader`_. +Mesa's implementations (LLVMpipe, Lavapipe and virgl below) work out of box +on typical modern Linux distributions. + +.. parsed-literal:: + -device virtio-gpu + +.. _Mesa: https://www.mesa3d.org/ +.. _SwiftShader: https://github.com/google/swiftshader + +virtio-gpu virglrenderer +------------------------ + +When using virgl accelerated graphics mode in the guest, OpenGL API calls +are translated into an intermediate representation (see `Gallium3D`_). The +intermediate representation is communicated to the host and the +`virglrenderer`_ library on the host translates the intermediate +representation back to OpenGL API calls. + +.. parsed-literal:: + -device virtio-gpu-gl + +.. _Gallium3D: https://www.freedesktop.org/wiki/Software/gallium/ +.. _virglrenderer: https://gitlab.freedesktop.org/virgl/virglrenderer/ + +virtio-gpu rutabaga +------------------- + +virtio-gpu can also leverage rutabaga_gfx to provide `gfxstream`_ +rendering and `Wayland display passthrough`_. With the gfxstream rendering +mode, GLES and Vulkan calls are forwarded to the host with minimal +modification. + +The crosvm book provides directions on how to build a `gfxstream-enabled +rutabaga`_ and launch a `guest Wayland proxy`_. + +This device does require host blob support (``hostmem`` field below). The +``hostmem`` field specifies the size of virtio-gpu host memory window. +This is typically between 256M and 8G. + +At least one virtio-gpu capability set ("capset") must be specified when +starting the device. The currently capsets supported are ``gfxstream-vulkan`` +and ``cross-domain`` for Linux guests. For Android guests, the experimental +``x-gfxstream-gles`` and ``x-gfxstream-composer`` capsets are also supported. + +The device will try to auto-detect the wayland socket path if the +``cross-domain`` capset name is set. The user may optionally specify +``wayland-socket-path`` for non-standard paths. + +The ``wsi`` option can be set to ``surfaceless`` or ``headless``. +Surfaceless doesn't create a native window surface, but does copy from the +render target to the Pixman buffer if a virtio-gpu 2D hypercall is issued. +Headless is like surfaceless, but doesn't copy to the Pixman buffer. +Surfaceless is the default if ``wsi`` is not specified. + +.. parsed-literal:: + -device virtio-gpu-rutabaga,gfxstream-vulkan=on,cross-domain=on, + hostmem=8G,wayland-socket-path=/tmp/nonstandard/mock_wayland.sock, + wsi=headless + +.. _gfxstream: https://android.googlesource.com/platform/hardware/google/gfxstream/ +.. _Wayland display passthrough: https://www.youtube.com/watch?v=OZJiHMtIQ2M +.. _gfxstream-enabled rutabaga: https://crosvm.dev/book/appendix/rutabaga_gfx.html +.. _guest Wayland proxy: https://crosvm.dev/book/devices/wayland.html diff --git a/docs/system/devices/virtio-snd.rst b/docs/system/devices/virtio-snd.rst new file mode 100644 index 0000000000..2a9187fd70 --- /dev/null +++ b/docs/system/devices/virtio-snd.rst @@ -0,0 +1,49 @@ +virtio sound +============ + +This document explains the setup and usage of the Virtio sound device. +The Virtio sound device is a paravirtualized sound card device. + +Linux kernel support +-------------------- + +Virtio sound requires a guest Linux kernel built with the +``CONFIG_SND_VIRTIO`` option. + +Description +----------- + +Virtio sound implements capture and playback from inside a guest using the +configured audio backend of the host machine. + +Device properties +----------------- + +The Virtio sound device can be configured with the following properties: + + * ``jacks`` number of physical jacks (Unimplemented). + * ``streams`` number of PCM streams. At the moment, no stream configuration is supported: the first one will always be a playback stream, an optional second will always be a capture stream. Adding more will cycle stream directions from playback to capture. + * ``chmaps`` number of channel maps (Unimplemented). + +All streams are stereo and have the default channel positions ``Front left, right``. + +Examples +-------- + +Add an audio device and an audio backend at once with ``-audio`` and ``model=virtio``: + + * pulseaudio: ``-audio driver=pa,model=virtio`` + or ``-audio driver=pa,model=virtio,server=/run/user/1000/pulse/native`` + * sdl: ``-audio driver=sdl,model=virtio`` + * coreaudio: ``-audio driver=coreaudio,model=virtio`` + +etc. + +To specifically add virtualized sound devices, you have to specify a PCI device +and an audio backend listed with ``-audio driver=help`` that works on your host +machine, e.g.: + +:: + + -device virtio-sound-pci,audiodev=my_audiodev \ + -audiodev alsa,id=my_audiodev diff --git a/docs/system/gdb.rst b/docs/system/gdb.rst index 453eb73f6c..4228cb56bb 100644 --- a/docs/system/gdb.rst +++ b/docs/system/gdb.rst @@ -46,6 +46,39 @@ Here are some useful tips in order to use gdb on system code: 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. +Breakpoint and Watchpoint support +================================= + +While GDB can always fall back to inserting breakpoints into memory +(if writable) other features are very much dependent on support of the +accelerator. For TCG system emulation we advertise an infinite number +of hardware assisted breakpoints and watchpoints. For other +accelerators it will depend on if support has been added (see +supports_guest_debug and related hooks in AccelOpsClass). + +As TCG cannot track all memory accesses in user-mode there is no +support for watchpoints. + +Relocating code +=============== + +On modern kernels confusion can be caused by code being relocated by +features such as address space layout randomisation. To avoid +confusion when debugging such things you either need to update gdb's +view of where things are in memory or perhaps more trivially disable +ASLR when booting the system. + +Debugging user-space in system emulation +======================================== + +While it is technically possible to debug a user-space program running +inside a system image, it does present challenges. Kernel preemption +and execution mode changes between kernel and user mode can make it +hard to follow what's going on. Unless you are specifically trying to +debug some interaction between kernel and user-space you are better +off running your guest program with gdb either in the guest or using +a gdbserver exposed via a port to the outside world. + Debugging multicore machines ============================ @@ -192,3 +225,18 @@ The memory mode can be checked by sending the following command: ``maintenance packet Qqemu.PhyMemMode:0`` This will change it back to normal memory mode. + +Security considerations +======================= + +Connecting to the GDB socket allows running arbitrary code inside the guest; +in case of the TCG emulation, which is not considered a security boundary, this +also means running arbitrary code on the host. Additionally, when debugging +qemu-user, it allows directly downloading any file readable by QEMU from the +host. + +The GDB socket is not protected by authentication, authorization or encryption. +It is therefore a responsibility of the user to make sure that only authorized +clients can connect to it, e.g., by using a unix socket with proper +permissions, or by opening a TCP socket only on interfaces that are not +reachable by potential attackers. diff --git a/docs/system/guest-loader.rst b/docs/system/guest-loader.rst index 9ef9776bf0..304ee5d531 100644 --- a/docs/system/guest-loader.rst +++ b/docs/system/guest-loader.rst @@ -14,7 +14,7 @@ The guest loader does two things: - load blobs (kernels and initial ram disks) into memory - sets platform FDT data so hypervisors can find and boot them -This is what is typically done by a boot-loader like grub using it's +This is what is typically done by a boot-loader like grub using its multi-boot capability. A typical example would look like: .. parsed-literal:: @@ -25,9 +25,9 @@ multi-boot capability. A typical example would look like: -device guest-loader,addr=0x47000000,initrd=rootfs.cpio In the above example the Xen hypervisor is loaded by the -kernel -parameter and passed it's boot arguments via -append. The Dom0 guest +parameter and passed its boot arguments via -append. The Dom0 guest is loaded into the areas of memory. Each blob will get -``/chosen/module@`` entry in the FDT to indicate it's location and +``/chosen/module@`` entry in the FDT to indicate its location and size. Additional information can be passed with by using additional arguments. diff --git a/docs/system/i386/amd-memory-encryption.rst b/docs/system/i386/amd-memory-encryption.rst index dcf4add0e7..e9bc142bc1 100644 --- a/docs/system/i386/amd-memory-encryption.rst +++ b/docs/system/i386/amd-memory-encryption.rst @@ -183,13 +183,13 @@ References ---------- `AMD Memory Encryption whitepaper -`_ +`_ .. [SEVAPI] `Secure Encrypted Virtualization API `_ .. [APMVOL2] `AMD64 Architecture Programmer's Manual Volume 2: System Programming - `_ + `_ KVM Forum slides: @@ -199,7 +199,7 @@ KVM Forum slides: `_ `AMD64 Architecture Programmer's Manual: -`_ +`_ * SME is section 7.10 * SEV is section 15.34 diff --git a/docs/system/i386/sgx.rst b/docs/system/i386/sgx.rst index 0f0a73f758..ab58b29392 100644 --- a/docs/system/i386/sgx.rst +++ b/docs/system/i386/sgx.rst @@ -6,7 +6,7 @@ Overview Intel Software Guard eXtensions (SGX) is a set of instructions and mechanisms for memory accesses in order to provide security accesses for sensitive -applications and data. SGX allows an application to use it's pariticular +applications and data. SGX allows an application to use its particular address space as an *enclave*, which is a protected area provides confidentiality and integrity even in the presence of privileged malware. Accesses to the enclave memory area from any software not resident in the enclave are prevented, diff --git a/docs/system/i386/xen.rst b/docs/system/i386/xen.rst new file mode 100644 index 0000000000..46db5f34c1 --- /dev/null +++ b/docs/system/i386/xen.rst @@ -0,0 +1,144 @@ +Xen HVM guest support +===================== + + +Description +----------- + +KVM has support for hosting Xen guests, intercepting Xen hypercalls and event +channel (Xen PV interrupt) delivery. This allows guests which expect to be +run under Xen to be hosted in QEMU under Linux/KVM instead. + +Using the split irqchip is mandatory for Xen support. + +Setup +----- + +Xen mode is enabled by setting the ``xen-version`` property of the KVM +accelerator, for example for Xen 4.17: + +.. parsed-literal:: + + |qemu_system| --accel kvm,xen-version=0x40011,kernel-irqchip=split + +Additionally, virtual APIC support can be advertised to the guest through the +``xen-vapic`` CPU flag: + +.. parsed-literal:: + + |qemu_system| --accel kvm,xen-version=0x40011,kernel-irqchip=split --cpu host,+xen-vapic + +When Xen support is enabled, QEMU changes hypervisor identification (CPUID +0x40000000..0x4000000A) to Xen. The KVM identification and features are not +advertised to a Xen guest. If Hyper-V is also enabled, the Xen identification +moves to leaves 0x40000100..0x4000010A. + +Properties +---------- + +The following properties exist on the KVM accelerator object: + +``xen-version`` + This property contains the Xen version in ``XENVER_version`` form, with the + major version in the top 16 bits and the minor version in the low 16 bits. + Setting this property enables the Xen guest support. If Xen version 4.5 or + greater is specified, the HVM leaf in Xen CPUID is populated. Xen version + 4.6 enables the vCPU ID in CPUID, and version 4.17 advertises vCPU upcall + vector support to the guest. + +``xen-evtchn-max-pirq`` + Xen PIRQs represent an emulated physical interrupt, either GSI or MSI, which + can be routed to an event channel instead of to the emulated I/O or local + APIC. By default, QEMU permits only 256 PIRQs because this allows maximum + compatibility with 32-bit MSI where the higher bits of the PIRQ# would need + to be in the upper 64 bits of the MSI message. For guests with large numbers + of PCI devices (and none which are limited to 32-bit addressing) it may be + desirable to increase this value. + +``xen-gnttab-max-frames`` + Xen grant tables are the means by which a Xen guest grants access to its + memory for PV back ends (disk, network, etc.). Since QEMU only supports v1 + grant tables which are 8 bytes in size, each page (each frame) of the grant + table can reference 512 pages of guest memory. The default number of frames + is 64, allowing for 32768 pages of guest memory to be accessed by PV backends + through simultaneous grants. For guests with large numbers of PV devices and + high throughput, it may be desirable to increase this value. + +Xen paravirtual devices +----------------------- + +The Xen PCI platform device is enabled automatically for a Xen guest. This +allows a guest to unplug all emulated devices, in order to use paravirtual +block and network drivers instead. + +Those paravirtual Xen block, network (and console) devices can be created +through the command line, and/or hot-plugged. + +To provide a Xen console device, define a character device and then a device +of type ``xen-console`` to connect to it. For the Xen console equivalent of +the handy ``-serial mon:stdio`` option, for example: + +.. parsed-literal:: + -chardev stdio,mux=on,id=char0,signal=off -mon char0 \\ + -device xen-console,chardev=char0 + +The Xen network device is ``xen-net-device``, which becomes the default NIC +model for emulated Xen guests, meaning that just the default NIC provided +by QEMU should automatically work and present a Xen network device to the +guest. + +Disks can be configured with '``-drive file=${GUEST_IMAGE},if=xen``' and will +appear to the guest as ``xvda`` onwards. + +Under Xen, the boot disk is typically available both via IDE emulation, and +as a PV block device. Guest bootloaders typically use IDE to load the guest +kernel, which then unplugs the IDE and continues with the Xen PV block device. + +This configuration can be achieved as follows: + +.. parsed-literal:: + + |qemu_system| --accel kvm,xen-version=0x40011,kernel-irqchip=split \\ + -drive file=${GUEST_IMAGE},if=xen \\ + -drive file=${GUEST_IMAGE},file.locking=off,if=ide + +VirtIO devices can also be used; Linux guests may need to be dissuaded from +umplugging them by adding '``xen_emul_unplug=never``' on their command line. + +Booting Xen PV guests +--------------------- + +Booting PV guest kernels is possible by using the Xen PV shim (a version of Xen +itself, designed to run inside a Xen HVM guest and provide memory management +services for one guest alone). + +The Xen binary is provided as the ``-kernel`` and the guest kernel itself (or +PV Grub image) as the ``-initrd`` image, which actually just means the first +multiboot "module". For example: + +.. parsed-literal:: + + |qemu_system| --accel kvm,xen-version=0x40011,kernel-irqchip=split \\ + -chardev stdio,id=char0 -device xen-console,chardev=char0 \\ + -display none -m 1G -kernel xen -initrd bzImage \\ + -append "pv-shim console=xen,pv -- console=hvc0 root=/dev/xvda1" \\ + -drive file=${GUEST_IMAGE},if=xen + +The Xen image must be built with the ``CONFIG_XEN_GUEST`` and ``CONFIG_PV_SHIM`` +options, and as of Xen 4.17, Xen's PV shim mode does not support using a serial +port; it must have a Xen console or it will panic. + +The example above provides the guest kernel command line after a separator +(" ``--`` ") on the Xen command line, and does not provide the guest kernel +with an actual initramfs, which would need to listed as a second multiboot +module. For more complicated alternatives, see the command line +:ref:`documentation ` for the +``-initrd`` option. + +Host OS requirements +-------------------- + +The minimal Xen support in the KVM accelerator requires the host to be running +Linux v5.12 or newer. Later versions add optimisations: Linux v5.17 added +acceleration of interrupt delivery via the Xen PIRQ mechanism, and Linux v5.19 +accelerated Xen PV timers and inter-processor interrupts (IPIs). diff --git a/docs/system/index.rst b/docs/system/index.rst index e3695649c5..c21065e519 100644 --- a/docs/system/index.rst +++ b/docs/system/index.rst @@ -1,16 +1,18 @@ +.. _System Emulation: + ---------------- System Emulation ---------------- This section of the 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 +This includes working with hypervisors such as KVM, Xen or Hypervisor.Framework. .. toctree:: :maxdepth: 3 - quickstart + introduction invocation device-emulation keys @@ -36,3 +38,4 @@ or Hypervisor.Framework. security multi-process confidential-guest-support + vm-templating diff --git a/docs/system/introduction.rst b/docs/system/introduction.rst new file mode 100644 index 0000000000..746707eb00 --- /dev/null +++ b/docs/system/introduction.rst @@ -0,0 +1,219 @@ +Introduction +============ + +.. _Accelerators: + +Virtualisation Accelerators +--------------------------- + +QEMU's system emulation provides a virtual model of a machine (CPU, +memory and emulated devices) to run a guest OS. It supports a number +of hypervisors (known as accelerators) as well as a JIT known as the +Tiny Code Generator (TCG) capable of emulating many CPUs. + +.. list-table:: Supported Accelerators + :header-rows: 1 + + * - Accelerator + - Host OS + - Host Architectures + * - KVM + - Linux + - Arm (64 bit only), MIPS, PPC, RISC-V, s390x, x86 + * - Xen + - Linux (as dom0) + - Arm, x86 + * - Hypervisor Framework (hvf) + - MacOS + - x86 (64 bit only), Arm (64 bit only) + * - Windows Hypervisor Platform (whpx) + - Windows + - x86 + * - NetBSD Virtual Machine Monitor (nvmm) + - NetBSD + - x86 + * - Tiny Code Generator (tcg) + - Linux, other POSIX, Windows, MacOS + - Arm, x86, Loongarch64, MIPS, PPC, s390x, Sparc64 + +Feature Overview +---------------- + +System emulation provides a wide range of device models to emulate +various hardware components you may want to add to your machine. This +includes a wide number of VirtIO devices which are specifically tuned +for efficient operation under virtualisation. Some of the device +emulation can be offloaded from the main QEMU process using either +vhost-user (for VirtIO) or :ref:`Multi-process QEMU`. If the platform +supports it QEMU also supports directly passing devices through to +guest VMs to eliminate the device emulation overhead. See +:ref:`device-emulation` for more details. + +There is a full :ref:`featured block layer` +which allows for construction of complex storage topology which can be +stacked across multiple layers supporting redirection, networking, +snapshots and migration support. + +The flexible ``chardev`` system allows for handling IO from character +like devices using stdio, files, unix sockets and TCP networking. + +QEMU provides a number of management interfaces including a line based +:ref:`Human Monitor Protocol (HMP)` that allows you to +dynamically add and remove devices as well as introspect the system +state. The :ref:`QEMU Monitor Protocol` (QMP) is a well +defined, versioned, machine usable API that presents a rich interface +to other tools to create, control and manage Virtual Machines. This is +the interface used by higher level tools interfaces such as `Virt +Manager `_ using the `libvirt framework +`_. + +For the common accelerators QEMU, supported debugging with its +:ref:`gdbstub` which allows users to connect GDB and debug +system software images. + +Running +------- + +QEMU provides a rich and complex API which can be overwhelming to +understand. While some architectures can boot something with just a +disk image, those examples elide a lot of details with defaults that +may not be optimal for modern systems. + +For a non-x86 system where we emulate a broad range of machine types, +the command lines are generally more explicit in defining the machine +and boot behaviour. You will find often find example command lines in +the :ref:`system-targets-ref` section of the manual. + +While the project doesn't want to discourage users from using the +command line to launch VMs, we do want to highlight that there are a +number of projects dedicated to providing a more user friendly +experience. Those built around the ``libvirt`` framework can make use +of feature probing to build modern VM images tailored to run on the +hardware you have. + +That said, the general form of a QEMU command line can be expressed +as: + +.. parsed-literal:: + + $ |qemu_system| [machine opts] \\ + [cpu opts] \\ + [accelerator opts] \\ + [device opts] \\ + [backend opts] \\ + [interface opts] \\ + [boot opts] + +Most options will generate some help information. So for example: + +.. parsed-literal:: + + $ |qemu_system| -M help + +will list the machine types supported by that QEMU binary. ``help`` +can also be passed as an argument to another option. For example: + +.. parsed-literal:: + + $ |qemu_system| -device scsi-hd,help + +will list the arguments and their default values of additional options +that can control the behaviour of the ``scsi-hd`` device. + +.. list-table:: Options Overview + :header-rows: 1 + :widths: 10, 90 + + * - Options + - + * - Machine + - Define the machine type, amount of memory etc + * - CPU + - Type and number/topology of vCPUs. Most accelerators offer + a ``host`` cpu option which simply passes through your host CPU + configuration without filtering out any features. + * - Accelerator + - This will depend on the hypervisor you run. Note that the + default is TCG, which is purely emulated, so you must specify an + accelerator type to take advantage of hardware virtualization. + * - Devices + - Additional devices that are not defined by default with the + machine type. + * - Backends + - Backends are how QEMU deals with the guest's data, for example + how a block device is stored, how network devices see the + network or how a serial device is directed to the outside world. + * - Interfaces + - How the system is displayed, how it is managed and controlled or + debugged. + * - Boot + - How the system boots, via firmware or direct kernel boot. + +In the following example we first define a ``virt`` machine which is a +general purpose platform for running Aarch64 guests. We enable +virtualisation so we can use KVM inside the emulated guest. As the +``virt`` machine comes with some built in pflash devices we give them +names so we can override the defaults later. + +.. code:: + + $ qemu-system-aarch64 \ + -machine type=virt,virtualization=on,pflash0=rom,pflash1=efivars \ + -m 4096 \ + +We then define the 4 vCPUs using the ``max`` option which gives us all +the Arm features QEMU is capable of emulating. We enable a more +emulation friendly implementation of Arm's pointer authentication +algorithm. We explicitly specify TCG acceleration even though QEMU +would default to it anyway. + +.. code:: + + -cpu max,pauth-impdef=on \ + -smp 4 \ + -accel tcg \ + +As the ``virt`` platform doesn't have any default network or storage +devices we need to define them. We give them ids so we can link them +with the backend later on. + +.. code:: + + -device virtio-net-pci,netdev=unet \ + -device virtio-scsi-pci \ + -device scsi-hd,drive=hd \ + +We connect the user-mode networking to our network device. As +user-mode networking isn't directly accessible from the outside world +we forward localhost port 2222 to the ssh port on the guest. + +.. code:: + + -netdev user,id=unet,hostfwd=tcp::2222-:22 \ + +We connect the guest visible block device to an LVM partition we have +set aside for our guest. + +.. code:: + + -blockdev driver=raw,node-name=hd,file.driver=host_device,file.filename=/dev/lvm-disk/debian-bullseye-arm64 \ + +We then tell QEMU to multiplex the :ref:`QEMU monitor` with the serial +port output (we can switch between the two using :ref:`keys in the +character backend multiplexer`). As there is no default graphical +device we disable the display as we can work entirely in the terminal. + +.. code:: + + -serial mon:stdio \ + -display none \ + +Finally we override the default firmware to ensure we have some +storage for EFI to persist its configuration. That firmware is +responsible for finding the disk, booting grub and eventually running +our system. + +.. code:: + + -blockdev node-name=rom,driver=file,filename=(pwd)/pc-bios/edk2-aarch64-code.fd,read-only=true \ + -blockdev node-name=efivars,driver=file,filename=$HOME/images/qemu-arm64-efivars diff --git a/docs/system/invocation.rst b/docs/system/invocation.rst index 4ba38fc23d..14b7db1c10 100644 --- a/docs/system/invocation.rst +++ b/docs/system/invocation.rst @@ -10,6 +10,11 @@ Invocation disk_image is a raw hard disk image for IDE hard disk 0. Some targets do not need a disk image. +When dealing with options parameters as arbitrary strings containing +commas, such as in "file=my,file" and "string=a,b", it's necessary to +double the commas. For instance,"-fw_cfg name=z,string=a,,b" will be +parsed as "-fw_cfg name=z,string=a,b". + .. hxtool-doc:: qemu-options.hx Device URL Syntax diff --git a/docs/system/keys.rst b/docs/system/keys.rst index e596ae6c4e..0fc17b994d 100644 --- a/docs/system/keys.rst +++ b/docs/system/keys.rst @@ -1,4 +1,4 @@ -.. _pcsys_005fkeys: +.. _GUI_keys: Keys in the graphical frontends ------------------------------- diff --git a/docs/system/keys.rst.inc b/docs/system/keys.rst.inc index bd9b8e5f6f..59966a3fe7 100644 --- a/docs/system/keys.rst.inc +++ b/docs/system/keys.rst.inc @@ -1,8 +1,9 @@ -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): +During the graphical emulation, you can use special key combinations from +the following table to change modes. By default the modifier is Ctrl-Alt +(used in the table below) which can be changed with ``-display`` suboption +``mod=`` where appropriate. For example, ``-display sdl, +grab-mod=lshift-lctrl-lalt`` changes the modifier key to Ctrl-Alt-Shift, +while ``-display sdl,grab-mod=rctrl`` changes it to the right Ctrl key. Ctrl-Alt-f Toggle full screen @@ -28,7 +29,7 @@ Ctrl-Alt-n *3* Serial port -Ctrl-Alt +Ctrl-Alt-g Toggle mouse and keyboard grab. In the virtual consoles, you can use Ctrl-Up, Ctrl-Down, Ctrl-PageUp and diff --git a/docs/system/linuxboot.rst b/docs/system/linuxboot.rst index 228650abc5..5db2e560dc 100644 --- a/docs/system/linuxboot.rst +++ b/docs/system/linuxboot.rst @@ -27,4 +27,4 @@ virtual serial port and the QEMU monitor to the console with the -append "root=/dev/hda console=ttyS0" -nographic Use Ctrl-a c to switch between the serial console and the monitor (see -:ref:`pcsys_005fkeys`). +:ref:`GUI_keys`). diff --git a/docs/system/loongarch/loongson3.rst b/docs/system/loongarch/loongson3.rst deleted file mode 100644 index fa3acd01c0..0000000000 --- a/docs/system/loongarch/loongson3.rst +++ /dev/null @@ -1,41 +0,0 @@ -:orphan: - -========================================== -loongson3 virt generic platform (``virt``) -========================================== - -The ``virt`` machine use gpex host bridge, and there are some -emulated devices on virt board, such as loongson7a RTC device, -IOAPIC device, ACPI device and so on. - -Supported devices ------------------ - -The ``virt`` machine supports: -- Gpex host bridge -- Ls7a RTC device -- Ls7a IOAPIC device -- Ls7a ACPI device -- Fw_cfg device -- PCI/PCIe devices -- Memory device -- CPU device. Type: Loongson-3A5000. - -CPU and machine Type --------------------- - -The ``qemu-system-loongarch64`` provides emulation for virt -machine. You can specify the machine type ``virt`` and -cpu type ``Loongson-3A5000``. - -Boot options ------------- - -Now the ``virt`` machine can run test program in ELF format and the -method of compiling is in target/loongarch/README. - -.. code-block:: bash - - $ qemu-system-loongarch64 -machine virt -m 4G -cpu Loongson-3A5000 \ - -smp 1 -kernel hello -monitor none -display none \ - -chardev file,path=hello.out,id=output -serial chardev:output diff --git a/docs/system/loongarch/virt.rst b/docs/system/loongarch/virt.rst new file mode 100644 index 0000000000..c37268b404 --- /dev/null +++ b/docs/system/loongarch/virt.rst @@ -0,0 +1,108 @@ +:orphan: + +========================================== +loongson3 virt generic platform (``virt``) +========================================== + +The ``virt`` machine use gpex host bridge, and there are some +emulated devices on virt board, such as loongson7a RTC device, +IOAPIC device, ACPI device and so on. + +Supported devices +----------------- + +The ``virt`` machine supports: +- Gpex host bridge +- Ls7a RTC device +- Ls7a IOAPIC device +- ACPI GED device +- Fw_cfg device +- PCI/PCIe devices +- Memory device +- CPU device. Type: la464. + +CPU and machine Type +-------------------- + +The ``qemu-system-loongarch64`` provides emulation for virt +machine. You can specify the machine type ``virt`` and +cpu type ``la464``. + +Boot options +------------ + +We can boot the LoongArch virt machine by specifying the uefi bios, +initrd, and linux kernel. And those source codes and binary files +can be accessed by following steps. + +(1) Build qemu-system-loongarch64: + +.. code-block:: bash + + ./configure --disable-rdma --disable-pvrdma --prefix=/usr \ + --target-list="loongarch64-softmmu" \ + --disable-libiscsi --disable-libnfs --disable-libpmem \ + --disable-glusterfs --enable-libusb --enable-usb-redir \ + --disable-opengl --disable-xen --enable-spice \ + --enable-debug --disable-capstone --disable-kvm \ + --enable-profiler + make -j8 + +(2) Set cross tools: + +.. code-block:: bash + + wget https://github.com/loongson/build-tools/releases/download/2022.09.06/loongarch64-clfs-6.3-cross-tools-gcc-glibc.tar.xz + + tar -vxf loongarch64-clfs-6.3-cross-tools-gcc-glibc.tar.xz -C /opt + + export PATH=/opt/cross-tools/bin:$PATH + export LD_LIBRARY_PATH=/opt/cross-tools/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH=/opt/cross-tools/loongarch64-unknown-linux-gnu/lib/:$LD_LIBRARY_PATH + +Note: You need get the latest cross-tools at https://github.com/loongson/build-tools + +(3) Build BIOS: + + See: https://github.com/tianocore/edk2-platforms/tree/master/Platform/Loongson/LoongArchQemuPkg#readme + +Note: To build the release version of the bios, set --buildtarget=RELEASE, + the bios file path: Build/LoongArchQemu/RELEASE_GCC5/FV/QEMU_EFI.fd + +(4) Build kernel: + +.. code-block:: bash + + git clone https://github.com/loongson/linux.git + + cd linux + + git checkout loongarch-next + + make ARCH=loongarch CROSS_COMPILE=loongarch64-unknown-linux-gnu- loongson3_defconfig + + make ARCH=loongarch CROSS_COMPILE=loongarch64-unknown-linux-gnu- -j32 + +Note: The branch of linux source code is loongarch-next. + the kernel file: arch/loongarch/boot/vmlinuz.efi + +(5) Get initrd: + + You can use busybox tool and the linux modules to make a initrd file. Or you can access the + binary files: https://github.com/yangxiaojuan-loongson/qemu-binary + +.. code-block:: bash + + git clone https://github.com/yangxiaojuan-loongson/qemu-binary + +Note: the initrd file is ramdisk + +(6) Booting LoongArch: + +.. code-block:: bash + + $ ./build/qemu-system-loongarch64 -machine virt -m 4G -cpu la464 \ + -smp 1 -bios QEMU_EFI.fd -kernel vmlinuz.efi -initrd ramdisk \ + -serial stdio -monitor telnet:localhost:4495,server,nowait \ + -append "root=/dev/ram rdinit=/sbin/init console=ttyS0,115200" \ + --nographic diff --git a/docs/system/multi-process.rst b/docs/system/multi-process.rst index 210531ee17..2008a67809 100644 --- a/docs/system/multi-process.rst +++ b/docs/system/multi-process.rst @@ -1,8 +1,10 @@ +.. _Multi-process QEMU: + Multi-process QEMU ================== This document describes how to configure and use multi-process qemu. -For the design document refer to docs/devel/qemu-multiprocess. +For the design document refer to docs/devel/multi-process.rst. 1) Configuration ---------------- diff --git a/docs/system/openrisc/cpu-features.rst b/docs/system/openrisc/cpu-features.rst new file mode 100644 index 0000000000..aeb65e22ff --- /dev/null +++ b/docs/system/openrisc/cpu-features.rst @@ -0,0 +1,15 @@ +CPU Features +============ + +The QEMU emulation of the OpenRISC architecture provides following built in +features. + +- Shadow GPRs +- MMU TLB with 128 entries, 1 way +- Power Management (PM) +- Programmable Interrupt Controller (PIC) +- Tick Timer + +These features are on by default and the presence can be confirmed by checking +the contents of the Unit Presence Register (``UPR``) and CPU Configuration +Register (``CPUCFGR``). diff --git a/docs/system/openrisc/emulation.rst b/docs/system/openrisc/emulation.rst new file mode 100644 index 0000000000..0af898ab20 --- /dev/null +++ b/docs/system/openrisc/emulation.rst @@ -0,0 +1,17 @@ +OpenRISC 1000 CPU architecture support +====================================== + +QEMU's TCG emulation includes support for the OpenRISC or1200 implementation of +the OpenRISC 1000 cpu architecture. + +The or1200 cpu also has support for the following instruction subsets: + +- ORBIS32 (OpenRISC Basic Instruction Set) +- ORFPX32 (OpenRISC Floating-Point eXtension) + +In addition to the instruction subsets the QEMU TCG emulation also has support +for most Class II (optional) instructions. + +For information on all OpenRISC instructions please refer to the latest +architecture manual available on the OpenRISC website in the +`OpenRISC Architecture `_ section. diff --git a/docs/system/openrisc/or1k-sim.rst b/docs/system/openrisc/or1k-sim.rst new file mode 100644 index 0000000000..ef10439737 --- /dev/null +++ b/docs/system/openrisc/or1k-sim.rst @@ -0,0 +1,43 @@ +Or1ksim board +============= + +The QEMU Or1ksim machine emulates the standard OpenRISC board simulator which is +also the standard SoC configuration. + +Supported devices +----------------- + + * 16550A UART + * ETHOC Ethernet controller + * SMP (OpenRISC multicore using ompic) + +Boot options +------------ + +The Or1ksim machine can be started using the ``-kernel`` and ``-initrd`` options +to load a Linux kernel and optional disk image. + +.. code-block:: bash + + $ qemu-system-or1k -cpu or1220 -M or1k-sim -nographic \ + -kernel vmlinux \ + -initrd initramfs.cpio.gz \ + -m 128 + +Linux guest kernel configuration +"""""""""""""""""""""""""""""""" + +The 'or1ksim_defconfig' for Linux openrisc kernels includes the right +drivers for the or1ksim machine. If you would like to run an SMP system +choose the 'simple_smp_defconfig' config. + +Hardware configuration information +"""""""""""""""""""""""""""""""""" + +The ``or1k-sim`` board automatically generates a device tree blob ("dtb") +which it passes to the guest. This provides information about the +addresses, interrupt lines and other configuration of the various devices +in the system. + +The location of the DTB will be passed in register ``r3`` to the guest operating +system. diff --git a/docs/system/openrisc/virt.rst b/docs/system/openrisc/virt.rst new file mode 100644 index 0000000000..2fe61ac942 --- /dev/null +++ b/docs/system/openrisc/virt.rst @@ -0,0 +1,50 @@ +'virt' generic virtual platform +=============================== + +The ``virt`` board is a platform which does not correspond to any +real hardware; it is designed for use in virtual machines. +It is the recommended board type if you simply want to run +a guest such as Linux and do not care about reproducing the +idiosyncrasies and limitations of a particular bit of real-world +hardware. + +Supported devices +----------------- + + * PCI/PCIe devices + * 8 virtio-mmio transport devices + * 16550A UART + * Goldfish RTC + * SiFive Test device for poweroff and reboot + * SMP (OpenRISC multicore using ompic) + +Boot options +------------ + +The virt machine can be started using the ``-kernel`` and ``-initrd`` options +to load a Linux kernel and optional disk image. For example: + +.. code-block:: bash + + $ qemu-system-or1k -cpu or1220 -M or1k-sim -nographic \ + -device virtio-net-device,netdev=user -netdev user,id=user,net=10.9.0.1/24,host=10.9.0.100 \ + -device virtio-blk-device,drive=d0 -drive file=virt.qcow2,id=d0,if=none,format=qcow2 \ + -kernel vmlinux \ + -initrd initramfs.cpio.gz \ + -m 128 + +Linux guest kernel configuration +"""""""""""""""""""""""""""""""" + +The 'virt_defconfig' for Linux openrisc kernels includes the right drivers for +the ``virt`` machine. + +Hardware configuration information +"""""""""""""""""""""""""""""""""" + +The ``virt`` board automatically generates a device tree blob ("dtb") which it +passes to the guest. This provides information about the addresses, interrupt +lines and other configuration of the various devices in the system. + +The location of the DTB will be passed in register ``r3`` to the guest operating +system. diff --git a/docs/system/ppc/amigang.rst b/docs/system/ppc/amigang.rst new file mode 100644 index 0000000000..e2c9cb74b7 --- /dev/null +++ b/docs/system/ppc/amigang.rst @@ -0,0 +1,161 @@ +========================================================= +AmigaNG boards (``amigaone``, ``pegasos2``, ``sam460ex``) +========================================================= + +These PowerPC machines emulate boards that are primarily used for +running Amiga like OSes (AmigaOS 4, MorphOS and AROS) but these can +also run Linux which is what this section documents. + +Eyetech AmigaOne/Mai Logic Teron (``amigaone``) +=============================================== + +The ``amigaone`` machine emulates an AmigaOne XE mainboard by Eyetech +which is a rebranded Mai Logic Teron board with modified U-Boot +firmware to support AmigaOS 4. + +Emulated devices +---------------- + + * PowerPC 7457 CPU (can also use ``-cpu g3, 750cxe, 750fx`` or ``750gx``) + * Articia S north bridge + * VIA VT82C686B south bridge + * PCI VGA compatible card (guests may need other card instead) + * PS/2 keyboard and mouse + +Firmware +-------- + +A firmware binary is necessary for the boot process. It is a modified +U-Boot under GPL but its source is lost so it cannot be included in +QEMU. A binary is available at +https://www.hyperion-entertainment.com/index.php/downloads?view=files&parent=28. +The ROM image is in the last 512kB which can be extracted with the +following command: + +.. code-block:: bash + + $ tail -c 524288 updater.image > u-boot-amigaone.bin + +The BIOS emulator in the firmware is unable to run QEMU‘s standard +vgabios so ``VGABIOS-lgpl-latest.bin`` is needed instead which can be +downloaded from http://www.nongnu.org/vgabios. + +Running Linux +------------- + +There are some Linux images under the following link that work on the +``amigaone`` machine: +https://sourceforge.net/projects/amigaone-linux/files/debian-installer/. +To boot the system run: + +.. code-block:: bash + + $ qemu-system-ppc -machine amigaone -bios u-boot-amigaone.bin \ + -cdrom "A1 Linux Net Installer.iso" \ + -device ati-vga,model=rv100,romfile=VGABIOS-lgpl-latest.bin + +From the firmware menu that appears select ``Boot sequence`` → +``Amiga Multiboot Options`` and set ``Boot device 1`` to +``Onboard VIA IDE CDROM``. Then hit escape until the main screen appears again, +hit escape once more and from the exit menu that appears select either +``Save settings and exit`` or ``Use settings for this session only``. It may +take a long time loading the kernel into memory but eventually it boots and the +installer becomes visible. The ``ati-vga`` RV100 emulation is not +complete yet so only frame buffer works, DRM and 3D is not available. + +Genesi/bPlan Pegasos II (``pegasos2``) +====================================== + +The ``pegasos2`` machine emulates the Pegasos II sold by Genesi and +designed by bPlan. Its schematics are available at +https://www.powerdeveloper.org/platforms/pegasos/schematics. + +Emulated devices +---------------- + + * PowerPC 7457 CPU (can also use ``-cpu g3`` or ``750cxe``) + * Marvell MV64361 Discovery II north bridge + * VIA VT8231 south bridge + * PCI VGA compatible card (guests may need other card instead) + * PS/2 keyboard and mouse + +Firmware +-------- + +The Pegasos II board has an Open Firmware compliant ROM based on +SmartFirmware with some changes that are not open-sourced therefore +the ROM binary cannot be included in QEMU. An updater was available +from bPlan, it can be found in the `Internet Archive +`_. +The ROM image can be extracted from it with the following command: + +.. code-block:: bash + + $ tail -c +85581 up050404 | head -c 524288 > pegasos2.rom + +Running Linux +------------- + +The PowerPC version of Debian 8.11 supported Pegasos II. The BIOS +emulator in the firmware binary is unable to run QEMU‘s standard +vgabios so it needs to be disabled. To boot the system run: + +.. code-block:: bash + + $ qemu-system-ppc -machine pegasos2 -bios pegasos2.rom \ + -cdrom debian-8.11.0-powerpc-netinst.iso \ + -device VGA,romfile="" -serial stdio + +At the firmware ``ok`` prompt enter ``boot cd install/pegasos``. + +Alternatively, it is possible to boot the kernel directly without +firmware ROM using the QEMU built-in minimal Virtual Open Firmware +(VOF) emulation which is also supported on ``pegasos2``. For this, +extract the kernel ``install/powerpc/vmlinuz-chrp.initrd`` from the CD +image, then run: + +.. code-block:: bash + + $ qemu-system-ppc -machine pegasos2 -serial stdio \ + -kernel vmlinuz-chrp.initrd -append "---" \ + -cdrom debian-8.11.0-powerpc-netinst.iso + +aCube Sam460ex (``sam460ex``) +============================= + +The ``sam460ex`` machine emulates the Sam460ex board by aCube which is +based on the AMCC PowerPC 460EX SoC (that despite its name has a +PPC440 CPU core). + +Firmware +-------- + +The board has a firmware based on an older U-Boot version with +modifications to support booting AmigaOS 4. The firmware ROM is +included with QEMU. + +Emulated devices +---------------- + + * PowerPC 460EX SoC + * M41T80 serial RTC chip + * Silicon Motion SM501 display parts (identical to SM502 on real board) + * Silicon Image SiI3112 2 port SATA controller + * USB keyboard and mouse + +Running Linux +------------- + +The only Linux distro that supported Sam460ex out of box was CruxPPC +2.x. It can be booted by running: + +.. code-block:: bash + + $ qemu-system-ppc -machine sam460ex -serial stdio \ + -drive if=none,id=cd,format=raw,file=crux-ppc-2.7a.iso \ + -device ide-cd,drive=cd,bus=ide.1 + +There are some other kernels and instructions for booting other +distros on aCube's product page at +https://www.acube-systems.biz/index.php?page=hardware&pid=5 +but those are untested. diff --git a/docs/system/ppc/embedded.rst b/docs/system/ppc/embedded.rst index cfffbda24d..af3b3d9fa4 100644 --- a/docs/system/ppc/embedded.rst +++ b/docs/system/ppc/embedded.rst @@ -6,5 +6,4 @@ Embedded family boards - ``ppce500`` generic paravirt e500 platform - ``ref405ep`` ref405ep - ``sam460ex`` aCube Sam460ex -- ``taihu`` taihu - ``virtex-ml507`` Xilinx Virtex ML507 reference design diff --git a/docs/system/ppc/powernv.rst b/docs/system/ppc/powernv.rst index c8f9762342..09f3965858 100644 --- a/docs/system/ppc/powernv.rst +++ b/docs/system/ppc/powernv.rst @@ -195,11 +195,6 @@ Use a MTD drive to add a PNOR to the machine, and get a NVRAM : -drive file=./witherspoon.pnor,format=raw,if=mtd -CAVEATS -------- - - * No support for multiple HW threads (SMT=1). Same as pseries. - Maintainer contact information ------------------------------ diff --git a/docs/system/ppc/ppce500.rst b/docs/system/ppc/ppce500.rst index 9beef39171..c9fe0915dc 100644 --- a/docs/system/ppc/ppce500.rst +++ b/docs/system/ppc/ppce500.rst @@ -19,6 +19,7 @@ The ``ppce500`` machine supports the following devices: * Power-off functionality via one GPIO pin * 1 Freescale MPC8xxx PCI host controller * VirtIO devices via PCI bus +* 1 Freescale Enhanced Secure Digital Host controller (eSDHC) * 1 Freescale Enhanced Triple Speed Ethernet controller (eTSEC) Hardware configuration information @@ -113,7 +114,7 @@ To boot the 32-bit Linux kernel: .. code-block:: bash - $ qemu-system-ppc{64|32} -M ppce500 -cpu e500mc -smp 4 -m 2G \ + $ qemu-system-ppc64 -M ppce500 -cpu e500mc -smp 4 -m 2G \ -display none -serial stdio \ -kernel vmlinux \ -initrd /path/to/rootfs.cpio \ @@ -146,15 +147,18 @@ You can specify a real world SoC device that QEMU has built-in support but all these SoCs are e500v2 based MPC85xx series, hence you cannot test anything built for P4080 (e500mc), P5020 (e5500) and T2080 (e6500). +Networking +---------- + By default a VirtIO standard PCI networking device is connected as an ethernet interface at PCI address 0.1.0, but we can switch that to an e1000 NIC by: .. code-block:: bash - $ qemu-system-ppc -M ppce500 -smp 4 -m 2G \ - -display none -serial stdio \ - -bios u-boot \ - -nic tap,ifname=tap0,script=no,downscript=no,model=e1000 + $ qemu-system-ppc64 -M ppce500 -smp 4 -m 2G \ + -display none -serial stdio \ + -bios u-boot \ + -nic tap,ifname=tap0,script=no,downscript=no,model=e1000 The QEMU ``ppce500`` machine can also dynamically instantiate an eTSEC device if “-device eTSEC” is given to QEMU: @@ -162,3 +166,30 @@ if “-device eTSEC” is given to QEMU: .. code-block:: bash -netdev tap,ifname=tap0,script=no,downscript=no,id=net0 -device eTSEC,netdev=net0 + +Root file system on flash drive +------------------------------- + +Rather than using a root file system on ram disk, it is possible to have it on +CFI flash. Given an ext2 image whose size must be a power of two, it can be used +as follows: + +.. code-block:: bash + + $ qemu-system-ppc64 -M ppce500 -cpu e500mc -smp 4 -m 2G \ + -display none -serial stdio \ + -kernel vmlinux \ + -drive if=pflash,file=/path/to/rootfs.ext2,format=raw \ + -append "rootwait root=/dev/mtdblock0" + +Alternatively, the root file system can also reside on an emulated SD card +whose size must again be a power of two: + +.. code-block:: bash + + $ qemu-system-ppc64 -M ppce500 -cpu e500mc -smp 4 -m 2G \ + -display none -serial stdio \ + -kernel vmlinux \ + -device sd-card,drive=mydrive \ + -drive id=mydrive,if=none,file=/path/to/rootfs.ext2,format=raw \ + -append "rootwait root=/dev/mmcblk0" diff --git a/docs/system/ppc/pseries.rst b/docs/system/ppc/pseries.rst index 3e1bbe6726..a876d897b6 100644 --- a/docs/system/ppc/pseries.rst +++ b/docs/system/ppc/pseries.rst @@ -62,7 +62,7 @@ Booting via ``-kernel`` supports the following: +-------------------+-------------------+------------------+ | vmlinux LE | ✓ | ✓ | +-------------------+-------------------+------------------+ -| zImage.pseries BE | x | ✓¹ | +| zImage.pseries BE | ✓¹ | ✓¹ | +-------------------+-------------------+------------------+ | zImage.pseries LE | ✓ | ✓ | +-------------------+-------------------+------------------+ diff --git a/docs/system/qemu-block-drivers.rst.inc b/docs/system/qemu-block-drivers.rst.inc index dfe5d2293d..105cb9679c 100644 --- a/docs/system/qemu-block-drivers.rst.inc +++ b/docs/system/qemu-block-drivers.rst.inc @@ -430,6 +430,12 @@ Hard disks you may corrupt your host data (use the ``-snapshot`` command line option or modify the device permissions accordingly). +Zoned block devices + Zoned block devices can be passed through to the guest if the emulated storage + controller supports zoned storage. Use ``--blockdev host_device, + node-name=drive0,filename=/dev/nullb0,cache.direct=on`` to pass through + ``/dev/nullb0`` as ``drive0``. + Windows ^^^^^^^ diff --git a/docs/system/qemu-manpage.rst b/docs/system/qemu-manpage.rst index c47a412758..3ade4ee45b 100644 --- a/docs/system/qemu-manpage.rst +++ b/docs/system/qemu-manpage.rst @@ -31,6 +31,11 @@ Options disk_image is a raw hard disk image for IDE hard disk 0. Some targets do not need a disk image. +When dealing with options parameters as arbitrary strings containing +commas, such as in "file=my,file" and "string=a,b", it's necessary to +double the commas. For instance,"-fw_cfg name=z,string=a,,b" will be +parsed as "-fw_cfg name=z,string=a,b". + .. hxtool-doc:: qemu-options.hx .. include:: keys.rst.inc diff --git a/docs/system/quickstart.rst b/docs/system/quickstart.rst deleted file mode 100644 index 681678c86e..0000000000 --- a/docs/system/quickstart.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _pcsys_005fquickstart: - -Quick Start ------------ - -Download and uncompress a PC hard disk image with Linux installed (e.g. -``linux.img``) and type: - -.. parsed-literal:: - - |qemu_system| linux.img - -Linux should boot and give you a prompt. - -Users should be aware the above example elides a lot of the complexity -of setting up a VM with x86_64 specific defaults and assumes the -first non switch argument is a PC compatible disk image with a boot -sector. For a non-x86 system where we emulate a broad range of machine -types, the command lines are generally more explicit in defining the -machine and boot behaviour. You will find more example command lines -in the :ref:`system-targets-ref` section of the manual. diff --git a/docs/system/replay.rst b/docs/system/replay.rst index 3105327423..ca7c17c63d 100644 --- a/docs/system/replay.rst +++ b/docs/system/replay.rst @@ -181,7 +181,7 @@ Audio data is recorded and replay automatically. The command line for recording and replaying must contain identical specifications of audio hardware, e.g.: .. parsed-literal:: - -soundhw ac97 + -audio pa,model=ac97 Serial ports ------------ diff --git a/docs/system/riscv/sifive_u.rst b/docs/system/riscv/sifive_u.rst index 7b166567f9..8f55ae8e31 100644 --- a/docs/system/riscv/sifive_u.rst +++ b/docs/system/riscv/sifive_u.rst @@ -210,7 +210,7 @@ command line options with ``qemu-system-riscv32``. Running U-Boot -------------- -U-Boot mainline v2021.07 release is tested at the time of writing. To build a +U-Boot mainline v2024.01 release is tested at the time of writing. To build a U-Boot mainline bootloader that can be booted by the ``sifive_u`` machine, use the sifive_unleashed_defconfig with similar commands as described above for Linux: @@ -325,15 +325,10 @@ configuration of U-Boot: $ export CROSS_COMPILE=riscv64-linux- $ make sifive_unleashed_defconfig - $ make menuconfig - -then manually select the following configuration: - - * Device Tree Control ---> Provider of DTB for DT Control ---> Prior Stage bootloader DTB - -and unselect the following configuration: - - * Library routines ---> Allow access to binman information in the device tree + $ ./scripts/config --enable OF_BOARD + $ ./scripts/config --disable BINMAN_FDT + $ ./scripts/config --disable SPL + $ make olddefconfig This changes U-Boot to use the QEMU generated device tree blob, and bypass running the U-Boot SPL stage. @@ -352,17 +347,13 @@ It's possible to create a 32-bit U-Boot S-mode image as well. $ export CROSS_COMPILE=riscv64-linux- $ make sifive_unleashed_defconfig - $ make menuconfig - -then manually update the following configuration in U-Boot: - - * Device Tree Control ---> Provider of DTB for DT Control ---> Prior Stage bootloader DTB - * RISC-V architecture ---> Base ISA ---> RV32I - * Boot options ---> Boot images ---> Text Base ---> 0x80400000 - -and unselect the following configuration: - - * Library routines ---> Allow access to binman information in the device tree + $ ./scripts/config --disable ARCH_RV64I + $ ./scripts/config --enable ARCH_RV32I + $ ./scripts/config --set-val TEXT_BASE 0x80400000 + $ ./scripts/config --enable OF_BOARD + $ ./scripts/config --disable BINMAN_FDT + $ ./scripts/config --disable SPL + $ make olddefconfig Use the same command line options to boot the 32-bit U-Boot S-mode image: diff --git a/docs/system/riscv/virt.rst b/docs/system/riscv/virt.rst index f8ecec95f3..9a06f95a34 100644 --- a/docs/system/riscv/virt.rst +++ b/docs/system/riscv/virt.rst @@ -12,7 +12,7 @@ Supported devices The ``virt`` machine supports the following devices: -* Up to 8 generic RV32GC/RV64GC cores, with optional extensions +* Up to 512 generic RV32GC/RV64GC cores, with optional extensions * Core Local Interruptor (CLINT) * Platform-Level Interrupt Controller (PLIC) * CFI parallel NOR flash memory @@ -53,6 +53,37 @@ with the default OpenSBI firmware image as the -bios. It also supports the recommended RISC-V bootflow: U-Boot SPL (M-mode) loads OpenSBI fw_dynamic firmware and U-Boot proper (S-mode), using the standard -bios functionality. +Using flash devices +------------------- + +By default, the first flash device (pflash0) is expected to contain +S-mode firmware code. It can be configured as read-only, with the +second flash device (pflash1) available to store configuration data. + +For example, booting edk2 looks like + +.. code-block:: bash + + $ qemu-system-riscv64 \ + -blockdev node-name=pflash0,driver=file,read-only=on,filename= \ + -blockdev node-name=pflash1,driver=file,filename= \ + -M virt,pflash0=pflash0,pflash1=pflash1 \ + ... other args .... + +For TCG guests only, it is also possible to boot M-mode firmware from +the first flash device (pflash0) by additionally passing ``-bios +none``, as in + +.. code-block:: bash + + $ qemu-system-riscv64 \ + -bios none \ + -blockdev node-name=pflash0,driver=file,read-only=on,filename= \ + -M virt,pflash0=pflash0 \ + ... other args .... + +Firmware images used for pflash must be exactly 32 MiB in size. + Machine-specific options ------------------------ @@ -62,6 +93,12 @@ The following machine-specific options are supported: When this option is "on", ACLINT devices will be emulated instead of SiFive CLINT. When not specified, this option is assumed to be "off". + This option is restricted to the TCG accelerator. + +- acpi=[on|off|auto] + + When this option is "on" (which is the default), ACPI tables are generated and + exposed as firmware tables etc/acpi/rsdp and etc/acpi/tables. - aia=[none|aplic|aplic-imsic] @@ -168,14 +205,19 @@ Enabling TPM A TPM device can be connected to the virt board by following the steps below. -First launch the TPM emulator +First launch the TPM emulator: - swtpm socket --tpm2 -t -d --tpmstate dir=/tmp/tpm \ +.. code-block:: bash + + $ swtpm socket --tpm2 -t -d --tpmstate dir=/tmp/tpm \ --ctrl type=unixio,path=swtpm-sock -Then launch QEMU with: +Then launch QEMU with some additional arguments to link a TPM device to the backend: - ... +.. code-block:: bash + + $ qemu-system-riscv64 \ + ... other args .... \ -chardev socket,id=chrtpm,path=swtpm-sock \ -tpmdev emulator,id=tpm0,chardev=chrtpm \ -device tpm-tis-device,tpmdev=tpm0 diff --git a/docs/system/s390x/bootdevices.rst b/docs/system/s390x/bootdevices.rst index 9e591cb9dc..1a7a18b43b 100644 --- a/docs/system/s390x/bootdevices.rst +++ b/docs/system/s390x/bootdevices.rst @@ -53,6 +53,32 @@ recommended to specify a CD-ROM device via ``-device scsi-cd`` (as mentioned above) instead. +Selecting kernels with the ``loadparm`` property +------------------------------------------------ + +The ``s390-ccw-virtio`` machine supports the so-called ``loadparm`` parameter +which can be used to select the kernel on the disk of the guest that the +s390-ccw bios should boot. When starting QEMU, it can be specified like this:: + + qemu-system-s390x -machine s390-ccw-virtio,loadparm= + +The first way to use this parameter is to use the word ``PROMPT`` as the +```` here. In that case the s390-ccw bios will show a list of +installed kernels on the disk of the guest and ask the user to enter a number +to chose which kernel should be booted -- similar to what can be achieved by +specifying the ``-boot menu=on`` option when starting QEMU. Note that the menu +list will only show the names of the installed kernels when using a DASD-like +disk image with 4k byte sectors. On normal SCSI-style disks with 512-byte +sectors, there is not enough space for the zipl loader on the disk to store +the kernel names, so you only get a list without names here. + +The second way to use this parameter is to use a number in the range from 0 +to 31. The numbers that can be used here correspond to the numbers that are +shown when using the ``PROMPT`` option, and the s390-ccw bios will then try +to automatically boot the kernel that is associated with the given number. +Note that ``0`` can be used to boot the default entry. + + Booting from a network device ----------------------------- @@ -65,7 +91,7 @@ you can specify it via the ``-global s390-ipl.netboot_fw=filename`` command line option. The ``bootindex`` property is especially important for booting via the network. -If you don't specify the the ``bootindex`` property here, the network bootloader +If you don't specify the ``bootindex`` property here, the network bootloader firmware code won't get loaded into the guest memory so that the network boot will fail. For a successful network boot, try something like this:: diff --git a/docs/system/s390x/cpu-topology.rst b/docs/system/s390x/cpu-topology.rst new file mode 100644 index 0000000000..d5b506ee5c --- /dev/null +++ b/docs/system/s390x/cpu-topology.rst @@ -0,0 +1,246 @@ +.. _cpu-topology-s390x: + +CPU topology on s390x +===================== + +Since QEMU 8.2, CPU topology on s390x provides up to 3 levels of +topology containers: drawers, books and sockets. They define a +tree-shaped hierarchy. + +The socket container has one or more CPU entries. +Each of these CPU entries consists of a bitmap and three CPU attributes: + +- CPU type +- entitlement +- dedication + +Each bit set in the bitmap correspond to a core-id of a vCPU with matching +attributes. + +This documentation provides general information on S390 CPU topology, +how to enable it and explains the new CPU attributes. +For information on how to modify the S390 CPU topology and how to +monitor polarization changes, see ``docs/devel/s390-cpu-topology.rst``. + +Prerequisites +------------- + +To use the CPU topology, you currently need to choose the KVM accelerator. +See :ref:`Accelerators` for more details about accelerators and how to select them. + +The s390x host needs to use a Linux kernel v6.0 or newer (which provides the so-called +``KVM_CAP_S390_CPU_TOPOLOGY`` capability that allows QEMU to signal the +CPU topology facility via the so-called STFLE bit 11 to the VM). + +Enabling CPU topology +--------------------- + +Currently, CPU topology is enabled by default only in the "host" CPU model. + +Enabling CPU topology in another CPU model is done by setting the CPU flag +``ctop`` to ``on`` as in: + +.. code-block:: bash + + -cpu gen16b,ctop=on + +Having the topology disabled by default allows migration between +old and new QEMU without adding new flags. + +Default topology usage +---------------------- + +The CPU topology can be specified on the QEMU command line +with the ``-smp`` or the ``-device`` QEMU command arguments. + +Note also that since 7.2 threads are no longer supported in the topology +and the ``-smp`` command line argument accepts only ``threads=1``. + +If none of the containers attributes (drawers, books, sockets) are +specified for the ``-smp`` flag, the number of these containers +is 1. + +Thus the following two options will result in the same topology: + +.. code-block:: bash + + -smp cpus=5,drawer=1,books=1,sockets=8,cores=4,maxcpus=32 + +and + +.. code-block:: bash + + -smp cpus=5,sockets=8,cores=4,maxcpus=32 + +When a CPU is defined by the ``-smp`` command argument, its position +inside the topology is calculated by adding the CPUs to the topology +based on the core-id starting with core-0 at position 0 of socket-0, +book-0, drawer-0 and filling all CPUs of socket-0 before filling socket-1 +of book-0 and so on up to the last socket of the last book of the last +drawer. + +When a CPU is defined by the ``-device`` command argument, the +tree topology attributes must all be defined or all not defined. + +.. code-block:: bash + + -device gen16b-s390x-cpu,drawer-id=1,book-id=1,socket-id=2,core-id=1 + +or + +.. code-block:: bash + + -device gen16b-s390x-cpu,core-id=1,dedicated=true + +If none of the tree attributes (drawer, book, sockets), are specified +for the ``-device`` argument, like for all CPUs defined with the ``-smp`` +command argument the topology tree attributes will be set by simply +adding the CPUs to the topology based on the core-id. + +QEMU will not try to resolve collisions and will report an error if the +CPU topology defined explicitly or implicitly on a ``-device`` +argument collides with the definition of a CPU implicitly defined +on the ``-smp`` argument. + +When the topology modifier attributes are not defined for the +``-device`` command argument they takes following default values: + +- dedicated: ``false`` +- entitlement: ``medium`` + + +Hot plug +++++++++ + +New CPUs can be plugged using the device_add hmp command as in: + +.. code-block:: bash + + (qemu) device_add gen16b-s390x-cpu,core-id=9 + +The placement of the CPU is derived from the core-id as described above. + +The topology can of course also be fully defined: + +.. code-block:: bash + + (qemu) device_add gen16b-s390x-cpu,drawer-id=1,book-id=1,socket-id=2,core-id=1 + + +Examples +++++++++ + +In the following machine we define 8 sockets with 4 cores each. + +.. code-block:: bash + + $ qemu-system-s390x -accel kvm -m 2G \ + -cpu gen16b,ctop=on \ + -smp cpus=5,sockets=8,cores=4,maxcpus=32 \ + -device host-s390x-cpu,core-id=14 \ + +A new CPUs can be plugged using the device_add hmp command as before: + +.. code-block:: bash + + (qemu) device_add gen16b-s390x-cpu,core-id=9 + +The core-id defines the placement of the core in the topology by +starting with core 0 in socket 0 up to maxcpus. + +In the example above: + +* There are 5 CPUs provided to the guest with the ``-smp`` command line + They will take the core-ids 0,1,2,3,4 + As we have 4 cores in a socket, we have 4 CPUs provided + to the guest in socket 0, with core-ids 0,1,2,3. + The last CPU, with core-id 4, will be on socket 1. + +* the core with ID 14 provided by the ``-device`` command line will + be placed in socket 3, with core-id 14 + +* the core with ID 9 provided by the ``device_add`` qmp command will + be placed in socket 2, with core-id 9 + + +Polarization, entitlement and dedication +---------------------------------------- + +Polarization +++++++++++++ + +The polarization affects how the CPUs of a shared host are utilized/distributed +among guests. +The guest determines the polarization by using the PTF instruction. + +Polarization defines two models of CPU provisioning: horizontal +and vertical. + +The horizontal polarization is the default model on boot and after +subsystem reset. When horizontal polarization is in effect all vCPUs should +have about equal resource provisioning. + +In the vertical polarization model vCPUs are unequal, but overall more resources +might be available. +The guest can make use of the vCPU entitlement information provided by the host +to optimize kernel thread scheduling. + +A subsystem reset puts all vCPU of the configuration into the +horizontal polarization. + +Entitlement ++++++++++++ + +The vertical polarization specifies that the guest's vCPU can get +different real CPU provisioning: + +- a vCPU with vertical high entitlement specifies that this + vCPU gets 100% of the real CPU provisioning. + +- a vCPU with vertical medium entitlement specifies that this + vCPU shares the real CPU with other vCPUs. + +- a vCPU with vertical low entitlement specifies that this + vCPU only gets real CPU provisioning when no other vCPUs needs it. + +In the case a vCPU with vertical high entitlement does not use +the real CPU, the unused "slack" can be dispatched to other vCPU +with medium or low entitlement. + +A vCPU can be "dedicated" in which case the vCPU is fully dedicated to a single +real CPU. + +The dedicated bit is an indication of affinity of a vCPU for a real CPU +while the entitlement indicates the sharing or exclusivity of use. + +Defining the topology on the command line +----------------------------------------- + +The topology can entirely be defined using -device cpu statements, +with the exception of CPU 0 which must be defined with the -smp +argument. + +For example, here we set the position of the cores 1,2,3 to +drawer 1, book 1, socket 2 and cores 0,9 and 14 to drawer 0, +book 0, socket 0 without defining entitlement or dedication. +Core 4 will be set on its default position on socket 1 +(since we have 4 core per socket) and we define it as dedicated and +with vertical high entitlement. + +.. code-block:: bash + + $ qemu-system-s390x -accel kvm -m 2G \ + -cpu gen16b,ctop=on \ + -smp cpus=1,sockets=8,cores=4,maxcpus=32 \ + \ + -device gen16b-s390x-cpu,drawer-id=1,book-id=1,socket-id=2,core-id=1 \ + -device gen16b-s390x-cpu,drawer-id=1,book-id=1,socket-id=2,core-id=2 \ + -device gen16b-s390x-cpu,drawer-id=1,book-id=1,socket-id=2,core-id=3 \ + \ + -device gen16b-s390x-cpu,drawer-id=0,book-id=0,socket-id=0,core-id=9 \ + -device gen16b-s390x-cpu,drawer-id=0,book-id=0,socket-id=0,core-id=14 \ + \ + -device gen16b-s390x-cpu,core-id=4,dedicated=on,entitlement=high + +The entitlement defined for the CPU 4 will only be used after the guest +successfully enables vertical polarization by using the PTF instruction. diff --git a/docs/system/s390x/pcidevices.rst b/docs/system/s390x/pcidevices.rst new file mode 100644 index 0000000000..628effa2f4 --- /dev/null +++ b/docs/system/s390x/pcidevices.rst @@ -0,0 +1,41 @@ +PCI devices on s390x +==================== + +PCI devices on s390x work differently than on other architectures and need to +be configured in a slightly different way. + +Every PCI device is linked with an additional ``zpci`` device. +While the ``zpci`` device will be autogenerated if not specified, it is +recommended to specify it explicitly so that you can pass s390-specific +PCI configuration. + +For example, in order to pass a PCI device ``0000:00:00.0`` through to the +guest, you would specify:: + + qemu-system-s390x ... \ + -device zpci,uid=1,fid=0,target=hostdev0,id=zpci1 \ + -device vfio-pci,host=0000:00:00.0,id=hostdev0 + +Here, the zpci device is joined with the PCI device via the ``target`` property. + +Note that we don't set bus, slot or function here for the guest as is common in +other PCI implementations. Topology information is not available on s390x, and +the guest will not see any of the bus, slot or function information specified +on the command line. + +Instead, ``uid`` and ``fid`` determine how the device is presented to the guest +operating system. + +In case of Linux, ``uid`` will be used in the ``domain`` part of the PCI +identifier, and ``fid`` identifies the physical slot, i.e.:: + + qemu-system-s390x ... \ + -device zpci,uid=7,fid=8,target=hostdev0,id=zpci1 \ + ... + +will be presented in the guest as:: + + # lspci -v + 0007:00:00.0 ... + Physical Slot: 00000008 + ... diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index 91ebc26c6d..c9d7c0dda7 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -83,6 +83,8 @@ undocumented; you can get a complete list by running arm/versatile arm/vexpress arm/aspeed + arm/bananapi_m2u.rst + arm/b-l475e-iot01a.rst arm/sabrelite arm/digic arm/cubieboard @@ -106,6 +108,7 @@ undocumented; you can get a complete list by running arm/stm32 arm/virt arm/xlnx-versal-virt + arm/xenpvh Emulated CPU architecture support ================================= diff --git a/docs/system/target-i386-desc.rst.inc b/docs/system/target-i386-desc.rst.inc index 7d1fffacbe..5ebbcda9db 100644 --- a/docs/system/target-i386-desc.rst.inc +++ b/docs/system/target-i386-desc.rst.inc @@ -71,3 +71,11 @@ machine property, i.e. |qemu_system_x86| some.img \ -audiodev ,id= \ -machine pcspk-audiodev= + +Machine-specific options +~~~~~~~~~~~~~~~~~~~~~~~~ + +It supports the following machine-specific options: + +- ``x-south-bridge=PIIX3|piix4-isa`` (Experimental option to select a particular + south bridge. Default: ``PIIX3``) diff --git a/docs/system/target-i386.rst b/docs/system/target-i386.rst index e64c013077..1b8a1f248a 100644 --- a/docs/system/target-i386.rst +++ b/docs/system/target-i386.rst @@ -3,8 +3,6 @@ x86 System emulator ------------------- -.. _pcsys_005fdevices: - Board-specific documentation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -27,12 +25,11 @@ Architectural features i386/cpu i386/hyperv + i386/xen i386/kvm-pv i386/sgx i386/amd-memory-encryption -.. _pcsys_005freq: - OS requirements ~~~~~~~~~~~~~~~ diff --git a/docs/system/target-mips.rst b/docs/system/target-mips.rst index 138441bdec..83239fb9df 100644 --- a/docs/system/target-mips.rst +++ b/docs/system/target-mips.rst @@ -8,8 +8,6 @@ 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. @@ -19,18 +17,6 @@ machine types are emulated: - 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 diff --git a/docs/system/target-openrisc.rst b/docs/system/target-openrisc.rst new file mode 100644 index 0000000000..22cb2217a6 --- /dev/null +++ b/docs/system/target-openrisc.rst @@ -0,0 +1,71 @@ +.. _OpenRISC-System-emulator: + +OpenRISC System emulator +~~~~~~~~~~~~~~~~~~~~~~~~ + +QEMU can emulate 32-bit OpenRISC CPUs using the ``qemu-system-or1k`` executable. + +OpenRISC CPUs are generally built into "system-on-chip" (SoC) designs that run +on FPGAs. These SoCs are based on the same core architecture as the or1ksim +(the original OpenRISC instruction level simulator) which QEMU supports. For +this reason QEMU does not need to support many different boards to support the +OpenRISC hardware ecosystem. + +The OpenRISC CPU supported by QEMU is the ``or1200``, it supports an MMU and can +run linux. + +Choosing a board model +====================== + +For QEMU's OpenRISC system emulation, you must specify which board model you +want to use with the ``-M`` or ``--machine`` option; the default machine is +``or1k-sim``. + +If you intend to boot Linux, it is possible to have a single kernel image that +will boot on any of the QEMU machines. To do this one would compile all required +drivers into the kernel. This is possible because QEMU will create a device tree +structure that describes the QEMU machine and pass a pointer to the structure to +the kernel. The kernel can then use this to configure itself for the machine. + +However, typically users will have specific firmware images for a specific machine. + +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 +and large amounts of RAM. + +Board-specific documentation +============================ + +.. + This table of contents should be kept sorted alphabetically + by the title text of each file, which isn't the same ordering + as an alphabetical sort by filename. + +.. toctree:: + :maxdepth: 1 + + openrisc/or1k-sim + openrisc/virt + +Emulated CPU architecture support +================================= + +.. toctree:: + openrisc/emulation + +OpenRISC CPU features +===================== + +.. toctree:: + openrisc/cpu-features diff --git a/docs/system/target-ppc.rst b/docs/system/target-ppc.rst index 4f6eb93b17..87bf412ce5 100644 --- a/docs/system/target-ppc.rst +++ b/docs/system/target-ppc.rst @@ -17,6 +17,7 @@ help``. .. toctree:: :maxdepth: 1 + ppc/amigang ppc/embedded ppc/powermac ppc/powernv diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst index 89a866e4f4..ba195f1518 100644 --- a/docs/system/target-riscv.rst +++ b/docs/system/target-riscv.rst @@ -76,11 +76,19 @@ RISC-V CPU firmware When using the ``sifive_u`` or ``virt`` machine there are three different firmware boot options: -1. ``-bios default`` - This is the default behaviour if no -bios option -is included. 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 -2. ``-bios none`` - QEMU will not automatically load any firmware. It is up -to the user to load all the images they need. -3. ``-bios `` - Tells QEMU to load the specified file as the firmware. + +* ``-bios default`` + +This is the default behaviour if no ``-bios`` option is included. 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 + +* ``-bios none`` + +QEMU will not automatically load any firmware. It is up to the user to load all +the images they need. + +* ``-bios `` + +Tells QEMU to load the specified file as the firmware. diff --git a/docs/system/target-s390x.rst b/docs/system/target-s390x.rst index c636f64113..94c981e732 100644 --- a/docs/system/target-s390x.rst +++ b/docs/system/target-s390x.rst @@ -26,6 +26,7 @@ or vfio-ap is also available. s390x/css s390x/3270 s390x/vfio-ccw + s390x/pcidevices Architectural features ====================== @@ -33,3 +34,4 @@ Architectural features .. toctree:: s390x/bootdevices s390x/protvirt + s390x/cpu-topology diff --git a/docs/system/target-sparc.rst b/docs/system/target-sparc.rst index b55f8d09e9..9ec8c90c14 100644 --- a/docs/system/target-sparc.rst +++ b/docs/system/target-sparc.rst @@ -38,7 +38,7 @@ QEMU emulates the following sun4m peripherals: - Non Volatile RAM M48T02/M48T08 - Slave I/O: timers, interrupt controllers, Zilog serial ports, - keyboard and power/reset logic + :ref:`keyboard` and power/reset logic - ESP SCSI controller with hard disk and CD-ROM support diff --git a/docs/system/targets.rst b/docs/system/targets.rst index 9dcd95dd84..224fadae71 100644 --- a/docs/system/targets.rst +++ b/docs/system/targets.rst @@ -21,6 +21,7 @@ Contents: target-m68k target-mips target-ppc + target-openrisc target-riscv target-rx target-s390x diff --git a/docs/system/tls.rst b/docs/system/tls.rst index 1a04674362..e284c82801 100644 --- a/docs/system/tls.rst +++ b/docs/system/tls.rst @@ -182,7 +182,7 @@ certificates. --template client-hostNNN.info \ --outfile client-hostNNN-cert.pem -The subject alt name extension data is not required for clients, so the +The subject alt name extension data is not required for clients, so 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 diff --git a/docs/system/vm-templating.rst b/docs/system/vm-templating.rst new file mode 100644 index 0000000000..28905a1eeb --- /dev/null +++ b/docs/system/vm-templating.rst @@ -0,0 +1,125 @@ +QEMU VM templating +================== + +This document explains how to use VM templating in QEMU. + +For now, the focus is on VM memory aspects, and not about how to save and +restore other VM state (i.e., migrate-to-file with ``x-ignore-shared``). + +Overview +-------- + +With VM templating, a single template VM serves as the starting point for +new VMs. This allows for fast and efficient replication of VMs, resulting +in fast startup times and reduced memory consumption. + +Conceptually, the VM state is frozen, to then be used as a basis for new +VMs. The Copy-On-Write mechanism in the operating systems makes sure that +new VMs are able to read template VM memory; however, any modifications +stay private and don't modify the original template VM or any other +created VM. + +!!! Security Alert !!! +---------------------- + +When effectively cloning VMs by VM templating, hardware identifiers +(such as UUIDs and NIC MAC addresses), and similar data in the guest OS +(such as machine IDs, SSH keys, certificates) that are supposed to be +*unique* are no longer unique, which can be a security concern. + +Please be aware of these implications and how to mitigate them for your +use case, which might involve vmgenid, hot(un)plug of NIC, etc.. + +Memory configuration +-------------------- + +In order to create the template VM, we have to make sure that VM memory +ends up in a file, from where it can be reused for the new VMs: + +Supply VM RAM via memory-backend-file, with ``share=on`` (modifications go +to the file) and ``readonly=off`` (open the file writable). Note that +``readonly=off`` is implicit. + +In the following command-line example, a 2GB VM is created, whereby VM RAM +is to be stored in the ``template`` file. + +.. parsed-literal:: + + |qemu_system| [...] -m 2g \\ + -object memory-backend-file,id=pc.ram,mem-path=template,size=2g,share=on,... \\ + -machine q35,memory-backend=pc.ram + +If multiple memory backends are used (vNUMA, DIMMs), configure all +memory backends accordingly. + +Once the VM is in the desired state, stop the VM and save other VM state, +leaving the current state of VM RAM reside in the file. + +In order to have a new VM be based on a template VM, we have to +configure VM RAM to be based on a template VM RAM file; however, the VM +should not be able to modify file content. + +Supply VM RAM via memory-backend-file, with ``share=off`` (modifications +stay private), ``readonly=on`` (open the file readonly) and ``rom=off`` +(don't make the memory readonly for the VM). Note that ``share=off`` is +implicit and that other VM state has to be restored separately. + +In the following command-line example, a 2GB VM is created based on the +existing 2GB file ``template``. + +.. parsed-literal:: + + |qemu_system| [...] -m 2g \\ + -object memory-backend-file,id=pc.ram,mem-path=template,size=2g,readonly=on,rom=off,... \\ + -machine q35,memory-backend=pc.ram + +If multiple memory backends are used (vNUMA, DIMMs), configure all +memory backends accordingly. + +Note that ``-mem-path`` cannot be used for VM templating when creating the +template VM or when starting new VMs based on a template VM. + +Incompatible features +--------------------- + +Some features are incompatible with VM templating, as the underlying file +cannot be modified to discard VM RAM, or to actually share memory with +another process. + +vhost-user and multi-process QEMU +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +vhost-user and multi-process QEMU are incompatible with VM templating. +These technologies rely on shared memory, however, the template VMs +don't actually share memory (``share=off``), even though they are +file-based. + +virtio-balloon +~~~~~~~~~~~~~~ + +virtio-balloon inflation and "free page reporting" cannot discard VM RAM +and will repeatedly report errors. While virtio-balloon can be used +for template VMs (e.g., report VM RAM stats), "free page reporting" +should be disabled and the balloon should not be inflated. + +virtio-mem +~~~~~~~~~~ + +virtio-mem cannot discard VM RAM that is managed by the virtio-mem +device. virtio-mem will fail early when realizing the device. To use +VM templating with virtio-mem, either hotplug virtio-mem devices to the +new VM, or don't supply any memory to the template VM using virtio-mem +(requested-size=0), not using a template VM file as memory backend for the +virtio-mem device. + +VM migration +~~~~~~~~~~~~ + +For VM migration, "x-release-ram" similarly relies on discarding of VM +RAM on the migration source to free up migrated RAM, and will +repeatedly report errors. + +Postcopy live migration fails discarding VM RAM on the migration +destination early and refuses to activate postcopy live migration. Note +that postcopy live migration usually only works on selected filesystems +(shmem/tmpfs, hugetlbfs) either way. diff --git a/docs/tools/index.rst b/docs/tools/index.rst index 1edd5a8054..8e65ce0dfc 100644 --- a/docs/tools/index.rst +++ b/docs/tools/index.rst @@ -1,3 +1,5 @@ +.. _Tools: + ----- Tools ----- @@ -14,4 +16,3 @@ command line utilities and other standalone programs. qemu-pr-helper qemu-trace-stap virtfs-proxy-helper - virtiofsd diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index 85a6e05b35..3653adb963 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -57,7 +57,7 @@ cases. See below for a description of the supported disk formats. *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 +name=value format. Use ``-o help`` 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 @@ -106,7 +106,11 @@ by the used format or see the format descriptions below for details. .. option:: -c - Indicates that target image must be compressed (qcow format only). + Indicates that target image must be compressed (qcow/qcow2 and vmdk with + streamOptimized subformat only). + + For qcow2, the compression algorithm can be specified with the ``-o + compression_type=...`` option (see below). .. option:: -h @@ -402,7 +406,7 @@ Command description: 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: + The following table summarizes all exit codes of the compare subcommand: 0 Images are identical (or requested help was printed) @@ -663,7 +667,7 @@ Command description: 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 +.. option:: rebase [--object OBJECTDEF] [--image-opts] [-U] [-q] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-p] [-u] [-c] -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. @@ -690,7 +694,9 @@ Command description: 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. + into *FILENAME* before actually changing the backing file. With the + ``-c`` option specified, the clusters which are being merged (but not + the entire *FILENAME* image) are compressed when written. Note that the safe mode is an expensive operation, comparable to converting an image. It only works if the old backing file still @@ -776,7 +782,7 @@ Supported image file formats: 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 + on Windows), optional AES encryption, zlib or zstd based compression and support of multiple VM snapshots. Supported options: @@ -794,6 +800,17 @@ Supported image file formats: ``backing_fmt`` Image format of the base image + ``compression_type`` + This option configures which compression algorithm will be used for + compressed clusters on the image. Note that setting this option doesn't yet + cause the image to actually receive compressed writes. It is most commonly + used with the ``-c`` option of ``qemu-img convert``, but can also be used + with the ``compress`` filter driver or backup block jobs with compression + enabled. + + Valid values are ``zlib`` and ``zstd``. For images that use + ``compat=0.10``, only ``zlib`` compression is available. + ``encryption`` If this option is set to ``on``, the image is encrypted with 128-bit AES-CBC. diff --git a/docs/tools/qemu-nbd.rst b/docs/tools/qemu-nbd.rst index 8e08a29e89..329f44d989 100644 --- a/docs/tools/qemu-nbd.rst +++ b/docs/tools/qemu-nbd.rst @@ -197,7 +197,9 @@ driver options if :option:`--image-opts` is specified. .. option:: -v, --verbose - Display extra debugging information. + Display extra debugging information. This option also keeps the original + *STDERR* stream open if the ``qemu-nbd`` process is daemonized due to + other options like :option:`--fork` or :option:`-c`. .. option:: -h, --help @@ -225,7 +227,7 @@ 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 +and allow clients with a specific X.509 certificate to connect to a 1 megabyte subset of a raw file, using the export name 'subset': :: diff --git a/docs/tools/qemu-pr-helper.rst b/docs/tools/qemu-pr-helper.rst index eaebe40da0..c32867cfc6 100644 --- a/docs/tools/qemu-pr-helper.rst +++ b/docs/tools/qemu-pr-helper.rst @@ -21,8 +21,8 @@ programs because incorrect usage can disrupt regular operation of the storage fabric. QEMU's SCSI passthrough devices ``scsi-block`` and ``scsi-generic`` support passing guest persistent reservation requests to a privileged external helper program. :program:`qemu-pr-helper` -is that external helper; it creates a socket which QEMU can -connect to to communicate with it. +is that external helper; it creates a listener socket which will +accept incoming connections for communication with QEMU. If you want to run VMs in a setup like this, this helper should be started as a system service, and you should read the QEMU manual diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst index 8b97592663..ea00149a63 100644 --- a/docs/tools/qemu-storage-daemon.rst +++ b/docs/tools/qemu-storage-daemon.rst @@ -77,6 +77,7 @@ Standard options: --export [type=]vhost-user-blk,id=,node-name=,addr.type=unix,addr.path=[,writable=on|off][,logical-block-size=][,num-queues=] --export [type=]vhost-user-blk,id=,node-name=,addr.type=fd,addr.str=[,writable=on|off][,logical-block-size=][,num-queues=] --export [type=]fuse,id=,node-name=,mountpoint=[,growable=on|off][,writable=on|off][,allow-other=on|off|auto] + --export [type=]vduse-blk,id=,node-name=,name=[,writable=on|off][,num-queues=][,queue-size=][,logical-block-size=][,serial=] is a block export definition. ``node-name`` is the block node that should be exported. ``writable`` determines whether or not the export allows write @@ -110,6 +111,27 @@ Standard options: ``allow-other`` to auto (the default) will try enabling this option, and on error fall back to disabling it. + The ``vduse-blk`` export type takes a ``name`` (must be unique across the host) + to create the VDUSE device. + ``num-queues`` sets the number of virtqueues (the default is 1). + ``queue-size`` sets the virtqueue descriptor table size (the default is 256). + + The instantiated VDUSE device must then be added to the vDPA bus using the + vdpa(8) command from the iproute2 project:: + + # vdpa dev add name mgmtdev vduse + + The device can be removed from the vDPA bus later as follows:: + + # vdpa dev del + + For more information about attaching vDPA devices to the host with + virtio_vdpa.ko or attaching them to guests with vhost_vdpa.ko, see + https://vdpa-dev.gitlab.io/. + + For more information about VDUSE, see + https://docs.kernel.org/userspace-api/vduse.html. + .. option:: --monitor MONITORDEF is a QMP monitor definition. See the :manpage:`qemu(1)` manual page for diff --git a/docs/tools/virtfs-proxy-helper.rst b/docs/tools/virtfs-proxy-helper.rst index 6cdeedf8e9..bd310ebb07 100644 --- a/docs/tools/virtfs-proxy-helper.rst +++ b/docs/tools/virtfs-proxy-helper.rst @@ -9,6 +9,9 @@ Synopsis Description ----------- +NOTE: The 9p 'proxy' backend is deprecated (since QEMU 8.1) and will be +removed, along with this daemon, in a future version of QEMU! + 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: diff --git a/docs/tools/virtiofsd.rst b/docs/tools/virtiofsd.rst deleted file mode 100644 index 5f5ac9dd56..0000000000 --- a/docs/tools/virtiofsd.rst +++ /dev/null @@ -1,403 +0,0 @@ -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. The program drops privileges where -possible during startup although it must be able to create and access files -with any uid/gid: - -* The ability to invoke syscalls is limited using seccomp(2). -* Linux capabilities(7) are dropped. - -In "namespace" sandbox mode the program switches into a new file system -namespace and invokes pivot_root(2) to make the shared directory tree its root. -A new pid and net namespace is also created to isolate the process. - -In "chroot" sandbox mode the program invokes chroot(2) to make the shared -directory tree its root. This mode is intended for container environments where -the container runtime has already set up the namespaces and the program does -not have permission to create namespaces itself. - -Both sandbox modes prevent "file system escapes" due to symlinks and other file -system objects that might lead to files outside the shared directory. - -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``. - - * modcaps=CAPLIST - Modify the list of capabilities allowed; CAPLIST is a colon separated - list of capabilities, each preceded by either + or -, e.g. - ''+sys_admin:-chown''. - - * 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``. - - * posix_lock|no_posix_lock - - Enable/disable remote POSIX locks. The default is ``no_posix_lock``. - - * readdirplus|no_readdirplus - - Enable/disable readdirplus. The default is ``readdirplus``. - - * sandbox=namespace|chroot - - Sandbox mode: - - namespace: Create mount, pid, and net namespaces and pivot_root(2) into - the shared directory. - - chroot: chroot(2) into shared directory (use in containers). - The default is "namespace". - - * 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 allows 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``. - - * posix_acl|no_posix_acl - - Enable/disable posix acl support. Posix ACLs are disabled by default. - - * security_label|no_security_label - - Enable/disable security label support. Security labels are disabled by - default. This will allow client to send a MAC label of file during - file creation. Typically this is expected to be SELinux security - label. Server will try to set that label on newly created file - atomically wherever possible. - - * killpriv_v2|no_killpriv_v2 - - Enable/disable ``FUSE_HANDLE_KILLPRIV_V2`` support. KILLPRIV_V2 is enabled - by default as long as the client supports it. Enabling this option helps - with performance in write path. - -.. option:: --socket-path=PATH - - Listen on vhost-user UNIX domain socket at PATH. - -.. option:: --socket-group=GROUP - - Set the vhost-user UNIX domain socket gid to GROUP. - -.. 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 0. - -.. 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. - The default is ``auto``. - -Extended attribute (xattr) mapping ----------------------------------- - -By default the name of xattr's used by the client are passed through to the server -file system. This can be a problem where either those xattr names are used -by something on the server (e.g. selinux client/server confusion) or if the -``virtiofsd`` is running in a container with restricted privileges where it -cannot access some attributes. - -Mapping syntax -~~~~~~~~~~~~~~ - -A mapping of xattr names can be made using -o xattrmap=mapping where the ``mapping`` -string consists of a series of rules. - -The first matching rule terminates the mapping. -The set of rules must include a terminating rule to match any remaining attributes -at the end. - -Each rule consists of a number of fields separated with a separator that is the -first non-white space character in the rule. This separator must then be used -for the whole rule. -White space may be added before and after each rule. - -Using ':' as the separator a rule is of the form: - -``:type:scope:key:prepend:`` - -**scope** is: - -- 'client' - match 'key' against a xattr name from the client for - setxattr/getxattr/removexattr -- 'server' - match 'prepend' against a xattr name from the server - for listxattr -- 'all' - can be used to make a single rule where both the server - and client matches are triggered. - -**type** is one of: - -- 'prefix' - is designed to prepend and strip a prefix; the modified - attributes then being passed on to the client/server. - -- 'ok' - Causes the rule set to be terminated when a match is found - while allowing matching xattr's through unchanged. - It is intended both as a way of explicitly terminating - the list of rules, and to allow some xattr's to skip following rules. - -- 'bad' - If a client tries to use a name matching 'key' it's - denied using EPERM; when the server passes an attribute - name matching 'prepend' it's hidden. In many ways it's use is very like - 'ok' as either an explicit terminator or for special handling of certain - patterns. - -- 'unsupported' - If a client tries to use a name matching 'key' it's - denied using ENOTSUP; when the server passes an attribute - name matching 'prepend' it's hidden. In many ways it's use is very like - 'ok' as either an explicit terminator or for special handling of certain - patterns. - -**key** is a string tested as a prefix on an attribute name originating -on the client. It maybe empty in which case a 'client' rule -will always match on client names. - -**prepend** is a string tested as a prefix on an attribute name originating -on the server, and used as a new prefix. It may be empty -in which case a 'server' rule will always match on all names from -the server. - -e.g.: - - ``:prefix:client:trusted.:user.virtiofs.:`` - - will match 'trusted.' attributes in client calls and prefix them before - passing them to the server. - - ``:prefix:server::user.virtiofs.:`` - - will strip 'user.virtiofs.' from all server replies. - - ``:prefix:all:trusted.:user.virtiofs.:`` - - combines the previous two cases into a single rule. - - ``:ok:client:user.::`` - - will allow get/set xattr for 'user.' xattr's and ignore - following rules. - - ``:ok:server::security.:`` - - will pass 'securty.' xattr's in listxattr from the server - and ignore following rules. - - ``:ok:all:::`` - - will terminate the rule search passing any remaining attributes - in both directions. - - ``:bad:server::security.:`` - - would hide 'security.' xattr's in listxattr from the server. - -A simpler 'map' type provides a shorter syntax for the common case: - -``:map:key:prepend:`` - -The 'map' type adds a number of separate rules to add **prepend** as a prefix -to the matched **key** (or all attributes if **key** is empty). -There may be at most one 'map' rule and it must be the last rule in the set. - -Note: When the 'security.capability' xattr is remapped, the daemon has to do -extra work to remove it during many operations, which the host kernel normally -does itself. - -Security considerations -~~~~~~~~~~~~~~~~~~~~~~~ - -Operating systems typically partition the xattr namespace using -well defined name prefixes. Each partition may have different -access controls applied. For example, on Linux there are multiple -partitions - - * ``system.*`` - access varies depending on attribute & filesystem - * ``security.*`` - only processes with CAP_SYS_ADMIN - * ``trusted.*`` - only processes with CAP_SYS_ADMIN - * ``user.*`` - any process granted by file permissions / ownership - -While other OS such as FreeBSD have different name prefixes -and access control rules. - -When remapping attributes on the host, it is important to -ensure that the remapping does not allow a guest user to -evade the guest access control rules. - -Consider if ``trusted.*`` from the guest was remapped to -``user.virtiofs.trusted*`` in the host. An unprivileged -user in a Linux guest has the ability to write to xattrs -under ``user.*``. Thus the user can evade the access -control restriction on ``trusted.*`` by instead writing -to ``user.virtiofs.trusted.*``. - -As noted above, the partitions used and access controls -applied, will vary across guest OS, so it is not wise to -try to predict what the guest OS will use. - -The simplest way to avoid an insecure configuration is -to remap all xattrs at once, to a given fixed prefix. -This is shown in example (1) below. - -If selectively mapping only a subset of xattr prefixes, -then rules must be added to explicitly block direct -access to the target of the remapping. This is shown -in example (2) below. - -Mapping examples -~~~~~~~~~~~~~~~~ - -1) Prefix all attributes with 'user.virtiofs.' - -:: - - -o xattrmap=":prefix:all::user.virtiofs.::bad:all:::" - - -This uses two rules, using : as the field separator; -the first rule prefixes and strips 'user.virtiofs.', -the second rule hides any non-prefixed attributes that -the host set. - -This is equivalent to the 'map' rule: - -:: - - -o xattrmap=":map::user.virtiofs.:" - -2) Prefix 'trusted.' attributes, allow others through - -:: - - "/prefix/all/trusted./user.virtiofs./ - /bad/server//trusted./ - /bad/client/user.virtiofs.// - /ok/all///" - - -Here there are four rules, using / as the field -separator, and also demonstrating that new lines can -be included between rules. -The first rule is the prefixing of 'trusted.' and -stripping of 'user.virtiofs.'. -The second rule hides unprefixed 'trusted.' attributes -on the host. -The third rule stops a guest from explicitly setting -the 'user.virtiofs.' path directly to prevent access -control bypass on the target of the earlier prefix -remapping. -Finally, the fourth rule lets all remaining attributes -through. - -This is equivalent to the 'map' rule: - -:: - - -o xattrmap="/map/trusted./user.virtiofs./" - -3) Hide 'security.' attributes, and allow everything else - -:: - - "/bad/all/security./security./ - /ok/all///' - -The first rule combines what could be separate client and server -rules into a single 'all' rule, matching 'security.' in either -client arguments or lists returned from the host. This stops -the client seeing any 'security.' attributes on the server and -stops it setting any. - -SELinux support ---------------- -One can enable support for SELinux by running virtiofsd with option -"-o security_label". But this will try to save guest's security context -in xattr security.selinux on host and it might fail if host's SELinux -policy does not permit virtiofsd to do this operation. - -Hence, it is preferred to remap guest's "security.selinux" xattr to say -"trusted.virtiofs.security.selinux" on host. - -"-o xattrmap=:map:security.selinux:trusted.virtiofs.:" - -This will make sure that guest and host's SELinux xattrs on same file -remain separate and not interfere with each other. And will allow both -host and guest to implement their own separate SELinux policies. - -Setting trusted xattr on host requires CAP_SYS_ADMIN. So one will need -add this capability to daemon. - -"-o modcaps=+sys_admin" - -Giving CAP_SYS_ADMIN increases the risk on system. Now virtiofsd is more -powerful and if gets compromised, it can do lot of damage to host system. -So keep this trade-off in my mind while making a decision. - -Examples --------- - -Export ``/var/lib/fs/vm001/`` on vhost-user UNIX domain socket -``/var/run/vm001-vhost-fs.sock``: - -.. parsed-literal:: - - host# virtiofsd --socket-path=/var/run/vm001-vhost-fs.sock -o source=/var/lib/fs/vm001 - host# |qemu_system| \\ - -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/u2f.txt b/docs/u2f.txt deleted file mode 100644 index 7f5813a0b7..0000000000 --- a/docs/u2f.txt +++ /dev/null @@ -1,110 +0,0 @@ -QEMU U2F Key Device Documentation. - -Contents -1. USB U2F key device -2. Building -3. Using u2f-emulated -4. Using u2f-passthru -5. Libu2f-emu - -1. USB U2F key device - -U2F is an open authentication standard that enables relying parties -exposed to the internet to offer a strong second factor option for end -user authentication. - -The standard brings many advantages to both parties, client and server, -allowing to reduce over-reliance on passwords, it increases authentication -security and simplifies passwords. - -The second factor is materialized by a device implementing the U2F -protocol. In case of a USB U2F security key, it is a USB HID device -that implements the U2F protocol. - -In QEMU, the USB U2F key device offers a dedicated support of U2F, allowing -guest USB FIDO/U2F security keys operating in two possible modes: -pass-through and emulated. - -The pass-through mode consists of passing all requests made from the guest -to the physical security key connected to the host machine and vice versa. -In addition, the dedicated pass-through allows to have a U2F security key -shared on several guests which is not possible with a simple host device -assignment pass-through. - -The emulated mode consists of completely emulating the behavior of an -U2F device through software part. Libu2f-emu is used for that. - - -2. Building - -To ensure the build of the u2f-emulated device variant which depends -on libu2f-emu: configuring and building: - - ./configure --enable-u2f && make - -The pass-through mode is built by default on Linux. To take advantage -of the autoscan option it provides, make sure you have a working libudev -installed on the host. - - -3. Using u2f-emulated - -To work, an emulated U2F device must have four elements: - * ec x509 certificate - * ec private key - * counter (four bytes value) - * 48 bytes of entropy (random bits) - -To use this type of device, this one has to be configured, and these -four elements must be passed one way or another. - -Assuming that you have a working libu2f-emu installed on the host. -There are three possible ways of configurations: - * ephemeral - * setup directory - * manual - -Ephemeral is the simplest way to configure, it lets the device generate -all the elements it needs for a single use of the lifetime of the device. - - qemu -usb -device u2f-emulated - -Setup directory allows to configure the device from a directory containing -four files: - * certificate.pem: ec x509 certificate - * private-key.pem: ec private key - * counter: counter value - * entropy: 48 bytes of entropy - - qemu -usb -device u2f-emulated,dir=$dir - -Manual allows to configure the device more finely by specifying each -of the elements necessary for the device: - * cert - * priv - * counter - * entropy - - qemu -usb -device u2f-emulated,cert=$DIR1/$FILE1,priv=$DIR2/$FILE2,counter=$DIR3/$FILE3,entropy=$DIR4/$FILE4 - - -4. Using u2f-passthru - -On the host specify the u2f-passthru device with a suitable hidraw: - - qemu -usb -device u2f-passthru,hidraw=/dev/hidraw0 - -Alternately, the u2f-passthru device can autoscan to take the first -U2F device it finds on the host (this requires a working libudev): - - qemu -usb -device u2f-passthru - - -5. Libu2f-emu - -The u2f-emulated device uses libu2f-emu for the U2F key emulation. Libu2f-emu -implements completely the U2F protocol device part for all specified -transport given by the FIDO Alliance. - -For more information about libu2f-emu see this page: -https://github.com/MattGorko/libu2f-emu. diff --git a/docs/user/index.rst b/docs/user/index.rst index 2c4e29f3db..782d27cda2 100644 --- a/docs/user/index.rst +++ b/docs/user/index.rst @@ -1,3 +1,5 @@ +.. _User Mode Emulation: + ------------------- User Mode Emulation ------------------- diff --git a/docs/user/main.rst b/docs/user/main.rst index 6f2ffa080f..d5fbb78d3c 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -87,14 +87,13 @@ Debug options: 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. +``-one-insn-per-tb`` + Run the emulation with one guest instruction per translation block. + This slows down emulation a lot, but can be useful in some situations, + such as when trying to analyse the logs produced by the ``-d`` option. Environment variables: @@ -242,5 +241,7 @@ Debug options: ``-p pagesize`` Act as if the host page size was 'pagesize' bytes -``-singlestep`` - Run the emulation in single step mode. +``-one-insn-per-tb`` + Run the emulation with one guest instruction per translation block. + This slows down emulation a lot, but can be useful in some situations, + such as when trying to analyse the logs produced by the ``-d`` option. diff --git a/dtc b/dtc deleted file mode 160000 index b6910bec11..0000000000 --- a/dtc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b6910bec11614980a21e46fbccc35934b671bd81 diff --git a/dump/dump-hmp-cmds.c b/dump/dump-hmp-cmds.c index e5053b04cd..d9340427c3 100644 --- a/dump/dump-hmp-cmds.c +++ b/dump/dump-hmp-cmds.c @@ -1,5 +1,5 @@ /* - * Human Monitor Interface commands + * Windows crashdump (Human Monitor Interface commands) * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -19,6 +19,7 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) bool paging = qdict_get_try_bool(qdict, "paging", false); bool zlib = qdict_get_try_bool(qdict, "zlib", false); bool lzo = qdict_get_try_bool(qdict, "lzo", false); + bool raw = qdict_get_try_bool(qdict, "raw", false); bool snappy = qdict_get_try_bool(qdict, "snappy", false); const char *file = qdict_get_str(qdict, "filename"); bool has_begin = qdict_haskey(qdict, "begin"); @@ -41,15 +42,27 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) } if (zlib) { - dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB; + if (raw) { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_ZLIB; + } else { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB; + } } if (lzo) { - dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO; + if (raw) { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_LZO; + } else { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO; + } } if (snappy) { - dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY; + if (raw) { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_SNAPPY; + } else { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY; + } } if (has_begin) { diff --git a/dump/dump.c b/dump/dump.c index 4d9658ffa2..84064d890d 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -14,11 +14,10 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "elf.h" -#include "exec/hwaddr.h" +#include "qemu/bswap.h" +#include "exec/target_page.h" #include "monitor/monitor.h" -#include "sysemu/kvm.h" #include "sysemu/dump.h" -#include "sysemu/memory_mapping.h" #include "sysemu/runstate.h" #include "sysemu/cpus.h" #include "qapi/error.h" @@ -29,10 +28,8 @@ #include "qemu/main-loop.h" #include "hw/misc/vmcoreinfo.h" #include "migration/blocker.h" - -#ifdef TARGET_X86_64 +#include "hw/core/cpu.h" #include "win_dump.h" -#endif #include #ifdef CONFIG_LZO @@ -59,6 +56,11 @@ static inline bool dump_is_64bit(DumpState *s) return s->dump_info.d_class == ELFCLASS64; } +static inline bool dump_has_filter(DumpState *s) +{ + return s->filter_area_length > 0; +} + uint16_t cpu_to_dump16(DumpState *s, uint16_t val) { if (s->dump_info.d_endian == ELFDATA2LSB) { @@ -94,21 +96,26 @@ uint64_t cpu_to_dump64(DumpState *s, uint64_t val) static int dump_cleanup(DumpState *s) { + if (s->dump_info.arch_cleanup_fn) { + s->dump_info.arch_cleanup_fn(s); + } + guest_phys_blocks_free(&s->guest_phys_blocks); memory_mapping_list_free(&s->list); close(s->fd); g_free(s->guest_note); + g_clear_pointer(&s->string_table_buf, g_array_unref); s->guest_note = NULL; if (s->resume) { if (s->detached) { - qemu_mutex_lock_iothread(); + bql_lock(); } vm_start(); if (s->detached) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } } - migrate_del_blocker(dump_migration_blocker); + migrate_del_blocker(&dump_migration_blocker); return 0; } @@ -126,7 +133,7 @@ static int fd_write_vmcore(const void *buf, size_t size, void *opaque) return 0; } -static void write_elf64_header(DumpState *s, Error **errp) +static void prepare_elf64_header(DumpState *s, Elf64_Ehdr *elf_header) { /* * phnum in the elf header is 16 bit, if we have more segments we @@ -134,34 +141,26 @@ static void write_elf64_header(DumpState *s, Error **errp) * special section. */ uint16_t phnum = MIN(s->phdr_num, PN_XNUM); - Elf64_Ehdr elf_header; - int ret; - memset(&elf_header, 0, sizeof(Elf64_Ehdr)); - memcpy(&elf_header, ELFMAG, SELFMAG); - elf_header.e_ident[EI_CLASS] = ELFCLASS64; - elf_header.e_ident[EI_DATA] = s->dump_info.d_endian; - elf_header.e_ident[EI_VERSION] = EV_CURRENT; - elf_header.e_type = cpu_to_dump16(s, ET_CORE); - elf_header.e_machine = cpu_to_dump16(s, s->dump_info.d_machine); - elf_header.e_version = cpu_to_dump32(s, EV_CURRENT); - elf_header.e_ehsize = cpu_to_dump16(s, sizeof(elf_header)); - elf_header.e_phoff = cpu_to_dump64(s, s->phdr_offset); - elf_header.e_phentsize = cpu_to_dump16(s, sizeof(Elf64_Phdr)); - elf_header.e_phnum = cpu_to_dump16(s, phnum); - if (s->shdr_num) { - elf_header.e_shoff = cpu_to_dump64(s, s->shdr_offset); - elf_header.e_shentsize = cpu_to_dump16(s, sizeof(Elf64_Shdr)); - elf_header.e_shnum = cpu_to_dump16(s, s->shdr_num); - } - - ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s); - if (ret < 0) { - error_setg_errno(errp, -ret, "dump: failed to write elf header"); - } + memset(elf_header, 0, sizeof(Elf64_Ehdr)); + memcpy(elf_header, ELFMAG, SELFMAG); + elf_header->e_ident[EI_CLASS] = ELFCLASS64; + elf_header->e_ident[EI_DATA] = s->dump_info.d_endian; + elf_header->e_ident[EI_VERSION] = EV_CURRENT; + elf_header->e_type = cpu_to_dump16(s, ET_CORE); + elf_header->e_machine = cpu_to_dump16(s, s->dump_info.d_machine); + elf_header->e_version = cpu_to_dump32(s, EV_CURRENT); + elf_header->e_ehsize = cpu_to_dump16(s, sizeof(elf_header)); + elf_header->e_phoff = cpu_to_dump64(s, s->phdr_offset); + elf_header->e_phentsize = cpu_to_dump16(s, sizeof(Elf64_Phdr)); + elf_header->e_phnum = cpu_to_dump16(s, phnum); + elf_header->e_shoff = cpu_to_dump64(s, s->shdr_offset); + elf_header->e_shentsize = cpu_to_dump16(s, sizeof(Elf64_Shdr)); + elf_header->e_shnum = cpu_to_dump16(s, s->shdr_num); + elf_header->e_shstrndx = cpu_to_dump16(s, s->shdr_num - 1); } -static void write_elf32_header(DumpState *s, Error **errp) +static void prepare_elf32_header(DumpState *s, Elf32_Ehdr *elf_header) { /* * phnum in the elf header is 16 bit, if we have more segments we @@ -169,28 +168,46 @@ static void write_elf32_header(DumpState *s, Error **errp) * special section. */ uint16_t phnum = MIN(s->phdr_num, PN_XNUM); - Elf32_Ehdr elf_header; + + memset(elf_header, 0, sizeof(Elf32_Ehdr)); + memcpy(elf_header, ELFMAG, SELFMAG); + elf_header->e_ident[EI_CLASS] = ELFCLASS32; + elf_header->e_ident[EI_DATA] = s->dump_info.d_endian; + elf_header->e_ident[EI_VERSION] = EV_CURRENT; + elf_header->e_type = cpu_to_dump16(s, ET_CORE); + elf_header->e_machine = cpu_to_dump16(s, s->dump_info.d_machine); + elf_header->e_version = cpu_to_dump32(s, EV_CURRENT); + elf_header->e_ehsize = cpu_to_dump16(s, sizeof(elf_header)); + elf_header->e_phoff = cpu_to_dump32(s, s->phdr_offset); + elf_header->e_phentsize = cpu_to_dump16(s, sizeof(Elf32_Phdr)); + elf_header->e_phnum = cpu_to_dump16(s, phnum); + elf_header->e_shoff = cpu_to_dump32(s, s->shdr_offset); + elf_header->e_shentsize = cpu_to_dump16(s, sizeof(Elf32_Shdr)); + elf_header->e_shnum = cpu_to_dump16(s, s->shdr_num); + elf_header->e_shstrndx = cpu_to_dump16(s, s->shdr_num - 1); +} + +static void write_elf_header(DumpState *s, Error **errp) +{ + Elf32_Ehdr elf32_header; + Elf64_Ehdr elf64_header; + size_t header_size; + void *header_ptr; int ret; - memset(&elf_header, 0, sizeof(Elf32_Ehdr)); - memcpy(&elf_header, ELFMAG, SELFMAG); - elf_header.e_ident[EI_CLASS] = ELFCLASS32; - elf_header.e_ident[EI_DATA] = s->dump_info.d_endian; - elf_header.e_ident[EI_VERSION] = EV_CURRENT; - elf_header.e_type = cpu_to_dump16(s, ET_CORE); - elf_header.e_machine = cpu_to_dump16(s, s->dump_info.d_machine); - elf_header.e_version = cpu_to_dump32(s, EV_CURRENT); - elf_header.e_ehsize = cpu_to_dump16(s, sizeof(elf_header)); - elf_header.e_phoff = cpu_to_dump32(s, s->phdr_offset); - elf_header.e_phentsize = cpu_to_dump16(s, sizeof(Elf32_Phdr)); - elf_header.e_phnum = cpu_to_dump16(s, phnum); - if (s->shdr_num) { - elf_header.e_shoff = cpu_to_dump32(s, s->shdr_offset); - elf_header.e_shentsize = cpu_to_dump16(s, sizeof(Elf32_Shdr)); - elf_header.e_shnum = cpu_to_dump16(s, s->shdr_num); + /* The NULL header and the shstrtab are always defined */ + assert(s->shdr_num >= 2); + if (dump_is_64bit(s)) { + prepare_elf64_header(s, &elf64_header); + header_size = sizeof(elf64_header); + header_ptr = &elf64_header; + } else { + prepare_elf32_header(s, &elf32_header); + header_size = sizeof(elf32_header); + header_ptr = &elf32_header; } - ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s); + ret = fd_write_vmcore(header_ptr, header_size, s); if (ret < 0) { error_setg_errno(errp, -ret, "dump: failed to write elf header"); } @@ -245,7 +262,7 @@ static void write_elf32_load(DumpState *s, MemoryMapping *memory_mapping, } } -static void write_elf64_phdr_note(DumpState *s, Elf64_Phdr *phdr) +static void prepare_elf64_phdr_note(DumpState *s, Elf64_Phdr *phdr) { memset(phdr, 0, sizeof(*phdr)); phdr->p_type = cpu_to_dump32(s, PT_NOTE); @@ -301,7 +318,7 @@ static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s, write_guest_note(f, s, errp); } -static void write_elf32_phdr_note(DumpState *s, Elf32_Phdr *phdr) +static void prepare_elf32_phdr_note(DumpState *s, Elf32_Phdr *phdr) { memset(phdr, 0, sizeof(*phdr)); phdr->p_type = cpu_to_dump32(s, PT_NOTE); @@ -341,7 +358,6 @@ static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s, static void write_elf_phdr_note(DumpState *s, Error **errp) { - ERRP_GUARD(); Elf32_Phdr phdr32; Elf64_Phdr phdr64; void *phdr; @@ -349,11 +365,11 @@ static void write_elf_phdr_note(DumpState *s, Error **errp) int ret; if (dump_is_64bit(s)) { - write_elf64_phdr_note(s, &phdr64); + prepare_elf64_phdr_note(s, &phdr64); size = sizeof(phdr64); phdr = &phdr64; } else { - write_elf32_phdr_note(s, &phdr32); + prepare_elf32_phdr_note(s, &phdr32); size = sizeof(phdr32); phdr = &phdr32; } @@ -365,30 +381,136 @@ static void write_elf_phdr_note(DumpState *s, Error **errp) } } -static void write_elf_section(DumpState *s, int type, Error **errp) +static void prepare_elf_section_hdr_zero(DumpState *s) { - Elf32_Shdr shdr32; - Elf64_Shdr shdr64; + if (dump_is_64bit(s)) { + Elf64_Shdr *shdr64 = s->elf_section_hdrs; + + shdr64->sh_info = cpu_to_dump32(s, s->phdr_num); + } else { + Elf32_Shdr *shdr32 = s->elf_section_hdrs; + + shdr32->sh_info = cpu_to_dump32(s, s->phdr_num); + } +} + +static void prepare_elf_section_hdr_string(DumpState *s, void *buff) +{ + uint64_t index = s->string_table_buf->len; + const char strtab[] = ".shstrtab"; + Elf32_Shdr shdr32 = {}; + Elf64_Shdr shdr64 = {}; int shdr_size; void *shdr; - int ret; - if (type == 0) { - shdr_size = sizeof(Elf32_Shdr); - memset(&shdr32, 0, shdr_size); - shdr32.sh_info = cpu_to_dump32(s, s->phdr_num); - shdr = &shdr32; - } else { + g_array_append_vals(s->string_table_buf, strtab, sizeof(strtab)); + if (dump_is_64bit(s)) { shdr_size = sizeof(Elf64_Shdr); - memset(&shdr64, 0, shdr_size); - shdr64.sh_info = cpu_to_dump32(s, s->phdr_num); + shdr64.sh_type = SHT_STRTAB; + shdr64.sh_offset = s->section_offset + s->elf_section_data_size; + shdr64.sh_name = index; + shdr64.sh_size = s->string_table_buf->len; shdr = &shdr64; + } else { + shdr_size = sizeof(Elf32_Shdr); + shdr32.sh_type = SHT_STRTAB; + shdr32.sh_offset = s->section_offset + s->elf_section_data_size; + shdr32.sh_name = index; + shdr32.sh_size = s->string_table_buf->len; + shdr = &shdr32; + } + memcpy(buff, shdr, shdr_size); +} + +static bool prepare_elf_section_hdrs(DumpState *s, Error **errp) +{ + size_t len, sizeof_shdr; + void *buff_hdr; + + /* + * Section ordering: + * - HDR zero + * - Arch section hdrs + * - String table hdr + */ + sizeof_shdr = dump_is_64bit(s) ? sizeof(Elf64_Shdr) : sizeof(Elf32_Shdr); + len = sizeof_shdr * s->shdr_num; + s->elf_section_hdrs = g_malloc0(len); + buff_hdr = s->elf_section_hdrs; + + /* + * The first section header is ALWAYS a special initial section + * header. + * + * The header should be 0 with one exception being that if + * phdr_num is PN_XNUM then the sh_info field contains the real + * number of segment entries. + * + * As we zero allocate the buffer we will only need to modify + * sh_info for the PN_XNUM case. + */ + if (s->phdr_num >= PN_XNUM) { + prepare_elf_section_hdr_zero(s); + } + buff_hdr += sizeof_shdr; + + /* Add architecture defined section headers */ + if (s->dump_info.arch_sections_write_hdr_fn + && s->shdr_num > 2) { + buff_hdr += s->dump_info.arch_sections_write_hdr_fn(s, buff_hdr); + + if (s->shdr_num >= SHN_LORESERVE) { + error_setg_errno(errp, EINVAL, + "dump: too many architecture defined sections"); + return false; + } } - ret = fd_write_vmcore(shdr, shdr_size, s); + /* + * String table is the last section since strings are added via + * arch_sections_write_hdr(). + */ + prepare_elf_section_hdr_string(s, buff_hdr); + return true; +} + +static void write_elf_section_headers(DumpState *s, Error **errp) +{ + size_t sizeof_shdr = dump_is_64bit(s) ? sizeof(Elf64_Shdr) : sizeof(Elf32_Shdr); + int ret; + + if (!prepare_elf_section_hdrs(s, errp)) { + return; + } + + ret = fd_write_vmcore(s->elf_section_hdrs, s->shdr_num * sizeof_shdr, s); if (ret < 0) { - error_setg_errno(errp, -ret, - "dump: failed to write section header table"); + error_setg_errno(errp, -ret, "dump: failed to write section headers"); + } + + g_free(s->elf_section_hdrs); +} + +static void write_elf_sections(DumpState *s, Error **errp) +{ + int ret; + + if (s->elf_section_data_size) { + /* Write architecture section data */ + ret = fd_write_vmcore(s->elf_section_data, + s->elf_section_data_size, s); + if (ret < 0) { + error_setg_errno(errp, -ret, + "dump: failed to write architecture section data"); + return; + } + } + + /* Write string table */ + ret = fd_write_vmcore(s->string_table_buf->data, + s->string_table_buf->len, s); + if (ret < 0) { + error_setg_errno(errp, -ret, "dump: failed to write string table data"); } } @@ -443,29 +565,30 @@ static void get_offset_range(hwaddr phys_addr, *p_offset = -1; *p_filesz = 0; - if (s->has_filter) { - if (phys_addr < s->begin || phys_addr >= s->begin + s->length) { + if (dump_has_filter(s)) { + if (phys_addr < s->filter_area_begin || + phys_addr >= s->filter_area_begin + s->filter_area_length) { return; } } QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) { - if (s->has_filter) { - if (block->target_start >= s->begin + s->length || - block->target_end <= s->begin) { + if (dump_has_filter(s)) { + if (block->target_start >= s->filter_area_begin + s->filter_area_length || + block->target_end <= s->filter_area_begin) { /* This block is out of the range */ continue; } - if (s->begin <= block->target_start) { + if (s->filter_area_begin <= block->target_start) { start = block->target_start; } else { - start = s->begin; + start = s->filter_area_begin; } size_in_block = block->target_end - start; - if (s->begin + s->length < block->target_end) { - size_in_block -= block->target_end - (s->begin + s->length); + if (s->filter_area_begin + s->filter_area_length < block->target_end) { + size_in_block -= block->target_end - (s->filter_area_begin + s->filter_area_length); } } else { start = block->target_start; @@ -490,7 +613,7 @@ static void get_offset_range(hwaddr phys_addr, } } -static void write_elf_loads(DumpState *s, Error **errp) +static void write_elf_phdr_loads(DumpState *s, Error **errp) { ERRP_GUARD(); hwaddr offset, filesz; @@ -538,6 +661,8 @@ static void dump_begin(DumpState *s, Error **errp) * -------------- * | elf header | * -------------- + * | sctn_hdr | + * -------------- * | PT_NOTE | * -------------- * | PT_LOAD | @@ -546,8 +671,6 @@ static void dump_begin(DumpState *s, Error **errp) * -------------- * | PT_LOAD | * -------------- - * | sec_hdr | - * -------------- * | elf note | * -------------- * | memory | @@ -558,11 +681,13 @@ static void dump_begin(DumpState *s, Error **errp) */ /* write elf header to vmcore */ - if (dump_is_64bit(s)) { - write_elf64_header(s, errp); - } else { - write_elf32_header(s, errp); + write_elf_header(s, errp); + if (*errp) { + return; } + + /* write section headers to vmcore */ + write_elf_section_headers(s, errp); if (*errp) { return; } @@ -573,49 +698,53 @@ static void dump_begin(DumpState *s, Error **errp) return; } - /* write all PT_LOAD to vmcore */ - write_elf_loads(s, errp); + /* write all PT_LOADs to vmcore */ + write_elf_phdr_loads(s, errp); if (*errp) { return; } - /* write section to vmcore */ - if (s->shdr_num) { - write_elf_section(s, 1, errp); - if (*errp) { - return; - } - } - /* write notes to vmcore */ write_elf_notes(s, errp); } -static int get_next_block(DumpState *s, GuestPhysBlock *block) +int64_t dump_filtered_memblock_size(GuestPhysBlock *block, + int64_t filter_area_start, + int64_t filter_area_length) { - while (1) { - block = QTAILQ_NEXT(block, next); - if (!block) { - /* no more block */ - return 1; - } + int64_t size, left, right; - s->start = 0; - s->next_block = block; - if (s->has_filter) { - if (block->target_start >= s->begin + s->length || - block->target_end <= s->begin) { - /* This block is out of the range */ - continue; - } - - if (s->begin > block->target_start) { - s->start = s->begin - block->target_start; - } - } - - return 0; + /* No filter, return full size */ + if (!filter_area_length) { + return block->target_end - block->target_start; } + + /* calculate the overlapped region. */ + left = MAX(filter_area_start, block->target_start); + right = MIN(filter_area_start + filter_area_length, block->target_end); + size = right - left; + size = size > 0 ? size : 0; + + return size; +} + +int64_t dump_filtered_memblock_start(GuestPhysBlock *block, + int64_t filter_area_start, + int64_t filter_area_length) +{ + if (filter_area_length) { + /* return -1 if the block is not within filter area */ + if (block->target_start >= filter_area_start + filter_area_length || + block->target_end <= filter_area_start) { + return -1; + } + + if (filter_area_start > block->target_start) { + return filter_area_start - block->target_start; + } + } + + return 0; } /* write all memory to vmcore */ @@ -623,24 +752,46 @@ static void dump_iterate(DumpState *s, Error **errp) { ERRP_GUARD(); GuestPhysBlock *block; - int64_t size; + int64_t memblock_size, memblock_start; - do { - block = s->next_block; - - size = block->target_end - block->target_start; - if (s->has_filter) { - size -= s->start; - if (s->begin + s->length < block->target_end) { - size -= block->target_end - (s->begin + s->length); - } + QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) { + memblock_start = dump_filtered_memblock_start(block, s->filter_area_begin, s->filter_area_length); + if (memblock_start == -1) { + continue; } - write_memory(s, block, s->start, size, errp); + + memblock_size = dump_filtered_memblock_size(block, s->filter_area_begin, s->filter_area_length); + + /* Write the memory to file */ + write_memory(s, block, memblock_start, memblock_size, errp); if (*errp) { return; } + } +} - } while (!get_next_block(s, block)); +static void dump_end(DumpState *s, Error **errp) +{ + int rc; + + if (s->elf_section_data_size) { + s->elf_section_data = g_malloc0(s->elf_section_data_size); + } + + /* Adds the architecture defined section data to s->elf_section_data */ + if (s->dump_info.arch_sections_write_fn && + s->elf_section_data_size) { + rc = s->dump_info.arch_sections_write_fn(s, s->elf_section_data); + if (rc) { + error_setg_errno(errp, rc, + "dump: failed to get arch section data"); + g_free(s->elf_section_data); + return; + } + } + + /* write sections to vmcore */ + write_elf_sections(s, errp); } static void create_vmcore(DumpState *s, Error **errp) @@ -652,14 +803,25 @@ static void create_vmcore(DumpState *s, Error **errp) return; } + /* Iterate over memory and dump it to file */ dump_iterate(s, errp); + if (*errp) { + return; + } + + /* Write the section data */ + dump_end(s, errp); } -static int write_start_flat_header(int fd) +static int write_start_flat_header(DumpState *s) { MakedumpfileHeader *mh; int ret = 0; + if (s->kdump_raw) { + return 0; + } + QEMU_BUILD_BUG_ON(sizeof *mh > MAX_SIZE_MDF_HEADER); mh = g_malloc0(MAX_SIZE_MDF_HEADER); @@ -670,7 +832,7 @@ static int write_start_flat_header(int fd) mh->version = cpu_to_be64(VERSION_FLAT_HEADER); size_t written_size; - written_size = qemu_write_full(fd, mh, MAX_SIZE_MDF_HEADER); + written_size = qemu_write_full(s->fd, mh, MAX_SIZE_MDF_HEADER); if (written_size != MAX_SIZE_MDF_HEADER) { ret = -1; } @@ -679,15 +841,19 @@ static int write_start_flat_header(int fd) return ret; } -static int write_end_flat_header(int fd) +static int write_end_flat_header(DumpState *s) { MakedumpfileDataHeader mdh; + if (s->kdump_raw) { + return 0; + } + mdh.offset = END_FLAG_FLAT_HEADER; mdh.buf_size = END_FLAG_FLAT_HEADER; size_t written_size; - written_size = qemu_write_full(fd, &mdh, sizeof(mdh)); + written_size = qemu_write_full(s->fd, &mdh, sizeof(mdh)); if (written_size != sizeof(mdh)) { return -1; } @@ -695,20 +861,28 @@ static int write_end_flat_header(int fd) return 0; } -static int write_buffer(int fd, off_t offset, const void *buf, size_t size) +static int write_buffer(DumpState *s, off_t offset, const void *buf, size_t size) { size_t written_size; MakedumpfileDataHeader mdh; + off_t seek_loc; - mdh.offset = cpu_to_be64(offset); - mdh.buf_size = cpu_to_be64(size); + if (s->kdump_raw) { + seek_loc = lseek(s->fd, offset, SEEK_SET); + if (seek_loc == (off_t) -1) { + return -1; + } + } else { + mdh.offset = cpu_to_be64(offset); + mdh.buf_size = cpu_to_be64(size); - written_size = qemu_write_full(fd, &mdh, sizeof(mdh)); - if (written_size != sizeof(mdh)) { - return -1; + written_size = qemu_write_full(s->fd, &mdh, sizeof(mdh)); + if (written_size != sizeof(mdh)) { + return -1; + } } - written_size = qemu_write_full(fd, buf, size); + written_size = qemu_write_full(s->fd, buf, size); if (written_size != size) { return -1; } @@ -750,13 +924,13 @@ static void get_note_sizes(DumpState *s, const void *note, if (dump_is_64bit(s)) { const Elf64_Nhdr *hdr = note; note_head_sz = sizeof(Elf64_Nhdr); - name_sz = tswap64(hdr->n_namesz); - desc_sz = tswap64(hdr->n_descsz); + name_sz = cpu_to_dump64(s, hdr->n_namesz); + desc_sz = cpu_to_dump64(s, hdr->n_descsz); } else { const Elf32_Nhdr *hdr = note; note_head_sz = sizeof(Elf32_Nhdr); - name_sz = tswap32(hdr->n_namesz); - desc_sz = tswap32(hdr->n_descsz); + name_sz = cpu_to_dump32(s, hdr->n_namesz); + desc_sz = cpu_to_dump32(s, hdr->n_descsz); } if (note_head_size) { @@ -828,7 +1002,7 @@ static void create_header32(DumpState *s, Error **errp) #endif dh->status = cpu_to_dump32(s, status); - if (write_buffer(s->fd, 0, dh, size) < 0) { + if (write_buffer(s, 0, dh, size) < 0) { error_setg(errp, "dump: failed to write disk dump header"); goto out; } @@ -858,7 +1032,7 @@ static void create_header32(DumpState *s, Error **errp) kh->offset_note = cpu_to_dump64(s, offset_note); kh->note_size = cpu_to_dump32(s, s->note_size); - if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS * + if (write_buffer(s, DISKDUMP_HEADER_BLOCKS * block_size, kh, size) < 0) { error_setg(errp, "dump: failed to write kdump sub header"); goto out; @@ -873,7 +1047,7 @@ static void create_header32(DumpState *s, Error **errp) if (*errp) { goto out; } - if (write_buffer(s->fd, offset_note, s->note_buf, + if (write_buffer(s, offset_note, s->note_buf, s->note_size) < 0) { error_setg(errp, "dump: failed to write notes"); goto out; @@ -939,7 +1113,7 @@ static void create_header64(DumpState *s, Error **errp) #endif dh->status = cpu_to_dump32(s, status); - if (write_buffer(s->fd, 0, dh, size) < 0) { + if (write_buffer(s, 0, dh, size) < 0) { error_setg(errp, "dump: failed to write disk dump header"); goto out; } @@ -969,7 +1143,7 @@ static void create_header64(DumpState *s, Error **errp) kh->offset_note = cpu_to_dump64(s, offset_note); kh->note_size = cpu_to_dump64(s, s->note_size); - if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS * + if (write_buffer(s, DISKDUMP_HEADER_BLOCKS * block_size, kh, size) < 0) { error_setg(errp, "dump: failed to write kdump sub header"); goto out; @@ -985,7 +1159,7 @@ static void create_header64(DumpState *s, Error **errp) goto out; } - if (write_buffer(s->fd, offset_note, s->note_buf, + if (write_buffer(s, offset_note, s->note_buf, s->note_size) < 0) { error_setg(errp, "dump: failed to write notes"); goto out; @@ -1050,7 +1224,7 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value, while (old_offset < new_offset) { /* calculate the offset and write dump_bitmap */ offset_bitmap1 = s->offset_dump_bitmap + old_offset; - if (write_buffer(s->fd, offset_bitmap1, buf, + if (write_buffer(s, offset_bitmap1, buf, bitmap_bufsize) < 0) { return -1; } @@ -1058,7 +1232,7 @@ static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value, /* dump level 1 is chosen, so 1st and 2nd bitmap are same */ offset_bitmap2 = s->offset_dump_bitmap + s->len_dump_bitmap + old_offset; - if (write_buffer(s->fd, offset_bitmap2, buf, + if (write_buffer(s, offset_bitmap2, buf, bitmap_bufsize) < 0) { return -1; } @@ -1094,55 +1268,81 @@ static uint64_t dump_pfn_to_paddr(DumpState *s, uint64_t pfn) } /* - * exam every page and return the page frame number and the address of the page. - * bufptr can be NULL. note: the blocks here is supposed to reflect guest-phys - * blocks, so block->target_start and block->target_end should be interal - * multiples of the target page size. + * Return the page frame number and the page content in *bufptr. bufptr can be + * NULL. If not NULL, *bufptr must contains a target page size of pre-allocated + * memory. This is not necessarily the memory returned. */ static bool get_next_page(GuestPhysBlock **blockptr, uint64_t *pfnptr, uint8_t **bufptr, DumpState *s) { GuestPhysBlock *block = *blockptr; - hwaddr addr, target_page_mask = ~((hwaddr)s->dump_info.page_size - 1); - uint8_t *buf; + uint32_t page_size = s->dump_info.page_size; + uint8_t *buf = NULL, *hbuf; + hwaddr addr; /* block == NULL means the start of the iteration */ if (!block) { block = QTAILQ_FIRST(&s->guest_phys_blocks.head); *blockptr = block; - assert((block->target_start & ~target_page_mask) == 0); - assert((block->target_end & ~target_page_mask) == 0); - *pfnptr = dump_paddr_to_pfn(s, block->target_start); - if (bufptr) { - *bufptr = block->host_addr; - } - return true; - } - - *pfnptr = *pfnptr + 1; - addr = dump_pfn_to_paddr(s, *pfnptr); - - if ((addr >= block->target_start) && - (addr + s->dump_info.page_size <= block->target_end)) { - buf = block->host_addr + (addr - block->target_start); + addr = block->target_start; + *pfnptr = dump_paddr_to_pfn(s, addr); } else { - /* the next page is in the next block */ - block = QTAILQ_NEXT(block, next); - *blockptr = block; - if (!block) { - return false; + *pfnptr += 1; + addr = dump_pfn_to_paddr(s, *pfnptr); + } + assert(block != NULL); + + while (1) { + if (addr >= block->target_start && addr < block->target_end) { + size_t n = MIN(block->target_end - addr, page_size - addr % page_size); + hbuf = block->host_addr + (addr - block->target_start); + if (!buf) { + if (n == page_size) { + /* this is a whole target page, go for it */ + assert(addr % page_size == 0); + buf = hbuf; + break; + } else if (bufptr) { + assert(*bufptr); + buf = *bufptr; + memset(buf, 0, page_size); + } else { + return true; + } + } + + memcpy(buf + addr % page_size, hbuf, n); + addr += n; + if (addr % page_size == 0 || addr >= block->target_end) { + /* we filled up the page or the current block is finished */ + break; + } + } else { + /* the next page is in the next block */ + *blockptr = block = QTAILQ_NEXT(block, next); + if (!block) { + break; + } + + addr = block->target_start; + /* are we still in the same page? */ + if (dump_paddr_to_pfn(s, addr) != *pfnptr) { + if (buf) { + /* no, but we already filled something earlier, return it */ + break; + } else { + /* else continue from there */ + *pfnptr = dump_paddr_to_pfn(s, addr); + } + } } - assert((block->target_start & ~target_page_mask) == 0); - assert((block->target_end & ~target_page_mask) == 0); - *pfnptr = dump_paddr_to_pfn(s, block->target_start); - buf = block->host_addr; } if (bufptr) { *bufptr = buf; } - return true; + return buf != NULL; } static void write_dump_bitmap(DumpState *s, Error **errp) @@ -1200,7 +1400,7 @@ out: static void prepare_data_cache(DataCache *data_cache, DumpState *s, off_t offset) { - data_cache->fd = s->fd; + data_cache->state = s; data_cache->data_size = 0; data_cache->buf_size = 4 * dump_bitmap_get_bufsize(s); data_cache->buf = g_malloc0(data_cache->buf_size); @@ -1219,11 +1419,11 @@ static int write_cache(DataCache *dc, const void *buf, size_t size, /* * if flag_sync is set, synchronize data in dc->buf into vmcore. * otherwise check if the space is enough for caching data in buf, if not, - * write the data in dc->buf to dc->fd and reset dc->buf + * write the data in dc->buf to dc->state->fd and reset dc->buf */ if ((!flag_sync && dc->data_size + size > dc->buf_size) || (flag_sync && dc->data_size > 0)) { - if (write_buffer(dc->fd, dc->offset, dc->buf, dc->data_size) < 0) { + if (write_buffer(dc->state, dc->offset, dc->buf, dc->data_size) < 0) { return -1; } @@ -1280,6 +1480,7 @@ static void write_dump_pages(DumpState *s, Error **errp) uint8_t *buf; GuestPhysBlock *block_iter = NULL; uint64_t pfn_iter; + g_autofree uint8_t *page = NULL; /* get offset of page_desc and page_data in dump file */ offset_desc = s->offset_page; @@ -1315,12 +1516,13 @@ static void write_dump_pages(DumpState *s, Error **errp) } offset_data += s->dump_info.page_size; + page = g_malloc(s->dump_info.page_size); /* * dump memory to vmcore page by page. zero page will all be resided in the * first page of page section */ - while (get_next_page(&block_iter, &pfn_iter, &buf, s)) { + for (buf = page; get_next_page(&block_iter, &pfn_iter, &buf, s); buf = page) { /* check zero page */ if (buffer_is_zero(buf, s->dump_info.page_size)) { ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor), @@ -1462,7 +1664,7 @@ static void create_kdump_vmcore(DumpState *s, Error **errp) * +------------------------------------------+ */ - ret = write_start_flat_header(s->fd); + ret = write_start_flat_header(s); if (ret < 0) { error_setg(errp, "dump: failed to write start flat header"); return; @@ -1483,41 +1685,13 @@ static void create_kdump_vmcore(DumpState *s, Error **errp) return; } - ret = write_end_flat_header(s->fd); + ret = write_end_flat_header(s); if (ret < 0) { error_setg(errp, "dump: failed to write end flat header"); return; } } -static ram_addr_t get_start_block(DumpState *s) -{ - GuestPhysBlock *block; - - if (!s->has_filter) { - s->next_block = QTAILQ_FIRST(&s->guest_phys_blocks.head); - return 0; - } - - QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) { - if (block->target_start >= s->begin + s->length || - block->target_end <= s->begin) { - /* This block is out of the range */ - continue; - } - - s->next_block = block; - if (s->begin > block->target_start) { - s->start = s->begin - block->target_start; - } else { - s->start = 0; - } - return s->start; - } - - return -1; -} - static void get_max_mapnr(DumpState *s) { GuestPhysBlock *last_block; @@ -1540,25 +1714,19 @@ bool qemu_system_dump_in_progress(void) return (qatomic_read(&state->status) == DUMP_STATUS_ACTIVE); } -/* calculate total size of memory to be dumped (taking filter into - * acoount.) */ +/* + * calculate total size of memory to be dumped (taking filter into + * account.) + */ static int64_t dump_calculate_size(DumpState *s) { GuestPhysBlock *block; - int64_t size = 0, total = 0, left = 0, right = 0; + int64_t total = 0; QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) { - if (s->has_filter) { - /* calculate the overlapped region. */ - left = MAX(s->begin, block->target_start); - right = MIN(s->begin + s->length, block->target_end); - size = right - left; - size = size > 0 ? size : 0; - } else { - /* count the whole region in */ - size = (block->target_end - block->target_start); - } - total += size; + total += dump_filtered_memblock_size(block, + s->filter_area_begin, + s->filter_area_length); } return total; @@ -1607,7 +1775,8 @@ static void vmcoreinfo_update_phys_base(DumpState *s) static void dump_init(DumpState *s, int fd, bool has_format, DumpGuestMemoryFormat format, bool paging, bool has_filter, - int64_t begin, int64_t length, Error **errp) + int64_t begin, int64_t length, bool kdump_raw, + Error **errp) { ERRP_GUARD(); VMCoreInfoState *vmci = vmcoreinfo_find(); @@ -1618,6 +1787,7 @@ static void dump_init(DumpState *s, int fd, bool has_format, s->has_format = has_format; s->format = format; s->written_size = 0; + s->kdump_raw = kdump_raw; /* kdump-compressed is conflict with paging and filter */ if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) { @@ -1641,9 +1811,20 @@ static void dump_init(DumpState *s, int fd, bool has_format, } s->fd = fd; - s->has_filter = has_filter; - s->begin = begin; - s->length = length; + if (has_filter && !length) { + error_setg(errp, "parameter 'length' expects a non-zero size"); + goto cleanup; + } + s->filter_area_begin = begin; + s->filter_area_length = length; + + /* First index is 0, it's the special null name */ + s->string_table_buf = g_array_new(FALSE, TRUE, 1); + /* + * Allocate the null name, due to the clearing option set to true + * it will be 0. + */ + g_array_set_size(s->string_table_buf, 1); memory_mapping_list_init(&s->list); @@ -1660,32 +1841,24 @@ static void dump_init(DumpState *s, int fd, bool has_format, goto cleanup; } - s->start = get_start_block(s); - if (s->start == -1) { - error_setg(errp, QERR_INVALID_PARAMETER, "begin"); - goto cleanup; - } - /* get dump info: endian, class and architecture. * If the target architecture is not supported, cpu_get_dump_info() will * return -1. */ ret = cpu_get_dump_info(&s->dump_info, &s->guest_phys_blocks); if (ret < 0) { - error_setg(errp, QERR_UNSUPPORTED); + error_setg(errp, + "dumping guest memory is not supported on this target"); goto cleanup; } if (!s->dump_info.page_size) { - s->dump_info.page_size = TARGET_PAGE_SIZE; + s->dump_info.page_size = qemu_target_page_size(); } s->note_size = cpu_get_note_size(s->dump_info.d_class, s->dump_info.d_machine, nr_cpus); - if (s->note_size < 0) { - error_setg(errp, QERR_UNSUPPORTED); - goto cleanup; - } + assert(s->note_size >= 0); /* * The goal of this block is to (a) update the previously guessed @@ -1695,20 +1868,20 @@ static void dump_init(DumpState *s, int fd, bool has_format, if (vmci) { uint64_t addr, note_head_size, name_size, desc_size; uint32_t size; - uint16_t format; + uint16_t guest_format; note_head_size = dump_is_64bit(s) ? sizeof(Elf64_Nhdr) : sizeof(Elf32_Nhdr); - format = le16_to_cpu(vmci->vmcoreinfo.guest_format); + guest_format = le16_to_cpu(vmci->vmcoreinfo.guest_format); size = le32_to_cpu(vmci->vmcoreinfo.size); addr = le64_to_cpu(vmci->vmcoreinfo.paddr); if (!vmci->has_vmcoreinfo) { warn_report("guest note is not present"); } else if (size < note_head_size || size > MAX_GUEST_NOTE_SIZE) { warn_report("guest note size is invalid: %" PRIu32, size); - } else if (format != FW_CFG_VMCOREINFO_FORMAT_ELF) { - warn_report("guest note format is unsupported: %" PRIu16, format); + } else if (guest_format != FW_CFG_VMCOREINFO_FORMAT_ELF) { + warn_report("guest note format is unsupported: %" PRIu16, guest_format); } else { s->guest_note = g_malloc(size + 1); /* +1 for adding \0 */ cpu_physical_memory_read(addr, s->guest_note, size); @@ -1776,43 +1949,58 @@ static void dump_init(DumpState *s, int fd, bool has_format, return; } - if (s->has_filter) { - memory_mapping_filter(&s->list, s->begin, s->length); + if (dump_has_filter(s)) { + memory_mapping_filter(&s->list, s->filter_area_begin, s->filter_area_length); } /* - * calculate phdr_num - * - * the type of ehdr->e_phnum is uint16_t, so we should avoid overflow + * The first section header is always a special one in which most + * fields are 0. The section header string table is also always + * set. */ - s->phdr_num = 1; /* PT_NOTE */ - if (s->list.num < UINT16_MAX - 2) { - s->shdr_num = 0; + s->shdr_num = 2; + + /* + * Adds the number of architecture sections to shdr_num and sets + * elf_section_data_size so we know the offsets and sizes of all + * parts. + */ + if (s->dump_info.arch_sections_add_fn) { + s->dump_info.arch_sections_add_fn(s); + } + + /* + * calculate shdr_num so we know the offsets and sizes of all + * parts. + * Calculate phdr_num + * + * The absolute maximum amount of phdrs is UINT32_MAX - 1 as + * sh_info is 32 bit. There's special handling once we go over + * UINT16_MAX - 1 but that is handled in the ehdr and section + * code. + */ + s->phdr_num = 1; /* Reserve PT_NOTE */ + if (s->list.num <= UINT32_MAX - 1) { s->phdr_num += s->list.num; } else { - /* sh_info of section 0 holds the real number of phdrs */ - s->shdr_num = 1; - - /* the type of shdr->sh_info is uint32_t, so we should avoid overflow */ - if (s->list.num <= UINT32_MAX - 1) { - s->phdr_num += s->list.num; - } else { - s->phdr_num = UINT32_MAX; - } + s->phdr_num = UINT32_MAX; } + /* + * Now that the number of section and program headers is known we + * can calculate the offsets of the headers and data. + */ if (dump_is_64bit(s)) { - s->phdr_offset = sizeof(Elf64_Ehdr); - s->shdr_offset = s->phdr_offset + sizeof(Elf64_Phdr) * s->phdr_num; - s->note_offset = s->shdr_offset + sizeof(Elf64_Shdr) * s->shdr_num; - s->memory_offset = s->note_offset + s->note_size; + s->shdr_offset = sizeof(Elf64_Ehdr); + s->phdr_offset = s->shdr_offset + sizeof(Elf64_Shdr) * s->shdr_num; + s->note_offset = s->phdr_offset + sizeof(Elf64_Phdr) * s->phdr_num; } else { - - s->phdr_offset = sizeof(Elf32_Ehdr); - s->shdr_offset = s->phdr_offset + sizeof(Elf32_Phdr) * s->phdr_num; - s->note_offset = s->shdr_offset + sizeof(Elf32_Shdr) * s->shdr_num; - s->memory_offset = s->note_offset + s->note_size; + s->shdr_offset = sizeof(Elf32_Ehdr); + s->phdr_offset = s->shdr_offset + sizeof(Elf32_Shdr) * s->shdr_num; + s->note_offset = s->phdr_offset + sizeof(Elf32_Phdr) * s->phdr_num; } + s->memory_offset = s->note_offset + s->note_size; + s->section_offset = s->memory_offset + s->total_size; return; @@ -1827,9 +2015,7 @@ static void dump_process(DumpState *s, Error **errp) DumpQueryResult *result = NULL; if (s->has_format && s->format == DUMP_GUEST_MEMORY_FORMAT_WIN_DMP) { -#ifdef TARGET_X86_64 create_win_dump(s, errp); -#endif } else if (s->has_format && s->format != DUMP_GUEST_MEMORY_FORMAT_ELF) { create_kdump_vmcore(s, errp); } else { @@ -1845,8 +2031,8 @@ static void dump_process(DumpState *s, Error **errp) result = qmp_query_dump(NULL); /* should never fail */ assert(result); - qapi_event_send_dump_completed(result, !!*errp, (*errp ? - error_get_pretty(*errp) : NULL)); + qapi_event_send_dump_completed(result, + *errp ? error_get_pretty(*errp) : NULL); qapi_free_DumpQueryResult(result); dump_cleanup(s); @@ -1871,17 +2057,19 @@ DumpQueryResult *qmp_query_dump(Error **errp) return result; } -void qmp_dump_guest_memory(bool paging, const char *file, +void qmp_dump_guest_memory(bool paging, const char *protocol, bool has_detach, bool detach, - bool has_begin, int64_t begin, bool has_length, - int64_t length, bool has_format, - DumpGuestMemoryFormat format, Error **errp) + bool has_begin, int64_t begin, + bool has_length, int64_t length, + bool has_format, DumpGuestMemoryFormat format, + Error **errp) { ERRP_GUARD(); const char *p; - int fd = -1; + int fd; DumpState *s; bool detach_p = false; + bool kdump_raw = false; if (runstate_check(RUN_STATE_INMIGRATE)) { error_setg(errp, "Dump not allowed during incoming migration."); @@ -1895,6 +2083,29 @@ void qmp_dump_guest_memory(bool paging, const char *file, return; } + /* + * externally, we represent kdump-raw-* as separate formats, but internally + * they are handled the same, except for the "raw" flag + */ + if (has_format) { + switch (format) { + case DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_ZLIB: + format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB; + kdump_raw = true; + break; + case DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_LZO: + format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO; + kdump_raw = true; + break; + case DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_SNAPPY: + format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY; + kdump_raw = true; + break; + default: + break; + } + } + /* * kdump-compressed format need the whole memory dumped, so paging or * filter is not supported here. @@ -1932,32 +2143,29 @@ void qmp_dump_guest_memory(bool paging, const char *file, } #endif -#ifndef TARGET_X86_64 - if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_WIN_DMP) { - error_setg(errp, "Windows dump is only available for x86-64"); + if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_WIN_DMP + && !win_dump_available(errp)) { return; } -#endif -#if !defined(WIN32) - if (strstart(file, "fd:", &p)) { + if (strstart(protocol, "fd:", &p)) { fd = monitor_get_fd(monitor_cur(), p, errp); if (fd == -1) { return; } - } -#endif - - if (strstart(file, "file:", &p)) { - fd = qemu_open_old(p, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR); + } else if (strstart(protocol, "file:", &p)) { + fd = qemu_create(p, O_WRONLY | O_TRUNC | O_BINARY, S_IRUSR, errp); if (fd < 0) { - error_setg_file_open(errp, errno, p); return; } + } else { + error_setg(errp, + "parameter 'protocol' must start with 'file:' or 'fd:'"); + return; } - - if (fd == -1) { - error_setg(errp, QERR_INVALID_PARAMETER, "protocol"); + if (kdump_raw && lseek(fd, 0, SEEK_CUR) == (off_t) -1) { + close(fd); + error_setg(errp, "kdump-raw formats require a seekable file"); return; } @@ -1970,7 +2178,7 @@ void qmp_dump_guest_memory(bool paging, const char *file, * Allows even for -only-migratable, but forbid migration during the * process of dump guest memory. */ - if (migrate_add_blocker_internal(dump_migration_blocker, errp)) { + if (migrate_add_blocker_internal(&dump_migration_blocker, errp)) { /* Remember to release the fd before passing it over to dump state */ close(fd); return; @@ -1980,7 +2188,7 @@ void qmp_dump_guest_memory(bool paging, const char *file, dump_state_prepare(s); dump_init(s, fd, has_format, format, paging, has_begin, - begin, length, errp); + begin, length, kdump_raw, errp); if (*errp) { qatomic_set(&s->status, DUMP_STATUS_FAILED); return; @@ -2008,21 +2216,23 @@ DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp) /* kdump-zlib is always available */ QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB); + QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_ZLIB); /* add new item if kdump-lzo is available */ #ifdef CONFIG_LZO QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO); + QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_LZO); #endif /* add new item if kdump-snappy is available */ #ifdef CONFIG_SNAPPY QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY); + QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_SNAPPY); #endif - /* Windows dump is available only if target is x86_64 */ -#ifdef TARGET_X86_64 - QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_WIN_DMP); -#endif + if (win_dump_available(NULL)) { + QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_WIN_DMP); + } return cap; } diff --git a/dump/meson.build b/dump/meson.build index 2eff29c3ea..4277ce9328 100644 --- a/dump/meson.build +++ b/dump/meson.build @@ -1,4 +1,2 @@ -softmmu_ss.add(files('dump-hmp-cmds.c')) - -specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files('dump.c'), snappy, lzo]) -specific_ss.add(when: ['CONFIG_SOFTMMU', 'TARGET_X86_64'], if_true: files('win_dump.c')) +system_ss.add([files('dump.c', 'dump-hmp-cmds.c'), snappy, lzo]) +specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: files('win_dump.c')) diff --git a/dump/win_dump.c b/dump/win_dump.c index fd91350fbb..b7bfaff379 100644 --- a/dump/win_dump.c +++ b/dump/win_dump.c @@ -1,5 +1,5 @@ /* - * Windows crashdump + * Windows crashdump (target specific implementations) * * Copyright (c) 2018 Virtuozzo International GmbH * @@ -9,19 +9,22 @@ */ #include "qemu/osdep.h" -#include "qemu/cutils.h" -#include "elf.h" -#include "exec/hwaddr.h" -#include "monitor/monitor.h" -#include "sysemu/kvm.h" #include "sysemu/dump.h" -#include "sysemu/memory_mapping.h" -#include "sysemu/cpus.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qemu/error-report.h" -#include "hw/misc/vmcoreinfo.h" +#include "qapi/qmp/qerror.h" +#include "exec/cpu-defs.h" +#include "hw/core/cpu.h" +#include "qemu/win_dump_defs.h" #include "win_dump.h" +#include "cpu.h" + +#if defined(TARGET_X86_64) + +bool win_dump_available(Error **errp) +{ + return true; +} static size_t win_dump_ptr_size(bool x64) { @@ -273,6 +276,13 @@ static void patch_and_save_context(WinDumpHeader *h, bool x64, uint64_t Context; WinContext ctx; + if (i >= WIN_DUMP_FIELD(NumberProcessors)) { + warn_report("win-dump: number of QEMU CPUs is bigger than" + " NumberProcessors (%u) in guest Windows", + WIN_DUMP_FIELD(NumberProcessors)); + return; + } + if (cpu_read_ptr(x64, first_cpu, KiProcessorBlock + i * win_dump_ptr_size(x64), &Prcb)) { @@ -468,3 +478,19 @@ out_cr3: return; } + +#else /* !TARGET_X86_64 */ + +bool win_dump_available(Error **errp) +{ + error_setg(errp, "Windows dump is only available for x86-64"); + + return false; +} + +void create_win_dump(DumpState *s, Error **errp) +{ + win_dump_available(errp); +} + +#endif diff --git a/dump/win_dump.h b/dump/win_dump.h index b8c25348f4..c9b49f87dc 100644 --- a/dump/win_dump.h +++ b/dump/win_dump.h @@ -11,7 +11,10 @@ #ifndef WIN_DUMP_H #define WIN_DUMP_H -#include "qemu/win_dump_defs.h" +#include "sysemu/dump.h" + +/* Check Windows dump availability for the current target */ +bool win_dump_available(Error **errp); void create_win_dump(DumpState *s, Error **errp); diff --git a/ebpf/ebpf.c b/ebpf/ebpf.c new file mode 100644 index 0000000000..2d73beb479 --- /dev/null +++ b/ebpf/ebpf.c @@ -0,0 +1,69 @@ +/* + * QEMU eBPF binary declaration routine. + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Andrew Melnychenko + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/queue.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-ebpf.h" +#include "ebpf/ebpf.h" + +typedef struct ElfBinaryDataEntry { + int id; + const void *data; + size_t datalen; + + QSLIST_ENTRY(ElfBinaryDataEntry) node; +} ElfBinaryDataEntry; + +static QSLIST_HEAD(, ElfBinaryDataEntry) ebpf_elf_obj_list = + QSLIST_HEAD_INITIALIZER(); + +void ebpf_register_binary_data(int id, const void *data, size_t datalen) +{ + struct ElfBinaryDataEntry *dataentry = NULL; + + dataentry = g_new0(struct ElfBinaryDataEntry, 1); + dataentry->data = data; + dataentry->datalen = datalen; + dataentry->id = id; + + QSLIST_INSERT_HEAD(&ebpf_elf_obj_list, dataentry, node); +} + +const void *ebpf_find_binary_by_id(int id, size_t *sz, Error **errp) +{ + struct ElfBinaryDataEntry *it = NULL; + QSLIST_FOREACH(it, &ebpf_elf_obj_list, node) { + if (id == it->id) { + *sz = it->datalen; + return it->data; + } + } + + error_setg(errp, "can't find eBPF object with id: %d", id); + + return NULL; +} + +EbpfObject *qmp_request_ebpf(EbpfProgramID id, Error **errp) +{ + EbpfObject *ret = NULL; + size_t size = 0; + const void *data = ebpf_find_binary_by_id(id, &size, errp); + if (!data) { + return NULL; + } + + ret = g_new0(EbpfObject, 1); + ret->object = g_base64_encode(data, size); + + return ret; +} diff --git a/ebpf/ebpf.h b/ebpf/ebpf.h new file mode 100644 index 0000000000..378d4e9c70 --- /dev/null +++ b/ebpf/ebpf.h @@ -0,0 +1,29 @@ +/* + * QEMU eBPF binary declaration routine. + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Andrew Melnychenko + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef EBPF_H +#define EBPF_H + + +void ebpf_register_binary_data(int id, const void *data, + size_t datalen); +const void *ebpf_find_binary_by_id(int id, size_t *sz, + struct Error **errp); + +#define ebpf_binary_init(id, fn) \ +static void __attribute__((constructor)) ebpf_binary_init_ ## fn(void) \ +{ \ + size_t datalen = 0; \ + const void *data = fn(&datalen); \ + ebpf_register_binary_data(id, data, datalen); \ +} + +#endif /* EBPF_H */ diff --git a/ebpf/ebpf_rss-stub.c b/ebpf/ebpf_rss-stub.c index e71e229190..8d7fae2ad9 100644 --- a/ebpf/ebpf_rss-stub.c +++ b/ebpf/ebpf_rss-stub.c @@ -28,6 +28,12 @@ bool ebpf_rss_load(struct EBPFRSSContext *ctx) return false; } +bool ebpf_rss_load_fds(struct EBPFRSSContext *ctx, int program_fd, + int config_fd, int toeplitz_fd, int table_fd) +{ + return false; +} + bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config, uint16_t *indirections_table, uint8_t *toeplitz_key) { diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c index 118c68da83..d102f3dd09 100644 --- a/ebpf/ebpf_rss.c +++ b/ebpf/ebpf_rss.c @@ -13,6 +13,8 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "qapi/qapi-types-misc.h" +#include "qapi/qapi-commands-ebpf.h" #include #include @@ -21,38 +23,97 @@ #include "ebpf/ebpf_rss.h" #include "ebpf/rss.bpf.skeleton.h" -#include "trace.h" +#include "ebpf/ebpf.h" void ebpf_rss_init(struct EBPFRSSContext *ctx) { if (ctx != NULL) { ctx->obj = NULL; + ctx->program_fd = -1; + ctx->map_configuration = -1; + ctx->map_toeplitz_key = -1; + ctx->map_indirections_table = -1; + + ctx->mmap_configuration = NULL; + ctx->mmap_toeplitz_key = NULL; + ctx->mmap_indirections_table = NULL; } } bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx) { - return ctx != NULL && ctx->obj != NULL; + return ctx != NULL && (ctx->obj != NULL || ctx->program_fd != -1); +} + +static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx) +{ + if (!ebpf_rss_is_loaded(ctx)) { + return false; + } + + ctx->mmap_configuration = mmap(NULL, qemu_real_host_page_size(), + PROT_READ | PROT_WRITE, MAP_SHARED, + ctx->map_configuration, 0); + if (ctx->mmap_configuration == MAP_FAILED) { + return false; + } + ctx->mmap_toeplitz_key = mmap(NULL, qemu_real_host_page_size(), + PROT_READ | PROT_WRITE, MAP_SHARED, + ctx->map_toeplitz_key, 0); + if (ctx->mmap_toeplitz_key == MAP_FAILED) { + goto toeplitz_fail; + } + ctx->mmap_indirections_table = mmap(NULL, qemu_real_host_page_size(), + PROT_READ | PROT_WRITE, MAP_SHARED, + ctx->map_indirections_table, 0); + if (ctx->mmap_indirections_table == MAP_FAILED) { + goto indirection_fail; + } + + return true; + +indirection_fail: + munmap(ctx->mmap_toeplitz_key, qemu_real_host_page_size()); + ctx->mmap_toeplitz_key = NULL; +toeplitz_fail: + munmap(ctx->mmap_configuration, qemu_real_host_page_size()); + ctx->mmap_configuration = NULL; + + ctx->mmap_indirections_table = NULL; + return false; +} + +static void ebpf_rss_munmap(struct EBPFRSSContext *ctx) +{ + if (!ebpf_rss_is_loaded(ctx)) { + return; + } + + munmap(ctx->mmap_indirections_table, qemu_real_host_page_size()); + munmap(ctx->mmap_toeplitz_key, qemu_real_host_page_size()); + munmap(ctx->mmap_configuration, qemu_real_host_page_size()); + + ctx->mmap_configuration = NULL; + ctx->mmap_toeplitz_key = NULL; + ctx->mmap_indirections_table = NULL; } bool ebpf_rss_load(struct EBPFRSSContext *ctx) { struct rss_bpf *rss_bpf_ctx; - if (ctx == NULL) { + if (ebpf_rss_is_loaded(ctx)) { return false; } rss_bpf_ctx = rss_bpf__open(); if (rss_bpf_ctx == NULL) { - trace_ebpf_error("eBPF RSS", "can not open eBPF RSS object"); goto error; } - bpf_program__set_socket_filter(rss_bpf_ctx->progs.tun_rss_steering_prog); + bpf_program__set_type(rss_bpf_ctx->progs.tun_rss_steering_prog, BPF_PROG_TYPE_SOCKET_FILTER); if (rss_bpf__load(rss_bpf_ctx)) { - trace_ebpf_error("eBPF RSS", "can not load RSS program"); goto error; } @@ -66,26 +127,57 @@ bool ebpf_rss_load(struct EBPFRSSContext *ctx) ctx->map_toeplitz_key = bpf_map__fd( rss_bpf_ctx->maps.tap_rss_map_toeplitz_key); + if (!ebpf_rss_mmap(ctx)) { + goto error; + } + return true; error: rss_bpf__destroy(rss_bpf_ctx); ctx->obj = NULL; + ctx->program_fd = -1; + ctx->map_configuration = -1; + ctx->map_toeplitz_key = -1; + ctx->map_indirections_table = -1; return false; } +bool ebpf_rss_load_fds(struct EBPFRSSContext *ctx, int program_fd, + int config_fd, int toeplitz_fd, int table_fd) +{ + if (ebpf_rss_is_loaded(ctx)) { + return false; + } + + if (program_fd < 0 || config_fd < 0 || toeplitz_fd < 0 || table_fd < 0) { + return false; + } + + ctx->program_fd = program_fd; + ctx->map_configuration = config_fd; + ctx->map_toeplitz_key = toeplitz_fd; + ctx->map_indirections_table = table_fd; + + if (!ebpf_rss_mmap(ctx)) { + ctx->program_fd = -1; + ctx->map_configuration = -1; + ctx->map_toeplitz_key = -1; + ctx->map_indirections_table = -1; + return false; + } + + return true; +} + static bool ebpf_rss_set_config(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config) { - uint32_t map_key = 0; - if (!ebpf_rss_is_loaded(ctx)) { return false; } - if (bpf_map_update_elem(ctx->map_configuration, - &map_key, config, 0) < 0) { - return false; - } + + memcpy(ctx->mmap_configuration, config, sizeof(*config)); return true; } @@ -93,27 +185,24 @@ static bool ebpf_rss_set_indirections_table(struct EBPFRSSContext *ctx, uint16_t *indirections_table, size_t len) { - uint32_t i = 0; + char *cursor = ctx->mmap_indirections_table; if (!ebpf_rss_is_loaded(ctx) || indirections_table == NULL || len > VIRTIO_NET_RSS_MAX_TABLE_LEN) { return false; } - for (; i < len; ++i) { - if (bpf_map_update_elem(ctx->map_indirections_table, &i, - indirections_table + i, 0) < 0) { - return false; - } + for (size_t i = 0; i < len; i++) { + *(uint16_t *)cursor = indirections_table[i]; + cursor += 8; } + return true; } static bool ebpf_rss_set_toepliz_key(struct EBPFRSSContext *ctx, uint8_t *toeplitz_key) { - uint32_t map_key = 0; - /* prepare toeplitz key */ uint8_t toe[VIRTIO_NET_RSS_MAX_KEY_SIZE] = {}; @@ -123,10 +212,7 @@ static bool ebpf_rss_set_toepliz_key(struct EBPFRSSContext *ctx, memcpy(toe, toeplitz_key, VIRTIO_NET_RSS_MAX_KEY_SIZE); *(uint32_t *)toe = ntohl(*(uint32_t *)toe); - if (bpf_map_update_elem(ctx->map_toeplitz_key, &map_key, toe, - 0) < 0) { - return false; - } + memcpy(ctx->mmap_toeplitz_key, toe, VIRTIO_NET_RSS_MAX_KEY_SIZE); return true; } @@ -160,6 +246,22 @@ void ebpf_rss_unload(struct EBPFRSSContext *ctx) return; } - rss_bpf__destroy(ctx->obj); + ebpf_rss_munmap(ctx); + + if (ctx->obj) { + rss_bpf__destroy(ctx->obj); + } else { + close(ctx->program_fd); + close(ctx->map_configuration); + close(ctx->map_toeplitz_key); + close(ctx->map_indirections_table); + } + ctx->obj = NULL; + ctx->program_fd = -1; + ctx->map_configuration = -1; + ctx->map_toeplitz_key = -1; + ctx->map_indirections_table = -1; } + +ebpf_binary_init(EBPF_PROGRAMID_RSS, rss_bpf__elf_bytes) diff --git a/ebpf/ebpf_rss.h b/ebpf/ebpf_rss.h index bf3f2572c7..239242b0d2 100644 --- a/ebpf/ebpf_rss.h +++ b/ebpf/ebpf_rss.h @@ -14,12 +14,19 @@ #ifndef QEMU_EBPF_RSS_H #define QEMU_EBPF_RSS_H +#define EBPF_RSS_MAX_FDS 4 + struct EBPFRSSContext { void *obj; int program_fd; int map_configuration; int map_toeplitz_key; int map_indirections_table; + + /* mapped eBPF maps for direct access to omit bpf_map_update_elem() */ + void *mmap_configuration; + void *mmap_toeplitz_key; + void *mmap_indirections_table; }; struct EBPFRSSConfig { @@ -36,6 +43,9 @@ bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx); bool ebpf_rss_load(struct EBPFRSSContext *ctx); +bool ebpf_rss_load_fds(struct EBPFRSSContext *ctx, int program_fd, + int config_fd, int toeplitz_fd, int table_fd); + bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config, uint16_t *indirections_table, uint8_t *toeplitz_key); diff --git a/ebpf/meson.build b/ebpf/meson.build index 2dd0fd8948..c5bf9295a2 100644 --- a/ebpf/meson.build +++ b/ebpf/meson.build @@ -1 +1 @@ -softmmu_ss.add(when: libbpf, if_true: files('ebpf_rss.c'), if_false: files('ebpf_rss-stub.c')) +common_ss.add(when: libbpf, if_true: files('ebpf.c', 'ebpf_rss.c'), if_false: files('ebpf_rss-stub.c')) diff --git a/ebpf/rss.bpf.skeleton.h b/ebpf/rss.bpf.skeleton.h index 126683eb87..aed4ef9a03 100644 --- a/ebpf/rss.bpf.skeleton.h +++ b/ebpf/rss.bpf.skeleton.h @@ -1,9 +1,10 @@ /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ -/* THIS FILE IS AUTOGENERATED! */ +/* THIS FILE IS AUTOGENERATED BY BPFTOOL! */ #ifndef __RSS_BPF_SKEL_H__ #define __RSS_BPF_SKEL_H__ +#include #include #include @@ -12,8 +13,8 @@ struct rss_bpf { struct bpf_object *obj; struct { struct bpf_map *tap_rss_map_configurations; - struct bpf_map *tap_rss_map_indirection_table; struct bpf_map *tap_rss_map_toeplitz_key; + struct bpf_map *tap_rss_map_indirection_table; } maps; struct { struct bpf_program *tun_rss_steering_prog; @@ -21,6 +22,16 @@ struct rss_bpf { struct { struct bpf_link *tun_rss_steering_prog; } links; + +#ifdef __cplusplus + static inline struct rss_bpf *open(const struct bpf_object_open_opts *opts = nullptr); + static inline struct rss_bpf *open_and_load(); + static inline int load(struct rss_bpf *skel); + static inline int attach(struct rss_bpf *skel); + static inline void detach(struct rss_bpf *skel); + static inline void destroy(struct rss_bpf *skel); + static inline const void *elf_bytes(size_t *sz); +#endif /* __cplusplus */ }; static void @@ -40,18 +51,26 @@ static inline struct rss_bpf * rss_bpf__open_opts(const struct bpf_object_open_opts *opts) { struct rss_bpf *obj; + int err; obj = (struct rss_bpf *)calloc(1, sizeof(*obj)); - if (!obj) + if (!obj) { + errno = ENOMEM; return NULL; - if (rss_bpf__create_skeleton(obj)) - goto err; - if (bpf_object__open_skeleton(obj->skeleton, opts)) - goto err; + } + + err = rss_bpf__create_skeleton(obj); + if (err) + goto err_out; + + err = bpf_object__open_skeleton(obj->skeleton, opts); + if (err) + goto err_out; return obj; -err: +err_out: rss_bpf__destroy(obj); + errno = -err; return NULL; } @@ -71,12 +90,15 @@ static inline struct rss_bpf * rss_bpf__open_and_load(void) { struct rss_bpf *obj; + int err; obj = rss_bpf__open(); if (!obj) return NULL; - if (rss_bpf__load(obj)) { + err = rss_bpf__load(obj); + if (err) { rss_bpf__destroy(obj); + errno = -err; return NULL; } return obj; @@ -91,18 +113,22 @@ rss_bpf__attach(struct rss_bpf *obj) static inline void rss_bpf__detach(struct rss_bpf *obj) { - return bpf_object__detach_skeleton(obj->skeleton); + bpf_object__detach_skeleton(obj->skeleton); } +static inline const void *rss_bpf__elf_bytes(size_t *sz); + static inline int rss_bpf__create_skeleton(struct rss_bpf *obj) { struct bpf_object_skeleton *s; + int err; s = (struct bpf_object_skeleton *)calloc(1, sizeof(*s)); - if (!s) - return -1; - obj->skeleton = s; + if (!s) { + err = -ENOMEM; + goto err; + } s->sz = sizeof(*s); s->name = "rss_bpf"; @@ -112,320 +138,862 @@ rss_bpf__create_skeleton(struct rss_bpf *obj) s->map_cnt = 3; s->map_skel_sz = sizeof(*s->maps); s->maps = (struct bpf_map_skeleton *)calloc(s->map_cnt, s->map_skel_sz); - if (!s->maps) + if (!s->maps) { + err = -ENOMEM; goto err; + } s->maps[0].name = "tap_rss_map_configurations"; s->maps[0].map = &obj->maps.tap_rss_map_configurations; - s->maps[1].name = "tap_rss_map_indirection_table"; - s->maps[1].map = &obj->maps.tap_rss_map_indirection_table; + s->maps[1].name = "tap_rss_map_toeplitz_key"; + s->maps[1].map = &obj->maps.tap_rss_map_toeplitz_key; - s->maps[2].name = "tap_rss_map_toeplitz_key"; - s->maps[2].map = &obj->maps.tap_rss_map_toeplitz_key; + s->maps[2].name = "tap_rss_map_indirection_table"; + s->maps[2].map = &obj->maps.tap_rss_map_indirection_table; /* programs */ s->prog_cnt = 1; s->prog_skel_sz = sizeof(*s->progs); s->progs = (struct bpf_prog_skeleton *)calloc(s->prog_cnt, s->prog_skel_sz); - if (!s->progs) + if (!s->progs) { + err = -ENOMEM; goto err; + } s->progs[0].name = "tun_rss_steering_prog"; s->progs[0].prog = &obj->progs.tun_rss_steering_prog; s->progs[0].link = &obj->links.tun_rss_steering_prog; - s->data_sz = 8088; - s->data = (void *)"\ + s->data = (void *)rss_bpf__elf_bytes(&s->data_sz); + + obj->skeleton = s; + return 0; +err: + bpf_object__destroy_skeleton(s); + return err; +} + +static inline const void *rss_bpf__elf_bytes(size_t *sz) +{ + *sz = 20600; + return (const void *)"\ \x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\x18\x1d\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0a\0\ -\x01\0\xbf\x18\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\x4c\xff\0\0\0\0\xbf\xa7\ +\0\0\0\0\0\0\0\0\0\0\0\x38\x4d\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0d\0\ +\x01\0\xbf\x19\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\x4c\xff\0\0\0\0\xbf\xa7\ \0\0\0\0\0\0\x07\x07\0\0\x4c\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \xbf\x72\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x06\0\0\0\0\0\0\x18\x01\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\xbf\x72\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\xbf\x07\0\0\0\0\0\0\ -\x18\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\x15\x06\x66\x02\0\0\0\0\xbf\x79\0\0\ -\0\0\0\0\x15\x09\x64\x02\0\0\0\0\x71\x61\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\ -\0\x5d\x02\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xc0\xff\0\0\0\0\x7b\x1a\xb8\xff\ +\x18\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\x15\x06\x61\x02\0\0\0\0\xbf\x78\0\0\ +\0\0\0\0\x15\x08\x5f\x02\0\0\0\0\x71\x61\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\ +\0\x58\x02\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xc0\xff\0\0\0\0\x7b\x1a\xb8\xff\ \0\0\0\0\x7b\x1a\xb0\xff\0\0\0\0\x7b\x1a\xa8\xff\0\0\0\0\x7b\x1a\xa0\xff\0\0\0\ \0\x63\x1a\x98\xff\0\0\0\0\x7b\x1a\x90\xff\0\0\0\0\x7b\x1a\x88\xff\0\0\0\0\x7b\ \x1a\x80\xff\0\0\0\0\x7b\x1a\x78\xff\0\0\0\0\x7b\x1a\x70\xff\0\0\0\0\x7b\x1a\ \x68\xff\0\0\0\0\x7b\x1a\x60\xff\0\0\0\0\x7b\x1a\x58\xff\0\0\0\0\x7b\x1a\x50\ -\xff\0\0\0\0\x15\x08\x4c\x02\0\0\0\0\x6b\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\ -\0\x07\x03\0\0\xd0\xff\xff\xff\xbf\x81\0\0\0\0\0\0\xb7\x02\0\0\x0c\0\0\0\xb7\ +\xff\0\0\0\0\x15\x09\x47\x02\0\0\0\0\x6b\x1a\xc8\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\ +\0\x07\x03\0\0\xc8\xff\xff\xff\xbf\x91\0\0\0\0\0\0\xb7\x02\0\0\x0c\0\0\0\xb7\ \x04\0\0\x02\0\0\0\xb7\x05\0\0\0\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\ -\x77\0\0\0\x20\0\0\0\x55\0\x11\0\0\0\0\0\xb7\x02\0\0\x10\0\0\0\x69\xa1\xd0\xff\ -\0\0\0\0\xbf\x13\0\0\0\0\0\0\xdc\x03\0\0\x10\0\0\0\x15\x03\x02\0\0\x81\0\0\x55\ -\x03\x0c\0\xa8\x88\0\0\xb7\x02\0\0\x14\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\ -\xd0\xff\xff\xff\xbf\x81\0\0\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\0\0\0\0\ -\x85\0\0\0\x44\0\0\0\x69\xa1\xd0\xff\0\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\ -\0\0\0\x15\0\x01\0\0\0\0\0\x05\0\x2f\x02\0\0\0\0\x15\x01\x2e\x02\0\0\0\0\x7b\ -\x9a\x30\xff\0\0\0\0\x15\x01\x57\0\x86\xdd\0\0\x55\x01\x3b\0\x08\0\0\0\x7b\x7a\ -\x20\xff\0\0\0\0\xb7\x07\0\0\x01\0\0\0\x73\x7a\x50\xff\0\0\0\0\xb7\x01\0\0\0\0\ -\0\0\x63\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\ -\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\xff\xff\xbf\x81\0\0\0\0\0\0\xb7\x02\0\ -\0\0\0\0\0\xb7\x04\0\0\x14\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\ -\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x1a\x02\0\0\0\0\x69\xa1\xd6\xff\0\0\ -\0\0\x55\x01\x01\0\0\0\0\0\xb7\x07\0\0\0\0\0\0\x61\xa1\xdc\xff\0\0\0\0\x63\x1a\ -\x5c\xff\0\0\0\0\x61\xa1\xe0\xff\0\0\0\0\x63\x1a\x60\xff\0\0\0\0\x73\x7a\x56\ -\xff\0\0\0\0\x71\xa9\xd9\xff\0\0\0\0\x71\xa1\xd0\xff\0\0\0\0\x67\x01\0\0\x02\0\ -\0\0\x57\x01\0\0\x3c\0\0\0\x7b\x1a\x40\xff\0\0\0\0\x79\xa7\x20\xff\0\0\0\0\xbf\ -\x91\0\0\0\0\0\0\x57\x01\0\0\xff\0\0\0\x15\x01\x19\0\0\0\0\0\x71\xa1\x56\xff\0\ -\0\0\0\x55\x01\x17\0\0\0\0\0\x57\x09\0\0\xff\0\0\0\x15\x09\x7a\x01\x11\0\0\0\ -\x55\x09\x14\0\x06\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x53\xff\0\0\0\0\xb7\x01\ -\0\0\0\0\0\0\x63\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\ -\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\xff\xff\xbf\x81\0\0\0\0\0\0\x79\ -\xa2\x40\xff\0\0\0\0\xb7\x04\0\0\x14\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\ -\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\xf4\x01\0\0\0\0\x69\xa1\ -\xd0\xff\0\0\0\0\x6b\x1a\x58\xff\0\0\0\0\x69\xa1\xd2\xff\0\0\0\0\x6b\x1a\x5a\ -\xff\0\0\0\0\x71\xa1\x50\xff\0\0\0\0\x15\x01\xd4\0\0\0\0\0\x71\x62\x03\0\0\0\0\ -\0\x67\x02\0\0\x08\0\0\0\x71\x61\x02\0\0\0\0\0\x4f\x12\0\0\0\0\0\0\x71\x63\x04\ -\0\0\0\0\0\x71\x61\x05\0\0\0\0\0\x67\x01\0\0\x08\0\0\0\x4f\x31\0\0\0\0\0\0\x67\ -\x01\0\0\x10\0\0\0\x4f\x21\0\0\0\0\0\0\x71\xa2\x53\xff\0\0\0\0\x79\xa0\x30\xff\ -\0\0\0\0\x15\x02\x06\x01\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x02\0\0\0\x15\ -\x02\x03\x01\0\0\0\0\x61\xa1\x5c\xff\0\0\0\0\x63\x1a\xa0\xff\0\0\0\0\x61\xa1\ -\x60\xff\0\0\0\0\x63\x1a\xa4\xff\0\0\0\0\x69\xa1\x58\xff\0\0\0\0\x6b\x1a\xa8\ -\xff\0\0\0\0\x69\xa1\x5a\xff\0\0\0\0\x6b\x1a\xaa\xff\0\0\0\0\x05\0\x65\x01\0\0\ -\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x51\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\ -\xf0\xff\0\0\0\0\x7b\x1a\xe8\xff\0\0\0\0\x7b\x1a\xe0\xff\0\0\0\0\x7b\x1a\xd8\ -\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xd0\xff\ -\xff\xff\xb7\x01\0\0\x28\0\0\0\x7b\x1a\x40\xff\0\0\0\0\xbf\x81\0\0\0\0\0\0\xb7\ -\x02\0\0\0\0\0\0\xb7\x04\0\0\x28\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\ -\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x10\x01\0\0\0\0\x79\xa1\xe0\ -\xff\0\0\0\0\x63\x1a\x64\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x68\xff\0\0\ -\0\0\x79\xa1\xd8\xff\0\0\0\0\x63\x1a\x5c\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\ -\x1a\x60\xff\0\0\0\0\x79\xa1\xe8\xff\0\0\0\0\x63\x1a\x6c\xff\0\0\0\0\x77\x01\0\ -\0\x20\0\0\0\x63\x1a\x70\xff\0\0\0\0\x79\xa1\xf0\xff\0\0\0\0\x63\x1a\x74\xff\0\ -\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x78\xff\0\0\0\0\x71\xa9\xd6\xff\0\0\0\0\ -\x25\x09\xff\0\x3c\0\0\0\xb7\x01\0\0\x01\0\0\0\x6f\x91\0\0\0\0\0\0\x18\x02\0\0\ -\x01\0\0\0\0\0\0\0\0\x18\0\x1c\x5f\x21\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\ -\xf8\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x6b\x1a\xfe\xff\0\0\0\0\xb7\x01\0\0\x28\0\0\ -\0\x7b\x1a\x40\xff\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\x8c\xff\xff\xff\x7b\ -\x1a\x18\xff\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\x7c\xff\xff\xff\x7b\x1a\ -\x10\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\x28\xff\0\0\0\0\x7b\x7a\x20\xff\0\ -\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xfe\xff\xff\xff\xbf\x81\0\0\0\0\0\0\x79\ -\xa2\x40\xff\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\ -\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x15\0\x01\0\0\0\0\0\x05\0\x90\ -\x01\0\0\0\0\xbf\x91\0\0\0\0\0\0\x15\x01\x23\0\x3c\0\0\0\x15\x01\x59\0\x2c\0\0\ -\0\x55\x01\x5a\0\x2b\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xf8\xff\0\0\0\0\xbf\xa3\ -\0\0\0\0\0\0\x07\x03\0\0\xf8\xff\xff\xff\xbf\x81\0\0\0\0\0\0\x79\xa2\x40\xff\0\ -\0\0\0\xb7\x04\0\0\x04\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\xbf\x01\ -\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\x03\x01\0\0\0\ -\0\x71\xa1\xfa\xff\0\0\0\0\x55\x01\x4b\0\x02\0\0\0\x71\xa1\xf9\xff\0\0\0\0\x55\ -\x01\x49\0\x02\0\0\0\x71\xa1\xfb\xff\0\0\0\0\x55\x01\x47\0\x01\0\0\0\x79\xa2\ -\x40\xff\0\0\0\0\x07\x02\0\0\x08\0\0\0\xbf\x81\0\0\0\0\0\0\x79\xa3\x18\xff\0\0\ -\0\0\xb7\x04\0\0\x10\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\xbf\x01\0\ -\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\xf2\0\0\0\0\0\ -\xb7\x01\0\0\x01\0\0\0\x73\x1a\x55\xff\0\0\0\0\x05\0\x39\0\0\0\0\0\xb7\x01\0\0\ -\0\0\0\0\x6b\x1a\xf8\xff\0\0\0\0\xb7\x09\0\0\x02\0\0\0\xb7\x07\0\0\x1e\0\0\0\ -\x05\0\x0e\0\0\0\0\0\x79\xa2\x38\xff\0\0\0\0\x0f\x29\0\0\0\0\0\0\xbf\x92\0\0\0\ -\0\0\0\x07\x02\0\0\x01\0\0\0\x71\xa3\xff\xff\0\0\0\0\x67\x03\0\0\x03\0\0\0\x2d\ -\x23\x02\0\0\0\0\0\x79\xa7\x20\xff\0\0\0\0\x05\0\x2b\0\0\0\0\0\x07\x07\0\0\xff\ -\xff\xff\xff\xbf\x72\0\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\x77\x02\0\0\x20\0\0\0\ -\x15\x02\xf9\xff\0\0\0\0\x7b\x9a\x38\xff\0\0\0\0\x79\xa1\x40\xff\0\0\0\0\x0f\ -\x19\0\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xf8\xff\xff\xff\xbf\x81\0\0\0\ -\0\0\0\xbf\x92\0\0\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\ -\0\x44\0\0\0\xbf\x01\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\ -\x55\x01\x94\0\0\0\0\0\x71\xa2\xf8\xff\0\0\0\0\x55\x02\x0f\0\xc9\0\0\0\x07\x09\ -\0\0\x02\0\0\0\xbf\x81\0\0\0\0\0\0\xbf\x92\0\0\0\0\0\0\x79\xa3\x10\xff\0\0\0\0\ -\xb7\x04\0\0\x10\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\xbf\x01\0\0\0\ -\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\x87\0\0\0\0\0\xb7\ -\x01\0\0\x01\0\0\0\x73\x1a\x54\xff\0\0\0\0\x79\xa7\x20\xff\0\0\0\0\x05\0\x07\0\ -\0\0\0\0\xb7\x09\0\0\x01\0\0\0\x15\x02\xd1\xff\0\0\0\0\x71\xa9\xf9\xff\0\0\0\0\ -\x07\x09\0\0\x02\0\0\0\x05\0\xce\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x56\ -\xff\0\0\0\0\x71\xa1\xff\xff\0\0\0\0\x67\x01\0\0\x03\0\0\0\x79\xa2\x40\xff\0\0\ -\0\0\x0f\x12\0\0\0\0\0\0\x07\x02\0\0\x08\0\0\0\x7b\x2a\x40\xff\0\0\0\0\x71\xa9\ -\xfe\xff\0\0\0\0\x25\x09\x0e\0\x3c\0\0\0\xb7\x01\0\0\x01\0\0\0\x6f\x91\0\0\0\0\ -\0\0\x18\x02\0\0\x01\0\0\0\0\0\0\0\0\x18\0\x1c\x5f\x21\0\0\0\0\0\0\x55\x01\x01\ -\0\0\0\0\0\x05\0\x07\0\0\0\0\0\x79\xa1\x28\xff\0\0\0\0\x07\x01\0\0\x01\0\0\0\ -\x7b\x1a\x28\xff\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\ -\x82\xff\x0b\0\0\0\x05\0\x10\xff\0\0\0\0\x15\x09\xf8\xff\x87\0\0\0\x05\0\xfd\ -\xff\0\0\0\0\x71\xa1\x51\xff\0\0\0\0\x79\xa0\x30\xff\0\0\0\0\x15\x01\x17\x01\0\ -\0\0\0\x71\x62\x03\0\0\0\0\0\x67\x02\0\0\x08\0\0\0\x71\x61\x02\0\0\0\0\0\x4f\ -\x12\0\0\0\0\0\0\x71\x63\x04\0\0\0\0\0\x71\x61\x05\0\0\0\0\0\x67\x01\0\0\x08\0\ -\0\0\x4f\x31\0\0\0\0\0\0\x67\x01\0\0\x10\0\0\0\x4f\x21\0\0\0\0\0\0\x71\xa2\x53\ -\xff\0\0\0\0\x15\x02\x3d\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x10\0\0\0\ -\x15\x02\x3a\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x5c\xff\xff\xff\x71\xa4\ -\x54\xff\0\0\0\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\ -\x07\x03\0\0\x7c\xff\xff\xff\x67\x01\0\0\x38\0\0\0\xc7\x01\0\0\x38\0\0\0\x65\ -\x01\x01\0\xff\xff\xff\xff\xbf\x32\0\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\ -\x6c\xff\xff\xff\x71\xa5\x55\xff\0\0\0\0\xbf\x34\0\0\0\0\0\0\x15\x05\x02\0\0\0\ -\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\x8c\xff\xff\xff\x65\x01\x01\0\xff\xff\xff\ -\xff\xbf\x43\0\0\0\0\0\0\x61\x21\x04\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x61\x24\0\ -\0\0\0\0\0\x4f\x41\0\0\0\0\0\0\x7b\x1a\xa0\xff\0\0\0\0\x61\x21\x08\0\0\0\0\0\ -\x61\x22\x0c\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\x4f\x12\0\0\0\0\0\0\x7b\x2a\xa8\ -\xff\0\0\0\0\x61\x31\0\0\0\0\0\0\x61\x32\x04\0\0\0\0\0\x61\x34\x08\0\0\0\0\0\ -\x61\x33\x0c\0\0\0\0\0\x69\xa5\x5a\xff\0\0\0\0\x6b\x5a\xc2\xff\0\0\0\0\x69\xa5\ -\x58\xff\0\0\0\0\x6b\x5a\xc0\xff\0\0\0\0\x67\x03\0\0\x20\0\0\0\x4f\x43\0\0\0\0\ -\0\0\x7b\x3a\xb8\xff\0\0\0\0\x67\x02\0\0\x20\0\0\0\x4f\x12\0\0\0\0\0\0\x7b\x2a\ -\xb0\xff\0\0\0\0\x05\0\x6b\0\0\0\0\0\x71\xa2\x52\xff\0\0\0\0\x15\x02\x04\0\0\0\ -\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x04\0\0\0\x15\x02\x01\0\0\0\0\0\x05\0\xf7\ -\xfe\0\0\0\0\x57\x01\0\0\x01\0\0\0\x15\x01\xd3\0\0\0\0\0\x61\xa1\x5c\xff\0\0\0\ -\0\x63\x1a\xa0\xff\0\0\0\0\x61\xa1\x60\xff\0\0\0\0\x63\x1a\xa4\xff\0\0\0\0\x05\ -\0\x5e\0\0\0\0\0\x71\xa2\x52\xff\0\0\0\0\x15\x02\x1e\0\0\0\0\0\xbf\x12\0\0\0\0\ -\0\0\x57\x02\0\0\x20\0\0\0\x15\x02\x1b\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\ -\0\x5c\xff\xff\xff\x71\xa4\x54\xff\0\0\0\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\ -\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x7c\xff\xff\xff\x57\x01\0\0\0\x01\0\0\ -\x15\x01\x01\0\0\0\0\0\xbf\x32\0\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x6c\ -\xff\xff\xff\x71\xa5\x55\xff\0\0\0\0\xbf\x34\0\0\0\0\0\0\x15\x05\x02\0\0\0\0\0\ -\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\x8c\xff\xff\xff\x15\x01\xc3\xff\0\0\0\0\x05\0\ -\xc1\xff\0\0\0\0\xb7\x09\0\0\x3c\0\0\0\x79\xa7\x20\xff\0\0\0\0\x67\0\0\0\x20\0\ -\0\0\x77\0\0\0\x20\0\0\0\x15\0\xa5\xfe\0\0\0\0\x05\0\xb0\0\0\0\0\0\x15\x09\x07\ -\xff\x87\0\0\0\x05\0\xa2\xfe\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x08\0\0\0\ -\x15\x02\xab\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x5c\xff\xff\xff\x71\xa4\ -\x54\xff\0\0\0\0\xbf\x23\0\0\0\0\0\0\x15\x04\x02\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\ -\x07\x03\0\0\x7c\xff\xff\xff\x57\x01\0\0\x40\0\0\0\x15\x01\x01\0\0\0\0\0\xbf\ -\x32\0\0\0\0\0\0\x61\x23\x04\0\0\0\0\0\x67\x03\0\0\x20\0\0\0\x61\x24\0\0\0\0\0\ -\0\x4f\x43\0\0\0\0\0\0\x7b\x3a\xa0\xff\0\0\0\0\x61\x23\x08\0\0\0\0\0\x61\x22\ -\x0c\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\x4f\x32\0\0\0\0\0\0\x7b\x2a\xa8\xff\0\0\0\ -\0\x15\x01\x1c\0\0\0\0\0\x71\xa1\x55\xff\0\0\0\0\x15\x01\x1a\0\0\0\0\0\x61\xa1\ -\x98\xff\0\0\0\0\x67\x01\0\0\x20\0\0\0\x61\xa2\x94\xff\0\0\0\0\x4f\x21\0\0\0\0\ -\0\0\x7b\x1a\xb8\xff\0\0\0\0\x61\xa1\x90\xff\0\0\0\0\x67\x01\0\0\x20\0\0\0\x61\ -\xa2\x8c\xff\0\0\0\0\x05\0\x19\0\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x52\xff\ -\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\ -\x03\0\0\xd0\xff\xff\xff\xbf\x81\0\0\0\0\0\0\x79\xa2\x40\xff\0\0\0\0\xb7\x04\0\ -\0\x08\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\ -\0\0\0\x20\0\0\0\x55\0\x7d\0\0\0\0\0\x05\0\x88\xfe\0\0\0\0\xb7\x09\0\0\x2b\0\0\ -\0\x05\0\xc6\xff\0\0\0\0\x61\xa1\x78\xff\0\0\0\0\x67\x01\0\0\x20\0\0\0\x61\xa2\ -\x74\xff\0\0\0\0\x4f\x21\0\0\0\0\0\0\x7b\x1a\xb8\xff\0\0\0\0\x61\xa1\x70\xff\0\ -\0\0\0\x67\x01\0\0\x20\0\0\0\x61\xa2\x6c\xff\0\0\0\0\x4f\x21\0\0\0\0\0\0\x7b\ -\x1a\xb0\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x07\x07\0\0\x04\0\0\0\x61\x03\0\0\0\0\ -\0\0\xb7\x05\0\0\0\0\0\0\x05\0\x4e\0\0\0\0\0\xaf\x52\0\0\0\0\0\0\xbf\x75\0\0\0\ -\0\0\0\x0f\x15\0\0\0\0\0\0\x71\x55\0\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\ -\0\0\0\0\0\x77\0\0\0\x07\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\ -\0\x39\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\xbf\ -\x50\0\0\0\0\0\0\x77\0\0\0\x06\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\ -\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3a\0\0\0\xc7\0\0\0\x3f\0\0\ -\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\ -\0\0\0\x77\0\0\0\x05\0\0\0\x57\0\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\ -\0\0\0\0\x67\0\0\0\x3b\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\ -\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x04\0\0\0\x57\0\ -\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3c\0\0\0\xc7\ -\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\ -\x77\0\0\0\x03\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\ -\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3d\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\ -\0\0\0\xaf\x02\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x02\0\0\0\x57\0\0\0\ -\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\ -\0\0\x3e\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x02\0\0\0\0\0\0\xbf\ -\x50\0\0\0\0\0\0\x77\0\0\0\x01\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\ -\x4f\x03\0\0\0\0\0\0\x57\x04\0\0\x01\0\0\0\x87\x04\0\0\0\0\0\0\x5f\x34\0\0\0\0\ -\0\0\xaf\x42\0\0\0\0\0\0\x57\x05\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x53\0\ -\0\0\0\0\0\x07\x01\0\0\x01\0\0\0\xbf\x25\0\0\0\0\0\0\x15\x01\x0b\0\x24\0\0\0\ -\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xa0\xff\xff\xff\x0f\x12\0\0\0\0\0\0\x71\x24\0\ -\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x38\0\0\0\xc7\0\0\0\x38\0\0\0\xb7\x02\ -\0\0\0\0\0\0\x65\0\xa9\xff\xff\xff\xff\xff\xbf\x32\0\0\0\0\0\0\x05\0\xa7\xff\0\ -\0\0\0\xbf\x21\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x15\x01\ -\x0e\0\0\0\0\0\x71\x63\x06\0\0\0\0\0\x71\x64\x07\0\0\0\0\0\x67\x04\0\0\x08\0\0\ -\0\x4f\x34\0\0\0\0\0\0\x3f\x41\0\0\0\0\0\0\x2f\x41\0\0\0\0\0\0\x1f\x12\0\0\0\0\ -\0\0\x63\x2a\x50\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x50\xff\xff\xff\ -\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\x55\0\x05\0\0\0\0\0\ -\x71\x61\x08\0\0\0\0\0\x71\x60\x09\0\0\0\0\0\x67\0\0\0\x08\0\0\0\x4f\x10\0\0\0\ -\0\0\0\x95\0\0\0\0\0\0\0\x69\0\0\0\0\0\0\0\x05\0\xfd\xff\0\0\0\0\x02\0\0\0\x04\ -\0\0\0\x0a\0\0\0\x01\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x28\0\0\0\x01\0\0\0\0\0\ -\0\0\x02\0\0\0\x04\0\0\0\x02\0\0\0\x80\0\0\0\0\0\0\0\x47\x50\x4c\x20\x76\x32\0\ -\0\0\0\0\0\x10\0\0\0\0\0\0\0\x01\x7a\x52\0\x08\x7c\x0b\x01\x0c\0\0\0\x18\0\0\0\ -\x18\0\0\0\0\0\0\0\0\0\0\0\xd8\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\xa0\0\0\0\x04\0\xf1\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\x60\x02\0\0\0\0\x03\0\x20\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x3f\x02\0\0\0\0\ -\x03\0\xd0\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xed\x01\0\0\0\0\x03\0\x10\x10\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\xd4\x01\0\0\0\0\x03\0\x20\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\xa3\x01\0\0\0\0\x03\0\xb8\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x63\x01\0\0\0\0\ -\x03\0\x48\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2a\x01\0\0\0\0\x03\0\x10\x13\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\xe1\0\0\0\0\0\x03\0\xa0\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\x2e\x02\0\0\0\0\x03\0\x28\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x68\x02\0\0\0\0\x03\ -\0\xc0\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x36\x02\0\0\0\0\x03\0\xc8\x13\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\x22\x01\0\0\0\0\x03\0\xe8\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\x02\x01\0\0\0\0\x03\0\x40\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd9\0\0\0\0\0\x03\0\ -\xf8\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x26\x02\0\0\0\0\x03\0\x20\x0e\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\xcc\x01\0\0\0\0\x03\0\x60\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9b\ -\x01\0\0\0\0\x03\0\xc8\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x5b\x01\0\0\0\0\x03\0\ -\x20\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x7c\x01\0\0\0\0\x03\0\x48\x08\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\x53\x01\0\0\0\0\x03\0\xb8\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1a\ -\x01\0\0\0\0\x03\0\xe0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x84\x01\0\0\0\0\x03\0\ -\xb8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1e\x02\0\0\0\0\x03\0\xd8\x09\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\xc4\x01\0\0\0\0\x03\0\x70\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x93\ -\x01\0\0\0\0\x03\0\xa8\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x74\x01\0\0\0\0\x03\0\ -\xf0\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x4b\x01\0\0\0\0\x03\0\0\x0a\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\x12\x01\0\0\0\0\x03\0\x10\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xfa\0\ -\0\0\0\0\x03\0\xc0\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x58\x02\0\0\0\0\x03\0\x88\ -\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x02\0\0\0\0\x03\0\xb8\x0a\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\xe5\x01\0\0\0\0\x03\0\xc0\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xbc\x01\ -\0\0\0\0\x03\0\0\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x8b\x01\0\0\0\0\x03\0\x18\x0e\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd1\0\0\0\0\0\x03\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\x50\x02\0\0\0\0\x03\0\x20\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0e\x02\0\0\0\0\ -\x03\0\x48\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x6c\x01\0\0\0\0\x03\0\xb0\x04\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\x43\x01\0\0\0\0\x03\0\xc8\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\xc9\0\0\0\0\0\x03\0\xf8\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\x02\0\0\0\0\x03\ -\0\xd0\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x3b\x01\0\0\0\0\x03\0\x98\x0b\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\xf2\0\0\0\0\0\x03\0\xb8\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x48\ -\x02\0\0\0\0\x03\0\xf0\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xfe\x01\0\0\0\0\x03\0\ -\xf8\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xdd\x01\0\0\0\0\x03\0\0\x0c\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\xb4\x01\0\0\0\0\x03\0\x30\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\ -\x01\0\0\0\0\x03\0\x90\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc1\0\0\0\0\0\x03\0\xa8\ -\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xba\0\0\0\0\0\x03\0\xd0\x01\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\xf6\x01\0\0\0\0\x03\0\xe0\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xac\x01\0\ -\0\0\0\x03\0\x30\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x33\x01\0\0\0\0\x03\0\x80\x0e\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xea\0\0\0\0\0\x03\0\x98\x0e\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\x03\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x6b\0\0\0\x11\0\x06\ -\0\0\0\0\0\0\0\0\0\x07\0\0\0\0\0\0\0\x25\0\0\0\x11\0\x05\0\0\0\0\0\0\0\0\0\x14\ -\0\0\0\0\0\0\0\x82\0\0\0\x11\0\x05\0\x28\0\0\0\0\0\0\0\x14\0\0\0\0\0\0\0\x01\0\ -\0\0\x11\0\x05\0\x14\0\0\0\0\0\0\0\x14\0\0\0\0\0\0\0\x40\0\0\0\x12\0\x03\0\0\0\ -\0\0\0\0\0\0\xd8\x13\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\x01\0\0\0\x3a\0\0\0\x50\0\0\ -\0\0\0\0\0\x01\0\0\0\x3c\0\0\0\x80\x13\0\0\0\0\0\0\x01\0\0\0\x3b\0\0\0\x1c\0\0\ -\0\0\0\0\0\x01\0\0\0\x38\0\0\0\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\ -\x5f\x74\x6f\x65\x70\x6c\x69\x74\x7a\x5f\x6b\x65\x79\0\x2e\x74\x65\x78\x74\0\ -\x6d\x61\x70\x73\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x63\x6f\x6e\ -\x66\x69\x67\x75\x72\x61\x74\x69\x6f\x6e\x73\0\x74\x75\x6e\x5f\x72\x73\x73\x5f\ -\x73\x74\x65\x65\x72\x69\x6e\x67\x5f\x70\x72\x6f\x67\0\x2e\x72\x65\x6c\x74\x75\ -\x6e\x5f\x72\x73\x73\x5f\x73\x74\x65\x65\x72\x69\x6e\x67\0\x5f\x6c\x69\x63\x65\ -\x6e\x73\x65\0\x2e\x72\x65\x6c\x2e\x65\x68\x5f\x66\x72\x61\x6d\x65\0\x74\x61\ -\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x69\x6e\x64\x69\x72\x65\x63\x74\x69\ -\x6f\x6e\x5f\x74\x61\x62\x6c\x65\0\x72\x73\x73\x2e\x62\x70\x66\x2e\x63\0\x2e\ -\x73\x74\x72\x74\x61\x62\0\x2e\x73\x79\x6d\x74\x61\x62\0\x4c\x42\x42\x30\x5f\ -\x39\0\x4c\x42\x42\x30\x5f\x38\x39\0\x4c\x42\x42\x30\x5f\x36\x39\0\x4c\x42\x42\ -\x30\x5f\x35\x39\0\x4c\x42\x42\x30\x5f\x31\x39\0\x4c\x42\x42\x30\x5f\x31\x30\ -\x39\0\x4c\x42\x42\x30\x5f\x39\x38\0\x4c\x42\x42\x30\x5f\x37\x38\0\x4c\x42\x42\ -\x30\x5f\x34\x38\0\x4c\x42\x42\x30\x5f\x31\x38\0\x4c\x42\x42\x30\x5f\x38\x37\0\ -\x4c\x42\x42\x30\x5f\x34\x37\0\x4c\x42\x42\x30\x5f\x33\x37\0\x4c\x42\x42\x30\ -\x5f\x31\x37\0\x4c\x42\x42\x30\x5f\x31\x30\x37\0\x4c\x42\x42\x30\x5f\x39\x36\0\ -\x4c\x42\x42\x30\x5f\x37\x36\0\x4c\x42\x42\x30\x5f\x36\x36\0\x4c\x42\x42\x30\ -\x5f\x34\x36\0\x4c\x42\x42\x30\x5f\x33\x36\0\x4c\x42\x42\x30\x5f\x32\x36\0\x4c\ -\x42\x42\x30\x5f\x31\x30\x36\0\x4c\x42\x42\x30\x5f\x36\x35\0\x4c\x42\x42\x30\ -\x5f\x34\x35\0\x4c\x42\x42\x30\x5f\x33\x35\0\x4c\x42\x42\x30\x5f\x34\0\x4c\x42\ -\x42\x30\x5f\x35\x34\0\x4c\x42\x42\x30\x5f\x34\x34\0\x4c\x42\x42\x30\x5f\x32\ -\x34\0\x4c\x42\x42\x30\x5f\x31\x30\x34\0\x4c\x42\x42\x30\x5f\x39\x33\0\x4c\x42\ -\x42\x30\x5f\x38\x33\0\x4c\x42\x42\x30\x5f\x35\x33\0\x4c\x42\x42\x30\x5f\x34\ -\x33\0\x4c\x42\x42\x30\x5f\x32\x33\0\x4c\x42\x42\x30\x5f\x31\x30\x33\0\x4c\x42\ -\x42\x30\x5f\x38\x32\0\x4c\x42\x42\x30\x5f\x35\x32\0\x4c\x42\x42\x30\x5f\x31\ -\x30\x32\0\x4c\x42\x42\x30\x5f\x39\x31\0\x4c\x42\x42\x30\x5f\x38\x31\0\x4c\x42\ -\x42\x30\x5f\x37\x31\0\x4c\x42\x42\x30\x5f\x36\x31\0\x4c\x42\x42\x30\x5f\x35\ -\x31\0\x4c\x42\x42\x30\x5f\x34\x31\0\x4c\x42\x42\x30\x5f\x32\x31\0\x4c\x42\x42\ -\x30\x5f\x31\x31\0\x4c\x42\x42\x30\x5f\x31\x31\x31\0\x4c\x42\x42\x30\x5f\x31\ -\x30\x31\0\x4c\x42\x42\x30\x5f\x38\x30\0\x4c\x42\x42\x30\x5f\x36\x30\0\x4c\x42\ -\x42\x30\x5f\x35\x30\0\x4c\x42\x42\x30\x5f\x31\x30\0\x4c\x42\x42\x30\x5f\x31\ -\x31\x30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xaa\ -\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa0\x1a\0\0\0\0\0\0\x71\x02\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1a\0\0\0\x01\0\0\ -\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x5a\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\xd8\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x56\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\x60\x1a\0\0\0\0\0\0\x30\0\0\0\0\0\0\0\x09\0\0\0\x03\0\0\0\x08\0\0\0\0\0\0\0\ -\x10\0\0\0\0\0\0\0\x20\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\ -\x14\0\0\0\0\0\0\x3c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\x6c\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x54\x14\0\0\0\0\0\ -\0\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x78\0\0\ -\0\x01\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x60\x14\0\0\0\0\0\0\x30\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x74\0\0\0\x09\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x1a\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x09\0\0\0\ -\x07\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\xb2\0\0\0\x02\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\x90\x14\0\0\0\0\0\0\xd0\x05\0\0\0\0\0\0\x01\0\0\0\x39\0\0\ -\0\x08\0\0\0\0\0\0\0\x18\0\0\0\0\0\0\0"; +\x77\0\0\0\x20\0\0\0\x55\0\x3c\x02\0\0\0\0\xb7\x02\0\0\x10\0\0\0\x69\xa1\xc8\ +\xff\0\0\0\0\xbf\x13\0\0\0\0\0\0\xdc\x03\0\0\x10\0\0\0\x15\x03\x02\0\0\x81\0\0\ +\x55\x03\x0b\0\xa8\x88\0\0\xb7\x02\0\0\x14\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\ +\0\xc8\xff\xff\xff\xbf\x91\0\0\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\0\0\0\ +\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x2c\x02\0\ +\0\0\0\x69\xa1\xc8\xff\0\0\0\0\x15\x01\x2a\x02\0\0\0\0\x7b\x9a\x38\xff\0\0\0\0\ +\x15\x01\x56\0\x86\xdd\0\0\x55\x01\x3b\0\x08\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\ +\x1a\x50\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\ +\xff\0\0\0\0\x7b\x1a\xc8\xff\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xc8\xff\ +\xff\xff\x79\xa1\x38\xff\0\0\0\0\xb7\x02\0\0\0\0\0\0\xb7\x04\0\0\x14\0\0\0\xb7\ +\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\ +\x55\0\x17\x02\0\0\0\0\x69\xa1\xce\xff\0\0\0\0\x57\x01\0\0\x3f\xff\0\0\xb7\x02\ +\0\0\x01\0\0\0\x55\x01\x01\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\x61\xa1\xd4\xff\0\0\0\ +\0\x63\x1a\x5c\xff\0\0\0\0\x61\xa1\xd8\xff\0\0\0\0\x63\x1a\x60\xff\0\0\0\0\x71\ +\xa9\xd1\xff\0\0\0\0\x71\xa1\xc8\xff\0\0\0\0\x67\x01\0\0\x02\0\0\0\x57\x01\0\0\ +\x3c\0\0\0\x7b\x1a\x40\xff\0\0\0\0\x73\x2a\x56\xff\0\0\0\0\xbf\x91\0\0\0\0\0\0\ +\x57\x01\0\0\xff\0\0\0\x15\x01\x19\0\0\0\0\0\x57\x02\0\0\xff\0\0\0\x55\x02\x17\ +\0\0\0\0\0\x57\x09\0\0\xff\0\0\0\x15\x09\x57\x01\x11\0\0\0\x55\x09\x14\0\x06\0\ +\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x53\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\ +\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\x7b\x1a\xc8\xff\0\0\0\0\xbf\xa3\0\0\0\ +\0\0\0\x07\x03\0\0\xc8\xff\xff\xff\x79\xa1\x38\xff\0\0\0\0\x79\xa2\x40\xff\0\0\ +\0\0\xb7\x04\0\0\x14\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\ +\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\xf0\x01\0\0\0\0\x69\xa1\xc8\xff\0\0\0\0\ +\x6b\x1a\x58\xff\0\0\0\0\x69\xa1\xca\xff\0\0\0\0\x6b\x1a\x5a\xff\0\0\0\0\x71\ +\xa1\x50\xff\0\0\0\0\x15\x01\xd8\0\0\0\0\0\x71\x62\x03\0\0\0\0\0\x67\x02\0\0\ +\x08\0\0\0\x71\x61\x02\0\0\0\0\0\x4f\x12\0\0\0\0\0\0\x71\x63\x04\0\0\0\0\0\x67\ +\x03\0\0\x10\0\0\0\x71\x61\x05\0\0\0\0\0\x67\x01\0\0\x18\0\0\0\x4f\x31\0\0\0\0\ +\0\0\x4f\x21\0\0\0\0\0\0\x71\xa2\x53\xff\0\0\0\0\x15\x02\x09\x01\0\0\0\0\xbf\ +\x12\0\0\0\0\0\0\x57\x02\0\0\x02\0\0\0\x15\x02\x06\x01\0\0\0\0\x61\xa1\x5c\xff\ +\0\0\0\0\x63\x1a\xa0\xff\0\0\0\0\x61\xa1\x60\xff\0\0\0\0\x63\x1a\xa4\xff\0\0\0\ +\0\x69\xa1\x58\xff\0\0\0\0\x6b\x1a\xa8\xff\0\0\0\0\x69\xa1\x5a\xff\0\0\0\0\x6b\ +\x1a\xaa\xff\0\0\0\0\x05\0\x68\x01\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x51\ +\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\xe8\xff\0\0\0\0\x7b\x1a\xe0\xff\0\0\0\ +\0\x7b\x1a\xd8\xff\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\x7b\x1a\xc8\xff\0\0\0\0\xbf\ +\xa3\0\0\0\0\0\0\x07\x03\0\0\xc8\xff\xff\xff\xb7\x01\0\0\x28\0\0\0\x7b\x1a\x40\ +\xff\0\0\0\0\xbf\x91\0\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\xb7\x04\0\0\x28\0\0\0\xb7\ +\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\ +\x55\0\xfc\0\0\0\0\0\x79\xa1\xd8\xff\0\0\0\0\x63\x1a\x64\xff\0\0\0\0\x77\x01\0\ +\0\x20\0\0\0\x63\x1a\x68\xff\0\0\0\0\x79\xa1\xd0\xff\0\0\0\0\x63\x1a\x5c\xff\0\ +\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x60\xff\0\0\0\0\x79\xa1\xe0\xff\0\0\0\0\ +\x63\x1a\x6c\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x70\xff\0\0\0\0\x79\xa1\ +\xe8\xff\0\0\0\0\x63\x1a\x74\xff\0\0\0\0\x77\x01\0\0\x20\0\0\0\x63\x1a\x78\xff\ +\0\0\0\0\x71\xa9\xce\xff\0\0\0\0\x25\x09\x11\x01\x3c\0\0\0\xb7\x01\0\0\x01\0\0\ +\0\x6f\x91\0\0\0\0\0\0\x18\x02\0\0\x01\0\0\0\0\0\0\0\0\x18\0\x1c\x5f\x21\0\0\0\ +\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\x0a\x01\0\0\0\0\xb7\x01\0\0\0\0\0\0\x6b\x1a\ +\xf8\xff\0\0\0\0\xb7\x01\0\0\x28\0\0\0\x7b\x1a\x40\xff\0\0\0\0\xbf\xa1\0\0\0\0\ +\0\0\x07\x01\0\0\x8c\xff\xff\xff\x7b\x1a\x18\xff\0\0\0\0\xbf\xa1\0\0\0\0\0\0\ +\x07\x01\0\0\x7c\xff\xff\xff\x7b\x1a\x10\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\ +\x1a\x30\xff\0\0\0\0\x7b\x7a\x28\xff\0\0\0\0\x7b\x8a\x20\xff\0\0\0\0\xbf\xa3\0\ +\0\0\0\0\0\x07\x03\0\0\xf8\xff\xff\xff\x79\xa1\x38\xff\0\0\0\0\x79\xa2\x40\xff\ +\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\xbf\ +\x01\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\xc7\0\0\0\ +\0\0\xbf\x91\0\0\0\0\0\0\x15\x01\x24\0\x3c\0\0\0\x15\x01\x5c\0\x2c\0\0\0\x55\ +\x01\x5d\0\x2b\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\xf0\xff\0\0\0\0\xbf\xa3\0\0\0\ +\0\0\0\x07\x03\0\0\xf0\xff\xff\xff\x79\xa9\x38\xff\0\0\0\0\xbf\x91\0\0\0\0\0\0\ +\x79\xa2\x40\xff\0\0\0\0\xb7\x04\0\0\x04\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\ +\x44\0\0\0\xbf\x01\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\ +\x01\x06\x01\0\0\0\0\x71\xa1\xf2\xff\0\0\0\0\x55\x01\x4d\0\x02\0\0\0\x71\xa1\ +\xf1\xff\0\0\0\0\x55\x01\x4b\0\x02\0\0\0\x71\xa1\xf3\xff\0\0\0\0\x55\x01\x49\0\ +\x01\0\0\0\x79\xa2\x40\xff\0\0\0\0\x07\x02\0\0\x08\0\0\0\xbf\x91\0\0\0\0\0\0\ +\x79\xa3\x18\xff\0\0\0\0\xb7\x04\0\0\x10\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\ +\x44\0\0\0\xbf\x01\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\ +\x01\xf5\0\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x55\xff\0\0\0\0\x05\0\x3b\0\0\ +\0\0\0\xb7\x08\0\0\x02\0\0\0\xb7\x07\0\0\0\0\0\0\x6b\x7a\xf0\xff\0\0\0\0\x05\0\ +\x12\0\0\0\0\0\x0f\x81\0\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x07\x02\0\0\x01\0\0\0\ +\x71\xa3\xf9\xff\0\0\0\0\x67\x03\0\0\x03\0\0\0\x3d\x32\x09\0\0\0\0\0\xbf\x72\0\ +\0\0\0\0\0\x07\x02\0\0\x01\0\0\0\x67\x07\0\0\x20\0\0\0\xbf\x73\0\0\0\0\0\0\x77\ +\x03\0\0\x20\0\0\0\xbf\x27\0\0\0\0\0\0\xbf\x18\0\0\0\0\0\0\xb7\x01\0\0\x1d\0\0\ +\0\x2d\x31\x03\0\0\0\0\0\x79\xa7\x28\xff\0\0\0\0\x79\xa8\x20\xff\0\0\0\0\x05\0\ +\x25\0\0\0\0\0\xbf\x89\0\0\0\0\0\0\x79\xa1\x40\xff\0\0\0\0\x0f\x19\0\0\0\0\0\0\ +\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xf0\xff\xff\xff\x79\xa1\x38\xff\0\0\0\0\xbf\ +\x92\0\0\0\0\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\ +\0\xbf\x01\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\x7a\ +\0\0\0\0\0\x71\xa2\xf0\xff\0\0\0\0\x55\x02\x0e\0\xc9\0\0\0\x07\x09\0\0\x02\0\0\ +\0\x79\xa1\x38\xff\0\0\0\0\xbf\x92\0\0\0\0\0\0\x79\xa3\x10\xff\0\0\0\0\xb7\x04\ +\0\0\x10\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\xbf\x01\0\0\0\0\0\0\ +\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\x6d\0\0\0\0\0\xb7\x01\0\0\ +\x01\0\0\0\x73\x1a\x54\xff\0\0\0\0\x05\0\xdf\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\ +\x15\x02\xce\xff\0\0\0\0\x71\xa1\xf1\xff\0\0\0\0\x07\x01\0\0\x02\0\0\0\x05\0\ +\xcb\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x56\xff\0\0\0\0\x71\xa1\xf9\xff\ +\0\0\0\0\x67\x01\0\0\x03\0\0\0\x79\xa2\x40\xff\0\0\0\0\x0f\x12\0\0\0\0\0\0\x07\ +\x02\0\0\x08\0\0\0\x7b\x2a\x40\xff\0\0\0\0\x71\xa9\xf8\xff\0\0\0\0\x25\x09\x0f\ +\0\x3c\0\0\0\xb7\x01\0\0\x01\0\0\0\x6f\x91\0\0\0\0\0\0\x18\x02\0\0\x01\0\0\0\0\ +\0\0\0\0\x18\0\x1c\x5f\x21\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\x08\0\0\0\0\ +\0\x79\xa1\x30\xff\0\0\0\0\x07\x01\0\0\x01\0\0\0\x7b\x1a\x30\xff\0\0\0\0\x67\ +\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\x7f\xff\x0b\0\0\0\x71\xa2\x56\ +\xff\0\0\0\0\x05\0\x0c\xff\0\0\0\0\x15\x09\xf7\xff\x87\0\0\0\x05\0\xfc\xff\0\0\ +\0\0\x71\xa1\x51\xff\0\0\0\0\x15\x01\x10\x01\0\0\0\0\x71\x62\x03\0\0\0\0\0\x67\ +\x02\0\0\x08\0\0\0\x71\x61\x02\0\0\0\0\0\x4f\x12\0\0\0\0\0\0\x71\x63\x04\0\0\0\ +\0\0\x67\x03\0\0\x10\0\0\0\x71\x61\x05\0\0\0\0\0\x67\x01\0\0\x18\0\0\0\x4f\x31\ +\0\0\0\0\0\0\x4f\x21\0\0\0\0\0\0\x71\xa2\x53\xff\0\0\0\0\x15\x02\x43\0\0\0\0\0\ +\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x10\0\0\0\x15\x02\x40\0\0\0\0\0\x57\x01\0\0\ +\x80\0\0\0\xb7\x02\0\0\x10\0\0\0\xb7\x03\0\0\x10\0\0\0\x15\x01\x01\0\0\0\0\0\ +\xb7\x03\0\0\x30\0\0\0\x71\xa4\x55\xff\0\0\0\0\x15\x04\x01\0\0\0\0\0\xbf\x32\0\ +\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x5c\xff\xff\xff\xbf\x34\0\0\0\0\0\0\ +\x15\x01\x02\0\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\x7c\xff\xff\xff\x71\xa5\ +\x54\xff\0\0\0\0\xbf\x31\0\0\0\0\0\0\x15\x05\x01\0\0\0\0\0\xbf\x41\0\0\0\0\0\0\ +\x61\x14\x04\0\0\0\0\0\x67\x04\0\0\x20\0\0\0\x61\x15\0\0\0\0\0\0\x4f\x54\0\0\0\ +\0\0\0\x7b\x4a\xa0\xff\0\0\0\0\x61\x14\x08\0\0\0\0\0\x61\x11\x0c\0\0\0\0\0\x67\ +\x01\0\0\x20\0\0\0\x4f\x41\0\0\0\0\0\0\x7b\x1a\xa8\xff\0\0\0\0\x0f\x23\0\0\0\0\ +\0\0\x61\x31\0\0\0\0\0\0\x61\x32\x04\0\0\0\0\0\x61\x34\x08\0\0\0\0\0\x61\x33\ +\x0c\0\0\0\0\0\x69\xa5\x5a\xff\0\0\0\0\x6b\x5a\xc2\xff\0\0\0\0\x69\xa5\x58\xff\ +\0\0\0\0\x6b\x5a\xc0\xff\0\0\0\0\x67\x03\0\0\x20\0\0\0\x4f\x43\0\0\0\0\0\0\x7b\ +\x3a\xb8\xff\0\0\0\0\x67\x02\0\0\x20\0\0\0\x4f\x12\0\0\0\0\0\0\x7b\x2a\xb0\xff\ +\0\0\0\0\x05\0\x6b\0\0\0\0\0\x71\xa2\x52\xff\0\0\0\0\x15\x02\x04\0\0\0\0\0\xbf\ +\x12\0\0\0\0\0\0\x57\x02\0\0\x04\0\0\0\x15\x02\x01\0\0\0\0\0\x05\0\xf4\xfe\0\0\ +\0\0\x57\x01\0\0\x01\0\0\0\x15\x01\xcd\0\0\0\0\0\x61\xa1\x5c\xff\0\0\0\0\x63\ +\x1a\xa0\xff\0\0\0\0\x61\xa1\x60\xff\0\0\0\0\x63\x1a\xa4\xff\0\0\0\0\x05\0\x5e\ +\0\0\0\0\0\xb7\x09\0\0\x3c\0\0\0\x79\xa7\x28\xff\0\0\0\0\x79\xa8\x20\xff\0\0\0\ +\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x15\0\xac\xff\0\0\0\0\x05\0\xc1\0\0\ +\0\0\0\x71\xa2\x52\xff\0\0\0\0\x15\x02\x26\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\ +\x02\0\0\x20\0\0\0\x15\x02\x23\0\0\0\0\0\x57\x01\0\0\0\x01\0\0\xb7\x02\0\0\x10\ +\0\0\0\xb7\x03\0\0\x10\0\0\0\x15\x01\x01\0\0\0\0\0\xb7\x03\0\0\x30\0\0\0\x71\ +\xa4\x55\xff\0\0\0\0\x15\x04\x01\0\0\0\0\0\xbf\x32\0\0\0\0\0\0\xbf\xa3\0\0\0\0\ +\0\0\x07\x03\0\0\x5c\xff\xff\xff\xbf\x34\0\0\0\0\0\0\x15\x01\x02\0\0\0\0\0\xbf\ +\xa4\0\0\0\0\0\0\x07\x04\0\0\x7c\xff\xff\xff\x71\xa5\x54\xff\0\0\0\0\xbf\x31\0\ +\0\0\0\0\0\x15\x05\xbc\xff\0\0\0\0\x05\0\xba\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\ +\x73\x1a\x52\xff\0\0\0\0\xb7\x01\0\0\0\0\0\0\x7b\x1a\xc8\xff\0\0\0\0\xbf\xa3\0\ +\0\0\0\0\0\x07\x03\0\0\xc8\xff\xff\xff\x79\xa1\x38\xff\0\0\0\0\x79\xa2\x40\xff\ +\0\0\0\0\xb7\x04\0\0\x08\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\ +\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x9c\0\0\0\0\0\x05\0\xab\xfe\0\0\0\0\ +\x15\x09\xf5\xfe\x87\0\0\0\x05\0\x83\xff\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\ +\0\x08\0\0\0\x15\x02\x96\0\0\0\0\0\x57\x01\0\0\x40\0\0\0\xb7\x02\0\0\x0c\0\0\0\ +\xb7\x03\0\0\x0c\0\0\0\x15\x01\x01\0\0\0\0\0\xb7\x03\0\0\x2c\0\0\0\x71\xa4\x54\ +\xff\0\0\0\0\x15\x04\x01\0\0\0\0\0\xbf\x32\0\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\ +\x03\0\0\x50\xff\xff\xff\x0f\x23\0\0\0\0\0\0\x61\x32\x04\0\0\0\0\0\x67\x02\0\0\ +\x20\0\0\0\x61\x34\0\0\0\0\0\0\x4f\x42\0\0\0\0\0\0\x7b\x2a\xa0\xff\0\0\0\0\x61\ +\x32\x08\0\0\0\0\0\x61\x33\x0c\0\0\0\0\0\x67\x03\0\0\x20\0\0\0\x4f\x23\0\0\0\0\ +\0\0\x7b\x3a\xa8\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xb0\xff\xff\xff\ +\x71\xa3\x55\xff\0\0\0\0\x15\x03\x0b\0\0\0\0\0\x15\x01\x0a\0\0\0\0\0\x61\xa1\ +\x98\xff\0\0\0\0\x63\x12\x0c\0\0\0\0\0\x61\xa1\x94\xff\0\0\0\0\x63\x12\x08\0\0\ +\0\0\0\x61\xa1\x90\xff\0\0\0\0\x63\x12\x04\0\0\0\0\0\x61\xa1\x8c\xff\0\0\0\0\ +\x05\0\x09\0\0\0\0\0\xb7\x09\0\0\x2b\0\0\0\x05\0\xad\xff\0\0\0\0\x61\xa1\x78\ +\xff\0\0\0\0\x63\x12\x0c\0\0\0\0\0\x61\xa1\x74\xff\0\0\0\0\x63\x12\x08\0\0\0\0\ +\0\x61\xa1\x70\xff\0\0\0\0\x63\x12\x04\0\0\0\0\0\x61\xa1\x6c\xff\0\0\0\0\x63\ +\x12\0\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\x07\x07\0\0\x04\0\0\0\x61\x83\0\0\0\0\0\0\ +\xb7\x05\0\0\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\xa0\xff\xff\xff\x0f\x21\0\ +\0\0\0\0\0\x71\x14\0\0\0\0\0\0\xbf\x41\0\0\0\0\0\0\x67\x01\0\0\x38\0\0\0\xc7\ +\x01\0\0\x3f\0\0\0\x5f\x31\0\0\0\0\0\0\xaf\x51\0\0\0\0\0\0\xbf\x75\0\0\0\0\0\0\ +\x0f\x25\0\0\0\0\0\0\x71\x55\0\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\0\ +\0\0\x77\0\0\0\x07\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x39\ +\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x01\0\0\0\0\0\0\xbf\x50\0\0\ +\0\0\0\0\x77\0\0\0\x06\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\ +\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3a\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\ +\x30\0\0\0\0\0\0\xaf\x01\0\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\0\0\0\ +\x77\0\0\0\x05\0\0\0\x57\0\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\ +\0\x67\0\0\0\x3b\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x01\0\0\0\0\ +\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x04\0\0\0\x57\0\0\0\ +\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3c\0\0\0\xc7\0\0\ +\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x01\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\ +\0\0\x03\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\ +\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3d\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\ +\0\xaf\x01\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x02\0\0\0\x57\0\0\0\x01\0\ +\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\ +\x3e\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x01\0\0\0\0\0\0\xbf\x50\ +\0\0\0\0\0\0\x77\0\0\0\x01\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\ +\x03\0\0\0\0\0\0\x57\x04\0\0\x01\0\0\0\x87\x04\0\0\0\0\0\0\x5f\x34\0\0\0\0\0\0\ +\xaf\x41\0\0\0\0\0\0\x57\x05\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x53\0\0\0\ +\0\0\0\x07\x02\0\0\x01\0\0\0\xbf\x15\0\0\0\0\0\0\x15\x02\x01\0\x24\0\0\0\x05\0\ +\xa9\xff\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x15\x01\x0c\0\0\0\ +\0\0\x71\x62\x06\0\0\0\0\0\x71\x63\x07\0\0\0\0\0\x67\x03\0\0\x08\0\0\0\x4f\x23\ +\0\0\0\0\0\0\x9f\x31\0\0\0\0\0\0\x63\x1a\x50\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\ +\x07\x02\0\0\x50\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x01\ +\0\0\0\x55\0\x05\0\0\0\0\0\x71\x61\x08\0\0\0\0\0\x71\x60\x09\0\0\0\0\0\x67\0\0\ +\0\x08\0\0\0\x4f\x10\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\x69\0\0\0\0\0\0\0\x05\0\xfd\ +\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\x47\x50\x4c\x20\x76\x32\0\0\x9f\xeb\x01\0\x18\0\0\0\0\0\0\0\ +\x58\x05\0\0\x58\x05\0\0\x85\x11\0\0\0\0\0\0\0\0\0\x02\x03\0\0\0\x01\0\0\0\0\0\ +\0\x01\x04\0\0\0\x20\0\0\x01\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x02\ +\0\0\0\x05\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\x02\x06\0\0\0\0\0\ +\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\x02\x08\0\0\ +\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x0a\0\0\0\0\0\0\0\0\0\0\x02\ +\x0a\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x01\0\0\0\0\0\0\0\0\0\ +\0\x02\x0c\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\0\x04\0\0\0\0\0\ +\0\x05\0\0\x04\x28\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\ +\0\0\x27\0\0\0\x07\0\0\0\x80\0\0\0\x32\0\0\0\x09\0\0\0\xc0\0\0\0\x3e\0\0\0\x0b\ +\0\0\0\0\x01\0\0\x48\0\0\0\0\0\0\x0e\x0d\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x10\ +\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x28\0\0\0\0\0\0\0\x05\0\0\ +\x04\x28\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\0\x27\0\ +\0\0\x0f\0\0\0\x80\0\0\0\x32\0\0\0\x09\0\0\0\xc0\0\0\0\x3e\0\0\0\x0b\0\0\0\0\ +\x01\0\0\x63\0\0\0\0\0\0\x0e\x11\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x14\0\0\0\0\ +\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x80\0\0\0\0\0\0\0\x05\0\0\x04\x28\ +\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\x40\0\0\0\x27\0\0\0\x01\ +\0\0\0\x80\0\0\0\x32\0\0\0\x13\0\0\0\xc0\0\0\0\x3e\0\0\0\x0b\0\0\0\0\x01\0\0\ +\x7c\0\0\0\0\0\0\x0e\x15\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x18\0\0\0\x9a\0\0\0\ +\x22\0\0\x04\xc0\0\0\0\xa4\0\0\0\x19\0\0\0\0\0\0\0\xa8\0\0\0\x19\0\0\0\x20\0\0\ +\0\xb1\0\0\0\x19\0\0\0\x40\0\0\0\xb6\0\0\0\x19\0\0\0\x60\0\0\0\xc4\0\0\0\x19\0\ +\0\0\x80\0\0\0\xcd\0\0\0\x19\0\0\0\xa0\0\0\0\xda\0\0\0\x19\0\0\0\xc0\0\0\0\xe3\ +\0\0\0\x19\0\0\0\xe0\0\0\0\xee\0\0\0\x19\0\0\0\0\x01\0\0\xf7\0\0\0\x19\0\0\0\ +\x20\x01\0\0\x07\x01\0\0\x19\0\0\0\x40\x01\0\0\x0f\x01\0\0\x19\0\0\0\x60\x01\0\ +\0\x18\x01\0\0\x1b\0\0\0\x80\x01\0\0\x1b\x01\0\0\x19\0\0\0\x20\x02\0\0\x20\x01\ +\0\0\x19\0\0\0\x40\x02\0\0\x2b\x01\0\0\x19\0\0\0\x60\x02\0\0\x30\x01\0\0\x19\0\ +\0\0\x80\x02\0\0\x39\x01\0\0\x19\0\0\0\xa0\x02\0\0\x41\x01\0\0\x19\0\0\0\xc0\ +\x02\0\0\x48\x01\0\0\x19\0\0\0\xe0\x02\0\0\x53\x01\0\0\x19\0\0\0\0\x03\0\0\x5d\ +\x01\0\0\x1c\0\0\0\x20\x03\0\0\x68\x01\0\0\x1c\0\0\0\xa0\x03\0\0\x72\x01\0\0\ +\x19\0\0\0\x20\x04\0\0\x7e\x01\0\0\x19\0\0\0\x40\x04\0\0\x89\x01\0\0\x19\0\0\0\ +\x60\x04\0\0\0\0\0\0\x1d\0\0\0\x80\x04\0\0\x93\x01\0\0\x1f\0\0\0\xc0\x04\0\0\ +\x9a\x01\0\0\x19\0\0\0\0\x05\0\0\xa3\x01\0\0\x19\0\0\0\x20\x05\0\0\0\0\0\0\x21\ +\0\0\0\x40\x05\0\0\xac\x01\0\0\x19\0\0\0\x80\x05\0\0\xb5\x01\0\0\x23\0\0\0\xa0\ +\x05\0\0\xc1\x01\0\0\x1f\0\0\0\xc0\x05\0\0\xca\x01\0\0\0\0\0\x08\x1a\0\0\0\xd0\ +\x01\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x19\0\0\0\x04\ +\0\0\0\x05\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x19\0\0\0\x04\0\0\0\x04\0\0\0\0\0\0\ +\0\x01\0\0\x05\x08\0\0\0\xdd\x01\0\0\x1e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\x2c\0\ +\0\0\xe7\x01\0\0\0\0\0\x08\x20\0\0\0\xed\x01\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\0\ +\0\0\0\0\x01\0\0\x05\x08\0\0\0\0\x02\0\0\x22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\ +\x2d\0\0\0\x03\x02\0\0\0\0\0\x08\x24\0\0\0\x08\x02\0\0\0\0\0\x01\x01\0\0\0\x08\ +\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x16\x02\0\0\x17\0\0\0\x1a\x02\0\0\x01\0\0\ +\x0c\x25\0\0\0\x52\x11\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\x01\0\0\0\0\0\0\0\x03\0\ +\0\0\0\x27\0\0\0\x04\0\0\0\x07\0\0\0\x57\x11\0\0\0\0\0\x0e\x28\0\0\0\x01\0\0\0\ +\x60\x11\0\0\x03\0\0\x0f\0\0\0\0\x0e\0\0\0\0\0\0\0\x28\0\0\0\x12\0\0\0\0\0\0\0\ +\x28\0\0\0\x16\0\0\0\0\0\0\0\x28\0\0\0\x66\x11\0\0\x01\0\0\x0f\0\0\0\0\x29\0\0\ +\0\0\0\0\0\x07\0\0\0\x6e\x11\0\0\0\0\0\x07\0\0\0\0\x7c\x11\0\0\0\0\0\x07\0\0\0\ +\0\0\x69\x6e\x74\0\x5f\x5f\x41\x52\x52\x41\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\ +\x50\x45\x5f\x5f\0\x74\x79\x70\x65\0\x6b\x65\x79\x5f\x73\x69\x7a\x65\0\x76\x61\ +\x6c\x75\x65\x5f\x73\x69\x7a\x65\0\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\ +\0\x6d\x61\x70\x5f\x66\x6c\x61\x67\x73\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\ +\x61\x70\x5f\x63\x6f\x6e\x66\x69\x67\x75\x72\x61\x74\x69\x6f\x6e\x73\0\x74\x61\ +\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x74\x6f\x65\x70\x6c\x69\x74\x7a\x5f\ +\x6b\x65\x79\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x69\x6e\x64\x69\ +\x72\x65\x63\x74\x69\x6f\x6e\x5f\x74\x61\x62\x6c\x65\0\x5f\x5f\x73\x6b\x5f\x62\ +\x75\x66\x66\0\x6c\x65\x6e\0\x70\x6b\x74\x5f\x74\x79\x70\x65\0\x6d\x61\x72\x6b\ +\0\x71\x75\x65\x75\x65\x5f\x6d\x61\x70\x70\x69\x6e\x67\0\x70\x72\x6f\x74\x6f\ +\x63\x6f\x6c\0\x76\x6c\x61\x6e\x5f\x70\x72\x65\x73\x65\x6e\x74\0\x76\x6c\x61\ +\x6e\x5f\x74\x63\x69\0\x76\x6c\x61\x6e\x5f\x70\x72\x6f\x74\x6f\0\x70\x72\x69\ +\x6f\x72\x69\x74\x79\0\x69\x6e\x67\x72\x65\x73\x73\x5f\x69\x66\x69\x6e\x64\x65\ +\x78\0\x69\x66\x69\x6e\x64\x65\x78\0\x74\x63\x5f\x69\x6e\x64\x65\x78\0\x63\x62\ +\0\x68\x61\x73\x68\0\x74\x63\x5f\x63\x6c\x61\x73\x73\x69\x64\0\x64\x61\x74\x61\ +\0\x64\x61\x74\x61\x5f\x65\x6e\x64\0\x6e\x61\x70\x69\x5f\x69\x64\0\x66\x61\x6d\ +\x69\x6c\x79\0\x72\x65\x6d\x6f\x74\x65\x5f\x69\x70\x34\0\x6c\x6f\x63\x61\x6c\ +\x5f\x69\x70\x34\0\x72\x65\x6d\x6f\x74\x65\x5f\x69\x70\x36\0\x6c\x6f\x63\x61\ +\x6c\x5f\x69\x70\x36\0\x72\x65\x6d\x6f\x74\x65\x5f\x70\x6f\x72\x74\0\x6c\x6f\ +\x63\x61\x6c\x5f\x70\x6f\x72\x74\0\x64\x61\x74\x61\x5f\x6d\x65\x74\x61\0\x74\ +\x73\x74\x61\x6d\x70\0\x77\x69\x72\x65\x5f\x6c\x65\x6e\0\x67\x73\x6f\x5f\x73\ +\x65\x67\x73\0\x67\x73\x6f\x5f\x73\x69\x7a\x65\0\x74\x73\x74\x61\x6d\x70\x5f\ +\x74\x79\x70\x65\0\x68\x77\x74\x73\x74\x61\x6d\x70\0\x5f\x5f\x75\x33\x32\0\x75\ +\x6e\x73\x69\x67\x6e\x65\x64\x20\x69\x6e\x74\0\x66\x6c\x6f\x77\x5f\x6b\x65\x79\ +\x73\0\x5f\x5f\x75\x36\x34\0\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x6c\x6f\x6e\ +\x67\x20\x6c\x6f\x6e\x67\0\x73\x6b\0\x5f\x5f\x75\x38\0\x75\x6e\x73\x69\x67\x6e\ +\x65\x64\x20\x63\x68\x61\x72\0\x73\x6b\x62\0\x74\x75\x6e\x5f\x72\x73\x73\x5f\ +\x73\x74\x65\x65\x72\x69\x6e\x67\x5f\x70\x72\x6f\x67\0\x73\x6f\x63\x6b\x65\x74\ +\0\x2f\x68\x6f\x6d\x65\x2f\x61\x6e\x64\x2f\x53\x52\x43\x53\x2f\x71\x65\x6d\x75\ +\x2f\x74\x6f\x6f\x6c\x73\x2f\x65\x62\x70\x66\x2f\x72\x73\x73\x2e\x62\x70\x66\ +\x2e\x63\0\x69\x6e\x74\x20\x74\x75\x6e\x5f\x72\x73\x73\x5f\x73\x74\x65\x65\x72\ +\x69\x6e\x67\x5f\x70\x72\x6f\x67\x28\x73\x74\x72\x75\x63\x74\x20\x5f\x5f\x73\ +\x6b\x5f\x62\x75\x66\x66\x20\x2a\x73\x6b\x62\x29\0\x20\x20\x20\x20\x5f\x5f\x75\ +\x33\x32\x20\x6b\x65\x79\x20\x3d\x20\x30\x3b\0\x20\x20\x20\x20\x63\x6f\x6e\x66\ +\x69\x67\x20\x3d\x20\x62\x70\x66\x5f\x6d\x61\x70\x5f\x6c\x6f\x6f\x6b\x75\x70\ +\x5f\x65\x6c\x65\x6d\x28\x26\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\ +\x63\x6f\x6e\x66\x69\x67\x75\x72\x61\x74\x69\x6f\x6e\x73\x2c\x20\x26\x6b\x65\ +\x79\x29\x3b\0\x20\x20\x20\x20\x74\x6f\x65\x20\x3d\x20\x62\x70\x66\x5f\x6d\x61\ +\x70\x5f\x6c\x6f\x6f\x6b\x75\x70\x5f\x65\x6c\x65\x6d\x28\x26\x74\x61\x70\x5f\ +\x72\x73\x73\x5f\x6d\x61\x70\x5f\x74\x6f\x65\x70\x6c\x69\x74\x7a\x5f\x6b\x65\ +\x79\x2c\x20\x26\x6b\x65\x79\x29\x3b\0\x20\x20\x20\x20\x69\x66\x20\x28\x63\x6f\ +\x6e\x66\x69\x67\x20\x26\x26\x20\x74\x6f\x65\x29\x20\x7b\0\x20\x20\x20\x20\x20\ +\x20\x20\x20\x69\x66\x20\x28\x21\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x72\x65\x64\ +\x69\x72\x65\x63\x74\x29\x20\x7b\0\x20\x20\x20\x20\x5f\x5f\x75\x38\x20\x72\x73\ +\x73\x5f\x69\x6e\x70\x75\x74\x5b\x48\x41\x53\x48\x5f\x43\x41\x4c\x43\x55\x4c\ +\x41\x54\x49\x4f\x4e\x5f\x42\x55\x46\x46\x45\x52\x5f\x53\x49\x5a\x45\x5d\x20\ +\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x70\x61\x63\ +\x6b\x65\x74\x5f\x68\x61\x73\x68\x5f\x69\x6e\x66\x6f\x5f\x74\x20\x70\x61\x63\ +\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x69\ +\x66\x20\x28\x21\x69\x6e\x66\x6f\x20\x7c\x7c\x20\x21\x73\x6b\x62\x29\x20\x7b\0\ +\x20\x20\x20\x20\x5f\x5f\x62\x65\x31\x36\x20\x72\x65\x74\x20\x3d\x20\x30\x3b\0\ +\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\ +\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\ +\x73\x6b\x62\x2c\x20\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x72\x65\x74\x2c\x20\ +\x73\x69\x7a\x65\x6f\x66\x28\x72\x65\x74\x29\x2c\0\x20\x20\x20\x20\x69\x66\x20\ +\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x73\x77\x69\x74\x63\x68\x20\x28\ +\x62\x70\x66\x5f\x6e\x74\x6f\x68\x73\x28\x72\x65\x74\x29\x29\x20\x7b\0\x20\x20\ +\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\ +\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\ +\x65\x28\x73\x6b\x62\x2c\x20\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x72\x65\x74\ +\x2c\x20\x73\x69\x7a\x65\x6f\x66\x28\x72\x65\x74\x29\x2c\0\x20\x20\x20\x20\x72\ +\x65\x74\x75\x72\x6e\x20\x72\x65\x74\x3b\0\x20\x20\x20\x20\x69\x66\x20\x28\x6c\ +\x33\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x3d\x20\x30\x29\x20\x7b\0\x20\ +\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\x70\x76\ +\x34\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x72\x75\x63\ +\x74\x20\x69\x70\x68\x64\x72\x20\x69\x70\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\ +\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\ +\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\ +\x28\x73\x6b\x62\x2c\x20\x30\x2c\x20\x26\x69\x70\x2c\x20\x73\x69\x7a\x65\x6f\ +\x66\x28\x69\x70\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x65\ +\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\ +\x69\x73\x5f\x66\x72\x61\x67\x6d\x65\x6e\x74\x65\x64\x20\x3d\x20\x21\x21\x28\ +\x62\x70\x66\x5f\x6e\x74\x6f\x68\x73\x28\x69\x70\x2e\x66\x72\x61\x67\x5f\x6f\ +\x66\x66\x29\x20\x26\x20\x28\x30\x78\x32\x30\x30\x30\x20\x7c\x20\x30\x78\x31\ +\x66\x66\x66\x29\x29\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\ +\x3e\x69\x6e\x5f\x73\x72\x63\x20\x3d\x20\x69\x70\x2e\x73\x61\x64\x64\x72\x3b\0\ +\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x6e\x5f\x64\x73\ +\x74\x20\x3d\x20\x69\x70\x2e\x64\x61\x64\x64\x72\x3b\0\x20\x20\x20\x20\x20\x20\ +\x20\x20\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x20\x69\x70\x2e\ +\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x6c\x34\ +\x5f\x6f\x66\x66\x73\x65\x74\x20\x3d\x20\x69\x70\x2e\x69\x68\x6c\x20\x2a\x20\ +\x34\x3b\0\x20\x20\x20\x20\x69\x66\x20\x28\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\ +\x6f\x6c\x20\x21\x3d\x20\x30\x20\x26\x26\x20\x21\x69\x6e\x66\x6f\x2d\x3e\x69\ +\x73\x5f\x66\x72\x61\x67\x6d\x65\x6e\x74\x65\x64\x29\x20\x7b\0\x20\x20\x20\x20\ +\x20\x20\x20\x20\x69\x66\x20\x28\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\ +\x20\x3d\x3d\x20\x49\x50\x50\x52\x4f\x54\x4f\x5f\x54\x43\x50\x29\x20\x7b\0\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\ +\x5f\x74\x63\x70\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x73\x74\x72\x75\x63\x74\x20\x74\x63\x70\x68\x64\x72\x20\x74\x63\x70\ +\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\ +\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\ +\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\ +\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x74\x63\x70\x2c\x20\x73\x69\ +\x7a\x65\x6f\x66\x28\x74\x63\x70\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x69\x66\x20\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x69\x66\ +\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x69\x70\ +\x76\x34\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x70\x61\ +\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x74\x63\x70\x20\x26\x26\0\ +\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\x70\ +\x76\x36\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x72\x75\ +\x63\x74\x20\x69\x70\x76\x36\x68\x64\x72\x20\x69\x70\x36\x20\x3d\x20\x7b\x7d\ +\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\ +\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\ +\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x30\x2c\x20\x26\x69\x70\x36\x2c\x20\ +\x73\x69\x7a\x65\x6f\x66\x28\x69\x70\x36\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\ +\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x6e\x36\x5f\x73\x72\x63\x20\x3d\x20\x69\x70\ +\x36\x2e\x73\x61\x64\x64\x72\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\ +\x6f\x2d\x3e\x69\x6e\x36\x5f\x64\x73\x74\x20\x3d\x20\x69\x70\x36\x2e\x64\x61\ +\x64\x64\x72\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x6c\x34\x5f\x70\x72\x6f\x74\ +\x6f\x63\x6f\x6c\x20\x3d\x20\x69\x70\x36\x2e\x6e\x65\x78\x74\x68\x64\x72\x3b\0\ +\x20\x20\x20\x20\x73\x77\x69\x74\x63\x68\x20\x28\x68\x64\x72\x5f\x74\x79\x70\ +\x65\x29\x20\x7b\0\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x69\x70\x76\x36\ +\x5f\x6f\x70\x74\x5f\x68\x64\x72\x20\x65\x78\x74\x5f\x68\x64\x72\x20\x3d\x20\ +\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\ +\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\ +\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x2a\x6c\x34\x5f\x6f\x66\x66\ +\x73\x65\x74\x2c\x20\x26\x65\x78\x74\x5f\x68\x64\x72\x2c\0\x20\x20\x20\x20\x20\ +\x20\x20\x20\x69\x66\x20\x28\x2a\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\ +\x20\x3d\x3d\x20\x49\x50\x50\x52\x4f\x54\x4f\x5f\x52\x4f\x55\x54\x49\x4e\x47\ +\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x72\x75\ +\x63\x74\x20\x69\x70\x76\x36\x5f\x72\x74\x5f\x68\x64\x72\x20\x65\x78\x74\x5f\ +\x72\x74\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\ +\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\ +\x2c\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x65\x78\x74\x5f\ +\x72\x74\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\ +\x28\x65\x78\x74\x5f\x72\x74\x2e\x74\x79\x70\x65\x20\x3d\x3d\x20\x49\x50\x56\ +\x36\x5f\x53\x52\x43\x52\x54\x5f\x54\x59\x50\x45\x5f\x32\x29\x20\x26\x26\0\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x20\x6f\x66\x66\x73\x65\x74\ +\x6f\x66\x28\x73\x74\x72\x75\x63\x74\x20\x72\x74\x32\x5f\x68\x64\x72\x2c\x20\ +\x61\x64\x64\x72\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\ +\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\ +\x6b\x62\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x69\x66\x20\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\x70\x76\ +\x36\x5f\x65\x78\x74\x5f\x64\x73\x74\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x5f\x5f\x61\x74\x74\x72\x69\x62\x75\x74\ +\x65\x5f\x5f\x28\x28\x70\x61\x63\x6b\x65\x64\x29\x29\x20\x6f\x70\x74\x20\x3d\ +\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x3d\x20\x28\x6f\x70\x74\ +\x2e\x74\x79\x70\x65\x20\x3d\x3d\x20\x49\x50\x56\x36\x5f\x54\x4c\x56\x5f\x50\ +\x41\x44\x31\x29\x20\x3f\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x69\x66\x20\x28\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\ +\x20\x31\x20\x3e\x3d\x20\x65\x78\x74\x5f\x68\x64\x72\x2e\x68\x64\x72\x6c\x65\ +\x6e\x20\x2a\x20\x38\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\ +\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\ +\x28\x73\x6b\x62\x2c\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x20\ +\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x6f\x70\x74\x2e\x74\x79\x70\ +\x65\x20\x3d\x3d\x20\x49\x50\x56\x36\x5f\x54\x4c\x56\x5f\x48\x41\x4f\x29\x20\ +\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x20\ +\x6f\x70\x74\x5f\x6f\x66\x66\x73\x65\x74\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\ +\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\ +\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x65\x72\x72\ +\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\x70\x76\x36\x5f\x65\ +\x78\x74\x5f\x73\x72\x63\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x66\x72\x61\x67\x6d\x65\ +\x6e\x74\x65\x64\x20\x3d\x20\x74\x72\x75\x65\x3b\0\x20\x20\x20\x20\x20\x20\x20\ +\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x3d\x20\x28\x65\x78\x74\ +\x5f\x68\x64\x72\x2e\x68\x64\x72\x6c\x65\x6e\x20\x2b\x20\x31\x29\x20\x2a\x20\ +\x38\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\x70\x72\x6f\x74\x6f\ +\x63\x6f\x6c\x20\x3d\x20\x65\x78\x74\x5f\x68\x64\x72\x2e\x6e\x65\x78\x74\x68\ +\x64\x72\x3b\0\x20\x20\x20\x20\x66\x6f\x72\x20\x28\x75\x6e\x73\x69\x67\x6e\x65\ +\x64\x20\x69\x6e\x74\x20\x69\x20\x3d\x20\x30\x3b\x20\x69\x20\x3c\x20\x49\x50\ +\x36\x5f\x45\x58\x54\x45\x4e\x53\x49\x4f\x4e\x53\x5f\x43\x4f\x55\x4e\x54\x3b\ +\x20\x2b\x2b\x69\x29\x20\x7b\0\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\ +\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x69\ +\x70\x76\x36\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\ +\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x69\ +\x70\x76\x36\x5f\x65\x78\x74\x5f\x64\x73\x74\x20\x26\x26\0\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\ +\x6e\x66\x6f\x2e\x69\x73\x5f\x69\x70\x76\x36\x5f\x65\x78\x74\x5f\x73\x72\x63\ +\x20\x26\x26\0\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\ +\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x75\ +\x64\x70\x20\x26\x26\0\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\ +\x20\x69\x66\x20\x28\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x68\x61\x73\x68\x5f\x74\ +\x79\x70\x65\x73\x20\x26\x20\x56\x49\x52\x54\x49\x4f\x5f\x4e\x45\x54\x5f\x52\ +\x53\x53\x5f\x48\x41\x53\x48\x5f\x54\x59\x50\x45\x5f\x49\x50\x76\x34\x29\x20\ +\x7b\0\x20\x20\x20\x20\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x5f\x6d\x65\x6d\x63\ +\x70\x79\x28\x26\x72\x73\x73\x5f\x69\x6e\x70\x75\x74\x5b\x2a\x62\x79\x74\x65\ +\x73\x5f\x77\x72\x69\x74\x74\x65\x6e\x5d\x2c\x20\x70\x74\x72\x2c\x20\x73\x69\ +\x7a\x65\x29\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\ +\x6f\x2d\x3e\x69\x73\x5f\x75\x64\x70\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x75\x64\x70\x68\x64\ +\x72\x20\x75\x64\x70\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\ +\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\ +\x73\x6b\x62\x2c\x20\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x75\x64\ +\x70\x2c\x20\x73\x69\x7a\x65\x6f\x66\x28\x75\x64\x70\x29\x2c\0\x20\x20\x20\x20\ +\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\x66\x20\x28\x63\x6f\x6e\x66\ +\x69\x67\x2d\x3e\x68\x61\x73\x68\x5f\x74\x79\x70\x65\x73\x20\x26\x20\x56\x49\ +\x52\x54\x49\x4f\x5f\x4e\x45\x54\x5f\x52\x53\x53\x5f\x48\x41\x53\x48\x5f\x54\ +\x59\x50\x45\x5f\x49\x50\x76\x36\x29\x20\x7b\0\x20\x20\x20\x20\x66\x6f\x72\x20\ +\x28\x62\x79\x74\x65\x20\x3d\x20\x30\x3b\x20\x62\x79\x74\x65\x20\x3c\x20\x48\ +\x41\x53\x48\x5f\x43\x41\x4c\x43\x55\x4c\x41\x54\x49\x4f\x4e\x5f\x42\x55\x46\ +\x46\x45\x52\x5f\x53\x49\x5a\x45\x3b\x20\x62\x79\x74\x65\x2b\x2b\x29\x20\x7b\0\ +\x20\x20\x20\x20\x5f\x5f\x75\x33\x32\x20\x6c\x65\x66\x74\x6d\x6f\x73\x74\x5f\ +\x33\x32\x5f\x62\x69\x74\x73\x20\x3d\x20\x6b\x65\x79\x2d\x3e\x6c\x65\x66\x74\ +\x6d\x6f\x73\x74\x5f\x33\x32\x5f\x62\x69\x74\x73\x3b\0\x20\x20\x20\x20\x20\x20\ +\x20\x20\x5f\x5f\x75\x38\x20\x69\x6e\x70\x75\x74\x5f\x62\x79\x74\x65\x20\x3d\ +\x20\x69\x6e\x70\x75\x74\x5b\x62\x79\x74\x65\x5d\x3b\0\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x69\x6e\x70\x75\x74\x5f\x62\x79\x74\ +\x65\x20\x26\x20\x28\x31\x20\x3c\x3c\x20\x37\x29\x29\x20\x7b\0\x20\x20\x20\x20\ +\x20\x20\x20\x20\x5f\x5f\x75\x38\x20\x6b\x65\x79\x5f\x62\x79\x74\x65\x20\x3d\ +\x20\x6b\x65\x79\x2d\x3e\x6e\x65\x78\x74\x5f\x62\x79\x74\x65\x5b\x62\x79\x74\ +\x65\x5d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x28\x6c\x65\x66\x74\x6d\x6f\x73\x74\x5f\x33\x32\x5f\x62\x69\ +\x74\x73\x20\x3c\x3c\x20\x31\x29\x20\x7c\x20\x28\x28\x6b\x65\x79\x5f\x62\x79\ +\x74\x65\x20\x26\x20\x28\x31\x20\x3c\x3c\x20\x37\x29\x29\x20\x3e\x3e\x20\x37\ +\x29\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x68\x61\x73\x68\x29\ +\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x5f\x5f\x75\x33\x32\ +\x20\x74\x61\x62\x6c\x65\x5f\x69\x64\x78\x20\x3d\x20\x68\x61\x73\x68\x20\x25\ +\x20\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x69\x6e\x64\x69\x72\x65\x63\x74\x69\x6f\ +\x6e\x73\x5f\x6c\x65\x6e\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x71\x75\x65\x75\x65\x20\x3d\x20\x62\x70\x66\x5f\x6d\x61\x70\x5f\x6c\x6f\x6f\ +\x6b\x75\x70\x5f\x65\x6c\x65\x6d\x28\x26\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\ +\x61\x70\x5f\x69\x6e\x64\x69\x72\x65\x63\x74\x69\x6f\x6e\x5f\x74\x61\x62\x6c\ +\x65\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x71\ +\x75\x65\x75\x65\x29\x20\x7b\0\x7d\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x72\x65\x74\x75\x72\x6e\x20\x2a\x71\x75\x65\x75\x65\ +\x3b\0\x63\x68\x61\x72\0\x5f\x6c\x69\x63\x65\x6e\x73\x65\0\x2e\x6d\x61\x70\x73\ +\0\x6c\x69\x63\x65\x6e\x73\x65\0\x62\x70\x66\x5f\x66\x6c\x6f\x77\x5f\x6b\x65\ +\x79\x73\0\x62\x70\x66\x5f\x73\x6f\x63\x6b\0\0\0\0\x9f\xeb\x01\0\x20\0\0\0\0\0\ +\0\0\x14\0\0\0\x14\0\0\0\xbc\x0c\0\0\xd0\x0c\0\0\0\0\0\0\x08\0\0\0\x30\x02\0\0\ +\x01\0\0\0\0\0\0\0\x26\0\0\0\x10\0\0\0\x30\x02\0\0\xcb\0\0\0\0\0\0\0\x37\x02\0\ +\0\x60\x02\0\0\0\x5c\x08\0\x10\0\0\0\x37\x02\0\0\x91\x02\0\0\x0b\x74\x08\0\x20\ +\0\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x28\0\0\0\x37\x02\0\0\xa4\x02\0\0\x0e\x80\ +\x08\0\x50\0\0\0\x37\x02\0\0\xe9\x02\0\0\x0b\x84\x08\0\x88\0\0\0\x37\x02\0\0\ +\x29\x03\0\0\x10\x8c\x08\0\x90\0\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x98\0\0\0\x37\ +\x02\0\0\x29\x03\0\0\x10\x8c\x08\0\xa0\0\0\0\x37\x02\0\0\x42\x03\0\0\x16\x90\ +\x08\0\xa8\0\0\0\x37\x02\0\0\x42\x03\0\0\x0d\x90\x08\0\xc0\0\0\0\x37\x02\0\0\ +\x63\x03\0\0\x0a\x08\x06\0\xe8\0\0\0\x37\x02\0\0\x9a\x03\0\0\x1f\x18\x06\0\x38\ +\x01\0\0\x37\x02\0\0\xca\x03\0\0\x0f\xac\x04\0\x40\x01\0\0\x37\x02\0\0\xe3\x03\ +\0\0\x0c\x2c\x04\0\x50\x01\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x58\x01\0\0\x37\x02\ +\0\0\xf7\x03\0\0\x0b\x38\x04\0\x80\x01\0\0\x37\x02\0\0\x3d\x04\0\0\x09\x40\x04\ +\0\x90\x01\0\0\x37\x02\0\0\x3d\x04\0\0\x09\x40\x04\0\xa0\x01\0\0\x37\x02\0\0\ +\x4c\x04\0\0\x0d\x50\x04\0\xb8\x01\0\0\x37\x02\0\0\x4c\x04\0\0\x05\x50\x04\0\ +\xd8\x01\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\xe0\x01\0\0\x37\x02\0\0\x6a\x04\0\0\ +\x0f\x64\x04\0\0\x02\0\0\x37\x02\0\0\x3d\x04\0\0\x09\x7c\x04\0\x10\x02\0\0\x37\ +\x02\0\0\x3d\x04\0\0\x09\x7c\x04\0\x18\x02\0\0\x37\x02\0\0\xb4\x04\0\0\x0c\x8c\ +\x04\0\x20\x02\0\0\x37\x02\0\0\xc4\x04\0\0\x09\xc8\x04\0\x48\x02\0\0\x37\x02\0\ +\0\xe0\x04\0\0\x17\xe0\x04\0\x58\x02\0\0\x37\x02\0\0\xfb\x04\0\0\x16\xe8\x04\0\ +\x78\x02\0\0\x37\x02\0\0\xe0\x04\0\0\x17\xe0\x04\0\x80\x02\0\0\x37\x02\0\0\x19\ +\x05\0\0\x0f\xec\x04\0\xa8\x02\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\xf4\x04\0\xb8\ +\x02\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\xf4\x04\0\xc0\x02\0\0\x37\x02\0\0\x6f\x05\ +\0\0\x22\x0c\x05\0\xc8\x02\0\0\x37\x02\0\0\x6f\x05\0\0\x39\x0c\x05\0\xd8\x02\0\ +\0\x37\x02\0\0\x6f\x05\0\0\x20\x0c\x05\0\xe8\x02\0\0\x37\x02\0\0\xbd\x05\0\0\ +\x1b\x04\x05\0\xf0\x02\0\0\x37\x02\0\0\xbd\x05\0\0\x16\x04\x05\0\xf8\x02\0\0\ +\x37\x02\0\0\xde\x05\0\0\x1b\x08\x05\0\0\x03\0\0\x37\x02\0\0\xde\x05\0\0\x16\ +\x08\x05\0\x08\x03\0\0\x37\x02\0\0\xff\x05\0\0\x1a\x14\x05\0\x10\x03\0\0\x37\ +\x02\0\0\x22\x06\0\0\x18\x18\x05\0\x18\x03\0\0\x37\x02\0\0\x22\x06\0\0\x1c\x18\ +\x05\0\x30\x03\0\0\x37\x02\0\0\x6f\x05\0\0\x1d\x0c\x05\0\x38\x03\0\0\x37\x02\0\ +\0\x42\x06\0\0\x15\x74\x05\0\x48\x03\0\0\x37\x02\0\0\x42\x06\0\0\x1a\x74\x05\0\ +\x60\x03\0\0\x37\x02\0\0\x76\x06\0\0\x0d\x78\x05\0\x80\x03\0\0\x37\x02\0\0\xa0\ +\x06\0\0\x1a\x7c\x05\0\x90\x03\0\0\x37\x02\0\0\xbe\x06\0\0\x1b\x84\x05\0\xb0\ +\x03\0\0\x37\x02\0\0\xa0\x06\0\0\x1a\x7c\x05\0\xb8\x03\0\0\x37\x02\0\0\xe2\x06\ +\0\0\x13\x88\x05\0\xe0\x03\0\0\x37\x02\0\0\x33\x07\0\0\x11\x90\x05\0\xf0\x03\0\ +\0\x37\x02\0\0\x33\x07\0\0\x11\x90\x05\0\xf8\x03\0\0\x37\x02\0\0\0\0\0\0\0\0\0\ +\0\x18\x04\0\0\x37\x02\0\0\x4a\x07\0\0\x15\x34\x06\0\x20\x04\0\0\x37\x02\0\0\ +\x4a\x07\0\0\x09\x34\x06\0\x28\x04\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x78\x04\0\0\ +\x37\x02\0\0\x69\x07\0\0\x19\x38\x06\0\x80\x04\0\0\x37\x02\0\0\x69\x07\0\0\x20\ +\x38\x06\0\xa0\x04\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\xf0\x04\0\0\x37\x02\0\0\x8b\ +\x07\0\0\x17\x20\x05\0\0\x05\0\0\x37\x02\0\0\xa6\x07\0\0\x18\x28\x05\0\x30\x05\ +\0\0\x37\x02\0\0\x8b\x07\0\0\x17\x20\x05\0\x48\x05\0\0\x37\x02\0\0\xc7\x07\0\0\ +\x0f\x2c\x05\0\x70\x05\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\x34\x05\0\x80\x05\0\0\ +\x37\x02\0\0\x5c\x05\0\0\x0d\x34\x05\0\x88\x05\0\0\x37\x02\0\0\x0c\x08\0\0\x1d\ +\x44\x05\0\xc8\x05\0\0\x37\x02\0\0\x2f\x08\0\0\x1d\x48\x05\0\x08\x06\0\0\x37\ +\x02\0\0\x52\x08\0\0\x1b\x50\x05\0\x10\x06\0\0\x37\x02\0\0\x75\x08\0\0\x05\x3c\ +\x02\0\x58\x06\0\0\x37\x02\0\0\x8d\x08\0\0\x19\xc4\x02\0\xc8\x06\0\0\x37\x02\0\ +\0\0\0\0\0\0\0\0\0\xd0\x06\0\0\x37\x02\0\0\xb3\x08\0\0\x0f\xd4\x02\0\xf8\x06\0\ +\0\x37\x02\0\0\x5c\x05\0\0\x0d\xdc\x02\0\x10\x07\0\0\x37\x02\0\0\x5c\x05\0\0\ +\x0d\xdc\x02\0\x18\x07\0\0\x37\x02\0\0\xf8\x08\0\0\x0d\xec\x02\0\x38\x07\0\0\ +\x37\x02\0\0\x27\x09\0\0\x20\xf0\x02\0\x60\x07\0\0\x37\x02\0\0\x53\x09\0\0\x13\ +\xf8\x02\0\x88\x07\0\0\x37\x02\0\0\x33\x07\0\0\x11\0\x03\0\xa0\x07\0\0\x37\x02\ +\0\0\x33\x07\0\0\x11\0\x03\0\xa8\x07\0\0\x37\x02\0\0\x9b\x09\0\0\x19\x10\x03\0\ +\xb0\x07\0\0\x37\x02\0\0\x9b\x09\0\0\x34\x10\x03\0\xd8\x07\0\0\x37\x02\0\0\xd1\ +\x09\0\0\x15\x24\x03\0\xe8\x07\0\0\x37\x02\0\0\x12\x0a\0\0\x17\x20\x03\0\x10\ +\x08\0\0\x37\x02\0\0\x49\x0a\0\0\x15\x30\x03\0\x28\x08\0\0\x37\x02\0\0\x49\x0a\ +\0\0\x15\x30\x03\0\x30\x08\0\0\x37\x02\0\0\x64\x0a\0\0\x27\x40\x03\0\x58\x08\0\ +\0\x37\x02\0\0\x8f\x0a\0\0\x27\x5c\x03\0\x68\x08\0\0\x37\x02\0\0\xbf\x0a\0\0\ +\x1c\xc0\x03\0\x70\x08\0\0\x37\x02\0\0\xfb\x0a\0\0\x20\xcc\x03\0\x80\x08\0\0\ +\x37\x02\0\0\xfb\x0a\0\0\x2f\xcc\x03\0\x88\x08\0\0\x37\x02\0\0\xfb\x0a\0\0\x36\ +\xcc\x03\0\x90\x08\0\0\x37\x02\0\0\xfb\x0a\0\0\x15\xcc\x03\0\xf8\x08\0\0\x37\ +\x02\0\0\x37\x0b\0\0\x43\x70\x03\0\x18\x09\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x20\ +\x09\0\0\x37\x02\0\0\x37\x0b\0\0\x17\x70\x03\0\x48\x09\0\0\x37\x02\0\0\x49\x0a\ +\0\0\x15\x78\x03\0\x60\x09\0\0\x37\x02\0\0\x49\x0a\0\0\x15\x78\x03\0\x68\x09\0\ +\0\x37\x02\0\0\x87\x0b\0\0\x19\x88\x03\0\x70\x09\0\0\x37\x02\0\0\x87\x0b\0\0\ +\x15\x88\x03\0\x78\x09\0\0\x37\x02\0\0\xb7\x0b\0\0\x19\x90\x03\0\x80\x09\0\0\ +\x37\x02\0\0\xe7\x0b\0\0\x1b\x8c\x03\0\xb0\x09\0\0\x37\x02\0\0\x22\x0c\0\0\x19\ +\xa0\x03\0\xc8\x09\0\0\x37\x02\0\0\x22\x0c\0\0\x19\xa0\x03\0\xd0\x09\0\0\x37\ +\x02\0\0\x41\x0c\0\0\x2b\xb0\x03\0\xf0\x09\0\0\x37\x02\0\0\xbf\x0a\0\0\x1f\xc0\ +\x03\0\x10\x0a\0\0\x37\x02\0\0\x70\x0c\0\0\x21\xe0\x03\0\x20\x0a\0\0\x37\x02\0\ +\0\x98\x0c\0\0\x20\xf0\x03\0\x28\x0a\0\0\x37\x02\0\0\x98\x0c\0\0\x2c\xf0\x03\0\ +\x40\x0a\0\0\x37\x02\0\0\x98\x0c\0\0\x14\xf0\x03\0\x50\x0a\0\0\x37\x02\0\0\xc8\ +\x0c\0\0\x20\xec\x03\0\x58\x0a\0\0\x37\x02\0\0\x75\x08\0\0\x05\x3c\x02\0\xa0\ +\x0a\0\0\x37\x02\0\0\xf0\x0c\0\0\x38\xcc\x02\0\xc0\x0a\0\0\x37\x02\0\0\xf0\x0c\ +\0\0\x05\xcc\x02\0\xd8\x0a\0\0\x37\x02\0\0\x75\x08\0\0\x05\x3c\x02\0\xe8\x0a\0\ +\0\x37\x02\0\0\x2e\x0d\0\0\x1c\xd0\x06\0\xf0\x0a\0\0\x37\x02\0\0\x2e\x0d\0\0\ +\x10\xd0\x06\0\xf8\x0a\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x48\x0b\0\0\x37\x02\0\0\ +\x69\x07\0\0\x19\xd4\x06\0\x50\x0b\0\0\x37\x02\0\0\x69\x07\0\0\x20\xd4\x06\0\ +\x88\x0b\0\0\x37\x02\0\0\x54\x0d\0\0\x2d\x0c\x07\0\x98\x0b\0\0\x37\x02\0\0\x54\ +\x0d\0\0\x1d\x0c\x07\0\xa0\x0b\0\0\x37\x02\0\0\x54\x0d\0\0\x2d\x0c\x07\0\xb0\ +\x0b\0\0\x37\x02\0\0\x83\x0d\0\0\x2d\xe0\x06\0\xe0\x0b\0\0\x37\x02\0\0\x83\x0d\ +\0\0\x1d\xe0\x06\0\xf0\x0b\0\0\x37\x02\0\0\x83\x0d\0\0\x2d\xe0\x06\0\0\x0c\0\0\ +\x37\x02\0\0\0\0\0\0\0\0\0\0\xd0\x0c\0\0\x37\x02\0\0\xb2\x0d\0\0\x20\x74\x06\0\ +\xd8\x0c\0\0\x37\x02\0\0\xb2\x0d\0\0\x27\x74\x06\0\0\x0d\0\0\x37\x02\0\0\xdb\ +\x0d\0\0\x27\xb0\x06\0\x08\x0d\0\0\x37\x02\0\0\xdb\x0d\0\0\x14\xb0\x06\0\x10\ +\x0d\0\0\x37\x02\0\0\x24\x0e\0\0\x05\xa4\x01\0\x20\x0d\0\0\x37\x02\0\0\x24\x0e\ +\0\0\x05\xa4\x01\0\x50\x0d\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\x60\x05\0\x60\x0d\0\ +\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x70\x0d\0\0\x37\x02\0\0\xb2\x0d\0\0\x20\x50\x07\ +\0\x78\x0d\0\0\x37\x02\0\0\xb2\x0d\0\0\x27\x50\x07\0\xb0\x0d\0\0\x37\x02\0\0\ +\x54\x0d\0\0\x2d\x88\x07\0\xc0\x0d\0\0\x37\x02\0\0\x54\x0d\0\0\x1d\x88\x07\0\ +\xc8\x0d\0\0\x37\x02\0\0\x54\x0d\0\0\x2d\x88\x07\0\xd8\x0d\0\0\x37\x02\0\0\x83\ +\x0d\0\0\x2d\x5c\x07\0\x08\x0e\0\0\x37\x02\0\0\x83\x0d\0\0\x1d\x5c\x07\0\x18\ +\x0e\0\0\x37\x02\0\0\x83\x0d\0\0\x2d\x5c\x07\0\x30\x0e\0\0\x37\x02\0\0\x61\x0e\ +\0\0\x1a\xac\x05\0\x40\x0e\0\0\x37\x02\0\0\x7f\x0e\0\0\x1b\xb4\x05\0\x50\x0e\0\ +\0\x37\x02\0\0\x61\x0e\0\0\x1a\xac\x05\0\x58\x0e\0\0\x37\x02\0\0\xa3\x0e\0\0\ +\x13\xb8\x05\0\x80\x0e\0\0\x37\x02\0\0\x33\x07\0\0\x11\xc0\x05\0\x90\x0e\0\0\ +\x37\x02\0\0\x33\x07\0\0\x11\xc0\x05\0\xa0\x0e\0\0\x37\x02\0\0\x75\x08\0\0\x05\ +\x3c\x02\0\xb0\x0e\0\0\x37\x02\0\0\xf4\x0e\0\0\x27\xd4\x07\0\xc0\x0e\0\0\x37\ +\x02\0\0\xf4\x0e\0\0\x14\xd4\x07\0\xe0\x0e\0\0\x37\x02\0\0\x83\x0d\0\0\x2d\xd8\ +\x07\0\xf0\x0e\0\0\x37\x02\0\0\x83\x0d\0\0\x1d\xd8\x07\0\xf8\x0e\0\0\x37\x02\0\ +\0\x83\x0d\0\0\x2d\xd8\x07\0\x20\x0f\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x70\x0f\0\ +\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x80\x0f\0\0\x37\x02\0\0\x54\x0d\0\0\x1d\x04\x08\ +\0\x88\x0f\0\0\x37\x02\0\0\x54\x0d\0\0\x2d\x04\x08\0\x98\x0f\0\0\x37\x02\0\0\ +\x24\x0e\0\0\x05\xa4\x01\0\xe8\x0f\0\0\x37\x02\0\0\x24\x0e\0\0\x05\xa4\x01\0\ +\x20\x10\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x30\x10\0\0\x37\x02\0\0\x3d\x0f\0\0\ +\x05\xdc\x01\0\x38\x10\0\0\x37\x02\0\0\x7f\x0f\0\0\x23\xd0\x01\0\x50\x10\0\0\ +\x37\x02\0\0\0\0\0\0\0\0\0\0\x58\x10\0\0\x37\x02\0\0\xb3\x0f\0\0\x1b\xe0\x01\0\ +\x78\x10\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x90\x10\0\0\x37\x02\0\0\x03\ +\x10\0\0\x19\xe4\x01\0\xa8\x10\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\xc0\ +\x10\0\0\x37\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\xc8\x10\0\0\x37\x02\0\0\xda\x0f\ +\0\0\x11\xf4\x01\0\x08\x11\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x10\x11\0\ +\0\x37\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\x18\x11\0\0\x37\x02\0\0\xda\x0f\0\0\ +\x11\xf4\x01\0\x40\x11\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x60\x11\0\0\ +\x37\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\x68\x11\0\0\x37\x02\0\0\xda\x0f\0\0\x11\ +\xf4\x01\0\x90\x11\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\xb0\x11\0\0\x37\ +\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\xb8\x11\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\ +\x01\0\xf8\x11\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\0\x12\0\0\x37\x02\0\0\ +\x31\x10\0\0\x2d\x08\x02\0\x08\x12\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\ +\x48\x12\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x50\x12\0\0\x37\x02\0\0\x31\ +\x10\0\0\x2d\x08\x02\0\x58\x12\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x98\ +\x12\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\xa0\x12\0\0\x37\x02\0\0\x31\x10\ +\0\0\x2d\x08\x02\0\xa8\x12\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\xd0\x12\0\ +\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\xd8\x12\0\0\x37\x02\0\0\x31\x10\0\0\ +\x2d\x08\x02\0\xe0\x12\0\0\x37\x02\0\0\x3d\x0f\0\0\x3d\xdc\x01\0\xf0\x12\0\0\ +\x37\x02\0\0\x3d\x0f\0\0\x05\xdc\x01\0\0\x13\0\0\x37\x02\0\0\x7d\x10\0\0\x0d\ +\xa4\x08\0\x10\x13\0\0\x37\x02\0\0\x7d\x10\0\0\x0d\xa4\x08\0\x18\x13\0\0\x37\ +\x02\0\0\x91\x10\0\0\x2e\xa8\x08\0\x38\x13\0\0\x37\x02\0\0\x91\x10\0\0\x24\xa8\ +\x08\0\x40\x13\0\0\x37\x02\0\0\x91\x10\0\0\x13\xa8\x08\0\x50\x13\0\0\x37\x02\0\ +\0\x91\x10\0\0\x2e\xa8\x08\0\x58\x13\0\0\x37\x02\0\0\xd0\x10\0\0\x15\xb4\x08\0\ +\x70\x13\0\0\x37\x02\0\0\x18\x11\0\0\x11\xc0\x08\0\x78\x13\0\0\x37\x02\0\0\0\0\ +\0\0\0\0\0\0\x98\x13\0\0\x37\x02\0\0\x31\x11\0\0\x01\xe4\x08\0\xa0\x13\0\0\x37\ +\x02\0\0\x33\x11\0\0\x18\xc4\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\x03\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2d\x01\0\0\0\0\x03\0\ +\x98\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x6f\x01\0\0\0\0\x03\0\xb8\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\x46\x01\0\0\0\0\x03\0\x78\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xbd\0\ +\0\0\0\0\x03\0\xd0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x02\0\0\0\0\x03\0\x20\ +\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\x01\0\0\0\0\x03\0\xe8\x04\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\xcc\0\0\0\0\0\x03\0\x18\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x25\x01\0\ +\0\0\0\x03\0\xe8\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x32\x02\0\0\0\0\x03\0\x38\x03\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2a\x02\0\0\0\0\x03\0\x28\x0e\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\xec\0\0\0\0\0\x03\0\xf8\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x22\x02\0\0\0\ +\0\x03\0\xe8\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x5f\x01\0\0\0\0\x03\0\xd0\x0c\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\x76\x01\0\0\0\0\x03\0\xa0\x04\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\xe8\x01\0\0\0\0\x03\0\x28\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x97\x01\0\0\0\0\ +\x03\0\x68\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x42\x02\0\0\0\0\x03\0\xa0\x0e\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\xe0\x01\0\0\0\0\x03\0\x50\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\xaf\x01\0\0\0\0\x03\0\xc0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd0\x01\0\0\0\0\ +\x03\0\x50\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa7\x01\0\0\0\0\x03\0\x48\x08\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\x7e\x01\0\0\0\0\x03\0\x10\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\x67\x01\0\0\0\0\x03\0\x20\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf9\x01\0\0\0\0\ +\x03\0\xd8\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x86\x01\0\0\0\0\x03\0\xf8\x08\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\x3a\x02\0\0\0\0\x03\0\x68\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\xd8\x01\0\0\0\0\x03\0\xe0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9f\x01\0\0\0\0\ +\x03\0\x38\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xfc\0\0\0\0\0\x03\0\xe8\x09\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\x3e\x01\0\0\0\0\x03\0\xd8\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\xf4\0\0\0\0\0\x03\0\x98\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd4\0\0\0\0\0\x03\0\ +\xc8\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc8\x01\0\0\0\0\x03\0\x70\x0d\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\x57\x01\0\0\0\0\x03\0\x98\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1d\ +\x01\0\0\0\0\x03\0\xb0\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc4\0\0\0\0\0\x03\0\xe0\ +\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf1\x01\0\0\0\0\x03\0\0\x0c\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\x1a\x02\0\0\0\0\x03\0\xf8\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe4\0\0\0\ +\0\0\x03\0\0\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc0\x01\0\0\0\0\x03\0\xb0\x0e\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\x36\x01\0\0\0\0\x03\0\xc0\x0d\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\xdc\0\0\0\0\0\x03\0\xd8\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x12\x02\0\0\0\0\ +\x03\0\x08\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x4f\x01\0\0\0\0\x03\0\xf0\x0e\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\x15\x01\0\0\0\0\x03\0\x08\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\x4a\x02\0\0\0\0\x03\0\xe8\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x09\x02\0\0\0\0\ +\x03\0\x20\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb7\x01\0\0\0\0\x03\0\x48\x10\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\x8e\x01\0\0\0\0\x03\0\0\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\x0c\x01\0\0\0\0\x03\0\xa0\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x59\0\0\0\x12\0\x03\ +\0\0\0\0\0\0\0\0\0\xb0\x13\0\0\0\0\0\0\x3e\0\0\0\x11\0\x05\0\0\0\0\0\0\0\0\0\ +\x28\0\0\0\0\0\0\0\x01\0\0\0\x11\0\x05\0\x28\0\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\ +\x86\0\0\0\x11\0\x05\0\x50\0\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\x7d\0\0\0\x11\0\x06\ +\0\0\0\0\0\0\0\0\0\x07\0\0\0\0\0\0\0\x28\0\0\0\0\0\0\0\x01\0\0\0\x35\0\0\0\x50\ +\0\0\0\0\0\0\0\x01\0\0\0\x36\0\0\0\x58\x13\0\0\0\0\0\0\x01\0\0\0\x37\0\0\0\x20\ +\x05\0\0\0\0\0\0\x04\0\0\0\x35\0\0\0\x2c\x05\0\0\0\0\0\0\x04\0\0\0\x36\0\0\0\ +\x38\x05\0\0\0\0\0\0\x04\0\0\0\x37\0\0\0\x50\x05\0\0\0\0\0\0\x04\0\0\0\x38\0\0\ +\0\x2c\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\x50\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\x70\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\x90\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\xb0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\xd0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\xf0\0\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\x10\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\x30\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\x50\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x01\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\x70\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x01\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\x90\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x01\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\xb0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x01\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\xd0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x01\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\xf0\x01\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x02\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\x10\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x02\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\x30\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x02\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\x50\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x02\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\x70\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x02\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\x90\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x02\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x02\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x02\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x02\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x03\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\ +\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\x40\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\x60\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\x80\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x03\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\xa0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x03\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\xc0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x03\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\xe0\x03\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x03\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x04\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\x20\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x04\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\x40\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x04\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\x60\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x04\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\x80\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x04\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\xa0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x04\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x04\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x04\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x04\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x05\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x05\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\ +\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\x70\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\x90\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\xb0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x05\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\xd0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x05\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\xf0\x05\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x06\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\x10\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x06\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\x30\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x06\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\x50\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x06\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\x70\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x06\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\x90\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x06\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\xb0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x06\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\xd0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x06\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x06\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x07\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\x10\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x07\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x07\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x07\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\ +\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\xa0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\xc0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\xe0\x07\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x07\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x08\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\x20\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x08\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\x40\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x08\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\x60\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x08\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\x80\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x08\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\xa0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x08\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\xc0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x08\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\xe0\x08\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x08\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x09\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\x20\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x09\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\x40\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x09\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x09\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x09\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\ +\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\xd0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\xf0\x09\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\x10\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\x30\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x0a\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\x50\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x0a\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\x70\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x0a\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\x90\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x0a\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\xb0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x0a\0\0\0\0\0\0\x04\ +\0\0\0\x01\0\0\0\xd0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\x0a\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\xf0\x0a\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\x0b\0\0\0\0\0\0\ +\x04\0\0\0\x01\0\0\0\x10\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x20\x0b\0\0\0\0\0\ +\0\x04\0\0\0\x01\0\0\0\x30\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x40\x0b\0\0\0\0\ +\0\0\x04\0\0\0\x01\0\0\0\x50\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x60\x0b\0\0\0\ +\0\0\0\x04\0\0\0\x01\0\0\0\x70\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x80\x0b\0\0\ +\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xa0\x0b\0\ +\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xc0\x0b\ +\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xe0\ +\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xf0\x0b\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\0\ +\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x10\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\ +\x20\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x30\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\ +\0\x40\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x50\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\ +\0\0\x60\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x70\x0c\0\0\0\0\0\0\x04\0\0\0\x01\ +\0\0\0\x80\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x90\x0c\0\0\0\0\0\0\x04\0\0\0\ +\x01\0\0\0\xa0\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xb0\x0c\0\0\0\0\0\0\x04\0\0\ +\0\x01\0\0\0\xc0\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\xd0\x0c\0\0\0\0\0\0\x04\0\ +\0\0\x01\0\0\0\xe0\x0c\0\0\0\0\0\0\x04\0\0\0\x01\0\0\0\x3e\x3f\x40\x41\x42\0\ +\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x74\x6f\x65\x70\x6c\x69\x74\ +\x7a\x5f\x6b\x65\x79\0\x2e\x74\x65\x78\x74\0\x2e\x72\x65\x6c\x2e\x42\x54\x46\ +\x2e\x65\x78\x74\0\x2e\x72\x65\x6c\x73\x6f\x63\x6b\x65\x74\0\x2e\x6d\x61\x70\ +\x73\0\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\x5f\x63\x6f\x6e\x66\x69\x67\ +\x75\x72\x61\x74\x69\x6f\x6e\x73\0\x74\x75\x6e\x5f\x72\x73\x73\x5f\x73\x74\x65\ +\x65\x72\x69\x6e\x67\x5f\x70\x72\x6f\x67\0\x2e\x6c\x6c\x76\x6d\x5f\x61\x64\x64\ +\x72\x73\x69\x67\0\x5f\x6c\x69\x63\x65\x6e\x73\x65\0\x74\x61\x70\x5f\x72\x73\ +\x73\x5f\x6d\x61\x70\x5f\x69\x6e\x64\x69\x72\x65\x63\x74\x69\x6f\x6e\x5f\x74\ +\x61\x62\x6c\x65\0\x2e\x73\x74\x72\x74\x61\x62\0\x2e\x73\x79\x6d\x74\x61\x62\0\ +\x2e\x72\x65\x6c\x2e\x42\x54\x46\0\x4c\x42\x42\x30\x5f\x39\0\x4c\x42\x42\x30\ +\x5f\x37\x39\0\x4c\x42\x42\x30\x5f\x35\x39\0\x4c\x42\x42\x30\x5f\x34\x39\0\x4c\ +\x42\x42\x30\x5f\x38\x38\0\x4c\x42\x42\x30\x5f\x36\x38\0\x4c\x42\x42\x30\x5f\ +\x35\x38\0\x4c\x42\x42\x30\x5f\x34\x38\0\x4c\x42\x42\x30\x5f\x33\x38\0\x4c\x42\ +\x42\x30\x5f\x31\x38\0\x4c\x42\x42\x30\x5f\x31\x30\x38\0\x4c\x42\x42\x30\x5f\ +\x39\x37\0\x4c\x42\x42\x30\x5f\x37\x37\0\x4c\x42\x42\x30\x5f\x31\x37\0\x4c\x42\ +\x42\x30\x5f\x31\x30\x37\0\x4c\x42\x42\x30\x5f\x38\x36\0\x4c\x42\x42\x30\x5f\ +\x34\x36\0\x4c\x42\x42\x30\x5f\x31\x30\x36\0\x4c\x42\x42\x30\x5f\x39\x35\0\x4c\ +\x42\x42\x30\x5f\x37\x35\0\x4c\x42\x42\x30\x5f\x36\x35\0\x4c\x42\x42\x30\x5f\ +\x34\x35\0\x4c\x42\x42\x30\x5f\x34\0\x4c\x42\x42\x30\x5f\x36\x34\0\x4c\x42\x42\ +\x30\x5f\x34\x34\0\x4c\x42\x42\x30\x5f\x33\x34\0\x4c\x42\x42\x30\x5f\x31\x30\ +\x34\0\x4c\x42\x42\x30\x5f\x35\x33\0\x4c\x42\x42\x30\x5f\x34\x33\0\x4c\x42\x42\ +\x30\x5f\x33\x33\0\x4c\x42\x42\x30\x5f\x32\x33\0\x4c\x42\x42\x30\x5f\x31\x30\ +\x33\0\x4c\x42\x42\x30\x5f\x39\x32\0\x4c\x42\x42\x30\x5f\x38\x32\0\x4c\x42\x42\ +\x30\x5f\x35\x32\0\x4c\x42\x42\x30\x5f\x34\x32\0\x4c\x42\x42\x30\x5f\x32\x32\0\ +\x4c\x42\x42\x30\x5f\x31\x30\x32\0\x4c\x42\x42\x30\x5f\x38\x31\0\x4c\x42\x42\ +\x30\x5f\x35\x31\0\x4c\x42\x42\x30\x5f\x31\x31\0\x4c\x42\x42\x30\x5f\x31\x30\ +\x31\0\x4c\x42\x42\x30\x5f\x39\x30\0\x4c\x42\x42\x30\x5f\x38\x30\0\x4c\x42\x42\ +\x30\x5f\x37\x30\0\x4c\x42\x42\x30\x5f\x36\x30\0\x4c\x42\x42\x30\x5f\x35\x30\0\ +\x4c\x42\x42\x30\x5f\x34\x30\0\x4c\x42\x42\x30\x5f\x32\x30\0\x4c\x42\x42\x30\ +\x5f\x31\x30\x30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa4\0\0\0\ +\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe5\x4a\0\0\0\0\0\0\x53\x02\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1a\0\0\0\x01\0\0\0\x06\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x31\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\xb0\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\x2d\0\0\0\x09\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\xb0\x3d\0\0\0\0\0\0\x30\0\0\0\0\0\0\0\x0c\0\0\0\x03\0\0\0\x08\0\0\0\0\0\0\0\ +\x10\0\0\0\0\0\0\0\x38\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf0\ +\x13\0\0\0\0\0\0\x78\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\x7e\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x68\x14\0\0\0\0\0\ +\0\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb8\0\0\ +\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x70\x14\0\0\0\0\0\0\xf5\x16\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb4\0\0\0\x09\0\0\0\ +\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe0\x3d\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\x0c\0\ +\0\0\x07\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x24\0\0\0\x01\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\x68\x2b\0\0\0\0\0\0\xf0\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x20\0\0\0\x09\0\0\0\x40\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\x20\x3e\0\0\0\0\0\0\xc0\x0c\0\0\0\0\0\0\x0c\0\0\0\x09\0\0\0\x08\0\ +\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x6f\0\0\0\x03\x4c\xff\x6f\0\0\0\x80\0\0\0\0\0\0\ +\0\0\0\0\0\0\xe0\x4a\0\0\0\0\0\0\x05\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\xac\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x58\ +\x38\0\0\0\0\0\0\x58\x05\0\0\0\0\0\0\x01\0\0\0\x34\0\0\0\x08\0\0\0\0\0\0\0\x18\ +\0\0\0\0\0\0\0"; +} - return 0; -err: - bpf_object__destroy_skeleton(s); - return -1; +#ifdef __cplusplus +struct rss_bpf *rss_bpf::open(const struct bpf_object_open_opts *opts) { return rss_bpf__open_opts(opts); } +struct rss_bpf *rss_bpf::open_and_load() { return rss_bpf__open_and_load(); } +int rss_bpf::load(struct rss_bpf *skel) { return rss_bpf__load(skel); } +int rss_bpf::attach(struct rss_bpf *skel) { return rss_bpf__attach(skel); } +void rss_bpf::detach(struct rss_bpf *skel) { rss_bpf__detach(skel); } +void rss_bpf::destroy(struct rss_bpf *skel) { rss_bpf__destroy(skel); } +const void *rss_bpf::elf_bytes(size_t *sz) { return rss_bpf__elf_bytes(sz); } +#endif /* __cplusplus */ + +__attribute__((unused)) static void +rss_bpf__assert(struct rss_bpf *s __attribute__((unused))) +{ +#ifdef __cplusplus +#define _Static_assert static_assert +#endif +#ifdef __cplusplus +#undef _Static_assert +#endif } #endif /* __RSS_BPF_SKEL_H__ */ diff --git a/ebpf/trace-events b/ebpf/trace-events index 411b1e2be3..b3ad1a35f2 100644 --- a/ebpf/trace-events +++ b/ebpf/trace-events @@ -1,4 +1,4 @@ -# See docs/devel/tracing.txt for syntax documentation. +# See docs/devel/tracing.rst for syntax documentation. # ebpf-rss.c ebpf_error(const char *s1, const char *s2) "error in %s: %s" diff --git a/ebpf/trace.h b/ebpf/trace.h deleted file mode 100644 index abefc46ab1..0000000000 --- a/ebpf/trace.h +++ /dev/null @@ -1 +0,0 @@ -#include "trace/trace-ebpf.h" diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index bbeadaa189..a44649f4f4 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -118,7 +118,8 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status, } else { int shift = frac_normalize(p); p->cls = float_class_normal; - p->exp = fmt->frac_shift - fmt->exp_bias - shift + 1; + p->exp = fmt->frac_shift - fmt->exp_bias + - shift + !fmt->m68k_denormal; } } else if (likely(p->exp < fmt->exp_max) || fmt->arm_althp) { p->cls = float_class_normal; @@ -214,18 +215,35 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, p->frac_lo &= ~round_mask; } } else if (unlikely(exp >= exp_max)) { - flags |= float_flag_overflow | float_flag_inexact; - if (overflow_norm) { + flags |= float_flag_overflow; + if (s->rebias_overflow) { + exp -= fmt->exp_re_bias; + } else if (overflow_norm) { + flags |= float_flag_inexact; exp = exp_max - 1; frac_allones(p); p->frac_lo &= ~round_mask; } else { + flags |= float_flag_inexact; p->cls = float_class_inf; exp = exp_max; frac_clear(p); } } frac_shr(p, frac_shift); + } else if (unlikely(s->rebias_underflow)) { + flags |= float_flag_underflow; + exp += fmt->exp_re_bias; + if (p->frac_lo & round_mask) { + flags |= float_flag_inexact; + if (frac_addi(p, p, inc)) { + frac_shr(p, 1); + p->frac_hi |= DECOMPOSED_IMPLICIT_BIT; + exp++; + } + p->frac_lo &= ~round_mask; + } + frac_shr(p, frac_shift); } else if (s->flush_to_zero) { flags |= float_flag_output_denormal; p->cls = float_class_zero; @@ -239,7 +257,7 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, is_tiny = !frac_addi(&discard, p, inc); } - frac_shrjam(p, 1 - exp); + frac_shrjam(p, !fmt->m68k_denormal - exp); if (p->frac_lo & round_mask) { /* Need to recompute round-to-even/round-to-odd. */ @@ -270,7 +288,7 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, p->frac_lo &= ~round_mask; } - exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) != 0; + exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) && !fmt->m68k_denormal; frac_shr(p, frac_shift); if (is_tiny && (flags & float_flag_inexact)) { @@ -1164,6 +1182,84 @@ static uint64_t partsN(float_to_uint)(FloatPartsN *p, FloatRoundMode rmode, return r; } +/* + * Like partsN(float_to_sint), except do not saturate the result. + * Instead, return the rounded unbounded precision two's compliment result, + * modulo 2**(bitsm1 + 1). + */ +static int64_t partsN(float_to_sint_modulo)(FloatPartsN *p, + FloatRoundMode rmode, + int bitsm1, float_status *s) +{ + int flags = 0; + uint64_t r; + bool overflow = false; + + switch (p->cls) { + case float_class_snan: + flags |= float_flag_invalid_snan; + /* fall through */ + case float_class_qnan: + flags |= float_flag_invalid; + r = 0; + break; + + case float_class_inf: + overflow = true; + r = 0; + break; + + case float_class_zero: + return 0; + + case float_class_normal: + /* TODO: N - 2 is frac_size for rounding; could use input fmt. */ + if (parts_round_to_int_normal(p, rmode, 0, N - 2)) { + flags = float_flag_inexact; + } + + if (p->exp <= DECOMPOSED_BINARY_POINT) { + /* + * Because we rounded to integral, and exp < 64, + * we know frac_low is zero. + */ + r = p->frac_hi >> (DECOMPOSED_BINARY_POINT - p->exp); + if (p->exp < bitsm1) { + /* Result in range. */ + } else if (p->exp == bitsm1) { + /* The only in-range value is INT_MIN. */ + overflow = !p->sign || p->frac_hi != DECOMPOSED_IMPLICIT_BIT; + } else { + overflow = true; + } + } else { + /* Overflow, but there might still be bits to return. */ + int shl = p->exp - DECOMPOSED_BINARY_POINT; + if (shl < N) { + frac_shl(p, shl); + r = p->frac_hi; + } else { + r = 0; + } + overflow = true; + } + + if (p->sign) { + r = -r; + } + break; + + default: + g_assert_not_reached(); + } + + if (overflow) { + flags = float_flag_invalid | float_flag_invalid_cvti; + } + float_raise(flags, s); + return r; +} + /* * Integer to float conversions * @@ -1419,6 +1515,7 @@ static void partsN(log2)(FloatPartsN *a, float_status *s, const FloatFmt *fmt) parts_return_nan(a, s); return; case float_class_zero: + float_raise(float_flag_divbyzero, s); /* log2(0) = -inf */ a->cls = float_class_inf; a->sign = 1; diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index c416cff1d4..4b80d3f2b1 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -152,7 +152,7 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status) /* * This case is true for Alpha, ARM, MIPS, OpenRISC, PPC, RISC-V, * S390, SH4, TriCore, and Xtensa. Our other supported targets, - * CRIS, Nios2, and Tile, do not have floating-point. + * CRIS and Nios2, do not have floating-point. */ if (snan_bit_is_one(status)) { /* set all bits other than msb */ @@ -390,7 +390,8 @@ bool float32_is_signaling_nan(float32 a_, float_status *status) static int pickNaN(FloatClass a_cls, FloatClass b_cls, bool aIsLargerSignificand, float_status *status) { -#if defined(TARGET_ARM) || defined(TARGET_MIPS) || defined(TARGET_HPPA) +#if defined(TARGET_ARM) || defined(TARGET_MIPS) || defined(TARGET_HPPA) || \ + defined(TARGET_LOONGARCH64) || defined(TARGET_S390X) /* ARM mandated NaN propagation rules (see FPProcessNaNs()), take * the first of: * 1. A if it is signaling @@ -574,6 +575,29 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, return 1; } } +#elif defined(TARGET_LOONGARCH64) + /* + * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) + * case sets InvalidOp and returns the input value 'c' + */ + if (infzero) { + float_raise(float_flag_invalid | float_flag_invalid_imz, status); + return 2; + } + /* Prefer sNaN over qNaN, in the c, a, b order. */ + if (is_snan(c_cls)) { + return 2; + } else if (is_snan(a_cls)) { + return 0; + } else if (is_snan(b_cls)) { + return 1; + } else if (is_qnan(c_cls)) { + return 2; + } else if (is_qnan(a_cls)) { + return 0; + } else { + return 1; + } #elif defined(TARGET_PPC) /* For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer * to return an input NaN if we have one (ie c) rather than generating diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 4a871ef2a1..027a8e576d 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -517,14 +517,17 @@ typedef struct { * round_mask: bits below lsb which must be rounded * The following optional modifiers are available: * arm_althp: handle ARM Alternative Half Precision + * m68k_denormal: explicit integer bit for extended precision may be 1 */ typedef struct { int exp_size; int exp_bias; + int exp_re_bias; int exp_max; int frac_size; int frac_shift; bool arm_althp; + bool m68k_denormal; uint64_t round_mask; } FloatFmt; @@ -532,6 +535,7 @@ typedef struct { #define FLOAT_PARAMS_(E) \ .exp_size = E, \ .exp_bias = ((1 << E) - 1) >> 1, \ + .exp_re_bias = (1 << (E - 1)) + (1 << (E - 2)), \ .exp_max = (1 << E) - 1 #define FLOAT_PARAMS(E, F) \ @@ -574,7 +578,12 @@ static const FloatFmt float128_params = { static const FloatFmt floatx80_params[3] = { [floatx80_precision_s] = { FLOATX80_PARAMS(23) }, [floatx80_precision_d] = { FLOATX80_PARAMS(52) }, - [floatx80_precision_x] = { FLOATX80_PARAMS(64) }, + [floatx80_precision_x] = { + FLOATX80_PARAMS(64), +#ifdef TARGET_M68K + .m68k_denormal = true, +#endif + }, }; /* Unpack a float to parts, but do not canonicalize. */ @@ -591,27 +600,27 @@ static void unpack_raw64(FloatParts64 *r, const FloatFmt *fmt, uint64_t raw) }; } -static inline void float16_unpack_raw(FloatParts64 *p, float16 f) +static void QEMU_FLATTEN float16_unpack_raw(FloatParts64 *p, float16 f) { unpack_raw64(p, &float16_params, f); } -static inline void bfloat16_unpack_raw(FloatParts64 *p, bfloat16 f) +static void QEMU_FLATTEN bfloat16_unpack_raw(FloatParts64 *p, bfloat16 f) { unpack_raw64(p, &bfloat16_params, f); } -static inline void float32_unpack_raw(FloatParts64 *p, float32 f) +static void QEMU_FLATTEN float32_unpack_raw(FloatParts64 *p, float32 f) { unpack_raw64(p, &float32_params, f); } -static inline void float64_unpack_raw(FloatParts64 *p, float64 f) +static void QEMU_FLATTEN float64_unpack_raw(FloatParts64 *p, float64 f) { unpack_raw64(p, &float64_params, f); } -static void floatx80_unpack_raw(FloatParts128 *p, floatx80 f) +static void QEMU_FLATTEN floatx80_unpack_raw(FloatParts128 *p, floatx80 f) { *p = (FloatParts128) { .cls = float_class_unclassified, @@ -621,7 +630,7 @@ static void floatx80_unpack_raw(FloatParts128 *p, floatx80 f) }; } -static void float128_unpack_raw(FloatParts128 *p, float128 f) +static void QEMU_FLATTEN float128_unpack_raw(FloatParts128 *p, float128 f) { const int f_size = float128_params.frac_size - 64; const int e_size = float128_params.exp_size; @@ -648,27 +657,27 @@ static uint64_t pack_raw64(const FloatParts64 *p, const FloatFmt *fmt) return ret; } -static inline float16 float16_pack_raw(const FloatParts64 *p) +static float16 QEMU_FLATTEN float16_pack_raw(const FloatParts64 *p) { return make_float16(pack_raw64(p, &float16_params)); } -static inline bfloat16 bfloat16_pack_raw(const FloatParts64 *p) +static bfloat16 QEMU_FLATTEN bfloat16_pack_raw(const FloatParts64 *p) { return pack_raw64(p, &bfloat16_params); } -static inline float32 float32_pack_raw(const FloatParts64 *p) +static float32 QEMU_FLATTEN float32_pack_raw(const FloatParts64 *p) { return make_float32(pack_raw64(p, &float32_params)); } -static inline float64 float64_pack_raw(const FloatParts64 *p) +static float64 QEMU_FLATTEN float64_pack_raw(const FloatParts64 *p) { return make_float64(pack_raw64(p, &float64_params)); } -static float128 float128_pack_raw(const FloatParts128 *p) +static float128 QEMU_FLATTEN float128_pack_raw(const FloatParts128 *p) { const int f_size = float128_params.frac_size - 64; const int e_size = float128_params.exp_size; @@ -850,11 +859,24 @@ static uint64_t parts128_float_to_uint(FloatParts128 *p, FloatRoundMode rmode, #define parts_float_to_uint(P, R, Z, M, S) \ PARTS_GENERIC_64_128(float_to_uint, P)(P, R, Z, M, S) +static int64_t parts64_float_to_sint_modulo(FloatParts64 *p, + FloatRoundMode rmode, + int bitsm1, float_status *s); +static int64_t parts128_float_to_sint_modulo(FloatParts128 *p, + FloatRoundMode rmode, + int bitsm1, float_status *s); + +#define parts_float_to_sint_modulo(P, R, M, S) \ + PARTS_GENERIC_64_128(float_to_sint_modulo, P)(P, R, M, S) + static void parts64_sint_to_float(FloatParts64 *p, int64_t a, int scale, float_status *s); static void parts128_sint_to_float(FloatParts128 *p, int64_t a, int scale, float_status *s); +#define parts_float_to_sint(P, R, Z, MN, MX, S) \ + PARTS_GENERIC_64_128(float_to_sint, P)(P, R, Z, MN, MX, S) + #define parts_sint_to_float(P, I, Z, S) \ PARTS_GENERIC_64_128(sint_to_float, P)(P, I, Z, S) @@ -3111,6 +3133,15 @@ int64_t float64_to_int64_scalbn(float64 a, FloatRoundMode rmode, int scale, return parts_float_to_sint(&p, rmode, scale, INT64_MIN, INT64_MAX, s); } +int8_t bfloat16_to_int8_scalbn(bfloat16 a, FloatRoundMode rmode, int scale, + float_status *s) +{ + FloatParts64 p; + + bfloat16_unpack_canonical(&p, a, s); + return parts_float_to_sint(&p, rmode, scale, INT8_MIN, INT8_MAX, s); +} + int16_t bfloat16_to_int16_scalbn(bfloat16 a, FloatRoundMode rmode, int scale, float_status *s) { @@ -3377,6 +3408,11 @@ int64_t floatx80_to_int64_round_to_zero(floatx80 a, float_status *s) return floatx80_to_int64_scalbn(a, float_round_to_zero, 0, s); } +int8_t bfloat16_to_int8(bfloat16 a, float_status *s) +{ + return bfloat16_to_int8_scalbn(a, s->float_rounding_mode, 0, s); +} + int16_t bfloat16_to_int16(bfloat16 a, float_status *s) { return bfloat16_to_int16_scalbn(a, s->float_rounding_mode, 0, s); @@ -3392,6 +3428,11 @@ int64_t bfloat16_to_int64(bfloat16 a, float_status *s) return bfloat16_to_int64_scalbn(a, s->float_rounding_mode, 0, s); } +int8_t bfloat16_to_int8_round_to_zero(bfloat16 a, float_status *s) +{ + return bfloat16_to_int8_scalbn(a, float_round_to_zero, 0, s); +} + int16_t bfloat16_to_int16_round_to_zero(bfloat16 a, float_status *s) { return bfloat16_to_int16_scalbn(a, float_round_to_zero, 0, s); @@ -3407,6 +3448,24 @@ int64_t bfloat16_to_int64_round_to_zero(bfloat16 a, float_status *s) return bfloat16_to_int64_scalbn(a, float_round_to_zero, 0, s); } +int32_t float64_to_int32_modulo(float64 a, FloatRoundMode rmode, + float_status *s) +{ + FloatParts64 p; + + float64_unpack_canonical(&p, a, s); + return parts_float_to_sint_modulo(&p, rmode, 31, s); +} + +int64_t float64_to_int64_modulo(float64 a, FloatRoundMode rmode, + float_status *s) +{ + FloatParts64 p; + + float64_unpack_canonical(&p, a, s); + return parts_float_to_sint_modulo(&p, rmode, 63, s); +} + /* * Floating-point to unsigned integer conversions */ @@ -3501,6 +3560,15 @@ uint64_t float64_to_uint64_scalbn(float64 a, FloatRoundMode rmode, int scale, return parts_float_to_uint(&p, rmode, scale, UINT64_MAX, s); } +uint8_t bfloat16_to_uint8_scalbn(bfloat16 a, FloatRoundMode rmode, + int scale, float_status *s) +{ + FloatParts64 p; + + bfloat16_unpack_canonical(&p, a, s); + return parts_float_to_uint(&p, rmode, scale, UINT8_MAX, s); +} + uint16_t bfloat16_to_uint16_scalbn(bfloat16 a, FloatRoundMode rmode, int scale, float_status *s) { @@ -3726,6 +3794,11 @@ Int128 float128_to_uint128_round_to_zero(float128 a, float_status *s) return float128_to_uint128_scalbn(a, float_round_to_zero, 0, s); } +uint8_t bfloat16_to_uint8(bfloat16 a, float_status *s) +{ + return bfloat16_to_uint8_scalbn(a, s->float_rounding_mode, 0, s); +} + uint16_t bfloat16_to_uint16(bfloat16 a, float_status *s) { return bfloat16_to_uint16_scalbn(a, s->float_rounding_mode, 0, s); @@ -3741,6 +3814,11 @@ uint64_t bfloat16_to_uint64(bfloat16 a, float_status *s) return bfloat16_to_uint64_scalbn(a, s->float_rounding_mode, 0, s); } +uint8_t bfloat16_to_uint8_round_to_zero(bfloat16 a, float_status *s) +{ + return bfloat16_to_uint8_scalbn(a, float_round_to_zero, 0, s); +} + uint16_t bfloat16_to_uint16_round_to_zero(bfloat16 a, float_status *s) { return bfloat16_to_uint16_scalbn(a, float_round_to_zero, 0, s); @@ -3896,6 +3974,11 @@ bfloat16 int16_to_bfloat16_scalbn(int16_t a, int scale, float_status *status) return int64_to_bfloat16_scalbn(a, scale, status); } +bfloat16 int8_to_bfloat16_scalbn(int8_t a, int scale, float_status *status) +{ + return int64_to_bfloat16_scalbn(a, scale, status); +} + bfloat16 int64_to_bfloat16(int64_t a, float_status *status) { return int64_to_bfloat16_scalbn(a, 0, status); @@ -3911,6 +3994,11 @@ bfloat16 int16_to_bfloat16(int16_t a, float_status *status) return int64_to_bfloat16_scalbn(a, 0, status); } +bfloat16 int8_to_bfloat16(int8_t a, float_status *status) +{ + return int64_to_bfloat16_scalbn(a, 0, status); +} + float128 int128_to_float128(Int128 a, float_status *status) { FloatParts128 p = { }; @@ -4106,6 +4194,11 @@ bfloat16 uint16_to_bfloat16_scalbn(uint16_t a, int scale, float_status *status) return uint64_to_bfloat16_scalbn(a, scale, status); } +bfloat16 uint8_to_bfloat16_scalbn(uint8_t a, int scale, float_status *status) +{ + return uint64_to_bfloat16_scalbn(a, scale, status); +} + bfloat16 uint64_to_bfloat16(uint64_t a, float_status *status) { return uint64_to_bfloat16_scalbn(a, 0, status); @@ -4121,6 +4214,11 @@ bfloat16 uint16_to_bfloat16(uint16_t a, float_status *status) return uint64_to_bfloat16_scalbn(a, 0, status); } +bfloat16 uint8_to_bfloat16(uint8_t a, float_status *status) +{ + return uint64_to_bfloat16_scalbn(a, 0, status); +} + float128 uint64_to_float128(uint64_t a, float_status *status) { FloatParts128 p; @@ -5133,7 +5231,7 @@ float32 float32_exp2(float32 a, float_status *status) float64_unpack_canonical(&rp, float64_one, status); for (i = 0 ; i < 15 ; i++) { float64_unpack_canonical(&tp, float32_exp2_coefficients[i], status); - rp = *parts_muladd(&tp, &xp, &rp, 0, status); + rp = *parts_muladd(&tp, &xnp, &rp, 0, status); xnp = *parts_mul(&xnp, &xp, status); } diff --git a/fsdev/meson.build b/fsdev/meson.build index b632b66348..e20d7255e1 100644 --- a/fsdev/meson.build +++ b/fsdev/meson.build @@ -1,13 +1,13 @@ fsdev_ss = ss.source_set() fsdev_ss.add(files('qemu-fsdev-opts.c', 'qemu-fsdev-throttle.c')) -fsdev_ss.add(when: 'CONFIG_ALL', if_true: files('qemu-fsdev-dummy.c')) fsdev_ss.add(when: ['CONFIG_FSDEV_9P'], if_true: files( '9p-iov-marshal.c', '9p-marshal.c', 'qemu-fsdev.c', ), if_false: files('qemu-fsdev-dummy.c')) -softmmu_ss.add_all(when: 'CONFIG_LINUX', if_true: fsdev_ss) -softmmu_ss.add_all(when: 'CONFIG_DARWIN', if_true: fsdev_ss) +if host_os in ['linux', 'darwin'] + system_ss.add_all(fsdev_ss) +endif if have_virtfs_proxy_helper executable('virtfs-proxy-helper', diff --git a/fsdev/p9array.h b/fsdev/p9array.h index 90e83a7c7b..50a1b15fe9 100644 --- a/fsdev/p9array.h +++ b/fsdev/p9array.h @@ -27,8 +27,6 @@ #ifndef QEMU_P9ARRAY_H #define QEMU_P9ARRAY_H -#include "qemu/compiler.h" - /** * P9Array provides a mechanism to access arrays in common C-style (e.g. by * square bracket [] operator) in conjunction with reference variables that diff --git a/fsdev/qemu-fsdev-throttle.c b/fsdev/qemu-fsdev-throttle.c index 5c83a1cc09..d912da906d 100644 --- a/fsdev/qemu-fsdev-throttle.c +++ b/fsdev/qemu-fsdev-throttle.c @@ -94,20 +94,22 @@ void fsdev_throttle_init(FsThrottle *fst) } } -void coroutine_fn fsdev_co_throttle_request(FsThrottle *fst, bool is_write, +void coroutine_fn fsdev_co_throttle_request(FsThrottle *fst, + ThrottleDirection direction, struct iovec *iov, int iovcnt) { + assert(direction < THROTTLE_MAX); if (throttle_enabled(&fst->cfg)) { - if (throttle_schedule_timer(&fst->ts, &fst->tt, is_write) || - !qemu_co_queue_empty(&fst->throttled_reqs[is_write])) { - qemu_co_queue_wait(&fst->throttled_reqs[is_write], NULL); + if (throttle_schedule_timer(&fst->ts, &fst->tt, direction) || + !qemu_co_queue_empty(&fst->throttled_reqs[direction])) { + qemu_co_queue_wait(&fst->throttled_reqs[direction], NULL); } - throttle_account(&fst->ts, is_write, iov_size(iov, iovcnt)); + throttle_account(&fst->ts, direction, iov_size(iov, iovcnt)); - if (!qemu_co_queue_empty(&fst->throttled_reqs[is_write]) && - !throttle_schedule_timer(&fst->ts, &fst->tt, is_write)) { - qemu_co_queue_next(&fst->throttled_reqs[is_write]); + if (!qemu_co_queue_empty(&fst->throttled_reqs[direction]) && + !throttle_schedule_timer(&fst->ts, &fst->tt, direction)) { + qemu_co_queue_next(&fst->throttled_reqs[direction]); } } } diff --git a/fsdev/qemu-fsdev-throttle.h b/fsdev/qemu-fsdev-throttle.h index a21aecddc7..daa8ca2494 100644 --- a/fsdev/qemu-fsdev-throttle.h +++ b/fsdev/qemu-fsdev-throttle.h @@ -23,14 +23,14 @@ typedef struct FsThrottle { ThrottleState ts; ThrottleTimers tt; ThrottleConfig cfg; - CoQueue throttled_reqs[2]; + CoQueue throttled_reqs[THROTTLE_MAX]; } FsThrottle; int fsdev_throttle_parse_opts(QemuOpts *, FsThrottle *, Error **); void fsdev_throttle_init(FsThrottle *); -void coroutine_fn fsdev_co_throttle_request(FsThrottle *, bool , +void coroutine_fn fsdev_co_throttle_request(FsThrottle *, ThrottleDirection , struct iovec *, int); void fsdev_throttle_cleanup(FsThrottle *); diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c index 3da64e9f72..f5c953a710 100644 --- a/fsdev/qemu-fsdev.c +++ b/fsdev/qemu-fsdev.c @@ -133,6 +133,14 @@ int qemu_fsdev_add(QemuOpts *opts, Error **errp) } if (fsdriver) { + if (strncmp(fsdriver, "proxy", 5) == 0) { + warn_report( + "'-fsdev proxy' and '-virtfs proxy' are deprecated, use " + "'local' instead of 'proxy, or consider deploying virtiofsd " + "as alternative to 9p" + ); + } + for (i = 0; i < ARRAY_SIZE(FsDrivers); i++) { if (strcmp(FsDrivers[i].name, fsdriver) == 0) { break; diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c index 2dde27922f..144aaf585a 100644 --- a/fsdev/virtfs-proxy-helper.c +++ b/fsdev/virtfs-proxy-helper.c @@ -9,7 +9,13 @@ * the COPYING file in the top-level directory. */ +/* + * NOTE: The 9p 'proxy' backend is deprecated (since QEMU 8.1) and will be + * removed in a future version of QEMU! + */ + #include "qemu/osdep.h" +#include #include #include #include @@ -25,6 +31,7 @@ #include "qemu/xattr.h" #include "9p-iov-marshal.h" #include "hw/9pfs/9p-proxy.h" +#include "hw/9pfs/9p-util.h" #include "fsdev/9p-iov-marshal.h" #define PROGNAME "virtfs-proxy-helper" @@ -337,6 +344,28 @@ static void resetugid(int suid, int sgid) } } +/* + * Open regular file or directory. Attempts to open any special file are + * rejected. + * + * returns file descriptor or -1 on error + */ +static int open_regular(const char *pathname, int flags, mode_t mode) +{ + int fd; + + fd = open(pathname, flags, mode); + if (fd < 0) { + return fd; + } + + if (close_if_special_file(fd) < 0) { + return -1; + } + + return fd; +} + /* * send response in two parts * 1) ProxyHeader @@ -639,7 +668,7 @@ static int do_create_others(int type, struct iovec *iovec) if (retval < 0) { goto err_out; } - retval = mkdir(path.data, mode); + retval = g_mkdir(path.data, mode); break; case T_SYMLINK: retval = proxy_unmarshal(iovec, offset, "ss", &oldpath, &path); @@ -681,7 +710,7 @@ static int do_create(struct iovec *iovec) if (ret < 0) { goto unmarshal_err_out; } - ret = open(path.data, flags, mode); + ret = open_regular(path.data, flags, mode); if (ret < 0) { ret = -errno; } @@ -706,7 +735,7 @@ static int do_open(struct iovec *iovec) if (ret < 0) { goto err_out; } - ret = open(path.data, flags); + ret = open_regular(path.data, flags, 0); if (ret < 0) { ret = -errno; } @@ -1033,6 +1062,10 @@ int main(int argc, char **argv) struct statfs st_fs; #endif + fprintf(stderr, "NOTE: The 9p 'proxy' backend is deprecated (since " + "QEMU 8.1) and will be removed in a future version of " + "QEMU!\n"); + prog_name = g_path_get_basename(argv[0]); is_daemon = true; diff --git a/gdb-xml/aarch64-pauth.xml b/gdb-xml/aarch64-pauth.xml new file mode 100644 index 0000000000..0a5c566d66 --- /dev/null +++ b/gdb-xml/aarch64-pauth.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/gdb-xml/arm-neon.xml b/gdb-xml/arm-neon.xml index 9dce0a996f..d61f6b8549 100644 --- a/gdb-xml/arm-neon.xml +++ b/gdb-xml/arm-neon.xml @@ -76,7 +76,7 @@ - + diff --git a/gdb-xml/hexagon-core.xml b/gdb-xml/hexagon-core.xml new file mode 100644 index 0000000000..e181163cff --- /dev/null +++ b/gdb-xml/hexagon-core.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/hexagon-hvx.xml b/gdb-xml/hexagon-hvx.xml new file mode 100644 index 0000000000..5f2e220733 --- /dev/null +++ b/gdb-xml/hexagon-hvx.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/i386-32bit.xml b/gdb-xml/i386-32bit.xml index 872fcea9c2..7a66a02b67 100644 --- a/gdb-xml/i386-32bit.xml +++ b/gdb-xml/i386-32bit.xml @@ -110,7 +110,7 @@ - + diff --git a/gdb-xml/loongarch-base32.xml b/gdb-xml/loongarch-base32.xml new file mode 100644 index 0000000000..af47bbd3da --- /dev/null +++ b/gdb-xml/loongarch-base32.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/loongarch-base64.xml b/gdb-xml/loongarch-base64.xml index 4962bdbd28..2d8a1f6b73 100644 --- a/gdb-xml/loongarch-base64.xml +++ b/gdb-xml/loongarch-base64.xml @@ -1,5 +1,5 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/loongarch-fpu64.xml b/gdb-xml/loongarch-fpu64.xml deleted file mode 100644 index e52cf89fbc..0000000000 --- a/gdb-xml/loongarch-fpu64.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gdb-xml/microblaze-core.xml b/gdb-xml/microblaze-core.xml new file mode 100644 index 0000000000..becf77c89c --- /dev/null +++ b/gdb-xml/microblaze-core.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/microblaze-stack-protect.xml b/gdb-xml/microblaze-stack-protect.xml new file mode 100644 index 0000000000..997301e8a2 --- /dev/null +++ b/gdb-xml/microblaze-stack-protect.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/gdb-xml/riscv-32bit-cpu.xml b/gdb-xml/riscv-32bit-cpu.xml index 0d07aaec85..466f2c0648 100644 --- a/gdb-xml/riscv-32bit-cpu.xml +++ b/gdb-xml/riscv-32bit-cpu.xml @@ -5,13 +5,9 @@ are permitted in any medium without royalty provided the copyright notice and this notice are preserved. --> - - - + diff --git a/gdb-xml/riscv-32bit-fpu.xml b/gdb-xml/riscv-32bit-fpu.xml index 1eaae9119e..24aa087031 100644 --- a/gdb-xml/riscv-32bit-fpu.xml +++ b/gdb-xml/riscv-32bit-fpu.xml @@ -5,13 +5,9 @@ are permitted in any medium without royalty provided the copyright notice and this notice are preserved. --> - - - + @@ -43,8 +39,4 @@ - - - - diff --git a/gdb-xml/riscv-64bit-cpu.xml b/gdb-xml/riscv-64bit-cpu.xml index b8aa424ae4..c4d83de09b 100644 --- a/gdb-xml/riscv-64bit-cpu.xml +++ b/gdb-xml/riscv-64bit-cpu.xml @@ -5,13 +5,9 @@ are permitted in any medium without royalty provided the copyright notice and this notice are preserved. --> - - - + diff --git a/gdb-xml/riscv-64bit-fpu.xml b/gdb-xml/riscv-64bit-fpu.xml index 794854cc01..d0f17f9984 100644 --- a/gdb-xml/riscv-64bit-fpu.xml +++ b/gdb-xml/riscv-64bit-fpu.xml @@ -5,10 +5,6 @@ are permitted in any medium without royalty provided the copyright notice and this notice are preserved. --> - - @@ -17,7 +13,7 @@ - + @@ -49,8 +45,4 @@ - - - - diff --git a/gdb-xml/s390-virt-kvm.xml b/gdb-xml/s390-virt-kvm.xml new file mode 100644 index 0000000000..a256eddaf5 --- /dev/null +++ b/gdb-xml/s390-virt-kvm.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/gdb-xml/s390-virt.xml b/gdb-xml/s390-virt.xml index e2e9a7ad3c..438eb68aab 100644 --- a/gdb-xml/s390-virt.xml +++ b/gdb-xml/s390-virt.xml @@ -11,8 +11,4 @@ - - - - diff --git a/gdbstub.c b/gdbstub/gdbstub.c similarity index 50% rename from gdbstub.c rename to gdbstub/gdbstub.c index 4da1e48713..9c23d44baf 100644 --- a/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -24,356 +24,37 @@ */ #include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/error-report.h" #include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/module.h" -#include "trace/trace-root.h" +#include "qemu/error-report.h" +#include "trace.h" #include "exec/gdbstub.h" +#include "gdbstub/syscalls.h" #ifdef CONFIG_USER_ONLY -#include "qemu.h" +#include "gdbstub/user.h" #else -#include "monitor/monitor.h" -#include "chardev/char.h" -#include "chardev/char-fe.h" #include "hw/cpu/cluster.h" #include "hw/boards.h" #endif -#define MAX_PACKET_LENGTH 4096 - -#include "qemu/sockets.h" #include "sysemu/hw_accel.h" -#include "sysemu/kvm.h" #include "sysemu/runstate.h" -#include "semihosting/semihost.h" -#include "exec/exec-all.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" +#include "exec/hwaddr.h" -#ifdef CONFIG_USER_ONLY -#define GDB_ATTACHED "0" -#else -#define GDB_ATTACHED "1" -#endif - -#ifndef CONFIG_USER_ONLY -static int phy_memory_mode; -#endif - -static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr, - uint8_t *buf, int len, bool is_write) -{ - CPUClass *cc; - -#ifndef CONFIG_USER_ONLY - if (phy_memory_mode) { - if (is_write) { - cpu_physical_memory_write(addr, buf, len); - } else { - cpu_physical_memory_read(addr, buf, len); - } - return 0; - } -#endif - - cc = CPU_GET_CLASS(cpu); - if (cc->memory_rw_debug) { - return cc->memory_rw_debug(cpu, addr, buf, len, is_write); - } - return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); -} - -/* Return the GDB index for a given vCPU state. - * - * For user mode this is simply the thread id. In system mode GDB - * numbers CPUs from 1 as 0 is reserved as an "any cpu" index. - */ -static inline int cpu_gdb_index(CPUState *cpu) -{ -#if defined(CONFIG_USER_ONLY) - TaskState *ts = (TaskState *) cpu->opaque; - return ts ? ts->ts_tid : -1; -#else - return cpu->cpu_index + 1; -#endif -} - -enum { - GDB_SIGNAL_0 = 0, - GDB_SIGNAL_INT = 2, - GDB_SIGNAL_QUIT = 3, - GDB_SIGNAL_TRAP = 5, - GDB_SIGNAL_ABRT = 6, - GDB_SIGNAL_ALRM = 14, - GDB_SIGNAL_IO = 23, - GDB_SIGNAL_XCPU = 24, - GDB_SIGNAL_UNKNOWN = 143 -}; - -#ifdef CONFIG_USER_ONLY - -/* Map target signal numbers to GDB protocol signal numbers and vice - * versa. For user emulation's currently supported systems, we can - * assume most signals are defined. - */ - -static int gdb_signal_table[] = { - 0, - TARGET_SIGHUP, - TARGET_SIGINT, - TARGET_SIGQUIT, - TARGET_SIGILL, - TARGET_SIGTRAP, - TARGET_SIGABRT, - -1, /* SIGEMT */ - TARGET_SIGFPE, - TARGET_SIGKILL, - TARGET_SIGBUS, - TARGET_SIGSEGV, - TARGET_SIGSYS, - TARGET_SIGPIPE, - TARGET_SIGALRM, - TARGET_SIGTERM, - TARGET_SIGURG, - TARGET_SIGSTOP, - TARGET_SIGTSTP, - TARGET_SIGCONT, - TARGET_SIGCHLD, - TARGET_SIGTTIN, - TARGET_SIGTTOU, - TARGET_SIGIO, - TARGET_SIGXCPU, - TARGET_SIGXFSZ, - TARGET_SIGVTALRM, - TARGET_SIGPROF, - TARGET_SIGWINCH, - -1, /* SIGLOST */ - TARGET_SIGUSR1, - TARGET_SIGUSR2, -#ifdef TARGET_SIGPWR - TARGET_SIGPWR, -#else - -1, -#endif - -1, /* SIGPOLL */ - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, -#ifdef __SIGRTMIN - __SIGRTMIN + 1, - __SIGRTMIN + 2, - __SIGRTMIN + 3, - __SIGRTMIN + 4, - __SIGRTMIN + 5, - __SIGRTMIN + 6, - __SIGRTMIN + 7, - __SIGRTMIN + 8, - __SIGRTMIN + 9, - __SIGRTMIN + 10, - __SIGRTMIN + 11, - __SIGRTMIN + 12, - __SIGRTMIN + 13, - __SIGRTMIN + 14, - __SIGRTMIN + 15, - __SIGRTMIN + 16, - __SIGRTMIN + 17, - __SIGRTMIN + 18, - __SIGRTMIN + 19, - __SIGRTMIN + 20, - __SIGRTMIN + 21, - __SIGRTMIN + 22, - __SIGRTMIN + 23, - __SIGRTMIN + 24, - __SIGRTMIN + 25, - __SIGRTMIN + 26, - __SIGRTMIN + 27, - __SIGRTMIN + 28, - __SIGRTMIN + 29, - __SIGRTMIN + 30, - __SIGRTMIN + 31, - -1, /* SIGCANCEL */ - __SIGRTMIN, - __SIGRTMIN + 32, - __SIGRTMIN + 33, - __SIGRTMIN + 34, - __SIGRTMIN + 35, - __SIGRTMIN + 36, - __SIGRTMIN + 37, - __SIGRTMIN + 38, - __SIGRTMIN + 39, - __SIGRTMIN + 40, - __SIGRTMIN + 41, - __SIGRTMIN + 42, - __SIGRTMIN + 43, - __SIGRTMIN + 44, - __SIGRTMIN + 45, - __SIGRTMIN + 46, - __SIGRTMIN + 47, - __SIGRTMIN + 48, - __SIGRTMIN + 49, - __SIGRTMIN + 50, - __SIGRTMIN + 51, - __SIGRTMIN + 52, - __SIGRTMIN + 53, - __SIGRTMIN + 54, - __SIGRTMIN + 55, - __SIGRTMIN + 56, - __SIGRTMIN + 57, - __SIGRTMIN + 58, - __SIGRTMIN + 59, - __SIGRTMIN + 60, - __SIGRTMIN + 61, - __SIGRTMIN + 62, - __SIGRTMIN + 63, - __SIGRTMIN + 64, - __SIGRTMIN + 65, - __SIGRTMIN + 66, - __SIGRTMIN + 67, - __SIGRTMIN + 68, - __SIGRTMIN + 69, - __SIGRTMIN + 70, - __SIGRTMIN + 71, - __SIGRTMIN + 72, - __SIGRTMIN + 73, - __SIGRTMIN + 74, - __SIGRTMIN + 75, - __SIGRTMIN + 76, - __SIGRTMIN + 77, - __SIGRTMIN + 78, - __SIGRTMIN + 79, - __SIGRTMIN + 80, - __SIGRTMIN + 81, - __SIGRTMIN + 82, - __SIGRTMIN + 83, - __SIGRTMIN + 84, - __SIGRTMIN + 85, - __SIGRTMIN + 86, - __SIGRTMIN + 87, - __SIGRTMIN + 88, - __SIGRTMIN + 89, - __SIGRTMIN + 90, - __SIGRTMIN + 91, - __SIGRTMIN + 92, - __SIGRTMIN + 93, - __SIGRTMIN + 94, - __SIGRTMIN + 95, - -1, /* SIGINFO */ - -1, /* UNKNOWN */ - -1, /* DEFAULT */ - -1, - -1, - -1, - -1, - -1, - -1 -#endif -}; -#else -/* In system mode we only need SIGINT and SIGTRAP; other signals - are not yet supported. */ - -enum { - TARGET_SIGINT = 2, - TARGET_SIGTRAP = 5 -}; - -static int gdb_signal_table[] = { - -1, - -1, - TARGET_SIGINT, - -1, - -1, - TARGET_SIGTRAP -}; -#endif - -#ifdef CONFIG_USER_ONLY -static int target_signal_to_gdb (int sig) -{ - int i; - for (i = 0; i < ARRAY_SIZE (gdb_signal_table); i++) - if (gdb_signal_table[i] == sig) - return i; - return GDB_SIGNAL_UNKNOWN; -} -#endif - -static int gdb_signal_to_target (int sig) -{ - if (sig < ARRAY_SIZE (gdb_signal_table)) - return gdb_signal_table[sig]; - else - return -1; -} +#include "internals.h" typedef struct GDBRegisterState { int base_reg; - int num_regs; gdb_get_reg_cb get_reg; gdb_set_reg_cb set_reg; - const char *xml; - struct GDBRegisterState *next; + const GDBFeature *feature; } GDBRegisterState; -typedef struct GDBProcess { - uint32_t pid; - bool attached; +GDBState gdbserver_state; - char target_xml[1024]; -} GDBProcess; - -enum RSState { - RS_INACTIVE, - RS_IDLE, - RS_GETLINE, - RS_GETLINE_ESC, - RS_GETLINE_RLE, - RS_CHKSUM1, - 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 */ - enum RSState state; /* parsing state */ - char line_buf[MAX_PACKET_LENGTH]; - int line_buf_index; - int line_sum; /* running checksum */ - int line_csum; /* checksum at the end of the packet */ - GByteArray *last_packet; - int signal; -#ifdef CONFIG_USER_ONLY - int fd; - char *socket_path; - int running_state; -#else - CharBackend chr; - Chardev *mon_chr; -#endif - bool multiprocess; - GDBProcess *processes; - int process_num; - char syscall_buf[256]; - gdb_syscall_complete_cb current_syscall_cb; - GString *str_buf; - GByteArray *mem_buf; - int sstep_flags; - int supported_sstep_flags; -} GDBState; - -static GDBState gdbserver_state; - -static void init_gdbserver_state(void) +void gdb_init_gdbserver_state(void) { g_assert(!gdbserver_state.init); memset(&gdbserver_state, 0, sizeof(GDBState)); @@ -383,226 +64,17 @@ static void init_gdbserver_state(void) gdbserver_state.last_packet = g_byte_array_sized_new(MAX_PACKET_LENGTH + 4); /* - * In replay mode all events will come from the log and can't be - * suppressed otherwise we would break determinism. However as those - * events are tied to the number of executed instructions we won't see - * them occurring every time we single step. - */ - if (replay_mode != REPLAY_MODE_NONE) { - gdbserver_state.supported_sstep_flags = SSTEP_ENABLE; - } else if (kvm_enabled()) { - gdbserver_state.supported_sstep_flags = kvm_get_supported_sstep_flags(); - } else { - gdbserver_state.supported_sstep_flags = - SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER; - } - - /* - * By default use no IRQs and no timers while single stepping so as to - * make single stepping like an ICE HW step. + * What single-step modes are supported is accelerator dependent. + * By default try to use no IRQs and no timers while single + * stepping so as to make single stepping like a typical ICE HW step. */ + gdbserver_state.supported_sstep_flags = accel_supported_gdbstub_sstep_flags(); gdbserver_state.sstep_flags = SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER; gdbserver_state.sstep_flags &= gdbserver_state.supported_sstep_flags; - -} - -#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; - -#ifdef CONFIG_USER_ONLY - -static int get_char(void) -{ - uint8_t ch; - int ret; - - for(;;) { - ret = recv(gdbserver_state.fd, &ch, 1, 0); - if (ret < 0) { - if (errno == ECONNRESET) - gdbserver_state.fd = -1; - if (errno != EINTR) - return -1; - } else if (ret == 0) { - close(gdbserver_state.fd); - gdbserver_state.fd = -1; - return -1; - } else { - break; - } - } - return ch; -} -#endif - -static enum { - GDB_SYS_UNKNOWN, - GDB_SYS_ENABLED, - GDB_SYS_DISABLED, -} gdb_syscall_mode; - -/* Decide if either remote gdb syscalls or native file IO should be used. */ -int use_gdb_syscalls(void) -{ - SemihostingTarget target = semihosting_get_target(); - if (target == SEMIHOSTING_TARGET_NATIVE) { - /* -semihosting-config target=native */ - return false; - } else if (target == SEMIHOSTING_TARGET_GDB) { - /* -semihosting-config target=gdb */ - return true; - } - - /* -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.init ? - GDB_SYS_ENABLED : GDB_SYS_DISABLED; - } - return gdb_syscall_mode == GDB_SYS_ENABLED; -} - -static bool stub_can_reverse(void) -{ -#ifdef CONFIG_USER_ONLY - return false; -#else - return replay_mode == REPLAY_MODE_PLAY; -#endif -} - -/* Resume execution. */ -static inline void gdb_continue(void) -{ - -#ifdef CONFIG_USER_ONLY - gdbserver_state.running_state = 1; - trace_gdbstub_op_continue(); -#else - if (!runstate_needs_reset()) { - trace_gdbstub_op_continue(); - vm_start(); - } -#endif -} - -/* - * Resume execution, per CPU actions. For user-mode emulation it's - * equivalent to gdb_continue. - */ -static int gdb_continue_partial(char *newstates) -{ - CPUState *cpu; - int res = 0; -#ifdef CONFIG_USER_ONLY - /* - * This is not exactly accurate, but it's an improvement compared to the - * previous situation, where only one CPU would be single-stepped. - */ - CPU_FOREACH(cpu) { - if (newstates[cpu->cpu_index] == 's') { - trace_gdbstub_op_stepping(cpu->cpu_index); - cpu_single_step(cpu, gdbserver_state.sstep_flags); - } - } - gdbserver_state.running_state = 1; -#else - int flag = 0; - - if (!runstate_needs_reset()) { - bool step_requested = false; - CPU_FOREACH(cpu) { - if (newstates[cpu->cpu_index] == 's') { - step_requested = true; - break; - } - } - - if (vm_prepare_start(step_requested)) { - return 0; - } - - CPU_FOREACH(cpu) { - switch (newstates[cpu->cpu_index]) { - case 0: - case 1: - break; /* nothing to do here */ - case 's': - trace_gdbstub_op_stepping(cpu->cpu_index); - cpu_single_step(cpu, gdbserver_state.sstep_flags); - cpu_resume(cpu); - flag = 1; - break; - case 'c': - trace_gdbstub_op_continue_cpu(cpu->cpu_index); - cpu_resume(cpu); - flag = 1; - break; - default: - res = -1; - break; - } - } - } - if (flag) { - qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true); - } -#endif - return res; -} - -static void put_buffer(const uint8_t *buf, int len) -{ -#ifdef CONFIG_USER_ONLY - int ret; - - while (len > 0) { - ret = send(gdbserver_state.fd, buf, len, 0); - if (ret < 0) { - if (errno != EINTR) - return; - } else { - buf += ret; - len -= ret; - } - } -#else - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(&gdbserver_state.chr, buf, len); -#endif -} - -static inline int fromhex(int v) -{ - if (v >= '0' && v <= '9') - return v - '0'; - else if (v >= 'A' && v <= 'F') - return v - 'A' + 10; - else if (v >= 'a' && v <= 'f') - return v - 'a' + 10; - else - return 0; -} - -static inline int tohex(int v) -{ - if (v < 10) - return v + '0'; - else - return v - 10 + 'a'; } /* writes 2*len+1 bytes in buf */ -static void memtohex(GString *buf, const uint8_t *mem, int len) +void gdb_memtohex(GString *buf, const uint8_t *mem, int len) { int i, c; for(i = 0; i < len; i++) { @@ -613,7 +85,7 @@ static void memtohex(GString *buf, const uint8_t *mem, int len) g_string_append_c(buf, '\0'); } -static void hextomem(GByteArray *mem, const char *buf, int len) +void gdb_hextomem(GByteArray *mem, const char *buf, int len) { int i; @@ -658,7 +130,7 @@ static void hexdump(const char *buf, int len, } /* return -1 if error, 0 if OK */ -static int put_packet_binary(const char *buf, int len, bool dump) +int gdb_put_packet_binary(const char *buf, int len, bool dump) { int csum, i; uint8_t footer[3]; @@ -682,37 +154,31 @@ static int put_packet_binary(const char *buf, int len, bool dump) footer[2] = tohex((csum) & 0xf); g_byte_array_append(gdbserver_state.last_packet, footer, 3); - put_buffer(gdbserver_state.last_packet->data, + gdb_put_buffer(gdbserver_state.last_packet->data, gdbserver_state.last_packet->len); -#ifdef CONFIG_USER_ONLY - i = get_char(); - if (i < 0) - return -1; - if (i == '+') + if (gdb_got_immediate_ack()) { break; -#else - break; -#endif + } } return 0; } /* return -1 if error, 0 if OK */ -static int put_packet(const char *buf) +int gdb_put_packet(const char *buf) { trace_gdbstub_io_reply(buf); - return put_packet_binary(buf, strlen(buf), false); + return gdb_put_packet_binary(buf, strlen(buf), false); } -static void put_strbuf(void) +void gdb_put_strbuf(void) { - put_packet(gdbserver_state.str_buf->str); + gdb_put_packet(gdbserver_state.str_buf->str); } /* Encode data using the encoding for 'x' packets. */ -static void memtox(GString *buf, const char *mem, int len) +void gdb_memtox(GString *buf, const char *mem, int len) { char c; @@ -732,16 +198,19 @@ static void memtox(GString *buf, const char *mem, int len) static uint32_t gdb_get_cpu_pid(CPUState *cpu) { - /* TODO: In user mode, we should use the task state PID */ +#ifdef CONFIG_USER_ONLY + return getpid(); +#else if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) { /* Return the default process' PID */ int index = gdbserver_state.process_num - 1; return gdbserver_state.processes[index].pid; } return cpu->cluster_index + 1; +#endif } -static GDBProcess *gdb_get_process(uint32_t pid) +GDBProcess *gdb_get_process(uint32_t pid) { int i; @@ -769,7 +238,7 @@ static CPUState *find_cpu(uint32_t thread_id) CPUState *cpu; CPU_FOREACH(cpu) { - if (cpu_gdb_index(cpu) == thread_id) { + if (gdb_get_cpu_index(cpu) == thread_id) { return cpu; } } @@ -777,7 +246,7 @@ static CPUState *find_cpu(uint32_t thread_id) return NULL; } -static CPUState *get_first_cpu_in_process(GDBProcess *process) +CPUState *gdb_get_first_cpu_in_process(GDBProcess *process) { CPUState *cpu; @@ -823,7 +292,7 @@ static CPUState *gdb_next_attached_cpu(CPUState *cpu) } /* Return the first attached cpu */ -static CPUState *gdb_first_attached_cpu(void) +CPUState *gdb_first_attached_cpu(void) { CPUState *cpu = first_cpu; GDBProcess *process = gdb_get_cpu_process(cpu); @@ -855,7 +324,7 @@ static CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid) return NULL; } - return get_first_cpu_in_process(process); + return gdb_get_first_cpu_in_process(process); } else { /* a specific thread */ cpu = find_cpu(tid); @@ -881,79 +350,184 @@ static CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid) 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(process); + CPUState *cpu = gdb_get_first_cpu_in_process(process); CPUClass *cc = CPU_GET_CLASS(cpu); + GDBRegisterState *r; + size_t len; - len = 0; - while (p[len] && p[len] != ':') - len++; - *newp = p + len; + /* + * qXfer:features:read:ANNEX:OFFSET,LENGTH' + * ^p ^newp + */ + char *term = strchr(p, ':'); + *newp = term + 1; + len = term - p; - name = NULL; + /* Is it the main target xml? */ if (strncmp(p, "target.xml", len) == 0) { - char *buf = process->target_xml; - const size_t buf_sz = sizeof(process->target_xml); + if (!process->target_xml) { + g_autoptr(GPtrArray) xml = g_ptr_array_new_with_free_func(g_free); - /* Generate the XML description for this CPU. */ - if (!buf[0]) { - GDBRegisterState *r; + g_ptr_array_add( + xml, + g_strdup("" + "" + "")); - pstrcat(buf, buf_sz, - "" - "" - ""); if (cc->gdb_arch_name) { - gchar *arch = cc->gdb_arch_name(cpu); - pstrcat(buf, buf_sz, ""); - pstrcat(buf, buf_sz, arch); - pstrcat(buf, buf_sz, ""); - g_free(arch); + g_ptr_array_add( + xml, + g_markup_printf_escaped("%s", + cc->gdb_arch_name(cpu))); } - pstrcat(buf, buf_sz, "gdb_core_xml_file); - pstrcat(buf, buf_sz, "\"/>"); - for (r = cpu->gdb_regs; r; r = r->next) { - pstrcat(buf, buf_sz, "xml); - pstrcat(buf, buf_sz, "\"/>"); + for (guint i = 0; i < cpu->gdb_regs->len; i++) { + r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); + g_ptr_array_add( + xml, + g_markup_printf_escaped("", + r->feature->xmlname)); } - pstrcat(buf, buf_sz, ""); - } - return buf; - } - if (cc->gdb_get_dynamic_xml) { - char *xmlname = g_strndup(p, len); - const char *xml = cc->gdb_get_dynamic_xml(cpu, xmlname); + g_ptr_array_add(xml, g_strdup("")); + g_ptr_array_add(xml, NULL); - g_free(xmlname); - if (xml) { - return xml; + process->target_xml = g_strjoinv(NULL, (void *)xml->pdata); + } + return process->target_xml; + } + /* Is it one of the features? */ + for (guint i = 0; i < cpu->gdb_regs->len; i++) { + r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); + if (strncmp(p, r->feature->xmlname, len) == 0) { + return r->feature->xml; } } - for (i = 0; ; i++) { - name = xml_builtin[i][0]; - if (!name || (strncmp(name, p, len) == 0 && strlen(name) == len)) - break; - } - return name ? xml_builtin[i][1] : NULL; + + /* failed */ + return NULL; } -static int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) +void gdb_feature_builder_init(GDBFeatureBuilder *builder, GDBFeature *feature, + const char *name, const char *xmlname, + int base_reg) +{ + char *header = g_markup_printf_escaped( + "" + "" + "", + name); + + builder->feature = feature; + builder->xml = g_ptr_array_new(); + g_ptr_array_add(builder->xml, header); + builder->regs = g_ptr_array_new(); + builder->base_reg = base_reg; + feature->xmlname = xmlname; + feature->name = name; +} + +void gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder, + const char *format, ...) +{ + va_list ap; + va_start(ap, format); + g_ptr_array_add(builder->xml, g_markup_vprintf_escaped(format, ap)); + va_end(ap); +} + +void gdb_feature_builder_append_reg(const GDBFeatureBuilder *builder, + const char *name, + int bitsize, + int regnum, + const char *type, + const char *group) +{ + if (builder->regs->len <= regnum) { + g_ptr_array_set_size(builder->regs, regnum + 1); + } + + builder->regs->pdata[regnum] = (gpointer *)name; + + if (group) { + gdb_feature_builder_append_tag( + builder, + "", + name, bitsize, builder->base_reg + regnum, type, group); + } else { + gdb_feature_builder_append_tag( + builder, + "", + name, bitsize, builder->base_reg + regnum, type); + } +} + +void gdb_feature_builder_end(const GDBFeatureBuilder *builder) +{ + g_ptr_array_add(builder->xml, (void *)""); + g_ptr_array_add(builder->xml, NULL); + + builder->feature->xml = g_strjoinv(NULL, (void *)builder->xml->pdata); + + for (guint i = 0; i < builder->xml->len - 2; i++) { + g_free(g_ptr_array_index(builder->xml, i)); + } + + g_ptr_array_free(builder->xml, TRUE); + + builder->feature->num_regs = builder->regs->len; + builder->feature->regs = (void *)g_ptr_array_free(builder->regs, FALSE); +} + +const GDBFeature *gdb_find_static_feature(const char *xmlname) +{ + const GDBFeature *feature; + + for (feature = gdb_static_features; feature->xmlname; feature++) { + if (!strcmp(feature->xmlname, xmlname)) { + return feature; + } + } + + g_assert_not_reached(); +} + +GArray *gdb_get_register_list(CPUState *cpu) +{ + GArray *results = g_array_new(true, true, sizeof(GDBRegDesc)); + + /* registers are only available once the CPU is initialised */ + if (!cpu->gdb_regs) { + return results; + } + + for (int f = 0; f < cpu->gdb_regs->len; f++) { + GDBRegisterState *r = &g_array_index(cpu->gdb_regs, GDBRegisterState, f); + for (int i = 0; i < r->feature->num_regs; i++) { + const char *name = r->feature->regs[i]; + GDBRegDesc desc = { + r->base_reg + i, + name, + r->feature->name + }; + g_array_append_val(results, desc); + } + } + + return results; +} + +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, 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, buf, reg - r->base_reg); + for (guint i = 0; i < cpu->gdb_regs->len; i++) { + r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); + if (r->base_reg <= reg && reg < r->base_reg + r->feature->num_regs) { + return r->get_reg(cpu, buf, reg - r->base_reg); } } return 0; @@ -962,188 +536,97 @@ static int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) static int gdb_write_register(CPUState *cpu, uint8_t *mem_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_write_register(cpu, mem_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->set_reg(env, mem_buf, reg - r->base_reg); + for (guint i = 0; i < cpu->gdb_regs->len; i++) { + r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); + if (r->base_reg <= reg && reg < r->base_reg + r->feature->num_regs) { + return r->set_reg(cpu, mem_buf, reg - r->base_reg); } } return 0; } -/* Register a supplemental set of CPU registers. If g_pos is nonzero it - specifies the first register number and these registers are included in - a standard "g" packet. Direction is relative to gdb, i.e. get_reg is - gdb reading a CPU register, and set_reg is gdb modifying a CPU register. - */ +static void gdb_register_feature(CPUState *cpu, int base_reg, + gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, + const GDBFeature *feature) +{ + GDBRegisterState s = { + .base_reg = base_reg, + .get_reg = get_reg, + .set_reg = set_reg, + .feature = feature + }; + + g_array_append_val(cpu->gdb_regs, s); +} + +void gdb_init_cpu(CPUState *cpu) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + const GDBFeature *feature; + + cpu->gdb_regs = g_array_new(false, false, sizeof(GDBRegisterState)); + + if (cc->gdb_core_xml_file) { + feature = gdb_find_static_feature(cc->gdb_core_xml_file); + gdb_register_feature(cpu, 0, + cc->gdb_read_register, cc->gdb_write_register, + feature); + cpu->gdb_num_regs = cpu->gdb_num_g_regs = feature->num_regs; + } + + if (cc->gdb_num_core_regs) { + cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs; + } +} void gdb_register_coprocessor(CPUState *cpu, gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, - int num_regs, const char *xml, int g_pos) + const GDBFeature *feature, int g_pos) { GDBRegisterState *s; - GDBRegisterState **p; + guint i; + int base_reg = cpu->gdb_num_regs; - p = &cpu->gdb_regs; - while (*p) { + for (i = 0; i < cpu->gdb_regs->len; i++) { /* Check for duplicates. */ - if (strcmp((*p)->xml, xml) == 0) + s = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); + if (s->feature == feature) { return; - p = &(*p)->next; + } } - s = g_new0(GDBRegisterState, 1); - s->base_reg = cpu->gdb_num_regs; - s->num_regs = num_regs; - s->get_reg = get_reg; - s->set_reg = set_reg; - s->xml = xml; + gdb_register_feature(cpu, base_reg, get_reg, set_reg, feature); /* Add to end of list. */ - cpu->gdb_num_regs += num_regs; - *p = s; + cpu->gdb_num_regs += feature->num_regs; if (g_pos) { - if (g_pos != s->base_reg) { + if (g_pos != base_reg) { error_report("Error: Bad gdb register numbering for '%s', " - "expected %d got %d", xml, g_pos, s->base_reg); + "expected %d got %d", feature->xml, g_pos, base_reg); } else { cpu->gdb_num_g_regs = cpu->gdb_num_regs; } } } -#ifndef CONFIG_USER_ONLY -/* Translate GDB watchpoint type to a flags value for cpu_watchpoint_* */ -static inline int xlat_gdb_type(CPUState *cpu, int gdbtype) -{ - static const int xlat[] = { - [GDB_WATCHPOINT_WRITE] = BP_GDB | BP_MEM_WRITE, - [GDB_WATCHPOINT_READ] = BP_GDB | BP_MEM_READ, - [GDB_WATCHPOINT_ACCESS] = BP_GDB | BP_MEM_ACCESS, - }; - - CPUClass *cc = CPU_GET_CLASS(cpu); - int cputype = xlat[gdbtype]; - - if (cc->gdb_stop_before_watchpoint) { - cputype |= BP_STOP_BEFORE_ACCESS; - } - return cputype; -} -#endif - -static int gdb_breakpoint_insert(int type, target_ulong addr, target_ulong len) -{ - CPUState *cpu; - int err = 0; - - if (kvm_enabled()) { - return kvm_insert_breakpoint(gdbserver_state.c_cpu, addr, len, type); - } - - switch (type) { - case GDB_BREAKPOINT_SW: - case GDB_BREAKPOINT_HW: - CPU_FOREACH(cpu) { - err = cpu_breakpoint_insert(cpu, addr, BP_GDB, NULL); - if (err) { - break; - } - } - return err; -#ifndef CONFIG_USER_ONLY - case GDB_WATCHPOINT_WRITE: - case GDB_WATCHPOINT_READ: - case GDB_WATCHPOINT_ACCESS: - CPU_FOREACH(cpu) { - err = cpu_watchpoint_insert(cpu, addr, len, - xlat_gdb_type(cpu, type), NULL); - if (err) { - break; - } - } - return err; -#endif - default: - return -ENOSYS; - } -} - -static int gdb_breakpoint_remove(int type, target_ulong addr, target_ulong len) -{ - CPUState *cpu; - int err = 0; - - if (kvm_enabled()) { - return kvm_remove_breakpoint(gdbserver_state.c_cpu, addr, len, type); - } - - switch (type) { - case GDB_BREAKPOINT_SW: - case GDB_BREAKPOINT_HW: - CPU_FOREACH(cpu) { - err = cpu_breakpoint_remove(cpu, addr, BP_GDB); - if (err) { - break; - } - } - return err; -#ifndef CONFIG_USER_ONLY - case GDB_WATCHPOINT_WRITE: - case GDB_WATCHPOINT_READ: - case GDB_WATCHPOINT_ACCESS: - CPU_FOREACH(cpu) { - err = cpu_watchpoint_remove(cpu, addr, len, - xlat_gdb_type(cpu, type)); - if (err) - break; - } - return err; -#endif - default: - return -ENOSYS; - } -} - -static inline void gdb_cpu_breakpoint_remove_all(CPUState *cpu) -{ - cpu_breakpoint_remove_all(cpu, BP_GDB); -#ifndef CONFIG_USER_ONLY - cpu_watchpoint_remove_all(cpu, BP_GDB); -#endif -} - static void gdb_process_breakpoint_remove_all(GDBProcess *p) { - CPUState *cpu = get_first_cpu_in_process(p); + CPUState *cpu = gdb_get_first_cpu_in_process(p); while (cpu) { - gdb_cpu_breakpoint_remove_all(cpu); + gdb_breakpoint_remove_all(cpu); cpu = gdb_next_cpu_in_process(cpu); } } -static void gdb_breakpoint_remove_all(void) -{ - CPUState *cpu; - if (kvm_enabled()) { - kvm_remove_all_breakpoints(gdbserver_state.c_cpu); - return; - } - - CPU_FOREACH(cpu) { - gdb_cpu_breakpoint_remove_all(cpu); - } -} - -static void gdb_set_cpu_pc(target_ulong pc) +static void gdb_set_cpu_pc(vaddr pc) { CPUState *cpu = gdbserver_state.c_cpu; @@ -1151,23 +634,16 @@ static void gdb_set_cpu_pc(target_ulong pc) cpu_set_pc(cpu, pc); } -static void gdb_append_thread_id(CPUState *cpu, GString *buf) +void gdb_append_thread_id(CPUState *cpu, GString *buf) { if (gdbserver_state.multiprocess) { g_string_append_printf(buf, "p%02x.%02x", - gdb_get_cpu_pid(cpu), cpu_gdb_index(cpu)); + gdb_get_cpu_pid(cpu), gdb_get_cpu_index(cpu)); } else { - g_string_append_printf(buf, "%02x", cpu_gdb_index(cpu)); + g_string_append_printf(buf, "%02x", gdb_get_cpu_index(cpu)); } } -typedef enum GDBThreadIdKind { - GDB_ONE_THREAD = 0, - GDB_ALL_THREADS, /* One process, all threads */ - GDB_ALL_PROCESSES, - GDB_READ_THREAD_ERR -} GDBThreadIdKind; - static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, uint32_t *pid, uint32_t *tid) { @@ -1185,7 +661,7 @@ static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, /* Skip '.' */ buf++; } else { - p = 1; + p = 0; } ret = qemu_strtoul(buf, &buf, 16, &t); @@ -1224,24 +700,14 @@ static int gdb_handle_vcont(const char *p) { int res, signal = 0; char cur_action; - char *newstates; unsigned long tmp; uint32_t pid, tid; GDBProcess *process; CPUState *cpu; GDBThreadIdKind kind; -#ifdef CONFIG_USER_ONLY - int max_cpus = 1; /* global variable max_cpus exists only in system mode */ - - CPU_FOREACH(cpu) { - max_cpus = max_cpus <= cpu->cpu_index ? cpu->cpu_index + 1 : max_cpus; - } -#else - MachineState *ms = MACHINE(qdev_get_machine()); - unsigned int max_cpus = ms->smp.max_cpus; -#endif + unsigned int max_cpus = gdb_get_max_cpus(); /* uninitialised CPUs stay 0 */ - newstates = g_new0(char, max_cpus); + g_autofree char *newstates = g_new0(char, max_cpus); /* mark valid CPUs with 1 */ CPU_FOREACH(cpu) { @@ -1255,10 +721,18 @@ static int gdb_handle_vcont(const char *p) * or incorrect parameters passed. */ res = 0; + + /* + * target_count and last_target keep track of how many CPUs we are going to + * step or resume, and a pointer to the state structure of one of them, + * respectively + */ + int target_count = 0; + CPUState *last_target = NULL; + while (*p) { if (*p++ != ';') { - res = -ENOTSUP; - goto out; + return -ENOTSUP; } cur_action = *p++; @@ -1266,13 +740,12 @@ static int gdb_handle_vcont(const char *p) cur_action = qemu_tolower(cur_action); res = qemu_strtoul(p, &p, 16, &tmp); if (res) { - goto out; + return res; } signal = gdb_signal_to_target(tmp); } else if (cur_action != 'c' && cur_action != 's') { /* unknown/invalid/unsupported command */ - res = -ENOTSUP; - goto out; + return -ENOTSUP; } if (*p == '\0' || *p == ';') { @@ -1285,20 +758,21 @@ static int gdb_handle_vcont(const char *p) } else if (*p++ == ':') { kind = read_thread_id(p, &p, &pid, &tid); } else { - res = -ENOTSUP; - goto out; + return -ENOTSUP; } switch (kind) { case GDB_READ_THREAD_ERR: - res = -EINVAL; - goto out; + return -EINVAL; case GDB_ALL_PROCESSES: cpu = gdb_first_attached_cpu(); while (cpu) { if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; + + target_count++; + last_target = cpu; } cpu = gdb_next_attached_cpu(cpu); @@ -1309,14 +783,16 @@ static int gdb_handle_vcont(const char *p) process = gdb_get_process(pid); if (!process->attached) { - res = -EINVAL; - goto out; + return -EINVAL; } - cpu = get_first_cpu_in_process(process); + cpu = gdb_get_first_cpu_in_process(process); while (cpu) { if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; + + target_count++; + last_target = cpu; } cpu = gdb_next_cpu_in_process(cpu); @@ -1328,40 +804,36 @@ static int gdb_handle_vcont(const char *p) /* invalid CPU/thread specified */ if (!cpu) { - res = -EINVAL; - goto out; + return -EINVAL; } /* only use if no previous match occourred */ if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; + + target_count++; + last_target = cpu; } break; } } + + /* + * if we're about to resume a specific set of CPUs/threads, make it so that + * in case execution gets interrupted, we can send GDB a stop reply with a + * correct value. it doesn't really matter which CPU we tell GDB the signal + * happened in (VM pauses stop all of them anyway), so long as it is one of + * the ones we resumed/single stepped here. + */ + if (target_count > 0) { + gdbserver_state.c_cpu = last_target; + } + gdbserver_state.signal = signal; gdb_continue_partial(newstates); - -out: - g_free(newstates); - return res; } -typedef union GdbCmdVariant { - const char *data; - uint8_t opcode; - unsigned long val_ul; - unsigned long long val_ull; - struct { - GDBThreadIdKind kind; - uint32_t pid; - uint32_t tid; - } thread_id; -} GdbCmdVariant; - -#define get_param(p, i) (&g_array_index(p, GdbCmdVariant, i)) - static const char *cmd_next_param(const char *param, const char delimiter) { static const char all_delimiters[] = ",;:="; @@ -1451,6 +923,10 @@ typedef void (*GdbCmdHandler)(GArray *params, void *user_ctx); /* * cmd_startswith -> cmd is compared using startswith * + * allow_stop_reply -> true iff the gdbstub can respond to this command with a + * "stop reply" packet. The list of commands that accept such response is + * defined at the GDB Remote Serial Protocol documentation. see: + * https://sourceware.org/gdb/onlinedocs/gdb/Stop-Reply-Packets.html#Stop-Reply-Packets. * * schema definitions: * Each schema parameter entry consists of 2 chars, @@ -1476,6 +952,7 @@ typedef struct GdbCmdParseEntry { const char *cmd; bool cmd_startswith; const char *schema; + bool allow_stop_reply; } GdbCmdParseEntry; static inline int startswith(const char *string, const char *pattern) @@ -1483,7 +960,7 @@ static inline int startswith(const char *string, const char *pattern) return !strncmp(string, pattern, strlen(pattern)); } -static int process_string_cmd(void *user_ctx, const char *data, +static int process_string_cmd(const char *data, const GdbCmdParseEntry *cmds, int num_cmds) { int i; @@ -1509,7 +986,8 @@ static int process_string_cmd(void *user_ctx, const char *data, } } - cmd->handler(params, user_ctx); + gdbserver_state.allow_stop_reply = cmd->allow_stop_reply; + cmd->handler(params, NULL); return 0; } @@ -1527,8 +1005,8 @@ static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) /* 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(NULL, data, cmd, 1)) { - put_packet(""); + if (process_string_cmd(data, cmd, 1)) { + gdb_put_packet(""); } } @@ -1539,13 +1017,19 @@ static void handle_detach(GArray *params, void *user_ctx) if (gdbserver_state.multiprocess) { if (!params->len) { - put_packet("E22"); + gdb_put_packet("E22"); return; } pid = get_param(params, 0)->val_ul; } +#ifdef CONFIG_USER_ONLY + if (gdb_handle_detach_user(pid)) { + return; + } +#endif + process = gdb_get_process(pid); gdb_process_breakpoint_remove_all(process); process->attached = false; @@ -1560,10 +1044,10 @@ static void handle_detach(GArray *params, void *user_ctx) if (!gdbserver_state.c_cpu) { /* No more process attached */ - gdb_syscall_mode = GDB_SYS_DISABLED; + gdb_disable_syscalls(); gdb_continue(); } - put_packet("OK"); + gdb_put_packet("OK"); } static void handle_thread_alive(GArray *params, void *user_ctx) @@ -1571,23 +1055,23 @@ static void handle_thread_alive(GArray *params, void *user_ctx) CPUState *cpu; if (!params->len) { - put_packet("E22"); + gdb_put_packet("E22"); return; } if (get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet("E22"); + gdb_put_packet("E22"); return; } cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid, get_param(params, 0)->thread_id.tid); if (!cpu) { - put_packet("E22"); + gdb_put_packet("E22"); return; } - put_packet("OK"); + gdb_put_packet("OK"); } static void handle_continue(GArray *params, void *user_ctx) @@ -1621,27 +1105,34 @@ static void handle_cont_with_sig(GArray *params, void *user_ctx) static void handle_set_thread(GArray *params, void *user_ctx) { + uint32_t pid, tid; CPUState *cpu; if (params->len != 2) { - put_packet("E22"); + gdb_put_packet("E22"); return; } if (get_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet("E22"); + gdb_put_packet("E22"); return; } if (get_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) { - put_packet("OK"); + gdb_put_packet("OK"); return; } - cpu = gdb_get_cpu(get_param(params, 1)->thread_id.pid, - get_param(params, 1)->thread_id.tid); + pid = get_param(params, 1)->thread_id.pid; + tid = get_param(params, 1)->thread_id.tid; +#ifdef CONFIG_USER_ONLY + if (gdb_handle_set_thread_user(pid, tid)) { + return; + } +#endif + cpu = gdb_get_cpu(pid, tid); if (!cpu) { - put_packet("E22"); + gdb_put_packet("E22"); return; } @@ -1652,14 +1143,14 @@ static void handle_set_thread(GArray *params, void *user_ctx) switch (get_param(params, 0)->opcode) { case 'c': gdbserver_state.c_cpu = cpu; - put_packet("OK"); + gdb_put_packet("OK"); break; case 'g': gdbserver_state.g_cpu = cpu; - put_packet("OK"); + gdb_put_packet("OK"); break; default: - put_packet("E22"); + gdb_put_packet("E22"); break; } } @@ -1669,22 +1160,23 @@ static void handle_insert_bp(GArray *params, void *user_ctx) int res; if (params->len != 3) { - put_packet("E22"); + gdb_put_packet("E22"); return; } - res = gdb_breakpoint_insert(get_param(params, 0)->val_ul, + res = gdb_breakpoint_insert(gdbserver_state.c_cpu, + get_param(params, 0)->val_ul, get_param(params, 1)->val_ull, get_param(params, 2)->val_ull); if (res >= 0) { - put_packet("OK"); + gdb_put_packet("OK"); return; } else if (res == -ENOSYS) { - put_packet(""); + gdb_put_packet(""); return; } - put_packet("E22"); + gdb_put_packet("E22"); } static void handle_remove_bp(GArray *params, void *user_ctx) @@ -1692,22 +1184,23 @@ static void handle_remove_bp(GArray *params, void *user_ctx) int res; if (params->len != 3) { - put_packet("E22"); + gdb_put_packet("E22"); return; } - res = gdb_breakpoint_remove(get_param(params, 0)->val_ul, + res = gdb_breakpoint_remove(gdbserver_state.c_cpu, + get_param(params, 0)->val_ul, get_param(params, 1)->val_ull, get_param(params, 2)->val_ull); if (res >= 0) { - put_packet("OK"); + gdb_put_packet("OK"); return; } else if (res == -ENOSYS) { - put_packet(""); + gdb_put_packet(""); return; } - put_packet("E22"); + gdb_put_packet("E22"); } /* @@ -1725,34 +1218,24 @@ static void handle_set_reg(GArray *params, void *user_ctx) { int reg_size; - if (!gdb_has_xml) { - put_packet(""); - return; - } - if (params->len != 2) { - put_packet("E22"); + gdb_put_packet("E22"); return; } reg_size = strlen(get_param(params, 1)->data) / 2; - hextomem(gdbserver_state.mem_buf, get_param(params, 1)->data, reg_size); + gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 1)->data, reg_size); gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data, get_param(params, 0)->val_ull); - put_packet("OK"); + gdb_put_packet("OK"); } static void handle_get_reg(GArray *params, void *user_ctx) { int reg_size; - if (!gdb_has_xml) { - put_packet(""); - return; - } - if (!params->len) { - put_packet("E14"); + gdb_put_packet("E14"); return; } @@ -1760,75 +1243,77 @@ static void handle_get_reg(GArray *params, void *user_ctx) gdbserver_state.mem_buf, get_param(params, 0)->val_ull); if (!reg_size) { - put_packet("E14"); + gdb_put_packet("E14"); return; } else { g_byte_array_set_size(gdbserver_state.mem_buf, reg_size); } - memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, reg_size); - put_strbuf(); + gdb_memtohex(gdbserver_state.str_buf, + gdbserver_state.mem_buf->data, reg_size); + gdb_put_strbuf(); } static void handle_write_mem(GArray *params, void *user_ctx) { if (params->len != 3) { - put_packet("E22"); + gdb_put_packet("E22"); return; } - /* hextomem() reads 2*len bytes */ + /* gdb_hextomem() reads 2*len bytes */ if (get_param(params, 1)->val_ull > strlen(get_param(params, 2)->data) / 2) { - put_packet("E22"); + gdb_put_packet("E22"); return; } - hextomem(gdbserver_state.mem_buf, get_param(params, 2)->data, - get_param(params, 1)->val_ull); - if (target_memory_rw_debug(gdbserver_state.g_cpu, - get_param(params, 0)->val_ull, - gdbserver_state.mem_buf->data, - gdbserver_state.mem_buf->len, true)) { - put_packet("E14"); + gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 2)->data, + get_param(params, 1)->val_ull); + if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, + get_param(params, 0)->val_ull, + gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len, true)) { + gdb_put_packet("E14"); return; } - put_packet("OK"); + gdb_put_packet("OK"); } static void handle_read_mem(GArray *params, void *user_ctx) { if (params->len != 2) { - put_packet("E22"); + gdb_put_packet("E22"); return; } - /* memtohex() doubles the required space */ + /* gdb_memtohex() doubles the required space */ if (get_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) { - put_packet("E22"); + gdb_put_packet("E22"); return; } g_byte_array_set_size(gdbserver_state.mem_buf, get_param(params, 1)->val_ull); - if (target_memory_rw_debug(gdbserver_state.g_cpu, - get_param(params, 0)->val_ull, - gdbserver_state.mem_buf->data, - gdbserver_state.mem_buf->len, false)) { - put_packet("E14"); + if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, + get_param(params, 0)->val_ull, + gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len, false)) { + gdb_put_packet("E14"); return; } - memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, + gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len); - put_strbuf(); + gdb_put_strbuf(); } static void handle_write_all_regs(GArray *params, void *user_ctx) { - target_ulong addr, len; + int reg_id; + size_t len; uint8_t *registers; int reg_size; @@ -1838,62 +1323,42 @@ static void handle_write_all_regs(GArray *params, void *user_ctx) cpu_synchronize_state(gdbserver_state.g_cpu); len = strlen(get_param(params, 0)->data) / 2; - hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len); + gdb_hextomem(gdbserver_state.mem_buf, get_param(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(gdbserver_state.g_cpu, registers, addr); + for (reg_id = 0; + reg_id < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0; + reg_id++) { + reg_size = gdb_write_register(gdbserver_state.g_cpu, registers, reg_id); len -= reg_size; registers += reg_size; } - put_packet("OK"); + gdb_put_packet("OK"); } static void handle_read_all_regs(GArray *params, void *user_ctx) { - target_ulong addr, len; + int reg_id; + size_t len; cpu_synchronize_state(gdbserver_state.g_cpu); g_byte_array_set_size(gdbserver_state.mem_buf, 0); len = 0; - for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs; addr++) { + for (reg_id = 0; reg_id < gdbserver_state.g_cpu->gdb_num_g_regs; reg_id++) { len += gdb_read_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf, - addr); + reg_id); } g_assert(len == gdbserver_state.mem_buf->len); - memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len); - put_strbuf(); + gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len); + gdb_put_strbuf(); } -static void handle_file_io(GArray *params, void *user_ctx) -{ - if (params->len >= 1 && gdbserver_state.current_syscall_cb) { - target_ulong ret, err; - - ret = (target_ulong)get_param(params, 0)->val_ull; - if (params->len >= 2) { - err = (target_ulong)get_param(params, 1)->val_ull; - } else { - err = 0; - } - gdbserver_state.current_syscall_cb(gdbserver_state.c_cpu, ret, err); - gdbserver_state.current_syscall_cb = NULL; - } - - if (params->len >= 3 && get_param(params, 2)->opcode == (uint8_t)'C') { - put_packet("T02"); - return; - } - - gdb_continue(); -} static void handle_step(GArray *params, void *user_ctx) { if (params->len) { - gdb_set_cpu_pc((target_ulong)get_param(params, 0)->val_ull); + gdb_set_cpu_pc(get_param(params, 0)->val_ull); } cpu_single_step(gdbserver_state.c_cpu, gdbserver_state.sstep_flags); @@ -1902,8 +1367,8 @@ static void handle_step(GArray *params, void *user_ctx) static void handle_backward(GArray *params, void *user_ctx) { - if (!stub_can_reverse()) { - put_packet("E22"); + if (!gdb_can_reverse()) { + gdb_put_packet("E22"); } if (params->len == 1) { switch (get_param(params, 0)->opcode) { @@ -1911,26 +1376,26 @@ static void handle_backward(GArray *params, void *user_ctx) if (replay_reverse_step()) { gdb_continue(); } else { - put_packet("E14"); + gdb_put_packet("E14"); } return; case 'c': if (replay_reverse_continue()) { gdb_continue(); } else { - put_packet("E14"); + gdb_put_packet("E14"); } return; } } /* Default invalid command */ - put_packet(""); + gdb_put_packet(""); } static void handle_v_cont_query(GArray *params, void *user_ctx) { - put_packet("vCont;c;C;s;S"); + gdb_put_packet("vCont;c;C;s;S"); } static void handle_v_cont(GArray *params, void *user_ctx) @@ -1943,9 +1408,9 @@ static void handle_v_cont(GArray *params, void *user_ctx) res = gdb_handle_vcont(get_param(params, 0)->data); if ((res == -EINVAL) || (res == -ERANGE)) { - put_packet("E22"); + gdb_put_packet("E22"); } else if (res) { - put_packet(""); + gdb_put_packet(""); } } @@ -1964,7 +1429,7 @@ static void handle_v_attach(GArray *params, void *user_ctx) goto cleanup; } - cpu = get_first_cpu_in_process(process); + cpu = gdb_get_first_cpu_in_process(process); if (!cpu) { goto cleanup; } @@ -1973,20 +1438,23 @@ static void handle_v_attach(GArray *params, void *user_ctx) gdbserver_state.g_cpu = cpu; gdbserver_state.c_cpu = cpu; - 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, ';'); + if (gdbserver_state.allow_stop_reply) { + 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, ';'); + gdbserver_state.allow_stop_reply = false; cleanup: - put_strbuf(); + gdb_put_strbuf(); + } } static void handle_v_kill(GArray *params, void *user_ctx) { /* Kill the target */ - put_packet("OK"); + gdb_put_packet("OK"); error_report("QEMU: Terminated via GDBstub"); gdb_exit(0); - exit(0); + gdb_qemu_exit(0); } static const GdbCmdParseEntry gdb_v_commands_table[] = { @@ -2000,12 +1468,14 @@ static const GdbCmdParseEntry gdb_v_commands_table[] = { .handler = handle_v_cont, .cmd = "Cont", .cmd_startswith = 1, + .allow_stop_reply = true, .schema = "s0" }, { .handler = handle_v_attach, .cmd = "Attach;", .cmd_startswith = 1, + .allow_stop_reply = true, .schema = "l0" }, { @@ -2013,6 +1483,36 @@ static const GdbCmdParseEntry gdb_v_commands_table[] = { .cmd = "Kill;", .cmd_startswith = 1 }, +#ifdef CONFIG_USER_ONLY + /* + * Host I/O Packets. See [1] for details. + * [1] https://sourceware.org/gdb/onlinedocs/gdb/Host-I_002fO-Packets.html + */ + { + .handler = gdb_handle_v_file_open, + .cmd = "File:open:", + .cmd_startswith = 1, + .schema = "s,L,L0" + }, + { + .handler = gdb_handle_v_file_close, + .cmd = "File:close:", + .cmd_startswith = 1, + .schema = "l0" + }, + { + .handler = gdb_handle_v_file_pread, + .cmd = "File:pread:", + .cmd_startswith = 1, + .schema = "l,L,L0" + }, + { + .handler = gdb_handle_v_file_readlink, + .cmd = "File:readlink:", + .cmd_startswith = 1, + .schema = "s0" + }, +#endif }; static void handle_v_commands(GArray *params, void *user_ctx) @@ -2021,10 +1521,10 @@ static void handle_v_commands(GArray *params, void *user_ctx) return; } - if (process_string_cmd(NULL, get_param(params, 0)->data, + if (process_string_cmd(get_param(params, 0)->data, gdb_v_commands_table, ARRAY_SIZE(gdb_v_commands_table))) { - put_packet(""); + gdb_put_packet(""); } } @@ -2042,7 +1542,7 @@ static void handle_query_qemu_sstepbits(GArray *params, void *user_ctx) SSTEP_NOTIMER); } - put_strbuf(); + gdb_put_strbuf(); } static void handle_set_qemu_sstep(GArray *params, void *user_ctx) @@ -2056,19 +1556,19 @@ static void handle_set_qemu_sstep(GArray *params, void *user_ctx) new_sstep_flags = get_param(params, 0)->val_ul; if (new_sstep_flags & ~gdbserver_state.supported_sstep_flags) { - put_packet("E22"); + gdb_put_packet("E22"); return; } gdbserver_state.sstep_flags = new_sstep_flags; - put_packet("OK"); + gdb_put_packet("OK"); } static void handle_query_qemu_sstep(GArray *params, void *user_ctx) { g_string_printf(gdbserver_state.str_buf, "0x%x", gdbserver_state.sstep_flags); - put_strbuf(); + gdb_put_strbuf(); } static void handle_query_curr_tid(GArray *params, void *user_ctx) @@ -2082,22 +1582,22 @@ static void handle_query_curr_tid(GArray *params, void *user_ctx) * first thread). */ process = gdb_get_cpu_process(gdbserver_state.g_cpu); - cpu = get_first_cpu_in_process(process); + cpu = gdb_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(); + gdb_put_strbuf(); } static void handle_query_threads(GArray *params, void *user_ctx) { if (!gdbserver_state.query_cpu) { - put_packet("l"); + gdb_put_packet("l"); return; } g_string_assign(gdbserver_state.str_buf, "m"); gdb_append_thread_id(gdbserver_state.query_cpu, gdbserver_state.str_buf); - put_strbuf(); + gdb_put_strbuf(); gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu); } @@ -2114,7 +1614,7 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx) if (!params->len || get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet("E22"); + gdb_put_packet("E22"); return; } @@ -2139,52 +1639,10 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx) cpu->halted ? "halted " : "running"); } trace_gdbstub_op_extra_info(rs->str); - memtohex(gdbserver_state.str_buf, (uint8_t *)rs->str, rs->len); - put_strbuf(); + gdb_memtohex(gdbserver_state.str_buf, (uint8_t *)rs->str, rs->len); + gdb_put_strbuf(); } -#ifdef CONFIG_USER_ONLY -static void handle_query_offsets(GArray *params, void *user_ctx) -{ - TaskState *ts; - - 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(GArray *params, void *user_ctx) -{ - const guint8 zero = 0; - int len; - - if (!params->len) { - put_packet("E22"); - return; - } - - len = strlen(get_param(params, 0)->data); - if (len % 2) { - put_packet("E01"); - return; - } - - g_assert(gdbserver_state.mem_buf->len == 0); - len = len / 2; - hextomem(gdbserver_state.mem_buf, get_param(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 - static void handle_query_supported(GArray *params, void *user_ctx) { CPUClass *cc; @@ -2195,30 +1653,36 @@ static void handle_query_supported(GArray *params, void *user_ctx) g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+"); } - if (stub_can_reverse()) { + if (gdb_can_reverse()) { g_string_append(gdbserver_state.str_buf, ";ReverseStep+;ReverseContinue+"); } -#ifdef CONFIG_USER_ONLY +#if defined(CONFIG_USER_ONLY) +#if defined(CONFIG_LINUX) if (gdbserver_state.c_cpu->opaque) { g_string_append(gdbserver_state.str_buf, ";qXfer:auxv:read+"); } + g_string_append(gdbserver_state.str_buf, ";QCatchSyscalls+"); + + g_string_append(gdbserver_state.str_buf, ";qXfer:siginfo:read+"); +#endif + g_string_append(gdbserver_state.str_buf, ";qXfer:exec-file:read+"); #endif - if (params->len && - strstr(get_param(params, 0)->data, "multiprocess+")) { - gdbserver_state.multiprocess = true; + if (params->len) { + const char *gdb_supported = get_param(params, 0)->data; + + if (strstr(gdb_supported, "multiprocess+")) { + gdbserver_state.multiprocess = true; + } +#if defined(CONFIG_USER_ONLY) + gdb_handle_query_supported_user(gdb_supported); +#endif } -#ifdef TARGET_E2K - // TODO: qXfer:tags:write+ - // TODO: qXfer:packed_tags:read+ - g_string_append(gdbserver_state.str_buf, ";qXfer:tags:read+"); -#endif - g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+"); - put_strbuf(); + gdb_put_strbuf(); } static void handle_query_xfer_features(GArray *params, void *user_ctx) @@ -2230,22 +1694,21 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx) const char *p; if (params->len < 3) { - put_packet("E22"); + gdb_put_packet("E22"); return; } 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_put_packet(""); return; } - gdb_has_xml = true; p = get_param(params, 0)->data; xml = get_feature_xml(p, &p, process); if (!xml) { - put_packet("E00"); + gdb_put_packet("E00"); return; } @@ -2253,7 +1716,7 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx) len = get_param(params, 2)->val_ul; total_len = strlen(xml); if (addr > total_len) { - put_packet("E00"); + gdb_put_packet("E00"); return; } @@ -2263,127 +1726,25 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx) if (len < total_len - addr) { g_string_assign(gdbserver_state.str_buf, "m"); - memtox(gdbserver_state.str_buf, xml + addr, len); + gdb_memtox(gdbserver_state.str_buf, xml + addr, len); } else { g_string_assign(gdbserver_state.str_buf, "l"); - memtox(gdbserver_state.str_buf, xml + addr, total_len - addr); + gdb_memtox(gdbserver_state.str_buf, xml + addr, total_len - addr); } - put_packet_binary(gdbserver_state.str_buf->str, + gdb_put_packet_binary(gdbserver_state.str_buf->str, gdbserver_state.str_buf->len, true); } -#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX_USER) -static void handle_query_xfer_auxv(GArray *params, void *user_ctx) -{ - TaskState *ts; - unsigned long offset, len, saved_auxv, auxv_len; - - if (params->len < 2) { - put_packet("E22"); - return; - } - - offset = get_param(params, 0)->val_ul; - len = get_param(params, 1)->val_ul; - ts = gdbserver_state.c_cpu->opaque; - saved_auxv = ts->info->saved_auxv; - auxv_len = ts->info->auxv_len; - - if (offset >= auxv_len) { - put_packet("E00"); - return; - } - - if (len > (MAX_PACKET_LENGTH - 5) / 2) { - len = (MAX_PACKET_LENGTH - 5) / 2; - } - - if (len < auxv_len - offset) { - g_string_assign(gdbserver_state.str_buf, "m"); - } else { - g_string_assign(gdbserver_state.str_buf, "l"); - len = auxv_len - offset; - } - - g_byte_array_set_size(gdbserver_state.mem_buf, len); - if (target_memory_rw_debug(gdbserver_state.g_cpu, saved_auxv + offset, - gdbserver_state.mem_buf->data, len, false)) { - put_packet("E14"); - return; - } - - memtox(gdbserver_state.str_buf, - (const char *)gdbserver_state.mem_buf->data, len); - put_packet_binary(gdbserver_state.str_buf->str, - gdbserver_state.str_buf->len, true); -} -#endif - -static void handle_query_attached(GArray *params, void *user_ctx) -{ - put_packet(GDB_ATTACHED); -} - static void handle_query_qemu_supported(GArray *params, void *user_ctx) { g_string_printf(gdbserver_state.str_buf, "sstepbits;sstep"); #ifndef CONFIG_USER_ONLY g_string_append(gdbserver_state.str_buf, ";PhyMemMode"); #endif - put_strbuf(); + gdb_put_strbuf(); } -#ifndef CONFIG_USER_ONLY -static void handle_query_qemu_phy_mem_mode(GArray *params, - void *user_ctx) -{ - g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode); - put_strbuf(); -} - -static void handle_set_qemu_phy_mem_mode(GArray *params, void *user_ctx) -{ - if (!params->len) { - put_packet("E22"); - return; - } - - if (!get_param(params, 0)->val_ul) { - phy_memory_mode = 0; - } else { - phy_memory_mode = 1; - } - put_packet("OK"); -} -#endif - -#ifdef TARGET_E2K -static void handle_query_e2k_tags_read(GArray *params, void *user_ctx) -{ - E2KCPU *cpu = E2K_CPU(gdbserver_state.g_cpu); - CPUE2KState *env = &cpu->env; - target_ulong addr = get_param(params, 0)->val_ull; - unsigned long len = get_param(params, 1)->val_ul; - unsigned int i; - int tags = 0; - - g_string_assign(gdbserver_state.str_buf, "l"); - if (env->psp.base <= addr && addr < (env->psp.base + env->psp.size)) { - target_ulong offset = addr - env->psp.base; - tags = cpu_ldub_data(env, env->psp.base_tag + offset / 8); - } - - for (i = 0; i < len; i++) { - int tag = (tags >> (i * 2)) & 0x3; - g_string_append_c(gdbserver_state.str_buf, tag); - } - - put_packet_binary(gdbserver_state.str_buf->str, - gdbserver_state.str_buf->len, true); -} -#endif - static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = { /* Order is important if has same prefix */ { @@ -2423,12 +1784,12 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { }, #ifdef CONFIG_USER_ONLY { - .handler = handle_query_offsets, + .handler = gdb_handle_query_offsets, .cmd = "Offsets", }, #else { - .handler = handle_query_rcmd, + .handler = gdb_handle_query_rcmd, .cmd = "Rcmd,", .cmd_startswith = 1, .schema = "s0" @@ -2451,21 +1812,35 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { .cmd_startswith = 1, .schema = "s:l,l0" }, -#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX_USER) +#if defined(CONFIG_USER_ONLY) +#if defined(CONFIG_LINUX) { - .handler = handle_query_xfer_auxv, + .handler = gdb_handle_query_xfer_auxv, .cmd = "Xfer:auxv:read::", .cmd_startswith = 1, .schema = "l,l0" }, + { + .handler = gdb_handle_query_xfer_siginfo, + .cmd = "Xfer:siginfo:read::", + .cmd_startswith = 1, + .schema = "l,l0" + }, #endif { - .handler = handle_query_attached, + .handler = gdb_handle_query_xfer_exec_file, + .cmd = "Xfer:exec-file:read:", + .cmd_startswith = 1, + .schema = "l:l,l0" + }, +#endif + { + .handler = gdb_handle_query_attached, .cmd = "Attached:", .cmd_startswith = 1 }, { - .handler = handle_query_attached, + .handler = gdb_handle_query_attached, .cmd = "Attached", }, { @@ -2474,18 +1849,10 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { }, #ifndef CONFIG_USER_ONLY { - .handler = handle_query_qemu_phy_mem_mode, + .handler = gdb_handle_query_qemu_phy_mem_mode, .cmd = "qemu.PhyMemMode", }, #endif -#ifdef TARGET_E2K - { - .handler = handle_query_e2k_tags_read, - .cmd = "Xfer:tags:read::", - .cmd_startswith = 1, - .schema = "L,l0", - }, -#endif }; static const GdbCmdParseEntry gdb_gen_set_table[] = { @@ -2498,12 +1865,20 @@ static const GdbCmdParseEntry gdb_gen_set_table[] = { }, #ifndef CONFIG_USER_ONLY { - .handler = handle_set_qemu_phy_mem_mode, + .handler = gdb_handle_set_qemu_phy_mem_mode, .cmd = "qemu.PhyMemMode:", .cmd_startswith = 1, .schema = "l0" }, #endif +#if defined(CONFIG_USER_ONLY) + { + .handler = gdb_handle_set_catch_syscalls, + .cmd = "CatchSyscalls:", + .cmd_startswith = 1, + .schema = "s0", + }, +#endif }; static void handle_gen_query(GArray *params, void *user_ctx) @@ -2512,16 +1887,16 @@ static void handle_gen_query(GArray *params, void *user_ctx) return; } - if (!process_string_cmd(NULL, get_param(params, 0)->data, + if (!process_string_cmd(get_param(params, 0)->data, gdb_gen_query_set_common_table, ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (process_string_cmd(NULL, get_param(params, 0)->data, + if (process_string_cmd(get_param(params, 0)->data, gdb_gen_query_table, ARRAY_SIZE(gdb_gen_query_table))) { - put_packet(""); + gdb_put_packet(""); } } @@ -2531,31 +1906,34 @@ static void handle_gen_set(GArray *params, void *user_ctx) return; } - if (!process_string_cmd(NULL, get_param(params, 0)->data, + if (!process_string_cmd(get_param(params, 0)->data, gdb_gen_query_set_common_table, ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (process_string_cmd(NULL, get_param(params, 0)->data, + if (process_string_cmd(get_param(params, 0)->data, gdb_gen_set_table, ARRAY_SIZE(gdb_gen_set_table))) { - put_packet(""); + gdb_put_packet(""); } } static void handle_target_halt(GArray *params, void *user_ctx) { - 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(); + if (gdbserver_state.allow_stop_reply) { + 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, ';'); + gdb_put_strbuf(); + gdbserver_state.allow_stop_reply = false; + } /* * Remove all the breakpoints when this query is issued, * because gdb is doing an initial connect and the state * should be cleaned up. */ - gdb_breakpoint_remove_all(); + gdb_breakpoint_remove_all(gdbserver_state.c_cpu); } static int gdb_handle_packet(const char *line_buf) @@ -2566,14 +1944,15 @@ static int gdb_handle_packet(const char *line_buf) switch (line_buf[0]) { case '!': - put_packet("OK"); + gdb_put_packet("OK"); break; case '?': { static const GdbCmdParseEntry target_halted_cmd_desc = { .handler = handle_target_halt, .cmd = "?", - .cmd_startswith = 1 + .cmd_startswith = 1, + .allow_stop_reply = true, }; cmd_parser = &target_halted_cmd_desc; } @@ -2584,6 +1963,7 @@ static int gdb_handle_packet(const char *line_buf) .handler = handle_continue, .cmd = "c", .cmd_startswith = 1, + .allow_stop_reply = true, .schema = "L0" }; cmd_parser = &continue_cmd_desc; @@ -2595,6 +1975,7 @@ static int gdb_handle_packet(const char *line_buf) .handler = handle_cont_with_sig, .cmd = "C", .cmd_startswith = 1, + .allow_stop_reply = true, .schema = "l0" }; cmd_parser = &cont_with_sig_cmd_desc; @@ -2615,7 +1996,8 @@ static int gdb_handle_packet(const char *line_buf) /* Kill the target */ error_report("QEMU: Terminated via GDBstub"); gdb_exit(0); - exit(0); + gdb_qemu_exit(0); + break; case 'D': { static const GdbCmdParseEntry detach_cmd_desc = { @@ -2633,6 +2015,7 @@ static int gdb_handle_packet(const char *line_buf) .handler = handle_step, .cmd = "s", .cmd_startswith = 1, + .allow_stop_reply = true, .schema = "L0" }; cmd_parser = &step_cmd_desc; @@ -2644,6 +2027,7 @@ static int gdb_handle_packet(const char *line_buf) .handler = handle_backward, .cmd = "b", .cmd_startswith = 1, + .allow_stop_reply = true, .schema = "o0" }; cmd_parser = &backward_cmd_desc; @@ -2652,7 +2036,7 @@ static int gdb_handle_packet(const char *line_buf) case 'F': { static const GdbCmdParseEntry file_io_cmd_desc = { - .handler = handle_file_io, + .handler = gdb_handle_file_io, .cmd = "F", .cmd_startswith = 1, .schema = "L,L,o0" @@ -2793,7 +2177,7 @@ static int gdb_handle_packet(const char *line_buf) break; default: /* put empty packet */ - put_packet(""); + gdb_put_packet(""); break; } @@ -2820,193 +2204,18 @@ void gdb_set_stop_cpu(CPUState *cpu) gdbserver_state.g_cpu = cpu; } -#ifndef CONFIG_USER_ONLY -static void gdb_vm_state_change(void *opaque, bool running, RunState state) -{ - 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 || gdbserver_state.state == RS_INACTIVE) { - return; - } - /* Is there a GDB syscall waiting to be sent? */ - if (gdbserver_state.current_syscall_cb) { - put_packet(gdbserver_state.syscall_buf); - return; - } - - if (cpu == NULL) { - /* No process attached */ - return; - } - - gdb_append_thread_id(cpu, tid); - - switch (state) { - case RUN_STATE_DEBUG: - if (cpu->watchpoint_hit) { - switch (cpu->watchpoint_hit->flags & BP_MEM_ACCESS) { - case BP_MEM_READ: - type = "r"; - break; - case BP_MEM_ACCESS: - type = "a"; - break; - default: - type = ""; - break; - } - trace_gdbstub_hit_watchpoint(type, cpu_gdb_index(cpu), - (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 { - trace_gdbstub_hit_break(); - } - tb_flush(cpu); - ret = GDB_SIGNAL_TRAP; - break; - case RUN_STATE_PAUSED: - trace_gdbstub_hit_paused(); - ret = GDB_SIGNAL_INT; - break; - case RUN_STATE_SHUTDOWN: - trace_gdbstub_hit_shutdown(); - ret = GDB_SIGNAL_QUIT; - break; - case RUN_STATE_IO_ERROR: - trace_gdbstub_hit_io_error(); - ret = GDB_SIGNAL_IO; - break; - case RUN_STATE_WATCHDOG: - trace_gdbstub_hit_watchdog(); - ret = GDB_SIGNAL_ALRM; - break; - case RUN_STATE_INTERNAL_ERROR: - trace_gdbstub_hit_internal_error(); - ret = GDB_SIGNAL_ABRT; - break; - case RUN_STATE_SAVE_VM: - case RUN_STATE_RESTORE_VM: - return; - case RUN_STATE_FINISH_MIGRATE: - ret = GDB_SIGNAL_XCPU; - break; - default: - trace_gdbstub_hit_unknown(state); - ret = GDB_SIGNAL_UNKNOWN; - break; - } - gdb_set_stop_cpu(cpu); - g_string_printf(buf, "T%02xthread:%s;", ret, tid->str); - -send_packet: - put_packet(buf->str); - - /* disable single step if it was enabled */ - cpu_single_step(cpu, 0); -} -#endif - -/* Send a gdb syscall request. - This accepts limited printf-style format specifiers, specifically: - %x - target_ulong argument printed in hex. - %lx - 64-bit argument printed in hex. - %s - string pointer (target_ulong) and length (int) pair. */ -void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) -{ - char *p; - char *p_end; - target_ulong addr; - uint64_t i64; - - if (!gdbserver_state.init) { - return; - } - - gdbserver_state.current_syscall_cb = cb; -#ifndef CONFIG_USER_ONLY - vm_stop(RUN_STATE_DEBUG); -#endif - p = &gdbserver_state.syscall_buf[0]; - p_end = &gdbserver_state.syscall_buf[sizeof(gdbserver_state.syscall_buf)]; - *(p++) = 'F'; - while (*fmt) { - if (*fmt == '%') { - fmt++; - switch (*fmt++) { - case 'x': - addr = va_arg(va, target_ulong); - p += snprintf(p, p_end - p, TARGET_FMT_lx, addr); - break; - case 'l': - if (*(fmt++) != 'x') - goto bad_format; - i64 = va_arg(va, uint64_t); - p += snprintf(p, p_end - p, "%" PRIx64, i64); - break; - case 's': - addr = va_arg(va, target_ulong); - p += snprintf(p, p_end - p, TARGET_FMT_lx "/%x", - addr, va_arg(va, int)); - break; - default: - bad_format: - error_report("gdbstub: Bad syscall format string '%s'", - fmt - 1); - break; - } - } else { - *(p++) = *(fmt++); - } - } - *p = 0; -#ifdef CONFIG_USER_ONLY - 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(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 - now the reply from the syscall request could be received while the CPU - 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(gdbserver_state.c_cpu); -#endif -} - -void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) -{ - va_list va; - - va_start(va, fmt); - gdb_do_syscallv(cb, fmt, va); - va_end(va); -} - -static void gdb_read_byte(uint8_t ch) +void gdb_read_byte(uint8_t ch) { uint8_t reply; + gdbserver_state.allow_stop_reply = false; #ifndef CONFIG_USER_ONLY 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(gdbserver_state.last_packet->data, + gdb_put_buffer(gdbserver_state.last_packet->data, gdbserver_state.last_packet->len); } else if (ch == '+') { trace_gdbstub_io_got_ack(); @@ -3021,8 +2230,18 @@ static void gdb_read_byte(uint8_t ch) return; } if (runstate_is_running()) { - /* when the CPU is running, we cannot do anything except stop - it when receiving a char */ + /* + * When the CPU is running, we cannot do anything except stop + * it when receiving a char. This is expected on a Ctrl-C in the + * gdb client. Because we are in all-stop mode, gdb sends a + * 0x03 byte which is not a usual packet, so we handle it specially + * here, but it does expect a stop reply. + */ + if (ch != 0x03) { + trace_gdbstub_err_unexpected_runpkt(ch); + } else { + gdbserver_state.allow_stop_reply = true; + } vm_stop(RUN_STATE_PAUSED); } else #endif @@ -3034,6 +2253,11 @@ static void gdb_read_byte(uint8_t ch) gdbserver_state.line_buf_index = 0; gdbserver_state.line_sum = 0; gdbserver_state.state = RS_GETLINE; + } else if (ch == '+') { + /* + * do nothing, gdb may preemptively send out ACKs on + * initial connection + */ } else { trace_gdbstub_err_garbage(ch); } @@ -3128,12 +2352,12 @@ static void gdb_read_byte(uint8_t ch) trace_gdbstub_err_checksum_incorrect(gdbserver_state.line_sum, gdbserver_state.line_csum); /* send NAK reply */ reply = '-'; - put_buffer(&reply, 1); + gdb_put_buffer(&reply, 1); gdbserver_state.state = RS_IDLE; } else { /* send ACK reply */ reply = '+'; - put_buffer(&reply, 1); + gdb_put_buffer(&reply, 1); gdbserver_state.state = gdb_handle_packet(gdbserver_state.line_buf); } break; @@ -3143,499 +2367,34 @@ static void gdb_read_byte(uint8_t ch) } } -/* Tell the remote gdb that the process has exited. */ -void gdb_exit(int code) -{ - char buf[4]; - - if (!gdbserver_state.init) { - return; - } -#ifdef CONFIG_USER_ONLY - if (gdbserver_state.socket_path) { - unlink(gdbserver_state.socket_path); - } - if (gdbserver_state.fd < 0) { - return; - } -#endif - - trace_gdbstub_op_exiting((uint8_t)code); - - snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); - put_packet(buf); - -#ifndef CONFIG_USER_ONLY - qemu_chr_fe_deinit(&gdbserver_state.chr, true); -#endif -} - /* * Create the process that will contain all the "orphan" CPUs (that are not * part of a CPU cluster). Note that if this process contains no CPUs, it won't * be attachable and thus will be invisible to the user. */ -static void create_default_process(GDBState *s) +void gdb_create_default_process(GDBState *s) { GDBProcess *process; - int max_pid = 0; + int pid; +#ifdef CONFIG_USER_ONLY + assert(gdbserver_state.process_num == 0); + pid = getpid(); +#else if (gdbserver_state.process_num) { - max_pid = s->processes[s->process_num - 1].pid; + pid = s->processes[s->process_num - 1].pid; + } else { + pid = 0; } + /* We need an available PID slot for this process */ + assert(pid < UINT32_MAX); + pid++; +#endif s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); process = &s->processes[s->process_num - 1]; - - /* We need an available PID slot for this process */ - assert(max_pid < UINT32_MAX); - - process->pid = max_pid + 1; + process->pid = pid; process->attached = false; - process->target_xml[0] = '\0'; + process->target_xml = NULL; } -#ifdef CONFIG_USER_ONLY -int -gdb_handlesig(CPUState *cpu, int sig) -{ - char buf[256]; - int n; - - if (!gdbserver_state.init || gdbserver_state.fd < 0) { - return sig; - } - - /* disable single step if it was enabled */ - cpu_single_step(cpu, 0); - tb_flush(cpu); - - if (sig != 0) { - gdb_set_stop_cpu(cpu); - g_string_printf(gdbserver_state.str_buf, - "T%02xthread:", target_signal_to_gdb(sig)); - gdb_append_thread_id(cpu, gdbserver_state.str_buf); - g_string_append_c(gdbserver_state.str_buf, ';'); - put_strbuf(); - } - /* put_packet() might have detected that the peer terminated the - connection. */ - if (gdbserver_state.fd < 0) { - return sig; - } - - sig = 0; - 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(buf[i]); - } - } else { - /* XXX: Connection closed. Should probably wait for another - connection before continuing. */ - if (n == 0) { - close(gdbserver_state.fd); - } - gdbserver_state.fd = -1; - return sig; - } - } - 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) -{ - char buf[4]; - - if (!gdbserver_state.init || gdbserver_state.fd < 0) { - return; - } - - snprintf(buf, sizeof(buf), "X%02x", target_signal_to_gdb(sig)); - put_packet(buf); -} - -static void gdb_accept_init(int 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; -} - -static bool gdb_accept_socket(int gdb_fd) -{ - int fd; - - for(;;) { - fd = accept(gdb_fd, NULL, NULL); - if (fd < 0 && errno != EINTR) { - perror("accept socket"); - return false; - } else if (fd >= 0) { - qemu_set_cloexec(fd); - break; - } - } - - gdb_accept_init(fd); - return true; -} - -static int gdbserver_open_socket(const char *path) -{ - struct sockaddr_un sockaddr = {}; - int fd, ret; - - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - perror("create socket"); - return -1; - } - - sockaddr.sun_family = AF_UNIX; - pstrcpy(sockaddr.sun_path, sizeof(sockaddr.sun_path) - 1, path); - ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); - if (ret < 0) { - perror("bind socket"); - close(fd); - return -1; - } - ret = listen(fd, 1); - if (ret < 0) { - perror("listen socket"); - close(fd); - return -1; - } - - return fd; -} - -static bool gdb_accept_tcp(int gdb_fd) -{ - struct sockaddr_in sockaddr = {}; - socklen_t len; - int fd; - - for(;;) { - len = sizeof(sockaddr); - fd = accept(gdb_fd, (struct sockaddr *)&sockaddr, &len); - if (fd < 0 && errno != EINTR) { - perror("accept"); - return false; - } else if (fd >= 0) { - qemu_set_cloexec(fd); - break; - } - } - - /* set short latency */ - if (socket_set_nodelay(fd)) { - perror("setsockopt"); - close(fd); - return false; - } - - gdb_accept_init(fd); - return true; -} - -static int gdbserver_open_port(int port) -{ - struct sockaddr_in sockaddr; - int fd, ret; - - fd = socket(PF_INET, SOCK_STREAM, 0); - if (fd < 0) { - perror("socket"); - return -1; - } - qemu_set_cloexec(fd); - - socket_set_fast_reuse(fd); - - sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(port); - sockaddr.sin_addr.s_addr = 0; - ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); - if (ret < 0) { - perror("bind"); - close(fd); - return -1; - } - ret = listen(fd, 1); - if (ret < 0) { - perror("listen"); - close(fd); - return -1; - } - - return fd; -} - -int gdbserver_start(const char *port_or_path) -{ - int port = g_ascii_strtoull(port_or_path, NULL, 10); - int gdb_fd; - - if (port > 0) { - gdb_fd = gdbserver_open_port(port); - } else { - gdb_fd = gdbserver_open_socket(port_or_path); - } - - if (gdb_fd < 0) { - return -1; - } - - if (port > 0 && gdb_accept_tcp(gdb_fd)) { - return 0; - } else if (gdb_accept_socket(gdb_fd)) { - gdbserver_state.socket_path = g_strdup(port_or_path); - return 0; - } - - /* gone wrong */ - close(gdb_fd); - return -1; -} - -/* Disable gdb stub for child processes. */ -void gdbserver_fork(CPUState *cpu) -{ - if (!gdbserver_state.init || gdbserver_state.fd < 0) { - return; - } - close(gdbserver_state.fd); - gdbserver_state.fd = -1; - cpu_breakpoint_remove_all(cpu, BP_GDB); - cpu_watchpoint_remove_all(cpu, BP_GDB); -} -#else -static int gdb_chr_can_receive(void *opaque) -{ - /* We can handle an arbitrarily large amount of data. - Pick the maximum packet size, which is as good as anything. */ - return MAX_PACKET_LENGTH; -} - -static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size) -{ - int i; - - for (i = 0; i < size; i++) { - gdb_read_byte(buf[i]); - } -} - -static void gdb_chr_event(void *opaque, QEMUChrEvent event) -{ - int i; - GDBState *s = (GDBState *) opaque; - - switch (event) { - case CHR_EVENT_OPENED: - /* Start with first process attached, others detached */ - for (i = 0; i < s->process_num; i++) { - s->processes[i].attached = !i; - } - - s->c_cpu = gdb_first_attached_cpu(); - s->g_cpu = s->c_cpu; - - vm_stop(RUN_STATE_PAUSED); - replay_gdb_attached(); - gdb_has_xml = false; - break; - default: - break; - } -} - -static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len) -{ - g_autoptr(GString) hex_buf = g_string_new("O"); - memtohex(hex_buf, buf, len); - put_packet(hex_buf->str); - return len; -} - -#ifndef _WIN32 -static void gdb_sigterm_handler(int signal) -{ - if (runstate_is_running()) { - vm_stop(RUN_STATE_PAUSED); - } -} -#endif - -static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend, - bool *be_opened, Error **errp) -{ - *be_opened = false; -} - -static void char_gdb_class_init(ObjectClass *oc, void *data) -{ - ChardevClass *cc = CHARDEV_CLASS(oc); - - cc->internal = true; - cc->open = gdb_monitor_open; - cc->chr_write = gdb_monitor_write; -} - -#define TYPE_CHARDEV_GDB "chardev-gdb" - -static const TypeInfo char_gdb_type_info = { - .name = TYPE_CHARDEV_GDB, - .parent = TYPE_CHARDEV, - .class_init = char_gdb_class_init, -}; - -static int find_cpu_clusters(Object *child, void *opaque) -{ - if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) { - GDBState *s = (GDBState *) opaque; - CPUClusterState *cluster = CPU_CLUSTER(child); - GDBProcess *process; - - s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); - - process = &s->processes[s->process_num - 1]; - - /* - * GDB process IDs -1 and 0 are reserved. To avoid subtle errors at - * runtime, we enforce here that the machine does not use a cluster ID - * that would lead to PID 0. - */ - assert(cluster->cluster_id != UINT32_MAX); - process->pid = cluster->cluster_id + 1; - process->attached = false; - process->target_xml[0] = '\0'; - - return 0; - } - - return object_child_foreach(child, find_cpu_clusters, opaque); -} - -static int pid_order(const void *a, const void *b) -{ - GDBProcess *pa = (GDBProcess *) a; - GDBProcess *pb = (GDBProcess *) b; - - if (pa->pid < pb->pid) { - return -1; - } else if (pa->pid > pb->pid) { - return 1; - } else { - return 0; - } -} - -static void create_processes(GDBState *s) -{ - object_child_foreach(object_get_root(), find_cpu_clusters, s); - - if (gdbserver_state.processes) { - /* Sort by PID */ - qsort(gdbserver_state.processes, gdbserver_state.process_num, sizeof(gdbserver_state.processes[0]), pid_order); - } - - create_default_process(s); -} - -int gdbserver_start(const char *device) -{ - trace_gdbstub_op_start(device); - - char gdbstub_device_name[128]; - Chardev *chr = NULL; - Chardev *mon_chr; - - if (!first_cpu) { - error_report("gdbstub: meaningless to attach gdb to a " - "machine without any CPU."); - return -1; - } - - if (kvm_enabled() && !kvm_supports_guest_debug()) { - error_report("gdbstub: KVM doesn't support guest debugging"); - return -1; - } - - if (!device) - return -1; - if (strcmp(device, "none") != 0) { - if (strstart(device, "tcp:", NULL)) { - /* enforce required TCP attributes */ - snprintf(gdbstub_device_name, sizeof(gdbstub_device_name), - "%s,wait=off,nodelay=on,server=on", device); - device = gdbstub_device_name; - } -#ifndef _WIN32 - else if (strcmp(device, "stdio") == 0) { - struct sigaction act; - - memset(&act, 0, sizeof(act)); - act.sa_handler = gdb_sigterm_handler; - sigaction(SIGINT, &act, NULL); - } -#endif - /* - * FIXME: it's a bit weird to allow using a mux chardev here - * and implicitly setup a monitor. We may want to break this. - */ - chr = qemu_chr_new_noreplay("gdb", device, true, NULL); - if (!chr) - return -1; - } - - 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, &error_abort); - } else { - qemu_chr_fe_deinit(&gdbserver_state.chr, true); - mon_chr = gdbserver_state.mon_chr; - reset_gdbserver_state(); - } - - create_processes(&gdbserver_state); - - if (chr) { - 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); - } - gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE; - gdbserver_state.mon_chr = mon_chr; - gdbserver_state.current_syscall_cb = NULL; - - return 0; -} - -static void register_types(void) -{ - type_register_static(&char_gdb_type_info); -} - -type_init(register_types); -#endif diff --git a/gdbstub/internals.h b/gdbstub/internals.h new file mode 100644 index 0000000000..32f9f63297 --- /dev/null +++ b/gdbstub/internals.h @@ -0,0 +1,243 @@ +/* + * gdbstub internals + * + * Copyright (c) 2022 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GDBSTUB_INTERNALS_H +#define GDBSTUB_INTERNALS_H + +#include "exec/cpu-common.h" + +#define MAX_PACKET_LENGTH 4096 + +/* + * Shared structures and definitions + */ + +enum { + GDB_SIGNAL_0 = 0, + GDB_SIGNAL_INT = 2, + GDB_SIGNAL_QUIT = 3, + GDB_SIGNAL_TRAP = 5, + GDB_SIGNAL_ABRT = 6, + GDB_SIGNAL_ALRM = 14, + GDB_SIGNAL_STOP = 17, + GDB_SIGNAL_IO = 23, + GDB_SIGNAL_XCPU = 24, + GDB_SIGNAL_UNKNOWN = 143 +}; + +typedef struct GDBProcess { + uint32_t pid; + bool attached; + char *target_xml; +} GDBProcess; + +enum RSState { + RS_INACTIVE, + RS_IDLE, + RS_GETLINE, + RS_GETLINE_ESC, + RS_GETLINE_RLE, + RS_CHKSUM1, + 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 */ + enum RSState state; /* parsing state */ + char line_buf[MAX_PACKET_LENGTH]; + int line_buf_index; + int line_sum; /* running checksum */ + int line_csum; /* checksum at the end of the packet */ + GByteArray *last_packet; + int signal; + bool multiprocess; + GDBProcess *processes; + int process_num; + GString *str_buf; + GByteArray *mem_buf; + int sstep_flags; + int supported_sstep_flags; + /* + * Whether we are allowed to send a stop reply packet at this moment. + * Must be set off after sending the stop reply itself. + */ + bool allow_stop_reply; +} GDBState; + +/* lives in main gdbstub.c */ +extern GDBState gdbserver_state; + +/* + * Inline utility function, convert from int to hex and back + */ + +static inline int fromhex(int v) +{ + if (v >= '0' && v <= '9') { + return v - '0'; + } else if (v >= 'A' && v <= 'F') { + return v - 'A' + 10; + } else if (v >= 'a' && v <= 'f') { + return v - 'a' + 10; + } else { + return 0; + } +} + +static inline int tohex(int v) +{ + if (v < 10) { + return v + '0'; + } else { + return v - 10 + 'a'; + } +} + +/* + * Connection helpers for both system and user backends + */ + +void gdb_put_strbuf(void); +int gdb_put_packet(const char *buf); +int gdb_put_packet_binary(const char *buf, int len, bool dump); +void gdb_hextomem(GByteArray *mem, const char *buf, int len); +void gdb_memtohex(GString *buf, const uint8_t *mem, int len); +void gdb_memtox(GString *buf, const char *mem, int len); +void gdb_read_byte(uint8_t ch); + +/* + * Packet acknowledgement - we handle this slightly differently + * between user and system mode, mainly to deal with the differences + * between the flexible chardev and the direct fd approaches. + * + * We currently don't support a negotiated QStartNoAckMode + */ + +/** + * gdb_got_immediate_ack() - check ok to continue + * + * Returns true to continue, false to re-transmit for user only, the + * system stub always returns true. + */ +bool gdb_got_immediate_ack(void); +/* utility helpers */ +GDBProcess *gdb_get_process(uint32_t pid); +CPUState *gdb_get_first_cpu_in_process(GDBProcess *process); +CPUState *gdb_first_attached_cpu(void); +void gdb_append_thread_id(CPUState *cpu, GString *buf); +int gdb_get_cpu_index(CPUState *cpu); +unsigned int gdb_get_max_cpus(void); /* both */ +bool gdb_can_reverse(void); /* system emulation, stub for user */ +int gdb_target_sigtrap(void); /* user */ + +void gdb_create_default_process(GDBState *s); + +/* signal mapping, common for system, specialised for user-mode */ +int gdb_signal_to_target(int sig); +int gdb_target_signal_to_gdb(int sig); + +int gdb_get_char(void); /* user only */ + +/** + * gdb_continue() - handle continue in mode specific way. + */ +void gdb_continue(void); + +/** + * gdb_continue_partial() - handle partial continue in mode specific way. + */ +int gdb_continue_partial(char *newstates); + +/* + * Helpers with separate system and user implementations + */ +void gdb_put_buffer(const uint8_t *buf, int len); + +/* + * Command handlers - either specialised or system or user only + */ +void gdb_init_gdbserver_state(void); + +typedef enum GDBThreadIdKind { + GDB_ONE_THREAD = 0, + GDB_ALL_THREADS, /* One process, all threads */ + GDB_ALL_PROCESSES, + GDB_READ_THREAD_ERR +} GDBThreadIdKind; + +typedef union GdbCmdVariant { + const char *data; + uint8_t opcode; + unsigned long val_ul; + unsigned long long val_ull; + struct { + GDBThreadIdKind kind; + uint32_t pid; + uint32_t tid; + } thread_id; +} GdbCmdVariant; + +#define get_param(p, i) (&g_array_index(p, GdbCmdVariant, i)) + +void gdb_handle_query_rcmd(GArray *params, void *ctx); /* system */ +void gdb_handle_query_offsets(GArray *params, void *user_ctx); /* user */ +void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx); /*user */ +void gdb_handle_query_xfer_siginfo(GArray *params, void *user_ctx); /*user */ +void gdb_handle_v_file_open(GArray *params, void *user_ctx); /* user */ +void gdb_handle_v_file_close(GArray *params, void *user_ctx); /* user */ +void gdb_handle_v_file_pread(GArray *params, void *user_ctx); /* user */ +void gdb_handle_v_file_readlink(GArray *params, void *user_ctx); /* user */ +void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user */ +void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */ +void gdb_handle_query_supported_user(const char *gdb_supported); /* user */ +bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid); /* user */ +bool gdb_handle_detach_user(uint32_t pid); /* user */ + +void gdb_handle_query_attached(GArray *params, void *ctx); /* both */ + +/* system only */ +void gdb_handle_query_qemu_phy_mem_mode(GArray *params, void *ctx); +void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *ctx); + +/* sycall handling */ +void gdb_handle_file_io(GArray *params, void *user_ctx); +bool gdb_handled_syscall(void); +void gdb_disable_syscalls(void); +void gdb_syscall_reset(void); + +/* user/system specific syscall handling */ +void gdb_syscall_handling(const char *syscall_packet); + +/* + * Break/Watch point support - there is an implementation for system + * and user mode. + */ +bool gdb_supports_guest_debug(void); +int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len); +int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len); +void gdb_breakpoint_remove_all(CPUState *cs); + +/** + * gdb_target_memory_rw_debug() - handle debug access to memory + * @cs: CPUState + * @addr: nominal address, could be an entire physical address + * @buf: data + * @len: length of access + * @is_write: is it a write operation + * + * This function is specialised depending on the mode we are running + * in. For system guests we can switch the interpretation of the + * address to a physical address. + */ +int gdb_target_memory_rw_debug(CPUState *cs, hwaddr addr, + uint8_t *buf, int len, bool is_write); + +#endif /* GDBSTUB_INTERNALS_H */ diff --git a/gdbstub/meson.build b/gdbstub/meson.build new file mode 100644 index 0000000000..da5721d845 --- /dev/null +++ b/gdbstub/meson.build @@ -0,0 +1,39 @@ +# +# The main gdbstub still relies on per-build definitions of various +# types. The bits pushed to system/user.c try to use guest agnostic +# types such as hwaddr. +# + +# We need to build the core gdb code via a library to be able to tweak +# cflags so: + +gdb_user_ss = ss.source_set() +gdb_system_ss = ss.source_set() + +# We build two versions of gdbstub, one for each mode +gdb_user_ss.add(files('gdbstub.c', 'user.c')) +gdb_system_ss.add(files('gdbstub.c', 'system.c')) + +gdb_user_ss = gdb_user_ss.apply({}) +gdb_system_ss = gdb_system_ss.apply({}) + +libgdb_user = static_library('gdb_user', + gdb_user_ss.sources() + genh, + name_suffix: 'fa', + c_args: '-DCONFIG_USER_ONLY', + build_by_default: false) + +libgdb_system = static_library('gdb_system', + gdb_system_ss.sources() + genh, + name_suffix: 'fa', + build_by_default: false) + +gdb_user = declare_dependency(link_whole: libgdb_user) +user_ss.add(gdb_user) +gdb_system = declare_dependency(link_whole: libgdb_system) +system_ss.add(gdb_system) + +common_ss.add(files('syscalls.c')) + +# The user-target is specialised by the guest +specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-target.c')) diff --git a/gdbstub/syscalls.c b/gdbstub/syscalls.c new file mode 100644 index 0000000000..02e3a8f74c --- /dev/null +++ b/gdbstub/syscalls.c @@ -0,0 +1,205 @@ +/* + * GDB Syscall Handling + * + * GDB can execute syscalls on the guests behalf, currently used by + * the various semihosting extensions. + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2023 Linaro Ltd + * + * SPDX-License-Identifier: LGPL-2.0+ + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "semihosting/semihost.h" +#include "sysemu/runstate.h" +#include "gdbstub/user.h" +#include "gdbstub/syscalls.h" +#include "trace.h" +#include "internals.h" + +/* Syscall specific state */ +typedef struct { + char syscall_buf[256]; + gdb_syscall_complete_cb current_syscall_cb; +} GDBSyscallState; + +static GDBSyscallState gdbserver_syscall_state; + +/* + * Return true if there is a GDB currently connected to the stub + * and attached to a CPU + */ +static bool gdb_attached(void) +{ + return gdbserver_state.init && gdbserver_state.c_cpu; +} + +static enum { + GDB_SYS_UNKNOWN, + GDB_SYS_ENABLED, + GDB_SYS_DISABLED, +} gdb_syscall_mode; + +/* Decide if either remote gdb syscalls or native file IO should be used. */ +int use_gdb_syscalls(void) +{ + SemihostingTarget target = semihosting_get_target(); + if (target == SEMIHOSTING_TARGET_NATIVE) { + /* -semihosting-config target=native */ + return false; + } else if (target == SEMIHOSTING_TARGET_GDB) { + /* -semihosting-config target=gdb */ + return true; + } + + /* -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 = gdb_attached() ? GDB_SYS_ENABLED : GDB_SYS_DISABLED; + } + return gdb_syscall_mode == GDB_SYS_ENABLED; +} + +/* called when the stub detaches */ +void gdb_disable_syscalls(void) +{ + gdb_syscall_mode = GDB_SYS_DISABLED; +} + +void gdb_syscall_reset(void) +{ + gdbserver_syscall_state.current_syscall_cb = NULL; +} + +bool gdb_handled_syscall(void) +{ + if (gdbserver_syscall_state.current_syscall_cb) { + gdb_put_packet(gdbserver_syscall_state.syscall_buf); + return true; + } + + return false; +} + +/* + * Send a gdb syscall request. + * This accepts limited printf-style format specifiers, specifically: + * %x - target_ulong argument printed in hex. + * %lx - 64-bit argument printed in hex. + * %s - string pointer (target_ulong) and length (int) pair. + */ +void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) +{ + char *p, *p_end; + va_list va; + + if (!gdb_attached()) { + return; + } + + gdbserver_syscall_state.current_syscall_cb = cb; + va_start(va, fmt); + + p = gdbserver_syscall_state.syscall_buf; + p_end = p + sizeof(gdbserver_syscall_state.syscall_buf); + *(p++) = 'F'; + while (*fmt) { + if (*fmt == '%') { + uint64_t i64; + uint32_t i32; + + fmt++; + switch (*fmt++) { + case 'x': + i32 = va_arg(va, uint32_t); + p += snprintf(p, p_end - p, "%" PRIx32, i32); + break; + case 'l': + if (*(fmt++) != 'x') { + goto bad_format; + } + i64 = va_arg(va, uint64_t); + p += snprintf(p, p_end - p, "%" PRIx64, i64); + break; + case 's': + i64 = va_arg(va, uint64_t); + i32 = va_arg(va, uint32_t); + p += snprintf(p, p_end - p, "%" PRIx64 "/%x" PRIx32, i64, i32); + break; + default: + bad_format: + error_report("gdbstub: Bad syscall format string '%s'", + fmt - 1); + break; + } + } else { + *(p++) = *(fmt++); + } + } + *p = 0; + + va_end(va); + gdb_syscall_handling(gdbserver_syscall_state.syscall_buf); +} + +/* + * GDB Command Handlers + */ + +void gdb_handle_file_io(GArray *params, void *user_ctx) +{ + if (params->len >= 1 && gdbserver_syscall_state.current_syscall_cb) { + uint64_t ret; + int err; + + ret = get_param(params, 0)->val_ull; + if (params->len >= 2) { + err = get_param(params, 1)->val_ull; + } else { + err = 0; + } + + /* Convert GDB error numbers back to host error numbers. */ +#define E(X) case GDB_E##X: err = E##X; break + switch (err) { + case 0: + break; + E(PERM); + E(NOENT); + E(INTR); + E(BADF); + E(ACCES); + E(FAULT); + E(BUSY); + E(EXIST); + E(NODEV); + E(NOTDIR); + E(ISDIR); + E(INVAL); + E(NFILE); + E(MFILE); + E(FBIG); + E(NOSPC); + E(SPIPE); + E(ROFS); + E(NAMETOOLONG); + default: + err = EINVAL; + break; + } +#undef E + + gdbserver_syscall_state.current_syscall_cb(gdbserver_state.c_cpu, + ret, err); + gdbserver_syscall_state.current_syscall_cb = NULL; + } + + if (params->len >= 3 && get_param(params, 2)->opcode == (uint8_t)'C') { + gdb_put_packet("T02"); + return; + } + + gdb_continue(); +} diff --git a/gdbstub/system.c b/gdbstub/system.c new file mode 100644 index 0000000000..d235403855 --- /dev/null +++ b/gdbstub/system.c @@ -0,0 +1,664 @@ +/* + * gdb server stub - system specific bits + * + * Debug integration depends on support from the individual + * accelerators so most of this involves calling the ops helpers. + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2022 Linaro Ltd + * + * SPDX-License-Identifier: LGPL-2.0+ + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/cutils.h" +#include "exec/gdbstub.h" +#include "gdbstub/syscalls.h" +#include "exec/hwaddr.h" +#include "exec/tb-flush.h" +#include "sysemu/cpus.h" +#include "sysemu/runstate.h" +#include "sysemu/replay.h" +#include "hw/core/cpu.h" +#include "hw/cpu/cluster.h" +#include "hw/boards.h" +#include "chardev/char.h" +#include "chardev/char-fe.h" +#include "monitor/monitor.h" +#include "trace.h" +#include "internals.h" + +/* System emulation specific state */ +typedef struct { + CharBackend chr; + Chardev *mon_chr; +} GDBSystemState; + +GDBSystemState gdbserver_system_state; + +static void reset_gdbserver_state(void) +{ + g_free(gdbserver_state.processes); + gdbserver_state.processes = NULL; + gdbserver_state.process_num = 0; + gdbserver_state.allow_stop_reply = false; +} + +/* + * Return the GDB index for a given vCPU state. + * + * In system mode GDB numbers CPUs from 1 as 0 is reserved as an "any + * cpu" index. + */ +int gdb_get_cpu_index(CPUState *cpu) +{ + return cpu->cpu_index + 1; +} + +/* + * We check the status of the last message in the chardev receive code + */ +bool gdb_got_immediate_ack(void) +{ + return true; +} + +/* + * GDB Connection management. For system emulation we do all of this + * via our existing Chardev infrastructure which allows us to support + * network and unix sockets. + */ + +void gdb_put_buffer(const uint8_t *buf, int len) +{ + /* + * XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks + */ + qemu_chr_fe_write_all(&gdbserver_system_state.chr, buf, len); +} + +static void gdb_chr_event(void *opaque, QEMUChrEvent event) +{ + int i; + GDBState *s = (GDBState *) opaque; + + switch (event) { + case CHR_EVENT_OPENED: + /* Start with first process attached, others detached */ + for (i = 0; i < s->process_num; i++) { + s->processes[i].attached = !i; + } + + s->c_cpu = gdb_first_attached_cpu(); + s->g_cpu = s->c_cpu; + + vm_stop(RUN_STATE_PAUSED); + replay_gdb_attached(); + break; + default: + break; + } +} + +/* + * In system-mode we stop the VM and wait to send the syscall packet + * until notification that the CPU has stopped. This must be done + * because if the packet is sent now the reply from the syscall + * request could be received while the CPU 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. + */ +void gdb_syscall_handling(const char *syscall_packet) +{ + vm_stop(RUN_STATE_DEBUG); + qemu_cpu_kick(gdbserver_state.c_cpu); +} + +static void gdb_vm_state_change(void *opaque, bool running, RunState state) +{ + 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 || gdbserver_state.state == RS_INACTIVE) { + return; + } + + /* Is there a GDB syscall waiting to be sent? */ + if (gdb_handled_syscall()) { + return; + } + + if (cpu == NULL) { + /* No process attached */ + return; + } + + if (!gdbserver_state.allow_stop_reply) { + return; + } + + gdb_append_thread_id(cpu, tid); + + switch (state) { + case RUN_STATE_DEBUG: + if (cpu->watchpoint_hit) { + switch (cpu->watchpoint_hit->flags & BP_MEM_ACCESS) { + case BP_MEM_READ: + type = "r"; + break; + case BP_MEM_ACCESS: + type = "a"; + break; + default: + type = ""; + break; + } + trace_gdbstub_hit_watchpoint(type, + gdb_get_cpu_index(cpu), + cpu->watchpoint_hit->vaddr); + g_string_printf(buf, "T%02xthread:%s;%swatch:%" VADDR_PRIx ";", + GDB_SIGNAL_TRAP, tid->str, type, + cpu->watchpoint_hit->vaddr); + cpu->watchpoint_hit = NULL; + goto send_packet; + } else { + trace_gdbstub_hit_break(); + } + tb_flush(cpu); + ret = GDB_SIGNAL_TRAP; + break; + case RUN_STATE_PAUSED: + trace_gdbstub_hit_paused(); + ret = GDB_SIGNAL_INT; + break; + case RUN_STATE_SHUTDOWN: + trace_gdbstub_hit_shutdown(); + ret = GDB_SIGNAL_QUIT; + break; + case RUN_STATE_IO_ERROR: + trace_gdbstub_hit_io_error(); + ret = GDB_SIGNAL_STOP; + break; + case RUN_STATE_WATCHDOG: + trace_gdbstub_hit_watchdog(); + ret = GDB_SIGNAL_ALRM; + break; + case RUN_STATE_INTERNAL_ERROR: + trace_gdbstub_hit_internal_error(); + ret = GDB_SIGNAL_ABRT; + break; + case RUN_STATE_SAVE_VM: + case RUN_STATE_RESTORE_VM: + return; + case RUN_STATE_FINISH_MIGRATE: + ret = GDB_SIGNAL_XCPU; + break; + default: + trace_gdbstub_hit_unknown(state); + ret = GDB_SIGNAL_UNKNOWN; + break; + } + gdb_set_stop_cpu(cpu); + g_string_printf(buf, "T%02xthread:%s;", ret, tid->str); + +send_packet: + gdb_put_packet(buf->str); + gdbserver_state.allow_stop_reply = false; + + /* disable single step if it was enabled */ + cpu_single_step(cpu, 0); +} + +#ifndef _WIN32 +static void gdb_sigterm_handler(int signal) +{ + if (runstate_is_running()) { + vm_stop(RUN_STATE_PAUSED); + } +} +#endif + +static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len) +{ + g_autoptr(GString) hex_buf = g_string_new("O"); + gdb_memtohex(hex_buf, buf, len); + gdb_put_packet(hex_buf->str); + return len; +} + +static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend, + bool *be_opened, Error **errp) +{ + *be_opened = false; +} + +static void char_gdb_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->internal = true; + cc->open = gdb_monitor_open; + cc->chr_write = gdb_monitor_write; +} + +#define TYPE_CHARDEV_GDB "chardev-gdb" + +static const TypeInfo char_gdb_type_info = { + .name = TYPE_CHARDEV_GDB, + .parent = TYPE_CHARDEV, + .class_init = char_gdb_class_init, +}; + +static int gdb_chr_can_receive(void *opaque) +{ + /* + * We can handle an arbitrarily large amount of data. + * Pick the maximum packet size, which is as good as anything. + */ + return MAX_PACKET_LENGTH; +} + +static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size) +{ + int i; + + for (i = 0; i < size; i++) { + gdb_read_byte(buf[i]); + } +} + +static int find_cpu_clusters(Object *child, void *opaque) +{ + if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) { + GDBState *s = (GDBState *) opaque; + CPUClusterState *cluster = CPU_CLUSTER(child); + GDBProcess *process; + + s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); + + process = &s->processes[s->process_num - 1]; + + /* + * GDB process IDs -1 and 0 are reserved. To avoid subtle errors at + * runtime, we enforce here that the machine does not use a cluster ID + * that would lead to PID 0. + */ + assert(cluster->cluster_id != UINT32_MAX); + process->pid = cluster->cluster_id + 1; + process->attached = false; + process->target_xml = NULL; + + return 0; + } + + return object_child_foreach(child, find_cpu_clusters, opaque); +} + +static int pid_order(const void *a, const void *b) +{ + GDBProcess *pa = (GDBProcess *) a; + GDBProcess *pb = (GDBProcess *) b; + + if (pa->pid < pb->pid) { + return -1; + } else if (pa->pid > pb->pid) { + return 1; + } else { + return 0; + } +} + +static void create_processes(GDBState *s) +{ + object_child_foreach(object_get_root(), find_cpu_clusters, s); + + if (gdbserver_state.processes) { + /* Sort by PID */ + qsort(gdbserver_state.processes, + gdbserver_state.process_num, + sizeof(gdbserver_state.processes[0]), + pid_order); + } + + gdb_create_default_process(s); +} + +int gdbserver_start(const char *device) +{ + Chardev *chr = NULL; + Chardev *mon_chr; + g_autoptr(GString) cs = g_string_new(device); + + if (!first_cpu) { + error_report("gdbstub: meaningless to attach gdb to a " + "machine without any CPU."); + return -1; + } + + if (!gdb_supports_guest_debug()) { + error_report("gdbstub: current accelerator doesn't " + "support guest debugging"); + return -1; + } + + if (cs->len == 0) { + return -1; + } + + trace_gdbstub_op_start(cs->str); + + if (g_strcmp0(cs->str, "none") != 0) { + if (g_str_has_prefix(cs->str, "tcp:")) { + /* enforce required TCP attributes */ + g_string_append_printf(cs, ",wait=off,nodelay=on,server=on"); + } +#ifndef _WIN32 + else if (strcmp(device, "stdio") == 0) { + struct sigaction act; + + memset(&act, 0, sizeof(act)); + act.sa_handler = gdb_sigterm_handler; + sigaction(SIGINT, &act, NULL); + } +#endif + /* + * FIXME: it's a bit weird to allow using a mux chardev here + * and implicitly setup a monitor. We may want to break this. + */ + chr = qemu_chr_new_noreplay("gdb", cs->str, true, NULL); + if (!chr) { + return -1; + } + } + + if (!gdbserver_state.init) { + gdb_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, &error_abort); + } else { + qemu_chr_fe_deinit(&gdbserver_system_state.chr, true); + mon_chr = gdbserver_system_state.mon_chr; + reset_gdbserver_state(); + } + + create_processes(&gdbserver_state); + + if (chr) { + qemu_chr_fe_init(&gdbserver_system_state.chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&gdbserver_system_state.chr, + gdb_chr_can_receive, + gdb_chr_receive, gdb_chr_event, + NULL, &gdbserver_state, NULL, true); + } + gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE; + gdbserver_system_state.mon_chr = mon_chr; + gdb_syscall_reset(); + + return 0; +} + +static void register_types(void) +{ + type_register_static(&char_gdb_type_info); +} + +type_init(register_types); + +/* Tell the remote gdb that the process has exited. */ +void gdb_exit(int code) +{ + char buf[4]; + + if (!gdbserver_state.init) { + return; + } + + trace_gdbstub_op_exiting((uint8_t)code); + + if (gdbserver_state.allow_stop_reply) { + snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); + gdb_put_packet(buf); + gdbserver_state.allow_stop_reply = false; + } + + qemu_chr_fe_deinit(&gdbserver_system_state.chr, true); +} + +void gdb_qemu_exit(int code) +{ + qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_SHUTDOWN, + code); +} + +/* + * Memory access + */ +static int phy_memory_mode; + +int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, + uint8_t *buf, int len, bool is_write) +{ + CPUClass *cc; + + if (phy_memory_mode) { + if (is_write) { + cpu_physical_memory_write(addr, buf, len); + } else { + cpu_physical_memory_read(addr, buf, len); + } + return 0; + } + + cc = CPU_GET_CLASS(cpu); + if (cc->memory_rw_debug) { + return cc->memory_rw_debug(cpu, addr, buf, len, is_write); + } + + return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); +} + +/* + * cpu helpers + */ + +unsigned int gdb_get_max_cpus(void) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + return ms->smp.max_cpus; +} + +bool gdb_can_reverse(void) +{ + return replay_mode == REPLAY_MODE_PLAY; +} + +/* + * Softmmu specific command helpers + */ + +void gdb_handle_query_qemu_phy_mem_mode(GArray *params, + void *ctx) +{ + g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode); + gdb_put_strbuf(); +} + +void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *ctx) +{ + if (!params->len) { + gdb_put_packet("E22"); + return; + } + + if (!get_param(params, 0)->val_ul) { + phy_memory_mode = 0; + } else { + phy_memory_mode = 1; + } + gdb_put_packet("OK"); +} + +void gdb_handle_query_rcmd(GArray *params, void *ctx) +{ + const guint8 zero = 0; + int len; + + if (!params->len) { + gdb_put_packet("E22"); + return; + } + + len = strlen(get_param(params, 0)->data); + if (len % 2) { + gdb_put_packet("E01"); + return; + } + + g_assert(gdbserver_state.mem_buf->len == 0); + len = len / 2; + gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len); + g_byte_array_append(gdbserver_state.mem_buf, &zero, 1); + qemu_chr_be_write(gdbserver_system_state.mon_chr, + gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len); + gdb_put_packet("OK"); +} + +/* + * Execution state helpers + */ + +void gdb_handle_query_attached(GArray *params, void *ctx) +{ + gdb_put_packet("1"); +} + +void gdb_continue(void) +{ + if (!runstate_needs_reset()) { + trace_gdbstub_op_continue(); + vm_start(); + } +} + +/* + * Resume execution, per CPU actions. + */ +int gdb_continue_partial(char *newstates) +{ + CPUState *cpu; + int res = 0; + int flag = 0; + + if (!runstate_needs_reset()) { + bool step_requested = false; + CPU_FOREACH(cpu) { + if (newstates[cpu->cpu_index] == 's') { + step_requested = true; + break; + } + } + + if (vm_prepare_start(step_requested)) { + return 0; + } + + CPU_FOREACH(cpu) { + switch (newstates[cpu->cpu_index]) { + case 0: + case 1: + break; /* nothing to do here */ + case 's': + trace_gdbstub_op_stepping(cpu->cpu_index); + cpu_single_step(cpu, gdbserver_state.sstep_flags); + cpu_resume(cpu); + flag = 1; + break; + case 'c': + trace_gdbstub_op_continue_cpu(cpu->cpu_index); + cpu_resume(cpu); + flag = 1; + break; + default: + res = -1; + break; + } + } + } + if (flag) { + qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true); + } + return res; +} + +/* + * Signal Handling - in system mode we only need SIGINT and SIGTRAP; other + * signals are not yet supported. + */ + +enum { + TARGET_SIGINT = 2, + TARGET_SIGTRAP = 5 +}; + +int gdb_signal_to_target(int sig) +{ + switch (sig) { + case 2: + return TARGET_SIGINT; + case 5: + return TARGET_SIGTRAP; + default: + return -1; + } +} + +/* + * Break/Watch point helpers + */ + +bool gdb_supports_guest_debug(void) +{ + const AccelOpsClass *ops = cpus_get_accel(); + if (ops->supports_guest_debug) { + return ops->supports_guest_debug(); + } + return false; +} + +int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len) +{ + const AccelOpsClass *ops = cpus_get_accel(); + if (ops->insert_breakpoint) { + return ops->insert_breakpoint(cs, type, addr, len); + } + return -ENOSYS; +} + +int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len) +{ + const AccelOpsClass *ops = cpus_get_accel(); + if (ops->remove_breakpoint) { + return ops->remove_breakpoint(cs, type, addr, len); + } + return -ENOSYS; +} + +void gdb_breakpoint_remove_all(CPUState *cs) +{ + const AccelOpsClass *ops = cpus_get_accel(); + if (ops->remove_all_breakpoints) { + ops->remove_all_breakpoints(cs); + } +} diff --git a/gdbstub/trace-events b/gdbstub/trace-events new file mode 100644 index 0000000000..4fd126a38c --- /dev/null +++ b/gdbstub/trace-events @@ -0,0 +1,32 @@ +# See docs/devel/tracing.rst for syntax documentation. + +# gdbstub.c +gdbstub_op_start(const char *device) "Starting gdbstub using device %s" +gdbstub_op_exiting(uint8_t code) "notifying exit with code=0x%02x" +gdbstub_op_continue(void) "Continuing all CPUs" +gdbstub_op_continue_cpu(int cpu_index) "Continuing CPU %d" +gdbstub_op_stepping(int cpu_index) "Stepping CPU %d" +gdbstub_op_extra_info(const char *info) "Thread extra info: %s" +gdbstub_hit_internal_error(void) "RUN_STATE_INTERNAL_ERROR" +gdbstub_hit_break(void) "RUN_STATE_DEBUG" +gdbstub_hit_paused(void) "RUN_STATE_PAUSED" +gdbstub_hit_shutdown(void) "RUN_STATE_SHUTDOWN" +gdbstub_hit_io_error(void) "RUN_STATE_IO_ERROR" +gdbstub_hit_watchdog(void) "RUN_STATE_WATCHDOG" +gdbstub_hit_unknown(int state) "Unknown run state=0x%x" +gdbstub_io_reply(const char *message) "Sent: %s" +gdbstub_io_binaryreply(size_t ofs, const char *line) "0x%04zx: %s" +gdbstub_io_command(const char *command) "Received: %s" +gdbstub_io_got_ack(void) "Got ACK" +gdbstub_io_got_unexpected(uint8_t ch) "Got 0x%02x when expecting ACK/NACK" +gdbstub_err_got_nack(void) "Got NACK, retransmitting" +gdbstub_err_garbage(uint8_t ch) "received garbage between packets: 0x%02x" +gdbstub_err_overrun(void) "command buffer overrun, dropping command" +gdbstub_err_invalid_repeat(uint8_t ch) "got invalid RLE count: 0x%02x" +gdbstub_err_invalid_rle(void) "got invalid RLE sequence" +gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x%02x" +gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packet with incorrect checksum, expected=0x%02x, received=0x%02x" +gdbstub_err_unexpected_runpkt(uint8_t ch) "unexpected packet (0x%02x) while target running" + +# system.c +gdbstub_hit_watchpoint(const char *type, int cpu_gdb_index, uint64_t vaddr) "Watchpoint hit, type=\"%s\" cpu=%d, vaddr=0x%" PRIx64 "" diff --git a/gdbstub/trace.h b/gdbstub/trace.h new file mode 100644 index 0000000000..dee87b1238 --- /dev/null +++ b/gdbstub/trace.h @@ -0,0 +1 @@ +#include "trace/trace-gdbstub.h" diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c new file mode 100644 index 0000000000..6646684a4c --- /dev/null +++ b/gdbstub/user-target.c @@ -0,0 +1,425 @@ +/* + * Target specific user-mode handling + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2022 Linaro Ltd + * + * SPDX-License-Identifier: LGPL-2.0+ + */ + +#include "qemu/osdep.h" +#include "exec/gdbstub.h" +#include "qemu.h" +#include "internals.h" +#ifdef CONFIG_LINUX +#include "linux-user/loader.h" +#include "linux-user/qemu.h" +#endif + +/* + * Map target signal numbers to GDB protocol signal numbers and vice + * versa. For user emulation's currently supported systems, we can + * assume most signals are defined. + */ + +static int gdb_signal_table[] = { + 0, + TARGET_SIGHUP, + TARGET_SIGINT, + TARGET_SIGQUIT, + TARGET_SIGILL, + TARGET_SIGTRAP, + TARGET_SIGABRT, + -1, /* SIGEMT */ + TARGET_SIGFPE, + TARGET_SIGKILL, + TARGET_SIGBUS, + TARGET_SIGSEGV, + TARGET_SIGSYS, + TARGET_SIGPIPE, + TARGET_SIGALRM, + TARGET_SIGTERM, + TARGET_SIGURG, + TARGET_SIGSTOP, + TARGET_SIGTSTP, + TARGET_SIGCONT, + TARGET_SIGCHLD, + TARGET_SIGTTIN, + TARGET_SIGTTOU, + TARGET_SIGIO, + TARGET_SIGXCPU, + TARGET_SIGXFSZ, + TARGET_SIGVTALRM, + TARGET_SIGPROF, + TARGET_SIGWINCH, + -1, /* SIGLOST */ + TARGET_SIGUSR1, + TARGET_SIGUSR2, +#ifdef TARGET_SIGPWR + TARGET_SIGPWR, +#else + -1, +#endif + -1, /* SIGPOLL */ + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, +#ifdef __SIGRTMIN + __SIGRTMIN + 1, + __SIGRTMIN + 2, + __SIGRTMIN + 3, + __SIGRTMIN + 4, + __SIGRTMIN + 5, + __SIGRTMIN + 6, + __SIGRTMIN + 7, + __SIGRTMIN + 8, + __SIGRTMIN + 9, + __SIGRTMIN + 10, + __SIGRTMIN + 11, + __SIGRTMIN + 12, + __SIGRTMIN + 13, + __SIGRTMIN + 14, + __SIGRTMIN + 15, + __SIGRTMIN + 16, + __SIGRTMIN + 17, + __SIGRTMIN + 18, + __SIGRTMIN + 19, + __SIGRTMIN + 20, + __SIGRTMIN + 21, + __SIGRTMIN + 22, + __SIGRTMIN + 23, + __SIGRTMIN + 24, + __SIGRTMIN + 25, + __SIGRTMIN + 26, + __SIGRTMIN + 27, + __SIGRTMIN + 28, + __SIGRTMIN + 29, + __SIGRTMIN + 30, + __SIGRTMIN + 31, + -1, /* SIGCANCEL */ + __SIGRTMIN, + __SIGRTMIN + 32, + __SIGRTMIN + 33, + __SIGRTMIN + 34, + __SIGRTMIN + 35, + __SIGRTMIN + 36, + __SIGRTMIN + 37, + __SIGRTMIN + 38, + __SIGRTMIN + 39, + __SIGRTMIN + 40, + __SIGRTMIN + 41, + __SIGRTMIN + 42, + __SIGRTMIN + 43, + __SIGRTMIN + 44, + __SIGRTMIN + 45, + __SIGRTMIN + 46, + __SIGRTMIN + 47, + __SIGRTMIN + 48, + __SIGRTMIN + 49, + __SIGRTMIN + 50, + __SIGRTMIN + 51, + __SIGRTMIN + 52, + __SIGRTMIN + 53, + __SIGRTMIN + 54, + __SIGRTMIN + 55, + __SIGRTMIN + 56, + __SIGRTMIN + 57, + __SIGRTMIN + 58, + __SIGRTMIN + 59, + __SIGRTMIN + 60, + __SIGRTMIN + 61, + __SIGRTMIN + 62, + __SIGRTMIN + 63, + __SIGRTMIN + 64, + __SIGRTMIN + 65, + __SIGRTMIN + 66, + __SIGRTMIN + 67, + __SIGRTMIN + 68, + __SIGRTMIN + 69, + __SIGRTMIN + 70, + __SIGRTMIN + 71, + __SIGRTMIN + 72, + __SIGRTMIN + 73, + __SIGRTMIN + 74, + __SIGRTMIN + 75, + __SIGRTMIN + 76, + __SIGRTMIN + 77, + __SIGRTMIN + 78, + __SIGRTMIN + 79, + __SIGRTMIN + 80, + __SIGRTMIN + 81, + __SIGRTMIN + 82, + __SIGRTMIN + 83, + __SIGRTMIN + 84, + __SIGRTMIN + 85, + __SIGRTMIN + 86, + __SIGRTMIN + 87, + __SIGRTMIN + 88, + __SIGRTMIN + 89, + __SIGRTMIN + 90, + __SIGRTMIN + 91, + __SIGRTMIN + 92, + __SIGRTMIN + 93, + __SIGRTMIN + 94, + __SIGRTMIN + 95, + -1, /* SIGINFO */ + -1, /* UNKNOWN */ + -1, /* DEFAULT */ + -1, + -1, + -1, + -1, + -1, + -1 +#endif +}; + +int gdb_signal_to_target(int sig) +{ + if (sig < ARRAY_SIZE(gdb_signal_table)) { + return gdb_signal_table[sig]; + } else { + return -1; + } +} + +int gdb_target_signal_to_gdb(int sig) +{ + int i; + for (i = 0; i < ARRAY_SIZE(gdb_signal_table); i++) { + if (gdb_signal_table[i] == sig) { + return i; + } + } + return GDB_SIGNAL_UNKNOWN; +} + +int gdb_get_cpu_index(CPUState *cpu) +{ + TaskState *ts = get_task_state(cpu); + return ts ? ts->ts_tid : -1; +} + +/* + * User-mode specific command helpers + */ + +void gdb_handle_query_offsets(GArray *params, void *user_ctx) +{ + TaskState *ts; + + 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); + gdb_put_strbuf(); +} + +#if defined(CONFIG_LINUX) +/* Partial user only duplicate of helper in gdbstub.c */ +static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr, + uint8_t *buf, int len, bool is_write) +{ + CPUClass *cc; + cc = CPU_GET_CLASS(cpu); + if (cc->memory_rw_debug) { + return cc->memory_rw_debug(cpu, addr, buf, len, is_write); + } + return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); +} + +void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx) +{ + TaskState *ts; + unsigned long offset, len, saved_auxv, auxv_len; + + if (params->len < 2) { + gdb_put_packet("E22"); + return; + } + + offset = get_param(params, 0)->val_ul; + len = get_param(params, 1)->val_ul; + ts = gdbserver_state.c_cpu->opaque; + saved_auxv = ts->info->saved_auxv; + auxv_len = ts->info->auxv_len; + + if (offset >= auxv_len) { + gdb_put_packet("E00"); + return; + } + + if (len > (MAX_PACKET_LENGTH - 5) / 2) { + len = (MAX_PACKET_LENGTH - 5) / 2; + } + + if (len < auxv_len - offset) { + g_string_assign(gdbserver_state.str_buf, "m"); + } else { + g_string_assign(gdbserver_state.str_buf, "l"); + len = auxv_len - offset; + } + + g_byte_array_set_size(gdbserver_state.mem_buf, len); + if (target_memory_rw_debug(gdbserver_state.g_cpu, saved_auxv + offset, + gdbserver_state.mem_buf->data, len, false)) { + gdb_put_packet("E14"); + return; + } + + gdb_memtox(gdbserver_state.str_buf, + (const char *)gdbserver_state.mem_buf->data, len); + gdb_put_packet_binary(gdbserver_state.str_buf->str, + gdbserver_state.str_buf->len, true); +} +#endif + +static const char *get_filename_param(GArray *params, int i) +{ + const char *hex_filename = get_param(params, i)->data; + gdb_hextomem(gdbserver_state.mem_buf, hex_filename, + strlen(hex_filename) / 2); + g_byte_array_append(gdbserver_state.mem_buf, (const guint8 *)"", 1); + return (const char *)gdbserver_state.mem_buf->data; +} + +static void hostio_reply_with_data(const void *buf, size_t n) +{ + g_string_printf(gdbserver_state.str_buf, "F%zx;", n); + gdb_memtox(gdbserver_state.str_buf, buf, n); + gdb_put_packet_binary(gdbserver_state.str_buf->str, + gdbserver_state.str_buf->len, true); +} + +void gdb_handle_v_file_open(GArray *params, void *user_ctx) +{ + const char *filename = get_filename_param(params, 0); + uint64_t flags = get_param(params, 1)->val_ull; + uint64_t mode = get_param(params, 2)->val_ull; + +#ifdef CONFIG_LINUX + int fd = do_guest_openat(cpu_env(gdbserver_state.g_cpu), 0, filename, + flags, mode, false); +#else + int fd = open(filename, flags, mode); +#endif + if (fd < 0) { + g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); + } else { + g_string_printf(gdbserver_state.str_buf, "F%d", fd); + } + gdb_put_strbuf(); +} + +void gdb_handle_v_file_close(GArray *params, void *user_ctx) +{ + int fd = get_param(params, 0)->val_ul; + + if (close(fd) == -1) { + g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); + gdb_put_strbuf(); + return; + } + + gdb_put_packet("F00"); +} + +void gdb_handle_v_file_pread(GArray *params, void *user_ctx) +{ + int fd = get_param(params, 0)->val_ul; + size_t count = get_param(params, 1)->val_ull; + off_t offset = get_param(params, 2)->val_ull; + + size_t bufsiz = MIN(count, BUFSIZ); + g_autofree char *buf = g_try_malloc(bufsiz); + if (buf == NULL) { + gdb_put_packet("E12"); + return; + } + + ssize_t n = pread(fd, buf, bufsiz, offset); + if (n < 0) { + g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); + gdb_put_strbuf(); + return; + } + hostio_reply_with_data(buf, n); +} + +void gdb_handle_v_file_readlink(GArray *params, void *user_ctx) +{ + const char *filename = get_filename_param(params, 0); + + g_autofree char *buf = g_try_malloc(BUFSIZ); + if (buf == NULL) { + gdb_put_packet("E12"); + return; + } + +#ifdef CONFIG_LINUX + ssize_t n = do_guest_readlink(filename, buf, BUFSIZ); +#else + ssize_t n = readlink(filename, buf, BUFSIZ); +#endif + if (n < 0) { + g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); + gdb_put_strbuf(); + return; + } + hostio_reply_with_data(buf, n); +} + +void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx) +{ + uint32_t pid = get_param(params, 0)->val_ul; + uint32_t offset = get_param(params, 1)->val_ul; + uint32_t length = get_param(params, 2)->val_ul; + + GDBProcess *process = gdb_get_process(pid); + if (!process) { + gdb_put_packet("E00"); + return; + } + + CPUState *cpu = gdb_get_first_cpu_in_process(process); + if (!cpu) { + gdb_put_packet("E00"); + return; + } + + TaskState *ts = get_task_state(cpu); + if (!ts || !ts->bprm || !ts->bprm->filename) { + gdb_put_packet("E00"); + return; + } + + size_t total_length = strlen(ts->bprm->filename); + if (offset > total_length) { + gdb_put_packet("E00"); + return; + } + if (offset + length > total_length) { + length = total_length - offset; + } + + g_string_printf(gdbserver_state.str_buf, "l%.*s", length, + ts->bprm->filename + offset); + gdb_put_strbuf(); +} + +int gdb_target_sigtrap(void) +{ + return TARGET_SIGTRAP; +} diff --git a/gdbstub/user.c b/gdbstub/user.c new file mode 100644 index 0000000000..edeb72efeb --- /dev/null +++ b/gdbstub/user.c @@ -0,0 +1,876 @@ +/* + * gdbstub user-mode helper routines. + * + * We know for user-mode we are using TCG so we can call stuff directly. + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2022 Linaro Ltd + * + * SPDX-License-Identifier: LGPL-2.0+ + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/cutils.h" +#include "qemu/sockets.h" +#include "exec/hwaddr.h" +#include "exec/tb-flush.h" +#include "exec/gdbstub.h" +#include "gdbstub/syscalls.h" +#include "gdbstub/user.h" +#include "hw/core/cpu.h" +#include "trace.h" +#include "internals.h" + +#define GDB_NR_SYSCALLS 1024 +typedef unsigned long GDBSyscallsMask[BITS_TO_LONGS(GDB_NR_SYSCALLS)]; + +/* + * Forked child talks to its parent in order to let GDB enforce the + * follow-fork-mode. This happens inside a start_exclusive() section, so that + * the other threads, which may be forking too, do not interfere. The + * implementation relies on GDB not sending $vCont until it has detached + * either from the parent (follow-fork-mode child) or from the child + * (follow-fork-mode parent). + * + * The parent and the child share the GDB socket; at any given time only one + * of them is allowed to use it, as is reflected in the respective fork_state. + * This is negotiated via the fork_sockets pair as a reaction to $Hg. + * + * Below is a short summary of the possible state transitions: + * + * ENABLED : Terminal state. + * DISABLED : Terminal state. + * ACTIVE : Parent initial state. + * INACTIVE : Child initial state. + * ACTIVE -> DEACTIVATING: On $Hg. + * ACTIVE -> ENABLING : On $D. + * ACTIVE -> DISABLING : On $D. + * ACTIVE -> DISABLED : On communication error. + * DEACTIVATING -> INACTIVE : On gdb_read_byte() return. + * DEACTIVATING -> DISABLED : On communication error. + * INACTIVE -> ACTIVE : On $Hg in the peer. + * INACTIVE -> ENABLE : On $D in the peer. + * INACTIVE -> DISABLE : On $D in the peer. + * INACTIVE -> DISABLED : On communication error. + * ENABLING -> ENABLED : On gdb_read_byte() return. + * ENABLING -> DISABLED : On communication error. + * DISABLING -> DISABLED : On gdb_read_byte() return. + */ +enum GDBForkState { + /* Fully owning the GDB socket. */ + GDB_FORK_ENABLED, + /* Working with the GDB socket; the peer is inactive. */ + GDB_FORK_ACTIVE, + /* Handing off the GDB socket to the peer. */ + GDB_FORK_DEACTIVATING, + /* The peer is working with the GDB socket. */ + GDB_FORK_INACTIVE, + /* Asking the peer to close its GDB socket fd. */ + GDB_FORK_ENABLING, + /* Asking the peer to take over, closing our GDB socket fd. */ + GDB_FORK_DISABLING, + /* The peer has taken over, our GDB socket fd is closed. */ + GDB_FORK_DISABLED, +}; + +enum GDBForkMessage { + GDB_FORK_ACTIVATE = 'a', + GDB_FORK_ENABLE = 'e', + GDB_FORK_DISABLE = 'd', +}; + +/* User-mode specific state */ +typedef struct { + int fd; + char *socket_path; + int running_state; + /* + * Store syscalls mask without memory allocation in order to avoid + * implementing synchronization. + */ + bool catch_all_syscalls; + GDBSyscallsMask catch_syscalls_mask; + bool fork_events; + enum GDBForkState fork_state; + int fork_sockets[2]; + pid_t fork_peer_pid, fork_peer_tid; + uint8_t siginfo[MAX_SIGINFO_LENGTH]; + unsigned long siginfo_len; +} GDBUserState; + +static GDBUserState gdbserver_user_state; + +int gdb_get_char(void) +{ + uint8_t ch; + int ret; + + for (;;) { + ret = recv(gdbserver_user_state.fd, &ch, 1, 0); + if (ret < 0) { + if (errno == ECONNRESET) { + gdbserver_user_state.fd = -1; + } + if (errno != EINTR) { + return -1; + } + } else if (ret == 0) { + close(gdbserver_user_state.fd); + gdbserver_user_state.fd = -1; + return -1; + } else { + break; + } + } + return ch; +} + +bool gdb_got_immediate_ack(void) +{ + int i; + + i = gdb_get_char(); + if (i < 0) { + /* no response, continue anyway */ + return true; + } + + if (i == '+') { + /* received correctly, continue */ + return true; + } + + /* anything else, including '-' then try again */ + return false; +} + +void gdb_put_buffer(const uint8_t *buf, int len) +{ + int ret; + + while (len > 0) { + ret = send(gdbserver_user_state.fd, buf, len, 0); + if (ret < 0) { + if (errno != EINTR) { + return; + } + } else { + buf += ret; + len -= ret; + } + } +} + +/* Tell the remote gdb that the process has exited. */ +void gdb_exit(int code) +{ + char buf[4]; + + if (!gdbserver_state.init) { + return; + } + if (gdbserver_user_state.socket_path) { + unlink(gdbserver_user_state.socket_path); + } + if (gdbserver_user_state.fd < 0) { + return; + } + + trace_gdbstub_op_exiting((uint8_t)code); + + if (gdbserver_state.allow_stop_reply) { + snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); + gdb_put_packet(buf); + gdbserver_state.allow_stop_reply = false; + } + +} + +void gdb_qemu_exit(int code) +{ + exit(code); +} + +int gdb_handlesig(CPUState *cpu, int sig, const char *reason, void *siginfo, + int siginfo_len) +{ + char buf[256]; + int n; + + if (!gdbserver_state.init || gdbserver_user_state.fd < 0) { + return sig; + } + + if (siginfo) { + /* + * Save target-specific siginfo. + * + * siginfo size, i.e. siginfo_len, is asserted at compile-time to fit in + * gdbserver_user_state.siginfo, usually in the source file calling + * gdb_handlesig. See, for instance, {linux,bsd}-user/signal.c. + */ + memcpy(gdbserver_user_state.siginfo, siginfo, siginfo_len); + gdbserver_user_state.siginfo_len = siginfo_len; + } + + /* disable single step if it was enabled */ + cpu_single_step(cpu, 0); + tb_flush(cpu); + + if (sig != 0) { + gdb_set_stop_cpu(cpu); + if (gdbserver_state.allow_stop_reply) { + g_string_printf(gdbserver_state.str_buf, + "T%02xthread:", gdb_target_signal_to_gdb(sig)); + gdb_append_thread_id(cpu, gdbserver_state.str_buf); + g_string_append_c(gdbserver_state.str_buf, ';'); + if (reason) { + g_string_append(gdbserver_state.str_buf, reason); + } + gdb_put_strbuf(); + gdbserver_state.allow_stop_reply = false; + } + } + /* + * gdb_put_packet() might have detected that the peer terminated the + * connection. + */ + if (gdbserver_user_state.fd < 0) { + return sig; + } + + sig = 0; + gdbserver_state.state = RS_IDLE; + gdbserver_user_state.running_state = 0; + while (gdbserver_user_state.running_state == 0) { + n = read(gdbserver_user_state.fd, buf, 256); + if (n > 0) { + int i; + + for (i = 0; i < n; i++) { + gdb_read_byte(buf[i]); + } + } else { + /* + * XXX: Connection closed. Should probably wait for another + * connection before continuing. + */ + if (n == 0) { + close(gdbserver_user_state.fd); + } + gdbserver_user_state.fd = -1; + return sig; + } + } + 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) +{ + char buf[4]; + + if (!gdbserver_state.init || gdbserver_user_state.fd < 0 || + !gdbserver_state.allow_stop_reply) { + return; + } + + snprintf(buf, sizeof(buf), "X%02x", gdb_target_signal_to_gdb(sig)); + gdb_put_packet(buf); + gdbserver_state.allow_stop_reply = false; +} + +static void gdb_accept_init(int fd) +{ + gdb_init_gdbserver_state(); + gdb_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_user_state.fd = fd; +} + +static bool gdb_accept_socket(int gdb_fd) +{ + int fd; + + for (;;) { + fd = accept(gdb_fd, NULL, NULL); + if (fd < 0 && errno != EINTR) { + perror("accept socket"); + return false; + } else if (fd >= 0) { + qemu_set_cloexec(fd); + break; + } + } + + gdb_accept_init(fd); + return true; +} + +static int gdbserver_open_socket(const char *path) +{ + struct sockaddr_un sockaddr = {}; + int fd, ret; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + perror("create socket"); + return -1; + } + + sockaddr.sun_family = AF_UNIX; + pstrcpy(sockaddr.sun_path, sizeof(sockaddr.sun_path) - 1, path); + ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); + if (ret < 0) { + perror("bind socket"); + close(fd); + return -1; + } + ret = listen(fd, 1); + if (ret < 0) { + perror("listen socket"); + close(fd); + return -1; + } + + return fd; +} + +static bool gdb_accept_tcp(int gdb_fd) +{ + struct sockaddr_in sockaddr = {}; + socklen_t len; + int fd; + + for (;;) { + len = sizeof(sockaddr); + fd = accept(gdb_fd, (struct sockaddr *)&sockaddr, &len); + if (fd < 0 && errno != EINTR) { + perror("accept"); + return false; + } else if (fd >= 0) { + qemu_set_cloexec(fd); + break; + } + } + + /* set short latency */ + if (socket_set_nodelay(fd)) { + perror("setsockopt"); + close(fd); + return false; + } + + gdb_accept_init(fd); + return true; +} + +static int gdbserver_open_port(int port) +{ + struct sockaddr_in sockaddr; + int fd, ret; + + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + qemu_set_cloexec(fd); + + socket_set_fast_reuse(fd); + + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(port); + sockaddr.sin_addr.s_addr = 0; + ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); + if (ret < 0) { + perror("bind"); + close(fd); + return -1; + } + ret = listen(fd, 1); + if (ret < 0) { + perror("listen"); + close(fd); + return -1; + } + + return fd; +} + +int gdbserver_start(const char *port_or_path) +{ + int port = g_ascii_strtoull(port_or_path, NULL, 10); + int gdb_fd; + + if (port > 0) { + gdb_fd = gdbserver_open_port(port); + } else { + gdb_fd = gdbserver_open_socket(port_or_path); + } + + if (gdb_fd < 0) { + return -1; + } + + if (port > 0 && gdb_accept_tcp(gdb_fd)) { + return 0; + } else if (gdb_accept_socket(gdb_fd)) { + gdbserver_user_state.socket_path = g_strdup(port_or_path); + return 0; + } + + /* gone wrong */ + close(gdb_fd); + return -1; +} + +void gdbserver_fork_start(void) +{ + if (!gdbserver_state.init || gdbserver_user_state.fd < 0) { + return; + } + if (!gdbserver_user_state.fork_events || + qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, + gdbserver_user_state.fork_sockets) < 0) { + gdbserver_user_state.fork_state = GDB_FORK_DISABLED; + return; + } + gdbserver_user_state.fork_state = GDB_FORK_INACTIVE; + gdbserver_user_state.fork_peer_pid = getpid(); + gdbserver_user_state.fork_peer_tid = qemu_get_thread_id(); +} + +static void disable_gdbstub(CPUState *thread_cpu) +{ + CPUState *cpu; + + close(gdbserver_user_state.fd); + gdbserver_user_state.fd = -1; + CPU_FOREACH(cpu) { + cpu_breakpoint_remove_all(cpu, BP_GDB); + /* no cpu_watchpoint_remove_all for user-mode */ + cpu_single_step(cpu, 0); + } + tb_flush(thread_cpu); +} + +void gdbserver_fork_end(CPUState *cpu, pid_t pid) +{ + char b; + int fd; + + if (!gdbserver_state.init || gdbserver_user_state.fd < 0) { + return; + } + + if (pid == -1) { + if (gdbserver_user_state.fork_state != GDB_FORK_DISABLED) { + g_assert(gdbserver_user_state.fork_state == GDB_FORK_INACTIVE); + close(gdbserver_user_state.fork_sockets[0]); + close(gdbserver_user_state.fork_sockets[1]); + } + return; + } + + if (gdbserver_user_state.fork_state == GDB_FORK_DISABLED) { + if (pid == 0) { + disable_gdbstub(cpu); + } + return; + } + + if (pid == 0) { + close(gdbserver_user_state.fork_sockets[0]); + fd = gdbserver_user_state.fork_sockets[1]; + g_assert(gdbserver_state.process_num == 1); + g_assert(gdbserver_state.processes[0].pid == + gdbserver_user_state.fork_peer_pid); + g_assert(gdbserver_state.processes[0].attached); + gdbserver_state.processes[0].pid = getpid(); + } else { + close(gdbserver_user_state.fork_sockets[1]); + fd = gdbserver_user_state.fork_sockets[0]; + gdbserver_user_state.fork_state = GDB_FORK_ACTIVE; + gdbserver_user_state.fork_peer_pid = pid; + gdbserver_user_state.fork_peer_tid = pid; + + if (!gdbserver_state.allow_stop_reply) { + goto fail; + } + g_string_printf(gdbserver_state.str_buf, + "T%02xfork:p%02x.%02x;thread:p%02x.%02x;", + gdb_target_signal_to_gdb(gdb_target_sigtrap()), + pid, pid, (int)getpid(), qemu_get_thread_id()); + gdb_put_strbuf(); + } + + gdbserver_state.state = RS_IDLE; + gdbserver_state.allow_stop_reply = false; + gdbserver_user_state.running_state = 0; + for (;;) { + switch (gdbserver_user_state.fork_state) { + case GDB_FORK_ENABLED: + if (gdbserver_user_state.running_state) { + close(fd); + return; + } + QEMU_FALLTHROUGH; + case GDB_FORK_ACTIVE: + if (read(gdbserver_user_state.fd, &b, 1) != 1) { + goto fail; + } + gdb_read_byte(b); + break; + case GDB_FORK_DEACTIVATING: + b = GDB_FORK_ACTIVATE; + if (write(fd, &b, 1) != 1) { + goto fail; + } + gdbserver_user_state.fork_state = GDB_FORK_INACTIVE; + break; + case GDB_FORK_INACTIVE: + if (read(fd, &b, 1) != 1) { + goto fail; + } + switch (b) { + case GDB_FORK_ACTIVATE: + gdbserver_user_state.fork_state = GDB_FORK_ACTIVE; + break; + case GDB_FORK_ENABLE: + gdbserver_user_state.fork_state = GDB_FORK_ENABLED; + break; + case GDB_FORK_DISABLE: + gdbserver_user_state.fork_state = GDB_FORK_DISABLED; + break; + default: + g_assert_not_reached(); + } + break; + case GDB_FORK_ENABLING: + b = GDB_FORK_DISABLE; + if (write(fd, &b, 1) != 1) { + goto fail; + } + gdbserver_user_state.fork_state = GDB_FORK_ENABLED; + break; + case GDB_FORK_DISABLING: + b = GDB_FORK_ENABLE; + if (write(fd, &b, 1) != 1) { + goto fail; + } + gdbserver_user_state.fork_state = GDB_FORK_DISABLED; + break; + case GDB_FORK_DISABLED: + close(fd); + disable_gdbstub(cpu); + return; + default: + g_assert_not_reached(); + } + } + +fail: + close(fd); + if (pid == 0) { + disable_gdbstub(cpu); + } +} + +void gdb_handle_query_supported_user(const char *gdb_supported) +{ + if (strstr(gdb_supported, "fork-events+")) { + gdbserver_user_state.fork_events = true; + } + g_string_append(gdbserver_state.str_buf, ";fork-events+"); +} + +bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid) +{ + if (gdbserver_user_state.fork_state == GDB_FORK_ACTIVE && + pid == gdbserver_user_state.fork_peer_pid && + tid == gdbserver_user_state.fork_peer_tid) { + gdbserver_user_state.fork_state = GDB_FORK_DEACTIVATING; + gdb_put_packet("OK"); + return true; + } + return false; +} + +bool gdb_handle_detach_user(uint32_t pid) +{ + bool enable; + + if (gdbserver_user_state.fork_state == GDB_FORK_ACTIVE) { + enable = pid == gdbserver_user_state.fork_peer_pid; + if (enable || pid == getpid()) { + gdbserver_user_state.fork_state = enable ? GDB_FORK_ENABLING : + GDB_FORK_DISABLING; + gdb_put_packet("OK"); + return true; + } + } + return false; +} + +/* + * Execution state helpers + */ + +void gdb_handle_query_attached(GArray *params, void *user_ctx) +{ + gdb_put_packet("0"); +} + +void gdb_continue(void) +{ + gdbserver_user_state.running_state = 1; + trace_gdbstub_op_continue(); +} + +/* + * Resume execution, for user-mode emulation it's equivalent to + * gdb_continue. + */ +int gdb_continue_partial(char *newstates) +{ + CPUState *cpu; + int res = 0; + /* + * This is not exactly accurate, but it's an improvement compared to the + * previous situation, where only one CPU would be single-stepped. + */ + CPU_FOREACH(cpu) { + if (newstates[cpu->cpu_index] == 's') { + trace_gdbstub_op_stepping(cpu->cpu_index); + cpu_single_step(cpu, gdbserver_state.sstep_flags); + } + } + gdbserver_user_state.running_state = 1; + return res; +} + +/* + * Memory access helpers + */ +int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, + uint8_t *buf, int len, bool is_write) +{ + CPUClass *cc; + + cc = CPU_GET_CLASS(cpu); + if (cc->memory_rw_debug) { + return cc->memory_rw_debug(cpu, addr, buf, len, is_write); + } + return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); +} + +/* + * cpu helpers + */ + +unsigned int gdb_get_max_cpus(void) +{ + CPUState *cpu; + unsigned int max_cpus = 1; + + CPU_FOREACH(cpu) { + max_cpus = max_cpus <= cpu->cpu_index ? cpu->cpu_index + 1 : max_cpus; + } + + return max_cpus; +} + +/* replay not supported for user-mode */ +bool gdb_can_reverse(void) +{ + return false; +} + +/* + * Break/Watch point helpers + */ + +bool gdb_supports_guest_debug(void) +{ + /* user-mode == TCG == supported */ + return true; +} + +int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len) +{ + CPUState *cpu; + int err = 0; + + switch (type) { + case GDB_BREAKPOINT_SW: + case GDB_BREAKPOINT_HW: + CPU_FOREACH(cpu) { + err = cpu_breakpoint_insert(cpu, addr, BP_GDB, NULL); + if (err) { + break; + } + } + return err; + default: + /* user-mode doesn't support watchpoints */ + return -ENOSYS; + } +} + +int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len) +{ + CPUState *cpu; + int err = 0; + + switch (type) { + case GDB_BREAKPOINT_SW: + case GDB_BREAKPOINT_HW: + CPU_FOREACH(cpu) { + err = cpu_breakpoint_remove(cpu, addr, BP_GDB); + if (err) { + break; + } + } + return err; + default: + /* user-mode doesn't support watchpoints */ + return -ENOSYS; + } +} + +void gdb_breakpoint_remove_all(CPUState *cs) +{ + cpu_breakpoint_remove_all(cs, BP_GDB); +} + +/* + * For user-mode syscall support we send the system call immediately + * and then 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). + */ +void gdb_syscall_handling(const char *syscall_packet) +{ + gdb_put_packet(syscall_packet); + gdb_handlesig(gdbserver_state.c_cpu, 0, NULL, NULL, 0); +} + +static bool should_catch_syscall(int num) +{ + if (gdbserver_user_state.catch_all_syscalls) { + return true; + } + if (num < 0 || num >= GDB_NR_SYSCALLS) { + return false; + } + return test_bit(num, gdbserver_user_state.catch_syscalls_mask); +} + +void gdb_syscall_entry(CPUState *cs, int num) +{ + if (should_catch_syscall(num)) { + g_autofree char *reason = g_strdup_printf("syscall_entry:%x;", num); + gdb_handlesig(cs, gdb_target_sigtrap(), reason, NULL, 0); + } +} + +void gdb_syscall_return(CPUState *cs, int num) +{ + if (should_catch_syscall(num)) { + g_autofree char *reason = g_strdup_printf("syscall_return:%x;", num); + gdb_handlesig(cs, gdb_target_sigtrap(), reason, NULL, 0); + } +} + +void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx) +{ + const char *param = get_param(params, 0)->data; + GDBSyscallsMask catch_syscalls_mask; + bool catch_all_syscalls; + unsigned int num; + const char *p; + + /* "0" means not catching any syscalls. */ + if (strcmp(param, "0") == 0) { + gdbserver_user_state.catch_all_syscalls = false; + memset(gdbserver_user_state.catch_syscalls_mask, 0, + sizeof(gdbserver_user_state.catch_syscalls_mask)); + gdb_put_packet("OK"); + return; + } + + /* "1" means catching all syscalls. */ + if (strcmp(param, "1") == 0) { + gdbserver_user_state.catch_all_syscalls = true; + gdb_put_packet("OK"); + return; + } + + /* + * "1;..." means catching only the specified syscalls. + * The syscall list must not be empty. + */ + if (param[0] == '1' && param[1] == ';') { + catch_all_syscalls = false; + memset(catch_syscalls_mask, 0, sizeof(catch_syscalls_mask)); + for (p = ¶m[2];; p++) { + if (qemu_strtoui(p, &p, 16, &num) || (*p && *p != ';')) { + goto err; + } + if (num >= GDB_NR_SYSCALLS) { + /* + * Fall back to reporting all syscalls. Reporting extra + * syscalls is inefficient, but the spec explicitly allows it. + * Keep parsing in case there is a syntax error ahead. + */ + catch_all_syscalls = true; + } else { + set_bit(num, catch_syscalls_mask); + } + if (!*p) { + break; + } + } + gdbserver_user_state.catch_all_syscalls = catch_all_syscalls; + if (!catch_all_syscalls) { + memcpy(gdbserver_user_state.catch_syscalls_mask, + catch_syscalls_mask, sizeof(catch_syscalls_mask)); + } + gdb_put_packet("OK"); + return; + } + +err: + gdb_put_packet("E00"); +} + +void gdb_handle_query_xfer_siginfo(GArray *params, void *user_ctx) +{ + unsigned long offset, len; + uint8_t *siginfo_offset; + + offset = get_param(params, 0)->val_ul; + len = get_param(params, 1)->val_ul; + + if (offset + len > gdbserver_user_state.siginfo_len) { + /* Invalid offset and/or requested length. */ + gdb_put_packet("E01"); + return; + } + + siginfo_offset = (uint8_t *)gdbserver_user_state.siginfo + offset; + + /* Reply */ + g_string_assign(gdbserver_state.str_buf, "l"); + gdb_memtox(gdbserver_state.str_buf, (const char *)siginfo_offset, len); + gdb_put_packet_binary(gdbserver_state.str_buf->str, + gdbserver_state.str_buf->len, true); +} diff --git a/gitdm.config b/gitdm.config index 288b100d89..9db43ca142 100644 --- a/gitdm.config +++ b/gitdm.config @@ -31,8 +31,11 @@ EmailMap contrib/gitdm/domain-map # identifiable corporate emails. Please keep this list sorted. # +GroupMap contrib/gitdm/group-map-alibaba Alibaba +GroupMap contrib/gitdm/group-map-amd AMD GroupMap contrib/gitdm/group-map-cadence Cadence Design Systems GroupMap contrib/gitdm/group-map-codeweavers CodeWeavers +GroupMap contrib/gitdm/group-map-facebook Facebook GroupMap contrib/gitdm/group-map-ibm IBM GroupMap contrib/gitdm/group-map-janustech Janus Technologies GroupMap contrib/gitdm/group-map-netflix Netflix diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 834bed089e..ad1b1306e3 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -1,8 +1,8 @@ -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 See docs/devel/docs.rst for the format of this file. +HXCOMM +HXCOMM This file defines the contents of an array of HMPCommand structs +HXCOMM which specify the name, behaviour and help text for HMP commands. +HXCOMM Text between SRST and ERST is rST format documentation. 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 @@ -100,9 +100,11 @@ ERST { .name = "registers", - .args_type = "cpustate_all:-a", - .params = "[-a]", - .help = "show the cpu registers (-a: all - show register info for all cpus)", + .args_type = "cpustate_all:-a,vcpu:i?", + .params = "[-a|vcpu]", + .help = "show the cpu registers (-a: show register info for all cpus;" + " vcpu: specific vCPU to query; show the current CPU's registers if" + " no argument is specified)", .cmd = hmp_info_registers, }, @@ -358,21 +360,6 @@ SRST Show host USB devices. ERST -#if defined(CONFIG_TCG) - { - .name = "profile", - .args_type = "", - .params = "", - .help = "show profiling information", - .cmd_info_hrt = qmp_x_query_profile, - }, -#endif - -SRST - ``info profile`` - Show profiling information. -ERST - { .name = "capture", .args_type = "", @@ -553,9 +540,9 @@ ERST { .name = "qtree", - .args_type = "", - .params = "", - .help = "show device tree", + .args_type = "brief:-b", + .params = "[-b]", + .help = "show device tree (-b: brief, omit properties)", .cmd = hmp_info_qtree, }, @@ -865,6 +852,19 @@ SRST Display the vcpu dirty rate information. ERST + { + .name = "vcpu_dirty_limit", + .args_type = "", + .params = "", + .help = "show dirty page limit information of all vCPU", + .cmd = hmp_info_vcpu_dirty_limit, + }, + +SRST + ``info vcpu_dirty_limit`` + Display the vcpu dirty page limit information. +ERST + #if defined(TARGET_I386) { .name = "sgx", @@ -894,3 +894,101 @@ SRST ``info via`` Show guest mos6522 VIA devices. ERST + + { + .name = "stats", + .args_type = "target:s,names:s?,provider:s?", + .params = "target [names] [provider]", + .help = "show statistics for the given target (vm or vcpu); optionally filter by" + "name (comma-separated list, or * for all) and provider", + .cmd = hmp_info_stats, + }, + +SRST + ``stats`` + Show runtime-collected statistics +ERST + + { + .name = "virtio", + .args_type = "", + .params = "", + .help = "List all available virtio devices", + .cmd = hmp_virtio_query, + .flags = "p", + }, + +SRST + ``info virtio`` + List all available virtio devices +ERST + + { + .name = "virtio-status", + .args_type = "path:s", + .params = "path", + .help = "Display status of a given virtio device", + .cmd = hmp_virtio_status, + .flags = "p", + }, + +SRST + ``info virtio-status`` *path* + Display status of a given virtio device +ERST + + { + .name = "virtio-queue-status", + .args_type = "path:s,queue:i", + .params = "path queue", + .help = "Display status of a given virtio queue", + .cmd = hmp_virtio_queue_status, + .flags = "p", + }, + +SRST + ``info virtio-queue-status`` *path* *queue* + Display status of a given virtio queue +ERST + + { + .name = "virtio-vhost-queue-status", + .args_type = "path:s,queue:i", + .params = "path queue", + .help = "Display status of a given vhost queue", + .cmd = hmp_vhost_queue_status, + .flags = "p", + }, + +SRST + ``info virtio-vhost-queue-status`` *path* *queue* + Display status of a given vhost queue +ERST + + { + .name = "virtio-queue-element", + .args_type = "path:s,queue:i,index:i?", + .params = "path queue [index]", + .help = "Display element of a given virtio queue", + .cmd = hmp_virtio_queue_element, + .flags = "p", + }, + +SRST + ``info virtio-queue-element`` *path* *queue* [*index*] + Display element of a given virtio queue +ERST + + { + .name = "cryptodev", + .args_type = "", + .params = "", + .help = "show the crypto devices", + .cmd = hmp_info_cryptodev, + .flags = "p", + }, + +SRST + ``info cryptodev`` + Show the crypto devices. +ERST diff --git a/hmp-commands.hx b/hmp-commands.hx index 564f1de364..2e2a3bcf98 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1,8 +1,8 @@ -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 See docs/devel/docs.rst for the format of this file. +HXCOMM +HXCOMM This file defines the contents of an array of HMPCommand structs +HXCOMM which specify the name, behaviour and help text for HMP commands. +HXCOMM Text between SRST and ERST is rST format documentation. HXCOMM HXCOMM can be used for comments, discarded from both rST and C. @@ -11,7 +11,7 @@ HXCOMM HXCOMM can be used for comments, discarded from both rST and C. .args_type = "name:S?", .params = "[cmd]", .help = "show the help", - .cmd = do_help_cmd, + .cmd = hmp_help, .flags = "p", }, @@ -78,6 +78,7 @@ ERST .help = "resize a block image", .cmd = hmp_block_resize, .coroutine = true, + .flags = "p", }, SRST @@ -94,6 +95,7 @@ ERST .params = "device [speed [base]]", .help = "copy data from a backing file into a block device", .cmd = hmp_block_stream, + .flags = "p", }, SRST @@ -107,6 +109,7 @@ ERST .params = "device speed", .help = "set maximum speed for a background block operation", .cmd = hmp_block_job_set_speed, + .flags = "p", }, SRST @@ -122,6 +125,7 @@ ERST "\n\t\t\t if you want to abort the operation immediately" "\n\t\t\t instead of keep running until data is in sync)", .cmd = hmp_block_job_cancel, + .flags = "p", }, SRST @@ -135,6 +139,7 @@ ERST .params = "device", .help = "stop an active background block operation", .cmd = hmp_block_job_complete, + .flags = "p", }, SRST @@ -149,6 +154,7 @@ ERST .params = "device", .help = "pause an active background block operation", .cmd = hmp_block_job_pause, + .flags = "p", }, SRST @@ -162,6 +168,7 @@ ERST .params = "device", .help = "resume a paused background block operation", .cmd = hmp_block_job_resume, + .flags = "p", }, SRST @@ -245,6 +252,7 @@ SRST ERST +#ifdef CONFIG_PIXMAN { .name = "screendump", .args_type = "filename:F,format:-fs,device:s?,head:i?", @@ -260,6 +268,7 @@ SRST ``screendump`` *filename* Save screen into PPM image *filename*. ERST +#endif { .name = "logfile", @@ -372,16 +381,20 @@ SRST ERST { - .name = "singlestep", + .name = "one-insn-per-tb", .args_type = "option:s?", .params = "[on|off]", - .help = "run emulation in singlestep mode or switch to normal mode", - .cmd = hmp_singlestep, + .help = "run emulation with one guest instruction per translation block", + .cmd = hmp_one_insn_per_tb, }, SRST -``singlestep [off]`` - Run the emulation in single step mode. +``one-insn-per-tb [off]`` + Run the emulation with one guest instruction per translation block. + This slows down emulation a lot, but can be useful in some situations, + such as when trying to analyse the logs produced by the ``-d`` option. + This only has an effect when using TCG, not with KVM or other accelerators. + If called with option off, the emulation returns to normal mode. ERST @@ -556,7 +569,7 @@ ERST .args_type = "fmt:/,val:l", .params = "/fmt expr", .help = "print expression value (use $reg for CPU register access)", - .cmd = do_print, + .cmd = hmp_print, }, SRST @@ -1028,6 +1041,7 @@ SRST migration (or once already in postcopy). ERST +#ifdef CONFIG_REPLICATION { .name = "x_colo_lost_heartbeat", .args_type = "", @@ -1036,6 +1050,7 @@ ERST "a failover or takeover is needed.", .cmd = hmp_x_colo_lost_heartbeat, }, +#endif SRST ``x_colo_lost_heartbeat`` @@ -1059,14 +1074,16 @@ ERST { .name = "dump-guest-memory", - .args_type = "paging:-p,detach:-d,windmp:-w,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:l?,length:l?", - .params = "[-p] [-d] [-z|-l|-s|-w] filename [begin length]", + .args_type = "paging:-p,detach:-d,windmp:-w,zlib:-z,lzo:-l,snappy:-s,raw:-R,filename:F,begin:l?,length:l?", + .params = "[-p] [-d] [-z|-l|-s|-w] [-R] filename [begin length]", .help = "dump guest memory into file 'filename'.\n\t\t\t" "-p: do paging to get guest's memory mapping.\n\t\t\t" "-d: return immediately (do not wait for completion).\n\t\t\t" "-z: dump in kdump-compressed format, with zlib compression.\n\t\t\t" "-l: dump in kdump-compressed format, with lzo compression.\n\t\t\t" "-s: dump in kdump-compressed format, with snappy compression.\n\t\t\t" + "-R: when using kdump (-z, -l, -s), use raw rather than makedumpfile-flattened\n\t\t\t" + " format\n\t\t\t" "-w: dump in Windows crashdump format (can be used instead of ELF-dump converting),\n\t\t\t" " for Windows x86 and x64 guests with vmcoreinfo driver only.\n\t\t\t" "begin: the starting physical address.\n\t\t\t" @@ -1089,6 +1106,9 @@ SRST dump in kdump-compressed format, with lzo compression. ``-s`` dump in kdump-compressed format, with snappy compression. + ``-R`` + when using kdump (-z, -l, -s), use raw rather than makedumpfile-flattened + format ``-w`` dump in Windows crashdump format (can be used instead of ELF-dump converting), for Windows x64 guests with vmcoreinfo driver only @@ -1269,7 +1289,10 @@ ERST { .name = "netdev_add", .args_type = "netdev:O", - .params = "[user|tap|socket|vde|bridge|hubport|netmap|vhost-user" + .params = "[user|tap|socket|stream|dgram|vde|bridge|hubport|netmap|vhost-user" +#ifdef CONFIG_AF_XDP + "|af-xdp" +#endif #ifdef CONFIG_VMNET "|vmnet-host|vmnet-shared|vmnet-bridged" #endif @@ -1389,7 +1412,7 @@ ERST { .name = "watchdog_action", .args_type = "action:s", - .params = "[reset|shutdown|poweroff|pause|debug|none]", + .params = "[reset|shutdown|poweroff|pause|debug|none|inject-nmi]", .help = "change watchdog action", .cmd = hmp_watchdog_action, .command_completion = watchdog_action_completion, @@ -1406,6 +1429,7 @@ ERST .params = "nbd_server_start [-a] [-w] host:port", .help = "serve block devices on the given host and port", .cmd = hmp_nbd_server_start, + .flags = "p", }, SRST ``nbd_server_start`` *host*:*port* @@ -1421,6 +1445,7 @@ ERST .params = "nbd_server_add [-w] device [name]", .help = "export a block device via NBD", .cmd = hmp_nbd_server_add, + .flags = "p", }, SRST ``nbd_server_add`` *device* [ *name* ] @@ -1436,6 +1461,7 @@ ERST .params = "nbd_server_remove [-f] name", .help = "remove an export previously exposed via NBD", .cmd = hmp_nbd_server_remove, + .flags = "p", }, SRST ``nbd_server_remove [-f]`` *name* @@ -1452,6 +1478,7 @@ ERST .params = "nbd_server_stop", .help = "stop serving block devices using the NBD protocol", .cmd = hmp_nbd_server_stop, + .flags = "p", }, SRST ``nbd_server_stop`` @@ -1475,12 +1502,14 @@ SRST Inject an MCE on the given CPU (x86 only). ERST +#ifdef CONFIG_POSIX { .name = "getfd", .args_type = "fdname:s", .params = "getfd name", .help = "receive a file descriptor via SCM rights and assign it a name", .cmd = hmp_getfd, + .flags = "p", }, SRST @@ -1489,6 +1518,7 @@ SRST mechanism on unix sockets, it is stored using the name *fdname* for later use by other monitor commands. ERST +#endif { .name = "closefd", @@ -1496,6 +1526,7 @@ ERST .params = "closefd name", .help = "close a file descriptor previously passed via SCM rights", .cmd = hmp_closefd, + .flags = "p", }, SRST @@ -1511,6 +1542,7 @@ ERST .params = "device bps bps_rd bps_wr iops iops_rd iops_wr", .help = "change I/O throttle limits for a block drive", .cmd = hmp_block_set_io_throttle, + .flags = "p", }, SRST @@ -1729,13 +1761,13 @@ SRST ERST { - .name = "info", - .args_type = "item:s?", - .params = "[subcommand]", - .help = "show various information about the system state", - .cmd = hmp_info_help, - .sub_table = hmp_info_cmds, - .flags = "p", + .name = "calc_dirty_rate", + .args_type = "dirty_ring:-r,dirty_bitmap:-b,second:l,sample_pages_per_GB:l?", + .params = "[-r] [-b] second [sample_pages_per_GB]", + .help = "start a round of guest dirty rate measurement (using -r to" + "\n\t\t\t specify dirty ring as the method of calculation and" + "\n\t\t\t -b to specify dirty bitmap as method of calculation)", + .cmd = hmp_calc_dirty_rate, }, SRST @@ -1746,11 +1778,87 @@ SRST ERST { - .name = "calc_dirty_rate", - .args_type = "dirty_ring:-r,dirty_bitmap:-b,second:l,sample_pages_per_GB:l?", - .params = "[-r] [-b] second [sample_pages_per_GB]", - .help = "start a round of guest dirty rate measurement (using -r to" - "\n\t\t\t specify dirty ring as the method of calculation and" - "\n\t\t\t -b to specify dirty bitmap as method of calculation)", - .cmd = hmp_calc_dirty_rate, + .name = "set_vcpu_dirty_limit", + .args_type = "dirty_rate:l,cpu_index:l?", + .params = "dirty_rate [cpu_index]", + .help = "set dirty page rate limit, use cpu_index to set limit" + "\n\t\t\t\t\t on a specified virtual cpu", + .cmd = hmp_set_vcpu_dirty_limit, }, + +SRST +``set_vcpu_dirty_limit`` + Set dirty page rate limit on virtual CPU, the information about all the + virtual CPU dirty limit status can be observed with ``info vcpu_dirty_limit`` + command. +ERST + + { + .name = "cancel_vcpu_dirty_limit", + .args_type = "cpu_index:l?", + .params = "[cpu_index]", + .help = "cancel dirty page rate limit, use cpu_index to cancel" + "\n\t\t\t\t\t limit on a specified virtual cpu", + .cmd = hmp_cancel_vcpu_dirty_limit, + }, + +SRST +``cancel_vcpu_dirty_limit`` + Cancel dirty page rate limit on virtual CPU, the information about all the + virtual CPU dirty limit status can be observed with ``info vcpu_dirty_limit`` + command. +ERST + + { + .name = "info", + .args_type = "item:s?", + .params = "[subcommand]", + .help = "show various information about the system state", + .cmd = hmp_info_help, + .sub_table = hmp_info_cmds, + .flags = "p", + }, + +#if defined(CONFIG_FDT) + { + .name = "dumpdtb", + .args_type = "filename:F", + .params = "filename", + .help = "dump the FDT in dtb format to 'filename'", + .cmd = hmp_dumpdtb, + }, + +SRST +``dumpdtb`` *filename* + Dump the FDT in dtb format to *filename*. +ERST +#endif + +#if defined(CONFIG_XEN_EMU) + { + .name = "xen-event-inject", + .args_type = "port:i", + .params = "port", + .help = "inject event channel", + .cmd = hmp_xen_event_inject, + }, + +SRST +``xen-event-inject`` *port* + Notify guest via event channel on port *port*. +ERST + + + { + .name = "xen-event-list", + .args_type = "", + .params = "", + .help = "list event channel state", + .cmd = hmp_xen_event_list, + }, + +SRST +``xen-event-list`` + List event channels in the guest +ERST +#endif diff --git a/host/include/aarch64/host/atomic128-cas.h b/host/include/aarch64/host/atomic128-cas.h new file mode 100644 index 0000000000..58630107bc --- /dev/null +++ b/host/include/aarch64/host/atomic128-cas.h @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Compare-and-swap for 128-bit atomic operations, AArch64 version. + * + * Copyright (C) 2018, 2023 Linaro, Ltd. + * + * See docs/devel/atomics.rst for discussion about the guarantees each + * atomic primitive is meant to provide. + */ + +#ifndef AARCH64_ATOMIC128_CAS_H +#define AARCH64_ATOMIC128_CAS_H + +/* Through gcc 10, aarch64 has no support for 128-bit atomics. */ +#if defined(CONFIG_ATOMIC128) || defined(CONFIG_CMPXCHG128) +#include "host/include/generic/host/atomic128-cas.h" +#else +static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) +{ + uint64_t cmpl = int128_getlo(cmp), cmph = int128_gethi(cmp); + uint64_t newl = int128_getlo(new), newh = int128_gethi(new); + uint64_t oldl, oldh; + uint32_t tmp; + + asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" + "cmp %[oldl], %[cmpl]\n\t" + "ccmp %[oldh], %[cmph], #0, eq\n\t" + "b.ne 1f\n\t" + "stlxp %w[tmp], %[newl], %[newh], %[mem]\n\t" + "cbnz %w[tmp], 0b\n" + "1:" + : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), + [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) + : [cmpl] "r"(cmpl), [cmph] "r"(cmph), + [newl] "r"(newl), [newh] "r"(newh) + : "memory", "cc"); + + return int128_make128(oldl, oldh); +} + +# define CONFIG_CMPXCHG128 1 +# define HAVE_CMPXCHG128 1 +#endif + +#endif /* AARCH64_ATOMIC128_CAS_H */ diff --git a/host/include/aarch64/host/atomic128-ldst.h b/host/include/aarch64/host/atomic128-ldst.h new file mode 100644 index 0000000000..a08f62c40a --- /dev/null +++ b/host/include/aarch64/host/atomic128-ldst.h @@ -0,0 +1,79 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Load/store for 128-bit atomic operations, AArch64 version. + * + * Copyright (C) 2018, 2023 Linaro, Ltd. + * + * See docs/devel/atomics.rst for discussion about the guarantees each + * atomic primitive is meant to provide. + */ + +#ifndef AARCH64_ATOMIC128_LDST_H +#define AARCH64_ATOMIC128_LDST_H + +#include "host/cpuinfo.h" +#include "tcg/debug-assert.h" + +/* + * Through gcc 10, aarch64 has no support for 128-bit atomics. + * Through clang 16, without -march=armv8.4-a, __atomic_load_16 + * is incorrectly expanded to a read-write operation. + * + * Anyway, this method allows runtime detection of FEAT_LSE2. + */ + +#define HAVE_ATOMIC128_RO (cpuinfo & CPUINFO_LSE2) +#define HAVE_ATOMIC128_RW 1 + +static inline Int128 atomic16_read_ro(const Int128 *ptr) +{ + uint64_t l, h; + + tcg_debug_assert(HAVE_ATOMIC128_RO); + /* With FEAT_LSE2, 16-byte aligned LDP is atomic. */ + asm("ldp %[l], %[h], %[mem]" + : [l] "=r"(l), [h] "=r"(h) : [mem] "m"(*ptr)); + + return int128_make128(l, h); +} + +static inline Int128 atomic16_read_rw(Int128 *ptr) +{ + uint64_t l, h; + uint32_t tmp; + + if (cpuinfo & CPUINFO_LSE2) { + /* With FEAT_LSE2, 16-byte aligned LDP is atomic. */ + asm("ldp %[l], %[h], %[mem]" + : [l] "=r"(l), [h] "=r"(h) : [mem] "m"(*ptr)); + } else { + /* The load must be paired with the store to guarantee not tearing. */ + asm("0: ldxp %[l], %[h], %[mem]\n\t" + "stxp %w[tmp], %[l], %[h], %[mem]\n\t" + "cbnz %w[tmp], 0b" + : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), [l] "=&r"(l), [h] "=&r"(h)); + } + + return int128_make128(l, h); +} + +static inline void atomic16_set(Int128 *ptr, Int128 val) +{ + uint64_t l = int128_getlo(val), h = int128_gethi(val); + uint64_t t1, t2; + + if (cpuinfo & CPUINFO_LSE2) { + /* With FEAT_LSE2, 16-byte aligned STP is atomic. */ + asm("stp %[l], %[h], %[mem]" + : [mem] "=m"(*ptr) : [l] "r"(l), [h] "r"(h)); + } else { + /* Load into temporaries to acquire the exclusive access lock. */ + asm("0: ldxp %[t1], %[t2], %[mem]\n\t" + "stxp %w[t1], %[l], %[h], %[mem]\n\t" + "cbnz %w[t1], 0b" + : [mem] "+m"(*ptr), [t1] "=&r"(t1), [t2] "=&r"(t2) + : [l] "r"(l), [h] "r"(h)); + } +} + +#endif /* AARCH64_ATOMIC128_LDST_H */ diff --git a/host/include/aarch64/host/cpuinfo.h b/host/include/aarch64/host/cpuinfo.h new file mode 100644 index 0000000000..fe671534e4 --- /dev/null +++ b/host/include/aarch64/host/cpuinfo.h @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for AArch64. + */ + +#ifndef HOST_CPUINFO_H +#define HOST_CPUINFO_H + +#define CPUINFO_ALWAYS (1u << 0) /* so cpuinfo is nonzero */ +#define CPUINFO_LSE (1u << 1) +#define CPUINFO_LSE2 (1u << 2) +#define CPUINFO_AES (1u << 3) +#define CPUINFO_PMULL (1u << 4) +#define CPUINFO_BTI (1u << 5) + +/* Initialized with a constructor. */ +extern unsigned cpuinfo; + +/* + * We cannot rely on constructor ordering, so other constructors must + * use the function interface rather than the variable above. + */ +unsigned cpuinfo_init(void); + +#endif /* HOST_CPUINFO_H */ diff --git a/host/include/aarch64/host/crypto/aes-round.h b/host/include/aarch64/host/crypto/aes-round.h new file mode 100644 index 0000000000..8b5f88d50c --- /dev/null +++ b/host/include/aarch64/host/crypto/aes-round.h @@ -0,0 +1,205 @@ +/* + * AArch64 specific aes acceleration. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef AARCH64_HOST_CRYPTO_AES_ROUND_H +#define AARCH64_HOST_CRYPTO_AES_ROUND_H + +#include "host/cpuinfo.h" +#include + +#ifdef __ARM_FEATURE_AES +# define HAVE_AES_ACCEL true +#else +# define HAVE_AES_ACCEL likely(cpuinfo & CPUINFO_AES) +#endif +#if !defined(__ARM_FEATURE_AES) && defined(CONFIG_ARM_AES_BUILTIN) +# define ATTR_AES_ACCEL __attribute__((target("+crypto"))) +#else +# define ATTR_AES_ACCEL +#endif + +static inline uint8x16_t aes_accel_bswap(uint8x16_t x) +{ + return vqtbl1q_u8(x, (uint8x16_t){ 15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0, }); +} + +#ifdef CONFIG_ARM_AES_BUILTIN +# define aes_accel_aesd vaesdq_u8 +# define aes_accel_aese vaeseq_u8 +# define aes_accel_aesmc vaesmcq_u8 +# define aes_accel_aesimc vaesimcq_u8 +# define aes_accel_aesd_imc(S, K) vaesimcq_u8(vaesdq_u8(S, K)) +# define aes_accel_aese_mc(S, K) vaesmcq_u8(vaeseq_u8(S, K)) +#else +static inline uint8x16_t aes_accel_aesd(uint8x16_t d, uint8x16_t k) +{ + asm(".arch_extension aes\n\t" + "aesd %0.16b, %1.16b" : "+w"(d) : "w"(k)); + return d; +} + +static inline uint8x16_t aes_accel_aese(uint8x16_t d, uint8x16_t k) +{ + asm(".arch_extension aes\n\t" + "aese %0.16b, %1.16b" : "+w"(d) : "w"(k)); + return d; +} + +static inline uint8x16_t aes_accel_aesmc(uint8x16_t d) +{ + asm(".arch_extension aes\n\t" + "aesmc %0.16b, %1.16b" : "=w"(d) : "w"(d)); + return d; +} + +static inline uint8x16_t aes_accel_aesimc(uint8x16_t d) +{ + asm(".arch_extension aes\n\t" + "aesimc %0.16b, %1.16b" : "=w"(d) : "w"(d)); + return d; +} + +/* Most CPUs fuse AESD+AESIMC in the execution pipeline. */ +static inline uint8x16_t aes_accel_aesd_imc(uint8x16_t d, uint8x16_t k) +{ + asm(".arch_extension aes\n\t" + "aesd %0.16b, %1.16b\n\t" + "aesimc %0.16b, %0.16b" : "+w"(d) : "w"(k)); + return d; +} + +/* Most CPUs fuse AESE+AESMC in the execution pipeline. */ +static inline uint8x16_t aes_accel_aese_mc(uint8x16_t d, uint8x16_t k) +{ + asm(".arch_extension aes\n\t" + "aese %0.16b, %1.16b\n\t" + "aesmc %0.16b, %0.16b" : "+w"(d) : "w"(k)); + return d; +} +#endif /* CONFIG_ARM_AES_BUILTIN */ + +static inline void ATTR_AES_ACCEL +aesenc_MC_accel(AESState *ret, const AESState *st, bool be) +{ + uint8x16_t t = (uint8x16_t)st->v; + + if (be) { + t = aes_accel_bswap(t); + t = aes_accel_aesmc(t); + t = aes_accel_bswap(t); + } else { + t = aes_accel_aesmc(t); + } + ret->v = (AESStateVec)t; +} + +static inline void ATTR_AES_ACCEL +aesenc_SB_SR_AK_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + uint8x16_t t = (uint8x16_t)st->v; + uint8x16_t z = { }; + + if (be) { + t = aes_accel_bswap(t); + t = aes_accel_aese(t, z); + t = aes_accel_bswap(t); + } else { + t = aes_accel_aese(t, z); + } + ret->v = (AESStateVec)t ^ rk->v; +} + +static inline void ATTR_AES_ACCEL +aesenc_SB_SR_MC_AK_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + uint8x16_t t = (uint8x16_t)st->v; + uint8x16_t z = { }; + + if (be) { + t = aes_accel_bswap(t); + t = aes_accel_aese_mc(t, z); + t = aes_accel_bswap(t); + } else { + t = aes_accel_aese_mc(t, z); + } + ret->v = (AESStateVec)t ^ rk->v; +} + +static inline void ATTR_AES_ACCEL +aesdec_IMC_accel(AESState *ret, const AESState *st, bool be) +{ + uint8x16_t t = (uint8x16_t)st->v; + + if (be) { + t = aes_accel_bswap(t); + t = aes_accel_aesimc(t); + t = aes_accel_bswap(t); + } else { + t = aes_accel_aesimc(t); + } + ret->v = (AESStateVec)t; +} + +static inline void ATTR_AES_ACCEL +aesdec_ISB_ISR_AK_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + uint8x16_t t = (uint8x16_t)st->v; + uint8x16_t z = { }; + + if (be) { + t = aes_accel_bswap(t); + t = aes_accel_aesd(t, z); + t = aes_accel_bswap(t); + } else { + t = aes_accel_aesd(t, z); + } + ret->v = (AESStateVec)t ^ rk->v; +} + +static inline void ATTR_AES_ACCEL +aesdec_ISB_ISR_AK_IMC_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + uint8x16_t t = (uint8x16_t)st->v; + uint8x16_t k = (uint8x16_t)rk->v; + uint8x16_t z = { }; + + if (be) { + t = aes_accel_bswap(t); + k = aes_accel_bswap(k); + t = aes_accel_aesd(t, z); + t ^= k; + t = aes_accel_aesimc(t); + t = aes_accel_bswap(t); + } else { + t = aes_accel_aesd(t, z); + t ^= k; + t = aes_accel_aesimc(t); + } + ret->v = (AESStateVec)t; +} + +static inline void ATTR_AES_ACCEL +aesdec_ISB_ISR_IMC_AK_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + uint8x16_t t = (uint8x16_t)st->v; + uint8x16_t z = { }; + + if (be) { + t = aes_accel_bswap(t); + t = aes_accel_aesd_imc(t, z); + t = aes_accel_bswap(t); + } else { + t = aes_accel_aesd_imc(t, z); + } + ret->v = (AESStateVec)t ^ rk->v; +} + +#endif /* AARCH64_HOST_CRYPTO_AES_ROUND_H */ diff --git a/host/include/aarch64/host/crypto/clmul.h b/host/include/aarch64/host/crypto/clmul.h new file mode 100644 index 0000000000..bb516d8b2f --- /dev/null +++ b/host/include/aarch64/host/crypto/clmul.h @@ -0,0 +1,41 @@ +/* + * AArch64 specific clmul acceleration. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef AARCH64_HOST_CRYPTO_CLMUL_H +#define AARCH64_HOST_CRYPTO_CLMUL_H + +#include "host/cpuinfo.h" +#include + +/* + * 64x64->128 pmull is available with FEAT_PMULL. + * Both FEAT_AES and FEAT_PMULL are covered under the same macro. + */ +#ifdef __ARM_FEATURE_AES +# define HAVE_CLMUL_ACCEL true +#else +# define HAVE_CLMUL_ACCEL likely(cpuinfo & CPUINFO_PMULL) +#endif +#if !defined(__ARM_FEATURE_AES) && defined(CONFIG_ARM_AES_BUILTIN) +# define ATTR_CLMUL_ACCEL __attribute__((target("+crypto"))) +#else +# define ATTR_CLMUL_ACCEL +#endif + +static inline Int128 ATTR_CLMUL_ACCEL +clmul_64_accel(uint64_t n, uint64_t m) +{ + union { poly128_t v; Int128 s; } u; + +#ifdef CONFIG_ARM_AES_BUILTIN + u.v = vmull_p64((poly64_t)n, (poly64_t)m); +#else + asm(".arch_extension aes\n\t" + "pmull %0.1q, %1.1d, %2.1d" : "=w"(u.v) : "w"(n), "w"(m)); +#endif + return u.s; +} + +#endif /* AARCH64_HOST_CRYPTO_CLMUL_H */ diff --git a/host/include/aarch64/host/load-extract-al16-al8.h b/host/include/aarch64/host/load-extract-al16-al8.h new file mode 100644 index 0000000000..bd677c5e26 --- /dev/null +++ b/host/include/aarch64/host/load-extract-al16-al8.h @@ -0,0 +1,40 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Atomic extract 64 from 128-bit, AArch64 version. + * + * Copyright (C) 2023 Linaro, Ltd. + */ + +#ifndef AARCH64_LOAD_EXTRACT_AL16_AL8_H +#define AARCH64_LOAD_EXTRACT_AL16_AL8_H + +#include "host/cpuinfo.h" +#include "tcg/debug-assert.h" + +/** + * load_atom_extract_al16_or_al8: + * @pv: host address + * @s: object size in bytes, @s <= 8. + * + * Load @s bytes from @pv, when pv % s != 0. If [p, p+s-1] does not + * cross an 16-byte boundary then the access must be 16-byte atomic, + * otherwise the access must be 8-byte atomic. + */ +static inline uint64_t load_atom_extract_al16_or_al8(void *pv, int s) +{ + uintptr_t pi = (uintptr_t)pv; + __int128_t *ptr_align = (__int128_t *)(pi & ~7); + int shr = (pi & 7) * 8; + uint64_t l, h; + + /* + * With FEAT_LSE2, LDP is single-copy atomic if 16-byte aligned + * and single-copy atomic on the parts if 8-byte aligned. + * All we need do is align the pointer mod 8. + */ + tcg_debug_assert(HAVE_ATOMIC128_RO); + asm("ldp %0, %1, %2" : "=r"(l), "=r"(h) : "m"(*ptr_align)); + return (l >> shr) | (h << (-shr & 63)); +} + +#endif /* AARCH64_LOAD_EXTRACT_AL16_AL8_H */ diff --git a/host/include/aarch64/host/store-insert-al16.h b/host/include/aarch64/host/store-insert-al16.h new file mode 100644 index 0000000000..1943155bc6 --- /dev/null +++ b/host/include/aarch64/host/store-insert-al16.h @@ -0,0 +1,47 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Atomic store insert into 128-bit, AArch64 version. + * + * Copyright (C) 2023 Linaro, Ltd. + */ + +#ifndef AARCH64_STORE_INSERT_AL16_H +#define AARCH64_STORE_INSERT_AL16_H + +/** + * store_atom_insert_al16: + * @p: host address + * @val: shifted value to store + * @msk: mask for value to store + * + * Atomically store @val to @p masked by @msk. + */ +static inline void ATTRIBUTE_ATOMIC128_OPT +store_atom_insert_al16(Int128 *ps, Int128 val, Int128 msk) +{ + /* + * GCC only implements __sync* primitives for int128 on aarch64. + * We can do better without the barriers, and integrating the + * arithmetic into the load-exclusive/store-conditional pair. + */ + uint64_t tl, th, vl, vh, ml, mh; + uint32_t fail; + + qemu_build_assert(!HOST_BIG_ENDIAN); + vl = int128_getlo(val); + vh = int128_gethi(val); + ml = int128_getlo(msk); + mh = int128_gethi(msk); + + asm("0: ldxp %[l], %[h], %[mem]\n\t" + "bic %[l], %[l], %[ml]\n\t" + "bic %[h], %[h], %[mh]\n\t" + "orr %[l], %[l], %[vl]\n\t" + "orr %[h], %[h], %[vh]\n\t" + "stxp %w[f], %[l], %[h], %[mem]\n\t" + "cbnz %w[f], 0b\n" + : [mem] "+Q"(*ps), [f] "=&r"(fail), [l] "=&r"(tl), [h] "=&r"(th) + : [vl] "r"(vl), [vh] "r"(vh), [ml] "r"(ml), [mh] "r"(mh)); +} + +#endif /* AARCH64_STORE_INSERT_AL16_H */ diff --git a/host/include/generic/host/atomic128-cas.h b/host/include/generic/host/atomic128-cas.h new file mode 100644 index 0000000000..6b40cc2271 --- /dev/null +++ b/host/include/generic/host/atomic128-cas.h @@ -0,0 +1,47 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Compare-and-swap for 128-bit atomic operations, generic version. + * + * Copyright (C) 2018, 2023 Linaro, Ltd. + * + * See docs/devel/atomics.rst for discussion about the guarantees each + * atomic primitive is meant to provide. + */ + +#ifndef HOST_ATOMIC128_CAS_H +#define HOST_ATOMIC128_CAS_H + +#if defined(CONFIG_ATOMIC128) +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) +{ + __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Alias r, c, n; + + c.s = cmp; + n.s = new; + r.i = qatomic_cmpxchg__nocheck(ptr_align, c.i, n.i); + return r.s; +} +# define HAVE_CMPXCHG128 1 +#elif defined(CONFIG_CMPXCHG128) +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) +{ + Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Alias r, c, n; + + c.s = cmp; + n.s = new; + r.i = __sync_val_compare_and_swap_16(ptr_align, c.i, n.i); + return r.s; +} +# define HAVE_CMPXCHG128 1 +#else +/* Fallback definition that must be optimized away, or error. */ +Int128 QEMU_ERROR("unsupported atomic") + atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new); +# define HAVE_CMPXCHG128 0 +#endif + +#endif /* HOST_ATOMIC128_CAS_H */ diff --git a/host/include/generic/host/atomic128-ldst.h b/host/include/generic/host/atomic128-ldst.h new file mode 100644 index 0000000000..691e6a8531 --- /dev/null +++ b/host/include/generic/host/atomic128-ldst.h @@ -0,0 +1,81 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Load/store for 128-bit atomic operations, generic version. + * + * Copyright (C) 2018, 2023 Linaro, Ltd. + * + * See docs/devel/atomics.rst for discussion about the guarantees each + * atomic primitive is meant to provide. + */ + +#ifndef HOST_ATOMIC128_LDST_H +#define HOST_ATOMIC128_LDST_H + +#if defined(CONFIG_ATOMIC128) +# define HAVE_ATOMIC128_RO 1 +# define HAVE_ATOMIC128_RW 1 + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_read_ro(const Int128 *ptr) +{ + const __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Alias r; + + r.i = qatomic_read__nocheck(ptr_align); + return r.s; +} + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_read_rw(Int128 *ptr) +{ + return atomic16_read_ro(ptr); +} + +static inline void ATTRIBUTE_ATOMIC128_OPT +atomic16_set(Int128 *ptr, Int128 val) +{ + __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Alias v; + + v.s = val; + qatomic_set__nocheck(ptr_align, v.i); +} + +#elif defined(CONFIG_CMPXCHG128) +# define HAVE_ATOMIC128_RO 0 +# define HAVE_ATOMIC128_RW 1 + +Int128 QEMU_ERROR("unsupported atomic") atomic16_read_ro(const Int128 *ptr); + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_read_rw(Int128 *ptr) +{ + /* Maybe replace 0 with 0, returning the old value. */ + Int128 z = int128_make64(0); + return atomic16_cmpxchg(ptr, z, z); +} + +static inline void ATTRIBUTE_ATOMIC128_OPT +atomic16_set(Int128 *ptr, Int128 val) +{ + Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16); + __int128_t old; + Int128Alias new; + + new.s = val; + do { + old = *ptr_align; + } while (!__sync_bool_compare_and_swap_16(ptr_align, old, new.i)); +} + +#else +# define HAVE_ATOMIC128_RO 0 +# define HAVE_ATOMIC128_RW 0 + +/* Fallback definitions that must be optimized away, or error. */ +Int128 QEMU_ERROR("unsupported atomic") atomic16_read_ro(const Int128 *ptr); +Int128 QEMU_ERROR("unsupported atomic") atomic16_read_rw(Int128 *ptr); +void QEMU_ERROR("unsupported atomic") atomic16_set(Int128 *ptr, Int128 val); +#endif + +#endif /* HOST_ATOMIC128_LDST_H */ diff --git a/host/include/generic/host/cpuinfo.h b/host/include/generic/host/cpuinfo.h new file mode 100644 index 0000000000..67ad410871 --- /dev/null +++ b/host/include/generic/host/cpuinfo.h @@ -0,0 +1,4 @@ +/* + * No host specific cpu identification. + * SPDX-License-Identifier: GPL-2.0-or-later + */ diff --git a/host/include/generic/host/crypto/aes-round.h b/host/include/generic/host/crypto/aes-round.h new file mode 100644 index 0000000000..1b9720f917 --- /dev/null +++ b/host/include/generic/host/crypto/aes-round.h @@ -0,0 +1,33 @@ +/* + * No host specific aes acceleration. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GENERIC_HOST_CRYPTO_AES_ROUND_H +#define GENERIC_HOST_CRYPTO_AES_ROUND_H + +#define HAVE_AES_ACCEL false +#define ATTR_AES_ACCEL + +void aesenc_MC_accel(AESState *, const AESState *, bool) + QEMU_ERROR("unsupported accel"); +void aesenc_SB_SR_AK_accel(AESState *, const AESState *, + const AESState *, bool) + QEMU_ERROR("unsupported accel"); +void aesenc_SB_SR_MC_AK_accel(AESState *, const AESState *, + const AESState *, bool) + QEMU_ERROR("unsupported accel"); + +void aesdec_IMC_accel(AESState *, const AESState *, bool) + QEMU_ERROR("unsupported accel"); +void aesdec_ISB_ISR_AK_accel(AESState *, const AESState *, + const AESState *, bool) + QEMU_ERROR("unsupported accel"); +void aesdec_ISB_ISR_AK_IMC_accel(AESState *, const AESState *, + const AESState *, bool) + QEMU_ERROR("unsupported accel"); +void aesdec_ISB_ISR_IMC_AK_accel(AESState *, const AESState *, + const AESState *, bool) + QEMU_ERROR("unsupported accel"); + +#endif /* GENERIC_HOST_CRYPTO_AES_ROUND_H */ diff --git a/host/include/generic/host/crypto/clmul.h b/host/include/generic/host/crypto/clmul.h new file mode 100644 index 0000000000..915bfb88d3 --- /dev/null +++ b/host/include/generic/host/crypto/clmul.h @@ -0,0 +1,15 @@ +/* + * No host specific carry-less multiply acceleration. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GENERIC_HOST_CRYPTO_CLMUL_H +#define GENERIC_HOST_CRYPTO_CLMUL_H + +#define HAVE_CLMUL_ACCEL false +#define ATTR_CLMUL_ACCEL + +Int128 clmul_64_accel(uint64_t, uint64_t) + QEMU_ERROR("unsupported accel"); + +#endif /* GENERIC_HOST_CRYPTO_CLMUL_H */ diff --git a/host/include/generic/host/load-extract-al16-al8.h b/host/include/generic/host/load-extract-al16-al8.h new file mode 100644 index 0000000000..d95556130f --- /dev/null +++ b/host/include/generic/host/load-extract-al16-al8.h @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Atomic extract 64 from 128-bit, generic version. + * + * Copyright (C) 2023 Linaro, Ltd. + */ + +#ifndef HOST_LOAD_EXTRACT_AL16_AL8_H +#define HOST_LOAD_EXTRACT_AL16_AL8_H + +/** + * load_atom_extract_al16_or_al8: + * @pv: host address + * @s: object size in bytes, @s <= 8. + * + * Load @s bytes from @pv, when pv % s != 0. If [p, p+s-1] does not + * cross an 16-byte boundary then the access must be 16-byte atomic, + * otherwise the access must be 8-byte atomic. + */ +static inline uint64_t ATTRIBUTE_ATOMIC128_OPT +load_atom_extract_al16_or_al8(void *pv, int s) +{ + uintptr_t pi = (uintptr_t)pv; + int o = pi & 7; + int shr = (HOST_BIG_ENDIAN ? 16 - s - o : o) * 8; + Int128 r; + + pv = (void *)(pi & ~7); + if (pi & 8) { + uint64_t *p8 = __builtin_assume_aligned(pv, 16, 8); + uint64_t a = qatomic_read__nocheck(p8); + uint64_t b = qatomic_read__nocheck(p8 + 1); + + if (HOST_BIG_ENDIAN) { + r = int128_make128(b, a); + } else { + r = int128_make128(a, b); + } + } else { + r = atomic16_read_ro(pv); + } + return int128_getlo(int128_urshift(r, shr)); +} + +#endif /* HOST_LOAD_EXTRACT_AL16_AL8_H */ diff --git a/host/include/generic/host/store-insert-al16.h b/host/include/generic/host/store-insert-al16.h new file mode 100644 index 0000000000..4a1662183d --- /dev/null +++ b/host/include/generic/host/store-insert-al16.h @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Atomic store insert into 128-bit, generic version. + * + * Copyright (C) 2023 Linaro, Ltd. + */ + +#ifndef HOST_STORE_INSERT_AL16_H +#define HOST_STORE_INSERT_AL16_H + +/** + * store_atom_insert_al16: + * @p: host address + * @val: shifted value to store + * @msk: mask for value to store + * + * Atomically store @val to @p masked by @msk. + */ +static inline void ATTRIBUTE_ATOMIC128_OPT +store_atom_insert_al16(Int128 *ps, Int128 val, Int128 msk) +{ +#if defined(CONFIG_ATOMIC128) + __uint128_t *pu; + Int128Alias old, new; + + /* With CONFIG_ATOMIC128, we can avoid the memory barriers. */ + pu = __builtin_assume_aligned(ps, 16); + old.u = *pu; + msk = int128_not(msk); + do { + new.s = int128_and(old.s, msk); + new.s = int128_or(new.s, val); + } while (!__atomic_compare_exchange_n(pu, &old.u, new.u, true, + __ATOMIC_RELAXED, __ATOMIC_RELAXED)); +#else + Int128 old, new, cmp; + + ps = __builtin_assume_aligned(ps, 16); + old = *ps; + msk = int128_not(msk); + do { + cmp = old; + new = int128_and(old, msk); + new = int128_or(new, val); + old = atomic16_cmpxchg(ps, cmp, new); + } while (int128_ne(cmp, old)); +#endif +} + +#endif /* HOST_STORE_INSERT_AL16_H */ diff --git a/host/include/i386/host/cpuinfo.h b/host/include/i386/host/cpuinfo.h new file mode 100644 index 0000000000..b89e6d2e55 --- /dev/null +++ b/host/include/i386/host/cpuinfo.h @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for x86. + */ + +#ifndef HOST_CPUINFO_H +#define HOST_CPUINFO_H + +/* Digested version of */ + +#define CPUINFO_ALWAYS (1u << 0) /* so cpuinfo is nonzero */ +#define CPUINFO_CMOV (1u << 1) +#define CPUINFO_MOVBE (1u << 2) +#define CPUINFO_LZCNT (1u << 3) +#define CPUINFO_POPCNT (1u << 4) +#define CPUINFO_BMI1 (1u << 5) +#define CPUINFO_BMI2 (1u << 6) +#define CPUINFO_SSE2 (1u << 7) +#define CPUINFO_SSE4 (1u << 8) +#define CPUINFO_AVX1 (1u << 9) +#define CPUINFO_AVX2 (1u << 10) +#define CPUINFO_AVX512F (1u << 11) +#define CPUINFO_AVX512VL (1u << 12) +#define CPUINFO_AVX512BW (1u << 13) +#define CPUINFO_AVX512DQ (1u << 14) +#define CPUINFO_AVX512VBMI2 (1u << 15) +#define CPUINFO_ATOMIC_VMOVDQA (1u << 16) +#define CPUINFO_ATOMIC_VMOVDQU (1u << 17) +#define CPUINFO_AES (1u << 18) +#define CPUINFO_PCLMUL (1u << 19) + +/* Initialized with a constructor. */ +extern unsigned cpuinfo; + +/* + * We cannot rely on constructor ordering, so other constructors must + * use the function interface rather than the variable above. + */ +unsigned cpuinfo_init(void); + +#endif /* HOST_CPUINFO_H */ diff --git a/host/include/i386/host/crypto/aes-round.h b/host/include/i386/host/crypto/aes-round.h new file mode 100644 index 0000000000..59a64130f7 --- /dev/null +++ b/host/include/i386/host/crypto/aes-round.h @@ -0,0 +1,152 @@ +/* + * x86 specific aes acceleration. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef X86_HOST_CRYPTO_AES_ROUND_H +#define X86_HOST_CRYPTO_AES_ROUND_H + +#include "host/cpuinfo.h" +#include + +#if defined(__AES__) && defined(__SSSE3__) +# define HAVE_AES_ACCEL true +# define ATTR_AES_ACCEL +#else +# define HAVE_AES_ACCEL likely(cpuinfo & CPUINFO_AES) +# define ATTR_AES_ACCEL __attribute__((target("aes,ssse3"))) +#endif + +static inline __m128i ATTR_AES_ACCEL +aes_accel_bswap(__m128i x) +{ + return _mm_shuffle_epi8(x, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15)); +} + +static inline void ATTR_AES_ACCEL +aesenc_MC_accel(AESState *ret, const AESState *st, bool be) +{ + __m128i t = (__m128i)st->v; + __m128i z = _mm_setzero_si128(); + + if (be) { + t = aes_accel_bswap(t); + t = _mm_aesdeclast_si128(t, z); + t = _mm_aesenc_si128(t, z); + t = aes_accel_bswap(t); + } else { + t = _mm_aesdeclast_si128(t, z); + t = _mm_aesenc_si128(t, z); + } + ret->v = (AESStateVec)t; +} + +static inline void ATTR_AES_ACCEL +aesenc_SB_SR_AK_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + __m128i t = (__m128i)st->v; + __m128i k = (__m128i)rk->v; + + if (be) { + t = aes_accel_bswap(t); + k = aes_accel_bswap(k); + t = _mm_aesenclast_si128(t, k); + t = aes_accel_bswap(t); + } else { + t = _mm_aesenclast_si128(t, k); + } + ret->v = (AESStateVec)t; +} + +static inline void ATTR_AES_ACCEL +aesenc_SB_SR_MC_AK_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + __m128i t = (__m128i)st->v; + __m128i k = (__m128i)rk->v; + + if (be) { + t = aes_accel_bswap(t); + k = aes_accel_bswap(k); + t = _mm_aesenc_si128(t, k); + t = aes_accel_bswap(t); + } else { + t = _mm_aesenc_si128(t, k); + } + ret->v = (AESStateVec)t; +} + +static inline void ATTR_AES_ACCEL +aesdec_IMC_accel(AESState *ret, const AESState *st, bool be) +{ + __m128i t = (__m128i)st->v; + + if (be) { + t = aes_accel_bswap(t); + t = _mm_aesimc_si128(t); + t = aes_accel_bswap(t); + } else { + t = _mm_aesimc_si128(t); + } + ret->v = (AESStateVec)t; +} + +static inline void ATTR_AES_ACCEL +aesdec_ISB_ISR_AK_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + __m128i t = (__m128i)st->v; + __m128i k = (__m128i)rk->v; + + if (be) { + t = aes_accel_bswap(t); + k = aes_accel_bswap(k); + t = _mm_aesdeclast_si128(t, k); + t = aes_accel_bswap(t); + } else { + t = _mm_aesdeclast_si128(t, k); + } + ret->v = (AESStateVec)t; +} + +static inline void ATTR_AES_ACCEL +aesdec_ISB_ISR_AK_IMC_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + __m128i t = (__m128i)st->v; + __m128i k = (__m128i)rk->v; + + if (be) { + t = aes_accel_bswap(t); + k = aes_accel_bswap(k); + t = _mm_aesdeclast_si128(t, k); + t = _mm_aesimc_si128(t); + t = aes_accel_bswap(t); + } else { + t = _mm_aesdeclast_si128(t, k); + t = _mm_aesimc_si128(t); + } + ret->v = (AESStateVec)t; +} + +static inline void ATTR_AES_ACCEL +aesdec_ISB_ISR_IMC_AK_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + __m128i t = (__m128i)st->v; + __m128i k = (__m128i)rk->v; + + if (be) { + t = aes_accel_bswap(t); + k = aes_accel_bswap(k); + t = _mm_aesdec_si128(t, k); + t = aes_accel_bswap(t); + } else { + t = _mm_aesdec_si128(t, k); + } + ret->v = (AESStateVec)t; +} + +#endif /* X86_HOST_CRYPTO_AES_ROUND_H */ diff --git a/host/include/i386/host/crypto/clmul.h b/host/include/i386/host/crypto/clmul.h new file mode 100644 index 0000000000..dc3c814797 --- /dev/null +++ b/host/include/i386/host/crypto/clmul.h @@ -0,0 +1,29 @@ +/* + * x86 specific clmul acceleration. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef X86_HOST_CRYPTO_CLMUL_H +#define X86_HOST_CRYPTO_CLMUL_H + +#include "host/cpuinfo.h" +#include + +#if defined(__PCLMUL__) +# define HAVE_CLMUL_ACCEL true +# define ATTR_CLMUL_ACCEL +#else +# define HAVE_CLMUL_ACCEL likely(cpuinfo & CPUINFO_PCLMUL) +# define ATTR_CLMUL_ACCEL __attribute__((target("pclmul"))) +#endif + +static inline Int128 ATTR_CLMUL_ACCEL +clmul_64_accel(uint64_t n, uint64_t m) +{ + union { __m128i v; Int128 s; } u; + + u.v = _mm_clmulepi64_si128(_mm_set_epi64x(0, n), _mm_set_epi64x(0, m), 0); + return u.s; +} + +#endif /* X86_HOST_CRYPTO_CLMUL_H */ diff --git a/host/include/loongarch64/host/atomic128-ldst.h b/host/include/loongarch64/host/atomic128-ldst.h new file mode 100644 index 0000000000..9a4a8f8b9e --- /dev/null +++ b/host/include/loongarch64/host/atomic128-ldst.h @@ -0,0 +1,52 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Load/store for 128-bit atomic operations, LoongArch version. + * + * See docs/devel/atomics.rst for discussion about the guarantees each + * atomic primitive is meant to provide. + */ + +#ifndef LOONGARCH_ATOMIC128_LDST_H +#define LOONGARCH_ATOMIC128_LDST_H + +#include "host/cpuinfo.h" +#include "tcg/debug-assert.h" + +#define HAVE_ATOMIC128_RO likely(cpuinfo & CPUINFO_LSX) +#define HAVE_ATOMIC128_RW HAVE_ATOMIC128_RO + +/* + * As of gcc 13 and clang 16, there is no compiler support for LSX at all. + * Use inline assembly throughout. + */ + +static inline Int128 atomic16_read_ro(const Int128 *ptr) +{ + uint64_t l, h; + + tcg_debug_assert(HAVE_ATOMIC128_RO); + asm("vld $vr0, %2, 0\n\t" + "vpickve2gr.d %0, $vr0, 0\n\t" + "vpickve2gr.d %1, $vr0, 1" + : "=r"(l), "=r"(h) : "r"(ptr), "m"(*ptr) : "f0"); + + return int128_make128(l, h); +} + +static inline Int128 atomic16_read_rw(Int128 *ptr) +{ + return atomic16_read_ro(ptr); +} + +static inline void atomic16_set(Int128 *ptr, Int128 val) +{ + uint64_t l = int128_getlo(val), h = int128_gethi(val); + + tcg_debug_assert(HAVE_ATOMIC128_RW); + asm("vinsgr2vr.d $vr0, %1, 0\n\t" + "vinsgr2vr.d $vr0, %2, 1\n\t" + "vst $vr0, %3, 0" + : "=m"(*ptr) : "r"(l), "r"(h), "r"(ptr) : "f0"); +} + +#endif /* LOONGARCH_ATOMIC128_LDST_H */ diff --git a/host/include/loongarch64/host/cpuinfo.h b/host/include/loongarch64/host/cpuinfo.h new file mode 100644 index 0000000000..fab664a10b --- /dev/null +++ b/host/include/loongarch64/host/cpuinfo.h @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for LoongArch + */ + +#ifndef HOST_CPUINFO_H +#define HOST_CPUINFO_H + +#define CPUINFO_ALWAYS (1u << 0) /* so cpuinfo is nonzero */ +#define CPUINFO_LSX (1u << 1) + +/* Initialized with a constructor. */ +extern unsigned cpuinfo; + +/* + * We cannot rely on constructor ordering, so other constructors must + * use the function interface rather than the variable above. + */ +unsigned cpuinfo_init(void); + +#endif /* HOST_CPUINFO_H */ diff --git a/host/include/loongarch64/host/load-extract-al16-al8.h b/host/include/loongarch64/host/load-extract-al16-al8.h new file mode 100644 index 0000000000..d1fb59d8af --- /dev/null +++ b/host/include/loongarch64/host/load-extract-al16-al8.h @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Atomic extract 64 from 128-bit, LoongArch version. + * + * Copyright (C) 2023 Linaro, Ltd. + */ + +#ifndef LOONGARCH_LOAD_EXTRACT_AL16_AL8_H +#define LOONGARCH_LOAD_EXTRACT_AL16_AL8_H + +#include "host/cpuinfo.h" +#include "tcg/debug-assert.h" + +/** + * load_atom_extract_al16_or_al8: + * @pv: host address + * @s: object size in bytes, @s <= 8. + * + * Load @s bytes from @pv, when pv % s != 0. If [p, p+s-1] does not + * cross an 16-byte boundary then the access must be 16-byte atomic, + * otherwise the access must be 8-byte atomic. + */ +static inline uint64_t load_atom_extract_al16_or_al8(void *pv, int s) +{ + uintptr_t pi = (uintptr_t)pv; + Int128 *ptr_align = (Int128 *)(pi & ~7); + int shr = (pi & 7) * 8; + uint64_t l, h; + + tcg_debug_assert(HAVE_ATOMIC128_RO); + asm("vld $vr0, %2, 0\n\t" + "vpickve2gr.d %0, $vr0, 0\n\t" + "vpickve2gr.d %1, $vr0, 1" + : "=r"(l), "=r"(h) : "r"(ptr_align), "m"(*ptr_align) : "f0"); + + return (l >> shr) | (h << (-shr & 63)); +} + +#endif /* LOONGARCH_LOAD_EXTRACT_AL16_AL8_H */ diff --git a/host/include/loongarch64/host/store-insert-al16.h b/host/include/loongarch64/host/store-insert-al16.h new file mode 100644 index 0000000000..919fd8d744 --- /dev/null +++ b/host/include/loongarch64/host/store-insert-al16.h @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Atomic store insert into 128-bit, LoongArch version. + */ + +#ifndef LOONGARCH_STORE_INSERT_AL16_H +#define LOONGARCH_STORE_INSERT_AL16_H + +void store_atom_insert_al16(Int128 *ps, Int128 val, Int128 msk) + QEMU_ERROR("unsupported atomic"); + +#endif /* LOONGARCH_STORE_INSERT_AL16_H */ diff --git a/host/include/ppc/host/cpuinfo.h b/host/include/ppc/host/cpuinfo.h new file mode 100644 index 0000000000..38b8eabe2a --- /dev/null +++ b/host/include/ppc/host/cpuinfo.h @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for ppc. + */ + +#ifndef HOST_CPUINFO_H +#define HOST_CPUINFO_H + +/* Digested version of */ + +#define CPUINFO_ALWAYS (1u << 0) /* so cpuinfo is nonzero */ +#define CPUINFO_V2_06 (1u << 1) +#define CPUINFO_V2_07 (1u << 2) +#define CPUINFO_V3_0 (1u << 3) +#define CPUINFO_V3_1 (1u << 4) +#define CPUINFO_ISEL (1u << 5) +#define CPUINFO_ALTIVEC (1u << 6) +#define CPUINFO_VSX (1u << 7) +#define CPUINFO_CRYPTO (1u << 8) + +/* Initialized with a constructor. */ +extern unsigned cpuinfo; + +/* + * We cannot rely on constructor ordering, so other constructors must + * use the function interface rather than the variable above. + */ +unsigned cpuinfo_init(void); + +#endif /* HOST_CPUINFO_H */ diff --git a/host/include/ppc/host/crypto/aes-round.h b/host/include/ppc/host/crypto/aes-round.h new file mode 100644 index 0000000000..8062d2a537 --- /dev/null +++ b/host/include/ppc/host/crypto/aes-round.h @@ -0,0 +1,182 @@ +/* + * Power v2.07 specific aes acceleration. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PPC_HOST_CRYPTO_AES_ROUND_H +#define PPC_HOST_CRYPTO_AES_ROUND_H + +#ifdef __ALTIVEC__ +#include "host/cpuinfo.h" + +#ifdef __CRYPTO__ +# define HAVE_AES_ACCEL true +#else +# define HAVE_AES_ACCEL likely(cpuinfo & CPUINFO_CRYPTO) +#endif +#define ATTR_AES_ACCEL + +/* + * While there is , both gcc and clang "aid" with the + * endianness issues in different ways. Just use inline asm instead. + */ + +/* Bytes in memory are host-endian; bytes in register are @be. */ +static inline AESStateVec aes_accel_ld(const AESState *p, bool be) +{ + AESStateVec r; + + if (be) { + asm("lvx %0, 0, %1" : "=v"(r) : "r"(p), "m"(*p)); + } else if (HOST_BIG_ENDIAN) { + AESStateVec rev = { + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + }; + asm("lvx %0, 0, %1\n\t" + "vperm %0, %0, %0, %2" + : "=v"(r) : "r"(p), "v"(rev), "m"(*p)); + } else { +#ifdef __POWER9_VECTOR__ + asm("lxvb16x %x0, 0, %1" : "=v"(r) : "r"(p), "m"(*p)); +#else + asm("lxvd2x %x0, 0, %1\n\t" + "xxpermdi %x0, %x0, %x0, 2" + : "=v"(r) : "r"(p), "m"(*p)); +#endif + } + return r; +} + +static void aes_accel_st(AESState *p, AESStateVec r, bool be) +{ + if (be) { + asm("stvx %1, 0, %2" : "=m"(*p) : "v"(r), "r"(p)); + } else if (HOST_BIG_ENDIAN) { + AESStateVec rev = { + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + }; + asm("vperm %1, %1, %1, %2\n\t" + "stvx %1, 0, %3" + : "=m"(*p), "+v"(r) : "v"(rev), "r"(p)); + } else { +#ifdef __POWER9_VECTOR__ + asm("stxvb16x %x1, 0, %2" : "=m"(*p) : "v"(r), "r"(p)); +#else + asm("xxpermdi %x1, %x1, %x1, 2\n\t" + "stxvd2x %x1, 0, %2" + : "=m"(*p), "+v"(r) : "r"(p)); +#endif + } +} + +static inline AESStateVec aes_accel_vcipher(AESStateVec d, AESStateVec k) +{ + asm("vcipher %0, %0, %1" : "+v"(d) : "v"(k)); + return d; +} + +static inline AESStateVec aes_accel_vncipher(AESStateVec d, AESStateVec k) +{ + asm("vncipher %0, %0, %1" : "+v"(d) : "v"(k)); + return d; +} + +static inline AESStateVec aes_accel_vcipherlast(AESStateVec d, AESStateVec k) +{ + asm("vcipherlast %0, %0, %1" : "+v"(d) : "v"(k)); + return d; +} + +static inline AESStateVec aes_accel_vncipherlast(AESStateVec d, AESStateVec k) +{ + asm("vncipherlast %0, %0, %1" : "+v"(d) : "v"(k)); + return d; +} + +static inline void +aesenc_MC_accel(AESState *ret, const AESState *st, bool be) +{ + AESStateVec t, z = { }; + + t = aes_accel_ld(st, be); + t = aes_accel_vncipherlast(t, z); + t = aes_accel_vcipher(t, z); + aes_accel_st(ret, t, be); +} + +static inline void +aesenc_SB_SR_AK_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + AESStateVec t, k; + + t = aes_accel_ld(st, be); + k = aes_accel_ld(rk, be); + t = aes_accel_vcipherlast(t, k); + aes_accel_st(ret, t, be); +} + +static inline void +aesenc_SB_SR_MC_AK_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + AESStateVec t, k; + + t = aes_accel_ld(st, be); + k = aes_accel_ld(rk, be); + t = aes_accel_vcipher(t, k); + aes_accel_st(ret, t, be); +} + +static inline void +aesdec_IMC_accel(AESState *ret, const AESState *st, bool be) +{ + AESStateVec t, z = { }; + + t = aes_accel_ld(st, be); + t = aes_accel_vcipherlast(t, z); + t = aes_accel_vncipher(t, z); + aes_accel_st(ret, t, be); +} + +static inline void +aesdec_ISB_ISR_AK_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + AESStateVec t, k; + + t = aes_accel_ld(st, be); + k = aes_accel_ld(rk, be); + t = aes_accel_vncipherlast(t, k); + aes_accel_st(ret, t, be); +} + +static inline void +aesdec_ISB_ISR_AK_IMC_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + AESStateVec t, k; + + t = aes_accel_ld(st, be); + k = aes_accel_ld(rk, be); + t = aes_accel_vncipher(t, k); + aes_accel_st(ret, t, be); +} + +static inline void +aesdec_ISB_ISR_IMC_AK_accel(AESState *ret, const AESState *st, + const AESState *rk, bool be) +{ + AESStateVec t, k, z = { }; + + t = aes_accel_ld(st, be); + k = aes_accel_ld(rk, be); + t = aes_accel_vncipher(t, z); + aes_accel_st(ret, t ^ k, be); +} +#else +/* Without ALTIVEC, we can't even write inline assembly. */ +#include "host/include/generic/host/crypto/aes-round.h" +#endif + +#endif /* PPC_HOST_CRYPTO_AES_ROUND_H */ diff --git a/host/include/ppc64/host/cpuinfo.h b/host/include/ppc64/host/cpuinfo.h new file mode 100644 index 0000000000..2f036a0627 --- /dev/null +++ b/host/include/ppc64/host/cpuinfo.h @@ -0,0 +1 @@ +#include "host/include/ppc/host/cpuinfo.h" diff --git a/host/include/ppc64/host/crypto/aes-round.h b/host/include/ppc64/host/crypto/aes-round.h new file mode 100644 index 0000000000..5eeba6dcb7 --- /dev/null +++ b/host/include/ppc64/host/crypto/aes-round.h @@ -0,0 +1 @@ +#include "host/include/ppc/host/crypto/aes-round.h" diff --git a/host/include/x86_64/host/atomic128-ldst.h b/host/include/x86_64/host/atomic128-ldst.h new file mode 100644 index 0000000000..8d6f909d3c --- /dev/null +++ b/host/include/x86_64/host/atomic128-ldst.h @@ -0,0 +1,75 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Load/store for 128-bit atomic operations, x86_64 version. + * + * Copyright (C) 2023 Linaro, Ltd. + * + * See docs/devel/atomics.rst for discussion about the guarantees each + * atomic primitive is meant to provide. + */ + +#ifndef X86_64_ATOMIC128_LDST_H +#define X86_64_ATOMIC128_LDST_H + +#ifdef CONFIG_INT128_TYPE +#include "host/cpuinfo.h" +#include "tcg/debug-assert.h" +#include + +typedef union { + __m128i v; + __int128_t i; + Int128 s; +} X86Int128Union; + +/* + * Through clang 16, with -mcx16, __atomic_load_n is incorrectly + * expanded to a read-write operation: lock cmpxchg16b. + */ + +#define HAVE_ATOMIC128_RO likely(cpuinfo & CPUINFO_ATOMIC_VMOVDQA) +#define HAVE_ATOMIC128_RW 1 + +static inline Int128 atomic16_read_ro(const Int128 *ptr) +{ + X86Int128Union r; + + tcg_debug_assert(HAVE_ATOMIC128_RO); + asm("vmovdqa %1, %0" : "=x" (r.v) : "m" (*ptr)); + + return r.s; +} + +static inline Int128 atomic16_read_rw(Int128 *ptr) +{ + __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + X86Int128Union r; + + if (HAVE_ATOMIC128_RO) { + asm("vmovdqa %1, %0" : "=x" (r.v) : "m" (*ptr_align)); + } else { + r.i = __sync_val_compare_and_swap_16(ptr_align, 0, 0); + } + return r.s; +} + +static inline void atomic16_set(Int128 *ptr, Int128 val) +{ + __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + X86Int128Union new = { .s = val }; + + if (HAVE_ATOMIC128_RO) { + asm("vmovdqa %1, %0" : "=m"(*ptr_align) : "x" (new.v)); + } else { + __int128_t old; + do { + old = *ptr_align; + } while (!__sync_bool_compare_and_swap_16(ptr_align, old, new.i)); + } +} +#else +/* Provide QEMU_ERROR stubs. */ +#include "host/include/generic/host/atomic128-ldst.h" +#endif + +#endif /* X86_64_ATOMIC128_LDST_H */ diff --git a/host/include/x86_64/host/cpuinfo.h b/host/include/x86_64/host/cpuinfo.h new file mode 100644 index 0000000000..67debab9a0 --- /dev/null +++ b/host/include/x86_64/host/cpuinfo.h @@ -0,0 +1 @@ +#include "host/include/i386/host/cpuinfo.h" diff --git a/host/include/x86_64/host/crypto/aes-round.h b/host/include/x86_64/host/crypto/aes-round.h new file mode 100644 index 0000000000..2773cc9f10 --- /dev/null +++ b/host/include/x86_64/host/crypto/aes-round.h @@ -0,0 +1 @@ +#include "host/include/i386/host/crypto/aes-round.h" diff --git a/host/include/x86_64/host/crypto/clmul.h b/host/include/x86_64/host/crypto/clmul.h new file mode 100644 index 0000000000..f25eced416 --- /dev/null +++ b/host/include/x86_64/host/crypto/clmul.h @@ -0,0 +1 @@ +#include "host/include/i386/host/crypto/clmul.h" diff --git a/host/include/x86_64/host/load-extract-al16-al8.h b/host/include/x86_64/host/load-extract-al16-al8.h new file mode 100644 index 0000000000..baa506b7b5 --- /dev/null +++ b/host/include/x86_64/host/load-extract-al16-al8.h @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Atomic extract 64 from 128-bit, x86_64 version. + * + * Copyright (C) 2023 Linaro, Ltd. + */ + +#ifndef X86_64_LOAD_EXTRACT_AL16_AL8_H +#define X86_64_LOAD_EXTRACT_AL16_AL8_H + +#ifdef CONFIG_INT128_TYPE +#include "host/atomic128-ldst.h" + +/** + * load_atom_extract_al16_or_al8: + * @pv: host address + * @s: object size in bytes, @s <= 8. + * + * Load @s bytes from @pv, when pv % s != 0. If [p, p+s-1] does not + * cross an 16-byte boundary then the access must be 16-byte atomic, + * otherwise the access must be 8-byte atomic. + */ +static inline uint64_t ATTRIBUTE_ATOMIC128_OPT +load_atom_extract_al16_or_al8(void *pv, int s) +{ + uintptr_t pi = (uintptr_t)pv; + __int128_t *ptr_align = (__int128_t *)(pi & ~7); + int shr = (pi & 7) * 8; + X86Int128Union r; + + /* + * ptr_align % 16 is now only 0 or 8. + * If the host supports atomic loads with VMOVDQU, then always use that, + * making the branch highly predictable. Otherwise we must use VMOVDQA + * when ptr_align % 16 == 0 for 16-byte atomicity. + */ + if ((cpuinfo & CPUINFO_ATOMIC_VMOVDQU) || (pi & 8)) { + asm("vmovdqu %1, %0" : "=x" (r.v) : "m" (*ptr_align)); + } else { + asm("vmovdqa %1, %0" : "=x" (r.v) : "m" (*ptr_align)); + } + return int128_getlo(int128_urshift(r.s, shr)); +} +#else +/* Fallback definition that must be optimized away, or error. */ +uint64_t QEMU_ERROR("unsupported atomic") + load_atom_extract_al16_or_al8(void *pv, int s); +#endif + +#endif /* X86_64_LOAD_EXTRACT_AL16_AL8_H */ diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index d42ce6d8b8..1b1f3b9ec8 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -103,14 +103,14 @@ static void renameat_preserve_errno(int odirfd, const char *opath, int ndirfd, const char *npath) { int serrno = errno; - renameat(odirfd, opath, ndirfd, npath); + qemu_renameat(odirfd, opath, ndirfd, npath); errno = serrno; } static void unlinkat_preserve_errno(int dirfd, const char *path, int flags) { int serrno = errno; - unlinkat(dirfd, path, flags); + qemu_unlinkat(dirfd, path, flags); errno = serrno; } @@ -194,7 +194,7 @@ static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) goto out; } - err = fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW); + err = qemu_fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW); if (err) { goto err_out; } @@ -253,7 +253,7 @@ static int local_set_mapped_file_attrat(int dirfd, const char *name, } } } else { - ret = mkdirat(dirfd, VIRTFS_META_DIR, 0700); + ret = qemu_mkdirat(dirfd, VIRTFS_META_DIR, 0700); if (ret < 0 && errno != EEXIST) { return -1; } @@ -349,7 +349,7 @@ static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode) */ /* First, we clear non-racing symlinks out of the way. */ - if (fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW)) { + if (qemu_fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW)) { return -1; } if (S_ISLNK(stbuf.st_mode)) { @@ -470,9 +470,7 @@ static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, if (fd == -1) { return -1; } - do { - tsize = read(fd, (void *)buf, bufsz); - } while (tsize == -1 && errno == EINTR); + tsize = RETRY_ON_EINTR(read(fd, (void *)buf, bufsz)); close_preserve_errno(fd); } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || (fs_ctx->export_flags & V9FS_SM_NONE)) { @@ -626,7 +624,7 @@ static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, /* * Initiate a writeback. This is not a data integrity sync. * We want to ensure that we don't leave dirty pages in the cache - * after write when writeout=immediate is sepcified. + * after write when writeout=immediate is specified. */ sync_file_range(fs->fd, offset, ret, SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); @@ -734,7 +732,7 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, if (fs_ctx->export_flags & V9FS_SM_MAPPED || fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - err = mkdirat(dirfd, name, fs_ctx->dmode); + err = qemu_mkdirat(dirfd, name, fs_ctx->dmode); if (err == -1) { goto out; } @@ -750,7 +748,7 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, } } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || fs_ctx->export_flags & V9FS_SM_NONE) { - err = mkdirat(dirfd, name, credp->fc_mode); + err = qemu_mkdirat(dirfd, name, credp->fc_mode); if (err == -1) { goto out; } @@ -845,7 +843,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, } credp->fc_mode = credp->fc_mode | S_IFREG; if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - /* Set cleint credentials in xattr */ + /* Set client credentials in xattr */ err = local_set_xattrat(dirfd, name, credp); } else { err = local_set_mapped_file_attrat(dirfd, name, credp); @@ -908,15 +906,13 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, } /* Write the oldpath (target) to the file. */ oldpath_size = strlen(oldpath); - do { - write_size = write(fd, (void *)oldpath, oldpath_size); - } while (write_size == -1 && errno == EINTR); + write_size = RETRY_ON_EINTR(write(fd, (void *)oldpath, oldpath_size)); close_preserve_errno(fd); if (write_size != oldpath_size) { goto err_end; } - /* Set cleint credentials in symlink's xattr */ + /* Set client credentials in symlink's xattr */ credp->fc_mode = credp->fc_mode | S_IFLNK; if (fs_ctx->export_flags & V9FS_SM_MAPPED) { @@ -990,7 +986,7 @@ static int local_link(FsContext *ctx, V9fsPath *oldpath, if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { int omap_dirfd, nmap_dirfd; - ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700); + ret = qemu_mkdirat(ndirfd, VIRTFS_META_DIR, 0700); if (ret < 0 && errno != EEXIST) { goto err_undo_link; } @@ -1085,7 +1081,7 @@ static int local_utimensat(FsContext *s, V9fsPath *fs_path, goto out; } - ret = utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW); + ret = qemu_utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW); close_preserve_errno(dirfd); out: g_free(dirpath); @@ -1116,7 +1112,7 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name, if (fd == -1) { return -1; } - ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR); + ret = qemu_unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR); close_preserve_errno(fd); if (ret < 0 && errno != ENOENT) { return -1; @@ -1124,7 +1120,7 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name, } map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); if (map_dirfd != -1) { - ret = unlinkat(map_dirfd, name, 0); + ret = qemu_unlinkat(map_dirfd, name, 0); close_preserve_errno(map_dirfd); if (ret < 0 && errno != ENOENT) { return -1; @@ -1134,7 +1130,7 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name, } } - return unlinkat(dirfd, name, flags); + return qemu_unlinkat(dirfd, name, flags); } static int local_remove(FsContext *ctx, const char *path) @@ -1151,7 +1147,7 @@ static int local_remove(FsContext *ctx, const char *path) goto out; } - if (fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) { + if (qemu_fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) { goto err_out; } @@ -1296,7 +1292,7 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir, return -1; } - ret = renameat(odirfd, old_name, ndirfd, new_name); + ret = qemu_renameat(odirfd, old_name, ndirfd, new_name); if (ret < 0) { goto out; } @@ -1304,7 +1300,7 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir, if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { int omap_dirfd, nmap_dirfd; - ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700); + ret = qemu_mkdirat(ndirfd, VIRTFS_META_DIR, 0700); if (ret < 0 && errno != EEXIST) { goto err_undo_rename; } @@ -1321,7 +1317,7 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir, } /* rename the .virtfs_metadata files */ - ret = renameat(omap_dirfd, old_name, nmap_dirfd, new_name); + ret = qemu_renameat(omap_dirfd, old_name, nmap_dirfd, new_name); close_preserve_errno(nmap_dirfd); close_preserve_errno(omap_dirfd); if (ret < 0 && errno != ENOENT) { @@ -1422,7 +1418,7 @@ static int local_ioc_getversion_init(FsContext *ctx, LocalData *data, Error **er struct statfs stbuf; /* - * use ioc_getversion only if the ioctl is definied + * use ioc_getversion only if the ioctl is defined */ if (fstatfs(data->mountfd, &stbuf) < 0) { error_setg_errno(errp, errno, diff --git a/hw/9pfs/9p-proxy.c b/hw/9pfs/9p-proxy.c index 99d115ff0d..7aac49ad4a 100644 --- a/hw/9pfs/9p-proxy.c +++ b/hw/9pfs/9p-proxy.c @@ -15,6 +15,11 @@ * https://wiki.qemu.org/Documentation/9p */ +/* + * NOTE: The 9p 'proxy' backend is deprecated (since QEMU 8.1) and will be + * removed in a future version of QEMU! + */ + #include "qemu/osdep.h" #include #include @@ -762,7 +767,7 @@ static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs, /* * Initiate a writeback. This is not a data integrity sync. * We want to ensure that we don't leave dirty pages in the cache - * after write when writeout=immediate is sepcified. + * after write when writeout=immediate is specified. */ sync_file_range(fs->fd, offset, ret, SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); diff --git a/hw/9pfs/9p-proxy.h b/hw/9pfs/9p-proxy.h index b84301d001..9be4718d3e 100644 --- a/hw/9pfs/9p-proxy.h +++ b/hw/9pfs/9p-proxy.h @@ -10,6 +10,11 @@ * the COPYING file in the top-level directory. */ +/* + * NOTE: The 9p 'proxy' backend is deprecated (since QEMU 8.1) and will be + * removed in a future version of QEMU! + */ + #ifndef QEMU_9P_PROXY_H #define QEMU_9P_PROXY_H diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index 1c5813e4dd..0ac79a500b 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -72,14 +72,13 @@ static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode, int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, const char *name, V9fsSynthNode **result) { - int ret; V9fsSynthNode *node, *tmp; if (!synth_fs) { - return EAGAIN; + return -EAGAIN; } if (!name || (strlen(name) >= NAME_MAX)) { - return EINVAL; + return -EINVAL; } if (!parent) { parent = &synth_root; @@ -87,8 +86,7 @@ int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, QEMU_LOCK_GUARD(&synth_mutex); QLIST_FOREACH(tmp, &parent->child, sibling) { if (!strcmp(tmp->name, name)) { - ret = EEXIST; - return ret; + return -EEXIST; } } /* Add the name */ @@ -98,22 +96,20 @@ int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, v9fs_add_dir_node(node, node->attr->mode, ".", node->attr, node->attr->inode); *result = node; - ret = 0; - return ret; + return 0; } int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, const char *name, v9fs_synth_read read, v9fs_synth_write write, void *arg) { - int ret; V9fsSynthNode *node, *tmp; if (!synth_fs) { - return EAGAIN; + return -EAGAIN; } if (!name || (strlen(name) >= NAME_MAX)) { - return EINVAL; + return -EINVAL; } if (!parent) { parent = &synth_root; @@ -122,8 +118,7 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, QEMU_LOCK_GUARD(&synth_mutex); QLIST_FOREACH(tmp, &parent->child, sibling) { if (!strcmp(tmp->name, name)) { - ret = EEXIST; - return ret; + return -EEXIST; } } /* Add file type and remove write bits */ @@ -138,8 +133,7 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, node->private = arg; pstrcpy(node->name, sizeof(node->name), name); QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); - ret = 0; - return ret; + return 0; } static void synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf) @@ -499,7 +493,7 @@ static int synth_name_to_path(FsContext *ctx, V9fsPath *dir_path, node = dir_node; goto out; } - /* search for the name in the childern */ + /* search for the name in the children */ rcu_read_lock(); QLIST_FOREACH(node, &dir_node->child, sibling) { if (!strcmp(node->name, name)) { diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index c3526144c9..51c94b0116 100644 --- a/hw/9pfs/9p-util.h +++ b/hw/9pfs/9p-util.h @@ -13,6 +13,8 @@ #ifndef QEMU_9P_UTIL_H #define QEMU_9P_UTIL_H +#include "qemu/error-report.h" + #ifdef O_PATH #define O_PATH_9P_UTIL O_PATH #else @@ -46,7 +48,7 @@ static inline uint64_t makedev_dotl(uint32_t dev_major, uint32_t dev_minor) /* * Converts given device number from host's device number format to Linux * device number format. As both the size of type dev_t and encoding of - * dev_t is system dependant, we have to convert them for Linux guests if + * dev_t is system dependent, we have to convert them for Linux guests if * host is not running Linux. */ static inline uint64_t host_dev_to_dotl_dev(dev_t dev) @@ -90,21 +92,18 @@ static inline int errno_to_dotl(int err) { #ifdef CONFIG_DARWIN #define qemu_fgetxattr(...) fgetxattr(__VA_ARGS__, 0, 0) -#define qemu_lgetxattr(...) getxattr(__VA_ARGS__, 0, XATTR_NOFOLLOW) -#define qemu_llistxattr(...) listxattr(__VA_ARGS__, XATTR_NOFOLLOW) -#define qemu_lremovexattr(...) removexattr(__VA_ARGS__, XATTR_NOFOLLOW) -static inline int qemu_lsetxattr(const char *path, const char *name, - const void *value, size_t size, int flags) { - return setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW); -} #else #define qemu_fgetxattr fgetxattr -#define qemu_lgetxattr lgetxattr -#define qemu_llistxattr llistxattr -#define qemu_lremovexattr lremovexattr -#define qemu_lsetxattr lsetxattr #endif +#define qemu_openat openat +#define qemu_fstat fstat +#define qemu_fstatat fstatat +#define qemu_mkdirat mkdirat +#define qemu_renameat renameat +#define qemu_utimensat utimensat +#define qemu_unlinkat unlinkat + static inline void close_preserve_errno(int fd) { int serrno = errno; @@ -112,10 +111,42 @@ static inline void close_preserve_errno(int fd) errno = serrno; } +/** + * close_if_special_file() - Close @fd if neither regular file nor directory. + * + * @fd: file descriptor of open file + * Return: 0 on regular file or directory, -1 otherwise + * + * CVE-2023-2861: Prohibit opening any special file directly on host + * (especially device files), as a compromised client could potentially gain + * access outside exported tree under certain, unsafe setups. We expect + * client to handle I/O on special files exclusively on guest side. + */ +static inline int close_if_special_file(int fd) +{ + struct stat stbuf; + + if (qemu_fstat(fd, &stbuf) < 0) { + close_preserve_errno(fd); + return -1; + } + if (!S_ISREG(stbuf.st_mode) && !S_ISDIR(stbuf.st_mode)) { + error_report_once( + "9p: broken or compromised client detected; attempt to open " + "special file (i.e. neither regular file, nor directory)" + ); + close(fd); + errno = ENXIO; + return -1; + } + + return 0; +} + static inline int openat_dir(int dirfd, const char *name) { - return openat(dirfd, name, - O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_PATH_9P_UTIL); + return qemu_openat(dirfd, name, + O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_PATH_9P_UTIL); } static inline int openat_file(int dirfd, const char *name, int flags, @@ -126,8 +157,8 @@ static inline int openat_file(int dirfd, const char *name, int flags, #ifndef CONFIG_DARWIN again: #endif - fd = openat(dirfd, name, flags | O_NOFOLLOW | O_NOCTTY | O_NONBLOCK, - mode); + fd = qemu_openat(dirfd, name, flags | O_NOFOLLOW | O_NOCTTY | O_NONBLOCK, + mode); if (fd == -1) { #ifndef CONFIG_DARWIN if (errno == EPERM && (flags & O_NOATIME)) { @@ -146,6 +177,10 @@ again: return -1; } + if (close_if_special_file(fd) < 0) { + return -1; + } + serrno = errno; /* O_NONBLOCK was only needed to open the file. Let's drop it. We don't * do that with O_PATH since fcntl(F_SETFL) isn't supported, and openat() diff --git a/hw/9pfs/9p-xattr-user.c b/hw/9pfs/9p-xattr-user.c index f2ae9582e6..535677ed60 100644 --- a/hw/9pfs/9p-xattr-user.c +++ b/hw/9pfs/9p-xattr-user.c @@ -27,7 +27,7 @@ static ssize_t mp_user_getxattr(FsContext *ctx, const char *path, { if (strncmp(name, "user.virtfs.", 12) == 0) { /* - * Don't allow fetch of user.virtfs namesapce + * Don't allow fetch of user.virtfs namespace * in case of mapped security */ errno = ENOATTR; @@ -49,7 +49,7 @@ static ssize_t mp_user_listxattr(FsContext *ctx, const char *path, name_size -= 12; } else { /* - * Don't allow fetch of user.virtfs namesapce + * Don't allow fetch of user.virtfs namespace * in case of mapped security */ return 0; @@ -74,7 +74,7 @@ static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name, { if (strncmp(name, "user.virtfs.", 12) == 0) { /* - * Don't allow fetch of user.virtfs namesapce + * Don't allow fetch of user.virtfs namespace * in case of mapped security */ errno = EACCES; @@ -88,7 +88,7 @@ static int mp_user_removexattr(FsContext *ctx, { if (strncmp(name, "user.virtfs.", 12) == 0) { /* - * Don't allow fetch of user.virtfs namesapce + * Don't allow fetch of user.virtfs namespace * in case of mapped security */ errno = EACCES; diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 0cd0c14c2a..af636cfb2d 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -19,8 +19,6 @@ #include "qemu/osdep.h" #ifdef CONFIG_LINUX #include -#else -#include #endif #include #include "hw/virtio/virtio.h" @@ -256,7 +254,8 @@ static size_t v9fs_string_size(V9fsString *str) } /* - * returns 0 if fid got re-opened, 1 if not, < 0 on error */ + * returns 0 if fid got re-opened, 1 if not, < 0 on error + */ static int coroutine_fn v9fs_reopen_fid(V9fsPDU *pdu, V9fsFidState *f) { int err = 1; @@ -282,33 +281,32 @@ static V9fsFidState *coroutine_fn get_fid(V9fsPDU *pdu, int32_t fid) V9fsFidState *f; V9fsState *s = pdu->s; - QSIMPLEQ_FOREACH(f, &s->fid_list, next) { + f = g_hash_table_lookup(s->fids, GINT_TO_POINTER(fid)); + if (f) { BUG_ON(f->clunked); - if (f->fid == fid) { - /* - * Update the fid ref upfront so that - * we don't get reclaimed when we yield - * in open later. - */ - f->ref++; - /* - * check whether we need to reopen the - * file. We might have closed the fd - * while trying to free up some file - * descriptors. - */ - err = v9fs_reopen_fid(pdu, f); - if (err < 0) { - f->ref--; - return NULL; - } - /* - * Mark the fid as referenced so that the LRU - * reclaim won't close the file descriptor - */ - f->flags |= FID_REFERENCED; - return f; + /* + * Update the fid ref upfront so that + * we don't get reclaimed when we yield + * in open later. + */ + f->ref++; + /* + * check whether we need to reopen the + * file. We might have closed the fd + * while trying to free up some file + * descriptors. + */ + err = v9fs_reopen_fid(pdu, f); + if (err < 0) { + f->ref--; + return NULL; } + /* + * Mark the fid as referenced so that the LRU + * reclaim won't close the file descriptor + */ + f->flags |= FID_REFERENCED; + return f; } return NULL; } @@ -317,12 +315,11 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid) { V9fsFidState *f; - QSIMPLEQ_FOREACH(f, &s->fid_list, next) { + f = g_hash_table_lookup(s->fids, GINT_TO_POINTER(fid)); + if (f) { /* If fid is already there return NULL */ BUG_ON(f->clunked); - if (f->fid == fid) { - return NULL; - } + return NULL; } f = g_new0(V9fsFidState, 1); f->fid = fid; @@ -333,7 +330,7 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid) * reclaim won't close the file descriptor */ f->flags |= FID_REFERENCED; - QSIMPLEQ_INSERT_TAIL(&s->fid_list, f, next); + g_hash_table_insert(s->fids, GINT_TO_POINTER(fid), f); v9fs_readdir_init(s->proto_version, &f->fs.dir); v9fs_readdir_init(s->proto_version, &f->fs_reclaim.dir); @@ -409,11 +406,7 @@ static int coroutine_fn put_fid(V9fsPDU *pdu, V9fsFidState *fidp) * delete the migration blocker. Ideally, this * should be hooked to transport close notification */ - if (pdu->s->migration_blocker) { - migrate_del_blocker(pdu->s->migration_blocker); - error_free(pdu->s->migration_blocker); - pdu->s->migration_blocker = NULL; - } + migrate_del_blocker(&pdu->s->migration_blocker); } return free_fid(pdu, fidp); } @@ -424,12 +417,12 @@ static V9fsFidState *clunk_fid(V9fsState *s, int32_t fid) { V9fsFidState *fidp; - QSIMPLEQ_FOREACH(fidp, &s->fid_list, next) { - if (fidp->fid == fid) { - QSIMPLEQ_REMOVE(&s->fid_list, fidp, V9fsFidState, next); - fidp->clunked = true; - return fidp; - } + /* TODO: Use g_hash_table_steal_extended() instead? */ + fidp = g_hash_table_lookup(s->fids, GINT_TO_POINTER(fid)); + if (fidp) { + g_hash_table_remove(s->fids, GINT_TO_POINTER(fid)); + fidp->clunked = true; + return fidp; } return NULL; } @@ -439,10 +432,15 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu) int reclaim_count = 0; V9fsState *s = pdu->s; V9fsFidState *f; + GHashTableIter iter; + gpointer fid; + + g_hash_table_iter_init(&iter, s->fids); + QSLIST_HEAD(, V9fsFidState) reclaim_list = QSLIST_HEAD_INITIALIZER(reclaim_list); - QSIMPLEQ_FOREACH(f, &s->fid_list, next) { + while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &f)) { /* * Unlink fids cannot be reclaimed. Check * for them and skip them. Also skip fids @@ -514,72 +512,85 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu) } } +/* + * This is used when a path is removed from the directory tree. Any + * fids that still reference it must not be closed from then on, since + * they cannot be reopened. + */ static int coroutine_fn v9fs_mark_fids_unreclaim(V9fsPDU *pdu, V9fsPath *path) { - int err; + int err = 0; V9fsState *s = pdu->s; - V9fsFidState *fidp, *fidp_next; + V9fsFidState *fidp; + gpointer fid; + GHashTableIter iter; + /* + * The most common case is probably that we have exactly one + * fid for the given path, so preallocate exactly one. + */ + g_autoptr(GArray) to_reopen = g_array_sized_new(FALSE, FALSE, + sizeof(V9fsFidState *), 1); + gint i; - fidp = QSIMPLEQ_FIRST(&s->fid_list); - if (!fidp) { - return 0; - } + g_hash_table_iter_init(&iter, s->fids); /* - * v9fs_reopen_fid() can yield : a reference on the fid must be held - * to ensure its pointer remains valid and we can safely pass it to - * QSIMPLEQ_NEXT(). The corresponding put_fid() can also yield so - * we must keep a reference on the next fid as well. So the logic here - * is to get a reference on a fid and only put it back during the next - * iteration after we could get a reference on the next fid. Start with - * the first one. + * We iterate over the fid table looking for the entries we need + * to reopen, and store them in to_reopen. This is because + * v9fs_reopen_fid() and put_fid() yield. This allows the fid table + * to be modified in the meantime, invalidating our iterator. */ - for (fidp->ref++; fidp; fidp = fidp_next) { + while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &fidp)) { if (fidp->path.size == path->size && !memcmp(fidp->path.data, path->data, path->size)) { - /* Mark the fid non reclaimable. */ - fidp->flags |= FID_NON_RECLAIMABLE; - - /* reopen the file/dir if already closed */ - err = v9fs_reopen_fid(pdu, fidp); - if (err < 0) { - put_fid(pdu, fidp); - return err; - } - } - - fidp_next = QSIMPLEQ_NEXT(fidp, next); - - if (fidp_next) { /* - * Ensure the next fid survives a potential clunk request during - * put_fid() below and v9fs_reopen_fid() in the next iteration. + * Ensure the fid survives a potential clunk request during + * v9fs_reopen_fid or put_fid. */ - fidp_next->ref++; + fidp->ref++; + fidp->flags |= FID_NON_RECLAIMABLE; + g_array_append_val(to_reopen, fidp); } - - /* We're done with this fid */ - put_fid(pdu, fidp); } - return 0; + for (i = 0; i < to_reopen->len; i++) { + fidp = g_array_index(to_reopen, V9fsFidState*, i); + /* reopen the file/dir if already closed */ + err = v9fs_reopen_fid(pdu, fidp); + if (err < 0) { + break; + } + } + + for (i = 0; i < to_reopen->len; i++) { + put_fid(pdu, g_array_index(to_reopen, V9fsFidState*, i)); + } + return err; } static void coroutine_fn virtfs_reset(V9fsPDU *pdu) { V9fsState *s = pdu->s; V9fsFidState *fidp; + GList *freeing; + /* + * Get a list of all the values (fid states) in the table, which + * we then... + */ + g_autoptr(GList) fids = g_hash_table_get_values(s->fids); - /* Free all fids */ - while (!QSIMPLEQ_EMPTY(&s->fid_list)) { - /* Get fid */ - fidp = QSIMPLEQ_FIRST(&s->fid_list); + /* ... remove from the table, taking over ownership. */ + g_hash_table_steal_all(s->fids); + + /* + * This allows us to release our references to them asynchronously without + * iterating over the hash table and risking iterator invalidation + * through concurrent modifications. + */ + for (freeing = fids; freeing; freeing = freeing->next) { + fidp = freeing->data; fidp->ref++; - - /* Clunk fid */ - QSIMPLEQ_REMOVE(&s->fid_list, fidp, V9fsFidState, next); fidp->clunked = true; - put_fid(pdu, fidp); } } @@ -629,7 +640,7 @@ static inline uint64_t mirror64bit(uint64_t value) } /* - * Parameter k for the Exponential Golomb algorihm to be used. + * Parameter k for the Exponential Golomb algorithm to be used. * * The smaller this value, the smaller the minimum bit count for the Exp. * Golomb generated affixes will be (at lowest index) however for the @@ -723,15 +734,14 @@ static VariLenAffix affixForIndex(uint64_t index) return invertAffix(&prefix); /* convert prefix to suffix */ } -/* creative abuse of tb_hash_func7, which is based on xxhash */ static uint32_t qpp_hash(QppEntry e) { - return qemu_xxhash7(e.ino_prefix, e.dev, 0, 0, 0); + return qemu_xxhash4(e.ino_prefix, e.dev); } static uint32_t qpf_hash(QpfEntry e) { - return qemu_xxhash7(e.ino, e.dev, 0, 0, 0); + return qemu_xxhash4(e.ino, e.dev); } static bool qpd_cmp_func(const void *obj, const void *userp) @@ -1025,7 +1035,7 @@ static void coroutine_fn pdu_complete(V9fsPDU *pdu, ssize_t len) * Sending a reply would confuse clients because they would * assume that any EINTR is the actual result of the operation, * rather than a consequence of the cancellation. However, if - * the operation completed (succesfully or with an error other + * the operation completed (successfully or with an error other * than caused be cancellation), we do send out that reply, both * for efficiency and to avoid confusing the rest of the state machine * that assumes passing a non-error here will mean a successful @@ -1491,10 +1501,8 @@ static void coroutine_fn v9fs_attach(void *opaque) error_setg(&s->migration_blocker, "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'", s->ctx.fs_root ? s->ctx.fs_root : "NULL", s->tag); - err = migrate_add_blocker(s->migration_blocker, NULL); + err = migrate_add_blocker(&s->migration_blocker, NULL); if (err < 0) { - error_free(s->migration_blocker); - s->migration_blocker = NULL; clunk_fid(s, fid); goto out; } @@ -1766,9 +1774,9 @@ static bool same_stat_id(const struct stat *a, const struct stat *b) static void coroutine_fn v9fs_walk(void *opaque) { - int name_idx; + int name_idx, nwalked; g_autofree V9fsQID *qids = NULL; - int i, err = 0; + int i, err = 0, any_err = 0; V9fsPath dpath, path; P9ARRAY_REF(V9fsPath) pathes = NULL; uint16_t nwnames; @@ -1786,7 +1794,7 @@ static void coroutine_fn v9fs_walk(void *opaque) err = pdu_unmarshal(pdu, offset, "ddw", &fid, &newfid, &nwnames); if (err < 0) { pdu_complete(pdu, err); - return ; + return; } offset += err; @@ -1834,54 +1842,61 @@ static void coroutine_fn v9fs_walk(void *opaque) * driver code altogether inside the following block. */ v9fs_co_run_in_worker({ + nwalked = 0; if (v9fs_request_cancelled(pdu)) { - err = -EINTR; + any_err |= err = -EINTR; break; } err = s->ops->lstat(&s->ctx, &dpath, &fidst); if (err < 0) { - err = -errno; + any_err |= err = -errno; break; } stbuf = fidst; - for (name_idx = 0; name_idx < nwnames; name_idx++) { + for (; nwalked < nwnames; nwalked++) { if (v9fs_request_cancelled(pdu)) { - err = -EINTR; + any_err |= err = -EINTR; break; } if (!same_stat_id(&pdu->s->root_st, &stbuf) || - strcmp("..", wnames[name_idx].data)) + strcmp("..", wnames[nwalked].data)) { err = s->ops->name_to_path(&s->ctx, &dpath, - wnames[name_idx].data, - &pathes[name_idx]); + wnames[nwalked].data, + &pathes[nwalked]); if (err < 0) { - err = -errno; + any_err |= err = -errno; break; } if (v9fs_request_cancelled(pdu)) { - err = -EINTR; + any_err |= err = -EINTR; break; } - err = s->ops->lstat(&s->ctx, &pathes[name_idx], &stbuf); + err = s->ops->lstat(&s->ctx, &pathes[nwalked], &stbuf); if (err < 0) { - err = -errno; + any_err |= err = -errno; break; } - stbufs[name_idx] = stbuf; - v9fs_path_copy(&dpath, &pathes[name_idx]); + stbufs[nwalked] = stbuf; + v9fs_path_copy(&dpath, &pathes[nwalked]); } } }); /* * Handle all the rest of this Twalk request on main thread ... + * + * NOTE: -EINTR is an exception where we deviate from the protocol spec + * and simply send a (R)Lerror response instead of bothering to assemble + * a (deducted) Rwalk response; because -EINTR is always the result of a + * Tflush request, so client would no longer wait for a response in this + * case anyway. */ - if (err < 0) { + if ((err < 0 && !nwalked) || err == -EINTR) { goto out; } - err = stat_to_qid(pdu, &fidst, &qid); - if (err < 0) { + any_err |= err = stat_to_qid(pdu, &fidst, &qid); + if (err < 0 && !nwalked) { goto out; } stbuf = fidst; @@ -1890,20 +1905,29 @@ static void coroutine_fn v9fs_walk(void *opaque) v9fs_path_copy(&dpath, &fidp->path); v9fs_path_copy(&path, &fidp->path); - for (name_idx = 0; name_idx < nwnames; name_idx++) { + for (name_idx = 0; name_idx < nwalked; name_idx++) { if (!same_stat_id(&pdu->s->root_st, &stbuf) || strcmp("..", wnames[name_idx].data)) { stbuf = stbufs[name_idx]; - err = stat_to_qid(pdu, &stbuf, &qid); + any_err |= err = stat_to_qid(pdu, &stbuf, &qid); if (err < 0) { - goto out; + break; } v9fs_path_copy(&path, &pathes[name_idx]); v9fs_path_copy(&dpath, &path); } memcpy(&qids[name_idx], &qid, sizeof(qid)); } + if (any_err < 0) { + if (!name_idx) { + /* don't send any QIDs, send Rlerror instead */ + goto out; + } else { + /* send QIDs (not Rlerror), but fid MUST remain unaffected */ + goto send_qids; + } + } if (fid == newfid) { if (fidp->fid_type != P9_FID_NONE) { err = -EINVAL; @@ -1921,8 +1945,9 @@ static void coroutine_fn v9fs_walk(void *opaque) newfidp->uid = fidp->uid; v9fs_path_copy(&newfidp->path, &path); } - err = v9fs_walk_marshal(pdu, nwnames, qids); - trace_v9fs_walk_return(pdu->tag, pdu->id, nwnames, qids); +send_qids: + err = v9fs_walk_marshal(pdu, name_idx, qids); + trace_v9fs_walk_return(pdu->tag, pdu->id, name_idx, qids); out: put_fid(pdu, fidp); if (newfidp) { @@ -3188,6 +3213,8 @@ static int coroutine_fn v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp, V9fsFidState *tfidp; V9fsState *s = pdu->s; V9fsFidState *dirfidp = NULL; + GHashTableIter iter; + gpointer fid; v9fs_path_init(&new_path); if (newdirfid != -1) { @@ -3221,11 +3248,13 @@ static int coroutine_fn v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp, if (err < 0) { goto out; } + /* * Fixup fid's pointing to the old name to * start pointing to the new name */ - QSIMPLEQ_FOREACH(tfidp, &s->fid_list, next) { + g_hash_table_iter_init(&iter, s->fids); + while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &tfidp)) { if (v9fs_path_is_ancestor(&fidp->path, &tfidp->path)) { /* replace the name */ v9fs_fix_path(&tfidp->path, &new_path, strlen(fidp->path.data)); @@ -3303,6 +3332,8 @@ static int coroutine_fn v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir, V9fsPath oldpath, newpath; V9fsState *s = pdu->s; int err; + GHashTableIter iter; + gpointer fid; v9fs_path_init(&oldpath); v9fs_path_init(&newpath); @@ -3319,7 +3350,8 @@ static int coroutine_fn v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir, * Fixup fid's pointing to the old name to * start pointing to the new name */ - QSIMPLEQ_FOREACH(tfidp, &s->fid_list, next) { + g_hash_table_iter_init(&iter, s->fids); + while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &tfidp)) { if (v9fs_path_is_ancestor(&oldpath, &tfidp->path)) { /* replace the name */ v9fs_fix_path(&tfidp->path, &newpath, strlen(oldpath.data)); @@ -4209,7 +4241,7 @@ int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t, s->ctx.fmode = fse->fmode; s->ctx.dmode = fse->dmode; - QSIMPLEQ_INIT(&s->fid_list); + s->fids = g_hash_table_new(NULL, NULL); qemu_co_rwlock_init(&s->rename_lock); if (s->ops->init(&s->ctx, errp) < 0) { @@ -4269,6 +4301,10 @@ void v9fs_device_unrealize_common(V9fsState *s) if (s->ctx.fst) { fsdev_throttle_cleanup(s->ctx.fst); } + if (s->fids) { + g_hash_table_destroy(s->fids); + s->fids = NULL; + } g_free(s->tag); qp_table_destroy(&s->qpd_table); qp_table_destroy(&s->qpp_table); diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 994f952600..a6f59abccb 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -203,7 +203,7 @@ typedef struct V9fsDir { QemuMutex readdir_mutex_L; } V9fsDir; -static inline void v9fs_readdir_lock(V9fsDir *dir) +static inline void coroutine_fn v9fs_readdir_lock(V9fsDir *dir) { if (dir->proto_version == V9FS_PROTO_2000U) { qemu_co_mutex_lock(&dir->readdir_mutex_u); @@ -212,7 +212,7 @@ static inline void v9fs_readdir_lock(V9fsDir *dir) } } -static inline void v9fs_readdir_unlock(V9fsDir *dir) +static inline void coroutine_fn v9fs_readdir_unlock(V9fsDir *dir) { if (dir->proto_version == V9FS_PROTO_2000U) { qemu_co_mutex_unlock(&dir->readdir_mutex_u); @@ -304,7 +304,7 @@ typedef struct VariLenAffix { AffixType_t type; /* Whether this affix is a suffix or a prefix. */ uint64_t value; /* Actual numerical value of this affix. */ /* - * Lenght of the affix, that is how many (of the lowest) bits of ``value`` + * Length of the affix, that is how many (of the lowest) bits of ``value`` * must be used for appending/prepending this affix to its final resulting, * unique number. */ @@ -339,7 +339,7 @@ typedef struct { struct V9fsState { QLIST_HEAD(, V9fsPDU) free_list; QLIST_HEAD(, V9fsPDU) active_list; - QSIMPLEQ_HEAD(, V9fsFidState) fid_list; + GHashTable *fids; FileOperations *ops; FsContext ctx; char *tag; @@ -424,21 +424,24 @@ typedef struct V9fsGetlock extern int open_fd_hw; extern int total_open_fd; -static inline void v9fs_path_write_lock(V9fsState *s) +static inline void coroutine_fn +v9fs_path_write_lock(V9fsState *s) { if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { qemu_co_rwlock_wrlock(&s->rename_lock); } } -static inline void v9fs_path_read_lock(V9fsState *s) +static inline void coroutine_fn +v9fs_path_read_lock(V9fsState *s) { if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { qemu_co_rwlock_rdlock(&s->rename_lock); } } -static inline void v9fs_path_unlock(V9fsState *s) +static inline void coroutine_fn +v9fs_path_unlock(V9fsState *s) { if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { qemu_co_rwlock_unlock(&s->rename_lock); diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c index 93ba44fb75..2068a4779d 100644 --- a/hw/9pfs/codir.c +++ b/hw/9pfs/codir.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "qemu/coroutine.h" #include "qemu/main-loop.h" #include "coth.h" #include "9p-xattr.h" @@ -69,9 +68,9 @@ int coroutine_fn v9fs_co_readdir(V9fsPDU *pdu, V9fsFidState *fidp, * * See v9fs_co_readdir_many() (as its only user) below for details. */ -static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, - struct V9fsDirEnt **entries, off_t offset, - int32_t maxsize, bool dostat) +static int coroutine_fn +do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, struct V9fsDirEnt **entries, + off_t offset, int32_t maxsize, bool dostat) { V9fsState *s = pdu->s; V9fsString name; diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c index 20f93a90e7..71174c3e4a 100644 --- a/hw/9pfs/cofile.c +++ b/hw/9pfs/cofile.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "qemu/coroutine.h" #include "qemu/main-loop.h" #include "coth.h" @@ -253,7 +252,7 @@ int coroutine_fn v9fs_co_pwritev(V9fsPDU *pdu, V9fsFidState *fidp, if (v9fs_request_cancelled(pdu)) { return -EINTR; } - fsdev_co_throttle_request(s->ctx.fst, true, iov, iovcnt); + fsdev_co_throttle_request(s->ctx.fst, THROTTLE_WRITE, iov, iovcnt); v9fs_co_run_in_worker( { err = s->ops->pwritev(&s->ctx, &fidp->fs, iov, iovcnt, offset); @@ -273,7 +272,7 @@ int coroutine_fn v9fs_co_preadv(V9fsPDU *pdu, V9fsFidState *fidp, if (v9fs_request_cancelled(pdu)) { return -EINTR; } - fsdev_co_throttle_request(s->ctx.fst, false, iov, iovcnt); + fsdev_co_throttle_request(s->ctx.fst, THROTTLE_READ, iov, iovcnt); v9fs_co_run_in_worker( { err = s->ops->preadv(&s->ctx, &fidp->fs, iov, iovcnt, offset); diff --git a/hw/9pfs/cofs.c b/hw/9pfs/cofs.c index 9d0adc2e78..67e3ae5c5c 100644 --- a/hw/9pfs/cofs.c +++ b/hw/9pfs/cofs.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "qemu/coroutine.h" #include "qemu/main-loop.h" #include "coth.h" diff --git a/hw/9pfs/coth.c b/hw/9pfs/coth.c index 2802d41cce..598f46add9 100644 --- a/hw/9pfs/coth.c +++ b/hw/9pfs/coth.c @@ -41,6 +41,5 @@ static int coroutine_enter_func(void *arg) void co_run_in_worker_bh(void *opaque) { Coroutine *co = opaque; - thread_pool_submit_aio(aio_get_thread_pool(qemu_get_aio_context()), - coroutine_enter_func, co, coroutine_enter_cb, co); + thread_pool_submit_aio(coroutine_enter_func, co, coroutine_enter_cb, co); } diff --git a/hw/9pfs/coth.h b/hw/9pfs/coth.h index 1a1edbdc2a..2c54249b35 100644 --- a/hw/9pfs/coth.h +++ b/hw/9pfs/coth.h @@ -16,7 +16,7 @@ #define QEMU_9P_COTH_H #include "qemu/thread.h" -#include "qemu/coroutine.h" +#include "qemu/coroutine-core.h" #include "9p.h" /* diff --git a/hw/9pfs/coxattr.c b/hw/9pfs/coxattr.c index dbcd09e0fd..cd0f8488ac 100644 --- a/hw/9pfs/coxattr.c +++ b/hw/9pfs/coxattr.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" -#include "qemu/coroutine.h" #include "qemu/main-loop.h" #include "coth.h" diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build index 12443b6ad5..f1b62fa8c8 100644 --- a/hw/9pfs/meson.build +++ b/hw/9pfs/meson.build @@ -13,9 +13,12 @@ fs_ss.add(files( 'coth.c', 'coxattr.c', )) -fs_ss.add(when: 'CONFIG_LINUX', if_true: files('9p-util-linux.c')) -fs_ss.add(when: 'CONFIG_DARWIN', if_true: files('9p-util-darwin.c')) -fs_ss.add(when: 'CONFIG_XEN', if_true: files('xen-9p-backend.c')) -softmmu_ss.add_all(when: 'CONFIG_FSDEV_9P', if_true: fs_ss) +if host_os == 'darwin' + fs_ss.add(files('9p-util-darwin.c')) +elif host_os == 'linux' + fs_ss.add(files('9p-util-linux.c')) +endif +fs_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-9p-backend.c')) +system_ss.add_all(when: 'CONFIG_FSDEV_9P', if_true: fs_ss) specific_ss.add(when: 'CONFIG_VIRTIO_9P', if_true: files('virtio-9p-device.c')) diff --git a/hw/9pfs/trace-events b/hw/9pfs/trace-events index 6c77966c0b..a12e55c165 100644 --- a/hw/9pfs/trace-events +++ b/hw/9pfs/trace-events @@ -48,3 +48,9 @@ v9fs_readlink(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d" v9fs_readlink_return(uint16_t tag, uint8_t id, char* target) "tag %d id %d name %s" v9fs_setattr(uint16_t tag, uint8_t id, int32_t fid, int32_t valid, int32_t mode, int32_t uid, int32_t gid, int64_t size, int64_t atime_sec, int64_t mtime_sec) "tag %u id %u fid %d iattr={valid %d mode %d uid %d gid %d size %"PRId64" atime=%"PRId64" mtime=%"PRId64" }" v9fs_setattr_return(uint16_t tag, uint8_t id) "tag %u id %u" + +# xen-9p-backend.c +xen_9pfs_alloc(char *name) "name %s" +xen_9pfs_connect(char *name) "name %s" +xen_9pfs_disconnect(char *name) "name %s" +xen_9pfs_free(char *name) "name %s" diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 5f522e68e9..efa41cfd73 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -237,7 +237,7 @@ static const VMStateDescription vmstate_virtio_9p = { .name = "virtio-9p", .minimum_version_id = 1, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index 65c4979c3c..4aa9c8c736 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -22,8 +22,11 @@ #include "qemu/config-file.h" #include "qemu/main-loop.h" #include "qemu/option.h" +#include "qemu/iov.h" #include "fsdev/qemu-fsdev.h" +#include "trace.h" + #define VERSIONS "1" #define MAX_RINGS 8 #define MAX_RING_ORDER 9 @@ -60,6 +63,7 @@ typedef struct Xen9pfsDev { int num_rings; Xen9pfsRing *rings; + MemReentrancyGuard mem_reentrancy_guard; } Xen9pfsDev; static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev); @@ -241,7 +245,7 @@ static void xen_9pfs_push_and_notify(V9fsPDU *pdu) xen_wmb(); ring->inprogress = false; - xenevtchn_notify(ring->evtchndev, ring->local_port); + qemu_xen_evtchn_notify(ring->evtchndev, ring->local_port); qemu_bh_schedule(ring->bh); } @@ -324,8 +328,8 @@ static void xen_9pfs_evtchn_event(void *opaque) Xen9pfsRing *ring = opaque; evtchn_port_t port; - port = xenevtchn_pending(ring->evtchndev); - xenevtchn_unmask(ring->evtchndev, port); + port = qemu_xen_evtchn_pending(ring->evtchndev); + qemu_xen_evtchn_unmask(ring->evtchndev, port); qemu_bh_schedule(ring->bh); } @@ -335,47 +339,51 @@ static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev) Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); int i; + trace_xen_9pfs_disconnect(xendev->name); + for (i = 0; i < xen_9pdev->num_rings; i++) { if (xen_9pdev->rings[i].evtchndev != NULL) { - qemu_set_fd_handler(xenevtchn_fd(xen_9pdev->rings[i].evtchndev), - NULL, NULL, NULL); - xenevtchn_unbind(xen_9pdev->rings[i].evtchndev, - xen_9pdev->rings[i].local_port); + qemu_set_fd_handler(qemu_xen_evtchn_fd(xen_9pdev->rings[i].evtchndev), + NULL, NULL, NULL); + qemu_xen_evtchn_unbind(xen_9pdev->rings[i].evtchndev, + xen_9pdev->rings[i].local_port); xen_9pdev->rings[i].evtchndev = NULL; } - } -} - -static int xen_9pfs_free(struct XenLegacyDevice *xendev) -{ - Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); - int i; - - if (xen_9pdev->rings[0].evtchndev != NULL) { - xen_9pfs_disconnect(xendev); - } - - for (i = 0; i < xen_9pdev->num_rings; i++) { if (xen_9pdev->rings[i].data != NULL) { xen_be_unmap_grant_refs(&xen_9pdev->xendev, xen_9pdev->rings[i].data, + xen_9pdev->rings[i].intf->ref, (1 << xen_9pdev->rings[i].ring_order)); + xen_9pdev->rings[i].data = NULL; } if (xen_9pdev->rings[i].intf != NULL) { - xen_be_unmap_grant_refs(&xen_9pdev->xendev, - xen_9pdev->rings[i].intf, - 1); + xen_be_unmap_grant_ref(&xen_9pdev->xendev, + xen_9pdev->rings[i].intf, + xen_9pdev->rings[i].ref); + xen_9pdev->rings[i].intf = NULL; } if (xen_9pdev->rings[i].bh != NULL) { qemu_bh_delete(xen_9pdev->rings[i].bh); + xen_9pdev->rings[i].bh = NULL; } } g_free(xen_9pdev->id); + xen_9pdev->id = NULL; g_free(xen_9pdev->tag); + xen_9pdev->tag = NULL; g_free(xen_9pdev->path); + xen_9pdev->path = NULL; g_free(xen_9pdev->security_model); + xen_9pdev->security_model = NULL; g_free(xen_9pdev->rings); + xen_9pdev->rings = NULL; +} + +static int xen_9pfs_free(struct XenLegacyDevice *xendev) +{ + trace_xen_9pfs_free(xendev->name); + return 0; } @@ -387,6 +395,8 @@ static int xen_9pfs_connect(struct XenLegacyDevice *xendev) V9fsState *s = &xen_9pdev->state; QemuOpts *fsdev; + trace_xen_9pfs_connect(xendev->name); + if (xenstore_read_fe_int(&xen_9pdev->xendev, "num-rings", &xen_9pdev->num_rings) == -1 || xen_9pdev->num_rings > MAX_RINGS || xen_9pdev->num_rings < 1) { @@ -441,18 +451,20 @@ static int xen_9pfs_connect(struct XenLegacyDevice *xendev) xen_9pdev->rings[i].ring.out = xen_9pdev->rings[i].data + XEN_FLEX_RING_SIZE(ring_order); - xen_9pdev->rings[i].bh = qemu_bh_new(xen_9pfs_bh, &xen_9pdev->rings[i]); + xen_9pdev->rings[i].bh = qemu_bh_new_guarded(xen_9pfs_bh, + &xen_9pdev->rings[i], + &xen_9pdev->mem_reentrancy_guard); xen_9pdev->rings[i].out_cons = 0; xen_9pdev->rings[i].out_size = 0; xen_9pdev->rings[i].inprogress = false; - xen_9pdev->rings[i].evtchndev = xenevtchn_open(NULL, 0); + xen_9pdev->rings[i].evtchndev = qemu_xen_evtchn_open(); if (xen_9pdev->rings[i].evtchndev == NULL) { goto out; } - qemu_set_cloexec(xenevtchn_fd(xen_9pdev->rings[i].evtchndev)); - xen_9pdev->rings[i].local_port = xenevtchn_bind_interdomain + qemu_set_cloexec(qemu_xen_evtchn_fd(xen_9pdev->rings[i].evtchndev)); + xen_9pdev->rings[i].local_port = qemu_xen_evtchn_bind_interdomain (xen_9pdev->rings[i].evtchndev, xendev->dom, xen_9pdev->rings[i].evtchn); @@ -463,8 +475,8 @@ static int xen_9pfs_connect(struct XenLegacyDevice *xendev) goto out; } xen_pv_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); - qemu_set_fd_handler(xenevtchn_fd(xen_9pdev->rings[i].evtchndev), - xen_9pfs_evtchn_event, NULL, &xen_9pdev->rings[i]); + qemu_set_fd_handler(qemu_xen_evtchn_fd(xen_9pdev->rings[i].evtchndev), + xen_9pfs_evtchn_event, NULL, &xen_9pdev->rings[i]); } xen_9pdev->security_model = xenstore_read_be_str(xendev, "security_model"); @@ -494,6 +506,8 @@ out: static void xen_9pfs_alloc(struct XenLegacyDevice *xendev) { + trace_xen_9pfs_alloc(xendev->name); + xenstore_write_be_str(xendev, "versions", VERSIONS); xenstore_write_be_int(xendev, "max-rings", MAX_RINGS); xenstore_write_be_int(xendev, "max-ring-page-order", MAX_RING_ORDER); diff --git a/hw/Kconfig b/hw/Kconfig index 38233bbb0f..2c00936c28 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -9,6 +9,7 @@ source core/Kconfig source cxl/Kconfig source display/Kconfig source dma/Kconfig +source fsi/Kconfig source gpio/Kconfig source hyperv/Kconfig source i2c/Kconfig @@ -38,9 +39,11 @@ source smbios/Kconfig source ssi/Kconfig source timer/Kconfig source tpm/Kconfig +source ufs/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig +source xen/Kconfig source watchdog/Kconfig # arch Kconfig diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig index 3703aca212..e07d3204eb 100644 --- a/hw/acpi/Kconfig +++ b/hw/acpi/Kconfig @@ -9,13 +9,14 @@ config ACPI_X86 select ACPI_CPU_HOTPLUG select ACPI_MEMORY_HOTPLUG select ACPI_HMAT - select ACPI_PIIX4 select ACPI_PCIHP select ACPI_ERST -config ACPI_X86_ICH +config ACPI_ICH9 bool + select ACPI_SMBUS select ACPI_X86 + select APM config ACPI_CPU_HOTPLUG bool @@ -30,12 +31,18 @@ config ACPI_NVDIMM config ACPI_PIIX4 bool - depends on ACPI + select ACPI + select ACPI_SMBUS + select APM config ACPI_PCIHP bool depends on ACPI +config ACPI_PCI_BRIDGE + bool + depends on ACPI && PCI && ACPI_PCIHP + config ACPI_HMAT bool depends on ACPI diff --git a/hw/acpi/acpi-pci-hotplug-stub.c b/hw/acpi/acpi-pci-hotplug-stub.c index a43f6dafc9..dcee3ad7a1 100644 --- a/hw/acpi/acpi-pci-hotplug-stub.c +++ b/hw/acpi/acpi-pci-hotplug-stub.c @@ -5,8 +5,7 @@ const VMStateDescription vmstate_acpi_pcihp_pci_status; void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, - MemoryRegion *address_space_io, bool bridges_enabled, - uint16_t io_base) + MemoryRegion *address_space_io, uint16_t io_base) { return; } @@ -36,8 +35,12 @@ void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, return; } -void acpi_pcihp_reset(AcpiPciHpState *s, bool acpihp_root_off) +void acpi_pcihp_reset(AcpiPciHpState *s) { return; } +bool acpi_pcihp_is_hotpluggbale_bus(AcpiPciHpState *s, BusState *bus) +{ + return true; +} diff --git a/hw/acpi/acpi-qmp-cmds.c b/hw/acpi/acpi-qmp-cmds.c new file mode 100644 index 0000000000..2d47cac52c --- /dev/null +++ b/hw/acpi/acpi-qmp-cmds.c @@ -0,0 +1,30 @@ +/* + * QMP commands related to ACPI + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "hw/acpi/acpi_dev_interface.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-acpi.h" + +ACPIOSTInfoList *qmp_query_acpi_ospm_status(Error **errp) +{ + bool ambig; + ACPIOSTInfoList *head = NULL; + ACPIOSTInfoList **prev = &head; + Object *obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, &ambig); + + if (obj) { + AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj); + AcpiDeviceIf *adev = ACPI_DEVICE_IF(obj); + + adevc->ospm_status(adev, &prev); + } else { + error_setg(errp, "command is not supported, missing ACPI device"); + } + + return head; +} diff --git a/hw/acpi/acpi-stub.c b/hw/acpi/acpi-stub.c index 4c9d081ed4..e268ce9b1a 100644 --- a/hw/acpi/acpi-stub.c +++ b/hw/acpi/acpi-stub.c @@ -19,11 +19,9 @@ */ #include "qemu/osdep.h" -#include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "hw/acpi/acpi.h" void acpi_table_add(const QemuOpts *opts, Error **errp) { - error_setg(errp, QERR_UNSUPPORTED); + g_assert_not_reached(); } diff --git a/hw/acpi/acpi-x86-stub.c b/hw/acpi/acpi-x86-stub.c index 3df1e090f4..9662a594ad 100644 --- a/hw/acpi/acpi-x86-stub.c +++ b/hw/acpi/acpi-x86-stub.c @@ -1,13 +1,6 @@ #include "qemu/osdep.h" -#include "hw/i386/pc.h" #include "hw/i386/acpi-build.h" -void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, - const CPUArchIdList *apic_ids, GArray *entry, - bool force_enabled) -{ -} - Object *acpi_get_i386_pci_host(void) { return NULL; diff --git a/hw/acpi/acpi_generic_initiator.c b/hw/acpi/acpi_generic_initiator.c new file mode 100644 index 0000000000..17b9a052f5 --- /dev/null +++ b/hw/acpi/acpi_generic_initiator.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved + */ + +#include "qemu/osdep.h" +#include "hw/acpi/acpi_generic_initiator.h" +#include "hw/acpi/aml-build.h" +#include "hw/boards.h" +#include "hw/pci/pci_device.h" +#include "qemu/error-report.h" + +typedef struct AcpiGenericInitiatorClass { + ObjectClass parent_class; +} AcpiGenericInitiatorClass; + +OBJECT_DEFINE_TYPE_WITH_INTERFACES(AcpiGenericInitiator, acpi_generic_initiator, + ACPI_GENERIC_INITIATOR, OBJECT, + { TYPE_USER_CREATABLE }, + { NULL }) + +OBJECT_DECLARE_SIMPLE_TYPE(AcpiGenericInitiator, ACPI_GENERIC_INITIATOR) + +static void acpi_generic_initiator_init(Object *obj) +{ + AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj); + + gi->node = MAX_NODES; + gi->pci_dev = NULL; +} + +static void acpi_generic_initiator_finalize(Object *obj) +{ + AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj); + + g_free(gi->pci_dev); +} + +static void acpi_generic_initiator_set_pci_device(Object *obj, const char *val, + Error **errp) +{ + AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj); + + gi->pci_dev = g_strdup(val); +} + +static void acpi_generic_initiator_set_node(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj); + MachineState *ms = MACHINE(qdev_get_machine()); + uint32_t value; + + if (!visit_type_uint32(v, name, &value, errp)) { + return; + } + + if (value >= MAX_NODES) { + error_printf("%s: Invalid NUMA node specified\n", + TYPE_ACPI_GENERIC_INITIATOR); + exit(1); + } + + gi->node = value; + ms->numa_state->nodes[gi->node].has_gi = true; +} + +static void acpi_generic_initiator_class_init(ObjectClass *oc, void *data) +{ + object_class_property_add_str(oc, "pci-dev", NULL, + acpi_generic_initiator_set_pci_device); + object_class_property_add(oc, "node", "int", NULL, + acpi_generic_initiator_set_node, NULL, NULL); +} + +/* + * ACPI 6.3: + * Table 5-78 Generic Initiator Affinity Structure + */ +static void +build_srat_generic_pci_initiator_affinity(GArray *table_data, int node, + PCIDeviceHandle *handle) +{ + uint8_t index; + + build_append_int_noprefix(table_data, 5, 1); /* Type */ + build_append_int_noprefix(table_data, 32, 1); /* Length */ + build_append_int_noprefix(table_data, 0, 1); /* Reserved */ + build_append_int_noprefix(table_data, 1, 1); /* Device Handle Type: PCI */ + build_append_int_noprefix(table_data, node, 4); /* Proximity Domain */ + + /* Device Handle - PCI */ + build_append_int_noprefix(table_data, handle->segment, 2); + build_append_int_noprefix(table_data, handle->bdf, 2); + for (index = 0; index < 12; index++) { + build_append_int_noprefix(table_data, 0, 1); + } + + build_append_int_noprefix(table_data, GEN_AFFINITY_ENABLED, 4); /* Flags */ + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ +} + +static int build_all_acpi_generic_initiators(Object *obj, void *opaque) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + AcpiGenericInitiator *gi; + GArray *table_data = opaque; + PCIDeviceHandle dev_handle; + PCIDevice *pci_dev; + Object *o; + + if (!object_dynamic_cast(obj, TYPE_ACPI_GENERIC_INITIATOR)) { + return 0; + } + + gi = ACPI_GENERIC_INITIATOR(obj); + if (gi->node >= ms->numa_state->num_nodes) { + error_printf("%s: Specified node %d is invalid.\n", + TYPE_ACPI_GENERIC_INITIATOR, gi->node); + exit(1); + } + + o = object_resolve_path_type(gi->pci_dev, TYPE_PCI_DEVICE, NULL); + if (!o) { + error_printf("%s: Specified device must be a PCI device.\n", + TYPE_ACPI_GENERIC_INITIATOR); + exit(1); + } + + pci_dev = PCI_DEVICE(o); + + dev_handle.segment = 0; + dev_handle.bdf = PCI_BUILD_BDF(pci_bus_num(pci_get_bus(pci_dev)), + pci_dev->devfn); + + build_srat_generic_pci_initiator_affinity(table_data, + gi->node, &dev_handle); + + return 0; +} + +void build_srat_generic_pci_initiator(GArray *table_data) +{ + object_child_foreach_recursive(object_get_root(), + build_all_acpi_generic_initiators, + table_data); +} diff --git a/hw/acpi/acpi_interface.c b/hw/acpi/acpi_interface.c index 6583917b8e..8637ff18fc 100644 --- a/hw/acpi/acpi_interface.c +++ b/hw/acpi/acpi_interface.c @@ -1,6 +1,8 @@ #include "qemu/osdep.h" #include "hw/acpi/acpi_dev_interface.h" +#include "hw/acpi/acpi_aml_interface.h" #include "qemu/module.h" +#include "qemu/queue.h" void acpi_send_event(DeviceState *dev, AcpiEventStatusBits event) { @@ -11,6 +13,15 @@ void acpi_send_event(DeviceState *dev, AcpiEventStatusBits event) } } +void qbus_build_aml(BusState *bus, Aml *scope) +{ + BusChild *kid; + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + call_dev_aml_func(DEVICE(kid->child), scope); + } +} + static void register_types(void) { static const TypeInfo acpi_dev_if_info = { @@ -18,8 +29,15 @@ static void register_types(void) .parent = TYPE_INTERFACE, .class_size = sizeof(AcpiDeviceIfClass), }; + static const TypeInfo acpi_dev_aml_if_info = { + .name = TYPE_ACPI_DEV_AML_IF, + .parent = TYPE_INTERFACE, + .class_size = sizeof(AcpiDevAmlIfClass), + }; + type_register_static(&acpi_dev_if_info); + type_register_static(&acpi_dev_aml_if_info); } type_init(register_types) diff --git a/hw/acpi/aml-build-stub.c b/hw/acpi/aml-build-stub.c index 8d8ad1a314..89a8fec4af 100644 --- a/hw/acpi/aml-build-stub.c +++ b/hw/acpi/aml-build-stub.c @@ -26,6 +26,16 @@ void aml_append(Aml *parent_ctx, Aml *child) { } +Aml *aml_return(Aml *val) +{ + return NULL; +} + +Aml *aml_method(const char *name, int arg_count, AmlSerializeFlag sflag) +{ + return NULL; +} + Aml *aml_resource_template(void) { return NULL; diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index e6bfac95c7..6d4517cfbe 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -312,7 +312,7 @@ build_prepend_package_length(GArray *package, unsigned length, bool incl_self) /* * PkgLength is the length of the inclusive length of the data * and PkgLength's length itself when used for terms with - * explitit length. + * explicit length. */ length += length_bytes; } @@ -680,7 +680,7 @@ Aml *aml_store(Aml *val, Aml *target) * "Op Operand Operand Target" * pattern. * - * Returns: The newly allocated and composed according to patter Aml object. + * Returns: The newly allocated and composed according to pattern Aml object. */ static Aml * build_opcode_2arg_dst(uint8_t op, Aml *arg1, Aml *arg2, Aml *dst) @@ -1994,6 +1994,59 @@ static void build_processor_hierarchy_node(GArray *tbl, uint32_t flags, } } +void build_spcr(GArray *table_data, BIOSLinker *linker, + const AcpiSpcrData *f, const uint8_t rev, + const char *oem_id, const char *oem_table_id) +{ + AcpiTable table = { .sig = "SPCR", .rev = rev, .oem_id = oem_id, + .oem_table_id = oem_table_id }; + + acpi_table_begin(&table, table_data); + /* Interface type */ + build_append_int_noprefix(table_data, f->interface_type, 1); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 3); + /* Base Address */ + build_append_gas(table_data, f->base_addr.id, f->base_addr.width, + f->base_addr.offset, f->base_addr.size, + f->base_addr.addr); + /* Interrupt type */ + build_append_int_noprefix(table_data, f->interrupt_type, 1); + /* IRQ */ + build_append_int_noprefix(table_data, f->pc_interrupt, 1); + /* Global System Interrupt */ + build_append_int_noprefix(table_data, f->interrupt, 4); + /* Baud Rate */ + build_append_int_noprefix(table_data, f->baud_rate, 1); + /* Parity */ + build_append_int_noprefix(table_data, f->parity, 1); + /* Stop Bits */ + build_append_int_noprefix(table_data, f->stop_bits, 1); + /* Flow Control */ + build_append_int_noprefix(table_data, f->flow_control, 1); + /* Language */ + build_append_int_noprefix(table_data, f->language, 1); + /* Terminal Type */ + build_append_int_noprefix(table_data, f->terminal_type, 1); + /* PCI Device ID */ + build_append_int_noprefix(table_data, f->pci_device_id, 2); + /* PCI Vendor ID */ + build_append_int_noprefix(table_data, f->pci_vendor_id, 2); + /* PCI Bus Number */ + build_append_int_noprefix(table_data, f->pci_bus, 1); + /* PCI Device Number */ + build_append_int_noprefix(table_data, f->pci_device, 1); + /* PCI Function Number */ + build_append_int_noprefix(table_data, f->pci_function, 1); + /* PCI Flags */ + build_append_int_noprefix(table_data, f->pci_flags, 4); + /* PCI Segment */ + build_append_int_noprefix(table_data, f->pci_segment, 1); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); + + acpi_table_end(linker, &table); +} /* * ACPI spec, Revision 6.3 * 5.2.29 Processor Properties Topology Table (PPTT) @@ -2030,7 +2083,7 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, 0, socket_id, NULL, 0); } - if (mc->smp_props.clusters_supported) { + if (mc->smp_props.clusters_supported && mc->smp_props.has_clusters) { if (cpus->cpus[n].props.cluster_id != cluster_id) { assert(cpus->cpus[n].props.cluster_id > cluster_id); cluster_id = cpus->cpus[n].props.cluster_id; @@ -2070,7 +2123,7 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, acpi_table_end(linker, &table); } -/* build rev1/rev3/rev5.1 FADT */ +/* build rev1/rev3/rev5.1/rev6.0 FADT */ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, const char *oem_id, const char *oem_table_id) { @@ -2159,7 +2212,7 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, /* FADT Minor Version */ build_append_int_noprefix(tbl, f->minor_ver, 1); } else { - build_append_int_noprefix(tbl, 0, 3); /* Reserved upto ACPI 5.0 */ + build_append_int_noprefix(tbl, 0, 3); /* Reserved up to ACPI 5.0 */ } build_append_int_noprefix(tbl, 0, 8); /* X_FIRMWARE_CTRL */ @@ -2193,8 +2246,15 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, /* SLEEP_STATUS_REG */ build_append_gas_from_struct(tbl, &f->sleep_sts); - /* TODO: extra fields need to be added to support revisions above rev5 */ - assert(f->rev == 5); + if (f->rev == 5) { + goto done; + } + + /* Hypervisor Vendor Identity */ + build_append_padded_str(tbl, "QEMU", 8, '\0'); + + /* TODO: extra fields need to be added to support revisions above rev6 */ + assert(f->rev == 6); done: acpi_table_end(linker, &table); diff --git a/hw/acpi/core.c b/hw/acpi/core.c index 3e811bf03c..ec5e127d17 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -32,6 +32,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "sysemu/runstate.h" +#include "trace.h" struct acpi_table_header { uint16_t _length; /* our length, not actual part of the hdr */ @@ -185,7 +186,7 @@ static void acpi_table_install(const char unsigned *blob, size_t bloblen, changed_fields = 0; ext_hdr->_length = cpu_to_le16(acpi_payload_size); - if (hdrs->has_sig) { + if (hdrs->sig) { strncpy(ext_hdr->sig, hdrs->sig, sizeof ext_hdr->sig); ++changed_fields; } @@ -204,11 +205,11 @@ static void acpi_table_install(const char unsigned *blob, size_t bloblen, ext_hdr->checksum = 0; - if (hdrs->has_oem_id) { + if (hdrs->oem_id) { strncpy(ext_hdr->oem_id, hdrs->oem_id, sizeof ext_hdr->oem_id); ++changed_fields; } - if (hdrs->has_oem_table_id) { + if (hdrs->oem_table_id) { strncpy(ext_hdr->oem_table_id, hdrs->oem_table_id, sizeof ext_hdr->oem_table_id); ++changed_fields; @@ -217,7 +218,7 @@ static void acpi_table_install(const char unsigned *blob, size_t bloblen, ext_hdr->oem_revision = cpu_to_le32(hdrs->oem_rev); ++changed_fields; } - if (hdrs->has_asl_compiler_id) { + if (hdrs->asl_compiler_id) { strncpy(ext_hdr->asl_compiler_id, hdrs->asl_compiler_id, sizeof ext_hdr->asl_compiler_id); ++changed_fields; @@ -255,12 +256,12 @@ void acpi_table_add(const QemuOpts *opts, Error **errp) if (!hdrs) { goto out; } - if (hdrs->has_file == hdrs->has_data) { + if (!hdrs->file == !hdrs->data) { error_setg(errp, "'-acpitable' requires one of 'data' or 'file'"); goto out; } - pathnames = g_strsplit(hdrs->has_file ? hdrs->file : hdrs->data, ":", 0); + pathnames = g_strsplit(hdrs->file ?: hdrs->data, ":", 0); if (pathnames == NULL || pathnames[0] == NULL) { error_setg(errp, "'-acpitable' requires at least one pathname"); goto out; @@ -297,7 +298,7 @@ void acpi_table_add(const QemuOpts *opts, Error **errp) close(fd); } - acpi_table_install(blob, bloblen, hdrs->has_file, hdrs, errp); + acpi_table_install(blob, bloblen, !!hdrs->file, hdrs, errp); out: g_free(blob); @@ -551,8 +552,35 @@ void acpi_pm_tmr_reset(ACPIREGS *ar) } /* ACPI PM1aCNT */ -static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val) +void acpi_pm1_cnt_update(ACPIREGS *ar, + bool sci_enable, bool sci_disable) { + /* ACPI specs 3.0, 4.7.2.5 */ + if (ar->pm1.cnt.acpi_only) { + return; + } + + if (sci_enable) { + ar->pm1.cnt.cnt |= ACPI_BITMASK_SCI_ENABLE; + } else if (sci_disable) { + ar->pm1.cnt.cnt &= ~ACPI_BITMASK_SCI_ENABLE; + } +} + +static uint64_t acpi_pm_cnt_read(void *opaque, hwaddr addr, unsigned width) +{ + ACPIREGS *ar = opaque; + return ar->pm1.cnt.cnt >> addr * 8; +} + +static void acpi_pm_cnt_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + ACPIREGS *ar = opaque; + + if (addr == 1) { + val = val << 8 | (ar->pm1.cnt.cnt & 0xff); + } ar->pm1.cnt.cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE); if (val & ACPI_BITMASK_SLEEP_ENABLE) { @@ -575,33 +603,6 @@ static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val) } } -void acpi_pm1_cnt_update(ACPIREGS *ar, - bool sci_enable, bool sci_disable) -{ - /* ACPI specs 3.0, 4.7.2.5 */ - if (ar->pm1.cnt.acpi_only) { - return; - } - - if (sci_enable) { - ar->pm1.cnt.cnt |= ACPI_BITMASK_SCI_ENABLE; - } else if (sci_disable) { - ar->pm1.cnt.cnt &= ~ACPI_BITMASK_SCI_ENABLE; - } -} - -static uint64_t acpi_pm_cnt_read(void *opaque, hwaddr addr, unsigned width) -{ - ACPIREGS *ar = opaque; - return ar->pm1.cnt.cnt; -} - -static void acpi_pm_cnt_write(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - acpi_pm1_cnt_write(opaque, val); -} - static const MemoryRegionOps acpi_pm_cnt_ops = { .read = acpi_pm_cnt_read, .write = acpi_pm_cnt_write, @@ -688,9 +689,11 @@ void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val) cur = acpi_gpe_ioport_get_ptr(ar, addr); if (addr < ar->gpe.len / 2) { + trace_acpi_gpe_sts_ioport_writeb(addr, val); /* GPE_STS */ *cur = (*cur) & ~val; } else if (addr < ar->gpe.len) { + trace_acpi_gpe_en_ioport_writeb(addr - (ar->gpe.len / 2), val); /* GPE_EN */ *cur = val; } else { @@ -709,6 +712,12 @@ uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr) val = *cur; } + if (addr < ar->gpe.len / 2) { + trace_acpi_gpe_sts_ioport_readb(addr, val); + } else { + trace_acpi_gpe_en_ioport_readb(addr - (ar->gpe.len / 2), val); + } + return val; } diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index 3646dbfe68..2d81c1e790 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -1,6 +1,7 @@ #include "qemu/osdep.h" #include "migration/vmstate.h" #include "hw/acpi/cpu.h" +#include "hw/core/cpu.h" #include "qapi/error.h" #include "qapi/qapi-events-acpi.h" #include "trace.h" @@ -35,7 +36,6 @@ static ACPIOSTInfo *acpi_cpu_device_status(int idx, AcpiCpuStatus *cdev) DeviceState *dev = DEVICE(cdev->cpu); if (dev->id) { info->device = g_strdup(dev->id); - info->has_device = true; } } return info; @@ -297,7 +297,7 @@ static const VMStateDescription vmstate_cpuhp_sts = { .name = "CPU hotplug device state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(is_inserting, AcpiCpuStatus), VMSTATE_BOOL(is_removing, AcpiCpuStatus), VMSTATE_UINT32(ost_event, AcpiCpuStatus), @@ -310,7 +310,7 @@ const VMStateDescription vmstate_cpu_hotplug = { .name = "CPU hotplug state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(selector, CPUHotplugState), VMSTATE_UINT8(command, CPUHotplugState), VMSTATE_STRUCT_VARRAY_POINTER_UINT32(devs, CPUHotplugState, dev_count, @@ -339,7 +339,7 @@ const VMStateDescription vmstate_cpu_hotplug = { #define CPU_FW_EJECT_EVENT "CEJF" void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, - hwaddr io_base, + build_madt_cpu_fn build_madt_cpu, hwaddr io_base, const char *res_root, const char *event_handler_method) { @@ -354,9 +354,6 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, MachineClass *mc = MACHINE_GET_CLASS(machine); const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine); char *cphp_res_path = g_strdup_printf("%s." CPUHP_RES_DEVICE, res_root); - Object *obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, NULL); - AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj); - AcpiDeviceIf *adev = ACPI_DEVICE_IF(obj); cpu_ctrl_dev = aml_device("%s", cphp_res_path); { @@ -666,9 +663,7 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(dev, method); /* build _MAT object */ - assert(adevc && adevc->madt_cpu); - adevc->madt_cpu(adev, i, arch_ids, madt_buf, - true); /* set enabled flag */ + build_madt_cpu(i, arch_ids, madt_buf, true); /* set enabled flag */ aml_append(dev, aml_name_decl("_MAT", aml_buffer(madt_buf->len, (uint8_t *)madt_buf->data))); g_array_free(madt_buf, true); diff --git a/hw/acpi/cpu_hotplug.c b/hw/acpi/cpu_hotplug.c index 53654f8638..83b8bc5deb 100644 --- a/hw/acpi/cpu_hotplug.c +++ b/hw/acpi/cpu_hotplug.c @@ -13,8 +13,8 @@ #include "hw/acpi/cpu_hotplug.h" #include "qapi/error.h" #include "hw/core/cpu.h" -#include "hw/i386/pc.h" -#include "hw/pci/pci.h" +#include "hw/i386/x86.h" +#include "hw/pci/pci_device.h" #include "qemu/error-report.h" #define CPU_EJECT_METHOD "CPEJ" @@ -52,11 +52,15 @@ static const MemoryRegionOps AcpiCpuHotplug_ops = { .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, + .max_access_size = 4, + }, + .impl = { .max_access_size = 1, }, }; -static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu) +static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu, + bool *swtchd_to_modern) { CPUClass *k = CPU_GET_CLASS(cpu); int64_t cpu_id; @@ -65,23 +69,34 @@ static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu) if ((cpu_id / 8) >= ACPI_GPE_PROC_LEN) { object_property_set_bool(g->device, "cpu-hotplug-legacy", false, &error_abort); + *swtchd_to_modern = true; return; } + *swtchd_to_modern = false; g->sts[cpu_id / 8] |= (1 << (cpu_id % 8)); } void legacy_acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, AcpiCpuHotplug *g, DeviceState *dev, Error **errp) { - acpi_set_cpu_present_bit(g, CPU(dev)); - acpi_send_event(DEVICE(hotplug_dev), ACPI_CPU_HOTPLUG_STATUS); + bool swtchd_to_modern; + Error *local_err = NULL; + + acpi_set_cpu_present_bit(g, CPU(dev), &swtchd_to_modern); + if (swtchd_to_modern) { + /* propagate the hotplug to the modern interface */ + hotplug_handler_plug(hotplug_dev, dev, &local_err); + } else { + acpi_send_event(DEVICE(hotplug_dev), ACPI_CPU_HOTPLUG_STATUS); + } } void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, AcpiCpuHotplug *gpe_cpu, uint16_t base) { CPUState *cpu; + bool swtchd_to_modern; memory_region_init_io(&gpe_cpu->io, owner, &AcpiCpuHotplug_ops, gpe_cpu, "acpi-cpu-hotplug", ACPI_GPE_PROC_LEN); @@ -89,7 +104,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); + acpi_set_cpu_present_bit(gpe_cpu, cpu, &swtchd_to_modern); } } @@ -262,26 +277,27 @@ void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine, /* build Processor object for each processor */ for (i = 0; i < apic_ids->len; i++) { - int apic_id = apic_ids->cpus[i].arch_id; + int cpu_apic_id = apic_ids->cpus[i].arch_id; - assert(apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT); + assert(cpu_apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT); - dev = aml_processor(i, 0, 0, "CP%.02X", apic_id); + dev = aml_processor(i, 0, 0, "CP%.02X", cpu_apic_id); method = aml_method("_MAT", 0, AML_NOTSERIALIZED); aml_append(method, - aml_return(aml_call2(CPU_MAT_METHOD, aml_int(apic_id), aml_int(i)) + aml_return(aml_call2(CPU_MAT_METHOD, + aml_int(cpu_apic_id), aml_int(i)) )); aml_append(dev, method); method = aml_method("_STA", 0, AML_NOTSERIALIZED); aml_append(method, - aml_return(aml_call1(CPU_STATUS_METHOD, aml_int(apic_id)))); + aml_return(aml_call1(CPU_STATUS_METHOD, aml_int(cpu_apic_id)))); aml_append(dev, method); method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); aml_append(method, - aml_return(aml_call2(CPU_EJECT_METHOD, aml_int(apic_id), + aml_return(aml_call2(CPU_EJECT_METHOD, aml_int(cpu_apic_id), aml_arg(0))) ); aml_append(dev, method); @@ -295,11 +311,11 @@ void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine, /* Arg0 = APIC ID */ method = aml_method(AML_NOTIFY_METHOD, 2, AML_NOTSERIALIZED); for (i = 0; i < apic_ids->len; i++) { - int apic_id = apic_ids->cpus[i].arch_id; + int cpu_apic_id = apic_ids->cpus[i].arch_id; - if_ctx = aml_if(aml_equal(aml_arg(0), aml_int(apic_id))); + if_ctx = aml_if(aml_equal(aml_arg(0), aml_int(cpu_apic_id))); aml_append(if_ctx, - aml_notify(aml_name("CP%.02X", apic_id), aml_arg(1)) + aml_notify(aml_name("CP%.02X", cpu_apic_id), aml_arg(1)) ); aml_append(method, if_ctx); } @@ -316,13 +332,13 @@ void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine, aml_varpackage(x86ms->apic_id_limit); for (i = 0, apic_idx = 0; i < apic_ids->len; i++) { - int apic_id = apic_ids->cpus[i].arch_id; + int cpu_apic_id = apic_ids->cpus[i].arch_id; - for (; apic_idx < apic_id; apic_idx++) { + for (; apic_idx < cpu_apic_id; apic_idx++) { aml_append(pkg, aml_int(0)); } aml_append(pkg, aml_int(apic_ids->cpus[i].cpu ? 1 : 0)); - apic_idx = apic_id + 1; + apic_idx = cpu_apic_id + 1; } aml_append(sb_scope, aml_name_decl(CPU_ON_BITMAP, pkg)); aml_append(ctx, sb_scope); diff --git a/hw/acpi/cxl.c b/hw/acpi/cxl.c index 31d5235136..9cd7905ea2 100644 --- a/hw/acpi/cxl.c +++ b/hw/acpi/cxl.c @@ -30,9 +30,79 @@ #include "qapi/error.h" #include "qemu/uuid.h" -static void cedt_build_chbs(GArray *table_data, PXBDev *cxl) +void build_cxl_dsm_method(Aml *dev) { - SysBusDevice *sbd = SYS_BUS_DEVICE(cxl->cxl.cxl_host_bridge); + Aml *method, *ifctx, *ifctx2; + + method = aml_method("_DSM", 4, AML_SERIALIZED); + { + Aml *function, *uuid; + + uuid = aml_arg(0); + function = aml_arg(2); + /* CXL spec v3.0 9.17.3.1 _DSM Function for Retrieving QTG ID */ + ifctx = aml_if(aml_equal( + uuid, aml_touuid("F365F9A6-A7DE-4071-A66A-B40C0B4F8E52"))); + + /* Function 0, standard DSM query function */ + ifctx2 = aml_if(aml_equal(function, aml_int(0))); + { + uint8_t byte_list[1] = { 0x01 }; /* function 1 only */ + + aml_append(ifctx2, + aml_return(aml_buffer(sizeof(byte_list), byte_list))); + } + aml_append(ifctx, ifctx2); + + /* + * Function 1 + * Creating a package with static values. The max supported QTG ID will + * be 1 and recommended QTG IDs are 0 and then 1. + * The values here are statically created to simplify emulation. Values + * from a real BIOS would be determined by the performance of all the + * present CXL memory and then assigned. + */ + ifctx2 = aml_if(aml_equal(function, aml_int(1))); + { + Aml *pak, *pak1; + + /* + * Return: A package containing two elements - a WORD that returns + * the maximum throttling group that the platform supports, and a + * package containing the QTG ID(s) that the platform recommends. + * Package { + * Max Supported QTG ID + * Package {QTG Recommendations} + * } + * + * While the SPEC specified WORD that hints at the value being + * 16bit, the ACPI dump of BIOS DSDT table showed that the values + * are integers with no specific size specification. aml_int() will + * be used for the values. + */ + pak1 = aml_package(2); + /* Set QTG ID of 0 */ + aml_append(pak1, aml_int(0)); + /* Set QTG ID of 1 */ + aml_append(pak1, aml_int(1)); + + pak = aml_package(2); + /* Set Max QTG 1 */ + aml_append(pak, aml_int(1)); + aml_append(pak, pak1); + + aml_append(ifctx2, aml_return(pak)); + } + aml_append(ifctx, ifctx2); + } + aml_append(method, ifctx); + aml_append(dev, method); +} + +static void cedt_build_chbs(GArray *table_data, PXBCXLDev *cxl) +{ + PXBDev *pxb = PXB_DEV(cxl); + SysBusDevice *sbd = SYS_BUS_DEVICE(cxl->cxl_host_bridge); struct MemoryRegion *mr = sbd->mmio[0].memory; /* Type */ @@ -45,7 +115,7 @@ static void cedt_build_chbs(GArray *table_data, PXBDev *cxl) build_append_int_noprefix(table_data, 32, 2); /* UID - currently equal to bus number */ - build_append_int_noprefix(table_data, cxl->bus_nr, 4); + build_append_int_noprefix(table_data, pxb->bus_nr, 4); /* Version */ build_append_int_noprefix(table_data, 1, 4); @@ -65,9 +135,8 @@ static void cedt_build_chbs(GArray *table_data, PXBDev *cxl) * Interleave ways encoding in CXL 2.0 ECN: 3, 6, 12 and 16-way memory * interleaving. */ -static void cedt_build_cfmws(GArray *table_data, MachineState *ms) +static void cedt_build_cfmws(GArray *table_data, CXLState *cxls) { - CXLState *cxls = ms->cxl_devices_state; GList *it; for (it = cxls->fixed_windows; it; it = it->next) { @@ -113,7 +182,7 @@ static void cedt_build_cfmws(GArray *table_data, MachineState *ms) /* Host Bridge List (list of UIDs - currently bus_nr) */ for (i = 0; i < fw->num_targets; i++) { g_assert(fw->target_hbs[i]); - build_append_int_noprefix(table_data, fw->target_hbs[i]->bus_nr, 4); + build_append_int_noprefix(table_data, PXB_DEV(fw->target_hbs[i])->bus_nr, 4); } } } @@ -122,16 +191,16 @@ static int cxl_foreach_pxb_hb(Object *obj, void *opaque) { Aml *cedt = opaque; - if (object_dynamic_cast(obj, TYPE_PXB_CXL_DEVICE)) { + if (object_dynamic_cast(obj, TYPE_PXB_CXL_DEV)) { cedt_build_chbs(cedt->buf, PXB_CXL_DEV(obj)); } return 0; } -void cxl_build_cedt(MachineState *ms, GArray *table_offsets, GArray *table_data, +void cxl_build_cedt(GArray *table_offsets, GArray *table_data, BIOSLinker *linker, const char *oem_id, - const char *oem_table_id) + const char *oem_table_id, CXLState *cxl_state) { Aml *cedt; AcpiTable table = { .sig = "CEDT", .rev = 1, .oem_id = oem_id, @@ -144,7 +213,7 @@ void cxl_build_cedt(MachineState *ms, GArray *table_offsets, GArray *table_data, /* reserve space for CEDT header */ object_child_foreach_recursive(object_get_root(), cxl_foreach_pxb_hb, cedt); - cedt_build_cfmws(cedt->buf, ms); + cedt_build_cfmws(cedt->buf, cxl_state); /* copy AML table into ACPI tables blob and patch header there */ g_array_append_vals(table_data, cedt->buf->data, cedt->buf->len); diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c index de509c2b48..b2f1b13630 100644 --- a/hw/acpi/erst.c +++ b/hw/acpi/erst.c @@ -14,7 +14,7 @@ #include "hw/qdev-core.h" #include "exec/memory.h" #include "qom/object.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "qom/object_interfaces.h" #include "qemu/error-report.h" #include "migration/vmstate.h" @@ -440,6 +440,7 @@ static void check_erst_backend_storage(ERSTDeviceState *s, Error **errp) (record_size >= 4096) /* PAGE_SIZE */ )) { error_setg(errp, "ERST record_size %u is invalid", record_size); + return; } /* Validity check header */ @@ -450,6 +451,7 @@ static void check_erst_backend_storage(ERSTDeviceState *s, Error **errp) (le16_to_cpu(header->reserved) == 0) )) { error_setg(errp, "ERST backend storage header is invalid"); + return; } /* Check storage_size against record_size */ @@ -457,6 +459,7 @@ static void check_erst_backend_storage(ERSTDeviceState *s, Error **errp) (record_size > s->storage_size)) { error_setg(errp, "ACPI ERST requires storage size be multiple of " "record size (%uKiB)", record_size); + return; } /* Compute offset of first and last record storage slot */ @@ -632,7 +635,7 @@ static unsigned read_erst_record(ERSTDeviceState *s) if (record_length < UEFI_CPER_RECORD_MIN_SIZE) { rc = STATUS_FAILED; } - if ((s->record_offset + record_length) > exchange_length) { + if (record_length > exchange_length - s->record_offset) { rc = STATUS_FAILED; } /* If all is ok, copy the record to the exchange buffer */ @@ -681,7 +684,7 @@ static unsigned write_erst_record(ERSTDeviceState *s) if (record_length < UEFI_CPER_RECORD_MIN_SIZE) { return STATUS_FAILED; } - if ((s->record_offset + record_length) > exchange_length) { + if (record_length > exchange_length - s->record_offset) { return STATUS_FAILED; } @@ -713,7 +716,7 @@ static unsigned write_erst_record(ERSTDeviceState *s) if (nvram) { /* Write the record into the slot */ memcpy(nvram, exchange, record_length); - memset(nvram + record_length, exchange_length - record_length, 0xFF); + memset(nvram + record_length, 0xFF, exchange_length - record_length); /* If a new record, increment the record_count */ if (!record_found) { uint32_t record_count; @@ -929,7 +932,7 @@ static const VMStateDescription erst_vmstate = { .version_id = 1, .minimum_version_id = 1, .post_load = erst_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(operation, ERSTDeviceState), VMSTATE_UINT8(busy_status, ERSTDeviceState), VMSTATE_UINT8(command_status, ERSTDeviceState), @@ -944,6 +947,7 @@ static const VMStateDescription erst_vmstate = { static void erst_realizefn(PCIDevice *pci_dev, Error **errp) { + ERRP_GUARD(); ERSTDeviceState *s = ACPIERST(pci_dev); trace_acpi_erst_realizefn_in(); @@ -961,9 +965,15 @@ static void erst_realizefn(PCIDevice *pci_dev, Error **errp) /* HostMemoryBackend size will be multiple of PAGE_SIZE */ s->storage_size = object_property_get_int(OBJECT(s->hostmem), "size", errp); + if (*errp) { + return; + } /* Initialize backend storage and record_count */ check_erst_backend_storage(s, errp); + if (*errp) { + return; + } /* BAR 0: Programming registers */ memory_region_init_io(&s->iomem_mr, OBJECT(pci_dev), &erst_reg_ops, s, @@ -974,6 +984,9 @@ static void erst_realizefn(PCIDevice *pci_dev, Error **errp) memory_region_init_ram(&s->exchange_mr, OBJECT(pci_dev), "erst.exchange", le32_to_cpu(s->header->record_size), errp); + if (*errp) { + return; + } pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->exchange_mr); diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index e28457a7d1..2d6e91b124 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -267,6 +267,13 @@ static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev, } } +static void acpi_ged_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list) +{ + AcpiGedState *s = ACPI_GED(adev); + + acpi_memory_ospm_status(&s->memhp_state, list); +} + static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) { AcpiGedState *s = ACPI_GED(adev); @@ -305,7 +312,7 @@ static const VMStateDescription vmstate_memhp_state = { .name = "acpi-ged/memhp", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_MEMORY_HOTPLUG(memhp_state, AcpiGedState), VMSTATE_END_OF_LIST() } @@ -315,7 +322,7 @@ static const VMStateDescription vmstate_ged_state = { .name = "acpi-ged-state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(sel, GEDState), VMSTATE_END_OF_LIST() } @@ -325,7 +332,7 @@ static const VMStateDescription vmstate_ghes = { .name = "acpi-ghes", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(ghes_addr_le, AcpiGhesState), VMSTATE_END_OF_LIST() }, @@ -342,7 +349,7 @@ static const VMStateDescription vmstate_ghes_state = { .version_id = 1, .minimum_version_id = 1, .needed = ghes_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(ghes_state, AcpiGedState, 1, vmstate_ghes, AcpiGhesState), VMSTATE_END_OF_LIST() @@ -353,11 +360,11 @@ static const VMStateDescription vmstate_acpi_ged = { .name = "acpi-ged", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(ged_state, AcpiGedState, 1, vmstate_ged_state, GEDState), VMSTATE_END_OF_LIST(), }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_memhp_state, &vmstate_ghes_state, NULL @@ -409,6 +416,7 @@ static void acpi_ged_class_init(ObjectClass *class, void *data) hc->unplug_request = acpi_ged_unplug_request_cb; hc->unplug = acpi_ged_unplug_cb; + adevc->ospm_status = acpi_ged_ospm_status; adevc->send_event = acpi_ged_send_event; } diff --git a/hw/acpi/hmat.c b/hw/acpi/hmat.c index 3a6d51282a..9b1662b6b8 100644 --- a/hw/acpi/hmat.c +++ b/hw/acpi/hmat.c @@ -27,6 +27,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "sysemu/numa.h" +#include "hw/acpi/aml-build.h" #include "hw/acpi/hmat.h" /* @@ -77,12 +78,13 @@ static void build_hmat_lb(GArray *table_data, HMAT_LB_Info *hmat_lb, uint32_t *initiator_list) { int i, index; + uint32_t initiator_to_index[MAX_NODES] = {}; 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 */ + = 32 /* Table length up to 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 */ @@ -120,6 +122,8 @@ static void build_hmat_lb(GArray *table_data, HMAT_LB_Info *hmat_lb, /* Initiator Proximity Domain List */ for (i = 0; i < num_initiator; i++) { build_append_int_noprefix(table_data, initiator_list[i], 4); + /* Reverse mapping for array possitions */ + initiator_to_index[initiator_list[i]] = i; } /* Target Proximity Domain List */ @@ -131,7 +135,8 @@ static void build_hmat_lb(GArray *table_data, HMAT_LB_Info *hmat_lb, entry_list = g_new0(uint16_t, num_initiator * num_target); for (i = 0; i < hmat_lb->list->len; i++) { lb_data = &g_array_index(hmat_lb->list, HMAT_LB_Data, i); - index = lb_data->initiator * num_target + lb_data->target; + index = initiator_to_index[lb_data->initiator] * num_target + + lb_data->target; entry_list[index] = (uint16_t)(lb_data->data / hmat_lb->base); } @@ -203,6 +208,13 @@ static void hmat_build_table_structs(GArray *table_data, NumaState *numa_state) build_append_int_noprefix(table_data, 0, 4); /* Reserved */ for (i = 0; i < numa_state->num_nodes; i++) { + /* + * Linux rejects whole HMAT table if a node with no memory + * has one of these structures listing it as a target. + */ + if (!numa_state->nodes[i].node_mem) { + continue; + } flags = 0; if (numa_state->nodes[i].initiator < MAX_NODES) { @@ -213,7 +225,7 @@ static void hmat_build_table_structs(GArray *table_data, NumaState *numa_state) } for (i = 0; i < numa_state->num_nodes; i++) { - if (numa_state->nodes[i].has_cpu) { + if (numa_state->nodes[i].has_cpu || numa_state->nodes[i].has_gi) { initiator_list[num_initiator++] = i; } } diff --git a/hw/acpi/hmat.h b/hw/acpi/hmat.h index b57f0e7e80..fd989cb661 100644 --- a/hw/acpi/hmat.h +++ b/hw/acpi/hmat.h @@ -27,7 +27,8 @@ #ifndef HMAT_H #define HMAT_H -#include "hw/acpi/aml-build.h" +#include "hw/acpi/bios-linker-loader.h" +#include "sysemu/numa.h" /* * ACPI 6.3: 5.2.27.3 Memory Proximity Domain Attributes Structure, diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index bd9bbade70..573d032e8e 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -34,9 +34,9 @@ #include "sysemu/reset.h" #include "sysemu/runstate.h" #include "hw/acpi/acpi.h" -#include "hw/acpi/tco.h" +#include "hw/acpi/ich9_tco.h" -#include "hw/i386/ich9.h" +#include "hw/southbridge/ich9.h" #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" @@ -164,7 +164,7 @@ static const VMStateDescription vmstate_memhp_state = { .version_id = 1, .minimum_version_id = 1, .needed = vmstate_test_use_memhp, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, ICH9LPCPMRegs), VMSTATE_END_OF_LIST() } @@ -181,7 +181,7 @@ static const VMStateDescription vmstate_tco_io_state = { .version_id = 1, .minimum_version_id = 1, .needed = vmstate_test_use_tco, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts, TCOIORegs), VMSTATE_END_OF_LIST() @@ -208,7 +208,7 @@ static const VMStateDescription vmstate_cpuhp_state = { .minimum_version_id = 1, .needed = vmstate_test_use_cpuhp, .pre_load = vmstate_cpuhp_pre_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CPU_HOTPLUG(cpuhp_state, ICH9LPCPMRegs), VMSTATE_END_OF_LIST() } @@ -218,7 +218,7 @@ static bool vmstate_test_use_pcihp(void *opaque) { ICH9LPCPMRegs *s = opaque; - return s->use_acpi_hotplug_bridge; + return s->acpi_pci_hotplug.use_acpi_hotplug_bridge; } static const VMStateDescription vmstate_pcihp_state = { @@ -226,7 +226,7 @@ static const VMStateDescription vmstate_pcihp_state = { .version_id = 1, .minimum_version_id = 1, .needed = vmstate_test_use_pcihp, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug, ICH9LPCPMRegs, NULL, NULL), @@ -239,7 +239,7 @@ const VMStateDescription vmstate_ich9_pm = { .version_id = 1, .minimum_version_id = 1, .post_load = ich9_pm_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(acpi_regs.pm1.evt.sts, ICH9LPCPMRegs), VMSTATE_UINT16(acpi_regs.pm1.evt.en, ICH9LPCPMRegs), VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, ICH9LPCPMRegs), @@ -251,7 +251,7 @@ const VMStateDescription vmstate_ich9_pm = { VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_memhp_state, &vmstate_tco_io_state, &vmstate_cpuhp_state, @@ -277,8 +277,8 @@ static void pm_reset(void *opaque) } pm->smi_en_wmask = ~0; - if (pm->use_acpi_hotplug_bridge) { - acpi_pcihp_reset(&pm->acpi_pci_hotplug, true); + if (pm->acpi_pci_hotplug.use_acpi_hotplug_bridge) { + acpi_pcihp_reset(&pm->acpi_pci_hotplug); } acpi_update_sci(&pm->acpi_regs, pm->irq); @@ -291,9 +291,7 @@ static void pm_powerdown_req(Notifier *n, void *opaque) acpi_pm1_evt_power_down(&pm->acpi_regs); } -void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, - bool smm_enabled, - qemu_irq sci_irq) +void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq) { memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE); memory_region_set_enabled(&pm->io, false); @@ -303,7 +301,7 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io); acpi_pm1_evt_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io); acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, pm->disable_s3, pm->disable_s4, - pm->s4_val, !pm->smm_compat && !smm_enabled); + pm->s4_val, !pm->smm_compat && !pm->smm_enabled); acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN); memory_region_init_io(&pm->io_gpe, OBJECT(lpc_pci), &ich9_gpe_ops, pm, @@ -314,17 +312,15 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, "acpi-smi", 8); memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi); - pm->smm_enabled = smm_enabled; + if (pm->enable_tco) { + acpi_pm_tco_init(&pm->tco_regs, &pm->io); + } - pm->enable_tco = true; - acpi_pm_tco_init(&pm->tco_regs, &pm->io); - - if (pm->use_acpi_hotplug_bridge) { + if (pm->acpi_pci_hotplug.use_acpi_hotplug_bridge) { acpi_pcihp_init(OBJECT(lpc_pci), &pm->acpi_pci_hotplug, pci_get_bus(lpc_pci), pci_address_space_io(lpc_pci), - true, ACPI_PCIHP_ADDR_ICH9); qbus_set_hotplug_handler(BUS(pci_get_bus(lpc_pci)), @@ -406,14 +402,14 @@ static bool ich9_pm_get_acpi_pci_hotplug(Object *obj, Error **errp) { ICH9LPCState *s = ICH9_LPC_DEVICE(obj); - return s->pm.use_acpi_hotplug_bridge; + return s->pm.acpi_pci_hotplug.use_acpi_hotplug_bridge; } static void ich9_pm_set_acpi_pci_hotplug(Object *obj, bool value, Error **errp) { ICH9LPCState *s = ICH9_LPC_DEVICE(obj); - s->pm.use_acpi_hotplug_bridge = value; + s->pm.acpi_pci_hotplug.use_acpi_hotplug_bridge = value; } static bool ich9_pm_get_keep_pci_slot_hpc(Object *obj, Error **errp) @@ -438,8 +434,9 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm) pm->disable_s3 = 0; pm->disable_s4 = 0; pm->s4_val = 2; - pm->use_acpi_hotplug_bridge = true; + pm->acpi_pci_hotplug.use_acpi_hotplug_bridge = true; pm->keep_pci_slot_hpc = true; + pm->enable_tco = true; object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE, &pm->pm_io_base, OBJ_PROP_FLAG_READ); @@ -581,6 +578,12 @@ void ich9_pm_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, } } +bool ich9_pm_is_hotpluggable_bus(HotplugHandler *hotplug_dev, BusState *bus) +{ + ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); + return acpi_pcihp_is_hotpluggbale_bus(&lpc->pm.acpi_pci_hotplug, bus); +} + void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list) { ICH9LPCState *s = ICH9_LPC_DEVICE(adev); diff --git a/hw/acpi/tco.c b/hw/acpi/ich9_tco.c similarity index 90% rename from hw/acpi/tco.c rename to hw/acpi/ich9_tco.c index 4783721e4e..81606219f7 100644 --- a/hw/acpi/tco.c +++ b/hw/acpi/ich9_tco.c @@ -9,10 +9,10 @@ #include "qemu/osdep.h" #include "sysemu/watchdog.h" -#include "hw/i386/ich9.h" +#include "hw/southbridge/ich9.h" #include "migration/vmstate.h" -#include "hw/acpi/tco.h" +#include "hw/acpi/ich9_tco.h" #include "trace.h" enum { @@ -86,6 +86,7 @@ static inline int can_start_tco_timer(TCOIORegs *tr) static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr) { uint16_t rld; + uint32_t ret = 0; switch (addr) { case TCO_RLD: @@ -96,35 +97,49 @@ static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr) } else { rld = tr->tco.rld; } - return rld; + ret = rld; + break; case TCO_DAT_IN: - return tr->tco.din; + ret = tr->tco.din; + break; case TCO_DAT_OUT: - return tr->tco.dout; + ret = tr->tco.dout; + break; case TCO1_STS: - return tr->tco.sts1; + ret = tr->tco.sts1; + break; case TCO2_STS: - return tr->tco.sts2; + ret = tr->tco.sts2; + break; case TCO1_CNT: - return tr->tco.cnt1; + ret = tr->tco.cnt1; + break; case TCO2_CNT: - return tr->tco.cnt2; + ret = tr->tco.cnt2; + break; case TCO_MESSAGE1: - return tr->tco.msg1; + ret = tr->tco.msg1; + break; case TCO_MESSAGE2: - return tr->tco.msg2; + ret = tr->tco.msg2; + break; case TCO_WDCNT: - return tr->tco.wdcnt; + ret = tr->tco.wdcnt; + break; case TCO_TMR: - return tr->tco.tmr; + ret = tr->tco.tmr; + break; case SW_IRQ_GEN: - return tr->sw_irq_gen; + ret = tr->sw_irq_gen; + break; } - return 0; + trace_tco_io_read(addr, ret); + return ret; } static void tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val) { + trace_tco_io_write(addr, val); switch (addr) { case TCO_RLD: tr->timeouts_no = 0; @@ -239,7 +254,7 @@ const VMStateDescription vmstate_tco_io_sts = { .name = "tco io device status", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(tco.rld, TCOIORegs), VMSTATE_UINT8(tco.din, TCOIORegs), VMSTATE_UINT8(tco.dout, TCOIORegs), diff --git a/hw/acpi/ipmi-stub.c b/hw/acpi/ipmi-stub.c index 8634fb325c..befaf0a882 100644 --- a/hw/acpi/ipmi-stub.c +++ b/hw/acpi/ipmi-stub.c @@ -10,6 +10,6 @@ #include "qemu/osdep.h" #include "hw/acpi/ipmi.h" -void build_acpi_ipmi_devices(Aml *table, BusState *bus, const char *resource) +void build_ipmi_dev_aml(AcpiDevAmlIf *adev, Aml *scope) { } diff --git a/hw/acpi/ipmi.c b/hw/acpi/ipmi.c index 96e48eba15..a20e57d465 100644 --- a/hw/acpi/ipmi.c +++ b/hw/acpi/ipmi.c @@ -13,7 +13,7 @@ #include "hw/acpi/acpi.h" #include "hw/acpi/ipmi.h" -static Aml *aml_ipmi_crs(IPMIFwInfo *info, const char *resource) +static Aml *aml_ipmi_crs(IPMIFwInfo *info) { Aml *crs = aml_resource_template(); @@ -49,7 +49,7 @@ static Aml *aml_ipmi_crs(IPMIFwInfo *info, const char *resource) break; case IPMI_MEMSPACE_SMBUS: aml_append(crs, aml_i2c_serial_bus_device(info->base_address, - resource)); + "^")); break; default: abort(); @@ -62,46 +62,27 @@ static Aml *aml_ipmi_crs(IPMIFwInfo *info, const char *resource) return crs; } -static Aml *aml_ipmi_device(IPMIFwInfo *info, const char *resource) +void build_ipmi_dev_aml(AcpiDevAmlIf *adev, Aml *scope) { Aml *dev; - uint16_t version = ((info->ipmi_spec_major_revision << 8) - | (info->ipmi_spec_minor_revision << 4)); + IPMIFwInfo info = {}; + IPMIInterface *ii = IPMI_INTERFACE(adev); + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + uint16_t version; - assert(info->ipmi_spec_minor_revision <= 15); + iic->get_fwinfo(ii, &info); + assert(info.ipmi_spec_minor_revision <= 15); + version = ((info.ipmi_spec_major_revision << 8) + | (info.ipmi_spec_minor_revision << 4)); - dev = aml_device("MI%d", info->uuid); + dev = aml_device("MI%d", info.uuid); aml_append(dev, aml_name_decl("_HID", aml_eisaid("IPI0001"))); aml_append(dev, aml_name_decl("_STR", aml_string("ipmi_%s", - info->interface_name))); - aml_append(dev, aml_name_decl("_UID", aml_int(info->uuid))); - aml_append(dev, aml_name_decl("_CRS", aml_ipmi_crs(info, resource))); - aml_append(dev, aml_name_decl("_IFT", aml_int(info->interface_type))); + info.interface_name))); + aml_append(dev, aml_name_decl("_UID", aml_int(info.uuid))); + aml_append(dev, aml_name_decl("_CRS", aml_ipmi_crs(&info))); + aml_append(dev, aml_name_decl("_IFT", aml_int(info.interface_type))); aml_append(dev, aml_name_decl("_SRV", aml_int(version))); - return dev; -} - -void build_acpi_ipmi_devices(Aml *scope, BusState *bus, const char *resource) -{ - - BusChild *kid; - - QTAILQ_FOREACH(kid, &bus->children, sibling) { - IPMIInterface *ii; - IPMIInterfaceClass *iic; - IPMIFwInfo info; - Object *obj = object_dynamic_cast(OBJECT(kid->child), - TYPE_IPMI_INTERFACE); - - if (!obj) { - continue; - } - - ii = IPMI_INTERFACE(obj); - iic = IPMI_INTERFACE_GET_CLASS(obj); - memset(&info, 0, sizeof(info)); - iic->get_fwinfo(ii, &info); - aml_append(scope, aml_ipmi_device(&info, resource)); - } + aml_append(scope, dev); } diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index 0a7e89a13e..de6f974ebb 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -1,6 +1,7 @@ #include "qemu/osdep.h" #include "hw/acpi/memory_hotplug.h" #include "hw/mem/pc-dimm.h" +#include "hw/boards.h" #include "hw/qdev-core.h" #include "migration/vmstate.h" #include "trace.h" @@ -44,7 +45,6 @@ static ACPIOSTInfo *acpi_memory_device_status(int slot, MemStatus *mdev) DeviceState *dev = DEVICE(mdev->dimm); if (dev->id) { info->device = g_strdup(dev->id); - info->has_device = true; } } return info; @@ -186,7 +186,7 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, */ qapi_event_send_mem_unplug_error(dev->id ? : "", error_get_pretty(local_err)); - qapi_event_send_device_unplug_guest_error(!!dev->id, dev->id, + qapi_event_send_device_unplug_guest_error(dev->id, dev->canonical_path); error_free(local_err); break; @@ -317,7 +317,7 @@ static const VMStateDescription vmstate_memhp_sts = { .name = "memory hotplug device state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(is_enabled, MemStatus), VMSTATE_BOOL(is_inserting, MemStatus), VMSTATE_UINT32(ost_event, MemStatus), @@ -330,7 +330,7 @@ const VMStateDescription vmstate_memory_hotplug = { .name = "memory hotplug state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(selector, MemHotplugState), VMSTATE_STRUCT_VARRAY_POINTER_UINT32(devs, MemHotplugState, dev_count, vmstate_memhp_sts, MemStatus), diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build index cea2f5f93a..fa5c07db90 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -1,5 +1,6 @@ acpi_ss = ss.source_set() acpi_ss.add(files( + 'acpi_generic_initiator.c', 'acpi_interface.c', 'aml-build.c', 'bios-linker-loader.c', @@ -19,20 +20,18 @@ acpi_ss.add(when: 'CONFIG_ACPI_HW_REDUCED', if_true: files('generic_event_device acpi_ss.add(when: 'CONFIG_ACPI_HMAT', if_true: files('hmat.c')) acpi_ss.add(when: 'CONFIG_ACPI_APEI', if_true: files('ghes.c'), if_false: files('ghes-stub.c')) acpi_ss.add(when: 'CONFIG_ACPI_PIIX4', if_true: files('piix4.c')) +acpi_ss.add(when: 'CONFIG_ACPI_PCI_BRIDGE', if_true: files('pci-bridge.c')) acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_true: files('pcihp.c')) acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_false: files('acpi-pci-hotplug-stub.c')) acpi_ss.add(when: 'CONFIG_ACPI_VIOT', if_true: files('viot.c')) -acpi_ss.add(when: 'CONFIG_ACPI_X86_ICH', if_true: files('ich9.c', 'tco.c')) +acpi_ss.add(when: 'CONFIG_ACPI_ICH9', if_true: files('ich9.c', 'ich9_tco.c')) acpi_ss.add(when: 'CONFIG_ACPI_ERST', if_true: files('erst.c')) acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c'), if_false: files('ipmi-stub.c')) acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c')) if have_tpm acpi_ss.add(files('tpm.c')) endif -softmmu_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c')) -softmmu_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c', 'aml-build-stub.c', - 'acpi-x86-stub.c', 'ipmi-stub.c', 'ghes-stub.c', - 'acpi-mem-hotplug-stub.c', 'acpi-cpu-hotplug-stub.c', - 'acpi-pci-hotplug-stub.c', 'acpi-nvdimm-stub.c', - 'cxl-stub.c')) +system_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c', 'acpi_interface.c')) +system_ss.add(when: 'CONFIG_ACPI_PCI_BRIDGE', if_false: files('pci-bridge-stub.c')) +system_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss) +system_ss.add(files('acpi-qmp-cmds.c')) diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 0d43da19ea..9ba90806f2 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -35,6 +35,7 @@ #include "hw/nvram/fw_cfg.h" #include "hw/mem/nvdimm.h" #include "qemu/nvdimm-utils.h" +#include "trace.h" /* * define Byte Addressable Persistent Memory (PM) Region according to @@ -476,7 +477,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[]; /* the data got via Get Namesapce Label function. */ + uint8_t out_buf[]; /* the data got via Get Namespace Label function. */ } QEMU_PACKED; typedef struct NvdimmFuncGetLabelDataOut NvdimmFuncGetLabelDataOut; QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataOut) > NVDIMM_DSM_MEMORY_SIZE); @@ -550,8 +551,8 @@ static void nvdimm_dsm_func_read_fit(NVDIMMState *state, NvdimmDsmIn *in, fit = fit_buf->fit; - nvdimm_debug("Read FIT: offset 0x%x FIT size 0x%x Dirty %s.\n", - read_fit->offset, fit->len, fit_buf->dirty ? "Yes" : "No"); + trace_acpi_nvdimm_read_fit(read_fit->offset, fit->len, + fit_buf->dirty ? "Yes" : "No"); if (read_fit->offset > fit->len) { func_ret_status = NVDIMM_DSM_RET_STATUS_INVALID; @@ -658,7 +659,7 @@ static void nvdimm_dsm_label_size(NVDIMMDevice *nvdimm, hwaddr dsm_mem_addr) label_size = nvdimm->label_size; mxfer = nvdimm_get_max_xfer_label_size(); - nvdimm_debug("label_size 0x%x, max_xfer 0x%x.\n", label_size, mxfer); + trace_acpi_nvdimm_label_info(label_size, mxfer); label_size_out.func_ret_status = cpu_to_le32(NVDIMM_DSM_RET_STATUS_SUCCESS); label_size_out.label_size = cpu_to_le32(label_size); @@ -669,28 +670,31 @@ static void nvdimm_dsm_label_size(NVDIMMDevice *nvdimm, hwaddr dsm_mem_addr) } static uint32_t nvdimm_rw_label_data_check(NVDIMMDevice *nvdimm, - uint32_t offset, uint32_t length) + uint32_t offset, uint32_t length, + bool is_write) { uint32_t ret = NVDIMM_DSM_RET_STATUS_INVALID; if (offset + length < offset) { - nvdimm_debug("offset 0x%x + length 0x%x is overflow.\n", offset, - length); + trace_acpi_nvdimm_label_overflow(offset, length); return ret; } if (nvdimm->label_size < offset + length) { - nvdimm_debug("position 0x%x is beyond label data (len = %" PRIx64 ").\n", - offset + length, nvdimm->label_size); + trace_acpi_nvdimm_label_oversize(offset + length, nvdimm->label_size); return ret; } if (length > nvdimm_get_max_xfer_label_size()) { - nvdimm_debug("length (0x%x) is larger than max_xfer (0x%x).\n", - length, nvdimm_get_max_xfer_label_size()); + trace_acpi_nvdimm_label_xfer_exceed(length, + nvdimm_get_max_xfer_label_size()); return ret; } + if (is_write && nvdimm->readonly) { + return NVDIMM_DSM_RET_STATUS_UNSUPPORT; + } + return NVDIMM_DSM_RET_STATUS_SUCCESS; } @@ -710,11 +714,11 @@ static void nvdimm_dsm_get_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in, get_label_data->offset = le32_to_cpu(get_label_data->offset); get_label_data->length = le32_to_cpu(get_label_data->length); - nvdimm_debug("Read Label Data: offset 0x%x length 0x%x.\n", - get_label_data->offset, get_label_data->length); + trace_acpi_nvdimm_read_label(get_label_data->offset, + get_label_data->length); status = nvdimm_rw_label_data_check(nvdimm, get_label_data->offset, - get_label_data->length); + get_label_data->length, false); if (status != NVDIMM_DSM_RET_STATUS_SUCCESS) { nvdimm_dsm_no_payload(status, dsm_mem_addr); return; @@ -749,11 +753,11 @@ static void nvdimm_dsm_set_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in, set_label_data->offset = le32_to_cpu(set_label_data->offset); set_label_data->length = le32_to_cpu(set_label_data->length); - nvdimm_debug("Write Label Data: offset 0x%x length 0x%x.\n", - set_label_data->offset, set_label_data->length); + trace_acpi_nvdimm_write_label(set_label_data->offset, + set_label_data->length); status = nvdimm_rw_label_data_check(nvdimm, set_label_data->offset, - set_label_data->length); + set_label_data->length, true); if (status != NVDIMM_DSM_RET_STATUS_SUCCESS) { nvdimm_dsm_no_payload(status, dsm_mem_addr); return; @@ -821,7 +825,7 @@ static void nvdimm_dsm_device(NvdimmDsmIn *in, hwaddr dsm_mem_addr) static uint64_t nvdimm_dsm_read(void *opaque, hwaddr addr, unsigned size) { - nvdimm_debug("BUG: we never read _DSM IO Port.\n"); + trace_acpi_nvdimm_read_io_port(); return 0; } @@ -832,7 +836,7 @@ nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) NvdimmDsmIn *in; hwaddr dsm_mem_addr = val; - nvdimm_debug("dsm memory address 0x%" HWADDR_PRIx ".\n", dsm_mem_addr); + trace_acpi_nvdimm_dsm_mem_addr(dsm_mem_addr); /* * The DSM memory is mapped to guest address space so an evil guest @@ -846,12 +850,10 @@ nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) in->function = le32_to_cpu(in->function); in->handle = le32_to_cpu(in->handle); - nvdimm_debug("Revision 0x%x Handler 0x%x Function 0x%x.\n", in->revision, - in->handle, in->function); + trace_acpi_nvdimm_dsm_info(in->revision, in->handle, in->function); if (in->revision != 0x1 /* Currently we only support DSM Spec Rev1. */) { - nvdimm_debug("Revision 0x%x is not supported, expect 0x%x.\n", - in->revision, 0x1); + trace_acpi_nvdimm_invalid_revision(in->revision); nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_UNSUPPORT, dsm_mem_addr); goto exit; } @@ -925,6 +927,7 @@ void nvdimm_init_acpi_state(NVDIMMState *state, MemoryRegion *io, #define NVDIMM_DSM_RFIT_STATUS "RSTA" #define NVDIMM_QEMU_RSVD_UUID "648B9CF2-CDA1-4312-8AD9-49C4AF32BD62" +#define NVDIMM_DEVICE_DSM_UUID "4309AC30-0D11-11E4-9191-0800200C9A66" static void nvdimm_build_common_dsm(Aml *dev, NVDIMMState *nvdimm_state) @@ -1032,15 +1035,14 @@ static void nvdimm_build_common_dsm(Aml *dev, /* UUID for QEMU internal use */), expected_uuid)); aml_append(elsectx, ifctx); elsectx2 = aml_else(); - aml_append(elsectx2, aml_store( - aml_touuid("4309AC30-0D11-11E4-9191-0800200C9A66") + aml_append(elsectx2, aml_store(aml_touuid(NVDIMM_DEVICE_DSM_UUID) /* UUID for NVDIMM Devices */, expected_uuid)); aml_append(elsectx, elsectx2); aml_append(method, elsectx); uuid_invalid = aml_lnot(aml_equal(uuid, expected_uuid)); - unsupport = aml_if(aml_or(unpatched, uuid_invalid, NULL)); + unsupport = aml_if(aml_lor(unpatched, uuid_invalid)); /* * function 0 is called to inquire what functions are supported by @@ -1072,10 +1074,9 @@ static void nvdimm_build_common_dsm(Aml *dev, * in the DSM Spec. */ pckg = aml_arg(3); - ifctx = aml_if(aml_and(aml_equal(aml_object_type(pckg), + ifctx = aml_if(aml_land(aml_equal(aml_object_type(pckg), aml_int(4 /* Package */)) /* It is a Package? */, - aml_equal(aml_sizeof(pckg), aml_int(1)) /* 1 element? */, - NULL)); + aml_equal(aml_sizeof(pckg), aml_int(1)) /* 1 element? */)); pckg_index = aml_local(2); pckg_buf = aml_local(3); @@ -1101,7 +1102,7 @@ static void nvdimm_build_common_dsm(Aml *dev, * be treated as an integer. Moreover, the integer size depends on * DSDT tables revision number. If revision number is < 2, integer * size is 32 bits, otherwise it is 64 bits. - * Because of this CreateField() canot be used if RLEN < Integer Size. + * Because of this CreateField() cannot be used if RLEN < Integer Size. * * Also please note that APCI ASL operator SizeOf() doesn't support * Integer and there isn't any other way to figure out the Integer @@ -1247,6 +1248,7 @@ static void nvdimm_build_fit(Aml *dev) static void nvdimm_build_nvdimm_devices(Aml *root_dev, uint32_t ram_slots) { uint32_t slot; + Aml *method, *pkg, *field, *com_call; for (slot = 0; slot < ram_slots; slot++) { uint32_t handle = nvdimm_slot_to_handle(slot); @@ -1264,6 +1266,100 @@ static void nvdimm_build_nvdimm_devices(Aml *root_dev, uint32_t ram_slots) */ aml_append(nvdimm_dev, aml_name_decl("_ADR", aml_int(handle))); + /* + * ACPI v6.4: Section 6.5.10 NVDIMM Label Methods + */ + /* _LSI */ + method = aml_method("_LSI", 0, AML_SERIALIZED); + com_call = aml_call5(NVDIMM_COMMON_DSM, + aml_touuid(NVDIMM_DEVICE_DSM_UUID), + aml_int(1), aml_int(4), aml_int(0), + aml_int(handle)); + aml_append(method, aml_store(com_call, aml_local(0))); + + aml_append(method, aml_create_dword_field(aml_local(0), + aml_int(0), "STTS")); + aml_append(method, aml_create_dword_field(aml_local(0), aml_int(4), + "SLSA")); + aml_append(method, aml_create_dword_field(aml_local(0), aml_int(8), + "MAXT")); + + pkg = aml_package(3); + aml_append(pkg, aml_name("STTS")); + aml_append(pkg, aml_name("SLSA")); + aml_append(pkg, aml_name("MAXT")); + aml_append(method, aml_store(pkg, aml_local(1))); + aml_append(method, aml_return(aml_local(1))); + + aml_append(nvdimm_dev, method); + + /* _LSR */ + method = aml_method("_LSR", 2, AML_SERIALIZED); + aml_append(method, aml_name_decl("INPT", aml_buffer(8, NULL))); + + aml_append(method, aml_create_dword_field(aml_name("INPT"), + aml_int(0), "OFST")); + aml_append(method, aml_create_dword_field(aml_name("INPT"), + aml_int(4), "LEN")); + aml_append(method, aml_store(aml_arg(0), aml_name("OFST"))); + aml_append(method, aml_store(aml_arg(1), aml_name("LEN"))); + + pkg = aml_package(1); + aml_append(pkg, aml_name("INPT")); + aml_append(method, aml_store(pkg, aml_local(0))); + + com_call = aml_call5(NVDIMM_COMMON_DSM, + aml_touuid(NVDIMM_DEVICE_DSM_UUID), + aml_int(1), aml_int(5), aml_local(0), + aml_int(handle)); + aml_append(method, aml_store(com_call, aml_local(3))); + field = aml_create_dword_field(aml_local(3), aml_int(0), "STTS"); + aml_append(method, field); + field = aml_create_field(aml_local(3), aml_int(32), + aml_shiftleft(aml_name("LEN"), aml_int(3)), + "LDAT"); + aml_append(method, field); + aml_append(method, aml_name_decl("LSA", aml_buffer(0, NULL))); + aml_append(method, aml_to_buffer(aml_name("LDAT"), aml_name("LSA"))); + + pkg = aml_package(2); + aml_append(pkg, aml_name("STTS")); + aml_append(pkg, aml_name("LSA")); + + aml_append(method, aml_store(pkg, aml_local(1))); + aml_append(method, aml_return(aml_local(1))); + + aml_append(nvdimm_dev, method); + + /* _LSW */ + method = aml_method("_LSW", 3, AML_SERIALIZED); + aml_append(method, aml_store(aml_arg(2), aml_local(2))); + aml_append(method, aml_name_decl("INPT", aml_buffer(8, NULL))); + field = aml_create_dword_field(aml_name("INPT"), + aml_int(0), "OFST"); + aml_append(method, field); + field = aml_create_dword_field(aml_name("INPT"), + aml_int(4), "TLEN"); + aml_append(method, field); + aml_append(method, aml_store(aml_arg(0), aml_name("OFST"))); + aml_append(method, aml_store(aml_arg(1), aml_name("TLEN"))); + + aml_append(method, aml_concatenate(aml_name("INPT"), aml_local(2), + aml_name("INPT"))); + pkg = aml_package(1); + aml_append(pkg, aml_name("INPT")); + aml_append(method, aml_store(pkg, aml_local(0))); + com_call = aml_call5(NVDIMM_COMMON_DSM, + aml_touuid(NVDIMM_DEVICE_DSM_UUID), + aml_int(1), aml_int(6), aml_local(0), + aml_int(handle)); + aml_append(method, aml_store(com_call, aml_local(3))); + field = aml_create_dword_field(aml_local(3), aml_int(0), "STTS"); + aml_append(method, field); + aml_append(method, aml_return(aml_name("STTS"))); + + aml_append(nvdimm_dev, method); + nvdimm_build_device_dsm(nvdimm_dev, handle); aml_append(root_dev, nvdimm_dev); } diff --git a/hw/acpi/pci-bridge-stub.c b/hw/acpi/pci-bridge-stub.c new file mode 100644 index 0000000000..9d78638c48 --- /dev/null +++ b/hw/acpi/pci-bridge-stub.c @@ -0,0 +1,20 @@ +/* + * QEMU ACPI PCI bridge stub + * + * Copyright (c) 2023 Red Hat, Inc. + * + * Author: + * Igor Mammedov + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/acpi/pci.h" + +void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope) +{ +} diff --git a/hw/acpi/pci-bridge.c b/hw/acpi/pci-bridge.c new file mode 100644 index 0000000000..7baa7034a1 --- /dev/null +++ b/hw/acpi/pci-bridge.c @@ -0,0 +1,37 @@ +/* + * QEMU ACPI PCI bridge + * + * Copyright (c) 2023 Red Hat, Inc. + * + * Author: + * Igor Mammedov + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/acpi/pci.h" +#include "hw/pci/pci_bridge.h" +#include "hw/acpi/pcihp.h" + +void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope) +{ + PCIBridge *br = PCI_BRIDGE(adev); + + if (!DEVICE(br)->hotplugged) { + PCIBus *sec_bus = pci_bridge_get_sec_bus(br); + + build_append_pci_bus_devices(scope, sec_bus); + + /* + * generate hotplug slots descriptors if + * bridge has ACPI PCI hotplug attached, + */ + if (object_property_find(OBJECT(sec_bus), ACPI_PCIHP_PROP_BSEL)) { + build_append_pcihp_slots(scope, sec_bus); + } + } +} diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index bf65bbea49..5f79c9016b 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -54,21 +54,6 @@ typedef struct AcpiPciHpFind { PCIBus *bus; } AcpiPciHpFind; -static gint g_cmp_uint32(gconstpointer a, gconstpointer b, gpointer user_data) -{ - return a - b; -} - -static GSequence *pci_acpi_index_list(void) -{ - static GSequence *used_acpi_index_list; - - if (!used_acpi_index_list) { - used_acpi_index_list = g_sequence_new(NULL); - } - return used_acpi_index_list; -} - static int acpi_pcihp_get_bsel(PCIBus *bus) { Error *local_err = NULL; @@ -85,31 +70,40 @@ static int acpi_pcihp_get_bsel(PCIBus *bus) } } -/* Assign BSEL property to all buses. In the future, this can be changed - * to only assign to buses that support hotplug. - */ +typedef struct { + unsigned bsel_alloc; + bool has_bridge_hotplug; +} BSELInfo; + +/* Assign BSEL property only to buses that support hotplug. */ static void *acpi_set_bsel(PCIBus *bus, void *opaque) { - unsigned *bsel_alloc = opaque; + BSELInfo *info = opaque; unsigned *bus_bsel; + DeviceState *br = bus->qbus.parent; + bool is_bridge = IS_PCI_BRIDGE(br); + /* hotplugged bridges can't be described in ACPI ignore them */ if (qbus_is_hotpluggable(BUS(bus))) { - bus_bsel = g_malloc(sizeof *bus_bsel); + if (!is_bridge || (!br->hotplugged && info->has_bridge_hotplug)) { + bus_bsel = g_malloc(sizeof *bus_bsel); - *bus_bsel = (*bsel_alloc)++; - object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, - bus_bsel, OBJ_PROP_FLAG_READ); + *bus_bsel = info->bsel_alloc++; + object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, + bus_bsel, OBJ_PROP_FLAG_READ); + } } - return bsel_alloc; + return info; } -static void acpi_set_pci_info(void) +static void acpi_set_pci_info(bool has_bridge_hotplug) { static bool bsel_is_set; Object *host = acpi_get_i386_pci_host(); PCIBus *bus; - unsigned bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT; + BSELInfo info = { .bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT, + .has_bridge_hotplug = has_bridge_hotplug }; if (bsel_is_set) { return; @@ -123,24 +117,10 @@ static void acpi_set_pci_info(void) bus = PCI_HOST_BRIDGE(host)->bus; if (bus) { /* Scan all PCI buses. Set property to enable acpi based hotplug. */ - pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &bsel_alloc); + pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &info); } } -static void acpi_pcihp_disable_root_bus(void) -{ - Object *host = acpi_get_i386_pci_host(); - PCIBus *bus; - - bus = PCI_HOST_BRIDGE(host)->bus; - if (bus && qbus_is_hotpluggable(BUS(bus))) { - /* setting the hotplug handler to NULL makes the bus non-hotpluggable */ - qbus_set_hotplug_handler(BUS(bus), NULL); - } - - return; -} - static void acpi_pcihp_test_hotplug_bus(PCIBus *bus, void *opaque) { AcpiPciHpFind *find = opaque; @@ -186,14 +166,17 @@ static PCIBus *acpi_pcihp_find_hotplug_bus(AcpiPciHpState *s, int bsel) static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev) { - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); DeviceClass *dc = DEVICE_GET_CLASS(dev); /* * ACPI doesn't allow hotplug of bridge devices. Don't allow * hot-unplug of bridge devices unless they were added by hotplug * (and so, not described by acpi). + * + * Don't allow hot-unplug of SR-IOV Virtual Functions, as they + * will be removed implicitly, when Physical Function is unplugged. */ - return (pc->is_bridge && !dev->qdev.hotplugged) || !dc->hotpluggable; + return (IS_PCI_BRIDGE(dev) && !dev->qdev.hotplugged) || !dc->hotpluggable || + pci_is_vf(dev); } static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots) @@ -279,17 +262,12 @@ static void acpi_pcihp_update(AcpiPciHpState *s) } } -void acpi_pcihp_reset(AcpiPciHpState *s, bool acpihp_root_off) +void acpi_pcihp_reset(AcpiPciHpState *s) { - if (acpihp_root_off) { - acpi_pcihp_disable_root_bus(); - } - acpi_set_pci_info(); + acpi_set_pci_info(s->use_acpi_hotplug_bridge); acpi_pcihp_update(s); } -#define ONBOARD_INDEX_MAX (16 * 1024 - 1) - void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -302,34 +280,6 @@ void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev, ACPI_PCIHP_PROP_BSEL "' set"); return; } - - /* - * capped by systemd (see: udev-builtin-net_id.c) - * as it's the only known user honor it to avoid users - * misconfigure QEMU and then wonder why acpi-index doesn't work - */ - if (pdev->acpi_index > ONBOARD_INDEX_MAX) { - error_setg(errp, "acpi-index should be less or equal to %u", - ONBOARD_INDEX_MAX); - return; - } - - /* - * make sure that acpi-index is unique across all present PCI devices - */ - if (pdev->acpi_index) { - GSequence *used_indexes = pci_acpi_index_list(); - - if (g_sequence_lookup(used_indexes, GINT_TO_POINTER(pdev->acpi_index), - g_cmp_uint32, NULL)) { - error_setg(errp, "a PCI device with acpi-index = %" PRIu32 - " already exist", pdev->acpi_index); - return; - } - g_sequence_insert_sorted(used_indexes, - GINT_TO_POINTER(pdev->acpi_index), - g_cmp_uint32, NULL); - } } void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, @@ -349,17 +299,10 @@ void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, * Overwrite the default hotplug handler with the ACPI PCI one * for cold plugged bridges only. */ - if (!s->legacy_piix && + if (s->use_acpi_hotplug_bridge && object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) { PCIBus *sec = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev)); - /* Remove all hot-plug handlers if hot-plug is disabled on slot */ - if (object_dynamic_cast(OBJECT(dev), TYPE_PCIE_SLOT) && - !PCIE_SLOT(pdev)->hotplug) { - qbus_set_hotplug_handler(BUS(sec), NULL); - return; - } - qbus_set_hotplug_handler(BUS(sec), OBJECT(hotplug_dev)); /* We don't have to overwrite any other hotplug handler yet */ assert(QLIST_EMPTY(&sec->child)); @@ -389,17 +332,6 @@ void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, trace_acpi_pci_unplug(PCI_SLOT(pdev->devfn), acpi_pcihp_get_bsel(pci_get_bus(pdev))); - /* - * clean up acpi-index so it could reused by another device - */ - if (pdev->acpi_index) { - GSequence *used_indexes = pci_acpi_index_list(); - - g_sequence_remove(g_sequence_lookup(used_indexes, - GINT_TO_POINTER(pdev->acpi_index), - g_cmp_uint32, NULL)); - } - qdev_unrealize(dev); } @@ -425,10 +357,38 @@ void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, * acpi_pcihp_eject_slot() when the operation is completed. */ pdev->qdev.pending_deleted_event = true; + /* if unplug was requested before OSPM is initialized, + * linux kernel will clear GPE0.sts[] bits during boot, which effectively + * hides unplug event. And than followup qmp_device_del() calls remain + * blocked by above flag permanently. + * Unblock qmp_device_del() by setting expire limit, so user can + * repeat unplug request later when OSPM has been booted. + */ + pdev->qdev.pending_deleted_expires_ms = + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); /* 1 msec */ + s->acpi_pcihp_pci_status[bsel].down |= (1U << slot); acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS); } +bool acpi_pcihp_is_hotpluggbale_bus(AcpiPciHpState *s, BusState *bus) +{ + Object *o = OBJECT(bus->parent); + + if (s->use_acpi_hotplug_bridge && + object_dynamic_cast(o, TYPE_PCI_BRIDGE)) { + if (object_dynamic_cast(o, TYPE_PCIE_SLOT) && !PCIE_SLOT(o)->hotplug) { + return false; + } + return true; + } + + if (s->use_acpi_root_pci_hotplug) { + return true; + } + return false; +} + static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size) { AcpiPciHpState *s = opaque; @@ -442,7 +402,7 @@ static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size) switch (addr) { case PCI_UP_BASE: val = s->acpi_pcihp_pci_status[bsel].up; - if (!s->legacy_piix) { + if (s->use_acpi_hotplug_bridge) { s->acpi_pcihp_pci_status[bsel].up = 0; } trace_acpi_pci_up_read(val); @@ -517,7 +477,8 @@ static void pci_write(void *opaque, hwaddr addr, uint64_t data, trace_acpi_pci_ej_write(addr, data); break; case PCI_SEL_BASE: - s->hotplug_select = s->legacy_piix ? ACPI_PCIHP_BSEL_DEFAULT : data; + s->hotplug_select = s->use_acpi_hotplug_bridge ? data : + ACPI_PCIHP_BSEL_DEFAULT; trace_acpi_pci_sel_write(addr, data); default: break; @@ -535,18 +496,16 @@ static const MemoryRegionOps acpi_pcihp_io_ops = { }; void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, - MemoryRegion *address_space_io, bool bridges_enabled, - uint16_t io_base) + MemoryRegion *io, uint16_t io_base) { s->io_len = ACPI_PCIHP_SIZE; s->io_base = io_base; s->root = root_bus; - s->legacy_piix = !bridges_enabled; memory_region_init_io(&s->io, owner, &acpi_pcihp_io_ops, s, "acpi-pci-hotplug", s->io_len); - memory_region_add_subregion(address_space_io, s->io_base, &s->io); + memory_region_add_subregion(io, s->io_base, &s->io); object_property_add_uint16_ptr(owner, ACPI_PCIHP_IO_BASE_PROP, &s->io_base, OBJ_PROP_FLAG_READ); @@ -558,7 +517,7 @@ const VMStateDescription vmstate_acpi_pcihp_pci_status = { .name = "acpi_pcihp_pci_status", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(up, AcpiPciHpPciStatus), VMSTATE_UINT32(down, AcpiPciHpPciStatus), VMSTATE_END_OF_LIST() diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index fe5625d07a..debe1adb84 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -20,20 +20,19 @@ */ #include "qemu/osdep.h" -#include "hw/i386/pc.h" -#include "hw/southbridge/piix.h" #include "hw/irq.h" #include "hw/isa/apm.h" #include "hw/i2c/pm_smbus.h" #include "hw/pci/pci.h" #include "hw/qdev-properties.h" #include "hw/acpi/acpi.h" +#include "hw/acpi/pcihp.h" +#include "hw/acpi/piix4.h" #include "sysemu/runstate.h" #include "sysemu/sysemu.h" #include "sysemu/xen.h" #include "qapi/error.h" #include "qemu/range.h" -#include "hw/acpi/pcihp.h" #include "hw/acpi/cpu_hotplug.h" #include "hw/acpi/cpu.h" #include "hw/hotplug.h" @@ -43,7 +42,6 @@ #include "hw/acpi/acpi_dev_interface.h" #include "migration/vmstate.h" #include "hw/core/cpu.h" -#include "trace.h" #include "qom/object.h" #define GPE_BASE 0xafe0 @@ -56,47 +54,6 @@ struct pci_status { uint32_t down; }; -struct PIIX4PMState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - MemoryRegion io; - uint32_t io_base; - - MemoryRegion io_gpe; - ACPIREGS ar; - - APMState apm; - - PMSMBus smb; - uint32_t smb_io_base; - - qemu_irq irq; - qemu_irq smi_irq; - int smm_enabled; - bool smm_compat; - Notifier machine_ready; - Notifier powerdown_notifier; - - AcpiPciHpState acpi_pci_hotplug; - bool use_acpi_hotplug_bridge; - bool use_acpi_root_pci_hotplug; - bool not_migrate_acpi_index; - - uint8_t disable_s3; - uint8_t disable_s4; - uint8_t s4_val; - - bool cpu_hotplug_legacy; - AcpiCpuHotplug gpe_cpu; - CPUHotplugState cpuhp_state; - - MemHotplugState acpi_memory_hotplug; -}; - -OBJECT_DECLARE_SIMPLE_TYPE(PIIX4PMState, PIIX4_PM) - static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, PCIBus *bus, PIIX4PMState *s); @@ -190,7 +147,7 @@ static const VMStateDescription vmstate_gpe = { .name = "gpe", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_GPE_ARRAY(sts, ACPIGPE), VMSTATE_GPE_ARRAY(en, ACPIGPE), VMSTATE_END_OF_LIST() @@ -201,7 +158,7 @@ static const VMStateDescription vmstate_pci_status = { .name = "pci_status", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(up, struct AcpiPciHpPciStatus), VMSTATE_UINT32(down, struct AcpiPciHpPciStatus), VMSTATE_END_OF_LIST() @@ -211,14 +168,14 @@ static const VMStateDescription vmstate_pci_status = { static bool vmstate_test_use_acpi_hotplug_bridge(void *opaque, int version_id) { PIIX4PMState *s = opaque; - return s->use_acpi_hotplug_bridge; + return s->acpi_pci_hotplug.use_acpi_hotplug_bridge; } static bool vmstate_test_no_use_acpi_hotplug_bridge(void *opaque, int version_id) { PIIX4PMState *s = opaque; - return !s->use_acpi_hotplug_bridge; + return !s->acpi_pci_hotplug.use_acpi_hotplug_bridge; } static bool vmstate_test_use_memhp(void *opaque) @@ -232,7 +189,7 @@ static const VMStateDescription vmstate_memhp_state = { .version_id = 1, .minimum_version_id = 1, .needed = vmstate_test_use_memhp, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, PIIX4PMState), VMSTATE_END_OF_LIST() } @@ -257,7 +214,7 @@ static const VMStateDescription vmstate_cpuhp_state = { .minimum_version_id = 1, .needed = vmstate_test_use_cpuhp, .pre_load = vmstate_cpuhp_pre_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CPU_HOTPLUG(cpuhp_state, PIIX4PMState), VMSTATE_END_OF_LIST() } @@ -275,7 +232,8 @@ static bool piix4_vmstate_need_smbus(void *opaque, int version_id) static bool vmstate_test_migrate_acpi_index(void *opaque, int version_id) { PIIX4PMState *s = PIIX4_PM(opaque); - return s->use_acpi_hotplug_bridge && !s->not_migrate_acpi_index; + return s->acpi_pci_hotplug.use_acpi_hotplug_bridge && + !s->not_migrate_acpi_index; } /* qemu-kvm 1.2 uses version 3 but advertised as 2 @@ -289,7 +247,7 @@ static const VMStateDescription vmstate_acpi = { .version_id = 3, .minimum_version_id = 3, .post_load = vmstate_acpi_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PIIX4PMState), VMSTATE_UINT16(ar.pm1.evt.sts, PIIX4PMState), VMSTATE_UINT16(ar.pm1.evt.en, PIIX4PMState), @@ -311,7 +269,7 @@ static const VMStateDescription vmstate_acpi = { vmstate_test_migrate_acpi_index), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_memhp_state, &vmstate_cpuhp_state, NULL @@ -344,7 +302,10 @@ static void piix4_pm_reset(DeviceState *dev) acpi_update_sci(&s->ar, s->irq); pm_io_space_update(s); - acpi_pcihp_reset(&s->acpi_pci_hotplug, !s->use_acpi_root_pci_hotplug); + if (s->acpi_pci_hotplug.use_acpi_hotplug_bridge || + s->acpi_pci_hotplug.use_acpi_root_pci_hotplug) { + acpi_pcihp_reset(&s->acpi_pci_hotplug); + } } static void piix4_pm_powerdown_req(Notifier *n, void *opaque) @@ -441,6 +402,13 @@ static void piix4_device_unplug_cb(HotplugHandler *hotplug_dev, } } +static bool piix4_is_hotpluggable_bus(HotplugHandler *hotplug_dev, + BusState *bus) +{ + PIIX4PMState *s = PIIX4_PM(hotplug_dev); + return acpi_pcihp_is_hotpluggbale_bus(&s->acpi_pci_hotplug, bus); +} + static void piix4_pm_machine_ready(Notifier *n, void *opaque) { PIIX4PMState *s = container_of(n, PIIX4PMState, machine_ready); @@ -525,39 +493,22 @@ static void piix4_pm_realize(PCIDevice *dev, Error **errp) s->machine_ready.notify = piix4_pm_machine_ready; qemu_add_machine_init_done_notifier(&s->machine_ready); + if (xen_enabled()) { + s->acpi_pci_hotplug.use_acpi_hotplug_bridge = false; + } + piix4_acpi_system_hot_add_init(pci_address_space_io(dev), pci_get_bus(dev), s); - qbus_set_hotplug_handler(BUS(pci_get_bus(dev)), OBJECT(s)); piix4_pm_add_properties(s); } -I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, - qemu_irq sci_irq, qemu_irq smi_irq, - int smm_enabled, DeviceState **piix4_pm) +static void piix4_pm_init(Object *obj) { - PCIDevice *pci_dev; - DeviceState *dev; - PIIX4PMState *s; + PIIX4PMState *s = PIIX4_PM(obj); - pci_dev = pci_new(devfn, TYPE_PIIX4_PM); - dev = DEVICE(pci_dev); - qdev_prop_set_uint32(dev, "smb_io_base", smb_io_base); - if (piix4_pm) { - *piix4_pm = dev; - } - - s = PIIX4_PM(dev); - s->irq = sci_irq; - s->smi_irq = smi_irq; - s->smm_enabled = smm_enabled; - if (xen_enabled()) { - s->use_acpi_hotplug_bridge = false; - } - - pci_realize_and_unref(pci_dev, bus, &error_fatal); - - return s->smb.smbus; + qdev_init_gpio_out(DEVICE(obj), &s->irq, 1); + qdev_init_gpio_out_named(DEVICE(obj), &s->smi_irq, "smi-irq", 1); } static uint64_t gpe_readb(void *opaque, hwaddr addr, unsigned width) @@ -565,7 +516,6 @@ static uint64_t gpe_readb(void *opaque, hwaddr addr, unsigned width) PIIX4PMState *s = opaque; uint32_t val = acpi_gpe_ioport_readb(&s->ar, addr); - trace_piix4_gpe_readb(addr, width, val); return val; } @@ -574,7 +524,6 @@ static void gpe_writeb(void *opaque, hwaddr addr, uint64_t val, { PIIX4PMState *s = opaque; - trace_piix4_gpe_writeb(addr, width, val); acpi_gpe_ioport_writeb(&s->ar, addr, val); acpi_update_sci(&s->ar, s->irq); } @@ -616,9 +565,11 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, "acpi-gpe0", GPE_LEN); memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe); - if (s->use_acpi_hotplug_bridge || s->use_acpi_root_pci_hotplug) { + if (s->acpi_pci_hotplug.use_acpi_hotplug_bridge || + s->acpi_pci_hotplug.use_acpi_root_pci_hotplug) { acpi_pcihp_init(OBJECT(s), &s->acpi_pci_hotplug, bus, parent, - s->use_acpi_hotplug_bridge, ACPI_PCIHP_ADDR_PIIX4); + ACPI_PCIHP_ADDR_PIIX4); + qbus_set_hotplug_handler(BUS(pci_get_bus(PCI_DEVICE(s))), OBJECT(s)); } s->cpu_hotplug_legacy = true; @@ -657,12 +608,13 @@ static Property piix4_pm_properties[] = { DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0), DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2), DEFINE_PROP_BOOL(ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, PIIX4PMState, - use_acpi_hotplug_bridge, true), + acpi_pci_hotplug.use_acpi_hotplug_bridge, true), DEFINE_PROP_BOOL(ACPI_PM_PROP_ACPI_PCI_ROOTHP, PIIX4PMState, - use_acpi_root_pci_hotplug, true), + acpi_pci_hotplug.use_acpi_root_pci_hotplug, true), DEFINE_PROP_BOOL("memory-hotplug-support", PIIX4PMState, acpi_memory_hotplug.is_enabled, true), DEFINE_PROP_BOOL("smm-compat", PIIX4PMState, smm_compat, false), + DEFINE_PROP_BOOL("smm-enabled", PIIX4PMState, smm_enabled, false), DEFINE_PROP_BOOL("x-not-migrate-acpi-index", PIIX4PMState, not_migrate_acpi_index, false), DEFINE_PROP_END_OF_LIST(), @@ -695,14 +647,15 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data) hc->plug = piix4_device_plug_cb; hc->unplug_request = piix4_device_unplug_request_cb; hc->unplug = piix4_device_unplug_cb; + hc->is_hotpluggable_bus = piix4_is_hotpluggable_bus; adevc->ospm_status = piix4_ospm_status; adevc->send_event = piix4_send_gpe; - adevc->madt_cpu = pc_madt_cpu_entry; } static const TypeInfo piix4_pm_info = { .name = TYPE_PIIX4_PM, .parent = TYPE_PCI_DEVICE, + .instance_init = piix4_pm_init, .instance_size = sizeof(PIIX4PMState), .class_init = piix4_pm_class_init, .interfaces = (InterfaceInfo[]) { diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index 2250126a22..edc93e703c 100644 --- a/hw/acpi/trace-events +++ b/hw/acpi/trace-events @@ -17,6 +17,12 @@ mhp_acpi_clear_remove_evt(uint32_t slot) "slot[0x%"PRIx32"] clear remove event" mhp_acpi_pc_dimm_deleted(uint32_t slot) "slot[0x%"PRIx32"] pc-dimm deleted" mhp_acpi_pc_dimm_delete_failed(uint32_t slot) "slot[0x%"PRIx32"] pc-dimm delete failed" +# core.c +acpi_gpe_en_ioport_readb(uint32_t addr, uint8_t val) "addr: 0x%" PRIx32 " ==> 0x%02" PRIx8 +acpi_gpe_en_ioport_writeb(uint32_t addr, uint8_t val) "addr: 0x%" PRIx32 " <== 0x%02" PRIx8 +acpi_gpe_sts_ioport_readb(uint32_t addr, uint8_t val) "addr: 0x%" PRIx32 " ==> 0x%02" PRIx8 +acpi_gpe_sts_ioport_writeb(uint32_t addr, uint8_t val) "addr: 0x%" PRIx32 " <== 0x%02" PRIx8 + # cpu.c cpuhp_acpi_invalid_idx_selected(uint32_t idx) "0x%"PRIx32 cpuhp_acpi_read_flags(uint32_t idx, uint8_t flags) "idx[0x%"PRIx32"] flags: 0x%"PRIx8 @@ -48,13 +54,11 @@ acpi_pci_sel_read(uint32_t val) "%" PRIu32 acpi_pci_ej_write(uint64_t addr, uint64_t data) "0x%" PRIx64 " <== %" PRIu64 acpi_pci_sel_write(uint64_t addr, uint64_t data) "0x%" PRIx64 " <== %" PRIu64 -# piix4.c -piix4_gpe_readb(uint64_t addr, unsigned width, uint64_t val) "addr: 0x%" PRIx64 " width: %d ==> 0x%" PRIx64 -piix4_gpe_writeb(uint64_t addr, unsigned width, uint64_t val) "addr: 0x%" PRIx64 " width: %d <== 0x%" PRIx64 - # tco.c tco_timer_reload(int ticks, int msec) "ticks=%d (%d ms)" tco_timer_expired(int timeouts_no, bool strap, bool no_reboot) "timeouts_no=%d no_reboot=%d/%d" +tco_io_write(uint64_t addr, uint32_t val) "addr=0x%" PRIx64 " val=0x%" PRIx32 +tco_io_read(uint64_t addr, uint32_t val) "addr=0x%" PRIx64 " val=0x%" PRIx32 # erst.c acpi_erst_reg_write(uint64_t addr, uint64_t val, unsigned size) "addr: 0x%04" PRIx64 " <== 0x%016" PRIx64 " (size: %u)" @@ -70,3 +74,16 @@ acpi_erst_reset_out(unsigned record_count) "record_count %u" acpi_erst_post_load(void *header, unsigned slot_size) "header: 0x%p slot_size %u" acpi_erst_class_init_in(void) acpi_erst_class_init_out(void) + +# nvdimm.c +acpi_nvdimm_read_fit(uint32_t offset, uint32_t len, const char *dirty) "Read FIT: offset 0x%" PRIx32 " FIT size 0x%" PRIx32 " Dirty %s" +acpi_nvdimm_label_info(uint32_t label_size, uint32_t mxfer) "label_size 0x%" PRIx32 ", max_xfer 0x%" PRIx32 +acpi_nvdimm_label_overflow(uint32_t offset, uint32_t length) "offset 0x%" PRIx32 " + length 0x%" PRIx32 " is overflow" +acpi_nvdimm_label_oversize(uint32_t pos, uint64_t size) "position 0x%" PRIx32 " is beyond label data (len = %" PRIu64 ")" +acpi_nvdimm_label_xfer_exceed(uint32_t length, uint32_t max_xfer) "length (0x%" PRIx32 ") is larger than max_xfer (0x%" PRIx32 ")" +acpi_nvdimm_read_label(uint32_t offset, uint32_t length) "Read Label Data: offset 0x%" PRIx32 " length 0x%" PRIx32 +acpi_nvdimm_write_label(uint32_t offset, uint32_t length) "Write Label Data: offset 0x%" PRIx32 " length 0x%" PRIx32 +acpi_nvdimm_read_io_port(void) "Alert: we never read _DSM IO Port" +acpi_nvdimm_dsm_mem_addr(uint64_t dsm_mem_addr) "dsm memory address 0x%" PRIx64 +acpi_nvdimm_dsm_info(uint32_t revision, uint32_t handle, uint32_t function) "Revision 0x%" PRIx32 " Handle 0x%" PRIx32 " Function 0x%" PRIx32 +acpi_nvdimm_invalid_revision(uint32_t revision) "Revision 0x%" PRIx32 " is not supported, expect 0x1" diff --git a/hw/acpi/viot.c b/hw/acpi/viot.c index c1af75206e..4e0bf69067 100644 --- a/hw/acpi/viot.c +++ b/hw/acpi/viot.c @@ -10,17 +10,40 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" -struct viot_pci_ranges { - GArray *blob; - size_t count; - uint16_t output_node; +struct viot_pci_host_range { + int min_bus; + int max_bus; }; -/* Build PCI range for a given PCI host bridge */ -static int build_pci_range_node(Object *obj, void *opaque) +static void build_pci_host_range(GArray *table_data, int min_bus, int max_bus, + uint16_t output_node) { - struct viot_pci_ranges *pci_ranges = opaque; - GArray *blob = pci_ranges->blob; + /* Type */ + build_append_int_noprefix(table_data, 1 /* PCI range */, 1); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 1); + /* Length */ + build_append_int_noprefix(table_data, 24, 2); + /* Endpoint start */ + build_append_int_noprefix(table_data, PCI_BUILD_BDF(min_bus, 0), 4); + /* PCI Segment start */ + build_append_int_noprefix(table_data, 0, 2); + /* PCI Segment end */ + build_append_int_noprefix(table_data, 0, 2); + /* PCI BDF start */ + build_append_int_noprefix(table_data, PCI_BUILD_BDF(min_bus, 0), 2); + /* PCI BDF end */ + build_append_int_noprefix(table_data, PCI_BUILD_BDF(max_bus, 0xff), 2); + /* Output node */ + build_append_int_noprefix(table_data, output_node, 2); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 6); +} + +/* Build PCI range for a given PCI host bridge */ +static int enumerate_pci_host_bridges(Object *obj, void *opaque) +{ + GArray *pci_host_ranges = opaque; if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) { PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus; @@ -30,34 +53,31 @@ static int build_pci_range_node(Object *obj, void *opaque) pci_bus_range(bus, &min_bus, &max_bus); - /* Type */ - build_append_int_noprefix(blob, 1 /* PCI range */, 1); - /* Reserved */ - build_append_int_noprefix(blob, 0, 1); - /* Length */ - build_append_int_noprefix(blob, 24, 2); - /* Endpoint start */ - build_append_int_noprefix(blob, PCI_BUILD_BDF(min_bus, 0), 4); - /* PCI Segment start */ - build_append_int_noprefix(blob, 0, 2); - /* PCI Segment end */ - build_append_int_noprefix(blob, 0, 2); - /* PCI BDF start */ - build_append_int_noprefix(blob, PCI_BUILD_BDF(min_bus, 0), 2); - /* PCI BDF end */ - build_append_int_noprefix(blob, PCI_BUILD_BDF(max_bus, 0xff), 2); - /* Output node */ - build_append_int_noprefix(blob, pci_ranges->output_node, 2); - /* Reserved */ - build_append_int_noprefix(blob, 0, 6); - - pci_ranges->count++; + const struct viot_pci_host_range pci_host_range = { + .min_bus = min_bus, + .max_bus = max_bus, + }; + g_array_append_val(pci_host_ranges, pci_host_range); } } return 0; } +static gint pci_host_range_compare(gconstpointer a, gconstpointer b) +{ + struct viot_pci_host_range *range_a = (struct viot_pci_host_range *)a; + struct viot_pci_host_range *range_b = (struct viot_pci_host_range *)b; + + if (range_a->min_bus < range_b->min_bus) { + return -1; + } else if (range_a->min_bus > range_b->min_bus) { + return 1; + } else { + return 0; + } +} + /* * Generate a VIOT table with one PCI-based virtio-iommu that manages PCI * endpoints. @@ -72,19 +92,22 @@ void build_viot(MachineState *ms, GArray *table_data, BIOSLinker *linker, int viommu_off = 48; AcpiTable table = { .sig = "VIOT", .rev = 0, .oem_id = oem_id, .oem_table_id = oem_table_id }; - struct viot_pci_ranges pci_ranges = { - .output_node = viommu_off, - .blob = g_array_new(false, true /* clear */, 1), - }; + GArray *pci_host_ranges = g_array_new(false, true, + sizeof(struct viot_pci_host_range)); + struct viot_pci_host_range *pci_host_range; + int i; /* Build the list of PCI ranges that this viommu manages */ - object_child_foreach_recursive(OBJECT(ms), build_pci_range_node, - &pci_ranges); + object_child_foreach_recursive(OBJECT(ms), enumerate_pci_host_bridges, + pci_host_ranges); + + /* Sort the pci host ranges by min_bus */ + g_array_sort(pci_host_ranges, pci_host_range_compare); /* ACPI table header */ acpi_table_begin(&table, table_data); /* Node count */ - build_append_int_noprefix(table_data, pci_ranges.count + 1, 2); + build_append_int_noprefix(table_data, pci_host_ranges->len + 1, 2); /* Node offset */ build_append_int_noprefix(table_data, viommu_off, 2); /* Reserved */ @@ -105,9 +128,15 @@ void build_viot(MachineState *ms, GArray *table_data, BIOSLinker *linker, build_append_int_noprefix(table_data, 0, 8); /* PCI ranges found above */ - g_array_append_vals(table_data, pci_ranges.blob->data, - pci_ranges.blob->len); - g_array_free(pci_ranges.blob, true); + for (i = 0; i < pci_host_ranges->len; i++) { + pci_host_range = &g_array_index(pci_host_ranges, + struct viot_pci_host_range, i); + + build_pci_host_range(table_data, pci_host_range->min_bus, + pci_host_range->max_bus, viommu_off); + } + + g_array_free(pci_host_ranges, true); acpi_table_end(linker, &table); } diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c index 0c9f158ac9..e63c8af4c3 100644 --- a/hw/acpi/vmgenid.c +++ b/hw/acpi/vmgenid.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qapi-commands-machine.h" #include "qemu/module.h" #include "hw/acpi/acpi.h" #include "hw/acpi/aml-build.h" @@ -179,7 +178,7 @@ static const VMStateDescription vmstate_vmgenid = { .version_id = 1, .minimum_version_id = 1, .post_load = vmgenid_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(vmgenid_addr_le, VmGenIdState, sizeof(uint64_t)), VMSTATE_END_OF_LIST() }, @@ -244,20 +243,3 @@ static void vmgenid_register_types(void) } type_init(vmgenid_register_types) - -GuidInfo *qmp_query_vm_generation_id(Error **errp) -{ - GuidInfo *info; - VmGenIdState *vms; - Object *obj = find_vmgenid_dev(); - - if (!obj) { - error_setg(errp, "VM Generation ID device not found"); - return NULL; - } - vms = VMGENID(obj); - - info = g_malloc0(sizeof(*info)); - info->guid = qemu_uuid_unparse_strdup(&vms->guid); - return info; -} diff --git a/hw/adc/aspeed_adc.c b/hw/adc/aspeed_adc.c index 0d29663129..68bdbc73b0 100644 --- a/hw/adc/aspeed_adc.c +++ b/hw/adc/aspeed_adc.c @@ -280,7 +280,7 @@ static const VMStateDescription vmstate_aspeed_adc_engine = { .name = TYPE_ASPEED_ADC, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedADCEngineState, ASPEED_ADC_NR_REGS), VMSTATE_END_OF_LIST(), } diff --git a/hw/adc/max111x.c b/hw/adc/max111x.c index e8bf4cccd4..957d177e1c 100644 --- a/hw/adc/max111x.c +++ b/hw/adc/max111x.c @@ -96,7 +96,7 @@ static const VMStateDescription vmstate_max111x = { .name = "max111x", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_SSI_PERIPHERAL(parent_obj, MAX111xState), VMSTATE_UINT8(tb1, MAX111xState), VMSTATE_UINT8(rb2, MAX111xState), diff --git a/hw/adc/meson.build b/hw/adc/meson.build index b29ac7ccdf..a4f85b7d46 100644 --- a/hw/adc/meson.build +++ b/hw/adc/meson.build @@ -1,5 +1,5 @@ -softmmu_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_adc.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c')) -softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c')) -softmmu_ss.add(when: 'CONFIG_MAX111X', if_true: files('max111x.c')) +system_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_adc.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c')) +system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c')) +system_ss.add(when: 'CONFIG_MAX111X', if_true: files('max111x.c')) diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c index 0f0a9f63e2..c6647eec6d 100644 --- a/hw/adc/npcm7xx_adc.c +++ b/hw/adc/npcm7xx_adc.c @@ -36,7 +36,7 @@ REG32(NPCM7XX_ADC_DATA, 0x4) #define NPCM7XX_ADC_CON_INT BIT(18) #define NPCM7XX_ADC_CON_EN BIT(17) #define NPCM7XX_ADC_CON_RST BIT(16) -#define NPCM7XX_ADC_CON_CONV BIT(14) +#define NPCM7XX_ADC_CON_CONV BIT(13) #define NPCM7XX_ADC_CON_DIV(rv) extract32(rv, 1, 8) #define NPCM7XX_ADC_MAX_RESULT 1023 @@ -242,7 +242,7 @@ static void npcm7xx_adc_init(Object *obj) for (i = 0; i < NPCM7XX_ADC_NUM_INPUTS; ++i) { object_property_add_uint32_ptr(obj, "adci[*]", - &s->adci[i], OBJ_PROP_FLAG_WRITE); + &s->adci[i], OBJ_PROP_FLAG_READWRITE); } object_property_add_uint32_ptr(obj, "vref", &s->vref, OBJ_PROP_FLAG_WRITE); @@ -253,7 +253,7 @@ static const VMStateDescription vmstate_npcm7xx_adc = { .name = "npcm7xx-adc", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER(conv_timer, NPCM7xxADCState), VMSTATE_UINT32(con, NPCM7xxADCState), VMSTATE_UINT32(data, NPCM7xxADCState), diff --git a/hw/adc/stm32f2xx_adc.c b/hw/adc/stm32f2xx_adc.c index 01a0b14e69..e9df6ea53f 100644 --- a/hw/adc/stm32f2xx_adc.c +++ b/hw/adc/stm32f2xx_adc.c @@ -254,7 +254,7 @@ static const VMStateDescription vmstate_stm32f2xx_adc = { .name = TYPE_STM32F2XX_ADC, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(adc_sr, STM32F2XXADCState), VMSTATE_UINT32(adc_cr1, STM32F2XXADCState), VMSTATE_UINT32(adc_cr2, STM32F2XXADCState), diff --git a/hw/adc/zynq-xadc.c b/hw/adc/zynq-xadc.c index 032e19cbd0..34268319a4 100644 --- a/hw/adc/zynq-xadc.c +++ b/hw/adc/zynq-xadc.c @@ -269,7 +269,7 @@ static const VMStateDescription vmstate_zynq_xadc = { .name = "zynq-xadc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, ZynqXADCState, ZYNQ_XADC_NUM_IO_REGS), VMSTATE_UINT16_ARRAY(xadc_regs, ZynqXADCState, ZYNQ_XADC_NUM_ADC_REGS), diff --git a/hw/alpha/alpha_sys.h b/hw/alpha/alpha_sys.h index 2263e821da..a303c58438 100644 --- a/hw/alpha/alpha_sys.h +++ b/hw/alpha/alpha_sys.h @@ -5,7 +5,6 @@ #include "target/alpha/cpu-qom.h" #include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" #include "hw/boards.h" #include "hw/intc/i8259.h" diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index f4349eba83..52a1fa310b 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -18,9 +18,6 @@ #include "net/net.h" #include "qemu/cutils.h" #include "qemu/datadir.h" -#include "net/net.h" - -#define MAX_IDE_BUS 2 static uint64_t cpu_alpha_superpage_to_phys(void *opaque, uint64_t addr) { @@ -52,6 +49,7 @@ static void clipper_init(MachineState *machine) const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; + MachineClass *mc = MACHINE_GET_CLASS(machine); AlphaCPU *cpus[4]; PCIBus *pci_bus; PCIDevice *pci_dev; @@ -126,9 +124,7 @@ static void clipper_init(MachineState *machine) pci_vga_init(pci_bus); /* Network setup. e1000 is good enough, failing Tulip support. */ - for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci_bus, "e1000", NULL); - } + pci_init_nic_devices(pci_bus, mc->default_nic); /* Super I/O */ isa_create_simple(isa_bus, TYPE_SMC37C669_SUPERIO); @@ -216,6 +212,7 @@ static void clipper_machine_init(MachineClass *mc) mc->is_default = true; mc->default_cpu_type = ALPHA_CPU_TYPE_NAME("ev67"); mc->default_ram_id = "ram"; + mc->default_nic = "e1000"; } DEFINE_MACHINE("clipper", clipper_machine_init) diff --git a/hw/alpha/pci.c b/hw/alpha/pci.c index 72251fcdf0..7c18297177 100644 --- a/hw/alpha/pci.c +++ b/hw/alpha/pci.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "hw/pci/pci_host.h" #include "alpha_sys.h" #include "qemu/log.h" #include "trace.h" diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index bd39c8ca86..e8711ae16a 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -10,10 +10,10 @@ #include "qemu/module.h" #include "qemu/units.h" #include "qapi/error.h" +#include "hw/pci/pci_host.h" #include "cpu.h" #include "hw/irq.h" #include "alpha_sys.h" -#include "qom/object.h" #define TYPE_TYPHOON_PCI_HOST_BRIDGE "typhoon-pcihost" @@ -738,6 +738,10 @@ static AddressSpace *typhoon_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &s->pchip.iommu_as; } +static const PCIIOMMUOps typhoon_iommu_ops = { + .get_address_space = typhoon_pci_dma_iommu, +}; + static void typhoon_set_irq(void *opaque, int irq, int level) { TyphoonState *s = opaque; @@ -897,7 +901,7 @@ PCIBus *typhoon_init(MemoryRegion *ram, qemu_irq *p_isa_irq, "iommu-typhoon", UINT64_MAX); address_space_init(&s->pchip.iommu_as, MEMORY_REGION(&s->pchip.iommu), "pchip0-pci"); - pci_setup_iommu(b, typhoon_pci_dma_iommu, s); + pci_setup_iommu(b, &typhoon_iommu_ops, s); /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ memory_region_init_io(&s->pchip.reg_iack, OBJECT(s), &alpha_pci_iack_ops, diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 219262a8da..893a7bff66 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -6,7 +6,9 @@ config ARM_VIRT imply VFIO_PLATFORM imply VFIO_XGMAC imply TPM_TIS_SYSBUS + imply TPM_TIS_I2C imply NVDIMM + imply IOMMUFD select ARM_GIC select ACPI select ARM_SMMUV3 @@ -30,23 +32,32 @@ config ARM_VIRT select ACPI_VIOT select VIRTIO_MEM_SUPPORTED select ACPI_CXL + select ACPI_HMAT config CHEETAH bool + default y + depends on TCG && ARM select OMAP select TSC210X config CUBIEBOARD bool + default y + depends on TCG && ARM select ALLWINNER_A10 config DIGIC bool + default y + depends on TCG && ARM select PTIMER select PFLASH_CFI02 config EXYNOS4 bool + default y + depends on TCG && ARM imply I2C_DEVICES select A9MPCORE select I2C @@ -59,6 +70,8 @@ config EXYNOS4 config HIGHBANK bool + default y + depends on TCG && ARM select A9MPCORE select A15MPCORE select AHCI @@ -73,6 +86,8 @@ config HIGHBANK config INTEGRATOR bool + default y + depends on TCG && ARM select ARM_TIMER select INTEGRATOR_DEBUG select PL011 # UART @@ -85,12 +100,21 @@ config INTEGRATOR config MAINSTONE bool + default y + depends on TCG && ARM select PXA2XX select PFLASH_CFI01 select SMC91C111 +config MPS3R + bool + default y + depends on TCG && ARM + config MUSCA bool + default y + depends on TCG && ARM select ARMSSE select PL011 select PL031 @@ -102,6 +126,8 @@ config MARVELL_88W8618 config MUSICPAL bool + default y + depends on TCG && ARM select OR_IRQ select BITBANG_I2C select MARVELL_88W8618 @@ -112,16 +138,28 @@ config MUSICPAL config NETDUINO2 bool + default y + depends on TCG && ARM select STM32F205_SOC config NETDUINOPLUS2 bool + default y + depends on TCG && ARM + select STM32F405_SOC + +config OLIMEX_STM32_H405 + bool + default y + depends on TCG && ARM select STM32F405_SOC config NSERIES bool + default y + depends on TCG && ARM select OMAP - select TMP105 # tempature sensor + select TMP105 # temperature sensor select BLIZZARD # LCD/TV controller select ONENAND select TSC210X # touchscreen/sensors/audio @@ -147,17 +185,21 @@ config PXA2XX select SERIAL select SD select SSI - select USB_OHCI + select USB_OHCI_SYSBUS select PCMCIA config GUMSTIX bool + default y + depends on TCG && ARM select PFLASH_CFI01 select SMC91C111 select PXA2XX config TOSA bool + default y + depends on TCG && ARM select ZAURUS # scoop select MICRODRIVE select PXA2XX @@ -165,6 +207,8 @@ config TOSA config SPITZ bool + default y + depends on TCG && ARM select ADS7846 # touch-screen controller select MAX111X # A/D converter select WM8750 # audio codec @@ -177,6 +221,8 @@ config SPITZ config Z2 bool + default y + depends on TCG && ARM select PFLASH_CFI01 select WM8750 select PL011 # UART @@ -184,6 +230,8 @@ config Z2 config REALVIEW bool + default y + depends on TCG && ARM imply PCI_DEVICES imply PCI_TESTDEV imply I2C_DEVICES @@ -206,12 +254,14 @@ config REALVIEW select PL110 select PL181 # display select PL310 # cache controller - select VERSATILE_I2C + select ARM_SBCON_I2C select DS1338 # I2C RTC+NVRAM - select USB_OHCI + select USB_OHCI_SYSBUS config SBSA_REF bool + default y + depends on TCG && AARCH64 imply PCI_DEVICES select AHCI select ARM_SMMUV3 @@ -222,16 +272,23 @@ config SBSA_REF select PL011 # UART select PL031 # RTC select PL061 # GPIO - select USB_EHCI_SYSBUS + select USB_XHCI_SYSBUS select WDT_SBSA + select BOCHS_DISPLAY + select IDE_BUS + select IDE_DEV config SABRELITE bool + default y + depends on TCG && ARM select FSL_IMX6 select SSI_M25P80 config STELLARIS bool + default y + depends on TCG && ARM imply I2C_DEVICES select ARM_V7M select CMSDK_APB_WATCHDOG @@ -242,13 +299,15 @@ config STELLARIS select SSD0303 # OLED display select SSD0323 # OLED display select SSI_SD - select STELLARIS_INPUT + select STELLARIS_GAMEPAD select STELLARIS_ENET # ethernet select STELLARIS_GPTM # general purpose timer module select UNIMP config STM32VLDISCOVERY bool + default y + depends on TCG && ARM select STM32F100_SOC config STRONGARM @@ -257,16 +316,22 @@ config STRONGARM config COLLIE bool + default y + depends on TCG && ARM select PFLASH_CFI01 select ZAURUS # scoop select STRONGARM config SX1 bool + default y + depends on TCG && ARM select OMAP config VERSATILE bool + default y + depends on TCG && ARM select ARM_TIMER # sp804 select PFLASH_CFI01 select LSI_SCSI_PCI @@ -274,10 +339,12 @@ config VERSATILE select PL080 # DMA controller select PL190 # Vector PIC select REALVIEW - select USB_OHCI + select USB_OHCI_SYSBUS config VEXPRESS bool + default y + depends on TCG && ARM select A9MPCORE select A15MPCORE select ARM_MPTIMER @@ -293,6 +360,8 @@ config VEXPRESS config ZYNQ bool + default y + depends on TCG && ARM select A9MPCORE select CADENCE # UART select PFLASH_CFI02 @@ -309,37 +378,67 @@ config ZYNQ config ARM_V7M bool # currently v7M must be included in a TCG build due to translate.c - default y if TCG && (ARM || AARCH64) + default y + depends on TCG && ARM select PTIMER - select ARM_COMPATIBLE_SEMIHOSTING config ALLWINNER_A10 bool select AHCI select ALLWINNER_A10_PIT select ALLWINNER_A10_PIC + select ALLWINNER_A10_CCM + select ALLWINNER_A10_DRAMC + select ALLWINNER_WDT select ALLWINNER_EMAC + select ALLWINNER_I2C + select AXP2XX_PMU select SERIAL select UNIMP + select USB_OHCI_SYSBUS config ALLWINNER_H3 bool + default y + depends on TCG && ARM select ALLWINNER_A10_PIT select ALLWINNER_SUN8I_EMAC + select ALLWINNER_I2C + select ALLWINNER_WDT select SERIAL select ARM_TIMER select ARM_GIC select UNIMP - select USB_OHCI + select USB_OHCI_SYSBUS + select USB_EHCI_SYSBUS + select SD + +config ALLWINNER_R40 + bool + default y if TCG && ARM + select AHCI + select ALLWINNER_SRAMC + select ALLWINNER_A10_PIT + select ALLWINNER_WDT + select AXP2XX_PMU + select SERIAL + select ARM_TIMER + select ARM_GIC + select UNIMP + select USB_OHCI_SYSBUS select USB_EHCI_SYSBUS select SD config RASPI bool + default y + depends on TCG && ARM select FRAMEBUFFER select PL011 # UART select SDHCI select USB_DWC2 + select BCM2835_SPI + select BCM2835_I2C config STM32F100_SOC bool @@ -364,8 +463,25 @@ config STM32F405_SOC select STM32F4XX_SYSCFG select STM32F4XX_EXTI +config B_L475E_IOT01A + bool + default y + depends on TCG && ARM + select STM32L4X5_SOC + +config STM32L4X5_SOC + bool + select ARM_V7M + select OR_IRQ + select STM32L4X5_EXTI + select STM32L4X5_SYSCFG + select STM32L4X5_RCC + select STM32L4X5_GPIO + config XLNX_ZYNQMP_ARM bool + default y if PIXMAN + depends on TCG && AARCH64 select AHCI select ARM_GIC select CADENCE @@ -377,11 +493,15 @@ config XLNX_ZYNQMP_ARM select XILINX_AXI select XILINX_SPIPS select XLNX_CSU_DMA + select XLNX_DISPLAYPORT select XLNX_ZYNQMP select XLNX_ZDMA + select USB_DWC3 config XLNX_VERSAL bool + default y + depends on TCG && AARCH64 select ARM_GIC select PL011 select CADENCE @@ -392,10 +512,16 @@ config XLNX_VERSAL select OR_IRQ select XLNX_BBRAM select XLNX_EFUSE_VERSAL + select XLNX_USB_SUBSYS + select XLNX_VERSAL_TRNG + select XLNX_CSU_DMA config NPCM7XX bool + default y + depends on TCG && ARM select A9MPCORE + select ADM1266 select ADM1272 select ARM_GIC select SMBUS @@ -408,9 +534,12 @@ config NPCM7XX select SSI select UNIMP select PCA954X + select USB_OHCI_SYSBUS config FSL_IMX25 bool + default y + depends on TCG && ARM imply I2C_DEVICES select IMX select IMX_FEC @@ -420,6 +549,8 @@ config FSL_IMX25 config FSL_IMX31 bool + default y + depends on TCG && ARM imply I2C_DEVICES select SERIAL select IMX @@ -429,6 +560,7 @@ config FSL_IMX31 config FSL_IMX6 bool + imply PCIE_DEVICES imply I2C_DEVICES select A9MPCORE select IMX @@ -436,10 +568,14 @@ config FSL_IMX6 select IMX_I2C select IMX_USBPHY select WDT_IMX2 + select PL310 # cache controller + select PCI_EXPRESS_DESIGNWARE select SDHCI config ASPEED_SOC bool + default y + depends on TCG && ARM select DS1338 select FTGMAC100 select I2C @@ -455,9 +591,14 @@ config ASPEED_SOC select EMC141X select UNIMP select LED + select PMBUS + select MAX31785 + select FSI_APB2OPB_ASPEED config MPS2 bool + default y + depends on TCG && ARM imply I2C_DEVICES select ARMSSE select LAN9118 @@ -469,10 +610,12 @@ config MPS2 select SPLIT_IRQ select UNIMP select CMSDK_APB_WATCHDOG - select VERSATILE_I2C + select ARM_SBCON_I2C config FSL_IMX7 bool + default y + depends on TCG && ARM imply PCI_DEVICES imply TEST_DEVICES imply I2C_DEVICES @@ -491,6 +634,8 @@ config ARM_SMMUV3 config FSL_IMX6UL bool + default y + depends on TCG && ARM imply I2C_DEVICES select A15MPCORE select IMX @@ -502,6 +647,8 @@ config FSL_IMX6UL config MICROBIT bool + default y + depends on TCG && ARM select NRF51_SOC config NRF51_SOC @@ -513,6 +660,8 @@ config NRF51_SOC config EMCRAFT_SF2 bool + default y + depends on TCG && ARM select MSF2 select SSI_M25P80 diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index 79082289ea..57d5d80159 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -18,14 +18,20 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" +#include "hw/char/serial.h" #include "hw/sysbus.h" #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" +#include "hw/loader.h" +#include "target/arm/cpu-qom.h" +#define AW_A10_SRAM_A_BASE 0x00000000 +#define AW_A10_DRAMC_BASE 0x01c01000 #define AW_A10_MMC0_BASE 0x01c0f000 +#define AW_A10_CCM_BASE 0x01c20000 #define AW_A10_PIC_REG_BASE 0x01c20400 #define AW_A10_PIT_REG_BASE 0x01c20c00 #define AW_A10_UART0_REG_BASE 0x01c28000 @@ -33,7 +39,25 @@ #define AW_A10_EHCI_BASE 0x01c14000 #define AW_A10_OHCI_BASE 0x01c14400 #define AW_A10_SATA_BASE 0x01c18000 +#define AW_A10_WDT_BASE 0x01c20c90 #define AW_A10_RTC_BASE 0x01c20d00 +#define AW_A10_I2C0_BASE 0x01c2ac00 + +void allwinner_a10_bootrom_setup(AwA10State *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, rom_size, buffer, 0) < 0) { + error_setg(&error_fatal, "%s: failed to read BlockBackend data", + __func__); + return; + } + + rom_add_blob("allwinner-a10.bootrom", buffer, rom_size, + rom_size, AW_A10_SRAM_A_BASE, + NULL, NULL, NULL, NULL, false); +} static void aw_a10_init(Object *obj) { @@ -46,24 +70,27 @@ static void aw_a10_init(Object *obj) object_initialize_child(obj, "timer", &s->timer, TYPE_AW_A10_PIT); + object_initialize_child(obj, "ccm", &s->ccm, TYPE_AW_A10_CCM); + + object_initialize_child(obj, "dramc", &s->dramc, TYPE_AW_A10_DRAMC); + object_initialize_child(obj, "emac", &s->emac, TYPE_AW_EMAC); object_initialize_child(obj, "sata", &s->sata, TYPE_ALLWINNER_AHCI); - if (machine_usb(current_machine)) { - int i; + object_initialize_child(obj, "i2c0", &s->i2c0, TYPE_AW_I2C); - for (i = 0; i < AW_A10_NUM_USB; i++) { - object_initialize_child(obj, "ehci[*]", &s->ehci[i], - TYPE_PLATFORM_EHCI); - object_initialize_child(obj, "ohci[*]", &s->ohci[i], - TYPE_SYSBUS_OHCI); - } + for (size_t i = 0; i < AW_A10_NUM_USB; i++) { + object_initialize_child(obj, "ehci[*]", &s->ehci[i], + TYPE_PLATFORM_EHCI); + object_initialize_child(obj, "ohci[*]", &s->ohci[i], TYPE_SYSBUS_OHCI); } object_initialize_child(obj, "mmc0", &s->mmc0, TYPE_AW_SDHOST_SUN4I); object_initialize_child(obj, "rtc", &s->rtc, TYPE_AW_RTC_SUN4I); + + object_initialize_child(obj, "wdt", &s->wdt, TYPE_AW_WDT_SUN4I); } static void aw_a10_realize(DeviceState *dev, Error **errp) @@ -103,11 +130,15 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(get_system_memory(), 0x00000000, &s->sram_a); create_unimplemented_device("a10-sram-ctrl", 0x01c00000, 4 * KiB); - /* FIXME use qdev NIC properties instead of nd_table[] */ - if (nd_table[0].used) { - qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC); - qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]); - } + /* Clock Control Module */ + sysbus_realize(SYS_BUS_DEVICE(&s->ccm), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, AW_A10_CCM_BASE); + + /* DRAM Control Module */ + sysbus_realize(SYS_BUS_DEVICE(&s->dramc), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 0, AW_A10_DRAMC_BASE); + + qemu_configure_nic_device(DEVICE(&s->emac), true, NULL); if (!sysbus_realize(SYS_BUS_DEVICE(&s->emac), errp)) { return; } @@ -126,28 +157,24 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(dev, 1), 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); - if (machine_usb(current_machine)) { - int i; + for (size_t i = 0; i < AW_A10_NUM_USB; i++) { + g_autofree char *bus = g_strdup_printf("usb-bus.%zu", i); - for (i = 0; i < AW_A10_NUM_USB; i++) { - g_autofree char *bus = g_strdup_printf("usb-bus.%d", i); + object_property_set_bool(OBJECT(&s->ehci[i]), "companion-enable", + true, &error_fatal); + sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), &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_bool(OBJECT(&s->ehci[i]), "companion-enable", - true, &error_fatal); - sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), &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]), "masterbus", bus, - &error_fatal); - sysbus_realize(SYS_BUS_DEVICE(&s->ohci[i]), &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)); - } + object_property_set_str(OBJECT(&s->ohci[i]), "masterbus", bus, + &error_fatal); + sysbus_realize(SYS_BUS_DEVICE(&s->ohci[i]), &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 */ @@ -162,6 +189,15 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) /* RTC */ sysbus_realize(SYS_BUS_DEVICE(&s->rtc), &error_fatal); sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->rtc), 0, AW_A10_RTC_BASE, 10); + + /* I2C */ + sysbus_realize(SYS_BUS_DEVICE(&s->i2c0), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c0), 0, AW_A10_I2C0_BASE); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0, qdev_get_gpio_in(dev, 7)); + + /* WDT */ + sysbus_realize(SYS_BUS_DEVICE(&s->wdt), &error_fatal); + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->wdt), 0, AW_A10_WDT_BASE, 1); } static void aw_a10_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index 318ed4348c..6870c3fe96 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -30,6 +30,8 @@ #include "hw/loader.h" #include "sysemu/sysemu.h" #include "hw/arm/allwinner-h3.h" +#include "target/arm/cpu-qom.h" +#include "target/arm/gtimer.h" /* Memory map */ const hwaddr allwinner_h3_memmap[] = { @@ -49,10 +51,14 @@ const hwaddr allwinner_h3_memmap[] = { [AW_H3_DEV_OHCI3] = 0x01c1d400, [AW_H3_DEV_CCU] = 0x01c20000, [AW_H3_DEV_PIT] = 0x01c20c00, + [AW_H3_DEV_WDT] = 0x01c20ca0, [AW_H3_DEV_UART0] = 0x01c28000, [AW_H3_DEV_UART1] = 0x01c28400, [AW_H3_DEV_UART2] = 0x01c28800, [AW_H3_DEV_UART3] = 0x01c28c00, + [AW_H3_DEV_TWI0] = 0x01c2ac00, + [AW_H3_DEV_TWI1] = 0x01c2b000, + [AW_H3_DEV_TWI2] = 0x01c2b400, [AW_H3_DEV_EMAC] = 0x01c30000, [AW_H3_DEV_DRAMCOM] = 0x01c62000, [AW_H3_DEV_DRAMCTL] = 0x01c63000, @@ -63,6 +69,7 @@ const hwaddr allwinner_h3_memmap[] = { [AW_H3_DEV_GIC_VCPU] = 0x01c86000, [AW_H3_DEV_RTC] = 0x01f00000, [AW_H3_DEV_CPUCFG] = 0x01f01c00, + [AW_H3_DEV_R_TWI] = 0x01f02400, [AW_H3_DEV_SDRAM] = 0x40000000 }; @@ -106,9 +113,6 @@ struct AwH3Unimplemented { { "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 }, @@ -123,7 +127,6 @@ struct AwH3Unimplemented { { "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 }, @@ -150,8 +153,12 @@ enum { AW_H3_GIC_SPI_UART1 = 1, AW_H3_GIC_SPI_UART2 = 2, AW_H3_GIC_SPI_UART3 = 3, + AW_H3_GIC_SPI_TWI0 = 6, + AW_H3_GIC_SPI_TWI1 = 7, + AW_H3_GIC_SPI_TWI2 = 8, AW_H3_GIC_SPI_TIMER0 = 18, AW_H3_GIC_SPI_TIMER1 = 19, + AW_H3_GIC_SPI_R_TWI = 44, AW_H3_GIC_SPI_MMC0 = 60, AW_H3_GIC_SPI_EHCI0 = 72, AW_H3_GIC_SPI_OHCI0 = 73, @@ -174,7 +181,7 @@ 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) { + if (blk_pread(blk, 8 * KiB, rom_size, buffer, 0) < 0) { error_setg(&error_fatal, "%s: failed to read BlockBackend data", __func__); return; @@ -225,6 +232,13 @@ static void allwinner_h3_init(Object *obj) "ram-size"); object_initialize_child(obj, "rtc", &s->rtc, TYPE_AW_RTC_SUN6I); + + object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C_SUN6I); + object_initialize_child(obj, "twi1", &s->i2c1, TYPE_AW_I2C_SUN6I); + object_initialize_child(obj, "twi2", &s->i2c2, TYPE_AW_I2C_SUN6I); + object_initialize_child(obj, "r_twi", &s->r_twi, TYPE_AW_I2C_SUN6I); + + object_initialize_child(obj, "wdt", &s->wdt, TYPE_AW_WDT_SUN6I); } static void allwinner_h3_realize(DeviceState *dev, Error **errp) @@ -357,11 +371,7 @@ static void allwinner_h3_realize(DeviceState *dev, Error **errp) "sd-bus"); /* EMAC */ - /* FIXME use qdev NIC properties instead of nd_table[] */ - 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]); - } + qemu_configure_nic_device(DEVICE(&s->emac), true, NULL); object_property_set_link(OBJECT(&s->emac), "dma-memory", OBJECT(get_system_memory()), &error_fatal); sysbus_realize(SYS_BUS_DEVICE(&s->emac), &error_fatal); @@ -423,6 +433,32 @@ static void allwinner_h3_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(&s->rtc), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, s->memmap[AW_H3_DEV_RTC]); + /* I2C */ + sysbus_realize(SYS_BUS_DEVICE(&s->i2c0), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c0), 0, s->memmap[AW_H3_DEV_TWI0]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_TWI0)); + + sysbus_realize(SYS_BUS_DEVICE(&s->i2c1), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c1), 0, s->memmap[AW_H3_DEV_TWI1]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c1), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_TWI1)); + + sysbus_realize(SYS_BUS_DEVICE(&s->i2c2), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c2), 0, s->memmap[AW_H3_DEV_TWI2]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c2), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_TWI2)); + + sysbus_realize(SYS_BUS_DEVICE(&s->r_twi), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->r_twi), 0, s->memmap[AW_H3_DEV_R_TWI]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->r_twi), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_R_TWI)); + + /* WDT */ + sysbus_realize(SYS_BUS_DEVICE(&s->wdt), &error_fatal); + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->wdt), 0, + s->memmap[AW_H3_DEV_WDT], 1); + /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(unimplemented); i++) { create_unimplemented_device(unimplemented[i].device_name, diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c new file mode 100644 index 0000000000..b8c7202133 --- /dev/null +++ b/hw/arm/allwinner-r40.c @@ -0,0 +1,565 @@ +/* + * Allwinner R40/A40i/T3 System on Chip emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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 "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/bswap.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "hw/boards.h" +#include "hw/qdev-core.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-r40.h" +#include "hw/misc/allwinner-r40-dramc.h" +#include "target/arm/cpu-qom.h" +#include "target/arm/gtimer.h" + +/* Memory map */ +const hwaddr allwinner_r40_memmap[] = { + [AW_R40_DEV_SRAM_A1] = 0x00000000, + [AW_R40_DEV_SRAM_A2] = 0x00004000, + [AW_R40_DEV_SRAM_A3] = 0x00008000, + [AW_R40_DEV_SRAM_A4] = 0x0000b400, + [AW_R40_DEV_SRAMC] = 0x01c00000, + [AW_R40_DEV_EMAC] = 0x01c0b000, + [AW_R40_DEV_MMC0] = 0x01c0f000, + [AW_R40_DEV_MMC1] = 0x01c10000, + [AW_R40_DEV_MMC2] = 0x01c11000, + [AW_R40_DEV_MMC3] = 0x01c12000, + [AW_R40_DEV_AHCI] = 0x01c18000, + [AW_R40_DEV_EHCI1] = 0x01c19000, + [AW_R40_DEV_OHCI1] = 0x01c19400, + [AW_R40_DEV_EHCI2] = 0x01c1c000, + [AW_R40_DEV_OHCI2] = 0x01c1c400, + [AW_R40_DEV_CCU] = 0x01c20000, + [AW_R40_DEV_PIT] = 0x01c20c00, + [AW_R40_DEV_WDT] = 0x01c20c90, + [AW_R40_DEV_UART0] = 0x01c28000, + [AW_R40_DEV_UART1] = 0x01c28400, + [AW_R40_DEV_UART2] = 0x01c28800, + [AW_R40_DEV_UART3] = 0x01c28c00, + [AW_R40_DEV_UART4] = 0x01c29000, + [AW_R40_DEV_UART5] = 0x01c29400, + [AW_R40_DEV_UART6] = 0x01c29800, + [AW_R40_DEV_UART7] = 0x01c29c00, + [AW_R40_DEV_TWI0] = 0x01c2ac00, + [AW_R40_DEV_GMAC] = 0x01c50000, + [AW_R40_DEV_DRAMCOM] = 0x01c62000, + [AW_R40_DEV_DRAMCTL] = 0x01c63000, + [AW_R40_DEV_DRAMPHY] = 0x01c65000, + [AW_R40_DEV_GIC_DIST] = 0x01c81000, + [AW_R40_DEV_GIC_CPU] = 0x01c82000, + [AW_R40_DEV_GIC_HYP] = 0x01c84000, + [AW_R40_DEV_GIC_VCPU] = 0x01c86000, + [AW_R40_DEV_SDRAM] = 0x40000000 +}; + +/* List of unimplemented devices */ +struct AwR40Unimplemented { + const char *device_name; + hwaddr base; + hwaddr size; +}; + +static struct AwR40Unimplemented r40_unimplemented[] = { + { "d-engine", 0x01000000, 4 * MiB }, + { "d-inter", 0x01400000, 128 * KiB }, + { "dma", 0x01c02000, 4 * KiB }, + { "nfdc", 0x01c03000, 4 * KiB }, + { "ts", 0x01c04000, 4 * KiB }, + { "spi0", 0x01c05000, 4 * KiB }, + { "spi1", 0x01c06000, 4 * KiB }, + { "cs0", 0x01c09000, 4 * KiB }, + { "keymem", 0x01c0a000, 4 * KiB }, + { "usb0-otg", 0x01c13000, 4 * KiB }, + { "usb0-host", 0x01c14000, 4 * KiB }, + { "crypto", 0x01c15000, 4 * KiB }, + { "spi2", 0x01c17000, 4 * KiB }, + { "usb1-phy", 0x01c19800, 2 * KiB }, + { "sid", 0x01c1b000, 4 * KiB }, + { "usb2-phy", 0x01c1c800, 2 * KiB }, + { "cs1", 0x01c1d000, 4 * KiB }, + { "spi3", 0x01c1f000, 4 * KiB }, + { "rtc", 0x01c20400, 1 * KiB }, + { "pio", 0x01c20800, 1 * KiB }, + { "owa", 0x01c21000, 1 * KiB }, + { "ac97", 0x01c21400, 1 * KiB }, + { "cir0", 0x01c21800, 1 * KiB }, + { "cir1", 0x01c21c00, 1 * KiB }, + { "pcm0", 0x01c22000, 1 * KiB }, + { "pcm1", 0x01c22400, 1 * KiB }, + { "pcm2", 0x01c22800, 1 * KiB }, + { "audio", 0x01c22c00, 1 * KiB }, + { "keypad", 0x01c23000, 1 * KiB }, + { "pwm", 0x01c23400, 1 * KiB }, + { "keyadc", 0x01c24400, 1 * KiB }, + { "ths", 0x01c24c00, 1 * KiB }, + { "rtp", 0x01c25000, 1 * KiB }, + { "pmu", 0x01c25400, 1 * KiB }, + { "cpu-cfg", 0x01c25c00, 1 * KiB }, + { "uart0", 0x01c28000, 1 * KiB }, + { "uart1", 0x01c28400, 1 * KiB }, + { "uart2", 0x01c28800, 1 * KiB }, + { "uart3", 0x01c28c00, 1 * KiB }, + { "uart4", 0x01c29000, 1 * KiB }, + { "uart5", 0x01c29400, 1 * KiB }, + { "uart6", 0x01c29800, 1 * KiB }, + { "uart7", 0x01c29c00, 1 * KiB }, + { "ps20", 0x01c2a000, 1 * KiB }, + { "ps21", 0x01c2a400, 1 * KiB }, + { "twi1", 0x01c2b000, 1 * KiB }, + { "twi2", 0x01c2b400, 1 * KiB }, + { "twi3", 0x01c2b800, 1 * KiB }, + { "twi4", 0x01c2c000, 1 * KiB }, + { "scr", 0x01c2c400, 1 * KiB }, + { "tvd-top", 0x01c30000, 4 * KiB }, + { "tvd0", 0x01c31000, 4 * KiB }, + { "tvd1", 0x01c32000, 4 * KiB }, + { "tvd2", 0x01c33000, 4 * KiB }, + { "tvd3", 0x01c34000, 4 * KiB }, + { "gpu", 0x01c40000, 64 * KiB }, + { "hstmr", 0x01c60000, 4 * KiB }, + { "tcon-top", 0x01c70000, 4 * KiB }, + { "lcd0", 0x01c71000, 4 * KiB }, + { "lcd1", 0x01c72000, 4 * KiB }, + { "tv0", 0x01c73000, 4 * KiB }, + { "tv1", 0x01c74000, 4 * KiB }, + { "tve-top", 0x01c90000, 16 * KiB }, + { "tve0", 0x01c94000, 16 * KiB }, + { "tve1", 0x01c98000, 16 * KiB }, + { "mipi_dsi", 0x01ca0000, 4 * KiB }, + { "mipi_dphy", 0x01ca1000, 4 * KiB }, + { "ve", 0x01d00000, 1024 * KiB }, + { "mp", 0x01e80000, 128 * KiB }, + { "hdmi", 0x01ee0000, 128 * KiB }, + { "prcm", 0x01f01400, 1 * KiB }, + { "debug", 0x3f500000, 64 * KiB }, + { "cpubist", 0x3f501000, 4 * KiB }, + { "dcu", 0x3fff0000, 64 * KiB }, + { "hstmr", 0x01c60000, 4 * KiB }, + { "brom", 0xffff0000, 36 * KiB } +}; + +/* Per Processor Interrupts */ +enum { + AW_R40_GIC_PPI_MAINT = 9, + AW_R40_GIC_PPI_HYPTIMER = 10, + AW_R40_GIC_PPI_VIRTTIMER = 11, + AW_R40_GIC_PPI_SECTIMER = 13, + AW_R40_GIC_PPI_PHYSTIMER = 14 +}; + +/* Shared Processor Interrupts */ +enum { + AW_R40_GIC_SPI_UART0 = 1, + AW_R40_GIC_SPI_UART1 = 2, + AW_R40_GIC_SPI_UART2 = 3, + AW_R40_GIC_SPI_UART3 = 4, + AW_R40_GIC_SPI_TWI0 = 7, + AW_R40_GIC_SPI_UART4 = 17, + AW_R40_GIC_SPI_UART5 = 18, + AW_R40_GIC_SPI_UART6 = 19, + AW_R40_GIC_SPI_UART7 = 20, + AW_R40_GIC_SPI_TIMER0 = 22, + AW_R40_GIC_SPI_TIMER1 = 23, + AW_R40_GIC_SPI_MMC0 = 32, + AW_R40_GIC_SPI_MMC1 = 33, + AW_R40_GIC_SPI_MMC2 = 34, + AW_R40_GIC_SPI_MMC3 = 35, + AW_R40_GIC_SPI_EMAC = 55, + AW_R40_GIC_SPI_AHCI = 56, + AW_R40_GIC_SPI_OHCI1 = 64, + AW_R40_GIC_SPI_OHCI2 = 65, + AW_R40_GIC_SPI_EHCI1 = 76, + AW_R40_GIC_SPI_EHCI2 = 78, + AW_R40_GIC_SPI_GMAC = 85, +}; + +/* Allwinner R40 general constants */ +enum { + AW_R40_GIC_NUM_SPI = 128 +}; + +#define BOOT0_MAGIC "eGON.BT0" + +/* The low 8-bits of the 'boot_media' field in the SPL header */ +#define SUNXI_BOOTED_FROM_MMC0 0 +#define SUNXI_BOOTED_FROM_NAND 1 +#define SUNXI_BOOTED_FROM_MMC2 2 +#define SUNXI_BOOTED_FROM_SPI 3 + +struct boot_file_head { + uint32_t b_instruction; + uint8_t magic[8]; + uint32_t check_sum; + uint32_t length; + uint32_t pub_head_size; + uint32_t fel_script_address; + uint32_t fel_uEnv_length; + uint32_t dt_name_offset; + uint32_t dram_size; + uint32_t boot_media; + uint32_t string_pool[13]; +}; + +bool allwinner_r40_bootrom_setup(AwR40State *s, BlockBackend *blk, int unit) +{ + const int64_t rom_size = 32 * KiB; + g_autofree uint8_t *buffer = g_new0(uint8_t, rom_size); + struct boot_file_head *head = (struct boot_file_head *)buffer; + + if (blk_pread(blk, 8 * KiB, rom_size, buffer, 0) < 0) { + error_setg(&error_fatal, "%s: failed to read BlockBackend data", + __func__); + return false; + } + + /* we only check the magic string here. */ + if (memcmp(head->magic, BOOT0_MAGIC, sizeof(head->magic))) { + return false; + } + + /* + * Simulate the behavior of the bootROM, it will change the boot_media + * flag to indicate where the chip is booting from. R40 can boot from + * mmc0 or mmc2, the default value of boot_media is zero + * (SUNXI_BOOTED_FROM_MMC0), let's fix this flag when it is booting from + * the others. + */ + if (unit == 2) { + head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC2); + } else { + head->boot_media = cpu_to_le32(SUNXI_BOOTED_FROM_MMC0); + } + + rom_add_blob("allwinner-r40.bootrom", buffer, rom_size, + rom_size, s->memmap[AW_R40_DEV_SRAM_A1], + NULL, NULL, NULL, NULL, false); + return true; +} + +static void allwinner_r40_init(Object *obj) +{ + static const char *mmc_names[AW_R40_NUM_MMCS] = { + "mmc0", "mmc1", "mmc2", "mmc3" + }; + AwR40State *s = AW_R40(obj); + + s->memmap = allwinner_r40_memmap; + + for (int i = 0; i < AW_R40_NUM_CPUS; i++) { + object_initialize_child(obj, "cpu[*]", &s->cpus[i], + ARM_CPU_TYPE_NAME("cortex-a7")); + } + + object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GIC); + + object_initialize_child(obj, "timer", &s->timer, TYPE_AW_A10_PIT); + object_property_add_alias(obj, "clk0-freq", OBJECT(&s->timer), + "clk0-freq"); + object_property_add_alias(obj, "clk1-freq", OBJECT(&s->timer), + "clk1-freq"); + + object_initialize_child(obj, "wdt", &s->wdt, TYPE_AW_WDT_SUN4I); + + object_initialize_child(obj, "ccu", &s->ccu, TYPE_AW_R40_CCU); + + for (int i = 0; i < AW_R40_NUM_MMCS; i++) { + object_initialize_child(obj, mmc_names[i], &s->mmc[i], + TYPE_AW_SDHOST_SUN50I_A64); + } + + object_initialize_child(obj, "sata", &s->sata, TYPE_ALLWINNER_AHCI); + + for (size_t i = 0; i < AW_R40_NUM_USB; i++) { + object_initialize_child(obj, "ehci[*]", &s->ehci[i], + TYPE_PLATFORM_EHCI); + object_initialize_child(obj, "ohci[*]", &s->ohci[i], + TYPE_SYSBUS_OHCI); + } + + object_initialize_child(obj, "twi0", &s->i2c0, TYPE_AW_I2C_SUN6I); + + object_initialize_child(obj, "emac", &s->emac, TYPE_AW_EMAC); + object_initialize_child(obj, "gmac", &s->gmac, TYPE_AW_SUN8I_EMAC); + object_property_add_alias(obj, "gmac-phy-addr", + OBJECT(&s->gmac), "phy-addr"); + + object_initialize_child(obj, "dramc", &s->dramc, TYPE_AW_R40_DRAMC); + object_property_add_alias(obj, "ram-addr", OBJECT(&s->dramc), + "ram-addr"); + object_property_add_alias(obj, "ram-size", OBJECT(&s->dramc), + "ram-size"); + + object_initialize_child(obj, "sramc", &s->sramc, TYPE_AW_SRAMC_SUN8I_R40); +} + +static void allwinner_r40_realize(DeviceState *dev, Error **errp) +{ + AwR40State *s = AW_R40(dev); + + /* CPUs */ + for (unsigned i = 0; i < AW_R40_NUM_CPUS; i++) { + + /* + * Disable secondary CPUs. Guest EL3 firmware will start + * them via CPU reset control registers. + */ + 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_realize(DEVICE(&s->cpus[i]), NULL, &error_fatal); + } + + /* Generic Interrupt Controller */ + qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", AW_R40_GIC_NUM_SPI + + GIC_INTERNAL); + qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2); + qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", AW_R40_NUM_CPUS); + qdev_prop_set_bit(DEVICE(&s->gic), "has-security-extensions", false); + qdev_prop_set_bit(DEVICE(&s->gic), "has-virtualization-extensions", true); + sysbus_realize(SYS_BUS_DEVICE(&s->gic), &error_fatal); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, s->memmap[AW_R40_DEV_GIC_DIST]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, s->memmap[AW_R40_DEV_GIC_CPU]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 2, s->memmap[AW_R40_DEV_GIC_HYP]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 3, s->memmap[AW_R40_DEV_GIC_VCPU]); + + /* + * Wire the outputs from each CPU's generic timer and the GICv2 + * 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 (unsigned i = 0; i < AW_R40_NUM_CPUS; i++) { + DeviceState *cpudev = DEVICE(&s->cpus[i]); + int ppibase = AW_R40_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_R40_GIC_PPI_PHYSTIMER, + [GTIMER_VIRT] = AW_R40_GIC_PPI_VIRTTIMER, + [GTIMER_HYP] = AW_R40_GIC_PPI_HYPTIMER, + [GTIMER_SEC] = AW_R40_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_R40_NUM_CPUS, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (2 * AW_R40_NUM_CPUS), + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (3 * AW_R40_NUM_CPUS), + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + + /* GIC maintenance signal */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (4 * AW_R40_NUM_CPUS), + qdev_get_gpio_in(DEVICE(&s->gic), + ppibase + AW_R40_GIC_PPI_MAINT)); + } + + /* Timer */ + sysbus_realize(SYS_BUS_DEVICE(&s->timer), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer), 0, s->memmap[AW_R40_DEV_PIT]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 0, + qdev_get_gpio_in(DEVICE(&s->gic), + AW_R40_GIC_SPI_TIMER0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 1, + qdev_get_gpio_in(DEVICE(&s->gic), + AW_R40_GIC_SPI_TIMER1)); + + /* SRAM */ + sysbus_realize(SYS_BUS_DEVICE(&s->sramc), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sramc), 0, s->memmap[AW_R40_DEV_SRAMC]); + + memory_region_init_ram(&s->sram_a1, OBJECT(dev), "sram A1", + 16 * KiB, &error_abort); + memory_region_init_ram(&s->sram_a2, OBJECT(dev), "sram A2", + 16 * KiB, &error_abort); + memory_region_init_ram(&s->sram_a3, OBJECT(dev), "sram A3", + 13 * KiB, &error_abort); + memory_region_init_ram(&s->sram_a4, OBJECT(dev), "sram A4", + 3 * KiB, &error_abort); + memory_region_add_subregion(get_system_memory(), + s->memmap[AW_R40_DEV_SRAM_A1], &s->sram_a1); + memory_region_add_subregion(get_system_memory(), + s->memmap[AW_R40_DEV_SRAM_A2], &s->sram_a2); + memory_region_add_subregion(get_system_memory(), + s->memmap[AW_R40_DEV_SRAM_A3], &s->sram_a3); + memory_region_add_subregion(get_system_memory(), + s->memmap[AW_R40_DEV_SRAM_A4], &s->sram_a4); + + /* Clock Control Unit */ + sysbus_realize(SYS_BUS_DEVICE(&s->ccu), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, s->memmap[AW_R40_DEV_CCU]); + + /* SATA / AHCI */ + sysbus_realize(SYS_BUS_DEVICE(&s->sata), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sata), 0, + allwinner_r40_memmap[AW_R40_DEV_AHCI]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_AHCI)); + + /* USB */ + for (size_t i = 0; i < AW_R40_NUM_USB; i++) { + g_autofree char *bus = g_strdup_printf("usb-bus.%zu", i); + + object_property_set_bool(OBJECT(&s->ehci[i]), "companion-enable", true, + &error_fatal); + sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci[i]), 0, + allwinner_r40_memmap[i ? AW_R40_DEV_EHCI2 + : AW_R40_DEV_EHCI1]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, + qdev_get_gpio_in(DEVICE(&s->gic), + i ? AW_R40_GIC_SPI_EHCI2 + : AW_R40_GIC_SPI_EHCI1)); + + object_property_set_str(OBJECT(&s->ohci[i]), "masterbus", bus, + &error_fatal); + sysbus_realize(SYS_BUS_DEVICE(&s->ohci[i]), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ohci[i]), 0, + allwinner_r40_memmap[i ? AW_R40_DEV_OHCI2 + : AW_R40_DEV_OHCI1]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ohci[i]), 0, + qdev_get_gpio_in(DEVICE(&s->gic), + i ? AW_R40_GIC_SPI_OHCI2 + : AW_R40_GIC_SPI_OHCI1)); + } + + /* SD/MMC */ + for (int i = 0; i < AW_R40_NUM_MMCS; i++) { + qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->gic), + AW_R40_GIC_SPI_MMC0 + i); + const hwaddr addr = s->memmap[AW_R40_DEV_MMC0 + i]; + + object_property_set_link(OBJECT(&s->mmc[i]), "dma-memory", + OBJECT(get_system_memory()), &error_fatal); + sysbus_realize(SYS_BUS_DEVICE(&s->mmc[i]), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc[i]), 0, addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc[i]), 0, irq); + } + + /* UART0. For future clocktree API: All UARTS are connected to APB2_CLK. */ + for (int i = 0; i < AW_R40_NUM_UARTS; i++) { + static const int uart_irqs[AW_R40_NUM_UARTS] = { + AW_R40_GIC_SPI_UART0, + AW_R40_GIC_SPI_UART1, + AW_R40_GIC_SPI_UART2, + AW_R40_GIC_SPI_UART3, + AW_R40_GIC_SPI_UART4, + AW_R40_GIC_SPI_UART5, + AW_R40_GIC_SPI_UART6, + AW_R40_GIC_SPI_UART7, + }; + const hwaddr addr = s->memmap[AW_R40_DEV_UART0 + i]; + + serial_mm_init(get_system_memory(), addr, 2, + qdev_get_gpio_in(DEVICE(&s->gic), uart_irqs[i]), + 115200, serial_hd(i), DEVICE_NATIVE_ENDIAN); + } + + /* I2C */ + sysbus_realize(SYS_BUS_DEVICE(&s->i2c0), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c0), 0, s->memmap[AW_R40_DEV_TWI0]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_TWI0)); + + /* DRAMC */ + sysbus_realize(SYS_BUS_DEVICE(&s->dramc), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 0, + s->memmap[AW_R40_DEV_DRAMCOM]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 1, + s->memmap[AW_R40_DEV_DRAMCTL]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 2, + s->memmap[AW_R40_DEV_DRAMPHY]); + + /* GMAC */ + qemu_configure_nic_device(DEVICE(&s->gmac), true, "gmac"); + object_property_set_link(OBJECT(&s->gmac), "dma-memory", + OBJECT(get_system_memory()), &error_fatal); + sysbus_realize(SYS_BUS_DEVICE(&s->gmac), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gmac), 0, s->memmap[AW_R40_DEV_GMAC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gmac), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_GMAC)); + + /* EMAC */ + qemu_configure_nic_device(DEVICE(&s->emac), true, "emac"); + sysbus_realize(SYS_BUS_DEVICE(&s->emac), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->emac), 0, s->memmap[AW_R40_DEV_EMAC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->emac), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_R40_GIC_SPI_EMAC)); + + /* WDT */ + sysbus_realize(SYS_BUS_DEVICE(&s->wdt), &error_fatal); + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->wdt), 0, + allwinner_r40_memmap[AW_R40_DEV_WDT], 1); + + /* Unimplemented devices */ + for (unsigned i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) { + create_unimplemented_device(r40_unimplemented[i].device_name, + r40_unimplemented[i].base, + r40_unimplemented[i].size); + } +} + +static void allwinner_r40_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = allwinner_r40_realize; + /* Reason: uses serial_hd() in realize function */ + dc->user_creatable = false; +} + +static const TypeInfo allwinner_r40_type_info = { + .name = TYPE_AW_R40, + .parent = TYPE_DEVICE, + .instance_size = sizeof(AwR40State), + .instance_init = allwinner_r40_init, + .class_init = allwinner_r40_class_init, +}; + +static void allwinner_r40_register_types(void) +{ + type_register_static(&allwinner_r40_type_info); +} + +type_init(allwinner_r40_register_types) diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index aecdeb9815..91502d157a 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -85,6 +85,8 @@ static Property iotkit_properties[] = { DEFINE_PROP_UINT32("init-svtor", ARMSSE, init_svtor, 0x10000000), DEFINE_PROP_BOOL("CPU0_FPU", ARMSSE, cpu_fpu[0], true), DEFINE_PROP_BOOL("CPU0_DSP", ARMSSE, cpu_dsp[0], true), + DEFINE_PROP_UINT32("CPU0_MPU_NS", ARMSSE, cpu_mpu_ns[0], 8), + DEFINE_PROP_UINT32("CPU0_MPU_S", ARMSSE, cpu_mpu_s[0], 8), DEFINE_PROP_END_OF_LIST() }; @@ -98,6 +100,10 @@ static Property sse200_properties[] = { DEFINE_PROP_BOOL("CPU0_DSP", ARMSSE, cpu_dsp[0], false), DEFINE_PROP_BOOL("CPU1_FPU", ARMSSE, cpu_fpu[1], true), DEFINE_PROP_BOOL("CPU1_DSP", ARMSSE, cpu_dsp[1], true), + DEFINE_PROP_UINT32("CPU0_MPU_NS", ARMSSE, cpu_mpu_ns[0], 8), + DEFINE_PROP_UINT32("CPU0_MPU_S", ARMSSE, cpu_mpu_s[0], 8), + DEFINE_PROP_UINT32("CPU1_MPU_NS", ARMSSE, cpu_mpu_ns[1], 8), + DEFINE_PROP_UINT32("CPU1_MPU_S", ARMSSE, cpu_mpu_s[1], 8), DEFINE_PROP_END_OF_LIST() }; @@ -109,6 +115,8 @@ static Property sse300_properties[] = { DEFINE_PROP_UINT32("init-svtor", ARMSSE, init_svtor, 0x10000000), DEFINE_PROP_BOOL("CPU0_FPU", ARMSSE, cpu_fpu[0], true), DEFINE_PROP_BOOL("CPU0_DSP", ARMSSE, cpu_dsp[0], true), + DEFINE_PROP_UINT32("CPU0_MPU_NS", ARMSSE, cpu_mpu_ns[0], 8), + DEFINE_PROP_UINT32("CPU0_MPU_S", ARMSSE, cpu_mpu_s[0], 8), DEFINE_PROP_END_OF_LIST() }; @@ -900,6 +908,7 @@ static qemu_irq armsse_get_common_irq_in(ARMSSE *s, int irqno) static void armsse_realize(DeviceState *dev, Error **errp) { + ERRP_GUARD(); ARMSSE *s = ARM_SSE(dev); ARMSSEClass *asc = ARM_SSE_GET_CLASS(dev); const ARMSSEInfo *info = asc->info; @@ -914,8 +923,6 @@ static void armsse_realize(DeviceState *dev, Error **errp) DeviceState *dev_splitter; uint32_t addr_width_max; - ERRP_GUARD(); - if (!s->board_memory) { error_setg(errp, "memory property was not set"); return; @@ -1015,10 +1022,8 @@ static void armsse_realize(DeviceState *dev, Error **errp) * later if necessary. */ if (extract32(info->cpuwait_rst, i, 1)) { - if (!object_property_set_bool(cpuobj, "start-powered-off", true, - errp)) { - return; - } + object_property_set_bool(cpuobj, "start-powered-off", true, + &error_abort); } if (!s->cpu_fpu[i]) { if (!object_property_set_bool(cpuobj, "vfp", false, errp)) { @@ -1030,6 +1035,14 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } } + if (!object_property_set_uint(cpuobj, "mpu-ns-regions", + s->cpu_mpu_ns[i], errp)) { + return; + } + if (!object_property_set_uint(cpuobj, "mpu-s-regions", + s->cpu_mpu_s[i], errp)) { + return; + } if (i > 0) { memory_region_add_subregion_overlap(&s->cpu_container[i], 0, @@ -1453,7 +1466,6 @@ static void armsse_realize(DeviceState *dev, Error **errp) if (info->has_cachectrl) { for (i = 0; i < info->num_cpus; i++) { char *name = g_strdup_printf("cachectrl%d", i); - MemoryRegion *mr; qdev_prop_set_string(DEVICE(&s->cachectrl[i]), "name", name); g_free(name); @@ -1469,7 +1481,6 @@ static void armsse_realize(DeviceState *dev, Error **errp) if (info->has_cpusecctrl) { for (i = 0; i < info->num_cpus; i++) { char *name = g_strdup_printf("CPUSECCTRL%d", i); - MemoryRegion *mr; qdev_prop_set_string(DEVICE(&s->cpusecctrl[i]), "name", name); g_free(name); @@ -1484,7 +1495,6 @@ static void armsse_realize(DeviceState *dev, Error **errp) } if (info->has_cpuid) { for (i = 0; i < info->num_cpus; i++) { - MemoryRegion *mr; qdev_prop_set_uint32(DEVICE(&s->cpuid[i]), "CPUID", i); if (!sysbus_realize(SYS_BUS_DEVICE(&s->cpuid[i]), errp)) { @@ -1497,7 +1507,6 @@ static void armsse_realize(DeviceState *dev, Error **errp) } if (info->has_cpu_pwrctrl) { for (i = 0; i < info->num_cpus; i++) { - MemoryRegion *mr; if (!sysbus_realize(SYS_BUS_DEVICE(&s->cpu_pwrctrl[i]), errp)) { return; @@ -1590,7 +1599,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) /* Wire up the splitters for the MPC IRQs */ for (i = 0; i < IOTS_NUM_EXP_MPC + info->sram_banks; i++) { SplitIRQ *splitter = &s->mpc_irq_splitter[i]; - DeviceState *dev_splitter = DEVICE(splitter); + DeviceState *devs = DEVICE(splitter); if (!object_property_set_int(OBJECT(splitter), "num-lines", 2, errp)) { @@ -1602,22 +1611,22 @@ static void armsse_realize(DeviceState *dev, Error **errp) if (i < IOTS_NUM_EXP_MPC) { /* Splitter input is from GPIO input line */ - s->mpcexp_status_in[i] = qdev_get_gpio_in(dev_splitter, 0); - qdev_connect_gpio_out(dev_splitter, 0, + s->mpcexp_status_in[i] = qdev_get_gpio_in(devs, 0); + qdev_connect_gpio_out(devs, 0, qdev_get_gpio_in_named(dev_secctl, "mpcexp_status", i)); } else { /* Splitter input is from our own MPC */ qdev_connect_gpio_out_named(DEVICE(&s->mpc[i - IOTS_NUM_EXP_MPC]), "irq", 0, - qdev_get_gpio_in(dev_splitter, 0)); - qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in(devs, 0)); + qdev_connect_gpio_out(devs, 0, qdev_get_gpio_in_named(dev_secctl, "mpc_status", i - IOTS_NUM_EXP_MPC)); } - qdev_connect_gpio_out(dev_splitter, 1, + qdev_connect_gpio_out(devs, 1, qdev_get_gpio_in(DEVICE(&s->mpc_irq_orgate), i)); } /* Create GPIO inputs which will pass the line state for our @@ -1666,7 +1675,7 @@ static const VMStateDescription armsse_vmstate = { .name = "iotkit", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CLOCK(mainclk, ARMSSE), VMSTATE_CLOCK(s32kclk, ARMSSE), VMSTATE_UINT32(nsccfg, ARMSSE), diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 32349ec94b..7c68525a9e 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -21,6 +21,9 @@ #include "qemu/module.h" #include "qemu/log.h" #include "target/arm/idau.h" +#include "target/arm/cpu.h" +#include "target/arm/cpu-features.h" +#include "target/arm/cpu-qom.h" #include "migration/vmstate.h" /* Bitbanded IO. Each word corresponds to a single bit. */ @@ -255,6 +258,8 @@ static void armv7m_instance_init(Object *obj) object_initialize_child(obj, "nvic", &s->nvic, TYPE_NVIC); object_property_add_alias(obj, "num-irq", OBJECT(&s->nvic), "num-irq"); + object_property_add_alias(obj, "num-prio-bits", + OBJECT(&s->nvic), "num-prio-bits"); object_initialize_child(obj, "systick-reg-ns", &s->systick[M_REG_NS], TYPE_SYSTICK); @@ -317,12 +322,6 @@ static void armv7m_realize(DeviceState *dev, Error **errp) return; } } - if (object_property_find(OBJECT(s->cpu), "start-powered-off")) { - if (!object_property_set_bool(OBJECT(s->cpu), "start-powered-off", - s->start_powered_off, errp)) { - return; - } - } if (object_property_find(OBJECT(s->cpu), "vfp")) { if (!object_property_set_bool(OBJECT(s->cpu), "vfp", s->vfp, errp)) { return; @@ -333,6 +332,27 @@ static void armv7m_realize(DeviceState *dev, Error **errp) return; } } + object_property_set_bool(OBJECT(s->cpu), "start-powered-off", + s->start_powered_off, &error_abort); + + /* + * Real M-profile hardware can be configured with a different number of + * MPU regions for Secure vs NonSecure. QEMU's CPU implementation doesn't + * support that yet, so catch attempts to select that. + */ + if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY) && + s->mpu_ns_regions != s->mpu_s_regions) { + error_setg(errp, + "mpu-ns-regions and mpu-s-regions properties must have the same value"); + return; + } + if (s->mpu_ns_regions != UINT_MAX && + object_property_find(OBJECT(s->cpu), "pmsav7-dregion")) { + if (!object_property_set_uint(OBJECT(s->cpu), "pmsav7-dregion", + s->mpu_ns_regions, errp)) { + return; + } + } /* * Tell the CPU where the NVIC is; it will fail realize if it doesn't @@ -498,7 +518,7 @@ static void armv7m_realize(DeviceState *dev, Error **errp) for (i = 0; i < ARRAY_SIZE(s->bitband); i++) { if (s->enable_bitband) { Object *obj = OBJECT(&s->bitband[i]); - SysBusDevice *sbd = SYS_BUS_DEVICE(&s->bitband[i]); + sbd = SYS_BUS_DEVICE(&s->bitband[i]); if (!object_property_set_int(obj, "base", bitband_input_addr[i], errp)) { @@ -530,6 +550,8 @@ static Property armv7m_properties[] = { false), DEFINE_PROP_BOOL("vfp", ARMv7MState, vfp, true), DEFINE_PROP_BOOL("dsp", ARMv7MState, dsp, true), + DEFINE_PROP_UINT32("mpu-ns-regions", ARMv7MState, mpu_ns_regions, UINT_MAX), + DEFINE_PROP_UINT32("mpu-s-regions", ARMv7MState, mpu_s_regions, UINT_MAX), DEFINE_PROP_END_OF_LIST(), }; @@ -537,7 +559,7 @@ static const VMStateDescription vmstate_armv7m = { .name = "armv7m", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CLOCK(refclk, ARMv7MState), VMSTATE_CLOCK(cpuclk, ARMv7MState), VMSTATE_END_OF_LIST() @@ -568,21 +590,15 @@ static void armv7m_reset(void *opaque) cpu_reset(CPU(cpu)); } -void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size) +void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, + hwaddr mem_base, int mem_size) { - int image_size; + ssize_t image_size; uint64_t entry; - int big_endian; AddressSpace *as; int asidx; CPUState *cs = CPU(cpu); -#if TARGET_BIG_ENDIAN - big_endian = 1; -#else - big_endian = 0; -#endif - if (arm_feature(&cpu->env, ARM_FEATURE_EL3)) { asidx = ARMASIdx_S; } else { @@ -593,9 +609,9 @@ 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, NULL, NULL, - NULL, big_endian, EM_ARM, 1, 0, as); + NULL, 0, EM_ARM, 1, 0, as); if (image_size < 0) { - image_size = load_image_targphys_as(kernel_filename, 0, + image_size = load_image_targphys_as(kernel_filename, mem_base, mem_size, as); } if (image_size < 0) { diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 98dc185acd..93ca87fda2 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -14,9 +14,12 @@ #include "hw/arm/boot.h" #include "hw/arm/aspeed.h" #include "hw/arm/aspeed_soc.h" +#include "hw/arm/aspeed_eeprom.h" +#include "hw/block/flash.h" #include "hw/i2c/i2c_mux_pca954x.h" #include "hw/i2c/smbus_eeprom.h" -#include "hw/misc/pca9552.h" +#include "hw/gpio/pca9552.h" +#include "hw/nvram/eeprom_at24c.h" #include "hw/sensor/tmp105.h" #include "hw/misc/led.h" #include "hw/qdev-properties.h" @@ -26,6 +29,7 @@ #include "qemu/error-report.h" #include "qemu/units.h" #include "hw/qdev-clock.h" +#include "sysemu/sysemu.h" static struct arm_boot_info aspeed_board_binfo = { .board_id = -1, /* device-tree-only board */ @@ -36,14 +40,21 @@ struct AspeedMachineState { MachineState parent_obj; /* Public */ - AspeedSoCState soc; - MemoryRegion ram_container; - MemoryRegion max_ram; + AspeedSoCState *soc; + MemoryRegion boot_rom; bool mmio_exec; + uint32_t uart_chosen; char *fmc_model; char *spi_model; }; +/* On 32-bit hosts, lower RAM to 1G because of the 2047 MB limit */ +#if HOST_LONG_BITS == 32 +#define ASPEED_RAM_SIZE(sz) MIN((sz), 1 * GiB) +#else +#define ASPEED_RAM_SIZE(sz) (sz) +#endif + /* Palmetto hardware value: 0x120CE416 */ #define PALMETTO_BMC_HW_STRAP1 ( \ SCU_AST2400_HW_STRAP_DRAM_SIZE(DRAM_SIZE_256MB) | \ @@ -72,6 +83,16 @@ struct AspeedMachineState { SCU_HW_STRAP_VGA_SIZE_SET(VGA_16M_DRAM) | \ SCU_AST2400_HW_STRAP_BOOT_MODE(AST2400_SPI_BOOT)) +/* TODO: Find the actual hardware value */ +#define SUPERMICRO_X11SPI_BMC_HW_STRAP1 ( \ + AST2500_HW_STRAP1_DEFAULTS | \ + SCU_AST2500_HW_STRAP_SPI_AUTOFETCH_ENABLE | \ + SCU_AST2500_HW_STRAP_GPIO_STRAP_ENABLE | \ + SCU_AST2500_HW_STRAP_UART_DEBUG | \ + SCU_AST2500_HW_STRAP_DDR4_ENABLE | \ + SCU_HW_STRAP_SPI_WIDTH | \ + SCU_HW_STRAP_SPI_MODE(SCU_HW_STRAP_SPI_M_S_EN)) + /* AST2500 evb hardware value: 0xF100C2E6 */ #define AST2500_EVB_HW_STRAP1 (( \ AST2500_HW_STRAP1_DEFAULTS | \ @@ -174,26 +195,9 @@ struct AspeedMachineState { #define BLETCHLEY_BMC_HW_STRAP1 AST2600_EVB_HW_STRAP1 #define BLETCHLEY_BMC_HW_STRAP2 AST2600_EVB_HW_STRAP2 -/* - * The max ram region is for firmwares that scan the address space - * with load/store to guess how much RAM the SoC has. - */ -static uint64_t max_ram_read(void *opaque, hwaddr offset, unsigned size) -{ - return 0; -} - -static void max_ram_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) -{ - /* Discard writes */ -} - -static const MemoryRegionOps max_ram_ops = { - .read = max_ram_read, - .write = max_ram_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; +/* Qualcomm DC-SCM hardware value */ +#define QCOM_DC_SCM_V1_BMC_HW_STRAP1 0x00000000 +#define QCOM_DC_SCM_V1_BMC_HW_STRAP2 0x00000041 #define AST_SMP_MAILBOX_BASE 0x1e6e2180 #define AST_SMP_MBOX_FIELD_ENTRY (AST_SMP_MAILBOX_BASE + 0x0) @@ -206,33 +210,35 @@ static const MemoryRegionOps max_ram_ops = { static void aspeed_write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info) { - static const uint32_t poll_mailbox_ready[] = { + AddressSpace *as = arm_boot_address_space(cpu, info); + static const ARMInsnFixup poll_mailbox_ready[] = { /* * r2 = per-cpu go sign value * r1 = AST_SMP_MBOX_FIELD_ENTRY * r0 = AST_SMP_MBOX_FIELD_GOSIGN */ - 0xee100fb0, /* mrc p15, 0, r0, c0, c0, 5 */ - 0xe21000ff, /* ands r0, r0, #255 */ - 0xe59f201c, /* ldr r2, [pc, #28] */ - 0xe1822000, /* orr r2, r2, r0 */ + { 0xee100fb0 }, /* mrc p15, 0, r0, c0, c0, 5 */ + { 0xe21000ff }, /* ands r0, r0, #255 */ + { 0xe59f201c }, /* ldr r2, [pc, #28] */ + { 0xe1822000 }, /* orr r2, r2, r0 */ - 0xe59f1018, /* ldr r1, [pc, #24] */ - 0xe59f0018, /* ldr r0, [pc, #24] */ + { 0xe59f1018 }, /* ldr r1, [pc, #24] */ + { 0xe59f0018 }, /* ldr r0, [pc, #24] */ - 0xe320f002, /* wfe */ - 0xe5904000, /* ldr r4, [r0] */ - 0xe1520004, /* cmp r2, r4 */ - 0x1afffffb, /* bne */ - 0xe591f000, /* ldr pc, [r1] */ - AST_SMP_MBOX_GOSIGN, - AST_SMP_MBOX_FIELD_ENTRY, - AST_SMP_MBOX_FIELD_GOSIGN, + { 0xe320f002 }, /* wfe */ + { 0xe5904000 }, /* ldr r4, [r0] */ + { 0xe1520004 }, /* cmp r2, r4 */ + { 0x1afffffb }, /* bne */ + { 0xe591f000 }, /* ldr pc, [r1] */ + { AST_SMP_MBOX_GOSIGN }, + { AST_SMP_MBOX_FIELD_ENTRY }, + { AST_SMP_MBOX_FIELD_GOSIGN }, + { 0, FIXUP_TERMINATOR } }; + static const uint32_t fixupcontext[FIXUP_MAX] = { 0 }; - rom_add_blob_fixed("aspeed.smpboot", poll_mailbox_ready, - sizeof(poll_mailbox_ready), - info->smp_loader_start); + arm_write_bootloader("aspeed.smpboot", as, info->smp_loader_start, + poll_mailbox_ready, fixupcontext); } static void aspeed_reset_secondary(ARMCPU *cpu, @@ -247,12 +253,9 @@ static void aspeed_reset_secondary(ARMCPU *cpu, cpu_set_pc(cs, info->smp_loader_start); } -#define FIRMWARE_ADDR 0x0 - -static void write_boot_rom(DriveInfo *dinfo, hwaddr addr, size_t rom_size, +static void write_boot_rom(BlockBackend *blk, hwaddr addr, size_t rom_size, Error **errp) { - BlockBackend *blk = blk_by_legacy_dinfo(dinfo); g_autofree void *storage = NULL; int64_t size; @@ -270,7 +273,7 @@ static void write_boot_rom(DriveInfo *dinfo, hwaddr addr, size_t rom_size, } storage = g_malloc0(rom_size); - if (blk_pread(blk, 0, storage, rom_size) < 0) { + if (blk_pread(blk, 0, rom_size, storage, 0) < 0) { error_setg(errp, "failed to read the initial flash content"); return; } @@ -278,7 +281,25 @@ static void write_boot_rom(DriveInfo *dinfo, hwaddr addr, size_t rom_size, rom_add_blob_fixed("aspeed.boot_rom", storage, rom_size, addr); } -static void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, +/* + * Create a ROM and copy the flash contents at the expected address + * (0x0). Boots faster than execute-in-place. + */ +static void aspeed_install_boot_rom(AspeedMachineState *bmc, BlockBackend *blk, + uint64_t rom_size) +{ + AspeedSoCState *soc = bmc->soc; + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(soc); + + memory_region_init_rom(&bmc->boot_rom, NULL, "aspeed.boot_rom", rom_size, + &error_abort); + memory_region_add_subregion_overlap(&soc->spi_boot_container, 0, + &bmc->boot_rom, 1); + write_boot_rom(blk, sc->memmap[ASPEED_DEV_SPI_BOOT], + rom_size, &error_abort); +} + +void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, unsigned int count, int unit0) { int i; @@ -289,17 +310,14 @@ static void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, for (i = 0; i < count; ++i) { DriveInfo *dinfo = drive_get(IF_MTD, 0, unit0 + i); - qemu_irq cs_line; DeviceState *dev; dev = qdev_new(flashtype); if (dinfo) { qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo)); } + qdev_prop_set_uint8(dev, "cs", i); qdev_realize_and_unref(dev, BUS(s->spi), &error_fatal); - - cs_line = qdev_get_gpio_in_named(dev, SSI_GPIO_CS, 0); - sysbus_connect_irq(SYS_BUS_DEVICE(s), i + 1, cs_line); } } @@ -318,43 +336,56 @@ static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo) &error_fatal); } +static void connect_serial_hds_to_uarts(AspeedMachineState *bmc) +{ + AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(bmc); + AspeedSoCState *s = bmc->soc; + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int uart_chosen = bmc->uart_chosen ? bmc->uart_chosen : amc->uart_default; + + aspeed_soc_uart_set_chr(s, uart_chosen, serial_hd(0)); + for (int i = 1, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) { + if (uart == uart_chosen) { + continue; + } + aspeed_soc_uart_set_chr(s, uart, serial_hd(i)); + } +} + static void aspeed_machine_init(MachineState *machine) { AspeedMachineState *bmc = ASPEED_MACHINE(machine); AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(machine); AspeedSoCClass *sc; - DriveInfo *drive0 = drive_get(IF_MTD, 0, 0); - ram_addr_t max_ram_size; int i; - NICInfo *nd = &nd_table[0]; - memory_region_init(&bmc->ram_container, NULL, "aspeed-ram-container", - 4 * GiB); - memory_region_add_subregion(&bmc->ram_container, 0, machine->ram); - - object_initialize_child(OBJECT(machine), "soc", &bmc->soc, amc->soc_name); - - sc = ASPEED_SOC_GET_CLASS(&bmc->soc); + bmc->soc = ASPEED_SOC(object_new(amc->soc_name)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(bmc->soc)); + object_unref(OBJECT(bmc->soc)); + sc = ASPEED_SOC_GET_CLASS(bmc->soc); /* - * This will error out if isize is not supported by memory controller. + * This will error out if the RAM size is not supported by the + * memory controller of the SoC. */ - object_property_set_uint(OBJECT(&bmc->soc), "ram-size", machine->ram_size, + object_property_set_uint(OBJECT(bmc->soc), "ram-size", machine->ram_size, &error_fatal); for (i = 0; i < sc->macs_num; i++) { - if ((amc->macs_mask & (1 << i)) && nd->used) { - qemu_check_nic_model(nd, TYPE_FTGMAC100); - qdev_set_nic_properties(DEVICE(&bmc->soc.ftgmac100[i]), nd); - nd++; + if ((amc->macs_mask & (1 << i)) && + !qemu_configure_nic_device(DEVICE(&bmc->soc->ftgmac100[i]), + true, NULL)) { + break; /* No configs left; stop asking */ } } - object_property_set_int(OBJECT(&bmc->soc), "hw-strap1", amc->hw_strap1, + object_property_set_int(OBJECT(bmc->soc), "hw-strap1", amc->hw_strap1, &error_abort); - object_property_set_int(OBJECT(&bmc->soc), "hw-strap2", amc->hw_strap2, + object_property_set_int(OBJECT(bmc->soc), "hw-strap2", amc->hw_strap2, &error_abort); - object_property_set_link(OBJECT(&bmc->soc), "dram", + object_property_set_link(OBJECT(bmc->soc), "memory", + OBJECT(get_system_memory()), &error_abort); + object_property_set_link(OBJECT(bmc->soc), "dram", OBJECT(machine->ram), &error_abort); if (machine->kernel_filename) { /* @@ -362,54 +393,19 @@ static void aspeed_machine_init(MachineState *machine) * that runs to unlock the SCU. In this case set the default to * be unlocked as the kernel expects */ - object_property_set_int(OBJECT(&bmc->soc), "hw-prot-key", + object_property_set_int(OBJECT(bmc->soc), "hw-prot-key", ASPEED_SCU_PROT_KEY, &error_abort); } - qdev_prop_set_uint32(DEVICE(&bmc->soc), "uart-default", - amc->uart_default); - qdev_realize(DEVICE(&bmc->soc), NULL, &error_abort); + connect_serial_hds_to_uarts(bmc); + qdev_realize(DEVICE(bmc->soc), NULL, &error_abort); - memory_region_add_subregion(get_system_memory(), - sc->memmap[ASPEED_DEV_SDRAM], - &bmc->ram_container); - - max_ram_size = object_property_get_uint(OBJECT(&bmc->soc), "max-ram-size", - &error_abort); - memory_region_init_io(&bmc->max_ram, NULL, &max_ram_ops, NULL, - "max_ram", max_ram_size - machine->ram_size); - memory_region_add_subregion(&bmc->ram_container, machine->ram_size, &bmc->max_ram); - - aspeed_board_init_flashes(&bmc->soc.fmc, + if (defaults_enabled()) { + aspeed_board_init_flashes(&bmc->soc->fmc, bmc->fmc_model ? bmc->fmc_model : amc->fmc_model, amc->num_cs, 0); - aspeed_board_init_flashes(&bmc->soc.spi[0], + aspeed_board_init_flashes(&bmc->soc->spi[0], bmc->spi_model ? bmc->spi_model : amc->spi_model, 1, amc->num_cs); - - /* Install first FMC flash content as a boot rom. */ - if (drive0) { - AspeedSMCFlash *fl = &bmc->soc.fmc.flashes[0]; - MemoryRegion *boot_rom = g_new(MemoryRegion, 1); - uint64_t size = memory_region_size(&fl->mmio); - - /* - * create a ROM region using the default mapping window size of - * the flash module. The window size is 64MB for the AST2400 - * SoC and 128MB for the AST2500 SoC, which is twice as big as - * needed by the flash modules of the Aspeed machines. - */ - if (ASPEED_MACHINE(machine)->mmio_exec) { - memory_region_init_alias(boot_rom, NULL, "aspeed.boot_rom", - &fl->mmio, 0, size); - memory_region_add_subregion(get_system_memory(), FIRMWARE_ADDR, - boot_rom); - } else { - memory_region_init_rom(boot_rom, NULL, "aspeed.boot_rom", - size, &error_abort); - memory_region_add_subregion(get_system_memory(), FIRMWARE_ADDR, - boot_rom); - write_boot_rom(drive0, FIRMWARE_ADDR, size, &error_abort); - } } if (machine->kernel_filename && sc->num_cpus > 1) { @@ -432,31 +428,32 @@ static void aspeed_machine_init(MachineState *machine) amc->i2c_init(bmc); } - for (i = 0; i < bmc->soc.sdhci.num_slots; i++) { - sdhci_attach_drive(&bmc->soc.sdhci.slots[i], + for (i = 0; i < bmc->soc->sdhci.num_slots; i++) { + sdhci_attach_drive(&bmc->soc->sdhci.slots[i], drive_get(IF_SD, 0, i)); } - if (bmc->soc.emmc.num_slots) { - sdhci_attach_drive(&bmc->soc.emmc.slots[0], - drive_get(IF_SD, 0, bmc->soc.sdhci.num_slots)); + if (bmc->soc->emmc.num_slots) { + sdhci_attach_drive(&bmc->soc->emmc.slots[0], + drive_get(IF_SD, 0, bmc->soc->sdhci.num_slots)); + } + + if (!bmc->mmio_exec) { + DeviceState *dev = ssi_get_cs(bmc->soc->fmc.spi, 0); + BlockBackend *fmc0 = dev ? m25p80_get_blk(dev) : NULL; + + if (fmc0) { + uint64_t rom_size = memory_region_size(&bmc->soc->spi_boot); + aspeed_install_boot_rom(bmc, fmc0, rom_size); + } } arm_load_kernel(ARM_CPU(first_cpu), machine, &aspeed_board_binfo); } -static void at24c_eeprom_init(I2CBus *bus, uint8_t addr, uint32_t rsize) -{ - I2CSlave *i2c_dev = i2c_slave_new("at24c-eeprom", addr); - DeviceState *dev = DEVICE(i2c_dev); - - qdev_prop_set_uint32(dev, "rom-size", rsize); - i2c_slave_realize_and_unref(i2c_dev, bus, &error_abort); -} - static void palmetto_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; DeviceState *dev; uint8_t *eeprom_buf = g_malloc0(32 * 1024); @@ -478,7 +475,7 @@ static void palmetto_bmc_i2c_init(AspeedMachineState *bmc) static void quanta_q71l_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; /* * The quanta-q71l platform expects tmp75s which are compatible with @@ -510,7 +507,7 @@ static void quanta_q71l_bmc_i2c_init(AspeedMachineState *bmc) static void ast2500_evb_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; uint8_t *eeprom_buf = g_malloc0(8 * 1024); smbus_eeprom_init_one(aspeed_i2c_get_bus(&soc->i2c, 3), 0x50, @@ -519,15 +516,11 @@ static void ast2500_evb_i2c_init(AspeedMachineState *bmc) /* The AST2500 EVB expects a LM75 but a TMP105 is compatible */ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), TYPE_TMP105, 0x4d); - - /* The AST2500 EVB does not have an RTC. Let's pretend that one is - * plugged on the I2C bus header */ - i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 11), "ds1338", 0x32); } static void ast2600_evb_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; uint8_t *eeprom_buf = g_malloc0(8 * 1024); smbus_eeprom_init_one(aspeed_i2c_get_bus(&soc->i2c, 7), 0x50, @@ -538,15 +531,42 @@ static void ast2600_evb_i2c_init(AspeedMachineState *bmc) TYPE_TMP105, 0x4d); } +static void yosemitev2_bmc_i2c_init(AspeedMachineState *bmc) +{ + AspeedSoCState *soc = bmc->soc; + + at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 4), 0x51, 128 * KiB); + at24c_eeprom_init_rom(aspeed_i2c_get_bus(&soc->i2c, 8), 0x51, 128 * KiB, + yosemitev2_bmc_fruid, yosemitev2_bmc_fruid_len); + /* TMP421 */ + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 11), "tmp421", 0x1f); + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 9), "tmp421", 0x4e); + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 9), "tmp421", 0x4f); + +} + static void romulus_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; /* The romulus board expects Epson RX8900 I2C RTC but a ds1338 is * good enough */ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 11), "ds1338", 0x32); } +static void tiogapass_bmc_i2c_init(AspeedMachineState *bmc) +{ + AspeedSoCState *soc = bmc->soc; + + at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 4), 0x54, 128 * KiB); + at24c_eeprom_init_rom(aspeed_i2c_get_bus(&soc->i2c, 6), 0x54, 128 * KiB, + tiogapass_bmc_fruid, tiogapass_bmc_fruid_len); + /* TMP421 */ + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), "tmp421", 0x1f); + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 6), "tmp421", 0x4f); + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 6), "tmp421", 0x4e); +} + static void create_pca9552(AspeedSoCState *soc, int bus_id, int addr) { i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, bus_id), @@ -555,7 +575,7 @@ static void create_pca9552(AspeedSoCState *soc, int bus_id, int addr) static void sonorapass_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; /* bus 2 : */ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 2), "tmp105", 0x48); @@ -609,13 +629,12 @@ static void witherspoon_bmc_i2c_init(AspeedMachineState *bmc) {14, LED_COLOR_GREEN, "front-power-3", GPIO_POLARITY_ACTIVE_LOW}, {15, LED_COLOR_GREEN, "front-id-5", GPIO_POLARITY_ACTIVE_LOW}, }; - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; uint8_t *eeprom_buf = g_malloc0(8 * 1024); DeviceState *dev; LEDState *led; /* Bus 3: TODO bmp280@77 */ - /* Bus 3: TODO max31785@52 */ dev = DEVICE(i2c_slave_new(TYPE_PCA9552, 0x60)); qdev_prop_set_string(dev, "description", "pca1"); i2c_slave_realize_and_unref(I2C_SLAVE(dev), @@ -631,6 +650,7 @@ static void witherspoon_bmc_i2c_init(AspeedMachineState *bmc) qdev_get_gpio_in(DEVICE(led), 0)); } i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 3), "dps310", 0x76); + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 3), "max31785", 0x52); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 4), "tmp423", 0x4c); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 5), "tmp423", 0x4c); @@ -654,7 +674,7 @@ static void witherspoon_bmc_i2c_init(AspeedMachineState *bmc) static void g220a_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; DeviceState *dev; dev = DEVICE(i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 3), @@ -688,18 +708,9 @@ static void g220a_bmc_i2c_init(AspeedMachineState *bmc) eeprom_buf); } -static void aspeed_eeprom_init(I2CBus *bus, uint8_t addr, uint32_t rsize) -{ - I2CSlave *i2c_dev = i2c_slave_new("at24c-eeprom", addr); - DeviceState *dev = DEVICE(i2c_dev); - - qdev_prop_set_uint32(dev, "rom-size", rsize); - i2c_slave_realize_and_unref(i2c_dev, bus, &error_abort); -} - static void fp5280g2_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; I2CSlave *i2c_mux; /* The at24c256 */ @@ -726,10 +737,10 @@ static void fp5280g2_bmc_i2c_init(AspeedMachineState *bmc) static void rainier_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; I2CSlave *i2c_mux; - aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 0), 0x51, 32 * KiB); + at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 0), 0x51, 32 * KiB); create_pca9552(soc, 3, 0x61); @@ -742,9 +753,9 @@ static void rainier_bmc_i2c_init(AspeedMachineState *bmc) 0x4a); i2c_mux = i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 4), "pca9546", 0x70); - aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 0), 0x50, 64 * KiB); - aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 1), 0x51, 64 * KiB); - aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 2), 0x52, 64 * KiB); + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 0), 0x50, 64 * KiB); + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 1), 0x51, 64 * KiB); + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 2), 0x52, 64 * KiB); create_pca9552(soc, 4, 0x60); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 5), TYPE_TMP105, @@ -755,8 +766,8 @@ static void rainier_bmc_i2c_init(AspeedMachineState *bmc) create_pca9552(soc, 5, 0x61); i2c_mux = i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 5), "pca9546", 0x70); - aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 0), 0x50, 64 * KiB); - aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 1), 0x51, 64 * KiB); + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 0), 0x50, 64 * KiB); + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 1), 0x51, 64 * KiB); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 6), TYPE_TMP105, 0x48); @@ -766,31 +777,33 @@ static void rainier_bmc_i2c_init(AspeedMachineState *bmc) 0x4b); i2c_mux = i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 6), "pca9546", 0x70); - aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 0), 0x50, 64 * KiB); - aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 1), 0x51, 64 * KiB); - aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 2), 0x50, 64 * KiB); - aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 3), 0x51, 64 * KiB); + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 0), 0x50, 64 * KiB); + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 1), 0x51, 64 * KiB); + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 2), 0x50, 64 * KiB); + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 3), 0x51, 64 * KiB); create_pca9552(soc, 7, 0x30); create_pca9552(soc, 7, 0x31); create_pca9552(soc, 7, 0x32); create_pca9552(soc, 7, 0x33); - /* Bus 7: TODO max31785@52 */ create_pca9552(soc, 7, 0x60); create_pca9552(soc, 7, 0x61); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), "dps310", 0x76); /* Bus 7: TODO si7021-a20@20 */ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), TYPE_TMP105, 0x48); - aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 7), 0x50, 64 * KiB); - aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 7), 0x51, 64 * KiB); + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), "max31785", 0x52); + at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 7), 0x50, 64 * KiB); + at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 7), 0x51, 64 * KiB); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), TYPE_TMP105, 0x48); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), TYPE_TMP105, 0x4a); - aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 8), 0x50, 64 * KiB); - aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 8), 0x51, 64 * KiB); + at24c_eeprom_init_rom(aspeed_i2c_get_bus(&soc->i2c, 8), 0x50, + 64 * KiB, rainier_bb_fruid, rainier_bb_fruid_len); + at24c_eeprom_init_rom(aspeed_i2c_get_bus(&soc->i2c, 8), 0x51, + 64 * KiB, rainier_bmc_fruid, rainier_bmc_fruid_len); create_pca9552(soc, 8, 0x60); create_pca9552(soc, 8, 0x61); /* Bus 8: ucd90320@11 */ @@ -799,11 +812,11 @@ static void rainier_bmc_i2c_init(AspeedMachineState *bmc) i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 9), "tmp423", 0x4c); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 9), "tmp423", 0x4d); - aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 9), 0x50, 128 * KiB); + at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 9), 0x50, 128 * KiB); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 10), "tmp423", 0x4c); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 10), "tmp423", 0x4d); - aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 10), 0x50, 128 * KiB); + at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 10), 0x50, 128 * KiB); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 11), TYPE_TMP105, 0x48); @@ -811,18 +824,18 @@ static void rainier_bmc_i2c_init(AspeedMachineState *bmc) 0x49); i2c_mux = i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 11), "pca9546", 0x70); - aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 0), 0x50, 64 * KiB); - aspeed_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 1), 0x51, 64 * KiB); + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 0), 0x50, 64 * KiB); + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 1), 0x51, 64 * KiB); create_pca9552(soc, 11, 0x60); - aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 13), 0x50, 64 * KiB); + at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 13), 0x50, 64 * KiB); create_pca9552(soc, 13, 0x60); - aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 14), 0x50, 64 * KiB); + at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 14), 0x50, 64 * KiB); create_pca9552(soc, 14, 0x60); - aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 15), 0x50, 64 * KiB); + at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 15), 0x50, 64 * KiB); create_pca9552(soc, 15, 0x60); } @@ -841,7 +854,7 @@ static void get_pca9548_channels(I2CBus *bus, uint8_t mux_addr, static void fuji_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; I2CBus *i2c[144] = {}; for (int i = 0; i < 16; i++) { @@ -866,45 +879,49 @@ static void fuji_bmc_i2c_init(AspeedMachineState *bmc) i2c_slave_create_simple(i2c[17], TYPE_LM75, 0x4c); i2c_slave_create_simple(i2c[17], TYPE_LM75, 0x4d); - aspeed_eeprom_init(i2c[19], 0x52, 64 * KiB); - aspeed_eeprom_init(i2c[20], 0x50, 2 * KiB); - aspeed_eeprom_init(i2c[22], 0x52, 2 * KiB); + /* + * EEPROM 24c64 size is 64Kbits or 8 Kbytes + * 24c02 size is 2Kbits or 256 bytes + */ + at24c_eeprom_init(i2c[19], 0x52, 8 * KiB); + at24c_eeprom_init(i2c[20], 0x50, 256); + at24c_eeprom_init(i2c[22], 0x52, 256); i2c_slave_create_simple(i2c[3], TYPE_LM75, 0x48); i2c_slave_create_simple(i2c[3], TYPE_LM75, 0x49); i2c_slave_create_simple(i2c[3], TYPE_LM75, 0x4a); i2c_slave_create_simple(i2c[3], TYPE_TMP422, 0x4c); - aspeed_eeprom_init(i2c[8], 0x51, 64 * KiB); + at24c_eeprom_init(i2c[8], 0x51, 8 * KiB); i2c_slave_create_simple(i2c[8], TYPE_LM75, 0x4a); i2c_slave_create_simple(i2c[50], TYPE_LM75, 0x4c); - aspeed_eeprom_init(i2c[50], 0x52, 64 * KiB); + at24c_eeprom_init(i2c[50], 0x52, 8 * KiB); i2c_slave_create_simple(i2c[51], TYPE_TMP75, 0x48); i2c_slave_create_simple(i2c[52], TYPE_TMP75, 0x49); i2c_slave_create_simple(i2c[59], TYPE_TMP75, 0x48); i2c_slave_create_simple(i2c[60], TYPE_TMP75, 0x49); - aspeed_eeprom_init(i2c[65], 0x53, 64 * KiB); + at24c_eeprom_init(i2c[65], 0x53, 8 * KiB); i2c_slave_create_simple(i2c[66], TYPE_TMP75, 0x49); i2c_slave_create_simple(i2c[66], TYPE_TMP75, 0x48); - aspeed_eeprom_init(i2c[68], 0x52, 64 * KiB); - aspeed_eeprom_init(i2c[69], 0x52, 64 * KiB); - aspeed_eeprom_init(i2c[70], 0x52, 64 * KiB); - aspeed_eeprom_init(i2c[71], 0x52, 64 * KiB); + at24c_eeprom_init(i2c[68], 0x52, 8 * KiB); + at24c_eeprom_init(i2c[69], 0x52, 8 * KiB); + at24c_eeprom_init(i2c[70], 0x52, 8 * KiB); + at24c_eeprom_init(i2c[71], 0x52, 8 * KiB); - aspeed_eeprom_init(i2c[73], 0x53, 64 * KiB); + at24c_eeprom_init(i2c[73], 0x53, 8 * KiB); i2c_slave_create_simple(i2c[74], TYPE_TMP75, 0x49); i2c_slave_create_simple(i2c[74], TYPE_TMP75, 0x48); - aspeed_eeprom_init(i2c[76], 0x52, 64 * KiB); - aspeed_eeprom_init(i2c[77], 0x52, 64 * KiB); - aspeed_eeprom_init(i2c[78], 0x52, 64 * KiB); - aspeed_eeprom_init(i2c[79], 0x52, 64 * KiB); - aspeed_eeprom_init(i2c[28], 0x50, 2 * KiB); + at24c_eeprom_init(i2c[76], 0x52, 8 * KiB); + at24c_eeprom_init(i2c[77], 0x52, 8 * KiB); + at24c_eeprom_init(i2c[78], 0x52, 8 * KiB); + at24c_eeprom_init(i2c[79], 0x52, 8 * KiB); + at24c_eeprom_init(i2c[28], 0x50, 256); for (int i = 0; i < 8; i++) { - aspeed_eeprom_init(i2c[81 + i * 8], 0x56, 64 * KiB); + at24c_eeprom_init(i2c[81 + i * 8], 0x56, 64 * KiB); i2c_slave_create_simple(i2c[82 + i * 8], TYPE_TMP75, 0x48); i2c_slave_create_simple(i2c[83 + i * 8], TYPE_TMP75, 0x4b); i2c_slave_create_simple(i2c[84 + i * 8], TYPE_TMP75, 0x4a); @@ -915,7 +932,7 @@ static void fuji_bmc_i2c_init(AspeedMachineState *bmc) static void bletchley_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; I2CBus *i2c[13] = {}; for (int i = 0; i < 13; i++) { if ((i == 8) || (i == 11)) { @@ -961,7 +978,7 @@ static void bletchley_bmc_i2c_init(AspeedMachineState *bmc) static void fby35_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; I2CBus *i2c[16]; for (int i = 0; i < 16; i++) { @@ -975,11 +992,14 @@ static void fby35_i2c_init(AspeedMachineState *bmc) i2c_slave_create_simple(i2c[12], TYPE_LM75, 0x4e); i2c_slave_create_simple(i2c[12], TYPE_LM75, 0x4f); - aspeed_eeprom_init(i2c[4], 0x51, 128 * KiB); - aspeed_eeprom_init(i2c[6], 0x51, 128 * KiB); - aspeed_eeprom_init(i2c[8], 0x50, 32 * KiB); - aspeed_eeprom_init(i2c[11], 0x51, 128 * KiB); - aspeed_eeprom_init(i2c[11], 0x54, 128 * KiB); + at24c_eeprom_init(i2c[4], 0x51, 128 * KiB); + at24c_eeprom_init(i2c[6], 0x51, 128 * KiB); + at24c_eeprom_init_rom(i2c[8], 0x50, 32 * KiB, fby35_nic_fruid, + fby35_nic_fruid_len); + at24c_eeprom_init_rom(i2c[11], 0x51, 128 * KiB, fby35_bb_fruid, + fby35_bb_fruid_len); + at24c_eeprom_init_rom(i2c[11], 0x54, 128 * KiB, fby35_bmc_fruid, + fby35_bmc_fruid_len); /* * TODO: There is a multi-master i2c connection to an AST1030 MiniBMC on @@ -988,6 +1008,45 @@ static void fby35_i2c_init(AspeedMachineState *bmc) */ } +static void qcom_dc_scm_bmc_i2c_init(AspeedMachineState *bmc) +{ + AspeedSoCState *soc = bmc->soc; + + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 15), "tmp105", 0x4d); +} + +static void qcom_dc_scm_firework_i2c_init(AspeedMachineState *bmc) +{ + AspeedSoCState *soc = bmc->soc; + I2CSlave *therm_mux, *cpuvr_mux; + + /* Create the generic DC-SCM hardware */ + qcom_dc_scm_bmc_i2c_init(bmc); + + /* Now create the Firework specific hardware */ + + /* I2C7 CPUVR MUX */ + cpuvr_mux = i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), + "pca9546", 0x70); + i2c_slave_create_simple(pca954x_i2c_get_bus(cpuvr_mux, 0), "pca9548", 0x72); + i2c_slave_create_simple(pca954x_i2c_get_bus(cpuvr_mux, 1), "pca9548", 0x72); + i2c_slave_create_simple(pca954x_i2c_get_bus(cpuvr_mux, 2), "pca9548", 0x72); + i2c_slave_create_simple(pca954x_i2c_get_bus(cpuvr_mux, 3), "pca9548", 0x72); + + /* I2C8 Thermal Diodes*/ + therm_mux = i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 8), + "pca9548", 0x70); + i2c_slave_create_simple(pca954x_i2c_get_bus(therm_mux, 0), TYPE_LM75, 0x4C); + i2c_slave_create_simple(pca954x_i2c_get_bus(therm_mux, 1), TYPE_LM75, 0x4C); + i2c_slave_create_simple(pca954x_i2c_get_bus(therm_mux, 2), TYPE_LM75, 0x48); + i2c_slave_create_simple(pca954x_i2c_get_bus(therm_mux, 3), TYPE_LM75, 0x48); + i2c_slave_create_simple(pca954x_i2c_get_bus(therm_mux, 4), TYPE_LM75, 0x48); + + /* I2C9 Fan Controller (MAX31785) */ + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 9), "max31785", 0x52); + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 9), "max31785", 0x54); +} + static bool aspeed_get_mmio_exec(Object *obj, Error **errp) { return ASPEED_MACHINE(obj)->mmio_exec; @@ -1031,6 +1090,38 @@ static void aspeed_set_spi_model(Object *obj, const char *value, Error **errp) bmc->spi_model = g_strdup(value); } +static char *aspeed_get_bmc_console(Object *obj, Error **errp) +{ + AspeedMachineState *bmc = ASPEED_MACHINE(obj); + AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(bmc); + int uart_chosen = bmc->uart_chosen ? bmc->uart_chosen : amc->uart_default; + + return g_strdup_printf("uart%d", aspeed_uart_index(uart_chosen)); +} + +static void aspeed_set_bmc_console(Object *obj, const char *value, Error **errp) +{ + AspeedMachineState *bmc = ASPEED_MACHINE(obj); + AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(bmc); + AspeedSoCClass *sc = ASPEED_SOC_CLASS(object_class_by_name(amc->soc_name)); + int val; + int uart_first = aspeed_uart_first(sc); + int uart_last = aspeed_uart_last(sc); + + if (sscanf(value, "uart%u", &val) != 1) { + error_setg(errp, "Bad value for \"uart\" property"); + return; + } + + /* The number of UART depends on the SoC */ + if (val < uart_first || val > uart_last) { + error_setg(errp, "\"uart\" should be in range [%d - %d]", + uart_first, uart_last); + return; + } + bmc->uart_chosen = val + ASPEED_DEV_UART0; +} + static void aspeed_machine_class_props_init(ObjectClass *oc) { object_class_property_add_bool(oc, "execute-in-place", @@ -1039,6 +1130,11 @@ static void aspeed_machine_class_props_init(ObjectClass *oc) object_class_property_set_description(oc, "execute-in-place", "boot directly from CE0 flash device"); + object_class_property_add_str(oc, "bmc-console", aspeed_get_bmc_console, + aspeed_set_bmc_console); + object_class_property_set_description(oc, "bmc-console", + "Change the default UART to \"uartX\""); + object_class_property_add_str(oc, "fmc-model", aspeed_get_fmc_model, aspeed_set_fmc_model); object_class_property_set_description(oc, "fmc-model", @@ -1049,10 +1145,15 @@ static void aspeed_machine_class_props_init(ObjectClass *oc) "Change the SPI Flash model"); } -static int aspeed_soc_num_cpus(const char *soc_name) +static void aspeed_machine_class_init_cpus_defaults(MachineClass *mc) { - AspeedSoCClass *sc = ASPEED_SOC_CLASS(object_class_by_name(soc_name)); - return sc->num_cpus; + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(mc); + AspeedSoCClass *sc = ASPEED_SOC_CLASS(object_class_by_name(amc->soc_name)); + + mc->default_cpus = sc->num_cpus; + mc->min_cpus = sc->num_cpus; + mc->max_cpus = sc->num_cpus; + mc->valid_cpu_types = sc->valid_cpu_types; } static void aspeed_machine_class_init(ObjectClass *oc, void *data) @@ -1080,12 +1181,11 @@ static void aspeed_machine_palmetto_class_init(ObjectClass *oc, void *data) amc->soc_name = "ast2400-a1"; amc->hw_strap1 = PALMETTO_BMC_HW_STRAP1; amc->fmc_model = "n25q256a"; - amc->spi_model = "mx25l25635e"; + amc->spi_model = "mx25l25635f"; amc->num_cs = 1; amc->i2c_init = palmetto_bmc_i2c_init; mc->default_ram_size = 256 * MiB; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); + aspeed_machine_class_init_cpus_defaults(mc); }; static void aspeed_machine_quanta_q71l_class_init(ObjectClass *oc, void *data) @@ -1101,8 +1201,7 @@ static void aspeed_machine_quanta_q71l_class_init(ObjectClass *oc, void *data) amc->num_cs = 1; amc->i2c_init = quanta_q71l_bmc_i2c_init; mc->default_ram_size = 128 * MiB; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); + aspeed_machine_class_init_cpus_defaults(mc); } static void aspeed_machine_supermicrox11_bmc_class_init(ObjectClass *oc, @@ -1120,6 +1219,25 @@ static void aspeed_machine_supermicrox11_bmc_class_init(ObjectClass *oc, amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = palmetto_bmc_i2c_init; mc->default_ram_size = 256 * MiB; + aspeed_machine_class_init_cpus_defaults(mc); +} + +static void aspeed_machine_supermicro_x11spi_bmc_class_init(ObjectClass *oc, + void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Supermicro X11 SPI BMC (ARM1176)"; + amc->soc_name = "ast2500-a1"; + amc->hw_strap1 = SUPERMICRO_X11SPI_BMC_HW_STRAP1; + amc->fmc_model = "mx25l25635e"; + amc->spi_model = "mx25l25635e"; + amc->num_cs = 1; + amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; + amc->i2c_init = palmetto_bmc_i2c_init; + mc->default_ram_size = 512 * MiB; + aspeed_machine_class_init_cpus_defaults(mc); } static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, void *data) @@ -1131,12 +1249,28 @@ static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, void *data) amc->soc_name = "ast2500-a1"; amc->hw_strap1 = AST2500_EVB_HW_STRAP1; amc->fmc_model = "mx25l25635e"; - amc->spi_model = "mx25l25635e"; + amc->spi_model = "mx25l25635f"; amc->num_cs = 1; amc->i2c_init = ast2500_evb_i2c_init; mc->default_ram_size = 512 * MiB; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); + aspeed_machine_class_init_cpus_defaults(mc); +}; + +static void aspeed_machine_yosemitev2_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Facebook YosemiteV2 BMC (ARM1176)"; + amc->soc_name = "ast2500-a1"; + amc->hw_strap1 = AST2500_EVB_HW_STRAP1; + amc->hw_strap2 = 0; + amc->fmc_model = "n25q256a"; + amc->spi_model = "mx25l25635e"; + amc->num_cs = 2; + amc->i2c_init = yosemitev2_bmc_i2c_init; + mc->default_ram_size = 512 * MiB; + aspeed_machine_class_init_cpus_defaults(mc); }; static void aspeed_machine_romulus_class_init(ObjectClass *oc, void *data) @@ -1152,8 +1286,24 @@ static void aspeed_machine_romulus_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->i2c_init = romulus_bmc_i2c_init; mc->default_ram_size = 512 * MiB; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); + aspeed_machine_class_init_cpus_defaults(mc); +}; + +static void aspeed_machine_tiogapass_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Facebook Tiogapass BMC (ARM1176)"; + amc->soc_name = "ast2500-a1"; + amc->hw_strap1 = AST2500_EVB_HW_STRAP1; + amc->hw_strap2 = 0; + amc->fmc_model = "n25q256a"; + amc->spi_model = "mx25l25635e"; + amc->num_cs = 2; + amc->i2c_init = tiogapass_bmc_i2c_init; + mc->default_ram_size = 1 * GiB; + aspeed_machine_class_init_cpus_defaults(mc); }; static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, void *data) @@ -1169,8 +1319,7 @@ static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->i2c_init = sonorapass_bmc_i2c_init; mc->default_ram_size = 512 * MiB; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); + aspeed_machine_class_init_cpus_defaults(mc); }; static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, void *data) @@ -1181,13 +1330,12 @@ static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, void *data) mc->desc = "OpenPOWER Witherspoon BMC (ARM1176)"; amc->soc_name = "ast2500-a1"; amc->hw_strap1 = WITHERSPOON_BMC_HW_STRAP1; - amc->fmc_model = "mx25l25635e"; + amc->fmc_model = "mx25l25635f"; amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = witherspoon_bmc_i2c_init; mc->default_ram_size = 512 * MiB; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); + aspeed_machine_class_init_cpus_defaults(mc); }; static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, void *data) @@ -1206,8 +1354,7 @@ static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, void *data) ASPEED_MAC3_ON; amc->i2c_init = ast2600_evb_i2c_init; mc->default_ram_size = 1 * GiB; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); + aspeed_machine_class_init_cpus_defaults(mc); }; static void aspeed_machine_tacoma_class_init(ObjectClass *oc, void *data) @@ -1225,8 +1372,7 @@ static void aspeed_machine_tacoma_class_init(ObjectClass *oc, void *data) amc->macs_mask = ASPEED_MAC2_ON; amc->i2c_init = witherspoon_bmc_i2c_init; /* Same board layout */ mc->default_ram_size = 1 * GiB; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); + aspeed_machine_class_init_cpus_defaults(mc); }; static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data) @@ -1243,8 +1389,7 @@ static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data) amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = g220a_bmc_i2c_init; mc->default_ram_size = 1024 * MiB; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); + aspeed_machine_class_init_cpus_defaults(mc); }; static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, void *data) @@ -1261,8 +1406,7 @@ static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, void *data) amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = fp5280g2_bmc_i2c_init; mc->default_ram_size = 512 * MiB; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); + aspeed_machine_class_init_cpus_defaults(mc); }; static void aspeed_machine_rainier_class_init(ObjectClass *oc, void *data) @@ -1280,16 +1424,10 @@ static void aspeed_machine_rainier_class_init(ObjectClass *oc, void *data) amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = rainier_bmc_i2c_init; mc->default_ram_size = 1 * GiB; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); + aspeed_machine_class_init_cpus_defaults(mc); }; -/* On 32-bit hosts, lower RAM to 1G because of the 2047 MB limit */ -#if HOST_LONG_BITS == 32 -#define FUJI_BMC_RAM_SIZE (1 * GiB) -#else -#define FUJI_BMC_RAM_SIZE (2 * GiB) -#endif +#define FUJI_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB) static void aspeed_machine_fuji_class_init(ObjectClass *oc, void *data) { @@ -1307,10 +1445,11 @@ static void aspeed_machine_fuji_class_init(ObjectClass *oc, void *data) amc->i2c_init = fuji_bmc_i2c_init; amc->uart_default = ASPEED_DEV_UART1; mc->default_ram_size = FUJI_BMC_RAM_SIZE; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); + aspeed_machine_class_init_cpus_defaults(mc); }; +#define BLETCHLEY_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB) + static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1325,23 +1464,34 @@ static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON; amc->i2c_init = bletchley_bmc_i2c_init; - mc->default_ram_size = 512 * MiB; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); + mc->default_ram_size = BLETCHLEY_BMC_RAM_SIZE; + aspeed_machine_class_init_cpus_defaults(mc); } -static void fby35_reset(MachineState *state) +static void fby35_reset(MachineState *state, ShutdownCause reason) { AspeedMachineState *bmc = ASPEED_MACHINE(state); - AspeedGPIOState *gpio = &bmc->soc.gpio; + AspeedGPIOState *gpio = &bmc->soc->gpio; - qemu_devices_reset(); + qemu_devices_reset(reason); - /* Board ID */ + /* Board ID: 7 (Class-1, 4 slots) */ object_property_set_bool(OBJECT(gpio), "gpioV4", true, &error_fatal); object_property_set_bool(OBJECT(gpio), "gpioV5", true, &error_fatal); object_property_set_bool(OBJECT(gpio), "gpioV6", true, &error_fatal); object_property_set_bool(OBJECT(gpio), "gpioV7", false, &error_fatal); + + /* Slot presence pins, inverse polarity. (False means present) */ + object_property_set_bool(OBJECT(gpio), "gpioH4", false, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioH5", true, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioH6", true, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioH7", true, &error_fatal); + + /* Slot 12v power pins, normal polarity. (True means powered-on) */ + object_property_set_bool(OBJECT(gpio), "gpioB2", true, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioB3", false, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioB4", false, &error_fatal); + object_property_set_bool(OBJECT(gpio), "gpioB5", false, &error_fatal); } static void aspeed_machine_fby35_class_init(ObjectClass *oc, void *data) @@ -1357,6 +1507,7 @@ static void aspeed_machine_fby35_class_init(ObjectClass *oc, void *data) amc->i2c_init = fby35_i2c_init; /* FIXME: Replace this macro with something more general */ mc->default_ram_size = FUJI_BMC_RAM_SIZE; + aspeed_machine_class_init_cpus_defaults(mc); } #define AST1030_INTERNAL_FLASH_SIZE (1024 * 1024) @@ -1372,23 +1523,26 @@ static void aspeed_minibmc_machine_init(MachineState *machine) sysclk = clock_new(OBJECT(machine), "SYSCLK"); clock_set_hz(sysclk, SYSCLK_FRQ); - object_initialize_child(OBJECT(machine), "soc", &bmc->soc, amc->soc_name); - qdev_connect_clock_in(DEVICE(&bmc->soc), "sysclk", sysclk); + bmc->soc = ASPEED_SOC(object_new(amc->soc_name)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(bmc->soc)); + object_unref(OBJECT(bmc->soc)); + qdev_connect_clock_in(DEVICE(bmc->soc), "sysclk", sysclk); - qdev_prop_set_uint32(DEVICE(&bmc->soc), "uart-default", - amc->uart_default); - qdev_realize(DEVICE(&bmc->soc), NULL, &error_abort); + object_property_set_link(OBJECT(bmc->soc), "memory", + OBJECT(get_system_memory()), &error_abort); + connect_serial_hds_to_uarts(bmc); + qdev_realize(DEVICE(bmc->soc), NULL, &error_abort); - aspeed_board_init_flashes(&bmc->soc.fmc, + aspeed_board_init_flashes(&bmc->soc->fmc, bmc->fmc_model ? bmc->fmc_model : amc->fmc_model, amc->num_cs, 0); - aspeed_board_init_flashes(&bmc->soc.spi[0], + aspeed_board_init_flashes(&bmc->soc->spi[0], bmc->spi_model ? bmc->spi_model : amc->spi_model, amc->num_cs, amc->num_cs); - aspeed_board_init_flashes(&bmc->soc.spi[1], + aspeed_board_init_flashes(&bmc->soc->spi[1], bmc->spi_model ? bmc->spi_model : amc->spi_model, amc->num_cs, (amc->num_cs * 2)); @@ -1398,9 +1552,22 @@ static void aspeed_minibmc_machine_init(MachineState *machine) armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + 0, AST1030_INTERNAL_FLASH_SIZE); } +static void ast1030_evb_i2c_init(AspeedMachineState *bmc) +{ + AspeedSoCState *soc = bmc->soc; + + /* U10 24C08 connects to SDA/SCL Group 1 by default */ + uint8_t *eeprom_buf = g_malloc0(32 * 1024); + smbus_eeprom_init_one(aspeed_i2c_get_bus(&soc->i2c, 0), 0x50, eeprom_buf); + + /* U11 LM75 connects to SDA/SCL Group 2 by default */ + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 1), "tmp105", 0x4d); +} + static void aspeed_minibmc_machine_ast1030_evb_class_init(ObjectClass *oc, void *data) { @@ -1412,14 +1579,53 @@ static void aspeed_minibmc_machine_ast1030_evb_class_init(ObjectClass *oc, amc->hw_strap1 = 0; amc->hw_strap2 = 0; mc->init = aspeed_minibmc_machine_init; + amc->i2c_init = ast1030_evb_i2c_init; mc->default_ram_size = 0; - mc->default_cpus = mc->min_cpus = mc->max_cpus = 1; amc->fmc_model = "sst25vf032b"; amc->spi_model = "sst25vf032b"; amc->num_cs = 2; amc->macs_mask = 0; + aspeed_machine_class_init_cpus_defaults(mc); } +static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc, + void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Qualcomm DC-SCM V1 BMC (Cortex A7)"; + amc->soc_name = "ast2600-a3"; + amc->hw_strap1 = QCOM_DC_SCM_V1_BMC_HW_STRAP1; + amc->hw_strap2 = QCOM_DC_SCM_V1_BMC_HW_STRAP2; + amc->fmc_model = "n25q512a"; + amc->spi_model = "n25q512a"; + amc->num_cs = 2; + amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; + amc->i2c_init = qcom_dc_scm_bmc_i2c_init; + mc->default_ram_size = 1 * GiB; + aspeed_machine_class_init_cpus_defaults(mc); +}; + +static void aspeed_machine_qcom_firework_class_init(ObjectClass *oc, + void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Qualcomm DC-SCM V1/Firework BMC (Cortex A7)"; + amc->soc_name = "ast2600-a3"; + amc->hw_strap1 = QCOM_DC_SCM_V1_BMC_HW_STRAP1; + amc->hw_strap2 = QCOM_DC_SCM_V1_BMC_HW_STRAP2; + amc->fmc_model = "n25q512a"; + amc->spi_model = "n25q512a"; + amc->num_cs = 2; + amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; + amc->i2c_init = qcom_dc_scm_firework_i2c_init; + mc->default_ram_size = 1 * GiB; + aspeed_machine_class_init_cpus_defaults(mc); +}; + static const TypeInfo aspeed_machine_types[] = { { .name = MACHINE_TYPE_NAME("palmetto-bmc"), @@ -1429,6 +1635,10 @@ static const TypeInfo aspeed_machine_types[] = { .name = MACHINE_TYPE_NAME("supermicrox11-bmc"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_machine_supermicrox11_bmc_class_init, + }, { + .name = MACHINE_TYPE_NAME("supermicro-x11spi-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_supermicro_x11spi_bmc_class_init, }, { .name = MACHINE_TYPE_NAME("ast2500-evb"), .parent = TYPE_ASPEED_MACHINE, @@ -1449,14 +1659,30 @@ static const TypeInfo aspeed_machine_types[] = { .name = MACHINE_TYPE_NAME("ast2600-evb"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_machine_ast2600_evb_class_init, + }, { + .name = MACHINE_TYPE_NAME("yosemitev2-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_yosemitev2_class_init, }, { .name = MACHINE_TYPE_NAME("tacoma-bmc"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_machine_tacoma_class_init, + }, { + .name = MACHINE_TYPE_NAME("tiogapass-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_tiogapass_class_init, }, { .name = MACHINE_TYPE_NAME("g220a-bmc"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_machine_g220a_class_init, + }, { + .name = MACHINE_TYPE_NAME("qcom-dc-scm-v1-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_qcom_dc_scm_v1_class_init, + }, { + .name = MACHINE_TYPE_NAME("qcom-firework-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_qcom_firework_class_init, }, { .name = MACHINE_TYPE_NAME("fp5280g2-bmc"), .parent = TYPE_ASPEED_MACHINE, diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index d534541684..9f98ad8e87 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -21,16 +21,22 @@ static const hwaddr aspeed_soc_ast1030_memmap[] = { [ASPEED_DEV_SRAM] = 0x00000000, - [ASPEED_DEV_SBC] = 0x79000000, + [ASPEED_DEV_SECSRAM] = 0x79000000, [ASPEED_DEV_IOMEM] = 0x7E600000, [ASPEED_DEV_PWM] = 0x7E610000, [ASPEED_DEV_FMC] = 0x7E620000, [ASPEED_DEV_SPI1] = 0x7E630000, [ASPEED_DEV_SPI2] = 0x7E640000, + [ASPEED_DEV_UDC] = 0x7E6A2000, + [ASPEED_DEV_HACE] = 0x7E6D0000, [ASPEED_DEV_SCU] = 0x7E6E2000, + [ASPEED_DEV_JTAG0] = 0x7E6E4000, + [ASPEED_DEV_JTAG1] = 0x7E6E4100, [ASPEED_DEV_ADC] = 0x7E6E9000, + [ASPEED_DEV_ESPI] = 0x7E6EE000, [ASPEED_DEV_SBC] = 0x7E6F2000, [ASPEED_DEV_GPIO] = 0x7E780000, + [ASPEED_DEV_SGPIOM] = 0x7E780500, [ASPEED_DEV_TIMER1] = 0x7E782000, [ASPEED_DEV_UART1] = 0x7E783000, [ASPEED_DEV_UART2] = 0x7E78D000, @@ -47,6 +53,8 @@ static const hwaddr aspeed_soc_ast1030_memmap[] = { [ASPEED_DEV_UART13] = 0x7E790700, [ASPEED_DEV_WDT] = 0x7E785000, [ASPEED_DEV_LPC] = 0x7E789000, + [ASPEED_DEV_PECI] = 0x7E78B000, + [ASPEED_DEV_I3C] = 0x7E7A0000, [ASPEED_DEV_I2C] = 0x7E7B0000, }; @@ -75,24 +83,33 @@ static const int aspeed_soc_ast1030_irqmap[] = { [ASPEED_DEV_TIMER8] = 23, [ASPEED_DEV_WDT] = 24, [ASPEED_DEV_LPC] = 35, + [ASPEED_DEV_PECI] = 38, [ASPEED_DEV_FMC] = 39, + [ASPEED_DEV_ESPI] = 42, [ASPEED_DEV_PWM] = 44, [ASPEED_DEV_ADC] = 46, [ASPEED_DEV_SPI1] = 65, [ASPEED_DEV_SPI2] = 66, + [ASPEED_DEV_I3C] = 102, /* 102 -> 105 */ [ASPEED_DEV_I2C] = 110, /* 110 ~ 123 */ [ASPEED_DEV_KCS] = 138, /* 138 -> 142 */ + [ASPEED_DEV_UDC] = 9, + [ASPEED_DEV_SGPIOM] = 51, + [ASPEED_DEV_JTAG0] = 27, + [ASPEED_DEV_JTAG1] = 53, }; static qemu_irq aspeed_soc_ast1030_get_irq(AspeedSoCState *s, int dev) { + Aspeed10x0SoCState *a = ASPEED10X0_SOC(s); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - return qdev_get_gpio_in(DEVICE(&s->armv7m), sc->irqmap[dev]); + return qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[dev]); } static void aspeed_soc_ast1030_init(Object *obj) { + Aspeed10x0SoCState *a = ASPEED10X0_SOC(obj); AspeedSoCState *s = ASPEED_SOC(obj); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); char socname[8]; @@ -103,7 +120,7 @@ static void aspeed_soc_ast1030_init(Object *obj) g_assert_not_reached(); } - object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M); + object_initialize_child(obj, "armv7m", &a->armv7m, TYPE_ARMV7M); s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); @@ -114,6 +131,11 @@ static void aspeed_soc_ast1030_init(Object *obj) object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu), "hw-strap1"); object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu), "hw-strap2"); + snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname); + object_initialize_child(obj, "i2c", &s->i2c, typename); + + object_initialize_child(obj, "i3c", &s->i3c, TYPE_ASPEED_I3C); + snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); object_initialize_child(obj, "timerctrl", &s->timerctrl, typename); @@ -130,6 +152,8 @@ static void aspeed_soc_ast1030_init(Object *obj) object_initialize_child(obj, "lpc", &s->lpc, TYPE_ASPEED_LPC); + object_initialize_child(obj, "peci", &s->peci, TYPE_ASPEED_PECI); + object_initialize_child(obj, "sbc", &s->sbc, TYPE_ASPEED_SBC); for (i = 0; i < sc->wdts_num; i++) { @@ -137,18 +161,39 @@ static void aspeed_soc_ast1030_init(Object *obj) object_initialize_child(obj, "wdt[*]", &s->wdt[i], typename); } + for (i = 0; i < sc->uarts_num; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); + } + snprintf(typename, sizeof(typename), "aspeed.gpio-%s", socname); object_initialize_child(obj, "gpio", &s->gpio, typename); + + snprintf(typename, sizeof(typename), "aspeed.hace-%s", socname); + object_initialize_child(obj, "hace", &s->hace, typename); + + object_initialize_child(obj, "iomem", &s->iomem, TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "sbc-unimplemented", &s->sbc_unimplemented, + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "pwm", &s->pwm, TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "espi", &s->espi, TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "udc", &s->udc, TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "sgpiom", &s->sgpiom, + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "jtag[0]", &s->jtag[0], + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "jtag[1]", &s->jtag[1], + TYPE_UNIMPLEMENTED_DEVICE); } static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) { + Aspeed10x0SoCState *a = ASPEED10X0_SOC(dev_soc); AspeedSoCState *s = ASPEED_SOC(dev_soc); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - MemoryRegion *system_memory = get_system_memory(); DeviceState *armv7m; Error *err = NULL; int i; + g_autofree char *sram_name = NULL; if (!clock_has_source(s->sysclk)) { error_setg(errp, "sysclk clock must be wired up by the board code"); @@ -156,43 +201,89 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) } /* General I/O memory space to catch all unimplemented device */ - create_unimplemented_device("aspeed.sbc", - sc->memmap[ASPEED_DEV_SBC], - 0x40000); - create_unimplemented_device("aspeed.io", - sc->memmap[ASPEED_DEV_IOMEM], - ASPEED_SOC_IOMEM_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), "aspeed.io", + sc->memmap[ASPEED_DEV_IOMEM], + ASPEED_SOC_IOMEM_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->sbc_unimplemented), + "aspeed.sbc", sc->memmap[ASPEED_DEV_SBC], + 0x40000); /* AST1030 CPU Core */ - armv7m = DEVICE(&s->armv7m); + armv7m = DEVICE(&a->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 256); - qdev_prop_set_string(armv7m, "cpu-type", sc->cpu_type); + qdev_prop_set_string(armv7m, "cpu-type", aspeed_soc_cpu_type(sc)); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); - object_property_set_link(OBJECT(&s->armv7m), "memory", - OBJECT(system_memory), &error_abort); - sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), &error_abort); + object_property_set_link(OBJECT(&a->armv7m), "memory", + OBJECT(s->memory), &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&a->armv7m), &error_abort); /* Internal SRAM */ - memory_region_init_ram(&s->sram, NULL, "aspeed.sram", sc->sram_size, &err); + sram_name = g_strdup_printf("aspeed.sram.%d", + CPU(a->armv7m.cpu)->cpu_index); + memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, &err); if (err != NULL) { error_propagate(errp, err); return; } - memory_region_add_subregion(system_memory, + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SRAM], &s->sram); + memory_region_init_ram(&s->secsram, OBJECT(s), "sec.sram", + sc->secsram_size, &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SECSRAM], + &s->secsram); /* SCU */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + + /* I2C */ + + object_property_set_link(OBJECT(&s->i2c), "dram", OBJECT(&s->sram), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); + for (i = 0; i < ASPEED_I2C_GET_CLASS(&s->i2c)->num_busses; i++) { + qemu_irq irq = qdev_get_gpio_in(DEVICE(&a->armv7m), + sc->irqmap[ASPEED_DEV_I2C] + i); + /* The AST1030 I2C controller has one IRQ per bus. */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c.busses[i]), 0, irq); + } + + /* I3C */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->i3c), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i3c), 0, sc->memmap[ASPEED_DEV_I3C]); + for (i = 0; i < ASPEED_I3C_NR_DEVICES; i++) { + qemu_irq irq = qdev_get_gpio_in(DEVICE(&a->armv7m), + sc->irqmap[ASPEED_DEV_I3C] + i); + /* The AST1030 I3C controller has one IRQ per bus. */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i3c.devices[i]), 0, irq); + } + + /* PECI */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->peci), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->peci), 0, + sc->memmap[ASPEED_DEV_PECI]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_PECI)); /* LPC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); /* Connect the LPC IRQ to the GIC. It is otherwise unused. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, @@ -202,23 +293,25 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) * On the AST1030 LPC subdevice IRQs are connected straight to the GIC. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_1, - qdev_get_gpio_in(DEVICE(&s->armv7m), + qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_1)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_2, - qdev_get_gpio_in(DEVICE(&s->armv7m), + qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_2)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_3, - qdev_get_gpio_in(DEVICE(&s->armv7m), + qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_3)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_4, - qdev_get_gpio_in(DEVICE(&s->armv7m), + qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_4)); /* UART */ - aspeed_soc_uart_init(s); + if (!aspeed_soc_uart_realize(s, errp)) { + return; + } /* Timer */ object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&s->scu), @@ -226,7 +319,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->timerctrl), 0, sc->memmap[ASPEED_DEV_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); @@ -237,7 +330,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); @@ -247,8 +340,8 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->fmc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 1, ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); @@ -260,9 +353,9 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 0, sc->memmap[ASPEED_DEV_SPI1 + i]); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 1, ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); } @@ -270,64 +363,100 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sbc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sbc), 0, sc->memmap[ASPEED_DEV_SBC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sbc), 0, sc->memmap[ASPEED_DEV_SBC]); + + /* HACE */ + object_property_set_link(OBJECT(&s->hace), "dram", OBJECT(&s->sram), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->hace), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->hace), 0, + sc->memmap[ASPEED_DEV_HACE]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); /* Watch dog */ for (i = 0; i < sc->wdts_num; i++) { AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(&s->wdt[i]); + hwaddr wdt_offset = sc->memmap[ASPEED_DEV_WDT] + i * awc->iosize; object_property_set_link(OBJECT(&s->wdt[i]), "scu", OBJECT(&s->scu), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, - sc->memmap[ASPEED_DEV_WDT] + i * awc->offset); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->wdt[i]), 0, wdt_offset); } /* GPIO */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio), 0, + sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); + + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->pwm), "aspeed.pwm", + sc->memmap[ASPEED_DEV_PWM], 0x100); + + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->espi), "aspeed.espi", + sc->memmap[ASPEED_DEV_ESPI], 0x800); + + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->udc), "aspeed.udc", + sc->memmap[ASPEED_DEV_UDC], 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->sgpiom), "aspeed.sgpiom", + sc->memmap[ASPEED_DEV_SGPIOM], 0x100); + + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->jtag[0]), "aspeed.jtag", + sc->memmap[ASPEED_DEV_JTAG0], 0x20); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->jtag[1]), "aspeed.jtag", + sc->memmap[ASPEED_DEV_JTAG1], 0x20); } static void aspeed_soc_ast1030_class_init(ObjectClass *klass, void *data) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m4"), /* TODO cortex-m4f */ + NULL + }; DeviceClass *dc = DEVICE_CLASS(klass); AspeedSoCClass *sc = ASPEED_SOC_CLASS(dc); + /* Reason: The Aspeed SoC can only be instantiated from a board */ + dc->user_creatable = false; dc->realize = aspeed_soc_ast1030_realize; sc->name = "ast1030-a1"; - sc->cpu_type = ARM_CPU_TYPE_NAME("cortex-m4"); + sc->valid_cpu_types = valid_cpu_types; sc->silicon_rev = AST1030_A1_SILICON_REV; sc->sram_size = 0xc0000; + sc->secsram_size = 0x40000; /* 256 * KiB */ sc->spis_num = 2; sc->ehcis_num = 0; sc->wdts_num = 4; sc->macs_num = 1; sc->uarts_num = 13; + sc->uarts_base = ASPEED_DEV_UART1; sc->irqmap = aspeed_soc_ast1030_irqmap; sc->memmap = aspeed_soc_ast1030_memmap; sc->num_cpus = 1; sc->get_irq = aspeed_soc_ast1030_get_irq; } -static const TypeInfo aspeed_soc_ast1030_type_info = { - .name = "ast1030-a1", - .parent = TYPE_ASPEED_SOC, - .instance_size = sizeof(AspeedSoCState), - .instance_init = aspeed_soc_ast1030_init, - .class_init = aspeed_soc_ast1030_class_init, - .class_size = sizeof(AspeedSoCClass), +static const TypeInfo aspeed_soc_ast10x0_types[] = { + { + .name = TYPE_ASPEED10X0_SOC, + .parent = TYPE_ASPEED_SOC, + .instance_size = sizeof(Aspeed10x0SoCState), + .abstract = true, + }, { + .name = "ast1030-a1", + .parent = TYPE_ASPEED10X0_SOC, + .instance_init = aspeed_soc_ast1030_init, + .class_init = aspeed_soc_ast1030_class_init, + }, }; -static void aspeed_soc_register_types(void) -{ - type_register_static(&aspeed_soc_ast1030_type_info); -} - -type_init(aspeed_soc_register_types) +DEFINE_TYPES(aspeed_soc_ast10x0_types) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_ast2400.c similarity index 71% rename from hw/arm/aspeed_soc.c rename to hw/arm/aspeed_ast2400.c index 30574d4276..d125886207 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_ast2400.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/misc/unimp.h" #include "hw/arm/aspeed_soc.h" @@ -20,10 +21,12 @@ #include "hw/i2c/aspeed_i2c.h" #include "net/net.h" #include "sysemu/sysemu.h" +#include "target/arm/cpu-qom.h" #define ASPEED_SOC_IOMEM_SIZE 0x00200000 static const hwaddr aspeed_soc_ast2400_memmap[] = { + [ASPEED_DEV_SPI_BOOT] = 0x00000000, [ASPEED_DEV_IOMEM] = 0x1E600000, [ASPEED_DEV_FMC] = 0x1E620000, [ASPEED_DEV_SPI1] = 0x1E630000, @@ -45,6 +48,7 @@ static const hwaddr aspeed_soc_ast2400_memmap[] = { [ASPEED_DEV_LPC] = 0x1E789000, [ASPEED_DEV_IBT] = 0x1E789140, [ASPEED_DEV_I2C] = 0x1E78A000, + [ASPEED_DEV_PECI] = 0x1E78B000, [ASPEED_DEV_ETH1] = 0x1E660000, [ASPEED_DEV_ETH2] = 0x1E680000, [ASPEED_DEV_UART1] = 0x1E783000, @@ -57,6 +61,7 @@ static const hwaddr aspeed_soc_ast2400_memmap[] = { }; static const hwaddr aspeed_soc_ast2500_memmap[] = { + [ASPEED_DEV_SPI_BOOT] = 0x00000000, [ASPEED_DEV_IOMEM] = 0x1E600000, [ASPEED_DEV_FMC] = 0x1E620000, [ASPEED_DEV_SPI1] = 0x1E630000, @@ -80,6 +85,7 @@ static const hwaddr aspeed_soc_ast2500_memmap[] = { [ASPEED_DEV_LPC] = 0x1E789000, [ASPEED_DEV_IBT] = 0x1E789140, [ASPEED_DEV_I2C] = 0x1E78A000, + [ASPEED_DEV_PECI] = 0x1E78B000, [ASPEED_DEV_ETH1] = 0x1E660000, [ASPEED_DEV_ETH2] = 0x1E680000, [ASPEED_DEV_UART1] = 0x1E783000, @@ -118,6 +124,7 @@ static const int aspeed_soc_ast2400_irqmap[] = { [ASPEED_DEV_PWM] = 28, [ASPEED_DEV_LPC] = 8, [ASPEED_DEV_I2C] = 12, + [ASPEED_DEV_PECI] = 15, [ASPEED_DEV_ETH1] = 2, [ASPEED_DEV_ETH2] = 3, [ASPEED_DEV_XDMA] = 6, @@ -129,13 +136,15 @@ static const int aspeed_soc_ast2400_irqmap[] = { static qemu_irq aspeed_soc_ast2400_get_irq(AspeedSoCState *s, int dev) { + Aspeed2400SoCState *a = ASPEED2400_SOC(s); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - return qdev_get_gpio_in(DEVICE(&s->vic), sc->irqmap[dev]); + return qdev_get_gpio_in(DEVICE(&a->vic), sc->irqmap[dev]); } -static void aspeed_soc_init(Object *obj) +static void aspeed_ast2400_soc_init(Object *obj) { + Aspeed2400SoCState *a = ASPEED2400_SOC(obj); AspeedSoCState *s = ASPEED_SOC(obj); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); int i; @@ -147,7 +156,8 @@ static void aspeed_soc_init(Object *obj) } for (i = 0; i < sc->num_cpus; i++) { - object_initialize_child(obj, "cpu[*]", &s->cpu[i], sc->cpu_type); + object_initialize_child(obj, "cpu[*]", &a->cpu[i], + aspeed_soc_cpu_type(sc)); } snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname); @@ -161,7 +171,7 @@ static void aspeed_soc_init(Object *obj) object_property_add_alias(obj, "hw-prot-key", OBJECT(&s->scu), "hw-prot-key"); - object_initialize_child(obj, "vic", &s->vic, TYPE_ASPEED_VIC); + object_initialize_child(obj, "vic", &a->vic, TYPE_ASPEED_VIC); object_initialize_child(obj, "rtc", &s->rtc, TYPE_ASPEED_RTC); @@ -174,6 +184,8 @@ static void aspeed_soc_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname); object_initialize_child(obj, "i2c", &s->i2c, typename); + object_initialize_child(obj, "peci", &s->peci, TYPE_ASPEED_PECI); + snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname); object_initialize_child(obj, "fmc", &s->fmc, typename); @@ -191,8 +203,6 @@ static void aspeed_soc_init(Object *obj) object_initialize_child(obj, "sdmc", &s->sdmc, typename); object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), "ram-size"); - object_property_add_alias(obj, "max-ram-size", OBJECT(&s->sdmc), - "max-ram-size"); for (i = 0; i < sc->wdts_num; i++) { snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); @@ -204,6 +214,10 @@ static void aspeed_soc_init(Object *obj) TYPE_FTGMAC100); } + for (i = 0; i < sc->uarts_num; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); + } + snprintf(typename, sizeof(typename), TYPE_ASPEED_XDMA "-%s", socname); object_initialize_child(obj, "xdma", &s->xdma, typename); @@ -224,61 +238,73 @@ static void aspeed_soc_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.hace-%s", socname); object_initialize_child(obj, "hace", &s->hace, typename); + + object_initialize_child(obj, "iomem", &s->iomem, TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "video", &s->video, TYPE_UNIMPLEMENTED_DEVICE); } -static void aspeed_soc_realize(DeviceState *dev, Error **errp) +static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) { int i; + Aspeed2400SoCState *a = ASPEED2400_SOC(dev); AspeedSoCState *s = ASPEED_SOC(dev); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - Error *err = NULL; + g_autofree char *sram_name = NULL; + + /* Default boot region (SPI memory or ROMs) */ + memory_region_init(&s->spi_boot_container, OBJECT(s), + "aspeed.spi_boot_container", 0x10000000); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SPI_BOOT], + &s->spi_boot_container); /* IO space */ - create_unimplemented_device("aspeed_soc.io", sc->memmap[ASPEED_DEV_IOMEM], - ASPEED_SOC_IOMEM_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), "aspeed.io", + sc->memmap[ASPEED_DEV_IOMEM], + ASPEED_SOC_IOMEM_SIZE); /* Video engine stub */ - create_unimplemented_device("aspeed.video", sc->memmap[ASPEED_DEV_VIDEO], - 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->video), "aspeed.video", + sc->memmap[ASPEED_DEV_VIDEO], 0x1000); /* CPU */ for (i = 0; i < sc->num_cpus; i++) { - if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { + object_property_set_link(OBJECT(&a->cpu[i]), "memory", + OBJECT(s->memory), &error_abort); + if (!qdev_realize(DEVICE(&a->cpu[i]), NULL, errp)) { return; } } /* SRAM */ - memory_region_init_ram(&s->sram, OBJECT(dev), "aspeed.sram", - sc->sram_size, &err); - if (err) { - error_propagate(errp, err); + sram_name = g_strdup_printf("aspeed.sram.%d", CPU(&a->cpu[0])->cpu_index); + if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, + errp)) { return; } - memory_region_add_subregion(get_system_memory(), + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SRAM], &s->sram); /* SCU */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); /* VIC */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->vic), errp)) { + if (!sysbus_realize(SYS_BUS_DEVICE(&a->vic), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, sc->memmap[ASPEED_DEV_VIC]); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 0, - qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ)); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 1, - qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ)); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->vic), 0, sc->memmap[ASPEED_DEV_VIC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&a->vic), 0, + qdev_get_gpio_in(DEVICE(&a->cpu), ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&a->vic), 1, + qdev_get_gpio_in(DEVICE(&a->cpu), ARM_CPU_FIQ)); /* RTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->rtc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_DEV_RTC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_DEV_RTC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_RTC)); @@ -288,7 +314,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->timerctrl), 0, sc->memmap[ASPEED_DEV_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); @@ -299,12 +325,14 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); /* UART */ - aspeed_soc_uart_init(s); + if (!aspeed_soc_uart_realize(s, errp)) { + return; + } /* I2C */ object_property_set_link(OBJECT(&s->i2c), "dram", OBJECT(s->dram_mr), @@ -312,30 +340,45 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), 0, aspeed_soc_get_irq(s, ASPEED_DEV_I2C)); + /* PECI */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->peci), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->peci), 0, + sc->memmap[ASPEED_DEV_PECI]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_PECI)); + /* FMC, The number of CS is set at the board level */ object_property_set_link(OBJECT(&s->fmc), "dram", OBJECT(s->dram_mr), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->fmc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 1, ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); + /* Set up an alias on the FMC CE0 region (boot default) */ + MemoryRegion *fmc0_mmio = &s->fmc.flashes[0].mmio; + memory_region_init_alias(&s->spi_boot, OBJECT(s), "aspeed.spi_boot", + fmc0_mmio, 0, memory_region_size(fmc0_mmio)); + memory_region_add_subregion(&s->spi_boot_container, 0x0, &s->spi_boot); + /* SPI */ for (i = 0; i < sc->spis_num; i++) { if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 0, sc->memmap[ASPEED_DEV_SPI1 + i]); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 1, ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); } @@ -344,7 +387,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ehci[i]), 0, sc->memmap[ASPEED_DEV_EHCI1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_EHCI1 + i)); @@ -354,19 +397,25 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdmc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, sc->memmap[ASPEED_DEV_SDMC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdmc), 0, + sc->memmap[ASPEED_DEV_SDMC]); /* Watch dog */ for (i = 0; i < sc->wdts_num; i++) { AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(&s->wdt[i]); + hwaddr wdt_offset = sc->memmap[ASPEED_DEV_WDT] + i * awc->iosize; object_property_set_link(OBJECT(&s->wdt[i]), "scu", OBJECT(&s->scu), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, - sc->memmap[ASPEED_DEV_WDT] + i * awc->offset); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->wdt[i]), 0, wdt_offset); + } + + /* RAM */ + if (!aspeed_soc_dram_init(s, errp)) { + return; } /* Net */ @@ -376,7 +425,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->ftgmac100[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, sc->memmap[ASPEED_DEV_ETH1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ETH1 + i)); @@ -386,7 +435,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->xdma), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->xdma), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->xdma), 0, sc->memmap[ASPEED_DEV_XDMA]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0, aspeed_soc_get_irq(s, ASPEED_DEV_XDMA)); @@ -395,7 +444,8 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio), 0, + sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); @@ -403,7 +453,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdhci), 0, sc->memmap[ASPEED_DEV_SDHCI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, aspeed_soc_get_irq(s, ASPEED_DEV_SDHCI)); @@ -412,7 +462,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); /* Connect the LPC IRQ to the VIC */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, @@ -445,43 +495,27 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->hace), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->hace), 0, + sc->memmap[ASPEED_DEV_HACE]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); } -static Property aspeed_soc_properties[] = { - DEFINE_PROP_LINK("dram", AspeedSoCState, dram_mr, TYPE_MEMORY_REGION, - MemoryRegion *), - DEFINE_PROP_UINT32("uart-default", AspeedSoCState, uart_default, - ASPEED_DEV_UART5), - DEFINE_PROP_END_OF_LIST(), -}; - -static void aspeed_soc_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = aspeed_soc_realize; - /* Reason: Uses serial_hds and nd_table in realize() directly */ - dc->user_creatable = false; - device_class_set_props(dc, aspeed_soc_properties); -} - -static const TypeInfo aspeed_soc_type_info = { - .name = TYPE_ASPEED_SOC, - .parent = TYPE_DEVICE, - .instance_size = sizeof(AspeedSoCState), - .class_size = sizeof(AspeedSoCClass), - .class_init = aspeed_soc_class_init, - .abstract = true, -}; static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("arm926"), + NULL + }; AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = aspeed_ast2400_soc_realize; + /* Reason: Uses serial_hds and nd_table in realize() directly */ + dc->user_creatable = false; sc->name = "ast2400-a1"; - sc->cpu_type = ARM_CPU_TYPE_NAME("arm926"); + sc->valid_cpu_types = valid_cpu_types; sc->silicon_rev = AST2400_A1_SILICON_REV; sc->sram_size = 0x8000; sc->spis_num = 1; @@ -489,26 +523,28 @@ static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data) sc->wdts_num = 2; sc->macs_num = 2; sc->uarts_num = 5; + sc->uarts_base = ASPEED_DEV_UART1; sc->irqmap = aspeed_soc_ast2400_irqmap; sc->memmap = aspeed_soc_ast2400_memmap; sc->num_cpus = 1; sc->get_irq = aspeed_soc_ast2400_get_irq; } -static const TypeInfo aspeed_soc_ast2400_type_info = { - .name = "ast2400-a1", - .parent = TYPE_ASPEED_SOC, - .instance_init = aspeed_soc_init, - .instance_size = sizeof(AspeedSoCState), - .class_init = aspeed_soc_ast2400_class_init, -}; - static void aspeed_soc_ast2500_class_init(ObjectClass *oc, void *data) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("arm1176"), + NULL + }; AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = aspeed_ast2400_soc_realize; + /* Reason: Uses serial_hds and nd_table in realize() directly */ + dc->user_creatable = false; sc->name = "ast2500-a1"; - sc->cpu_type = ARM_CPU_TYPE_NAME("arm1176"); + sc->valid_cpu_types = valid_cpu_types; sc->silicon_rev = AST2500_A1_SILICON_REV; sc->sram_size = 0x9000; sc->spis_num = 2; @@ -516,48 +552,29 @@ static void aspeed_soc_ast2500_class_init(ObjectClass *oc, void *data) sc->wdts_num = 3; sc->macs_num = 2; sc->uarts_num = 5; + sc->uarts_base = ASPEED_DEV_UART1; sc->irqmap = aspeed_soc_ast2500_irqmap; sc->memmap = aspeed_soc_ast2500_memmap; sc->num_cpus = 1; sc->get_irq = aspeed_soc_ast2400_get_irq; } -static const TypeInfo aspeed_soc_ast2500_type_info = { - .name = "ast2500-a1", - .parent = TYPE_ASPEED_SOC, - .instance_init = aspeed_soc_init, - .instance_size = sizeof(AspeedSoCState), - .class_init = aspeed_soc_ast2500_class_init, -}; -static void aspeed_soc_register_types(void) -{ - type_register_static(&aspeed_soc_type_info); - type_register_static(&aspeed_soc_ast2400_type_info); - type_register_static(&aspeed_soc_ast2500_type_info); +static const TypeInfo aspeed_soc_ast2400_types[] = { + { + .name = TYPE_ASPEED2400_SOC, + .parent = TYPE_ASPEED_SOC, + .instance_init = aspeed_ast2400_soc_init, + .instance_size = sizeof(Aspeed2400SoCState), + .abstract = true, + }, { + .name = "ast2400-a1", + .parent = TYPE_ASPEED2400_SOC, + .class_init = aspeed_soc_ast2400_class_init, + }, { + .name = "ast2500-a1", + .parent = TYPE_ASPEED2400_SOC, + .class_init = aspeed_soc_ast2500_class_init, + }, }; -type_init(aspeed_soc_register_types); - -qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev) -{ - return ASPEED_SOC_GET_CLASS(s)->get_irq(s, dev); -} - -void aspeed_soc_uart_init(AspeedSoCState *s) -{ - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - int i, uart; - - /* Attach an 8250 to the IO space as our UART */ - serial_mm_init(get_system_memory(), sc->memmap[s->uart_default], 2, - aspeed_soc_get_irq(s, s->uart_default), 38400, - serial_hd(0), DEVICE_LITTLE_ENDIAN); - for (i = 1, uart = ASPEED_DEV_UART1; i < sc->uarts_num; i++, uart++) { - if (uart == s->uart_default) { - uart++; - } - serial_mm_init(get_system_memory(), sc->memmap[uart], 2, - aspeed_soc_get_irq(s, uart), 38400, - serial_hd(i), DEVICE_LITTLE_ENDIAN); - } -} +DEFINE_TYPES(aspeed_soc_ast2400_types) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index b0a4199b69..31713de74a 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -16,11 +16,13 @@ #include "hw/i2c/aspeed_i2c.h" #include "net/net.h" #include "sysemu/sysemu.h" +#include "target/arm/cpu-qom.h" #define ASPEED_SOC_IOMEM_SIZE 0x00200000 #define ASPEED_SOC_DPMCU_SIZE 0x00040000 static const hwaddr aspeed_soc_ast2600_memmap[] = { + [ASPEED_DEV_SPI_BOOT] = 0x00000000, [ASPEED_DEV_SRAM] = 0x10000000, [ASPEED_DEV_DPMCU] = 0x18000000, /* 0x16000000 0x17FFFFFF : AHB BUS do LPC Bus bridge */ @@ -59,6 +61,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = { [ASPEED_DEV_LPC] = 0x1E789000, [ASPEED_DEV_IBT] = 0x1E789140, [ASPEED_DEV_I2C] = 0x1E78A000, + [ASPEED_DEV_PECI] = 0x1E78B000, [ASPEED_DEV_UART1] = 0x1E783000, [ASPEED_DEV_UART2] = 0x1E78D000, [ASPEED_DEV_UART3] = 0x1E78E000, @@ -73,6 +76,8 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = { [ASPEED_DEV_UART12] = 0x1E790600, [ASPEED_DEV_UART13] = 0x1E790700, [ASPEED_DEV_VUART] = 0x1E787000, + [ASPEED_DEV_FSI1] = 0x1E79B000, + [ASPEED_DEV_FSI2] = 0x1E79B100, [ASPEED_DEV_I3C] = 0x1E7A0000, [ASPEED_DEV_SDRAM] = 0x80000000, }; @@ -122,6 +127,7 @@ static const int aspeed_soc_ast2600_irqmap[] = { [ASPEED_DEV_LPC] = 35, [ASPEED_DEV_IBT] = 143, [ASPEED_DEV_I2C] = 110, /* 110 -> 125 */ + [ASPEED_DEV_PECI] = 38, [ASPEED_DEV_ETH1] = 2, [ASPEED_DEV_ETH2] = 3, [ASPEED_DEV_HACE] = 4, @@ -129,18 +135,22 @@ static const int aspeed_soc_ast2600_irqmap[] = { [ASPEED_DEV_ETH4] = 33, [ASPEED_DEV_KCS] = 138, /* 138 -> 142 */ [ASPEED_DEV_DP] = 62, + [ASPEED_DEV_FSI1] = 100, + [ASPEED_DEV_FSI2] = 101, [ASPEED_DEV_I3C] = 102, /* 102 -> 107 */ }; static qemu_irq aspeed_soc_ast2600_get_irq(AspeedSoCState *s, int dev) { + Aspeed2600SoCState *a = ASPEED2600_SOC(s); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - return qdev_get_gpio_in(DEVICE(&s->a7mpcore), sc->irqmap[dev]); + return qdev_get_gpio_in(DEVICE(&a->a7mpcore), sc->irqmap[dev]); } static void aspeed_soc_ast2600_init(Object *obj) { + Aspeed2600SoCState *a = ASPEED2600_SOC(obj); AspeedSoCState *s = ASPEED_SOC(obj); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); int i; @@ -152,7 +162,8 @@ static void aspeed_soc_ast2600_init(Object *obj) } for (i = 0; i < sc->num_cpus; i++) { - object_initialize_child(obj, "cpu[*]", &s->cpu[i], sc->cpu_type); + object_initialize_child(obj, "cpu[*]", &a->cpu[i], + aspeed_soc_cpu_type(sc)); } snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname); @@ -166,7 +177,7 @@ static void aspeed_soc_ast2600_init(Object *obj) object_property_add_alias(obj, "hw-prot-key", OBJECT(&s->scu), "hw-prot-key"); - object_initialize_child(obj, "a7mpcore", &s->a7mpcore, + object_initialize_child(obj, "a7mpcore", &a->a7mpcore, TYPE_A15MPCORE_PRIV); object_initialize_child(obj, "rtc", &s->rtc, TYPE_ASPEED_RTC); @@ -180,6 +191,8 @@ static void aspeed_soc_ast2600_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname); object_initialize_child(obj, "i2c", &s->i2c, typename); + object_initialize_child(obj, "peci", &s->peci, TYPE_ASPEED_PECI); + snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname); object_initialize_child(obj, "fmc", &s->fmc, typename); @@ -197,8 +210,6 @@ static void aspeed_soc_ast2600_init(Object *obj) object_initialize_child(obj, "sdmc", &s->sdmc, typename); object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), "ram-size"); - object_property_add_alias(obj, "max-ram-size", OBJECT(&s->sdmc), - "max-ram-size"); for (i = 0; i < sc->wdts_num; i++) { snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); @@ -212,6 +223,10 @@ static void aspeed_soc_ast2600_init(Object *obj) object_initialize_child(obj, "mii[*]", &s->mii[i], TYPE_ASPEED_MII); } + for (i = 0; i < sc->uarts_num; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); + } + snprintf(typename, sizeof(typename), TYPE_ASPEED_XDMA "-%s", socname); object_initialize_child(obj, "xdma", &s->xdma, typename); @@ -248,6 +263,17 @@ static void aspeed_soc_ast2600_init(Object *obj) object_initialize_child(obj, "i3c", &s->i3c, TYPE_ASPEED_I3C); object_initialize_child(obj, "sbc", &s->sbc, TYPE_ASPEED_SBC); + + object_initialize_child(obj, "iomem", &s->iomem, TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "video", &s->video, TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "dpmcu", &s->dpmcu, TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "emmc-boot-controller", + &s->emmc_boot_controller, + TYPE_UNIMPLEMENTED_DEVICE); + + for (i = 0; i < ASPEED_FSI_NUM; i++) { + object_initialize_child(obj, "fsi[*]", &s->fsi[i], TYPE_ASPEED_APB2OPB); + } } /* @@ -263,54 +289,68 @@ static uint64_t aspeed_calc_affinity(int cpu) static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) { int i; + Aspeed2600SoCState *a = ASPEED2600_SOC(dev); AspeedSoCState *s = ASPEED_SOC(dev); AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - Error *err = NULL; qemu_irq irq; + g_autofree char *sram_name = NULL; + + /* Default boot region (SPI memory or ROMs) */ + memory_region_init(&s->spi_boot_container, OBJECT(s), + "aspeed.spi_boot_container", 0x10000000); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SPI_BOOT], + &s->spi_boot_container); /* IO space */ - create_unimplemented_device("aspeed_soc.io", sc->memmap[ASPEED_DEV_IOMEM], - ASPEED_SOC_IOMEM_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), "aspeed.io", + sc->memmap[ASPEED_DEV_IOMEM], + ASPEED_SOC_IOMEM_SIZE); /* Video engine stub */ - create_unimplemented_device("aspeed.video", sc->memmap[ASPEED_DEV_VIDEO], - 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->video), "aspeed.video", + sc->memmap[ASPEED_DEV_VIDEO], 0x1000); /* eMMC Boot Controller stub */ - create_unimplemented_device("aspeed.emmc-boot-controller", - sc->memmap[ASPEED_DEV_EMMC_BC], - 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->emmc_boot_controller), + "aspeed.emmc-boot-controller", + sc->memmap[ASPEED_DEV_EMMC_BC], 0x1000); /* CPU */ for (i = 0; i < sc->num_cpus; i++) { if (sc->num_cpus > 1) { - object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar", + object_property_set_int(OBJECT(&a->cpu[i]), "reset-cbar", ASPEED_A7MPCORE_ADDR, &error_abort); } - object_property_set_int(OBJECT(&s->cpu[i]), "mp-affinity", + object_property_set_int(OBJECT(&a->cpu[i]), "mp-affinity", aspeed_calc_affinity(i), &error_abort); - object_property_set_int(OBJECT(&s->cpu[i]), "cntfrq", 1125000000, + object_property_set_int(OBJECT(&a->cpu[i]), "cntfrq", 1125000000, &error_abort); + object_property_set_bool(OBJECT(&a->cpu[i]), "neon", false, + &error_abort); + object_property_set_bool(OBJECT(&a->cpu[i]), "vfp-d32", false, + &error_abort); + object_property_set_link(OBJECT(&a->cpu[i]), "memory", + OBJECT(s->memory), &error_abort); - if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { + if (!qdev_realize(DEVICE(&a->cpu[i]), NULL, errp)) { return; } } /* A7MPCORE */ - object_property_set_int(OBJECT(&s->a7mpcore), "num-cpu", sc->num_cpus, + object_property_set_int(OBJECT(&a->a7mpcore), "num-cpu", sc->num_cpus, &error_abort); - object_property_set_int(OBJECT(&s->a7mpcore), "num-irq", + object_property_set_int(OBJECT(&a->a7mpcore), "num-irq", ROUND_UP(AST2600_MAX_IRQ + GIC_INTERNAL, 32), &error_abort); - sysbus_realize(SYS_BUS_DEVICE(&s->a7mpcore), &error_abort); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, ASPEED_A7MPCORE_ADDR); + sysbus_realize(SYS_BUS_DEVICE(&a->a7mpcore), &error_abort); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->a7mpcore), 0, ASPEED_A7MPCORE_ADDR); for (i = 0; i < sc->num_cpus; i++) { - SysBusDevice *sbd = SYS_BUS_DEVICE(&s->a7mpcore); - DeviceState *d = DEVICE(qemu_get_cpu(i)); + SysBusDevice *sbd = SYS_BUS_DEVICE(&a->a7mpcore); + DeviceState *d = DEVICE(&a->cpu[i]); irq = qdev_get_gpio_in(d, ARM_CPU_IRQ); sysbus_connect_irq(sbd, i, irq); @@ -323,30 +363,30 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) } /* SRAM */ - memory_region_init_ram(&s->sram, OBJECT(dev), "aspeed.sram", - sc->sram_size, &err); - if (err) { - error_propagate(errp, err); + sram_name = g_strdup_printf("aspeed.sram.%d", CPU(&a->cpu[0])->cpu_index); + if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, + errp)) { return; } - memory_region_add_subregion(get_system_memory(), + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SRAM], &s->sram); /* DPMCU */ - create_unimplemented_device("aspeed.dpmcu", sc->memmap[ASPEED_DEV_DPMCU], - ASPEED_SOC_DPMCU_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->dpmcu), "aspeed.dpmcu", + sc->memmap[ASPEED_DEV_DPMCU], + ASPEED_SOC_DPMCU_SIZE); /* SCU */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); /* RTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->rtc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_DEV_RTC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->rtc), 0, sc->memmap[ASPEED_DEV_RTC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_RTC)); @@ -356,10 +396,10 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->timerctrl), 0, sc->memmap[ASPEED_DEV_TIMER1]); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { - qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); + irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); } @@ -367,12 +407,14 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->adc), 0, sc->memmap[ASPEED_DEV_ADC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ADC)); /* UART */ - aspeed_soc_uart_init(s); + if (!aspeed_soc_uart_realize(s, errp)) { + return; + } /* I2C */ object_property_set_link(OBJECT(&s->i2c), "dram", OBJECT(s->dram_mr), @@ -380,26 +422,41 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i2c), 0, sc->memmap[ASPEED_DEV_I2C]); for (i = 0; i < ASPEED_I2C_GET_CLASS(&s->i2c)->num_busses; i++) { - qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), - sc->irqmap[ASPEED_DEV_I2C] + i); + irq = qdev_get_gpio_in(DEVICE(&a->a7mpcore), + sc->irqmap[ASPEED_DEV_I2C] + i); /* The AST2600 I2C controller has one IRQ per bus. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c.busses[i]), 0, irq); } + /* PECI */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->peci), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->peci), 0, + sc->memmap[ASPEED_DEV_PECI]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_PECI)); + /* FMC, The number of CS is set at the board level */ object_property_set_link(OBJECT(&s->fmc), "dram", OBJECT(s->dram_mr), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->fmc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 0, sc->memmap[ASPEED_DEV_FMC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fmc), 1, ASPEED_SMC_GET_CLASS(&s->fmc)->flash_window_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_FMC)); + /* Set up an alias on the FMC CE0 region (boot default) */ + MemoryRegion *fmc0_mmio = &s->fmc.flashes[0].mmio; + memory_region_init_alias(&s->spi_boot, OBJECT(s), "aspeed.spi_boot", + fmc0_mmio, 0, memory_region_size(fmc0_mmio)); + memory_region_add_subregion(&s->spi_boot_container, 0x0, &s->spi_boot); + /* SPI */ for (i = 0; i < sc->spis_num; i++) { object_property_set_link(OBJECT(&s->spi[i]), "dram", @@ -407,9 +464,9 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 0, sc->memmap[ASPEED_DEV_SPI1 + i]); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 1, ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); } @@ -418,7 +475,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ehci[i]), 0, sc->memmap[ASPEED_DEV_EHCI1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_EHCI1 + i)); @@ -428,19 +485,25 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdmc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, sc->memmap[ASPEED_DEV_SDMC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdmc), 0, + sc->memmap[ASPEED_DEV_SDMC]); /* Watch dog */ for (i = 0; i < sc->wdts_num; i++) { AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(&s->wdt[i]); + hwaddr wdt_offset = sc->memmap[ASPEED_DEV_WDT] + i * awc->iosize; object_property_set_link(OBJECT(&s->wdt[i]), "scu", OBJECT(&s->scu), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, - sc->memmap[ASPEED_DEV_WDT] + i * awc->offset); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->wdt[i]), 0, wdt_offset); + } + + /* RAM */ + if (!aspeed_soc_dram_init(s, errp)) { + return; } /* Net */ @@ -450,7 +513,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->ftgmac100[i]), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, sc->memmap[ASPEED_DEV_ETH1 + i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, aspeed_soc_get_irq(s, ASPEED_DEV_ETH1 + i)); @@ -461,7 +524,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->mii[i]), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->mii[i]), 0, sc->memmap[ASPEED_DEV_MII1 + i]); } @@ -469,7 +532,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->xdma), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->xdma), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->xdma), 0, sc->memmap[ASPEED_DEV_XDMA]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0, aspeed_soc_get_irq(s, ASPEED_DEV_XDMA)); @@ -478,14 +541,14 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio), 0, sc->memmap[ASPEED_DEV_GPIO]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO)); if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio_1_8v), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio_1_8v), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->gpio_1_8v), 0, sc->memmap[ASPEED_DEV_GPIO_1_8V]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio_1_8v), 0, aspeed_soc_get_irq(s, ASPEED_DEV_GPIO_1_8V)); @@ -494,7 +557,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci), 0, + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdhci), 0, sc->memmap[ASPEED_DEV_SDHCI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, aspeed_soc_get_irq(s, ASPEED_DEV_SDHCI)); @@ -503,7 +566,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->emmc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->emmc), 0, sc->memmap[ASPEED_DEV_EMMC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->emmc), 0, + sc->memmap[ASPEED_DEV_EMMC]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc), 0, aspeed_soc_get_irq(s, ASPEED_DEV_EMMC)); @@ -511,7 +575,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->lpc), 0, sc->memmap[ASPEED_DEV_LPC]); /* Connect the LPC IRQ to the GIC. It is otherwise unused. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 0, @@ -526,19 +590,19 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) * offset 0. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_1, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), + qdev_get_gpio_in(DEVICE(&a->a7mpcore), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_1)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_2, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), + qdev_get_gpio_in(DEVICE(&a->a7mpcore), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_2)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_3, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), + qdev_get_gpio_in(DEVICE(&a->a7mpcore), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_3)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_4, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), + qdev_get_gpio_in(DEVICE(&a->a7mpcore), sc->irqmap[ASPEED_DEV_KCS] + aspeed_lpc_kcs_4)); /* HACE */ @@ -547,7 +611,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->hace), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->hace), 0, + sc->memmap[ASPEED_DEV_HACE]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); @@ -555,10 +620,10 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->i3c), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->i3c), 0, sc->memmap[ASPEED_DEV_I3C]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->i3c), 0, sc->memmap[ASPEED_DEV_I3C]); for (i = 0; i < ASPEED_I3C_NR_DEVICES; i++) { - qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), - sc->irqmap[ASPEED_DEV_I3C] + i); + irq = qdev_get_gpio_in(DEVICE(&a->a7mpcore), + sc->irqmap[ASPEED_DEV_I3C] + i); /* The AST2600 I3C controller has one IRQ per bus. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i3c.devices[i]), 0, irq); } @@ -567,18 +632,35 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(&s->sbc), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->sbc), 0, sc->memmap[ASPEED_DEV_SBC]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sbc), 0, sc->memmap[ASPEED_DEV_SBC]); + + /* FSI */ + for (i = 0; i < ASPEED_FSI_NUM; i++) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->fsi[i]), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->fsi[i]), 0, + sc->memmap[ASPEED_DEV_FSI1 + i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->fsi[i]), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_FSI1 + i)); + } } static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a7"), + NULL + }; DeviceClass *dc = DEVICE_CLASS(oc); AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); dc->realize = aspeed_soc_ast2600_realize; + /* Reason: The Aspeed SoC can only be instantiated from a board */ + dc->user_creatable = false; sc->name = "ast2600-a3"; - sc->cpu_type = ARM_CPU_TYPE_NAME("cortex-a7"); + sc->valid_cpu_types = valid_cpu_types; sc->silicon_rev = AST2600_A3_SILICON_REV; sc->sram_size = 0x16400; sc->spis_num = 2; @@ -586,24 +668,25 @@ static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) sc->wdts_num = 4; sc->macs_num = 4; sc->uarts_num = 13; + sc->uarts_base = ASPEED_DEV_UART1; sc->irqmap = aspeed_soc_ast2600_irqmap; sc->memmap = aspeed_soc_ast2600_memmap; sc->num_cpus = 2; sc->get_irq = aspeed_soc_ast2600_get_irq; } -static const TypeInfo aspeed_soc_ast2600_type_info = { - .name = "ast2600-a3", - .parent = TYPE_ASPEED_SOC, - .instance_size = sizeof(AspeedSoCState), - .instance_init = aspeed_soc_ast2600_init, - .class_init = aspeed_soc_ast2600_class_init, - .class_size = sizeof(AspeedSoCClass), +static const TypeInfo aspeed_soc_ast2600_types[] = { + { + .name = TYPE_ASPEED2600_SOC, + .parent = TYPE_ASPEED_SOC, + .instance_size = sizeof(Aspeed2600SoCState), + .abstract = true, + }, { + .name = "ast2600-a3", + .parent = TYPE_ASPEED2600_SOC, + .instance_init = aspeed_soc_ast2600_init, + .class_init = aspeed_soc_ast2600_class_init, + }, }; -static void aspeed_soc_register_types(void) -{ - type_register_static(&aspeed_soc_ast2600_type_info); -}; - -type_init(aspeed_soc_register_types) +DEFINE_TYPES(aspeed_soc_ast2600_types) diff --git a/hw/arm/aspeed_eeprom.c b/hw/arm/aspeed_eeprom.c new file mode 100644 index 0000000000..daa3d329d1 --- /dev/null +++ b/hw/arm/aspeed_eeprom.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include "qemu/osdep.h" +#include "aspeed_eeprom.h" + +/* Tiogapass BMC FRU */ +const uint8_t tiogapass_bmc_fruid[] = { + 0x01, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0xf1, 0x01, 0x0c, 0x00, 0x36, + 0xe6, 0xd0, 0xc6, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x42, 0x4d, + 0x43, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x20, 0x4d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0xcd, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc3, 0x31, 0x2e, + 0x30, 0xc9, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc1, 0x39, 0x01, 0x0c, 0x00, 0xc6, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x54, 0x69, 0x6f, 0x67, 0x61, + 0x20, 0x50, 0x61, 0x73, 0x73, 0x20, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, + 0x32, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0xc4, 0x58, 0x58, 0x58, 0x32, 0xcd, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc7, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc3, 0x31, 0x2e, 0x30, 0xc9, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc8, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x20, 0x41, 0xc1, 0x45, +}; + +const uint8_t fby35_nic_fruid[] = { + 0x01, 0x00, 0x00, 0x01, 0x0f, 0x20, 0x00, 0xcf, 0x01, 0x0e, 0x19, 0xd7, + 0x5e, 0xcf, 0xc8, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xdd, + 0x4d, 0x65, 0x6c, 0x6c, 0x61, 0x6e, 0x6f, 0x78, 0x20, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x58, 0x2d, 0x36, 0x20, 0x44, 0x58, 0x20, 0x4f, + 0x43, 0x50, 0x33, 0x2e, 0x30, 0xd8, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd5, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0xcc, 0x46, 0x52, 0x55, 0x20, 0x56, 0x65, 0x72, + 0x20, 0x30, 0x2e, 0x30, 0x32, 0xc0, 0xc0, 0xc0, 0xc1, 0x00, 0x00, 0x2f, + 0x01, 0x11, 0x19, 0xc8, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0xdd, 0x4d, 0x65, 0x6c, 0x6c, 0x61, 0x6e, 0x6f, 0x78, 0x20, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x58, 0x2d, 0x36, 0x20, 0x44, 0x58, 0x20, + 0x4f, 0x43, 0x50, 0x33, 0x2e, 0x30, 0xd5, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0xd3, 0x41, 0x39, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xd8, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0xc0, 0xc0, 0xc0, 0xc0, 0xcd, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x58, 0x2d, 0x36, 0x20, 0x44, 0x58, 0xc1, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xdb, 0xc0, 0x82, 0x30, 0x15, 0x79, 0x7f, 0xa6, 0x00, + 0x01, 0x18, 0x0b, 0xff, 0x08, 0x00, 0xff, 0xff, 0x64, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x20, 0x01, 0xff, 0xff, 0x04, 0x46, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x81, 0x09, 0x15, 0xb3, 0x10, 0x1d, 0x00, + 0x24, 0x15, 0xb3, 0x00, 0x02, 0xeb, 0x8a, 0x95, 0x5c, +}; + +const uint8_t fby35_bb_fruid[] = { + 0x01, 0x00, 0x01, 0x03, 0x10, 0x00, 0x00, 0xeb, 0x01, 0x02, 0x17, 0xc3, + 0x4e, 0x2f, 0x41, 0xc3, 0x4e, 0x2f, 0x41, 0xc1, 0x00, 0x00, 0x00, 0x23, + 0x01, 0x0d, 0x00, 0xb6, 0xd2, 0xd0, 0xc6, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0xd5, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x20, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x20, 0x77, 0x42, 0x4d, 0x43, 0xcd, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0xc3, 0x31, 0x2e, 0x30, 0xc9, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x01, 0x0c, 0x00, 0xc6, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x59, 0x6f, 0x73, 0x65, 0x6d, + 0x69, 0x74, 0x65, 0x20, 0x56, 0x33, 0x2e, 0x35, 0x20, 0x45, 0x56, 0x54, + 0x32, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0xc4, 0x45, 0x56, 0x54, 0x32, 0xcd, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc7, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc3, 0x31, 0x2e, 0x30, 0xc9, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc3, 0x4e, 0x2f, + 0x41, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, +}; + +const uint8_t fby35_bmc_fruid[] = { + 0x01, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0xf1, 0x01, 0x0c, 0x00, 0x36, + 0xe6, 0xd0, 0xc6, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x42, 0x4d, + 0x43, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x20, 0x4d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0xcd, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc3, 0x31, 0x2e, + 0x30, 0xc9, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc1, 0x39, 0x01, 0x0c, 0x00, 0xc6, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x59, 0x6f, 0x73, 0x65, 0x6d, + 0x69, 0x74, 0x65, 0x20, 0x56, 0x33, 0x2e, 0x35, 0x20, 0x45, 0x56, 0x54, + 0x32, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0xc4, 0x45, 0x56, 0x54, 0x32, 0xcd, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc7, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc3, 0x31, 0x2e, 0x30, 0xc9, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc8, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x20, 0x41, 0xc1, 0x45, +}; + +/* Yosemite V2 BMC FRU */ +const uint8_t yosemitev2_bmc_fruid[] = { + 0x01, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0xf1, 0x01, 0x0c, 0x00, 0x36, + 0xe6, 0xd0, 0xc6, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x42, 0x61, + 0x73, 0x65, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x20, 0x4d, 0x50, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xcd, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc3, 0x31, 0x2e, + 0x30, 0xc9, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc1, 0x39, 0x01, 0x0c, 0x00, 0xc6, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xd2, 0x59, 0x6f, 0x73, 0x65, 0x6d, + 0x69, 0x74, 0x65, 0x20, 0x56, 0x32, 0x20, 0x4d, 0x50, 0x00, 0x00, 0x00, + 0x00, 0xce, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0xc4, 0x45, 0x56, 0x54, 0x32, 0xcd, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc7, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc3, 0x31, 0x2e, 0x30, 0xc9, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0xc8, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x20, 0x41, 0xc1, 0x45, +}; + +const uint8_t rainier_bb_fruid[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, + 0x28, 0x00, 0x52, 0x54, 0x04, 0x56, 0x48, 0x44, 0x52, 0x56, 0x44, 0x02, + 0x01, 0x00, 0x50, 0x54, 0x0e, 0x56, 0x54, 0x4f, 0x43, 0x00, 0x00, 0x37, + 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x52, 0x54, + 0x04, 0x56, 0x54, 0x4f, 0x43, 0x50, 0x54, 0x38, 0x56, 0x49, 0x4e, 0x49, + 0x00, 0x00, 0x81, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x53, + 0x59, 0x53, 0x00, 0x00, 0xbb, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x56, 0x43, 0x45, 0x4e, 0x00, 0x00, 0xe2, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x56, 0x53, 0x42, 0x50, 0x00, 0x00, 0x09, 0x01, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, + 0x52, 0x54, 0x04, 0x56, 0x49, 0x4e, 0x49, 0x44, 0x52, 0x04, 0x44, 0x45, + 0x53, 0x43, 0x48, 0x57, 0x02, 0x30, 0x31, 0x43, 0x43, 0x04, 0x33, 0x34, + 0x35, 0x36, 0x46, 0x4e, 0x04, 0x46, 0x52, 0x34, 0x39, 0x53, 0x4e, 0x04, + 0x53, 0x52, 0x31, 0x32, 0x50, 0x4e, 0x04, 0x50, 0x52, 0x39, 0x39, 0x50, + 0x46, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x52, 0x54, + 0x04, 0x56, 0x53, 0x59, 0x53, 0x53, 0x45, 0x07, 0x49, 0x42, 0x4d, 0x53, + 0x59, 0x53, 0x31, 0x54, 0x4d, 0x08, 0x32, 0x32, 0x32, 0x32, 0x2d, 0x32, + 0x32, 0x32, 0x50, 0x46, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, + 0x00, 0x52, 0x54, 0x04, 0x56, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x07, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x46, 0x43, 0x08, 0x31, 0x31, 0x31, + 0x31, 0x2d, 0x31, 0x31, 0x31, 0x50, 0x46, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x15, 0x00, 0x52, 0x54, 0x04, 0x56, 0x53, 0x42, 0x50, 0x49, + 0x4d, 0x04, 0x50, 0x00, 0x10, 0x01, 0x50, 0x46, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, +}; + +/* Rainier BMC FRU */ +const uint8_t rainier_bmc_fruid[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, + 0x28, 0x00, 0x52, 0x54, 0x04, 0x56, 0x48, 0x44, 0x52, 0x56, 0x44, 0x02, + 0x01, 0x00, 0x50, 0x54, 0x0e, 0x56, 0x54, 0x4f, 0x43, 0x00, 0x00, 0x37, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x52, 0x54, + 0x04, 0x56, 0x54, 0x4f, 0x43, 0x50, 0x54, 0x0e, 0x56, 0x49, 0x4e, 0x49, + 0x00, 0x00, 0x57, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, + 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x52, 0x54, 0x04, 0x56, 0x49, 0x4e, + 0x49, 0x44, 0x52, 0x04, 0x44, 0x45, 0x53, 0x43, 0x48, 0x57, 0x02, 0x30, + 0x31, 0x50, 0x46, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const size_t tiogapass_bmc_fruid_len = sizeof(tiogapass_bmc_fruid); +const size_t fby35_nic_fruid_len = sizeof(fby35_nic_fruid); +const size_t fby35_bb_fruid_len = sizeof(fby35_bb_fruid); +const size_t fby35_bmc_fruid_len = sizeof(fby35_bmc_fruid); +const size_t yosemitev2_bmc_fruid_len = sizeof(yosemitev2_bmc_fruid); +const size_t rainier_bb_fruid_len = sizeof(rainier_bb_fruid); +const size_t rainier_bmc_fruid_len = sizeof(rainier_bmc_fruid); diff --git a/hw/arm/aspeed_eeprom.h b/hw/arm/aspeed_eeprom.h new file mode 100644 index 0000000000..f08c16ef50 --- /dev/null +++ b/hw/arm/aspeed_eeprom.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef ASPEED_EEPROM_H +#define ASPEED_EEPROM_H + + +extern const uint8_t tiogapass_bmc_fruid[]; +extern const size_t tiogapass_bmc_fruid_len; + +extern const uint8_t fby35_nic_fruid[]; +extern const uint8_t fby35_bb_fruid[]; +extern const uint8_t fby35_bmc_fruid[]; +extern const size_t fby35_nic_fruid_len; +extern const size_t fby35_bb_fruid_len; +extern const size_t fby35_bmc_fruid_len; + +extern const uint8_t yosemitev2_bmc_fruid[]; +extern const size_t yosemitev2_bmc_fruid_len; + +extern const uint8_t rainier_bb_fruid[]; +extern const size_t rainier_bb_fruid_len; +extern const uint8_t rainier_bmc_fruid[]; +extern const size_t rainier_bmc_fruid_len; + +#endif diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c new file mode 100644 index 0000000000..1e8f2558fd --- /dev/null +++ b/hw/arm/aspeed_soc_common.c @@ -0,0 +1,164 @@ +/* + * ASPEED SoC family + * + * Andrew Jeffery + * Jeremy Kerr + * + * Copyright 2016 IBM Corp. + * + * 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 "hw/qdev-properties.h" +#include "hw/misc/unimp.h" +#include "hw/arm/aspeed_soc.h" +#include "hw/char/serial.h" + + +const char *aspeed_soc_cpu_type(AspeedSoCClass *sc) +{ + assert(sc->valid_cpu_types); + assert(sc->valid_cpu_types[0]); + assert(!sc->valid_cpu_types[1]); + return sc->valid_cpu_types[0]; +} + +qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev) +{ + return ASPEED_SOC_GET_CLASS(s)->get_irq(s, dev); +} + +bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp) +{ + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + SerialMM *smm; + + for (int i = 0, uart = sc->uarts_base; i < sc->uarts_num; i++, uart++) { + smm = &s->uart[i]; + + /* Chardev property is set by the machine. */ + qdev_prop_set_uint8(DEVICE(smm), "regshift", 2); + qdev_prop_set_uint32(DEVICE(smm), "baudbase", 38400); + qdev_set_legacy_instance_id(DEVICE(smm), sc->memmap[uart], 2); + qdev_prop_set_uint8(DEVICE(smm), "endianness", DEVICE_LITTLE_ENDIAN); + if (!sysbus_realize(SYS_BUS_DEVICE(smm), errp)) { + return false; + } + + sysbus_connect_irq(SYS_BUS_DEVICE(smm), 0, aspeed_soc_get_irq(s, uart)); + aspeed_mmio_map(s, SYS_BUS_DEVICE(smm), 0, sc->memmap[uart]); + } + + return true; +} + +void aspeed_soc_uart_set_chr(AspeedSoCState *s, int dev, Chardev *chr) +{ + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int uart_first = aspeed_uart_first(sc); + int uart_index = aspeed_uart_index(dev); + int i = uart_index - uart_first; + + g_assert(0 <= i && i < ARRAY_SIZE(s->uart) && i < sc->uarts_num); + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); +} + +/* + * SDMC should be realized first to get correct RAM size and max size + * values + */ +bool aspeed_soc_dram_init(AspeedSoCState *s, Error **errp) +{ + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + ram_addr_t ram_size, max_ram_size; + + ram_size = object_property_get_uint(OBJECT(&s->sdmc), "ram-size", + &error_abort); + max_ram_size = object_property_get_uint(OBJECT(&s->sdmc), "max-ram-size", + &error_abort); + + memory_region_init(&s->dram_container, OBJECT(s), "ram-container", + max_ram_size); + memory_region_add_subregion(&s->dram_container, 0, s->dram_mr); + + /* + * Add a memory region beyond the RAM region to let firmwares scan + * the address space with load/store and guess how much RAM the + * SoC has. + */ + if (ram_size < max_ram_size) { + DeviceState *dev = qdev_new(TYPE_UNIMPLEMENTED_DEVICE); + + qdev_prop_set_string(dev, "name", "ram-empty"); + qdev_prop_set_uint64(dev, "size", max_ram_size - ram_size); + if (!sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp)) { + return false; + } + + memory_region_add_subregion_overlap(&s->dram_container, ram_size, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0), -1000); + } + + memory_region_add_subregion(s->memory, + sc->memmap[ASPEED_DEV_SDRAM], &s->dram_container); + return true; +} + +void aspeed_mmio_map(AspeedSoCState *s, SysBusDevice *dev, int n, hwaddr addr) +{ + memory_region_add_subregion(s->memory, addr, + sysbus_mmio_get_region(dev, n)); +} + +void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, + const char *name, hwaddr addr, uint64_t size) +{ + qdev_prop_set_string(DEVICE(dev), "name", name); + qdev_prop_set_uint64(DEVICE(dev), "size", size); + sysbus_realize(dev, &error_abort); + + memory_region_add_subregion_overlap(s->memory, addr, + sysbus_mmio_get_region(dev, 0), -1000); +} + +static void aspeed_soc_realize(DeviceState *dev, Error **errp) +{ + AspeedSoCState *s = ASPEED_SOC(dev); + + if (!s->memory) { + error_setg(errp, "'memory' link is not set"); + return; + } +} + +static Property aspeed_soc_properties[] = { + DEFINE_PROP_LINK("dram", AspeedSoCState, dram_mr, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_LINK("memory", AspeedSoCState, memory, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void aspeed_soc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = aspeed_soc_realize; + device_class_set_props(dc, aspeed_soc_properties); +} + +static const TypeInfo aspeed_soc_types[] = { + { + .name = TYPE_ASPEED_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(AspeedSoCState), + .class_size = sizeof(AspeedSoCClass), + .class_init = aspeed_soc_class_init, + .abstract = true, + }, +}; + +DEFINE_TYPES(aspeed_soc_types) diff --git a/hw/arm/b-l475e-iot01a.c b/hw/arm/b-l475e-iot01a.c new file mode 100644 index 0000000000..d862aa43fc --- /dev/null +++ b/hw/arm/b-l475e-iot01a.c @@ -0,0 +1,64 @@ +/* + * B-L475E-IOT01A Discovery Kit machine + * (B-L475E-IOT01A IoT Node) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This work is heavily inspired by the netduinoplus2 by Alistair Francis. + * Original code is licensed under the MIT License: + * + * Copyright (c) 2014 Alistair Francis + */ + +/* + * The reference used is the STMicroElectronics UM2153 User manual + * Discovery kit for IoT node, multi-channel communication with STM32L4. + * https://www.st.com/en/evaluation-tools/b-l475e-iot01a.html#documentation + */ + +#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/stm32l4x5_soc.h" +#include "hw/arm/boot.h" + +/* B-L475E-IOT01A implementation is derived from netduinoplus2 */ + +static void b_l475e_iot01a_init(MachineState *machine) +{ + const Stm32l4x5SocClass *sc; + DeviceState *dev; + + dev = qdev_new(TYPE_STM32L4X5XG_SOC); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + sc = STM32L4X5_SOC_GET_CLASS(dev); + armv7m_load_kernel(ARM_CPU(first_cpu), + machine->kernel_filename, + 0, sc->flash_size); +} + +static void b_l475e_iot01a_machine_init(MachineClass *mc) +{ + static const char *machine_valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m4"), + NULL + }; + mc->desc = "B-L475E-IOT01A Discovery Kit (Cortex-M4)"; + mc->init = b_l475e_iot01a_init; + mc->valid_cpu_types = machine_valid_cpu_types; + + /* SRAM pre-allocated as part of the SoC instantiation */ + mc->default_ram_size = 0; +} + +DEFINE_MACHINE("b-l475e-iot01a", b_l475e_iot01a_machine_init) diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c new file mode 100644 index 0000000000..0a4b6f29b1 --- /dev/null +++ b/hw/arm/bananapi_m2u.c @@ -0,0 +1,146 @@ +/* + * Bananapi M2U emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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 "qemu/error-report.h" +#include "hw/boards.h" +#include "hw/i2c/i2c.h" +#include "hw/qdev-properties.h" +#include "hw/arm/allwinner-r40.h" +#include "hw/arm/boot.h" + +static struct arm_boot_info bpim2u_binfo; + +/* + * R40 can boot from mmc0 and mmc2, and bpim2u has two mmc interface, one is + * connected to sdcard and another mount an emmc media. + * Attach the mmc driver and try loading bootloader. + */ +static void mmc_attach_drive(AwR40State *s, AwSdHostState *mmc, int unit, + bool load_bootroom, bool *bootroom_loaded) +{ + DriveInfo *di = drive_get(IF_SD, 0, unit); + BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL; + BusState *bus; + DeviceState *carddev; + + bus = qdev_get_child_bus(DEVICE(mmc), "sd-bus"); + if (bus == NULL) { + error_report("No SD bus found in SOC object"); + exit(1); + } + + carddev = qdev_new(TYPE_SD_CARD); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); + qdev_realize_and_unref(carddev, bus, &error_fatal); + + if (load_bootroom && blk && blk_is_available(blk)) { + /* Use Boot ROM to copy data from SD card to SRAM */ + *bootroom_loaded = allwinner_r40_bootrom_setup(s, blk, unit); + } +} + +static void bpim2u_init(MachineState *machine) +{ + bool bootroom_loaded = false; + AwR40State *r40; + I2CBus *i2c; + + /* BIOS is not supported by this board */ + if (machine->firmware) { + error_report("BIOS not supported for this machine"); + exit(1); + } + + r40 = AW_R40(object_new(TYPE_AW_R40)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(r40)); + object_unref(OBJECT(r40)); + + /* Setup timer properties */ + object_property_set_int(OBJECT(r40), "clk0-freq", 32768, &error_abort); + object_property_set_int(OBJECT(r40), "clk1-freq", 24 * 1000 * 1000, + &error_abort); + + /* DRAMC */ + r40->ram_size = machine->ram_size / MiB; + object_property_set_uint(OBJECT(r40), "ram-addr", + r40->memmap[AW_R40_DEV_SDRAM], &error_abort); + object_property_set_int(OBJECT(r40), "ram-size", + r40->ram_size, &error_abort); + + /* GMAC PHY */ + object_property_set_uint(OBJECT(r40), "gmac-phy-addr", 1, &error_abort); + + /* Mark R40 object realized */ + qdev_realize(DEVICE(r40), NULL, &error_abort); + + /* + * Plug in SD card and try load bootrom, R40 has 4 mmc controllers but can + * only booting from mmc0 and mmc2. + */ + for (int i = 0; i < AW_R40_NUM_MMCS; i++) { + switch (i) { + case 0: + case 2: + mmc_attach_drive(r40, &r40->mmc[i], i, + !machine->kernel_filename && !bootroom_loaded, + &bootroom_loaded); + break; + default: + mmc_attach_drive(r40, &r40->mmc[i], i, false, NULL); + break; + } + } + + /* Connect AXP221 */ + i2c = I2C_BUS(qdev_get_child_bus(DEVICE(&r40->i2c0), "i2c")); + i2c_slave_create_simple(i2c, "axp221_pmu", 0x34); + + /* SDRAM */ + memory_region_add_subregion(get_system_memory(), + r40->memmap[AW_R40_DEV_SDRAM], machine->ram); + + bpim2u_binfo.loader_start = r40->memmap[AW_R40_DEV_SDRAM]; + bpim2u_binfo.ram_size = machine->ram_size; + bpim2u_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; + arm_load_kernel(&r40->cpus[0], machine, &bpim2u_binfo); +} + +static void bpim2u_machine_init(MachineClass *mc) +{ + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a7"), + NULL + }; + + mc->desc = "Bananapi M2U (Cortex-A7)"; + mc->init = bpim2u_init; + mc->min_cpus = AW_R40_NUM_CPUS; + mc->max_cpus = AW_R40_NUM_CPUS; + mc->default_cpus = AW_R40_NUM_CPUS; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a7"); + mc->valid_cpu_types = valid_cpu_types; + mc->default_ram_size = 1 * GiB; + mc->default_ram_id = "bpim2u.ram"; +} + +DEFINE_MACHINE("bpim2u", bpim2u_machine_init) diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 48538c9360..1695d8b453 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -23,9 +23,19 @@ /* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */ #define BCM2835_SDHC_CAPAREG 0x52134b4 -static void create_unimp(BCM2835PeripheralState *ps, - UnimplementedDeviceState *uds, - const char *name, hwaddr ofs, hwaddr size) +/* + * According to Linux driver & DTS, dma channels 0--10 have separate IRQ, + * while channels 11--14 share one IRQ: + */ +#define SEPARATE_DMA_IRQ_MAX 10 +#define ORGATED_DMA_IRQ_COUNT 4 + +/* All three I2C controllers share the same IRQ */ +#define ORGATED_I2C_IRQ_COUNT 3 + +void create_unimp(BCMSocPeripheralBaseState *ps, + UnimplementedDeviceState *uds, + const char *name, hwaddr ofs, hwaddr size) { object_initialize_child(OBJECT(ps), name, uds, TYPE_UNIMPLEMENTED_DEVICE); qdev_prop_set_string(DEVICE(uds), "name", name); @@ -38,9 +48,36 @@ static void create_unimp(BCM2835PeripheralState *ps, static void bcm2835_peripherals_init(Object *obj) { BCM2835PeripheralState *s = BCM2835_PERIPHERALS(obj); + BCMSocPeripheralBaseState *s_base = BCM_SOC_PERIPHERALS_BASE(obj); + + /* Random Number Generator */ + object_initialize_child(obj, "rng", &s->rng, TYPE_BCM2835_RNG); + + /* Thermal */ + object_initialize_child(obj, "thermal", &s->thermal, TYPE_BCM2835_THERMAL); + + /* GPIO */ + object_initialize_child(obj, "gpio", &s->gpio, TYPE_BCM2835_GPIO); + + object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhci", + OBJECT(&s_base->sdhci.sdbus)); + object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhost", + OBJECT(&s_base->sdhost.sdbus)); + + /* Gated DMA interrupts */ + object_initialize_child(obj, "orgated-dma-irq", + &s_base->orgated_dma_irq, TYPE_OR_IRQ); + object_property_set_int(OBJECT(&s_base->orgated_dma_irq), "num-lines", + ORGATED_DMA_IRQ_COUNT, &error_abort); +} + +static void raspi_peripherals_base_init(Object *obj) +{ + BCMSocPeripheralBaseState *s = BCM_SOC_PERIPHERALS_BASE(obj); + BCMSocPeripheralBaseClass *bc = BCM_SOC_PERIPHERALS_BASE_GET_CLASS(obj); /* Memory region for peripheral devices, which we export to our parent */ - memory_region_init(&s->peri_mr, obj,"bcm2835-peripherals", 0x1000000); + memory_region_init(&s->peri_mr, obj, "bcm2835-peripherals", bc->peri_size); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->peri_mr); /* Internal memory region for peripheral bus addresses (not exported) */ @@ -74,6 +111,7 @@ static void bcm2835_peripherals_init(Object *obj) /* Framebuffer */ object_initialize_child(obj, "fb", &s->fb, TYPE_BCM2835_FB); object_property_add_alias(obj, "vcram-size", OBJECT(&s->fb), "vcram-size"); + object_property_add_alias(obj, "vcram-base", OBJECT(&s->fb), "vcram-base"); object_property_add_const_link(OBJECT(&s->fb), "dma-mr", OBJECT(&s->gpu_bus_mr)); @@ -83,15 +121,14 @@ static void bcm2835_peripherals_init(Object *obj) TYPE_BCM2835_PROPERTY); object_property_add_alias(obj, "board-rev", OBJECT(&s->property), "board-rev"); + object_property_add_alias(obj, "command-line", OBJECT(&s->property), + "command-line"); object_property_add_const_link(OBJECT(&s->property), "fb", OBJECT(&s->fb)); object_property_add_const_link(OBJECT(&s->property), "dma-mr", OBJECT(&s->gpu_bus_mr)); - /* Random Number Generator */ - object_initialize_child(obj, "rng", &s->rng, TYPE_BCM2835_RNG); - /* Extended Mass Media Controller */ object_initialize_child(obj, "sdhci", &s->sdhci, TYPE_SYSBUS_SDHCI); @@ -104,17 +141,6 @@ static void bcm2835_peripherals_init(Object *obj) object_property_add_const_link(OBJECT(&s->dma), "dma-mr", OBJECT(&s->gpu_bus_mr)); - /* Thermal */ - object_initialize_child(obj, "thermal", &s->thermal, TYPE_BCM2835_THERMAL); - - /* GPIO */ - object_initialize_child(obj, "gpio", &s->gpio, TYPE_BCM2835_GPIO); - - object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhci", - OBJECT(&s->sdhci.sdbus)); - object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhost", - OBJECT(&s->sdhost.sdbus)); - /* Mphi */ object_initialize_child(obj, "mphi", &s->mphi, TYPE_BCM2835_MPHI); @@ -130,15 +156,97 @@ static void bcm2835_peripherals_init(Object *obj) /* Power Management */ object_initialize_child(obj, "powermgt", &s->powermgt, TYPE_BCM2835_POWERMGT); + + /* SPI */ + object_initialize_child(obj, "bcm2835-spi0", &s->spi[0], + TYPE_BCM2835_SPI); + + /* I2C */ + object_initialize_child(obj, "bcm2835-i2c0", &s->i2c[0], + TYPE_BCM2835_I2C); + object_initialize_child(obj, "bcm2835-i2c1", &s->i2c[1], + TYPE_BCM2835_I2C); + object_initialize_child(obj, "bcm2835-i2c2", &s->i2c[2], + TYPE_BCM2835_I2C); + + object_initialize_child(obj, "orgated-i2c-irq", + &s->orgated_i2c_irq, TYPE_OR_IRQ); + object_property_set_int(OBJECT(&s->orgated_i2c_irq), "num-lines", + ORGATED_I2C_IRQ_COUNT, &error_abort); } static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) { + MemoryRegion *mphi_mr; BCM2835PeripheralState *s = BCM2835_PERIPHERALS(dev); + BCMSocPeripheralBaseState *s_base = BCM_SOC_PERIPHERALS_BASE(dev); + int n; + + bcm_soc_peripherals_common_realize(dev, errp); + + /* Extended Mass Media Controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->sdhci), 0, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), BCM2835_IC_GPU_IRQ, + INTERRUPT_ARASANSDIO)); + + /* Connect DMA 0-12 to the interrupt controller */ + for (n = 0; n <= SEPARATE_DMA_IRQ_MAX; n++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), n, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + INTERRUPT_DMA0 + n)); + } + + if (!qdev_realize(DEVICE(&s_base->orgated_dma_irq), NULL, errp)) { + return; + } + for (n = 0; n < ORGATED_DMA_IRQ_COUNT; n++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), + SEPARATE_DMA_IRQ_MAX + 1 + n, + qdev_get_gpio_in(DEVICE(&s_base->orgated_dma_irq), n)); + } + qdev_connect_gpio_out(DEVICE(&s_base->orgated_dma_irq), 0, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + INTERRUPT_DMA0 + SEPARATE_DMA_IRQ_MAX + 1)); + + /* Random Number Generator */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->rng), errp)) { + return; + } + memory_region_add_subregion( + &s_base->peri_mr, RNG_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0)); + + /* THERMAL */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->thermal), errp)) { + return; + } + memory_region_add_subregion(&s_base->peri_mr, THERMAL_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->thermal), 0)); + + /* Map MPHI to the peripherals memory map */ + mphi_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s_base->mphi), 0); + memory_region_add_subregion(&s_base->peri_mr, MPHI_OFFSET, mphi_mr); + + /* GPIO */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { + return; + } + memory_region_add_subregion( + &s_base->peri_mr, GPIO_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0)); + + object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->gpio), "sd-bus"); +} + +void bcm_soc_peripherals_common_realize(DeviceState *dev, Error **errp) +{ + BCMSocPeripheralBaseState *s = BCM_SOC_PERIPHERALS_BASE(dev); Object *obj; MemoryRegion *ram; Error *err = NULL; - uint64_t ram_size, vcram_size; + uint64_t ram_size, vcram_size, vcram_base; int n; obj = object_property_get_link(OBJECT(dev), "ram", &error_abort); @@ -242,11 +350,21 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) return; } - if (!object_property_set_uint(OBJECT(&s->fb), "vcram-base", - ram_size - vcram_size, errp)) { + vcram_base = object_property_get_uint(OBJECT(s), "vcram-base", &err); + if (err) { + error_propagate(errp, err); return; } + if (vcram_base == 0) { + vcram_base = ram_size - vcram_size; + } + vcram_base = MIN(vcram_base, UPPER_RAM_BASE - vcram_size); + + if (!object_property_set_uint(OBJECT(&s->fb), "vcram-base", vcram_base, + errp)) { + return; + } if (!sysbus_realize(SYS_BUS_DEVICE(&s->fb), errp)) { return; } @@ -267,14 +385,6 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->property), 0, qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_PROPERTY)); - /* Random Number Generator */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->rng), errp)) { - return; - } - - memory_region_add_subregion(&s->peri_mr, RNG_OFFSET, - sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0)); - /* Extended Mass Media Controller * * Compatible with: @@ -297,9 +407,6 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->peri_mr, EMMC1_OFFSET, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->sdhci), 0)); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, - qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, - INTERRUPT_ARASANSDIO)); /* SDHOST */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhost), errp)) { @@ -322,37 +429,11 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->peri_mr, DMA15_OFFSET, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 1)); - for (n = 0; n <= 12; n++) { - sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), n, - qdev_get_gpio_in_named(DEVICE(&s->ic), - BCM2835_IC_GPU_IRQ, - INTERRUPT_DMA0 + n)); - } - - /* THERMAL */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->thermal), errp)) { - return; - } - memory_region_add_subregion(&s->peri_mr, THERMAL_OFFSET, - sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->thermal), 0)); - - /* GPIO */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { - return; - } - - memory_region_add_subregion(&s->peri_mr, GPIO_OFFSET, - sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0)); - - object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->gpio), "sd-bus"); - /* Mphi */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->mphi), errp)) { return; } - memory_region_add_subregion(&s->peri_mr, MPHI_OFFSET, - sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mphi), 0)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->mphi), 0, qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, INTERRUPT_HOSTPORT)); @@ -376,15 +457,49 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->peri_mr, PM_OFFSET, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->powermgt), 0)); + /* SPI */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[0]), errp)) { + return; + } + + memory_region_add_subregion(&s->peri_mr, SPI0_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->spi[0]), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[0]), 0, + qdev_get_gpio_in_named(DEVICE(&s->ic), + BCM2835_IC_GPU_IRQ, + INTERRUPT_SPI)); + + /* I2C */ + for (n = 0; n < 3; n++) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c[n]), errp)) { + return; + } + } + + memory_region_add_subregion(&s->peri_mr, BSC0_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->i2c[0]), 0)); + memory_region_add_subregion(&s->peri_mr, BSC1_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->i2c[1]), 0)); + memory_region_add_subregion(&s->peri_mr, BSC2_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->i2c[2]), 0)); + + if (!qdev_realize(DEVICE(&s->orgated_i2c_irq), NULL, errp)) { + return; + } + for (n = 0; n < ORGATED_I2C_IRQ_COUNT; n++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[n]), 0, + qdev_get_gpio_in(DEVICE(&s->orgated_i2c_irq), n)); + } + qdev_connect_gpio_out(DEVICE(&s->orgated_i2c_irq), 0, + qdev_get_gpio_in_named(DEVICE(&s->ic), + BCM2835_IC_GPU_IRQ, + INTERRUPT_I2C)); + create_unimp(s, &s->txp, "bcm2835-txp", TXP_OFFSET, 0x1000); create_unimp(s, &s->armtmr, "bcm2835-sp804", ARMCTRL_TIMER0_1_OFFSET, 0x40); create_unimp(s, &s->i2s, "bcm2835-i2s", I2S_OFFSET, 0x100); create_unimp(s, &s->smi, "bcm2835-smi", SMI_OFFSET, 0x100); - create_unimp(s, &s->spi[0], "bcm2835-spi0", SPI0_OFFSET, 0x20); create_unimp(s, &s->bscsl, "bcm2835-spis", BSC_SL_OFFSET, 0x100); - create_unimp(s, &s->i2c[0], "bcm2835-i2c0", BSC0_OFFSET, 0x20); - create_unimp(s, &s->i2c[1], "bcm2835-i2c1", BSC1_OFFSET, 0x20); - create_unimp(s, &s->i2c[2], "bcm2835-i2c2", BSC2_OFFSET, 0x20); create_unimp(s, &s->otp, "bcm2835-otp", OTP_OFFSET, 0x80); create_unimp(s, &s->dbus, "bcm2835-dbus", DBUS_OFFSET, 0x8000); create_unimp(s, &s->ave0, "bcm2835-ave0", AVE0_OFFSET, 0x8000); @@ -395,21 +510,27 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + BCMSocPeripheralBaseClass *bc = BCM_SOC_PERIPHERALS_BASE_CLASS(oc); + bc->peri_size = 0x1000000; dc->realize = bcm2835_peripherals_realize; } -static const TypeInfo bcm2835_peripherals_type_info = { - .name = TYPE_BCM2835_PERIPHERALS, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(BCM2835PeripheralState), - .instance_init = bcm2835_peripherals_init, - .class_init = bcm2835_peripherals_class_init, +static const TypeInfo bcm2835_peripherals_types[] = { + { + .name = TYPE_BCM2835_PERIPHERALS, + .parent = TYPE_BCM_SOC_PERIPHERALS_BASE, + .instance_size = sizeof(BCM2835PeripheralState), + .instance_init = bcm2835_peripherals_init, + .class_init = bcm2835_peripherals_class_init, + }, { + .name = TYPE_BCM_SOC_PERIPHERALS_BASE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCMSocPeripheralBaseState), + .instance_init = raspi_peripherals_base_init, + .class_size = sizeof(BCMSocPeripheralBaseClass), + .abstract = true, + } }; -static void bcm2835_peripherals_register_types(void) -{ - type_register_static(&bcm2835_peripherals_type_info); -} - -type_init(bcm2835_peripherals_register_types) +DEFINE_TYPES(bcm2835_peripherals_types) diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index 24354338ca..db191661f2 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -15,8 +15,10 @@ #include "hw/arm/bcm2836.h" #include "hw/arm/raspi_platform.h" #include "hw/sysbus.h" +#include "target/arm/cpu-qom.h" +#include "target/arm/gtimer.h" -typedef struct BCM283XClass { +struct BCM283XClass { /*< private >*/ DeviceClass parent_class; /*< public >*/ @@ -26,20 +28,15 @@ typedef struct BCM283XClass { hwaddr peri_base; /* Peripheral base address seen by the CPU */ hwaddr ctrl_base; /* Interrupt controller and mailboxes etc. */ int clusterid; -} BCM283XClass; - -#define BCM283X_CLASS(klass) \ - OBJECT_CLASS_CHECK(BCM283XClass, (klass), TYPE_BCM283X) -#define BCM283X_GET_CLASS(obj) \ - OBJECT_GET_CLASS(BCM283XClass, (obj), TYPE_BCM283X) +}; static Property bcm2836_enabled_cores_property = - DEFINE_PROP_UINT32("enabled-cpus", BCM283XState, enabled_cpus, 0); + DEFINE_PROP_UINT32("enabled-cpus", BCM283XBaseState, enabled_cpus, 0); -static void bcm2836_init(Object *obj) +static void bcm283x_base_init(Object *obj) { - BCM283XState *s = BCM283X(obj); - BCM283XClass *bc = BCM283X_GET_CLASS(obj); + BCM283XBaseState *s = BCM283X_BASE(obj); + BCM283XBaseClass *bc = BCM283X_BASE_GET_CLASS(obj); int n; for (n = 0; n < bc->core_count; n++) { @@ -55,121 +52,130 @@ static void bcm2836_init(Object *obj) object_initialize_child(obj, "control", &s->control, TYPE_BCM2836_CONTROL); } +} + +static void bcm283x_init(Object *obj) +{ + BCM283XState *s = BCM283X(obj); object_initialize_child(obj, "peripherals", &s->peripherals, TYPE_BCM2835_PERIPHERALS); object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals), "board-rev"); + object_property_add_alias(obj, "command-line", OBJECT(&s->peripherals), + "command-line"); object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals), "vcram-size"); + object_property_add_alias(obj, "vcram-base", OBJECT(&s->peripherals), + "vcram-base"); } -static bool bcm283x_common_realize(DeviceState *dev, Error **errp) +bool bcm283x_common_realize(DeviceState *dev, BCMSocPeripheralBaseState *ps, + Error **errp) { - BCM283XState *s = BCM283X(dev); - BCM283XClass *bc = BCM283X_GET_CLASS(dev); + BCM283XBaseState *s = BCM283X_BASE(dev); + BCM283XBaseClass *bc = BCM283X_BASE_GET_CLASS(dev); Object *obj; /* common peripherals from bcm2835 */ obj = object_property_get_link(OBJECT(dev), "ram", &error_abort); - object_property_add_const_link(OBJECT(&s->peripherals), "ram", obj); + object_property_add_const_link(OBJECT(ps), "ram", obj); - if (!sysbus_realize(SYS_BUS_DEVICE(&s->peripherals), errp)) { + if (!sysbus_realize(SYS_BUS_DEVICE(ps), errp)) { return false; } - object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->peripherals), - "sd-bus"); + object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(ps), "sd-bus"); - sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->peripherals), 0, - bc->peri_base, 1); + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(ps), 0, bc->peri_base, 1); return true; } static void bcm2835_realize(DeviceState *dev, Error **errp) { BCM283XState *s = BCM283X(dev); + BCM283XBaseState *s_base = BCM283X_BASE(dev); + BCMSocPeripheralBaseState *ps_base + = BCM_SOC_PERIPHERALS_BASE(&s->peripherals); - if (!bcm283x_common_realize(dev, errp)) { + if (!bcm283x_common_realize(dev, ps_base, errp)) { return; } - if (!qdev_realize(DEVICE(&s->cpu[0].core), NULL, errp)) { + if (!qdev_realize(DEVICE(&s_base->cpu[0].core), NULL, errp)) { return; } /* Connect irq/fiq outputs from the interrupt controller. */ sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 0, - qdev_get_gpio_in(DEVICE(&s->cpu[0].core), ARM_CPU_IRQ)); + qdev_get_gpio_in(DEVICE(&s_base->cpu[0].core), ARM_CPU_IRQ)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 1, - qdev_get_gpio_in(DEVICE(&s->cpu[0].core), ARM_CPU_FIQ)); + qdev_get_gpio_in(DEVICE(&s_base->cpu[0].core), ARM_CPU_FIQ)); } static void bcm2836_realize(DeviceState *dev, Error **errp) { - BCM283XState *s = BCM283X(dev); - BCM283XClass *bc = BCM283X_GET_CLASS(dev); int n; + BCM283XState *s = BCM283X(dev); + BCM283XBaseState *s_base = BCM283X_BASE(dev); + BCM283XBaseClass *bc = BCM283X_BASE_GET_CLASS(dev); + BCMSocPeripheralBaseState *ps_base + = BCM_SOC_PERIPHERALS_BASE(&s->peripherals); - if (!bcm283x_common_realize(dev, errp)) { + if (!bcm283x_common_realize(dev, ps_base, errp)) { return; } /* bcm2836 interrupt controller (and mailboxes, etc.) */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->control), errp)) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s_base->control), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->control), 0, bc->ctrl_base); + sysbus_mmio_map(SYS_BUS_DEVICE(&s_base->control), 0, bc->ctrl_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 0, - qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-irq", 0)); + qdev_get_gpio_in_named(DEVICE(&s_base->control), "gpu-irq", 0)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 1, - qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-fiq", 0)); + qdev_get_gpio_in_named(DEVICE(&s_base->control), "gpu-fiq", 0)); for (n = 0; n < BCM283X_NCPUS; n++) { - /* TODO: this should be converted to a property of ARM_CPU */ - s->cpu[n].core.mp_affinity = (bc->clusterid << 8) | n; + object_property_set_int(OBJECT(&s_base->cpu[n].core), "mp-affinity", + (bc->clusterid << 8) | n, &error_abort); /* set periphbase/CBAR value for CPU-local registers */ - if (!object_property_set_int(OBJECT(&s->cpu[n].core), "reset-cbar", - bc->peri_base, errp)) { - return; - } + object_property_set_int(OBJECT(&s_base->cpu[n].core), "reset-cbar", + bc->peri_base, &error_abort); /* start powered off if not enabled */ - if (!object_property_set_bool(OBJECT(&s->cpu[n].core), - "start-powered-off", - n >= s->enabled_cpus, - errp)) { - return; - } + object_property_set_bool(OBJECT(&s_base->cpu[n].core), + "start-powered-off", + n >= s_base->enabled_cpus, &error_abort); - if (!qdev_realize(DEVICE(&s->cpu[n].core), NULL, errp)) { + if (!qdev_realize(DEVICE(&s_base->cpu[n].core), NULL, errp)) { return; } /* Connect irq/fiq outputs from the interrupt controller. */ - qdev_connect_gpio_out_named(DEVICE(&s->control), "irq", n, - qdev_get_gpio_in(DEVICE(&s->cpu[n].core), ARM_CPU_IRQ)); - qdev_connect_gpio_out_named(DEVICE(&s->control), "fiq", n, - qdev_get_gpio_in(DEVICE(&s->cpu[n].core), ARM_CPU_FIQ)); + qdev_connect_gpio_out_named(DEVICE(&s_base->control), "irq", n, + qdev_get_gpio_in(DEVICE(&s_base->cpu[n].core), ARM_CPU_IRQ)); + qdev_connect_gpio_out_named(DEVICE(&s_base->control), "fiq", n, + qdev_get_gpio_in(DEVICE(&s_base->cpu[n].core), ARM_CPU_FIQ)); /* Connect timers from the CPU to the interrupt controller */ - qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_PHYS, - qdev_get_gpio_in_named(DEVICE(&s->control), "cntpnsirq", n)); - qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_VIRT, - qdev_get_gpio_in_named(DEVICE(&s->control), "cntvirq", n)); - qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_HYP, - qdev_get_gpio_in_named(DEVICE(&s->control), "cnthpirq", n)); - qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_SEC, - qdev_get_gpio_in_named(DEVICE(&s->control), "cntpsirq", n)); + qdev_connect_gpio_out(DEVICE(&s_base->cpu[n].core), GTIMER_PHYS, + qdev_get_gpio_in_named(DEVICE(&s_base->control), "cntpnsirq", n)); + qdev_connect_gpio_out(DEVICE(&s_base->cpu[n].core), GTIMER_VIRT, + qdev_get_gpio_in_named(DEVICE(&s_base->control), "cntvirq", n)); + qdev_connect_gpio_out(DEVICE(&s_base->cpu[n].core), GTIMER_HYP, + qdev_get_gpio_in_named(DEVICE(&s_base->control), "cnthpirq", n)); + qdev_connect_gpio_out(DEVICE(&s_base->cpu[n].core), GTIMER_SEC, + qdev_get_gpio_in_named(DEVICE(&s_base->control), "cntpsirq", n)); } } -static void bcm283x_class_init(ObjectClass *oc, void *data) +static void bcm283x_base_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -180,7 +186,7 @@ static void bcm283x_class_init(ObjectClass *oc, void *data) static void bcm2835_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - BCM283XClass *bc = BCM283X_CLASS(oc); + BCM283XBaseClass *bc = BCM283X_BASE_CLASS(oc); bc->cpu_type = ARM_CPU_TYPE_NAME("arm1176"); bc->core_count = 1; @@ -191,7 +197,7 @@ static void bcm2835_class_init(ObjectClass *oc, void *data) static void bcm2836_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - BCM283XClass *bc = BCM283X_CLASS(oc); + BCM283XBaseClass *bc = BCM283X_BASE_CLASS(oc); bc->cpu_type = ARM_CPU_TYPE_NAME("cortex-a7"); bc->core_count = BCM283X_NCPUS; @@ -205,7 +211,7 @@ static void bcm2836_class_init(ObjectClass *oc, void *data) static void bcm2837_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - BCM283XClass *bc = BCM283X_CLASS(oc); + BCM283XBaseClass *bc = BCM283X_BASE_CLASS(oc); bc->cpu_type = ARM_CPU_TYPE_NAME("cortex-a53"); bc->core_count = BCM283X_NCPUS; @@ -233,11 +239,17 @@ static const TypeInfo bcm283x_types[] = { #endif }, { .name = TYPE_BCM283X, - .parent = TYPE_DEVICE, + .parent = TYPE_BCM283X_BASE, .instance_size = sizeof(BCM283XState), - .instance_init = bcm2836_init, - .class_size = sizeof(BCM283XClass), - .class_init = bcm283x_class_init, + .instance_init = bcm283x_init, + .abstract = true, + }, { + .name = TYPE_BCM283X_BASE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(BCM283XBaseState), + .instance_init = bcm283x_base_init, + .class_size = sizeof(BCM283XBaseClass), + .class_init = bcm283x_base_class_init, .abstract = true, } }; diff --git a/hw/arm/bcm2838.c b/hw/arm/bcm2838.c new file mode 100644 index 0000000000..ddb7c5f757 --- /dev/null +++ b/hw/arm/bcm2838.c @@ -0,0 +1,263 @@ +/* + * BCM2838 SoC emulation + * + * Copyright (C) 2022 Ovchinnikov Vitalii + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "hw/arm/raspi_platform.h" +#include "hw/sysbus.h" +#include "hw/arm/bcm2838.h" +#include "trace.h" + +#define GIC400_MAINTENANCE_IRQ 9 +#define GIC400_TIMER_NS_EL2_IRQ 10 +#define GIC400_TIMER_VIRT_IRQ 11 +#define GIC400_LEGACY_FIQ 12 +#define GIC400_TIMER_S_EL1_IRQ 13 +#define GIC400_TIMER_NS_EL1_IRQ 14 +#define GIC400_LEGACY_IRQ 15 + +/* Number of external interrupt lines to configure the GIC with */ +#define GIC_NUM_IRQS 192 + +#define PPI(cpu, irq) (GIC_NUM_IRQS + (cpu) * GIC_INTERNAL + GIC_NR_SGIS + irq) + +#define GIC_BASE_OFS 0x0000 +#define GIC_DIST_OFS 0x1000 +#define GIC_CPU_OFS 0x2000 +#define GIC_VIFACE_THIS_OFS 0x4000 +#define GIC_VIFACE_OTHER_OFS(cpu) (0x5000 + (cpu) * 0x200) +#define GIC_VCPU_OFS 0x6000 + +#define VIRTUAL_PMU_IRQ 7 + +static void bcm2838_gic_set_irq(void *opaque, int irq, int level) +{ + BCM2838State *s = (BCM2838State *)opaque; + + trace_bcm2838_gic_set_irq(irq, level); + qemu_set_irq(qdev_get_gpio_in(DEVICE(&s->gic), irq), level); +} + +static void bcm2838_init(Object *obj) +{ + BCM2838State *s = BCM2838(obj); + + object_initialize_child(obj, "peripherals", &s->peripherals, + TYPE_BCM2838_PERIPHERALS); + object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals), + "board-rev"); + object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals), + "vcram-size"); + object_property_add_alias(obj, "vcram-base", OBJECT(&s->peripherals), + "vcram-base"); + object_property_add_alias(obj, "command-line", OBJECT(&s->peripherals), + "command-line"); + + object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GIC); +} + +static void bcm2838_realize(DeviceState *dev, Error **errp) +{ + BCM2838State *s = BCM2838(dev); + BCM283XBaseState *s_base = BCM283X_BASE(dev); + BCM283XBaseClass *bc_base = BCM283X_BASE_GET_CLASS(dev); + BCM2838PeripheralState *ps = BCM2838_PERIPHERALS(&s->peripherals); + BCMSocPeripheralBaseState *ps_base = + BCM_SOC_PERIPHERALS_BASE(&s->peripherals); + + DeviceState *gicdev = NULL; + + if (!bcm283x_common_realize(dev, ps_base, errp)) { + return; + } + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(ps), 1, BCM2838_PERI_LOW_BASE, 1); + + /* bcm2836 interrupt controller (and mailboxes, etc.) */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s_base->control), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s_base->control), 0, bc_base->ctrl_base); + + /* Create cores */ + for (int n = 0; n < bc_base->core_count; n++) { + + object_property_set_int(OBJECT(&s_base->cpu[n].core), "mp-affinity", + (bc_base->clusterid << 8) | n, &error_abort); + + /* set periphbase/CBAR value for CPU-local registers */ + object_property_set_int(OBJECT(&s_base->cpu[n].core), "reset-cbar", + bc_base->peri_base, &error_abort); + + /* start powered off if not enabled */ + object_property_set_bool(OBJECT(&s_base->cpu[n].core), + "start-powered-off", + n >= s_base->enabled_cpus, &error_abort); + + if (!qdev_realize(DEVICE(&s_base->cpu[n].core), NULL, errp)) { + return; + } + } + + if (!object_property_set_uint(OBJECT(&s->gic), "revision", 2, errp)) { + return; + } + + if (!object_property_set_uint(OBJECT(&s->gic), "num-cpu", BCM283X_NCPUS, + errp)) { + return; + } + + if (!object_property_set_uint(OBJECT(&s->gic), "num-irq", + GIC_NUM_IRQS + GIC_INTERNAL, errp)) { + return; + } + + if (!object_property_set_bool(OBJECT(&s->gic), + "has-virtualization-extensions", true, + errp)) { + return; + } + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gic), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, + bc_base->ctrl_base + BCM2838_GIC_BASE + GIC_DIST_OFS); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, + bc_base->ctrl_base + BCM2838_GIC_BASE + GIC_CPU_OFS); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 2, + bc_base->ctrl_base + BCM2838_GIC_BASE + GIC_VIFACE_THIS_OFS); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 3, + bc_base->ctrl_base + BCM2838_GIC_BASE + GIC_VCPU_OFS); + + for (int n = 0; n < BCM283X_NCPUS; n++) { + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 4 + n, + bc_base->ctrl_base + BCM2838_GIC_BASE + + GIC_VIFACE_OTHER_OFS(n)); + } + + gicdev = DEVICE(&s->gic); + + for (int n = 0; n < BCM283X_NCPUS; n++) { + DeviceState *cpudev = DEVICE(&s_base->cpu[n]); + + /* Connect the GICv2 outputs to the CPU */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n + BCM283X_NCPUS, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n + 2 * BCM283X_NCPUS, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n + 3 * BCM283X_NCPUS, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), n + 4 * BCM283X_NCPUS, + qdev_get_gpio_in(gicdev, + PPI(n, GIC400_MAINTENANCE_IRQ))); + + /* Connect timers from the CPU to the interrupt controller */ + qdev_connect_gpio_out(cpudev, GTIMER_PHYS, + qdev_get_gpio_in(gicdev, PPI(n, GIC400_TIMER_NS_EL1_IRQ))); + qdev_connect_gpio_out(cpudev, GTIMER_VIRT, + qdev_get_gpio_in(gicdev, PPI(n, GIC400_TIMER_VIRT_IRQ))); + qdev_connect_gpio_out(cpudev, GTIMER_HYP, + qdev_get_gpio_in(gicdev, PPI(n, GIC400_TIMER_NS_EL2_IRQ))); + qdev_connect_gpio_out(cpudev, GTIMER_SEC, + qdev_get_gpio_in(gicdev, PPI(n, GIC400_TIMER_S_EL1_IRQ))); + /* PMU interrupt */ + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, + qdev_get_gpio_in(gicdev, PPI(n, VIRTUAL_PMU_IRQ))); + } + + /* Connect UART0 to the interrupt controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->uart0), 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_UART0)); + + /* Connect AUX / UART1 to the interrupt controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->aux), 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_AUX_UART1)); + + /* Connect VC mailbox to the interrupt controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->mboxes), 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_MBOX)); + + /* Connect SD host to the interrupt controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->sdhost), 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_SDHOST)); + + /* According to DTS, EMMC and EMMC2 share one irq */ + DeviceState *mmc_irq_orgate = DEVICE(&ps->mmc_irq_orgate); + + /* Connect EMMC and EMMC2 to the interrupt controller */ + qdev_connect_gpio_out(mmc_irq_orgate, 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_EMMC_EMMC2)); + + /* Connect USB OTG and MPHI to the interrupt controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->mphi), 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_MPHI)); + sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->dwc2), 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_DWC2)); + + /* Connect DMA 0-6 to the interrupt controller */ + for (int n = GIC_SPI_INTERRUPT_DMA_0; n <= GIC_SPI_INTERRUPT_DMA_6; n++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->dma), + n - GIC_SPI_INTERRUPT_DMA_0, + qdev_get_gpio_in(gicdev, n)); + } + + /* According to DTS, DMA 7 and 8 share one irq */ + DeviceState *dma_7_8_irq_orgate = DEVICE(&ps->dma_7_8_irq_orgate); + + /* Connect DMA 7-8 to the interrupt controller */ + qdev_connect_gpio_out(dma_7_8_irq_orgate, 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_DMA_7_8)); + + /* According to DTS, DMA 9 and 10 share one irq */ + DeviceState *dma_9_10_irq_orgate = DEVICE(&ps->dma_9_10_irq_orgate); + + /* Connect DMA 9-10 to the interrupt controller */ + qdev_connect_gpio_out(dma_9_10_irq_orgate, 0, + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_DMA_9_10)); + + /* Pass through inbound GPIO lines to the GIC */ + qdev_init_gpio_in(dev, bcm2838_gic_set_irq, GIC_NUM_IRQS); + + /* Pass through outbound IRQ lines from the GIC */ + qdev_pass_gpios(DEVICE(&s->gic), DEVICE(&s->peripherals), NULL); +} + +static void bcm2838_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + BCM283XBaseClass *bc_base = BCM283X_BASE_CLASS(oc); + + bc_base->cpu_type = ARM_CPU_TYPE_NAME("cortex-a72"); + bc_base->core_count = BCM283X_NCPUS; + bc_base->peri_base = 0xfe000000; + bc_base->ctrl_base = 0xff800000; + bc_base->clusterid = 0x0; + dc->realize = bcm2838_realize; +} + +static const TypeInfo bcm2838_type = { + .name = TYPE_BCM2838, + .parent = TYPE_BCM283X_BASE, + .instance_size = sizeof(BCM2838State), + .instance_init = bcm2838_init, + .class_size = sizeof(BCM283XBaseClass), + .class_init = bcm2838_class_init, +}; + +static void bcm2838_register_types(void) +{ + type_register_static(&bcm2838_type); +} + +type_init(bcm2838_register_types); diff --git a/hw/arm/bcm2838_peripherals.c b/hw/arm/bcm2838_peripherals.c new file mode 100644 index 0000000000..e28bef4a37 --- /dev/null +++ b/hw/arm/bcm2838_peripherals.c @@ -0,0 +1,224 @@ +/* + * BCM2838 peripherals emulation + * + * Copyright (C) 2022 Ovchinnikov Vitalii + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "hw/arm/raspi_platform.h" +#include "hw/arm/bcm2838_peripherals.h" + +#define CLOCK_ISP_OFFSET 0xc11000 +#define CLOCK_ISP_SIZE 0x100 + +/* Lower peripheral base address on the VC (GPU) system bus */ +#define BCM2838_VC_PERI_LOW_BASE 0x7c000000 + +/* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */ +#define BCM2835_SDHC_CAPAREG 0x52134b4 + +static void bcm2838_peripherals_init(Object *obj) +{ + BCM2838PeripheralState *s = BCM2838_PERIPHERALS(obj); + BCM2838PeripheralClass *bc = BCM2838_PERIPHERALS_GET_CLASS(obj); + BCMSocPeripheralBaseState *s_base = BCM_SOC_PERIPHERALS_BASE(obj); + + /* Lower memory region for peripheral devices (exported to the Soc) */ + memory_region_init(&s->peri_low_mr, obj, "bcm2838-peripherals", + bc->peri_low_size); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->peri_low_mr); + + /* Extended Mass Media Controller 2 */ + object_initialize_child(obj, "emmc2", &s->emmc2, TYPE_SYSBUS_SDHCI); + + /* GPIO */ + object_initialize_child(obj, "gpio", &s->gpio, TYPE_BCM2838_GPIO); + + object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhci", + OBJECT(&s_base->sdhci.sdbus)); + object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhost", + OBJECT(&s_base->sdhost.sdbus)); + + object_initialize_child(obj, "mmc_irq_orgate", &s->mmc_irq_orgate, + TYPE_OR_IRQ); + object_property_set_int(OBJECT(&s->mmc_irq_orgate), "num-lines", 2, + &error_abort); + + object_initialize_child(obj, "dma_7_8_irq_orgate", &s->dma_7_8_irq_orgate, + TYPE_OR_IRQ); + object_property_set_int(OBJECT(&s->dma_7_8_irq_orgate), "num-lines", 2, + &error_abort); + + object_initialize_child(obj, "dma_9_10_irq_orgate", &s->dma_9_10_irq_orgate, + TYPE_OR_IRQ); + object_property_set_int(OBJECT(&s->dma_9_10_irq_orgate), "num-lines", 2, + &error_abort); +} + +static void bcm2838_peripherals_realize(DeviceState *dev, Error **errp) +{ + DeviceState *mmc_irq_orgate; + DeviceState *dma_7_8_irq_orgate; + DeviceState *dma_9_10_irq_orgate; + MemoryRegion *mphi_mr; + BCM2838PeripheralState *s = BCM2838_PERIPHERALS(dev); + BCMSocPeripheralBaseState *s_base = BCM_SOC_PERIPHERALS_BASE(dev); + int n; + + bcm_soc_peripherals_common_realize(dev, errp); + + /* Map lower peripherals into the GPU address space */ + memory_region_init_alias(&s->peri_low_mr_alias, OBJECT(s), + "bcm2838-peripherals", &s->peri_low_mr, 0, + memory_region_size(&s->peri_low_mr)); + memory_region_add_subregion_overlap(&s_base->gpu_bus_mr, + BCM2838_VC_PERI_LOW_BASE, + &s->peri_low_mr_alias, 1); + + /* Extended Mass Media Controller 2 */ + object_property_set_uint(OBJECT(&s->emmc2), "sd-spec-version", 3, + &error_abort); + object_property_set_uint(OBJECT(&s->emmc2), "capareg", + BCM2835_SDHC_CAPAREG, &error_abort); + object_property_set_bool(OBJECT(&s->emmc2), "pending-insert-quirk", true, + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->emmc2), errp)) { + return; + } + + memory_region_add_subregion(&s_base->peri_mr, EMMC2_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->emmc2), + 0)); + + /* According to DTS, EMMC and EMMC2 share one irq */ + if (!qdev_realize(DEVICE(&s->mmc_irq_orgate), NULL, errp)) { + return; + } + + mmc_irq_orgate = DEVICE(&s->mmc_irq_orgate); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc2), 0, + qdev_get_gpio_in(mmc_irq_orgate, 0)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->sdhci), 0, + qdev_get_gpio_in(mmc_irq_orgate, 1)); + + /* Connect EMMC and EMMC2 to the interrupt controller */ + qdev_connect_gpio_out(mmc_irq_orgate, 0, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + INTERRUPT_ARASANSDIO)); + + /* Connect DMA 0-6 to the interrupt controller */ + for (n = 0; n < 7; n++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), n, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + GPU_INTERRUPT_DMA0 + n)); + } + + /* According to DTS, DMA 7 and 8 share one irq */ + if (!qdev_realize(DEVICE(&s->dma_7_8_irq_orgate), NULL, errp)) { + return; + } + dma_7_8_irq_orgate = DEVICE(&s->dma_7_8_irq_orgate); + + /* Connect DMA 7-8 to the interrupt controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 7, + qdev_get_gpio_in(dma_7_8_irq_orgate, 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 8, + qdev_get_gpio_in(dma_7_8_irq_orgate, 1)); + + qdev_connect_gpio_out(dma_7_8_irq_orgate, 0, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + GPU_INTERRUPT_DMA7_8)); + + /* According to DTS, DMA 9 and 10 share one irq */ + if (!qdev_realize(DEVICE(&s->dma_9_10_irq_orgate), NULL, errp)) { + return; + } + dma_9_10_irq_orgate = DEVICE(&s->dma_9_10_irq_orgate); + + /* Connect DMA 9-10 to the interrupt controller */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 9, + qdev_get_gpio_in(dma_9_10_irq_orgate, 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 10, + qdev_get_gpio_in(dma_9_10_irq_orgate, 1)); + + qdev_connect_gpio_out(dma_9_10_irq_orgate, 0, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + GPU_INTERRUPT_DMA9_10)); + + /* Connect DMA 11-14 to the interrupt controller */ + for (n = 11; n < 15; n++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), n, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + GPU_INTERRUPT_DMA11 + n + - 11)); + } + + /* + * Connect DMA 15 to the interrupt controller, it is physically removed + * from other DMA channels and exclusively used by the GPU + */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 15, + qdev_get_gpio_in_named(DEVICE(&s_base->ic), + BCM2835_IC_GPU_IRQ, + GPU_INTERRUPT_DMA15)); + + /* Map MPHI to BCM2838 memory map */ + mphi_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s_base->mphi), 0); + memory_region_init_alias(&s->mphi_mr_alias, OBJECT(s), "mphi", mphi_mr, 0, + BCM2838_MPHI_SIZE); + memory_region_add_subregion(&s_base->peri_mr, BCM2838_MPHI_OFFSET, + &s->mphi_mr_alias); + + create_unimp(s_base, &s->clkisp, "bcm2835-clkisp", CLOCK_ISP_OFFSET, + CLOCK_ISP_SIZE); + + /* GPIO */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { + return; + } + memory_region_add_subregion( + &s_base->peri_mr, GPIO_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0)); + + object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->gpio), "sd-bus"); + + /* BCM2838 RPiVid ASB must be mapped to prevent kernel crash */ + create_unimp(s_base, &s->asb, "bcm2838-asb", BRDG_OFFSET, 0x24); +} + +static void bcm2838_peripherals_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + BCM2838PeripheralClass *bc = BCM2838_PERIPHERALS_CLASS(oc); + BCMSocPeripheralBaseClass *bc_base = BCM_SOC_PERIPHERALS_BASE_CLASS(oc); + + bc->peri_low_size = 0x2000000; + bc_base->peri_size = 0x1800000; + dc->realize = bcm2838_peripherals_realize; +} + +static const TypeInfo bcm2838_peripherals_type_info = { + .name = TYPE_BCM2838_PERIPHERALS, + .parent = TYPE_BCM_SOC_PERIPHERALS_BASE, + .instance_size = sizeof(BCM2838PeripheralState), + .instance_init = bcm2838_peripherals_init, + .class_size = sizeof(BCM2838PeripheralClass), + .class_init = bcm2838_peripherals_class_init, +}; + +static void bcm2838_peripherals_register_types(void) +{ + type_register_static(&bcm2838_peripherals_type_info); +} + +type_init(bcm2838_peripherals_register_types) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index a8de33fd64..84ea6a807a 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -15,6 +15,7 @@ #include "hw/arm/boot.h" #include "hw/arm/linux-boot-if.h" #include "sysemu/kvm.h" +#include "sysemu/tcg.h" #include "sysemu/sysemu.h" #include "sysemu/numa.h" #include "hw/boards.h" @@ -59,26 +60,6 @@ AddressSpace *arm_boot_address_space(ARMCPU *cpu, return cpu_get_address_space(cs, asidx); } -typedef enum { - FIXUP_NONE = 0, /* do nothing */ - FIXUP_TERMINATOR, /* end of insns */ - FIXUP_BOARDID, /* overwrite with board ID number */ - FIXUP_BOARD_SETUP, /* overwrite with board specific setup code address */ - FIXUP_ARGPTR_LO, /* overwrite with pointer to kernel args */ - FIXUP_ARGPTR_HI, /* overwrite with pointer to kernel args (high half) */ - FIXUP_ENTRYPOINT_LO, /* overwrite with kernel entry point */ - FIXUP_ENTRYPOINT_HI, /* overwrite with kernel entry point (high half) */ - FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */ - FIXUP_BOOTREG, /* overwrite with boot register address */ - FIXUP_DSB, /* overwrite with correct DSB insn for cpu */ - FIXUP_MAX, -} FixupType; - -typedef struct ARMInsnFixup { - uint32_t insn; - FixupType fixup; -} ARMInsnFixup; - static const ARMInsnFixup bootloader_aarch64[] = { { 0x580000c0 }, /* ldr x0, arg ; Load the lower 32-bits of DTB */ { 0xaa1f03e1 }, /* mov x1, xzr */ @@ -149,9 +130,10 @@ static const ARMInsnFixup smpboot[] = { { 0, FIXUP_TERMINATOR } }; -static void write_bootloader(const char *name, hwaddr addr, - const ARMInsnFixup *insns, uint32_t *fixupcontext, - AddressSpace *as) +void arm_write_bootloader(const char *name, + AddressSpace *as, hwaddr addr, + const ARMInsnFixup *insns, + const uint32_t *fixupcontext) { /* Fix up the specified bootloader fragment and write it into * guest memory using rom_add_blob_fixed(). fixupcontext is @@ -213,8 +195,8 @@ static void default_write_secondary(ARMCPU *cpu, fixupcontext[FIXUP_DSB] = CP15_DSB_INSN; } - write_bootloader("smpboot", info->smp_loader_start, - smpboot, fixupcontext, as); + arm_write_bootloader("smpboot", as, info->smp_loader_start, + smpboot, fixupcontext); } void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu, @@ -656,15 +638,17 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, } if (binfo->initrd_size) { - rc = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", - binfo->initrd_start); + rc = qemu_fdt_setprop_sized_cells(fdt, "/chosen", "linux,initrd-start", + acells, binfo->initrd_start); if (rc < 0) { fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); goto fail; } - rc = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", - binfo->initrd_start + binfo->initrd_size); + rc = qemu_fdt_setprop_sized_cells(fdt, "/chosen", "linux,initrd-end", + acells, + binfo->initrd_start + + binfo->initrd_size); if (rc < 0) { fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); goto fail; @@ -683,8 +667,13 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, * the DTB is copied again upon reset, even if addr points into RAM. */ rom_add_blob_fixed_as("dtb", fdt, size, addr, as); + qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, + rom_ptr_for_as(as, addr, size)); - g_free(fdt); + if (fdt != ms->fdt) { + g_free(ms->fdt); + ms->fdt = fdt; + } return size; @@ -733,71 +722,35 @@ static void do_cpu_reset(void *opaque) cpu_set_pc(cs, entry); } else { - /* If we are booting Linux then we need to check whether we are - * booting into secure or non-secure state and adjust the state - * accordingly. Out of reset, ARM is defined to be in secure state - * (SCR.NS = 0), we change that here if non-secure boot has been - * requested. + /* + * If we are booting Linux then we might need to do so at: + * - AArch64 NS EL2 or NS EL1 + * - AArch32 Secure SVC (EL3) + * - AArch32 NS Hyp (EL2) + * - AArch32 NS SVC (EL1) + * Configure the CPU in the way boot firmware would do to + * drop us down to the appropriate level. */ - if (arm_feature(env, ARM_FEATURE_EL3)) { - /* AArch64 is defined to come out of reset into EL3 if enabled. - * If we are booting Linux then we need to adjust our EL as - * Linux expects us to be in EL2 or EL1. AArch32 resets into - * SVC, which Linux expects, so no privilege/exception level to - * adjust. - */ - if (env->aarch64) { - env->cp15.scr_el3 |= SCR_RW; - if (arm_feature(env, ARM_FEATURE_EL2)) { - env->cp15.hcr_el2 |= HCR_RW; - env->pstate = PSTATE_MODE_EL2h; - } else { - env->pstate = PSTATE_MODE_EL1h; - } - if (cpu_isar_feature(aa64_pauth, cpu)) { - env->cp15.scr_el3 |= SCR_API | SCR_APK; - } - if (cpu_isar_feature(aa64_mte, cpu)) { - env->cp15.scr_el3 |= SCR_ATA; - } - if (cpu_isar_feature(aa64_sve, cpu)) { - env->cp15.cptr_el[3] |= R_CPTR_EL3_EZ_MASK; - } - /* AArch64 kernels never boot in secure mode */ - assert(!info->secure_boot); - /* This hook is only supported for AArch32 currently: - * bootloader_aarch64[] will not call the hook, and - * the code above has already dropped us into EL2 or EL1. - */ - assert(!info->secure_board_setup); - } + int target_el = arm_feature(env, ARM_FEATURE_EL2) ? 2 : 1; - if (arm_feature(env, ARM_FEATURE_EL2)) { - /* If we have EL2 then Linux expects the HVC insn to work */ - env->cp15.scr_el3 |= SCR_HCE; - } - - /* Set to non-secure if not a secure boot */ - if (!info->secure_boot && - (cs != first_cpu || !info->secure_board_setup)) { - /* Linux expects non-secure state */ - env->cp15.scr_el3 |= SCR_NS; - /* Set NSACR.{CP11,CP10} so NS can access the FPU */ - env->cp15.nsacr |= 3 << 10; - } - } - - if (!env->aarch64 && !info->secure_boot && - arm_feature(env, ARM_FEATURE_EL2)) { + if (env->aarch64) { /* - * This is an AArch32 boot not to Secure state, and - * we have Hyp mode available, so boot the kernel into - * Hyp mode. This is not how the CPU comes out of reset, - * so we need to manually put it there. + * AArch64 kernels never boot in secure mode, and we don't + * support the secure_board_setup hook for AArch64. */ - cpsr_write(env, ARM_CPU_MODE_HYP, CPSR_M, CPSRWriteRaw); + assert(!info->secure_boot); + assert(!info->secure_board_setup); + } else { + if (arm_feature(env, ARM_FEATURE_EL3) && + (info->secure_boot || + (info->secure_board_setup && cs == first_cpu))) { + /* Start this CPU in Secure SVC */ + target_el = 3; + } } + arm_emulate_firmware_reset(cs, target_el); + if (cs == first_cpu) { AddressSpace *as = arm_boot_address_space(cpu, info); @@ -814,57 +767,11 @@ static void do_cpu_reset(void *opaque) info->secondary_cpu_reset_hook(cpu, info); } } - arm_rebuild_hflags(env); - } -} -/** - * load_image_to_fw_cfg() - Load an image file into an fw_cfg entry identified - * by key. - * @fw_cfg: The firmware config instance to store the data in. - * @size_key: The firmware config key to store the size of the loaded - * data under, with fw_cfg_add_i32(). - * @data_key: The firmware config key to store the loaded data under, - * with fw_cfg_add_bytes(). - * @image_name: The name of the image file to load. If it is NULL, the - * function returns without doing anything. - * @try_decompress: Whether the image should be decompressed (gunzipped) before - * adding it to fw_cfg. If decompression fails, the image is - * loaded as-is. - * - * In case of failure, the function prints an error message to stderr and the - * process exits with status 1. - */ -static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key, - uint16_t data_key, const char *image_name, - bool try_decompress) -{ - size_t size = -1; - uint8_t *data; - - if (image_name == NULL) { - return; - } - - if (try_decompress) { - size = load_image_gzipped_buffer(image_name, - LOAD_IMAGE_MAX_GUNZIP_BYTES, &data); - } - - if (size == (size_t)-1) { - gchar *contents; - gsize length; - - if (!g_file_get_contents(image_name, &contents, &length, NULL)) { - error_report("failed to load \"%s\"", image_name); - exit(1); + if (tcg_enabled()) { + arm_rebuild_hflags(env); } - size = length; - data = (uint8_t *)contents; } - - fw_cfg_add_i32(fw_cfg, size_key, size); - fw_cfg_add_bytes(fw_cfg, data_key, data, size); } static int do_arm_linux_init(Object *obj, void *opaque) @@ -881,7 +788,7 @@ static int do_arm_linux_init(Object *obj, void *opaque) return 0; } -static int64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, +static ssize_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, int elf_machine, AddressSpace *as) { @@ -892,7 +799,7 @@ static int64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, } elf_header; int data_swab = 0; bool big_endian; - int64_t ret = -1; + ssize_t ret = -1; Error *err = NULL; @@ -958,6 +865,12 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base, return -1; } size = len; + + /* Unpack the image if it is a EFI zboot image */ + if (unpack_efi_zboot_image(&buffer, &size) < 0) { + g_free(buffer); + return -1; + } } /* check the arm64 magic header value -- very old kernels may not have it */ @@ -1014,7 +927,7 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu, /* Set up for a direct boot of a kernel image file. */ CPUState *cs; AddressSpace *as = arm_boot_address_space(cpu, info); - int kernel_size; + ssize_t kernel_size; int initrd_size; int is_linux = 0; uint64_t elf_entry; @@ -1093,7 +1006,7 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu, if (kernel_size > info->ram_size) { error_report("kernel '%s' is too large to fit in RAM " - "(kernel size %d, RAM size %" PRId64 ")", + "(kernel size %zd, RAM size %" PRId64 ")", info->kernel_filename, kernel_size, info->ram_size); exit(1); } @@ -1209,8 +1122,8 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu, fixupcontext[FIXUP_ENTRYPOINT_LO] = entry; fixupcontext[FIXUP_ENTRYPOINT_HI] = entry >> 32; - write_bootloader("bootloader", info->loader_start, - primary_loader, fixupcontext, as); + arm_write_bootloader("bootloader", as, info->loader_start, + primary_loader, fixupcontext); if (info->write_board_setup) { info->write_board_setup(cpu, info); diff --git a/hw/arm/collie.c b/hw/arm/collie.c index 8df31e2793..eaa5c52d45 100644 --- a/hw/arm/collie.c +++ b/hw/arm/collie.c @@ -17,8 +17,13 @@ #include "hw/arm/boot.h" #include "hw/block/flash.h" #include "exec/address-spaces.h" -#include "cpu.h" #include "qom/object.h" +#include "qemu/error-report.h" + + +#define RAM_SIZE (512 * MiB) +#define FLASH_SIZE (32 * MiB) +#define FLASH_SECTOR_SIZE (64 * KiB) struct CollieMachineState { MachineState parent; @@ -31,12 +36,11 @@ OBJECT_DECLARE_SIMPLE_TYPE(CollieMachineState, COLLIE_MACHINE) static struct arm_boot_info collie_binfo = { .loader_start = SA_SDCS0, - .ram_size = 0x20000000, + .ram_size = RAM_SIZE, }; static void collie_init(MachineState *machine) { - DriveInfo *dinfo; MachineClass *mc = MACHINE_GET_CLASS(machine); CollieMachineState *cms = COLLIE_MACHINE(machine); @@ -51,15 +55,13 @@ static void collie_init(MachineState *machine) 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, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - 64 * KiB, 4, 0x00, 0x00, 0x00, 0x00, 0); - - dinfo = drive_get(IF_PFLASH, 0, 1); - pflash_cfi01_register(SA_CS1, "collie.fl2", 0x02000000, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - 64 * KiB, 4, 0x00, 0x00, 0x00, 0x00, 0); + for (unsigned i = 0; i < 2; i++) { + DriveInfo *dinfo = drive_get(IF_PFLASH, 0, i); + pflash_cfi01_register(i ? SA_CS1 : SA_CS0, + i ? "collie.fl2" : "collie.fl1", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + FLASH_SECTOR_SIZE, 4, 0x00, 0x00, 0x00, 0x00, 0); + } sysbus_create_simple("scoop", 0x40800000, NULL); @@ -75,7 +77,7 @@ static void collie_machine_class_init(ObjectClass *oc, void *data) 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_size = RAM_SIZE; mc->default_ram_id = "strongarm.sdram"; } diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index 5e3372a3c7..b976727eef 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -17,9 +17,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "hw/boards.h" #include "hw/qdev-properties.h" #include "hw/arm/allwinner-a10.h" +#include "hw/arm/boot.h" +#include "hw/i2c/i2c.h" static struct arm_boot_info cubieboard_binfo = { .loader_start = AW_A10_SDRAM_BASE, @@ -34,6 +37,7 @@ static void cubieboard_init(MachineState *machine) BlockBackend *blk; BusState *bus; DeviceState *carddev; + I2CBus *i2c; /* BIOS is not supported by this board */ if (machine->firmware) { @@ -48,12 +52,6 @@ static void cubieboard_init(MachineState *machine) 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)); object_unref(OBJECT(a10)); @@ -80,6 +78,10 @@ static void cubieboard_init(MachineState *machine) exit(1); } + /* Connect AXP 209 */ + i2c = I2C_BUS(qdev_get_child_bus(DEVICE(&a10->i2c0), "i2c")); + i2c_slave_create_simple(i2c, "axp209_pmu", 0x34); + /* Retrieve SD bus */ di = drive_get(IF_SD, 0, 0); blk = di ? blk_by_legacy_dinfo(di) : NULL; @@ -93,6 +95,11 @@ static void cubieboard_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE, 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_a10_bootrom_setup(a10, blk); + } /* TODO create and connect IDE devices for ide_drive_get() */ cubieboard_binfo.ram_size = machine->ram_size; @@ -101,8 +108,14 @@ static void cubieboard_init(MachineState *machine) static void cubieboard_machine_init(MachineClass *mc) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a8"), + NULL + }; + mc->desc = "cubietech cubieboard (Cortex-A8)"; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a8"); + mc->valid_cpu_types = valid_cpu_types; mc->default_ram_size = 1 * GiB; mc->init = cubieboard_init; mc->block_default_type = IF_IDE; diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index 8dafa2215b..e3f1de2631 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -23,6 +23,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "exec/tswap.h" #include "cpu.h" #include "hw/cpu/a9mpcore.h" #include "hw/irq.h" @@ -35,6 +36,7 @@ #include "hw/arm/exynos4210.h" #include "hw/sd/sdhci.h" #include "hw/usb/hcd-ehci.h" +#include "target/arm/cpu-qom.h" #define EXYNOS4210_CHIPID_ADDR 0x10000000 @@ -326,7 +328,7 @@ static int mapline_size(const int *mapline) /* * Initialize board IRQs. - * These IRQs contain splitted Int/External Combiner and External Gic IRQs. + * These IRQs contain split Int/External Combiner and External Gic IRQs. */ static void exynos4210_init_board_irqs(Exynos4210State *s) { @@ -507,7 +509,7 @@ static uint64_t exynos4210_calc_affinity(int cpu) return (0x9 << ARM_AFF1_SHIFT) | cpu; } -static DeviceState *pl330_create(uint32_t base, qemu_or_irq *orgate, +static DeviceState *pl330_create(uint32_t base, OrIRQState *orgate, qemu_irq irq, int nreq, int nevents, int width) { SysBusDevice *busdev; @@ -554,6 +556,7 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp) for (n = 0; n < EXYNOS4210_NCPUS; n++) { Object *cpuobj = object_new(ARM_CPU_TYPE_NAME("cortex-a9")); + object_property_add_child(OBJECT(s), "cpu[*]", cpuobj); /* By default A9 CPUs have EL3 enabled. This board does not currently * support EL3 so the CPU EL3 property is disabled before realization. */ @@ -744,7 +747,7 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp) * - SDMA * - ADMA2 * - * As this part of the Exynos4210 is not publically available, + * As this part of the Exynos4210 is not publicly available, * we used the "HS-MMC Controller S3C2416X RISC Microprocessor" * public datasheet which is very similar (implementing * MMC Specification Version 4.0 being the only difference noted) @@ -766,11 +769,15 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp) } /*** Display controller (FIMD) ***/ - sysbus_create_varargs("exynos4210.fimd", EXYNOS4210_FIMD0_BASE_ADDR, - s->irq_table[exynos4210_get_irq(11, 0)], - s->irq_table[exynos4210_get_irq(11, 1)], - s->irq_table[exynos4210_get_irq(11, 2)], - NULL); + dev = qdev_new("exynos4210.fimd"); + object_property_set_link(OBJECT(dev), "framebuffer-memory", + OBJECT(system_mem), &error_fatal); + busdev = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(busdev, &error_fatal); + sysbus_mmio_map(busdev, 0, EXYNOS4210_FIMD0_BASE_ADDR); + for (n = 0; n < 3; n++) { + sysbus_connect_irq(busdev, n, s->irq_table[exynos4210_get_irq(11, n)]); + } sysbus_create_simple(TYPE_EXYNOS4210_EHCI, EXYNOS4210_EHCI_BASE_ADDR, s->irq_table[exynos4210_get_irq(28, 3)]); @@ -806,7 +813,7 @@ static void exynos4210_init(Object *obj) 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]; + OrIRQState *orgate = &s->pl330_irq_orgate[i]; object_initialize_child(obj, name, orgate, TYPE_OR_IRQ); g_free(name); diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c index ef5bcbc212..2410e2a28e 100644 --- a/hw/arm/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -34,6 +34,7 @@ #include "hw/qdev-properties.h" #include "hw/boards.h" #include "hw/irq.h" +#include "target/arm/cpu-qom.h" #define SMDK_LAN9118_BASE_ADDR 0x05000000 @@ -76,10 +77,8 @@ static void lan9215_init(uint32_t base, qemu_irq irq) SysBusDevice *s; /* This should be a 9215 but the 9118 is close enough */ - if (nd_table[0].used) { - qemu_check_nic_model(&nd_table[0], "lan9118"); - dev = qdev_new(TYPE_LAN9118); - qdev_set_nic_properties(dev, &nd_table[0]); + dev = qemu_create_nic_device(TYPE_LAN9118, true, NULL); + if (dev) { qdev_prop_set_uint32(dev, "mode_16bit", 1); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); @@ -134,9 +133,10 @@ exynos4_boards_init_common(MachineState *machine, static void nuri_init(MachineState *machine) { - exynos4_boards_init_common(machine, EXYNOS4_BOARD_NURI); + Exynos4BoardState *s = exynos4_boards_init_common(machine, + EXYNOS4_BOARD_NURI); - arm_load_kernel(ARM_CPU(first_cpu), machine, &exynos4_board_binfo); + arm_load_kernel(s->soc.cpu[0], machine, &exynos4_board_binfo); } static void smdkc210_init(MachineState *machine) @@ -146,15 +146,21 @@ static void smdkc210_init(MachineState *machine) lan9215_init(SMDK_LAN9118_BASE_ADDR, qemu_irq_invert(s->soc.irq_table[exynos4210_get_irq(37, 1)])); - arm_load_kernel(ARM_CPU(first_cpu), machine, &exynos4_board_binfo); + arm_load_kernel(s->soc.cpu[0], machine, &exynos4_board_binfo); } +static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a9"), + NULL +}; + static void nuri_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->desc = "Samsung NURI board (Exynos4210)"; mc->init = nuri_init; + mc->valid_cpu_types = valid_cpu_types; mc->max_cpus = EXYNOS4210_NCPUS; mc->min_cpus = EXYNOS4210_NCPUS; mc->default_cpus = EXYNOS4210_NCPUS; @@ -173,6 +179,7 @@ static void smdkc210_class_init(ObjectClass *oc, void *data) mc->desc = "Samsung SMDKC210 board (Exynos4210)"; mc->init = smdkc210_init; + mc->valid_cpu_types = valid_cpu_types; mc->max_cpus = EXYNOS4210_NCPUS; mc->min_cpus = EXYNOS4210_NCPUS; mc->default_cpus = EXYNOS4210_NCPUS; diff --git a/hw/arm/fby35.c b/hw/arm/fby35.c new file mode 100644 index 0000000000..c9964bd283 --- /dev/null +++ b/hw/arm/fby35.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com) + * + * 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/units.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "sysemu/block-backend.h" +#include "hw/boards.h" +#include "hw/qdev-clock.h" +#include "hw/arm/aspeed_soc.h" +#include "hw/arm/boot.h" + +#define TYPE_FBY35 MACHINE_TYPE_NAME("fby35") +OBJECT_DECLARE_SIMPLE_TYPE(Fby35State, FBY35); + +struct Fby35State { + MachineState parent_obj; + + MemoryRegion bmc_memory; + MemoryRegion bmc_dram; + MemoryRegion bmc_boot_rom; + MemoryRegion bic_memory; + Clock *bic_sysclk; + + Aspeed2600SoCState bmc; + Aspeed10x0SoCState bic; + + bool mmio_exec; +}; + +#define FBY35_BMC_RAM_SIZE (2 * GiB) +#define FBY35_BMC_FIRMWARE_ADDR 0x0 + +static void fby35_bmc_write_boot_rom(DriveInfo *dinfo, MemoryRegion *mr, + hwaddr offset, size_t rom_size, + Error **errp) +{ + BlockBackend *blk = blk_by_legacy_dinfo(dinfo); + g_autofree void *storage = NULL; + int64_t size; + + /* + * The block backend size should have already been 'validated' by + * the creation of the m25p80 object. + */ + size = blk_getlength(blk); + if (size <= 0) { + error_setg(errp, "failed to get flash size"); + return; + } + + if (rom_size > size) { + rom_size = size; + } + + storage = g_malloc0(rom_size); + if (blk_pread(blk, 0, rom_size, storage, 0) < 0) { + error_setg(errp, "failed to read the initial flash content"); + return; + } + + /* TODO: find a better way to install the ROM */ + memcpy(memory_region_get_ram_ptr(mr) + offset, storage, rom_size); +} + +static void fby35_bmc_init(Fby35State *s) +{ + AspeedSoCState *soc; + + object_initialize_child(OBJECT(s), "bmc", &s->bmc, "ast2600-a3"); + soc = ASPEED_SOC(&s->bmc); + + memory_region_init(&s->bmc_memory, OBJECT(&s->bmc), "bmc-memory", + UINT64_MAX); + memory_region_init_ram(&s->bmc_dram, OBJECT(&s->bmc), "bmc-dram", + FBY35_BMC_RAM_SIZE, &error_abort); + + object_property_set_int(OBJECT(&s->bmc), "ram-size", FBY35_BMC_RAM_SIZE, + &error_abort); + object_property_set_link(OBJECT(&s->bmc), "memory", OBJECT(&s->bmc_memory), + &error_abort); + object_property_set_link(OBJECT(&s->bmc), "dram", OBJECT(&s->bmc_dram), + &error_abort); + object_property_set_int(OBJECT(&s->bmc), "hw-strap1", 0x000000C0, + &error_abort); + object_property_set_int(OBJECT(&s->bmc), "hw-strap2", 0x00000003, + &error_abort); + aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART5, serial_hd(0)); + qdev_realize(DEVICE(&s->bmc), NULL, &error_abort); + + aspeed_board_init_flashes(&soc->fmc, "n25q00", 2, 0); + + /* Install first FMC flash content as a boot rom. */ + if (!s->mmio_exec) { + DriveInfo *mtd0 = drive_get(IF_MTD, 0, 0); + + if (mtd0) { + uint64_t rom_size = memory_region_size(&soc->spi_boot); + + memory_region_init_rom(&s->bmc_boot_rom, NULL, "aspeed.boot_rom", + rom_size, &error_abort); + memory_region_add_subregion_overlap(&soc->spi_boot_container, 0, + &s->bmc_boot_rom, 1); + + fby35_bmc_write_boot_rom(mtd0, &s->bmc_boot_rom, + FBY35_BMC_FIRMWARE_ADDR, + rom_size, &error_abort); + } + } +} + +static void fby35_bic_init(Fby35State *s) +{ + AspeedSoCState *soc; + + s->bic_sysclk = clock_new(OBJECT(s), "SYSCLK"); + clock_set_hz(s->bic_sysclk, 200000000ULL); + + object_initialize_child(OBJECT(s), "bic", &s->bic, "ast1030-a1"); + soc = ASPEED_SOC(&s->bic); + + memory_region_init(&s->bic_memory, OBJECT(&s->bic), "bic-memory", + UINT64_MAX); + + qdev_connect_clock_in(DEVICE(&s->bic), "sysclk", s->bic_sysclk); + object_property_set_link(OBJECT(&s->bic), "memory", OBJECT(&s->bic_memory), + &error_abort); + aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART5, serial_hd(1)); + qdev_realize(DEVICE(&s->bic), NULL, &error_abort); + + aspeed_board_init_flashes(&soc->fmc, "sst25vf032b", 2, 2); + aspeed_board_init_flashes(&soc->spi[0], "sst25vf032b", 2, 4); + aspeed_board_init_flashes(&soc->spi[1], "sst25vf032b", 2, 6); +} + +static void fby35_init(MachineState *machine) +{ + Fby35State *s = FBY35(machine); + + fby35_bmc_init(s); + fby35_bic_init(s); +} + + +static bool fby35_get_mmio_exec(Object *obj, Error **errp) +{ + return FBY35(obj)->mmio_exec; +} + +static void fby35_set_mmio_exec(Object *obj, bool value, Error **errp) +{ + FBY35(obj)->mmio_exec = value; +} + +static void fby35_instance_init(Object *obj) +{ + FBY35(obj)->mmio_exec = false; +} + +static void fby35_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Meta Platforms fby35"; + mc->init = fby35_init; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->min_cpus = mc->max_cpus = mc->default_cpus = 3; + + object_class_property_add_bool(oc, "execute-in-place", + fby35_get_mmio_exec, + fby35_set_mmio_exec); + object_class_property_set_description(oc, "execute-in-place", + "boot directly from CE0 flash device"); +} + +static const TypeInfo fby35_types[] = { + { + .name = MACHINE_TYPE_NAME("fby35"), + .parent = TYPE_MACHINE, + .class_init = fby35_class_init, + .instance_size = sizeof(Fby35State), + .instance_init = fby35_instance_init, + }, +}; + +DEFINE_TYPES(fby35_types); diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 24c4374590..5ed87edfe4 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -28,6 +28,7 @@ #include "sysemu/sysemu.h" #include "hw/qdev-properties.h" #include "chardev/char.h" +#include "target/arm/cpu-qom.h" #define IMX25_ESDHC_CAPABILITIES 0x07e20000 @@ -81,7 +82,6 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) { FslIMX25State *s = FSL_IMX25(dev); uint8_t i; - Error *err = NULL; if (!qdev_realize(DEVICE(&s->cpu), NULL, errp)) { return; @@ -169,8 +169,9 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) epit_table[i].irq)); } - object_property_set_uint(OBJECT(&s->fec), "phy-num", s->phy_num, &err); - qdev_set_nic_properties(DEVICE(&s->fec), &nd_table[0]); + object_property_set_uint(OBJECT(&s->fec), "phy-num", s->phy_num, + &error_abort); + qemu_configure_nic_device(DEVICE(&s->fec), true, NULL); if (!sysbus_realize(SYS_BUS_DEVICE(&s->fec), errp)) { return; @@ -280,28 +281,22 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) FSL_IMX25_WDT_IRQ)); /* initialize 2 x 16 KB ROM */ - memory_region_init_rom(&s->rom[0], OBJECT(dev), "imx25.rom0", - FSL_IMX25_ROM0_SIZE, &err); - if (err) { - error_propagate(errp, err); + if (!memory_region_init_rom(&s->rom[0], OBJECT(dev), "imx25.rom0", + FSL_IMX25_ROM0_SIZE, errp)) { return; } memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM0_ADDR, &s->rom[0]); - memory_region_init_rom(&s->rom[1], OBJECT(dev), "imx25.rom1", - FSL_IMX25_ROM1_SIZE, &err); - if (err) { - error_propagate(errp, err); + if (!memory_region_init_rom(&s->rom[1], OBJECT(dev), "imx25.rom1", + FSL_IMX25_ROM1_SIZE, errp)) { return; } memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM1_ADDR, &s->rom[1]); /* initialize internal RAM (128 KB) */ - memory_region_init_ram(&s->iram, NULL, "imx25.iram", FSL_IMX25_IRAM_SIZE, - &err); - if (err) { - error_propagate(errp, err); + if (!memory_region_init_ram(&s->iram, NULL, "imx25.iram", + FSL_IMX25_IRAM_SIZE, errp)) { return; } memory_region_add_subregion(get_system_memory(), FSL_IMX25_IRAM_ADDR, diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index def27bb913..4b8d9b8e4f 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -26,6 +26,7 @@ #include "exec/address-spaces.h" #include "hw/qdev-properties.h" #include "chardev/char.h" +#include "target/arm/cpu-qom.h" static void fsl_imx31_init(Object *obj) { @@ -63,7 +64,6 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) { FslIMX31State *s = FSL_IMX31(dev); uint16_t i; - Error *err = NULL; if (!qdev_realize(DEVICE(&s->cpu), NULL, errp)) { return; @@ -188,30 +188,24 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, FSL_IMX31_WDT_ADDR); /* On a real system, the first 16k is a `secure boot 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); + if (!memory_region_init_rom(&s->secure_rom, OBJECT(dev), "imx31.secure_rom", + FSL_IMX31_SECURE_ROM_SIZE, errp)) { return; } memory_region_add_subregion(get_system_memory(), FSL_IMX31_SECURE_ROM_ADDR, &s->secure_rom); /* There is also a 16k ROM */ - memory_region_init_rom(&s->rom, OBJECT(dev), "imx31.rom", - FSL_IMX31_ROM_SIZE, &err); - if (err) { - error_propagate(errp, err); + if (!memory_region_init_rom(&s->rom, OBJECT(dev), "imx31.rom", + FSL_IMX31_ROM_SIZE, errp)) { return; } memory_region_add_subregion(get_system_memory(), FSL_IMX31_ROM_ADDR, &s->rom); /* initialize internal RAM (16 KB) */ - memory_region_init_ram(&s->iram, NULL, "imx31.iram", FSL_IMX31_IRAM_SIZE, - &err); - if (err) { - error_propagate(errp, err); + if (!memory_region_init_ram(&s->iram, NULL, "imx31.iram", + FSL_IMX31_IRAM_SIZE, errp)) { return; } memory_region_add_subregion(get_system_memory(), FSL_IMX31_IRAM_ADDR, diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 00dafe3f62..85748cb233 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/misc/unimp.h" #include "hw/usb/imx-usb-phy.h" #include "hw/boards.h" #include "hw/qdev-properties.h" @@ -29,6 +30,7 @@ #include "chardev/char.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "target/arm/cpu-qom.h" #define IMX6_ESDHC_CAPABILITIES 0x057834b4 @@ -53,6 +55,8 @@ static void fsl_imx6_init(Object *obj) object_initialize_child(obj, "src", &s->src, TYPE_IMX6_SRC); + object_initialize_child(obj, "snvs", &s->snvs, TYPE_IMX7_SNVS); + for (i = 0; i < FSL_IMX6_NUM_UARTS; i++) { snprintf(name, NAME_SIZE, "uart%d", i + 1); object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); @@ -100,6 +104,8 @@ static void fsl_imx6_init(Object *obj) object_initialize_child(obj, "eth", &s->eth, TYPE_IMX_ENET); + + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); } static void fsl_imx6_realize(DeviceState *dev, Error **errp) @@ -107,7 +113,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) MachineState *ms = MACHINE(qdev_get_machine()); FslIMX6State *s = FSL_IMX6(dev); uint16_t i; - Error *err = NULL; + qemu_irq irq; unsigned int smp_cpus = ms->smp.cpus; if (smp_cpus > FSL_IMX6_NUM_CPUS) { @@ -153,6 +159,9 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ)); } + /* L2 cache controller */ + sysbus_create_simple("l2x0", FSL_IMX6_PL310_ADDR, NULL); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ccm), errp)) { return; } @@ -377,8 +386,9 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) spi_table[i].irq)); } - object_property_set_uint(OBJECT(&s->eth), "phy-num", s->phy_num, &err); - qdev_set_nic_properties(DEVICE(&s->eth), &nd_table[0]); + object_property_set_uint(OBJECT(&s->eth), "phy-num", s->phy_num, + &error_abort); + qemu_configure_nic_device(DEVICE(&s->eth), true, NULL); if (!sysbus_realize(SYS_BUS_DEVICE(&s->eth), errp)) { return; } @@ -390,6 +400,12 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(&s->a9mpcore), FSL_IMX6_ENET_MAC_1588_IRQ)); + /* + * SNVS + */ + sysbus_realize(SYS_BUS_DEVICE(&s->snvs), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, FSL_IMX6_SNVSHP_ADDR); + /* * Watchdog */ @@ -413,31 +429,46 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) FSL_IMX6_WDOGn_IRQ[i])); } + /* + * PCIe + */ + sysbus_realize(SYS_BUS_DEVICE(&s->pcie), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, FSL_IMX6_PCIe_REG_ADDR); + + irq = qdev_get_gpio_in(DEVICE(&s->a9mpcore), FSL_IMX6_PCIE1_IRQ); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, irq); + irq = qdev_get_gpio_in(DEVICE(&s->a9mpcore), FSL_IMX6_PCIE2_IRQ); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, irq); + irq = qdev_get_gpio_in(DEVICE(&s->a9mpcore), FSL_IMX6_PCIE3_IRQ); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, irq); + irq = qdev_get_gpio_in(DEVICE(&s->a9mpcore), FSL_IMX6_PCIE4_IRQ); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, irq); + + /* + * PCIe PHY + */ + create_unimplemented_device("pcie-phy", FSL_IMX6_PCIe_ADDR, + FSL_IMX6_PCIe_SIZE); + /* ROM memory */ - memory_region_init_rom(&s->rom, OBJECT(dev), "imx6.rom", - FSL_IMX6_ROM_SIZE, &err); - if (err) { - error_propagate(errp, err); + if (!memory_region_init_rom(&s->rom, OBJECT(dev), "imx6.rom", + FSL_IMX6_ROM_SIZE, errp)) { return; } memory_region_add_subregion(get_system_memory(), FSL_IMX6_ROM_ADDR, &s->rom); /* CAAM memory */ - memory_region_init_rom(&s->caam, OBJECT(dev), "imx6.caam", - FSL_IMX6_CAAM_MEM_SIZE, &err); - if (err) { - error_propagate(errp, err); + if (!memory_region_init_rom(&s->caam, OBJECT(dev), "imx6.caam", + FSL_IMX6_CAAM_MEM_SIZE, errp)) { return; } memory_region_add_subregion(get_system_memory(), FSL_IMX6_CAAM_MEM_ADDR, &s->caam); /* OCRAM memory */ - memory_region_init_ram(&s->ocram, NULL, "imx6.ocram", FSL_IMX6_OCRAM_SIZE, - &err); - if (err) { - error_propagate(errp, err); + if (!memory_region_init_ram(&s->ocram, NULL, "imx6.ocram", + FSL_IMX6_OCRAM_SIZE, errp)) { return; } memory_region_add_subregion(get_system_memory(), FSL_IMX6_OCRAM_ADDR, diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index f189712329..19f443570b 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -25,6 +25,7 @@ #include "sysemu/sysemu.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "target/arm/cpu-qom.h" #define NAME_SIZE 20 @@ -64,12 +65,7 @@ static void fsl_imx6ul_init(Object *obj) object_initialize_child(obj, "snvs", &s->snvs, TYPE_IMX7_SNVS); /* - * GPR - */ - object_initialize_child(obj, "gpr", &s->gpr, TYPE_IMX7_GPR); - - /* - * GPIOs 1 to 5 + * GPIOs */ for (i = 0; i < FSL_IMX6UL_NUM_GPIOS; i++) { snprintf(name, NAME_SIZE, "gpio%d", i); @@ -77,15 +73,15 @@ static void fsl_imx6ul_init(Object *obj) } /* - * GPT 1, 2 + * GPTs */ for (i = 0; i < FSL_IMX6UL_NUM_GPTS; i++) { snprintf(name, NAME_SIZE, "gpt%d", i); - object_initialize_child(obj, name, &s->gpt[i], TYPE_IMX7_GPT); + object_initialize_child(obj, name, &s->gpt[i], TYPE_IMX6UL_GPT); } /* - * EPIT 1, 2 + * EPITs */ for (i = 0; i < FSL_IMX6UL_NUM_EPITS; i++) { snprintf(name, NAME_SIZE, "epit%d", i + 1); @@ -93,7 +89,7 @@ static void fsl_imx6ul_init(Object *obj) } /* - * eCSPI + * eCSPIs */ for (i = 0; i < FSL_IMX6UL_NUM_ECSPIS; i++) { snprintf(name, NAME_SIZE, "spi%d", i + 1); @@ -101,7 +97,7 @@ static void fsl_imx6ul_init(Object *obj) } /* - * I2C + * I2Cs */ for (i = 0; i < FSL_IMX6UL_NUM_I2CS; i++) { snprintf(name, NAME_SIZE, "i2c%d", i + 1); @@ -109,7 +105,7 @@ static void fsl_imx6ul_init(Object *obj) } /* - * UART + * UARTs */ for (i = 0; i < FSL_IMX6UL_NUM_UARTS; i++) { snprintf(name, NAME_SIZE, "uart%d", i); @@ -117,25 +113,31 @@ static void fsl_imx6ul_init(Object *obj) } /* - * Ethernet + * Ethernets */ for (i = 0; i < FSL_IMX6UL_NUM_ETHS; i++) { snprintf(name, NAME_SIZE, "eth%d", i); object_initialize_child(obj, name, &s->eth[i], TYPE_IMX_ENET); } - /* USB */ + /* + * USB PHYs + */ for (i = 0; i < FSL_IMX6UL_NUM_USB_PHYS; i++) { snprintf(name, NAME_SIZE, "usbphy%d", i); object_initialize_child(obj, name, &s->usbphy[i], TYPE_IMX_USBPHY); } + + /* + * USBs + */ for (i = 0; i < FSL_IMX6UL_NUM_USBS; i++) { snprintf(name, NAME_SIZE, "usb%d", i); object_initialize_child(obj, name, &s->usb[i], TYPE_CHIPIDEA); } /* - * SDHCI + * SDHCIs */ for (i = 0; i < FSL_IMX6UL_NUM_USDHCS; i++) { snprintf(name, NAME_SIZE, "usdhc%d", i); @@ -143,7 +145,7 @@ static void fsl_imx6ul_init(Object *obj) } /* - * Watchdog + * Watchdogs */ for (i = 0; i < FSL_IMX6UL_NUM_WDTS; i++) { snprintf(name, NAME_SIZE, "wdt%d", i); @@ -189,10 +191,40 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) * A7MPCORE DAP */ create_unimplemented_device("a7mpcore-dap", FSL_IMX6UL_A7MPCORE_DAP_ADDR, - 0x100000); + FSL_IMX6UL_A7MPCORE_DAP_SIZE); /* - * GPT 1, 2 + * MMDC + */ + create_unimplemented_device("a7mpcore-mmdc", FSL_IMX6UL_MMDC_CFG_ADDR, + FSL_IMX6UL_MMDC_CFG_SIZE); + + /* + * OCOTP + */ + create_unimplemented_device("a7mpcore-ocotp", FSL_IMX6UL_OCOTP_CTRL_ADDR, + FSL_IMX6UL_OCOTP_CTRL_SIZE); + + /* + * QSPI + */ + create_unimplemented_device("a7mpcore-qspi", FSL_IMX6UL_QSPI_ADDR, + FSL_IMX6UL_QSPI_SIZE); + + /* + * CAAM + */ + create_unimplemented_device("a7mpcore-qspi", FSL_IMX6UL_CAAM_ADDR, + FSL_IMX6UL_CAAM_SIZE); + + /* + * USBMISC + */ + create_unimplemented_device("a7mpcore-usbmisc", FSL_IMX6UL_USBO2_USBMISC_ADDR, + FSL_IMX6UL_USBO2_USBMISC_SIZE); + + /* + * GPTs */ for (i = 0; i < FSL_IMX6UL_NUM_GPTS; i++) { static const hwaddr FSL_IMX6UL_GPTn_ADDR[FSL_IMX6UL_NUM_GPTS] = { @@ -217,7 +249,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) } /* - * EPIT 1, 2 + * EPITs */ for (i = 0; i < FSL_IMX6UL_NUM_EPITS; i++) { static const hwaddr FSL_IMX6UL_EPITn_ADDR[FSL_IMX6UL_NUM_EPITS] = { @@ -242,7 +274,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) } /* - * GPIO + * GPIOs */ for (i = 0; i < FSL_IMX6UL_NUM_GPIOS; i++) { static const hwaddr FSL_IMX6UL_GPIOn_ADDR[FSL_IMX6UL_NUM_GPIOS] = { @@ -284,17 +316,12 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) } /* - * IOMUXC and IOMUXC_GPR + * IOMUXC */ - for (i = 0; i < 1; i++) { - static const hwaddr FSL_IMX6UL_IOMUXCn_ADDR[FSL_IMX6UL_NUM_IOMUXCS] = { - FSL_IMX6UL_IOMUXC_ADDR, - FSL_IMX6UL_IOMUXC_GPR_ADDR, - }; - - snprintf(name, NAME_SIZE, "iomuxc%d", i); - create_unimplemented_device(name, FSL_IMX6UL_IOMUXCn_ADDR[i], 0x4000); - } + create_unimplemented_device("iomuxc", FSL_IMX6UL_IOMUXC_ADDR, + FSL_IMX6UL_IOMUXC_SIZE); + create_unimplemented_device("iomuxc_gpr", FSL_IMX6UL_IOMUXC_GPR_ADDR, + FSL_IMX6UL_IOMUXC_GPR_SIZE); /* * CCM @@ -314,7 +341,9 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(&s->gpcv2), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpcv2), 0, FSL_IMX6UL_GPC_ADDR); - /* Initialize all ECSPI */ + /* + * ECSPIs + */ for (i = 0; i < FSL_IMX6UL_NUM_ECSPIS; i++) { static const hwaddr FSL_IMX6UL_SPIn_ADDR[FSL_IMX6UL_NUM_ECSPIS] = { FSL_IMX6UL_ECSPI1_ADDR, @@ -342,7 +371,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) } /* - * I2C + * I2Cs */ for (i = 0; i < FSL_IMX6UL_NUM_I2CS; i++) { static const hwaddr FSL_IMX6UL_I2Cn_ADDR[FSL_IMX6UL_NUM_I2CS] = { @@ -368,7 +397,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) } /* - * UART + * UARTs */ for (i = 0; i < FSL_IMX6UL_NUM_UARTS; i++) { static const hwaddr FSL_IMX6UL_UARTn_ADDR[FSL_IMX6UL_NUM_UARTS] = { @@ -406,8 +435,24 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) } /* - * Ethernet + * Ethernets + * + * We must use two loops since phy_connected affects the other interface + * and we have to set all properties before calling sysbus_realize(). */ + for (i = 0; i < FSL_IMX6UL_NUM_ETHS; i++) { + object_property_set_bool(OBJECT(&s->eth[i]), "phy-connected", + s->phy_connected[i], &error_abort); + /* + * If the MDIO bus on this controller is not connected, assume the + * other controller provides support for it. + */ + if (!s->phy_connected[i]) { + object_property_set_link(OBJECT(&s->eth[1 - i]), "phy-consumer", + OBJECT(&s->eth[i]), &error_abort); + } + } + for (i = 0; i < FSL_IMX6UL_NUM_ETHS; i++) { static const hwaddr FSL_IMX6UL_ENETn_ADDR[FSL_IMX6UL_NUM_ETHS] = { FSL_IMX6UL_ENET1_ADDR, @@ -428,7 +473,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) s->phy_num[i], &error_abort); object_property_set_uint(OBJECT(&s->eth[i]), "tx-ring-num", FSL_IMX6UL_ETH_NUM_TX_RINGS, &error_abort); - qdev_set_nic_properties(DEVICE(&s->eth[i]), &nd_table[i]); + qemu_configure_nic_device(DEVICE(&s->eth[i]), true, NULL); sysbus_realize(SYS_BUS_DEVICE(&s->eth[i]), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->eth[i]), 0, @@ -443,28 +488,45 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_ENETn_TIMER_IRQ[i])); } - /* USB */ + /* + * USB PHYs + */ for (i = 0; i < FSL_IMX6UL_NUM_USB_PHYS; i++) { + static const hwaddr + FSL_IMX6UL_USB_PHYn_ADDR[FSL_IMX6UL_NUM_USB_PHYS] = { + FSL_IMX6UL_USBPHY1_ADDR, + FSL_IMX6UL_USBPHY2_ADDR, + }; + sysbus_realize(SYS_BUS_DEVICE(&s->usbphy[i]), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->usbphy[i]), 0, - FSL_IMX6UL_USBPHY1_ADDR + i * 0x1000); + FSL_IMX6UL_USB_PHYn_ADDR[i]); } + /* + * USBs + */ for (i = 0; i < FSL_IMX6UL_NUM_USBS; i++) { + static const hwaddr FSL_IMX6UL_USB02_USBn_ADDR[FSL_IMX6UL_NUM_USBS] = { + FSL_IMX6UL_USBO2_USB1_ADDR, + FSL_IMX6UL_USBO2_USB2_ADDR, + }; + static const int FSL_IMX6UL_USBn_IRQ[] = { FSL_IMX6UL_USB1_IRQ, FSL_IMX6UL_USB2_IRQ, }; + sysbus_realize(SYS_BUS_DEVICE(&s->usb[i]), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, - FSL_IMX6UL_USBO2_USB_ADDR + i * 0x200); + FSL_IMX6UL_USB02_USBn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX6UL_USBn_IRQ[i])); } /* - * USDHC + * USDHCs */ for (i = 0; i < FSL_IMX6UL_NUM_USDHCS; i++) { static const hwaddr FSL_IMX6UL_USDHCn_ADDR[FSL_IMX6UL_NUM_USDHCS] = { @@ -496,7 +558,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, FSL_IMX6UL_SNVS_HP_ADDR); /* - * Watchdog + * Watchdogs */ for (i = 0; i < FSL_IMX6UL_NUM_WDTS; i++) { static const hwaddr FSL_IMX6UL_WDOGn_ADDR[FSL_IMX6UL_NUM_WDTS] = { @@ -504,6 +566,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_WDOG2_ADDR, FSL_IMX6UL_WDOG3_ADDR, }; + static const int FSL_IMX6UL_WDOGn_IRQ[FSL_IMX6UL_NUM_WDTS] = { FSL_IMX6UL_WDOG1_IRQ, FSL_IMX6UL_WDOG2_IRQ, @@ -521,42 +584,66 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_WDOGn_IRQ[i])); } - /* - * GPR - */ - sysbus_realize(SYS_BUS_DEVICE(&s->gpr), &error_abort); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpr), 0, FSL_IMX6UL_IOMUXC_GPR_ADDR); - /* * SDMA */ - create_unimplemented_device("sdma", FSL_IMX6UL_SDMA_ADDR, 0x4000); + create_unimplemented_device("sdma", FSL_IMX6UL_SDMA_ADDR, + FSL_IMX6UL_SDMA_SIZE); /* - * SAI (Audio SSI (Synchronous Serial Interface)) + * SAIs (Audio SSI (Synchronous Serial Interface)) */ - create_unimplemented_device("sai1", FSL_IMX6UL_SAI1_ADDR, 0x4000); - create_unimplemented_device("sai2", FSL_IMX6UL_SAI2_ADDR, 0x4000); - create_unimplemented_device("sai3", FSL_IMX6UL_SAI3_ADDR, 0x4000); + for (i = 0; i < FSL_IMX6UL_NUM_SAIS; i++) { + static const hwaddr FSL_IMX6UL_SAIn_ADDR[FSL_IMX6UL_NUM_SAIS] = { + FSL_IMX6UL_SAI1_ADDR, + FSL_IMX6UL_SAI2_ADDR, + FSL_IMX6UL_SAI3_ADDR, + }; + + snprintf(name, NAME_SIZE, "sai%d", i); + create_unimplemented_device(name, FSL_IMX6UL_SAIn_ADDR[i], + FSL_IMX6UL_SAIn_SIZE); + } /* - * PWM + * PWMs */ - 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); + for (i = 0; i < FSL_IMX6UL_NUM_PWMS; i++) { + static const hwaddr FSL_IMX6UL_PWMn_ADDR[FSL_IMX6UL_NUM_PWMS] = { + FSL_IMX6UL_PWM1_ADDR, + FSL_IMX6UL_PWM2_ADDR, + FSL_IMX6UL_PWM3_ADDR, + FSL_IMX6UL_PWM4_ADDR, + FSL_IMX6UL_PWM5_ADDR, + FSL_IMX6UL_PWM6_ADDR, + FSL_IMX6UL_PWM7_ADDR, + FSL_IMX6UL_PWM8_ADDR, + }; + + snprintf(name, NAME_SIZE, "pwm%d", i); + create_unimplemented_device(name, FSL_IMX6UL_PWMn_ADDR[i], + FSL_IMX6UL_PWMn_SIZE); + } /* * Audio ASRC (asynchronous sample rate converter) */ - create_unimplemented_device("asrc", FSL_IMX6UL_ASRC_ADDR, 0x4000); + create_unimplemented_device("asrc", FSL_IMX6UL_ASRC_ADDR, + FSL_IMX6UL_ASRC_SIZE); /* - * CAN + * CANs */ - create_unimplemented_device("can1", FSL_IMX6UL_CAN1_ADDR, 0x4000); - create_unimplemented_device("can2", FSL_IMX6UL_CAN2_ADDR, 0x4000); + for (i = 0; i < FSL_IMX6UL_NUM_CANS; i++) { + static const hwaddr FSL_IMX6UL_CANn_ADDR[FSL_IMX6UL_NUM_CANS] = { + FSL_IMX6UL_CAN1_ADDR, + FSL_IMX6UL_CAN2_ADDR, + }; + + snprintf(name, NAME_SIZE, "can%d", i); + create_unimplemented_device(name, FSL_IMX6UL_CANn_ADDR[i], + FSL_IMX6UL_CANn_SIZE); + } /* * APHB_DMA @@ -574,13 +661,27 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) }; snprintf(name, NAME_SIZE, "adc%d", i); - create_unimplemented_device(name, FSL_IMX6UL_ADCn_ADDR[i], 0x4000); + create_unimplemented_device(name, FSL_IMX6UL_ADCn_ADDR[i], + FSL_IMX6UL_ADCn_SIZE); } /* * LCD */ - create_unimplemented_device("lcdif", FSL_IMX6UL_LCDIF_ADDR, 0x4000); + create_unimplemented_device("lcdif", FSL_IMX6UL_LCDIF_ADDR, + FSL_IMX6UL_LCDIF_SIZE); + + /* + * CSU + */ + create_unimplemented_device("csu", FSL_IMX6UL_CSU_ADDR, + FSL_IMX6UL_CSU_SIZE); + + /* + * TZASC + */ + create_unimplemented_device("tzasc", FSL_IMX6UL_TZASC_ADDR, + FSL_IMX6UL_TZASC_SIZE); /* * ROM memory @@ -620,6 +721,10 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) static Property fsl_imx6ul_properties[] = { DEFINE_PROP_UINT32("fec1-phy-num", FslIMX6ULState, phy_num[0], 0), DEFINE_PROP_UINT32("fec2-phy-num", FslIMX6ULState, phy_num[1], 1), + DEFINE_PROP_BOOL("fec1-phy-connected", FslIMX6ULState, phy_connected[0], + true), + DEFINE_PROP_BOOL("fec2-phy-connected", FslIMX6ULState, phy_connected[1], + true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index cc6fdb9373..9f2ef34555 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -26,6 +26,7 @@ #include "sysemu/sysemu.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "target/arm/cpu-qom.h" #define NAME_SIZE 20 @@ -36,6 +37,9 @@ static void fsl_imx7_init(Object *obj) char name[NAME_SIZE]; int i; + /* + * CPUs + */ for (i = 0; i < MIN(ms->smp.cpus, FSL_IMX7_NUM_CPUS); i++) { snprintf(name, NAME_SIZE, "cpu%d", i); object_initialize_child(obj, name, &s->cpu[i], @@ -49,7 +53,7 @@ static void fsl_imx7_init(Object *obj) TYPE_A15MPCORE_PRIV); /* - * GPIOs 1 to 7 + * GPIOs */ for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) { snprintf(name, NAME_SIZE, "gpio%d", i); @@ -57,7 +61,7 @@ static void fsl_imx7_init(Object *obj) } /* - * GPT1, 2, 3, 4 + * GPTs */ for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) { snprintf(name, NAME_SIZE, "gpt%d", i); @@ -79,19 +83,29 @@ static void fsl_imx7_init(Object *obj) */ object_initialize_child(obj, "gpcv2", &s->gpcv2, TYPE_IMX_GPCV2); + /* + * SRC + */ + object_initialize_child(obj, "src", &s->src, TYPE_IMX7_SRC); + + /* + * ECSPIs + */ for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) { snprintf(name, NAME_SIZE, "spi%d", i + 1); object_initialize_child(obj, name, &s->spi[i], TYPE_IMX_SPI); } - + /* + * I2Cs + */ for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) { snprintf(name, NAME_SIZE, "i2c%d", i + 1); object_initialize_child(obj, name, &s->i2c[i], TYPE_IMX_I2C); } /* - * UART + * UARTs */ for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) { snprintf(name, NAME_SIZE, "uart%d", i); @@ -99,7 +113,7 @@ static void fsl_imx7_init(Object *obj) } /* - * Ethernet + * Ethernets */ for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) { snprintf(name, NAME_SIZE, "eth%d", i); @@ -107,7 +121,7 @@ static void fsl_imx7_init(Object *obj) } /* - * SDHCI + * SDHCIs */ for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) { snprintf(name, NAME_SIZE, "usdhc%d", i); @@ -120,7 +134,7 @@ static void fsl_imx7_init(Object *obj) object_initialize_child(obj, "snvs", &s->snvs, TYPE_IMX7_SNVS); /* - * Watchdog + * Watchdogs */ for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) { snprintf(name, NAME_SIZE, "wdt%d", i); @@ -132,8 +146,14 @@ static void fsl_imx7_init(Object *obj) */ object_initialize_child(obj, "gpr", &s->gpr, TYPE_IMX7_GPR); + /* + * PCIE + */ object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); + /* + * USBs + */ for (i = 0; i < FSL_IMX7_NUM_USBS; i++) { snprintf(name, NAME_SIZE, "usb%d", i); object_initialize_child(obj, name, &s->usb[i], TYPE_CHIPIDEA); @@ -156,6 +176,9 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) return; } + /* + * CPUs + */ for (i = 0; i < smp_cpus; i++) { o = OBJECT(&s->cpu[i]); @@ -206,10 +229,10 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) * A7MPCORE DAP */ create_unimplemented_device("a7mpcore-dap", FSL_IMX7_A7MPCORE_DAP_ADDR, - 0x100000); + FSL_IMX7_A7MPCORE_DAP_SIZE); /* - * GPT1, 2, 3, 4 + * GPTs */ for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) { static const hwaddr FSL_IMX7_GPTn_ADDR[FSL_IMX7_NUM_GPTS] = { @@ -219,11 +242,24 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) FSL_IMX7_GPT4_ADDR, }; + static const int FSL_IMX7_GPTn_IRQ[FSL_IMX7_NUM_GPTS] = { + FSL_IMX7_GPT1_IRQ, + FSL_IMX7_GPT2_IRQ, + FSL_IMX7_GPT3_IRQ, + FSL_IMX7_GPT4_IRQ, + }; + s->gpt[i].ccm = IMX_CCM(&s->ccm); sysbus_realize(SYS_BUS_DEVICE(&s->gpt[i]), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, FSL_IMX7_GPTn_ADDR[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX7_GPTn_IRQ[i])); } + /* + * GPIOs + */ for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) { static const hwaddr FSL_IMX7_GPIOn_ADDR[FSL_IMX7_NUM_GPIOS] = { FSL_IMX7_GPIO1_ADDR, @@ -235,23 +271,46 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) FSL_IMX7_GPIO7_ADDR, }; + static const int FSL_IMX7_GPIOn_LOW_IRQ[FSL_IMX7_NUM_GPIOS] = { + FSL_IMX7_GPIO1_LOW_IRQ, + FSL_IMX7_GPIO2_LOW_IRQ, + FSL_IMX7_GPIO3_LOW_IRQ, + FSL_IMX7_GPIO4_LOW_IRQ, + FSL_IMX7_GPIO5_LOW_IRQ, + FSL_IMX7_GPIO6_LOW_IRQ, + FSL_IMX7_GPIO7_LOW_IRQ, + }; + + static const int FSL_IMX7_GPIOn_HIGH_IRQ[FSL_IMX7_NUM_GPIOS] = { + FSL_IMX7_GPIO1_HIGH_IRQ, + FSL_IMX7_GPIO2_HIGH_IRQ, + FSL_IMX7_GPIO3_HIGH_IRQ, + FSL_IMX7_GPIO4_HIGH_IRQ, + FSL_IMX7_GPIO5_HIGH_IRQ, + FSL_IMX7_GPIO6_HIGH_IRQ, + FSL_IMX7_GPIO7_HIGH_IRQ, + }; + sysbus_realize(SYS_BUS_DEVICE(&s->gpio[i]), &error_abort); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, FSL_IMX7_GPIOn_ADDR[i]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, + FSL_IMX7_GPIOn_ADDR[i]); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX7_GPIOn_LOW_IRQ[i])); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX7_GPIOn_HIGH_IRQ[i])); } /* * IOMUXC and IOMUXC_LPSR */ - for (i = 0; i < FSL_IMX7_NUM_IOMUXCS; i++) { - static const hwaddr FSL_IMX7_IOMUXCn_ADDR[FSL_IMX7_NUM_IOMUXCS] = { - FSL_IMX7_IOMUXC_ADDR, - FSL_IMX7_IOMUXC_LPSR_ADDR, - }; - - snprintf(name, NAME_SIZE, "iomuxc%d", i); - create_unimplemented_device(name, FSL_IMX7_IOMUXCn_ADDR[i], - FSL_IMX7_IOMUXCn_SIZE); - } + create_unimplemented_device("iomuxc", FSL_IMX7_IOMUXC_ADDR, + FSL_IMX7_IOMUXC_SIZE); + create_unimplemented_device("iomuxc_lspr", FSL_IMX7_IOMUXC_LPSR_ADDR, + FSL_IMX7_IOMUXC_LPSR_SIZE); /* * CCM @@ -271,7 +330,9 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(&s->gpcv2), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpcv2), 0, FSL_IMX7_GPC_ADDR); - /* Initialize all ECSPI */ + /* + * ECSPIs + */ for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) { static const hwaddr FSL_IMX7_SPIn_ADDR[FSL_IMX7_NUM_ECSPIS] = { FSL_IMX7_ECSPI1_ADDR, @@ -296,6 +357,9 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) FSL_IMX7_SPIn_IRQ[i])); } + /* + * I2Cs + */ for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) { static const hwaddr FSL_IMX7_I2Cn_ADDR[FSL_IMX7_NUM_I2CS] = { FSL_IMX7_I2C1_ADDR, @@ -320,7 +384,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) } /* - * UART + * UARTs */ for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) { static const hwaddr FSL_IMX7_UARTn_ADDR[FSL_IMX7_NUM_UARTS] = { @@ -355,8 +419,24 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) } /* - * Ethernet + * Ethernets + * + * We must use two loops since phy_connected affects the other interface + * and we have to set all properties before calling sysbus_realize(). */ + for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) { + object_property_set_bool(OBJECT(&s->eth[i]), "phy-connected", + s->phy_connected[i], &error_abort); + /* + * If the MDIO bus on this controller is not connected, assume the + * other controller provides support for it. + */ + if (!s->phy_connected[i]) { + object_property_set_link(OBJECT(&s->eth[1 - i]), "phy-consumer", + OBJECT(&s->eth[i]), &error_abort); + } + } + for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) { static const hwaddr FSL_IMX7_ENETn_ADDR[FSL_IMX7_NUM_ETHS] = { FSL_IMX7_ENET1_ADDR, @@ -367,7 +447,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) s->phy_num[i], &error_abort); object_property_set_uint(OBJECT(&s->eth[i]), "tx-ring-num", FSL_IMX7_ETH_NUM_TX_RINGS, &error_abort); - qdev_set_nic_properties(DEVICE(&s->eth[i]), &nd_table[i]); + qemu_configure_nic_device(DEVICE(&s->eth[i]), true, NULL); sysbus_realize(SYS_BUS_DEVICE(&s->eth[i]), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->eth[i]), 0, FSL_IMX7_ENETn_ADDR[i]); @@ -379,7 +459,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) } /* - * USDHC + * USDHCs */ for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) { static const hwaddr FSL_IMX7_USDHCn_ADDR[FSL_IMX7_NUM_USDHCS] = { @@ -409,15 +489,16 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) * SNVS */ sysbus_realize(SYS_BUS_DEVICE(&s->snvs), &error_abort); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, FSL_IMX7_SNVS_ADDR); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, FSL_IMX7_SNVS_HP_ADDR); /* * SRC */ - create_unimplemented_device("src", FSL_IMX7_SRC_ADDR, FSL_IMX7_SRC_SIZE); + sysbus_realize(SYS_BUS_DEVICE(&s->src), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->src), 0, FSL_IMX7_SRC_ADDR); /* - * Watchdog + * Watchdogs */ for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) { static const hwaddr FSL_IMX7_WDOGn_ADDR[FSL_IMX7_NUM_WDTS] = { @@ -454,25 +535,49 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) create_unimplemented_device("caam", FSL_IMX7_CAAM_ADDR, FSL_IMX7_CAAM_SIZE); /* - * PWM + * PWMs */ - create_unimplemented_device("pwm1", FSL_IMX7_PWM1_ADDR, FSL_IMX7_PWMn_SIZE); - create_unimplemented_device("pwm2", FSL_IMX7_PWM2_ADDR, FSL_IMX7_PWMn_SIZE); - create_unimplemented_device("pwm3", FSL_IMX7_PWM3_ADDR, FSL_IMX7_PWMn_SIZE); - create_unimplemented_device("pwm4", FSL_IMX7_PWM4_ADDR, FSL_IMX7_PWMn_SIZE); + for (i = 0; i < FSL_IMX7_NUM_PWMS; i++) { + static const hwaddr FSL_IMX7_PWMn_ADDR[FSL_IMX7_NUM_PWMS] = { + FSL_IMX7_PWM1_ADDR, + FSL_IMX7_PWM2_ADDR, + FSL_IMX7_PWM3_ADDR, + FSL_IMX7_PWM4_ADDR, + }; + + snprintf(name, NAME_SIZE, "pwm%d", i); + create_unimplemented_device(name, FSL_IMX7_PWMn_ADDR[i], + FSL_IMX7_PWMn_SIZE); + } /* - * CAN + * CANs */ - create_unimplemented_device("can1", FSL_IMX7_CAN1_ADDR, FSL_IMX7_CANn_SIZE); - create_unimplemented_device("can2", FSL_IMX7_CAN2_ADDR, FSL_IMX7_CANn_SIZE); + for (i = 0; i < FSL_IMX7_NUM_CANS; i++) { + static const hwaddr FSL_IMX7_CANn_ADDR[FSL_IMX7_NUM_CANS] = { + FSL_IMX7_CAN1_ADDR, + FSL_IMX7_CAN2_ADDR, + }; + + snprintf(name, NAME_SIZE, "can%d", i); + create_unimplemented_device(name, FSL_IMX7_CANn_ADDR[i], + FSL_IMX7_CANn_SIZE); + } /* - * SAI (Audio SSI (Synchronous Serial Interface)) + * SAIs (Audio SSI (Synchronous Serial Interface)) */ - create_unimplemented_device("sai1", FSL_IMX7_SAI1_ADDR, FSL_IMX7_SAIn_SIZE); - create_unimplemented_device("sai2", FSL_IMX7_SAI2_ADDR, FSL_IMX7_SAIn_SIZE); - create_unimplemented_device("sai2", FSL_IMX7_SAI3_ADDR, FSL_IMX7_SAIn_SIZE); + for (i = 0; i < FSL_IMX7_NUM_SAIS; i++) { + static const hwaddr FSL_IMX7_SAIn_ADDR[FSL_IMX7_NUM_SAIS] = { + FSL_IMX7_SAI1_ADDR, + FSL_IMX7_SAI2_ADDR, + FSL_IMX7_SAI3_ADDR, + }; + + snprintf(name, NAME_SIZE, "sai%d", i); + create_unimplemented_device(name, FSL_IMX7_SAIn_ADDR[i], + FSL_IMX7_SAIn_SIZE); + } /* * OCOTP @@ -480,9 +585,15 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) create_unimplemented_device("ocotp", FSL_IMX7_OCOTP_ADDR, FSL_IMX7_OCOTP_SIZE); + /* + * GPR + */ sysbus_realize(SYS_BUS_DEVICE(&s->gpr), &error_abort); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpr), 0, FSL_IMX7_GPR_ADDR); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpr), 0, FSL_IMX7_IOMUXC_GPR_ADDR); + /* + * PCIE + */ sysbus_realize(SYS_BUS_DEVICE(&s->pcie), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, FSL_IMX7_PCIE_REG_ADDR); @@ -495,7 +606,9 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTD_IRQ); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, irq); - + /* + * USBs + */ for (i = 0; i < FSL_IMX7_NUM_USBS; i++) { static const hwaddr FSL_IMX7_USBMISCn_ADDR[FSL_IMX7_NUM_USBS] = { FSL_IMX7_USBMISC1_ADDR, @@ -557,11 +670,79 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) */ create_unimplemented_device("pcie-phy", FSL_IMX7_PCIE_PHY_ADDR, FSL_IMX7_PCIE_PHY_SIZE); + + /* + * CSU + */ + create_unimplemented_device("csu", FSL_IMX7_CSU_ADDR, + FSL_IMX7_CSU_SIZE); + + /* + * TZASC + */ + create_unimplemented_device("tzasc", FSL_IMX7_TZASC_ADDR, + FSL_IMX7_TZASC_SIZE); + + /* + * OCRAM memory + */ + memory_region_init_ram(&s->ocram, NULL, "imx7.ocram", + FSL_IMX7_OCRAM_MEM_SIZE, + &error_abort); + memory_region_add_subregion(get_system_memory(), FSL_IMX7_OCRAM_MEM_ADDR, + &s->ocram); + + /* + * OCRAM EPDC memory + */ + memory_region_init_ram(&s->ocram_epdc, NULL, "imx7.ocram_epdc", + FSL_IMX7_OCRAM_EPDC_SIZE, + &error_abort); + memory_region_add_subregion(get_system_memory(), FSL_IMX7_OCRAM_EPDC_ADDR, + &s->ocram_epdc); + + /* + * OCRAM PXP memory + */ + memory_region_init_ram(&s->ocram_pxp, NULL, "imx7.ocram_pxp", + FSL_IMX7_OCRAM_PXP_SIZE, + &error_abort); + memory_region_add_subregion(get_system_memory(), FSL_IMX7_OCRAM_PXP_ADDR, + &s->ocram_pxp); + + /* + * OCRAM_S memory + */ + memory_region_init_ram(&s->ocram_s, NULL, "imx7.ocram_s", + FSL_IMX7_OCRAM_S_SIZE, + &error_abort); + memory_region_add_subregion(get_system_memory(), FSL_IMX7_OCRAM_S_ADDR, + &s->ocram_s); + + /* + * ROM memory + */ + memory_region_init_rom(&s->rom, OBJECT(dev), "imx7.rom", + FSL_IMX7_ROM_SIZE, &error_abort); + memory_region_add_subregion(get_system_memory(), FSL_IMX7_ROM_ADDR, + &s->rom); + + /* + * CAAM memory + */ + memory_region_init_rom(&s->caam, OBJECT(dev), "imx7.caam", + FSL_IMX7_CAAM_MEM_SIZE, &error_abort); + memory_region_add_subregion(get_system_memory(), FSL_IMX7_CAAM_MEM_ADDR, + &s->caam); } static Property fsl_imx7_properties[] = { DEFINE_PROP_UINT32("fec1-phy-num", FslIMX7State, phy_num[0], 0), DEFINE_PROP_UINT32("fec2-phy-num", FslIMX7State, phy_num[1], 1), + DEFINE_PROP_BOOL("fec1-phy-connected", FslIMX7State, phy_connected[0], + true), + DEFINE_PROP_BOOL("fec2-phy-connected", FslIMX7State, phy_connected[1], + true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/arm/gumstix.c b/hw/arm/gumstix.c index 3a4bc332c4..9146269153 100644 --- a/hw/arm/gumstix.c +++ b/hw/arm/gumstix.c @@ -10,7 +10,7 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ - + /* * Example usage: * @@ -35,6 +35,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "hw/arm/pxa.h" #include "net/net.h" @@ -43,20 +44,21 @@ #include "hw/boards.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" -#include "cpu.h" -static const int sector_len = 128 * 1024; +#define CONNEX_FLASH_SIZE (16 * MiB) +#define CONNEX_RAM_SIZE (64 * MiB) + +#define VERDEX_FLASH_SIZE (32 * MiB) +#define VERDEX_RAM_SIZE (256 * MiB) + +#define FLASH_SECTOR_SIZE (128 * KiB) static void connex_init(MachineState *machine) { PXA2xxState *cpu; DriveInfo *dinfo; - MemoryRegion *address_space_mem = get_system_memory(); - uint32_t connex_rom = 0x01000000; - uint32_t connex_ram = 0x04000000; - - cpu = pxa255_init(address_space_mem, connex_ram); + cpu = pxa255_init(CONNEX_RAM_SIZE); dinfo = drive_get(IF_PFLASH, 0, 0); if (!dinfo && !qtest_enabled()) { @@ -65,28 +67,21 @@ static void connex_init(MachineState *machine) exit(1); } - if (!pflash_cfi01_register(0x00000000, "connext.rom", connex_rom, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - sector_len, 2, 0, 0, 0, 0, 0)) { - error_report("Error registering flash memory"); - exit(1); - } + /* Numonyx RC28F128J3F75 */ + pflash_cfi01_register(0x00000000, "connext.rom", CONNEX_FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + FLASH_SECTOR_SIZE, 2, 0, 0, 0, 0, 0); /* Interrupt line of NIC is connected to GPIO line 36 */ - smc91c111_init(&nd_table[0], 0x04000300, - qdev_get_gpio_in(cpu->gpio, 36)); + smc91c111_init(0x04000300, qdev_get_gpio_in(cpu->gpio, 36)); } static void verdex_init(MachineState *machine) { PXA2xxState *cpu; DriveInfo *dinfo; - MemoryRegion *address_space_mem = get_system_memory(); - uint32_t verdex_rom = 0x02000000; - uint32_t verdex_ram = 0x10000000; - - cpu = pxa270_init(address_space_mem, verdex_ram, machine->cpu_type); + cpu = pxa270_init(VERDEX_RAM_SIZE, machine->cpu_type); dinfo = drive_get(IF_PFLASH, 0, 0); if (!dinfo && !qtest_enabled()) { @@ -95,16 +90,13 @@ static void verdex_init(MachineState *machine) exit(1); } - if (!pflash_cfi01_register(0x00000000, "verdex.rom", verdex_rom, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - sector_len, 2, 0, 0, 0, 0, 0)) { - error_report("Error registering flash memory"); - exit(1); - } + /* Micron RC28F256P30TFA */ + pflash_cfi01_register(0x00000000, "verdex.rom", VERDEX_FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + FLASH_SECTOR_SIZE, 2, 0, 0, 0, 0, 0); /* Interrupt line of NIC is connected to GPIO line 99 */ - smc91c111_init(&nd_table[0], 0x04000300, - qdev_get_gpio_in(cpu->gpio, 99)); + smc91c111_init(0x04000300, qdev_get_gpio_in(cpu->gpio, 99)); } static void connex_class_init(ObjectClass *oc, void *data) @@ -114,6 +106,7 @@ static void connex_class_init(ObjectClass *oc, void *data) mc->desc = "Gumstix Connex (PXA255)"; mc->init = connex_init; mc->ignore_memory_transaction_failures = true; + mc->deprecation_reason = "machine is old and unmaintained"; } static const TypeInfo connex_type = { @@ -126,9 +119,10 @@ static void verdex_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->desc = "Gumstix Verdex (PXA270)"; + mc->desc = "Gumstix Verdex Pro XL6P COMs (PXA270)"; mc->init = verdex_init; mc->ignore_memory_transaction_failures = true; + mc->deprecation_reason = "machine is old and unmaintained"; mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c0"); } diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index f12aacea6b..c71b1a8db3 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -30,12 +30,13 @@ #include "hw/boards.h" #include "qemu/error-report.h" #include "hw/char/pl011.h" -#include "hw/ide/ahci.h" +#include "hw/ide/ahci-sysbus.h" #include "hw/cpu/a9mpcore.h" #include "hw/cpu/a15mpcore.h" #include "qemu/log.h" #include "qom/object.h" #include "cpu.h" +#include "target/arm/cpu-qom.h" #define SMP_BOOT_ADDR 0x100 #define SMP_BOOT_REG 0x40 @@ -112,7 +113,7 @@ static const VMStateDescription vmstate_highbank_regs = { .name = "highbank-regs", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, HighbankRegsState, NUM_REGS), VMSTATE_END_OF_LIST(), }, @@ -208,6 +209,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) cpuobj = object_new(machine->cpu_type); cpu = ARM_CPU(cpuobj); + object_property_add_child(OBJECT(machine), "cpu[*]", cpuobj); object_property_set_int(cpuobj, "psci-conduit", QEMU_PSCI_CONDUIT_SMC, &error_abort); @@ -296,19 +298,17 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) sysbus_create_simple(TYPE_SYSBUS_AHCI, 0xffe08000, pic[83]); - if (nd_table[0].used) { - qemu_check_nic_model(&nd_table[0], "xgmac"); - dev = qdev_new("xgmac"); - qdev_set_nic_properties(dev, &nd_table[0]); + dev = qemu_create_nic_device("xgmac", true, NULL); + if (dev) { sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xfff50000); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[77]); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, pic[78]); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, pic[79]); + } - qemu_check_nic_model(&nd_table[1], "xgmac"); - dev = qdev_new("xgmac"); - qdev_set_nic_properties(dev, &nd_table[1]); + dev = qemu_create_nic_device("xgmac", true, NULL); + if (dev) { sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xfff51000); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[80]); @@ -343,10 +343,15 @@ static void midway_init(MachineState *machine) static void highbank_class_init(ObjectClass *oc, void *data) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a9"), + NULL + }; MachineClass *mc = MACHINE_CLASS(oc); mc->desc = "Calxeda Highbank (ECX-1000)"; mc->init = highbank_init; + mc->valid_cpu_types = valid_cpu_types; mc->block_default_type = IF_IDE; mc->units_per_default_bus = 1; mc->max_cpus = 4; @@ -362,10 +367,15 @@ static const TypeInfo highbank_type = { static void midway_class_init(ObjectClass *oc, void *data) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a15"), + NULL + }; MachineClass *mc = MACHINE_CLASS(oc); mc->desc = "Calxeda Midway (ECX-2000)"; mc->init = midway_init; + mc->valid_cpu_types = valid_cpu_types; mc->block_default_type = IF_IDE; mc->units_per_default_bus = 1; mc->max_cpus = 4; diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c index b4f7f4e8a7..7dfddd49e2 100644 --- a/hw/arm/imx25_pdk.c +++ b/hw/arm/imx25_pdk.c @@ -27,6 +27,7 @@ #include "qapi/error.h" #include "hw/qdev-properties.h" #include "hw/arm/fsl-imx25.h" +#include "hw/arm/boot.h" #include "hw/boards.h" #include "qemu/error-report.h" #include "sysemu/qtest.h" diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index b109ece3ae..feb0dd63df 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -9,7 +9,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "cpu.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "hw/boards.h" @@ -27,6 +26,8 @@ #include "hw/irq.h" #include "hw/sd/sd.h" #include "qom/object.h" +#include "audio/audio.h" +#include "target/arm/cpu-qom.h" #define TYPE_INTEGRATOR_CM "integrator_core" OBJECT_DECLARE_SIMPLE_TYPE(IntegratorCMState, INTEGRATOR_CM) @@ -62,7 +63,7 @@ static const VMStateDescription vmstate_integratorcm = { .name = "integratorcm", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(cm_osc, IntegratorCMState), VMSTATE_UINT32(cm_ctrl, IntegratorCMState), VMSTATE_UINT32(cm_lock, IntegratorCMState), @@ -290,12 +291,9 @@ static void integratorcm_realize(DeviceState *d, Error **errp) { IntegratorCMState *s = INTEGRATOR_CM(d); SysBusDevice *dev = SYS_BUS_DEVICE(d); - Error *local_err = NULL; - memory_region_init_ram(&s->flash, OBJECT(d), "integrator.flash", 0x100000, - &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!memory_region_init_ram(&s->flash, OBJECT(d), "integrator.flash", + 0x100000, errp)) { return; } @@ -345,7 +343,7 @@ static const VMStateDescription vmstate_icp_pic = { .name = "icp_pic", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(level, icp_pic_state), VMSTATE_UINT32(irq_enabled, icp_pic_state), VMSTATE_UINT32(fiq_enabled, icp_pic_state), @@ -487,7 +485,7 @@ static const VMStateDescription vmstate_icp_control = { .name = "icp_control", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(intreg_state, ICPCtrlRegsState), VMSTATE_END_OF_LIST() } @@ -660,12 +658,24 @@ static void integratorcp_init(MachineState *machine) &error_fatal); } - sysbus_create_varargs("pl041", 0x1d000000, pic[25], NULL); + dev = qdev_new("pl041"); + if (machine->audiodev) { + qdev_prop_set_string(dev, "audiodev", machine->audiodev); + } + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x1d000000); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[25]); - if (nd_table[0].used) - smc91c111_init(&nd_table[0], 0xc8000000, pic[27]); + if (qemu_find_nic_info("smc91c111", true, NULL)) { + smc91c111_init(0xc8000000, pic[27]); + } - sysbus_create_simple("pl110", 0xc0000000, pic[22]); + dev = qdev_new("pl110"); + object_property_set_link(OBJECT(dev), "framebuffer-memory", + OBJECT(address_space_mem), &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xc0000000); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[22]); integrator_binfo.ram_size = ram_size; arm_load_kernel(cpu, machine, &integrator_binfo); @@ -678,6 +688,8 @@ static void integratorcp_machine_init(MachineClass *mc) mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); mc->default_ram_id = "integrator.ram"; + + machine_add_audiodev_property(mc); } DEFINE_MACHINE("integratorcp", integratorcp_machine_init) diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index b1b281c9ac..2ccd6f8a76 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -16,6 +16,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx31.h" +#include "hw/arm/boot.h" #include "hw/boards.h" #include "qemu/error-report.h" #include "exec/address-spaces.h" @@ -112,8 +113,8 @@ static void kzm_init(MachineState *machine) alias_offset += ram[i].size; } - if (nd_table[0].used) { - lan9118_init(&nd_table[0], KZM_LAN9118_ADDR, + if (qemu_find_nic_info("lan9118", true, NULL)) { + lan9118_init(KZM_LAN9118_ADDR, qdev_get_gpio_in(DEVICE(&s->soc.avic), 52)); } diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c index 8454b65458..3a6c22fddb 100644 --- a/hw/arm/mainstone.c +++ b/hw/arm/mainstone.c @@ -12,6 +12,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "hw/arm/pxa.h" @@ -22,7 +23,6 @@ #include "hw/block/flash.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" -#include "cpu.h" /* Device addresses */ #define MST_FPGA_PHYS 0x08000000 @@ -99,20 +99,20 @@ static const struct keymap map[0xE0] = { enum mainstone_model_e { mainstone }; -#define MAINSTONE_RAM 0x04000000 -#define MAINSTONE_ROM 0x00800000 -#define MAINSTONE_FLASH 0x02000000 +#define MAINSTONE_RAM_SIZE (64 * MiB) +#define MAINSTONE_ROM_SIZE (8 * MiB) +#define MAINSTONE_FLASH_SIZE (32 * MiB) static struct arm_boot_info mainstone_binfo = { .loader_start = PXA2XX_SDRAM_BASE, - .ram_size = 0x04000000, + .ram_size = MAINSTONE_RAM_SIZE, }; -static void mainstone_common_init(MemoryRegion *address_space_mem, - MachineState *machine, +#define FLASH_SECTOR_SIZE (256 * KiB) + +static void mainstone_common_init(MachineState *machine, enum mainstone_model_e model, int arm_id) { - uint32_t sector_len = 256 * 1024; hwaddr mainstone_flash_base[] = { MST_FLASH_0, MST_FLASH_1 }; PXA2xxState *mpu; DeviceState *mst_irq; @@ -121,23 +121,19 @@ static void mainstone_common_init(MemoryRegion *address_space_mem, 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_rom(rom, NULL, "mainstone.rom", MAINSTONE_ROM, + mpu = pxa270_init(mainstone_binfo.ram_size, machine->cpu_type); + memory_region_init_rom(rom, NULL, "mainstone.rom", MAINSTONE_ROM_SIZE, &error_fatal); - memory_region_add_subregion(address_space_mem, 0, rom); + memory_region_add_subregion(get_system_memory(), 0x00000000, rom); /* There are two 32MiB flash devices on the board */ for (i = 0; i < 2; i ++) { dinfo = drive_get(IF_PFLASH, 0, i); - if (!pflash_cfi01_register(mainstone_flash_base[i], - i ? "mainstone.flash1" : "mainstone.flash0", - MAINSTONE_FLASH, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - sector_len, 4, 0, 0, 0, 0, 0)) { - error_report("Error registering flash memory"); - exit(1); - } + pflash_cfi01_register(mainstone_flash_base[i], + i ? "mainstone.flash1" : "mainstone.flash0", + MAINSTONE_FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + FLASH_SECTOR_SIZE, 4, 0, 0, 0, 0, 0); } mst_irq = sysbus_create_simple("mainstone-fpga", MST_FPGA_PHYS, @@ -156,8 +152,7 @@ static void mainstone_common_init(MemoryRegion *address_space_mem, qdev_get_gpio_in(mst_irq, S1_IRQ), qdev_get_gpio_in(mst_irq, S1_CD_IRQ)); - smc91c111_init(&nd_table[0], MST_ETH_PHYS, - qdev_get_gpio_in(mst_irq, ETHERNET_IRQ)); + smc91c111_init(MST_ETH_PHYS, qdev_get_gpio_in(mst_irq, ETHERNET_IRQ)); mainstone_binfo.board_id = arm_id; arm_load_kernel(mpu->cpu, machine, &mainstone_binfo); @@ -165,7 +160,7 @@ static void mainstone_common_init(MemoryRegion *address_space_mem, static void mainstone_init(MachineState *machine) { - mainstone_common_init(get_system_memory(), machine, mainstone, 0x196); + mainstone_common_init(machine, mainstone, 0x196); } static void mainstone2_machine_init(MachineClass *mc) @@ -174,6 +169,7 @@ static void mainstone2_machine_init(MachineClass *mc) mc->init = mainstone_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c5"); + mc->deprecation_reason = "machine is old and unmaintained"; } DEFINE_MACHINE("mainstone", mainstone2_machine_init) diff --git a/hw/arm/mcimx6ul-evk.c b/hw/arm/mcimx6ul-evk.c index d83c3c380e..500427e94b 100644 --- a/hw/arm/mcimx6ul-evk.c +++ b/hw/arm/mcimx6ul-evk.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx6ul.h" +#include "hw/arm/boot.h" #include "hw/boards.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" @@ -41,6 +42,8 @@ static void mcimx6ul_evk_init(MachineState *machine) object_property_add_child(OBJECT(machine), "soc", OBJECT(s)); object_property_set_uint(OBJECT(s), "fec1-phy-num", 2, &error_fatal); object_property_set_uint(OBJECT(s), "fec2-phy-num", 1, &error_fatal); + object_property_set_bool(OBJECT(s), "fec1-phy-connected", false, + &error_fatal); qdev_realize(DEVICE(s), NULL, &error_fatal); memory_region_add_subregion(get_system_memory(), FSL_IMX6UL_MMDC_ADDR, diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c index 6182b15f19..693a1023b6 100644 --- a/hw/arm/mcimx7d-sabre.c +++ b/hw/arm/mcimx7d-sabre.c @@ -15,6 +15,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx7.h" +#include "hw/arm/boot.h" #include "hw/boards.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" @@ -41,6 +42,8 @@ static void mcimx7d_sabre_init(MachineState *machine) s = FSL_IMX7(object_new(TYPE_FSL_IMX7)); object_property_add_child(OBJECT(machine), "soc", OBJECT(s)); + object_property_set_bool(OBJECT(s), "fec2-phy-connected", false, + &error_fatal); qdev_realize(DEVICE(s), NULL, &error_fatal); memory_region_add_subregion(get_system_memory(), FSL_IMX7_MMDC_ADDR, diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 2d8381339c..6808135c1f 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -3,30 +3,21 @@ arm_ss.add(files('boot.c'), fdt) arm_ss.add(when: 'CONFIG_ARM_VIRT', if_true: files('virt.c')) arm_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) arm_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic_boards.c')) -arm_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4_boards.c')) arm_ss.add(when: 'CONFIG_EMCRAFT_SF2', if_true: files('msf2-som.c')) arm_ss.add(when: 'CONFIG_HIGHBANK', if_true: files('highbank.c')) arm_ss.add(when: 'CONFIG_INTEGRATOR', if_true: files('integratorcp.c')) arm_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mainstone.c')) arm_ss.add(when: 'CONFIG_MICROBIT', if_true: files('microbit.c')) +arm_ss.add(when: 'CONFIG_MPS3R', if_true: files('mps3r.c')) arm_ss.add(when: 'CONFIG_MUSICPAL', if_true: files('musicpal.c')) -arm_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c')) arm_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c')) +arm_ss.add(when: 'CONFIG_OLIMEX_STM32_H405', if_true: files('olimex-stm32-h405.c')) arm_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c', 'npcm7xx_boards.c')) arm_ss.add(when: 'CONFIG_NSERIES', if_true: files('nseries.c')) -arm_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c')) -arm_ss.add(when: 'CONFIG_CHEETAH', if_true: files('palm.c')) -arm_ss.add(when: 'CONFIG_GUMSTIX', if_true: files('gumstix.c')) -arm_ss.add(when: 'CONFIG_SPITZ', if_true: files('spitz.c')) -arm_ss.add(when: 'CONFIG_TOSA', if_true: files('tosa.c')) -arm_ss.add(when: 'CONFIG_Z2', if_true: files('z2.c')) arm_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview.c')) arm_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa-ref.c')) arm_ss.add(when: 'CONFIG_STELLARIS', if_true: files('stellaris.c')) arm_ss.add(when: 'CONFIG_STM32VLDISCOVERY', if_true: files('stm32vldiscovery.c')) -arm_ss.add(when: 'CONFIG_COLLIE', if_true: files('collie.c')) -arm_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c')) -arm_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c')) arm_ss.add(when: 'CONFIG_ZYNQ', if_true: files('xilinx_zynq.c')) arm_ss.add(when: 'CONFIG_SABRELITE', if_true: files('sabrelite.c')) @@ -34,32 +25,56 @@ arm_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m.c')) arm_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210.c')) arm_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx.c', 'pxa2xx_gpio.c', 'pxa2xx_pic.c')) arm_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic.c')) -arm_ss.add(when: 'CONFIG_OMAP', if_true: files('omap1.c', 'omap2.c')) -arm_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c')) +arm_ss.add(when: 'CONFIG_OMAP', if_true: files('omap1.c')) arm_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('allwinner-a10.c', 'cubieboard.c')) arm_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3.c', 'orangepi.c')) -arm_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c', 'bcm2836.c', 'raspi.c')) +arm_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40.c', 'bananapi_m2u.c')) +arm_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2836.c', 'raspi.c')) +arm_ss.add(when: ['CONFIG_RASPI', 'TARGET_AARCH64'], if_true: files('bcm2838.c', 'raspi4b.c')) arm_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('stm32f100_soc.c')) arm_ss.add(when: 'CONFIG_STM32F205_SOC', if_true: files('stm32f205_soc.c')) arm_ss.add(when: 'CONFIG_STM32F405_SOC', if_true: files('stm32f405_soc.c')) +arm_ss.add(when: 'CONFIG_B_L475E_IOT01A', if_true: files('b-l475e-iot01a.c')) +arm_ss.add(when: 'CONFIG_STM32L4X5_SOC', if_true: files('stm32l4x5_soc.c')) arm_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp.c', 'xlnx-zcu102.c')) arm_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal.c', 'xlnx-versal-virt.c')) arm_ss.add(when: 'CONFIG_FSL_IMX25', if_true: files('fsl-imx25.c', 'imx25_pdk.c')) arm_ss.add(when: 'CONFIG_FSL_IMX31', if_true: files('fsl-imx31.c', 'kzm.c')) arm_ss.add(when: 'CONFIG_FSL_IMX6', if_true: files('fsl-imx6.c')) arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( - 'aspeed_soc.c', 'aspeed.c', + 'aspeed_soc_common.c', + 'aspeed_ast2400.c', 'aspeed_ast2600.c', - 'aspeed_ast10x0.c')) + 'aspeed_ast10x0.c', + 'aspeed_eeprom.c', + 'fby35.c')) arm_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2.c')) arm_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2-tz.c')) arm_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-soc.c')) arm_ss.add(when: 'CONFIG_MUSCA', if_true: files('musca.c')) arm_ss.add(when: 'CONFIG_ARMSSE', if_true: files('armsse.c')) arm_ss.add(when: 'CONFIG_FSL_IMX7', if_true: files('fsl-imx7.c', 'mcimx7d-sabre.c')) -arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c', 'smmuv3.c')) +arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c')) arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c')) arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c')) +arm_ss.add(when: 'CONFIG_XEN', if_true: files('xen_arm.c')) + +system_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c')) +system_ss.add(when: 'CONFIG_CHEETAH', if_true: files('palm.c')) +system_ss.add(when: 'CONFIG_COLLIE', if_true: files('collie.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4_boards.c')) +system_ss.add(when: 'CONFIG_GUMSTIX', if_true: files('gumstix.c')) +system_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap2.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2838_peripherals.c')) +system_ss.add(when: 'CONFIG_SPITZ', if_true: files('spitz.c')) +system_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c')) +system_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c')) +system_ss.add(when: 'CONFIG_TOSA', if_true: files('tosa.c')) +system_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c')) +system_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c')) +system_ss.add(when: 'CONFIG_Z2', if_true: files('z2.c')) hw_arch += {'arm': arm_ss} diff --git a/hw/arm/microbit.c b/hw/arm/microbit.c index e9494334ce..50df362088 100644 --- a/hw/arm/microbit.c +++ b/hw/arm/microbit.c @@ -57,7 +57,7 @@ static void microbit_init(MachineState *machine) mr, -1); armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, - s->nrf51.flash_size); + 0, s->nrf51.flash_size); } static void microbit_machine_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 4017392bf5..a2d18afd79 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -48,6 +48,7 @@ #include "qemu/units.h" #include "qemu/cutils.h" #include "qapi/error.h" +#include "qapi/qmp/qlist.h" #include "qemu/error-report.h" #include "hw/arm/boot.h" #include "hw/arm/armv7m.h" @@ -124,6 +125,10 @@ struct MPS2TZMachineClass { int uart_overflow_irq; /* number of the combined UART overflow IRQ */ uint32_t init_svtor; /* init-svtor setting for SSE */ uint32_t sram_addr_width; /* SRAM_ADDR_WIDTH setting for SSE */ + uint32_t cpu0_mpu_ns; /* CPU0_MPU_NS setting for SSE */ + uint32_t cpu0_mpu_s; /* CPU0_MPU_S setting for SSE */ + uint32_t cpu1_mpu_ns; /* CPU1_MPU_NS setting for SSE */ + uint32_t cpu1_mpu_s; /* CPU1_MPU_S setting for SSE */ const RAMInfo *raminfo; const char *armsse_type; uint32_t boot_ram_size; /* size of ram at address 0; 0 == find in raminfo */ @@ -152,7 +157,7 @@ struct MPS2TZMachineState { TZMSC msc[4]; CMSDKAPBUART uart[6]; SplitIRQ sec_resp_splitter; - qemu_or_irq uart_irq_orgate; + OrIRQState uart_irq_orgate; DeviceState *lan9118; SplitIRQ cpu_irq_splitter[MPS2TZ_NUMIRQ_MAX]; Clock *sysclk; @@ -183,6 +188,9 @@ OBJECT_DECLARE_TYPE(MPS2TZMachineState, MPS2TZMachineClass, MPS2TZ_MACHINE) #define MPS3_DDR_SIZE (2 * GiB) #endif +/* For cpu{0,1}_mpu_{ns,s}, means "leave at SSE's default value" */ +#define MPU_REGION_DEFAULT UINT32_MAX + static const uint32_t an505_oscclk[] = { 40000000, 24580000, @@ -454,6 +462,7 @@ static MemoryRegion *make_scc(MPS2TZMachineState *mms, void *opaque, MPS2SCC *scc = opaque; DeviceState *sccdev; MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); + QList *oscclk; uint32_t i; object_initialize_child(OBJECT(mms), "scc", scc, TYPE_MPS2_SCC); @@ -462,11 +471,13 @@ static MemoryRegion *make_scc(MPS2TZMachineState *mms, void *opaque, qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2); qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008); qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); - qdev_prop_set_uint32(sccdev, "len-oscclk", mmc->len_oscclk); + + oscclk = qlist_new(); for (i = 0; i < mmc->len_oscclk; i++) { - g_autofree char *propname = g_strdup_printf("oscclk[%u]", i); - qdev_prop_set_uint32(sccdev, propname, mmc->oscclk[i]); + qlist_append_int(oscclk, mmc->oscclk[i]); } + qdev_prop_set_array(sccdev, "oscclk", oscclk); + sysbus_realize(SYS_BUS_DEVICE(scc), &error_fatal); return sysbus_mmio_get_region(SYS_BUS_DEVICE(sccdev), 0); } @@ -492,14 +503,12 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, const PPCExtraData *extradata) { SysBusDevice *s; - NICInfo *nd = &nd_table[0]; /* In hardware this is a LAN9220; the LAN9118 is software compatible * except that it doesn't support the checksum-offload feature. */ - qemu_check_nic_model(nd, "lan9118"); mms->lan9118 = qdev_new(TYPE_LAN9118); - qdev_set_nic_properties(mms->lan9118, nd); + qemu_configure_nic_device(mms->lan9118, true, NULL); s = SYS_BUS_DEVICE(mms->lan9118); sysbus_realize_and_unref(s, &error_fatal); @@ -517,7 +526,6 @@ static MemoryRegion *make_eth_usb(MPS2TZMachineState *mms, void *opaque, * irqs[] is the ethernet IRQ. */ SysBusDevice *s; - NICInfo *nd = &nd_table[0]; memory_region_init(&mms->eth_usb_container, OBJECT(mms), "mps2-tz-eth-usb-container", 0x200000); @@ -526,9 +534,8 @@ static MemoryRegion *make_eth_usb(MPS2TZMachineState *mms, void *opaque, * In hardware this is a LAN9220; the LAN9118 is software compatible * except that it doesn't support the checksum-offload feature. */ - qemu_check_nic_model(nd, "lan9118"); mms->lan9118 = qdev_new(TYPE_LAN9118); - qdev_set_nic_properties(mms->lan9118, nd); + qemu_configure_nic_device(mms->lan9118, true, NULL); s = SYS_BUS_DEVICE(mms->lan9118); sysbus_realize_and_unref(s, &error_fatal); @@ -802,12 +809,6 @@ static void mps2tz_common_init(MachineState *machine) int num_ppcs; int i; - if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) { - error_report("This board can only be used with CPU %s", - mc->default_cpu_type); - 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); @@ -828,6 +829,20 @@ static void mps2tz_common_init(MachineState *machine) OBJECT(system_memory), &error_abort); qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", mmc->numirq); qdev_prop_set_uint32(iotkitdev, "init-svtor", mmc->init_svtor); + if (mmc->cpu0_mpu_ns != MPU_REGION_DEFAULT) { + qdev_prop_set_uint32(iotkitdev, "CPU0_MPU_NS", mmc->cpu0_mpu_ns); + } + if (mmc->cpu0_mpu_s != MPU_REGION_DEFAULT) { + qdev_prop_set_uint32(iotkitdev, "CPU0_MPU_S", mmc->cpu0_mpu_s); + } + if (object_property_find(OBJECT(iotkitdev), "CPU1_MPU_NS")) { + if (mmc->cpu1_mpu_ns != MPU_REGION_DEFAULT) { + qdev_prop_set_uint32(iotkitdev, "CPU1_MPU_NS", mmc->cpu1_mpu_ns); + } + if (mmc->cpu1_mpu_s != MPU_REGION_DEFAULT) { + qdev_prop_set_uint32(iotkitdev, "CPU1_MPU_S", mmc->cpu1_mpu_s); + } + } qdev_prop_set_uint32(iotkitdev, "SRAM_ADDR_WIDTH", mmc->sram_addr_width); qdev_connect_clock_in(iotkitdev, "MAINCLK", mms->sysclk); qdev_connect_clock_in(iotkitdev, "S32KCLK", mms->s32kclk); @@ -1197,7 +1212,7 @@ static void mps2tz_common_init(MachineState *machine) } armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, - boot_ram_size(mms)); + 0, boot_ram_size(mms)); } static void mps2_tz_idau_check(IDAUInterface *ii, uint32_t address, @@ -1205,7 +1220,7 @@ static void mps2_tz_idau_check(IDAUInterface *ii, uint32_t address, { /* * The MPS2 TZ FPGA images have IDAUs in them which are connected to - * the Master Security Controllers. Thes have the same logic as + * the Master Security Controllers. These have the same logic as * is used by the IoTKit for the IDAU connected to the CPU, except * that MSCs don't care about the NSC attribute. */ @@ -1239,7 +1254,7 @@ static void mps2_set_remap(Object *obj, const char *value, Error **errp) } } -static void mps2_machine_reset(MachineState *machine) +static void mps2_machine_reset(MachineState *machine, ShutdownCause reason) { MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); @@ -1249,17 +1264,24 @@ static void mps2_machine_reset(MachineState *machine) * reset see the correct mapping. */ remap_memory(mms, mms->remap); - qemu_devices_reset(); + qemu_devices_reset(reason); } static void mps2tz_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(oc); + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); mc->init = mps2tz_common_init; mc->reset = mps2_machine_reset; iic->check = mps2_tz_idau_check; + + /* Most machines leave these at the SSE defaults */ + mmc->cpu0_mpu_ns = MPU_REGION_DEFAULT; + mmc->cpu0_mpu_s = MPU_REGION_DEFAULT; + mmc->cpu1_mpu_ns = MPU_REGION_DEFAULT; + mmc->cpu1_mpu_s = MPU_REGION_DEFAULT; } static void mps2tz_set_default_ram_info(MPS2TZMachineClass *mmc) @@ -1286,6 +1308,10 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m33"), + NULL + }; mc->desc = "ARM MPS2 with AN505 FPGA image for Cortex-M33"; mc->default_cpus = 1; @@ -1293,6 +1319,7 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data) mc->max_cpus = mc->default_cpus; mmc->fpga_type = FPGA_AN505; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); + mc->valid_cpu_types = valid_cpu_types; mmc->scc_id = 0x41045050; mmc->sysclk_frq = 20 * 1000 * 1000; /* 20MHz */ mmc->apb_periph_frq = mmc->sysclk_frq; @@ -1315,6 +1342,10 @@ static void mps2tz_an521_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m33"), + NULL + }; mc->desc = "ARM MPS2 with AN521 FPGA image for dual Cortex-M33"; mc->default_cpus = 2; @@ -1322,6 +1353,7 @@ static void mps2tz_an521_class_init(ObjectClass *oc, void *data) mc->max_cpus = mc->default_cpus; mmc->fpga_type = FPGA_AN521; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); + mc->valid_cpu_types = valid_cpu_types; mmc->scc_id = 0x41045210; mmc->sysclk_frq = 20 * 1000 * 1000; /* 20MHz */ mmc->apb_periph_frq = mmc->sysclk_frq; @@ -1344,6 +1376,10 @@ static void mps3tz_an524_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m33"), + NULL + }; mc->desc = "ARM MPS3 with AN524 FPGA image for dual Cortex-M33"; mc->default_cpus = 2; @@ -1351,6 +1387,7 @@ static void mps3tz_an524_class_init(ObjectClass *oc, void *data) mc->max_cpus = mc->default_cpus; mmc->fpga_type = FPGA_AN524; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); + mc->valid_cpu_types = valid_cpu_types; mmc->scc_id = 0x41045240; mmc->sysclk_frq = 32 * 1000 * 1000; /* 32MHz */ mmc->apb_periph_frq = mmc->sysclk_frq; @@ -1378,6 +1415,10 @@ static void mps3tz_an547_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m55"), + NULL + }; mc->desc = "ARM MPS3 with AN547 FPGA image for Cortex-M55"; mc->default_cpus = 1; @@ -1385,6 +1426,7 @@ static void mps3tz_an547_class_init(ObjectClass *oc, void *data) mc->max_cpus = mc->default_cpus; mmc->fpga_type = FPGA_AN547; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m55"); + mc->valid_cpu_types = valid_cpu_types; mmc->scc_id = 0x41055470; mmc->sysclk_frq = 32 * 1000 * 1000; /* 32MHz */ mmc->apb_periph_frq = 25 * 1000 * 1000; /* 25MHz */ @@ -1396,6 +1438,7 @@ static void mps3tz_an547_class_init(ObjectClass *oc, void *data) mmc->numirq = 96; mmc->uart_overflow_irq = 48; mmc->init_svtor = 0x00000000; + mmc->cpu0_mpu_s = mmc->cpu0_mpu_ns = 16; mmc->sram_addr_width = 21; mmc->raminfo = an547_raminfo; mmc->armsse_type = TYPE_SSE300; diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index bb76fa6889..50919ee46d 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -35,6 +35,7 @@ #include "hw/boards.h" #include "exec/address-spaces.h" #include "sysemu/sysemu.h" +#include "hw/qdev-properties.h" #include "hw/misc/unimp.h" #include "hw/char/cmsdk-apb-uart.h" #include "hw/timer/cmsdk-apb-timer.h" @@ -47,6 +48,7 @@ #include "net/net.h" #include "hw/watchdog/cmsdk-apb-watchdog.h" #include "hw/qdev-clock.h" +#include "qapi/qmp/qlist.h" #include "qom/object.h" typedef enum MPS2FPGAType { @@ -137,14 +139,9 @@ static void mps2_common_init(MachineState *machine) MemoryRegion *system_memory = get_system_memory(); MachineClass *mc = MACHINE_GET_CLASS(machine); DeviceState *armv7m, *sccdev; + QList *oscclk; int i; - if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) { - error_report("This board can only be used with CPU %s", - mc->default_cpu_type); - 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); @@ -282,6 +279,9 @@ static void mps2_common_init(MachineState *machine) qdev_connect_gpio_out(orgate_dev, 0, qdev_get_gpio_in(armv7m, 12)); for (i = 0; i < 5; i++) { + DeviceState *dev; + SysBusDevice *s; + static const hwaddr uartbase[] = {0x40004000, 0x40005000, 0x40006000, 0x40007000, 0x40009000}; @@ -294,12 +294,16 @@ static void mps2_common_init(MachineState *machine) rxovrint = qdev_get_gpio_in(orgate_dev, i * 2 + 1); } - cmsdk_apb_uart_create(uartbase[i], - qdev_get_gpio_in(armv7m, uartirq[i] + 1), - qdev_get_gpio_in(armv7m, uartirq[i]), - txovrint, rxovrint, - NULL, - serial_hd(i), SYSCLK_FRQ); + dev = qdev_new(TYPE_CMSDK_APB_UART); + s = SYS_BUS_DEVICE(dev); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); + qdev_prop_set_uint32(dev, "pclk-frq", SYSCLK_FRQ); + sysbus_realize_and_unref(s, &error_fatal); + sysbus_mmio_map(s, 0, uartbase[i]); + sysbus_connect_irq(s, 0, qdev_get_gpio_in(armv7m, uartirq[i] + 1)); + sysbus_connect_irq(s, 1, qdev_get_gpio_in(armv7m, uartirq[i])); + sysbus_connect_irq(s, 2, txovrint); + sysbus_connect_irq(s, 3, rxovrint); } break; } @@ -324,7 +328,8 @@ static void mps2_common_init(MachineState *machine) 0x4002c000, 0x4002d000, 0x4002e000}; Object *txrx_orgate; - DeviceState *txrx_orgate_dev; + DeviceState *txrx_orgate_dev, *dev; + SysBusDevice *s; txrx_orgate = object_new(TYPE_OR_IRQ); object_property_set_int(txrx_orgate, "num-lines", 2, &error_fatal); @@ -332,13 +337,17 @@ static void mps2_common_init(MachineState *machine) txrx_orgate_dev = DEVICE(txrx_orgate); qdev_connect_gpio_out(txrx_orgate_dev, 0, qdev_get_gpio_in(armv7m, uart_txrx_irqno[i])); - cmsdk_apb_uart_create(uartbase[i], - qdev_get_gpio_in(txrx_orgate_dev, 0), - qdev_get_gpio_in(txrx_orgate_dev, 1), - qdev_get_gpio_in(orgate_dev, i * 2), - qdev_get_gpio_in(orgate_dev, i * 2 + 1), - NULL, - serial_hd(i), SYSCLK_FRQ); + + dev = qdev_new(TYPE_CMSDK_APB_UART); + s = SYS_BUS_DEVICE(dev); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); + qdev_prop_set_uint32(dev, "pclk-frq", SYSCLK_FRQ); + sysbus_realize_and_unref(s, &error_fatal); + sysbus_mmio_map(s, 0, uartbase[i]); + sysbus_connect_irq(s, 0, qdev_get_gpio_in(txrx_orgate_dev, 0)); + sysbus_connect_irq(s, 1, qdev_get_gpio_in(txrx_orgate_dev, 1)); + sysbus_connect_irq(s, 2, qdev_get_gpio_in(orgate_dev, i * 2)); + sysbus_connect_irq(s, 3, qdev_get_gpio_in(orgate_dev, i * 2 + 1)); } break; } @@ -389,10 +398,12 @@ static void mps2_common_init(MachineState *machine) qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008); qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); /* All these FPGA images have the same OSCCLK configuration */ - qdev_prop_set_uint32(sccdev, "len-oscclk", 3); - qdev_prop_set_uint32(sccdev, "oscclk[0]", 50000000); - qdev_prop_set_uint32(sccdev, "oscclk[1]", 24576000); - qdev_prop_set_uint32(sccdev, "oscclk[2]", 25000000); + oscclk = qlist_new(); + qlist_append_int(oscclk, 50000000); + qlist_append_int(oscclk, 24576000); + qlist_append_int(oscclk, 25000000); + qdev_prop_set_array(sccdev, "oscclk", oscclk); + sysbus_realize(SYS_BUS_DEVICE(&mms->scc), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(sccdev), 0, 0x4002f000); object_initialize_child(OBJECT(mms), "fpgaio", @@ -445,12 +456,12 @@ static void mps2_common_init(MachineState *machine) /* In hardware this is a LAN9220; the LAN9118 is software compatible * except that it doesn't support the checksum-offload feature. */ - lan9118_init(&nd_table[0], mmc->ethernet_base, + lan9118_init(mmc->ethernet_base, qdev_get_gpio_in(armv7m, mmc->fpga_type == FPGA_AN511 ? 47 : 13)); armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, - 0x400000); + 0, 0x400000); } static void mps2_class_init(ObjectClass *oc, void *data) @@ -467,10 +478,15 @@ static void mps2_an385_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m3"), + NULL + }; mc->desc = "ARM MPS2 with AN385 FPGA image for Cortex-M3"; mmc->fpga_type = FPGA_AN385; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); + mc->valid_cpu_types = valid_cpu_types; mmc->scc_id = 0x41043850; mmc->psram_base = 0x21000000; mmc->ethernet_base = 0x40200000; @@ -481,10 +497,15 @@ static void mps2_an386_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m4"), + NULL + }; mc->desc = "ARM MPS2 with AN386 FPGA image for Cortex-M4"; mmc->fpga_type = FPGA_AN386; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m4"); + mc->valid_cpu_types = valid_cpu_types; mmc->scc_id = 0x41043860; mmc->psram_base = 0x21000000; mmc->ethernet_base = 0x40200000; @@ -495,10 +516,15 @@ static void mps2_an500_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m7"), + NULL + }; mc->desc = "ARM MPS2 with AN500 FPGA image for Cortex-M7"; mmc->fpga_type = FPGA_AN500; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m7"); + mc->valid_cpu_types = valid_cpu_types; mmc->scc_id = 0x41045000; mmc->psram_base = 0x60000000; mmc->ethernet_base = 0xa0000000; @@ -509,10 +535,15 @@ static void mps2_an511_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m3"), + NULL + }; mc->desc = "ARM MPS2 with AN511 DesignStart FPGA image for Cortex-M3"; mmc->fpga_type = FPGA_AN511; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); + mc->valid_cpu_types = valid_cpu_types; mmc->scc_id = 0x41045110; mmc->psram_base = 0x21000000; mmc->ethernet_base = 0x40200000; diff --git a/hw/arm/mps3r.c b/hw/arm/mps3r.c new file mode 100644 index 0000000000..4d55a6564c --- /dev/null +++ b/hw/arm/mps3r.c @@ -0,0 +1,640 @@ +/* + * Arm MPS3 board emulation for Cortex-R-based FPGA images. + * (For M-profile images see mps2.c and mps2tz.c.) + * + * Copyright (c) 2017 Linaro Limited + * Written by Peter Maydell + * + * 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. + */ + +/* + * The MPS3 is an FPGA based dev board. This file handles FPGA images + * which use the Cortex-R CPUs. We model these separately from the + * M-profile images, because on M-profile the FPGA image is based on + * a "Subsystem for Embedded" which is similar to an SoC, whereas + * the R-profile FPGA images don't have that abstraction layer. + * + * We model the following FPGA images here: + * "mps3-an536" -- dual Cortex-R52 as documented in Arm Application Note AN536 + * + * Application Note AN536: + * https://developer.arm.com/documentation/dai0536/latest/ + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qapi/qmp/qlist.h" +#include "exec/address-spaces.h" +#include "cpu.h" +#include "sysemu/sysemu.h" +#include "hw/boards.h" +#include "hw/or-irq.h" +#include "hw/qdev-clock.h" +#include "hw/qdev-properties.h" +#include "hw/arm/boot.h" +#include "hw/arm/bsa.h" +#include "hw/char/cmsdk-apb-uart.h" +#include "hw/i2c/arm_sbcon_i2c.h" +#include "hw/intc/arm_gicv3.h" +#include "hw/misc/mps2-scc.h" +#include "hw/misc/mps2-fpgaio.h" +#include "hw/misc/unimp.h" +#include "hw/net/lan9118.h" +#include "hw/rtc/pl031.h" +#include "hw/ssi/pl022.h" +#include "hw/timer/cmsdk-apb-dualtimer.h" +#include "hw/watchdog/cmsdk-apb-watchdog.h" + +/* Define the layout of RAM and ROM in a board */ +typedef struct RAMInfo { + const char *name; + hwaddr base; + hwaddr size; + int mrindex; /* index into rams[]; -1 for the system RAM block */ + int flags; +} RAMInfo; + +/* + * The MPS3 DDR is 3GiB, but on a 32-bit host QEMU doesn't permit + * emulation of that much guest RAM, so artificially make it smaller. + */ +#if HOST_LONG_BITS == 32 +#define MPS3_DDR_SIZE (1 * GiB) +#else +#define MPS3_DDR_SIZE (3 * GiB) +#endif + +/* + * Flag values: + * IS_MAIN: this is the main machine RAM + * IS_ROM: this area is read-only + */ +#define IS_MAIN 1 +#define IS_ROM 2 + +#define MPS3R_RAM_MAX 9 +#define MPS3R_CPU_MAX 2 +#define MPS3R_UART_MAX 4 /* shared UART count */ + +#define PERIPHBASE 0xf0000000 +#define NUM_SPIS 96 + +typedef enum MPS3RFPGAType { + FPGA_AN536, +} MPS3RFPGAType; + +struct MPS3RMachineClass { + MachineClass parent; + MPS3RFPGAType fpga_type; + const RAMInfo *raminfo; + hwaddr loader_start; +}; + +struct MPS3RMachineState { + MachineState parent; + struct arm_boot_info bootinfo; + MemoryRegion ram[MPS3R_RAM_MAX]; + Object *cpu[MPS3R_CPU_MAX]; + MemoryRegion cpu_sysmem[MPS3R_CPU_MAX]; + MemoryRegion sysmem_alias[MPS3R_CPU_MAX]; + MemoryRegion cpu_ram[MPS3R_CPU_MAX]; + GICv3State gic; + /* per-CPU UARTs followed by the shared UARTs */ + CMSDKAPBUART uart[MPS3R_CPU_MAX + MPS3R_UART_MAX]; + OrIRQState cpu_uart_oflow[MPS3R_CPU_MAX]; + OrIRQState uart_oflow; + CMSDKAPBWatchdog watchdog; + CMSDKAPBDualTimer dualtimer; + ArmSbconI2CState i2c[5]; + PL022State spi[3]; + MPS2SCC scc; + MPS2FPGAIO fpgaio; + UnimplementedDeviceState i2s_audio; + PL031State rtc; + Clock *clk; +}; + +#define TYPE_MPS3R_MACHINE "mps3r" +#define TYPE_MPS3R_AN536_MACHINE MACHINE_TYPE_NAME("mps3-an536") + +OBJECT_DECLARE_TYPE(MPS3RMachineState, MPS3RMachineClass, MPS3R_MACHINE) + +/* + * Main clock frequency CLK in Hz (50MHz). In the image there are also + * ACLK, MCLK, GPUCLK and PERIPHCLK at the same frequency; for our + * model we just roll them all into one. + */ +#define CLK_FRQ 50000000 + +static const RAMInfo an536_raminfo[] = { + { + .name = "ATCM", + .base = 0x00000000, + .size = 0x00008000, + .mrindex = 0, + }, { + /* We model the QSPI flash as simple ROM for now */ + .name = "QSPI", + .base = 0x08000000, + .size = 0x00800000, + .flags = IS_ROM, + .mrindex = 1, + }, { + .name = "BRAM", + .base = 0x10000000, + .size = 0x00080000, + .mrindex = 2, + }, { + .name = "DDR", + .base = 0x20000000, + .size = MPS3_DDR_SIZE, + .mrindex = -1, + }, { + .name = "ATCM0", + .base = 0xee000000, + .size = 0x00008000, + .mrindex = 3, + }, { + .name = "BTCM0", + .base = 0xee100000, + .size = 0x00008000, + .mrindex = 4, + }, { + .name = "CTCM0", + .base = 0xee200000, + .size = 0x00008000, + .mrindex = 5, + }, { + .name = "ATCM1", + .base = 0xee400000, + .size = 0x00008000, + .mrindex = 6, + }, { + .name = "BTCM1", + .base = 0xee500000, + .size = 0x00008000, + .mrindex = 7, + }, { + .name = "CTCM1", + .base = 0xee600000, + .size = 0x00008000, + .mrindex = 8, + }, { + .name = NULL, + } +}; + +static const int an536_oscclk[] = { + 24000000, /* 24MHz reference for RTC and timers */ + 50000000, /* 50MHz ACLK */ + 50000000, /* 50MHz MCLK */ + 50000000, /* 50MHz GPUCLK */ + 24576000, /* 24.576MHz AUDCLK */ + 23750000, /* 23.75MHz HDLCDCLK */ + 100000000, /* 100MHz DDR4_REF_CLK */ +}; + +static MemoryRegion *mr_for_raminfo(MPS3RMachineState *mms, + const RAMInfo *raminfo) +{ + /* Return an initialized MemoryRegion for the RAMInfo. */ + MemoryRegion *ram; + + if (raminfo->mrindex < 0) { + /* Means this RAMInfo is for QEMU's "system memory" */ + MachineState *machine = MACHINE(mms); + assert(!(raminfo->flags & IS_ROM)); + return machine->ram; + } + + assert(raminfo->mrindex < MPS3R_RAM_MAX); + ram = &mms->ram[raminfo->mrindex]; + + memory_region_init_ram(ram, NULL, raminfo->name, + raminfo->size, &error_fatal); + if (raminfo->flags & IS_ROM) { + memory_region_set_readonly(ram, true); + } + return ram; +} + +/* + * There is no defined secondary boot protocol for Linux for the AN536, + * because real hardware has a restriction that atomic operations between + * the two CPUs do not function correctly, and so true SMP is not + * possible. Therefore for cases where the user is directly booting + * a kernel, we treat the system as essentially uniprocessor, and + * put the secondary CPU into power-off state (as if the user on the + * real hardware had configured the secondary to be halted via the + * SCC config registers). + * + * Note that the default secondary boot code would not work here anyway + * as it assumes a GICv2, and we have a GICv3. + */ +static void mps3r_write_secondary_boot(ARMCPU *cpu, + const struct arm_boot_info *info) +{ + /* + * Power the secondary CPU off. This means we don't need to write any + * boot code into guest memory. Note that the 'cpu' argument to this + * function is the primary CPU we passed to arm_load_kernel(), not + * the secondary. Loop around all the other CPUs, as the boot.c + * code does for the "disable secondaries if PSCI is enabled" case. + */ + for (CPUState *cs = first_cpu; cs; cs = CPU_NEXT(cs)) { + if (cs != first_cpu) { + object_property_set_bool(OBJECT(cs), "start-powered-off", true, + &error_abort); + } + } +} + +static void mps3r_secondary_cpu_reset(ARMCPU *cpu, + const struct arm_boot_info *info) +{ + /* We don't need to do anything here because the CPU will be off */ +} + +static void create_gic(MPS3RMachineState *mms, MemoryRegion *sysmem) +{ + MachineState *machine = MACHINE(mms); + DeviceState *gicdev; + QList *redist_region_count; + + object_initialize_child(OBJECT(mms), "gic", &mms->gic, TYPE_ARM_GICV3); + gicdev = DEVICE(&mms->gic); + qdev_prop_set_uint32(gicdev, "num-cpu", machine->smp.cpus); + qdev_prop_set_uint32(gicdev, "num-irq", NUM_SPIS + GIC_INTERNAL); + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, machine->smp.cpus); + qdev_prop_set_array(gicdev, "redist-region-count", redist_region_count); + object_property_set_link(OBJECT(&mms->gic), "sysmem", + OBJECT(sysmem), &error_fatal); + sysbus_realize(SYS_BUS_DEVICE(&mms->gic), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->gic), 0, PERIPHBASE); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->gic), 1, PERIPHBASE + 0x100000); + /* + * 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 (int i = 0; i < machine->smp.cpus; i++) { + DeviceState *cpudev = DEVICE(mms->cpu[i]); + SysBusDevice *gicsbd = SYS_BUS_DEVICE(&mms->gic); + int intidbase = NUM_SPIS + i * GIC_INTERNAL; + int irq; + /* + * Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs used for this board. This isn't a BSA board, + * but it uses the standard convention for the PPI numbers. + */ + const int timer_irq[] = { + [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, + [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, + [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, + }; + + for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { + qdev_connect_gpio_out(cpudev, irq, + qdev_get_gpio_in(gicdev, + intidbase + timer_irq[irq])); + } + + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", 0, + qdev_get_gpio_in(gicdev, + intidbase + ARCH_GIC_MAINT_IRQ)); + + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, + qdev_get_gpio_in(gicdev, + intidbase + VIRTUAL_PMU_IRQ)); + + sysbus_connect_irq(gicsbd, i, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(gicsbd, i + machine->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(gicsbd, i + 2 * machine->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(gicsbd, i + 3 * machine->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + } +} + +/* + * Create UART uartno, and map it into the MemoryRegion mem at address baseaddr. + * The qemu_irq arguments are where we connect the various IRQs from the UART. + */ +static void create_uart(MPS3RMachineState *mms, int uartno, MemoryRegion *mem, + hwaddr baseaddr, qemu_irq txirq, qemu_irq rxirq, + qemu_irq txoverirq, qemu_irq rxoverirq, + qemu_irq combirq) +{ + g_autofree char *s = g_strdup_printf("uart%d", uartno); + SysBusDevice *sbd; + + assert(uartno < ARRAY_SIZE(mms->uart)); + object_initialize_child(OBJECT(mms), s, &mms->uart[uartno], + TYPE_CMSDK_APB_UART); + qdev_prop_set_uint32(DEVICE(&mms->uart[uartno]), "pclk-frq", CLK_FRQ); + qdev_prop_set_chr(DEVICE(&mms->uart[uartno]), "chardev", serial_hd(uartno)); + sbd = SYS_BUS_DEVICE(&mms->uart[uartno]); + sysbus_realize(sbd, &error_fatal); + memory_region_add_subregion(mem, baseaddr, + sysbus_mmio_get_region(sbd, 0)); + sysbus_connect_irq(sbd, 0, txirq); + sysbus_connect_irq(sbd, 1, rxirq); + sysbus_connect_irq(sbd, 2, txoverirq); + sysbus_connect_irq(sbd, 3, rxoverirq); + sysbus_connect_irq(sbd, 4, combirq); +} + +static void mps3r_common_init(MachineState *machine) +{ + MPS3RMachineState *mms = MPS3R_MACHINE(machine); + MPS3RMachineClass *mmc = MPS3R_MACHINE_GET_CLASS(mms); + MemoryRegion *sysmem = get_system_memory(); + DeviceState *gicdev; + QList *oscclk; + + mms->clk = clock_new(OBJECT(machine), "CLK"); + clock_set_hz(mms->clk, CLK_FRQ); + + for (const RAMInfo *ri = mmc->raminfo; ri->name; ri++) { + MemoryRegion *mr = mr_for_raminfo(mms, ri); + memory_region_add_subregion(sysmem, ri->base, mr); + } + + assert(machine->smp.cpus <= MPS3R_CPU_MAX); + for (int i = 0; i < machine->smp.cpus; i++) { + g_autofree char *sysmem_name = g_strdup_printf("cpu-%d-memory", i); + g_autofree char *ramname = g_strdup_printf("cpu-%d-memory", i); + g_autofree char *alias_name = g_strdup_printf("sysmem-alias-%d", i); + + /* + * Each CPU has some private RAM/peripherals, so create the container + * which will house those, with the whole-machine system memory being + * used where there's no CPU-specific device. Note that we need the + * sysmem_alias aliases because we can't put one MR (the original + * 'sysmem') into more than one other MR. + */ + memory_region_init(&mms->cpu_sysmem[i], OBJECT(machine), + sysmem_name, UINT64_MAX); + memory_region_init_alias(&mms->sysmem_alias[i], OBJECT(machine), + alias_name, sysmem, 0, UINT64_MAX); + memory_region_add_subregion_overlap(&mms->cpu_sysmem[i], 0, + &mms->sysmem_alias[i], -1); + + mms->cpu[i] = object_new(machine->cpu_type); + object_property_set_link(mms->cpu[i], "memory", + OBJECT(&mms->cpu_sysmem[i]), &error_abort); + object_property_set_int(mms->cpu[i], "reset-cbar", + PERIPHBASE, &error_abort); + qdev_realize(DEVICE(mms->cpu[i]), NULL, &error_fatal); + object_unref(mms->cpu[i]); + + /* Per-CPU RAM */ + memory_region_init_ram(&mms->cpu_ram[i], NULL, ramname, + 0x1000, &error_fatal); + memory_region_add_subregion(&mms->cpu_sysmem[i], 0xe7c01000, + &mms->cpu_ram[i]); + } + + create_gic(mms, sysmem); + gicdev = DEVICE(&mms->gic); + + /* + * UARTs 0 and 1 are per-CPU; their interrupts are wired to + * the relevant CPU's PPI 0..3, aka INTID 16..19 + */ + for (int i = 0; i < machine->smp.cpus; i++) { + int intidbase = NUM_SPIS + i * GIC_INTERNAL; + g_autofree char *s = g_strdup_printf("cpu-uart-oflow-orgate%d", i); + DeviceState *orgate; + + /* The two overflow IRQs from the UART are ORed together into PPI 3 */ + object_initialize_child(OBJECT(mms), s, &mms->cpu_uart_oflow[i], + TYPE_OR_IRQ); + orgate = DEVICE(&mms->cpu_uart_oflow[i]); + qdev_prop_set_uint32(orgate, "num-lines", 2); + qdev_realize(orgate, NULL, &error_fatal); + qdev_connect_gpio_out(orgate, 0, + qdev_get_gpio_in(gicdev, intidbase + 19)); + + create_uart(mms, i, &mms->cpu_sysmem[i], 0xe7c00000, + qdev_get_gpio_in(gicdev, intidbase + 17), /* tx */ + qdev_get_gpio_in(gicdev, intidbase + 16), /* rx */ + qdev_get_gpio_in(orgate, 0), /* txover */ + qdev_get_gpio_in(orgate, 1), /* rxover */ + qdev_get_gpio_in(gicdev, intidbase + 18) /* combined */); + } + /* + * UARTs 2 to 5 are whole-system; all overflow IRQs are ORed + * together into IRQ 17 + */ + object_initialize_child(OBJECT(mms), "uart-oflow-orgate", + &mms->uart_oflow, TYPE_OR_IRQ); + qdev_prop_set_uint32(DEVICE(&mms->uart_oflow), "num-lines", + MPS3R_UART_MAX * 2); + qdev_realize(DEVICE(&mms->uart_oflow), NULL, &error_fatal); + qdev_connect_gpio_out(DEVICE(&mms->uart_oflow), 0, + qdev_get_gpio_in(gicdev, 17)); + + for (int i = 0; i < MPS3R_UART_MAX; i++) { + hwaddr baseaddr = 0xe0205000 + i * 0x1000; + int rxirq = 5 + i * 2, txirq = 6 + i * 2, combirq = 13 + i; + + create_uart(mms, i + MPS3R_CPU_MAX, sysmem, baseaddr, + qdev_get_gpio_in(gicdev, txirq), + qdev_get_gpio_in(gicdev, rxirq), + qdev_get_gpio_in(DEVICE(&mms->uart_oflow), i * 2), + qdev_get_gpio_in(DEVICE(&mms->uart_oflow), i * 2 + 1), + qdev_get_gpio_in(gicdev, combirq)); + } + + for (int i = 0; i < 4; i++) { + /* CMSDK GPIO controllers */ + g_autofree char *s = g_strdup_printf("gpio%d", i); + create_unimplemented_device(s, 0xe0000000 + i * 0x1000, 0x1000); + } + + object_initialize_child(OBJECT(mms), "watchdog", &mms->watchdog, + TYPE_CMSDK_APB_WATCHDOG); + qdev_connect_clock_in(DEVICE(&mms->watchdog), "WDOGCLK", mms->clk); + sysbus_realize(SYS_BUS_DEVICE(&mms->watchdog), &error_fatal); + sysbus_connect_irq(SYS_BUS_DEVICE(&mms->watchdog), 0, + qdev_get_gpio_in(gicdev, 0)); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->watchdog), 0, 0xe0100000); + + object_initialize_child(OBJECT(mms), "dualtimer", &mms->dualtimer, + TYPE_CMSDK_APB_DUALTIMER); + qdev_connect_clock_in(DEVICE(&mms->dualtimer), "TIMCLK", mms->clk); + sysbus_realize(SYS_BUS_DEVICE(&mms->dualtimer), &error_fatal); + sysbus_connect_irq(SYS_BUS_DEVICE(&mms->dualtimer), 0, + qdev_get_gpio_in(gicdev, 3)); + sysbus_connect_irq(SYS_BUS_DEVICE(&mms->dualtimer), 1, + qdev_get_gpio_in(gicdev, 1)); + sysbus_connect_irq(SYS_BUS_DEVICE(&mms->dualtimer), 2, + qdev_get_gpio_in(gicdev, 2)); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->dualtimer), 0, 0xe0101000); + + for (int i = 0; i < ARRAY_SIZE(mms->i2c); i++) { + static const hwaddr i2cbase[] = {0xe0102000, /* Touch */ + 0xe0103000, /* Audio */ + 0xe0107000, /* Shield0 */ + 0xe0108000, /* Shield1 */ + 0xe0109000}; /* DDR4 EEPROM */ + g_autofree char *s = g_strdup_printf("i2c%d", i); + + object_initialize_child(OBJECT(mms), s, &mms->i2c[i], + TYPE_ARM_SBCON_I2C); + sysbus_realize(SYS_BUS_DEVICE(&mms->i2c[i]), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->i2c[i]), 0, i2cbase[i]); + if (i != 2 && i != 3) { + /* + * internal-only bus: mark it full to avoid user-created + * i2c devices being plugged into it. + */ + qbus_mark_full(qdev_get_child_bus(DEVICE(&mms->i2c[i]), "i2c")); + } + } + + for (int i = 0; i < ARRAY_SIZE(mms->spi); i++) { + g_autofree char *s = g_strdup_printf("spi%d", i); + hwaddr baseaddr = 0xe0104000 + i * 0x1000; + + object_initialize_child(OBJECT(mms), s, &mms->spi[i], TYPE_PL022); + sysbus_realize(SYS_BUS_DEVICE(&mms->spi[i]), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->spi[i]), 0, baseaddr); + sysbus_connect_irq(SYS_BUS_DEVICE(&mms->spi[i]), 0, + qdev_get_gpio_in(gicdev, 22 + i)); + } + + object_initialize_child(OBJECT(mms), "scc", &mms->scc, TYPE_MPS2_SCC); + qdev_prop_set_uint32(DEVICE(&mms->scc), "scc-cfg0", 0); + qdev_prop_set_uint32(DEVICE(&mms->scc), "scc-cfg4", 0x2); + qdev_prop_set_uint32(DEVICE(&mms->scc), "scc-aid", 0x00200008); + qdev_prop_set_uint32(DEVICE(&mms->scc), "scc-id", 0x41055360); + oscclk = qlist_new(); + for (int i = 0; i < ARRAY_SIZE(an536_oscclk); i++) { + qlist_append_int(oscclk, an536_oscclk[i]); + } + qdev_prop_set_array(DEVICE(&mms->scc), "oscclk", oscclk); + sysbus_realize(SYS_BUS_DEVICE(&mms->scc), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->scc), 0, 0xe0200000); + + create_unimplemented_device("i2s-audio", 0xe0201000, 0x1000); + + object_initialize_child(OBJECT(mms), "fpgaio", &mms->fpgaio, + TYPE_MPS2_FPGAIO); + qdev_prop_set_uint32(DEVICE(&mms->fpgaio), "prescale-clk", an536_oscclk[1]); + qdev_prop_set_uint32(DEVICE(&mms->fpgaio), "num-leds", 10); + qdev_prop_set_bit(DEVICE(&mms->fpgaio), "has-switches", true); + qdev_prop_set_bit(DEVICE(&mms->fpgaio), "has-dbgctrl", false); + sysbus_realize(SYS_BUS_DEVICE(&mms->fpgaio), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->fpgaio), 0, 0xe0202000); + + create_unimplemented_device("clcd", 0xe0209000, 0x1000); + + object_initialize_child(OBJECT(mms), "rtc", &mms->rtc, TYPE_PL031); + sysbus_realize(SYS_BUS_DEVICE(&mms->rtc), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->rtc), 0, 0xe020a000); + sysbus_connect_irq(SYS_BUS_DEVICE(&mms->rtc), 0, + qdev_get_gpio_in(gicdev, 4)); + + /* + * In hardware this is a LAN9220; the LAN9118 is software compatible + * except that it doesn't support the checksum-offload feature. + */ + lan9118_init(0xe0300000, + qdev_get_gpio_in(gicdev, 18)); + + create_unimplemented_device("usb", 0xe0301000, 0x1000); + create_unimplemented_device("qspi-write-config", 0xe0600000, 0x1000); + + mms->bootinfo.ram_size = machine->ram_size; + mms->bootinfo.board_id = -1; + mms->bootinfo.loader_start = mmc->loader_start; + mms->bootinfo.write_secondary_boot = mps3r_write_secondary_boot; + mms->bootinfo.secondary_cpu_reset_hook = mps3r_secondary_cpu_reset; + arm_load_kernel(ARM_CPU(mms->cpu[0]), machine, &mms->bootinfo); +} + +static void mps3r_set_default_ram_info(MPS3RMachineClass *mmc) +{ + /* + * Set mc->default_ram_size and default_ram_id from the + * information in mmc->raminfo. + */ + MachineClass *mc = MACHINE_CLASS(mmc); + const RAMInfo *p; + + for (p = mmc->raminfo; p->name; p++) { + if (p->mrindex < 0) { + /* Found the entry for "system memory" */ + mc->default_ram_size = p->size; + mc->default_ram_id = p->name; + mmc->loader_start = p->base; + return; + } + } + g_assert_not_reached(); +} + +static void mps3r_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = mps3r_common_init; +} + +static void mps3r_an536_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + MPS3RMachineClass *mmc = MPS3R_MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-r52"), + NULL + }; + + mc->desc = "ARM MPS3 with AN536 FPGA image for Cortex-R52"; + /* + * In the real FPGA image there are always two cores, but the standard + * initial setting for the SCC SYSCON 0x000 register is 0x21, meaning + * that the second core is held in reset and halted. Many images built for + * the board do not expect the second core to run at startup (especially + * since on the real FPGA image it is not possible to use LDREX/STREX + * in RAM between the two cores, so a true SMP setup isn't supported). + * + * As QEMU's equivalent of this, we support both -smp 1 and -smp 2, + * with the default being -smp 1. This seems a more intuitive UI for + * QEMU users than, for instance, having a machine property to allow + * the user to set the initial value of the SYSCON 0x000 register. + */ + mc->default_cpus = 1; + mc->min_cpus = 1; + mc->max_cpus = 2; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-r52"); + mc->valid_cpu_types = valid_cpu_types; + mmc->raminfo = an536_raminfo; + mps3r_set_default_ram_info(mmc); +} + +static const TypeInfo mps3r_machine_types[] = { + { + .name = TYPE_MPS3R_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .instance_size = sizeof(MPS3RMachineState), + .class_size = sizeof(MPS3RMachineClass), + .class_init = mps3r_class_init, + }, { + .name = TYPE_MPS3R_AN536_MACHINE, + .parent = TYPE_MPS3R_MACHINE, + .class_init = mps3r_an536_class_init, + }, +}; + +DEFINE_TYPES(mps3r_machine_types); diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index b5fe9f364d..a94a10adcc 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -134,7 +134,7 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) armv7m = DEVICE(&s->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 81); - qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type); + qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3")); qdev_prop_set_bit(armv7m, "enable-bitband", true); qdev_connect_clock_in(armv7m, "cpuclk", s->m3clk); qdev_connect_clock_in(armv7m, "refclk", s->refclk); @@ -197,12 +197,8 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) g_free(bus_name); } - /* FIXME use qdev NIC properties instead of nd_table[] */ - if (nd_table[0].used) { - qemu_check_nic_model(&nd_table[0], TYPE_MSS_EMAC); - qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]); - } dev = DEVICE(&s->emac); + qemu_configure_nic_device(dev, true, NULL); object_property_set_link(OBJECT(&s->emac), "ahb-bus", OBJECT(get_system_memory()), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->emac), errp)) { @@ -231,7 +227,6 @@ static Property m2sxxx_soc_properties[] = { * part name specifies the type of SmartFusion2 device variant(this * property is for information purpose only. */ - DEFINE_PROP_STRING("cpu-type", MSF2State, cpu_type), DEFINE_PROP_STRING("part-name", MSF2State, part_name), DEFINE_PROP_UINT64("eNVM-size", MSF2State, envm_size, MSF2_ENVM_MAX_SIZE), DEFINE_PROP_UINT64("eSRAM-size", MSF2State, esram_size, diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c index d9f881690e..5c415abe85 100644 --- a/hw/arm/msf2-som.c +++ b/hw/arm/msf2-som.c @@ -1,6 +1,9 @@ /* * SmartFusion2 SOM starter kit(from Emcraft) emulation. * + * M2S-FG484 SOM hardware architecture specification: + * https://www.emcraft.com/jdownloads/som/m2s/m2s-som-ha.pdf + * * Copyright (c) 2017 Subbaraya Sundeep * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -44,7 +47,6 @@ static void emcraft_sf2_s2s010_init(MachineState *machine) DeviceState *dev; DeviceState *spi_flash; MSF2State *soc; - MachineClass *mc = MACHINE_GET_CLASS(machine); DriveInfo *dinfo = drive_get(IF_MTD, 0, 0); qemu_irq cs_line; BusState *spi_bus; @@ -52,20 +54,13 @@ static void emcraft_sf2_s2s010_init(MachineState *machine) MemoryRegion *ddr = g_new(MemoryRegion, 1); Clock *m3clk; - if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) { - error_report("This board can only be used with CPU %s", - mc->default_cpu_type); - exit(1); - } - memory_region_init_ram(ddr, NULL, "ddr-ram", DDR_SIZE, &error_fatal); memory_region_add_subregion(sysmem, DDR_BASE_ADDRESS, ddr); dev = qdev_new(TYPE_MSF2_SOC); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); qdev_prop_set_string(dev, "part-name", "M2S010"); - qdev_prop_set_string(dev, "cpu-type", mc->default_cpu_type); - qdev_prop_set_uint64(dev, "eNVM-size", M2S010_ENVM_SIZE); qdev_prop_set_uint64(dev, "eSRAM-size", M2S010_ESRAM_SIZE); @@ -87,7 +82,7 @@ static void emcraft_sf2_s2s010_init(MachineState *machine) /* Attach SPI flash to SPI0 controller */ spi_bus = qdev_get_child_bus(dev, "spi0"); - spi_flash = qdev_new("s25sl12801"); + spi_flash = qdev_new("s25sl12801"); /* Spansion S25FL128SDPBHICO */ qdev_prop_set_uint8(spi_flash, "spansion-cr2nv", 1); if (dinfo) { qdev_prop_set_drive_err(spi_flash, "drive", @@ -98,14 +93,19 @@ static void emcraft_sf2_s2s010_init(MachineState *machine) sysbus_connect_irq(SYS_BUS_DEVICE(&soc->spi[0]), 1, cs_line); armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, - soc->envm_size); + 0, soc->envm_size); } static void emcraft_sf2_machine_init(MachineClass *mc) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m3"), + NULL + }; + mc->desc = "SmartFusion2 SOM kit from Emcraft (M2S010)"; mc->init = emcraft_sf2_s2s010_init; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); + mc->valid_cpu_types = valid_cpu_types; } DEFINE_MACHINE("emcraft-sf2", emcraft_sf2_machine_init) diff --git a/hw/arm/musca.c b/hw/arm/musca.c index 7a83f7dda7..e2c9d49af5 100644 --- a/hw/arm/musca.c +++ b/hw/arm/musca.c @@ -355,7 +355,6 @@ static void musca_init(MachineState *machine) { MuscaMachineState *mms = MUSCA_MACHINE(machine); MuscaMachineClass *mmc = MUSCA_MACHINE_GET_CLASS(mms); - MachineClass *mc = MACHINE_GET_CLASS(machine); MemoryRegion *system_memory = get_system_memory(); DeviceState *ssedev; DeviceState *dev_splitter; @@ -366,12 +365,6 @@ static void musca_init(MachineState *machine) assert(mmc->num_irqs <= MUSCA_NUMIRQ_MAX); assert(mmc->num_mpcs <= MUSCA_MPC_MAX); - if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) { - error_report("This board can only be used with CPU %s", - mc->default_cpu_type); - exit(1); - } - mms->sysclk = clock_new(OBJECT(machine), "SYSCLK"); clock_set_hz(mms->sysclk, SYSCLK_FRQ); mms->s32kclk = clock_new(OBJECT(machine), "S32KCLK"); @@ -597,17 +590,22 @@ static void musca_init(MachineState *machine) "cfg_sec_resp", 0)); } - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x2000000); + armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + 0, 0x2000000); } static void musca_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m33"), + NULL + }; mc->default_cpus = 2; mc->min_cpus = mc->default_cpus; mc->max_cpus = mc->default_cpus; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); + mc->valid_cpu_types = valid_cpu_types; mc->init = musca_init; } diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index b65c020115..2020f73a57 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -10,8 +10,8 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" -#include "cpu.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "hw/arm/boot.h" @@ -25,6 +25,7 @@ #include "hw/block/flash.h" #include "ui/console.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/bitbang_i2c.h" #include "hw/irq.h" #include "hw/or-irq.h" #include "hw/audio/wm8750.h" @@ -35,6 +36,9 @@ #include "qemu/cutils.h" #include "qom/object.h" #include "hw/net/mv88w8618_eth.h" +#include "audio/audio.h" +#include "qemu/error-report.h" +#include "target/arm/cpu-qom.h" #define MP_MISC_BASE 0x80002000 #define MP_MISC_SIZE 0x00001000 @@ -96,7 +100,7 @@ #define MP_LCD_SPI_CMD 0x00104011 #define MP_LCD_SPI_INVALID 0x00000000 -/* Commmands */ +/* Commands */ #define MP_LCD_INST_SETPAGE0 0xB0 /* ... */ #define MP_LCD_INST_SETPAGE7 0xB7 @@ -271,7 +275,7 @@ static const VMStateDescription musicpal_lcd_vmsd = { .name = "musicpal_lcd", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(brightness, musicpal_lcd_state), VMSTATE_UINT32(mode, musicpal_lcd_state), VMSTATE_UINT32(irqctrl, musicpal_lcd_state), @@ -396,7 +400,7 @@ static const VMStateDescription mv88w8618_pic_vmsd = { .name = "mv88w8618_pic", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(level, mv88w8618_pic_state), VMSTATE_UINT32(enabled, mv88w8618_pic_state), VMSTATE_END_OF_LIST() @@ -579,7 +583,7 @@ static const VMStateDescription mv88w8618_timer_vmsd = { .name = "timer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PTIMER(ptimer, mv88w8618_timer_state), VMSTATE_UINT32(limit, mv88w8618_timer_state), VMSTATE_END_OF_LIST() @@ -590,7 +594,7 @@ static const VMStateDescription mv88w8618_pit_vmsd = { .name = "mv88w8618_pit", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1, mv88w8618_timer_vmsd, mv88w8618_timer_state), VMSTATE_END_OF_LIST() @@ -677,7 +681,7 @@ static const VMStateDescription mv88w8618_flashcfg_vmsd = { .name = "mv88w8618_flashcfg", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state), VMSTATE_END_OF_LIST() } @@ -1011,7 +1015,7 @@ static const VMStateDescription musicpal_gpio_vmsd = { .name = "musicpal_gpio", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state), VMSTATE_UINT32(out_state, musicpal_gpio_state), VMSTATE_UINT32(in_state, musicpal_gpio_state), @@ -1039,20 +1043,6 @@ static const TypeInfo musicpal_gpio_info = { }; /* Keyboard codes & masks */ -#define KEY_RELEASED 0x80 -#define KEY_CODE 0x7f - -#define KEYCODE_TAB 0x0f -#define KEYCODE_ENTER 0x1c -#define KEYCODE_F 0x21 -#define KEYCODE_M 0x32 - -#define KEYCODE_EXTENDED 0xe0 -#define KEYCODE_UP 0x48 -#define KEYCODE_DOWN 0x50 -#define KEYCODE_LEFT 0x4b -#define KEYCODE_RIGHT 0x4d - #define MP_KEY_WHEEL_VOL (1 << 0) #define MP_KEY_WHEEL_VOL_INV (1 << 1) #define MP_KEY_WHEEL_NAV (1 << 2) @@ -1070,68 +1060,66 @@ struct musicpal_key_state { SysBusDevice parent_obj; /*< public >*/ - MemoryRegion iomem; - uint32_t kbd_extended; uint32_t pressed_keys; qemu_irq out[8]; }; -static void musicpal_key_event(void *opaque, int keycode) +static void musicpal_key_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) { - musicpal_key_state *s = opaque; + musicpal_key_state *s = MUSICPAL_KEY(dev); + InputKeyEvent *key = evt->u.key.data; + int qcode = qemu_input_key_value_to_qcode(key->key); uint32_t event = 0; int i; - if (keycode == KEYCODE_EXTENDED) { - s->kbd_extended = 1; - return; + switch (qcode) { + case Q_KEY_CODE_UP: + event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV; + break; + + case Q_KEY_CODE_DOWN: + event = MP_KEY_WHEEL_NAV; + break; + + case Q_KEY_CODE_LEFT: + event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV; + break; + + case Q_KEY_CODE_RIGHT: + event = MP_KEY_WHEEL_VOL; + break; + + case Q_KEY_CODE_F: + event = MP_KEY_BTN_FAVORITS; + break; + + case Q_KEY_CODE_TAB: + event = MP_KEY_BTN_VOLUME; + break; + + case Q_KEY_CODE_RET: + event = MP_KEY_BTN_NAVIGATION; + break; + + case Q_KEY_CODE_M: + event = MP_KEY_BTN_MENU; + break; } - if (s->kbd_extended) { - switch (keycode & KEY_CODE) { - case KEYCODE_UP: - event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV; - break; - - case KEYCODE_DOWN: - event = MP_KEY_WHEEL_NAV; - break; - - case KEYCODE_LEFT: - event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV; - break; - - case KEYCODE_RIGHT: - event = MP_KEY_WHEEL_VOL; - break; - } - } else { - switch (keycode & KEY_CODE) { - case KEYCODE_F: - event = MP_KEY_BTN_FAVORITS; - break; - - case KEYCODE_TAB: - event = MP_KEY_BTN_VOLUME; - break; - - case KEYCODE_ENTER: - event = MP_KEY_BTN_NAVIGATION; - break; - - case KEYCODE_M: - event = MP_KEY_BTN_MENU; - break; - } - /* Do not repeat already pressed buttons */ - if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { + /* + * We allow repeated wheel-events when the arrow keys are held down, + * but do not repeat already-pressed buttons for the other key inputs. + */ + if (!(event & (MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_VOL))) { + if (key->down && (s->pressed_keys & event)) { event = 0; } } if (event) { /* Raise GPIO pin first if repeating a key */ - if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { + if (key->down && (s->pressed_keys & event)) { for (i = 0; i <= 7; i++) { if (event & (1 << i)) { qemu_set_irq(s->out[i], 1); @@ -1140,17 +1128,15 @@ static void musicpal_key_event(void *opaque, int keycode) } for (i = 0; i <= 7; i++) { if (event & (1 << i)) { - qemu_set_irq(s->out[i], !!(keycode & KEY_RELEASED)); + qemu_set_irq(s->out[i], !key->down); } } - if (keycode & KEY_RELEASED) { - s->pressed_keys &= ~event; - } else { + if (key->down) { s->pressed_keys |= event; + } else { + s->pressed_keys &= ~event; } } - - s->kbd_extended = 0; } static void musicpal_key_init(Object *obj) @@ -1159,23 +1145,27 @@ static void musicpal_key_init(Object *obj) DeviceState *dev = DEVICE(sbd); musicpal_key_state *s = MUSICPAL_KEY(dev); - memory_region_init(&s->iomem, obj, "dummy", 0); - sysbus_init_mmio(sbd, &s->iomem); - - s->kbd_extended = 0; s->pressed_keys = 0; qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); +} - qemu_add_kbd_event_handler(musicpal_key_event, s); +static const QemuInputHandler musicpal_key_handler = { + .name = "musicpal_key", + .mask = INPUT_EVENT_MASK_KEY, + .event = musicpal_key_event, +}; + +static void musicpal_key_realize(DeviceState *dev, Error **errp) +{ + qemu_input_handler_register(dev, &musicpal_key_handler); } static const VMStateDescription musicpal_key_vmsd = { .name = "musicpal_key", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(kbd_extended, musicpal_key_state), + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { VMSTATE_UINT32(pressed_keys, musicpal_key_state), VMSTATE_END_OF_LIST() } @@ -1186,6 +1176,7 @@ static void musicpal_key_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &musicpal_key_vmsd; + dc->realize = musicpal_key_realize; } static const TypeInfo musicpal_key_info = { @@ -1196,6 +1187,8 @@ static const TypeInfo musicpal_key_info = { .class_init = musicpal_key_class_init, }; +#define FLASH_SECTOR_SIZE (64 * KiB) + static struct arm_boot_info musicpal_binfo = { .loader_start = 0x0, .board_id = 0x20e, @@ -1248,7 +1241,7 @@ static void musicpal_init(MachineState *machine) uart_orgate = DEVICE(object_new(TYPE_OR_IRQ)); object_property_set_int(OBJECT(uart_orgate), "num-lines", 2, &error_fatal); qdev_realize_and_unref(uart_orgate, NULL, &error_fatal); - qdev_connect_gpio_out(DEVICE(uart_orgate), 0, + qdev_connect_gpio_out(uart_orgate, 0, qdev_get_gpio_in(pic, MP_UART_SHARED_IRQ)); serial_mm_init(address_space_mem, MP_UART1_BASE, 2, @@ -1264,8 +1257,8 @@ static void musicpal_init(MachineState *machine) BlockBackend *blk = blk_by_legacy_dinfo(dinfo); flash_size = blk_getlength(blk); - if (flash_size != 8*1024*1024 && flash_size != 16*1024*1024 && - flash_size != 32*1024*1024) { + if (flash_size != 8 * MiB && flash_size != 16 * MiB && + flash_size != 32 * MiB) { error_report("Invalid flash image size"); exit(1); } @@ -1277,16 +1270,15 @@ static void musicpal_init(MachineState *machine) */ pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX, "musicpal.flash", flash_size, - blk, 0x10000, + blk, FLASH_SECTOR_SIZE, MP_FLASH_SIZE_MAX / flash_size, 2, 0x00BF, 0x236D, 0x0000, 0x0000, 0x5555, 0x2AAA, 0); } sysbus_create_simple(TYPE_MV88W8618_FLASHCFG, MP_FLASHCFG_BASE, NULL); - qemu_check_nic_model(&nd_table[0], "mv88w8618"); dev = qdev_new(TYPE_MV88W8618_ETH); - qdev_set_nic_properties(dev, &nd_table[0]); + qemu_configure_nic_device(dev, true, "mv88w8618"); object_property_set_link(OBJECT(dev), "dma-memory", OBJECT(get_system_memory()), &error_fatal); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -1300,7 +1292,7 @@ static void musicpal_init(MachineState *machine) dev = sysbus_create_simple(TYPE_MUSICPAL_GPIO, MP_GPIO_BASE, qdev_get_gpio_in(pic, MP_GPIO_IRQ)); - i2c_dev = sysbus_create_simple("gpio_i2c", -1, NULL); + i2c_dev = sysbus_create_simple(TYPE_GPIO_I2C, -1, NULL); i2c = (I2CBus *)qdev_get_child_bus(i2c_dev, "i2c"); lcd_dev = sysbus_create_simple(TYPE_MUSICPAL_LCD, MP_LCD_BASE, NULL); @@ -1324,7 +1316,12 @@ static void musicpal_init(MachineState *machine) qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15)); } - wm8750_dev = i2c_slave_create_simple(i2c, TYPE_WM8750, MP_WM_ADDR); + wm8750_dev = i2c_slave_new(TYPE_WM8750, MP_WM_ADDR); + if (machine->audiodev) { + qdev_prop_set_string(DEVICE(wm8750_dev), "audiodev", machine->audiodev); + } + i2c_slave_realize_and_unref(wm8750_dev, i2c, &error_abort); + dev = qdev_new(TYPE_MV88W8618_AUDIO); s = SYS_BUS_DEVICE(dev); object_property_set_link(OBJECT(dev), "wm8750", OBJECT(wm8750_dev), @@ -1345,6 +1342,8 @@ static void musicpal_machine_init(MachineClass *mc) mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); mc->default_ram_size = MP_RAM_DEFAULT_SIZE; mc->default_ram_id = "musicpal.ram"; + + machine_add_audiodev_property(mc); } DEFINE_MACHINE("musicpal", musicpal_machine_init) diff --git a/hw/arm/netduino2.c b/hw/arm/netduino2.c index 3365da11bf..8b1a9a2437 100644 --- a/hw/arm/netduino2.c +++ b/hw/arm/netduino2.c @@ -44,18 +44,24 @@ static void netduino2_init(MachineState *machine) clock_set_hz(sysclk, SYSCLK_FRQ); dev = qdev_new(TYPE_STM32F205_SOC); - qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3")); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, - FLASH_SIZE); + 0, FLASH_SIZE); } static void netduino2_machine_init(MachineClass *mc) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m3"), + NULL + }; + mc->desc = "Netduino 2 Machine (Cortex-M3)"; mc->init = netduino2_init; + mc->valid_cpu_types = valid_cpu_types; mc->ignore_memory_transaction_failures = true; } diff --git a/hw/arm/netduinoplus2.c b/hw/arm/netduinoplus2.c index 76cea8e489..bccd100354 100644 --- a/hw/arm/netduinoplus2.c +++ b/hw/arm/netduinoplus2.c @@ -44,19 +44,25 @@ static void netduinoplus2_init(MachineState *machine) clock_set_hz(sysclk, SYSCLK_FRQ); dev = qdev_new(TYPE_STM32F405_SOC); - qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4")); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, - FLASH_SIZE); + 0, FLASH_SIZE); } static void netduinoplus2_machine_init(MachineClass *mc) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m4"), + NULL + }; + mc->desc = "Netduino Plus 2 Machine (Cortex-M4)"; mc->init = netduinoplus2_init; + mc->valid_cpu_types = valid_cpu_types; } DEFINE_MACHINE("netduinoplus2", netduinoplus2_machine_init) diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index d85cc02765..cc68b5d8f1 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "qemu/units.h" #include "sysemu/sysemu.h" +#include "target/arm/cpu-qom.h" /* * This covers the whole MMIO space. We'll use this to catch any MMIO accesses @@ -83,9 +84,13 @@ enum NPCM7xxInterrupt { NPCM7XX_UART1_IRQ, NPCM7XX_UART2_IRQ, NPCM7XX_UART3_IRQ, + NPCM7XX_GMAC1_IRQ = 14, NPCM7XX_EMC1RX_IRQ = 15, NPCM7XX_EMC1TX_IRQ, + NPCM7XX_GMAC2_IRQ, NPCM7XX_MMC_IRQ = 26, + NPCM7XX_PSPI2_IRQ = 28, + NPCM7XX_PSPI1_IRQ = 31, NPCM7XX_TIMER0_IRQ = 32, /* Timer Module 0 */ NPCM7XX_TIMER1_IRQ, NPCM7XX_TIMER2_IRQ, @@ -220,6 +225,18 @@ static const hwaddr npcm7xx_emc_addr[] = { 0xf0826000, }; +/* Register base address for each PSPI Module */ +static const hwaddr npcm7xx_pspi_addr[] = { + 0xf0200000, + 0xf0201000, +}; + +/* Register base address for each GMAC Module */ +static const hwaddr npcm7xx_gmac_addr[] = { + 0xf0802000, + 0xf0804000, +}; + static const struct { hwaddr regs_addr; uint32_t unconnected_pins; @@ -444,6 +461,14 @@ static void npcm7xx_init(Object *obj) object_initialize_child(obj, "emc[*]", &s->emc[i], TYPE_NPCM7XX_EMC); } + for (i = 0; i < ARRAY_SIZE(s->pspi); i++) { + object_initialize_child(obj, "pspi[*]", &s->pspi[i], TYPE_NPCM_PSPI); + } + + for (i = 0; i < ARRAY_SIZE(s->gmac); i++) { + object_initialize_child(obj, "gmac[*]", &s->gmac[i], TYPE_NPCM_GMAC); + } + object_initialize_child(obj, "mmc", &s->mmc, TYPE_NPCM7XX_SDHCI); } @@ -462,7 +487,7 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) /* CPUs */ for (i = 0; i < nc->num_cpus; i++) { object_property_set_int(OBJECT(&s->cpu[i]), "mp-affinity", - arm_cpu_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS), + arm_build_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS), &error_abort); object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar", NPCM7XX_GIC_CPU_IF_ADDR, &error_abort); @@ -643,8 +668,9 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) /* * EMC Modules. Cannot fail. - * The mapping of the device to its netdev backend works as follows: - * emc[i] = nd_table[i] + * Use the available NIC configurations in order, allowing 'emc0' and + * 'emc1' to by used as aliases for the model= parameter to override. + * * This works around the inability to specify the netdev property for the * emc device: it's not pluggable and thus the -device option can't be * used. @@ -652,12 +678,13 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_emc_addr) != ARRAY_SIZE(s->emc)); QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->emc) != 2); for (i = 0; i < ARRAY_SIZE(s->emc); i++) { - s->emc[i].emc_num = i; SysBusDevice *sbd = SYS_BUS_DEVICE(&s->emc[i]); - if (nd_table[i].used) { - qemu_check_nic_model(&nd_table[i], TYPE_NPCM7XX_EMC); - qdev_set_nic_properties(DEVICE(sbd), &nd_table[i]); - } + char alias[6]; + + s->emc[i].emc_num = i; + snprintf(alias, sizeof(alias), "emc%u", i); + qemu_configure_nic_device(DEVICE(sbd), true, alias); + /* * The device exists regardless of whether it's connected to a QEMU * netdev backend. So always instantiate it even if there is no @@ -675,6 +702,30 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(sbd, 1, npcm7xx_irq(s, rx_irq)); } + /* + * GMAC Modules. Cannot fail. + */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_gmac_addr) != ARRAY_SIZE(s->gmac)); + QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->gmac) != 2); + for (i = 0; i < ARRAY_SIZE(s->gmac); i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->gmac[i]); + + qemu_configure_nic_device(DEVICE(sbd), false, NULL); + /* + * The device exists regardless of whether it's connected to a QEMU + * netdev backend. So always instantiate it even if there is no + * backend. + */ + sysbus_realize(sbd, &error_abort); + sysbus_mmio_map(sbd, 0, npcm7xx_gmac_addr[i]); + int irq = i == 0 ? NPCM7XX_GMAC1_IRQ : NPCM7XX_GMAC2_IRQ; + /* + * N.B. The values for the second argument sysbus_connect_irq are + * chosen to match the registration order in npcm7xx_emc_realize. + */ + sysbus_connect_irq(sbd, 0, npcm7xx_irq(s, irq)); + } + /* * Flash Interface Unit (FIU). Can fail if incorrect number of chip selects * specified, but this is a programming error. @@ -715,6 +766,17 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc), 0, npcm7xx_irq(s, NPCM7XX_MMC_IRQ)); + /* PSPI */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_pspi_addr) != ARRAY_SIZE(s->pspi)); + for (i = 0; i < ARRAY_SIZE(s->pspi); i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->pspi[i]); + int irq = (i == 0) ? NPCM7XX_PSPI1_IRQ : NPCM7XX_PSPI2_IRQ; + + sysbus_realize(sbd, &error_abort); + sysbus_mmio_map(sbd, 0, npcm7xx_pspi_addr[i]); + sysbus_connect_irq(sbd, 0, npcm7xx_irq(s, irq)); + } + create_unimplemented_device("npcm7xx.shm", 0xc0001000, 4 * KiB); create_unimplemented_device("npcm7xx.vdmx", 0xe0800000, 4 * KiB); create_unimplemented_device("npcm7xx.pcierc", 0xe1000000, 64 * KiB); @@ -724,12 +786,8 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) create_unimplemented_device("npcm7xx.peci", 0xf0100000, 4 * KiB); create_unimplemented_device("npcm7xx.siox[1]", 0xf0101000, 4 * KiB); create_unimplemented_device("npcm7xx.siox[2]", 0xf0102000, 4 * KiB); - create_unimplemented_device("npcm7xx.pspi1", 0xf0200000, 4 * KiB); - create_unimplemented_device("npcm7xx.pspi2", 0xf0201000, 4 * KiB); create_unimplemented_device("npcm7xx.ahbpci", 0xf0400000, 1 * MiB); create_unimplemented_device("npcm7xx.mcphy", 0xf05f0000, 64 * KiB); - create_unimplemented_device("npcm7xx.gmac1", 0xf0802000, 8 * KiB); - create_unimplemented_device("npcm7xx.gmac2", 0xf0804000, 8 * KiB); create_unimplemented_device("npcm7xx.vcd", 0xf0810000, 64 * KiB); create_unimplemented_device("npcm7xx.ece", 0xf0820000, 8 * KiB); create_unimplemented_device("npcm7xx.vdma", 0xf0822000, 8 * KiB); diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c index 6bc6f5d2fe..e229efb447 100644 --- a/hw/arm/npcm7xx_boards.c +++ b/hw/arm/npcm7xx_boards.c @@ -21,6 +21,7 @@ #include "hw/i2c/i2c_mux_pca954x.h" #include "hw/i2c/smbus_eeprom.h" #include "hw/loader.h" +#include "hw/nvram/eeprom_at24c.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "qapi/error.h" @@ -29,6 +30,8 @@ #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" +#include "qemu/error-report.h" + #define NPCM7XX_POWER_ON_STRAPS_DEFAULT ( \ NPCM7XX_PWRON_STRAP_SPI0F18 | \ @@ -118,15 +121,8 @@ static NPCM7xxState *npcm7xx_create_soc(MachineState *machine, uint32_t hw_straps) { NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_GET_CLASS(machine); - MachineClass *mc = MACHINE_CLASS(nmc); Object *obj; - if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) { - error_report("This board can only be used with %s", - mc->default_cpu_type); - exit(1); - } - obj = object_new_with_props(nmc->soc_type, OBJECT(machine), "soc", &error_abort, NULL); object_property_set_uint(obj, "power-on-straps", hw_straps, &error_abort); @@ -140,17 +136,6 @@ static I2CBus *npcm7xx_i2c_get_bus(NPCM7xxState *soc, uint32_t num) return I2C_BUS(qdev_get_child_bus(DEVICE(&soc->smbus[num]), "i2c-bus")); } -static void at24c_eeprom_init(NPCM7xxState *soc, int bus, uint8_t addr, - uint32_t rsize) -{ - I2CBus *i2c_bus = npcm7xx_i2c_get_bus(soc, bus); - I2CSlave *i2c_dev = i2c_slave_new("at24c-eeprom", addr); - DeviceState *dev = DEVICE(i2c_dev); - - qdev_prop_set_uint32(dev, "rom-size", rsize); - i2c_slave_realize_and_unref(i2c_dev, i2c_bus, &error_abort); -} - static void npcm7xx_init_pwm_splitter(NPCM7xxMachine *machine, NPCM7xxState *soc, const int *fan_counts) { @@ -253,8 +238,8 @@ static void quanta_gsj_i2c_init(NPCM7xxState *soc) i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 3), "tmp105", 0x5c); i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 4), "tmp105", 0x5c); - at24c_eeprom_init(soc, 9, 0x55, 8192); - at24c_eeprom_init(soc, 10, 0x55, 8192); + at24c_eeprom_init(npcm7xx_i2c_get_bus(soc, 9), 0x55, 8192); + at24c_eeprom_init(npcm7xx_i2c_get_bus(soc, 10), 0x55, 8192); /* * i2c-11: @@ -360,7 +345,7 @@ static void kudo_bmc_i2c_init(NPCM7xxState *soc) i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 4), TYPE_PCA9548, 0x77); - at24c_eeprom_init(soc, 4, 0x50, 8192); /* mbfru */ + at24c_eeprom_init(npcm7xx_i2c_get_bus(soc, 4), 0x50, 8192); /* mbfru */ i2c_mux = i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 13), TYPE_PCA9548, 0x77); @@ -371,7 +356,7 @@ static void kudo_bmc_i2c_init(NPCM7xxState *soc) i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 4), "tmp105", 0x48); i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 5), "tmp105", 0x49); - at24c_eeprom_init(soc, 14, 0x55, 8192); /* bmcfru */ + at24c_eeprom_init(npcm7xx_i2c_get_bus(soc, 14), 0x55, 8192); /* bmcfru */ /* TODO: Add remaining i2c devices. */ } @@ -471,12 +456,16 @@ static void npcm7xx_set_soc_type(NPCM7xxMachineClass *nmc, const char *type) static void npcm7xx_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a9"), + NULL + }; mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; mc->default_ram_id = "ram"; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a9"); + mc->valid_cpu_types = valid_cpu_types; } /* diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index 34da0d62f0..ac53441630 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -58,7 +58,6 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) { NRF51State *s = NRF51_SOC(dev_soc); MemoryRegion *mr; - Error *err = NULL; uint8_t i = 0; hwaddr base_addr = 0; @@ -92,10 +91,8 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); - memory_region_init_ram(&s->sram, OBJECT(s), "nrf51.sram", s->sram_size, - &err); - if (err) { - error_propagate(errp, err); + if (!memory_region_init_ram(&s->sram, OBJECT(s), "nrf51.sram", s->sram_size, + errp)) { return; } memory_region_add_subregion(&s->container, NRF51_SRAM_BASE, &s->sram); diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index 692c94ceb4..35364312c7 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -45,6 +45,8 @@ #include "hw/loader.h" #include "hw/sysbus.h" #include "qemu/log.h" +#include "qemu/error-report.h" + /* Nokia N8x0 support */ struct n800_s { @@ -230,13 +232,13 @@ static void n8x0_i2c_setup(struct n800_s *s) } /* Touchscreen and keypad controller */ -static MouseTransformInfo n800_pointercal = { +static const MouseTransformInfo n800_pointercal = { .x = 800, .y = 480, .a = { 14560, -68, -3455208, -39, -9621, 35152972, 65536 }, }; -static MouseTransformInfo n810_pointercal = { +static const MouseTransformInfo n810_pointercal = { .x = 800, .y = 480, .a = { 15041, 148, -4731056, 171, -10238, 35933380, 65536 }, @@ -334,7 +336,7 @@ static void n810_key_event(void *opaque, int keycode) #define M 0 -static int n810_keys[0x80] = { +static const int n810_keys[0x80] = { [0x01] = 16, /* Q */ [0x02] = 37, /* K */ [0x03] = 24, /* O */ @@ -702,7 +704,7 @@ static uint32_t mipid_txrx(void *opaque, uint32_t cmd, int len) static void *mipid_init(void) { - struct mipid_s *s = (struct mipid_s *) g_malloc0(sizeof(*s)); + struct mipid_s *s = g_malloc0(sizeof(*s)); s->id = 0x838f03; mipid_reset(s); @@ -810,7 +812,7 @@ static void n8x0_usb_setup(struct n800_s *s) /* Setup done before the main bootloader starts by some early setup code * - used when we want to run the main bootloader in emulation. This * isn't documented. */ -static uint32_t n800_pinout[104] = { +static const uint32_t n800_pinout[104] = { 0x080f00d8, 0x00d40808, 0x03080808, 0x080800d0, 0x00dc0808, 0x0b0f0f00, 0x080800b4, 0x00c00808, 0x08080808, 0x180800c4, 0x00b80000, 0x08080808, @@ -1060,7 +1062,7 @@ static void n8x0_boot_init(void *opaque) #define OMAP_TAG_CBUS 0x4e03 #define OMAP_TAG_EM_ASIC_BB5 0x4e04 -static struct omap_gpiosw_info_s { +static const struct omap_gpiosw_info_s { const char *name; int line; int type; @@ -1078,7 +1080,7 @@ static struct omap_gpiosw_info_s { "headphone", N8X0_HEADPHONE_GPIO, OMAP_GPIOSW_TYPE_CONNECTION | OMAP_GPIOSW_INVERTED, }, - { NULL } + { /* end of list */ } }, n810_gpiosw_info[] = { { "gps_reset", N810_GPS_RESET_GPIO, @@ -1099,10 +1101,10 @@ static struct omap_gpiosw_info_s { "slide", N810_SLIDE_GPIO, OMAP_GPIOSW_TYPE_COVER | OMAP_GPIOSW_INVERTED, }, - { NULL } + { /* end of list */ } }; -static struct omap_partition_info_s { +static const struct omap_partition_info_s { uint32_t offset; uint32_t size; int mask; @@ -1113,27 +1115,25 @@ static struct omap_partition_info_s { { 0x00080000, 0x00200000, 0x0, "kernel" }, { 0x00280000, 0x00200000, 0x3, "initfs" }, { 0x00480000, 0x0fb80000, 0x3, "rootfs" }, - - { 0, 0, 0, NULL } + { /* end of list */ } }, n810_part_info[] = { { 0x00000000, 0x00020000, 0x3, "bootloader" }, { 0x00020000, 0x00060000, 0x0, "config" }, { 0x00080000, 0x00220000, 0x0, "kernel" }, { 0x002a0000, 0x00400000, 0x0, "initfs" }, { 0x006a0000, 0x0f960000, 0x0, "rootfs" }, - - { 0, 0, 0, NULL } + { /* end of list */ } }; -static uint8_t n8x0_bd_addr[6] = { N8X0_BD_ADDR }; +static const uint8_t n8x0_bd_addr[6] = { N8X0_BD_ADDR }; static int n8x0_atag_setup(void *p, int model) { uint8_t *b; uint16_t *w; uint32_t *l; - struct omap_gpiosw_info_s *gpiosw; - struct omap_partition_info_s *partition; + const struct omap_gpiosw_info_s *gpiosw; + const struct omap_partition_info_s *partition; const char *tag; w = p; @@ -1300,7 +1300,7 @@ static int n810_atag_setup(const struct arm_boot_info *info, void *p) static void n8x0_init(MachineState *machine, struct arm_boot_info *binfo, int model) { - struct n800_s *s = (struct n800_s *) g_malloc0(sizeof(*s)); + struct n800_s *s = g_malloc0(sizeof(*s)); MachineClass *mc = MACHINE_GET_CLASS(machine); if (machine->ram_size != mc->default_ram_size) { @@ -1353,9 +1353,7 @@ static void n8x0_init(MachineState *machine, n8x0_spi_setup(s); n8x0_dss_setup(s); n8x0_cbus_setup(s); - if (machine_usb(machine)) { - n8x0_usb_setup(s); - } + n8x0_usb_setup(s); if (machine->kernel_filename) { /* Or at the linux loader. */ @@ -1432,6 +1430,9 @@ static void n800_class_init(ObjectClass *oc, void *data) /* Actually two chips of 0x4000000 bytes each */ mc->default_ram_size = 0x08000000; mc->default_ram_id = "omap2.dram"; + mc->deprecation_reason = "machine is old and unmaintained"; + + machine_add_audiodev_property(mc); } static const TypeInfo n800_type = { @@ -1452,6 +1453,9 @@ static void n810_class_init(ObjectClass *oc, void *data) /* Actually two chips of 0x4000000 bytes each */ mc->default_ram_size = 0x08000000; mc->default_ram_id = "omap2.dram"; + mc->deprecation_reason = "machine is old and unmaintained"; + + machine_add_audiodev_property(mc); } static const TypeInfo n810_type = { diff --git a/hw/arm/olimex-stm32-h405.c b/hw/arm/olimex-stm32-h405.c new file mode 100644 index 0000000000..4ad7b043be --- /dev/null +++ b/hw/arm/olimex-stm32-h405.c @@ -0,0 +1,74 @@ +/* + * ST STM32VLDISCOVERY machine + * Olimex STM32-H405 machine + * + * Copyright (c) 2022 Felipe Balbi + * + * 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 "hw/qdev-clock.h" +#include "qemu/error-report.h" +#include "hw/arm/stm32f405_soc.h" +#include "hw/arm/boot.h" + +/* olimex-stm32-h405 implementation is derived from netduinoplus2 */ + +/* Main SYSCLK frequency in Hz (168MHz) */ +#define SYSCLK_FRQ 168000000ULL + +static void olimex_stm32_h405_init(MachineState *machine) +{ + DeviceState *dev; + Clock *sysclk; + + /* This clock doesn't need migration because it is fixed-frequency */ + sysclk = clock_new(OBJECT(machine), "SYSCLK"); + clock_set_hz(sysclk, SYSCLK_FRQ); + + dev = qdev_new(TYPE_STM32F405_SOC); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); + qdev_connect_clock_in(dev, "sysclk", sysclk); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + armv7m_load_kernel(ARM_CPU(first_cpu), + machine->kernel_filename, + 0, FLASH_SIZE); +} + +static void olimex_stm32_h405_machine_init(MachineClass *mc) +{ + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m4"), + NULL + }; + + mc->desc = "Olimex STM32-H405 (Cortex-M4)"; + mc->init = olimex_stm32_h405_init; + mc->valid_cpu_types = valid_cpu_types; + + /* SRAM pre-allocated as part of the SoC instantiation */ + mc->default_ram_size = 0; +} + +DEFINE_MACHINE("olimex-stm32-h405", olimex_stm32_h405_machine_init) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index f693faa43e..86ee336e59 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -40,6 +40,7 @@ #include "hw/sysbus.h" #include "qemu/cutils.h" #include "qemu/bcd.h" +#include "target/arm/cpu-qom.h" static inline void omap_log_badwidth(const char *funcname, hwaddr addr, int sz) { @@ -176,7 +177,7 @@ static void omap_timer_fire(void *opaque) static void omap_timer_tick(void *opaque) { - struct omap_mpu_timer_s *timer = (struct omap_mpu_timer_s *) opaque; + struct omap_mpu_timer_s *timer = opaque; omap_timer_sync(timer); omap_timer_fire(timer); @@ -185,7 +186,7 @@ static void omap_timer_tick(void *opaque) static void omap_timer_clk_update(void *opaque, int line, int on) { - struct omap_mpu_timer_s *timer = (struct omap_mpu_timer_s *) opaque; + struct omap_mpu_timer_s *timer = opaque; omap_timer_sync(timer); timer->rate = on ? omap_clk_getrate(timer->clk) : 0; @@ -202,7 +203,7 @@ static void omap_timer_clk_setup(struct omap_mpu_timer_s *timer) static uint64_t omap_mpu_timer_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *) opaque; + struct omap_mpu_timer_s *s = opaque; if (size != 4) { return omap_badwidth_read32(opaque, addr); @@ -226,7 +227,7 @@ static uint64_t omap_mpu_timer_read(void *opaque, hwaddr addr, static void omap_mpu_timer_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *) opaque; + struct omap_mpu_timer_s *s = opaque; if (size != 4) { omap_badwidth_write32(opaque, addr, value); @@ -308,7 +309,7 @@ struct omap_watchdog_timer_s { static uint64_t omap_wd_timer_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *) opaque; + struct omap_watchdog_timer_s *s = opaque; if (size != 2) { return omap_badwidth_read16(opaque, addr); @@ -333,7 +334,7 @@ static uint64_t omap_wd_timer_read(void *opaque, hwaddr addr, static void omap_wd_timer_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *) opaque; + struct omap_watchdog_timer_s *s = opaque; if (size != 2) { omap_badwidth_write16(opaque, addr, value); @@ -431,7 +432,7 @@ struct omap_32khz_timer_s { static uint64_t omap_os_timer_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *) opaque; + struct omap_32khz_timer_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; if (size != 4) { @@ -458,7 +459,7 @@ static uint64_t omap_os_timer_read(void *opaque, hwaddr addr, static void omap_os_timer_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *) opaque; + struct omap_32khz_timer_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; if (size != 4) { @@ -532,7 +533,7 @@ static struct omap_32khz_timer_s *omap_os_timer_init(MemoryRegion *memory, static uint64_t omap_ulpd_pm_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *s = opaque; uint16_t ret; if (size != 2) { @@ -600,7 +601,7 @@ static inline void omap_ulpd_req_update(struct omap_mpu_state_s *s, static void omap_ulpd_pm_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *s = opaque; int64_t now, ticks; int div, mult; static const int bypass_div[4] = { 1, 2, 4, 4 }; @@ -765,7 +766,7 @@ static void omap_ulpd_pm_init(MemoryRegion *system_memory, static uint64_t omap_pin_cfg_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *s = opaque; if (size != 4) { return omap_badwidth_read32(opaque, addr); @@ -876,7 +877,7 @@ static inline void omap_pin_modconf1_update(struct omap_mpu_state_s *s, static void omap_pin_cfg_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *s = opaque; uint32_t diff; if (size != 4) { @@ -988,7 +989,7 @@ static void omap_pin_cfg_init(MemoryRegion *system_memory, static uint64_t omap_id_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *s = opaque; if (size != 4) { return omap_badwidth_read32(opaque, addr); @@ -1070,7 +1071,7 @@ static void omap_id_init(MemoryRegion *memory, struct omap_mpu_state_s *mpu) static uint64_t omap_mpui_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *s = opaque; if (size != 4) { return omap_badwidth_read32(opaque, addr); @@ -1103,7 +1104,7 @@ static uint64_t omap_mpui_read(void *opaque, hwaddr addr, static void omap_mpui_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *s = opaque; if (size != 4) { omap_badwidth_write32(opaque, addr, value); @@ -1168,7 +1169,7 @@ struct omap_tipb_bridge_s { static uint64_t omap_tipb_bridge_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *) opaque; + struct omap_tipb_bridge_s *s = opaque; if (size < 2) { return omap_badwidth_read16(opaque, addr); @@ -1198,7 +1199,7 @@ static uint64_t omap_tipb_bridge_read(void *opaque, hwaddr addr, static void omap_tipb_bridge_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *) opaque; + struct omap_tipb_bridge_s *s = opaque; if (size < 2) { omap_badwidth_write16(opaque, addr, value); @@ -1269,7 +1270,7 @@ static struct omap_tipb_bridge_s *omap_tipb_bridge_init( static uint64_t omap_tcmi_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *s = opaque; uint32_t ret; if (size != 4) { @@ -1307,7 +1308,7 @@ static uint64_t omap_tcmi_read(void *opaque, hwaddr addr, static void omap_tcmi_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *s = opaque; if (size != 4) { omap_badwidth_write32(opaque, addr, value); @@ -1384,7 +1385,7 @@ struct dpll_ctl_s { static uint64_t omap_dpll_read(void *opaque, hwaddr addr, unsigned size) { - struct dpll_ctl_s *s = (struct dpll_ctl_s *) opaque; + struct dpll_ctl_s *s = opaque; if (size != 2) { return omap_badwidth_read16(opaque, addr); @@ -1400,7 +1401,7 @@ static uint64_t omap_dpll_read(void *opaque, hwaddr addr, static void omap_dpll_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct dpll_ctl_s *s = (struct dpll_ctl_s *) opaque; + struct dpll_ctl_s *s = opaque; uint16_t diff; static const int bypass_div[4] = { 1, 2, 4, 4 }; int div, mult; @@ -1464,7 +1465,7 @@ static struct dpll_ctl_s *omap_dpll_init(MemoryRegion *memory, static uint64_t omap_clkm_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *s = opaque; if (size != 2) { return omap_badwidth_read16(opaque, addr); @@ -1668,7 +1669,7 @@ static inline void omap_clkm_ckout1_update(struct omap_mpu_state_s *s, static void omap_clkm_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *s = opaque; uint16_t diff; omap_clk clk; static const char *clkschemename[8] = { @@ -1756,7 +1757,7 @@ static const MemoryRegionOps omap_clkm_ops = { static uint64_t omap_clkdsp_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *s = opaque; CPUState *cpu = CPU(s->cpu); if (size != 2) { @@ -1801,7 +1802,7 @@ static inline void omap_clkdsp_idlect2_update(struct omap_mpu_state_s *s, static void omap_clkdsp_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *s = opaque; uint16_t diff; if (size != 2) { @@ -1911,7 +1912,7 @@ struct omap_mpuio_s { static void omap_mpuio_set(void *opaque, int line, int level) { - struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque; + struct omap_mpuio_s *s = opaque; uint16_t prev = s->inputs; if (level) @@ -1947,7 +1948,7 @@ static void omap_mpuio_kbd_update(struct omap_mpuio_s *s) static uint64_t omap_mpuio_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque; + struct omap_mpuio_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; uint16_t ret; @@ -2007,7 +2008,7 @@ static uint64_t omap_mpuio_read(void *opaque, hwaddr addr, static void omap_mpuio_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque; + struct omap_mpuio_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; uint16_t diff; int ln; @@ -2104,7 +2105,7 @@ static void omap_mpuio_reset(struct omap_mpuio_s *s) static void omap_mpuio_onoff(void *opaque, int line, int on) { - struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque; + struct omap_mpuio_s *s = opaque; s->clk = on; if (on) @@ -2198,10 +2199,9 @@ static void omap_uwire_transfer_start(struct omap_uwire_s *s) } } -static uint64_t omap_uwire_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_uwire_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_uwire_s *s = (struct omap_uwire_s *) opaque; + struct omap_uwire_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; if (size != 2) { @@ -2235,7 +2235,7 @@ static uint64_t omap_uwire_read(void *opaque, hwaddr addr, static void omap_uwire_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_uwire_s *s = (struct omap_uwire_s *) opaque; + struct omap_uwire_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; if (size != 2) { @@ -2351,10 +2351,9 @@ static void omap_pwl_update(struct omap_pwl_s *s) } } -static uint64_t omap_pwl_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_pwl_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_pwl_s *s = (struct omap_pwl_s *) opaque; + struct omap_pwl_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; if (size != 1) { @@ -2374,7 +2373,7 @@ static uint64_t omap_pwl_read(void *opaque, hwaddr addr, static void omap_pwl_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_pwl_s *s = (struct omap_pwl_s *) opaque; + struct omap_pwl_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; if (size != 1) { @@ -2414,7 +2413,7 @@ static void omap_pwl_reset(struct omap_pwl_s *s) static void omap_pwl_clk_update(void *opaque, int line, int on) { - struct omap_pwl_s *s = (struct omap_pwl_s *) opaque; + struct omap_pwl_s *s = opaque; s->clk = on; omap_pwl_update(s); @@ -2445,10 +2444,9 @@ struct omap_pwt_s { omap_clk clk; }; -static uint64_t omap_pwt_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_pwt_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_pwt_s *s = (struct omap_pwt_s *) opaque; + struct omap_pwt_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; if (size != 1) { @@ -2470,7 +2468,7 @@ static uint64_t omap_pwt_read(void *opaque, hwaddr addr, static void omap_pwt_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_pwt_s *s = (struct omap_pwt_s *) opaque; + struct omap_pwt_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; if (size != 1) { @@ -2577,10 +2575,9 @@ static void omap_rtc_alarm_update(struct omap_rtc_s *s) printf("%s: conversion failed\n", __func__); } -static uint64_t omap_rtc_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_rtc_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_rtc_s *s = (struct omap_rtc_s *) opaque; + struct omap_rtc_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; uint8_t i; @@ -2662,7 +2659,7 @@ static uint64_t omap_rtc_read(void *opaque, hwaddr addr, static void omap_rtc_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_rtc_s *s = (struct omap_rtc_s *) opaque; + struct omap_rtc_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; struct tm new_tm; time_t ti[2]; @@ -3034,7 +3031,7 @@ static void omap_mcbsp_rx_newdata(struct omap_mcbsp_s *s) static void omap_mcbsp_source_tick(void *opaque) { - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; + struct omap_mcbsp_s *s = opaque; static const int bps[8] = { 0, 1, 1, 2, 2, 2, -255, -255 }; if (!s->rx_rate) @@ -3080,7 +3077,7 @@ static void omap_mcbsp_tx_newdata(struct omap_mcbsp_s *s) static void omap_mcbsp_sink_tick(void *opaque) { - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; + struct omap_mcbsp_s *s = opaque; static const int bps[8] = { 0, 1, 1, 2, 2, 2, -255, -255 }; if (!s->tx_rate) @@ -3173,7 +3170,7 @@ static void omap_mcbsp_req_update(struct omap_mcbsp_s *s) static uint64_t omap_mcbsp_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; + struct omap_mcbsp_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; uint16_t ret; @@ -3271,7 +3268,7 @@ static uint64_t omap_mcbsp_read(void *opaque, hwaddr addr, static void omap_mcbsp_writeh(void *opaque, hwaddr addr, uint32_t value) { - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; + struct omap_mcbsp_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; switch (offset) { @@ -3407,7 +3404,7 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr, static void omap_mcbsp_writew(void *opaque, hwaddr addr, uint32_t value) { - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; + struct omap_mcbsp_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; if (offset == 0x04) { /* DXR */ @@ -3498,7 +3495,7 @@ static struct omap_mcbsp_s *omap_mcbsp_init(MemoryRegion *system_memory, static void omap_mcbsp_i2s_swallow(void *opaque, int line, int level) { - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; + struct omap_mcbsp_s *s = opaque; if (s->rx_rate) { s->rx_req = s->codec->in.len; @@ -3508,7 +3505,7 @@ static void omap_mcbsp_i2s_swallow(void *opaque, int line, int level) static void omap_mcbsp_i2s_start(void *opaque, int line, int level) { - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; + struct omap_mcbsp_s *s = opaque; if (s->tx_rate) { s->tx_req = s->codec->out.size; @@ -3590,10 +3587,9 @@ static void omap_lpg_reset(struct omap_lpg_s *s) omap_lpg_update(s); } -static uint64_t omap_lpg_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_lpg_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_lpg_s *s = (struct omap_lpg_s *) opaque; + struct omap_lpg_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; if (size != 1) { @@ -3615,7 +3611,7 @@ static uint64_t omap_lpg_read(void *opaque, hwaddr addr, static void omap_lpg_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_lpg_s *s = (struct omap_lpg_s *) opaque; + struct omap_lpg_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; if (size != 1) { @@ -3650,7 +3646,7 @@ static const MemoryRegionOps omap_lpg_ops = { static void omap_lpg_clk_update(void *opaque, int line, int on) { - struct omap_lpg_s *s = (struct omap_lpg_s *) opaque; + struct omap_lpg_s *s = opaque; s->clk = on; omap_lpg_update(s); @@ -3713,7 +3709,7 @@ static void omap_setup_mpui_io(MemoryRegion *system_memory, /* General chip reset */ static void omap1_mpu_reset(void *opaque) { - struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *mpu = opaque; omap_dma_reset(mpu->dma); omap_mpu_timer_reset(mpu->timer[0]); @@ -3793,7 +3789,7 @@ static void omap_setup_dsp_mapping(MemoryRegion *system_memory, void omap_mpu_wakeup(void *opaque, int irq, int req) { - struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *mpu = opaque; CPUState *cpu = CPU(mpu->cpu); if (cpu->halted) { @@ -4062,7 +4058,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *dram, s->led[1] = omap_lpg_init(system_memory, 0xfffbd800, omap_findclk(s, "clk32-kHz")); - /* Register mappings not currenlty implemented: + /* Register mappings not currently implemented: * MCSI2 Comm fffb2000 - fffb27ff (not mapped on OMAP310) * MCSI1 Bluetooth fffb2800 - fffb2fff (not mapped on OMAP310) * USB W2FC fffb4000 - fffb47ff diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index 02b1aa8c97..d9683276c6 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "cpu.h" #include "exec/address-spaces.h" #include "sysemu/blockdev.h" #include "sysemu/qtest.h" @@ -37,7 +36,9 @@ #include "hw/block/flash.h" #include "hw/arm/soc_dma.h" #include "hw/sysbus.h" +#include "hw/boards.h" #include "audio/audio.h" +#include "target/arm/cpu-qom.h" /* Enhanced Audio Controller (CODEC only) */ struct omap_eac_s { @@ -167,7 +168,7 @@ static inline void omap_eac_out_empty(struct omap_eac_s *s) static void omap_eac_in_cb(void *opaque, int avail_b) { - struct omap_eac_s *s = (struct omap_eac_s *) opaque; + struct omap_eac_s *s = opaque; s->codec.rxavail = avail_b >> 2; omap_eac_in_refill(s); @@ -177,7 +178,7 @@ static void omap_eac_in_cb(void *opaque, int avail_b) static void omap_eac_out_cb(void *opaque, int free_b) { - struct omap_eac_s *s = (struct omap_eac_s *) opaque; + struct omap_eac_s *s = opaque; s->codec.txavail = free_b >> 2; if (s->codec.txlen) @@ -274,7 +275,7 @@ static void omap_eac_format_update(struct omap_eac_s *s) fmt.freq = s->codec.rate; /* TODO: signedness possibly depends on the CODEC hardware - or * does I2S specify it? */ - /* All register writes are 16 bits so we we store 16-bit samples + /* All register writes are 16 bits so we store 16-bit samples * in the buffers regardless of AGCFR[B8_16] value. */ fmt.fmt = AUDIO_FORMAT_U16; @@ -333,10 +334,9 @@ static void omap_eac_reset(struct omap_eac_s *s) omap_eac_interrupt_update(s); } -static uint64_t omap_eac_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_eac_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_eac_s *s = (struct omap_eac_s *) opaque; + struct omap_eac_s *s = opaque; uint32_t ret; if (size != 2) { @@ -452,7 +452,7 @@ static uint64_t omap_eac_read(void *opaque, hwaddr addr, static void omap_eac_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_eac_s *s = (struct omap_eac_s *) opaque; + struct omap_eac_s *s = opaque; if (size != 2) { omap_badwidth_write16(opaque, addr, value); @@ -610,7 +610,11 @@ static struct omap_eac_s *omap_eac_init(struct omap_target_agent_s *ta, s->codec.txdrq = *drq; omap_eac_reset(s); - AUD_register_card("OMAP EAC", &s->codec.card); + if (current_machine->audiodev) { + s->codec.card.name = g_strdup(current_machine->audiodev); + s->codec.card.state = audio_state_by_name(s->codec.card.name, &error_fatal); + } + AUD_register_card("OMAP EAC", &s->codec.card, &error_fatal); memory_region_init_io(&s->iomem, NULL, &omap_eac_ops, s, "omap.eac", omap_l4_region_size(ta, 0)); @@ -656,7 +660,7 @@ static void omap_sti_reset(struct omap_sti_s *s) static uint64_t omap_sti_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_sti_s *s = (struct omap_sti_s *) opaque; + struct omap_sti_s *s = opaque; if (size != 4) { return omap_badwidth_read32(opaque, addr); @@ -697,7 +701,7 @@ static uint64_t omap_sti_read(void *opaque, hwaddr addr, static void omap_sti_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_sti_s *s = (struct omap_sti_s *) opaque; + struct omap_sti_s *s = opaque; if (size != 4) { omap_badwidth_write32(opaque, addr, value); @@ -751,8 +755,7 @@ static const MemoryRegionOps omap_sti_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static uint64_t omap_sti_fifo_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_sti_fifo_read(void *opaque, hwaddr addr, unsigned size) { OMAP_BAD_REG(addr); return 0; @@ -761,7 +764,7 @@ static uint64_t omap_sti_fifo_read(void *opaque, hwaddr addr, static void omap_sti_fifo_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_sti_s *s = (struct omap_sti_s *) opaque; + struct omap_sti_s *s = opaque; int ch = addr >> 6; uint8_t byte = value; @@ -1057,7 +1060,7 @@ static void omap_prcm_int_update(struct omap_prcm_s *s, int dom) static uint64_t omap_prcm_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_prcm_s *s = (struct omap_prcm_s *) opaque; + struct omap_prcm_s *s = opaque; uint32_t ret; if (size != 4) { @@ -1369,7 +1372,7 @@ static void omap_prcm_dpll_update(struct omap_prcm_s *s) static void omap_prcm_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_prcm_s *s = (struct omap_prcm_s *) opaque; + struct omap_prcm_s *s = opaque; if (size != 4) { omap_badwidth_write32(opaque, addr, value); @@ -1849,7 +1852,7 @@ struct omap_sysctl_s { static uint32_t omap_sysctl_read8(void *opaque, hwaddr addr) { - struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque; + struct omap_sysctl_s *s = opaque; int pad_offset, byte_offset; int value; @@ -1873,7 +1876,7 @@ static uint32_t omap_sysctl_read8(void *opaque, hwaddr addr) static uint32_t omap_sysctl_read(void *opaque, hwaddr addr) { - struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque; + struct omap_sysctl_s *s = opaque; switch (addr) { case 0x000: /* CONTROL_REVISION */ @@ -1971,10 +1974,9 @@ static uint32_t omap_sysctl_read(void *opaque, hwaddr addr) return 0; } -static void omap_sysctl_write8(void *opaque, hwaddr addr, - uint32_t value) +static void omap_sysctl_write8(void *opaque, hwaddr addr, uint32_t value) { - struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque; + struct omap_sysctl_s *s = opaque; int pad_offset, byte_offset; int prev_value; @@ -1995,10 +1997,9 @@ static void omap_sysctl_write8(void *opaque, hwaddr addr, } } -static void omap_sysctl_write(void *opaque, hwaddr addr, - uint32_t value) +static void omap_sysctl_write(void *opaque, hwaddr addr, uint32_t value) { - struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque; + struct omap_sysctl_s *s = opaque; switch (addr) { case 0x000: /* CONTROL_REVISION */ @@ -2233,7 +2234,7 @@ static struct omap_sysctl_s *omap_sysctl_init(struct omap_target_agent_s *ta, /* General chip reset */ static void omap2_mpu_reset(void *opaque) { - struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *mpu = opaque; omap_dma_reset(mpu->dma); omap_prcm_reset(mpu->prcm); @@ -2527,7 +2528,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sdram, omap_findclk(s, "func_96m_clk"), omap_findclk(s, "core_l4_iclk")); - /* All register mappings (includin those not currenlty implemented): + /* All register mappings (including those not currently implemented): * SystemControlMod 48000000 - 48000fff * SystemControlL4 48001000 - 48001fff * 32kHz Timer Mod 48004000 - 48004fff diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index 57829b3744..62d7915fb8 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -26,6 +26,7 @@ * with this program; if not, see . */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "ui/console.h" #include "hw/arm/omap.h" @@ -34,8 +35,9 @@ #include "hw/block/flash.h" #include "sysemu/qtest.h" #include "exec/address-spaces.h" -#include "cpu.h" #include "qemu/cutils.h" +#include "qemu/error-report.h" + /*****************************************************************************/ /* Siemens SX1 Cellphone V1 */ @@ -65,7 +67,7 @@ static uint64_t static_read(void *opaque, hwaddr offset, unsigned size) { - uint32_t *val = (uint32_t *) opaque; + uint32_t *val = opaque; uint32_t mask = (4 / size) - 1; return *val >> ((offset & mask) << 3); @@ -86,17 +88,15 @@ static const MemoryRegionOps static_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -#define sdram_size 0x02000000 -#define sector_size (128 * 1024) -#define flash0_size (16 * 1024 * 1024) -#define flash1_size ( 8 * 1024 * 1024) -#define flash2_size (32 * 1024 * 1024) -#define total_ram_v1 (sdram_size + flash0_size + flash1_size + OMAP15XX_SRAM_SIZE) -#define total_ram_v2 (sdram_size + flash2_size + OMAP15XX_SRAM_SIZE) +#define SDRAM_SIZE (32 * MiB) +#define SECTOR_SIZE (128 * KiB) +#define FLASH0_SIZE (16 * MiB) +#define FLASH1_SIZE (8 * MiB) +#define FLASH2_SIZE (32 * MiB) static struct arm_boot_info sx1_binfo = { .loader_start = OMAP_EMIFF_BASE, - .ram_size = sdram_size, + .ram_size = SDRAM_SIZE, .board_id = 0x265, }; @@ -113,7 +113,7 @@ static void sx1_init(MachineState *machine, const int version) static uint32_t cs3val = 0x00001139; DriveInfo *dinfo; int fl_idx; - uint32_t flash_size = flash0_size; + uint32_t flash_size = FLASH0_SIZE; if (machine->ram_size != mc->default_ram_size) { char *sz = size_to_str(mc->default_ram_size); @@ -123,7 +123,7 @@ static void sx1_init(MachineState *machine, const int version) } if (version == 2) { - flash_size = flash2_size; + flash_size = FLASH2_SIZE; } memory_region_add_subregion(address_space, OMAP_EMIFF_BASE, machine->ram); @@ -153,13 +153,10 @@ static void sx1_init(MachineState *machine, const int version) fl_idx = 0; 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, 0)) { - fprintf(stderr, "qemu: Error registering flash memory %d.\n", - fl_idx); - } + pflash_cfi01_register(OMAP_CS0_BASE, + "omap_sx1.flash0-1", flash_size, + blk_by_legacy_dinfo(dinfo), + SECTOR_SIZE, 4, 0, 0, 0, 0, 0); fl_idx++; } @@ -167,21 +164,18 @@ static void sx1_init(MachineState *machine, const int version) (dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) { MemoryRegion *flash_1 = g_new(MemoryRegion, 1); memory_region_init_rom(flash_1, NULL, "omap_sx1.flash1-0", - flash1_size, &error_fatal); + FLASH1_SIZE, &error_fatal); memory_region_add_subregion(address_space, OMAP_CS1_BASE, flash_1); memory_region_init_io(&cs[1], NULL, &static_ops, &cs1val, - "sx1.cs1", OMAP_CS1_SIZE - flash1_size); + "sx1.cs1", OMAP_CS1_SIZE - FLASH1_SIZE); memory_region_add_subregion(address_space, - OMAP_CS1_BASE + flash1_size, &cs[1]); + OMAP_CS1_BASE + FLASH1_SIZE, &cs[1]); - 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, 0)) { - fprintf(stderr, "qemu: Error registering flash memory %d.\n", - fl_idx); - } + pflash_cfi01_register(OMAP_CS1_BASE, + "omap_sx1.flash1-1", FLASH1_SIZE, + blk_by_legacy_dinfo(dinfo), + SECTOR_SIZE, 4, 0, 0, 0, 0, 0); fl_idx++; } else { memory_region_init_io(&cs[1], NULL, &static_ops, &cs1val, @@ -220,7 +214,7 @@ 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_size = SDRAM_SIZE; mc->default_ram_id = "omap1.dram"; } @@ -238,7 +232,7 @@ 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_size = SDRAM_SIZE; mc->default_ram_id = "omap1.dram"; } diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index 3ace474870..77e328191d 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -21,9 +21,11 @@ #include "qemu/units.h" #include "exec/address-spaces.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "hw/boards.h" #include "hw/qdev-properties.h" #include "hw/arm/allwinner-h3.h" +#include "hw/arm/boot.h" static struct arm_boot_info orangepi_binfo; @@ -47,12 +49,6 @@ static void orangepi_init(MachineState *machine) 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)); object_unref(OBJECT(h3)); @@ -104,11 +100,16 @@ static void orangepi_init(MachineState *machine) orangepi_binfo.loader_start = h3->memmap[AW_H3_DEV_SDRAM]; orangepi_binfo.ram_size = machine->ram_size; orangepi_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; - arm_load_kernel(ARM_CPU(first_cpu), machine, &orangepi_binfo); + arm_load_kernel(&h3->cpus[0], machine, &orangepi_binfo); } static void orangepi_machine_init(MachineClass *mc) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a7"), + NULL + }; + mc->desc = "Orange Pi PC (Cortex-A7)"; mc->init = orangepi_init; mc->block_default_type = IF_SD; @@ -117,6 +118,7 @@ static void orangepi_machine_init(MachineClass *mc) 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->valid_cpu_types = valid_cpu_types; mc->default_ram_size = 1 * GiB; mc->default_ram_id = "orangepi.ram"; } diff --git a/hw/arm/palm.c b/hw/arm/palm.c index 68e11dd1ec..e04ac92eb7 100644 --- a/hw/arm/palm.c +++ b/hw/arm/palm.c @@ -29,9 +29,10 @@ #include "hw/input/tsc2xxx.h" #include "hw/irq.h" #include "hw/loader.h" -#include "cpu.h" #include "qemu/cutils.h" #include "qom/object.h" +#include "qemu/error-report.h" + static uint64_t static_read(void *opaque, hwaddr offset, unsigned size) { @@ -115,7 +116,7 @@ static struct { static void palmte_button_event(void *opaque, int keycode) { - struct omap_mpu_state_s *cpu = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *cpu = opaque; if (palmte_keymap[keycode & 0x7f].row != -1) omap_mpuio_key(cpu->mpuio, @@ -308,6 +309,9 @@ static void palmte_machine_init(MachineClass *mc) mc->default_cpu_type = ARM_CPU_TYPE_NAME("ti925t"); mc->default_ram_size = 0x02000000; mc->default_ram_id = "omap1.dram"; + mc->deprecation_reason = "machine is old and unmaintained"; + + machine_add_audiodev_property(mc); } DEFINE_MACHINE("cheetah", palmte_machine_init) diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index f4f687df68..6b2e54473b 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -11,6 +11,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qapi/error.h" +#include "exec/address-spaces.h" #include "cpu.h" #include "hw/sysbus.h" #include "migration/vmstate.h" @@ -167,7 +168,7 @@ static const VMStateDescription vmstate_pxa2xx_pm = { .name = "pxa2xx_pm", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(pm_regs, PXA2xxState, 0x40), VMSTATE_END_OF_LIST() } @@ -237,7 +238,7 @@ static const VMStateDescription vmstate_pxa2xx_cm = { .name = "pxa2xx_cm", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(cm_regs, PXA2xxState, 4), VMSTATE_UINT32(clkcfg, PXA2xxState), VMSTATE_UINT32(pmnc, PXA2xxState), @@ -464,7 +465,7 @@ static const VMStateDescription vmstate_pxa2xx_mm = { .name = "pxa2xx_mm", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(mm_regs, PXA2xxState, 0x1a), VMSTATE_END_OF_LIST() } @@ -509,7 +510,7 @@ static const VMStateDescription vmstate_pxa2xx_ssp = { .name = "pxa2xx-ssp", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(enable, PXA2xxSSPState), VMSTATE_UINT32_ARRAY(sscr, PXA2xxSSPState, 2), VMSTATE_UINT32(sspsp, PXA2xxSSPState), @@ -1199,7 +1200,7 @@ static const VMStateDescription vmstate_pxa2xx_rtc_regs = { .minimum_version_id = 0, .pre_save = pxa2xx_rtc_pre_save, .post_load = pxa2xx_rtc_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(rttr, PXA2xxRTCState), VMSTATE_UINT32(rtsr, PXA2xxRTCState), VMSTATE_UINT32(rtar, PXA2xxRTCState), @@ -1305,6 +1306,8 @@ static int pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event) case I2C_NACK: s->status |= 1 << 1; /* set ACKNAK */ break; + default: + return -1; } pxa2xx_i2c_update(s); @@ -1461,7 +1464,7 @@ static const VMStateDescription vmstate_pxa2xx_i2c_slave = { .name = "pxa2xx_i2c_slave", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_I2C_SLAVE(parent_obj, PXA2xxI2CSlaveState), VMSTATE_END_OF_LIST() } @@ -1471,7 +1474,7 @@ static const VMStateDescription vmstate_pxa2xx_i2c = { .name = "pxa2xx_i2c", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(control, PXA2xxI2CState), VMSTATE_UINT16(status, PXA2xxI2CState), VMSTATE_UINT8(ibmr, PXA2xxI2CState), @@ -1510,14 +1513,15 @@ PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base, qdev_prop_set_uint32(dev, "size", region_size + 1); qdev_prop_set_uint32(dev, "offset", base & region_size); + /* FIXME: Should the slave device really be on a separate bus? */ + i2cbus = i2c_init_bus(dev, "dummy"); + i2c_dev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(i2c_dev, &error_fatal); sysbus_mmio_map(i2c_dev, 0, base & ~region_size); sysbus_connect_irq(i2c_dev, 0, irq); s = PXA2XX_I2C(i2c_dev); - /* FIXME: Should the slave device really be on a separate bus? */ - i2cbus = i2c_init_bus(dev, "dummy"); s->slave = PXA2XX_I2C_SLAVE(i2c_slave_create_simple(i2cbus, TYPE_PXA2XX_I2C_SLAVE, 0)); @@ -1724,7 +1728,7 @@ static const VMStateDescription vmstate_pxa2xx_i2s = { .name = "pxa2xx_i2s", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(control, PXA2xxI2SState, 2), VMSTATE_UINT32(status, PXA2xxI2SState), VMSTATE_UINT32(mask, PXA2xxI2SState), @@ -2023,7 +2027,7 @@ static const VMStateDescription pxa2xx_fir_vmsd = { .name = "pxa2xx-fir", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(enable, PXA2xxFIrState), VMSTATE_UINT8_ARRAY(control, PXA2xxFIrState, 3), VMSTATE_UINT8_ARRAY(status, PXA2xxFIrState, 2), @@ -2089,9 +2093,9 @@ static void pxa2xx_reset(void *opaque, int line, int level) } /* Initialise a PXA270 integrated chip (ARM based core). */ -PXA2xxState *pxa270_init(MemoryRegion *address_space, - unsigned int sdram_size, const char *cpu_type) +PXA2xxState *pxa270_init(unsigned int sdram_size, const char *cpu_type) { + MemoryRegion *address_space = get_system_memory(); PXA2xxState *s; int i; DriveInfo *dinfo; @@ -2202,8 +2206,10 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, 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); + s->pcmcia[0] = PXA2XX_PCMCIA(sysbus_create_simple(TYPE_PXA2XX_PCMCIA, + 0x20000000, NULL)); + s->pcmcia[1] = PXA2XX_PCMCIA(sysbus_create_simple(TYPE_PXA2XX_PCMCIA, + 0x30000000, NULL)); sysbus_create_simple(TYPE_PXA2XX_RTC, 0x40900000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_RTCALARM)); @@ -2228,8 +2234,9 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, } /* Initialise a PXA255 integrated chip (ARM based core). */ -PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) +PXA2xxState *pxa255_init(unsigned int sdram_size) { + MemoryRegion *address_space = get_system_memory(); PXA2xxState *s; int i; DriveInfo *dinfo; @@ -2334,8 +2341,10 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi"); } - s->pcmcia[0] = pxa2xx_pcmcia_init(address_space, 0x20000000); - s->pcmcia[1] = pxa2xx_pcmcia_init(address_space, 0x30000000); + s->pcmcia[0] = PXA2XX_PCMCIA(sysbus_create_simple(TYPE_PXA2XX_PCMCIA, + 0x20000000, NULL)); + s->pcmcia[1] = PXA2XX_PCMCIA(sysbus_create_simple(TYPE_PXA2XX_PCMCIA, + 0x30000000, NULL)); sysbus_create_simple(TYPE_PXA2XX_RTC, 0x40900000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_RTCALARM)); diff --git a/hw/arm/pxa2xx_gpio.c b/hw/arm/pxa2xx_gpio.c index e7c3d99224..41dca036fb 100644 --- a/hw/arm/pxa2xx_gpio.c +++ b/hw/arm/pxa2xx_gpio.c @@ -32,7 +32,6 @@ struct PXA2xxGPIOInfo { MemoryRegion iomem; qemu_irq irq0, irq1, irqX; int lines; - int ncpu; ARMCPU *cpu; /* XXX: GNU C vectors are more suitable */ @@ -266,12 +265,11 @@ static const MemoryRegionOps pxa_gpio_ops = { DeviceState *pxa2xx_gpio_init(hwaddr base, ARMCPU *cpu, DeviceState *pic, int lines) { - CPUState *cs = CPU(cpu); DeviceState *dev; dev = qdev_new(TYPE_PXA2XX_GPIO); qdev_prop_set_int32(dev, "lines", lines); - qdev_prop_set_int32(dev, "ncpu", cs->cpu_index); + object_property_set_link(OBJECT(dev), "cpu", OBJECT(cpu), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); @@ -303,8 +301,6 @@ static void pxa2xx_gpio_realize(DeviceState *dev, Error **errp) { PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev); - s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu)); - qdev_init_gpio_in(dev, pxa2xx_gpio_set, s->lines); qdev_init_gpio_out(dev, s->handler, s->lines); } @@ -324,7 +320,7 @@ static const VMStateDescription vmstate_pxa2xx_gpio_regs = { .name = "pxa2xx-gpio", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(ilevel, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), VMSTATE_UINT32_ARRAY(olevel, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), VMSTATE_UINT32_ARRAY(dir, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), @@ -339,7 +335,7 @@ static const VMStateDescription vmstate_pxa2xx_gpio_regs = { static Property pxa2xx_gpio_properties[] = { DEFINE_PROP_INT32("lines", PXA2xxGPIOInfo, lines, 0), - DEFINE_PROP_INT32("ncpu", PXA2xxGPIOInfo, ncpu, 0), + DEFINE_PROP_LINK("cpu", PXA2xxGPIOInfo, cpu, TYPE_ARM_CPU, ARMCPU *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c index 47132ab982..f54546cd4d 100644 --- a/hw/arm/pxa2xx_pic.c +++ b/hw/arm/pxa2xx_pic.c @@ -15,6 +15,7 @@ #include "cpu.h" #include "hw/arm/pxa.h" #include "hw/sysbus.h" +#include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qom/object.h" #include "target/arm/cpregs.h" @@ -271,12 +272,9 @@ static int pxa2xx_pic_post_load(void *opaque, int version_id) return 0; } -DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu) +static void pxa2xx_pic_reset_hold(Object *obj) { - DeviceState *dev = qdev_new(TYPE_PXA2XX_PIC); - PXA2xxPICState *s = PXA2XX_PIC(dev); - - s->cpu = cpu; + PXA2xxPICState *s = PXA2XX_PIC(obj); s->int_pending[0] = 0; s->int_pending[1] = 0; @@ -284,8 +282,23 @@ DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu) s->int_enabled[1] = 0; s->is_fiq[0] = 0; s->is_fiq[1] = 0; +} +DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu) +{ + DeviceState *dev = qdev_new(TYPE_PXA2XX_PIC); + + object_property_set_link(OBJECT(dev), "arm-cpu", + OBJECT(cpu), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + + return dev; +} + +static void pxa2xx_pic_realize(DeviceState *dev, Error **errp) +{ + PXA2xxPICState *s = PXA2XX_PIC(dev); qdev_init_gpio_in(dev, pxa2xx_pic_set_irq, PXA2XX_PIC_SRCS); @@ -293,12 +306,9 @@ DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu) memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_pic_ops, s, "pxa2xx-pic", 0x00100000); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); /* Enable IC coprocessor access. */ - define_arm_cp_regs_with_opaque(cpu, pxa_pic_cp_reginfo, s); - - return dev; + define_arm_cp_regs_with_opaque(s->cpu, pxa_pic_cp_reginfo, s); } static const VMStateDescription vmstate_pxa2xx_pic_regs = { @@ -306,7 +316,7 @@ static const VMStateDescription vmstate_pxa2xx_pic_regs = { .version_id = 0, .minimum_version_id = 0, .post_load = pxa2xx_pic_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(int_enabled, PXA2xxPICState, 2), VMSTATE_UINT32_ARRAY(int_pending, PXA2xxPICState, 2), VMSTATE_UINT32_ARRAY(is_fiq, PXA2xxPICState, 2), @@ -316,12 +326,22 @@ static const VMStateDescription vmstate_pxa2xx_pic_regs = { }, }; +static Property pxa2xx_pic_properties[] = { + DEFINE_PROP_LINK("arm-cpu", PXA2xxPICState, cpu, + TYPE_ARM_CPU, ARMCPU *), + DEFINE_PROP_END_OF_LIST(), +}; + static void pxa2xx_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + device_class_set_props(dc, pxa2xx_pic_properties); + dc->realize = pxa2xx_pic_realize; dc->desc = "PXA2xx PIC"; dc->vmsd = &vmstate_pxa2xx_pic_regs; + rc->phases.hold = pxa2xx_pic_reset_hold; } static const TypeInfo pxa2xx_pic_info = { diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 92d068d1f9..a7a662f40d 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -16,7 +16,10 @@ #include "qemu/units.h" #include "qemu/cutils.h" #include "qapi/error.h" +#include "hw/arm/boot.h" #include "hw/arm/bcm2836.h" +#include "hw/arm/bcm2838.h" +#include "hw/arm/raspi_platform.h" #include "hw/registerfields.h" #include "qemu/error-report.h" #include "hw/boards.h" @@ -24,6 +27,9 @@ #include "hw/arm/boot.h" #include "qom/object.h" +#define TYPE_RASPI_MACHINE MACHINE_TYPE_NAME("raspi-common") +OBJECT_DECLARE_SIMPLE_TYPE(RaspiMachineState, RASPI_MACHINE) + #define SMPBOOT_ADDR 0x300 /* this should leave enough space for ATAGS */ #define MVBAR_ADDR 0x400 /* secure vectors */ #define BOARDSETUP_ADDR (MVBAR_ADDR + 0x20) /* board setup code */ @@ -31,30 +37,12 @@ #define FIRMWARE_ADDR_3 0x80000 /* Pi 3 loads kernel.img here by default */ #define SPINTABLE_ADDR 0xd8 /* Pi 3 bootloader spintable */ -/* Registered machine type (matches RPi Foundation bootloader and U-Boot) */ -#define MACH_TYPE_BCM2708 3138 - struct RaspiMachineState { /*< private >*/ - MachineState parent_obj; + RaspiBaseMachineState parent_obj; /*< public >*/ BCM283XState soc; - struct arm_boot_info binfo; }; -typedef struct RaspiMachineState RaspiMachineState; - -struct RaspiMachineClass { - /*< private >*/ - MachineClass parent_obj; - /*< public >*/ - uint32_t board_rev; -}; -typedef struct RaspiMachineClass RaspiMachineClass; - -#define TYPE_RASPI_MACHINE MACHINE_TYPE_NAME("raspi-common") -DECLARE_OBJ_CHECKERS(RaspiMachineState, RaspiMachineClass, - RASPI_MACHINE, TYPE_RASPI_MACHINE) - /* * Board revision codes: @@ -71,6 +59,7 @@ typedef enum RaspiProcessorId { PROCESSOR_ID_BCM2835 = 0, PROCESSOR_ID_BCM2836 = 1, PROCESSOR_ID_BCM2837 = 2, + PROCESSOR_ID_BCM2838 = 3, } RaspiProcessorId; static const struct { @@ -80,9 +69,10 @@ static const struct { [PROCESSOR_ID_BCM2835] = {TYPE_BCM2835, 1}, [PROCESSOR_ID_BCM2836] = {TYPE_BCM2836, BCM283X_NCPUS}, [PROCESSOR_ID_BCM2837] = {TYPE_BCM2837, BCM283X_NCPUS}, + [PROCESSOR_ID_BCM2838] = {TYPE_BCM2838, BCM283X_NCPUS}, }; -static uint64_t board_ram_size(uint32_t board_rev) +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); @@ -98,7 +88,7 @@ static RaspiProcessorId board_processor_id(uint32_t board_rev) return proc_id; } -static const char *board_soc_type(uint32_t board_rev) +const char *board_soc_type(uint32_t board_rev) { return soc_property[board_processor_id(board_rev)].type; } @@ -124,20 +114,22 @@ static const char *board_type(uint32_t board_rev) static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info) { - static const uint32_t smpboot[] = { - 0xe1a0e00f, /* mov lr, pc */ - 0xe3a0fe00 + (BOARDSETUP_ADDR >> 4), /* mov pc, BOARDSETUP_ADDR */ - 0xee100fb0, /* mrc p15, 0, r0, c0, c0, 5;get core ID */ - 0xe7e10050, /* ubfx r0, r0, #0, #2 ;extract LSB */ - 0xe59f5014, /* ldr r5, =0x400000CC ;load mbox base */ - 0xe320f001, /* 1: yield */ - 0xe7953200, /* ldr r3, [r5, r0, lsl #4] ;read mbox for our core*/ - 0xe3530000, /* cmp r3, #0 ;spin while zero */ - 0x0afffffb, /* beq 1b */ - 0xe7853200, /* str r3, [r5, r0, lsl #4] ;clear mbox */ - 0xe12fff13, /* bx r3 ;jump to target */ - 0x400000cc, /* (constant: mailbox 3 read/clear base) */ + static const ARMInsnFixup smpboot[] = { + { 0xe1a0e00f }, /* mov lr, pc */ + { 0xe3a0fe00 + (BOARDSETUP_ADDR >> 4) }, /* mov pc, BOARDSETUP_ADDR */ + { 0xee100fb0 }, /* mrc p15, 0, r0, c0, c0, 5;get core ID */ + { 0xe7e10050 }, /* ubfx r0, r0, #0, #2 ;extract LSB */ + { 0xe59f5014 }, /* ldr r5, =0x400000CC ;load mbox base */ + { 0xe320f001 }, /* 1: yield */ + { 0xe7953200 }, /* ldr r3, [r5, r0, lsl #4] ;read mbox for our core */ + { 0xe3530000 }, /* cmp r3, #0 ;spin while zero */ + { 0x0afffffb }, /* beq 1b */ + { 0xe7853200 }, /* str r3, [r5, r0, lsl #4] ;clear mbox */ + { 0xe12fff13 }, /* bx r3 ;jump to target */ + { 0x400000cc }, /* (constant: mailbox 3 read/clear base) */ + { 0, FIXUP_TERMINATOR } }; + static const uint32_t fixupcontext[FIXUP_MAX] = { 0 }; /* check that we don't overrun board setup vectors */ QEMU_BUILD_BUG_ON(SMPBOOT_ADDR + sizeof(smpboot) > MVBAR_ADDR); @@ -145,9 +137,8 @@ static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info) QEMU_BUILD_BUG_ON((BOARDSETUP_ADDR & 0xf) != 0 || (BOARDSETUP_ADDR >> 4) >= 0x100); - rom_add_blob_fixed_as("raspi_smpboot", smpboot, sizeof(smpboot), - info->smp_loader_start, - arm_boot_address_space(cpu, info)); + arm_write_bootloader("raspi_smpboot", arm_boot_address_space(cpu, info), + info->smp_loader_start, smpboot, fixupcontext); } static void write_smpboot64(ARMCPU *cpu, const struct arm_boot_info *info) @@ -161,26 +152,28 @@ static void write_smpboot64(ARMCPU *cpu, const struct arm_boot_info *info) * the primary CPU goes into the kernel. We put these variables inside * a rom blob, so that the reset for ROM contents zeroes them for us. */ - static const uint32_t smpboot[] = { - 0xd2801b05, /* mov x5, 0xd8 */ - 0xd53800a6, /* mrs x6, mpidr_el1 */ - 0x924004c6, /* and x6, x6, #0x3 */ - 0xd503205f, /* spin: wfe */ - 0xf86678a4, /* ldr x4, [x5,x6,lsl #3] */ - 0xb4ffffc4, /* cbz x4, spin */ - 0xd2800000, /* mov x0, #0x0 */ - 0xd2800001, /* mov x1, #0x0 */ - 0xd2800002, /* mov x2, #0x0 */ - 0xd2800003, /* mov x3, #0x0 */ - 0xd61f0080, /* br x4 */ + static const ARMInsnFixup smpboot[] = { + { 0xd2801b05 }, /* mov x5, 0xd8 */ + { 0xd53800a6 }, /* mrs x6, mpidr_el1 */ + { 0x924004c6 }, /* and x6, x6, #0x3 */ + { 0xd503205f }, /* spin: wfe */ + { 0xf86678a4 }, /* ldr x4, [x5,x6,lsl #3] */ + { 0xb4ffffc4 }, /* cbz x4, spin */ + { 0xd2800000 }, /* mov x0, #0x0 */ + { 0xd2800001 }, /* mov x1, #0x0 */ + { 0xd2800002 }, /* mov x2, #0x0 */ + { 0xd2800003 }, /* mov x3, #0x0 */ + { 0xd61f0080 }, /* br x4 */ + { 0, FIXUP_TERMINATOR } }; + static const uint32_t fixupcontext[FIXUP_MAX] = { 0 }; static const uint64_t spintables[] = { 0, 0, 0, 0 }; - rom_add_blob_fixed_as("raspi_smpboot", smpboot, sizeof(smpboot), - info->smp_loader_start, as); + arm_write_bootloader("raspi_smpboot", as, info->smp_loader_start, + smpboot, fixupcontext); rom_add_blob_fixed_as("raspi_spintables", spintables, sizeof(spintables), SPINTABLE_ADDR, as); } @@ -196,13 +189,12 @@ static void reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info) cpu_set_pc(cs, info->smp_loader_start); } -static void setup_boot(MachineState *machine, RaspiProcessorId processor_id, - size_t ram_size) +static void setup_boot(MachineState *machine, ARMCPU *cpu, + RaspiProcessorId processor_id, size_t ram_size) { - RaspiMachineState *s = RASPI_MACHINE(machine); + RaspiBaseMachineState *s = RASPI_BASE_MACHINE(machine); int r; - s->binfo.board_id = MACH_TYPE_BCM2708; s->binfo.ram_size = ram_size; if (processor_id <= PROCESSOR_ID_BCM2836) { @@ -248,16 +240,17 @@ static void setup_boot(MachineState *machine, RaspiProcessorId processor_id, s->binfo.firmware_loaded = true; } - arm_load_kernel(&s->soc.cpu[0].core, machine, &s->binfo); + arm_load_kernel(cpu, machine, &s->binfo); } -static void raspi_machine_init(MachineState *machine) +void raspi_base_machine_init(MachineState *machine, + BCM283XBaseState *soc) { - RaspiMachineClass *mc = RASPI_MACHINE_GET_CLASS(machine); - RaspiMachineState *s = RASPI_MACHINE(machine); + RaspiBaseMachineClass *mc = RASPI_BASE_MACHINE_GET_CLASS(machine); uint32_t board_rev = mc->board_rev; uint64_t ram_size = board_ram_size(board_rev); - uint32_t vcram_size; + uint32_t vcram_base, vcram_size; + size_t boot_ram_size; DriveInfo *di; BlockBackend *blk; BusState *bus; @@ -275,17 +268,17 @@ static void raspi_machine_init(MachineState *machine) machine->ram, 0); /* Setup the SOC */ - object_initialize_child(OBJECT(machine), "soc", &s->soc, - board_soc_type(board_rev)); - object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(machine->ram)); - object_property_set_int(OBJECT(&s->soc), "board-rev", board_rev, + object_property_add_const_link(OBJECT(soc), "ram", OBJECT(machine->ram)); + object_property_set_int(OBJECT(soc), "board-rev", board_rev, &error_abort); - qdev_realize(DEVICE(&s->soc), NULL, &error_fatal); + object_property_set_str(OBJECT(soc), "command-line", + machine->kernel_cmdline, &error_abort); + qdev_realize(DEVICE(soc), NULL, &error_fatal); /* Create and plug in the SD cards */ di = drive_get(IF_SD, 0, 0); blk = di ? blk_by_legacy_dinfo(di) : NULL; - bus = qdev_get_child_bus(DEVICE(&s->soc), "sd-bus"); + bus = qdev_get_child_bus(DEVICE(soc), "sd-bus"); if (bus == NULL) { error_report("No SD bus found in SOC object"); exit(1); @@ -294,19 +287,40 @@ static void raspi_machine_init(MachineState *machine) qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); qdev_realize_and_unref(carddev, bus, &error_fatal); - vcram_size = object_property_get_uint(OBJECT(&s->soc), "vcram-size", + vcram_size = object_property_get_uint(OBJECT(soc), "vcram-size", &error_abort); - setup_boot(machine, board_processor_id(mc->board_rev), - machine->ram_size - vcram_size); + vcram_base = object_property_get_uint(OBJECT(soc), "vcram-base", + &error_abort); + + if (vcram_base == 0) { + vcram_base = ram_size - vcram_size; + } + boot_ram_size = MIN(vcram_base, UPPER_RAM_BASE - vcram_size); + + setup_boot(machine, &soc->cpu[0].core, board_processor_id(board_rev), + boot_ram_size); } -static void raspi_machine_class_common_init(MachineClass *mc, - uint32_t board_rev) +void raspi_machine_init(MachineState *machine) +{ + RaspiMachineState *s = RASPI_MACHINE(machine); + RaspiBaseMachineState *s_base = RASPI_BASE_MACHINE(machine); + RaspiBaseMachineClass *mc = RASPI_BASE_MACHINE_GET_CLASS(machine); + BCM283XState *soc = &s->soc; + + s_base->binfo.board_id = MACH_TYPE_BCM2708; + + object_initialize_child(OBJECT(machine), "soc", soc, + board_soc_type(mc->board_rev)); + raspi_base_machine_init(machine, &soc->parent_obj); +} + +void raspi_machine_class_common_init(MachineClass *mc, + uint32_t board_rev) { mc->desc = g_strdup_printf("Raspberry Pi %s (revision 1.%u)", board_type(board_rev), FIELD_EX32(board_rev, REV_CODE, REVISION)); - mc->init = raspi_machine_init; mc->block_default_type = IF_SD; mc->no_parallel = 1; mc->no_floppy = 1; @@ -316,50 +330,57 @@ static void raspi_machine_class_common_init(MachineClass *mc, mc->default_ram_id = "ram"; }; +static void raspi_machine_class_init(MachineClass *mc, + uint32_t board_rev) +{ + raspi_machine_class_common_init(mc, board_rev); + mc->init = raspi_machine_init; +}; + static void raspi0_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); rmc->board_rev = 0x920092; /* Revision 1.2 */ - raspi_machine_class_common_init(mc, rmc->board_rev); + raspi_machine_class_init(mc, rmc->board_rev); }; static void raspi1ap_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); rmc->board_rev = 0x900021; /* Revision 1.1 */ - raspi_machine_class_common_init(mc, rmc->board_rev); + raspi_machine_class_init(mc, rmc->board_rev); }; static void raspi2b_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); rmc->board_rev = 0xa21041; - raspi_machine_class_common_init(mc, rmc->board_rev); + raspi_machine_class_init(mc, rmc->board_rev); }; #ifdef TARGET_AARCH64 static void raspi3ap_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); rmc->board_rev = 0x9020e0; /* Revision 1.0 */ - raspi_machine_class_common_init(mc, rmc->board_rev); + raspi_machine_class_init(mc, rmc->board_rev); }; static void raspi3b_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); rmc->board_rev = 0xa02082; - raspi_machine_class_common_init(mc, rmc->board_rev); + raspi_machine_class_init(mc, rmc->board_rev); }; #endif /* TARGET_AARCH64 */ @@ -388,9 +409,14 @@ static const TypeInfo raspi_machine_types[] = { #endif }, { .name = TYPE_RASPI_MACHINE, - .parent = TYPE_MACHINE, + .parent = TYPE_RASPI_BASE_MACHINE, .instance_size = sizeof(RaspiMachineState), - .class_size = sizeof(RaspiMachineClass), + .abstract = true, + }, { + .name = TYPE_RASPI_BASE_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(RaspiBaseMachineState), + .class_size = sizeof(RaspiBaseMachineClass), .abstract = true, } }; diff --git a/hw/arm/raspi4b.c b/hw/arm/raspi4b.c new file mode 100644 index 0000000000..85877880fc --- /dev/null +++ b/hw/arm/raspi4b.c @@ -0,0 +1,136 @@ +/* + * Raspberry Pi 4B emulation + * + * Copyright (C) 2022 Ovchinnikov Vitalii + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/cutils.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "hw/arm/raspi_platform.h" +#include "hw/display/bcm2835_fb.h" +#include "hw/registerfields.h" +#include "qemu/error-report.h" +#include "sysemu/device_tree.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/arm/boot.h" +#include "qom/object.h" +#include "hw/arm/bcm2838.h" +#include + +#define TYPE_RASPI4B_MACHINE MACHINE_TYPE_NAME("raspi4b") +OBJECT_DECLARE_SIMPLE_TYPE(Raspi4bMachineState, RASPI4B_MACHINE) + +struct Raspi4bMachineState { + RaspiBaseMachineState parent_obj; + BCM2838State soc; +}; + +/* + * Add second memory region if board RAM amount exceeds VC base address + * (see https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf + * 1.2 Address Map) + */ +static int raspi_add_memory_node(void *fdt, hwaddr mem_base, hwaddr mem_len) +{ + int ret; + uint32_t acells, scells; + char *nodename = g_strdup_printf("/memory@%" PRIx64, mem_base); + + acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells", + NULL, &error_fatal); + scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells", + NULL, &error_fatal); + if (acells == 0 || scells == 0) { + fprintf(stderr, "dtb file invalid (#address-cells or #size-cells 0)\n"); + ret = -1; + } else { + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); + ret = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", + acells, mem_base, + scells, mem_len); + } + + g_free(nodename); + return ret; +} + +static void raspi4_modify_dtb(const struct arm_boot_info *info, void *fdt) +{ + uint64_t ram_size; + + /* Temporarily disable following devices until they are implemented */ + const char *nodes_to_remove[] = { + "brcm,bcm2711-pcie", + "brcm,bcm2711-rng200", + "brcm,bcm2711-thermal", + "brcm,bcm2711-genet-v5", + }; + + for (int i = 0; i < ARRAY_SIZE(nodes_to_remove); i++) { + const char *dev_str = nodes_to_remove[i]; + + int offset = fdt_node_offset_by_compatible(fdt, -1, dev_str); + if (offset >= 0) { + if (!fdt_nop_node(fdt, offset)) { + warn_report("bcm2711 dtc: %s has been disabled!", dev_str); + } + } + } + + ram_size = board_ram_size(info->board_id); + + if (info->ram_size > UPPER_RAM_BASE) { + raspi_add_memory_node(fdt, UPPER_RAM_BASE, ram_size - UPPER_RAM_BASE); + } +} + +static void raspi4b_machine_init(MachineState *machine) +{ + Raspi4bMachineState *s = RASPI4B_MACHINE(machine); + RaspiBaseMachineState *s_base = RASPI_BASE_MACHINE(machine); + RaspiBaseMachineClass *mc = RASPI_BASE_MACHINE_GET_CLASS(machine); + BCM2838State *soc = &s->soc; + + s_base->binfo.modify_dtb = raspi4_modify_dtb; + s_base->binfo.board_id = mc->board_rev; + + object_initialize_child(OBJECT(machine), "soc", soc, + board_soc_type(mc->board_rev)); + + raspi_base_machine_init(machine, &soc->parent_obj); +} + +static void raspi4b_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); + +#if HOST_LONG_BITS == 32 + rmc->board_rev = 0xa03111; /* Revision 1.1, 1 Gb RAM */ +#else + rmc->board_rev = 0xb03115; /* Revision 1.5, 2 Gb RAM */ +#endif + raspi_machine_class_common_init(mc, rmc->board_rev); + mc->init = raspi4b_machine_init; +} + +static const TypeInfo raspi4b_machine_type = { + .name = TYPE_RASPI4B_MACHINE, + .parent = TYPE_RASPI_BASE_MACHINE, + .instance_size = sizeof(Raspi4bMachineState), + .class_init = raspi4b_machine_class_init, +}; + +static void raspi4b_machine_register_type(void) +{ + type_register_static(&raspi4b_machine_type); +} + +type_init(raspi4b_machine_register_type) diff --git a/hw/arm/realview.c b/hw/arm/realview.c index d2dc8a8952..b186f965c6 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -29,6 +29,8 @@ #include "hw/irq.h" #include "hw/i2c/arm_sbcon_i2c.h" #include "hw/sd/sd.h" +#include "audio/audio.h" +#include "target/arm/cpu-qom.h" #define SMP_BOOT_ADDR 0xe0000000 #define SMP_BOOTREG_ADDR 0x10000030 @@ -83,12 +85,10 @@ static void realview_init(MachineState *machine, SysBusDevice *busdev; qemu_irq pic[64]; PCIBus *pci_bus = NULL; - NICInfo *nd; DriveInfo *dinfo; I2CBus *i2c; int n; unsigned int smp_cpus = machine->smp.cpus; - int done_nic = 0; qemu_irq cpu_irq[4]; int is_mpcore = 0; int is_pb = 0; @@ -207,6 +207,9 @@ static void realview_init(MachineState *machine, pl041 = qdev_new("pl041"); qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); + if (machine->audiodev) { + qdev_prop_set_string(pl041, "audiodev", machine->audiodev); + } sysbus_realize_and_unref(SYS_BUS_DEVICE(pl041), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(pl041), 0, 0x10004000); sysbus_connect_irq(SYS_BUS_DEVICE(pl041), 0, pic[19]); @@ -235,7 +238,12 @@ static void realview_init(MachineState *machine, sysbus_create_simple("pl061", 0x10014000, pic[7]); gpio2 = sysbus_create_simple("pl061", 0x10015000, pic[8]); - sysbus_create_simple("pl111", 0x10020000, pic[23]); + dev = qdev_new("pl111"); + object_property_set_link(OBJECT(dev), "framebuffer-memory", + OBJECT(sysmem), &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x10020000); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[23]); dev = sysbus_create_varargs("pl181", 0x10005000, pic[17], pic[18], NULL); /* Wire up MMC card detect and read-only signals. These have @@ -291,25 +299,20 @@ static void realview_init(MachineState *machine, n--; } } - for(n = 0; n < nb_nics; n++) { - nd = &nd_table[n]; - if (!done_nic && (!nd->model || - strcmp(nd->model, is_pb ? "lan9118" : "smc91c111") == 0)) { - if (is_pb) { - lan9118_init(nd, 0x4e000000, pic[28]); - } else { - smc91c111_init(nd, 0x4e000000, pic[28]); - } - done_nic = 1; + if (qemu_find_nic_info(is_pb ? "lan9118" : "smc91c111", true, NULL)) { + if (is_pb) { + lan9118_init(0x4e000000, pic[28]); } else { - if (pci_bus) { - pci_nic_init_nofail(nd, pci_bus, "rtl8139", NULL); - } + smc91c111_init(0x4e000000, pic[28]); } } - dev = sysbus_create_simple(TYPE_VERSATILE_I2C, 0x10002000, NULL); + if (pci_bus) { + pci_init_nic_devices(pci_bus, "rtl8139"); + } + + dev = sysbus_create_simple(TYPE_ARM_SBCON_I2C, 0x10002000, NULL); i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); i2c_slave_create_simple(i2c, "ds1338", 0x68); @@ -380,7 +383,7 @@ static void realview_init(MachineState *machine, realview_binfo.ram_size = ram_size; realview_binfo.board_id = realview_board_id[board_type]; realview_binfo.loader_start = (board_type == BOARD_PB_A8 ? 0x70000000 : 0); - arm_load_kernel(ARM_CPU(first_cpu), machine, &realview_binfo); + arm_load_kernel(cpu, machine, &realview_binfo); } static void realview_eb_init(MachineState *machine) @@ -412,6 +415,8 @@ static void realview_eb_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"); + + machine_add_audiodev_property(mc); } static const TypeInfo realview_eb_type = { @@ -430,6 +435,8 @@ static void realview_eb_mpcore_class_init(ObjectClass *oc, void *data) mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm11mpcore"); + + machine_add_audiodev_property(mc); } static const TypeInfo realview_eb_mpcore_type = { @@ -446,6 +453,8 @@ static void realview_pb_a8_class_init(ObjectClass *oc, void *data) mc->init = realview_pb_a8_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a8"); + + machine_add_audiodev_property(mc); } static const TypeInfo realview_pb_a8_type = { @@ -463,6 +472,8 @@ static void realview_pbx_a9_class_init(ObjectClass *oc, void *data) mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a9"); + + machine_add_audiodev_property(mc); } static const TypeInfo realview_pbx_a9_type = { diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c index 41191245b8..56f184b9ae 100644 --- a/hw/arm/sabrelite.c +++ b/hw/arm/sabrelite.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx6.h" +#include "hw/arm/boot.h" #include "hw/boards.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 4bb444684f..f5709d6c14 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -2,6 +2,7 @@ * ARM SBSA Reference Platform emulation * * Copyright (c) 2018 Linaro Limited + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Written by Hongbo Zhang * * This program is free software; you can redistribute it and/or modify it @@ -23,25 +24,34 @@ #include "qemu/error-report.h" #include "qemu/units.h" #include "sysemu/device_tree.h" +#include "sysemu/kvm.h" #include "sysemu/numa.h" #include "sysemu/runstate.h" #include "sysemu/sysemu.h" #include "exec/hwaddr.h" #include "kvm_arm.h" #include "hw/arm/boot.h" +#include "hw/arm/bsa.h" +#include "hw/arm/fdt.h" +#include "hw/arm/smmuv3.h" #include "hw/block/flash.h" #include "hw/boards.h" -#include "hw/ide/internal.h" -#include "hw/ide/ahci_internal.h" +#include "hw/ide/ide-bus.h" +#include "hw/ide/ahci-sysbus.h" #include "hw/intc/arm_gicv3_common.h" +#include "hw/intc/arm_gicv3_its_common.h" #include "hw/loader.h" #include "hw/pci-host/gpex.h" #include "hw/qdev-properties.h" #include "hw/usb.h" +#include "hw/usb/xhci.h" #include "hw/char/pl011.h" #include "hw/watchdog/sbsa_gwdt.h" #include "net/net.h" +#include "qapi/qmp/qlist.h" #include "qom/object.h" +#include "target/arm/cpu-qom.h" +#include "target/arm/gtimer.h" #define RAMLIMIT_GB 8192 #define RAMLIMIT_BYTES (RAMLIMIT_GB * GiB) @@ -50,19 +60,13 @@ #define NUM_SMMU_IRQS 4 #define NUM_SATA_PORTS 6 -#define VIRTUAL_PMU_IRQ 7 -#define ARCH_GIC_MAINT_IRQ 9 -#define ARCH_TIMER_VIRT_IRQ 11 -#define ARCH_TIMER_S_EL1_IRQ 13 -#define ARCH_TIMER_NS_EL1_IRQ 14 -#define ARCH_TIMER_NS_EL2_IRQ 10 - enum { SBSA_FLASH, SBSA_MEM, SBSA_CPUPERIPHS, SBSA_GIC_DIST, SBSA_GIC_REDIST, + SBSA_GIC_ITS, SBSA_SECURE_EC, SBSA_GWDT_WS0, SBSA_GWDT_REFRESH, @@ -80,7 +84,7 @@ enum { SBSA_SECURE_UART_MM, SBSA_SECURE_MEM, SBSA_AHCI, - SBSA_EHCI, + SBSA_XHCI, }; struct SBSAMachineState { @@ -106,6 +110,7 @@ static const MemMapEntry sbsa_ref_memmap[] = { [SBSA_CPUPERIPHS] = { 0x40000000, 0x00040000 }, [SBSA_GIC_DIST] = { 0x40060000, 0x00010000 }, [SBSA_GIC_REDIST] = { 0x40080000, 0x04000000 }, + [SBSA_GIC_ITS] = { 0x44081000, 0x00020000 }, [SBSA_SECURE_EC] = { 0x50000000, 0x00001000 }, [SBSA_GWDT_REFRESH] = { 0x50010000, 0x00001000 }, [SBSA_GWDT_CONTROL] = { 0x50011000, 0x00001000 }, @@ -117,7 +122,7 @@ static const MemMapEntry sbsa_ref_memmap[] = { [SBSA_SMMU] = { 0x60050000, 0x00020000 }, /* Space here reserved for more SMMUs */ [SBSA_AHCI] = { 0x60100000, 0x00010000 }, - [SBSA_EHCI] = { 0x60110000, 0x00010000 }, + [SBSA_XHCI] = { 0x60110000, 0x00010000 }, /* Space here reserved for other devices */ [SBSA_PCIE_PIO] = { 0x7fff0000, 0x00010000 }, /* 32-bit address PCIE MMIO space */ @@ -137,35 +142,36 @@ static const int sbsa_ref_irqmap[] = { [SBSA_SECURE_UART] = 8, [SBSA_SECURE_UART_MM] = 9, [SBSA_AHCI] = 10, - [SBSA_EHCI] = 11, + [SBSA_XHCI] = 11, [SBSA_SMMU] = 12, /* ... to 15 */ [SBSA_GWDT_WS0] = 16, }; -static const char * const valid_cpus[] = { - ARM_CPU_TYPE_NAME("cortex-a57"), - ARM_CPU_TYPE_NAME("cortex-a72"), - ARM_CPU_TYPE_NAME("cortex-a76"), - ARM_CPU_TYPE_NAME("neoverse-n1"), - ARM_CPU_TYPE_NAME("max"), -}; - -static bool cpu_type_valid(const char *cpu) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(valid_cpus); i++) { - if (strcmp(cpu, valid_cpus[i]) == 0) { - return true; - } - } - return false; -} - static uint64_t sbsa_ref_cpu_mp_affinity(SBSAMachineState *sms, int idx) { uint8_t clustersz = ARM_DEFAULT_CPUS_PER_CLUSTER; - return arm_cpu_mp_affinity(idx, clustersz); + return arm_build_mp_affinity(idx, clustersz); +} + +static void sbsa_fdt_add_gic_node(SBSAMachineState *sms) +{ + char *nodename; + + nodename = g_strdup_printf("/intc"); + qemu_fdt_add_subnode(sms->fdt, nodename); + qemu_fdt_setprop_sized_cells(sms->fdt, nodename, "reg", + 2, sbsa_ref_memmap[SBSA_GIC_DIST].base, + 2, sbsa_ref_memmap[SBSA_GIC_DIST].size, + 2, sbsa_ref_memmap[SBSA_GIC_REDIST].base, + 2, sbsa_ref_memmap[SBSA_GIC_REDIST].size); + + nodename = g_strdup_printf("/intc/its"); + qemu_fdt_add_subnode(sms->fdt, nodename); + qemu_fdt_setprop_sized_cells(sms->fdt, nodename, "reg", + 2, sbsa_ref_memmap[SBSA_GIC_ITS].base, + 2, sbsa_ref_memmap[SBSA_GIC_ITS].size); + + g_free(nodename); } /* @@ -204,7 +210,7 @@ static void create_fdt(SBSAMachineState *sms) * fw compatibility. */ qemu_fdt_setprop_cell(fdt, "/", "machine-version-major", 0); - qemu_fdt_setprop_cell(fdt, "/", "machine-version-minor", 0); + qemu_fdt_setprop_cell(fdt, "/", "machine-version-minor", 3); if (ms->numa_state->have_numa_distance) { int size = nb_numa_nodes * nb_numa_nodes * 3 * sizeof(uint32_t); @@ -260,6 +266,8 @@ static void create_fdt(SBSAMachineState *sms) g_free(nodename); } + + sbsa_fdt_add_gic_node(sms); } #define SBSA_FLASH_SECTOR_SIZE (256 * KiB) @@ -392,12 +400,26 @@ static void create_secure_ram(SBSAMachineState *sms, memory_region_add_subregion(secure_sysmem, base, secram); } -static void create_gic(SBSAMachineState *sms) +static void create_its(SBSAMachineState *sms) +{ + const char *itsclass = its_class_name(); + DeviceState *dev; + + dev = qdev_new(itsclass); + + object_property_set_link(OBJECT(dev), "parent-gicv3", OBJECT(sms->gic), + &error_abort); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, sbsa_ref_memmap[SBSA_GIC_ITS].base); +} + +static void create_gic(SBSAMachineState *sms, MemoryRegion *mem) { unsigned int smp_cpus = MACHINE(sms)->smp.cpus; SysBusDevice *gicbusdev; const char *gictype; uint32_t redist0_capacity, redist0_count; + QList *redist_region_count; int i; gictype = gicv3_class_name(); @@ -416,8 +438,13 @@ static void create_gic(SBSAMachineState *sms) sbsa_ref_memmap[SBSA_GIC_REDIST].size / GICV3_REDIST_SIZE; redist0_count = MIN(smp_cpus, redist0_capacity); - qdev_prop_set_uint32(sms->gic, "len-redist-region-count", 1); - qdev_prop_set_uint32(sms->gic, "redist-region-count[0]", redist0_count); + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, redist0_count); + qdev_prop_set_array(sms->gic, "redist-region-count", redist_region_count); + + object_property_set_link(OBJECT(sms->gic), "sysmem", + OBJECT(mem), &error_fatal); + qdev_prop_set_bit(sms->gic, "has-lpi", true); gicbusdev = SYS_BUS_DEVICE(sms->gic); sysbus_realize_and_unref(gicbusdev, &error_fatal); @@ -431,7 +458,7 @@ static void create_gic(SBSAMachineState *sms) */ for (i = 0; i < smp_cpus; i++) { DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); - int ppibase = NUM_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS; + int intidbase = NUM_IRQS + i * GIC_INTERNAL; int irq; /* * Mapping from the output timer irq lines from the CPU to the @@ -442,19 +469,23 @@ static void create_gic(SBSAMachineState *sms) [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, + [GTIMER_HYPVIRT] = ARCH_TIMER_NS_EL2_VIRT_IRQ, }; for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { qdev_connect_gpio_out(cpudev, irq, qdev_get_gpio_in(sms->gic, - ppibase + timer_irq[irq])); + intidbase + timer_irq[irq])); } qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", 0, - qdev_get_gpio_in(sms->gic, ppibase + qdev_get_gpio_in(sms->gic, + intidbase + ARCH_GIC_MAINT_IRQ)); + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, - qdev_get_gpio_in(sms->gic, ppibase + qdev_get_gpio_in(sms->gic, + intidbase + VIRTUAL_PMU_IRQ)); sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); @@ -465,6 +496,7 @@ static void create_gic(SBSAMachineState *sms) sysbus_connect_irq(gicbusdev, i + 3 * smp_cpus, qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); } + create_its(sms); } static void create_uart(const SBSAMachineState *sms, int uart, @@ -538,8 +570,6 @@ static void create_ahci(const SBSAMachineState *sms) DeviceState *dev; DriveInfo *hd[NUM_SATA_PORTS]; SysbusAHCIState *sysahci; - AHCIState *ahci; - int i; dev = qdev_new("sysbus-ahci"); qdev_prop_set_uint32(dev, "num-ports", NUM_SATA_PORTS); @@ -548,23 +578,20 @@ static void create_ahci(const SBSAMachineState *sms) sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(sms->gic, irq)); sysahci = SYSBUS_AHCI(dev); - ahci = &sysahci->ahci; ide_drive_get(hd, ARRAY_SIZE(hd)); - for (i = 0; i < ahci->ports; i++) { - if (hd[i] == NULL) { - continue; - } - ide_create_drive(&ahci->dev[i].port, 0, hd[i]); - } + ahci_ide_create_devs(&sysahci->ahci, hd); } -static void create_ehci(const SBSAMachineState *sms) +static void create_xhci(const SBSAMachineState *sms) { - hwaddr base = sbsa_ref_memmap[SBSA_EHCI].base; - int irq = sbsa_ref_irqmap[SBSA_EHCI]; + hwaddr base = sbsa_ref_memmap[SBSA_XHCI].base; + int irq = sbsa_ref_irqmap[SBSA_XHCI]; + DeviceState *dev = qdev_new(TYPE_XHCI_SYSBUS); + qdev_prop_set_uint32(dev, "slots", XHCI_MAXSLOTS); - sysbus_create_simple("platform-ehci-usb", base, - qdev_get_gpio_in(sms->gic, irq)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(sms->gic, irq)); } static void create_smmu(const SBSAMachineState *sms, PCIBus *bus) @@ -574,7 +601,7 @@ static void create_smmu(const SBSAMachineState *sms, PCIBus *bus) DeviceState *dev; int i; - dev = qdev_new("arm-smmuv3"); + dev = qdev_new(TYPE_ARM_SMMUV3); object_property_set_link(OBJECT(dev), "primary-bus", OBJECT(bus), &error_abort); @@ -596,6 +623,7 @@ static void create_pcie(SBSAMachineState *sms) hwaddr size_mmio_high = sbsa_ref_memmap[SBSA_PCIE_MMIO_HIGH].size; hwaddr base_pio = sbsa_ref_memmap[SBSA_PCIE_PIO].base; int irq = sbsa_ref_irqmap[SBSA_PCIE]; + MachineClass *mc = MACHINE_GET_CLASS(sms); MemoryRegion *mmio_alias, *mmio_alias_high, *mmio_reg; MemoryRegion *ecam_alias, *ecam_reg; DeviceState *dev; @@ -636,19 +664,10 @@ static void create_pcie(SBSAMachineState *sms) } pci = PCI_HOST_BRIDGE(dev); - if (pci->bus) { - for (i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - if (!nd->model) { - nd->model = g_strdup("e1000e"); - } + pci_init_nic_devices(pci->bus, mc->default_nic); - pci_nic_init_nofail(nd, pci->bus, nd->model, NULL); - } - } - - pci_create_simple(pci->bus, -1, "VGA"); + pci_create_simple(pci->bus, -1, "bochs-display"); create_smmu(sms, pci->bus); } @@ -684,11 +703,6 @@ static void sbsa_ref_init(MachineState *machine) const CPUArchIdList *possible_cpus; int n, sbsa_max_cpus; - if (!cpu_type_valid(machine->cpu_type)) { - error_report("sbsa-ref: CPU type %s not supported", machine->cpu_type); - exit(1); - } - if (kvm_enabled()) { error_report("sbsa-ref: KVM is not supported for this machine"); exit(1); @@ -770,7 +784,7 @@ static void sbsa_ref_init(MachineState *machine) create_secure_ram(sms, secure_sysmem); - create_gic(sms); + create_gic(sms, sysmem); create_uart(sms, SBSA_UART, sysmem, serial_hd(0)); create_uart(sms, SBSA_SECURE_UART, secure_sysmem, serial_hd(1)); @@ -785,7 +799,7 @@ static void sbsa_ref_init(MachineState *machine) create_ahci(sms); - create_ehci(sms); + create_xhci(sms); create_pcie(sms); @@ -849,21 +863,34 @@ static void sbsa_ref_instance_init(Object *obj) static void sbsa_ref_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a57"), + ARM_CPU_TYPE_NAME("cortex-a72"), + ARM_CPU_TYPE_NAME("neoverse-n1"), + ARM_CPU_TYPE_NAME("neoverse-v1"), + ARM_CPU_TYPE_NAME("neoverse-n2"), + ARM_CPU_TYPE_NAME("max"), + NULL, + }; mc->init = sbsa_ref_init; mc->desc = "QEMU 'SBSA Reference' ARM Virtual Machine"; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a57"); + mc->default_cpu_type = ARM_CPU_TYPE_NAME("neoverse-n1"); + mc->valid_cpu_types = valid_cpu_types; mc->max_cpus = 512; mc->pci_allow_0_address = true; mc->minimum_page_bits = 12; mc->block_default_type = IF_IDE; mc->no_cdrom = 1; + mc->default_nic = "e1000e"; 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; + /* platform instead of architectural choice */ + mc->cpu_cluster_has_numa_boundary = true; } static const TypeInfo sbsa_ref_info = { diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index e09b9c13b7..c4b540656c 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -38,7 +38,7 @@ static guint smmu_iotlb_key_hash(gconstpointer v) /* Jenkins hash */ a = b = c = JHASH_INITVAL + sizeof(*key); - a += key->asid + key->level + key->tg; + a += key->asid + key->vmid + key->level + key->tg; b += extract64(key->iova, 0, 32); c += extract64(key->iova, 32, 32); @@ -53,13 +53,15 @@ static gboolean smmu_iotlb_key_equal(gconstpointer v1, gconstpointer v2) SMMUIOTLBKey *k1 = (SMMUIOTLBKey *)v1, *k2 = (SMMUIOTLBKey *)v2; return (k1->asid == k2->asid) && (k1->iova == k2->iova) && - (k1->level == k2->level) && (k1->tg == k2->tg); + (k1->level == k2->level) && (k1->tg == k2->tg) && + (k1->vmid == k2->vmid); } -SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint64_t iova, +SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint16_t vmid, uint64_t iova, uint8_t tg, uint8_t level) { - SMMUIOTLBKey key = {.asid = asid, .iova = iova, .tg = tg, .level = level}; + SMMUIOTLBKey key = {.asid = asid, .vmid = vmid, .iova = iova, + .tg = tg, .level = level}; return key; } @@ -78,7 +80,8 @@ SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, uint64_t mask = subpage_size - 1; SMMUIOTLBKey key; - key = smmu_get_iotlb_key(cfg->asid, iova & ~mask, tg, level); + key = smmu_get_iotlb_key(cfg->asid, cfg->s2cfg.vmid, + iova & ~mask, tg, level); entry = g_hash_table_lookup(bs->iotlb, &key); if (entry) { break; @@ -88,13 +91,13 @@ SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, if (entry) { cfg->iotlb_hits++; - trace_smmu_iotlb_lookup_hit(cfg->asid, iova, + trace_smmu_iotlb_lookup_hit(cfg->asid, cfg->s2cfg.vmid, iova, cfg->iotlb_hits, cfg->iotlb_misses, 100 * cfg->iotlb_hits / (cfg->iotlb_hits + cfg->iotlb_misses)); } else { cfg->iotlb_misses++; - trace_smmu_iotlb_lookup_miss(cfg->asid, iova, + trace_smmu_iotlb_lookup_miss(cfg->asid, cfg->s2cfg.vmid, iova, cfg->iotlb_hits, cfg->iotlb_misses, 100 * cfg->iotlb_hits / (cfg->iotlb_hits + cfg->iotlb_misses)); @@ -111,12 +114,14 @@ void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *new) smmu_iotlb_inv_all(bs); } - *key = smmu_get_iotlb_key(cfg->asid, new->entry.iova, tg, new->level); - trace_smmu_iotlb_insert(cfg->asid, new->entry.iova, tg, new->level); + *key = smmu_get_iotlb_key(cfg->asid, cfg->s2cfg.vmid, new->entry.iova, + tg, new->level); + trace_smmu_iotlb_insert(cfg->asid, cfg->s2cfg.vmid, new->entry.iova, + tg, new->level); g_hash_table_insert(bs->iotlb, key, new); } -inline void smmu_iotlb_inv_all(SMMUState *s) +void smmu_iotlb_inv_all(SMMUState *s) { trace_smmu_iotlb_inv_all(); g_hash_table_remove_all(s->iotlb); @@ -131,7 +136,16 @@ static gboolean smmu_hash_remove_by_asid(gpointer key, gpointer value, return SMMU_IOTLB_ASID(*iotlb_key) == asid; } -static gboolean smmu_hash_remove_by_asid_iova(gpointer key, gpointer value, +static gboolean smmu_hash_remove_by_vmid(gpointer key, gpointer value, + gpointer user_data) +{ + uint16_t vmid = *(uint16_t *)user_data; + SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key; + + return SMMU_IOTLB_VMID(*iotlb_key) == vmid; +} + +static gboolean smmu_hash_remove_by_asid_vmid_iova(gpointer key, gpointer value, gpointer user_data) { SMMUTLBEntry *iter = (SMMUTLBEntry *)value; @@ -142,19 +156,21 @@ static gboolean smmu_hash_remove_by_asid_iova(gpointer key, gpointer value, if (info->asid >= 0 && info->asid != SMMU_IOTLB_ASID(iotlb_key)) { return false; } + if (info->vmid >= 0 && info->vmid != SMMU_IOTLB_VMID(iotlb_key)) { + return false; + } return ((info->iova & ~entry->addr_mask) == entry->iova) || ((entry->iova & ~info->mask) == info->iova); } -inline void -smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova, - uint8_t tg, uint64_t num_pages, uint8_t ttl) +void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, + uint8_t tg, uint64_t num_pages, uint8_t ttl) { /* if tg is not set we use 4KB range invalidation */ uint8_t granule = tg ? tg * 2 + 10 : 12; if (ttl && (num_pages == 1) && (asid >= 0)) { - SMMUIOTLBKey key = smmu_get_iotlb_key(asid, iova, tg, ttl); + SMMUIOTLBKey key = smmu_get_iotlb_key(asid, vmid, iova, tg, ttl); if (g_hash_table_remove(s->iotlb, &key)) { return; @@ -167,19 +183,26 @@ smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova, SMMUIOTLBPageInvInfo info = { .asid = asid, .iova = iova, + .vmid = vmid, .mask = (num_pages * 1 << granule) - 1}; g_hash_table_foreach_remove(s->iotlb, - smmu_hash_remove_by_asid_iova, + smmu_hash_remove_by_asid_vmid_iova, &info); } -inline void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid) +void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid) { trace_smmu_iotlb_inv_asid(asid); g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid, &asid); } +void smmu_iotlb_inv_vmid(SMMUState *s, uint16_t vmid) +{ + trace_smmu_iotlb_inv_vmid(vmid); + g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid, &vmid); +} + /* VMSAv8-64 Translation */ /** @@ -193,8 +216,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, pte, sizeof(*pte), - MEMTXATTRS_UNSPECIFIED); + ret = ldq_le_dma(&address_space_memory, addr, pte, MEMTXATTRS_UNSPECIFIED); if (ret != MEMTX_OK) { info->type = SMMU_PTW_ERR_WALK_EABT; @@ -250,7 +272,7 @@ SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova) /* there is a ttbr0 region and we are in it (high bits all zero) */ return &cfg->tt[0]; } else if (cfg->tt[1].tsz && - !extract64(iova, 64 - cfg->tt[1].tsz, cfg->tt[1].tsz - tbi_byte)) { + sextract64(iova, 64 - cfg->tt[1].tsz, cfg->tt[1].tsz - tbi_byte) == -1) { /* there is a ttbr1 region and we are in it (high bits all one) */ return &cfg->tt[1]; } else if (!cfg->tt[0].tsz) { @@ -265,7 +287,7 @@ SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova) } /** - * smmu_ptw_64 - VMSAv8-64 Walk of the page tables for a given IOVA + * smmu_ptw_64_s1 - VMSAv8-64 Walk of the page tables for a given IOVA * @cfg: translation config * @iova: iova to translate * @perm: access type @@ -277,9 +299,9 @@ SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova) * Upon success, @tlbe is filled with translated_addr and entry * permission rights. */ -static int smmu_ptw_64(SMMUTransCfg *cfg, - dma_addr_t iova, IOMMUAccessFlags perm, - SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) +static int smmu_ptw_64_s1(SMMUTransCfg *cfg, + dma_addr_t iova, IOMMUAccessFlags perm, + SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) { dma_addr_t baseaddr, indexmask; int stage = cfg->stage; @@ -292,14 +314,14 @@ static int smmu_ptw_64(SMMUTransCfg *cfg, } granule_sz = tt->granule_sz; - stride = granule_sz - 3; + stride = VMSA_STRIDE(granule_sz); inputsize = 64 - tt->tsz; level = 4 - (inputsize - 4) / stride; - indexmask = (1ULL << (inputsize - (stride * (4 - level)))) - 1; + indexmask = VMSA_IDXMSK(inputsize, stride, level); baseaddr = extract64(tt->ttb, 0, 48); baseaddr &= ~indexmask; - while (level <= 3) { + while (level < VMSA_LEVELS) { uint64_t subpage_size = 1ULL << level_shift(level, granule_sz); uint64_t mask = subpage_size - 1; uint32_t offset = iova_level_offset(iova, inputsize, level, granule_sz); @@ -310,7 +332,7 @@ static int smmu_ptw_64(SMMUTransCfg *cfg, if (get_pte(baseaddr, offset, &pte, info)) { goto error; } - trace_smmu_ptw_level(level, iova, subpage_size, + trace_smmu_ptw_level(stage, level, iova, subpage_size, baseaddr, offset, pte); if (is_invalid_pte(pte) || is_reserved_pte(pte, level)) { @@ -342,6 +364,17 @@ static int smmu_ptw_64(SMMUTransCfg *cfg, pte_addr, pte, iova, gpa, block_size >> 20); } + + /* + * QEMU does not currently implement HTTU, so if AFFD and PTE.AF + * are 0 we take an Access flag fault. (5.4. Context Descriptor) + * An Access flag fault takes priority over a Permission fault. + */ + if (!PTE_AF(pte) && !cfg->affd) { + info->type = SMMU_PTW_ERR_ACCESS; + goto error; + } + ap = PTE_AP(pte); if (is_permission_fault(ap, perm)) { info->type = SMMU_PTW_ERR_PERMISSION; @@ -359,6 +392,128 @@ static int smmu_ptw_64(SMMUTransCfg *cfg, info->type = SMMU_PTW_ERR_TRANSLATION; error: + info->stage = 1; + tlbe->entry.perm = IOMMU_NONE; + return -EINVAL; +} + +/** + * smmu_ptw_64_s2 - VMSAv8-64 Walk of the page tables for a given ipa + * for stage-2. + * @cfg: translation config + * @ipa: ipa to translate + * @perm: access type + * @tlbe: SMMUTLBEntry (out) + * @info: handle to an error info + * + * Return 0 on success, < 0 on error. In case of error, @info is filled + * and tlbe->perm is set to IOMMU_NONE. + * Upon success, @tlbe is filled with translated_addr and entry + * permission rights. + */ +static int smmu_ptw_64_s2(SMMUTransCfg *cfg, + dma_addr_t ipa, IOMMUAccessFlags perm, + SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) +{ + const int stage = 2; + int granule_sz = cfg->s2cfg.granule_sz; + /* ARM DDI0487I.a: Table D8-7. */ + int inputsize = 64 - cfg->s2cfg.tsz; + int level = get_start_level(cfg->s2cfg.sl0, granule_sz); + int stride = VMSA_STRIDE(granule_sz); + int idx = pgd_concat_idx(level, granule_sz, ipa); + /* + * Get the ttb from concatenated structure. + * The offset is the idx * size of each ttb(number of ptes * (sizeof(pte)) + */ + uint64_t baseaddr = extract64(cfg->s2cfg.vttb, 0, 48) + (1 << stride) * + idx * sizeof(uint64_t); + dma_addr_t indexmask = VMSA_IDXMSK(inputsize, stride, level); + + baseaddr &= ~indexmask; + + /* + * On input, a stage 2 Translation fault occurs if the IPA is outside the + * range configured by the relevant S2T0SZ field of the STE. + */ + if (ipa >= (1ULL << inputsize)) { + info->type = SMMU_PTW_ERR_TRANSLATION; + goto error; + } + + while (level < VMSA_LEVELS) { + uint64_t subpage_size = 1ULL << level_shift(level, granule_sz); + uint64_t mask = subpage_size - 1; + uint32_t offset = iova_level_offset(ipa, inputsize, level, granule_sz); + uint64_t pte, gpa; + dma_addr_t pte_addr = baseaddr + offset * sizeof(pte); + uint8_t s2ap; + + if (get_pte(baseaddr, offset, &pte, info)) { + goto error; + } + trace_smmu_ptw_level(stage, level, ipa, subpage_size, + baseaddr, offset, pte); + if (is_invalid_pte(pte) || is_reserved_pte(pte, level)) { + trace_smmu_ptw_invalid_pte(stage, level, baseaddr, + pte_addr, offset, pte); + break; + } + + if (is_table_pte(pte, level)) { + baseaddr = get_table_pte_address(pte, granule_sz); + level++; + continue; + } else if (is_page_pte(pte, level)) { + gpa = get_page_pte_address(pte, granule_sz); + trace_smmu_ptw_page_pte(stage, level, ipa, + baseaddr, pte_addr, pte, gpa); + } else { + uint64_t block_size; + + gpa = get_block_pte_address(pte, level, granule_sz, + &block_size); + trace_smmu_ptw_block_pte(stage, level, baseaddr, + pte_addr, pte, ipa, gpa, + block_size >> 20); + } + + /* + * If S2AFFD and PTE.AF are 0 => fault. (5.2. Stream Table Entry) + * An Access fault takes priority over a Permission fault. + */ + if (!PTE_AF(pte) && !cfg->s2cfg.affd) { + info->type = SMMU_PTW_ERR_ACCESS; + goto error; + } + + s2ap = PTE_AP(pte); + if (is_permission_fault_s2(s2ap, perm)) { + info->type = SMMU_PTW_ERR_PERMISSION; + goto error; + } + + /* + * The address output from the translation causes a stage 2 Address + * Size fault if it exceeds the effective PA output range. + */ + if (gpa >= (1ULL << cfg->s2cfg.eff_ps)) { + info->type = SMMU_PTW_ERR_ADDR_SIZE; + goto error; + } + + tlbe->entry.translated_addr = gpa; + tlbe->entry.iova = ipa & ~mask; + tlbe->entry.addr_mask = mask; + tlbe->entry.perm = s2ap; + tlbe->level = level; + tlbe->granule = granule_sz; + return 0; + } + info->type = SMMU_PTW_ERR_TRANSLATION; + +error: + info->stage = 2; tlbe->entry.perm = IOMMU_NONE; return -EINVAL; } @@ -374,18 +529,29 @@ error: * * return 0 on success */ -inline int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, - SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) +int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, + SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) { - if (!cfg->aa64) { + if (cfg->stage == 1) { + return smmu_ptw_64_s1(cfg, iova, perm, tlbe, info); + } else if (cfg->stage == 2) { /* - * This code path is not entered as we check this while decoding - * the configuration data in the derived SMMU model. + * If bypassing stage 1(or unimplemented), the input address is passed + * directly to stage 2 as IPA. If the input address of a transaction + * exceeds the size of the IAS, a stage 1 Address Size fault occurs. + * For AA64, IAS = OAS according to (IHI 0070.E.a) "3.4 Address sizes" */ - g_assert_not_reached(); + if (iova >= (1ULL << cfg->oas)) { + info->type = SMMU_PTW_ERR_ADDR_SIZE; + info->stage = 1; + tlbe->entry.perm = IOMMU_NONE; + return -EINVAL; + } + + return smmu_ptw_64_s2(cfg, iova, perm, tlbe, info); } - return smmu_ptw_64(cfg, iova, perm, tlbe, info); + g_assert_not_reached(); } /** @@ -440,7 +606,7 @@ static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn) memory_region_init_iommu(&sdev->iommu, sizeof(sdev->iommu), s->mrtypename, - OBJECT(s), name, 1ULL << SMMU_MAX_VA_BITS); + OBJECT(s), name, UINT64_MAX); address_space_init(&sdev->as, MEMORY_REGION(&sdev->iommu), name); trace_smmu_add_mr(name); @@ -450,6 +616,10 @@ static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn) return &sdev->as; } +static const PCIIOMMUOps smmu_ops = { + .get_address_space = smmu_find_add_as, +}; + IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid) { uint8_t bus_n, devfn; @@ -468,28 +638,14 @@ IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid) return NULL; } -/* Unmap the whole notifier's range */ -static void smmu_unmap_notifier_range(IOMMUNotifier *n) -{ - IOMMUTLBEvent event; - - event.type = IOMMU_NOTIFIER_UNMAP; - event.entry.target_as = &address_space_memory; - event.entry.iova = n->start; - event.entry.perm = IOMMU_NONE; - event.entry.addr_mask = n->end - n->start; - - memory_region_notify_iommu_one(n, &event); -} - /* Unmap all notifiers attached to @mr */ -inline void smmu_inv_notifiers_mr(IOMMUMemoryRegion *mr) +static void smmu_inv_notifiers_mr(IOMMUMemoryRegion *mr) { IOMMUNotifier *n; trace_smmu_inv_notifiers_mr(mr->parent_obj.name); IOMMU_NOTIFIER_FOREACH(n, mr) { - smmu_unmap_notifier_range(n); + memory_region_unmap_iommu_notifier_range(n); } } @@ -520,15 +676,17 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL); if (s->primary_bus) { - pci_setup_iommu(s->primary_bus, smmu_find_add_as, s); + pci_setup_iommu(s->primary_bus, &smmu_ops, s); } else { error_setg(errp, "SMMU is not attached to any PCI bus!"); } } -static void smmu_base_reset(DeviceState *dev) +static void smmu_base_reset_hold(Object *obj) { - SMMUState *s = ARM_SMMU(dev); + SMMUState *s = ARM_SMMU(obj); + + memset(s->smmu_pcibus_by_bus_num, 0, sizeof(s->smmu_pcibus_by_bus_num)); g_hash_table_remove_all(s->configs); g_hash_table_remove_all(s->iotlb); @@ -536,19 +694,21 @@ static void smmu_base_reset(DeviceState *dev) static Property smmu_dev_properties[] = { DEFINE_PROP_UINT8("bus_num", SMMUState, bus_num, 0), - DEFINE_PROP_LINK("primary-bus", SMMUState, primary_bus, "PCI", PCIBus *), + DEFINE_PROP_LINK("primary-bus", SMMUState, primary_bus, + TYPE_PCI_BUS, PCIBus *), DEFINE_PROP_END_OF_LIST(), }; static void smmu_base_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); SMMUBaseClass *sbc = ARM_SMMU_CLASS(klass); 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; + rc->phases.hold = smmu_base_reset_hold; } static const TypeInfo smmu_base_info = { diff --git a/hw/arm/smmu-internal.h b/hw/arm/smmu-internal.h index 2d75b31953..843bebb185 100644 --- a/hw/arm/smmu-internal.h +++ b/hw/arm/smmu-internal.h @@ -66,6 +66,8 @@ #define PTE_APTABLE(pte) \ (extract64(pte, 61, 2)) +#define PTE_AF(pte) \ + (extract64(pte, 10, 1)) /* * TODO: At the moment all transactions are considered as privileged (EL1) * as IOMMU translation callback does not pass user/priv attributes. @@ -73,6 +75,9 @@ #define is_permission_fault(ap, perm) \ (((perm) & IOMMU_WO) && ((ap) & 0x2)) +#define is_permission_fault_s2(s2ap, perm) \ + (!(((s2ap) & (perm)) == (perm))) + #define PTE_AP_TO_PERM(ap) \ (IOMMU_ACCESS_FLAG(true, !((ap) & 0x2))) @@ -96,10 +101,42 @@ uint64_t iova_level_offset(uint64_t iova, int inputsize, MAKE_64BIT_MASK(0, gsz - 3); } +/* FEAT_LPA2 and FEAT_TTST are not implemented. */ +static inline int get_start_level(int sl0 , int granule_sz) +{ + /* ARM DDI0487I.a: Table D8-12. */ + if (granule_sz == 12) { + return 2 - sl0; + } + /* ARM DDI0487I.a: Table D8-22 and Table D8-31. */ + return 3 - sl0; +} + +/* + * Index in a concatenated first level stage-2 page table. + * ARM DDI0487I.a: D8.2.2 Concatenated translation tables. + */ +static inline int pgd_concat_idx(int start_level, int granule_sz, + dma_addr_t ipa) +{ + uint64_t ret; + /* + * Get the number of bits handled by next levels, then any extra bits in + * the address should index the concatenated tables. This relation can be + * deduced from tables in ARM DDI0487I.a: D8.2.7-9 + */ + int shift = level_shift(start_level - 1, granule_sz); + + ret = ipa >> shift; + return ret; +} + #define SMMU_IOTLB_ASID(key) ((key).asid) +#define SMMU_IOTLB_VMID(key) ((key).vmid) typedef struct SMMUIOTLBPageInvInfo { int asid; + int vmid; uint64_t iova; uint64_t mask; } SMMUIOTLBPageInvInfo; diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index bce161870f..e4dd11e1e6 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -21,6 +21,7 @@ #ifndef HW_ARM_SMMUV3_INTERNAL_H #define HW_ARM_SMMUV3_INTERNAL_H +#include "hw/registerfields.h" #include "hw/arm/smmu-common.h" typedef enum SMMUTranslationStatus { @@ -34,35 +35,75 @@ typedef enum SMMUTranslationStatus { /* MMIO Registers */ REG32(IDR0, 0x0) + FIELD(IDR0, S2P, 0 , 1) FIELD(IDR0, S1P, 1 , 1) FIELD(IDR0, TTF, 2 , 2) FIELD(IDR0, COHACC, 4 , 1) + FIELD(IDR0, BTM, 5 , 1) + FIELD(IDR0, HTTU, 6 , 2) + FIELD(IDR0, DORMHINT, 8 , 1) + FIELD(IDR0, HYP, 9 , 1) + FIELD(IDR0, ATS, 10, 1) + FIELD(IDR0, NS1ATS, 11, 1) FIELD(IDR0, ASID16, 12, 1) + FIELD(IDR0, MSI, 13, 1) + FIELD(IDR0, SEV, 14, 1) + FIELD(IDR0, ATOS, 15, 1) + FIELD(IDR0, PRI, 16, 1) + FIELD(IDR0, VMW, 17, 1) + FIELD(IDR0, VMID16, 18, 1) + FIELD(IDR0, CD2L, 19, 1) + FIELD(IDR0, VATOS, 20, 1) FIELD(IDR0, TTENDIAN, 21, 2) + FIELD(IDR0, ATSRECERR, 23, 1) FIELD(IDR0, STALL_MODEL, 24, 2) FIELD(IDR0, TERM_MODEL, 26, 1) FIELD(IDR0, STLEVEL, 27, 2) + FIELD(IDR0, RME_IMPL, 30, 1) REG32(IDR1, 0x4) FIELD(IDR1, SIDSIZE, 0 , 6) + FIELD(IDR1, SSIDSIZE, 6 , 5) + FIELD(IDR1, PRIQS, 11, 5) FIELD(IDR1, EVENTQS, 16, 5) FIELD(IDR1, CMDQS, 21, 5) + FIELD(IDR1, ATTR_PERMS_OVR, 26, 1) + FIELD(IDR1, ATTR_TYPES_OVR, 27, 1) + FIELD(IDR1, REL, 28, 1) + FIELD(IDR1, QUEUES_PRESET, 29, 1) + FIELD(IDR1, TABLES_PRESET, 30, 1) + FIELD(IDR1, ECMDQ, 31, 1) #define SMMU_IDR1_SIDSIZE 16 #define SMMU_CMDQS 19 #define SMMU_EVENTQS 19 REG32(IDR2, 0x8) + FIELD(IDR2, BA_VATOS, 0, 10) + REG32(IDR3, 0xc) FIELD(IDR3, HAD, 2, 1); + FIELD(IDR3, PBHA, 3, 1); + FIELD(IDR3, XNX, 4, 1); + FIELD(IDR3, PPS, 5, 1); + FIELD(IDR3, MPAM, 7, 1); + FIELD(IDR3, FWB, 8, 1); + FIELD(IDR3, STT, 9, 1); FIELD(IDR3, RIL, 10, 1); FIELD(IDR3, BBML, 11, 2); + FIELD(IDR3, E0PD, 13, 1); + FIELD(IDR3, PTWNNC, 14, 1); + FIELD(IDR3, DPT, 15, 1); + REG32(IDR4, 0x10) + REG32(IDR5, 0x14) FIELD(IDR5, OAS, 0, 3); FIELD(IDR5, GRAN4K, 4, 1); FIELD(IDR5, GRAN16K, 5, 1); FIELD(IDR5, GRAN64K, 6, 1); + FIELD(IDR5, VAX, 10, 2); + FIELD(IDR5, STALL_MAX, 16, 16); #define SMMU_IDR5_OAS 4 @@ -79,6 +120,13 @@ REG32(CR0ACK, 0x24) REG32(CR1, 0x28) REG32(CR2, 0x2c) REG32(STATUSR, 0x40) +REG32(GBPA, 0x44) + FIELD(GBPA, ABORT, 20, 1) + FIELD(GBPA, UPDATE, 31, 1) + +/* Use incoming. */ +#define SMMU_GBPA_RESET_VAL 0x1000 + REG32(IRQ_CTRL, 0x50) FIELD(IRQ_CTRL, GERROR_IRQEN, 0, 1) FIELD(IRQ_CTRL, PRI_IRQEN, 1, 1) @@ -319,12 +367,9 @@ enum { /* Command completion notification */ #define CMD_TTL(x) extract32((x)->word[2], 8 , 2) #define CMD_TG(x) extract32((x)->word[2], 10, 2) #define CMD_STE_RANGE(x) extract32((x)->word[2], 0 , 5) -#define CMD_ADDR(x) ({ \ - uint64_t high = (uint64_t)(x)->word[3]; \ - uint64_t low = extract32((x)->word[2], 12, 20); \ - uint64_t addr = high << 32 | (low << 12); \ - addr; \ - }) +#define CMD_ADDR(x) \ + (((uint64_t)((x)->word[3]) << 32) | \ + ((extract64((x)->word[2], 12, 20)) << 12)) #define SMMU_FEATURE_2LVL_STE (1 << 0) @@ -517,24 +562,20 @@ typedef struct CD { #define STE_S2TG(x) extract32((x)->word[5], 14, 2) #define STE_S2PS(x) extract32((x)->word[5], 16, 3) #define STE_S2AA64(x) extract32((x)->word[5], 19, 1) -#define STE_S2HD(x) extract32((x)->word[5], 24, 1) -#define STE_S2HA(x) extract32((x)->word[5], 25, 1) -#define STE_S2S(x) extract32((x)->word[5], 26, 1) -#define STE_CTXPTR(x) \ - ({ \ - unsigned long addr; \ - addr = (uint64_t)extract32((x)->word[1], 0, 16) << 32; \ - addr |= (uint64_t)((x)->word[0] & 0xffffffc0); \ - addr; \ - }) +#define STE_S2ENDI(x) extract32((x)->word[5], 20, 1) +#define STE_S2AFFD(x) extract32((x)->word[5], 21, 1) +#define STE_S2HD(x) extract32((x)->word[5], 23, 1) +#define STE_S2HA(x) extract32((x)->word[5], 24, 1) +#define STE_S2S(x) extract32((x)->word[5], 25, 1) +#define STE_S2R(x) extract32((x)->word[5], 26, 1) -#define STE_S2TTB(x) \ - ({ \ - unsigned long addr; \ - addr = (uint64_t)extract32((x)->word[7], 0, 16) << 32; \ - addr |= (uint64_t)((x)->word[6] & 0xfffffff0); \ - addr; \ - }) +#define STE_CTXPTR(x) \ + ((extract64((x)->word[1], 0, 16) << 32) | \ + ((x)->word[0] & 0xffffffc0)) + +#define STE_S2TTB(x) \ + ((extract64((x)->word[7], 0, 16) << 32) | \ + ((x)->word[6] & 0xfffffff0)) static inline int oas2bits(int oas_field) { @@ -572,14 +613,10 @@ static inline int pa_range(STE *ste) #define CD_VALID(x) extract32((x)->word[0], 31, 1) #define CD_ASID(x) extract32((x)->word[1], 16, 16) -#define CD_TTB(x, sel) \ - ({ \ - uint64_t hi, lo; \ - hi = extract32((x)->word[(sel) * 2 + 3], 0, 19); \ - hi <<= 32; \ - lo = (x)->word[(sel) * 2 + 2] & ~0xfULL; \ - hi | lo; \ - }) +#define CD_TTB(x, sel) \ + ((extract64((x)->word[(sel) * 2 + 3], 0, 19) << 32) | \ + ((x)->word[(sel) * 2 + 2] & ~0xfULL)) + #define CD_HAD(x, sel) extract32((x)->word[(sel) * 2 + 2], 1, 1) #define CD_TSZ(x, sel) extract32((x)->word[0], (16 * (sel)) + 0, 6) @@ -587,6 +624,7 @@ static inline int pa_range(STE *ste) #define CD_EPD(x, sel) extract32((x)->word[0], (16 * (sel)) + 14, 1) #define CD_ENDI(x) extract32((x)->word[0], 15, 1) #define CD_IPS(x) extract32((x)->word[1], 0 , 3) +#define CD_AFFD(x) extract32((x)->word[1], 3 , 1) #define CD_TBI(x) extract32((x)->word[1], 6 , 2) #define CD_HD(x) extract32((x)->word[1], 10 , 1) #define CD_HA(x) extract32((x)->word[1], 11 , 1) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index daa80e9c7b..9eb56a70f3 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -21,6 +21,7 @@ #include "hw/irq.h" #include "hw/sysbus.h" #include "migration/vmstate.h" +#include "hw/qdev-properties.h" #include "hw/qdev-core.h" #include "hw/pci/pci.h" #include "cpu.h" @@ -33,6 +34,9 @@ #include "smmuv3-internal.h" #include "smmu-internal.h" +#define PTW_RECORD_FAULT(cfg) (((cfg)->stage == 1) ? (cfg)->record_faults : \ + (cfg)->s2cfg.record_faults) + /** * smmuv3_trigger_irq - pulse @irq if enabled and update * GERROR register in case of GERROR interrupt @@ -98,20 +102,34 @@ static void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn) trace_smmuv3_write_gerrorn(toggled & pending, s->gerrorn); } -static inline MemTxResult queue_read(SMMUQueue *q, void *data) +static inline MemTxResult queue_read(SMMUQueue *q, Cmd *cmd) { dma_addr_t addr = Q_CONS_ENTRY(q); + MemTxResult ret; + int i; - return dma_memory_read(&address_space_memory, addr, data, q->entry_size, - MEMTXATTRS_UNSPECIFIED); + ret = dma_memory_read(&address_space_memory, addr, cmd, sizeof(Cmd), + MEMTXATTRS_UNSPECIFIED); + if (ret != MEMTX_OK) { + return ret; + } + for (i = 0; i < ARRAY_SIZE(cmd->word); i++) { + le32_to_cpus(&cmd->word[i]); + } + return ret; } -static MemTxResult queue_write(SMMUQueue *q, void *data) +static MemTxResult queue_write(SMMUQueue *q, Evt *evt_in) { dma_addr_t addr = Q_PROD_ENTRY(q); MemTxResult ret; + Evt evt = *evt_in; + int i; - ret = dma_memory_write(&address_space_memory, addr, data, q->entry_size, + for (i = 0; i < ARRAY_SIZE(evt.word); i++) { + cpu_to_le32s(&evt.word[i]); + } + ret = dma_memory_write(&address_space_memory, addr, &evt, sizeof(Evt), MEMTXATTRS_UNSPECIFIED); if (ret != MEMTX_OK) { return ret; @@ -238,14 +256,17 @@ void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *info) static void smmuv3_init_regs(SMMUv3State *s) { - /** - * IDR0: stage1 only, AArch64 only, coherent access, 16b ASID, - * multi-level stream table - */ - s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S1P, 1); /* stage 1 supported */ + /* Based on sys property, the stages supported in smmu will be advertised.*/ + if (s->stage && !strcmp("2", s->stage)) { + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S2P, 1); + } else { + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S1P, 1); + } + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, TTF, 2); /* AArch64 PTW only */ s->idr[0] = FIELD_DP32(s->idr[0], IDR0, COHACC, 1); /* IO coherent */ s->idr[0] = FIELD_DP32(s->idr[0], IDR0, ASID16, 1); /* 16-bit ASID */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, VMID16, 1); /* 16-bit VMID */ s->idr[0] = FIELD_DP32(s->idr[0], IDR0, TTENDIAN, 2); /* little endian */ s->idr[0] = FIELD_DP32(s->idr[0], IDR0, STALL_MODEL, 1); /* No stall */ /* terminated transaction will always be aborted/error returned */ @@ -257,15 +278,19 @@ static void smmuv3_init_regs(SMMUv3State *s) s->idr[1] = FIELD_DP32(s->idr[1], IDR1, EVENTQS, SMMU_EVENTQS); s->idr[1] = FIELD_DP32(s->idr[1], IDR1, CMDQS, SMMU_CMDQS); - s->idr[3] = FIELD_DP32(s->idr[3], IDR3, RIL, 1); s->idr[3] = FIELD_DP32(s->idr[3], IDR3, HAD, 1); + if (FIELD_EX32(s->idr[0], IDR0, S2P)) { + /* XNX is a stage-2-specific feature */ + s->idr[3] = FIELD_DP32(s->idr[3], IDR3, XNX, 1); + } + s->idr[3] = FIELD_DP32(s->idr[3], IDR3, RIL, 1); s->idr[3] = FIELD_DP32(s->idr[3], IDR3, BBML, 2); + s->idr[5] = FIELD_DP32(s->idr[5], IDR5, OAS, SMMU_IDR5_OAS); /* 44 bits */ /* 4K, 16K and 64K granule support */ s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN4K, 1); s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN16K, 1); s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN64K, 1); - s->idr[5] = FIELD_DP32(s->idr[5], IDR5, OAS, SMMU_IDR5_OAS); /* 44 bits */ s->cmdq.base = deposit64(s->cmdq.base, 0, 5, SMMU_CMDQS); s->cmdq.prod = 0; @@ -285,12 +310,13 @@ static void smmuv3_init_regs(SMMUv3State *s) s->gerror = 0; s->gerrorn = 0; s->statusr = 0; + s->gbpa = SMMU_GBPA_RESET_VAL; } static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf, SMMUEventInfo *event) { - int ret; + int ret, i; trace_smmuv3_get_ste(addr); /* TODO: guarantee 64-bit single-copy atomicity */ @@ -303,6 +329,9 @@ static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf, event->u.f_ste_fetch.addr = addr; return -EINVAL; } + for (i = 0; i < ARRAY_SIZE(buf->word); i++) { + le32_to_cpus(&buf->word[i]); + } return 0; } @@ -312,7 +341,7 @@ static int smmu_get_cd(SMMUv3State *s, STE *ste, uint32_t ssid, CD *buf, SMMUEventInfo *event) { dma_addr_t addr = STE_CTXPTR(ste); - int ret; + int ret, i; trace_smmuv3_get_cd(addr); /* TODO: guarantee 64-bit single-copy atomicity */ @@ -325,14 +354,144 @@ static int smmu_get_cd(SMMUv3State *s, STE *ste, uint32_t ssid, event->u.f_ste_fetch.addr = addr; return -EINVAL; } + for (i = 0; i < ARRAY_SIZE(buf->word); i++) { + le32_to_cpus(&buf->word[i]); + } return 0; } +/* + * Max valid value is 39 when SMMU_IDR3.STT == 0. + * In architectures after SMMUv3.0: + * - If STE.S2TG selects a 4KB or 16KB granule, the minimum valid value for this + * field is MAX(16, 64-IAS) + * - If STE.S2TG selects a 64KB granule, the minimum valid value for this field + * is (64-IAS). + * As we only support AA64, IAS = OAS. + */ +static bool s2t0sz_valid(SMMUTransCfg *cfg) +{ + if (cfg->s2cfg.tsz > 39) { + return false; + } + + if (cfg->s2cfg.granule_sz == 16) { + return (cfg->s2cfg.tsz >= 64 - oas2bits(SMMU_IDR5_OAS)); + } + + return (cfg->s2cfg.tsz >= MAX(64 - oas2bits(SMMU_IDR5_OAS), 16)); +} + +/* + * Return true if s2 page table config is valid. + * This checks with the configured start level, ias_bits and granularity we can + * have a valid page table as described in ARM ARM D8.2 Translation process. + * The idea here is to see for the highest possible number of IPA bits, how + * many concatenated tables we would need, if it is more than 16, then this is + * not possible. + */ +static bool s2_pgtable_config_valid(uint8_t sl0, uint8_t t0sz, uint8_t gran) +{ + int level = get_start_level(sl0, gran); + uint64_t ipa_bits = 64 - t0sz; + uint64_t max_ipa = (1ULL << ipa_bits) - 1; + int nr_concat = pgd_concat_idx(level, gran, max_ipa) + 1; + + return nr_concat <= VMSA_MAX_S2_CONCAT; +} + +static int decode_ste_s2_cfg(SMMUTransCfg *cfg, STE *ste) +{ + cfg->stage = 2; + + if (STE_S2AA64(ste) == 0x0) { + qemu_log_mask(LOG_UNIMP, + "SMMUv3 AArch32 tables not supported\n"); + g_assert_not_reached(); + } + + switch (STE_S2TG(ste)) { + case 0x0: /* 4KB */ + cfg->s2cfg.granule_sz = 12; + break; + case 0x1: /* 64KB */ + cfg->s2cfg.granule_sz = 16; + break; + case 0x2: /* 16KB */ + cfg->s2cfg.granule_sz = 14; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "SMMUv3 bad STE S2TG: %x\n", STE_S2TG(ste)); + goto bad_ste; + } + + cfg->s2cfg.vttb = STE_S2TTB(ste); + + cfg->s2cfg.sl0 = STE_S2SL0(ste); + /* FEAT_TTST not supported. */ + if (cfg->s2cfg.sl0 == 0x3) { + qemu_log_mask(LOG_UNIMP, "SMMUv3 S2SL0 = 0x3 has no meaning!\n"); + goto bad_ste; + } + + /* For AA64, The effective S2PS size is capped to the OAS. */ + cfg->s2cfg.eff_ps = oas2bits(MIN(STE_S2PS(ste), SMMU_IDR5_OAS)); + /* + * It is ILLEGAL for the address in S2TTB to be outside the range + * described by the effective S2PS value. + */ + if (cfg->s2cfg.vttb & ~(MAKE_64BIT_MASK(0, cfg->s2cfg.eff_ps))) { + qemu_log_mask(LOG_GUEST_ERROR, + "SMMUv3 S2TTB too large 0x%" PRIx64 + ", effective PS %d bits\n", + cfg->s2cfg.vttb, cfg->s2cfg.eff_ps); + goto bad_ste; + } + + cfg->s2cfg.tsz = STE_S2T0SZ(ste); + + if (!s2t0sz_valid(cfg)) { + qemu_log_mask(LOG_GUEST_ERROR, "SMMUv3 bad STE S2T0SZ = %d\n", + cfg->s2cfg.tsz); + goto bad_ste; + } + + if (!s2_pgtable_config_valid(cfg->s2cfg.sl0, cfg->s2cfg.tsz, + cfg->s2cfg.granule_sz)) { + qemu_log_mask(LOG_GUEST_ERROR, + "SMMUv3 STE stage 2 config not valid!\n"); + goto bad_ste; + } + + /* Only LE supported(IDR0.TTENDIAN). */ + if (STE_S2ENDI(ste)) { + qemu_log_mask(LOG_GUEST_ERROR, + "SMMUv3 STE_S2ENDI only supports LE!\n"); + goto bad_ste; + } + + cfg->s2cfg.affd = STE_S2AFFD(ste); + + cfg->s2cfg.record_faults = STE_S2R(ste); + /* As stall is not supported. */ + if (STE_S2S(ste)) { + qemu_log_mask(LOG_UNIMP, "SMMUv3 Stall not implemented!\n"); + goto bad_ste; + } + + return 0; + +bad_ste: + return -EINVAL; +} + /* Returns < 0 in case of invalid STE, 0 otherwise */ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, STE *ste, SMMUEventInfo *event) { uint32_t config; + int ret; if (!STE_VALID(ste)) { if (!event->inval_ste_allowed) { @@ -353,10 +512,38 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, return 0; } - if (STE_CFG_S2_ENABLED(config)) { - qemu_log_mask(LOG_UNIMP, "SMMUv3 does not support stage 2 yet\n"); + /* + * If a stage is enabled in SW while not advertised, throw bad ste + * according to user manual(IHI0070E) "5.2 Stream Table Entry". + */ + if (!STAGE1_SUPPORTED(s) && STE_CFG_S1_ENABLED(config)) { + qemu_log_mask(LOG_GUEST_ERROR, "SMMUv3 S1 used but not supported.\n"); goto bad_ste; } + if (!STAGE2_SUPPORTED(s) && STE_CFG_S2_ENABLED(config)) { + qemu_log_mask(LOG_GUEST_ERROR, "SMMUv3 S2 used but not supported.\n"); + goto bad_ste; + } + + if (STAGE2_SUPPORTED(s)) { + /* VMID is considered even if s2 is disabled. */ + cfg->s2cfg.vmid = STE_S2VMID(ste); + } else { + /* Default to -1 */ + cfg->s2cfg.vmid = -1; + } + + if (STE_CFG_S2_ENABLED(config)) { + /* + * Stage-1 OAS defaults to OAS even if not enabled as it would be used + * in input address check for stage-2. + */ + cfg->oas = oas2bits(SMMU_IDR5_OAS); + ret = decode_ste_s2_cfg(cfg, ste); + if (ret) { + goto bad_ste; + } + } if (STE_S1CDMAX(ste) != 0) { qemu_log_mask(LOG_UNIMP, @@ -406,7 +593,7 @@ static int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, return -EINVAL; } if (s->features & SMMU_FEATURE_2LVL_STE) { - int l1_ste_offset, l2_ste_offset, max_l2_ste, span; + int l1_ste_offset, l2_ste_offset, max_l2_ste, span, i; dma_addr_t l1ptr, l2ptr; STEDesc l1std; @@ -430,6 +617,9 @@ static int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, event->u.f_ste_fetch.addr = l1ptr; return -EINVAL; } + for (i = 0; i < ARRAY_SIZE(l1std.word); i++) { + le32_to_cpus(&l1std.word[i]); + } span = L1STD_SPAN(&l1std); @@ -494,6 +684,7 @@ static int decode_cd(SMMUTransCfg *cfg, CD *cd, SMMUEventInfo *event) cfg->oas = MIN(oas2bits(SMMU_IDR5_OAS), cfg->oas); cfg->tbi = CD_TBI(cd); cfg->asid = CD_ASID(cd); + cfg->affd = CD_AFFD(cd); trace_smmuv3_decode_cd(cfg->oas); @@ -558,6 +749,9 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg, STE ste; CD cd; + /* ASID defaults to -1 (if s1 is not supported). */ + cfg->asid = -1; + ret = smmu_find_ste(s, sid, &ste, event); if (ret) { return ret; @@ -568,7 +762,7 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg, return ret; } - if (cfg->aborted || cfg->bypassed) { + if (cfg->aborted || cfg->bypassed || (cfg->stage == 2)) { return 0; } @@ -655,11 +849,20 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, .addr_mask = ~(hwaddr)0, .perm = IOMMU_NONE, }; + /* + * Combined attributes used for TLB lookup, as only one stage is supported, + * it will hold attributes based on the enabled stage. + */ + SMMUTransTableInfo tt_combined; qemu_mutex_lock(&s->mutex); if (!smmu_enabled(s)) { - status = SMMU_TRANS_DISABLE; + if (FIELD_EX32(s->gbpa, GBPA, ABORT)) { + status = SMMU_TRANS_ABORT; + } else { + status = SMMU_TRANS_DISABLE; + } goto epilogue; } @@ -679,25 +882,45 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, goto epilogue; } - tt = select_tt(cfg, addr); - if (!tt) { - if (cfg->record_faults) { - event.type = SMMU_EVT_F_TRANSLATION; - event.u.f_translation.addr = addr; - event.u.f_translation.rnw = flag & 0x1; + if (cfg->stage == 1) { + /* Select stage1 translation table. */ + tt = select_tt(cfg, addr); + if (!tt) { + if (cfg->record_faults) { + event.type = SMMU_EVT_F_TRANSLATION; + event.u.f_translation.addr = addr; + event.u.f_translation.rnw = flag & 0x1; + } + status = SMMU_TRANS_ERROR; + goto epilogue; } - status = SMMU_TRANS_ERROR; - goto epilogue; - } + tt_combined.granule_sz = tt->granule_sz; + tt_combined.tsz = tt->tsz; - page_mask = (1ULL << (tt->granule_sz)) - 1; + } else { + /* Stage2. */ + tt_combined.granule_sz = cfg->s2cfg.granule_sz; + tt_combined.tsz = cfg->s2cfg.tsz; + } + /* + * TLB lookup looks for granule and input size for a translation stage, + * as only one stage is supported right now, choose the right values + * from the configuration. + */ + page_mask = (1ULL << tt_combined.granule_sz) - 1; aligned_addr = addr & ~page_mask; - cached_entry = smmu_iotlb_lookup(bs, cfg, tt, aligned_addr); + cached_entry = smmu_iotlb_lookup(bs, cfg, &tt_combined, aligned_addr); if (cached_entry) { if ((flag & IOMMU_WO) && !(cached_entry->entry.perm & IOMMU_WO)) { status = SMMU_TRANS_ERROR; - if (cfg->record_faults) { + /* + * We know that the TLB only contains either stage-1 or stage-2 as + * nesting is not supported. So it is sufficient to check the + * translation stage to know the TLB stage for now. + */ + event.u.f_walk_eabt.s2 = (cfg->stage == 2); + if (PTW_RECORD_FAULT(cfg)) { event.type = SMMU_EVT_F_PERMISSION; event.u.f_permission.addr = addr; event.u.f_permission.rnw = flag & 0x1; @@ -711,6 +934,8 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, cached_entry = g_new0(SMMUTLBEntry, 1); if (smmu_ptw(cfg, aligned_addr, flag, cached_entry, &ptw_info)) { + /* All faults from PTW has S2 field. */ + event.u.f_walk_eabt.s2 = (ptw_info.stage == 2); g_free(cached_entry); switch (ptw_info.type) { case SMMU_PTW_ERR_WALK_EABT: @@ -721,28 +946,28 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, event.u.f_walk_eabt.addr2 = ptw_info.addr; break; case SMMU_PTW_ERR_TRANSLATION: - if (cfg->record_faults) { + if (PTW_RECORD_FAULT(cfg)) { event.type = SMMU_EVT_F_TRANSLATION; event.u.f_translation.addr = addr; event.u.f_translation.rnw = flag & 0x1; } break; case SMMU_PTW_ERR_ADDR_SIZE: - if (cfg->record_faults) { + if (PTW_RECORD_FAULT(cfg)) { event.type = SMMU_EVT_F_ADDR_SIZE; event.u.f_addr_size.addr = addr; event.u.f_addr_size.rnw = flag & 0x1; } break; case SMMU_PTW_ERR_ACCESS: - if (cfg->record_faults) { + if (PTW_RECORD_FAULT(cfg)) { event.type = SMMU_EVT_F_ACCESS; event.u.f_access.addr = addr; event.u.f_access.rnw = flag & 0x1; } break; case SMMU_PTW_ERR_PERMISSION: - if (cfg->record_faults) { + if (PTW_RECORD_FAULT(cfg)) { event.type = SMMU_EVT_F_PERMISSION; event.u.f_permission.addr = addr; event.u.f_permission.rnw = flag & 0x1; @@ -803,22 +1028,25 @@ epilogue: * @mr: IOMMU mr region handle * @n: notifier to be called * @asid: address space ID or negative value if we don't care + * @vmid: virtual machine ID or negative value if we don't care * @iova: iova * @tg: translation granule (if communicated through range invalidation) * @num_pages: number of @granule sized pages (if tg != 0), otherwise 1 */ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, IOMMUNotifier *n, - int asid, dma_addr_t iova, - uint8_t tg, uint64_t num_pages) + int asid, int vmid, + dma_addr_t iova, uint8_t tg, + uint64_t num_pages) { SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); IOMMUTLBEvent event; uint8_t granule; + SMMUv3State *s = sdev->smmu; if (!tg) { - SMMUEventInfo event = {.inval_ste_allowed = true}; - SMMUTransCfg *cfg = smmuv3_get_config(sdev, &event); + SMMUEventInfo eventinfo = {.inval_ste_allowed = true}; + SMMUTransCfg *cfg = smmuv3_get_config(sdev, &eventinfo); SMMUTransTableInfo *tt; if (!cfg) { @@ -829,11 +1057,20 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, return; } - tt = select_tt(cfg, iova); - if (!tt) { + if (vmid >= 0 && cfg->s2cfg.vmid != vmid) { return; } - granule = tt->granule_sz; + + if (STAGE1_SUPPORTED(s)) { + tt = select_tt(cfg, iova); + if (!tt) { + return; + } + granule = tt->granule_sz; + } else { + granule = cfg->s2cfg.granule_sz; + } + } else { granule = tg * 2 + 10; } @@ -847,9 +1084,10 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, memory_region_notify_iommu_one(n, &event); } -/* invalidate an asid/iova range tuple in all mr's */ -static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova, - uint8_t tg, uint64_t num_pages) +/* invalidate an asid/vmid/iova range tuple in all mr's */ +static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, int vmid, + dma_addr_t iova, uint8_t tg, + uint64_t num_pages) { SMMUDevice *sdev; @@ -857,20 +1095,20 @@ static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova, IOMMUMemoryRegion *mr = &sdev->iommu; IOMMUNotifier *n; - trace_smmuv3_inv_notifiers_iova(mr->parent_obj.name, asid, iova, - tg, num_pages); + trace_smmuv3_inv_notifiers_iova(mr->parent_obj.name, asid, vmid, + iova, tg, num_pages); IOMMU_NOTIFIER_FOREACH(n, mr) { - smmuv3_notify_iova(mr, n, asid, iova, tg, num_pages); + smmuv3_notify_iova(mr, n, asid, vmid, iova, tg, num_pages); } } } -static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd) +static void smmuv3_range_inval(SMMUState *s, Cmd *cmd) { dma_addr_t end, addr = CMD_ADDR(cmd); uint8_t type = CMD_TYPE(cmd); - uint16_t vmid = CMD_VMID(cmd); + int vmid = -1; uint8_t scale = CMD_SCALE(cmd); uint8_t num = CMD_NUM(cmd); uint8_t ttl = CMD_TTL(cmd); @@ -879,15 +1117,21 @@ static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd) uint64_t num_pages; uint8_t granule; int asid = -1; + SMMUv3State *smmuv3 = ARM_SMMUV3(s); + + /* Only consider VMID if stage-2 is supported. */ + if (STAGE2_SUPPORTED(smmuv3)) { + vmid = CMD_VMID(cmd); + } if (type == SMMU_CMD_TLBI_NH_VA) { asid = CMD_ASID(cmd); } if (!tg) { - trace_smmuv3_s1_range_inval(vmid, asid, addr, tg, 1, ttl, leaf); - smmuv3_inv_notifiers_iova(s, asid, addr, tg, 1); - smmu_iotlb_inv_iova(s, asid, addr, tg, 1, ttl); + trace_smmuv3_range_inval(vmid, asid, addr, tg, 1, ttl, leaf); + smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, 1); + smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, 1, ttl); return; } @@ -903,9 +1147,9 @@ static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd) uint64_t mask = dma_aligned_pow2_mask(addr, end, 64); num_pages = (mask + 1) >> granule; - trace_smmuv3_s1_range_inval(vmid, asid, addr, tg, num_pages, ttl, leaf); - smmuv3_inv_notifiers_iova(s, asid, addr, tg, num_pages); - smmu_iotlb_inv_iova(s, asid, addr, tg, num_pages, ttl); + trace_smmuv3_range_inval(vmid, asid, addr, tg, num_pages, ttl, leaf); + smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, num_pages); + smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, num_pages, ttl); addr += mask + 1; } } @@ -1037,12 +1281,22 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) { uint16_t asid = CMD_ASID(&cmd); + if (!STAGE1_SUPPORTED(s)) { + cmd_error = SMMU_CERROR_ILL; + break; + } + trace_smmuv3_cmdq_tlbi_nh_asid(asid); smmu_inv_notifiers_all(&s->smmu_state); smmu_iotlb_inv_asid(bs, asid); break; } case SMMU_CMD_TLBI_NH_ALL: + if (!STAGE1_SUPPORTED(s)) { + cmd_error = SMMU_CERROR_ILL; + break; + } + QEMU_FALLTHROUGH; case SMMU_CMD_TLBI_NSNH_ALL: trace_smmuv3_cmdq_tlbi_nh(); smmu_inv_notifiers_all(&s->smmu_state); @@ -1050,7 +1304,36 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) break; case SMMU_CMD_TLBI_NH_VAA: case SMMU_CMD_TLBI_NH_VA: - smmuv3_s1_range_inval(bs, &cmd); + if (!STAGE1_SUPPORTED(s)) { + cmd_error = SMMU_CERROR_ILL; + break; + } + smmuv3_range_inval(bs, &cmd); + break; + case SMMU_CMD_TLBI_S12_VMALL: + { + uint16_t vmid = CMD_VMID(&cmd); + + if (!STAGE2_SUPPORTED(s)) { + cmd_error = SMMU_CERROR_ILL; + break; + } + + trace_smmuv3_cmdq_tlbi_s12_vmid(vmid); + smmu_inv_notifiers_all(&s->smmu_state); + smmu_iotlb_inv_vmid(bs, vmid); + break; + } + case SMMU_CMD_TLBI_S2_IPA: + if (!STAGE2_SUPPORTED(s)) { + cmd_error = SMMU_CERROR_ILL; + break; + } + /* + * As currently only either s1 or s2 are supported + * we can reuse same function for s2. + */ + smmuv3_range_inval(bs, &cmd); break; case SMMU_CMD_TLBI_EL3_ALL: case SMMU_CMD_TLBI_EL3_VA: @@ -1058,8 +1341,6 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) case SMMU_CMD_TLBI_EL2_ASID: case SMMU_CMD_TLBI_EL2_VA: case SMMU_CMD_TLBI_EL2_VAA: - case SMMU_CMD_TLBI_S12_VMALL: - case SMMU_CMD_TLBI_S2_IPA: case SMMU_CMD_ATC_INV: case SMMU_CMD_PRI_RESP: case SMMU_CMD_RESUME: @@ -1068,12 +1349,14 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) break; default: cmd_error = SMMU_CERROR_ILL; - qemu_log_mask(LOG_GUEST_ERROR, - "Illegal command type: %d\n", CMD_TYPE(&cmd)); break; } qemu_mutex_unlock(&s->mutex); if (cmd_error) { + if (cmd_error == SMMU_CERROR_ILL) { + qemu_log_mask(LOG_GUEST_ERROR, + "Illegal command type: %d\n", CMD_TYPE(&cmd)); + } break; } /* @@ -1170,6 +1453,16 @@ static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset, case A_GERROR_IRQ_CFG2: s->gerror_irq_cfg2 = data; return MEMTX_OK; + case A_GBPA: + /* + * If UPDATE is not set, the write is ignored. This is the only + * permitted behavior in SMMUv3.2 and later. + */ + if (data & R_GBPA_UPDATE_MASK) { + /* Ignore update bit as write is synchronous. */ + s->gbpa = data & ~R_GBPA_UPDATE_MASK; + } + return MEMTX_OK; case A_STRTAB_BASE: /* 64b */ s->strtab_base = deposit64(s->strtab_base, 0, 32, data); return MEMTX_OK; @@ -1318,6 +1611,9 @@ static MemTxResult smmu_readl(SMMUv3State *s, hwaddr offset, case A_STATUSR: *data = s->statusr; return MEMTX_OK; + case A_GBPA: + *data = s->gbpa; + return MEMTX_OK; case A_IRQ_CTRL: case A_IRQ_CTRL_ACK: *data = s->irq_ctrl; @@ -1431,12 +1727,14 @@ static void smmu_init_irq(SMMUv3State *s, SysBusDevice *dev) } } -static void smmu_reset(DeviceState *dev) +static void smmu_reset_hold(Object *obj) { - SMMUv3State *s = ARM_SMMUV3(dev); + SMMUv3State *s = ARM_SMMUV3(obj); SMMUv3Class *c = ARM_SMMUV3_GET_CLASS(s); - c->parent_reset(dev); + if (c->parent_phases.hold) { + c->parent_phases.hold(obj); + } smmuv3_init_regs(s); } @@ -1471,7 +1769,7 @@ static const VMStateDescription vmstate_smmuv3_queue = { .name = "smmuv3_queue", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(base, SMMUQueue), VMSTATE_UINT32(prod, SMMUQueue), VMSTATE_UINT32(cons, SMMUQueue), @@ -1480,12 +1778,31 @@ static const VMStateDescription vmstate_smmuv3_queue = { }, }; +static bool smmuv3_gbpa_needed(void *opaque) +{ + SMMUv3State *s = opaque; + + /* Only migrate GBPA if it has different reset value. */ + return s->gbpa != SMMU_GBPA_RESET_VAL; +} + +static const VMStateDescription vmstate_gbpa = { + .name = "smmuv3/gbpa", + .version_id = 1, + .minimum_version_id = 1, + .needed = smmuv3_gbpa_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(gbpa, SMMUv3State), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_smmuv3 = { .name = "smmuv3", .version_id = 1, .minimum_version_id = 1, .priority = MIG_PRI_IOMMU, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(features, SMMUv3State), VMSTATE_UINT8(sid_size, SMMUv3State), VMSTATE_UINT8(sid_split, SMMUv3State), @@ -1510,6 +1827,21 @@ static const VMStateDescription vmstate_smmuv3 = { VMSTATE_END_OF_LIST(), }, + .subsections = (const VMStateDescription * const []) { + &vmstate_gbpa, + NULL + } +}; + +static Property smmuv3_properties[] = { + /* + * Stages of translation advertised. + * "1": Stage 1 + * "2": Stage 2 + * Defaults to stage 1 + */ + DEFINE_PROP_STRING("stage", SMMUv3State, stage), + DEFINE_PROP_END_OF_LIST() }; static void smmuv3_instance_init(Object *obj) @@ -1520,12 +1852,15 @@ static void smmuv3_instance_init(Object *obj) static void smmuv3_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); SMMUv3Class *c = ARM_SMMUV3_CLASS(klass); dc->vmsd = &vmstate_smmuv3; - device_class_set_parent_reset(dc, smmu_reset, &c->parent_reset); - c->parent_realize = dc->realize; - dc->realize = smmu_realize; + resettable_class_set_parent_phases(rc, NULL, smmu_reset_hold, NULL, + &c->parent_phases); + device_class_set_parent_realize(dc, smmu_realize, + &c->parent_realize); + device_class_set_props(dc, smmuv3_properties); } static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu, diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index 5aab0b8565..62cd55ba91 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -33,8 +33,8 @@ #include "hw/adc/max111x.h" #include "migration/vmstate.h" #include "exec/address-spaces.h" -#include "cpu.h" #include "qom/object.h" +#include "audio/audio.h" enum spitz_model_e { spitz, akita, borzoi, terrier }; @@ -774,15 +774,19 @@ static void spitz_wm8750_addr(void *opaque, int line, int level) i2c_slave_set_address(wm, SPITZ_WM_ADDRL); } -static void spitz_i2c_setup(PXA2xxState *cpu) +static void spitz_i2c_setup(MachineState *machine, PXA2xxState *cpu) { /* Attach the CPU on one end of our I2C bus. */ I2CBus *bus = pxa2xx_i2c_bus(cpu->i2c[0]); - DeviceState *wm; - /* Attach a WM8750 to the bus */ - wm = DEVICE(i2c_slave_create_simple(bus, TYPE_WM8750, 0)); + I2CSlave *i2c_dev = i2c_slave_new(TYPE_WM8750, 0); + DeviceState *wm = DEVICE(i2c_dev); + + if (machine->audiodev) { + qdev_prop_set_string(wm, "audiodev", machine->audiodev); + } + i2c_slave_realize_and_unref(i2c_dev, bus, &error_abort); spitz_wm8750_addr(wm, 0, 0); qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_WM, @@ -986,18 +990,16 @@ static void spitz_common_init(MachineState *machine) SpitzMachineState *sms = SPITZ_MACHINE(machine); enum spitz_model_e model = smc->model; PXA2xxState *mpu; - MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *rom = g_new(MemoryRegion, 1); /* Setup CPU & memory */ - mpu = pxa270_init(address_space_mem, spitz_binfo.ram_size, - machine->cpu_type); + mpu = pxa270_init(spitz_binfo.ram_size, machine->cpu_type); sms->mpu = mpu; sl_flash_register(mpu, (model == spitz) ? FLASH_128M : FLASH_1024M); memory_region_init_rom(rom, NULL, "spitz.rom", SPITZ_ROM, &error_fatal); - memory_region_add_subregion(address_space_mem, 0, rom); + memory_region_add_subregion(get_system_memory(), 0, rom); /* Setup peripherals */ spitz_keyboard_register(mpu); @@ -1015,7 +1017,7 @@ static void spitz_common_init(MachineState *machine) spitz_gpio_setup(mpu, (model == akita) ? 1 : 2); - spitz_i2c_setup(mpu); + spitz_i2c_setup(machine, mpu); if (model == akita) spitz_akita_i2c_setup(mpu); @@ -1039,6 +1041,9 @@ static void spitz_common_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_IDE; mc->ignore_memory_transaction_failures = true; mc->init = spitz_common_init; + mc->deprecation_reason = "machine is old and unmaintained"; + + machine_add_audiodev_property(mc); } static const TypeInfo spitz_common_info = { @@ -1138,7 +1143,7 @@ static const VMStateDescription vmstate_sl_nand_info = { .name = "sl-nand", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(ctl, SLNANDState), VMSTATE_STRUCT(ecc, SLNANDState, 0, vmstate_ecc_state, ECCState), VMSTATE_END_OF_LIST(), @@ -1175,7 +1180,7 @@ static const VMStateDescription vmstate_spitz_kbd = { .version_id = 1, .minimum_version_id = 0, .post_load = spitz_keyboard_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(sense_state, SpitzKeyboardState), VMSTATE_UINT16(strobe_state, SpitzKeyboardState), VMSTATE_UNUSED_TEST(is_version_0, 5), @@ -1203,7 +1208,7 @@ static const VMStateDescription vmstate_corgi_ssp_regs = { .name = "corgi-ssp", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_SSI_PERIPHERAL(ssidev, CorgiSSPState), VMSTATE_UINT32_ARRAY(enable, CorgiSSPState, 3), VMSTATE_END_OF_LIST(), @@ -1231,7 +1236,7 @@ static const VMStateDescription vmstate_spitz_lcdtg_regs = { .name = "spitz-lcdtg", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_SSI_PERIPHERAL(ssidev, SpitzLCDTG), VMSTATE_UINT32(bl_intensity, SpitzLCDTG), VMSTATE_UINT32(bl_power, SpitzLCDTG), diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 12c673c917..a2f998bf9e 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -23,7 +23,7 @@ #include "sysemu/sysemu.h" #include "hw/arm/armv7m.h" #include "hw/char/pl011.h" -#include "hw/input/gamepad.h" +#include "hw/input/stellaris_gamepad.h" #include "hw/irq.h" #include "hw/watchdog/cmsdk-apb-watchdog.h" #include "migration/vmstate.h" @@ -31,6 +31,8 @@ #include "hw/timer/stellaris-gptm.h" #include "hw/qdev-clock.h" #include "qom/object.h" +#include "qapi/qmp/qlist.h" +#include "ui/input.h" #define GPIO_A 0 #define GPIO_B 1 @@ -45,6 +47,7 @@ #define BP_GAMEPAD 0x04 #define NUM_IRQ_LINES 64 +#define NUM_PRIO_BITS 3 typedef const struct { const char *name; @@ -417,7 +420,7 @@ static const VMStateDescription vmstate_stellaris_sys = { .version_id = 2, .minimum_version_id = 1, .post_load = stellaris_sys_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(pborctl, ssys_state), VMSTATE_UINT32(ldopctl, ssys_state), VMSTATE_UINT32(int_mask, ssys_state), @@ -459,7 +462,10 @@ static void stellaris_sys_instance_init(Object *obj) s->sysclk = qdev_init_clock_out(DEVICE(s), "SYSCLK"); } -/* I2C controller. */ +/* + * I2C controller. + * ??? For now we only implement the master interface. + */ #define TYPE_STELLARIS_I2C "stellaris-i2c" OBJECT_DECLARE_SIMPLE_TYPE(stellaris_i2c_state, STELLARIS_I2C) @@ -604,10 +610,17 @@ static void stellaris_i2c_write(void *opaque, hwaddr offset, stellaris_i2c_update(s); } -static void stellaris_i2c_reset(stellaris_i2c_state *s) +static void stellaris_i2c_reset_enter(Object *obj, ResetType type) { + stellaris_i2c_state *s = STELLARIS_I2C(obj); + if (s->mcs & STELLARIS_I2C_MCS_BUSBSY) i2c_end_transfer(s->bus); +} + +static void stellaris_i2c_reset_hold(Object *obj) +{ + stellaris_i2c_state *s = STELLARIS_I2C(obj); s->msa = 0; s->mcs = 0; @@ -616,6 +629,12 @@ static void stellaris_i2c_reset(stellaris_i2c_state *s) s->mimr = 0; s->mris = 0; s->mcr = 0; +} + +static void stellaris_i2c_reset_exit(Object *obj) +{ + stellaris_i2c_state *s = STELLARIS_I2C(obj); + stellaris_i2c_update(s); } @@ -629,7 +648,7 @@ static const VMStateDescription vmstate_stellaris_i2c = { .name = "stellaris_i2c", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(msa, stellaris_i2c_state), VMSTATE_UINT32(mcs, stellaris_i2c_state), VMSTATE_UINT32(mdr, stellaris_i2c_state), @@ -655,8 +674,6 @@ static void stellaris_i2c_init(Object *obj) memory_region_init_io(&s->iomem, obj, &stellaris_i2c_ops, s, "i2c", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - /* ??? For now we only implement the master interface. */ - stellaris_i2c_reset(s); } /* Analogue to Digital Converter. This is only partially implemented, @@ -674,9 +691,8 @@ static void stellaris_i2c_init(Object *obj) #define STELLARIS_ADC_FIFO_FULL 0x1000 #define TYPE_STELLARIS_ADC "stellaris-adc" -typedef struct StellarisADCState stellaris_adc_state; -DECLARE_INSTANCE_CHECKER(stellaris_adc_state, STELLARIS_ADC, - TYPE_STELLARIS_ADC) +typedef struct StellarisADCState StellarisADCState; +DECLARE_INSTANCE_CHECKER(StellarisADCState, STELLARIS_ADC, TYPE_STELLARIS_ADC) struct StellarisADCState { SysBusDevice parent_obj; @@ -700,7 +716,7 @@ struct StellarisADCState { qemu_irq irq[4]; }; -static uint32_t stellaris_adc_fifo_read(stellaris_adc_state *s, int n) +static uint32_t stellaris_adc_fifo_read(StellarisADCState *s, int n) { int tail; @@ -716,7 +732,7 @@ static uint32_t stellaris_adc_fifo_read(stellaris_adc_state *s, int n) return s->fifo[n].data[tail]; } -static void stellaris_adc_fifo_write(stellaris_adc_state *s, int n, +static void stellaris_adc_fifo_write(StellarisADCState *s, int n, uint32_t value) { int head; @@ -736,7 +752,7 @@ static void stellaris_adc_fifo_write(stellaris_adc_state *s, int n, s->fifo[n].state |= STELLARIS_ADC_FIFO_FULL; } -static void stellaris_adc_update(stellaris_adc_state *s) +static void stellaris_adc_update(StellarisADCState *s) { int level; int n; @@ -749,7 +765,7 @@ static void stellaris_adc_update(stellaris_adc_state *s) static void stellaris_adc_trigger(void *opaque, int irq, int level) { - stellaris_adc_state *s = (stellaris_adc_state *)opaque; + StellarisADCState *s = opaque; int n; for (n = 0; n < 4; n++) { @@ -771,8 +787,9 @@ static void stellaris_adc_trigger(void *opaque, int irq, int level) } } -static void stellaris_adc_reset(stellaris_adc_state *s) +static void stellaris_adc_reset_hold(Object *obj) { + StellarisADCState *s = STELLARIS_ADC(obj); int n; for (n = 0; n < 4; n++) { @@ -785,7 +802,7 @@ static void stellaris_adc_reset(stellaris_adc_state *s) static uint64_t stellaris_adc_read(void *opaque, hwaddr offset, unsigned size) { - stellaris_adc_state *s = (stellaris_adc_state *)opaque; + StellarisADCState *s = opaque; /* TODO: Implement this. */ if (offset >= 0x40 && offset < 0xc0) { @@ -833,7 +850,7 @@ static uint64_t stellaris_adc_read(void *opaque, hwaddr offset, static void stellaris_adc_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { - stellaris_adc_state *s = (stellaris_adc_state *)opaque; + StellarisADCState *s = opaque; /* TODO: Implement this. */ if (offset >= 0x40 && offset < 0xc0) { @@ -900,32 +917,32 @@ static const VMStateDescription vmstate_stellaris_adc = { .name = "stellaris_adc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(actss, stellaris_adc_state), - VMSTATE_UINT32(ris, stellaris_adc_state), - VMSTATE_UINT32(im, stellaris_adc_state), - VMSTATE_UINT32(emux, stellaris_adc_state), - VMSTATE_UINT32(ostat, stellaris_adc_state), - VMSTATE_UINT32(ustat, stellaris_adc_state), - VMSTATE_UINT32(sspri, stellaris_adc_state), - VMSTATE_UINT32(sac, stellaris_adc_state), - VMSTATE_UINT32(fifo[0].state, stellaris_adc_state), - VMSTATE_UINT32_ARRAY(fifo[0].data, stellaris_adc_state, 16), - VMSTATE_UINT32(ssmux[0], stellaris_adc_state), - VMSTATE_UINT32(ssctl[0], stellaris_adc_state), - VMSTATE_UINT32(fifo[1].state, stellaris_adc_state), - VMSTATE_UINT32_ARRAY(fifo[1].data, stellaris_adc_state, 16), - VMSTATE_UINT32(ssmux[1], stellaris_adc_state), - VMSTATE_UINT32(ssctl[1], stellaris_adc_state), - VMSTATE_UINT32(fifo[2].state, stellaris_adc_state), - VMSTATE_UINT32_ARRAY(fifo[2].data, stellaris_adc_state, 16), - VMSTATE_UINT32(ssmux[2], stellaris_adc_state), - VMSTATE_UINT32(ssctl[2], stellaris_adc_state), - VMSTATE_UINT32(fifo[3].state, stellaris_adc_state), - VMSTATE_UINT32_ARRAY(fifo[3].data, stellaris_adc_state, 16), - VMSTATE_UINT32(ssmux[3], stellaris_adc_state), - VMSTATE_UINT32(ssctl[3], stellaris_adc_state), - VMSTATE_UINT32(noise, stellaris_adc_state), + .fields = (const VMStateField[]) { + VMSTATE_UINT32(actss, StellarisADCState), + VMSTATE_UINT32(ris, StellarisADCState), + VMSTATE_UINT32(im, StellarisADCState), + VMSTATE_UINT32(emux, StellarisADCState), + VMSTATE_UINT32(ostat, StellarisADCState), + VMSTATE_UINT32(ustat, StellarisADCState), + VMSTATE_UINT32(sspri, StellarisADCState), + VMSTATE_UINT32(sac, StellarisADCState), + VMSTATE_UINT32(fifo[0].state, StellarisADCState), + VMSTATE_UINT32_ARRAY(fifo[0].data, StellarisADCState, 16), + VMSTATE_UINT32(ssmux[0], StellarisADCState), + VMSTATE_UINT32(ssctl[0], StellarisADCState), + VMSTATE_UINT32(fifo[1].state, StellarisADCState), + VMSTATE_UINT32_ARRAY(fifo[1].data, StellarisADCState, 16), + VMSTATE_UINT32(ssmux[1], StellarisADCState), + VMSTATE_UINT32(ssctl[1], StellarisADCState), + VMSTATE_UINT32(fifo[2].state, StellarisADCState), + VMSTATE_UINT32_ARRAY(fifo[2].data, StellarisADCState, 16), + VMSTATE_UINT32(ssmux[2], StellarisADCState), + VMSTATE_UINT32(ssctl[2], StellarisADCState), + VMSTATE_UINT32(fifo[3].state, StellarisADCState), + VMSTATE_UINT32_ARRAY(fifo[3].data, StellarisADCState, 16), + VMSTATE_UINT32(ssmux[3], StellarisADCState), + VMSTATE_UINT32(ssctl[3], StellarisADCState), + VMSTATE_UINT32(noise, StellarisADCState), VMSTATE_END_OF_LIST() } }; @@ -933,7 +950,7 @@ static const VMStateDescription vmstate_stellaris_adc = { static void stellaris_adc_init(Object *obj) { DeviceState *dev = DEVICE(obj); - stellaris_adc_state *s = STELLARIS_ADC(obj); + StellarisADCState *s = STELLARIS_ADC(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); int n; @@ -944,7 +961,6 @@ static void stellaris_adc_init(Object *obj) memory_region_init_io(&s->iomem, obj, &stellaris_adc_ops, s, "adc", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - stellaris_adc_reset(s); qdev_init_gpio_in(dev, stellaris_adc_trigger, 1); } @@ -1015,6 +1031,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) * 400fe000 system control */ + Object *soc_container; DeviceState *gpio_dev[7], *nvic; qemu_irq gpio_in[7][8]; qemu_irq gpio_out[7][8]; @@ -1026,7 +1043,8 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) DeviceState *ssys_dev; int i; int j; - const uint8_t *macaddr; + NICInfo *nd; + MACAddr mac; MemoryRegion *sram = g_new(MemoryRegion, 1); MemoryRegion *flash = g_new(MemoryRegion, 1); @@ -1035,6 +1053,9 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) flash_size = (((board->dc0 & 0xffff) + 1) << 1) * 1024; sram_size = ((board->dc0 >> 18) + 1) * 1024; + soc_container = object_new("container"); + object_property_add_child(OBJECT(ms), "soc", soc_container); + /* Flash programming is done via the SCU, so pretend it is ROM. */ memory_region_init_rom(flash, NULL, "stellaris.flash", flash_size, &error_fatal); @@ -1049,12 +1070,23 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) * need its sysclk output. */ ssys_dev = qdev_new(TYPE_STELLARIS_SYS); - /* Most devices come preprogrammed with a MAC address in the user data. */ - macaddr = nd_table[0].macaddr.a; + object_property_add_child(soc_container, "sys", OBJECT(ssys_dev)); + + /* + * Most devices come preprogrammed with a MAC address in the user data. + * Generate a MAC address now, if there isn't a matching -nic for it. + */ + nd = qemu_find_nic_info("stellaris_enet", true, "stellaris"); + if (nd) { + memcpy(mac.a, nd->macaddr.a, sizeof(mac.a)); + } else { + qemu_macaddr_default_if_unset(&mac); + } + qdev_prop_set_uint32(ssys_dev, "user0", - macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16)); + mac.a[0] | (mac.a[1] << 8) | (mac.a[2] << 16)); qdev_prop_set_uint32(ssys_dev, "user1", - macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16)); + mac.a[3] | (mac.a[4] << 8) | (mac.a[5] << 16)); qdev_prop_set_uint32(ssys_dev, "did0", board->did0); qdev_prop_set_uint32(ssys_dev, "did1", board->did1); qdev_prop_set_uint32(ssys_dev, "dc0", board->dc0); @@ -1065,7 +1097,9 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) sysbus_realize_and_unref(SYS_BUS_DEVICE(ssys_dev), &error_fatal); nvic = qdev_new(TYPE_ARMV7M); + object_property_add_child(soc_container, "v7m", OBJECT(nvic)); qdev_prop_set_uint32(nvic, "num-irq", NUM_IRQ_LINES); + qdev_prop_set_uint8(nvic, "num-prio-bits", NUM_PRIO_BITS); qdev_prop_set_string(nvic, "cpu-type", ms->cpu_type); qdev_prop_set_bit(nvic, "enable-bitband", true); qdev_connect_clock_in(nvic, "cpuclk", @@ -1097,6 +1131,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) dev = qdev_new(TYPE_STELLARIS_GPTM); sbd = SYS_BUS_DEVICE(dev); + object_property_add_child(soc_container, "gptm[*]", OBJECT(dev)); qdev_connect_clock_in(dev, "clk", qdev_get_clock_out(ssys_dev, "SYSCLK")); sysbus_realize_and_unref(sbd, &error_fatal); @@ -1110,7 +1145,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) if (board->dc1 & (1 << 3)) { /* watchdog present */ dev = qdev_new(TYPE_LUMINARY_WATCHDOG); - + object_property_add_child(soc_container, "wdg", OBJECT(dev)); qdev_connect_clock_in(dev, "WDOGCLK", qdev_get_clock_out(ssys_dev, "SYSCLK")); @@ -1147,9 +1182,15 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) for (i = 0; i < 4; i++) { if (board->dc2 & (1 << i)) { - pl011_luminary_create(0x4000c000 + i * 0x1000, - qdev_get_gpio_in(nvic, uart_irq[i]), - serial_hd(i)); + SysBusDevice *sbd; + + dev = qdev_new("pl011_luminary"); + object_property_add_child(soc_container, "uart[*]", OBJECT(dev)); + sbd = SYS_BUS_DEVICE(dev); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); + sysbus_realize_and_unref(sbd, &error_fatal); + sysbus_mmio_map(sbd, 0, 0x4000c000 + i * 0x1000); + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(nvic, uart_irq[i])); } } if (board->dc2 & (1 << 4)) { @@ -1231,16 +1272,20 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) dinfo = drive_get(IF_SD, 0, 0); blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; - carddev = qdev_new(TYPE_SD_CARD); + carddev = qdev_new(TYPE_SD_CARD_SPI); qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); - qdev_prop_set_bit(carddev, "spi", true); qdev_realize_and_unref(carddev, qdev_get_child_bus(sddev, "sd-bus"), &error_fatal); - ssddev = ssi_create_peripheral(bus, "ssd0323"); + ssddev = qdev_new("ssd0323"); + object_property_add_child(OBJECT(ms), "oled", OBJECT(ssddev)); + qdev_prop_set_uint8(ssddev, "cs", 1); + qdev_realize_and_unref(ssddev, bus, &error_fatal); gpio_d_splitter = qdev_new(TYPE_SPLIT_IRQ); + object_property_add_child(OBJECT(ms), "splitter", + OBJECT(gpio_d_splitter)); qdev_prop_set_uint32(gpio_d_splitter, "num-lines", 2); qdev_realize_and_unref(gpio_d_splitter, NULL, &error_fatal); qdev_connect_gpio_out( @@ -1260,25 +1305,44 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) if (board->dc4 & (1 << 28)) { DeviceState *enet; - qemu_check_nic_model(&nd_table[0], "stellaris"); - enet = qdev_new("stellaris_enet"); - qdev_set_nic_properties(enet, &nd_table[0]); + object_property_add_child(soc_container, "enet", OBJECT(enet)); + if (nd) { + qdev_set_nic_properties(enet, nd); + } else { + qdev_prop_set_macaddr(enet, "mac", mac.a); + } + sysbus_realize_and_unref(SYS_BUS_DEVICE(enet), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(enet), 0, 0x40048000); sysbus_connect_irq(SYS_BUS_DEVICE(enet), 0, qdev_get_gpio_in(nvic, 42)); } if (board->peripherals & BP_GAMEPAD) { - qemu_irq gpad_irq[5]; - static const int gpad_keycode[5] = { 0xc8, 0xd0, 0xcb, 0xcd, 0x1d }; + QList *gpad_keycode_list = qlist_new(); + static const int gpad_keycode[5] = { + Q_KEY_CODE_UP, Q_KEY_CODE_DOWN, Q_KEY_CODE_LEFT, + Q_KEY_CODE_RIGHT, Q_KEY_CODE_CTRL, + }; + DeviceState *gpad; - gpad_irq[0] = qemu_irq_invert(gpio_in[GPIO_E][0]); /* up */ - gpad_irq[1] = qemu_irq_invert(gpio_in[GPIO_E][1]); /* down */ - gpad_irq[2] = qemu_irq_invert(gpio_in[GPIO_E][2]); /* left */ - gpad_irq[3] = qemu_irq_invert(gpio_in[GPIO_E][3]); /* right */ - gpad_irq[4] = qemu_irq_invert(gpio_in[GPIO_F][1]); /* select */ + gpad = qdev_new(TYPE_STELLARIS_GAMEPAD); + object_property_add_child(OBJECT(ms), "gamepad", OBJECT(gpad)); + for (i = 0; i < ARRAY_SIZE(gpad_keycode); i++) { + qlist_append_int(gpad_keycode_list, gpad_keycode[i]); + } + qdev_prop_set_array(gpad, "keycodes", gpad_keycode_list); + sysbus_realize_and_unref(SYS_BUS_DEVICE(gpad), &error_fatal); - stellaris_gamepad_init(5, gpad_irq, gpad_keycode); + qdev_connect_gpio_out(gpad, 0, + qemu_irq_invert(gpio_in[GPIO_E][0])); /* up */ + qdev_connect_gpio_out(gpad, 1, + qemu_irq_invert(gpio_in[GPIO_E][1])); /* down */ + qdev_connect_gpio_out(gpad, 2, + qemu_irq_invert(gpio_in[GPIO_E][2])); /* left */ + qdev_connect_gpio_out(gpad, 3, + qemu_irq_invert(gpio_in[GPIO_E][3])); /* right */ + qdev_connect_gpio_out(gpad, 4, + qemu_irq_invert(gpio_in[GPIO_F][1])); /* select */ } for (i = 0; i < 7; i++) { if (board->dc4 & (1 << i)) { @@ -1302,7 +1366,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) create_unimplemented_device("hibernation", 0x400fc000, 0x1000); create_unimplemented_device("flash-control", 0x400fd000, 0x1000); - armv7m_load_kernel(ARM_CPU(first_cpu), ms->kernel_filename, flash_size); + armv7m_load_kernel(ARM_CPU(first_cpu), ms->kernel_filename, 0, flash_size); } /* FIXME: Figure out how to generate these from stellaris_boards. */ @@ -1359,7 +1423,11 @@ type_init(stellaris_machine_init) static void stellaris_i2c_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + rc->phases.enter = stellaris_i2c_reset_enter; + rc->phases.hold = stellaris_i2c_reset_hold; + rc->phases.exit = stellaris_i2c_reset_exit; dc->vmsd = &vmstate_stellaris_i2c; } @@ -1374,14 +1442,16 @@ static const TypeInfo stellaris_i2c_info = { static void stellaris_adc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + rc->phases.hold = stellaris_adc_reset_hold; dc->vmsd = &vmstate_stellaris_adc; } static const TypeInfo stellaris_adc_info = { .name = TYPE_STELLARIS_ADC, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(stellaris_adc_state), + .instance_size = sizeof(StellarisADCState), .instance_init = stellaris_adc_init, .class_init = stellaris_adc_class_init, }; diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c index f7b344ba9f..808b783515 100644 --- a/hw/arm/stm32f100_soc.c +++ b/hw/arm/stm32f100_soc.c @@ -115,7 +115,8 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp) /* Init ARMv7m */ armv7m = DEVICE(&s->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 61); - qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type); + qdev_prop_set_uint8(armv7m, "num-prio-bits", 4); + qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3")); qdev_prop_set_bit(armv7m, "enable-bitband", true); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); qdev_connect_clock_in(armv7m, "refclk", s->refclk); @@ -180,17 +181,12 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("CRC", 0x40023000, 0x400); } -static Property stm32f100_soc_properties[] = { - DEFINE_PROP_STRING("cpu-type", STM32F100State, cpu_type), - DEFINE_PROP_END_OF_LIST(), -}; - static void stm32f100_soc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = stm32f100_soc_realize; - device_class_set_props(dc, stm32f100_soc_properties); + /* No vmstate or reset required: device has no internal state */ } static const TypeInfo stm32f100_soc_info = { diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index c6b75a381d..a451e21f59 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -127,7 +127,8 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) 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_uint8(armv7m, "num-prio-bits", 4); + qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3")); qdev_prop_set_bit(armv7m, "enable-bitband", true); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); qdev_connect_clock_in(armv7m, "refclk", s->refclk); @@ -201,17 +202,12 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) } } -static Property stm32f205_soc_properties[] = { - DEFINE_PROP_STRING("cpu-type", STM32F205State, cpu_type), - DEFINE_PROP_END_OF_LIST(), -}; - static void stm32f205_soc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = stm32f205_soc_realize; - device_class_set_props(dc, stm32f205_soc_properties); + /* No vmstate or reset required: device has no internal state */ } static const TypeInfo stm32f205_soc_info = { diff --git a/hw/arm/stm32f405_soc.c b/hw/arm/stm32f405_soc.c index c07947d9f8..2ad5b79a06 100644 --- a/hw/arm/stm32f405_soc.c +++ b/hw/arm/stm32f405_soc.c @@ -139,9 +139,18 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp) } memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram); + memory_region_init_ram(&s->ccm, NULL, "STM32F405.ccm", CCM_SIZE, + &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(system_memory, CCM_BASE_ADDRESS, &s->ccm); + 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_uint8(armv7m, "num-prio-bits", 4); + qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4")); qdev_prop_set_bit(armv7m, "enable-bitband", true); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); qdev_connect_clock_in(armv7m, "refclk", s->refclk); @@ -279,17 +288,11 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp) 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 */ } diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c new file mode 100644 index 0000000000..40e294f838 --- /dev/null +++ b/hw/arm/stm32l4x5_soc.c @@ -0,0 +1,421 @@ +/* + * STM32L4x5 SoC family + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This work is heavily inspired by the stm32f405_soc by Alistair Francis. + * Original code is licensed under the MIT License: + * + * Copyright (c) 2014 Alistair Francis + */ + +/* + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "exec/address-spaces.h" +#include "sysemu/sysemu.h" +#include "hw/or-irq.h" +#include "hw/arm/stm32l4x5_soc.h" +#include "hw/gpio/stm32l4x5_gpio.h" +#include "hw/qdev-clock.h" +#include "hw/misc/unimp.h" + +#define FLASH_BASE_ADDRESS 0x08000000 +#define SRAM1_BASE_ADDRESS 0x20000000 +#define SRAM1_SIZE (96 * KiB) +#define SRAM2_BASE_ADDRESS 0x10000000 +#define SRAM2_SIZE (32 * KiB) + +#define EXTI_ADDR 0x40010400 +#define SYSCFG_ADDR 0x40010000 + +#define NUM_EXTI_IRQ 40 +/* Match exti line connections with their CPU IRQ number */ +/* See Vector Table (Reference Manual p.396) */ +/* + * Some IRQs are connected to the same CPU IRQ (denoted by -1) + * and require an intermediary OR gate to function correctly. + */ +static const int exti_irq[NUM_EXTI_IRQ] = { + 6, /* GPIO[0] */ + 7, /* GPIO[1] */ + 8, /* GPIO[2] */ + 9, /* GPIO[3] */ + 10, /* GPIO[4] */ + -1, -1, -1, -1, -1, /* GPIO[5..9] OR gate 23 */ + -1, -1, -1, -1, -1, -1, /* GPIO[10..15] OR gate 40 */ + -1, /* PVD OR gate 1 */ + 67, /* OTG_FS_WKUP, Direct */ + 41, /* RTC_ALARM */ + 2, /* RTC_TAMP_STAMP2/CSS_LSE */ + 3, /* RTC wakeup timer */ + -1, -1, /* COMP[1..2] OR gate 63 */ + 31, /* I2C1 wakeup, Direct */ + 33, /* I2C2 wakeup, Direct */ + 72, /* I2C3 wakeup, Direct */ + 37, /* USART1 wakeup, Direct */ + 38, /* USART2 wakeup, Direct */ + 39, /* USART3 wakeup, Direct */ + 52, /* UART4 wakeup, Direct */ + 53, /* UART4 wakeup, Direct */ + 70, /* LPUART1 wakeup, Direct */ + 65, /* LPTIM1, Direct */ + 66, /* LPTIM2, Direct */ + 76, /* SWPMI1 wakeup, Direct */ + -1, -1, -1, -1, /* PVM[1..4] OR gate 1 */ + 78 /* LCD wakeup, Direct */ +}; +#define RCC_BASE_ADDRESS 0x40021000 +#define RCC_IRQ 5 + +static const int exti_or_gates_out[NUM_EXTI_OR_GATES] = { + 23, 40, 63, 1, +}; + +static const int exti_or_gates_num_lines_in[NUM_EXTI_OR_GATES] = { + 5, 6, 2, 5, +}; + +/* 3 OR gates with consecutive inputs */ +#define NUM_EXTI_SIMPLE_OR_GATES 3 +static const int exti_or_gates_first_line_in[NUM_EXTI_SIMPLE_OR_GATES] = { + 5, 10, 21, +}; + +/* 1 OR gate with non-consecutive inputs */ +#define EXTI_OR_GATE1_NUM_LINES_IN 5 +static const int exti_or_gate1_lines_in[EXTI_OR_GATE1_NUM_LINES_IN] = { + 16, 35, 36, 37, 38, +}; + +static const struct { + uint32_t addr; + uint32_t moder_reset; + uint32_t ospeedr_reset; + uint32_t pupdr_reset; +} stm32l4x5_gpio_cfg[NUM_GPIOS] = { + { 0x48000000, 0xABFFFFFF, 0x0C000000, 0x64000000 }, + { 0x48000400, 0xFFFFFEBF, 0x00000000, 0x00000100 }, + { 0x48000800, 0xFFFFFFFF, 0x00000000, 0x00000000 }, + { 0x48000C00, 0xFFFFFFFF, 0x00000000, 0x00000000 }, + { 0x48001000, 0xFFFFFFFF, 0x00000000, 0x00000000 }, + { 0x48001400, 0xFFFFFFFF, 0x00000000, 0x00000000 }, + { 0x48001800, 0xFFFFFFFF, 0x00000000, 0x00000000 }, + { 0x48001C00, 0x0000000F, 0x00000000, 0x00000000 }, +}; + +static void stm32l4x5_soc_initfn(Object *obj) +{ + Stm32l4x5SocState *s = STM32L4X5_SOC(obj); + + object_initialize_child(obj, "exti", &s->exti, TYPE_STM32L4X5_EXTI); + for (unsigned i = 0; i < NUM_EXTI_OR_GATES; i++) { + object_initialize_child(obj, "exti_or_gates[*]", &s->exti_or_gates[i], + TYPE_OR_IRQ); + } + object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32L4X5_SYSCFG); + object_initialize_child(obj, "rcc", &s->rcc, TYPE_STM32L4X5_RCC); + + for (unsigned i = 0; i < NUM_GPIOS; i++) { + g_autofree char *name = g_strdup_printf("gpio%c", 'a' + i); + object_initialize_child(obj, name, &s->gpio[i], TYPE_STM32L4X5_GPIO); + } +} + +static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) +{ + ERRP_GUARD(); + Stm32l4x5SocState *s = STM32L4X5_SOC(dev_soc); + const Stm32l4x5SocClass *sc = STM32L4X5_SOC_GET_CLASS(dev_soc); + MemoryRegion *system_memory = get_system_memory(); + DeviceState *armv7m, *dev; + SysBusDevice *busdev; + uint32_t pin_index; + + if (!memory_region_init_rom(&s->flash, OBJECT(dev_soc), "flash", + sc->flash_size, errp)) { + return; + } + memory_region_init_alias(&s->flash_alias, OBJECT(dev_soc), + "flash_boot_alias", &s->flash, 0, + sc->flash_size); + + memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash); + memory_region_add_subregion(system_memory, 0, &s->flash_alias); + + if (!memory_region_init_ram(&s->sram1, OBJECT(dev_soc), "SRAM1", SRAM1_SIZE, + errp)) { + return; + } + memory_region_add_subregion(system_memory, SRAM1_BASE_ADDRESS, &s->sram1); + + if (!memory_region_init_ram(&s->sram2, OBJECT(dev_soc), "SRAM2", SRAM2_SIZE, + errp)) { + return; + } + memory_region_add_subregion(system_memory, SRAM2_BASE_ADDRESS, &s->sram2); + + object_initialize_child(OBJECT(dev_soc), "armv7m", &s->armv7m, TYPE_ARMV7M); + armv7m = DEVICE(&s->armv7m); + qdev_prop_set_uint32(armv7m, "num-irq", 96); + qdev_prop_set_uint32(armv7m, "num-prio-bits", 4); + qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4")); + qdev_prop_set_bit(armv7m, "enable-bitband", true); + qdev_connect_clock_in(armv7m, "cpuclk", + qdev_get_clock_out(DEVICE(&(s->rcc)), "cortex-fclk-out")); + qdev_connect_clock_in(armv7m, "refclk", + qdev_get_clock_out(DEVICE(&(s->rcc)), "cortex-refclk-out")); + object_property_set_link(OBJECT(&s->armv7m), "memory", + OBJECT(system_memory), &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { + return; + } + + /* GPIOs */ + for (unsigned i = 0; i < NUM_GPIOS; i++) { + g_autofree char *name = g_strdup_printf("%c", 'A' + i); + dev = DEVICE(&s->gpio[i]); + qdev_prop_set_string(dev, "name", name); + qdev_prop_set_uint32(dev, "mode-reset", + stm32l4x5_gpio_cfg[i].moder_reset); + qdev_prop_set_uint32(dev, "ospeed-reset", + stm32l4x5_gpio_cfg[i].ospeedr_reset); + qdev_prop_set_uint32(dev, "pupd-reset", + stm32l4x5_gpio_cfg[i].pupdr_reset); + busdev = SYS_BUS_DEVICE(&s->gpio[i]); + g_free(name); + name = g_strdup_printf("gpio%c-out", 'a' + i); + qdev_connect_clock_in(DEVICE(&s->gpio[i]), "clk", + qdev_get_clock_out(DEVICE(&(s->rcc)), name)); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, stm32l4x5_gpio_cfg[i].addr); + } + + /* System configuration controller */ + busdev = SYS_BUS_DEVICE(&s->syscfg); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, SYSCFG_ADDR); + + for (unsigned i = 0; i < NUM_GPIOS; i++) { + for (unsigned j = 0; j < GPIO_NUM_PINS; j++) { + pin_index = GPIO_NUM_PINS * i + j; + qdev_connect_gpio_out(DEVICE(&s->gpio[i]), j, + qdev_get_gpio_in(DEVICE(&s->syscfg), + pin_index)); + } + } + + /* EXTI device */ + busdev = SYS_BUS_DEVICE(&s->exti); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, EXTI_ADDR); + + /* IRQs with fan-in that require an OR gate */ + for (unsigned i = 0; i < NUM_EXTI_OR_GATES; i++) { + if (!object_property_set_int(OBJECT(&s->exti_or_gates[i]), "num-lines", + exti_or_gates_num_lines_in[i], errp)) { + return; + } + if (!qdev_realize(DEVICE(&s->exti_or_gates[i]), NULL, errp)) { + return; + } + + qdev_connect_gpio_out(DEVICE(&s->exti_or_gates[i]), 0, + qdev_get_gpio_in(armv7m, exti_or_gates_out[i])); + + if (i < NUM_EXTI_SIMPLE_OR_GATES) { + /* consecutive inputs for OR gates 23, 40, 63 */ + for (unsigned j = 0; j < exti_or_gates_num_lines_in[i]; j++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->exti), + exti_or_gates_first_line_in[i] + j, + qdev_get_gpio_in(DEVICE(&s->exti_or_gates[i]), j)); + } + } else { + /* non-consecutive inputs for OR gate 1 */ + for (unsigned j = 0; j < EXTI_OR_GATE1_NUM_LINES_IN; j++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->exti), + exti_or_gate1_lines_in[j], + qdev_get_gpio_in(DEVICE(&s->exti_or_gates[i]), j)); + } + } + } + + /* IRQs that don't require fan-in */ + for (unsigned i = 0; i < NUM_EXTI_IRQ; i++) { + if (exti_irq[i] != -1) { + sysbus_connect_irq(busdev, i, + qdev_get_gpio_in(armv7m, exti_irq[i])); + } + } + + for (unsigned i = 0; i < GPIO_NUM_PINS; i++) { + qdev_connect_gpio_out(DEVICE(&s->syscfg), i, + qdev_get_gpio_in(DEVICE(&s->exti), i)); + } + + /* RCC device */ + busdev = SYS_BUS_DEVICE(&s->rcc); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, RCC_BASE_ADDRESS); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, RCC_IRQ)); + + /* APB1 BUS */ + create_unimplemented_device("TIM2", 0x40000000, 0x400); + create_unimplemented_device("TIM3", 0x40000400, 0x400); + create_unimplemented_device("TIM4", 0x40000800, 0x400); + create_unimplemented_device("TIM5", 0x40000C00, 0x400); + create_unimplemented_device("TIM6", 0x40001000, 0x400); + create_unimplemented_device("TIM7", 0x40001400, 0x400); + /* RESERVED: 0x40001800, 0x1000 */ + create_unimplemented_device("RTC", 0x40002800, 0x400); + create_unimplemented_device("WWDG", 0x40002C00, 0x400); + create_unimplemented_device("IWDG", 0x40003000, 0x400); + /* RESERVED: 0x40001800, 0x400 */ + create_unimplemented_device("SPI2", 0x40003800, 0x400); + create_unimplemented_device("SPI3", 0x40003C00, 0x400); + /* RESERVED: 0x40004000, 0x400 */ + create_unimplemented_device("USART2", 0x40004400, 0x400); + create_unimplemented_device("USART3", 0x40004800, 0x400); + create_unimplemented_device("UART4", 0x40004C00, 0x400); + create_unimplemented_device("UART5", 0x40005000, 0x400); + create_unimplemented_device("I2C1", 0x40005400, 0x400); + create_unimplemented_device("I2C2", 0x40005800, 0x400); + create_unimplemented_device("I2C3", 0x40005C00, 0x400); + /* RESERVED: 0x40006000, 0x400 */ + create_unimplemented_device("CAN1", 0x40006400, 0x400); + /* RESERVED: 0x40006800, 0x400 */ + create_unimplemented_device("PWR", 0x40007000, 0x400); + create_unimplemented_device("DAC1", 0x40007400, 0x400); + create_unimplemented_device("OPAMP", 0x40007800, 0x400); + create_unimplemented_device("LPTIM1", 0x40007C00, 0x400); + create_unimplemented_device("LPUART1", 0x40008000, 0x400); + /* RESERVED: 0x40008400, 0x400 */ + create_unimplemented_device("SWPMI1", 0x40008800, 0x400); + /* RESERVED: 0x40008C00, 0x800 */ + create_unimplemented_device("LPTIM2", 0x40009400, 0x400); + /* RESERVED: 0x40009800, 0x6800 */ + + /* APB2 BUS */ + create_unimplemented_device("VREFBUF", 0x40010030, 0x1D0); + create_unimplemented_device("COMP", 0x40010200, 0x200); + /* RESERVED: 0x40010800, 0x1400 */ + create_unimplemented_device("FIREWALL", 0x40011C00, 0x400); + /* RESERVED: 0x40012000, 0x800 */ + create_unimplemented_device("SDMMC1", 0x40012800, 0x400); + create_unimplemented_device("TIM1", 0x40012C00, 0x400); + create_unimplemented_device("SPI1", 0x40013000, 0x400); + create_unimplemented_device("TIM8", 0x40013400, 0x400); + create_unimplemented_device("USART1", 0x40013800, 0x400); + /* RESERVED: 0x40013C00, 0x400 */ + create_unimplemented_device("TIM15", 0x40014000, 0x400); + create_unimplemented_device("TIM16", 0x40014400, 0x400); + create_unimplemented_device("TIM17", 0x40014800, 0x400); + /* RESERVED: 0x40014C00, 0x800 */ + create_unimplemented_device("SAI1", 0x40015400, 0x400); + create_unimplemented_device("SAI2", 0x40015800, 0x400); + /* RESERVED: 0x40015C00, 0x400 */ + create_unimplemented_device("DFSDM1", 0x40016000, 0x400); + /* RESERVED: 0x40016400, 0x9C00 */ + + /* AHB1 BUS */ + create_unimplemented_device("DMA1", 0x40020000, 0x400); + create_unimplemented_device("DMA2", 0x40020400, 0x400); + /* RESERVED: 0x40020800, 0x800 */ + /* RESERVED: 0x40021400, 0xC00 */ + create_unimplemented_device("FLASH", 0x40022000, 0x400); + /* RESERVED: 0x40022400, 0xC00 */ + create_unimplemented_device("CRC", 0x40023000, 0x400); + /* RESERVED: 0x40023400, 0x400 */ + create_unimplemented_device("TSC", 0x40024000, 0x400); + + /* RESERVED: 0x40024400, 0x7FDBC00 */ + + /* AHB2 BUS */ + /* RESERVED: 0x48002000, 0x7FDBC00 */ + create_unimplemented_device("OTG_FS", 0x50000000, 0x40000); + create_unimplemented_device("ADC", 0x50040000, 0x400); + /* RESERVED: 0x50040400, 0x20400 */ + create_unimplemented_device("RNG", 0x50060800, 0x400); + + /* AHB3 BUS */ + create_unimplemented_device("FMC", 0xA0000000, 0x1000); + create_unimplemented_device("QUADSPI", 0xA0001000, 0x400); +} + +static void stm32l4x5_soc_class_init(ObjectClass *klass, void *data) +{ + + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = stm32l4x5_soc_realize; + /* Reason: Mapped at fixed location on the system bus */ + dc->user_creatable = false; + /* No vmstate or reset required: device has no internal state */ +} + +static void stm32l4x5xc_soc_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); + + ssc->flash_size = 256 * KiB; +} + +static void stm32l4x5xe_soc_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); + + ssc->flash_size = 512 * KiB; +} + +static void stm32l4x5xg_soc_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); + + ssc->flash_size = 1 * MiB; +} + +static const TypeInfo stm32l4x5_soc_types[] = { + { + .name = TYPE_STM32L4X5XC_SOC, + .parent = TYPE_STM32L4X5_SOC, + .class_init = stm32l4x5xc_soc_class_init, + }, { + .name = TYPE_STM32L4X5XE_SOC, + .parent = TYPE_STM32L4X5_SOC, + .class_init = stm32l4x5xe_soc_class_init, + }, { + .name = TYPE_STM32L4X5XG_SOC, + .parent = TYPE_STM32L4X5_SOC, + .class_init = stm32l4x5xg_soc_class_init, + }, { + .name = TYPE_STM32L4X5_SOC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Stm32l4x5SocState), + .instance_init = stm32l4x5_soc_initfn, + .class_size = sizeof(Stm32l4x5SocClass), + .class_init = stm32l4x5_soc_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(stm32l4x5_soc_types) diff --git a/hw/arm/stm32vldiscovery.c b/hw/arm/stm32vldiscovery.c index 04036da3ee..cc41935160 100644 --- a/hw/arm/stm32vldiscovery.c +++ b/hw/arm/stm32vldiscovery.c @@ -47,19 +47,25 @@ static void stm32vldiscovery_init(MachineState *machine) clock_set_hz(sysclk, SYSCLK_FRQ); dev = qdev_new(TYPE_STM32F100_SOC); - qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3")); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, - FLASH_SIZE); + 0, FLASH_SIZE); } static void stm32vldiscovery_machine_init(MachineClass *mc) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m3"), + NULL + }; + mc->desc = "ST STM32VLDISCOVERY (Cortex-M3)"; mc->init = stm32vldiscovery_init; + mc->valid_cpu_types = valid_cpu_types; } DEFINE_MACHINE("stm32vldiscovery", stm32vldiscovery_machine_init) diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 39b8f01ac4..823b4931b0 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -28,7 +28,6 @@ */ #include "qemu/osdep.h" -#include "cpu.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" @@ -46,8 +45,8 @@ #include "qemu/cutils.h" #include "qemu/log.h" #include "qom/object.h" - -//#define DEBUG +#include "target/arm/cpu-qom.h" +#include "trace.h" /* TODO @@ -66,12 +65,6 @@ - Enhance UART with modem signals */ -#ifdef DEBUG -# define DPRINTF(format, ...) printf(format , ## __VA_ARGS__) -#else -# define DPRINTF(format, ...) do { } while (0) -#endif - static struct { hwaddr io_base; int irq; @@ -151,8 +144,9 @@ static uint64_t strongarm_pic_mem_read(void *opaque, hwaddr offset, case ICPR: return s->pending; default: - printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n", - __func__, offset); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad register offset 0x"HWADDR_FMT_plx"\n", + __func__, offset); return 0; } } @@ -173,8 +167,9 @@ static void strongarm_pic_mem_write(void *opaque, hwaddr offset, s->int_idle = (value & 1) ? 0 : ~0; break; default: - printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n", - __func__, offset); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad register offset 0x"HWADDR_FMT_plx"\n", + __func__, offset); break; } strongarm_pic_update(s); @@ -211,7 +206,7 @@ static const VMStateDescription vmstate_strongarm_pic_regs = { .version_id = 0, .minimum_version_id = 0, .post_load = strongarm_pic_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(pending, StrongARMPICState), VMSTATE_UINT32(enabled, StrongARMPICState), VMSTATE_UINT32(is_fiq, StrongARMPICState), @@ -333,7 +328,9 @@ static uint64_t strongarm_rtc_read(void *opaque, hwaddr addr, ((qemu_clock_get_ms(rtc_clock) - s->last_hz) << 15) / (1000 * ((s->rttr & 0xffff) + 1)); default: - printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad rtc register read 0x"HWADDR_FMT_plx"\n", + __func__, addr); return 0; } } @@ -375,7 +372,9 @@ static void strongarm_rtc_write(void *opaque, hwaddr addr, break; default: - printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad rtc register write 0x"HWADDR_FMT_plx"\n", + __func__, addr); } } @@ -439,7 +438,7 @@ static const VMStateDescription vmstate_strongarm_rtc_regs = { .minimum_version_id = 0, .pre_save = strongarm_rtc_pre_save, .post_load = strongarm_rtc_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(rttr, StrongARMRTCState), VMSTATE_UINT32(rtsr, StrongARMRTCState), VMSTATE_UINT32(rtar, StrongARMRTCState), @@ -556,12 +555,12 @@ static uint64_t strongarm_gpio_read(void *opaque, hwaddr offset, case GPSR: /* GPIO Pin-Output Set registers */ qemu_log_mask(LOG_GUEST_ERROR, - "strongarm GPIO: read from write only register GPSR\n"); + "%s: read from write only register GPSR\n", __func__); return 0; case GPCR: /* GPIO Pin-Output Clear registers */ qemu_log_mask(LOG_GUEST_ERROR, - "strongarm GPIO: read from write only register GPCR\n"); + "%s: read from write only register GPCR\n", __func__); return 0; case GRER: /* GPIO Rising-Edge Detect Enable registers */ @@ -581,7 +580,9 @@ static uint64_t strongarm_gpio_read(void *opaque, hwaddr offset, return s->status; default: - printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad gpio read offset 0x"HWADDR_FMT_plx"\n", + __func__, offset); } return 0; @@ -626,7 +627,9 @@ static void strongarm_gpio_write(void *opaque, hwaddr offset, break; default: - printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad write offset 0x"HWADDR_FMT_plx"\n", + __func__, offset); } } @@ -677,7 +680,7 @@ static const VMStateDescription vmstate_strongarm_gpio_regs = { .name = "strongarm-gpio", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ilevel, StrongARMGPIOInfo), VMSTATE_UINT32(olevel, StrongARMGPIOInfo), VMSTATE_UINT32(dir, StrongARMGPIOInfo), @@ -782,7 +785,9 @@ static uint64_t strongarm_ppc_read(void *opaque, hwaddr offset, return s->ppfr | ~0x7f001; default: - printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad ppc read offset 0x"HWADDR_FMT_plx "\n", + __func__, offset); } return 0; @@ -817,7 +822,9 @@ static void strongarm_ppc_write(void *opaque, hwaddr offset, break; default: - printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad ppc write offset 0x"HWADDR_FMT_plx"\n", + __func__, offset); } } @@ -846,7 +853,7 @@ static const VMStateDescription vmstate_strongarm_ppc_regs = { .name = "strongarm-ppc", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ilevel, StrongARMPPCInfo), VMSTATE_UINT32(olevel, StrongARMPPCInfo), VMSTATE_UINT32(dir, StrongARMPPCInfo), @@ -1029,8 +1036,13 @@ static void strongarm_uart_update_parameters(StrongARMUARTState *s) s->char_transmit_time = (NANOSECONDS_PER_SECOND / speed) * frame_size; qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); - DPRINTF(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label, - speed, parity, data_bits, stop_bits); + trace_strongarm_uart_update_parameters((s->chr.chr ? + s->chr.chr->label : "NULL") ?: + "NULL", + speed, + parity, + data_bits, + stop_bits); } static void strongarm_uart_rx_to(void *opaque) @@ -1164,7 +1176,9 @@ static uint64_t strongarm_uart_read(void *opaque, hwaddr addr, return s->utsr1; default: - printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad uart register read 0x"HWADDR_FMT_plx"\n", + __func__, addr); return 0; } } @@ -1221,7 +1235,9 @@ static void strongarm_uart_write(void *opaque, hwaddr addr, break; default: - printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad uart register write 0x"HWADDR_FMT_plx"\n", + __func__, addr); } } @@ -1300,7 +1316,7 @@ static const VMStateDescription vmstate_strongarm_uart_regs = { .version_id = 0, .minimum_version_id = 0, .post_load = strongarm_uart_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(utcr0, StrongARMUARTState), VMSTATE_UINT16(brd, StrongARMUARTState), VMSTATE_UINT8(utcr3, StrongARMUARTState), @@ -1434,7 +1450,7 @@ static uint64_t strongarm_ssp_read(void *opaque, hwaddr addr, return 0xffffffff; } if (s->rx_level < 1) { - printf("%s: SSP Rx Underrun\n", __func__); + trace_strongarm_ssp_read_underrun(); return 0xffffffff; } s->rx_level--; @@ -1443,7 +1459,9 @@ static uint64_t strongarm_ssp_read(void *opaque, hwaddr addr, strongarm_ssp_fifo_update(s); return retval; default: - printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad ssp register read 0x"HWADDR_FMT_plx"\n", + __func__, addr); break; } return 0; @@ -1458,8 +1476,8 @@ static void strongarm_ssp_write(void *opaque, hwaddr addr, case SSCR0: s->sscr[0] = value & 0xffbf; if ((s->sscr[0] & SSCR0_SSE) && SSCR0_DSS(value) < 4) { - printf("%s: Wrong data size: %i bits\n", __func__, - (int)SSCR0_DSS(value)); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Wrong data size: %i bits\n", + __func__, (int)SSCR0_DSS(value)); } if (!(value & SSCR0_SSE)) { s->sssr = 0; @@ -1471,7 +1489,9 @@ static void strongarm_ssp_write(void *opaque, hwaddr addr, case SSCR1: s->sscr[1] = value & 0x2f; if (value & SSCR1_LBM) { - printf("%s: Attempt to use SSP LBM mode\n", __func__); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Attempt to use SSP LBM mode\n", + __func__); } strongarm_ssp_fifo_update(s); break; @@ -1509,7 +1529,9 @@ static void strongarm_ssp_write(void *opaque, hwaddr addr, break; default: - printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad ssp register write 0x"HWADDR_FMT_plx"\n", + __func__, addr); break; } } @@ -1558,7 +1580,7 @@ static const VMStateDescription vmstate_strongarm_ssp_regs = { .version_id = 0, .minimum_version_id = 0, .post_load = strongarm_ssp_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16_ARRAY(sscr, StrongARMSSPState, 2), VMSTATE_UINT16(sssr, StrongARMSSPState), VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMSSPState, 8), diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c index d5a6763cf9..5891f6064f 100644 --- a/hw/arm/tosa.c +++ b/hw/arm/tosa.c @@ -242,7 +242,7 @@ static void tosa_init(MachineState *machine) TC6393xbState *tmio; DeviceState *scp0, *scp1; - mpu = pxa255_init(address_space_mem, tosa_binfo.ram_size); + mpu = pxa255_init(tosa_binfo.ram_size); memory_region_init_rom(rom, NULL, "tosa.rom", TOSA_ROM, &error_fatal); memory_region_add_subregion(address_space_mem, 0, rom); @@ -270,6 +270,7 @@ static void tosapda_machine_init(MachineClass *mc) mc->init = tosa_init; mc->block_default_type = IF_IDE; mc->ignore_memory_transaction_failures = true; + mc->deprecation_reason = "machine is old and unmaintained"; } DEFINE_MACHINE("tosa", tosapda_machine_init) diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 2dee296c8f..f1a54a02df 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -5,18 +5,19 @@ virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out." # smmu-common.c smmu_add_mr(const char *name) "%s" -smmu_ptw_level(int level, uint64_t iova, size_t subpage_size, uint64_t baseaddr, uint32_t offset, uint64_t pte) "level=%d iova=0x%"PRIx64" subpage_sz=0x%zx baseaddr=0x%"PRIx64" offset=%d => pte=0x%"PRIx64 +smmu_ptw_level(int stage, int level, uint64_t iova, size_t subpage_size, uint64_t baseaddr, uint32_t offset, uint64_t pte) "stage=%d level=%d iova=0x%"PRIx64" subpage_sz=0x%zx baseaddr=0x%"PRIx64" offset=%d => pte=0x%"PRIx64 smmu_ptw_invalid_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint32_t offset, uint64_t pte) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" offset=%d pte=0x%"PRIx64 smmu_ptw_page_pte(int stage, int level, uint64_t iova, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t address) "stage=%d level=%d iova=0x%"PRIx64" base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" page address = 0x%"PRIx64 smmu_ptw_block_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t iova, uint64_t gpa, int bsize_mb) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" iova=0x%"PRIx64" block address = 0x%"PRIx64" block size = %d MiB" smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "baseaddr=0x%"PRIx64" index=0x%x, pteaddr=0x%"PRIx64", pte=0x%"PRIx64 smmu_iotlb_inv_all(void) "IOTLB invalidate all" smmu_iotlb_inv_asid(uint16_t asid) "IOTLB invalidate asid=%d" +smmu_iotlb_inv_vmid(uint16_t vmid) "IOTLB invalidate vmid=%d" smmu_iotlb_inv_iova(uint16_t asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64 smmu_inv_notifiers_mr(const char *name) "iommu mr=%s" -smmu_iotlb_lookup_hit(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" -smmu_iotlb_lookup_miss(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" -smmu_iotlb_insert(uint16_t asid, uint64_t addr, uint8_t tg, uint8_t level) "IOTLB ++ asid=%d addr=0x%"PRIx64" tg=%d level=%d" +smmu_iotlb_lookup_hit(uint16_t asid, uint16_t vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" +smmu_iotlb_lookup_miss(uint16_t asid, uint16_t vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" +smmu_iotlb_insert(uint16_t asid, uint16_t vmid, uint64_t addr, uint8_t tg, uint8_t level) "IOTLB ++ asid=%d vmid=%d addr=0x%"PRIx64" tg=%d level=%d" # smmuv3.c smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)" @@ -45,11 +46,30 @@ smmuv3_cmdq_cfgi_ste_range(int start, int end) "start=0x%x - end=0x%x" smmuv3_cmdq_cfgi_cd(uint32_t sid) "sid=0x%x" smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid=0x%x (hits=%d, misses=%d, hit rate=%d)" smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid=0x%x (hits=%d, misses=%d, hit rate=%d)" -smmuv3_s1_range_inval(int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf) "vmid=%d asid=%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d" +smmuv3_range_inval(int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf) "vmid=%d asid=%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d" smmuv3_cmdq_tlbi_nh(void) "" smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d" +smmuv3_cmdq_tlbi_s12_vmid(uint16_t vmid) "vmid=%d" smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x" smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s" smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s" -smmuv3_inv_notifiers_iova(const char *name, uint16_t asid, uint64_t iova, uint8_t tg, uint64_t num_pages) "iommu mr=%s asid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64 +smmuv3_inv_notifiers_iova(const char *name, uint16_t asid, uint16_t vmid, uint64_t iova, uint8_t tg, uint64_t num_pages) "iommu mr=%s asid=%d vmid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64 +# strongarm.c +strongarm_uart_update_parameters(const char *label, int speed, char parity, int data_bits, int stop_bits) "%s speed=%d parity=%c data=%d stop=%d" +strongarm_ssp_read_underrun(void) "SSP rx underrun" + +# z2.c +z2_lcd_reg_update(uint8_t cur, uint8_t i_0, uint8_t i_1, uint8_t i_2, uint32_t value) "cur_reg = 0x%x, buf = [0x%x, 0x%x, 0x%x], value = 0x%x" +z2_lcd_enable_disable_result(const char *result) "LCD %s" +z2_aer915_send_too_long(int8_t msg) "message too long (%i bytes)" +z2_aer915_send(uint8_t reg, uint8_t value) "reg %d value 0x%02x" +z2_aer915_event(int8_t event, int8_t len) "i2c event =0x%x len=%d bytes" + +# xen_arm.c +xen_create_virtio_mmio_devices(int i, int irq, uint64_t base) "Created virtio-mmio device %d: irq %d base 0x%"PRIx64 +xen_init_ram(uint64_t machine_ram_size) "Initialized xen ram with size 0x%"PRIx64 +xen_enable_tpm(uint64_t addr) "Connected tpmdev at address 0x%"PRIx64 + +# bcm2838.c +bcm2838_gic_set_irq(int irq, int level) "gic irq:%d lvl:%d" diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index ecc1f6cf74..d48235453e 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -9,7 +9,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "cpu.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "hw/arm/boot.h" @@ -26,6 +25,8 @@ #include "hw/char/pl011.h" #include "hw/sd/sd.h" #include "qom/object.h" +#include "audio/audio.h" +#include "target/arm/cpu-qom.h" #define VERSATILE_FLASH_ADDR 0x34000000 #define VERSATILE_FLASH_SIZE (64 * 1024 * 1024) @@ -51,7 +52,7 @@ static const VMStateDescription vmstate_vpb_sic = { .name = "versatilepb_sic", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(level, vpb_sic_state), VMSTATE_UINT32(mask, vpb_sic_state), VMSTATE_UINT32(pic_enable, vpb_sic_state), @@ -191,10 +192,8 @@ static void versatile_init(MachineState *machine, int board_id) SysBusDevice *busdev; DeviceState *pl041; PCIBus *pci_bus; - NICInfo *nd; I2CBus *i2c; int n; - int done_smc = 0; DriveInfo *dinfo; if (machine->ram_size > 0x10000000) { @@ -262,16 +261,11 @@ static void versatile_init(MachineState *machine, int board_id) sysbus_connect_irq(busdev, 3, sic[30]); pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci"); - for(n = 0; n < nb_nics; n++) { - nd = &nd_table[n]; - - if (!done_smc && (!nd->model || strcmp(nd->model, "smc91c111") == 0)) { - smc91c111_init(nd, 0x10010000, sic[25]); - done_smc = 1; - } else { - pci_nic_init_nofail(nd, pci_bus, "rtl8139", NULL); - } + if (qemu_find_nic_info("smc91c111", true, NULL)) { + smc91c111_init(0x10010000, sic[25]); } + pci_init_nic_devices(pci_bus, "rtl8139"); + if (machine_usb(machine)) { pci_create_simple(pci_bus, -1, "pci-ohci"); } @@ -305,7 +299,13 @@ static void versatile_init(MachineState *machine, int board_id) /* The versatile/PB actually has a modified Color LCD controller that includes hardware cursor support from the PL111. */ - dev = sysbus_create_simple("pl110_versatile", 0x10120000, pic[16]); + dev = qdev_new("pl110_versatile"); + object_property_set_link(OBJECT(dev), "framebuffer-memory", + OBJECT(sysmem), &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x10120000); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[16]); + /* Wire up the mux control signals from the SYS_CLCD register */ qdev_connect_gpio_out(sysctl, 0, qdev_get_gpio_in(dev, 0)); @@ -336,13 +336,16 @@ static void versatile_init(MachineState *machine, int board_id) /* Add PL031 Real Time Clock. */ sysbus_create_simple("pl031", 0x101e8000, pic[10]); - dev = sysbus_create_simple(TYPE_VERSATILE_I2C, 0x10002000, NULL); + dev = sysbus_create_simple(TYPE_ARM_SBCON_I2C, 0x10002000, NULL); i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); i2c_slave_create_simple(i2c, "ds1338", 0x68); /* Add PL041 AACI Interface to the LM4549 codec */ pl041 = qdev_new("pl041"); qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); + if (machine->audiodev) { + qdev_prop_set_string(pl041, "audiodev", machine->audiodev); + } sysbus_realize_and_unref(SYS_BUS_DEVICE(pl041), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(pl041), 0, 0x10004000); sysbus_connect_irq(SYS_BUS_DEVICE(pl041), 0, sic[24]); @@ -385,13 +388,11 @@ static void versatile_init(MachineState *machine, int board_id) /* 0x34000000 NOR Flash */ dinfo = drive_get(IF_PFLASH, 0, 0); - if (!pflash_cfi01_register(VERSATILE_FLASH_ADDR, "versatile.flash", + pflash_cfi01_register(VERSATILE_FLASH_ADDR, "versatile.flash", VERSATILE_FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, VERSATILE_FLASH_SECT_SIZE, - 4, 0x0089, 0x0018, 0x0000, 0x0, 0)) { - fprintf(stderr, "qemu: Error registering flash memory.\n"); - } + 4, 0x0089, 0x0018, 0x0000, 0x0, 0); versatile_binfo.ram_size = machine->ram_size; versatile_binfo.board_id = board_id; @@ -418,6 +419,8 @@ static void versatilepb_class_init(ObjectClass *oc, void *data) mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); mc->default_ram_id = "versatile.ram"; + + machine_add_audiodev_property(mc); } static const TypeInfo versatilepb_type = { @@ -436,6 +439,8 @@ static void versatileab_class_init(ObjectClass *oc, void *data) mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); mc->default_ram_id = "versatile.ram"; + + machine_add_audiodev_property(mc); } static const TypeInfo versatileab_type = { diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index e1d1983ae6..de815d84cc 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -24,7 +24,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/datadir.h" -#include "cpu.h" #include "hw/sysbus.h" #include "hw/arm/boot.h" #include "hw/arm/primecell.h" @@ -43,7 +42,10 @@ #include "hw/cpu/a15mpcore.h" #include "hw/i2c/arm_sbcon_i2c.h" #include "hw/sd/sd.h" +#include "qapi/qmp/qlist.h" #include "qom/object.h" +#include "audio/audio.h" +#include "target/arm/cpu-qom.h" #define VEXPRESS_BOARD_ID 0x8e0 #define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024) @@ -173,6 +175,10 @@ struct VexpressMachineClass { struct VexpressMachineState { MachineState parent; + MemoryRegion vram; + MemoryRegion sram; + MemoryRegion flashalias; + MemoryRegion a15sram; bool secure; bool virt; }; @@ -182,7 +188,7 @@ struct VexpressMachineState { #define TYPE_VEXPRESS_A15_MACHINE MACHINE_TYPE_NAME("vexpress-a15") OBJECT_DECLARE_TYPE(VexpressMachineState, VexpressMachineClass, VEXPRESS_MACHINE) -typedef void DBoardInitFn(const VexpressMachineState *machine, +typedef void DBoardInitFn(VexpressMachineState *machine, ram_addr_t ram_size, const char *cpu_type, qemu_irq *pic); @@ -263,15 +269,14 @@ static void init_cpus(MachineState *ms, const char *cpu_type, } } -static void a9_daughterboard_init(const VexpressMachineState *vms, +static void a9_daughterboard_init(VexpressMachineState *vms, ram_addr_t ram_size, const char *cpu_type, qemu_irq *pic) { MachineState *machine = MACHINE(vms); MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *lowram = g_new(MemoryRegion, 1); - ram_addr_t low_ram_size; + DeviceState *dev; if (ram_size > 0x40000000) { /* 1GB is the maximum the address space permits */ @@ -279,17 +284,11 @@ static void a9_daughterboard_init(const VexpressMachineState *vms, exit(1); } - low_ram_size = ram_size; - if (low_ram_size > 0x4000000) { - low_ram_size = 0x4000000; - } - /* RAM is from 0x60000000 upwards. The bottom 64MB of the + /* + * RAM is from 0x60000000 upwards. The bottom 64MB of the * address space should in theory be remappable to various - * things including ROM or RAM; we always map the RAM there. + * things including ROM or RAM; we always map the flash there. */ - 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, machine->ram); /* 0x1e000000 A9MPCore (SCU) private memory region */ @@ -299,7 +298,12 @@ static void a9_daughterboard_init(const VexpressMachineState *vms, /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */ /* 0x10020000 PL111 CLCD (daughterboard) */ - sysbus_create_simple("pl111", 0x10020000, pic[44]); + dev = qdev_new("pl111"); + object_property_set_link(OBJECT(dev), "framebuffer-memory", + OBJECT(sysmem), &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x10020000); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[44]); /* 0x10060000 AXI RAM */ /* 0x100e0000 PL341 Dynamic Memory Controller */ @@ -348,14 +352,13 @@ static VEDBoardInfo a9_daughterboard = { .init = a9_daughterboard_init, }; -static void a15_daughterboard_init(const VexpressMachineState *vms, +static void a15_daughterboard_init(VexpressMachineState *vms, ram_addr_t ram_size, const char *cpu_type, qemu_irq *pic) { MachineState *machine = MACHINE(vms); MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *sram = g_new(MemoryRegion, 1); { /* We have to use a separate 64 bit variable here to avoid the gcc @@ -386,9 +389,9 @@ static void a15_daughterboard_init(const VexpressMachineState *vms, /* 0x2b060000: SP805 watchdog: not modelled */ /* 0x2b0a0000: PL341 dynamic memory controller: not modelled */ /* 0x2e000000: system SRAM */ - memory_region_init_ram(sram, NULL, "vexpress.a15sram", 0x10000, + memory_region_init_ram(&vms->a15sram, NULL, "vexpress.a15sram", 0x10000, &error_fatal); - memory_region_add_subregion(sysmem, 0x2e000000, sram); + memory_region_add_subregion(sysmem, 0x2e000000, &vms->a15sram); /* 0x7ffb0000: DMA330 DMA controller: not modelled */ /* 0x7ffd0000: PL354 static memory controller: not modelled */ @@ -547,11 +550,8 @@ static void vexpress_common_init(MachineState *machine) I2CBus *i2c; ram_addr_t vram_size, sram_size; MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *vram = g_new(MemoryRegion, 1); - MemoryRegion *sram = g_new(MemoryRegion, 1); - MemoryRegion *flashalias = g_new(MemoryRegion, 1); - MemoryRegion *flash0mem; const hwaddr *map = daughterboard->motherboard_map; + QList *db_voltage, *db_clock; int i; daughterboard->init(vms, machine->ram_size, machine->cpu_type, pic); @@ -592,20 +592,19 @@ static void vexpress_common_init(MachineState *machine) sysctl = qdev_new("realview_sysctl"); qdev_prop_set_uint32(sysctl, "sys_id", sys_id); qdev_prop_set_uint32(sysctl, "proc_id", daughterboard->proc_id); - qdev_prop_set_uint32(sysctl, "len-db-voltage", - daughterboard->num_voltage_sensors); + + db_voltage = qlist_new(); for (i = 0; i < daughterboard->num_voltage_sensors; i++) { - char *propname = g_strdup_printf("db-voltage[%d]", i); - qdev_prop_set_uint32(sysctl, propname, daughterboard->voltages[i]); - g_free(propname); + qlist_append_int(db_voltage, daughterboard->voltages[i]); } - qdev_prop_set_uint32(sysctl, "len-db-clock", - daughterboard->num_clocks); + qdev_prop_set_array(sysctl, "db-voltage", db_voltage); + + db_clock = qlist_new(); for (i = 0; i < daughterboard->num_clocks; i++) { - char *propname = g_strdup_printf("db-clock[%d]", i); - qdev_prop_set_uint32(sysctl, propname, daughterboard->clocks[i]); - g_free(propname); + qlist_append_int(db_clock, daughterboard->clocks[i]); } + qdev_prop_set_array(sysctl, "db-clock", db_clock); + sysbus_realize_and_unref(SYS_BUS_DEVICE(sysctl), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, map[VE_SYSREGS]); @@ -614,6 +613,9 @@ static void vexpress_common_init(MachineState *machine) pl041 = qdev_new("pl041"); qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); + if (machine->audiodev) { + qdev_prop_set_string(pl041, "audiodev", machine->audiodev); + } sysbus_realize_and_unref(SYS_BUS_DEVICE(pl041), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(pl041), 0, map[VE_PL041]); sysbus_connect_irq(SYS_BUS_DEVICE(pl041), 0, pic[11]); @@ -646,7 +648,7 @@ static void vexpress_common_init(MachineState *machine) sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]); sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]); - dev = sysbus_create_simple(TYPE_VERSATILE_I2C, map[VE_SERIALDVI], NULL); + dev = sysbus_create_simple(TYPE_ARM_SBCON_I2C, map[VE_SERIALDVI], NULL); i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); i2c_slave_create_simple(i2c, "sii9022", 0x39); @@ -654,44 +656,42 @@ static void vexpress_common_init(MachineState *machine) /* VE_COMPACTFLASH: not modelled */ - sysbus_create_simple("pl111", map[VE_CLCD], pic[14]); + dev = qdev_new("pl111"); + object_property_set_link(OBJECT(dev), "framebuffer-memory", + OBJECT(sysmem), &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, map[VE_CLCD]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[14]); dinfo = drive_get(IF_PFLASH, 0, 0); pflash0 = ve_pflash_cfi01_register(map[VE_NORFLASH0], "vexpress.flash0", dinfo); - if (!pflash0) { - error_report("vexpress: error registering flash 0"); - exit(1); - } if (map[VE_NORFLASHALIAS] != -1) { /* Map flash 0 as an alias into low memory */ + MemoryRegion *flash0mem; flash0mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(pflash0), 0); - memory_region_init_alias(flashalias, NULL, "vexpress.flashalias", + memory_region_init_alias(&vms->flashalias, NULL, "vexpress.flashalias", flash0mem, 0, VEXPRESS_FLASH_SIZE); - memory_region_add_subregion(sysmem, map[VE_NORFLASHALIAS], flashalias); + memory_region_add_subregion(sysmem, map[VE_NORFLASHALIAS], &vms->flashalias); } dinfo = drive_get(IF_PFLASH, 0, 1); - if (!ve_pflash_cfi01_register(map[VE_NORFLASH1], "vexpress.flash1", - dinfo)) { - error_report("vexpress: error registering flash 1"); - exit(1); - } + ve_pflash_cfi01_register(map[VE_NORFLASH1], "vexpress.flash1", dinfo); sram_size = 0x2000000; - memory_region_init_ram(sram, NULL, "vexpress.sram", sram_size, + memory_region_init_ram(&vms->sram, NULL, "vexpress.sram", sram_size, &error_fatal); - memory_region_add_subregion(sysmem, map[VE_SRAM], sram); + memory_region_add_subregion(sysmem, map[VE_SRAM], &vms->sram); vram_size = 0x800000; - memory_region_init_ram(vram, NULL, "vexpress.vram", vram_size, + memory_region_init_ram(&vms->vram, NULL, "vexpress.vram", vram_size, &error_fatal); - memory_region_add_subregion(sysmem, map[VE_VIDEORAM], vram); + memory_region_add_subregion(sysmem, map[VE_VIDEORAM], &vms->vram); /* 0x4e000000 LAN9118 Ethernet */ - if (nd_table[0].used) { - lan9118_init(&nd_table[0], map[VE_ETHERNET], pic[15]); + if (qemu_find_nic_info("lan9118", true, NULL)) { + lan9118_init(map[VE_ETHERNET], pic[15]); } /* VE_USB: not modelled */ @@ -784,6 +784,7 @@ static void vexpress_class_init(ObjectClass *oc, void *data) mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "vexpress.highmem"; + machine_add_audiodev_property(mc); object_class_property_add_bool(oc, "secure", vexpress_get_secure, vexpress_set_secure); object_class_property_set_description(oc, "secure", @@ -793,22 +794,30 @@ static void vexpress_class_init(ObjectClass *oc, void *data) static void vexpress_a9_class_init(ObjectClass *oc, void *data) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a9"), + NULL + }; MachineClass *mc = MACHINE_CLASS(oc); VexpressMachineClass *vmc = VEXPRESS_MACHINE_CLASS(oc); mc->desc = "ARM Versatile Express for Cortex-A9"; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a9"); + mc->valid_cpu_types = valid_cpu_types; vmc->daughterboard = &a9_daughterboard; } static void vexpress_a15_class_init(ObjectClass *oc, void *data) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a15"), + NULL + }; MachineClass *mc = MACHINE_CLASS(oc); VexpressMachineClass *vmc = VEXPRESS_MACHINE_CLASS(oc); mc->desc = "ARM Versatile Express for Cortex-A15"; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15"); + mc->valid_cpu_types = valid_cpu_types; vmc->daughterboard = &a15_daughterboard; diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 449fab0080..c3ccfef026 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -29,12 +29,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/bitmap.h" +#include "qemu/error-report.h" #include "trace.h" #include "hw/core/cpu.h" -#include "target/arm/cpu.h" #include "hw/acpi/acpi-defs.h" #include "hw/acpi/acpi.h" -#include "hw/nvram/fw_cfg.h" +#include "hw/nvram/fw_cfg_acpi.h" #include "hw/acpi/bios-linker-loader.h" #include "hw/acpi/aml-build.h" #include "hw/acpi/utils.h" @@ -42,20 +42,24 @@ #include "hw/acpi/memory_hotplug.h" #include "hw/acpi/generic_event_device.h" #include "hw/acpi/tpm.h" +#include "hw/acpi/hmat.h" #include "hw/pci/pcie_host.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" #include "hw/pci-host/gpex.h" #include "hw/arm/virt.h" +#include "hw/intc/arm_gicv3_its_common.h" #include "hw/mem/nvdimm.h" #include "hw/platform-bus.h" #include "sysemu/numa.h" #include "sysemu/reset.h" #include "sysemu/tpm.h" -#include "kvm_arm.h" #include "migration/vmstate.h" #include "hw/acpi/ghes.h" #include "hw/acpi/viot.h" +#include "hw/acpi/acpi_generic_initiator.h" +#include "hw/virtio/virtio-acpi.h" +#include "target/arm/multiprocessing.h" #define ARM_SPI_BASE 32 @@ -92,21 +96,6 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, aml_append(scope, dev); } -static void acpi_dsdt_add_fw_cfg(Aml *scope, const MemMapEntry *fw_cfg_memmap) -{ - Aml *dev = aml_device("FWCF"); - aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002"))); - /* device present, functioning, decoding, not shown in UI */ - aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); - aml_append(dev, aml_name_decl("_CCA", aml_int(1))); - - Aml *crs = aml_resource_template(); - aml_append(crs, aml_memory32_fixed(fw_cfg_memmap->base, - fw_cfg_memmap->size, AML_READ_WRITE)); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(scope, dev); -} - static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap) { Aml *dev, *crs; @@ -131,32 +120,6 @@ static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap) aml_append(scope, dev); } -static void acpi_dsdt_add_virtio(Aml *scope, - const MemMapEntry *virtio_mmio_memmap, - uint32_t mmio_irq, int num) -{ - hwaddr base = virtio_mmio_memmap->base; - hwaddr size = virtio_mmio_memmap->size; - int i; - - for (i = 0; i < num; i++) { - uint32_t irq = mmio_irq + i; - Aml *dev = aml_device("VR%02u", i); - aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005"))); - aml_append(dev, aml_name_decl("_UID", aml_int(i))); - aml_append(dev, aml_name_decl("_CCA", aml_int(1))); - - Aml *crs = aml_resource_template(); - aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE)); - aml_append(crs, - aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, - AML_EXCLUSIVE, &irq, 1)); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(scope, dev); - base += size; - } -} - static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, uint32_t irq, VirtMachineState *vms) { @@ -469,48 +432,34 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) * Rev: 1.07 */ static void -build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) +spcr_setup(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { - AcpiTable table = { .sig = "SPCR", .rev = 2, .oem_id = vms->oem_id, - .oem_table_id = vms->oem_table_id }; + AcpiSpcrData serial = { + .interface_type = 3, /* ARM PL011 UART */ + .base_addr.id = AML_AS_SYSTEM_MEMORY, + .base_addr.width = 32, + .base_addr.offset = 0, + .base_addr.size = 3, + .base_addr.addr = vms->memmap[VIRT_UART].base, + .interrupt_type = (1 << 3),/* Bit[3] ARMH GIC interrupt*/ + .pc_interrupt = 0, /* IRQ */ + .interrupt = (vms->irqmap[VIRT_UART] + ARM_SPI_BASE), + .baud_rate = 3, /* 9600 */ + .parity = 0, /* No Parity */ + .stop_bits = 1, /* 1 Stop bit */ + .flow_control = 1 << 1, /* RTS/CTS hardware flow control */ + .terminal_type = 0, /* VT100 */ + .language = 0, /* Language */ + .pci_device_id = 0xffff, /* not a PCI device*/ + .pci_vendor_id = 0xffff, /* not a PCI device*/ + .pci_bus = 0, + .pci_device = 0, + .pci_function = 0, + .pci_flags = 0, + .pci_segment = 0, + }; - acpi_table_begin(&table, table_data); - - /* Interface Type */ - build_append_int_noprefix(table_data, 3, 1); /* ARM PL011 UART */ - build_append_int_noprefix(table_data, 0, 3); /* Reserved */ - /* Base Address */ - build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 8, 0, 1, - vms->memmap[VIRT_UART].base); - /* Interrupt Type */ - build_append_int_noprefix(table_data, - (1 << 3) /* Bit[3] ARMH GIC interrupt */, 1); - build_append_int_noprefix(table_data, 0, 1); /* IRQ */ - /* Global System Interrupt */ - build_append_int_noprefix(table_data, - vms->irqmap[VIRT_UART] + ARM_SPI_BASE, 4); - build_append_int_noprefix(table_data, 3 /* 9600 */, 1); /* Baud Rate */ - build_append_int_noprefix(table_data, 0 /* No Parity */, 1); /* Parity */ - /* Stop Bits */ - build_append_int_noprefix(table_data, 1 /* 1 Stop bit */, 1); - /* Flow Control */ - build_append_int_noprefix(table_data, - (1 << 1) /* RTS/CTS hardware flow control */, 1); - /* Terminal Type */ - build_append_int_noprefix(table_data, 0 /* VT100 */, 1); - build_append_int_noprefix(table_data, 0, 1); /* Language */ - /* PCI Device ID */ - build_append_int_noprefix(table_data, 0xffff /* not a PCI device*/, 2); - /* PCI Vendor ID */ - build_append_int_noprefix(table_data, 0xffff /* not a PCI device*/, 2); - build_append_int_noprefix(table_data, 0, 1); /* PCI Bus Number */ - build_append_int_noprefix(table_data, 0, 1); /* PCI Device Number */ - build_append_int_noprefix(table_data, 0, 1); /* PCI Function Number */ - build_append_int_noprefix(table_data, 0, 4); /* PCI Flags */ - build_append_int_noprefix(table_data, 0, 1); /* PCI Segment */ - build_append_int_noprefix(table_data, 0, 4); /* Reserved */ - - acpi_table_end(linker, &table); + build_spcr(table_data, linker, &serial, 2, vms->oem_id, vms->oem_table_id); } /* @@ -556,6 +505,8 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } } + build_srat_generic_pci_initiator(table_data); + if (ms->nvdimms_state->is_enabled) { nvdimm_build_srat(table_data); } @@ -571,8 +522,8 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } /* - * ACPI spec, Revision 5.1 - * 5.2.24 Generic Timer Description Table (GTDT) + * ACPI spec, Revision 6.5 + * 5.2.25 Generic Timer Description Table (GTDT) */ static void build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) @@ -586,44 +537,51 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) uint32_t irqflags = vmc->claim_edge_triggered_timers ? 1 : /* Interrupt is Edge triggered */ 0; /* Interrupt is Level triggered */ - AcpiTable table = { .sig = "GTDT", .rev = 2, .oem_id = vms->oem_id, + AcpiTable table = { .sig = "GTDT", .rev = 3, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; acpi_table_begin(&table, table_data); /* CntControlBase Physical Address */ - /* FIXME: invalid value, should be 0xFFFFFFFFFFFFFFFF if not impl. ? */ - build_append_int_noprefix(table_data, 0, 8); + build_append_int_noprefix(table_data, 0xFFFFFFFFFFFFFFFF, 8); build_append_int_noprefix(table_data, 0, 4); /* Reserved */ /* * FIXME: clarify comment: * The interrupt values are the same with the device tree when adding 16 */ /* Secure EL1 timer GSIV */ - build_append_int_noprefix(table_data, ARCH_TIMER_S_EL1_IRQ + 16, 4); + build_append_int_noprefix(table_data, ARCH_TIMER_S_EL1_IRQ, 4); /* Secure EL1 timer Flags */ build_append_int_noprefix(table_data, irqflags, 4); /* Non-Secure EL1 timer GSIV */ - build_append_int_noprefix(table_data, ARCH_TIMER_NS_EL1_IRQ + 16, 4); + build_append_int_noprefix(table_data, ARCH_TIMER_NS_EL1_IRQ, 4); /* Non-Secure EL1 timer Flags */ build_append_int_noprefix(table_data, irqflags | 1UL << 2, /* Always-on Capability */ 4); /* Virtual timer GSIV */ - build_append_int_noprefix(table_data, ARCH_TIMER_VIRT_IRQ + 16, 4); + build_append_int_noprefix(table_data, ARCH_TIMER_VIRT_IRQ, 4); /* Virtual Timer Flags */ build_append_int_noprefix(table_data, irqflags, 4); /* Non-Secure EL2 timer GSIV */ - build_append_int_noprefix(table_data, ARCH_TIMER_NS_EL2_IRQ + 16, 4); + build_append_int_noprefix(table_data, ARCH_TIMER_NS_EL2_IRQ, 4); /* Non-Secure EL2 timer Flags */ build_append_int_noprefix(table_data, irqflags, 4); /* CntReadBase Physical address */ - build_append_int_noprefix(table_data, 0, 8); + build_append_int_noprefix(table_data, 0xFFFFFFFFFFFFFFFF, 8); /* Platform Timer Count */ build_append_int_noprefix(table_data, 0, 4); /* Platform Timer Offset */ build_append_int_noprefix(table_data, 0, 4); - + if (vms->ns_el2_virt_timer_irq) { + /* Virtual EL2 Timer GSIV */ + build_append_int_noprefix(table_data, ARCH_TIMER_NS_EL2_VIRT_IRQ, 4); + /* Virtual EL2 Timer Flags */ + build_append_int_noprefix(table_data, irqflags, 4); + } else { + build_append_int_noprefix(table_data, 0, 4); + build_append_int_noprefix(table_data, 0, 4); + } acpi_table_end(linker, &table); } @@ -672,7 +630,7 @@ build_dbg2(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, 34, 2); /* BaseAddressRegister[] */ - build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 8, 0, 1, + build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 32, 0, 3, vms->memmap[VIRT_UART].base); /* AddressSize[] */ @@ -686,7 +644,7 @@ build_dbg2(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) }; /* - * ACPI spec, Revision 5.1 Errata A + * ACPI spec, Revision 6.0 Errata A * 5.2.12 Multiple APIC Description Table (MADT) */ static void build_append_gicr(GArray *table_data, uint64_t base, uint32_t size) @@ -694,7 +652,7 @@ static void build_append_gicr(GArray *table_data, uint64_t base, uint32_t size) build_append_int_noprefix(table_data, 0xE, 1); /* Type */ build_append_int_noprefix(table_data, 16, 1); /* Length */ build_append_int_noprefix(table_data, 0, 2); /* Reserved */ - /* Discovery Range Base Addres */ + /* Discovery Range Base Address */ build_append_int_noprefix(table_data, base, 8); build_append_int_noprefix(table_data, size, 4); /* Discovery Range Length */ } @@ -705,7 +663,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) int i; VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); const MemMapEntry *memmap = vms->memmap; - AcpiTable table = { .sig = "APIC", .rev = 3, .oem_id = vms->oem_id, + AcpiTable table = { .sig = "APIC", .rev = 4, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; acpi_table_begin(&table, table_data); @@ -728,11 +686,11 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) for (i = 0; i < MACHINE(vms)->smp.cpus; i++) { ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i)); uint64_t physical_base_address = 0, gich = 0, gicv = 0; - uint32_t vgic_interrupt = vms->virt ? PPI(ARCH_GIC_MAINT_IRQ) : 0; + uint32_t vgic_interrupt = vms->virt ? ARCH_GIC_MAINT_IRQ : 0; uint32_t pmu_interrupt = arm_feature(&armcpu->env, ARM_FEATURE_PMU) ? - PPI(VIRTUAL_PMU_IRQ) : 0; + VIRTUAL_PMU_IRQ : 0; - if (vms->gic_version == 2) { + if (vms->gic_version == VIRT_GIC_VERSION_2) { physical_base_address = memmap[VIRT_GIC_CPU].base; gicv = memmap[VIRT_GIC_VCPU].base; gich = memmap[VIRT_GIC_HYP].base; @@ -740,7 +698,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) /* 5.2.12.14 GIC Structure */ build_append_int_noprefix(table_data, 0xB, 1); /* Type */ - build_append_int_noprefix(table_data, 76, 1); /* Length */ + build_append_int_noprefix(table_data, 80, 1); /* Length */ build_append_int_noprefix(table_data, 0, 2); /* Reserved */ build_append_int_noprefix(table_data, i, 4); /* GIC ID */ build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */ @@ -759,10 +717,14 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, vgic_interrupt, 4); build_append_int_noprefix(table_data, 0, 8); /* GICR Base Address*/ /* MPIDR */ - build_append_int_noprefix(table_data, armcpu->mp_affinity, 8); + build_append_int_noprefix(table_data, arm_cpu_mp_affinity(armcpu), 8); + /* Processor Power Efficiency Class */ + build_append_int_noprefix(table_data, 0, 1); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 3); } - if (vms->gic_version == 3) { + if (vms->gic_version != VIRT_GIC_VERSION_2) { build_append_gicr(table_data, memmap[VIRT_GIC_REDIST].base, memmap[VIRT_GIC_REDIST].size); if (virt_gicv3_redist_region_count(vms) == 2) { @@ -771,12 +733,6 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } if (its_class_name() && !vmc->no_its) { - /* - * FIXME: Structure is from Revision 6.0 where 'GIC Structure' - * has additional fields on top of implemented 5.1 Errata A, - * to make it consistent with v6.0 we need to bump everything - * to v6.0 - */ /* * ACPI spec, Revision 6.0 Errata A * (original 6.0 definition has invalid Length) @@ -809,13 +765,13 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } /* FADT */ -static void build_fadt_rev5(GArray *table_data, BIOSLinker *linker, +static void build_fadt_rev6(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms, unsigned dsdt_tbl_offset) { - /* ACPI v5.1 */ + /* ACPI v6.3 */ AcpiFadtData fadt = { - .rev = 5, - .minor_ver = 1, + .rev = 6, + .minor_ver = 3, .flags = 1 << ACPI_FADT_F_HW_REDUCED_ACPI, .xdsdt_tbl_offset = &dsdt_tbl_offset, }; @@ -865,9 +821,10 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) if (vmc->acpi_expose_flash) { acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]); } - acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]); - acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO], - (irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS); + fw_cfg_acpi_dsdt_add(scope, &memmap[VIRT_FW_CFG]); + virtio_acpi_dsdt_add(scope, memmap[VIRT_MMIO].base, memmap[VIRT_MMIO].size, + (irqmap[VIRT_MMIO] + ARM_SPI_BASE), + 0, NUM_VIRTIO_TRANSPORTS); acpi_dsdt_add_pci(scope, memmap, irqmap[VIRT_PCIE] + ARM_SPI_BASE, vms); if (vms->acpi_dev) { build_ged_aml(scope, "\\_SB."GED_DEVICE, @@ -945,7 +902,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) /* FADT MADT PPTT GTDT MCFG SPCR DBG2 pointed to by RSDT */ acpi_add_table(table_offsets, tables_blob); - build_fadt_rev5(tables_blob, tables->linker, vms, dsdt); + build_fadt_rev6(tables_blob, tables->linker, vms, dsdt); acpi_add_table(table_offsets, tables_blob); build_madt(tables_blob, tables->linker, vms); @@ -970,7 +927,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) } acpi_add_table(table_offsets, tables_blob); - build_spcr(tables_blob, tables->linker, vms); + spcr_setup(tables_blob, tables->linker, vms); acpi_add_table(table_offsets, tables_blob); build_dbg2(tables_blob, tables->linker, vms); @@ -990,6 +947,12 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) build_slit(tables_blob, tables->linker, ms, vms->oem_id, vms->oem_table_id); } + + if (ms->numa_state->hmat_enabled) { + acpi_add_table(table_offsets, tables_blob); + build_hmat(tables_blob, tables->linker, ms->numa_state, + vms->oem_id, vms->oem_table_id); + } } if (ms->nvdimms_state->is_enabled) { @@ -1042,7 +1005,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) " migration may not work", tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2); error_printf("Try removing CPUs, NUMA nodes, memory slots" - " or PCI bridges."); + " or PCI bridges.\n"); } acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE); @@ -1095,7 +1058,7 @@ static const VMStateDescription vmstate_virt_acpi_build = { .name = "virt_acpi_build", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(patched, AcpiBuildState), VMSTATE_END_OF_LIST() }, diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 097238faa7..a9a913aead 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -33,7 +33,6 @@ #include "qemu/units.h" #include "qemu/option.h" #include "monitor/qdev.h" -#include "qapi/error.h" #include "hw/sysbus.h" #include "hw/arm/boot.h" #include "hw/arm/primecell.h" @@ -47,8 +46,10 @@ #include "sysemu/numa.h" #include "sysemu/runstate.h" #include "sysemu/tpm.h" +#include "sysemu/tcg.h" #include "sysemu/kvm.h" #include "sysemu/hvf.h" +#include "sysemu/qtest.h" #include "hw/loader.h" #include "qapi/error.h" #include "qemu/bitops.h" @@ -62,29 +63,50 @@ #include "hw/arm/fdt.h" #include "hw/intc/arm_gic.h" #include "hw/intc/arm_gicv3_common.h" +#include "hw/intc/arm_gicv3_its_common.h" #include "hw/irq.h" #include "kvm_arm.h" #include "hw/firmware/smbios.h" #include "qapi/visitor.h" #include "qapi/qapi-visit-common.h" +#include "qapi/qmp/qlist.h" #include "standard-headers/linux/input.h" #include "hw/arm/smmuv3.h" #include "hw/acpi/acpi.h" +#include "target/arm/cpu-qom.h" #include "target/arm/internals.h" -#include "hw/mem/memory-device.h" +#include "target/arm/multiprocessing.h" +#include "target/arm/gtimer.h" #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" #include "hw/acpi/generic_event_device.h" -#include "hw/virtio/virtio-mem-pci.h" +#include "hw/virtio/virtio-md-pci.h" #include "hw/virtio/virtio-iommu.h" #include "hw/char/pl011.h" #include "qemu/guest-random.h" +static GlobalProperty arm_virt_compat[] = { + { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "48" }, +}; +static const size_t arm_virt_compat_len = G_N_ELEMENTS(arm_virt_compat); + +/* + * This cannot be called from the virt_machine_class_init() because + * TYPE_VIRT_MACHINE is abstract and mc->compat_props g_ptr_array_new() + * only is called on virt non abstract class init. + */ +static void arm_virt_compat_set(MachineClass *mc) +{ + compat_props_add(mc->compat_props, arm_virt_compat, + arm_virt_compat_len); +} + #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ + arm_virt_compat_set(mc); \ virt_machine_##major##_##minor##_options(mc); \ mc->desc = "QEMU " # major "." # minor " ARM Virtual Machine"; \ if (latest) { \ @@ -174,6 +196,12 @@ static const MemMapEntry base_memmap[] = { * Note the extended_memmap is sized so that it eventually also includes the * base_memmap entries (VIRT_HIGH_GIC_REDIST2 index is greater than the last * index of base_memmap). + * + * The memory map for these Highmem IO Regions can be in legacy or compact + * layout, depending on 'compact-highmem' property. With legacy layout, the + * PA space for one specific region is always reserved, even if the region + * has been disabled or doesn't fit into the PA space. However, the PA space + * for the region won't be reserved in these circumstances with compact layout. */ static MemMapEntry extended_memmap[] = { /* Additional 64 MB redist region (can contain up to 512 redistributors) */ @@ -196,39 +224,32 @@ static const int a15irqmap[] = { [VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */ }; -static const char *valid_cpus[] = { - ARM_CPU_TYPE_NAME("cortex-a7"), - ARM_CPU_TYPE_NAME("cortex-a15"), - ARM_CPU_TYPE_NAME("cortex-a53"), - ARM_CPU_TYPE_NAME("cortex-a57"), - ARM_CPU_TYPE_NAME("cortex-a72"), - ARM_CPU_TYPE_NAME("cortex-a76"), - ARM_CPU_TYPE_NAME("a64fx"), - ARM_CPU_TYPE_NAME("neoverse-n1"), - ARM_CPU_TYPE_NAME("host"), - ARM_CPU_TYPE_NAME("max"), -}; - -static bool cpu_type_valid(const char *cpu) +static void create_randomness(MachineState *ms, const char *node) { - int i; - - for (i = 0; i < ARRAY_SIZE(valid_cpus); i++) { - if (strcmp(cpu, valid_cpus[i]) == 0) { - return true; - } - } - return false; -} - -static void create_kaslr_seed(MachineState *ms, const char *node) -{ - uint64_t seed; + struct { + uint64_t kaslr; + uint8_t rng[32]; + } seed; if (qemu_guest_getrandom(&seed, sizeof(seed), NULL)) { return; } - qemu_fdt_setprop_u64(ms->fdt, node, "kaslr-seed", seed); + qemu_fdt_setprop_u64(ms->fdt, node, "kaslr-seed", seed.kaslr); + qemu_fdt_setprop(ms->fdt, node, "rng-seed", seed.rng, sizeof(seed.rng)); +} + +/* + * The CPU object always exposes the NS EL2 virt timer IRQ line, + * but we don't want to advertise it to the guest in the dtb or ACPI + * table unless it's really going to do something. + */ +static bool ns_el2_virt_timer_present(void) +{ + ARMCPU *cpu = ARM_CPU(qemu_get_cpu(0)); + CPUARMState *env = &cpu->env; + + return arm_feature(env, ARM_FEATURE_AARCH64) && + arm_feature(env, ARM_FEATURE_EL2) && cpu_isar_feature(aa64_vh, cpu); } static void create_fdt(VirtMachineState *vms) @@ -248,17 +269,18 @@ static void create_fdt(VirtMachineState *vms) qemu_fdt_setprop_string(fdt, "/", "compatible", "linux,dummy-virt"); qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); + qemu_fdt_setprop_string(fdt, "/", "model", "linux,dummy-virt"); /* /chosen must exist for load_dtb to fill in necessary properties later */ qemu_fdt_add_subnode(fdt, "/chosen"); - if (vms->dtb_kaslr_seed) { - create_kaslr_seed(ms, "/chosen"); + if (vms->dtb_randomness) { + create_randomness(ms, "/chosen"); } if (vms->secure) { qemu_fdt_add_subnode(fdt, "/secure-chosen"); - if (vms->dtb_kaslr_seed) { - create_kaslr_seed(ms, "/secure-chosen"); + if (vms->dtb_randomness) { + create_randomness(ms, "/secure-chosen"); } } @@ -347,11 +369,29 @@ static void fdt_add_timer_nodes(const VirtMachineState *vms) "arm,armv7-timer"); } qemu_fdt_setprop(ms->fdt, "/timer", "always-on", NULL, 0); - qemu_fdt_setprop_cells(ms->fdt, "/timer", "interrupts", - GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_S_EL1_IRQ, irqflags, - GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_NS_EL1_IRQ, irqflags, - GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_VIRT_IRQ, irqflags, - GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_NS_EL2_IRQ, irqflags); + if (vms->ns_el2_virt_timer_irq) { + qemu_fdt_setprop_cells(ms->fdt, "/timer", "interrupts", + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_TIMER_S_EL1_IRQ), irqflags, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_TIMER_NS_EL1_IRQ), irqflags, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_TIMER_VIRT_IRQ), irqflags, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_TIMER_NS_EL2_IRQ), irqflags, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_TIMER_NS_EL2_VIRT_IRQ), irqflags); + } else { + qemu_fdt_setprop_cells(ms->fdt, "/timer", "interrupts", + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_TIMER_S_EL1_IRQ), irqflags, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_TIMER_NS_EL1_IRQ), irqflags, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_TIMER_VIRT_IRQ), irqflags, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_TIMER_NS_EL2_IRQ), irqflags); + } } static void fdt_add_cpu_nodes(const VirtMachineState *vms) @@ -378,7 +418,7 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) for (cpu = 0; cpu < smp_cpus; cpu++) { ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu)); - if (armcpu->mp_affinity & ARM_AFF3_MASK) { + if (arm_cpu_mp_affinity(armcpu) & ARM_AFF3_MASK) { addr_cells = 2; break; } @@ -405,10 +445,10 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) if (addr_cells == 2) { qemu_fdt_setprop_u64(ms->fdt, nodename, "reg", - armcpu->mp_affinity); + arm_cpu_mp_affinity(armcpu)); } else { qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", - armcpu->mp_affinity); + arm_cpu_mp_affinity(armcpu)); } if (ms->possible_cpus->cpus[cs->cpu_index].props.has_node_id) { @@ -482,6 +522,7 @@ static void fdt_add_its_gic_node(VirtMachineState *vms) qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "arm,gic-v3-its"); qemu_fdt_setprop(ms->fdt, nodename, "msi-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#msi-cells", 1); qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_ITS].base, 2, vms->memmap[VIRT_GIC_ITS].size); @@ -551,7 +592,8 @@ static void fdt_add_gic_node(VirtMachineState *vms) if (vms->virt) { qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", - GIC_FDT_IRQ_TYPE_PPI, ARCH_GIC_MAINT_IRQ, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_GIC_MAINT_IRQ), GIC_FDT_IRQ_FLAGS_LEVEL_HI); } } else { @@ -575,7 +617,8 @@ static void fdt_add_gic_node(VirtMachineState *vms) 2, vms->memmap[VIRT_GIC_VCPU].base, 2, vms->memmap[VIRT_GIC_VCPU].size); qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", - GIC_FDT_IRQ_TYPE_PPI, ARCH_GIC_MAINT_IRQ, + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(ARCH_GIC_MAINT_IRQ), GIC_FDT_IRQ_FLAGS_LEVEL_HI); } } @@ -607,7 +650,8 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms) qemu_fdt_setprop(ms->fdt, "/pmu", "compatible", compat, sizeof(compat)); qemu_fdt_setprop_cells(ms->fdt, "/pmu", "interrupts", - GIC_FDT_IRQ_TYPE_PPI, VIRTUAL_PMU_IRQ, irqflags); + GIC_FDT_IRQ_TYPE_PPI, + INTID_TO_PPI(VIRTUAL_PMU_IRQ), irqflags); } } @@ -628,13 +672,12 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) dev = qdev_new(TYPE_ACPI_GED); qdev_prop_set_uint32(dev, "ged-event", event); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); 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, qdev_get_gpio_in(vms->gic, irq)); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - return dev; } @@ -672,10 +715,10 @@ static void create_v2m(VirtMachineState *vms) DeviceState *dev; dev = qdev_new("arm-gicv2m"); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_GIC_V2M].base); qdev_prop_set_uint32(dev, "base-spi", irq); qdev_prop_set_uint32(dev, "num-spi", NUM_GICV2M_SPIS); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_GIC_V2M].base); for (i = 0; i < NUM_GICV2M_SPIS; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, @@ -728,14 +771,23 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) } if (vms->gic_version != VIRT_GIC_VERSION_2) { + QList *redist_region_count; uint32_t redist0_capacity = virt_redist_capacity(vms, VIRT_GIC_REDIST); uint32_t redist0_count = MIN(smp_cpus, redist0_capacity); nb_redist_regions = virt_gicv3_redist_region_count(vms); - qdev_prop_set_uint32(vms->gic, "len-redist-region-count", - nb_redist_regions); - qdev_prop_set_uint32(vms->gic, "redist-region-count[0]", redist0_count); + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, redist0_count); + if (nb_redist_regions == 2) { + uint32_t redist1_capacity = + virt_redist_capacity(vms, VIRT_HIGH_GIC_REDIST2); + + qlist_append_int(redist_region_count, + MIN(smp_cpus - redist0_count, redist1_capacity)); + } + qdev_prop_set_array(vms->gic, "redist-region-count", + redist_region_count); if (!kvm_irqchip_in_kernel()) { if (vms->tcg_its) { @@ -744,14 +796,6 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) qdev_prop_set_bit(vms->gic, "has-lpi", true); } } - - if (nb_redist_regions == 2) { - uint32_t redist1_capacity = - virt_redist_capacity(vms, VIRT_HIGH_GIC_REDIST2); - - qdev_prop_set_uint32(vms->gic, "redist-region-count[1]", - MIN(smp_cpus - redist0_count, redist1_capacity)); - } } else { if (!kvm_irqchip_in_kernel()) { qdev_prop_set_bit(vms->gic, "has-virtualization-extensions", @@ -781,8 +825,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) */ for (i = 0; i < smp_cpus; i++) { DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); - int ppibase = NUM_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS; - int irq; + int intidbase = NUM_IRQS + i * GIC_INTERNAL; /* Mapping from the output timer irq lines from the CPU to the * GIC PPI inputs we use for the virt board. */ @@ -791,27 +834,28 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, + [GTIMER_HYPVIRT] = ARCH_TIMER_NS_EL2_VIRT_IRQ, }; - for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { + for (unsigned irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { qdev_connect_gpio_out(cpudev, irq, qdev_get_gpio_in(vms->gic, - ppibase + timer_irq[irq])); + intidbase + timer_irq[irq])); } if (vms->gic_version != VIRT_GIC_VERSION_2) { qemu_irq irq = qdev_get_gpio_in(vms->gic, - ppibase + ARCH_GIC_MAINT_IRQ); + intidbase + 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(vms->gic, - ppibase + ARCH_GIC_MAINT_IRQ); + intidbase + 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(vms->gic, ppibase + qdev_get_gpio_in(vms->gic, intidbase + VIRTUAL_PMU_IRQ)); sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); @@ -1329,7 +1373,7 @@ static void create_smmu(const VirtMachineState *vms, return; } - dev = qdev_new("arm-smmuv3"); + dev = qdev_new(TYPE_ARM_SMMUV3); object_property_set_link(OBJECT(dev), "primary-bus", OBJECT(bus), &error_abort); @@ -1354,8 +1398,6 @@ static void create_smmu(const VirtMachineState *vms, qemu_fdt_setprop(ms->fdt, node, "interrupt-names", irq_names, sizeof(irq_names)); - qemu_fdt_setprop_cell(ms->fdt, node, "clocks", vms->clock_phandle); - qemu_fdt_setprop_string(ms->fdt, node, "clock-names", "apb_pclk"); qemu_fdt_setprop(ms->fdt, node, "dma-coherent", NULL, 0); qemu_fdt_setprop_cell(ms->fdt, node, "#iommu-cells", 1); @@ -1366,14 +1408,15 @@ static void create_smmu(const VirtMachineState *vms, static void create_virtio_iommu_dt_bindings(VirtMachineState *vms) { - const char compat[] = "virtio,pci-iommu"; + const char compat[] = "virtio,pci-iommu\0pci1af4,1057"; uint16_t bdf = vms->virtio_iommu_bdf; MachineState *ms = MACHINE(vms); char *node; vms->iommu_phandle = qemu_fdt_alloc_phandle(ms->fdt); - node = g_strdup_printf("%s/virtio_iommu@%d", vms->pciehb_nodename, bdf); + node = g_strdup_printf("%s/virtio_iommu@%x,%x", vms->pciehb_nodename, + PCI_SLOT(bdf), PCI_FUNC(bdf)); qemu_fdt_add_subnode(ms->fdt, node); qemu_fdt_setprop(ms->fdt, node, "compatible", compat, sizeof(compat)); qemu_fdt_setprop_sized_cells(ms->fdt, node, "reg", @@ -1410,6 +1453,7 @@ static void create_pcie(VirtMachineState *vms) int i, ecam_id; PCIHostState *pci; MachineState *ms = MACHINE(vms); + MachineClass *mc = MACHINE_GET_CLASS(ms); dev = qdev_new(TYPE_GPEX_HOST); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -1459,15 +1503,7 @@ static void create_pcie(VirtMachineState *vms) pci->bypass_iommu = vms->default_bus_bypass_iommu; vms->bus = pci->bus; if (vms->bus) { - for (i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - - if (!nd->model) { - nd->model = g_strdup("virtio"); - } - - pci_nic_init_nofail(nd, pci->bus, nd->model, NULL); - } + pci_init_nic_devices(pci->bus, mc->default_nic); } nodename = vms->pciehb_nodename = g_strdup_printf("/pcie@%" PRIx64, base); @@ -1483,8 +1519,8 @@ static void create_pcie(VirtMachineState *vms) qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); if (vms->msi_phandle) { - qemu_fdt_setprop_cells(ms->fdt, nodename, "msi-parent", - vms->msi_phandle); + qemu_fdt_setprop_cells(ms->fdt, nodename, "msi-map", + 0, vms->msi_phandle, 0, 0x10000); } qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", @@ -1602,9 +1638,11 @@ static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) static void virt_build_smbios(VirtMachineState *vms) { MachineClass *mc = MACHINE_GET_CLASS(vms); + MachineState *ms = MACHINE(vms); VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); uint8_t *smbios_tables, *smbios_anchor; size_t smbios_tables_len, smbios_anchor_len; + struct smbios_phys_mem_area mem_array; const char *product = "QEMU Virtual Machine"; if (kvm_enabled()) { @@ -1612,10 +1650,14 @@ static void virt_build_smbios(VirtMachineState *vms) } smbios_set_defaults("QEMU", product, - vmc->smbios_old_sys_ver ? "1.0" : mc->name, false, - true, SMBIOS_ENTRY_POINT_TYPE_64); + vmc->smbios_old_sys_ver ? "1.0" : mc->name, + true); - smbios_get_tables(MACHINE(vms), NULL, 0, + /* build the array of physical mem area from base_memmap */ + mem_array.address = vms->memmap[VIRT_MEM].base; + mem_array.length = ms->ram_size; + + smbios_get_tables(ms, SMBIOS_ENTRY_POINT_TYPE_64, &mem_array, 1, &smbios_tables, &smbios_tables_len, &smbios_anchor, &smbios_anchor_len, &error_fatal); @@ -1681,7 +1723,59 @@ static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx) clustersz = GICV3_TARGETLIST_BITS; } } - return arm_cpu_mp_affinity(idx, clustersz); + return arm_build_mp_affinity(idx, clustersz); +} + +static inline bool *virt_get_high_memmap_enabled(VirtMachineState *vms, + int index) +{ + bool *enabled_array[] = { + &vms->highmem_redists, + &vms->highmem_ecam, + &vms->highmem_mmio, + }; + + assert(ARRAY_SIZE(extended_memmap) - VIRT_LOWMEMMAP_LAST == + ARRAY_SIZE(enabled_array)); + assert(index - VIRT_LOWMEMMAP_LAST < ARRAY_SIZE(enabled_array)); + + return enabled_array[index - VIRT_LOWMEMMAP_LAST]; +} + +static void virt_set_high_memmap(VirtMachineState *vms, + hwaddr base, int pa_bits) +{ + hwaddr region_base, region_size; + bool *region_enabled, fits; + int i; + + for (i = VIRT_LOWMEMMAP_LAST; i < ARRAY_SIZE(extended_memmap); i++) { + region_enabled = virt_get_high_memmap_enabled(vms, i); + region_base = ROUND_UP(base, extended_memmap[i].size); + region_size = extended_memmap[i].size; + + vms->memmap[i].base = region_base; + vms->memmap[i].size = region_size; + + /* + * Check each device to see if it fits in the PA space, + * moving highest_gpa as we go. For compatibility, move + * highest_gpa for disabled fitting devices as well, if + * the compact layout has been disabled. + * + * For each device that doesn't fit, disable it. + */ + fits = (region_base + region_size) <= BIT_ULL(pa_bits); + *region_enabled &= fits; + if (vms->highmem_compact && !*region_enabled) { + continue; + } + + base = region_base + region_size; + if (fits) { + vms->highest_gpa = base - 1; + } + } } static void virt_set_memmap(VirtMachineState *vms, int pa_bits) @@ -1724,8 +1818,8 @@ static void virt_set_memmap(VirtMachineState *vms, int pa_bits) /* Base address of the high IO region */ memtop = base = device_memory_base + ROUND_UP(device_memory_size, GiB); if (memtop > BIT_ULL(pa_bits)) { - error_report("Addressing limited to %d bits, but memory exceeds it by %llu bytes\n", - pa_bits, memtop - BIT_ULL(pa_bits)); + error_report("Addressing limited to %d bits, but memory exceeds it by %llu bytes", + pa_bits, memtop - BIT_ULL(pa_bits)); exit(EXIT_FAILURE); } if (base < device_memory_base) { @@ -1739,48 +1833,91 @@ static void virt_set_memmap(VirtMachineState *vms, int pa_bits) /* We know for sure that at least the memory fits in the PA space */ vms->highest_gpa = memtop - 1; - for (i = VIRT_LOWMEMMAP_LAST; i < ARRAY_SIZE(extended_memmap); i++) { - hwaddr size = extended_memmap[i].size; - bool fits; - - base = ROUND_UP(base, size); - vms->memmap[i].base = base; - vms->memmap[i].size = size; - - /* - * Check each device to see if they fit in the PA space, - * moving highest_gpa as we go. - * - * For each device that doesn't fit, disable it. - */ - fits = (base + size) <= BIT_ULL(pa_bits); - if (fits) { - vms->highest_gpa = base + size - 1; - } - - switch (i) { - case VIRT_HIGH_GIC_REDIST2: - vms->highmem_redists &= fits; - break; - case VIRT_HIGH_PCIE_ECAM: - vms->highmem_ecam &= fits; - break; - case VIRT_HIGH_PCIE_MMIO: - vms->highmem_mmio &= fits; - break; - } - - base += size; - } + virt_set_high_memmap(vms, base, pa_bits); if (device_memory_size > 0) { - ms->device_memory = g_malloc0(sizeof(*ms->device_memory)); - ms->device_memory->base = device_memory_base; - memory_region_init(&ms->device_memory->mr, OBJECT(vms), - "device-memory", device_memory_size); + machine_memory_devices_init(ms, device_memory_base, device_memory_size); } } +static VirtGICType finalize_gic_version_do(const char *accel_name, + VirtGICType gic_version, + int gics_supported, + unsigned int max_cpus) +{ + /* Convert host/max/nosel to GIC version number */ + switch (gic_version) { + case VIRT_GIC_VERSION_HOST: + if (!kvm_enabled()) { + error_report("gic-version=host requires KVM"); + exit(1); + } + + /* For KVM, gic-version=host means gic-version=max */ + return finalize_gic_version_do(accel_name, VIRT_GIC_VERSION_MAX, + gics_supported, max_cpus); + case VIRT_GIC_VERSION_MAX: + if (gics_supported & VIRT_GIC_VERSION_4_MASK) { + gic_version = VIRT_GIC_VERSION_4; + } else if (gics_supported & VIRT_GIC_VERSION_3_MASK) { + gic_version = VIRT_GIC_VERSION_3; + } else { + gic_version = VIRT_GIC_VERSION_2; + } + break; + case VIRT_GIC_VERSION_NOSEL: + if ((gics_supported & VIRT_GIC_VERSION_2_MASK) && + max_cpus <= GIC_NCPU) { + gic_version = VIRT_GIC_VERSION_2; + } else if (gics_supported & VIRT_GIC_VERSION_3_MASK) { + /* + * in case the host does not support v2 emulation or + * the end-user requested more than 8 VCPUs we now default + * to v3. In any case defaulting to v2 would be broken. + */ + gic_version = VIRT_GIC_VERSION_3; + } else if (max_cpus > GIC_NCPU) { + error_report("%s only supports GICv2 emulation but more than 8 " + "vcpus are requested", accel_name); + exit(1); + } + break; + case VIRT_GIC_VERSION_2: + case VIRT_GIC_VERSION_3: + case VIRT_GIC_VERSION_4: + break; + } + + /* Check chosen version is effectively supported */ + switch (gic_version) { + case VIRT_GIC_VERSION_2: + if (!(gics_supported & VIRT_GIC_VERSION_2_MASK)) { + error_report("%s does not support GICv2 emulation", accel_name); + exit(1); + } + break; + case VIRT_GIC_VERSION_3: + if (!(gics_supported & VIRT_GIC_VERSION_3_MASK)) { + error_report("%s does not support GICv3 emulation", accel_name); + exit(1); + } + break; + case VIRT_GIC_VERSION_4: + if (!(gics_supported & VIRT_GIC_VERSION_4_MASK)) { + error_report("%s does not support GICv4 emulation, is virtualization=on?", + accel_name); + exit(1); + } + break; + default: + error_report("logic error in finalize_gic_version"); + exit(1); + break; + } + + return gic_version; +} + /* * finalize_gic_version - Determines the final gic_version * according to the gic-version property @@ -1789,118 +1926,49 @@ static void virt_set_memmap(VirtMachineState *vms, int pa_bits) */ static void finalize_gic_version(VirtMachineState *vms) { + const char *accel_name = current_accel_name(); unsigned int max_cpus = MACHINE(vms)->smp.max_cpus; + int gics_supported = 0; - if (kvm_enabled()) { - int probe_bitmap; + /* Determine which GIC versions the current environment supports */ + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + int probe_bitmap = kvm_arm_vgic_probe(); - if (!kvm_irqchip_in_kernel()) { - switch (vms->gic_version) { - case VIRT_GIC_VERSION_HOST: - warn_report( - "gic-version=host not relevant with kernel-irqchip=off " - "as only userspace GICv2 is supported. Using v2 ..."); - return; - case VIRT_GIC_VERSION_MAX: - case VIRT_GIC_VERSION_NOSEL: - vms->gic_version = VIRT_GIC_VERSION_2; - return; - case VIRT_GIC_VERSION_2: - return; - case VIRT_GIC_VERSION_3: - error_report( - "gic-version=3 is not supported with kernel-irqchip=off"); - exit(1); - case VIRT_GIC_VERSION_4: - error_report( - "gic-version=4 is not supported with kernel-irqchip=off"); - exit(1); - } - } - - probe_bitmap = kvm_arm_vgic_probe(); if (!probe_bitmap) { error_report("Unable to determine GIC version supported by host"); exit(1); } - switch (vms->gic_version) { - case VIRT_GIC_VERSION_HOST: - case VIRT_GIC_VERSION_MAX: - if (probe_bitmap & KVM_ARM_VGIC_V3) { - vms->gic_version = VIRT_GIC_VERSION_3; - } else { - vms->gic_version = VIRT_GIC_VERSION_2; - } - return; - case VIRT_GIC_VERSION_NOSEL: - if ((probe_bitmap & KVM_ARM_VGIC_V2) && max_cpus <= GIC_NCPU) { - vms->gic_version = VIRT_GIC_VERSION_2; - } else if (probe_bitmap & KVM_ARM_VGIC_V3) { - /* - * in case the host does not support v2 in-kernel emulation or - * the end-user requested more than 8 VCPUs we now default - * to v3. In any case defaulting to v2 would be broken. - */ - vms->gic_version = VIRT_GIC_VERSION_3; - } else if (max_cpus > GIC_NCPU) { - error_report("host only supports in-kernel GICv2 emulation " - "but more than 8 vcpus are requested"); - exit(1); - } - break; - case VIRT_GIC_VERSION_2: - case VIRT_GIC_VERSION_3: - break; - case VIRT_GIC_VERSION_4: - error_report("gic-version=4 is not supported with KVM"); - exit(1); + if (probe_bitmap & KVM_ARM_VGIC_V2) { + gics_supported |= VIRT_GIC_VERSION_2_MASK; } - - /* Check chosen version is effectively supported by the host */ - if (vms->gic_version == VIRT_GIC_VERSION_2 && - !(probe_bitmap & KVM_ARM_VGIC_V2)) { - error_report("host does not support in-kernel GICv2 emulation"); - exit(1); - } else if (vms->gic_version == VIRT_GIC_VERSION_3 && - !(probe_bitmap & KVM_ARM_VGIC_V3)) { - error_report("host does not support in-kernel GICv3 emulation"); - exit(1); + if (probe_bitmap & KVM_ARM_VGIC_V3) { + gics_supported |= VIRT_GIC_VERSION_3_MASK; } - return; - } - - /* TCG mode */ - switch (vms->gic_version) { - case VIRT_GIC_VERSION_NOSEL: - vms->gic_version = VIRT_GIC_VERSION_2; - break; - case VIRT_GIC_VERSION_MAX: + } else if (kvm_enabled() && !kvm_irqchip_in_kernel()) { + /* KVM w/o kernel irqchip can only deal with GICv2 */ + gics_supported |= VIRT_GIC_VERSION_2_MASK; + accel_name = "KVM with kernel-irqchip=off"; + } else if (tcg_enabled() || hvf_enabled() || qtest_enabled()) { + gics_supported |= VIRT_GIC_VERSION_2_MASK; if (module_object_class_by_name("arm-gicv3")) { - /* CONFIG_ARM_GICV3_TCG was set */ + gics_supported |= VIRT_GIC_VERSION_3_MASK; if (vms->virt) { /* GICv4 only makes sense if CPU has EL2 */ - vms->gic_version = VIRT_GIC_VERSION_4; - } else { - vms->gic_version = VIRT_GIC_VERSION_3; + gics_supported |= VIRT_GIC_VERSION_4_MASK; } - } else { - vms->gic_version = VIRT_GIC_VERSION_2; } - break; - case VIRT_GIC_VERSION_HOST: - error_report("gic-version=host requires KVM"); + } else { + error_report("Unsupported accelerator, can not determine GIC support"); exit(1); - case VIRT_GIC_VERSION_4: - if (!vms->virt) { - error_report("gic-version=4 requires virtualization enabled"); - exit(1); - } - break; - case VIRT_GIC_VERSION_2: - case VIRT_GIC_VERSION_3: - break; } + + /* + * Then convert helpers like host/max to concrete GIC versions and ensure + * the desired version is supported + */ + vms->gic_version = finalize_gic_version_do(accel_name, vms->gic_version, + gics_supported, max_cpus); } /* @@ -1945,13 +2013,14 @@ static void virt_cpu_post_init(VirtMachineState *vms, MemoryRegion *sysmem) if (pmu) { assert(arm_feature(&ARM_CPU(cpu)->env, ARM_FEATURE_PMU)); if (kvm_irqchip_in_kernel()) { - kvm_arm_pmu_set_irq(cpu, PPI(VIRTUAL_PMU_IRQ)); + kvm_arm_pmu_set_irq(ARM_CPU(cpu), VIRTUAL_PMU_IRQ); } - kvm_arm_pmu_init(cpu); + kvm_arm_pmu_init(ARM_CPU(cpu)); } if (steal_time) { - kvm_arm_pvtime_init(cpu, pvtime_reg_base + - cpu->cpu_index * PVTIME_SIZE_PER_CPU); + kvm_arm_pvtime_init(ARM_CPU(cpu), pvtime_reg_base + + cpu->cpu_index + * PVTIME_SIZE_PER_CPU); } } } else { @@ -1986,11 +2055,6 @@ static void machvirt_init(MachineState *machine) unsigned int smp_cpus = machine->smp.cpus; unsigned int max_cpus = machine->smp.max_cpus; - if (!cpu_type_valid(machine->cpu_type)) { - error_report("mach-virt: CPU type %s not supported", machine->cpu_type); - exit(1); - } - possible_cpus = mc->possible_cpu_arch_ids(machine); /* @@ -2003,22 +2067,14 @@ static void machvirt_init(MachineState *machine) int pa_bits; /* - * Instanciate a temporary CPU object to find out about what + * Instantiate a temporary CPU object to find out about what * we are about to deal with. Once this is done, get rid of * the object. */ cpuobj = object_new(possible_cpus->cpus[0].type); armcpu = ARM_CPU(cpuobj); - if (object_property_get_bool(cpuobj, "aarch64", NULL)) { - pa_bits = arm_pamax(armcpu); - } else if (arm_feature(&armcpu->env, ARM_FEATURE_LPAE)) { - /* v7 with LPAE */ - pa_bits = 40; - } else { - /* Anything else */ - pa_bits = 32; - } + pa_bits = arm_pamax(armcpu); object_unref(cpuobj); @@ -2072,35 +2128,41 @@ static void machvirt_init(MachineState *machine) if (vms->gic_version == VIRT_GIC_VERSION_2) { virt_max_cpus = GIC_NCPU; } else { - virt_max_cpus = virt_redist_capacity(vms, VIRT_GIC_REDIST) + - virt_redist_capacity(vms, VIRT_HIGH_GIC_REDIST2); + virt_max_cpus = virt_redist_capacity(vms, VIRT_GIC_REDIST); + if (vms->highmem_redists) { + virt_max_cpus += virt_redist_capacity(vms, VIRT_HIGH_GIC_REDIST2); + } } if (max_cpus > virt_max_cpus) { error_report("Number of SMP CPUs requested (%d) exceeds max CPUs " "supported by machine 'mach-virt' (%d)", max_cpus, virt_max_cpus); + if (vms->gic_version != VIRT_GIC_VERSION_2 && !vms->highmem_redists) { + error_printf("Try 'highmem-redists=on' for more CPUs\n"); + } + exit(1); } if (vms->secure && (kvm_enabled() || hvf_enabled())) { error_report("mach-virt: %s does not support providing " "Security extensions (TrustZone) to the guest CPU", - kvm_enabled() ? "KVM" : "HVF"); + current_accel_name()); exit(1); } if (vms->virt && (kvm_enabled() || hvf_enabled())) { error_report("mach-virt: %s does not support providing " "Virtualization extensions to the guest CPU", - kvm_enabled() ? "KVM" : "HVF"); + current_accel_name()); exit(1); } if (vms->mte && (kvm_enabled() || hvf_enabled())) { error_report("mach-virt: %s does not support providing " "MTE to the guest CPU", - kvm_enabled() ? "KVM" : "HVF"); + current_accel_name()); exit(1); } @@ -2206,15 +2268,16 @@ static void machvirt_init(MachineState *machine) qdev_realize(DEVICE(cpuobj), NULL, &error_fatal); object_unref(cpuobj); } + + /* Now we've created the CPUs we can see if they have the hypvirt timer */ + vms->ns_el2_virt_timer_irq = ns_el2_virt_timer_present() && + !vmc->no_ns_el2_virt_timer_irq; + fdt_add_timer_nodes(vms); fdt_add_cpu_nodes(vms); 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); - } virt_flash_fdt(vms, sysmem, secure_sysmem ?: sysmem); @@ -2334,6 +2397,63 @@ static void virt_set_highmem(Object *obj, bool value, Error **errp) vms->highmem = value; } +static bool virt_get_compact_highmem(Object *obj, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + return vms->highmem_compact; +} + +static void virt_set_compact_highmem(Object *obj, bool value, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + vms->highmem_compact = value; +} + +static bool virt_get_highmem_redists(Object *obj, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + return vms->highmem_redists; +} + +static void virt_set_highmem_redists(Object *obj, bool value, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + vms->highmem_redists = value; +} + +static bool virt_get_highmem_ecam(Object *obj, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + return vms->highmem_ecam; +} + +static void virt_set_highmem_ecam(Object *obj, bool value, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + vms->highmem_ecam = value; +} + +static bool virt_get_highmem_mmio(Object *obj, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + return vms->highmem_mmio; +} + +static void virt_set_highmem_mmio(Object *obj, bool value, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + vms->highmem_mmio = value; +} + + static bool virt_get_its(Object *obj, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(obj); @@ -2348,18 +2468,18 @@ static void virt_set_its(Object *obj, bool value, Error **errp) vms->its = value; } -static bool virt_get_dtb_kaslr_seed(Object *obj, Error **errp) +static bool virt_get_dtb_randomness(Object *obj, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(obj); - return vms->dtb_kaslr_seed; + return vms->dtb_randomness; } -static void virt_set_dtb_kaslr_seed(Object *obj, bool value, Error **errp) +static void virt_set_dtb_randomness(Object *obj, bool value, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(obj); - vms->dtb_kaslr_seed = value; + vms->dtb_randomness = value; } static char *virt_get_oem_id(Object *obj, Error **errp) @@ -2637,64 +2757,6 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev, dev, &error_abort); } -static void virt_virtio_md_pci_pre_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - HotplugHandler *hotplug_dev2 = qdev_get_bus_hotplug_handler(dev); - Error *local_err = NULL; - - if (!hotplug_dev2 && dev->hotplugged) { - /* - * Without a bus hotplug handler, we cannot control the plug/unplug - * order. We should never reach this point when hotplugging on ARM. - * However, it's nice to add a safety net, similar to what we have - * on x86. - */ - error_setg(errp, "hotplug of virtio based memory devices not supported" - " on this bus."); - return; - } - /* - * First, see if we can plug this memory device at all. If that - * succeeds, branch of to the actual hotplug handler. - */ - memory_device_pre_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev), NULL, - &local_err); - if (!local_err && hotplug_dev2) { - hotplug_handler_pre_plug(hotplug_dev2, dev, &local_err); - } - error_propagate(errp, local_err); -} - -static void virt_virtio_md_pci_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - HotplugHandler *hotplug_dev2 = qdev_get_bus_hotplug_handler(dev); - Error *local_err = NULL; - - /* - * Plug the memory device first and then branch off to the actual - * hotplug handler. If that one fails, we can easily undo the memory - * device bits. - */ - memory_device_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev)); - if (hotplug_dev2) { - hotplug_handler_plug(hotplug_dev2, dev, &local_err); - if (local_err) { - memory_device_unplug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev)); - } - } - error_propagate(errp, local_err); -} - -static void virt_virtio_md_pci_unplug_request(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - /* We don't support hot unplug of virtio based memory devices */ - error_setg(errp, "virtio based memory devices cannot be unplugged."); -} - - static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -2702,10 +2764,11 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { virt_memory_pre_plug(hotplug_dev, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) { - virt_virtio_md_pci_pre_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { + virtio_md_pci_pre_plug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { hwaddr db_start = 0, db_end = 0; + QList *reserved_regions; char *resv_prop_str; if (vms->iommu != VIRT_IOMMU_NONE) { @@ -2732,9 +2795,9 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, db_start, db_end, VIRTIO_IOMMU_RESV_MEM_T_MSI); - object_property_set_uint(OBJECT(dev), "len-reserved-regions", 1, errp); - object_property_set_str(OBJECT(dev), "reserved-regions[0]", - resv_prop_str, errp); + reserved_regions = qlist_new(); + qlist_append_str(reserved_regions, resv_prop_str); + qdev_prop_set_array(dev, "reserved-regions", reserved_regions); g_free(resv_prop_str); } } @@ -2752,12 +2815,11 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, SYS_BUS_DEVICE(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_MEM_PCI)) { - virt_virtio_md_pci_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { + virtio_md_pci_plug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp); } if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { @@ -2773,24 +2835,20 @@ static void virt_dimm_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); - Error *local_err = NULL; if (!vms->acpi_dev) { - error_setg(&local_err, + error_setg(errp, "memory hotplug is not enabled: missing acpi-ged device"); - goto out; + return; } if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) { - error_setg(&local_err, - "nvdimm device hot unplug is not supported yet."); - goto out; + error_setg(errp, "nvdimm device hot unplug is not supported yet."); + return; } hotplug_handler_unplug_request(HOTPLUG_HANDLER(vms->acpi_dev), dev, - &local_err); -out: - error_propagate(errp, local_err); + errp); } static void virt_dimm_unplug(HotplugHandler *hotplug_dev, @@ -2816,8 +2874,9 @@ static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev, { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { virt_dimm_unplug_request(hotplug_dev, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) { - virt_virtio_md_pci_unplug_request(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { + virtio_md_pci_unplug_request(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), + errp); } else { error_setg(errp, "device unplug request for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -2829,6 +2888,8 @@ static void virt_machine_device_unplug_cb(HotplugHandler *hotplug_dev, { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { virt_dimm_unplug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { + virtio_md_pci_unplug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp); } else { error_setg(errp, "virt: device unplug for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -2842,7 +2903,7 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, if (device_is_dynamic_sysbus(mc, dev) || object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || - object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { return HOTPLUG_HANDLER(machine); } @@ -2878,7 +2939,7 @@ static int virt_kvm_type(MachineState *ms, const char *type_str) "require an IPA range (%d bits) larger than " "the one supported by the host (%d bits)", requested_pa_size, max_vm_pa_size); - exit(1); + return -1; } /* * We return the requested PA log size, unless KVM only supports @@ -2892,6 +2953,32 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + static const char * const valid_cpu_types[] = { +#ifdef CONFIG_TCG + ARM_CPU_TYPE_NAME("cortex-a7"), + ARM_CPU_TYPE_NAME("cortex-a15"), +#ifdef TARGET_AARCH64 + ARM_CPU_TYPE_NAME("cortex-a35"), + ARM_CPU_TYPE_NAME("cortex-a55"), + ARM_CPU_TYPE_NAME("cortex-a72"), + ARM_CPU_TYPE_NAME("cortex-a76"), + ARM_CPU_TYPE_NAME("cortex-a710"), + ARM_CPU_TYPE_NAME("a64fx"), + ARM_CPU_TYPE_NAME("neoverse-n1"), + ARM_CPU_TYPE_NAME("neoverse-v1"), + ARM_CPU_TYPE_NAME("neoverse-n2"), +#endif /* TARGET_AARCH64 */ +#endif /* CONFIG_TCG */ +#ifdef TARGET_AARCH64 + ARM_CPU_TYPE_NAME("cortex-a53"), + ARM_CPU_TYPE_NAME("cortex-a57"), +#if defined(CONFIG_KVM) || defined(CONFIG_HVF) + ARM_CPU_TYPE_NAME("host"), +#endif /* CONFIG_KVM || CONFIG_HVF */ +#endif /* TARGET_AARCH64 */ + ARM_CPU_TYPE_NAME("max"), + NULL + }; mc->init = machvirt_init; /* Start with max_cpus set to 512, which is the maximum supported by KVM. @@ -2913,7 +3000,12 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->minimum_page_bits = 12; mc->possible_cpu_arch_ids = virt_possible_cpu_arch_ids; mc->cpu_index_to_instance_props = virt_cpu_index_to_props; +#ifdef CONFIG_TCG mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15"); +#else + mc->default_cpu_type = ARM_CPU_TYPE_NAME("max"); +#endif + mc->valid_cpu_types = valid_cpu_types; mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; mc->kvm_type = virt_kvm_type; assert(!mc->get_hotplug_handler); @@ -2926,7 +3018,10 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->smp_props.clusters_supported = true; mc->auto_enable_numa_with_memhp = true; mc->auto_enable_numa_with_memdev = true; + /* platform instead of architectural choice */ + mc->cpu_cluster_has_numa_boundary = true; mc->default_ram_id = "mach-virt.ram"; + mc->default_nic = "virtio-net-pci"; object_class_property_add(oc, "acpi", "OnOffAuto", virt_get_acpi, virt_set_acpi, @@ -2952,6 +3047,35 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) "Set on/off to enable/disable using " "physical address space above 32 bits"); + object_class_property_add_bool(oc, "compact-highmem", + virt_get_compact_highmem, + virt_set_compact_highmem); + object_class_property_set_description(oc, "compact-highmem", + "Set on/off to enable/disable compact " + "layout for high memory regions"); + + object_class_property_add_bool(oc, "highmem-redists", + virt_get_highmem_redists, + virt_set_highmem_redists); + object_class_property_set_description(oc, "highmem-redists", + "Set on/off to enable/disable high " + "memory region for GICv3 or GICv4 " + "redistributor"); + + object_class_property_add_bool(oc, "highmem-ecam", + virt_get_highmem_ecam, + virt_set_highmem_ecam); + object_class_property_set_description(oc, "highmem-ecam", + "Set on/off to enable/disable high " + "memory region for PCI ECAM"); + + object_class_property_add_bool(oc, "highmem-mmio", + virt_get_highmem_mmio, + virt_set_highmem_mmio); + object_class_property_set_description(oc, "highmem-mmio", + "Set on/off to enable/disable high " + "memory region for PCI MMIO"); + object_class_property_add_str(oc, "gic-version", virt_get_gic_version, virt_set_gic_version); object_class_property_set_description(oc, "gic-version", @@ -2988,12 +3112,18 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) "Set on/off to enable/disable " "ITS instantiation"); + object_class_property_add_bool(oc, "dtb-randomness", + virt_get_dtb_randomness, + virt_set_dtb_randomness); + object_class_property_set_description(oc, "dtb-randomness", + "Set off to disable passing random or " + "non-deterministic dtb nodes to guest"); + object_class_property_add_bool(oc, "dtb-kaslr-seed", - virt_get_dtb_kaslr_seed, - virt_set_dtb_kaslr_seed); + virt_get_dtb_randomness, + virt_set_dtb_randomness); object_class_property_set_description(oc, "dtb-kaslr-seed", - "Set off to disable passing of kaslr-seed " - "dtb node to guest"); + "Deprecated synonym of dtb-randomness"); object_class_property_add_str(oc, "x-oem-id", virt_get_oem_id, @@ -3030,6 +3160,7 @@ static void virt_instance_init(Object *obj) /* High memory is enabled by default */ vms->highmem = true; + vms->highmem_compact = !vmc->no_highmem_compact; vms->gic_version = VIRT_GIC_VERSION_NOSEL; vms->highmem_ecam = !vmc->no_highmem_ecam; @@ -3061,8 +3192,8 @@ static void virt_instance_init(Object *obj) /* MTE is disabled by default. */ vms->mte = false; - /* Supply a kaslr-seed by default */ - vms->dtb_kaslr_seed = true; + /* Supply kaslr-seed and rng-seed by default */ + vms->dtb_randomness = true; vms->irqmap = a15irqmap; @@ -3092,10 +3223,57 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); -static void virt_machine_7_1_options(MachineClass *mc) +static void virt_machine_9_0_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(7, 1) +DEFINE_VIRT_MACHINE_AS_LATEST(9, 0) + +static void virt_machine_8_2_options(MachineClass *mc) +{ + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + + virt_machine_9_0_options(mc); + compat_props_add(mc->compat_props, hw_compat_8_2, hw_compat_8_2_len); + /* + * Don't expose NS_EL2_VIRT timer IRQ in DTB on ACPI on 8.2 and + * earlier machines. (Exposing it tickles a bug in older EDK2 + * guest BIOS binaries.) + */ + vmc->no_ns_el2_virt_timer_irq = true; +} +DEFINE_VIRT_MACHINE(8, 2) + +static void virt_machine_8_1_options(MachineClass *mc) +{ + virt_machine_8_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_8_1, hw_compat_8_1_len); +} +DEFINE_VIRT_MACHINE(8, 1) + +static void virt_machine_8_0_options(MachineClass *mc) +{ + virt_machine_8_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_8_0, hw_compat_8_0_len); +} +DEFINE_VIRT_MACHINE(8, 0) + +static void virt_machine_7_2_options(MachineClass *mc) +{ + virt_machine_8_0_options(mc); + compat_props_add(mc->compat_props, hw_compat_7_2, hw_compat_7_2_len); +} +DEFINE_VIRT_MACHINE(7, 2) + +static void virt_machine_7_1_options(MachineClass *mc) +{ + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + + virt_machine_7_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_7_1, hw_compat_7_1_len); + /* Compact layout for high memory regions was introduced with 7.2 */ + vmc->no_highmem_compact = true; +} +DEFINE_VIRT_MACHINE(7, 1) static void virt_machine_7_0_options(MachineClass *mc) { diff --git a/hw/arm/xen_arm.c b/hw/arm/xen_arm.c new file mode 100644 index 0000000000..15fa7dfa84 --- /dev/null +++ b/hw/arm/xen_arm.c @@ -0,0 +1,262 @@ +/* + * QEMU ARM Xen PVH Machine + * + * + * 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/error-report.h" +#include "qapi/qapi-commands-migration.h" +#include "qapi/visitor.h" +#include "hw/boards.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "sysemu/block-backend.h" +#include "sysemu/tpm_backend.h" +#include "sysemu/sysemu.h" +#include "hw/xen/xen-hvm-common.h" +#include "sysemu/tpm.h" +#include "hw/xen/arch_hvm.h" +#include "trace.h" + +#define TYPE_XEN_ARM MACHINE_TYPE_NAME("xenpvh") +OBJECT_DECLARE_SIMPLE_TYPE(XenArmState, XEN_ARM) + +static const MemoryListener xen_memory_listener = { + .region_add = xen_region_add, + .region_del = xen_region_del, + .log_start = NULL, + .log_stop = NULL, + .log_sync = NULL, + .log_global_start = NULL, + .log_global_stop = NULL, + .priority = MEMORY_LISTENER_PRIORITY_ACCEL, +}; + +struct XenArmState { + /*< private >*/ + MachineState parent; + + XenIOState *state; + + struct { + uint64_t tpm_base_addr; + } cfg; +}; + +static MemoryRegion ram_lo, ram_hi; + +/* + * VIRTIO_MMIO_DEV_SIZE is imported from tools/libs/light/libxl_arm.c under Xen + * repository. + * + * Origin: git://xenbits.xen.org/xen.git 2128143c114c + */ +#define VIRTIO_MMIO_DEV_SIZE 0x200 + +#define NR_VIRTIO_MMIO_DEVICES \ + (GUEST_VIRTIO_MMIO_SPI_LAST - GUEST_VIRTIO_MMIO_SPI_FIRST) + +static void xen_set_irq(void *opaque, int irq, int level) +{ + if (xendevicemodel_set_irq_level(xen_dmod, xen_domid, irq, level)) { + error_report("xendevicemodel_set_irq_level failed"); + } +} + +static void xen_create_virtio_mmio_devices(XenArmState *xam) +{ + int i; + + for (i = 0; i < NR_VIRTIO_MMIO_DEVICES; i++) { + hwaddr base = GUEST_VIRTIO_MMIO_BASE + i * VIRTIO_MMIO_DEV_SIZE; + qemu_irq irq = qemu_allocate_irq(xen_set_irq, NULL, + GUEST_VIRTIO_MMIO_SPI_FIRST + i); + + sysbus_create_simple("virtio-mmio", base, irq); + + trace_xen_create_virtio_mmio_devices(i, + GUEST_VIRTIO_MMIO_SPI_FIRST + i, + base); + } +} + +static void xen_init_ram(MachineState *machine) +{ + MemoryRegion *sysmem = get_system_memory(); + ram_addr_t block_len, ram_size[GUEST_RAM_BANKS]; + + trace_xen_init_ram(machine->ram_size); + if (machine->ram_size <= GUEST_RAM0_SIZE) { + ram_size[0] = machine->ram_size; + ram_size[1] = 0; + block_len = GUEST_RAM0_BASE + ram_size[0]; + } else { + ram_size[0] = GUEST_RAM0_SIZE; + ram_size[1] = machine->ram_size - GUEST_RAM0_SIZE; + block_len = GUEST_RAM1_BASE + ram_size[1]; + } + + memory_region_init_ram(&xen_memory, NULL, "xen.ram", block_len, + &error_fatal); + + memory_region_init_alias(&ram_lo, NULL, "xen.ram.lo", &xen_memory, + GUEST_RAM0_BASE, ram_size[0]); + memory_region_add_subregion(sysmem, GUEST_RAM0_BASE, &ram_lo); + if (ram_size[1] > 0) { + memory_region_init_alias(&ram_hi, NULL, "xen.ram.hi", &xen_memory, + GUEST_RAM1_BASE, ram_size[1]); + memory_region_add_subregion(sysmem, GUEST_RAM1_BASE, &ram_hi); + } +} + +void arch_handle_ioreq(XenIOState *state, ioreq_t *req) +{ + hw_error("Invalid ioreq type 0x%x\n", req->type); + + return; +} + +void arch_xen_set_memory(XenIOState *state, MemoryRegionSection *section, + bool add) +{ +} + +void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) +{ +} + +void qmp_xen_set_global_dirty_log(bool enable, Error **errp) +{ +} + +#ifdef CONFIG_TPM +static void xen_enable_tpm(XenArmState *xam) +{ + Error *errp = NULL; + DeviceState *dev; + SysBusDevice *busdev; + + TPMBackend *be = qemu_find_tpm_be("tpm0"); + if (be == NULL) { + error_report("Couldn't find tmp0 backend"); + return; + } + dev = qdev_new(TYPE_TPM_TIS_SYSBUS); + object_property_set_link(OBJECT(dev), "tpmdev", OBJECT(be), &errp); + object_property_set_str(OBJECT(dev), "tpmdev", be->id, &errp); + busdev = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(busdev, &error_fatal); + sysbus_mmio_map(busdev, 0, xam->cfg.tpm_base_addr); + + trace_xen_enable_tpm(xam->cfg.tpm_base_addr); +} +#endif + +static void xen_arm_init(MachineState *machine) +{ + XenArmState *xam = XEN_ARM(machine); + + xam->state = g_new0(XenIOState, 1); + + if (machine->ram_size == 0) { + warn_report("%s non-zero ram size not specified. QEMU machine started" + " without IOREQ (no emulated devices including virtio)", + MACHINE_CLASS(object_get_class(OBJECT(machine)))->desc); + return; + } + + xen_init_ram(machine); + + xen_register_ioreq(xam->state, machine->smp.cpus, &xen_memory_listener); + + xen_create_virtio_mmio_devices(xam); + +#ifdef CONFIG_TPM + if (xam->cfg.tpm_base_addr) { + xen_enable_tpm(xam); + } else { + warn_report("tpm-base-addr is not provided. TPM will not be enabled"); + } +#endif +} + +#ifdef CONFIG_TPM +static void xen_arm_get_tpm_base_addr(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + XenArmState *xam = XEN_ARM(obj); + uint64_t value = xam->cfg.tpm_base_addr; + + visit_type_uint64(v, name, &value, errp); +} + +static void xen_arm_set_tpm_base_addr(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + XenArmState *xam = XEN_ARM(obj); + uint64_t value; + + if (!visit_type_uint64(v, name, &value, errp)) { + return; + } + + xam->cfg.tpm_base_addr = value; +} +#endif + +static void xen_arm_machine_class_init(ObjectClass *oc, void *data) +{ + + MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "Xen Para-virtualized PC"; + mc->init = xen_arm_init; + mc->max_cpus = 1; + mc->default_machine_opts = "accel=xen"; + /* Set explicitly here to make sure that real ram_size is passed */ + mc->default_ram_size = 0; + +#ifdef CONFIG_TPM + object_class_property_add(oc, "tpm-base-addr", "uint64_t", + xen_arm_get_tpm_base_addr, + xen_arm_set_tpm_base_addr, + NULL, NULL); + object_class_property_set_description(oc, "tpm-base-addr", + "Set Base address for TPM device."); + + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); +#endif +} + +static const TypeInfo xen_arm_machine_type = { + .name = TYPE_XEN_ARM, + .parent = TYPE_MACHINE, + .class_init = xen_arm_machine_class_init, + .instance_size = sizeof(XenArmState), +}; + +static void xen_arm_machine_register_types(void) +{ + type_register_static(&xen_arm_machine_type); +} + +type_init(xen_arm_machine_register_types) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 3190cc0b8d..fc3abcbe88 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -18,7 +18,6 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "cpu.h" #include "hw/sysbus.h" #include "hw/arm/boot.h" #include "net/net.h" @@ -37,6 +36,8 @@ #include "hw/qdev-clock.h" #include "sysemu/reset.h" #include "qom/object.h" +#include "exec/tswap.h" +#include "target/arm/cpu-qom.h" #define TYPE_ZYNQ_MACHINE MACHINE_TYPE_NAME("xilinx-zynq-a9") OBJECT_DECLARE_SIMPLE_TYPE(ZynqMachineState, ZYNQ_MACHINE) @@ -108,16 +109,13 @@ static void zynq_write_board_setup(ARMCPU *cpu, static struct arm_boot_info zynq_binfo = {}; -static void gem_init(NICInfo *nd, uint32_t base, qemu_irq irq) +static void gem_init(uint32_t base, qemu_irq irq) { DeviceState *dev; SysBusDevice *s; dev = qdev_new(TYPE_CADENCE_GEM); - if (nd->used) { - qemu_check_nic_model(nd, TYPE_CADENCE_GEM); - qdev_set_nic_properties(dev, nd); - } + qemu_configure_nic_device(dev, true, NULL); object_property_set_int(OBJECT(dev), "phy-addr", 7, &error_abort); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); @@ -164,6 +162,7 @@ static inline int zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq, blk_by_legacy_dinfo(dinfo), &error_fatal); } + qdev_prop_set_uint8(flash_dev, "cs", j); qdev_realize_and_unref(flash_dev, BUS(spi), &error_fatal); cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); @@ -244,6 +243,8 @@ static void zynq_init(MachineState *machine) sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); + sysbus_connect_irq(busdev, 1, + qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ)); for (n = 0; n < 64; n++) { pic[n] = qdev_get_gpio_in(dev, n); @@ -278,8 +279,8 @@ static void zynq_init(MachineState *machine) sysbus_create_varargs("cadence_ttc", 0xF8002000, pic[69-IRQ_OFFSET], pic[70-IRQ_OFFSET], pic[71-IRQ_OFFSET], NULL); - gem_init(&nd_table[0], 0xE000B000, pic[54-IRQ_OFFSET]); - gem_init(&nd_table[1], 0xE000C000, pic[77-IRQ_OFFSET]); + gem_init(0xE000B000, pic[54 - IRQ_OFFSET]); + gem_init(0xE000C000, pic[77 - IRQ_OFFSET]); for (n = 0; n < 2; n++) { int hci_irq = n ? 79 : 56; @@ -348,18 +349,22 @@ static void zynq_init(MachineState *machine) zynq_binfo.board_setup_addr = BOARD_SETUP_ADDR; zynq_binfo.write_board_setup = zynq_write_board_setup; - arm_load_kernel(ARM_CPU(first_cpu), machine, &zynq_binfo); + arm_load_kernel(cpu, machine, &zynq_binfo); } static void zynq_machine_class_init(ObjectClass *oc, void *data) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a9"), + NULL + }; MachineClass *mc = MACHINE_CLASS(oc); mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9"; mc->init = zynq_init; mc->max_cpus = 1; mc->no_sdcard = 1; mc->ignore_memory_transaction_failures = true; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a9"); + mc->valid_cpu_types = valid_cpu_types; mc->default_ram_id = "zynq.ext_ram"; } diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 37fc9b919c..962f98fee2 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -13,12 +13,14 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "sysemu/device_tree.h" +#include "hw/block/flash.h" #include "hw/boards.h" #include "hw/sysbus.h" #include "hw/arm/fdt.h" -#include "cpu.h" #include "hw/qdev-properties.h" #include "hw/arm/xlnx-versal.h" +#include "hw/arm/boot.h" +#include "target/arm/multiprocessing.h" #include "qom/object.h" #define TYPE_XLNX_VERSAL_VIRT_MACHINE MACHINE_TYPE_NAME("xlnx-versal-virt") @@ -40,12 +42,15 @@ struct VersalVirt { uint32_t clk_25Mhz; uint32_t usb; uint32_t dwc; + uint32_t canfd[2]; } phandle; struct arm_boot_info binfo; + CanBusState *canbus[XLNX_VERSAL_NR_CANFD]; struct { bool secure; } cfg; + char *ospi_model; }; static void fdt_create(VersalVirt *s) @@ -104,7 +109,8 @@ static void fdt_add_cpu_nodes(VersalVirt *s, uint32_t psci_conduit) ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i)); qemu_fdt_add_subnode(s->fdt, name); - qemu_fdt_setprop_cell(s->fdt, name, "reg", armcpu->mp_affinity); + qemu_fdt_setprop_cell(s->fdt, name, "reg", + arm_cpu_mp_affinity(armcpu)); if (psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) { qemu_fdt_setprop_string(s->fdt, name, "enable-method", "psci"); } @@ -235,6 +241,38 @@ static void fdt_add_uart_nodes(VersalVirt *s) } } +static void fdt_add_canfd_nodes(VersalVirt *s) +{ + uint64_t addrs[] = { MM_CANFD1, MM_CANFD0 }; + uint32_t size[] = { MM_CANFD1_SIZE, MM_CANFD0_SIZE }; + unsigned int irqs[] = { VERSAL_CANFD1_IRQ_0, VERSAL_CANFD0_IRQ_0 }; + const char clocknames[] = "can_clk\0s_axi_aclk"; + int i; + + /* Create and connect CANFD0 and CANFD1 nodes to canbus0. */ + for (i = 0; i < ARRAY_SIZE(addrs); i++) { + char *name = g_strdup_printf("/canfd@%" PRIx64, addrs[i]); + qemu_fdt_add_subnode(s->fdt, name); + + qemu_fdt_setprop_cell(s->fdt, name, "rx-fifo-depth", 0x40); + qemu_fdt_setprop_cell(s->fdt, name, "tx-mailbox-count", 0x20); + + 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, irqs[i], + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", + 2, addrs[i], 2, size[i]); + qemu_fdt_setprop_string(s->fdt, name, "compatible", + "xlnx,canfd-2.0"); + + g_free(name); + } +} + static void fdt_add_fixed_link_nodes(VersalVirt *s, char *gemname, uint32_t phandle) { @@ -602,6 +640,22 @@ static void sd_plugin_card(SDHCIState *sd, DriveInfo *di) &error_fatal); } +static char *versal_get_ospi_model(Object *obj, Error **errp) +{ + VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); + + return g_strdup(s->ospi_model); +} + +static void versal_set_ospi_model(Object *obj, const char *value, Error **errp) +{ + VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); + + g_free(s->ospi_model); + s->ospi_model = g_strdup(value); +} + + static void versal_virt_init(MachineState *machine) { VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(machine); @@ -639,12 +693,17 @@ static void versal_virt_init(MachineState *machine) TYPE_XLNX_VERSAL); object_property_set_link(OBJECT(&s->soc), "ddr", OBJECT(machine->ram), &error_abort); + object_property_set_link(OBJECT(&s->soc), "canbus0", OBJECT(s->canbus[0]), + &error_abort); + object_property_set_link(OBJECT(&s->soc), "canbus1", OBJECT(s->canbus[1]), + &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); fdt_create(s); create_virtio_regions(s); fdt_add_gem_nodes(s); fdt_add_uart_nodes(s); + fdt_add_canfd_nodes(s); fdt_add_gic_nodes(s); fdt_add_timer_nodes(s); fdt_add_zdma_nodes(s); @@ -659,7 +718,7 @@ static void versal_virt_init(MachineState *machine) fdt_add_clk_node(s, "/clk25", 25000000, s->phandle.clk_25Mhz); /* Make the APU cpu address space visible to virtio and other - * modules unaware of muliple address-spaces. */ + * modules unaware of multiple address-spaces. */ memory_region_add_subregion_overlap(get_system_memory(), 0, &s->soc.fpd.apu.mr, 0); @@ -691,16 +750,30 @@ static void versal_virt_init(MachineState *machine) for (i = 0; i < XLNX_VERSAL_NUM_OSPI_FLASH; i++) { BusState *spi_bus; DeviceState *flash_dev; + ObjectClass *flash_klass; qemu_irq cs_line; DriveInfo *dinfo = drive_get(IF_MTD, 0, i); spi_bus = qdev_get_child_bus(DEVICE(&s->soc.pmc.iou.ospi), "spi0"); - flash_dev = qdev_new("mt35xu01g"); + if (s->ospi_model) { + flash_klass = object_class_by_name(s->ospi_model); + if (!flash_klass || + object_class_is_abstract(flash_klass) || + !object_class_dynamic_cast(flash_klass, TYPE_M25P80)) { + error_setg(&error_fatal, "'%s' is either abstract or" + " not a subtype of m25p80", s->ospi_model); + return; + } + } + + flash_dev = qdev_new(s->ospi_model ? s->ospi_model : "mt35xu01g"); + if (dinfo) { qdev_prop_set_drive_err(flash_dev, "drive", blk_by_legacy_dinfo(dinfo), &error_fatal); } + qdev_prop_set_uint8(flash_dev, "cs", i); qdev_realize_and_unref(flash_dev, spi_bus, &error_fatal); cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); @@ -712,6 +785,27 @@ static void versal_virt_init(MachineState *machine) static void versal_virt_machine_instance_init(Object *obj) { + VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); + + /* + * User can set canbus0 and canbus1 properties to can-bus object and connect + * to socketcan(optional) interface via command line. + */ + object_property_add_link(obj, "canbus0", TYPE_CAN_BUS, + (Object **)&s->canbus[0], + object_property_allow_set_link, + 0); + object_property_add_link(obj, "canbus1", TYPE_CAN_BUS, + (Object **)&s->canbus[1], + object_property_allow_set_link, + 0); +} + +static void versal_virt_machine_finalize(Object *obj) +{ + VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(obj); + + g_free(s->ospi_model); } static void versal_virt_machine_class_init(ObjectClass *oc, void *data) @@ -725,6 +819,10 @@ static void versal_virt_machine_class_init(ObjectClass *oc, void *data) mc->default_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS; mc->no_cdrom = true; mc->default_ram_id = "ddr"; + object_class_property_add_str(oc, "ospi-flash", versal_get_ospi_model, + versal_set_ospi_model); + object_class_property_set_description(oc, "ospi-flash", + "Change the OSPI Flash model"); } static const TypeInfo versal_virt_machine_init_typeinfo = { @@ -733,6 +831,7 @@ static const TypeInfo versal_virt_machine_init_typeinfo = { .class_init = versal_virt_machine_class_init, .instance_init = versal_virt_machine_instance_init, .instance_size = sizeof(VersalVirt), + .instance_finalize = versal_virt_machine_finalize, }; static void versal_virt_machine_init_register_types(void) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 57276e1506..50cb0606cb 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" +#include "qapi/qmp/qlist.h" #include "qemu/module.h" #include "hw/sysbus.h" #include "net/net.h" @@ -22,13 +23,14 @@ #include "hw/misc/unimp.h" #include "hw/arm/xlnx-versal.h" #include "qemu/log.h" -#include "hw/sysbus.h" +#include "target/arm/cpu-qom.h" +#include "target/arm/gtimer.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") #define GEM_REVISION 0x40070106 -#define VERSAL_NUM_PMC_APB_IRQS 3 +#define VERSAL_NUM_PMC_APB_IRQS 18 #define NUM_OSPI_IRQ_LINES 3 static void versal_create_apu_cpus(Versal *s) @@ -70,6 +72,7 @@ static void versal_create_apu_gic(Versal *s, qemu_irq *pic) }; SysBusDevice *gicbusdev; DeviceState *gicdev; + QList *redist_region_count; int nr_apu_cpus = ARRAY_SIZE(s->fpd.apu.cpu); int i; @@ -80,8 +83,11 @@ static void versal_create_apu_gic(Versal *s, qemu_irq *pic) qdev_prop_set_uint32(gicdev, "revision", 3); qdev_prop_set_uint32(gicdev, "num-cpu", nr_apu_cpus); qdev_prop_set_uint32(gicdev, "num-irq", XLNX_VERSAL_NR_IRQS + 32); - qdev_prop_set_uint32(gicdev, "len-redist-region-count", 1); - qdev_prop_set_uint32(gicdev, "redist-region-count[0]", nr_apu_cpus); + + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, nr_apu_cpus); + qdev_prop_set_array(gicdev, "redist-region-count", redist_region_count); + qdev_prop_set_bit(gicdev, "has-security-extensions", true); sysbus_realize(SYS_BUS_DEVICE(&s->fpd.apu.gic), &error_fatal); @@ -185,6 +191,38 @@ static void versal_create_uarts(Versal *s, qemu_irq *pic) } } +static void versal_create_canfds(Versal *s, qemu_irq *pic) +{ + int i; + uint32_t irqs[] = { VERSAL_CANFD0_IRQ_0, VERSAL_CANFD1_IRQ_0}; + uint64_t addrs[] = { MM_CANFD0, MM_CANFD1 }; + + for (i = 0; i < ARRAY_SIZE(s->lpd.iou.canfd); i++) { + char *name = g_strdup_printf("canfd%d", i); + SysBusDevice *sbd; + MemoryRegion *mr; + + object_initialize_child(OBJECT(s), name, &s->lpd.iou.canfd[i], + TYPE_XILINX_CANFD); + sbd = SYS_BUS_DEVICE(&s->lpd.iou.canfd[i]); + + object_property_set_int(OBJECT(&s->lpd.iou.canfd[i]), "ext_clk_freq", + XLNX_VERSAL_CANFD_REF_CLK , &error_abort); + + object_property_set_link(OBJECT(&s->lpd.iou.canfd[i]), "canfdbus", + OBJECT(s->lpd.iou.canbus[i]), + &error_abort); + + sysbus_realize(sbd, &error_fatal); + + mr = sysbus_mmio_get_region(sbd, 0); + memory_region_add_subregion(&s->mr_ps, addrs[i], mr); + + sysbus_connect_irq(sbd, 0, pic[irqs[i]]); + g_free(name); + } +} + static void versal_create_usbs(Versal *s, qemu_irq *pic) { DeviceState *dev; @@ -218,18 +256,13 @@ static void versal_create_gems(Versal *s, qemu_irq *pic) static const int irqs[] = { VERSAL_GEM0_IRQ_0, VERSAL_GEM1_IRQ_0}; static const uint64_t addrs[] = { MM_GEM0, MM_GEM1 }; char *name = g_strdup_printf("gem%d", i); - NICInfo *nd = &nd_table[i]; DeviceState *dev; MemoryRegion *mr; object_initialize_child(OBJECT(s), name, &s->lpd.iou.gem[i], TYPE_CADENCE_GEM); dev = DEVICE(&s->lpd.iou.gem[i]); - /* FIXME use qdev NIC properties instead of nd_table[] */ - if (nd->used) { - qemu_check_nic_model(nd, "cadence_gem"); - qdev_set_nic_properties(dev, nd); - } + qemu_configure_nic_device(dev, true, NULL); object_property_set_int(OBJECT(dev), "phy-addr", 23, &error_abort); object_property_set_int(OBJECT(dev), "num-priority-queues", 2, &error_abort); @@ -310,6 +343,7 @@ static void versal_create_pmc_apb_irq_orgate(Versal *s, qemu_irq *pic) * - RTC * - BBRAM * - PMC SLCR + * - CFRAME regs (input 3 - 17 to the orgate) */ object_initialize_child(OBJECT(s), "pmc-apb-irq-orgate", &s->pmc.apb_irq_orgate, TYPE_OR_IRQ); @@ -328,7 +362,7 @@ static void versal_create_rtc(Versal *s, qemu_irq *pic) object_initialize_child(OBJECT(s), "rtc", &s->pmc.rtc, TYPE_XLNX_ZYNQMP_RTC); sbd = SYS_BUS_DEVICE(&s->pmc.rtc); - sysbus_realize(SYS_BUS_DEVICE(sbd), &error_fatal); + sysbus_realize(sbd, &error_fatal); mr = sysbus_mmio_get_region(sbd, 0); memory_region_add_subregion(&s->mr_ps, MM_PMC_RTC, mr); @@ -341,6 +375,21 @@ static void versal_create_rtc(Versal *s, qemu_irq *pic) qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 0)); } +static void versal_create_trng(Versal *s, qemu_irq *pic) +{ + SysBusDevice *sbd; + MemoryRegion *mr; + + object_initialize_child(OBJECT(s), "trng", &s->pmc.trng, + TYPE_XLNX_VERSAL_TRNG); + sbd = SYS_BUS_DEVICE(&s->pmc.trng); + sysbus_realize(sbd, &error_fatal); + + mr = sysbus_mmio_get_region(sbd, 0); + memory_region_add_subregion(&s->mr_ps, MM_PMC_TRNG, mr); + sysbus_connect_irq(sbd, 0, pic[VERSAL_TRNG_IRQ]); +} + static void versal_create_xrams(Versal *s, qemu_irq *pic) { int nr_xrams = ARRAY_SIZE(s->lpd.xram.ctrl); @@ -539,6 +588,157 @@ static void versal_create_ospi(Versal *s, qemu_irq *pic) qdev_connect_gpio_out(orgate, 0, pic[VERSAL_OSPI_IRQ]); } +static void versal_create_cfu(Versal *s, qemu_irq *pic) +{ + SysBusDevice *sbd; + DeviceState *dev; + int i; + const struct { + uint64_t reg_base; + uint64_t fdri_base; + } cframe_addr[] = { + { MM_PMC_CFRAME0_REG, MM_PMC_CFRAME0_FDRI }, + { MM_PMC_CFRAME1_REG, MM_PMC_CFRAME1_FDRI }, + { MM_PMC_CFRAME2_REG, MM_PMC_CFRAME2_FDRI }, + { MM_PMC_CFRAME3_REG, MM_PMC_CFRAME3_FDRI }, + { MM_PMC_CFRAME4_REG, MM_PMC_CFRAME4_FDRI }, + { MM_PMC_CFRAME5_REG, MM_PMC_CFRAME5_FDRI }, + { MM_PMC_CFRAME6_REG, MM_PMC_CFRAME6_FDRI }, + { MM_PMC_CFRAME7_REG, MM_PMC_CFRAME7_FDRI }, + { MM_PMC_CFRAME8_REG, MM_PMC_CFRAME8_FDRI }, + { MM_PMC_CFRAME9_REG, MM_PMC_CFRAME9_FDRI }, + { MM_PMC_CFRAME10_REG, MM_PMC_CFRAME10_FDRI }, + { MM_PMC_CFRAME11_REG, MM_PMC_CFRAME11_FDRI }, + { MM_PMC_CFRAME12_REG, MM_PMC_CFRAME12_FDRI }, + { MM_PMC_CFRAME13_REG, MM_PMC_CFRAME13_FDRI }, + { MM_PMC_CFRAME14_REG, MM_PMC_CFRAME14_FDRI }, + }; + const struct { + uint32_t blktype0_frames; + uint32_t blktype1_frames; + uint32_t blktype2_frames; + uint32_t blktype3_frames; + uint32_t blktype4_frames; + uint32_t blktype5_frames; + uint32_t blktype6_frames; + } cframe_cfg[] = { + [0] = { 34111, 3528, 12800, 11, 5, 1, 1 }, + [1] = { 38498, 3841, 15361, 13, 7, 3, 1 }, + [2] = { 38498, 3841, 15361, 13, 7, 3, 1 }, + [3] = { 38498, 3841, 15361, 13, 7, 3, 1 }, + }; + + /* CFU FDRO */ + object_initialize_child(OBJECT(s), "cfu-fdro", &s->pmc.cfu_fdro, + TYPE_XLNX_VERSAL_CFU_FDRO); + sbd = SYS_BUS_DEVICE(&s->pmc.cfu_fdro); + + sysbus_realize(sbd, &error_fatal); + memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_FDRO, + sysbus_mmio_get_region(sbd, 0)); + + /* CFRAME REG */ + for (i = 0; i < ARRAY_SIZE(s->pmc.cframe); i++) { + g_autofree char *name = g_strdup_printf("cframe%d", i); + + object_initialize_child(OBJECT(s), name, &s->pmc.cframe[i], + TYPE_XLNX_VERSAL_CFRAME_REG); + + sbd = SYS_BUS_DEVICE(&s->pmc.cframe[i]); + dev = DEVICE(&s->pmc.cframe[i]); + + if (i < ARRAY_SIZE(cframe_cfg)) { + object_property_set_int(OBJECT(dev), "blktype0-frames", + cframe_cfg[i].blktype0_frames, + &error_abort); + object_property_set_int(OBJECT(dev), "blktype1-frames", + cframe_cfg[i].blktype1_frames, + &error_abort); + object_property_set_int(OBJECT(dev), "blktype2-frames", + cframe_cfg[i].blktype2_frames, + &error_abort); + object_property_set_int(OBJECT(dev), "blktype3-frames", + cframe_cfg[i].blktype3_frames, + &error_abort); + object_property_set_int(OBJECT(dev), "blktype4-frames", + cframe_cfg[i].blktype4_frames, + &error_abort); + object_property_set_int(OBJECT(dev), "blktype5-frames", + cframe_cfg[i].blktype5_frames, + &error_abort); + object_property_set_int(OBJECT(dev), "blktype6-frames", + cframe_cfg[i].blktype6_frames, + &error_abort); + } + object_property_set_link(OBJECT(dev), "cfu-fdro", + OBJECT(&s->pmc.cfu_fdro), &error_fatal); + + sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal); + + memory_region_add_subregion(&s->mr_ps, cframe_addr[i].reg_base, + sysbus_mmio_get_region(sbd, 0)); + memory_region_add_subregion(&s->mr_ps, cframe_addr[i].fdri_base, + sysbus_mmio_get_region(sbd, 1)); + sysbus_connect_irq(sbd, 0, + qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), + 3 + i)); + } + + /* CFRAME BCAST */ + object_initialize_child(OBJECT(s), "cframe_bcast", &s->pmc.cframe_bcast, + TYPE_XLNX_VERSAL_CFRAME_BCAST_REG); + + sbd = SYS_BUS_DEVICE(&s->pmc.cframe_bcast); + dev = DEVICE(&s->pmc.cframe_bcast); + + for (i = 0; i < ARRAY_SIZE(s->pmc.cframe); i++) { + g_autofree char *propname = g_strdup_printf("cframe%d", i); + object_property_set_link(OBJECT(dev), propname, + OBJECT(&s->pmc.cframe[i]), &error_fatal); + } + + sysbus_realize(sbd, &error_fatal); + + memory_region_add_subregion(&s->mr_ps, MM_PMC_CFRAME_BCAST_REG, + sysbus_mmio_get_region(sbd, 0)); + memory_region_add_subregion(&s->mr_ps, MM_PMC_CFRAME_BCAST_FDRI, + sysbus_mmio_get_region(sbd, 1)); + + /* CFU APB */ + object_initialize_child(OBJECT(s), "cfu-apb", &s->pmc.cfu_apb, + TYPE_XLNX_VERSAL_CFU_APB); + sbd = SYS_BUS_DEVICE(&s->pmc.cfu_apb); + dev = DEVICE(&s->pmc.cfu_apb); + + for (i = 0; i < ARRAY_SIZE(s->pmc.cframe); i++) { + g_autofree char *propname = g_strdup_printf("cframe%d", i); + object_property_set_link(OBJECT(dev), propname, + OBJECT(&s->pmc.cframe[i]), &error_fatal); + } + + sysbus_realize(sbd, &error_fatal); + memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_APB, + sysbus_mmio_get_region(sbd, 0)); + memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_STREAM, + sysbus_mmio_get_region(sbd, 1)); + memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_STREAM_2, + sysbus_mmio_get_region(sbd, 2)); + sysbus_connect_irq(sbd, 0, pic[VERSAL_CFU_IRQ_0]); + + /* CFU SFR */ + object_initialize_child(OBJECT(s), "cfu-sfr", &s->pmc.cfu_sfr, + TYPE_XLNX_VERSAL_CFU_SFR); + + sbd = SYS_BUS_DEVICE(&s->pmc.cfu_sfr); + + object_property_set_link(OBJECT(&s->pmc.cfu_sfr), + "cfu", OBJECT(&s->pmc.cfu_apb), &error_abort); + + sysbus_realize(sbd, &error_fatal); + memory_region_add_subregion(&s->mr_ps, MM_PMC_CFU_SFR, + sysbus_mmio_get_region(sbd, 0)); +} + static void versal_create_crl(Versal *s, qemu_irq *pic) { SysBusDevice *sbd; @@ -719,18 +919,21 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_apu_gic(s, pic); versal_create_rpu_cpus(s); versal_create_uarts(s, pic); + versal_create_canfds(s, pic); versal_create_usbs(s, pic); versal_create_gems(s, pic); versal_create_admas(s, pic); versal_create_sds(s, pic); versal_create_pmc_apb_irq_orgate(s, pic); versal_create_rtc(s, pic); + versal_create_trng(s, pic); versal_create_xrams(s, pic); versal_create_bbram(s, pic); versal_create_efuse(s, pic); versal_create_pmc_iou_slcr(s, pic); versal_create_ospi(s, pic); versal_create_crl(s, pic); + versal_create_cfu(s, pic); versal_map_ddr(s); versal_unimp(s); @@ -758,6 +961,10 @@ static void versal_init(Object *obj) static Property versal_properties[] = { DEFINE_PROP_LINK("ddr", Versal, cfg.mr_ddr, TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_LINK("canbus0", Versal, lpd.iou.canbus[0], + TYPE_CAN_BUS, CanBusState *), + DEFINE_PROP_LINK("canbus1", Versal, lpd.iou.canbus[1], + TYPE_CAN_BUS, CanBusState *), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index 4c84bb932a..4667cb333c 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -18,12 +18,14 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/xlnx-zynqmp.h" +#include "hw/arm/boot.h" #include "hw/boards.h" #include "qemu/error-report.h" #include "qemu/log.h" #include "sysemu/device_tree.h" #include "qom/object.h" #include "net/can_emu.h" +#include "audio/audio.h" struct XlnxZCU102 { MachineState parent_obj; @@ -143,6 +145,10 @@ static void xlnx_zcu102_init(MachineState *machine) object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_XLNX_ZYNQMP); + if (machine->audiodev) { + qdev_prop_set_string(DEVICE(&s->soc.dp), "audiodev", machine->audiodev); + } + object_property_set_link(OBJECT(&s->soc), "ddr-ram", OBJECT(machine->ram), &error_abort); object_property_set_bool(OBJECT(&s->soc), "secure", s->secure, @@ -201,6 +207,7 @@ static void xlnx_zcu102_init(MachineState *machine) qdev_prop_set_drive_err(flash_dev, "drive", blk_by_legacy_dinfo(dinfo), &error_fatal); } + qdev_prop_set_uint8(flash_dev, "cs", i); qdev_realize_and_unref(flash_dev, spi_bus, &error_fatal); cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); @@ -224,6 +231,7 @@ static void xlnx_zcu102_init(MachineState *machine) qdev_prop_set_drive_err(flash_dev, "drive", blk_by_legacy_dinfo(dinfo), &error_fatal); } + qdev_prop_set_uint8(flash_dev, "cs", i); qdev_realize_and_unref(flash_dev, spi_bus, &error_fatal); cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); @@ -273,6 +281,7 @@ static void xlnx_zcu102_machine_class_init(ObjectClass *oc, void *data) mc->default_cpus = XLNX_ZYNQMP_NUM_APU_CPUS; mc->default_ram_id = "ddr-ram"; + machine_add_audiodev_property(mc); object_class_property_add_bool(oc, "secure", zcu102_get_secure, zcu102_set_secure); object_class_property_set_description(oc, "secure", diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 383e177a00..afeb3f88f8 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -25,6 +25,8 @@ #include "sysemu/kvm.h" #include "sysemu/sysemu.h" #include "kvm_arm.h" +#include "target/arm/cpu-qom.h" +#include "target/arm/gtimer.h" #define GIC_NUM_SPI_INTR 160 @@ -143,6 +145,14 @@ static const int adma_ch_intr[XLNX_ZYNQMP_NUM_ADMA_CH] = { 77, 78, 79, 80, 81, 82, 83, 84 }; +static const uint64_t usb_addr[XLNX_ZYNQMP_NUM_USB] = { + 0xFE200000, 0xFE300000 +}; + +static const int usb_intr[XLNX_ZYNQMP_NUM_USB] = { + 65, 70 +}; + typedef struct XlnxZynqMPGICRegion { int region_index; uint32_t address; @@ -205,7 +215,7 @@ static void xlnx_zynqmp_create_rpu(MachineState *ms, XlnxZynqMPState *s, const char *boot_cpu, Error **errp) { int i; - int num_rpus = MIN(ms->smp.cpus - XLNX_ZYNQMP_NUM_APU_CPUS, + int num_rpus = MIN((int)(ms->smp.cpus - XLNX_ZYNQMP_NUM_APU_CPUS), XLNX_ZYNQMP_NUM_RPU_CPUS); if (num_rpus <= 0) { @@ -428,6 +438,10 @@ static void xlnx_zynqmp_init(Object *obj) object_initialize_child(obj, "qspi-dma", &s->qspi_dma, TYPE_XLNX_CSU_DMA); object_initialize_child(obj, "qspi-irq-orgate", &s->qspi_irq_orgate, TYPE_OR_IRQ); + + for (i = 0; i < XLNX_ZYNQMP_NUM_USB; i++) { + object_initialize_child(obj, "usb[*]", &s->usb[i], TYPE_USB_DWC3); + } } static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) @@ -604,13 +618,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) { - NICInfo *nd = &nd_table[i]; - - /* FIXME use qdev NIC properties instead of nd_table[] */ - if (nd->used) { - qemu_check_nic_model(nd, TYPE_CADENCE_GEM); - qdev_set_nic_properties(DEVICE(&s->gem[i]), nd); - } + qemu_configure_nic_device(DEVICE(&s->gem[i]), true, NULL); object_property_set_int(OBJECT(&s->gem[i]), "revision", GEM_REVISION, &error_abort); object_property_set_int(OBJECT(&s->gem[i]), "phy-addr", 23, @@ -814,6 +822,30 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) object_property_add_alias(OBJECT(s), bus_name, OBJECT(&s->qspi), target_bus); } + + for (i = 0; i < XLNX_ZYNQMP_NUM_USB; i++) { + if (!object_property_set_link(OBJECT(&s->usb[i].sysbus_xhci), "dma", + OBJECT(system_memory), errp)) { + return; + } + + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "intrs", 4); + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "slots", 2); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->usb[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, usb_addr[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i].sysbus_xhci), 0, + gic_spi[usb_intr[i]]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i].sysbus_xhci), 1, + gic_spi[usb_intr[i] + 1]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i].sysbus_xhci), 2, + gic_spi[usb_intr[i] + 2]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i].sysbus_xhci), 3, + gic_spi[usb_intr[i] + 3]); + } } static Property xlnx_zynqmp_props[] = { diff --git a/hw/arm/z2.c b/hw/arm/z2.c index 9c1e876207..fc5672e7ab 100644 --- a/hw/arm/z2.c +++ b/hw/arm/z2.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/arm/pxa.h" #include "hw/arm/boot.h" #include "hw/i2c/i2c.h" @@ -24,15 +25,9 @@ #include "hw/audio/wm8750.h" #include "audio/audio.h" #include "exec/address-spaces.h" -#include "cpu.h" #include "qom/object.h" - -#ifdef DEBUG_Z2 -#define DPRINTF(fmt, ...) \ - printf(fmt, ## __VA_ARGS__) -#else -#define DPRINTF(fmt, ...) -#endif +#include "qapi/error.h" +#include "trace.h" static const struct keymap map[0x100] = { [0 ... 0xff] = { -1, -1 }, @@ -118,6 +113,8 @@ static uint32_t zipit_lcd_transfer(SSIPeripheral *dev, uint32_t value) { ZipitLCD *z = ZIPIT_LCD(dev); uint16_t val; + + trace_z2_lcd_reg_update(z->cur_reg, z->buf[0], z->buf[1], z->buf[2], value); if (z->selected) { z->buf[z->pos] = value & 0xff; z->pos++; @@ -125,22 +122,19 @@ static uint32_t zipit_lcd_transfer(SSIPeripheral *dev, uint32_t value) if (z->pos == 3) { switch (z->buf[0]) { case 0x74: - DPRINTF("%s: reg: 0x%.2x\n", __func__, z->buf[2]); z->cur_reg = z->buf[2]; break; case 0x76: val = z->buf[1] << 8 | z->buf[2]; - DPRINTF("%s: value: 0x%.4x\n", __func__, val); if (z->cur_reg == 0x22 && val == 0x0000) { z->enabled = 1; - printf("%s: LCD enabled\n", __func__); + trace_z2_lcd_enable_disable_result("enabled"); } else if (z->cur_reg == 0x10 && val == 0x0000) { z->enabled = 0; - printf("%s: LCD disabled\n", __func__); + trace_z2_lcd_enable_disable_result("disabled"); } break; default: - DPRINTF("%s: unknown command!\n", __func__); break; } z->pos = 0; @@ -166,7 +160,7 @@ static const VMStateDescription vmstate_zipit_lcd_state = { .name = "zipit-lcd", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_SSI_PERIPHERAL(ssidev, ZipitLCD), VMSTATE_INT32(selected, ZipitLCD), VMSTATE_INT32(enabled, ZipitLCD), @@ -210,14 +204,12 @@ static int aer915_send(I2CSlave *i2c, uint8_t data) s->buf[s->len] = data; if (s->len++ > 2) { - DPRINTF("%s: message too long (%i bytes)\n", - __func__, s->len); + trace_z2_aer915_send_too_long(s->len); return 1; } if (s->len == 2) { - DPRINTF("%s: reg %d value 0x%02x\n", __func__, - s->buf[0], s->buf[1]); + trace_z2_aer915_send(s->buf[0], s->buf[1]); } return 0; @@ -227,14 +219,12 @@ static int aer915_event(I2CSlave *i2c, enum i2c_event event) { AER915State *s = AER915(i2c); + trace_z2_aer915_event(s->len, event); switch (event) { case I2C_START_SEND: s->len = 0; break; case I2C_START_RECV: - if (s->len != 1) { - DPRINTF("%s: short message!?\n", __func__); - } break; case I2C_FINISH: break; @@ -272,7 +262,7 @@ static const VMStateDescription vmstate_aer915_state = { .name = "aer915", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(len, AER915State), VMSTATE_BUFFER(buf, AER915State), VMSTATE_END_OF_LIST(), @@ -297,26 +287,24 @@ static const TypeInfo aer915_info = { .class_init = aer915_class_init, }; +#define FLASH_SECTOR_SIZE (64 * KiB) + static void z2_init(MachineState *machine) { - MemoryRegion *address_space_mem = get_system_memory(); - uint32_t sector_len = 0x10000; PXA2xxState *mpu; DriveInfo *dinfo; void *z2_lcd; I2CBus *bus; DeviceState *wm; + I2CSlave *i2c_dev; /* Setup CPU & memory */ - mpu = pxa270_init(address_space_mem, z2_binfo.ram_size, machine->cpu_type); + mpu = pxa270_init(z2_binfo.ram_size, machine->cpu_type); dinfo = drive_get(IF_PFLASH, 0, 0); - 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, 0)) { - error_report("Error registering flash memory"); - exit(1); - } + pflash_cfi01_register(Z2_FLASH_BASE, "z2.flash0", Z2_FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + FLASH_SECTOR_SIZE, 4, 0, 0, 0, 0, 0); /* setup keypad */ pxa27x_register_keypad(mpu->kp, map, 0x100); @@ -330,8 +318,17 @@ static void z2_init(MachineState *machine) type_register_static(&aer915_info); z2_lcd = ssi_create_peripheral(mpu->ssp[1], TYPE_ZIPIT_LCD); bus = pxa2xx_i2c_bus(mpu->i2c[0]); + i2c_slave_create_simple(bus, TYPE_AER915, 0x55); - wm = DEVICE(i2c_slave_create_simple(bus, TYPE_WM8750, 0x1b)); + + i2c_dev = i2c_slave_new(TYPE_WM8750, 0x1b); + wm = DEVICE(i2c_dev); + + if (machine->audiodev) { + qdev_prop_set_string(wm, "audiodev", machine->audiodev); + } + i2c_slave_realize_and_unref(i2c_dev, bus, &error_abort); + mpu->i2s->opaque = wm; mpu->i2s->codec_out = wm8750_dac_dat; mpu->i2s->codec_in = wm8750_adc_dat; @@ -350,6 +347,9 @@ static void z2_machine_init(MachineClass *mc) mc->init = z2_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c5"); + mc->deprecation_reason = "machine is old and unmaintained"; + + machine_add_audiodev_property(mc); } DEFINE_MACHINE("z2", z2_machine_init) diff --git a/hw/audio/Kconfig b/hw/audio/Kconfig index e76c69ca7e..daf060e1be 100644 --- a/hw/audio/Kconfig +++ b/hw/audio/Kconfig @@ -47,3 +47,11 @@ config PL041 config CS4231 bool + +config ASC + bool + +config VIRTIO_SND + bool + default y + depends on VIRTIO diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index be2dd701a4..3f0053f94d 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -20,49 +20,13 @@ #include "qemu/osdep.h" #include "hw/audio/soundhw.h" #include "audio/audio.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/module.h" #include "sysemu/dma.h" #include "qom/object.h" - -enum { - AC97_Reset = 0x00, - AC97_Master_Volume_Mute = 0x02, - AC97_Headphone_Volume_Mute = 0x04, - AC97_Master_Volume_Mono_Mute = 0x06, - AC97_Master_Tone_RL = 0x08, - AC97_PC_BEEP_Volume_Mute = 0x0A, - AC97_Phone_Volume_Mute = 0x0C, - AC97_Mic_Volume_Mute = 0x0E, - AC97_Line_In_Volume_Mute = 0x10, - AC97_CD_Volume_Mute = 0x12, - AC97_Video_Volume_Mute = 0x14, - AC97_Aux_Volume_Mute = 0x16, - AC97_PCM_Out_Volume_Mute = 0x18, - AC97_Record_Select = 0x1A, - AC97_Record_Gain_Mute = 0x1C, - AC97_Record_Gain_Mic_Mute = 0x1E, - AC97_General_Purpose = 0x20, - AC97_3D_Control = 0x22, - AC97_AC_97_RESERVED = 0x24, - AC97_Powerdown_Ctrl_Stat = 0x26, - AC97_Extended_Audio_ID = 0x28, - AC97_Extended_Audio_Ctrl_Stat = 0x2A, - AC97_PCM_Front_DAC_Rate = 0x2C, - AC97_PCM_Surround_DAC_Rate = 0x2E, - AC97_PCM_LFE_DAC_Rate = 0x30, - AC97_PCM_LR_ADC_Rate = 0x32, - AC97_MIC_ADC_Rate = 0x34, - AC97_6Ch_Vol_C_LFE_Mute = 0x36, - AC97_6Ch_Vol_L_R_Surround_Mute = 0x38, - AC97_Vendor_Reserved = 0x58, - AC97_Sigmatel_Analog = 0x6c, /* We emulate a Sigmatel codec */ - AC97_Sigmatel_Dac2Invert = 0x6e, /* We emulate a Sigmatel codec */ - AC97_Vendor_ID1 = 0x7c, - AC97_Vendor_ID2 = 0x7e -}; +#include "ac97.h" #define SOFT_VOLUME #define SR_FIFOE 16 /* rwc */ @@ -121,11 +85,6 @@ enum { #define BD_IOC (1 << 31) #define BD_BUP (1 << 30) -#define EACS_VRA 1 -#define EACS_VRM 8 - -#define MUTE_SHIFT 15 - #define TYPE_AC97 "AC97" OBJECT_DECLARE_SIMPLE_TYPE(AC97LinkState, AC97) @@ -1135,7 +1094,7 @@ static const VMStateDescription vmstate_ac97_bm_regs = { .name = "ac97_bm_regs", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(bdbar, AC97BusMasterRegs), VMSTATE_UINT8(civ, AC97BusMasterRegs), VMSTATE_UINT8(lvi, AC97BusMasterRegs), @@ -1183,7 +1142,7 @@ static const VMStateDescription vmstate_ac97 = { .version_id = 3, .minimum_version_id = 2, .post_load = ac97_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, AC97LinkState), VMSTATE_UINT32(glob_cnt, AC97LinkState), VMSTATE_UINT32(glob_sta, AC97LinkState), @@ -1295,7 +1254,7 @@ static const MemoryRegionOps ac97_io_nabm_ops = { static void ac97_on_reset(DeviceState *dev) { - AC97LinkState *s = container_of(dev, AC97LinkState, dev.qdev); + AC97LinkState *s = AC97(dev); reset_bm_regs(s, &s->bm_regs[0]); reset_bm_regs(s, &s->bm_regs[1]); @@ -1314,6 +1273,10 @@ static void ac97_realize(PCIDevice *dev, Error **errp) AC97LinkState *s = AC97(dev); uint8_t *c = s->dev.config; + if (!AUD_register_card ("ac97", &s->card, errp)) { + return; + } + /* TODO: no need to override */ c[PCI_COMMAND] = 0x00; /* pcicmd pci command rw, ro */ c[PCI_COMMAND + 1] = 0x00; @@ -1347,7 +1310,7 @@ static void ac97_realize(PCIDevice *dev, Error **errp) "ac97-nabm", 256); pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_nam); pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_nabm); - AUD_register_card("ac97", &s->card); + ac97_on_reset(DEVICE(s)); } diff --git a/hw/audio/ac97.h b/hw/audio/ac97.h new file mode 100644 index 0000000000..0358b56ff4 --- /dev/null +++ b/hw/audio/ac97.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2006 InnoTek Systemberatung GmbH + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file 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, + * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE + * distribution. VirtualBox OSE is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY of any kind. + * + * If you received this file as part of a commercial VirtualBox + * distribution, then only the terms of your commercial VirtualBox + * license agreement apply instead of the previous paragraph. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#ifndef AC97_H +#define AC97_H + +enum { + AC97_Reset = 0x00, + AC97_Master_Volume_Mute = 0x02, + AC97_Headphone_Volume_Mute = 0x04, + AC97_Master_Volume_Mono_Mute = 0x06, + AC97_Master_Tone_RL = 0x08, + AC97_PC_BEEP_Volume_Mute = 0x0A, + AC97_Phone_Volume_Mute = 0x0C, + AC97_Mic_Volume_Mute = 0x0E, + AC97_Line_In_Volume_Mute = 0x10, + AC97_CD_Volume_Mute = 0x12, + AC97_Video_Volume_Mute = 0x14, + AC97_Aux_Volume_Mute = 0x16, + AC97_PCM_Out_Volume_Mute = 0x18, + AC97_Record_Select = 0x1A, + AC97_Record_Gain_Mute = 0x1C, + AC97_Record_Gain_Mic_Mute = 0x1E, + AC97_General_Purpose = 0x20, + AC97_3D_Control = 0x22, + AC97_AC_97_RESERVED = 0x24, + AC97_Powerdown_Ctrl_Stat = 0x26, + AC97_Extended_Audio_ID = 0x28, + AC97_Extended_Audio_Ctrl_Stat = 0x2A, + AC97_PCM_Front_DAC_Rate = 0x2C, + AC97_PCM_Surround_DAC_Rate = 0x2E, + AC97_PCM_LFE_DAC_Rate = 0x30, + AC97_PCM_LR_ADC_Rate = 0x32, + AC97_MIC_ADC_Rate = 0x34, + AC97_6Ch_Vol_C_LFE_Mute = 0x36, + AC97_6Ch_Vol_L_R_Surround_Mute = 0x38, + AC97_Vendor_Reserved = 0x58, + AC97_Sigmatel_Analog = 0x6c, /* We emulate a Sigmatel codec */ + AC97_Sigmatel_Dac2Invert = 0x6e, /* We emulate a Sigmatel codec */ + AC97_Vendor_ID1 = 0x7c, + AC97_Vendor_ID2 = 0x7e +}; + +#define EACS_VRA 1 +#define EACS_VRM 8 + +#define MUTE_SHIFT 15 + +#endif /* AC97_H */ diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index 5f979b1487..bd73806d83 100644 --- a/hw/audio/adlib.c +++ b/hw/audio/adlib.c @@ -255,6 +255,10 @@ static void adlib_realizefn (DeviceState *dev, Error **errp) AdlibState *s = ADLIB(dev); struct audsettings as; + if (!AUD_register_card ("adlib", &s->card, errp)) { + return; + } + s->opl = OPLCreate (3579545, s->freq); if (!s->opl) { error_setg (errp, "OPLCreate %d failed", s->freq); @@ -270,8 +274,6 @@ static void adlib_realizefn (DeviceState *dev, Error **errp) as.fmt = AUDIO_FORMAT_S16; as.endianness = AUDIO_HOST_ENDIANNESS; - AUD_register_card ("adlib", &s->card); - s->voice = AUD_open_out ( &s->card, s->voice, diff --git a/hw/audio/asc.c b/hw/audio/asc.c new file mode 100644 index 0000000000..87b5624326 --- /dev/null +++ b/hw/audio/asc.c @@ -0,0 +1,727 @@ +/* + * QEMU Apple Sound Chip emulation + * + * Apple Sound Chip (ASC) 344S0063 + * Enhanced Apple Sound Chip (EASC) 343S1063 + * + * Copyright (c) 2012-2018 Laurent Vivier + * Copyright (c) 2022 Mark Cave-Ayland + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "hw/sysbus.h" +#include "hw/irq.h" +#include "audio/audio.h" +#include "hw/audio/asc.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "trace.h" + +/* + * Linux doesn't provide information about ASC, see arch/m68k/mac/macboing.c + * and arch/m68k/include/asm/mac_asc.h + * + * best information is coming from MAME: + * https://github.com/mamedev/mame/blob/master/src/devices/sound/asc.h + * https://github.com/mamedev/mame/blob/master/src/devices/sound/asc.cpp + * Emulation by R. Belmont + * or MESS: + * http://mess.redump.net/mess/driver_info/easc + * + * 0x800: VERSION + * 0x801: MODE + * 1=FIFO mode, + * 2=wavetable mode + * 0x802: CONTROL + * bit 0=analog or PWM output, + * 1=stereo/mono, + * 7=processing time exceeded + * 0x803: FIFO MODE + * bit 7=clear FIFO, + * bit 1="non-ROM companding", + * bit 0="ROM companding") + * 0x804: FIFO IRQ STATUS + * bit 0=ch A 1/2 full, + * 1=ch A full, + * 2=ch B 1/2 full, + * 3=ch B full) + * 0x805: WAVETABLE CONTROL + * bits 0-3 wavetables 0-3 start + * 0x806: VOLUME + * bits 2-4 = 3 bit internal ASC volume, + * bits 5-7 = volume control sent to Sony sound chip + * 0x807: CLOCK RATE + * 0 = Mac 22257 Hz, + * 1 = undefined, + * 2 = 22050 Hz, + * 3 = 44100 Hz + * 0x80a: PLAY REC A + * 0x80f: TEST + * bits 6-7 = digital test, + * bits 4-5 = analog test + * 0x810: WAVETABLE 0 PHASE + * big-endian 9.15 fixed-point, only 24 bits valid + * 0x814: WAVETABLE 0 INCREMENT + * big-endian 9.15 fixed-point, only 24 bits valid + * 0x818: WAVETABLE 1 PHASE + * 0x81C: WAVETABLE 1 INCREMENT + * 0x820: WAVETABLE 2 PHASE + * 0x824: WAVETABLE 2 INCREMENT + * 0x828: WAVETABLE 3 PHASE + * 0x82C: WAVETABLE 3 INCREMENT + * 0x830: UNKNOWN START + * NetBSD writes Wavetable data here (are there more + * wavetables/channels than we know about?) + * 0x857: UNKNOWN END + */ + +#define ASC_SIZE 0x2000 + +enum { + ASC_VERSION = 0x00, + ASC_MODE = 0x01, + ASC_CONTROL = 0x02, + ASC_FIFOMODE = 0x03, + ASC_FIFOIRQ = 0x04, + ASC_WAVECTRL = 0x05, + ASC_VOLUME = 0x06, + ASC_CLOCK = 0x07, + ASC_PLAYRECA = 0x0a, + ASC_TEST = 0x0f, + ASC_WAVETABLE = 0x10 +}; + +#define ASC_FIFO_STATUS_HALF_FULL 1 +#define ASC_FIFO_STATUS_FULL_EMPTY 2 + +#define ASC_EXTREGS_FIFOCTRL 0x8 +#define ASC_EXTREGS_INTCTRL 0x9 +#define ASC_EXTREGS_CDXA_DECOMP_FILT 0x10 + +#define ASC_FIFO_CYCLE_TIME ((NANOSECONDS_PER_SECOND / ASC_FREQ) * \ + 0x400) + +static void asc_raise_irq(ASCState *s) +{ + qemu_set_irq(s->irq, 1); +} + +static void asc_lower_irq(ASCState *s) +{ + qemu_set_irq(s->irq, 0); +} + +static uint8_t asc_fifo_get(ASCFIFOState *fs) +{ + ASCState *s = container_of(fs, ASCState, fifos[fs->index]); + bool fifo_half_irq_enabled = fs->extregs[ASC_EXTREGS_INTCTRL] & 1; + uint8_t val; + + assert(fs->cnt); + + val = fs->fifo[fs->rptr]; + trace_asc_fifo_get('A' + fs->index, fs->rptr, fs->cnt, val); + + fs->rptr++; + fs->rptr &= 0x3ff; + fs->cnt--; + + if (fs->cnt <= 0x1ff) { + /* FIFO less than half full */ + fs->int_status |= ASC_FIFO_STATUS_HALF_FULL; + } else { + /* FIFO more than half full */ + fs->int_status &= ~ASC_FIFO_STATUS_HALF_FULL; + } + + if (fs->cnt == 0x1ff && fifo_half_irq_enabled) { + /* Raise FIFO half full IRQ */ + asc_raise_irq(s); + } + + if (fs->cnt == 0) { + /* Raise FIFO empty IRQ */ + fs->int_status |= ASC_FIFO_STATUS_FULL_EMPTY; + asc_raise_irq(s); + } + + return val; +} + +static int generate_fifo(ASCState *s, int maxsamples) +{ + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint8_t *buf = s->mixbuf; + int i, wcount = 0; + + while (wcount < maxsamples) { + uint8_t val; + int16_t d, f0, f1; + int32_t t; + int shift, filter; + bool hasdata = false; + + for (i = 0; i < 2; i++) { + ASCFIFOState *fs = &s->fifos[i]; + + switch (fs->extregs[ASC_EXTREGS_FIFOCTRL] & 0x83) { + case 0x82: + /* + * CD-XA BRR mode: decompress 15 bytes into 28 16-bit + * samples + */ + if (!fs->cnt) { + val = 0x80; + break; + } + + if (fs->xa_cnt == -1) { + /* Start of packet, get flags */ + fs->xa_flags = asc_fifo_get(fs); + fs->xa_cnt = 0; + } + + shift = fs->xa_flags & 0xf; + filter = fs->xa_flags >> 4; + f0 = (int8_t)fs->extregs[ASC_EXTREGS_CDXA_DECOMP_FILT + + (filter << 1) + 1]; + f1 = (int8_t)fs->extregs[ASC_EXTREGS_CDXA_DECOMP_FILT + + (filter << 1)]; + + if ((fs->xa_cnt & 1) == 0) { + if (!fs->cnt) { + val = 0x80; + break; + } + + fs->xa_val = asc_fifo_get(fs); + d = (fs->xa_val & 0xf) << 12; + } else { + d = (fs->xa_val & 0xf0) << 8; + } + t = (d >> shift) + (((fs->xa_last[0] * f0) + + (fs->xa_last[1] * f1) + 32) >> 6); + if (t < -32768) { + t = -32768; + } else if (t > 32767) { + t = 32767; + } + + /* + * CD-XA BRR generates 16-bit signed output, so convert to + * 8-bit before writing to buffer. Does real hardware do the + * same? + */ + val = (uint8_t)(t / 256) ^ 0x80; + hasdata = true; + fs->xa_cnt++; + + fs->xa_last[1] = fs->xa_last[0]; + fs->xa_last[0] = (int16_t)t; + + if (fs->xa_cnt == 28) { + /* End of packet */ + fs->xa_cnt = -1; + } + break; + + default: + /* fallthrough */ + case 0x80: + /* Raw mode */ + if (fs->cnt) { + val = asc_fifo_get(fs); + hasdata = true; + } else { + val = 0x80; + } + break; + } + + buf[wcount * 2 + i] = val; + } + + if (!hasdata) { + break; + } + + wcount++; + } + + /* + * MacOS (un)helpfully leaves the FIFO engine running even when it has + * finished writing out samples, but still expects the FIFO empty + * interrupts to be generated for each FIFO cycle (without these interrupts + * MacOS will freeze) + */ + if (s->fifos[0].cnt == 0 && s->fifos[1].cnt == 0) { + if (!s->fifo_empty_ns) { + /* FIFO has completed first empty cycle */ + s->fifo_empty_ns = now; + } else if (now > (s->fifo_empty_ns + ASC_FIFO_CYCLE_TIME)) { + /* FIFO has completed entire cycle with no data */ + s->fifos[0].int_status |= ASC_FIFO_STATUS_HALF_FULL | + ASC_FIFO_STATUS_FULL_EMPTY; + s->fifos[1].int_status |= ASC_FIFO_STATUS_HALF_FULL | + ASC_FIFO_STATUS_FULL_EMPTY; + s->fifo_empty_ns = now; + asc_raise_irq(s); + } + } else { + /* FIFO contains data, reset empty time */ + s->fifo_empty_ns = 0; + } + + return wcount; +} + +static int generate_wavetable(ASCState *s, int maxsamples) +{ + uint8_t *buf = s->mixbuf; + int channel, count = 0; + + while (count < maxsamples) { + uint32_t left = 0, right = 0; + uint8_t sample; + + for (channel = 0; channel < 4; channel++) { + ASCFIFOState *fs = &s->fifos[channel >> 1]; + int chanreg = ASC_WAVETABLE + (channel << 3); + uint32_t phase, incr, offset; + + phase = ldl_be_p(&s->regs[chanreg]); + incr = ldl_be_p(&s->regs[chanreg + sizeof(uint32_t)]); + + phase += incr; + offset = (phase >> 15) & 0x1ff; + sample = fs->fifo[0x200 * (channel >> 1) + offset]; + + stl_be_p(&s->regs[chanreg], phase); + + left += sample; + right += sample; + } + + buf[count * 2] = left >> 2; + buf[count * 2 + 1] = right >> 2; + + count++; + } + + return count; +} + +static void asc_out_cb(void *opaque, int free_b) +{ + ASCState *s = opaque; + int samples, generated; + + if (free_b == 0) { + return; + } + + samples = MIN(s->samples, free_b >> s->shift); + + switch (s->regs[ASC_MODE] & 3) { + default: + /* Off */ + generated = 0; + break; + case 1: + /* FIFO mode */ + generated = generate_fifo(s, samples); + break; + case 2: + /* Wave table mode */ + generated = generate_wavetable(s, samples); + break; + } + + if (!generated) { + /* Workaround for audio underflow bug on Windows dsound backend */ + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int silent_samples = muldiv64(now - s->fifo_empty_ns, + NANOSECONDS_PER_SECOND, ASC_FREQ); + + if (silent_samples > ASC_FIFO_CYCLE_TIME / 2) { + /* + * No new FIFO data within half a cycle time (~23ms) so fill the + * entire available buffer with silence. This prevents an issue + * with the Windows dsound backend whereby the sound appears to + * loop because the FIFO has run out of data, and the driver + * reuses the stale content in its circular audio buffer. + */ + AUD_write(s->voice, s->silentbuf, samples << s->shift); + } + return; + } + + AUD_write(s->voice, s->mixbuf, generated << s->shift); +} + +static uint64_t asc_fifo_read(void *opaque, hwaddr addr, + unsigned size) +{ + ASCFIFOState *fs = opaque; + + trace_asc_read_fifo('A' + fs->index, addr, size, fs->fifo[addr]); + return fs->fifo[addr]; +} + +static void asc_fifo_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + ASCFIFOState *fs = opaque; + ASCState *s = container_of(fs, ASCState, fifos[fs->index]); + bool fifo_half_irq_enabled = fs->extregs[ASC_EXTREGS_INTCTRL] & 1; + + trace_asc_write_fifo('A' + fs->index, addr, size, fs->wptr, fs->cnt, value); + + if (s->regs[ASC_MODE] == 1) { + fs->fifo[fs->wptr++] = value; + fs->wptr &= 0x3ff; + fs->cnt++; + + if (fs->cnt <= 0x1ff) { + /* FIFO less than half full */ + fs->int_status |= ASC_FIFO_STATUS_HALF_FULL; + } else { + /* FIFO at least half full */ + fs->int_status &= ~ASC_FIFO_STATUS_HALF_FULL; + } + + if (fs->cnt == 0x200 && fifo_half_irq_enabled) { + /* Raise FIFO half full interrupt */ + asc_raise_irq(s); + } + + if (fs->cnt == 0x3ff) { + /* Raise FIFO full interrupt */ + fs->int_status |= ASC_FIFO_STATUS_FULL_EMPTY; + asc_raise_irq(s); + } + } else { + fs->fifo[addr] = value; + } + return; +} + +static const MemoryRegionOps asc_fifo_ops = { + .read = asc_fifo_read, + .write = asc_fifo_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void asc_fifo_reset(ASCFIFOState *fs); + +static uint64_t asc_read(void *opaque, hwaddr addr, + unsigned size) +{ + ASCState *s = opaque; + uint64_t prev, value; + + switch (addr) { + case ASC_VERSION: + switch (s->type) { + default: + case ASC_TYPE_ASC: + value = 0; + break; + case ASC_TYPE_EASC: + value = 0xb0; + break; + } + break; + case ASC_FIFOIRQ: + prev = (s->fifos[0].int_status & 0x3) | + (s->fifos[1].int_status & 0x3) << 2; + + s->fifos[0].int_status = 0; + s->fifos[1].int_status = 0; + asc_lower_irq(s); + value = prev; + break; + default: + value = s->regs[addr]; + break; + } + + trace_asc_read_reg(addr, size, value); + return value; +} + +static void asc_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + ASCState *s = opaque; + + switch (addr) { + case ASC_MODE: + value &= 3; + if (value != s->regs[ASC_MODE]) { + asc_fifo_reset(&s->fifos[0]); + asc_fifo_reset(&s->fifos[1]); + asc_lower_irq(s); + if (value != 0) { + AUD_set_active_out(s->voice, 1); + } else { + AUD_set_active_out(s->voice, 0); + } + } + break; + case ASC_FIFOMODE: + if (value & 0x80) { + asc_fifo_reset(&s->fifos[0]); + asc_fifo_reset(&s->fifos[1]); + asc_lower_irq(s); + } + break; + case ASC_WAVECTRL: + break; + case ASC_VOLUME: + { + int vol = (value & 0xe0); + + AUD_set_volume_out(s->voice, 0, vol, vol); + break; + } + } + + trace_asc_write_reg(addr, size, value); + s->regs[addr] = value; +} + +static const MemoryRegionOps asc_regs_ops = { + .read = asc_read, + .write = asc_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + } +}; + +static uint64_t asc_ext_read(void *opaque, hwaddr addr, + unsigned size) +{ + ASCFIFOState *fs = opaque; + uint64_t value; + + value = fs->extregs[addr]; + + trace_asc_read_extreg('A' + fs->index, addr, size, value); + return value; +} + +static void asc_ext_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + ASCFIFOState *fs = opaque; + + trace_asc_write_extreg('A' + fs->index, addr, size, value); + + fs->extregs[addr] = value; +} + +static const MemoryRegionOps asc_extregs_ops = { + .read = asc_ext_read, + .write = asc_ext_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static int asc_post_load(void *opaque, int version) +{ + ASCState *s = ASC(opaque); + + if (s->regs[ASC_MODE] != 0) { + AUD_set_active_out(s->voice, 1); + } + + return 0; +} + +static const VMStateDescription vmstate_asc_fifo = { + .name = "apple-sound-chip.fifo", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT8_ARRAY(fifo, ASCFIFOState, ASC_FIFO_SIZE), + VMSTATE_UINT8(int_status, ASCFIFOState), + VMSTATE_INT32(cnt, ASCFIFOState), + VMSTATE_INT32(wptr, ASCFIFOState), + VMSTATE_INT32(rptr, ASCFIFOState), + VMSTATE_UINT8_ARRAY(extregs, ASCFIFOState, ASC_EXTREG_SIZE), + VMSTATE_INT32(xa_cnt, ASCFIFOState), + VMSTATE_UINT8(xa_val, ASCFIFOState), + VMSTATE_UINT8(xa_flags, ASCFIFOState), + VMSTATE_INT16_ARRAY(xa_last, ASCFIFOState, 2), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_asc = { + .name = "apple-sound-chip", + .version_id = 0, + .minimum_version_id = 0, + .post_load = asc_post_load, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT_ARRAY(fifos, ASCState, 2, 0, vmstate_asc_fifo, + ASCFIFOState), + VMSTATE_UINT8_ARRAY(regs, ASCState, ASC_REG_SIZE), + VMSTATE_INT64(fifo_empty_ns, ASCState), + VMSTATE_END_OF_LIST() + } +}; + +static void asc_fifo_reset(ASCFIFOState *fs) +{ + fs->wptr = 0; + fs->rptr = 0; + fs->cnt = 0; + fs->xa_cnt = -1; + fs->int_status = 0; +} + +static void asc_fifo_init(ASCFIFOState *fs, int index) +{ + ASCState *s = container_of(fs, ASCState, fifos[index]); + char *name; + + fs->index = index; + name = g_strdup_printf("asc.fifo%c", 'A' + index); + memory_region_init_io(&fs->mem_fifo, OBJECT(s), &asc_fifo_ops, fs, + name, ASC_FIFO_SIZE); + g_free(name); + + name = g_strdup_printf("asc.extregs%c", 'A' + index); + memory_region_init_io(&fs->mem_extregs, OBJECT(s), &asc_extregs_ops, + fs, name, ASC_EXTREG_SIZE); + g_free(name); +} + +static void asc_reset_hold(Object *obj) +{ + ASCState *s = ASC(obj); + + AUD_set_active_out(s->voice, 0); + + memset(s->regs, 0, sizeof(s->regs)); + asc_fifo_reset(&s->fifos[0]); + asc_fifo_reset(&s->fifos[1]); + s->fifo_empty_ns = 0; + + if (s->type == ASC_TYPE_ASC) { + /* FIFO half full IRQs enabled by default */ + s->fifos[0].extregs[ASC_EXTREGS_INTCTRL] = 1; + s->fifos[1].extregs[ASC_EXTREGS_INTCTRL] = 1; + } +} + +static void asc_unrealize(DeviceState *dev) +{ + ASCState *s = ASC(dev); + + g_free(s->mixbuf); + g_free(s->silentbuf); + + AUD_remove_card(&s->card); +} + +static void asc_realize(DeviceState *dev, Error **errp) +{ + ASCState *s = ASC(dev); + struct audsettings as; + + if (!AUD_register_card("Apple Sound Chip", &s->card, errp)) { + return; + } + + as.freq = ASC_FREQ; + as.nchannels = 2; + as.fmt = AUDIO_FORMAT_U8; + as.endianness = AUDIO_HOST_ENDIANNESS; + + s->voice = AUD_open_out(&s->card, s->voice, "asc.out", s, asc_out_cb, + &as); + s->shift = 1; + s->samples = AUD_get_buffer_size_out(s->voice) >> s->shift; + s->mixbuf = g_malloc0(s->samples << s->shift); + + s->silentbuf = g_malloc0(s->samples << s->shift); + memset(s->silentbuf, 0x80, s->samples << s->shift); + + /* Add easc registers if required */ + if (s->type == ASC_TYPE_EASC) { + memory_region_add_subregion(&s->asc, ASC_EXTREG_OFFSET, + &s->fifos[0].mem_extregs); + memory_region_add_subregion(&s->asc, + ASC_EXTREG_OFFSET + ASC_EXTREG_SIZE, + &s->fifos[1].mem_extregs); + } +} + +static void asc_init(Object *obj) +{ + ASCState *s = ASC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init(&s->asc, OBJECT(obj), "asc", ASC_SIZE); + + asc_fifo_init(&s->fifos[0], 0); + asc_fifo_init(&s->fifos[1], 1); + + memory_region_add_subregion(&s->asc, ASC_FIFO_OFFSET, + &s->fifos[0].mem_fifo); + memory_region_add_subregion(&s->asc, + ASC_FIFO_OFFSET + ASC_FIFO_SIZE, + &s->fifos[1].mem_fifo); + + memory_region_init_io(&s->mem_regs, OBJECT(obj), &asc_regs_ops, s, + "asc.regs", ASC_REG_SIZE); + memory_region_add_subregion(&s->asc, ASC_REG_OFFSET, &s->mem_regs); + + sysbus_init_irq(sbd, &s->irq); + sysbus_init_mmio(sbd, &s->asc); +} + +static Property asc_properties[] = { + DEFINE_AUDIO_PROPERTIES(ASCState, card), + DEFINE_PROP_UINT8("asctype", ASCState, type, ASC_TYPE_ASC), + DEFINE_PROP_END_OF_LIST(), +}; + +static void asc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); + + dc->realize = asc_realize; + dc->unrealize = asc_unrealize; + set_bit(DEVICE_CATEGORY_SOUND, dc->categories); + dc->vmsd = &vmstate_asc; + device_class_set_props(dc, asc_properties); + rc->phases.hold = asc_reset_hold; +} + +static const TypeInfo asc_info_types[] = { + { + .name = TYPE_ASC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ASCState), + .instance_init = asc_init, + .class_init = asc_class_init, + }, +}; + +DEFINE_TYPES(asc_info_types) diff --git a/hw/audio/cs4231.c b/hw/audio/cs4231.c index aefc3edea1..967caa7fcb 100644 --- a/hw/audio/cs4231.c +++ b/hw/audio/cs4231.c @@ -142,7 +142,7 @@ static const VMStateDescription vmstate_cs4231 = { .name ="cs4231", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, CSState, CS_REGS), VMSTATE_UINT8_ARRAY(dregs, CSState, CS_DREGS), VMSTATE_END_OF_LIST() diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index 0723e39430..9ef57f042d 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -84,7 +84,7 @@ struct CSState { int transferred; int aci_counter; SWVoiceOut *voice; - int16_t *tab; + const int16_t *tab; }; #define MODE2 (1 << 6) @@ -142,13 +142,13 @@ enum { Capture_Lower_Base_Count }; -static int freqs[2][8] = { +static const int freqs[2][8] = { { 8000, 16000, 27420, 32000, -1, -1, 48000, 9000 }, { 5510, 11025, 18900, 22050, 37800, 44100, 33075, 6620 } }; /* Tables courtesy http://hazelware.luggle.com/tutorials/mulawcompression.html */ -static int16_t MuLawDecompressTable[256] = +static const int16_t MuLawDecompressTable[256] = { -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, @@ -184,7 +184,7 @@ static int16_t MuLawDecompressTable[256] = 56, 48, 40, 32, 24, 16, 8, 0 }; -static int16_t ALawDecompressTable[256] = +static const int16_t ALawDecompressTable[256] = { -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, @@ -637,7 +637,7 @@ static const VMStateDescription vmstate_cs4231a = { .minimum_version_id = 1, .pre_load = cs4231a_pre_load, .post_load = cs4231a_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY (regs, CSState, CS_REGS), VMSTATE_BUFFER (dregs, CSState), VMSTATE_INT32 (dma_running, CSState), @@ -668,22 +668,25 @@ static void cs4231a_initfn (Object *obj) static void cs4231a_realizefn (DeviceState *dev, Error **errp) { ISADevice *d = ISA_DEVICE (dev); + ISABus *bus = isa_bus_from_device(d); CSState *s = CS4231A (dev); IsaDmaClass *k; - s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->dma); + s->isa_dma = isa_bus_get_dma(bus, s->dma); if (!s->isa_dma) { error_setg(errp, "ISA controller does not support DMA"); return; } - s->pic = isa_get_irq(d, s->irq); + if (!AUD_register_card ("cs4231a", &s->card, errp)) { + return; + } + + s->pic = isa_bus_get_irq(bus, s->irq); k = ISADMA_GET_CLASS(s->isa_dma); k->register_channel(s->isa_dma, s->dma, cs_dma_read, s); isa_register_ioport (d, &s->ioports, s->port); - - AUD_register_card ("cs4231a", &s->card); } static Property cs4231a_properties[] = { diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 6904589814..4ab61d3b9d 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -22,18 +22,19 @@ * THE SOFTWARE. */ -/* #define DEBUG_ES1370 */ -/* #define VERBOSE_ES1370 */ -#define SILENT_ES1370 +#define DEBUG_ES1370 0 +#define VERBOSE_ES1370 0 #include "qemu/osdep.h" #include "hw/audio/soundhw.h" #include "audio/audio.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "migration/vmstate.h" +#include "qemu/cutils.h" #include "qemu/module.h" #include "sysemu/dma.h" #include "qom/object.h" +#include "trace.h" /* Missing stuff: SCTRL_P[12](END|ST)INC @@ -164,97 +165,88 @@ static void es1370_dac1_callback (void *opaque, int free); static void es1370_dac2_callback (void *opaque, int free); static void es1370_adc_callback (void *opaque, int avail); -#ifdef DEBUG_ES1370 - -#define ldebug(...) AUD_log ("es1370", __VA_ARGS__) - -static void print_ctl (uint32_t val) +static void print_ctl(uint32_t val) { - char buf[1024]; + if (DEBUG_ES1370) { + char buf[1024]; - buf[0] = '\0'; -#define a(n) if (val & CTRL_##n) strcat (buf, " "#n) - a (ADC_STOP); - a (XCTL1); - a (OPEN); - a (MSFMTSEL); - a (M_SBB); - a (DAC_SYNC); - a (CCB_INTRM); - a (M_CB); - a (XCTL0); - a (BREQ); - a (DAC1_EN); - a (DAC2_EN); - a (ADC_EN); - a (UART_EN); - a (JYSTK_EN); - a (CDC_EN); - a (SERR_DIS); + buf[0] = '\0'; +#define a(n) if (val & CTRL_##n) pstrcat(buf, sizeof(buf), " "#n) + a(ADC_STOP); + a(XCTL1); + a(OPEN); + a(MSFMTSEL); + a(M_SBB); + a(DAC_SYNC); + a(CCB_INTRM); + a(M_CB); + a(XCTL0); + a(BREQ); + a(DAC1_EN); + a(DAC2_EN); + a(ADC_EN); + a(UART_EN); + a(JYSTK_EN); + a(CDC_EN); + a(SERR_DIS); #undef a - AUD_log ("es1370", "ctl - PCLKDIV %d(DAC2 freq %d), freq %d,%s\n", - (val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV, - DAC2_DIVTOSR ((val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), - dac1_samplerate[(val & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], - buf); + AUD_log("es1370", "ctl - PCLKDIV %d(DAC2 freq %d), freq %d,%s\n", + (val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV, + DAC2_DIVTOSR((val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), + dac1_samplerate[(val & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], + buf); + } } -static void print_sctl (uint32_t val) +static void print_sctl(uint32_t val) { - static const char *fmt_names[] = {"8M", "8S", "16M", "16S"}; - char buf[1024]; + if (DEBUG_ES1370) { + static const char *fmt_names[] = {"8M", "8S", "16M", "16S"}; + char buf[1024]; - buf[0] = '\0'; + buf[0] = '\0'; -#define a(n) if (val & SCTRL_##n) strcat (buf, " "#n) -#define b(n) if (!(val & SCTRL_##n)) strcat (buf, " "#n) - b (R1LOOPSEL); - b (P2LOOPSEL); - b (P1LOOPSEL); - a (P2PAUSE); - a (P1PAUSE); - a (R1INTEN); - a (P2INTEN); - a (P1INTEN); - a (P1SCTRLD); - a (P2DACSEN); - if (buf[0]) { - strcat (buf, "\n "); - } - else { - buf[0] = ' '; - buf[1] = '\0'; - } +#define a(n) if (val & SCTRL_##n) pstrcat(buf, sizeof(buf), " "#n) +#define b(n) if (!(val & SCTRL_##n)) pstrcat(buf, sizeof(buf), " "#n) + b(R1LOOPSEL); + b(P2LOOPSEL); + b(P1LOOPSEL); + a(P2PAUSE); + a(P1PAUSE); + a(R1INTEN); + a(P2INTEN); + a(P1INTEN); + a(P1SCTRLD); + a(P2DACSEN); + if (buf[0]) { + pstrcat(buf, sizeof(buf), "\n "); + } else { + buf[0] = ' '; + buf[1] = '\0'; + } #undef b #undef a - AUD_log ("es1370", - "%s" - "p2_end_inc %d, p2_st_inc %d, r1_fmt %s, p2_fmt %s, p1_fmt %s\n", - buf, - (val & SCTRL_P2ENDINC) >> SCTRL_SH_P2ENDINC, - (val & SCTRL_P2STINC) >> SCTRL_SH_P2STINC, - fmt_names [(val >> SCTRL_SH_R1FMT) & 3], - fmt_names [(val >> SCTRL_SH_P2FMT) & 3], - fmt_names [(val >> SCTRL_SH_P1FMT) & 3] - ); + AUD_log("es1370", + "%s p2_end_inc %d, p2_st_inc %d," + " r1_fmt %s, p2_fmt %s, p1_fmt %s\n", + buf, + (val & SCTRL_P2ENDINC) >> SCTRL_SH_P2ENDINC, + (val & SCTRL_P2STINC) >> SCTRL_SH_P2STINC, + fmt_names[(val >> SCTRL_SH_R1FMT) & 3], + fmt_names[(val >> SCTRL_SH_P2FMT) & 3], + fmt_names[(val >> SCTRL_SH_P1FMT) & 3]); + } } -#else -#define ldebug(...) -#define print_ctl(...) -#define print_sctl(...) -#endif -#ifdef VERBOSE_ES1370 -#define dolog(...) AUD_log ("es1370", __VA_ARGS__) -#else -#define dolog(...) -#endif +#define lwarn(...) \ +do { \ + if (VERBOSE_ES1370) { \ + AUD_log("es1370: warning", __VA_ARGS__); \ + } \ +} while (0) -#ifndef SILENT_ES1370 -#define lwarn(...) AUD_log ("es1370: warning", __VA_ARGS__) -#else -#define lwarn(...) -#endif +#define TYPE_ES1370 "ES1370" +OBJECT_DECLARE_SIMPLE_TYPE(ES1370State, ES1370) struct chan { uint32_t shift; @@ -278,7 +270,6 @@ struct ES1370State { uint32_t codec; uint32_t sctl; }; -typedef struct ES1370State ES1370State; struct chan_bits { uint32_t ctl_en; @@ -292,9 +283,6 @@ struct chan_bits { uint32_t *old_freq, uint32_t *new_freq); }; -#define TYPE_ES1370 "ES1370" -OBJECT_DECLARE_SIMPLE_TYPE(ES1370State, ES1370) - static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl, uint32_t *old_freq, uint32_t *new_freq); static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl, @@ -321,8 +309,7 @@ static void es1370_update_status (ES1370State *s, uint32_t new_status) if (level) { s->status = new_status | STAT_INTR; - } - else { + } else { s->status = new_status & ~STAT_INTR; } pci_set_irq(&s->dev, !!level); @@ -345,8 +332,7 @@ static void es1370_reset (ES1370State *s) if (i == ADC_CHANNEL) { AUD_close_in (&s->card, s->adc_voice); s->adc_voice = NULL; - } - else { + } else { AUD_close_out (&s->card, s->dac_voice[i]); s->dac_voice[i] = NULL; } @@ -412,12 +398,9 @@ static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl) if ((old_fmt != new_fmt) || (old_freq != new_freq)) { d->shift = (new_fmt & 1) + (new_fmt >> 1); - ldebug ("channel %zu, freq = %d, nchannels %d, fmt %d, shift %d\n", - i, - new_freq, - 1 << (new_fmt & 1), - (new_fmt & 2) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8, - d->shift); + trace_es1370_stream_format(i, new_freq, + new_fmt & 2 ? "s16" : "u8", new_fmt & 1 ? "stereo" : "mono", + d->shift); if (new_freq) { struct audsettings as; @@ -436,8 +419,7 @@ static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl) es1370_adc_callback, &as ); - } - else { + } else { s->dac_voice[i] = AUD_open_out ( &s->card, @@ -457,8 +439,7 @@ static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl) if (i == ADC_CHANNEL) { AUD_set_active_in (s->adc_voice, on); - } - else { + } else { AUD_set_active_out (s->dac_voice[i], on); } } @@ -471,8 +452,9 @@ static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl) static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr) { addr &= 0xff; - if (addr >= 0x30 && addr <= 0x3f) + if (addr >= 0x30 && addr <= 0x3f) { addr |= s->mempage << 8; + } return addr; } @@ -503,9 +485,9 @@ static void es1370_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) case ES1370_REG_DAC2_SCOUNT: case ES1370_REG_ADC_SCOUNT: d += (addr - ES1370_REG_DAC1_SCOUNT) >> 2; - d->scount = (val & 0xffff) | (d->scount & ~0xffff); - ldebug ("chan %td CURR_SAMP_CT %d, SAMP_CT %d\n", - d - &s->chan[0], val >> 16, (val & 0xffff)); + d->scount = (val & 0xffff) << 16 | (val & 0xffff); + trace_es1370_sample_count_wr(d - &s->chan[0], + d->scount >> 16, d->scount & 0xffff); break; case ES1370_REG_ADC_FRAMEADR: @@ -516,14 +498,14 @@ static void es1370_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) d += (addr - ES1370_REG_DAC1_FRAMEADR) >> 3; frameadr: d->frame_addr = val; - ldebug ("chan %td frame address %#x\n", d - &s->chan[0], val); + trace_es1370_frame_address_wr(d - &s->chan[0], d->frame_addr); break; case ES1370_REG_PHANTOM_FRAMECNT: - lwarn ("writing to phantom frame count %#x\n", val); + lwarn("writing to phantom frame count 0x%" PRIx64 "\n", val); break; case ES1370_REG_PHANTOM_FRAMEADR: - lwarn ("writing to phantom frame address %#x\n", val); + lwarn("writing to phantom frame address 0x%" PRIx64 "\n", val); break; case ES1370_REG_ADC_FRAMECNT: @@ -535,12 +517,12 @@ static void es1370_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) framecnt: d->frame_cnt = val; d->leftover = 0; - ldebug ("chan %td frame count %d, buffer size %d\n", - d - &s->chan[0], val >> 16, val & 0xffff); + trace_es1370_frame_count_wr(d - &s->chan[0], + d->frame_cnt >> 16, d->frame_cnt & 0xffff); break; default: - lwarn ("writel %#x <- %#x\n", addr, val); + lwarn("writel 0x%" PRIx64 " <- 0x%" PRIx64 "\n", addr, val); break; } } @@ -574,17 +556,9 @@ static uint64_t es1370_read(void *opaque, hwaddr addr, unsigned size) case ES1370_REG_DAC2_SCOUNT: case ES1370_REG_ADC_SCOUNT: d += (addr - ES1370_REG_DAC1_SCOUNT) >> 2; + trace_es1370_sample_count_rd(d - &s->chan[0], + d->scount >> 16, d->scount & 0xffff); val = d->scount; -#ifdef DEBUG_ES1370 - { - uint32_t curr_count = d->scount >> 16; - uint32_t count = d->scount & 0xffff; - - curr_count <<= d->shift; - count <<= d->shift; - dolog ("read scount curr %d, total %d\n", curr_count, count); - } -#endif break; case ES1370_REG_ADC_FRAMECNT: @@ -594,17 +568,9 @@ static uint64_t es1370_read(void *opaque, hwaddr addr, unsigned size) case ES1370_REG_DAC2_FRAMECNT: d += (addr - ES1370_REG_DAC1_FRAMECNT) >> 3; framecnt: + trace_es1370_frame_count_rd(d - &s->chan[0], + d->frame_cnt >> 16, d->frame_cnt & 0xffff); val = d->frame_cnt; -#ifdef DEBUG_ES1370 - { - uint32_t size = ((d->frame_cnt & 0xffff) + 1) << 2; - uint32_t curr = ((d->frame_cnt >> 16) + 1) << 2; - if (curr > size) { - dolog ("read framecnt curr %d, size %d %d\n", curr, size, - curr > size); - } - } -#endif break; case ES1370_REG_ADC_FRAMEADR: @@ -614,30 +580,32 @@ static uint64_t es1370_read(void *opaque, hwaddr addr, unsigned size) case ES1370_REG_DAC2_FRAMEADR: d += (addr - ES1370_REG_DAC1_FRAMEADR) >> 3; frameadr: + trace_es1370_frame_address_rd(d - &s->chan[0], d->frame_addr); val = d->frame_addr; break; case ES1370_REG_PHANTOM_FRAMECNT: val = ~0U; - lwarn ("reading from phantom frame count\n"); + lwarn("reading from phantom frame count\n"); break; case ES1370_REG_PHANTOM_FRAMEADR: val = ~0U; - lwarn ("reading from phantom frame address\n"); + lwarn("reading from phantom frame address\n"); break; default: val = ~0U; - lwarn ("readl %#x -> %#x\n", addr, val); + lwarn("readl 0x%" PRIx64 " -> 0x%x\n", addr, val); break; } return val; } static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel, - int max, int *irq) + int max, bool *irq) { uint8_t tmpbuf[4096]; + size_t to_transfer; uint32_t addr = d->frame_addr; int sc = d->scount & 0xffff; int csc = d->scount >> 16; @@ -649,77 +617,87 @@ static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel, } int left = ((size - cnt + 1) << 2) + d->leftover; int transferred = 0; - int temp = MIN (max, MIN (left, csc_bytes)); int index = d - &s->chan[0]; + to_transfer = MIN(max, MIN(left, csc_bytes)); addr += (cnt << 2) + d->leftover; if (index == ADC_CHANNEL) { - while (temp > 0) { + while (to_transfer > 0) { int acquired, to_copy; - to_copy = MIN ((size_t) temp, sizeof (tmpbuf)); + to_copy = MIN(to_transfer, sizeof(tmpbuf)); acquired = AUD_read (s->adc_voice, tmpbuf, to_copy); - if (!acquired) + if (!acquired) { break; + } pci_dma_write (&s->dev, addr, tmpbuf, acquired); - temp -= acquired; + to_transfer -= acquired; addr += acquired; transferred += acquired; } - } - else { + } else { SWVoiceOut *voice = s->dac_voice[index]; - while (temp > 0) { + while (to_transfer > 0) { int copied, to_copy; - to_copy = MIN ((size_t) temp, sizeof (tmpbuf)); + to_copy = MIN(to_transfer, sizeof(tmpbuf)); pci_dma_read (&s->dev, addr, tmpbuf, to_copy); copied = AUD_write (voice, tmpbuf, to_copy); - if (!copied) + if (!copied) { break; - temp -= copied; + } + to_transfer -= copied; addr += copied; transferred += copied; } } if (csc_bytes == transferred) { - *irq = 1; + if (*irq) { + trace_es1370_lost_interrupt(index); + } + *irq = true; d->scount = sc | (sc << 16); - ldebug ("sc = %d, rate = %f\n", - (sc + 1) << d->shift, - (sc + 1) / (double) 44100); - } - else { - *irq = 0; + } else { + *irq = false; d->scount = sc | (((csc_bytes - transferred - 1) >> d->shift) << 16); } cnt += (transferred + d->leftover) >> 2; if (s->sctl & loop_sel) { - /* Bah, how stupid is that having a 0 represent true value? - i just spent few hours on this shit */ + /* + * loop_sel tells us which bit in the SCTL register to look at + * (either P1_LOOP_SEL, P2_LOOP_SEL or R1_LOOP_SEL). The sense + * of these bits is 0 for loop mode (set interrupt and keep recording + * when the sample count reaches zero) or 1 for stop mode (set + * interrupt and stop recording). + */ AUD_log ("es1370: warning", "non looping mode\n"); - } - else { + } else { d->frame_cnt = size; - if ((uint32_t) cnt <= d->frame_cnt) + if ((uint32_t) cnt <= d->frame_cnt) { d->frame_cnt |= cnt << 16; + } } d->leftover = (transferred + d->leftover) & 3; + trace_es1370_transfer_audio(index, + d->frame_cnt >> 16, d->frame_cnt & 0xffff, + d->scount >> 16, d->scount & 0xffff, + d->leftover, *irq); } static void es1370_run_channel (ES1370State *s, size_t chan, int free_or_avail) { uint32_t new_status = s->status; - int max_bytes, irq; + int max_bytes; + bool irq; struct chan *d = &s->chan[chan]; const struct chan_bits *b = &es1370_chan_bits[chan]; @@ -733,6 +711,8 @@ static void es1370_run_channel (ES1370State *s, size_t chan, int free_or_avail) return; } + irq = s->sctl & b->sctl_inten && s->status & b->stat_int; + es1370_transfer_audio (s, d, b->sctl_loopsel, max_bytes, &irq); if (irq) { @@ -785,7 +765,7 @@ static const VMStateDescription vmstate_es1370_channel = { .name = "es1370_channel", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32 (shift, struct chan), VMSTATE_UINT32 (leftover, struct chan), VMSTATE_UINT32 (scount, struct chan), @@ -807,8 +787,7 @@ static int es1370_post_load (void *opaque, int version_id) AUD_close_in (&s->card, s->adc_voice); s->adc_voice = NULL; } - } - else { + } else { if (s->dac_voice[i]) { AUD_close_out (&s->card, s->dac_voice[i]); s->dac_voice[i] = NULL; @@ -829,7 +808,7 @@ static const VMStateDescription vmstate_es1370 = { .version_id = 2, .minimum_version_id = 2, .post_load = es1370_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE (dev, ES1370State), VMSTATE_STRUCT_ARRAY (chan, ES1370State, NB_CHANNELS, 2, vmstate_es1370_channel, struct chan), @@ -844,7 +823,8 @@ static const VMStateDescription vmstate_es1370 = { static void es1370_on_reset(DeviceState *dev) { - ES1370State *s = container_of(dev, ES1370State, dev.qdev); + ES1370State *s = ES1370(dev); + es1370_reset (s); } @@ -853,6 +833,10 @@ static void es1370_realize(PCIDevice *dev, Error **errp) ES1370State *s = ES1370(dev); uint8_t *c = s->dev.config; + if (!AUD_register_card ("es1370", &s->card, errp)) { + return; + } + c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_SLOW >> 8; #if 0 @@ -868,7 +852,6 @@ static void es1370_realize(PCIDevice *dev, Error **errp) memory_region_init_io (&s->io, OBJECT(s), &es1370_io_ops, s, "es1370", 256); pci_register_bar (&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io); - AUD_register_card ("es1370", &s->card); es1370_reset (s); } diff --git a/hw/audio/fmopl.c b/hw/audio/fmopl.c index 8a71a569fa..a63ad0f04d 100644 --- a/hw/audio/fmopl.c +++ b/hw/audio/fmopl.c @@ -355,7 +355,7 @@ static void set_algorithm( OPL_CH *CH) CH->connect2 = carrier; } -/* ---------- frequency counter for operater update ---------- */ +/* ---------- frequency counter for operator update ---------- */ static inline void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) { int ksr; @@ -640,7 +640,7 @@ static int OPLOpenTable( void ) TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0; } - /* make sinwave table (total level offet) */ + /* make sinwave table (total level offset) */ /* degree 0 = degree 180 = off */ SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2] = &TL_TABLE[EG_ENT-1]; for (s = 1;s <= SIN_ENT/4;s++){ @@ -1075,7 +1075,7 @@ FM_OPL *OPLCreate(int clock, int rate) char *ptr; FM_OPL *OPL; int state_size; - int max_ch = 9; /* normaly 9 channels */ + int max_ch = 9; /* normally 9 channels */ if( OPL_LockTable() ==-1) return NULL; /* allocate OPL state space */ @@ -1092,7 +1092,7 @@ FM_OPL *OPLCreate(int clock, int rate) OPL->clock = clock; OPL->rate = rate; OPL->max_ch = max_ch; - /* init grobal tables */ + /* init global tables */ OPL_initialize(OPL); /* reset chip */ OPLResetChip(OPL); diff --git a/hw/audio/fmopl.h b/hw/audio/fmopl.h index e008e72d7a..89086b93f4 100644 --- a/hw/audio/fmopl.h +++ b/hw/audio/fmopl.h @@ -69,7 +69,7 @@ typedef struct fm_opl_f { /* FM channel slots */ OPL_CH *P_CH; /* pointer of CH */ int max_ch; /* maximum channel */ - /* Rhythm sention */ + /* Rhythm section */ uint8_t rhythm; /* Rhythm mode , key flag */ /* time tables */ int32_t AR_TABLE[76]; /* attack rate tables */ diff --git a/hw/audio/gus.c b/hw/audio/gus.c index 42f010b671..4beb3fd74e 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -209,7 +209,7 @@ static const VMStateDescription vmstate_gus = { .name = "gus", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32 (pos, GUSState), VMSTATE_INT32 (left, GUSState), VMSTATE_INT32 (shift, GUSState), @@ -236,18 +236,21 @@ static const MemoryRegionPortio gus_portio_list2[] = { static void gus_realizefn (DeviceState *dev, Error **errp) { ISADevice *d = ISA_DEVICE(dev); + ISABus *bus = isa_bus_from_device(d); GUSState *s = GUS (dev); IsaDmaClass *k; struct audsettings as; - s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->emu.gusdma); + if (!AUD_register_card ("gus", &s->card, errp)) { + return; + } + + s->isa_dma = isa_bus_get_dma(bus, s->emu.gusdma); if (!s->isa_dma) { error_setg(errp, "ISA controller does not support DMA"); return; } - AUD_register_card ("gus", &s->card); - as.freq = s->freq; as.nchannels = 2; as.fmt = AUDIO_FORMAT_S16; @@ -282,7 +285,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp) s->emu.himemaddr = s->himem; s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32; s->emu.opaque = s; - s->pic = isa_get_irq(d, s->emu.gusirq); + s->pic = isa_bus_get_irq(bus, s->emu.gusirq); AUD_set_active_out (s->voice, 1); } diff --git a/hw/audio/gusemu_hal.c b/hw/audio/gusemu_hal.c index 5b9a14ee21..f159978b49 100644 --- a/hw/audio/gusemu_hal.c +++ b/hw/audio/gusemu_hal.c @@ -154,7 +154,7 @@ unsigned int gus_read(GUSEmuState * state, int port, int size) case 0x8d: { int offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f); - offset += ((int) GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */ + offset += ((int) GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Function*2 */ value_read = GUSregw(offset); } break; @@ -353,7 +353,7 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data) if (!(GUSregb(GUS4cReset) & 0x01)) break; /* reset flag active? */ offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f); - offset += (GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */ + offset += (GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Function*2 */ GUSregw(offset) = (uint16_t) ((GUSregw(offset) & readmask) | writedata); } break; diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index feb8f9e2bb..b22e486fda 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -22,6 +22,7 @@ #include "hw/qdev-properties.h" #include "intel-hda.h" #include "migration/vmstate.h" +#include "qemu/host-utils.h" #include "qemu/module.h" #include "intel-hda-defs.h" #include "audio/audio.h" @@ -145,7 +146,9 @@ static const char *fmt2name[] = { [ AUDIO_FORMAT_S32 ] = "PCM-S32", }; -typedef struct HDAAudioState HDAAudioState; +#define TYPE_HDA_AUDIO "hda-audio" +OBJECT_DECLARE_SIMPLE_TYPE(HDAAudioState, HDA_AUDIO) + typedef struct HDAAudioStream HDAAudioStream; struct HDAAudioStream { @@ -171,9 +174,6 @@ struct HDAAudioStream { int64_t buft_start; }; -#define TYPE_HDA_AUDIO "hda-audio" -OBJECT_DECLARE_SIMPLE_TYPE(HDAAudioState, HDA_AUDIO) - struct HDAAudioState { HDACodecDevice hda; const char *name; @@ -190,9 +190,9 @@ struct HDAAudioState { bool use_timer; }; -static inline int64_t hda_bytes_per_second(HDAAudioStream *st) +static inline uint32_t hda_bytes_per_second(HDAAudioStream *st) { - return 2LL * st->as.nchannels * st->as.freq; + return 2 * (uint32_t)st->as.nchannels * (uint32_t)st->as.freq; } static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos) @@ -223,12 +223,18 @@ static void hda_audio_input_timer(void *opaque) int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - int64_t buft_start = st->buft_start; + int64_t uptime = now - st->buft_start; int64_t wpos = st->wpos; int64_t rpos = st->rpos; + int64_t wanted_rpos; - int64_t wanted_rpos = hda_bytes_per_second(st) * (now - buft_start) - / NANOSECONDS_PER_SECOND; + if (uptime <= 0) { + /* wanted_rpos <= 0 */ + goto out_timer; + } + + wanted_rpos = muldiv64(uptime, hda_bytes_per_second(st), + NANOSECONDS_PER_SECOND); wanted_rpos &= -4; /* IMPORTANT! clip to frames */ if (wanted_rpos <= rpos) { @@ -287,12 +293,18 @@ static void hda_audio_output_timer(void *opaque) int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - int64_t buft_start = st->buft_start; + int64_t uptime = now - st->buft_start; int64_t wpos = st->wpos; int64_t rpos = st->rpos; + int64_t wanted_wpos; - int64_t wanted_wpos = hda_bytes_per_second(st) * (now - buft_start) - / NANOSECONDS_PER_SECOND; + if (uptime <= 0) { + /* wanted_wpos <= 0 */ + goto out_timer; + } + + wanted_wpos = muldiv64(uptime, hda_bytes_per_second(st), + NANOSECONDS_PER_SECOND); wanted_wpos &= -4; /* IMPORTANT! clip to frames */ if (wanted_wpos <= wpos) { @@ -676,7 +688,9 @@ static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, b } } -static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) +static void hda_audio_init(HDACodecDevice *hda, + const struct desc_codec *desc, + Error **errp) { HDAAudioState *a = HDA_AUDIO(hda); HDAAudioStream *st; @@ -684,11 +698,14 @@ static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) const desc_param *param; uint32_t i, type; + if (!AUD_register_card("hda", &a->card, errp)) { + return; + } + a->desc = desc; a->name = object_get_typename(OBJECT(a)); dprint(a, 1, "%s: cad %d\n", __func__, a->hda.cad); - AUD_register_card("hda", &a->card); for (i = 0; i < a->desc->nnodes; i++) { node = a->desc->nodes + i; param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP); @@ -719,7 +736,6 @@ static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) break; } } - return 0; } static void hda_audio_exit(HDACodecDevice *hda) @@ -796,7 +812,7 @@ static const VMStateDescription vmstate_hda_audio_stream_buf = { .name = "hda-audio-stream/buffer", .version_id = 1, .needed = vmstate_hda_audio_stream_buf_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BUFFER(buf, HDAAudioStream), VMSTATE_INT64(rpos, HDAAudioStream), VMSTATE_INT64(wpos, HDAAudioStream), @@ -809,7 +825,7 @@ static const VMStateDescription vmstate_hda_audio_stream_buf = { static const VMStateDescription vmstate_hda_audio_stream = { .name = "hda-audio-stream", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(stream, HDAAudioStream), VMSTATE_UINT32(channel, HDAAudioStream), VMSTATE_UINT32(format, HDAAudioStream), @@ -821,7 +837,7 @@ static const VMStateDescription vmstate_hda_audio_stream = { VMSTATE_BUFFER(compat_buf, HDAAudioStream), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_hda_audio_stream_buf, NULL } @@ -831,7 +847,7 @@ static const VMStateDescription vmstate_hda_audio = { .name = "hda-audio", .version_id = 2, .post_load = hda_audio_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0, vmstate_hda_audio_stream, HDAAudioStream), @@ -849,37 +865,40 @@ static Property hda_audio_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static int hda_audio_init_output(HDACodecDevice *hda) +static void hda_audio_init_output(HDACodecDevice *hda, Error **errp) { HDAAudioState *a = HDA_AUDIO(hda); + const struct desc_codec *desc = &output_mixemu; if (!a->mixer) { - return hda_audio_init(hda, &output_nomixemu); - } else { - return hda_audio_init(hda, &output_mixemu); + desc = &output_nomixemu; } + + hda_audio_init(hda, desc, errp); } -static int hda_audio_init_duplex(HDACodecDevice *hda) +static void hda_audio_init_duplex(HDACodecDevice *hda, Error **errp) { HDAAudioState *a = HDA_AUDIO(hda); + const struct desc_codec *desc = &duplex_mixemu; if (!a->mixer) { - return hda_audio_init(hda, &duplex_nomixemu); - } else { - return hda_audio_init(hda, &duplex_mixemu); + desc = &duplex_nomixemu; } + + hda_audio_init(hda, desc, errp); } -static int hda_audio_init_micro(HDACodecDevice *hda) +static void hda_audio_init_micro(HDACodecDevice *hda, Error **errp) { HDAAudioState *a = HDA_AUDIO(hda); + const struct desc_codec *desc = µ_mixemu; if (!a->mixer) { - return hda_audio_init(hda, µ_nomixemu); - } else { - return hda_audio_init(hda, µ_mixemu); + desc = µ_nomixemu; } + + hda_audio_init(hda, desc, errp); } static void hda_audio_base_class_init(ObjectClass *klass, void *data) diff --git a/hw/audio/intel-hda-defs.h b/hw/audio/intel-hda-defs.h index 2e37e5b874..261bdb48ff 100644 --- a/hw/audio/intel-hda-defs.h +++ b/hw/audio/intel-hda-defs.h @@ -418,7 +418,7 @@ enum { #define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */ #define AC_UNSOL_RES_CP_READY (1<<0) /* content protection */ -/* Pin widget capabilies */ +/* Pin widget capabilities */ #define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */ #define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */ #define AC_PINCAP_PRES_DETECT (1<<2) /* presence detect capable */ @@ -483,7 +483,7 @@ enum { #define AC_PWRST_D2 0x02 #define AC_PWRST_D3 0x03 -/* Processing capabilies */ +/* Processing capabilities */ #define AC_PCAP_BENIGN (1<<0) #define AC_PCAP_NUM_COEF (0xff<<8) #define AC_PCAP_NUM_COEF_SHIFT 8 diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index f38117057b..9c54e60b71 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -71,9 +71,7 @@ static void hda_codec_dev_realize(DeviceState *qdev, Error **errp) return; } bus->next_cad = dev->cad + 1; - if (cdc->init(dev) != 0) { - error_setg(errp, "HDA audio init failed"); - } + cdc->init(dev, errp); } static void hda_codec_dev_unrealize(DeviceState *qdev) @@ -220,8 +218,6 @@ struct IntelHDAReg { void (*rhandler)(IntelHDAState *d, const IntelHDAReg *reg); }; -static void intel_hda_reset(DeviceState *dev); - /* --------------------------------------------------------------------- */ static hwaddr intel_hda_addr(uint32_t lbase, uint32_t ubase) @@ -516,7 +512,7 @@ static void intel_hda_notify_codecs(IntelHDAState *d, uint32_t stream, bool runn static void intel_hda_set_g_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) { if ((d->g_ctl & ICH6_GCTL_RESET) == 0) { - intel_hda_reset(DEVICE(d)); + device_cold_reset(DEVICE(d)); } } @@ -1083,11 +1079,9 @@ static void intel_hda_reset(DeviceState *dev) intel_hda_regs_reset(d); d->wall_base_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - /* reset codecs */ QTAILQ_FOREACH(kid, &d->codecs.qbus.children, sibling) { DeviceState *qdev = kid->child; cdev = HDA_CODEC_DEVICE(qdev); - device_legacy_reset(DEVICE(cdev)); d->state_sts |= (1 << cdev->cad); } intel_hda_update_irq(d); @@ -1164,7 +1158,7 @@ static int intel_hda_post_load(void *opaque, int version) static const VMStateDescription vmstate_intel_hda_stream = { .name = "intel-hda-stream", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ctl, IntelHDAStream), VMSTATE_UINT32(lpib, IntelHDAStream), VMSTATE_UINT32(cbl, IntelHDAStream), @@ -1180,7 +1174,7 @@ static const VMStateDescription vmstate_intel_hda = { .name = "intel-hda", .version_id = 1, .post_load = intel_hda_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(pci, IntelHDAState), /* registers */ diff --git a/hw/audio/intel-hda.h b/hw/audio/intel-hda.h index f78c1833e3..8d710eee5d 100644 --- a/hw/audio/intel-hda.h +++ b/hw/audio/intel-hda.h @@ -31,7 +31,7 @@ struct HDACodecBus { struct HDACodecDeviceClass { DeviceClass parent_class; - int (*init)(HDACodecDevice *dev); + void (*init)(HDACodecDevice *dev, Error **errp); void (*exit)(HDACodecDevice *dev); void (*command)(HDACodecDevice *dev, uint32_t nid, uint32_t data); void (*stream)(HDACodecDevice *dev, uint32_t stnr, bool running, bool output); diff --git a/hw/audio/lm4549.c b/hw/audio/lm4549.c index 32b1481b56..a4a77c8dc6 100644 --- a/hw/audio/lm4549.c +++ b/hw/audio/lm4549.c @@ -276,10 +276,16 @@ static int lm4549_post_load(void *opaque, int version_id) return 0; } -void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque) +void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque, + Error **errp) { struct audsettings as; + /* Register an audio card */ + if (!AUD_register_card("lm4549", &s->card, errp)) { + return; + } + /* Store the callback and opaque pointer */ s->data_req_cb = data_req_cb; s->opaque = opaque; @@ -287,9 +293,6 @@ void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque) /* Init the registers */ lm4549_reset(s); - /* Register an audio card */ - AUD_register_card("lm4549", &s->card); - /* Open a default voice */ as.freq = 48000; as.nchannels = 2; @@ -326,7 +329,7 @@ const VMStateDescription vmstate_lm4549_state = { .version_id = 1, .minimum_version_id = 1, .post_load = lm4549_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(voice_is_active, lm4549_state), VMSTATE_UINT16_ARRAY(regfile, lm4549_state, 128), VMSTATE_UINT16_ARRAY(buffer, lm4549_state, LM4549_BUFFER_SIZE), diff --git a/hw/audio/lm4549.h b/hw/audio/lm4549.h index aba9bb5b07..61c3ab12dd 100644 --- a/hw/audio/lm4549.h +++ b/hw/audio/lm4549.h @@ -36,7 +36,8 @@ typedef struct { extern const VMStateDescription vmstate_lm4549_state; -void lm4549_init(lm4549_state *s, lm4549_callback data_req, void *opaque); +void lm4549_init(lm4549_state *s, lm4549_callback data_req, void *opaque, + Error **errp); uint32_t lm4549_read(lm4549_state *s, hwaddr offset); void lm4549_write(lm4549_state *s, hwaddr offset, uint32_t value); uint32_t lm4549_write_samples(lm4549_state *s, uint32_t left, uint32_t right); diff --git a/hw/audio/marvell_88w8618.c b/hw/audio/marvell_88w8618.c index e6c09bdb8e..cc285444bc 100644 --- a/hw/audio/marvell_88w8618.c +++ b/hw/audio/marvell_88w8618.c @@ -273,7 +273,7 @@ static const VMStateDescription mv88w8618_audio_vmsd = { .name = "mv88w8618_audio", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(playback_mode, mv88w8618_audio_state), VMSTATE_UINT32(status, mv88w8618_audio_state), VMSTATE_UINT32(irq_enable, mv88w8618_audio_state), diff --git a/hw/audio/meson.build b/hw/audio/meson.build index e48a9fc73d..2990974449 100644 --- a/hw/audio/meson.build +++ b/hw/audio/meson.build @@ -1,14 +1,17 @@ -softmmu_ss.add(files('soundhw.c')) -softmmu_ss.add(when: 'CONFIG_AC97', if_true: files('ac97.c')) -softmmu_ss.add(when: 'CONFIG_ADLIB', if_true: files('fmopl.c', 'adlib.c')) -softmmu_ss.add(when: 'CONFIG_CS4231', if_true: files('cs4231.c')) -softmmu_ss.add(when: 'CONFIG_CS4231A', if_true: files('cs4231a.c')) -softmmu_ss.add(when: 'CONFIG_ES1370', if_true: files('es1370.c')) -softmmu_ss.add(when: 'CONFIG_GUS', if_true: files('gus.c', 'gusemu_hal.c', 'gusemu_mixer.c')) -softmmu_ss.add(when: 'CONFIG_HDA', if_true: files('intel-hda.c', 'hda-codec.c')) -softmmu_ss.add(when: 'CONFIG_MARVELL_88W8618', if_true: files('marvell_88w8618.c')) -softmmu_ss.add(when: 'CONFIG_PCSPK', if_true: files('pcspk.c')) -softmmu_ss.add(when: 'CONFIG_PL041', if_true: files('pl041.c', 'lm4549.c')) -softmmu_ss.add(when: 'CONFIG_SB16', if_true: files('sb16.c')) -softmmu_ss.add(when: 'CONFIG_VT82C686', if_true: files('via-ac97.c')) -softmmu_ss.add(when: 'CONFIG_WM8750', if_true: files('wm8750.c')) +system_ss.add(files('soundhw.c')) +system_ss.add(when: 'CONFIG_AC97', if_true: files('ac97.c')) +system_ss.add(when: 'CONFIG_ADLIB', if_true: files('fmopl.c', 'adlib.c')) +system_ss.add(when: 'CONFIG_ASC', if_true: files('asc.c')) +system_ss.add(when: 'CONFIG_CS4231', if_true: files('cs4231.c')) +system_ss.add(when: 'CONFIG_CS4231A', if_true: files('cs4231a.c')) +system_ss.add(when: 'CONFIG_ES1370', if_true: files('es1370.c')) +system_ss.add(when: 'CONFIG_GUS', if_true: files('gus.c', 'gusemu_hal.c', 'gusemu_mixer.c')) +system_ss.add(when: 'CONFIG_HDA', if_true: files('intel-hda.c', 'hda-codec.c')) +system_ss.add(when: 'CONFIG_MARVELL_88W8618', if_true: files('marvell_88w8618.c')) +system_ss.add(when: 'CONFIG_PCSPK', if_true: files('pcspk.c')) +system_ss.add(when: 'CONFIG_PL041', if_true: files('pl041.c', 'lm4549.c')) +system_ss.add(when: 'CONFIG_SB16', if_true: files('sb16.c')) +system_ss.add(when: 'CONFIG_VT82C686', if_true: files('via-ac97.c')) +system_ss.add(when: 'CONFIG_WM8750', if_true: files('wm8750.c')) +system_ss.add(when: ['CONFIG_VIRTIO_SND', 'CONFIG_VIRTIO'], if_true: files('virtio-snd.c')) +system_ss.add(when: ['CONFIG_VIRTIO_SND', 'CONFIG_VIRTIO', 'CONFIG_VIRTIO_PCI'], if_true: files('virtio-snd-pci.c')) diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index daf92a4ce1..a4b89f1768 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -123,8 +123,6 @@ static int pcspk_audio_init(PCSpkState *s) return 0; } - AUD_register_card(s_spk, &s->card); - s->voice = AUD_open_out(&s->card, s->voice, s_spk, s, pcspk_callback, &as); if (!s->voice) { AUD_log(s_spk, "Could not open voice\n"); @@ -191,7 +189,7 @@ static void pcspk_realizefn(DeviceState *dev, Error **errp) isa_register_ioport(isadev, &s->ioport, s->iobase); - if (s->card.state) { + if (s->card.state && AUD_register_card(s_spk, &s->card, errp)) { pcspk_audio_init(s); } @@ -210,7 +208,7 @@ static const VMStateDescription vmstate_spk = { .version_id = 1, .minimum_version_id = 1, .needed = migrate_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(data_on, PCSpkState), VMSTATE_UINT8(dummy_refresh_clock, PCSpkState), VMSTATE_END_OF_LIST() diff --git a/hw/audio/pl041.c b/hw/audio/pl041.c index 03acd4fe34..b435208c24 100644 --- a/hw/audio/pl041.c +++ b/hw/audio/pl041.c @@ -564,14 +564,14 @@ static void pl041_realize(DeviceState *dev, Error **errp) } /* Init the codec */ - lm4549_init(&s->codec, &pl041_request_data, (void *)s); + lm4549_init(&s->codec, &pl041_request_data, (void *)s, errp); } static const VMStateDescription vmstate_pl041_regfile = { .name = "pl041_regfile", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { #define REGISTER(name, offset) VMSTATE_UINT32(name, pl041_regfile), #include "pl041.hx" #undef REGISTER @@ -583,7 +583,7 @@ static const VMStateDescription vmstate_pl041_fifo = { .name = "pl041_fifo", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(level, pl041_fifo), VMSTATE_UINT32_ARRAY(data, pl041_fifo, MAX_FIFO_DEPTH), VMSTATE_END_OF_LIST() @@ -594,7 +594,7 @@ static const VMStateDescription vmstate_pl041_channel = { .name = "pl041_channel", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(tx_fifo, pl041_channel, 0, vmstate_pl041_fifo, pl041_fifo), VMSTATE_UINT8(tx_enabled, pl041_channel), @@ -613,7 +613,7 @@ static const VMStateDescription vmstate_pl041 = { .name = "pl041", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(fifo_depth, PL041State), VMSTATE_STRUCT(regs, PL041State, 0, vmstate_pl041_regfile, pl041_regfile), diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index 2215386ddb..fd76e78d18 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -1324,12 +1324,12 @@ static const VMStateDescription vmstate_sb16 = { .version_id = 1, .minimum_version_id = 1, .post_load = sb16_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32 (irq, SB16State), - VMSTATE_UINT32 (dma, SB16State), - VMSTATE_UINT32 (hdma, SB16State), - VMSTATE_UINT32 (port, SB16State), - VMSTATE_UINT32 (ver, SB16State), + .fields = (const VMStateField[]) { + VMSTATE_UNUSED( 4 /* irq */ + + 4 /* dma */ + + 4 /* hdma */ + + 4 /* port */ + + 4 /* ver */), VMSTATE_INT32 (in_index, SB16State), VMSTATE_INT32 (out_data_len, SB16State), VMSTATE_INT32 (fmt_stereo, SB16State), @@ -1398,17 +1398,22 @@ static void sb16_initfn (Object *obj) static void sb16_realizefn (DeviceState *dev, Error **errp) { ISADevice *isadev = ISA_DEVICE (dev); + ISABus *bus = isa_bus_from_device(isadev); SB16State *s = SB16 (dev); IsaDmaClass *k; - s->isa_hdma = isa_get_dma(isa_bus_from_device(isadev), s->hdma); - s->isa_dma = isa_get_dma(isa_bus_from_device(isadev), s->dma); + if (!AUD_register_card ("sb16", &s->card, errp)) { + return; + } + + s->isa_hdma = isa_bus_get_dma(bus, s->hdma); + s->isa_dma = isa_bus_get_dma(bus, s->dma); if (!s->isa_dma || !s->isa_hdma) { error_setg(errp, "ISA controller does not support DMA"); return; } - s->pic = isa_get_irq(isadev, s->irq); + s->pic = isa_bus_get_irq(bus, s->irq); s->mixer_regs[0x80] = magic_of_irq (s->irq); s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma); @@ -1433,8 +1438,6 @@ static void sb16_realizefn (DeviceState *dev, Error **errp) k->register_channel(s->isa_dma, s->dma, SB_read_DMA, s); s->can_write = 1; - - AUD_register_card ("sb16", &s->card); } static Property sb16_properties[] = { diff --git a/hw/audio/soundhw.c b/hw/audio/soundhw.c index 94d9463e42..b387b0ef7d 100644 --- a/hw/audio/soundhw.c +++ b/hw/audio/soundhw.c @@ -83,7 +83,7 @@ void show_valid_soundhw(void) static struct soundhw *selected = NULL; static const char *audiodev_id; -void select_soundhw(const char *optarg, const char *audiodev) +void select_soundhw(const char *name, const char *audiodev) { struct soundhw *c; @@ -92,7 +92,7 @@ void select_soundhw(const char *optarg, const char *audiodev) } for (c = soundhw; c->name; ++c) { - if (g_str_equal(c->name, optarg)) { + if (g_str_equal(c->name, name)) { selected = c; audiodev_id = audiodev; break; @@ -100,7 +100,7 @@ void select_soundhw(const char *optarg, const char *audiodev) } if (!c->name) { - error_report("Unknown sound card name `%s'", optarg); + error_report("Unknown sound card name `%s'", name); show_valid_soundhw(); exit(1); } diff --git a/hw/audio/trace-events b/hw/audio/trace-events index e0e71cd9b1..b1870ff224 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -6,8 +6,55 @@ cs4231_mem_readl_reg(uint32_t reg, uint32_t ret) "read reg %d: 0x%08x" cs4231_mem_writel_reg(uint32_t reg, uint32_t old, uint32_t val) "write reg %d: 0x%08x -> 0x%08x" cs4231_mem_writel_dreg(uint32_t reg, uint32_t old, uint32_t val) "write dreg %d: 0x%02x -> 0x%02x" +# es1370.c +es1370_frame_address_rd(int ch, uint32_t addr) "ch=%d addr=0x%08x" +es1370_frame_address_wr(int ch, uint32_t addr) "ch=%d addr=0x%08x" +es1370_frame_count_rd(int ch, uint32_t curr, uint32_t size) "ch=%d CURR_CT=%u BUF_SIZE=%u" +es1370_frame_count_wr(int ch, uint32_t curr, uint32_t size) "ch=%d CURR_CT=%u BUF_SIZE=%u" +es1370_lost_interrupt(int ch) "ch=%d lost interrupt" +es1370_sample_count_rd(int ch, uint32_t curr, uint32_t num) "ch=%d CURR_SAMP_CT=%u SAMP_CT=%u" +es1370_sample_count_wr(int ch, uint32_t curr, uint32_t num) "ch=%d CURR_SAMP_CT=%u SAMP_CT=%u" +es1370_stream_format(int ch, uint32_t freq, const char *fmt, const char *mode, uint32_t shift) "ch=%d fmt=%u:%s:%s shift=%u" +es1370_transfer_audio(int ch, uint32_t f_curr, uint32_t f_size, uint32_t s_curr, uint32_t s_num, uint32_t leftover, bool irq) "ch=%d CURR_CT=%u BUF_SIZE=%u CURR_SAMP_CT=%u SAMP_CT=%u leftover=%u irq=%d" + # hda-codec.c hda_audio_running(const char *stream, int nr, bool running) "st %s, nr %d, run %d" hda_audio_format(const char *stream, int chan, const char *fmt, int freq) "st %s, %d x %s @ %d Hz" hda_audio_adjust(const char *stream, int pos) "st %s, pos %d" hda_audio_overrun(const char *stream) "st %s" + +#via-ac97.c +via_ac97_codec_write(uint8_t addr, uint16_t val) "0x%x <- 0x%x" +via_ac97_sgd_fetch(uint32_t curr, uint32_t addr, char stop, char eol, char flag, uint32_t len) "curr=0x%x addr=0x%x %c%c%c len=%d" +via_ac97_sgd_read(uint64_t addr, unsigned size, uint64_t val) "0x%"PRIx64" %d -> 0x%"PRIx64 +via_ac97_sgd_write(uint64_t addr, unsigned size, uint64_t val) "0x%"PRIx64" %d <- 0x%"PRIx64 + +# asc.c +asc_read_fifo(const char fifo, int reg, unsigned size, uint64_t value) "fifo %c reg=0x%03x size=%u value=0x%"PRIx64 +asc_read_reg(int reg, unsigned size, uint64_t value) "reg=0x%03x size=%u value=0x%"PRIx64 +asc_read_extreg(const char fifo, int reg, unsigned size, uint64_t value) "fifo %c reg=0x%03x size=%u value=0x%"PRIx64 +asc_fifo_get(const char fifo, int rptr, int cnt, uint64_t value) "fifo %c rptr=0x%x cnt=0x%x value=0x%"PRIx64 +asc_write_fifo(const char fifo, int reg, unsigned size, int wrptr, int cnt, uint64_t value) "fifo %c reg=0x%03x size=%u wptr=0x%x cnt=0x%x value=0x%"PRIx64 +asc_write_reg(int reg, unsigned size, uint64_t value) "reg=0x%03x size=%u value=0x%"PRIx64 +asc_write_extreg(const char fifo, int reg, unsigned size, uint64_t value) "fifo %c reg=0x%03x size=%u value=0x%"PRIx64 +asc_update_irq(int irq, int a, int b) "set IRQ to %d (A: 0x%x B: 0x%x)" + +#virtio-snd.c +virtio_snd_get_config(void *vdev, uint32_t jacks, uint32_t streams, uint32_t chmaps) "snd %p: get_config jacks=%"PRIu32" streams=%"PRIu32" chmaps=%"PRIu32"" +virtio_snd_set_config(void *vdev, uint32_t jacks, uint32_t new_jacks, uint32_t streams, uint32_t new_streams, uint32_t chmaps, uint32_t new_chmaps) "snd %p: set_config jacks from %"PRIu32"->%"PRIu32", streams from %"PRIu32"->%"PRIu32", chmaps from %"PRIu32"->%"PRIu32 +virtio_snd_get_features(void *vdev, uint64_t features) "snd %p: get_features 0x%"PRIx64 +virtio_snd_vm_state_running(void) "vm state running" +virtio_snd_vm_state_stopped(void) "vm state stopped" +virtio_snd_realize(void *snd) "snd %p: realize" +virtio_snd_unrealize(void *snd) "snd %p: unrealize" +virtio_snd_handle_pcm_set_params(uint32_t stream) "VIRTIO_SND_PCM_SET_PARAMS called for stream %"PRIu32 +virtio_snd_handle_ctrl(void *vdev, void *vq) "snd %p: handle ctrl event for queue %p" +virtio_snd_handle_pcm_info(uint32_t stream) "VIRTIO_SND_R_PCM_INFO called for stream %"PRIu32 +virtio_snd_handle_pcm_start_stop(const char *code, uint32_t stream) "%s called for stream %"PRIu32 +virtio_snd_handle_pcm_release(uint32_t stream) "VIRTIO_SND_PCM_RELEASE called for stream %"PRIu32 +virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PRIu32" == %s" +virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called" +virtio_snd_handle_event(void) "event queue callback called" +virtio_snd_pcm_stream_flush(uint32_t stream) "flushing stream %"PRIu32 +virtio_snd_handle_tx_xfer(void) "tx queue callback called" +virtio_snd_handle_rx_xfer(void) "rx queue callback called" diff --git a/hw/audio/via-ac97.c b/hw/audio/via-ac97.c index 6d556f74fc..4c127a1def 100644 --- a/hw/audio/via-ac97.c +++ b/hw/audio/via-ac97.c @@ -1,39 +1,484 @@ /* * VIA south bridges sound support * + * Copyright (c) 2022-2023 BALATON Zoltan + * * This work is licensed under the GNU GPL license version 2 or later. */ /* - * TODO: This is entirely boiler plate just registering empty PCI devices - * with the right ID guests expect, functionality should be added here. + * TODO: This is only a basic implementation of one audio playback channel + * more functionality should be added here. */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/isa/vt82c686.h" -#include "hw/pci/pci.h" +#include "ac97.h" +#include "trace.h" + +#define CLEN_IS_EOL(x) ((x)->clen & BIT(31)) +#define CLEN_IS_FLAG(x) ((x)->clen & BIT(30)) +#define CLEN_IS_STOP(x) ((x)->clen & BIT(29)) +#define CLEN_LEN(x) ((x)->clen & 0xffffff) + +#define STAT_ACTIVE BIT(7) +#define STAT_PAUSED BIT(6) +#define STAT_TRIG BIT(3) +#define STAT_STOP BIT(2) +#define STAT_EOL BIT(1) +#define STAT_FLAG BIT(0) + +#define CNTL_START BIT(7) +#define CNTL_TERM BIT(6) +#define CNTL_PAUSE BIT(3) + +static void open_voice_out(ViaAC97State *s); + +static uint16_t codec_rates[] = { 8000, 11025, 16000, 22050, 32000, 44100, + 48000 }; + +#define CODEC_REG(s, o) ((s)->codec_regs[(o) / 2]) +#define CODEC_VOL(vol, mask) ((255 * ((vol) & mask)) / mask) + +static void codec_volume_set_out(ViaAC97State *s) +{ + int lvol, rvol, mute; + + lvol = 255 - CODEC_VOL(CODEC_REG(s, AC97_Master_Volume_Mute) >> 8, 0x1f); + lvol *= 255 - CODEC_VOL(CODEC_REG(s, AC97_PCM_Out_Volume_Mute) >> 8, 0x1f); + lvol /= 255; + rvol = 255 - CODEC_VOL(CODEC_REG(s, AC97_Master_Volume_Mute), 0x1f); + rvol *= 255 - CODEC_VOL(CODEC_REG(s, AC97_PCM_Out_Volume_Mute), 0x1f); + rvol /= 255; + mute = CODEC_REG(s, AC97_Master_Volume_Mute) >> MUTE_SHIFT; + mute |= CODEC_REG(s, AC97_PCM_Out_Volume_Mute) >> MUTE_SHIFT; + AUD_set_volume_out(s->vo, mute, lvol, rvol); +} + +static void codec_reset(ViaAC97State *s) +{ + memset(s->codec_regs, 0, sizeof(s->codec_regs)); + CODEC_REG(s, AC97_Reset) = 0x6a90; + CODEC_REG(s, AC97_Master_Volume_Mute) = 0x8000; + CODEC_REG(s, AC97_Headphone_Volume_Mute) = 0x8000; + CODEC_REG(s, AC97_Master_Volume_Mono_Mute) = 0x8000; + CODEC_REG(s, AC97_Phone_Volume_Mute) = 0x8008; + CODEC_REG(s, AC97_Mic_Volume_Mute) = 0x8008; + CODEC_REG(s, AC97_Line_In_Volume_Mute) = 0x8808; + CODEC_REG(s, AC97_CD_Volume_Mute) = 0x8808; + CODEC_REG(s, AC97_Video_Volume_Mute) = 0x8808; + CODEC_REG(s, AC97_Aux_Volume_Mute) = 0x8808; + CODEC_REG(s, AC97_PCM_Out_Volume_Mute) = 0x8808; + CODEC_REG(s, AC97_Record_Gain_Mute) = 0x8000; + CODEC_REG(s, AC97_Powerdown_Ctrl_Stat) = 0x000f; + CODEC_REG(s, AC97_Extended_Audio_ID) = 0x0a05; + CODEC_REG(s, AC97_Extended_Audio_Ctrl_Stat) = 0x0400; + CODEC_REG(s, AC97_PCM_Front_DAC_Rate) = 48000; + CODEC_REG(s, AC97_PCM_LR_ADC_Rate) = 48000; + /* Sigmatel 9766 (STAC9766) */ + CODEC_REG(s, AC97_Vendor_ID1) = 0x8384; + CODEC_REG(s, AC97_Vendor_ID2) = 0x7666; +} + +static uint16_t codec_read(ViaAC97State *s, uint8_t addr) +{ + return CODEC_REG(s, addr); +} + +static void codec_write(ViaAC97State *s, uint8_t addr, uint16_t val) +{ + trace_via_ac97_codec_write(addr, val); + switch (addr) { + case AC97_Reset: + codec_reset(s); + return; + case AC97_Master_Volume_Mute: + case AC97_PCM_Out_Volume_Mute: + if (addr == AC97_Master_Volume_Mute) { + if (val & BIT(13)) { + val |= 0x1f00; + } + if (val & BIT(5)) { + val |= 0x1f; + } + } + CODEC_REG(s, addr) = val & 0x9f1f; + codec_volume_set_out(s); + return; + case AC97_Extended_Audio_Ctrl_Stat: + CODEC_REG(s, addr) &= ~EACS_VRA; + CODEC_REG(s, addr) |= val & EACS_VRA; + if (!(val & EACS_VRA)) { + CODEC_REG(s, AC97_PCM_Front_DAC_Rate) = 48000; + CODEC_REG(s, AC97_PCM_LR_ADC_Rate) = 48000; + open_voice_out(s); + } + return; + case AC97_PCM_Front_DAC_Rate: + case AC97_PCM_LR_ADC_Rate: + if (CODEC_REG(s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) { + int i; + uint16_t rate = val; + + for (i = 0; i < ARRAY_SIZE(codec_rates) - 1; i++) { + if (rate < codec_rates[i] + + (codec_rates[i + 1] - codec_rates[i]) / 2) { + rate = codec_rates[i]; + break; + } + } + if (rate > 48000) { + rate = 48000; + } + CODEC_REG(s, addr) = rate; + open_voice_out(s); + } + return; + case AC97_Powerdown_Ctrl_Stat: + CODEC_REG(s, addr) = (val & 0xff00) | (CODEC_REG(s, addr) & 0xff); + return; + case AC97_Extended_Audio_ID: + case AC97_Vendor_ID1: + case AC97_Vendor_ID2: + /* Read only registers */ + return; + default: + qemu_log_mask(LOG_UNIMP, + "via-ac97: Unimplemented codec register 0x%x\n", addr); + CODEC_REG(s, addr) = val; + } +} + +static void fetch_sgd(ViaAC97SGDChannel *c, PCIDevice *d) +{ + uint32_t b[2]; + + if (c->curr < c->base) { + c->curr = c->base; + } + if (unlikely(pci_dma_read(d, c->curr, b, sizeof(b)) != MEMTX_OK)) { + qemu_log_mask(LOG_GUEST_ERROR, + "via-ac97: DMA error reading SGD table\n"); + return; + } + c->addr = le32_to_cpu(b[0]); + c->clen = le32_to_cpu(b[1]); + trace_via_ac97_sgd_fetch(c->curr, c->addr, CLEN_IS_STOP(c) ? 'S' : '-', + CLEN_IS_EOL(c) ? 'E' : '-', + CLEN_IS_FLAG(c) ? 'F' : '-', CLEN_LEN(c)); +} + +static void out_cb(void *opaque, int avail) +{ + ViaAC97State *s = opaque; + ViaAC97SGDChannel *c = &s->aur; + int temp, to_copy, copied; + bool stop = false; + uint8_t tmpbuf[4096]; + + if (c->stat & STAT_PAUSED) { + return; + } + c->stat |= STAT_ACTIVE; + while (avail && !stop) { + if (!c->clen) { + fetch_sgd(c, &s->dev); + } + temp = MIN(CLEN_LEN(c), avail); + while (temp) { + to_copy = MIN(temp, sizeof(tmpbuf)); + pci_dma_read(&s->dev, c->addr, tmpbuf, to_copy); + copied = AUD_write(s->vo, tmpbuf, to_copy); + if (!copied) { + stop = true; + break; + } + temp -= copied; + avail -= copied; + c->addr += copied; + c->clen -= copied; + } + if (CLEN_LEN(c) == 0) { + c->curr += 8; + if (CLEN_IS_EOL(c)) { + c->stat |= STAT_EOL; + if (c->type & CNTL_START) { + c->curr = c->base; + c->stat |= STAT_PAUSED; + } else { + c->stat &= ~STAT_ACTIVE; + AUD_set_active_out(s->vo, 0); + } + if (c->type & STAT_EOL) { + via_isa_set_irq(&s->dev, 0, 1); + } + } + if (CLEN_IS_FLAG(c)) { + c->stat |= STAT_FLAG; + c->stat |= STAT_PAUSED; + if (c->type & STAT_FLAG) { + via_isa_set_irq(&s->dev, 0, 1); + } + } + if (CLEN_IS_STOP(c)) { + c->stat |= STAT_STOP; + c->stat |= STAT_PAUSED; + } + c->clen = 0; + stop = true; + } + } +} + +static void open_voice_out(ViaAC97State *s) +{ + struct audsettings as = { + .freq = CODEC_REG(s, AC97_PCM_Front_DAC_Rate), + .nchannels = s->aur.type & BIT(4) ? 2 : 1, + .fmt = s->aur.type & BIT(5) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_S8, + .endianness = 0, + }; + s->vo = AUD_open_out(&s->card, s->vo, "via-ac97.out", s, out_cb, &as); +} + +static uint64_t sgd_read(void *opaque, hwaddr addr, unsigned size) +{ + ViaAC97State *s = opaque; + uint64_t val = 0; + + switch (addr) { + case 0: + val = s->aur.stat; + if (s->aur.type & CNTL_START) { + val |= STAT_TRIG; + } + break; + case 1: + val = s->aur.stat & STAT_PAUSED ? BIT(3) : 0; + break; + case 2: + val = s->aur.type; + break; + case 4: + val = s->aur.curr; + break; + case 0xc: + val = CLEN_LEN(&s->aur); + break; + case 0x10: + /* silence unimplemented log message that happens at every IRQ */ + break; + case 0x80: + val = s->ac97_cmd; + break; + case 0x84: + val = s->aur.stat & STAT_FLAG; + if (s->aur.stat & STAT_EOL) { + val |= BIT(4); + } + if (s->aur.stat & STAT_STOP) { + val |= BIT(8); + } + if (s->aur.stat & STAT_ACTIVE) { + val |= BIT(12); + } + break; + default: + qemu_log_mask(LOG_UNIMP, "via-ac97: Unimplemented register read 0x%" + HWADDR_PRIx"\n", addr); + } + trace_via_ac97_sgd_read(addr, size, val); + return val; +} + +static void sgd_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + ViaAC97State *s = opaque; + + trace_via_ac97_sgd_write(addr, size, val); + switch (addr) { + case 0: + if (val & STAT_STOP) { + s->aur.stat &= ~STAT_PAUSED; + } + if (val & STAT_EOL) { + s->aur.stat &= ~(STAT_EOL | STAT_PAUSED); + if (s->aur.type & STAT_EOL) { + via_isa_set_irq(&s->dev, 0, 0); + } + } + if (val & STAT_FLAG) { + s->aur.stat &= ~(STAT_FLAG | STAT_PAUSED); + if (s->aur.type & STAT_FLAG) { + via_isa_set_irq(&s->dev, 0, 0); + } + } + break; + case 1: + if (val & CNTL_START) { + AUD_set_active_out(s->vo, 1); + s->aur.stat = STAT_ACTIVE; + } + if (val & CNTL_TERM) { + AUD_set_active_out(s->vo, 0); + s->aur.stat &= ~(STAT_ACTIVE | STAT_PAUSED); + s->aur.clen = 0; + } + if (val & CNTL_PAUSE) { + AUD_set_active_out(s->vo, 0); + s->aur.stat &= ~STAT_ACTIVE; + s->aur.stat |= STAT_PAUSED; + } else if (!(val & CNTL_PAUSE) && (s->aur.stat & STAT_PAUSED)) { + AUD_set_active_out(s->vo, 1); + s->aur.stat |= STAT_ACTIVE; + s->aur.stat &= ~STAT_PAUSED; + } + break; + case 2: + { + uint32_t oldval = s->aur.type; + s->aur.type = val; + if ((oldval & 0x30) != (val & 0x30)) { + open_voice_out(s); + } + break; + } + case 4: + s->aur.base = val & ~1ULL; + s->aur.curr = s->aur.base; + break; + case 0x80: + if (val >> 30) { + /* we only have primary codec */ + break; + } + if (val & BIT(23)) { /* read reg */ + s->ac97_cmd = val & 0xc0ff0000ULL; + s->ac97_cmd |= codec_read(s, (val >> 16) & 0x7f); + s->ac97_cmd |= BIT(25); /* data valid */ + } else { + s->ac97_cmd = val & 0xc0ffffffULL; + codec_write(s, (val >> 16) & 0x7f, val); + } + break; + case 0xc: + case 0x84: + /* Read only */ + break; + default: + qemu_log_mask(LOG_UNIMP, "via-ac97: Unimplemented register write 0x%" + HWADDR_PRIx"\n", addr); + } +} + +static const MemoryRegionOps sgd_ops = { + .read = sgd_read, + .write = sgd_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t fm_read(void *opaque, hwaddr addr, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d\n", __func__, addr, size); + return 0; +} + +static void fm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d <= 0x%"PRIX64"\n", + __func__, addr, size, val); +} + +static const MemoryRegionOps fm_ops = { + .read = fm_read, + .write = fm_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t midi_read(void *opaque, hwaddr addr, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d\n", __func__, addr, size); + return 0; +} + +static void midi_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d <= 0x%"PRIX64"\n", + __func__, addr, size, val); +} + +static const MemoryRegionOps midi_ops = { + .read = midi_read, + .write = midi_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void via_ac97_reset(DeviceState *dev) +{ + ViaAC97State *s = VIA_AC97(dev); + + codec_reset(s); +} static void via_ac97_realize(PCIDevice *pci_dev, Error **errp) { - pci_set_word(pci_dev->config + PCI_COMMAND, - PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY); + ViaAC97State *s = VIA_AC97(pci_dev); + Object *o = OBJECT(s); + + if (!AUD_register_card ("via-ac97", &s->card, errp)) { + return; + } + + /* + * Command register Bus Master bit is documented to be fixed at 0 but it's + * needed for PCI DMA to work in QEMU. The pegasos2 firmware writes 0 here + * and the AmigaOS driver writes 1 only enabling IO bit which works on + * real hardware. So set it here and fix it to 1 to allow DMA. + */ + pci_set_word(pci_dev->config + PCI_COMMAND, PCI_COMMAND_MASTER); + pci_set_word(pci_dev->wmask + PCI_COMMAND, PCI_COMMAND_IO); pci_set_word(pci_dev->config + PCI_STATUS, PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_MEDIUM); pci_set_long(pci_dev->config + PCI_INTERRUPT_PIN, 0x03); + pci_set_byte(pci_dev->config + 0x40, 1); /* codec ready */ + + memory_region_init_io(&s->sgd, o, &sgd_ops, s, "via-ac97.sgd", 256); + pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->sgd); + memory_region_init_io(&s->fm, o, &fm_ops, s, "via-ac97.fm", 4); + pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->fm); + memory_region_init_io(&s->midi, o, &midi_ops, s, "via-ac97.midi", 4); + pci_register_bar(pci_dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &s->midi); } +static void via_ac97_exit(PCIDevice *dev) +{ + ViaAC97State *s = VIA_AC97(dev); + + AUD_close_out(&s->card, s->vo); + AUD_remove_card(&s->card); +} + +static Property via_ac97_properties[] = { + DEFINE_AUDIO_PROPERTIES(ViaAC97State, card), + DEFINE_PROP_END_OF_LIST(), +}; + static void via_ac97_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->realize = via_ac97_realize; + k->exit = via_ac97_exit; k->vendor_id = PCI_VENDOR_ID_VIA; k->device_id = PCI_DEVICE_ID_VIA_AC97; k->revision = 0x50; k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO; + device_class_set_props(dc, via_ac97_properties); set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->desc = "VIA AC97"; + dc->reset = via_ac97_reset; /* Reason: Part of a south bridge chip */ dc->user_creatable = false; } @@ -41,7 +486,7 @@ static void via_ac97_class_init(ObjectClass *klass, void *data) static const TypeInfo via_ac97_info = { .name = TYPE_VIA_AC97, .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), + .instance_size = sizeof(ViaAC97State), .class_init = via_ac97_class_init, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, diff --git a/hw/audio/virtio-snd-pci.c b/hw/audio/virtio-snd-pci.c new file mode 100644 index 0000000000..ab58c6410e --- /dev/null +++ b/hw/audio/virtio-snd-pci.c @@ -0,0 +1,95 @@ +/* + * VIRTIO Sound Device PCI Bindings + * + * Copyright (c) 2023 Emmanouil Pitsidianakis + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "qom/object.h" +#include "qapi/error.h" +#include "hw/audio/soundhw.h" +#include "hw/virtio/virtio-pci.h" +#include "hw/audio/virtio-snd.h" + +/* + * virtio-snd-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_SND_PCI "virtio-sound-pci" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOSoundPCI, VIRTIO_SND_PCI) + +struct VirtIOSoundPCI { + VirtIOPCIProxy parent_obj; + + VirtIOSound vdev; +}; + +static Property virtio_snd_pci_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_snd_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VirtIOSoundPCI *dev = VIRTIO_SND_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + virtio_pci_force_virtio_1(vpci_dev); + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); +} + +static void virtio_snd_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidevklass = PCI_DEVICE_CLASS(klass); + + device_class_set_props(dc, virtio_snd_pci_properties); + dc->desc = "Virtio Sound"; + set_bit(DEVICE_CATEGORY_SOUND, dc->categories); + + vpciklass->realize = virtio_snd_pci_realize; + pcidevklass->class_id = PCI_CLASS_MULTIMEDIA_AUDIO; +} + +static void virtio_snd_pci_instance_init(Object *obj) +{ + VirtIOSoundPCI *dev = VIRTIO_SND_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_SND); +} + +static const VirtioPCIDeviceTypeInfo virtio_snd_pci_info = { + .generic_name = TYPE_VIRTIO_SND_PCI, + .instance_size = sizeof(VirtIOSoundPCI), + .instance_init = virtio_snd_pci_instance_init, + .class_init = virtio_snd_pci_class_init, +}; + +/* Create a Virtio Sound PCI device, so '-audio driver,model=virtio' works. */ +static int virtio_snd_pci_init(PCIBus *bus, const char *audiodev) +{ + DeviceState *vdev = NULL; + VirtIOSoundPCI *dev = NULL; + + vdev = qdev_new(TYPE_VIRTIO_SND_PCI); + assert(vdev); + dev = VIRTIO_SND_PCI(vdev); + qdev_prop_set_string(DEVICE(&dev->vdev), "audiodev", audiodev); + qdev_realize_and_unref(vdev, BUS(bus), &error_fatal); + return 0; +} + +static void virtio_snd_pci_register(void) +{ + virtio_pci_types_register(&virtio_snd_pci_info); + pci_register_soundhw("virtio", "Virtio Sound", virtio_snd_pci_init); +} + +type_init(virtio_snd_pci_register); diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c new file mode 100644 index 0000000000..c80b58bf5d --- /dev/null +++ b/hw/audio/virtio-snd.c @@ -0,0 +1,1411 @@ +/* + * VIRTIO Sound Device conforming to + * + * "Virtual I/O Device (VIRTIO) Version 1.2 + * Committee Specification Draft 01 + * 09 May 2022" + * + * + * + * Copyright (c) 2023 Emmanouil Pitsidianakis + * Copyright (C) 2019 OpenSynergy GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/iov.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "include/qemu/lockable.h" +#include "sysemu/runstate.h" +#include "trace.h" +#include "qapi/error.h" +#include "hw/audio/virtio-snd.h" +#include "hw/core/cpu.h" + +#define VIRTIO_SOUND_VM_VERSION 1 +#define VIRTIO_SOUND_JACK_DEFAULT 0 +#define VIRTIO_SOUND_STREAM_DEFAULT 2 +#define VIRTIO_SOUND_CHMAP_DEFAULT 0 +#define VIRTIO_SOUND_HDA_FN_NID 0 + +static void virtio_snd_pcm_out_cb(void *data, int available); +static void virtio_snd_process_cmdq(VirtIOSound *s); +static void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream); +static void virtio_snd_pcm_in_cb(void *data, int available); +static void virtio_snd_unrealize(DeviceState *dev); + +static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8) + | BIT(VIRTIO_SND_PCM_FMT_U8) + | BIT(VIRTIO_SND_PCM_FMT_S16) + | BIT(VIRTIO_SND_PCM_FMT_U16) + | BIT(VIRTIO_SND_PCM_FMT_S32) + | BIT(VIRTIO_SND_PCM_FMT_U32) + | BIT(VIRTIO_SND_PCM_FMT_FLOAT); + +static uint32_t supported_rates = BIT(VIRTIO_SND_PCM_RATE_5512) + | BIT(VIRTIO_SND_PCM_RATE_8000) + | BIT(VIRTIO_SND_PCM_RATE_11025) + | BIT(VIRTIO_SND_PCM_RATE_16000) + | BIT(VIRTIO_SND_PCM_RATE_22050) + | BIT(VIRTIO_SND_PCM_RATE_32000) + | BIT(VIRTIO_SND_PCM_RATE_44100) + | BIT(VIRTIO_SND_PCM_RATE_48000) + | BIT(VIRTIO_SND_PCM_RATE_64000) + | BIT(VIRTIO_SND_PCM_RATE_88200) + | BIT(VIRTIO_SND_PCM_RATE_96000) + | BIT(VIRTIO_SND_PCM_RATE_176400) + | BIT(VIRTIO_SND_PCM_RATE_192000) + | BIT(VIRTIO_SND_PCM_RATE_384000); + +static const VMStateDescription vmstate_virtio_snd_device = { + .name = TYPE_VIRTIO_SND, + .version_id = VIRTIO_SOUND_VM_VERSION, + .minimum_version_id = VIRTIO_SOUND_VM_VERSION, +}; + +static const VMStateDescription vmstate_virtio_snd = { + .name = TYPE_VIRTIO_SND, + .unmigratable = 1, + .minimum_version_id = VIRTIO_SOUND_VM_VERSION, + .version_id = VIRTIO_SOUND_VM_VERSION, + .fields = (const VMStateField[]) { + VMSTATE_VIRTIO_DEVICE, + VMSTATE_END_OF_LIST() + }, +}; + +static Property virtio_snd_properties[] = { + DEFINE_AUDIO_PROPERTIES(VirtIOSound, card), + DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks, + VIRTIO_SOUND_JACK_DEFAULT), + DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams, + VIRTIO_SOUND_STREAM_DEFAULT), + DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps, + VIRTIO_SOUND_CHMAP_DEFAULT), + DEFINE_PROP_END_OF_LIST(), +}; + +static void +virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config) +{ + VirtIOSound *s = VIRTIO_SND(vdev); + virtio_snd_config *sndconfig = + (virtio_snd_config *)config; + trace_virtio_snd_get_config(vdev, + s->snd_conf.jacks, + s->snd_conf.streams, + s->snd_conf.chmaps); + + memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf)); + cpu_to_le32s(&sndconfig->jacks); + cpu_to_le32s(&sndconfig->streams); + cpu_to_le32s(&sndconfig->chmaps); + +} + +static void +virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config) +{ + VirtIOSound *s = VIRTIO_SND(vdev); + const virtio_snd_config *sndconfig = + (const virtio_snd_config *)config; + + + trace_virtio_snd_set_config(vdev, + s->snd_conf.jacks, + sndconfig->jacks, + s->snd_conf.streams, + sndconfig->streams, + s->snd_conf.chmaps, + sndconfig->chmaps); + + memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config)); + le32_to_cpus(&s->snd_conf.jacks); + le32_to_cpus(&s->snd_conf.streams); + le32_to_cpus(&s->snd_conf.chmaps); + +} + +static void +virtio_snd_pcm_buffer_free(VirtIOSoundPCMBuffer *buffer) +{ + g_free(buffer->elem); + g_free(buffer); +} + +static void +virtio_snd_ctrl_cmd_free(virtio_snd_ctrl_command *cmd) +{ + g_free(cmd->elem); + g_free(cmd); +} + +/* + * Get a specific stream from the virtio sound card device. + * Returns NULL if @stream_id is invalid or not allocated. + * + * @s: VirtIOSound device + * @stream_id: stream id + */ +static VirtIOSoundPCMStream *virtio_snd_pcm_get_stream(VirtIOSound *s, + uint32_t stream_id) +{ + return stream_id >= s->snd_conf.streams ? NULL : + s->pcm->streams[stream_id]; +} + +/* + * Get params for a specific stream. + * + * @s: VirtIOSound device + * @stream_id: stream id + */ +static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s, + uint32_t stream_id) +{ + return stream_id >= s->snd_conf.streams ? NULL + : &s->pcm->pcm_params[stream_id]; +} + +/* + * Handle the VIRTIO_SND_R_PCM_INFO request. + * The function writes the info structs to the request element. + * + * @s: VirtIOSound device + * @cmd: The request command queue element from VirtIOSound cmdq field + */ +static void virtio_snd_handle_pcm_info(VirtIOSound *s, + virtio_snd_ctrl_command *cmd) +{ + uint32_t stream_id, start_id, count, size; + virtio_snd_pcm_info val; + virtio_snd_query_info req; + VirtIOSoundPCMStream *stream = NULL; + g_autofree virtio_snd_pcm_info *pcm_info = NULL; + size_t msg_sz = iov_to_buf(cmd->elem->out_sg, + cmd->elem->out_num, + 0, + &req, + sizeof(virtio_snd_query_info)); + + if (msg_sz != sizeof(virtio_snd_query_info)) { + /* + * TODO: do we need to set DEVICE_NEEDS_RESET? + */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: virtio-snd command size incorrect %zu vs \ + %zu\n", __func__, msg_sz, sizeof(virtio_snd_query_info)); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + + start_id = le32_to_cpu(req.start_id); + count = le32_to_cpu(req.count); + size = le32_to_cpu(req.size); + + if (iov_size(cmd->elem->in_sg, cmd->elem->in_num) < + sizeof(virtio_snd_hdr) + size * count) { + /* + * TODO: do we need to set DEVICE_NEEDS_RESET? + */ + error_report("pcm info: buffer too small, got: %zu, needed: %zu", + iov_size(cmd->elem->in_sg, cmd->elem->in_num), + sizeof(virtio_snd_pcm_info)); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + + pcm_info = g_new0(virtio_snd_pcm_info, count); + for (uint32_t i = 0; i < count; i++) { + stream_id = i + start_id; + trace_virtio_snd_handle_pcm_info(stream_id); + stream = virtio_snd_pcm_get_stream(s, stream_id); + if (!stream) { + error_report("Invalid stream id: %"PRIu32, stream_id); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + val = stream->info; + val.hdr.hda_fn_nid = cpu_to_le32(val.hdr.hda_fn_nid); + val.features = cpu_to_le32(val.features); + val.formats = cpu_to_le64(val.formats); + val.rates = cpu_to_le64(val.rates); + /* + * 5.14.6.6.2.1 Device Requirements: Stream Information The device MUST + * NOT set undefined feature, format, rate and direction values. The + * device MUST initialize the padding bytes to 0. + */ + pcm_info[i] = val; + memset(&pcm_info[i].padding, 0, 5); + } + + cmd->payload_size = sizeof(virtio_snd_pcm_info) * count; + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); + iov_from_buf(cmd->elem->in_sg, + cmd->elem->in_num, + sizeof(virtio_snd_hdr), + pcm_info, + cmd->payload_size); +} + +/* + * Set the given stream params. + * Called by both virtio_snd_handle_pcm_set_params and during device + * initialization. + * Returns the response status code. (VIRTIO_SND_S_*). + * + * @s: VirtIOSound device + * @params: The PCM params as defined in the virtio specification + */ +static +uint32_t virtio_snd_set_pcm_params(VirtIOSound *s, + uint32_t stream_id, + virtio_snd_pcm_set_params *params) +{ + virtio_snd_pcm_set_params *st_params; + + if (stream_id >= s->snd_conf.streams || s->pcm->pcm_params == NULL) { + /* + * TODO: do we need to set DEVICE_NEEDS_RESET? + */ + virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n"); + return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + } + + st_params = virtio_snd_pcm_get_params(s, stream_id); + + if (params->channels < 1 || params->channels > AUDIO_MAX_CHANNELS) { + error_report("Number of channels is not supported."); + return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); + } + if (!(supported_formats & BIT(params->format))) { + error_report("Stream format is not supported."); + return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); + } + if (!(supported_rates & BIT(params->rate))) { + error_report("Stream rate is not supported."); + return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); + } + + st_params->buffer_bytes = le32_to_cpu(params->buffer_bytes); + st_params->period_bytes = le32_to_cpu(params->period_bytes); + st_params->features = le32_to_cpu(params->features); + /* the following are uint8_t, so there's no need to bswap the values. */ + st_params->channels = params->channels; + st_params->format = params->format; + st_params->rate = params->rate; + + return cpu_to_le32(VIRTIO_SND_S_OK); +} + +/* + * Handles the VIRTIO_SND_R_PCM_SET_PARAMS request. + * + * @s: VirtIOSound device + * @cmd: The request command queue element from VirtIOSound cmdq field + */ +static void virtio_snd_handle_pcm_set_params(VirtIOSound *s, + virtio_snd_ctrl_command *cmd) +{ + virtio_snd_pcm_set_params req = { 0 }; + uint32_t stream_id; + size_t msg_sz = iov_to_buf(cmd->elem->out_sg, + cmd->elem->out_num, + 0, + &req, + sizeof(virtio_snd_pcm_set_params)); + + if (msg_sz != sizeof(virtio_snd_pcm_set_params)) { + /* + * TODO: do we need to set DEVICE_NEEDS_RESET? + */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: virtio-snd command size incorrect %zu vs \ + %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_set_params)); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + stream_id = le32_to_cpu(req.hdr.stream_id); + trace_virtio_snd_handle_pcm_set_params(stream_id); + cmd->resp.code = virtio_snd_set_pcm_params(s, stream_id, &req); +} + +/* + * Get a QEMU Audiosystem compatible format value from a VIRTIO_SND_PCM_FMT_* + */ +static AudioFormat virtio_snd_get_qemu_format(uint32_t format) +{ + #define CASE(FMT) \ + case VIRTIO_SND_PCM_FMT_##FMT: \ + return AUDIO_FORMAT_##FMT; + + switch (format) { + CASE(U8) + CASE(S8) + CASE(U16) + CASE(S16) + CASE(U32) + CASE(S32) + case VIRTIO_SND_PCM_FMT_FLOAT: + return AUDIO_FORMAT_F32; + default: + g_assert_not_reached(); + } + + #undef CASE +} + +/* + * Get a QEMU Audiosystem compatible frequency value from a + * VIRTIO_SND_PCM_RATE_* + */ +static uint32_t virtio_snd_get_qemu_freq(uint32_t rate) +{ + #define CASE(RATE) \ + case VIRTIO_SND_PCM_RATE_##RATE: \ + return RATE; + + switch (rate) { + CASE(5512) + CASE(8000) + CASE(11025) + CASE(16000) + CASE(22050) + CASE(32000) + CASE(44100) + CASE(48000) + CASE(64000) + CASE(88200) + CASE(96000) + CASE(176400) + CASE(192000) + CASE(384000) + default: + g_assert_not_reached(); + } + + #undef CASE +} + +/* + * Get QEMU Audiosystem compatible audsettings from virtio based pcm stream + * params. + */ +static void virtio_snd_get_qemu_audsettings(audsettings *as, + virtio_snd_pcm_set_params *params) +{ + as->nchannels = MIN(AUDIO_MAX_CHANNELS, params->channels); + as->fmt = virtio_snd_get_qemu_format(params->format); + as->freq = virtio_snd_get_qemu_freq(params->rate); + as->endianness = target_words_bigendian() ? 1 : 0; +} + +/* + * Close a stream and free all its resources. + * + * @stream: VirtIOSoundPCMStream *stream + */ +static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream) +{ + if (stream) { + virtio_snd_pcm_flush(stream); + if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { + AUD_close_out(&stream->pcm->snd->card, stream->voice.out); + stream->voice.out = NULL; + } else if (stream->info.direction == VIRTIO_SND_D_INPUT) { + AUD_close_in(&stream->pcm->snd->card, stream->voice.in); + stream->voice.in = NULL; + } + } +} + +/* + * Prepares a VirtIOSound card stream. + * Returns the response status code. (VIRTIO_SND_S_*). + * + * @s: VirtIOSound device + * @stream_id: stream id + */ +static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id) +{ + audsettings as; + virtio_snd_pcm_set_params *params; + VirtIOSoundPCMStream *stream; + + if (s->pcm->streams == NULL || + s->pcm->pcm_params == NULL || + stream_id >= s->snd_conf.streams) { + return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + } + + params = virtio_snd_pcm_get_params(s, stream_id); + if (params == NULL) { + return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + } + + stream = virtio_snd_pcm_get_stream(s, stream_id); + if (stream == NULL) { + stream = g_new0(VirtIOSoundPCMStream, 1); + stream->active = false; + stream->id = stream_id; + stream->pcm = s->pcm; + stream->s = s; + qemu_mutex_init(&stream->queue_mutex); + QSIMPLEQ_INIT(&stream->queue); + + /* + * stream_id >= s->snd_conf.streams was checked before so this is + * in-bounds + */ + s->pcm->streams[stream_id] = stream; + } + + virtio_snd_get_qemu_audsettings(&as, params); + stream->info.direction = stream_id < s->snd_conf.streams / 2 + + (s->snd_conf.streams & 1) ? VIRTIO_SND_D_OUTPUT : VIRTIO_SND_D_INPUT; + stream->info.hdr.hda_fn_nid = VIRTIO_SOUND_HDA_FN_NID; + stream->info.features = 0; + stream->info.channels_min = 1; + stream->info.channels_max = as.nchannels; + stream->info.formats = supported_formats; + stream->info.rates = supported_rates; + stream->params = *params; + + stream->positions[0] = VIRTIO_SND_CHMAP_FL; + stream->positions[1] = VIRTIO_SND_CHMAP_FR; + stream->as = as; + + if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { + stream->voice.out = AUD_open_out(&s->card, + stream->voice.out, + "virtio-sound.out", + stream, + virtio_snd_pcm_out_cb, + &as); + AUD_set_volume_out(stream->voice.out, 0, 255, 255); + } else { + stream->voice.in = AUD_open_in(&s->card, + stream->voice.in, + "virtio-sound.in", + stream, + virtio_snd_pcm_in_cb, + &as); + AUD_set_volume_in(stream->voice.in, 0, 255, 255); + } + + return cpu_to_le32(VIRTIO_SND_S_OK); +} + +static const char *print_code(uint32_t code) +{ + #define CASE(CODE) \ + case VIRTIO_SND_R_##CODE: \ + return "VIRTIO_SND_R_"#CODE + + switch (code) { + CASE(JACK_INFO); + CASE(JACK_REMAP); + CASE(PCM_INFO); + CASE(PCM_SET_PARAMS); + CASE(PCM_PREPARE); + CASE(PCM_RELEASE); + CASE(PCM_START); + CASE(PCM_STOP); + CASE(CHMAP_INFO); + default: + return "invalid code"; + } + + #undef CASE +}; + +/* + * Handles VIRTIO_SND_R_PCM_PREPARE. + * + * @s: VirtIOSound device + * @cmd: The request command queue element from VirtIOSound cmdq field + */ +static void virtio_snd_handle_pcm_prepare(VirtIOSound *s, + virtio_snd_ctrl_command *cmd) +{ + uint32_t stream_id; + size_t msg_sz = iov_to_buf(cmd->elem->out_sg, + cmd->elem->out_num, + sizeof(virtio_snd_hdr), + &stream_id, + sizeof(stream_id)); + + stream_id = le32_to_cpu(stream_id); + cmd->resp.code = msg_sz == sizeof(stream_id) + ? virtio_snd_pcm_prepare(s, stream_id) + : cpu_to_le32(VIRTIO_SND_S_BAD_MSG); +} + +/* + * Handles VIRTIO_SND_R_PCM_START. + * + * @s: VirtIOSound device + * @cmd: The request command queue element from VirtIOSound cmdq field + * @start: whether to start or stop the device + */ +static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s, + virtio_snd_ctrl_command *cmd, + bool start) +{ + VirtIOSoundPCMStream *stream; + virtio_snd_pcm_hdr req; + uint32_t stream_id; + size_t msg_sz = iov_to_buf(cmd->elem->out_sg, + cmd->elem->out_num, + 0, + &req, + sizeof(virtio_snd_pcm_hdr)); + + if (msg_sz != sizeof(virtio_snd_pcm_hdr)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: virtio-snd command size incorrect %zu vs \ + %zu\n", __func__, msg_sz, sizeof(virtio_snd_pcm_hdr)); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + + stream_id = le32_to_cpu(req.stream_id); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); + trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" : + "VIRTIO_SND_R_PCM_STOP", stream_id); + + stream = virtio_snd_pcm_get_stream(s, stream_id); + if (stream) { + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + stream->active = start; + } + if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { + AUD_set_active_out(stream->voice.out, start); + } else { + AUD_set_active_in(stream->voice.in, start); + } + } else { + error_report("Invalid stream id: %"PRIu32, stream_id); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + stream->active = start; +} + +/* + * Returns the number of I/O messages that are being processed. + * + * @stream: VirtIOSoundPCMStream + */ +static size_t virtio_snd_pcm_get_io_msgs_count(VirtIOSoundPCMStream *stream) +{ + VirtIOSoundPCMBuffer *buffer, *next; + size_t count = 0; + + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + QSIMPLEQ_FOREACH_SAFE(buffer, &stream->queue, entry, next) { + count += 1; + } + } + return count; +} + +/* + * Handles VIRTIO_SND_R_PCM_RELEASE. + * + * @s: VirtIOSound device + * @cmd: The request command queue element from VirtIOSound cmdq field + */ +static void virtio_snd_handle_pcm_release(VirtIOSound *s, + virtio_snd_ctrl_command *cmd) +{ + uint32_t stream_id; + VirtIOSoundPCMStream *stream; + size_t msg_sz = iov_to_buf(cmd->elem->out_sg, + cmd->elem->out_num, + sizeof(virtio_snd_hdr), + &stream_id, + sizeof(stream_id)); + + if (msg_sz != sizeof(stream_id)) { + /* + * TODO: do we need to set DEVICE_NEEDS_RESET? + */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: virtio-snd command size incorrect %zu vs \ + %zu\n", __func__, msg_sz, sizeof(stream_id)); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + + stream_id = le32_to_cpu(stream_id); + trace_virtio_snd_handle_pcm_release(stream_id); + stream = virtio_snd_pcm_get_stream(s, stream_id); + if (stream == NULL) { + /* + * TODO: do we need to set DEVICE_NEEDS_RESET? + */ + error_report("already released stream %"PRIu32, stream_id); + virtio_error(VIRTIO_DEVICE(s), + "already released stream %"PRIu32, + stream_id); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + return; + } + + if (virtio_snd_pcm_get_io_msgs_count(stream)) { + /* + * virtio-v1.2-csd01, 5.14.6.6.5.1, + * Device Requirements: Stream Release + * + * - The device MUST complete all pending I/O messages for the + * specified stream ID. + * - The device MUST NOT complete the control request while there + * are pending I/O messages for the specified stream ID. + */ + trace_virtio_snd_pcm_stream_flush(stream_id); + virtio_snd_pcm_flush(stream); + } + + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); +} + +/* + * The actual processing done in virtio_snd_process_cmdq(). + * + * @s: VirtIOSound device + * @cmd: control command request + */ +static inline void +process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd) +{ + uint32_t code; + size_t msg_sz = iov_to_buf(cmd->elem->out_sg, + cmd->elem->out_num, + 0, + &cmd->ctrl, + sizeof(virtio_snd_hdr)); + + if (msg_sz != sizeof(virtio_snd_hdr)) { + /* + * TODO: do we need to set DEVICE_NEEDS_RESET? + */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: virtio-snd command size incorrect %zu vs \ + %zu\n", __func__, msg_sz, sizeof(virtio_snd_hdr)); + return; + } + + code = le32_to_cpu(cmd->ctrl.code); + + trace_virtio_snd_handle_code(code, print_code(code)); + + switch (code) { + case VIRTIO_SND_R_JACK_INFO: + case VIRTIO_SND_R_JACK_REMAP: + qemu_log_mask(LOG_UNIMP, + "virtio_snd: jack functionality is unimplemented.\n"); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); + break; + case VIRTIO_SND_R_PCM_INFO: + virtio_snd_handle_pcm_info(s, cmd); + break; + case VIRTIO_SND_R_PCM_START: + virtio_snd_handle_pcm_start_stop(s, cmd, true); + break; + case VIRTIO_SND_R_PCM_STOP: + virtio_snd_handle_pcm_start_stop(s, cmd, false); + break; + case VIRTIO_SND_R_PCM_SET_PARAMS: + virtio_snd_handle_pcm_set_params(s, cmd); + break; + case VIRTIO_SND_R_PCM_PREPARE: + virtio_snd_handle_pcm_prepare(s, cmd); + break; + case VIRTIO_SND_R_PCM_RELEASE: + virtio_snd_handle_pcm_release(s, cmd); + break; + case VIRTIO_SND_R_CHMAP_INFO: + qemu_log_mask(LOG_UNIMP, + "virtio_snd: chmap info functionality is unimplemented.\n"); + trace_virtio_snd_handle_chmap_info(); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); + break; + default: + /* error */ + error_report("virtio snd header not recognized: %"PRIu32, code); + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + } + + iov_from_buf(cmd->elem->in_sg, + cmd->elem->in_num, + 0, + &cmd->resp, + sizeof(virtio_snd_hdr)); + virtqueue_push(cmd->vq, cmd->elem, + sizeof(virtio_snd_hdr) + cmd->payload_size); + virtio_notify(VIRTIO_DEVICE(s), cmd->vq); +} + +/* + * Consume all elements in command queue. + * + * @s: VirtIOSound device + */ +static void virtio_snd_process_cmdq(VirtIOSound *s) +{ + virtio_snd_ctrl_command *cmd; + + if (unlikely(qatomic_read(&s->processing_cmdq))) { + return; + } + + WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) { + qatomic_set(&s->processing_cmdq, true); + while (!QTAILQ_EMPTY(&s->cmdq)) { + cmd = QTAILQ_FIRST(&s->cmdq); + + /* process command */ + process_cmd(s, cmd); + + QTAILQ_REMOVE(&s->cmdq, cmd, next); + + virtio_snd_ctrl_cmd_free(cmd); + } + qatomic_set(&s->processing_cmdq, false); + } +} + +/* + * The control message handler. Pops an element from the control virtqueue, + * and stores them to VirtIOSound's cmdq queue and finally calls + * virtio_snd_process_cmdq() for processing. + * + * @vdev: VirtIOSound device + * @vq: Control virtqueue + */ +static void virtio_snd_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOSound *s = VIRTIO_SND(vdev); + VirtQueueElement *elem; + virtio_snd_ctrl_command *cmd; + + trace_virtio_snd_handle_ctrl(vdev, vq); + + if (!virtio_queue_ready(vq)) { + return; + } + + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + while (elem) { + cmd = g_new0(virtio_snd_ctrl_command, 1); + cmd->elem = elem; + cmd->vq = vq; + cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK); + /* implicit cmd->payload_size = 0; */ + QTAILQ_INSERT_TAIL(&s->cmdq, cmd, next); + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + } + + virtio_snd_process_cmdq(s); +} + +/* + * The event virtqueue handler. + * Not implemented yet. + * + * @vdev: VirtIOSound device + * @vq: event vq + */ +static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq) +{ + qemu_log_mask(LOG_UNIMP, "virtio_snd: event queue is unimplemented.\n"); + trace_virtio_snd_handle_event(); +} + +/* + * Must only be called if vsnd->invalid is not empty. + */ +static inline void empty_invalid_queue(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOSoundPCMBuffer *buffer = NULL; + virtio_snd_pcm_status resp = { 0 }; + VirtIOSound *vsnd = VIRTIO_SND(vdev); + + g_assert(!QSIMPLEQ_EMPTY(&vsnd->invalid)); + + while (!QSIMPLEQ_EMPTY(&vsnd->invalid)) { + buffer = QSIMPLEQ_FIRST(&vsnd->invalid); + /* If buffer->vq != vq, our logic is fundamentally wrong, so bail out */ + g_assert(buffer->vq == vq); + + resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG); + iov_from_buf(buffer->elem->in_sg, + buffer->elem->in_num, + 0, + &resp, + sizeof(virtio_snd_pcm_status)); + virtqueue_push(vq, + buffer->elem, + sizeof(virtio_snd_pcm_status)); + QSIMPLEQ_REMOVE_HEAD(&vsnd->invalid, entry); + virtio_snd_pcm_buffer_free(buffer); + } + /* Notify vq about virtio_snd_pcm_status responses. */ + virtio_notify(vdev, vq); +} + +/* + * The tx virtqueue handler. Makes the buffers available to their respective + * streams for consumption. + * + * @vdev: VirtIOSound device + * @vq: tx virtqueue + */ +static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOSound *vsnd = VIRTIO_SND(vdev); + VirtIOSoundPCMBuffer *buffer; + VirtQueueElement *elem; + size_t msg_sz, size; + virtio_snd_pcm_xfer hdr; + uint32_t stream_id; + /* + * If any of the I/O messages are invalid, put them in vsnd->invalid and + * return them after the for loop. + */ + bool must_empty_invalid_queue = false; + + if (!virtio_queue_ready(vq)) { + return; + } + trace_virtio_snd_handle_tx_xfer(); + + for (;;) { + VirtIOSoundPCMStream *stream; + + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + /* get the message hdr object */ + msg_sz = iov_to_buf(elem->out_sg, + elem->out_num, + 0, + &hdr, + sizeof(virtio_snd_pcm_xfer)); + if (msg_sz != sizeof(virtio_snd_pcm_xfer)) { + goto tx_err; + } + stream_id = le32_to_cpu(hdr.stream_id); + + if (stream_id >= vsnd->snd_conf.streams + || vsnd->pcm->streams[stream_id] == NULL) { + goto tx_err; + } + + stream = vsnd->pcm->streams[stream_id]; + if (stream->info.direction != VIRTIO_SND_D_OUTPUT) { + goto tx_err; + } + + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + size = iov_size(elem->out_sg, elem->out_num) - msg_sz; + + buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); + buffer->elem = elem; + buffer->populated = false; + buffer->vq = vq; + buffer->size = size; + buffer->offset = 0; + + QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry); + } + continue; + +tx_err: + must_empty_invalid_queue = true; + buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer)); + buffer->elem = elem; + buffer->vq = vq; + QSIMPLEQ_INSERT_TAIL(&vsnd->invalid, buffer, entry); + } + + if (must_empty_invalid_queue) { + empty_invalid_queue(vdev, vq); + } +} + +/* + * The rx virtqueue handler. Makes the buffers available to their respective + * streams for consumption. + * + * @vdev: VirtIOSound device + * @vq: rx virtqueue + */ +static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOSound *vsnd = VIRTIO_SND(vdev); + VirtIOSoundPCMBuffer *buffer; + VirtQueueElement *elem; + size_t msg_sz, size; + virtio_snd_pcm_xfer hdr; + uint32_t stream_id; + /* + * if any of the I/O messages are invalid, put them in vsnd->invalid and + * return them after the for loop. + */ + bool must_empty_invalid_queue = false; + + if (!virtio_queue_ready(vq)) { + return; + } + trace_virtio_snd_handle_rx_xfer(); + + for (;;) { + VirtIOSoundPCMStream *stream; + + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + /* get the message hdr object */ + msg_sz = iov_to_buf(elem->out_sg, + elem->out_num, + 0, + &hdr, + sizeof(virtio_snd_pcm_xfer)); + if (msg_sz != sizeof(virtio_snd_pcm_xfer)) { + goto rx_err; + } + stream_id = le32_to_cpu(hdr.stream_id); + + if (stream_id >= vsnd->snd_conf.streams + || !vsnd->pcm->streams[stream_id]) { + goto rx_err; + } + + stream = vsnd->pcm->streams[stream_id]; + if (stream == NULL || stream->info.direction != VIRTIO_SND_D_INPUT) { + goto rx_err; + } + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + size = iov_size(elem->in_sg, elem->in_num) - + sizeof(virtio_snd_pcm_status); + buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); + buffer->elem = elem; + buffer->vq = vq; + buffer->size = 0; + buffer->offset = 0; + QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry); + } + continue; + +rx_err: + must_empty_invalid_queue = true; + buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer)); + buffer->elem = elem; + buffer->vq = vq; + QSIMPLEQ_INSERT_TAIL(&vsnd->invalid, buffer, entry); + } + + if (must_empty_invalid_queue) { + empty_invalid_queue(vdev, vq); + } +} + +static uint64_t get_features(VirtIODevice *vdev, uint64_t features, + Error **errp) +{ + /* + * virtio-v1.2-csd01, 5.14.3, + * Feature Bits + * None currently defined. + */ + VirtIOSound *s = VIRTIO_SND(vdev); + features |= s->features; + + trace_virtio_snd_get_features(vdev, features); + + return features; +} + +static void +virtio_snd_vm_state_change(void *opaque, bool running, + RunState state) +{ + if (running) { + trace_virtio_snd_vm_state_running(); + } else { + trace_virtio_snd_vm_state_stopped(); + } +} + +static void virtio_snd_realize(DeviceState *dev, Error **errp) +{ + ERRP_GUARD(); + VirtIOSound *vsnd = VIRTIO_SND(dev); + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + virtio_snd_pcm_set_params default_params = { 0 }; + uint32_t status; + + trace_virtio_snd_realize(vsnd); + + /* check number of jacks and streams */ + if (vsnd->snd_conf.jacks > 8) { + error_setg(errp, + "Invalid number of jacks: %"PRIu32, + vsnd->snd_conf.jacks); + return; + } + if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) { + error_setg(errp, + "Invalid number of streams: %"PRIu32, + vsnd->snd_conf.streams); + return; + } + + if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) { + error_setg(errp, + "Invalid number of channel maps: %"PRIu32, + vsnd->snd_conf.chmaps); + return; + } + + if (!AUD_register_card("virtio-sound", &vsnd->card, errp)) { + return; + } + + vsnd->vmstate = + qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd); + + vsnd->pcm = g_new0(VirtIOSoundPCM, 1); + vsnd->pcm->snd = vsnd; + vsnd->pcm->streams = + g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams); + vsnd->pcm->pcm_params = + g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams); + + virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config)); + virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1); + + /* set default params for all streams */ + default_params.features = 0; + default_params.buffer_bytes = cpu_to_le32(8192); + default_params.period_bytes = cpu_to_le32(2048); + default_params.channels = 2; + default_params.format = VIRTIO_SND_PCM_FMT_S16; + default_params.rate = VIRTIO_SND_PCM_RATE_48000; + vsnd->queues[VIRTIO_SND_VQ_CONTROL] = + virtio_add_queue(vdev, 64, virtio_snd_handle_ctrl); + vsnd->queues[VIRTIO_SND_VQ_EVENT] = + virtio_add_queue(vdev, 64, virtio_snd_handle_event); + vsnd->queues[VIRTIO_SND_VQ_TX] = + virtio_add_queue(vdev, 64, virtio_snd_handle_tx_xfer); + vsnd->queues[VIRTIO_SND_VQ_RX] = + virtio_add_queue(vdev, 64, virtio_snd_handle_rx_xfer); + qemu_mutex_init(&vsnd->cmdq_mutex); + QTAILQ_INIT(&vsnd->cmdq); + QSIMPLEQ_INIT(&vsnd->invalid); + + for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) { + status = virtio_snd_set_pcm_params(vsnd, i, &default_params); + if (status != cpu_to_le32(VIRTIO_SND_S_OK)) { + error_setg(errp, + "Can't initialize stream params, device responded with %s.", + print_code(status)); + goto error_cleanup; + } + status = virtio_snd_pcm_prepare(vsnd, i); + if (status != cpu_to_le32(VIRTIO_SND_S_OK)) { + error_setg(errp, + "Can't prepare streams, device responded with %s.", + print_code(status)); + goto error_cleanup; + } + } + + return; + +error_cleanup: + virtio_snd_unrealize(dev); +} + +static inline void return_tx_buffer(VirtIOSoundPCMStream *stream, + VirtIOSoundPCMBuffer *buffer) +{ + virtio_snd_pcm_status resp = { 0 }; + resp.status = cpu_to_le32(VIRTIO_SND_S_OK); + resp.latency_bytes = cpu_to_le32((uint32_t)buffer->size); + iov_from_buf(buffer->elem->in_sg, + buffer->elem->in_num, + 0, + &resp, + sizeof(virtio_snd_pcm_status)); + virtqueue_push(buffer->vq, + buffer->elem, + sizeof(virtio_snd_pcm_status)); + virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq); + QSIMPLEQ_REMOVE(&stream->queue, + buffer, + VirtIOSoundPCMBuffer, + entry); + virtio_snd_pcm_buffer_free(buffer); +} + +/* + * AUD_* output callback. + * + * @data: VirtIOSoundPCMStream stream + * @available: number of bytes that can be written with AUD_write() + */ +static void virtio_snd_pcm_out_cb(void *data, int available) +{ + VirtIOSoundPCMStream *stream = data; + VirtIOSoundPCMBuffer *buffer; + size_t size; + + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + while (!QSIMPLEQ_EMPTY(&stream->queue)) { + buffer = QSIMPLEQ_FIRST(&stream->queue); + if (!virtio_queue_ready(buffer->vq)) { + return; + } + if (!stream->active) { + /* Stream has stopped, so do not perform AUD_write. */ + return_tx_buffer(stream, buffer); + continue; + } + if (!buffer->populated) { + iov_to_buf(buffer->elem->out_sg, + buffer->elem->out_num, + sizeof(virtio_snd_pcm_xfer), + buffer->data, + buffer->size); + buffer->populated = true; + } + for (;;) { + size = AUD_write(stream->voice.out, + buffer->data + buffer->offset, + MIN(buffer->size, available)); + assert(size <= MIN(buffer->size, available)); + if (size == 0) { + /* break out of both loops */ + available = 0; + break; + } + buffer->size -= size; + buffer->offset += size; + available -= size; + if (buffer->size < 1) { + return_tx_buffer(stream, buffer); + break; + } + if (!available) { + break; + } + } + if (!available) { + break; + } + } + } +} + +/* + * Flush all buffer data from this input stream's queue into the driver's + * virtual queue. + * + * @stream: VirtIOSoundPCMStream *stream + */ +static inline void return_rx_buffer(VirtIOSoundPCMStream *stream, + VirtIOSoundPCMBuffer *buffer) +{ + virtio_snd_pcm_status resp = { 0 }; + resp.status = cpu_to_le32(VIRTIO_SND_S_OK); + resp.latency_bytes = 0; + /* Copy data -if any- to guest */ + iov_from_buf(buffer->elem->in_sg, + buffer->elem->in_num, + 0, + buffer->data, + buffer->size); + iov_from_buf(buffer->elem->in_sg, + buffer->elem->in_num, + buffer->size, + &resp, + sizeof(virtio_snd_pcm_status)); + virtqueue_push(buffer->vq, + buffer->elem, + sizeof(virtio_snd_pcm_status) + buffer->size); + virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq); + QSIMPLEQ_REMOVE(&stream->queue, + buffer, + VirtIOSoundPCMBuffer, + entry); + virtio_snd_pcm_buffer_free(buffer); +} + + +/* + * AUD_* input callback. + * + * @data: VirtIOSoundPCMStream stream + * @available: number of bytes that can be read with AUD_read() + */ +static void virtio_snd_pcm_in_cb(void *data, int available) +{ + VirtIOSoundPCMStream *stream = data; + VirtIOSoundPCMBuffer *buffer; + size_t size; + + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + while (!QSIMPLEQ_EMPTY(&stream->queue)) { + buffer = QSIMPLEQ_FIRST(&stream->queue); + if (!virtio_queue_ready(buffer->vq)) { + return; + } + if (!stream->active) { + /* Stream has stopped, so do not perform AUD_read. */ + return_rx_buffer(stream, buffer); + continue; + } + + for (;;) { + size = AUD_read(stream->voice.in, + buffer->data + buffer->size, + MIN(available, (stream->params.period_bytes - + buffer->size))); + if (!size) { + available = 0; + break; + } + buffer->size += size; + available -= size; + if (buffer->size >= stream->params.period_bytes) { + return_rx_buffer(stream, buffer); + break; + } + if (!available) { + break; + } + } + if (!available) { + break; + } + } + } +} + +/* + * Flush all buffer data from this output stream's queue into the driver's + * virtual queue. + * + * @stream: VirtIOSoundPCMStream *stream + */ +static inline void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream) +{ + VirtIOSoundPCMBuffer *buffer; + void (*cb)(VirtIOSoundPCMStream *, VirtIOSoundPCMBuffer *) = + (stream->info.direction == VIRTIO_SND_D_OUTPUT) ? return_tx_buffer : + return_rx_buffer; + + WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { + while (!QSIMPLEQ_EMPTY(&stream->queue)) { + buffer = QSIMPLEQ_FIRST(&stream->queue); + cb(stream, buffer); + } + } +} + +static void virtio_snd_unrealize(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtIOSound *vsnd = VIRTIO_SND(dev); + VirtIOSoundPCMStream *stream; + + qemu_del_vm_change_state_handler(vsnd->vmstate); + trace_virtio_snd_unrealize(vsnd); + + if (vsnd->pcm) { + if (vsnd->pcm->streams) { + for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) { + stream = vsnd->pcm->streams[i]; + if (stream) { + virtio_snd_process_cmdq(stream->s); + virtio_snd_pcm_close(stream); + qemu_mutex_destroy(&stream->queue_mutex); + g_free(stream); + } + } + g_free(vsnd->pcm->streams); + } + g_free(vsnd->pcm->pcm_params); + g_free(vsnd->pcm); + vsnd->pcm = NULL; + } + AUD_remove_card(&vsnd->card); + qemu_mutex_destroy(&vsnd->cmdq_mutex); + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]); + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]); + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]); + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]); + virtio_cleanup(vdev); +} + + +static void virtio_snd_reset(VirtIODevice *vdev) +{ + VirtIOSound *vsnd = VIRTIO_SND(vdev); + virtio_snd_ctrl_command *cmd; + + /* + * Sanity check that the invalid buffer message queue is emptied at the end + * of every virtio_snd_handle_tx_xfer/virtio_snd_handle_rx_xfer call, and + * must be empty otherwise. + */ + g_assert(QSIMPLEQ_EMPTY(&vsnd->invalid)); + + WITH_QEMU_LOCK_GUARD(&vsnd->cmdq_mutex) { + while (!QTAILQ_EMPTY(&vsnd->cmdq)) { + cmd = QTAILQ_FIRST(&vsnd->cmdq); + QTAILQ_REMOVE(&vsnd->cmdq, cmd, next); + virtio_snd_ctrl_cmd_free(cmd); + } + } +} + +static void virtio_snd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + + + set_bit(DEVICE_CATEGORY_SOUND, dc->categories); + device_class_set_props(dc, virtio_snd_properties); + + dc->vmsd = &vmstate_virtio_snd; + vdc->vmsd = &vmstate_virtio_snd_device; + vdc->realize = virtio_snd_realize; + vdc->unrealize = virtio_snd_unrealize; + vdc->get_config = virtio_snd_get_config; + vdc->set_config = virtio_snd_set_config; + vdc->get_features = get_features; + vdc->reset = virtio_snd_reset; + vdc->legacy_features = 0; +} + +static const TypeInfo virtio_snd_types[] = { + { + .name = TYPE_VIRTIO_SND, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VirtIOSound), + .class_init = virtio_snd_class_init, + } +}; + +DEFINE_TYPES(virtio_snd_types) diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c index b5722b37c3..ec2c4e1374 100644 --- a/hw/audio/wm8750.c +++ b/hw/audio/wm8750.c @@ -592,7 +592,7 @@ static const VMStateDescription vmstate_wm8750 = { .minimum_version_id = 0, .pre_save = wm8750_pre_save, .post_load = wm8750_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(i2c_data, WM8750State, 2), VMSTATE_INT32(i2c_len, WM8750State), VMSTATE_INT32(enable, WM8750State), @@ -624,7 +624,10 @@ static void wm8750_realize(DeviceState *dev, Error **errp) { WM8750State *s = WM8750(dev); - AUD_register_card(CODEC, &s->card); + if (!AUD_register_card(CODEC, &s->card, errp)) { + return; + } + wm8750_reset(I2C_SLAVE(s)); } diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c index a34803e642..31c8992d75 100644 --- a/hw/avr/atmega.c +++ b/hw/avr/atmega.c @@ -233,6 +233,10 @@ static void atmega_realize(DeviceState *dev, Error **errp) /* CPU */ object_initialize_child(OBJECT(dev), "cpu", &s->cpu, mc->cpu_type); + + object_property_set_uint(OBJECT(&s->cpu), "init-sp", + mc->io_size + mc->sram_size - 1, &error_abort); + qdev_realize(DEVICE(&s->cpu), NULL, &error_abort); cpudev = DEVICE(&s->cpu); diff --git a/hw/block/block.c b/hw/block/block.c index 25f45df723..3ceca7dce6 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -8,41 +8,76 @@ */ #include "qemu/osdep.h" +#include "block/block_int-common.h" #include "sysemu/blockdev.h" #include "sysemu/block-backend.h" #include "hw/block/block.h" #include "qapi/error.h" #include "qapi/qapi-types-block.h" +/* + * Read the non-zeroes parts of @blk into @buf + * Reading all of the @blk is expensive if the zeroes parts of @blk + * is large enough. Therefore check the block status and only write + * the non-zeroes block into @buf. + * + * Return 0 on success, non-zero on error. + */ +static int blk_pread_nonzeroes(BlockBackend *blk, hwaddr size, void *buf) +{ + int ret; + int64_t bytes, offset = 0; + BlockDriverState *bs = blk_bs(blk); + + for (;;) { + bytes = MIN(size - offset, BDRV_REQUEST_MAX_BYTES); + if (bytes <= 0) { + return 0; + } + ret = bdrv_block_status(bs, offset, bytes, &bytes, NULL, NULL); + if (ret < 0) { + return ret; + } + if (!(ret & BDRV_BLOCK_ZERO)) { + ret = blk_pread(blk, offset, bytes, (uint8_t *) buf + offset, 0); + if (ret < 0) { + return ret; + } + } + offset += bytes; + } +} + /* * Read the entire contents of @blk into @buf. * @blk's contents must be @size bytes, and @size must be at most * BDRV_REQUEST_MAX_BYTES. * On success, return true. * On failure, store an error through @errp and return false. - * Note that the error messages do not identify the block backend. - * TODO Since callers don't either, this can result in confusing - * errors. + * * This function not intended for actual block devices, which read on * demand. It's for things like memory devices that (ab)use a block * backend to provide persistence. */ -bool blk_check_size_and_read_all(BlockBackend *blk, void *buf, hwaddr size, - Error **errp) +bool blk_check_size_and_read_all(BlockBackend *blk, DeviceState *dev, + void *buf, hwaddr size, Error **errp) { int64_t blk_len; int ret; + g_autofree char *dev_id = NULL; blk_len = blk_getlength(blk); if (blk_len < 0) { error_setg_errno(errp, -blk_len, - "can't get size of block backend"); + "can't get size of %s block backend", blk_name(blk)); return false; } if (blk_len != size) { - error_setg(errp, "device requires %" HWADDR_PRIu " bytes, " - "block backend provides %" PRIu64 " bytes", - size, blk_len); + dev_id = qdev_get_human_name(dev); + error_setg(errp, "%s device '%s' requires %" HWADDR_PRIu + " bytes, %s block backend provides %" PRIu64 " bytes", + object_get_typename(OBJECT(dev)), dev_id, size, + blk_name(blk), blk_len); return false; } @@ -53,9 +88,13 @@ bool blk_check_size_and_read_all(BlockBackend *blk, void *buf, hwaddr size, * block device and read only on demand. */ assert(size <= BDRV_REQUEST_MAX_BYTES); - ret = blk_pread(blk, 0, buf, size); + ret = blk_pread_nonzeroes(blk, size, buf); if (ret < 0) { - error_setg_errno(errp, -ret, "can't read block backend"); + dev_id = qdev_get_human_name(dev); + error_setg_errno(errp, -ret, "can't read %s block backend" + " for %s device '%s'", + blk_name(blk), object_get_typename(OBJECT(dev)), + dev_id); return false; } return true; @@ -205,6 +244,8 @@ bool blkconf_apply_backend_options(BlockConf *conf, bool readonly, blk_set_enable_write_cache(blk, wce); blk_set_on_error(blk, rerror, werror); + block_acct_setup(blk_get_stats(blk), conf->account_invalid, + conf->account_failed); return true; } diff --git a/hw/block/dataplane/meson.build b/hw/block/dataplane/meson.build index 12c6a264f1..11a5eba2f4 100644 --- a/hw/block/dataplane/meson.build +++ b/hw/block/dataplane/meson.build @@ -1,2 +1 @@ -specific_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c')) -specific_ss.add(when: 'CONFIG_XEN', if_true: files('xen-block.c')) +specific_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-block.c')) diff --git a/hw/block/dataplane/trace-events b/hw/block/dataplane/trace-events deleted file mode 100644 index 38fc3e7507..0000000000 --- a/hw/block/dataplane/trace-events +++ /dev/null @@ -1,5 +0,0 @@ -# See docs/devel/tracing.rst for syntax documentation. - -# virtio-blk.c -virtio_blk_data_plane_start(void *s) "dataplane %p" -virtio_blk_data_plane_stop(void *s) "dataplane %p" diff --git a/hw/block/dataplane/trace.h b/hw/block/dataplane/trace.h deleted file mode 100644 index 240cc59834..0000000000 --- a/hw/block/dataplane/trace.h +++ /dev/null @@ -1 +0,0 @@ -#include "trace/trace-hw_block_dataplane.h" diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c deleted file mode 100644 index 49276e46f2..0000000000 --- a/hw/block/dataplane/virtio-blk.c +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Dedicated thread for virtio-blk I/O processing - * - * Copyright 2012 IBM, Corp. - * Copyright 2012 Red Hat, Inc. and/or its affiliates - * - * Authors: - * Stefan Hajnoczi - * - * 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/error.h" -#include "trace.h" -#include "qemu/iov.h" -#include "qemu/main-loop.h" -#include "qemu/thread.h" -#include "qemu/error-report.h" -#include "hw/virtio/virtio-access.h" -#include "hw/virtio/virtio-blk.h" -#include "virtio-blk.h" -#include "block/aio.h" -#include "hw/virtio/virtio-bus.h" -#include "qom/object_interfaces.h" - -struct VirtIOBlockDataPlane { - bool starting; - bool stopping; - - VirtIOBlkConf *conf; - VirtIODevice *vdev; - QEMUBH *bh; /* bh for guest notification */ - unsigned long *batch_notify_vqs; - bool batch_notifications; - - /* Note that these EventNotifiers are assigned by value. This is - * fine as long as you do not call event_notifier_cleanup on them - * (because you don't own the file descriptor or handle; you just - * use it). - */ - IOThread *iothread; - AioContext *ctx; -}; - -/* Raise an interrupt to signal guest, if necessary */ -void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq) -{ - if (s->batch_notifications) { - set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs); - qemu_bh_schedule(s->bh); - } else { - virtio_notify_irqfd(s->vdev, vq); - } -} - -static void notify_guest_bh(void *opaque) -{ - VirtIOBlockDataPlane *s = opaque; - unsigned nvqs = s->conf->num_queues; - unsigned long bitmap[BITS_TO_LONGS(nvqs)]; - unsigned j; - - memcpy(bitmap, s->batch_notify_vqs, sizeof(bitmap)); - memset(s->batch_notify_vqs, 0, sizeof(bitmap)); - - for (j = 0; j < nvqs; j += BITS_PER_LONG) { - unsigned long bits = bitmap[j / BITS_PER_LONG]; - - while (bits != 0) { - unsigned i = j + ctzl(bits); - VirtQueue *vq = virtio_get_queue(s->vdev, i); - - virtio_notify_irqfd(s->vdev, vq); - - bits &= bits - 1; /* clear right-most bit */ - } - } -} - -/* Context: QEMU global mutex held */ -bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, - VirtIOBlockDataPlane **dataplane, - Error **errp) -{ - VirtIOBlockDataPlane *s; - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - - *dataplane = NULL; - - if (conf->iothread) { - if (!k->set_guest_notifiers || !k->ioeventfd_assign) { - error_setg(errp, - "device is incompatible with iothread " - "(transport does not support notifiers)"); - return false; - } - if (!virtio_device_ioeventfd_enabled(vdev)) { - error_setg(errp, "ioeventfd is required for iothread"); - return false; - } - - /* If dataplane is (re-)enabled while the guest is running there could - * be block jobs that can conflict. - */ - if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { - error_prepend(errp, "cannot start virtio-blk dataplane: "); - return false; - } - } - /* Don't try if transport does not support notifiers. */ - if (!virtio_device_ioeventfd_enabled(vdev)) { - return false; - } - - s = g_new0(VirtIOBlockDataPlane, 1); - s->vdev = vdev; - s->conf = conf; - - if (conf->iothread) { - s->iothread = conf->iothread; - object_ref(OBJECT(s->iothread)); - s->ctx = iothread_get_aio_context(s->iothread); - } else { - s->ctx = qemu_get_aio_context(); - } - s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); - s->batch_notify_vqs = bitmap_new(conf->num_queues); - - *dataplane = s; - - return true; -} - -/* Context: QEMU global mutex held */ -void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s) -{ - VirtIOBlock *vblk; - - if (!s) { - return; - } - - vblk = VIRTIO_BLK(s->vdev); - assert(!vblk->dataplane_started); - g_free(s->batch_notify_vqs); - qemu_bh_delete(s->bh); - if (s->iothread) { - object_unref(OBJECT(s->iothread)); - } - g_free(s); -} - -/* Context: QEMU global mutex held */ -int virtio_blk_data_plane_start(VirtIODevice *vdev) -{ - VirtIOBlock *vblk = VIRTIO_BLK(vdev); - VirtIOBlockDataPlane *s = vblk->dataplane; - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vblk))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - AioContext *old_context; - unsigned i; - unsigned nvqs = s->conf->num_queues; - Error *local_err = NULL; - int r; - - if (vblk->dataplane_started || s->starting) { - return 0; - } - - s->starting = true; - - if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { - s->batch_notifications = true; - } else { - s->batch_notifications = false; - } - - /* Set up guest notifier (irq) */ - r = k->set_guest_notifiers(qbus->parent, nvqs, true); - if (r != 0) { - error_report("virtio-blk failed to set guest notifier (%d), " - "ensure -accel kvm is set.", r); - goto fail_guest_notifiers; - } - - /* - * Batch all the host notifiers in a single transaction to avoid - * quadratic time complexity in address_space_update_ioeventfds(). - */ - memory_region_transaction_begin(); - - /* Set up virtqueue notify */ - for (i = 0; i < nvqs; i++) { - r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, true); - if (r != 0) { - int j = i; - - fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r); - while (i--) { - virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); - } - - /* - * The transaction expects the ioeventfds to be open when it - * commits. Do it now, before the cleanup loop. - */ - memory_region_transaction_commit(); - - while (j--) { - virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), j); - } - goto fail_host_notifiers; - } - } - - memory_region_transaction_commit(); - - s->starting = false; - vblk->dataplane_started = true; - trace_virtio_blk_data_plane_start(s); - - old_context = blk_get_aio_context(s->conf->conf.blk); - aio_context_acquire(old_context); - r = blk_set_aio_context(s->conf->conf.blk, s->ctx, &local_err); - aio_context_release(old_context); - if (r < 0) { - error_report_err(local_err); - goto fail_aio_context; - } - - /* Process queued requests before the ones in vring */ - virtio_blk_process_queued_requests(vblk, false); - - /* Kick right away to begin processing requests already in vring */ - for (i = 0; i < nvqs; i++) { - VirtQueue *vq = virtio_get_queue(s->vdev, i); - - event_notifier_set(virtio_queue_get_host_notifier(vq)); - } - - /* Get this show started by hooking up our callbacks */ - aio_context_acquire(s->ctx); - for (i = 0; i < nvqs; i++) { - VirtQueue *vq = virtio_get_queue(s->vdev, i); - - virtio_queue_aio_attach_host_notifier(vq, s->ctx); - } - aio_context_release(s->ctx); - return 0; - - fail_aio_context: - memory_region_transaction_begin(); - - for (i = 0; i < nvqs; i++) { - virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); - } - - memory_region_transaction_commit(); - - for (i = 0; i < nvqs; i++) { - virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i); - } - fail_host_notifiers: - k->set_guest_notifiers(qbus->parent, nvqs, false); - fail_guest_notifiers: - /* - * If we failed to set up the guest notifiers queued requests will be - * processed on the main context. - */ - virtio_blk_process_queued_requests(vblk, false); - vblk->dataplane_disabled = true; - s->starting = false; - vblk->dataplane_started = true; - return -ENOSYS; -} - -/* Stop notifications for new requests from guest. - * - * Context: BH in IOThread - */ -static void virtio_blk_data_plane_stop_bh(void *opaque) -{ - VirtIOBlockDataPlane *s = opaque; - unsigned i; - - for (i = 0; i < s->conf->num_queues; i++) { - VirtQueue *vq = virtio_get_queue(s->vdev, i); - - virtio_queue_aio_detach_host_notifier(vq, s->ctx); - } -} - -/* Context: QEMU global mutex held */ -void virtio_blk_data_plane_stop(VirtIODevice *vdev) -{ - VirtIOBlock *vblk = VIRTIO_BLK(vdev); - VirtIOBlockDataPlane *s = vblk->dataplane; - BusState *qbus = qdev_get_parent_bus(DEVICE(vblk)); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - unsigned i; - unsigned nvqs = s->conf->num_queues; - - if (!vblk->dataplane_started || s->stopping) { - return; - } - - /* Better luck next time. */ - if (vblk->dataplane_disabled) { - vblk->dataplane_disabled = false; - vblk->dataplane_started = false; - return; - } - s->stopping = true; - trace_virtio_blk_data_plane_stop(s); - - aio_context_acquire(s->ctx); - aio_wait_bh_oneshot(s->ctx, virtio_blk_data_plane_stop_bh, s); - - /* Drain and try to switch bs back to the QEMU main loop. If other users - * keep the BlockBackend in the iothread, that's ok */ - blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context(), NULL); - - aio_context_release(s->ctx); - - /* - * Batch all the host notifiers in a single transaction to avoid - * quadratic time complexity in address_space_update_ioeventfds(). - */ - memory_region_transaction_begin(); - - for (i = 0; i < nvqs; i++) { - virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); - } - - /* - * The transaction expects the ioeventfds to be open when it - * commits. Do it now, before the cleanup loop. - */ - memory_region_transaction_commit(); - - for (i = 0; i < nvqs; i++) { - virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i); - } - - qemu_bh_cancel(s->bh); - notify_guest_bh(s); /* final chance to notify guest */ - - /* Clean up guest notifier (irq) */ - k->set_guest_notifiers(qbus->parent, nvqs, false); - - vblk->dataplane_started = false; - s->stopping = false; -} diff --git a/hw/block/dataplane/virtio-blk.h b/hw/block/dataplane/virtio-blk.h deleted file mode 100644 index 5e18bb99ae..0000000000 --- a/hw/block/dataplane/virtio-blk.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Dedicated thread for virtio-blk I/O processing - * - * Copyright 2012 IBM, Corp. - * Copyright 2012 Red Hat, Inc. and/or its affiliates - * - * Authors: - * Stefan Hajnoczi - * - * 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_DATAPLANE_VIRTIO_BLK_H -#define HW_DATAPLANE_VIRTIO_BLK_H - -#include "hw/virtio/virtio.h" - -typedef struct VirtIOBlockDataPlane VirtIOBlockDataPlane; - -bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, - VirtIOBlockDataPlane **dataplane, - Error **errp); -void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s); -void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq); - -int virtio_blk_data_plane_start(VirtIODevice *vdev); -void virtio_blk_data_plane_stop(VirtIODevice *vdev); - -#endif /* HW_DATAPLANE_VIRTIO_BLK_H */ diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c index 2785b9e849..98501e6885 100644 --- a/hw/block/dataplane/xen-block.c +++ b/hw/block/dataplane/xen-block.c @@ -19,12 +19,14 @@ */ #include "qemu/osdep.h" +#include "qemu/defer-call.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/memalign.h" #include "qapi/error.h" -#include "hw/xen/xen_common.h" +#include "hw/xen/xen.h" #include "hw/block/xen_blkif.h" +#include "hw/xen/interface/io/ring.h" #include "sysemu/block-backend.h" #include "sysemu/iothread.h" #include "xen-block.h" @@ -101,9 +103,9 @@ static XenBlockRequest *xen_block_start_request(XenBlockDataPlane *dataplane) * re-use requests, allocate the memory once here. It will be freed * xen_block_dataplane_destroy() when the request list is freed. */ - request->buf = qemu_memalign(XC_PAGE_SIZE, + request->buf = qemu_memalign(XEN_PAGE_SIZE, BLKIF_MAX_SEGMENTS_PER_REQUEST * - XC_PAGE_SIZE); + XEN_PAGE_SIZE); dataplane->requests_total++; qemu_iovec_init(&request->v, 1); } else { @@ -185,7 +187,7 @@ static int xen_block_parse_request(XenBlockRequest *request) goto err; } if (request->req.seg[i].last_sect * dataplane->sector_size >= - XC_PAGE_SIZE) { + XEN_PAGE_SIZE) { error_report("error: page crossing"); goto err; } @@ -258,8 +260,6 @@ static void xen_block_complete_aio(void *opaque, int ret) XenBlockRequest *request = opaque; XenBlockDataPlane *dataplane = request->dataplane; - aio_context_acquire(dataplane->ctx); - if (ret != 0) { error_report("%s I/O error", request->req.operation == BLKIF_OP_READ ? @@ -271,10 +271,10 @@ static void xen_block_complete_aio(void *opaque, int ret) if (request->presync) { request->presync = 0; xen_block_do_aio(request); - goto done; + return; } if (request->aio_inflight > 0) { - goto done; + return; } switch (request->req.operation) { @@ -316,9 +316,6 @@ static void xen_block_complete_aio(void *opaque, int ret) if (dataplane->more_work) { qemu_bh_schedule(dataplane->bh); } - -done: - aio_context_release(dataplane->ctx); } static bool xen_block_split_discard(XenBlockRequest *request, @@ -508,7 +505,7 @@ static int xen_block_get_request(XenBlockDataPlane *dataplane, /* * Threshold of in-flight requests above which we will start using - * blk_io_plug()/blk_io_unplug() to batch requests. + * defer_call_begin()/defer_call_end() to batch requests. */ #define IO_PLUG_THRESHOLD 1 @@ -536,7 +533,7 @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane) * is below us. */ if (inflight_atstart > IO_PLUG_THRESHOLD) { - blk_io_plug(dataplane->blk); + defer_call_begin(); } while (rc != rp) { /* pull request from ring */ @@ -576,12 +573,12 @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane) if (inflight_atstart > IO_PLUG_THRESHOLD && batched >= inflight_atstart) { - blk_io_unplug(dataplane->blk); + defer_call_end(); } xen_block_do_aio(request); if (inflight_atstart > IO_PLUG_THRESHOLD) { if (batched >= inflight_atstart) { - blk_io_plug(dataplane->blk); + defer_call_begin(); batched = 0; } else { batched++; @@ -589,7 +586,7 @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane) } } if (inflight_atstart > IO_PLUG_THRESHOLD) { - blk_io_unplug(dataplane->blk); + defer_call_end(); } return done_something; @@ -599,9 +596,7 @@ static void xen_block_dataplane_bh(void *opaque) { XenBlockDataPlane *dataplane = opaque; - aio_context_acquire(dataplane->ctx); xen_block_handle_requests(dataplane); - aio_context_release(dataplane->ctx); } static bool xen_block_dataplane_event(void *opaque) @@ -632,8 +627,9 @@ XenBlockDataPlane *xen_block_dataplane_create(XenDevice *xendev, } else { dataplane->ctx = qemu_get_aio_context(); } - dataplane->bh = aio_bh_new(dataplane->ctx, xen_block_dataplane_bh, - dataplane); + dataplane->bh = aio_bh_new_guarded(dataplane->ctx, xen_block_dataplane_bh, + dataplane, + &DEVICE(xendev)->mem_reentrancy_guard); return dataplane; } @@ -662,6 +658,30 @@ void xen_block_dataplane_destroy(XenBlockDataPlane *dataplane) g_free(dataplane); } +void xen_block_dataplane_detach(XenBlockDataPlane *dataplane) +{ + if (!dataplane || !dataplane->event_channel) { + return; + } + + /* Only reason for failure is a NULL channel */ + xen_device_set_event_channel_context(dataplane->xendev, + dataplane->event_channel, + NULL, &error_abort); +} + +void xen_block_dataplane_attach(XenBlockDataPlane *dataplane) +{ + if (!dataplane || !dataplane->event_channel) { + return; + } + + /* Only reason for failure is a NULL channel */ + xen_device_set_event_channel_context(dataplane->xendev, + dataplane->event_channel, + dataplane->ctx, &error_abort); +} + void xen_block_dataplane_stop(XenBlockDataPlane *dataplane) { XenDevice *xendev; @@ -672,16 +692,12 @@ void xen_block_dataplane_stop(XenBlockDataPlane *dataplane) 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); + if (!blk_in_drain(dataplane->blk)) { + xen_block_dataplane_detach(dataplane); } + /* 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); /* * Now that the context has been moved onto the main thread, cancel @@ -705,6 +721,7 @@ void xen_block_dataplane_stop(XenBlockDataPlane *dataplane) Error *local_err = NULL; xen_device_unmap_grant_refs(xendev, dataplane->sring, + dataplane->ring_ref, dataplane->nr_ring_ref, &local_err); dataplane->sring = NULL; @@ -726,7 +743,6 @@ void xen_block_dataplane_start(XenBlockDataPlane *dataplane, { ERRP_GUARD(); XenDevice *xendev = dataplane->xendev; - AioContext *old_context; unsigned int ring_size; unsigned int i; @@ -739,7 +755,7 @@ void xen_block_dataplane_start(XenBlockDataPlane *dataplane, dataplane->protocol = protocol; - ring_size = XC_PAGE_SIZE * dataplane->nr_ring_ref; + ring_size = XEN_PAGE_SIZE * dataplane->nr_ring_ref; switch (dataplane->protocol) { case BLKIF_PROTOCOL_NATIVE: { @@ -810,17 +826,12 @@ void xen_block_dataplane_start(XenBlockDataPlane *dataplane, goto stop; } - old_context = blk_get_aio_context(dataplane->blk); - aio_context_acquire(old_context); /* If other users keep the BlockBackend in the iothread, that's ok */ blk_set_aio_context(dataplane->blk, dataplane->ctx, NULL); - aio_context_release(old_context); - /* Only reason for failure is a NULL channel */ - aio_context_acquire(dataplane->ctx); - xen_device_set_event_channel_context(xendev, dataplane->event_channel, - dataplane->ctx, &error_abort); - aio_context_release(dataplane->ctx); + if (!blk_in_drain(dataplane->blk)) { + xen_block_dataplane_attach(dataplane); + } return; diff --git a/hw/block/dataplane/xen-block.h b/hw/block/dataplane/xen-block.h index 76dcd51c3d..7b8e9df09f 100644 --- a/hw/block/dataplane/xen-block.h +++ b/hw/block/dataplane/xen-block.h @@ -26,5 +26,7 @@ void xen_block_dataplane_start(XenBlockDataPlane *dataplane, unsigned int protocol, Error **errp); void xen_block_dataplane_stop(XenBlockDataPlane *dataplane); +void xen_block_dataplane_attach(XenBlockDataPlane *dataplane); +void xen_block_dataplane_detach(XenBlockDataPlane *dataplane); #endif /* HW_BLOCK_DATAPLANE_XEN_BLOCK_H */ diff --git a/hw/block/ecc.c b/hw/block/ecc.c index 6e0d63842c..ed889a4184 100644 --- a/hw/block/ecc.c +++ b/hw/block/ecc.c @@ -82,7 +82,7 @@ const VMStateDescription vmstate_ecc_state = { .name = "ecc-state", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(cp, ECCState), VMSTATE_UINT16_ARRAY(lp, ECCState, 2), VMSTATE_UINT16(count, ECCState), diff --git a/hw/block/fdc-internal.h b/hw/block/fdc-internal.h index 036392e9fc..e219623dc7 100644 --- a/hw/block/fdc-internal.h +++ b/hw/block/fdc-internal.h @@ -25,8 +25,6 @@ #ifndef HW_BLOCK_FDC_INTERNAL_H #define HW_BLOCK_FDC_INTERNAL_H -#include "exec/memory.h" -#include "exec/ioport.h" #include "hw/block/block.h" #include "hw/block/fdc.h" #include "qapi/qapi-types-block.h" @@ -92,7 +90,6 @@ typedef struct FDrive { } FDrive; struct FDCtrl { - MemoryRegion iomem; qemu_irq irq; /* Controller state */ QEMUTimer *result_timer; @@ -140,7 +137,6 @@ struct FDCtrl { /* Timers state */ uint8_t timer0; uint8_t timer1; - PortioList portio_list; }; extern const FDFormat fd_formats[]; diff --git a/hw/block/fdc-isa.c b/hw/block/fdc-isa.c index fa20450747..e43dc532af 100644 --- a/hw/block/fdc-isa.c +++ b/hw/block/fdc-isa.c @@ -32,7 +32,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/timer.h" -#include "hw/acpi/aml-build.h" +#include "hw/acpi/acpi_aml_interface.h" #include "hw/irq.h" #include "hw/isa/isa.h" #include "hw/qdev-properties.h" @@ -42,6 +42,7 @@ #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" +#include "exec/ioport.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/module.h" @@ -60,6 +61,7 @@ struct FDCtrlISABus { uint32_t irq; uint32_t dma; struct FDCtrl state; + PortioList portio_list; int32_t bootindexA; int32_t bootindexB; }; @@ -86,19 +88,20 @@ static const MemoryRegionPortio fdc_portio_list[] = { static void isabus_fdc_realize(DeviceState *dev, Error **errp) { ISADevice *isadev = ISA_DEVICE(dev); + ISABus *bus = isa_bus_from_device(isadev); FDCtrlISABus *isa = ISA_FDC(dev); FDCtrl *fdctrl = &isa->state; Error *err = NULL; - isa_register_portio_list(isadev, &fdctrl->portio_list, + isa_register_portio_list(isadev, &isa->portio_list, isa->iobase, fdc_portio_list, fdctrl, "fdc"); - fdctrl->irq = isa_get_irq(isadev, isa->irq); + fdctrl->irq = isa_bus_get_irq(bus, isa->irq); fdctrl->dma_chann = isa->dma; if (fdctrl->dma_chann != -1) { IsaDmaClass *k; - fdctrl->dma = isa_get_dma(isa_bus_from_device(isadev), isa->dma); + fdctrl->dma = isa_bus_get_dma(bus, isa->dma); if (!fdctrl->dma) { error_setg(errp, "ISA controller does not support DMA"); return; @@ -189,6 +192,20 @@ static Aml *build_fdinfo_aml(int idx, FloppyDriveType type) return dev; } +void isa_fdc_set_iobase(ISADevice *fdc, hwaddr iobase) +{ + FDCtrlISABus *isa = ISA_FDC(fdc); + + fdc->ioport_id = iobase; + isa->iobase = iobase; + portio_list_set_address(&isa->portio_list, isa->iobase); +} + +void isa_fdc_set_enabled(ISADevice *fdc, bool enabled) +{ + portio_list_set_enabled(&ISA_FDC(fdc)->portio_list, enabled); +} + int cmos_get_fd_drive_type(FloppyDriveType fd0) { int val; @@ -214,9 +231,9 @@ int cmos_get_fd_drive_type(FloppyDriveType fd0) return val; } -static void fdc_isa_build_aml(ISADevice *isadev, Aml *scope) +static void build_fdc_aml(AcpiDevAmlIf *adev, Aml *scope) { - FDCtrlISABus *isa = ISA_FDC(isadev); + FDCtrlISABus *isa = ISA_FDC(adev); Aml *dev; Aml *crs; int i; @@ -241,7 +258,7 @@ static void fdc_isa_build_aml(ISADevice *isadev, Aml *scope) aml_append(dev, aml_name_decl("_CRS", crs)); for (i = 0; i < MIN(MAX_FD, ACPI_FDE_MAX_FD); i++) { - FloppyDriveType type = isa_fdc_get_drive_type(isadev, i); + FloppyDriveType type = isa_fdc_get_drive_type(ISA_DEVICE(adev), i); if (type < FLOPPY_DRIVE_TYPE_NONE) { fde_buf[i] = cpu_to_le32(1); /* drive present */ @@ -258,7 +275,7 @@ static const VMStateDescription vmstate_isa_fdc = { .name = "fdc", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl), VMSTATE_END_OF_LIST() } @@ -283,14 +300,14 @@ static Property isa_fdc_properties[] = { static void isabus_fdc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - ISADeviceClass *isa = ISA_DEVICE_CLASS(klass); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); dc->desc = "virtual floppy controller"; dc->realize = isabus_fdc_realize; dc->fw_name = "fdc"; dc->reset = fdctrl_external_reset_isa; dc->vmsd = &vmstate_isa_fdc; - isa->build_aml = fdc_isa_build_aml; + adevc->build_dev_aml = build_fdc_aml; device_class_set_props(dc, isa_fdc_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } @@ -313,6 +330,10 @@ static const TypeInfo isa_fdc_info = { .instance_size = sizeof(FDCtrlISABus), .class_init = isabus_fdc_class_init, .instance_init = isabus_fdc_instance_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_ACPI_DEV_AML_IF }, + { }, + }, }; static void isa_fdc_register_types(void) diff --git a/hw/block/fdc-sysbus.c b/hw/block/fdc-sysbus.c index 57fc8773f1..035bc08975 100644 --- a/hw/block/fdc-sysbus.c +++ b/hw/block/fdc-sysbus.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qom/object.h" +#include "exec/memory.h" #include "hw/sysbus.h" #include "hw/block/fdc.h" #include "migration/vmstate.h" @@ -52,6 +53,7 @@ struct FDCtrlSysBus { /*< public >*/ struct FDCtrl state; + MemoryRegion iomem; }; static uint64_t fdctrl_read_mem(void *opaque, hwaddr reg, unsigned ize) @@ -94,18 +96,14 @@ static void fdctrl_handle_tc(void *opaque, int irq, int level) trace_fdctrl_tc_pulse(level); } -void fdctrl_init_sysbus(qemu_irq irq, int dma_chann, - hwaddr mmio_base, DriveInfo **fds) +void fdctrl_init_sysbus(qemu_irq irq, hwaddr mmio_base, DriveInfo **fds) { - FDCtrl *fdctrl; DeviceState *dev; SysBusDevice *sbd; FDCtrlSysBus *sys; dev = qdev_new("sysbus-fdc"); sys = SYSBUS_FDC(dev); - fdctrl = &sys->state; - fdctrl->dma_chann = dma_chann; /* FIXME */ sbd = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(sbd, &error_fatal); sysbus_connect_irq(sbd, 0, irq); @@ -138,13 +136,23 @@ static void sysbus_fdc_common_instance_init(Object *obj) FDCtrlSysBus *sys = SYSBUS_FDC(obj); FDCtrl *fdctrl = &sys->state; + /* + * DMA is not currently supported for sysbus floppy controllers. + * If we wanted to add support then probably the best approach is + * to have a QOM link property 'dma-controller' which the board + * code can set to an instance of IsaDmaClass, and an integer + * property 'dma-channel', so that we can set fdctrl->dma and + * fdctrl->dma_chann accordingly. + */ + fdctrl->dma_chann = -1; + qdev_set_legacy_instance_id(dev, 0 /* io */, 2); /* FIXME */ - memory_region_init_io(&fdctrl->iomem, obj, + memory_region_init_io(&sys->iomem, obj, sbdc->use_strict_io ? &fdctrl_mem_strict_ops : &fdctrl_mem_ops, fdctrl, "fdc", 0x08); - sysbus_init_mmio(sbd, &fdctrl->iomem); + sysbus_init_mmio(sbd, &sys->iomem); sysbus_init_irq(sbd, &fdctrl->irq); qdev_init_gpio_in(dev, fdctrl_handle_tc, 1); @@ -162,7 +170,7 @@ static const VMStateDescription vmstate_sysbus_fdc = { .name = "fdc", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(state, FDCtrlSysBus, 0, vmstate_fdc, FDCtrl), VMSTATE_END_OF_LIST() } diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 57bb355794..6dd94e98bc 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -601,8 +601,8 @@ enum { }; enum { - FD_STATE_MULTI = 0x01, /* multi track flag */ - FD_STATE_FORMAT = 0x02, /* format flag */ + FD_STATE_MULTI = 0x01, /* multi track flag */ + FD_STATE_FORMAT = 0x02, /* format flag */ }; enum { @@ -854,7 +854,7 @@ static const VMStateDescription vmstate_fdrive_media_changed = { .version_id = 1, .minimum_version_id = 1, .needed = fdrive_media_changed_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(media_changed, FDrive), VMSTATE_END_OF_LIST() } @@ -864,7 +864,7 @@ static const VMStateDescription vmstate_fdrive_media_rate = { .name = "fdrive/media_rate", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(media_rate, FDrive), VMSTATE_END_OF_LIST() } @@ -882,7 +882,7 @@ static const VMStateDescription vmstate_fdrive_perpendicular = { .version_id = 1, .minimum_version_id = 1, .needed = fdrive_perpendicular_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(perpendicular, FDrive), VMSTATE_END_OF_LIST() } @@ -899,13 +899,13 @@ static const VMStateDescription vmstate_fdrive = { .version_id = 1, .minimum_version_id = 1, .post_load = fdrive_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(head, FDrive), VMSTATE_UINT8(track, FDrive), VMSTATE_UINT8(sect, FDrive), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_fdrive_media_changed, &vmstate_fdrive_media_rate, &vmstate_fdrive_perpendicular, @@ -977,7 +977,7 @@ static const VMStateDescription vmstate_fdc_reset_sensei = { .version_id = 1, .minimum_version_id = 1, .needed = fdc_reset_sensei_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(reset_sensei, FDCtrl), VMSTATE_END_OF_LIST() } @@ -995,7 +995,7 @@ static const VMStateDescription vmstate_fdc_result_timer = { .version_id = 1, .minimum_version_id = 1, .needed = fdc_result_timer_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(result_timer, FDCtrl), VMSTATE_END_OF_LIST() } @@ -1013,7 +1013,7 @@ static const VMStateDescription vmstate_fdc_phase = { .version_id = 1, .minimum_version_id = 1, .needed = fdc_phase_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(phase, FDCtrl), VMSTATE_END_OF_LIST() } @@ -1026,7 +1026,7 @@ const VMStateDescription vmstate_fdc = { .pre_save = fdc_pre_save, .pre_load = fdc_pre_load, .post_load = fdc_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { /* Controller State */ VMSTATE_UINT8(sra, FDCtrl), VMSTATE_UINT8(srb, FDCtrl), @@ -1057,7 +1057,7 @@ const VMStateDescription vmstate_fdc = { vmstate_fdrive, FDrive), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_fdc_reset_sensei, &vmstate_fdc_result_timer, &vmstate_fdc_phase, @@ -1628,8 +1628,8 @@ int fdctrl_transfer_handler(void *opaque, int nchan, int dma_pos, int dma_len) if (fdctrl->data_dir != FD_DIR_WRITE || len < FD_SECTOR_LEN || rel_pos != 0) { /* READ & SCAN commands and realign to a sector for WRITE */ - if (blk_pread(cur_drv->blk, fd_offset(cur_drv), - fdctrl->fifo, BDRV_SECTOR_SIZE) < 0) { + if (blk_pread(cur_drv->blk, fd_offset(cur_drv), BDRV_SECTOR_SIZE, + fdctrl->fifo, 0) < 0) { FLOPPY_DPRINTF("Floppy: error getting sector %d\n", fd_sector(cur_drv)); /* Sure, image size is too small... */ @@ -1656,8 +1656,8 @@ int fdctrl_transfer_handler(void *opaque, int nchan, int dma_pos, int dma_len) k->read_memory(fdctrl->dma, nchan, fdctrl->fifo + rel_pos, fdctrl->data_pos, len); - if (blk_pwrite(cur_drv->blk, fd_offset(cur_drv), - fdctrl->fifo, BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(cur_drv->blk, fd_offset(cur_drv), BDRV_SECTOR_SIZE, + fdctrl->fifo, 0) < 0) { FLOPPY_DPRINTF("error writing sector %d\n", fd_sector(cur_drv)); fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); @@ -1740,8 +1740,8 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl) fd_sector(cur_drv)); return 0; } - if (blk_pread(cur_drv->blk, fd_offset(cur_drv), fdctrl->fifo, - BDRV_SECTOR_SIZE) + if (blk_pread(cur_drv->blk, fd_offset(cur_drv), BDRV_SECTOR_SIZE, + fdctrl->fifo, 0) < 0) { FLOPPY_DPRINTF("error getting sector %d\n", fd_sector(cur_drv)); @@ -1820,8 +1820,8 @@ static void fdctrl_format_sector(FDCtrl *fdctrl) } memset(fdctrl->fifo, 0, FD_SECTOR_LEN); if (cur_drv->blk == NULL || - blk_pwrite(cur_drv->blk, fd_offset(cur_drv), fdctrl->fifo, - BDRV_SECTOR_SIZE, 0) < 0) { + blk_pwrite(cur_drv->blk, fd_offset(cur_drv), BDRV_SECTOR_SIZE, + fdctrl->fifo, 0) < 0) { FLOPPY_DPRINTF("error formatting sector %d\n", fd_sector(cur_drv)); fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); } else { @@ -2244,8 +2244,8 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) if (pos == FD_SECTOR_LEN - 1 || fdctrl->data_pos == fdctrl->data_len) { cur_drv = get_cur_drv(fdctrl); - if (blk_pwrite(cur_drv->blk, fd_offset(cur_drv), fdctrl->fifo, - BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(cur_drv->blk, fd_offset(cur_drv), BDRV_SECTOR_SIZE, + fdctrl->fifo, 0) < 0) { FLOPPY_DPRINTF("error writing sector %d\n", fd_sector(cur_drv)); break; diff --git a/hw/block/hd-geometry.c b/hw/block/hd-geometry.c index dcbccee294..2b0af4430f 100644 --- a/hw/block/hd-geometry.c +++ b/hw/block/hd-geometry.c @@ -50,7 +50,7 @@ struct partition { uint32_t nr_sects; /* nr of sectors in partition */ } QEMU_PACKED; -/* try to guess the disk logical geometry from the MSDOS partition table. +/* try to guess the disk logical geometry from the MS-DOS partition table. Return 0 if OK, -1 if could not guess */ static int guess_disk_lchs(BlockBackend *blk, int *pcylinders, int *pheads, int *psectors) @@ -63,10 +63,10 @@ static int guess_disk_lchs(BlockBackend *blk, blk_get_geometry(blk, &nb_sectors); - if (blk_pread(blk, 0, buf, BDRV_SECTOR_SIZE) < 0) { + if (blk_pread(blk, 0, BDRV_SECTOR_SIZE, buf, 0) < 0) { return -1; } - /* test msdos magic */ + /* test MS-DOS magic */ if (buf[510] != 0x55 || buf[511] != 0xaa) { return -1; } @@ -150,7 +150,12 @@ void hd_geometry_guess(BlockBackend *blk, translation = BIOS_ATA_TRANSLATION_NONE; } if (ptrans) { - *ptrans = translation; + if (*ptrans == BIOS_ATA_TRANSLATION_AUTO) { + *ptrans = translation; + } else { + /* Defer to the translation specified by the user. */ + translation = *ptrans; + } } trace_hd_geometry_guess(blk, *pcyls, *pheads, *psecs, translation); } diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 81ba3da4df..8dec134832 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -24,6 +24,8 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "sysemu/block-backend.h" +#include "hw/block/block.h" +#include "hw/block/flash.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "hw/ssi/ssi.h" @@ -35,22 +37,21 @@ #include "qapi/error.h" #include "trace.h" #include "qom/object.h" - -/* Fields for FlashPartInfo->flags */ - -/* erase capabilities */ -#define ER_4K 1 -#define ER_32K 2 -/* set to allow the page program command to write 0s back to 1. Useful for - * modelling EEPROM with SPI flash command set - */ -#define EEPROM 0x100 +#include "m25p80_sfdp.h" /* 16 MiB max in 3 byte address mode */ #define MAX_3BYTES_SIZE 0x1000000 - #define SPI_NOR_MAX_ID_LEN 6 +/* Fields for FlashPartInfo->flags */ +enum spi_flash_option_flags { + ER_4K = BIT(0), + ER_32K = BIT(1), + EEPROM = BIT(2), + HAS_SR_TB = BIT(3), + HAS_SR_BP3_BIT6 = BIT(4), +}; + typedef struct FlashPartInfo { const char *part_name; /* @@ -74,6 +75,7 @@ typedef struct FlashPartInfo { * This field inform how many die is in the chip. */ uint8_t die_cnt; + uint8_t (*sfdp_read)(uint32_t sfdp_addr); } FlashPartInfo; /* adapted from linux */ @@ -221,7 +223,8 @@ static const FlashPartInfo known_devices[] = { { INFO("is25wp032", 0x9d7016, 0, 64 << 10, 64, ER_4K) }, { INFO("is25wp064", 0x9d7017, 0, 64 << 10, 128, ER_4K) }, { INFO("is25wp128", 0x9d7018, 0, 64 << 10, 256, ER_4K) }, - { INFO("is25wp256", 0x9d7019, 0, 64 << 10, 512, ER_4K) }, + { INFO("is25wp256", 0x9d7019, 0, 64 << 10, 512, ER_4K), + .sfdp_read = m25p80_sfdp_is25wp256 }, /* Macronix */ { INFO("mx25l2005a", 0xc22012, 0, 64 << 10, 4, ER_4K) }, @@ -232,12 +235,16 @@ static const FlashPartInfo known_devices[] = { { INFO("mx25l6405d", 0xc22017, 0, 64 << 10, 128, 0) }, { INFO("mx25l12805d", 0xc22018, 0, 64 << 10, 256, 0) }, { INFO("mx25l12855e", 0xc22618, 0, 64 << 10, 256, 0) }, - { INFO6("mx25l25635e", 0xc22019, 0xc22019, 64 << 10, 512, 0) }, + { INFO6("mx25l25635e", 0xc22019, 0xc22019, 64 << 10, 512, + ER_4K | ER_32K), .sfdp_read = m25p80_sfdp_mx25l25635e }, + { INFO6("mx25l25635f", 0xc22019, 0xc22019, 64 << 10, 512, + ER_4K | ER_32K), .sfdp_read = m25p80_sfdp_mx25l25635f }, { INFO("mx25l25655e", 0xc22619, 0, 64 << 10, 512, 0) }, { INFO("mx66l51235f", 0xc2201a, 0, 64 << 10, 1024, ER_4K | ER_32K) }, { INFO("mx66u51235f", 0xc2253a, 0, 64 << 10, 1024, ER_4K | ER_32K) }, { INFO("mx66u1g45g", 0xc2253b, 0, 64 << 10, 2048, ER_4K | ER_32K) }, - { INFO("mx66l1g45g", 0xc2201b, 0, 64 << 10, 2048, ER_4K | ER_32K) }, + { INFO("mx66l1g45g", 0xc2201b, 0, 64 << 10, 2048, ER_4K | ER_32K), + .sfdp_read = m25p80_sfdp_mx66l1g45g }, /* Micron */ { INFO("n25q032a11", 0x20bb16, 0, 64 << 10, 64, ER_4K) }, @@ -247,16 +254,22 @@ static const FlashPartInfo known_devices[] = { { INFO("n25q128a11", 0x20bb18, 0, 64 << 10, 256, ER_4K) }, { INFO("n25q128a13", 0x20ba18, 0, 64 << 10, 256, ER_4K) }, { INFO("n25q256a11", 0x20bb19, 0, 64 << 10, 512, ER_4K) }, - { INFO("n25q256a13", 0x20ba19, 0, 64 << 10, 512, ER_4K) }, + { INFO("n25q256a13", 0x20ba19, 0, 64 << 10, 512, ER_4K), + .sfdp_read = m25p80_sfdp_n25q256a }, { INFO("n25q512a11", 0x20bb20, 0, 64 << 10, 1024, ER_4K) }, { INFO("n25q512a13", 0x20ba20, 0, 64 << 10, 1024, ER_4K) }, { INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) }, - { INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) }, - { INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) }, + { INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, + ER_4K | HAS_SR_BP3_BIT6 | HAS_SR_TB), + .sfdp_read = m25p80_sfdp_n25q256a }, + { INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) }, { INFO("n25q512ax3", 0x20ba20, 0x1000, 64 << 10, 1024, ER_4K) }, { INFO("mt25ql512ab", 0x20ba20, 0x1044, 64 << 10, 1024, ER_4K | ER_32K) }, { INFO_STACKED("mt35xu01g", 0x2c5b1b, 0x104100, 128 << 10, 1024, ER_4K | ER_32K, 2) }, + { INFO_STACKED("mt35xu02gbba", 0x2c5b1c, 0x104100, 128 << 10, 2048, + ER_4K | ER_32K, 4), + .sfdp_read = m25p80_sfdp_mt35xu02g }, { INFO_STACKED("n25q00", 0x20ba21, 0x1000, 64 << 10, 2048, ER_4K, 4) }, { INFO_STACKED("n25q00a", 0x20bb21, 0x1000, 64 << 10, 2048, ER_4K, 4) }, { INFO_STACKED("mt25ql01g", 0x20ba21, 0x1040, 64 << 10, 2048, ER_4K, 2) }, @@ -338,9 +351,12 @@ static const FlashPartInfo known_devices[] = { { INFO("w25q64", 0xef4017, 0, 64 << 10, 128, ER_4K) }, { INFO("w25q80", 0xef5014, 0, 64 << 10, 16, ER_4K) }, { INFO("w25q80bl", 0xef4014, 0, 64 << 10, 16, ER_4K) }, - { INFO("w25q256", 0xef4019, 0, 64 << 10, 512, ER_4K) }, - { INFO("w25q512jv", 0xef4020, 0, 64 << 10, 1024, ER_4K) }, - { INFO("w25q01jvq", 0xef4021, 0, 64 << 10, 2048, ER_4K) }, + { INFO("w25q256", 0xef4019, 0, 64 << 10, 512, ER_4K), + .sfdp_read = m25p80_sfdp_w25q256 }, + { INFO("w25q512jv", 0xef4020, 0, 64 << 10, 1024, ER_4K), + .sfdp_read = m25p80_sfdp_w25q512jv }, + { INFO("w25q01jvq", 0xef4021, 0, 64 << 10, 2048, ER_4K), + .sfdp_read = m25p80_sfdp_w25q01jvq }, }; typedef enum { @@ -356,6 +372,7 @@ typedef enum { BULK_ERASE = 0xc7, READ_FSR = 0x70, RDCR = 0x15, + RDSFDP = 0x5a, READ = 0x03, READ4 = 0x13, @@ -422,6 +439,7 @@ typedef enum { STATE_COLLECTING_DATA, STATE_COLLECTING_VAR_LEN_DATA, STATE_READING_DATA, + STATE_READING_SFDP, } CMDState; typedef enum { @@ -472,11 +490,18 @@ struct Flash { uint8_t spansion_cr2v; uint8_t spansion_cr3v; uint8_t spansion_cr4v; + bool wp_level; bool write_enable; bool four_bytes_address_mode; bool reset_enable; bool quad_enable; bool aai_enable; + bool block_protect0; + bool block_protect1; + bool block_protect2; + bool block_protect3; + bool top_bottom_bit; + bool status_register_write_disabled; uint8_t ear; int64_t dirty_page; @@ -490,7 +515,6 @@ struct M25P80Class { FlashPartInfo *pi; }; -#define TYPE_M25P80 "m25p80-generic" OBJECT_DECLARE_TYPE(Flash, M25P80Class, M25P80) static inline Manufacturer get_man(Flash *s) @@ -621,12 +645,36 @@ void flash_write8(Flash *s, uint32_t addr, uint8_t data) { uint32_t page = addr / s->pi->page_size; uint8_t prev = s->storage[s->cur_addr]; + uint32_t block_protect_value = (s->block_protect3 << 3) | + (s->block_protect2 << 2) | + (s->block_protect1 << 1) | + (s->block_protect0 << 0); if (!s->write_enable) { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: write with write protect!\n"); return; } + if (block_protect_value > 0) { + uint32_t num_protected_sectors = 1 << (block_protect_value - 1); + uint32_t sector = addr / s->pi->sector_size; + + /* top_bottom_bit == 0 means TOP */ + if (!s->top_bottom_bit) { + if (s->pi->n_sectors <= sector + num_protected_sectors) { + qemu_log_mask(LOG_GUEST_ERROR, + "M25P80: write with write protect!\n"); + return; + } + } else { + if (sector < num_protected_sectors) { + qemu_log_mask(LOG_GUEST_ERROR, + "M25P80: write with write protect!\n"); + return; + } + } + } + if ((prev ^ data) & data) { trace_m25p80_programming_zero_to_one(s, addr, prev, data); } @@ -649,6 +697,8 @@ static inline int get_addr_length(Flash *s) } switch (s->cmd_in_progress) { + case RDSFDP: + return 3; case PP4: case PP4_4: case QPP_4: @@ -723,6 +773,17 @@ static void complete_collecting_data(Flash *s) flash_erase(s, s->cur_addr, s->cmd_in_progress); break; case WRSR: + s->status_register_write_disabled = extract32(s->data[0], 7, 1); + s->block_protect0 = extract32(s->data[0], 2, 1); + s->block_protect1 = extract32(s->data[0], 3, 1); + s->block_protect2 = extract32(s->data[0], 4, 1); + if (s->pi->flags & HAS_SR_TB) { + s->top_bottom_bit = extract32(s->data[0], 5, 1); + } + if (s->pi->flags & HAS_SR_BP3_BIT6) { + s->block_protect3 = extract32(s->data[0], 6, 1); + } + switch (get_man(s)) { case MAN_SPANSION: s->quad_enable = !!(s->data[1] & 0x02); @@ -782,6 +843,11 @@ static void complete_collecting_data(Flash *s) " by device\n"); } break; + + case RDSFDP: + s->state = STATE_READING_SFDP; + break; + default: break; } @@ -1165,22 +1231,34 @@ static void decode_new_cmd(Flash *s, uint32_t value) break; case WRSR: - if (s->write_enable) { - switch (get_man(s)) { - case MAN_SPANSION: - s->needed_bytes = 2; - s->state = STATE_COLLECTING_DATA; - break; - case MAN_MACRONIX: - s->needed_bytes = 2; - s->state = STATE_COLLECTING_VAR_LEN_DATA; - break; - default: - s->needed_bytes = 1; - s->state = STATE_COLLECTING_DATA; - } - s->pos = 0; + /* + * If WP# is low and status_register_write_disabled is high, + * status register writes are disabled. + * This is also called "hardware protected mode" (HPM). All other + * combinations of the two states are called "software protected mode" + * (SPM), and status register writes are permitted. + */ + if ((s->wp_level == 0 && s->status_register_write_disabled) + || !s->write_enable) { + qemu_log_mask(LOG_GUEST_ERROR, + "M25P80: Status register write is disabled!\n"); + break; } + + switch (get_man(s)) { + case MAN_SPANSION: + s->needed_bytes = 2; + s->state = STATE_COLLECTING_DATA; + break; + case MAN_MACRONIX: + s->needed_bytes = 2; + s->state = STATE_COLLECTING_VAR_LEN_DATA; + break; + default: + s->needed_bytes = 1; + s->state = STATE_COLLECTING_DATA; + } + s->pos = 0; break; case WRDI: @@ -1195,6 +1273,17 @@ static void decode_new_cmd(Flash *s, uint32_t value) case RDSR: s->data[0] = (!!s->write_enable) << 1; + s->data[0] |= (!!s->status_register_write_disabled) << 7; + s->data[0] |= (!!s->block_protect0) << 2; + s->data[0] |= (!!s->block_protect1) << 3; + s->data[0] |= (!!s->block_protect2) << 4; + if (s->pi->flags & HAS_SR_TB) { + s->data[0] |= (!!s->top_bottom_bit) << 5; + } + if (s->pi->flags & HAS_SR_BP3_BIT6) { + s->data[0] |= (!!s->block_protect3) << 6; + } + if (get_man(s) == MAN_MACRONIX || get_man(s) == MAN_ISSI) { s->data[0] |= (!!s->quad_enable) << 6; } @@ -1367,6 +1456,16 @@ static void decode_new_cmd(Flash *s, uint32_t value) qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value); } break; + case RDSFDP: + if (s->pi->sfdp_read) { + s->needed_bytes = get_addr_length(s) + 1; /* SFDP addr + dummy */ + s->pos = 0; + s->len = 0; + s->state = STATE_COLLECTING_DATA; + break; + } + /* Fallthrough */ + default: s->pos = 0; s->len = 1; @@ -1474,6 +1573,12 @@ static uint32_t m25p80_transfer8(SSIPeripheral *ss, uint32_t tx) } } break; + case STATE_READING_SFDP: + assert(s->pi->sfdp_read); + r = s->pi->sfdp_read(s->cur_addr); + trace_m25p80_read_sfdp(s, s->cur_addr, (uint8_t)r); + s->cur_addr = (s->cur_addr + 1) & (M25P80_SFDP_MAX_SIZE - 1); + break; default: case STATE_IDLE: @@ -1484,6 +1589,14 @@ static uint32_t m25p80_transfer8(SSIPeripheral *ss, uint32_t tx) return r; } +static void m25p80_write_protect_pin_irq_handler(void *opaque, int n, int level) +{ + Flash *s = M25P80(opaque); + /* WP# is just a single pin. */ + assert(n == 0); + s->wp_level = !!level; +} + static void m25p80_realize(SSIPeripheral *ss, Error **errp) { Flash *s = M25P80(ss); @@ -1506,8 +1619,8 @@ static void m25p80_realize(SSIPeripheral *ss, Error **errp) trace_m25p80_binding(s); 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"); + if (!blk_check_size_and_read_all(s->blk, DEVICE(s), + s->storage, s->size, errp)) { return; } } else { @@ -1515,12 +1628,23 @@ static void m25p80_realize(SSIPeripheral *ss, Error **errp) s->storage = blk_blockalign(NULL, s->size); memset(s->storage, 0xFF, s->size); } + + qdev_init_gpio_in_named(DEVICE(s), + m25p80_write_protect_pin_irq_handler, "WP#", 1); } static void m25p80_reset(DeviceState *d) { Flash *s = M25P80(d); + s->wp_level = true; + s->status_register_write_disabled = false; + s->block_protect0 = false; + s->block_protect1 = false; + s->block_protect2 = false; + s->block_protect3 = false; + s->top_bottom_bit = false; + reset_memory(s); } @@ -1563,7 +1687,7 @@ static const VMStateDescription vmstate_m25p80_data_read_loop = { .version_id = 1, .minimum_version_id = 1, .needed = m25p80_data_read_loop_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(data_read_loop, Flash), VMSTATE_END_OF_LIST() } @@ -1581,19 +1705,64 @@ static const VMStateDescription vmstate_m25p80_aai_enable = { .version_id = 1, .minimum_version_id = 1, .needed = m25p80_aai_enable_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(aai_enable, Flash), VMSTATE_END_OF_LIST() } }; +static bool m25p80_wp_level_srwd_needed(void *opaque) +{ + Flash *s = (Flash *)opaque; + + return !s->wp_level || s->status_register_write_disabled; +} + +static const VMStateDescription vmstate_m25p80_write_protect = { + .name = "m25p80/write_protect", + .version_id = 1, + .minimum_version_id = 1, + .needed = m25p80_wp_level_srwd_needed, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(wp_level, Flash), + VMSTATE_BOOL(status_register_write_disabled, Flash), + VMSTATE_END_OF_LIST() + } +}; + +static bool m25p80_block_protect_needed(void *opaque) +{ + Flash *s = (Flash *)opaque; + + return s->block_protect0 || + s->block_protect1 || + s->block_protect2 || + s->block_protect3 || + s->top_bottom_bit; +} + +static const VMStateDescription vmstate_m25p80_block_protect = { + .name = "m25p80/block_protect", + .version_id = 1, + .minimum_version_id = 1, + .needed = m25p80_block_protect_needed, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(block_protect0, Flash), + VMSTATE_BOOL(block_protect1, Flash), + VMSTATE_BOOL(block_protect2, Flash), + VMSTATE_BOOL(block_protect3, Flash), + VMSTATE_BOOL(top_bottom_bit, Flash), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_m25p80 = { .name = "m25p80", .version_id = 0, .minimum_version_id = 0, .pre_save = m25p80_pre_save, .pre_load = m25p80_pre_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(state, Flash), VMSTATE_UINT8_ARRAY(data, Flash, M25P80_INTERNAL_DATA_BUFFER_SZ), VMSTATE_UINT32(len, Flash), @@ -1615,9 +1784,11 @@ static const VMStateDescription vmstate_m25p80 = { VMSTATE_UINT8(spansion_cr4nv, Flash), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_m25p80_data_read_loop, &vmstate_m25p80_aai_enable, + &vmstate_m25p80_write_protect, + &vmstate_m25p80_block_protect, NULL } }; @@ -1663,3 +1834,8 @@ static void m25p80_register_types(void) } type_init(m25p80_register_types) + +BlockBackend *m25p80_get_blk(DeviceState *dev) +{ + return M25P80(dev)->blk; +} diff --git a/hw/block/m25p80_sfdp.c b/hw/block/m25p80_sfdp.c new file mode 100644 index 0000000000..6ee2cfaf11 --- /dev/null +++ b/hw/block/m25p80_sfdp.c @@ -0,0 +1,408 @@ +/* + * M25P80 Serial Flash Discoverable Parameter (SFDP) + * + * 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. + */ + +#include "qemu/osdep.h" +#include "qemu/host-utils.h" +#include "m25p80_sfdp.h" + +#define define_sfdp_read(model) \ + uint8_t m25p80_sfdp_##model(uint32_t addr) \ + { \ + assert(is_power_of_2(sizeof(sfdp_##model))); \ + return sfdp_##model[addr & (sizeof(sfdp_##model) - 1)]; \ + } + +/* + * Micron + */ +static const uint8_t sfdp_n25q256a[] = { + 0x53, 0x46, 0x44, 0x50, 0x00, 0x01, 0x00, 0xff, + 0x00, 0x00, 0x01, 0x09, 0x30, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe5, 0x20, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x29, 0xeb, 0x27, 0x6b, 0x08, 0x3b, 0x27, 0xbb, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x27, 0xbb, + 0xff, 0xff, 0x29, 0xeb, 0x0c, 0x20, 0x10, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; +define_sfdp_read(n25q256a); + +static const uint8_t sfdp_mt35xu02g[] = { + 0x53, 0x46, 0x44, 0x50, 0x06, 0x01, 0x01, 0xff, + 0x00, 0x06, 0x01, 0x10, 0x30, 0x00, 0x00, 0xff, + 0x84, 0x00, 0x01, 0x02, 0x80, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe5, 0x20, 0x8a, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x0c, 0x20, 0x11, 0xd8, + 0x0f, 0x52, 0x00, 0x00, 0x24, 0x5a, 0x99, 0x00, + 0x8b, 0x8e, 0x03, 0xe1, 0xac, 0x01, 0x27, 0x38, + 0x7a, 0x75, 0x7a, 0x75, 0xfb, 0xbd, 0xd5, 0x5c, + 0x00, 0x00, 0x70, 0xff, 0x81, 0xb0, 0x38, 0x36, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x43, 0x0e, 0xff, 0xff, 0x21, 0xdc, 0x5c, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +define_sfdp_read(mt35xu02g); + +/* + * Matronix + */ + +/* mx25l25635e. No 4B opcodes */ +static const uint8_t sfdp_mx25l25635e[] = { + 0x53, 0x46, 0x44, 0x50, 0x00, 0x01, 0x01, 0xff, + 0x00, 0x00, 0x01, 0x09, 0x30, 0x00, 0x00, 0xff, + 0xc2, 0x00, 0x01, 0x04, 0x60, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe5, 0x20, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x44, 0xeb, 0x08, 0x6b, 0x08, 0x3b, 0x04, 0xbb, + 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0x0c, 0x20, 0x0f, 0x52, + 0x10, 0xd8, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x36, 0x00, 0x27, 0xf7, 0x4f, 0xff, 0xff, + 0xd9, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; +define_sfdp_read(mx25l25635e) + +static const uint8_t sfdp_mx25l25635f[] = { + 0x53, 0x46, 0x44, 0x50, 0x00, 0x01, 0x01, 0xff, + 0x00, 0x00, 0x01, 0x09, 0x30, 0x00, 0x00, 0xff, + 0xc2, 0x00, 0x01, 0x04, 0x60, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe5, 0x20, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x44, 0xeb, 0x08, 0x6b, 0x08, 0x3b, 0x04, 0xbb, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x44, 0xeb, 0x0c, 0x20, 0x0f, 0x52, + 0x10, 0xd8, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x36, 0x00, 0x27, 0x9d, 0xf9, 0xc0, 0x64, + 0x85, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc2, 0xf5, 0x08, 0x0a, + 0x08, 0x04, 0x03, 0x06, 0x00, 0x00, 0x07, 0x29, + 0x17, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; +define_sfdp_read(mx25l25635f); + +static const uint8_t sfdp_mx66l1g45g[] = { + 0x53, 0x46, 0x44, 0x50, 0x06, 0x01, 0x02, 0xff, + 0x00, 0x06, 0x01, 0x10, 0x30, 0x00, 0x00, 0xff, + 0xc2, 0x00, 0x01, 0x04, 0x10, 0x01, 0x00, 0xff, + 0x84, 0x00, 0x01, 0x02, 0xc0, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe5, 0x20, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x3f, + 0x44, 0xeb, 0x08, 0x6b, 0x08, 0x3b, 0x04, 0xbb, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x44, 0xeb, 0x0c, 0x20, 0x0f, 0x52, + 0x10, 0xd8, 0x00, 0xff, 0xd6, 0x49, 0xc5, 0x00, + 0x85, 0xdf, 0x04, 0xe3, 0x44, 0x03, 0x67, 0x38, + 0x30, 0xb0, 0x30, 0xb0, 0xf7, 0xbd, 0xd5, 0x5c, + 0x4a, 0x9e, 0x29, 0xff, 0xf0, 0x50, 0xf9, 0x85, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7f, 0xef, 0xff, 0xff, 0x21, 0x5c, 0xdc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x36, 0x00, 0x27, 0x9d, 0xf9, 0xc0, 0x64, + 0x85, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc2, 0xf5, 0x08, 0x00, 0x0c, 0x04, 0x08, 0x08, + 0x01, 0x00, 0x19, 0x0f, 0x01, 0x01, 0x06, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; +define_sfdp_read(mx66l1g45g); + +/* + * Windbond + */ + +static const uint8_t sfdp_w25q256[] = { + 0x53, 0x46, 0x44, 0x50, 0x00, 0x01, 0x00, 0xff, + 0x00, 0x00, 0x01, 0x09, 0x80, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe5, 0x20, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x44, 0xeb, 0x08, 0x6b, 0x08, 0x3b, 0x42, 0xbb, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x21, 0xeb, 0x0c, 0x20, 0x0f, 0x52, + 0x10, 0xd8, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; +define_sfdp_read(w25q256); + +static const uint8_t sfdp_w25q512jv[] = { + 0x53, 0x46, 0x44, 0x50, 0x06, 0x01, 0x01, 0xff, + 0x00, 0x06, 0x01, 0x10, 0x80, 0x00, 0x00, 0xff, + 0x84, 0x00, 0x01, 0x02, 0xd0, 0x00, 0x00, 0xff, + 0x03, 0x00, 0x01, 0x02, 0xf0, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe5, 0x20, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x1f, + 0x44, 0xeb, 0x08, 0x6b, 0x08, 0x3b, 0x42, 0xbb, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x40, 0xeb, 0x0c, 0x20, 0x0f, 0x52, + 0x10, 0xd8, 0x00, 0x00, 0x36, 0x02, 0xa6, 0x00, + 0x82, 0xea, 0x14, 0xe2, 0xe9, 0x63, 0x76, 0x33, + 0x7a, 0x75, 0x7a, 0x75, 0xf7, 0xa2, 0xd5, 0x5c, + 0x19, 0xf7, 0x4d, 0xff, 0xe9, 0x70, 0xf9, 0xa5, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0a, 0xf0, 0xff, 0x21, 0xff, 0xdc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; +define_sfdp_read(w25q512jv); + +static const uint8_t sfdp_w25q01jvq[] = { + 0x53, 0x46, 0x44, 0x50, 0x06, 0x01, 0x01, 0xff, + 0x00, 0x06, 0x01, 0x10, 0x80, 0x00, 0x00, 0xff, + 0x84, 0x00, 0x01, 0x02, 0xd0, 0x00, 0x00, 0xff, + 0x03, 0x00, 0x01, 0x02, 0xf0, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe5, 0x20, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x3f, + 0x44, 0xeb, 0x08, 0x6b, 0x08, 0x3b, 0x42, 0xbb, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x40, 0xeb, 0x0c, 0x20, 0x0f, 0x52, + 0x10, 0xd8, 0x00, 0x00, 0x36, 0x02, 0xa6, 0x00, + 0x82, 0xea, 0x14, 0xe2, 0xe9, 0x63, 0x76, 0x33, + 0x7a, 0x75, 0x7a, 0x75, 0xf7, 0xa2, 0xd5, 0x5c, + 0x19, 0xf7, 0x4d, 0xff, 0xe9, 0x70, 0xf9, 0xa5, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0a, 0xf0, 0xff, 0x21, 0xff, 0xdc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; +define_sfdp_read(w25q01jvq); + +/* + * Integrated Silicon Solution (ISSI) + */ + +static const uint8_t sfdp_is25wp256[] = { + 0x53, 0x46, 0x44, 0x50, 0x06, 0x01, 0x01, 0xff, + 0x00, 0x06, 0x01, 0x10, 0x30, 0x00, 0x00, 0xff, + 0x9d, 0x05, 0x01, 0x03, 0x80, 0x00, 0x00, 0x02, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe5, 0x20, 0xf9, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x44, 0xeb, 0x08, 0x6b, 0x08, 0x3b, 0x80, 0xbb, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x44, 0xeb, 0x0c, 0x20, 0x0f, 0x52, + 0x10, 0xd8, 0x00, 0xff, 0x23, 0x4a, 0xc9, 0x00, + 0x82, 0xd8, 0x11, 0xce, 0xcc, 0xcd, 0x68, 0x46, + 0x7a, 0x75, 0x7a, 0x75, 0xf7, 0xae, 0xd5, 0x5c, + 0x4a, 0x42, 0x2c, 0xff, 0xf0, 0x30, 0xfa, 0xa9, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x50, 0x19, 0x50, 0x16, 0x9f, 0xf9, 0xc0, 0x64, + 0x8f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; +define_sfdp_read(is25wp256); diff --git a/hw/block/m25p80_sfdp.h b/hw/block/m25p80_sfdp.h new file mode 100644 index 0000000000..1733b56950 --- /dev/null +++ b/hw/block/m25p80_sfdp.h @@ -0,0 +1,32 @@ +/* + * M25P80 SFDP + * + * 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_M25P80_SFDP_H +#define HW_M25P80_SFDP_H + +/* + * SFDP area has a 3 bytes address space. + */ +#define M25P80_SFDP_MAX_SIZE (1 << 24) + +uint8_t m25p80_sfdp_n25q256a(uint32_t addr); +uint8_t m25p80_sfdp_mt35xu02g(uint32_t addr); + +uint8_t m25p80_sfdp_mx25l25635e(uint32_t addr); +uint8_t m25p80_sfdp_mx25l25635f(uint32_t addr); +uint8_t m25p80_sfdp_mx66l1g45g(uint32_t addr); + +uint8_t m25p80_sfdp_w25q256(uint32_t addr); +uint8_t m25p80_sfdp_w25q512jv(uint32_t addr); + +uint8_t m25p80_sfdp_w25q01jvq(uint32_t addr); + +uint8_t m25p80_sfdp_is25wp256(uint32_t addr); + +#endif diff --git a/hw/block/meson.build b/hw/block/meson.build index 2389326112..8aa4dc3893 100644 --- a/hw/block/meson.build +++ b/hw/block/meson.build @@ -1,22 +1,23 @@ -softmmu_ss.add(files( +system_ss.add(files( 'block.c', 'cdrom.c', 'hd-geometry.c' )) -softmmu_ss.add(when: 'CONFIG_ECC', if_true: files('ecc.c')) -softmmu_ss.add(when: 'CONFIG_FDC', if_true: files('fdc.c')) -softmmu_ss.add(when: 'CONFIG_FDC_ISA', if_true: files('fdc-isa.c')) -softmmu_ss.add(when: 'CONFIG_FDC_SYSBUS', if_true: files('fdc-sysbus.c')) -softmmu_ss.add(when: 'CONFIG_NAND', if_true: files('nand.c')) -softmmu_ss.add(when: 'CONFIG_ONENAND', if_true: files('onenand.c')) -softmmu_ss.add(when: 'CONFIG_PFLASH_CFI01', if_true: files('pflash_cfi01.c')) -softmmu_ss.add(when: 'CONFIG_PFLASH_CFI02', if_true: files('pflash_cfi02.c')) -softmmu_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80.c')) -softmmu_ss.add(when: 'CONFIG_SWIM', if_true: files('swim.c')) -softmmu_ss.add(when: 'CONFIG_XEN', if_true: files('xen-block.c')) -softmmu_ss.add(when: 'CONFIG_TC58128', if_true: files('tc58128.c')) +system_ss.add(when: 'CONFIG_ECC', if_true: files('ecc.c')) +system_ss.add(when: 'CONFIG_FDC', if_true: files('fdc.c')) +system_ss.add(when: 'CONFIG_FDC_ISA', if_true: files('fdc-isa.c')) +system_ss.add(when: 'CONFIG_FDC_SYSBUS', if_true: files('fdc-sysbus.c')) +system_ss.add(when: 'CONFIG_NAND', if_true: files('nand.c')) +system_ss.add(when: 'CONFIG_ONENAND', if_true: files('onenand.c')) +system_ss.add(when: 'CONFIG_PFLASH_CFI01', if_true: files('pflash_cfi01.c')) +system_ss.add(when: 'CONFIG_PFLASH_CFI02', if_true: files('pflash_cfi02.c')) +system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80.c')) +system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80_sfdp.c')) +system_ss.add(when: 'CONFIG_SWIM', if_true: files('swim.c')) +system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-block.c')) +system_ss.add(when: 'CONFIG_TC58128', if_true: files('tc58128.c')) -specific_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c')) -specific_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('vhost-user-blk.c')) +specific_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c', 'virtio-blk-common.c')) +specific_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('vhost-user-blk.c', 'virtio-blk-common.c')) subdir('dataplane') diff --git a/hw/block/nand.c b/hw/block/nand.c index 8bc80e3514..e2433c25bd 100644 --- a/hw/block/nand.c +++ b/hw/block/nand.c @@ -30,33 +30,33 @@ #include "qemu/module.h" #include "qom/object.h" -# define NAND_CMD_READ0 0x00 -# define NAND_CMD_READ1 0x01 -# define NAND_CMD_READ2 0x50 -# define NAND_CMD_LPREAD2 0x30 -# define NAND_CMD_NOSERIALREAD2 0x35 -# define NAND_CMD_RANDOMREAD1 0x05 -# define NAND_CMD_RANDOMREAD2 0xe0 -# define NAND_CMD_READID 0x90 -# define NAND_CMD_RESET 0xff -# define NAND_CMD_PAGEPROGRAM1 0x80 -# define NAND_CMD_PAGEPROGRAM2 0x10 -# define NAND_CMD_CACHEPROGRAM2 0x15 -# define NAND_CMD_BLOCKERASE1 0x60 -# define NAND_CMD_BLOCKERASE2 0xd0 -# define NAND_CMD_READSTATUS 0x70 -# define NAND_CMD_COPYBACKPRG1 0x85 +# define NAND_CMD_READ0 0x00 +# define NAND_CMD_READ1 0x01 +# define NAND_CMD_READ2 0x50 +# define NAND_CMD_LPREAD2 0x30 +# define NAND_CMD_NOSERIALREAD2 0x35 +# define NAND_CMD_RANDOMREAD1 0x05 +# define NAND_CMD_RANDOMREAD2 0xe0 +# define NAND_CMD_READID 0x90 +# define NAND_CMD_RESET 0xff +# define NAND_CMD_PAGEPROGRAM1 0x80 +# define NAND_CMD_PAGEPROGRAM2 0x10 +# define NAND_CMD_CACHEPROGRAM2 0x15 +# define NAND_CMD_BLOCKERASE1 0x60 +# define NAND_CMD_BLOCKERASE2 0xd0 +# define NAND_CMD_READSTATUS 0x70 +# define NAND_CMD_COPYBACKPRG1 0x85 -# define NAND_IOSTATUS_ERROR (1 << 0) -# define NAND_IOSTATUS_PLANE0 (1 << 1) -# define NAND_IOSTATUS_PLANE1 (1 << 2) -# define NAND_IOSTATUS_PLANE2 (1 << 3) -# define NAND_IOSTATUS_PLANE3 (1 << 4) +# define NAND_IOSTATUS_ERROR (1 << 0) +# define NAND_IOSTATUS_PLANE0 (1 << 1) +# define NAND_IOSTATUS_PLANE1 (1 << 2) +# define NAND_IOSTATUS_PLANE2 (1 << 3) +# define NAND_IOSTATUS_PLANE3 (1 << 4) # define NAND_IOSTATUS_READY (1 << 6) -# define NAND_IOSTATUS_UNPROTCT (1 << 7) +# define NAND_IOSTATUS_UNPROTCT (1 << 7) -# define MAX_PAGE 0x800 -# define MAX_OOB 0x40 +# define MAX_PAGE 0x800 +# define MAX_OOB 0x40 typedef struct NANDFlashState NANDFlashState; struct NANDFlashState { @@ -84,7 +84,11 @@ struct NANDFlashState { void (*blk_write)(NANDFlashState *s); void (*blk_erase)(NANDFlashState *s); - void (*blk_load)(NANDFlashState *s, uint64_t addr, int offset); + /* + * Returns %true when block containing (@addr + @offset) is + * successfully loaded, otherwise %false. + */ + bool (*blk_load)(NANDFlashState *s, uint64_t addr, unsigned offset); uint32_t ioaddr_vmstate; }; @@ -102,40 +106,40 @@ static void mem_and(uint8_t *dest, const uint8_t *src, size_t n) } } -# define NAND_NO_AUTOINCR 0x00000001 -# define NAND_BUSWIDTH_16 0x00000002 -# define NAND_NO_PADDING 0x00000004 -# define NAND_CACHEPRG 0x00000008 -# define NAND_COPYBACK 0x00000010 -# define NAND_IS_AND 0x00000020 -# define NAND_4PAGE_ARRAY 0x00000040 -# define NAND_NO_READRDY 0x00000100 -# define NAND_SAMSUNG_LP (NAND_NO_PADDING | NAND_COPYBACK) +# define NAND_NO_AUTOINCR 0x00000001 +# define NAND_BUSWIDTH_16 0x00000002 +# define NAND_NO_PADDING 0x00000004 +# define NAND_CACHEPRG 0x00000008 +# define NAND_COPYBACK 0x00000010 +# define NAND_IS_AND 0x00000020 +# define NAND_4PAGE_ARRAY 0x00000040 +# define NAND_NO_READRDY 0x00000100 +# define NAND_SAMSUNG_LP (NAND_NO_PADDING | NAND_COPYBACK) # define NAND_IO -# define PAGE(addr) ((addr) >> ADDR_SHIFT) -# define PAGE_START(page) (PAGE(page) * (NAND_PAGE_SIZE + OOB_SIZE)) -# define PAGE_MASK ((1 << ADDR_SHIFT) - 1) -# define OOB_SHIFT (PAGE_SHIFT - 5) -# define OOB_SIZE (1 << OOB_SHIFT) -# define SECTOR(addr) ((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT)) -# define SECTOR_OFFSET(addr) ((addr) & ((511 >> PAGE_SHIFT) << 8)) +# define PAGE(addr) ((addr) >> ADDR_SHIFT) +# define PAGE_START(page) (PAGE(page) * (NAND_PAGE_SIZE + OOB_SIZE)) +# define PAGE_MASK ((1 << ADDR_SHIFT) - 1) +# define OOB_SHIFT (PAGE_SHIFT - 5) +# define OOB_SIZE (1 << OOB_SHIFT) +# define SECTOR(addr) ((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT)) +# define SECTOR_OFFSET(addr) ((addr) & ((511 >> PAGE_SHIFT) << 8)) -# define NAND_PAGE_SIZE 256 -# define PAGE_SHIFT 8 -# define PAGE_SECTORS 1 -# define ADDR_SHIFT 8 +# define NAND_PAGE_SIZE 256 +# define PAGE_SHIFT 8 +# define PAGE_SECTORS 1 +# define ADDR_SHIFT 8 # include "nand.c" -# define NAND_PAGE_SIZE 512 -# define PAGE_SHIFT 9 -# define PAGE_SECTORS 1 -# define ADDR_SHIFT 8 +# define NAND_PAGE_SIZE 512 +# define PAGE_SHIFT 9 +# define PAGE_SECTORS 1 +# define ADDR_SHIFT 8 # include "nand.c" -# define NAND_PAGE_SIZE 2048 -# define PAGE_SHIFT 11 -# define PAGE_SECTORS 4 -# define ADDR_SHIFT 16 +# define NAND_PAGE_SIZE 2048 +# define PAGE_SHIFT 11 +# define PAGE_SECTORS 4 +# define ADDR_SHIFT 16 # include "nand.c" /* Information based on Linux drivers/mtd/nand/raw/nand_ids.c */ @@ -148,79 +152,79 @@ static const struct { } nand_flash_ids[0x100] = { [0 ... 0xff] = { 0 }, - [0x6b] = { 4, 8, 9, 4, 0 }, - [0xe3] = { 4, 8, 9, 4, 0 }, - [0xe5] = { 4, 8, 9, 4, 0 }, - [0xd6] = { 8, 8, 9, 4, 0 }, - [0xe6] = { 8, 8, 9, 4, 0 }, + [0x6b] = { 4, 8, 9, 4, 0 }, + [0xe3] = { 4, 8, 9, 4, 0 }, + [0xe5] = { 4, 8, 9, 4, 0 }, + [0xd6] = { 8, 8, 9, 4, 0 }, + [0xe6] = { 8, 8, 9, 4, 0 }, - [0x33] = { 16, 8, 9, 5, 0 }, - [0x73] = { 16, 8, 9, 5, 0 }, - [0x43] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x53] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x33] = { 16, 8, 9, 5, 0 }, + [0x73] = { 16, 8, 9, 5, 0 }, + [0x43] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x53] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x35] = { 32, 8, 9, 5, 0 }, - [0x75] = { 32, 8, 9, 5, 0 }, - [0x45] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x55] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x35] = { 32, 8, 9, 5, 0 }, + [0x75] = { 32, 8, 9, 5, 0 }, + [0x45] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x55] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x36] = { 64, 8, 9, 5, 0 }, - [0x76] = { 64, 8, 9, 5, 0 }, - [0x46] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x56] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x36] = { 64, 8, 9, 5, 0 }, + [0x76] = { 64, 8, 9, 5, 0 }, + [0x46] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x56] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x78] = { 128, 8, 9, 5, 0 }, - [0x39] = { 128, 8, 9, 5, 0 }, - [0x79] = { 128, 8, 9, 5, 0 }, - [0x72] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x49] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x74] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x59] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x78] = { 128, 8, 9, 5, 0 }, + [0x39] = { 128, 8, 9, 5, 0 }, + [0x79] = { 128, 8, 9, 5, 0 }, + [0x72] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x49] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x74] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, + [0x59] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x71] = { 256, 8, 9, 5, 0 }, + [0x71] = { 256, 8, 9, 5, 0 }, /* * These are the new chips with large page size. The pagesize and the * erasesize is determined from the extended id bytes */ -# define LP_OPTIONS (NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR) -# define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) +# define LP_OPTIONS (NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR) +# define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) /* 512 Megabit */ - [0xa2] = { 64, 8, 0, 0, LP_OPTIONS }, - [0xf2] = { 64, 8, 0, 0, LP_OPTIONS }, - [0xb2] = { 64, 16, 0, 0, LP_OPTIONS16 }, - [0xc2] = { 64, 16, 0, 0, LP_OPTIONS16 }, + [0xa2] = { 64, 8, 0, 0, LP_OPTIONS }, + [0xf2] = { 64, 8, 0, 0, LP_OPTIONS }, + [0xb2] = { 64, 16, 0, 0, LP_OPTIONS16 }, + [0xc2] = { 64, 16, 0, 0, LP_OPTIONS16 }, /* 1 Gigabit */ - [0xa1] = { 128, 8, 0, 0, LP_OPTIONS }, - [0xf1] = { 128, 8, 0, 0, LP_OPTIONS }, - [0xb1] = { 128, 16, 0, 0, LP_OPTIONS16 }, - [0xc1] = { 128, 16, 0, 0, LP_OPTIONS16 }, + [0xa1] = { 128, 8, 0, 0, LP_OPTIONS }, + [0xf1] = { 128, 8, 0, 0, LP_OPTIONS }, + [0xb1] = { 128, 16, 0, 0, LP_OPTIONS16 }, + [0xc1] = { 128, 16, 0, 0, LP_OPTIONS16 }, /* 2 Gigabit */ - [0xaa] = { 256, 8, 0, 0, LP_OPTIONS }, - [0xda] = { 256, 8, 0, 0, LP_OPTIONS }, - [0xba] = { 256, 16, 0, 0, LP_OPTIONS16 }, - [0xca] = { 256, 16, 0, 0, LP_OPTIONS16 }, + [0xaa] = { 256, 8, 0, 0, LP_OPTIONS }, + [0xda] = { 256, 8, 0, 0, LP_OPTIONS }, + [0xba] = { 256, 16, 0, 0, LP_OPTIONS16 }, + [0xca] = { 256, 16, 0, 0, LP_OPTIONS16 }, /* 4 Gigabit */ - [0xac] = { 512, 8, 0, 0, LP_OPTIONS }, - [0xdc] = { 512, 8, 0, 0, LP_OPTIONS }, - [0xbc] = { 512, 16, 0, 0, LP_OPTIONS16 }, - [0xcc] = { 512, 16, 0, 0, LP_OPTIONS16 }, + [0xac] = { 512, 8, 0, 0, LP_OPTIONS }, + [0xdc] = { 512, 8, 0, 0, LP_OPTIONS }, + [0xbc] = { 512, 16, 0, 0, LP_OPTIONS16 }, + [0xcc] = { 512, 16, 0, 0, LP_OPTIONS16 }, /* 8 Gigabit */ - [0xa3] = { 1024, 8, 0, 0, LP_OPTIONS }, - [0xd3] = { 1024, 8, 0, 0, LP_OPTIONS }, - [0xb3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, - [0xc3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, + [0xa3] = { 1024, 8, 0, 0, LP_OPTIONS }, + [0xd3] = { 1024, 8, 0, 0, LP_OPTIONS }, + [0xb3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, + [0xc3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, /* 16 Gigabit */ - [0xa5] = { 2048, 8, 0, 0, LP_OPTIONS }, - [0xd5] = { 2048, 8, 0, 0, LP_OPTIONS }, - [0xb5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, - [0xc5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, + [0xa5] = { 2048, 8, 0, 0, LP_OPTIONS }, + [0xd5] = { 2048, 8, 0, 0, LP_OPTIONS }, + [0xb5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, + [0xc5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, }; static void nand_reset(DeviceState *dev) @@ -243,9 +247,30 @@ static inline void nand_pushio_byte(NANDFlashState *s, uint8_t value) } } +/* + * nand_load_block: Load block containing (s->addr + @offset). + * Returns length of data available at @offset in this block. + */ +static unsigned nand_load_block(NANDFlashState *s, unsigned offset) +{ + unsigned iolen; + + if (!s->blk_load(s, s->addr, offset)) { + return 0; + } + + iolen = (1 << s->page_shift); + if (s->gnd) { + iolen += 1 << s->oob_shift; + } + assert(offset <= iolen); + iolen -= offset; + + return iolen; +} + static void nand_command(NANDFlashState *s) { - unsigned int offset; switch (s->cmd) { case NAND_CMD_READ0: s->iolen = 0; @@ -271,12 +296,7 @@ static void nand_command(NANDFlashState *s) case NAND_CMD_NOSERIALREAD2: if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)) break; - offset = s->addr & ((1 << s->addr_shift) - 1); - s->blk_load(s, s->addr, offset); - if (s->gnd) - s->iolen = (1 << s->page_shift) - offset; - else - s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset; + s->iolen = nand_load_block(s, s->addr & ((1 << s->addr_shift) - 1)); break; case NAND_CMD_RESET: @@ -345,7 +365,7 @@ static const VMStateDescription vmstate_nand = { .minimum_version_id = 1, .pre_save = nand_pre_save, .post_load = nand_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(cle, NANDFlashState), VMSTATE_UINT8(ale, NANDFlashState), VMSTATE_UINT8(ce, NANDFlashState), @@ -597,12 +617,7 @@ uint32_t nand_getio(DeviceState *dev) if (!s->iolen && s->cmd == NAND_CMD_READ0) { offset = (int) (s->addr & ((1 << s->addr_shift) - 1)) + s->offset; s->offset = 0; - - s->blk_load(s, s->addr, offset); - if (s->gnd) - s->iolen = (1 << s->page_shift) - offset; - else - s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset; + s->iolen = nand_load_block(s, offset); } if (s->ce || s->iolen <= 0) { @@ -666,8 +681,8 @@ static void glue(nand_blk_write_, NAND_PAGE_SIZE)(NANDFlashState *s) sector = SECTOR(s->addr); off = (s->addr & PAGE_MASK) + s->offset; soff = SECTOR_OFFSET(s->addr); - if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS, iobuf, - PAGE_SECTORS << BDRV_SECTOR_BITS) < 0) { + if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS, + PAGE_SECTORS << BDRV_SECTOR_BITS, iobuf, 0) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); return; } @@ -679,24 +694,24 @@ static void glue(nand_blk_write_, NAND_PAGE_SIZE)(NANDFlashState *s) MIN(OOB_SIZE, off + s->iolen - NAND_PAGE_SIZE)); } - if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS, iobuf, - PAGE_SECTORS << BDRV_SECTOR_BITS, 0) < 0) { + if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS, + PAGE_SECTORS << BDRV_SECTOR_BITS, iobuf, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); } } else { off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset; sector = off >> 9; soff = off & 0x1ff; - if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS, iobuf, - (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS) < 0) { + if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS, + (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, iobuf, 0) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); return; } mem_and(iobuf + soff, s->io, s->iolen); - if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS, iobuf, - (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, 0) < 0) { + if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS, + (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, iobuf, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); } } @@ -723,20 +738,20 @@ static void glue(nand_blk_erase_, NAND_PAGE_SIZE)(NANDFlashState *s) i = SECTOR(addr); page = SECTOR(addr + (1 << (ADDR_SHIFT + s->erase_shift))); for (; i < page; i ++) - if (blk_pwrite(s->blk, i << BDRV_SECTOR_BITS, iobuf, - BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(s->blk, i << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, iobuf, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, i); } } else { addr = PAGE_START(addr); page = addr >> 9; - if (blk_pread(s->blk, page << BDRV_SECTOR_BITS, iobuf, - BDRV_SECTOR_SIZE) < 0) { + if (blk_pread(s->blk, page << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, iobuf, 0) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, page); } memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1); - if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS, iobuf, - BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, iobuf, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, page); } @@ -744,36 +759,40 @@ static void glue(nand_blk_erase_, NAND_PAGE_SIZE)(NANDFlashState *s) i = (addr & ~0x1ff) + 0x200; for (addr += ((NAND_PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200; i < addr; i += 0x200) { - if (blk_pwrite(s->blk, i, iobuf, BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(s->blk, i, BDRV_SECTOR_SIZE, iobuf, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, i >> 9); } } page = i >> 9; - if (blk_pread(s->blk, page << BDRV_SECTOR_BITS, iobuf, - BDRV_SECTOR_SIZE) < 0) { + if (blk_pread(s->blk, page << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, iobuf, 0) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, page); } memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1); - if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS, iobuf, - BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, iobuf, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, page); } } } -static void glue(nand_blk_load_, NAND_PAGE_SIZE)(NANDFlashState *s, - uint64_t addr, int offset) +static bool glue(nand_blk_load_, NAND_PAGE_SIZE)(NANDFlashState *s, + uint64_t addr, unsigned offset) { if (PAGE(addr) >= s->pages) { - return; + return false; + } + + if (offset > NAND_PAGE_SIZE + OOB_SIZE) { + return false; } if (s->blk) { if (s->mem_oob) { - if (blk_pread(s->blk, SECTOR(addr) << BDRV_SECTOR_BITS, s->io, - PAGE_SECTORS << BDRV_SECTOR_BITS) < 0) { + if (blk_pread(s->blk, SECTOR(addr) << BDRV_SECTOR_BITS, + PAGE_SECTORS << BDRV_SECTOR_BITS, s->io, 0) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, SECTOR(addr)); } @@ -782,8 +801,9 @@ static void glue(nand_blk_load_, NAND_PAGE_SIZE)(NANDFlashState *s, OOB_SIZE); s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset; } else { - if (blk_pread(s->blk, PAGE_START(addr), s->io, - (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS) < 0) { + if (blk_pread(s->blk, PAGE_START(addr), + (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, s->io, 0) + < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, PAGE_START(addr) >> 9); } @@ -794,6 +814,8 @@ static void glue(nand_blk_load_, NAND_PAGE_SIZE)(NANDFlashState *s, offset, NAND_PAGE_SIZE + OOB_SIZE - offset); s->ioaddr = s->io; } + + return true; } static void glue(nand_init_, NAND_PAGE_SIZE)(NANDFlashState *s) @@ -811,4 +833,4 @@ static void glue(nand_init_, NAND_PAGE_SIZE)(NANDFlashState *s) # undef PAGE_SHIFT # undef PAGE_SECTORS # undef ADDR_SHIFT -#endif /* NAND_IO */ +#endif /* NAND_IO */ diff --git a/hw/block/onenand.c b/hw/block/onenand.c index afc0cd3a0f..d8a6944027 100644 --- a/hw/block/onenand.c +++ b/hw/block/onenand.c @@ -35,10 +35,10 @@ #include "qom/object.h" /* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */ -#define PAGE_SHIFT 11 +#define PAGE_SHIFT 11 /* Fixed */ -#define BLOCK_SHIFT (PAGE_SHIFT + 6) +#define BLOCK_SHIFT (PAGE_SHIFT + 6) #define TYPE_ONE_NAND "onenand" OBJECT_DECLARE_SIMPLE_TYPE(OneNANDState, ONE_NAND) @@ -179,7 +179,7 @@ static const VMStateDescription vmstate_onenand = { .minimum_version_id = 1, .pre_save = onenand_pre_save, .post_load = onenand_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(current_direction, OneNANDState), VMSTATE_INT32(cycle, OneNANDState), VMSTATE_INT32(otpmode, OneNANDState), @@ -229,8 +229,8 @@ static void onenand_reset(OneNANDState *s, int cold) /* Lock the whole flash */ memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks); - if (s->blk_cur && blk_pread(s->blk_cur, 0, s->boot[0], - 8 << BDRV_SECTOR_BITS) < 0) { + if (s->blk_cur && blk_pread(s->blk_cur, 0, 8 << BDRV_SECTOR_BITS, + s->boot[0], 0) < 0) { hw_error("%s: Loading the BootRAM failed.\n", __func__); } } @@ -249,8 +249,8 @@ static inline int onenand_load_main(OneNANDState *s, int sec, int secn, assert(UINT32_MAX >> BDRV_SECTOR_BITS > sec); assert(UINT32_MAX >> BDRV_SECTOR_BITS > secn); if (s->blk_cur) { - return blk_pread(s->blk_cur, sec << BDRV_SECTOR_BITS, dest, - secn << BDRV_SECTOR_BITS) < 0; + return blk_pread(s->blk_cur, sec << BDRV_SECTOR_BITS, + secn << BDRV_SECTOR_BITS, dest, 0) < 0; } else if (sec + secn > s->secs_cur) { return 1; } @@ -274,7 +274,7 @@ static inline int onenand_prog_main(OneNANDState *s, int sec, int secn, uint8_t *dp = 0; if (s->blk_cur) { dp = g_malloc(size); - if (!dp || blk_pread(s->blk_cur, offset, dp, size) < 0) { + if (!dp || blk_pread(s->blk_cur, offset, size, dp, 0) < 0) { result = 1; } } else { @@ -290,7 +290,7 @@ static inline int onenand_prog_main(OneNANDState *s, int sec, int secn, dp[i] &= sp[i]; } if (s->blk_cur) { - result = blk_pwrite(s->blk_cur, offset, dp, size, 0) < 0; + result = blk_pwrite(s->blk_cur, offset, size, dp, 0) < 0; } } if (dp && s->blk_cur) { @@ -308,7 +308,7 @@ static inline int onenand_load_spare(OneNANDState *s, int sec, int secn, if (s->blk_cur) { uint32_t offset = (s->secs_cur + (sec >> 5)) << BDRV_SECTOR_BITS; - if (blk_pread(s->blk_cur, offset, buf, BDRV_SECTOR_SIZE) < 0) { + if (blk_pread(s->blk_cur, offset, BDRV_SECTOR_SIZE, buf, 0) < 0) { return 1; } memcpy(dest, buf + ((sec & 31) << 4), secn << 4); @@ -333,7 +333,7 @@ static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn, if (s->blk_cur) { dp = g_malloc(512); if (!dp - || blk_pread(s->blk_cur, offset, dp, BDRV_SECTOR_SIZE) < 0) { + || blk_pread(s->blk_cur, offset, BDRV_SECTOR_SIZE, dp, 0) < 0) { result = 1; } else { dpp = dp + ((sec & 31) << 4); @@ -351,8 +351,8 @@ static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn, dpp[i] &= sp[i]; } if (s->blk_cur) { - result = blk_pwrite(s->blk_cur, offset, dp, - BDRV_SECTOR_SIZE, 0) < 0; + result = blk_pwrite(s->blk_cur, offset, BDRV_SECTOR_SIZE, dp, + 0) < 0; } } g_free(dp); @@ -370,17 +370,17 @@ static inline int onenand_erase(OneNANDState *s, int sec, int num) for (; num > 0; num--, sec++) { if (s->blk_cur) { int erasesec = s->secs_cur + (sec >> 5); - if (blk_pwrite(s->blk_cur, sec << BDRV_SECTOR_BITS, blankbuf, - BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(s->blk_cur, sec << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, blankbuf, 0) < 0) { goto fail; } - if (blk_pread(s->blk_cur, erasesec << BDRV_SECTOR_BITS, tmpbuf, - BDRV_SECTOR_SIZE) < 0) { + if (blk_pread(s->blk_cur, erasesec << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, tmpbuf, 0) < 0) { goto fail; } memcpy(tmpbuf + ((sec & 31) << 4), blankbuf, 1 << 4); - if (blk_pwrite(s->blk_cur, erasesec << BDRV_SECTOR_BITS, tmpbuf, - BDRV_SECTOR_SIZE, 0) < 0) { + if (blk_pwrite(s->blk_cur, erasesec << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, tmpbuf, 0) < 0) { goto fail; } } else { @@ -408,23 +408,23 @@ static void onenand_command(OneNANDState *s) int b; int sec; void *buf; -#define SETADDR(block, page) \ - sec = (s->addr[page] & 3) + \ - ((((s->addr[page] >> 2) & 0x3f) + \ - (((s->addr[block] & 0xfff) | \ - (s->addr[block] >> 15 ? \ - s->density_mask : 0)) << 6)) << (PAGE_SHIFT - 9)); -#define SETBUF_M() \ - buf = (s->bufaddr & 8) ? \ - s->data[(s->bufaddr >> 2) & 1][0] : s->boot[0]; \ +#define SETADDR(block, page) \ + sec = (s->addr[page] & 3) + \ + ((((s->addr[page] >> 2) & 0x3f) + \ + (((s->addr[block] & 0xfff) | \ + (s->addr[block] >> 15 ? s->density_mask : 0)) \ + << 6)) \ + << (PAGE_SHIFT - 9)); +#define SETBUF_M() \ + buf = (s->bufaddr & 8) ? s->data[(s->bufaddr >> 2) & 1][0] : s->boot[0]; \ buf += (s->bufaddr & 3) << 9; -#define SETBUF_S() \ - buf = (s->bufaddr & 8) ? \ - s->data[(s->bufaddr >> 2) & 1][1] : s->boot[1]; \ +#define SETBUF_S() \ + buf = (s->bufaddr & 8) ? \ + s->data[(s->bufaddr >> 2) & 1][1] : s->boot[1]; \ buf += (s->bufaddr & 3) << 4; switch (s->command) { - case 0x00: /* Load single/multiple sector data unit into buffer */ + case 0x00: /* Load single/multiple sector data unit into buffer */ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) SETBUF_M() @@ -443,7 +443,7 @@ static void onenand_command(OneNANDState *s) */ s->intstatus |= ONEN_INT | ONEN_INT_LOAD; break; - case 0x13: /* Load single/multiple spare sector into buffer */ + case 0x13: /* Load single/multiple spare sector into buffer */ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) SETBUF_S() @@ -456,7 +456,7 @@ static void onenand_command(OneNANDState *s) */ s->intstatus |= ONEN_INT | ONEN_INT_LOAD; break; - case 0x80: /* Program single/multiple sector data unit from buffer */ + case 0x80: /* Program single/multiple sector data unit from buffer */ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) SETBUF_M() @@ -475,7 +475,7 @@ static void onenand_command(OneNANDState *s) */ s->intstatus |= ONEN_INT | ONEN_INT_PROG; break; - case 0x1a: /* Program single/multiple spare area sector from buffer */ + case 0x1a: /* Program single/multiple spare area sector from buffer */ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) SETBUF_S() @@ -488,7 +488,7 @@ static void onenand_command(OneNANDState *s) */ s->intstatus |= ONEN_INT | ONEN_INT_PROG; break; - case 0x1b: /* Copy-back program */ + case 0x1b: /* Copy-back program */ SETBUF_S() SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) @@ -504,7 +504,7 @@ static void onenand_command(OneNANDState *s) s->intstatus |= ONEN_INT | ONEN_INT_PROG; break; - case 0x23: /* Unlock NAND array block(s) */ + case 0x23: /* Unlock NAND array block(s) */ s->intstatus |= ONEN_INT; /* XXX the previous (?) area should be locked automatically */ @@ -519,7 +519,7 @@ static void onenand_command(OneNANDState *s) s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED; } break; - case 0x27: /* Unlock All NAND array blocks */ + case 0x27: /* Unlock All NAND array blocks */ s->intstatus |= ONEN_INT; for (b = 0; b < s->blocks; b ++) { @@ -530,7 +530,7 @@ static void onenand_command(OneNANDState *s) } break; - case 0x2a: /* Lock NAND array block(s) */ + case 0x2a: /* Lock NAND array block(s) */ s->intstatus |= ONEN_INT; for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { @@ -544,7 +544,7 @@ static void onenand_command(OneNANDState *s) s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKED; } break; - case 0x2c: /* Lock-tight NAND array block(s) */ + case 0x2c: /* Lock-tight NAND array block(s) */ s->intstatus |= ONEN_INT; for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { @@ -559,13 +559,13 @@ static void onenand_command(OneNANDState *s) } break; - case 0x71: /* Erase-Verify-Read */ + case 0x71: /* Erase-Verify-Read */ s->intstatus |= ONEN_INT; break; - case 0x95: /* Multi-block erase */ + case 0x95: /* Multi-block erase */ qemu_irq_pulse(s->intr); /* Fall through. */ - case 0x94: /* Block erase */ + case 0x94: /* Block erase */ sec = ((s->addr[ONEN_BUF_BLOCK] & 0xfff) | (s->addr[ONEN_BUF_BLOCK] >> 15 ? s->density_mask : 0)) << (BLOCK_SHIFT - 9); @@ -574,20 +574,20 @@ static void onenand_command(OneNANDState *s) s->intstatus |= ONEN_INT | ONEN_INT_ERASE; break; - case 0xb0: /* Erase suspend */ + case 0xb0: /* Erase suspend */ break; - case 0x30: /* Erase resume */ + case 0x30: /* Erase resume */ s->intstatus |= ONEN_INT | ONEN_INT_ERASE; break; - case 0xf0: /* Reset NAND Flash core */ + case 0xf0: /* Reset NAND Flash core */ onenand_reset(s, 0); break; - case 0xf3: /* Reset OneNAND */ + case 0xf3: /* Reset OneNAND */ onenand_reset(s, 0); break; - case 0x65: /* OTP Access */ + case 0x65: /* OTP Access */ s->intstatus |= ONEN_INT; s->blk_cur = NULL; s->current = s->otp; @@ -616,52 +616,52 @@ static uint64_t onenand_read(void *opaque, hwaddr addr, case 0x0000 ... 0xbffe: return lduw_le_p(s->boot[0] + addr); - case 0xf000: /* Manufacturer ID */ + case 0xf000: /* Manufacturer ID */ return s->id.man; - case 0xf001: /* Device ID */ + case 0xf001: /* Device ID */ return s->id.dev; - case 0xf002: /* Version ID */ + case 0xf002: /* Version ID */ return s->id.ver; /* TODO: get the following values from a real chip! */ - case 0xf003: /* Data Buffer size */ + case 0xf003: /* Data Buffer size */ return 1 << PAGE_SHIFT; - case 0xf004: /* Boot Buffer size */ + case 0xf004: /* Boot Buffer size */ return 0x200; - case 0xf005: /* Amount of buffers */ + case 0xf005: /* Amount of buffers */ return 1 | (2 << 8); - case 0xf006: /* Technology */ + case 0xf006: /* Technology */ return 0; - case 0xf100 ... 0xf107: /* Start addresses */ + case 0xf100 ... 0xf107: /* Start addresses */ return s->addr[offset - 0xf100]; - case 0xf200: /* Start buffer */ + case 0xf200: /* Start buffer */ return (s->bufaddr << 8) | ((s->count - 1) & (1 << (PAGE_SHIFT - 10))); - case 0xf220: /* Command */ + case 0xf220: /* Command */ return s->command; - case 0xf221: /* System Configuration 1 */ + case 0xf221: /* System Configuration 1 */ return s->config[0] & 0xffe0; - case 0xf222: /* System Configuration 2 */ + case 0xf222: /* System Configuration 2 */ return s->config[1]; - case 0xf240: /* Controller Status */ + case 0xf240: /* Controller Status */ return s->status; - case 0xf241: /* Interrupt */ + case 0xf241: /* Interrupt */ return s->intstatus; - case 0xf24c: /* Unlock Start Block Address */ + case 0xf24c: /* Unlock Start Block Address */ return s->unladdr[0]; - case 0xf24d: /* Unlock End Block Address */ + case 0xf24d: /* Unlock End Block Address */ return s->unladdr[1]; - case 0xf24e: /* Write Protection Status */ + case 0xf24e: /* Write Protection Status */ return s->wpstatus; - case 0xff00: /* ECC Status */ + case 0xff00: /* ECC Status */ return 0x00; - case 0xff01: /* ECC Result of main area data */ - case 0xff02: /* ECC Result of spare area data */ - case 0xff03: /* ECC Result of main area data */ - case 0xff04: /* ECC Result of spare area data */ + case 0xff01: /* ECC Result of main area data */ + case 0xff02: /* ECC Result of spare area data */ + case 0xff03: /* ECC Result of main area data */ + case 0xff04: /* ECC Result of spare area data */ qemu_log_mask(LOG_UNIMP, "onenand: ECC result registers unimplemented\n"); return 0x0000; @@ -696,15 +696,15 @@ static void onenand_write(void *opaque, hwaddr addr, } switch (value) { - case 0x00f0: /* Reset OneNAND */ + case 0x00f0: /* Reset OneNAND */ onenand_reset(s, 0); break; - case 0x00e0: /* Load Data into Buffer */ + case 0x00e0: /* Load Data into Buffer */ s->cycle = 1; break; - case 0x0090: /* Read Identification Data */ + case 0x0090: /* Read Identification Data */ memset(s->boot[0], 0, 3 << s->shift); s->boot[0][0 << s->shift] = s->id.man & 0xff; s->boot[0][1 << s->shift] = s->id.dev & 0xff; @@ -718,11 +718,11 @@ static void onenand_write(void *opaque, hwaddr addr, } break; - case 0xf100 ... 0xf107: /* Start addresses */ + case 0xf100 ... 0xf107: /* Start addresses */ s->addr[offset - 0xf100] = value; break; - case 0xf200: /* Start buffer */ + case 0xf200: /* Start buffer */ s->bufaddr = (value >> 8) & 0xf; if (PAGE_SHIFT == 11) s->count = (value & 3) ?: 4; @@ -730,36 +730,36 @@ static void onenand_write(void *opaque, hwaddr addr, s->count = (value & 1) ?: 2; break; - case 0xf220: /* Command */ + case 0xf220: /* Command */ if (s->intstatus & (1 << 15)) break; s->command = value; onenand_command(s); break; - case 0xf221: /* System Configuration 1 */ + case 0xf221: /* System Configuration 1 */ s->config[0] = value; onenand_intr_update(s); qemu_set_irq(s->rdy, (s->config[0] >> 7) & 1); break; - case 0xf222: /* System Configuration 2 */ + case 0xf222: /* System Configuration 2 */ s->config[1] = value; break; - case 0xf241: /* Interrupt */ + case 0xf241: /* Interrupt */ s->intstatus &= value; if ((1 << 15) & ~s->intstatus) s->status &= ~(ONEN_ERR_CMD | ONEN_ERR_ERASE | ONEN_ERR_PROG | ONEN_ERR_LOAD); onenand_intr_update(s); break; - case 0xf24c: /* Unlock Start Block Address */ + case 0xf24c: /* Unlock Start Block Address */ s->unladdr[0] = value & (s->blocks - 1); /* For some reason we have to set the end address to by default * be same as start because the software forgets to write anything * in there. */ s->unladdr[1] = value & (s->blocks - 1); break; - case 0xf24d: /* Unlock End Block Address */ + case 0xf24d: /* Unlock End Block Address */ s->unladdr[1] = value & (s->blocks - 1); break; diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 74c7190302..1bda8424b9 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -45,7 +45,6 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/bitops.h" -#include "qemu/error-report.h" #include "qemu/host-utils.h" #include "qemu/log.h" #include "qemu/module.h" @@ -81,27 +80,54 @@ struct PFlashCFI01 { uint16_t ident3; uint8_t cfi_table[0x52]; uint64_t counter; - unsigned int writeblock_size; + uint32_t writeblock_size; MemoryRegion mem; char *name; void *storage; VMChangeStateEntry *vmstate; bool old_multiple_chip_handling; + + /* block update buffer */ + unsigned char *blk_bytes; + uint32_t blk_offset; }; static int pflash_post_load(void *opaque, int version_id); +static bool pflash_blk_write_state_needed(void *opaque) +{ + PFlashCFI01 *pfl = opaque; + + return (pfl->blk_offset != -1); +} + +static const VMStateDescription vmstate_pflash_blk_write = { + .name = "pflash_cfi01_blk_write", + .version_id = 1, + .minimum_version_id = 1, + .needed = pflash_blk_write_state_needed, + .fields = (const VMStateField[]) { + VMSTATE_VBUFFER_UINT32(blk_bytes, PFlashCFI01, 0, NULL, writeblock_size), + VMSTATE_UINT32(blk_offset, PFlashCFI01), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_pflash = { .name = "pflash_cfi01", .version_id = 1, .minimum_version_id = 1, .post_load = pflash_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(wcycle, PFlashCFI01), VMSTATE_UINT8(cmd, PFlashCFI01), VMSTATE_UINT8(status, PFlashCFI01), VMSTATE_UINT64(counter, PFlashCFI01), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_pflash_blk_write, + NULL } }; @@ -226,34 +252,10 @@ static uint32_t pflash_data_read(PFlashCFI01 *pfl, hwaddr offset, uint32_t ret; p = pfl->storage; - switch (width) { - case 1: - ret = p[offset]; - break; - case 2: - if (be) { - ret = p[offset] << 8; - ret |= p[offset + 1]; - } else { - ret = p[offset]; - ret |= p[offset + 1] << 8; - } - break; - case 4: - if (be) { - ret = p[offset] << 24; - ret |= p[offset + 1] << 16; - ret |= p[offset + 2] << 8; - ret |= p[offset + 3]; - } else { - ret = p[offset]; - ret |= p[offset + 1] << 8; - ret |= p[offset + 2] << 16; - ret |= p[offset + 3] << 24; - } - break; - default: - abort(); + if (be) { + ret = ldn_be_p(p + offset, width); + } else { + ret = ldn_le_p(p + offset, width); } trace_pflash_data_read(pfl->name, offset, width, ret); return ret; @@ -392,8 +394,8 @@ static void pflash_update(PFlashCFI01 *pfl, int offset, /* widen to sector boundaries */ offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE); offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE); - ret = blk_pwrite(pfl->blk, offset, pfl->storage + offset, - offset_end - offset, 0); + ret = blk_pwrite(pfl->blk, offset, offset_end - offset, + pfl->storage + offset, 0); if (ret < 0) { /* TODO set error bit in status */ error_report("Could not update PFLASH: %s", strerror(-ret)); @@ -401,40 +403,61 @@ static void pflash_update(PFlashCFI01 *pfl, int offset, } } +/* copy current flash content to block update buffer */ +static void pflash_blk_write_start(PFlashCFI01 *pfl, hwaddr offset) +{ + hwaddr mask = ~(pfl->writeblock_size - 1); + + trace_pflash_write_block_start(pfl->name, pfl->counter); + pfl->blk_offset = offset & mask; + memcpy(pfl->blk_bytes, pfl->storage + pfl->blk_offset, + pfl->writeblock_size); +} + +/* commit block update buffer changes */ +static void pflash_blk_write_flush(PFlashCFI01 *pfl) +{ + g_assert(pfl->blk_offset != -1); + trace_pflash_write_block_flush(pfl->name); + memcpy(pfl->storage + pfl->blk_offset, pfl->blk_bytes, + pfl->writeblock_size); + pflash_update(pfl, pfl->blk_offset, pfl->writeblock_size); + pfl->blk_offset = -1; +} + +/* discard block update buffer changes */ +static void pflash_blk_write_abort(PFlashCFI01 *pfl) +{ + trace_pflash_write_block_abort(pfl->name); + pfl->blk_offset = -1; +} + static inline void pflash_data_write(PFlashCFI01 *pfl, hwaddr offset, uint32_t value, int width, int be) { - uint8_t *p = pfl->storage; + uint8_t *p; - trace_pflash_data_write(pfl->name, offset, width, value, pfl->counter); - switch (width) { - case 1: - p[offset] = value; - break; - case 2: - if (be) { - p[offset] = value >> 8; - p[offset + 1] = value; - } else { - p[offset] = value; - p[offset + 1] = value >> 8; + if (pfl->blk_offset != -1) { + /* block write: redirect writes to block update buffer */ + if ((offset < pfl->blk_offset) || + (offset + width > pfl->blk_offset + pfl->writeblock_size)) { + pfl->status |= 0x10; /* Programming error */ + return; } - break; - case 4: - if (be) { - p[offset] = value >> 24; - p[offset + 1] = value >> 16; - p[offset + 2] = value >> 8; - p[offset + 3] = value; - } else { - p[offset] = value; - p[offset + 1] = value >> 8; - p[offset + 2] = value >> 16; - p[offset + 3] = value >> 24; - } - break; + trace_pflash_data_write_block(pfl->name, offset, width, value, + pfl->counter); + p = pfl->blk_bytes + (offset - pfl->blk_offset); + } else { + /* write directly to storage */ + trace_pflash_data_write(pfl->name, offset, width, value); + p = pfl->storage + offset; } + if (be) { + stn_be_p(p, width, value); + } else { + stn_le_p(p, width, value); + } } static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, @@ -549,9 +572,9 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, } else { value = extract32(value, 0, pfl->bank_width * 8); } - trace_pflash_write_block(pfl->name, value); pfl->counter = value; pfl->wcycle++; + pflash_blk_write_start(pfl, offset); break; case 0x60: if (cmd == 0xd0) { @@ -582,12 +605,7 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, switch (pfl->cmd) { case 0xe8: /* Block write */ /* FIXME check @offset, @width */ - if (!pfl->ro) { - /* - * FIXME writing straight to memory is *wrong*. We - * should write to a buffer, and flush it to memory - * only on confirm command (see below). - */ + if (!pfl->ro && (pfl->blk_offset != -1)) { pflash_data_write(pfl, offset, value, width, be); } else { pfl->status |= 0x10; /* Programming error */ @@ -596,18 +614,8 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, pfl->status |= 0x80; if (!pfl->counter) { - hwaddr mask = pfl->writeblock_size - 1; - mask = ~mask; - trace_pflash_write(pfl->name, "block write finished"); pfl->wcycle++; - if (!pfl->ro) { - /* Flush the entire write buffer onto backing storage. */ - /* FIXME premature! */ - pflash_update(pfl, offset & mask, pfl->writeblock_size); - } else { - pfl->status |= 0x10; /* Programming error */ - } } pfl->counter--; @@ -619,20 +627,17 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, case 3: /* Confirm mode */ switch (pfl->cmd) { case 0xe8: /* Block write */ - if (cmd == 0xd0) { - /* FIXME this is where we should write out the buffer */ + if ((cmd == 0xd0) && !(pfl->status & 0x10)) { + pflash_blk_write_flush(pfl); pfl->wcycle = 0; pfl->status |= 0x80; } else { - qemu_log_mask(LOG_UNIMP, - "%s: Aborting write to buffer not implemented," - " the data is already written to storage!\n" - "Flash device reset into READ mode.\n", - __func__); + pflash_blk_write_abort(pfl); goto mode_read_array; } break; default: + pflash_blk_write_abort(pfl); goto error_flash; } break; @@ -645,7 +650,7 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, error_flash: qemu_log_mask(LOG_UNIMP, "%s: Unimplemented flash cmd sequence " - "(offset " TARGET_FMT_plx ", wcycle 0x%x cmd 0x%x value 0x%x)" + "(offset " HWADDR_FMT_plx ", wcycle 0x%x cmd 0x%x value 0x%x)" "\n", __func__, offset, pfl->wcycle, pfl->cmd, value); mode_read_array: @@ -843,8 +848,8 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) } if (pfl->blk) { - if (!blk_check_size_and_read_all(pfl->blk, pfl->storage, total_len, - errp)) { + if (!blk_check_size_and_read_all(pfl->blk, dev, pfl->storage, + total_len, errp)) { vmstate_unregister_ram(&pfl->mem, DEVICE(pfl)); return; } @@ -866,6 +871,9 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) pfl->cmd = 0x00; pfl->status = 0x80; /* WSM ready */ pflash_cfi01_fill_cfi_table(pfl); + + pfl->blk_bytes = g_malloc(pfl->writeblock_size); + pfl->blk_offset = -1; } static void pflash_cfi01_system_reset(DeviceState *dev) @@ -885,6 +893,8 @@ static void pflash_cfi01_system_reset(DeviceState *dev) * This model deliberately ignores this delay. */ pfl->status = 0x80; + + pfl->blk_offset = -1; } static Property pflash_cfi01_properties[] = { @@ -892,7 +902,7 @@ static Property pflash_cfi01_properties[] = { /* num-blocks is the number of blocks actually visible to the guest, * ie the total size of the device divided by the sector length. * If we're emulating flash devices wired in parallel the actual - * number of blocks per indvidual device will differ. + * number of blocks per individual device will differ. */ DEFINE_PROP_UINT32("num-blocks", PFlashCFI01, nb_blocs, 0), DEFINE_PROP_UINT64("sector-length", PFlashCFI01, sector_len, 0), diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index 02c514fb6e..2314142373 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -400,8 +400,8 @@ static void pflash_update(PFlashCFI02 *pfl, int offset, int size) /* widen to sector boundaries */ offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE); offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE); - ret = blk_pwrite(pfl->blk, offset, pfl->storage + offset, - offset_end - offset, 0); + ret = blk_pwrite(pfl->blk, offset, offset_end - offset, + pfl->storage + offset, 0); if (ret < 0) { /* TODO set error bit in status */ error_report("Could not update PFLASH: %s", strerror(-ret)); @@ -546,7 +546,7 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value, } goto reset_flash; } - trace_pflash_data_write(pfl->name, offset, width, value, 0); + trace_pflash_data_write(pfl->name, offset, width, value); if (!pfl->ro) { p = (uint8_t *)pfl->storage + offset; if (pfl->be) { @@ -902,7 +902,7 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp) } if (pfl->blk) { - if (!blk_check_size_and_read_all(pfl->blk, pfl->storage, + if (!blk_check_size_and_read_all(pfl->blk, dev, pfl->storage, pfl->chip_len, errp)) { vmstate_unregister_ram(&pfl->orig_mem, DEVICE(pfl)); return; diff --git a/hw/block/swim.c b/hw/block/swim.c index 333da08ce0..44761c11cb 100644 --- a/hw/block/swim.c +++ b/hw/block/swim.c @@ -19,25 +19,30 @@ #include "hw/block/block.h" #include "hw/block/swim.h" #include "hw/qdev-properties.h" +#include "trace.h" + + +/* IWM latch bits */ + +#define IWMLB_PHASE0 0 +#define IWMLB_PHASE1 1 +#define IWMLB_PHASE2 2 +#define IWMLB_PHASE3 3 +#define IWMLB_MOTORON 4 +#define IWMLB_DRIVESEL 5 +#define IWMLB_L6 6 +#define IWMLB_L7 7 /* IWM registers */ -#define IWM_PH0L 0 -#define IWM_PH0H 1 -#define IWM_PH1L 2 -#define IWM_PH1H 3 -#define IWM_PH2L 4 -#define IWM_PH2H 5 -#define IWM_PH3L 6 -#define IWM_PH3H 7 -#define IWM_MTROFF 8 -#define IWM_MTRON 9 -#define IWM_INTDRIVE 10 -#define IWM_EXTDRIVE 11 -#define IWM_Q6L 12 -#define IWM_Q6H 13 -#define IWM_Q7L 14 -#define IWM_Q7H 15 +#define IWM_READALLONES 0 +#define IWM_READDATA 1 +#define IWM_READSTATUS0 2 +#define IWM_READSTATUS1 3 +#define IWM_READWHANDSHAKE0 4 +#define IWM_READWHANDSHAKE1 5 +#define IWM_WRITESETMODE 6 +#define IWM_WRITEDATA 7 /* SWIM registers */ @@ -61,8 +66,9 @@ #define REG_SHIFT 9 -#define SWIM_MODE_IWM 0 -#define SWIM_MODE_SWIM 1 +#define SWIM_MODE_STATUS_BIT 6 +#define SWIM_MODE_IWM 0 +#define SWIM_MODE_ISM 1 /* bits in phase register */ @@ -125,6 +131,18 @@ #define SWIM_HEDSEL 0x20 #define SWIM_MOTON 0x80 +static const char *iwm_reg_names[] = { + "READALLONES", "READDATA", "READSTATUS0", "READSTATUS1", + "READWHANDSHAKE0", "READWHANDSHAKE1", "WRITESETMODE", "WRITEDATA" +}; + +static const char *ism_reg_names[] = { + "WRITE_DATA", "WRITE_MARK", "WRITE_CRC", "WRITE_PARAMETER", + "WRITE_PHASE", "WRITE_SETUP", "WRITE_MODE0", "WRITE_MODE1", + "READ_DATA", "READ_MARK", "READ_ERROR", "READ_PARAMETER", + "READ_PHASE", "READ_SETUP", "READ_STATUS", "READ_HANDSHAKE" +}; + static void fd_recalibrate(FDrive *drive) { } @@ -259,73 +277,116 @@ static const TypeInfo swim_bus_info = { .instance_size = sizeof(SWIMBus), }; -static void iwmctrl_write(void *opaque, hwaddr reg, uint64_t value, +static void iwmctrl_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + SWIMCtrl *swimctrl = opaque; + uint8_t latch, reg, ism_bit; + + addr >>= REG_SHIFT; + + /* A3-A1 select a latch, A0 specifies the value */ + latch = (addr >> 1) & 7; + if (addr & 1) { + swimctrl->iwm_latches |= (1 << latch); + } else { + swimctrl->iwm_latches &= ~(1 << latch); + } + + reg = (swimctrl->iwm_latches & 0xc0) >> 5 | + (swimctrl->iwm_latches & 0x10) >> 4; + + swimctrl->iwmregs[reg] = value; + trace_swim_iwmctrl_write(reg, iwm_reg_names[reg], size, value); + + switch (reg) { + case IWM_WRITESETMODE: + /* detect sequence to switch from IWM mode to SWIM mode */ + ism_bit = (value & (1 << SWIM_MODE_STATUS_BIT)); + + switch (swimctrl->iwm_switch) { + case 0: + if (ism_bit) { /* 1 */ + swimctrl->iwm_switch++; + } + break; + case 1: + if (!ism_bit) { /* 0 */ + swimctrl->iwm_switch++; + } + break; + case 2: + if (ism_bit) { /* 1 */ + swimctrl->iwm_switch++; + } + break; + case 3: + if (ism_bit) { /* 1 */ + swimctrl->iwm_switch++; + + swimctrl->mode = SWIM_MODE_ISM; + swimctrl->swim_mode |= (1 << SWIM_MODE_STATUS_BIT); + swimctrl->iwm_switch = 0; + trace_swim_switch_to_ism(); + + /* Switch to ISM registers */ + memory_region_del_subregion(&swimctrl->swim, &swimctrl->iwm); + memory_region_add_subregion(&swimctrl->swim, 0x0, + &swimctrl->ism); + } + break; + } + break; + default: + break; + } +} + +static uint64_t iwmctrl_read(void *opaque, hwaddr addr, unsigned size) +{ + SWIMCtrl *swimctrl = opaque; + uint8_t latch, reg, value; + + addr >>= REG_SHIFT; + + /* A3-A1 select a latch, A0 specifies the value */ + latch = (addr >> 1) & 7; + if (addr & 1) { + swimctrl->iwm_latches |= (1 << latch); + } else { + swimctrl->iwm_latches &= ~(1 << latch); + } + + reg = (swimctrl->iwm_latches & 0xc0) >> 5 | + (swimctrl->iwm_latches & 0x10) >> 4; + + switch (reg) { + case IWM_READALLONES: + value = 0xff; + break; + default: + value = 0; + break; + } + + trace_swim_iwmctrl_read(reg, iwm_reg_names[reg], size, value); + return value; +} + +static const MemoryRegionOps swimctrl_iwm_ops = { + .write = iwmctrl_write, + .read = iwmctrl_read, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void ismctrl_write(void *opaque, hwaddr reg, uint64_t value, unsigned size) { SWIMCtrl *swimctrl = opaque; reg >>= REG_SHIFT; - swimctrl->regs[reg >> 1] = reg & 1; - - if (swimctrl->regs[IWM_Q6] && - swimctrl->regs[IWM_Q7]) { - if (swimctrl->regs[IWM_MTR]) { - /* data register */ - swimctrl->iwm_data = value; - } else { - /* mode register */ - swimctrl->iwm_mode = value; - /* detect sequence to switch from IWM mode to SWIM mode */ - switch (swimctrl->iwm_switch) { - case 0: - if (value == 0x57) { - swimctrl->iwm_switch++; - } - break; - case 1: - if (value == 0x17) { - swimctrl->iwm_switch++; - } - break; - case 2: - if (value == 0x57) { - swimctrl->iwm_switch++; - } - break; - case 3: - if (value == 0x57) { - swimctrl->mode = SWIM_MODE_SWIM; - swimctrl->iwm_switch = 0; - } - break; - } - } - } -} - -static uint64_t iwmctrl_read(void *opaque, hwaddr reg, unsigned size) -{ - SWIMCtrl *swimctrl = opaque; - - reg >>= REG_SHIFT; - - swimctrl->regs[reg >> 1] = reg & 1; - - return 0; -} - -static void swimctrl_write(void *opaque, hwaddr reg, uint64_t value, - unsigned size) -{ - SWIMCtrl *swimctrl = opaque; - - if (swimctrl->mode == SWIM_MODE_IWM) { - iwmctrl_write(opaque, reg, value, size); - return; - } - - reg >>= REG_SHIFT; + trace_swim_ismctrl_write(reg, ism_reg_names[reg], size, value); switch (reg) { case SWIM_WRITE_PHASE: @@ -333,28 +394,41 @@ static void swimctrl_write(void *opaque, hwaddr reg, uint64_t value, break; case SWIM_WRITE_MODE0: swimctrl->swim_mode &= ~value; + /* Any access to MODE0 register resets PRAM index */ + swimctrl->pram_idx = 0; + + if (!(swimctrl->swim_mode & (1 << SWIM_MODE_STATUS_BIT))) { + /* Clearing the mode bit switches to IWM mode */ + swimctrl->mode = SWIM_MODE_IWM; + swimctrl->iwm_latches = 0; + trace_swim_switch_to_iwm(); + + /* Switch to IWM registers */ + memory_region_del_subregion(&swimctrl->swim, &swimctrl->ism); + memory_region_add_subregion(&swimctrl->swim, 0x0, + &swimctrl->iwm); + } break; case SWIM_WRITE_MODE1: swimctrl->swim_mode |= value; break; + case SWIM_WRITE_PARAMETER: + swimctrl->pram[swimctrl->pram_idx++] = value; + swimctrl->pram_idx &= 0xf; + break; case SWIM_WRITE_DATA: case SWIM_WRITE_MARK: case SWIM_WRITE_CRC: - case SWIM_WRITE_PARAMETER: case SWIM_WRITE_SETUP: break; } } -static uint64_t swimctrl_read(void *opaque, hwaddr reg, unsigned size) +static uint64_t ismctrl_read(void *opaque, hwaddr reg, unsigned size) { SWIMCtrl *swimctrl = opaque; uint32_t value = 0; - if (swimctrl->mode == SWIM_MODE_IWM) { - return iwmctrl_read(opaque, reg, size); - } - reg >>= REG_SHIFT; switch (reg) { @@ -367,22 +441,31 @@ static uint64_t swimctrl_read(void *opaque, hwaddr reg, unsigned size) value = SWIM_SENSE; } break; + case SWIM_READ_PARAMETER: + value = swimctrl->pram[swimctrl->pram_idx++]; + swimctrl->pram_idx &= 0xf; + break; + case SWIM_READ_STATUS: + value = swimctrl->swim_status & ~(1 << SWIM_MODE_STATUS_BIT); + if (swimctrl->swim_mode == SWIM_MODE_ISM) { + value |= (1 << SWIM_MODE_STATUS_BIT); + } + break; case SWIM_READ_DATA: case SWIM_READ_MARK: case SWIM_READ_ERROR: - case SWIM_READ_PARAMETER: case SWIM_READ_SETUP: - case SWIM_READ_STATUS: break; } + trace_swim_ismctrl_read(reg, ism_reg_names[reg], size, value); return value; } -static const MemoryRegionOps swimctrl_mem_ops = { - .write = swimctrl_write, - .read = swimctrl_read, - .endianness = DEVICE_NATIVE_ENDIAN, +static const MemoryRegionOps swimctrl_ism_ops = { + .write = ismctrl_write, + .read = ismctrl_read, + .endianness = DEVICE_BIG_ENDIAN, }; static void sysbus_swim_reset(DeviceState *d) @@ -393,13 +476,11 @@ static void sysbus_swim_reset(DeviceState *d) ctrl->mode = 0; ctrl->iwm_switch = 0; - for (i = 0; i < 8; i++) { - ctrl->regs[i] = 0; - } - ctrl->iwm_data = 0; - ctrl->iwm_mode = 0; + memset(ctrl->iwmregs, 0, sizeof(ctrl->iwmregs)); + ctrl->swim_phase = 0; ctrl->swim_mode = 0; + memset(ctrl->ismregs, 0, sizeof(ctrl->ismregs)); for (i = 0; i < SWIM_MAX_FD; i++) { fd_recalibrate(&ctrl->drives[i]); } @@ -411,9 +492,12 @@ static void sysbus_swim_init(Object *obj) Swim *sbs = SWIM(obj); SWIMCtrl *swimctrl = &sbs->ctrl; - memory_region_init_io(&swimctrl->iomem, obj, &swimctrl_mem_ops, swimctrl, - "swim", 0x2000); - sysbus_init_mmio(sbd, &swimctrl->iomem); + memory_region_init(&swimctrl->swim, obj, "swim", 0x2000); + memory_region_init_io(&swimctrl->iwm, obj, &swimctrl_iwm_ops, swimctrl, + "iwm", 0x2000); + memory_region_init_io(&swimctrl->ism, obj, &swimctrl_ism_ops, swimctrl, + "ism", 0x2000); + sysbus_init_mmio(sbd, &swimctrl->swim); } static void sysbus_swim_realize(DeviceState *dev, Error **errp) @@ -423,13 +507,16 @@ static void sysbus_swim_realize(DeviceState *dev, Error **errp) qbus_init(&swimctrl->bus, sizeof(SWIMBus), TYPE_SWIM_BUS, dev, NULL); swimctrl->bus.ctrl = swimctrl; + + /* Default register set is IWM */ + memory_region_add_subregion(&swimctrl->swim, 0x0, &swimctrl->iwm); } static const VMStateDescription vmstate_fdrive = { .name = "fdrive", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_END_OF_LIST() }, }; @@ -438,14 +525,14 @@ static const VMStateDescription vmstate_swim = { .name = "swim", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(mode, SWIMCtrl), /* IWM mode */ VMSTATE_INT32(iwm_switch, SWIMCtrl), - VMSTATE_UINT16_ARRAY(regs, SWIMCtrl, 8), - VMSTATE_UINT8(iwm_data, SWIMCtrl), - VMSTATE_UINT8(iwm_mode, SWIMCtrl), + VMSTATE_UINT8(iwm_latches, SWIMCtrl), + VMSTATE_UINT8_ARRAY(iwmregs, SWIMCtrl, 8), /* SWIM mode */ + VMSTATE_UINT8_ARRAY(ismregs, SWIMCtrl, 16), VMSTATE_UINT8(swim_phase, SWIMCtrl), VMSTATE_UINT8(swim_mode, SWIMCtrl), /* Drives */ @@ -458,7 +545,7 @@ static const VMStateDescription vmstate_swim = { static const VMStateDescription vmstate_sysbus_swim = { .name = "SWIM", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(ctrl, Swim, 0, vmstate_swim, SWIMCtrl), VMSTATE_END_OF_LIST() } diff --git a/hw/block/tc58128.c b/hw/block/tc58128.c index bfc27ad899..0984e37417 100644 --- a/hw/block/tc58128.c +++ b/hw/block/tc58128.c @@ -62,24 +62,24 @@ static void init_dev(tc58128_dev * dev, const char *filename) dev->flash_contents = g_malloc(FLASH_SIZE); memset(dev->flash_contents, 0xff, FLASH_SIZE); if (filename) { - /* Load flash image skipping the first block */ + /* Load flash image skipping the first block */ ret = load_image_size(filename, dev->flash_contents + 528 * 32, FLASH_SIZE - 528 * 32); - if (ret < 0) { + if (ret < 0) { if (!qtest_enabled()) { error_report("Could not load flash image %s", filename); exit(1); } - } else { - /* Build first block with number of blocks */ + } else { + /* Build first block with number of blocks */ blocks = DIV_ROUND_UP(ret, 528 * 32); - dev->flash_contents[0] = blocks & 0xff; - dev->flash_contents[1] = (blocks >> 8) & 0xff; - dev->flash_contents[2] = (blocks >> 16) & 0xff; - dev->flash_contents[3] = (blocks >> 24) & 0xff; - fprintf(stderr, "loaded %d bytes for %s into flash\n", ret, - filename); - } + dev->flash_contents[0] = blocks & 0xff; + dev->flash_contents[1] = (blocks >> 8) & 0xff; + dev->flash_contents[2] = (blocks >> 16) & 0xff; + dev->flash_contents[3] = (blocks >> 24) & 0xff; + fprintf(stderr, "loaded %d bytes for %s into flash\n", ret, + filename); + } } } @@ -87,26 +87,26 @@ static void handle_command(tc58128_dev * dev, uint8_t command) { switch (command) { case 0xff: - fprintf(stderr, "reset flash device\n"); - dev->state = WAIT; - break; + fprintf(stderr, "reset flash device\n"); + dev->state = WAIT; + break; case 0x00: - fprintf(stderr, "read mode 1\n"); - dev->state = READ1; - dev->address_cycle = 0; - break; + fprintf(stderr, "read mode 1\n"); + dev->state = READ1; + dev->address_cycle = 0; + break; case 0x01: - fprintf(stderr, "read mode 2\n"); - dev->state = READ2; - dev->address_cycle = 0; - break; + fprintf(stderr, "read mode 2\n"); + dev->state = READ2; + dev->address_cycle = 0; + break; case 0x50: - fprintf(stderr, "read mode 3\n"); - dev->state = READ3; - dev->address_cycle = 0; - break; + fprintf(stderr, "read mode 3\n"); + dev->state = READ3; + dev->address_cycle = 0; + break; default: - fprintf(stderr, "unknown flash command 0x%02x\n", command); + fprintf(stderr, "unknown flash command 0x%02x\n", command); abort(); } } @@ -117,28 +117,28 @@ static void handle_address(tc58128_dev * dev, uint8_t data) case READ1: case READ2: case READ3: - switch (dev->address_cycle) { - case 0: - dev->address = data; - if (dev->state == READ2) - dev->address |= 0x100; - else if (dev->state == READ3) - dev->address |= 0x200; - break; - case 1: - dev->address += data * 528 * 0x100; - break; - case 2: - dev->address += data * 528; - fprintf(stderr, "address pointer in flash: 0x%08x\n", - dev->address); - break; - default: - /* Invalid data */ + switch (dev->address_cycle) { + case 0: + dev->address = data; + if (dev->state == READ2) + dev->address |= 0x100; + else if (dev->state == READ3) + dev->address |= 0x200; + break; + case 1: + dev->address += data * 528 * 0x100; + break; + case 2: + dev->address += data * 528; + fprintf(stderr, "address pointer in flash: 0x%08x\n", + dev->address); + break; + default: + /* Invalid data */ abort(); - } - dev->address_cycle++; - break; + } + dev->address_cycle++; + break; default: abort(); } @@ -148,7 +148,7 @@ static uint8_t handle_read(tc58128_dev * dev) { #if 0 if (dev->address % 0x100000 == 0) - fprintf(stderr, "reading flash at address 0x%08x\n", dev->address); + fprintf(stderr, "reading flash at address 0x%08x\n", dev->address); #endif return dev->flash_contents[dev->address++]; } @@ -163,31 +163,31 @@ static int tc58128_cb(uint16_t porta, uint16_t portb, int dev; if ((porta & CE1) == 0) - dev = 0; + dev = 0; else if ((porta & CE2) == 0) - dev = 1; + dev = 1; else - return 0; /* No device selected */ + return 0; /* No device selected */ if ((porta & RE) && (porta & WE)) { - /* Nothing to do, assert ready and return to input state */ - *periph_portadir &= 0xff00; - *periph_portadir |= RDY(dev); - *periph_pdtra |= RDY(dev); - return 1; + /* Nothing to do, assert ready and return to input state */ + *periph_portadir &= 0xff00; + *periph_portadir |= RDY(dev); + *periph_pdtra |= RDY(dev); + return 1; } if (porta & CLE) { - /* Command */ - assert((porta & WE) == 0); - handle_command(&tc58128_devs[dev], porta & 0x00ff); + /* Command */ + assert((porta & WE) == 0); + handle_command(&tc58128_devs[dev], porta & 0x00ff); } else if (porta & ALE) { - assert((porta & WE) == 0); - handle_address(&tc58128_devs[dev], porta & 0x00ff); + assert((porta & WE) == 0); + handle_address(&tc58128_devs[dev], porta & 0x00ff); } else if ((porta & RE) == 0) { - *periph_portadir |= 0x00ff; - *periph_pdtra &= 0xff00; - *periph_pdtra |= handle_read(&tc58128_devs[dev]); + *periph_portadir |= 0x00ff; + *periph_pdtra &= 0xff00; + *periph_pdtra |= handle_read(&tc58128_devs[dev]); } else { abort(); } @@ -195,13 +195,16 @@ static int tc58128_cb(uint16_t porta, uint16_t portb, } static sh7750_io_device tc58128 = { - RE | WE, /* Port A triggers */ - 0, /* Port B triggers */ - tc58128_cb /* Callback */ + RE | WE, /* Port A triggers */ + 0, /* Port B triggers */ + tc58128_cb /* Callback */ }; int tc58128_init(struct SH7750State *s, const char *zone1, const char *zone2) { + if (!qtest_enabled()) { + warn_report_once("The TC58128 flash device is deprecated"); + } init_dev(&tc58128_devs[0], zone1); init_dev(&tc58128_devs[1], zone2); return sh7750_register_io_device(s, &tc58128); diff --git a/hw/block/trace-events b/hw/block/trace-events index d86b53520c..cc9a9f2460 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -12,7 +12,8 @@ fdctrl_tc_pulse(int level) "TC pulse: %u" pflash_chip_erase_invalid(const char *name, uint64_t offset) "%s: chip erase: invalid address 0x%" PRIx64 pflash_chip_erase_start(const char *name) "%s: start chip erase" pflash_data_read(const char *name, uint64_t offset, unsigned size, uint32_t value) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x" -pflash_data_write(const char *name, uint64_t offset, unsigned size, uint32_t value, uint64_t counter) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x counter:0x%016"PRIx64 +pflash_data_write(const char *name, uint64_t offset, unsigned size, uint32_t value) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x" +pflash_data_write_block(const char *name, uint64_t offset, unsigned size, uint32_t value, uint64_t counter) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x counter:0x%016"PRIx64 pflash_device_id(const char *name, uint16_t id) "%s: read device ID: 0x%04x" pflash_device_info(const char *name, uint64_t offset) "%s: read device information offset:0x%04" PRIx64 pflash_erase_complete(const char *name) "%s: sector erase complete" @@ -32,7 +33,9 @@ pflash_unlock0_failed(const char *name, uint64_t offset, uint8_t cmd, uint16_t a pflash_unlock1_failed(const char *name, uint64_t offset, uint8_t cmd) "%s: unlock0 failed 0x%" PRIx64 " 0x%02x" pflash_unsupported_device_configuration(const char *name, uint8_t width, uint8_t max) "%s: unsupported device configuration: device_width:%d max_device_width:%d" pflash_write(const char *name, const char *str) "%s: %s" -pflash_write_block(const char *name, uint32_t value) "%s: block write: bytes:0x%x" +pflash_write_block_start(const char *name, uint32_t value) "%s: block write start: bytes:0x%x" +pflash_write_block_flush(const char *name) "%s: block write flush" +pflash_write_block_abort(const char *name) "%s: block write abort" pflash_write_block_erase(const char *name, uint64_t offset, uint64_t len) "%s: block erase offset:0x%" PRIx64 " bytes:0x%" PRIx64 pflash_write_failed(const char *name, uint64_t offset, uint8_t cmd) "%s: command failed 0x%" PRIx64 " 0x%02x" pflash_write_invalid(const char *name, uint8_t cmd) "%s: invalid write for command 0x%02x" @@ -44,9 +47,16 @@ pflash_write_unknown(const char *name, uint8_t cmd) "%s: unknown command 0x%02x" # virtio-blk.c virtio_blk_req_complete(void *vdev, void *req, int status) "vdev %p req %p status %d" virtio_blk_rw_complete(void *vdev, void *req, int ret) "vdev %p req %p ret %d" +virtio_blk_zone_report_complete(void *vdev, void *req, unsigned int nr_zones, int ret) "vdev %p req %p nr_zones %u ret %d" +virtio_blk_zone_mgmt_complete(void *vdev, void *req, int ret) "vdev %p req %p ret %d" +virtio_blk_zone_append_complete(void *vdev, void *req, int64_t sector, int ret) "vdev %p req %p, append sector 0x%" PRIx64 " ret %d" virtio_blk_handle_write(void *vdev, void *req, uint64_t sector, size_t nsectors) "vdev %p req %p sector %"PRIu64" nsectors %zu" virtio_blk_handle_read(void *vdev, void *req, uint64_t sector, size_t nsectors) "vdev %p req %p sector %"PRIu64" nsectors %zu" virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint64_t offset, size_t size, bool is_write) "vdev %p mrb %p start %d num_reqs %d offset %"PRIu64" size %zu is_write %d" +virtio_blk_handle_zone_report(void *vdev, void *req, int64_t sector, unsigned int nr_zones) "vdev %p req %p sector 0x%" PRIx64 " nr_zones %u" +virtio_blk_handle_zone_mgmt(void *vdev, void *req, uint8_t op, int64_t sector, int64_t len) "vdev %p req %p op 0x%x sector 0x%" PRIx64 " len 0x%" PRIx64 "" +virtio_blk_handle_zone_reset_all(void *vdev, void *req, int64_t sector, int64_t len) "vdev %p req %p sector 0x%" PRIx64 " cap 0x%" PRIx64 "" +virtio_blk_handle_zone_append(void *vdev, void *req, int64_t sector) "vdev %p req %p, append sector 0x%" PRIx64 "" # hd-geometry.c hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d" @@ -80,5 +90,14 @@ m25p80_page_program(void *s, uint32_t addr, uint8_t tx) "[%p] page program cur_a 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_read_sfdp(void *s, uint32_t addr, uint8_t v) "[%p] Read SFDP 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" + +# swim.c +swim_ismctrl_read(int reg, const char *name, unsigned size, uint64_t value) "reg=%d [%s] size=%u value=0x%"PRIx64 +swim_ismctrl_write(int reg, const char *name, unsigned size, uint64_t value) "reg=%d [%s] size=%u value=0x%"PRIx64 +swim_iwmctrl_read(int reg, const char *name, unsigned size, uint64_t value) "reg=%d [%s] size=%u value=0x%"PRIx64 +swim_iwmctrl_write(int reg, const char *name, unsigned size, uint64_t value) "reg=%d [%s] size=%u value=0x%"PRIx64 +swim_switch_to_ism(void) "switch from IWM to ISM mode" +swim_switch_to_iwm(void) "switch from ISM to IWM mode" diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 5dca4eab09..9e6bbc6950 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -23,6 +23,7 @@ #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" +#include "hw/virtio/virtio-blk-common.h" #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-user-blk.h" #include "hw/virtio/virtio.h" @@ -31,8 +32,6 @@ #include "sysemu/sysemu.h" #include "sysemu/runstate.h" -#define REALIZE_CONNECTION_RETRIES 3 - static const int user_feature_bits[] = { VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_SEG_MAX, @@ -51,6 +50,7 @@ static const int user_feature_bits[] = { VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_RING_PACKED, VIRTIO_F_IOMMU_PLATFORM, + VIRTIO_F_RING_RESET, VHOST_INVALID_FEATURE_BIT }; @@ -63,7 +63,7 @@ static void vhost_user_blk_update_config(VirtIODevice *vdev, uint8_t *config) /* Our num_queues overrides the device backend */ virtio_stw_p(vdev, &s->blkcfg.num_queues, s->num_queues); - memcpy(config, &s->blkcfg, sizeof(struct virtio_blk_config)); + memcpy(config, &s->blkcfg, vdev->config_len); } static void vhost_user_blk_set_config(VirtIODevice *vdev, const uint8_t *config) @@ -79,7 +79,7 @@ static void vhost_user_blk_set_config(VirtIODevice *vdev, const uint8_t *config) ret = vhost_dev_set_config(&s->dev, &blkcfg->wce, offsetof(struct virtio_blk_config, wce), sizeof(blkcfg->wce), - VHOST_SET_CONFIG_TYPE_MASTER); + VHOST_SET_CONFIG_TYPE_FRONTEND); if (ret) { error_report("set device config space failed"); return; @@ -91,24 +91,23 @@ static void vhost_user_blk_set_config(VirtIODevice *vdev, const uint8_t *config) static int vhost_user_blk_handle_config_change(struct vhost_dev *dev) { int ret; - struct virtio_blk_config blkcfg; + VirtIODevice *vdev = dev->vdev; VHostUserBlk *s = VHOST_USER_BLK(dev->vdev); Error *local_err = NULL; - ret = vhost_dev_get_config(dev, (uint8_t *)&blkcfg, - sizeof(struct virtio_blk_config), - &local_err); + if (!dev->started) { + return 0; + } + + ret = vhost_dev_get_config(dev, (uint8_t *)&s->blkcfg, + vdev->config_len, &local_err); if (ret < 0) { error_report_err(local_err); return ret; } - /* valid for resize only */ - if (blkcfg.capacity != s->blkcfg.capacity) { - s->blkcfg.capacity = blkcfg.capacity; - memcpy(dev->vdev->config, &s->blkcfg, sizeof(struct virtio_blk_config)); - virtio_notify_config(dev->vdev); - } + memcpy(dev->vdev->config, &s->blkcfg, vdev->config_len); + virtio_notify_config(dev->vdev); return 0; } @@ -163,13 +162,6 @@ static int vhost_user_blk_start(VirtIODevice *vdev, Error **errp) goto err_guest_notifiers; } - ret = vhost_dev_start(&s->dev, vdev); - if (ret < 0) { - error_setg_errno(errp, -ret, "Error starting vhost"); - goto err_guest_notifiers; - } - s->started_vu = true; - /* guest_notifier_mask/pending not used yet, so just unmask * everything here. virtio-pci will do the right thing by * enabling/disabling irqfd. @@ -178,9 +170,20 @@ static int vhost_user_blk_start(VirtIODevice *vdev, Error **errp) vhost_virtqueue_mask(&s->dev, vdev, i, false); } + s->dev.vq_index_end = s->dev.nvqs; + ret = vhost_dev_start(&s->dev, vdev, true); + if (ret < 0) { + error_setg_errno(errp, -ret, "Error starting vhost"); + goto err_guest_notifiers; + } + s->started_vu = true; + return ret; err_guest_notifiers: + for (i = 0; i < s->dev.nvqs; i++) { + vhost_virtqueue_mask(&s->dev, vdev, i, true); + } k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); err_host_notifiers: vhost_dev_disable_notifiers(&s->dev, vdev); @@ -203,7 +206,7 @@ static void vhost_user_blk_stop(VirtIODevice *vdev) return; } - vhost_dev_stop(&s->dev, vdev); + vhost_dev_stop(&s->dev, vdev, true); ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); if (ret < 0) { @@ -217,19 +220,15 @@ static void vhost_user_blk_stop(VirtIODevice *vdev) static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status) { VHostUserBlk *s = VHOST_USER_BLK(vdev); - bool should_start = virtio_device_started(vdev, status); + bool should_start = virtio_device_should_start(vdev, status); Error *local_err = NULL; int ret; - if (!vdev->vm_running) { - should_start = false; - } - if (!s->connected) { return; } - if (s->dev.started == should_start) { + if (vhost_dev_is_started(&s->dev) == should_start) { return; } @@ -259,12 +258,7 @@ static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev, virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE); virtio_add_feature(&features, VIRTIO_BLK_F_FLUSH); virtio_add_feature(&features, VIRTIO_BLK_F_RO); - virtio_add_feature(&features, VIRTIO_BLK_F_DISCARD); - virtio_add_feature(&features, VIRTIO_BLK_F_WRITE_ZEROES); - if (s->config_wce) { - virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE); - } if (s->num_queues > 1) { virtio_add_feature(&features, VIRTIO_BLK_F_MQ); } @@ -286,7 +280,7 @@ static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) return; } - if (s->dev.started) { + if (vhost_dev_is_started(&s->dev)) { return; } @@ -327,7 +321,6 @@ static int vhost_user_blk_connect(DeviceState *dev, Error **errp) if (s->connected) { return 0; } - s->connected = true; s->dev.num_queues = s->num_queues; s->dev.nvqs = s->num_queues; @@ -337,21 +330,21 @@ static int vhost_user_blk_connect(DeviceState *dev, Error **errp) vhost_dev_set_config_notifier(&s->dev, &blk_ops); + s->vhost_user.supports_config = true; ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0, errp); if (ret < 0) { return ret; } + s->connected = true; + /* restore vhost state */ if (virtio_device_started(vdev, vdev->status)) { ret = vhost_user_blk_start(vdev, errp); - if (ret < 0) { - return ret; - } } - return 0; + return ret; } static void vhost_user_blk_disconnect(DeviceState *dev) @@ -367,17 +360,10 @@ static void vhost_user_blk_disconnect(DeviceState *dev) vhost_user_blk_stop(vdev); vhost_dev_cleanup(&s->dev); -} -static void vhost_user_blk_chr_closed_bh(void *opaque) -{ - DeviceState *dev = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserBlk *s = VHOST_USER_BLK(vdev); - - vhost_user_blk_disconnect(dev); + /* Re-instate the event handler for new connections */ qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, vhost_user_blk_event, - NULL, opaque, NULL, true); + NULL, dev, NULL, true); } static void vhost_user_blk_event(void *opaque, QEMUChrEvent event) @@ -396,27 +382,9 @@ static void vhost_user_blk_event(void *opaque, QEMUChrEvent event) } break; case CHR_EVENT_CLOSED: - if (!runstate_check(RUN_STATE_SHUTDOWN)) { - /* - * A close event may happen during a read/write, but vhost - * code assumes the vhost_dev remains setup, so delay the - * stop & clear. - */ - AioContext *ctx = qemu_get_current_aio_context(); - - qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, NULL, NULL, - NULL, NULL, false); - aio_bh_schedule_oneshot(ctx, vhost_user_blk_chr_closed_bh, opaque); - - /* - * Move vhost device to the stopped state. The vhost-user device - * will be clean up and disconnected in BH. This can be useful in - * the vhost migration code. If disconnect was caught there is an - * option for the general vhost code to get the dev state without - * knowing its type (in this case vhost-user). - */ - s->dev.started = false; - } + /* defer close until later to avoid circular close */ + vhost_user_async_close(dev, &s->chardev, &s->dev, + vhost_user_blk_disconnect, vhost_user_blk_event); break; case CHR_EVENT_BREAK: case CHR_EVENT_MUX_IN: @@ -428,7 +396,7 @@ static void vhost_user_blk_event(void *opaque, QEMUChrEvent event) static int vhost_user_blk_realize_connect(VHostUserBlk *s, Error **errp) { - DeviceState *dev = &s->parent_obj.parent_obj; + DeviceState *dev = DEVICE(s); int ret; s->connected = false; @@ -446,7 +414,7 @@ static int vhost_user_blk_realize_connect(VHostUserBlk *s, Error **errp) assert(s->connected); ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg, - sizeof(struct virtio_blk_config), errp); + VIRTIO_DEVICE(s)->config_len, errp); if (ret < 0) { qemu_chr_fe_disconnect(&s->chardev); vhost_dev_cleanup(&s->dev); @@ -461,6 +429,7 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) ERRP_GUARD(); VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostUserBlk *s = VHOST_USER_BLK(vdev); + size_t config_size; int retries; int i, ret; @@ -491,8 +460,9 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) return; } - virtio_init(vdev, VIRTIO_ID_BLOCK, - sizeof(struct virtio_blk_config)); + config_size = virtio_get_config_size(&virtio_blk_cfg_size_params, + vdev->host_features); + virtio_init(vdev, VIRTIO_ID_BLOCK, config_size); s->virtqs = g_new(VirtQueue *, s->num_queues); for (i = 0; i < s->num_queues; i++) { @@ -503,7 +473,7 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) s->inflight = g_new0(struct vhost_inflight, 1); s->vhost_vqs = g_new0(struct vhost_virtqueue, s->num_queues); - retries = REALIZE_CONNECTION_RETRIES; + retries = VU_REALIZE_CONN_RETRIES; assert(!*errp); do { if (*errp) { @@ -579,7 +549,7 @@ static const VMStateDescription vmstate_vhost_user_blk = { .name = "vhost-user-blk", .minimum_version_id = 1, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, @@ -590,7 +560,12 @@ static Property vhost_user_blk_properties[] = { DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues, VHOST_USER_BLK_AUTO_NUM_QUEUES), DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128), - DEFINE_PROP_BIT("config-wce", VHostUserBlk, config_wce, 0, true), + DEFINE_PROP_BIT64("config-wce", VHostUserBlk, parent_obj.host_features, + VIRTIO_BLK_F_CONFIG_WCE, true), + DEFINE_PROP_BIT64("discard", VHostUserBlk, parent_obj.host_features, + VIRTIO_BLK_F_DISCARD, true), + DEFINE_PROP_BIT64("write-zeroes", VHostUserBlk, parent_obj.host_features, + VIRTIO_BLK_F_WRITE_ZEROES, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/block/virtio-blk-common.c b/hw/block/virtio-blk-common.c new file mode 100644 index 0000000000..e2f8e2f6da --- /dev/null +++ b/hw/block/virtio-blk-common.c @@ -0,0 +1,41 @@ +/* + * Virtio Block Device common helpers + * + * Copyright IBM, Corp. 2007 + * + * 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. + */ + +#include "qemu/osdep.h" + +#include "standard-headers/linux/virtio_blk.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-blk-common.h" + +/* Config size before the discard support (hide associated config fields) */ +#define VIRTIO_BLK_CFG_SIZE offsetof(struct virtio_blk_config, \ + max_discard_sectors) + +/* + * Starting from the discard feature, we can use this array to properly + * set the config size depending on the features enabled. + */ +static const VirtIOFeature feature_sizes[] = { + {.flags = 1ULL << VIRTIO_BLK_F_DISCARD, + .end = endof(struct virtio_blk_config, discard_sector_alignment)}, + {.flags = 1ULL << VIRTIO_BLK_F_WRITE_ZEROES, + .end = endof(struct virtio_blk_config, write_zeroes_may_unmap)}, + {.flags = 1ULL << VIRTIO_BLK_F_ZONED, + .end = endof(struct virtio_blk_config, zoned)}, + {} +}; + +const VirtIOConfigSizeParams virtio_blk_cfg_size_params = { + .min_size = VIRTIO_BLK_CFG_SIZE, + .max_size = sizeof(struct virtio_blk_config), + .feature_sizes = feature_sizes +}; diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index cd804795c6..bb86e65f65 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -12,19 +12,21 @@ */ #include "qemu/osdep.h" +#include "qemu/defer-call.h" #include "qapi/error.h" #include "qemu/iov.h" #include "qemu/module.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +#include "block/block_int.h" #include "trace.h" #include "hw/block/block.h" #include "hw/qdev-properties.h" #include "sysemu/blockdev.h" +#include "sysemu/block-ram-registrar.h" #include "sysemu/sysemu.h" #include "sysemu/runstate.h" #include "hw/virtio/virtio-blk.h" -#include "dataplane/virtio-blk.h" #include "scsi/constants.h" #ifdef __linux__ # include @@ -32,30 +34,10 @@ #include "hw/virtio/virtio-bus.h" #include "migration/qemu-file-types.h" #include "hw/virtio/virtio-access.h" +#include "hw/virtio/virtio-blk-common.h" #include "qemu/coroutine.h" -/* Config size before the discard support (hide associated config fields) */ -#define VIRTIO_BLK_CFG_SIZE offsetof(struct virtio_blk_config, \ - max_discard_sectors) -/* - * Starting from the discard feature, we can use this array to properly - * set the config size depending on the features enabled. - */ -static const VirtIOFeature feature_sizes[] = { - {.flags = 1ULL << VIRTIO_BLK_F_DISCARD, - .end = endof(struct virtio_blk_config, discard_sector_alignment)}, - {.flags = 1ULL << VIRTIO_BLK_F_WRITE_ZEROES, - .end = endof(struct virtio_blk_config, write_zeroes_may_unmap)}, - {} -}; - -static void virtio_blk_set_config_size(VirtIOBlock *s, uint64_t host_features) -{ - s->config_size = MAX(VIRTIO_BLK_CFG_SIZE, - virtio_feature_get_config_size(feature_sizes, host_features)); - - assert(s->config_size <= sizeof(struct virtio_blk_config)); -} +static void virtio_blk_ioeventfd_attach(VirtIOBlock *s); static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq, VirtIOBlockReq *req) @@ -84,8 +66,8 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) iov_discard_undo(&req->inhdr_undo); iov_discard_undo(&req->outhdr_undo); virtqueue_push(req->vq, &req->elem, req->in_len); - if (s->dataplane_started && !s->dataplane_disabled) { - virtio_blk_data_plane_notify(s->dataplane, req->vq); + if (qemu_in_iothread()) { + virtio_notify_irqfd(vdev, req->vq); } else { virtio_notify(vdev, req->vq); } @@ -101,8 +83,11 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, /* Break the link as the next request is going to be parsed from the * ring again. Otherwise we may end up doing a double completion! */ req->mr_next = NULL; - req->next = s->rq; - s->rq = req; + + WITH_QEMU_LOCK_GUARD(&s->rq_lock) { + req->next = s->rq; + s->rq = req; + } } else if (action == BLOCK_ERROR_ACTION_REPORT) { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); if (acct_failed) { @@ -121,7 +106,6 @@ static void virtio_blk_rw_complete(void *opaque, int ret) VirtIOBlock *s = next->dev; VirtIODevice *vdev = VIRTIO_DEVICE(s); - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); while (next) { VirtIOBlockReq *req = next; next = req->mr_next; @@ -154,7 +138,6 @@ static void virtio_blk_rw_complete(void *opaque, int ret) block_acct_done(blk_get_stats(s->blk), &req->acct); virtio_blk_free_request(req); } - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); } static void virtio_blk_flush_complete(void *opaque, int ret) @@ -162,19 +145,13 @@ static void virtio_blk_flush_complete(void *opaque, int ret) VirtIOBlockReq *req = opaque; VirtIOBlock *s = req->dev; - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); - if (ret) { - if (virtio_blk_handle_rw_error(req, -ret, 0, true)) { - goto out; - } + if (ret && virtio_blk_handle_rw_error(req, -ret, 0, true)) { + return; } virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); block_acct_done(blk_get_stats(s->blk), &req->acct); virtio_blk_free_request(req); - -out: - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); } static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret) @@ -184,11 +161,8 @@ static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret) bool is_write_zeroes = (virtio_ldl_p(VIRTIO_DEVICE(s), &req->out.type) & ~VIRTIO_BLK_T_BARRIER) == VIRTIO_BLK_T_WRITE_ZEROES; - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); - if (ret) { - if (virtio_blk_handle_rw_error(req, -ret, false, is_write_zeroes)) { - goto out; - } + if (ret && virtio_blk_handle_rw_error(req, -ret, false, is_write_zeroes)) { + return; } virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); @@ -196,9 +170,6 @@ static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret) block_acct_done(blk_get_stats(s->blk), &req->acct); } virtio_blk_free_request(req); - -out: - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); } #ifdef __linux__ @@ -245,10 +216,8 @@ static void virtio_blk_ioctl_complete(void *opaque, int status) virtio_stl_p(vdev, &scsi->data_len, hdr->dxfer_len); out: - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); virtio_blk_req_complete(req, status); virtio_blk_free_request(req); - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); g_free(ioctl_req); } @@ -384,12 +353,14 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) } } -static inline void submit_requests(BlockBackend *blk, MultiReqBuffer *mrb, +static inline void submit_requests(VirtIOBlock *s, MultiReqBuffer *mrb, int start, int num_reqs, int niov) { + BlockBackend *blk = s->blk; QEMUIOVector *qiov = &mrb->reqs[start]->qiov; int64_t sector_num = mrb->reqs[start]->sector_num; bool is_write = mrb->is_write; + BdrvRequestFlags flags = 0; if (num_reqs > 1) { int i; @@ -420,12 +391,18 @@ static inline void submit_requests(BlockBackend *blk, MultiReqBuffer *mrb, num_reqs - 1); } + if (blk_ram_registrar_ok(&s->blk_ram_registrar)) { + flags |= BDRV_REQ_REGISTERED_BUF; + } + if (is_write) { - blk_aio_pwritev(blk, sector_num << BDRV_SECTOR_BITS, qiov, 0, - virtio_blk_rw_complete, mrb->reqs[start]); + blk_aio_pwritev(blk, sector_num << BDRV_SECTOR_BITS, qiov, + flags, virtio_blk_rw_complete, + mrb->reqs[start]); } else { - blk_aio_preadv(blk, sector_num << BDRV_SECTOR_BITS, qiov, 0, - virtio_blk_rw_complete, mrb->reqs[start]); + blk_aio_preadv(blk, sector_num << BDRV_SECTOR_BITS, qiov, + flags, virtio_blk_rw_complete, + mrb->reqs[start]); } } @@ -447,14 +424,14 @@ static int multireq_compare(const void *a, const void *b) } } -static void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb) +static void virtio_blk_submit_multireq(VirtIOBlock *s, MultiReqBuffer *mrb) { int i = 0, start = 0, num_reqs = 0, niov = 0, nb_sectors = 0; uint32_t max_transfer; int64_t sector_num = 0; if (mrb->num_reqs == 1) { - submit_requests(blk, mrb, 0, 1, -1); + submit_requests(s, mrb, 0, 1, -1); mrb->num_reqs = 0; return; } @@ -474,11 +451,11 @@ static void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb) * 3. merge would exceed maximum transfer length of backend device */ if (sector_num + nb_sectors != req->sector_num || - niov > blk_get_max_iov(blk) - req->qiov.niov || + niov > blk_get_max_iov(s->blk) - req->qiov.niov || req->qiov.size > max_transfer || nb_sectors > (max_transfer - req->qiov.size) / BDRV_SECTOR_SIZE) { - submit_requests(blk, mrb, start, num_reqs, niov); + submit_requests(s, mrb, start, num_reqs, niov); num_reqs = 0; } } @@ -494,7 +471,7 @@ static void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb) num_reqs++; } - submit_requests(blk, mrb, start, num_reqs, niov); + submit_requests(s, mrb, start, num_reqs, niov); mrb->num_reqs = 0; } @@ -509,7 +486,7 @@ static void virtio_blk_handle_flush(VirtIOBlockReq *req, MultiReqBuffer *mrb) * Make sure all outstanding writes are posted to the backing device. */ if (mrb->is_write && mrb->num_reqs > 0) { - virtio_blk_submit_multireq(s->blk, mrb); + virtio_blk_submit_multireq(s, mrb); } blk_aio_flush(s->blk, virtio_blk_flush_complete, req); } @@ -614,6 +591,343 @@ err: return err_status; } +typedef struct ZoneCmdData { + VirtIOBlockReq *req; + struct iovec *in_iov; + unsigned in_num; + union { + struct { + unsigned int nr_zones; + BlockZoneDescriptor *zones; + } zone_report_data; + struct { + int64_t offset; + } zone_append_data; + }; +} ZoneCmdData; + +/* + * check zoned_request: error checking before issuing requests. If all checks + * passed, return true. + * append: true if only zone append requests issued. + */ +static bool check_zoned_request(VirtIOBlock *s, int64_t offset, int64_t len, + bool append, uint8_t *status) { + BlockDriverState *bs = blk_bs(s->blk); + int index; + + if (!virtio_has_feature(s->host_features, VIRTIO_BLK_F_ZONED)) { + *status = VIRTIO_BLK_S_UNSUPP; + return false; + } + + if (offset < 0 || len < 0 || len > (bs->total_sectors << BDRV_SECTOR_BITS) + || offset > (bs->total_sectors << BDRV_SECTOR_BITS) - len) { + *status = VIRTIO_BLK_S_ZONE_INVALID_CMD; + return false; + } + + if (append) { + if (bs->bl.write_granularity) { + if ((offset % bs->bl.write_granularity) != 0) { + *status = VIRTIO_BLK_S_ZONE_UNALIGNED_WP; + return false; + } + } + + index = offset / bs->bl.zone_size; + if (BDRV_ZT_IS_CONV(bs->wps->wp[index])) { + *status = VIRTIO_BLK_S_ZONE_INVALID_CMD; + return false; + } + + if (len / 512 > bs->bl.max_append_sectors) { + if (bs->bl.max_append_sectors == 0) { + *status = VIRTIO_BLK_S_UNSUPP; + } else { + *status = VIRTIO_BLK_S_ZONE_INVALID_CMD; + } + return false; + } + } + return true; +} + +static void virtio_blk_zone_report_complete(void *opaque, int ret) +{ + ZoneCmdData *data = opaque; + VirtIOBlockReq *req = data->req; + VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); + struct iovec *in_iov = data->in_iov; + unsigned in_num = data->in_num; + int64_t zrp_size, n, j = 0; + int64_t nz = data->zone_report_data.nr_zones; + int8_t err_status = VIRTIO_BLK_S_OK; + struct virtio_blk_zone_report zrp_hdr = (struct virtio_blk_zone_report) { + .nr_zones = cpu_to_le64(nz), + }; + + trace_virtio_blk_zone_report_complete(vdev, req, nz, ret); + if (ret) { + err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD; + goto out; + } + + zrp_size = sizeof(struct virtio_blk_zone_report) + + sizeof(struct virtio_blk_zone_descriptor) * nz; + n = iov_from_buf(in_iov, in_num, 0, &zrp_hdr, sizeof(zrp_hdr)); + if (n != sizeof(zrp_hdr)) { + virtio_error(vdev, "Driver provided input buffer that is too small!"); + err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD; + goto out; + } + + for (size_t i = sizeof(zrp_hdr); i < zrp_size; + i += sizeof(struct virtio_blk_zone_descriptor), ++j) { + struct virtio_blk_zone_descriptor desc = + (struct virtio_blk_zone_descriptor) { + .z_start = cpu_to_le64(data->zone_report_data.zones[j].start + >> BDRV_SECTOR_BITS), + .z_cap = cpu_to_le64(data->zone_report_data.zones[j].cap + >> BDRV_SECTOR_BITS), + .z_wp = cpu_to_le64(data->zone_report_data.zones[j].wp + >> BDRV_SECTOR_BITS), + }; + + switch (data->zone_report_data.zones[j].type) { + case BLK_ZT_CONV: + desc.z_type = VIRTIO_BLK_ZT_CONV; + break; + case BLK_ZT_SWR: + desc.z_type = VIRTIO_BLK_ZT_SWR; + break; + case BLK_ZT_SWP: + desc.z_type = VIRTIO_BLK_ZT_SWP; + break; + default: + g_assert_not_reached(); + } + + switch (data->zone_report_data.zones[j].state) { + case BLK_ZS_RDONLY: + desc.z_state = VIRTIO_BLK_ZS_RDONLY; + break; + case BLK_ZS_OFFLINE: + desc.z_state = VIRTIO_BLK_ZS_OFFLINE; + break; + case BLK_ZS_EMPTY: + desc.z_state = VIRTIO_BLK_ZS_EMPTY; + break; + case BLK_ZS_CLOSED: + desc.z_state = VIRTIO_BLK_ZS_CLOSED; + break; + case BLK_ZS_FULL: + desc.z_state = VIRTIO_BLK_ZS_FULL; + break; + case BLK_ZS_EOPEN: + desc.z_state = VIRTIO_BLK_ZS_EOPEN; + break; + case BLK_ZS_IOPEN: + desc.z_state = VIRTIO_BLK_ZS_IOPEN; + break; + case BLK_ZS_NOT_WP: + desc.z_state = VIRTIO_BLK_ZS_NOT_WP; + break; + default: + g_assert_not_reached(); + } + + /* TODO: it takes O(n^2) time complexity. Optimizations required. */ + n = iov_from_buf(in_iov, in_num, i, &desc, sizeof(desc)); + if (n != sizeof(desc)) { + virtio_error(vdev, "Driver provided input buffer " + "for descriptors that is too small!"); + err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD; + } + } + +out: + virtio_blk_req_complete(req, err_status); + virtio_blk_free_request(req); + g_free(data->zone_report_data.zones); + g_free(data); +} + +static void virtio_blk_handle_zone_report(VirtIOBlockReq *req, + struct iovec *in_iov, + unsigned in_num) +{ + VirtIOBlock *s = req->dev; + VirtIODevice *vdev = VIRTIO_DEVICE(s); + unsigned int nr_zones; + ZoneCmdData *data; + int64_t zone_size, offset; + uint8_t err_status; + + if (req->in_len < sizeof(struct virtio_blk_inhdr) + + sizeof(struct virtio_blk_zone_report) + + sizeof(struct virtio_blk_zone_descriptor)) { + virtio_error(vdev, "in buffer too small for zone report"); + err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD; + goto out; + } + + /* start byte offset of the zone report */ + offset = virtio_ldq_p(vdev, &req->out.sector) << BDRV_SECTOR_BITS; + if (!check_zoned_request(s, offset, 0, false, &err_status)) { + goto out; + } + nr_zones = (req->in_len - sizeof(struct virtio_blk_inhdr) - + sizeof(struct virtio_blk_zone_report)) / + sizeof(struct virtio_blk_zone_descriptor); + trace_virtio_blk_handle_zone_report(vdev, req, + offset >> BDRV_SECTOR_BITS, nr_zones); + + zone_size = sizeof(BlockZoneDescriptor) * nr_zones; + data = g_malloc(sizeof(ZoneCmdData)); + data->req = req; + data->in_iov = in_iov; + data->in_num = in_num; + data->zone_report_data.nr_zones = nr_zones; + data->zone_report_data.zones = g_malloc(zone_size), + + blk_aio_zone_report(s->blk, offset, &data->zone_report_data.nr_zones, + data->zone_report_data.zones, + virtio_blk_zone_report_complete, data); + return; +out: + virtio_blk_req_complete(req, err_status); + virtio_blk_free_request(req); +} + +static void virtio_blk_zone_mgmt_complete(void *opaque, int ret) +{ + VirtIOBlockReq *req = opaque; + VirtIOBlock *s = req->dev; + VirtIODevice *vdev = VIRTIO_DEVICE(s); + int8_t err_status = VIRTIO_BLK_S_OK; + trace_virtio_blk_zone_mgmt_complete(vdev, req,ret); + + if (ret) { + err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD; + } + + virtio_blk_req_complete(req, err_status); + virtio_blk_free_request(req); +} + +static int virtio_blk_handle_zone_mgmt(VirtIOBlockReq *req, BlockZoneOp op) +{ + VirtIOBlock *s = req->dev; + VirtIODevice *vdev = VIRTIO_DEVICE(s); + BlockDriverState *bs = blk_bs(s->blk); + int64_t offset = virtio_ldq_p(vdev, &req->out.sector) << BDRV_SECTOR_BITS; + uint64_t len; + uint64_t capacity = bs->total_sectors << BDRV_SECTOR_BITS; + uint8_t err_status = VIRTIO_BLK_S_OK; + + uint32_t type = virtio_ldl_p(vdev, &req->out.type); + if (type == VIRTIO_BLK_T_ZONE_RESET_ALL) { + /* Entire drive capacity */ + offset = 0; + len = capacity; + trace_virtio_blk_handle_zone_reset_all(vdev, req, 0, + bs->total_sectors); + } else { + if (bs->bl.zone_size > capacity - offset) { + /* The zoned device allows the last smaller zone. */ + len = capacity - bs->bl.zone_size * (bs->bl.nr_zones - 1); + } else { + len = bs->bl.zone_size; + } + trace_virtio_blk_handle_zone_mgmt(vdev, req, op, + offset >> BDRV_SECTOR_BITS, + len >> BDRV_SECTOR_BITS); + } + + if (!check_zoned_request(s, offset, len, false, &err_status)) { + goto out; + } + + blk_aio_zone_mgmt(s->blk, op, offset, len, + virtio_blk_zone_mgmt_complete, req); + + return 0; +out: + virtio_blk_req_complete(req, err_status); + virtio_blk_free_request(req); + return err_status; +} + +static void virtio_blk_zone_append_complete(void *opaque, int ret) +{ + ZoneCmdData *data = opaque; + VirtIOBlockReq *req = data->req; + VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); + int64_t append_sector, n; + uint8_t err_status = VIRTIO_BLK_S_OK; + + if (ret) { + err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD; + goto out; + } + + virtio_stq_p(vdev, &append_sector, + data->zone_append_data.offset >> BDRV_SECTOR_BITS); + n = iov_from_buf(data->in_iov, data->in_num, 0, &append_sector, + sizeof(append_sector)); + if (n != sizeof(append_sector)) { + virtio_error(vdev, "Driver provided input buffer less than size of " + "append_sector"); + err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD; + goto out; + } + trace_virtio_blk_zone_append_complete(vdev, req, append_sector, ret); + +out: + virtio_blk_req_complete(req, err_status); + virtio_blk_free_request(req); + g_free(data); +} + +static int virtio_blk_handle_zone_append(VirtIOBlockReq *req, + struct iovec *out_iov, + struct iovec *in_iov, + uint64_t out_num, + unsigned in_num) { + VirtIOBlock *s = req->dev; + VirtIODevice *vdev = VIRTIO_DEVICE(s); + uint8_t err_status = VIRTIO_BLK_S_OK; + + int64_t offset = virtio_ldq_p(vdev, &req->out.sector) << BDRV_SECTOR_BITS; + int64_t len = iov_size(out_iov, out_num); + ZoneCmdData *data; + + trace_virtio_blk_handle_zone_append(vdev, req, offset >> BDRV_SECTOR_BITS); + if (!check_zoned_request(s, offset, len, true, &err_status)) { + goto out; + } + + data = g_malloc(sizeof(ZoneCmdData)); + data->req = req; + data->in_iov = in_iov; + data->in_num = in_num; + data->zone_append_data.offset = offset; + qemu_iovec_init_external(&req->qiov, out_iov, out_num); + + block_acct_start(blk_get_stats(s->blk), &req->acct, len, + BLOCK_ACCT_ZONE_APPEND); + + blk_aio_zone_append(s->blk, &data->zone_append_data.offset, &req->qiov, 0, + virtio_blk_zone_append_complete, data); + return 0; + +out: + virtio_blk_req_complete(req, err_status); + virtio_blk_free_request(req); + return err_status; +} + static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) { uint32_t type; @@ -689,7 +1003,7 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) if (mrb->num_reqs > 0 && (mrb->num_reqs == VIRTIO_BLK_MAX_MERGE_REQS || is_write != mrb->is_write || !s->conf.request_merging)) { - virtio_blk_submit_multireq(s->blk, mrb); + virtio_blk_submit_multireq(s, mrb); } assert(mrb->num_reqs < VIRTIO_BLK_MAX_MERGE_REQS); @@ -700,6 +1014,24 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) case VIRTIO_BLK_T_FLUSH: virtio_blk_handle_flush(req, mrb); break; + case VIRTIO_BLK_T_ZONE_REPORT: + virtio_blk_handle_zone_report(req, in_iov, in_num); + break; + case VIRTIO_BLK_T_ZONE_OPEN: + virtio_blk_handle_zone_mgmt(req, BLK_ZO_OPEN); + break; + case VIRTIO_BLK_T_ZONE_CLOSE: + virtio_blk_handle_zone_mgmt(req, BLK_ZO_CLOSE); + break; + case VIRTIO_BLK_T_ZONE_FINISH: + virtio_blk_handle_zone_mgmt(req, BLK_ZO_FINISH); + break; + case VIRTIO_BLK_T_ZONE_RESET: + virtio_blk_handle_zone_mgmt(req, BLK_ZO_RESET); + break; + case VIRTIO_BLK_T_ZONE_RESET_ALL: + virtio_blk_handle_zone_mgmt(req, BLK_ZO_RESET); + break; case VIRTIO_BLK_T_SCSI_CMD: virtio_blk_handle_scsi(req); break; @@ -718,6 +1050,14 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) virtio_blk_free_request(req); break; } + case VIRTIO_BLK_T_ZONE_APPEND & ~VIRTIO_BLK_T_OUT: + /* + * Passing out_iov/out_num and in_iov/in_num is not safe + * to access req->elem.out_sg directly because it may be + * modified by virtio_blk_handle_request(). + */ + virtio_blk_handle_zone_append(req, out_iov, in_iov, out_num, in_num); + break; /* * VIRTIO_BLK_T_DISCARD and VIRTIO_BLK_T_WRITE_ZEROES are defined with * VIRTIO_BLK_T_OUT flag set. We masked this flag in the switch statement, @@ -774,8 +1114,7 @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) MultiReqBuffer mrb = {}; bool suppress_notifications = virtio_queue_get_notification(vq); - aio_context_acquire(blk_get_aio_context(s->blk)); - blk_io_plug(s->blk); + defer_call_begin(); do { if (suppress_notifications) { @@ -796,37 +1135,36 @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) } while (!virtio_queue_empty(vq)); if (mrb.num_reqs) { - virtio_blk_submit_multireq(s->blk, &mrb); + virtio_blk_submit_multireq(s, &mrb); } - blk_io_unplug(s->blk); - aio_context_release(blk_get_aio_context(s->blk)); + defer_call_end(); } static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBlock *s = (VirtIOBlock *)vdev; - if (s->dataplane && !s->dataplane_started) { + if (!s->ioeventfd_disabled && !s->ioeventfd_started) { /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start - * dataplane here instead of waiting for .set_status(). + * ioeventfd here instead of waiting for .set_status(). */ virtio_device_start_ioeventfd(vdev); - if (!s->dataplane_disabled) { + if (!s->ioeventfd_disabled) { return; } } + virtio_blk_handle_vq(s, vq); } -void virtio_blk_process_queued_requests(VirtIOBlock *s, bool is_bh) +static void virtio_blk_dma_restart_bh(void *opaque) { - VirtIOBlockReq *req = s->rq; + VirtIOBlockReq *req = opaque; + VirtIOBlock *s = req->dev; /* we're called with at least one request */ + MultiReqBuffer mrb = {}; - s->rq = NULL; - - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); while (req) { VirtIOBlockReq *next = req->next; if (virtio_blk_handle_request(req, &mrb)) { @@ -845,69 +1183,84 @@ void virtio_blk_process_queued_requests(VirtIOBlock *s, bool is_bh) } if (mrb.num_reqs) { - virtio_blk_submit_multireq(s->blk, &mrb); + virtio_blk_submit_multireq(s, &mrb); } - if (is_bh) { - blk_dec_in_flight(s->conf.conf.blk); - } - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); -} -static void virtio_blk_dma_restart_bh(void *opaque) -{ - VirtIOBlock *s = opaque; - - qemu_bh_delete(s->bh); - s->bh = NULL; - - virtio_blk_process_queued_requests(s, true); + /* Paired with inc in virtio_blk_dma_restart_cb() */ + blk_dec_in_flight(s->conf.conf.blk); } static void virtio_blk_dma_restart_cb(void *opaque, bool running, RunState state) { VirtIOBlock *s = opaque; - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); - VirtioBusState *bus = VIRTIO_BUS(qbus); + uint16_t num_queues = s->conf.num_queues; + g_autofree VirtIOBlockReq **vq_rq = NULL; + VirtIOBlockReq *rq; if (!running) { return; } - /* - * If ioeventfd is enabled, don't schedule the BH here as queued - * requests will be processed while starting the data plane. - */ - if (!s->bh && !virtio_bus_ioeventfd_enabled(bus)) { - s->bh = aio_bh_new(blk_get_aio_context(s->conf.conf.blk), - virtio_blk_dma_restart_bh, s); + /* Split the device-wide s->rq request list into per-vq request lists */ + vq_rq = g_new0(VirtIOBlockReq *, num_queues); + + WITH_QEMU_LOCK_GUARD(&s->rq_lock) { + rq = s->rq; + s->rq = NULL; + } + + while (rq) { + VirtIOBlockReq *next = rq->next; + uint16_t idx = virtio_get_queue_index(rq->vq); + + /* Only num_queues vqs were created so vq_rq[idx] is within bounds */ + assert(idx < num_queues); + rq->next = vq_rq[idx]; + vq_rq[idx] = rq; + rq = next; + } + + /* Schedule a BH to submit the requests in each vq's AioContext */ + for (uint16_t i = 0; i < num_queues; i++) { + if (!vq_rq[i]) { + continue; + } + + /* Paired with dec in virtio_blk_dma_restart_bh() */ blk_inc_in_flight(s->conf.conf.blk); - qemu_bh_schedule(s->bh); + + aio_bh_schedule_oneshot(s->vq_aio_context[i], + virtio_blk_dma_restart_bh, + vq_rq[i]); } } static void virtio_blk_reset(VirtIODevice *vdev) { VirtIOBlock *s = VIRTIO_BLK(vdev); - AioContext *ctx; VirtIOBlockReq *req; - ctx = blk_get_aio_context(s->blk); - aio_context_acquire(ctx); + /* Dataplane has stopped... */ + assert(!s->ioeventfd_started); + + /* ...but requests may still be in flight. */ blk_drain(s->blk); /* We drop queued requests after blk_drain() because blk_drain() itself can * produce them. */ - while (s->rq) { - req = s->rq; - s->rq = req->next; - virtqueue_detach_element(req->vq, &req->elem, 0); - virtio_blk_free_request(req); + WITH_QEMU_LOCK_GUARD(&s->rq_lock) { + while (s->rq) { + req = s->rq; + s->rq = req->next; + + /* No other threads can access req->vq here */ + virtqueue_detach_element(req->vq, &req->elem, 0); + + virtio_blk_free_request(req); + } } - aio_context_release(ctx); - - assert(!s->dataplane_started); blk_set_enable_write_cache(s->blk, s->original_wce); } @@ -917,6 +1270,7 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) { VirtIOBlock *s = VIRTIO_BLK(vdev); BlockConf *conf = &s->conf.conf; + BlockDriverState *bs = blk_bs(s->blk); struct virtio_blk_config blkcfg; uint64_t capacity; int64_t length; @@ -976,6 +1330,30 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) blkcfg.write_zeroes_may_unmap = 1; virtio_stl_p(vdev, &blkcfg.max_write_zeroes_seg, 1); } + if (bs->bl.zoned != BLK_Z_NONE) { + switch (bs->bl.zoned) { + case BLK_Z_HM: + blkcfg.zoned.model = VIRTIO_BLK_Z_HM; + break; + case BLK_Z_HA: + blkcfg.zoned.model = VIRTIO_BLK_Z_HA; + break; + default: + g_assert_not_reached(); + } + + virtio_stl_p(vdev, &blkcfg.zoned.zone_sectors, + bs->bl.zone_size / 512); + virtio_stl_p(vdev, &blkcfg.zoned.max_active_zones, + bs->bl.max_active_zones); + virtio_stl_p(vdev, &blkcfg.zoned.max_open_zones, + bs->bl.max_open_zones); + virtio_stl_p(vdev, &blkcfg.zoned.write_granularity, blk_size); + virtio_stl_p(vdev, &blkcfg.zoned.max_append_sectors, + bs->bl.max_append_sectors); + } else { + blkcfg.zoned.model = VIRTIO_BLK_Z_NONE; + } memcpy(config, &blkcfg, s->config_size); } @@ -986,9 +1364,7 @@ static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config) memcpy(&blkcfg, config, s->config_size); - aio_context_acquire(blk_get_aio_context(s->blk)); blk_set_enable_write_cache(s->blk, blkcfg.wce != 0); - aio_context_release(blk_get_aio_context(s->blk)); } static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, @@ -1033,7 +1409,7 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) VirtIOBlock *s = VIRTIO_BLK(vdev); if (!(status & (VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK))) { - assert(!s->dataplane_started); + assert(!s->ioeventfd_started); } if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { @@ -1056,29 +1432,31 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) * s->blk would erroneously be placed in writethrough mode. */ if (!virtio_vdev_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE)) { - aio_context_acquire(blk_get_aio_context(s->blk)); blk_set_enable_write_cache(s->blk, virtio_vdev_has_feature(vdev, VIRTIO_BLK_F_WCE)); - aio_context_release(blk_get_aio_context(s->blk)); } } static void virtio_blk_save_device(VirtIODevice *vdev, QEMUFile *f) { VirtIOBlock *s = VIRTIO_BLK(vdev); - VirtIOBlockReq *req = s->rq; - while (req) { - qemu_put_sbyte(f, 1); + WITH_QEMU_LOCK_GUARD(&s->rq_lock) { + VirtIOBlockReq *req = s->rq; - if (s->conf.num_queues > 1) { - qemu_put_be32(f, virtio_get_queue_index(req->vq)); + while (req) { + qemu_put_sbyte(f, 1); + + if (s->conf.num_queues > 1) { + qemu_put_be32(f, virtio_get_queue_index(req->vq)); + } + + qemu_put_virtqueue_element(vdev, f, &req->elem); + req = req->next; } - - qemu_put_virtqueue_element(vdev, f, &req->elem); - req = req->next; } + qemu_put_sbyte(f, 0); } @@ -1104,8 +1482,11 @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f, req = qemu_get_virtqueue_element(vdev, f, sizeof(VirtIOBlockReq)); virtio_blk_init_request(s, virtio_get_queue(vdev, vq_idx), req); - req->next = s->rq; - s->rq = req; + + WITH_QEMU_LOCK_GUARD(&s->rq_lock) { + req->next = s->rq; + s->rq = req; + } } return 0; @@ -1124,22 +1505,466 @@ static void virtio_blk_resize(void *opaque) VirtIODevice *vdev = VIRTIO_DEVICE(opaque); /* - * virtio_notify_config() needs to acquire the global mutex, + * virtio_notify_config() needs to acquire the BQL, * so it can't be called from an iothread. Instead, schedule * it to be run in the main context BH. */ aio_bh_schedule_oneshot(qemu_get_aio_context(), virtio_resize_cb, vdev); } +static void virtio_blk_ioeventfd_detach(VirtIOBlock *s) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(s); + + for (uint16_t i = 0; i < s->conf.num_queues; i++) { + VirtQueue *vq = virtio_get_queue(vdev, i); + virtio_queue_aio_detach_host_notifier(vq, s->vq_aio_context[i]); + } +} + +static void virtio_blk_ioeventfd_attach(VirtIOBlock *s) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(s); + + for (uint16_t i = 0; i < s->conf.num_queues; i++) { + VirtQueue *vq = virtio_get_queue(vdev, i); + virtio_queue_aio_attach_host_notifier(vq, s->vq_aio_context[i]); + } +} + +/* Suspend virtqueue ioeventfd processing during drain */ +static void virtio_blk_drained_begin(void *opaque) +{ + VirtIOBlock *s = opaque; + + if (s->ioeventfd_started) { + virtio_blk_ioeventfd_detach(s); + } +} + +/* Resume virtqueue ioeventfd processing after drain */ +static void virtio_blk_drained_end(void *opaque) +{ + VirtIOBlock *s = opaque; + + if (s->ioeventfd_started) { + virtio_blk_ioeventfd_attach(s); + } +} + static const BlockDevOps virtio_block_ops = { - .resize_cb = virtio_blk_resize, + .resize_cb = virtio_blk_resize, + .drained_begin = virtio_blk_drained_begin, + .drained_end = virtio_blk_drained_end, }; +static bool +validate_iothread_vq_mapping_list(IOThreadVirtQueueMappingList *list, + uint16_t num_queues, Error **errp) +{ + g_autofree unsigned long *vqs = bitmap_new(num_queues); + g_autoptr(GHashTable) iothreads = + g_hash_table_new(g_str_hash, g_str_equal); + + for (IOThreadVirtQueueMappingList *node = list; node; node = node->next) { + const char *name = node->value->iothread; + uint16List *vq; + + if (!iothread_by_id(name)) { + error_setg(errp, "IOThread \"%s\" object does not exist", name); + return false; + } + + if (!g_hash_table_add(iothreads, (gpointer)name)) { + error_setg(errp, + "duplicate IOThread name \"%s\" in iothread-vq-mapping", + name); + return false; + } + + if (node != list) { + if (!!node->value->vqs != !!list->value->vqs) { + error_setg(errp, "either all items in iothread-vq-mapping " + "must have vqs or none of them must have it"); + return false; + } + } + + for (vq = node->value->vqs; vq; vq = vq->next) { + if (vq->value >= num_queues) { + error_setg(errp, "vq index %u for IOThread \"%s\" must be " + "less than num_queues %u in iothread-vq-mapping", + vq->value, name, num_queues); + return false; + } + + if (test_and_set_bit(vq->value, vqs)) { + error_setg(errp, "cannot assign vq %u to IOThread \"%s\" " + "because it is already assigned", vq->value, name); + return false; + } + } + } + + if (list->value->vqs) { + for (uint16_t i = 0; i < num_queues; i++) { + if (!test_bit(i, vqs)) { + error_setg(errp, + "missing vq %u IOThread assignment in iothread-vq-mapping", + i); + return false; + } + } + } + + return true; +} + +/** + * apply_iothread_vq_mapping: + * @iothread_vq_mapping_list: The mapping of virtqueues to IOThreads. + * @vq_aio_context: The array of AioContext pointers to fill in. + * @num_queues: The length of @vq_aio_context. + * @errp: If an error occurs, a pointer to the area to store the error. + * + * Fill in the AioContext for each virtqueue in the @vq_aio_context array given + * the iothread-vq-mapping parameter in @iothread_vq_mapping_list. + * + * Returns: %true on success, %false on failure. + **/ +static bool apply_iothread_vq_mapping( + IOThreadVirtQueueMappingList *iothread_vq_mapping_list, + AioContext **vq_aio_context, + uint16_t num_queues, + Error **errp) +{ + IOThreadVirtQueueMappingList *node; + size_t num_iothreads = 0; + size_t cur_iothread = 0; + + if (!validate_iothread_vq_mapping_list(iothread_vq_mapping_list, + num_queues, errp)) { + return false; + } + + for (node = iothread_vq_mapping_list; node; node = node->next) { + num_iothreads++; + } + + for (node = iothread_vq_mapping_list; node; node = node->next) { + IOThread *iothread = iothread_by_id(node->value->iothread); + AioContext *ctx = iothread_get_aio_context(iothread); + + /* Released in virtio_blk_vq_aio_context_cleanup() */ + object_ref(OBJECT(iothread)); + + if (node->value->vqs) { + uint16List *vq; + + /* Explicit vq:IOThread assignment */ + for (vq = node->value->vqs; vq; vq = vq->next) { + assert(vq->value < num_queues); + vq_aio_context[vq->value] = ctx; + } + } else { + /* Round-robin vq:IOThread assignment */ + for (unsigned i = cur_iothread; i < num_queues; + i += num_iothreads) { + vq_aio_context[i] = ctx; + } + } + + cur_iothread++; + } + + return true; +} + +/* Context: BQL held */ +static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp) +{ + ERRP_GUARD(); + VirtIODevice *vdev = VIRTIO_DEVICE(s); + VirtIOBlkConf *conf = &s->conf; + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + + if (conf->iothread && conf->iothread_vq_mapping_list) { + error_setg(errp, + "iothread and iothread-vq-mapping properties cannot be set " + "at the same time"); + return false; + } + + if (conf->iothread || conf->iothread_vq_mapping_list) { + if (!k->set_guest_notifiers || !k->ioeventfd_assign) { + error_setg(errp, + "device is incompatible with iothread " + "(transport does not support notifiers)"); + return false; + } + if (!virtio_device_ioeventfd_enabled(vdev)) { + error_setg(errp, "ioeventfd is required for iothread"); + return false; + } + + /* + * If ioeventfd is (re-)enabled while the guest is running there could + * be block jobs that can conflict. + */ + if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { + error_prepend(errp, "cannot start virtio-blk ioeventfd: "); + return false; + } + } + + s->vq_aio_context = g_new(AioContext *, conf->num_queues); + + if (conf->iothread_vq_mapping_list) { + if (!apply_iothread_vq_mapping(conf->iothread_vq_mapping_list, + s->vq_aio_context, + conf->num_queues, + errp)) { + g_free(s->vq_aio_context); + s->vq_aio_context = NULL; + return false; + } + } else if (conf->iothread) { + AioContext *ctx = iothread_get_aio_context(conf->iothread); + for (unsigned i = 0; i < conf->num_queues; i++) { + s->vq_aio_context[i] = ctx; + } + + /* Released in virtio_blk_vq_aio_context_cleanup() */ + object_ref(OBJECT(conf->iothread)); + } else { + AioContext *ctx = qemu_get_aio_context(); + for (unsigned i = 0; i < conf->num_queues; i++) { + s->vq_aio_context[i] = ctx; + } + } + + return true; +} + +/* Context: BQL held */ +static void virtio_blk_vq_aio_context_cleanup(VirtIOBlock *s) +{ + VirtIOBlkConf *conf = &s->conf; + + assert(!s->ioeventfd_started); + + if (conf->iothread_vq_mapping_list) { + IOThreadVirtQueueMappingList *node; + + for (node = conf->iothread_vq_mapping_list; node; node = node->next) { + IOThread *iothread = iothread_by_id(node->value->iothread); + object_unref(OBJECT(iothread)); + } + } + + if (conf->iothread) { + object_unref(OBJECT(conf->iothread)); + } + + g_free(s->vq_aio_context); + s->vq_aio_context = NULL; +} + +/* Context: BQL held */ +static int virtio_blk_start_ioeventfd(VirtIODevice *vdev) +{ + VirtIOBlock *s = VIRTIO_BLK(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + unsigned i; + unsigned nvqs = s->conf.num_queues; + Error *local_err = NULL; + int r; + + if (s->ioeventfd_started || s->ioeventfd_starting) { + return 0; + } + + s->ioeventfd_starting = true; + + /* Set up guest notifier (irq) */ + r = k->set_guest_notifiers(qbus->parent, nvqs, true); + if (r != 0) { + error_report("virtio-blk failed to set guest notifier (%d), " + "ensure -accel kvm is set.", r); + goto fail_guest_notifiers; + } + + /* + * Batch all the host notifiers in a single transaction to avoid + * quadratic time complexity in address_space_update_ioeventfds(). + */ + memory_region_transaction_begin(); + + /* Set up virtqueue notify */ + for (i = 0; i < nvqs; i++) { + r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, true); + if (r != 0) { + int j = i; + + fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r); + while (i--) { + virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); + } + + /* + * The transaction expects the ioeventfds to be open when it + * commits. Do it now, before the cleanup loop. + */ + memory_region_transaction_commit(); + + while (j--) { + virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), j); + } + goto fail_host_notifiers; + } + } + + memory_region_transaction_commit(); + + /* + * Try to change the AioContext so that block jobs and other operations can + * co-locate their activity in the same AioContext. If it fails, nevermind. + */ + assert(nvqs > 0); /* enforced during ->realize() */ + r = blk_set_aio_context(s->conf.conf.blk, s->vq_aio_context[0], + &local_err); + if (r < 0) { + warn_report_err(local_err); + } + + /* + * These fields must be visible to the IOThread when it processes the + * virtqueue, otherwise it will think ioeventfd has not started yet. + * + * Make sure ->ioeventfd_started is false when blk_set_aio_context() is + * called above so that draining does not cause the host notifier to be + * detached/attached prematurely. + */ + s->ioeventfd_starting = false; + s->ioeventfd_started = true; + smp_wmb(); /* paired with aio_notify_accept() on the read side */ + + /* + * Get this show started by hooking up our callbacks. If drained now, + * virtio_blk_drained_end() will do this later. + * Attaching the notifier also kicks the virtqueues, processing any requests + * they may already have. + */ + if (!blk_in_drain(s->conf.conf.blk)) { + virtio_blk_ioeventfd_attach(s); + } + return 0; + + fail_host_notifiers: + k->set_guest_notifiers(qbus->parent, nvqs, false); + fail_guest_notifiers: + s->ioeventfd_disabled = true; + s->ioeventfd_starting = false; + return -ENOSYS; +} + +/* Stop notifications for new requests from guest. + * + * Context: BH in IOThread + */ +static void virtio_blk_ioeventfd_stop_vq_bh(void *opaque) +{ + VirtQueue *vq = opaque; + EventNotifier *host_notifier = virtio_queue_get_host_notifier(vq); + + virtio_queue_aio_detach_host_notifier(vq, qemu_get_current_aio_context()); + + /* + * Test and clear notifier after disabling event, in case poll callback + * didn't have time to run. + */ + virtio_queue_host_notifier_read(host_notifier); +} + +/* Context: BQL held */ +static void virtio_blk_stop_ioeventfd(VirtIODevice *vdev) +{ + VirtIOBlock *s = VIRTIO_BLK(vdev); + BusState *qbus = qdev_get_parent_bus(DEVICE(s)); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + unsigned i; + unsigned nvqs = s->conf.num_queues; + + if (!s->ioeventfd_started || s->ioeventfd_stopping) { + return; + } + + /* Better luck next time. */ + if (s->ioeventfd_disabled) { + s->ioeventfd_disabled = false; + s->ioeventfd_started = false; + return; + } + s->ioeventfd_stopping = true; + + if (!blk_in_drain(s->conf.conf.blk)) { + for (i = 0; i < nvqs; i++) { + VirtQueue *vq = virtio_get_queue(vdev, i); + AioContext *ctx = s->vq_aio_context[i]; + + aio_wait_bh_oneshot(ctx, virtio_blk_ioeventfd_stop_vq_bh, vq); + } + } + + /* + * Batch all the host notifiers in a single transaction to avoid + * quadratic time complexity in address_space_update_ioeventfds(). + */ + memory_region_transaction_begin(); + + for (i = 0; i < nvqs; i++) { + virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); + } + + /* + * The transaction expects the ioeventfds to be open when it + * commits. Do it now, before the cleanup loop. + */ + memory_region_transaction_commit(); + + for (i = 0; i < nvqs; i++) { + virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i); + } + + /* + * Set ->ioeventfd_started to false before draining so that host notifiers + * are not detached/attached anymore. + */ + s->ioeventfd_started = false; + + /* Wait for virtio_blk_dma_restart_bh() and in flight I/O to complete */ + blk_drain(s->conf.conf.blk); + + /* + * Try to switch bs back to the QEMU main loop. If other users keep the + * BlockBackend in the iothread, that's ok + */ + blk_set_aio_context(s->conf.conf.blk, qemu_get_aio_context(), NULL); + + /* Clean up guest notifier (irq) */ + k->set_guest_notifiers(qbus->parent, nvqs, false); + + s->ioeventfd_stopping = false; +} + static void virtio_blk_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOBlock *s = VIRTIO_BLK(dev); VirtIOBlkConf *conf = &s->conf; + BlockDriverState *bs; Error *err = NULL; unsigned i; @@ -1185,6 +2010,14 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) return; } + bs = blk_bs(conf->conf.blk); + if (bs->bl.zoned != BLK_Z_NONE) { + virtio_add_feature(&s->host_features, VIRTIO_BLK_F_ZONED); + if (bs->bl.zoned == BLK_Z_HM) { + virtio_clear_feature(&s->host_features, VIRTIO_BLK_F_DISCARD); + } + } + if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_DISCARD) && (!conf->max_discard_sectors || conf->max_discard_sectors > BDRV_REQUEST_MAX_SECTORS)) { @@ -1204,10 +2037,12 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) return; } - virtio_blk_set_config_size(s, s->host_features); - + s->config_size = virtio_get_config_size(&virtio_blk_cfg_size_params, + s->host_features); virtio_init(vdev, VIRTIO_ID_BLOCK, s->config_size); + qemu_mutex_init(&s->rq_lock); + s->blk = conf->conf.blk; s->rq = NULL; s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1; @@ -1216,7 +2051,13 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) virtio_add_queue(vdev, conf->queue_size, virtio_blk_handle_output); } qemu_coroutine_inc_pool_size(conf->num_queues * conf->queue_size / 2); - virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err); + + /* Don't start ioeventfd if transport does not support notifiers. */ + if (!virtio_device_ioeventfd_enabled(vdev)) { + s->ioeventfd_disabled = true; + } + + virtio_blk_vq_aio_context_init(s, &err); if (err != NULL) { error_propagate(errp, err); for (i = 0; i < conf->num_queues; i++) { @@ -1226,9 +2067,15 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) return; } - s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s); + /* + * This must be after virtio_init() so virtio_blk_dma_restart_cb() gets + * called after ->start_ioeventfd() has already set blk's AioContext. + */ + s->change = + qdev_add_vm_change_state_handler(dev, virtio_blk_dma_restart_cb, s); + + blk_ram_registrar_init(&s->blk_ram_registrar, s->blk); blk_set_dev_ops(s->blk, &virtio_block_ops, s); - blk_set_guest_block_size(s->blk, s->conf.conf.logical_block_size); blk_iostatus_enable(s->blk); @@ -1247,12 +2094,13 @@ static void virtio_blk_device_unrealize(DeviceState *dev) blk_drain(s->blk); del_boot_device_lchs(dev, "/disk@0,0"); - virtio_blk_data_plane_destroy(s->dataplane); - s->dataplane = NULL; + virtio_blk_vq_aio_context_cleanup(s); for (i = 0; i < conf->num_queues; i++) { virtio_del_queue(vdev, i); } qemu_coroutine_dec_pool_size(conf->num_queues * conf->queue_size / 2); + qemu_mutex_destroy(&s->rq_lock); + blk_ram_registrar_destroy(&s->blk_ram_registrar); qemu_del_vm_change_state_handler(s->change); blockdev_mark_auto_del(s->blk); virtio_cleanup(vdev); @@ -1271,7 +2119,7 @@ static const VMStateDescription vmstate_virtio_blk = { .name = "virtio-blk", .minimum_version_id = 2, .version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, @@ -1296,6 +2144,8 @@ static Property virtio_blk_properties[] = { DEFINE_PROP_BOOL("seg-max-adjust", VirtIOBlock, conf.seg_max_adjust, true), DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD, IOThread *), + DEFINE_PROP_IOTHREAD_VQ_MAPPING_LIST("iothread-vq-mapping", VirtIOBlock, + conf.iothread_vq_mapping_list), DEFINE_PROP_BIT64("discard", VirtIOBlock, host_features, VIRTIO_BLK_F_DISCARD, true), DEFINE_PROP_BOOL("report-discard-granularity", VirtIOBlock, @@ -1328,8 +2178,8 @@ static void virtio_blk_class_init(ObjectClass *klass, void *data) vdc->reset = virtio_blk_reset; vdc->save = virtio_blk_save_device; vdc->load = virtio_blk_load_device; - vdc->start_ioeventfd = virtio_blk_data_plane_start; - vdc->stop_ioeventfd = virtio_blk_data_plane_stop; + vdc->start_ioeventfd = virtio_blk_start_ioeventfd; + vdc->stop_ioeventfd = virtio_blk_stop_ioeventfd; } static const TypeInfo virtio_blk_info = { diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 674953f1ad..aed1d5c330 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -19,7 +19,6 @@ #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" #include "hw/xen/xen-block.h" @@ -28,13 +27,137 @@ #include "sysemu/block-backend.h" #include "sysemu/iothread.h" #include "dataplane/xen-block.h" +#include "hw/xen/interface/io/xs_wire.h" #include "trace.h" +#define XVDA_MAJOR 202 +#define XVDQ_MAJOR (1 << 20) +#define XVDBGQCV_MAJOR ((1 << 21) - 1) +#define HDA_MAJOR 3 +#define HDC_MAJOR 22 +#define SDA_MAJOR 8 + + +static int vdev_to_diskno(unsigned int vdev_nr) +{ + switch (vdev_nr >> 8) { + case XVDA_MAJOR: + case SDA_MAJOR: + return (vdev_nr >> 4) & 0x15; + + case HDA_MAJOR: + return (vdev_nr >> 6) & 1; + + case HDC_MAJOR: + return ((vdev_nr >> 6) & 1) + 2; + + case XVDQ_MAJOR ... XVDBGQCV_MAJOR: + return (vdev_nr >> 8) & 0xfffff; + + default: + return -1; + } +} + +#define MAX_AUTO_VDEV 4096 + +/* + * Find a free device name in the xvda → xvdfan range and set it in + * blockdev->props.vdev. Our definition of "free" is that there must + * be no other disk or partition with the same disk number. + * + * You are technically permitted to have all of hda, hda1, sda, sda1, + * xvda and xvda1 as *separate* PV block devices with separate backing + * stores. That doesn't make it a good idea. This code will skip xvda + * if *any* of those "conflicting" devices already exists. + * + * The limit of xvdfan (disk 4095) is fairly arbitrary just to avoid a + * stupidly sized bitmap, but Linux as of v6.6 doesn't support anything + * higher than that anyway. + */ +static bool xen_block_find_free_vdev(XenBlockDevice *blockdev, Error **errp) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(blockdev))); + unsigned long used_devs[BITS_TO_LONGS(MAX_AUTO_VDEV)]; + XenBlockVdev *vdev = &blockdev->props.vdev; + char fe_path[XENSTORE_ABS_PATH_MAX + 1]; + char **existing_frontends; + unsigned int nr_existing = 0; + unsigned int vdev_nr; + int i, disk = 0; + + snprintf(fe_path, sizeof(fe_path), "/local/domain/%u/device/vbd", + blockdev->xendev.frontend_id); + + existing_frontends = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, fe_path, + &nr_existing); + if (!existing_frontends) { + if (errno == ENOENT) { + /* + * If the frontend directory doesn't exist because there are + * no existing vbd devices, that's fine. Just ensure that we + * don't dereference the NULL existing_frontends pointer, by + * checking that nr_existing is zero so the loop below is not + * entered. + * + * In fact this is redundant since nr_existing is initialized + * to zero, but setting it again here makes it abundantly clear + * to Coverity, and to the human reader who doesn't know the + * semantics of qemu_xen_xs_directory() off the top of their + * head. + */ + nr_existing = 0; + } else { + /* All other errors accessing the frontend directory are fatal. */ + error_setg_errno(errp, errno, "cannot read %s", fe_path); + return false; + } + } + + memset(used_devs, 0, sizeof(used_devs)); + for (i = 0; i < nr_existing; i++) { + if (qemu_strtoui(existing_frontends[i], NULL, 10, &vdev_nr)) { + free(existing_frontends[i]); + continue; + } + + free(existing_frontends[i]); + + disk = vdev_to_diskno(vdev_nr); + if (disk < 0 || disk >= MAX_AUTO_VDEV) { + continue; + } + + set_bit(disk, used_devs); + } + free(existing_frontends); + + disk = find_first_zero_bit(used_devs, MAX_AUTO_VDEV); + if (disk == MAX_AUTO_VDEV) { + error_setg(errp, "cannot find device vdev for block device"); + return false; + } + + vdev->type = XEN_BLOCK_VDEV_TYPE_XVD; + vdev->partition = 0; + vdev->disk = disk; + if (disk < (1 << 4)) { + vdev->number = (XVDA_MAJOR << 8) | (disk << 4); + } else { + vdev->number = (XVDQ_MAJOR << 8) | (disk << 8); + } + return true; +} + static char *xen_block_get_name(XenDevice *xendev, Error **errp) { XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev); XenBlockVdev *vdev = &blockdev->props.vdev; + if (vdev->type == XEN_BLOCK_VDEV_TYPE_INVALID && + !xen_block_find_free_vdev(blockdev, errp)) { + return NULL; + } return g_strdup_printf("%lu", vdev->number); } @@ -84,7 +207,8 @@ static void xen_block_connect(XenDevice *xendev, Error **errp) g_free(ring_ref); return; } - } else if (order <= blockdev->props.max_ring_page_order) { + } else if (qemu_xen_gnttab_can_map_multi() && + order <= blockdev->props.max_ring_page_order) { unsigned int i; nr_ring_ref = 1 << order; @@ -115,9 +239,13 @@ static void xen_block_connect(XenDevice *xendev, Error **errp) return; } - if (xen_device_frontend_scanf(xendev, "protocol", "%ms", - &str) != 1) { - protocol = BLKIF_PROTOCOL_NATIVE; + if (xen_device_frontend_scanf(xendev, "protocol", "%ms", &str) != 1) { + /* x86 defaults to the 32-bit protocol even for 64-bit guests. */ + if (object_dynamic_cast(OBJECT(qdev_get_machine()), "x86-machine")) { + protocol = BLKIF_PROTOCOL_X86_32; + } else { + protocol = BLKIF_PROTOCOL_NATIVE; + } } else { if (strcmp(str, XEN_IO_PROTO_ABI_X86_32) == 0) { protocol = BLKIF_PROTOCOL_X86_32; @@ -189,8 +317,26 @@ static void xen_block_resize_cb(void *opaque) xen_device_backend_printf(xendev, "state", "%u", state); } +/* Suspend request handling */ +static void xen_block_drained_begin(void *opaque) +{ + XenBlockDevice *blockdev = opaque; + + xen_block_dataplane_detach(blockdev->dataplane); +} + +/* Resume request handling */ +static void xen_block_drained_end(void *opaque) +{ + XenBlockDevice *blockdev = opaque; + + xen_block_dataplane_attach(blockdev->dataplane); +} + static const BlockDevOps xen_block_dev_ops = { - .resize_cb = xen_block_resize_cb, + .resize_cb = xen_block_resize_cb, + .drained_begin = xen_block_drained_begin, + .drained_end = xen_block_drained_end, }; static void xen_block_realize(XenDevice *xendev, Error **errp) @@ -242,9 +388,6 @@ static void xen_block_realize(XenDevice *xendev, Error **errp) return; } - blk_set_dev_ops(blk, &xen_block_dev_ops, blockdev); - blk_set_guest_block_size(blk, conf->logical_block_size); - if (conf->discard_granularity == -1) { conf->discard_granularity = conf->physical_block_size; } @@ -257,8 +400,12 @@ static void xen_block_realize(XenDevice *xendev, Error **errp) } xen_device_backend_printf(xendev, "feature-flush-cache", "%u", 1); - xen_device_backend_printf(xendev, "max-ring-page-order", "%u", - blockdev->props.max_ring_page_order); + + if (qemu_xen_gnttab_can_map_multi()) { + xen_device_backend_printf(xendev, "max-ring-page-order", "%u", + blockdev->props.max_ring_page_order); + } + xen_device_backend_printf(xendev, "info", "%u", blockdev->info); xen_device_frontend_printf(xendev, "virtual-device", "%lu", @@ -274,6 +421,8 @@ static void xen_block_realize(XenDevice *xendev, Error **errp) blockdev->dataplane = xen_block_dataplane_create(xendev, blk, conf->logical_block_size, blockdev->props.iothread); + + blk_set_dev_ops(blk, &xen_block_dev_ops, blockdev); } static void xen_block_frontend_changed(XenDevice *xendev, @@ -348,7 +497,7 @@ static void xen_block_get_vdev(Object *obj, Visitor *v, const char *name, case XEN_BLOCK_VDEV_TYPE_XVD: case XEN_BLOCK_VDEV_TYPE_HD: case XEN_BLOCK_VDEV_TYPE_SD: { - char *name = disk_to_vbd_name(vdev->disk); + char *vbd_name = disk_to_vbd_name(vdev->disk); str = g_strdup_printf("%s%s%lu", (vdev->type == XEN_BLOCK_VDEV_TYPE_XVD) ? @@ -356,8 +505,8 @@ static void xen_block_get_vdev(Object *obj, Visitor *v, const char *name, (vdev->type == XEN_BLOCK_VDEV_TYPE_HD) ? "hd" : "sd", - name, vdev->partition); - g_free(name); + vbd_name, vdev->partition); + g_free(vbd_name); break; } default: @@ -457,10 +606,10 @@ static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name, case XEN_BLOCK_VDEV_TYPE_DP: case XEN_BLOCK_VDEV_TYPE_XVD: if (vdev->disk < (1 << 4) && vdev->partition < (1 << 4)) { - vdev->number = (202 << 8) | (vdev->disk << 4) | + vdev->number = (XVDA_MAJOR << 8) | (vdev->disk << 4) | vdev->partition; } else if (vdev->disk < (1 << 20) && vdev->partition < (1 << 8)) { - vdev->number = (1 << 28) | (vdev->disk << 8) | + vdev->number = (XVDQ_MAJOR << 8) | (vdev->disk << 8) | vdev->partition; } else { goto invalid; @@ -470,10 +619,11 @@ static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name, case XEN_BLOCK_VDEV_TYPE_HD: if ((vdev->disk == 0 || vdev->disk == 1) && vdev->partition < (1 << 6)) { - vdev->number = (3 << 8) | (vdev->disk << 6) | vdev->partition; + vdev->number = (HDA_MAJOR << 8) | (vdev->disk << 6) | + vdev->partition; } else if ((vdev->disk == 2 || vdev->disk == 3) && vdev->partition < (1 << 6)) { - vdev->number = (22 << 8) | ((vdev->disk - 2) << 6) | + vdev->number = (HDC_MAJOR << 8) | ((vdev->disk - 2) << 6) | vdev->partition; } else { goto invalid; @@ -482,7 +632,8 @@ static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name, case XEN_BLOCK_VDEV_TYPE_SD: if (vdev->disk < (1 << 4) && vdev->partition < (1 << 4)) { - vdev->number = (8 << 8) | (vdev->disk << 4) | vdev->partition; + vdev->number = (SDA_MAJOR << 8) | (vdev->disk << 4) | + vdev->partition; } else { goto invalid; } @@ -760,14 +911,15 @@ static XenBlockDrive *xen_block_drive_create(const char *id, drive = g_new0(XenBlockDrive, 1); drive->id = g_strdup(id); - file_layer = qdict_new(); - driver_layer = qdict_new(); - rc = stat(filename, &st); if (rc) { error_setg_errno(errp, errno, "Could not stat file '%s'", filename); goto done; } + + file_layer = qdict_new(); + driver_layer = qdict_new(); + if (S_ISBLK(st.st_mode)) { qdict_put_str(file_layer, "driver", "host_device"); } else { @@ -775,7 +927,6 @@ static XenBlockDrive *xen_block_drive_create(const char *id, } qdict_put_str(file_layer, "filename", filename); - g_free(filename); if (mode && *mode != 'w') { qdict_put_bool(file_layer, "read-only", true); @@ -810,7 +961,6 @@ static XenBlockDrive *xen_block_drive_create(const char *id, qdict_put_str(file_layer, "locking", "off"); qdict_put_str(driver_layer, "driver", driver); - g_free(driver); qdict_put(driver_layer, "file", file_layer); @@ -821,6 +971,8 @@ static XenBlockDrive *xen_block_drive_create(const char *id, qobject_unref(driver_layer); done: + g_free(filename); + g_free(driver); if (*errp) { xen_block_drive_destroy(drive, NULL); return NULL; diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index 96410b1ff8..83990e20f7 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -260,7 +260,7 @@ static const VMStateDescription vmstate_bcm2835_aux = { .name = TYPE_BCM2835_AUX, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState, BCM2835_AUX_RX_FIFO_LEN), VMSTATE_UINT8(read_pos, BCM2835AuxState), diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index c069a30842..db31d7cc85 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -307,11 +307,11 @@ static gboolean cadence_uart_xmit(void *do_not_use, GIOCondition cond, /* instant drain the fifo when there's no back-end */ if (!qemu_chr_fe_backend_connected(&s->chr)) { s->tx_count = 0; - return FALSE; + return G_SOURCE_REMOVE; } if (!s->tx_count) { - return FALSE; + return G_SOURCE_REMOVE; } ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_count); @@ -326,12 +326,12 @@ static gboolean cadence_uart_xmit(void *do_not_use, GIOCondition cond, cadence_uart_xmit, s); if (!r) { s->tx_count = 0; - return FALSE; + return G_SOURCE_REMOVE; } } uart_update_status(s); - return FALSE; + return G_SOURCE_REMOVE; } static void uart_write_tx_fifo(CadenceUARTState *s, const uint8_t *buf, @@ -450,13 +450,15 @@ static MemTxResult uart_write(void *opaque, hwaddr offset, } break; case R_BRGR: /* Baud rate generator */ + value &= 0xffff; if (value >= 0x01) { - s->r[offset] = value & 0xFFFF; + s->r[offset] = value; } break; case R_BDIV: /* Baud rate divider */ + value &= 0xff; if (value >= 0x04) { - s->r[offset] = value & 0xFF; + s->r[offset] = value; } break; default: @@ -573,7 +575,7 @@ static int cadence_uart_pre_load(void *opaque) { CadenceUARTState *s = opaque; - /* the frequency will be overriden if the refclk field is present */ + /* the frequency will be overridden if the refclk field is present */ clock_set_hz(s->refclk, UART_DEFAULT_REF_CLK); return 0; } @@ -600,7 +602,7 @@ static const VMStateDescription vmstate_cadence_uart = { .minimum_version_id = 2, .pre_load = cadence_uart_pre_load, .post_load = cadence_uart_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(r, CadenceUARTState, CADENCE_UART_R_MAX), VMSTATE_UINT8_ARRAY(rx_fifo, CadenceUARTState, CADENCE_UART_RX_FIFO_SIZE), diff --git a/hw/char/cmsdk-apb-uart.c b/hw/char/cmsdk-apb-uart.c index f8dc89ee3d..d07cca1bd4 100644 --- a/hw/char/cmsdk-apb-uart.c +++ b/hw/char/cmsdk-apb-uart.c @@ -199,7 +199,7 @@ static gboolean uart_transmit(void *do_not_use, GIOCondition cond, void *opaque) s->watch_tag = 0; if (!(s->ctrl & R_CTRL_TX_EN_MASK) || !(s->state & R_STATE_TXFULL_MASK)) { - return FALSE; + return G_SOURCE_REMOVE; } ret = qemu_chr_fe_write(&s->chr, &s->txbuf, 1); @@ -215,7 +215,7 @@ static gboolean uart_transmit(void *do_not_use, GIOCondition cond, void *opaque) } /* Transmit pending */ trace_cmsdk_apb_uart_tx_pending(); - return FALSE; + return G_SOURCE_REMOVE; } buffer_drained: @@ -227,7 +227,7 @@ buffer_drained: s->intstatus |= R_INTSTATUS_TX_MASK; } cmsdk_apb_uart_update(s); - return FALSE; + return G_SOURCE_REMOVE; } static void uart_cancel_transmit(CMSDKAPBUART *s) @@ -366,7 +366,7 @@ static const VMStateDescription cmsdk_apb_uart_vmstate = { .version_id = 1, .minimum_version_id = 1, .post_load = cmsdk_apb_uart_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(state, CMSDKAPBUART), VMSTATE_UINT32(ctrl, CMSDKAPBUART), VMSTATE_UINT32(intstatus, CMSDKAPBUART), diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index 00e5df5517..ef2d762726 100644 --- a/hw/char/digic-uart.c +++ b/hw/char/digic-uart.c @@ -63,7 +63,7 @@ static uint64_t digic_uart_read(void *opaque, hwaddr addr, default: qemu_log_mask(LOG_UNIMP, "digic-uart: read access to unknown register 0x" - TARGET_FMT_plx "\n", addr << 2); + HWADDR_FMT_plx "\n", addr << 2); } return ret; @@ -101,7 +101,7 @@ static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value, default: qemu_log_mask(LOG_UNIMP, "digic-uart: write access to unknown register 0x" - TARGET_FMT_plx "\n", addr << 2); + HWADDR_FMT_plx "\n", addr << 2); } } @@ -165,7 +165,7 @@ static const VMStateDescription vmstate_digic_uart = { .name = "digic-uart", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(reg_rx, DigicUartState), VMSTATE_UINT32(reg_st, DigicUartState), VMSTATE_END_OF_LIST() diff --git a/hw/char/escc.c b/hw/char/escc.c index 17a908c59b..d450d70eda 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -31,6 +31,8 @@ #include "qemu/module.h" #include "hw/char/escc.h" #include "ui/console.h" + +#include "qemu/cutils.h" #include "trace.h" /* @@ -190,6 +192,7 @@ #define R_MISC1I 14 #define R_EXTINT 15 +static uint8_t sunkbd_layout_dip_switch(const char *sunkbd_layout); static void handle_kbd_command(ESCCChannelState *s, int val); static int serial_can_receive(void *opaque); static void serial_receive_byte(ESCCChannelState *s, int ch); @@ -650,7 +653,9 @@ static void escc_mem_write(void *opaque, hwaddr addr, escc_update_irq(s); s->tx = val; if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { /* tx enabled */ - if (qemu_chr_fe_backend_connected(&s->chr)) { + if (s->wregs[W_MISC2] & MISC2_LCL_LOOP) { + serial_receive_byte(s, s->tx); + } else if (qemu_chr_fe_backend_connected(&s->chr)) { /* * XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks @@ -761,7 +766,7 @@ static const VMStateDescription vmstate_escc_chn = { .name = "escc_chn", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(vmstate_dummy, ESCCChannelState), VMSTATE_UINT32(reg, ESCCChannelState), VMSTATE_UINT32(rxint, ESCCChannelState), @@ -780,7 +785,7 @@ static const VMStateDescription vmstate_escc = { .name = "escc", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(chn, ESCCState, 2, 2, vmstate_escc_chn, ESCCChannelState), VMSTATE_END_OF_LIST() @@ -840,12 +845,85 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, put_queue(s, keycode); } -static QemuInputHandler sunkbd_handler = { +static const QemuInputHandler sunkbd_handler = { .name = "sun keyboard", .mask = INPUT_EVENT_MASK_KEY, .event = sunkbd_handle_event, }; +static uint8_t sunkbd_layout_dip_switch(const char *kbd_layout) +{ + /* Return the value of the dip-switches in a SUN Type 5 keyboard */ + static uint8_t ret = 0xff; + + if ((ret == 0xff) && kbd_layout) { + int i; + struct layout_values { + const char *lang; + uint8_t dip; + } languages[] = + /* + * Dip values from table 3-16 Layouts for Type 4, 5 and 5c Keyboards + */ + { + {"en-us", 0x21}, /* U.S.A. (US5.kt) */ + /* 0x22 is some other US (US_UNIX5.kt) */ + {"fr", 0x23}, /* France (France5.kt) */ + {"da", 0x24}, /* Denmark (Denmark5.kt) */ + {"de", 0x25}, /* Germany (Germany5.kt) */ + {"it", 0x26}, /* Italy (Italy5.kt) */ + {"nl", 0x27}, /* The Netherlands (Netherland5.kt) */ + {"no", 0x28}, /* Norway (Norway.kt) */ + {"pt", 0x29}, /* Portugal (Portugal5.kt) */ + {"es", 0x2a}, /* Spain (Spain5.kt) */ + {"sv", 0x2b}, /* Sweden (Sweden5.kt) */ + {"fr-ch", 0x2c}, /* Switzerland/French (Switzer_Fr5.kt) */ + {"de-ch", 0x2d}, /* Switzerland/German (Switzer_Ge5.kt) */ + {"en-gb", 0x2e}, /* Great Britain (UK5.kt) */ + {"ko", 0x2f}, /* Korea (Korea5.kt) */ + {"tw", 0x30}, /* Taiwan (Taiwan5.kt) */ + {"ja", 0x31}, /* Japan (Japan5.kt) */ + {"fr-ca", 0x32}, /* Canada/French (Canada_Fr5.kt) */ + {"hu", 0x33}, /* Hungary (Hungary5.kt) */ + {"pl", 0x34}, /* Poland (Poland5.kt) */ + {"cz", 0x35}, /* Czech (Czech5.kt) */ + {"ru", 0x36}, /* Russia (Russia5.kt) */ + {"lv", 0x37}, /* Latvia (Latvia5.kt) */ + {"tr", 0x38}, /* Turkey-Q5 (TurkeyQ5.kt) */ + {"gr", 0x39}, /* Greece (Greece5.kt) */ + {"ar", 0x3a}, /* Arabic (Arabic5.kt) */ + {"lt", 0x3b}, /* Lithuania (Lithuania5.kt) */ + {"nl-be", 0x3c}, /* Belgium (Belgian5.kt) */ + {"be", 0x3c}, /* Belgium (Belgian5.kt) */ + }; + + for (i = 0; + i < sizeof(languages) / sizeof(struct layout_values); + i++) { + if (!strcmp(kbd_layout, languages[i].lang)) { + ret = languages[i].dip; + return ret; + } + } + + /* Found no known language code */ + if ((kbd_layout[0] >= '0') && (kbd_layout[0] <= '9')) { + unsigned int tmp; + + /* As a fallback we also accept numeric dip switch value */ + if (!qemu_strtoui(kbd_layout, NULL, 0, &tmp)) { + ret = tmp; + } + } + } + + if (ret == 0xff) { + /* Final fallback if keyboard_layout was not set or recognized */ + ret = 0x21; /* en-us layout */ + } + return ret; +} + static void handle_kbd_command(ESCCChannelState *s, int val) { trace_escc_kbd_command(val); @@ -867,7 +945,7 @@ static void handle_kbd_command(ESCCChannelState *s, int val) case 0xf: clear_queue(s); put_queue(s, 0xfe); - put_queue(s, 0x21); /* en-us layout */ + put_queue(s, sunkbd_layout_dip_switch(s->sunkbd_layout)); break; default: break; @@ -976,6 +1054,7 @@ static Property escc_properties[] = { DEFINE_PROP_UINT32("chnAtype", ESCCState, chn[1].type, 0), DEFINE_PROP_CHR("chrB", ESCCState, chn[0].chr), DEFINE_PROP_CHR("chrA", ESCCState, chn[1].chr), + DEFINE_PROP_STRING("chnA-sunkbd-layout", ESCCState, chn[1].sunkbd_layout), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c index e8c3017724..8d6422dae4 100644 --- a/hw/char/etraxfs_ser.c +++ b/hw/char/etraxfs_ser.c @@ -113,7 +113,7 @@ ser_read(void *opaque, hwaddr addr, unsigned int size) break; default: r = s->regs[addr]; - D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr, r)); + D(qemu_log("%s " HWADDR_FMT_plx "=%x\n", __func__, addr, r)); break; } return r; @@ -127,7 +127,7 @@ ser_write(void *opaque, hwaddr addr, uint32_t value = val64; unsigned char ch = val64; - D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr, value)); + D(qemu_log("%s " HWADDR_FMT_plx "=%x\n", __func__, addr, value)); addr >>= 2; switch (addr) { diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index addcd59b02..8cdd42e54f 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -211,7 +211,7 @@ static void fifo_reset(Exynos4210UartFIFO *q) g_free(q->data); q->data = NULL; - q->data = (uint8_t *)g_malloc0(q->size); + q->data = g_malloc0(q->size); q->sp = 0; q->rp = 0; @@ -628,7 +628,7 @@ static const VMStateDescription vmstate_exynos4210_uart_fifo = { .name = "exynos4210.uart.fifo", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(sp, Exynos4210UartFIFO), VMSTATE_UINT32(rp, Exynos4210UartFIFO), VMSTATE_VBUFFER_UINT32(data, Exynos4210UartFIFO, 1, NULL, size), @@ -641,7 +641,7 @@ static const VMStateDescription vmstate_exynos4210_uart = { .version_id = 1, .minimum_version_id = 1, .post_load = exynos4210_uart_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(rx, Exynos4210UartState, 1, vmstate_exynos4210_uart_fifo, Exynos4210UartFIFO), VMSTATE_UINT32_ARRAY(reg, Exynos4210UartState, diff --git a/hw/char/goldfish_tty.c b/hw/char/goldfish_tty.c index 20b77885c1..f8ff043c39 100644 --- a/hw/char/goldfish_tty.c +++ b/hw/char/goldfish_tty.c @@ -232,7 +232,7 @@ static const VMStateDescription vmstate_goldfish_tty = { .name = "goldfish_tty", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(data_len, GoldfishTTYState), VMSTATE_UINT64(data_ptr, GoldfishTTYState), VMSTATE_BOOL(int_enabled, GoldfishTTYState), diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c index 82ff40a530..515b65bc07 100644 --- a/hw/char/grlib_apbuart.c +++ b/hw/char/grlib_apbuart.c @@ -1,7 +1,9 @@ /* * QEMU GRLIB APB UART Emulator * - * Copyright (c) 2010-2019 AdaCore + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2010-2024 AdaCore * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,7 +28,7 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" -#include "hw/sparc/grlib.h" +#include "hw/char/grlib_uart.h" #include "hw/sysbus.h" #include "qemu/module.h" #include "chardev/char-fe.h" diff --git a/hw/char/ibex_uart.c b/hw/char/ibex_uart.c index e58181fcf4..63aae6dc2c 100644 --- a/hw/char/ibex_uart.c +++ b/hw/char/ibex_uart.c @@ -31,6 +31,7 @@ #include "hw/qdev-clock.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" +#include "hw/registerfields.h" #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" @@ -146,7 +147,7 @@ static gboolean ibex_uart_xmit(void *do_not_use, GIOCondition cond, /* instant drain the fifo when there's no back-end */ if (!qemu_chr_fe_backend_connected(&s->chr)) { s->tx_level = 0; - return FALSE; + return G_SOURCE_REMOVE; } if (!s->tx_level) { @@ -155,7 +156,7 @@ static gboolean ibex_uart_xmit(void *do_not_use, GIOCondition cond, s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK; s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK; ibex_uart_update_irqs(s); - return FALSE; + return G_SOURCE_REMOVE; } ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_level); @@ -170,7 +171,7 @@ static gboolean ibex_uart_xmit(void *do_not_use, GIOCondition cond, ibex_uart_xmit, s); if (!r) { s->tx_level = 0; - return FALSE; + return G_SOURCE_REMOVE; } } @@ -191,7 +192,7 @@ static gboolean ibex_uart_xmit(void *do_not_use, GIOCondition cond, } ibex_uart_update_irqs(s); - return FALSE; + return G_SOURCE_REMOVE; } static void uart_write_tx_fifo(IbexUartState *s, const uint8_t *buf, @@ -487,7 +488,7 @@ static const VMStateDescription vmstate_ibex_uart = { .version_id = 1, .minimum_version_id = 1, .post_load = ibex_uart_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(tx_fifo, IbexUartState, IBEX_UART_TX_FIFO_SIZE), VMSTATE_UINT32(tx_level, IbexUartState), diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index ee1375e26d..ba37be6faa 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -26,6 +26,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" +#include "qemu/fifo32.h" #ifndef DEBUG_IMX_UART #define DEBUG_IMX_UART 0 @@ -41,10 +42,11 @@ static const VMStateDescription vmstate_imx_serial = { .name = TYPE_IMX_SERIAL, - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_INT32(readbuff, IMXSerialState), + .version_id = 3, + .minimum_version_id = 3, + .fields = (const VMStateField[]) { + VMSTATE_FIFO32(rx_fifo, IMXSerialState), + VMSTATE_TIMER(ageing_timer, IMXSerialState), VMSTATE_UINT32(usr1, IMXSerialState), VMSTATE_UINT32(usr2, IMXSerialState), VMSTATE_UINT32(ucr1, IMXSerialState), @@ -71,6 +73,10 @@ static void imx_update(IMXSerialState *s) * following: */ usr1 = s->usr1 & s->ucr1 & (USR1_TRDY | USR1_RRDY); + /* + * Interrupt if AGTIM is set (ageing timer interrupt in RxFIFO) + */ + usr1 |= (s->ucr2 & UCR2_ATEN) ? (s->usr1 & USR1_AGTIM) : 0; /* * Bits that we want in USR2 are not as conveniently laid out, * unfortunately. @@ -78,15 +84,66 @@ static void imx_update(IMXSerialState *s) mask = (s->ucr1 & UCR1_TXMPTYEN) ? USR2_TXFE : 0; /* * TCEN and TXDC are both bit 3 + * ORE and OREN are both bit 1 * RDR and DREN are both bit 0 */ - mask |= s->ucr4 & (UCR4_TCEN | UCR4_DREN); + mask |= s->ucr4 & (UCR4_WKEN | UCR4_TCEN | UCR4_DREN | UCR4_OREN); usr2 = s->usr2 & mask; qemu_set_irq(s->irq, usr1 || usr2); } +static void imx_serial_rx_fifo_push(IMXSerialState *s, uint32_t value) +{ + uint32_t pushed_value = value; + if (fifo32_is_full(&s->rx_fifo)) { + /* Set ORE if FIFO is already full */ + s->usr2 |= USR2_ORE; + } else { + if (fifo32_num_used(&s->rx_fifo) == FIFO_SIZE - 1) { + /* Set OVRRUN on 32nd character in FIFO */ + pushed_value |= URXD_ERR | URXD_OVRRUN; + } + fifo32_push(&s->rx_fifo, pushed_value); + } +} + +static uint32_t imx_serial_rx_fifo_pop(IMXSerialState *s) +{ + if (fifo32_is_empty(&s->rx_fifo)) { + return 0; + } + return fifo32_pop(&s->rx_fifo); +} + +static void imx_serial_rx_fifo_ageing_timer_int(void *opaque) +{ + IMXSerialState *s = (IMXSerialState *) opaque; + s->usr1 |= USR1_AGTIM; + imx_update(s); +} + +static void imx_serial_rx_fifo_ageing_timer_restart(void *opaque) +{ + /* + * Ageing timer starts ticking when + * RX FIFO is non empty and below trigger level. + * Timer is reset if new character is received or + * a FIFO read occurs. + * Timer triggers an interrupt when duration of + * 8 characters has passed (assuming 115200 baudrate). + */ + IMXSerialState *s = (IMXSerialState *) opaque; + + if (!(s->usr1 & USR1_RRDY) && !(s->uts1 & UTS1_RXEMPTY)) { + timer_mod_ns(&s->ageing_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + AGE_DURATION_NS); + } else { + timer_del(&s->ageing_timer); + } +} + static void imx_serial_reset(IMXSerialState *s) { @@ -102,7 +159,9 @@ static void imx_serial_reset(IMXSerialState *s) s->ucr3 = 0x700; s->ubmr = 0; s->ubrc = 4; - s->readbuff = URXD_ERR; + + fifo32_reset(&s->rx_fifo); + timer_del(&s->ageing_timer); } static void imx_serial_reset_at_boot(DeviceState *dev) @@ -112,7 +171,7 @@ static void imx_serial_reset_at_boot(DeviceState *dev) imx_serial_reset(s); /* - * enable the uart on boot, so messages from the linux decompresser + * enable the uart on boot, so messages from the linux decompressor * are visible. On real hardware this is done by the boot rom * before anything else is loaded. */ @@ -125,20 +184,28 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset, unsigned size) { IMXSerialState *s = (IMXSerialState *)opaque; - uint32_t c; + uint32_t c, rx_used; + uint8_t rxtl = s->ufcr & TL_MASK; DPRINTF("read(offset=0x%" HWADDR_PRIx ")\n", offset); switch (offset >> 2) { case 0x0: /* URXD */ - c = s->readbuff; + c = imx_serial_rx_fifo_pop(s); if (!(s->uts1 & UTS1_RXEMPTY)) { /* Character is valid */ c |= URXD_CHARRDY; - s->usr1 &= ~USR1_RRDY; - s->usr2 &= ~USR2_RDR; - s->uts1 |= UTS1_RXEMPTY; + rx_used = fifo32_num_used(&s->rx_fifo); + /* Clear RRDY if below threshold */ + if (rx_used < rxtl) { + s->usr1 &= ~USR1_RRDY; + } + if (rx_used == 0) { + s->usr2 &= ~USR2_RDR; + s->uts1 |= UTS1_RXEMPTY; + } imx_update(s); + imx_serial_rx_fifo_ageing_timer_restart(s); qemu_chr_fe_accept_input(&s->chr); } return c; @@ -300,19 +367,24 @@ static void imx_serial_write(void *opaque, hwaddr offset, static int imx_can_receive(void *opaque) { IMXSerialState *s = (IMXSerialState *)opaque; - return !(s->usr1 & USR1_RRDY); + return s->ucr2 & UCR2_RXEN && fifo32_num_used(&s->rx_fifo) < FIFO_SIZE; } static void imx_put_data(void *opaque, uint32_t value) { IMXSerialState *s = (IMXSerialState *)opaque; + uint8_t rxtl = s->ufcr & TL_MASK; DPRINTF("received char\n"); + imx_serial_rx_fifo_push(s, value); + if (fifo32_num_used(&s->rx_fifo) >= rxtl) { + s->usr1 |= USR1_RRDY; + } + + imx_serial_rx_fifo_ageing_timer_restart(s); - s->usr1 |= USR1_RRDY; s->usr2 |= USR2_RDR; s->uts1 &= ~UTS1_RXEMPTY; - s->readbuff = value; if (value & URXD_BRK) { s->usr2 |= USR2_BRCD; } @@ -321,6 +393,9 @@ static void imx_put_data(void *opaque, uint32_t value) static void imx_receive(void *opaque, const uint8_t *buf, int size) { + IMXSerialState *s = (IMXSerialState *)opaque; + + s->usr2 |= USR2_WAKE; imx_put_data(opaque, *buf); } @@ -342,6 +417,10 @@ static void imx_serial_realize(DeviceState *dev, Error **errp) { IMXSerialState *s = IMX_SERIAL(dev); + fifo32_create(&s->rx_fifo, FIFO_SIZE); + timer_init_ns(&s->ageing_timer, QEMU_CLOCK_VIRTUAL, + imx_serial_rx_fifo_ageing_timer_int, s); + DPRINTF("char dev for uart: %p\n", qemu_chr_fe_get_driver(&s->chr)); qemu_chr_fe_set_handlers(&s->chr, imx_can_receive, imx_receive, diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c index 3311e0872c..64be5226d4 100644 --- a/hw/char/ipoctal232.c +++ b/hw/char/ipoctal232.c @@ -130,7 +130,7 @@ static const VMStateDescription vmstate_scc2698_channel = { .name = "scc2698_channel", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(rx_enabled, SCC2698Channel), VMSTATE_UINT8_ARRAY(mr, SCC2698Channel, 2), VMSTATE_UINT8(mr_idx, SCC2698Channel), @@ -146,7 +146,7 @@ static const VMStateDescription vmstate_scc2698_block = { .name = "scc2698_block", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(imr, SCC2698Block), VMSTATE_UINT8(isr, SCC2698Block), VMSTATE_END_OF_LIST() @@ -157,7 +157,7 @@ static const VMStateDescription vmstate_ipoctal = { .name = "ipoctal232", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_IPACK_DEVICE(parent_obj, IPOctalState), VMSTATE_STRUCT_ARRAY(ch, IPOctalState, N_CHANNELS, 1, vmstate_scc2698_channel, SCC2698Channel), diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index 6fa4ac502c..f9cbc9bdc4 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -342,25 +342,26 @@ static void mcf_uart_register(void) type_init(mcf_uart_register) -void *mcf_uart_init(qemu_irq irq, Chardev *chrdrv) +DeviceState *mcf_uart_create(qemu_irq irq, Chardev *chrdrv) { - DeviceState *dev; + DeviceState *dev; dev = qdev_new(TYPE_MCF_UART); if (chrdrv) { qdev_prop_set_chr(dev, "chardev", chrdrv); } sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); return dev; } -void mcf_uart_mm_init(hwaddr base, qemu_irq irq, Chardev *chrdrv) +DeviceState *mcf_uart_create_mmap(hwaddr base, qemu_irq irq, Chardev *chrdrv) { - DeviceState *dev; + DeviceState *dev; - dev = mcf_uart_init(irq, chrdrv); + dev = mcf_uart_create(irq, chrdrv); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + + return dev; } diff --git a/hw/char/mchp_pfsoc_mmuart.c b/hw/char/mchp_pfsoc_mmuart.c index 22f3e78eb9..e7908bbfb5 100644 --- a/hw/char/mchp_pfsoc_mmuart.c +++ b/hw/char/mchp_pfsoc_mmuart.c @@ -114,7 +114,7 @@ static const VMStateDescription mchp_pfsoc_mmuart_vmstate = { .name = "mchp.pfsoc.uart", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(reg, MchpPfSoCMMUartState, MCHP_PFSOC_MMUART_REG_COUNT), VMSTATE_END_OF_LIST() diff --git a/hw/char/meson.build b/hw/char/meson.build index 7b594f51b8..006d20f1e2 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -1,41 +1,40 @@ -softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_uart.c')) -softmmu_ss.add(when: 'CONFIG_CMSDK_APB_UART', if_true: files('cmsdk-apb-uart.c')) -softmmu_ss.add(when: 'CONFIG_ESCC', if_true: files('escc.c')) -softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_ser.c')) -softmmu_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_apbuart.c')) -softmmu_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_uart.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_serial.c')) -softmmu_ss.add(when: 'CONFIG_IPACK', if_true: files('ipoctal232.c')) -softmmu_ss.add(when: 'CONFIG_ISA_BUS', if_true: files('parallel-isa.c')) -softmmu_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugcon.c')) -softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_uart.c')) -softmmu_ss.add(when: 'CONFIG_PARALLEL', if_true: files('parallel.c')) -softmmu_ss.add(when: 'CONFIG_PL011', if_true: files('pl011.c')) -softmmu_ss.add(when: 'CONFIG_SCLPCONSOLE', if_true: files('sclpconsole.c', 'sclpconsole-lm.c')) -softmmu_ss.add(when: 'CONFIG_SERIAL', if_true: files('serial.c')) -softmmu_ss.add(when: 'CONFIG_SERIAL_ISA', if_true: files('serial-isa.c')) -softmmu_ss.add(when: 'CONFIG_SERIAL_PCI', if_true: files('serial-pci.c')) -softmmu_ss.add(when: 'CONFIG_SERIAL_PCI_MULTI', if_true: files('serial-pci-multi.c')) -softmmu_ss.add(when: 'CONFIG_SHAKTI_UART', if_true: files('shakti_uart.c')) -softmmu_ss.add(when: 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-console.c')) -softmmu_ss.add(when: 'CONFIG_XEN', if_true: files('xen_console.c')) -softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_uartlite.c')) +system_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_uart.c')) +system_ss.add(when: 'CONFIG_CMSDK_APB_UART', if_true: files('cmsdk-apb-uart.c')) +system_ss.add(when: 'CONFIG_ESCC', if_true: files('escc.c')) +system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_ser.c')) +system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_apbuart.c')) +system_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_uart.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_serial.c')) +system_ss.add(when: 'CONFIG_IPACK', if_true: files('ipoctal232.c')) +system_ss.add(when: 'CONFIG_ISA_BUS', if_true: files('parallel-isa.c')) +system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugcon.c')) +system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_uart.c')) +system_ss.add(when: 'CONFIG_PARALLEL', if_true: files('parallel.c')) +system_ss.add(when: 'CONFIG_PL011', if_true: files('pl011.c')) +system_ss.add(when: 'CONFIG_SCLPCONSOLE', if_true: files('sclpconsole.c', 'sclpconsole-lm.c')) +system_ss.add(when: 'CONFIG_SERIAL', if_true: files('serial.c')) +system_ss.add(when: 'CONFIG_SERIAL_ISA', if_true: files('serial-isa.c')) +system_ss.add(when: 'CONFIG_SERIAL_PCI', if_true: files('serial-pci.c')) +system_ss.add(when: 'CONFIG_SERIAL_PCI_MULTI', if_true: files('serial-pci-multi.c')) +system_ss.add(when: 'CONFIG_SHAKTI_UART', if_true: files('shakti_uart.c')) +system_ss.add(when: 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-console.c')) +system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen_console.c')) +system_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_uartlite.c')) -softmmu_ss.add(when: 'CONFIG_AVR_USART', if_true: files('avr_usart.c')) -softmmu_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_uart.c')) -softmmu_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-uart.c')) -softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_uart.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_uart.c')) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_aux.c')) -softmmu_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_UART', if_true: files('sifive_uart.c')) -softmmu_ss.add(when: 'CONFIG_SH_SCI', if_true: files('sh_serial.c')) -softmmu_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_usart.c')) -softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c')) +system_ss.add(when: 'CONFIG_AVR_USART', if_true: files('avr_usart.c')) +system_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_uart.c')) +system_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-uart.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_uart.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_uart.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_aux.c')) +system_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c')) +system_ss.add(when: 'CONFIG_SIFIVE_UART', if_true: files('sifive_uart.c')) +system_ss.add(when: 'CONFIG_SH_SCI', if_true: files('sh_serial.c')) +system_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_usart.c')) +system_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c')) +system_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c')) +system_ss.add(when: 'CONFIG_GOLDFISH_TTY', if_true: files('goldfish_tty.c')) -specific_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c')) specific_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('terminal3270.c')) specific_ss.add(when: 'CONFIG_VIRTIO', if_true: files('virtio-serial-bus.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_vty.c')) - -specific_ss.add(when: 'CONFIG_GOLDFISH_TTY', if_true: files('goldfish_tty.c')) diff --git a/hw/char/nrf51_uart.c b/hw/char/nrf51_uart.c index 3c6f982de9..c2cd6bb5e7 100644 --- a/hw/char/nrf51_uart.c +++ b/hw/char/nrf51_uart.c @@ -93,13 +93,13 @@ static gboolean uart_transmit(void *do_not_use, GIOCondition cond, void *opaque) */ goto buffer_drained; } - return FALSE; + return G_SOURCE_REMOVE; } buffer_drained: s->reg[R_UART_TXDRDY] = 1; s->pending_tx_byte = false; - return FALSE; + return G_SOURCE_REMOVE; } static void uart_cancel_transmit(NRF51UARTState *s) @@ -291,7 +291,7 @@ static int nrf51_uart_post_load(void *opaque, int version_id) static const VMStateDescription nrf51_uart_vmstate = { .name = "nrf51_soc.uart", .post_load = nrf51_uart_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(reg, NRF51UARTState, 0x56C), VMSTATE_UINT8_ARRAY(rx_fifo, NRF51UARTState, UART_FIFO_LENGTH), VMSTATE_UINT32(rx_fifo_pos, NRF51UARTState), diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c index e8da933378..6848bddb4e 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -67,10 +67,9 @@ struct omap_uart_s *omap_uart_init(hwaddr base, return s; } -static uint64_t omap_uart_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_uart_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_uart_s *s = (struct omap_uart_s *) opaque; + struct omap_uart_s *s = opaque; if (size == 4) { return omap_badwidth_read8(opaque, addr); @@ -108,7 +107,7 @@ static uint64_t omap_uart_read(void *opaque, hwaddr addr, static void omap_uart_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_uart_s *s = (struct omap_uart_s *) opaque; + struct omap_uart_s *s = opaque; if (size == 4) { omap_badwidth_write8(opaque, addr, value); @@ -176,12 +175,3 @@ struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem, return s; } - -void omap_uart_attach(struct omap_uart_s *s, Chardev *chr) -{ - /* TODO: Should reuse or destroy current s->serial */ - s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq, - omap_clk_getrate(s->fclk) / 16, - chr ?: qemu_chr_new("null", "null", NULL), - DEVICE_NATIVE_ENDIAN); -} diff --git a/hw/char/parallel-isa.c b/hw/char/parallel-isa.c index 1ccbb96e70..a5ce6ee13a 100644 --- a/hw/char/parallel-isa.c +++ b/hw/char/parallel-isa.c @@ -13,6 +13,7 @@ #include "sysemu/sysemu.h" #include "hw/isa/isa.h" #include "hw/qdev-properties.h" +#include "hw/char/parallel-isa.h" #include "hw/char/parallel.h" #include "qapi/error.h" @@ -21,7 +22,7 @@ static void parallel_init(ISABus *bus, int index, Chardev *chr) DeviceState *dev; ISADevice *isadev; - isadev = isa_new("isa-parallel"); + isadev = isa_new(TYPE_ISA_PARALLEL); dev = DEVICE(isadev); qdev_prop_set_uint32(dev, "index", index); qdev_prop_set_chr(dev, "chardev", chr); @@ -40,3 +41,17 @@ void parallel_hds_isa_init(ISABus *bus, int n) } } } + +void isa_parallel_set_iobase(ISADevice *parallel, hwaddr iobase) +{ + ISAParallelState *s = ISA_PARALLEL(parallel); + + parallel->ioport_id = iobase; + s->iobase = iobase; + portio_list_set_address(&s->portio_list, s->iobase); +} + +void isa_parallel_set_enabled(ISADevice *parallel, bool enabled) +{ + portio_list_set_enabled(&ISA_PARALLEL(parallel)->portio_list, enabled); +} diff --git a/hw/char/parallel.c b/hw/char/parallel.c index f735a6cd7f..c394635ada 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -27,13 +27,11 @@ #include "qapi/error.h" #include "qemu/module.h" #include "chardev/char-parallel.h" -#include "chardev/char-fe.h" -#include "hw/acpi/aml-build.h" -#include "hw/irq.h" -#include "hw/isa/isa.h" +#include "hw/acpi/acpi_aml_interface.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" +#include "hw/char/parallel-isa.h" #include "hw/char/parallel.h" #include "sysemu/reset.h" #include "sysemu/sysemu.h" @@ -57,54 +55,25 @@ /* * These are the definitions for the Printer Status Register */ -#define PARA_STS_BUSY 0x80 /* Busy complement */ -#define PARA_STS_ACK 0x40 /* Acknowledge */ -#define PARA_STS_PAPER 0x20 /* Out of paper */ -#define PARA_STS_ONLINE 0x10 /* Online */ -#define PARA_STS_ERROR 0x08 /* Error complement */ -#define PARA_STS_TMOUT 0x01 /* EPP timeout */ +#define PARA_STS_BUSY 0x80 /* Busy complement */ +#define PARA_STS_ACK 0x40 /* Acknowledge */ +#define PARA_STS_PAPER 0x20 /* Out of paper */ +#define PARA_STS_ONLINE 0x10 /* Online */ +#define PARA_STS_ERROR 0x08 /* Error complement */ +#define PARA_STS_TMOUT 0x01 /* EPP timeout */ /* * These are the definitions for the Printer Control Register */ -#define PARA_CTR_DIR 0x20 /* Direction (1=read, 0=write) */ -#define PARA_CTR_INTEN 0x10 /* IRQ Enable */ -#define PARA_CTR_SELECT 0x08 /* Select In complement */ -#define PARA_CTR_INIT 0x04 /* Initialize Printer complement */ -#define PARA_CTR_AUTOLF 0x02 /* Auto linefeed complement */ -#define PARA_CTR_STROBE 0x01 /* Strobe complement */ +#define PARA_CTR_DIR 0x20 /* Direction (1=read, 0=write) */ +#define PARA_CTR_INTEN 0x10 /* IRQ Enable */ +#define PARA_CTR_SELECT 0x08 /* Select In complement */ +#define PARA_CTR_INIT 0x04 /* Initialize Printer complement */ +#define PARA_CTR_AUTOLF 0x02 /* Auto linefeed complement */ +#define PARA_CTR_STROBE 0x01 /* Strobe complement */ #define PARA_CTR_SIGNAL (PARA_CTR_SELECT|PARA_CTR_INIT|PARA_CTR_AUTOLF|PARA_CTR_STROBE) -typedef struct ParallelState { - MemoryRegion iomem; - uint8_t dataw; - uint8_t datar; - uint8_t status; - uint8_t control; - qemu_irq irq; - int irq_pending; - CharBackend chr; - int hw_driver; - int epp_timeout; - uint32_t last_read_offset; /* For debugging */ - /* Memory-mapped interface */ - int it_shift; - PortioList portio_list; -} ParallelState; - -#define TYPE_ISA_PARALLEL "isa-parallel" -OBJECT_DECLARE_SIMPLE_TYPE(ISAParallelState, ISA_PARALLEL) - -struct ISAParallelState { - ISADevice parent_obj; - - uint32_t index; - uint32_t iobase; - uint32_t isairq; - ParallelState state; -}; - static void parallel_update_irq(ParallelState *s) { if (s->irq_pending) @@ -509,7 +478,7 @@ static const VMStateDescription vmstate_parallel_isa = { .name = "parallel_isa", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(state.dataw, ISAParallelState), VMSTATE_UINT8(state.datar, ISAParallelState), VMSTATE_UINT8(state.status, ISAParallelState), @@ -563,16 +532,16 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp) s->status = dummy; } - isa_register_portio_list(isadev, &s->portio_list, base, + isa_register_portio_list(isadev, &isa->portio_list, base, (s->hw_driver ? &isa_parallel_portio_hw_list[0] : &isa_parallel_portio_sw_list[0]), s, "parallel"); } -static void parallel_isa_build_aml(ISADevice *isadev, Aml *scope) +static void parallel_isa_build_aml(AcpiDevAmlIf *adev, Aml *scope) { - ISAParallelState *isa = ISA_PARALLEL(isadev); + ISAParallelState *isa = ISA_PARALLEL(adev); Aml *dev; Aml *crs; @@ -645,11 +614,11 @@ static Property parallel_isa_properties[] = { static void parallel_isa_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - ISADeviceClass *isa = ISA_DEVICE_CLASS(klass); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); dc->realize = parallel_isa_realizefn; dc->vmsd = &vmstate_parallel_isa; - isa->build_aml = parallel_isa_build_aml; + adevc->build_dev_aml = parallel_isa_build_aml; device_class_set_props(dc, parallel_isa_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } @@ -659,6 +628,10 @@ static const TypeInfo parallel_isa_info = { .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISAParallelState), .class_init = parallel_isa_class_initfn, + .interfaces = (InterfaceInfo[]) { + { TYPE_ACPI_DEV_AML_IF }, + { }, + }, }; static void parallel_register_types(void) diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 6e2d7f7509..8753b84a84 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -19,10 +19,12 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/char/pl011.h" #include "hw/irq.h" #include "hw/sysbus.h" #include "hw/qdev-clock.h" +#include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" #include "chardev/char-fe.h" @@ -31,13 +33,33 @@ #include "qemu/module.h" #include "trace.h" -#define PL011_INT_TX 0x20 -#define PL011_INT_RX 0x10 +DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr) +{ + DeviceState *dev; + SysBusDevice *s; + dev = qdev_new("pl011"); + s = SYS_BUS_DEVICE(dev); + qdev_prop_set_chr(dev, "chardev", chr); + sysbus_realize_and_unref(s, &error_fatal); + sysbus_mmio_map(s, 0, addr); + sysbus_connect_irq(s, 0, irq); + + return dev; +} + +/* Flag Register, UARTFR */ +#define PL011_FLAG_RI 0x100 #define PL011_FLAG_TXFE 0x80 #define PL011_FLAG_RXFF 0x40 #define PL011_FLAG_TXFF 0x20 #define PL011_FLAG_RXFE 0x10 +#define PL011_FLAG_DCD 0x04 +#define PL011_FLAG_DSR 0x02 +#define PL011_FLAG_CTS 0x01 + +/* Data Register, UARTDR */ +#define DR_BE (1 << 10) /* Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC */ #define INT_OE (1 << 10) @@ -54,11 +76,40 @@ #define INT_E (INT_OE | INT_BE | INT_PE | INT_FE) #define INT_MS (INT_RI | INT_DSR | INT_DCD | INT_CTS) +/* Line Control Register, UARTLCR_H */ +#define LCR_FEN (1 << 4) +#define LCR_BRK (1 << 0) + +/* Control Register, UARTCR */ +#define CR_OUT2 (1 << 13) +#define CR_OUT1 (1 << 12) +#define CR_RTS (1 << 11) +#define CR_DTR (1 << 10) +#define CR_LBE (1 << 7) + static const unsigned char pl011_id_arm[8] = { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; static const unsigned char pl011_id_luminary[8] = { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }; +static const char *pl011_regname(hwaddr offset) +{ + static const char *const rname[] = { + [0] = "DR", [1] = "RSR", [6] = "FR", [8] = "ILPR", [9] = "IBRD", + [10] = "FBRD", [11] = "LCRH", [12] = "CR", [13] = "IFLS", [14] = "IMSC", + [15] = "RIS", [16] = "MIS", [17] = "ICR", [18] = "DMACR", + }; + unsigned idx = offset >> 2; + + if (idx < ARRAY_SIZE(rname) && rname[idx]) { + return rname[idx]; + } + if (idx >= 0x3f8 && idx <= 0x400) { + return "ID"; + } + return "UNKN"; +} + /* Which bits in the interrupt status matter for each outbound IRQ line ? */ static const uint32_t irqmask[] = { INT_E | INT_MS | INT_RT | INT_TX | INT_RX, /* combined IRQ */ @@ -81,6 +132,27 @@ static void pl011_update(PL011State *s) } } +static bool pl011_is_fifo_enabled(PL011State *s) +{ + return (s->lcr & LCR_FEN) != 0; +} + +static inline unsigned pl011_get_fifo_depth(PL011State *s) +{ + /* Note: FIFO depth is expected to be power-of-2 */ + return pl011_is_fifo_enabled(s) ? PL011_FIFO_DEPTH : 1; +} + +static inline void pl011_reset_fifo(PL011State *s) +{ + s->read_count = 0; + s->read_pos = 0; + + /* Reset FIFO flags */ + s->flags &= ~(PL011_FLAG_RXFF | PL011_FLAG_TXFF); + s->flags |= PL011_FLAG_RXFE | PL011_FLAG_TXFE; +} + static uint64_t pl011_read(void *opaque, hwaddr offset, unsigned size) { @@ -94,14 +166,13 @@ static uint64_t pl011_read(void *opaque, hwaddr offset, c = s->read_fifo[s->read_pos]; if (s->read_count > 0) { s->read_count--; - if (++s->read_pos == 16) - s->read_pos = 0; + s->read_pos = (s->read_pos + 1) & (pl011_get_fifo_depth(s) - 1); } if (s->read_count == 0) { s->flags |= PL011_FLAG_RXFE; } if (s->read_count == s->read_trigger - 1) - s->int_level &= ~ PL011_INT_RX; + s->int_level &= ~ INT_RX; trace_pl011_read_fifo(s->read_count); s->rsr = c >> 8; pl011_update(s); @@ -154,7 +225,7 @@ static uint64_t pl011_read(void *opaque, hwaddr offset, break; } - trace_pl011_read(offset, r); + trace_pl011_read(offset, r, pl011_regname(offset)); return r; } @@ -165,7 +236,7 @@ static void pl011_set_read_trigger(PL011State *s) the threshold. However linux only reads the FIFO in response to an interrupt. Triggering the interrupt when the FIFO is non-empty seems to make things work. */ - if (s->lcr & 0x10) + if (s->lcr & LCR_FEN) s->read_trigger = (s->ifl >> 1) & 0x1c; else #endif @@ -176,7 +247,7 @@ static unsigned int pl011_get_baudrate(const PL011State *s) { uint64_t clk; - if (s->fbrd == 0) { + if (s->ibrd == 0) { return 0; } @@ -191,13 +262,96 @@ static void pl011_trace_baudrate_change(const PL011State *s) s->ibrd, s->fbrd); } +static bool pl011_loopback_enabled(PL011State *s) +{ + return !!(s->cr & CR_LBE); +} + +static void pl011_loopback_mdmctrl(PL011State *s) +{ + uint32_t cr, fr, il; + + if (!pl011_loopback_enabled(s)) { + return; + } + + /* + * Loopback software-driven modem control outputs to modem status inputs: + * FR.RI <= CR.Out2 + * FR.DCD <= CR.Out1 + * FR.CTS <= CR.RTS + * FR.DSR <= CR.DTR + * + * The loopback happens immediately even if this call is triggered + * by setting only CR.LBE. + * + * CTS/RTS updates due to enabled hardware flow controls are not + * dealt with here. + */ + cr = s->cr; + fr = s->flags & ~(PL011_FLAG_RI | PL011_FLAG_DCD | + PL011_FLAG_DSR | PL011_FLAG_CTS); + fr |= (cr & CR_OUT2) ? PL011_FLAG_RI : 0; + fr |= (cr & CR_OUT1) ? PL011_FLAG_DCD : 0; + fr |= (cr & CR_RTS) ? PL011_FLAG_CTS : 0; + fr |= (cr & CR_DTR) ? PL011_FLAG_DSR : 0; + + /* Change interrupts based on updated FR */ + il = s->int_level & ~(INT_DSR | INT_DCD | INT_CTS | INT_RI); + il |= (fr & PL011_FLAG_DSR) ? INT_DSR : 0; + il |= (fr & PL011_FLAG_DCD) ? INT_DCD : 0; + il |= (fr & PL011_FLAG_CTS) ? INT_CTS : 0; + il |= (fr & PL011_FLAG_RI) ? INT_RI : 0; + + s->flags = fr; + s->int_level = il; + pl011_update(s); +} + +static void pl011_put_fifo(void *opaque, uint32_t value); + +static void pl011_loopback_tx(PL011State *s, uint32_t value) +{ + if (!pl011_loopback_enabled(s)) { + return; + } + + /* + * Caveat: + * + * In real hardware, TX loopback happens at the serial-bit level + * and then reassembled by the RX logics back into bytes and placed + * into the RX fifo. That is, loopback happens after TX fifo. + * + * Because the real hardware TX fifo is time-drained at the frame + * rate governed by the configured serial format, some loopback + * bytes in TX fifo may still be able to get into the RX fifo + * that could be full at times while being drained at software + * pace. + * + * In such scenario, the RX draining pace is the major factor + * deciding which loopback bytes get into the RX fifo, unless + * hardware flow-control is enabled. + * + * For simplicity, the above described is not emulated. + */ + pl011_put_fifo(s, value); +} + +static void pl011_loopback_break(PL011State *s, int brk_enable) +{ + if (brk_enable) { + pl011_loopback_tx(s, DR_BE); + } +} + static void pl011_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { PL011State *s = (PL011State *)opaque; unsigned char ch; - trace_pl011_write(offset, value); + trace_pl011_write(offset, value, pl011_regname(offset)); switch (offset >> 2) { case 0: /* UARTDR */ @@ -206,7 +360,8 @@ static void pl011_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); - s->int_level |= PL011_INT_TX; + pl011_loopback_tx(s, ch); + s->int_level |= INT_TX; pl011_update(s); break; case 1: /* UARTRSR/UARTECR */ @@ -215,7 +370,7 @@ static void pl011_write(void *opaque, hwaddr offset, case 6: /* UARTFR */ /* Writes to Flag register are ignored. */ break; - case 8: /* UARTUARTILPR */ + case 8: /* UARTILPR */ s->ilpr = value; break; case 9: /* UARTIBRD */ @@ -228,21 +383,22 @@ static void pl011_write(void *opaque, hwaddr offset, break; case 11: /* UARTLCR_H */ /* Reset the FIFO state on FIFO enable or disable */ - if ((s->lcr ^ value) & 0x10) { - s->read_count = 0; - s->read_pos = 0; + if ((s->lcr ^ value) & LCR_FEN) { + pl011_reset_fifo(s); } - if ((s->lcr ^ value) & 0x1) { - int break_enable = value & 0x1; + if ((s->lcr ^ value) & LCR_BRK) { + int break_enable = value & LCR_BRK; qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK, &break_enable); + pl011_loopback_break(s, break_enable); } s->lcr = value; pl011_set_read_trigger(s); break; case 12: /* UARTCR */ - /* ??? Need to implement the enable and loopback bits. */ + /* ??? Need to implement the enable bit. */ s->cr = value; + pl011_loopback_mdmctrl(s); break; case 13: /* UARTIFS */ s->ifl = value; @@ -273,11 +429,7 @@ static int pl011_can_receive(void *opaque) PL011State *s = (PL011State *)opaque; int r; - if (s->lcr & 0x10) { - r = s->read_count < 16; - } else { - r = s->read_count < 1; - } + r = s->read_count < pl011_get_fifo_depth(s); trace_pl011_can_receive(s->lcr, s->read_count, r); return r; } @@ -286,33 +438,43 @@ static void pl011_put_fifo(void *opaque, uint32_t value) { PL011State *s = (PL011State *)opaque; int slot; + unsigned pipe_depth; - slot = s->read_pos + s->read_count; - if (slot >= 16) - slot -= 16; + pipe_depth = pl011_get_fifo_depth(s); + slot = (s->read_pos + s->read_count) & (pipe_depth - 1); s->read_fifo[slot] = value; s->read_count++; s->flags &= ~PL011_FLAG_RXFE; trace_pl011_put_fifo(value, s->read_count); - if (!(s->lcr & 0x10) || s->read_count == 16) { + if (s->read_count == pipe_depth) { trace_pl011_put_fifo_full(); s->flags |= PL011_FLAG_RXFF; } if (s->read_count == s->read_trigger) { - s->int_level |= PL011_INT_RX; + s->int_level |= INT_RX; pl011_update(s); } } static void pl011_receive(void *opaque, const uint8_t *buf, int size) { + /* + * In loopback mode, the RX input signal is internally disconnected + * from the entire receiving logics; thus, all inputs are ignored, + * and BREAK detection on RX input signal is also not performed. + */ + if (pl011_loopback_enabled(opaque)) { + return; + } + pl011_put_fifo(opaque, *buf); } static void pl011_event(void *opaque, QEMUChrEvent event) { - if (event == CHR_EVENT_BREAK) - pl011_put_fifo(opaque, 0x400); + if (event == CHR_EVENT_BREAK && !pl011_loopback_enabled(opaque)) { + pl011_put_fifo(opaque, DR_BE); + } } static void pl011_clock_update(void *opaque, ClockEvent event) @@ -326,6 +488,8 @@ static const MemoryRegionOps pl011_ops = { .read = pl011_read, .write = pl011_write, .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, }; static bool pl011_clock_needed(void *opaque) @@ -340,17 +504,42 @@ static const VMStateDescription vmstate_pl011_clock = { .version_id = 1, .minimum_version_id = 1, .needed = pl011_clock_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CLOCK(clk, PL011State), VMSTATE_END_OF_LIST() } }; +static int pl011_post_load(void *opaque, int version_id) +{ + PL011State* s = opaque; + + /* Sanity-check input state */ + if (s->read_pos >= ARRAY_SIZE(s->read_fifo) || + s->read_count > ARRAY_SIZE(s->read_fifo)) { + return -1; + } + + if (!pl011_is_fifo_enabled(s) && s->read_count > 0 && s->read_pos > 0) { + /* + * Older versions of PL011 didn't ensure that the single + * character in the FIFO in FIFO-disabled mode is in + * element 0 of the array; convert to follow the current + * code's assumptions. + */ + s->read_fifo[0] = s->read_fifo[s->read_pos]; + s->read_pos = 0; + } + + return 0; +} + static const VMStateDescription vmstate_pl011 = { .name = "pl011", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .post_load = pl011_post_load, + .fields = (const VMStateField[]) { VMSTATE_UINT32(readbuff, PL011State), VMSTATE_UINT32(flags, PL011State), VMSTATE_UINT32(lcr, PL011State), @@ -359,7 +548,7 @@ static const VMStateDescription vmstate_pl011 = { VMSTATE_UINT32(dmacr, PL011State), VMSTATE_UINT32(int_enabled, PL011State), VMSTATE_UINT32(int_level, PL011State), - VMSTATE_UINT32_ARRAY(read_fifo, PL011State, 16), + VMSTATE_UINT32_ARRAY(read_fifo, PL011State, PL011_FIFO_DEPTH), VMSTATE_UINT32(ilpr, PL011State), VMSTATE_UINT32(ibrd, PL011State), VMSTATE_UINT32(fbrd, PL011State), @@ -369,7 +558,7 @@ static const VMStateDescription vmstate_pl011 = { VMSTATE_INT32(read_trigger, PL011State), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_pl011_clock, NULL } @@ -396,11 +585,6 @@ static void pl011_init(Object *obj) s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s, ClockUpdate); - s->read_trigger = 1; - s->ifl = 0x12; - s->cr = 0x300; - s->flags = 0x90; - s->id = pl011_id_arm; } @@ -412,11 +596,31 @@ static void pl011_realize(DeviceState *dev, Error **errp) pl011_event, NULL, s, NULL, true); } +static void pl011_reset(DeviceState *dev) +{ + PL011State *s = PL011(dev); + + s->lcr = 0; + s->rsr = 0; + s->dmacr = 0; + s->int_enabled = 0; + s->int_level = 0; + s->ilpr = 0; + s->ibrd = 0; + s->fbrd = 0; + s->read_trigger = 1; + s->ifl = 0x12; + s->cr = 0x300; + s->flags = 0; + pl011_reset_fifo(s); +} + static void pl011_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = pl011_realize; + dc->reset = pl011_reset; dc->vmsd = &vmstate_pl011; device_class_set_props(dc, pl011_properties); } diff --git a/hw/char/renesas_sci.c b/hw/char/renesas_sci.c index 1c63467290..5cb733545c 100644 --- a/hw/char/renesas_sci.c +++ b/hw/char/renesas_sci.c @@ -302,7 +302,7 @@ static const VMStateDescription vmstate_rsci = { .name = "renesas-sci", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64(trtime, RSCIState), VMSTATE_INT64(rx_next, RSCIState), VMSTATE_UINT8(smr, RSCIState), diff --git a/hw/char/riscv_htif.c b/hw/char/riscv_htif.c index 6577f0e640..9bef60def1 100644 --- a/hw/char/riscv_htif.c +++ b/hw/char/riscv_htif.c @@ -29,6 +29,10 @@ #include "chardev/char-fe.h" #include "qemu/timer.h" #include "qemu/error-report.h" +#include "exec/address-spaces.h" +#include "exec/tswap.h" +#include "sysemu/dma.h" +#include "sysemu/runstate.h" #define RISCV_DEBUG_HTIF 0 #define HTIF_DEBUG(fmt, ...) \ @@ -38,26 +42,43 @@ } \ } while (0) -static uint64_t fromhost_addr, tohost_addr; -static int address_symbol_set; +#define HTIF_DEV_SHIFT 56 +#define HTIF_CMD_SHIFT 48 + +#define HTIF_DEV_SYSTEM 0 +#define HTIF_DEV_CONSOLE 1 + +#define HTIF_SYSTEM_CMD_SYSCALL 0 +#define HTIF_CONSOLE_CMD_GETC 0 +#define HTIF_CONSOLE_CMD_PUTC 1 + +/* PK system call number */ +#define PK_SYS_WRITE 64 + +const char *sig_file; +uint8_t line_size = 16; + +static uint64_t fromhost_addr, tohost_addr, begin_sig_addr, end_sig_addr; void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value, uint64_t st_size) { if (strcmp("fromhost", st_name) == 0) { - address_symbol_set |= 1; fromhost_addr = st_value; if (st_size != 8) { error_report("HTIF fromhost must be 8 bytes"); exit(1); } } else if (strcmp("tohost", st_name) == 0) { - address_symbol_set |= 2; tohost_addr = st_value; if (st_size != 8) { error_report("HTIF tohost must be 8 bytes"); exit(1); } + } else if (strcmp("begin_signature", st_name) == 0) { + begin_sig_addr = st_value; + } else if (strcmp("end_signature", st_name) == 0) { + end_sig_addr = st_value; } } @@ -75,20 +96,22 @@ static int htif_can_recv(void *opaque) */ static void htif_recv(void *opaque, const uint8_t *buf, int size) { - HTIFState *htifstate = opaque; + HTIFState *s = opaque; if (size != 1) { return; } - /* TODO - we need to check whether mfromhost is zero which indicates - the device is ready to receive. The current implementation - will drop characters */ + /* + * TODO - we need to check whether mfromhost is zero which indicates + * the device is ready to receive. The current implementation + * will drop characters + */ - uint64_t val_written = htifstate->pending_read; + uint64_t val_written = s->pending_read; uint64_t resp = 0x100 | *buf; - htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); + s->fromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); } /* @@ -110,10 +133,30 @@ static int htif_be_change(void *opaque) return 0; } -static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written) +/* + * See below the tohost register format. + * + * Bits 63:56 indicate the "device". + * Bits 55:48 indicate the "command". + * + * Device 0 is the syscall device, which is used to emulate Unixy syscalls. + * It only implements command 0, which has two subfunctions: + * - If bit 0 is clear, then bits 47:0 represent a pointer to a struct + * describing the syscall. + * - If bit 1 is set, then bits 47:1 represent an exit code, with a zero + * value indicating success and other values indicating failure. + * + * Device 1 is the blocking character device. + * - Command 0 reads a character + * - Command 1 writes a character from the 8 LSBs of tohost + * + * For RV32, the tohost register is zero-extended, so only device=0 and + * command=0 (i.e. HTIF syscalls/exit codes) are supported. + */ +static void htif_handle_tohost_write(HTIFState *s, uint64_t val_written) { - uint8_t device = val_written >> 56; - uint8_t cmd = val_written >> 48; + uint8_t device = val_written >> HTIF_DEV_SHIFT; + uint8_t cmd = val_written >> HTIF_CMD_SHIFT; uint64_t payload = val_written & 0xFFFFFFFFFFFFULL; int resp = 0; @@ -125,28 +168,76 @@ static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written) * 0: riscv-tests Pass/Fail Reporting Only (no syscall proxy) * 1: Console */ - if (unlikely(device == 0x0)) { + if (unlikely(device == HTIF_DEV_SYSTEM)) { /* frontend syscall handler, shutdown and exit code support */ - if (cmd == 0x0) { + if (cmd == HTIF_SYSTEM_CMD_SYSCALL) { if (payload & 0x1) { /* exit code */ int exit_code = payload >> 1; - exit(exit_code); + + /* + * Dump signature data if sig_file is specified and + * begin/end_signature symbols exist. + */ + if (sig_file && begin_sig_addr && end_sig_addr) { + uint64_t sig_len = end_sig_addr - begin_sig_addr; + char *sig_data = g_malloc(sig_len); + dma_memory_read(&address_space_memory, begin_sig_addr, + sig_data, sig_len, MEMTXATTRS_UNSPECIFIED); + FILE *signature = fopen(sig_file, "w"); + if (signature == NULL) { + error_report("Unable to open %s with error %s", + sig_file, strerror(errno)); + exit(1); + } + + for (int i = 0; i < sig_len; i += line_size) { + for (int j = line_size; j > 0; j--) { + if (i + j <= sig_len) { + fprintf(signature, "%02x", + sig_data[i + j - 1] & 0xff); + } else { + fprintf(signature, "%02x", 0); + } + } + fprintf(signature, "\n"); + } + + fclose(signature); + g_free(sig_data); + } + + qemu_system_shutdown_request_with_code( + SHUTDOWN_CAUSE_GUEST_SHUTDOWN, exit_code); + return; } else { - qemu_log_mask(LOG_UNIMP, "pk syscall proxy not supported\n"); + uint64_t syscall[8]; + cpu_physical_memory_read(payload, syscall, sizeof(syscall)); + if (tswap64(syscall[0]) == PK_SYS_WRITE && + tswap64(syscall[1]) == HTIF_DEV_CONSOLE && + tswap64(syscall[3]) == HTIF_CONSOLE_CMD_PUTC) { + uint8_t ch; + cpu_physical_memory_read(tswap64(syscall[2]), &ch, 1); + qemu_chr_fe_write(&s->chr, &ch, 1); + resp = 0x100 | (uint8_t)payload; + } else { + qemu_log_mask(LOG_UNIMP, + "pk syscall proxy not supported\n"); + } } } else { qemu_log("HTIF device %d: unknown command\n", device); } - } else if (likely(device == 0x1)) { + } else if (likely(device == HTIF_DEV_CONSOLE)) { /* HTIF Console */ - if (cmd == 0x0) { + if (cmd == HTIF_CONSOLE_CMD_GETC) { /* this should be a queue, but not yet implemented as such */ - htifstate->pending_read = val_written; - htifstate->env->mtohost = 0; /* clear to indicate we read */ + s->pending_read = val_written; + s->tohost = 0; /* clear to indicate we read */ return; - } else if (cmd == 0x1) { - qemu_chr_fe_write(&htifstate->chr, (uint8_t *)&payload, 1); + } else if (cmd == HTIF_CONSOLE_CMD_PUTC) { + uint8_t ch = (uint8_t)payload; + qemu_chr_fe_write(&s->chr, &ch, 1); resp = 0x100 | (uint8_t)payload; } else { qemu_log("HTIF device %d: unknown command\n", device); @@ -157,36 +248,36 @@ static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written) " payload: %016" PRIx64, device, cmd, payload & 0xFF, payload); } /* - * - latest bbl does not set fromhost to 0 if there is a value in tohost - * - with this code enabled, qemu hangs waiting for fromhost to go to 0 - * - with this code disabled, qemu works with bbl priv v1.9.1 and v1.10 - * - HTIF needs protocol documentation and a more complete state machine - - while (!htifstate->fromhost_inprogress && - htifstate->env->mfromhost != 0x0) { - } - */ - htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); - htifstate->env->mtohost = 0; /* clear to indicate we read */ + * Latest bbl does not set fromhost to 0 if there is a value in tohost. + * With this code enabled, qemu hangs waiting for fromhost to go to 0. + * With this code disabled, qemu works with bbl priv v1.9.1 and v1.10. + * HTIF needs protocol documentation and a more complete state machine. + * + * while (!s->fromhost_inprogress && + * s->fromhost != 0x0) { + * } + */ + s->fromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); + s->tohost = 0; /* clear to indicate we read */ } -#define TOHOST_OFFSET1 (htifstate->tohost_offset) -#define TOHOST_OFFSET2 (htifstate->tohost_offset + 4) -#define FROMHOST_OFFSET1 (htifstate->fromhost_offset) -#define FROMHOST_OFFSET2 (htifstate->fromhost_offset + 4) +#define TOHOST_OFFSET1 (s->tohost_offset) +#define TOHOST_OFFSET2 (s->tohost_offset + 4) +#define FROMHOST_OFFSET1 (s->fromhost_offset) +#define FROMHOST_OFFSET2 (s->fromhost_offset + 4) /* CPU wants to read an HTIF register */ static uint64_t htif_mm_read(void *opaque, hwaddr addr, unsigned size) { - HTIFState *htifstate = opaque; + HTIFState *s = opaque; if (addr == TOHOST_OFFSET1) { - return htifstate->env->mtohost & 0xFFFFFFFF; + return s->tohost & 0xFFFFFFFF; } else if (addr == TOHOST_OFFSET2) { - return (htifstate->env->mtohost >> 32) & 0xFFFFFFFF; + return (s->tohost >> 32) & 0xFFFFFFFF; } else if (addr == FROMHOST_OFFSET1) { - return htifstate->env->mfromhost & 0xFFFFFFFF; + return s->fromhost & 0xFFFFFFFF; } else if (addr == FROMHOST_OFFSET2) { - return (htifstate->env->mfromhost >> 32) & 0xFFFFFFFF; + return (s->fromhost >> 32) & 0xFFFFFFFF; } else { qemu_log("Invalid htif read: address %016" PRIx64 "\n", (uint64_t)addr); @@ -196,27 +287,27 @@ static uint64_t htif_mm_read(void *opaque, hwaddr addr, unsigned size) /* CPU wrote to an HTIF register */ static void htif_mm_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) + uint64_t value, unsigned size) { - HTIFState *htifstate = opaque; + HTIFState *s = opaque; if (addr == TOHOST_OFFSET1) { - if (htifstate->env->mtohost == 0x0) { - htifstate->allow_tohost = 1; - htifstate->env->mtohost = value & 0xFFFFFFFF; + if (s->tohost == 0x0) { + s->allow_tohost = 1; + s->tohost = value & 0xFFFFFFFF; } else { - htifstate->allow_tohost = 0; + s->allow_tohost = 0; } } else if (addr == TOHOST_OFFSET2) { - if (htifstate->allow_tohost) { - htifstate->env->mtohost |= value << 32; - htif_handle_tohost_write(htifstate, htifstate->env->mtohost); + if (s->allow_tohost) { + s->tohost |= value << 32; + htif_handle_tohost_write(s, s->tohost); } } else if (addr == FROMHOST_OFFSET1) { - htifstate->fromhost_inprogress = 1; - htifstate->env->mfromhost = value & 0xFFFFFFFF; + s->fromhost_inprogress = 1; + s->fromhost = value & 0xFFFFFFFF; } else if (addr == FROMHOST_OFFSET2) { - htifstate->env->mfromhost |= value << 32; - htifstate->fromhost_inprogress = 0; + s->fromhost |= value << 32; + s->fromhost_inprogress = 0; } else { qemu_log("Invalid htif write: address %016" PRIx64 "\n", (uint64_t)addr); @@ -228,19 +319,19 @@ static const MemoryRegionOps htif_mm_ops = { .write = htif_mm_write, }; -bool htif_uses_elf_symbols(void) -{ - return (address_symbol_set == 3) ? true : false; -} - -HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem, - CPURISCVState *env, Chardev *chr, uint64_t nonelf_base) +HTIFState *htif_mm_init(MemoryRegion *address_space, Chardev *chr, + uint64_t nonelf_base, bool custom_base) { uint64_t base, size, tohost_offset, fromhost_offset; - if (!htif_uses_elf_symbols()) { + if (custom_base) { fromhost_addr = nonelf_base; tohost_addr = nonelf_base + 8; + } else { + if (!fromhost_addr || !tohost_addr) { + error_report("Invalid HTIF fromhost or tohost address"); + exit(1); + } } base = MIN(tohost_addr, fromhost_addr); @@ -249,10 +340,6 @@ HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem, fromhost_offset = fromhost_addr - base; HTIFState *s = g_new0(HTIFState, 1); - s->address_space = address_space; - s->main_mem = main_mem; - s->main_mem_ram_ptr = memory_region_get_ram_ptr(main_mem); - s->env = env; s->tohost_offset = tohost_offset; s->fromhost_offset = fromhost_offset; s->pending_read = 0; diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index b9e9b2d453..7719f438f6 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -292,7 +292,7 @@ static const VMStateDescription vmstate_sclplmconsole = { .name = "sclplmconsole", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(event.event_pending, SCLPConsoleLM), VMSTATE_UINT32(write_errors, SCLPConsoleLM), VMSTATE_UINT32(length, SCLPConsoleLM), diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index c36b572222..5d630b04bb 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -206,7 +206,7 @@ static const VMStateDescription vmstate_sclpconsole = { .name = "sclpconsole", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(event.event_pending, SCLPConsole), VMSTATE_UINT8_ARRAY(iov, SCLPConsole, SIZE_BUFFER_VT220), VMSTATE_UINT32(iov_sclp, SCLPConsole), diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index 7a7ed239cd..329b352b9a 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -27,7 +27,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "sysemu/sysemu.h" -#include "hw/acpi/aml-build.h" +#include "hw/acpi/acpi_aml_interface.h" #include "hw/char/serial.h" #include "hw/isa/isa.h" #include "hw/qdev-properties.h" @@ -83,9 +83,9 @@ static void serial_isa_realizefn(DeviceState *dev, Error **errp) isa_register_ioport(isadev, &s->io, isa->iobase); } -static void serial_isa_build_aml(ISADevice *isadev, Aml *scope) +static void serial_isa_build_aml(AcpiDevAmlIf *adev, Aml *scope) { - ISASerialState *isa = ISA_SERIAL(isadev); + ISASerialState *isa = ISA_SERIAL(adev); Aml *dev; Aml *crs; @@ -106,7 +106,7 @@ static const VMStateDescription vmstate_isa_serial = { .name = "serial", .version_id = 3, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(state, ISASerialState, 0, vmstate_serial, SerialState), VMSTATE_END_OF_LIST() } @@ -122,11 +122,11 @@ static Property serial_isa_properties[] = { static void serial_isa_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - ISADeviceClass *isa = ISA_DEVICE_CLASS(klass); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); dc->realize = serial_isa_realizefn; dc->vmsd = &vmstate_isa_serial; - isa->build_aml = serial_isa_build_aml; + adevc->build_dev_aml = serial_isa_build_aml; device_class_set_props(dc, serial_isa_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } @@ -146,6 +146,10 @@ static const TypeInfo serial_isa_info = { .instance_size = sizeof(ISASerialState), .instance_init = serial_isa_initfn, .class_init = serial_isa_class_initfn, + .interfaces = (InterfaceInfo[]) { + { TYPE_ACPI_DEV_AML_IF }, + { }, + }, }; static void serial_register_types(void) @@ -180,3 +184,17 @@ void serial_hds_isa_init(ISABus *bus, int from, int to) } } } + +void isa_serial_set_iobase(ISADevice *serial, hwaddr iobase) +{ + ISASerialState *s = ISA_SERIAL(serial); + + serial->ioport_id = iobase; + s->iobase = iobase; + memory_region_set_address(&s->state.io, s->iobase); +} + +void isa_serial_set_enabled(ISADevice *serial, bool enabled) +{ + memory_region_set_enabled(&ISA_SERIAL(serial)->state.io, enabled); +} diff --git a/hw/char/serial-pci-multi.c b/hw/char/serial-pci-multi.c index 3a9f96c2d1..28b275709a 100644 --- a/hw/char/serial-pci-multi.c +++ b/hw/char/serial-pci-multi.c @@ -25,13 +25,13 @@ * THE SOFTWARE. */ -/* see docs/specs/pci-serial.txt */ +/* see docs/specs/pci-serial.rst */ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/char/serial.h" #include "hw/irq.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" @@ -123,7 +123,7 @@ static const VMStateDescription vmstate_pci_multi_serial = { .name = "pci-serial-multi", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, PCIMultiSerialState), VMSTATE_STRUCT_ARRAY(state, PCIMultiSerialState, PCI_SERIAL_MAX_PORTS, 0, vmstate_serial, SerialState), diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c index 93d6f99244..f8a1a94d0c 100644 --- a/hw/char/serial-pci.c +++ b/hw/char/serial-pci.c @@ -23,14 +23,14 @@ * THE SOFTWARE. */ -/* see docs/specs/pci-serial.txt */ +/* see docs/specs/pci-serial.rst */ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" #include "hw/char/serial.h" #include "hw/irq.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qom/object.h" @@ -74,7 +74,7 @@ static const VMStateDescription vmstate_pci_serial = { .name = "pci-serial", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, PCISerialState), VMSTATE_STRUCT(state, PCISerialState, 0, vmstate_serial, SerialState), VMSTATE_END_OF_LIST() diff --git a/hw/char/serial.c b/hw/char/serial.c index 7061aacbce..d8b2db5082 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -38,55 +38,55 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" -#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ +#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ -#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */ -#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */ -#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ -#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ +#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */ +#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */ +#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ +#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ -#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ -#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ +#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ +#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ -#define UART_IIR_MSI 0x00 /* Modem status interrupt */ -#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ -#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ -#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ +#define UART_IIR_MSI 0x00 /* Modem status interrupt */ +#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ +#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ +#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ #define UART_IIR_CTI 0x0C /* Character Timeout Indication */ -#define UART_IIR_FENF 0x80 /* Fifo enabled, but not functionning */ +#define UART_IIR_FENF 0x80 /* Fifo enabled, but not functioning */ #define UART_IIR_FE 0xC0 /* Fifo enabled */ /* * These are the definitions for the Modem Control Register */ -#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ -#define UART_MCR_OUT2 0x08 /* Out2 complement */ -#define UART_MCR_OUT1 0x04 /* Out1 complement */ -#define UART_MCR_RTS 0x02 /* RTS complement */ -#define UART_MCR_DTR 0x01 /* DTR complement */ +#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ +#define UART_MCR_OUT2 0x08 /* Out2 complement */ +#define UART_MCR_OUT1 0x04 /* Out1 complement */ +#define UART_MCR_RTS 0x02 /* RTS complement */ +#define UART_MCR_DTR 0x01 /* DTR complement */ /* * These are the definitions for the Modem Status Register */ -#define UART_MSR_DCD 0x80 /* Data Carrier Detect */ -#define UART_MSR_RI 0x40 /* Ring Indicator */ -#define UART_MSR_DSR 0x20 /* Data Set Ready */ -#define UART_MSR_CTS 0x10 /* Clear to Send */ -#define UART_MSR_DDCD 0x08 /* Delta DCD */ -#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */ -#define UART_MSR_DDSR 0x02 /* Delta DSR */ -#define UART_MSR_DCTS 0x01 /* Delta CTS */ -#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */ +#define UART_MSR_DCD 0x80 /* Data Carrier Detect */ +#define UART_MSR_RI 0x40 /* Ring Indicator */ +#define UART_MSR_DSR 0x20 /* Data Set Ready */ +#define UART_MSR_CTS 0x10 /* Clear to Send */ +#define UART_MSR_DDCD 0x08 /* Delta DCD */ +#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */ +#define UART_MSR_DDSR 0x02 /* Delta DSR */ +#define UART_MSR_DCTS 0x01 /* Delta CTS */ +#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */ -#define UART_LSR_TEMT 0x40 /* Transmitter empty */ -#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ -#define UART_LSR_BI 0x10 /* Break interrupt indicator */ -#define UART_LSR_FE 0x08 /* Frame error indicator */ -#define UART_LSR_PE 0x04 /* Parity error indicator */ -#define UART_LSR_OE 0x02 /* Overrun error indicator */ -#define UART_LSR_DR 0x01 /* Receiver data ready */ -#define UART_LSR_INT_ANY 0x1E /* Any of the lsr-interrupt-triggering status bits */ +#define UART_LSR_TEMT 0x40 /* Transmitter empty */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x10 /* Break interrupt indicator */ +#define UART_LSR_FE 0x08 /* Frame error indicator */ +#define UART_LSR_PE 0x04 /* Parity error indicator */ +#define UART_LSR_OE 0x02 /* Overrun error indicator */ +#define UART_LSR_DR 0x01 /* Receiver data ready */ +#define UART_LSR_INT_ANY 0x1E /* Any of the lsr-interrupt-triggering status bits */ /* Interrupt trigger levels. The byte-counts are for 16550A - in newer UARTs the byte-count for each ITL is higher. */ @@ -226,7 +226,7 @@ static gboolean serial_watch_cb(void *do_not_use, GIOCondition cond, SerialState *s = opaque; s->watch_tag = 0; serial_xmit(s); - return FALSE; + return G_SOURCE_REMOVE; } static void serial_xmit(SerialState *s) @@ -707,7 +707,7 @@ static const VMStateDescription vmstate_serial_thr_ipending = { .version_id = 1, .minimum_version_id = 1, .needed = serial_thr_ipending_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(thr_ipending, SerialState), VMSTATE_END_OF_LIST() } @@ -724,7 +724,7 @@ static const VMStateDescription vmstate_serial_tsr = { .version_id = 1, .minimum_version_id = 1, .needed = serial_tsr_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(tsr_retry, SerialState), VMSTATE_UINT8(thr, SerialState), VMSTATE_UINT8(tsr, SerialState), @@ -744,7 +744,7 @@ static const VMStateDescription vmstate_serial_recv_fifo = { .version_id = 1, .minimum_version_id = 1, .needed = serial_recv_fifo_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(recv_fifo, SerialState, 1, vmstate_fifo8, Fifo8), VMSTATE_END_OF_LIST() } @@ -761,7 +761,7 @@ static const VMStateDescription vmstate_serial_xmit_fifo = { .version_id = 1, .minimum_version_id = 1, .needed = serial_xmit_fifo_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(xmit_fifo, SerialState, 1, vmstate_fifo8, Fifo8), VMSTATE_END_OF_LIST() } @@ -778,7 +778,7 @@ static const VMStateDescription vmstate_serial_fifo_timeout_timer = { .version_id = 1, .minimum_version_id = 1, .needed = serial_fifo_timeout_timer_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(fifo_timeout_timer, SerialState), VMSTATE_END_OF_LIST() } @@ -795,7 +795,7 @@ static const VMStateDescription vmstate_serial_timeout_ipending = { .version_id = 1, .minimum_version_id = 1, .needed = serial_timeout_ipending_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(timeout_ipending, SerialState), VMSTATE_END_OF_LIST() } @@ -812,7 +812,7 @@ static const VMStateDescription vmstate_serial_poll = { .version_id = 1, .needed = serial_poll_needed, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(poll_msl, SerialState), VMSTATE_TIMER_PTR(modem_status_poll, SerialState), VMSTATE_END_OF_LIST() @@ -826,7 +826,7 @@ const VMStateDescription vmstate_serial = { .pre_save = serial_pre_save, .pre_load = serial_pre_load, .post_load = serial_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16_V(divider, SerialState, 2), VMSTATE_UINT8(rbr, SerialState), VMSTATE_UINT8(ier, SerialState), @@ -839,7 +839,7 @@ const VMStateDescription vmstate_serial = { VMSTATE_UINT8_V(fcr_vmstate, SerialState, 3), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_serial_thr_ipending, &vmstate_serial_tsr, &vmstate_serial_recv_fifo, @@ -961,6 +961,9 @@ void serial_set_frequency(SerialState *s, uint32_t frequency) const MemoryRegionOps serial_io_ops = { .read = serial_ioport_read, .write = serial_ioport_write, + .valid = { + .unaligned = 1, + }, .impl = { .min_access_size = 1, .max_access_size = 1, @@ -1053,7 +1056,7 @@ static const VMStateDescription vmstate_serial_mm = { .name = "serial", .version_id = 3, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(serial, SerialMM, 0, vmstate_serial, SerialState), VMSTATE_END_OF_LIST() } diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index 1c75f792b3..e8716c4252 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -224,7 +224,7 @@ static const VMStateDescription vmstate_sifive_uart = { .name = TYPE_SIFIVE_UART, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(rx_fifo, SiFiveUARTState, SIFIVE_UART_RX_FIFO_SIZE), VMSTATE_UINT8(rx_fifo_len, SiFiveUARTState), @@ -274,7 +274,6 @@ SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base, { DeviceState *dev; SysBusDevice *s; - SiFiveUARTState *r; dev = qdev_new("riscv.sifive.uart"); s = SYS_BUS_DEVICE(dev); @@ -284,6 +283,5 @@ SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base, sysbus_mmio_get_region(s, 0)); sysbus_connect_irq(s, 0, irq); - r = SIFIVE_UART(dev); - return r; + return SIFIVE_UART(dev); } diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index 91eae1a598..3e23d9cbab 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -173,7 +173,7 @@ static const VMStateDescription vmstate_spapr_vty = { .name = "spapr_vty", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_SPAPR_VIO(sdev, SpaprVioVty), VMSTATE_UINT32(in, SpaprVioVty), diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index fde67f4f03..8753afeb2b 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -53,6 +53,17 @@ static int stm32f2xx_usart_can_receive(void *opaque) return 0; } +static void stm32f2xx_update_irq(STM32F2XXUsartState *s) +{ + uint32_t mask = s->usart_sr & s->usart_cr1; + + if (mask & (USART_SR_TXE | USART_SR_TC | USART_SR_RXNE)) { + qemu_set_irq(s->irq, 1); + } else { + qemu_set_irq(s->irq, 0); + } +} + static void stm32f2xx_usart_receive(void *opaque, const uint8_t *buf, int size) { STM32F2XXUsartState *s = opaque; @@ -66,9 +77,7 @@ static void stm32f2xx_usart_receive(void *opaque, const uint8_t *buf, int size) s->usart_dr = *buf; s->usart_sr |= USART_SR_RXNE; - if (s->usart_cr1 & USART_CR1_RXNEIE) { - qemu_set_irq(s->irq, 1); - } + stm32f2xx_update_irq(s); DB_PRINT("Receiving: %c\n", s->usart_dr); } @@ -85,7 +94,7 @@ static void stm32f2xx_usart_reset(DeviceState *dev) s->usart_cr3 = 0x00000000; s->usart_gtpr = 0x00000000; - qemu_set_irq(s->irq, 0); + stm32f2xx_update_irq(s); } static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr, @@ -106,7 +115,7 @@ static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr, retvalue = s->usart_dr & 0x3FF; s->usart_sr &= ~USART_SR_RXNE; qemu_chr_fe_accept_input(&s->chr); - qemu_set_irq(s->irq, 0); + stm32f2xx_update_irq(s); return retvalue; case USART_BRR: return s->usart_brr; @@ -145,9 +154,7 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr, } else { s->usart_sr &= value; } - if (!(s->usart_sr & USART_SR_RXNE)) { - qemu_set_irq(s->irq, 0); - } + stm32f2xx_update_irq(s); return; case USART_DR: if (value < 0xF000) { @@ -161,6 +168,7 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr, clear TC by writing 0 to the SR register, so set it again on each write. */ s->usart_sr |= USART_SR_TC; + stm32f2xx_update_irq(s); } return; case USART_BRR: @@ -168,10 +176,7 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr, return; case USART_CR1: s->usart_cr1 = value; - if (s->usart_cr1 & USART_CR1_RXNEIE && - s->usart_sr & USART_SR_RXNE) { - qemu_set_irq(s->irq, 1); - } + stm32f2xx_update_irq(s); return; case USART_CR2: s->usart_cr2 = value; diff --git a/hw/char/trace-events b/hw/char/trace-events index 2ecb36232e..7a398c82a5 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -54,9 +54,9 @@ escc_sunmouse_event(int dx, int dy, int buttons_state) "dx=%d dy=%d buttons=0x%0 # pl011.c pl011_irq_state(int level) "irq state %d" -pl011_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" +pl011_read(uint32_t addr, uint32_t value, const char *regname) "addr 0x%03x value 0x%08x reg %s" pl011_read_fifo(int read_count) "FIFO read, read_count now %d" -pl011_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" +pl011_write(uint32_t addr, uint32_t value, const char *regname) "addr 0x%03x value 0x%08x reg %s" pl011_can_receive(uint32_t lcr, int read_count, int r) "LCR 0x%08x read_count %d returning %d" pl011_put_fifo(uint32_t c, int read_count) "new char 0x%x read_count now %d" pl011_put_fifo_full(void) "FIFO now full, RXFF set" @@ -105,3 +105,11 @@ cadence_uart_baudrate(unsigned baudrate) "baudrate %u" # sh_serial.c sh_serial_read(char *id, unsigned size, uint64_t offs, uint64_t val) " %s size %d offs 0x%02" PRIx64 " -> 0x%02" PRIx64 sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size %d offs 0x%02" PRIx64 " <- 0x%02" PRIx64 + +# xen_console.c +xen_console_connect(unsigned int idx, unsigned int ring_ref, unsigned int port, unsigned int limit) "idx %u ring_ref %u port %u limit %u" +xen_console_disconnect(unsigned int idx) "idx %u" +xen_console_unrealize(unsigned int idx) "idx %u" +xen_console_realize(unsigned int idx, const char *chrdev) "idx %u chrdev %s" +xen_console_device_create(unsigned int idx) "idx %u" +xen_console_device_destroy(unsigned int idx) "idx %u" diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index dd5a02e339..dbe0b28e60 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -45,7 +45,7 @@ static gboolean chr_write_unblocked(void *do_not_use, GIOCondition cond, vcon->watch = 0; virtio_serial_throttle_port(VIRTIO_SERIAL_PORT(vcon), false); - return FALSE; + return G_SOURCE_REMOVE; } /* Callback function that's called when the guest sends us data */ diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index 7d4601cb5d..2094d213cd 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -985,7 +985,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) return; } - port->bh = qemu_bh_new(flush_queued_data_bh, port); + port->bh = virtio_bh_new_guarded(dev, flush_queued_data_bh, port); port->elem = NULL; } @@ -1147,7 +1147,7 @@ static const VMStateDescription vmstate_virtio_console = { .name = "virtio-console", .minimum_version_id = 3, .version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index 63153dfde4..683c92aca1 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -20,15 +20,22 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include #include #include "qapi/error.h" #include "sysemu/sysemu.h" #include "chardev/char-fe.h" -#include "hw/xen/xen-legacy-backend.h" - +#include "hw/xen/xen-backend.h" +#include "hw/xen/xen-bus-helper.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "hw/xen/interface/io/console.h" +#include "hw/xen/interface/io/xs_wire.h" +#include "hw/xen/interface/grant_table.h" +#include "hw/i386/kvm/xen_primary_console.h" +#include "trace.h" struct buffer { uint8_t *data; @@ -39,16 +46,22 @@ struct buffer { }; struct XenConsole { - struct XenLegacyDevice xendev; /* must be first */ + struct XenDevice xendev; /* must be first */ + XenEventChannel *event_channel; + int dev; struct buffer buffer; - char console[XEN_BUFSIZE]; - int ring_ref; + char *fe_path; + unsigned int ring_ref; void *sring; CharBackend chr; int backlog; }; +typedef struct XenConsole XenConsole; -static void buffer_append(struct XenConsole *con) +#define TYPE_XEN_CONSOLE_DEVICE "xen-console" +OBJECT_DECLARE_SIMPLE_TYPE(XenConsole, XEN_CONSOLE_DEVICE) + +static bool buffer_append(XenConsole *con) { struct buffer *buffer = &con->buffer; XENCONS_RING_IDX cons, prod, size; @@ -60,7 +73,7 @@ static void buffer_append(struct XenConsole *con) size = prod - cons; if ((size == 0) || (size > sizeof(intf->out))) - return; + return false; if ((buffer->capacity - buffer->size) < size) { buffer->capacity += (size + 1024); @@ -73,7 +86,7 @@ static void buffer_append(struct XenConsole *con) xen_mb(); intf->out_cons = cons; - xen_pv_send_notify(&con->xendev); + xen_device_notify_event_channel(XEN_DEVICE(con), con->event_channel, NULL); if (buffer->max_capacity && buffer->size > buffer->max_capacity) { @@ -89,6 +102,7 @@ static void buffer_append(struct XenConsole *con) if (buffer->consumed > buffer->max_capacity - over) buffer->consumed = buffer->max_capacity - over; } + return true; } static void buffer_advance(struct buffer *buffer, size_t len) @@ -100,7 +114,7 @@ static void buffer_advance(struct buffer *buffer, size_t len) } } -static int ring_free_bytes(struct XenConsole *con) +static int ring_free_bytes(XenConsole *con) { struct xencons_interface *intf = con->sring; XENCONS_RING_IDX cons, prod, space; @@ -118,13 +132,13 @@ static int ring_free_bytes(struct XenConsole *con) static int xencons_can_receive(void *opaque) { - struct XenConsole *con = opaque; + XenConsole *con = opaque; return ring_free_bytes(con); } static void xencons_receive(void *opaque, const uint8_t *buf, int len) { - struct XenConsole *con = opaque; + XenConsole *con = opaque; struct xencons_interface *intf = con->sring; XENCONS_RING_IDX prod; int i, max; @@ -141,10 +155,10 @@ static void xencons_receive(void *opaque, const uint8_t *buf, int len) } xen_wmb(); intf->in_prod = prod; - xen_pv_send_notify(&con->xendev); + xen_device_notify_event_channel(XEN_DEVICE(con), con->event_channel, NULL); } -static void xencons_send(struct XenConsole *con) +static bool xencons_send(XenConsole *con) { ssize_t len, size; @@ -159,140 +173,473 @@ static void xencons_send(struct XenConsole *con) if (len < 1) { if (!con->backlog) { con->backlog = 1; - xen_pv_printf(&con->xendev, 1, - "backlog piling up, nobody listening?\n"); } } else { buffer_advance(&con->buffer, len); if (con->backlog && len == size) { con->backlog = 0; - xen_pv_printf(&con->xendev, 1, "backlog is gone\n"); } } + return len > 0; } /* -------------------------------------------------------------------- */ -static int con_init(struct XenLegacyDevice *xendev) +static bool con_event(void *_xendev) { - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - char *type, *dom, label[32]; - int ret = 0; - const char *output; + XenConsole *con = XEN_CONSOLE_DEVICE(_xendev); + bool done_something; - /* setup */ - dom = xs_get_domain_path(xenstore, con->xendev.dom); - if (!xendev->dev) { - snprintf(con->console, sizeof(con->console), "%s/console", dom); - } else { - snprintf(con->console, sizeof(con->console), "%s/device/console/%d", dom, xendev->dev); - } - free(dom); - - type = xenstore_read_str(con->console, "type"); - if (!type || strcmp(type, "ioemu") != 0) { - xen_pv_printf(xendev, 1, "not for me (type=%s)\n", type); - ret = -1; - goto out; + if (xen_device_backend_get_state(&con->xendev) != XenbusStateConnected) { + return false; } - output = xenstore_read_str(con->console, "output"); + done_something = buffer_append(con); - /* no Xen override, use qemu output device */ - if (output == NULL) { - if (con->xendev.dev) { - qemu_chr_fe_init(&con->chr, serial_hd(con->xendev.dev), - &error_abort); - } - } else { - snprintf(label, sizeof(label), "xencons%d", con->xendev.dev); - qemu_chr_fe_init(&con->chr, - /* - * FIXME: sure we want to support implicit - * muxed monitors here? - */ - qemu_chr_new_mux_mon(label, output, NULL), - &error_abort); + if (con->buffer.size - con->buffer.consumed) { + done_something |= xencons_send(con); } - - xenstore_store_pv_console_info(con->xendev.dev, - qemu_chr_fe_get_driver(&con->chr)); - -out: - g_free(type); - return ret; + return done_something; } -static int con_initialise(struct XenLegacyDevice *xendev) -{ - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - int limit; +/* -------------------------------------------------------------------- */ - if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1) - return -1; - if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1) - return -1; - if (xenstore_read_int(con->console, "limit", &limit) == 0) +static bool xen_console_connect(XenDevice *xendev, Error **errp) +{ + ERRP_GUARD(); + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); + unsigned int port, limit; + + if (xen_device_frontend_scanf(xendev, "ring-ref", "%u", + &con->ring_ref) != 1) { + error_setg(errp, "failed to read ring-ref"); + return false; + } + + if (xen_device_frontend_scanf(xendev, "port", "%u", &port) != 1) { + error_setg(errp, "failed to read remote port"); + return false; + } + + if (xen_device_frontend_scanf(xendev, "limit", "%u", &limit) == 1) { con->buffer.max_capacity = limit; - - if (!xendev->dev) { - xen_pfn_t mfn = con->ring_ref; - con->sring = xenforeignmemory_map(xen_fmem, con->xendev.dom, - PROT_READ | PROT_WRITE, - 1, &mfn, NULL); - } else { - con->sring = xen_be_map_grant_ref(xendev, con->ring_ref, - PROT_READ | PROT_WRITE); } - if (!con->sring) - return -1; - xen_be_bind_evtchn(&con->xendev); + con->event_channel = xen_device_bind_event_channel(xendev, port, + con_event, + con, + errp); + if (!con->event_channel) { + return false; + } + + switch (con->dev) { + case 0: + /* + * The primary console is special. For real Xen the ring-ref is + * actually a GFN which needs to be mapped as foreignmem. + */ + if (xen_mode != XEN_EMULATE) { + xen_pfn_t mfn = (xen_pfn_t)con->ring_ref; + con->sring = qemu_xen_foreignmem_map(xendev->frontend_id, NULL, + PROT_READ | PROT_WRITE, + 1, &mfn, NULL); + if (!con->sring) { + error_setg(errp, "failed to map console page"); + return false; + } + break; + } + + /* + * For Xen emulation, we still follow the convention of ring-ref + * holding the GFN, but we map the fixed GNTTAB_RESERVED_CONSOLE + * grant ref because there is no implementation of foreignmem + * operations for emulated mode. The emulation code which handles + * the guest-side page and event channel also needs to be informed + * of the backend event channel port, in order to reconnect to it + * after a soft reset. + */ + xen_primary_console_set_be_port( + xen_event_channel_get_local_port(con->event_channel)); + con->ring_ref = GNTTAB_RESERVED_CONSOLE; + /* fallthrough */ + default: + con->sring = xen_device_map_grant_refs(xendev, + &con->ring_ref, 1, + PROT_READ | PROT_WRITE, + errp); + if (!con->sring) { + error_prepend(errp, "failed to map console grant ref: "); + return false; + } + break; + } + + trace_xen_console_connect(con->dev, con->ring_ref, port, + con->buffer.max_capacity); + qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive, - xencons_receive, NULL, NULL, con, NULL, true); - - xen_pv_printf(xendev, 1, - "ring mfn %d, remote port %d, local port %d, limit %zd\n", - con->ring_ref, - con->xendev.remote_port, - con->xendev.local_port, - con->buffer.max_capacity); - return 0; + xencons_receive, NULL, NULL, con, NULL, + true); + return true; } -static void con_disconnect(struct XenLegacyDevice *xendev) +static void xen_console_disconnect(XenDevice *xendev, Error **errp) { - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); - qemu_chr_fe_deinit(&con->chr, false); - xen_pv_unbind_evtchn(&con->xendev); + trace_xen_console_disconnect(con->dev); + + qemu_chr_fe_set_handlers(&con->chr, NULL, NULL, NULL, NULL, + con, NULL, true); + + if (con->event_channel) { + xen_device_unbind_event_channel(xendev, con->event_channel, + errp); + con->event_channel = NULL; + + if (xen_mode == XEN_EMULATE && !con->dev) { + xen_primary_console_set_be_port(0); + } + } if (con->sring) { - if (!xendev->dev) { - xenforeignmemory_unmap(xen_fmem, con->sring, 1); + if (!con->dev && xen_mode != XEN_EMULATE) { + qemu_xen_foreignmem_unmap(con->sring, 1); } else { - xen_be_unmap_grant_ref(xendev, con->sring); + xen_device_unmap_grant_refs(xendev, con->sring, + &con->ring_ref, 1, errp); } con->sring = NULL; } } -static void con_event(struct XenLegacyDevice *xendev) +static void xen_console_frontend_changed(XenDevice *xendev, + enum xenbus_state frontend_state, + Error **errp) { - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + ERRP_GUARD(); + enum xenbus_state backend_state = xen_device_backend_get_state(xendev); - buffer_append(con); - if (con->buffer.size - con->buffer.consumed) - xencons_send(con); + switch (frontend_state) { + case XenbusStateInitialised: + case XenbusStateConnected: + if (backend_state == XenbusStateConnected) { + break; + } + + xen_console_disconnect(xendev, errp); + if (*errp) { + break; + } + + if (!xen_console_connect(xendev, errp)) { + xen_device_backend_set_state(xendev, XenbusStateClosing); + break; + } + + xen_device_backend_set_state(xendev, XenbusStateConnected); + break; + + case XenbusStateClosing: + xen_device_backend_set_state(xendev, XenbusStateClosing); + break; + + case XenbusStateClosed: + case XenbusStateUnknown: + xen_console_disconnect(xendev, errp); + if (*errp) { + break; + } + + xen_device_backend_set_state(xendev, XenbusStateClosed); + break; + + default: + break; + } } -/* -------------------------------------------------------------------- */ +static char *xen_console_get_name(XenDevice *xendev, Error **errp) +{ + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); -struct XenDevOps xen_console_ops = { - .size = sizeof(struct XenConsole), - .flags = DEVOPS_FLAG_IGNORE_STATE|DEVOPS_FLAG_NEED_GNTDEV, - .init = con_init, - .initialise = con_initialise, - .event = con_event, - .disconnect = con_disconnect, + if (con->dev == -1) { + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + char fe_path[XENSTORE_ABS_PATH_MAX + 1]; + int idx = (xen_mode == XEN_EMULATE) ? 0 : 1; + char *value; + + /* Theoretically we could go up to INT_MAX here but that's overkill */ + while (idx < 100) { + if (!idx) { + snprintf(fe_path, sizeof(fe_path), + "/local/domain/%u/console", xendev->frontend_id); + } else { + snprintf(fe_path, sizeof(fe_path), + "/local/domain/%u/device/console/%u", + xendev->frontend_id, idx); + } + value = qemu_xen_xs_read(xenbus->xsh, XBT_NULL, fe_path, NULL); + if (!value) { + if (errno == ENOENT) { + con->dev = idx; + goto found; + } + error_setg(errp, "cannot read %s: %s", fe_path, + strerror(errno)); + return NULL; + } + free(value); + idx++; + } + error_setg(errp, "cannot find device index for console device"); + return NULL; + } + found: + return g_strdup_printf("%u", con->dev); +} + +static void xen_console_unrealize(XenDevice *xendev) +{ + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); + + trace_xen_console_unrealize(con->dev); + + /* Disconnect from the frontend in case this has not already happened */ + xen_console_disconnect(xendev, NULL); + + qemu_chr_fe_deinit(&con->chr, false); +} + +static void xen_console_realize(XenDevice *xendev, Error **errp) +{ + ERRP_GUARD(); + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); + Chardev *cs = qemu_chr_fe_get_driver(&con->chr); + unsigned int u; + + if (!cs) { + error_setg(errp, "no backing character device"); + return; + } + + if (con->dev == -1) { + error_setg(errp, "no device index provided"); + return; + } + + /* + * The Xen primary console is special. The ring-ref is actually a GFN to + * be mapped directly as foreignmem (not a grant ref), and the guest port + * was allocated *for* the guest by the toolstack. The guest gets these + * through HVMOP_get_param and can use the console long before it's got + * XenStore up and running. We cannot create those for a true Xen guest, + * but we can for Xen emulation. + */ + if (!con->dev) { + if (xen_mode == XEN_EMULATE) { + xen_primary_console_create(); + } else if (xen_device_frontend_scanf(xendev, "ring-ref", "%u", &u) + != 1 || + xen_device_frontend_scanf(xendev, "port", "%u", &u) != 1) { + error_setg(errp, "cannot create primary Xen console"); + return; + } + } + + trace_xen_console_realize(con->dev, object_get_typename(OBJECT(cs))); + + if (CHARDEV_IS_PTY(cs)) { + /* Strip the leading 'pty:' */ + xen_device_frontend_printf(xendev, "tty", "%s", cs->filename + 4); + } + + /* No normal PV driver initialization for the primary console under Xen */ + if (!con->dev && xen_mode != XEN_EMULATE) { + xen_console_connect(xendev, errp); + } +} + +static char *console_frontend_path(struct qemu_xs_handle *xenstore, + unsigned int dom_id, unsigned int dev) +{ + if (!dev) { + return g_strdup_printf("/local/domain/%u/console", dom_id); + } else { + return g_strdup_printf("/local/domain/%u/device/console/%u", dom_id, + dev); + } +} + +static char *xen_console_get_frontend_path(XenDevice *xendev, Error **errp) +{ + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + char *ret = console_frontend_path(xenbus->xsh, xendev->frontend_id, + con->dev); + + if (!ret) { + error_setg(errp, "failed to create frontend path"); + } + return ret; +} + + +static Property xen_console_properties[] = { + DEFINE_PROP_CHR("chardev", XenConsole, chr), + DEFINE_PROP_INT32("idx", XenConsole, dev, -1), + DEFINE_PROP_END_OF_LIST(), }; + +static void xen_console_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dev_class = DEVICE_CLASS(class); + XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class); + + xendev_class->backend = "console"; + xendev_class->device = "console"; + xendev_class->get_name = xen_console_get_name; + xendev_class->realize = xen_console_realize; + xendev_class->frontend_changed = xen_console_frontend_changed; + xendev_class->unrealize = xen_console_unrealize; + xendev_class->get_frontend_path = xen_console_get_frontend_path; + + device_class_set_props(dev_class, xen_console_properties); +} + +static const TypeInfo xen_console_type_info = { + .name = TYPE_XEN_CONSOLE_DEVICE, + .parent = TYPE_XEN_DEVICE, + .instance_size = sizeof(XenConsole), + .class_init = xen_console_class_init, +}; + +static void xen_console_register_types(void) +{ + type_register_static(&xen_console_type_info); +} + +type_init(xen_console_register_types) + +/* Called to instantiate a XenConsole when the backend is detected. */ +static void xen_console_device_create(XenBackendInstance *backend, + QDict *opts, Error **errp) +{ + ERRP_GUARD(); + XenBus *xenbus = xen_backend_get_bus(backend); + const char *name = xen_backend_get_name(backend); + unsigned long number; + char *fe = NULL, *type = NULL, *output = NULL; + char label[32]; + XenDevice *xendev = NULL; + XenConsole *con; + Chardev *cd = NULL; + struct qemu_xs_handle *xsh = xenbus->xsh; + + if (qemu_strtoul(name, NULL, 10, &number) || number > INT_MAX) { + error_setg(errp, "failed to parse name '%s'", name); + goto fail; + } + + trace_xen_console_device_create(number); + + fe = console_frontend_path(xsh, xen_domid, number); + if (fe == NULL) { + error_setg(errp, "failed to generate frontend path"); + goto fail; + } + + if (xs_node_scanf(xsh, XBT_NULL, fe, "type", errp, "%ms", &type) != 1) { + error_prepend(errp, "failed to read console device type: "); + goto fail; + } + + if (strcmp(type, "ioemu")) { + error_setg(errp, "declining to handle console type '%s'", + type); + goto fail; + } + + xendev = XEN_DEVICE(qdev_new(TYPE_XEN_CONSOLE_DEVICE)); + con = XEN_CONSOLE_DEVICE(xendev); + + con->dev = number; + + snprintf(label, sizeof(label), "xencons%ld", number); + + if (xs_node_scanf(xsh, XBT_NULL, fe, "output", NULL, "%ms", &output) == 1) { + /* + * FIXME: sure we want to support implicit + * muxed monitors here? + */ + cd = qemu_chr_new_mux_mon(label, output, NULL); + if (!cd) { + error_setg(errp, "console: No valid chardev found at '%s': ", + output); + goto fail; + } + } else if (number) { + cd = serial_hd(number); + if (!cd) { + error_prepend(errp, "console: No serial device #%ld found: ", + number); + goto fail; + } + } else { + /* No 'output' node on primary console: use null. */ + cd = qemu_chr_new(label, "null", NULL); + if (!cd) { + error_setg(errp, "console: failed to create null device"); + goto fail; + } + } + + if (!qemu_chr_fe_init(&con->chr, cd, errp)) { + error_prepend(errp, "console: failed to initialize backing chardev: "); + goto fail; + } + + if (qdev_realize_and_unref(DEVICE(xendev), BUS(xenbus), errp)) { + xen_backend_set_device(backend, xendev); + goto done; + } + + error_prepend(errp, "realization of console device %lu failed: ", + number); + + fail: + if (xendev) { + object_unparent(OBJECT(xendev)); + } + done: + g_free(fe); + free(type); + free(output); +} + +static void xen_console_device_destroy(XenBackendInstance *backend, + Error **errp) +{ + ERRP_GUARD(); + XenDevice *xendev = xen_backend_get_device(backend); + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); + + trace_xen_console_device_destroy(con->dev); + + object_unparent(OBJECT(xendev)); +} + +static const XenBackendInfo xen_console_backend_info = { + .type = "console", + .create = xen_console_device_create, + .destroy = xen_console_device_destroy, +}; + +static void xen_console_register_backend(void) +{ + xen_backend_register(&xen_console_backend_info); +} + +xen_backend_init(xen_console_register_backend); diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c index 99b9a6f851..180bb97202 100644 --- a/hw/char/xilinx_uartlite.c +++ b/hw/char/xilinx_uartlite.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "hw/char/xilinx_uartlite.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" @@ -53,9 +54,6 @@ #define CONTROL_RST_RX 0x02 #define CONTROL_IE 0x10 -#define TYPE_XILINX_UARTLITE "xlnx.xps-uartlite" -OBJECT_DECLARE_SIMPLE_TYPE(XilinxUARTLite, XILINX_UARTLITE) - struct XilinxUARTLite { SysBusDevice parent_obj; diff --git a/hw/core/bus.c b/hw/core/bus.c index c7831b5293..b9d89495cd 100644 --- a/hw/core/bus.c +++ b/hw/core/bus.c @@ -232,57 +232,6 @@ 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); @@ -293,22 +242,6 @@ static void bus_class_init(ObjectClass *class, void *data) 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) diff --git a/hw/core/clock-vmstate.c b/hw/core/clock-vmstate.c index 7eccb6d4ea..e831fc596f 100644 --- a/hw/core/clock-vmstate.c +++ b/hw/core/clock-vmstate.c @@ -41,7 +41,7 @@ const VMStateDescription vmstate_muldiv = { .version_id = 1, .minimum_version_id = 1, .needed = muldiv_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(multiplier, Clock), VMSTATE_UINT32(divider, Clock), VMSTATE_END_OF_LIST() @@ -53,11 +53,11 @@ const VMStateDescription vmstate_clock = { .version_id = 0, .minimum_version_id = 0, .pre_load = clock_pre_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(period, Clock), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_muldiv, NULL }, diff --git a/hw/core/clock.c b/hw/core/clock.c index 916875e07a..a19c7db7df 100644 --- a/hw/core/clock.c +++ b/hw/core/clock.c @@ -68,7 +68,7 @@ static uint64_t clock_get_child_period(Clock *clk) { /* * Return the period to be used for child clocks, which is the parent - * clock period adjusted for for multiplier and divider effects. + * clock period adjusted for multiplier and divider effects. */ return muldiv64(clk->period, clk->multiplier, clk->divider); } @@ -143,14 +143,20 @@ char *clock_display_freq(Clock *clk) return freq_to_str(clock_get_hz(clk)); } -void clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider) +bool clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider) { assert(divider != 0); + if (clk->multiplier == multiplier && clk->divider == divider) { + return false; + } + trace_clock_set_mul_div(CLOCK_PATH(clk), clk->multiplier, multiplier, clk->divider, divider); clk->multiplier = multiplier; clk->divider = divider; + + return true; } static void clock_initfn(Object *obj) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 9e3241b430..4bd9c70a83 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -22,17 +22,14 @@ #include "qapi/error.h" #include "hw/core/cpu.h" #include "sysemu/hw_accel.h" -#include "qemu/notify.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "exec/log.h" -#include "exec/cpu-common.h" -#include "qemu/error-report.h" -#include "qemu/qemu-print.h" +#include "exec/gdbstub.h" #include "sysemu/tcg.h" #include "hw/boards.h" #include "hw/qdev-properties.h" -#include "trace/trace-root.h" +#include "trace.h" #include "qemu/plugin.h" CPUState *cpu_by_arch_id(int64_t id) @@ -70,14 +67,14 @@ CPUState *cpu_create(const char *typename) * BQL here if we need to. cpu_interrupt assumes it is held.*/ void cpu_reset_interrupt(CPUState *cpu, int mask) { - bool need_lock = !qemu_mutex_iothread_locked(); + bool need_lock = !bql_locked(); if (need_lock) { - qemu_mutex_lock_iothread(); + bql_lock(); } cpu->interrupt_request &= ~mask; if (need_lock) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } } @@ -86,7 +83,7 @@ void cpu_exit(CPUState *cpu) qatomic_set(&cpu->exit_request, 1); /* Ensure cpu_exec will see the exit request after TCG has exited. */ smp_wmb(); - qatomic_set(&cpu->icount_decr_ptr->u16.high, -1); + qatomic_set(&cpu->neg.icount_decr.u16.high, -1); } static int cpu_common_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) @@ -113,12 +110,12 @@ void cpu_reset(CPUState *cpu) { device_cold_reset(DEVICE(cpu)); - trace_guest_cpu_reset(cpu); + trace_cpu_reset(cpu->cpu_index); } -static void cpu_common_reset(DeviceState *dev) +static void cpu_common_reset_hold(Object *obj) { - CPUState *cpu = CPU(dev); + CPUState *cpu = CPU(obj); CPUClass *cc = CPU_GET_CLASS(cpu); if (qemu_loglevel_mask(CPU_LOG_RESET)) { @@ -130,17 +127,13 @@ static void cpu_common_reset(DeviceState *dev) cpu->halted = cpu->start_powered_off; cpu->mem_io_pc = 0; cpu->icount_extra = 0; - qatomic_set(&cpu->icount_decr_ptr->u32, 0); - cpu->can_do_io = 1; + qatomic_set(&cpu->neg.icount_decr.u32, 0); + cpu->neg.can_do_io = true; cpu->exception_index = -1; cpu->crash_occurred = false; cpu->cflags_next_tb = -1; - if (tcg_enabled()) { - cpu_tb_jmp_cache_clear(cpu); - - tcg_flush_softmmu_tlb(cpu); - } + cpu_exec_reset_hold(cpu); } static bool cpu_common_has_work(CPUState *cs) @@ -150,10 +143,20 @@ static bool cpu_common_has_work(CPUState *cs) ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) { - CPUClass *cc = CPU_CLASS(object_class_by_name(typename)); + ObjectClass *oc; + CPUClass *cc; - assert(cpu_model && cc->class_by_name); - return cc->class_by_name(cpu_model); + oc = object_class_by_name(typename); + cc = CPU_CLASS(oc); + assert(cc->class_by_name); + assert(cpu_model); + oc = cc->class_by_name(cpu_model); + if (object_class_dynamic_cast(oc, typename) && + !object_class_is_abstract(oc)) { + return oc; + } + + return NULL; } static void cpu_common_parse_features(const char *typename, char *features, @@ -187,6 +190,13 @@ static void cpu_common_parse_features(const char *typename, char *features, } } +#ifdef CONFIG_PLUGIN +static void qemu_plugin_vcpu_init__async(CPUState *cpu, run_on_cpu_data unused) +{ + qemu_plugin_vcpu_init_hook(cpu); +} +#endif + static void cpu_common_realizefn(DeviceState *dev, Error **errp) { CPUState *cpu = CPU(dev); @@ -197,8 +207,7 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp) * no need to check the ignore_memory_transaction_failures board flag. */ if (object_dynamic_cast(machine, TYPE_MACHINE)) { - ObjectClass *oc = object_get_class(machine); - MachineClass *mc = MACHINE_CLASS(oc); + MachineClass *mc = MACHINE_GET_CLASS(machine); if (mc) { cpu->ignore_memory_transaction_failures = @@ -211,33 +220,45 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp) cpu_resume(cpu); } + /* Plugin initialization must wait until the cpu start executing code */ +#ifdef CONFIG_PLUGIN + if (tcg_enabled()) { + cpu->plugin_state = qemu_plugin_create_vcpu_state(); + async_run_on_cpu(cpu, qemu_plugin_vcpu_init__async, RUN_ON_CPU_NULL); + } +#endif + /* NOTE: latest generic point where the cpu is fully realized */ - trace_init_vcpu(cpu); } static void cpu_common_unrealizefn(DeviceState *dev) { CPUState *cpu = CPU(dev); + /* Call the plugin hook before clearing the cpu is fully unrealized */ + if (tcg_enabled()) { + qemu_plugin_vcpu_exit_hook(cpu); + } + /* NOTE: latest generic point before the cpu is fully unrealized */ - trace_fini_vcpu(cpu); cpu_exec_unrealizefn(cpu); } static void cpu_common_initfn(Object *obj) { CPUState *cpu = CPU(obj); - CPUClass *cc = CPU_GET_CLASS(obj); + gdb_init_cpu(cpu); cpu->cpu_index = UNASSIGNED_CPU_INDEX; cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX; - cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs; - /* *-user doesn't have configurable SMP topology */ - /* the default value is changed by qemu_init_vcpu() for softmmu */ + /* user-mode doesn't have configurable SMP topology */ + /* the default value is changed by qemu_init_vcpu() for system-mode */ cpu->nr_cores = 1; cpu->nr_threads = 1; + cpu->cflags_next_tb = -1; qemu_mutex_init(&cpu->work_mutex); + qemu_lockcnt_init(&cpu->in_ioctl_lock); QSIMPLEQ_INIT(&cpu->work_list); QTAILQ_INIT(&cpu->breakpoints); QTAILQ_INIT(&cpu->watchpoints); @@ -249,6 +270,8 @@ static void cpu_common_finalize(Object *obj) { CPUState *cpu = CPU(obj); + g_array_free(cpu->gdb_regs, TRUE); + qemu_lockcnt_destroy(&cpu->in_ioctl_lock); qemu_mutex_destroy(&cpu->work_mutex); } @@ -257,9 +280,10 @@ static int64_t cpu_common_get_arch_id(CPUState *cpu) return cpu->cpu_index; } -static void cpu_class_init(ObjectClass *klass, void *data) +static void cpu_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); CPUClass *k = CPU_CLASS(klass); k->parse_features = cpu_common_parse_features; @@ -270,7 +294,7 @@ 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->reset = cpu_common_reset; + rc->phases.hold = cpu_common_reset_hold; cpu_class_init_props(dc); /* * Reason: CPUs still need special care by board code: wiring up @@ -287,7 +311,7 @@ static const TypeInfo cpu_type_info = { .instance_finalize = cpu_common_finalize, .abstract = true, .class_size = sizeof(CPUClass), - .class_init = cpu_class_init, + .class_init = cpu_common_class_init, }; static void cpu_register_types(void) diff --git a/hw/core/cpu-sysemu.c b/hw/core/cpu-sysemu.c index 00253f8929..d0d6a910f9 100644 --- a/hw/core/cpu-sysemu.c +++ b/hw/core/cpu-sysemu.c @@ -34,17 +34,17 @@ bool cpu_paging_enabled(const CPUState *cpu) return false; } -void cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, +bool cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, Error **errp) { CPUClass *cc = CPU_GET_CLASS(cpu); if (cc->sysemu_ops->get_memory_mapping) { - cc->sysemu_ops->get_memory_mapping(cpu, list, errp); - return; + return cc->sysemu_ops->get_memory_mapping(cpu, list, errp); } error_setg(errp, "Obtaining memory mappings is unsupported on this CPU."); + return false; } hwaddr cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, @@ -69,11 +69,10 @@ hwaddr cpu_get_phys_page_debug(CPUState *cpu, vaddr addr) int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs) { - CPUClass *cc = CPU_GET_CLASS(cpu); int ret = 0; - if (cc->sysemu_ops->asidx_from_attrs) { - ret = cc->sysemu_ops->asidx_from_attrs(cpu, attrs); + if (cpu->cc->sysemu_ops->asidx_from_attrs) { + ret = cpu->cc->sysemu_ops->asidx_from_attrs(cpu, attrs); assert(ret < cpu->num_ases && ret >= 0); } return ret; diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c index c666545aa0..d4b5c501d8 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -24,7 +24,7 @@ * callback that does the memory operations. * This device allows the user to monkey patch memory. To be able to do - * this it needs a backend to manage the datas, the same as other + * this it needs a backend to manage the data, the same as other * memory-related devices. In this case as the backend is so trivial we * have merged it with the frontend instead of creating and maintaining a * separate backend. @@ -67,7 +67,7 @@ static void generic_loader_realize(DeviceState *dev, Error **errp) GenericLoaderState *s = GENERIC_LOADER(dev); hwaddr entry; int big_endian; - int size = 0; + ssize_t size = 0; s->set_pc = false; @@ -166,7 +166,7 @@ static void generic_loader_realize(DeviceState *dev, Error **errp) } } - /* Convert the data endiannes */ + /* Convert the data endianness */ if (s->data_be) { s->data = cpu_to_be64(s->data); } else { diff --git a/hw/core/irq.c b/hw/core/irq.c index 3623f711fe..3f14e2dda7 100644 --- a/hw/core/irq.c +++ b/hw/core/irq.c @@ -26,8 +26,7 @@ #include "hw/irq.h" #include "qom/object.h" -DECLARE_INSTANCE_CHECKER(struct IRQState, IRQ, - TYPE_IRQ) +OBJECT_DECLARE_SIMPLE_TYPE(IRQState, IRQ) struct IRQState { Object parent_obj; @@ -68,7 +67,7 @@ qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n) qemu_irq qemu_allocate_irq(qemu_irq_handler handler, void *opaque, int n) { - struct IRQState *irq; + IRQState *irq; irq = IRQ(object_new(TYPE_IRQ)); irq->handler = handler; @@ -94,7 +93,7 @@ void qemu_free_irq(qemu_irq irq) static void qemu_notirq(void *opaque, int line, int level) { - struct IRQState *irq = opaque; + IRQState *irq = opaque; irq->handler(irq->opaque, irq->n, !level); } @@ -120,7 +119,7 @@ void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n) static const TypeInfo irq_type_info = { .name = TYPE_IRQ, .parent = TYPE_OBJECT, - .instance_size = sizeof(struct IRQState), + .instance_size = sizeof(IRQState), }; static void irq_register_types(void) diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index b7c7b3ba94..9f20007dbb 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -120,6 +120,7 @@ static int fit_load_kernel(const struct fit_loader *ldr, const void *itb, int cfg, void *opaque, hwaddr *pend, Error **errp) { + ERRP_GUARD(); const char *name; const void *data; const void *load_data; @@ -178,6 +179,7 @@ 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) { + ERRP_GUARD(); Error *err = NULL; const char *name; const void *data; diff --git a/hw/core/loader.c b/hw/core/loader.c index edde657ac3..b8e52f3fb0 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -35,7 +35,7 @@ * * 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 + * 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 @@ -44,6 +44,7 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" #include "qapi/type-helpers.h" @@ -61,6 +62,7 @@ #include "hw/boards.h" #include "qemu/cutils.h" #include "sysemu/runstate.h" +#include "tcg/debuginfo.h" #include @@ -114,17 +116,17 @@ ssize_t read_targphys(const char *name, return did; } -int load_image_targphys(const char *filename, - hwaddr addr, uint64_t max_sz) +ssize_t load_image_targphys(const char *filename, + hwaddr addr, uint64_t max_sz) { return load_image_targphys_as(filename, addr, max_sz, NULL); } /* return the size or -1 if error */ -int load_image_targphys_as(const char *filename, - hwaddr addr, uint64_t max_sz, AddressSpace *as) +ssize_t load_image_targphys_as(const char *filename, + hwaddr addr, uint64_t max_sz, AddressSpace *as) { - int size; + ssize_t size; size = get_image_size(filename); if (size < 0 || size > max_sz) { @@ -138,9 +140,9 @@ int load_image_targphys_as(const char *filename, return size; } -int load_image_mr(const char *filename, MemoryRegion *mr) +ssize_t load_image_mr(const char *filename, MemoryRegion *mr) { - int size; + ssize_t size; if (!memory_access_is_direct(mr, false)) { /* Can only load an image into RAM or ROM */ @@ -209,8 +211,8 @@ static void bswap_ahdr(struct exec *e) #define ZMAGIC 0413 #define QMAGIC 0314 #define _N_HDROFF(x) (1024 - sizeof (struct exec)) -#define N_TXTOFF(x) \ - (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : \ +#define N_TXTOFF(x) \ + (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : \ (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec))) #define N_TXTADDR(x, target_page_size) (N_MAGIC(x) == QMAGIC ? target_page_size : 0) #define _N_SEGMENT_ROUND(x, target_page_size) (((x) + target_page_size - 1) & ~(target_page_size - 1)) @@ -222,8 +224,8 @@ static void bswap_ahdr(struct exec *e) : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x, target_page_size), target_page_size))) -int load_aout(const char *filename, hwaddr addr, int max_sz, - int bswap_needed, hwaddr target_page_size) +ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, + int bswap_needed, hwaddr target_page_size) { int fd; ssize_t size, ret; @@ -299,10 +301,10 @@ static void *load_at(int fd, off_t offset, size_t size) #define ELF_CLASS ELFCLASS32 #include "elf.h" -#define SZ 32 +#define SZ 32 #define elf_word uint32_t -#define elf_sword int32_t -#define bswapSZs bswap32s +#define elf_sword int32_t +#define bswapSZs bswap32s #include "hw/elf_ops.h" #undef elfhdr @@ -315,16 +317,16 @@ static void *load_at(int fd, off_t offset, size_t size) #undef elf_sword #undef bswapSZs #undef SZ -#define elfhdr elf64_hdr -#define elf_phdr elf64_phdr -#define elf_note elf64_note -#define elf_shdr elf64_shdr -#define elf_sym elf64_sym +#define elfhdr elf64_hdr +#define elf_phdr elf64_phdr +#define elf_note elf64_note +#define elf_shdr elf64_shdr +#define elf_sym elf64_sym #define elf_rela elf64_rela #define elf_word uint64_t -#define elf_sword int64_t -#define bswapSZs bswap64s -#define SZ 64 +#define elf_sword int64_t +#define bswapSZs bswap64s +#define SZ 64 #include "hw/elf_ops.h" const char *load_elf_strerror(ssize_t error) @@ -503,6 +505,10 @@ ssize_t load_elf_ram_sym(const char *filename, clear_lsb, data_swab, as, load_rom, sym_cb); } + if (ret > 0) { + debuginfo_report_elf(filename, fd, 0); + } + fail: close(fd); return ret; @@ -522,7 +528,7 @@ static void bswap_uboot_header(uboot_image_header_t *hdr) } -#define ZALLOC_ALIGNMENT 16 +#define ZALLOC_ALIGNMENT 16 static void *zalloc(void *x, unsigned items, unsigned size) { @@ -542,17 +548,17 @@ static void zfree(void *x, void *addr) } -#define HEAD_CRC 2 -#define EXTRA_FIELD 4 -#define ORIG_NAME 8 -#define COMMENT 0x10 -#define RESERVED 0xe0 +#define HEAD_CRC 2 +#define EXTRA_FIELD 4 +#define ORIG_NAME 8 +#define COMMENT 0x10 +#define RESERVED 0xe0 -#define DEFLATED 8 +#define DEFLATED 8 ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen) { - z_stream s; + z_stream s = {}; ssize_t dstbytes; int r, i, flags; @@ -617,13 +623,14 @@ toosmall: } /* Load a U-Boot image. */ -static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr, - int *is_linux, uint8_t image_type, - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, AddressSpace *as) +static ssize_t load_uboot_image(const char *filename, hwaddr *ep, + hwaddr *loadaddr, int *is_linux, + uint8_t image_type, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, AddressSpace *as) { int fd; - int size; + ssize_t size; hwaddr address; uboot_image_header_t h; uboot_image_header_t *hdr = &h; @@ -760,40 +767,40 @@ out: return ret; } -int load_uimage(const char *filename, hwaddr *ep, hwaddr *loadaddr, - int *is_linux, - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque) +ssize_t load_uimage(const char *filename, hwaddr *ep, hwaddr *loadaddr, + int *is_linux, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque) { return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL, translate_fn, translate_opaque, NULL); } -int load_uimage_as(const char *filename, hwaddr *ep, hwaddr *loadaddr, - int *is_linux, - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, AddressSpace *as) +ssize_t load_uimage_as(const char *filename, hwaddr *ep, hwaddr *loadaddr, + int *is_linux, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, AddressSpace *as) { return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL, translate_fn, translate_opaque, as); } /* Load a ramdisk. */ -int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz) +ssize_t load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz) { return load_ramdisk_as(filename, addr, max_sz, NULL); } -int load_ramdisk_as(const char *filename, hwaddr addr, uint64_t max_sz, - AddressSpace *as) +ssize_t load_ramdisk_as(const char *filename, hwaddr addr, uint64_t max_sz, + AddressSpace *as) { return load_uboot_image(filename, NULL, &addr, NULL, IH_TYPE_RAMDISK, NULL, NULL, as); } /* Load a gzip-compressed kernel to a dynamically allocated buffer. */ -int load_image_gzipped_buffer(const char *filename, uint64_t max_sz, - uint8_t **buffer) +ssize_t load_image_gzipped_buffer(const char *filename, uint64_t max_sz, + uint8_t **buffer) { uint8_t *compressed_data = NULL; uint8_t *data = NULL; @@ -838,9 +845,9 @@ int load_image_gzipped_buffer(const char *filename, uint64_t max_sz, } /* Load a gzip-compressed kernel. */ -int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) +ssize_t load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) { - int bytes; + ssize_t bytes; uint8_t *data; bytes = load_image_gzipped_buffer(filename, max_sz, &data); @@ -851,6 +858,97 @@ int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) return bytes; } +/* The PE/COFF MS-DOS stub magic number */ +#define EFI_PE_MSDOS_MAGIC "MZ" + +/* + * The Linux header magic number for a EFI PE/COFF + * image targeting an unspecified architecture. + */ +#define EFI_PE_LINUX_MAGIC "\xcd\x23\x82\x81" + +/* + * Bootable Linux kernel images may be packaged as EFI zboot images, which are + * self-decompressing executables when loaded via EFI. The compressed payload + * can also be extracted from the image and decompressed by a non-EFI loader. + * + * The de facto specification for this format is at the following URL: + * + * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/firmware/efi/libstub/zboot-header.S + * + * This definition is based on Linux upstream commit 29636a5ce87beba. + */ +struct linux_efi_zboot_header { + uint8_t msdos_magic[2]; /* PE/COFF 'MZ' magic number */ + uint8_t reserved0[2]; + uint8_t zimg[4]; /* "zimg" for Linux EFI zboot images */ + uint32_t payload_offset; /* LE offset to compressed payload */ + uint32_t payload_size; /* LE size of the compressed payload */ + uint8_t reserved1[8]; + char compression_type[32]; /* Compression type, NUL terminated */ + uint8_t linux_magic[4]; /* Linux header magic */ + uint32_t pe_header_offset; /* LE offset to the PE header */ +}; + +/* + * Check whether *buffer points to a Linux EFI zboot image in memory. + * + * If it does, attempt to decompress it to a new buffer, and free the old one. + * If any of this fails, return an error to the caller. + * + * If the image is not a Linux EFI zboot image, do nothing and return success. + */ +ssize_t unpack_efi_zboot_image(uint8_t **buffer, int *size) +{ + const struct linux_efi_zboot_header *header; + uint8_t *data = NULL; + int ploff, plsize; + ssize_t bytes; + + /* ignore if this is too small to be a EFI zboot image */ + if (*size < sizeof(*header)) { + return 0; + } + + header = (struct linux_efi_zboot_header *)*buffer; + + /* ignore if this is not a Linux EFI zboot image */ + if (memcmp(&header->msdos_magic, EFI_PE_MSDOS_MAGIC, 2) != 0 || + memcmp(&header->zimg, "zimg", 4) != 0 || + memcmp(&header->linux_magic, EFI_PE_LINUX_MAGIC, 4) != 0) { + return 0; + } + + if (strcmp(header->compression_type, "gzip") != 0) { + fprintf(stderr, + "unable to handle EFI zboot image with \"%.*s\" compression\n", + (int)sizeof(header->compression_type) - 1, + header->compression_type); + return -1; + } + + ploff = ldl_le_p(&header->payload_offset); + plsize = ldl_le_p(&header->payload_size); + + if (ploff < 0 || plsize < 0 || ploff + plsize > *size) { + fprintf(stderr, "unable to handle corrupt EFI zboot image\n"); + return -1; + } + + data = g_malloc(LOAD_IMAGE_MAX_GUNZIP_BYTES); + bytes = gunzip(data, LOAD_IMAGE_MAX_GUNZIP_BYTES, *buffer + ploff, plsize); + if (bytes < 0) { + fprintf(stderr, "failed to decompress EFI zboot image\n"); + g_free(data); + return -1; + } + + g_free(*buffer); + *buffer = g_realloc(data, bytes); + *size = bytes; + return bytes; +} + /* * Functions for reboot-persistent memory regions. * - used for vga bios and option roms. @@ -970,14 +1068,15 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name, bool ro) return data; } -int rom_add_file(const char *file, const char *fw_dir, - hwaddr addr, int32_t bootindex, - bool option_rom, MemoryRegion *mr, - AddressSpace *as) +ssize_t rom_add_file(const char *file, const char *fw_dir, + hwaddr addr, int32_t bootindex, + bool has_option_rom, MemoryRegion *mr, + AddressSpace *as) { MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); Rom *rom; - int rc, fd = -1; + ssize_t rc; + int fd = -1; char devpath[100]; if (as && mr) { @@ -1019,7 +1118,7 @@ int rom_add_file(const char *file, const char *fw_dir, lseek(fd, 0, SEEK_SET); rc = read(fd, rom->data, rom->datasize); if (rc != rom->datasize) { - fprintf(stderr, "rom: file %-20s: read error: rc=%d (expected %zd)\n", + fprintf(stderr, "rom: file %-20s: read error: rc=%zd (expected %zd)\n", rom->name, rc, rom->datasize); goto err; } @@ -1040,7 +1139,7 @@ int rom_add_file(const char *file, const char *fw_dir, basename); snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); - if ((!option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) { + if ((!has_option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) { data = rom_set_mr(rom, OBJECT(fw_cfg), devpath, true); } else { data = rom->data; @@ -1052,7 +1151,7 @@ int rom_add_file(const char *file, const char *fw_dir, rom->mr = mr; snprintf(devpath, sizeof(devpath), "/rom@%s", file); } else { - snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr); + snprintf(devpath, sizeof(devpath), "/rom@" HWADDR_FMT_plx, addr); } } @@ -1138,12 +1237,12 @@ int rom_add_elf_program(const char *name, GMappedFile *mapped_file, void *data, return 0; } -int rom_add_vga(const char *file) +ssize_t rom_add_vga(const char *file) { return rom_add_file(file, "vgaroms", 0, -1, true, NULL, NULL); } -int rom_add_option(const char *file, int32_t bootindex) +ssize_t rom_add_option(const char *file, int32_t bootindex) { return rom_add_file(file, "genroms", 0, bootindex, true, NULL, NULL); } @@ -1236,10 +1335,10 @@ static void rom_print_one_overlap_error(Rom *last_rom, Rom *rom) "\nThe following two regions overlap (in the %s address space):\n", rom_as_name(rom)); error_printf( - " %s (addresses 0x" TARGET_FMT_plx " - 0x" TARGET_FMT_plx ")\n", + " %s (addresses 0x" HWADDR_FMT_plx " - 0x" HWADDR_FMT_plx ")\n", last_rom->name, last_rom->addr, last_rom->addr + last_rom->romsize); error_printf( - " %s (addresses 0x" TARGET_FMT_plx " - 0x" TARGET_FMT_plx ")\n", + " %s (addresses 0x" HWADDR_FMT_plx " - 0x" HWADDR_FMT_plx ")\n", rom->name, rom->addr, rom->addr + rom->romsize); } @@ -1393,7 +1492,7 @@ RomGap rom_find_largest_gap_between(hwaddr base, size_t size) if (rom->mr || rom->fw_file) { continue; } - /* ignore anything finishing bellow base */ + /* ignore anything finishing below base */ if (rom->addr + rom->romsize <= base) { continue; } @@ -1593,7 +1692,7 @@ HumanReadableText *qmp_x_query_roms(Error **errp) rom->romsize, rom->name); } else if (!rom->fw_file) { - g_string_append_printf(buf, "addr=" TARGET_FMT_plx + g_string_append_printf(buf, "addr=" HWADDR_FMT_plx " size=0x%06zx mem=%s name=\"%s\"\n", rom->addr, rom->romsize, rom->isrom ? "rom" : "ram", @@ -1846,11 +1945,12 @@ out: } /* return size or -1 if error */ -int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as) +ssize_t load_targphys_hex_as(const char *filename, hwaddr *entry, + AddressSpace *as) { gsize hex_blob_size; gchar *hex_blob; - int total_size = 0; + ssize_t total_size = 0; if (!g_file_get_contents(filename, &hex_blob, &hex_blob_size, NULL)) { return -1; diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index 5cb5eecbfc..a6ff6a4875 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -62,7 +62,7 @@ void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict) monitor_printf(mon, " type: \"%s\"\n", l->value->type); monitor_printf(mon, " vcpus_count: \"%" PRIu64 "\"\n", l->value->vcpus_count); - if (l->value->has_qom_path) { + if (l->value->qom_path) { monitor_printf(mon, " qom_path: \"%s\"\n", l->value->qom_path); } @@ -71,6 +71,12 @@ void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict) if (c->has_node_id) { monitor_printf(mon, " node-id: \"%" PRIu64 "\"\n", c->node_id); } + if (c->has_drawer_id) { + monitor_printf(mon, " drawer-id: \"%" PRIu64 "\"\n", c->drawer_id); + } + if (c->has_book_id) { + monitor_printf(mon, " book-id: \"%" PRIu64 "\"\n", c->book_id); + } if (c->has_socket_id) { monitor_printf(mon, " socket-id: \"%" PRIu64 "\"\n", c->socket_id); } @@ -134,3 +140,226 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict) qapi_free_MemdevList(memdev_list); hmp_handle_error(mon, err); } + +void hmp_info_kvm(Monitor *mon, const QDict *qdict) +{ + KvmInfo *info; + + info = qmp_query_kvm(NULL); + monitor_printf(mon, "kvm support: "); + if (info->present) { + monitor_printf(mon, "%s\n", info->enabled ? "enabled" : "disabled"); + } else { + monitor_printf(mon, "not compiled\n"); + } + + qapi_free_KvmInfo(info); +} + +void hmp_info_uuid(Monitor *mon, const QDict *qdict) +{ + UuidInfo *info; + + info = qmp_query_uuid(NULL); + monitor_printf(mon, "%s\n", info->UUID); + qapi_free_UuidInfo(info); +} + +void hmp_info_balloon(Monitor *mon, const QDict *qdict) +{ + BalloonInfo *info; + Error *err = NULL; + + info = qmp_query_balloon(&err); + if (hmp_handle_error(mon, err)) { + return; + } + + monitor_printf(mon, "balloon: actual=%" PRId64 "\n", info->actual >> 20); + + qapi_free_BalloonInfo(info); +} + +void hmp_system_reset(Monitor *mon, const QDict *qdict) +{ + qmp_system_reset(NULL); +} + +void hmp_system_powerdown(Monitor *mon, const QDict *qdict) +{ + qmp_system_powerdown(NULL); +} + +void hmp_memsave(Monitor *mon, const QDict *qdict) +{ + uint32_t size = qdict_get_int(qdict, "size"); + const char *filename = qdict_get_str(qdict, "filename"); + uint64_t addr = qdict_get_int(qdict, "val"); + Error *err = NULL; + int cpu_index = monitor_get_cpu_index(mon); + + if (cpu_index < 0) { + monitor_printf(mon, "No CPU available\n"); + return; + } + + qmp_memsave(addr, size, filename, true, cpu_index, &err); + hmp_handle_error(mon, err); +} + +void hmp_pmemsave(Monitor *mon, const QDict *qdict) +{ + uint32_t size = qdict_get_int(qdict, "size"); + const char *filename = qdict_get_str(qdict, "filename"); + uint64_t addr = qdict_get_int(qdict, "val"); + Error *err = NULL; + + qmp_pmemsave(addr, size, filename, &err); + hmp_handle_error(mon, err); +} + +void hmp_system_wakeup(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qmp_system_wakeup(&err); + hmp_handle_error(mon, err); +} + +void hmp_nmi(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qmp_inject_nmi(&err); + hmp_handle_error(mon, err); +} + +void hmp_balloon(Monitor *mon, const QDict *qdict) +{ + int64_t value = qdict_get_int(qdict, "value"); + Error *err = NULL; + + qmp_balloon(value, &err); + hmp_handle_error(mon, err); +} + +void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + MemoryDeviceInfoList *info_list = qmp_query_memory_devices(&err); + MemoryDeviceInfoList *info; + VirtioPMEMDeviceInfo *vpi; + VirtioMEMDeviceInfo *vmi; + MemoryDeviceInfo *value; + PCDIMMDeviceInfo *di; + SgxEPCDeviceInfo *se; + HvBalloonDeviceInfo *hi; + + for (info = info_list; info; info = info->next) { + value = info->value; + + if (value) { + switch (value->type) { + case MEMORY_DEVICE_INFO_KIND_DIMM: + case MEMORY_DEVICE_INFO_KIND_NVDIMM: + di = value->type == MEMORY_DEVICE_INFO_KIND_DIMM ? + value->u.dimm.data : value->u.nvdimm.data; + monitor_printf(mon, "Memory device [%s]: \"%s\"\n", + MemoryDeviceInfoKind_str(value->type), + di->id ? di->id : ""); + monitor_printf(mon, " addr: 0x%" PRIx64 "\n", di->addr); + monitor_printf(mon, " slot: %" PRId64 "\n", di->slot); + monitor_printf(mon, " node: %" PRId64 "\n", di->node); + monitor_printf(mon, " size: %" PRIu64 "\n", di->size); + monitor_printf(mon, " memdev: %s\n", di->memdev); + monitor_printf(mon, " hotplugged: %s\n", + di->hotplugged ? "true" : "false"); + monitor_printf(mon, " hotpluggable: %s\n", + di->hotpluggable ? "true" : "false"); + break; + case MEMORY_DEVICE_INFO_KIND_VIRTIO_PMEM: + vpi = value->u.virtio_pmem.data; + monitor_printf(mon, "Memory device [%s]: \"%s\"\n", + MemoryDeviceInfoKind_str(value->type), + vpi->id ? vpi->id : ""); + monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", vpi->memaddr); + monitor_printf(mon, " size: %" PRIu64 "\n", vpi->size); + monitor_printf(mon, " memdev: %s\n", vpi->memdev); + break; + case MEMORY_DEVICE_INFO_KIND_VIRTIO_MEM: + vmi = value->u.virtio_mem.data; + monitor_printf(mon, "Memory device [%s]: \"%s\"\n", + MemoryDeviceInfoKind_str(value->type), + vmi->id ? vmi->id : ""); + monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", vmi->memaddr); + monitor_printf(mon, " node: %" PRId64 "\n", vmi->node); + monitor_printf(mon, " requested-size: %" PRIu64 "\n", + vmi->requested_size); + monitor_printf(mon, " size: %" PRIu64 "\n", vmi->size); + monitor_printf(mon, " max-size: %" PRIu64 "\n", vmi->max_size); + monitor_printf(mon, " block-size: %" PRIu64 "\n", + vmi->block_size); + monitor_printf(mon, " memdev: %s\n", vmi->memdev); + break; + case MEMORY_DEVICE_INFO_KIND_SGX_EPC: + se = value->u.sgx_epc.data; + monitor_printf(mon, "Memory device [%s]: \"%s\"\n", + MemoryDeviceInfoKind_str(value->type), + se->id ? se->id : ""); + monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", se->memaddr); + monitor_printf(mon, " size: %" PRIu64 "\n", se->size); + monitor_printf(mon, " node: %" PRId64 "\n", se->node); + monitor_printf(mon, " memdev: %s\n", se->memdev); + break; + case MEMORY_DEVICE_INFO_KIND_HV_BALLOON: + hi = value->u.hv_balloon.data; + monitor_printf(mon, "Memory device [%s]: \"%s\"\n", + MemoryDeviceInfoKind_str(value->type), + hi->id ? hi->id : ""); + if (hi->has_memaddr) { + monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", + hi->memaddr); + } + monitor_printf(mon, " max-size: %" PRIu64 "\n", hi->max_size); + if (hi->memdev) { + monitor_printf(mon, " memdev: %s\n", hi->memdev); + } + break; + default: + g_assert_not_reached(); + } + } + } + + qapi_free_MemoryDeviceInfoList(info_list); + hmp_handle_error(mon, err); +} + +void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + GuidInfo *info = qmp_query_vm_generation_id(&err); + if (info) { + monitor_printf(mon, "%s\n", info->guid); + } + hmp_handle_error(mon, err); + qapi_free_GuidInfo(info); +} + +void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + MemoryInfo *info = qmp_query_memory_size_summary(&err); + if (info) { + monitor_printf(mon, "base memory: %" PRIu64 "\n", + info->base_memory); + + if (info->has_plugged_memory) { + monitor_printf(mon, "plugged memory: %" PRIu64 "\n", + info->plugged_memory); + } + + qapi_free_MemoryInfo(info); + } + hmp_handle_error(mon, err); +} diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 4f4ab30f8c..4b72009cd3 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -8,32 +8,24 @@ */ #include "qemu/osdep.h" +#include "hw/acpi/vmgenid.h" #include "hw/boards.h" +#include "hw/intc/intc.h" +#include "hw/mem/memory-device.h" +#include "hw/rdma/rdma.h" #include "qapi/error.h" #include "qapi/qapi-builtin-visit.h" #include "qapi/qapi-commands-machine.h" -#include "qapi/qmp/qerror.h" #include "qapi/qmp/qobject.h" #include "qapi/qobject-input-visitor.h" #include "qapi/type-helpers.h" -#include "qemu/main-loop.h" +#include "qemu/uuid.h" #include "qom/qom-qobject.h" #include "sysemu/hostmem.h" #include "sysemu/hw_accel.h" #include "sysemu/numa.h" #include "sysemu/runstate.h" - -static void cpustate_to_cpuinfo_s390(CpuInfoS390 *info, const CPUState *cpu) -{ -#ifdef TARGET_S390X - S390CPU *s390_cpu = S390_CPU(cpu); - CPUS390XState *env = &s390_cpu->env; - - info->cpu_state = env->cpu_state; -#else - abort(); -#endif -} +#include "sysemu/sysemu.h" /* * fast means: we NEVER interrupt vCPU threads to retrieve @@ -44,7 +36,7 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) MachineState *ms = MACHINE(qdev_get_machine()); MachineClass *mc = MACHINE_GET_CLASS(ms); CpuInfoFastList *head = NULL, **tail = &head; - SysEmuTarget target = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, + SysEmuTarget target = qapi_enum_parse(&SysEmuTarget_lookup, target_name(), -1, &error_abort); CPUState *cpu; @@ -55,8 +47,7 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) value->qom_path = object_get_canonical_path(OBJECT(cpu)); value->thread_id = cpu->thread_id; - value->has_props = !!mc->cpu_index_to_instance_props; - if (value->has_props) { + if (mc->cpu_index_to_instance_props) { CpuInstanceProperties *props; props = g_malloc0(sizeof(*props)); *props = mc->cpu_index_to_instance_props(ms, cpu->cpu_index); @@ -64,8 +55,8 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) } value->target = target; - if (target == SYS_EMU_TARGET_S390X) { - cpustate_to_cpuinfo_s390(&value->u.s390x, cpu); + if (cpu->cc->query_cpu_fast) { + cpu->cc->query_cpu_fast(cpu, value); } QAPI_LIST_APPEND(tail, value); @@ -90,7 +81,6 @@ MachineInfoList *qmp_query_machines(Error **errp) } if (mc->alias) { - info->has_alias = true; info->alias = g_strdup(mc->alias); } @@ -99,13 +89,12 @@ MachineInfoList *qmp_query_machines(Error **errp) info->hotpluggable_cpus = mc->has_hotpluggable_cpus; info->numa_mem_supported = mc->numa_mem_supported; info->deprecated = !!mc->deprecation_reason; + info->acpi = !!object_class_property_find(OBJECT_CLASS(mc), "acpi"); if (mc->default_cpu_type) { info->default_cpu_type = g_strdup(mc->default_cpu_type); - info->has_default_cpu_type = true; } if (mc->default_ram_id) { info->default_ram_id = g_strdup(mc->default_ram_id); - info->has_default_ram_id = true; } QAPI_LIST_PREPEND(mach_list, info); @@ -127,7 +116,7 @@ TargetInfo *qmp_query_target(Error **errp) { TargetInfo *info = g_malloc0(sizeof(*info)); - info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1, + info->arch = qapi_enum_parse(&SysEmuTarget_lookup, target_name(), -1, &error_abort); return info; @@ -139,7 +128,7 @@ HotpluggableCPUList *qmp_query_hotpluggable_cpus(Error **errp) MachineClass *mc = MACHINE_GET_CLASS(ms); if (!mc->has_hotpluggable_cpus) { - error_setg(errp, QERR_FEATURE_DISABLED, "query-hotpluggable-cpus"); + error_setg(errp, "machine does not support hot-plugging CPUs"); return NULL; } @@ -168,7 +157,6 @@ static int query_memdev(Object *obj, void *opaque) m = g_malloc0(sizeof(*m)); m->id = g_strdup(object_get_canonical_path_component(obj)); - m->has_id = !!m->id; m->size = object_property_get_uint(obj, "size", &error_abort); m->merge = object_property_get_bool(obj, "merge", &error_abort); @@ -227,7 +215,7 @@ HumanReadableText *qmp_x_query_numa(Error **errp) for (i = 0; i < nb_numa_nodes; i++) { g_string_append_printf(buf, "node %d cpus:", i); for (cpu = cpu_list; cpu; cpu = cpu->next) { - if (cpu->value->has_props && cpu->value->props->has_node_id && + if (cpu->value->props && cpu->value->props->has_node_id && cpu->value->props->node_id == i) { g_string_append_printf(buf, " %" PRIi64, cpu->value->cpu_index); } @@ -244,3 +232,159 @@ HumanReadableText *qmp_x_query_numa(Error **errp) done: return human_readable_text_from_str(buf); } + +KvmInfo *qmp_query_kvm(Error **errp) +{ + KvmInfo *info = g_malloc0(sizeof(*info)); + + info->enabled = kvm_enabled(); + info->present = accel_find("kvm"); + + return info; +} + +UuidInfo *qmp_query_uuid(Error **errp) +{ + UuidInfo *info = g_malloc0(sizeof(*info)); + + info->UUID = qemu_uuid_unparse_strdup(&qemu_uuid); + return info; +} + +void qmp_system_reset(Error **errp) +{ + qemu_system_reset_request(SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET); +} + +void qmp_system_powerdown(Error **errp) +{ + qemu_system_powerdown_request(); +} + +void qmp_system_wakeup(Error **errp) +{ + if (!qemu_wakeup_suspend_enabled()) { + error_setg(errp, + "wake-up from suspend is not supported by this guest"); + return; + } + + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, errp); +} + +MemoryDeviceInfoList *qmp_query_memory_devices(Error **errp) +{ + return qmp_memory_device_list(); +} + +MemoryInfo *qmp_query_memory_size_summary(Error **errp) +{ + MemoryInfo *mem_info = g_new0(MemoryInfo, 1); + MachineState *ms = MACHINE(qdev_get_machine()); + + mem_info->base_memory = ms->ram_size; + + mem_info->plugged_memory = get_plugged_memory_size(); + mem_info->has_plugged_memory = + mem_info->plugged_memory != (uint64_t)-1; + + return mem_info; +} + +static int qmp_x_query_rdma_foreach(Object *obj, void *opaque) +{ + RdmaProvider *rdma; + RdmaProviderClass *k; + GString *buf = opaque; + + if (object_dynamic_cast(obj, INTERFACE_RDMA_PROVIDER)) { + rdma = RDMA_PROVIDER(obj); + k = RDMA_PROVIDER_GET_CLASS(obj); + if (k->format_statistics) { + k->format_statistics(rdma, buf); + } else { + g_string_append_printf(buf, + "RDMA statistics not available for %s.\n", + object_get_typename(obj)); + } + } + + return 0; +} + +HumanReadableText *qmp_x_query_rdma(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + object_child_foreach_recursive(object_get_root(), + qmp_x_query_rdma_foreach, buf); + + return human_readable_text_from_str(buf); +} + +HumanReadableText *qmp_x_query_ramblock(Error **errp) +{ + g_autoptr(GString) buf = ram_block_format(); + + return human_readable_text_from_str(buf); +} + +static int qmp_x_query_irq_foreach(Object *obj, void *opaque) +{ + InterruptStatsProvider *intc; + InterruptStatsProviderClass *k; + GString *buf = opaque; + + if (object_dynamic_cast(obj, TYPE_INTERRUPT_STATS_PROVIDER)) { + intc = INTERRUPT_STATS_PROVIDER(obj); + k = INTERRUPT_STATS_PROVIDER_GET_CLASS(obj); + uint64_t *irq_counts; + unsigned int nb_irqs, i; + if (k->get_statistics && + k->get_statistics(intc, &irq_counts, &nb_irqs)) { + if (nb_irqs > 0) { + g_string_append_printf(buf, "IRQ statistics for %s:\n", + object_get_typename(obj)); + for (i = 0; i < nb_irqs; i++) { + if (irq_counts[i] > 0) { + g_string_append_printf(buf, "%2d: %" PRId64 "\n", i, + irq_counts[i]); + } + } + } + } else { + g_string_append_printf(buf, + "IRQ statistics not available for %s.\n", + object_get_typename(obj)); + } + } + + return 0; +} + +HumanReadableText *qmp_x_query_irq(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + object_child_foreach_recursive(object_get_root(), + qmp_x_query_irq_foreach, buf); + + return human_readable_text_from_str(buf); +} + +GuidInfo *qmp_query_vm_generation_id(Error **errp) +{ + GuidInfo *info; + VmGenIdState *vms; + Object *obj = find_vmgenid_dev(); + + if (!obj) { + error_setg(errp, "VM Generation ID device not found"); + return NULL; + } + vms = VMGENID(obj); + + info = g_malloc0(sizeof(*info)); + info->guid = qemu_uuid_unparse_strdup(&vms->guid); + return info; +} diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c index b39ed21e65..27864c9507 100644 --- a/hw/core/machine-smp.c +++ b/hw/core/machine-smp.c @@ -20,6 +20,8 @@ #include "qemu/osdep.h" #include "hw/boards.h" #include "qapi/error.h" +#include "qemu/error-report.h" + /* * Report information of a machine's supported CPU topology hierarchy. @@ -31,6 +33,14 @@ static char *cpu_hierarchy_to_string(MachineState *ms) MachineClass *mc = MACHINE_GET_CLASS(ms); GString *s = g_string_new(NULL); + if (mc->smp_props.drawers_supported) { + g_string_append_printf(s, "drawers (%u) * ", ms->smp.drawers); + } + + if (mc->smp_props.books_supported) { + g_string_append_printf(s, "books (%u) * ", ms->smp.books); + } + g_string_append_printf(s, "sockets (%u)", ms->smp.sockets); if (mc->smp_props.dies_supported) { @@ -73,44 +83,94 @@ void machine_parse_smp_config(MachineState *ms, { MachineClass *mc = MACHINE_GET_CLASS(ms); unsigned cpus = config->has_cpus ? config->cpus : 0; + unsigned drawers = config->has_drawers ? config->drawers : 0; + unsigned books = config->has_books ? config->books : 0; unsigned sockets = config->has_sockets ? config->sockets : 0; unsigned dies = config->has_dies ? config->dies : 0; unsigned clusters = config->has_clusters ? config->clusters : 0; unsigned cores = config->has_cores ? config->cores : 0; unsigned threads = config->has_threads ? config->threads : 0; unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0; + unsigned total_cpus; /* * Specified CPU topology parameters must be greater than zero, * explicit configuration like "cpus=0" is not allowed. */ if ((config->has_cpus && config->cpus == 0) || + (config->has_drawers && config->drawers == 0) || + (config->has_books && config->books == 0) || (config->has_sockets && config->sockets == 0) || (config->has_dies && config->dies == 0) || (config->has_clusters && config->clusters == 0) || (config->has_cores && config->cores == 0) || (config->has_threads && config->threads == 0) || (config->has_maxcpus && config->maxcpus == 0)) { - warn_report("Deprecated CPU topology (considered invalid): " - "CPU topology parameters must be greater than zero"); + error_setg(errp, "Invalid CPU topology: " + "CPU topology parameters must be greater than zero"); + return; } /* * If not supported by the machine, a topology parameter must be - * omitted or specified equal to 1. + * omitted. */ - if (!mc->smp_props.dies_supported && dies > 1) { - error_setg(errp, "dies not supported by this machine's CPU topology"); - return; + if (!mc->smp_props.clusters_supported && config->has_clusters) { + if (config->clusters > 1) { + error_setg(errp, "clusters not supported by this " + "machine's CPU topology"); + return; + } else { + /* Here clusters only equals 1 since we've checked zero case. */ + warn_report("Deprecated CPU topology (considered invalid): " + "Unsupported clusters parameter mustn't be " + "specified as 1"); + } } - if (!mc->smp_props.clusters_supported && clusters > 1) { - error_setg(errp, "clusters not supported by this machine's CPU topology"); - return; - } - - dies = dies > 0 ? dies : 1; clusters = clusters > 0 ? clusters : 1; + if (!mc->smp_props.dies_supported && config->has_dies) { + if (config->dies > 1) { + error_setg(errp, "dies not supported by this " + "machine's CPU topology"); + return; + } else { + /* Here dies only equals 1 since we've checked zero case. */ + warn_report("Deprecated CPU topology (considered invalid): " + "Unsupported dies parameter mustn't be " + "specified as 1"); + } + } + dies = dies > 0 ? dies : 1; + + if (!mc->smp_props.books_supported && config->has_books) { + if (config->books > 1) { + error_setg(errp, "books not supported by this " + "machine's CPU topology"); + return; + } else { + /* Here books only equals 1 since we've checked zero case. */ + warn_report("Deprecated CPU topology (considered invalid): " + "Unsupported books parameter mustn't be " + "specified as 1"); + } + } + books = books > 0 ? books : 1; + + if (!mc->smp_props.drawers_supported && config->has_drawers) { + if (config->drawers > 1) { + error_setg(errp, "drawers not supported by this " + "machine's CPU topology"); + return; + } else { + /* Here drawers only equals 1 since we've checked zero case. */ + warn_report("Deprecated CPU topology (considered invalid): " + "Unsupported drawers parameter mustn't be " + "specified as 1"); + } + } + drawers = drawers > 0 ? drawers : 1; + /* compute missing values based on the provided ones */ if (cpus == 0 && maxcpus == 0) { sockets = sockets > 0 ? sockets : 1; @@ -124,33 +184,41 @@ void machine_parse_smp_config(MachineState *ms, if (sockets == 0) { cores = cores > 0 ? cores : 1; threads = threads > 0 ? threads : 1; - sockets = maxcpus / (dies * clusters * cores * threads); + sockets = maxcpus / + (drawers * books * dies * clusters * cores * threads); } else if (cores == 0) { threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * dies * clusters * threads); + cores = maxcpus / + (drawers * books * sockets * dies * clusters * threads); } } else { /* prefer cores over sockets since 6.2 */ if (cores == 0) { sockets = sockets > 0 ? sockets : 1; threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * dies * clusters * threads); + cores = maxcpus / + (drawers * books * sockets * dies * clusters * threads); } else if (sockets == 0) { threads = threads > 0 ? threads : 1; - sockets = maxcpus / (dies * clusters * cores * threads); + sockets = maxcpus / + (drawers * books * dies * clusters * cores * threads); } } /* try to calculate omitted threads at last */ if (threads == 0) { - threads = maxcpus / (sockets * dies * clusters * cores); + threads = maxcpus / + (drawers * books * sockets * dies * clusters * cores); } } - maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * clusters * cores * threads; + total_cpus = drawers * books * sockets * dies * clusters * cores * threads; + maxcpus = maxcpus > 0 ? maxcpus : total_cpus; cpus = cpus > 0 ? cpus : maxcpus; ms->smp.cpus = cpus; + ms->smp.drawers = drawers; + ms->smp.books = books; ms->smp.sockets = sockets; ms->smp.dies = dies; ms->smp.clusters = clusters; @@ -158,8 +226,10 @@ void machine_parse_smp_config(MachineState *ms, ms->smp.threads = threads; ms->smp.max_cpus = maxcpus; + mc->smp_props.has_clusters = config->has_clusters; + /* sanity-check of the computed topology */ - if (sockets * dies * clusters * cores * threads != maxcpus) { + if (total_cpus != maxcpus) { g_autofree char *topo_msg = cpu_hierarchy_to_string(ms); error_setg(errp, "Invalid CPU topology: " "product of the hierarchy must match maxcpus: " @@ -193,3 +263,13 @@ void machine_parse_smp_config(MachineState *ms, return; } } + +unsigned int machine_topo_get_cores_per_socket(const MachineState *ms) +{ + return ms->smp.cores * ms->smp.clusters * ms->smp.dies; +} + +unsigned int machine_topo_get_threads_per_socket(const MachineState *ms) +{ + return ms->smp.threads * machine_topo_get_cores_per_socket(ms); +} diff --git a/hw/core/machine.c b/hw/core/machine.c index c53548d0b1..37ede0e7d4 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -11,35 +11,67 @@ */ #include "qemu/osdep.h" -#include "qemu/option.h" -#include "qapi/qmp/qerror.h" +#include "qemu/accel.h" #include "sysemu/replay.h" -#include "qemu/units.h" #include "hw/boards.h" #include "hw/loader.h" #include "qapi/error.h" -#include "qapi/qapi-visit-common.h" #include "qapi/qapi-visit-machine.h" -#include "qapi/visitor.h" #include "qom/object_interfaces.h" -#include "hw/sysbus.h" #include "sysemu/cpus.h" #include "sysemu/sysemu.h" #include "sysemu/reset.h" #include "sysemu/runstate.h" -#include "sysemu/numa.h" #include "sysemu/xen.h" -#include "qemu/error-report.h" #include "sysemu/qtest.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_bridge.h" #include "hw/mem/nvdimm.h" -#include "hw/cxl/cxl.h" #include "migration/global_state.h" -#include "migration/vmstate.h" #include "exec/confidential-guest-support.h" -#include "hw/virtio/virtio.h" #include "hw/virtio/virtio-pci.h" -#include "qom/object_interfaces.h" +#include "hw/virtio/virtio-net.h" +#include "hw/virtio/virtio-iommu.h" +#include "audio/audio.h" + +GlobalProperty hw_compat_8_2[] = { + { "migration", "zero-page-detection", "legacy"}, + { TYPE_VIRTIO_IOMMU_PCI, "granule", "4k" }, + { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "64" }, +}; +const size_t hw_compat_8_2_len = G_N_ELEMENTS(hw_compat_8_2); + +GlobalProperty hw_compat_8_1[] = { + { TYPE_PCI_BRIDGE, "x-pci-express-writeable-slt-bug", "true" }, + { "ramfb", "x-migrate", "off" }, + { "vfio-pci-nohotplug", "x-ramfb-migrate", "off" }, + { "igb", "x-pcie-flr-init", "off" }, +}; +const size_t hw_compat_8_1_len = G_N_ELEMENTS(hw_compat_8_1); + +GlobalProperty hw_compat_8_0[] = { + { "migration", "multifd-flush-after-each-section", "on"}, + { TYPE_PCI_DEVICE, "x-pcie-ari-nextfn-1", "on" }, + { TYPE_VIRTIO_NET, "host_uso", "off"}, + { TYPE_VIRTIO_NET, "guest_uso4", "off"}, + { TYPE_VIRTIO_NET, "guest_uso6", "off"}, +}; +const size_t hw_compat_8_0_len = G_N_ELEMENTS(hw_compat_8_0); + +GlobalProperty hw_compat_7_2[] = { + { "e1000e", "migrate-timadj", "off" }, + { "virtio-mem", "x-early-migration", "false" }, + { "migration", "x-preempt-pre-7-2", "true" }, + { TYPE_PCI_DEVICE, "x-pcie-err-unc-mask", "off" }, +}; +const size_t hw_compat_7_2_len = G_N_ELEMENTS(hw_compat_7_2); + +GlobalProperty hw_compat_7_1[] = { + { "virtio-device", "queue_reset", "false" }, + { "virtio-rng-pci", "vectors", "0" }, + { "virtio-rng-pci-transitional", "vectors", "0" }, + { "virtio-rng-pci-non-transitional", "vectors", "0" }, +}; +const size_t hw_compat_7_1_len = G_N_ELEMENTS(hw_compat_7_1); GlobalProperty hw_compat_7_0[] = { { "arm-gicv3-common", "force-8-bit-prio", "on" }, @@ -73,6 +105,7 @@ GlobalProperty hw_compat_5_2[] = { { "PIIX4_PM", "smm-compat", "on"}, { "virtio-blk-device", "report-discard-granularity", "off" }, { "virtio-net-pci-base", "vectors", "3"}, + { "nvme", "msix-exclusive-bar", "on"}, }; const size_t hw_compat_5_2_len = G_N_ELEMENTS(hw_compat_5_2); @@ -550,12 +583,11 @@ static void machine_get_mem(Object *obj, Visitor *v, const char *name, static void machine_set_mem(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { + ERRP_GUARD(); MachineState *ms = MACHINE(obj); MachineClass *mc = MACHINE_GET_CLASS(obj); MemorySizeConfiguration *mem; - ERRP_GUARD(); - if (!visit_type_MemorySizeConfiguration(v, name, &mem, errp)) { return; } @@ -629,20 +661,6 @@ static void machine_set_nvdimm_persistence(Object *obj, const char *value, nvdimms_state->persistence_string = g_strdup(value); } -static bool machine_get_cxl(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return ms->cxl_devices_state->is_enabled; -} - -static void machine_set_cxl(Object *obj, bool value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - ms->cxl_devices_state->is_enabled = value; -} - void machine_class_allow_dynamic_sysbus_dev(MachineClass *mc, const char *type) { QAPI_LIST_PREPEND(mc->allowed_dynamic_sysbus_devices, g_strdup(type)); @@ -674,6 +692,26 @@ bool device_type_is_dynamic_sysbus(MachineClass *mc, const char *type) return allowed; } +static char *machine_get_audiodev(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->audiodev); +} + +static void machine_set_audiodev(Object *obj, const char *value, + Error **errp) +{ + MachineState *ms = MACHINE(obj); + + if (!audio_state_by_name(value, errp)) { + return; + } + + g_free(ms->audiodev); + ms->audiodev = g_strdup(value); +} + HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine) { int i; @@ -684,7 +722,7 @@ HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine) mc->possible_cpu_arch_ids(machine); for (i = 0; i < machine->possible_cpus->len; i++) { - Object *cpu; + CPUState *cpu; HotpluggableCPU *cpu_item = g_new0(typeof(*cpu_item), 1); cpu_item->type = g_strdup(machine->possible_cpus->cpus[i].type); @@ -694,8 +732,7 @@ HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine) cpu = machine->possible_cpus->cpus[i].cpu; if (cpu) { - cpu_item->has_qom_path = true; - cpu_item->qom_path = object_get_canonical_path(cpu); + cpu_item->qom_path = object_get_canonical_path(OBJECT(cpu)); } QAPI_LIST_PREPEND(head, cpu_item); } @@ -836,6 +873,8 @@ static void machine_get_smp(Object *obj, Visitor *v, const char *name, MachineState *ms = MACHINE(obj); SMPConfiguration *config = &(SMPConfiguration){ .has_cpus = true, .cpus = ms->smp.cpus, + .has_drawers = true, .drawers = ms->smp.drawers, + .has_books = true, .books = ms->smp.books, .has_sockets = true, .sockets = ms->smp.sockets, .has_dies = true, .dies = ms->smp.dies, .has_clusters = true, .clusters = ms->smp.clusters, @@ -883,8 +922,7 @@ static void machine_copy_boot_config(MachineState *ms, BootConfiguration *config machine_free_boot_config(ms); ms->boot_config = *config; - if (!config->has_order) { - ms->boot_config.has_order = true; + if (!config->order) { ms->boot_config.order = g_strdup(machine_class->default_boot_order); } } @@ -899,13 +937,13 @@ static void machine_set_boot(Object *obj, Visitor *v, const char *name, if (!visit_type_BootConfiguration(v, name, &config, errp)) { return; } - if (config->has_order) { + if (config->order) { validate_bootdevices(config->order, errp); if (*errp) { goto out_free; } } - if (config->has_once) { + if (config->once) { validate_bootdevices(config->once, errp); if (*errp) { goto out_free; @@ -921,6 +959,17 @@ out_free: qapi_free_BootConfiguration(config); } +void machine_add_audiodev_property(MachineClass *mc) +{ + ObjectClass *oc = OBJECT_CLASS(mc); + + object_class_property_add_str(oc, "audiodev", + machine_get_audiodev, + machine_set_audiodev); + object_class_property_set_description(oc, "audiodev", + "Audiodev to use for default machine devices"); +} + static void machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -929,8 +978,6 @@ static void machine_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 128 * MiB; mc->rom_file_has_mr = true; - /* Few machines support CXL, so default to off */ - mc->cxl_supported = false; /* numa node memory size aligned on 8MB by default. * On Linux, each node's border has to be 8MB aligned */ @@ -1074,8 +1121,6 @@ static void machine_initfn(Object *obj) ms->maxram_size = mc->default_ram_size; if (mc->nvdimm_supported) { - Object *obj = OBJECT(ms); - ms->nvdimms_state = g_new0(NVDIMMState, 1); object_property_add_bool(obj, "nvdimm", machine_get_nvdimm, machine_set_nvdimm); @@ -1091,16 +1136,6 @@ static void machine_initfn(Object *obj) "Valid values are cpu, mem-ctrl"); } - if (mc->cxl_supported) { - Object *obj = OBJECT(ms); - - ms->cxl_devices_state = g_new0(CXLState, 1); - object_property_add_bool(obj, "cxl", machine_get_cxl, machine_set_cxl); - object_property_set_description(obj, "cxl", - "Set on/off to enable/disable " - "CXL instantiation"); - } - 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", @@ -1114,6 +1149,8 @@ static void machine_initfn(Object *obj) /* default to mc->default_cpus */ ms->smp.cpus = mc->default_cpus; ms->smp.max_cpus = mc->default_cpus; + ms->smp.drawers = 1; + ms->smp.books = 1; ms->smp.sockets = 1; ms->smp.dies = 1; ms->smp.clusters = 1; @@ -1138,7 +1175,7 @@ static void machine_finalize(Object *obj) g_free(ms->device_memory); g_free(ms->nvdimms_state); g_free(ms->numa_state); - g_free(ms->cxl_devices_state); + g_free(ms->audiodev); } bool machine_usb(MachineState *machine) @@ -1201,9 +1238,7 @@ static void numa_validate_initiator(NumaState *numa_state) 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); + continue; } if (!numa_info[numa_info[i].initiator].present) { @@ -1275,6 +1310,45 @@ static void machine_numa_finish_cpu_init(MachineState *machine) g_string_free(s, true); } +static void validate_cpu_cluster_to_numa_boundary(MachineState *ms) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + NumaState *state = ms->numa_state; + const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); + const CPUArchId *cpus = possible_cpus->cpus; + int i, j; + + if (qtest_enabled() || state->num_nodes <= 1 || possible_cpus->len <= 1) { + return; + } + + /* + * The Linux scheduling domain can't be parsed when the multiple CPUs + * in one cluster have been associated with different NUMA nodes. However, + * it's fine to associate one NUMA node with CPUs in different clusters. + */ + for (i = 0; i < possible_cpus->len; i++) { + for (j = i + 1; j < possible_cpus->len; j++) { + if (cpus[i].props.has_socket_id && + cpus[i].props.has_cluster_id && + cpus[i].props.has_node_id && + cpus[j].props.has_socket_id && + cpus[j].props.has_cluster_id && + cpus[j].props.has_node_id && + cpus[i].props.socket_id == cpus[j].props.socket_id && + cpus[i].props.cluster_id == cpus[j].props.cluster_id && + cpus[i].props.node_id != cpus[j].props.node_id) { + warn_report("CPU-%d and CPU-%d in socket-%" PRId64 "-cluster-%" PRId64 + " have been associated with node-%" PRId64 " and node-%" PRId64 + " respectively. It can cause OSes like Linux to" + " misbehave", i, j, cpus[i].props.socket_id, + cpus[i].props.cluster_id, cpus[i].props.node_id, + cpus[j].props.node_id); + } + } + } +} + MemoryRegion *machine_consume_memdev(MachineState *machine, HostMemoryBackend *backend) { @@ -1322,12 +1396,74 @@ out: return r; } +const char *machine_class_default_cpu_type(MachineClass *mc) +{ + if (mc->valid_cpu_types && !mc->valid_cpu_types[1]) { + /* Only a single CPU type allowed: use it as default. */ + return mc->valid_cpu_types[0]; + } + return mc->default_cpu_type; +} + +static bool is_cpu_type_supported(const MachineState *machine, Error **errp) +{ + MachineClass *mc = MACHINE_GET_CLASS(machine); + ObjectClass *oc = object_class_by_name(machine->cpu_type); + CPUClass *cc; + int i; + + /* + * Check if the user specified CPU type is supported when the valid + * CPU types have been determined. Note that the user specified CPU + * type is provided through '-cpu' option. + */ + if (mc->valid_cpu_types) { + assert(mc->valid_cpu_types[0] != NULL); + for (i = 0; mc->valid_cpu_types[i]; i++) { + if (object_class_dynamic_cast(oc, mc->valid_cpu_types[i])) { + break; + } + } + + /* The user specified CPU type isn't valid */ + if (!mc->valid_cpu_types[i]) { + g_autofree char *requested = cpu_model_from_type(machine->cpu_type); + error_setg(errp, "Invalid CPU model: %s", requested); + if (!mc->valid_cpu_types[1]) { + g_autofree char *model = cpu_model_from_type( + mc->valid_cpu_types[0]); + error_append_hint(errp, "The only valid type is: %s\n", model); + } else { + error_append_hint(errp, "The valid models are: "); + for (i = 0; mc->valid_cpu_types[i]; i++) { + g_autofree char *model = cpu_model_from_type( + mc->valid_cpu_types[i]); + error_append_hint(errp, "%s%s", + model, + mc->valid_cpu_types[i + 1] ? ", " : ""); + } + error_append_hint(errp, "\n"); + } + + return false; + } + } + + /* Check if CPU type is deprecated and warn if so */ + cc = CPU_CLASS(oc); + assert(cc != NULL); + if (cc->deprecation_note) { + warn_report("CPU model %s is deprecated -- %s", + machine->cpu_type, cc->deprecation_note); + } + + return true; +} void machine_run_board_init(MachineState *machine, const char *mem_path, Error **errp) { + ERRP_GUARD(); MachineClass *machine_class = MACHINE_GET_CLASS(machine); - ObjectClass *oc = object_class_by_name(machine->cpu_type); - CPUClass *cc; /* This checkpoint is required by replay to separate prior clock reading from the other reads, because timer polling functions query @@ -1351,6 +1487,18 @@ void machine_run_board_init(MachineState *machine, const char *mem_path, Error * } } else if (machine_class->default_ram_id && machine->ram_size && numa_uses_legacy_mem()) { + if (object_property_find(object_get_objects_root(), + machine_class->default_ram_id)) { + error_setg(errp, "object's id '%s' is reserved for the default" + " RAM backend, it can't be used for any other purposes", + machine_class->default_ram_id); + error_append_hint(errp, + "Change the object's 'id' to something else or disable" + " automatic creation of the default RAM backend by setting" + " 'memory-backend=%s' with '-machine'.\n", + machine_class->default_ram_id); + return; + } if (!create_default_memdev(current_machine, mem_path, errp)) { return; } @@ -1360,6 +1508,9 @@ void machine_run_board_init(MachineState *machine, const char *mem_path, Error * numa_complete_configuration(machine); if (machine->numa_state->num_nodes) { machine_numa_finish_cpu_init(machine); + if (machine_class->cpu_cluster_has_numa_boundary) { + validate_cpu_cluster_to_numa_boundary(machine); + } } } @@ -1367,41 +1518,9 @@ void machine_run_board_init(MachineState *machine, const char *mem_path, Error * machine->ram = machine_consume_memdev(machine, machine->memdev); } - /* If the machine supports the valid_cpu_types check and the user - * specified a CPU with -cpu check here that the user CPU is supported. - */ - if (machine_class->valid_cpu_types && machine->cpu_type) { - int i; - - for (i = 0; machine_class->valid_cpu_types[i]; i++) { - if (object_class_dynamic_cast(oc, - machine_class->valid_cpu_types[i])) { - /* The user specificed CPU is in the valid field, we are - * good to go. - */ - break; - } - } - - if (!machine_class->valid_cpu_types[i]) { - /* The user specified CPU is not valid */ - error_report("Invalid CPU type: %s", machine->cpu_type); - error_printf("The valid types are: %s", - machine_class->valid_cpu_types[0]); - for (i = 1; machine_class->valid_cpu_types[i]; i++) { - error_printf(", %s", machine_class->valid_cpu_types[i]); - } - error_printf("\n"); - - exit(1); - } - } - - /* Check if CPU type is deprecated and warn if so */ - cc = CPU_CLASS(oc); - if (cc && cc->deprecation_note) { - warn_report("CPU model %s is deprecated -- %s", machine->cpu_type, - cc->deprecation_note); + /* Check if the CPU type is supported */ + if (machine->cpu_type && !is_cpu_type_supported(machine, errp)) { + return; } if (machine->cgs) { @@ -1449,7 +1568,7 @@ void qdev_machine_creation_done(void) { cpu_synchronize_all_post_init(); - if (current_machine->boot_config.has_once) { + if (current_machine->boot_config.once) { qemu_boot_set(current_machine->boot_config.once, &error_fatal); qemu_register_reset(restore_boot_order, g_strdup(current_machine->boot_config.order)); } @@ -1464,14 +1583,13 @@ void qdev_machine_creation_done(void) /* TODO: once all bus devices are qdevified, this should be done * when bus is created by qdev.c */ /* - * 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 + * This is where we arrange for the sysbus to be reset when the + * whole simulation is reset. In turn, resetting the sysbus 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_register_resettable(OBJECT(sysbus_get_default())); notifier_list_notify(&machine_init_done_notifiers, NULL); diff --git a/hw/core/meson.build b/hw/core/meson.build index 7a4d02b6c0..e26f2e088c 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -4,6 +4,7 @@ hwcore_ss.add(files( 'qdev-properties.c', 'qdev.c', 'reset.c', + 'resetcontainer.c', 'resettable.c', 'vmstate-if.c', # irq.c needed for qdev GPIO handling: @@ -24,34 +25,31 @@ endif common_ss.add(files('cpu-common.c')) common_ss.add(files('machine-smp.c')) -softmmu_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c')) -softmmu_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c')) -softmmu_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c')) -softmmu_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c')) -softmmu_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('platform-bus.c')) -softmmu_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c')) -softmmu_ss.add(when: 'CONFIG_REGISTER', if_true: files('register.c')) -softmmu_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c')) -softmmu_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) -softmmu_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) +system_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c')) +system_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c')) +system_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c')) +system_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c')) +system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('platform-bus.c')) +system_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c')) +system_ss.add(when: 'CONFIG_REGISTER', if_true: files('register.c')) +system_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c')) +system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) +system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) -softmmu_ss.add(files( +system_ss.add(files( 'cpu-sysemu.c', 'fw-path-provider.c', 'gpio.c', 'loader.c', 'machine-hmp-cmds.c', + 'machine-qmp-cmds.c', 'machine.c', 'nmi.c', 'null-machine.c', + 'numa.c', 'qdev-fw.c', 'qdev-properties-system.c', 'sysbus.c', 'vm-change-state-handler.c', 'clock-vmstate.c', )) - -specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files( - 'machine-qmp-cmds.c', - 'numa.c', -)) diff --git a/hw/core/nmi.c b/hw/core/nmi.c index 481c4b3c7e..a7bce8a04a 100644 --- a/hw/core/nmi.c +++ b/hw/core/nmi.c @@ -22,7 +22,6 @@ #include "qemu/osdep.h" #include "hw/nmi.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qemu/module.h" #include "monitor/monitor.h" @@ -70,7 +69,7 @@ void nmi_monitor_handle(int cpu_index, Error **errp) if (ns.handled) { error_propagate(errp, ns.err); } else { - error_setg(errp, QERR_UNSUPPORTED); + error_setg(errp, "machine does not provide NMIs"); } } diff --git a/hw/core/numa.c b/hw/core/numa.c index 26d8e5f616..f8ce332cfe 100644 --- a/hw/core/numa.c +++ b/hw/core/numa.c @@ -28,7 +28,6 @@ #include "sysemu/numa.h" #include "exec/cpu-common.h" #include "exec/ramlist.h" -#include "qemu/bitmap.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/opts-visitor.h" @@ -36,7 +35,6 @@ #include "sysemu/qtest.h" #include "hw/core/cpu.h" #include "hw/mem/pc-dimm.h" -#include "migration/vmstate.h" #include "hw/boards.h" #include "hw/mem/memory-device.h" #include "qemu/option.h" @@ -130,9 +128,9 @@ static void parse_numa_node(MachineState *ms, NumaNodeOptions *node, } } - have_memdevs = have_memdevs ? : node->has_memdev; - have_mem = have_mem ? : node->has_mem; - if ((node->has_mem && have_memdevs) || (node->has_memdev && have_mem)) { + have_memdevs = have_memdevs || node->memdev; + have_mem = have_mem || node->has_mem; + if ((node->has_mem && have_memdevs) || (node->memdev && have_mem)) { error_setg(errp, "numa configuration should use either mem= or memdev=," "mixing both is not allowed"); return; @@ -152,7 +150,7 @@ static void parse_numa_node(MachineState *ms, NumaNodeOptions *node, " use -numa node,memdev instead"); } } - if (node->has_memdev) { + if (node->memdev) { Object *o; o = object_resolve_path_type(node->memdev, TYPE_MEMORY_BACKEND, NULL); if (!o) { @@ -229,7 +227,8 @@ void parse_numa_hmat_lb(NumaState *numa_state, NumaHmatLBOptions *node, node->target, numa_state->num_nodes); return; } - if (!numa_info[node->initiator].has_cpu) { + if (!numa_info[node->initiator].has_cpu && + !numa_info[node->initiator].has_gi) { error_setg(errp, "Invalid initiator=%d, it isn't an " "initiator proximity domain", node->initiator); return; @@ -531,10 +530,17 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) /* Fix up legacy suffix-less format */ if ((object->type == NUMA_OPTIONS_TYPE_NODE) && object->u.node.has_mem) { const char *mem_str = qemu_opt_get(opts, "mem"); - qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem); + int ret = qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem); + + if (ret < 0) { + error_setg_errno(&err, -ret, "could not parse memory size '%s'", + mem_str); + } } - set_numa_options(ms, object, &err); + if (!err) { + set_numa_options(ms, object, &err); + } qapi_free_NumaOptions(object); if (err) { @@ -822,6 +828,19 @@ static int ram_block_notify_add_single(RAMBlock *rb, void *opaque) return 0; } +static int ram_block_notify_remove_single(RAMBlock *rb, void *opaque) +{ + const ram_addr_t max_size = qemu_ram_get_max_length(rb); + const ram_addr_t size = qemu_ram_get_used_length(rb); + void *host = qemu_ram_get_host_addr(rb); + RAMBlockNotifier *notifier = opaque; + + if (host) { + notifier->ram_block_removed(notifier, host, size, max_size); + } + return 0; +} + void ram_block_notifier_add(RAMBlockNotifier *n) { QLIST_INSERT_HEAD(&ram_list.ramblock_notifiers, n, next); @@ -835,13 +854,18 @@ void ram_block_notifier_add(RAMBlockNotifier *n) void ram_block_notifier_remove(RAMBlockNotifier *n) { QLIST_REMOVE(n, next); + + if (n->ram_block_removed) { + qemu_ram_foreach_block(ram_block_notify_remove_single, n); + } } void ram_block_notify_add(void *host, size_t size, size_t max_size) { RAMBlockNotifier *notifier; + RAMBlockNotifier *next; - QLIST_FOREACH(notifier, &ram_list.ramblock_notifiers, next) { + QLIST_FOREACH_SAFE(notifier, &ram_list.ramblock_notifiers, next, next) { if (notifier->ram_block_added) { notifier->ram_block_added(notifier, host, size, max_size); } @@ -851,8 +875,9 @@ void ram_block_notify_add(void *host, size_t size, size_t max_size) void ram_block_notify_remove(void *host, size_t size, size_t max_size) { RAMBlockNotifier *notifier; + RAMBlockNotifier *next; - QLIST_FOREACH(notifier, &ram_list.ramblock_notifiers, next) { + QLIST_FOREACH_SAFE(notifier, &ram_list.ramblock_notifiers, next, next) { if (notifier->ram_block_removed) { notifier->ram_block_removed(notifier, host, size, max_size); } @@ -862,8 +887,9 @@ void ram_block_notify_remove(void *host, size_t size, size_t max_size) void ram_block_notify_resize(void *host, size_t old_size, size_t new_size) { RAMBlockNotifier *notifier; + RAMBlockNotifier *next; - QLIST_FOREACH(notifier, &ram_list.ramblock_notifiers, next) { + QLIST_FOREACH_SAFE(notifier, &ram_list.ramblock_notifiers, next, next) { if (notifier->ram_block_resized) { notifier->ram_block_resized(notifier, host, old_size, new_size); } diff --git a/hw/core/or-irq.c b/hw/core/or-irq.c index d8f3754e96..13907df026 100644 --- a/hw/core/or-irq.c +++ b/hw/core/or-irq.c @@ -31,7 +31,7 @@ static void or_irq_handler(void *opaque, int n, int level) { - qemu_or_irq *s = OR_IRQ(opaque); + OrIRQState *s = OR_IRQ(opaque); int or_level = 0; int i; @@ -46,7 +46,7 @@ static void or_irq_handler(void *opaque, int n, int level) static void or_irq_reset(DeviceState *dev) { - qemu_or_irq *s = OR_IRQ(dev); + OrIRQState *s = OR_IRQ(dev); int i; for (i = 0; i < MAX_OR_LINES; i++) { @@ -56,7 +56,7 @@ static void or_irq_reset(DeviceState *dev) static void or_irq_realize(DeviceState *dev, Error **errp) { - qemu_or_irq *s = OR_IRQ(dev); + OrIRQState *s = OR_IRQ(dev); assert(s->num_lines <= MAX_OR_LINES); @@ -65,7 +65,7 @@ static void or_irq_realize(DeviceState *dev, Error **errp) static void or_irq_init(Object *obj) { - qemu_or_irq *s = OR_IRQ(obj); + OrIRQState *s = OR_IRQ(obj); qdev_init_gpio_out(DEVICE(obj), &s->out_irq, 1); } @@ -84,7 +84,7 @@ static void or_irq_init(Object *obj) static bool vmstate_extras_needed(void *opaque) { - qemu_or_irq *s = OR_IRQ(opaque); + OrIRQState *s = OR_IRQ(opaque); return s->num_lines >= OLD_MAX_OR_LINES; } @@ -94,8 +94,8 @@ static const VMStateDescription vmstate_or_irq_extras = { .version_id = 1, .minimum_version_id = 1, .needed = vmstate_extras_needed, - .fields = (VMStateField[]) { - VMSTATE_VARRAY_UINT16_UNSAFE(levels, qemu_or_irq, num_lines, 0, + .fields = (const VMStateField[]) { + VMSTATE_VARRAY_UINT16_UNSAFE(levels, OrIRQState, num_lines, 0, vmstate_info_bool, bool), VMSTATE_END_OF_LIST(), }, @@ -105,18 +105,18 @@ static const VMStateDescription vmstate_or_irq = { .name = TYPE_OR_IRQ, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BOOL_SUB_ARRAY(levels, qemu_or_irq, 0, OLD_MAX_OR_LINES), + .fields = (const VMStateField[]) { + VMSTATE_BOOL_SUB_ARRAY(levels, OrIRQState, 0, OLD_MAX_OR_LINES), VMSTATE_END_OF_LIST(), }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_or_irq_extras, NULL }, }; static Property or_irq_properties[] = { - DEFINE_PROP_UINT16("num-lines", qemu_or_irq, num_lines, 1), + DEFINE_PROP_UINT16("num-lines", OrIRQState, num_lines, 1), DEFINE_PROP_END_OF_LIST(), }; @@ -136,7 +136,7 @@ static void or_irq_class_init(ObjectClass *klass, void *data) static const TypeInfo or_irq_type_info = { .name = TYPE_OR_IRQ, .parent = TYPE_DEVICE, - .instance_size = sizeof(qemu_or_irq), + .instance_size = sizeof(OrIRQState), .instance_init = or_irq_init, .class_init = or_irq_class_init, }; diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index eb5ba1aff7..b1517592c6 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -10,7 +10,7 @@ #include "hw/ptimer.h" #include "migration/vmstate.h" #include "qemu/host-utils.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" #include "sysemu/cpu-timers.h" #include "sysemu/qtest.h" #include "block/aio.h" @@ -441,7 +441,7 @@ const VMStateDescription vmstate_ptimer = { .name = "ptimer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(enabled, ptimer_state), VMSTATE_UINT64(limit, ptimer_state), VMSTATE_UINT64(delta, ptimer_state), diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c index 117f4c6ea4..82799577f3 100644 --- a/hw/core/qdev-clock.c +++ b/hw/core/qdev-clock.c @@ -134,7 +134,7 @@ void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks) Clock **clkp; /* offset cannot be inside the DeviceState part */ assert(elem->offset > sizeof(DeviceState)); - clkp = (Clock **)(((void *) dev) + elem->offset); + clkp = ((void *)dev) + elem->offset; if (elem->is_output) { *clkp = qdev_init_clock_out(dev, elem->name); } else { diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index a91f60567a..d79d6f4b53 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -18,6 +18,7 @@ #include "qapi/qapi-types-block.h" #include "qapi/qapi-types-machine.h" #include "qapi/qapi-types-migration.h" +#include "qapi/qapi-visit-virtio.h" #include "qapi/qmp/qerror.h" #include "qemu/ctype.h" #include "qemu/cutils.h" @@ -32,6 +33,8 @@ #include "sysemu/blockdev.h" #include "net/net.h" #include "hw/pci/pci.h" +#include "hw/pci/pcie.h" +#include "hw/i386/x86.h" #include "util/block-helpers.h" static bool check_prop_still_unset(Object *obj, const char *name, @@ -105,7 +108,7 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name, } if (*ptr) { - /* BlockBackend alread exists. So, we want to change attached node */ + /* BlockBackend already exists. So, we want to change attached node */ blk = *ptr; ctx = blk_get_aio_context(blk); bs = bdrv_lookup_bs(NULL, str, errp); @@ -118,9 +121,7 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name, "node"); } - aio_context_acquire(ctx); blk_replace_bs(blk, bs, errp); - aio_context_release(ctx); return; } @@ -141,8 +142,9 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name, * aware of iothreads require their BlockBackends to be in the main * AioContext. */ - ctx = iothread ? bdrv_get_aio_context(bs) : qemu_get_aio_context(); - blk = blk_new(ctx, 0, BLK_PERM_ALL); + ctx = bdrv_get_aio_context(bs); + blk = blk_new(iothread ? ctx : qemu_get_aio_context(), + 0, BLK_PERM_ALL); blk_created = true; ret = blk_insert_bs(blk, bs, errp); @@ -201,12 +203,8 @@ static void release_drive(Object *obj, const char *name, void *opaque) BlockBackend **ptr = object_field_prop_ptr(obj, prop); if (*ptr) { - AioContext *ctx = blk_get_aio_context(*ptr); - - aio_context_acquire(ctx); blockdev_auto_del(*ptr); blk_detach_dev(*ptr, dev); - aio_context_release(ctx); } } @@ -244,6 +242,7 @@ static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque, static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { + ERRP_GUARD(); Property *prop = opaque; CharBackend *be = object_field_prop_ptr(obj, prop); Chardev *s; @@ -444,7 +443,7 @@ static void set_netdev(Object *obj, Visitor *v, const char *name, peers_ptr->queues = queues; out: - error_set_from_qdev_prop_error(errp, err, obj, name, str); + error_set_from_qdev_prop_error(errp, err, obj, prop->name, str); g_free(str); } @@ -474,24 +473,16 @@ static void set_audiodev(Object *obj, Visitor *v, const char* name, Property *prop = opaque; QEMUSoundCard *card = object_field_prop_ptr(obj, prop); AudioState *state; - int err = 0; - char *str; + g_autofree char *str = NULL; if (!visit_type_str(v, name, &str, errp)) { return; } - state = audio_state_by_name(str); - - if (!state) { - err = -ENOENT; - goto out; + state = audio_state_by_name(str, errp); + if (state) { + card->state = state; } - card->state = state; - -out: - error_set_from_qdev_prop_error(errp, err, obj, name, str); - g_free(str); } const PropertyInfo qdev_prop_audiodev = { @@ -557,13 +548,38 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) /* --- lost tick policy --- */ +static void qdev_propinfo_set_losttickpolicy(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + int *ptr = object_field_prop_ptr(obj, prop); + int value; + + if (!visit_type_enum(v, name, &value, prop->info->enum_table, errp)) { + return; + } + + if (value == LOST_TICK_POLICY_SLEW) { + MachineState *ms = MACHINE(qdev_get_machine()); + + if (!object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE)) { + error_setg(errp, + "the 'slew' policy is only available for x86 machines"); + return; + } + } + + *ptr = value; +} + QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int)); const PropertyInfo qdev_prop_losttickpolicy = { .name = "LostTickPolicy", .enum_table = &LostTickPolicy_lookup, .get = qdev_propinfo_get_enum, - .set = qdev_propinfo_set_enum, + .set = qdev_propinfo_set_losttickpolicy, .set_default_value = qdev_propinfo_set_default_value_enum, }; @@ -650,6 +666,44 @@ const PropertyInfo qdev_prop_multifd_compression = { .set_default_value = qdev_propinfo_set_default_value_enum, }; +/* --- MigMode --- */ + +QEMU_BUILD_BUG_ON(sizeof(MigMode) != sizeof(int)); + +const PropertyInfo qdev_prop_mig_mode = { + .name = "MigMode", + .description = "mig_mode values, " + "normal,cpr-reboot", + .enum_table = &MigMode_lookup, + .get = qdev_propinfo_get_enum, + .set = qdev_propinfo_set_enum, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; + +/* --- GranuleMode --- */ + +QEMU_BUILD_BUG_ON(sizeof(GranuleMode) != sizeof(int)); + +const PropertyInfo qdev_prop_granule_mode = { + .name = "GranuleMode", + .description = "granule_mode values, " + "4k, 8k, 16k, 64k, host", + .enum_table = &GranuleMode_lookup, + .get = qdev_propinfo_get_enum, + .set = qdev_propinfo_set_enum, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; + +const PropertyInfo qdev_prop_zero_page_detection = { + .name = "ZeroPageDetection", + .description = "zero_page_detection values, " + "none,legacy,multifd", + .enum_table = &ZeroPageDetection_lookup, + .get = qdev_propinfo_get_enum, + .set = qdev_propinfo_set_enum, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; + /* --- Reserved Region --- */ /* @@ -668,7 +722,7 @@ static void get_reserved_region(Object *obj, Visitor *v, const char *name, int rc; rc = snprintf(buffer, sizeof(buffer), "0x%"PRIx64":0x%"PRIx64":%u", - rr->low, rr->high, rr->type); + range_lob(&rr->range), range_upb(&rr->range), rr->type); assert(rc < sizeof(buffer)); visit_type_str(v, name, &p, errp); @@ -679,18 +733,16 @@ static void set_reserved_region(Object *obj, Visitor *v, const char *name, { Property *prop = opaque; ReservedRegion *rr = object_field_prop_ptr(obj, prop); - Error *local_err = NULL; const char *endptr; + uint64_t lob, upb; char *str; int ret; - visit_type_str(v, name, &str, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!visit_type_str(v, name, &str, errp)) { return; } - ret = qemu_strtou64(str, &endptr, 16, &rr->low); + ret = qemu_strtou64(str, &endptr, 16, &lob); if (ret) { error_setg(errp, "start address of '%s'" " must be a hexadecimal integer", name); @@ -700,7 +752,7 @@ static void set_reserved_region(Object *obj, Visitor *v, const char *name, goto separator_error; } - ret = qemu_strtou64(endptr + 1, &endptr, 16, &rr->high); + ret = qemu_strtou64(endptr + 1, &endptr, 16, &upb); if (ret) { error_setg(errp, "end address of '%s'" " must be a hexadecimal integer", name); @@ -710,6 +762,8 @@ static void set_reserved_region(Object *obj, Visitor *v, const char *name, goto separator_error; } + range_set_bounds(&rr->range, lob, upb); + ret = qemu_strtoui(endptr + 1, &endptr, 10, &rr->type); if (ret) { error_setg(errp, "type of '%s'" @@ -912,7 +966,7 @@ const PropertyInfo qdev_prop_off_auto_pcibar = { .set_default_value = qdev_propinfo_set_default_value_enum, }; -/* --- PCIELinkSpeed 2_5/5/8/16 -- */ +/* --- PCIELinkSpeed 2_5/5/8/16/32/64 -- */ static void get_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -934,6 +988,12 @@ static void get_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, case QEMU_PCI_EXP_LNK_16GT: speed = PCIE_LINK_SPEED_16; break; + case QEMU_PCI_EXP_LNK_32GT: + speed = PCIE_LINK_SPEED_32; + break; + case QEMU_PCI_EXP_LNK_64GT: + speed = PCIE_LINK_SPEED_64; + break; default: /* Unreachable */ abort(); @@ -967,6 +1027,12 @@ static void set_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, case PCIE_LINK_SPEED_16: *p = QEMU_PCI_EXP_LNK_16GT; break; + case PCIE_LINK_SPEED_32: + *p = QEMU_PCI_EXP_LNK_32GT; + break; + case PCIE_LINK_SPEED_64: + *p = QEMU_PCI_EXP_LNK_64GT; + break; default: /* Unreachable */ abort(); @@ -975,7 +1041,7 @@ static void set_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, const PropertyInfo qdev_prop_pcie_link_speed = { .name = "PCIELinkSpeed", - .description = "2_5/5/8/16", + .description = "2_5/5/8/16/32/64", .enum_table = &PCIELinkSpeed_lookup, .get = get_prop_pcielinkspeed, .set = set_prop_pcielinkspeed, @@ -1077,7 +1143,7 @@ static void get_uuid(Object *obj, Visitor *v, const char *name, void *opaque, { Property *prop = opaque; QemuUUID *uuid = object_field_prop_ptr(obj, prop); - char buffer[UUID_FMT_LEN + 1]; + char buffer[UUID_STR_LEN]; char *p = buffer; qemu_uuid_unparse(uuid, buffer); @@ -1119,3 +1185,61 @@ const PropertyInfo qdev_prop_uuid = { .set = set_uuid, .set_default_value = set_default_uuid_auto, }; + +/* --- s390 cpu entitlement policy --- */ + +QEMU_BUILD_BUG_ON(sizeof(CpuS390Entitlement) != sizeof(int)); + +const PropertyInfo qdev_prop_cpus390entitlement = { + .name = "CpuS390Entitlement", + .description = "low/medium (default)/high", + .enum_table = &CpuS390Entitlement_lookup, + .get = qdev_propinfo_get_enum, + .set = qdev_propinfo_set_enum, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; + +/* --- IOThreadVirtQueueMappingList --- */ + +static void get_iothread_vq_mapping_list(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) +{ + IOThreadVirtQueueMappingList **prop_ptr = + object_field_prop_ptr(obj, opaque); + + visit_type_IOThreadVirtQueueMappingList(v, name, prop_ptr, errp); +} + +static void set_iothread_vq_mapping_list(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) +{ + IOThreadVirtQueueMappingList **prop_ptr = + object_field_prop_ptr(obj, opaque); + IOThreadVirtQueueMappingList *list; + + if (!visit_type_IOThreadVirtQueueMappingList(v, name, &list, errp)) { + return; + } + + qapi_free_IOThreadVirtQueueMappingList(*prop_ptr); + *prop_ptr = list; +} + +static void release_iothread_vq_mapping_list(Object *obj, + const char *name, void *opaque) +{ + IOThreadVirtQueueMappingList **prop_ptr = + object_field_prop_ptr(obj, opaque); + + qapi_free_IOThreadVirtQueueMappingList(*prop_ptr); + *prop_ptr = NULL; +} + +const PropertyInfo qdev_prop_iothread_vq_mapping_list = { + .name = "IOThreadVirtQueueMappingList", + .description = "IOThread virtqueue mapping list [{\"iothread\":\"\", " + "\"vqs\":[1,2,3,...]},...]", + .get = get_iothread_vq_mapping_list, + .set = set_iothread_vq_mapping_list, + .release = release_iothread_vq_mapping_list, +}; diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 357b8761b5..7d6fa726fd 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -3,12 +3,14 @@ #include "qapi/error.h" #include "qapi/qapi-types-misc.h" #include "qapi/qmp/qerror.h" +#include "qapi/qmp/qlist.h" #include "qemu/ctype.h" #include "qemu/error-report.h" #include "qapi/visitor.h" #include "qemu/units.h" #include "qemu/cutils.h" #include "qdev-prop-internal.h" +#include "qom/qom-qobject.h" void qdev_prop_set_after_realize(DeviceState *dev, const char *name, Error **errp) @@ -544,98 +546,206 @@ const PropertyInfo qdev_prop_size32 = { /* --- support for array properties --- */ -/* Used as an opaque for the object properties we add for each - * array element. Note that the struct Property must be first - * in the struct so that a pointer to this works as the opaque - * for the underlying element's property hooks as well as for - * our own release callback. - */ -typedef struct { - struct Property prop; - char *propname; - ObjectPropertyRelease *release; -} ArrayElementProperty; +typedef struct ArrayElementList ArrayElementList; -/* object property release callback for array element properties: - * we call the underlying element's property release hook, and - * then free the memory we allocated when we added the property. +struct ArrayElementList { + ArrayElementList *next; + void *value; +}; + +/* + * Given an array property @parent_prop in @obj, return a Property for a + * specific element of the array. Arrays are backed by an uint32_t length field + * and an element array. @elem points at an element in this element array. */ -static void array_element_release(Object *obj, const char *name, void *opaque) +static Property array_elem_prop(Object *obj, Property *parent_prop, + const char *name, char *elem) { - ArrayElementProperty *p = opaque; - if (p->release) { - p->release(obj, name, opaque); - } - g_free(p->propname); - g_free(p); + return (Property) { + .info = parent_prop->arrayinfo, + .name = name, + /* + * This ugly piece of pointer arithmetic sets up the offset so + * that when the underlying release hook calls qdev_get_prop_ptr + * they get the right answer despite the array element not actually + * being inside the device struct. + */ + .offset = (uintptr_t)elem - (uintptr_t)obj, + }; } -static void set_prop_arraylen(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +/* + * Object property release callback for array properties: We call the + * underlying element's property release hook for each element. + * + * Note that it is the responsibility of the individual device's deinit + * to free the array proper. + */ +static void release_prop_array(Object *obj, const char *name, void *opaque) { - /* Setter for the property which defines the length of a - * variable-sized property array. As well as actually setting the - * array-length field in the device struct, we have to create the - * array itself and dynamically add the corresponding properties. - */ Property *prop = opaque; uint32_t *alenptr = object_field_prop_ptr(obj, prop); void **arrayptr = (void *)obj + prop->arrayoffset; - void *eltptr; - const char *arrayname; + char *elem = *arrayptr; int i; + if (!prop->arrayinfo->release) { + return; + } + + for (i = 0; i < *alenptr; i++) { + Property elem_prop = array_elem_prop(obj, prop, name, elem); + prop->arrayinfo->release(obj, NULL, &elem_prop); + elem += prop->arrayfieldsize; + } +} + +/* + * Setter for an array property. This sets both the array length (which + * is technically the property field in the object) and the array itself + * (a pointer to which is stored in the additional field described by + * prop->arrayoffset). + */ +static void set_prop_array(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ERRP_GUARD(); + Property *prop = opaque; + uint32_t *alenptr = object_field_prop_ptr(obj, prop); + void **arrayptr = (void *)obj + prop->arrayoffset; + ArrayElementList *list, *elem, *next; + const size_t size = sizeof(*list); + char *elemptr; + bool ok = true; + if (*alenptr) { error_setg(errp, "array size property %s may not be set more than once", name); return; } - if (!visit_type_uint32(v, name, alenptr, errp)) { - return; - } - if (!*alenptr) { + + if (!visit_start_list(v, name, (GenericList **) &list, size, errp)) { return; } - /* DEFINE_PROP_ARRAY guarantees that name should start with this prefix; - * strip it off so we can get the name of the array itself. - */ - assert(strncmp(name, PROP_ARRAY_LEN_PREFIX, - strlen(PROP_ARRAY_LEN_PREFIX)) == 0); - arrayname = name + strlen(PROP_ARRAY_LEN_PREFIX); + /* Read the whole input into a temporary list */ + elem = list; + while (elem) { + Property elem_prop; - /* Note that it is the responsibility of the individual device's deinit - * to free the array proper. + elem->value = g_malloc0(prop->arrayfieldsize); + elem_prop = array_elem_prop(obj, prop, name, elem->value); + prop->arrayinfo->set(obj, v, NULL, &elem_prop, errp); + if (*errp) { + ok = false; + goto out_obj; + } + if (*alenptr == INT_MAX) { + error_setg(errp, "array is too big"); + return; + } + (*alenptr)++; + elem = (ArrayElementList *) visit_next_list(v, (GenericList*) elem, + size); + } + + ok = visit_check_list(v, errp); +out_obj: + visit_end_list(v, (void**) &list); + + if (!ok) { + for (elem = list; elem; elem = next) { + Property elem_prop = array_elem_prop(obj, prop, name, + elem->value); + if (prop->arrayinfo->release) { + prop->arrayinfo->release(obj, NULL, &elem_prop); + } + next = elem->next; + g_free(elem->value); + g_free(elem); + } + return; + } + + /* + * Now that we know how big the array has to be, move the data over to a + * linear array and free the temporary list. */ - *arrayptr = eltptr = g_malloc0(*alenptr * prop->arrayfieldsize); - for (i = 0; i < *alenptr; i++, eltptr += prop->arrayfieldsize) { - char *propname = g_strdup_printf("%s[%d]", arrayname, i); - ArrayElementProperty *arrayprop = g_new0(ArrayElementProperty, 1); - arrayprop->release = prop->arrayinfo->release; - arrayprop->propname = propname; - arrayprop->prop.info = prop->arrayinfo; - arrayprop->prop.name = propname; - /* This ugly piece of pointer arithmetic sets up the offset so - * that when the underlying get/set hooks call qdev_get_prop_ptr - * they get the right answer despite the array element not actually - * being inside the device struct. - */ - arrayprop->prop.offset = eltptr - (void *)obj; - assert(object_field_prop_ptr(obj, &arrayprop->prop) == eltptr); - object_property_add(obj, propname, - arrayprop->prop.info->name, - field_prop_getter(arrayprop->prop.info), - field_prop_setter(arrayprop->prop.info), - array_element_release, - arrayprop); + *arrayptr = g_malloc_n(*alenptr, prop->arrayfieldsize); + elemptr = *arrayptr; + for (elem = list; elem; elem = next) { + memcpy(elemptr, elem->value, prop->arrayfieldsize); + elemptr += prop->arrayfieldsize; + next = elem->next; + g_free(elem->value); + g_free(elem); } } -const PropertyInfo qdev_prop_arraylen = { - .name = "uint32", - .get = get_uint32, - .set = set_prop_arraylen, - .set_default_value = qdev_propinfo_set_default_value_uint, +static void get_prop_array(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ERRP_GUARD(); + Property *prop = opaque; + uint32_t *alenptr = object_field_prop_ptr(obj, prop); + void **arrayptr = (void *)obj + prop->arrayoffset; + char *elemptr = *arrayptr; + ArrayElementList *list = NULL, *elem; + ArrayElementList **tail = &list; + const size_t size = sizeof(*list); + int i; + bool ok; + + /* At least the string output visitor needs a real list */ + for (i = 0; i < *alenptr; i++) { + elem = g_new0(ArrayElementList, 1); + elem->value = elemptr; + elemptr += prop->arrayfieldsize; + + *tail = elem; + tail = &elem->next; + } + + if (!visit_start_list(v, name, (GenericList **) &list, size, errp)) { + return; + } + + elem = list; + while (elem) { + Property elem_prop = array_elem_prop(obj, prop, name, elem->value); + prop->arrayinfo->get(obj, v, NULL, &elem_prop, errp); + if (*errp) { + goto out_obj; + } + elem = (ArrayElementList *) visit_next_list(v, (GenericList*) elem, + size); + } + + /* visit_check_list() can only fail for input visitors */ + ok = visit_check_list(v, errp); + assert(ok); + +out_obj: + visit_end_list(v, (void**) &list); + + while (list) { + elem = list; + list = elem->next; + g_free(elem); + } +} + +static void default_prop_array(ObjectProperty *op, const Property *prop) +{ + object_property_set_default_list(op); +} + +const PropertyInfo qdev_prop_array = { + .name = "list", + .get = get_prop_array, + .set = set_prop_array, + .release = release_prop_array, + .set_default_value = default_prop_array, }; /* --- public helpers --- */ @@ -739,6 +849,13 @@ void qdev_prop_set_enum(DeviceState *dev, const char *name, int value) &error_abort); } +void qdev_prop_set_array(DeviceState *dev, const char *name, QList *values) +{ + object_property_set_qobject(OBJECT(dev), name, QOBJECT(values), + &error_abort); + qobject_unref(values); +} + static GPtrArray *global_props(void) { static GPtrArray *gp; @@ -959,16 +1076,18 @@ void device_class_set_props(DeviceClass *dc, Property *props) void qdev_alias_all_properties(DeviceState *target, Object *source) { ObjectClass *class; - Property *prop; + ObjectPropertyIterator iter; + ObjectProperty *prop; class = object_get_class(OBJECT(target)); - do { - DeviceClass *dc = DEVICE_CLASS(class); - for (prop = dc->props_; prop && prop->name; prop++) { - object_property_add_alias(source, prop->name, - OBJECT(target), prop->name); + object_class_property_iter_init(&iter, class); + while ((prop = object_property_iter_next(&iter))) { + if (object_property_find(source, prop->name)) { + continue; /* skip duplicate properties */ } - class = object_class_get_parent(class); - } while (class != object_class_by_name(TYPE_DEVICE)); + + object_property_add_alias(source, prop->name, + OBJECT(target), prop->name); + } } diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 84f3019440..c68d0f7c51 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -147,8 +147,21 @@ bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp) DeviceState *qdev_new(const char *name) { - if (!object_class_by_name(name)) { - module_load_qom_one(name); + ObjectClass *oc = object_class_by_name(name); +#ifdef CONFIG_MODULES + if (!oc) { + int rv = module_load_qom(name, &error_fatal); + if (rv > 0) { + oc = object_class_by_name(name); + } else { + error_report("could not find a module for type '%s'", name); + exit(1); + } + } +#endif + if (!oc) { + error_report("unknown type '%s'", name); + abort(); } return DEVICE(object_new(name)); } @@ -237,60 +250,6 @@ void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, dev->alias_required_for_version = required_for_version; } -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_legacy_reset(dev); - - return 0; -} - -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); - } - return 0; -} - -void qdev_reset_all(DeviceState *dev) -{ - 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) -{ - qdev_reset_all(DEVICE(opaque)); -} - -void qbus_reset_all(BusState *bus) -{ - 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) -{ - BusState *bus = opaque; - qbus_reset_all(bus); -} - void device_cold_reset(DeviceState *dev) { resettable_reset(OBJECT(dev), RESET_TYPE_COLD); @@ -371,7 +330,7 @@ bool qdev_machine_modified(void) return qdev_hot_added || qdev_hot_removed; } -BusState *qdev_get_parent_bus(DeviceState *dev) +BusState *qdev_get_parent_bus(const DeviceState *dev) { return dev->parent_bus; } @@ -468,6 +427,26 @@ char *qdev_get_dev_path(DeviceState *dev) return NULL; } +void qdev_add_unplug_blocker(DeviceState *dev, Error *reason) +{ + dev->unplug_blockers = g_slist_prepend(dev->unplug_blockers, reason); +} + +void qdev_del_unplug_blocker(DeviceState *dev, Error *reason) +{ + dev->unplug_blockers = g_slist_remove(dev->unplug_blockers, reason); +} + +bool qdev_unplug_blocked(DeviceState *dev, Error **errp) +{ + if (dev->unplug_blockers) { + error_propagate(errp, error_copy(dev->unplug_blockers->data)); + return true; + } + + return false; +} + static bool device_get_realized(Object *obj, Error **errp) { DeviceState *dev = DEVICE(obj); @@ -704,6 +683,8 @@ static void device_finalize(Object *obj) DeviceState *dev = DEVICE(obj); + g_assert(!dev->unplug_blockers); + QLIST_FOREACH_SAFE(ngl, &dev->gpios, node, next) { QLIST_REMOVE(ngl, node); qemu_free_irqs(ngl->in, ngl->num_in); @@ -720,7 +701,7 @@ static void device_finalize(Object *obj) if (dev->pending_deleted_event) { g_assert(dev->canonical_path); - qapi_event_send_device_deleted(!!dev->id, dev->id, dev->canonical_path); + qapi_event_send_device_deleted(dev->id, dev->canonical_path); g_free(dev->canonical_path); dev->canonical_path = NULL; } @@ -887,16 +868,6 @@ void device_class_set_parent_unrealize(DeviceClass *dc, dc->unrealize = dev_unrealize; } -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); - } -} - Object *qdev_get_machine(void) { static Object *dev; @@ -908,6 +879,14 @@ Object *qdev_get_machine(void) return dev; } +char *qdev_get_human_name(DeviceState *dev) +{ + g_assert(dev != NULL); + + return dev->id ? + g_strdup(dev->id) : object_get_canonical_path(OBJECT(dev)); +} + static MachineInitPhase machine_phase; bool phase_check(MachineInitPhase phase) diff --git a/hw/core/reset.c b/hw/core/reset.c index 36be82c491..d50da7e304 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -24,49 +24,164 @@ */ #include "qemu/osdep.h" -#include "qemu/queue.h" #include "sysemu/reset.h" +#include "hw/resettable.h" +#include "hw/core/resetcontainer.h" -/* reset/shutdown handler */ +/* + * Return a pointer to the singleton container that holds all the Resettable + * items that will be reset when qemu_devices_reset() is called. + */ +static ResettableContainer *get_root_reset_container(void) +{ + static ResettableContainer *root_reset_container; -typedef struct QEMUResetEntry { - QTAILQ_ENTRY(QEMUResetEntry) entry; + if (!root_reset_container) { + root_reset_container = + RESETTABLE_CONTAINER(object_new(TYPE_RESETTABLE_CONTAINER)); + } + return root_reset_container; +} + +/* + * Reason why the currently in-progress qemu_devices_reset() was called. + * If we made at least SHUTDOWN_CAUSE_SNAPSHOT_LOAD have a corresponding + * ResetType we could perhaps avoid the need for this global. + */ +static ShutdownCause device_reset_reason; + +/* + * This is an Object which implements Resettable simply to call the + * callback function in the hold phase. + */ +#define TYPE_LEGACY_RESET "legacy-reset" +OBJECT_DECLARE_SIMPLE_TYPE(LegacyReset, LEGACY_RESET) + +struct LegacyReset { + Object parent; + ResettableState reset_state; QEMUResetHandler *func; void *opaque; -} QEMUResetEntry; + bool skip_on_snapshot_load; +}; -static QTAILQ_HEAD(, QEMUResetEntry) reset_handlers = - QTAILQ_HEAD_INITIALIZER(reset_handlers); +OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(LegacyReset, legacy_reset, LEGACY_RESET, OBJECT, { TYPE_RESETTABLE_INTERFACE }, { }) + +static ResettableState *legacy_reset_get_state(Object *obj) +{ + LegacyReset *lr = LEGACY_RESET(obj); + return &lr->reset_state; +} + +static void legacy_reset_hold(Object *obj) +{ + LegacyReset *lr = LEGACY_RESET(obj); + + if (device_reset_reason == SHUTDOWN_CAUSE_SNAPSHOT_LOAD && + lr->skip_on_snapshot_load) { + return; + } + lr->func(lr->opaque); +} + +static void legacy_reset_init(Object *obj) +{ +} + +static void legacy_reset_finalize(Object *obj) +{ +} + +static void legacy_reset_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->get_state = legacy_reset_get_state; + rc->phases.hold = legacy_reset_hold; +} void qemu_register_reset(QEMUResetHandler *func, void *opaque) { - QEMUResetEntry *re = g_new0(QEMUResetEntry, 1); + Object *obj = object_new(TYPE_LEGACY_RESET); + LegacyReset *lr = LEGACY_RESET(obj); - re->func = func; - re->opaque = opaque; - QTAILQ_INSERT_TAIL(&reset_handlers, re, entry); + lr->func = func; + lr->opaque = opaque; + qemu_register_resettable(obj); +} + +void qemu_register_reset_nosnapshotload(QEMUResetHandler *func, void *opaque) +{ + Object *obj = object_new(TYPE_LEGACY_RESET); + LegacyReset *lr = LEGACY_RESET(obj); + + lr->func = func; + lr->opaque = opaque; + lr->skip_on_snapshot_load = true; + qemu_register_resettable(obj); +} + +typedef struct FindLegacyInfo { + QEMUResetHandler *func; + void *opaque; + LegacyReset *lr; +} FindLegacyInfo; + +static void find_legacy_reset_cb(Object *obj, void *opaque, ResetType type) +{ + LegacyReset *lr; + FindLegacyInfo *fli = opaque; + + /* Not everything in the ResettableContainer will be a LegacyReset */ + lr = LEGACY_RESET(object_dynamic_cast(obj, TYPE_LEGACY_RESET)); + if (lr && lr->func == fli->func && lr->opaque == fli->opaque) { + fli->lr = lr; + } +} + +static LegacyReset *find_legacy_reset(QEMUResetHandler *func, void *opaque) +{ + /* + * Find the LegacyReset with the specified func and opaque, + * by getting the ResettableContainer to call our callback for + * every item in it. + */ + ResettableContainer *rootcon = get_root_reset_container(); + ResettableClass *rc = RESETTABLE_GET_CLASS(rootcon); + FindLegacyInfo fli; + + fli.func = func; + fli.opaque = opaque; + fli.lr = NULL; + rc->child_foreach(OBJECT(rootcon), find_legacy_reset_cb, + &fli, RESET_TYPE_COLD); + return fli.lr; } void qemu_unregister_reset(QEMUResetHandler *func, void *opaque) { - QEMUResetEntry *re; + Object *obj = OBJECT(find_legacy_reset(func, opaque)); - QTAILQ_FOREACH(re, &reset_handlers, entry) { - if (re->func == func && re->opaque == opaque) { - QTAILQ_REMOVE(&reset_handlers, re, entry); - g_free(re); - return; - } + if (obj) { + qemu_unregister_resettable(obj); + object_unref(obj); } } -void qemu_devices_reset(void) +void qemu_register_resettable(Object *obj) { - QEMUResetEntry *re, *nre; - - /* reset all devices */ - QTAILQ_FOREACH_SAFE(re, &reset_handlers, entry, nre) { - re->func(re->opaque); - } + resettable_container_add(get_root_reset_container(), obj); } +void qemu_unregister_resettable(Object *obj) +{ + resettable_container_remove(get_root_reset_container(), obj); +} + +void qemu_devices_reset(ShutdownCause reason) +{ + device_reset_reason = reason; + + /* Reset the simulation */ + resettable_reset(OBJECT(get_root_reset_container()), RESET_TYPE_COLD); +} diff --git a/hw/core/resetcontainer.c b/hw/core/resetcontainer.c new file mode 100644 index 0000000000..e4ece68e83 --- /dev/null +++ b/hw/core/resetcontainer.c @@ -0,0 +1,77 @@ +/* + * Reset container + * + * Copyright (c) 2024 Linaro, Ltd + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * The "reset container" is an object which implements the Resettable + * interface. It contains a list of arbitrary other objects which also + * implement Resettable. Resetting the reset container resets all the + * objects in it. + */ + +#include "qemu/osdep.h" +#include "hw/resettable.h" +#include "hw/core/resetcontainer.h" + +struct ResettableContainer { + Object parent; + ResettableState reset_state; + GPtrArray *children; +}; + +OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(ResettableContainer, resettable_container, RESETTABLE_CONTAINER, OBJECT, { TYPE_RESETTABLE_INTERFACE }, { }) + +void resettable_container_add(ResettableContainer *rc, Object *obj) +{ + INTERFACE_CHECK(void, obj, TYPE_RESETTABLE_INTERFACE); + g_ptr_array_add(rc->children, obj); +} + +void resettable_container_remove(ResettableContainer *rc, Object *obj) +{ + g_ptr_array_remove(rc->children, obj); +} + +static ResettableState *resettable_container_get_state(Object *obj) +{ + ResettableContainer *rc = RESETTABLE_CONTAINER(obj); + return &rc->reset_state; +} + +static void resettable_container_child_foreach(Object *obj, + ResettableChildCallback cb, + void *opaque, ResetType type) +{ + ResettableContainer *rc = RESETTABLE_CONTAINER(obj); + unsigned int len = rc->children->len; + + for (unsigned int i = 0; i < len; i++) { + cb(g_ptr_array_index(rc->children, i), opaque, type); + /* Detect callbacks trying to unregister themselves */ + assert(len == rc->children->len); + } +} + +static void resettable_container_init(Object *obj) +{ + ResettableContainer *rc = RESETTABLE_CONTAINER(obj); + + rc->children = g_ptr_array_new(); +} + +static void resettable_container_finalize(Object *obj) +{ +} + +static void resettable_container_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->get_state = resettable_container_get_state; + rc->child_foreach = resettable_container_child_foreach; +} diff --git a/hw/core/resettable.c b/hw/core/resettable.c index 96a99ce39e..c3df75c6ba 100644 --- a/hw/core/resettable.c +++ b/hw/core/resettable.c @@ -201,12 +201,11 @@ static void resettable_phase_exit(Object *obj, void *opaque, ResetType type) resettable_child_foreach(rc, obj, resettable_phase_exit, NULL, type); assert(s->count > 0); - if (s->count == 1) { + if (--s->count == 0) { 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); diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index 19d22cbe73..eebcd28f9a 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -299,7 +299,8 @@ static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque) void *guest_fdt = data->fdt, *host_fdt; const void *r; int i, prop_len; - uint32_t *irq_attr, *reg_attr, *host_clock_phandles; + uint32_t *irq_attr, *reg_attr; + const uint32_t *host_clock_phandles; uint64_t mmio_base, irq_number; uint32_t guest_clock_phandles[2]; @@ -339,7 +340,7 @@ static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque) error_report("%s clocks property should contain 2 handles", __func__); exit(1); } - host_clock_phandles = (uint32_t *)r; + host_clock_phandles = r; guest_clock_phandles[0] = qemu_fdt_alloc_phandle(guest_fdt); guest_clock_phandles[1] = qemu_fdt_alloc_phandle(guest_fdt); @@ -539,7 +540,7 @@ void platform_bus_add_all_fdt_nodes(void *fdt, const char *intc, hwaddr addr, assert(fdt); - node = g_strdup_printf("/platform@%"PRIx64, addr); + node = g_strdup_printf("/platform-bus@%"PRIx64, addr); /* Create a /platform node that we can put all devices into */ qemu_fdt_add_subnode(fdt, node); diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 05c1da3d31..ad34fb7344 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -269,7 +269,7 @@ static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent) for (i = 0; i < s->num_mmio; i++) { size = memory_region_size(s->mmio[i].memory); - monitor_printf(mon, "%*smmio " TARGET_FMT_plx "/" TARGET_FMT_plx "\n", + monitor_printf(mon, "%*smmio " HWADDR_FMT_plx "/" HWADDR_FMT_plx "\n", indent, "", s->mmio[i].addr, size); } } @@ -289,7 +289,7 @@ static char *sysbus_get_fw_dev_path(DeviceState *dev) } } if (s->num_mmio) { - return g_strdup_printf("%s@" TARGET_FMT_plx, qdev_fw_name(dev), + return g_strdup_printf("%s@" HWADDR_FMT_plx, qdev_fw_name(dev), s->mmio[0].addr); } if (s->num_pio) { @@ -298,17 +298,6 @@ static char *sysbus_get_fw_dev_path(DeviceState *dev) return g_strdup(qdev_fw_name(dev)); } -void sysbus_add_io(SysBusDevice *dev, hwaddr addr, - MemoryRegion *mem) -{ - memory_region_add_subregion(get_system_io(), addr, mem); -} - -MemoryRegion *sysbus_address_space(SysBusDevice *dev) -{ - return get_system_memory(); -} - static void sysbus_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); diff --git a/hw/core/trace-events b/hw/core/trace-events index 9b3ecce3b2..2cf085ac66 100644 --- a/hw/core/trace-events +++ b/hw/core/trace-events @@ -2,12 +2,6 @@ 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 @@ -35,3 +29,6 @@ clock_set(const char *clk, uint64_t old, uint64_t new) "'%s', %"PRIu64"Hz->%"PRI clock_propagate(const char *clk) "'%s'" clock_update(const char *clk, const char *src, uint64_t hz, int cb) "'%s', src='%s', val=%"PRIu64"Hz cb=%d" clock_set_mul_div(const char *clk, uint32_t oldmul, uint32_t mul, uint32_t olddiv, uint32_t div) "'%s', mul: %u -> %u, div: %u -> %u" + +# cpu-common.c +cpu_reset(int cpu_index) "%d" diff --git a/hw/core/vm-change-state-handler.c b/hw/core/vm-change-state-handler.c index 1f3630986d..8e2639224e 100644 --- a/hw/core/vm-change-state-handler.c +++ b/hw/core/vm-change-state-handler.c @@ -55,8 +55,20 @@ static int qdev_get_dev_tree_depth(DeviceState *dev) VMChangeStateEntry *qdev_add_vm_change_state_handler(DeviceState *dev, VMChangeStateHandler *cb, void *opaque) +{ + return qdev_add_vm_change_state_handler_full(dev, cb, NULL, opaque); +} + +/* + * Exactly like qdev_add_vm_change_state_handler() but passes a prepare_cb + * argument too. + */ +VMChangeStateEntry *qdev_add_vm_change_state_handler_full( + DeviceState *dev, VMChangeStateHandler *cb, + VMChangeStateHandler *prepare_cb, void *opaque) { int depth = qdev_get_dev_tree_depth(dev); - return qemu_add_vm_change_state_handler_prio(cb, opaque, depth); + return qemu_add_vm_change_state_handler_prio_full(cb, prepare_cb, opaque, + depth); } diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index 774ca9987a..967d8d3dd5 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -26,6 +26,7 @@ #include "hw/qdev-properties.h" #include "sysemu/kvm.h" #include "kvm_arm.h" +#include "target/arm/gtimer.h" static void a15mp_priv_set_irq(void *opaque, int irq, int level) { @@ -161,7 +162,7 @@ static void a15mp_priv_class_init(ObjectClass *klass, void *data) dc->realize = a15mp_priv_realize; device_class_set_props(dc, a15mp_priv_properties); - /* We currently have no savable state */ + /* We currently have no saveable state */ } static const TypeInfo a15mp_priv_info = { diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index d03f57e579..c30ef72c66 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -15,7 +15,7 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/core/cpu.h" -#include "cpu.h" +#include "target/arm/cpu-qom.h" #define A9_GIC_NUM_PRIORITY_BITS 5 diff --git a/hw/cpu/cluster.c b/hw/cpu/cluster.c index e444b7c29d..61289a840d 100644 --- a/hw/cpu/cluster.c +++ b/hw/cpu/cluster.c @@ -19,12 +19,11 @@ */ #include "qemu/osdep.h" + +#include "hw/core/cpu.h" #include "hw/cpu/cluster.h" #include "hw/qdev-properties.h" -#include "hw/core/cpu.h" #include "qapi/error.h" -#include "qemu/module.h" -#include "qemu/cutils.h" static Property cpu_cluster_properties[] = { DEFINE_PROP_UINT32("cluster-id", CPUClusterState, cluster_id, 0), diff --git a/hw/cpu/core.c b/hw/cpu/core.c index 9876075155..495a5c30ff 100644 --- a/hw/cpu/core.c +++ b/hw/cpu/core.c @@ -8,12 +8,11 @@ */ #include "qemu/osdep.h" -#include "hw/cpu/core.h" -#include "qapi/visitor.h" -#include "qemu/module.h" -#include "qapi/error.h" -#include "sysemu/cpus.h" + #include "hw/boards.h" +#include "hw/cpu/core.h" +#include "qapi/error.h" +#include "qapi/visitor.h" static void core_prop_get_core_id(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) diff --git a/hw/cpu/meson.build b/hw/cpu/meson.build index 9e52fee9e7..38cdcfbe57 100644 --- a/hw/cpu/meson.build +++ b/hw/cpu/meson.build @@ -1,6 +1,6 @@ -softmmu_ss.add(files('core.c', 'cluster.c')) +system_ss.add(files('core.c', 'cluster.c')) -specific_ss.add(when: 'CONFIG_ARM11MPCORE', if_true: files('arm11mpcore.c')) -specific_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview_mpcore.c')) -specific_ss.add(when: 'CONFIG_A9MPCORE', if_true: files('a9mpcore.c')) +system_ss.add(when: 'CONFIG_ARM11MPCORE', if_true: files('arm11mpcore.c')) +system_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview_mpcore.c')) +system_ss.add(when: 'CONFIG_A9MPCORE', if_true: files('a9mpcore.c')) specific_ss.add(when: 'CONFIG_A15MPCORE', if_true: files('a15mpcore.c')) diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c index d82050d927..5556634921 100644 --- a/hw/cris/axis_dev88.c +++ b/hw/cris/axis_dev88.c @@ -308,15 +308,14 @@ void axisdev88_init(MachineState *machine) /* Add the two ethernet blocks. */ dma_eth = g_malloc0(sizeof dma_eth[0] * 4); /* Allocate 4 channels. */ - etraxfs_eth_init(&nd_table[0], 0x30034000, 1, &dma_eth[0], &dma_eth[1]); - if (nb_nics > 1) { - etraxfs_eth_init(&nd_table[1], 0x30036000, 2, &dma_eth[2], &dma_eth[3]); - } + etraxfs_eth_init(0x30034000, 1, &dma_eth[0], &dma_eth[1]); /* The DMA Connector block is missing, hardwire things for now. */ etraxfs_dmac_connect_client(etraxfs_dmac, 0, &dma_eth[0]); etraxfs_dmac_connect_client(etraxfs_dmac, 1, &dma_eth[1]); - if (nb_nics > 1) { + + if (qemu_find_nic_info("etraxfs-eth", true, "fseth")) { + etraxfs_eth_init(0x30036000, 2, &dma_eth[2], &dma_eth[3]); etraxfs_dmac_connect_client(etraxfs_dmac, 6, &dma_eth[2]); etraxfs_dmac_connect_client(etraxfs_dmac, 7, &dma_eth[3]); } diff --git a/hw/cxl/cxl-cdat.c b/hw/cxl/cxl-cdat.c new file mode 100644 index 0000000000..551545f782 --- /dev/null +++ b/hw/cxl/cxl-cdat.c @@ -0,0 +1,220 @@ +/* + * CXL CDAT Structure + * + * Copyright (C) 2021 Avery Design Systems, 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/pci/pci.h" +#include "hw/cxl/cxl.h" +#include "qapi/error.h" +#include "qemu/error-report.h" + +static void cdat_len_check(CDATSubHeader *hdr, Error **errp) +{ + assert(hdr->length); + assert(hdr->reserved == 0); + + switch (hdr->type) { + case CDAT_TYPE_DSMAS: + assert(hdr->length == sizeof(CDATDsmas)); + break; + case CDAT_TYPE_DSLBIS: + assert(hdr->length == sizeof(CDATDslbis)); + break; + case CDAT_TYPE_DSMSCIS: + assert(hdr->length == sizeof(CDATDsmscis)); + break; + case CDAT_TYPE_DSIS: + assert(hdr->length == sizeof(CDATDsis)); + break; + case CDAT_TYPE_DSEMTS: + assert(hdr->length == sizeof(CDATDsemts)); + break; + case CDAT_TYPE_SSLBIS: + assert(hdr->length >= sizeof(CDATSslbisHeader)); + assert((hdr->length - sizeof(CDATSslbisHeader)) % + sizeof(CDATSslbe) == 0); + break; + default: + error_setg(errp, "Type %d is reserved", hdr->type); + } +} + +static void ct3_build_cdat(CDATObject *cdat, Error **errp) +{ + g_autofree CDATTableHeader *cdat_header = NULL; + g_autofree CDATEntry *cdat_st = NULL; + uint8_t sum = 0; + uint8_t *hdr_buf; + int ent, i; + + /* Use default table if fopen == NULL */ + assert(cdat->build_cdat_table); + + cdat_header = g_malloc0(sizeof(*cdat_header)); + if (!cdat_header) { + error_setg(errp, "Failed to allocate CDAT header"); + return; + } + + cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf, + cdat->private); + + if (cdat->built_buf_len <= 0) { + /* Build later as not all data available yet */ + cdat->to_update = true; + return; + } + cdat->to_update = false; + + cdat_st = g_malloc0(sizeof(*cdat_st) * (cdat->built_buf_len + 1)); + if (!cdat_st) { + error_setg(errp, "Failed to allocate CDAT entry array"); + return; + } + + /* Entry 0 for CDAT header, starts with Entry 1 */ + for (ent = 1; ent < cdat->built_buf_len + 1; ent++) { + CDATSubHeader *hdr = cdat->built_buf[ent - 1]; + uint8_t *buf = (uint8_t *)cdat->built_buf[ent - 1]; + + cdat_st[ent].base = hdr; + cdat_st[ent].length = hdr->length; + + cdat_header->length += hdr->length; + for (i = 0; i < hdr->length; i++) { + sum += buf[i]; + } + } + + /* CDAT header */ + cdat_header->revision = CXL_CDAT_REV; + /* For now, no runtime updates */ + cdat_header->sequence = 0; + cdat_header->length += sizeof(CDATTableHeader); + + hdr_buf = (uint8_t *)cdat_header; + for (i = 0; i < sizeof(*cdat_header); i++) { + sum += hdr_buf[i]; + } + + /* Sum of all bytes including checksum must be 0 */ + cdat_header->checksum = ~sum + 1; + + cdat_st[0].base = g_steal_pointer(&cdat_header); + cdat_st[0].length = sizeof(*cdat_header); + cdat->entry_len = 1 + cdat->built_buf_len; + cdat->entry = g_steal_pointer(&cdat_st); +} + +static void ct3_load_cdat(CDATObject *cdat, Error **errp) +{ + g_autofree CDATEntry *cdat_st = NULL; + g_autofree uint8_t *buf = NULL; + uint8_t sum = 0; + int num_ent; + int i = 0, ent = 1; + gsize file_size = 0; + CDATSubHeader *hdr; + GError *error = NULL; + + /* Read CDAT file and create its cache */ + if (!g_file_get_contents(cdat->filename, (gchar **)&buf, + &file_size, &error)) { + error_setg(errp, "CDAT: File read failed: %s", error->message); + g_error_free(error); + return; + } + if (file_size < sizeof(CDATTableHeader)) { + error_setg(errp, "CDAT: File too short"); + return; + } + i = sizeof(CDATTableHeader); + num_ent = 1; + while (i < file_size) { + hdr = (CDATSubHeader *)(buf + i); + if (i + sizeof(CDATSubHeader) > file_size) { + error_setg(errp, "CDAT: Truncated table"); + return; + } + cdat_len_check(hdr, errp); + i += hdr->length; + if (i > file_size) { + error_setg(errp, "CDAT: Truncated table"); + return; + } + num_ent++; + } + if (i != file_size) { + error_setg(errp, "CDAT: File length mismatch"); + return; + } + + cdat_st = g_new0(CDATEntry, num_ent); + + /* Set CDAT header, Entry = 0 */ + cdat_st[0].base = buf; + cdat_st[0].length = sizeof(CDATTableHeader); + i = 0; + + while (i < cdat_st[0].length) { + sum += buf[i++]; + } + + /* Read CDAT structures */ + while (i < file_size) { + hdr = (CDATSubHeader *)(buf + i); + cdat_st[ent].base = hdr; + cdat_st[ent].length = hdr->length; + + while (buf + i < (uint8_t *)cdat_st[ent].base + cdat_st[ent].length) { + assert(i < file_size); + sum += buf[i++]; + } + + ent++; + } + + if (sum != 0) { + warn_report("CDAT: Found checksum mismatch in %s", cdat->filename); + } + cdat->entry_len = num_ent; + cdat->entry = g_steal_pointer(&cdat_st); + cdat->buf = g_steal_pointer(&buf); +} + +void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp) +{ + CDATObject *cdat = &cxl_cstate->cdat; + + if (cdat->filename) { + ct3_load_cdat(cdat, errp); + } else { + ct3_build_cdat(cdat, errp); + } +} + +void cxl_doe_cdat_update(CXLComponentState *cxl_cstate, Error **errp) +{ + CDATObject *cdat = &cxl_cstate->cdat; + + if (cdat->to_update) { + ct3_build_cdat(cdat, errp); + } +} + +void cxl_doe_cdat_release(CXLComponentState *cxl_cstate) +{ + CDATObject *cdat = &cxl_cstate->cdat; + + free(cdat->entry); + if (cdat->built_buf) { + cdat->free_cdat_table(cdat->built_buf, cdat->built_buf_len, + cdat->private); + } + g_free(cdat->buf); +} diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c index 7985c9bfca..cd116c0401 100644 --- a/hw/cxl/cxl-component-utils.c +++ b/hw/cxl/cxl-component-utils.c @@ -13,22 +13,78 @@ #include "hw/pci/pci.h" #include "hw/cxl/cxl.h" +/* CXL r3.1 Section 8.2.4.20.1 CXL HDM Decoder Capability Register */ +int cxl_decoder_count_enc(int count) +{ + switch (count) { + case 1: return 0x0; + case 2: return 0x1; + case 4: return 0x2; + case 6: return 0x3; + case 8: return 0x4; + case 10: return 0x5; + /* Switches and Host Bridges may have more than 10 decoders */ + case 12: return 0x6; + case 14: return 0x7; + case 16: return 0x8; + case 20: return 0x9; + case 24: return 0xa; + case 28: return 0xb; + case 32: return 0xc; + } + return 0; +} + +int cxl_decoder_count_dec(int enc_cnt) +{ + switch (enc_cnt) { + case 0x0: return 1; + case 0x1: return 2; + case 0x2: return 4; + case 0x3: return 6; + case 0x4: return 8; + case 0x5: return 10; + /* Switches and Host Bridges may have more than 10 decoders */ + case 0x6: return 12; + case 0x7: return 14; + case 0x8: return 16; + case 0x9: return 20; + case 0xa: return 24; + case 0xb: return 28; + case 0xc: return 32; + } + return 0; +} + +hwaddr cxl_decode_ig(int ig) +{ + return 1ULL << (ig + 8); +} + static uint64_t cxl_cache_mem_read_reg(void *opaque, hwaddr offset, unsigned size) { CXLComponentState *cxl_cstate = opaque; ComponentRegisters *cregs = &cxl_cstate->crb; - if (size == 8) { + switch (size) { + case 4: + if (cregs->special_ops && cregs->special_ops->read) { + return cregs->special_ops->read(cxl_cstate, offset, 4); + } else { + QEMU_BUILD_BUG_ON(sizeof(*cregs->cache_mem_registers) != 4); + return cregs->cache_mem_registers[offset / 4]; + } + case 8: qemu_log_mask(LOG_UNIMP, "CXL 8 byte cache mem registers not implemented\n"); return 0; - } - - if (cregs->special_ops && cregs->special_ops->read) { - return cregs->special_ops->read(cxl_cstate, offset, size); - } else { - return cregs->cache_mem_registers[offset / sizeof(*cregs->cache_mem_registers)]; + default: + /* + * In line with specification limitaions on access sizes, this + * routine is not called with other sizes. + */ + g_assert_not_reached(); } } @@ -38,23 +94,28 @@ static void dumb_hdm_handler(CXLComponentState *cxl_cstate, hwaddr offset, ComponentRegisters *cregs = &cxl_cstate->crb; uint32_t *cache_mem = cregs->cache_mem_registers; bool should_commit = false; + bool should_uncommit = false; switch (offset) { case A_CXL_HDM_DECODER0_CTRL: + case A_CXL_HDM_DECODER1_CTRL: + case A_CXL_HDM_DECODER2_CTRL: + case A_CXL_HDM_DECODER3_CTRL: should_commit = FIELD_EX32(value, CXL_HDM_DECODER0_CTRL, COMMIT); + should_uncommit = !should_commit; break; default: break; } - memory_region_transaction_begin(); - stl_le_p((uint8_t *)cache_mem + offset, value); if (should_commit) { - ARRAY_FIELD_DP32(cache_mem, CXL_HDM_DECODER0_CTRL, COMMIT, 0); - ARRAY_FIELD_DP32(cache_mem, CXL_HDM_DECODER0_CTRL, ERR, 0); - ARRAY_FIELD_DP32(cache_mem, CXL_HDM_DECODER0_CTRL, COMMITTED, 1); + value = FIELD_DP32(value, CXL_HDM_DECODER0_CTRL, ERR, 0); + value = FIELD_DP32(value, CXL_HDM_DECODER0_CTRL, COMMITTED, 1); + } else if (should_uncommit) { + value = FIELD_DP32(value, CXL_HDM_DECODER0_CTRL, ERR, 0); + value = FIELD_DP32(value, CXL_HDM_DECODER0_CTRL, COMMITTED, 0); } - memory_region_transaction_commit(); + stl_le_p((uint8_t *)cache_mem + offset, value); } static void cxl_cache_mem_write_reg(void *opaque, hwaddr offset, uint64_t value, @@ -64,34 +125,46 @@ static void cxl_cache_mem_write_reg(void *opaque, hwaddr offset, uint64_t value, ComponentRegisters *cregs = &cxl_cstate->crb; uint32_t mask; - if (size == 8) { + switch (size) { + case 4: { + QEMU_BUILD_BUG_ON(sizeof(*cregs->cache_mem_regs_write_mask) != 4); + QEMU_BUILD_BUG_ON(sizeof(*cregs->cache_mem_registers) != 4); + mask = cregs->cache_mem_regs_write_mask[offset / 4]; + value &= mask; + /* RO bits should remain constant. Done by reading existing value */ + value |= ~mask & cregs->cache_mem_registers[offset / 4]; + if (cregs->special_ops && cregs->special_ops->write) { + cregs->special_ops->write(cxl_cstate, offset, value, size); + return; + } + + if (offset >= A_CXL_HDM_DECODER_CAPABILITY && + offset <= A_CXL_HDM_DECODER3_TARGET_LIST_HI) { + dumb_hdm_handler(cxl_cstate, offset, value); + } else { + cregs->cache_mem_registers[offset / 4] = value; + } + return; + } + case 8: qemu_log_mask(LOG_UNIMP, "CXL 8 byte cache mem registers not implemented\n"); return; - } - mask = cregs->cache_mem_regs_write_mask[offset / sizeof(*cregs->cache_mem_regs_write_mask)]; - value &= mask; - /* RO bits should remain constant. Done by reading existing value */ - value |= ~mask & cregs->cache_mem_registers[offset / sizeof(*cregs->cache_mem_registers)]; - if (cregs->special_ops && cregs->special_ops->write) { - cregs->special_ops->write(cxl_cstate, offset, value, size); - return; - } - - if (offset >= A_CXL_HDM_DECODER_CAPABILITY && - offset <= A_CXL_HDM_DECODER0_TARGET_LIST_HI) { - dumb_hdm_handler(cxl_cstate, offset, value); - } else { - cregs->cache_mem_registers[offset / sizeof(*cregs->cache_mem_registers)] = value; + default: + /* + * In line with specification limitaions on access sizes, this + * routine is not called with other sizes. + */ + g_assert_not_reached(); } } /* - * 8.2.3 + * CXL r3.1 Section 8.2.3: Component Register Layout and Definition * The access restrictions specified in Section 8.2.2 also apply to CXL 2.0 * Component Registers. * - * 8.2.2 + * CXL r3.1 Section 8.2.2: Accessing Component Registers * • A 32 bit register shall be accessed as a 4 Bytes quantity. Partial * reads are not permitted. * • A 64 bit register shall be accessed as a 8 Bytes quantity. Partial @@ -124,9 +197,9 @@ void cxl_component_register_block_init(Object *obj, CXL2_COMPONENT_BLOCK_SIZE); /* io registers controls link which we don't care about in QEMU */ - memory_region_init_io(&cregs->io, obj, NULL, cregs, ".io", + memory_region_init_io(&cregs->io, obj, NULL, NULL, ".io", CXL2_COMPONENT_IO_REGION_SIZE); - memory_region_init_io(&cregs->cache_mem, obj, &cache_mem_ops, cregs, + memory_region_init_io(&cregs->cache_mem, obj, &cache_mem_ops, cxl_cstate, ".cache_mem", CXL2_COMPONENT_CM_REGION_SIZE); memory_region_add_subregion(&cregs->component_registers, 0, &cregs->io); @@ -141,22 +214,26 @@ static void ras_init_common(uint32_t *reg_state, uint32_t *write_msk) * Error status is RW1C but given bits are not yet set, it can * be handled as RO. */ - reg_state[R_CXL_RAS_UNC_ERR_STATUS] = 0; + stl_le_p(reg_state + R_CXL_RAS_UNC_ERR_STATUS, 0); + stl_le_p(write_msk + R_CXL_RAS_UNC_ERR_STATUS, 0x1cfff); /* Bits 12-13 and 17-31 reserved in CXL 2.0 */ - reg_state[R_CXL_RAS_UNC_ERR_MASK] = 0x1cfff; - write_msk[R_CXL_RAS_UNC_ERR_MASK] = 0x1cfff; - reg_state[R_CXL_RAS_UNC_ERR_SEVERITY] = 0x1cfff; - write_msk[R_CXL_RAS_UNC_ERR_SEVERITY] = 0x1cfff; - reg_state[R_CXL_RAS_COR_ERR_STATUS] = 0; - reg_state[R_CXL_RAS_COR_ERR_MASK] = 0x7f; - write_msk[R_CXL_RAS_COR_ERR_MASK] = 0x7f; + stl_le_p(reg_state + R_CXL_RAS_UNC_ERR_MASK, 0x1cfff); + stl_le_p(write_msk + R_CXL_RAS_UNC_ERR_MASK, 0x1cfff); + stl_le_p(reg_state + R_CXL_RAS_UNC_ERR_SEVERITY, 0x1cfff); + stl_le_p(write_msk + R_CXL_RAS_UNC_ERR_SEVERITY, 0x1cfff); + stl_le_p(reg_state + R_CXL_RAS_COR_ERR_STATUS, 0); + stl_le_p(write_msk + R_CXL_RAS_COR_ERR_STATUS, 0x7f); + stl_le_p(reg_state + R_CXL_RAS_COR_ERR_MASK, 0x7f); + stl_le_p(write_msk + R_CXL_RAS_COR_ERR_MASK, 0x7f); /* CXL switches and devices must set */ - reg_state[R_CXL_RAS_ERR_CAP_CTRL] = 0x00; + stl_le_p(reg_state + R_CXL_RAS_ERR_CAP_CTRL, 0x200); } -static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk) +static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk, + enum reg_type type) { - int decoder_count = 1; + int decoder_count = CXL_HDM_DECODER_COUNT; + int hdm_inc = R_CXL_HDM_DECODER1_BASE_LO - R_CXL_HDM_DECODER0_BASE_LO; int i; ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, DECODER_COUNT, @@ -164,28 +241,48 @@ static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk) ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, TARGET_COUNT, 1); ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, INTERLEAVE_256B, 1); ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, INTERLEAVE_4K, 1); - ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, POISON_ON_ERR_CAP, 0); + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, + POISON_ON_ERR_CAP, 0); + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 3_6_12_WAY, 0); + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 16_WAY, 0); + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, UIO, 0); + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, + UIO_DECODER_COUNT, 0); + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, MEMDATA_NXM_CAP, 0); + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, + SUPPORTED_COHERENCY_MODEL, 0); /* Unknown */ ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_GLOBAL_CONTROL, HDM_DECODER_ENABLE, 0); write_msk[R_CXL_HDM_DECODER_GLOBAL_CONTROL] = 0x3; for (i = 0; i < decoder_count; i++) { - write_msk[R_CXL_HDM_DECODER0_BASE_LO + i * 0x20] = 0xf0000000; - write_msk[R_CXL_HDM_DECODER0_BASE_HI + i * 0x20] = 0xffffffff; - write_msk[R_CXL_HDM_DECODER0_SIZE_LO + i * 0x20] = 0xf0000000; - write_msk[R_CXL_HDM_DECODER0_SIZE_HI + i * 0x20] = 0xffffffff; - write_msk[R_CXL_HDM_DECODER0_CTRL + i * 0x20] = 0x13ff; + write_msk[R_CXL_HDM_DECODER0_BASE_LO + i * hdm_inc] = 0xf0000000; + write_msk[R_CXL_HDM_DECODER0_BASE_HI + i * hdm_inc] = 0xffffffff; + write_msk[R_CXL_HDM_DECODER0_SIZE_LO + i * hdm_inc] = 0xf0000000; + write_msk[R_CXL_HDM_DECODER0_SIZE_HI + i * hdm_inc] = 0xffffffff; + write_msk[R_CXL_HDM_DECODER0_CTRL + i * hdm_inc] = 0x13ff; + if (type == CXL2_DEVICE || + type == CXL2_TYPE3_DEVICE || + type == CXL2_LOGICAL_DEVICE) { + write_msk[R_CXL_HDM_DECODER0_TARGET_LIST_LO + i * hdm_inc] = + 0xf0000000; + } else { + write_msk[R_CXL_HDM_DECODER0_TARGET_LIST_LO + i * hdm_inc] = + 0xffffffff; + } + write_msk[R_CXL_HDM_DECODER0_TARGET_LIST_HI + i * hdm_inc] = 0xffffffff; } } -void cxl_component_register_init_common(uint32_t *reg_state, uint32_t *write_msk, +void cxl_component_register_init_common(uint32_t *reg_state, + uint32_t *write_msk, enum reg_type type) { int caps = 0; /* - * In CXL 2.0 the capabilities required for each CXL component are such that, - * with the ordering chosen here, a single number can be used to define - * which capabilities should be provided. + * In CXL 2.0 the capabilities required for each CXL component are such + * that, with the ordering chosen here, a single number can be used to + * define which capabilities should be provided. */ switch (type) { case CXL2_DOWNSTREAM_PORT: @@ -200,6 +297,7 @@ void cxl_component_register_init_common(uint32_t *reg_state, uint32_t *write_msk caps = 3; break; case CXL2_ROOT_PORT: + case CXL2_RC: /* + Extended Security, + Snoop */ caps = 5; break; @@ -211,12 +309,12 @@ void cxl_component_register_init_common(uint32_t *reg_state, uint32_t *write_msk /* CXL Capability Header Register */ ARRAY_FIELD_DP32(reg_state, CXL_CAPABILITY_HEADER, ID, 1); - ARRAY_FIELD_DP32(reg_state, CXL_CAPABILITY_HEADER, VERSION, 1); + ARRAY_FIELD_DP32(reg_state, CXL_CAPABILITY_HEADER, VERSION, + CXL_CAPABILITY_VERSION); ARRAY_FIELD_DP32(reg_state, CXL_CAPABILITY_HEADER, CACHE_MEM_VERSION, 1); ARRAY_FIELD_DP32(reg_state, CXL_CAPABILITY_HEADER, ARRAY_SIZE, caps); #define init_cap_reg(reg, id, version) \ - QEMU_BUILD_BUG_ON(CXL_##reg##_REGISTERS_OFFSET == 0); \ do { \ int which = R_CXL_##reg##_CAPABILITY_HEADER; \ reg_state[which] = FIELD_DP32(reg_state[which], \ @@ -229,24 +327,36 @@ void cxl_component_register_init_common(uint32_t *reg_state, uint32_t *write_msk CXL_##reg##_REGISTERS_OFFSET); \ } while (0) - init_cap_reg(RAS, 2, 2); - ras_init_common(reg_state, write_msk); + switch (type) { + case CXL2_DEVICE: + case CXL2_TYPE3_DEVICE: + case CXL2_LOGICAL_DEVICE: + case CXL2_ROOT_PORT: + case CXL2_UPSTREAM_PORT: + case CXL2_DOWNSTREAM_PORT: + init_cap_reg(RAS, 2, CXL_RAS_CAPABILITY_VERSION); + ras_init_common(reg_state, write_msk); + break; + default: + break; + } - init_cap_reg(LINK, 4, 2); + init_cap_reg(LINK, 4, CXL_LINK_CAPABILITY_VERSION); if (caps < 3) { return; } - init_cap_reg(HDM, 5, 1); - hdm_init_common(reg_state, write_msk); - + if (type != CXL2_ROOT_PORT) { + init_cap_reg(HDM, 5, CXL_HDM_CAPABILITY_VERSION); + hdm_init_common(reg_state, write_msk, type); + } if (caps < 5) { return; } - init_cap_reg(EXTSEC, 6, 1); - init_cap_reg(SNOOP, 8, 1); + init_cap_reg(EXTSEC, 6, CXL_EXTSEC_CAP_VERSION); + init_cap_reg(SNOOP, 8, CXL_SNOOP_CAP_VERSION); #undef init_cap_reg } @@ -306,26 +416,35 @@ void cxl_component_create_dvsec(CXLComponentState *cxl, case NON_CXL_FUNCTION_MAP_DVSEC: break; /* Not yet implemented */ case EXTENSIONS_PORT_DVSEC: - wmask[offset + offsetof(CXLDVSECPortExtensions, control)] = 0x0F; - wmask[offset + offsetof(CXLDVSECPortExtensions, control) + 1] = 0x40; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_bus_base)] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_bus_limit)] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_base)] = 0xF0; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_base) + 1] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_limit)] = 0xF0; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_limit) + 1] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base)] = 0xF0; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base) + 1] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit)] = 0xF0; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit) + 1] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high)] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high) + 1] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high) + 2] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high) + 3] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high)] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high) + 1] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high) + 2] = 0xFF; - wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high) + 3] = 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, control)] = 0x0F; + wmask[offset + offsetof(CXLDVSECPortExt, control) + 1] = 0x40; + wmask[offset + offsetof(CXLDVSECPortExt, alt_bus_base)] = 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_bus_limit)] = 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_base)] = 0xF0; + wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_base) + 1] = 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_limit)] = 0xF0; + wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_limit) + 1] = 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base)] = 0xF0; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base) + 1] = 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit)] = 0xF0; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit) + 1] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high)] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high) + 1] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high) + 2] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high) + 3] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high)] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high) + 1] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high) + 2] = + 0xFF; + wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high) + 3] = + 0xFF; break; case GPF_PORT_DVSEC: wmask[offset + offsetof(CXLDVSECPortGPF, phase1_ctrl)] = 0x0F; @@ -353,7 +472,7 @@ void cxl_component_create_dvsec(CXLComponentState *cxl, default: /* Registers are RO for other component types */ break; } - /* There are rw1cs bits in the status register but never set currently */ + /* There are rw1cs bits in the status register but never set */ break; } @@ -362,6 +481,7 @@ void cxl_component_create_dvsec(CXLComponentState *cxl, cxl->dvsec_offset += length; } +/* CXL r3.1 Section 8.2.4.20.7 CXL HDM Decoder n Control Register */ uint8_t cxl_interleave_ways_enc(int iw, Error **errp) { switch (iw) { @@ -379,6 +499,23 @@ uint8_t cxl_interleave_ways_enc(int iw, Error **errp) } } +int cxl_interleave_ways_dec(uint8_t iw_enc, Error **errp) +{ + switch (iw_enc) { + case 0x0: return 1; + case 0x1: return 2; + case 0x2: return 4; + case 0x3: return 8; + case 0x4: return 16; + case 0x8: return 3; + case 0x9: return 6; + case 0xa: return 12; + default: + error_setg(errp, "Encoded interleave ways: %d not supported", iw_enc); + return 0; + } +} + uint8_t cxl_interleave_granularity_enc(uint64_t gran, Error **errp) { switch (gran) { diff --git a/hw/cxl/cxl-device-utils.c b/hw/cxl/cxl-device-utils.c index 687759b301..035d034f6d 100644 --- a/hw/cxl/cxl-device-utils.c +++ b/hw/cxl/cxl-device-utils.c @@ -13,7 +13,7 @@ /* * Device registers have no restrictions per the spec, and so fall back to the - * default memory mapped register rules in 8.2: + * default memory mapped register rules in CXL r3.1 Section 8.2: * Software shall use CXL.io Memory Read and Write to access memory mapped * register defined in this section. Unless otherwise specified, software * shall restrict the accesses width based on the following: @@ -32,21 +32,47 @@ static uint64_t caps_reg_read(void *opaque, hwaddr offset, unsigned size) { CXLDeviceState *cxl_dstate = opaque; - if (size == 4) { - return cxl_dstate->caps_reg_state32[offset / sizeof(*cxl_dstate->caps_reg_state32)]; - } else { - return cxl_dstate->caps_reg_state64[offset / sizeof(*cxl_dstate->caps_reg_state64)]; + switch (size) { + case 4: + return cxl_dstate->caps_reg_state32[offset / size]; + case 8: + return cxl_dstate->caps_reg_state64[offset / size]; + default: + g_assert_not_reached(); } } static uint64_t dev_reg_read(void *opaque, hwaddr offset, unsigned size) { - return 0; + CXLDeviceState *cxl_dstate = opaque; + + switch (size) { + case 1: + return cxl_dstate->dev_reg_state[offset]; + case 2: + return cxl_dstate->dev_reg_state16[offset / size]; + case 4: + return cxl_dstate->dev_reg_state32[offset / size]; + case 8: + return cxl_dstate->dev_reg_state64[offset / size]; + default: + g_assert_not_reached(); + } } static uint64_t mailbox_reg_read(void *opaque, hwaddr offset, unsigned size) { - CXLDeviceState *cxl_dstate = opaque; + CXLDeviceState *cxl_dstate; + CXLCCI *cci = opaque; + + if (object_dynamic_cast(OBJECT(cci->intf), TYPE_CXL_TYPE3)) { + cxl_dstate = &CXL_TYPE3(cci->intf)->cxl_dstate; + } else if (object_dynamic_cast(OBJECT(cci->intf), + TYPE_CXL_SWITCH_MAILBOX_CCI)) { + cxl_dstate = &CXL_SWITCH_MAILBOX_CCI(cci->intf)->cxl_dstate; + } else { + return 0; + } switch (size) { case 1: @@ -56,6 +82,25 @@ static uint64_t mailbox_reg_read(void *opaque, hwaddr offset, unsigned size) case 4: return cxl_dstate->mbox_reg_state32[offset / size]; case 8: + if (offset == A_CXL_DEV_BG_CMD_STS) { + uint64_t bg_status_reg; + bg_status_reg = FIELD_DP64(0, CXL_DEV_BG_CMD_STS, OP, + cci->bg.opcode); + bg_status_reg = FIELD_DP64(bg_status_reg, CXL_DEV_BG_CMD_STS, + PERCENTAGE_COMP, cci->bg.complete_pct); + bg_status_reg = FIELD_DP64(bg_status_reg, CXL_DEV_BG_CMD_STS, + RET_CODE, cci->bg.ret_code); + /* endian? */ + cxl_dstate->mbox_reg_state64[offset / size] = bg_status_reg; + } + if (offset == A_CXL_DEV_MAILBOX_STS) { + uint64_t status_reg = cxl_dstate->mbox_reg_state64[offset / size]; + if (cci->bg.complete_pct) { + status_reg = FIELD_DP64(status_reg, CXL_DEV_MAILBOX_STS, BG_OP, + 0); + cxl_dstate->mbox_reg_state64[offset / size] = status_reg; + } + } return cxl_dstate->mbox_reg_state64[offset / size]; default: g_assert_not_reached(); @@ -88,8 +133,7 @@ static void mailbox_mem_writeq(uint64_t *reg_state, hwaddr offset, case A_CXL_DEV_MAILBOX_CMD: break; case A_CXL_DEV_BG_CMD_STS: - /* BG not supported */ - /* fallthrough */ + break; case A_CXL_DEV_MAILBOX_STS: /* Read only register, will get updated by the state machine */ return; @@ -107,7 +151,17 @@ static void mailbox_mem_writeq(uint64_t *reg_state, hwaddr offset, static void mailbox_reg_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { - CXLDeviceState *cxl_dstate = opaque; + CXLDeviceState *cxl_dstate; + CXLCCI *cci = opaque; + + if (object_dynamic_cast(OBJECT(cci->intf), TYPE_CXL_TYPE3)) { + cxl_dstate = &CXL_TYPE3(cci->intf)->cxl_dstate; + } else if (object_dynamic_cast(OBJECT(cci->intf), + TYPE_CXL_SWITCH_MAILBOX_CCI)) { + cxl_dstate = &CXL_SWITCH_MAILBOX_CCI(cci->intf)->cxl_dstate; + } else { + return; + } if (offset >= A_CXL_DEV_CMD_PAYLOAD) { memcpy(cxl_dstate->mbox_reg_state + offset, &value, size); @@ -127,23 +181,68 @@ static void mailbox_reg_write(void *opaque, hwaddr offset, uint64_t value, if (ARRAY_FIELD_EX32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CTRL, DOORBELL)) { - cxl_process_mailbox(cxl_dstate); + uint64_t command_reg = + cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD]; + uint8_t cmd_set = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, + COMMAND_SET); + uint8_t cmd = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND); + size_t len_in = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, LENGTH); + uint8_t *pl = cxl_dstate->mbox_reg_state + A_CXL_DEV_CMD_PAYLOAD; + /* + * Copy taken to avoid need for individual command handlers to care + * about aliasing. + */ + g_autofree uint8_t *pl_in_copy = NULL; + size_t len_out = 0; + uint64_t status_reg; + bool bg_started = false; + int rc; + + pl_in_copy = g_memdup2(pl, len_in); + if (len_in == 0 || pl_in_copy) { + /* Avoid stale data - including from earlier cmds */ + memset(pl, 0, CXL_MAILBOX_MAX_PAYLOAD_SIZE); + rc = cxl_process_cci_message(cci, cmd_set, cmd, len_in, pl_in_copy, + &len_out, pl, &bg_started); + } else { + rc = CXL_MBOX_INTERNAL_ERROR; + } + + /* Set bg and the return code */ + status_reg = FIELD_DP64(0, CXL_DEV_MAILBOX_STS, BG_OP, + bg_started ? 1 : 0); + status_reg = FIELD_DP64(status_reg, CXL_DEV_MAILBOX_STS, ERRNO, rc); + /* Set the return length */ + command_reg = FIELD_DP64(0, CXL_DEV_MAILBOX_CMD, COMMAND_SET, cmd_set); + command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, + COMMAND, cmd); + command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, + LENGTH, len_out); + + cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD] = command_reg; + cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_STS] = status_reg; + /* Tell the host we're done */ + ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CTRL, + DOORBELL, 0); } } static uint64_t mdev_reg_read(void *opaque, hwaddr offset, unsigned size) { - uint64_t retval = 0; + CXLDeviceState *cxl_dstate = opaque; - retval = FIELD_DP64(retval, CXL_MEM_DEV_STS, MEDIA_STATUS, 1); - retval = FIELD_DP64(retval, CXL_MEM_DEV_STS, MBOX_READY, 1); + return cxl_dstate->memdev_status; +} - return retval; +static void ro_reg_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + /* Many register sets are read only */ } static const MemoryRegionOps mdev_ops = { .read = mdev_reg_read, - .write = NULL, /* memory device register is read only */ + .write = ro_reg_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, @@ -173,7 +272,7 @@ static const MemoryRegionOps mailbox_ops = { static const MemoryRegionOps dev_ops = { .read = dev_reg_read, - .write = NULL, /* status register is read only */ + .write = ro_reg_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, @@ -188,7 +287,7 @@ static const MemoryRegionOps dev_ops = { static const MemoryRegionOps caps_ops = { .read = caps_reg_read, - .write = NULL, /* caps registers are read only */ + .write = ro_reg_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, @@ -201,7 +300,8 @@ static const MemoryRegionOps caps_ops = { }, }; -void cxl_device_register_block_init(Object *obj, CXLDeviceState *cxl_dstate) +void cxl_device_register_block_init(Object *obj, CXLDeviceState *cxl_dstate, + CXLCCI *cci) { /* This will be a BAR, so needs to be rounded up to pow2 for PCI spec */ memory_region_init(&cxl_dstate->device_registers, obj, "device-registers", @@ -211,7 +311,7 @@ void cxl_device_register_block_init(Object *obj, CXLDeviceState *cxl_dstate) "cap-array", CXL_CAPS_SIZE); memory_region_init_io(&cxl_dstate->device, obj, &dev_ops, cxl_dstate, "device-status", CXL_DEVICE_STATUS_REGISTERS_LENGTH); - memory_region_init_io(&cxl_dstate->mailbox, obj, &mailbox_ops, cxl_dstate, + memory_region_init_io(&cxl_dstate->mailbox, obj, &mailbox_ops, cci, "mailbox", CXL_MAILBOX_REGISTERS_LENGTH); memory_region_init_io(&cxl_dstate->memory_device, obj, &mdev_ops, cxl_dstate, "memory device caps", @@ -230,36 +330,116 @@ void cxl_device_register_block_init(Object *obj, CXLDeviceState *cxl_dstate) &cxl_dstate->memory_device); } -static void device_reg_init_common(CXLDeviceState *cxl_dstate) { } +void cxl_event_set_status(CXLDeviceState *cxl_dstate, CXLEventLogType log_type, + bool available) +{ + if (available) { + cxl_dstate->event_status |= (1 << log_type); + } else { + cxl_dstate->event_status &= ~(1 << log_type); + } + + ARRAY_FIELD_DP64(cxl_dstate->dev_reg_state64, CXL_DEV_EVENT_STATUS, + EVENT_STATUS, cxl_dstate->event_status); +} + +static void device_reg_init_common(CXLDeviceState *cxl_dstate) +{ + CXLEventLogType log; + + for (log = 0; log < CXL_EVENT_TYPE_MAX; log++) { + cxl_event_set_status(cxl_dstate, log, false); + } +} static void mailbox_reg_init_common(CXLDeviceState *cxl_dstate) { - /* 2048 payload size, with no interrupt or background support */ + const uint8_t msi_n = 9; + + /* 2048 payload size */ ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP, PAYLOAD_SIZE, CXL_MAILBOX_PAYLOAD_SHIFT); cxl_dstate->payload_size = CXL_MAILBOX_MAX_PAYLOAD_SIZE; + /* irq support */ + ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP, + BG_INT_CAP, 1); + ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP, + MSI_N, msi_n); + cxl_dstate->mbox_msi_n = msi_n; + ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP, + MBOX_READY_TIME, 0); /* Not reported */ + ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP, + TYPE, 0); /* Inferred from class code */ } -static void memdev_reg_init_common(CXLDeviceState *cxl_dstate) { } - -void cxl_device_register_init_common(CXLDeviceState *cxl_dstate) +static void memdev_reg_init_common(CXLDeviceState *cxl_dstate) { - uint64_t *cap_hdrs = cxl_dstate->caps_reg_state64; + uint64_t memdev_status_reg; + + memdev_status_reg = FIELD_DP64(0, CXL_MEM_DEV_STS, MEDIA_STATUS, 1); + memdev_status_reg = FIELD_DP64(memdev_status_reg, CXL_MEM_DEV_STS, + MBOX_READY, 1); + cxl_dstate->memdev_status = memdev_status_reg; +} + +void cxl_device_register_init_t3(CXLType3Dev *ct3d) +{ + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; + uint64_t *cap_h = cxl_dstate->caps_reg_state64; const int cap_count = 3; /* CXL Device Capabilities Array Register */ - ARRAY_FIELD_DP64(cap_hdrs, CXL_DEV_CAP_ARRAY, CAP_ID, 0); - ARRAY_FIELD_DP64(cap_hdrs, CXL_DEV_CAP_ARRAY, CAP_VERSION, 1); - ARRAY_FIELD_DP64(cap_hdrs, CXL_DEV_CAP_ARRAY, CAP_COUNT, cap_count); + ARRAY_FIELD_DP64(cap_h, CXL_DEV_CAP_ARRAY, CAP_ID, 0); + ARRAY_FIELD_DP64(cap_h, CXL_DEV_CAP_ARRAY, CAP_VERSION, 1); + ARRAY_FIELD_DP64(cap_h, CXL_DEV_CAP_ARRAY, CAP_COUNT, cap_count); - cxl_device_cap_init(cxl_dstate, DEVICE_STATUS, 1); + cxl_device_cap_init(cxl_dstate, DEVICE_STATUS, 1, + CXL_DEVICE_STATUS_VERSION); device_reg_init_common(cxl_dstate); - cxl_device_cap_init(cxl_dstate, MAILBOX, 2); + cxl_device_cap_init(cxl_dstate, MAILBOX, 2, CXL_DEV_MAILBOX_VERSION); mailbox_reg_init_common(cxl_dstate); - cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000); + cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000, + CXL_MEM_DEV_STATUS_VERSION); memdev_reg_init_common(cxl_dstate); - assert(cxl_initialize_mailbox(cxl_dstate) == 0); + cxl_initialize_mailbox_t3(&ct3d->cci, DEVICE(ct3d), + CXL_MAILBOX_MAX_PAYLOAD_SIZE); +} + +void cxl_device_register_init_swcci(CSWMBCCIDev *sw) +{ + CXLDeviceState *cxl_dstate = &sw->cxl_dstate; + uint64_t *cap_h = cxl_dstate->caps_reg_state64; + const int cap_count = 3; + + /* CXL Device Capabilities Array Register */ + ARRAY_FIELD_DP64(cap_h, CXL_DEV_CAP_ARRAY, CAP_ID, 0); + ARRAY_FIELD_DP64(cap_h, CXL_DEV_CAP_ARRAY, CAP_VERSION, 1); + ARRAY_FIELD_DP64(cap_h, CXL_DEV_CAP_ARRAY, CAP_COUNT, cap_count); + + cxl_device_cap_init(cxl_dstate, DEVICE_STATUS, 1, 2); + device_reg_init_common(cxl_dstate); + + cxl_device_cap_init(cxl_dstate, MAILBOX, 2, 1); + mailbox_reg_init_common(cxl_dstate); + + cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000, 1); + memdev_reg_init_common(cxl_dstate); +} + +uint64_t cxl_device_get_timestamp(CXLDeviceState *cxl_dstate) +{ + uint64_t time, delta; + uint64_t final_time = 0; + + if (cxl_dstate->timestamp.set) { + /* Find the delta from the last time the host set the time. */ + time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + delta = time - cxl_dstate->timestamp.last_set; + final_time = cxl_dstate->timestamp.host_set + delta; + } + + return final_time; } diff --git a/hw/cxl/cxl-events.c b/hw/cxl/cxl-events.c new file mode 100644 index 0000000000..d397718b1b --- /dev/null +++ b/hw/cxl/cxl-events.c @@ -0,0 +1,249 @@ +/* + * CXL Event processing + * + * Copyright(C) 2023 Intel Corporation. + * + * 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/bswap.h" +#include "qemu/error-report.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" +#include "hw/cxl/cxl.h" +#include "hw/cxl/cxl_events.h" + +/* Artificial limit on the number of events a log can hold */ +#define CXL_TEST_EVENT_OVERFLOW 8 + +static void reset_overflow(CXLEventLog *log) +{ + log->overflow_err_count = 0; + log->first_overflow_timestamp = 0; + log->last_overflow_timestamp = 0; +} + +void cxl_event_init(CXLDeviceState *cxlds, int start_msg_num) +{ + CXLEventLog *log; + int i; + + for (i = 0; i < CXL_EVENT_TYPE_MAX; i++) { + log = &cxlds->event_logs[i]; + log->next_handle = 1; + log->overflow_err_count = 0; + log->first_overflow_timestamp = 0; + log->last_overflow_timestamp = 0; + log->irq_enabled = false; + log->irq_vec = start_msg_num++; + qemu_mutex_init(&log->lock); + QSIMPLEQ_INIT(&log->events); + } + + /* Override -- Dynamic Capacity uses the same vector as info */ + cxlds->event_logs[CXL_EVENT_TYPE_DYNAMIC_CAP].irq_vec = + cxlds->event_logs[CXL_EVENT_TYPE_INFO].irq_vec; + +} + +static CXLEvent *cxl_event_get_head(CXLEventLog *log) +{ + return QSIMPLEQ_FIRST(&log->events); +} + +static CXLEvent *cxl_event_get_next(CXLEvent *entry) +{ + return QSIMPLEQ_NEXT(entry, node); +} + +static int cxl_event_count(CXLEventLog *log) +{ + CXLEvent *event; + int rc = 0; + + QSIMPLEQ_FOREACH(event, &log->events, node) { + rc++; + } + + return rc; +} + +static bool cxl_event_empty(CXLEventLog *log) +{ + return QSIMPLEQ_EMPTY(&log->events); +} + +static void cxl_event_delete_head(CXLDeviceState *cxlds, + CXLEventLogType log_type, + CXLEventLog *log) +{ + CXLEvent *entry = cxl_event_get_head(log); + + reset_overflow(log); + QSIMPLEQ_REMOVE_HEAD(&log->events, node); + if (cxl_event_empty(log)) { + cxl_event_set_status(cxlds, log_type, false); + } + g_free(entry); +} + +/* + * return true if an interrupt should be generated as a result + * of inserting this event. + */ +bool cxl_event_insert(CXLDeviceState *cxlds, CXLEventLogType log_type, + CXLEventRecordRaw *event) +{ + uint64_t time; + CXLEventLog *log; + CXLEvent *entry; + + if (log_type >= CXL_EVENT_TYPE_MAX) { + return false; + } + + time = cxl_device_get_timestamp(cxlds); + + log = &cxlds->event_logs[log_type]; + + QEMU_LOCK_GUARD(&log->lock); + + if (cxl_event_count(log) >= CXL_TEST_EVENT_OVERFLOW) { + if (log->overflow_err_count == 0) { + log->first_overflow_timestamp = time; + } + log->overflow_err_count++; + log->last_overflow_timestamp = time; + return false; + } + + entry = g_new0(CXLEvent, 1); + + memcpy(&entry->data, event, sizeof(*event)); + + entry->data.hdr.handle = cpu_to_le16(log->next_handle); + log->next_handle++; + /* 0 handle is never valid */ + if (log->next_handle == 0) { + log->next_handle++; + } + entry->data.hdr.timestamp = cpu_to_le64(time); + + QSIMPLEQ_INSERT_TAIL(&log->events, entry, node); + cxl_event_set_status(cxlds, log_type, true); + + /* Count went from 0 to 1 */ + return cxl_event_count(log) == 1; +} + +CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl, + uint8_t log_type, int max_recs, + size_t *len) +{ + CXLEventLog *log; + CXLEvent *entry; + uint16_t nr; + + if (log_type >= CXL_EVENT_TYPE_MAX) { + return CXL_MBOX_INVALID_INPUT; + } + + log = &cxlds->event_logs[log_type]; + + QEMU_LOCK_GUARD(&log->lock); + + entry = cxl_event_get_head(log); + for (nr = 0; entry && nr < max_recs; nr++) { + memcpy(&pl->records[nr], &entry->data, CXL_EVENT_RECORD_SIZE); + entry = cxl_event_get_next(entry); + } + + if (!cxl_event_empty(log)) { + pl->flags |= CXL_GET_EVENT_FLAG_MORE_RECORDS; + } + + if (log->overflow_err_count) { + pl->flags |= CXL_GET_EVENT_FLAG_OVERFLOW; + pl->overflow_err_count = cpu_to_le16(log->overflow_err_count); + pl->first_overflow_timestamp = + cpu_to_le64(log->first_overflow_timestamp); + pl->last_overflow_timestamp = + cpu_to_le64(log->last_overflow_timestamp); + } + + pl->record_count = cpu_to_le16(nr); + *len = CXL_EVENT_PAYLOAD_HDR_SIZE + (CXL_EVENT_RECORD_SIZE * nr); + + return CXL_MBOX_SUCCESS; +} + +CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds, + CXLClearEventPayload *pl) +{ + CXLEventLog *log; + uint8_t log_type; + CXLEvent *entry; + int nr; + + log_type = pl->event_log; + + if (log_type >= CXL_EVENT_TYPE_MAX) { + return CXL_MBOX_INVALID_INPUT; + } + + log = &cxlds->event_logs[log_type]; + + QEMU_LOCK_GUARD(&log->lock); + /* + * Must iterate the queue twice. + * "The device shall verify the event record handles specified in the input + * payload are in temporal order. If the device detects an older event + * record that will not be cleared when Clear Event Records is executed, + * the device shall return the Invalid Handle return code and shall not + * clear any of the specified event records." + * -- CXL r3.1 Section 8.2.9.2.3: Clear Event Records (0101h) + */ + entry = cxl_event_get_head(log); + for (nr = 0; entry && nr < pl->nr_recs; nr++) { + uint16_t handle = pl->handle[nr]; + + /* NOTE: Both handles are little endian. */ + if (handle == 0 || entry->data.hdr.handle != handle) { + return CXL_MBOX_INVALID_INPUT; + } + entry = cxl_event_get_next(entry); + } + + entry = cxl_event_get_head(log); + for (nr = 0; entry && nr < pl->nr_recs; nr++) { + cxl_event_delete_head(cxlds, log_type, log); + entry = cxl_event_get_head(log); + } + + return CXL_MBOX_SUCCESS; +} + +void cxl_event_irq_assert(CXLType3Dev *ct3d) +{ + CXLDeviceState *cxlds = &ct3d->cxl_dstate; + PCIDevice *pdev = &ct3d->parent_obj; + int i; + + for (i = 0; i < CXL_EVENT_TYPE_MAX; i++) { + CXLEventLog *log = &cxlds->event_logs[i]; + + if (!log->irq_enabled || cxl_event_empty(log)) { + continue; + } + + /* Notifies interrupt, legacy IRQ is not supported */ + if (msix_enabled(pdev)) { + msix_notify(pdev, log->irq_vec); + } else if (msi_enabled(pdev)) { + msi_notify(pdev, log->irq_vec); + } + } +} diff --git a/hw/cxl/cxl-host-stubs.c b/hw/cxl/cxl-host-stubs.c index 24465a52ab..cae4afcdde 100644 --- a/hw/cxl/cxl-host-stubs.c +++ b/hw/cxl/cxl-host-stubs.c @@ -6,11 +6,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/cxl/cxl.h" +#include "hw/cxl/cxl_host.h" -void cxl_fixed_memory_window_config(MachineState *ms, - CXLFixedMemoryWindowOptions *object, - Error **errp) {}; - -void cxl_fixed_memory_window_link_targets(Error **errp) {}; +void cxl_fmws_link_targets(CXLState *stat, Error **errp) {}; +void cxl_machine_init(Object *obj, CXLState *state) {}; +void cxl_hook_up_pxb_registers(PCIBus *bus, CXLState *state, Error **errp) {}; const MemoryRegionOps cfmws_ops; diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c index 469b3c4ced..c5f5fcfd64 100644 --- a/hw/cxl/cxl-host.c +++ b/hw/cxl/cxl-host.c @@ -15,16 +15,19 @@ #include "qapi/qapi-visit-machine.h" #include "hw/cxl/cxl.h" +#include "hw/cxl/cxl_host.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pci_host.h" #include "hw/pci/pcie_port.h" +#include "hw/pci-bridge/pci_expander_bridge.h" -void cxl_fixed_memory_window_config(MachineState *ms, - CXLFixedMemoryWindowOptions *object, - Error **errp) +static void cxl_fixed_memory_window_config(CXLState *cxl_state, + CXLFixedMemoryWindowOptions *object, + Error **errp) { - CXLFixedWindow *fw = g_malloc0(sizeof(*fw)); + ERRP_GUARD(); + g_autofree CXLFixedWindow *fw = g_malloc0(sizeof(*fw)); strList *target; int i; @@ -37,15 +40,9 @@ void cxl_fixed_memory_window_config(MachineState *ms, return; } - fw->targets = g_malloc0_n(fw->num_targets, sizeof(*fw->targets)); - for (i = 0, target = object->targets; target; i++, target = target->next) { - /* This link cannot be resolved yet, so stash the name for now */ - fw->targets[i] = g_strdup(target->value); - } - if (object->size % (256 * MiB)) { error_setg(errp, - "Size of a CXL fixed memory window must my a multiple of 256MiB"); + "Size of a CXL fixed memory window must be a multiple of 256MiB"); return; } fw->size = object->size; @@ -62,20 +59,24 @@ void cxl_fixed_memory_window_config(MachineState *ms, fw->enc_int_gran = 0; } - ms->cxl_devices_state->fixed_windows = - g_list_append(ms->cxl_devices_state->fixed_windows, fw); + fw->targets = g_malloc0_n(fw->num_targets, sizeof(*fw->targets)); + for (i = 0, target = object->targets; target; i++, target = target->next) { + /* This link cannot be resolved yet, so stash the name for now */ + fw->targets[i] = g_strdup(target->value); + } + + cxl_state->fixed_windows = g_list_append(cxl_state->fixed_windows, + g_steal_pointer(&fw)); return; } -void cxl_fixed_memory_window_link_targets(Error **errp) +void cxl_fmws_link_targets(CXLState *cxl_state, Error **errp) { - MachineState *ms = MACHINE(qdev_get_machine()); - - if (ms->cxl_devices_state && ms->cxl_devices_state->fixed_windows) { + if (cxl_state && cxl_state->fixed_windows) { GList *it; - for (it = ms->cxl_devices_state->fixed_windows; it; it = it->next) { + for (it = cxl_state->fixed_windows; it; it = it->next) { CXLFixedWindow *fw = it->data; int i; @@ -84,7 +85,7 @@ void cxl_fixed_memory_window_link_targets(Error **errp) bool ambig; o = object_resolve_path_type(fw->targets[i], - TYPE_PXB_CXL_DEVICE, + TYPE_PXB_CXL_DEV, &ambig); if (!o) { error_setg(errp, "Could not resolve CXLFM target %s", @@ -97,41 +98,65 @@ void cxl_fixed_memory_window_link_targets(Error **errp) } } -/* TODO: support, multiple hdm decoders */ static bool cxl_hdm_find_target(uint32_t *cache_mem, hwaddr addr, uint8_t *target) { - uint32_t ctrl; - uint32_t ig_enc; - uint32_t iw_enc; - uint32_t target_reg; - uint32_t target_idx; + int hdm_inc = R_CXL_HDM_DECODER1_BASE_LO - R_CXL_HDM_DECODER0_BASE_LO; + unsigned int hdm_count; + bool found = false; + int i; + uint32_t cap; - ctrl = cache_mem[R_CXL_HDM_DECODER0_CTRL]; - if (!FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED)) { - return false; + cap = ldl_le_p(cache_mem + R_CXL_HDM_DECODER_CAPABILITY); + hdm_count = cxl_decoder_count_dec(FIELD_EX32(cap, + CXL_HDM_DECODER_CAPABILITY, + DECODER_COUNT)); + for (i = 0; i < hdm_count; i++) { + uint32_t ctrl, ig_enc, iw_enc, target_idx; + uint32_t low, high; + uint64_t base, size; + + low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_BASE_LO + i * hdm_inc); + high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_BASE_HI + i * hdm_inc); + base = (low & 0xf0000000) | ((uint64_t)high << 32); + low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_LO + i * hdm_inc); + high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_HI + i * hdm_inc); + size = (low & 0xf0000000) | ((uint64_t)high << 32); + if (addr < base || addr >= base + size) { + continue; + } + + ctrl = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL + i * hdm_inc); + if (!FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED)) { + return false; + } + found = true; + ig_enc = FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, IG); + iw_enc = FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, IW); + target_idx = (addr / cxl_decode_ig(ig_enc)) % (1 << iw_enc); + + if (target_idx < 4) { + uint32_t val = ldl_le_p(cache_mem + + R_CXL_HDM_DECODER0_TARGET_LIST_LO + + i * hdm_inc); + *target = extract32(val, target_idx * 8, 8); + } else { + uint32_t val = ldl_le_p(cache_mem + + R_CXL_HDM_DECODER0_TARGET_LIST_HI + + i * hdm_inc); + *target = extract32(val, (target_idx - 4) * 8, 8); + } + break; } - ig_enc = FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, IG); - iw_enc = FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, IW); - target_idx = (addr / cxl_decode_ig(ig_enc)) % (1 << iw_enc); - - if (target_idx > 4) { - target_reg = cache_mem[R_CXL_HDM_DECODER0_TARGET_LIST_LO]; - target_reg >>= target_idx * 8; - } else { - target_reg = cache_mem[R_CXL_HDM_DECODER0_TARGET_LIST_LO]; - target_reg >>= (target_idx - 4) * 8; - } - *target = target_reg & 0xff; - - return true; + return found; } static PCIDevice *cxl_cfmws_find_device(CXLFixedWindow *fw, hwaddr addr) { - CXLComponentState *hb_cstate; + CXLComponentState *hb_cstate, *usp_cstate; PCIHostState *hb; + CXLUpstreamPort *usp; int rb_index; uint32_t *cache_mem; uint8_t target; @@ -142,31 +167,76 @@ static PCIDevice *cxl_cfmws_find_device(CXLFixedWindow *fw, hwaddr addr) addr += fw->base; rb_index = (addr / cxl_decode_ig(fw->enc_int_gran)) % fw->num_targets; - hb = PCI_HOST_BRIDGE(fw->target_hbs[rb_index]->cxl.cxl_host_bridge); + hb = PCI_HOST_BRIDGE(fw->target_hbs[rb_index]->cxl_host_bridge); if (!hb || !hb->bus || !pci_bus_is_cxl(hb->bus)) { return NULL; } - hb_cstate = cxl_get_hb_cstate(hb); - if (!hb_cstate) { + if (cxl_get_hb_passthrough(hb)) { + rp = pcie_find_port_first(hb->bus); + if (!rp) { + return NULL; + } + } else { + hb_cstate = cxl_get_hb_cstate(hb); + if (!hb_cstate) { + return NULL; + } + + cache_mem = hb_cstate->crb.cache_mem_registers; + + target_found = cxl_hdm_find_target(cache_mem, addr, &target); + if (!target_found) { + return NULL; + } + + rp = pcie_find_port_by_pn(hb->bus, target); + if (!rp) { + return NULL; + } + } + + d = pci_bridge_get_sec_bus(PCI_BRIDGE(rp))->devices[0]; + if (!d) { return NULL; } - cache_mem = hb_cstate->crb.cache_mem_registers; + if (object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3)) { + return d; + } + + /* + * Could also be a switch. Note only one level of switching currently + * supported. + */ + if (!object_dynamic_cast(OBJECT(d), TYPE_CXL_USP)) { + return NULL; + } + usp = CXL_USP(d); + + usp_cstate = cxl_usp_to_cstate(usp); + if (!usp_cstate) { + return NULL; + } + + cache_mem = usp_cstate->crb.cache_mem_registers; target_found = cxl_hdm_find_target(cache_mem, addr, &target); if (!target_found) { return NULL; } - rp = pcie_find_port_by_pn(hb->bus, target); - if (!rp) { + d = pcie_find_port_by_pn(&PCI_BRIDGE(d)->sec_bus, target); + if (!d) { return NULL; } - d = pci_bridge_get_sec_bus(PCI_BRIDGE(rp))->devices[0]; + d = pci_bridge_get_sec_bus(PCI_BRIDGE(d))->devices[0]; + if (!d) { + return NULL; + } - if (!d || !object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3)) { + if (!object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3)) { return NULL; } @@ -220,3 +290,84 @@ const MemoryRegionOps cfmws_ops = { .unaligned = true, }, }; + +static void machine_get_cxl(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + CXLState *cxl_state = opaque; + bool value = cxl_state->is_enabled; + + visit_type_bool(v, name, &value, errp); +} + +static void machine_set_cxl(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + CXLState *cxl_state = opaque; + bool value; + + if (!visit_type_bool(v, name, &value, errp)) { + return; + } + cxl_state->is_enabled = value; +} + +static void machine_get_cfmw(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + CXLFixedMemoryWindowOptionsList **list = opaque; + + visit_type_CXLFixedMemoryWindowOptionsList(v, name, list, errp); +} + +static void machine_set_cfmw(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + CXLState *state = opaque; + CXLFixedMemoryWindowOptionsList *cfmw_list = NULL; + CXLFixedMemoryWindowOptionsList *it; + + visit_type_CXLFixedMemoryWindowOptionsList(v, name, &cfmw_list, errp); + if (!cfmw_list) { + return; + } + + for (it = cfmw_list; it; it = it->next) { + cxl_fixed_memory_window_config(state, it->value, errp); + } + state->cfmw_list = cfmw_list; +} + +void cxl_machine_init(Object *obj, CXLState *state) +{ + object_property_add(obj, "cxl", "bool", machine_get_cxl, + machine_set_cxl, NULL, state); + object_property_set_description(obj, "cxl", + "Set on/off to enable/disable " + "CXL instantiation"); + + object_property_add(obj, "cxl-fmw", "CXLFixedMemoryWindow", + machine_get_cfmw, machine_set_cfmw, + NULL, state); + object_property_set_description(obj, "cxl-fmw", + "CXL Fixed Memory Windows (array)"); +} + +void cxl_hook_up_pxb_registers(PCIBus *bus, CXLState *state, Error **errp) +{ + /* Walk the pci busses looking for pxb busses to hook up */ + if (bus) { + QLIST_FOREACH(bus, &bus->child, sibling) { + if (!pci_bus_is_root(bus)) { + continue; + } + if (pci_bus_is_cxl(bus)) { + if (!state->is_enabled) { + error_setg(errp, "CXL host bridges present, but cxl=off"); + return; + } + pxb_cxl_hook_up_registers(state, bus, errp); + } + } + } +} diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index bb66c765a5..e5eb97cb91 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -8,11 +8,19 @@ */ #include "qemu/osdep.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" #include "hw/cxl/cxl.h" +#include "hw/cxl/cxl_events.h" #include "hw/pci/pci.h" +#include "hw/pci-bridge/cxl_upstream_port.h" #include "qemu/cutils.h" #include "qemu/log.h" +#include "qemu/units.h" #include "qemu/uuid.h" +#include "sysemu/hostmem.h" + +#define CXL_CAPACITY_MULTIPLIER (256 * MiB) /* * How to add a new command, example. The command set FOO, with cmd BAR. @@ -20,7 +28,7 @@ * FOO = 0x7f, * #define BAR 0 * 2. Implement the handler - * static ret_code cmd_foo_bar(struct cxl_cmd *cmd, + * static CXLRetCode cmd_foo_bar(struct cxl_cmd *cmd, * CXLDeviceState *cxl_dstate, uint16_t *len) * 3. Add the command to the cxl_cmd_set[][] * [FOO][BAR] = { "FOO_BAR", cmd_foo_bar, x, y }, @@ -35,11 +43,14 @@ * fill the output data into cmd->payload (overwriting what was there), * setting the length, and returning a valid return code. * - * XXX: The handler need not worry about endianess. The payload is read out of + * XXX: The handler need not worry about endianness. The payload is read out of * a register interface that already deals with it. */ enum { + INFOSTAT = 0x00, + #define IS_IDENTIFY 0x1 + #define BACKGROUND_OPERATION_STATUS 0x2 EVENTS = 0x01, #define GET_RECORDS 0x0 #define CLEAR_RECORDS 0x1 @@ -59,73 +70,554 @@ enum { #define GET_PARTITION_INFO 0x0 #define GET_LSA 0x2 #define SET_LSA 0x3 + SANITIZE = 0x44, + #define OVERWRITE 0x0 + #define SECURE_ERASE 0x1 + PERSISTENT_MEM = 0x45, + #define GET_SECURITY_STATE 0x0 + MEDIA_AND_POISON = 0x43, + #define GET_POISON_LIST 0x0 + #define INJECT_POISON 0x1 + #define CLEAR_POISON 0x2 + PHYSICAL_SWITCH = 0x51, + #define IDENTIFY_SWITCH_DEVICE 0x0 + #define GET_PHYSICAL_PORT_STATE 0x1 + TUNNEL = 0x53, + #define MANAGEMENT_COMMAND 0x0 }; -/* 8.2.8.4.5.1 Command Return Codes */ -typedef enum { - CXL_MBOX_SUCCESS = 0x0, - CXL_MBOX_BG_STARTED = 0x1, - CXL_MBOX_INVALID_INPUT = 0x2, - CXL_MBOX_UNSUPPORTED = 0x3, - CXL_MBOX_INTERNAL_ERROR = 0x4, - CXL_MBOX_RETRY_REQUIRED = 0x5, - CXL_MBOX_BUSY = 0x6, - CXL_MBOX_MEDIA_DISABLED = 0x7, - CXL_MBOX_FW_XFER_IN_PROGRESS = 0x8, - CXL_MBOX_FW_XFER_OUT_OF_ORDER = 0x9, - CXL_MBOX_FW_AUTH_FAILED = 0xa, - CXL_MBOX_FW_INVALID_SLOT = 0xb, - CXL_MBOX_FW_ROLLEDBACK = 0xc, - CXL_MBOX_FW_REST_REQD = 0xd, - CXL_MBOX_INVALID_HANDLE = 0xe, - CXL_MBOX_INVALID_PA = 0xf, - CXL_MBOX_INJECT_POISON_LIMIT = 0x10, - CXL_MBOX_PERMANENT_MEDIA_FAILURE = 0x11, - CXL_MBOX_ABORTED = 0x12, - CXL_MBOX_INVALID_SECURITY_STATE = 0x13, - CXL_MBOX_INCORRECT_PASSPHRASE = 0x14, - CXL_MBOX_UNSUPPORTED_MAILBOX = 0x15, - CXL_MBOX_INVALID_PAYLOAD_LENGTH = 0x16, - CXL_MBOX_MAX = 0x17 -} ret_code; +/* CCI Message Format CXL r3.1 Figure 7-19 */ +typedef struct CXLCCIMessage { + uint8_t category; +#define CXL_CCI_CAT_REQ 0 +#define CXL_CCI_CAT_RSP 1 + uint8_t tag; + uint8_t resv1; + uint8_t command; + uint8_t command_set; + uint8_t pl_length[3]; + uint16_t rc; + uint16_t vendor_specific; + uint8_t payload[]; +} QEMU_PACKED CXLCCIMessage; -struct cxl_cmd; -typedef ret_code (*opcode_handler)(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, uint16_t *len); -struct cxl_cmd { - const char *name; - opcode_handler handler; - ssize_t in; - uint16_t effect; /* Reported in CEL */ - uint8_t *payload; -}; - -#define DEFINE_MAILBOX_HANDLER_ZEROED(name, size) \ - uint16_t __zero##name = size; \ - static ret_code cmd_##name(struct cxl_cmd *cmd, \ - CXLDeviceState *cxl_dstate, uint16_t *len) \ - { \ - *len = __zero##name; \ - memset(cmd->payload, 0, *len); \ - return CXL_MBOX_SUCCESS; \ - } -#define DEFINE_MAILBOX_HANDLER_NOP(name) \ - static ret_code cmd_##name(struct cxl_cmd *cmd, \ - CXLDeviceState *cxl_dstate, uint16_t *len) \ - { \ - return CXL_MBOX_SUCCESS; \ - } - -DEFINE_MAILBOX_HANDLER_ZEROED(events_get_records, 0x20); -DEFINE_MAILBOX_HANDLER_NOP(events_clear_records); -DEFINE_MAILBOX_HANDLER_ZEROED(events_get_interrupt_policy, 4); -DEFINE_MAILBOX_HANDLER_NOP(events_set_interrupt_policy); - -/* 8.2.9.2.1 */ -static ret_code cmd_firmware_update_get_info(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +/* This command is only defined to an MLD FM Owned LD or an MHD */ +static CXLRetCode cmd_tunnel_management_cmd(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) { + PCIDevice *tunnel_target; + CXLCCI *target_cci; + struct { + uint8_t port_or_ld_id; + uint8_t target_type; + uint16_t size; + CXLCCIMessage ccimessage; + } QEMU_PACKED *in; + struct { + uint16_t resp_len; + uint8_t resv[2]; + CXLCCIMessage ccimessage; + } QEMU_PACKED *out; + size_t pl_length, length_out; + bool bg_started; + int rc; + + if (cmd->in < sizeof(*in)) { + return CXL_MBOX_INVALID_INPUT; + } + in = (void *)payload_in; + out = (void *)payload_out; + + /* Enough room for minimum sized message - no payload */ + if (in->size < sizeof(in->ccimessage)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + /* Length of input payload should be in->size + a wrapping tunnel header */ + if (in->size != len_in - offsetof(typeof(*out), ccimessage)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + if (in->ccimessage.category != CXL_CCI_CAT_REQ) { + return CXL_MBOX_INVALID_INPUT; + } + + if (in->target_type != 0) { + qemu_log_mask(LOG_UNIMP, + "Tunneled Command sent to non existent FM-LD"); + return CXL_MBOX_INVALID_INPUT; + } + + /* + * Target of a tunnel unfortunately depends on type of CCI readint + * the message. + * If in a switch, then it's the port number. + * If in an MLD it is the ld number. + * If in an MHD target type indicate where we are going. + */ + if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + if (in->port_or_ld_id != 0) { + /* Only pretending to have one for now! */ + return CXL_MBOX_INVALID_INPUT; + } + target_cci = &ct3d->ld0_cci; + } else if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_USP)) { + CXLUpstreamPort *usp = CXL_USP(cci->d); + + tunnel_target = pcie_find_port_by_pn(&PCI_BRIDGE(usp)->sec_bus, + in->port_or_ld_id); + if (!tunnel_target) { + return CXL_MBOX_INVALID_INPUT; + } + tunnel_target = + pci_bridge_get_sec_bus(PCI_BRIDGE(tunnel_target))->devices[0]; + if (!tunnel_target) { + return CXL_MBOX_INVALID_INPUT; + } + if (object_dynamic_cast(OBJECT(tunnel_target), TYPE_CXL_TYPE3)) { + CXLType3Dev *ct3d = CXL_TYPE3(tunnel_target); + /* Tunneled VDMs always land on FM Owned LD */ + target_cci = &ct3d->vdm_fm_owned_ld_mctp_cci; + } else { + return CXL_MBOX_INVALID_INPUT; + } + } else { + return CXL_MBOX_INVALID_INPUT; + } + + pl_length = in->ccimessage.pl_length[2] << 16 | + in->ccimessage.pl_length[1] << 8 | in->ccimessage.pl_length[0]; + rc = cxl_process_cci_message(target_cci, + in->ccimessage.command_set, + in->ccimessage.command, + pl_length, in->ccimessage.payload, + &length_out, out->ccimessage.payload, + &bg_started); + /* Payload should be in place. Rest of CCI header and needs filling */ + out->resp_len = length_out + sizeof(CXLCCIMessage); + st24_le_p(out->ccimessage.pl_length, length_out); + out->ccimessage.rc = rc; + out->ccimessage.category = CXL_CCI_CAT_RSP; + out->ccimessage.command = in->ccimessage.command; + out->ccimessage.command_set = in->ccimessage.command_set; + out->ccimessage.tag = in->ccimessage.tag; + *len_out = length_out + sizeof(*out); + + return CXL_MBOX_SUCCESS; +} + +static CXLRetCode cmd_events_get_records(const struct cxl_cmd *cmd, + uint8_t *payload_in, size_t len_in, + uint8_t *payload_out, size_t *len_out, + CXLCCI *cci) +{ + CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; + CXLGetEventPayload *pl; + uint8_t log_type; + int max_recs; + + if (cmd->in < sizeof(log_type)) { + return CXL_MBOX_INVALID_INPUT; + } + + log_type = payload_in[0]; + + pl = (CXLGetEventPayload *)payload_out; + memset(pl, 0, sizeof(*pl)); + + max_recs = (cxlds->payload_size - CXL_EVENT_PAYLOAD_HDR_SIZE) / + CXL_EVENT_RECORD_SIZE; + if (max_recs > 0xFFFF) { + max_recs = 0xFFFF; + } + + return cxl_event_get_records(cxlds, pl, log_type, max_recs, len_out); +} + +static CXLRetCode cmd_events_clear_records(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; + CXLClearEventPayload *pl; + + pl = (CXLClearEventPayload *)payload_in; + *len_out = 0; + return cxl_event_clear_records(cxlds, pl); +} + +static CXLRetCode cmd_events_get_interrupt_policy(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; + CXLEventInterruptPolicy *policy; + CXLEventLog *log; + + policy = (CXLEventInterruptPolicy *)payload_out; + memset(policy, 0, sizeof(*policy)); + + log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO]; + if (log->irq_enabled) { + policy->info_settings = CXL_EVENT_INT_SETTING(log->irq_vec); + } + + log = &cxlds->event_logs[CXL_EVENT_TYPE_WARN]; + if (log->irq_enabled) { + policy->warn_settings = CXL_EVENT_INT_SETTING(log->irq_vec); + } + + log = &cxlds->event_logs[CXL_EVENT_TYPE_FAIL]; + if (log->irq_enabled) { + policy->failure_settings = CXL_EVENT_INT_SETTING(log->irq_vec); + } + + log = &cxlds->event_logs[CXL_EVENT_TYPE_FATAL]; + if (log->irq_enabled) { + policy->fatal_settings = CXL_EVENT_INT_SETTING(log->irq_vec); + } + + log = &cxlds->event_logs[CXL_EVENT_TYPE_DYNAMIC_CAP]; + if (log->irq_enabled) { + /* Dynamic Capacity borrows the same vector as info */ + policy->dyn_cap_settings = CXL_INT_MSI_MSIX; + } + + *len_out = sizeof(*policy); + return CXL_MBOX_SUCCESS; +} + +static CXLRetCode cmd_events_set_interrupt_policy(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; + CXLEventInterruptPolicy *policy; + CXLEventLog *log; + + if (len_in < CXL_EVENT_INT_SETTING_MIN_LEN) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + policy = (CXLEventInterruptPolicy *)payload_in; + + log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO]; + log->irq_enabled = (policy->info_settings & CXL_EVENT_INT_MODE_MASK) == + CXL_INT_MSI_MSIX; + + log = &cxlds->event_logs[CXL_EVENT_TYPE_WARN]; + log->irq_enabled = (policy->warn_settings & CXL_EVENT_INT_MODE_MASK) == + CXL_INT_MSI_MSIX; + + log = &cxlds->event_logs[CXL_EVENT_TYPE_FAIL]; + log->irq_enabled = (policy->failure_settings & CXL_EVENT_INT_MODE_MASK) == + CXL_INT_MSI_MSIX; + + log = &cxlds->event_logs[CXL_EVENT_TYPE_FATAL]; + log->irq_enabled = (policy->fatal_settings & CXL_EVENT_INT_MODE_MASK) == + CXL_INT_MSI_MSIX; + + /* DCD is optional */ + if (len_in < sizeof(*policy)) { + return CXL_MBOX_SUCCESS; + } + + log = &cxlds->event_logs[CXL_EVENT_TYPE_DYNAMIC_CAP]; + log->irq_enabled = (policy->dyn_cap_settings & CXL_EVENT_INT_MODE_MASK) == + CXL_INT_MSI_MSIX; + + *len_out = 0; + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.1 section 8.2.9.1.1: Identify (Opcode 0001h) */ +static CXLRetCode cmd_infostat_identify(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + PCIDeviceClass *class = PCI_DEVICE_GET_CLASS(cci->d); + struct { + uint16_t pcie_vid; + uint16_t pcie_did; + uint16_t pcie_subsys_vid; + uint16_t pcie_subsys_id; + uint64_t sn; + uint8_t max_message_size; + uint8_t component_type; + } QEMU_PACKED *is_identify; + QEMU_BUILD_BUG_ON(sizeof(*is_identify) != 18); + + is_identify = (void *)payload_out; + memset(is_identify, 0, sizeof(*is_identify)); + is_identify->pcie_vid = class->vendor_id; + is_identify->pcie_did = class->device_id; + if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_USP)) { + is_identify->sn = CXL_USP(cci->d)->sn; + /* Subsystem info not defined for a USP */ + is_identify->pcie_subsys_vid = 0; + is_identify->pcie_subsys_id = 0; + is_identify->component_type = 0x0; /* Switch */ + } else if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { + PCIDevice *pci_dev = PCI_DEVICE(cci->d); + + is_identify->sn = CXL_TYPE3(cci->d)->sn; + /* + * We can't always use class->subsystem_vendor_id as + * it is not set if the defaults are used. + */ + is_identify->pcie_subsys_vid = + pci_get_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID); + is_identify->pcie_subsys_id = + pci_get_word(pci_dev->config + PCI_SUBSYSTEM_ID); + is_identify->component_type = 0x3; /* Type 3 */ + } + + /* TODO: Allow this to vary across different CCIs */ + is_identify->max_message_size = 9; /* 512 bytes - MCTP_CXL_MAILBOX_BYTES */ + *len_out = sizeof(*is_identify); + return CXL_MBOX_SUCCESS; +} + +static void cxl_set_dsp_active_bm(PCIBus *b, PCIDevice *d, + void *private) +{ + uint8_t *bm = private; + if (object_dynamic_cast(OBJECT(d), TYPE_CXL_DSP)) { + uint8_t port = PCIE_PORT(d)->port; + bm[port / 8] |= 1 << (port % 8); + } +} + +/* CXL r3.1 Section 7.6.7.1.1: Identify Switch Device (Opcode 5100h) */ +static CXLRetCode cmd_identify_switch_device(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + PCIEPort *usp = PCIE_PORT(cci->d); + PCIBus *bus = &PCI_BRIDGE(cci->d)->sec_bus; + int num_phys_ports = pcie_count_ds_ports(bus); + + struct cxl_fmapi_ident_switch_dev_resp_pl { + uint8_t ingress_port_id; + uint8_t rsvd; + uint8_t num_physical_ports; + uint8_t num_vcss; + uint8_t active_port_bitmask[0x20]; + uint8_t active_vcs_bitmask[0x20]; + uint16_t total_vppbs; + uint16_t bound_vppbs; + uint8_t num_hdm_decoders_per_usp; + } QEMU_PACKED *out; + QEMU_BUILD_BUG_ON(sizeof(*out) != 0x49); + + out = (struct cxl_fmapi_ident_switch_dev_resp_pl *)payload_out; + *out = (struct cxl_fmapi_ident_switch_dev_resp_pl) { + .num_physical_ports = num_phys_ports + 1, /* 1 USP */ + .num_vcss = 1, /* Not yet support multiple VCS - potentially tricky */ + .active_vcs_bitmask[0] = 0x1, + .total_vppbs = num_phys_ports + 1, + .bound_vppbs = num_phys_ports + 1, + .num_hdm_decoders_per_usp = 4, + }; + + /* Depends on the CCI type */ + if (object_dynamic_cast(OBJECT(cci->intf), TYPE_PCIE_PORT)) { + out->ingress_port_id = PCIE_PORT(cci->intf)->port; + } else { + /* MCTP? */ + out->ingress_port_id = 0; + } + + pci_for_each_device_under_bus(bus, cxl_set_dsp_active_bm, + out->active_port_bitmask); + out->active_port_bitmask[usp->port / 8] |= (1 << usp->port % 8); + + *len_out = sizeof(*out); + + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.1 Section 7.6.7.1.2: Get Physical Port State (Opcode 5101h) */ +static CXLRetCode cmd_get_physical_port_state(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + /* CXL r3.1 Table 7-17: Get Physical Port State Request Payload */ + struct cxl_fmapi_get_phys_port_state_req_pl { + uint8_t num_ports; + uint8_t ports[]; + } QEMU_PACKED *in; + + /* + * CXL r3.1 Table 7-19: Get Physical Port State Port Information Block + * Format + */ + struct cxl_fmapi_port_state_info_block { + uint8_t port_id; + uint8_t config_state; + uint8_t connected_device_cxl_version; + uint8_t rsv1; + uint8_t connected_device_type; + uint8_t port_cxl_version_bitmask; + uint8_t max_link_width; + uint8_t negotiated_link_width; + uint8_t supported_link_speeds_vector; + uint8_t max_link_speed; + uint8_t current_link_speed; + uint8_t ltssm_state; + uint8_t first_lane_num; + uint16_t link_state; + uint8_t supported_ld_count; + } QEMU_PACKED; + + /* CXL r3.1 Table 7-18: Get Physical Port State Response Payload */ + struct cxl_fmapi_get_phys_port_state_resp_pl { + uint8_t num_ports; + uint8_t rsv1[3]; + struct cxl_fmapi_port_state_info_block ports[]; + } QEMU_PACKED *out; + PCIBus *bus = &PCI_BRIDGE(cci->d)->sec_bus; + PCIEPort *usp = PCIE_PORT(cci->d); + size_t pl_size; + int i; + + in = (struct cxl_fmapi_get_phys_port_state_req_pl *)payload_in; + out = (struct cxl_fmapi_get_phys_port_state_resp_pl *)payload_out; + + /* Check if what was requested can fit */ + if (sizeof(*out) + sizeof(*out->ports) * in->num_ports > cci->payload_max) { + return CXL_MBOX_INVALID_INPUT; + } + + /* For success there should be a match for each requested */ + out->num_ports = in->num_ports; + + for (i = 0; i < in->num_ports; i++) { + struct cxl_fmapi_port_state_info_block *port; + /* First try to match on downstream port */ + PCIDevice *port_dev; + uint16_t lnkcap, lnkcap2, lnksta; + + port = &out->ports[i]; + + port_dev = pcie_find_port_by_pn(bus, in->ports[i]); + if (port_dev) { /* DSP */ + PCIDevice *ds_dev = pci_bridge_get_sec_bus(PCI_BRIDGE(port_dev)) + ->devices[0]; + port->config_state = 3; + if (ds_dev) { + if (object_dynamic_cast(OBJECT(ds_dev), TYPE_CXL_TYPE3)) { + port->connected_device_type = 5; /* Assume MLD for now */ + } else { + port->connected_device_type = 1; + } + } else { + port->connected_device_type = 0; + } + port->supported_ld_count = 3; + } else if (usp->port == in->ports[i]) { /* USP */ + port_dev = PCI_DEVICE(usp); + port->config_state = 4; + port->connected_device_type = 0; + } else { + return CXL_MBOX_INVALID_INPUT; + } + + port->port_id = in->ports[i]; + /* Information on status of this port in lnksta, lnkcap */ + if (!port_dev->exp.exp_cap) { + return CXL_MBOX_INTERNAL_ERROR; + } + lnksta = port_dev->config_read(port_dev, + port_dev->exp.exp_cap + PCI_EXP_LNKSTA, + sizeof(lnksta)); + lnkcap = port_dev->config_read(port_dev, + port_dev->exp.exp_cap + PCI_EXP_LNKCAP, + sizeof(lnkcap)); + lnkcap2 = port_dev->config_read(port_dev, + port_dev->exp.exp_cap + PCI_EXP_LNKCAP2, + sizeof(lnkcap2)); + + port->max_link_width = (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4; + port->negotiated_link_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> 4; + /* No definition for SLS field in linux/pci_regs.h */ + port->supported_link_speeds_vector = (lnkcap2 & 0xFE) >> 1; + port->max_link_speed = lnkcap & PCI_EXP_LNKCAP_SLS; + port->current_link_speed = lnksta & PCI_EXP_LNKSTA_CLS; + /* TODO: Track down if we can get the rest of the info */ + port->ltssm_state = 0x7; + port->first_lane_num = 0; + port->link_state = 0; + port->port_cxl_version_bitmask = 0x2; + port->connected_device_cxl_version = 0x2; + } + + pl_size = sizeof(*out) + sizeof(*out->ports) * in->num_ports; + *len_out = pl_size; + + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.1 Section 8.2.9.1.2: Background Operation Status (Opcode 0002h) */ +static CXLRetCode cmd_infostat_bg_op_sts(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint8_t status; + uint8_t rsvd; + uint16_t opcode; + uint16_t returncode; + uint16_t vendor_ext_status; + } QEMU_PACKED *bg_op_status; + QEMU_BUILD_BUG_ON(sizeof(*bg_op_status) != 8); + + bg_op_status = (void *)payload_out; + memset(bg_op_status, 0, sizeof(*bg_op_status)); + bg_op_status->status = cci->bg.complete_pct << 1; + if (cci->bg.runtime > 0) { + bg_op_status->status |= 1U << 0; + } + bg_op_status->opcode = cci->bg.opcode; + bg_op_status->returncode = cci->bg.ret_code; + *len_out = sizeof(*bg_op_status); + + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.1 Section 8.2.9.3.1: Get FW Info (Opcode 0200h) */ +static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; struct { uint8_t slots_supported; uint8_t slot_info; @@ -138,11 +630,12 @@ static ret_code cmd_firmware_update_get_info(struct cxl_cmd *cmd, } QEMU_PACKED *fw_info; QEMU_BUILD_BUG_ON(sizeof(*fw_info) != 0x50); - if (cxl_dstate->pmem_size < (256 << 20)) { + if ((cxl_dstate->vmem_size < CXL_CAPACITY_MULTIPLIER) || + (cxl_dstate->pmem_size < CXL_CAPACITY_MULTIPLIER)) { return CXL_MBOX_INTERNAL_ERROR; } - fw_info = (void *)cmd->payload; + fw_info = (void *)payload_out; memset(fw_info, 0, sizeof(*fw_info)); fw_info->slots_supported = 2; @@ -150,52 +643,59 @@ static ret_code cmd_firmware_update_get_info(struct cxl_cmd *cmd, fw_info->caps = 0; pstrcpy(fw_info->fw_rev1, sizeof(fw_info->fw_rev1), "BWFW VERSION 0"); - *len = sizeof(*fw_info); + *len_out = sizeof(*fw_info); return CXL_MBOX_SUCCESS; } -/* 8.2.9.3.1 */ -static ret_code cmd_timestamp_get(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +/* CXL r3.1 Section 8.2.9.4.1: Get Timestamp (Opcode 0300h) */ +static CXLRetCode cmd_timestamp_get(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) { - uint64_t time, delta; - uint64_t final_time = 0; + CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; + uint64_t final_time = cxl_device_get_timestamp(cxl_dstate); - if (cxl_dstate->timestamp.set) { - /* First find the delta from the last time the host set the time. */ - time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - delta = time - cxl_dstate->timestamp.last_set; - final_time = cxl_dstate->timestamp.host_set + delta; - } - - /* Then adjust the actual time */ - stq_le_p(cmd->payload, final_time); - *len = 8; + stq_le_p(payload_out, final_time); + *len_out = 8; return CXL_MBOX_SUCCESS; } -/* 8.2.9.3.2 */ -static ret_code cmd_timestamp_set(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +/* CXL r3.1 Section 8.2.9.4.2: Set Timestamp (Opcode 0301h) */ +static CXLRetCode cmd_timestamp_set(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) { + CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; + cxl_dstate->timestamp.set = true; cxl_dstate->timestamp.last_set = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - cxl_dstate->timestamp.host_set = le64_to_cpu(*(uint64_t *)cmd->payload); + cxl_dstate->timestamp.host_set = le64_to_cpu(*(uint64_t *)payload_in); - *len = 0; + *len_out = 0; return CXL_MBOX_SUCCESS; } -static QemuUUID cel_uuid; +/* CXL r3.1 Section 8.2.9.5.2.1: Command Effects Log (CEL) */ +static const QemuUUID cel_uuid = { + .data = UUID(0x0da9c0b5, 0xbf41, 0x4b78, 0x8f, 0x79, + 0x96, 0xb1, 0x62, 0x3b, 0x3f, 0x17) +}; -/* 8.2.9.4.1 */ -static ret_code cmd_logs_get_supported(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +/* CXL r3.1 Section 8.2.9.5.1: Get Supported Logs (Opcode 0400h) */ +static CXLRetCode cmd_logs_get_supported(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) { struct { uint16_t entries; @@ -204,62 +704,66 @@ static ret_code cmd_logs_get_supported(struct cxl_cmd *cmd, QemuUUID uuid; uint32_t size; } log_entries[1]; - } QEMU_PACKED *supported_logs = (void *)cmd->payload; + } QEMU_PACKED *supported_logs = (void *)payload_out; QEMU_BUILD_BUG_ON(sizeof(*supported_logs) != 0x1c); supported_logs->entries = 1; supported_logs->log_entries[0].uuid = cel_uuid; - supported_logs->log_entries[0].size = 4 * cxl_dstate->cel_size; + supported_logs->log_entries[0].size = 4 * cci->cel_size; - *len = sizeof(*supported_logs); + *len_out = sizeof(*supported_logs); return CXL_MBOX_SUCCESS; } -/* 8.2.9.4.2 */ -static ret_code cmd_logs_get_log(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +/* CXL r3.1 Section 8.2.9.5.2: Get Log (Opcode 0401h) */ +static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) { struct { QemuUUID uuid; uint32_t offset; uint32_t length; - } QEMU_PACKED QEMU_ALIGNED(16) *get_log = (void *)cmd->payload; + } QEMU_PACKED QEMU_ALIGNED(16) *get_log; + + get_log = (void *)payload_in; /* - * 8.2.9.4.2 - * The device shall return Invalid Parameter if the Offset or Length + * CXL r3.1 Section 8.2.9.5.2: Get Log (Opcode 0401h) + * The device shall return Invalid Input if the Offset or Length * fields attempt to access beyond the size of the log as reported by Get * Supported Logs. * - * XXX: Spec is wrong, "Invalid Parameter" isn't a thing. - * XXX: Spec doesn't address incorrect UUID incorrectness. - * * The CEL buffer is large enough to fit all commands in the emulation, so * the only possible failure would be if the mailbox itself isn't big * enough. */ - if (get_log->offset + get_log->length > cxl_dstate->payload_size) { + if (get_log->offset + get_log->length > cci->payload_max) { return CXL_MBOX_INVALID_INPUT; } if (!qemu_uuid_is_equal(&get_log->uuid, &cel_uuid)) { - return CXL_MBOX_UNSUPPORTED; + return CXL_MBOX_INVALID_LOG; } /* Store off everything to local variables so we can wipe out the payload */ - *len = get_log->length; + *len_out = get_log->length; - memmove(cmd->payload, cxl_dstate->cel_log + get_log->offset, - get_log->length); + memmove(payload_out, cci->cel_log + get_log->offset, get_log->length); return CXL_MBOX_SUCCESS; } -/* 8.2.9.5.1.1 */ -static ret_code cmd_identify_memory_device(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +/* CXL r3.1 Section 8.2.9.9.1.1: Identify Memory Device (Opcode 4000h) */ +static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) { struct { char fw_revision[0x10]; @@ -278,107 +782,456 @@ static ret_code cmd_identify_memory_device(struct cxl_cmd *cmd, uint8_t qos_telemetry_caps; } QEMU_PACKED *id; QEMU_BUILD_BUG_ON(sizeof(*id) != 0x43); - - CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); - uint64_t size = cxl_dstate->pmem_size; + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; - if (!QEMU_IS_ALIGNED(size, 256 << 20)) { + if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) || + (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER))) { return CXL_MBOX_INTERNAL_ERROR; } - id = (void *)cmd->payload; + id = (void *)payload_out; memset(id, 0, sizeof(*id)); - /* PMEM only */ snprintf(id->fw_revision, 0x10, "BWFW VERSION %02d", 0); - id->total_capacity = size / (256 << 20); - id->persistent_capacity = size / (256 << 20); - id->lsa_size = cvc->get_lsa_size(ct3d); + stq_le_p(&id->total_capacity, + cxl_dstate->mem_size / CXL_CAPACITY_MULTIPLIER); + stq_le_p(&id->persistent_capacity, + cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER); + stq_le_p(&id->volatile_capacity, + cxl_dstate->vmem_size / CXL_CAPACITY_MULTIPLIER); + stl_le_p(&id->lsa_size, cvc->get_lsa_size(ct3d)); + /* 256 poison records */ + st24_le_p(id->poison_list_max_mer, 256); + /* No limit - so limited by main poison record limit */ + stw_le_p(&id->inject_poison_limit, 0); - *len = sizeof(*id); + *len_out = sizeof(*id); return CXL_MBOX_SUCCESS; } -static ret_code cmd_ccls_get_partition_info(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +/* CXL r3.1 Section 8.2.9.9.2.1: Get Partition Info (Opcode 4100h) */ +static CXLRetCode cmd_ccls_get_partition_info(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) { + CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; struct { uint64_t active_vmem; uint64_t active_pmem; uint64_t next_vmem; uint64_t next_pmem; - } QEMU_PACKED *part_info = (void *)cmd->payload; + } QEMU_PACKED *part_info = (void *)payload_out; QEMU_BUILD_BUG_ON(sizeof(*part_info) != 0x20); - uint64_t size = cxl_dstate->pmem_size; - if (!QEMU_IS_ALIGNED(size, 256 << 20)) { + if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) || + (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER))) { return CXL_MBOX_INTERNAL_ERROR; } - /* PMEM only */ - part_info->active_vmem = 0; - part_info->next_vmem = 0; - part_info->active_pmem = size / (256 << 20); - part_info->next_pmem = 0; + stq_le_p(&part_info->active_vmem, + cxl_dstate->vmem_size / CXL_CAPACITY_MULTIPLIER); + /* + * When both next_vmem and next_pmem are 0, there is no pending change to + * partitioning. + */ + stq_le_p(&part_info->next_vmem, 0); + stq_le_p(&part_info->active_pmem, + cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER); + stq_le_p(&part_info->next_pmem, 0); - *len = sizeof(*part_info); + *len_out = sizeof(*part_info); return CXL_MBOX_SUCCESS; } -static ret_code cmd_ccls_get_lsa(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +/* CXL r3.1 Section 8.2.9.9.2.3: Get LSA (Opcode 4102h) */ +static CXLRetCode cmd_ccls_get_lsa(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) { struct { uint32_t offset; uint32_t length; } QEMU_PACKED *get_lsa; - CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); uint32_t offset, length; - get_lsa = (void *)cmd->payload; + get_lsa = (void *)payload_in; offset = get_lsa->offset; length = get_lsa->length; if (offset + length > cvc->get_lsa_size(ct3d)) { - *len = 0; + *len_out = 0; return CXL_MBOX_INVALID_INPUT; } - *len = cvc->get_lsa(ct3d, get_lsa, length, offset); + *len_out = cvc->get_lsa(ct3d, payload_out, length, offset); return CXL_MBOX_SUCCESS; } -static ret_code cmd_ccls_set_lsa(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +/* CXL r3.1 Section 8.2.9.9.2.4: Set LSA (Opcode 4103h) */ +static CXLRetCode cmd_ccls_set_lsa(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) { struct set_lsa_pl { uint32_t offset; uint32_t rsvd; uint8_t data[]; } QEMU_PACKED; - struct set_lsa_pl *set_lsa_payload = (void *)cmd->payload; - CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); + struct set_lsa_pl *set_lsa_payload = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); const size_t hdr_len = offsetof(struct set_lsa_pl, data); - uint16_t plen = *len; - *len = 0; - if (!plen) { + *len_out = 0; + if (!len_in) { return CXL_MBOX_SUCCESS; } - if (set_lsa_payload->offset + plen > cvc->get_lsa_size(ct3d) + hdr_len) { + if (set_lsa_payload->offset + len_in > cvc->get_lsa_size(ct3d) + hdr_len) { return CXL_MBOX_INVALID_INPUT; } - plen -= hdr_len; + len_in -= hdr_len; + + cvc->set_lsa(ct3d, set_lsa_payload->data, len_in, set_lsa_payload->offset); + return CXL_MBOX_SUCCESS; +} + +/* Perform the actual device zeroing */ +static void __do_sanitization(CXLType3Dev *ct3d) +{ + MemoryRegion *mr; + + if (ct3d->hostvmem) { + mr = host_memory_backend_get_memory(ct3d->hostvmem); + if (mr) { + void *hostmem = memory_region_get_ram_ptr(mr); + memset(hostmem, 0, memory_region_size(mr)); + } + } + + if (ct3d->hostpmem) { + mr = host_memory_backend_get_memory(ct3d->hostpmem); + if (mr) { + void *hostmem = memory_region_get_ram_ptr(mr); + memset(hostmem, 0, memory_region_size(mr)); + } + } + if (ct3d->lsa) { + mr = host_memory_backend_get_memory(ct3d->lsa); + if (mr) { + void *lsa = memory_region_get_ram_ptr(mr); + memset(lsa, 0, memory_region_size(mr)); + } + } +} + +/* + * CXL r3.1 Section 8.2.9.9.5.1: Sanitize (Opcode 4400h) + * + * Once the Sanitize command has started successfully, the device shall be + * placed in the media disabled state. If the command fails or is interrupted + * by a reset or power failure, it shall remain in the media disabled state + * until a successful Sanitize command has been completed. During this state: + * + * 1. Memory writes to the device will have no effect, and all memory reads + * will return random values (no user data returned, even for locations that + * the failed Sanitize operation didn’t sanitize yet). + * + * 2. Mailbox commands shall still be processed in the disabled state, except + * that commands that access Sanitized areas shall fail with the Media Disabled + * error code. + */ +static CXLRetCode cmd_sanitize_overwrite(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + uint64_t total_mem; /* in Mb */ + int secs; + + total_mem = (ct3d->cxl_dstate.vmem_size + ct3d->cxl_dstate.pmem_size) >> 20; + if (total_mem <= 512) { + secs = 4; + } else if (total_mem <= 1024) { + secs = 8; + } else if (total_mem <= 2 * 1024) { + secs = 15; + } else if (total_mem <= 4 * 1024) { + secs = 30; + } else if (total_mem <= 8 * 1024) { + secs = 60; + } else if (total_mem <= 16 * 1024) { + secs = 2 * 60; + } else if (total_mem <= 32 * 1024) { + secs = 4 * 60; + } else if (total_mem <= 64 * 1024) { + secs = 8 * 60; + } else if (total_mem <= 128 * 1024) { + secs = 15 * 60; + } else if (total_mem <= 256 * 1024) { + secs = 30 * 60; + } else if (total_mem <= 512 * 1024) { + secs = 60 * 60; + } else if (total_mem <= 1024 * 1024) { + secs = 120 * 60; + } else { + secs = 240 * 60; /* max 4 hrs */ + } + + /* EBUSY other bg cmds as of now */ + cci->bg.runtime = secs * 1000UL; + *len_out = 0; + + cxl_dev_disable_media(&ct3d->cxl_dstate); + + /* sanitize when done */ + return CXL_MBOX_BG_STARTED; +} + +static CXLRetCode cmd_get_security_state(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + uint32_t *state = (uint32_t *)payload_out; + + *state = 0; + *len_out = 4; + return CXL_MBOX_SUCCESS; +} + +/* + * CXL r3.1 Section 8.2.9.9.4.1: Get Poison List (Opcode 4300h) + * + * This is very inefficient, but good enough for now! + * Also the payload will always fit, so no need to handle the MORE flag and + * make this stateful. We may want to allow longer poison lists to aid + * testing that kernel functionality. + */ +static CXLRetCode cmd_media_get_poison_list(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct get_poison_list_pl { + uint64_t pa; + uint64_t length; + } QEMU_PACKED; + + struct get_poison_list_out_pl { + uint8_t flags; + uint8_t rsvd1; + uint64_t overflow_timestamp; + uint16_t count; + uint8_t rsvd2[0x14]; + struct { + uint64_t addr; + uint32_t length; + uint32_t resv; + } QEMU_PACKED records[]; + } QEMU_PACKED; + + struct get_poison_list_pl *in = (void *)payload_in; + struct get_poison_list_out_pl *out = (void *)payload_out; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + uint16_t record_count = 0, i = 0; + uint64_t query_start, query_length; + CXLPoisonList *poison_list = &ct3d->poison_list; + CXLPoison *ent; + uint16_t out_pl_len; + + query_start = ldq_le_p(&in->pa); + /* 64 byte alignment required */ + if (query_start & 0x3f) { + return CXL_MBOX_INVALID_INPUT; + } + query_length = ldq_le_p(&in->length) * CXL_CACHE_LINE_SIZE; + + QLIST_FOREACH(ent, poison_list, node) { + /* Check for no overlap */ + if (ent->start >= query_start + query_length || + ent->start + ent->length <= query_start) { + continue; + } + record_count++; + } + out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); + assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE); + + memset(out, 0, out_pl_len); + QLIST_FOREACH(ent, poison_list, node) { + uint64_t start, stop; + + /* Check for no overlap */ + if (ent->start >= query_start + query_length || + ent->start + ent->length <= query_start) { + continue; + } + + /* Deal with overlap */ + start = MAX(ROUND_DOWN(ent->start, 64ull), query_start); + stop = MIN(ROUND_DOWN(ent->start, 64ull) + ent->length, + query_start + query_length); + stq_le_p(&out->records[i].addr, start | (ent->type & 0x7)); + stl_le_p(&out->records[i].length, (stop - start) / CXL_CACHE_LINE_SIZE); + i++; + } + if (ct3d->poison_list_overflowed) { + out->flags = (1 << 1); + stq_le_p(&out->overflow_timestamp, ct3d->poison_list_overflow_ts); + } + stw_le_p(&out->count, record_count); + *len_out = out_pl_len; + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.1 Section 8.2.9.9.4.2: Inject Poison (Opcode 4301h) */ +static CXLRetCode cmd_media_inject_poison(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLPoisonList *poison_list = &ct3d->poison_list; + CXLPoison *ent; + struct inject_poison_pl { + uint64_t dpa; + }; + struct inject_poison_pl *in = (void *)payload_in; + uint64_t dpa = ldq_le_p(&in->dpa); + CXLPoison *p; + + QLIST_FOREACH(ent, poison_list, node) { + if (dpa >= ent->start && + dpa + CXL_CACHE_LINE_SIZE <= ent->start + ent->length) { + return CXL_MBOX_SUCCESS; + } + } + + if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) { + return CXL_MBOX_INJECT_POISON_LIMIT; + } + p = g_new0(CXLPoison, 1); + + p->length = CXL_CACHE_LINE_SIZE; + p->start = dpa; + p->type = CXL_POISON_TYPE_INJECTED; + + /* + * Possible todo: Merge with existing entry if next to it and if same type + */ + QLIST_INSERT_HEAD(poison_list, p, node); + ct3d->poison_list_cnt++; + *len_out = 0; + + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.1 Section 8.2.9.9.4.3: Clear Poison (Opcode 4302h */ +static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; + CXLPoisonList *poison_list = &ct3d->poison_list; + CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); + struct clear_poison_pl { + uint64_t dpa; + uint8_t data[64]; + }; + CXLPoison *ent; + uint64_t dpa; + + struct clear_poison_pl *in = (void *)payload_in; + + dpa = ldq_le_p(&in->dpa); + if (dpa + CXL_CACHE_LINE_SIZE > cxl_dstate->mem_size) { + return CXL_MBOX_INVALID_PA; + } + + /* Clearing a region with no poison is not an error so always do so */ + if (cvc->set_cacheline) { + if (!cvc->set_cacheline(ct3d, dpa, in->data)) { + return CXL_MBOX_INTERNAL_ERROR; + } + } + + QLIST_FOREACH(ent, poison_list, node) { + /* + * Test for contained in entry. Simpler than general case + * as clearing 64 bytes and entries 64 byte aligned + */ + if ((dpa >= ent->start) && (dpa < ent->start + ent->length)) { + break; + } + } + if (!ent) { + return CXL_MBOX_SUCCESS; + } + + QLIST_REMOVE(ent, node); + ct3d->poison_list_cnt--; + + if (dpa > ent->start) { + CXLPoison *frag; + /* Cannot overflow as replacing existing entry */ + + frag = g_new0(CXLPoison, 1); + + frag->start = ent->start; + frag->length = dpa - ent->start; + frag->type = ent->type; + + QLIST_INSERT_HEAD(poison_list, frag, node); + ct3d->poison_list_cnt++; + } + + if (dpa + CXL_CACHE_LINE_SIZE < ent->start + ent->length) { + CXLPoison *frag; + + if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) { + cxl_set_poison_list_overflowed(ct3d); + } else { + frag = g_new0(CXLPoison, 1); + + frag->start = dpa + CXL_CACHE_LINE_SIZE; + frag->length = ent->start + ent->length - frag->start; + frag->type = ent->type; + QLIST_INSERT_HEAD(poison_list, frag, node); + ct3d->poison_list_cnt++; + } + } + /* Any fragments have been added, free original entry */ + g_free(ent); + *len_out = 0; - cvc->set_lsa(ct3d, set_lsa_payload->data, plen, set_lsa_payload->offset); return CXL_MBOX_SUCCESS; } @@ -386,93 +1239,265 @@ static ret_code cmd_ccls_set_lsa(struct cxl_cmd *cmd, #define IMMEDIATE_DATA_CHANGE (1 << 2) #define IMMEDIATE_POLICY_CHANGE (1 << 3) #define IMMEDIATE_LOG_CHANGE (1 << 4) +#define SECURITY_STATE_CHANGE (1 << 5) +#define BACKGROUND_OPERATION (1 << 6) -static struct cxl_cmd cxl_cmd_set[256][256] = { +static const struct cxl_cmd cxl_cmd_set[256][256] = { [EVENTS][GET_RECORDS] = { "EVENTS_GET_RECORDS", cmd_events_get_records, 1, 0 }, [EVENTS][CLEAR_RECORDS] = { "EVENTS_CLEAR_RECORDS", cmd_events_clear_records, ~0, IMMEDIATE_LOG_CHANGE }, [EVENTS][GET_INTERRUPT_POLICY] = { "EVENTS_GET_INTERRUPT_POLICY", - cmd_events_get_interrupt_policy, 0, 0 }, + cmd_events_get_interrupt_policy, 0, 0 }, [EVENTS][SET_INTERRUPT_POLICY] = { "EVENTS_SET_INTERRUPT_POLICY", - cmd_events_set_interrupt_policy, 4, IMMEDIATE_CONFIG_CHANGE }, + cmd_events_set_interrupt_policy, + ~0, IMMEDIATE_CONFIG_CHANGE }, [FIRMWARE_UPDATE][GET_INFO] = { "FIRMWARE_UPDATE_GET_INFO", cmd_firmware_update_get_info, 0, 0 }, [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, - [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 8, IMMEDIATE_POLICY_CHANGE }, - [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, 0 }, + [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, + 8, IMMEDIATE_POLICY_CHANGE }, + [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, + 0, 0 }, [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, [IDENTIFY][MEMORY_DEVICE] = { "IDENTIFY_MEMORY_DEVICE", cmd_identify_memory_device, 0, 0 }, [CCLS][GET_PARTITION_INFO] = { "CCLS_GET_PARTITION_INFO", cmd_ccls_get_partition_info, 0, 0 }, - [CCLS][GET_LSA] = { "CCLS_GET_LSA", cmd_ccls_get_lsa, 0, 0 }, + [CCLS][GET_LSA] = { "CCLS_GET_LSA", cmd_ccls_get_lsa, 8, 0 }, [CCLS][SET_LSA] = { "CCLS_SET_LSA", cmd_ccls_set_lsa, ~0, IMMEDIATE_CONFIG_CHANGE | IMMEDIATE_DATA_CHANGE }, + [SANITIZE][OVERWRITE] = { "SANITIZE_OVERWRITE", cmd_sanitize_overwrite, 0, + IMMEDIATE_DATA_CHANGE | SECURITY_STATE_CHANGE | BACKGROUND_OPERATION }, + [PERSISTENT_MEM][GET_SECURITY_STATE] = { "GET_SECURITY_STATE", + cmd_get_security_state, 0, 0 }, + [MEDIA_AND_POISON][GET_POISON_LIST] = { "MEDIA_AND_POISON_GET_POISON_LIST", + cmd_media_get_poison_list, 16, 0 }, + [MEDIA_AND_POISON][INJECT_POISON] = { "MEDIA_AND_POISON_INJECT_POISON", + cmd_media_inject_poison, 8, 0 }, + [MEDIA_AND_POISON][CLEAR_POISON] = { "MEDIA_AND_POISON_CLEAR_POISON", + cmd_media_clear_poison, 72, 0 }, }; -void cxl_process_mailbox(CXLDeviceState *cxl_dstate) -{ - uint16_t ret = CXL_MBOX_SUCCESS; - struct cxl_cmd *cxl_cmd; - uint64_t status_reg; - opcode_handler h; - uint64_t command_reg = cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD]; +static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { + [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0 }, + [INFOSTAT][BACKGROUND_OPERATION_STATUS] = { "BACKGROUND_OPERATION_STATUS", + cmd_infostat_bg_op_sts, 0, 0 }, + [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, + [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 0, + IMMEDIATE_POLICY_CHANGE }, + [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, + 0 }, + [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, + [PHYSICAL_SWITCH][IDENTIFY_SWITCH_DEVICE] = { "IDENTIFY_SWITCH_DEVICE", + cmd_identify_switch_device, 0, 0 }, + [PHYSICAL_SWITCH][GET_PHYSICAL_PORT_STATE] = { "SWITCH_PHYSICAL_PORT_STATS", + cmd_get_physical_port_state, ~0, 0 }, + [TUNNEL][MANAGEMENT_COMMAND] = { "TUNNEL_MANAGEMENT_COMMAND", + cmd_tunnel_management_cmd, ~0, 0 }, +}; - uint8_t set = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND_SET); - uint8_t cmd = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND); - uint16_t len = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, LENGTH); - cxl_cmd = &cxl_cmd_set[set][cmd]; +/* + * While the command is executing in the background, the device should + * update the percentage complete in the Background Command Status Register + * at least once per second. + */ + +#define CXL_MBOX_BG_UPDATE_FREQ 1000UL + +int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, + size_t len_in, uint8_t *pl_in, size_t *len_out, + uint8_t *pl_out, bool *bg_started) +{ + int ret; + const struct cxl_cmd *cxl_cmd; + opcode_handler h; + + *len_out = 0; + cxl_cmd = &cci->cxl_cmd_set[set][cmd]; h = cxl_cmd->handler; - if (h) { - if (len == cxl_cmd->in) { - cxl_cmd->payload = cxl_dstate->mbox_reg_state + - A_CXL_DEV_CMD_PAYLOAD; - ret = (*h)(cxl_cmd, cxl_dstate, &len); - assert(len <= cxl_dstate->payload_size); - } else { - ret = CXL_MBOX_INVALID_PAYLOAD_LENGTH; - } - } else { + if (!h) { qemu_log_mask(LOG_UNIMP, "Command %04xh not implemented\n", set << 8 | cmd); - ret = CXL_MBOX_UNSUPPORTED; + return CXL_MBOX_UNSUPPORTED; } - /* Set the return code */ - status_reg = FIELD_DP64(0, CXL_DEV_MAILBOX_STS, ERRNO, ret); + if (len_in != cxl_cmd->in && cxl_cmd->in != ~0) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } - /* Set the return length */ - command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND_SET, 0); - command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND, 0); - command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD, LENGTH, len); + /* Only one bg command at a time */ + if ((cxl_cmd->effect & BACKGROUND_OPERATION) && + cci->bg.runtime > 0) { + return CXL_MBOX_BUSY; + } - cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD] = command_reg; - cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_STS] = status_reg; + /* forbid any selected commands while overwriting */ + if (sanitize_running(cci)) { + if (h == cmd_events_get_records || + h == cmd_ccls_get_partition_info || + h == cmd_ccls_set_lsa || + h == cmd_ccls_get_lsa || + h == cmd_logs_get_log || + h == cmd_media_get_poison_list || + h == cmd_media_inject_poison || + h == cmd_media_clear_poison || + h == cmd_sanitize_overwrite) { + return CXL_MBOX_MEDIA_DISABLED; + } + } - /* Tell the host we're done */ - ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CTRL, - DOORBELL, 0); + ret = (*h)(cxl_cmd, pl_in, len_in, pl_out, len_out, cci); + if ((cxl_cmd->effect & BACKGROUND_OPERATION) && + ret == CXL_MBOX_BG_STARTED) { + *bg_started = true; + } else { + *bg_started = false; + } + + /* Set bg and the return code */ + if (*bg_started) { + uint64_t now; + + cci->bg.opcode = (set << 8) | cmd; + + cci->bg.complete_pct = 0; + cci->bg.ret_code = 0; + + now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); + cci->bg.starttime = now; + timer_mod(cci->bg.timer, now + CXL_MBOX_BG_UPDATE_FREQ); + } + + return ret; } -int cxl_initialize_mailbox(CXLDeviceState *cxl_dstate) +static void bg_timercb(void *opaque) { - /* CXL 2.0: Table 169 Get Supported Logs Log Entry */ - const char *cel_uuidstr = "0da9c0b5-bf41-4b78-8f79-96b1623b3f17"; + CXLCCI *cci = opaque; + uint64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); + uint64_t total_time = cci->bg.starttime + cci->bg.runtime; + assert(cci->bg.runtime > 0); + + if (now >= total_time) { /* we are done */ + uint16_t ret = CXL_MBOX_SUCCESS; + + cci->bg.complete_pct = 100; + cci->bg.ret_code = ret; + switch (cci->bg.opcode) { + case 0x4400: /* sanitize */ + { + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + + __do_sanitization(ct3d); + cxl_dev_enable_media(&ct3d->cxl_dstate); + } + break; + case 0x4304: /* TODO: scan media */ + break; + default: + __builtin_unreachable(); + break; + } + } else { + /* estimate only */ + cci->bg.complete_pct = 100 * now / total_time; + timer_mod(cci->bg.timer, now + CXL_MBOX_BG_UPDATE_FREQ); + } + + if (cci->bg.complete_pct == 100) { + /* TODO: generalize to switch CCI */ + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; + PCIDevice *pdev = PCI_DEVICE(cci->d); + + cci->bg.starttime = 0; + /* registers are updated, allow new bg-capable cmds */ + cci->bg.runtime = 0; + + if (msix_enabled(pdev)) { + msix_notify(pdev, cxl_dstate->mbox_msi_n); + } else if (msi_enabled(pdev)) { + msi_notify(pdev, cxl_dstate->mbox_msi_n); + } + } +} + +void cxl_init_cci(CXLCCI *cci, size_t payload_max) +{ + cci->payload_max = payload_max; for (int set = 0; set < 256; set++) { for (int cmd = 0; cmd < 256; cmd++) { - if (cxl_cmd_set[set][cmd].handler) { - struct cxl_cmd *c = &cxl_cmd_set[set][cmd]; + if (cci->cxl_cmd_set[set][cmd].handler) { + const struct cxl_cmd *c = &cci->cxl_cmd_set[set][cmd]; struct cel_log *log = - &cxl_dstate->cel_log[cxl_dstate->cel_size]; + &cci->cel_log[cci->cel_size]; log->opcode = (set << 8) | cmd; log->effect = c->effect; - cxl_dstate->cel_size++; + cci->cel_size++; } } } - - return qemu_uuid_parse(cel_uuidstr, &cel_uuid); + cci->bg.complete_pct = 0; + cci->bg.starttime = 0; + cci->bg.runtime = 0; + cci->bg.timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, + bg_timercb, cci); +} + +void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf, + DeviceState *d, size_t payload_max) +{ + cci->cxl_cmd_set = cxl_cmd_set_sw; + cci->d = d; + cci->intf = intf; + cxl_init_cci(cci, payload_max); +} + +void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max) +{ + cci->cxl_cmd_set = cxl_cmd_set; + cci->d = d; + + /* No separation for PCI MB as protocol handled in PCI device */ + cci->intf = d; + cxl_init_cci(cci, payload_max); +} + +static const struct cxl_cmd cxl_cmd_set_t3_ld[256][256] = { + [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0 }, + [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, + 0 }, + [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, +}; + +void cxl_initialize_t3_ld_cci(CXLCCI *cci, DeviceState *d, DeviceState *intf, + size_t payload_max) +{ + cci->cxl_cmd_set = cxl_cmd_set_t3_ld; + cci->d = d; + cci->intf = intf; + cxl_init_cci(cci, payload_max); +} + +static const struct cxl_cmd cxl_cmd_set_t3_fm_owned_ld_mctp[256][256] = { + [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0}, + [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, + 0 }, + [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, + [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, + [TUNNEL][MANAGEMENT_COMMAND] = { "TUNNEL_MANAGEMENT_COMMAND", + cmd_tunnel_management_cmd, ~0, 0 }, +}; + +void cxl_initialize_t3_fm_owned_ld_mctpcci(CXLCCI *cci, DeviceState *d, + DeviceState *intf, + size_t payload_max) +{ + cci->cxl_cmd_set = cxl_cmd_set_t3_fm_owned_ld_mctp; + cci->d = d; + cci->intf = intf; + cxl_init_cci(cci, payload_max); } diff --git a/hw/cxl/meson.build b/hw/cxl/meson.build index f117b99949..3e375f61a9 100644 --- a/hw/cxl/meson.build +++ b/hw/cxl/meson.build @@ -1,12 +1,13 @@ -softmmu_ss.add(when: 'CONFIG_CXL', +system_ss.add(when: 'CONFIG_CXL', if_true: files( 'cxl-component-utils.c', 'cxl-device-utils.c', 'cxl-mailbox-utils.c', 'cxl-host.c', + 'cxl-cdat.c', + 'cxl-events.c', + 'switch-mailbox-cci.c', ), if_false: files( 'cxl-host-stubs.c', )) - -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('cxl-host-stubs.c')) diff --git a/hw/cxl/switch-mailbox-cci.c b/hw/cxl/switch-mailbox-cci.c new file mode 100644 index 0000000000..ba399c6240 --- /dev/null +++ b/hw/cxl/switch-mailbox-cci.c @@ -0,0 +1,111 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Emulation of a CXL Switch Mailbox CCI PCIe function. + * + * Copyright (c) 2023 Huawei Technologies. + * + * From www.computeexpresslink.org + * Compute Express Link (CXL) Specification revision 3.0 Version 1.0 + */ +#include "qemu/osdep.h" +#include "hw/pci/pci.h" +#include "hw/pci-bridge/cxl_upstream_port.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/qdev-properties.h" +#include "hw/cxl/cxl.h" + +static void cswmbcci_reset(DeviceState *dev) +{ + CSWMBCCIDev *cswmb = CXL_SWITCH_MAILBOX_CCI(dev); + cxl_device_register_init_swcci(cswmb); +} + +static void cswbcci_realize(PCIDevice *pci_dev, Error **errp) +{ + CSWMBCCIDev *cswmb = CXL_SWITCH_MAILBOX_CCI(pci_dev); + CXLComponentState *cxl_cstate = &cswmb->cxl_cstate; + CXLDeviceState *cxl_dstate = &cswmb->cxl_dstate; + CXLDVSECRegisterLocator *regloc_dvsec; + CXLUpstreamPort *usp; + + if (!cswmb->target) { + error_setg(errp, "Target not set"); + return; + } + usp = CXL_USP(cswmb->target); + + pcie_endpoint_cap_init(pci_dev, 0x80); + cxl_cstate->dvsec_offset = 0x100; + cxl_cstate->pdev = pci_dev; + cswmb->cci = &usp->swcci; + cxl_device_register_block_init(OBJECT(pci_dev), cxl_dstate, cswmb->cci); + pci_register_bar(pci_dev, 0, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, + &cxl_dstate->device_registers); + regloc_dvsec = &(CXLDVSECRegisterLocator) { + .rsvd = 0, + .reg0_base_lo = RBI_CXL_DEVICE_REG | 0, + .reg0_base_hi = 0, + }; + cxl_component_create_dvsec(cxl_cstate, CXL3_SWITCH_MAILBOX_CCI, + REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC, + REG_LOC_DVSEC_REVID, (uint8_t *)regloc_dvsec); + + cxl_initialize_mailbox_swcci(cswmb->cci, DEVICE(pci_dev), + DEVICE(cswmb->target), + CXL_MAILBOX_MAX_PAYLOAD_SIZE); +} + +static void cswmbcci_exit(PCIDevice *pci_dev) +{ + /* Nothing to do here yet */ +} + +static Property cxl_switch_cci_props[] = { + DEFINE_PROP_LINK("target", CSWMBCCIDev, + target, TYPE_CXL_USP, PCIDevice *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void cswmbcci_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); + + pc->realize = cswbcci_realize; + pc->exit = cswmbcci_exit; + /* Serial bus, CXL Switch CCI */ + pc->class_id = 0x0c0b; + /* + * Huawei Technologies + * CXL Switch Mailbox CCI - DID assigned for emulation only. + * No real hardware will ever use this ID. + */ + pc->vendor_id = 0x19e5; + pc->device_id = 0xa123; + pc->revision = 0; + dc->desc = "CXL Switch Mailbox CCI"; + dc->reset = cswmbcci_reset; + device_class_set_props(dc, cxl_switch_cci_props); +} + +static const TypeInfo cswmbcci_info = { + .name = TYPE_CXL_SWITCH_MAILBOX_CCI, + .parent = TYPE_PCI_DEVICE, + .class_init = cswmbcci_class_init, + .instance_size = sizeof(CSWMBCCIDev), + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, +}; + +static void cxl_switch_mailbox_cci_register(void) +{ + type_register_static(&cswmbcci_info); +} +type_init(cxl_switch_mailbox_cci_register); diff --git a/hw/display/Kconfig b/hw/display/Kconfig index a1b159becd..234c7de027 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -55,7 +55,7 @@ config VGA_MMIO config VMWARE_VGA bool - default y if PCI_DEVICES + default y if PCI_DEVICES && (PC_PCI || MIPS) depends on PCI select VGA @@ -77,6 +77,7 @@ config SM501 select I2C select DDC select SERIAL + select USB_OHCI_SYSBUS config TCX bool @@ -93,7 +94,7 @@ config VGA config QXL bool - depends on SPICE && PCI + depends on SPICE && PCI && PIXMAN select VGA config VIRTIO_GPU @@ -134,3 +135,8 @@ config MACFB bool select FRAMEBUFFER depends on NUBUS + +config XLNX_DISPLAYPORT + bool + # defaults to "N", enabled by specific boards + depends on PIXMAN diff --git a/hw/display/acpi-vga-stub.c b/hw/display/acpi-vga-stub.c new file mode 100644 index 0000000000..a9b0ecf76d --- /dev/null +++ b/hw/display/acpi-vga-stub.c @@ -0,0 +1,7 @@ +#include "qemu/osdep.h" +#include "hw/acpi/acpi_aml_interface.h" +#include "vga_int.h" + +void build_vga_aml(AcpiDevAmlIf *adev, Aml *scope) +{ +} diff --git a/hw/display/acpi-vga.c b/hw/display/acpi-vga.c new file mode 100644 index 0000000000..f0e9ef1fcf --- /dev/null +++ b/hw/display/acpi-vga.c @@ -0,0 +1,26 @@ +#include "qemu/osdep.h" +#include "hw/acpi/acpi_aml_interface.h" +#include "hw/pci/pci.h" +#include "vga_int.h" + +void build_vga_aml(AcpiDevAmlIf *adev, Aml *scope) +{ + int s3d = 0; + Aml *method; + + if (object_dynamic_cast(OBJECT(adev), "qxl-vga")) { + s3d = 3; + } + + method = aml_method("_S1D", 0, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_int(0))); + aml_append(scope, method); + + method = aml_method("_S2D", 0, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_int(0))); + aml_append(scope, method); + + method = aml_method("_S3D", 0, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_int(s3d))); + aml_append(scope, method); +} diff --git a/hw/display/artist.c b/hw/display/artist.c index eadaef0d46..d9134532fb 100644 --- a/hw/display/artist.c +++ b/hw/display/artist.c @@ -1358,7 +1358,7 @@ static void artist_create_buffer(ARTISTState *s, const char *name, { struct vram_buffer *buf = s->vram_buffer + idx; - memory_region_init_ram(&buf->mr, NULL, name, width * height, + memory_region_init_ram(&buf->mr, OBJECT(s), name, width * height, &error_fatal); memory_region_add_subregion_overlap(&s->mem_as_root, *offset, &buf->mr, 0); @@ -1435,7 +1435,7 @@ static const VMStateDescription vmstate_artist = { .version_id = 2, .minimum_version_id = 2, .post_load = vmstate_artist_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(height, ARTISTState), VMSTATE_UINT16(width, ARTISTState), VMSTATE_UINT16(depth, ARTISTState), diff --git a/hw/display/ati.c b/hw/display/ati.c index 6e38e00502..8d2501bd82 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -32,6 +32,12 @@ #define ATI_DEBUG_HW_CURSOR 0 +#ifdef CONFIG_PIXMAN +#define DEFAULT_X_PIXMAN 3 +#else +#define DEFAULT_X_PIXMAN 0 +#endif + static const struct { const char *name; uint16_t dev_id; @@ -319,11 +325,13 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size) case DAC_CNTL: val = s->regs.dac_cntl; break; - case GPIO_VGA_DDC: - val = s->regs.gpio_vga_ddc; + case GPIO_VGA_DDC ... GPIO_VGA_DDC + 3: + val = ati_reg_read_offs(s->regs.gpio_vga_ddc, + addr - GPIO_VGA_DDC, size); break; - case GPIO_DVI_DDC: - val = s->regs.gpio_dvi_ddc; + case GPIO_DVI_DDC ... GPIO_DVI_DDC + 3: + val = ati_reg_read_offs(s->regs.gpio_dvi_ddc, + addr - GPIO_DVI_DDC, size); break; case GPIO_MONID ... GPIO_MONID + 3: val = ati_reg_read_offs(s->regs.gpio_monid, @@ -337,6 +345,9 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size) case PALETTE_DATA: val = vga_ioport_read(&s->vga, VGA_PEL_D); break; + case PALETTE_30_DATA: + val = s->regs.palette[vga_ioport_read(&s->vga, VGA_PEL_IR)]; + break; case CNFG_CNTL: val = s->regs.config_cntl; break; @@ -349,14 +360,17 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size) PCI_BASE_ADDRESS_0, size) & 0xfffffff0; break; case CONFIG_APER_SIZE: - val = s->vga.vram_size; + val = s->vga.vram_size / 2; break; case CONFIG_REG_1_BASE: val = pci_default_read_config(&s->dev, PCI_BASE_ADDRESS_2, size) & 0xfffffff0; break; case CONFIG_REG_APER_SIZE: - val = memory_region_size(&s->mm); + val = memory_region_size(&s->mm) / 2; + break; + case HOST_PATH_CNTL: + val = BIT(23); /* Radeon HDP_APER_CNTL */ break; case MC_STATUS: val = 5; @@ -612,29 +626,34 @@ static void ati_mm_write(void *opaque, hwaddr addr, s->regs.dac_cntl = data & 0xffffe3ff; s->vga.dac_8bit = !!(data & DAC_8BIT_EN); break; - case GPIO_VGA_DDC: + /* + * GPIO regs for DDC access. Because some drivers access these via + * multiple byte writes we have to be careful when we send bits to + * avoid spurious changes in bitbang_i2c state. Only do it when either + * the enable bits are changed or output bits changed while enabled. + */ + case GPIO_VGA_DDC ... GPIO_VGA_DDC + 3: if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF) { /* FIXME: Maybe add a property to select VGA or DVI port? */ } break; - case GPIO_DVI_DDC: + case GPIO_DVI_DDC ... GPIO_DVI_DDC + 3: if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF) { - s->regs.gpio_dvi_ddc = ati_i2c(&s->bbi2c, data, 0); + ati_reg_write_offs(&s->regs.gpio_dvi_ddc, + addr - GPIO_DVI_DDC, data, size); + if ((addr <= GPIO_DVI_DDC + 2 && addr + size > GPIO_DVI_DDC + 2) || + (addr == GPIO_DVI_DDC && (s->regs.gpio_dvi_ddc & 0x30000))) { + s->regs.gpio_dvi_ddc = ati_i2c(&s->bbi2c, + s->regs.gpio_dvi_ddc, 0); + } } break; case GPIO_MONID ... GPIO_MONID + 3: /* FIXME What does Radeon have here? */ if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + /* Rage128p accesses DDC via MONID(1-2) with additional mask bit */ ati_reg_write_offs(&s->regs.gpio_monid, addr - GPIO_MONID, data, size); - /* - * Rage128p accesses DDC used to get EDID via these bits. - * Because some drivers access this via multiple byte writes - * we have to be careful when we send bits to avoid spurious - * changes in bitbang_i2c state. So only do it when mask is set - * and either the enable bits are changed or output bits changed - * while enabled. - */ if ((s->regs.gpio_monid & BIT(25)) && ((addr <= GPIO_MONID + 2 && addr + size > GPIO_MONID + 2) || (addr == GPIO_MONID && (s->regs.gpio_monid & 0x60000)))) { @@ -663,6 +682,12 @@ static void ati_mm_write(void *opaque, hwaddr addr, data >>= 8; vga_ioport_write(&s->vga, VGA_PEL_D, data & 0xff); break; + case PALETTE_30_DATA: + s->regs.palette[vga_ioport_read(&s->vga, VGA_PEL_IW)] = data; + vga_ioport_write(&s->vga, VGA_PEL_D, (data >> 22) & 0xff); + vga_ioport_write(&s->vga, VGA_PEL_D, (data >> 12) & 0xff); + vga_ioport_write(&s->vga, VGA_PEL_D, (data >> 2) & 0xff); + break; case CNFG_CNTL: s->regs.config_cntl = data; break; @@ -927,6 +952,12 @@ static void ati_vga_realize(PCIDevice *dev, Error **errp) ATIVGAState *s = ATI_VGA(dev); VGACommonState *vga = &s->vga; +#ifndef CONFIG_PIXMAN + if (s->use_pixman != 0) { + warn_report("x-pixman != 0, not effective without PIXMAN"); + } +#endif + if (s->model) { int i; for (i = 0; i < ARRAY_SIZE(ati_model_aliases); i++) { @@ -960,7 +991,7 @@ static void ati_vga_realize(PCIDevice *dev, Error **errp) } vga_init(vga, OBJECT(s), pci_address_space(dev), pci_address_space_io(dev), true); - vga->con = graphic_console_init(DEVICE(s), 0, s->vga.hw_ops, &s->vga); + vga->con = graphic_console_init(DEVICE(s), 0, s->vga.hw_ops, vga); if (s->cursor_guest_mode) { vga->cursor_invalidate = ati_cursor_invalidate; vga->cursor_draw_line = ati_cursor_draw_line; @@ -1014,6 +1045,8 @@ static Property ati_vga_properties[] = { DEFINE_PROP_UINT16("x-device-id", ATIVGAState, dev_id, PCI_DEVICE_ID_ATI_RAGE128_PF), DEFINE_PROP_BOOL("guest_hwcursor", ATIVGAState, cursor_guest_mode, false), + /* this is a debug option, prefer PROP_UINT over PROP_BIT for simplicity */ + DEFINE_PROP_UINT8("x-pixman", ATIVGAState, use_pixman, DEFAULT_X_PIXMAN), DEFINE_PROP_END_OF_LIST() }; @@ -1035,11 +1068,18 @@ static void ati_vga_class_init(ObjectClass *klass, void *data) k->exit = ati_vga_exit; } +static void ati_vga_init(Object *o) +{ + object_property_set_description(o, "x-pixman", "Use pixman for: " + "1: fill, 2: blit"); +} + static const TypeInfo ati_vga_info = { .name = TYPE_ATI_VGA, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(ATIVGAState), .class_init = ati_vga_class_init, + .instance_init = ati_vga_init, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index 4dc10ea795..309bb5ccb6 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -12,6 +12,7 @@ #include "ati_regs.h" #include "qemu/log.h" #include "ui/pixel_ops.h" +#include "ui/console.h" /* * NOTE: @@ -84,13 +85,14 @@ void ati_2d_blt(ATIVGAState *s) DPRINTF("%d %d %d, %d %d %d, (%d,%d) -> (%d,%d) %dx%d %c %c\n", s->regs.src_offset, s->regs.dst_offset, s->regs.default_offset, s->regs.src_pitch, s->regs.dst_pitch, s->regs.default_pitch, - s->regs.src_x, s->regs.src_y, s->regs.dst_x, s->regs.dst_y, + s->regs.src_x, s->regs.src_y, dst_x, dst_y, s->regs.dst_width, s->regs.dst_height, (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ? '>' : '<'), (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ? 'v' : '^')); switch (s->regs.dp_mix & GMC_ROP3_MASK) { case ROP3_SRCCOPY: { + bool fallback = false; 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 ? @@ -121,27 +123,53 @@ void ati_2d_blt(ATIVGAState *s) src_bits, dst_bits, src_stride, dst_stride, bpp, bpp, src_x, src_y, dst_x, dst_y, s->regs.dst_width, s->regs.dst_height); - if (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT && +#ifdef CONFIG_PIXMAN + if ((s->use_pixman & BIT(1)) && + s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT && s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM) { - pixman_blt((uint32_t *)src_bits, (uint32_t *)dst_bits, - src_stride, dst_stride, bpp, bpp, - src_x, src_y, dst_x, dst_y, - s->regs.dst_width, s->regs.dst_height); - } else { + fallback = !pixman_blt((uint32_t *)src_bits, (uint32_t *)dst_bits, + src_stride, dst_stride, bpp, bpp, + src_x, src_y, dst_x, dst_y, + s->regs.dst_width, s->regs.dst_height); + } else if (s->use_pixman & BIT(1)) { /* FIXME: We only really need a temporary if src and dst overlap */ int llb = s->regs.dst_width * (bpp / 8); int tmp_stride = DIV_ROUND_UP(llb, sizeof(uint32_t)); uint32_t *tmp = g_malloc(tmp_stride * sizeof(uint32_t) * s->regs.dst_height); - pixman_blt((uint32_t *)src_bits, tmp, - src_stride, tmp_stride, bpp, bpp, - src_x, src_y, 0, 0, - s->regs.dst_width, s->regs.dst_height); - pixman_blt(tmp, (uint32_t *)dst_bits, - tmp_stride, dst_stride, bpp, bpp, - 0, 0, dst_x, dst_y, - s->regs.dst_width, s->regs.dst_height); + fallback = !pixman_blt((uint32_t *)src_bits, tmp, + src_stride, tmp_stride, bpp, bpp, + src_x, src_y, 0, 0, + s->regs.dst_width, s->regs.dst_height); + if (!fallback) { + fallback = !pixman_blt(tmp, (uint32_t *)dst_bits, + tmp_stride, dst_stride, bpp, bpp, + 0, 0, dst_x, dst_y, + s->regs.dst_width, s->regs.dst_height); + } g_free(tmp); + } else +#endif + { + fallback = true; + } + if (fallback) { + unsigned int y, i, j, bypp = bpp / 8; + unsigned int src_pitch = src_stride * sizeof(uint32_t); + unsigned int dst_pitch = dst_stride * sizeof(uint32_t); + + for (y = 0; y < s->regs.dst_height; y++) { + i = dst_x * bypp; + j = src_x * bypp; + if (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM) { + i += (dst_y + y) * dst_pitch; + j += (src_y + y) * src_pitch; + } else { + i += (dst_y + s->regs.dst_height - 1 - y) * dst_pitch; + j += (src_y + s->regs.dst_height - 1 - y) * src_pitch; + } + memmove(&dst_bits[i], &src_bits[j], s->regs.dst_width * bypp); + } } if (dst_bits >= s->vga.vram_ptr + s->vga.vbe_start_addr && dst_bits < s->vga.vram_ptr + s->vga.vbe_start_addr + @@ -179,14 +207,24 @@ void ati_2d_blt(ATIVGAState *s) dst_stride /= sizeof(uint32_t); DPRINTF("pixman_fill(%p, %d, %d, %d, %d, %d, %d, %x)\n", - dst_bits, dst_stride, bpp, - s->regs.dst_x, s->regs.dst_y, - s->regs.dst_width, s->regs.dst_height, - filler); - pixman_fill((uint32_t *)dst_bits, dst_stride, bpp, - s->regs.dst_x, s->regs.dst_y, - s->regs.dst_width, s->regs.dst_height, - filler); + dst_bits, dst_stride, bpp, dst_x, dst_y, + s->regs.dst_width, s->regs.dst_height, filler); +#ifdef CONFIG_PIXMAN + if (!(s->use_pixman & BIT(0)) || + !pixman_fill((uint32_t *)dst_bits, dst_stride, bpp, dst_x, dst_y, + s->regs.dst_width, s->regs.dst_height, filler)) +#endif + { + /* fallback when pixman failed or we don't want to call it */ + unsigned int x, y, i, bypp = bpp / 8; + unsigned int dst_pitch = dst_stride * sizeof(uint32_t); + for (y = 0; y < s->regs.dst_height; y++) { + i = dst_x * bypp + (dst_y + y) * dst_pitch; + for (x = 0; x < s->regs.dst_width; x++, i += bypp) { + stn_he_p(&dst_bits[i], bypp, filler); + } + } + } if (dst_bits >= s->vga.vram_ptr + s->vga.vbe_start_addr && dst_bits < s->vga.vram_ptr + s->vga.vbe_start_addr + s->vga.vbe_regs[VBE_DISPI_INDEX_YRES] * s->vga.vbe_line_offset) { diff --git a/hw/display/ati_dbg.c b/hw/display/ati_dbg.c index bd0ecd48c7..3ffa7f35df 100644 --- a/hw/display/ati_dbg.c +++ b/hw/display/ati_dbg.c @@ -30,6 +30,7 @@ static struct ati_regdesc ati_reg_names[] = { {"AMCGPIO_EN_MIR", 0x00a8}, {"PALETTE_INDEX", 0x00b0}, {"PALETTE_DATA", 0x00b4}, + {"PALETTE_30_DATA", 0x00b8}, {"CNFG_CNTL", 0x00e0}, {"GEN_RESET_CNTL", 0x00f0}, {"CNFG_MEMSIZE", 0x00f8}, @@ -38,6 +39,7 @@ static struct ati_regdesc ati_reg_names[] = { {"CONFIG_APER_SIZE", 0x0108}, {"CONFIG_REG_1_BASE", 0x010c}, {"CONFIG_REG_APER_SIZE", 0x0110}, + {"HOST_PATH_CNTL", 0x0130}, {"MEM_CNTL", 0x0140}, {"MC_FB_LOCATION", 0x0148}, {"MC_AGP_LOCATION", 0x014C}, diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h index 8acb9c7466..f5a47b82b0 100644 --- a/hw/display/ati_int.h +++ b/hw/display/ati_int.h @@ -10,7 +10,7 @@ #define ATI_INT_H #include "qemu/timer.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/i2c/bitbang_i2c.h" #include "vga_int.h" #include "qom/object.h" @@ -44,6 +44,7 @@ typedef struct ATIVGARegs { uint32_t gpio_dvi_ddc; uint32_t gpio_monid; uint32_t config_cntl; + uint32_t palette[256]; uint32_t crtc_h_total_disp; uint32_t crtc_h_sync_strt_wid; uint32_t crtc_v_total_disp; @@ -89,6 +90,7 @@ struct ATIVGAState { char *model; uint16_t dev_id; uint8_t mode; + uint8_t use_pixman; bool cursor_guest_mode; uint16_t cursor_size; uint32_t cursor_offset; diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h index d6282b2ef2..d7127748ff 100644 --- a/hw/display/ati_regs.h +++ b/hw/display/ati_regs.h @@ -48,6 +48,7 @@ #define AMCGPIO_EN_MIR 0x00a8 #define PALETTE_INDEX 0x00b0 #define PALETTE_DATA 0x00b4 +#define PALETTE_30_DATA 0x00b8 #define CNFG_CNTL 0x00e0 #define GEN_RESET_CNTL 0x00f0 #define CNFG_MEMSIZE 0x00f8 @@ -56,6 +57,7 @@ #define CONFIG_APER_SIZE 0x0108 #define CONFIG_REG_1_BASE 0x010c #define CONFIG_REG_APER_SIZE 0x0110 +#define HOST_PATH_CNTL 0x0130 #define MEM_CNTL 0x0140 #define MC_FB_LOCATION 0x0148 #define MC_AGP_LOCATION 0x014C diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index 088fc3d51c..e40ed2d2e1 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -279,8 +279,7 @@ static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value) newconf.xoffset = ldl_le_phys(&s->dma_as, value + 24); newconf.yoffset = ldl_le_phys(&s->dma_as, value + 28); - newconf.base = s->vcram_base | (value & 0xc0000000); - newconf.base += BCM2835_FB_OFFSET; + newconf.base = s->vcram_base + BCM2835_FB_OFFSET; /* Copy fields which we don't want to change from the existing config */ newconf.pixo = s->config.pixo; @@ -356,7 +355,7 @@ static const VMStateDescription vmstate_bcm2835_fb = { .name = TYPE_BCM2835_FB, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(lock, BCM2835FBState), VMSTATE_BOOL(invalidate, BCM2835FBState), VMSTATE_BOOL(pending, BCM2835FBState), diff --git a/hw/display/blizzard.c b/hw/display/blizzard.c index 105241577d..030abbe7d4 100644 --- a/hw/display/blizzard.c +++ b/hw/display/blizzard.c @@ -123,14 +123,14 @@ typedef struct { /* Bytes(!) per pixel */ static const int blizzard_iformat_bpp[0x10] = { 0, - 2, /* RGB 5:6:5*/ - 3, /* RGB 6:6:6 mode 1 */ - 3, /* RGB 8:8:8 mode 1 */ + 2, /* RGB 5:6:5*/ + 3, /* RGB 6:6:6 mode 1 */ + 3, /* RGB 8:8:8 mode 1 */ 0, 0, - 4, /* RGB 6:6:6 mode 2 */ - 4, /* RGB 8:8:8 mode 2 */ - 0, /* YUV 4:2:2 */ - 0, /* YUV 4:2:0 */ + 4, /* RGB 6:6:6 mode 2 */ + 4, /* RGB 8:8:8 mode 2 */ + 0, /* YUV 4:2:2 */ + 0, /* YUV 4:2:0 */ 0, 0, 0, 0, 0, 0, }; @@ -281,196 +281,196 @@ static uint16_t blizzard_reg_read(void *opaque, uint8_t reg) BlizzardState *s = (BlizzardState *) opaque; switch (reg) { - case 0x00: /* Revision Code */ + case 0x00: /* Revision Code */ return 0xa5; - case 0x02: /* Configuration Readback */ - return 0x83; /* Macrovision OK, CNF[2:0] = 3 */ + case 0x02: /* Configuration Readback */ + return 0x83; /* Macrovision OK, CNF[2:0] = 3 */ - case 0x04: /* PLL M-Divider */ + case 0x04: /* PLL M-Divider */ return (s->pll - 1) | (1 << 7); - case 0x06: /* PLL Lock Range Control */ + case 0x06: /* PLL Lock Range Control */ return s->pll_range; - case 0x08: /* PLL Lock Synthesis Control 0 */ + case 0x08: /* PLL Lock Synthesis Control 0 */ return s->pll_ctrl & 0xff; - case 0x0a: /* PLL Lock Synthesis Control 1 */ + case 0x0a: /* PLL Lock Synthesis Control 1 */ return s->pll_ctrl >> 8; - case 0x0c: /* PLL Mode Control 0 */ + case 0x0c: /* PLL Mode Control 0 */ return s->pll_mode; - case 0x0e: /* Clock-Source Select */ + case 0x0e: /* Clock-Source Select */ return s->clksel; - case 0x10: /* Memory Controller Activate */ - case 0x14: /* Memory Controller Bank 0 Status Flag */ + case 0x10: /* Memory Controller Activate */ + case 0x14: /* Memory Controller Bank 0 Status Flag */ return s->memenable; - case 0x18: /* Auto-Refresh Interval Setting 0 */ + case 0x18: /* Auto-Refresh Interval Setting 0 */ return s->memrefresh & 0xff; - case 0x1a: /* Auto-Refresh Interval Setting 1 */ + case 0x1a: /* Auto-Refresh Interval Setting 1 */ return s->memrefresh >> 8; - case 0x1c: /* Power-On Sequence Timing Control */ + case 0x1c: /* Power-On Sequence Timing Control */ return s->timing[0]; - case 0x1e: /* Timing Control 0 */ + case 0x1e: /* Timing Control 0 */ return s->timing[1]; - case 0x20: /* Timing Control 1 */ + case 0x20: /* Timing Control 1 */ return s->timing[2]; - case 0x24: /* Arbitration Priority Control */ + case 0x24: /* Arbitration Priority Control */ return s->priority; - case 0x28: /* LCD Panel Configuration */ + case 0x28: /* LCD Panel Configuration */ return s->lcd_config; - case 0x2a: /* LCD Horizontal Display Width */ + case 0x2a: /* LCD Horizontal Display Width */ return s->x >> 3; - case 0x2c: /* LCD Horizontal Non-display Period */ + case 0x2c: /* LCD Horizontal Non-display Period */ return s->hndp; - case 0x2e: /* LCD Vertical Display Height 0 */ + case 0x2e: /* LCD Vertical Display Height 0 */ return s->y & 0xff; - case 0x30: /* LCD Vertical Display Height 1 */ + case 0x30: /* LCD Vertical Display Height 1 */ return s->y >> 8; - case 0x32: /* LCD Vertical Non-display Period */ + case 0x32: /* LCD Vertical Non-display Period */ return s->vndp; - case 0x34: /* LCD HS Pulse-width */ + case 0x34: /* LCD HS Pulse-width */ return s->hsync; - case 0x36: /* LCd HS Pulse Start Position */ + case 0x36: /* LCd HS Pulse Start Position */ return s->skipx >> 3; - case 0x38: /* LCD VS Pulse-width */ + case 0x38: /* LCD VS Pulse-width */ return s->vsync; - case 0x3a: /* LCD VS Pulse Start Position */ + case 0x3a: /* LCD VS Pulse Start Position */ return s->skipy; - case 0x3c: /* PCLK Polarity */ + case 0x3c: /* PCLK Polarity */ return s->pclk; - case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */ + case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */ return s->hssi_config[0]; - case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */ + case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */ return s->hssi_config[1]; - case 0x42: /* High-speed Serial Interface Tx Mode */ + case 0x42: /* High-speed Serial Interface Tx Mode */ return s->hssi_config[2]; - case 0x44: /* TV Display Configuration */ + case 0x44: /* TV Display Configuration */ return s->tv_config; - case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits */ + case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits */ return s->tv_timing[(reg - 0x46) >> 1]; - case 0x4e: /* VBI: Closed Caption / XDS Control / Status */ + case 0x4e: /* VBI: Closed Caption / XDS Control / Status */ return s->vbi; - case 0x50: /* TV Horizontal Start Position */ + case 0x50: /* TV Horizontal Start Position */ return s->tv_x; - case 0x52: /* TV Vertical Start Position */ + case 0x52: /* TV Vertical Start Position */ return s->tv_y; - case 0x54: /* TV Test Pattern Setting */ + case 0x54: /* TV Test Pattern Setting */ return s->tv_test; - case 0x56: /* TV Filter Setting */ + case 0x56: /* TV Filter Setting */ return s->tv_filter_config; - case 0x58: /* TV Filter Coefficient Index */ + case 0x58: /* TV Filter Coefficient Index */ return s->tv_filter_idx; - case 0x5a: /* TV Filter Coefficient Data */ + case 0x5a: /* TV Filter Coefficient Data */ if (s->tv_filter_idx < 0x20) return s->tv_filter_coeff[s->tv_filter_idx ++]; return 0; - case 0x60: /* Input YUV/RGB Translate Mode 0 */ + case 0x60: /* Input YUV/RGB Translate Mode 0 */ return s->yrc[0]; - case 0x62: /* Input YUV/RGB Translate Mode 1 */ + case 0x62: /* Input YUV/RGB Translate Mode 1 */ return s->yrc[1]; - case 0x64: /* U Data Fix */ + case 0x64: /* U Data Fix */ return s->u; - case 0x66: /* V Data Fix */ + case 0x66: /* V Data Fix */ return s->v; - case 0x68: /* Display Mode */ + case 0x68: /* Display Mode */ return s->mode; - case 0x6a: /* Special Effects */ + case 0x6a: /* Special Effects */ return s->effect; - case 0x6c: /* Input Window X Start Position 0 */ + case 0x6c: /* Input Window X Start Position 0 */ return s->ix[0] & 0xff; - case 0x6e: /* Input Window X Start Position 1 */ + case 0x6e: /* Input Window X Start Position 1 */ return s->ix[0] >> 3; - case 0x70: /* Input Window Y Start Position 0 */ + case 0x70: /* Input Window Y Start Position 0 */ return s->ix[0] & 0xff; - case 0x72: /* Input Window Y Start Position 1 */ + case 0x72: /* Input Window Y Start Position 1 */ return s->ix[0] >> 3; - case 0x74: /* Input Window X End Position 0 */ + case 0x74: /* Input Window X End Position 0 */ return s->ix[1] & 0xff; - case 0x76: /* Input Window X End Position 1 */ + case 0x76: /* Input Window X End Position 1 */ return s->ix[1] >> 3; - case 0x78: /* Input Window Y End Position 0 */ + case 0x78: /* Input Window Y End Position 0 */ return s->ix[1] & 0xff; - case 0x7a: /* Input Window Y End Position 1 */ + case 0x7a: /* Input Window Y End Position 1 */ return s->ix[1] >> 3; - case 0x7c: /* Output Window X Start Position 0 */ + case 0x7c: /* Output Window X Start Position 0 */ return s->ox[0] & 0xff; - case 0x7e: /* Output Window X Start Position 1 */ + case 0x7e: /* Output Window X Start Position 1 */ return s->ox[0] >> 3; - case 0x80: /* Output Window Y Start Position 0 */ + case 0x80: /* Output Window Y Start Position 0 */ return s->oy[0] & 0xff; - case 0x82: /* Output Window Y Start Position 1 */ + case 0x82: /* Output Window Y Start Position 1 */ return s->oy[0] >> 3; - case 0x84: /* Output Window X End Position 0 */ + case 0x84: /* Output Window X End Position 0 */ return s->ox[1] & 0xff; - case 0x86: /* Output Window X End Position 1 */ + case 0x86: /* Output Window X End Position 1 */ return s->ox[1] >> 3; - case 0x88: /* Output Window Y End Position 0 */ + case 0x88: /* Output Window Y End Position 0 */ return s->oy[1] & 0xff; - case 0x8a: /* Output Window Y End Position 1 */ + case 0x8a: /* Output Window Y End Position 1 */ return s->oy[1] >> 3; - case 0x8c: /* Input Data Format */ + case 0x8c: /* Input Data Format */ return s->iformat; - case 0x8e: /* Data Source Select */ + case 0x8e: /* Data Source Select */ return s->source; - case 0x90: /* Display Memory Data Port */ + case 0x90: /* Display Memory Data Port */ return 0; - case 0xa8: /* Border Color 0 */ + case 0xa8: /* Border Color 0 */ return s->border_r; - case 0xaa: /* Border Color 1 */ + case 0xaa: /* Border Color 1 */ return s->border_g; - case 0xac: /* Border Color 2 */ + case 0xac: /* Border Color 2 */ return s->border_b; - case 0xb4: /* Gamma Correction Enable */ + case 0xb4: /* Gamma Correction Enable */ return s->gamma_config; - case 0xb6: /* Gamma Correction Table Index */ + case 0xb6: /* Gamma Correction Table Index */ return s->gamma_idx; - case 0xb8: /* Gamma Correction Table Data */ + case 0xb8: /* Gamma Correction Table Data */ return s->gamma_lut[s->gamma_idx ++]; - case 0xba: /* 3x3 Matrix Enable */ + case 0xba: /* 3x3 Matrix Enable */ return s->matrix_ena; - case 0xbc ... 0xde: /* Coefficient Registers */ + case 0xbc ... 0xde: /* Coefficient Registers */ return s->matrix_coeff[(reg - 0xbc) >> 1]; - case 0xe0: /* 3x3 Matrix Red Offset */ + case 0xe0: /* 3x3 Matrix Red Offset */ return s->matrix_r; - case 0xe2: /* 3x3 Matrix Green Offset */ + case 0xe2: /* 3x3 Matrix Green Offset */ return s->matrix_g; - case 0xe4: /* 3x3 Matrix Blue Offset */ + case 0xe4: /* 3x3 Matrix Blue Offset */ return s->matrix_b; - case 0xe6: /* Power-save */ + case 0xe6: /* Power-save */ return s->pm; - case 0xe8: /* Non-display Period Control / Status */ + case 0xe8: /* Non-display Period Control / Status */ return s->status | (1 << 5); - case 0xea: /* RGB Interface Control */ + case 0xea: /* RGB Interface Control */ return s->rgbgpio_dir; - case 0xec: /* RGB Interface Status */ + case 0xec: /* RGB Interface Status */ return s->rgbgpio; - case 0xee: /* General-purpose IO Pins Configuration */ + case 0xee: /* General-purpose IO Pins Configuration */ return s->gpio_dir; - case 0xf0: /* General-purpose IO Pins Status / Control */ + case 0xf0: /* General-purpose IO Pins Status / Control */ return s->gpio; - case 0xf2: /* GPIO Positive Edge Interrupt Trigger */ + case 0xf2: /* GPIO Positive Edge Interrupt Trigger */ return s->gpio_edge[0]; - case 0xf4: /* GPIO Negative Edge Interrupt Trigger */ + case 0xf4: /* GPIO Negative Edge Interrupt Trigger */ return s->gpio_edge[1]; - case 0xf6: /* GPIO Interrupt Status */ + case 0xf6: /* GPIO Interrupt Status */ return s->gpio_irq; - case 0xf8: /* GPIO Pull-down Control */ + case 0xf8: /* GPIO Pull-down Control */ return s->gpio_pdown; default: @@ -484,157 +484,157 @@ static void blizzard_reg_write(void *opaque, uint8_t reg, uint16_t value) BlizzardState *s = (BlizzardState *) opaque; switch (reg) { - case 0x04: /* PLL M-Divider */ + case 0x04: /* PLL M-Divider */ s->pll = (value & 0x3f) + 1; break; - case 0x06: /* PLL Lock Range Control */ + case 0x06: /* PLL Lock Range Control */ s->pll_range = value & 3; break; - case 0x08: /* PLL Lock Synthesis Control 0 */ + case 0x08: /* PLL Lock Synthesis Control 0 */ s->pll_ctrl &= 0xf00; s->pll_ctrl |= (value << 0) & 0x0ff; break; - case 0x0a: /* PLL Lock Synthesis Control 1 */ + case 0x0a: /* PLL Lock Synthesis Control 1 */ s->pll_ctrl &= 0x0ff; s->pll_ctrl |= (value << 8) & 0xf00; break; - case 0x0c: /* PLL Mode Control 0 */ + case 0x0c: /* PLL Mode Control 0 */ s->pll_mode = value & 0x77; if ((value & 3) == 0 || (value & 3) == 3) fprintf(stderr, "%s: wrong PLL Control bits (%i)\n", __func__, value & 3); break; - case 0x0e: /* Clock-Source Select */ + case 0x0e: /* Clock-Source Select */ s->clksel = value & 0xff; break; - case 0x10: /* Memory Controller Activate */ + case 0x10: /* Memory Controller Activate */ s->memenable = value & 1; break; - case 0x14: /* Memory Controller Bank 0 Status Flag */ + case 0x14: /* Memory Controller Bank 0 Status Flag */ break; - case 0x18: /* Auto-Refresh Interval Setting 0 */ + case 0x18: /* Auto-Refresh Interval Setting 0 */ s->memrefresh &= 0xf00; s->memrefresh |= (value << 0) & 0x0ff; break; - case 0x1a: /* Auto-Refresh Interval Setting 1 */ + case 0x1a: /* Auto-Refresh Interval Setting 1 */ s->memrefresh &= 0x0ff; s->memrefresh |= (value << 8) & 0xf00; break; - case 0x1c: /* Power-On Sequence Timing Control */ + case 0x1c: /* Power-On Sequence Timing Control */ s->timing[0] = value & 0x7f; break; - case 0x1e: /* Timing Control 0 */ + case 0x1e: /* Timing Control 0 */ s->timing[1] = value & 0x17; break; - case 0x20: /* Timing Control 1 */ + case 0x20: /* Timing Control 1 */ s->timing[2] = value & 0x35; break; - case 0x24: /* Arbitration Priority Control */ + case 0x24: /* Arbitration Priority Control */ s->priority = value & 1; break; - case 0x28: /* LCD Panel Configuration */ + case 0x28: /* LCD Panel Configuration */ s->lcd_config = value & 0xff; if (value & (1 << 7)) fprintf(stderr, "%s: data swap not supported!\n", __func__); break; - case 0x2a: /* LCD Horizontal Display Width */ + case 0x2a: /* LCD Horizontal Display Width */ s->x = value << 3; break; - case 0x2c: /* LCD Horizontal Non-display Period */ + case 0x2c: /* LCD Horizontal Non-display Period */ s->hndp = value & 0xff; break; - case 0x2e: /* LCD Vertical Display Height 0 */ + case 0x2e: /* LCD Vertical Display Height 0 */ s->y &= 0x300; s->y |= (value << 0) & 0x0ff; break; - case 0x30: /* LCD Vertical Display Height 1 */ + case 0x30: /* LCD Vertical Display Height 1 */ s->y &= 0x0ff; s->y |= (value << 8) & 0x300; break; - case 0x32: /* LCD Vertical Non-display Period */ + case 0x32: /* LCD Vertical Non-display Period */ s->vndp = value & 0xff; break; - case 0x34: /* LCD HS Pulse-width */ + case 0x34: /* LCD HS Pulse-width */ s->hsync = value & 0xff; break; - case 0x36: /* LCD HS Pulse Start Position */ + case 0x36: /* LCD HS Pulse Start Position */ s->skipx = value & 0xff; break; - case 0x38: /* LCD VS Pulse-width */ + case 0x38: /* LCD VS Pulse-width */ s->vsync = value & 0xbf; break; - case 0x3a: /* LCD VS Pulse Start Position */ + case 0x3a: /* LCD VS Pulse Start Position */ s->skipy = value & 0xff; break; - case 0x3c: /* PCLK Polarity */ + case 0x3c: /* PCLK Polarity */ s->pclk = value & 0x82; /* Affects calculation of s->hndp, s->hsync and s->skipx. */ break; - case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */ + case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */ s->hssi_config[0] = value; break; - case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */ + case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */ s->hssi_config[1] = value; if (((value >> 4) & 3) == 3) fprintf(stderr, "%s: Illegal active-data-links value\n", __func__); break; - case 0x42: /* High-speed Serial Interface Tx Mode */ + case 0x42: /* High-speed Serial Interface Tx Mode */ s->hssi_config[2] = value & 0xbd; break; - case 0x44: /* TV Display Configuration */ + case 0x44: /* TV Display Configuration */ s->tv_config = value & 0xfe; break; - case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits 0 */ + case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits 0 */ s->tv_timing[(reg - 0x46) >> 1] = value; break; - case 0x4e: /* VBI: Closed Caption / XDS Control / Status */ + case 0x4e: /* VBI: Closed Caption / XDS Control / Status */ s->vbi = value; break; - case 0x50: /* TV Horizontal Start Position */ + case 0x50: /* TV Horizontal Start Position */ s->tv_x = value; break; - case 0x52: /* TV Vertical Start Position */ + case 0x52: /* TV Vertical Start Position */ s->tv_y = value & 0x7f; break; - case 0x54: /* TV Test Pattern Setting */ + case 0x54: /* TV Test Pattern Setting */ s->tv_test = value; break; - case 0x56: /* TV Filter Setting */ + case 0x56: /* TV Filter Setting */ s->tv_filter_config = value & 0xbf; break; - case 0x58: /* TV Filter Coefficient Index */ + case 0x58: /* TV Filter Coefficient Index */ s->tv_filter_idx = value & 0x1f; break; - case 0x5a: /* TV Filter Coefficient Data */ + case 0x5a: /* TV Filter Coefficient Data */ if (s->tv_filter_idx < 0x20) s->tv_filter_coeff[s->tv_filter_idx ++] = value; break; - case 0x60: /* Input YUV/RGB Translate Mode 0 */ + case 0x60: /* Input YUV/RGB Translate Mode 0 */ s->yrc[0] = value & 0xb0; break; - case 0x62: /* Input YUV/RGB Translate Mode 1 */ + case 0x62: /* Input YUV/RGB Translate Mode 1 */ s->yrc[1] = value & 0x30; break; - case 0x64: /* U Data Fix */ + case 0x64: /* U Data Fix */ s->u = value & 0xff; break; - case 0x66: /* V Data Fix */ + case 0x66: /* V Data Fix */ s->v = value & 0xff; break; - case 0x68: /* Display Mode */ + case 0x68: /* Display Mode */ if ((s->mode ^ value) & 3) s->invalidate = 1; s->mode = value & 0xb7; @@ -644,83 +644,83 @@ static void blizzard_reg_write(void *opaque, uint8_t reg, uint16_t value) fprintf(stderr, "%s: Macrovision enable attempt!\n", __func__); break; - case 0x6a: /* Special Effects */ + case 0x6a: /* Special Effects */ s->effect = value & 0xfb; break; - case 0x6c: /* Input Window X Start Position 0 */ + case 0x6c: /* Input Window X Start Position 0 */ s->ix[0] &= 0x300; s->ix[0] |= (value << 0) & 0x0ff; break; - case 0x6e: /* Input Window X Start Position 1 */ + case 0x6e: /* Input Window X Start Position 1 */ s->ix[0] &= 0x0ff; s->ix[0] |= (value << 8) & 0x300; break; - case 0x70: /* Input Window Y Start Position 0 */ + case 0x70: /* Input Window Y Start Position 0 */ s->iy[0] &= 0x300; s->iy[0] |= (value << 0) & 0x0ff; break; - case 0x72: /* Input Window Y Start Position 1 */ + case 0x72: /* Input Window Y Start Position 1 */ s->iy[0] &= 0x0ff; s->iy[0] |= (value << 8) & 0x300; break; - case 0x74: /* Input Window X End Position 0 */ + case 0x74: /* Input Window X End Position 0 */ s->ix[1] &= 0x300; s->ix[1] |= (value << 0) & 0x0ff; break; - case 0x76: /* Input Window X End Position 1 */ + case 0x76: /* Input Window X End Position 1 */ s->ix[1] &= 0x0ff; s->ix[1] |= (value << 8) & 0x300; break; - case 0x78: /* Input Window Y End Position 0 */ + case 0x78: /* Input Window Y End Position 0 */ s->iy[1] &= 0x300; s->iy[1] |= (value << 0) & 0x0ff; break; - case 0x7a: /* Input Window Y End Position 1 */ + case 0x7a: /* Input Window Y End Position 1 */ s->iy[1] &= 0x0ff; s->iy[1] |= (value << 8) & 0x300; break; - case 0x7c: /* Output Window X Start Position 0 */ + case 0x7c: /* Output Window X Start Position 0 */ s->ox[0] &= 0x300; s->ox[0] |= (value << 0) & 0x0ff; break; - case 0x7e: /* Output Window X Start Position 1 */ + case 0x7e: /* Output Window X Start Position 1 */ s->ox[0] &= 0x0ff; s->ox[0] |= (value << 8) & 0x300; break; - case 0x80: /* Output Window Y Start Position 0 */ + case 0x80: /* Output Window Y Start Position 0 */ s->oy[0] &= 0x300; s->oy[0] |= (value << 0) & 0x0ff; break; - case 0x82: /* Output Window Y Start Position 1 */ + case 0x82: /* Output Window Y Start Position 1 */ s->oy[0] &= 0x0ff; s->oy[0] |= (value << 8) & 0x300; break; - case 0x84: /* Output Window X End Position 0 */ + case 0x84: /* Output Window X End Position 0 */ s->ox[1] &= 0x300; s->ox[1] |= (value << 0) & 0x0ff; break; - case 0x86: /* Output Window X End Position 1 */ + case 0x86: /* Output Window X End Position 1 */ s->ox[1] &= 0x0ff; s->ox[1] |= (value << 8) & 0x300; break; - case 0x88: /* Output Window Y End Position 0 */ + case 0x88: /* Output Window Y End Position 0 */ s->oy[1] &= 0x300; s->oy[1] |= (value << 0) & 0x0ff; break; - case 0x8a: /* Output Window Y End Position 1 */ + case 0x8a: /* Output Window Y End Position 1 */ s->oy[1] &= 0x0ff; s->oy[1] |= (value << 8) & 0x300; break; - case 0x8c: /* Input Data Format */ + case 0x8c: /* Input Data Format */ s->iformat = value & 0xf; s->bpp = blizzard_iformat_bpp[s->iformat]; if (!s->bpp) fprintf(stderr, "%s: Illegal or unsupported input format %x\n", __func__, s->iformat); break; - case 0x8e: /* Data Source Select */ + case 0x8e: /* Data Source Select */ s->source = value & 7; /* Currently all windows will be "destructive overlays". */ if ((!(s->effect & (1 << 3)) && (s->ix[0] != s->ox[0] || @@ -735,7 +735,7 @@ static void blizzard_reg_write(void *opaque, uint8_t reg, uint16_t value) blizzard_transfer_setup(s); break; - case 0x90: /* Display Memory Data Port */ + case 0x90: /* Display Memory Data Port */ if (!s->data.len && !blizzard_transfer_setup(s)) break; @@ -744,73 +744,73 @@ static void blizzard_reg_write(void *opaque, uint8_t reg, uint16_t value) blizzard_window(s); break; - case 0xa8: /* Border Color 0 */ + case 0xa8: /* Border Color 0 */ s->border_r = value; break; - case 0xaa: /* Border Color 1 */ + case 0xaa: /* Border Color 1 */ s->border_g = value; break; - case 0xac: /* Border Color 2 */ + case 0xac: /* Border Color 2 */ s->border_b = value; break; - case 0xb4: /* Gamma Correction Enable */ + case 0xb4: /* Gamma Correction Enable */ s->gamma_config = value & 0x87; break; - case 0xb6: /* Gamma Correction Table Index */ + case 0xb6: /* Gamma Correction Table Index */ s->gamma_idx = value; break; - case 0xb8: /* Gamma Correction Table Data */ + case 0xb8: /* Gamma Correction Table Data */ s->gamma_lut[s->gamma_idx ++] = value; break; - case 0xba: /* 3x3 Matrix Enable */ + case 0xba: /* 3x3 Matrix Enable */ s->matrix_ena = value & 1; break; - case 0xbc ... 0xde: /* Coefficient Registers */ + case 0xbc ... 0xde: /* Coefficient Registers */ s->matrix_coeff[(reg - 0xbc) >> 1] = value & ((reg & 2) ? 0x80 : 0xff); break; - case 0xe0: /* 3x3 Matrix Red Offset */ + case 0xe0: /* 3x3 Matrix Red Offset */ s->matrix_r = value; break; - case 0xe2: /* 3x3 Matrix Green Offset */ + case 0xe2: /* 3x3 Matrix Green Offset */ s->matrix_g = value; break; - case 0xe4: /* 3x3 Matrix Blue Offset */ + case 0xe4: /* 3x3 Matrix Blue Offset */ s->matrix_b = value; break; - case 0xe6: /* Power-save */ + case 0xe6: /* Power-save */ s->pm = value & 0x83; if (value & s->mode & 1) fprintf(stderr, "%s: The display must be disabled before entering " "Standby Mode\n", __func__); break; - case 0xe8: /* Non-display Period Control / Status */ + case 0xe8: /* Non-display Period Control / Status */ s->status = value & 0x1b; break; - case 0xea: /* RGB Interface Control */ + case 0xea: /* RGB Interface Control */ s->rgbgpio_dir = value & 0x8f; break; - case 0xec: /* RGB Interface Status */ + case 0xec: /* RGB Interface Status */ s->rgbgpio = value & 0xcf; break; - case 0xee: /* General-purpose IO Pins Configuration */ + case 0xee: /* General-purpose IO Pins Configuration */ s->gpio_dir = value; break; - case 0xf0: /* General-purpose IO Pins Status / Control */ + case 0xf0: /* General-purpose IO Pins Status / Control */ s->gpio = value; break; - case 0xf2: /* GPIO Positive Edge Interrupt Trigger */ + case 0xf2: /* GPIO Positive Edge Interrupt Trigger */ s->gpio_edge[0] = value; break; - case 0xf4: /* GPIO Negative Edge Interrupt Trigger */ + case 0xf4: /* GPIO Negative Edge Interrupt Trigger */ s->gpio_edge[1] = value; break; - case 0xf6: /* GPIO Interrupt Status */ + case 0xf6: /* GPIO Interrupt Status */ s->gpio_irq &= value; break; - case 0xf8: /* GPIO Pull-down Control */ + case 0xf8: /* GPIO Pull-down Control */ s->gpio_pdown = value; break; @@ -1007,7 +1007,7 @@ static const GraphicHwOps blizzard_ops = { void *s1d13745_init(qemu_irq gpio_int) { - BlizzardState *s = (BlizzardState *) g_malloc0(sizeof(*s)); + BlizzardState *s = g_malloc0(sizeof(*s)); DisplaySurface *surface; s->fb = g_malloc(0x180000); diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index 8ed734b195..3b1d922b6e 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -8,7 +8,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "qemu/units.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "hw/display/bochs-vbe.h" @@ -61,7 +61,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(BochsDisplayState, BOCHS_DISPLAY) static const VMStateDescription vmstate_bochs_display = { .name = "bochs-display", - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(pci, BochsDisplayState), VMSTATE_UINT16_ARRAY(vbe_regs, BochsDisplayState, VBE_DISPI_INDEX_NB), VMSTATE_BOOL(big_endian_fb, BochsDisplayState), @@ -164,7 +164,7 @@ static int bochs_display_get_mode(BochsDisplayState *s, memset(mode, 0, sizeof(*mode)); switch (vbe[VBE_DISPI_INDEX_BPP]) { case 16: - /* best effort: support native endianess only */ + /* best effort: support native endianness only */ mode->format = PIXMAN_r5g6b5; mode->bytepp = 2; break; diff --git a/hw/display/cg3.c b/hw/display/cg3.c index 2e9656ae1c..b271faaa48 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -334,7 +334,7 @@ static const VMStateDescription vmstate_cg3 = { .version_id = 1, .minimum_version_id = 1, .post_load = vmstate_cg3_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(height, CG3State), VMSTATE_UINT16(width, CG3State), VMSTATE_UINT16(depth, CG3State), diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 3bb6a58698..150883a971 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -39,12 +39,14 @@ #include "sysemu/reset.h" #include "qapi/error.h" #include "trace.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "ui/pixel_ops.h" +#include "vga_regs.h" #include "cirrus_vga_internal.h" #include "qom/object.h" +#include "ui/console.h" /* * TODO: @@ -76,12 +78,12 @@ #define CIRRUS_MEMSIZE_512k 0x08 #define CIRRUS_MEMSIZE_1M 0x10 #define CIRRUS_MEMSIZE_2M 0x18 -#define CIRRUS_MEMFLAGS_BANKSWITCH 0x80 // bank switching is enabled. +#define CIRRUS_MEMFLAGS_BANKSWITCH 0x80 // bank switching is enabled. // sequencer 0x12 #define CIRRUS_CURSOR_SHOW 0x01 #define CIRRUS_CURSOR_HIDDENPEL 0x02 -#define CIRRUS_CURSOR_LARGE 0x04 // 64x64 if set, 32x32 if clear +#define CIRRUS_CURSOR_LARGE 0x04 // 64x64 if set, 32x32 if clear // sequencer 0x17 #define CIRRUS_BUSTYPE_VLBFAST 0x10 @@ -89,12 +91,12 @@ #define CIRRUS_BUSTYPE_VLBSLOW 0x30 #define CIRRUS_BUSTYPE_ISA 0x38 #define CIRRUS_MMIO_ENABLE 0x04 -#define CIRRUS_MMIO_USE_PCIADDR 0x40 // 0xb8000 if cleared. +#define CIRRUS_MMIO_USE_PCIADDR 0x40 // 0xb8000 if cleared. #define CIRRUS_MEMSIZEEXT_DOUBLE 0x80 // control 0x0b #define CIRRUS_BANKING_DUAL 0x01 -#define CIRRUS_BANKING_GRANULARITY_16K 0x20 // set:16k, clear:4k +#define CIRRUS_BANKING_GRANULARITY_16K 0x20 // set:16k, clear:4k // control 0x30 #define CIRRUS_BLTMODE_BACKWARDS 0x01 @@ -143,35 +145,35 @@ #define CIRRUS_BLTMODEEXT_DWORDGRANULARITY 0x01 // memory-mapped IO -#define CIRRUS_MMIO_BLTBGCOLOR 0x00 // dword -#define CIRRUS_MMIO_BLTFGCOLOR 0x04 // dword -#define CIRRUS_MMIO_BLTWIDTH 0x08 // word -#define CIRRUS_MMIO_BLTHEIGHT 0x0a // word -#define CIRRUS_MMIO_BLTDESTPITCH 0x0c // word -#define CIRRUS_MMIO_BLTSRCPITCH 0x0e // word -#define CIRRUS_MMIO_BLTDESTADDR 0x10 // dword -#define CIRRUS_MMIO_BLTSRCADDR 0x14 // dword -#define CIRRUS_MMIO_BLTWRITEMASK 0x17 // byte -#define CIRRUS_MMIO_BLTMODE 0x18 // byte -#define CIRRUS_MMIO_BLTROP 0x1a // byte -#define CIRRUS_MMIO_BLTMODEEXT 0x1b // byte -#define CIRRUS_MMIO_BLTTRANSPARENTCOLOR 0x1c // word? -#define CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK 0x20 // word? -#define CIRRUS_MMIO_LINEARDRAW_START_X 0x24 // word -#define CIRRUS_MMIO_LINEARDRAW_START_Y 0x26 // word -#define CIRRUS_MMIO_LINEARDRAW_END_X 0x28 // word -#define CIRRUS_MMIO_LINEARDRAW_END_Y 0x2a // word -#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_INC 0x2c // byte -#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ROLLOVER 0x2d // byte -#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_MASK 0x2e // byte -#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ACCUM 0x2f // byte -#define CIRRUS_MMIO_BRESENHAM_K1 0x30 // word -#define CIRRUS_MMIO_BRESENHAM_K3 0x32 // word -#define CIRRUS_MMIO_BRESENHAM_ERROR 0x34 // word -#define CIRRUS_MMIO_BRESENHAM_DELTA_MAJOR 0x36 // word -#define CIRRUS_MMIO_BRESENHAM_DIRECTION 0x38 // byte -#define CIRRUS_MMIO_LINEDRAW_MODE 0x39 // byte -#define CIRRUS_MMIO_BLTSTATUS 0x40 // byte +#define CIRRUS_MMIO_BLTBGCOLOR 0x00 // dword +#define CIRRUS_MMIO_BLTFGCOLOR 0x04 // dword +#define CIRRUS_MMIO_BLTWIDTH 0x08 // word +#define CIRRUS_MMIO_BLTHEIGHT 0x0a // word +#define CIRRUS_MMIO_BLTDESTPITCH 0x0c // word +#define CIRRUS_MMIO_BLTSRCPITCH 0x0e // word +#define CIRRUS_MMIO_BLTDESTADDR 0x10 // dword +#define CIRRUS_MMIO_BLTSRCADDR 0x14 // dword +#define CIRRUS_MMIO_BLTWRITEMASK 0x17 // byte +#define CIRRUS_MMIO_BLTMODE 0x18 // byte +#define CIRRUS_MMIO_BLTROP 0x1a // byte +#define CIRRUS_MMIO_BLTMODEEXT 0x1b // byte +#define CIRRUS_MMIO_BLTTRANSPARENTCOLOR 0x1c // word? +#define CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK 0x20 // word? +#define CIRRUS_MMIO_LINEARDRAW_START_X 0x24 // word +#define CIRRUS_MMIO_LINEARDRAW_START_Y 0x26 // word +#define CIRRUS_MMIO_LINEARDRAW_END_X 0x28 // word +#define CIRRUS_MMIO_LINEARDRAW_END_Y 0x2a // word +#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_INC 0x2c // byte +#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ROLLOVER 0x2d // byte +#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_MASK 0x2e // byte +#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ACCUM 0x2f // byte +#define CIRRUS_MMIO_BRESENHAM_K1 0x30 // word +#define CIRRUS_MMIO_BRESENHAM_K3 0x32 // word +#define CIRRUS_MMIO_BRESENHAM_ERROR 0x34 // word +#define CIRRUS_MMIO_BRESENHAM_DELTA_MAJOR 0x36 // word +#define CIRRUS_MMIO_BRESENHAM_DIRECTION 0x38 // byte +#define CIRRUS_MMIO_LINEDRAW_MODE 0x39 // byte +#define CIRRUS_MMIO_BLTSTATUS 0x40 // byte #define CIRRUS_PNPMMIO_SIZE 0x1000 @@ -628,8 +630,8 @@ static inline void cirrus_bitblt_bgcol(CirrusVGAState *s) } static void cirrus_invalidate_region(CirrusVGAState * s, int off_begin, - int off_pitch, int bytesperline, - int lines) + int off_pitch, int bytesperline, + int lines) { int y; int off_cur; @@ -708,8 +710,8 @@ static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop) s->cirrus_blt_dstpitch, s->cirrus_blt_width, s->cirrus_blt_height); cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, - s->cirrus_blt_dstpitch, s->cirrus_blt_width, - s->cirrus_blt_height); + s->cirrus_blt_dstpitch, s->cirrus_blt_width, + s->cirrus_blt_height); cirrus_bitblt_reset(s); return 1; } @@ -773,8 +775,8 @@ static int cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) (*s->cirrus_rop) (s, s->cirrus_blt_dstaddr, s->cirrus_blt_srcaddr, - s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch, - s->cirrus_blt_width, s->cirrus_blt_height); + s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch, + s->cirrus_blt_width, s->cirrus_blt_height); if (notify) { dpy_gfx_update(s->vga.con, dx, dy, @@ -786,8 +788,8 @@ static int cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) changed since qemu_console_copy implies this */ cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, - s->cirrus_blt_dstpitch, s->cirrus_blt_width, - s->cirrus_blt_height); + s->cirrus_blt_dstpitch, s->cirrus_blt_width, + s->cirrus_blt_height); return 1; } @@ -797,9 +799,9 @@ static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s) if (blit_is_unsafe(s, false)) return 0; - return cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->vga.start_addr, - s->cirrus_blt_srcaddr - s->vga.start_addr, - s->cirrus_blt_width, s->cirrus_blt_height); + return cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->vga.params.start_addr, + s->cirrus_blt_srcaddr - s->vga.params.start_addr, + s->cirrus_blt_width, s->cirrus_blt_height); } /*************************************** @@ -834,7 +836,7 @@ static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s) word alignment, so we keep them for the next line */ /* XXX: keep alignment to speed up transfer */ end_ptr = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; - copy_count = s->cirrus_srcptr_end - end_ptr; + copy_count = MIN(s->cirrus_srcptr_end - end_ptr, CIRRUS_BLTBUFSIZE); memmove(s->cirrus_bltbuf, end_ptr, copy_count); s->cirrus_srcptr = s->cirrus_bltbuf + copy_count; s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; @@ -854,7 +856,7 @@ static void cirrus_bitblt_reset(CirrusVGAState * s) int need_update; s->vga.gr[0x31] &= - ~(CIRRUS_BLT_START | CIRRUS_BLT_BUSY | CIRRUS_BLT_FIFOUSED); + ~(CIRRUS_BLT_START | CIRRUS_BLT_BUSY | CIRRUS_BLT_FIFOUSED); need_update = s->cirrus_srcptr != &s->cirrus_bltbuf[0] || s->cirrus_srcptr_end != &s->cirrus_bltbuf[0]; s->cirrus_srcptr = &s->cirrus_bltbuf[0]; @@ -878,24 +880,24 @@ static int cirrus_bitblt_cputovideo(CirrusVGAState * s) s->cirrus_srcptr_end = &s->cirrus_bltbuf[0]; if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { - s->cirrus_blt_srcpitch = 8; - } else { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { + s->cirrus_blt_srcpitch = 8; + } else { /* XXX: check for 24 bpp */ - s->cirrus_blt_srcpitch = 8 * 8 * s->cirrus_blt_pixelwidth; - } - s->cirrus_srccounter = s->cirrus_blt_srcpitch; + s->cirrus_blt_srcpitch = 8 * 8 * s->cirrus_blt_pixelwidth; + } + s->cirrus_srccounter = s->cirrus_blt_srcpitch; } else { - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { w = s->cirrus_blt_width / s->cirrus_blt_pixelwidth; if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_DWORDGRANULARITY) s->cirrus_blt_srcpitch = ((w + 31) >> 5); else s->cirrus_blt_srcpitch = ((w + 7) >> 3); - } else { + } else { /* always align input size to 32 bits */ - s->cirrus_blt_srcpitch = (s->cirrus_blt_width + 3) & ~3; - } + s->cirrus_blt_srcpitch = (s->cirrus_blt_width + 3) & ~3; + } s->cirrus_srccounter = s->cirrus_blt_srcpitch * s->cirrus_blt_height; } @@ -921,12 +923,12 @@ static int cirrus_bitblt_videotovideo(CirrusVGAState * s) int ret; if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { - ret = cirrus_bitblt_videotovideo_patterncopy(s); + ret = cirrus_bitblt_videotovideo_patterncopy(s); } else { - ret = cirrus_bitblt_videotovideo_copy(s); + ret = cirrus_bitblt_videotovideo_copy(s); } if (ret) - cirrus_bitblt_reset(s); + cirrus_bitblt_reset(s); return ret; } @@ -945,9 +947,9 @@ static void cirrus_bitblt_start(CirrusVGAState * s) s->cirrus_blt_dstpitch = (s->vga.gr[0x24] | (s->vga.gr[0x25] << 8)); s->cirrus_blt_srcpitch = (s->vga.gr[0x26] | (s->vga.gr[0x27] << 8)); s->cirrus_blt_dstaddr = - (s->vga.gr[0x28] | (s->vga.gr[0x29] << 8) | (s->vga.gr[0x2a] << 16)); + (s->vga.gr[0x28] | (s->vga.gr[0x29] << 8) | (s->vga.gr[0x2a] << 16)); s->cirrus_blt_srcaddr = - (s->vga.gr[0x2c] | (s->vga.gr[0x2d] << 8) | (s->vga.gr[0x2e] << 16)); + (s->vga.gr[0x2c] | (s->vga.gr[0x2d] << 8) | (s->vga.gr[0x2e] << 16)); s->cirrus_blt_mode = s->vga.gr[0x30]; s->cirrus_blt_modeext = s->vga.gr[0x33]; blt_rop = s->vga.gr[0x32]; @@ -968,31 +970,31 @@ static void cirrus_bitblt_start(CirrusVGAState * s) switch (s->cirrus_blt_mode & CIRRUS_BLTMODE_PIXELWIDTHMASK) { case CIRRUS_BLTMODE_PIXELWIDTH8: - s->cirrus_blt_pixelwidth = 1; - break; + s->cirrus_blt_pixelwidth = 1; + break; case CIRRUS_BLTMODE_PIXELWIDTH16: - s->cirrus_blt_pixelwidth = 2; - break; + s->cirrus_blt_pixelwidth = 2; + break; case CIRRUS_BLTMODE_PIXELWIDTH24: - s->cirrus_blt_pixelwidth = 3; - break; + s->cirrus_blt_pixelwidth = 3; + break; case CIRRUS_BLTMODE_PIXELWIDTH32: - s->cirrus_blt_pixelwidth = 4; - break; + s->cirrus_blt_pixelwidth = 4; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "cirrus: bitblt - pixel width is unknown\n"); - goto bitblt_ignore; + goto bitblt_ignore; } s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_PIXELWIDTHMASK; if ((s-> - cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSSRC | - CIRRUS_BLTMODE_MEMSYSDEST)) - == (CIRRUS_BLTMODE_MEMSYSSRC | CIRRUS_BLTMODE_MEMSYSDEST)) { + cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSSRC | + CIRRUS_BLTMODE_MEMSYSDEST)) + == (CIRRUS_BLTMODE_MEMSYSSRC | CIRRUS_BLTMODE_MEMSYSDEST)) { qemu_log_mask(LOG_UNIMP, "cirrus: bitblt - memory-to-memory copy requested\n"); - goto bitblt_ignore; + goto bitblt_ignore; } if ((s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_SOLIDFILL) && @@ -1036,30 +1038,30 @@ static void cirrus_bitblt_start(CirrusVGAState * s) s->cirrus_rop = cirrus_patternfill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; } } else { - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) { - if (s->cirrus_blt_pixelwidth > 2) { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) { + if (s->cirrus_blt_pixelwidth > 2) { qemu_log_mask(LOG_GUEST_ERROR, "cirrus: src transparent without colorexpand " "must be 8bpp or 16bpp\n"); - goto bitblt_ignore; - } - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) { - s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch; - s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch; - s->cirrus_rop = cirrus_bkwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; - } else { - s->cirrus_rop = cirrus_fwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; - } - } else { - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) { - s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch; - s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch; - s->cirrus_rop = cirrus_bkwd_rop[rop_to_index[blt_rop]]; - } else { - s->cirrus_rop = cirrus_fwd_rop[rop_to_index[blt_rop]]; - } - } - } + goto bitblt_ignore; + } + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) { + s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch; + s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch; + s->cirrus_rop = cirrus_bkwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } else { + s->cirrus_rop = cirrus_fwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } + } else { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) { + s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch; + s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch; + s->cirrus_rop = cirrus_bkwd_rop[rop_to_index[blt_rop]]; + } else { + s->cirrus_rop = cirrus_fwd_rop[rop_to_index[blt_rop]]; + } + } + } // setup bitblt engine. if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSSRC) { if (!cirrus_bitblt_cputovideo(s)) @@ -1085,11 +1087,11 @@ static void cirrus_write_bitblt(CirrusVGAState * s, unsigned reg_value) s->vga.gr[0x31] = reg_value; if (((old_value & CIRRUS_BLT_RESET) != 0) && - ((reg_value & CIRRUS_BLT_RESET) == 0)) { - cirrus_bitblt_reset(s); + ((reg_value & CIRRUS_BLT_RESET) == 0)) { + cirrus_bitblt_reset(s); } else if (((old_value & CIRRUS_BLT_START) == 0) && - ((reg_value & CIRRUS_BLT_START) != 0)) { - cirrus_bitblt_start(s); + ((reg_value & CIRRUS_BLT_START) != 0)) { + cirrus_bitblt_start(s); } } @@ -1100,30 +1102,29 @@ static void cirrus_write_bitblt(CirrusVGAState * s, unsigned reg_value) * ***************************************/ -static void cirrus_get_offsets(VGACommonState *s1, - uint32_t *pline_offset, - uint32_t *pstart_addr, - uint32_t *pline_compare) +static void cirrus_get_params(VGACommonState *s1, + VGADisplayParams *params) { CirrusVGAState * s = container_of(s1, CirrusVGAState, vga); - uint32_t start_addr, line_offset, line_compare; + uint32_t line_offset; line_offset = s->vga.cr[0x13] - | ((s->vga.cr[0x1b] & 0x10) << 4); + | ((s->vga.cr[0x1b] & 0x10) << 4); line_offset <<= 3; - *pline_offset = line_offset; + params->line_offset = line_offset; - start_addr = (s->vga.cr[0x0c] << 8) - | s->vga.cr[0x0d] - | ((s->vga.cr[0x1b] & 0x01) << 16) - | ((s->vga.cr[0x1b] & 0x0c) << 15) - | ((s->vga.cr[0x1d] & 0x80) << 12); - *pstart_addr = start_addr; + params->start_addr = (s->vga.cr[0x0c] << 8) + | s->vga.cr[0x0d] + | ((s->vga.cr[0x1b] & 0x01) << 16) + | ((s->vga.cr[0x1b] & 0x0c) << 15) + | ((s->vga.cr[0x1d] & 0x80) << 12); - line_compare = s->vga.cr[0x18] | + params->line_compare = s->vga.cr[0x18] | ((s->vga.cr[0x07] & 0x10) << 4) | ((s->vga.cr[0x09] & 0x40) << 3); - *pline_compare = line_compare; + + params->hpel = s->vga.ar[VGA_ATC_PEL]; + params->hpel_split = s->vga.ar[VGA_ATC_MODE] & 0x20; } static uint32_t cirrus_get_bpp16_depth(CirrusVGAState * s) @@ -1132,17 +1133,17 @@ static uint32_t cirrus_get_bpp16_depth(CirrusVGAState * s) switch (s->cirrus_hidden_dac_data & 0xf) { case 0: - ret = 15; - break; /* Sierra HiColor */ + ret = 15; + break; /* Sierra HiColor */ case 1: - ret = 16; - break; /* XGA HiColor */ + ret = 16; + break; /* XGA HiColor */ default: qemu_log_mask(LOG_GUEST_ERROR, "cirrus: invalid DAC value 0x%x in 16bpp\n", (s->cirrus_hidden_dac_data & 0xf)); - ret = 15; /* XXX */ - break; + ret = 15; /* XXX */ + break; } return ret; } @@ -1153,33 +1154,33 @@ static int cirrus_get_bpp(VGACommonState *s1) uint32_t ret = 8; if ((s->vga.sr[0x07] & 0x01) != 0) { - /* Cirrus SVGA */ - switch (s->vga.sr[0x07] & CIRRUS_SR7_BPP_MASK) { - case CIRRUS_SR7_BPP_8: - ret = 8; - break; - case CIRRUS_SR7_BPP_16_DOUBLEVCLK: - ret = cirrus_get_bpp16_depth(s); - break; - case CIRRUS_SR7_BPP_24: - ret = 24; - break; - case CIRRUS_SR7_BPP_16: - ret = cirrus_get_bpp16_depth(s); - break; - case CIRRUS_SR7_BPP_32: - ret = 32; - break; - default: + /* Cirrus SVGA */ + switch (s->vga.sr[0x07] & CIRRUS_SR7_BPP_MASK) { + case CIRRUS_SR7_BPP_8: + ret = 8; + break; + case CIRRUS_SR7_BPP_16_DOUBLEVCLK: + ret = cirrus_get_bpp16_depth(s); + break; + case CIRRUS_SR7_BPP_24: + ret = 24; + break; + case CIRRUS_SR7_BPP_16: + ret = cirrus_get_bpp16_depth(s); + break; + case CIRRUS_SR7_BPP_32: + ret = 32; + break; + default: #ifdef DEBUG_CIRRUS - printf("cirrus: unknown bpp - sr7=%x\n", s->vga.sr[0x7]); + printf("cirrus: unknown bpp - sr7=%x\n", s->vga.sr[0x7]); #endif - ret = 8; - break; - } + ret = 8; + break; + } } else { - /* VGA */ - ret = 0; + /* VGA */ + ret = 0; } return ret; @@ -1212,36 +1213,36 @@ static void cirrus_update_bank_ptr(CirrusVGAState * s, unsigned bank_index) unsigned offset; unsigned limit; - if ((s->vga.gr[0x0b] & 0x01) != 0) /* dual bank */ - offset = s->vga.gr[0x09 + bank_index]; - else /* single bank */ - offset = s->vga.gr[0x09]; + if ((s->vga.gr[0x0b] & 0x01) != 0) /* dual bank */ + offset = s->vga.gr[0x09 + bank_index]; + else /* single bank */ + offset = s->vga.gr[0x09]; if ((s->vga.gr[0x0b] & 0x20) != 0) - offset <<= 14; + offset <<= 14; else - offset <<= 12; + offset <<= 12; if (s->real_vram_size <= offset) - limit = 0; + limit = 0; else - limit = s->real_vram_size - offset; + limit = s->real_vram_size - offset; if (((s->vga.gr[0x0b] & 0x01) == 0) && (bank_index != 0)) { - if (limit > 0x8000) { - offset += 0x8000; - limit -= 0x8000; - } else { - limit = 0; - } + if (limit > 0x8000) { + offset += 0x8000; + limit -= 0x8000; + } else { + limit = 0; + } } if (limit > 0) { - s->cirrus_bank_base[bank_index] = offset; - s->cirrus_bank_limit[bank_index] = limit; + s->cirrus_bank_base[bank_index] = offset; + s->cirrus_bank_limit[bank_index] = limit; } else { - s->cirrus_bank_base[bank_index] = 0; - s->cirrus_bank_limit[bank_index] = 0; + s->cirrus_bank_base[bank_index] = 0; + s->cirrus_bank_limit[bank_index] = 0; } } @@ -1254,148 +1255,148 @@ static void cirrus_update_bank_ptr(CirrusVGAState * s, unsigned bank_index) static int cirrus_vga_read_sr(CirrusVGAState * s) { switch (s->vga.sr_index) { - case 0x00: // Standard VGA - case 0x01: // Standard VGA - case 0x02: // Standard VGA - case 0x03: // Standard VGA - case 0x04: // Standard VGA - return s->vga.sr[s->vga.sr_index]; - case 0x06: // Unlock Cirrus extensions - return s->vga.sr[s->vga.sr_index]; + case 0x00: // Standard VGA + case 0x01: // Standard VGA + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + return s->vga.sr[s->vga.sr_index]; + case 0x06: // Unlock Cirrus extensions + return s->vga.sr[s->vga.sr_index]; case 0x10: case 0x30: case 0x50: - case 0x70: // Graphics Cursor X + case 0x70: // Graphics Cursor X case 0x90: case 0xb0: case 0xd0: - case 0xf0: // Graphics Cursor X - return s->vga.sr[0x10]; + case 0xf0: // Graphics Cursor X + return s->vga.sr[0x10]; case 0x11: case 0x31: case 0x51: - case 0x71: // Graphics Cursor Y + case 0x71: // Graphics Cursor Y case 0x91: case 0xb1: case 0xd1: - case 0xf1: // Graphics Cursor Y - return s->vga.sr[0x11]; - case 0x05: // ??? - case 0x07: // Extended Sequencer Mode - case 0x08: // EEPROM Control - case 0x09: // Scratch Register 0 - case 0x0a: // Scratch Register 1 - case 0x0b: // VCLK 0 - case 0x0c: // VCLK 1 - case 0x0d: // VCLK 2 - case 0x0e: // VCLK 3 - case 0x0f: // DRAM Control - case 0x12: // Graphics Cursor Attribute - case 0x13: // Graphics Cursor Pattern Address - case 0x14: // Scratch Register 2 - case 0x15: // Scratch Register 3 - case 0x16: // Performance Tuning Register - case 0x17: // Configuration Readback and Extended Control - case 0x18: // Signature Generator Control - case 0x19: // Signal Generator Result - case 0x1a: // Signal Generator Result - case 0x1b: // VCLK 0 Denominator & Post - case 0x1c: // VCLK 1 Denominator & Post - case 0x1d: // VCLK 2 Denominator & Post - case 0x1e: // VCLK 3 Denominator & Post - case 0x1f: // BIOS Write Enable and MCLK select + case 0xf1: // Graphics Cursor Y + return s->vga.sr[0x11]; + case 0x05: // ??? + case 0x07: // Extended Sequencer Mode + case 0x08: // EEPROM Control + case 0x09: // Scratch Register 0 + case 0x0a: // Scratch Register 1 + case 0x0b: // VCLK 0 + case 0x0c: // VCLK 1 + case 0x0d: // VCLK 2 + case 0x0e: // VCLK 3 + case 0x0f: // DRAM Control + case 0x12: // Graphics Cursor Attribute + case 0x13: // Graphics Cursor Pattern Address + case 0x14: // Scratch Register 2 + case 0x15: // Scratch Register 3 + case 0x16: // Performance Tuning Register + case 0x17: // Configuration Readback and Extended Control + case 0x18: // Signature Generator Control + case 0x19: // Signal Generator Result + case 0x1a: // Signal Generator Result + case 0x1b: // VCLK 0 Denominator & Post + case 0x1c: // VCLK 1 Denominator & Post + case 0x1d: // VCLK 2 Denominator & Post + case 0x1e: // VCLK 3 Denominator & Post + case 0x1f: // BIOS Write Enable and MCLK select #ifdef DEBUG_CIRRUS - printf("cirrus: handled inport sr_index %02x\n", s->vga.sr_index); + printf("cirrus: handled inport sr_index %02x\n", s->vga.sr_index); #endif - return s->vga.sr[s->vga.sr_index]; + return s->vga.sr[s->vga.sr_index]; default: qemu_log_mask(LOG_GUEST_ERROR, "cirrus: inport sr_index 0x%02x\n", s->vga.sr_index); - return 0xff; + return 0xff; } } static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val) { switch (s->vga.sr_index) { - case 0x00: // Standard VGA - case 0x01: // Standard VGA - case 0x02: // Standard VGA - case 0x03: // Standard VGA - case 0x04: // Standard VGA - s->vga.sr[s->vga.sr_index] = val & sr_mask[s->vga.sr_index]; - if (s->vga.sr_index == 1) + case 0x00: // Standard VGA + case 0x01: // Standard VGA + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + s->vga.sr[s->vga.sr_index] = val & sr_mask[s->vga.sr_index]; + if (s->vga.sr_index == 1) s->vga.update_retrace_info(&s->vga); break; - case 0x06: // Unlock Cirrus extensions - val &= 0x17; - if (val == 0x12) { - s->vga.sr[s->vga.sr_index] = 0x12; - } else { - s->vga.sr[s->vga.sr_index] = 0x0f; - } - break; + case 0x06: // Unlock Cirrus extensions + val &= 0x17; + if (val == 0x12) { + s->vga.sr[s->vga.sr_index] = 0x12; + } else { + s->vga.sr[s->vga.sr_index] = 0x0f; + } + break; case 0x10: case 0x30: case 0x50: - case 0x70: // Graphics Cursor X + case 0x70: // Graphics Cursor X case 0x90: case 0xb0: case 0xd0: - case 0xf0: // Graphics Cursor X - s->vga.sr[0x10] = val; + case 0xf0: // Graphics Cursor X + s->vga.sr[0x10] = val; s->vga.hw_cursor_x = (val << 3) | (s->vga.sr_index >> 5); - break; + break; case 0x11: case 0x31: case 0x51: - case 0x71: // Graphics Cursor Y + case 0x71: // Graphics Cursor Y case 0x91: case 0xb1: case 0xd1: - case 0xf1: // Graphics Cursor Y - s->vga.sr[0x11] = val; + case 0xf1: // Graphics Cursor Y + s->vga.sr[0x11] = val; s->vga.hw_cursor_y = (val << 3) | (s->vga.sr_index >> 5); - break; - case 0x07: // Extended Sequencer Mode + break; + case 0x07: // Extended Sequencer Mode cirrus_update_memory_access(s); /* fall through */ - case 0x08: // EEPROM Control - case 0x09: // Scratch Register 0 - case 0x0a: // Scratch Register 1 - case 0x0b: // VCLK 0 - case 0x0c: // VCLK 1 - case 0x0d: // VCLK 2 - case 0x0e: // VCLK 3 - case 0x0f: // DRAM Control - case 0x13: // Graphics Cursor Pattern Address - case 0x14: // Scratch Register 2 - case 0x15: // Scratch Register 3 - case 0x16: // Performance Tuning Register - case 0x18: // Signature Generator Control - case 0x19: // Signature Generator Result - case 0x1a: // Signature Generator Result - case 0x1b: // VCLK 0 Denominator & Post - case 0x1c: // VCLK 1 Denominator & Post - case 0x1d: // VCLK 2 Denominator & Post - case 0x1e: // VCLK 3 Denominator & Post - case 0x1f: // BIOS Write Enable and MCLK select - s->vga.sr[s->vga.sr_index] = val; + case 0x08: // EEPROM Control + case 0x09: // Scratch Register 0 + case 0x0a: // Scratch Register 1 + case 0x0b: // VCLK 0 + case 0x0c: // VCLK 1 + case 0x0d: // VCLK 2 + case 0x0e: // VCLK 3 + case 0x0f: // DRAM Control + case 0x13: // Graphics Cursor Pattern Address + case 0x14: // Scratch Register 2 + case 0x15: // Scratch Register 3 + case 0x16: // Performance Tuning Register + case 0x18: // Signature Generator Control + case 0x19: // Signature Generator Result + case 0x1a: // Signature Generator Result + case 0x1b: // VCLK 0 Denominator & Post + case 0x1c: // VCLK 1 Denominator & Post + case 0x1d: // VCLK 2 Denominator & Post + case 0x1e: // VCLK 3 Denominator & Post + case 0x1f: // BIOS Write Enable and MCLK select + s->vga.sr[s->vga.sr_index] = val; #ifdef DEBUG_CIRRUS - printf("cirrus: handled outport sr_index %02x, sr_value %02x\n", - s->vga.sr_index, val); + printf("cirrus: handled outport sr_index %02x, sr_value %02x\n", + s->vga.sr_index, val); #endif - break; - case 0x12: // Graphics Cursor Attribute - s->vga.sr[0x12] = val; + break; + case 0x12: // Graphics Cursor Attribute + s->vga.sr[0x12] = val; s->vga.force_shadow = !!(val & CIRRUS_CURSOR_SHOW); #ifdef DEBUG_CIRRUS printf("cirrus: cursor ctl SR12=%02x (force shadow: %d)\n", val, s->vga.force_shadow); #endif break; - case 0x17: // Configuration Readback and Extended Control - s->vga.sr[s->vga.sr_index] = (s->vga.sr[s->vga.sr_index] & 0x38) + case 0x17: // Configuration Readback and Extended Control + s->vga.sr[s->vga.sr_index] = (s->vga.sr[s->vga.sr_index] & 0x38) | (val & 0xc7); cirrus_update_memory_access(s); break; @@ -1403,7 +1404,7 @@ static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val) qemu_log_mask(LOG_GUEST_ERROR, "cirrus: outport sr_index 0x%02x, sr_value 0x%02x\n", s->vga.sr_index, val); - break; + break; } } @@ -1425,9 +1426,9 @@ static int cirrus_read_hidden_dac(CirrusVGAState * s) static void cirrus_write_hidden_dac(CirrusVGAState * s, int reg_value) { if (s->cirrus_hidden_dac_lockindex == 4) { - s->cirrus_hidden_dac_data = reg_value; + s->cirrus_hidden_dac_data = reg_value; #if defined(DEBUG_CIRRUS) - printf("cirrus: outport hidden DAC, value %02x\n", reg_value); + printf("cirrus: outport hidden DAC, value %02x\n", reg_value); #endif } s->cirrus_hidden_dac_lockindex = 0; @@ -1450,8 +1451,8 @@ static int cirrus_vga_read_palette(CirrusVGAState * s) val = s->vga.palette[s->vga.dac_read_index * 3 + s->vga.dac_sub_index]; } if (++s->vga.dac_sub_index == 3) { - s->vga.dac_sub_index = 0; - s->vga.dac_read_index++; + s->vga.dac_sub_index = 0; + s->vga.dac_read_index++; } return val; } @@ -1467,8 +1468,8 @@ static void cirrus_vga_write_palette(CirrusVGAState * s, int reg_value) memcpy(&s->vga.palette[s->vga.dac_write_index * 3], s->vga.dac_cache, 3); } /* XXX update cursor */ - s->vga.dac_sub_index = 0; - s->vga.dac_write_index++; + s->vga.dac_sub_index = 0; + s->vga.dac_write_index++; } } @@ -1485,24 +1486,24 @@ static int cirrus_vga_read_gr(CirrusVGAState * s, unsigned reg_index) return s->cirrus_shadow_gr0; case 0x01: // Standard VGA, FGCOLOR 0x000000ff return s->cirrus_shadow_gr1; - case 0x02: // Standard VGA - case 0x03: // Standard VGA - case 0x04: // Standard VGA - case 0x06: // Standard VGA - case 0x07: // Standard VGA - case 0x08: // Standard VGA + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + case 0x06: // Standard VGA + case 0x07: // Standard VGA + case 0x08: // Standard VGA return s->vga.gr[s->vga.gr_index]; - case 0x05: // Standard VGA, Cirrus extended mode + case 0x05: // Standard VGA, Cirrus extended mode default: - break; + break; } if (reg_index < 0x3a) { - return s->vga.gr[reg_index]; + return s->vga.gr[reg_index]; } else { qemu_log_mask(LOG_GUEST_ERROR, "cirrus: inport gr_index 0x%02x\n", reg_index); - return 0xff; + return 0xff; } } @@ -1511,87 +1512,87 @@ cirrus_vga_write_gr(CirrusVGAState * s, unsigned reg_index, int reg_value) { trace_vga_cirrus_write_gr(reg_index, reg_value); switch (reg_index) { - case 0x00: // Standard VGA, BGCOLOR 0x000000ff - s->vga.gr[reg_index] = reg_value & gr_mask[reg_index]; - s->cirrus_shadow_gr0 = reg_value; - break; - case 0x01: // Standard VGA, FGCOLOR 0x000000ff - s->vga.gr[reg_index] = reg_value & gr_mask[reg_index]; - s->cirrus_shadow_gr1 = reg_value; - break; - case 0x02: // Standard VGA - case 0x03: // Standard VGA - case 0x04: // Standard VGA - case 0x06: // Standard VGA - case 0x07: // Standard VGA - case 0x08: // Standard VGA - s->vga.gr[reg_index] = reg_value & gr_mask[reg_index]; + case 0x00: // Standard VGA, BGCOLOR 0x000000ff + s->vga.gr[reg_index] = reg_value & gr_mask[reg_index]; + s->cirrus_shadow_gr0 = reg_value; break; - case 0x05: // Standard VGA, Cirrus extended mode - s->vga.gr[reg_index] = reg_value & 0x7f; + case 0x01: // Standard VGA, FGCOLOR 0x000000ff + s->vga.gr[reg_index] = reg_value & gr_mask[reg_index]; + s->cirrus_shadow_gr1 = reg_value; + break; + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + case 0x06: // Standard VGA + case 0x07: // Standard VGA + case 0x08: // Standard VGA + s->vga.gr[reg_index] = reg_value & gr_mask[reg_index]; + break; + case 0x05: // Standard VGA, Cirrus extended mode + s->vga.gr[reg_index] = reg_value & 0x7f; cirrus_update_memory_access(s); - break; - case 0x09: // bank offset #0 - case 0x0A: // bank offset #1 - s->vga.gr[reg_index] = reg_value; - cirrus_update_bank_ptr(s, 0); - cirrus_update_bank_ptr(s, 1); + break; + case 0x09: // bank offset #0 + case 0x0A: // bank offset #1 + s->vga.gr[reg_index] = reg_value; + cirrus_update_bank_ptr(s, 0); + cirrus_update_bank_ptr(s, 1); cirrus_update_memory_access(s); break; case 0x0B: - s->vga.gr[reg_index] = reg_value; - cirrus_update_bank_ptr(s, 0); - cirrus_update_bank_ptr(s, 1); + s->vga.gr[reg_index] = reg_value; + cirrus_update_bank_ptr(s, 0); + cirrus_update_bank_ptr(s, 1); cirrus_update_memory_access(s); - break; - case 0x10: // BGCOLOR 0x0000ff00 - case 0x11: // FGCOLOR 0x0000ff00 - case 0x12: // BGCOLOR 0x00ff0000 - case 0x13: // FGCOLOR 0x00ff0000 - case 0x14: // BGCOLOR 0xff000000 - case 0x15: // FGCOLOR 0xff000000 - case 0x20: // BLT WIDTH 0x0000ff - case 0x22: // BLT HEIGHT 0x0000ff - case 0x24: // BLT DEST PITCH 0x0000ff - case 0x26: // BLT SRC PITCH 0x0000ff - case 0x28: // BLT DEST ADDR 0x0000ff - case 0x29: // BLT DEST ADDR 0x00ff00 - case 0x2c: // BLT SRC ADDR 0x0000ff - case 0x2d: // BLT SRC ADDR 0x00ff00 + break; + case 0x10: // BGCOLOR 0x0000ff00 + case 0x11: // FGCOLOR 0x0000ff00 + case 0x12: // BGCOLOR 0x00ff0000 + case 0x13: // FGCOLOR 0x00ff0000 + case 0x14: // BGCOLOR 0xff000000 + case 0x15: // FGCOLOR 0xff000000 + case 0x20: // BLT WIDTH 0x0000ff + case 0x22: // BLT HEIGHT 0x0000ff + case 0x24: // BLT DEST PITCH 0x0000ff + case 0x26: // BLT SRC PITCH 0x0000ff + case 0x28: // BLT DEST ADDR 0x0000ff + case 0x29: // BLT DEST ADDR 0x00ff00 + case 0x2c: // BLT SRC ADDR 0x0000ff + case 0x2d: // BLT SRC ADDR 0x00ff00 case 0x2f: // BLT WRITEMASK - case 0x30: // BLT MODE - case 0x32: // RASTER OP - case 0x33: // BLT MODEEXT - case 0x34: // BLT TRANSPARENT COLOR 0x00ff - case 0x35: // BLT TRANSPARENT COLOR 0xff00 - case 0x38: // BLT TRANSPARENT COLOR MASK 0x00ff - case 0x39: // BLT TRANSPARENT COLOR MASK 0xff00 - s->vga.gr[reg_index] = reg_value; - break; - case 0x21: // BLT WIDTH 0x001f00 - case 0x23: // BLT HEIGHT 0x001f00 - case 0x25: // BLT DEST PITCH 0x001f00 - case 0x27: // BLT SRC PITCH 0x001f00 - s->vga.gr[reg_index] = reg_value & 0x1f; - break; - case 0x2a: // BLT DEST ADDR 0x3f0000 - s->vga.gr[reg_index] = reg_value & 0x3f; + case 0x30: // BLT MODE + case 0x32: // RASTER OP + case 0x33: // BLT MODEEXT + case 0x34: // BLT TRANSPARENT COLOR 0x00ff + case 0x35: // BLT TRANSPARENT COLOR 0xff00 + case 0x38: // BLT TRANSPARENT COLOR MASK 0x00ff + case 0x39: // BLT TRANSPARENT COLOR MASK 0xff00 + s->vga.gr[reg_index] = reg_value; + break; + case 0x21: // BLT WIDTH 0x001f00 + case 0x23: // BLT HEIGHT 0x001f00 + case 0x25: // BLT DEST PITCH 0x001f00 + case 0x27: // BLT SRC PITCH 0x001f00 + s->vga.gr[reg_index] = reg_value & 0x1f; + break; + case 0x2a: // BLT DEST ADDR 0x3f0000 + s->vga.gr[reg_index] = reg_value & 0x3f; /* if auto start mode, starts bit blt now */ if (s->vga.gr[0x31] & CIRRUS_BLT_AUTOSTART) { cirrus_bitblt_start(s); } - break; - case 0x2e: // BLT SRC ADDR 0x3f0000 - s->vga.gr[reg_index] = reg_value & 0x3f; - break; - case 0x31: // BLT STATUS/START - cirrus_write_bitblt(s, reg_value); - break; + break; + case 0x2e: // BLT SRC ADDR 0x3f0000 + s->vga.gr[reg_index] = reg_value & 0x3f; + break; + case 0x31: // BLT STATUS/START + cirrus_write_bitblt(s, reg_value); + break; default: qemu_log_mask(LOG_GUEST_ERROR, "cirrus: outport gr_index 0x%02x, gr_value 0x%02x\n", reg_index, reg_value); - break; + break; } } @@ -1604,122 +1605,122 @@ cirrus_vga_write_gr(CirrusVGAState * s, unsigned reg_index, int reg_value) static int cirrus_vga_read_cr(CirrusVGAState * s, unsigned reg_index) { switch (reg_index) { - case 0x00: // Standard VGA - case 0x01: // Standard VGA - case 0x02: // Standard VGA - case 0x03: // Standard VGA - case 0x04: // Standard VGA - case 0x05: // Standard VGA - case 0x06: // Standard VGA - case 0x07: // Standard VGA - case 0x08: // Standard VGA - case 0x09: // Standard VGA - case 0x0a: // Standard VGA - case 0x0b: // Standard VGA - case 0x0c: // Standard VGA - case 0x0d: // Standard VGA - case 0x0e: // Standard VGA - case 0x0f: // Standard VGA - case 0x10: // Standard VGA - case 0x11: // Standard VGA - case 0x12: // Standard VGA - case 0x13: // Standard VGA - case 0x14: // Standard VGA - case 0x15: // Standard VGA - case 0x16: // Standard VGA - case 0x17: // Standard VGA - case 0x18: // Standard VGA - return s->vga.cr[s->vga.cr_index]; - case 0x24: // Attribute Controller Toggle Readback (R) + case 0x00: // Standard VGA + case 0x01: // Standard VGA + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + case 0x05: // Standard VGA + case 0x06: // Standard VGA + case 0x07: // Standard VGA + case 0x08: // Standard VGA + case 0x09: // Standard VGA + case 0x0a: // Standard VGA + case 0x0b: // Standard VGA + case 0x0c: // Standard VGA + case 0x0d: // Standard VGA + case 0x0e: // Standard VGA + case 0x0f: // Standard VGA + case 0x10: // Standard VGA + case 0x11: // Standard VGA + case 0x12: // Standard VGA + case 0x13: // Standard VGA + case 0x14: // Standard VGA + case 0x15: // Standard VGA + case 0x16: // Standard VGA + case 0x17: // Standard VGA + case 0x18: // Standard VGA + return s->vga.cr[s->vga.cr_index]; + case 0x24: // Attribute Controller Toggle Readback (R) return (s->vga.ar_flip_flop << 7); - case 0x19: // Interlace End - case 0x1a: // Miscellaneous Control - case 0x1b: // Extended Display Control - case 0x1c: // Sync Adjust and Genlock - case 0x1d: // Overlay Extended Control - case 0x22: // Graphics Data Latches Readback (R) - case 0x25: // Part Status - case 0x27: // Part ID (R) - return s->vga.cr[s->vga.cr_index]; - case 0x26: // Attribute Controller Index Readback (R) - return s->vga.ar_index & 0x3f; + case 0x19: // Interlace End + case 0x1a: // Miscellaneous Control + case 0x1b: // Extended Display Control + case 0x1c: // Sync Adjust and Genlock + case 0x1d: // Overlay Extended Control + case 0x22: // Graphics Data Latches Readback (R) + case 0x25: // Part Status + case 0x27: // Part ID (R) + return s->vga.cr[s->vga.cr_index]; + case 0x26: // Attribute Controller Index Readback (R) + return s->vga.ar_index & 0x3f; default: qemu_log_mask(LOG_GUEST_ERROR, "cirrus: inport cr_index 0x%02x\n", reg_index); - return 0xff; + return 0xff; } } static void cirrus_vga_write_cr(CirrusVGAState * s, int reg_value) { switch (s->vga.cr_index) { - case 0x00: // Standard VGA - case 0x01: // Standard VGA - case 0x02: // Standard VGA - case 0x03: // Standard VGA - case 0x04: // Standard VGA - case 0x05: // Standard VGA - case 0x06: // Standard VGA - case 0x07: // Standard VGA - case 0x08: // Standard VGA - case 0x09: // Standard VGA - case 0x0a: // Standard VGA - case 0x0b: // Standard VGA - case 0x0c: // Standard VGA - case 0x0d: // Standard VGA - case 0x0e: // Standard VGA - case 0x0f: // Standard VGA - case 0x10: // Standard VGA - case 0x11: // Standard VGA - case 0x12: // Standard VGA - case 0x13: // Standard VGA - case 0x14: // Standard VGA - case 0x15: // Standard VGA - case 0x16: // Standard VGA - case 0x17: // Standard VGA - case 0x18: // Standard VGA - /* handle CR0-7 protection */ - if ((s->vga.cr[0x11] & 0x80) && s->vga.cr_index <= 7) { - /* can always write bit 4 of CR7 */ - if (s->vga.cr_index == 7) - s->vga.cr[7] = (s->vga.cr[7] & ~0x10) | (reg_value & 0x10); - return; - } - s->vga.cr[s->vga.cr_index] = reg_value; - switch(s->vga.cr_index) { - case 0x00: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x11: - case 0x17: - s->vga.update_retrace_info(&s->vga); - break; - } + case 0x00: // Standard VGA + case 0x01: // Standard VGA + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + case 0x05: // Standard VGA + case 0x06: // Standard VGA + case 0x07: // Standard VGA + case 0x08: // Standard VGA + case 0x09: // Standard VGA + case 0x0a: // Standard VGA + case 0x0b: // Standard VGA + case 0x0c: // Standard VGA + case 0x0d: // Standard VGA + case 0x0e: // Standard VGA + case 0x0f: // Standard VGA + case 0x10: // Standard VGA + case 0x11: // Standard VGA + case 0x12: // Standard VGA + case 0x13: // Standard VGA + case 0x14: // Standard VGA + case 0x15: // Standard VGA + case 0x16: // Standard VGA + case 0x17: // Standard VGA + case 0x18: // Standard VGA + /* handle CR0-7 protection */ + if ((s->vga.cr[0x11] & 0x80) && s->vga.cr_index <= 7) { + /* can always write bit 4 of CR7 */ + if (s->vga.cr_index == 7) + s->vga.cr[7] = (s->vga.cr[7] & ~0x10) | (reg_value & 0x10); + return; + } + s->vga.cr[s->vga.cr_index] = reg_value; + switch(s->vga.cr_index) { + case 0x00: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x11: + case 0x17: + s->vga.update_retrace_info(&s->vga); + break; + } break; - case 0x19: // Interlace End - case 0x1a: // Miscellaneous Control - case 0x1b: // Extended Display Control - case 0x1c: // Sync Adjust and Genlock - case 0x1d: // Overlay Extended Control - s->vga.cr[s->vga.cr_index] = reg_value; + case 0x19: // Interlace End + case 0x1a: // Miscellaneous Control + case 0x1b: // Extended Display Control + case 0x1c: // Sync Adjust and Genlock + case 0x1d: // Overlay Extended Control + s->vga.cr[s->vga.cr_index] = reg_value; #ifdef DEBUG_CIRRUS - printf("cirrus: handled outport cr_index %02x, cr_value %02x\n", - s->vga.cr_index, reg_value); + printf("cirrus: handled outport cr_index %02x, cr_value %02x\n", + s->vga.cr_index, reg_value); #endif - break; - case 0x22: // Graphics Data Latches Readback (R) - case 0x24: // Attribute Controller Toggle Readback (R) - case 0x26: // Attribute Controller Index Readback (R) - case 0x27: // Part ID (R) - break; - case 0x25: // Part Status + break; + case 0x22: // Graphics Data Latches Readback (R) + case 0x24: // Attribute Controller Toggle Readback (R) + case 0x26: // Attribute Controller Index Readback (R) + case 0x27: // Part ID (R) + break; + case 0x25: // Part Status default: qemu_log_mask(LOG_GUEST_ERROR, "cirrus: outport cr_index 0x%02x, cr_value 0x%02x\n", s->vga.cr_index, reg_value); - break; + break; } } @@ -1735,102 +1736,102 @@ static uint8_t cirrus_mmio_blt_read(CirrusVGAState * s, unsigned address) switch (address) { case (CIRRUS_MMIO_BLTBGCOLOR + 0): - value = cirrus_vga_read_gr(s, 0x00); - break; + value = cirrus_vga_read_gr(s, 0x00); + break; case (CIRRUS_MMIO_BLTBGCOLOR + 1): - value = cirrus_vga_read_gr(s, 0x10); - break; + value = cirrus_vga_read_gr(s, 0x10); + break; case (CIRRUS_MMIO_BLTBGCOLOR + 2): - value = cirrus_vga_read_gr(s, 0x12); - break; + value = cirrus_vga_read_gr(s, 0x12); + break; case (CIRRUS_MMIO_BLTBGCOLOR + 3): - value = cirrus_vga_read_gr(s, 0x14); - break; + value = cirrus_vga_read_gr(s, 0x14); + break; case (CIRRUS_MMIO_BLTFGCOLOR + 0): - value = cirrus_vga_read_gr(s, 0x01); - break; + value = cirrus_vga_read_gr(s, 0x01); + break; case (CIRRUS_MMIO_BLTFGCOLOR + 1): - value = cirrus_vga_read_gr(s, 0x11); - break; + value = cirrus_vga_read_gr(s, 0x11); + break; case (CIRRUS_MMIO_BLTFGCOLOR + 2): - value = cirrus_vga_read_gr(s, 0x13); - break; + value = cirrus_vga_read_gr(s, 0x13); + break; case (CIRRUS_MMIO_BLTFGCOLOR + 3): - value = cirrus_vga_read_gr(s, 0x15); - break; + value = cirrus_vga_read_gr(s, 0x15); + break; case (CIRRUS_MMIO_BLTWIDTH + 0): - value = cirrus_vga_read_gr(s, 0x20); - break; + value = cirrus_vga_read_gr(s, 0x20); + break; case (CIRRUS_MMIO_BLTWIDTH + 1): - value = cirrus_vga_read_gr(s, 0x21); - break; + value = cirrus_vga_read_gr(s, 0x21); + break; case (CIRRUS_MMIO_BLTHEIGHT + 0): - value = cirrus_vga_read_gr(s, 0x22); - break; + value = cirrus_vga_read_gr(s, 0x22); + break; case (CIRRUS_MMIO_BLTHEIGHT + 1): - value = cirrus_vga_read_gr(s, 0x23); - break; + value = cirrus_vga_read_gr(s, 0x23); + break; case (CIRRUS_MMIO_BLTDESTPITCH + 0): - value = cirrus_vga_read_gr(s, 0x24); - break; + value = cirrus_vga_read_gr(s, 0x24); + break; case (CIRRUS_MMIO_BLTDESTPITCH + 1): - value = cirrus_vga_read_gr(s, 0x25); - break; + value = cirrus_vga_read_gr(s, 0x25); + break; case (CIRRUS_MMIO_BLTSRCPITCH + 0): - value = cirrus_vga_read_gr(s, 0x26); - break; + value = cirrus_vga_read_gr(s, 0x26); + break; case (CIRRUS_MMIO_BLTSRCPITCH + 1): - value = cirrus_vga_read_gr(s, 0x27); - break; + value = cirrus_vga_read_gr(s, 0x27); + break; case (CIRRUS_MMIO_BLTDESTADDR + 0): - value = cirrus_vga_read_gr(s, 0x28); - break; + value = cirrus_vga_read_gr(s, 0x28); + break; case (CIRRUS_MMIO_BLTDESTADDR + 1): - value = cirrus_vga_read_gr(s, 0x29); - break; + value = cirrus_vga_read_gr(s, 0x29); + break; case (CIRRUS_MMIO_BLTDESTADDR + 2): - value = cirrus_vga_read_gr(s, 0x2a); - break; + value = cirrus_vga_read_gr(s, 0x2a); + break; case (CIRRUS_MMIO_BLTSRCADDR + 0): - value = cirrus_vga_read_gr(s, 0x2c); - break; + value = cirrus_vga_read_gr(s, 0x2c); + break; case (CIRRUS_MMIO_BLTSRCADDR + 1): - value = cirrus_vga_read_gr(s, 0x2d); - break; + value = cirrus_vga_read_gr(s, 0x2d); + break; case (CIRRUS_MMIO_BLTSRCADDR + 2): - value = cirrus_vga_read_gr(s, 0x2e); - break; + value = cirrus_vga_read_gr(s, 0x2e); + break; case CIRRUS_MMIO_BLTWRITEMASK: - value = cirrus_vga_read_gr(s, 0x2f); - break; + value = cirrus_vga_read_gr(s, 0x2f); + break; case CIRRUS_MMIO_BLTMODE: - value = cirrus_vga_read_gr(s, 0x30); - break; + value = cirrus_vga_read_gr(s, 0x30); + break; case CIRRUS_MMIO_BLTROP: - value = cirrus_vga_read_gr(s, 0x32); - break; + value = cirrus_vga_read_gr(s, 0x32); + break; case CIRRUS_MMIO_BLTMODEEXT: - value = cirrus_vga_read_gr(s, 0x33); - break; + value = cirrus_vga_read_gr(s, 0x33); + break; case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0): - value = cirrus_vga_read_gr(s, 0x34); - break; + value = cirrus_vga_read_gr(s, 0x34); + break; case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1): - value = cirrus_vga_read_gr(s, 0x35); - break; + value = cirrus_vga_read_gr(s, 0x35); + break; case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0): - value = cirrus_vga_read_gr(s, 0x38); - break; + value = cirrus_vga_read_gr(s, 0x38); + break; case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1): - value = cirrus_vga_read_gr(s, 0x39); - break; + value = cirrus_vga_read_gr(s, 0x39); + break; case CIRRUS_MMIO_BLTSTATUS: - value = cirrus_vga_read_gr(s, 0x31); - break; + value = cirrus_vga_read_gr(s, 0x31); + break; default: qemu_log_mask(LOG_GUEST_ERROR, "cirrus: mmio read - address 0x%04x\n", address); - break; + break; } trace_vga_cirrus_write_blt(address, value); @@ -1838,111 +1839,111 @@ static uint8_t cirrus_mmio_blt_read(CirrusVGAState * s, unsigned address) } static void cirrus_mmio_blt_write(CirrusVGAState * s, unsigned address, - uint8_t value) + uint8_t value) { trace_vga_cirrus_write_blt(address, value); switch (address) { case (CIRRUS_MMIO_BLTBGCOLOR + 0): - cirrus_vga_write_gr(s, 0x00, value); - break; + cirrus_vga_write_gr(s, 0x00, value); + break; case (CIRRUS_MMIO_BLTBGCOLOR + 1): - cirrus_vga_write_gr(s, 0x10, value); - break; + cirrus_vga_write_gr(s, 0x10, value); + break; case (CIRRUS_MMIO_BLTBGCOLOR + 2): - cirrus_vga_write_gr(s, 0x12, value); - break; + cirrus_vga_write_gr(s, 0x12, value); + break; case (CIRRUS_MMIO_BLTBGCOLOR + 3): - cirrus_vga_write_gr(s, 0x14, value); - break; + cirrus_vga_write_gr(s, 0x14, value); + break; case (CIRRUS_MMIO_BLTFGCOLOR + 0): - cirrus_vga_write_gr(s, 0x01, value); - break; + cirrus_vga_write_gr(s, 0x01, value); + break; case (CIRRUS_MMIO_BLTFGCOLOR + 1): - cirrus_vga_write_gr(s, 0x11, value); - break; + cirrus_vga_write_gr(s, 0x11, value); + break; case (CIRRUS_MMIO_BLTFGCOLOR + 2): - cirrus_vga_write_gr(s, 0x13, value); - break; + cirrus_vga_write_gr(s, 0x13, value); + break; case (CIRRUS_MMIO_BLTFGCOLOR + 3): - cirrus_vga_write_gr(s, 0x15, value); - break; + cirrus_vga_write_gr(s, 0x15, value); + break; case (CIRRUS_MMIO_BLTWIDTH + 0): - cirrus_vga_write_gr(s, 0x20, value); - break; + cirrus_vga_write_gr(s, 0x20, value); + break; case (CIRRUS_MMIO_BLTWIDTH + 1): - cirrus_vga_write_gr(s, 0x21, value); - break; + cirrus_vga_write_gr(s, 0x21, value); + break; case (CIRRUS_MMIO_BLTHEIGHT + 0): - cirrus_vga_write_gr(s, 0x22, value); - break; + cirrus_vga_write_gr(s, 0x22, value); + break; case (CIRRUS_MMIO_BLTHEIGHT + 1): - cirrus_vga_write_gr(s, 0x23, value); - break; + cirrus_vga_write_gr(s, 0x23, value); + break; case (CIRRUS_MMIO_BLTDESTPITCH + 0): - cirrus_vga_write_gr(s, 0x24, value); - break; + cirrus_vga_write_gr(s, 0x24, value); + break; case (CIRRUS_MMIO_BLTDESTPITCH + 1): - cirrus_vga_write_gr(s, 0x25, value); - break; + cirrus_vga_write_gr(s, 0x25, value); + break; case (CIRRUS_MMIO_BLTSRCPITCH + 0): - cirrus_vga_write_gr(s, 0x26, value); - break; + cirrus_vga_write_gr(s, 0x26, value); + break; case (CIRRUS_MMIO_BLTSRCPITCH + 1): - cirrus_vga_write_gr(s, 0x27, value); - break; + cirrus_vga_write_gr(s, 0x27, value); + break; case (CIRRUS_MMIO_BLTDESTADDR + 0): - cirrus_vga_write_gr(s, 0x28, value); - break; + cirrus_vga_write_gr(s, 0x28, value); + break; case (CIRRUS_MMIO_BLTDESTADDR + 1): - cirrus_vga_write_gr(s, 0x29, value); - break; + cirrus_vga_write_gr(s, 0x29, value); + break; case (CIRRUS_MMIO_BLTDESTADDR + 2): - cirrus_vga_write_gr(s, 0x2a, value); - break; + cirrus_vga_write_gr(s, 0x2a, value); + break; case (CIRRUS_MMIO_BLTDESTADDR + 3): - /* ignored */ - break; + /* ignored */ + break; case (CIRRUS_MMIO_BLTSRCADDR + 0): - cirrus_vga_write_gr(s, 0x2c, value); - break; + cirrus_vga_write_gr(s, 0x2c, value); + break; case (CIRRUS_MMIO_BLTSRCADDR + 1): - cirrus_vga_write_gr(s, 0x2d, value); - break; + cirrus_vga_write_gr(s, 0x2d, value); + break; case (CIRRUS_MMIO_BLTSRCADDR + 2): - cirrus_vga_write_gr(s, 0x2e, value); - break; + cirrus_vga_write_gr(s, 0x2e, value); + break; case CIRRUS_MMIO_BLTWRITEMASK: - cirrus_vga_write_gr(s, 0x2f, value); - break; + cirrus_vga_write_gr(s, 0x2f, value); + break; case CIRRUS_MMIO_BLTMODE: - cirrus_vga_write_gr(s, 0x30, value); - break; + cirrus_vga_write_gr(s, 0x30, value); + break; case CIRRUS_MMIO_BLTROP: - cirrus_vga_write_gr(s, 0x32, value); - break; + cirrus_vga_write_gr(s, 0x32, value); + break; case CIRRUS_MMIO_BLTMODEEXT: - cirrus_vga_write_gr(s, 0x33, value); - break; + cirrus_vga_write_gr(s, 0x33, value); + break; case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0): - cirrus_vga_write_gr(s, 0x34, value); - break; + cirrus_vga_write_gr(s, 0x34, value); + break; case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1): - cirrus_vga_write_gr(s, 0x35, value); - break; + cirrus_vga_write_gr(s, 0x35, value); + break; case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0): - cirrus_vga_write_gr(s, 0x38, value); - break; + cirrus_vga_write_gr(s, 0x38, value); + break; case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1): - cirrus_vga_write_gr(s, 0x39, value); - break; + cirrus_vga_write_gr(s, 0x39, value); + break; case CIRRUS_MMIO_BLTSTATUS: - cirrus_vga_write_gr(s, 0x31, value); - break; + cirrus_vga_write_gr(s, 0x31, value); + break; default: qemu_log_mask(LOG_GUEST_ERROR, "cirrus: mmio write - addr 0x%04x val 0x%02x (ignored)\n", address, value); - break; + break; } } @@ -1953,9 +1954,9 @@ static void cirrus_mmio_blt_write(CirrusVGAState * s, unsigned address, ***************************************/ static void cirrus_mem_writeb_mode4and5_8bpp(CirrusVGAState * s, - unsigned mode, - unsigned offset, - uint32_t mem_value) + unsigned mode, + unsigned offset, + uint32_t mem_value) { int x; unsigned val = mem_value; @@ -1963,20 +1964,20 @@ static void cirrus_mem_writeb_mode4and5_8bpp(CirrusVGAState * s, for (x = 0; x < 8; x++) { dst = s->vga.vram_ptr + ((offset + x) & s->cirrus_addr_mask); - if (val & 0x80) { - *dst = s->cirrus_shadow_gr1; - } else if (mode == 5) { - *dst = s->cirrus_shadow_gr0; - } - val <<= 1; + if (val & 0x80) { + *dst = s->cirrus_shadow_gr1; + } else if (mode == 5) { + *dst = s->cirrus_shadow_gr0; + } + val <<= 1; } memory_region_set_dirty(&s->vga.vram, offset, 8); } static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s, - unsigned mode, - unsigned offset, - uint32_t mem_value) + unsigned mode, + unsigned offset, + uint32_t mem_value) { int x; unsigned val = mem_value; @@ -1984,14 +1985,14 @@ static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s, for (x = 0; x < 8; x++) { dst = s->vga.vram_ptr + ((offset + 2 * x) & s->cirrus_addr_mask & ~1); - if (val & 0x80) { - *dst = s->cirrus_shadow_gr1; - *(dst + 1) = s->vga.gr[0x11]; - } else if (mode == 5) { - *dst = s->cirrus_shadow_gr0; - *(dst + 1) = s->vga.gr[0x10]; - } - val <<= 1; + if (val & 0x80) { + *dst = s->cirrus_shadow_gr1; + *(dst + 1) = s->vga.gr[0x11]; + } else if (mode == 5) { + *dst = s->cirrus_shadow_gr0; + *(dst + 1) = s->vga.gr[0x10]; + } + val <<= 1; } memory_region_set_dirty(&s->vga.vram, offset, 16); } @@ -2016,31 +2017,31 @@ static uint64_t cirrus_vga_mem_read(void *opaque, } if (addr < 0x10000) { - /* XXX handle bitblt */ - /* video memory */ - bank_index = addr >> 15; - bank_offset = addr & 0x7fff; - if (bank_offset < s->cirrus_bank_limit[bank_index]) { - bank_offset += s->cirrus_bank_base[bank_index]; - if ((s->vga.gr[0x0B] & 0x14) == 0x14) { - bank_offset <<= 4; - } else if (s->vga.gr[0x0B] & 0x02) { - bank_offset <<= 3; - } - bank_offset &= s->cirrus_addr_mask; - val = *(s->vga.vram_ptr + bank_offset); - } else - val = 0xff; + /* XXX handle bitblt */ + /* video memory */ + bank_index = addr >> 15; + bank_offset = addr & 0x7fff; + if (bank_offset < s->cirrus_bank_limit[bank_index]) { + bank_offset += s->cirrus_bank_base[bank_index]; + if ((s->vga.gr[0x0B] & 0x14) == 0x14) { + bank_offset <<= 4; + } else if (s->vga.gr[0x0B] & 0x02) { + bank_offset <<= 3; + } + bank_offset &= s->cirrus_addr_mask; + val = *(s->vga.vram_ptr + bank_offset); + } else + val = 0xff; } else if (addr >= 0x18000 && addr < 0x18100) { - /* memory-mapped I/O */ - val = 0xff; - if ((s->vga.sr[0x17] & 0x44) == 0x04) { - val = cirrus_mmio_blt_read(s, addr & 0xff); - } + /* memory-mapped I/O */ + val = 0xff; + if ((s->vga.sr[0x17] & 0x44) == 0x04) { + val = cirrus_mmio_blt_read(s, addr & 0xff); + } } else { - val = 0xff; + val = 0xff; qemu_log_mask(LOG_GUEST_ERROR, - "cirrus: mem_readb 0x" TARGET_FMT_plx "\n", addr); + "cirrus: mem_readb 0x" HWADDR_FMT_plx "\n", addr); } return val; } @@ -2061,50 +2062,50 @@ static void cirrus_vga_mem_write(void *opaque, } if (addr < 0x10000) { - if (s->cirrus_srcptr != s->cirrus_srcptr_end) { - /* bitblt */ - *s->cirrus_srcptr++ = (uint8_t) mem_value; - if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { - cirrus_bitblt_cputovideo_next(s); - } - } else { - /* video memory */ - bank_index = addr >> 15; - bank_offset = addr & 0x7fff; - if (bank_offset < s->cirrus_bank_limit[bank_index]) { - bank_offset += s->cirrus_bank_base[bank_index]; - if ((s->vga.gr[0x0B] & 0x14) == 0x14) { - bank_offset <<= 4; - } else if (s->vga.gr[0x0B] & 0x02) { - bank_offset <<= 3; - } - bank_offset &= s->cirrus_addr_mask; - mode = s->vga.gr[0x05] & 0x7; - if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) { - *(s->vga.vram_ptr + bank_offset) = mem_value; + if (s->cirrus_srcptr != s->cirrus_srcptr_end) { + /* bitblt */ + *s->cirrus_srcptr++ = (uint8_t) mem_value; + if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { + cirrus_bitblt_cputovideo_next(s); + } + } else { + /* video memory */ + bank_index = addr >> 15; + bank_offset = addr & 0x7fff; + if (bank_offset < s->cirrus_bank_limit[bank_index]) { + bank_offset += s->cirrus_bank_base[bank_index]; + if ((s->vga.gr[0x0B] & 0x14) == 0x14) { + bank_offset <<= 4; + } else if (s->vga.gr[0x0B] & 0x02) { + bank_offset <<= 3; + } + bank_offset &= s->cirrus_addr_mask; + mode = s->vga.gr[0x05] & 0x7; + if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) { + *(s->vga.vram_ptr + bank_offset) = mem_value; memory_region_set_dirty(&s->vga.vram, bank_offset, sizeof(mem_value)); - } else { - if ((s->vga.gr[0x0B] & 0x14) != 0x14) { - cirrus_mem_writeb_mode4and5_8bpp(s, mode, - bank_offset, - mem_value); - } else { - cirrus_mem_writeb_mode4and5_16bpp(s, mode, - bank_offset, - mem_value); - } - } - } - } + } else { + if ((s->vga.gr[0x0B] & 0x14) != 0x14) { + cirrus_mem_writeb_mode4and5_8bpp(s, mode, + bank_offset, + mem_value); + } else { + cirrus_mem_writeb_mode4and5_16bpp(s, mode, + bank_offset, + mem_value); + } + } + } + } } else if (addr >= 0x18000 && addr < 0x18100) { - /* memory-mapped I/O */ - if ((s->vga.sr[0x17] & 0x44) == 0x04) { - cirrus_mmio_blt_write(s, addr & 0xff, mem_value); - } + /* memory-mapped I/O */ + if ((s->vga.sr[0x17] & 0x44) == 0x04) { + cirrus_mmio_blt_write(s, addr & 0xff, mem_value); + } } else { qemu_log_mask(LOG_GUEST_ERROR, - "cirrus: mem_writeb 0x" TARGET_FMT_plx " " + "cirrus: mem_writeb 0x" HWADDR_FMT_plx " " "value 0x%02" PRIx64 "\n", addr, mem_value); } } @@ -2326,20 +2327,20 @@ static uint64_t cirrus_linear_read(void *opaque, hwaddr addr, if (((s->vga.sr[0x17] & 0x44) == 0x44) && ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) { - /* memory-mapped I/O */ - ret = cirrus_mmio_blt_read(s, addr & 0xff); + /* memory-mapped I/O */ + ret = cirrus_mmio_blt_read(s, addr & 0xff); } else if (0) { - /* XXX handle bitblt */ - ret = 0xff; + /* XXX handle bitblt */ + ret = 0xff; } else { - /* video memory */ - if ((s->vga.gr[0x0B] & 0x14) == 0x14) { - addr <<= 4; - } else if (s->vga.gr[0x0B] & 0x02) { - addr <<= 3; - } - addr &= s->cirrus_addr_mask; - ret = *(s->vga.vram_ptr + addr); + /* video memory */ + if ((s->vga.gr[0x0B] & 0x14) == 0x14) { + addr <<= 4; + } else if (s->vga.gr[0x0B] & 0x02) { + addr <<= 3; + } + addr &= s->cirrus_addr_mask; + ret = *(s->vga.vram_ptr + addr); } return ret; @@ -2355,34 +2356,34 @@ static void cirrus_linear_write(void *opaque, hwaddr addr, if (((s->vga.sr[0x17] & 0x44) == 0x44) && ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) { - /* memory-mapped I/O */ - cirrus_mmio_blt_write(s, addr & 0xff, val); + /* memory-mapped I/O */ + cirrus_mmio_blt_write(s, addr & 0xff, val); } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) { - /* bitblt */ - *s->cirrus_srcptr++ = (uint8_t) val; - if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { - cirrus_bitblt_cputovideo_next(s); - } + /* bitblt */ + *s->cirrus_srcptr++ = (uint8_t) val; + if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { + cirrus_bitblt_cputovideo_next(s); + } } else { - /* video memory */ - if ((s->vga.gr[0x0B] & 0x14) == 0x14) { - addr <<= 4; - } else if (s->vga.gr[0x0B] & 0x02) { - addr <<= 3; - } - addr &= s->cirrus_addr_mask; + /* video memory */ + if ((s->vga.gr[0x0B] & 0x14) == 0x14) { + addr <<= 4; + } else if (s->vga.gr[0x0B] & 0x02) { + addr <<= 3; + } + addr &= s->cirrus_addr_mask; - mode = s->vga.gr[0x05] & 0x7; - if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) { - *(s->vga.vram_ptr + addr) = (uint8_t) val; + mode = s->vga.gr[0x05] & 0x7; + if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) { + *(s->vga.vram_ptr + addr) = (uint8_t) val; memory_region_set_dirty(&s->vga.vram, addr, 1); - } else { - if ((s->vga.gr[0x0B] & 0x14) != 0x14) { - cirrus_mem_writeb_mode4and5_8bpp(s, mode, addr, val); - } else { - cirrus_mem_writeb_mode4and5_16bpp(s, mode, addr, val); - } - } + } else { + if ((s->vga.gr[0x0B] & 0x14) != 0x14) { + cirrus_mem_writeb_mode4and5_8bpp(s, mode, addr, val); + } else { + cirrus_mem_writeb_mode4and5_16bpp(s, mode, addr, val); + } + } } } @@ -2415,11 +2416,11 @@ static void cirrus_linear_bitblt_write(void *opaque, CirrusVGAState *s = opaque; if (s->cirrus_srcptr != s->cirrus_srcptr_end) { - /* bitblt */ - *s->cirrus_srcptr++ = (uint8_t) val; - if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { - cirrus_bitblt_cputovideo_next(s); - } + /* bitblt */ + *s->cirrus_srcptr++ = (uint8_t) val; + if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { + cirrus_bitblt_cputovideo_next(s); + } } } @@ -2476,14 +2477,14 @@ static void cirrus_update_memory_access(CirrusVGAState *s) } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) { goto generic_io; } else { - if ((s->vga.gr[0x0B] & 0x14) == 0x14) { + if ((s->vga.gr[0x0B] & 0x14) == 0x14) { goto generic_io; - } else if (s->vga.gr[0x0B] & 0x02) { + } else if (s->vga.gr[0x0B] & 0x02) { goto generic_io; } - mode = s->vga.gr[0x05] & 0x7; - if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) { + mode = s->vga.gr[0x05] & 0x7; + if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) { map_linear_vram(s); } else { generic_io: @@ -2506,76 +2507,76 @@ static uint64_t cirrus_vga_ioport_read(void *opaque, hwaddr addr, addr += 0x3b0; if (vga_ioport_invalid(s, addr)) { - val = 0xff; + val = 0xff; } else { - switch (addr) { - case 0x3c0: - if (s->ar_flip_flop == 0) { - val = s->ar_index; - } else { - val = 0; - } - break; - case 0x3c1: - index = s->ar_index & 0x1f; - if (index < 21) - val = s->ar[index]; - else - val = 0; - break; - case 0x3c2: - val = s->st00; - break; - case 0x3c4: - val = s->sr_index; - break; - case 0x3c5: - val = cirrus_vga_read_sr(c); + switch (addr) { + case 0x3c0: + if (s->ar_flip_flop == 0) { + val = s->ar_index; + } else { + val = 0; + } + break; + case 0x3c1: + index = s->ar_index & 0x1f; + if (index < 21) + val = s->ar[index]; + else + val = 0; + break; + case 0x3c2: + val = s->st00; + break; + case 0x3c4: + val = s->sr_index; + break; + case 0x3c5: + val = cirrus_vga_read_sr(c); + break; + break; + case 0x3c6: + val = cirrus_read_hidden_dac(c); + break; + case 0x3c7: + val = s->dac_state; + break; + case 0x3c8: + val = s->dac_write_index; + c->cirrus_hidden_dac_lockindex = 0; break; - break; - case 0x3c6: - val = cirrus_read_hidden_dac(c); - break; - case 0x3c7: - val = s->dac_state; - break; - case 0x3c8: - val = s->dac_write_index; - c->cirrus_hidden_dac_lockindex = 0; - break; case 0x3c9: val = cirrus_vga_read_palette(c); break; - case 0x3ca: - val = s->fcr; - break; - case 0x3cc: - val = s->msr; - break; - case 0x3ce: - val = s->gr_index; - break; - case 0x3cf: - val = cirrus_vga_read_gr(c, s->gr_index); - break; - case 0x3b4: - case 0x3d4: - val = s->cr_index; - break; - case 0x3b5: - case 0x3d5: + case 0x3ca: + val = s->fcr; + break; + case 0x3cc: + val = s->msr; + break; + case 0x3ce: + val = s->gr_index; + break; + case 0x3cf: + val = cirrus_vga_read_gr(c, s->gr_index); + break; + case 0x3b4: + case 0x3d4: + val = s->cr_index; + break; + case 0x3b5: + case 0x3d5: val = cirrus_vga_read_cr(c, s->cr_index); - break; - case 0x3ba: - case 0x3da: - /* just toggle to fool polling */ - val = s->st01 = s->retrace(s); - s->ar_flip_flop = 0; - break; - default: - val = 0x00; - break; - } + break; + case 0x3ba: + case 0x3da: + /* just toggle to fool polling */ + val = s->st01 = s->retrace(s); + s->ar_flip_flop = 0; + break; + default: + val = 0x00; + break; + } } trace_vga_cirrus_read_io(addr, val); return val; @@ -2592,86 +2593,86 @@ static void cirrus_vga_ioport_write(void *opaque, hwaddr addr, uint64_t val, /* check port range access depending on color/monochrome mode */ if (vga_ioport_invalid(s, addr)) { - return; + return; } trace_vga_cirrus_write_io(addr, val); switch (addr) { case 0x3c0: - if (s->ar_flip_flop == 0) { - val &= 0x3f; - s->ar_index = val; - } else { - index = s->ar_index & 0x1f; - switch (index) { - case 0x00 ... 0x0f: - s->ar[index] = val & 0x3f; - break; - case 0x10: - s->ar[index] = val & ~0x10; - break; - case 0x11: - s->ar[index] = val; - break; - case 0x12: - s->ar[index] = val & ~0xc0; - break; - case 0x13: - s->ar[index] = val & ~0xf0; - break; - case 0x14: - s->ar[index] = val & ~0xf0; - break; - default: - break; - } - } - s->ar_flip_flop ^= 1; - break; + if (s->ar_flip_flop == 0) { + val &= 0x3f; + s->ar_index = val; + } else { + index = s->ar_index & 0x1f; + switch (index) { + case 0x00 ... 0x0f: + s->ar[index] = val & 0x3f; + break; + case 0x10: + s->ar[index] = val & ~0x10; + break; + case 0x11: + s->ar[index] = val; + break; + case 0x12: + s->ar[index] = val & ~0xc0; + break; + case 0x13: + s->ar[index] = val & ~0xf0; + break; + case 0x14: + s->ar[index] = val & ~0xf0; + break; + default: + break; + } + } + s->ar_flip_flop ^= 1; + break; case 0x3c2: - s->msr = val & ~0x10; - s->update_retrace_info(s); - break; + s->msr = val & ~0x10; + s->update_retrace_info(s); + break; case 0x3c4: - s->sr_index = val; - break; + s->sr_index = val; + break; case 0x3c5: - cirrus_vga_write_sr(c, val); + cirrus_vga_write_sr(c, val); break; case 0x3c6: - cirrus_write_hidden_dac(c, val); - break; + cirrus_write_hidden_dac(c, val); + break; case 0x3c7: - s->dac_read_index = val; - s->dac_sub_index = 0; - s->dac_state = 3; - break; + s->dac_read_index = val; + s->dac_sub_index = 0; + s->dac_state = 3; + break; case 0x3c8: - s->dac_write_index = val; - s->dac_sub_index = 0; - s->dac_state = 0; - break; + s->dac_write_index = val; + s->dac_sub_index = 0; + s->dac_state = 0; + break; case 0x3c9: cirrus_vga_write_palette(c, val); break; case 0x3ce: - s->gr_index = val; - break; + s->gr_index = val; + break; case 0x3cf: - cirrus_vga_write_gr(c, s->gr_index, val); - break; + cirrus_vga_write_gr(c, s->gr_index, val); + break; case 0x3b4: case 0x3d4: - s->cr_index = val; - break; + s->cr_index = val; + break; case 0x3b5: case 0x3d5: - cirrus_vga_write_cr(c, val); - break; + cirrus_vga_write_cr(c, val); + break; case 0x3ba: case 0x3da: - s->fcr = val & 0x10; - break; + s->fcr = val & 0x10; + break; } } @@ -2699,7 +2700,7 @@ static void cirrus_mmio_write(void *opaque, hwaddr addr, CirrusVGAState *s = opaque; if (addr >= 0x100) { - cirrus_mmio_blt_write(s, addr - 0x100, val); + cirrus_mmio_blt_write(s, addr - 0x100, val); } else { cirrus_vga_ioport_write(s, addr + 0x10, val, size); } @@ -2738,7 +2739,7 @@ const VMStateDescription vmstate_cirrus_vga = { .version_id = 2, .minimum_version_id = 1, .post_load = cirrus_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(vga.latch, CirrusVGAState), VMSTATE_UINT8(vga.sr_index, CirrusVGAState), VMSTATE_BUFFER(vga.sr, CirrusVGAState), @@ -2776,7 +2777,7 @@ static const VMStateDescription vmstate_pci_cirrus_vga = { .name = "cirrus_vga", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, PCICirrusVGAState), VMSTATE_STRUCT(cirrus_vga, PCICirrusVGAState, 0, vmstate_cirrus_vga, CirrusVGAState), @@ -2799,13 +2800,13 @@ static void cirrus_reset(void *opaque) s->vga.sr[0x06] = 0x0f; if (s->device_id == CIRRUS_ID_CLGD5446) { /* 4MB 64 bit memory config, always PCI */ - s->vga.sr[0x1F] = 0x2d; // MemClock + s->vga.sr[0x1F] = 0x2d; // MemClock s->vga.gr[0x18] = 0x0f; // fastest memory configuration s->vga.sr[0x0f] = 0x98; s->vga.sr[0x17] = 0x20; s->vga.sr[0x15] = 0x04; /* memory size, 3=2MB, 4=4MB */ } else { - s->vga.sr[0x1F] = 0x22; // MemClock + s->vga.sr[0x1F] = 0x22; // MemClock s->vga.sr[0x0F] = CIRRUS_MEMSIZE_2M; s->vga.sr[0x17] = s->bustype; s->vga.sr[0x15] = 0x03; /* memory size, 3=2MB, 4=4MB */ @@ -2924,7 +2925,7 @@ void cirrus_init_common(CirrusVGAState *s, Object *owner, s->linear_mmio_mask = s->real_vram_size - 256; s->vga.get_bpp = cirrus_get_bpp; - s->vga.get_offsets = cirrus_get_offsets; + s->vga.get_params = cirrus_get_params; s->vga.get_resolution = cirrus_get_resolution; s->vga.cursor_invalidate = cirrus_cursor_invalidate; s->vga.cursor_draw_line = cirrus_cursor_draw_line; diff --git a/hw/display/cirrus_vga_isa.c b/hw/display/cirrus_vga_isa.c index 96144bd690..84be51670e 100644 --- a/hw/display/cirrus_vga_isa.c +++ b/hw/display/cirrus_vga_isa.c @@ -31,6 +31,7 @@ #include "hw/isa/isa.h" #include "cirrus_vga_internal.h" #include "qom/object.h" +#include "ui/console.h" #define TYPE_ISA_CIRRUS_VGA "isa-cirrus-vga" OBJECT_DECLARE_SIMPLE_TYPE(ISACirrusVGAState, ISA_CIRRUS_VGA) diff --git a/hw/display/dpcd.c b/hw/display/dpcd.c index 64463654a1..aab1b1a2d7 100644 --- a/hw/display/dpcd.c +++ b/hw/display/dpcd.c @@ -135,7 +135,7 @@ static const VMStateDescription vmstate_dpcd = { .name = TYPE_DPCD, .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY_V(dpcd_info, DPCDState, DPCD_READABLE_AREA, 0), VMSTATE_END_OF_LIST() } diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 34a960a976..5712558e13 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "hw/qdev-properties.h" #include "hw/hw.h" #include "hw/irq.h" #include "hw/sysbus.h" @@ -32,6 +33,7 @@ #include "qemu/bswap.h" #include "qemu/module.h" #include "qemu/log.h" +#include "qapi/error.h" #include "qom/object.h" /* Debug messages configuration */ @@ -302,6 +304,7 @@ struct Exynos4210fimdState { MemoryRegion iomem; QemuConsole *console; qemu_irq irq[3]; + MemoryRegion *fbmem; uint32_t vidcon[4]; /* Video main control registers 0-3 */ uint32_t vidtcon[4]; /* Video time control registers 0-3 */ @@ -1119,7 +1122,6 @@ static void exynos4210_fimd_invalidate(void *opaque) * VIDOSDA, VIDOSDB, VIDWADDx and SHADOWCON registers */ static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win) { - SysBusDevice *sbd = SYS_BUS_DEVICE(s); Exynos4210fimdWindow *w = &s->window[win]; hwaddr fb_start_addr, fb_mapped_len; @@ -1147,8 +1149,7 @@ static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win) memory_region_unref(w->mem_section.mr); } - w->mem_section = memory_region_find(sysbus_address_space(sbd), - fb_start_addr, w->fb_len); + w->mem_section = memory_region_find(s->fbmem, fb_start_addr, w->fb_len); assert(w->mem_section.mr); assert(w->mem_section.offset_within_address_space == fb_start_addr); DPRINT_TRACE("Window %u framebuffer changed: address=0x%08x, len=0x%x\n", @@ -1865,7 +1866,7 @@ static const VMStateDescription exynos4210_fimd_window_vmstate = { .name = "exynos4210.fimd_window", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(wincon, Exynos4210fimdWindow), VMSTATE_UINT32_ARRAY(buf_start, Exynos4210fimdWindow, 3), VMSTATE_UINT32_ARRAY(buf_end, Exynos4210fimdWindow, 3), @@ -1895,7 +1896,7 @@ static const VMStateDescription exynos4210_fimd_vmstate = { .version_id = 1, .minimum_version_id = 1, .post_load = exynos4210_fimd_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(vidcon, Exynos4210fimdState, 4), VMSTATE_UINT32_ARRAY(vidtcon, Exynos4210fimdState, 4), VMSTATE_UINT32(shadowcon, Exynos4210fimdState), @@ -1924,6 +1925,12 @@ static const GraphicHwOps exynos4210_fimd_ops = { .gfx_update = exynos4210_fimd_update, }; +static Property exynos4210_fimd_properties[] = { + DEFINE_PROP_LINK("framebuffer-memory", Exynos4210fimdState, fbmem, + TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_END_OF_LIST(), +}; + static void exynos4210_fimd_init(Object *obj) { Exynos4210fimdState *s = EXYNOS4210_FIMD(obj); @@ -1944,6 +1951,11 @@ static void exynos4210_fimd_realize(DeviceState *dev, Error **errp) { Exynos4210fimdState *s = EXYNOS4210_FIMD(dev); + if (!s->fbmem) { + error_setg(errp, "'framebuffer-memory' property was not set"); + return; + } + s->console = graphic_console_init(dev, 0, &exynos4210_fimd_ops, s); } @@ -1954,6 +1966,7 @@ static void exynos4210_fimd_class_init(ObjectClass *klass, void *data) dc->vmsd = &exynos4210_fimd_vmstate; dc->reset = exynos4210_fimd_reset; dc->realize = exynos4210_fimd_realize; + device_class_set_props(dc, exynos4210_fimd_properties); } static const TypeInfo exynos4210_fimd_info = { diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index caca86d773..e08ec3f8de 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -320,7 +320,7 @@ static uint64_t g364fb_ctrl_read(void *opaque, break; default: { - error_report("g364: invalid read at [" TARGET_FMT_plx "]", + error_report("g364: invalid read at [" HWADDR_FMT_plx "]", addr); val = 0; break; @@ -424,7 +424,7 @@ static void g364fb_ctrl_write(void *opaque, break; default: error_report("g364: invalid write of 0x%" PRIx64 - " at [" TARGET_FMT_plx "]", val, addr); + " at [" HWADDR_FMT_plx "]", val, addr); break; } } @@ -455,7 +455,7 @@ static const VMStateDescription vmstate_g364fb = { .version_id = 2, .minimum_version_id = 2, .post_load = g364fb_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BUFFER_UNSAFE(color_palette, G364State, 0, 256 * 3), VMSTATE_BUFFER_UNSAFE(cursor_palette, G364State, 0, 9), VMSTATE_UINT16_ARRAY(cursor, G364State, 512), @@ -521,7 +521,7 @@ static const VMStateDescription vmstate_g364fb_sysbus = { .name = "g364fb-sysbus", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(g364, G364SysBusState, 2, vmstate_g364fb, G364State), VMSTATE_END_OF_LIST() } diff --git a/hw/display/i2c-ddc.c b/hw/display/i2c-ddc.c index 146489518c..3f9d1e1f6f 100644 --- a/hw/display/i2c-ddc.c +++ b/hw/display/i2c-ddc.c @@ -88,7 +88,7 @@ static void i2c_ddc_init(Object *obj) static const VMStateDescription vmstate_i2c_ddc = { .name = TYPE_I2CDDC, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(firstbyte, I2CDDCState), VMSTATE_UINT8(reg, I2CDDCState), VMSTATE_END_OF_LIST() diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c index dd5f4696c4..534f15dcfd 100644 --- a/hw/display/jazz_led.c +++ b/hw/display/jazz_led.c @@ -257,7 +257,7 @@ static const VMStateDescription vmstate_jazz_led = { .version_id = 0, .minimum_version_id = 0, .post_load = jazz_led_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(segments, LedState), VMSTATE_END_OF_LIST() } diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 2f8e016566..1ace341a0f 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -36,8 +36,8 @@ #define DAFB_INTR_MASK 0x104 #define DAFB_INTR_STAT 0x108 #define DAFB_INTR_CLEAR 0x10c -#define DAFB_RESET 0x200 -#define DAFB_LUT 0x213 +#define DAFB_LUT_INDEX 0x200 +#define DAFB_LUT 0x210 #define DAFB_INTR_VBL 0x4 @@ -537,6 +537,11 @@ static uint64_t macfb_ctrl_read(void *opaque, case DAFB_MODE_SENSE: val = macfb_sense_read(s); break; + case DAFB_LUT ... DAFB_LUT + 3: + val = s->color_palette[s->palette_current]; + s->palette_current = (s->palette_current + 1) % + ARRAY_SIZE(s->color_palette); + break; default: if (addr < MACFB_CTRL_TOPADDR) { val = s->regs[addr >> 2]; @@ -583,13 +588,11 @@ static void macfb_ctrl_write(void *opaque, s->regs[DAFB_INTR_STAT >> 2] &= ~DAFB_INTR_VBL; macfb_update_irq(s); break; - case DAFB_RESET: - s->palette_current = 0; - s->regs[DAFB_INTR_STAT >> 2] &= ~DAFB_INTR_VBL; - macfb_update_irq(s); + case DAFB_LUT_INDEX: + s->palette_current = (val & 0xff) * 3; break; - case DAFB_LUT: - s->color_palette[s->palette_current] = val; + case DAFB_LUT ... DAFB_LUT + 3: + s->color_palette[s->palette_current] = val & 0xff; s->palette_current = (s->palette_current + 1) % ARRAY_SIZE(s->color_palette); if (s->palette_current % 3) { @@ -624,7 +627,7 @@ static const VMStateDescription vmstate_macfb = { .version_id = 1, .minimum_version_id = 1, .post_load = macfb_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(type, MacfbState), VMSTATE_UINT8_ARRAY(color_palette, MacfbState, 256 * 3), VMSTATE_UINT32(palette_current, MacfbState), @@ -711,6 +714,7 @@ static void macfb_nubus_set_irq(void *opaque, int n, int level) static void macfb_nubus_realize(DeviceState *dev, Error **errp) { + ERRP_GUARD(); NubusDevice *nd = NUBUS_DEVICE(dev); MacfbNubusState *s = NUBUS_MACFB(dev); MacfbNubusDeviceClass *ndc = NUBUS_MACFB_GET_CLASS(dev); @@ -767,7 +771,7 @@ static const VMStateDescription vmstate_macfb_sysbus = { .name = "macfb-sysbus", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(macfb, MacfbSysBusState, 1, vmstate_macfb, MacfbState), VMSTATE_END_OF_LIST() } @@ -786,7 +790,7 @@ static const VMStateDescription vmstate_macfb_nubus = { .name = "macfb-nubus", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(macfb, MacfbNubusState, 1, vmstate_macfb, MacfbState), VMSTATE_END_OF_LIST() } diff --git a/hw/display/meson.build b/hw/display/meson.build index adc53dd8b6..f93a69f70f 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -1,70 +1,95 @@ hw_display_modules = {} -softmmu_ss.add(when: 'CONFIG_DDC', if_true: files('i2c-ddc.c')) -softmmu_ss.add(when: 'CONFIG_EDID', if_true: files('edid-generate.c', 'edid-region.c')) +system_ss.add(when: 'CONFIG_DDC', if_true: files('i2c-ddc.c')) +system_ss.add(when: 'CONFIG_EDID', if_true: files('edid-generate.c', 'edid-region.c')) -softmmu_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('ramfb.c')) -softmmu_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('ramfb-standalone.c')) +system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('ramfb.c')) +system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('ramfb-standalone.c')) -softmmu_ss.add(when: 'CONFIG_VGA_CIRRUS', if_true: files('cirrus_vga.c')) -softmmu_ss.add(when: ['CONFIG_VGA_CIRRUS', 'CONFIG_VGA_ISA'], if_true: files('cirrus_vga_isa.c')) -softmmu_ss.add(when: 'CONFIG_G364FB', if_true: files('g364fb.c')) -softmmu_ss.add(when: 'CONFIG_JAZZ_LED', if_true: files('jazz_led.c')) -softmmu_ss.add(when: 'CONFIG_PL110', if_true: files('pl110.c')) -softmmu_ss.add(when: 'CONFIG_SII9022', if_true: files('sii9022.c')) -softmmu_ss.add(when: 'CONFIG_SSD0303', if_true: files('ssd0303.c')) -softmmu_ss.add(when: 'CONFIG_SSD0323', if_true: files('ssd0323.c')) -softmmu_ss.add(when: 'CONFIG_XEN', if_true: files('xenfb.c')) +system_ss.add(when: 'CONFIG_VGA_CIRRUS', if_true: files('cirrus_vga.c')) +system_ss.add(when: ['CONFIG_VGA_CIRRUS', 'CONFIG_VGA_ISA'], if_true: files('cirrus_vga_isa.c')) +system_ss.add(when: 'CONFIG_G364FB', if_true: files('g364fb.c')) +system_ss.add(when: 'CONFIG_JAZZ_LED', if_true: files('jazz_led.c')) +system_ss.add(when: 'CONFIG_PL110', if_true: files('pl110.c')) +system_ss.add(when: 'CONFIG_SII9022', if_true: files('sii9022.c')) +system_ss.add(when: 'CONFIG_SSD0303', if_true: files('ssd0303.c')) +system_ss.add(when: 'CONFIG_SSD0323', if_true: files('ssd0323.c')) +system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xenfb.c')) -softmmu_ss.add(when: 'CONFIG_VGA_PCI', if_true: files('vga-pci.c')) -softmmu_ss.add(when: 'CONFIG_VGA_ISA', if_true: files('vga-isa.c')) -softmmu_ss.add(when: 'CONFIG_VGA_MMIO', if_true: files('vga-mmio.c')) -softmmu_ss.add(when: 'CONFIG_VMWARE_VGA', if_true: files('vmware_vga.c')) -softmmu_ss.add(when: 'CONFIG_BOCHS_DISPLAY', if_true: files('bochs-display.c')) +system_ss.add(when: 'CONFIG_VGA_PCI', if_true: files('vga-pci.c')) +system_ss.add(when: 'CONFIG_VGA_ISA', if_true: files('vga-isa.c')) +system_ss.add(when: 'CONFIG_VGA_MMIO', if_true: files('vga-mmio.c')) +system_ss.add(when: 'CONFIG_VMWARE_VGA', if_true: files('vmware_vga.c')) +system_ss.add(when: 'CONFIG_BOCHS_DISPLAY', if_true: files('bochs-display.c')) -softmmu_ss.add(when: 'CONFIG_BLIZZARD', if_true: files('blizzard.c')) -softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_fimd.c')) -softmmu_ss.add(when: 'CONFIG_FRAMEBUFFER', if_true: files('framebuffer.c')) -softmmu_ss.add(when: 'CONFIG_ZAURUS', if_true: files('tc6393xb.c')) +system_ss.add(when: 'CONFIG_BLIZZARD', if_true: files('blizzard.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_fimd.c')) +system_ss.add(when: 'CONFIG_FRAMEBUFFER', if_true: files('framebuffer.c')) +system_ss.add(when: 'CONFIG_ZAURUS', if_true: files('tc6393xb.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dss.c')) -softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_lcd.c')) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_fb.c')) -softmmu_ss.add(when: 'CONFIG_SM501', if_true: files('sm501.c')) -softmmu_ss.add(when: 'CONFIG_TCX', if_true: files('tcx.c')) -softmmu_ss.add(when: 'CONFIG_CG3', if_true: files('cg3.c')) -softmmu_ss.add(when: 'CONFIG_MACFB', if_true: files('macfb.c')) -softmmu_ss.add(when: 'CONFIG_NEXTCUBE', if_true: files('next-fb.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dss.c')) +system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_lcd.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_fb.c')) +system_ss.add(when: 'CONFIG_SM501', if_true: files('sm501.c')) +system_ss.add(when: 'CONFIG_TCX', if_true: files('tcx.c')) +system_ss.add(when: 'CONFIG_CG3', if_true: files('cg3.c')) +system_ss.add(when: 'CONFIG_MACFB', if_true: files('macfb.c')) +system_ss.add(when: 'CONFIG_NEXTCUBE', if_true: files('next-fb.c')) -specific_ss.add(when: 'CONFIG_VGA', if_true: files('vga.c')) +system_ss.add(when: 'CONFIG_VGA', if_true: files('vga.c')) +system_ss.add(when: 'CONFIG_VIRTIO', if_true: files('virtio-dmabuf.c')) + +if (config_all_devices.has_key('CONFIG_VGA_CIRRUS') or + config_all_devices.has_key('CONFIG_VGA_PCI') or + config_all_devices.has_key('CONFIG_VMWARE_VGA') or + config_all_devices.has_key('CONFIG_ATI_VGA') + ) + system_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'), + if_false: files('acpi-vga-stub.c')) +endif if config_all_devices.has_key('CONFIG_QXL') qxl_ss = ss.source_set() qxl_ss.add(when: 'CONFIG_QXL', if_true: [files('qxl.c', 'qxl-logger.c', 'qxl-render.c'), pixman, spice]) + qxl_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'), + if_false: files('acpi-vga-stub.c')) hw_display_modules += {'qxl': qxl_ss} endif -softmmu_ss.add(when: 'CONFIG_DPCD', if_true: files('dpcd.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx_dp.c')) +system_ss.add(when: 'CONFIG_DPCD', if_true: files('dpcd.c')) +system_ss.add(when: 'CONFIG_XLNX_DISPLAYPORT', if_true: files('xlnx_dp.c')) -softmmu_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) +system_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) + +system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_dbg.c'), pixman]) -softmmu_ss.add(when: [pixman, 'CONFIG_ATI_VGA'], if_true: files('ati.c', 'ati_2d.c', 'ati_dbg.c')) if config_all_devices.has_key('CONFIG_VIRTIO_GPU') virtio_gpu_ss = ss.source_set() virtio_gpu_ss.add(when: 'CONFIG_VIRTIO_GPU', if_true: [files('virtio-gpu-base.c', 'virtio-gpu.c'), pixman]) - virtio_gpu_ss.add(when: 'CONFIG_LINUX', if_true: files('virtio-gpu-udmabuf.c'), - if_false: files('virtio-gpu-udmabuf-stubs.c')) + if host_os == 'linux' + virtio_gpu_ss.add(files('virtio-gpu-udmabuf.c')) + else + virtio_gpu_ss.add(files('virtio-gpu-udmabuf-stubs.c')) + endif virtio_gpu_ss.add(when: 'CONFIG_VHOST_USER_GPU', if_true: files('vhost-user-gpu.c')) hw_display_modules += {'virtio-gpu': virtio_gpu_ss} - virtio_gpu_gl_ss = ss.source_set() - virtio_gpu_gl_ss.add(when: ['CONFIG_VIRTIO_GPU', virgl, opengl], - if_true: [files('virtio-gpu-gl.c', 'virtio-gpu-virgl.c'), pixman, virgl]) - hw_display_modules += {'virtio-gpu-gl': virtio_gpu_gl_ss} + if virgl.found() and opengl.found() + virtio_gpu_gl_ss = ss.source_set() + virtio_gpu_gl_ss.add(when: ['CONFIG_VIRTIO_GPU', virgl, opengl], + if_true: [files('virtio-gpu-gl.c', 'virtio-gpu-virgl.c'), pixman, virgl]) + hw_display_modules += {'virtio-gpu-gl': virtio_gpu_gl_ss} + endif + + if rutabaga.found() + virtio_gpu_rutabaga_ss = ss.source_set() + virtio_gpu_rutabaga_ss.add(when: ['CONFIG_VIRTIO_GPU', rutabaga], + if_true: [files('virtio-gpu-rutabaga.c'), pixman]) + hw_display_modules += {'virtio-gpu-rutabaga': virtio_gpu_rutabaga_ss} + endif endif if config_all_devices.has_key('CONFIG_VIRTIO_PCI') @@ -75,10 +100,18 @@ if config_all_devices.has_key('CONFIG_VIRTIO_PCI') if_true: files('vhost-user-gpu-pci.c')) hw_display_modules += {'virtio-gpu-pci': virtio_gpu_pci_ss} - virtio_gpu_pci_gl_ss = ss.source_set() - virtio_gpu_pci_gl_ss.add(when: ['CONFIG_VIRTIO_GPU', 'CONFIG_VIRTIO_PCI', virgl, opengl], - if_true: [files('virtio-gpu-pci-gl.c'), pixman]) - hw_display_modules += {'virtio-gpu-pci-gl': virtio_gpu_pci_gl_ss} + if virgl.found() and opengl.found() + virtio_gpu_pci_gl_ss = ss.source_set() + virtio_gpu_pci_gl_ss.add(when: ['CONFIG_VIRTIO_GPU', 'CONFIG_VIRTIO_PCI', virgl, opengl], + if_true: [files('virtio-gpu-pci-gl.c'), pixman]) + hw_display_modules += {'virtio-gpu-pci-gl': virtio_gpu_pci_gl_ss} + endif + if rutabaga.found() + virtio_gpu_pci_rutabaga_ss = ss.source_set() + virtio_gpu_pci_rutabaga_ss.add(when: ['CONFIG_VIRTIO_GPU', 'CONFIG_VIRTIO_PCI', rutabaga], + if_true: [files('virtio-gpu-pci-rutabaga.c'), pixman]) + hw_display_modules += {'virtio-gpu-pci-rutabaga': virtio_gpu_pci_rutabaga_ss} + endif endif if config_all_devices.has_key('CONFIG_VIRTIO_VGA') @@ -87,14 +120,27 @@ if config_all_devices.has_key('CONFIG_VIRTIO_VGA') if_true: [files('virtio-vga.c'), pixman]) virtio_vga_ss.add(when: 'CONFIG_VHOST_USER_VGA', if_true: files('vhost-user-vga.c')) + virtio_vga_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'), + if_false: files('acpi-vga-stub.c')) hw_display_modules += {'virtio-vga': virtio_vga_ss} virtio_vga_gl_ss = ss.source_set() virtio_vga_gl_ss.add(when: ['CONFIG_VIRTIO_VGA', virgl, opengl], if_true: [files('virtio-vga-gl.c'), pixman]) + virtio_vga_gl_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'), + if_false: files('acpi-vga-stub.c')) hw_display_modules += {'virtio-vga-gl': virtio_vga_gl_ss} + + if rutabaga.found() + virtio_vga_rutabaga_ss = ss.source_set() + virtio_vga_rutabaga_ss.add(when: ['CONFIG_VIRTIO_VGA', rutabaga], + if_true: [files('virtio-vga-rutabaga.c'), pixman]) + virtio_vga_rutabaga_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'), + if_false: files('acpi-vga-stub.c')) + hw_display_modules += {'virtio-vga-rutabaga': virtio_vga_rutabaga_ss} + endif endif -specific_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_lcdc.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_lcdc.c')) modules += { 'hw-display': hw_display_modules } diff --git a/hw/display/next-fb.c b/hw/display/next-fb.c index dd6a1aa8ae..8446ff3c00 100644 --- a/hw/display/next-fb.c +++ b/hw/display/next-fb.c @@ -126,7 +126,7 @@ static void nextfb_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->realize = nextfb_realize; - /* Note: This device does not any state that we have to reset or migrate */ + /* Note: This device does not have any state that we have to reset or migrate */ } static const TypeInfo nextfb_info = { diff --git a/hw/display/omap_dss.c b/hw/display/omap_dss.c index 8c0e9ee700..f33fc7606d 100644 --- a/hw/display/omap_dss.c +++ b/hw/display/omap_dss.c @@ -175,32 +175,32 @@ void omap_dss_reset(struct omap_dss_s *s) static uint64_t omap_diss_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_dss_s *s = (struct omap_dss_s *) opaque; + struct omap_dss_s *s = opaque; if (size != 4) { return omap_badwidth_read32(opaque, addr); } switch (addr) { - case 0x00: /* DSS_REVISIONNUMBER */ + case 0x00: /* DSS_REVISIONNUMBER */ return 0x20; - case 0x10: /* DSS_SYSCONFIG */ + case 0x10: /* DSS_SYSCONFIG */ return s->autoidle; - case 0x14: /* DSS_SYSSTATUS */ - return 1; /* RESETDONE */ + case 0x14: /* DSS_SYSSTATUS */ + return 1; /* RESETDONE */ - case 0x40: /* DSS_CONTROL */ + case 0x40: /* DSS_CONTROL */ return s->control; - case 0x50: /* DSS_PSA_LCD_REG_1 */ - case 0x54: /* DSS_PSA_LCD_REG_2 */ - case 0x58: /* DSS_PSA_VIDEO_REG */ + case 0x50: /* DSS_PSA_LCD_REG_1 */ + case 0x54: /* DSS_PSA_LCD_REG_2 */ + case 0x58: /* DSS_PSA_VIDEO_REG */ /* TODO: fake some values when appropriate s->control bits are set */ return 0; - case 0x5c: /* DSS_STATUS */ + case 0x5c: /* DSS_STATUS */ return 1 + (s->control & 1); default: @@ -213,7 +213,7 @@ static uint64_t omap_diss_read(void *opaque, hwaddr addr, static void omap_diss_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_dss_s *s = (struct omap_dss_s *) opaque; + struct omap_dss_s *s = opaque; if (size != 4) { omap_badwidth_write32(opaque, addr, value); @@ -221,22 +221,22 @@ static void omap_diss_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* DSS_REVISIONNUMBER */ - case 0x14: /* DSS_SYSSTATUS */ - case 0x50: /* DSS_PSA_LCD_REG_1 */ - case 0x54: /* DSS_PSA_LCD_REG_2 */ - case 0x58: /* DSS_PSA_VIDEO_REG */ - case 0x5c: /* DSS_STATUS */ + case 0x00: /* DSS_REVISIONNUMBER */ + case 0x14: /* DSS_SYSSTATUS */ + case 0x50: /* DSS_PSA_LCD_REG_1 */ + case 0x54: /* DSS_PSA_LCD_REG_2 */ + case 0x58: /* DSS_PSA_VIDEO_REG */ + case 0x5c: /* DSS_STATUS */ OMAP_RO_REG(addr); break; - case 0x10: /* DSS_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ + case 0x10: /* DSS_SYSCONFIG */ + if (value & 2) /* SOFTRESET */ omap_dss_reset(s); s->autoidle = value & 1; break; - case 0x40: /* DSS_CONTROL */ + case 0x40: /* DSS_CONTROL */ s->control = value & 0x3dd; break; @@ -254,119 +254,119 @@ static const MemoryRegionOps omap_diss_ops = { static uint64_t omap_disc_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_dss_s *s = (struct omap_dss_s *) opaque; + struct omap_dss_s *s = opaque; if (size != 4) { return omap_badwidth_read32(opaque, addr); } switch (addr) { - case 0x000: /* DISPC_REVISION */ + case 0x000: /* DISPC_REVISION */ return 0x20; - case 0x010: /* DISPC_SYSCONFIG */ + case 0x010: /* DISPC_SYSCONFIG */ return s->dispc.idlemode; - case 0x014: /* DISPC_SYSSTATUS */ - return 1; /* RESETDONE */ + case 0x014: /* DISPC_SYSSTATUS */ + return 1; /* RESETDONE */ - case 0x018: /* DISPC_IRQSTATUS */ + case 0x018: /* DISPC_IRQSTATUS */ return s->dispc.irqst; - case 0x01c: /* DISPC_IRQENABLE */ + case 0x01c: /* DISPC_IRQENABLE */ return s->dispc.irqen; - case 0x040: /* DISPC_CONTROL */ + case 0x040: /* DISPC_CONTROL */ return s->dispc.control; - case 0x044: /* DISPC_CONFIG */ + case 0x044: /* DISPC_CONFIG */ return s->dispc.config; - case 0x048: /* DISPC_CAPABLE */ + case 0x048: /* DISPC_CAPABLE */ return s->dispc.capable; - case 0x04c: /* DISPC_DEFAULT_COLOR0 */ + case 0x04c: /* DISPC_DEFAULT_COLOR0 */ return s->dispc.bg[0]; - case 0x050: /* DISPC_DEFAULT_COLOR1 */ + case 0x050: /* DISPC_DEFAULT_COLOR1 */ return s->dispc.bg[1]; - case 0x054: /* DISPC_TRANS_COLOR0 */ + case 0x054: /* DISPC_TRANS_COLOR0 */ return s->dispc.trans[0]; - case 0x058: /* DISPC_TRANS_COLOR1 */ + case 0x058: /* DISPC_TRANS_COLOR1 */ return s->dispc.trans[1]; - case 0x05c: /* DISPC_LINE_STATUS */ + case 0x05c: /* DISPC_LINE_STATUS */ return 0x7ff; - case 0x060: /* DISPC_LINE_NUMBER */ + case 0x060: /* DISPC_LINE_NUMBER */ return s->dispc.line; - case 0x064: /* DISPC_TIMING_H */ + case 0x064: /* DISPC_TIMING_H */ return s->dispc.timing[0]; - case 0x068: /* DISPC_TIMING_V */ + case 0x068: /* DISPC_TIMING_V */ return s->dispc.timing[1]; - case 0x06c: /* DISPC_POL_FREQ */ + case 0x06c: /* DISPC_POL_FREQ */ return s->dispc.timing[2]; - case 0x070: /* DISPC_DIVISOR */ + case 0x070: /* DISPC_DIVISOR */ return s->dispc.timing[3]; - case 0x078: /* DISPC_SIZE_DIG */ + case 0x078: /* DISPC_SIZE_DIG */ return ((s->dig.ny - 1) << 16) | (s->dig.nx - 1); - case 0x07c: /* DISPC_SIZE_LCD */ + case 0x07c: /* DISPC_SIZE_LCD */ return ((s->lcd.ny - 1) << 16) | (s->lcd.nx - 1); - case 0x080: /* DISPC_GFX_BA0 */ + case 0x080: /* DISPC_GFX_BA0 */ return s->dispc.l[0].addr[0]; - case 0x084: /* DISPC_GFX_BA1 */ + case 0x084: /* DISPC_GFX_BA1 */ return s->dispc.l[0].addr[1]; - case 0x088: /* DISPC_GFX_POSITION */ + case 0x088: /* DISPC_GFX_POSITION */ return (s->dispc.l[0].posy << 16) | s->dispc.l[0].posx; - case 0x08c: /* DISPC_GFX_SIZE */ + case 0x08c: /* DISPC_GFX_SIZE */ return ((s->dispc.l[0].ny - 1) << 16) | (s->dispc.l[0].nx - 1); - case 0x0a0: /* DISPC_GFX_ATTRIBUTES */ + case 0x0a0: /* DISPC_GFX_ATTRIBUTES */ return s->dispc.l[0].attr; - case 0x0a4: /* DISPC_GFX_FIFO_TRESHOLD */ + case 0x0a4: /* DISPC_GFX_FIFO_TRESHOLD */ return s->dispc.l[0].tresh; - case 0x0a8: /* DISPC_GFX_FIFO_SIZE_STATUS */ + case 0x0a8: /* DISPC_GFX_FIFO_SIZE_STATUS */ return 256; - case 0x0ac: /* DISPC_GFX_ROW_INC */ + case 0x0ac: /* DISPC_GFX_ROW_INC */ return s->dispc.l[0].rowinc; - case 0x0b0: /* DISPC_GFX_PIXEL_INC */ + case 0x0b0: /* DISPC_GFX_PIXEL_INC */ return s->dispc.l[0].colinc; - case 0x0b4: /* DISPC_GFX_WINDOW_SKIP */ + case 0x0b4: /* DISPC_GFX_WINDOW_SKIP */ return s->dispc.l[0].wininc; - case 0x0b8: /* DISPC_GFX_TABLE_BA */ + case 0x0b8: /* DISPC_GFX_TABLE_BA */ return s->dispc.l[0].addr[2]; - case 0x0bc: /* DISPC_VID1_BA0 */ - case 0x0c0: /* DISPC_VID1_BA1 */ - case 0x0c4: /* DISPC_VID1_POSITION */ - case 0x0c8: /* DISPC_VID1_SIZE */ - case 0x0cc: /* DISPC_VID1_ATTRIBUTES */ - case 0x0d0: /* DISPC_VID1_FIFO_TRESHOLD */ - case 0x0d4: /* DISPC_VID1_FIFO_SIZE_STATUS */ - case 0x0d8: /* DISPC_VID1_ROW_INC */ - case 0x0dc: /* DISPC_VID1_PIXEL_INC */ - case 0x0e0: /* DISPC_VID1_FIR */ - case 0x0e4: /* DISPC_VID1_PICTURE_SIZE */ - case 0x0e8: /* DISPC_VID1_ACCU0 */ - case 0x0ec: /* DISPC_VID1_ACCU1 */ - case 0x0f0 ... 0x140: /* DISPC_VID1_FIR_COEF, DISPC_VID1_CONV_COEF */ - case 0x14c: /* DISPC_VID2_BA0 */ - case 0x150: /* DISPC_VID2_BA1 */ - case 0x154: /* DISPC_VID2_POSITION */ - case 0x158: /* DISPC_VID2_SIZE */ - case 0x15c: /* DISPC_VID2_ATTRIBUTES */ - case 0x160: /* DISPC_VID2_FIFO_TRESHOLD */ - case 0x164: /* DISPC_VID2_FIFO_SIZE_STATUS */ - case 0x168: /* DISPC_VID2_ROW_INC */ - case 0x16c: /* DISPC_VID2_PIXEL_INC */ - case 0x170: /* DISPC_VID2_FIR */ - case 0x174: /* DISPC_VID2_PICTURE_SIZE */ - case 0x178: /* DISPC_VID2_ACCU0 */ - case 0x17c: /* DISPC_VID2_ACCU1 */ - case 0x180 ... 0x1d0: /* DISPC_VID2_FIR_COEF, DISPC_VID2_CONV_COEF */ - case 0x1d4: /* DISPC_DATA_CYCLE1 */ - case 0x1d8: /* DISPC_DATA_CYCLE2 */ - case 0x1dc: /* DISPC_DATA_CYCLE3 */ + case 0x0bc: /* DISPC_VID1_BA0 */ + case 0x0c0: /* DISPC_VID1_BA1 */ + case 0x0c4: /* DISPC_VID1_POSITION */ + case 0x0c8: /* DISPC_VID1_SIZE */ + case 0x0cc: /* DISPC_VID1_ATTRIBUTES */ + case 0x0d0: /* DISPC_VID1_FIFO_TRESHOLD */ + case 0x0d4: /* DISPC_VID1_FIFO_SIZE_STATUS */ + case 0x0d8: /* DISPC_VID1_ROW_INC */ + case 0x0dc: /* DISPC_VID1_PIXEL_INC */ + case 0x0e0: /* DISPC_VID1_FIR */ + case 0x0e4: /* DISPC_VID1_PICTURE_SIZE */ + case 0x0e8: /* DISPC_VID1_ACCU0 */ + case 0x0ec: /* DISPC_VID1_ACCU1 */ + case 0x0f0 ... 0x140: /* DISPC_VID1_FIR_COEF, DISPC_VID1_CONV_COEF */ + case 0x14c: /* DISPC_VID2_BA0 */ + case 0x150: /* DISPC_VID2_BA1 */ + case 0x154: /* DISPC_VID2_POSITION */ + case 0x158: /* DISPC_VID2_SIZE */ + case 0x15c: /* DISPC_VID2_ATTRIBUTES */ + case 0x160: /* DISPC_VID2_FIFO_TRESHOLD */ + case 0x164: /* DISPC_VID2_FIFO_SIZE_STATUS */ + case 0x168: /* DISPC_VID2_ROW_INC */ + case 0x16c: /* DISPC_VID2_PIXEL_INC */ + case 0x170: /* DISPC_VID2_FIR */ + case 0x174: /* DISPC_VID2_PICTURE_SIZE */ + case 0x178: /* DISPC_VID2_ACCU0 */ + case 0x17c: /* DISPC_VID2_ACCU1 */ + case 0x180 ... 0x1d0: /* DISPC_VID2_FIR_COEF, DISPC_VID2_CONV_COEF */ + case 0x1d4: /* DISPC_DATA_CYCLE1 */ + case 0x1d8: /* DISPC_DATA_CYCLE2 */ + case 0x1dc: /* DISPC_DATA_CYCLE3 */ return 0; default: @@ -379,7 +379,7 @@ static uint64_t omap_disc_read(void *opaque, hwaddr addr, static void omap_disc_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_dss_s *s = (struct omap_dss_s *) opaque; + struct omap_dss_s *s = opaque; if (size != 4) { omap_badwidth_write32(opaque, addr, value); @@ -387,33 +387,33 @@ static void omap_disc_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x010: /* DISPC_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ + case 0x010: /* DISPC_SYSCONFIG */ + if (value & 2) /* SOFTRESET */ omap_dss_reset(s); s->dispc.idlemode = value & 0x301b; break; - case 0x018: /* DISPC_IRQSTATUS */ + case 0x018: /* DISPC_IRQSTATUS */ s->dispc.irqst &= ~value; omap_dispc_interrupt_update(s); break; - case 0x01c: /* DISPC_IRQENABLE */ + case 0x01c: /* DISPC_IRQENABLE */ s->dispc.irqen = value & 0xffff; omap_dispc_interrupt_update(s); break; - case 0x040: /* DISPC_CONTROL */ + case 0x040: /* DISPC_CONTROL */ s->dispc.control = value & 0x07ff9fff; s->dig.enable = (value >> 1) & 1; s->lcd.enable = (value >> 0) & 1; - if (value & (1 << 12)) /* OVERLAY_OPTIMIZATION */ + if (value & (1 << 12)) /* OVERLAY_OPTIMIZATION */ if (!((s->dispc.l[1].attr | s->dispc.l[2].attr) & 1)) { fprintf(stderr, "%s: Overlay Optimization when no overlay " "region effectively exists leads to " "unpredictable behaviour!\n", __func__); } - if (value & (1 << 6)) { /* GODIGITAL */ + if (value & (1 << 6)) { /* GODIGITAL */ /* XXX: Shadowed fields are: * s->dispc.config * s->dispc.capable @@ -444,13 +444,13 @@ static void omap_disc_write(void *opaque, hwaddr addr, * All they need to be loaded here from their shadow registers. */ } - if (value & (1 << 5)) { /* GOLCD */ + if (value & (1 << 5)) { /* GOLCD */ /* XXX: Likewise for LCD here. */ } s->dispc.invalidate = 1; break; - case 0x044: /* DISPC_CONFIG */ + case 0x044: /* DISPC_CONFIG */ s->dispc.config = value & 0x3fff; /* XXX: * bits 2:1 (LOADMODE) reset to 0 after set to 1 and palette loaded @@ -459,73 +459,73 @@ static void omap_disc_write(void *opaque, hwaddr addr, s->dispc.invalidate = 1; break; - case 0x048: /* DISPC_CAPABLE */ + case 0x048: /* DISPC_CAPABLE */ s->dispc.capable = value & 0x3ff; break; - case 0x04c: /* DISPC_DEFAULT_COLOR0 */ + case 0x04c: /* DISPC_DEFAULT_COLOR0 */ s->dispc.bg[0] = value & 0xffffff; s->dispc.invalidate = 1; break; - case 0x050: /* DISPC_DEFAULT_COLOR1 */ + case 0x050: /* DISPC_DEFAULT_COLOR1 */ s->dispc.bg[1] = value & 0xffffff; s->dispc.invalidate = 1; break; - case 0x054: /* DISPC_TRANS_COLOR0 */ + case 0x054: /* DISPC_TRANS_COLOR0 */ s->dispc.trans[0] = value & 0xffffff; s->dispc.invalidate = 1; break; - case 0x058: /* DISPC_TRANS_COLOR1 */ + case 0x058: /* DISPC_TRANS_COLOR1 */ s->dispc.trans[1] = value & 0xffffff; s->dispc.invalidate = 1; break; - case 0x060: /* DISPC_LINE_NUMBER */ + case 0x060: /* DISPC_LINE_NUMBER */ s->dispc.line = value & 0x7ff; break; - case 0x064: /* DISPC_TIMING_H */ + case 0x064: /* DISPC_TIMING_H */ s->dispc.timing[0] = value & 0x0ff0ff3f; break; - case 0x068: /* DISPC_TIMING_V */ + case 0x068: /* DISPC_TIMING_V */ s->dispc.timing[1] = value & 0x0ff0ff3f; break; - case 0x06c: /* DISPC_POL_FREQ */ + case 0x06c: /* DISPC_POL_FREQ */ s->dispc.timing[2] = value & 0x0003ffff; break; - case 0x070: /* DISPC_DIVISOR */ + case 0x070: /* DISPC_DIVISOR */ s->dispc.timing[3] = value & 0x00ff00ff; break; - case 0x078: /* DISPC_SIZE_DIG */ - s->dig.nx = ((value >> 0) & 0x7ff) + 1; /* PPL */ - s->dig.ny = ((value >> 16) & 0x7ff) + 1; /* LPP */ + case 0x078: /* DISPC_SIZE_DIG */ + s->dig.nx = ((value >> 0) & 0x7ff) + 1; /* PPL */ + s->dig.ny = ((value >> 16) & 0x7ff) + 1; /* LPP */ s->dispc.invalidate = 1; break; - case 0x07c: /* DISPC_SIZE_LCD */ - s->lcd.nx = ((value >> 0) & 0x7ff) + 1; /* PPL */ - s->lcd.ny = ((value >> 16) & 0x7ff) + 1; /* LPP */ + case 0x07c: /* DISPC_SIZE_LCD */ + s->lcd.nx = ((value >> 0) & 0x7ff) + 1; /* PPL */ + s->lcd.ny = ((value >> 16) & 0x7ff) + 1; /* LPP */ s->dispc.invalidate = 1; break; - case 0x080: /* DISPC_GFX_BA0 */ + case 0x080: /* DISPC_GFX_BA0 */ s->dispc.l[0].addr[0] = (hwaddr) value; s->dispc.invalidate = 1; break; - case 0x084: /* DISPC_GFX_BA1 */ + case 0x084: /* DISPC_GFX_BA1 */ s->dispc.l[0].addr[1] = (hwaddr) value; s->dispc.invalidate = 1; break; - case 0x088: /* DISPC_GFX_POSITION */ - s->dispc.l[0].posx = ((value >> 0) & 0x7ff); /* GFXPOSX */ - s->dispc.l[0].posy = ((value >> 16) & 0x7ff); /* GFXPOSY */ + case 0x088: /* DISPC_GFX_POSITION */ + s->dispc.l[0].posx = ((value >> 0) & 0x7ff); /* GFXPOSX */ + s->dispc.l[0].posy = ((value >> 16) & 0x7ff); /* GFXPOSY */ s->dispc.invalidate = 1; break; - case 0x08c: /* DISPC_GFX_SIZE */ - s->dispc.l[0].nx = ((value >> 0) & 0x7ff) + 1; /* GFXSIZEX */ - s->dispc.l[0].ny = ((value >> 16) & 0x7ff) + 1; /* GFXSIZEY */ + case 0x08c: /* DISPC_GFX_SIZE */ + s->dispc.l[0].nx = ((value >> 0) & 0x7ff) + 1; /* GFXSIZEX */ + s->dispc.l[0].ny = ((value >> 16) & 0x7ff) + 1; /* GFXSIZEY */ s->dispc.invalidate = 1; break; - case 0x0a0: /* DISPC_GFX_ATTRIBUTES */ + case 0x0a0: /* DISPC_GFX_ATTRIBUTES */ s->dispc.l[0].attr = value & 0x7ff; if (value & (3 << 9)) fprintf(stderr, "%s: Big-endian pixel format not supported\n", @@ -534,54 +534,54 @@ static void omap_disc_write(void *opaque, hwaddr addr, s->dispc.l[0].bpp = (value >> 1) & 0xf; s->dispc.invalidate = 1; break; - case 0x0a4: /* DISPC_GFX_FIFO_TRESHOLD */ + case 0x0a4: /* DISPC_GFX_FIFO_TRESHOLD */ s->dispc.l[0].tresh = value & 0x01ff01ff; break; - case 0x0ac: /* DISPC_GFX_ROW_INC */ + case 0x0ac: /* DISPC_GFX_ROW_INC */ s->dispc.l[0].rowinc = value; s->dispc.invalidate = 1; break; - case 0x0b0: /* DISPC_GFX_PIXEL_INC */ + case 0x0b0: /* DISPC_GFX_PIXEL_INC */ s->dispc.l[0].colinc = value; s->dispc.invalidate = 1; break; - case 0x0b4: /* DISPC_GFX_WINDOW_SKIP */ + case 0x0b4: /* DISPC_GFX_WINDOW_SKIP */ s->dispc.l[0].wininc = value; break; - case 0x0b8: /* DISPC_GFX_TABLE_BA */ + case 0x0b8: /* DISPC_GFX_TABLE_BA */ s->dispc.l[0].addr[2] = (hwaddr) value; s->dispc.invalidate = 1; break; - case 0x0bc: /* DISPC_VID1_BA0 */ - case 0x0c0: /* DISPC_VID1_BA1 */ - case 0x0c4: /* DISPC_VID1_POSITION */ - case 0x0c8: /* DISPC_VID1_SIZE */ - case 0x0cc: /* DISPC_VID1_ATTRIBUTES */ - case 0x0d0: /* DISPC_VID1_FIFO_TRESHOLD */ - case 0x0d8: /* DISPC_VID1_ROW_INC */ - case 0x0dc: /* DISPC_VID1_PIXEL_INC */ - case 0x0e0: /* DISPC_VID1_FIR */ - case 0x0e4: /* DISPC_VID1_PICTURE_SIZE */ - case 0x0e8: /* DISPC_VID1_ACCU0 */ - case 0x0ec: /* DISPC_VID1_ACCU1 */ - case 0x0f0 ... 0x140: /* DISPC_VID1_FIR_COEF, DISPC_VID1_CONV_COEF */ - case 0x14c: /* DISPC_VID2_BA0 */ - case 0x150: /* DISPC_VID2_BA1 */ - case 0x154: /* DISPC_VID2_POSITION */ - case 0x158: /* DISPC_VID2_SIZE */ - case 0x15c: /* DISPC_VID2_ATTRIBUTES */ - case 0x160: /* DISPC_VID2_FIFO_TRESHOLD */ - case 0x168: /* DISPC_VID2_ROW_INC */ - case 0x16c: /* DISPC_VID2_PIXEL_INC */ - case 0x170: /* DISPC_VID2_FIR */ - case 0x174: /* DISPC_VID2_PICTURE_SIZE */ - case 0x178: /* DISPC_VID2_ACCU0 */ - case 0x17c: /* DISPC_VID2_ACCU1 */ - case 0x180 ... 0x1d0: /* DISPC_VID2_FIR_COEF, DISPC_VID2_CONV_COEF */ - case 0x1d4: /* DISPC_DATA_CYCLE1 */ - case 0x1d8: /* DISPC_DATA_CYCLE2 */ - case 0x1dc: /* DISPC_DATA_CYCLE3 */ + case 0x0bc: /* DISPC_VID1_BA0 */ + case 0x0c0: /* DISPC_VID1_BA1 */ + case 0x0c4: /* DISPC_VID1_POSITION */ + case 0x0c8: /* DISPC_VID1_SIZE */ + case 0x0cc: /* DISPC_VID1_ATTRIBUTES */ + case 0x0d0: /* DISPC_VID1_FIFO_TRESHOLD */ + case 0x0d8: /* DISPC_VID1_ROW_INC */ + case 0x0dc: /* DISPC_VID1_PIXEL_INC */ + case 0x0e0: /* DISPC_VID1_FIR */ + case 0x0e4: /* DISPC_VID1_PICTURE_SIZE */ + case 0x0e8: /* DISPC_VID1_ACCU0 */ + case 0x0ec: /* DISPC_VID1_ACCU1 */ + case 0x0f0 ... 0x140: /* DISPC_VID1_FIR_COEF, DISPC_VID1_CONV_COEF */ + case 0x14c: /* DISPC_VID2_BA0 */ + case 0x150: /* DISPC_VID2_BA1 */ + case 0x154: /* DISPC_VID2_POSITION */ + case 0x158: /* DISPC_VID2_SIZE */ + case 0x15c: /* DISPC_VID2_ATTRIBUTES */ + case 0x160: /* DISPC_VID2_FIFO_TRESHOLD */ + case 0x168: /* DISPC_VID2_ROW_INC */ + case 0x16c: /* DISPC_VID2_PIXEL_INC */ + case 0x170: /* DISPC_VID2_FIR */ + case 0x174: /* DISPC_VID2_PICTURE_SIZE */ + case 0x178: /* DISPC_VID2_ACCU0 */ + case 0x17c: /* DISPC_VID2_ACCU1 */ + case 0x180 ... 0x1d0: /* DISPC_VID2_FIR_COEF, DISPC_VID2_CONV_COEF */ + case 0x1d4: /* DISPC_DATA_CYCLE1 */ + case 0x1d8: /* DISPC_DATA_CYCLE2 */ + case 0x1dc: /* DISPC_DATA_CYCLE3 */ break; default: @@ -617,14 +617,14 @@ static void omap_rfbi_transfer_start(struct omap_dss_s *s) if (!s->rfbi.enable || s->rfbi.busy) return; - if (s->rfbi.control & (1 << 1)) { /* BYPASS */ + if (s->rfbi.control & (1 << 1)) { /* BYPASS */ /* TODO: in non-Bypass mode we probably need to just assert the * DRQ and wait for DMA to write the pixels. */ qemu_log_mask(LOG_UNIMP, "%s: Bypass mode unimplemented\n", __func__); return; } - if (!(s->dispc.control & (1 << 11))) /* RFBIMODE */ + if (!(s->dispc.control & (1 << 11))) /* RFBIMODE */ return; /* TODO: check that LCD output is enabled in DISPC. */ @@ -665,71 +665,70 @@ static void omap_rfbi_transfer_start(struct omap_dss_s *s) omap_rfbi_transfer_stop(s); /* TODO */ - s->dispc.irqst |= 1; /* FRAMEDONE */ + s->dispc.irqst |= 1; /* FRAMEDONE */ omap_dispc_interrupt_update(s); } -static uint64_t omap_rfbi_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_rfbi_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_dss_s *s = (struct omap_dss_s *) opaque; + struct omap_dss_s *s = opaque; if (size != 4) { return omap_badwidth_read32(opaque, addr); } switch (addr) { - case 0x00: /* RFBI_REVISION */ + case 0x00: /* RFBI_REVISION */ return 0x10; - case 0x10: /* RFBI_SYSCONFIG */ + case 0x10: /* RFBI_SYSCONFIG */ return s->rfbi.idlemode; - case 0x14: /* RFBI_SYSSTATUS */ - return 1 | (s->rfbi.busy << 8); /* RESETDONE */ + case 0x14: /* RFBI_SYSSTATUS */ + return 1 | (s->rfbi.busy << 8); /* RESETDONE */ - case 0x40: /* RFBI_CONTROL */ + case 0x40: /* RFBI_CONTROL */ return s->rfbi.control; - case 0x44: /* RFBI_PIXELCNT */ + case 0x44: /* RFBI_PIXELCNT */ return s->rfbi.pixels; - case 0x48: /* RFBI_LINE_NUMBER */ + case 0x48: /* RFBI_LINE_NUMBER */ return s->rfbi.skiplines; - case 0x58: /* RFBI_READ */ - case 0x5c: /* RFBI_STATUS */ + case 0x58: /* RFBI_READ */ + case 0x5c: /* RFBI_STATUS */ return s->rfbi.rxbuf; - case 0x60: /* RFBI_CONFIG0 */ + case 0x60: /* RFBI_CONFIG0 */ return s->rfbi.config[0]; - case 0x64: /* RFBI_ONOFF_TIME0 */ + case 0x64: /* RFBI_ONOFF_TIME0 */ return s->rfbi.time[0]; - case 0x68: /* RFBI_CYCLE_TIME0 */ + case 0x68: /* RFBI_CYCLE_TIME0 */ return s->rfbi.time[1]; - case 0x6c: /* RFBI_DATA_CYCLE1_0 */ + case 0x6c: /* RFBI_DATA_CYCLE1_0 */ return s->rfbi.data[0]; - case 0x70: /* RFBI_DATA_CYCLE2_0 */ + case 0x70: /* RFBI_DATA_CYCLE2_0 */ return s->rfbi.data[1]; - case 0x74: /* RFBI_DATA_CYCLE3_0 */ + case 0x74: /* RFBI_DATA_CYCLE3_0 */ return s->rfbi.data[2]; - case 0x78: /* RFBI_CONFIG1 */ + case 0x78: /* RFBI_CONFIG1 */ return s->rfbi.config[1]; - case 0x7c: /* RFBI_ONOFF_TIME1 */ + case 0x7c: /* RFBI_ONOFF_TIME1 */ return s->rfbi.time[2]; - case 0x80: /* RFBI_CYCLE_TIME1 */ + case 0x80: /* RFBI_CYCLE_TIME1 */ return s->rfbi.time[3]; - case 0x84: /* RFBI_DATA_CYCLE1_1 */ + case 0x84: /* RFBI_DATA_CYCLE1_1 */ return s->rfbi.data[3]; - case 0x88: /* RFBI_DATA_CYCLE2_1 */ + case 0x88: /* RFBI_DATA_CYCLE2_1 */ return s->rfbi.data[4]; - case 0x8c: /* RFBI_DATA_CYCLE3_1 */ + case 0x8c: /* RFBI_DATA_CYCLE3_1 */ return s->rfbi.data[5]; - case 0x90: /* RFBI_VSYNC_WIDTH */ + case 0x90: /* RFBI_VSYNC_WIDTH */ return s->rfbi.vsync; - case 0x94: /* RFBI_HSYNC_WIDTH */ + case 0x94: /* RFBI_HSYNC_WIDTH */ return s->rfbi.hsync; } OMAP_BAD_REG(addr); @@ -739,7 +738,7 @@ static uint64_t omap_rfbi_read(void *opaque, hwaddr addr, static void omap_rfbi_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_dss_s *s = (struct omap_dss_s *) opaque; + struct omap_dss_s *s = opaque; if (size != 4) { omap_badwidth_write32(opaque, addr, value); @@ -747,41 +746,41 @@ static void omap_rfbi_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x10: /* RFBI_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ + case 0x10: /* RFBI_SYSCONFIG */ + if (value & 2) /* SOFTRESET */ omap_rfbi_reset(s); s->rfbi.idlemode = value & 0x19; break; - case 0x40: /* RFBI_CONTROL */ + case 0x40: /* RFBI_CONTROL */ s->rfbi.control = value & 0xf; s->rfbi.enable = value & 1; - if (value & (1 << 4) && /* ITE */ + if (value & (1 << 4) && /* ITE */ !(s->rfbi.config[0] & s->rfbi.config[1] & 0xc)) omap_rfbi_transfer_start(s); break; - case 0x44: /* RFBI_PIXELCNT */ + case 0x44: /* RFBI_PIXELCNT */ s->rfbi.pixels = value; break; - case 0x48: /* RFBI_LINE_NUMBER */ + case 0x48: /* RFBI_LINE_NUMBER */ s->rfbi.skiplines = value & 0x7ff; break; - case 0x4c: /* RFBI_CMD */ + case 0x4c: /* RFBI_CMD */ if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 0, value & 0xffff); if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 0, value & 0xffff); break; - case 0x50: /* RFBI_PARAM */ + case 0x50: /* RFBI_PARAM */ if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value & 0xffff); if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value & 0xffff); break; - case 0x54: /* RFBI_DATA */ + case 0x54: /* RFBI_DATA */ /* TODO: take into account the format set up in s->rfbi.config[?] and * s->rfbi.data[?], but special-case the most usual scenario so that * speed doesn't suffer. */ @@ -796,7 +795,7 @@ static void omap_rfbi_write(void *opaque, hwaddr addr, if (!-- s->rfbi.pixels) omap_rfbi_transfer_stop(s); break; - case 0x58: /* RFBI_READ */ + case 0x58: /* RFBI_READ */ if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 1); else if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) @@ -805,7 +804,7 @@ static void omap_rfbi_write(void *opaque, hwaddr addr, omap_rfbi_transfer_stop(s); break; - case 0x5c: /* RFBI_STATUS */ + case 0x5c: /* RFBI_STATUS */ if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 0); else if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) @@ -814,49 +813,49 @@ static void omap_rfbi_write(void *opaque, hwaddr addr, omap_rfbi_transfer_stop(s); break; - case 0x60: /* RFBI_CONFIG0 */ + case 0x60: /* RFBI_CONFIG0 */ s->rfbi.config[0] = value & 0x003f1fff; break; - case 0x64: /* RFBI_ONOFF_TIME0 */ + case 0x64: /* RFBI_ONOFF_TIME0 */ s->rfbi.time[0] = value & 0x3fffffff; break; - case 0x68: /* RFBI_CYCLE_TIME0 */ + case 0x68: /* RFBI_CYCLE_TIME0 */ s->rfbi.time[1] = value & 0x0fffffff; break; - case 0x6c: /* RFBI_DATA_CYCLE1_0 */ + case 0x6c: /* RFBI_DATA_CYCLE1_0 */ s->rfbi.data[0] = value & 0x0f1f0f1f; break; - case 0x70: /* RFBI_DATA_CYCLE2_0 */ + case 0x70: /* RFBI_DATA_CYCLE2_0 */ s->rfbi.data[1] = value & 0x0f1f0f1f; break; - case 0x74: /* RFBI_DATA_CYCLE3_0 */ + case 0x74: /* RFBI_DATA_CYCLE3_0 */ s->rfbi.data[2] = value & 0x0f1f0f1f; break; - case 0x78: /* RFBI_CONFIG1 */ + case 0x78: /* RFBI_CONFIG1 */ s->rfbi.config[1] = value & 0x003f1fff; break; - case 0x7c: /* RFBI_ONOFF_TIME1 */ + case 0x7c: /* RFBI_ONOFF_TIME1 */ s->rfbi.time[2] = value & 0x3fffffff; break; - case 0x80: /* RFBI_CYCLE_TIME1 */ + case 0x80: /* RFBI_CYCLE_TIME1 */ s->rfbi.time[3] = value & 0x0fffffff; break; - case 0x84: /* RFBI_DATA_CYCLE1_1 */ + case 0x84: /* RFBI_DATA_CYCLE1_1 */ s->rfbi.data[3] = value & 0x0f1f0f1f; break; - case 0x88: /* RFBI_DATA_CYCLE2_1 */ + case 0x88: /* RFBI_DATA_CYCLE2_1 */ s->rfbi.data[4] = value & 0x0f1f0f1f; break; - case 0x8c: /* RFBI_DATA_CYCLE3_1 */ + case 0x8c: /* RFBI_DATA_CYCLE3_1 */ s->rfbi.data[5] = value & 0x0f1f0f1f; break; - case 0x90: /* RFBI_VSYNC_WIDTH */ + case 0x90: /* RFBI_VSYNC_WIDTH */ s->rfbi.vsync = value & 0xffff; break; - case 0x94: /* RFBI_HSYNC_WIDTH */ + case 0x94: /* RFBI_HSYNC_WIDTH */ s->rfbi.hsync = value & 0xffff; break; @@ -879,49 +878,49 @@ static uint64_t omap_venc_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* REV_ID */ - case 0x04: /* STATUS */ - case 0x08: /* F_CONTROL */ - case 0x10: /* VIDOUT_CTRL */ - case 0x14: /* SYNC_CTRL */ - case 0x1c: /* LLEN */ - case 0x20: /* FLENS */ - case 0x24: /* HFLTR_CTRL */ - case 0x28: /* CC_CARR_WSS_CARR */ - case 0x2c: /* C_PHASE */ - case 0x30: /* GAIN_U */ - case 0x34: /* GAIN_V */ - case 0x38: /* GAIN_Y */ - case 0x3c: /* BLACK_LEVEL */ - case 0x40: /* BLANK_LEVEL */ - case 0x44: /* X_COLOR */ - case 0x48: /* M_CONTROL */ - case 0x4c: /* BSTAMP_WSS_DATA */ - case 0x50: /* S_CARR */ - case 0x54: /* LINE21 */ - case 0x58: /* LN_SEL */ - case 0x5c: /* L21__WC_CTL */ - case 0x60: /* HTRIGGER_VTRIGGER */ - case 0x64: /* SAVID__EAVID */ - case 0x68: /* FLEN__FAL */ - case 0x6c: /* LAL__PHASE_RESET */ - case 0x70: /* HS_INT_START_STOP_X */ - case 0x74: /* HS_EXT_START_STOP_X */ - case 0x78: /* VS_INT_START_X */ - case 0x7c: /* VS_INT_STOP_X__VS_INT_START_Y */ - case 0x80: /* VS_INT_STOP_Y__VS_INT_START_X */ - case 0x84: /* VS_EXT_STOP_X__VS_EXT_START_Y */ - case 0x88: /* VS_EXT_STOP_Y */ - case 0x90: /* AVID_START_STOP_X */ - case 0x94: /* AVID_START_STOP_Y */ - case 0xa0: /* FID_INT_START_X__FID_INT_START_Y */ - case 0xa4: /* FID_INT_OFFSET_Y__FID_EXT_START_X */ - case 0xa8: /* FID_EXT_START_Y__FID_EXT_OFFSET_Y */ - case 0xb0: /* TVDETGP_INT_START_STOP_X */ - case 0xb4: /* TVDETGP_INT_START_STOP_Y */ - case 0xb8: /* GEN_CTRL */ - case 0xc4: /* DAC_TST__DAC_A */ - case 0xc8: /* DAC_B__DAC_C */ + case 0x00: /* REV_ID */ + case 0x04: /* STATUS */ + case 0x08: /* F_CONTROL */ + case 0x10: /* VIDOUT_CTRL */ + case 0x14: /* SYNC_CTRL */ + case 0x1c: /* LLEN */ + case 0x20: /* FLENS */ + case 0x24: /* HFLTR_CTRL */ + case 0x28: /* CC_CARR_WSS_CARR */ + case 0x2c: /* C_PHASE */ + case 0x30: /* GAIN_U */ + case 0x34: /* GAIN_V */ + case 0x38: /* GAIN_Y */ + case 0x3c: /* BLACK_LEVEL */ + case 0x40: /* BLANK_LEVEL */ + case 0x44: /* X_COLOR */ + case 0x48: /* M_CONTROL */ + case 0x4c: /* BSTAMP_WSS_DATA */ + case 0x50: /* S_CARR */ + case 0x54: /* LINE21 */ + case 0x58: /* LN_SEL */ + case 0x5c: /* L21__WC_CTL */ + case 0x60: /* HTRIGGER_VTRIGGER */ + case 0x64: /* SAVID__EAVID */ + case 0x68: /* FLEN__FAL */ + case 0x6c: /* LAL__PHASE_RESET */ + case 0x70: /* HS_INT_START_STOP_X */ + case 0x74: /* HS_EXT_START_STOP_X */ + case 0x78: /* VS_INT_START_X */ + case 0x7c: /* VS_INT_STOP_X__VS_INT_START_Y */ + case 0x80: /* VS_INT_STOP_Y__VS_INT_START_X */ + case 0x84: /* VS_EXT_STOP_X__VS_EXT_START_Y */ + case 0x88: /* VS_EXT_STOP_Y */ + case 0x90: /* AVID_START_STOP_X */ + case 0x94: /* AVID_START_STOP_Y */ + case 0xa0: /* FID_INT_START_X__FID_INT_START_Y */ + case 0xa4: /* FID_INT_OFFSET_Y__FID_EXT_START_X */ + case 0xa8: /* FID_EXT_START_Y__FID_EXT_OFFSET_Y */ + case 0xb0: /* TVDETGP_INT_START_STOP_X */ + case 0xb4: /* TVDETGP_INT_START_STOP_Y */ + case 0xb8: /* GEN_CTRL */ + case 0xc4: /* DAC_TST__DAC_A */ + case 0xc8: /* DAC_B__DAC_C */ return 0; default: @@ -940,47 +939,47 @@ static void omap_venc_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x08: /* F_CONTROL */ - case 0x10: /* VIDOUT_CTRL */ - case 0x14: /* SYNC_CTRL */ - case 0x1c: /* LLEN */ - case 0x20: /* FLENS */ - case 0x24: /* HFLTR_CTRL */ - case 0x28: /* CC_CARR_WSS_CARR */ - case 0x2c: /* C_PHASE */ - case 0x30: /* GAIN_U */ - case 0x34: /* GAIN_V */ - case 0x38: /* GAIN_Y */ - case 0x3c: /* BLACK_LEVEL */ - case 0x40: /* BLANK_LEVEL */ - case 0x44: /* X_COLOR */ - case 0x48: /* M_CONTROL */ - case 0x4c: /* BSTAMP_WSS_DATA */ - case 0x50: /* S_CARR */ - case 0x54: /* LINE21 */ - case 0x58: /* LN_SEL */ - case 0x5c: /* L21__WC_CTL */ - case 0x60: /* HTRIGGER_VTRIGGER */ - case 0x64: /* SAVID__EAVID */ - case 0x68: /* FLEN__FAL */ - case 0x6c: /* LAL__PHASE_RESET */ - case 0x70: /* HS_INT_START_STOP_X */ - case 0x74: /* HS_EXT_START_STOP_X */ - case 0x78: /* VS_INT_START_X */ - case 0x7c: /* VS_INT_STOP_X__VS_INT_START_Y */ - case 0x80: /* VS_INT_STOP_Y__VS_INT_START_X */ - case 0x84: /* VS_EXT_STOP_X__VS_EXT_START_Y */ - case 0x88: /* VS_EXT_STOP_Y */ - case 0x90: /* AVID_START_STOP_X */ - case 0x94: /* AVID_START_STOP_Y */ - case 0xa0: /* FID_INT_START_X__FID_INT_START_Y */ - case 0xa4: /* FID_INT_OFFSET_Y__FID_EXT_START_X */ - case 0xa8: /* FID_EXT_START_Y__FID_EXT_OFFSET_Y */ - case 0xb0: /* TVDETGP_INT_START_STOP_X */ - case 0xb4: /* TVDETGP_INT_START_STOP_Y */ - case 0xb8: /* GEN_CTRL */ - case 0xc4: /* DAC_TST__DAC_A */ - case 0xc8: /* DAC_B__DAC_C */ + case 0x08: /* F_CONTROL */ + case 0x10: /* VIDOUT_CTRL */ + case 0x14: /* SYNC_CTRL */ + case 0x1c: /* LLEN */ + case 0x20: /* FLENS */ + case 0x24: /* HFLTR_CTRL */ + case 0x28: /* CC_CARR_WSS_CARR */ + case 0x2c: /* C_PHASE */ + case 0x30: /* GAIN_U */ + case 0x34: /* GAIN_V */ + case 0x38: /* GAIN_Y */ + case 0x3c: /* BLACK_LEVEL */ + case 0x40: /* BLANK_LEVEL */ + case 0x44: /* X_COLOR */ + case 0x48: /* M_CONTROL */ + case 0x4c: /* BSTAMP_WSS_DATA */ + case 0x50: /* S_CARR */ + case 0x54: /* LINE21 */ + case 0x58: /* LN_SEL */ + case 0x5c: /* L21__WC_CTL */ + case 0x60: /* HTRIGGER_VTRIGGER */ + case 0x64: /* SAVID__EAVID */ + case 0x68: /* FLEN__FAL */ + case 0x6c: /* LAL__PHASE_RESET */ + case 0x70: /* HS_INT_START_STOP_X */ + case 0x74: /* HS_EXT_START_STOP_X */ + case 0x78: /* VS_INT_START_X */ + case 0x7c: /* VS_INT_STOP_X__VS_INT_START_Y */ + case 0x80: /* VS_INT_STOP_Y__VS_INT_START_X */ + case 0x84: /* VS_EXT_STOP_X__VS_EXT_START_Y */ + case 0x88: /* VS_EXT_STOP_Y */ + case 0x90: /* AVID_START_STOP_X */ + case 0x94: /* AVID_START_STOP_Y */ + case 0xa0: /* FID_INT_START_X__FID_INT_START_Y */ + case 0xa4: /* FID_INT_OFFSET_Y__FID_EXT_START_X */ + case 0xa8: /* FID_EXT_START_Y__FID_EXT_OFFSET_Y */ + case 0xb0: /* TVDETGP_INT_START_STOP_X */ + case 0xb4: /* TVDETGP_INT_START_STOP_Y */ + case 0xb8: /* GEN_CTRL */ + case 0xc4: /* DAC_TST__DAC_A */ + case 0xc8: /* DAC_B__DAC_C */ break; default: @@ -1002,15 +1001,15 @@ static uint64_t omap_im3_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x0a8: /* SBIMERRLOGA */ - case 0x0b0: /* SBIMERRLOG */ - case 0x190: /* SBIMSTATE */ - case 0x198: /* SBTMSTATE_L */ - case 0x19c: /* SBTMSTATE_H */ - case 0x1a8: /* SBIMCONFIG_L */ - case 0x1ac: /* SBIMCONFIG_H */ - case 0x1f8: /* SBID_L */ - case 0x1fc: /* SBID_H */ + case 0x0a8: /* SBIMERRLOGA */ + case 0x0b0: /* SBIMERRLOG */ + case 0x190: /* SBIMSTATE */ + case 0x198: /* SBTMSTATE_L */ + case 0x19c: /* SBTMSTATE_H */ + case 0x1a8: /* SBIMCONFIG_L */ + case 0x1ac: /* SBIMCONFIG_H */ + case 0x1f8: /* SBID_L */ + case 0x1fc: /* SBID_H */ return 0; default: @@ -1029,12 +1028,12 @@ static void omap_im3_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x0b0: /* SBIMERRLOG */ - case 0x190: /* SBIMSTATE */ - case 0x198: /* SBTMSTATE_L */ - case 0x19c: /* SBTMSTATE_H */ - case 0x1a8: /* SBIMCONFIG_L */ - case 0x1ac: /* SBIMCONFIG_H */ + case 0x0b0: /* SBIMERRLOG */ + case 0x190: /* SBIMSTATE */ + case 0x198: /* SBTMSTATE_L */ + case 0x19c: /* SBTMSTATE_H */ + case 0x1a8: /* SBIMCONFIG_L */ + case 0x1ac: /* SBIMCONFIG_H */ break; default: diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c index 0ba42ef637..3532a801be 100644 --- a/hw/display/omap_lcdc.c +++ b/hw/display/omap_lcdc.c @@ -198,7 +198,7 @@ static void draw_line16_32(void *opaque, uint8_t *d, const uint8_t *s, static void omap_update_display(void *opaque) { - struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque; + struct omap_lcd_panel_s *omap_lcd = opaque; DisplaySurface *surface; drawfn draw_line; int size, height, first, last; @@ -376,10 +376,9 @@ static void omap_lcd_update(struct omap_lcd_panel_s *s) { } } -static uint64_t omap_lcdc_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_lcdc_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; + struct omap_lcd_panel_s *s = opaque; switch (addr) { case 0x00: /* LCD_CONTROL */ @@ -412,7 +411,7 @@ static uint64_t omap_lcdc_read(void *opaque, hwaddr addr, static void omap_lcdc_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; + struct omap_lcd_panel_s *s = opaque; switch (addr) { case 0x00: /* LCD_CONTROL */ diff --git a/hw/display/pl110.c b/hw/display/pl110.c index 4bf15c1da5..7f145bbdba 100644 --- a/hw/display/pl110.c +++ b/hw/display/pl110.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "hw/irq.h" #include "hw/sysbus.h" +#include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "ui/console.h" #include "framebuffer.h" @@ -17,6 +18,7 @@ #include "qemu/timer.h" #include "qemu/log.h" #include "qemu/module.h" +#include "qapi/error.h" #include "qom/object.h" #define PL110_CR_EN 0x001 @@ -74,6 +76,7 @@ struct PL110State { uint32_t palette[256]; uint32_t raw_palette[128]; qemu_irq irq; + MemoryRegion *fbmem; }; static int vmstate_pl110_post_load(void *opaque, int version_id); @@ -83,7 +86,7 @@ static const VMStateDescription vmstate_pl110 = { .version_id = 2, .minimum_version_id = 1, .post_load = vmstate_pl110_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(version, PL110State), VMSTATE_UINT32_ARRAY(timing, PL110State, 4), VMSTATE_UINT32(cr, PL110State), @@ -210,7 +213,6 @@ static int pl110_enabled(PL110State *s) static void pl110_update_display(void *opaque) { PL110State *s = (PL110State *)opaque; - SysBusDevice *sbd; DisplaySurface *surface = qemu_console_surface(s->con); drawfn fn; int src_width; @@ -222,8 +224,6 @@ static void pl110_update_display(void *opaque) return; } - sbd = SYS_BUS_DEVICE(s); - if (s->cr & PL110_CR_BGR) bpp_offset = 0; else @@ -290,7 +290,7 @@ static void pl110_update_display(void *opaque) first = 0; if (s->invalidate) { framebuffer_update_memory_section(&s->fbsection, - sysbus_address_space(sbd), + s->fbmem, s->upbase, s->rows, src_width); } @@ -535,11 +535,22 @@ static const GraphicHwOps pl110_gfx_ops = { .gfx_update = pl110_update_display, }; +static Property pl110_properties[] = { + DEFINE_PROP_LINK("framebuffer-memory", PL110State, fbmem, + TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_END_OF_LIST(), +}; + static void pl110_realize(DeviceState *dev, Error **errp) { PL110State *s = PL110(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + if (!s->fbmem) { + error_setg(errp, "'framebuffer-memory' property was not set"); + return; + } + memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); @@ -577,6 +588,7 @@ static void pl110_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->vmsd = &vmstate_pl110; dc->realize = pl110_realize; + device_class_set_props(dc, pl110_properties); } static const TypeInfo pl110_info = { diff --git a/hw/display/pxa2xx_lcd.c b/hw/display/pxa2xx_lcd.c index 7859c5d1cd..a9d0d981a0 100644 --- a/hw/display/pxa2xx_lcd.c +++ b/hw/display/pxa2xx_lcd.c @@ -86,106 +86,106 @@ typedef struct QEMU_PACKED { uint32_t ldcmd; } PXAFrameDescriptor; -#define LCCR0 0x000 /* LCD Controller Control register 0 */ -#define LCCR1 0x004 /* LCD Controller Control register 1 */ -#define LCCR2 0x008 /* LCD Controller Control register 2 */ -#define LCCR3 0x00c /* LCD Controller Control register 3 */ -#define LCCR4 0x010 /* LCD Controller Control register 4 */ -#define LCCR5 0x014 /* LCD Controller Control register 5 */ +#define LCCR0 0x000 /* LCD Controller Control register 0 */ +#define LCCR1 0x004 /* LCD Controller Control register 1 */ +#define LCCR2 0x008 /* LCD Controller Control register 2 */ +#define LCCR3 0x00c /* LCD Controller Control register 3 */ +#define LCCR4 0x010 /* LCD Controller Control register 4 */ +#define LCCR5 0x014 /* LCD Controller Control register 5 */ -#define FBR0 0x020 /* DMA Channel 0 Frame Branch register */ -#define FBR1 0x024 /* DMA Channel 1 Frame Branch register */ -#define FBR2 0x028 /* DMA Channel 2 Frame Branch register */ -#define FBR3 0x02c /* DMA Channel 3 Frame Branch register */ -#define FBR4 0x030 /* DMA Channel 4 Frame Branch register */ -#define FBR5 0x110 /* DMA Channel 5 Frame Branch register */ -#define FBR6 0x114 /* DMA Channel 6 Frame Branch register */ +#define FBR0 0x020 /* DMA Channel 0 Frame Branch register */ +#define FBR1 0x024 /* DMA Channel 1 Frame Branch register */ +#define FBR2 0x028 /* DMA Channel 2 Frame Branch register */ +#define FBR3 0x02c /* DMA Channel 3 Frame Branch register */ +#define FBR4 0x030 /* DMA Channel 4 Frame Branch register */ +#define FBR5 0x110 /* DMA Channel 5 Frame Branch register */ +#define FBR6 0x114 /* DMA Channel 6 Frame Branch register */ -#define LCSR1 0x034 /* LCD Controller Status register 1 */ -#define LCSR0 0x038 /* LCD Controller Status register 0 */ -#define LIIDR 0x03c /* LCD Controller Interrupt ID register */ +#define LCSR1 0x034 /* LCD Controller Status register 1 */ +#define LCSR0 0x038 /* LCD Controller Status register 0 */ +#define LIIDR 0x03c /* LCD Controller Interrupt ID register */ -#define TRGBR 0x040 /* TMED RGB Seed register */ -#define TCR 0x044 /* TMED Control register */ +#define TRGBR 0x040 /* TMED RGB Seed register */ +#define TCR 0x044 /* TMED Control register */ -#define OVL1C1 0x050 /* Overlay 1 Control register 1 */ -#define OVL1C2 0x060 /* Overlay 1 Control register 2 */ -#define OVL2C1 0x070 /* Overlay 2 Control register 1 */ -#define OVL2C2 0x080 /* Overlay 2 Control register 2 */ -#define CCR 0x090 /* Cursor Control register */ +#define OVL1C1 0x050 /* Overlay 1 Control register 1 */ +#define OVL1C2 0x060 /* Overlay 1 Control register 2 */ +#define OVL2C1 0x070 /* Overlay 2 Control register 1 */ +#define OVL2C2 0x080 /* Overlay 2 Control register 2 */ +#define CCR 0x090 /* Cursor Control register */ -#define CMDCR 0x100 /* Command Control register */ -#define PRSR 0x104 /* Panel Read Status register */ +#define CMDCR 0x100 /* Command Control register */ +#define PRSR 0x104 /* Panel Read Status register */ -#define PXA_LCDDMA_CHANS 7 -#define DMA_FDADR 0x00 /* Frame Descriptor Address register */ -#define DMA_FSADR 0x04 /* Frame Source Address register */ -#define DMA_FIDR 0x08 /* Frame ID register */ -#define DMA_LDCMD 0x0c /* Command register */ +#define PXA_LCDDMA_CHANS 7 +#define DMA_FDADR 0x00 /* Frame Descriptor Address register */ +#define DMA_FSADR 0x04 /* Frame Source Address register */ +#define DMA_FIDR 0x08 /* Frame ID register */ +#define DMA_LDCMD 0x0c /* Command register */ /* LCD Buffer Strength Control register */ -#define BSCNTR 0x04000054 +#define BSCNTR 0x04000054 /* Bitfield masks */ -#define LCCR0_ENB (1 << 0) -#define LCCR0_CMS (1 << 1) -#define LCCR0_SDS (1 << 2) -#define LCCR0_LDM (1 << 3) -#define LCCR0_SOFM0 (1 << 4) -#define LCCR0_IUM (1 << 5) -#define LCCR0_EOFM0 (1 << 6) -#define LCCR0_PAS (1 << 7) -#define LCCR0_DPD (1 << 9) -#define LCCR0_DIS (1 << 10) -#define LCCR0_QDM (1 << 11) -#define LCCR0_PDD (0xff << 12) -#define LCCR0_BSM0 (1 << 20) -#define LCCR0_OUM (1 << 21) -#define LCCR0_LCDT (1 << 22) -#define LCCR0_RDSTM (1 << 23) -#define LCCR0_CMDIM (1 << 24) -#define LCCR0_OUC (1 << 25) -#define LCCR0_LDDALT (1 << 26) -#define LCCR1_PPL(x) ((x) & 0x3ff) -#define LCCR2_LPP(x) ((x) & 0x3ff) -#define LCCR3_API (15 << 16) -#define LCCR3_BPP(x) ((((x) >> 24) & 7) | (((x) >> 26) & 8)) -#define LCCR3_PDFOR(x) (((x) >> 30) & 3) -#define LCCR4_K1(x) (((x) >> 0) & 7) -#define LCCR4_K2(x) (((x) >> 3) & 7) -#define LCCR4_K3(x) (((x) >> 6) & 7) -#define LCCR4_PALFOR(x) (((x) >> 15) & 3) -#define LCCR5_SOFM(ch) (1 << (ch - 1)) -#define LCCR5_EOFM(ch) (1 << (ch + 7)) -#define LCCR5_BSM(ch) (1 << (ch + 15)) -#define LCCR5_IUM(ch) (1 << (ch + 23)) -#define OVLC1_EN (1 << 31) -#define CCR_CEN (1 << 31) -#define FBR_BRA (1 << 0) -#define FBR_BINT (1 << 1) -#define FBR_SRCADDR (0xfffffff << 4) -#define LCSR0_LDD (1 << 0) -#define LCSR0_SOF0 (1 << 1) -#define LCSR0_BER (1 << 2) -#define LCSR0_ABC (1 << 3) -#define LCSR0_IU0 (1 << 4) -#define LCSR0_IU1 (1 << 5) -#define LCSR0_OU (1 << 6) -#define LCSR0_QD (1 << 7) -#define LCSR0_EOF0 (1 << 8) -#define LCSR0_BS0 (1 << 9) -#define LCSR0_SINT (1 << 10) -#define LCSR0_RDST (1 << 11) -#define LCSR0_CMDINT (1 << 12) -#define LCSR0_BERCH(x) (((x) & 7) << 28) -#define LCSR1_SOF(ch) (1 << (ch - 1)) -#define LCSR1_EOF(ch) (1 << (ch + 7)) -#define LCSR1_BS(ch) (1 << (ch + 15)) -#define LCSR1_IU(ch) (1 << (ch + 23)) -#define LDCMD_LENGTH(x) ((x) & 0x001ffffc) -#define LDCMD_EOFINT (1 << 21) -#define LDCMD_SOFINT (1 << 22) -#define LDCMD_PAL (1 << 26) +#define LCCR0_ENB (1 << 0) +#define LCCR0_CMS (1 << 1) +#define LCCR0_SDS (1 << 2) +#define LCCR0_LDM (1 << 3) +#define LCCR0_SOFM0 (1 << 4) +#define LCCR0_IUM (1 << 5) +#define LCCR0_EOFM0 (1 << 6) +#define LCCR0_PAS (1 << 7) +#define LCCR0_DPD (1 << 9) +#define LCCR0_DIS (1 << 10) +#define LCCR0_QDM (1 << 11) +#define LCCR0_PDD (0xff << 12) +#define LCCR0_BSM0 (1 << 20) +#define LCCR0_OUM (1 << 21) +#define LCCR0_LCDT (1 << 22) +#define LCCR0_RDSTM (1 << 23) +#define LCCR0_CMDIM (1 << 24) +#define LCCR0_OUC (1 << 25) +#define LCCR0_LDDALT (1 << 26) +#define LCCR1_PPL(x) ((x) & 0x3ff) +#define LCCR2_LPP(x) ((x) & 0x3ff) +#define LCCR3_API (15 << 16) +#define LCCR3_BPP(x) ((((x) >> 24) & 7) | (((x) >> 26) & 8)) +#define LCCR3_PDFOR(x) (((x) >> 30) & 3) +#define LCCR4_K1(x) (((x) >> 0) & 7) +#define LCCR4_K2(x) (((x) >> 3) & 7) +#define LCCR4_K3(x) (((x) >> 6) & 7) +#define LCCR4_PALFOR(x) (((x) >> 15) & 3) +#define LCCR5_SOFM(ch) (1 << (ch - 1)) +#define LCCR5_EOFM(ch) (1 << (ch + 7)) +#define LCCR5_BSM(ch) (1 << (ch + 15)) +#define LCCR5_IUM(ch) (1 << (ch + 23)) +#define OVLC1_EN (1 << 31) +#define CCR_CEN (1 << 31) +#define FBR_BRA (1 << 0) +#define FBR_BINT (1 << 1) +#define FBR_SRCADDR (0xfffffff << 4) +#define LCSR0_LDD (1 << 0) +#define LCSR0_SOF0 (1 << 1) +#define LCSR0_BER (1 << 2) +#define LCSR0_ABC (1 << 3) +#define LCSR0_IU0 (1 << 4) +#define LCSR0_IU1 (1 << 5) +#define LCSR0_OU (1 << 6) +#define LCSR0_QD (1 << 7) +#define LCSR0_EOF0 (1 << 8) +#define LCSR0_BS0 (1 << 9) +#define LCSR0_SINT (1 << 10) +#define LCSR0_RDST (1 << 11) +#define LCSR0_CMDINT (1 << 12) +#define LCSR0_BERCH(x) (((x) & 7) << 28) +#define LCSR1_SOF(ch) (1 << (ch - 1)) +#define LCSR1_EOF(ch) (1 << (ch + 7)) +#define LCSR1_BS(ch) (1 << (ch + 15)) +#define LCSR1_IU(ch) (1 << (ch + 23)) +#define LDCMD_LENGTH(x) ((x) & 0x001ffffc) +#define LDCMD_EOFINT (1 << 21) +#define LDCMD_SOFINT (1 << 22) +#define LDCMD_PAL (1 << 26) /* Size of a pixel in the QEMU UI output surface, in bytes */ #define DEST_PIXEL_WIDTH 4 @@ -788,7 +788,7 @@ static uint64_t pxa2xx_lcdc_read(void *opaque, hwaddr offset, case TCR: return s->tcr; - case 0x200 ... 0x1000: /* DMA per-channel registers */ + case 0x200 ... 0x1000: /* DMA per-channel registers */ ch = (offset - 0x200) >> 4; if (!(ch >= 0 && ch < PXA_LCDDMA_CHANS)) goto fail; @@ -938,7 +938,7 @@ static void pxa2xx_lcdc_write(void *opaque, hwaddr offset, s->tcr = value & 0x7fff; break; - case 0x200 ... 0x1000: /* DMA per-channel registers */ + case 0x200 ... 0x1000: /* DMA per-channel registers */ ch = (offset - 0x200) >> 4; if (!(ch >= 0 && ch < PXA_LCDDMA_CHANS)) goto fail; @@ -1371,7 +1371,7 @@ static const VMStateDescription vmstate_dma_channel = { .name = "dma_channel", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(branch, struct DMAChannel), VMSTATE_UINT8(up, struct DMAChannel), VMSTATE_BUFFER(pbuffer, struct DMAChannel), @@ -1398,7 +1398,7 @@ static const VMStateDescription vmstate_pxa2xx_lcdc = { .version_id = 0, .minimum_version_id = 0, .post_load = pxa2xx_lcdc_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(irqlevel, PXA2xxLCDState), VMSTATE_INT32(transp, PXA2xxLCDState), VMSTATE_UINT32_ARRAY(control, PXA2xxLCDState, 6), diff --git a/hw/display/qxl-logger.c b/hw/display/qxl-logger.c index 68bfa47568..35c38f6252 100644 --- a/hw/display/qxl-logger.c +++ b/hw/display/qxl-logger.c @@ -106,7 +106,7 @@ static int qxl_log_image(PCIQXLDevice *qxl, QXLPHYSICAL addr, int group_id) QXLImage *image; QXLImageDescriptor *desc; - image = qxl_phys2virt(qxl, addr, group_id); + image = qxl_phys2virt(qxl, addr, group_id, sizeof(QXLImage)); if (!image) { return 1; } @@ -214,7 +214,8 @@ int qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id) cmd->u.set.position.y, cmd->u.set.visible ? "yes" : "no", cmd->u.set.shape); - cursor = qxl_phys2virt(qxl, cmd->u.set.shape, group_id); + cursor = qxl_phys2virt(qxl, cmd->u.set.shape, group_id, + sizeof(QXLCursor)); if (!cursor) { return 1; } @@ -236,6 +237,7 @@ int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext) { bool compat = ext->flags & QXL_COMMAND_FLAG_COMPAT; void *data; + size_t datasz; int ret; if (!qxl->cmdlog) { @@ -247,7 +249,20 @@ int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext) qxl_name(qxl_type, ext->cmd.type), compat ? "(compat)" : ""); - data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); + switch (ext->cmd.type) { + case QXL_CMD_DRAW: + datasz = compat ? sizeof(QXLCompatDrawable) : sizeof(QXLDrawable); + break; + case QXL_CMD_SURFACE: + datasz = sizeof(QXLSurfaceCmd); + break; + case QXL_CMD_CURSOR: + datasz = sizeof(QXLCursorCmd); + break; + default: + goto out; + } + data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id, datasz); if (!data) { return 1; } @@ -269,6 +284,7 @@ int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext) qxl_log_cmd_cursor(qxl, data, ext->group_id); break; } +out: fprintf(stderr, "\n"); return 0; } diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index ca217004bf..ec99ec887a 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -107,7 +107,9 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) qxl->guest_primary.resized = 0; qxl->guest_primary.data = qxl_phys2virt(qxl, qxl->guest_primary.surface.mem, - MEMSLOT_GROUP_GUEST); + MEMSLOT_GROUP_GUEST, + qxl->guest_primary.abs_stride + * height); if (!qxl->guest_primary.data) { goto end; } @@ -228,7 +230,8 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, if (offset == size) { return; } - chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id); + chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id, + sizeof(QXLDataChunk) + chunk->data_size); if (!chunk) { return; } @@ -287,7 +290,7 @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, return c; fail: - cursor_put(c); + cursor_unref(c); return NULL; } @@ -295,7 +298,8 @@ fail: /* called from spice server thread context only */ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) { - QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); + QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id, + sizeof(QXLCursorCmd)); QXLCursor *cursor; QEMUCursor *c; @@ -314,7 +318,15 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) } switch (cmd->type) { case QXL_CURSOR_SET: - cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id); + /* First read the QXLCursor to get QXLDataChunk::data_size ... */ + cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id, + sizeof(QXLCursor)); + if (!cursor) { + return 1; + } + /* Then read including the chunked data following QXLCursor. */ + cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id, + sizeof(QXLCursor) + cursor->chunk.data_size); if (!cursor) { return 1; } @@ -324,7 +336,7 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) } qemu_mutex_lock(&qxl->ssd.lock); if (qxl->ssd.cursor) { - cursor_put(qxl->ssd.cursor); + cursor_unref(qxl->ssd.cursor); } qxl->ssd.cursor = c; qxl->ssd.mouse_x = cmd->u.set.position.x; diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 5b10f697f1..7178dec85d 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -260,8 +260,7 @@ static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay) QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG, 0)); } else { -/* >= release 0.12.6, < release 0.14.2 */ -#if SPICE_SERVER_VERSION >= 0x000c06 && SPICE_SERVER_VERSION < 0x000e02 +#if SPICE_SERVER_VERSION < 0x000e02 /* release 0.14.2 */ if (qxl->max_outputs) { spice_qxl_set_max_monitors(&qxl->ssd.qxl, qxl->max_outputs); } @@ -274,7 +273,8 @@ static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay) QXL_IO_MONITORS_CONFIG_ASYNC)); } - cfg = qxl_phys2virt(qxl, qxl->guest_monitors_config, MEMSLOT_GROUP_GUEST); + cfg = qxl_phys2virt(qxl, qxl->guest_monitors_config, MEMSLOT_GROUP_GUEST, + sizeof(QXLMonitorsConfig)); if (cfg != NULL && cfg->count == 1) { qxl->guest_primary.resized = 1; qxl->guest_head0_width = cfg->heads[0].width; @@ -299,7 +299,7 @@ void qxl_spice_reset_cursor(PCIQXLDevice *qxl) qxl->guest_cursor = 0; qemu_mutex_unlock(&qxl->track_lock); if (qxl->ssd.cursor) { - cursor_put(qxl->ssd.cursor); + cursor_unref(qxl->ssd.cursor); } qxl->ssd.cursor = cursor_builtin_hidden(); } @@ -459,7 +459,8 @@ static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext) switch (le32_to_cpu(ext->cmd.type)) { case QXL_CMD_SURFACE: { - QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); + QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id, + sizeof(QXLSurfaceCmd)); if (!cmd) { return 1; @@ -494,7 +495,8 @@ static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext) } case QXL_CMD_CURSOR: { - QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); + QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id, + sizeof(QXLCursorCmd)); if (!cmd) { return 1; @@ -541,22 +543,6 @@ static void interface_set_compression_level(QXLInstance *sin, int level) qxl_rom_set_dirty(qxl); } -#if SPICE_NEEDS_SET_MM_TIME -static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - - if (!qemu_spice_display_is_running(&qxl->ssd)) { - return; - } - - trace_qxl_interface_set_mm_time(qxl->id, mm_time); - qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time); - qxl->rom->mm_clock = cpu_to_le32(mm_time); - qxl_rom_set_dirty(qxl); -} -#endif - static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) { PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); @@ -1086,12 +1072,10 @@ static int interface_client_monitors_config(QXLInstance *sin, return 1; } -#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */ /* limit number of outputs based on setting limit */ if (qxl->max_outputs && qxl->max_outputs <= max_outputs) { max_outputs = qxl->max_outputs; } -#endif config_changed = qxl_rom_monitors_config_changed(rom, monitors_config, @@ -1145,9 +1129,6 @@ static const QXLInterface qxl_interface = { #endif .set_compression_level = interface_set_compression_level, -#if SPICE_NEEDS_SET_MM_TIME - .set_mm_time = interface_set_mm_time, -#endif .get_init_info = interface_get_init_info, /* the callbacks below are called from spice server thread context */ @@ -1381,6 +1362,7 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, qxl_set_guest_bug(d, "%s: pci_region = %d", __func__, pci_region); return 1; } + assert(guest_end - pci_start <= memory_region_size(mr)); virt_start = (intptr_t)memory_region_get_ram_ptr(mr); memslot.slot_id = slot_id; @@ -1421,11 +1403,13 @@ static void qxl_reset_surfaces(PCIQXLDevice *d) /* can be also called from spice server thread context */ static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, - uint32_t *s, uint64_t *o) + uint32_t *s, uint64_t *o, + size_t size_requested) { uint64_t phys = le64_to_cpu(pqxl); uint32_t slot = (phys >> (64 - 8)) & 0xff; uint64_t offset = phys & 0xffffffffffff; + uint64_t size_available; if (slot >= NUM_MEMSLOTS) { qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot, @@ -1449,6 +1433,23 @@ static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, slot, offset, qxl->guest_slots[slot].size); return false; } + size_available = memory_region_size(qxl->guest_slots[slot].mr); + if (qxl->guest_slots[slot].offset + offset >= size_available) { + qxl_set_guest_bug(qxl, + "slot %d offset %"PRIu64" > region size %"PRIu64"\n", + slot, qxl->guest_slots[slot].offset + offset, + size_available); + return false; + } + size_available -= qxl->guest_slots[slot].offset + offset; + if (size_requested > size_available) { + qxl_set_guest_bug(qxl, + "slot %d offset %"PRIu64" size %zu: " + "overrun by %"PRIu64" bytes\n", + slot, offset, size_requested, + size_requested - size_available); + return false; + } *s = slot; *o = offset; @@ -1456,7 +1457,8 @@ static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, } /* can be also called from spice server thread context */ -void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id) +void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id, + size_t size) { uint64_t offset; uint32_t slot; @@ -1467,7 +1469,7 @@ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id) offset = le64_to_cpu(pqxl) & 0xffffffffffff; return (void *)(intptr_t)offset; case MEMSLOT_GROUP_GUEST: - if (!qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset)) { + if (!qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset, size)) { return NULL; } ptr = memory_region_get_ram_ptr(qxl->guest_slots[slot].mr); @@ -1542,7 +1544,7 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm, } } -/* return 1 if surface destoy was initiated (in QXL_ASYNC case) or +/* return 1 if surface destroy was initiated (in QXL_ASYNC case) or * done (in QXL_SYNC case), 0 otherwise. */ static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async) { @@ -1589,7 +1591,10 @@ static void qxl_set_mode(PCIQXLDevice *d, unsigned int modenr, int loadvm) } d->guest_slots[0].slot = slot; - assert(qxl_add_memslot(d, 0, devmem, QXL_SYNC) == 0); + if (qxl_add_memslot(d, 0, devmem, QXL_SYNC) != 0) { + qxl_set_guest_bug(d, "device isn't initialized yet"); + return; + } d->guest_primary.surface = surface; qxl_create_guest_primary(d, 0, QXL_SYNC); @@ -1933,9 +1938,9 @@ static void qxl_dirty_one_surface(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, uint32_t slot; bool rc; - rc = qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset); - assert(rc == true); size = (uint64_t)height * abs(stride); + rc = qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset, size); + assert(rc == true); trace_qxl_surfaces_dirty(qxl->id, offset, size); qxl_set_dirty(qxl->guest_slots[slot].mr, qxl->guest_slots[slot].offset + offset, @@ -1964,7 +1969,7 @@ static void qxl_dirty_surfaces(PCIQXLDevice *qxl) } cmd = qxl_phys2virt(qxl, qxl->guest_surfaces.cmds[i], - MEMSLOT_GROUP_GUEST); + MEMSLOT_GROUP_GUEST, sizeof(QXLSurfaceCmd)); assert(cmd); assert(cmd->type == QXL_SURFACE_CMD_CREATE); qxl_dirty_one_surface(qxl, cmd->u.surface_create.data, @@ -2199,11 +2204,14 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl); - qxl->update_irq = qemu_bh_new(qxl_update_irq_bh, qxl); + qxl->update_irq = qemu_bh_new_guarded(qxl_update_irq_bh, qxl, + &DEVICE(qxl)->mem_reentrancy_guard); qxl_reset_state(qxl); - qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl); - qxl->ssd.cursor_bh = qemu_bh_new(qemu_spice_cursor_refresh_bh, &qxl->ssd); + qxl->update_area_bh = qemu_bh_new_guarded(qxl_render_update_area_bh, qxl, + &DEVICE(qxl)->mem_reentrancy_guard); + qxl->ssd.cursor_bh = qemu_bh_new_guarded(qemu_spice_cursor_refresh_bh, &qxl->ssd, + &DEVICE(qxl)->mem_reentrancy_guard); } static void qxl_realize_primary(PCIDevice *dev, Error **errp) @@ -2380,7 +2388,7 @@ static const VMStateDescription qxl_memslot = { .name = "qxl-memslot", .version_id = QXL_SAVE_VERSION, .minimum_version_id = QXL_SAVE_VERSION, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(slot.mem_start, struct guest_slots), VMSTATE_UINT64(slot.mem_end, struct guest_slots), VMSTATE_UINT32(active, struct guest_slots), @@ -2392,7 +2400,7 @@ static const VMStateDescription qxl_surface = { .name = "qxl-surface", .version_id = QXL_SAVE_VERSION, .minimum_version_id = QXL_SAVE_VERSION, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(width, QXLSurfaceCreate), VMSTATE_UINT32(height, QXLSurfaceCreate), VMSTATE_INT32(stride, QXLSurfaceCreate), @@ -2411,7 +2419,7 @@ static const VMStateDescription qxl_vmstate_monitors_config = { .version_id = 1, .minimum_version_id = 1, .needed = qxl_monitors_config_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(guest_monitors_config, PCIQXLDevice), VMSTATE_END_OF_LIST() }, @@ -2424,7 +2432,7 @@ static const VMStateDescription qxl_vmstate = { .pre_save = qxl_pre_save, .pre_load = qxl_pre_load, .post_load = qxl_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(pci, PCIQXLDevice), VMSTATE_STRUCT(vga, PCIQXLDevice, 0, vmstate_vga_common, VGACommonState), VMSTATE_UINT32(shadow_rom.mode, PCIQXLDevice), @@ -2444,7 +2452,7 @@ static const VMStateDescription qxl_vmstate = { VMSTATE_UINT64(guest_cursor, PCIQXLDevice), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &qxl_vmstate_monitors_config, NULL } @@ -2463,9 +2471,7 @@ static Property qxl_properties[] = { DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1), DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16), DEFINE_PROP_INT32("surfaces", PCIQXLDevice, ssd.num_surfaces, 1024), -#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */ DEFINE_PROP_UINT16("max_outputs", PCIQXLDevice, max_outputs, 0), -#endif DEFINE_PROP_UINT32("xres", PCIQXLDevice, xres, 0), DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0), DEFINE_PROP_BOOL("global-vmstate", PCIQXLDevice, vga.global_vmstate, false), diff --git a/hw/display/qxl.h b/hw/display/qxl.h index e74de9579d..e0a85a5ca4 100644 --- a/hw/display/qxl.h +++ b/hw/display/qxl.h @@ -1,8 +1,7 @@ #ifndef HW_QXL_H #define HW_QXL_H - -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "vga_int.h" #include "qemu/thread.h" @@ -100,9 +99,7 @@ struct PCIQXLDevice { QXLModes *modes; uint32_t rom_size; MemoryRegion rom_bar; -#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */ uint16_t max_outputs; -#endif /* vram pci bar */ uint64_t vram_size; @@ -147,7 +144,28 @@ OBJECT_DECLARE_SIMPLE_TYPE(PCIQXLDevice, PCI_QXL) #define QXL_DEFAULT_REVISION (QXL_REVISION_STABLE_V12 + 1) /* qxl.c */ -void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); +/** + * qxl_phys2virt: Get a pointer within a PCI VRAM memory region. + * + * @qxl: QXL device + * @phys: physical offset of buffer within the VRAM + * @group_id: memory slot group + * @size: size of the buffer + * + * Returns a host pointer to a buffer placed at offset @phys within the + * active slot @group_id of the PCI VGA RAM memory region associated with + * the @qxl device. If the slot is inactive, or the offset + size are out + * of the memory region, returns NULL. + * + * Use with care; by the time this function returns, the returned pointer is + * not protected by RCU anymore. If the caller is not within an RCU critical + * section and does not hold the BQL, it must have other means of + * protecting the pointer, such as a reference to the region that includes + * the incoming ram_addr_t. + * + */ +void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id, + size_t size); void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...) G_GNUC_PRINTF(2, 3); diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c index 8c0094397f..20eab34ff4 100644 --- a/hw/display/ramfb-standalone.c +++ b/hw/display/ramfb-standalone.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/module.h" #include "hw/loader.h" @@ -15,6 +16,7 @@ struct RAMFBStandaloneState { SysBusDevice parent_obj; QemuConsole *con; RAMFBState *state; + bool migrate; }; static void display_update_wrapper(void *dev) @@ -40,14 +42,39 @@ static void ramfb_realizefn(DeviceState *dev, Error **errp) ramfb->state = ramfb_setup(errp); } +static bool migrate_needed(void *opaque) +{ + RAMFBStandaloneState *ramfb = RAMFB(opaque); + + return ramfb->migrate; +} + +static const VMStateDescription ramfb_dev_vmstate = { + .name = "ramfb-dev", + .version_id = 1, + .minimum_version_id = 1, + .needed = migrate_needed, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT_POINTER(state, RAMFBStandaloneState, ramfb_vmstate, RAMFBState), + VMSTATE_END_OF_LIST() + } +}; + +static Property ramfb_properties[] = { + DEFINE_PROP_BOOL("x-migrate", RAMFBStandaloneState, migrate, true), + DEFINE_PROP_END_OF_LIST(), +}; + static void ramfb_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + dc->vmsd = &ramfb_dev_vmstate; dc->realize = ramfb_realizefn; dc->desc = "ram framebuffer standalone device"; dc->user_creatable = true; + device_class_set_props(dc, ramfb_properties); } static const TypeInfo ramfb_info = { diff --git a/hw/display/ramfb.c b/hw/display/ramfb.c index 79b9754a58..6086baf7a9 100644 --- a/hw/display/ramfb.c +++ b/hw/display/ramfb.c @@ -28,6 +28,8 @@ struct QEMU_PACKED RAMFBCfg { uint32_t stride; }; +typedef struct RAMFBCfg RAMFBCfg; + struct RAMFBState { DisplaySurface *ds; uint32_t width, height; @@ -97,6 +99,7 @@ static void ramfb_fw_cfg_write(void *dev, off_t offset, size_t len) s->width = width; s->height = height; + qemu_free_displaysurface(s->ds); s->ds = surface; } @@ -115,6 +118,23 @@ void ramfb_display_update(QemuConsole *con, RAMFBState *s) dpy_gfx_update_full(con); } +static int ramfb_post_load(void *opaque, int version_id) +{ + ramfb_fw_cfg_write(opaque, 0, 0); + return 0; +} + +const VMStateDescription ramfb_vmstate = { + .name = "ramfb", + .version_id = 1, + .minimum_version_id = 1, + .post_load = ramfb_post_load, + .fields = (const VMStateField[]) { + VMSTATE_BUFFER_UNSAFE(cfg, RAMFBState, 0, sizeof(RAMFBCfg)), + VMSTATE_END_OF_LIST() + } +}; + RAMFBState *ramfb_setup(Error **errp) { FWCfgState *fw_cfg = fw_cfg_find(); diff --git a/hw/display/sii9022.c b/hw/display/sii9022.c index b591a58789..60c3f78549 100644 --- a/hw/display/sii9022.c +++ b/hw/display/sii9022.c @@ -51,7 +51,7 @@ static const VMStateDescription vmstate_sii9022 = { .name = "sii9022", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_I2C_SLAVE(parent_obj, sii9022_state), VMSTATE_UINT8(ptr, sii9022_state), VMSTATE_BOOL(addr_byte, sii9022_state), @@ -76,6 +76,8 @@ static int sii9022_event(I2CSlave *i2c, enum i2c_event event) break; case I2C_NACK: break; + default: + return -1; } return 0; diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 663c37e7f2..26dc8170d8 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -28,11 +28,12 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" +#include "hw/usb/hcd-ohci.h" #include "hw/char/serial.h" #include "ui/console.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "hw/i2c/i2c.h" #include "hw/display/i2c-ddc.h" @@ -50,10 +51,10 @@ /* System Configuration area */ /* System config base */ -#define SM501_SYS_CONFIG (0x000000) +#define SM501_SYS_CONFIG 0x000000 /* config 1 */ -#define SM501_SYSTEM_CONTROL (0x000000) +#define SM501_SYSTEM_CONTROL 0x000000 #define SM501_SYSCTRL_PANEL_TRISTATE (1 << 0) #define SM501_SYSCTRL_MEM_TRISTATE (1 << 1) @@ -72,13 +73,13 @@ /* miscellaneous control */ -#define SM501_MISC_CONTROL (0x000004) +#define SM501_MISC_CONTROL 0x000004 -#define SM501_MISC_BUS_SH (0x0) -#define SM501_MISC_BUS_PCI (0x1) -#define SM501_MISC_BUS_XSCALE (0x2) -#define SM501_MISC_BUS_NEC (0x6) -#define SM501_MISC_BUS_MASK (0x7) +#define SM501_MISC_BUS_SH 0x0 +#define SM501_MISC_BUS_PCI 0x1 +#define SM501_MISC_BUS_XSCALE 0x2 +#define SM501_MISC_BUS_NEC 0x6 +#define SM501_MISC_BUS_MASK 0x7 #define SM501_MISC_VR_62MB (1 << 3) #define SM501_MISC_CDR_RESET (1 << 7) @@ -103,22 +104,22 @@ -#define SM501_GPIO31_0_CONTROL (0x000008) -#define SM501_GPIO63_32_CONTROL (0x00000C) -#define SM501_DRAM_CONTROL (0x000010) +#define SM501_GPIO31_0_CONTROL 0x000008 +#define SM501_GPIO63_32_CONTROL 0x00000C +#define SM501_DRAM_CONTROL 0x000010 /* command list */ -#define SM501_ARBTRTN_CONTROL (0x000014) +#define SM501_ARBTRTN_CONTROL 0x000014 /* command list */ -#define SM501_COMMAND_LIST_STATUS (0x000024) +#define SM501_COMMAND_LIST_STATUS 0x000024 /* interrupt debug */ -#define SM501_RAW_IRQ_STATUS (0x000028) -#define SM501_RAW_IRQ_CLEAR (0x000028) -#define SM501_IRQ_STATUS (0x00002C) -#define SM501_IRQ_MASK (0x000030) -#define SM501_DEBUG_CONTROL (0x000034) +#define SM501_RAW_IRQ_STATUS 0x000028 +#define SM501_RAW_IRQ_CLEAR 0x000028 +#define SM501_IRQ_STATUS 0x00002C +#define SM501_IRQ_MASK 0x000030 +#define SM501_DEBUG_CONTROL 0x000034 /* power management */ #define SM501_POWERMODE_P2X_SRC (1 << 29) @@ -126,74 +127,74 @@ #define SM501_POWERMODE_M_SRC (1 << 12) #define SM501_POWERMODE_M1_SRC (1 << 4) -#define SM501_CURRENT_GATE (0x000038) -#define SM501_CURRENT_CLOCK (0x00003C) -#define SM501_POWER_MODE_0_GATE (0x000040) -#define SM501_POWER_MODE_0_CLOCK (0x000044) -#define SM501_POWER_MODE_1_GATE (0x000048) -#define SM501_POWER_MODE_1_CLOCK (0x00004C) -#define SM501_SLEEP_MODE_GATE (0x000050) -#define SM501_POWER_MODE_CONTROL (0x000054) +#define SM501_CURRENT_GATE 0x000038 +#define SM501_CURRENT_CLOCK 0x00003C +#define SM501_POWER_MODE_0_GATE 0x000040 +#define SM501_POWER_MODE_0_CLOCK 0x000044 +#define SM501_POWER_MODE_1_GATE 0x000048 +#define SM501_POWER_MODE_1_CLOCK 0x00004C +#define SM501_SLEEP_MODE_GATE 0x000050 +#define SM501_POWER_MODE_CONTROL 0x000054 /* power gates for units within the 501 */ -#define SM501_GATE_HOST (0) -#define SM501_GATE_MEMORY (1) -#define SM501_GATE_DISPLAY (2) -#define SM501_GATE_2D_ENGINE (3) -#define SM501_GATE_CSC (4) -#define SM501_GATE_ZVPORT (5) -#define SM501_GATE_GPIO (6) -#define SM501_GATE_UART0 (7) -#define SM501_GATE_UART1 (8) -#define SM501_GATE_SSP (10) -#define SM501_GATE_USB_HOST (11) -#define SM501_GATE_USB_GADGET (12) -#define SM501_GATE_UCONTROLLER (17) -#define SM501_GATE_AC97 (18) +#define SM501_GATE_HOST 0 +#define SM501_GATE_MEMORY 1 +#define SM501_GATE_DISPLAY 2 +#define SM501_GATE_2D_ENGINE 3 +#define SM501_GATE_CSC 4 +#define SM501_GATE_ZVPORT 5 +#define SM501_GATE_GPIO 6 +#define SM501_GATE_UART0 7 +#define SM501_GATE_UART1 8 +#define SM501_GATE_SSP 10 +#define SM501_GATE_USB_HOST 11 +#define SM501_GATE_USB_GADGET 12 +#define SM501_GATE_UCONTROLLER 17 +#define SM501_GATE_AC97 18 /* panel clock */ -#define SM501_CLOCK_P2XCLK (24) +#define SM501_CLOCK_P2XCLK 24 /* crt clock */ -#define SM501_CLOCK_V2XCLK (16) +#define SM501_CLOCK_V2XCLK 16 /* main clock */ -#define SM501_CLOCK_MCLK (8) +#define SM501_CLOCK_MCLK 8 /* SDRAM controller clock */ -#define SM501_CLOCK_M1XCLK (0) +#define SM501_CLOCK_M1XCLK 0 /* config 2 */ -#define SM501_PCI_MASTER_BASE (0x000058) -#define SM501_ENDIAN_CONTROL (0x00005C) -#define SM501_DEVICEID (0x000060) +#define SM501_PCI_MASTER_BASE 0x000058 +#define SM501_ENDIAN_CONTROL 0x00005C +#define SM501_DEVICEID 0x000060 /* 0x050100A0 */ -#define SM501_DEVICEID_SM501 (0x05010000) -#define SM501_DEVICEID_IDMASK (0xffff0000) -#define SM501_DEVICEID_REVMASK (0x000000ff) +#define SM501_DEVICEID_SM501 0x05010000 +#define SM501_DEVICEID_IDMASK 0xffff0000 +#define SM501_DEVICEID_REVMASK 0x000000ff -#define SM501_PLLCLOCK_COUNT (0x000064) -#define SM501_MISC_TIMING (0x000068) -#define SM501_CURRENT_SDRAM_CLOCK (0x00006C) +#define SM501_PLLCLOCK_COUNT 0x000064 +#define SM501_MISC_TIMING 0x000068 +#define SM501_CURRENT_SDRAM_CLOCK 0x00006C -#define SM501_PROGRAMMABLE_PLL_CONTROL (0x000074) +#define SM501_PROGRAMMABLE_PLL_CONTROL 0x000074 /* GPIO base */ -#define SM501_GPIO (0x010000) -#define SM501_GPIO_DATA_LOW (0x00) -#define SM501_GPIO_DATA_HIGH (0x04) -#define SM501_GPIO_DDR_LOW (0x08) -#define SM501_GPIO_DDR_HIGH (0x0C) -#define SM501_GPIO_IRQ_SETUP (0x10) -#define SM501_GPIO_IRQ_STATUS (0x14) -#define SM501_GPIO_IRQ_RESET (0x14) +#define SM501_GPIO 0x010000 +#define SM501_GPIO_DATA_LOW 0x00 +#define SM501_GPIO_DATA_HIGH 0x04 +#define SM501_GPIO_DDR_LOW 0x08 +#define SM501_GPIO_DDR_HIGH 0x0C +#define SM501_GPIO_IRQ_SETUP 0x10 +#define SM501_GPIO_IRQ_STATUS 0x14 +#define SM501_GPIO_IRQ_RESET 0x14 /* I2C controller base */ -#define SM501_I2C (0x010040) -#define SM501_I2C_BYTE_COUNT (0x00) -#define SM501_I2C_CONTROL (0x01) -#define SM501_I2C_STATUS (0x02) -#define SM501_I2C_RESET (0x02) -#define SM501_I2C_SLAVE_ADDRESS (0x03) -#define SM501_I2C_DATA (0x04) +#define SM501_I2C 0x010040 +#define SM501_I2C_BYTE_COUNT 0x00 +#define SM501_I2C_CONTROL 0x01 +#define SM501_I2C_STATUS 0x02 +#define SM501_I2C_RESET 0x02 +#define SM501_I2C_SLAVE_ADDRESS 0x03 +#define SM501_I2C_DATA 0x04 #define SM501_I2C_CONTROL_START (1 << 2) #define SM501_I2C_CONTROL_ENABLE (1 << 0) @@ -204,25 +205,25 @@ #define SM501_I2C_RESET_ERROR (1 << 2) /* SSP base */ -#define SM501_SSP (0x020000) +#define SM501_SSP 0x020000 /* Uart 0 base */ -#define SM501_UART0 (0x030000) +#define SM501_UART0 0x030000 /* Uart 1 base */ -#define SM501_UART1 (0x030020) +#define SM501_UART1 0x030020 /* USB host port base */ -#define SM501_USB_HOST (0x040000) +#define SM501_USB_HOST 0x040000 /* USB slave/gadget base */ -#define SM501_USB_GADGET (0x060000) +#define SM501_USB_GADGET 0x060000 /* USB slave/gadget data port base */ -#define SM501_USB_GADGET_DATA (0x070000) +#define SM501_USB_GADGET_DATA 0x070000 /* Display controller/video engine base */ -#define SM501_DC (0x080000) +#define SM501_DC 0x080000 /* common defines for the SM501 address registers */ #define SM501_ADDR_FLIP (1 << 31) @@ -237,12 +238,12 @@ #define SM501_FIFO_11 (0x3 << 16) /* common registers for panel and the crt */ -#define SM501_OFF_DC_H_TOT (0x000) -#define SM501_OFF_DC_V_TOT (0x008) -#define SM501_OFF_DC_H_SYNC (0x004) -#define SM501_OFF_DC_V_SYNC (0x00C) +#define SM501_OFF_DC_H_TOT 0x000 +#define SM501_OFF_DC_V_TOT 0x008 +#define SM501_OFF_DC_H_SYNC 0x004 +#define SM501_OFF_DC_V_SYNC 0x00C -#define SM501_DC_PANEL_CONTROL (0x000) +#define SM501_DC_PANEL_CONTROL 0x000 #define SM501_DC_PANEL_CONTROL_FPEN (1 << 27) #define SM501_DC_PANEL_CONTROL_BIAS (1 << 26) @@ -277,65 +278,65 @@ #define SM501_DC_PANEL_CONTROL_32BPP (2 << 0) -#define SM501_DC_PANEL_PANNING_CONTROL (0x004) -#define SM501_DC_PANEL_COLOR_KEY (0x008) -#define SM501_DC_PANEL_FB_ADDR (0x00C) -#define SM501_DC_PANEL_FB_OFFSET (0x010) -#define SM501_DC_PANEL_FB_WIDTH (0x014) -#define SM501_DC_PANEL_FB_HEIGHT (0x018) -#define SM501_DC_PANEL_TL_LOC (0x01C) -#define SM501_DC_PANEL_BR_LOC (0x020) -#define SM501_DC_PANEL_H_TOT (0x024) -#define SM501_DC_PANEL_H_SYNC (0x028) -#define SM501_DC_PANEL_V_TOT (0x02C) -#define SM501_DC_PANEL_V_SYNC (0x030) -#define SM501_DC_PANEL_CUR_LINE (0x034) +#define SM501_DC_PANEL_PANNING_CONTROL 0x004 +#define SM501_DC_PANEL_COLOR_KEY 0x008 +#define SM501_DC_PANEL_FB_ADDR 0x00C +#define SM501_DC_PANEL_FB_OFFSET 0x010 +#define SM501_DC_PANEL_FB_WIDTH 0x014 +#define SM501_DC_PANEL_FB_HEIGHT 0x018 +#define SM501_DC_PANEL_TL_LOC 0x01C +#define SM501_DC_PANEL_BR_LOC 0x020 +#define SM501_DC_PANEL_H_TOT 0x024 +#define SM501_DC_PANEL_H_SYNC 0x028 +#define SM501_DC_PANEL_V_TOT 0x02C +#define SM501_DC_PANEL_V_SYNC 0x030 +#define SM501_DC_PANEL_CUR_LINE 0x034 -#define SM501_DC_VIDEO_CONTROL (0x040) -#define SM501_DC_VIDEO_FB0_ADDR (0x044) -#define SM501_DC_VIDEO_FB_WIDTH (0x048) -#define SM501_DC_VIDEO_FB0_LAST_ADDR (0x04C) -#define SM501_DC_VIDEO_TL_LOC (0x050) -#define SM501_DC_VIDEO_BR_LOC (0x054) -#define SM501_DC_VIDEO_SCALE (0x058) -#define SM501_DC_VIDEO_INIT_SCALE (0x05C) -#define SM501_DC_VIDEO_YUV_CONSTANTS (0x060) -#define SM501_DC_VIDEO_FB1_ADDR (0x064) -#define SM501_DC_VIDEO_FB1_LAST_ADDR (0x068) +#define SM501_DC_VIDEO_CONTROL 0x040 +#define SM501_DC_VIDEO_FB0_ADDR 0x044 +#define SM501_DC_VIDEO_FB_WIDTH 0x048 +#define SM501_DC_VIDEO_FB0_LAST_ADDR 0x04C +#define SM501_DC_VIDEO_TL_LOC 0x050 +#define SM501_DC_VIDEO_BR_LOC 0x054 +#define SM501_DC_VIDEO_SCALE 0x058 +#define SM501_DC_VIDEO_INIT_SCALE 0x05C +#define SM501_DC_VIDEO_YUV_CONSTANTS 0x060 +#define SM501_DC_VIDEO_FB1_ADDR 0x064 +#define SM501_DC_VIDEO_FB1_LAST_ADDR 0x068 -#define SM501_DC_VIDEO_ALPHA_CONTROL (0x080) -#define SM501_DC_VIDEO_ALPHA_FB_ADDR (0x084) -#define SM501_DC_VIDEO_ALPHA_FB_OFFSET (0x088) -#define SM501_DC_VIDEO_ALPHA_FB_LAST_ADDR (0x08C) -#define SM501_DC_VIDEO_ALPHA_TL_LOC (0x090) -#define SM501_DC_VIDEO_ALPHA_BR_LOC (0x094) -#define SM501_DC_VIDEO_ALPHA_SCALE (0x098) -#define SM501_DC_VIDEO_ALPHA_INIT_SCALE (0x09C) -#define SM501_DC_VIDEO_ALPHA_CHROMA_KEY (0x0A0) -#define SM501_DC_VIDEO_ALPHA_COLOR_LOOKUP (0x0A4) +#define SM501_DC_VIDEO_ALPHA_CONTROL 0x080 +#define SM501_DC_VIDEO_ALPHA_FB_ADDR 0x084 +#define SM501_DC_VIDEO_ALPHA_FB_OFFSET 0x088 +#define SM501_DC_VIDEO_ALPHA_FB_LAST_ADDR 0x08C +#define SM501_DC_VIDEO_ALPHA_TL_LOC 0x090 +#define SM501_DC_VIDEO_ALPHA_BR_LOC 0x094 +#define SM501_DC_VIDEO_ALPHA_SCALE 0x098 +#define SM501_DC_VIDEO_ALPHA_INIT_SCALE 0x09C +#define SM501_DC_VIDEO_ALPHA_CHROMA_KEY 0x0A0 +#define SM501_DC_VIDEO_ALPHA_COLOR_LOOKUP 0x0A4 -#define SM501_DC_PANEL_HWC_BASE (0x0F0) -#define SM501_DC_PANEL_HWC_ADDR (0x0F0) -#define SM501_DC_PANEL_HWC_LOC (0x0F4) -#define SM501_DC_PANEL_HWC_COLOR_1_2 (0x0F8) -#define SM501_DC_PANEL_HWC_COLOR_3 (0x0FC) +#define SM501_DC_PANEL_HWC_BASE 0x0F0 +#define SM501_DC_PANEL_HWC_ADDR 0x0F0 +#define SM501_DC_PANEL_HWC_LOC 0x0F4 +#define SM501_DC_PANEL_HWC_COLOR_1_2 0x0F8 +#define SM501_DC_PANEL_HWC_COLOR_3 0x0FC #define SM501_HWC_EN (1 << 31) -#define SM501_OFF_HWC_ADDR (0x00) -#define SM501_OFF_HWC_LOC (0x04) -#define SM501_OFF_HWC_COLOR_1_2 (0x08) -#define SM501_OFF_HWC_COLOR_3 (0x0C) +#define SM501_OFF_HWC_ADDR 0x00 +#define SM501_OFF_HWC_LOC 0x04 +#define SM501_OFF_HWC_COLOR_1_2 0x08 +#define SM501_OFF_HWC_COLOR_3 0x0C -#define SM501_DC_ALPHA_CONTROL (0x100) -#define SM501_DC_ALPHA_FB_ADDR (0x104) -#define SM501_DC_ALPHA_FB_OFFSET (0x108) -#define SM501_DC_ALPHA_TL_LOC (0x10C) -#define SM501_DC_ALPHA_BR_LOC (0x110) -#define SM501_DC_ALPHA_CHROMA_KEY (0x114) -#define SM501_DC_ALPHA_COLOR_LOOKUP (0x118) +#define SM501_DC_ALPHA_CONTROL 0x100 +#define SM501_DC_ALPHA_FB_ADDR 0x104 +#define SM501_DC_ALPHA_FB_OFFSET 0x108 +#define SM501_DC_ALPHA_TL_LOC 0x10C +#define SM501_DC_ALPHA_BR_LOC 0x110 +#define SM501_DC_ALPHA_CHROMA_KEY 0x114 +#define SM501_DC_ALPHA_COLOR_LOOKUP 0x118 -#define SM501_DC_CRT_CONTROL (0x200) +#define SM501_DC_CRT_CONTROL 0x200 #define SM501_DC_CRT_CONTROL_TVP (1 << 15) #define SM501_DC_CRT_CONTROL_CP (1 << 14) @@ -353,89 +354,95 @@ #define SM501_DC_CRT_CONTROL_16BPP (1 << 0) #define SM501_DC_CRT_CONTROL_32BPP (2 << 0) -#define SM501_DC_CRT_FB_ADDR (0x204) -#define SM501_DC_CRT_FB_OFFSET (0x208) -#define SM501_DC_CRT_H_TOT (0x20C) -#define SM501_DC_CRT_H_SYNC (0x210) -#define SM501_DC_CRT_V_TOT (0x214) -#define SM501_DC_CRT_V_SYNC (0x218) -#define SM501_DC_CRT_SIGNATURE_ANALYZER (0x21C) -#define SM501_DC_CRT_CUR_LINE (0x220) -#define SM501_DC_CRT_MONITOR_DETECT (0x224) +#define SM501_DC_CRT_FB_ADDR 0x204 +#define SM501_DC_CRT_FB_OFFSET 0x208 +#define SM501_DC_CRT_H_TOT 0x20C +#define SM501_DC_CRT_H_SYNC 0x210 +#define SM501_DC_CRT_V_TOT 0x214 +#define SM501_DC_CRT_V_SYNC 0x218 +#define SM501_DC_CRT_SIGNATURE_ANALYZER 0x21C +#define SM501_DC_CRT_CUR_LINE 0x220 +#define SM501_DC_CRT_MONITOR_DETECT 0x224 -#define SM501_DC_CRT_HWC_BASE (0x230) -#define SM501_DC_CRT_HWC_ADDR (0x230) -#define SM501_DC_CRT_HWC_LOC (0x234) -#define SM501_DC_CRT_HWC_COLOR_1_2 (0x238) -#define SM501_DC_CRT_HWC_COLOR_3 (0x23C) +#define SM501_DC_CRT_HWC_BASE 0x230 +#define SM501_DC_CRT_HWC_ADDR 0x230 +#define SM501_DC_CRT_HWC_LOC 0x234 +#define SM501_DC_CRT_HWC_COLOR_1_2 0x238 +#define SM501_DC_CRT_HWC_COLOR_3 0x23C -#define SM501_DC_PANEL_PALETTE (0x400) +#define SM501_DC_PANEL_PALETTE 0x400 -#define SM501_DC_VIDEO_PALETTE (0x800) +#define SM501_DC_VIDEO_PALETTE 0x800 -#define SM501_DC_CRT_PALETTE (0xC00) +#define SM501_DC_CRT_PALETTE 0xC00 /* Zoom Video port base */ -#define SM501_ZVPORT (0x090000) +#define SM501_ZVPORT 0x090000 /* AC97/I2S base */ -#define SM501_AC97 (0x0A0000) +#define SM501_AC97 0x0A0000 /* 8051 micro controller base */ -#define SM501_UCONTROLLER (0x0B0000) +#define SM501_UCONTROLLER 0x0B0000 /* 8051 micro controller SRAM base */ -#define SM501_UCONTROLLER_SRAM (0x0C0000) +#define SM501_UCONTROLLER_SRAM 0x0C0000 /* DMA base */ -#define SM501_DMA (0x0D0000) +#define SM501_DMA 0x0D0000 /* 2d engine base */ -#define SM501_2D_ENGINE (0x100000) -#define SM501_2D_SOURCE (0x00) -#define SM501_2D_DESTINATION (0x04) -#define SM501_2D_DIMENSION (0x08) -#define SM501_2D_CONTROL (0x0C) -#define SM501_2D_PITCH (0x10) -#define SM501_2D_FOREGROUND (0x14) -#define SM501_2D_BACKGROUND (0x18) -#define SM501_2D_STRETCH (0x1C) -#define SM501_2D_COLOR_COMPARE (0x20) -#define SM501_2D_COLOR_COMPARE_MASK (0x24) -#define SM501_2D_MASK (0x28) -#define SM501_2D_CLIP_TL (0x2C) -#define SM501_2D_CLIP_BR (0x30) -#define SM501_2D_MONO_PATTERN_LOW (0x34) -#define SM501_2D_MONO_PATTERN_HIGH (0x38) -#define SM501_2D_WINDOW_WIDTH (0x3C) -#define SM501_2D_SOURCE_BASE (0x40) -#define SM501_2D_DESTINATION_BASE (0x44) -#define SM501_2D_ALPHA (0x48) -#define SM501_2D_WRAP (0x4C) -#define SM501_2D_STATUS (0x50) +#define SM501_2D_ENGINE 0x100000 +#define SM501_2D_SOURCE 0x00 +#define SM501_2D_DESTINATION 0x04 +#define SM501_2D_DIMENSION 0x08 +#define SM501_2D_CONTROL 0x0C +#define SM501_2D_PITCH 0x10 +#define SM501_2D_FOREGROUND 0x14 +#define SM501_2D_BACKGROUND 0x18 +#define SM501_2D_STRETCH 0x1C +#define SM501_2D_COLOR_COMPARE 0x20 +#define SM501_2D_COLOR_COMPARE_MASK 0x24 +#define SM501_2D_MASK 0x28 +#define SM501_2D_CLIP_TL 0x2C +#define SM501_2D_CLIP_BR 0x30 +#define SM501_2D_MONO_PATTERN_LOW 0x34 +#define SM501_2D_MONO_PATTERN_HIGH 0x38 +#define SM501_2D_WINDOW_WIDTH 0x3C +#define SM501_2D_SOURCE_BASE 0x40 +#define SM501_2D_DESTINATION_BASE 0x44 +#define SM501_2D_ALPHA 0x48 +#define SM501_2D_WRAP 0x4C +#define SM501_2D_STATUS 0x50 -#define SM501_CSC_Y_SOURCE_BASE (0xC8) -#define SM501_CSC_CONSTANTS (0xCC) -#define SM501_CSC_Y_SOURCE_X (0xD0) -#define SM501_CSC_Y_SOURCE_Y (0xD4) -#define SM501_CSC_U_SOURCE_BASE (0xD8) -#define SM501_CSC_V_SOURCE_BASE (0xDC) -#define SM501_CSC_SOURCE_DIMENSION (0xE0) -#define SM501_CSC_SOURCE_PITCH (0xE4) -#define SM501_CSC_DESTINATION (0xE8) -#define SM501_CSC_DESTINATION_DIMENSION (0xEC) -#define SM501_CSC_DESTINATION_PITCH (0xF0) -#define SM501_CSC_SCALE_FACTOR (0xF4) -#define SM501_CSC_DESTINATION_BASE (0xF8) -#define SM501_CSC_CONTROL (0xFC) +#define SM501_CSC_Y_SOURCE_BASE 0xC8 +#define SM501_CSC_CONSTANTS 0xCC +#define SM501_CSC_Y_SOURCE_X 0xD0 +#define SM501_CSC_Y_SOURCE_Y 0xD4 +#define SM501_CSC_U_SOURCE_BASE 0xD8 +#define SM501_CSC_V_SOURCE_BASE 0xDC +#define SM501_CSC_SOURCE_DIMENSION 0xE0 +#define SM501_CSC_SOURCE_PITCH 0xE4 +#define SM501_CSC_DESTINATION 0xE8 +#define SM501_CSC_DESTINATION_DIMENSION 0xEC +#define SM501_CSC_DESTINATION_PITCH 0xF0 +#define SM501_CSC_SCALE_FACTOR 0xF4 +#define SM501_CSC_DESTINATION_BASE 0xF8 +#define SM501_CSC_CONTROL 0xFC /* 2d engine data port base */ -#define SM501_2D_ENGINE_DATA (0x110000) +#define SM501_2D_ENGINE_DATA 0x110000 /* end of register definitions */ -#define SM501_HWC_WIDTH (64) -#define SM501_HWC_HEIGHT (64) +#define SM501_HWC_WIDTH 64 +#define SM501_HWC_HEIGHT 64 + +#ifdef CONFIG_PIXMAN +#define DEFAULT_X_PIXMAN 7 +#else +#define DEFAULT_X_PIXMAN 0 +#endif /* SM501 local memory size taken from "linux/drivers/mfd/sm501.c" */ static const uint32_t sm501_mem_local_size[] = { @@ -464,6 +471,7 @@ typedef struct SM501State { uint32_t last_width; uint32_t last_height; bool do_full_update; /* perform a full update next time */ + uint8_t use_pixman; I2CBus *i2c_bus; /* mmio registers */ @@ -691,7 +699,7 @@ static void sm501_2d_operation(SM501State *s) unsigned int dst_pitch = (s->twoD_pitch >> 16) & 0x1FFF; int crt = (s->dc_crt_control & SM501_DC_CRT_CONTROL_SEL) ? 1 : 0; int fb_len = get_width(s, crt) * get_height(s, crt) * get_bpp(s, crt); - bool overlap = false; + bool overlap = false, fallback = false; if ((s->twoD_stretch >> 16) & 0xF) { qemu_log_mask(LOG_UNIMP, "sm501: only XY addressing is supported.\n"); @@ -728,7 +736,6 @@ static void sm501_2d_operation(SM501State *s) switch (cmd) { case 0: /* BitBlt */ { - static uint32_t tmp_buf[16384]; unsigned int src_x = (s->twoD_source >> 16) & 0x01FFF; unsigned int src_y = s->twoD_source & 0xFFFF; uint32_t src_base = s->twoD_source_base & 0x03FFFFFF; @@ -753,7 +760,7 @@ static void sm501_2d_operation(SM501State *s) } if ((rop_mode && rop == 0x5) || (!rop_mode && rop == 0x55)) { - /* Invert dest, is there a way to do this with pixman? */ + /* DSTINVERT, is there a way to do this with pixman? */ unsigned int x, y, i; uint8_t *d = s->local_mem + dst_base; @@ -763,6 +770,34 @@ static void sm501_2d_operation(SM501State *s) stn_he_p(&d[i], bypp, ~ldn_he_p(&d[i], bypp)); } } + } else if (!rop_mode && rop == 0x99) { + /* DSxn, is there a way to do this with pixman? */ + unsigned int x, y, i, j; + uint8_t *sp = s->local_mem + src_base; + uint8_t *d = s->local_mem + dst_base; + + for (y = 0; y < height; y++) { + i = (dst_x + (dst_y + y) * dst_pitch) * bypp; + j = (src_x + (src_y + y) * src_pitch) * bypp; + for (x = 0; x < width; x++, i += bypp, j += bypp) { + stn_he_p(&d[i], bypp, + ~(ldn_he_p(&sp[j], bypp) ^ ldn_he_p(&d[i], bypp))); + } + } + } else if (!rop_mode && rop == 0xee) { + /* SRCPAINT, is there a way to do this with pixman? */ + unsigned int x, y, i, j; + uint8_t *sp = s->local_mem + src_base; + uint8_t *d = s->local_mem + dst_base; + + for (y = 0; y < height; y++) { + i = (dst_x + (dst_y + y) * dst_pitch) * bypp; + j = (src_x + (src_y + y) * src_pitch) * bypp; + for (x = 0; x < width; x++, i += bypp, j += bypp) { + stn_he_p(&d[i], bypp, + ldn_he_p(&sp[j], bypp) | ldn_he_p(&d[i], bypp)); + } + } } else { /* Do copy src for unimplemented ops, better than unpainted area */ if ((rop_mode && (rop != 0xc || rop2_source_is_pattern)) || @@ -798,33 +833,62 @@ static void sm501_2d_operation(SM501State *s) de = db + (width + (height - 1) * dst_pitch) * bypp; overlap = (db < se && sb < de); } - if (overlap) { +#ifdef CONFIG_PIXMAN + if (overlap && (s->use_pixman & BIT(2))) { /* pixman can't do reverse blit: copy via temporary */ int tmp_stride = DIV_ROUND_UP(width * bypp, sizeof(uint32_t)); + static uint32_t tmp_buf[16384]; uint32_t *tmp = tmp_buf; if (tmp_stride * sizeof(uint32_t) * height > sizeof(tmp_buf)) { tmp = g_malloc(tmp_stride * sizeof(uint32_t) * height); } - pixman_blt((uint32_t *)&s->local_mem[src_base], tmp, - src_pitch * bypp / sizeof(uint32_t), - tmp_stride, 8 * bypp, 8 * bypp, - src_x, src_y, 0, 0, width, height); - pixman_blt(tmp, (uint32_t *)&s->local_mem[dst_base], - tmp_stride, - dst_pitch * bypp / sizeof(uint32_t), - 8 * bypp, 8 * bypp, - 0, 0, dst_x, dst_y, width, height); + fallback = !pixman_blt((uint32_t *)&s->local_mem[src_base], + tmp, + src_pitch * bypp / sizeof(uint32_t), + tmp_stride, + 8 * bypp, 8 * bypp, + src_x, src_y, 0, 0, width, height); + if (!fallback) { + fallback = !pixman_blt(tmp, + (uint32_t *)&s->local_mem[dst_base], + tmp_stride, + dst_pitch * bypp / sizeof(uint32_t), + 8 * bypp, 8 * bypp, + 0, 0, dst_x, dst_y, width, height); + } if (tmp != tmp_buf) { g_free(tmp); } - } else { - pixman_blt((uint32_t *)&s->local_mem[src_base], - (uint32_t *)&s->local_mem[dst_base], - src_pitch * bypp / sizeof(uint32_t), - dst_pitch * bypp / sizeof(uint32_t), - 8 * bypp, 8 * bypp, - src_x, src_y, dst_x, dst_y, width, height); + } else if (!overlap && (s->use_pixman & BIT(1))) { + fallback = !pixman_blt((uint32_t *)&s->local_mem[src_base], + (uint32_t *)&s->local_mem[dst_base], + src_pitch * bypp / sizeof(uint32_t), + dst_pitch * bypp / sizeof(uint32_t), + 8 * bypp, 8 * bypp, src_x, src_y, + dst_x, dst_y, width, height); + } else +#endif + { + fallback = true; + } + if (fallback) { + uint8_t *sp = s->local_mem + src_base; + uint8_t *d = s->local_mem + dst_base; + unsigned int y, i, j; + for (y = 0; y < height; y++) { + if (overlap) { /* overlap also means rtl */ + i = (dst_y + height - 1 - y) * dst_pitch; + i = (dst_x + i) * bypp; + j = (src_y + height - 1 - y) * src_pitch; + j = (src_x + j) * bypp; + memmove(&d[i], &sp[j], width * bypp); + } else { + i = (dst_x + (dst_y + y) * dst_pitch) * bypp; + j = (src_x + (src_y + y) * src_pitch) * bypp; + memcpy(&d[i], &sp[j], width * bypp); + } + } } } break; @@ -839,14 +903,23 @@ static void sm501_2d_operation(SM501State *s) color = cpu_to_le16(color); } - if (width == 1 && height == 1) { - unsigned int i = (dst_x + dst_y * dst_pitch) * bypp; - stn_he_p(&s->local_mem[dst_base + i], bypp, color); - } else { - pixman_fill((uint32_t *)&s->local_mem[dst_base], - dst_pitch * bypp / sizeof(uint32_t), - 8 * bypp, dst_x, dst_y, width, height, color); - } +#ifdef CONFIG_PIXMAN + if (!(s->use_pixman & BIT(0)) || (width == 1 && height == 1) || + !pixman_fill((uint32_t *)&s->local_mem[dst_base], + dst_pitch * bypp / sizeof(uint32_t), 8 * bypp, + dst_x, dst_y, width, height, color)) +#endif + { + /* fallback when pixman failed or we don't want to call it */ + uint8_t *d = s->local_mem + dst_base; + unsigned int x, y, i; + for (y = 0; y < height; y++) { + i = (dst_x + (dst_y + y) * dst_pitch) * bypp; + for (x = 0; x < width; x++, i += bypp) { + stn_he_p(&d[i], bypp, color); + } + } + } break; } default: @@ -868,7 +941,7 @@ static void sm501_2d_operation(SM501State *s) static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, unsigned size) { - SM501State *s = (SM501State *)opaque; + SM501State *s = opaque; uint32_t ret = 0; switch (addr) { @@ -928,7 +1001,7 @@ static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, static void sm501_system_config_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - SM501State *s = (SM501State *)opaque; + SM501State *s = opaque; trace_sm501_system_config_write((uint32_t)addr, (uint32_t)value); switch (addr) { @@ -996,7 +1069,7 @@ static const MemoryRegionOps sm501_system_config_ops = { static uint64_t sm501_i2c_read(void *opaque, hwaddr addr, unsigned size) { - SM501State *s = (SM501State *)opaque; + SM501State *s = opaque; uint8_t ret = 0; switch (addr) { @@ -1023,7 +1096,7 @@ static uint64_t sm501_i2c_read(void *opaque, hwaddr addr, unsigned size) static void sm501_i2c_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - SM501State *s = (SM501State *)opaque; + SM501State *s = opaque; trace_sm501_i2c_write((uint32_t)addr, (uint32_t)value); switch (addr) { @@ -1092,7 +1165,7 @@ static const MemoryRegionOps sm501_i2c_ops = { static uint32_t sm501_palette_read(void *opaque, hwaddr addr) { - SM501State *s = (SM501State *)opaque; + SM501State *s = opaque; trace_sm501_palette_read((uint32_t)addr); @@ -1106,7 +1179,7 @@ static uint32_t sm501_palette_read(void *opaque, hwaddr addr) static void sm501_palette_write(void *opaque, hwaddr addr, uint32_t value) { - SM501State *s = (SM501State *)opaque; + SM501State *s = opaque; trace_sm501_palette_write((uint32_t)addr, value); @@ -1121,7 +1194,7 @@ static void sm501_palette_write(void *opaque, hwaddr addr, static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, unsigned size) { - SM501State *s = (SM501State *)opaque; + SM501State *s = opaque; uint32_t ret = 0; switch (addr) { @@ -1234,7 +1307,7 @@ static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - SM501State *s = (SM501State *)opaque; + SM501State *s = opaque; trace_sm501_disp_ctrl_write((uint32_t)addr, (uint32_t)value); switch (addr) { @@ -1379,7 +1452,7 @@ static const MemoryRegionOps sm501_disp_ctrl_ops = { static uint64_t sm501_2d_engine_read(void *opaque, hwaddr addr, unsigned size) { - SM501State *s = (SM501State *)opaque; + SM501State *s = opaque; uint32_t ret = 0; switch (addr) { @@ -1457,7 +1530,7 @@ static uint64_t sm501_2d_engine_read(void *opaque, hwaddr addr, static void sm501_2d_engine_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - SM501State *s = (SM501State *)opaque; + SM501State *s = opaque; trace_sm501_2d_engine_write((uint32_t)addr, (uint32_t)value); switch (addr) { @@ -1644,7 +1717,7 @@ static void draw_hwc_line_32(uint8_t *d, const uint8_t *s, int width, static void sm501_update_display(void *opaque) { - SM501State *s = (SM501State *)opaque; + SM501State *s = opaque; DisplaySurface *surface = qemu_console_surface(s->con); DirtyBitmapSnapshot *snap; int y, c_x = 0, c_y = 0; @@ -1768,7 +1841,8 @@ static const GraphicHwOps sm501_ops = { static void sm501_reset(SM501State *s) { s->system_control = 0x00100000; /* 2D engine FIFO empty */ - /* Bits 17 (SH), 7 (CDR), 6:5 (Test), 2:0 (Bus) are all supposed + /* + * Bits 17 (SH), 7 (CDR), 6:5 (Test), 2:0 (Bus) are all supposed * to be determined at reset by GPIO lines which set config bits. * We hardwire them: * SH = 0 : Hitachi Ready Polarity == Active Low @@ -1816,6 +1890,12 @@ static void sm501_reset(SM501State *s) static void sm501_init(SM501State *s, DeviceState *dev, uint32_t local_mem_bytes) { +#ifndef CONFIG_PIXMAN + if (s->use_pixman != 0) { + warn_report("x-pixman != 0, not effective without PIXMAN"); + } +#endif + s->local_mem_size_index = get_local_mem_size_index(local_mem_bytes); /* local memory */ @@ -1860,7 +1940,7 @@ static const VMStateDescription vmstate_sm501_state = { .name = "sm501-state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(local_mem_size_index, SM501State), VMSTATE_UINT32(system_control, SM501State), VMSTATE_UINT32(misc_control, SM501State), @@ -1942,15 +2022,14 @@ struct SM501SysBusState { /*< public >*/ SM501State state; uint32_t vram_size; - uint32_t base; SerialMM serial; + OHCISysBusState ohci; }; 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); @@ -1963,13 +2042,10 @@ static void sm501_realize_sysbus(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->state.mmio_region); /* bridge to usb host emulation module */ - usb_dev = qdev_new("sysbus-ohci"); - qdev_prop_set_uint32(usb_dev, "num-ports", 2); - qdev_prop_set_uint64(usb_dev, "dma-offset", s->base); - sysbus_realize_and_unref(SYS_BUS_DEVICE(usb_dev), &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->ohci), &error_fatal); memory_region_add_subregion(&s->state.mmio_region, SM501_USB_HOST, - sysbus_mmio_get_region(SYS_BUS_DEVICE(usb_dev), 0)); - sysbus_pass_irq(sbd, SYS_BUS_DEVICE(usb_dev)); + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ohci), 0)); + sysbus_pass_irq(sbd, SYS_BUS_DEVICE(&s->ohci)); /* bridge to serial emulation module */ sysbus_realize(SYS_BUS_DEVICE(&s->serial), &error_fatal); @@ -1980,7 +2056,8 @@ static void sm501_realize_sysbus(DeviceState *dev, Error **errp) static Property sm501_sysbus_properties[] = { DEFINE_PROP_UINT32("vram-size", SM501SysBusState, vram_size, 0), - DEFINE_PROP_UINT32("base", SM501SysBusState, base, 0), + /* this a debug option, prefer PROP_UINT over PROP_BIT for simplicity */ + DEFINE_PROP_UINT8("x-pixman", SM501SysBusState, state.use_pixman, DEFAULT_X_PIXMAN), DEFINE_PROP_END_OF_LIST(), }; @@ -1994,7 +2071,7 @@ static const VMStateDescription vmstate_sm501_sysbus = { .name = TYPE_SYSBUS_SM501, .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(state, SM501SysBusState, 1, vmstate_sm501_state, SM501State), VMSTATE_END_OF_LIST() @@ -2016,15 +2093,19 @@ static void sm501_sysbus_class_init(ObjectClass *klass, void *data) static void sm501_sysbus_init(Object *o) { SM501SysBusState *sm501 = SYSBUS_SM501(o); + OHCISysBusState *ohci = &sm501->ohci; SerialMM *smm = &sm501->serial; + object_initialize_child(o, "ohci", ohci, TYPE_SYSBUS_OHCI); + object_property_add_alias(o, "dma-offset", OBJECT(ohci), "dma-offset"); + qdev_prop_set_uint32(DEVICE(ohci), "num-ports", 2); + object_initialize_child(o, "serial", smm, 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"); + object_property_add_alias(o, "chardev", OBJECT(smm), "chardev"); } static const TypeInfo sm501_sysbus_info = { @@ -2064,6 +2145,7 @@ static void sm501_realize_pci(PCIDevice *dev, Error **errp) static Property sm501_pci_properties[] = { DEFINE_PROP_UINT32("vram-size", SM501PCIState, vram_size, 64 * MiB), + DEFINE_PROP_UINT8("x-pixman", SM501PCIState, state.use_pixman, DEFAULT_X_PIXMAN), DEFINE_PROP_END_OF_LIST(), }; @@ -2079,7 +2161,7 @@ static const VMStateDescription vmstate_sm501_pci = { .name = TYPE_PCI_SM501, .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, SM501PCIState), VMSTATE_STRUCT(state, SM501PCIState, 1, vmstate_sm501_state, SM501State), @@ -2104,11 +2186,18 @@ static void sm501_pci_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_sm501_pci; } +static void sm501_pci_init(Object *o) +{ + object_property_set_description(o, "x-pixman", "Use pixman for: " + "1: fill, 2: blit, 4: overlap blit"); +} + static const TypeInfo sm501_pci_info = { .name = TYPE_PCI_SM501, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(SM501PCIState), .class_init = sm501_pci_class_init, + .instance_init = sm501_pci_init, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c index aeae22da9c..e292cff44e 100644 --- a/hw/display/ssd0303.c +++ b/hw/display/ssd0303.c @@ -8,7 +8,7 @@ */ /* The controller can support a variety of different displays, but we only - implement one. Most of the commends relating to brightness and geometry + implement one. Most of the commands relating to brightness and geometry setup are ignored. */ #include "qemu/osdep.h" @@ -196,6 +196,8 @@ static int ssd0303_event(I2CSlave *i2c, enum i2c_event event) case I2C_NACK: /* Nothing to do. */ break; + default: + return -1; } return 0; @@ -279,7 +281,7 @@ static const VMStateDescription vmstate_ssd0303 = { .name = "ssd0303_oled", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(row, ssd0303_state), VMSTATE_INT32(col, ssd0303_state), VMSTATE_INT32(start_line, ssd0303_state), diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c index ab229d32b7..96cf0dc662 100644 --- a/hw/display/ssd0323.c +++ b/hw/display/ssd0323.c @@ -8,7 +8,7 @@ */ /* The controller can support a variety of different displays, but we only - implement one. Most of the commends relating to brightness and geometry + implement one. Most of the commands relating to brightness and geometry setup are ignored. */ #include "qemu/osdep.h" @@ -324,7 +324,7 @@ static const VMStateDescription vmstate_ssd0323 = { .version_id = 2, .minimum_version_id = 2, .post_load = ssd0323_post_load, - .fields = (VMStateField []) { + .fields = (const VMStateField []) { VMSTATE_UINT32(cmd_len, ssd0323_state), VMSTATE_INT32(cmd, ssd0323_state), VMSTATE_INT32_ARRAY(cmd_data, ssd0323_state, 8), diff --git a/hw/display/tcx.c b/hw/display/tcx.c index 1b27b64f6d..99507e7638 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -344,7 +344,7 @@ static const VMStateDescription vmstate_tcx = { .version_id = 4, .minimum_version_id = 4, .post_load = vmstate_tcx_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(height, TCXState), VMSTATE_UINT16(width, TCXState), VMSTATE_UINT16(depth, TCXState), diff --git a/hw/display/trace-events b/hw/display/trace-events index 0c0ffcbe42..2336a0ca15 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -55,7 +55,6 @@ virtio_gpu_fence_ctrl(uint64_t fence, uint32_t type) "fence 0x%" PRIx64 ", type virtio_gpu_fence_resp(uint64_t fence) "fence 0x%" PRIx64 # qxl.c -disable qxl_interface_set_mm_time(int qid, uint32_t mm_time) "%d %d" disable qxl_io_write_vga(int qid, const char *mode, uint32_t addr, uint32_t val) "%d %s addr=%u val=%u" qxl_create_guest_primary(int qid, uint32_t width, uint32_t height, uint64_t mem, uint32_t format, uint32_t position) "%d %ux%u mem=0x%" PRIx64 " %u,%u" qxl_create_guest_primary_rest(int qid, int32_t stride, uint32_t type, uint32_t flags) "%d %d,%d,%d" diff --git a/hw/display/vga-helpers.h b/hw/display/vga-helpers.h index 10e9cfd40a..2029b61791 100644 --- a/hw/display/vga-helpers.h +++ b/hw/display/vga-helpers.h @@ -98,17 +98,22 @@ static void vga_draw_glyph9(uint8_t *d, int linesize, /* * 4 color mode */ -static void vga_draw_line2(VGACommonState *vga, uint8_t *d, - uint32_t addr, int width) +static void *vga_draw_line2(VGACommonState *vga, uint8_t *d, + uint32_t addr, int width, int hpel) { uint32_t plane_mask, *palette, data, v; int x; palette = vga->last_palette; plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; + hpel &= 7; + if (hpel) { + width += 8; + d = vga->panning_buf; + } width >>= 3; for(x = 0; x < width; x++) { - data = vga_read_dword_le(vga, addr); + data = vga_read_dword_le(vga, addr & (VGA_VRAM_SIZE - 1)); data &= plane_mask; v = expand2[GET_PLANE(data, 0)]; v |= expand2[GET_PLANE(data, 2)] << 2; @@ -126,6 +131,7 @@ static void vga_draw_line2(VGACommonState *vga, uint8_t *d, d += 32; addr += 4; } + return hpel ? vga->panning_buf + 4 * hpel : NULL; } #define PUT_PIXEL2(d, n, v) \ @@ -134,17 +140,22 @@ static void vga_draw_line2(VGACommonState *vga, uint8_t *d, /* * 4 color mode, dup2 horizontal */ -static void vga_draw_line2d2(VGACommonState *vga, uint8_t *d, - uint32_t addr, int width) +static void *vga_draw_line2d2(VGACommonState *vga, uint8_t *d, + uint32_t addr, int width, int hpel) { uint32_t plane_mask, *palette, data, v; int x; palette = vga->last_palette; plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; + hpel &= 7; + if (hpel) { + width += 8; + d = vga->panning_buf; + } width >>= 3; for(x = 0; x < width; x++) { - data = vga_read_dword_le(vga, addr); + data = vga_read_dword_le(vga, addr & (VGA_VRAM_SIZE - 1)); data &= plane_mask; v = expand2[GET_PLANE(data, 0)]; v |= expand2[GET_PLANE(data, 2)] << 2; @@ -162,22 +173,28 @@ static void vga_draw_line2d2(VGACommonState *vga, uint8_t *d, d += 64; addr += 4; } + return hpel ? vga->panning_buf + 8 * hpel : NULL; } /* * 16 color mode */ -static void vga_draw_line4(VGACommonState *vga, uint8_t *d, - uint32_t addr, int width) +static void *vga_draw_line4(VGACommonState *vga, uint8_t *d, + uint32_t addr, int width, int hpel) { uint32_t plane_mask, data, v, *palette; int x; palette = vga->last_palette; plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; + hpel &= 7; + if (hpel) { + width += 8; + d = vga->panning_buf; + } width >>= 3; for(x = 0; x < width; x++) { - data = vga_read_dword_le(vga, addr); + data = vga_read_dword_le(vga, addr & (VGA_VRAM_SIZE - 1)); data &= plane_mask; v = expand4[GET_PLANE(data, 0)]; v |= expand4[GET_PLANE(data, 1)] << 1; @@ -194,22 +211,28 @@ static void vga_draw_line4(VGACommonState *vga, uint8_t *d, d += 32; addr += 4; } + return hpel ? vga->panning_buf + 4 * hpel : NULL; } /* * 16 color mode, dup2 horizontal */ -static void vga_draw_line4d2(VGACommonState *vga, uint8_t *d, - uint32_t addr, int width) +static void *vga_draw_line4d2(VGACommonState *vga, uint8_t *d, + uint32_t addr, int width, int hpel) { uint32_t plane_mask, data, v, *palette; int x; palette = vga->last_palette; plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; + hpel &= 7; + if (hpel) { + width += 8; + d = vga->panning_buf; + } width >>= 3; for(x = 0; x < width; x++) { - data = vga_read_dword_le(vga, addr); + data = vga_read_dword_le(vga, addr & (VGA_VRAM_SIZE - 1)); data &= plane_mask; v = expand4[GET_PLANE(data, 0)]; v |= expand4[GET_PLANE(data, 1)] << 1; @@ -226,6 +249,7 @@ static void vga_draw_line4d2(VGACommonState *vga, uint8_t *d, d += 64; addr += 4; } + return hpel ? vga->panning_buf + 8 * hpel : NULL; } /* @@ -233,15 +257,33 @@ static void vga_draw_line4d2(VGACommonState *vga, uint8_t *d, * * XXX: add plane_mask support (never used in standard VGA modes) */ -static void vga_draw_line8d2(VGACommonState *vga, uint8_t *d, - uint32_t addr, int width) +static void *vga_draw_line8d2(VGACommonState *vga, uint8_t *d, + uint32_t addr, int width, int hpel) { uint32_t *palette; int x; palette = vga->last_palette; + hpel = (hpel >> 1) & 3; + + /* For 256 color modes, we can adjust the source address and write directly + * to the destination, even if horizontal pel panning is active. However, + * the loop below assumes that the address does not wrap in the middle of a + * plane. If that happens... + */ + if (addr + (width >> 3) * 4 < VGA_VRAM_SIZE) { + addr += hpel * 4; + hpel = 0; + } + + /* ... use the panning buffer as in planar modes. */ + if (hpel) { + width += 8; + d = vga->panning_buf; + } width >>= 3; for(x = 0; x < width; x++) { + addr &= VGA_VRAM_SIZE - 1; PUT_PIXEL2(d, 0, palette[vga_read_byte(vga, addr + 0)]); PUT_PIXEL2(d, 1, palette[vga_read_byte(vga, addr + 1)]); PUT_PIXEL2(d, 2, palette[vga_read_byte(vga, addr + 2)]); @@ -249,6 +291,7 @@ static void vga_draw_line8d2(VGACommonState *vga, uint8_t *d, d += 32; addr += 4; } + return hpel ? vga->panning_buf + 8 * hpel : NULL; } /* @@ -256,13 +299,18 @@ static void vga_draw_line8d2(VGACommonState *vga, uint8_t *d, * * XXX: add plane_mask support (never used in standard VGA modes) */ -static void vga_draw_line8(VGACommonState *vga, uint8_t *d, - uint32_t addr, int width) +static void *vga_draw_line8(VGACommonState *vga, uint8_t *d, + uint32_t addr, int width, int hpel) { uint32_t *palette; int x; palette = vga->last_palette; + hpel = (hpel >> 1) & 3; + if (hpel) { + width += 8; + d = vga->panning_buf; + } width >>= 3; for(x = 0; x < width; x++) { ((uint32_t *)d)[0] = palette[vga_read_byte(vga, addr + 0)]; @@ -276,13 +324,14 @@ static void vga_draw_line8(VGACommonState *vga, uint8_t *d, d += 32; addr += 8; } + return hpel ? vga->panning_buf + 4 * hpel : NULL; } /* * 15 bit color */ -static void vga_draw_line15_le(VGACommonState *vga, uint8_t *d, - uint32_t addr, int width) +static void *vga_draw_line15_le(VGACommonState *vga, uint8_t *d, + uint32_t addr, int width, int hpel) { int w; uint32_t v, r, g, b; @@ -297,10 +346,11 @@ static void vga_draw_line15_le(VGACommonState *vga, uint8_t *d, addr += 2; d += 4; } while (--w != 0); + return NULL; } -static void vga_draw_line15_be(VGACommonState *vga, uint8_t *d, - uint32_t addr, int width) +static void *vga_draw_line15_be(VGACommonState *vga, uint8_t *d, + uint32_t addr, int width, int hpel) { int w; uint32_t v, r, g, b; @@ -315,13 +365,14 @@ static void vga_draw_line15_be(VGACommonState *vga, uint8_t *d, addr += 2; d += 4; } while (--w != 0); + return NULL; } /* * 16 bit color */ -static void vga_draw_line16_le(VGACommonState *vga, uint8_t *d, - uint32_t addr, int width) +static void *vga_draw_line16_le(VGACommonState *vga, uint8_t *d, + uint32_t addr, int width, int hpel) { int w; uint32_t v, r, g, b; @@ -336,10 +387,11 @@ static void vga_draw_line16_le(VGACommonState *vga, uint8_t *d, addr += 2; d += 4; } while (--w != 0); + return NULL; } -static void vga_draw_line16_be(VGACommonState *vga, uint8_t *d, - uint32_t addr, int width) +static void *vga_draw_line16_be(VGACommonState *vga, uint8_t *d, + uint32_t addr, int width, int hpel) { int w; uint32_t v, r, g, b; @@ -354,13 +406,14 @@ static void vga_draw_line16_be(VGACommonState *vga, uint8_t *d, addr += 2; d += 4; } while (--w != 0); + return NULL; } /* * 24 bit color */ -static void vga_draw_line24_le(VGACommonState *vga, uint8_t *d, - uint32_t addr, int width) +static void *vga_draw_line24_le(VGACommonState *vga, uint8_t *d, + uint32_t addr, int width, int hpel) { int w; uint32_t r, g, b; @@ -374,10 +427,11 @@ static void vga_draw_line24_le(VGACommonState *vga, uint8_t *d, addr += 3; d += 4; } while (--w != 0); + return NULL; } -static void vga_draw_line24_be(VGACommonState *vga, uint8_t *d, - uint32_t addr, int width) +static void *vga_draw_line24_be(VGACommonState *vga, uint8_t *d, + uint32_t addr, int width, int hpel) { int w; uint32_t r, g, b; @@ -391,13 +445,14 @@ static void vga_draw_line24_be(VGACommonState *vga, uint8_t *d, addr += 3; d += 4; } while (--w != 0); + return NULL; } /* * 32 bit color */ -static void vga_draw_line32_le(VGACommonState *vga, uint8_t *d, - uint32_t addr, int width) +static void *vga_draw_line32_le(VGACommonState *vga, uint8_t *d, + uint32_t addr, int width, int hpel) { int w; uint32_t r, g, b; @@ -411,10 +466,11 @@ static void vga_draw_line32_le(VGACommonState *vga, uint8_t *d, addr += 4; d += 4; } while (--w != 0); + return NULL; } -static void vga_draw_line32_be(VGACommonState *vga, uint8_t *d, - uint32_t addr, int width) +static void *vga_draw_line32_be(VGACommonState *vga, uint8_t *d, + uint32_t addr, int width, int hpel) { int w; uint32_t r, g, b; @@ -428,4 +484,5 @@ static void vga_draw_line32_be(VGACommonState *vga, uint8_t *d, addr += 4; d += 4; } while (--w != 0); + return NULL; } diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c index 46abbc5653..c096ec93e5 100644 --- a/hw/display/vga-isa.c +++ b/hw/display/vga-isa.c @@ -1,7 +1,7 @@ /* * QEMU ISA VGA Emulator. * - * see docs/specs/standard-vga.txt for virtual hardware specs. + * see docs/specs/standard-vga.rst for virtual hardware specs. * * Copyright (c) 2003 Fabrice Bellard * @@ -32,6 +32,7 @@ #include "qemu/timer.h" #include "hw/loader.h" #include "hw/qdev-properties.h" +#include "ui/console.h" #include "qom/object.h" #define TYPE_ISA_VGA "isa-vga" diff --git a/hw/display/vga-mmio.c b/hw/display/vga-mmio.c index 75dfcedea5..cd2c46776d 100644 --- a/hw/display/vga-mmio.c +++ b/hw/display/vga-mmio.c @@ -27,6 +27,7 @@ #include "hw/sysbus.h" #include "hw/display/vga.h" #include "hw/qdev-properties.h" +#include "ui/console.h" #include "vga_int.h" /* diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index 3e5bc259f7..2d8adce5da 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -1,7 +1,7 @@ /* * QEMU PCI VGA Emulator. * - * see docs/specs/standard-vga.txt for virtual hardware specs. + * see docs/specs/standard-vga.rst for virtual hardware specs. * * Copyright (c) 2003 Fabrice Bellard * @@ -25,16 +25,18 @@ */ #include "qemu/osdep.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "vga_int.h" #include "ui/pixel_ops.h" +#include "ui/console.h" #include "qemu/module.h" #include "qemu/timer.h" #include "hw/loader.h" #include "hw/display/edid.h" #include "qom/object.h" +#include "hw/acpi/acpi_aml_interface.h" enum vga_pci_flags { PCI_VGA_FLAG_ENABLE_MMIO = 1, @@ -59,7 +61,7 @@ static const VMStateDescription vmstate_vga_pci = { .name = "vga", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, PCIVGAState), VMSTATE_STRUCT(vga, PCIVGAState, 0, vmstate_vga_common, VGACommonState), VMSTATE_END_OF_LIST() @@ -354,11 +356,13 @@ static void vga_pci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); k->vendor_id = PCI_VENDOR_ID_QEMU; k->device_id = PCI_DEVICE_ID_QEMU_VGA; dc->vmsd = &vmstate_vga_pci; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + adevc->build_dev_aml = build_vga_aml; } static const TypeInfo vga_pci_type_info = { @@ -369,6 +373,7 @@ static const TypeInfo vga_pci_type_info = { .class_init = vga_pci_class_init, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { TYPE_ACPI_DEV_AML_IF }, { }, }, }; diff --git a/hw/display/vga.c b/hw/display/vga.c index 5dca2d1528..77f59e8c11 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -26,11 +26,14 @@ #include "qemu/units.h" #include "sysemu/reset.h" #include "qapi/error.h" +#include "hw/core/cpu.h" #include "hw/display/vga.h" +#include "hw/i386/x86.h" #include "hw/pci/pci.h" #include "vga_int.h" #include "vga_regs.h" #include "ui/pixel_ops.h" +#include "ui/console.h" #include "qemu/timer.h" #include "hw/xen/xen.h" #include "migration/vmstate.h" @@ -44,6 +47,16 @@ bool have_vga = true; /* 16 state changes per vertical frame @60 Hz */ #define VGA_TEXT_CURSOR_PERIOD_MS (1000 * 2 * 16 / 60) +/* Address mask for non-VESA modes. */ +#define VGA_VRAM_SIZE (256 * KiB) + +/* This value corresponds to a shift of zero pixels + * in 9-dot text mode. In other modes, bit 3 is undefined; + * we just ignore it, so that 8 corresponds to zero pixels + * in all modes. + */ +#define VGA_HPEL_NEUTRAL 8 + /* * Video Graphics Array (VGA) * @@ -87,58 +100,27 @@ const uint8_t gr_mask[16] = { 0x00, /* 0x0f */ }; -#define cbswap_32(__x) \ -((uint32_t)( \ - (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ - (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ - (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ - (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) - -#if HOST_BIG_ENDIAN -#define PAT(x) cbswap_32(x) -#else -#define PAT(x) (x) -#endif - -#if HOST_BIG_ENDIAN -#define BIG 1 -#else -#define BIG 0 -#endif - -#if HOST_BIG_ENDIAN -#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff) -#else -#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff) -#endif +#define GET_PLANE(data, p) ((cpu_to_le32(data) >> ((p) * 8)) & 0xff) static const uint32_t mask16[16] = { - PAT(0x00000000), - PAT(0x000000ff), - PAT(0x0000ff00), - PAT(0x0000ffff), - PAT(0x00ff0000), - PAT(0x00ff00ff), - PAT(0x00ffff00), - PAT(0x00ffffff), - PAT(0xff000000), - PAT(0xff0000ff), - PAT(0xff00ff00), - PAT(0xff00ffff), - PAT(0xffff0000), - PAT(0xffff00ff), - PAT(0xffffff00), - PAT(0xffffffff), + const_le32(0x00000000), + const_le32(0x000000ff), + const_le32(0x0000ff00), + const_le32(0x0000ffff), + const_le32(0x00ff0000), + const_le32(0x00ff00ff), + const_le32(0x00ffff00), + const_le32(0x00ffffff), + const_le32(0xff000000), + const_le32(0xff0000ff), + const_le32(0xff00ff00), + const_le32(0xff00ffff), + const_le32(0xffff0000), + const_le32(0xffff00ff), + const_le32(0xffffff00), + const_le32(0xffffffff), }; -#undef PAT - -#if HOST_BIG_ENDIAN -#define PAT(x) (x) -#else -#define PAT(x) cbswap_32(x) -#endif - static uint32_t expand4[256]; static uint16_t expand2[256]; static uint8_t expand4to8[16]; @@ -833,48 +815,65 @@ uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr) } if (sr(s, VGA_SEQ_MEMORY_MODE) & VGA_SR04_CHN_4M) { - /* chain 4 mode : simplest access */ - assert(addr < s->vram_size); - ret = s->vram_ptr[addr]; - } else if (s->gr[VGA_GFX_MODE] & 0x10) { + /* chain4 mode */ + plane = addr & 3; + addr &= ~3; + } else if (s->gr[VGA_GFX_MODE] & VGA_GR05_HOST_ODD_EVEN) { /* odd/even mode (aka text mode mapping) */ plane = (s->gr[VGA_GFX_PLANE_READ] & 2) | (addr & 1); - addr = ((addr & ~1) << 1) | plane; - if (addr >= s->vram_size) { - return 0xff; - } - ret = s->vram_ptr[addr]; } else { /* standard VGA latched access */ - if (addr * sizeof(uint32_t) >= s->vram_size) { - return 0xff; - } - s->latch = ((uint32_t *)s->vram_ptr)[addr]; - - if (!(s->gr[VGA_GFX_MODE] & 0x08)) { - /* read mode 0 */ - plane = s->gr[VGA_GFX_PLANE_READ]; - ret = GET_PLANE(s->latch, plane); - } else { - /* read mode 1 */ - ret = (s->latch ^ mask16[s->gr[VGA_GFX_COMPARE_VALUE]]) & - mask16[s->gr[VGA_GFX_COMPARE_MASK]]; - ret |= ret >> 16; - ret |= ret >> 8; - ret = (~ret) & 0xff; - } + plane = s->gr[VGA_GFX_PLANE_READ]; } + + if (s->gr[VGA_GFX_MISC] & VGA_GR06_CHAIN_ODD_EVEN) { + addr &= ~1; + } + + /* Doubleword/word mode. See comment in vga_mem_writeb */ + if (s->cr[VGA_CRTC_UNDERLINE] & VGA_CR14_DW) { + addr >>= 2; + } else if ((s->gr[VGA_GFX_MODE] & VGA_GR05_HOST_ODD_EVEN) && + (s->cr[VGA_CRTC_MODE] & VGA_CR17_WORD_BYTE) == 0) { + addr >>= 1; + } + + if (addr * sizeof(uint32_t) >= s->vram_size) { + return 0xff; + } + + if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { + /* chain 4 mode: simplified access (but it should use the same + * algorithms as below, see e.g. vga_mem_writeb's plane mask check). + */ + return s->vram_ptr[(addr << 2) | plane]; + } + + s->latch = ((uint32_t *)s->vram_ptr)[addr]; + if (!(s->gr[VGA_GFX_MODE] & 0x08)) { + /* read mode 0 */ + ret = GET_PLANE(s->latch, plane); + } else { + /* read mode 1 */ + ret = (s->latch ^ mask16[s->gr[VGA_GFX_COMPARE_VALUE]]) & + mask16[s->gr[VGA_GFX_COMPARE_MASK]]; + ret |= ret >> 16; + ret |= ret >> 8; + ret = (~ret) & 0xff; + } + return ret; } /* called for accesses between 0xa0000 and 0xc0000 */ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val) { - int memory_map_mode, plane, write_mode, b, func_select, mask; + int memory_map_mode, write_mode, b, func_select, mask; uint32_t write_mask, bit_mask, set_mask; + int plane = 0; #ifdef DEBUG_VGA_MEM - printf("vga: [0x" TARGET_FMT_plx "] = 0x%02x\n", addr, val); + printf("vga: [0x" HWADDR_FMT_plx "] = 0x%02x\n", addr, val); #endif /* convert to VGA memory offset */ memory_map_mode = (s->gr[VGA_GFX_MISC] >> 2) & 3; @@ -900,117 +899,136 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val) break; } + mask = sr(s, VGA_SEQ_PLANE_WRITE); if (sr(s, VGA_SEQ_MEMORY_MODE) & VGA_SR04_CHN_4M) { /* chain 4 mode : simplest access */ plane = addr & 3; - mask = (1 << plane); - if (sr(s, VGA_SEQ_PLANE_WRITE) & mask) { - assert(addr < s->vram_size); - s->vram_ptr[addr] = val; -#ifdef DEBUG_VGA_MEM - printf("vga: chain4: [0x" TARGET_FMT_plx "]\n", addr); -#endif - s->plane_updated |= mask; /* only used to detect font change */ - memory_region_set_dirty(&s->vram, addr, 1); - } - } else if (s->gr[VGA_GFX_MODE] & 0x10) { - /* odd/even mode (aka text mode mapping) */ - plane = (s->gr[VGA_GFX_PLANE_READ] & 2) | (addr & 1); - mask = (1 << plane); - if (sr(s, VGA_SEQ_PLANE_WRITE) & mask) { - addr = ((addr & ~1) << 1) | plane; - if (addr >= s->vram_size) { - return; - } - s->vram_ptr[addr] = val; -#ifdef DEBUG_VGA_MEM - printf("vga: odd/even: [0x" TARGET_FMT_plx "]\n", addr); -#endif - s->plane_updated |= mask; /* only used to detect font change */ - memory_region_set_dirty(&s->vram, addr, 1); - } + mask &= (1 << plane); + addr &= ~3; } else { - /* standard VGA latched access */ - write_mode = s->gr[VGA_GFX_MODE] & 3; - switch(write_mode) { - default: - case 0: - /* rotate */ - b = s->gr[VGA_GFX_DATA_ROTATE] & 7; - val = ((val >> b) | (val << (8 - b))) & 0xff; - val |= val << 8; - val |= val << 16; - - /* apply set/reset mask */ - set_mask = mask16[s->gr[VGA_GFX_SR_ENABLE]]; - val = (val & ~set_mask) | - (mask16[s->gr[VGA_GFX_SR_VALUE]] & set_mask); - bit_mask = s->gr[VGA_GFX_BIT_MASK]; - break; - case 1: - val = s->latch; - goto do_write; - case 2: - val = mask16[val & 0x0f]; - bit_mask = s->gr[VGA_GFX_BIT_MASK]; - break; - case 3: - /* rotate */ - b = s->gr[VGA_GFX_DATA_ROTATE] & 7; - val = (val >> b) | (val << (8 - b)); - - bit_mask = s->gr[VGA_GFX_BIT_MASK] & val; - val = mask16[s->gr[VGA_GFX_SR_VALUE]]; - break; + if ((sr(s, VGA_SEQ_MEMORY_MODE) & VGA_SR04_SEQ_MODE) == 0) { + mask &= (addr & 1) ? 0x0a : 0x05; } - - /* apply logical operation */ - func_select = s->gr[VGA_GFX_DATA_ROTATE] >> 3; - switch(func_select) { - case 0: - default: - /* nothing to do */ - break; - case 1: - /* and */ - val &= s->latch; - break; - case 2: - /* or */ - val |= s->latch; - break; - case 3: - /* xor */ - val ^= s->latch; - break; + if (s->gr[VGA_GFX_MISC] & VGA_GR06_CHAIN_ODD_EVEN) { + addr &= ~1; } - - /* apply bit mask */ - bit_mask |= bit_mask << 8; - bit_mask |= bit_mask << 16; - val = (val & bit_mask) | (s->latch & ~bit_mask); - - do_write: - /* mask data according to sr[2] */ - mask = sr(s, VGA_SEQ_PLANE_WRITE); - s->plane_updated |= mask; /* only used to detect font change */ - write_mask = mask16[mask]; - if (addr * sizeof(uint32_t) >= s->vram_size) { - return; - } - ((uint32_t *)s->vram_ptr)[addr] = - (((uint32_t *)s->vram_ptr)[addr] & ~write_mask) | - (val & write_mask); -#ifdef DEBUG_VGA_MEM - printf("vga: latch: [0x" TARGET_FMT_plx "] mask=0x%08x val=0x%08x\n", - addr * 4, write_mask, val); -#endif - memory_region_set_dirty(&s->vram, addr << 2, sizeof(uint32_t)); } + + /* Doubleword/word mode. These should be honored when displaying, + * not when reading/writing to memory! For example, chain4 modes + * use double-word mode and, on real hardware, would fetch bytes + * 0,1,2,3, 16,17,18,19, 32,33,34,35, etc. Text modes use word + * mode and, on real hardware, would fetch bytes 0,1, 8,9, etc. + * + * QEMU instead shifted addresses on memory accesses because it + * allows more optimizations (e.g. chain4_alias) and simplifies + * the draw_line handlers. Unfortunately, there is one case where + * the difference shows. When fetching font data, accesses are + * always in consecutive bytes, even if the text/attribute pairs + * are done in word mode. Hence, doing a right shift when operating + * on font data is wrong. So check the odd/even mode bits together with + * word mode bit. The odd/even read bit is 0 when reading font data, + * and the odd/even write bit is 1 when writing it. + */ + if (s->cr[VGA_CRTC_UNDERLINE] & VGA_CR14_DW) { + addr >>= 2; + } else if ((sr(s, VGA_SEQ_MEMORY_MODE) & VGA_SR04_SEQ_MODE) == 0 && + (s->cr[VGA_CRTC_MODE] & VGA_CR17_WORD_BYTE) == 0) { + addr >>= 1; + } + + if (addr * sizeof(uint32_t) >= s->vram_size) { + return; + } + + if (sr(s, VGA_SEQ_MEMORY_MODE) & VGA_SR04_CHN_4M) { + if (mask) { + s->vram_ptr[(addr << 2) | plane] = val; +#ifdef DEBUG_VGA_MEM + printf("vga: chain4: [0x" HWADDR_FMT_plx "]\n", addr); +#endif + s->plane_updated |= mask; /* only used to detect font change */ + memory_region_set_dirty(&s->vram, addr, 1); + } + return; + } + + /* standard VGA latched access */ + write_mode = s->gr[VGA_GFX_MODE] & 3; + switch(write_mode) { + default: + case 0: + /* rotate */ + b = s->gr[VGA_GFX_DATA_ROTATE] & 7; + val = ((val >> b) | (val << (8 - b))) & 0xff; + val |= val << 8; + val |= val << 16; + + /* apply set/reset mask */ + set_mask = mask16[s->gr[VGA_GFX_SR_ENABLE]]; + val = (val & ~set_mask) | + (mask16[s->gr[VGA_GFX_SR_VALUE]] & set_mask); + bit_mask = s->gr[VGA_GFX_BIT_MASK]; + break; + case 1: + val = s->latch; + goto do_write; + case 2: + val = mask16[val & 0x0f]; + bit_mask = s->gr[VGA_GFX_BIT_MASK]; + break; + case 3: + /* rotate */ + b = s->gr[VGA_GFX_DATA_ROTATE] & 7; + val = (val >> b) | (val << (8 - b)); + + bit_mask = s->gr[VGA_GFX_BIT_MASK] & val; + val = mask16[s->gr[VGA_GFX_SR_VALUE]]; + break; + } + + /* apply logical operation */ + func_select = s->gr[VGA_GFX_DATA_ROTATE] >> 3; + switch(func_select) { + case 0: + default: + /* nothing to do */ + break; + case 1: + /* and */ + val &= s->latch; + break; + case 2: + /* or */ + val |= s->latch; + break; + case 3: + /* xor */ + val ^= s->latch; + break; + } + + /* apply bit mask */ + bit_mask |= bit_mask << 8; + bit_mask |= bit_mask << 16; + val = (val & bit_mask) | (s->latch & ~bit_mask); + +do_write: + /* mask data according to sr[2] */ + s->plane_updated |= mask; /* only used to detect font change */ + write_mask = mask16[mask]; + ((uint32_t *)s->vram_ptr)[addr] = + (((uint32_t *)s->vram_ptr)[addr] & ~write_mask) | + (val & write_mask); +#ifdef DEBUG_VGA_MEM + printf("vga: latch: [0x" HWADDR_FMT_plx "] mask=0x%08x val=0x%08x\n", + addr * 4, write_mask, val); +#endif + memory_region_set_dirty(&s->vram, addr << 2, sizeof(uint32_t)); } -typedef void vga_draw_line_func(VGACommonState *s1, uint8_t *d, - uint32_t srcaddr, int width); +typedef void *vga_draw_line_func(VGACommonState *s1, uint8_t *d, + uint32_t srcaddr, int width, int hpel); #include "vga-access.h" #include "vga-helpers.h" @@ -1070,52 +1088,45 @@ static int update_palette256(VGACommonState *s) return full_update; } -static void vga_get_offsets(VGACommonState *s, - uint32_t *pline_offset, - uint32_t *pstart_addr, - uint32_t *pline_compare) +static void vga_get_params(VGACommonState *s, + VGADisplayParams *params) { - uint32_t start_addr, line_offset, line_compare; - if (vbe_enabled(s)) { - line_offset = s->vbe_line_offset; - start_addr = s->vbe_start_addr; - line_compare = 65535; + params->line_offset = s->vbe_line_offset; + params->start_addr = s->vbe_start_addr; + params->line_compare = 65535; + params->hpel = VGA_HPEL_NEUTRAL; + params->hpel_split = false; } else { /* compute line_offset in bytes */ - line_offset = s->cr[VGA_CRTC_OFFSET]; - line_offset <<= 3; + params->line_offset = s->cr[VGA_CRTC_OFFSET] << 3; /* starting address */ - start_addr = s->cr[VGA_CRTC_START_LO] | + params->start_addr = s->cr[VGA_CRTC_START_LO] | (s->cr[VGA_CRTC_START_HI] << 8); /* line compare */ - line_compare = s->cr[VGA_CRTC_LINE_COMPARE] | + params->line_compare = s->cr[VGA_CRTC_LINE_COMPARE] | ((s->cr[VGA_CRTC_OVERFLOW] & 0x10) << 4) | ((s->cr[VGA_CRTC_MAX_SCAN] & 0x40) << 3); + + params->hpel = s->ar[VGA_ATC_PEL]; + params->hpel_split = s->ar[VGA_ATC_MODE] & 0x20; } - *pline_offset = line_offset; - *pstart_addr = start_addr; - *pline_compare = line_compare; } /* update start_addr and line_offset. Return TRUE if modified */ static int update_basic_params(VGACommonState *s) { int full_update; - uint32_t start_addr, line_offset, line_compare; + VGADisplayParams current; full_update = 0; - s->get_offsets(s, &line_offset, &start_addr, &line_compare); + s->get_params(s, ¤t); - if (line_offset != s->line_offset || - start_addr != s->start_addr || - line_compare != s->line_compare) { - s->line_offset = line_offset; - s->start_addr = start_addr; - s->line_compare = line_compare; + if (memcmp(¤t, &s->params, sizeof(current))) { + s->params = current; full_update = 1; } return full_update; @@ -1216,7 +1227,7 @@ static void vga_draw_text(VGACommonState *s, int full_update) } full_update |= update_basic_params(s); - line_offset = s->line_offset; + line_offset = s->params.line_offset; vga_get_text_resolution(s, &width, &height, &cw, &cheight); if ((height * width) <= 1) { @@ -1255,7 +1266,7 @@ static void vga_draw_text(VGACommonState *s, int full_update) } cursor_offset = ((s->cr[VGA_CRTC_CURSOR_HI] << 8) | - s->cr[VGA_CRTC_CURSOR_LO]) - s->start_addr; + s->cr[VGA_CRTC_CURSOR_LO]) - s->params.start_addr; if (cursor_offset != s->cursor_offset || s->cr[VGA_CRTC_CURSOR_START] != s->cursor_start || s->cr[VGA_CRTC_CURSOR_END] != s->cursor_end) { @@ -1269,7 +1280,7 @@ static void vga_draw_text(VGACommonState *s, int full_update) s->cursor_start = s->cr[VGA_CRTC_CURSOR_START]; s->cursor_end = s->cr[VGA_CRTC_CURSOR_END]; } - cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4; + cursor_ptr = s->vram_ptr + (s->params.start_addr + cursor_offset) * 4; if (now >= s->cursor_blink_time) { s->cursor_blink_time = now + VGA_TEXT_CURSOR_PERIOD_MS / 2; s->cursor_visible_phase = !s->cursor_visible_phase; @@ -1279,7 +1290,7 @@ static void vga_draw_text(VGACommonState *s, int full_update) linesize = surface_stride(surface); ch_attr_ptr = s->last_ch_attr; line = 0; - offset = s->start_addr * 4; + offset = s->params.start_addr * 4; for(cy = 0; cy < height; cy++) { d1 = dest; src = s->vram_ptr + offset; @@ -1359,7 +1370,7 @@ static void vga_draw_text(VGACommonState *s, int full_update) dest += linesize * cheight; line1 = line + cheight; offset += line_offset; - if (line < s->line_compare && line1 >= s->line_compare) { + if (line < s->params.line_compare && line1 >= s->params.line_compare) { offset = 0; } line = line1; @@ -1472,6 +1483,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) ram_addr_t page0, page1, region_start, region_end; DirtyBitmapSnapshot *snap = NULL; int disp_width, multi_scan, multi_run; + int hpel; uint8_t *d; uint32_t v, addr1, addr; vga_draw_line_func *vga_draw_line = NULL; @@ -1489,34 +1501,10 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) disp_width = width; depth = s->get_bpp(s); - region_start = (s->start_addr * 4); - region_end = region_start + (ram_addr_t)s->line_offset * height; - region_end += width * depth / 8; /* scanline length */ - region_end -= s->line_offset; - if (region_end > s->vbe_size || depth == 0 || depth == 15) { - /* - * We land here on: - * - wraps around (can happen with cirrus vbe modes) - * - depth == 0 (256 color palette video mode) - * - depth == 15 - * - * Take the safe and slow route: - * - create a dirty bitmap snapshot for all vga memory. - * - force shadowing (so all vga memory access goes - * through vga_read_*() helpers). - * - * Given this affects only vga features which are pretty much - * unused by modern guests there should be no performance - * impact. - */ - region_start = 0; - region_end = s->vbe_size; - force_shadow = true; - } - + /* bits 5-6: 0 = 16-color mode, 1 = 4-color mode, 2 = 256-color mode. */ shift_control = (s->gr[VGA_GFX_MODE] >> 5) & 3; double_scan = (s->cr[VGA_CRTC_MAX_SCAN] >> 7); - if (shift_control != 1) { + if (s->cr[VGA_CRTC_MODE] & 1) { multi_scan = (((s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1) << double_scan) - 1; } else { @@ -1532,83 +1520,28 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) s->double_scan = double_scan; } - if (shift_control == 0) { - if (sr(s, VGA_SEQ_CLOCK_MODE) & 8) { - disp_width <<= 1; - } - } else if (shift_control == 1) { - if (sr(s, VGA_SEQ_CLOCK_MODE) & 8) { - disp_width <<= 1; - } - } - - /* - * Check whether we can share the surface with the backend - * or whether we need a shadow surface. We share native - * endian surfaces for 15bpp and above and byteswapped - * surfaces for 24bpp and above. - */ - format = qemu_default_pixman_format(depth, !byteswap); - if (format) { - share_surface = dpy_gfx_check_format(s->con, format) - && !s->force_shadow && !force_shadow; - } else { - share_surface = false; - } - - if (s->line_offset != s->last_line_offset || - disp_width != s->last_width || - height != s->last_height || - s->last_depth != depth || - s->last_byteswap != byteswap || - share_surface != is_buffer_shared(surface)) { - /* display parameters changed -> need new display surface */ - s->last_scr_width = disp_width; - s->last_scr_height = height; - s->last_width = disp_width; - s->last_height = height; - s->last_line_offset = s->line_offset; - s->last_depth = depth; - s->last_byteswap = byteswap; - full_update = 1; - } - if (surface_data(surface) != s->vram_ptr + (s->start_addr * 4) - && is_buffer_shared(surface)) { - /* base address changed (page flip) -> shared display surfaces - * must be updated with the new base address */ - full_update = 1; - } - - if (full_update) { - if (share_surface) { - surface = qemu_create_displaysurface_from(disp_width, - height, format, s->line_offset, - s->vram_ptr + (s->start_addr * 4)); - dpy_gfx_replace_surface(s->con, surface); - } else { - qemu_console_resize(s->con, disp_width, height); - surface = qemu_console_surface(s->con); - } - } - if (shift_control == 0) { full_update |= update_palette16(s); if (sr(s, VGA_SEQ_CLOCK_MODE) & 8) { + disp_width <<= 1; v = VGA_DRAW_LINE4D2; } else { v = VGA_DRAW_LINE4; } bits = 4; + } else if (shift_control == 1) { full_update |= update_palette16(s); if (sr(s, VGA_SEQ_CLOCK_MODE) & 8) { + disp_width <<= 1; v = VGA_DRAW_LINE2D2; } else { v = VGA_DRAW_LINE2; } bits = 4; + } else { - switch(s->get_bpp(s)) { + switch (depth) { default: case 0: full_update |= update_palette256(s); @@ -1638,6 +1571,90 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) break; } } + + /* Horizontal pel panning bit 3 is only used in text mode. */ + hpel = bits <= 8 ? s->params.hpel & 7 : 0; + + region_start = (s->params.start_addr * 4); + region_end = region_start + (ram_addr_t)s->params.line_offset * height; + region_end += width * depth / 8; /* scanline length */ + region_end -= s->params.line_offset; + if (hpel) { + region_end += 4; + } + if (region_end > s->vbe_size || depth == 0 || depth == 15) { + /* + * We land here on: + * - wraps around (can happen with cirrus vbe modes) + * - depth == 0 (256 color palette video mode) + * - depth == 15 + * + * Take the safe and slow route: + * - create a dirty bitmap snapshot for all vga memory. + * - force shadowing (so all vga memory access goes + * through vga_read_*() helpers). + * + * Given this affects only vga features which are pretty much + * unused by modern guests there should be no performance + * impact. + */ + region_start = 0; + region_end = s->vbe_size; + force_shadow = true; + } + + /* + * Check whether we can share the surface with the backend + * or whether we need a shadow surface. We share native + * endian surfaces for 15bpp and above and byteswapped + * surfaces for 24bpp and above. + */ + format = qemu_default_pixman_format(depth, !byteswap); + if (format) { + share_surface = dpy_gfx_check_format(s->con, format) + && !s->force_shadow && !force_shadow; + } else { + share_surface = false; + } + + if (s->params.line_offset != s->last_line_offset || + disp_width != s->last_width || + height != s->last_height || + s->last_depth != depth || + s->last_byteswap != byteswap || + share_surface != is_buffer_shared(surface)) { + /* display parameters changed -> need new display surface */ + s->last_scr_width = disp_width; + s->last_scr_height = height; + s->last_width = disp_width; + s->last_height = height; + s->last_line_offset = s->params.line_offset; + s->last_depth = depth; + s->last_byteswap = byteswap; + /* 16 extra pixels are needed for double-width planar modes. */ + s->panning_buf = g_realloc(s->panning_buf, + (disp_width + 16) * sizeof(uint32_t)); + full_update = 1; + } + if (surface_data(surface) != s->vram_ptr + (s->params.start_addr * 4) + && is_buffer_shared(surface)) { + /* base address changed (page flip) -> shared display surfaces + * must be updated with the new base address */ + full_update = 1; + } + + if (full_update) { + if (share_surface) { + surface = qemu_create_displaysurface_from(disp_width, + height, format, s->params.line_offset, + s->vram_ptr + (s->params.start_addr * 4)); + dpy_gfx_replace_surface(s->con, surface); + } else { + qemu_console_resize(s->con, disp_width, height); + surface = qemu_console_surface(s->con); + } + } + vga_draw_line = vga_draw_line_table[v]; if (!is_buffer_shared(surface) && s->cursor_invalidate) { @@ -1647,17 +1664,20 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) #if 0 printf("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n", width, height, v, line_offset, s->cr[9], s->cr[VGA_CRTC_MODE], - s->line_compare, sr(s, VGA_SEQ_CLOCK_MODE)); + s->params.line_compare, sr(s, VGA_SEQ_CLOCK_MODE)); #endif - addr1 = (s->start_addr * 4); + addr1 = (s->params.start_addr * 4); bwidth = DIV_ROUND_UP(width * bits, 8); + if (hpel) { + bwidth += 4; + } y_start = -1; d = surface_data(surface); linesize = surface_stride(surface); y1 = 0; if (!full_update) { - if (s->line_compare < height) { + if (s->params.line_compare < height) { /* split screen mode */ region_start = 0; } @@ -1698,7 +1718,11 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) if (y_start < 0) y_start = y; if (!(is_buffer_shared(surface))) { - vga_draw_line(s, d, addr, width); + uint8_t *p; + p = vga_draw_line(s, d, addr, width, hpel); + if (p) { + memcpy(d, p, disp_width * sizeof(uint32_t)); + } if (s->cursor_draw_line) s->cursor_draw_line(s, d, y); } @@ -1713,15 +1737,19 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) if (!multi_run) { mask = (s->cr[VGA_CRTC_MODE] & 3) ^ 3; if ((y1 & mask) == mask) - addr1 += s->line_offset; + addr1 += s->params.line_offset; y1++; multi_run = multi_scan; } else { multi_run--; } /* line compare acts on the displayed lines */ - if (y == s->line_compare) + if (y == s->params.line_compare) { + if (s->params.hpel_split) { + hpel = VGA_HPEL_NEUTRAL; + } addr1 = 0; + } d += linesize; } if (y_start >= 0) { @@ -1837,9 +1865,7 @@ void vga_common_reset(VGACommonState *s) s->graphic_mode = -1; /* force full update */ s->shift_control = 0; s->double_scan = 0; - s->line_offset = 0; - s->line_compare = 0; - s->start_addr = 0; + memset(&s->params, '\0', sizeof(s->params)); s->plane_updated = 0; s->last_cw = 0; s->last_ch = 0; @@ -1961,7 +1987,7 @@ static void vga_update_text(void *opaque, console_ch_t *chardata) /* Update "hardware" cursor */ cursor_offset = ((s->cr[VGA_CRTC_CURSOR_HI] << 8) | - s->cr[VGA_CRTC_CURSOR_LO]) - s->start_addr; + s->cr[VGA_CRTC_CURSOR_LO]) - s->params.start_addr; if (cursor_offset != s->cursor_offset || s->cr[VGA_CRTC_CURSOR_START] != s->cursor_start || s->cr[VGA_CRTC_CURSOR_END] != s->cursor_end || full_update) { @@ -1977,7 +2003,7 @@ static void vga_update_text(void *opaque, console_ch_t *chardata) s->cursor_end = s->cr[VGA_CRTC_CURSOR_END]; } - src = (uint32_t *) s->vram_ptr + s->start_addr; + src = (uint32_t *) s->vram_ptr + s->params.start_addr; dst = chardata; if (full_update) { @@ -2102,7 +2128,7 @@ static const VMStateDescription vmstate_vga_endian = { .version_id = 1, .minimum_version_id = 1, .needed = vga_endian_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(big_endian_fb, VGACommonState), VMSTATE_END_OF_LIST() } @@ -2113,7 +2139,7 @@ const VMStateDescription vmstate_vga_common = { .version_id = 2, .minimum_version_id = 2, .post_load = vga_common_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(latch, VGACommonState), VMSTATE_UINT8(sr_index, VGACommonState), VMSTATE_PARTIAL_BUFFER(sr, VGACommonState, 8), @@ -2145,7 +2171,7 @@ const VMStateDescription vmstate_vga_common = { VMSTATE_UINT32(vbe_bank_mask, VGACommonState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_vga_endian, NULL } @@ -2222,7 +2248,7 @@ bool vga_common_init(VGACommonState *s, Object *obj, Error **errp) xen_register_framebuffer(&s->vram); s->vram_ptr = memory_region_get_ram_ptr(&s->vram); s->get_bpp = vga_get_bpp; - s->get_offsets = vga_get_offsets; + s->get_params = vga_get_params; s->get_resolution = vga_get_resolution; s->hw_ops = &vga_ops; switch (vga_retrace_method) { @@ -2242,11 +2268,8 @@ bool vga_common_init(VGACommonState *s, Object *obj, Error **errp) * into a device attribute set by the machine/platform to remove * all target endian dependencies from this file. */ -#if TARGET_BIG_ENDIAN - s->default_endian_fb = true; -#else - s->default_endian_fb = false; -#endif + s->default_endian_fb = target_words_bigendian(); + vga_dirty_log_start(s); return true; @@ -2261,11 +2284,15 @@ static const MemoryRegionPortio vga_portio_list[] = { PORTIO_END_OF_LIST(), }; -static const MemoryRegionPortio vbe_portio_list[] = { +static const MemoryRegionPortio vbe_portio_list_x86[] = { { 0, 1, 2, .read = vbe_ioport_read_index, .write = vbe_ioport_write_index }, -# ifdef TARGET_I386 { 1, 1, 2, .read = vbe_ioport_read_data, .write = vbe_ioport_write_data }, -# endif + { 2, 1, 2, .read = vbe_ioport_read_data, .write = vbe_ioport_write_data }, + PORTIO_END_OF_LIST(), +}; + +static const MemoryRegionPortio vbe_portio_list_no_x86[] = { + { 0, 1, 2, .read = vbe_ioport_read_index, .write = vbe_ioport_write_index }, { 2, 1, 2, .read = vbe_ioport_read_data, .write = vbe_ioport_write_data }, PORTIO_END_OF_LIST(), }; @@ -2276,9 +2303,19 @@ MemoryRegion *vga_init_io(VGACommonState *s, Object *obj, const MemoryRegionPortio **vbe_ports) { MemoryRegion *vga_mem; + MachineState *ms = MACHINE(qdev_get_machine()); + + /* + * We unfortunately need two VBE lists since non-x86 machines might + * not be able to do 16-bit accesses at unaligned addresses (0x1cf) + */ + if (object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE)) { + *vbe_ports = vbe_portio_list_x86; + } else { + *vbe_ports = vbe_portio_list_no_x86; + } *vga_ports = vga_portio_list; - *vbe_ports = vbe_portio_list; vga_mem = g_malloc(sizeof(*vga_mem)); memory_region_init_io(vga_mem, obj, &vga_mem_ops, s, diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index 305e700014..876a1d3697 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -27,9 +27,9 @@ #include "exec/ioport.h" #include "exec/memory.h" -#include "ui/console.h" #include "hw/display/bochs-vbe.h" +#include "hw/acpi/acpi_aml_interface.h" #define ST01_V_RETRACE 0x08 #define ST01_DISP_ENABLE 0x01 @@ -56,6 +56,14 @@ struct VGACommonState; typedef uint8_t (* vga_retrace_fn)(struct VGACommonState *s); typedef void (* vga_update_retrace_info_fn)(struct VGACommonState *s); +typedef struct VGADisplayParams { + uint32_t line_offset; + uint32_t start_addr; + uint32_t line_compare; + uint8_t hpel; + bool hpel_split; +} VGADisplayParams; + typedef struct VGACommonState { MemoryRegion *legacy_address_space; uint8_t *vram_ptr; @@ -90,10 +98,7 @@ typedef struct VGACommonState { uint8_t palette[768]; int32_t bank_offset; int (*get_bpp)(struct VGACommonState *s); - void (*get_offsets)(struct VGACommonState *s, - uint32_t *pline_offset, - uint32_t *pstart_addr, - uint32_t *pline_compare); + void (*get_params)(struct VGACommonState *s, VGADisplayParams *params); void (*get_resolution)(struct VGACommonState *s, int *pwidth, int *pheight); @@ -108,12 +113,11 @@ typedef struct VGACommonState { /* display refresh support */ QemuConsole *con; uint32_t font_offsets[2]; + uint8_t *panning_buf; int graphic_mode; uint8_t shift_control; uint8_t double_scan; - uint32_t line_offset; - uint32_t line_compare; - uint32_t start_addr; + VGADisplayParams params; uint32_t plane_updated; uint32_t last_line_offset; uint8_t last_cw, last_ch; @@ -195,4 +199,5 @@ void pci_std_vga_mmio_region_init(VGACommonState *s, MemoryRegion *subs, bool qext, bool edid); +void build_vga_aml(AcpiDevAmlIf *adev, Aml *scope); #endif diff --git a/hw/display/vga_regs.h b/hw/display/vga_regs.h index 30a98b8736..40e673f164 100644 --- a/hw/display/vga_regs.h +++ b/hw/display/vga_regs.h @@ -4,9 +4,9 @@ * Copyright 1999 Jeff Garzik * * Copyright history from vga16fb.c: - * Copyright 1999 Ben Pfaff and Petr Vandrovec - * Based on VGA info at http://www.osdever.net/FreeVGA/home.htm - * Based on VESA framebuffer (c) 1998 Gerd Knorr + * Copyright 1999 Ben Pfaff and Petr Vandrovec + * Based on VGA info at http://www.osdever.net/FreeVGA/home.htm + * Based on VESA framebuffer (c) 1998 Gerd Knorr * * 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 @@ -100,7 +100,9 @@ /* VGA CRT controller bit masks */ #define VGA_CR11_LOCK_CR0_CR7 0x80 /* lock writes to CR0 - CR7 */ +#define VGA_CR14_DW 0x40 #define VGA_CR17_H_V_SIGNALS_ENABLED 0x80 +#define VGA_CR17_WORD_BYTE 0x40 /* VGA attribute controller register indices */ #define VGA_ATC_PALETTE0 0x00 @@ -154,6 +156,8 @@ #define VGA_GFX_BIT_MASK 0x08 /* VGA graphics controller bit masks */ +#define VGA_GR05_HOST_ODD_EVEN 0x10 #define VGA_GR06_GRAPHICS_MODE 0x01 +#define VGA_GR06_CHAIN_ODD_EVEN 0x02 #endif /* HW_VGA_REGS_H */ diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 3340ef9e5f..709c8a02a1 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -11,6 +11,8 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/sockets.h" #include "hw/qdev-properties.h" #include "hw/virtio/virtio-gpu.h" #include "chardev/char-fe.h" @@ -29,6 +31,8 @@ typedef enum VhostUserGpuRequest { VHOST_USER_GPU_UPDATE, VHOST_USER_GPU_DMABUF_SCANOUT, VHOST_USER_GPU_DMABUF_UPDATE, + VHOST_USER_GPU_GET_EDID, + VHOST_USER_GPU_DMABUF_SCANOUT2, } VhostUserGpuRequest; typedef struct VhostUserGpuDisplayInfoReply { @@ -76,6 +80,15 @@ typedef struct VhostUserGpuDMABUFScanout { int fd_drm_fourcc; } QEMU_PACKED VhostUserGpuDMABUFScanout; +typedef struct VhostUserGpuDMABUFScanout2 { + struct VhostUserGpuDMABUFScanout dmabuf_scanout; + uint64_t modifier; +} QEMU_PACKED VhostUserGpuDMABUFScanout2; + +typedef struct VhostUserGpuEdidRequest { + uint32_t scanout_id; +} QEMU_PACKED VhostUserGpuEdidRequest; + typedef struct VhostUserGpuMsg { uint32_t request; /* VhostUserGpuRequest */ uint32_t flags; @@ -86,6 +99,9 @@ typedef struct VhostUserGpuMsg { VhostUserGpuScanout scanout; VhostUserGpuUpdate update; VhostUserGpuDMABUFScanout dmabuf_scanout; + VhostUserGpuDMABUFScanout2 dmabuf_scanout2; + VhostUserGpuEdidRequest edid_req; + struct virtio_gpu_resp_edid resp_edid; struct virtio_gpu_resp_display_info display_info; uint64_t u64; } payload; @@ -97,6 +113,9 @@ static VhostUserGpuMsg m __attribute__ ((unused)); #define VHOST_USER_GPU_MSG_FLAG_REPLY 0x4 +#define VHOST_USER_GPU_PROTOCOL_F_EDID 0 +#define VHOST_USER_GPU_PROTOCOL_F_DMABUF2 1 + static void vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked); static void @@ -159,6 +178,10 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) .request = msg->request, .flags = VHOST_USER_GPU_MSG_FLAG_REPLY, .size = sizeof(uint64_t), + .payload = { + .u64 = (1 << VHOST_USER_GPU_PROTOCOL_F_EDID) | + (1 << VHOST_USER_GPU_PROTOCOL_F_DMABUF2) + } }; vhost_user_gpu_send_msg(g, &reply); @@ -182,6 +205,26 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) vhost_user_gpu_send_msg(g, &reply); break; } + case VHOST_USER_GPU_GET_EDID: { + VhostUserGpuEdidRequest *m = &msg->payload.edid_req; + struct virtio_gpu_resp_edid resp = { {} }; + VhostUserGpuMsg reply = { + .request = msg->request, + .flags = VHOST_USER_GPU_MSG_FLAG_REPLY, + .size = sizeof(reply.payload.resp_edid), + }; + + if (m->scanout_id >= g->parent_obj.conf.max_outputs) { + error_report("invalid scanout: %d", m->scanout_id); + break; + } + + resp.hdr.type = VIRTIO_GPU_RESP_OK_EDID; + virtio_gpu_base_generate_edid(VIRTIO_GPU_BASE(g), m->scanout_id, &resp); + memcpy(&reply.payload.resp_edid, &resp, sizeof(resp)); + vhost_user_gpu_send_msg(g, &reply); + break; + } case VHOST_USER_GPU_SCANOUT: { VhostUserGpuScanout *m = &msg->payload.scanout; @@ -202,6 +245,7 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) break; } + case VHOST_USER_GPU_DMABUF_SCANOUT2: case VHOST_USER_GPU_DMABUF_SCANOUT: { VhostUserGpuDMABUFScanout *m = &msg->payload.dmabuf_scanout; int fd = qemu_chr_fe_get_msgfd(&g->vhost_chr); @@ -235,6 +279,11 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) .fourcc = m->fd_drm_fourcc, .y0_top = m->fd_flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP, }; + if (msg->request == VHOST_USER_GPU_DMABUF_SCANOUT2) { + VhostUserGpuDMABUFScanout2 *m2 = &msg->payload.dmabuf_scanout2; + dmabuf->modifier = m2->modifier; + } + dpy_gl_scanout_dmabuf(con, dmabuf); break; } @@ -258,6 +307,7 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) dpy_gl_update(con, m->x, m->y, m->width, m->height); break; } +#ifdef CONFIG_PIXMAN case VHOST_USER_GPU_UPDATE: { VhostUserGpuUpdate *m = &msg->payload.update; @@ -285,6 +335,7 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) } break; } +#endif default: g_warning("unhandled message %d %d", msg->request, msg->size); } @@ -362,11 +413,11 @@ vhost_user_gpu_gl_flushed(VirtIOGPUBase *b) VhostUserGPU *g = VHOST_USER_GPU(b); if (g->backend_blocked) { - vhost_user_gpu_unblock(VHOST_USER_GPU(g)); + vhost_user_gpu_unblock(g); g->backend_blocked = false; } - vhost_user_gpu_update_blocked(VHOST_USER_GPU(g), false); + vhost_user_gpu_update_blocked(g, false); } static bool @@ -375,7 +426,7 @@ vhost_user_gpu_do_set_socket(VhostUserGPU *g, Error **errp) Chardev *chr; int sv[2]; - if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { + if (qemu_socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { error_setg_errno(errp, errno, "socketpair() failed"); return false; } @@ -450,7 +501,7 @@ vhost_user_gpu_set_config(VirtIODevice *vdev, ret = vhost_dev_set_config(&g->vhost->dev, config_data, 0, sizeof(struct virtio_gpu_config), - VHOST_SET_CONFIG_TYPE_MASTER); + VHOST_SET_CONFIG_TYPE_FRONTEND); if (ret) { error_report("vhost-user-gpu: set device config space failed"); return; @@ -485,6 +536,15 @@ vhost_user_gpu_guest_notifier_pending(VirtIODevice *vdev, int idx) { VhostUserGPU *g = VHOST_USER_GPU(vdev); + /* + * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1 + * as the macro of configure interrupt's IDX, If this driver does not + * support, the function will return + */ + + if (idx == VIRTIO_CONFIG_IRQ_IDX) { + return false; + } return vhost_virtqueue_pending(&g->vhost->dev, idx); } @@ -493,6 +553,15 @@ vhost_user_gpu_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) { VhostUserGPU *g = VHOST_USER_GPU(vdev); + /* + * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1 + * as the macro of configure interrupt's IDX, If this driver does not + * support, the function will return + */ + + if (idx == VIRTIO_CONFIG_IRQ_IDX) { + return; + } vhost_virtqueue_mask(&g->vhost->dev, vdev, idx, mask); } diff --git a/hw/display/virtio-dmabuf.c b/hw/display/virtio-dmabuf.c new file mode 100644 index 0000000000..3dba4577ca --- /dev/null +++ b/hw/display/virtio-dmabuf.c @@ -0,0 +1,146 @@ +/* + * Virtio Shared dma-buf + * + * Copyright Red Hat, Inc. 2023 + * + * Authors: + * Albert Esteve + * + * 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/virtio/virtio-dmabuf.h" + + +static GMutex lock; +static GHashTable *resource_uuids; + +/* + * uuid_equal_func: wrapper for UUID is_equal function to + * satisfy g_hash_table_new expected parameters signatures. + */ +static int uuid_equal_func(const void *lhv, const void *rhv) +{ + return qemu_uuid_is_equal(lhv, rhv); +} + +static bool virtio_add_resource(QemuUUID *uuid, VirtioSharedObject *value) +{ + bool result = true; + + g_mutex_lock(&lock); + if (resource_uuids == NULL) { + resource_uuids = g_hash_table_new_full(qemu_uuid_hash, + uuid_equal_func, + NULL, + g_free); + } + if (g_hash_table_lookup(resource_uuids, uuid) == NULL) { + g_hash_table_insert(resource_uuids, uuid, value); + } else { + result = false; + } + g_mutex_unlock(&lock); + + return result; +} + +bool virtio_add_dmabuf(QemuUUID *uuid, int udmabuf_fd) +{ + bool result; + VirtioSharedObject *vso; + if (udmabuf_fd < 0) { + return false; + } + vso = g_new(VirtioSharedObject, 1); + vso->type = TYPE_DMABUF; + vso->value = GINT_TO_POINTER(udmabuf_fd); + result = virtio_add_resource(uuid, vso); + if (!result) { + g_free(vso); + } + + return result; +} + +bool virtio_add_vhost_device(QemuUUID *uuid, struct vhost_dev *dev) +{ + bool result; + VirtioSharedObject *vso; + if (dev == NULL) { + return false; + } + vso = g_new(VirtioSharedObject, 1); + vso->type = TYPE_VHOST_DEV; + vso->value = dev; + result = virtio_add_resource(uuid, vso); + if (!result) { + g_free(vso); + } + + return result; +} + +bool virtio_remove_resource(const QemuUUID *uuid) +{ + bool result; + g_mutex_lock(&lock); + result = g_hash_table_remove(resource_uuids, uuid); + g_mutex_unlock(&lock); + + return result; +} + +static VirtioSharedObject *get_shared_object(const QemuUUID *uuid) +{ + gpointer lookup_res = NULL; + + g_mutex_lock(&lock); + if (resource_uuids != NULL) { + lookup_res = g_hash_table_lookup(resource_uuids, uuid); + } + g_mutex_unlock(&lock); + + return (VirtioSharedObject *) lookup_res; +} + +int virtio_lookup_dmabuf(const QemuUUID *uuid) +{ + VirtioSharedObject *vso = get_shared_object(uuid); + if (vso == NULL) { + return -1; + } + assert(vso->type == TYPE_DMABUF); + return GPOINTER_TO_INT(vso->value); +} + +struct vhost_dev *virtio_lookup_vhost_device(const QemuUUID *uuid) +{ + VirtioSharedObject *vso = get_shared_object(uuid); + if (vso == NULL) { + return NULL; + } + assert(vso->type == TYPE_VHOST_DEV); + return (struct vhost_dev *) vso->value; +} + +SharedObjectType virtio_object_type(const QemuUUID *uuid) +{ + VirtioSharedObject *vso = get_shared_object(uuid); + if (vso == NULL) { + return TYPE_INVALID; + } + return vso->type; +} + +void virtio_free_resources(void) +{ + g_mutex_lock(&lock); + g_hash_table_destroy(resource_uuids); + /* Reference count shall be 0 after the implicit unref on destroy */ + resource_uuids = NULL; + g_mutex_unlock(&lock); +} diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index 790cec333c..4fc7ef8896 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -17,6 +17,7 @@ #include "migration/blocker.h" #include "qapi/error.h" #include "qemu/error-report.h" +#include "hw/display/edid.h" #include "trace.h" void @@ -51,6 +52,22 @@ virtio_gpu_base_fill_display_info(VirtIOGPUBase *g, } } +void +virtio_gpu_base_generate_edid(VirtIOGPUBase *g, int scanout, + struct virtio_gpu_resp_edid *edid) +{ + qemu_edid_info info = { + .width_mm = g->req_state[scanout].width_mm, + .height_mm = g->req_state[scanout].height_mm, + .prefx = g->req_state[scanout].width, + .prefy = g->req_state[scanout].height, + .refresh_rate = g->req_state[scanout].refresh_rate, + }; + + edid->size = cpu_to_le32(sizeof(edid->edid)); + qemu_edid_generate(edid->edid, sizeof(edid->edid), &info); +} + static void virtio_gpu_invalidate_display(void *opaque) { } @@ -69,16 +86,17 @@ static void virtio_gpu_notify_event(VirtIOGPUBase *g, uint32_t event_type) virtio_notify_config(&g->parent_obj); } -static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) +static void virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) { VirtIOGPUBase *g = opaque; if (idx >= g->conf.max_outputs) { - return -1; + return; } g->req_state[idx].x = info->xoff; g->req_state[idx].y = info->yoff; + g->req_state[idx].refresh_rate = info->refresh_rate; g->req_state[idx].width = info->width; g->req_state[idx].height = info->height; g->req_state[idx].width_mm = info->width_mm; @@ -92,7 +110,7 @@ static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) /* send event to guest */ virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY); - return 0; + return; } static void @@ -166,8 +184,7 @@ virtio_gpu_base_device_realize(DeviceState *qdev, if (virtio_gpu_virgl_enabled(g->conf)) { error_setg(&g->migration_blocker, "virgl is not yet migratable"); - if (migrate_add_blocker(g->migration_blocker, errp) < 0) { - error_free(g->migration_blocker); + if (migrate_add_blocker(&g->migration_blocker, errp) < 0) { return false; } } @@ -205,7 +222,8 @@ virtio_gpu_base_get_features(VirtIODevice *vdev, uint64_t features, { VirtIOGPUBase *g = VIRTIO_GPU_BASE(vdev); - if (virtio_gpu_virgl_enabled(g->conf)) { + if (virtio_gpu_virgl_enabled(g->conf) || + virtio_gpu_rutabaga_enabled(g->conf)) { features |= (1 << VIRTIO_GPU_F_VIRGL); } if (virtio_gpu_edid_enabled(g->conf)) { @@ -214,6 +232,9 @@ virtio_gpu_base_get_features(VirtIODevice *vdev, uint64_t features, if (virtio_gpu_blob_enabled(g->conf)) { features |= (1 << VIRTIO_GPU_F_RESOURCE_BLOB); } + if (virtio_gpu_context_init_enabled(g->conf)) { + features |= (1 << VIRTIO_GPU_F_CONTEXT_INIT); + } return features; } @@ -226,15 +247,16 @@ virtio_gpu_base_set_features(VirtIODevice *vdev, uint64_t features) trace_virtio_gpu_features(((features & virgl) == virgl)); } -static void +void virtio_gpu_base_device_unrealize(DeviceState *qdev) { VirtIOGPUBase *g = VIRTIO_GPU_BASE(qdev); + VirtIODevice *vdev = VIRTIO_DEVICE(qdev); - if (g->migration_blocker) { - migrate_del_blocker(g->migration_blocker); - error_free(g->migration_blocker); - } + virtio_del_queue(vdev, 0); + virtio_del_queue(vdev, 1); + virtio_cleanup(vdev); + migrate_del_blocker(&g->migration_blocker); } static void diff --git a/hw/display/virtio-gpu-pci-rutabaga.c b/hw/display/virtio-gpu-pci-rutabaga.c new file mode 100644 index 0000000000..abbb898c65 --- /dev/null +++ b/hw/display/virtio-gpu-pci-rutabaga.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "hw/pci/pci.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/virtio-gpu-pci.h" +#include "qom/object.h" + +#define TYPE_VIRTIO_GPU_RUTABAGA_PCI "virtio-gpu-rutabaga-pci" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOGPURutabagaPCI, VIRTIO_GPU_RUTABAGA_PCI) + +struct VirtIOGPURutabagaPCI { + VirtIOGPUPCIBase parent_obj; + + VirtIOGPURutabaga vdev; +}; + +static void virtio_gpu_rutabaga_initfn(Object *obj) +{ + VirtIOGPURutabagaPCI *dev = VIRTIO_GPU_RUTABAGA_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_GPU_RUTABAGA); + VIRTIO_GPU_PCI_BASE(obj)->vgpu = VIRTIO_GPU_BASE(&dev->vdev); +} + +static const TypeInfo virtio_gpu_rutabaga_pci_info[] = { + { + .name = TYPE_VIRTIO_GPU_RUTABAGA_PCI, + .parent = TYPE_VIRTIO_GPU_PCI_BASE, + .instance_size = sizeof(VirtIOGPURutabagaPCI), + .instance_init = virtio_gpu_rutabaga_initfn, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + } + }, +}; + +DEFINE_TYPES(virtio_gpu_rutabaga_pci_info) + +module_obj(TYPE_VIRTIO_GPU_RUTABAGA_PCI); +module_kconfig(VIRTIO_PCI); +module_dep("hw-display-virtio-gpu-pci"); diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index 93f214ff58..da6a99f038 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -33,6 +33,20 @@ static void virtio_gpu_pci_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp) DeviceState *vdev = DEVICE(g); int i; + if (virtio_gpu_hostmem_enabled(g->conf)) { + vpci_dev->msix_bar_idx = 1; + vpci_dev->modern_mem_bar_idx = 2; + memory_region_init(&g->hostmem, OBJECT(g), "virtio-gpu-hostmem", + g->conf.hostmem); + pci_register_bar(&vpci_dev->pci_dev, 4, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_PREFETCH | + PCI_BASE_ADDRESS_MEM_TYPE_64, + &g->hostmem); + virtio_pci_add_shm_cap(vpci_dev, 4, 0, g->conf.hostmem, + VIRTIO_GPU_SHM_ID_HOST_VISIBLE); + } + virtio_pci_force_virtio_1(vpci_dev); if (!qdev_realize(vdev, BUS(&vpci_dev->bus), errp)) { return; diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c new file mode 100644 index 0000000000..17bf701a21 --- /dev/null +++ b/hw/display/virtio-gpu-rutabaga.c @@ -0,0 +1,1143 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/iov.h" +#include "trace.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-gpu.h" +#include "hw/virtio/virtio-gpu-pixman.h" +#include "hw/virtio/virtio-iommu.h" + +#include +#include + +#define CHECK(condition, cmd) \ + do { \ + if (!(condition)) { \ + error_report("CHECK failed in %s() %s:" "%d", __func__, \ + __FILE__, __LINE__); \ + (cmd)->error = VIRTIO_GPU_RESP_ERR_UNSPEC; \ + return; \ + } \ + } while (0) + +struct rutabaga_aio_data { + struct VirtIOGPURutabaga *vr; + struct rutabaga_fence fence; +}; + +static void +virtio_gpu_rutabaga_update_cursor(VirtIOGPU *g, struct virtio_gpu_scanout *s, + uint32_t resource_id) +{ + struct virtio_gpu_simple_resource *res; + struct rutabaga_transfer transfer = { 0 }; + struct iovec transfer_iovec; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + res = virtio_gpu_find_resource(g, resource_id); + if (!res) { + return; + } + + if (res->width != s->current_cursor->width || + res->height != s->current_cursor->height) { + return; + } + + transfer.x = 0; + transfer.y = 0; + transfer.z = 0; + transfer.w = res->width; + transfer.h = res->height; + transfer.d = 1; + + transfer_iovec.iov_base = s->current_cursor->data; + transfer_iovec.iov_len = res->width * res->height * 4; + + rutabaga_resource_transfer_read(vr->rutabaga, 0, + resource_id, &transfer, + &transfer_iovec); +} + +static void +virtio_gpu_rutabaga_gl_flushed(VirtIOGPUBase *b) +{ + VirtIOGPU *g = VIRTIO_GPU(b); + virtio_gpu_process_cmdq(g); +} + +static void +rutabaga_cmd_create_resource_2d(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result; + struct rutabaga_create_3d rc_3d = { 0 }; + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_create_2d c2d; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(c2d); + trace_virtio_gpu_cmd_res_create_2d(c2d.resource_id, c2d.format, + c2d.width, c2d.height); + + rc_3d.target = 2; + rc_3d.format = c2d.format; + rc_3d.bind = (1 << 1); + rc_3d.width = c2d.width; + rc_3d.height = c2d.height; + rc_3d.depth = 1; + rc_3d.array_size = 1; + rc_3d.last_level = 0; + rc_3d.nr_samples = 0; + rc_3d.flags = VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP; + + result = rutabaga_resource_create_3d(vr->rutabaga, c2d.resource_id, &rc_3d); + CHECK(!result, cmd); + + res = g_new0(struct virtio_gpu_simple_resource, 1); + res->width = c2d.width; + res->height = c2d.height; + res->format = c2d.format; + res->resource_id = c2d.resource_id; + + QTAILQ_INSERT_HEAD(&g->reslist, res, next); +} + +static void +rutabaga_cmd_create_resource_3d(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result; + struct rutabaga_create_3d rc_3d = { 0 }; + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_create_3d c3d; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(c3d); + + trace_virtio_gpu_cmd_res_create_3d(c3d.resource_id, c3d.format, + c3d.width, c3d.height, c3d.depth); + + rc_3d.target = c3d.target; + rc_3d.format = c3d.format; + rc_3d.bind = c3d.bind; + rc_3d.width = c3d.width; + rc_3d.height = c3d.height; + rc_3d.depth = c3d.depth; + rc_3d.array_size = c3d.array_size; + rc_3d.last_level = c3d.last_level; + rc_3d.nr_samples = c3d.nr_samples; + rc_3d.flags = c3d.flags; + + result = rutabaga_resource_create_3d(vr->rutabaga, c3d.resource_id, &rc_3d); + CHECK(!result, cmd); + + res = g_new0(struct virtio_gpu_simple_resource, 1); + res->width = c3d.width; + res->height = c3d.height; + res->format = c3d.format; + res->resource_id = c3d.resource_id; + + QTAILQ_INSERT_HEAD(&g->reslist, res, next); +} + +static void +virtio_gpu_rutabaga_resource_unref(VirtIOGPU *g, + struct virtio_gpu_simple_resource *res, + Error **errp) +{ + int32_t result; + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + result = rutabaga_resource_unref(vr->rutabaga, res->resource_id); + if (result) { + error_setg_errno(errp, + (int)result, + "%s: rutabaga_resource_unref returned %"PRIi32 + " for resource_id = %"PRIu32, __func__, result, + res->resource_id); + } + + if (res->image) { + pixman_image_unref(res->image); + } + + QTAILQ_REMOVE(&g->reslist, res, next); + g_free(res); +} + +static void +rutabaga_cmd_resource_unref(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result = 0; + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_unref unref; + Error *local_err = NULL; + + VIRTIO_GPU_FILL_CMD(unref); + + trace_virtio_gpu_cmd_res_unref(unref.resource_id); + + res = virtio_gpu_find_resource(g, unref.resource_id); + CHECK(res, cmd); + + virtio_gpu_rutabaga_resource_unref(g, res, &local_err); + if (local_err) { + error_report_err(local_err); + /* local_err was freed, do not reuse it. */ + local_err = NULL; + result = 1; + } + CHECK(!result, cmd); +} + +static void +rutabaga_cmd_context_create(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result; + struct virtio_gpu_ctx_create cc; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(cc); + trace_virtio_gpu_cmd_ctx_create(cc.hdr.ctx_id, + cc.debug_name); + + result = rutabaga_context_create(vr->rutabaga, cc.hdr.ctx_id, + cc.context_init, cc.debug_name, cc.nlen); + CHECK(!result, cmd); +} + +static void +rutabaga_cmd_context_destroy(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result; + struct virtio_gpu_ctx_destroy cd; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(cd); + trace_virtio_gpu_cmd_ctx_destroy(cd.hdr.ctx_id); + + result = rutabaga_context_destroy(vr->rutabaga, cd.hdr.ctx_id); + CHECK(!result, cmd); +} + +static void +rutabaga_cmd_resource_flush(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result, i; + struct virtio_gpu_scanout *scanout = NULL; + struct virtio_gpu_simple_resource *res; + struct rutabaga_transfer transfer = { 0 }; + struct iovec transfer_iovec; + struct virtio_gpu_resource_flush rf; + bool found = false; + + VirtIOGPUBase *vb = VIRTIO_GPU_BASE(g); + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + if (vr->headless) { + return; + } + + VIRTIO_GPU_FILL_CMD(rf); + trace_virtio_gpu_cmd_res_flush(rf.resource_id, + rf.r.width, rf.r.height, rf.r.x, rf.r.y); + + res = virtio_gpu_find_resource(g, rf.resource_id); + CHECK(res, cmd); + + for (i = 0; i < vb->conf.max_outputs; i++) { + scanout = &vb->scanout[i]; + if (i == res->scanout_bitmask) { + found = true; + break; + } + } + + if (!found) { + return; + } + + transfer.x = 0; + transfer.y = 0; + transfer.z = 0; + transfer.w = res->width; + transfer.h = res->height; + transfer.d = 1; + + transfer_iovec.iov_base = pixman_image_get_data(res->image); + transfer_iovec.iov_len = res->width * res->height * 4; + + result = rutabaga_resource_transfer_read(vr->rutabaga, 0, + rf.resource_id, &transfer, + &transfer_iovec); + CHECK(!result, cmd); + dpy_gfx_update_full(scanout->con); +} + +static void +rutabaga_cmd_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_scanout *scanout = NULL; + struct virtio_gpu_set_scanout ss; + + VirtIOGPUBase *vb = VIRTIO_GPU_BASE(g); + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + if (vr->headless) { + return; + } + + VIRTIO_GPU_FILL_CMD(ss); + trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id, + ss.r.width, ss.r.height, ss.r.x, ss.r.y); + + CHECK(ss.scanout_id < VIRTIO_GPU_MAX_SCANOUTS, cmd); + scanout = &vb->scanout[ss.scanout_id]; + + if (ss.resource_id == 0) { + dpy_gfx_replace_surface(scanout->con, NULL); + dpy_gl_scanout_disable(scanout->con); + return; + } + + res = virtio_gpu_find_resource(g, ss.resource_id); + CHECK(res, cmd); + + if (!res->image) { + pixman_format_code_t pformat; + pformat = virtio_gpu_get_pixman_format(res->format); + CHECK(pformat, cmd); + + res->image = pixman_image_create_bits(pformat, + res->width, + res->height, + NULL, 0); + CHECK(res->image, cmd); + pixman_image_ref(res->image); + } + + vb->enable = 1; + + /* realloc the surface ptr */ + scanout->ds = qemu_create_displaysurface_pixman(res->image); + dpy_gfx_replace_surface(scanout->con, NULL); + dpy_gfx_replace_surface(scanout->con, scanout->ds); + res->scanout_bitmask = ss.scanout_id; +} + +static void +rutabaga_cmd_submit_3d(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result; + struct virtio_gpu_cmd_submit cs; + struct rutabaga_command rutabaga_cmd = { 0 }; + g_autofree uint8_t *buf = NULL; + size_t s; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(cs); + trace_virtio_gpu_cmd_ctx_submit(cs.hdr.ctx_id, cs.size); + + buf = g_new0(uint8_t, cs.size); + s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, + sizeof(cs), buf, cs.size); + CHECK(s == cs.size, cmd); + + rutabaga_cmd.ctx_id = cs.hdr.ctx_id; + rutabaga_cmd.cmd = buf; + rutabaga_cmd.cmd_size = cs.size; + + result = rutabaga_submit_command(vr->rutabaga, &rutabaga_cmd); + CHECK(!result, cmd); +} + +static void +rutabaga_cmd_transfer_to_host_2d(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result; + struct rutabaga_transfer transfer = { 0 }; + struct virtio_gpu_transfer_to_host_2d t2d; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(t2d); + trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id); + + transfer.x = t2d.r.x; + transfer.y = t2d.r.y; + transfer.z = 0; + transfer.w = t2d.r.width; + transfer.h = t2d.r.height; + transfer.d = 1; + + result = rutabaga_resource_transfer_write(vr->rutabaga, 0, t2d.resource_id, + &transfer); + CHECK(!result, cmd); +} + +static void +rutabaga_cmd_transfer_to_host_3d(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result; + struct rutabaga_transfer transfer = { 0 }; + struct virtio_gpu_transfer_host_3d t3d; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(t3d); + trace_virtio_gpu_cmd_res_xfer_toh_3d(t3d.resource_id); + + transfer.x = t3d.box.x; + transfer.y = t3d.box.y; + transfer.z = t3d.box.z; + transfer.w = t3d.box.w; + transfer.h = t3d.box.h; + transfer.d = t3d.box.d; + transfer.level = t3d.level; + transfer.stride = t3d.stride; + transfer.layer_stride = t3d.layer_stride; + transfer.offset = t3d.offset; + + result = rutabaga_resource_transfer_write(vr->rutabaga, t3d.hdr.ctx_id, + t3d.resource_id, &transfer); + CHECK(!result, cmd); +} + +static void +rutabaga_cmd_transfer_from_host_3d(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result; + struct rutabaga_transfer transfer = { 0 }; + struct virtio_gpu_transfer_host_3d t3d; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(t3d); + trace_virtio_gpu_cmd_res_xfer_fromh_3d(t3d.resource_id); + + transfer.x = t3d.box.x; + transfer.y = t3d.box.y; + transfer.z = t3d.box.z; + transfer.w = t3d.box.w; + transfer.h = t3d.box.h; + transfer.d = t3d.box.d; + transfer.level = t3d.level; + transfer.stride = t3d.stride; + transfer.layer_stride = t3d.layer_stride; + transfer.offset = t3d.offset; + + result = rutabaga_resource_transfer_read(vr->rutabaga, t3d.hdr.ctx_id, + t3d.resource_id, &transfer, NULL); + CHECK(!result, cmd); +} + +static void +rutabaga_cmd_attach_backing(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) +{ + struct rutabaga_iovecs vecs = { 0 }; + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_attach_backing att_rb; + int ret; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(att_rb); + trace_virtio_gpu_cmd_res_back_attach(att_rb.resource_id); + + res = virtio_gpu_find_resource(g, att_rb.resource_id); + CHECK(res, cmd); + CHECK(!res->iov, cmd); + + ret = virtio_gpu_create_mapping_iov(g, att_rb.nr_entries, sizeof(att_rb), + cmd, NULL, &res->iov, &res->iov_cnt); + CHECK(!ret, cmd); + + vecs.iovecs = res->iov; + vecs.num_iovecs = res->iov_cnt; + + ret = rutabaga_resource_attach_backing(vr->rutabaga, att_rb.resource_id, + &vecs); + if (ret != 0) { + virtio_gpu_cleanup_mapping(g, res); + } + + CHECK(!ret, cmd); +} + +static void +rutabaga_cmd_detach_backing(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_detach_backing detach_rb; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(detach_rb); + trace_virtio_gpu_cmd_res_back_detach(detach_rb.resource_id); + + res = virtio_gpu_find_resource(g, detach_rb.resource_id); + CHECK(res, cmd); + + rutabaga_resource_detach_backing(vr->rutabaga, + detach_rb.resource_id); + + virtio_gpu_cleanup_mapping(g, res); +} + +static void +rutabaga_cmd_ctx_attach_resource(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result; + struct virtio_gpu_ctx_resource att_res; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(att_res); + trace_virtio_gpu_cmd_ctx_res_attach(att_res.hdr.ctx_id, + att_res.resource_id); + + result = rutabaga_context_attach_resource(vr->rutabaga, att_res.hdr.ctx_id, + att_res.resource_id); + CHECK(!result, cmd); +} + +static void +rutabaga_cmd_ctx_detach_resource(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result; + struct virtio_gpu_ctx_resource det_res; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(det_res); + trace_virtio_gpu_cmd_ctx_res_detach(det_res.hdr.ctx_id, + det_res.resource_id); + + result = rutabaga_context_detach_resource(vr->rutabaga, det_res.hdr.ctx_id, + det_res.resource_id); + CHECK(!result, cmd); +} + +static void +rutabaga_cmd_get_capset_info(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result; + struct virtio_gpu_get_capset_info info; + struct virtio_gpu_resp_capset_info resp; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(info); + + result = rutabaga_get_capset_info(vr->rutabaga, info.capset_index, + &resp.capset_id, &resp.capset_max_version, + &resp.capset_max_size); + CHECK(!result, cmd); + + resp.hdr.type = VIRTIO_GPU_RESP_OK_CAPSET_INFO; + virtio_gpu_ctrl_response(g, cmd, &resp.hdr, sizeof(resp)); +} + +static void +rutabaga_cmd_get_capset(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result; + struct virtio_gpu_get_capset gc; + struct virtio_gpu_resp_capset *resp; + uint32_t capset_size, capset_version; + uint32_t current_id, i; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(gc); + for (i = 0; i < vr->num_capsets; i++) { + result = rutabaga_get_capset_info(vr->rutabaga, i, + ¤t_id, &capset_version, + &capset_size); + CHECK(!result, cmd); + + if (current_id == gc.capset_id) { + break; + } + } + + CHECK(i < vr->num_capsets, cmd); + + resp = g_malloc0(sizeof(*resp) + capset_size); + resp->hdr.type = VIRTIO_GPU_RESP_OK_CAPSET; + rutabaga_get_capset(vr->rutabaga, gc.capset_id, gc.capset_version, + resp->capset_data, capset_size); + + virtio_gpu_ctrl_response(g, cmd, &resp->hdr, sizeof(*resp) + capset_size); + g_free(resp); +} + +static void +rutabaga_cmd_resource_create_blob(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + int result; + struct rutabaga_iovecs vecs = { 0 }; + g_autofree struct virtio_gpu_simple_resource *res = NULL; + struct virtio_gpu_resource_create_blob cblob; + struct rutabaga_create_blob rc_blob = { 0 }; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(cblob); + trace_virtio_gpu_cmd_res_create_blob(cblob.resource_id, cblob.size); + + CHECK(cblob.resource_id != 0, cmd); + + res = g_new0(struct virtio_gpu_simple_resource, 1); + + res->resource_id = cblob.resource_id; + res->blob_size = cblob.size; + + if (cblob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D) { + result = virtio_gpu_create_mapping_iov(g, cblob.nr_entries, + sizeof(cblob), cmd, &res->addrs, + &res->iov, &res->iov_cnt); + CHECK(!result, cmd); + } + + rc_blob.blob_id = cblob.blob_id; + rc_blob.blob_mem = cblob.blob_mem; + rc_blob.blob_flags = cblob.blob_flags; + rc_blob.size = cblob.size; + + vecs.iovecs = res->iov; + vecs.num_iovecs = res->iov_cnt; + + result = rutabaga_resource_create_blob(vr->rutabaga, cblob.hdr.ctx_id, + cblob.resource_id, &rc_blob, &vecs, + NULL); + + if (result && cblob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D) { + virtio_gpu_cleanup_mapping(g, res); + } + + CHECK(!result, cmd); + + QTAILQ_INSERT_HEAD(&g->reslist, res, next); + res = NULL; +} + +static void +rutabaga_cmd_resource_map_blob(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result; + uint32_t map_info = 0; + uint32_t slot = 0; + struct virtio_gpu_simple_resource *res; + struct rutabaga_mapping mapping = { 0 }; + struct virtio_gpu_resource_map_blob mblob; + struct virtio_gpu_resp_map_info resp = { 0 }; + + VirtIOGPUBase *vb = VIRTIO_GPU_BASE(g); + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(mblob); + + CHECK(mblob.resource_id != 0, cmd); + + res = virtio_gpu_find_resource(g, mblob.resource_id); + CHECK(res, cmd); + + result = rutabaga_resource_map_info(vr->rutabaga, mblob.resource_id, + &map_info); + CHECK(!result, cmd); + + /* + * RUTABAGA_MAP_ACCESS_* flags are not part of the virtio-gpu spec, but do + * exist to potentially allow the hypervisor to restrict write access to + * memory. QEMU does not need to use this functionality at the moment. + */ + resp.map_info = map_info & RUTABAGA_MAP_CACHE_MASK; + + result = rutabaga_resource_map(vr->rutabaga, mblob.resource_id, &mapping); + CHECK(!result, cmd); + + /* + * There is small risk of the MemoryRegion dereferencing the pointer after + * rutabaga unmaps it. Please see discussion here: + * + * https://lists.gnu.org/archive/html/qemu-devel/2023-09/msg05141.html + * + * It is highly unlikely to happen in practice and doesn't affect known + * use cases. However, it should be fixed and is noted here for posterity. + */ + for (slot = 0; slot < MAX_SLOTS; slot++) { + if (vr->memory_regions[slot].used) { + continue; + } + + MemoryRegion *mr = &(vr->memory_regions[slot].mr); + memory_region_init_ram_ptr(mr, OBJECT(vr), "blob", mapping.size, + mapping.ptr); + memory_region_add_subregion(&vb->hostmem, mblob.offset, mr); + vr->memory_regions[slot].resource_id = mblob.resource_id; + vr->memory_regions[slot].used = 1; + break; + } + + if (slot >= MAX_SLOTS) { + result = rutabaga_resource_unmap(vr->rutabaga, mblob.resource_id); + CHECK(!result, cmd); + } + + CHECK(slot < MAX_SLOTS, cmd); + + resp.hdr.type = VIRTIO_GPU_RESP_OK_MAP_INFO; + virtio_gpu_ctrl_response(g, cmd, &resp.hdr, sizeof(resp)); +} + +static void +rutabaga_cmd_resource_unmap_blob(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + int32_t result; + uint32_t slot = 0; + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_unmap_blob ublob; + + VirtIOGPUBase *vb = VIRTIO_GPU_BASE(g); + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(ublob); + + CHECK(ublob.resource_id != 0, cmd); + + res = virtio_gpu_find_resource(g, ublob.resource_id); + CHECK(res, cmd); + + for (slot = 0; slot < MAX_SLOTS; slot++) { + if (vr->memory_regions[slot].resource_id != ublob.resource_id) { + continue; + } + + MemoryRegion *mr = &(vr->memory_regions[slot].mr); + memory_region_del_subregion(&vb->hostmem, mr); + + vr->memory_regions[slot].resource_id = 0; + vr->memory_regions[slot].used = 0; + break; + } + + CHECK(slot < MAX_SLOTS, cmd); + result = rutabaga_resource_unmap(vr->rutabaga, res->resource_id); + CHECK(!result, cmd); +} + +static void +virtio_gpu_rutabaga_process_cmd(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct rutabaga_fence fence = { 0 }; + int32_t result; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr); + + switch (cmd->cmd_hdr.type) { + case VIRTIO_GPU_CMD_CTX_CREATE: + rutabaga_cmd_context_create(g, cmd); + break; + case VIRTIO_GPU_CMD_CTX_DESTROY: + rutabaga_cmd_context_destroy(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: + rutabaga_cmd_create_resource_2d(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_CREATE_3D: + rutabaga_cmd_create_resource_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_SUBMIT_3D: + rutabaga_cmd_submit_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: + rutabaga_cmd_transfer_to_host_2d(g, cmd); + break; + case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D: + rutabaga_cmd_transfer_to_host_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D: + rutabaga_cmd_transfer_from_host_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: + rutabaga_cmd_attach_backing(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: + rutabaga_cmd_detach_backing(g, cmd); + break; + case VIRTIO_GPU_CMD_SET_SCANOUT: + rutabaga_cmd_set_scanout(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_FLUSH: + rutabaga_cmd_resource_flush(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_UNREF: + rutabaga_cmd_resource_unref(g, cmd); + break; + case VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE: + rutabaga_cmd_ctx_attach_resource(g, cmd); + break; + case VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE: + rutabaga_cmd_ctx_detach_resource(g, cmd); + break; + case VIRTIO_GPU_CMD_GET_CAPSET_INFO: + rutabaga_cmd_get_capset_info(g, cmd); + break; + case VIRTIO_GPU_CMD_GET_CAPSET: + rutabaga_cmd_get_capset(g, cmd); + break; + case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: + virtio_gpu_get_display_info(g, cmd); + break; + case VIRTIO_GPU_CMD_GET_EDID: + virtio_gpu_get_edid(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB: + rutabaga_cmd_resource_create_blob(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB: + rutabaga_cmd_resource_map_blob(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB: + rutabaga_cmd_resource_unmap_blob(g, cmd); + break; + default: + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + break; + } + + if (cmd->finished) { + return; + } + if (cmd->error) { + error_report("%s: ctrl 0x%x, error 0x%x", __func__, + cmd->cmd_hdr.type, cmd->error); + virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error); + return; + } + if (!(cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE)) { + virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA); + return; + } + + fence.flags = cmd->cmd_hdr.flags; + fence.ctx_id = cmd->cmd_hdr.ctx_id; + fence.fence_id = cmd->cmd_hdr.fence_id; + fence.ring_idx = cmd->cmd_hdr.ring_idx; + + trace_virtio_gpu_fence_ctrl(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type); + + result = rutabaga_create_fence(vr->rutabaga, &fence); + CHECK(!result, cmd); +} + +static void +virtio_gpu_rutabaga_aio_cb(void *opaque) +{ + struct rutabaga_aio_data *data = opaque; + VirtIOGPU *g = VIRTIO_GPU(data->vr); + struct rutabaga_fence fence_data = data->fence; + struct virtio_gpu_ctrl_command *cmd, *tmp; + + uint32_t signaled_ctx_specific = fence_data.flags & + RUTABAGA_FLAG_INFO_RING_IDX; + + QTAILQ_FOREACH_SAFE(cmd, &g->fenceq, next, tmp) { + /* + * Due to context specific timelines. + */ + uint32_t target_ctx_specific = cmd->cmd_hdr.flags & + RUTABAGA_FLAG_INFO_RING_IDX; + + if (signaled_ctx_specific != target_ctx_specific) { + continue; + } + + if (signaled_ctx_specific && + (cmd->cmd_hdr.ring_idx != fence_data.ring_idx)) { + continue; + } + + if (cmd->cmd_hdr.fence_id > fence_data.fence_id) { + continue; + } + + trace_virtio_gpu_fence_resp(cmd->cmd_hdr.fence_id); + virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA); + QTAILQ_REMOVE(&g->fenceq, cmd, next); + g_free(cmd); + } + + g_free(data); +} + +static void +virtio_gpu_rutabaga_fence_cb(uint64_t user_data, + const struct rutabaga_fence *fence) +{ + struct rutabaga_aio_data *data; + VirtIOGPU *g = (VirtIOGPU *)user_data; + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + /* + * gfxstream and both cross-domain (and even newer versions virglrenderer: + * see VIRGL_RENDERER_ASYNC_FENCE_CB) like to signal fence completion on + * threads ("callback threads") that are different from the thread that + * processes the command queue ("main thread"). + * + * crosvm and other virtio-gpu 1.1 implementations enable callback threads + * via locking. However, on QEMU a deadlock is observed if + * virtio_gpu_ctrl_response_nodata(..) [used in the fence callback] is used + * from a thread that is not the main thread. + * + * The reason is QEMU's internal locking is designed to work with QEMU + * threads (see rcu_register_thread()) and not generic C/C++/Rust threads. + * For now, we can workaround this by scheduling the return of the + * fence descriptors on the main thread. + */ + + data = g_new0(struct rutabaga_aio_data, 1); + data->vr = vr; + data->fence = *fence; + aio_bh_schedule_oneshot(qemu_get_aio_context(), + virtio_gpu_rutabaga_aio_cb, + data); +} + +static void +virtio_gpu_rutabaga_debug_cb(uint64_t user_data, + const struct rutabaga_debug *debug) +{ + switch (debug->debug_type) { + case RUTABAGA_DEBUG_ERROR: + error_report("%s", debug->message); + break; + case RUTABAGA_DEBUG_WARN: + warn_report("%s", debug->message); + break; + case RUTABAGA_DEBUG_INFO: + info_report("%s", debug->message); + break; + default: + error_report("unknown debug type: %u", debug->debug_type); + } +} + +static bool virtio_gpu_rutabaga_init(VirtIOGPU *g, Error **errp) +{ + int result; + struct rutabaga_builder builder = { 0 }; + struct rutabaga_channel channel = { 0 }; + struct rutabaga_channels channels = { 0 }; + + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + vr->rutabaga = NULL; + + builder.wsi = RUTABAGA_WSI_SURFACELESS; + /* + * Currently, if WSI is specified, the only valid strings are "surfaceless" + * or "headless". Surfaceless doesn't create a native window surface, but + * does copy from the render target to the Pixman buffer if a virtio-gpu + * 2D hypercall is issued. Surfacless is the default. + * + * Headless is like surfaceless, but doesn't copy to the Pixman buffer. The + * use case is automated testing environments where there is no need to view + * results. + * + * In the future, more performant virtio-gpu 2D UI integration may be added. + */ + if (vr->wsi) { + if (g_str_equal(vr->wsi, "surfaceless")) { + vr->headless = false; + } else if (g_str_equal(vr->wsi, "headless")) { + vr->headless = true; + } else { + error_setg(errp, "invalid wsi option selected"); + return false; + } + } + + builder.fence_cb = virtio_gpu_rutabaga_fence_cb; + builder.debug_cb = virtio_gpu_rutabaga_debug_cb; + builder.capset_mask = vr->capset_mask; + builder.user_data = (uint64_t)g; + + /* + * If the user doesn't specify the wayland socket path, we try to infer + * the socket via a process similar to the one used by libwayland. + * libwayland does the following: + * + * 1) If $WAYLAND_DISPLAY is set, attempt to connect to + * $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY + * 2) Otherwise, attempt to connect to $XDG_RUNTIME_DIR/wayland-0 + * 3) Otherwise, don't pass a wayland socket to rutabaga. If a guest + * wayland proxy is launched, it will fail to work. + */ + channel.channel_type = RUTABAGA_CHANNEL_TYPE_WAYLAND; + g_autofree gchar *path = NULL; + if (!vr->wayland_socket_path) { + const gchar *runtime_dir = g_get_user_runtime_dir(); + const gchar *display = g_getenv("WAYLAND_DISPLAY"); + if (!display) { + display = "wayland-0"; + } + + if (runtime_dir) { + path = g_build_filename(runtime_dir, display, NULL); + channel.channel_name = path; + } + } else { + channel.channel_name = vr->wayland_socket_path; + } + + if ((builder.capset_mask & (1 << RUTABAGA_CAPSET_CROSS_DOMAIN))) { + if (channel.channel_name) { + channels.channels = &channel; + channels.num_channels = 1; + builder.channels = &channels; + } + } + + result = rutabaga_init(&builder, &vr->rutabaga); + if (result) { + error_setg_errno(errp, -result, "Failed to init rutabaga"); + return false; + } + + return true; +} + +static int virtio_gpu_rutabaga_get_num_capsets(VirtIOGPU *g) +{ + int result; + uint32_t num_capsets; + VirtIOGPURutabaga *vr = VIRTIO_GPU_RUTABAGA(g); + + result = rutabaga_get_num_capsets(vr->rutabaga, &num_capsets); + if (result) { + error_report("Failed to get capsets"); + return 0; + } + vr->num_capsets = num_capsets; + return num_capsets; +} + +static void virtio_gpu_rutabaga_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOGPU *g = VIRTIO_GPU(vdev); + struct virtio_gpu_ctrl_command *cmd; + + if (!virtio_queue_ready(vq)) { + return; + } + + cmd = virtqueue_pop(vq, sizeof(struct virtio_gpu_ctrl_command)); + while (cmd) { + cmd->vq = vq; + cmd->error = 0; + cmd->finished = false; + QTAILQ_INSERT_TAIL(&g->cmdq, cmd, next); + cmd = virtqueue_pop(vq, sizeof(struct virtio_gpu_ctrl_command)); + } + + virtio_gpu_process_cmdq(g); +} + +static void virtio_gpu_rutabaga_realize(DeviceState *qdev, Error **errp) +{ + int num_capsets; + VirtIOGPUBase *bdev = VIRTIO_GPU_BASE(qdev); + VirtIOGPU *gpudev = VIRTIO_GPU(qdev); + +#if HOST_BIG_ENDIAN + error_setg(errp, "rutabaga is not supported on bigendian platforms"); + return; +#endif + + if (!virtio_gpu_rutabaga_init(gpudev, errp)) { + return; + } + + num_capsets = virtio_gpu_rutabaga_get_num_capsets(gpudev); + if (!num_capsets) { + return; + } + + bdev->conf.flags |= (1 << VIRTIO_GPU_FLAG_RUTABAGA_ENABLED); + bdev->conf.flags |= (1 << VIRTIO_GPU_FLAG_BLOB_ENABLED); + bdev->conf.flags |= (1 << VIRTIO_GPU_FLAG_CONTEXT_INIT_ENABLED); + + bdev->virtio_config.num_capsets = num_capsets; + virtio_gpu_device_realize(qdev, errp); +} + +static Property virtio_gpu_rutabaga_properties[] = { + DEFINE_PROP_BIT64("gfxstream-vulkan", VirtIOGPURutabaga, capset_mask, + RUTABAGA_CAPSET_GFXSTREAM_VULKAN, false), + DEFINE_PROP_BIT64("cross-domain", VirtIOGPURutabaga, capset_mask, + RUTABAGA_CAPSET_CROSS_DOMAIN, false), + DEFINE_PROP_BIT64("x-gfxstream-gles", VirtIOGPURutabaga, capset_mask, + RUTABAGA_CAPSET_GFXSTREAM_GLES, false), + DEFINE_PROP_BIT64("x-gfxstream-composer", VirtIOGPURutabaga, capset_mask, + RUTABAGA_CAPSET_GFXSTREAM_COMPOSER, false), + DEFINE_PROP_STRING("wayland-socket-path", VirtIOGPURutabaga, + wayland_socket_path), + DEFINE_PROP_STRING("wsi", VirtIOGPURutabaga, wsi), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_gpu_rutabaga_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + VirtIOGPUBaseClass *vbc = VIRTIO_GPU_BASE_CLASS(klass); + VirtIOGPUClass *vgc = VIRTIO_GPU_CLASS(klass); + + vbc->gl_flushed = virtio_gpu_rutabaga_gl_flushed; + vgc->handle_ctrl = virtio_gpu_rutabaga_handle_ctrl; + vgc->process_cmd = virtio_gpu_rutabaga_process_cmd; + vgc->update_cursor_data = virtio_gpu_rutabaga_update_cursor; + vgc->resource_destroy = virtio_gpu_rutabaga_resource_unref; + vdc->realize = virtio_gpu_rutabaga_realize; + device_class_set_props(dc, virtio_gpu_rutabaga_properties); +} + +static const TypeInfo virtio_gpu_rutabaga_info[] = { + { + .name = TYPE_VIRTIO_GPU_RUTABAGA, + .parent = TYPE_VIRTIO_GPU, + .instance_size = sizeof(VirtIOGPURutabaga), + .class_init = virtio_gpu_rutabaga_class_init, + }, +}; + +DEFINE_TYPES(virtio_gpu_rutabaga_info) + +module_obj(TYPE_VIRTIO_GPU_RUTABAGA); +module_kconfig(VIRTIO_GPU); +module_dep("hw-display-virtio-gpu"); diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index 8bdf4bac6e..d51184d658 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu/units.h" #include "qemu/iov.h" #include "ui/console.h" @@ -21,7 +22,6 @@ #include "exec/ramblock.h" #include "sysemu/hostmem.h" #include -#include #include #include "qemu/memfd.h" #include "standard-headers/linux/udmabuf.h" @@ -132,7 +132,8 @@ void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) void *pdata = NULL; res->dmabuf_fd = -1; - if (res->iov_cnt == 1) { + if (res->iov_cnt == 1 && + res->iov[0].iov_len < 4096) { pdata = res->iov[0].iov_base; } else { virtio_gpu_create_udmabuf(res); @@ -180,13 +181,13 @@ static VGPUDMABuf } dmabuf = g_new0(VGPUDMABuf, 1); - dmabuf->buf.width = fb->width; - dmabuf->buf.height = fb->height; + dmabuf->buf.width = r->width; + dmabuf->buf.height = r->height; dmabuf->buf.stride = fb->stride; dmabuf->buf.x = r->x; dmabuf->buf.y = r->y; - dmabuf->buf.scanout_width = r->width; - dmabuf->buf.scanout_height = r->height; + dmabuf->buf.backing_width = fb->width; + dmabuf->buf.backing_height = fb->height; dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format); dmabuf->buf.fd = res->dmabuf_fd; dmabuf->buf.allow_fences = true; @@ -217,8 +218,8 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g, g->dmabuf.primary[scanout_id] = new_primary; qemu_console_resize(scanout->con, - new_primary->buf.scanout_width, - new_primary->buf.scanout_height); + new_primary->buf.width, + new_primary->buf.height); dpy_gl_scanout_dmabuf(scanout->con, &new_primary->buf); if (old_primary) { diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c index 73cb92c8d5..9f34d0e661 100644 --- a/hw/display/virtio-gpu-virgl.c +++ b/hw/display/virtio-gpu-virgl.c @@ -12,14 +12,23 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu/iov.h" #include "trace.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-gpu.h" +#include "ui/egl-helpers.h" + #include -static struct virgl_renderer_callbacks virtio_gpu_3d_cbs; +#if VIRGL_RENDERER_CALLBACKS_VERSION >= 4 +static void * +virgl_get_egl_display(G_GNUC_UNUSED void *cookie) +{ + return qemu_egl_display; +} +#endif static void virgl_cmd_create_resource_2d(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) @@ -144,7 +153,6 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { struct virtio_gpu_set_scanout ss; - struct virgl_renderer_resource_info info; int ret; VIRTIO_GPU_FILL_CMD(ss); @@ -159,11 +167,21 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, } g->parent_obj.enable = 1; - memset(&info, 0, sizeof(info)); - if (ss.resource_id && ss.r.width && ss.r.height) { + struct virgl_renderer_resource_info info; + void *d3d_tex2d = NULL; + +#ifdef HAVE_VIRGL_D3D_INFO_EXT + struct virgl_renderer_resource_info_ext ext; + memset(&ext, 0, sizeof(ext)); + ret = virgl_renderer_resource_get_info_ext(ss.resource_id, &ext); + info = ext.base; + d3d_tex2d = ext.d3d_tex2d; +#else + memset(&info, 0, sizeof(info)); ret = virgl_renderer_resource_get_info(ss.resource_id, &info); - if (ret == -1) { +#endif + if (ret) { qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n", __func__, ss.resource_id); @@ -177,7 +195,8 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, g->parent_obj.scanout[ss.scanout_id].con, info.tex_id, info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP, info.width, info.height, - ss.r.x, ss.r.y, ss.r.width, ss.r.height); + ss.r.x, ss.r.y, ss.r.width, ss.r.height, + d3d_tex2d); } else { dpy_gfx_replace_surface( g->parent_obj.scanout[ss.scanout_id].con, NULL); @@ -606,8 +625,21 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g) int virtio_gpu_virgl_init(VirtIOGPU *g) { int ret; + uint32_t flags = 0; - ret = virgl_renderer_init(g, 0, &virtio_gpu_3d_cbs); +#if VIRGL_RENDERER_CALLBACKS_VERSION >= 4 + if (qemu_egl_display) { + virtio_gpu_3d_cbs.version = 4; + virtio_gpu_3d_cbs.get_egl_display = virgl_get_egl_display; + } +#endif +#ifdef VIRGL_RENDERER_D3D11_SHARE_TEXTURE + if (qemu_egl_angle_d3d) { + flags |= VIRGL_RENDERER_D3D11_SHARE_TEXTURE; + } +#endif + + ret = virgl_renderer_init(g, flags, &virtio_gpu_3d_cbs); if (ret != 0) { error_report("virgl could not be initialized: %d", ret); return ret; diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index cd4a56056f..ae831b6b3e 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -14,7 +14,9 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/iov.h" +#include "sysemu/cpus.h" #include "ui/console.h" +#include "ui/rect.h" #include "trace.h" #include "sysemu/dma.h" #include "sysemu/sysemu.h" @@ -24,7 +26,6 @@ #include "hw/virtio/virtio-gpu-bswap.h" #include "hw/virtio/virtio-gpu-pixman.h" #include "hw/virtio/virtio-bus.h" -#include "hw/display/edid.h" #include "hw/qdev-properties.h" #include "qemu/log.h" #include "qemu/module.h" @@ -33,15 +34,12 @@ #define VIRTIO_GPU_VM_VERSION 1 -static struct virtio_gpu_simple_resource* -virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id); static struct virtio_gpu_simple_resource * virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t resource_id, bool require_backing, const char *caller, uint32_t *error); -static void virtio_gpu_cleanup_mapping(VirtIOGPU *g, - struct virtio_gpu_simple_resource *res); +static void virtio_gpu_reset_bh(void *opaque); void virtio_gpu_update_cursor_data(VirtIOGPU *g, struct virtio_gpu_scanout *s, @@ -115,7 +113,7 @@ static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor) cursor->resource_id ? 1 : 0); } -static struct virtio_gpu_simple_resource * +struct virtio_gpu_simple_resource * virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id) { struct virtio_gpu_simple_resource *res; @@ -207,22 +205,6 @@ void virtio_gpu_get_display_info(VirtIOGPU *g, sizeof(display_info)); } -static void -virtio_gpu_generate_edid(VirtIOGPU *g, int scanout, - struct virtio_gpu_resp_edid *edid) -{ - VirtIOGPUBase *b = VIRTIO_GPU_BASE(g); - qemu_edid_info info = { - .width_mm = b->req_state[scanout].width_mm, - .height_mm = b->req_state[scanout].height_mm, - .prefx = b->req_state[scanout].width, - .prefy = b->req_state[scanout].height, - }; - - edid->size = cpu_to_le32(sizeof(edid->edid)); - qemu_edid_generate(edid->edid, sizeof(edid->edid), &info); -} - void virtio_gpu_get_edid(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { @@ -241,7 +223,7 @@ void virtio_gpu_get_edid(VirtIOGPU *g, trace_virtio_gpu_cmd_get_edid(get_edid.scanout); memset(&edid, 0, sizeof(edid)); edid.hdr.type = VIRTIO_GPU_RESP_OK_EDID; - virtio_gpu_generate_edid(g, get_edid.scanout, &edid); + virtio_gpu_base_generate_edid(VIRTIO_GPU_BASE(g), get_edid.scanout, &edid); virtio_gpu_ctrl_response(g, cmd, &edid.hdr, sizeof(edid)); } @@ -257,6 +239,16 @@ static uint32_t calc_image_hostmem(pixman_format_code_t pformat, return height * stride; } +#ifdef WIN32 +static void +win32_pixman_image_destroy(pixman_image_t *image, void *data) +{ + HANDLE handle = data; + + qemu_win32_map_free(pixman_image_get_data(image), handle, &error_warn); +} +#endif + static void virtio_gpu_resource_create_2d(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { @@ -303,12 +295,28 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, res->hostmem = calc_image_hostmem(pformat, c2d.width, c2d.height); if (res->hostmem + g->hostmem < g->conf_max_hostmem) { - res->image = pixman_image_create_bits(pformat, - c2d.width, - c2d.height, - NULL, 0); + void *bits = NULL; +#ifdef WIN32 + bits = qemu_win32_map_alloc(res->hostmem, &res->handle, &error_warn); + if (!bits) { + goto end; + } +#endif + res->image = pixman_image_create_bits( + pformat, + c2d.width, + c2d.height, + bits, c2d.height ? res->hostmem / c2d.height : 0); +#ifdef WIN32 + if (res->image) { + pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle); + } +#endif } +#ifdef WIN32 +end: +#endif if (!res->image) { qemu_log_mask(LOG_GUEST_ERROR, "%s: resource creation failed %d %d %d\n", @@ -394,7 +402,8 @@ static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id) } static void virtio_gpu_resource_destroy(VirtIOGPU *g, - struct virtio_gpu_simple_resource *res) + struct virtio_gpu_simple_resource *res, + Error **errp) { int i; @@ -430,18 +439,22 @@ static void virtio_gpu_resource_unref(VirtIOGPU *g, cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; return; } - virtio_gpu_resource_destroy(g, res); + /* + * virtio_gpu_resource_destroy does not set any errors, so pass a NULL errp + * to ignore them. + */ + virtio_gpu_resource_destroy(g, res, NULL); } static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { struct virtio_gpu_simple_resource *res; - int h; + int h, bpp; uint32_t src_offset, dst_offset, stride; - int bpp; pixman_format_code_t format; struct virtio_gpu_transfer_to_host_2d t2d; + void *img_data; VIRTIO_GPU_FILL_CMD(t2d); virtio_gpu_t2d_bswap(&t2d); @@ -470,23 +483,23 @@ static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g, format = pixman_image_get_format(res->image); bpp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8); stride = pixman_image_get_stride(res->image); + img_data = pixman_image_get_data(res->image); - if (t2d.offset || t2d.r.x || t2d.r.y || - t2d.r.width != pixman_image_get_width(res->image)) { - void *img_data = pixman_image_get_data(res->image); + if (t2d.r.x || t2d.r.width != pixman_image_get_width(res->image)) { for (h = 0; h < t2d.r.height; h++) { src_offset = t2d.offset + stride * h; dst_offset = (t2d.r.y + h) * stride + (t2d.r.x * bpp); iov_to_buf(res->iov, res->iov_cnt, src_offset, - (uint8_t *)img_data - + dst_offset, t2d.r.width * bpp); + (uint8_t *)img_data + dst_offset, + t2d.r.width * bpp); } } else { - iov_to_buf(res->iov, res->iov_cnt, 0, - pixman_image_get_data(res->image), - pixman_image_get_stride(res->image) - * pixman_image_get_height(res->image)); + src_offset = t2d.offset; + dst_offset = t2d.r.y * stride + t2d.r.x * bpp; + iov_to_buf(res->iov, res->iov_cnt, src_offset, + (uint8_t *)img_data + dst_offset, + stride * t2d.r.height); } } @@ -496,7 +509,9 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, struct virtio_gpu_simple_resource *res; struct virtio_gpu_resource_flush rf; struct virtio_gpu_scanout *scanout; - pixman_region16_t flush_region; + QemuRect flush_rect; + bool within_bounds = false; + bool update_submitted = false; int i; VIRTIO_GPU_FILL_CMD(rf); @@ -514,12 +529,31 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { scanout = &g->parent_obj.scanout[i]; if (scanout->resource_id == res->resource_id && - console_has_gl(scanout->con)) { - dpy_gl_update(scanout->con, 0, 0, scanout->width, - scanout->height); + rf.r.x < scanout->x + scanout->width && + rf.r.x + rf.r.width >= scanout->x && + rf.r.y < scanout->y + scanout->height && + rf.r.y + rf.r.height >= scanout->y) { + within_bounds = true; + + if (console_has_gl(scanout->con)) { + dpy_gl_update(scanout->con, 0, 0, scanout->width, + scanout->height); + update_submitted = true; + } } } - return; + + if (update_submitted) { + return; + } + if (!within_bounds) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: flush bounds outside scanouts" + " bounds for flush %d: %d %d %d %d\n", + __func__, rf.resource_id, rf.r.x, rf.r.y, + rf.r.width, rf.r.height); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } } if (!res->blob && @@ -537,34 +571,25 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, return; } - pixman_region_init_rect(&flush_region, - rf.r.x, rf.r.y, rf.r.width, rf.r.height); + qemu_rect_init(&flush_rect, rf.r.x, rf.r.y, rf.r.width, rf.r.height); for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { - pixman_region16_t region, finalregion; - pixman_box16_t *extents; + QemuRect rect; if (!(res->scanout_bitmask & (1 << i))) { continue; } scanout = &g->parent_obj.scanout[i]; - pixman_region_init(&finalregion); - pixman_region_init_rect(®ion, scanout->x, scanout->y, - scanout->width, scanout->height); + qemu_rect_init(&rect, scanout->x, scanout->y, + scanout->width, scanout->height); - pixman_region_intersect(&finalregion, &flush_region, ®ion); - pixman_region_translate(&finalregion, -scanout->x, -scanout->y); - extents = pixman_region_extents(&finalregion); /* work out the area we need to update for each console */ - dpy_gfx_update(g->parent_obj.scanout[i].con, - extents->x1, extents->y1, - extents->x2 - extents->x1, - extents->y2 - extents->y1); - - pixman_region_fini(®ion); - pixman_region_fini(&finalregion); + if (qemu_rect_intersect(&flush_rect, &rect, &rect)) { + qemu_rect_translate(&rect, -scanout->x, -scanout->y); + dpy_gfx_update(g->parent_obj.scanout[i].con, + rect.x, rect.y, rect.width, rect.height); + } } - pixman_region_fini(&flush_region); } static void virtio_unref_resource(pixman_image_t *image, void *data) @@ -575,6 +600,7 @@ static void virtio_unref_resource(pixman_image_t *image, void *data) static void virtio_gpu_update_scanout(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_simple_resource *res, + struct virtio_gpu_framebuffer *fb, struct virtio_gpu_rect *r) { struct virtio_gpu_simple_resource *ores; @@ -592,9 +618,10 @@ static void virtio_gpu_update_scanout(VirtIOGPU *g, scanout->y = r->y; scanout->width = r->width; scanout->height = r->height; + scanout->fb = *fb; } -static void virtio_gpu_do_set_scanout(VirtIOGPU *g, +static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_framebuffer *fb, struct virtio_gpu_simple_resource *res, @@ -620,7 +647,7 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g, r->x, r->y, r->width, r->height, fb->width, fb->height); *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; - return; + return false; } g->parent_obj.enable = 1; @@ -628,9 +655,12 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g, if (res->blob) { if (console_has_gl(scanout->con)) { if (!virtio_gpu_update_dmabuf(g, scanout_id, res, fb, r)) { - virtio_gpu_update_scanout(g, scanout_id, res, r); - return; + virtio_gpu_update_scanout(g, scanout_id, res, fb, r); + } else { + *error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return false; } + return true; } data = res->blob; @@ -657,17 +687,17 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g, /* realloc the surface ptr */ scanout->ds = qemu_create_displaysurface_pixman(rect); - if (!scanout->ds) { - *error = VIRTIO_GPU_RESP_ERR_UNSPEC; - return; - } +#ifdef WIN32 + qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, fb->offset); +#endif pixman_image_unref(rect); dpy_gfx_replace_surface(g->parent_obj.scanout[scanout_id].con, scanout->ds); } - virtio_gpu_update_scanout(g, scanout_id, res, r); + virtio_gpu_update_scanout(g, scanout_id, res, fb, r); + return true; } static void virtio_gpu_set_scanout(VirtIOGPU *g, @@ -867,8 +897,8 @@ void virtio_gpu_cleanup_mapping_iov(VirtIOGPU *g, g_free(iov); } -static void virtio_gpu_cleanup_mapping(VirtIOGPU *g, - struct virtio_gpu_simple_resource *res) +void virtio_gpu_cleanup_mapping(VirtIOGPU *g, + struct virtio_gpu_simple_resource *res) { virtio_gpu_cleanup_mapping_iov(g, res->iov, res->iov_cnt); res->iov = NULL; @@ -1095,7 +1125,7 @@ static void virtio_gpu_ctrl_bh(void *opaque) VirtIOGPU *g = opaque; VirtIOGPUClass *vgc = VIRTIO_GPU_GET_CLASS(g); - vgc->handle_ctrl(&g->parent_obj.parent_obj, g->ctrl_vq); + vgc->handle_ctrl(VIRTIO_DEVICE(g), g->ctrl_vq); } static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq) @@ -1138,8 +1168,9 @@ static void virtio_gpu_cursor_bh(void *opaque) static const VMStateDescription vmstate_virtio_gpu_scanout = { .name = "virtio-gpu-one-scanout", - .version_id = 1, - .fields = (VMStateField[]) { + .version_id = 2, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { VMSTATE_UINT32(resource_id, struct virtio_gpu_scanout), VMSTATE_UINT32(width, struct virtio_gpu_scanout), VMSTATE_UINT32(height, struct virtio_gpu_scanout), @@ -1150,6 +1181,12 @@ static const VMStateDescription vmstate_virtio_gpu_scanout = { VMSTATE_UINT32(cursor.hot_y, struct virtio_gpu_scanout), VMSTATE_UINT32(cursor.pos.x, struct virtio_gpu_scanout), VMSTATE_UINT32(cursor.pos.y, struct virtio_gpu_scanout), + VMSTATE_UINT32_V(fb.format, struct virtio_gpu_scanout, 2), + VMSTATE_UINT32_V(fb.bytes_pp, struct virtio_gpu_scanout, 2), + VMSTATE_UINT32_V(fb.width, struct virtio_gpu_scanout, 2), + VMSTATE_UINT32_V(fb.height, struct virtio_gpu_scanout, 2), + VMSTATE_UINT32_V(fb.stride, struct virtio_gpu_scanout, 2), + VMSTATE_UINT32_V(fb.offset, struct virtio_gpu_scanout, 2), VMSTATE_END_OF_LIST() }, }; @@ -1157,7 +1194,7 @@ static const VMStateDescription vmstate_virtio_gpu_scanout = { static const VMStateDescription vmstate_virtio_gpu_scanouts = { .name = "virtio-gpu-scanouts", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(parent_obj.enable, struct VirtIOGPU), VMSTATE_UINT32_EQUAL(parent_obj.conf.max_outputs, struct VirtIOGPU, NULL), @@ -1180,6 +1217,9 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size, assert(QTAILQ_EMPTY(&g->cmdq)); QTAILQ_FOREACH(res, &g->reslist, next) { + if (res->blob_size) { + continue; + } qemu_put_be32(f, res->resource_id); qemu_put_be32(f, res->width); qemu_put_be32(f, res->height); @@ -1197,13 +1237,42 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size, return vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL); } +static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g, + struct virtio_gpu_simple_resource *res) +{ + int i; + + for (i = 0; i < res->iov_cnt; i++) { + hwaddr len = res->iov[i].iov_len; + res->iov[i].iov_base = + dma_memory_map(VIRTIO_DEVICE(g)->dma_as, res->addrs[i], &len, + DMA_DIRECTION_TO_DEVICE, MEMTXATTRS_UNSPECIFIED); + + if (!res->iov[i].iov_base || len != res->iov[i].iov_len) { + /* Clean up the half-a-mapping we just created... */ + if (res->iov[i].iov_base) { + dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as, res->iov[i].iov_base, + len, DMA_DIRECTION_TO_DEVICE, 0); + } + /* ...and the mappings for previous loop iterations */ + res->iov_cnt = i; + virtio_gpu_cleanup_mapping(g, res); + return false; + } + } + + QTAILQ_INSERT_HEAD(&g->reslist, res, next); + g->hostmem += res->hostmem; + return true; +} + static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, const VMStateField *field) { VirtIOGPU *g = opaque; struct virtio_gpu_simple_resource *res; - struct virtio_gpu_scanout *scanout; uint32_t resource_id, pformat; + void *bits = NULL; int i; g->hostmem = 0; @@ -1228,15 +1297,26 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, g_free(res); return -EINVAL; } - res->image = pixman_image_create_bits(pformat, - res->width, res->height, - NULL, 0); + + res->hostmem = calc_image_hostmem(pformat, res->width, res->height); +#ifdef WIN32 + bits = qemu_win32_map_alloc(res->hostmem, &res->handle, &error_warn); + if (!bits) { + g_free(res); + return -EINVAL; + } +#endif + res->image = pixman_image_create_bits( + pformat, + res->width, res->height, + bits, res->height ? res->hostmem / res->height : 0); if (!res->image) { g_free(res); return -EINVAL; } - - res->hostmem = calc_image_hostmem(pformat, res->width, res->height); +#ifdef WIN32 + pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle); +#endif res->addrs = g_new(uint64_t, res->iov_cnt); res->iov = g_new(struct iovec, res->iov_cnt); @@ -1249,55 +1329,132 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, qemu_get_buffer(f, (void *)pixman_image_get_data(res->image), pixman_image_get_stride(res->image) * res->height); - /* restore mapping */ - for (i = 0; i < res->iov_cnt; i++) { - hwaddr len = res->iov[i].iov_len; - res->iov[i].iov_base = - dma_memory_map(VIRTIO_DEVICE(g)->dma_as, res->addrs[i], &len, - DMA_DIRECTION_TO_DEVICE, - MEMTXATTRS_UNSPECIFIED); - - if (!res->iov[i].iov_base || len != res->iov[i].iov_len) { - /* Clean up the half-a-mapping we just created... */ - if (res->iov[i].iov_base) { - dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as, - res->iov[i].iov_base, - len, - DMA_DIRECTION_TO_DEVICE, - 0); - } - /* ...and the mappings for previous loop iterations */ - res->iov_cnt = i; - virtio_gpu_cleanup_mapping(g, res); - pixman_image_unref(res->image); - g_free(res); - return -EINVAL; - } + if (!virtio_gpu_load_restore_mapping(g, res)) { + pixman_image_unref(res->image); + g_free(res); + return -EINVAL; } - QTAILQ_INSERT_HEAD(&g->reslist, res, next); - g->hostmem += res->hostmem; - resource_id = qemu_get_be32(f); } /* load & apply scanout state */ vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1); + + return 0; +} + +static int virtio_gpu_blob_save(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + VirtIOGPU *g = opaque; + struct virtio_gpu_simple_resource *res; + int i; + + /* in 2d mode we should never find unprocessed commands here */ + assert(QTAILQ_EMPTY(&g->cmdq)); + + QTAILQ_FOREACH(res, &g->reslist, next) { + if (!res->blob_size) { + continue; + } + assert(!res->image); + qemu_put_be32(f, res->resource_id); + qemu_put_be32(f, res->blob_size); + qemu_put_be32(f, res->iov_cnt); + for (i = 0; i < res->iov_cnt; i++) { + qemu_put_be64(f, res->addrs[i]); + qemu_put_be32(f, res->iov[i].iov_len); + } + } + qemu_put_be32(f, 0); /* end of list */ + + return 0; +} + +static int virtio_gpu_blob_load(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + VirtIOGPU *g = opaque; + struct virtio_gpu_simple_resource *res; + uint32_t resource_id; + int i; + + resource_id = qemu_get_be32(f); + while (resource_id != 0) { + res = virtio_gpu_find_resource(g, resource_id); + if (res) { + return -EINVAL; + } + + res = g_new0(struct virtio_gpu_simple_resource, 1); + res->resource_id = resource_id; + res->blob_size = qemu_get_be32(f); + res->iov_cnt = qemu_get_be32(f); + res->addrs = g_new(uint64_t, res->iov_cnt); + res->iov = g_new(struct iovec, res->iov_cnt); + + /* read data */ + for (i = 0; i < res->iov_cnt; i++) { + res->addrs[i] = qemu_get_be64(f); + res->iov[i].iov_len = qemu_get_be32(f); + } + + if (!virtio_gpu_load_restore_mapping(g, res)) { + g_free(res); + return -EINVAL; + } + + virtio_gpu_init_udmabuf(res); + + resource_id = qemu_get_be32(f); + } + + return 0; +} + +static int virtio_gpu_post_load(void *opaque, int version_id) +{ + VirtIOGPU *g = opaque; + struct virtio_gpu_scanout *scanout; + struct virtio_gpu_simple_resource *res; + int i; + for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { scanout = &g->parent_obj.scanout[i]; if (!scanout->resource_id) { continue; } + res = virtio_gpu_find_resource(g, scanout->resource_id); if (!res) { return -EINVAL; } - scanout->ds = qemu_create_displaysurface_pixman(res->image); - if (!scanout->ds) { - return -EINVAL; + + if (scanout->fb.format != 0) { + uint32_t error = 0; + struct virtio_gpu_rect r = { + .x = scanout->x, + .y = scanout->y, + .width = scanout->width, + .height = scanout->height + }; + + if (!virtio_gpu_do_set_scanout(g, i, &scanout->fb, res, &r, &error)) { + return -EINVAL; + } + } else { + /* legacy v1 migration support */ + if (!res->image) { + return -EINVAL; + } + scanout->ds = qemu_create_displaysurface_pixman(res->image); +#ifdef WIN32 + qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0); +#endif + dpy_gfx_replace_surface(scanout->con, scanout->ds); } - dpy_gfx_replace_surface(scanout->con, scanout->ds); dpy_gfx_update_full(scanout->con); if (scanout->cursor.resource_id) { update_cursor(g, &scanout->cursor); @@ -1314,8 +1471,9 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) VirtIOGPU *g = VIRTIO_GPU(qdev); if (virtio_gpu_blob_enabled(g->parent_obj.conf)) { - if (!virtio_gpu_have_udmabuf()) { - error_setg(errp, "cannot enable blob resources without udmabuf"); + if (!virtio_gpu_rutabaga_enabled(g->parent_obj.conf) && + !virtio_gpu_have_udmabuf()) { + error_setg(errp, "need rutabaga or udmabuf for blob resources"); return; } @@ -1334,21 +1492,70 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) g->ctrl_vq = virtio_get_queue(vdev, 0); g->cursor_vq = virtio_get_queue(vdev, 1); - g->ctrl_bh = qemu_bh_new(virtio_gpu_ctrl_bh, g); - g->cursor_bh = qemu_bh_new(virtio_gpu_cursor_bh, g); + g->ctrl_bh = virtio_bh_new_guarded(qdev, virtio_gpu_ctrl_bh, g); + g->cursor_bh = virtio_bh_new_guarded(qdev, virtio_gpu_cursor_bh, g); + g->reset_bh = qemu_bh_new(virtio_gpu_reset_bh, g); + qemu_cond_init(&g->reset_cond); QTAILQ_INIT(&g->reslist); QTAILQ_INIT(&g->cmdq); QTAILQ_INIT(&g->fenceq); } +static void virtio_gpu_device_unrealize(DeviceState *qdev) +{ + VirtIOGPU *g = VIRTIO_GPU(qdev); + + g_clear_pointer(&g->ctrl_bh, qemu_bh_delete); + g_clear_pointer(&g->cursor_bh, qemu_bh_delete); + g_clear_pointer(&g->reset_bh, qemu_bh_delete); + qemu_cond_destroy(&g->reset_cond); + virtio_gpu_base_device_unrealize(qdev); +} + +static void virtio_gpu_reset_bh(void *opaque) +{ + VirtIOGPU *g = VIRTIO_GPU(opaque); + VirtIOGPUClass *vgc = VIRTIO_GPU_GET_CLASS(g); + struct virtio_gpu_simple_resource *res, *tmp; + uint32_t resource_id; + Error *local_err = NULL; + int i = 0; + + QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) { + resource_id = res->resource_id; + vgc->resource_destroy(g, res, &local_err); + if (local_err) { + error_append_hint(&local_err, "%s: %s resource_destroy" + "for resource_id = %"PRIu32" failed.\n", + __func__, object_get_typename(OBJECT(g)), + resource_id); + /* error_report_err frees the error object for us */ + error_report_err(local_err); + local_err = NULL; + } + } + + for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { + dpy_gfx_replace_surface(g->parent_obj.scanout[i].con, NULL); + } + + g->reset_finished = true; + qemu_cond_signal(&g->reset_cond); +} + void virtio_gpu_reset(VirtIODevice *vdev) { VirtIOGPU *g = VIRTIO_GPU(vdev); - struct virtio_gpu_simple_resource *res, *tmp; struct virtio_gpu_ctrl_command *cmd; - QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) { - virtio_gpu_resource_destroy(g, res); + if (qemu_in_vcpu_thread()) { + g->reset_finished = false; + qemu_bh_schedule(g->reset_bh); + while (!g->reset_finished) { + qemu_cond_wait_bql(&g->reset_cond); + } + } else { + aio_bh_call(g->reset_bh); } while (!QTAILQ_EMPTY(&g->cmdq)) { @@ -1387,6 +1594,32 @@ virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config) } } +static bool virtio_gpu_blob_state_needed(void *opaque) +{ + VirtIOGPU *g = VIRTIO_GPU(opaque); + + return virtio_gpu_blob_enabled(g->parent_obj.conf); +} + +const VMStateDescription vmstate_virtio_gpu_blob_state = { + .name = "virtio-gpu/blob", + .minimum_version_id = VIRTIO_GPU_VM_VERSION, + .version_id = VIRTIO_GPU_VM_VERSION, + .needed = virtio_gpu_blob_state_needed, + .fields = (const VMStateField[]){ + { + .name = "virtio-gpu/blob", + .info = &(const VMStateInfo) { + .name = "blob", + .get = virtio_gpu_blob_load, + .put = virtio_gpu_blob_save, + }, + .flags = VMS_SINGLE, + } /* device */, + VMSTATE_END_OF_LIST() + }, +}; + /* * For historical reasons virtio_gpu does not adhere to virtio migration * scheme as described in doc/virtio-migration.txt, in a sense that no @@ -1399,7 +1632,7 @@ static const VMStateDescription vmstate_virtio_gpu = { .name = "virtio-gpu", .minimum_version_id = VIRTIO_GPU_VM_VERSION, .version_id = VIRTIO_GPU_VM_VERSION, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE /* core */, { .name = "virtio-gpu", @@ -1412,6 +1645,11 @@ static const VMStateDescription vmstate_virtio_gpu = { } /* device */, VMSTATE_END_OF_LIST() }, + .subsections = (const VMStateDescription * const []) { + &vmstate_virtio_gpu_blob_state, + NULL + }, + .post_load = virtio_gpu_post_load, }; static Property virtio_gpu_properties[] = { @@ -1420,6 +1658,7 @@ static Property virtio_gpu_properties[] = { 256 * MiB), DEFINE_PROP_BIT("blob", VirtIOGPU, parent_obj.conf.flags, VIRTIO_GPU_FLAG_BLOB_ENABLED, false), + DEFINE_PROP_SIZE("hostmem", VirtIOGPU, parent_obj.conf.hostmem, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -1433,9 +1672,11 @@ static void virtio_gpu_class_init(ObjectClass *klass, void *data) vgc->handle_ctrl = virtio_gpu_handle_ctrl; vgc->process_cmd = virtio_gpu_simple_process_cmd; vgc->update_cursor_data = virtio_gpu_update_cursor_data; + vgc->resource_destroy = virtio_gpu_resource_destroy; vgbc->gl_flushed = virtio_gpu_handle_gl_flushed; vdc->realize = virtio_gpu_device_realize; + vdc->unrealize = virtio_gpu_device_unrealize; vdc->reset = virtio_gpu_reset; vdc->get_config = virtio_gpu_get_config; vdc->set_config = virtio_gpu_set_config; diff --git a/hw/display/virtio-vga-rutabaga.c b/hw/display/virtio-vga-rutabaga.c new file mode 100644 index 0000000000..a7bef6da24 --- /dev/null +++ b/hw/display/virtio-vga-rutabaga.c @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "hw/pci/pci.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/virtio-gpu.h" +#include "hw/display/vga.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "virtio-vga.h" +#include "qom/object.h" + +#define TYPE_VIRTIO_VGA_RUTABAGA "virtio-vga-rutabaga" + +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOVGARutabaga, VIRTIO_VGA_RUTABAGA) + +struct VirtIOVGARutabaga { + VirtIOVGABase parent_obj; + + VirtIOGPURutabaga vdev; +}; + +static void virtio_vga_rutabaga_inst_initfn(Object *obj) +{ + VirtIOVGARutabaga *dev = VIRTIO_VGA_RUTABAGA(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_GPU_RUTABAGA); + VIRTIO_VGA_BASE(dev)->vgpu = VIRTIO_GPU_BASE(&dev->vdev); +} + +static VirtioPCIDeviceTypeInfo virtio_vga_rutabaga_info = { + .generic_name = TYPE_VIRTIO_VGA_RUTABAGA, + .parent = TYPE_VIRTIO_VGA_BASE, + .instance_size = sizeof(VirtIOVGARutabaga), + .instance_init = virtio_vga_rutabaga_inst_initfn, +}; +module_obj(TYPE_VIRTIO_VGA_RUTABAGA); +module_kconfig(VIRTIO_VGA); + +static void virtio_vga_register_types(void) +{ + if (have_vga) { + virtio_pci_types_register(&virtio_vga_rutabaga_info); + } +} + +type_init(virtio_vga_register_types) + +module_dep("hw-display-virtio-vga"); diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index c206b5da38..94d3353f54 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -47,15 +47,14 @@ static void virtio_vga_base_text_update(void *opaque, console_ch_t *chardata) } } -static int virtio_vga_base_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) +static void virtio_vga_base_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) { VirtIOVGABase *vvga = opaque; VirtIOGPUBase *g = vvga->vgpu; if (g->hw_ops->ui_info) { - return g->hw_ops->ui_info(g, idx, info); + g->hw_ops->ui_info(g, idx, info); } - return -1; } static void virtio_vga_base_gl_block(void *opaque, bool block) @@ -89,7 +88,7 @@ static const VMStateDescription vmstate_virtio_vga_base = { .name = "virtio-vga", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { /* no pci stuff here, saving the virtio device will handle that */ VMSTATE_STRUCT(vga, VirtIOVGABase, 0, vmstate_vga_common, VGACommonState), @@ -116,17 +115,32 @@ static void virtio_vga_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp) pci_register_bar(&vpci_dev->pci_dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &vga->vram); - /* - * Configure virtio bar and regions - * - * We use bar #2 for the mmio regions, to be compatible with stdvga. - * virtio regions are moved to the end of bar #2, to make room for - * the stdvga mmio registers at the start of bar #2. - */ - vpci_dev->modern_mem_bar_idx = 2; - vpci_dev->msix_bar_idx = 4; vpci_dev->modern_io_bar_idx = 5; + if (!virtio_gpu_hostmem_enabled(g->conf)) { + /* + * Configure virtio bar and regions + * + * We use bar #2 for the mmio regions, to be compatible with stdvga. + * virtio regions are moved to the end of bar #2, to make room for + * the stdvga mmio registers at the start of bar #2. + */ + vpci_dev->modern_mem_bar_idx = 2; + vpci_dev->msix_bar_idx = 4; + } else { + vpci_dev->msix_bar_idx = 1; + vpci_dev->modern_mem_bar_idx = 2; + memory_region_init(&g->hostmem, OBJECT(g), "virtio-gpu-hostmem", + g->conf.hostmem); + pci_register_bar(&vpci_dev->pci_dev, 4, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_PREFETCH | + PCI_BASE_ADDRESS_MEM_TYPE_64, + &g->hostmem); + virtio_pci_add_shm_cap(vpci_dev, 4, 0, g->conf.hostmem, + VIRTIO_GPU_SHM_ID_HOST_VISIBLE); + } + if (!(vpci_dev->flags & VIRTIO_PCI_FLAG_PAGE_PER_VQ)) { /* * with page-per-vq=off there is no padding space we can use @@ -166,13 +180,15 @@ static void virtio_vga_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp) } } -static void virtio_vga_base_reset(DeviceState *dev) +static void virtio_vga_base_reset_hold(Object *obj) { - VirtIOVGABaseClass *klass = VIRTIO_VGA_BASE_GET_CLASS(dev); - VirtIOVGABase *vvga = VIRTIO_VGA_BASE(dev); + VirtIOVGABaseClass *klass = VIRTIO_VGA_BASE_GET_CLASS(obj); + VirtIOVGABase *vvga = VIRTIO_VGA_BASE(obj); /* reset virtio-gpu */ - klass->parent_reset(dev); + if (klass->parent_phases.hold) { + klass->parent_phases.hold(obj); + } /* reset vga */ vga_common_reset(&vvga->vga); @@ -204,13 +220,14 @@ static void virtio_vga_base_class_init(ObjectClass *klass, void *data) VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); VirtIOVGABaseClass *v = VIRTIO_VGA_BASE_CLASS(klass); PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); 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, - &v->parent_reset); + resettable_class_set_parent_phases(rc, NULL, virtio_vga_base_reset_hold, + NULL, &v->parent_phases); k->realize = virtio_vga_base_realize; pcidev_k->romfile = "vgabios-virtio.bin"; diff --git a/hw/display/virtio-vga.h b/hw/display/virtio-vga.h index 977ad5edc2..0bd9db1cee 100644 --- a/hw/display/virtio-vga.h +++ b/hw/display/virtio-vga.h @@ -23,7 +23,7 @@ struct VirtIOVGABase { struct VirtIOVGABaseClass { VirtioPCIClass parent_class; - DeviceReset parent_reset; + ResettablePhases parent_phases; }; #endif /* VIRTIO_VGA_H */ diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index cedbbde522..1c0f9d9a99 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -29,10 +29,11 @@ #include "qemu/log.h" #include "hw/loader.h" #include "trace.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qom/object.h" +#include "ui/console.h" #undef VERBOSE #define HW_RECT_ACCEL @@ -335,8 +336,8 @@ static inline bool vmsvga_verify_rect(DisplaySurface *surface, return false; } if (h > SVGA_MAX_HEIGHT) { - trace_vmware_verify_rect_greater_than_bound(name, "y", SVGA_MAX_HEIGHT, - y); + trace_vmware_verify_rect_greater_than_bound(name, "h", SVGA_MAX_HEIGHT, + h); return false; } if (y + h > surface_height(surface)) { @@ -549,12 +550,12 @@ static inline void vmsvga_cursor_define(struct vmsvga_state_s *s, default: fprintf(stderr, "%s: unhandled bpp %d, using fallback cursor\n", __func__, c->bpp); - cursor_put(qc); + cursor_unref(qc); qc = cursor_builtin_left_ptr(); } dpy_cursor_define(s->vga.con, qc); - cursor_put(qc); + cursor_unref(qc); } #endif @@ -1209,7 +1210,7 @@ static const VMStateDescription vmstate_vmware_vga_internal = { .version_id = 0, .minimum_version_id = 0, .post_load = vmsvga_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32_EQUAL(new_depth, struct vmsvga_state_s, NULL), VMSTATE_INT32(enable, struct vmsvga_state_s), VMSTATE_INT32(config, struct vmsvga_state_s), @@ -1234,7 +1235,7 @@ static const VMStateDescription vmstate_vmware_vga = { .name = "vmware_vga", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, struct pci_vmsvga_state_s), VMSTATE_STRUCT(chip, struct pci_vmsvga_state_s, 0, vmstate_vmware_vga_internal, struct vmsvga_state_s), @@ -1263,7 +1264,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s, vga_common_init(&s->vga, OBJECT(dev), &error_fatal); vga_init(&s->vga, OBJECT(dev), address_space, io, true); - vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga); + vmstate_register_any(NULL, &vmstate_vga_common, &s->vga); s->new_depth = 32; } diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index cea10fe3c7..b2130a0d70 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -76,7 +76,7 @@ struct XenFB { int do_resize; struct { - int x,y,w,h; + int x,y,w,h; } up_rects[UP_QUEUE]; int up_count; int up_fullscreen; @@ -98,8 +98,9 @@ static int common_bind(struct common *c) if (xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port) == -1) return -1; - c->page = xenforeignmemory_map(xen_fmem, c->xendev.dom, - PROT_READ | PROT_WRITE, 1, &mfn, NULL); + c->page = qemu_xen_foreignmem_map(c->xendev.dom, NULL, + PROT_READ | PROT_WRITE, 1, &mfn, + NULL); if (c->page == NULL) return -1; @@ -115,33 +116,33 @@ static void common_unbind(struct common *c) { xen_pv_unbind_evtchn(&c->xendev); if (c->page) { - xenforeignmemory_unmap(xen_fmem, c->page, 1); - c->page = NULL; + qemu_xen_foreignmem_unmap(c->page, 1); + c->page = NULL; } } /* -------------------------------------------------------------------- */ /* Send an event to the keyboard frontend driver */ static int xenfb_kbd_event(struct XenInput *xenfb, - union xenkbd_in_event *event) + union xenkbd_in_event *event) { struct xenkbd_page *page = xenfb->c.page; uint32_t prod; if (xenfb->c.xendev.be_state != XenbusStateConnected) - return 0; + return 0; if (!page) return 0; prod = page->in_prod; if (prod - page->in_cons == XENKBD_IN_RING_LEN) { - errno = EAGAIN; - return -1; + errno = EAGAIN; + return -1; } - xen_mb(); /* ensure ring space available */ + xen_mb(); /* ensure ring space available */ XENKBD_IN_RING_REF(page, prod) = *event; - xen_wmb(); /* ensure ring contents visible */ + xen_wmb(); /* ensure ring contents visible */ page->in_prod = prod + 1; return xen_pv_send_notify(&xenfb->c.xendev); } @@ -161,7 +162,7 @@ static int xenfb_send_key(struct XenInput *xenfb, bool down, int keycode) /* Send a relative mouse movement event */ static int xenfb_send_motion(struct XenInput *xenfb, - int rel_x, int rel_y, int rel_z) + int rel_x, int rel_y, int rel_z) { union xenkbd_in_event event; @@ -176,7 +177,7 @@ static int xenfb_send_motion(struct XenInput *xenfb, /* Send an absolute mouse movement event */ static int xenfb_send_position(struct XenInput *xenfb, - int abs_x, int abs_y, int z) + int abs_x, int abs_y, int z) { union xenkbd_in_event event; @@ -320,20 +321,20 @@ static void xenfb_mouse_sync(DeviceState *dev) xenfb->wheel = 0; } -static QemuInputHandler xenfb_keyboard = { +static const QemuInputHandler xenfb_keyboard = { .name = "Xen PV Keyboard", .mask = INPUT_EVENT_MASK_KEY, .event = xenfb_key_event, }; -static QemuInputHandler xenfb_abs_mouse = { +static const QemuInputHandler xenfb_abs_mouse = { .name = "Xen PV Mouse", .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, .event = xenfb_mouse_event, .sync = xenfb_mouse_sync, }; -static QemuInputHandler xenfb_rel_mouse = { +static const QemuInputHandler xenfb_rel_mouse = { .name = "Xen PV Mouse", .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL, .event = xenfb_mouse_event, @@ -354,7 +355,7 @@ static int input_initialise(struct XenLegacyDevice *xendev) rc = common_bind(&in->c); if (rc != 0) - return rc; + return rc; return 0; } @@ -415,7 +416,7 @@ static void input_event(struct XenLegacyDevice *xendev) /* We don't understand any keyboard events, so just ignore them. */ if (page->out_prod == page->out_cons) - return; + return; page->out_cons = page->out_prod; xen_pv_send_notify(&xenfb->c.xendev); } @@ -429,7 +430,7 @@ static void xenfb_copy_mfns(int mode, int count, xen_pfn_t *dst, void *src) int i; for (i = 0; i < count; i++) - dst[i] = (mode == 32) ? src32[i] : src64[i]; + dst[i] = (mode == 32) ? src32[i] : src64[i]; } static int xenfb_map_fb(struct XenFB *xenfb) @@ -447,70 +448,71 @@ static int xenfb_map_fb(struct XenFB *xenfb) mode = sizeof(unsigned long) * 8; if (!protocol) { - /* - * Undefined protocol, some guesswork needed. - * - * Old frontends which don't set the protocol use - * one page directory only, thus pd[1] must be zero. - * pd[1] of the 32bit struct layout and the lower - * 32 bits of pd[0] of the 64bit struct layout have - * the same location, so we can check that ... - */ - uint32_t *ptr32 = NULL; - uint32_t *ptr64 = NULL; + /* + * Undefined protocol, some guesswork needed. + * + * Old frontends which don't set the protocol use + * one page directory only, thus pd[1] must be zero. + * pd[1] of the 32bit struct layout and the lower + * 32 bits of pd[0] of the 64bit struct layout have + * the same location, so we can check that ... + */ + uint32_t *ptr32 = NULL; + uint32_t *ptr64 = NULL; #if defined(__i386__) - ptr32 = (void*)page->pd; - ptr64 = ((void*)page->pd) + 4; + ptr32 = (void*)page->pd; + ptr64 = ((void*)page->pd) + 4; #elif defined(__x86_64__) - ptr32 = ((void*)page->pd) - 4; - ptr64 = (void*)page->pd; + ptr32 = ((void*)page->pd) - 4; + ptr64 = (void*)page->pd; #endif - if (ptr32) { - if (ptr32[1] == 0) { - mode = 32; - pd = ptr32; - } else { - mode = 64; - pd = ptr64; - } - } + if (ptr32) { + if (ptr32[1] == 0) { + mode = 32; + pd = ptr32; + } else { + mode = 64; + pd = ptr64; + } + } #if defined(__x86_64__) } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { - /* 64bit dom0, 32bit domU */ - mode = 32; - pd = ((void*)page->pd) - 4; + /* 64bit dom0, 32bit domU */ + mode = 32; + pd = ((void*)page->pd) - 4; #elif defined(__i386__) } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { - /* 32bit dom0, 64bit domU */ - mode = 64; - pd = ((void*)page->pd) + 4; + /* 32bit dom0, 64bit domU */ + mode = 64; + pd = ((void*)page->pd) + 4; #endif } if (xenfb->pixels) { - munmap(xenfb->pixels, xenfb->fbpages * XC_PAGE_SIZE); + munmap(xenfb->pixels, xenfb->fbpages * XEN_PAGE_SIZE); xenfb->pixels = NULL; } - xenfb->fbpages = DIV_ROUND_UP(xenfb->fb_len, XC_PAGE_SIZE); + xenfb->fbpages = DIV_ROUND_UP(xenfb->fb_len, XEN_PAGE_SIZE); n_fbdirs = xenfb->fbpages * mode / 8; - n_fbdirs = DIV_ROUND_UP(n_fbdirs, XC_PAGE_SIZE); + n_fbdirs = DIV_ROUND_UP(n_fbdirs, XEN_PAGE_SIZE); pgmfns = g_new0(xen_pfn_t, n_fbdirs); fbmfns = g_new0(xen_pfn_t, xenfb->fbpages); xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd); - map = xenforeignmemory_map(xen_fmem, xenfb->c.xendev.dom, - PROT_READ, n_fbdirs, pgmfns, NULL); + map = qemu_xen_foreignmem_map(xenfb->c.xendev.dom, NULL, PROT_READ, + n_fbdirs, pgmfns, NULL); if (map == NULL) - goto out; + goto out; xenfb_copy_mfns(mode, xenfb->fbpages, fbmfns, map); - xenforeignmemory_unmap(xen_fmem, map, n_fbdirs); + qemu_xen_foreignmem_unmap(map, n_fbdirs); - xenfb->pixels = xenforeignmemory_map(xen_fmem, xenfb->c.xendev.dom, - PROT_READ, xenfb->fbpages, fbmfns, NULL); + xenfb->pixels = qemu_xen_foreignmem_map(xenfb->c.xendev.dom, NULL, + PROT_READ, xenfb->fbpages, + fbmfns, NULL); if (xenfb->pixels == NULL) - goto out; + goto out; ret = 0; /* all is fine */ @@ -526,8 +528,8 @@ static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, { size_t mfn_sz = sizeof_field(struct xenfb_page, pd[0]); size_t pd_len = sizeof_field(struct xenfb_page, pd) / mfn_sz; - size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz; - size_t fb_len_max = fb_pages * XC_PAGE_SIZE; + size_t fb_pages = pd_len * XEN_PAGE_SIZE / mfn_sz; + size_t fb_len_max = fb_pages * XEN_PAGE_SIZE; int max_width, max_height; if (fb_len_lim > fb_len_max) { @@ -589,35 +591,35 @@ static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, /* A convenient function for munging pixels between different depths */ #define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \ - for (line = y ; line < (y+h) ; line++) { \ - SRC_T *src = (SRC_T *)(xenfb->pixels \ - + xenfb->offset \ - + (line * xenfb->row_stride) \ - + (x * xenfb->depth / 8)); \ - DST_T *dst = (DST_T *)(data \ - + (line * linesize) \ - + (x * bpp / 8)); \ - int col; \ - const int RSS = 32 - (RSB + GSB + BSB); \ - const int GSS = 32 - (GSB + BSB); \ - const int BSS = 32 - (BSB); \ - const uint32_t RSM = (~0U) << (32 - RSB); \ - const uint32_t GSM = (~0U) << (32 - GSB); \ - const uint32_t BSM = (~0U) << (32 - BSB); \ - const int RDS = 32 - (RDB + GDB + BDB); \ - const int GDS = 32 - (GDB + BDB); \ - const int BDS = 32 - (BDB); \ - const uint32_t RDM = (~0U) << (32 - RDB); \ - const uint32_t GDM = (~0U) << (32 - GDB); \ - const uint32_t BDM = (~0U) << (32 - BDB); \ - for (col = x ; col < (x+w) ; col++) { \ - uint32_t spix = *src; \ - *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \ - (((spix << GSS) & GSM & GDM) >> GDS) | \ - (((spix << BSS) & BSM & BDM) >> BDS); \ - src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \ - dst = (DST_T *) ((unsigned long) dst + bpp / 8); \ - } \ + for (line = y ; line < (y+h) ; line++) { \ + SRC_T *src = (SRC_T *)(xenfb->pixels \ + + xenfb->offset \ + + (line * xenfb->row_stride) \ + + (x * xenfb->depth / 8)); \ + DST_T *dst = (DST_T *)(data \ + + (line * linesize) \ + + (x * bpp / 8)); \ + int col; \ + const int RSS = 32 - (RSB + GSB + BSB); \ + const int GSS = 32 - (GSB + BSB); \ + const int BSS = 32 - (BSB); \ + const uint32_t RSM = (~0U) << (32 - RSB); \ + const uint32_t GSM = (~0U) << (32 - GSB); \ + const uint32_t BSM = (~0U) << (32 - BSB); \ + const int RDS = 32 - (RDB + GDB + BDB); \ + const int GDS = 32 - (GDB + BDB); \ + const int BDS = 32 - (BDB); \ + const uint32_t RDM = (~0U) << (32 - RDB); \ + const uint32_t GDM = (~0U) << (32 - GDB); \ + const uint32_t BDM = (~0U) << (32 - BDB); \ + for (col = x ; col < (x+w) ; col++) { \ + uint32_t spix = *src; \ + *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \ + (((spix << GSS) & GSM & GDM) >> GDS) | \ + (((spix << BSS) & BSM & BDM) >> BDS); \ + src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \ + dst = (DST_T *) ((unsigned long) dst + bpp / 8); \ + } \ } @@ -657,7 +659,7 @@ static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h) break; default: oops = 1; - } + } } if (oops) /* should not happen */ xen_pv_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n", @@ -777,16 +779,24 @@ static void xenfb_update(void *opaque) xenfb->up_fullscreen = 0; } -static void xenfb_update_interval(void *opaque, uint64_t interval) +static void xenfb_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) { struct XenFB *xenfb = opaque; + uint32_t refresh_rate; if (xenfb->feature_update) { #ifdef XENFB_TYPE_REFRESH_PERIOD if (xenfb_queue_full(xenfb)) { return; } - xenfb_send_refresh_period(xenfb, interval); + + refresh_rate = info->refresh_rate; + if (!refresh_rate) { + refresh_rate = 75; + } + + /* T = 1 / f = 1 [s*Hz] / f = 1000*1000 [ms*mHz] / f */ + xenfb_send_refresh_period(xenfb, 1000 * 1000 / refresh_rate); #endif } } @@ -808,60 +818,60 @@ static void xenfb_handle_events(struct XenFB *xenfb) if (prod - out_cons > XENFB_OUT_RING_LEN) { return; } - xen_rmb(); /* ensure we see ring contents up to prod */ + xen_rmb(); /* ensure we see ring contents up to prod */ for (cons = out_cons; cons != prod; cons++) { - union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons); + union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons); uint8_t type = event->type; - int x, y, w, h; + int x, y, w, h; - switch (type) { - case XENFB_TYPE_UPDATE: - if (xenfb->up_count == UP_QUEUE) - xenfb->up_fullscreen = 1; - if (xenfb->up_fullscreen) - break; - x = MAX(event->update.x, 0); - y = MAX(event->update.y, 0); - w = MIN(event->update.width, xenfb->width - x); - h = MIN(event->update.height, xenfb->height - y); - if (w < 0 || h < 0) { + switch (type) { + case XENFB_TYPE_UPDATE: + if (xenfb->up_count == UP_QUEUE) + xenfb->up_fullscreen = 1; + if (xenfb->up_fullscreen) + break; + x = MAX(event->update.x, 0); + y = MAX(event->update.y, 0); + w = MIN(event->update.width, xenfb->width - x); + h = MIN(event->update.height, xenfb->height - y); + if (w < 0 || h < 0) { xen_pv_printf(&xenfb->c.xendev, 1, "bogus update ignored\n"); - break; - } - if (x != event->update.x || + break; + } + if (x != event->update.x || y != event->update.y || - w != event->update.width || - h != event->update.height) { + w != event->update.width || + h != event->update.height) { xen_pv_printf(&xenfb->c.xendev, 1, "bogus update clipped\n"); - } - if (w == xenfb->width && h > xenfb->height / 2) { - /* scroll detector: updated more than 50% of the lines, - * don't bother keeping track of the rectangles then */ - xenfb->up_fullscreen = 1; - } else { - xenfb->up_rects[xenfb->up_count].x = x; - xenfb->up_rects[xenfb->up_count].y = y; - xenfb->up_rects[xenfb->up_count].w = w; - xenfb->up_rects[xenfb->up_count].h = h; - xenfb->up_count++; - } - break; + } + if (w == xenfb->width && h > xenfb->height / 2) { + /* scroll detector: updated more than 50% of the lines, + * don't bother keeping track of the rectangles then */ + xenfb->up_fullscreen = 1; + } else { + xenfb->up_rects[xenfb->up_count].x = x; + xenfb->up_rects[xenfb->up_count].y = y; + xenfb->up_rects[xenfb->up_count].w = w; + xenfb->up_rects[xenfb->up_count].h = h; + xenfb->up_count++; + } + break; #ifdef XENFB_TYPE_RESIZE - case XENFB_TYPE_RESIZE: - if (xenfb_configure_fb(xenfb, xenfb->fb_len, - event->resize.width, - event->resize.height, - event->resize.depth, - xenfb->fb_len, - event->resize.offset, - event->resize.stride) < 0) - break; - xenfb_invalidate(xenfb); - break; + case XENFB_TYPE_RESIZE: + if (xenfb_configure_fb(xenfb, xenfb->fb_len, + event->resize.width, + event->resize.height, + event->resize.depth, + xenfb->fb_len, + event->resize.offset, + event->resize.stride) < 0) + break; + xenfb_invalidate(xenfb); + break; #endif - } + } } - xen_mb(); /* ensure we're done with ring contents */ + xen_mb(); /* ensure we're done with ring contents */ page->out_cons = cons; } @@ -881,32 +891,32 @@ static int fb_initialise(struct XenLegacyDevice *xendev) int rc; if (xenstore_read_fe_int(xendev, "videoram", &videoram) == -1) - videoram = 0; + videoram = 0; rc = common_bind(&fb->c); if (rc != 0) - return rc; + return rc; fb_page = fb->c.page; rc = xenfb_configure_fb(fb, videoram * MiB, - fb_page->width, fb_page->height, fb_page->depth, - fb_page->mem_length, 0, fb_page->line_length); + fb_page->width, fb_page->height, fb_page->depth, + fb_page->mem_length, 0, fb_page->line_length); if (rc != 0) - return rc; + return rc; rc = xenfb_map_fb(fb); if (rc != 0) - return rc; + return rc; fb->con = graphic_console_init(NULL, 0, &xenfb_ops, fb); if (xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update) == -1) - fb->feature_update = 0; + fb->feature_update = 0; if (fb->feature_update) - xenstore_write_be_int(xendev, "request-update", 1); + xenstore_write_be_int(xendev, "request-update", 1); xen_pv_printf(xendev, 1, "feature-update=%d, videoram=%d\n", - fb->feature_update, videoram); + fb->feature_update, videoram); return 0; } @@ -919,8 +929,8 @@ static void fb_disconnect(struct XenLegacyDevice *xendev) * Replacing the framebuffer with anonymous shared memory * instead. This releases the guest pages and keeps qemu happy. */ - xenforeignmemory_unmap(xen_fmem, fb->pixels, fb->fbpages); - fb->pixels = mmap(fb->pixels, fb->fbpages * XC_PAGE_SIZE, + qemu_xen_foreignmem_unmap(fb->pixels, fb->fbpages); + fb->pixels = mmap(fb->pixels, fb->fbpages * XEN_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); if (fb->pixels == MAP_FAILED) { @@ -983,5 +993,5 @@ struct XenDevOps xen_framebuffer_ops = { static const GraphicHwOps xenfb_ops = { .invalidate = xenfb_invalidate, .gfx_update = xenfb_update, - .update_interval = xenfb_update_interval, + .ui_info = xenfb_ui_info, }; diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index a071c81883..c42fc388dc 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -262,7 +262,7 @@ typedef enum DPVideoFmt DPVideoFmt; static const VMStateDescription vmstate_dp = { .name = TYPE_XLNX_DP, .version_id = 2, - .fields = (VMStateField[]){ + .fields = (const VMStateField[]){ VMSTATE_UINT32_ARRAY(core_registers, XlnxDPState, DP_CORE_REG_ARRAY_SIZE), VMSTATE_UINT32_ARRAY(avbufm_registers, XlnxDPState, @@ -380,13 +380,16 @@ static inline void xlnx_dp_audio_mix_buffer(XlnxDPState *s) static void xlnx_dp_audio_callback(void *opaque, int avail) { /* - * Get some data from the DPDMA and compute these datas. - * Then wait for QEMU's audio subsystem to call this callback. + * Get the individual left and right audio streams from the DPDMA, + * and fill the output buffer with the combined stereo audio data + * adjusted by the volume controls. + * QEMU's audio subsystem will call this callback repeatedly; + * we return the data from the output buffer until it is emptied, + * and then we will read data from the DPDMA again. */ XlnxDPState *s = XLNX_DP(opaque); size_t written = 0; - /* If there are already some data don't get more data. */ if (s->byte_left == 0) { s->audio_data_available[0] = xlnx_dpdma_start_operation(s->dpdma, 4, true); @@ -532,8 +535,8 @@ static void xlnx_dp_aux_set_command(XlnxDPState *s, uint32_t value) qemu_log_mask(LOG_UNIMP, "xlnx_dp: Write i2c status not implemented\n"); break; default: - error_report("%s: invalid command: %u", __func__, cmd); - abort(); + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command: %u", __func__, cmd); + return; } s->core_registers[DP_INTERRUPT_SIGNAL_STATE] |= 0x04; @@ -1299,6 +1302,10 @@ static void xlnx_dp_realize(DeviceState *dev, Error **errp) DisplaySurface *surface; struct audsettings as; + if (!AUD_register_card("xlnx_dp.audio", &s->aud_card, errp)) { + return; + } + aux_bus_realize(s->aux_bus); qdev_realize(DEVICE(s->dpcd), BUS(s->aux_bus), &error_fatal); @@ -1317,8 +1324,6 @@ static void xlnx_dp_realize(DeviceState *dev, Error **errp) as.fmt = AUDIO_FORMAT_S16; as.endianness = 0; - AUD_register_card("xlnx_dp.audio", &s->aud_card); - s->amixer_output_stream = AUD_open_out(&s->aud_card, s->amixer_output_stream, "xlnx_dp.audio.out", @@ -1382,6 +1387,11 @@ static void xlnx_dp_reset(DeviceState *dev) xlnx_dp_update_irq(s); } +static Property xlnx_dp_device_properties[] = { + DEFINE_AUDIO_PROPERTIES(XlnxDPState, aud_card), + DEFINE_PROP_END_OF_LIST(), +}; + static void xlnx_dp_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -1389,6 +1399,7 @@ static void xlnx_dp_class_init(ObjectClass *oc, void *data) dc->realize = xlnx_dp_realize; dc->vmsd = &vmstate_dp; dc->reset = xlnx_dp_reset; + device_class_set_props(dc, xlnx_dp_device_properties); } static const TypeInfo xlnx_dp_info = { diff --git a/hw/dma/bcm2835_dma.c b/hw/dma/bcm2835_dma.c index 5e9306110d..9bda45072b 100644 --- a/hw/dma/bcm2835_dma.c +++ b/hw/dma/bcm2835_dma.c @@ -311,7 +311,7 @@ static const VMStateDescription vmstate_bcm2835_dma_chan = { .name = TYPE_BCM2835_DMA "-chan", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(cs, BCM2835DMAChan), VMSTATE_UINT32(conblk_ad, BCM2835DMAChan), VMSTATE_UINT32(ti, BCM2835DMAChan), @@ -329,7 +329,7 @@ static const VMStateDescription vmstate_bcm2835_dma = { .name = TYPE_BCM2835_DMA, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(chan, BCM2835DMAState, BCM2835_DMA_NCHANS, 1, vmstate_bcm2835_dma_chan, BCM2835DMAChan), VMSTATE_UINT32(int_status, BCM2835DMAState), diff --git a/hw/dma/etraxfs_dma.c b/hw/dma/etraxfs_dma.c index c4334e87bf..9c0003de51 100644 --- a/hw/dma/etraxfs_dma.c +++ b/hw/dma/etraxfs_dma.c @@ -28,6 +28,7 @@ #include "qemu/main-loop.h" #include "sysemu/runstate.h" #include "exec/address-spaces.h" +#include "exec/memory.h" #include "hw/cris/etraxfs_dma.h" @@ -160,38 +161,38 @@ enum { enum dma_ch_state { - RST = 1, - STOPPED = 2, - RUNNING = 4 + RST = 1, + STOPPED = 2, + RUNNING = 4 }; struct fs_dma_channel { - qemu_irq irq; - struct etraxfs_dma_client *client; + qemu_irq irq; + struct etraxfs_dma_client *client; - /* Internal status. */ - int stream_cmd_src; - enum dma_ch_state state; + /* Internal status. */ + int stream_cmd_src; + enum dma_ch_state state; - unsigned int input : 1; - unsigned int eol : 1; + unsigned int input : 1; + unsigned int eol : 1; - struct dma_descr_group current_g; - struct dma_descr_context current_c; - struct dma_descr_data current_d; + struct dma_descr_group current_g; + struct dma_descr_context current_c; + struct dma_descr_data current_d; - /* Control registers. */ - uint32_t regs[DMA_REG_MAX]; + /* Control registers. */ + uint32_t regs[DMA_REG_MAX]; }; struct fs_dma_ctrl { - MemoryRegion mmio; - int nr_channels; - struct fs_dma_channel *channels; + MemoryRegion mmio; + int nr_channels; + struct fs_dma_channel *channels; - QEMUBH *bh; + QEMUBH *bh; }; static void DMA_run(void *opaque); @@ -199,553 +200,553 @@ static int channel_out_run(struct fs_dma_ctrl *ctrl, int c); static inline uint32_t channel_reg(struct fs_dma_ctrl *ctrl, int c, int reg) { - return ctrl->channels[c].regs[reg]; + return ctrl->channels[c].regs[reg]; } static inline int channel_stopped(struct fs_dma_ctrl *ctrl, int c) { - return channel_reg(ctrl, c, RW_CFG) & 2; + return channel_reg(ctrl, c, RW_CFG) & 2; } static inline int channel_en(struct fs_dma_ctrl *ctrl, int c) { - return (channel_reg(ctrl, c, RW_CFG) & 1) - && ctrl->channels[c].client; + return (channel_reg(ctrl, c, RW_CFG) & 1) + && ctrl->channels[c].client; } static inline int fs_channel(hwaddr addr) { - /* Every channel has a 0x2000 ctrl register map. */ - return addr >> 13; + /* Every channel has a 0x2000 ctrl register map. */ + return addr >> 13; } #ifdef USE_THIS_DEAD_CODE static void channel_load_g(struct fs_dma_ctrl *ctrl, int c) { - hwaddr addr = channel_reg(ctrl, c, RW_GROUP); + hwaddr addr = channel_reg(ctrl, c, RW_GROUP); - /* Load and decode. FIXME: handle endianness. */ + /* Load and decode. FIXME: handle endianness. */ 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) { - printf("%s ch=%d\n", __func__, ch); - printf("next=%x\n", c->next); - printf("saved_data=%x\n", c->saved_data); - printf("saved_data_buf=%x\n", c->saved_data_buf); - printf("eol=%x\n", (uint32_t) c->eol); + printf("%s ch=%d\n", __func__, ch); + printf("next=%x\n", c->next); + printf("saved_data=%x\n", c->saved_data); + printf("saved_data_buf=%x\n", c->saved_data_buf); + printf("eol=%x\n", (uint32_t) c->eol); } static void dump_d(int ch, struct dma_descr_data *d) { - printf("%s ch=%d\n", __func__, ch); - printf("next=%x\n", d->next); - printf("buf=%x\n", d->buf); - printf("after=%x\n", d->after); - printf("intr=%x\n", (uint32_t) d->intr); - printf("out_eop=%x\n", (uint32_t) d->out_eop); - printf("in_eop=%x\n", (uint32_t) d->in_eop); - printf("eol=%x\n", (uint32_t) d->eol); + printf("%s ch=%d\n", __func__, ch); + printf("next=%x\n", d->next); + printf("buf=%x\n", d->buf); + printf("after=%x\n", d->after); + printf("intr=%x\n", (uint32_t) d->intr); + printf("out_eop=%x\n", (uint32_t) d->out_eop); + printf("in_eop=%x\n", (uint32_t) d->in_eop); + printf("eol=%x\n", (uint32_t) d->eol); } #endif static void channel_load_c(struct fs_dma_ctrl *ctrl, int c) { - hwaddr addr = channel_reg(ctrl, c, RW_GROUP_DOWN); + hwaddr addr = channel_reg(ctrl, c, RW_GROUP_DOWN); - /* Load and decode. FIXME: handle endianness. */ + /* Load and decode. FIXME: handle endianness. */ 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. */ - ctrl->channels[c].regs[RW_SAVED_DATA] = - (uint32_t)(unsigned long)ctrl->channels[c].current_c.saved_data; - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = - (uint32_t)(unsigned long)ctrl->channels[c].current_c.saved_data_buf; + D(dump_c(c, &ctrl->channels[c].current_c)); + /* I guess this should update the current pos. */ + ctrl->channels[c].regs[RW_SAVED_DATA] = + (uint32_t)(unsigned long)ctrl->channels[c].current_c.saved_data; + ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = + (uint32_t)(unsigned long)ctrl->channels[c].current_c.saved_data_buf; } static void channel_load_d(struct fs_dma_ctrl *ctrl, int c) { - hwaddr addr = channel_reg(ctrl, c, RW_SAVED_DATA); + hwaddr addr = channel_reg(ctrl, c, RW_SAVED_DATA); - /* Load and decode. FIXME: handle endianness. */ - D(printf("%s ch=%d addr=" TARGET_FMT_plx "\n", __func__, c, addr)); + /* Load and decode. FIXME: handle endianness. */ + D(printf("%s ch=%d addr=" HWADDR_FMT_plx "\n", __func__, c, addr)); 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; + D(dump_d(c, &ctrl->channels[c].current_d)); + ctrl->channels[c].regs[RW_DATA] = addr; } static void channel_store_c(struct fs_dma_ctrl *ctrl, int c) { - hwaddr addr = channel_reg(ctrl, c, RW_GROUP_DOWN); + hwaddr addr = channel_reg(ctrl, c, RW_GROUP_DOWN); - /* 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)); + /* Encode and store. FIXME: handle endianness. */ + D(printf("%s ch=%d addr=" HWADDR_FMT_plx "\n", __func__, c, addr)); + D(dump_d(c, &ctrl->channels[c].current_d)); 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) { - hwaddr addr = channel_reg(ctrl, c, RW_SAVED_DATA); + hwaddr addr = channel_reg(ctrl, c, RW_SAVED_DATA); - /* Encode and store. FIXME: handle endianness. */ - D(printf("%s ch=%d addr=" TARGET_FMT_plx "\n", __func__, c, addr)); + /* Encode and store. FIXME: handle endianness. */ + D(printf("%s ch=%d addr=" HWADDR_FMT_plx "\n", __func__, c, addr)); 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) { - /* FIXME: */ + /* FIXME: */ } static inline void channel_start(struct fs_dma_ctrl *ctrl, int c) { - if (ctrl->channels[c].client) - { - ctrl->channels[c].eol = 0; - ctrl->channels[c].state = RUNNING; - if (!ctrl->channels[c].input) - channel_out_run(ctrl, c); - } else - printf("WARNING: starting DMA ch %d with no client\n", c); + if (ctrl->channels[c].client) + { + ctrl->channels[c].eol = 0; + ctrl->channels[c].state = RUNNING; + if (!ctrl->channels[c].input) + channel_out_run(ctrl, c); + } else + printf("WARNING: starting DMA ch %d with no client\n", c); - qemu_bh_schedule_idle(ctrl->bh); + qemu_bh_schedule_idle(ctrl->bh); } static void channel_continue(struct fs_dma_ctrl *ctrl, int c) { - if (!channel_en(ctrl, c) - || channel_stopped(ctrl, c) - || ctrl->channels[c].state != RUNNING - /* Only reload the current data descriptor if it has eol set. */ - || !ctrl->channels[c].current_d.eol) { - D(printf("continue failed ch=%d state=%d stopped=%d en=%d eol=%d\n", - c, ctrl->channels[c].state, - channel_stopped(ctrl, c), - channel_en(ctrl,c), - ctrl->channels[c].eol)); - D(dump_d(c, &ctrl->channels[c].current_d)); - return; - } + if (!channel_en(ctrl, c) + || channel_stopped(ctrl, c) + || ctrl->channels[c].state != RUNNING + /* Only reload the current data descriptor if it has eol set. */ + || !ctrl->channels[c].current_d.eol) { + D(printf("continue failed ch=%d state=%d stopped=%d en=%d eol=%d\n", + c, ctrl->channels[c].state, + channel_stopped(ctrl, c), + channel_en(ctrl,c), + ctrl->channels[c].eol)); + D(dump_d(c, &ctrl->channels[c].current_d)); + return; + } - /* Reload the current descriptor. */ - channel_load_d(ctrl, c); + /* Reload the current descriptor. */ + channel_load_d(ctrl, c); - /* If the current descriptor cleared the eol flag and we had already - reached eol state, do the continue. */ - if (!ctrl->channels[c].current_d.eol && ctrl->channels[c].eol) { - D(printf("continue %d ok %x\n", c, - ctrl->channels[c].current_d.next)); - ctrl->channels[c].regs[RW_SAVED_DATA] = - (uint32_t)(unsigned long)ctrl->channels[c].current_d.next; - channel_load_d(ctrl, c); - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = - (uint32_t)(unsigned long)ctrl->channels[c].current_d.buf; + /* If the current descriptor cleared the eol flag and we had already + reached eol state, do the continue. */ + if (!ctrl->channels[c].current_d.eol && ctrl->channels[c].eol) { + D(printf("continue %d ok %x\n", c, + ctrl->channels[c].current_d.next)); + ctrl->channels[c].regs[RW_SAVED_DATA] = + (uint32_t)(unsigned long)ctrl->channels[c].current_d.next; + channel_load_d(ctrl, c); + ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = + (uint32_t)(unsigned long)ctrl->channels[c].current_d.buf; - channel_start(ctrl, c); - } - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = - (uint32_t)(unsigned long)ctrl->channels[c].current_d.buf; + channel_start(ctrl, c); + } + ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = + (uint32_t)(unsigned long)ctrl->channels[c].current_d.buf; } static void channel_stream_cmd(struct fs_dma_ctrl *ctrl, int c, uint32_t v) { - unsigned int cmd = v & ((1 << 10) - 1); + unsigned int cmd = v & ((1 << 10) - 1); - D(printf("%s ch=%d cmd=%x\n", - __func__, c, cmd)); - if (cmd & regk_dma_load_d) { - channel_load_d(ctrl, c); - if (cmd & regk_dma_burst) - channel_start(ctrl, c); - } + D(printf("%s ch=%d cmd=%x\n", + __func__, c, cmd)); + if (cmd & regk_dma_load_d) { + channel_load_d(ctrl, c); + if (cmd & regk_dma_burst) + channel_start(ctrl, c); + } - if (cmd & regk_dma_load_c) { - channel_load_c(ctrl, c); - } + if (cmd & regk_dma_load_c) { + channel_load_c(ctrl, c); + } } static void channel_update_irq(struct fs_dma_ctrl *ctrl, int c) { - D(printf("%s %d\n", __func__, c)); - ctrl->channels[c].regs[R_INTR] &= - ~(ctrl->channels[c].regs[RW_ACK_INTR]); + D(printf("%s %d\n", __func__, c)); + ctrl->channels[c].regs[R_INTR] &= + ~(ctrl->channels[c].regs[RW_ACK_INTR]); - ctrl->channels[c].regs[R_MASKED_INTR] = - ctrl->channels[c].regs[R_INTR] - & ctrl->channels[c].regs[RW_INTR_MASK]; + ctrl->channels[c].regs[R_MASKED_INTR] = + ctrl->channels[c].regs[R_INTR] + & ctrl->channels[c].regs[RW_INTR_MASK]; - D(printf("%s: chan=%d masked_intr=%x\n", __func__, - c, - ctrl->channels[c].regs[R_MASKED_INTR])); + D(printf("%s: chan=%d masked_intr=%x\n", __func__, + c, + ctrl->channels[c].regs[R_MASKED_INTR])); - qemu_set_irq(ctrl->channels[c].irq, - !!ctrl->channels[c].regs[R_MASKED_INTR]); + qemu_set_irq(ctrl->channels[c].irq, + !!ctrl->channels[c].regs[R_MASKED_INTR]); } static int channel_out_run(struct fs_dma_ctrl *ctrl, int c) { - uint32_t len; - uint32_t saved_data_buf; - unsigned char buf[2 * 1024]; + uint32_t len; + uint32_t saved_data_buf; + unsigned char buf[2 * 1024]; - struct dma_context_metadata meta; - bool send_context = true; + struct dma_context_metadata meta; + bool send_context = true; - if (ctrl->channels[c].eol) - return 0; + if (ctrl->channels[c].eol) + return 0; - do { - bool out_eop; - D(printf("ch=%d buf=%x after=%x\n", - c, - (uint32_t)ctrl->channels[c].current_d.buf, - (uint32_t)ctrl->channels[c].current_d.after)); + do { + bool out_eop; + D(printf("ch=%d buf=%x after=%x\n", + c, + (uint32_t)ctrl->channels[c].current_d.buf, + (uint32_t)ctrl->channels[c].current_d.after)); - if (send_context) { - if (ctrl->channels[c].client->client.metadata_push) { - meta.metadata = ctrl->channels[c].current_d.md; - ctrl->channels[c].client->client.metadata_push( - ctrl->channels[c].client->client.opaque, - &meta); - } - send_context = false; - } + if (send_context) { + if (ctrl->channels[c].client->client.metadata_push) { + meta.metadata = ctrl->channels[c].current_d.md; + ctrl->channels[c].client->client.metadata_push( + ctrl->channels[c].client->client.opaque, + &meta); + } + send_context = false; + } - channel_load_d(ctrl, c); - saved_data_buf = channel_reg(ctrl, c, RW_SAVED_DATA_BUF); - len = (uint32_t)(unsigned long) - ctrl->channels[c].current_d.after; - len -= saved_data_buf; + channel_load_d(ctrl, c); + saved_data_buf = channel_reg(ctrl, c, RW_SAVED_DATA_BUF); + len = (uint32_t)(unsigned long) + ctrl->channels[c].current_d.after; + len -= saved_data_buf; - if (len > sizeof buf) - len = sizeof buf; - cpu_physical_memory_read (saved_data_buf, buf, len); + if (len > sizeof buf) + len = sizeof buf; + cpu_physical_memory_read (saved_data_buf, buf, len); - out_eop = ((saved_data_buf + len) == - ctrl->channels[c].current_d.after) && - ctrl->channels[c].current_d.out_eop; + out_eop = ((saved_data_buf + len) == + ctrl->channels[c].current_d.after) && + ctrl->channels[c].current_d.out_eop; - D(printf("channel %d pushes %x %u bytes eop=%u\n", c, - saved_data_buf, len, out_eop)); + D(printf("channel %d pushes %x %u bytes eop=%u\n", c, + saved_data_buf, len, out_eop)); - if (ctrl->channels[c].client->client.push) { - if (len > 0) { - ctrl->channels[c].client->client.push( - ctrl->channels[c].client->client.opaque, - buf, len, out_eop); - } - } else { - printf("WARNING: DMA ch%d dataloss," - " no attached client.\n", c); - } + if (ctrl->channels[c].client->client.push) { + if (len > 0) { + ctrl->channels[c].client->client.push( + ctrl->channels[c].client->client.opaque, + buf, len, out_eop); + } + } else { + printf("WARNING: DMA ch%d dataloss," + " no attached client.\n", c); + } - saved_data_buf += len; + saved_data_buf += len; - if (saved_data_buf == (uint32_t)(unsigned long) - ctrl->channels[c].current_d.after) { - /* Done. Step to next. */ - if (ctrl->channels[c].current_d.out_eop) { - send_context = true; - } - if (ctrl->channels[c].current_d.intr) { - /* data intr. */ - D(printf("signal intr %d eol=%d\n", - len, ctrl->channels[c].current_d.eol)); - ctrl->channels[c].regs[R_INTR] |= (1 << 2); - channel_update_irq(ctrl, c); - } - channel_store_d(ctrl, c); - if (ctrl->channels[c].current_d.eol) { - D(printf("channel %d EOL\n", c)); - ctrl->channels[c].eol = 1; + if (saved_data_buf == (uint32_t)(unsigned long) + ctrl->channels[c].current_d.after) { + /* Done. Step to next. */ + if (ctrl->channels[c].current_d.out_eop) { + send_context = true; + } + if (ctrl->channels[c].current_d.intr) { + /* data intr. */ + D(printf("signal intr %d eol=%d\n", + len, ctrl->channels[c].current_d.eol)); + ctrl->channels[c].regs[R_INTR] |= (1 << 2); + channel_update_irq(ctrl, c); + } + channel_store_d(ctrl, c); + if (ctrl->channels[c].current_d.eol) { + D(printf("channel %d EOL\n", c)); + ctrl->channels[c].eol = 1; - /* Mark the context as disabled. */ - ctrl->channels[c].current_c.dis = 1; - channel_store_c(ctrl, c); + /* Mark the context as disabled. */ + ctrl->channels[c].current_c.dis = 1; + channel_store_c(ctrl, c); - channel_stop(ctrl, c); - } else { - ctrl->channels[c].regs[RW_SAVED_DATA] = - (uint32_t)(unsigned long)ctrl-> - channels[c].current_d.next; - /* Load new descriptor. */ - channel_load_d(ctrl, c); - saved_data_buf = (uint32_t)(unsigned long) - ctrl->channels[c].current_d.buf; - } + channel_stop(ctrl, c); + } else { + ctrl->channels[c].regs[RW_SAVED_DATA] = + (uint32_t)(unsigned long)ctrl-> + channels[c].current_d.next; + /* Load new descriptor. */ + channel_load_d(ctrl, c); + saved_data_buf = (uint32_t)(unsigned long) + ctrl->channels[c].current_d.buf; + } - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = - saved_data_buf; - D(dump_d(c, &ctrl->channels[c].current_d)); - } - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = saved_data_buf; - } while (!ctrl->channels[c].eol); - return 1; + ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = + saved_data_buf; + D(dump_d(c, &ctrl->channels[c].current_d)); + } + ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = saved_data_buf; + } while (!ctrl->channels[c].eol); + return 1; } static int channel_in_process(struct fs_dma_ctrl *ctrl, int c, - unsigned char *buf, int buflen, int eop) + unsigned char *buf, int buflen, int eop) { - uint32_t len; - uint32_t saved_data_buf; + uint32_t len; + uint32_t saved_data_buf; - if (ctrl->channels[c].eol == 1) - return 0; + if (ctrl->channels[c].eol == 1) + return 0; - channel_load_d(ctrl, c); - saved_data_buf = channel_reg(ctrl, c, RW_SAVED_DATA_BUF); - len = (uint32_t)(unsigned long)ctrl->channels[c].current_d.after; - len -= saved_data_buf; - - if (len > buflen) - len = buflen; + channel_load_d(ctrl, c); + saved_data_buf = channel_reg(ctrl, c, RW_SAVED_DATA_BUF); + len = (uint32_t)(unsigned long)ctrl->channels[c].current_d.after; + len -= saved_data_buf; - cpu_physical_memory_write (saved_data_buf, buf, len); - saved_data_buf += len; + if (len > buflen) + len = buflen; - if (saved_data_buf == - (uint32_t)(unsigned long)ctrl->channels[c].current_d.after - || eop) { - uint32_t r_intr = ctrl->channels[c].regs[R_INTR]; + cpu_physical_memory_write (saved_data_buf, buf, len); + saved_data_buf += len; - D(printf("in dscr end len=%d\n", - ctrl->channels[c].current_d.after - - ctrl->channels[c].current_d.buf)); - ctrl->channels[c].current_d.after = saved_data_buf; + if (saved_data_buf == + (uint32_t)(unsigned long)ctrl->channels[c].current_d.after + || eop) { + uint32_t r_intr = ctrl->channels[c].regs[R_INTR]; - /* Done. Step to next. */ - if (ctrl->channels[c].current_d.intr) { - /* TODO: signal eop to the client. */ - /* data intr. */ - ctrl->channels[c].regs[R_INTR] |= 3; - } - if (eop) { - ctrl->channels[c].current_d.in_eop = 1; - ctrl->channels[c].regs[R_INTR] |= 8; - } - if (r_intr != ctrl->channels[c].regs[R_INTR]) - channel_update_irq(ctrl, c); + D(printf("in dscr end len=%d\n", + ctrl->channels[c].current_d.after + - ctrl->channels[c].current_d.buf)); + ctrl->channels[c].current_d.after = saved_data_buf; - channel_store_d(ctrl, c); - D(dump_d(c, &ctrl->channels[c].current_d)); + /* Done. Step to next. */ + if (ctrl->channels[c].current_d.intr) { + /* TODO: signal eop to the client. */ + /* data intr. */ + ctrl->channels[c].regs[R_INTR] |= 3; + } + if (eop) { + ctrl->channels[c].current_d.in_eop = 1; + ctrl->channels[c].regs[R_INTR] |= 8; + } + if (r_intr != ctrl->channels[c].regs[R_INTR]) + channel_update_irq(ctrl, c); - if (ctrl->channels[c].current_d.eol) { - D(printf("channel %d EOL\n", c)); - ctrl->channels[c].eol = 1; + channel_store_d(ctrl, c); + D(dump_d(c, &ctrl->channels[c].current_d)); - /* Mark the context as disabled. */ - ctrl->channels[c].current_c.dis = 1; - channel_store_c(ctrl, c); + if (ctrl->channels[c].current_d.eol) { + D(printf("channel %d EOL\n", c)); + ctrl->channels[c].eol = 1; - channel_stop(ctrl, c); - } else { - ctrl->channels[c].regs[RW_SAVED_DATA] = - (uint32_t)(unsigned long)ctrl-> - channels[c].current_d.next; - /* Load new descriptor. */ - channel_load_d(ctrl, c); - saved_data_buf = (uint32_t)(unsigned long) - ctrl->channels[c].current_d.buf; - } - } + /* Mark the context as disabled. */ + ctrl->channels[c].current_c.dis = 1; + channel_store_c(ctrl, c); - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = saved_data_buf; - return len; + channel_stop(ctrl, c); + } else { + ctrl->channels[c].regs[RW_SAVED_DATA] = + (uint32_t)(unsigned long)ctrl-> + channels[c].current_d.next; + /* Load new descriptor. */ + channel_load_d(ctrl, c); + saved_data_buf = (uint32_t)(unsigned long) + ctrl->channels[c].current_d.buf; + } + } + + ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = saved_data_buf; + return len; } static inline int channel_in_run(struct fs_dma_ctrl *ctrl, int c) { - if (ctrl->channels[c].client->client.pull) { - ctrl->channels[c].client->client.pull( - ctrl->channels[c].client->client.opaque); - return 1; - } else - return 0; + if (ctrl->channels[c].client->client.pull) { + ctrl->channels[c].client->client.pull( + ctrl->channels[c].client->client.opaque); + return 1; + } else + return 0; } static uint32_t dma_rinvalid (void *opaque, hwaddr addr) { - hw_error("Unsupported short raccess. reg=" TARGET_FMT_plx "\n", addr); - return 0; + hw_error("Unsupported short raccess. reg=" HWADDR_FMT_plx "\n", addr); + return 0; } static uint64_t dma_read(void *opaque, hwaddr addr, unsigned int size) { - struct fs_dma_ctrl *ctrl = opaque; - int c; - uint32_t r = 0; + struct fs_dma_ctrl *ctrl = opaque; + int c; + uint32_t r = 0; - if (size != 4) { - dma_rinvalid(opaque, addr); - } + if (size != 4) { + dma_rinvalid(opaque, addr); + } - /* Make addr relative to this channel and bounded to nr regs. */ - c = fs_channel(addr); - addr &= 0xff; - addr >>= 2; - switch (addr) - { - case RW_STAT: - r = ctrl->channels[c].state & 7; - r |= ctrl->channels[c].eol << 5; - r |= ctrl->channels[c].stream_cmd_src << 8; - break; + /* Make addr relative to this channel and bounded to nr regs. */ + c = fs_channel(addr); + addr &= 0xff; + addr >>= 2; + switch (addr) + { + case RW_STAT: + r = ctrl->channels[c].state & 7; + r |= ctrl->channels[c].eol << 5; + r |= ctrl->channels[c].stream_cmd_src << 8; + break; - default: - r = ctrl->channels[c].regs[addr]; - D(printf ("%s c=%d addr=" TARGET_FMT_plx "\n", - __func__, c, addr)); - break; - } - return r; + default: + r = ctrl->channels[c].regs[addr]; + D(printf("%s c=%d addr=" HWADDR_FMT_plx "\n", + __func__, c, addr)); + break; + } + return r; } static void dma_winvalid (void *opaque, hwaddr addr, uint32_t value) { - hw_error("Unsupported short waccess. reg=" TARGET_FMT_plx "\n", addr); + hw_error("Unsupported short waccess. reg=" HWADDR_FMT_plx "\n", addr); } static void dma_update_state(struct fs_dma_ctrl *ctrl, int c) { - if (ctrl->channels[c].regs[RW_CFG] & 2) - ctrl->channels[c].state = STOPPED; - if (!(ctrl->channels[c].regs[RW_CFG] & 1)) - ctrl->channels[c].state = RST; + if (ctrl->channels[c].regs[RW_CFG] & 2) + ctrl->channels[c].state = STOPPED; + if (!(ctrl->channels[c].regs[RW_CFG] & 1)) + ctrl->channels[c].state = RST; } static void dma_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) + uint64_t val64, unsigned int size) { - struct fs_dma_ctrl *ctrl = opaque; - uint32_t value = val64; - int c; + struct fs_dma_ctrl *ctrl = opaque; + uint32_t value = val64; + int c; - if (size != 4) { - dma_winvalid(opaque, addr, value); - } + if (size != 4) { + dma_winvalid(opaque, addr, value); + } /* Make addr relative to this channel and bounded to nr regs. */ - c = fs_channel(addr); - addr &= 0xff; - addr >>= 2; - switch (addr) - { - case RW_DATA: - ctrl->channels[c].regs[addr] = value; - break; + c = fs_channel(addr); + addr &= 0xff; + addr >>= 2; + switch (addr) + { + case RW_DATA: + ctrl->channels[c].regs[addr] = value; + break; - case RW_CFG: - ctrl->channels[c].regs[addr] = value; - dma_update_state(ctrl, c); - break; - case RW_CMD: - /* continue. */ - if (value & ~1) - printf("Invalid store to ch=%d RW_CMD %x\n", - c, value); - ctrl->channels[c].regs[addr] = value; - channel_continue(ctrl, c); - break; + case RW_CFG: + ctrl->channels[c].regs[addr] = value; + dma_update_state(ctrl, c); + break; + case RW_CMD: + /* continue. */ + if (value & ~1) + printf("Invalid store to ch=%d RW_CMD %x\n", + c, value); + ctrl->channels[c].regs[addr] = value; + channel_continue(ctrl, c); + break; - case RW_SAVED_DATA: - case RW_SAVED_DATA_BUF: - case RW_GROUP: - case RW_GROUP_DOWN: - ctrl->channels[c].regs[addr] = value; - break; + case RW_SAVED_DATA: + case RW_SAVED_DATA_BUF: + case RW_GROUP: + case RW_GROUP_DOWN: + ctrl->channels[c].regs[addr] = value; + break; - case RW_ACK_INTR: - case RW_INTR_MASK: - ctrl->channels[c].regs[addr] = value; - channel_update_irq(ctrl, c); - if (addr == RW_ACK_INTR) - ctrl->channels[c].regs[RW_ACK_INTR] = 0; - break; + case RW_ACK_INTR: + case RW_INTR_MASK: + ctrl->channels[c].regs[addr] = value; + channel_update_irq(ctrl, c); + if (addr == RW_ACK_INTR) + ctrl->channels[c].regs[RW_ACK_INTR] = 0; + break; - case RW_STREAM_CMD: - if (value & ~1023) - printf("Invalid store to ch=%d " - "RW_STREAMCMD %x\n", - c, value); - ctrl->channels[c].regs[addr] = value; - D(printf("stream_cmd ch=%d\n", c)); - channel_stream_cmd(ctrl, c, value); - break; + case RW_STREAM_CMD: + if (value & ~1023) + printf("Invalid store to ch=%d " + "RW_STREAMCMD %x\n", + c, value); + ctrl->channels[c].regs[addr] = value; + D(printf("stream_cmd ch=%d\n", c)); + channel_stream_cmd(ctrl, c, value); + break; - default: - D(printf ("%s c=%d " TARGET_FMT_plx "\n", - __func__, c, addr)); - break; - } + default: + D(printf("%s c=%d " HWADDR_FMT_plx "\n", + __func__, c, addr)); + break; + } } static const MemoryRegionOps dma_ops = { - .read = dma_read, - .write = dma_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4 - } + .read = dma_read, + .write = dma_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4 + } }; static int etraxfs_dmac_run(void *opaque) { - struct fs_dma_ctrl *ctrl = opaque; - int i; - int p = 0; + struct fs_dma_ctrl *ctrl = opaque; + int i; + int p = 0; - for (i = 0; - i < ctrl->nr_channels; - i++) - { - if (ctrl->channels[i].state == RUNNING) - { - if (ctrl->channels[i].input) { - p += channel_in_run(ctrl, i); - } else { - p += channel_out_run(ctrl, i); - } - } - } - return p; + for (i = 0; + i < ctrl->nr_channels; + i++) + { + if (ctrl->channels[i].state == RUNNING) + { + if (ctrl->channels[i].input) { + p += channel_in_run(ctrl, i); + } else { + p += channel_out_run(ctrl, i); + } + } + } + return p; } int etraxfs_dmac_input(struct etraxfs_dma_client *client, - void *buf, int len, int eop) + void *buf, int len, int eop) { - return channel_in_process(client->ctrl, client->channel, - buf, len, eop); + return channel_in_process(client->ctrl, client->channel, + buf, len, eop); } /* Connect an IRQ line with a channel. */ void etraxfs_dmac_connect(void *opaque, int c, qemu_irq *line, int input) { - struct fs_dma_ctrl *ctrl = opaque; - ctrl->channels[c].irq = *line; - ctrl->channels[c].input = input; + struct fs_dma_ctrl *ctrl = opaque; + ctrl->channels[c].irq = *line; + ctrl->channels[c].input = input; } void etraxfs_dmac_connect_client(void *opaque, int c, - struct etraxfs_dma_client *cl) + struct etraxfs_dma_client *cl) { - struct fs_dma_ctrl *ctrl = opaque; - cl->ctrl = ctrl; - cl->channel = c; - ctrl->channels[c].client = cl; + struct fs_dma_ctrl *ctrl = opaque; + cl->ctrl = ctrl; + cl->channel = c; + ctrl->channels[c].client = cl; } @@ -763,18 +764,18 @@ static void DMA_run(void *opaque) void *etraxfs_dmac_init(hwaddr base, int nr_channels) { - struct fs_dma_ctrl *ctrl = NULL; + struct fs_dma_ctrl *ctrl = NULL; - ctrl = g_malloc0(sizeof *ctrl); + ctrl = g_malloc0(sizeof *ctrl); - ctrl->bh = qemu_bh_new(DMA_run, ctrl); + ctrl->bh = qemu_bh_new(DMA_run, ctrl); - ctrl->nr_channels = nr_channels; - ctrl->channels = g_malloc0(sizeof ctrl->channels[0] * nr_channels); + ctrl->nr_channels = nr_channels; + ctrl->channels = g_malloc0(sizeof ctrl->channels[0] * nr_channels); - memory_region_init_io(&ctrl->mmio, NULL, &dma_ops, ctrl, "etraxfs-dma", - nr_channels * 0x2000); - memory_region_add_subregion(get_system_memory(), base, &ctrl->mmio); + memory_region_init_io(&ctrl->mmio, NULL, &dma_ops, ctrl, "etraxfs-dma", + nr_channels * 0x2000); + memory_region_add_subregion(get_system_memory(), base, &ctrl->mmio); - return ctrl; + return ctrl; } diff --git a/hw/dma/i82374.c b/hw/dma/i82374.c index 34c3aaf7d3..e72aa2e1ce 100644 --- a/hw/dma/i82374.c +++ b/hw/dma/i82374.c @@ -58,7 +58,7 @@ static const VMStateDescription vmstate_i82374 = { .name = "i82374", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(commands, I82374State, 8), VMSTATE_END_OF_LIST() }, @@ -125,11 +125,11 @@ static void i82374_realize(DeviceState *dev, Error **errp) I82374State *s = I82374(dev); ISABus *isa_bus = isa_bus_from_device(ISA_DEVICE(dev)); - if (isa_get_dma(isa_bus, 0)) { + if (isa_bus_get_dma(isa_bus, 0)) { error_setg(errp, "DMA already initialized on ISA bus"); return; } - i8257_dma_init(isa_bus, true); + i8257_dma_init(OBJECT(dev), isa_bus, true); portio_list_init(&s->port_list, OBJECT(s), i82374_portio_list, s, "i82374"); diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c index de5f696919..24a54ca272 100644 --- a/hw/dma/i8257.c +++ b/hw/dma/i8257.c @@ -517,7 +517,7 @@ static const VMStateDescription vmstate_i8257_regs = { .name = "dma_regs", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32_ARRAY(now, I8257Regs, 2), VMSTATE_UINT16_ARRAY(base, I8257Regs, 2), VMSTATE_UINT8(mode, I8257Regs), @@ -542,7 +542,7 @@ static const VMStateDescription vmstate_i8257 = { .version_id = 1, .minimum_version_id = 1, .post_load = i8257_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(command, I8257State), VMSTATE_UINT8(mask, I8257State), VMSTATE_UINT8(flip_flop, I8257State), @@ -632,12 +632,13 @@ static void i8257_register_types(void) type_init(i8257_register_types) -void i8257_dma_init(ISABus *bus, bool high_page_enable) +void i8257_dma_init(Object *parent, ISABus *bus, bool high_page_enable) { ISADevice *isa1, *isa2; DeviceState *d; isa1 = isa_new(TYPE_I8257); + object_property_add_child(parent, "dma[*]", OBJECT(isa1)); d = DEVICE(isa1); qdev_prop_set_int32(d, "base", 0x00); qdev_prop_set_int32(d, "page-base", 0x80); @@ -646,6 +647,7 @@ void i8257_dma_init(ISABus *bus, bool high_page_enable) isa_realize_and_unref(isa1, bus, &error_fatal); isa2 = isa_new(TYPE_I8257); + object_property_add_child(parent, "dma[*]", OBJECT(isa2)); d = DEVICE(isa2); qdev_prop_set_int32(d, "base", 0xc0); qdev_prop_set_int32(d, "page-base", 0x88); diff --git a/hw/dma/meson.build b/hw/dma/meson.build index f3f0661bc3..a96c1be2c8 100644 --- a/hw/dma/meson.build +++ b/hw/dma/meson.build @@ -1,16 +1,16 @@ -softmmu_ss.add(when: 'CONFIG_RC4030', if_true: files('rc4030.c')) -softmmu_ss.add(when: 'CONFIG_PL080', if_true: files('pl080.c')) -softmmu_ss.add(when: 'CONFIG_PL330', if_true: files('pl330.c')) -softmmu_ss.add(when: 'CONFIG_I82374', if_true: files('i82374.c')) -softmmu_ss.add(when: 'CONFIG_I8257', if_true: files('i8257.c')) -softmmu_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('xilinx_axidma.c')) -softmmu_ss.add(when: 'CONFIG_ZYNQ_DEVCFG', if_true: files('xlnx-zynq-devcfg.c')) -softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_dma.c')) -softmmu_ss.add(when: 'CONFIG_STP2000', if_true: files('sparc32_dma.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx_dpdma.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZDMA', if_true: files('xlnx-zdma.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dma.c', 'soc_dma.c')) -softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_dma.c')) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_CSU_DMA', if_true: files('xlnx_csu_dma.c')) +system_ss.add(when: 'CONFIG_RC4030', if_true: files('rc4030.c')) +system_ss.add(when: 'CONFIG_PL080', if_true: files('pl080.c')) +system_ss.add(when: 'CONFIG_PL330', if_true: files('pl330.c')) +system_ss.add(when: 'CONFIG_I82374', if_true: files('i82374.c')) +system_ss.add(when: 'CONFIG_I8257', if_true: files('i8257.c')) +system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('xilinx_axidma.c')) +system_ss.add(when: 'CONFIG_ZYNQ_DEVCFG', if_true: files('xlnx-zynq-devcfg.c')) +system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_dma.c')) +system_ss.add(when: 'CONFIG_STP2000', if_true: files('sparc32_dma.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx_dpdma.c')) +system_ss.add(when: 'CONFIG_XLNX_ZDMA', if_true: files('xlnx-zdma.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dma.c', 'soc_dma.c')) +system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_dma.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c')) +system_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c')) +system_ss.add(when: 'CONFIG_XLNX_CSU_DMA', if_true: files('xlnx_csu_dma.c')) diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index 6677237d42..77797a67b5 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -247,7 +247,7 @@ static void omap_dma_deactivate_channel(struct omap_dma_s *s, return; } - /* Don't deactive the channel if it is synchronized and the DMA request is + /* Don't deactivate the channel if it is synchronized and the DMA request is active */ if (ch->sync && ch->enable && (s->dma->drqbmp & (1ULL << ch->sync))) return; @@ -422,7 +422,7 @@ static void omap_dma_transfer_generic(struct soc_dma_ch_s *dma) if (ch->fs && ch->bs) { a->pck_element ++; - /* Check if a full packet has beed transferred. */ + /* Check if a full packet has been transferred. */ if (a->pck_element == a->pck_elements) { a->pck_element = 0; @@ -1454,10 +1454,9 @@ static int omap_dma_sys_read(struct omap_dma_s *s, int offset, return 0; } -static uint64_t omap_dma_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_dma_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_dma_s *s = (struct omap_dma_s *) opaque; + struct omap_dma_s *s = opaque; int reg, ch; uint16_t ret; @@ -1505,7 +1504,7 @@ static uint64_t omap_dma_read(void *opaque, hwaddr addr, static void omap_dma_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_dma_s *s = (struct omap_dma_s *) opaque; + struct omap_dma_s *s = opaque; int reg, ch; if (size != 2) { @@ -1557,7 +1556,7 @@ static const MemoryRegionOps omap_dma_ops = { static void omap_dma_request(void *opaque, int drq, int req) { - struct omap_dma_s *s = (struct omap_dma_s *) opaque; + struct omap_dma_s *s = opaque; /* The request pins are level triggered in QEMU. */ if (req) { if (~s->dma->drqbmp & (1ULL << drq)) { @@ -1571,7 +1570,7 @@ static void omap_dma_request(void *opaque, int drq, int req) /* XXX: this won't be needed once soc_dma knows about clocks. */ static void omap_dma_clk_update(void *opaque, int line, int on) { - struct omap_dma_s *s = (struct omap_dma_s *) opaque; + struct omap_dma_s *s = opaque; int i; s->dma->freq = omap_clk_getrate(s->clk); @@ -1703,7 +1702,7 @@ static void omap_dma_interrupts_4_update(struct omap_dma_s *s) static uint64_t omap_dma4_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_dma_s *s = (struct omap_dma_s *) opaque; + struct omap_dma_s *s = opaque; int irqn = 0, chnum; struct omap_dma_channel_s *ch; @@ -1859,7 +1858,7 @@ static uint64_t omap_dma4_read(void *opaque, hwaddr addr, static void omap_dma4_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_dma_s *s = (struct omap_dma_s *) opaque; + struct omap_dma_s *s = opaque; int chnum, irqn = 0; struct omap_dma_channel_s *ch; diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index 2627307cc8..1e49c22e93 100644 --- a/hw/dma/pl080.c +++ b/hw/dma/pl080.c @@ -39,7 +39,7 @@ static const VMStateDescription vmstate_pl080_channel = { .name = "pl080_channel", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(src, pl080_channel), VMSTATE_UINT32(dest, pl080_channel), VMSTATE_UINT32(lli, pl080_channel), @@ -53,7 +53,7 @@ static const VMStateDescription vmstate_pl080 = { .name = "pl080", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(tc_int, PL080State), VMSTATE_UINT8(tc_mask, PL080State), VMSTATE_UINT8(err_int, PL080State), diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c index 08e5938ec7..70a502d245 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -139,7 +139,7 @@ static const VMStateDescription vmstate_pl330_chan = { .name = "pl330_chan", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(src, PL330Chan), VMSTATE_UINT32(dst, PL330Chan), VMSTATE_UINT32(pc, PL330Chan), @@ -170,7 +170,7 @@ static const VMStateDescription vmstate_pl330_fifo = { .name = "pl330_chan", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VBUFFER_UINT32(buf, PL330Fifo, 1, NULL, buf_size), VMSTATE_VBUFFER_UINT32(tag, PL330Fifo, 1, NULL, buf_size), VMSTATE_UINT32(head, PL330Fifo), @@ -194,7 +194,7 @@ static const VMStateDescription vmstate_pl330_queue_entry = { .name = "pl330_queue_entry", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(addr, PL330QueueEntry), VMSTATE_UINT32(len, PL330QueueEntry), VMSTATE_UINT8(n, PL330QueueEntry), @@ -216,7 +216,7 @@ static const VMStateDescription vmstate_pl330_queue = { .name = "pl330_queue", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_VARRAY_POINTER_UINT32(queue, PL330Queue, queue_size, vmstate_pl330_queue_entry, PL330QueueEntry), @@ -280,7 +280,7 @@ static const VMStateDescription vmstate_pl330 = { .name = "pl330", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(manager, PL330State, 0, vmstate_pl330_chan, PL330Chan), VMSTATE_STRUCT_VARRAY_POINTER_UINT32(chan, PL330State, num_chnls, vmstate_pl330_chan, PL330Chan), @@ -1328,7 +1328,7 @@ static void pl330_debug_exec(PL330State *s) } if (!insn) { pl330_fault(ch, PL330_FAULT_UNDEF_INSTR | PL330_FAULT_DBG_INSTR); - return ; + return; } ch->stall = 0; insn->exec(ch, opcode, args, insn->size - 1); @@ -1373,7 +1373,7 @@ static void pl330_iomem_write(void *opaque, hwaddr offset, pl330_exec(s); } else { qemu_log_mask(LOG_GUEST_ERROR, "pl330: write of illegal value %u " - "for offset " TARGET_FMT_plx "\n", (unsigned)value, + "for offset " HWADDR_FMT_plx "\n", (unsigned)value, offset); } break; @@ -1384,7 +1384,7 @@ static void pl330_iomem_write(void *opaque, hwaddr offset, s->dbg[1] = value; break; default: - qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad write offset " TARGET_FMT_plx + qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad write offset " HWADDR_FMT_plx "\n", offset); break; } @@ -1409,7 +1409,7 @@ static inline uint32_t pl330_iomem_read_imp(void *opaque, chan_id = offset >> 5; if (chan_id >= s->num_chnls) { qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); + HWADDR_FMT_plx "\n", offset); return 0; } switch (offset & 0x1f) { @@ -1425,7 +1425,7 @@ static inline uint32_t pl330_iomem_read_imp(void *opaque, return s->chan[chan_id].lc[1]; default: qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); + HWADDR_FMT_plx "\n", offset); return 0; } } @@ -1434,7 +1434,7 @@ static inline uint32_t pl330_iomem_read_imp(void *opaque, chan_id = offset >> 3; if (chan_id >= s->num_chnls) { qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); + HWADDR_FMT_plx "\n", offset); return 0; } switch ((offset >> 2) & 1) { @@ -1456,7 +1456,7 @@ static inline uint32_t pl330_iomem_read_imp(void *opaque, chan_id = offset >> 2; if (chan_id >= s->num_chnls) { qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); + HWADDR_FMT_plx "\n", offset); return 0; } return s->chan[chan_id].fault_type; @@ -1495,7 +1495,7 @@ static inline uint32_t pl330_iomem_read_imp(void *opaque, return s->debug_status; default: qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); + HWADDR_FMT_plx "\n", offset); } return 0; } diff --git a/hw/dma/pxa2xx_dma.c b/hw/dma/pxa2xx_dma.c index fa896f7edf..9f62f0b633 100644 --- a/hw/dma/pxa2xx_dma.c +++ b/hw/dma/pxa2xx_dma.c @@ -529,7 +529,7 @@ static const VMStateDescription vmstate_pxa2xx_dma_chan = { .name = "pxa2xx_dma_chan", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(descr, PXA2xxDMAChannel), VMSTATE_UINT32(src, PXA2xxDMAChannel), VMSTATE_UINT32(dest, PXA2xxDMAChannel), @@ -544,7 +544,7 @@ static const VMStateDescription vmstate_pxa2xx_dma = { .name = "pxa2xx_dma", .version_id = 1, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UNUSED_TEST(is_version_0, 4), VMSTATE_UINT32(stopintr, PXA2xxDMAState), VMSTATE_UINT32(eorintr, PXA2xxDMAState), diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index aa1d323a36..915284194f 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -568,7 +568,7 @@ static const VMStateDescription vmstate_rc4030 = { .name = "rc4030", .version_id = 3, .post_load = rc4030_post_load, - .fields = (VMStateField []) { + .fields = (const VMStateField []) { VMSTATE_UINT32(config, rc4030State), VMSTATE_UINT32(invalid_address_register, rc4030State), VMSTATE_UINT32_2DARRAY(dma_regs, rc4030State, 8, 4), diff --git a/hw/dma/sparc32_dma.c b/hw/dma/sparc32_dma.c index 0ef13c5e9a..8019641942 100644 --- a/hw/dma/sparc32_dma.c +++ b/hw/dma/sparc32_dma.c @@ -249,7 +249,7 @@ static const VMStateDescription vmstate_sparc32_dma_device = { .name ="sparc32_dma", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(dmaregs, DMADeviceState, DMA_REGS), VMSTATE_END_OF_LIST() } diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index cbb8f0f169..0ae056ed06 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -168,6 +168,11 @@ static inline int stream_idle(struct Stream *s) return !!(s->regs[R_DMASR] & DMASR_IDLE); } +static inline int stream_halted(struct Stream *s) +{ + return !!(s->regs[R_DMASR] & DMASR_HALTED); +} + static void stream_reset(struct Stream *s) { s->regs[R_DMASR] = DMASR_HALTED; /* starts up halted. */ @@ -269,7 +274,7 @@ static void stream_process_mem2s(struct Stream *s, StreamSink *tx_data_dev, uint64_t addr; bool eop; - if (!stream_running(s) || stream_idle(s)) { + if (!stream_running(s) || stream_idle(s) || stream_halted(s)) { return; } @@ -326,7 +331,7 @@ static size_t stream_process_s2mem(struct Stream *s, unsigned char *buf, unsigned int rxlen; size_t pos = 0; - if (!stream_running(s) || stream_idle(s)) { + if (!stream_running(s) || stream_idle(s) || stream_halted(s)) { return 0; } @@ -407,7 +412,7 @@ xilinx_axidma_data_stream_can_push(StreamSink *obj, XilinxAXIDMAStreamSink *ds = XILINX_AXI_DMA_DATA_STREAM(obj); struct Stream *s = &ds->dma->streams[1]; - if (!stream_running(s) || stream_idle(s)) { + if (!stream_running(s) || stream_idle(s) || stream_halted(s)) { ds->dma->notify = notify; ds->dma->notify_opaque = notify_opaque; return false; @@ -456,7 +461,7 @@ static uint64_t axidma_read(void *opaque, hwaddr addr, break; default: r = s->regs[addr]; - D(qemu_log("%s ch=%d addr=" TARGET_FMT_plx " v=%x\n", + D(qemu_log("%s ch=%d addr=" HWADDR_FMT_plx " v=%x\n", __func__, sid, addr * 4, r)); break; } @@ -509,7 +514,7 @@ static void axidma_write(void *opaque, hwaddr addr, } break; default: - D(qemu_log("%s: ch=%d addr=" TARGET_FMT_plx " v=%x\n", + D(qemu_log("%s: ch=%d addr=" HWADDR_FMT_plx " v=%x\n", __func__, sid, addr * 4, (unsigned)value)); s->regs[addr] = value; break; @@ -572,10 +577,6 @@ static void xilinx_axidma_init(Object *obj) object_initialize_child(OBJECT(s), "axistream-control-connected-target", &s->rx_control_dev, TYPE_XILINX_AXI_DMA_CONTROL_STREAM); - object_property_add_link(obj, "dma", TYPE_MEMORY_REGION, - (Object **)&s->dma_mr, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_STRONG); sysbus_init_irq(sbd, &s->streams[0].irq); sysbus_init_irq(sbd, &s->streams[1].irq); @@ -591,6 +592,8 @@ static Property axidma_properties[] = { tx_data_dev, TYPE_STREAM_SINK, StreamSink *), DEFINE_PROP_LINK("axistream-control-connected", XilinxAXIDMA, tx_control_dev, TYPE_STREAM_SINK, StreamSink *), + DEFINE_PROP_LINK("dma", XilinxAXIDMA, dma_mr, + TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index 4eb7f66e9f..670c956866 100644 --- a/hw/dma/xlnx-zdma.c +++ b/hw/dma/xlnx-zdma.c @@ -795,18 +795,13 @@ static void zdma_init(Object *obj) TYPE_XLNX_ZDMA, ZDMA_R_MAX * 4); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq_zdma_ch_imr); - - object_property_add_link(obj, "dma", TYPE_MEMORY_REGION, - (Object **)&s->dma_mr, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_STRONG); } static const VMStateDescription vmstate_zdma = { .name = TYPE_XLNX_ZDMA, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxZDMA, ZDMA_R_MAX), VMSTATE_UINT32(state, XlnxZDMA), VMSTATE_UINT32_ARRAY(dsc_src.words, XlnxZDMA, 4), @@ -817,6 +812,8 @@ static const VMStateDescription vmstate_zdma = { static Property zdma_props[] = { DEFINE_PROP_UINT32("bus-width", XlnxZDMA, cfg.bus_width, 64), + DEFINE_PROP_LINK("dma", XlnxZDMA, dma_mr, + TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/dma/xlnx-zynq-devcfg.c b/hw/dma/xlnx-zynq-devcfg.c index f5ad1a0d22..e901f68ff3 100644 --- a/hw/dma/xlnx-zynq-devcfg.c +++ b/hw/dma/xlnx-zynq-devcfg.c @@ -333,7 +333,7 @@ static const VMStateDescription vmstate_xlnx_zynq_devcfg_dma_cmd = { .name = "xlnx_zynq_devcfg_dma_cmd", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(src_addr, XlnxZynqDevcfgDMACmd), VMSTATE_UINT32(dest_addr, XlnxZynqDevcfgDMACmd), VMSTATE_UINT32(src_len, XlnxZynqDevcfgDMACmd), @@ -346,7 +346,7 @@ static const VMStateDescription vmstate_xlnx_zynq_devcfg = { .name = "xlnx_zynq_devcfg", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(dma_cmd_fifo, XlnxZynqDevcfg, XLNX_ZYNQ_DEVCFG_DMA_CMD_FIFO_LEN, 0, vmstate_xlnx_zynq_devcfg_dma_cmd, diff --git a/hw/dma/xlnx_csu_dma.c b/hw/dma/xlnx_csu_dma.c index 1ce52ea5a2..ae307482f2 100644 --- a/hw/dma/xlnx_csu_dma.c +++ b/hw/dma/xlnx_csu_dma.c @@ -33,13 +33,13 @@ /* * Ref: UG1087 (v1.7) February 8, 2019 - * https://www.xilinx.com/html_docs/registers/ug1087/ug1087-zynq-ultrascale-registers.html + * https://www.xilinx.com/html_docs/registers/ug1087/ug1087-zynq-ultrascale-registers * CSUDMA Module section */ REG32(ADDR, 0x0) FIELD(ADDR, ADDR, 2, 30) /* wo */ REG32(SIZE, 0x4) - FIELD(SIZE, SIZE, 2, 27) /* wo */ + FIELD(SIZE, SIZE, 2, 27) FIELD(SIZE, LAST_WORD, 0, 1) /* rw, only exists in SRC */ REG32(STATUS, 0x8) FIELD(STATUS, DONE_CNT, 13, 3) /* wtc */ @@ -211,7 +211,7 @@ static uint32_t xlnx_csu_dma_read(XlnxCSUDMA *s, uint8_t *buf, uint32_t len) if (result == MEMTX_OK) { xlnx_csu_dma_data_process(s, buf, len); } else { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address " TARGET_FMT_plx + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address " HWADDR_FMT_plx " for mem read", __func__, addr); s->regs[R_INT_STATUS] |= R_INT_STATUS_AXI_BRESP_ERR_MASK; xlnx_csu_dma_update_irq(s); @@ -241,7 +241,7 @@ static uint32_t xlnx_csu_dma_write(XlnxCSUDMA *s, uint8_t *buf, uint32_t len) } if (result != MEMTX_OK) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address " TARGET_FMT_plx + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address " HWADDR_FMT_plx " for mem write", __func__, addr); s->regs[R_INT_STATUS] |= R_INT_STATUS_AXI_BRESP_ERR_MASK; xlnx_csu_dma_update_irq(s); @@ -335,10 +335,14 @@ static uint64_t addr_pre_write(RegisterInfo *reg, uint64_t val) static uint64_t size_pre_write(RegisterInfo *reg, uint64_t val) { XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); + uint64_t size = val & R_SIZE_SIZE_MASK; if (s->regs[R_SIZE] != 0) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Starting DMA while already running.\n", __func__); + if (size || s->is_dst) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Starting DMA while already running.\n", + __func__); + } } if (!s->is_dst) { @@ -346,7 +350,7 @@ static uint64_t size_pre_write(RegisterInfo *reg, uint64_t val) } /* Size is word aligned */ - return val & R_SIZE_SIZE_MASK; + return size; } static uint64_t size_post_read(RegisterInfo *reg, uint64_t val) @@ -677,7 +681,7 @@ static const VMStateDescription vmstate_xlnx_csu_dma = { .name = TYPE_XLNX_CSU_DMA, .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PTIMER(src_timer, XlnxCSUDMA), VMSTATE_UINT16(width, XlnxCSUDMA), VMSTATE_BOOL(is_dst, XlnxCSUDMA), @@ -702,6 +706,10 @@ static Property xlnx_csu_dma_properties[] = { * which channel the device is connected to. */ DEFINE_PROP_BOOL("is-dst", XlnxCSUDMA, is_dst, true), + DEFINE_PROP_LINK("stream-connected-dma", XlnxCSUDMA, tx_dev, + TYPE_STREAM_SINK, StreamSink *), + DEFINE_PROP_LINK("dma", XlnxCSUDMA, dma_mr, + TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), }; @@ -728,15 +736,6 @@ static void xlnx_csu_dma_init(Object *obj) memory_region_init(&s->iomem, obj, TYPE_XLNX_CSU_DMA, XLNX_CSU_DMA_R_MAX * 4); - - object_property_add_link(obj, "stream-connected-dma", TYPE_STREAM_SINK, - (Object **)&s->tx_dev, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_STRONG); - object_property_add_link(obj, "dma", TYPE_MEMORY_REGION, - (Object **)&s->dma_mr, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_STRONG); } static const TypeInfo xlnx_csu_dma_info = { diff --git a/hw/dma/xlnx_dpdma.c b/hw/dma/xlnx_dpdma.c index dd66be5265..1f5cd64ed1 100644 --- a/hw/dma/xlnx_dpdma.c +++ b/hw/dma/xlnx_dpdma.c @@ -277,7 +277,7 @@ static inline bool xlnx_dpdma_desc_ignore_done_bit(DPDMADescriptor *desc) static const VMStateDescription vmstate_xlnx_dpdma = { .name = TYPE_XLNX_DPDMA, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(registers, XlnxDPDMAState, XLNX_DPDMA_REG_ARRAY_SIZE), VMSTATE_BOOL_ARRAY(operation_finished, XlnxDPDMAState, 6), diff --git a/hw/fsi/Kconfig b/hw/fsi/Kconfig new file mode 100644 index 0000000000..9cee657a0f --- /dev/null +++ b/hw/fsi/Kconfig @@ -0,0 +1,7 @@ +config FSI_APB2OPB_ASPEED + bool + depends on ASPEED_SOC + select FSI + +config FSI + bool diff --git a/hw/fsi/aspeed_apb2opb.c b/hw/fsi/aspeed_apb2opb.c new file mode 100644 index 0000000000..ea50718b6a --- /dev/null +++ b/hw/fsi/aspeed_apb2opb.c @@ -0,0 +1,367 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 IBM Corp. + * + * ASPEED APB-OPB FSI interface + * IBM On-chip Peripheral Bus + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qom/object.h" +#include "qapi/error.h" +#include "trace.h" + +#include "hw/fsi/aspeed_apb2opb.h" +#include "hw/qdev-core.h" + +#define TO_REG(x) (x >> 2) + +#define APB2OPB_VERSION TO_REG(0x00) +#define APB2OPB_TRIGGER TO_REG(0x04) + +#define APB2OPB_CONTROL TO_REG(0x08) +#define APB2OPB_CONTROL_OFF BE_GENMASK(31, 13) + +#define APB2OPB_OPB2FSI TO_REG(0x0c) +#define APB2OPB_OPB2FSI_OFF BE_GENMASK(31, 22) + +#define APB2OPB_OPB0_SEL TO_REG(0x10) +#define APB2OPB_OPB1_SEL TO_REG(0x28) +#define APB2OPB_OPB_SEL_EN BIT(0) + +#define APB2OPB_OPB0_MODE TO_REG(0x14) +#define APB2OPB_OPB1_MODE TO_REG(0x2c) +#define APB2OPB_OPB_MODE_RD BIT(0) + +#define APB2OPB_OPB0_XFER TO_REG(0x18) +#define APB2OPB_OPB1_XFER TO_REG(0x30) +#define APB2OPB_OPB_XFER_FULL BIT(1) +#define APB2OPB_OPB_XFER_HALF BIT(0) + +#define APB2OPB_OPB0_ADDR TO_REG(0x1c) +#define APB2OPB_OPB0_WRITE_DATA TO_REG(0x20) + +#define APB2OPB_OPB1_ADDR TO_REG(0x34) +#define APB2OPB_OPB1_WRITE_DATA TO_REG(0x38) + +#define APB2OPB_IRQ_STS TO_REG(0x48) +#define APB2OPB_IRQ_STS_OPB1_TX_ACK BIT(17) +#define APB2OPB_IRQ_STS_OPB0_TX_ACK BIT(16) + +#define APB2OPB_OPB0_WRITE_WORD_ENDIAN TO_REG(0x4c) +#define APB2OPB_OPB0_WRITE_WORD_ENDIAN_BE 0x0011101b +#define APB2OPB_OPB0_WRITE_BYTE_ENDIAN TO_REG(0x50) +#define APB2OPB_OPB0_WRITE_BYTE_ENDIAN_BE 0x0c330f3f +#define APB2OPB_OPB1_WRITE_WORD_ENDIAN TO_REG(0x54) +#define APB2OPB_OPB1_WRITE_BYTE_ENDIAN TO_REG(0x58) +#define APB2OPB_OPB0_READ_BYTE_ENDIAN TO_REG(0x5c) +#define APB2OPB_OPB1_READ_BYTE_ENDIAN TO_REG(0x60) +#define APB2OPB_OPB0_READ_WORD_ENDIAN_BE 0x00030b1b + +#define APB2OPB_OPB0_READ_DATA TO_REG(0x84) +#define APB2OPB_OPB1_READ_DATA TO_REG(0x90) + +/* + * The following magic values came from AST2600 data sheet + * The register values are defined under section "FSI controller" + * as initial values. + */ +static const uint32_t aspeed_apb2opb_reset[ASPEED_APB2OPB_NR_REGS] = { + [APB2OPB_VERSION] = 0x000000a1, + [APB2OPB_OPB0_WRITE_WORD_ENDIAN] = 0x0044eee4, + [APB2OPB_OPB0_WRITE_BYTE_ENDIAN] = 0x0055aaff, + [APB2OPB_OPB1_WRITE_WORD_ENDIAN] = 0x00117717, + [APB2OPB_OPB1_WRITE_BYTE_ENDIAN] = 0xffaa5500, + [APB2OPB_OPB0_READ_BYTE_ENDIAN] = 0x0044eee4, + [APB2OPB_OPB1_READ_BYTE_ENDIAN] = 0x00117717 +}; + +static void fsi_opb_fsi_master_address(FSIMasterState *fsi, hwaddr addr) +{ + memory_region_transaction_begin(); + memory_region_set_address(&fsi->iomem, addr); + memory_region_transaction_commit(); +} + +static void fsi_opb_opb2fsi_address(FSIMasterState *fsi, hwaddr addr) +{ + memory_region_transaction_begin(); + memory_region_set_address(&fsi->opb2fsi, addr); + memory_region_transaction_commit(); +} + +static uint64_t fsi_aspeed_apb2opb_read(void *opaque, hwaddr addr, + unsigned size) +{ + AspeedAPB2OPBState *s = ASPEED_APB2OPB(opaque); + unsigned int reg = TO_REG(addr); + + trace_fsi_aspeed_apb2opb_read(addr, size); + + if (reg >= ASPEED_APB2OPB_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out of bounds read: 0x%"HWADDR_PRIx" for %u\n", + __func__, addr, size); + return 0; + } + + return s->regs[reg]; +} + +static MemTxResult fsi_aspeed_apb2opb_rw(AddressSpace *as, hwaddr addr, + MemTxAttrs attrs, uint32_t *data, + uint32_t size, bool is_write) +{ + MemTxResult res; + + if (is_write) { + switch (size) { + case 4: + address_space_stl_le(as, addr, *data, attrs, &res); + break; + case 2: + address_space_stw_le(as, addr, *data, attrs, &res); + break; + case 1: + address_space_stb(as, addr, *data, attrs, &res); + break; + default: + g_assert_not_reached(); + } + } else { + switch (size) { + case 4: + *data = address_space_ldl_le(as, addr, attrs, &res); + break; + case 2: + *data = address_space_lduw_le(as, addr, attrs, &res); + break; + case 1: + *data = address_space_ldub(as, addr, attrs, &res); + break; + default: + g_assert_not_reached(); + } + } + return res; +} + +static void fsi_aspeed_apb2opb_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + AspeedAPB2OPBState *s = ASPEED_APB2OPB(opaque); + unsigned int reg = TO_REG(addr); + + trace_fsi_aspeed_apb2opb_write(addr, size, data); + + if (reg >= ASPEED_APB2OPB_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out of bounds write: %"HWADDR_PRIx" for %u\n", + __func__, addr, size); + return; + } + + switch (reg) { + case APB2OPB_CONTROL: + fsi_opb_fsi_master_address(&s->fsi[0], + data & APB2OPB_CONTROL_OFF); + break; + case APB2OPB_OPB2FSI: + fsi_opb_opb2fsi_address(&s->fsi[0], + data & APB2OPB_OPB2FSI_OFF); + break; + case APB2OPB_OPB0_WRITE_WORD_ENDIAN: + if (data != APB2OPB_OPB0_WRITE_WORD_ENDIAN_BE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bridge needs to be driven as BE (0x%x)\n", + __func__, APB2OPB_OPB0_WRITE_WORD_ENDIAN_BE); + } + break; + case APB2OPB_OPB0_WRITE_BYTE_ENDIAN: + if (data != APB2OPB_OPB0_WRITE_BYTE_ENDIAN_BE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bridge needs to be driven as BE (0x%x)\n", + __func__, APB2OPB_OPB0_WRITE_BYTE_ENDIAN_BE); + } + break; + case APB2OPB_OPB0_READ_BYTE_ENDIAN: + if (data != APB2OPB_OPB0_READ_WORD_ENDIAN_BE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bridge needs to be driven as BE (0x%x)\n", + __func__, APB2OPB_OPB0_READ_WORD_ENDIAN_BE); + } + break; + case APB2OPB_TRIGGER: + { + uint32_t opb, op_mode, op_size, op_addr, op_data; + MemTxResult result; + bool is_write; + int index; + AddressSpace *as; + + assert((s->regs[APB2OPB_OPB0_SEL] & APB2OPB_OPB_SEL_EN) ^ + (s->regs[APB2OPB_OPB1_SEL] & APB2OPB_OPB_SEL_EN)); + + if (s->regs[APB2OPB_OPB0_SEL] & APB2OPB_OPB_SEL_EN) { + opb = 0; + op_mode = s->regs[APB2OPB_OPB0_MODE]; + op_size = s->regs[APB2OPB_OPB0_XFER]; + op_addr = s->regs[APB2OPB_OPB0_ADDR]; + op_data = s->regs[APB2OPB_OPB0_WRITE_DATA]; + } else if (s->regs[APB2OPB_OPB1_SEL] & APB2OPB_OPB_SEL_EN) { + opb = 1; + op_mode = s->regs[APB2OPB_OPB1_MODE]; + op_size = s->regs[APB2OPB_OPB1_XFER]; + op_addr = s->regs[APB2OPB_OPB1_ADDR]; + op_data = s->regs[APB2OPB_OPB1_WRITE_DATA]; + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid operation: 0x%"HWADDR_PRIx" for %u\n", + __func__, addr, size); + return; + } + + if (op_size & ~(APB2OPB_OPB_XFER_HALF | APB2OPB_OPB_XFER_FULL)) { + qemu_log_mask(LOG_GUEST_ERROR, + "OPB transaction failed: Unrecognized access width: %d\n", + op_size); + return; + } + + op_size += 1; + is_write = !(op_mode & APB2OPB_OPB_MODE_RD); + index = opb ? APB2OPB_OPB1_READ_DATA : APB2OPB_OPB0_READ_DATA; + as = &s->opb[opb].as; + + result = fsi_aspeed_apb2opb_rw(as, op_addr, MEMTXATTRS_UNSPECIFIED, + &op_data, op_size, is_write); + if (result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: OPB %s failed @%08x\n", + __func__, is_write ? "write" : "read", op_addr); + return; + } + + if (!is_write) { + s->regs[index] = op_data; + } + + s->regs[APB2OPB_IRQ_STS] |= opb ? APB2OPB_IRQ_STS_OPB1_TX_ACK + : APB2OPB_IRQ_STS_OPB0_TX_ACK; + break; + } + } + + s->regs[reg] = data; +} + +static const struct MemoryRegionOps aspeed_apb2opb_ops = { + .read = fsi_aspeed_apb2opb_read, + .write = fsi_aspeed_apb2opb_write, + .valid.max_access_size = 4, + .valid.min_access_size = 4, + .impl.max_access_size = 4, + .impl.min_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void fsi_aspeed_apb2opb_init(Object *o) +{ + AspeedAPB2OPBState *s = ASPEED_APB2OPB(o); + int i; + + for (i = 0; i < ASPEED_FSI_NUM; i++) { + object_initialize_child(o, "fsi-master[*]", &s->fsi[i], + TYPE_FSI_MASTER); + } +} + +static void fsi_aspeed_apb2opb_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedAPB2OPBState *s = ASPEED_APB2OPB(dev); + int i; + + /* + * TODO: The OPBus model initializes the OPB address space in + * the .instance_init handler and this is problematic for test + * device-introspect-test. To avoid a memory corruption and a QEMU + * crash, qbus_init() should be called from realize(). Something to + * improve. Possibly, OPBus could also be removed. + */ + for (i = 0; i < ASPEED_FSI_NUM; i++) { + qbus_init(&s->opb[i], sizeof(s->opb[i]), TYPE_OP_BUS, DEVICE(s), + NULL); + } + + sysbus_init_irq(sbd, &s->irq); + + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_apb2opb_ops, s, + TYPE_ASPEED_APB2OPB, 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + + for (i = 0; i < ASPEED_FSI_NUM; i++) { + if (!qdev_realize(DEVICE(&s->fsi[i]), BUS(&s->opb[i]), errp)) { + return; + } + + memory_region_add_subregion(&s->opb[i].mr, 0x80000000, + &s->fsi[i].iomem); + + memory_region_add_subregion(&s->opb[i].mr, 0xa0000000, + &s->fsi[i].opb2fsi); + } +} + +static void fsi_aspeed_apb2opb_reset(DeviceState *dev) +{ + AspeedAPB2OPBState *s = ASPEED_APB2OPB(dev); + + memcpy(s->regs, aspeed_apb2opb_reset, ASPEED_APB2OPB_NR_REGS); +} + +static void fsi_aspeed_apb2opb_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "ASPEED APB2OPB Bridge"; + dc->realize = fsi_aspeed_apb2opb_realize; + dc->reset = fsi_aspeed_apb2opb_reset; +} + +static const TypeInfo aspeed_apb2opb_info = { + .name = TYPE_ASPEED_APB2OPB, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = fsi_aspeed_apb2opb_init, + .instance_size = sizeof(AspeedAPB2OPBState), + .class_init = fsi_aspeed_apb2opb_class_init, +}; + +static void aspeed_apb2opb_register_types(void) +{ + type_register_static(&aspeed_apb2opb_info); +} + +type_init(aspeed_apb2opb_register_types); + +static void fsi_opb_init(Object *o) +{ + OPBus *opb = OP_BUS(o); + + memory_region_init(&opb->mr, 0, TYPE_FSI_OPB, UINT32_MAX); + address_space_init(&opb->as, &opb->mr, TYPE_FSI_OPB); +} + +static const TypeInfo opb_info = { + .name = TYPE_OP_BUS, + .parent = TYPE_BUS, + .instance_init = fsi_opb_init, + .instance_size = sizeof(OPBus), +}; + +static void fsi_opb_register_types(void) +{ + type_register_static(&opb_info); +} + +type_init(fsi_opb_register_types); diff --git a/hw/fsi/cfam.c b/hw/fsi/cfam.c new file mode 100644 index 0000000000..c62f0f78de --- /dev/null +++ b/hw/fsi/cfam.c @@ -0,0 +1,168 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 IBM Corp. + * + * IBM Common FRU Access Macro + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" + +#include "qapi/error.h" +#include "trace.h" + +#include "hw/fsi/cfam.h" +#include "hw/fsi/fsi.h" + +#include "hw/qdev-properties.h" + +#define ENGINE_CONFIG_NEXT BIT(31) +#define ENGINE_CONFIG_TYPE_PEEK (0x02 << 4) +#define ENGINE_CONFIG_TYPE_FSI (0x03 << 4) +#define ENGINE_CONFIG_TYPE_SCRATCHPAD (0x06 << 4) + +/* Valid, slots, version, type, crc */ +#define CFAM_CONFIG_REG(__VER, __TYPE, __CRC) \ + (ENGINE_CONFIG_NEXT | \ + 0x00010000 | \ + (__VER) | \ + (__TYPE) | \ + (__CRC)) + +#define TO_REG(x) ((x) >> 2) + +#define CFAM_CONFIG_CHIP_ID TO_REG(0x00) +#define CFAM_CONFIG_PEEK_STATUS TO_REG(0x04) +#define CFAM_CONFIG_CHIP_ID_P9 0xc0022d15 +#define CFAM_CONFIG_CHIP_ID_BREAK 0xc0de0000 + +static uint64_t fsi_cfam_config_read(void *opaque, hwaddr addr, unsigned size) +{ + trace_fsi_cfam_config_read(addr, size); + + switch (addr) { + case 0x00: + return CFAM_CONFIG_CHIP_ID_P9; + case 0x04: + return CFAM_CONFIG_REG(0x1000, ENGINE_CONFIG_TYPE_PEEK, 0xc); + case 0x08: + return CFAM_CONFIG_REG(0x5000, ENGINE_CONFIG_TYPE_FSI, 0xa); + case 0xc: + return CFAM_CONFIG_REG(0x1000, ENGINE_CONFIG_TYPE_SCRATCHPAD, 0x7); + default: + /* + * The config table contains different engines from 0xc onwards. + * The scratch pad is already added at address 0xc. We need to add + * future engines from address 0x10 onwards. Returning 0 as engine + * is not implemented. + */ + return 0; + } +} + +static void fsi_cfam_config_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + FSICFAMState *cfam = FSI_CFAM(opaque); + + trace_fsi_cfam_config_write(addr, size, data); + + switch (TO_REG(addr)) { + case CFAM_CONFIG_CHIP_ID: + case CFAM_CONFIG_PEEK_STATUS: + if (data == CFAM_CONFIG_CHIP_ID_BREAK) { + bus_cold_reset(BUS(&cfam->lbus)); + } + break; + default: + trace_fsi_cfam_config_write_noaddr(addr, size, data); + } +} + +static const struct MemoryRegionOps cfam_config_ops = { + .read = fsi_cfam_config_read, + .write = fsi_cfam_config_write, + .valid.max_access_size = 4, + .valid.min_access_size = 4, + .impl.max_access_size = 4, + .impl.min_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static uint64_t fsi_cfam_unimplemented_read(void *opaque, hwaddr addr, + unsigned size) +{ + trace_fsi_cfam_unimplemented_read(addr, size); + + return 0; +} + +static void fsi_cfam_unimplemented_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + trace_fsi_cfam_unimplemented_write(addr, size, data); +} + +static const struct MemoryRegionOps fsi_cfam_unimplemented_ops = { + .read = fsi_cfam_unimplemented_read, + .write = fsi_cfam_unimplemented_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void fsi_cfam_instance_init(Object *obj) +{ + FSICFAMState *s = FSI_CFAM(obj); + + object_initialize_child(obj, "scratchpad", &s->scratchpad, + TYPE_FSI_SCRATCHPAD); +} + +static void fsi_cfam_realize(DeviceState *dev, Error **errp) +{ + FSICFAMState *cfam = FSI_CFAM(dev); + FSISlaveState *slave = FSI_SLAVE(dev); + + /* Each slave has a 2MiB address space */ + memory_region_init_io(&cfam->mr, OBJECT(cfam), &fsi_cfam_unimplemented_ops, + cfam, TYPE_FSI_CFAM, 2 * MiB); + + qbus_init(&cfam->lbus, sizeof(cfam->lbus), TYPE_FSI_LBUS, DEVICE(cfam), + NULL); + + memory_region_init_io(&cfam->config_iomem, OBJECT(cfam), &cfam_config_ops, + cfam, TYPE_FSI_CFAM ".config", 0x400); + + memory_region_add_subregion(&cfam->mr, 0, &cfam->config_iomem); + memory_region_add_subregion(&cfam->mr, 0x800, &slave->iomem); + memory_region_add_subregion(&cfam->mr, 0xc00, &cfam->lbus.mr); + + /* Add scratchpad engine */ + if (!qdev_realize(DEVICE(&cfam->scratchpad), BUS(&cfam->lbus), errp)) { + return; + } + + FSILBusDevice *fsi_dev = FSI_LBUS_DEVICE(&cfam->scratchpad); + memory_region_add_subregion(&cfam->lbus.mr, 0, &fsi_dev->iomem); +} + +static void fsi_cfam_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->bus_type = TYPE_FSI_BUS; + dc->realize = fsi_cfam_realize; +} + +static const TypeInfo fsi_cfam_info = { + .name = TYPE_FSI_CFAM, + .parent = TYPE_FSI_SLAVE, + .instance_init = fsi_cfam_instance_init, + .instance_size = sizeof(FSICFAMState), + .class_init = fsi_cfam_class_init, +}; + +static void fsi_cfam_register_types(void) +{ + type_register_static(&fsi_cfam_info); +} + +type_init(fsi_cfam_register_types); diff --git a/hw/fsi/fsi-master.c b/hw/fsi/fsi-master.c new file mode 100644 index 0000000000..a5f0598c98 --- /dev/null +++ b/hw/fsi/fsi-master.c @@ -0,0 +1,170 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 IBM Corp. + * + * IBM Flexible Service Interface master + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "trace.h" + +#include "hw/fsi/fsi-master.h" + +#define TYPE_OP_BUS "opb" + +#define TO_REG(x) ((x) >> 2) + +#define FSI_MENP0 TO_REG(0x010) +#define FSI_MENP32 TO_REG(0x014) +#define FSI_MSENP0 TO_REG(0x018) +#define FSI_MLEVP0 TO_REG(0x018) +#define FSI_MSENP32 TO_REG(0x01c) +#define FSI_MLEVP32 TO_REG(0x01c) +#define FSI_MCENP0 TO_REG(0x020) +#define FSI_MREFP0 TO_REG(0x020) +#define FSI_MCENP32 TO_REG(0x024) +#define FSI_MREFP32 TO_REG(0x024) + +#define FSI_MVER TO_REG(0x074) +#define FSI_MRESP0 TO_REG(0x0d0) + +#define FSI_MRESB0 TO_REG(0x1d0) +#define FSI_MRESB0_RESET_GENERAL BIT(31) +#define FSI_MRESB0_RESET_ERROR BIT(30) + +static uint64_t fsi_master_read(void *opaque, hwaddr addr, unsigned size) +{ + FSIMasterState *s = FSI_MASTER(opaque); + int reg = TO_REG(addr); + + trace_fsi_master_read(addr, size); + + if (reg >= FSI_MASTER_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out of bounds read: 0x%"HWADDR_PRIx" for %u\n", + __func__, addr, size); + return 0; + } + + return s->regs[reg]; +} + +static void fsi_master_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + FSIMasterState *s = FSI_MASTER(opaque); + int reg = TO_REG(addr); + + trace_fsi_master_write(addr, size, data); + + if (reg >= FSI_MASTER_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out of bounds write: %"HWADDR_PRIx" for %u\n", + __func__, addr, size); + return; + } + + switch (reg) { + case FSI_MENP0: + s->regs[FSI_MENP0] = data; + break; + case FSI_MENP32: + s->regs[FSI_MENP32] = data; + break; + case FSI_MSENP0: + s->regs[FSI_MENP0] |= data; + break; + case FSI_MSENP32: + s->regs[FSI_MENP32] |= data; + break; + case FSI_MCENP0: + s->regs[FSI_MENP0] &= ~data; + break; + case FSI_MCENP32: + s->regs[FSI_MENP32] &= ~data; + break; + case FSI_MRESP0: + /* Perform necessary resets leave register 0 to indicate no errors */ + break; + case FSI_MRESB0: + if (data & FSI_MRESB0_RESET_GENERAL) { + device_cold_reset(DEVICE(opaque)); + } + if (data & FSI_MRESB0_RESET_ERROR) { + /* FIXME: this seems dubious */ + device_cold_reset(DEVICE(opaque)); + } + break; + default: + s->regs[reg] = data; + } +} + +static const struct MemoryRegionOps fsi_master_ops = { + .read = fsi_master_read, + .write = fsi_master_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void fsi_master_init(Object *o) +{ + FSIMasterState *s = FSI_MASTER(o); + + object_initialize_child(o, "cfam", &s->cfam, TYPE_FSI_CFAM); + + qbus_init(&s->bus, sizeof(s->bus), TYPE_FSI_BUS, DEVICE(s), NULL); + + memory_region_init_io(&s->iomem, OBJECT(s), &fsi_master_ops, s, + TYPE_FSI_MASTER, 0x10000000); + memory_region_init(&s->opb2fsi, OBJECT(s), "fsi.opb2fsi", 0x10000000); +} + +static void fsi_master_realize(DeviceState *dev, Error **errp) +{ + FSIMasterState *s = FSI_MASTER(dev); + + if (!qdev_realize(DEVICE(&s->cfam), BUS(&s->bus), errp)) { + return; + } + + /* address ? */ + memory_region_add_subregion(&s->opb2fsi, 0, &s->cfam.mr); +} + +static void fsi_master_reset(DeviceState *dev) +{ + FSIMasterState *s = FSI_MASTER(dev); + + /* Initialize registers */ + memset(s->regs, 0, sizeof(s->regs)); + + /* ASPEED default */ + s->regs[FSI_MVER] = 0xe0050101; +} + +static void fsi_master_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->bus_type = TYPE_OP_BUS; + dc->desc = "FSI Master"; + dc->realize = fsi_master_realize; + dc->reset = fsi_master_reset; +} + +static const TypeInfo fsi_master_info = { + .name = TYPE_FSI_MASTER, + .parent = TYPE_DEVICE, + .instance_init = fsi_master_init, + .instance_size = sizeof(FSIMasterState), + .class_init = fsi_master_class_init, +}; + +static void fsi_register_types(void) +{ + type_register_static(&fsi_master_info); +} + +type_init(fsi_register_types); diff --git a/hw/fsi/fsi.c b/hw/fsi/fsi.c new file mode 100644 index 0000000000..9a5f4e616f --- /dev/null +++ b/hw/fsi/fsi.c @@ -0,0 +1,102 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 IBM Corp. + * + * IBM Flexible Service Interface + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "trace.h" + +#include "hw/fsi/fsi.h" + +#define TO_REG(x) ((x) >> 2) + +static const TypeInfo fsi_bus_info = { + .name = TYPE_FSI_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(FSIBus), +}; + +static uint64_t fsi_slave_read(void *opaque, hwaddr addr, unsigned size) +{ + FSISlaveState *s = FSI_SLAVE(opaque); + int reg = TO_REG(addr); + + trace_fsi_slave_read(addr, size); + + if (reg >= FSI_SLAVE_CONTROL_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out of bounds read: 0x%"HWADDR_PRIx" for %u\n", + __func__, addr, size); + return 0; + } + + return s->regs[reg]; +} + +static void fsi_slave_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + FSISlaveState *s = FSI_SLAVE(opaque); + int reg = TO_REG(addr); + + trace_fsi_slave_write(addr, size, data); + + if (reg >= FSI_SLAVE_CONTROL_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out of bounds write: 0x%"HWADDR_PRIx" for %u\n", + __func__, addr, size); + return; + } + + s->regs[reg] = data; +} + +static const struct MemoryRegionOps fsi_slave_ops = { + .read = fsi_slave_read, + .write = fsi_slave_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void fsi_slave_reset(DeviceState *dev) +{ + FSISlaveState *s = FSI_SLAVE(dev); + + /* Initialize registers */ + memset(s->regs, 0, sizeof(s->regs)); +} + +static void fsi_slave_init(Object *o) +{ + FSISlaveState *s = FSI_SLAVE(o); + + memory_region_init_io(&s->iomem, OBJECT(s), &fsi_slave_ops, + s, TYPE_FSI_SLAVE, 0x400); +} + +static void fsi_slave_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->bus_type = TYPE_FSI_BUS; + dc->desc = "FSI Slave"; + dc->reset = fsi_slave_reset; +} + +static const TypeInfo fsi_slave_info = { + .name = TYPE_FSI_SLAVE, + .parent = TYPE_DEVICE, + .instance_init = fsi_slave_init, + .instance_size = sizeof(FSISlaveState), + .class_init = fsi_slave_class_init, +}; + +static void fsi_register_types(void) +{ + type_register_static(&fsi_bus_info); + type_register_static(&fsi_slave_info); +} + +type_init(fsi_register_types); diff --git a/hw/fsi/lbus.c b/hw/fsi/lbus.c new file mode 100644 index 0000000000..20495f42fd --- /dev/null +++ b/hw/fsi/lbus.c @@ -0,0 +1,117 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 IBM Corp. + * + * IBM Local bus where FSI slaves are connected + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/fsi/lbus.h" +#include "hw/qdev-properties.h" +#include "qemu/log.h" +#include "trace.h" + +#define TO_REG(offset) ((offset) >> 2) + +static void fsi_lbus_init(Object *o) +{ + FSILBus *lbus = FSI_LBUS(o); + + memory_region_init(&lbus->mr, OBJECT(lbus), TYPE_FSI_LBUS, 1 * MiB); +} + +static const TypeInfo fsi_lbus_info = { + .name = TYPE_FSI_LBUS, + .parent = TYPE_BUS, + .instance_init = fsi_lbus_init, + .instance_size = sizeof(FSILBus), +}; + +static const TypeInfo fsi_lbus_device_type_info = { + .name = TYPE_FSI_LBUS_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(FSILBusDevice), + .abstract = true, +}; + +static uint64_t fsi_scratchpad_read(void *opaque, hwaddr addr, unsigned size) +{ + FSIScratchPad *s = SCRATCHPAD(opaque); + int reg = TO_REG(addr); + + trace_fsi_scratchpad_read(addr, size); + + if (reg >= FSI_SCRATCHPAD_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + return 0; + } + + return s->regs[reg]; +} + +static void fsi_scratchpad_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + FSIScratchPad *s = SCRATCHPAD(opaque); + + trace_fsi_scratchpad_write(addr, size, data); + int reg = TO_REG(addr); + + if (reg >= FSI_SCRATCHPAD_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + return; + } + + s->regs[reg] = data; +} + +static const struct MemoryRegionOps scratchpad_ops = { + .read = fsi_scratchpad_read, + .write = fsi_scratchpad_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void fsi_scratchpad_realize(DeviceState *dev, Error **errp) +{ + FSILBusDevice *ldev = FSI_LBUS_DEVICE(dev); + + memory_region_init_io(&ldev->iomem, OBJECT(ldev), &scratchpad_ops, + ldev, TYPE_FSI_SCRATCHPAD, 0x400); +} + +static void fsi_scratchpad_reset(DeviceState *dev) +{ + FSIScratchPad *s = SCRATCHPAD(dev); + + memset(s->regs, 0, sizeof(s->regs)); +} + +static void fsi_scratchpad_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->bus_type = TYPE_FSI_LBUS; + dc->realize = fsi_scratchpad_realize; + dc->reset = fsi_scratchpad_reset; +} + +static const TypeInfo fsi_scratchpad_info = { + .name = TYPE_FSI_SCRATCHPAD, + .parent = TYPE_FSI_LBUS_DEVICE, + .instance_size = sizeof(FSIScratchPad), + .class_init = fsi_scratchpad_class_init, +}; + +static void fsi_lbus_register_types(void) +{ + type_register_static(&fsi_lbus_info); + type_register_static(&fsi_lbus_device_type_info); + type_register_static(&fsi_scratchpad_info); +} + +type_init(fsi_lbus_register_types); diff --git a/hw/fsi/meson.build b/hw/fsi/meson.build new file mode 100644 index 0000000000..a18a076552 --- /dev/null +++ b/hw/fsi/meson.build @@ -0,0 +1,2 @@ +system_ss.add(when: 'CONFIG_FSI', if_true: files('lbus.c','fsi.c','cfam.c','fsi-master.c')) +system_ss.add(when: 'CONFIG_FSI_APB2OPB_ASPEED', if_true: files('aspeed_apb2opb.c')) diff --git a/hw/fsi/trace-events b/hw/fsi/trace-events new file mode 100644 index 0000000000..9e286d08d3 --- /dev/null +++ b/hw/fsi/trace-events @@ -0,0 +1,13 @@ +fsi_scratchpad_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" +fsi_scratchpad_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 +fsi_slave_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" +fsi_slave_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 +fsi_cfam_config_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" +fsi_cfam_config_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 +fsi_cfam_unimplemented_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" +fsi_cfam_unimplemented_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 +fsi_cfam_config_write_noaddr(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 +fsi_master_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" +fsi_master_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 +fsi_aspeed_apb2opb_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" +fsi_aspeed_apb2opb_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 diff --git a/hw/fsi/trace.h b/hw/fsi/trace.h new file mode 100644 index 0000000000..ee67c7fb04 --- /dev/null +++ b/hw/fsi/trace.h @@ -0,0 +1 @@ +#include "trace/trace-hw_fsi.h" diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig index f0e7405f6e..19c97cc823 100644 --- a/hw/gpio/Kconfig +++ b/hw/gpio/Kconfig @@ -8,8 +8,18 @@ config PL061 config GPIO_KEY bool +config GPIO_MPC8XXX + bool + config GPIO_PWR bool config SIFIVE_GPIO bool + +config STM32L4X5_GPIO + bool + +config PCF8574 + bool + depends on I2C diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index a62a673857..c1781e2ba3 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -268,7 +268,7 @@ static ptrdiff_t aspeed_gpio_set_idx(AspeedGPIOState *s, GPIOSets *regs) } static void aspeed_gpio_update(AspeedGPIOState *s, GPIOSets *regs, - uint32_t value) + uint32_t value, uint32_t mode_mask) { uint32_t input_mask = regs->input_mask; uint32_t direction = regs->direction; @@ -277,7 +277,8 @@ static void aspeed_gpio_update(AspeedGPIOState *s, GPIOSets *regs, uint32_t diff; int gpio; - diff = old ^ new; + diff = (old ^ new); + diff &= mode_mask; if (diff) { for (gpio = 0; gpio < ASPEED_GPIOS_PER_SET; gpio++) { uint32_t mask = 1 << gpio; @@ -339,7 +340,7 @@ static void aspeed_gpio_set_pin_level(AspeedGPIOState *s, uint32_t set_idx, value &= ~pin_mask; } - aspeed_gpio_update(s, &s->sets[set_idx], value); + aspeed_gpio_update(s, &s->sets[set_idx], value, ~s->sets[set_idx].direction); } /* @@ -653,7 +654,7 @@ static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset, reg_value = update_value_control_source(set, set->data_value, reg_value); set->data_read = reg_value; - aspeed_gpio_update(s, set, reg_value); + aspeed_gpio_update(s, set, reg_value, set->direction); return; case gpio_reg_idx_direction: reg_value = set->direction; @@ -753,7 +754,7 @@ static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset, __func__, offset, data, reg_idx_type); return; } - aspeed_gpio_update(s, set, set->data_value); + aspeed_gpio_update(s, set, set->data_value, UINT32_MAX); return; } @@ -799,7 +800,7 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, data &= props->output; data = update_value_control_source(set, set->data_value, data); set->data_read = data; - aspeed_gpio_update(s, set, data); + aspeed_gpio_update(s, set, data, set->direction); return; case gpio_reg_direction: /* @@ -875,7 +876,7 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, PRIx64"\n", __func__, offset); return; } - aspeed_gpio_update(s, set, set->data_value); + aspeed_gpio_update(s, set, set->data_value, UINT32_MAX); return; } @@ -1066,7 +1067,7 @@ static const VMStateDescription vmstate_gpio_regs = { .name = TYPE_ASPEED_GPIO"/regs", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(data_value, GPIOSets), VMSTATE_UINT32(data_read, GPIOSets), VMSTATE_UINT32(direction, GPIOSets), @@ -1089,7 +1090,7 @@ static const VMStateDescription vmstate_aspeed_gpio = { .name = TYPE_ASPEED_GPIO, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(sets, AspeedGPIOState, ASPEED_GPIO_MAX_NR_SETS, 1, vmstate_gpio_regs, GPIOSets), VMSTATE_UINT32_ARRAY(debounce_regs, AspeedGPIOState, diff --git a/hw/gpio/bcm2835_gpio.c b/hw/gpio/bcm2835_gpio.c index c995bba1d9..6bd50bb0b6 100644 --- a/hw/gpio/bcm2835_gpio.c +++ b/hw/gpio/bcm2835_gpio.c @@ -284,7 +284,7 @@ static const VMStateDescription vmstate_bcm2835_gpio = { .name = "bcm2835_gpio", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(fsel, BCM2835GpioState, 54), VMSTATE_UINT32(lev0, BCM2835GpioState), VMSTATE_UINT32(lev1, BCM2835GpioState), diff --git a/hw/gpio/bcm2838_gpio.c b/hw/gpio/bcm2838_gpio.c new file mode 100644 index 0000000000..2ddf62f695 --- /dev/null +++ b/hw/gpio/bcm2838_gpio.c @@ -0,0 +1,390 @@ +/* + * Raspberry Pi (BCM2838) GPIO Controller + * This implementation is based on bcm2835_gpio (hw/gpio/bcm2835_gpio.c) + * + * Copyright (c) 2022 Auriga LLC + * + * Authors: + * Lotosh, Aleksey + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "hw/sd/sd.h" +#include "hw/gpio/bcm2838_gpio.h" +#include "hw/irq.h" + +#define GPFSEL0 0x00 +#define GPFSEL1 0x04 +#define GPFSEL2 0x08 +#define GPFSEL3 0x0C +#define GPFSEL4 0x10 +#define GPFSEL5 0x14 +#define GPSET0 0x1C +#define GPSET1 0x20 +#define GPCLR0 0x28 +#define GPCLR1 0x2C +#define GPLEV0 0x34 +#define GPLEV1 0x38 +#define GPEDS0 0x40 +#define GPEDS1 0x44 +#define GPREN0 0x4C +#define GPREN1 0x50 +#define GPFEN0 0x58 +#define GPFEN1 0x5C +#define GPHEN0 0x64 +#define GPHEN1 0x68 +#define GPLEN0 0x70 +#define GPLEN1 0x74 +#define GPAREN0 0x7C +#define GPAREN1 0x80 +#define GPAFEN0 0x88 +#define GPAFEN1 0x8C + +#define GPIO_PUP_PDN_CNTRL_REG0 0xE4 +#define GPIO_PUP_PDN_CNTRL_REG1 0xE8 +#define GPIO_PUP_PDN_CNTRL_REG2 0xEC +#define GPIO_PUP_PDN_CNTRL_REG3 0xF0 + +#define RESET_VAL_CNTRL_REG0 0xAAA95555 +#define RESET_VAL_CNTRL_REG1 0xA0AAAAAA +#define RESET_VAL_CNTRL_REG2 0x50AAA95A +#define RESET_VAL_CNTRL_REG3 0x00055555 + +#define NUM_FSELN_IN_GPFSELN 10 +#define NUM_BITS_FSELN 3 +#define MASK_FSELN 0x7 + +#define BYTES_IN_WORD 4 + +/* bcm,function property */ +#define BCM2838_FSEL_GPIO_IN 0 +#define BCM2838_FSEL_GPIO_OUT 1 +#define BCM2838_FSEL_ALT5 2 +#define BCM2838_FSEL_ALT4 3 +#define BCM2838_FSEL_ALT0 4 +#define BCM2838_FSEL_ALT1 5 +#define BCM2838_FSEL_ALT2 6 +#define BCM2838_FSEL_ALT3 7 + +static uint32_t gpfsel_get(BCM2838GpioState *s, uint8_t reg) +{ + int i; + uint32_t value = 0; + for (i = 0; i < NUM_FSELN_IN_GPFSELN; i++) { + uint32_t index = NUM_FSELN_IN_GPFSELN * reg + i; + if (index < sizeof(s->fsel)) { + value |= (s->fsel[index] & MASK_FSELN) << (NUM_BITS_FSELN * i); + } + } + return value; +} + +static void gpfsel_set(BCM2838GpioState *s, uint8_t reg, uint32_t value) +{ + int i; + for (i = 0; i < NUM_FSELN_IN_GPFSELN; i++) { + uint32_t index = NUM_FSELN_IN_GPFSELN * reg + i; + if (index < sizeof(s->fsel)) { + int fsel = (value >> (NUM_BITS_FSELN * i)) & MASK_FSELN; + s->fsel[index] = fsel; + } + } + + /* SD controller selection (48-53) */ + if (s->sd_fsel != BCM2838_FSEL_GPIO_IN + && (s->fsel[48] == BCM2838_FSEL_GPIO_IN) + && (s->fsel[49] == BCM2838_FSEL_GPIO_IN) + && (s->fsel[50] == BCM2838_FSEL_GPIO_IN) + && (s->fsel[51] == BCM2838_FSEL_GPIO_IN) + && (s->fsel[52] == BCM2838_FSEL_GPIO_IN) + && (s->fsel[53] == BCM2838_FSEL_GPIO_IN) + ) { + /* SDHCI controller selected */ + sdbus_reparent_card(s->sdbus_sdhost, s->sdbus_sdhci); + s->sd_fsel = BCM2838_FSEL_GPIO_IN; + } else if (s->sd_fsel != BCM2838_FSEL_ALT0 + && (s->fsel[48] == BCM2838_FSEL_ALT0) /* SD_CLK_R */ + && (s->fsel[49] == BCM2838_FSEL_ALT0) /* SD_CMD_R */ + && (s->fsel[50] == BCM2838_FSEL_ALT0) /* SD_DATA0_R */ + && (s->fsel[51] == BCM2838_FSEL_ALT0) /* SD_DATA1_R */ + && (s->fsel[52] == BCM2838_FSEL_ALT0) /* SD_DATA2_R */ + && (s->fsel[53] == BCM2838_FSEL_ALT0) /* SD_DATA3_R */ + ) { + /* SDHost controller selected */ + sdbus_reparent_card(s->sdbus_sdhci, s->sdbus_sdhost); + s->sd_fsel = BCM2838_FSEL_ALT0; + } +} + +static int gpfsel_is_out(BCM2838GpioState *s, int index) +{ + if (index >= 0 && index < BCM2838_GPIO_NUM) { + return s->fsel[index] == 1; + } + return 0; +} + +static void gpset(BCM2838GpioState *s, uint32_t val, uint8_t start, + uint8_t count, uint32_t *lev) +{ + uint32_t changes = val & ~*lev; + uint32_t cur = 1; + + int i; + for (i = 0; i < count; i++) { + if ((changes & cur) && (gpfsel_is_out(s, start + i))) { + qemu_set_irq(s->out[start + i], 1); + } + cur <<= 1; + } + + *lev |= val; +} + +static void gpclr(BCM2838GpioState *s, uint32_t val, uint8_t start, + uint8_t count, uint32_t *lev) +{ + uint32_t changes = val & *lev; + uint32_t cur = 1; + + int i; + for (i = 0; i < count; i++) { + if ((changes & cur) && (gpfsel_is_out(s, start + i))) { + qemu_set_irq(s->out[start + i], 0); + } + cur <<= 1; + } + + *lev &= ~val; +} + +static uint64_t bcm2838_gpio_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2838GpioState *s = (BCM2838GpioState *)opaque; + uint64_t value = 0; + + switch (offset) { + case GPFSEL0: + case GPFSEL1: + case GPFSEL2: + case GPFSEL3: + case GPFSEL4: + case GPFSEL5: + value = gpfsel_get(s, offset / BYTES_IN_WORD); + break; + case GPSET0: + case GPSET1: + case GPCLR0: + case GPCLR1: + /* Write Only */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Attempt reading from write only" + " register. 0x%"PRIx64" will be returned." + " Address 0x%"HWADDR_PRIx", size %u\n", + TYPE_BCM2838_GPIO, __func__, value, offset, size); + break; + case GPLEV0: + value = s->lev0; + break; + case GPLEV1: + value = s->lev1; + break; + case GPEDS0: + case GPEDS1: + case GPREN0: + case GPREN1: + case GPFEN0: + case GPFEN1: + case GPHEN0: + case GPHEN1: + case GPLEN0: + case GPLEN1: + case GPAREN0: + case GPAREN1: + case GPAFEN0: + case GPAFEN1: + /* Not implemented */ + qemu_log_mask(LOG_UNIMP, "%s: %s: not implemented for %"HWADDR_PRIx"\n", + TYPE_BCM2838_GPIO, __func__, offset); + break; + case GPIO_PUP_PDN_CNTRL_REG0: + case GPIO_PUP_PDN_CNTRL_REG1: + case GPIO_PUP_PDN_CNTRL_REG2: + case GPIO_PUP_PDN_CNTRL_REG3: + value = s->pup_cntrl_reg[(offset - GPIO_PUP_PDN_CNTRL_REG0) + / sizeof(s->pup_cntrl_reg[0])]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: bad offset %"HWADDR_PRIx"\n", + TYPE_BCM2838_GPIO, __func__, offset); + break; + } + + return value; +} + +static void bcm2838_gpio_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + BCM2838GpioState *s = (BCM2838GpioState *)opaque; + + switch (offset) { + case GPFSEL0: + case GPFSEL1: + case GPFSEL2: + case GPFSEL3: + case GPFSEL4: + case GPFSEL5: + gpfsel_set(s, offset / BYTES_IN_WORD, value); + break; + case GPSET0: + gpset(s, value, 0, 32, &s->lev0); + break; + case GPSET1: + gpset(s, value, 32, 22, &s->lev1); + break; + case GPCLR0: + gpclr(s, value, 0, 32, &s->lev0); + break; + case GPCLR1: + gpclr(s, value, 32, 22, &s->lev1); + break; + case GPLEV0: + case GPLEV1: + /* Read Only */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Attempt writing 0x%"PRIx64"" + " to read only register. Ignored." + " Address 0x%"HWADDR_PRIx", size %u\n", + TYPE_BCM2838_GPIO, __func__, value, offset, size); + break; + case GPEDS0: + case GPEDS1: + case GPREN0: + case GPREN1: + case GPFEN0: + case GPFEN1: + case GPHEN0: + case GPHEN1: + case GPLEN0: + case GPLEN1: + case GPAREN0: + case GPAREN1: + case GPAFEN0: + case GPAFEN1: + /* Not implemented */ + qemu_log_mask(LOG_UNIMP, "%s: %s: not implemented for %"HWADDR_PRIx"\n", + TYPE_BCM2838_GPIO, __func__, offset); + break; + case GPIO_PUP_PDN_CNTRL_REG0: + case GPIO_PUP_PDN_CNTRL_REG1: + case GPIO_PUP_PDN_CNTRL_REG2: + case GPIO_PUP_PDN_CNTRL_REG3: + s->pup_cntrl_reg[(offset - GPIO_PUP_PDN_CNTRL_REG0) + / sizeof(s->pup_cntrl_reg[0])] = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: bad offset %"HWADDR_PRIx"\n", + TYPE_BCM2838_GPIO, __func__, offset); + } + return; +} + +static void bcm2838_gpio_reset(DeviceState *dev) +{ + BCM2838GpioState *s = BCM2838_GPIO(dev); + + memset(s->fsel, 0, sizeof(s->fsel)); + + s->sd_fsel = 0; + + /* SDHCI is selected by default */ + sdbus_reparent_card(&s->sdbus, s->sdbus_sdhci); + + s->lev0 = 0; + s->lev1 = 0; + + memset(s->fsel, 0, sizeof(s->fsel)); + + s->pup_cntrl_reg[0] = RESET_VAL_CNTRL_REG0; + s->pup_cntrl_reg[1] = RESET_VAL_CNTRL_REG1; + s->pup_cntrl_reg[2] = RESET_VAL_CNTRL_REG2; + s->pup_cntrl_reg[3] = RESET_VAL_CNTRL_REG3; +} + +static const MemoryRegionOps bcm2838_gpio_ops = { + .read = bcm2838_gpio_read, + .write = bcm2838_gpio_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_bcm2838_gpio = { + .name = "bcm2838_gpio", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(fsel, BCM2838GpioState, BCM2838_GPIO_NUM), + VMSTATE_UINT32(lev0, BCM2838GpioState), + VMSTATE_UINT32(lev1, BCM2838GpioState), + VMSTATE_UINT8(sd_fsel, BCM2838GpioState), + VMSTATE_UINT32_ARRAY(pup_cntrl_reg, BCM2838GpioState, + GPIO_PUP_PDN_CNTRL_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2838_gpio_init(Object *obj) +{ + BCM2838GpioState *s = BCM2838_GPIO(obj); + DeviceState *dev = DEVICE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, DEVICE(s), "sd-bus"); + + memory_region_init_io(&s->iomem, obj, &bcm2838_gpio_ops, s, + "bcm2838_gpio", BCM2838_GPIO_REGS_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + qdev_init_gpio_out(dev, s->out, BCM2838_GPIO_NUM); +} + +static void bcm2838_gpio_realize(DeviceState *dev, Error **errp) +{ + BCM2838GpioState *s = BCM2838_GPIO(dev); + Object *obj; + + obj = object_property_get_link(OBJECT(dev), "sdbus-sdhci", &error_abort); + s->sdbus_sdhci = SD_BUS(obj); + + obj = object_property_get_link(OBJECT(dev), "sdbus-sdhost", &error_abort); + s->sdbus_sdhost = SD_BUS(obj); +} + +static void bcm2838_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_bcm2838_gpio; + dc->realize = &bcm2838_gpio_realize; + dc->reset = &bcm2838_gpio_reset; +} + +static const TypeInfo bcm2838_gpio_info = { + .name = TYPE_BCM2838_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2838GpioState), + .instance_init = bcm2838_gpio_init, + .class_init = bcm2838_gpio_class_init, +}; + +static void bcm2838_gpio_register_types(void) +{ + type_register_static(&bcm2838_gpio_info); +} + +type_init(bcm2838_gpio_register_types) diff --git a/hw/gpio/gpio_key.c b/hw/gpio/gpio_key.c index 74f6138356..61bb587058 100644 --- a/hw/gpio/gpio_key.c +++ b/hw/gpio/gpio_key.c @@ -45,7 +45,7 @@ static const VMStateDescription vmstate_gpio_key = { .name = "gpio-key", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(timer, GPIOKEYState), VMSTATE_END_OF_LIST() } diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c index c7f98b7bb1..e53b00d951 100644 --- a/hw/gpio/imx_gpio.c +++ b/hw/gpio/imx_gpio.c @@ -277,7 +277,7 @@ static const VMStateDescription vmstate_imx_gpio = { .name = TYPE_IMX_GPIO, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(dr, IMXGPIOState), VMSTATE_UINT32(gdir, IMXGPIOState), VMSTATE_UINT32(psr, IMXGPIOState), diff --git a/hw/gpio/max7310.c b/hw/gpio/max7310.c index db6b5e3d76..86315714fb 100644 --- a/hw/gpio/max7310.c +++ b/hw/gpio/max7310.c @@ -49,22 +49,22 @@ static uint8_t max7310_rx(I2CSlave *i2c) MAX7310State *s = MAX7310(i2c); switch (s->command) { - case 0x00: /* Input port */ + case 0x00: /* Input port */ return s->level ^ s->polarity; - case 0x01: /* Output port */ + case 0x01: /* Output port */ return s->level & ~s->direction; - case 0x02: /* Polarity inversion */ + case 0x02: /* Polarity inversion */ return s->polarity; - case 0x03: /* Configuration */ + case 0x03: /* Configuration */ return s->direction; - case 0x04: /* Timeout */ + case 0x04: /* Timeout */ return s->status; - case 0xff: /* Reserved */ + case 0xff: /* Reserved */ return 0xff; default: @@ -95,7 +95,7 @@ static int max7310_tx(I2CSlave *i2c, uint8_t data) } switch (s->command) { - case 0x01: /* Output port */ + case 0x01: /* Output port */ for (diff = (data ^ s->level) & ~s->direction; diff; diff &= ~(1 << line)) { line = ctz32(diff); @@ -105,20 +105,20 @@ static int max7310_tx(I2CSlave *i2c, uint8_t data) s->level = (s->level & s->direction) | (data & ~s->direction); break; - case 0x02: /* Polarity inversion */ + case 0x02: /* Polarity inversion */ s->polarity = data; break; - case 0x03: /* Configuration */ + case 0x03: /* Configuration */ s->level &= ~(s->direction ^ data); s->direction = data; break; - case 0x04: /* Timeout */ + case 0x04: /* Timeout */ s->status = data; break; - case 0x00: /* Input port - ignore writes */ + case 0x00: /* Input port - ignore writes */ break; default: qemu_log_mask(LOG_UNIMP, "%s: Unsupported register 0x02%" PRIx8 "\n", @@ -155,7 +155,7 @@ static const VMStateDescription vmstate_max7310 = { .name = "max7310", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(i2c_command_byte, MAX7310State), VMSTATE_INT32(len, MAX7310State), VMSTATE_UINT8(level, MAX7310State), @@ -183,11 +183,10 @@ static void max7310_gpio_set(void *opaque, int line, int level) * but also accepts sequences that are not SMBus so return an I2C device. */ static void max7310_realize(DeviceState *dev, Error **errp) { - I2CSlave *i2c = I2C_SLAVE(dev); MAX7310State *s = MAX7310(dev); - qdev_init_gpio_in(&i2c->qdev, max7310_gpio_set, 8); - qdev_init_gpio_out(&i2c->qdev, s->handler, 8); + qdev_init_gpio_in(dev, max7310_gpio_set, ARRAY_SIZE(s->handler)); + qdev_init_gpio_out(dev, s->handler, ARRAY_SIZE(s->handler)); } static void max7310_class_init(ObjectClass *klass, void *data) diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build index 7bd6a57264..a7495d196a 100644 --- a/hw/gpio/meson.build +++ b/hw/gpio/meson.build @@ -1,14 +1,21 @@ -softmmu_ss.add(when: 'CONFIG_E500', if_true: files('mpc8xxx.c')) -softmmu_ss.add(when: 'CONFIG_GPIO_KEY', if_true: files('gpio_key.c')) -softmmu_ss.add(when: 'CONFIG_GPIO_PWR', if_true: files('gpio_pwr.c')) -softmmu_ss.add(when: 'CONFIG_MAX7310', if_true: files('max7310.c')) -softmmu_ss.add(when: 'CONFIG_PL061', if_true: files('pl061.c')) -softmmu_ss.add(when: 'CONFIG_ZAURUS', if_true: files('zaurus.c')) +system_ss.add(when: 'CONFIG_GPIO_KEY', if_true: files('gpio_key.c')) +system_ss.add(when: 'CONFIG_GPIO_MPC8XXX', if_true: files('mpc8xxx.c')) +system_ss.add(when: 'CONFIG_GPIO_PWR', if_true: files('gpio_pwr.c')) +system_ss.add(when: 'CONFIG_MAX7310', if_true: files('max7310.c')) +system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c')) +system_ss.add(when: 'CONFIG_PCA9554', if_true: files('pca9554.c')) +system_ss.add(when: 'CONFIG_PL061', if_true: files('pl061.c')) +system_ss.add(when: 'CONFIG_ZAURUS', if_true: files('zaurus.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpio.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_gpio.c')) -softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_gpio.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gpio.c')) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_gpio.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpio.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_gpio.c')) +system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_gpio.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gpio.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files( + 'bcm2835_gpio.c', + 'bcm2838_gpio.c' +)) +system_ss.add(when: 'CONFIG_STM32L4X5_SOC', if_true: files('stm32l4x5_gpio.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c')) +system_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c')) +system_ss.add(when: 'CONFIG_PCF8574', if_true: files('pcf8574.c')) diff --git a/hw/gpio/mpc8xxx.c b/hw/gpio/mpc8xxx.c index cb42acb6da..0b3f9e516d 100644 --- a/hw/gpio/mpc8xxx.c +++ b/hw/gpio/mpc8xxx.c @@ -48,7 +48,7 @@ static const VMStateDescription vmstate_mpc8xxx_gpio = { .name = "mpc8xxx_gpio", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(dir, MPC8XXXGPIOState), VMSTATE_UINT32(odr, MPC8XXXGPIOState), VMSTATE_UINT32(dat, MPC8XXXGPIOState), diff --git a/hw/gpio/npcm7xx_gpio.c b/hw/gpio/npcm7xx_gpio.c index 3376901ab1..6e70ac1f24 100644 --- a/hw/gpio/npcm7xx_gpio.c +++ b/hw/gpio/npcm7xx_gpio.c @@ -377,7 +377,7 @@ static const VMStateDescription vmstate_npcm7xx_gpio = { .name = "npcm7xx-gpio", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(pin_level, NPCM7xxGPIOState), VMSTATE_UINT32(ext_level, NPCM7xxGPIOState), VMSTATE_UINT32(ext_driven, NPCM7xxGPIOState), diff --git a/hw/gpio/nrf51_gpio.c b/hw/gpio/nrf51_gpio.c index b47fddf4ed..ffc7dff796 100644 --- a/hw/gpio/nrf51_gpio.c +++ b/hw/gpio/nrf51_gpio.c @@ -78,6 +78,7 @@ static void update_state(NRF51GPIOState *s) int pull; size_t i; bool connected_out, dir, connected_in, out, in, input; + bool assert_detect = false; for (i = 0; i < NRF51_GPIO_PINS; i++) { pull = pull_value(s->cnf[i]); @@ -99,7 +100,15 @@ static void update_state(NRF51GPIOState *s) qemu_log_mask(LOG_GUEST_ERROR, "GPIO pin %zu short circuited\n", i); } - if (!connected_in) { + if (connected_in) { + uint32_t detect_config = extract32(s->cnf[i], 16, 2); + if ((detect_config == 2) && (in == 1)) { + assert_detect = true; + } + if ((detect_config == 3) && (in == 0)) { + assert_detect = true; + } + } else { /* * Floating input: the output stimulates IN if connected, * otherwise pull-up/pull-down resistors put a value on both @@ -116,6 +125,8 @@ static void update_state(NRF51GPIOState *s) } update_output_irq(s, i, connected_out, out); } + + qemu_set_irq(s->detect, assert_detect); } /* @@ -269,7 +280,7 @@ static const VMStateDescription vmstate_nrf51_gpio = { .name = TYPE_NRF51_GPIO, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(out, NRF51GPIOState), VMSTATE_UINT32(in, NRF51GPIOState), VMSTATE_UINT32(in_mask, NRF51GPIOState), @@ -291,6 +302,7 @@ static void nrf51_gpio_init(Object *obj) qdev_init_gpio_in(DEVICE(s), nrf51_gpio_set, NRF51_GPIO_PINS); qdev_init_gpio_out(DEVICE(s), s->output, NRF51_GPIO_PINS); + qdev_init_gpio_out_named(DEVICE(s), &s->detect, "detect", 1); } static void nrf51_gpio_class_init(ObjectClass *klass, void *data) diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c index bd0841d57f..a3341d70f1 100644 --- a/hw/gpio/omap_gpio.c +++ b/hw/gpio/omap_gpio.c @@ -41,7 +41,7 @@ struct omap_gpio_s { uint16_t pins; }; -struct omap_gpif_s { +struct Omap1GpioState { SysBusDevice parent_obj; MemoryRegion iomem; @@ -53,7 +53,8 @@ struct omap_gpif_s { /* General-Purpose I/O of OMAP1 */ static void omap_gpio_set(void *opaque, int line, int level) { - struct omap_gpio_s *s = &((struct omap_gpif_s *) opaque)->omap1; + Omap1GpioState *p = opaque; + struct omap_gpio_s *s = &p->omap1; uint16_t prev = s->inputs; if (level) @@ -71,7 +72,7 @@ static void omap_gpio_set(void *opaque, int line, int level) static uint64_t omap_gpio_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; + struct omap_gpio_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; if (size != 2) { @@ -109,7 +110,7 @@ static uint64_t omap_gpio_read(void *opaque, hwaddr addr, static void omap_gpio_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; + struct omap_gpio_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; uint16_t diff; int ln; @@ -209,7 +210,7 @@ struct omap2_gpio_s { uint8_t delay; }; -struct omap2_gpif_s { +struct Omap2GpioState { SysBusDevice parent_obj; MemoryRegion iomem; @@ -273,7 +274,7 @@ static inline void omap2_gpio_module_int(struct omap2_gpio_s *s, int line) static void omap2_gpio_set(void *opaque, int line, int level) { - struct omap2_gpif_s *p = opaque; + Omap2GpioState *p = opaque; struct omap2_gpio_s *s = &p->modules[line >> 5]; line &= 31; @@ -308,7 +309,7 @@ static void omap2_gpio_module_reset(struct omap2_gpio_s *s) static uint32_t omap2_gpio_module_read(void *opaque, hwaddr addr) { - struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; + struct omap2_gpio_s *s = opaque; switch (addr) { case 0x00: /* GPIO_REVISION */ @@ -381,7 +382,7 @@ static uint32_t omap2_gpio_module_read(void *opaque, hwaddr addr) static void omap2_gpio_module_write(void *opaque, hwaddr addr, uint32_t value) { - struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; + struct omap2_gpio_s *s = opaque; uint32_t diff; int ln; @@ -593,14 +594,14 @@ static const MemoryRegionOps omap2_gpio_module_ops = { static void omap_gpif_reset(DeviceState *dev) { - struct omap_gpif_s *s = OMAP1_GPIO(dev); + Omap1GpioState *s = OMAP1_GPIO(dev); omap_gpio_reset(&s->omap1); } static void omap2_gpif_reset(DeviceState *dev) { - struct omap2_gpif_s *s = OMAP2_GPIO(dev); + Omap2GpioState *s = OMAP2_GPIO(dev); int i; for (i = 0; i < s->modulecount; i++) { @@ -610,10 +611,9 @@ static void omap2_gpif_reset(DeviceState *dev) s->gpo = 0; } -static uint64_t omap2_gpif_top_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap2_gpif_top_read(void *opaque, hwaddr addr, unsigned size) { - struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque; + Omap2GpioState *s = opaque; switch (addr) { case 0x00: /* IPGENERICOCPSPL_REVISION */ @@ -642,7 +642,7 @@ static uint64_t omap2_gpif_top_read(void *opaque, hwaddr addr, static void omap2_gpif_top_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque; + Omap2GpioState *s = opaque; switch (addr) { case 0x00: /* IPGENERICOCPSPL_REVISION */ @@ -677,7 +677,7 @@ static const MemoryRegionOps omap2_gpif_top_ops = { static void omap_gpio_init(Object *obj) { DeviceState *dev = DEVICE(obj); - struct omap_gpif_s *s = OMAP1_GPIO(obj); + Omap1GpioState *s = OMAP1_GPIO(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); qdev_init_gpio_in(dev, omap_gpio_set, 16); @@ -690,7 +690,7 @@ static void omap_gpio_init(Object *obj) static void omap_gpio_realize(DeviceState *dev, Error **errp) { - struct omap_gpif_s *s = OMAP1_GPIO(dev); + Omap1GpioState *s = OMAP1_GPIO(dev); if (!s->clk) { error_setg(errp, "omap-gpio: clk not connected"); @@ -699,7 +699,7 @@ static void omap_gpio_realize(DeviceState *dev, Error **errp) static void omap2_gpio_realize(DeviceState *dev, Error **errp) { - struct omap2_gpif_s *s = OMAP2_GPIO(dev); + Omap2GpioState *s = OMAP2_GPIO(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); int i; @@ -742,13 +742,13 @@ static void omap2_gpio_realize(DeviceState *dev, Error **errp) } } -void omap_gpio_set_clk(omap_gpif *gpio, omap_clk clk) +void omap_gpio_set_clk(Omap1GpioState *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_INT32("mpu_model", Omap1GpioState, mpu_model, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -766,24 +766,24 @@ static void omap_gpio_class_init(ObjectClass *klass, void *data) static const TypeInfo omap_gpio_info = { .name = TYPE_OMAP1_GPIO, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct omap_gpif_s), + .instance_size = sizeof(Omap1GpioState), .instance_init = omap_gpio_init, .class_init = omap_gpio_class_init, }; -void omap2_gpio_set_iclk(omap2_gpif *gpio, omap_clk clk) +void omap2_gpio_set_iclk(Omap2GpioState *gpio, omap_clk clk) { gpio->iclk = clk; } -void omap2_gpio_set_fclk(omap2_gpif *gpio, uint8_t i, omap_clk clk) +void omap2_gpio_set_fclk(Omap2GpioState *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_INT32("mpu_model", Omap2GpioState, mpu_model, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -801,7 +801,7 @@ static void omap2_gpio_class_init(ObjectClass *klass, void *data) static const TypeInfo omap2_gpio_info = { .name = TYPE_OMAP2_GPIO, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct omap2_gpif_s), + .instance_size = sizeof(Omap2GpioState), .class_init = omap2_gpio_class_init, }; diff --git a/hw/misc/pca9552.c b/hw/gpio/pca9552.c similarity index 85% rename from hw/misc/pca9552.c rename to hw/gpio/pca9552.c index fff19e369a..27d4db0680 100644 --- a/hw/misc/pca9552.c +++ b/hw/gpio/pca9552.c @@ -15,8 +15,8 @@ #include "qemu/module.h" #include "qemu/bitops.h" #include "hw/qdev-properties.h" -#include "hw/misc/pca9552.h" -#include "hw/misc/pca9552_regs.h" +#include "hw/gpio/pca9552.h" +#include "hw/gpio/pca9552_regs.h" #include "hw/irq.h" #include "migration/vmstate.h" #include "qapi/error.h" @@ -36,11 +36,16 @@ typedef struct PCA955xClass PCA955xClass; DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X, TYPE_PCA955X) - +/* + * Note: The LED_ON and LED_OFF configuration values for the PCA955X + * chips are the reverse of the PCA953X family of chips. + */ #define PCA9552_LED_ON 0x0 #define PCA9552_LED_OFF 0x1 #define PCA9552_LED_PWM0 0x2 #define PCA9552_LED_PWM1 0x3 +#define PCA9552_PIN_LOW 0x0 +#define PCA9552_PIN_HIZ 0x1 static const char *led_state[] = {"on", "off", "pwm0", "pwm1"}; @@ -107,17 +112,27 @@ static void pca955x_update_pin_input(PCA955xState *s) for (i = 0; i < k->pin_count; i++) { uint8_t input_reg = PCA9552_INPUT0 + (i / 8); - uint8_t input_shift = (i % 8); + uint8_t bit_mask = 1 << (i % 8); uint8_t config = pca955x_pin_get_config(s, i); + uint8_t old_value = s->regs[input_reg] & bit_mask; + uint8_t new_value; switch (config) { case PCA9552_LED_ON: - qemu_set_irq(s->gpio[i], 1); - s->regs[input_reg] |= 1 << input_shift; + /* Pin is set to 0V to turn on LED */ + s->regs[input_reg] &= ~bit_mask; break; case PCA9552_LED_OFF: - qemu_set_irq(s->gpio[i], 0); - s->regs[input_reg] &= ~(1 << input_shift); + /* + * Pin is set to Hi-Z to turn off LED and + * pullup sets it to a logical 1 unless + * external device drives it low. + */ + if (s->ext_state[i] == PCA9552_PIN_LOW) { + s->regs[input_reg] &= ~bit_mask; + } else { + s->regs[input_reg] |= bit_mask; + } break; case PCA9552_LED_PWM0: case PCA9552_LED_PWM1: @@ -125,6 +140,12 @@ static void pca955x_update_pin_input(PCA955xState *s) default: break; } + + /* update irq state only if pin state changed */ + new_value = s->regs[input_reg] & bit_mask; + if (new_value != old_value) { + qemu_set_irq(s->gpio_out[i], !!new_value); + } } } @@ -328,10 +349,11 @@ static const VMStateDescription pca9552_vmstate = { .name = "PCA9552", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(len, PCA955xState), VMSTATE_UINT8(pointer, PCA955xState), VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS), + VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX), VMSTATE_I2C_SLAVE(i2c, PCA955xState), VMSTATE_END_OF_LIST() } @@ -350,6 +372,7 @@ static void pca9552_reset(DeviceState *dev) s->regs[PCA9552_LS2] = 0x55; s->regs[PCA9552_LS3] = 0x55; + memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX); pca955x_update_pin_input(s); s->pointer = 0xFF; @@ -372,6 +395,26 @@ static void pca955x_initfn(Object *obj) } } +static void pca955x_set_ext_state(PCA955xState *s, int pin, int level) +{ + if (s->ext_state[pin] != level) { + uint16_t pins_status = pca955x_pins_get_status(s); + s->ext_state[pin] = level; + pca955x_update_pin_input(s); + pca955x_display_pins_status(s, pins_status); + } +} + +static void pca955x_gpio_in_handler(void *opaque, int pin, int level) +{ + + PCA955xState *s = PCA955X(opaque); + PCA955xClass *k = PCA955X_GET_CLASS(s); + + assert((pin >= 0) && (pin < k->pin_count)); + pca955x_set_ext_state(s, pin, level); +} + static void pca955x_realize(DeviceState *dev, Error **errp) { PCA955xClass *k = PCA955X_GET_CLASS(dev); @@ -381,7 +424,8 @@ static void pca955x_realize(DeviceState *dev, Error **errp) s->description = g_strdup("pca-unspecified"); } - qdev_init_gpio_out(dev, s->gpio, k->pin_count); + qdev_init_gpio_out(dev, s->gpio_out, k->pin_count); + qdev_init_gpio_in(dev, pca955x_gpio_in_handler, k->pin_count); } static Property pca955x_properties[] = { diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c new file mode 100644 index 0000000000..7d10a64ba7 --- /dev/null +++ b/hw/gpio/pca9554.c @@ -0,0 +1,328 @@ +/* + * PCA9554 I/O port + * + * Copyright (c) 2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/bitops.h" +#include "hw/qdev-properties.h" +#include "hw/gpio/pca9554.h" +#include "hw/gpio/pca9554_regs.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "trace.h" +#include "qom/object.h" + +struct PCA9554Class { + /*< private >*/ + I2CSlaveClass parent_class; + /*< public >*/ +}; +typedef struct PCA9554Class PCA9554Class; + +DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554, + TYPE_PCA9554) + +#define PCA9554_PIN_LOW 0x0 +#define PCA9554_PIN_HIZ 0x1 + +static const char *pin_state[] = {"low", "high"}; + +static void pca9554_update_pin_input(PCA9554State *s) +{ + int i; + uint8_t config = s->regs[PCA9554_CONFIG]; + uint8_t output = s->regs[PCA9554_OUTPUT]; + uint8_t internal_state = config | output; + + for (i = 0; i < PCA9554_PIN_COUNT; i++) { + uint8_t bit_mask = 1 << i; + uint8_t internal_pin_state = (internal_state >> i) & 0x1; + uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask; + uint8_t new_value; + + switch (internal_pin_state) { + case PCA9554_PIN_LOW: + s->regs[PCA9554_INPUT] &= ~bit_mask; + break; + case PCA9554_PIN_HIZ: + /* + * pullup sets it to a logical 1 unless + * external device drives it low. + */ + if (s->ext_state[i] == PCA9554_PIN_LOW) { + s->regs[PCA9554_INPUT] &= ~bit_mask; + } else { + s->regs[PCA9554_INPUT] |= bit_mask; + } + break; + default: + break; + } + + /* update irq state only if pin state changed */ + new_value = s->regs[PCA9554_INPUT] & bit_mask; + if (new_value != old_value) { + if (new_value) { + /* changed from 0 to 1 */ + qemu_set_irq(s->gpio_out[i], 1); + } else { + /* changed from 1 to 0 */ + qemu_set_irq(s->gpio_out[i], 0); + } + } + } +} + +static uint8_t pca9554_read(PCA9554State *s, uint8_t reg) +{ + switch (reg) { + case PCA9554_INPUT: + return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY]; + case PCA9554_OUTPUT: + case PCA9554_POLARITY: + case PCA9554_CONFIG: + return s->regs[reg]; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n", + __func__, reg); + return 0xFF; + } +} + +static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data) +{ + switch (reg) { + case PCA9554_OUTPUT: + case PCA9554_CONFIG: + s->regs[reg] = data; + pca9554_update_pin_input(s); + break; + case PCA9554_POLARITY: + s->regs[reg] = data; + break; + case PCA9554_INPUT: + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n", + __func__, reg); + } +} + +static uint8_t pca9554_recv(I2CSlave *i2c) +{ + PCA9554State *s = PCA9554(i2c); + uint8_t ret; + + ret = pca9554_read(s, s->pointer & 0x3); + + return ret; +} + +static int pca9554_send(I2CSlave *i2c, uint8_t data) +{ + PCA9554State *s = PCA9554(i2c); + + /* First byte sent by is the register address */ + if (s->len == 0) { + s->pointer = data; + s->len++; + } else { + pca9554_write(s, s->pointer & 0x3, data); + } + + return 0; +} + +static int pca9554_event(I2CSlave *i2c, enum i2c_event event) +{ + PCA9554State *s = PCA9554(i2c); + + s->len = 0; + return 0; +} + +static void pca9554_get_pin(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PCA9554State *s = PCA9554(obj); + int pin, rc; + uint8_t state; + + rc = sscanf(name, "pin%2d", &pin); + if (rc != 1) { + error_setg(errp, "%s: error reading %s", __func__, name); + return; + } + if (pin < 0 || pin >= PCA9554_PIN_COUNT) { + error_setg(errp, "%s invalid pin %s", __func__, name); + return; + } + + state = pca9554_read(s, PCA9554_CONFIG); + state |= pca9554_read(s, PCA9554_OUTPUT); + state = (state >> pin) & 0x1; + visit_type_str(v, name, (char **)&pin_state[state], errp); +} + +static void pca9554_set_pin(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PCA9554State *s = PCA9554(obj); + int pin, rc, val; + uint8_t state, mask; + char *state_str; + + if (!visit_type_str(v, name, &state_str, errp)) { + return; + } + rc = sscanf(name, "pin%2d", &pin); + if (rc != 1) { + error_setg(errp, "%s: error reading %s", __func__, name); + return; + } + if (pin < 0 || pin >= PCA9554_PIN_COUNT) { + error_setg(errp, "%s invalid pin %s", __func__, name); + return; + } + + for (state = 0; state < ARRAY_SIZE(pin_state); state++) { + if (!strcmp(state_str, pin_state[state])) { + break; + } + } + if (state >= ARRAY_SIZE(pin_state)) { + error_setg(errp, "%s invalid pin state %s", __func__, state_str); + return; + } + + /* First, modify the output register bit */ + val = pca9554_read(s, PCA9554_OUTPUT); + mask = 0x1 << pin; + if (state == PCA9554_PIN_LOW) { + val &= ~(mask); + } else { + val |= mask; + } + pca9554_write(s, PCA9554_OUTPUT, val); + + /* Then, clear the config register bit for output mode */ + val = pca9554_read(s, PCA9554_CONFIG); + val &= ~mask; + pca9554_write(s, PCA9554_CONFIG, val); +} + +static const VMStateDescription pca9554_vmstate = { + .name = "PCA9554", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8(len, PCA9554State), + VMSTATE_UINT8(pointer, PCA9554State), + VMSTATE_UINT8_ARRAY(regs, PCA9554State, PCA9554_NR_REGS), + VMSTATE_UINT8_ARRAY(ext_state, PCA9554State, PCA9554_PIN_COUNT), + VMSTATE_I2C_SLAVE(i2c, PCA9554State), + VMSTATE_END_OF_LIST() + } +}; + +static void pca9554_reset(DeviceState *dev) +{ + PCA9554State *s = PCA9554(dev); + + s->regs[PCA9554_INPUT] = 0xFF; + s->regs[PCA9554_OUTPUT] = 0xFF; + s->regs[PCA9554_POLARITY] = 0x0; /* No pins are inverted */ + s->regs[PCA9554_CONFIG] = 0xFF; /* All pins are inputs */ + + memset(s->ext_state, PCA9554_PIN_HIZ, PCA9554_PIN_COUNT); + pca9554_update_pin_input(s); + + s->pointer = 0x0; + s->len = 0; +} + +static void pca9554_initfn(Object *obj) +{ + int pin; + + for (pin = 0; pin < PCA9554_PIN_COUNT; pin++) { + char *name; + + name = g_strdup_printf("pin%d", pin); + object_property_add(obj, name, "bool", pca9554_get_pin, pca9554_set_pin, + NULL, NULL); + g_free(name); + } +} + +static void pca9554_set_ext_state(PCA9554State *s, int pin, int level) +{ + if (s->ext_state[pin] != level) { + s->ext_state[pin] = level; + pca9554_update_pin_input(s); + } +} + +static void pca9554_gpio_in_handler(void *opaque, int pin, int level) +{ + + PCA9554State *s = PCA9554(opaque); + + assert((pin >= 0) && (pin < PCA9554_PIN_COUNT)); + pca9554_set_ext_state(s, pin, level); +} + +static void pca9554_realize(DeviceState *dev, Error **errp) +{ + PCA9554State *s = PCA9554(dev); + + if (!s->description) { + s->description = g_strdup("pca9554"); + } + + qdev_init_gpio_out(dev, s->gpio_out, PCA9554_PIN_COUNT); + qdev_init_gpio_in(dev, pca9554_gpio_in_handler, PCA9554_PIN_COUNT); +} + +static Property pca9554_properties[] = { + DEFINE_PROP_STRING("description", PCA9554State, description), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pca9554_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + + k->event = pca9554_event; + k->recv = pca9554_recv; + k->send = pca9554_send; + dc->realize = pca9554_realize; + dc->reset = pca9554_reset; + dc->vmsd = &pca9554_vmstate; + device_class_set_props(dc, pca9554_properties); +} + +static const TypeInfo pca9554_info = { + .name = TYPE_PCA9554, + .parent = TYPE_I2C_SLAVE, + .instance_init = pca9554_initfn, + .instance_size = sizeof(PCA9554State), + .class_init = pca9554_class_init, + .class_size = sizeof(PCA9554Class), + .abstract = false, +}; + +static void pca9554_register_types(void) +{ + type_register_static(&pca9554_info); +} + +type_init(pca9554_register_types) diff --git a/hw/gpio/pcf8574.c b/hw/gpio/pcf8574.c new file mode 100644 index 0000000000..d37909e2ad --- /dev/null +++ b/hw/gpio/pcf8574.c @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * NXP PCF8574 8-port I2C GPIO expansion chip. + * Copyright (c) 2024 KNS Group (YADRO). + * Written by Dmitrii Sharikhin + */ + +#include "qemu/osdep.h" +#include "hw/i2c/i2c.h" +#include "hw/gpio/pcf8574.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qom/object.h" + +/* + * PCF8574 and compatible chips incorporate quasi-bidirectional + * IO. Electrically it means that device sustain pull-up to line + * unless IO port is configured as output _and_ driven low. + * + * IO access is implemented as simple I2C single-byte read + * or write operation. So, to configure line to input user write 1 + * to corresponding bit. To configure line to output and drive it low + * user write 0 to corresponding bit. + * + * In essence, user can think of quasi-bidirectional IO as + * open-drain line, except presence of builtin rising edge acceleration + * embedded in PCF8574 IC + * + * PCF8574 has interrupt request line, which is being pulled down when + * port line state differs from last read. Port read operation clears + * state and INT line returns to high state via pullup. + */ + +OBJECT_DECLARE_SIMPLE_TYPE(PCF8574State, PCF8574) + +#define PORTS_COUNT (8) + +struct PCF8574State { + I2CSlave parent_obj; + uint8_t lastrq; /* Last requested state. If changed - assert irq */ + uint8_t input; /* external electrical line state */ + uint8_t output; /* Pull-up (1) or drive low (0) on bit */ + qemu_irq handler[PORTS_COUNT]; + qemu_irq intrq; /* External irq request */ +}; + +static void pcf8574_reset(DeviceState *dev) +{ + PCF8574State *s = PCF8574(dev); + s->lastrq = MAKE_64BIT_MASK(0, PORTS_COUNT); + s->input = MAKE_64BIT_MASK(0, PORTS_COUNT); + s->output = MAKE_64BIT_MASK(0, PORTS_COUNT); +} + +static inline uint8_t pcf8574_line_state(PCF8574State *s) +{ + /* we driving line low or external circuit does that */ + return s->input & s->output; +} + +static uint8_t pcf8574_rx(I2CSlave *i2c) +{ + PCF8574State *s = PCF8574(i2c); + uint8_t linestate = pcf8574_line_state(s); + if (s->lastrq != linestate) { + s->lastrq = linestate; + if (s->intrq) { + qemu_set_irq(s->intrq, 1); + } + } + return linestate; +} + +static int pcf8574_tx(I2CSlave *i2c, uint8_t data) +{ + PCF8574State *s = PCF8574(i2c); + uint8_t prev; + uint8_t diff; + uint8_t actual; + int line = 0; + + prev = pcf8574_line_state(s); + s->output = data; + actual = pcf8574_line_state(s); + + for (diff = (actual ^ prev); diff; diff &= ~(1 << line)) { + line = ctz32(diff); + if (s->handler[line]) { + qemu_set_irq(s->handler[line], (actual >> line) & 1); + } + } + + if (s->intrq) { + qemu_set_irq(s->intrq, actual == s->lastrq); + } + + return 0; +} + +static const VMStateDescription vmstate_pcf8574 = { + .name = "pcf8574", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_I2C_SLAVE(parent_obj, PCF8574State), + VMSTATE_UINT8(lastrq, PCF8574State), + VMSTATE_UINT8(input, PCF8574State), + VMSTATE_UINT8(output, PCF8574State), + VMSTATE_END_OF_LIST() + } +}; + +static void pcf8574_gpio_set(void *opaque, int line, int level) +{ + PCF8574State *s = (PCF8574State *) opaque; + assert(line >= 0 && line < ARRAY_SIZE(s->handler)); + + if (level) { + s->input |= (1 << line); + } else { + s->input &= ~(1 << line); + } + + if (pcf8574_line_state(s) != s->lastrq && s->intrq) { + qemu_set_irq(s->intrq, 0); + } +} + +static void pcf8574_realize(DeviceState *dev, Error **errp) +{ + PCF8574State *s = PCF8574(dev); + + qdev_init_gpio_in(dev, pcf8574_gpio_set, ARRAY_SIZE(s->handler)); + qdev_init_gpio_out(dev, s->handler, ARRAY_SIZE(s->handler)); + qdev_init_gpio_out_named(dev, &s->intrq, "nINT", 1); +} + +static void pcf8574_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + + k->recv = pcf8574_rx; + k->send = pcf8574_tx; + dc->realize = pcf8574_realize; + dc->reset = pcf8574_reset; + dc->vmsd = &vmstate_pcf8574; +} + +static const TypeInfo pcf8574_infos[] = { + { + .name = TYPE_PCF8574, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(PCF8574State), + .class_init = pcf8574_class_init, + } +}; + +DEFINE_TYPES(pcf8574_infos); diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index 899be861cc..86f2383655 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -87,7 +87,7 @@ static const VMStateDescription vmstate_pl061 = { .name = "pl061", .version_id = 4, .minimum_version_id = 4, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(locked, PL061State), VMSTATE_UINT32(data, PL061State), VMSTATE_UINT32(old_out_data, PL061State), diff --git a/hw/gpio/sifive_gpio.c b/hw/gpio/sifive_gpio.c index 78bf29e996..995a43c795 100644 --- a/hw/gpio/sifive_gpio.c +++ b/hw/gpio/sifive_gpio.c @@ -326,7 +326,7 @@ static const VMStateDescription vmstate_sifive_gpio = { .name = TYPE_SIFIVE_GPIO, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(value, SIFIVEGPIOState), VMSTATE_UINT32(input_en, SIFIVEGPIOState), VMSTATE_UINT32(output_en, SIFIVEGPIOState), diff --git a/hw/gpio/stm32l4x5_gpio.c b/hw/gpio/stm32l4x5_gpio.c new file mode 100644 index 0000000000..63b8763e9d --- /dev/null +++ b/hw/gpio/stm32l4x5_gpio.c @@ -0,0 +1,477 @@ +/* + * STM32L4x5 GPIO (General Purpose Input/Ouput) + * + * Copyright (c) 2024 Arnaud Minier + * Copyright (c) 2024 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/gpio/stm32l4x5_gpio.h" +#include "hw/irq.h" +#include "hw/qdev-clock.h" +#include "hw/qdev-properties.h" +#include "qapi/visitor.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "trace.h" + +#define GPIO_MODER 0x00 +#define GPIO_OTYPER 0x04 +#define GPIO_OSPEEDR 0x08 +#define GPIO_PUPDR 0x0C +#define GPIO_IDR 0x10 +#define GPIO_ODR 0x14 +#define GPIO_BSRR 0x18 +#define GPIO_LCKR 0x1C +#define GPIO_AFRL 0x20 +#define GPIO_AFRH 0x24 +#define GPIO_BRR 0x28 +#define GPIO_ASCR 0x2C + +/* 0b11111111_11111111_00000000_00000000 */ +#define RESERVED_BITS_MASK 0xFFFF0000 + +static void update_gpio_idr(Stm32l4x5GpioState *s); + +static bool is_pull_up(Stm32l4x5GpioState *s, unsigned pin) +{ + return extract32(s->pupdr, 2 * pin, 2) == 1; +} + +static bool is_pull_down(Stm32l4x5GpioState *s, unsigned pin) +{ + return extract32(s->pupdr, 2 * pin, 2) == 2; +} + +static bool is_output(Stm32l4x5GpioState *s, unsigned pin) +{ + return extract32(s->moder, 2 * pin, 2) == 1; +} + +static bool is_open_drain(Stm32l4x5GpioState *s, unsigned pin) +{ + return extract32(s->otyper, pin, 1) == 1; +} + +static bool is_push_pull(Stm32l4x5GpioState *s, unsigned pin) +{ + return extract32(s->otyper, pin, 1) == 0; +} + +static void stm32l4x5_gpio_reset_hold(Object *obj) +{ + Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj); + + s->moder = s->moder_reset; + s->otyper = 0x00000000; + s->ospeedr = s->ospeedr_reset; + s->pupdr = s->pupdr_reset; + s->idr = 0x00000000; + s->odr = 0x00000000; + s->lckr = 0x00000000; + s->afrl = 0x00000000; + s->afrh = 0x00000000; + s->ascr = 0x00000000; + + s->disconnected_pins = 0xFFFF; + s->pins_connected_high = 0x0000; + update_gpio_idr(s); +} + +static void stm32l4x5_gpio_set(void *opaque, int line, int level) +{ + Stm32l4x5GpioState *s = opaque; + /* + * The pin isn't set if line is configured in output mode + * except if level is 0 and the output is open-drain. + * This way there will be no short-circuit prone situations. + */ + if (is_output(s, line) && !(is_open_drain(s, line) && (level == 0))) { + qemu_log_mask(LOG_GUEST_ERROR, "Line %d can't be driven externally\n", + line); + return; + } + + s->disconnected_pins &= ~(1 << line); + if (level) { + s->pins_connected_high |= (1 << line); + } else { + s->pins_connected_high &= ~(1 << line); + } + trace_stm32l4x5_gpio_pins(s->name, s->disconnected_pins, + s->pins_connected_high); + update_gpio_idr(s); +} + + +static void update_gpio_idr(Stm32l4x5GpioState *s) +{ + uint32_t new_idr_mask = 0; + uint32_t new_idr = s->odr; + uint32_t old_idr = s->idr; + int new_pin_state, old_pin_state; + + for (int i = 0; i < GPIO_NUM_PINS; i++) { + if (is_output(s, i)) { + if (is_push_pull(s, i)) { + new_idr_mask |= (1 << i); + } else if (!(s->odr & (1 << i))) { + /* open-drain ODR 0 */ + new_idr_mask |= (1 << i); + /* open-drain ODR 1 */ + } else if (!(s->disconnected_pins & (1 << i)) && + !(s->pins_connected_high & (1 << i))) { + /* open-drain ODR 1 with pin connected low */ + new_idr_mask |= (1 << i); + new_idr &= ~(1 << i); + /* open-drain ODR 1 with unactive pin */ + } else if (is_pull_up(s, i)) { + new_idr_mask |= (1 << i); + } else if (is_pull_down(s, i)) { + new_idr_mask |= (1 << i); + new_idr &= ~(1 << i); + } + /* + * The only case left is for open-drain ODR 1 + * with unactive pin without pull-up or pull-down : + * the value is floating. + */ + /* input or analog mode with connected pin */ + } else if (!(s->disconnected_pins & (1 << i))) { + if (s->pins_connected_high & (1 << i)) { + /* pin high */ + new_idr_mask |= (1 << i); + new_idr |= (1 << i); + } else { + /* pin low */ + new_idr_mask |= (1 << i); + new_idr &= ~(1 << i); + } + /* input or analog mode with disconnected pin */ + } else { + if (is_pull_up(s, i)) { + /* pull-up */ + new_idr_mask |= (1 << i); + new_idr |= (1 << i); + } else if (is_pull_down(s, i)) { + /* pull-down */ + new_idr_mask |= (1 << i); + new_idr &= ~(1 << i); + } + /* + * The only case left is for a disconnected pin + * without pull-up or pull-down : + * the value is floating. + */ + } + } + + s->idr = (old_idr & ~new_idr_mask) | (new_idr & new_idr_mask); + trace_stm32l4x5_gpio_update_idr(s->name, old_idr, s->idr); + + for (int i = 0; i < GPIO_NUM_PINS; i++) { + if (new_idr_mask & (1 << i)) { + new_pin_state = (new_idr & (1 << i)) > 0; + old_pin_state = (old_idr & (1 << i)) > 0; + if (new_pin_state > old_pin_state) { + qemu_irq_raise(s->pin[i]); + } else if (new_pin_state < old_pin_state) { + qemu_irq_lower(s->pin[i]); + } + } + } +} + +/* + * Return mask of pins that are both configured in output + * mode and externally driven (except pins in open-drain + * mode externally set to 0). + */ +static uint32_t get_gpio_pinmask_to_disconnect(Stm32l4x5GpioState *s) +{ + uint32_t pins_to_disconnect = 0; + for (int i = 0; i < GPIO_NUM_PINS; i++) { + /* for each connected pin in output mode */ + if (!(s->disconnected_pins & (1 << i)) && is_output(s, i)) { + /* if either push-pull or high level */ + if (is_push_pull(s, i) || s->pins_connected_high & (1 << i)) { + pins_to_disconnect |= (1 << i); + qemu_log_mask(LOG_GUEST_ERROR, + "Line %d can't be driven externally\n", + i); + } + } + } + return pins_to_disconnect; +} + +/* + * Set field `disconnected_pins` and call `update_gpio_idr()` + */ +static void disconnect_gpio_pins(Stm32l4x5GpioState *s, uint16_t lines) +{ + s->disconnected_pins |= lines; + trace_stm32l4x5_gpio_pins(s->name, s->disconnected_pins, + s->pins_connected_high); + update_gpio_idr(s); +} + +static void disconnected_pins_set(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) +{ + Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj); + uint16_t value; + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } + disconnect_gpio_pins(s, value); +} + +static void disconnected_pins_get(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) +{ + visit_type_uint16(v, name, (uint16_t *)opaque, errp); +} + +static void clock_freq_get(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) +{ + Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj); + uint32_t clock_freq_hz = clock_get_hz(s->clk); + visit_type_uint32(v, name, &clock_freq_hz, errp); +} + +static void stm32l4x5_gpio_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + Stm32l4x5GpioState *s = opaque; + + uint32_t value = val64; + trace_stm32l4x5_gpio_write(s->name, addr, val64); + + switch (addr) { + case GPIO_MODER: + s->moder = value; + disconnect_gpio_pins(s, get_gpio_pinmask_to_disconnect(s)); + qemu_log_mask(LOG_UNIMP, + "%s: Analog and AF modes aren't supported\n\ + Analog and AF mode behave like input mode\n", + __func__); + return; + case GPIO_OTYPER: + s->otyper = value & ~RESERVED_BITS_MASK; + disconnect_gpio_pins(s, get_gpio_pinmask_to_disconnect(s)); + return; + case GPIO_OSPEEDR: + qemu_log_mask(LOG_UNIMP, + "%s: Changing I/O output speed isn't supported\n\ + I/O speed is already maximal\n", + __func__); + s->ospeedr = value; + return; + case GPIO_PUPDR: + s->pupdr = value; + update_gpio_idr(s); + return; + case GPIO_IDR: + qemu_log_mask(LOG_UNIMP, + "%s: GPIO->IDR is read-only\n", + __func__); + return; + case GPIO_ODR: + s->odr = value & ~RESERVED_BITS_MASK; + update_gpio_idr(s); + return; + case GPIO_BSRR: { + uint32_t bits_to_reset = (value & RESERVED_BITS_MASK) >> GPIO_NUM_PINS; + uint32_t bits_to_set = value & ~RESERVED_BITS_MASK; + /* If both BSx and BRx are set, BSx has priority.*/ + s->odr &= ~bits_to_reset; + s->odr |= bits_to_set; + update_gpio_idr(s); + return; + } + case GPIO_LCKR: + qemu_log_mask(LOG_UNIMP, + "%s: Locking port bits configuration isn't supported\n", + __func__); + s->lckr = value & ~RESERVED_BITS_MASK; + return; + case GPIO_AFRL: + qemu_log_mask(LOG_UNIMP, + "%s: Alternate functions aren't supported\n", + __func__); + s->afrl = value; + return; + case GPIO_AFRH: + qemu_log_mask(LOG_UNIMP, + "%s: Alternate functions aren't supported\n", + __func__); + s->afrh = value; + return; + case GPIO_BRR: { + uint32_t bits_to_reset = value & ~RESERVED_BITS_MASK; + s->odr &= ~bits_to_reset; + update_gpio_idr(s); + return; + } + case GPIO_ASCR: + qemu_log_mask(LOG_UNIMP, + "%s: ADC function isn't supported\n", + __func__); + s->ascr = value & ~RESERVED_BITS_MASK; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); + } +} + +static uint64_t stm32l4x5_gpio_read(void *opaque, hwaddr addr, + unsigned int size) +{ + Stm32l4x5GpioState *s = opaque; + + trace_stm32l4x5_gpio_read(s->name, addr); + + switch (addr) { + case GPIO_MODER: + return s->moder; + case GPIO_OTYPER: + return s->otyper; + case GPIO_OSPEEDR: + return s->ospeedr; + case GPIO_PUPDR: + return s->pupdr; + case GPIO_IDR: + return s->idr; + case GPIO_ODR: + return s->odr; + case GPIO_BSRR: + return 0; + case GPIO_LCKR: + return s->lckr; + case GPIO_AFRL: + return s->afrl; + case GPIO_AFRH: + return s->afrh; + case GPIO_BRR: + return 0; + case GPIO_ASCR: + return s->ascr; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); + return 0; + } +} + +static const MemoryRegionOps stm32l4x5_gpio_ops = { + .read = stm32l4x5_gpio_read, + .write = stm32l4x5_gpio_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void stm32l4x5_gpio_init(Object *obj) +{ + Stm32l4x5GpioState *s = STM32L4X5_GPIO(obj); + + memory_region_init_io(&s->mmio, obj, &stm32l4x5_gpio_ops, s, + TYPE_STM32L4X5_GPIO, 0x400); + + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + qdev_init_gpio_out(DEVICE(obj), s->pin, GPIO_NUM_PINS); + qdev_init_gpio_in(DEVICE(obj), stm32l4x5_gpio_set, GPIO_NUM_PINS); + + s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0); + + object_property_add(obj, "disconnected-pins", "uint16", + disconnected_pins_get, disconnected_pins_set, + NULL, &s->disconnected_pins); + object_property_add(obj, "clock-freq-hz", "uint32", + clock_freq_get, NULL, NULL, NULL); +} + +static void stm32l4x5_gpio_realize(DeviceState *dev, Error **errp) +{ + Stm32l4x5GpioState *s = STM32L4X5_GPIO(dev); + if (!clock_has_source(s->clk)) { + error_setg(errp, "GPIO: clk input must be connected"); + return; + } +} + +static const VMStateDescription vmstate_stm32l4x5_gpio = { + .name = TYPE_STM32L4X5_GPIO, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]){ + VMSTATE_UINT32(moder, Stm32l4x5GpioState), + VMSTATE_UINT32(otyper, Stm32l4x5GpioState), + VMSTATE_UINT32(ospeedr, Stm32l4x5GpioState), + VMSTATE_UINT32(pupdr, Stm32l4x5GpioState), + VMSTATE_UINT32(idr, Stm32l4x5GpioState), + VMSTATE_UINT32(odr, Stm32l4x5GpioState), + VMSTATE_UINT32(lckr, Stm32l4x5GpioState), + VMSTATE_UINT32(afrl, Stm32l4x5GpioState), + VMSTATE_UINT32(afrh, Stm32l4x5GpioState), + VMSTATE_UINT32(ascr, Stm32l4x5GpioState), + VMSTATE_UINT16(disconnected_pins, Stm32l4x5GpioState), + VMSTATE_UINT16(pins_connected_high, Stm32l4x5GpioState), + VMSTATE_END_OF_LIST() + } +}; + +static Property stm32l4x5_gpio_properties[] = { + DEFINE_PROP_STRING("name", Stm32l4x5GpioState, name), + DEFINE_PROP_UINT32("mode-reset", Stm32l4x5GpioState, moder_reset, 0), + DEFINE_PROP_UINT32("ospeed-reset", Stm32l4x5GpioState, ospeedr_reset, 0), + DEFINE_PROP_UINT32("pupd-reset", Stm32l4x5GpioState, pupdr_reset, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stm32l4x5_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + device_class_set_props(dc, stm32l4x5_gpio_properties); + dc->vmsd = &vmstate_stm32l4x5_gpio; + dc->realize = stm32l4x5_gpio_realize; + rc->phases.hold = stm32l4x5_gpio_reset_hold; +} + +static const TypeInfo stm32l4x5_gpio_types[] = { + { + .name = TYPE_STM32L4X5_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Stm32l4x5GpioState), + .instance_init = stm32l4x5_gpio_init, + .class_init = stm32l4x5_gpio_class_init, + }, +}; + +DEFINE_TYPES(stm32l4x5_gpio_types) diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index 9736b362ac..b91cc7e9a4 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -13,6 +13,10 @@ nrf51_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x nrf51_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 nrf51_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 +# pca9552.c +pca955x_gpio_status(const char *description, const char *buf) "%s GPIOs 0-15 [%s]" +pca955x_gpio_change(const char *description, unsigned id, unsigned prev_state, unsigned current_state) "%s GPIO id:%u status: %u -> %u" + # pl061.c pl061_update(const char *id, uint32_t dir, uint32_t data, uint32_t pullups, uint32_t floating) "%s GPIODIR 0x%x GPIODATA 0x%x pullups 0x%x floating 0x%x" pl061_set_output(const char *id, int gpio, int level) "%s setting output %d to %d" @@ -31,3 +35,9 @@ sifive_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " val # aspeed_gpio.c aspeed_gpio_read(uint64_t offset, uint64_t value) "offset: 0x%" PRIx64 " value 0x%" PRIx64 aspeed_gpio_write(uint64_t offset, uint64_t value) "offset: 0x%" PRIx64 " value 0x%" PRIx64 + +# stm32l4x5_gpio.c +stm32l4x5_gpio_read(char *gpio, uint64_t addr) "GPIO%s addr: 0x%" PRIx64 " " +stm32l4x5_gpio_write(char *gpio, uint64_t addr, uint64_t data) "GPIO%s addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" +stm32l4x5_gpio_update_idr(char *gpio, uint32_t old_idr, uint32_t new_idr) "GPIO%s from: 0x%x to: 0x%x" +stm32l4x5_gpio_pins(char *gpio, uint16_t disconnected, uint16_t high) "GPIO%s disconnected pins: 0x%x levels: 0x%x" diff --git a/hw/gpio/zaurus.c b/hw/gpio/zaurus.c index 7cf52a5041..5884804c58 100644 --- a/hw/gpio/zaurus.c +++ b/hw/gpio/zaurus.c @@ -222,7 +222,7 @@ static const VMStateDescription vmstate_scoop_regs = { .version_id = 1, .minimum_version_id = 0, .post_load = scoop_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(status, ScoopInfo), VMSTATE_UINT16(power, ScoopInfo), VMSTATE_UINT32(gpio_level, ScoopInfo), diff --git a/hw/hppa/Kconfig b/hw/hppa/Kconfig index 5dd8b5b21e..ee7ffd2bfb 100644 --- a/hw/hppa/Kconfig +++ b/hw/hppa/Kconfig @@ -2,10 +2,13 @@ config HPPA_B160L bool imply PCI_DEVICES imply E1000_PCI + imply USB_OHCI_PCI imply VIRTIO_VGA + select ASTRO select DINO select LASI select SERIAL + select SERIAL_PCI select ISA_BUS select I8259 select IDE_CMD646 diff --git a/hw/hppa/hppa_hardware.h b/hw/hppa/hppa_hardware.h index a5ac3dd0fd..a9be7bb851 100644 --- a/hw/hppa/hppa_hardware.h +++ b/hw/hppa/hppa_hardware.h @@ -18,7 +18,6 @@ #define LASI_UART_HPA 0xffd05000 #define LASI_SCSI_HPA 0xffd06000 #define LASI_LAN_HPA 0xffd07000 -#define LASI_RTC_HPA 0xffd09000 #define LASI_LPT_HPA 0xffd02000 #define LASI_AUDIO_HPA 0xffd04000 #define LASI_PS2KBD_HPA 0xffd08000 diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 63b9dd2396..37ee6387e0 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -1,6 +1,8 @@ /* * QEMU HPPA hardware system emulator. - * Copyright 2018 Helge Deller + * (C) Copyright 2018-2023 Helge Deller + * + * This work is licensed under the GNU GPL license version 2 or later. */ #include "qemu/osdep.h" @@ -11,6 +13,7 @@ #include "qemu/error-report.h" #include "sysemu/reset.h" #include "sysemu/sysemu.h" +#include "sysemu/qtest.h" #include "sysemu/runstate.h" #include "hw/rtc/mc146818rtc.h" #include "hw/timer/i8254.h" @@ -20,7 +23,10 @@ #include "hw/input/lasips2.h" #include "hw/net/lasi_82596.h" #include "hw/nmi.h" +#include "hw/usb.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" +#include "hw/pci-host/astro.h" #include "hw/pci-host/dino.h" #include "hw/misc/lasi.h" #include "hppa_hardware.h" @@ -28,20 +34,18 @@ #include "qapi/error.h" #include "net/net.h" #include "qemu/log.h" -#include "net/net.h" -#define MAX_IDE_BUS 2 +#define MIN_SEABIOS_HPPA_VERSION 12 /* require at least this fw version */ -#define MIN_SEABIOS_HPPA_VERSION 6 /* require at least this fw version */ - -#define HPA_POWER_BUTTON (FIRMWARE_END - 0x10) +#define HPA_POWER_BUTTON (FIRMWARE_END - 0x10) +static hwaddr soft_power_reg; #define enable_lasi_lan() 0 +static DeviceState *lasi_dev; static void hppa_powerdown_req(Notifier *n, void *opaque) { - hwaddr soft_power_reg = HPA_POWER_BUTTON; uint32_t val; val = ldl_be_phys(&address_space_memory, soft_power_reg); @@ -84,7 +88,7 @@ static const MemoryRegionOps hppa_pci_ignore_ops = { }, }; -static ISABus *hppa_isa_bus(void) +static ISABus *hppa_isa_bus(hwaddr addr) { ISABus *isa_bus; qemu_irq *isa_irqs; @@ -93,25 +97,90 @@ static ISABus *hppa_isa_bus(void) isa_region = g_new(MemoryRegion, 1); memory_region_init_io(isa_region, NULL, &hppa_pci_ignore_ops, NULL, "isa-io", 0x800); - memory_region_add_subregion(get_system_memory(), IDE_HPA, - isa_region); + memory_region_add_subregion(get_system_memory(), addr, isa_region); isa_bus = isa_bus_new(NULL, get_system_memory(), isa_region, &error_abort); - isa_irqs = i8259_init(isa_bus, - /* qemu_allocate_irq(dino_set_isa_irq, s, 0)); */ - NULL); - isa_bus_irqs(isa_bus, isa_irqs); + isa_irqs = i8259_init(isa_bus, NULL); + isa_bus_register_input_irqs(isa_bus, isa_irqs); return isa_bus; } -static uint64_t cpu_hppa_to_phys(void *opaque, uint64_t addr) +/* + * Helper functions to emulate RTC clock and DebugOutputPort + */ +static time_t rtc_ref; + +static uint64_t io_cpu_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t val = 0; + + switch (addr) { + case 0: /* RTC clock */ + val = time(NULL); + val += rtc_ref; + break; + case 8: /* DebugOutputPort */ + return 0xe9; /* readback */ + } + return val; +} + +static void io_cpu_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + unsigned char ch; + Chardev *debugout; + + switch (addr) { + case 0: /* RTC clock */ + rtc_ref = val - time(NULL); + break; + case 8: /* DebugOutputPort */ + ch = val; + debugout = serial_hd(0); + if (debugout) { + qemu_chr_fe_write_all(debugout->be, &ch, 1); + } else { + fprintf(stderr, "%c", ch); + } + break; + } +} + +static const MemoryRegionOps hppa_io_helper_ops = { + .read = io_cpu_read, + .write = io_cpu_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +typedef uint64_t TranslateFn(void *opaque, uint64_t addr); + +static uint64_t linux_kernel_virt_to_phys(void *opaque, uint64_t addr) { addr &= (0x10000000 - 1); return addr; } +static uint64_t translate_pa10(void *dummy, uint64_t addr) +{ + return (uint32_t)addr; +} + +static uint64_t translate_pa20(void *dummy, uint64_t addr) +{ + return hppa_abs_to_phys_pa2_w0(addr); +} + static HPPACPU *cpu[HPPA_MAX_CPUS]; static uint64_t firmware_entry; @@ -121,12 +190,17 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device, fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); } -static FWCfgState *create_fw_cfg(MachineState *ms) +static FWCfgState *create_fw_cfg(MachineState *ms, PCIBus *pci_bus, + hwaddr addr) { FWCfgState *fw_cfg; uint64_t val; + const char qemu_version[] = QEMU_VERSION; + MachineClass *mc = MACHINE_GET_CLASS(ms); + int btlb_entries = HPPA_BTLB_ENTRIES(&cpu[0]->env); + int len; - fw_cfg = fw_cfg_init_mem(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4); + fw_cfg = fw_cfg_init_mem(addr, addr + 4); fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, ms->smp.cpus); fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, HPPA_MAX_CPUS); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, ms->ram_size); @@ -135,21 +209,39 @@ static FWCfgState *create_fw_cfg(MachineState *ms) fw_cfg_add_file(fw_cfg, "/etc/firmware-min-version", g_memdup(&val, sizeof(val)), sizeof(val)); - val = cpu_to_le64(HPPA_TLB_ENTRIES); + val = cpu_to_le64(HPPA_TLB_ENTRIES - btlb_entries); fw_cfg_add_file(fw_cfg, "/etc/cpu/tlb_entries", g_memdup(&val, sizeof(val)), sizeof(val)); - val = cpu_to_le64(HPPA_BTLB_ENTRIES); + val = cpu_to_le64(btlb_entries); fw_cfg_add_file(fw_cfg, "/etc/cpu/btlb_entries", g_memdup(&val, sizeof(val)), sizeof(val)); - val = cpu_to_le64(HPA_POWER_BUTTON); - fw_cfg_add_file(fw_cfg, "/etc/power-button-addr", + len = strlen(mc->name) + 1; + fw_cfg_add_file(fw_cfg, "/etc/hppa/machine", + g_memdup(mc->name, len), len); + + val = cpu_to_le64(soft_power_reg); + fw_cfg_add_file(fw_cfg, "/etc/hppa/power-button-addr", + g_memdup(&val, sizeof(val)), sizeof(val)); + + val = cpu_to_le64(CPU_HPA + 16); + fw_cfg_add_file(fw_cfg, "/etc/hppa/rtc-addr", + g_memdup(&val, sizeof(val)), sizeof(val)); + + val = cpu_to_le64(CPU_HPA + 24); + fw_cfg_add_file(fw_cfg, "/etc/hppa/DebugOutputPort", g_memdup(&val, sizeof(val)), sizeof(val)); fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ms->boot_config.order[0]); qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); + fw_cfg_add_file(fw_cfg, "/etc/qemu-version", + g_memdup(qemu_version, sizeof(qemu_version)), + sizeof(qemu_version)); + + fw_cfg_add_extra_pci_roots(pci_bus, fw_cfg); + return fw_cfg; } @@ -175,87 +267,91 @@ static DinoState *dino_init(MemoryRegion *addr_space) return DINO_PCI_HOST_BRIDGE(dev); } -static void machine_hppa_init(MachineState *machine) +/* + * Step 1: Create CPUs and Memory + */ +static TranslateFn *machine_HP_common_init_cpus(MachineState *machine) +{ + MemoryRegion *addr_space = get_system_memory(); + unsigned int smp_cpus = machine->smp.cpus; + TranslateFn *translate; + MemoryRegion *cpu_region; + uint64_t ram_max; + + /* Create CPUs. */ + for (unsigned int i = 0; i < smp_cpus; i++) { + cpu[i] = HPPA_CPU(cpu_create(machine->cpu_type)); + } + + /* + * For now, treat address layout as if PSW_W is clear. + * TODO: create a proper hppa64 board model and load elf64 firmware. + */ + if (hppa_is_pa20(&cpu[0]->env)) { + translate = translate_pa20; + ram_max = 0xf0000000; /* 3.75 GB (limited by 32-bit firmware) */ + } else { + translate = translate_pa10; + ram_max = 0xf0000000; /* 3.75 GB (32-bit CPU) */ + } + + soft_power_reg = translate(NULL, HPA_POWER_BUTTON); + + for (unsigned int i = 0; i < smp_cpus; i++) { + g_autofree char *name = g_strdup_printf("cpu%u-io-eir", i); + + cpu_region = g_new(MemoryRegion, 1); + memory_region_init_io(cpu_region, OBJECT(cpu[i]), &hppa_io_eir_ops, + cpu[i], name, 4); + memory_region_add_subregion(addr_space, + translate(NULL, CPU_HPA + i * 0x1000), + cpu_region); + } + + /* RTC and DebugOutputPort on CPU #0 */ + cpu_region = g_new(MemoryRegion, 1); + memory_region_init_io(cpu_region, OBJECT(cpu[0]), &hppa_io_helper_ops, + cpu[0], "cpu0-io-rtc", 2 * sizeof(uint64_t)); + memory_region_add_subregion(addr_space, translate(NULL, CPU_HPA + 16), + cpu_region); + + /* Main memory region. */ + if (machine->ram_size > ram_max) { + info_report("Max RAM size limited to %" PRIu64 " MB", ram_max / MiB); + machine->ram_size = ram_max; + } + memory_region_add_subregion_overlap(addr_space, 0, machine->ram, -1); + + return translate; +} + +/* + * Last creation step: Add SCSI discs, NICs, graphics & load firmware + */ +static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus, + TranslateFn *translate) { const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; - DeviceState *dev, *dino_dev, *lasi_dev; - PCIBus *pci_bus; - ISABus *isa_bus; + const char *firmware = machine->firmware; + MachineClass *mc = MACHINE_GET_CLASS(machine); + DeviceState *dev; + PCIDevice *pci_dev; char *firmware_filename; uint64_t firmware_low, firmware_high; long size; uint64_t kernel_entry = 0, kernel_low, kernel_high; MemoryRegion *addr_space = get_system_memory(); MemoryRegion *rom_region; - MemoryRegion *cpu_region; - long i; unsigned int smp_cpus = machine->smp.cpus; SysBusDevice *s; - /* Create CPUs. */ - for (i = 0; i < smp_cpus; i++) { - char *name = g_strdup_printf("cpu%ld-io-eir", i); - cpu[i] = HPPA_CPU(cpu_create(machine->cpu_type)); - - cpu_region = g_new(MemoryRegion, 1); - memory_region_init_io(cpu_region, OBJECT(cpu[i]), &hppa_io_eir_ops, - cpu[i], name, 4); - memory_region_add_subregion(addr_space, CPU_HPA + i * 0x1000, - cpu_region); - g_free(name); - } - - /* Main memory 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_dev = DEVICE(lasi_init()); - memory_region_add_subregion(addr_space, LASI_HPA, - sysbus_mmio_get_region( - SYS_BUS_DEVICE(lasi_dev), 0)); - - /* Init Dino (PCI host bus chip). */ - dino_dev = DEVICE(dino_init(addr_space)); - memory_region_add_subregion(addr_space, DINO_HPA, - sysbus_mmio_get_region( - SYS_BUS_DEVICE(dino_dev), 0)); - pci_bus = PCI_BUS(qdev_get_child_bus(dino_dev, "pci")); - assert(pci_bus); - - /* Create ISA bus. */ - isa_bus = hppa_isa_bus(); - assert(isa_bus); - - /* Realtime clock, used by firmware for PDC_TOD call. */ - mc146818_rtc_init(isa_bus, 2000, NULL); - - /* Serial ports: Lasi and Dino use a 7.272727 MHz clock. */ - serial_mm_init(addr_space, LASI_UART_HPA + 0x800, 0, - qdev_get_gpio_in(lasi_dev, LASI_IRQ_UART_HPA), 7272727 / 16, - serial_hd(0), DEVICE_BIG_ENDIAN); - - serial_mm_init(addr_space, DINO_UART_HPA + 0x800, 0, - qdev_get_gpio_in(dino_dev, DINO_IRQ_RS232INT), 7272727 / 16, - serial_hd(1), DEVICE_BIG_ENDIAN); - - /* Parallel port */ - parallel_mm_init(addr_space, LASI_LPT_HPA + 0x800, 0, - qdev_get_gpio_in(lasi_dev, LASI_IRQ_LAN_HPA), - parallel_hds[0]); - - /* fw_cfg configuration interface */ - create_fw_cfg(machine); - /* SCSI disk setup. */ - dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a")); - lsi53c8xx_handle_legacy_cmdline(dev); + if (drive_get_max_bus(IF_SCSI) >= 0) { + dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a")); + lsi53c8xx_handle_legacy_cmdline(dev); + } /* Graphics setup. */ if (machine->enable_graphics && vga_interface_type != VGA_NONE) { @@ -263,76 +359,106 @@ static void machine_hppa_init(MachineState *machine) dev = qdev_new("artist"); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); - sysbus_mmio_map(s, 0, LASI_GFX_HPA); - sysbus_mmio_map(s, 1, ARTIST_FB_ADDR); + sysbus_mmio_map(s, 0, translate(NULL, LASI_GFX_HPA)); + sysbus_mmio_map(s, 1, translate(NULL, ARTIST_FB_ADDR)); } /* Network setup. */ - if (enable_lasi_lan()) { - lasi_82596_init(addr_space, LASI_LAN_HPA, - qdev_get_gpio_in(lasi_dev, LASI_IRQ_LAN_HPA)); + if (lasi_dev) { + lasi_82596_init(addr_space, translate(NULL, LASI_LAN_HPA), + qdev_get_gpio_in(lasi_dev, LASI_IRQ_LAN_HPA), + enable_lasi_lan()); } - for (i = 0; i < nb_nics; i++) { - if (!enable_lasi_lan()) { - pci_nic_init_nofail(&nd_table[i], pci_bus, "tulip", NULL); - } + pci_init_nic_devices(pci_bus, mc->default_nic); + + /* BMC board: HP Powerbar SP2 Diva (with console only) */ + pci_dev = pci_new(-1, "pci-serial"); + if (!lasi_dev) { + /* bind default keyboard/serial to Diva card */ + qdev_prop_set_chr(DEVICE(pci_dev), "chardev", serial_hd(0)); + } + qdev_prop_set_uint8(DEVICE(pci_dev), "prog_if", 0); + pci_realize_and_unref(pci_dev, pci_bus, &error_fatal); + pci_config_set_vendor_id(pci_dev->config, PCI_VENDOR_ID_HP); + pci_config_set_device_id(pci_dev->config, 0x1048); + pci_set_word(&pci_dev->config[PCI_SUBSYSTEM_VENDOR_ID], PCI_VENDOR_ID_HP); + pci_set_word(&pci_dev->config[PCI_SUBSYSTEM_ID], 0x1227); /* Powerbar */ + + /* create a second serial PCI card when running Astro */ + if (serial_hd(1) && !lasi_dev) { + pci_dev = pci_new(-1, "pci-serial-4x"); + qdev_prop_set_chr(DEVICE(pci_dev), "chardev1", serial_hd(1)); + qdev_prop_set_chr(DEVICE(pci_dev), "chardev2", serial_hd(2)); + qdev_prop_set_chr(DEVICE(pci_dev), "chardev3", serial_hd(3)); + qdev_prop_set_chr(DEVICE(pci_dev), "chardev4", serial_hd(4)); + pci_realize_and_unref(pci_dev, pci_bus, &error_fatal); } - /* PS/2 Keyboard/Mouse */ - lasips2_init(addr_space, LASI_PS2KBD_HPA, - qdev_get_gpio_in(lasi_dev, LASI_IRQ_PS2KBD_HPA)); + /* create USB OHCI controller for USB keyboard & mouse on Astro machines */ + if (!lasi_dev && machine->enable_graphics && defaults_enabled()) { + USBBus *usb_bus; + + pci_create_simple(pci_bus, -1, "pci-ohci"); + usb_bus = USB_BUS(object_resolve_type_unambiguous(TYPE_USB_BUS, + &error_abort)); + usb_create_simple(usb_bus, "usb-kbd"); + usb_create_simple(usb_bus, "usb-mouse"); + } /* register power switch emulation */ qemu_register_powerdown_notifier(&hppa_system_powerdown_notifier); + /* fw_cfg configuration interface */ + create_fw_cfg(machine, pci_bus, translate(NULL, FW_CFG_IO_BASE)); + /* Load firmware. Given that this is not "real" firmware, but one explicitly written for the emulation, we might as - well load it directly from an ELF image. */ - firmware_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, - machine->firmware ?: "hppa-firmware.img"); - if (firmware_filename == NULL) { - error_report("no firmware provided"); - exit(1); - } + well load it directly from an ELF image. Load the 64-bit + firmware on 64-bit machines by default if not specified + on command line. */ + if (!qtest_enabled()) { + if (!firmware) { + firmware = lasi_dev ? "hppa-firmware.img" : "hppa-firmware64.img"; + } + firmware_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, firmware); + if (firmware_filename == NULL) { + error_report("no firmware provided"); + exit(1); + } - size = load_elf(firmware_filename, NULL, NULL, NULL, - &firmware_entry, &firmware_low, &firmware_high, NULL, - true, EM_PARISC, 0, 0); + size = load_elf(firmware_filename, NULL, translate, NULL, + &firmware_entry, &firmware_low, &firmware_high, NULL, + true, EM_PARISC, 0, 0); - /* Unfortunately, load_elf sign-extends reading elf32. */ - firmware_entry = (target_ureg)firmware_entry; - firmware_low = (target_ureg)firmware_low; - firmware_high = (target_ureg)firmware_high; - - if (size < 0) { - error_report("could not load firmware '%s'", firmware_filename); - exit(1); + if (size < 0) { + error_report("could not load firmware '%s'", firmware_filename); + exit(1); + } + 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 < translate(NULL, FIRMWARE_START) || + firmware_high >= translate(NULL, FIRMWARE_END)) { + error_report("Firmware overlaps with memory or IO space"); + exit(1); + } + g_free(firmware_filename); } - 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 < FIRMWARE_START || firmware_high >= FIRMWARE_END) { - error_report("Firmware overlaps with memory or IO space"); - exit(1); - } - g_free(firmware_filename); rom_region = g_new(MemoryRegion, 1); memory_region_init_ram(rom_region, NULL, "firmware", (FIRMWARE_END - FIRMWARE_START), &error_fatal); - memory_region_add_subregion(addr_space, FIRMWARE_START, rom_region); + memory_region_add_subregion(addr_space, + translate(NULL, FIRMWARE_START), rom_region); /* Load kernel */ if (kernel_filename) { - size = load_elf(kernel_filename, NULL, &cpu_hppa_to_phys, + size = load_elf(kernel_filename, NULL, linux_kernel_virt_to_phys, NULL, &kernel_entry, &kernel_low, &kernel_high, NULL, true, EM_PARISC, 0, 0); - /* Unfortunately, load_elf sign-extends reading elf32. */ - kernel_entry = (target_ureg) cpu_hppa_to_phys(NULL, kernel_entry); - kernel_low = (target_ureg)kernel_low; - kernel_high = (target_ureg)kernel_high; + kernel_entry = linux_kernel_virt_to_phys(NULL, kernel_entry); if (size < 0) { error_report("could not load kernel '%s'", kernel_filename); @@ -403,19 +529,138 @@ static void machine_hppa_init(MachineState *machine) cpu[0]->env.gr[19] = FW_CFG_IO_BASE; } -static void hppa_machine_reset(MachineState *ms) +/* + * Create HP B160L workstation + */ +static void machine_HP_B160L_init(MachineState *machine) +{ + DeviceState *dev, *dino_dev; + MemoryRegion *addr_space = get_system_memory(); + TranslateFn *translate; + ISABus *isa_bus; + PCIBus *pci_bus; + + /* Create CPUs and RAM. */ + translate = machine_HP_common_init_cpus(machine); + + if (hppa_is_pa20(&cpu[0]->env)) { + error_report("The HP B160L workstation requires a 32-bit " + "CPU. Use '-machine C3700' instead."); + exit(1); + } + + /* Init Lasi chip */ + lasi_dev = DEVICE(lasi_init()); + memory_region_add_subregion(addr_space, translate(NULL, LASI_HPA), + sysbus_mmio_get_region( + SYS_BUS_DEVICE(lasi_dev), 0)); + + /* Init Dino (PCI host bus chip). */ + dino_dev = DEVICE(dino_init(addr_space)); + memory_region_add_subregion(addr_space, translate(NULL, DINO_HPA), + sysbus_mmio_get_region( + SYS_BUS_DEVICE(dino_dev), 0)); + pci_bus = PCI_BUS(qdev_get_child_bus(dino_dev, "pci")); + assert(pci_bus); + + /* Create ISA bus, needed for PS/2 kbd/mouse port emulation */ + isa_bus = hppa_isa_bus(translate(NULL, IDE_HPA)); + assert(isa_bus); + + /* Serial ports: Lasi and Dino use a 7.272727 MHz clock. */ + serial_mm_init(addr_space, translate(NULL, LASI_UART_HPA + 0x800), 0, + qdev_get_gpio_in(lasi_dev, LASI_IRQ_UART_HPA), 7272727 / 16, + serial_hd(0), DEVICE_BIG_ENDIAN); + + serial_mm_init(addr_space, translate(NULL, DINO_UART_HPA + 0x800), 0, + qdev_get_gpio_in(dino_dev, DINO_IRQ_RS232INT), 7272727 / 16, + serial_hd(1), DEVICE_BIG_ENDIAN); + + /* Parallel port */ + parallel_mm_init(addr_space, translate(NULL, LASI_LPT_HPA + 0x800), 0, + qdev_get_gpio_in(lasi_dev, LASI_IRQ_LAN_HPA), + parallel_hds[0]); + + /* PS/2 Keyboard/Mouse */ + dev = qdev_new(TYPE_LASIPS2); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + qdev_get_gpio_in(lasi_dev, LASI_IRQ_PS2KBD_HPA)); + memory_region_add_subregion(addr_space, + translate(NULL, LASI_PS2KBD_HPA), + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), + 0)); + memory_region_add_subregion(addr_space, + translate(NULL, LASI_PS2KBD_HPA + 0x100), + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), + 1)); + + /* Add SCSI discs, NICs, graphics & load firmware */ + machine_HP_common_init_tail(machine, pci_bus, translate); +} + +static AstroState *astro_init(void) +{ + DeviceState *dev; + + dev = qdev_new(TYPE_ASTRO_CHIP); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + return ASTRO_CHIP(dev); +} + +/* + * Create HP C3700 workstation + */ +static void machine_HP_C3700_init(MachineState *machine) +{ + PCIBus *pci_bus; + AstroState *astro; + DeviceState *astro_dev; + MemoryRegion *addr_space = get_system_memory(); + TranslateFn *translate; + + /* Create CPUs and RAM. */ + translate = machine_HP_common_init_cpus(machine); + + if (!hppa_is_pa20(&cpu[0]->env)) { + error_report("The HP C3000 workstation requires a 64-bit CPU. " + "Use '-machine B160L' instead."); + exit(1); + } + + /* Init Astro and the Elroys (PCI host bus chips). */ + astro = astro_init(); + astro_dev = DEVICE(astro); + memory_region_add_subregion(addr_space, translate(NULL, ASTRO_HPA), + sysbus_mmio_get_region( + SYS_BUS_DEVICE(astro_dev), 0)); + pci_bus = PCI_BUS(qdev_get_child_bus(DEVICE(astro->elroy[0]), "pci")); + assert(pci_bus); + + /* Add SCSI discs, NICs, graphics & load firmware */ + machine_HP_common_init_tail(machine, pci_bus, translate); +} + +static void hppa_machine_reset(MachineState *ms, ShutdownCause reason) { unsigned int smp_cpus = ms->smp.cpus; int i; - qemu_devices_reset(); + qemu_devices_reset(reason); /* Start all CPUs at the firmware entry point. * Monarch CPU will initialize firmware, secondary CPUs - * will enter a small idle look and wait for rendevouz. */ + * will enter a small idle loop and wait for rendevouz. */ for (i = 0; i < smp_cpus; i++) { - cpu_set_pc(CPU(cpu[i]), firmware_entry); + CPUState *cs = CPU(cpu[i]); + + cpu_set_pc(cs, firmware_entry); + cpu[i]->env.psw = PSW_Q; cpu[i]->env.gr[5] = CPU_HPA + i * 0x1000; + + cs->exception_index = -1; + cs->halted = 0; } /* already initialized by machine_hppa_init()? */ @@ -441,14 +686,19 @@ static void hppa_nmi(NMIState *n, int cpu_index, Error **errp) } } -static void hppa_machine_init_class_init(ObjectClass *oc, void *data) +static void HP_B160L_machine_init_class_init(ObjectClass *oc, void *data) { + static const char * const valid_cpu_types[] = { + TYPE_HPPA_CPU, + NULL + }; MachineClass *mc = MACHINE_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); - mc->desc = "HPPA B160L machine"; + mc->desc = "HP B160L workstation"; mc->default_cpu_type = TYPE_HPPA_CPU; - mc->init = machine_hppa_init; + mc->valid_cpu_types = valid_cpu_types; + mc->init = machine_HP_B160L_init; mc->reset = hppa_machine_reset; mc->block_default_type = IF_SCSI; mc->max_cpus = HPPA_MAX_CPUS; @@ -457,14 +707,51 @@ static void hppa_machine_init_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 512 * MiB; mc->default_boot_order = "cd"; mc->default_ram_id = "ram"; + mc->default_nic = "tulip"; nc->nmi_monitor_handler = hppa_nmi; } -static const TypeInfo hppa_machine_init_typeinfo = { - .name = MACHINE_TYPE_NAME("hppa"), +static const TypeInfo HP_B160L_machine_init_typeinfo = { + .name = MACHINE_TYPE_NAME("B160L"), .parent = TYPE_MACHINE, - .class_init = hppa_machine_init_class_init, + .class_init = HP_B160L_machine_init_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NMI }, + { } + }, +}; + +static void HP_C3700_machine_init_class_init(ObjectClass *oc, void *data) +{ + static const char * const valid_cpu_types[] = { + TYPE_HPPA64_CPU, + NULL + }; + MachineClass *mc = MACHINE_CLASS(oc); + NMIClass *nc = NMI_CLASS(oc); + + mc->desc = "HP C3700 workstation"; + mc->default_cpu_type = TYPE_HPPA64_CPU; + mc->valid_cpu_types = valid_cpu_types; + mc->init = machine_HP_C3700_init; + mc->reset = hppa_machine_reset; + mc->block_default_type = IF_SCSI; + mc->max_cpus = HPPA_MAX_CPUS; + mc->default_cpus = 1; + mc->is_default = false; + mc->default_ram_size = 1024 * MiB; + mc->default_boot_order = "cd"; + mc->default_ram_id = "ram"; + mc->default_nic = "tulip"; + + nc->nmi_monitor_handler = hppa_nmi; +} + +static const TypeInfo HP_C3700_machine_init_typeinfo = { + .name = MACHINE_TYPE_NAME("C3700"), + .parent = TYPE_MACHINE, + .class_init = HP_C3700_machine_init_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_NMI }, { } @@ -473,7 +760,8 @@ static const TypeInfo hppa_machine_init_typeinfo = { static void hppa_machine_init_register_types(void) { - type_register_static(&hppa_machine_init_typeinfo); + type_register_static(&HP_B160L_machine_init_typeinfo); + type_register_static(&HP_C3700_machine_init_typeinfo); } type_init(hppa_machine_init_register_types) diff --git a/hw/hyperv/Kconfig b/hw/hyperv/Kconfig index fcf65903bd..41dd827c84 100644 --- a/hw/hyperv/Kconfig +++ b/hw/hyperv/Kconfig @@ -16,3 +16,13 @@ config SYNDBG bool default y depends on VMBUS + +config HV_BALLOON_SUPPORTED + bool + +config HV_BALLOON + bool + default y + depends on VMBUS + depends on HV_BALLOON_POSSIBLE + depends on HV_BALLOON_SUPPORTED diff --git a/hw/hyperv/hv-balloon-internal.h b/hw/hyperv/hv-balloon-internal.h new file mode 100644 index 0000000000..ee53a28a26 --- /dev/null +++ b/hw/hyperv/hv-balloon-internal.h @@ -0,0 +1,32 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * 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_HYPERV_HV_BALLOON_INTERNAL_H +#define HW_HYPERV_HV_BALLOON_INTERNAL_H + + +#define HV_BALLOON_PFN_SHIFT 12 +#define HV_BALLOON_PAGE_SIZE (1 << HV_BALLOON_PFN_SHIFT) + +#define SUM_OVERFLOW_U64(in1, in2) ((in1) > UINT64_MAX - (in2)) +#define SUM_SATURATE_U64(in1, in2) \ + ({ \ + uint64_t _in1 = (in1), _in2 = (in2); \ + uint64_t _result; \ + \ + if (!SUM_OVERFLOW_U64(_in1, _in2)) { \ + _result = _in1 + _in2; \ + } else { \ + _result = UINT64_MAX; \ + } \ + \ + _result; \ + }) + +#endif diff --git a/hw/hyperv/hv-balloon-our_range_memslots.c b/hw/hyperv/hv-balloon-our_range_memslots.c new file mode 100644 index 0000000000..1505a395cf --- /dev/null +++ b/hw/hyperv/hv-balloon-our_range_memslots.c @@ -0,0 +1,202 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * 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 "hv-balloon-internal.h" +#include "hv-balloon-our_range_memslots.h" +#include "trace.h" + +/* OurRange */ +static void our_range_init(OurRange *our_range, uint64_t start, uint64_t count) +{ + assert(count <= UINT64_MAX - start); + our_range->range.start = start; + our_range->range.count = count; + + hvb_page_range_tree_init(&our_range->removed_guest); + hvb_page_range_tree_init(&our_range->removed_both); + + /* mark the whole range as unused but for potential use */ + our_range->added = 0; + our_range->unusable_tail = 0; +} + +static void our_range_destroy(OurRange *our_range) +{ + hvb_page_range_tree_destroy(&our_range->removed_guest); + hvb_page_range_tree_destroy(&our_range->removed_both); +} + +void hvb_our_range_clear_removed_trees(OurRange *our_range) +{ + hvb_page_range_tree_destroy(&our_range->removed_guest); + hvb_page_range_tree_destroy(&our_range->removed_both); + hvb_page_range_tree_init(&our_range->removed_guest); + hvb_page_range_tree_init(&our_range->removed_both); +} + +void hvb_our_range_mark_added(OurRange *our_range, uint64_t additional_size) +{ + assert(additional_size <= UINT64_MAX - our_range->added); + + our_range->added += additional_size; + + assert(our_range->added <= UINT64_MAX - our_range->unusable_tail); + assert(our_range->added + our_range->unusable_tail <= + our_range->range.count); +} + +/* OurRangeMemslots */ +static void our_range_memslots_init_slots(OurRangeMemslots *our_range, + MemoryRegion *backing_mr, + Object *memslot_owner) +{ + OurRangeMemslotsSlots *memslots = &our_range->slots; + unsigned int idx; + uint64_t memslot_offset; + + assert(memslots->count > 0); + memslots->slots = g_new0(MemoryRegion, memslots->count); + + /* Initialize our memslots, but don't map them yet. */ + assert(memslots->size_each > 0); + for (idx = 0, memslot_offset = 0; idx < memslots->count; + idx++, memslot_offset += memslots->size_each) { + uint64_t memslot_size; + g_autofree char *name = NULL; + + /* The size of the last memslot might be smaller. */ + if (idx == memslots->count - 1) { + uint64_t region_size; + + assert(our_range->mr); + region_size = memory_region_size(our_range->mr); + memslot_size = region_size - memslot_offset; + } else { + memslot_size = memslots->size_each; + } + + name = g_strdup_printf("memslot-%u", idx); + memory_region_init_alias(&memslots->slots[idx], memslot_owner, name, + backing_mr, memslot_offset, memslot_size); + /* + * We want to be able to atomically and efficiently activate/deactivate + * individual memslots without affecting adjacent memslots in memory + * notifiers. + */ + memory_region_set_unmergeable(&memslots->slots[idx], true); + } + + memslots->mapped_count = 0; +} + +OurRangeMemslots *hvb_our_range_memslots_new(uint64_t addr, + MemoryRegion *parent_mr, + MemoryRegion *backing_mr, + Object *memslot_owner, + unsigned int memslot_count, + uint64_t memslot_size) +{ + OurRangeMemslots *our_range; + + our_range = g_malloc(sizeof(*our_range)); + our_range_init(&our_range->range, + addr / HV_BALLOON_PAGE_SIZE, + memory_region_size(parent_mr) / HV_BALLOON_PAGE_SIZE); + our_range->slots.size_each = memslot_size; + our_range->slots.count = memslot_count; + our_range->mr = parent_mr; + our_range_memslots_init_slots(our_range, backing_mr, memslot_owner); + + return our_range; +} + +static void our_range_memslots_free_memslots(OurRangeMemslots *our_range) +{ + OurRangeMemslotsSlots *memslots = &our_range->slots; + unsigned int idx; + uint64_t offset; + + memory_region_transaction_begin(); + for (idx = 0, offset = 0; idx < memslots->mapped_count; + idx++, offset += memslots->size_each) { + trace_hv_balloon_unmap_slot(idx, memslots->count, offset); + assert(memory_region_is_mapped(&memslots->slots[idx])); + memory_region_del_subregion(our_range->mr, &memslots->slots[idx]); + } + memory_region_transaction_commit(); + + for (idx = 0; idx < memslots->count; idx++) { + object_unparent(OBJECT(&memslots->slots[idx])); + } + + g_clear_pointer(&our_range->slots.slots, g_free); +} + +void hvb_our_range_memslots_free(OurRangeMemslots *our_range) +{ + OurRangeMemslotsSlots *memslots = &our_range->slots; + MemoryRegion *hostmem_mr; + RAMBlock *rb; + + assert(our_range->slots.count > 0); + assert(our_range->slots.slots); + + hostmem_mr = memslots->slots[0].alias; + rb = hostmem_mr->ram_block; + ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb)); + + our_range_memslots_free_memslots(our_range); + our_range_destroy(&our_range->range); + g_free(our_range); +} + +void hvb_our_range_memslots_ensure_mapped_additional(OurRangeMemslots *our_range, + uint64_t additional_map_size) +{ + OurRangeMemslotsSlots *memslots = &our_range->slots; + uint64_t total_map_size; + unsigned int idx; + uint64_t offset; + + total_map_size = (our_range->range.added + additional_map_size) * + HV_BALLOON_PAGE_SIZE; + idx = memslots->mapped_count; + assert(memslots->size_each > 0); + offset = idx * memslots->size_each; + + /* + * Activate all memslots covered by the newly added region in a single + * transaction. + */ + memory_region_transaction_begin(); + for ( ; idx < memslots->count; + idx++, offset += memslots->size_each) { + /* + * If this memslot starts beyond or at the end of the range to map so + * does every next one. + */ + if (offset >= total_map_size) { + break; + } + + /* + * Instead of enabling/disabling memslot, we add/remove them. This + * should make address space updates faster, because we don't have to + * loop over many disabled subregions. + */ + trace_hv_balloon_map_slot(idx, memslots->count, offset); + assert(!memory_region_is_mapped(&memslots->slots[idx])); + memory_region_add_subregion(our_range->mr, offset, + &memslots->slots[idx]); + + memslots->mapped_count++; + } + memory_region_transaction_commit(); +} diff --git a/hw/hyperv/hv-balloon-our_range_memslots.h b/hw/hyperv/hv-balloon-our_range_memslots.h new file mode 100644 index 0000000000..df3b686bc7 --- /dev/null +++ b/hw/hyperv/hv-balloon-our_range_memslots.h @@ -0,0 +1,109 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * 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_HYPERV_HV_BALLOON_OUR_RANGE_MEMSLOTS_H +#define HW_HYPERV_HV_BALLOON_OUR_RANGE_MEMSLOTS_H + + +#include "exec/memory.h" +#include "qom/object.h" +#include "hv-balloon-page_range_tree.h" + +/* OurRange */ +#define OUR_RANGE(ptr) ((OurRange *)(ptr)) + +/* "our range" means the memory range owned by this driver (for hot-adding) */ +typedef struct OurRange { + PageRange range; + + /* How many pages were hot-added to the guest */ + uint64_t added; + + /* Pages at the end not currently usable */ + uint64_t unusable_tail; + + /* Memory removed from the guest */ + PageRangeTree removed_guest, removed_both; +} OurRange; + +static inline uint64_t our_range_get_remaining_start(OurRange *our_range) +{ + return our_range->range.start + our_range->added; +} + +static inline uint64_t our_range_get_remaining_size(OurRange *our_range) +{ + return our_range->range.count - our_range->added - our_range->unusable_tail; +} + +void hvb_our_range_mark_added(OurRange *our_range, uint64_t additional_size); + +static inline void our_range_mark_remaining_unusable(OurRange *our_range) +{ + our_range->unusable_tail = our_range->range.count - our_range->added; +} + +static inline PageRangeTree our_range_get_removed_tree(OurRange *our_range, + bool both) +{ + if (both) { + return our_range->removed_both; + } else { + return our_range->removed_guest; + } +} + +static inline bool our_range_is_removed_tree_empty(OurRange *our_range, + bool both) +{ + if (both) { + return page_range_tree_is_empty(our_range->removed_both); + } else { + return page_range_tree_is_empty(our_range->removed_guest); + } +} + +void hvb_our_range_clear_removed_trees(OurRange *our_range); + +/* OurRangeMemslots */ +typedef struct OurRangeMemslotsSlots { + /* Nominal size of each memslot (the last one might be smaller) */ + uint64_t size_each; + + /* Slots array and its element count */ + MemoryRegion *slots; + unsigned int count; + + /* How many slots are currently mapped */ + unsigned int mapped_count; +} OurRangeMemslotsSlots; + +typedef struct OurRangeMemslots { + OurRange range; + + /* Memslots covering our range */ + OurRangeMemslotsSlots slots; + + MemoryRegion *mr; +} OurRangeMemslots; + +OurRangeMemslots *hvb_our_range_memslots_new(uint64_t addr, + MemoryRegion *parent_mr, + MemoryRegion *backing_mr, + Object *memslot_owner, + unsigned int memslot_count, + uint64_t memslot_size); +void hvb_our_range_memslots_free(OurRangeMemslots *our_range); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(OurRangeMemslots, hvb_our_range_memslots_free) + +void hvb_our_range_memslots_ensure_mapped_additional(OurRangeMemslots *our_range, + uint64_t additional_map_size); + +#endif diff --git a/hw/hyperv/hv-balloon-page_range_tree.c b/hw/hyperv/hv-balloon-page_range_tree.c new file mode 100644 index 0000000000..dfb14852f4 --- /dev/null +++ b/hw/hyperv/hv-balloon-page_range_tree.c @@ -0,0 +1,229 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * 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 "hv-balloon-internal.h" +#include "hv-balloon-page_range_tree.h" + +/* + * temporarily avoid warnings about enhanced GTree API usage requiring a + * too recent Glib version until GLIB_VERSION_MAX_ALLOWED finally reaches + * the Glib version with this API + */ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +/* PageRangeTree */ +static gint page_range_tree_key_compare(gconstpointer leftp, + gconstpointer rightp, + gpointer user_data) +{ + const uint64_t *left = leftp, *right = rightp; + + if (*left < *right) { + return -1; + } else if (*left > *right) { + return 1; + } else { /* *left == *right */ + return 0; + } +} + +static GTreeNode *page_range_tree_insert_new(PageRangeTree tree, + uint64_t start, uint64_t count) +{ + uint64_t *key = g_malloc(sizeof(*key)); + PageRange *range = g_malloc(sizeof(*range)); + + assert(count > 0); + + *key = range->start = start; + range->count = count; + + return g_tree_insert_node(tree.t, key, range); +} + +void hvb_page_range_tree_insert(PageRangeTree tree, + uint64_t start, uint64_t count, + uint64_t *dupcount) +{ + GTreeNode *node; + bool joinable; + uint64_t intersection; + PageRange *range; + + assert(!SUM_OVERFLOW_U64(start, count)); + if (count == 0) { + return; + } + + node = g_tree_upper_bound(tree.t, &start); + if (node) { + node = g_tree_node_previous(node); + } else { + node = g_tree_node_last(tree.t); + } + + if (node) { + range = g_tree_node_value(node); + assert(range); + intersection = page_range_intersection_size(range, start, count); + joinable = page_range_joinable_right(range, start, count); + } + + if (!node || + (!intersection && !joinable)) { + /* + * !node case: the tree is empty or the very first node in the tree + * already has a higher key (the start of its range). + * the other case: there is a gap in the tree between the new range + * and the previous one. + * anyway, let's just insert the new range into the tree. + */ + node = page_range_tree_insert_new(tree, start, count); + assert(node); + range = g_tree_node_value(node); + assert(range); + } else { + /* + * the previous range in the tree either partially covers the new + * range or ends just at its beginning - extend it + */ + if (dupcount) { + *dupcount += intersection; + } + + count += start - range->start; + range->count = MAX(range->count, count); + } + + /* check next nodes for possible merging */ + for (node = g_tree_node_next(node); node; ) { + PageRange *rangecur; + + rangecur = g_tree_node_value(node); + assert(rangecur); + + intersection = page_range_intersection_size(rangecur, + range->start, range->count); + joinable = page_range_joinable_left(rangecur, + range->start, range->count); + if (!intersection && !joinable) { + /* the current node is disjoint */ + break; + } + + if (dupcount) { + *dupcount += intersection; + } + + count = rangecur->count + (rangecur->start - range->start); + range->count = MAX(range->count, count); + + /* the current node was merged in, remove it */ + start = rangecur->start; + node = g_tree_node_next(node); + /* no hinted removal in GTree... */ + g_tree_remove(tree.t, &start); + } +} + +bool hvb_page_range_tree_pop(PageRangeTree tree, PageRange *out, + uint64_t maxcount) +{ + GTreeNode *node; + PageRange *range; + + node = g_tree_node_last(tree.t); + if (!node) { + return false; + } + + range = g_tree_node_value(node); + assert(range); + + out->start = range->start; + + /* can't modify range->start as it is the node key */ + if (range->count > maxcount) { + out->start += range->count - maxcount; + out->count = maxcount; + range->count -= maxcount; + } else { + out->count = range->count; + /* no hinted removal in GTree... */ + g_tree_remove(tree.t, &out->start); + } + + return true; +} + +bool hvb_page_range_tree_intree_any(PageRangeTree tree, + uint64_t start, uint64_t count) +{ + GTreeNode *node; + + if (count == 0) { + return false; + } + + /* find the first node that can possibly intersect our range */ + node = g_tree_upper_bound(tree.t, &start); + if (node) { + /* + * a NULL node below means that the very first node in the tree + * already has a higher key (the start of its range). + */ + node = g_tree_node_previous(node); + } else { + /* a NULL node below means that the tree is empty */ + node = g_tree_node_last(tree.t); + } + /* node range start <= range start */ + + if (!node) { + /* node range start > range start */ + node = g_tree_node_first(tree.t); + } + + for ( ; node; node = g_tree_node_next(node)) { + PageRange *range = g_tree_node_value(node); + + assert(range); + /* + * if this node starts beyond or at the end of our range so does + * every next one + */ + if (range->start >= start + count) { + break; + } + + if (page_range_intersection_size(range, start, count) > 0) { + return true; + } + } + + return false; +} + +void hvb_page_range_tree_init(PageRangeTree *tree) +{ + tree->t = g_tree_new_full(page_range_tree_key_compare, NULL, + g_free, g_free); +} + +void hvb_page_range_tree_destroy(PageRangeTree *tree) +{ + /* g_tree_destroy() is not NULL-safe */ + if (!tree->t) { + return; + } + + g_tree_destroy(tree->t); + tree->t = NULL; +} diff --git a/hw/hyperv/hv-balloon-page_range_tree.h b/hw/hyperv/hv-balloon-page_range_tree.h new file mode 100644 index 0000000000..333772b86d --- /dev/null +++ b/hw/hyperv/hv-balloon-page_range_tree.h @@ -0,0 +1,117 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * 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_HYPERV_HV_BALLOON_PAGE_RANGE_TREE_H +#define HW_HYPERV_HV_BALLOON_PAGE_RANGE_TREE_H + + +/* PageRange */ +typedef struct PageRange { + uint64_t start; + uint64_t count; +} PageRange; + +/* return just the part of range before (start) */ +static inline void page_range_part_before(const PageRange *range, + uint64_t start, PageRange *out) +{ + uint64_t endr = range->start + range->count; + uint64_t end = MIN(endr, start); + + out->start = range->start; + if (end > out->start) { + out->count = end - out->start; + } else { + out->count = 0; + } +} + +/* return just the part of range after (start, count) */ +static inline void page_range_part_after(const PageRange *range, + uint64_t start, uint64_t count, + PageRange *out) +{ + uint64_t end = range->start + range->count; + uint64_t ends = start + count; + + out->start = MAX(range->start, ends); + if (end > out->start) { + out->count = end - out->start; + } else { + out->count = 0; + } +} + +static inline void page_range_intersect(const PageRange *range, + uint64_t start, uint64_t count, + PageRange *out) +{ + uint64_t end1 = range->start + range->count; + uint64_t end2 = start + count; + uint64_t end = MIN(end1, end2); + + out->start = MAX(range->start, start); + out->count = out->start < end ? end - out->start : 0; +} + +static inline uint64_t page_range_intersection_size(const PageRange *range, + uint64_t start, uint64_t count) +{ + PageRange trange; + + page_range_intersect(range, start, count, &trange); + return trange.count; +} + +static inline bool page_range_joinable_left(const PageRange *range, + uint64_t start, uint64_t count) +{ + return start + count == range->start; +} + +static inline bool page_range_joinable_right(const PageRange *range, + uint64_t start, uint64_t count) +{ + return range->start + range->count == start; +} + +static inline bool page_range_joinable(const PageRange *range, + uint64_t start, uint64_t count) +{ + return page_range_joinable_left(range, start, count) || + page_range_joinable_right(range, start, count); +} + +/* PageRangeTree */ +/* type safety */ +typedef struct PageRangeTree { + GTree *t; +} PageRangeTree; + +static inline bool page_range_tree_is_empty(PageRangeTree tree) +{ + guint nnodes = g_tree_nnodes(tree.t); + + return nnodes == 0; +} + +void hvb_page_range_tree_init(PageRangeTree *tree); +void hvb_page_range_tree_destroy(PageRangeTree *tree); + +bool hvb_page_range_tree_intree_any(PageRangeTree tree, + uint64_t start, uint64_t count); + +bool hvb_page_range_tree_pop(PageRangeTree tree, PageRange *out, + uint64_t maxcount); + +void hvb_page_range_tree_insert(PageRangeTree tree, + uint64_t start, uint64_t count, + uint64_t *dupcount); + +#endif diff --git a/hw/hyperv/hv-balloon-stub.c b/hw/hyperv/hv-balloon-stub.c new file mode 100644 index 0000000000..a47412d4a8 --- /dev/null +++ b/hw/hyperv/hv-balloon-stub.c @@ -0,0 +1,19 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2023 Oracle and/or its affiliates. + * + * 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/error.h" +#include "qapi/qapi-commands-machine.h" +#include "qapi/qapi-types-machine.h" + +HvBalloonInfo *qmp_query_hv_balloon_status_report(Error **errp) +{ + error_setg(errp, "hv-balloon device not enabled in this build"); + return NULL; +} diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c new file mode 100644 index 0000000000..3a9ef07691 --- /dev/null +++ b/hw/hyperv/hv-balloon.c @@ -0,0 +1,1773 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * 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 "hv-balloon-internal.h" + +#include "exec/address-spaces.h" +#include "exec/cpu-common.h" +#include "exec/ramblock.h" +#include "hw/boards.h" +#include "hw/hyperv/dynmem-proto.h" +#include "hw/hyperv/hv-balloon.h" +#include "hw/hyperv/vmbus.h" +#include "hw/mem/memory-device.h" +#include "hw/mem/pc-dimm.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "monitor/qdev.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" +#include "qapi/qapi-events-machine.h" +#include "qapi/qapi-types-machine.h" +#include "qapi/qmp/qdict.h" +#include "qapi/visitor.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "qemu/timer.h" +#include "sysemu/balloon.h" +#include "sysemu/hostmem.h" +#include "sysemu/reset.h" +#include "hv-balloon-our_range_memslots.h" +#include "hv-balloon-page_range_tree.h" +#include "trace.h" + +#define HV_BALLOON_ADDR_PROP "addr" +#define HV_BALLOON_MEMDEV_PROP "memdev" +#define HV_BALLOON_GUID "525074DC-8985-46e2-8057-A307DC18A502" + +/* + * Some Windows versions (at least Server 2019) will crash with various + * error codes when receiving DM protocol requests (at least + * DM_MEM_HOT_ADD_REQUEST) immediately after boot. + * + * It looks like Hyper-V from Server 2016 uses a 50-second after-boot + * delay, probably to workaround this issue, so we'll use this value, too. + */ +#define HV_BALLOON_POST_INIT_WAIT (50 * 1000) + +#define HV_BALLOON_HA_CHUNK_SIZE (2 * GiB) +#define HV_BALLOON_HA_CHUNK_PAGES (HV_BALLOON_HA_CHUNK_SIZE / HV_BALLOON_PAGE_SIZE) + +#define HV_BALLOON_HA_MEMSLOT_SIZE_ALIGN (128 * MiB) + +#define HV_BALLOON_HR_CHUNK_PAGES 585728 +/* + * ^ that's the maximum number of pages + * that Windows returns in one hot remove response + * + * If the number requested is too high Windows will no longer honor + * these requests + */ + +struct HvBalloonClass { + VMBusDeviceClass parent_class; +} HvBalloonClass; + +typedef enum State { + /* not a real state */ + S_NO_CHANGE = 0, + + S_WAIT_RESET, + S_POST_RESET_CLOSED, + + /* init flow */ + S_VERSION, + S_CAPS, + S_POST_INIT_WAIT, + + S_IDLE, + + /* balloon op flow */ + S_BALLOON_POSTING, + S_BALLOON_RB_WAIT, + S_BALLOON_REPLY_WAIT, + + /* unballoon + hot add ops flow */ + S_UNBALLOON_POSTING, + S_UNBALLOON_RB_WAIT, + S_UNBALLOON_REPLY_WAIT, + S_HOT_ADD_SETUP, + S_HOT_ADD_RB_WAIT, + S_HOT_ADD_POSTING, + S_HOT_ADD_REPLY_WAIT, +} State; + +typedef struct StateDesc { + State state; + const char *desc; +} StateDesc; + +typedef struct HvBalloon { + VMBusDevice parent; + State state; + + union dm_version version; + union dm_caps caps; + + QEMUTimer post_init_timer; + + unsigned int trans_id; + + struct { + bool enabled; + bool received; + uint64_t committed; + uint64_t available; + } status_report; + + /* Guest target size */ + uint64_t target; + bool target_changed; + + /* Current (un)balloon / hot-add operation parameters */ + union { + uint64_t balloon_diff; + + struct { + uint64_t unballoon_diff; + uint64_t hot_add_diff; + }; + + struct { + PageRange hot_add_range; + uint64_t ha_current_count; + }; + }; + + OurRangeMemslots *our_range; + + /* Count of memslots covering our memory */ + unsigned int memslot_count; + + /* Nominal size of each memslot (the last one might be smaller) */ + uint64_t memslot_size; + + /* Non-ours removed memory */ + PageRangeTree removed_guest, removed_both; + + /* Grand totals of removed memory (both ours and non-ours) */ + uint64_t removed_guest_ctr, removed_both_ctr; + + /* MEMORY_DEVICE props */ + uint64_t addr; + HostMemoryBackend *hostmem; + MemoryRegion *mr; +} HvBalloon; + +OBJECT_DEFINE_TYPE_WITH_INTERFACES(HvBalloon, hv_balloon, HV_BALLOON, VMBUS_DEVICE, \ + { TYPE_MEMORY_DEVICE }, { }) + +#define HV_BALLOON_SET_STATE(hvb, news) \ + do { \ + assert(news != S_NO_CHANGE); \ + hv_balloon_state_set(hvb, news, # news); \ + } while (0) + +#define HV_BALLOON_STATE_DESC_SET(stdesc, news) \ + _hv_balloon_state_desc_set(stdesc, news, # news) + +#define HV_BALLOON_STATE_DESC_INIT \ + { \ + .state = S_NO_CHANGE, \ + } + +typedef struct HvBalloonReq { + VMBusChanReq vmreq; +} HvBalloonReq; + +/* total our memory includes parts currently removed from the guest */ +static uint64_t hv_balloon_total_our_ram(HvBalloon *balloon) +{ + if (!balloon->our_range) { + return 0; + } + + return balloon->our_range->range.added; +} + +/* TODO: unify the code below with virtio-balloon and cache the value */ +static int build_dimm_list(Object *obj, void *opaque) +{ + GSList **list = opaque; + + if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { + DeviceState *dev = DEVICE(obj); + if (dev->realized) { /* only realized DIMMs matter */ + *list = g_slist_prepend(*list, dev); + } + } + + object_child_foreach(obj, build_dimm_list, opaque); + return 0; +} + +static ram_addr_t get_current_ram_size(void) +{ + GSList *list = NULL, *item; + ram_addr_t size = current_machine->ram_size; + + build_dimm_list(qdev_get_machine(), &list); + for (item = list; item; item = g_slist_next(item)) { + Object *obj = OBJECT(item->data); + if (!strcmp(object_get_typename(obj), TYPE_PC_DIMM)) + size += object_property_get_int(obj, PC_DIMM_SIZE_PROP, + &error_abort); + } + g_slist_free(list); + + return size; +} + +/* total RAM includes memory currently removed from the guest */ +static uint64_t hv_balloon_total_ram(HvBalloon *balloon) +{ + ram_addr_t ram_size = get_current_ram_size(); + uint64_t ram_size_pages = ram_size >> HV_BALLOON_PFN_SHIFT; + uint64_t our_ram_size_pages = hv_balloon_total_our_ram(balloon); + + assert(ram_size_pages > 0); + + return SUM_SATURATE_U64(ram_size_pages, our_ram_size_pages); +} + +/* + * calculating the total RAM size is a slow operation, + * avoid it as much as possible + */ +static uint64_t hv_balloon_total_removed_rs(HvBalloon *balloon, + uint64_t ram_size_pages) +{ + uint64_t total_removed; + + total_removed = SUM_SATURATE_U64(balloon->removed_guest_ctr, + balloon->removed_both_ctr); + + /* possible if guest returns pages outside actual RAM */ + if (total_removed > ram_size_pages) { + total_removed = ram_size_pages; + } + + return total_removed; +} + +/* Returns whether the state has actually changed */ +static bool hv_balloon_state_set(HvBalloon *balloon, + State newst, const char *newststr) +{ + if (newst == S_NO_CHANGE || balloon->state == newst) { + return false; + } + + balloon->state = newst; + trace_hv_balloon_state_change(newststr); + return true; +} + +static void _hv_balloon_state_desc_set(StateDesc *stdesc, + State newst, const char *newststr) +{ + /* state setting is only permitted on a freshly init desc */ + assert(stdesc->state == S_NO_CHANGE); + + assert(newst != S_NO_CHANGE); + + stdesc->state = newst; + stdesc->desc = newststr; +} + +static VMBusChannel *hv_balloon_get_channel_maybe(HvBalloon *balloon) +{ + return vmbus_device_channel(&balloon->parent, 0); +} + +static VMBusChannel *hv_balloon_get_channel(HvBalloon *balloon) +{ + VMBusChannel *chan; + + chan = hv_balloon_get_channel_maybe(balloon); + assert(chan != NULL); + return chan; +} + +static ssize_t hv_balloon_send_packet(VMBusChannel *chan, + struct dm_message *msg) +{ + int ret; + + ret = vmbus_channel_reserve(chan, 0, msg->hdr.size); + if (ret < 0) { + return ret; + } + + return vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, + NULL, 0, msg, msg->hdr.size, false, + msg->hdr.trans_id); +} + +static bool hv_balloon_unballoon_get_source(HvBalloon *balloon, + PageRangeTree *dtree, + uint64_t **dctr, + bool *is_our_range) +{ + OurRange *our_range = OUR_RANGE(balloon->our_range); + + /* Try the boot memory first */ + if (g_tree_nnodes(balloon->removed_guest.t) > 0) { + *dtree = balloon->removed_guest; + *dctr = &balloon->removed_guest_ctr; + *is_our_range = false; + } else if (g_tree_nnodes(balloon->removed_both.t) > 0) { + *dtree = balloon->removed_both; + *dctr = &balloon->removed_both_ctr; + *is_our_range = false; + } else if (!our_range) { + return false; + } else if (!our_range_is_removed_tree_empty(our_range, false)) { + *dtree = our_range_get_removed_tree(our_range, false); + *dctr = &balloon->removed_guest_ctr; + *is_our_range = true; + } else if (!our_range_is_removed_tree_empty(our_range, true)) { + *dtree = our_range_get_removed_tree(our_range, true); + *dctr = &balloon->removed_both_ctr; + *is_our_range = true; + } else { + return false; + } + + return true; +} + +static void hv_balloon_unballoon_rb_wait(HvBalloon *balloon, StateDesc *stdesc) +{ + VMBusChannel *chan = hv_balloon_get_channel(balloon); + struct dm_unballoon_request *ur; + size_t ur_size = sizeof(*ur) + sizeof(ur->range_array[0]); + + assert(balloon->state == S_UNBALLOON_RB_WAIT); + + if (vmbus_channel_reserve(chan, 0, ur_size) < 0) { + return; + } + + HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_POSTING); +} + +static void hv_balloon_unballoon_posting(HvBalloon *balloon, StateDesc *stdesc) +{ + VMBusChannel *chan = hv_balloon_get_channel(balloon); + PageRangeTree dtree; + uint64_t *dctr; + bool our_range; + g_autofree struct dm_unballoon_request *ur = NULL; + size_t ur_size = sizeof(*ur) + sizeof(ur->range_array[0]); + PageRange range; + bool bret; + ssize_t ret; + + assert(balloon->state == S_UNBALLOON_POSTING); + assert(balloon->unballoon_diff > 0); + + if (!hv_balloon_unballoon_get_source(balloon, &dtree, &dctr, &our_range)) { + error_report("trying to unballoon but nothing seems to be ballooned"); + /* + * there is little we can do as we might have already + * sent the guest a partial request we can't cancel + */ + return; + } + + assert(balloon->our_range || !our_range); + assert(dtree.t); + assert(dctr); + + ur = g_malloc0(ur_size); + ur->hdr.type = DM_UNBALLOON_REQUEST; + ur->hdr.size = ur_size; + ur->hdr.trans_id = balloon->trans_id; + + bret = hvb_page_range_tree_pop(dtree, &range, MIN(balloon->unballoon_diff, + HV_BALLOON_HA_CHUNK_PAGES)); + assert(bret); + /* TODO: madvise? */ + + *dctr -= range.count; + balloon->unballoon_diff -= range.count; + + ur->range_count = 1; + ur->range_array[0].finfo.start_page = range.start; + ur->range_array[0].finfo.page_cnt = range.count; + ur->more_pages = balloon->unballoon_diff > 0; + + trace_hv_balloon_outgoing_unballoon(ur->hdr.trans_id, + range.count, range.start, + balloon->unballoon_diff); + + if (ur->more_pages) { + HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_RB_WAIT); + } else { + HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_REPLY_WAIT); + } + + ret = vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, + NULL, 0, ur, ur_size, false, + ur->hdr.trans_id); + if (ret <= 0) { + error_report("error %zd when posting unballoon msg, expect problems", + ret); + } +} + +static bool hv_balloon_our_range_ensure(HvBalloon *balloon) +{ + uint64_t align; + MemoryRegion *hostmem_mr; + g_autoptr(OurRangeMemslots) our_range_memslots = NULL; + OurRange *our_range; + + if (balloon->our_range) { + return true; + } + + if (!balloon->hostmem) { + return false; + } + + align = (1 << balloon->caps.cap_bits.hot_add_alignment) * MiB; + assert(QEMU_IS_ALIGNED(balloon->addr, align)); + + hostmem_mr = host_memory_backend_get_memory(balloon->hostmem); + + our_range_memslots = hvb_our_range_memslots_new(balloon->addr, + balloon->mr, hostmem_mr, + OBJECT(balloon), + balloon->memslot_count, + balloon->memslot_size); + our_range = OUR_RANGE(our_range_memslots); + + if (hvb_page_range_tree_intree_any(balloon->removed_guest, + our_range->range.start, + our_range->range.count) || + hvb_page_range_tree_intree_any(balloon->removed_both, + our_range->range.start, + our_range->range.count)) { + error_report("some parts of the memory backend were already returned by the guest. this should not happen, please reboot the guest and try again"); + return false; + } + + trace_hv_balloon_our_range_add(our_range->range.count, + our_range->range.start); + + balloon->our_range = g_steal_pointer(&our_range_memslots); + return true; +} + +static void hv_balloon_hot_add_setup(HvBalloon *balloon, StateDesc *stdesc) +{ + /* need to make copy since it is in union with hot_add_range */ + uint64_t hot_add_diff = balloon->hot_add_diff; + PageRange *hot_add_range = &balloon->hot_add_range; + uint64_t align, our_range_remaining; + OurRange *our_range; + + assert(balloon->state == S_HOT_ADD_SETUP); + assert(hot_add_diff > 0); + + if (!hv_balloon_our_range_ensure(balloon)) { + goto ret_idle; + } + + our_range = OUR_RANGE(balloon->our_range); + + align = (1 << balloon->caps.cap_bits.hot_add_alignment) * + (MiB / HV_BALLOON_PAGE_SIZE); + + /* Absolute GPA in pages */ + hot_add_range->start = our_range_get_remaining_start(our_range); + assert(QEMU_IS_ALIGNED(hot_add_range->start, align)); + + our_range_remaining = our_range_get_remaining_size(our_range); + hot_add_range->count = MIN(our_range_remaining, hot_add_diff); + hot_add_range->count = QEMU_ALIGN_DOWN(hot_add_range->count, align); + if (hot_add_range->count == 0) { + goto ret_idle; + } + + hvb_our_range_memslots_ensure_mapped_additional(balloon->our_range, + hot_add_range->count); + + HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_RB_WAIT); + return; + +ret_idle: + HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); +} + +static void hv_balloon_hot_add_rb_wait(HvBalloon *balloon, StateDesc *stdesc) +{ + VMBusChannel *chan = hv_balloon_get_channel(balloon); + struct dm_hot_add_with_region *ha; + size_t ha_size = sizeof(*ha); + + assert(balloon->state == S_HOT_ADD_RB_WAIT); + + if (vmbus_channel_reserve(chan, 0, ha_size) < 0) { + return; + } + + HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_POSTING); +} + +static void hv_balloon_hot_add_posting(HvBalloon *balloon, StateDesc *stdesc) +{ + PageRange *hot_add_range = &balloon->hot_add_range; + uint64_t *current_count = &balloon->ha_current_count; + VMBusChannel *chan = hv_balloon_get_channel(balloon); + g_autofree struct dm_hot_add_with_region *ha = NULL; + size_t ha_size = sizeof(*ha); + union dm_mem_page_range *ha_region; + uint64_t align, chunk_max_size; + ssize_t ret; + + assert(balloon->state == S_HOT_ADD_POSTING); + assert(hot_add_range->count > 0); + + align = (1 << balloon->caps.cap_bits.hot_add_alignment) * + (MiB / HV_BALLOON_PAGE_SIZE); + if (align >= HV_BALLOON_HA_CHUNK_PAGES) { + /* + * If the required alignment is higher than the chunk size we let it + * override that size. + */ + chunk_max_size = align; + } else { + chunk_max_size = QEMU_ALIGN_DOWN(HV_BALLOON_HA_CHUNK_PAGES, align); + } + + /* + * hot_add_range->count starts aligned in hv_balloon_hot_add_setup(), + * then it is either reduced by subtracting aligned current_count or + * further hot-adds are prevented by marking the whole remaining our range + * as unusable in hv_balloon_handle_hot_add_response(). + */ + *current_count = MIN(hot_add_range->count, chunk_max_size); + + ha = g_malloc0(ha_size); + ha_region = &ha->region; + ha->hdr.type = DM_MEM_HOT_ADD_REQUEST; + ha->hdr.size = ha_size; + ha->hdr.trans_id = balloon->trans_id; + + ha->range.finfo.start_page = hot_add_range->start; + ha->range.finfo.page_cnt = *current_count; + ha_region->finfo.start_page = hot_add_range->start; + ha_region->finfo.page_cnt = ha->range.finfo.page_cnt; + + trace_hv_balloon_outgoing_hot_add(ha->hdr.trans_id, + *current_count, hot_add_range->start); + + ret = vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, + NULL, 0, ha, ha_size, false, + ha->hdr.trans_id); + if (ret <= 0) { + error_report("error %zd when posting hot add msg, expect problems", + ret); + } + + HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_REPLY_WAIT); +} + +static void hv_balloon_balloon_rb_wait(HvBalloon *balloon, StateDesc *stdesc) +{ + VMBusChannel *chan = hv_balloon_get_channel(balloon); + size_t bl_size = sizeof(struct dm_balloon); + + assert(balloon->state == S_BALLOON_RB_WAIT); + + if (vmbus_channel_reserve(chan, 0, bl_size) < 0) { + return; + } + + HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_POSTING); +} + +static void hv_balloon_balloon_posting(HvBalloon *balloon, StateDesc *stdesc) +{ + VMBusChannel *chan = hv_balloon_get_channel(balloon); + struct dm_balloon bl; + size_t bl_size = sizeof(bl); + ssize_t ret; + + assert(balloon->state == S_BALLOON_POSTING); + assert(balloon->balloon_diff > 0); + + memset(&bl, 0, sizeof(bl)); + bl.hdr.type = DM_BALLOON_REQUEST; + bl.hdr.size = bl_size; + bl.hdr.trans_id = balloon->trans_id; + bl.num_pages = MIN(balloon->balloon_diff, HV_BALLOON_HR_CHUNK_PAGES); + + trace_hv_balloon_outgoing_balloon(bl.hdr.trans_id, bl.num_pages, + balloon->balloon_diff); + + ret = vmbus_channel_send(chan, VMBUS_PACKET_DATA_INBAND, + NULL, 0, &bl, bl_size, false, + bl.hdr.trans_id); + if (ret <= 0) { + error_report("error %zd when posting balloon msg, expect problems", + ret); + } + + HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_REPLY_WAIT); +} + +static void hv_balloon_idle_state_process_target(HvBalloon *balloon, + StateDesc *stdesc) +{ + bool can_balloon = balloon->caps.cap_bits.balloon; + uint64_t ram_size_pages, total_removed; + + ram_size_pages = hv_balloon_total_ram(balloon); + total_removed = hv_balloon_total_removed_rs(balloon, ram_size_pages); + + /* + * we need to cache the values computed from the balloon target value when + * starting the adjustment procedure in case someone changes the target when + * the procedure is in progress + */ + if (balloon->target > ram_size_pages - total_removed) { + bool can_hot_add = balloon->caps.cap_bits.hot_add; + uint64_t target_diff = balloon->target - + (ram_size_pages - total_removed); + + balloon->unballoon_diff = MIN(target_diff, total_removed); + + if (can_hot_add) { + balloon->hot_add_diff = target_diff - balloon->unballoon_diff; + } else { + balloon->hot_add_diff = 0; + } + + if (balloon->unballoon_diff > 0) { + assert(can_balloon); + HV_BALLOON_STATE_DESC_SET(stdesc, S_UNBALLOON_RB_WAIT); + } else if (balloon->hot_add_diff > 0) { + HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_SETUP); + } + } else if (can_balloon && + balloon->target < ram_size_pages - total_removed) { + balloon->balloon_diff = ram_size_pages - total_removed - + balloon->target; + HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_RB_WAIT); + } +} + +static void hv_balloon_idle_state(HvBalloon *balloon, + StateDesc *stdesc) +{ + assert(balloon->state == S_IDLE); + + if (balloon->target_changed) { + balloon->target_changed = false; + hv_balloon_idle_state_process_target(balloon, stdesc); + return; + } +} + +static const struct { + void (*handler)(HvBalloon *balloon, StateDesc *stdesc); +} state_handlers[] = { + [S_IDLE].handler = hv_balloon_idle_state, + [S_BALLOON_POSTING].handler = hv_balloon_balloon_posting, + [S_BALLOON_RB_WAIT].handler = hv_balloon_balloon_rb_wait, + [S_UNBALLOON_POSTING].handler = hv_balloon_unballoon_posting, + [S_UNBALLOON_RB_WAIT].handler = hv_balloon_unballoon_rb_wait, + [S_HOT_ADD_SETUP].handler = hv_balloon_hot_add_setup, + [S_HOT_ADD_RB_WAIT].handler = hv_balloon_hot_add_rb_wait, + [S_HOT_ADD_POSTING].handler = hv_balloon_hot_add_posting, +}; + +static void hv_balloon_handle_state(HvBalloon *balloon, StateDesc *stdesc) +{ + if (balloon->state >= ARRAY_SIZE(state_handlers) || + !state_handlers[balloon->state].handler) { + return; + } + + state_handlers[balloon->state].handler(balloon, stdesc); +} + +static void hv_balloon_remove_response_insert_range(PageRangeTree tree, + const PageRange *range, + uint64_t *ctr1, + uint64_t *ctr2, + uint64_t *ctr3) +{ + uint64_t dupcount, effcount; + + if (range->count == 0) { + return; + } + + dupcount = 0; + hvb_page_range_tree_insert(tree, range->start, range->count, &dupcount); + + assert(dupcount <= range->count); + effcount = range->count - dupcount; + + *ctr1 += effcount; + *ctr2 += effcount; + if (ctr3) { + *ctr3 += effcount; + } +} + +static void hv_balloon_remove_response_handle_range(HvBalloon *balloon, + PageRange *range, + bool both, + uint64_t *removedctr) +{ + OurRange *our_range = OUR_RANGE(balloon->our_range); + PageRangeTree globaltree = + both ? balloon->removed_both : balloon->removed_guest; + uint64_t *globalctr = + both ? &balloon->removed_both_ctr : &balloon->removed_guest_ctr; + PageRange rangeeff; + + if (range->count == 0) { + return; + } + + trace_hv_balloon_remove_response(range->count, range->start, both); + + if (our_range) { + /* Includes the not-yet-hot-added and unusable parts. */ + rangeeff = our_range->range; + } else { + rangeeff.start = rangeeff.count = 0; + } + + if (page_range_intersection_size(range, rangeeff.start, rangeeff.count) > 0) { + PageRangeTree ourtree = our_range_get_removed_tree(our_range, both); + PageRange rangehole, rangecommon; + uint64_t ourremoved = 0; + + /* process the hole before our range, if it exists */ + page_range_part_before(range, rangeeff.start, &rangehole); + hv_balloon_remove_response_insert_range(globaltree, &rangehole, + globalctr, removedctr, NULL); + if (rangehole.count > 0) { + trace_hv_balloon_remove_response_hole(rangehole.count, + rangehole.start, + range->count, range->start, + rangeeff.start, both); + } + + /* process our part */ + page_range_intersect(range, rangeeff.start, rangeeff.count, + &rangecommon); + hv_balloon_remove_response_insert_range(ourtree, &rangecommon, + globalctr, removedctr, + &ourremoved); + if (rangecommon.count > 0) { + trace_hv_balloon_remove_response_common(rangecommon.count, + rangecommon.start, + range->count, range->start, + rangeeff.count, + rangeeff.start, ourremoved, + both); + } + + /* calculate what's left after our range */ + rangecommon = *range; + page_range_part_after(&rangecommon, rangeeff.start, rangeeff.count, + range); + } + + /* process the remainder of the range that lies after our range */ + if (range->count > 0) { + hv_balloon_remove_response_insert_range(globaltree, range, + globalctr, removedctr, NULL); + trace_hv_balloon_remove_response_remainder(range->count, range->start, + both); + range->count = 0; + } +} + +static void hv_balloon_remove_response_handle_pages(HvBalloon *balloon, + PageRange *range, + uint64_t start, + uint64_t count, + bool both, + uint64_t *removedctr) +{ + assert(count > 0); + + /* + * if there is an existing range that the new range can't be joined to + * dump it into tree(s) + */ + if (range->count > 0 && !page_range_joinable(range, start, count)) { + hv_balloon_remove_response_handle_range(balloon, range, both, + removedctr); + } + + if (range->count == 0) { + range->start = start; + range->count = count; + } else if (page_range_joinable_left(range, start, count)) { + range->start = start; + range->count += count; + } else { /* page_range_joinable_right() */ + range->count += count; + } +} + +static gboolean hv_balloon_handle_remove_host_addr_node(gpointer key, + gpointer value, + gpointer data) +{ + PageRange *range = value; + uint64_t pageoff; + + for (pageoff = 0; pageoff < range->count; ) { + uint64_t addr_64 = (range->start + pageoff) * HV_BALLOON_PAGE_SIZE; + void *addr; + RAMBlock *rb; + ram_addr_t rb_offset; + size_t rb_page_size; + size_t discard_size; + + assert(addr_64 <= UINTPTR_MAX); + addr = (void *)((uintptr_t)addr_64); + rb = qemu_ram_block_from_host(addr, false, &rb_offset); + rb_page_size = qemu_ram_pagesize(rb); + + if (rb_page_size != HV_BALLOON_PAGE_SIZE) { + /* TODO: these should end in "removed_guest" */ + warn_report("guest reported removed page backed by unsupported page size %zu", + rb_page_size); + pageoff++; + continue; + } + + discard_size = MIN(range->count - pageoff, + (rb->max_length - rb_offset) / + HV_BALLOON_PAGE_SIZE); + discard_size = MAX(discard_size, 1); + + if (ram_block_discard_range(rb, rb_offset, discard_size * + HV_BALLOON_PAGE_SIZE) != 0) { + warn_report("guest reported removed page failed discard"); + } + + pageoff += discard_size; + } + + return false; +} + +static void hv_balloon_handle_remove_host_addr_tree(PageRangeTree tree) +{ + g_tree_foreach(tree.t, hv_balloon_handle_remove_host_addr_node, NULL); +} + +static int hv_balloon_handle_remove_section(PageRangeTree tree, + const MemoryRegionSection *section, + uint64_t count) +{ + void *addr = memory_region_get_ram_ptr(section->mr) + + section->offset_within_region; + uint64_t addr_page; + + assert(count > 0); + + if ((uintptr_t)addr % HV_BALLOON_PAGE_SIZE) { + warn_report("guest reported removed pages at an unaligned host addr %p", + addr); + return -EINVAL; + } + + addr_page = (uintptr_t)addr / HV_BALLOON_PAGE_SIZE; + hvb_page_range_tree_insert(tree, addr_page, count, NULL); + + return 0; +} + +static void hv_balloon_handle_remove_ranges(HvBalloon *balloon, + union dm_mem_page_range ranges[], + uint32_t count) +{ + uint64_t removedcnt; + PageRangeTree removed_host_addr; + PageRange range_guest, range_both; + + hvb_page_range_tree_init(&removed_host_addr); + range_guest.count = range_both.count = removedcnt = 0; + for (unsigned int ctr = 0; ctr < count; ctr++) { + union dm_mem_page_range *mr = &ranges[ctr]; + hwaddr pa; + MemoryRegionSection section; + + for (unsigned int offset = 0; offset < mr->finfo.page_cnt; ) { + int ret; + uint64_t pageno = mr->finfo.start_page + offset; + uint64_t pagecnt = 1; + + pa = (hwaddr)pageno << HV_BALLOON_PFN_SHIFT; + section = memory_region_find(get_system_memory(), pa, + (mr->finfo.page_cnt - offset) * + HV_BALLOON_PAGE_SIZE); + if (!section.mr) { + warn_report("guest reported removed page %"PRIu64" not found in RAM", + pageno); + ret = -EINVAL; + goto finish_page; + } + + pagecnt = int128_get64(section.size) / HV_BALLOON_PAGE_SIZE; + if (pagecnt <= 0) { + warn_report("guest reported removed page %"PRIu64" in a section smaller than page size", + pageno); + pagecnt = 1; /* skip the whole page */ + ret = -EINVAL; + goto finish_page; + } + + if (!memory_region_is_ram(section.mr) || + memory_region_is_rom(section.mr) || + memory_region_is_romd(section.mr)) { + warn_report("guest reported removed page %"PRIu64" in a section that is not an ordinary RAM", + pageno); + ret = -EINVAL; + goto finish_page; + } + + ret = hv_balloon_handle_remove_section(removed_host_addr, §ion, + pagecnt); + + finish_page: + if (ret == 0) { + hv_balloon_remove_response_handle_pages(balloon, + &range_both, + pageno, pagecnt, + true, &removedcnt); + } else { + hv_balloon_remove_response_handle_pages(balloon, + &range_guest, + pageno, pagecnt, + false, &removedcnt); + } + + if (section.mr) { + memory_region_unref(section.mr); + } + + offset += pagecnt; + } + } + + hv_balloon_remove_response_handle_range(balloon, &range_both, true, + &removedcnt); + hv_balloon_remove_response_handle_range(balloon, &range_guest, false, + &removedcnt); + + hv_balloon_handle_remove_host_addr_tree(removed_host_addr); + hvb_page_range_tree_destroy(&removed_host_addr); + + if (removedcnt > balloon->balloon_diff) { + warn_report("guest reported more pages removed than currently pending (%"PRIu64" vs %"PRIu64")", + removedcnt, balloon->balloon_diff); + balloon->balloon_diff = 0; + } else { + balloon->balloon_diff -= removedcnt; + } +} + +static bool hv_balloon_handle_msg_size(HvBalloonReq *req, size_t minsize, + const char *msgname) +{ + VMBusChanReq *vmreq = &req->vmreq; + uint32_t msglen = vmreq->msglen; + + if (msglen >= minsize) { + return true; + } + + warn_report("%s message too short (%u vs %zu), ignoring", msgname, + (unsigned int)msglen, minsize); + return false; +} + +static void hv_balloon_handle_version_request(HvBalloon *balloon, + HvBalloonReq *req, + StateDesc *stdesc) +{ + VMBusChanReq *vmreq = &req->vmreq; + struct dm_version_request *msgVr = vmreq->msg; + struct dm_version_response respVr; + + if (balloon->state != S_VERSION) { + warn_report("unexpected DM_VERSION_REQUEST in %d state", + balloon->state); + return; + } + + if (!hv_balloon_handle_msg_size(req, sizeof(*msgVr), + "DM_VERSION_REQUEST")) { + return; + } + + trace_hv_balloon_incoming_version(msgVr->version.major_version, + msgVr->version.minor_version); + + memset(&respVr, 0, sizeof(respVr)); + respVr.hdr.type = DM_VERSION_RESPONSE; + respVr.hdr.size = sizeof(respVr); + respVr.hdr.trans_id = msgVr->hdr.trans_id; + respVr.is_accepted = msgVr->version.version >= DYNMEM_PROTOCOL_VERSION_1 && + msgVr->version.version <= DYNMEM_PROTOCOL_VERSION_3; + + hv_balloon_send_packet(vmreq->chan, (struct dm_message *)&respVr); + + if (respVr.is_accepted) { + HV_BALLOON_STATE_DESC_SET(stdesc, S_CAPS); + } +} + +static void hv_balloon_handle_caps_report(HvBalloon *balloon, + HvBalloonReq *req, + StateDesc *stdesc) +{ + VMBusChanReq *vmreq = &req->vmreq; + struct dm_capabilities *msgCap = vmreq->msg; + struct dm_capabilities_resp_msg respCap; + + if (balloon->state != S_CAPS) { + warn_report("unexpected DM_CAPABILITIES_REPORT in %d state", + balloon->state); + return; + } + + if (!hv_balloon_handle_msg_size(req, sizeof(*msgCap), + "DM_CAPABILITIES_REPORT")) { + return; + } + + trace_hv_balloon_incoming_caps(msgCap->caps.caps); + balloon->caps = msgCap->caps; + + memset(&respCap, 0, sizeof(respCap)); + respCap.hdr.type = DM_CAPABILITIES_RESPONSE; + respCap.hdr.size = sizeof(respCap); + respCap.hdr.trans_id = msgCap->hdr.trans_id; + respCap.is_accepted = 1; + respCap.hot_remove = 1; + respCap.suppress_pressure_reports = !balloon->status_report.enabled; + hv_balloon_send_packet(vmreq->chan, (struct dm_message *)&respCap); + + timer_mod(&balloon->post_init_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + HV_BALLOON_POST_INIT_WAIT); + + HV_BALLOON_STATE_DESC_SET(stdesc, S_POST_INIT_WAIT); +} + +static void hv_balloon_handle_status_report(HvBalloon *balloon, + HvBalloonReq *req) +{ + VMBusChanReq *vmreq = &req->vmreq; + struct dm_status *msgStatus = vmreq->msg; + + if (!hv_balloon_handle_msg_size(req, sizeof(*msgStatus), + "DM_STATUS_REPORT")) { + return; + } + + if (!balloon->status_report.enabled) { + return; + } + + balloon->status_report.committed = msgStatus->num_committed; + balloon->status_report.committed *= HV_BALLOON_PAGE_SIZE; + balloon->status_report.available = msgStatus->num_avail; + balloon->status_report.available *= HV_BALLOON_PAGE_SIZE; + balloon->status_report.received = true; + + qapi_event_send_hv_balloon_status_report(balloon->status_report.committed, + balloon->status_report.available); +} + +HvBalloonInfo *qmp_query_hv_balloon_status_report(Error **errp) +{ + HvBalloon *balloon; + HvBalloonInfo *info; + + balloon = HV_BALLOON(object_resolve_path_type("", TYPE_HV_BALLOON, NULL)); + if (!balloon) { + error_setg(errp, "no %s device present", TYPE_HV_BALLOON); + return NULL; + } + + if (!balloon->status_report.enabled) { + error_setg(errp, "guest memory status reporting not enabled"); + return NULL; + } + + if (!balloon->status_report.received) { + error_setg(errp, "no guest memory status report received yet"); + return NULL; + } + + info = g_malloc0(sizeof(*info)); + info->committed = balloon->status_report.committed; + info->available = balloon->status_report.available; + return info; +} + +static void hv_balloon_handle_unballoon_response(HvBalloon *balloon, + HvBalloonReq *req, + StateDesc *stdesc) +{ + VMBusChanReq *vmreq = &req->vmreq; + struct dm_unballoon_response *msgUrR = vmreq->msg; + + if (balloon->state != S_UNBALLOON_REPLY_WAIT) { + warn_report("unexpected DM_UNBALLOON_RESPONSE in %d state", + balloon->state); + return; + } + + if (!hv_balloon_handle_msg_size(req, sizeof(*msgUrR), + "DM_UNBALLOON_RESPONSE")) + return; + + trace_hv_balloon_incoming_unballoon(msgUrR->hdr.trans_id); + + balloon->trans_id++; + + if (balloon->hot_add_diff > 0) { + bool can_hot_add = balloon->caps.cap_bits.hot_add; + + assert(can_hot_add); + HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_SETUP); + } else { + HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); + } +} + +static void hv_balloon_handle_hot_add_response(HvBalloon *balloon, + HvBalloonReq *req, + StateDesc *stdesc) +{ + PageRange *hot_add_range = &balloon->hot_add_range; + VMBusChanReq *vmreq = &req->vmreq; + struct dm_hot_add_response *msgHaR = vmreq->msg; + OurRange *our_range; + + if (balloon->state != S_HOT_ADD_REPLY_WAIT) { + warn_report("unexpected DM_HOT_ADD_RESPONSE in %d state", + balloon->state); + return; + } + + assert(balloon->our_range); + our_range = OUR_RANGE(balloon->our_range); + + if (!hv_balloon_handle_msg_size(req, sizeof(*msgHaR), + "DM_HOT_ADD_RESPONSE")) + return; + + trace_hv_balloon_incoming_hot_add(msgHaR->hdr.trans_id, msgHaR->result, + msgHaR->page_count); + + balloon->trans_id++; + + if (msgHaR->result) { + if (msgHaR->page_count > balloon->ha_current_count) { + warn_report("DM_HOT_ADD_RESPONSE page count higher than requested (%"PRIu32" vs %"PRIu64")", + msgHaR->page_count, balloon->ha_current_count); + msgHaR->page_count = balloon->ha_current_count; + } + + hvb_our_range_mark_added(our_range, msgHaR->page_count); + hot_add_range->start += msgHaR->page_count; + hot_add_range->count -= msgHaR->page_count; + } + + if (!msgHaR->result || msgHaR->page_count < balloon->ha_current_count) { + /* + * the current planned range was only partially hot-added, take note + * how much of it remains and don't attempt any further hot adds + */ + our_range_mark_remaining_unusable(our_range); + + goto ret_idle; + } + + /* any pages remaining to hot-add in our range? */ + if (hot_add_range->count > 0) { + HV_BALLOON_STATE_DESC_SET(stdesc, S_HOT_ADD_RB_WAIT); + return; + } + +ret_idle: + HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); +} + +static void hv_balloon_handle_balloon_response(HvBalloon *balloon, + HvBalloonReq *req, + StateDesc *stdesc) +{ + VMBusChanReq *vmreq = &req->vmreq; + struct dm_balloon_response *msgBR = vmreq->msg; + + if (balloon->state != S_BALLOON_REPLY_WAIT) { + warn_report("unexpected DM_BALLOON_RESPONSE in %d state", + balloon->state); + return; + } + + if (!hv_balloon_handle_msg_size(req, sizeof(*msgBR), + "DM_BALLOON_RESPONSE")) + return; + + trace_hv_balloon_incoming_balloon(msgBR->hdr.trans_id, msgBR->range_count, + msgBR->more_pages); + + if (vmreq->msglen < sizeof(*msgBR) + + (uint64_t)sizeof(msgBR->range_array[0]) * msgBR->range_count) { + warn_report("DM_BALLOON_RESPONSE too short for the range count"); + return; + } + + if (msgBR->range_count == 0) { + /* The guest is already at its minimum size */ + balloon->balloon_diff = 0; + goto ret_end_trans; + } else { + hv_balloon_handle_remove_ranges(balloon, + msgBR->range_array, + msgBR->range_count); + } + + /* More responses expected? */ + if (msgBR->more_pages) { + return; + } + +ret_end_trans: + balloon->trans_id++; + + if (balloon->balloon_diff > 0) { + HV_BALLOON_STATE_DESC_SET(stdesc, S_BALLOON_RB_WAIT); + } else { + HV_BALLOON_STATE_DESC_SET(stdesc, S_IDLE); + } +} + +static void hv_balloon_handle_packet(HvBalloon *balloon, HvBalloonReq *req, + StateDesc *stdesc) +{ + VMBusChanReq *vmreq = &req->vmreq; + struct dm_message *msg = vmreq->msg; + + if (vmreq->msglen < sizeof(msg->hdr)) { + return; + } + + switch (msg->hdr.type) { + case DM_VERSION_REQUEST: + hv_balloon_handle_version_request(balloon, req, stdesc); + break; + + case DM_CAPABILITIES_REPORT: + hv_balloon_handle_caps_report(balloon, req, stdesc); + break; + + case DM_STATUS_REPORT: + hv_balloon_handle_status_report(balloon, req); + break; + + case DM_MEM_HOT_ADD_RESPONSE: + hv_balloon_handle_hot_add_response(balloon, req, stdesc); + break; + + case DM_UNBALLOON_RESPONSE: + hv_balloon_handle_unballoon_response(balloon, req, stdesc); + break; + + case DM_BALLOON_RESPONSE: + hv_balloon_handle_balloon_response(balloon, req, stdesc); + break; + + default: + warn_report("unknown DM message %u", msg->hdr.type); + break; + } +} + +static bool hv_balloon_recv_channel(HvBalloon *balloon, StateDesc *stdesc) +{ + VMBusChannel *chan; + HvBalloonReq *req; + + if (balloon->state == S_WAIT_RESET || + balloon->state == S_POST_RESET_CLOSED) { + return false; + } + + chan = hv_balloon_get_channel(balloon); + if (vmbus_channel_recv_start(chan)) { + return false; + } + + while ((req = vmbus_channel_recv_peek(chan, sizeof(*req)))) { + hv_balloon_handle_packet(balloon, req, stdesc); + vmbus_free_req(req); + vmbus_channel_recv_pop(chan); + + if (stdesc->state != S_NO_CHANGE) { + break; + } + } + + return vmbus_channel_recv_done(chan) > 0; +} + +/* old state handler -> new state transition (potential) */ +static bool hv_balloon_event_loop_state(HvBalloon *balloon) +{ + StateDesc state_new = HV_BALLOON_STATE_DESC_INIT; + + hv_balloon_handle_state(balloon, &state_new); + return hv_balloon_state_set(balloon, state_new.state, state_new.desc); +} + +/* VMBus message -> new state transition (potential) */ +static bool hv_balloon_event_loop_recv(HvBalloon *balloon) +{ + StateDesc state_new = HV_BALLOON_STATE_DESC_INIT; + bool any_recv, state_changed; + + any_recv = hv_balloon_recv_channel(balloon, &state_new); + state_changed = hv_balloon_state_set(balloon, + state_new.state, state_new.desc); + + return state_changed || any_recv; +} + +static void hv_balloon_event_loop(HvBalloon *balloon) +{ + bool state_repeat, recv_repeat; + + do { + state_repeat = hv_balloon_event_loop_state(balloon); + recv_repeat = hv_balloon_event_loop_recv(balloon); + } while (state_repeat || recv_repeat); +} + +static void hv_balloon_vmdev_chan_notify(VMBusChannel *chan) +{ + HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan)); + + hv_balloon_event_loop(balloon); +} + +static void hv_balloon_stat(void *opaque, BalloonInfo *info) +{ + HvBalloon *balloon = opaque; + info->actual = (hv_balloon_total_ram(balloon) - balloon->removed_both_ctr) + << HV_BALLOON_PFN_SHIFT; +} + +static void hv_balloon_to_target(void *opaque, ram_addr_t target) +{ + HvBalloon *balloon = opaque; + uint64_t target_pages = target >> HV_BALLOON_PFN_SHIFT; + + if (!target_pages) { + return; + } + + /* + * always set target_changed, even with unchanged target, as the user + * might be asking us to try again reaching it + */ + balloon->target = target_pages; + balloon->target_changed = true; + + hv_balloon_event_loop(balloon); +} + +static int hv_balloon_vmdev_open_channel(VMBusChannel *chan) +{ + HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan)); + + if (balloon->state != S_POST_RESET_CLOSED) { + warn_report("guest trying to open a DM channel in invalid %d state", + balloon->state); + return -EINVAL; + } + + HV_BALLOON_SET_STATE(balloon, S_VERSION); + hv_balloon_event_loop(balloon); + + return 0; +} + +static void hv_balloon_vmdev_close_channel(VMBusChannel *chan) +{ + HvBalloon *balloon = HV_BALLOON(vmbus_channel_device(chan)); + + timer_del(&balloon->post_init_timer); + + /* Don't report stale data */ + balloon->status_report.received = false; + + HV_BALLOON_SET_STATE(balloon, S_WAIT_RESET); + hv_balloon_event_loop(balloon); +} + +static void hv_balloon_post_init_timer(void *opaque) +{ + HvBalloon *balloon = opaque; + + if (balloon->state != S_POST_INIT_WAIT) { + return; + } + + HV_BALLOON_SET_STATE(balloon, S_IDLE); + hv_balloon_event_loop(balloon); +} + +static void hv_balloon_system_reset_unrealize_common(HvBalloon *balloon) +{ + g_clear_pointer(&balloon->our_range, hvb_our_range_memslots_free); +} + +static void hv_balloon_system_reset(void *opaque) +{ + HvBalloon *balloon = HV_BALLOON(opaque); + + hv_balloon_system_reset_unrealize_common(balloon); +} + +static void hv_balloon_ensure_mr(HvBalloon *balloon) +{ + MemoryRegion *hostmem_mr; + + assert(balloon->hostmem); + + if (balloon->mr) { + return; + } + + hostmem_mr = host_memory_backend_get_memory(balloon->hostmem); + + balloon->mr = g_new0(MemoryRegion, 1); + memory_region_init(balloon->mr, OBJECT(balloon), TYPE_HV_BALLOON, + memory_region_size(hostmem_mr)); + balloon->mr->align = memory_region_get_alignment(hostmem_mr); +} + +static void hv_balloon_free_mr(HvBalloon *balloon) +{ + if (!balloon->mr) { + return; + } + + object_unparent(OBJECT(balloon->mr)); + g_clear_pointer(&balloon->mr, g_free); +} + +static void hv_balloon_vmdev_realize(VMBusDevice *vdev, Error **errp) +{ + ERRP_GUARD(); + HvBalloon *balloon = HV_BALLOON(vdev); + int ret; + + balloon->state = S_WAIT_RESET; + + ret = qemu_add_balloon_handler(hv_balloon_to_target, hv_balloon_stat, + balloon); + if (ret < 0) { + /* This also protects against having multiple hv-balloon instances */ + error_setg(errp, "Only one balloon device is supported"); + return; + } + + if (balloon->hostmem) { + if (host_memory_backend_is_mapped(balloon->hostmem)) { + Object *obj = OBJECT(balloon->hostmem); + + error_setg(errp, "'%s' property specifies a busy memdev: %s", + HV_BALLOON_MEMDEV_PROP, + object_get_canonical_path_component(obj)); + goto out_balloon_handler; + } + + hv_balloon_ensure_mr(balloon); + + /* This is rather unlikely to happen, but let's still check for it. */ + if (!QEMU_IS_ALIGNED(memory_region_size(balloon->mr), + HV_BALLOON_PAGE_SIZE)) { + error_setg(errp, "'%s' property memdev size has to be a multiple of 0x%" PRIx64, + HV_BALLOON_MEMDEV_PROP, (uint64_t)HV_BALLOON_PAGE_SIZE); + goto out_balloon_handler; + } + + host_memory_backend_set_mapped(balloon->hostmem, true); + vmstate_register_ram(host_memory_backend_get_memory(balloon->hostmem), + DEVICE(balloon)); + } else if (balloon->addr) { + error_setg(errp, "'%s' property must not be set without a memdev", + HV_BALLOON_MEMDEV_PROP); + goto out_balloon_handler; + } + + timer_init_ms(&balloon->post_init_timer, QEMU_CLOCK_VIRTUAL, + hv_balloon_post_init_timer, balloon); + + qemu_register_reset(hv_balloon_system_reset, balloon); + + return; + +out_balloon_handler: + qemu_remove_balloon_handler(balloon); +} + +/* + * VMBus device reset has to be implemented in case the guest decides to + * disconnect and reconnect to the VMBus without rebooting the whole system. + * + * However, the hot-added memory can't be removed here as Windows keeps on using + * it until the system is restarted, even after disconnecting from the VMBus. + */ +static void hv_balloon_vmdev_reset(VMBusDevice *vdev) +{ + HvBalloon *balloon = HV_BALLOON(vdev); + + if (balloon->state == S_POST_RESET_CLOSED) { + return; + } + + if (balloon->our_range) { + hvb_our_range_clear_removed_trees(OUR_RANGE(balloon->our_range)); + } + + hvb_page_range_tree_destroy(&balloon->removed_guest); + hvb_page_range_tree_destroy(&balloon->removed_both); + hvb_page_range_tree_init(&balloon->removed_guest); + hvb_page_range_tree_init(&balloon->removed_both); + + balloon->trans_id = 0; + balloon->removed_guest_ctr = 0; + balloon->removed_both_ctr = 0; + + HV_BALLOON_SET_STATE(balloon, S_POST_RESET_CLOSED); + hv_balloon_event_loop(balloon); +} + +/* + * Clean up things that were (possibly) allocated pre-realization, for example + * from memory_device_pre_plug(), so we don't leak them if the device don't + * actually get realized in the end. + */ +static void hv_balloon_unrealize_finalize_common(HvBalloon *balloon) +{ + hv_balloon_free_mr(balloon); + balloon->addr = 0; + + balloon->memslot_count = 0; +} + +static void hv_balloon_vmdev_unrealize(VMBusDevice *vdev) +{ + HvBalloon *balloon = HV_BALLOON(vdev); + + qemu_unregister_reset(hv_balloon_system_reset, balloon); + + hv_balloon_system_reset_unrealize_common(balloon); + + qemu_remove_balloon_handler(balloon); + + if (balloon->hostmem) { + vmstate_unregister_ram(host_memory_backend_get_memory(balloon->hostmem), + DEVICE(balloon)); + host_memory_backend_set_mapped(balloon->hostmem, false); + } + + hvb_page_range_tree_destroy(&balloon->removed_guest); + hvb_page_range_tree_destroy(&balloon->removed_both); + + hv_balloon_unrealize_finalize_common(balloon); +} + +static uint64_t hv_balloon_md_get_addr(const MemoryDeviceState *md) +{ + return object_property_get_uint(OBJECT(md), HV_BALLOON_ADDR_PROP, + &error_abort); +} + +static void hv_balloon_md_set_addr(MemoryDeviceState *md, uint64_t addr, + Error **errp) +{ + object_property_set_uint(OBJECT(md), HV_BALLOON_ADDR_PROP, addr, errp); +} + +static MemoryRegion *hv_balloon_md_get_memory_region(MemoryDeviceState *md, + Error **errp) +{ + HvBalloon *balloon = HV_BALLOON(md); + + if (!balloon->hostmem) { + return NULL; + } + + hv_balloon_ensure_mr(balloon); + + return balloon->mr; +} + +static uint64_t hv_balloon_md_get_min_alignment(const MemoryDeviceState *md) +{ + /* + * The VM can indicate an alignment up to 32 GiB. Memory device core can + * usually only handle/guarantee 1 GiB alignment. The user will have to + * specify a larger maxmem eventually. + * + * The memory device core will warn the user in case maxmem might have to be + * increased and will fail plugging the device if there is not sufficient + * space after alignment. + * + * TODO: we could do the alignment ourselves in a slightly bigger region. + * But this feels better, although the warning might be annoying. Maybe + * we can optimize that in the future (e.g., with such a device on the + * cmdline place/size the device memory region differently. + */ + return 32 * GiB; +} + +static void hv_balloon_md_fill_device_info(const MemoryDeviceState *md, + MemoryDeviceInfo *info) +{ + HvBalloonDeviceInfo *hi = g_new0(HvBalloonDeviceInfo, 1); + const HvBalloon *balloon = HV_BALLOON(md); + DeviceState *dev = DEVICE(md); + + if (dev->id) { + hi->id = g_strdup(dev->id); + } + + if (balloon->hostmem) { + hi->memdev = object_get_canonical_path(OBJECT(balloon->hostmem)); + hi->memaddr = balloon->addr; + hi->has_memaddr = true; + hi->max_size = memory_region_size(balloon->mr); + /* TODO: expose current provided size or something else? */ + } else { + hi->max_size = 0; + } + + info->u.hv_balloon.data = hi; + info->type = MEMORY_DEVICE_INFO_KIND_HV_BALLOON; +} + +static void hv_balloon_decide_memslots(MemoryDeviceState *md, + unsigned int limit) +{ + HvBalloon *balloon = HV_BALLOON(md); + MemoryRegion *hostmem_mr; + uint64_t region_size, memslot_size, memslots; + + /* We're called exactly once, before realizing the device. */ + assert(!balloon->memslot_count); + + /* We should not be called if we don't have a memory backend */ + assert(balloon->hostmem); + + hostmem_mr = host_memory_backend_get_memory(balloon->hostmem); + region_size = memory_region_size(hostmem_mr); + + assert(region_size > 0); + memslot_size = QEMU_ALIGN_UP(region_size / limit, + HV_BALLOON_HA_MEMSLOT_SIZE_ALIGN); + memslots = QEMU_ALIGN_UP(region_size, memslot_size) / memslot_size; + + if (memslots > 1) { + balloon->memslot_size = memslot_size; + } else { + balloon->memslot_size = region_size; + } + + assert(memslots <= UINT_MAX); + balloon->memslot_count = memslots; +} + +static unsigned int hv_balloon_get_memslots(MemoryDeviceState *md) +{ + const HvBalloon *balloon = HV_BALLOON(md); + + /* We're called after setting the suggested limit. */ + assert(balloon->memslot_count > 0); + + return balloon->memslot_count; +} + +static void hv_balloon_init(Object *obj) +{ +} + +static void hv_balloon_finalize(Object *obj) +{ + HvBalloon *balloon = HV_BALLOON(obj); + + hv_balloon_unrealize_finalize_common(balloon); +} + +static Property hv_balloon_properties[] = { + DEFINE_PROP_BOOL("status-report", HvBalloon, + status_report.enabled, false), + + /* MEMORY_DEVICE props */ + DEFINE_PROP_LINK(HV_BALLOON_MEMDEV_PROP, HvBalloon, hostmem, + TYPE_MEMORY_BACKEND, HostMemoryBackend *), + DEFINE_PROP_UINT64(HV_BALLOON_ADDR_PROP, HvBalloon, addr, 0), + + DEFINE_PROP_END_OF_LIST(), +}; + +static void hv_balloon_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VMBusDeviceClass *vdc = VMBUS_DEVICE_CLASS(klass); + MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(klass); + + device_class_set_props(dc, hv_balloon_properties); + qemu_uuid_parse(HV_BALLOON_GUID, &vdc->classid); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + vdc->vmdev_realize = hv_balloon_vmdev_realize; + vdc->vmdev_unrealize = hv_balloon_vmdev_unrealize; + vdc->vmdev_reset = hv_balloon_vmdev_reset; + vdc->open_channel = hv_balloon_vmdev_open_channel; + vdc->close_channel = hv_balloon_vmdev_close_channel; + vdc->chan_notify_cb = hv_balloon_vmdev_chan_notify; + + mdc->get_addr = hv_balloon_md_get_addr; + mdc->set_addr = hv_balloon_md_set_addr; + mdc->get_plugged_size = memory_device_get_region_size; + mdc->get_memory_region = hv_balloon_md_get_memory_region; + mdc->decide_memslots = hv_balloon_decide_memslots; + mdc->get_memslots = hv_balloon_get_memslots; + mdc->get_min_alignment = hv_balloon_md_get_min_alignment; + mdc->fill_device_info = hv_balloon_md_fill_device_info; +} diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index 4a1b59cb9d..3ea54ba818 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -12,6 +12,7 @@ #include "qemu/module.h" #include "qapi/error.h" #include "exec/address-spaces.h" +#include "exec/memory.h" #include "sysemu/kvm.h" #include "qemu/bitops.h" #include "qemu/error-report.h" @@ -21,6 +22,9 @@ #include "qemu/rcu_queue.h" #include "hw/hyperv/hyperv.h" #include "qom/object.h" +#include "target/i386/kvm/hyperv-proto.h" +#include "target/i386/cpu.h" +#include "exec/cpu-all.h" struct SynICState { DeviceState parent_obj; @@ -157,7 +161,7 @@ void hyperv_synic_reset(CPUState *cs) SynICState *synic = get_synic(cs); if (synic) { - device_legacy_reset(DEVICE(synic)); + device_cold_reset(DEVICE(synic)); } } @@ -947,3 +951,15 @@ uint64_t hyperv_syndbg_query_options(void) return msg.u.query_options.options; } + +static bool vmbus_recommended_features_enabled; + +bool hyperv_are_vmbus_recommended_features_enabled(void) +{ + return vmbus_recommended_features_enabled; +} + +void hyperv_set_vmbus_recommended_features_enabled(void) +{ + vmbus_recommended_features_enabled = true; +} diff --git a/hw/hyperv/meson.build b/hw/hyperv/meson.build index b43f119ea5..d3d2668c71 100644 --- a/hw/hyperv/meson.build +++ b/hw/hyperv/meson.build @@ -2,3 +2,4 @@ specific_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c')) specific_ss.add(when: 'CONFIG_HYPERV_TESTDEV', if_true: files('hyperv_testdev.c')) specific_ss.add(when: 'CONFIG_VMBUS', if_true: files('vmbus.c')) specific_ss.add(when: 'CONFIG_SYNDBG', if_true: files('syndbg.c')) +specific_ss.add(when: 'CONFIG_HV_BALLOON', if_true: files('hv-balloon.c', 'hv-balloon-page_range_tree.c', 'hv-balloon-our_range_memslots.c'), if_false: files('hv-balloon-stub.c')) diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c index 16d04cfdc6..065e12fb1e 100644 --- a/hw/hyperv/syndbg.c +++ b/hw/hyperv/syndbg.c @@ -5,8 +5,8 @@ * See the COPYING file in the top-level directory. */ -#include "qemu/ctype.h" #include "qemu/osdep.h" +#include "qemu/ctype.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/sockets.h" @@ -340,7 +340,7 @@ static void hv_syndbg_realize(DeviceState *dev, Error **errp) syndbg->servaddr.sin_family = AF_INET; if (connect(syndbg->socket, (struct sockaddr *)&syndbg->servaddr, sizeof(syndbg->servaddr)) < 0) { - closesocket(syndbg->socket); + close(syndbg->socket); error_setg(errp, "%s failed to connect to socket", TYPE_HV_SYNDBG); return; } @@ -357,7 +357,7 @@ static void hv_syndbg_unrealize(DeviceState *dev) if (syndbg->socket > 0) { qemu_set_fd_handler(syndbg->socket, NULL, NULL, NULL); - closesocket(syndbg->socket); + close(syndbg->socket); } } diff --git a/hw/hyperv/trace-events b/hw/hyperv/trace-events index b4c35ca8e3..7963c215b1 100644 --- a/hw/hyperv/trace-events +++ b/hw/hyperv/trace-events @@ -16,3 +16,21 @@ vmbus_gpadl_torndown(uint32_t gpadl_id) "gpadl #%d" vmbus_open_channel(uint32_t chan_id, uint32_t gpadl_id, uint32_t target_vp) "channel #%d gpadl #%d target vp %d" vmbus_channel_open(uint32_t chan_id, uint32_t status) "channel #%d status %d" vmbus_close_channel(uint32_t chan_id) "channel #%d" + +# hv-balloon +hv_balloon_state_change(const char *tostr) "-> %s" +hv_balloon_incoming_version(uint16_t major, uint16_t minor) "incoming proto version %u.%u" +hv_balloon_incoming_caps(uint32_t caps) "incoming caps 0x%x" +hv_balloon_outgoing_unballoon(uint32_t trans_id, uint64_t count, uint64_t start, uint64_t rempages) "posting unballoon %"PRIu32" for %"PRIu64" @ 0x%"PRIx64", remaining %"PRIu64 +hv_balloon_incoming_unballoon(uint32_t trans_id) "incoming unballoon response %"PRIu32 +hv_balloon_outgoing_hot_add(uint32_t trans_id, uint64_t count, uint64_t start) "posting hot add %"PRIu32" for %"PRIu64" @ 0x%"PRIx64 +hv_balloon_incoming_hot_add(uint32_t trans_id, uint32_t result, uint32_t count) "incoming hot add response %"PRIu32", result %"PRIu32", count %"PRIu32 +hv_balloon_outgoing_balloon(uint32_t trans_id, uint64_t count, uint64_t rempages) "posting balloon %"PRIu32" for %"PRIu64", remaining %"PRIu64 +hv_balloon_incoming_balloon(uint32_t trans_id, uint32_t range_count, uint32_t more_pages) "incoming balloon response %"PRIu32", ranges %"PRIu32", more %"PRIu32 +hv_balloon_our_range_add(uint64_t count, uint64_t start) "adding our range %"PRIu64" @ 0x%"PRIx64 +hv_balloon_remove_response(uint64_t count, uint64_t start, unsigned int both) "processing remove response range %"PRIu64" @ 0x%"PRIx64", both %u" +hv_balloon_remove_response_hole(uint64_t counthole, uint64_t starthole, uint64_t countrange, uint64_t startrange, uint64_t starthpr, unsigned int both) "response range hole %"PRIu64" @ 0x%"PRIx64" from range %"PRIu64" @ 0x%"PRIx64", before our start 0x%"PRIx64", both %u" +hv_balloon_remove_response_common(uint64_t countcommon, uint64_t startcommon, uint64_t countrange, uint64_t startrange, uint64_t counthpr, uint64_t starthpr, uint64_t removed, unsigned int both) "response common range %"PRIu64" @ 0x%"PRIx64" from range %"PRIu64" @ 0x%"PRIx64" with our %"PRIu64" @ 0x%"PRIx64", removed %"PRIu64", both %u" +hv_balloon_remove_response_remainder(uint64_t count, uint64_t start, unsigned int both) "remove response remaining range %"PRIu64" @ 0x%"PRIx64", both %u" +hv_balloon_map_slot(unsigned int idx, unsigned int total_slots, uint64_t offset) "mapping memslot %u / %u @ 0x%"PRIx64 +hv_balloon_unmap_slot(unsigned int idx, unsigned int total_slots, uint64_t offset) "unmapping memslot %u / %u @ 0x%"PRIx64 diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index 30bc04e1c4..f33afeeea2 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -526,7 +526,7 @@ static const VMStateDescription vmstate_gpadl = { .name = "vmbus/gpadl", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(id, VMBusGpadl), VMSTATE_UINT32(child_relid, VMBusGpadl), VMSTATE_UINT32(num_gfns, VMBusGpadl), @@ -1489,7 +1489,7 @@ static const VMStateDescription vmstate_channel = { .version_id = 0, .minimum_version_id = 0, .post_load = channel_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(id, VMBusChannel), VMSTATE_UINT16(subchan_idx, VMBusChannel), VMSTATE_UINT32(open_id, VMBusChannel), @@ -1578,7 +1578,7 @@ static bool vmbus_initialized(VMBus *vmbus) static void vmbus_reset_all(VMBus *vmbus) { - qbus_reset_all(BUS(vmbus)); + bus_cold_reset(BUS(vmbus)); } static void post_msg(VMBus *vmbus, void *msgdata, uint32_t msglen) @@ -2035,7 +2035,7 @@ static void vdev_reset_on_close(VMBusDevice *vdev) } /* all channels closed -- reset device */ - qdev_reset_all(DEVICE(vdev)); + device_cold_reset(DEVICE(vdev)); } static void handle_close_channel(VMBus *vmbus, vmbus_message_close_channel *msg, @@ -2104,7 +2104,7 @@ static void process_message(VMBus *vmbus) goto out; } msgdata = hv_msg->payload; - msg = (struct vmbus_message_header *)msgdata; + msg = msgdata; trace_vmbus_process_incoming_message(msg->message_type); @@ -2271,7 +2271,7 @@ static void vmbus_dev_realize(DeviceState *dev, Error **errp) VMBus *vmbus = VMBUS(qdev_get_parent_bus(dev)); BusChild *child; Error *err = NULL; - char idstr[UUID_FMT_LEN + 1]; + char idstr[UUID_STR_LEN]; assert(!qemu_uuid_is_null(&vdev->instanceid)); @@ -2380,7 +2380,7 @@ const VMStateDescription vmstate_vmbus_dev = { .name = TYPE_VMBUS_DEVICE, .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(instanceid.data, VMBusDevice, 16), VMSTATE_UINT16(num_channels, VMBusDevice), VMSTATE_STRUCT_VARRAY_POINTER_UINT16(channels, VMBusDevice, @@ -2404,7 +2404,6 @@ static const TypeInfo vmbus_dev_type_info = { static void vmbus_realize(BusState *bus, Error **errp) { int ret = 0; - Error *local_err = NULL; VMBus *vmbus = VMBUS(bus); qemu_mutex_init(&vmbus->rx_queue_lock); @@ -2415,13 +2414,13 @@ static void vmbus_realize(BusState *bus, Error **errp) ret = hyperv_set_msg_handler(VMBUS_MESSAGE_CONNECTION_ID, vmbus_recv_message, vmbus); if (ret != 0) { - error_setg(&local_err, "hyperv set message handler failed: %d", ret); + error_setg(errp, "hyperv set message handler failed: %d", ret); goto error_out; } ret = event_notifier_init(&vmbus->notifier, 0); if (ret != 0) { - error_setg(&local_err, "event notifier failed to init with %d", ret); + error_setg(errp, "event notifier failed to init with %d", ret); goto remove_msg_handler; } @@ -2429,7 +2428,7 @@ static void vmbus_realize(BusState *bus, Error **errp) ret = hyperv_set_event_flag_handler(VMBUS_EVENT_CONNECTION_ID, &vmbus->notifier); if (ret != 0) { - error_setg(&local_err, "hyperv set event handler failed with %d", ret); + error_setg(errp, "hyperv set event handler failed with %d", ret); goto clear_event_notifier; } @@ -2441,7 +2440,6 @@ remove_msg_handler: hyperv_set_msg_handler(VMBUS_MESSAGE_CONNECTION_ID, NULL, NULL); error_out: qemu_mutex_destroy(&vmbus->rx_queue_lock); - error_propagate(errp, local_err); } static void vmbus_unrealize(BusState *bus) @@ -2455,9 +2453,9 @@ static void vmbus_unrealize(BusState *bus) qemu_mutex_destroy(&vmbus->rx_queue_lock); } -static void vmbus_reset(BusState *bus) +static void vmbus_reset_hold(Object *obj) { - vmbus_deinit(VMBUS(bus)); + vmbus_deinit(VMBUS(obj)); } static char *vmbus_get_dev_path(DeviceState *dev) @@ -2469,7 +2467,7 @@ static char *vmbus_get_dev_path(DeviceState *dev) static char *vmbus_get_fw_dev_path(DeviceState *dev) { VMBusDevice *vdev = VMBUS_DEVICE(dev); - char uuid[UUID_FMT_LEN + 1]; + char uuid[UUID_STR_LEN]; qemu_uuid_unparse(&vdev->instanceid, uuid); return g_strdup_printf("%s@%s", qdev_fw_name(dev), uuid); @@ -2478,12 +2476,13 @@ static char *vmbus_get_fw_dev_path(DeviceState *dev) static void vmbus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); k->get_dev_path = vmbus_get_dev_path; k->get_fw_dev_path = vmbus_get_fw_dev_path; k->realize = vmbus_realize; k->unrealize = vmbus_unrealize; - k->reset = vmbus_reset; + rc->phases.hold = vmbus_reset_hold; } static int vmbus_pre_load(void *opaque) @@ -2551,7 +2550,7 @@ static const VMStateDescription vmstate_post_message_input = { .name = "vmbus/hyperv_post_message_input", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { /* * skip connection_id and message_type as they are validated before * queueing and ignored on dequeueing @@ -2574,7 +2573,7 @@ static const VMStateDescription vmstate_rx_queue = { .version_id = 0, .minimum_version_id = 0, .needed = vmbus_rx_queue_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(rx_queue_head, VMBus), VMSTATE_UINT8(rx_queue_size, VMBus), VMSTATE_STRUCT_ARRAY(rx_queue, VMBus, @@ -2591,7 +2590,7 @@ static const VMStateDescription vmstate_vmbus = { .minimum_version_id = 0, .pre_load = vmbus_pre_load, .post_load = vmbus_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(state, VMBus), VMSTATE_UINT32(version, VMBus), VMSTATE_UINT32(target_vp, VMBus), @@ -2600,7 +2599,7 @@ static const VMStateDescription vmstate_vmbus = { vmstate_gpadl, VMBusGpadl, link), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_rx_queue, NULL } @@ -2632,6 +2631,12 @@ static void vmbus_bridge_realize(DeviceState *dev, Error **errp) return; } + if (!hyperv_are_vmbus_recommended_features_enabled()) { + warn_report("VMBus enabled without the recommended set of Hyper-V features: " + "hv-stimer, hv-vapic and hv-runtime. " + "Some Windows versions might not boot or enable the VMBus device"); + } + bridge->bus = VMBUS(qbus_new(TYPE_VMBUS, dev, "vmbus")); } @@ -2645,7 +2650,7 @@ static const VMStateDescription vmstate_vmbus_bridge = { .name = TYPE_VMBUS_BRIDGE, .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_POINTER(bus, VMBusBridge, vmstate_vmbus, VMBus), VMSTATE_END_OF_LIST() }, diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig index 9bb8870517..596a7a3165 100644 --- a/hw/i2c/Kconfig +++ b/hw/i2c/Kconfig @@ -14,7 +14,7 @@ config SMBUS_EEPROM bool select SMBUS -config VERSATILE_I2C +config ARM_SBCON_I2C bool select BITBANG_I2C @@ -34,6 +34,10 @@ config MPC_I2C bool select I2C +config ALLWINNER_I2C + bool + select I2C + config PCA954X bool select I2C @@ -41,3 +45,7 @@ config PCA954X config PMBUS bool select SMBUS + +config BCM2835_I2C + bool + select I2C diff --git a/hw/i2c/allwinner-i2c.c b/hw/i2c/allwinner-i2c.c new file mode 100644 index 0000000000..8abcc39a5c --- /dev/null +++ b/hw/i2c/allwinner-i2c.c @@ -0,0 +1,479 @@ +/* + * Allwinner I2C Bus Serial Interface Emulation + * + * Copyright (C) 2022 Strahinja Jankovic + * + * This file is derived from IMX I2C controller, + * by Jean-Christophe DUBOIS . + * + * 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 . + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "hw/i2c/allwinner-i2c.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "hw/i2c/i2c.h" +#include "qemu/log.h" +#include "trace.h" +#include "qemu/module.h" + +/* Allwinner I2C memory map */ +#define TWI_ADDR_REG 0x00 /* slave address register */ +#define TWI_XADDR_REG 0x04 /* extended slave address register */ +#define TWI_DATA_REG 0x08 /* data register */ +#define TWI_CNTR_REG 0x0c /* control register */ +#define TWI_STAT_REG 0x10 /* status register */ +#define TWI_CCR_REG 0x14 /* clock control register */ +#define TWI_SRST_REG 0x18 /* software reset register */ +#define TWI_EFR_REG 0x1c /* enhance feature register */ +#define TWI_LCR_REG 0x20 /* line control register */ + +/* Used only in slave mode, do not set */ +#define TWI_ADDR_RESET 0 +#define TWI_XADDR_RESET 0 + +/* Data register */ +#define TWI_DATA_MASK 0xFF +#define TWI_DATA_RESET 0 + +/* Control register */ +#define TWI_CNTR_INT_EN (1 << 7) +#define TWI_CNTR_BUS_EN (1 << 6) +#define TWI_CNTR_M_STA (1 << 5) +#define TWI_CNTR_M_STP (1 << 4) +#define TWI_CNTR_INT_FLAG (1 << 3) +#define TWI_CNTR_A_ACK (1 << 2) +#define TWI_CNTR_MASK 0xFC +#define TWI_CNTR_RESET 0 + +/* Status register */ +#define TWI_STAT_MASK 0xF8 +#define TWI_STAT_RESET 0xF8 + +/* Clock register */ +#define TWI_CCR_CLK_M_MASK 0x78 +#define TWI_CCR_CLK_N_MASK 0x07 +#define TWI_CCR_MASK 0x7F +#define TWI_CCR_RESET 0 + +/* Soft reset */ +#define TWI_SRST_MASK 0x01 +#define TWI_SRST_RESET 0 + +/* Enhance feature */ +#define TWI_EFR_MASK 0x03 +#define TWI_EFR_RESET 0 + +/* Line control */ +#define TWI_LCR_SCL_STATE (1 << 5) +#define TWI_LCR_SDA_STATE (1 << 4) +#define TWI_LCR_SCL_CTL (1 << 3) +#define TWI_LCR_SCL_CTL_EN (1 << 2) +#define TWI_LCR_SDA_CTL (1 << 1) +#define TWI_LCR_SDA_CTL_EN (1 << 0) +#define TWI_LCR_MASK 0x3F +#define TWI_LCR_RESET 0x3A + +/* Status value in STAT register is shifted by 3 bits */ +#define TWI_STAT_SHIFT 3 +#define STAT_FROM_STA(x) ((x) << TWI_STAT_SHIFT) +#define STAT_TO_STA(x) ((x) >> TWI_STAT_SHIFT) + +enum { + STAT_BUS_ERROR = 0, + /* Master mode */ + STAT_M_STA_TX, + STAT_M_RSTA_TX, + STAT_M_ADDR_WR_ACK, + STAT_M_ADDR_WR_NACK, + STAT_M_DATA_TX_ACK, + STAT_M_DATA_TX_NACK, + STAT_M_ARB_LOST, + STAT_M_ADDR_RD_ACK, + STAT_M_ADDR_RD_NACK, + STAT_M_DATA_RX_ACK, + STAT_M_DATA_RX_NACK, + /* Slave mode */ + STAT_S_ADDR_WR_ACK, + STAT_S_ARB_LOST_AW_ACK, + STAT_S_GCA_ACK, + STAT_S_ARB_LOST_GCA_ACK, + STAT_S_DATA_RX_SA_ACK, + STAT_S_DATA_RX_SA_NACK, + STAT_S_DATA_RX_GCA_ACK, + STAT_S_DATA_RX_GCA_NACK, + STAT_S_STP_RSTA, + STAT_S_ADDR_RD_ACK, + STAT_S_ARB_LOST_AR_ACK, + STAT_S_DATA_TX_ACK, + STAT_S_DATA_TX_NACK, + STAT_S_LB_TX_ACK, + /* Master mode, 10-bit */ + STAT_M_2ND_ADDR_WR_ACK, + STAT_M_2ND_ADDR_WR_NACK, + /* Idle */ + STAT_IDLE = 0x1f +} TWI_STAT_STA; + +static const char *allwinner_i2c_get_regname(unsigned offset) +{ + switch (offset) { + case TWI_ADDR_REG: + return "ADDR"; + case TWI_XADDR_REG: + return "XADDR"; + case TWI_DATA_REG: + return "DATA"; + case TWI_CNTR_REG: + return "CNTR"; + case TWI_STAT_REG: + return "STAT"; + case TWI_CCR_REG: + return "CCR"; + case TWI_SRST_REG: + return "SRST"; + case TWI_EFR_REG: + return "EFR"; + case TWI_LCR_REG: + return "LCR"; + default: + return "[?]"; + } +} + +static inline bool allwinner_i2c_is_reset(AWI2CState *s) +{ + return s->srst & TWI_SRST_MASK; +} + +static inline bool allwinner_i2c_bus_is_enabled(AWI2CState *s) +{ + return s->cntr & TWI_CNTR_BUS_EN; +} + +static inline bool allwinner_i2c_interrupt_is_enabled(AWI2CState *s) +{ + return s->cntr & TWI_CNTR_INT_EN; +} + +static void allwinner_i2c_reset_hold(Object *obj) +{ + AWI2CState *s = AW_I2C(obj); + + if (STAT_TO_STA(s->stat) != STAT_IDLE) { + i2c_end_transfer(s->bus); + } + + s->addr = TWI_ADDR_RESET; + s->xaddr = TWI_XADDR_RESET; + s->data = TWI_DATA_RESET; + s->cntr = TWI_CNTR_RESET; + s->stat = TWI_STAT_RESET; + s->ccr = TWI_CCR_RESET; + s->srst = TWI_SRST_RESET; + s->efr = TWI_EFR_RESET; + s->lcr = TWI_LCR_RESET; +} + +static inline void allwinner_i2c_raise_interrupt(AWI2CState *s) +{ + /* + * Raise an interrupt if the device is not reset and it is configured + * to generate some interrupts. + */ + if (!allwinner_i2c_is_reset(s) && allwinner_i2c_bus_is_enabled(s)) { + if (STAT_TO_STA(s->stat) != STAT_IDLE) { + s->cntr |= TWI_CNTR_INT_FLAG; + if (allwinner_i2c_interrupt_is_enabled(s)) { + qemu_irq_raise(s->irq); + } + } + } +} + +static uint64_t allwinner_i2c_read(void *opaque, hwaddr offset, + unsigned size) +{ + uint16_t value; + AWI2CState *s = AW_I2C(opaque); + + switch (offset) { + case TWI_ADDR_REG: + value = s->addr; + break; + case TWI_XADDR_REG: + value = s->xaddr; + break; + case TWI_DATA_REG: + if ((STAT_TO_STA(s->stat) == STAT_M_ADDR_RD_ACK) || + (STAT_TO_STA(s->stat) == STAT_M_DATA_RX_ACK) || + (STAT_TO_STA(s->stat) == STAT_M_DATA_RX_NACK)) { + /* Get the next byte */ + s->data = i2c_recv(s->bus); + + if (s->cntr & TWI_CNTR_A_ACK) { + s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK); + } else { + s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK); + } + allwinner_i2c_raise_interrupt(s); + } + value = s->data; + break; + case TWI_CNTR_REG: + value = s->cntr; + break; + case TWI_STAT_REG: + value = s->stat; + /* + * If polling when reading then change state to indicate data + * is available + */ + if (STAT_TO_STA(s->stat) == STAT_M_ADDR_RD_ACK) { + if (s->cntr & TWI_CNTR_A_ACK) { + s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK); + } else { + s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK); + } + allwinner_i2c_raise_interrupt(s); + } + break; + case TWI_CCR_REG: + value = s->ccr; + break; + case TWI_SRST_REG: + value = s->srst; + break; + case TWI_EFR_REG: + value = s->efr; + break; + case TWI_LCR_REG: + value = s->lcr; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", TYPE_AW_I2C, __func__, offset); + value = 0; + break; + } + + trace_allwinner_i2c_read(allwinner_i2c_get_regname(offset), offset, value); + + return (uint64_t)value; +} + +static void allwinner_i2c_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + AWI2CState *s = AW_I2C(opaque); + + value &= 0xff; + + trace_allwinner_i2c_write(allwinner_i2c_get_regname(offset), offset, value); + + switch (offset) { + case TWI_ADDR_REG: + s->addr = (uint8_t)value; + break; + case TWI_XADDR_REG: + s->xaddr = (uint8_t)value; + break; + case TWI_DATA_REG: + /* If the device is in reset or not enabled, nothing to do */ + if (allwinner_i2c_is_reset(s) || (!allwinner_i2c_bus_is_enabled(s))) { + break; + } + + s->data = value & TWI_DATA_MASK; + + switch (STAT_TO_STA(s->stat)) { + case STAT_M_STA_TX: + case STAT_M_RSTA_TX: + /* Send address */ + if (i2c_start_transfer(s->bus, extract32(s->data, 1, 7), + extract32(s->data, 0, 1))) { + /* If non zero is returned, the address is not valid */ + s->stat = STAT_FROM_STA(STAT_M_ADDR_WR_NACK); + } else { + /* Determine if read of write */ + if (extract32(s->data, 0, 1)) { + s->stat = STAT_FROM_STA(STAT_M_ADDR_RD_ACK); + } else { + s->stat = STAT_FROM_STA(STAT_M_ADDR_WR_ACK); + } + allwinner_i2c_raise_interrupt(s); + } + break; + case STAT_M_ADDR_WR_ACK: + case STAT_M_DATA_TX_ACK: + if (i2c_send(s->bus, s->data)) { + /* If the target return non zero then end the transfer */ + s->stat = STAT_FROM_STA(STAT_M_DATA_TX_NACK); + i2c_end_transfer(s->bus); + } else { + s->stat = STAT_FROM_STA(STAT_M_DATA_TX_ACK); + allwinner_i2c_raise_interrupt(s); + } + break; + default: + break; + } + break; + case TWI_CNTR_REG: + if (!allwinner_i2c_is_reset(s)) { + /* Do something only if not in software reset */ + s->cntr = value & TWI_CNTR_MASK; + + /* Check if start condition should be sent */ + if (s->cntr & TWI_CNTR_M_STA) { + /* Update status */ + if (STAT_TO_STA(s->stat) == STAT_IDLE) { + /* Send start condition */ + s->stat = STAT_FROM_STA(STAT_M_STA_TX); + } else { + /* Send repeated start condition */ + s->stat = STAT_FROM_STA(STAT_M_RSTA_TX); + } + /* Clear start condition */ + s->cntr &= ~TWI_CNTR_M_STA; + } + if (s->cntr & TWI_CNTR_M_STP) { + /* Update status */ + i2c_end_transfer(s->bus); + s->stat = STAT_FROM_STA(STAT_IDLE); + s->cntr &= ~TWI_CNTR_M_STP; + } + + if (!s->irq_clear_inverted && !(s->cntr & TWI_CNTR_INT_FLAG)) { + /* Write 0 to clear this flag */ + qemu_irq_lower(s->irq); + } else if (s->irq_clear_inverted && (s->cntr & TWI_CNTR_INT_FLAG)) { + /* Write 1 to clear this flag */ + s->cntr &= ~TWI_CNTR_INT_FLAG; + qemu_irq_lower(s->irq); + } + + if ((s->cntr & TWI_CNTR_A_ACK) == 0) { + if (STAT_TO_STA(s->stat) == STAT_M_DATA_RX_ACK) { + s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK); + } + } else { + if (STAT_TO_STA(s->stat) == STAT_M_DATA_RX_NACK) { + s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK); + } + } + allwinner_i2c_raise_interrupt(s); + + } + break; + case TWI_CCR_REG: + s->ccr = value & TWI_CCR_MASK; + break; + case TWI_SRST_REG: + if (((value & TWI_SRST_MASK) == 0) && (s->srst & TWI_SRST_MASK)) { + /* Perform reset */ + allwinner_i2c_reset_hold(OBJECT(s)); + } + s->srst = value & TWI_SRST_MASK; + break; + case TWI_EFR_REG: + s->efr = value & TWI_EFR_MASK; + break; + case TWI_LCR_REG: + s->lcr = value & TWI_LCR_MASK; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", TYPE_AW_I2C, __func__, offset); + break; + } +} + +static const MemoryRegionOps allwinner_i2c_ops = { + .read = allwinner_i2c_read, + .write = allwinner_i2c_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription allwinner_i2c_vmstate = { + .name = TYPE_AW_I2C, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT8(addr, AWI2CState), + VMSTATE_UINT8(xaddr, AWI2CState), + VMSTATE_UINT8(data, AWI2CState), + VMSTATE_UINT8(cntr, AWI2CState), + VMSTATE_UINT8(ccr, AWI2CState), + VMSTATE_UINT8(srst, AWI2CState), + VMSTATE_UINT8(efr, AWI2CState), + VMSTATE_UINT8(lcr, AWI2CState), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_i2c_realize(DeviceState *dev, Error **errp) +{ + AWI2CState *s = AW_I2C(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_i2c_ops, s, + TYPE_AW_I2C, AW_I2C_MEM_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + s->bus = i2c_init_bus(dev, "i2c"); +} + +static void allwinner_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = allwinner_i2c_reset_hold; + dc->vmsd = &allwinner_i2c_vmstate; + dc->realize = allwinner_i2c_realize; + dc->desc = "Allwinner I2C Controller"; +} + +static const TypeInfo allwinner_i2c_type_info = { + .name = TYPE_AW_I2C, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AWI2CState), + .class_init = allwinner_i2c_class_init, +}; + +static void allwinner_i2c_sun6i_init(Object *obj) +{ + AWI2CState *s = AW_I2C(obj); + + s->irq_clear_inverted = true; +} + +static const TypeInfo allwinner_i2c_sun6i_type_info = { + .name = TYPE_AW_I2C_SUN6I, + .parent = TYPE_AW_I2C, + .instance_init = allwinner_i2c_sun6i_init, +}; + +static void allwinner_i2c_register_types(void) +{ + type_register_static(&allwinner_i2c_type_info); + type_register_static(&allwinner_i2c_sun6i_type_info); +} + +type_init(allwinner_i2c_register_types) diff --git a/hw/i2c/versatile_i2c.c b/hw/i2c/arm_sbcon_i2c.c similarity index 70% rename from hw/i2c/versatile_i2c.c rename to hw/i2c/arm_sbcon_i2c.c index 3a04ba3969..979ccbe0ed 100644 --- a/hw/i2c/versatile_i2c.c +++ b/hw/i2c/arm_sbcon_i2c.c @@ -29,11 +29,6 @@ #include "qemu/module.h" #include "qom/object.h" -typedef ArmSbconI2CState VersatileI2CState; -DECLARE_INSTANCE_CHECKER(VersatileI2CState, VERSATILE_I2C, - TYPE_VERSATILE_I2C) - - REG32(CONTROL_GET, 0) REG32(CONTROL_SET, 0) @@ -42,10 +37,10 @@ REG32(CONTROL_CLR, 4) #define SCL BIT(0) #define SDA BIT(1) -static uint64_t versatile_i2c_read(void *opaque, hwaddr offset, +static uint64_t arm_sbcon_i2c_read(void *opaque, hwaddr offset, unsigned size) { - VersatileI2CState *s = (VersatileI2CState *)opaque; + ArmSbconI2CState *s = opaque; switch (offset) { case A_CONTROL_SET: @@ -57,10 +52,10 @@ static uint64_t versatile_i2c_read(void *opaque, hwaddr offset, } } -static void versatile_i2c_write(void *opaque, hwaddr offset, +static void arm_sbcon_i2c_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { - VersatileI2CState *s = (VersatileI2CState *)opaque; + ArmSbconI2CState *s = opaque; switch (offset) { case A_CONTROL_SET: @@ -77,36 +72,36 @@ static void versatile_i2c_write(void *opaque, hwaddr offset, s->in = bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SDA, (s->out & SDA) != 0); } -static const MemoryRegionOps versatile_i2c_ops = { - .read = versatile_i2c_read, - .write = versatile_i2c_write, +static const MemoryRegionOps arm_sbcon_i2c_ops = { + .read = arm_sbcon_i2c_read, + .write = arm_sbcon_i2c_write, .endianness = DEVICE_NATIVE_ENDIAN, }; -static void versatile_i2c_init(Object *obj) +static void arm_sbcon_i2c_init(Object *obj) { DeviceState *dev = DEVICE(obj); - VersatileI2CState *s = VERSATILE_I2C(obj); + ArmSbconI2CState *s = ARM_SBCON_I2C(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); I2CBus *bus; bus = i2c_init_bus(dev, "i2c"); bitbang_i2c_init(&s->bitbang, bus); - memory_region_init_io(&s->iomem, obj, &versatile_i2c_ops, s, + memory_region_init_io(&s->iomem, obj, &arm_sbcon_i2c_ops, s, "arm_sbcon_i2c", 0x1000); sysbus_init_mmio(sbd, &s->iomem); } -static const TypeInfo versatile_i2c_info = { - .name = TYPE_VERSATILE_I2C, +static const TypeInfo arm_sbcon_i2c_info = { + .name = TYPE_ARM_SBCON_I2C, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(VersatileI2CState), - .instance_init = versatile_i2c_init, + .instance_size = sizeof(ArmSbconI2CState), + .instance_init = arm_sbcon_i2c_init, }; -static void versatile_i2c_register_types(void) +static void arm_sbcon_i2c_register_types(void) { - type_register_static(&versatile_i2c_info); + type_register_static(&arm_sbcon_i2c_info); } -type_init(versatile_i2c_register_types) +type_init(arm_sbcon_i2c_register_types) diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 03a4f5a910..b43afd250d 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "migration/vmstate.h" +#include "qemu/cutils.h" #include "qemu/log.h" #include "qemu/module.h" #include "qemu/error-report.h" @@ -28,196 +29,98 @@ #include "hw/i2c/aspeed_i2c.h" #include "hw/irq.h" #include "hw/qdev-properties.h" +#include "hw/registerfields.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_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) -#define I2CD_MSB_STS (0x1 << 9) -#define I2CD_SDA_DRIVE_1T_EN (0x1 << 8) -#define I2CD_M_SDA_DRIVE_1T_EN (0x1 << 7) -#define I2CD_M_HIGH_SPEED_EN (0x1 << 6) -#define I2CD_DEF_ADDR_EN (0x1 << 5) -#define I2CD_DEF_ALERT_EN (0x1 << 4) -#define I2CD_DEF_ARP_EN (0x1 << 3) -#define I2CD_DEF_GCALL_EN (0x1 << 2) -#define I2CD_SLAVE_EN (0x1 << 1) -#define I2CD_MASTER_EN (0x1) - -#define I2CD_AC_TIMING_REG1 0x04 /* Clock and AC Timing Control #1 */ -#define I2CD_AC_TIMING_REG2 0x08 /* Clock and AC Timing Control #1 */ -#define I2CD_INTR_CTRL_REG 0x0c /* I2CD Interrupt Control */ -#define I2CD_INTR_STS_REG 0x10 /* I2CD Interrupt Status */ - -#define I2CD_INTR_SLAVE_ADDR_MATCH (0x1 << 31) /* 0: addr1 1: addr2 */ -#define I2CD_INTR_SLAVE_ADDR_RX_PENDING (0x1 << 30) -/* bits[19-16] Reserved */ - -/* All bits below are cleared by writing 1 */ -#define I2CD_INTR_SLAVE_INACTIVE_TIMEOUT (0x1 << 15) -#define I2CD_INTR_SDA_DL_TIMEOUT (0x1 << 14) -#define I2CD_INTR_BUS_RECOVER_DONE (0x1 << 13) -#define I2CD_INTR_SMBUS_ALERT (0x1 << 12) /* Bus [0-3] only */ -#define I2CD_INTR_SMBUS_ARP_ADDR (0x1 << 11) /* Removed */ -#define I2CD_INTR_SMBUS_DEV_ALERT_ADDR (0x1 << 10) /* Removed */ -#define I2CD_INTR_SMBUS_DEF_ADDR (0x1 << 9) /* Removed */ -#define I2CD_INTR_GCALL_ADDR (0x1 << 8) /* Removed */ -#define I2CD_INTR_SLAVE_ADDR_RX_MATCH (0x1 << 7) /* use RX_DONE */ -#define I2CD_INTR_SCL_TIMEOUT (0x1 << 6) -#define I2CD_INTR_ABNORMAL (0x1 << 5) -#define I2CD_INTR_NORMAL_STOP (0x1 << 4) -#define I2CD_INTR_ARBIT_LOSS (0x1 << 3) -#define I2CD_INTR_RX_DONE (0x1 << 2) -#define I2CD_INTR_TX_NAK (0x1 << 1) -#define I2CD_INTR_TX_ACK (0x1 << 0) - -#define I2CD_CMD_REG 0x14 /* I2CD Command/Status */ -#define I2CD_SDA_OE (0x1 << 28) -#define I2CD_SDA_O (0x1 << 27) -#define I2CD_SCL_OE (0x1 << 26) -#define I2CD_SCL_O (0x1 << 25) -#define I2CD_TX_TIMING (0x1 << 24) -#define I2CD_TX_STATUS (0x1 << 23) - -#define I2CD_TX_STATE_SHIFT 19 /* Tx State Machine */ -#define I2CD_TX_STATE_MASK 0xf -#define I2CD_IDLE 0x0 -#define I2CD_MACTIVE 0x8 -#define I2CD_MSTART 0x9 -#define I2CD_MSTARTR 0xa -#define I2CD_MSTOP 0xb -#define I2CD_MTXD 0xc -#define I2CD_MRXACK 0xd -#define I2CD_MRXD 0xe -#define I2CD_MTXACK 0xf -#define I2CD_SWAIT 0x1 -#define I2CD_SRXD 0x4 -#define I2CD_STXACK 0x5 -#define I2CD_STXD 0x6 -#define I2CD_SRXACK 0x7 -#define I2CD_RECOVER 0x3 - -#define I2CD_SCL_LINE_STS (0x1 << 18) -#define I2CD_SDA_LINE_STS (0x1 << 17) -#define I2CD_BUS_BUSY_STS (0x1 << 16) -#define I2CD_SDA_OE_OUT_DIR (0x1 << 15) -#define I2CD_SDA_O_OUT_DIR (0x1 << 14) -#define I2CD_SCL_OE_OUT_DIR (0x1 << 13) -#define I2CD_SCL_O_OUT_DIR (0x1 << 12) -#define I2CD_BUS_RECOVER_CMD_EN (0x1 << 11) -#define I2CD_S_ALT_EN (0x1 << 10) - -/* 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) -#define I2CD_S_TX_CMD (0x1 << 2) -#define I2CD_M_TX_CMD (0x1 << 1) -#define I2CD_M_START_CMD (0x1) - -#define I2CD_DEV_ADDR_REG 0x18 /* Slave Device Address */ -#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) -{ - return bus->ctrl & I2CD_MASTER_EN; -} - -static inline bool aspeed_i2c_bus_is_enabled(AspeedI2CBus *bus) -{ - return bus->ctrl & (I2CD_MASTER_EN | I2CD_SLAVE_EN); -} +/* Enable SLAVE_ADDR_RX_MATCH always */ +#define R_I2CD_INTR_STS_ALWAYS_ENABLE R_I2CD_INTR_STS_SLAVE_ADDR_RX_MATCH_MASK static inline void aspeed_i2c_bus_raise_interrupt(AspeedI2CBus *bus) { AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); + uint32_t intr_ctrl_reg = aspeed_i2c_bus_intr_ctrl_offset(bus); + uint32_t intr_ctrl_mask = bus->regs[intr_ctrl_reg] | + R_I2CD_INTR_STS_ALWAYS_ENABLE; + bool raise_irq; - 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" : ""); + if (trace_event_get_state_backends(TRACE_ASPEED_I2C_BUS_RAISE_INTERRUPT)) { + g_autofree char *buf = g_strdup_printf("%s%s%s%s%s%s%s", + aspeed_i2c_bus_pkt_mode_en(bus) && + ARRAY_FIELD_EX32(bus->regs, I2CM_INTR_STS, PKT_CMD_DONE) ? + "pktdone|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, TX_NAK) ? + "nak|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, TX_ACK) ? + "ack|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, RX_DONE) ? + "done|" : "", + ARRAY_FIELD_EX32(bus->regs, I2CD_INTR_STS, SLAVE_ADDR_RX_MATCH) ? + "slave-match|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, NORMAL_STOP) ? + "stop|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, ABNORMAL) ? + "abnormal" : ""); - bus->intr_status &= bus->intr_ctrl; - if (bus->intr_status) { + trace_aspeed_i2c_bus_raise_interrupt(bus->regs[reg_intr_sts], buf); + } + + raise_irq = bus->regs[reg_intr_sts] & intr_ctrl_mask ; + + /* In packet mode we don't mask off INTR_STS */ + if (!aspeed_i2c_bus_pkt_mode_en(bus)) { + bus->regs[reg_intr_sts] &= intr_ctrl_mask; + } + + if (raise_irq) { bus->controller->intr_status |= 1 << bus->id; qemu_irq_raise(aic->bus_get_irq(bus)); } } -static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset, - unsigned size) +static inline void aspeed_i2c_bus_raise_slave_interrupt(AspeedI2CBus *bus) { - AspeedI2CBus *bus = opaque; AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); - uint64_t value = -1; + + if (!bus->regs[R_I2CS_INTR_STS]) { + return; + } + + bus->controller->intr_status |= 1 << bus->id; + qemu_irq_raise(aic->bus_get_irq(bus)); +} + +static uint64_t aspeed_i2c_bus_old_read(AspeedI2CBus *bus, hwaddr offset, + unsigned size) +{ + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + uint64_t value = bus->regs[offset / sizeof(*bus->regs)]; switch (offset) { - case I2CD_FUN_CTRL_REG: - value = bus->ctrl; + case A_I2CD_FUN_CTRL: + case A_I2CD_AC_TIMING1: + case A_I2CD_AC_TIMING2: + case A_I2CD_INTR_CTRL: + case A_I2CD_INTR_STS: + case A_I2CD_DEV_ADDR: + case A_I2CD_POOL_CTRL: + case A_I2CD_BYTE_BUF: + /* Value is already set, don't do anything. */ break; - case I2CD_AC_TIMING_REG1: - value = bus->timing[0]; + case A_I2CD_CMD: + value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus)); break; - case I2CD_AC_TIMING_REG2: - value = bus->timing[1]; - break; - case I2CD_INTR_CTRL_REG: - value = bus->intr_ctrl; - break; - case I2CD_INTR_STS_REG: - value = bus->intr_status; - break; - case I2CD_POOL_CTRL_REG: - value = bus->pool_ctrl; - break; - case I2CD_BYTE_BUF_REG: - value = bus->buf; - break; - case I2CD_CMD_REG: - value = bus->cmd | (i2c_bus_busy(bus->bus) << 16); - break; - case I2CD_DMA_ADDR: + case A_I2CD_DMA_ADDR: if (!aic->has_dma) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); - break; + value = -1; } - value = bus->dma_addr; break; - case I2CD_DMA_LEN: + case A_I2CD_DMA_LEN: if (!aic->has_dma) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); - break; + value = -1; } - value = bus->dma_len; break; default: @@ -231,68 +134,148 @@ static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset, return value; } +static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, + unsigned size) +{ + uint64_t value = bus->regs[offset / sizeof(*bus->regs)]; + + switch (offset) { + case A_I2CC_FUN_CTRL: + case A_I2CC_AC_TIMING: + case A_I2CC_POOL_CTRL: + case A_I2CM_INTR_CTRL: + case A_I2CM_INTR_STS: + case A_I2CC_MS_TXRX_BYTE_BUF: + case A_I2CM_DMA_LEN: + case A_I2CM_DMA_TX_ADDR: + case A_I2CM_DMA_RX_ADDR: + case A_I2CM_DMA_LEN_STS: + case A_I2CC_DMA_ADDR: + case A_I2CC_DMA_LEN: + + case A_I2CS_DEV_ADDR: + case A_I2CS_DMA_RX_ADDR: + case A_I2CS_DMA_LEN: + case A_I2CS_CMD: + case A_I2CS_INTR_CTRL: + case A_I2CS_DMA_LEN_STS: + /* Value is already set, don't do anything. */ + break; + case A_I2CS_INTR_STS: + break; + case A_I2CM_CMD: + value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus)); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); + value = -1; + break; + } + + trace_aspeed_i2c_bus_read(bus->id, offset, size, value); + return value; +} + +static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset, + unsigned size) +{ + AspeedI2CBus *bus = opaque; + if (aspeed_i2c_is_new_mode(bus->controller)) { + return aspeed_i2c_bus_new_read(bus, offset, size); + } + return aspeed_i2c_bus_old_read(bus, offset, size); +} + static void aspeed_i2c_set_state(AspeedI2CBus *bus, uint8_t state) { - bus->cmd &= ~(I2CD_TX_STATE_MASK << I2CD_TX_STATE_SHIFT); - bus->cmd |= (state & I2CD_TX_STATE_MASK) << I2CD_TX_STATE_SHIFT; + if (aspeed_i2c_is_new_mode(bus->controller)) { + SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CC_MS_TXRX_BYTE_BUF, TX_STATE, + state); + } else { + SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CD_CMD, TX_STATE, state); + } } static uint8_t aspeed_i2c_get_state(AspeedI2CBus *bus) { - return (bus->cmd >> I2CD_TX_STATE_SHIFT) & I2CD_TX_STATE_MASK; + if (aspeed_i2c_is_new_mode(bus->controller)) { + return SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CC_MS_TXRX_BYTE_BUF, + TX_STATE); + } + return SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD, TX_STATE); } static int aspeed_i2c_dma_read(AspeedI2CBus *bus, uint8_t *data) { MemTxResult result; AspeedI2CState *s = bus->controller; + uint32_t reg_dma_addr = aspeed_i2c_bus_dma_addr_offset(bus); + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); - result = address_space_read(&s->dram_as, bus->dma_addr, + result = address_space_read(&s->dram_as, bus->regs[reg_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); + __func__, bus->regs[reg_dma_addr]); return -1; } - bus->dma_addr++; - bus->dma_len--; + bus->regs[reg_dma_addr]++; + bus->regs[reg_dma_len]--; return 0; } -static int aspeed_i2c_bus_send(AspeedI2CBus *bus, uint8_t pool_start) +static int aspeed_i2c_bus_send(AspeedI2CBus *bus) { AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); int ret = -1; int i; + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); + uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); + int pool_tx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, + TX_COUNT) + 1; - if (bus->cmd & I2CD_TX_BUFF_ENABLE) { - for (i = pool_start; i < I2CD_POOL_TX_COUNT(bus->pool_ctrl); i++) { + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { + for (i = 0; i < pool_tx_count; 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), + trace_aspeed_i2c_bus_send("BUF", i + 1, pool_tx_count, 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) { + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_BUFF_EN, 0); + } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { + /* In new mode, clear how many bytes we TXed */ + if (aspeed_i2c_is_new_mode(bus->controller)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, 0); + } + while (bus->regs[reg_dma_len]) { uint8_t data; aspeed_i2c_dma_read(bus, &data); - trace_aspeed_i2c_bus_send("DMA", bus->dma_len, bus->dma_len, data); + trace_aspeed_i2c_bus_send("DMA", bus->regs[reg_dma_len], + bus->regs[reg_dma_len], data); ret = i2c_send(bus->bus, data); if (ret) { break; } + /* In new mode, keep track of how many bytes we TXed */ + if (aspeed_i2c_is_new_mode(bus->controller)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, + ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN_STS, + TX_LEN) + 1); + } } - bus->cmd &= ~I2CD_TX_DMA_ENABLE; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_DMA_EN, 0); } else { - trace_aspeed_i2c_bus_send("BYTE", pool_start, 1, bus->buf); - ret = i2c_send(bus->bus, bus->buf); + trace_aspeed_i2c_bus_send("BYTE", 0, 1, + bus->regs[reg_byte_buf]); + ret = i2c_send(bus->bus, bus->regs[reg_byte_buf]); } return ret; @@ -304,74 +287,103 @@ static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); uint8_t data; int i; + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); + uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); + uint32_t reg_dma_addr = aspeed_i2c_bus_dma_addr_offset(bus); + int pool_rx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, + RX_SIZE) + 1; - if (bus->cmd & I2CD_RX_BUFF_ENABLE) { + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN)) { uint8_t *pool_base = aic->bus_pool_base(bus); + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, + BUF_ORGANIZATION)) { + pool_base += 16; + } - for (i = 0; i < I2CD_POOL_RX_SIZE(bus->pool_ctrl); i++) { + for (i = 0; i < pool_rx_count; i++) { pool_base[i] = i2c_recv(bus->bus); - trace_aspeed_i2c_bus_recv("BUF", i + 1, - I2CD_POOL_RX_SIZE(bus->pool_ctrl), + trace_aspeed_i2c_bus_recv("BUF", i + 1, pool_rx_count, 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; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_pool_ctrl, RX_COUNT, i & 0xff); + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_BUFF_EN, 0); + } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN)) { + /* In new mode, clear how many bytes we RXed */ + if (aspeed_i2c_is_new_mode(bus->controller)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, 0); + } - while (bus->dma_len) { + while (bus->regs[reg_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, + trace_aspeed_i2c_bus_recv("DMA", bus->regs[reg_dma_len], + bus->regs[reg_dma_len], data); + result = address_space_write(&s->dram_as, bus->regs[reg_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); + __func__, bus->regs[reg_dma_addr]); return; } - bus->dma_addr++; - bus->dma_len--; + bus->regs[reg_dma_addr]++; + bus->regs[reg_dma_len]--; + /* In new mode, keep track of how many bytes we RXed */ + if (aspeed_i2c_is_new_mode(bus->controller)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, + ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN_STS, + RX_LEN) + 1); + } } - bus->cmd &= ~I2CD_RX_DMA_ENABLE; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_DMA_EN, 0); } 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; + trace_aspeed_i2c_bus_recv("BYTE", 1, 1, bus->regs[reg_byte_buf]); + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_byte_buf, RX_BUF, data); } } static void aspeed_i2c_handle_rx_cmd(AspeedI2CBus *bus) { + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); + aspeed_i2c_set_state(bus, I2CD_MRXD); aspeed_i2c_bus_recv(bus); - bus->intr_status |= I2CD_INTR_RX_DONE; - if (bus->cmd & I2CD_M_S_RX_CMD_LAST) { + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, RX_DONE, 1); + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_S_RX_CMD_LAST)) { i2c_nack(bus->bus); } - bus->cmd &= ~(I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST); + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_RX_CMD, 0); + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_S_RX_CMD_LAST, 0); aspeed_i2c_set_state(bus, I2CD_MACTIVE); } static uint8_t aspeed_i2c_get_addr(AspeedI2CBus *bus) { AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); - if (bus->cmd & I2CD_TX_BUFF_ENABLE) { + if (aspeed_i2c_bus_pkt_mode_en(bus)) { + return (ARRAY_FIELD_EX32(bus->regs, I2CM_CMD, PKT_DEV_ADDR) << 1) | + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_RX_CMD); + } + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { uint8_t *pool_base = aic->bus_pool_base(bus); return pool_base[0]; - } else if (bus->cmd & I2CD_TX_DMA_ENABLE) { + } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { uint8_t data; aspeed_i2c_dma_read(bus, &data); return data; } else { - return bus->buf; + return bus->regs[reg_byte_buf]; } } @@ -379,7 +391,11 @@ static bool aspeed_i2c_check_sram(AspeedI2CBus *bus) { AspeedI2CState *s = bus->controller; AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); - + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + bool dma_en = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN) || + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN) || + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN) || + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN); if (!aic->check_sram) { return true; } @@ -388,9 +404,7 @@ static bool aspeed_i2c_check_sram(AspeedI2CBus *bus) * 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))) { + if (!FIELD_EX32(s->ctrl_global, I2C_CTRL_GLOBAL, SRAM_EN) && dma_en) { qemu_log_mask(LOG_GUEST_ERROR, "%s: SRAM is not enabled\n", __func__); return false; } @@ -402,27 +416,31 @@ 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; + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); + uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN)) { + count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, TX_COUNT) + 1; + } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN)) { + count = bus->regs[reg_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" : ""); + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_START_CMD) ? "start|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN) ? "rxdma|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN) ? "txdma|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN) ? "rxbuf|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN) ? "txbuf|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_TX_CMD) ? "tx|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_RX_CMD) ? "rx|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_S_RX_CMD_LAST) ? "last|" : "", + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_STOP_CMD) ? "stop|" : ""); - trace_aspeed_i2c_bus_cmd(bus->cmd, cmd_flags, count, bus->intr_status); + trace_aspeed_i2c_bus_cmd(bus->regs[reg_cmd], cmd_flags, count, + bus->regs[reg_intr_sts]); } /* @@ -431,10 +449,9 @@ static void aspeed_i2c_bus_cmd_dump(AspeedI2CBus *bus) */ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) { - uint8_t pool_start = 0; - - bus->cmd &= ~0xFFFF; - bus->cmd |= value & 0xFFFF; + uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); if (!aspeed_i2c_check_sram(bus)) { return; @@ -444,7 +461,7 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) aspeed_i2c_bus_cmd_dump(bus); } - if (bus->cmd & I2CD_M_START_CMD) { + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_START_CMD)) { uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ? I2CD_MSTARTR : I2CD_MSTART; uint8_t addr; @@ -452,166 +469,359 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) aspeed_i2c_set_state(bus, state); 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; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_NAK, 1); + if (aspeed_i2c_bus_pkt_mode_en(bus)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_FAIL, 1); + } } else { - bus->intr_status |= I2CD_INTR_TX_ACK; + /* START doesn't set TX_ACK in packet mode */ + if (!aspeed_i2c_bus_pkt_mode_en(bus)) { + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_ACK, 1); + } } - bus->cmd &= ~I2CD_M_START_CMD; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_START_CMD, 0); - /* - * 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++; + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { + if (bus->regs[reg_dma_len] == 0) { + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); } - } 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; + } else if (!SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); } /* No slave found */ if (!i2c_bus_busy(bus->bus)) { + if (aspeed_i2c_bus_pkt_mode_en(bus)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_FAIL, 1); + ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_DONE, 1); + } return; } aspeed_i2c_set_state(bus, I2CD_MACTIVE); } - if (bus->cmd & I2CD_M_TX_CMD) { + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_TX_CMD)) { aspeed_i2c_set_state(bus, I2CD_MTXD); - if (aspeed_i2c_bus_send(bus, pool_start)) { - bus->intr_status |= (I2CD_INTR_TX_NAK); + if (aspeed_i2c_bus_send(bus)) { + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_NAK, 1); i2c_end_transfer(bus->bus); } else { - bus->intr_status |= I2CD_INTR_TX_ACK; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_ACK, 1); } - bus->cmd &= ~I2CD_M_TX_CMD; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); aspeed_i2c_set_state(bus, I2CD_MACTIVE); } - if ((bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST)) && - !(bus->intr_status & I2CD_INTR_RX_DONE)) { + if ((SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_RX_CMD) || + SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_S_RX_CMD_LAST)) && + !SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, RX_DONE)) { aspeed_i2c_handle_rx_cmd(bus); } - if (bus->cmd & I2CD_M_STOP_CMD) { + if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_STOP_CMD)) { if (!(aspeed_i2c_get_state(bus) & I2CD_MACTIVE)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: abnormal stop\n", __func__); - bus->intr_status |= I2CD_INTR_ABNORMAL; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, ABNORMAL, 1); + if (aspeed_i2c_bus_pkt_mode_en(bus)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_FAIL, 1); + } } else { aspeed_i2c_set_state(bus, I2CD_MSTOP); i2c_end_transfer(bus->bus); - bus->intr_status |= I2CD_INTR_NORMAL_STOP; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, NORMAL_STOP, 1); } - bus->cmd &= ~I2CD_M_STOP_CMD; + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_STOP_CMD, 0); aspeed_i2c_set_state(bus, I2CD_IDLE); + + i2c_schedule_pending_master(bus->bus); + } + + if (aspeed_i2c_bus_pkt_mode_en(bus)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_DONE, 1); } } -static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) +static void aspeed_i2c_bus_new_write(AspeedI2CBus *bus, hwaddr offset, + uint64_t value, unsigned size) +{ + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + bool handle_rx; + bool w1t; + + trace_aspeed_i2c_bus_write(bus->id, offset, size, value); + + switch (offset) { + case A_I2CC_FUN_CTRL: + bus->regs[R_I2CC_FUN_CTRL] = value; + break; + case A_I2CC_AC_TIMING: + bus->regs[R_I2CC_AC_TIMING] = value & 0x1ffff0ff; + break; + case A_I2CC_MS_TXRX_BYTE_BUF: + SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CC_MS_TXRX_BYTE_BUF, TX_BUF, + value); + break; + case A_I2CC_POOL_CTRL: + bus->regs[R_I2CC_POOL_CTRL] &= ~0xffffff; + bus->regs[R_I2CC_POOL_CTRL] |= (value & 0xffffff); + break; + case A_I2CM_INTR_CTRL: + bus->regs[R_I2CM_INTR_CTRL] = value & 0x0007f07f; + break; + case A_I2CM_INTR_STS: + handle_rx = SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CM_INTR_STS, RX_DONE) + && SHARED_FIELD_EX32(value, RX_DONE); + + /* In packet mode, clearing PKT_CMD_DONE clears other interrupts. */ + if (aspeed_i2c_bus_pkt_mode_en(bus) && + FIELD_EX32(value, I2CM_INTR_STS, PKT_CMD_DONE)) { + bus->regs[R_I2CM_INTR_STS] &= 0xf0001000; + if (!bus->regs[R_I2CM_INTR_STS]) { + bus->controller->intr_status &= ~(1 << bus->id); + qemu_irq_lower(aic->bus_get_irq(bus)); + } + aspeed_i2c_bus_raise_slave_interrupt(bus); + break; + } + bus->regs[R_I2CM_INTR_STS] &= ~(value & 0xf007f07f); + if (!bus->regs[R_I2CM_INTR_STS]) { + bus->controller->intr_status &= ~(1 << bus->id); + qemu_irq_lower(aic->bus_get_irq(bus)); + } + if (handle_rx && (SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CM_CMD, + M_RX_CMD) || + SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CM_CMD, + M_S_RX_CMD_LAST))) { + aspeed_i2c_handle_rx_cmd(bus); + aspeed_i2c_bus_raise_interrupt(bus); + } + break; + case A_I2CM_CMD: + if (!aspeed_i2c_bus_is_enabled(bus)) { + break; + } + + if (!aspeed_i2c_bus_is_master(bus)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Master mode is not enabled\n", + __func__); + break; + } + + if (!aic->has_dma && + (SHARED_FIELD_EX32(value, RX_DMA_EN) || + SHARED_FIELD_EX32(value, TX_DMA_EN))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); + break; + } + + if (bus->regs[R_I2CM_INTR_STS] & 0xffff0000) { + qemu_log_mask(LOG_UNIMP, "%s: Packet mode is not implemented\n", + __func__); + break; + } + + value &= 0xff0ffbfb; + if (ARRAY_FIELD_EX32(bus->regs, I2CM_CMD, W1_CTRL)) { + bus->regs[R_I2CM_CMD] |= value; + } else { + bus->regs[R_I2CM_CMD] = value; + } + + aspeed_i2c_bus_handle_cmd(bus, value); + aspeed_i2c_bus_raise_interrupt(bus); + break; + case A_I2CM_DMA_TX_ADDR: + bus->regs[R_I2CM_DMA_TX_ADDR] = FIELD_EX32(value, I2CM_DMA_TX_ADDR, + ADDR); + bus->regs[R_I2CC_DMA_ADDR] = FIELD_EX32(value, I2CM_DMA_TX_ADDR, ADDR); + bus->regs[R_I2CC_DMA_LEN] = ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, + TX_BUF_LEN) + 1; + break; + case A_I2CM_DMA_RX_ADDR: + bus->regs[R_I2CM_DMA_RX_ADDR] = FIELD_EX32(value, I2CM_DMA_RX_ADDR, + ADDR); + bus->regs[R_I2CC_DMA_ADDR] = FIELD_EX32(value, I2CM_DMA_RX_ADDR, ADDR); + bus->regs[R_I2CC_DMA_LEN] = ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, + RX_BUF_LEN) + 1; + break; + case A_I2CM_DMA_LEN: + w1t = FIELD_EX32(value, I2CM_DMA_LEN, RX_BUF_LEN_W1T) || + FIELD_EX32(value, I2CM_DMA_LEN, TX_BUF_LEN_W1T); + /* If none of the w1t bits are set, just write to the reg as normal. */ + if (!w1t) { + bus->regs[R_I2CM_DMA_LEN] = value; + break; + } + if (FIELD_EX32(value, I2CM_DMA_LEN, RX_BUF_LEN_W1T)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN, RX_BUF_LEN, + FIELD_EX32(value, I2CM_DMA_LEN, RX_BUF_LEN)); + } + if (FIELD_EX32(value, I2CM_DMA_LEN, TX_BUF_LEN_W1T)) { + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN, TX_BUF_LEN, + FIELD_EX32(value, I2CM_DMA_LEN, TX_BUF_LEN)); + } + break; + case A_I2CM_DMA_LEN_STS: + /* Writes clear to 0 */ + bus->regs[R_I2CM_DMA_LEN_STS] = 0; + break; + case A_I2CC_DMA_ADDR: + case A_I2CC_DMA_LEN: + /* RO */ + break; + case A_I2CS_DEV_ADDR: + bus->regs[R_I2CS_DEV_ADDR] = value; + break; + case A_I2CS_DMA_RX_ADDR: + bus->regs[R_I2CS_DMA_RX_ADDR] = value; + break; + case A_I2CS_DMA_LEN: + assert(FIELD_EX32(value, I2CS_DMA_LEN, TX_BUF_LEN) == 0); + if (FIELD_EX32(value, I2CS_DMA_LEN, RX_BUF_LEN_W1T)) { + ARRAY_FIELD_DP32(bus->regs, I2CS_DMA_LEN, RX_BUF_LEN, + FIELD_EX32(value, I2CS_DMA_LEN, RX_BUF_LEN)); + } else { + bus->regs[R_I2CS_DMA_LEN] = value; + } + break; + case A_I2CS_CMD: + if (FIELD_EX32(value, I2CS_CMD, W1_CTRL)) { + bus->regs[R_I2CS_CMD] |= value; + } else { + bus->regs[R_I2CS_CMD] = value; + } + i2c_slave_set_address(bus->slave, bus->regs[R_I2CS_DEV_ADDR]); + break; + case A_I2CS_INTR_CTRL: + bus->regs[R_I2CS_INTR_CTRL] = value; + break; + + case A_I2CS_INTR_STS: + if (ARRAY_FIELD_EX32(bus->regs, I2CS_INTR_CTRL, PKT_CMD_DONE)) { + if (ARRAY_FIELD_EX32(bus->regs, I2CS_INTR_STS, PKT_CMD_DONE) && + FIELD_EX32(value, I2CS_INTR_STS, PKT_CMD_DONE)) { + bus->regs[R_I2CS_INTR_STS] &= 0xfffc0000; + } + } else { + bus->regs[R_I2CS_INTR_STS] &= ~value; + } + if (!bus->regs[R_I2CS_INTR_STS]) { + bus->controller->intr_status &= ~(1 << bus->id); + qemu_irq_lower(aic->bus_get_irq(bus)); + } + aspeed_i2c_bus_raise_interrupt(bus); + break; + case A_I2CS_DMA_LEN_STS: + bus->regs[R_I2CS_DMA_LEN_STS] = 0; + break; + case A_I2CS_DMA_TX_ADDR: + qemu_log_mask(LOG_UNIMP, "%s: Slave mode DMA TX is not implemented\n", + __func__); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } +} + +static void aspeed_i2c_bus_old_write(AspeedI2CBus *bus, hwaddr offset, + uint64_t value, unsigned size) { - AspeedI2CBus *bus = opaque; 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) { - qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", - __func__); - break; + case A_I2CD_FUN_CTRL: + if (SHARED_FIELD_EX32(value, SLAVE_EN)) { + i2c_slave_set_address(bus->slave, bus->regs[R_I2CD_DEV_ADDR]); } - bus->ctrl = value & 0x0071C3FF; + bus->regs[R_I2CD_FUN_CTRL] = value & 0x0071C3FF; break; - case I2CD_AC_TIMING_REG1: - bus->timing[0] = value & 0xFFFFF0F; + case A_I2CD_AC_TIMING1: + bus->regs[R_I2CD_AC_TIMING1] = value & 0xFFFFF0F; break; - case I2CD_AC_TIMING_REG2: - bus->timing[1] = value & 0x7; + case A_I2CD_AC_TIMING2: + bus->regs[R_I2CD_AC_TIMING2] = value & 0x7; break; - case I2CD_INTR_CTRL_REG: - bus->intr_ctrl = value & 0x7FFF; + case A_I2CD_INTR_CTRL: + bus->regs[R_I2CD_INTR_CTRL] = value & 0x7FFF; break; - case I2CD_INTR_STS_REG: - handle_rx = (bus->intr_status & I2CD_INTR_RX_DONE) && - (value & I2CD_INTR_RX_DONE); - bus->intr_status &= ~(value & 0x7FFF); - if (!bus->intr_status) { + case A_I2CD_INTR_STS: + handle_rx = SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_INTR_STS, RX_DONE) + && SHARED_FIELD_EX32(value, RX_DONE); + bus->regs[R_I2CD_INTR_STS] &= ~(value & 0x7FFF); + if (!bus->regs[R_I2CD_INTR_STS]) { bus->controller->intr_status &= ~(1 << bus->id); qemu_irq_lower(aic->bus_get_irq(bus)); } - if (handle_rx && (bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST))) { - aspeed_i2c_handle_rx_cmd(bus); - aspeed_i2c_bus_raise_interrupt(bus); + if (handle_rx) { + if (SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD, M_RX_CMD) || + SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD, + M_S_RX_CMD_LAST)) { + aspeed_i2c_handle_rx_cmd(bus); + aspeed_i2c_bus_raise_interrupt(bus); + } else if (aspeed_i2c_get_state(bus) == I2CD_STXD) { + i2c_ack(bus->bus); + } } break; - case I2CD_DEV_ADDR_REG: - qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", - __func__); + case A_I2CD_DEV_ADDR: + bus->regs[R_I2CD_DEV_ADDR] = value; break; - case I2CD_POOL_CTRL_REG: - bus->pool_ctrl &= ~0xffffff; - bus->pool_ctrl |= (value & 0xffffff); + case A_I2CD_POOL_CTRL: + bus->regs[R_I2CD_POOL_CTRL] &= ~0xffffff; + bus->regs[R_I2CD_POOL_CTRL] |= (value & 0xffffff); break; - case I2CD_BYTE_BUF_REG: - bus->buf = (value & I2CD_BYTE_BUF_TX_MASK) << I2CD_BYTE_BUF_TX_SHIFT; + case A_I2CD_BYTE_BUF: + SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CD_BYTE_BUF, TX_BUF, value); break; - case I2CD_CMD_REG: + case A_I2CD_CMD: if (!aspeed_i2c_bus_is_enabled(bus)) { break; } if (!aspeed_i2c_bus_is_master(bus)) { - qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", + qemu_log_mask(LOG_GUEST_ERROR, "%s: Master mode is not enabled\n", __func__); break; } if (!aic->has_dma && - value & (I2CD_RX_DMA_ENABLE | I2CD_TX_DMA_ENABLE)) { + (SHARED_FIELD_EX32(value, RX_DMA_EN) || + SHARED_FIELD_EX32(value, TX_DMA_EN))) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); break; } + bus->regs[R_I2CD_CMD] &= ~0xFFFF; + bus->regs[R_I2CD_CMD] |= value & 0xFFFF; + aspeed_i2c_bus_handle_cmd(bus, value); aspeed_i2c_bus_raise_interrupt(bus); break; - case I2CD_DMA_ADDR: + case A_I2CD_DMA_ADDR: if (!aic->has_dma) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); break; } - bus->dma_addr = value & 0x3ffffffc; + bus->regs[R_I2CD_DMA_ADDR] = value & 0x3ffffffc; break; - case I2CD_DMA_LEN: + case A_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) { + bus->regs[R_I2CD_DMA_LEN] = value & 0xfff; + if (!bus->regs[R_I2CD_DMA_LEN]) { qemu_log_mask(LOG_UNIMP, "%s: invalid DMA length\n", __func__); } break; @@ -622,16 +832,34 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, } } +static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + AspeedI2CBus *bus = opaque; + if (aspeed_i2c_is_new_mode(bus->controller)) { + aspeed_i2c_bus_new_write(bus, offset, value, size); + } else { + aspeed_i2c_bus_old_write(bus, offset, value, size); + } +} + static uint64_t aspeed_i2c_ctrl_read(void *opaque, hwaddr offset, unsigned size) { AspeedI2CState *s = opaque; switch (offset) { - case I2C_CTRL_STATUS: + case A_I2C_CTRL_STATUS: return s->intr_status; - case I2C_CTRL_GLOBAL: + case A_I2C_CTRL_GLOBAL: return s->ctrl_global; + case A_I2C_CTRL_NEW_CLK_DIVIDER: + if (aspeed_i2c_is_new_mode(s)) { + return s->new_clk_divider; + } + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -647,10 +875,18 @@ static void aspeed_i2c_ctrl_write(void *opaque, hwaddr offset, AspeedI2CState *s = opaque; switch (offset) { - case I2C_CTRL_GLOBAL: + case A_I2C_CTRL_GLOBAL: s->ctrl_global = value; break; - case I2C_CTRL_STATUS: + case A_I2C_CTRL_NEW_CLK_DIVIDER: + if (aspeed_i2c_is_new_mode(s)) { + s->new_clk_divider = value; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx + "\n", __func__, offset); + } + break; + case A_I2C_CTRL_STATUS: default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -707,19 +943,10 @@ static const MemoryRegionOps aspeed_i2c_pool_ops = { static const VMStateDescription aspeed_i2c_bus_vmstate = { .name = TYPE_ASPEED_I2C, - .version_id = 3, - .minimum_version_id = 3, - .fields = (VMStateField[]) { - VMSTATE_UINT8(id, AspeedI2CBus), - VMSTATE_UINT32(ctrl, AspeedI2CBus), - VMSTATE_UINT32_ARRAY(timing, AspeedI2CBus, 2), - VMSTATE_UINT32(intr_ctrl, AspeedI2CBus), - 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), + .version_id = 5, + .minimum_version_id = 5, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AspeedI2CBus, ASPEED_I2C_NEW_NUM_REG), VMSTATE_END_OF_LIST() } }; @@ -728,7 +955,7 @@ static const VMStateDescription aspeed_i2c_vmstate = { .name = TYPE_ASPEED_I2C, .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(intr_status, AspeedI2CState), VMSTATE_STRUCT_ARRAY(busses, AspeedI2CState, ASPEED_I2C_NR_BUSSES, 1, aspeed_i2c_bus_vmstate, @@ -852,16 +1079,134 @@ static const TypeInfo aspeed_i2c_info = { .abstract = true, }; +static int aspeed_i2c_bus_new_slave_event(AspeedI2CBus *bus, + enum i2c_event event) +{ + switch (event) { + case I2C_START_SEND_ASYNC: + if (!SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CS_CMD, RX_DMA_EN)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Slave mode RX DMA is not enabled\n", __func__); + return -1; + } + ARRAY_FIELD_DP32(bus->regs, I2CS_DMA_LEN_STS, RX_LEN, 0); + bus->regs[R_I2CC_DMA_ADDR] = + ARRAY_FIELD_EX32(bus->regs, I2CS_DMA_RX_ADDR, ADDR); + bus->regs[R_I2CC_DMA_LEN] = + ARRAY_FIELD_EX32(bus->regs, I2CS_DMA_LEN, RX_BUF_LEN) + 1; + i2c_ack(bus->bus); + break; + case I2C_FINISH: + ARRAY_FIELD_DP32(bus->regs, I2CS_INTR_STS, PKT_CMD_DONE, 1); + ARRAY_FIELD_DP32(bus->regs, I2CS_INTR_STS, SLAVE_ADDR_RX_MATCH, 1); + SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CS_INTR_STS, NORMAL_STOP, 1); + SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CS_INTR_STS, RX_DONE, 1); + aspeed_i2c_bus_raise_slave_interrupt(bus); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: i2c event %d unimplemented\n", + __func__, event); + return -1; + } + + return 0; +} + +static int aspeed_i2c_bus_slave_event(I2CSlave *slave, enum i2c_event event) +{ + BusState *qbus = qdev_get_parent_bus(DEVICE(slave)); + AspeedI2CBus *bus = ASPEED_I2C_BUS(qbus->parent); + uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); + uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); + uint32_t reg_dev_addr = aspeed_i2c_bus_dev_addr_offset(bus); + uint32_t dev_addr = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_dev_addr, + SLAVE_DEV_ADDR1); + + if (aspeed_i2c_is_new_mode(bus->controller)) { + return aspeed_i2c_bus_new_slave_event(bus, event); + } + + switch (event) { + case I2C_START_SEND_ASYNC: + /* Bit[0] == 0 indicates "send". */ + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_byte_buf, RX_BUF, dev_addr << 1); + + ARRAY_FIELD_DP32(bus->regs, I2CD_INTR_STS, SLAVE_ADDR_RX_MATCH, 1); + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, RX_DONE, 1); + + aspeed_i2c_set_state(bus, I2CD_STXD); + + break; + + case I2C_FINISH: + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, NORMAL_STOP, 1); + + aspeed_i2c_set_state(bus, I2CD_IDLE); + + break; + + default: + return -1; + } + + aspeed_i2c_bus_raise_interrupt(bus); + + return 0; +} + +static void aspeed_i2c_bus_new_slave_send_async(AspeedI2CBus *bus, uint8_t data) +{ + assert(address_space_write(&bus->controller->dram_as, + bus->regs[R_I2CC_DMA_ADDR], + MEMTXATTRS_UNSPECIFIED, &data, 1) == MEMTX_OK); + + bus->regs[R_I2CC_DMA_ADDR]++; + bus->regs[R_I2CC_DMA_LEN]--; + ARRAY_FIELD_DP32(bus->regs, I2CS_DMA_LEN_STS, RX_LEN, + ARRAY_FIELD_EX32(bus->regs, I2CS_DMA_LEN_STS, RX_LEN) + 1); + i2c_ack(bus->bus); +} + +static void aspeed_i2c_bus_slave_send_async(I2CSlave *slave, uint8_t data) +{ + BusState *qbus = qdev_get_parent_bus(DEVICE(slave)); + AspeedI2CBus *bus = ASPEED_I2C_BUS(qbus->parent); + uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); + uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); + + if (aspeed_i2c_is_new_mode(bus->controller)) { + return aspeed_i2c_bus_new_slave_send_async(bus, data); + } + + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_byte_buf, RX_BUF, data); + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, RX_DONE, 1); + + aspeed_i2c_bus_raise_interrupt(bus); +} + +static void aspeed_i2c_bus_slave_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); + + dc->desc = "Aspeed I2C Bus Slave"; + + sc->event = aspeed_i2c_bus_slave_event; + sc->send_async = aspeed_i2c_bus_slave_send_async; +} + +static const TypeInfo aspeed_i2c_bus_slave_info = { + .name = TYPE_ASPEED_I2C_BUS_SLAVE, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(AspeedI2CBusSlave), + .class_init = aspeed_i2c_bus_slave_class_init, +}; + static void aspeed_i2c_bus_reset(DeviceState *dev) { AspeedI2CBus *s = ASPEED_I2C_BUS(dev); - s->intr_ctrl = 0; - s->intr_status = 0; - s->cmd = 0; - s->buf = 0; - s->dma_addr = 0; - s->dma_len = 0; + memset(s->regs, 0, sizeof(s->regs)); i2c_end_transfer(s->bus); } @@ -881,6 +1226,8 @@ static void aspeed_i2c_bus_realize(DeviceState *dev, Error **errp) sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); s->bus = i2c_init_bus(dev, name); + s->slave = i2c_slave_create_simple(s->bus, TYPE_ASPEED_I2C_BUS_SLAVE, + 0xff); memory_region_init_io(&s->mr, OBJECT(s), &aspeed_i2c_bus_ops, s, name, aic->reg_size); @@ -919,9 +1266,10 @@ static qemu_irq aspeed_2400_i2c_bus_get_irq(AspeedI2CBus *bus) 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]; + &bus->controller->pool[ARRAY_FIELD_EX32(bus->regs, I2CD_FUN_CTRL, + POOL_PAGE_SEL) * 0x100]; - return &pool_page[I2CD_POOL_OFFSET(bus->pool_ctrl)]; + return &pool_page[ARRAY_FIELD_EX32(bus->regs, I2CD_POOL_CTRL, OFFSET)]; } static void aspeed_2400_i2c_class_init(ObjectClass *klass, void *data) @@ -1013,13 +1361,38 @@ static const TypeInfo aspeed_2600_i2c_info = { .class_init = aspeed_2600_i2c_class_init, }; +static void aspeed_1030_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); + + dc->desc = "ASPEED 1030 I2C Controller"; + + aic->num_busses = 14; + 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_1030_i2c_info = { + .name = TYPE_ASPEED_1030_I2C, + .parent = TYPE_ASPEED_I2C, + .class_init = aspeed_1030_i2c_class_init, +}; + static void aspeed_i2c_register_types(void) { type_register_static(&aspeed_i2c_bus_info); + type_register_static(&aspeed_i2c_bus_slave_info); type_register_static(&aspeed_i2c_info); type_register_static(&aspeed_2400_i2c_info); type_register_static(&aspeed_2500_i2c_info); type_register_static(&aspeed_2600_i2c_info); + type_register_static(&aspeed_1030_i2c_info); } type_init(aspeed_i2c_register_types) diff --git a/hw/i2c/bcm2835_i2c.c b/hw/i2c/bcm2835_i2c.c new file mode 100644 index 0000000000..20ec46eeab --- /dev/null +++ b/hw/i2c/bcm2835_i2c.c @@ -0,0 +1,282 @@ +/* + * Broadcom Serial Controller (BSC) + * + * Copyright (c) 2024 Rayhan Faizel + * + * 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 "qemu/log.h" +#include "hw/i2c/bcm2835_i2c.h" +#include "hw/irq.h" +#include "migration/vmstate.h" + +static void bcm2835_i2c_update_interrupt(BCM2835I2CState *s) +{ + int do_interrupt = 0; + /* Interrupt on RXR (Needs reading) */ + if (s->c & BCM2835_I2C_C_INTR && s->s & BCM2835_I2C_S_RXR) { + do_interrupt = 1; + } + + /* Interrupt on TXW (Needs writing) */ + if (s->c & BCM2835_I2C_C_INTT && s->s & BCM2835_I2C_S_TXW) { + do_interrupt = 1; + } + + /* Interrupt on DONE (Transfer complete) */ + if (s->c & BCM2835_I2C_C_INTD && s->s & BCM2835_I2C_S_DONE) { + do_interrupt = 1; + } + qemu_set_irq(s->irq, do_interrupt); +} + +static void bcm2835_i2c_begin_transfer(BCM2835I2CState *s) +{ + int direction = s->c & BCM2835_I2C_C_READ; + if (i2c_start_transfer(s->bus, s->a, direction)) { + s->s |= BCM2835_I2C_S_ERR; + } + s->s |= BCM2835_I2C_S_TA; + + if (direction) { + s->s |= BCM2835_I2C_S_RXR | BCM2835_I2C_S_RXD; + } else { + s->s |= BCM2835_I2C_S_TXW; + } +} + +static void bcm2835_i2c_finish_transfer(BCM2835I2CState *s) +{ + /* + * STOP is sent when DLEN counts down to zero. + * + * https://github.com/torvalds/linux/blob/v6.7/drivers/i2c/busses/i2c-bcm2835.c#L223-L261 + * It is possible to initiate repeated starts on real hardware. + * However, this requires sending another ST request before the bytes in + * TX FIFO are shifted out. + * + * This is not emulated currently. + */ + i2c_end_transfer(s->bus); + s->s |= BCM2835_I2C_S_DONE; + + /* Ensure RXD is cleared, otherwise the driver registers an error */ + s->s &= ~(BCM2835_I2C_S_TA | BCM2835_I2C_S_RXR | + BCM2835_I2C_S_TXW | BCM2835_I2C_S_RXD); +} + +static uint64_t bcm2835_i2c_read(void *opaque, hwaddr addr, unsigned size) +{ + BCM2835I2CState *s = opaque; + uint32_t readval = 0; + + switch (addr) { + case BCM2835_I2C_C: + readval = s->c; + break; + case BCM2835_I2C_S: + readval = s->s; + break; + case BCM2835_I2C_DLEN: + readval = s->dlen; + break; + case BCM2835_I2C_A: + readval = s->a; + break; + case BCM2835_I2C_FIFO: + /* We receive I2C messages directly instead of using FIFOs */ + if (s->s & BCM2835_I2C_S_TA) { + readval = i2c_recv(s->bus); + s->dlen -= 1; + + if (s->dlen == 0) { + bcm2835_i2c_finish_transfer(s); + } + } + bcm2835_i2c_update_interrupt(s); + break; + case BCM2835_I2C_DIV: + readval = s->div; + break; + case BCM2835_I2C_DEL: + readval = s->del; + break; + case BCM2835_I2C_CLKT: + readval = s->clkt; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); + } + + return readval; +} + +static void bcm2835_i2c_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + BCM2835I2CState *s = opaque; + uint32_t writeval = value; + + switch (addr) { + case BCM2835_I2C_C: + /* ST is a one-shot operation; it must read back as 0 */ + s->c = writeval & ~BCM2835_I2C_C_ST; + + /* Start transfer */ + if (writeval & (BCM2835_I2C_C_ST | BCM2835_I2C_C_I2CEN)) { + bcm2835_i2c_begin_transfer(s); + /* + * Handle special case where transfer starts with zero data length. + * Required for zero length i2c quick messages to work. + */ + if (s->dlen == 0) { + bcm2835_i2c_finish_transfer(s); + } + } + + bcm2835_i2c_update_interrupt(s); + break; + case BCM2835_I2C_S: + if (writeval & BCM2835_I2C_S_DONE && s->s & BCM2835_I2C_S_DONE) { + /* When DONE is cleared, DLEN should read last written value. */ + s->dlen = s->last_dlen; + } + + /* Clear DONE, CLKT and ERR by writing 1 */ + s->s &= ~(writeval & (BCM2835_I2C_S_DONE | + BCM2835_I2C_S_ERR | BCM2835_I2C_S_CLKT)); + break; + case BCM2835_I2C_DLEN: + s->dlen = writeval; + s->last_dlen = writeval; + break; + case BCM2835_I2C_A: + s->a = writeval; + break; + case BCM2835_I2C_FIFO: + /* We send I2C messages directly instead of using FIFOs */ + if (s->s & BCM2835_I2C_S_TA) { + if (s->s & BCM2835_I2C_S_TXD) { + if (!i2c_send(s->bus, writeval & 0xff)) { + s->dlen -= 1; + } else { + s->s |= BCM2835_I2C_S_ERR; + } + } + + if (s->dlen == 0) { + bcm2835_i2c_finish_transfer(s); + } + } + bcm2835_i2c_update_interrupt(s); + break; + case BCM2835_I2C_DIV: + s->div = writeval; + break; + case BCM2835_I2C_DEL: + s->del = writeval; + break; + case BCM2835_I2C_CLKT: + s->clkt = writeval; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); + } +} + +static const MemoryRegionOps bcm2835_i2c_ops = { + .read = bcm2835_i2c_read, + .write = bcm2835_i2c_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void bcm2835_i2c_realize(DeviceState *dev, Error **errp) +{ + BCM2835I2CState *s = BCM2835_I2C(dev); + s->bus = i2c_init_bus(dev, NULL); + + memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_i2c_ops, s, + TYPE_BCM2835_I2C, 0x24); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); +} + +static void bcm2835_i2c_reset(DeviceState *dev) +{ + BCM2835I2CState *s = BCM2835_I2C(dev); + + /* Reset values according to BCM2835 Peripheral Documentation */ + s->c = 0x0; + s->s = BCM2835_I2C_S_TXD | BCM2835_I2C_S_TXE; + s->dlen = 0x0; + s->a = 0x0; + s->div = 0x5dc; + s->del = 0x00300030; + s->clkt = 0x40; +} + +static const VMStateDescription vmstate_bcm2835_i2c = { + .name = TYPE_BCM2835_I2C, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(c, BCM2835I2CState), + VMSTATE_UINT32(s, BCM2835I2CState), + VMSTATE_UINT32(dlen, BCM2835I2CState), + VMSTATE_UINT32(a, BCM2835I2CState), + VMSTATE_UINT32(div, BCM2835I2CState), + VMSTATE_UINT32(del, BCM2835I2CState), + VMSTATE_UINT32(clkt, BCM2835I2CState), + VMSTATE_UINT32(last_dlen, BCM2835I2CState), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = bcm2835_i2c_reset; + dc->realize = bcm2835_i2c_realize; + dc->vmsd = &vmstate_bcm2835_i2c; +} + +static const TypeInfo bcm2835_i2c_info = { + .name = TYPE_BCM2835_I2C, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835I2CState), + .class_init = bcm2835_i2c_class_init, +}; + +static void bcm2835_i2c_register_types(void) +{ + type_register_static(&bcm2835_i2c_info); +} + +type_init(bcm2835_i2c_register_types) diff --git a/hw/i2c/bitbang_i2c.c b/hw/i2c/bitbang_i2c.c index e9a0612a04..de5f5aacf5 100644 --- a/hw/i2c/bitbang_i2c.c +++ b/hw/i2c/bitbang_i2c.c @@ -16,34 +16,61 @@ #include "hw/sysbus.h" #include "qemu/module.h" #include "qom/object.h" +#include "trace.h" -//#define DEBUG_BITBANG_I2C -#ifdef DEBUG_BITBANG_I2C -#define DPRINTF(fmt, ...) \ -do { printf("bitbang_i2c: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif +/* bitbang_i2c_state enum to name */ +static const char * const sname[] = { +#define NAME(e) [e] = stringify(e) + NAME(STOPPED), + [SENDING_BIT7] = "SENDING_BIT7 (START)", + NAME(SENDING_BIT6), + NAME(SENDING_BIT5), + NAME(SENDING_BIT4), + NAME(SENDING_BIT3), + NAME(SENDING_BIT2), + NAME(SENDING_BIT1), + NAME(SENDING_BIT0), + NAME(WAITING_FOR_ACK), + [RECEIVING_BIT7] = "RECEIVING_BIT7 (ACK)", + NAME(RECEIVING_BIT6), + NAME(RECEIVING_BIT5), + NAME(RECEIVING_BIT4), + NAME(RECEIVING_BIT3), + NAME(RECEIVING_BIT2), + NAME(RECEIVING_BIT1), + NAME(RECEIVING_BIT0), + NAME(SENDING_ACK), + NAME(SENT_NACK) +#undef NAME +}; + +static void bitbang_i2c_set_state(bitbang_i2c_interface *i2c, + bitbang_i2c_state state) +{ + trace_bitbang_i2c_state(sname[i2c->state], sname[state]); + i2c->state = state; +} static void bitbang_i2c_enter_stop(bitbang_i2c_interface *i2c) { - DPRINTF("STOP\n"); if (i2c->current_addr >= 0) i2c_end_transfer(i2c->bus); i2c->current_addr = -1; - i2c->state = STOPPED; + bitbang_i2c_set_state(i2c, STOPPED); } /* Set device data pin. */ static int bitbang_i2c_ret(bitbang_i2c_interface *i2c, int level) { + trace_bitbang_i2c_data(i2c->last_clock, i2c->last_data, + i2c->device_out, level); i2c->device_out = level; - //DPRINTF("%d %d %d\n", i2c->last_clock, i2c->last_data, i2c->device_out); + return level & i2c->last_data; } -/* Leave device data pin unodified. */ +/* Leave device data pin unmodified. */ static int bitbang_i2c_nop(bitbang_i2c_interface *i2c) { return bitbang_i2c_ret(i2c, i2c->device_out); @@ -67,9 +94,8 @@ int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level) return bitbang_i2c_nop(i2c); } if (level == 0) { - DPRINTF("START\n"); /* START condition. */ - i2c->state = SENDING_BIT7; + bitbang_i2c_set_state(i2c, SENDING_BIT7); i2c->current_addr = -1; } else { /* STOP condition. */ @@ -96,7 +122,7 @@ int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level) case SENDING_BIT7 ... SENDING_BIT0: i2c->buffer = (i2c->buffer << 1) | data; /* will end up in WAITING_FOR_ACK */ - i2c->state++; + bitbang_i2c_set_state(i2c, i2c->state + 1); return bitbang_i2c_ret(i2c, 1); case WAITING_FOR_ACK: @@ -105,47 +131,45 @@ int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level) if (i2c->current_addr < 0) { i2c->current_addr = i2c->buffer; - DPRINTF("Address 0x%02x\n", i2c->current_addr); + trace_bitbang_i2c_addr(i2c->current_addr); ret = i2c_start_transfer(i2c->bus, i2c->current_addr >> 1, i2c->current_addr & 1); } else { - DPRINTF("Sent 0x%02x\n", i2c->buffer); + trace_bitbang_i2c_send(i2c->buffer); ret = i2c_send(i2c->bus, i2c->buffer); } if (ret) { /* NACK (either addressing a nonexistent device, or the * device we were sending to decided to NACK us). */ - DPRINTF("Got NACK\n"); + bitbang_i2c_set_state(i2c, SENT_NACK); bitbang_i2c_enter_stop(i2c); return bitbang_i2c_ret(i2c, 1); } if (i2c->current_addr & 1) { - i2c->state = RECEIVING_BIT7; + bitbang_i2c_set_state(i2c, RECEIVING_BIT7); } else { - i2c->state = SENDING_BIT7; + bitbang_i2c_set_state(i2c, SENDING_BIT7); } return bitbang_i2c_ret(i2c, 0); } case RECEIVING_BIT7: i2c->buffer = i2c_recv(i2c->bus); - DPRINTF("RX byte 0x%02x\n", i2c->buffer); + trace_bitbang_i2c_recv(i2c->buffer); /* Fall through... */ case RECEIVING_BIT6 ... RECEIVING_BIT0: data = i2c->buffer >> 7; /* will end up in SENDING_ACK */ - i2c->state++; + bitbang_i2c_set_state(i2c, i2c->state + 1); i2c->buffer <<= 1; return bitbang_i2c_ret(i2c, data); case SENDING_ACK: - i2c->state = RECEIVING_BIT7; if (data != 0) { - DPRINTF("NACKED\n"); - i2c->state = SENT_NACK; + bitbang_i2c_set_state(i2c, SENT_NACK); i2c_nack(i2c->bus); } else { - DPRINTF("ACKED\n"); + bitbang_i2c_set_state(i2c, RECEIVING_BIT7); } return bitbang_i2c_ret(i2c, 1); } @@ -162,13 +186,13 @@ void bitbang_i2c_init(bitbang_i2c_interface *s, I2CBus *bus) /* GPIO interface. */ -#define TYPE_GPIO_I2C "gpio_i2c" OBJECT_DECLARE_SIMPLE_TYPE(GPIOI2CState, GPIO_I2C) struct GPIOI2CState { + /*< private >*/ SysBusDevice parent_obj; + /*< public >*/ - MemoryRegion dummy_iomem; bitbang_i2c_interface bitbang; int last_level; qemu_irq out; @@ -189,12 +213,8 @@ static void gpio_i2c_init(Object *obj) { DeviceState *dev = DEVICE(obj); GPIOI2CState *s = GPIO_I2C(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); I2CBus *bus; - memory_region_init(&s->dummy_iomem, obj, "gpio_i2c", 0); - sysbus_init_mmio(sbd, &s->dummy_iomem); - bus = i2c_init_bus(dev, "i2c"); bitbang_i2c_init(&s->bitbang, bus); diff --git a/hw/i2c/core.c b/hw/i2c/core.c index d0cb2d32fa..4cf30b2c86 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -13,6 +13,7 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/module.h" +#include "qemu/main-loop.h" #include "trace.h" #define I2C_BROADCAST 0x00 @@ -49,7 +50,7 @@ static const VMStateDescription vmstate_i2c_bus = { .version_id = 1, .minimum_version_id = 1, .pre_save = i2c_bus_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(saved_address, I2CBus), VMSTATE_END_OF_LIST() } @@ -62,7 +63,8 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name) bus = I2C_BUS(qbus_new(TYPE_I2C_BUS, parent, name)); QLIST_INIT(&bus->current_devs); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_i2c_bus, bus); + QSIMPLEQ_INIT(&bus->pending_masters); + vmstate_register_any(NULL, &vmstate_i2c_bus, bus); return bus; } @@ -74,7 +76,7 @@ void i2c_slave_set_address(I2CSlave *dev, uint8_t address) /* Return nonzero if bus is busy. */ int i2c_bus_busy(I2CBus *bus) { - return !QLIST_EMPTY(&bus->current_devs); + return !QLIST_EMPTY(&bus->current_devs) || bus->bh; } bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, @@ -159,7 +161,8 @@ static int i2c_do_start_transfer(I2CBus *bus, uint8_t address, start condition. */ if (sc->event) { - trace_i2c_event("start", s->address); + trace_i2c_event(event == I2C_START_SEND ? "start" : "start_async", + s->address); rv = sc->event(s, event); if (rv && !bus->broadcast) { if (bus_scanned) { @@ -180,6 +183,43 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv) : I2C_START_SEND); } +void i2c_bus_master(I2CBus *bus, QEMUBH *bh) +{ + I2CPendingMaster *node = g_new(struct I2CPendingMaster, 1); + node->bh = bh; + + QSIMPLEQ_INSERT_TAIL(&bus->pending_masters, node, entry); +} + +void i2c_schedule_pending_master(I2CBus *bus) +{ + I2CPendingMaster *node; + + if (i2c_bus_busy(bus)) { + /* someone is already controlling the bus; wait for it to release it */ + return; + } + + if (QSIMPLEQ_EMPTY(&bus->pending_masters)) { + return; + } + + node = QSIMPLEQ_FIRST(&bus->pending_masters); + bus->bh = node->bh; + + QSIMPLEQ_REMOVE_HEAD(&bus->pending_masters, entry); + g_free(node); + + qemu_bh_schedule(bus->bh); +} + +void i2c_bus_release(I2CBus *bus) +{ + bus->bh = NULL; + + i2c_schedule_pending_master(bus); +} + int i2c_start_recv(I2CBus *bus, uint8_t address) { return i2c_do_start_transfer(bus, address, I2C_START_RECV); @@ -190,6 +230,11 @@ int i2c_start_send(I2CBus *bus, uint8_t address) return i2c_do_start_transfer(bus, address, I2C_START_SEND); } +int i2c_start_send_async(I2CBus *bus, uint8_t address) +{ + return i2c_do_start_transfer(bus, address, I2C_START_SEND_ASYNC); +} + void i2c_end_transfer(I2CBus *bus) { I2CSlaveClass *sc; @@ -229,6 +274,23 @@ int i2c_send(I2CBus *bus, uint8_t data) return ret ? -1 : 0; } +int i2c_send_async(I2CBus *bus, uint8_t data) +{ + I2CNode *node = QLIST_FIRST(&bus->current_devs); + I2CSlave *slave = node->elt; + I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(slave); + + if (!sc->send_async) { + return -1; + } + + trace_i2c_send_async(slave->address, data); + + sc->send_async(slave, data); + + return 0; +} + uint8_t i2c_recv(I2CBus *bus) { uint8_t data = 0xff; @@ -265,6 +327,17 @@ void i2c_nack(I2CBus *bus) } } +void i2c_ack(I2CBus *bus) +{ + if (!bus->bh) { + return; + } + + trace_i2c_ack(); + + qemu_bh_schedule(bus->bh); +} + static int i2c_slave_post_load(void *opaque, int version_id) { I2CSlave *dev = opaque; @@ -286,7 +359,7 @@ const VMStateDescription vmstate_i2c_slave = { .version_id = 1, .minimum_version_id = 1, .post_load = i2c_slave_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(address, I2CSlave), VMSTATE_END_OF_LIST() } diff --git a/hw/i2c/exynos4210_i2c.c b/hw/i2c/exynos4210_i2c.c index b65a7d0222..9445424d5f 100644 --- a/hw/i2c/exynos4210_i2c.c +++ b/hw/i2c/exynos4210_i2c.c @@ -273,7 +273,7 @@ static const VMStateDescription exynos4210_i2c_vmstate = { .name = "exynos4210.i2c", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(i2ccon, Exynos4210I2CState), VMSTATE_UINT8(i2cstat, Exynos4210I2CState), VMSTATE_UINT8(i2cds, Exynos4210I2CState), diff --git a/hw/i2c/i2c_mux_pca954x.c b/hw/i2c/i2c_mux_pca954x.c index 3945de795c..db5db956a6 100644 --- a/hw/i2c/i2c_mux_pca954x.c +++ b/hw/i2c/i2c_mux_pca954x.c @@ -20,6 +20,7 @@ #include "hw/i2c/i2c_mux_pca954x.h" #include "hw/i2c/smbus_slave.h" #include "hw/qdev-core.h" +#include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "qemu/log.h" #include "qemu/module.h" @@ -43,6 +44,8 @@ typedef struct Pca954xState { bool enabled[PCA9548_CHANNEL_COUNT]; I2CBus *bus[PCA9548_CHANNEL_COUNT]; + + char *name; } Pca954xState; /* @@ -181,6 +184,17 @@ static void pca9548_class_init(ObjectClass *klass, void *data) s->nchans = PCA9548_CHANNEL_COUNT; } +static void pca954x_realize(DeviceState *dev, Error **errp) +{ + Pca954xState *s = PCA954X(dev); + DeviceState *d = DEVICE(s); + if (s->name) { + d->id = g_strdup(s->name); + } else { + d->id = g_strdup_printf("pca954x[%x]", s->parent.i2c.address); + } +} + static void pca954x_init(Object *obj) { Pca954xState *s = PCA954X(obj); @@ -197,6 +211,11 @@ static void pca954x_init(Object *obj) } } +static Property pca954x_props[] = { + DEFINE_PROP_STRING("name", Pca954xState, name), + DEFINE_PROP_END_OF_LIST() +}; + static void pca954x_class_init(ObjectClass *klass, void *data) { I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); @@ -209,9 +228,12 @@ static void pca954x_class_init(ObjectClass *klass, void *data) rc->phases.enter = pca954x_enter_reset; dc->desc = "Pca954x i2c-mux"; + dc->realize = pca954x_realize; k->write_data = pca954x_write_data; k->receive_byte = pca954x_read_byte; + + device_class_set_props(dc, pca954x_props); } static const TypeInfo pca954x_info[] = { diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c index 9792583fea..a25676f025 100644 --- a/hw/i2c/imx_i2c.c +++ b/hw/i2c/imx_i2c.c @@ -285,7 +285,7 @@ static const VMStateDescription imx_i2c_vmstate = { .name = TYPE_IMX_I2C, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(address, IMXI2CState), VMSTATE_UINT16(iadr, IMXI2CState), VMSTATE_UINT16(ifdr, IMXI2CState), diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build index d3df273251..c459adcb59 100644 --- a/hw/i2c/meson.build +++ b/hw/i2c/meson.build @@ -2,18 +2,20 @@ i2c_ss = ss.source_set() i2c_ss.add(when: 'CONFIG_I2C', if_true: files('core.c')) i2c_ss.add(when: 'CONFIG_SMBUS', if_true: files('smbus_slave.c', 'smbus_master.c')) i2c_ss.add(when: 'CONFIG_ACPI_SMBUS', if_true: files('pm_smbus.c')) -i2c_ss.add(when: 'CONFIG_ACPI_X86_ICH', if_true: files('smbus_ich9.c')) +i2c_ss.add(when: 'CONFIG_ACPI_ICH9', if_true: files('smbus_ich9.c')) i2c_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_i2c.c')) i2c_ss.add(when: 'CONFIG_BITBANG_I2C', if_true: files('bitbang_i2c.c')) i2c_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_i2c.c')) i2c_ss.add(when: 'CONFIG_IMX_I2C', if_true: files('imx_i2c.c')) i2c_ss.add(when: 'CONFIG_MPC_I2C', if_true: files('mpc_i2c.c')) +i2c_ss.add(when: 'CONFIG_ALLWINNER_I2C', if_true: files('allwinner-i2c.c')) i2c_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('microbit_i2c.c')) i2c_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_smbus.c')) i2c_ss.add(when: 'CONFIG_SMBUS_EEPROM', if_true: files('smbus_eeprom.c')) -i2c_ss.add(when: 'CONFIG_VERSATILE_I2C', if_true: files('versatile_i2c.c')) +i2c_ss.add(when: 'CONFIG_ARM_SBCON_I2C', if_true: files('arm_sbcon_i2c.c')) i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c')) i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c')) i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c')) i2c_ss.add(when: 'CONFIG_PMBUS', if_true: files('pmbus_device.c')) -softmmu_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss) +i2c_ss.add(when: 'CONFIG_BCM2835_I2C', if_true: files('bcm2835_i2c.c')) +system_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss) diff --git a/hw/i2c/microbit_i2c.c b/hw/i2c/microbit_i2c.c index e92f9f84ea..24d36d15b0 100644 --- a/hw/i2c/microbit_i2c.c +++ b/hw/i2c/microbit_i2c.c @@ -80,7 +80,7 @@ static const VMStateDescription microbit_i2c_vmstate = { .name = TYPE_MICROBIT_I2C, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, MicrobitI2CState, MICROBIT_I2C_NREGS), VMSTATE_UINT32(read_idx, MicrobitI2CState), VMSTATE_END_OF_LIST() diff --git a/hw/i2c/mpc_i2c.c b/hw/i2c/mpc_i2c.c index 845392505f..cb051a520f 100644 --- a/hw/i2c/mpc_i2c.c +++ b/hw/i2c/mpc_i2c.c @@ -224,7 +224,7 @@ static uint64_t mpc_i2c_read(void *opaque, hwaddr addr, unsigned size) break; } - DPRINTF("%s: addr " TARGET_FMT_plx " %02" PRIx32 "\n", __func__, + DPRINTF("%s: addr " HWADDR_FMT_plx " %02" PRIx32 "\n", __func__, addr, value); return (uint64_t)value; } @@ -234,7 +234,7 @@ static void mpc_i2c_write(void *opaque, hwaddr addr, { MPCI2CState *s = opaque; - DPRINTF("%s: addr " TARGET_FMT_plx " val %08" PRIx64 "\n", __func__, + DPRINTF("%s: addr " HWADDR_FMT_plx " val %08" PRIx64 "\n", __func__, addr, value); switch (addr) { case MPC_I2C_ADR: @@ -312,7 +312,7 @@ static const VMStateDescription mpc_i2c_vmstate = { .name = TYPE_MPC_I2C, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(address, MPCI2CState), VMSTATE_UINT8(adr, MPCI2CState), VMSTATE_UINT8(fdr, MPCI2CState), diff --git a/hw/i2c/npcm7xx_smbus.c b/hw/i2c/npcm7xx_smbus.c index e7e0ba66fe..0ea3083bb6 100644 --- a/hw/i2c/npcm7xx_smbus.c +++ b/hw/i2c/npcm7xx_smbus.c @@ -1046,7 +1046,7 @@ static const VMStateDescription vmstate_npcm7xx_smbus = { .name = "npcm7xx-smbus", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(sda, NPCM7xxSMBusState), VMSTATE_UINT8(st, NPCM7xxSMBusState), VMSTATE_UINT8(cst, NPCM7xxSMBusState), diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index d7eae548cb..3eed8110b9 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -1,6 +1,6 @@ /* * PC SMBus implementation - * splitted from acpi.c + * split from acpi.c * * Copyright (c) 2006 Fabrice Bellard * @@ -23,6 +23,7 @@ #include "hw/i2c/pm_smbus.h" #include "hw/i2c/smbus_master.h" #include "migration/vmstate.h" +#include "trace.h" #define SMBHSTSTS 0x00 #define SMBHSTCNT 0x02 @@ -64,15 +65,6 @@ #define AUX_BLK (1 << 1) #define AUX_MASK 0x3 -/*#define DEBUG*/ - -#ifdef DEBUG -# define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) -#else -# define SMBUS_DPRINTF(format, ...) do { } while (0) -#endif - - static void smb_transaction(PMSMBus *s) { uint8_t prot = (s->smb_ctl >> 2) & 0x07; @@ -82,7 +74,7 @@ static void smb_transaction(PMSMBus *s) I2CBus *bus = s->smbus; int ret; - SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); + trace_smbus_transaction(addr, prot); /* Transaction isn't exec if STS_DEV_ERR bit set */ if ((s->smb_stat & STS_DEV_ERR) != 0) { goto error; @@ -258,8 +250,7 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, PMSMBus *s = opaque; uint8_t clear_byte_done; - SMBUS_DPRINTF("SMB writeb port=0x%04" HWADDR_PRIx - " val=0x%02" PRIx64 "\n", addr, val); + trace_smbus_ioport_writeb(addr, val); switch(addr) { case SMBHSTSTS: clear_byte_done = s->smb_stat & val & STS_BYTE_DONE; @@ -279,7 +270,7 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, if (!read && s->smb_index == s->smb_data0) { uint8_t prot = (s->smb_ctl >> 2) & 0x07; uint8_t cmd = s->smb_cmd; - uint8_t addr = s->smb_addr >> 1; + uint8_t smb_addr = s->smb_addr >> 1; int ret; if (prot == PROT_I2C_BLOCK_READ) { @@ -287,7 +278,7 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, goto out; } - ret = smbus_write_block(s->smbus, addr, cmd, s->smb_data, + ret = smbus_write_block(s->smbus, smb_addr, cmd, s->smb_data, s->smb_data0, !s->i2c_enable); if (ret < 0) { s->smb_stat |= STS_DEV_ERR; @@ -429,8 +420,7 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) val = 0; break; } - SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", - addr, val); + trace_smbus_ioport_readb(addr, val); if (s->set_irq) { s->set_irq(s, smb_irq_value(s)); @@ -465,7 +455,7 @@ const VMStateDescription pmsmb_vmstate = { .name = "pmsmb", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(smb_stat, PMSMBus), VMSTATE_UINT8(smb_ctl, PMSMBus), VMSTATE_UINT8(smb_cmd, PMSMBus), diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c index 62885fa6a1..ba1d2fd716 100644 --- a/hw/i2c/pmbus_device.c +++ b/hw/i2c/pmbus_device.c @@ -8,7 +8,6 @@ #include "qemu/osdep.h" #include -#include #include "hw/i2c/pmbus_device.h" #include "migration/vmstate.h" #include "qemu/module.h" @@ -95,8 +94,14 @@ void pmbus_send64(PMBusDevice *pmdev, uint64_t data) void pmbus_send_string(PMBusDevice *pmdev, const char *data) { + if (!data) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: uninitialised read from 0x%02x\n", + __func__, DEVICE(pmdev)->canonical_path, pmdev->code); + return; + } + size_t len = strlen(data); - g_assert(len > 0); g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN); pmdev->out_buf[len + pmdev->out_buf_len] = len; @@ -106,6 +111,35 @@ void pmbus_send_string(PMBusDevice *pmdev, const char *data) pmdev->out_buf_len += len + 1; } +uint8_t pmbus_receive_block(PMBusDevice *pmdev, uint8_t *dest, size_t len) +{ + /* dest may contain data from previous writes */ + memset(dest, 0, len); + + /* Exclude command code from return value */ + pmdev->in_buf++; + pmdev->in_buf_len--; + + /* The byte after the command code denotes the length */ + uint8_t sent_len = pmdev->in_buf[0]; + + if (sent_len != pmdev->in_buf_len - 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: length mismatch. Expected %d bytes, got %d bytes\n", + __func__, sent_len, pmdev->in_buf_len - 1); + } + + /* exclude length byte */ + pmdev->in_buf++; + pmdev->in_buf_len--; + + if (pmdev->in_buf_len < len) { + len = pmdev->in_buf_len; + } + memcpy(dest, pmdev->in_buf, len); + return len; +} + static uint64_t pmbus_receive_uint(PMBusDevice *pmdev) { @@ -184,15 +218,18 @@ static void pmbus_quick_cmd(SMBusDevice *smd, uint8_t read) } } +static uint8_t pmbus_pages_num(PMBusDevice *pmdev) +{ + const PMBusDeviceClass *k = PMBUS_DEVICE_GET_CLASS(pmdev); + + /* some PMBus devices don't use the PAGE command, so they get 1 page */ + return k->device_num_pages ? : 1; +} + static void pmbus_pages_alloc(PMBusDevice *pmdev) { - /* some PMBus devices don't use the PAGE command, so they get 1 page */ - PMBusDeviceClass *k = PMBUS_DEVICE_GET_CLASS(pmdev); - if (k->device_num_pages == 0) { - k->device_num_pages = 1; - } - pmdev->num_pages = k->device_num_pages; - pmdev->pages = g_new0(PMBusPage, k->device_num_pages); + pmdev->num_pages = pmbus_pages_num(pmdev); + pmdev->pages = g_new0(PMBusPage, pmdev->num_pages); } void pmbus_check_limits(PMBusDevice *pmdev) @@ -261,6 +298,11 @@ void pmbus_check_limits(PMBusDevice *pmdev) } } +void pmbus_idle(PMBusDevice *pmdev) +{ + pmdev->code = PMBUS_IDLE_STATE; +} + /* assert the status_cml error upon receipt of malformed command */ static void pmbus_cml_error(PMBusDevice *pmdev) { @@ -284,14 +326,10 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) /* * Reading from all pages will return the value from page 0, - * this is unspecified behaviour in general. + * means that all subsequent commands are to be applied to all output. */ if (pmdev->page == PB_ALL_PAGES) { index = 0; - qemu_log_mask(LOG_GUEST_ERROR, - "%s: tried to read from all pages\n", - __func__); - pmbus_cml_error(pmdev); } else if (pmdev->page > pmdev->num_pages - 1) { qemu_log_mask(LOG_GUEST_ERROR, "%s: page %d is out of range\n", @@ -462,6 +500,54 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) } break; + case PMBUS_FAN_CONFIG_1_2: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send8(pmdev, pmdev->pages[index].fan_config_1_2); + } else { + goto passthough; + } + break; + + case PMBUS_FAN_COMMAND_1: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].fan_command_1); + } else { + goto passthough; + } + break; + + case PMBUS_FAN_COMMAND_2: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].fan_command_2); + } else { + goto passthough; + } + break; + + case PMBUS_FAN_CONFIG_3_4: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send8(pmdev, pmdev->pages[index].fan_config_3_4); + } else { + goto passthough; + } + break; + + case PMBUS_FAN_COMMAND_3: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].fan_command_3); + } else { + goto passthough; + } + break; + + case PMBUS_FAN_COMMAND_4: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].fan_command_4); + } else { + goto passthough; + } + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */ if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { pmbus_send16(pmdev, pmdev->pages[index].vout_ov_fault_limit); @@ -772,6 +858,22 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific); break; + case PMBUS_STATUS_FANS_1_2: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send8(pmdev, pmdev->pages[index].status_fans_1_2); + } else { + goto passthough; + } + break; + + case PMBUS_STATUS_FANS_3_4: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send8(pmdev, pmdev->pages[index].status_fans_3_4); + } else { + goto passthough; + } + break; + case PMBUS_READ_EIN: /* Read-Only block 5 bytes */ if (pmdev->pages[index].page_flags & PB_HAS_EIN) { pmbus_send(pmdev, pmdev->pages[index].read_ein, 5); @@ -804,6 +906,14 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) } break; + case PMBUS_READ_VCAP: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_VCAP) { + pmbus_send16(pmdev, pmdev->pages[index].read_vcap); + } else { + goto passthough; + } + break; + case PMBUS_READ_VOUT: /* Read-Only word */ if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { pmbus_send16(pmdev, pmdev->pages[index].read_vout); @@ -844,6 +954,54 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) } break; + case PMBUS_READ_FAN_SPEED_1: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_1); + } else { + goto passthough; + } + break; + + case PMBUS_READ_FAN_SPEED_2: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_2); + } else { + goto passthough; + } + break; + + case PMBUS_READ_FAN_SPEED_3: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_3); + } else { + goto passthough; + } + break; + + case PMBUS_READ_FAN_SPEED_4: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_4); + } else { + goto passthough; + } + break; + + case PMBUS_READ_DUTY_CYCLE: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].read_duty_cycle); + } else { + goto passthough; + } + break; + + case PMBUS_READ_FREQUENCY: /* Read-Only word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send16(pmdev, pmdev->pages[index].read_frequency); + } else { + goto passthough; + } + break; + case PMBUS_READ_POUT: /* Read-Only word */ if (pmdev->pages[index].page_flags & PB_HAS_POUT) { pmbus_send16(pmdev, pmdev->pages[index].read_pout); @@ -984,6 +1142,10 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd) } break; + case PMBUS_IDLE_STATE: + pmbus_send8(pmdev, PMBUS_ERR_BYTE); + break; + case PMBUS_CLEAR_FAULTS: /* Send Byte */ case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */ case PMBUS_STORE_DEFAULT_ALL: /* Send Byte */ @@ -1082,12 +1244,26 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) pmdev->in_buf = buf; pmdev->code = buf[0]; /* PMBus command code */ + + if (pmdev->code == PMBUS_CLEAR_FAULTS) { + pmbus_clear_faults(pmdev); + } + if (len == 1) { /* Single length writes are command codes only */ return 0; } if (pmdev->code == PMBUS_PAGE) { pmdev->page = pmbus_receive8(pmdev); + + if (pmdev->page > pmdev->num_pages - 1 && pmdev->page != PB_ALL_PAGES) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: page %u is out of range\n", + __func__, pmdev->page); + pmdev->page = 0; /* undefined behaviour - reset to page 0 */ + pmbus_cml_error(pmdev); + return PMBUS_ERR_BYTE; + } return 0; } @@ -1101,15 +1277,6 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) return 0; } - if (pmdev->page > pmdev->num_pages - 1) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: page %u is out of range\n", - __func__, pmdev->page); - pmdev->page = 0; /* undefined behaviour - reset to page 0 */ - pmbus_cml_error(pmdev); - return PMBUS_ERR_BYTE; - } - index = pmdev->page; switch (pmdev->code) { @@ -1263,6 +1430,54 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) } break; + case PMBUS_FAN_CONFIG_1_2: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmdev->pages[index].fan_config_1_2 = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_FAN_COMMAND_1: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmdev->pages[index].fan_command_1 = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_FAN_COMMAND_2: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmdev->pages[index].fan_command_2 = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_FAN_CONFIG_3_4: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmdev->pages[index].fan_config_3_4 = pmbus_receive8(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_FAN_COMMAND_3: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmdev->pages[index].fan_command_3 = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + + case PMBUS_FAN_COMMAND_4: /* R/W word */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmdev->pages[index].fan_command_4 = pmbus_receive16(pmdev); + } else { + goto passthrough; + } + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */ if (pmdev->pages[index].page_flags & PB_HAS_VOUT) { pmdev->pages[index].vout_ov_fault_limit = pmbus_receive16(pmdev); @@ -1568,6 +1783,22 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev); break; + case PMBUS_STATUS_FANS_1_2: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send8(pmdev, pmdev->pages[index].status_fans_1_2); + } else { + goto passthrough; + } + break; + + case PMBUS_STATUS_FANS_3_4: /* R/W byte */ + if (pmdev->pages[index].page_flags & PB_HAS_FAN) { + pmbus_send8(pmdev, pmdev->pages[index].status_fans_3_4); + } else { + goto passthrough; + } + break; + case PMBUS_PAGE_PLUS_READ: /* Block Read-only */ case PMBUS_CAPABILITY: /* Read-Only byte */ case PMBUS_COEFFICIENTS: /* Read-only block 5 bytes */ @@ -1612,7 +1843,7 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len) break; passthrough: - /* Unimplimented registers get passed to the device */ + /* Unimplemented registers get passed to the device */ default: if (pmdc->write_data) { ret = pmdc->write_data(pmdev, buf, len); @@ -1655,7 +1886,7 @@ const VMStateDescription vmstate_pmbus_device = { .name = TYPE_PMBUS_DEVICE, .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_SMBUS_DEVICE(smb, PMBusDevice), VMSTATE_UINT8(num_pages, PMBusDevice), VMSTATE_UINT8(code, PMBusDevice), diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c index 12c5741f38..c42236bb13 100644 --- a/hw/i2c/smbus_eeprom.c +++ b/hw/i2c/smbus_eeprom.c @@ -100,7 +100,7 @@ static const VMStateDescription vmstate_smbus_eeprom = { .version_id = 1, .minimum_version_id = 1, .needed = smbus_eeprom_vmstate_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_SMBUS_DEVICE(smbusdev, SMBusEEPROMDevice), VMSTATE_UINT8_ARRAY(data, SMBusEEPROMDevice, SMBUS_EEPROM_SIZE), VMSTATE_UINT8(offset, SMBusEEPROMDevice), diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index 44dd5653b7..208f263ac5 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -27,8 +27,9 @@ #include "migration/vmstate.h" #include "qemu/module.h" -#include "hw/i386/ich9.h" +#include "hw/southbridge/ich9.h" #include "qom/object.h" +#include "hw/acpi/acpi_aml_interface.h" OBJECT_DECLARE_SIMPLE_TYPE(ICH9SMBState, ICH9_SMB_DEVICE) @@ -49,7 +50,7 @@ static const VMStateDescription vmstate_ich9_smbus = { .name = "ich9_smb", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, ICH9SMBState), VMSTATE_BOOL_TEST(irq_enabled, ICH9SMBState, ich9_vmstate_need_smbus), VMSTATE_STRUCT_TEST(smb, ICH9SMBState, ich9_vmstate_need_smbus, 1, @@ -79,6 +80,18 @@ static void ich9_smbus_write_config(PCIDevice *d, uint32_t address, } } +static void ich9_smb_set_irq(PMSMBus *pmsmb, bool enabled) +{ + ICH9SMBState *s = pmsmb->opaque; + + if (enabled == s->irq_enabled) { + return; + } + + s->irq_enabled = enabled; + pci_set_irq(&s->dev, enabled); +} + static void ich9_smbus_realize(PCIDevice *d, Error **errp) { ICH9SMBState *s = ICH9_SMB_DEVICE(d); @@ -92,12 +105,24 @@ static void ich9_smbus_realize(PCIDevice *d, Error **errp) pm_smbus_init(&d->qdev, &s->smb, false); pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR, PCI_BASE_ADDRESS_SPACE_IO, &s->smb.io); + + s->smb.set_irq = ich9_smb_set_irq; + s->smb.opaque = s; +} + +static void build_ich9_smb_aml(AcpiDevAmlIf *adev, Aml *scope) +{ + ICH9SMBState *s = ICH9_SMB_DEVICE(adev); + BusState *bus = BUS(s->smb.smbus); + + qbus_build_aml(bus, scope); } static void ich9_smb_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_ICH9_6; @@ -112,28 +137,7 @@ static void ich9_smb_class_init(ObjectClass *klass, void *data) * pc_q35_init() */ dc->user_creatable = false; -} - -static void ich9_smb_set_irq(PMSMBus *pmsmb, bool enabled) -{ - ICH9SMBState *s = pmsmb->opaque; - - if (enabled == s->irq_enabled) { - return; - } - - s->irq_enabled = enabled; - pci_set_irq(&s->dev, enabled); -} - -I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base) -{ - PCIDevice *d = - pci_create_simple_multifunction(bus, devfn, true, TYPE_ICH9_SMB_DEVICE); - ICH9SMBState *s = ICH9_SMB_DEVICE(d); - s->smb.set_irq = ich9_smb_set_irq; - s->smb.opaque = s; - return s->smb.smbus; + adevc->build_dev_aml = build_ich9_smb_aml; } static const TypeInfo ich9_smb_info = { @@ -143,6 +147,7 @@ static const TypeInfo ich9_smb_info = { .class_init = ich9_smb_class_init, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { TYPE_ACPI_DEV_AML_IF }, { }, }, }; diff --git a/hw/i2c/smbus_slave.c b/hw/i2c/smbus_slave.c index 5d10e27664..9f9afc25a4 100644 --- a/hw/i2c/smbus_slave.c +++ b/hw/i2c/smbus_slave.c @@ -2,7 +2,7 @@ * QEMU SMBus device emulation. * * This code is a helper for SMBus device emulation. It implements an - * I2C device inteface and runs the SMBus protocol from the device + * I2C device interface and runs the SMBus protocol from the device * point of view and maps those to simple calls to emulate. * * Copyright (c) 2007 CodeSourcery. @@ -25,11 +25,15 @@ #define DPRINTF(fmt, ...) \ do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0) #define BADF(fmt, ...) \ -do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) +do { g_autofree char *qom_path = object_get_canonical_path(OBJECT(dev)); \ + fprintf(stderr, "%s: smbus: error: " fmt , qom_path, ## __VA_ARGS__); \ + exit(1); } while (0) #else #define DPRINTF(fmt, ...) do {} while(0) #define BADF(fmt, ...) \ -do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0) +do { g_autofree char *qom_path = object_get_canonical_path(OBJECT(dev)); \ + fprintf(stderr, "%s: smbus: error: " fmt , qom_path, ## __VA_ARGS__); \ + } while (0) #endif enum { @@ -143,6 +147,10 @@ static int smbus_i2c_event(I2CSlave *s, enum i2c_event event) dev->mode = SMBUS_CONFUSED; break; } + break; + + default: + return -1; } return 0; @@ -211,7 +219,7 @@ const VMStateDescription vmstate_smbus_device = { .name = TYPE_SMBUS_DEVICE, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_I2C_SLAVE(i2c, SMBusDevice), VMSTATE_INT32(mode, SMBusDevice), VMSTATE_INT32(data_len, SMBusDevice), diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events index 7d8907c1ee..6900e06eda 100644 --- a/hw/i2c/trace-events +++ b/hw/i2c/trace-events @@ -1,15 +1,35 @@ # See docs/devel/tracing.rst for syntax documentation. +# bitbang_i2c.c +bitbang_i2c_state(const char *old_state, const char *new_state) "state %s -> %s" +bitbang_i2c_addr(uint8_t addr) "Address 0x%02x" +bitbang_i2c_send(uint8_t byte) "TX byte 0x%02x" +bitbang_i2c_recv(uint8_t byte) "RX byte 0x%02x" +bitbang_i2c_data(unsigned clk, unsigned dat, unsigned old_out, unsigned new_out) "clk %u dat %u out %u -> %u" + # core.c 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_send_async(uint8_t address, uint8_t data) "send_async(addr:0x%02x) data:0x%02x" i2c_recv(uint8_t address, uint8_t data) "recv(addr:0x%02x) data:0x%02x" +i2c_ack(void) "" + +# pm_smbus.c + +smbus_ioport_readb(uint16_t addr, uint8_t data) "[0x%04" PRIx16 "] -> val=0x%02x" +smbus_ioport_writeb(uint16_t addr, uint8_t data) "[0x%04" PRIx16 "] <- val=0x%02x" +smbus_transaction(uint8_t addr, uint8_t prot) "addr=0x%02x prot=0x%02x" + +# allwinner_i2c.c + +allwinner_i2c_read(const char* reg_name, uint64_t offset, uint64_t value) "read %s [0x%" PRIx64 "]: -> 0x%" PRIx64 +allwinner_i2c_write(const char* reg_name, uint64_t offset, uint64_t value) "write %s [0x%" PRIx64 "]: <- 0x%" PRIx64 # 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_raise_interrupt(uint32_t intr_status, const char *s) "handled intr=0x%x %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" diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index d22ac4a4b9..a6ee052f9a 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -26,7 +26,6 @@ config PC imply QXL imply SEV imply SGX - imply SGA imply TEST_DEVICES imply TPM_CRB imply TPM_TIS_ISA @@ -46,12 +45,12 @@ config PC select ACPI_VMGENID select VIRTIO_PMEM_SUPPORTED select VIRTIO_MEM_SUPPORTED + select HV_BALLOON_SUPPORTED config PC_PCI bool select APIC select IOAPIC - select APM select PC config PC_ACPI @@ -59,6 +58,7 @@ config PC_ACPI select ACPI_X86 select ACPI_CPU_HOTPLUG select ACPI_MEMORY_HOTPLUG + select ACPI_PCI_BRIDGE select ACPI_VIOT select SMBUS_EEPROM select PFLASH_CFI01 @@ -69,22 +69,22 @@ config I440FX imply E1000_PCI imply VMPORT imply VMMOUSE + select ACPI_PIIX4 select PC_PCI select PC_ACPI - select ACPI_SMBUS select PCI_I440FX - select PIIX3 - select IDE_PIIX + select PIIX select DIMM select SMBIOS + select SMBIOS_LEGACY select FW_CFG_DMA config ISAPC bool + imply VGA_ISA select ISA_BUS select PC select IDE_ISA - select VGA_ISA # FIXME: it is in the same file as i440fx, and does not compile # if separated depends on I440FX @@ -96,6 +96,7 @@ config Q35 imply E1000E_PCI_EXPRESS imply VMPORT imply VMMOUSE + imply IOMMUFD select PC_PCI select PC_ACPI select PCI_EXPRESS_Q35 @@ -137,3 +138,8 @@ config VMPORT config VMMOUSE bool depends on VMPORT + +config XEN_EMU + bool + default y + depends on KVM && (I386 || X86_64) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index c125939ed6..53f804ac16 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -27,25 +27,24 @@ #include "acpi-common.h" #include "qemu/bitmap.h" #include "qemu/error-report.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_bridge.h" #include "hw/cxl/cxl.h" #include "hw/core/cpu.h" #include "target/i386/cpu.h" -#include "hw/misc/pvpanic.h" #include "hw/timer/hpet.h" #include "hw/acpi/acpi-defs.h" #include "hw/acpi/acpi.h" #include "hw/acpi/cpu.h" #include "hw/nvram/fw_cfg.h" #include "hw/acpi/bios-linker-loader.h" -#include "hw/isa/isa.h" +#include "hw/acpi/acpi_aml_interface.h" #include "hw/input/i8042.h" -#include "hw/block/fdc.h" #include "hw/acpi/memory_hotplug.h" #include "sysemu/tpm.h" #include "hw/acpi/tpm.h" #include "hw/acpi/vmgenid.h" #include "hw/acpi/erst.h" +#include "hw/acpi/piix4.h" #include "sysemu/tpm_backend.h" #include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" @@ -56,11 +55,12 @@ #include "hw/hyperv/vmbus-bridge.h" /* Supported chipsets: */ -#include "hw/southbridge/piix.h" +#include "hw/southbridge/ich9.h" #include "hw/acpi/pcihp.h" #include "hw/i386/fw_cfg.h" -#include "hw/i386/ich9.h" +#include "hw/i386/pc.h" #include "hw/pci/pci_bus.h" +#include "hw/pci-host/i440fx.h" #include "hw/pci-host/q35.h" #include "hw/i386/x86-iommu.h" @@ -68,16 +68,15 @@ #include "hw/acpi/utils.h" #include "hw/acpi/pci.h" #include "hw/acpi/cxl.h" +#include "hw/acpi/acpi_generic_initiator.h" #include "qom/qom-qobject.h" #include "hw/i386/amd_iommu.h" #include "hw/i386/intel_iommu.h" #include "hw/virtio/virtio-iommu.h" -#include "hw/acpi/ipmi.h" #include "hw/acpi/hmat.h" #include "hw/acpi/viot.h" -#include "hw/acpi/cxl.h" #include CONFIG_DEVICES @@ -114,24 +113,12 @@ typedef struct AcpiPmInfo { } AcpiPmInfo; typedef struct AcpiMiscInfo { - bool is_piix4; bool has_hpet; #ifdef CONFIG_TPM TPMVersion tpm_version; #endif - const unsigned char *dsdt_code; - unsigned dsdt_size; - uint16_t pvpanic_port; - uint16_t applesmc_io_base; } AcpiMiscInfo; -typedef struct AcpiBuildPciBusHotplugState { - GArray *device_table; - GArray *notify_table; - struct AcpiBuildPciBusHotplugState *parent; - bool pcihp_bridge_en; -} AcpiBuildPciBusHotplugState; - typedef struct FwCfgTPMConfig { uint32_t tpmppi_address; uint8_t tpm_version; @@ -206,21 +193,10 @@ static void init_common_fadt_data(MachineState *ms, Object *o, *data = fadt; } -static Object *object_resolve_type_unambiguous(const char *typename) -{ - bool ambig; - Object *o = object_resolve_path_type("", typename, &ambig); - - if (ambig || !o) { - return NULL; - } - return o; -} - static void acpi_get_pm_info(MachineState *machine, AcpiPmInfo *pm) { - Object *piix = object_resolve_type_unambiguous(TYPE_PIIX4_PM); - Object *lpc = object_resolve_type_unambiguous(TYPE_ICH9_LPC_DEVICE); + Object *piix = object_resolve_type_unambiguous(TYPE_PIIX4_PM, NULL); + Object *lpc = object_resolve_type_unambiguous(TYPE_ICH9_LPC_DEVICE, NULL); Object *obj = piix ? piix : lpc; QObject *o; pm->cpu_hp_io_base = 0; @@ -255,10 +231,6 @@ static void acpi_get_pm_info(MachineState *machine, AcpiPmInfo *pm) pm->pcihp_io_len = object_property_get_uint(obj, ACPI_PCIHP_IO_LEN_PROP, NULL); - /* The above need not be conditional on machine type because the reset port - * happens to be the same on PIIX (pc) and ICH9 (q35). */ - QEMU_BUILD_BUG_ON(ICH9_RST_CNT_IOPORT != PIIX_RCR_IOPORT); - /* Fill in optional s3/s4 related properties */ o = object_property_get_qobject(obj, ACPI_PM_PROP_S3_DISABLED, NULL); if (o) { @@ -292,23 +264,10 @@ static void acpi_get_pm_info(MachineState *machine, AcpiPmInfo *pm) static void acpi_get_misc_info(AcpiMiscInfo *info) { - Object *piix = object_resolve_type_unambiguous(TYPE_PIIX4_PM); - Object *lpc = object_resolve_type_unambiguous(TYPE_ICH9_LPC_DEVICE); - assert(!!piix != !!lpc); - - if (piix) { - info->is_piix4 = true; - } - if (lpc) { - info->is_piix4 = false; - } - info->has_hpet = hpet_find(); #ifdef CONFIG_TPM info->tpm_version = tpm_get_version(tpm_find()); #endif - info->pvpanic_port = pvpanic_port(); - info->applesmc_io_base = applesmc_port(); } /* @@ -380,6 +339,127 @@ build_facs(GArray *table_data) g_array_append_vals(table_data, reserved, 40); /* Reserved */ } +Aml *aml_pci_device_dsm(void) +{ + Aml *method; + + method = aml_method("_DSM", 4, AML_SERIALIZED); + { + Aml *params = aml_local(0); + Aml *pkg = aml_package(2); + aml_append(pkg, aml_int(0)); + aml_append(pkg, aml_int(0)); + aml_append(method, aml_store(pkg, params)); + aml_append(method, + aml_store(aml_name("BSEL"), aml_index(params, aml_int(0)))); + aml_append(method, + aml_store(aml_name("ASUN"), aml_index(params, aml_int(1)))); + aml_append(method, + aml_return(aml_call5("PDSM", aml_arg(0), aml_arg(1), + aml_arg(2), aml_arg(3), params)) + ); + } + return method; +} + +static void build_append_pci_dsm_func0_common(Aml *ctx, Aml *retvar) +{ + Aml *UUID, *ifctx1; + uint8_t byte_list[1] = { 0 }; /* nothing supported yet */ + + aml_append(ctx, aml_store(aml_buffer(1, byte_list), retvar)); + /* + * PCI Firmware Specification 3.1 + * 4.6. _DSM Definitions for PCI + */ + UUID = aml_touuid("E5C937D0-3553-4D7A-9117-EA4D19C3434D"); + ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(0), UUID))); + { + /* call is for unsupported UUID, bail out */ + aml_append(ifctx1, aml_return(retvar)); + } + aml_append(ctx, ifctx1); + + ifctx1 = aml_if(aml_lless(aml_arg(1), aml_int(2))); + { + /* call is for unsupported REV, bail out */ + aml_append(ifctx1, aml_return(retvar)); + } + aml_append(ctx, ifctx1); +} + +static Aml *aml_pci_edsm(void) +{ + Aml *method, *ifctx; + Aml *zero = aml_int(0); + Aml *func = aml_arg(2); + Aml *ret = aml_local(0); + Aml *aidx = aml_local(1); + Aml *params = aml_arg(4); + + method = aml_method("EDSM", 5, AML_SERIALIZED); + + /* get supported functions */ + ifctx = aml_if(aml_equal(func, zero)); + { + /* 1: have supported functions */ + /* 7: support for function 7 */ + const uint8_t caps = 1 | BIT(7); + build_append_pci_dsm_func0_common(ifctx, ret); + aml_append(ifctx, aml_store(aml_int(caps), aml_index(ret, zero))); + aml_append(ifctx, aml_return(ret)); + } + aml_append(method, ifctx); + + /* handle specific functions requests */ + /* + * PCI Firmware Specification 3.1 + * 4.6.7. _DSM for Naming a PCI or PCI Express Device Under + * Operating Systems + */ + ifctx = aml_if(aml_equal(func, aml_int(7))); + { + Aml *pkg = aml_package(2); + aml_append(pkg, zero); + /* optional, if not impl. should return null string */ + aml_append(pkg, aml_string("%s", "")); + aml_append(ifctx, aml_store(pkg, ret)); + + /* + * IASL is fine when initializing Package with computational data, + * however it makes guest unhappy /it fails to process such AML/. + * So use runtime assignment to set acpi-index after initializer + * to make OSPM happy. + */ + aml_append(ifctx, + aml_store(aml_derefof(aml_index(params, aml_int(0))), aidx)); + aml_append(ifctx, aml_store(aidx, aml_index(ret, zero))); + aml_append(ifctx, aml_return(ret)); + } + aml_append(method, ifctx); + + return method; +} + +static Aml *aml_pci_static_endpoint_dsm(PCIDevice *pdev) +{ + Aml *method; + + g_assert(pdev->acpi_index != 0); + method = aml_method("_DSM", 4, AML_SERIALIZED); + { + Aml *params = aml_local(0); + Aml *pkg = aml_package(1); + aml_append(pkg, aml_int(pdev->acpi_index)); + aml_append(method, aml_store(pkg, params)); + aml_append(method, + aml_return(aml_call5("EDSM", aml_arg(0), aml_arg(1), + aml_arg(2), aml_arg(3), params)) + ); + } + return method; +} + static void build_append_pcihp_notify_entry(Aml *method, int slot) { Aml *if_ctx; @@ -390,88 +470,106 @@ static void build_append_pcihp_notify_entry(Aml *method, int slot) aml_append(method, if_ctx); } -static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, - bool pcihp_bridge_en) +static bool is_devfn_ignored_generic(const int devfn, const PCIBus *bus) { - Aml *dev, *notify_method = NULL, *method; - QObject *bsel; - PCIBus *sec; - int devfn; + const PCIDevice *pdev = bus->devices[devfn]; - bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); - if (bsel) { - uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); - - aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val))); - notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED); + if (PCI_FUNC(devfn)) { + if (IS_PCI_BRIDGE(pdev)) { + /* + * Ignore only hotplugged PCI bridges on !0 functions, but + * allow describing cold plugged bridges on all functions + */ + if (DEVICE(pdev)->hotplugged) { + return true; + } + } } + return false; +} + +static bool is_devfn_ignored_hotplug(const int devfn, const PCIBus *bus) +{ + PCIDevice *pdev = bus->devices[devfn]; + if (pdev) { + return is_devfn_ignored_generic(devfn, bus) || + !DEVICE_GET_CLASS(pdev)->hotpluggable || + /* Cold plugged bridges aren't themselves hot-pluggable */ + (IS_PCI_BRIDGE(pdev) && !DEVICE(pdev)->hotplugged); + } else { /* non populated slots */ + /* + * hotplug is supported only for non-multifunction device + * so generate device description only for function 0 + */ + if (PCI_FUNC(devfn) || + (pci_bus_is_express(bus) && PCI_SLOT(devfn) > 0)) { + return true; + } + } + return false; +} + +void build_append_pcihp_slots(Aml *parent_scope, PCIBus *bus) +{ + int devfn; + Aml *dev, *notify_method = NULL, *method; + QObject *bsel = object_property_get_qobject(OBJECT(bus), + ACPI_PCIHP_PROP_BSEL, NULL); + uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); + qobject_unref(bsel); + + aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val))); + notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED); for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { - DeviceClass *dc; - PCIDeviceClass *pc; - PCIDevice *pdev = bus->devices[devfn]; int slot = PCI_SLOT(devfn); - int func = PCI_FUNC(devfn); + int adr = slot << 16 | PCI_FUNC(devfn); + + if (is_devfn_ignored_hotplug(devfn, bus)) { + continue; + } + + if (bus->devices[devfn]) { + dev = aml_scope("S%.02X", devfn); + } else { + dev = aml_device("S%.02X", devfn); + aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); + } + + /* + * Can't declare _SUN here for every device as it changes 'slot' + * enumeration order in linux kernel, so use another variable for it + */ + aml_append(dev, aml_name_decl("ASUN", aml_int(slot))); + aml_append(dev, aml_pci_device_dsm()); + + aml_append(dev, aml_name_decl("_SUN", aml_int(slot))); + /* add _EJ0 to make slot hotpluggable */ + method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); + aml_append(method, + aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN")) + ); + aml_append(dev, method); + + build_append_pcihp_notify_entry(notify_method, slot); + + /* device descriptor has been composed, add it into parent context */ + aml_append(parent_scope, dev); + } + aml_append(parent_scope, notify_method); +} + +void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus) +{ + int devfn; + Aml *dev; + + for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { /* ACPI spec: 1.0b: Table 6-2 _ADR Object Bus Types, PCI type */ - int adr = slot << 16 | func; - bool hotplug_enabled_dev; - bool bridge_in_acpi; - bool cold_plugged_bridge; + int adr = PCI_SLOT(devfn) << 16 | PCI_FUNC(devfn); + PCIDevice *pdev = bus->devices[devfn]; - if (!pdev) { - /* - * add hotplug slots for non present devices. - * hotplug is supported only for non-multifunction device - * so generate device description only for function 0 - */ - if (bsel && !func) { - if (pci_bus_is_express(bus) && slot > 0) { - break; - } - dev = aml_device("S%.02X", devfn); - aml_append(dev, aml_name_decl("_SUN", aml_int(slot))); - aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); - method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); - aml_append(method, - aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN")) - ); - aml_append(dev, method); - method = aml_method("_DSM", 4, AML_SERIALIZED); - aml_append(method, - aml_return(aml_call6("PDSM", aml_arg(0), aml_arg(1), - aml_arg(2), aml_arg(3), - aml_name("BSEL"), aml_name("_SUN"))) - ); - aml_append(dev, method); - aml_append(parent_scope, dev); - - build_append_pcihp_notify_entry(notify_method, slot); - } - continue; - } - - pc = PCI_DEVICE_GET_CLASS(pdev); - dc = DEVICE_GET_CLASS(pdev); - - /* - * Cold plugged bridges aren't themselves hot-pluggable. - * Hotplugged bridges *are* hot-pluggable. - */ - cold_plugged_bridge = pc->is_bridge && !DEVICE(pdev)->hotplugged; - bridge_in_acpi = cold_plugged_bridge && pcihp_bridge_en; - - hotplug_enabled_dev = bsel && dc->hotpluggable && !cold_plugged_bridge; - - if (pc->class_id == PCI_CLASS_BRIDGE_ISA) { - continue; - } - - /* - * allow describing coldplugged bridges in ACPI even if they are not - * on function 0, as they are not unpluggable, for all other devices - * generate description only for function 0 per slot - */ - if (func && !bridge_in_acpi) { + if (!pdev || is_devfn_ignored_generic(devfn, bus)) { continue; } @@ -479,183 +577,150 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, dev = aml_device("S%.02X", devfn); aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); - if (bsel) { - /* - * Can't declare _SUN here for every device as it changes 'slot' - * enumeration order in linux kernel, so use another variable for it - */ - aml_append(dev, aml_name_decl("ASUN", aml_int(slot))); - method = aml_method("_DSM", 4, AML_SERIALIZED); - aml_append(method, aml_return( - aml_call6("PDSM", aml_arg(0), aml_arg(1), aml_arg(2), - aml_arg(3), aml_name("BSEL"), aml_name("ASUN")) - )); - aml_append(dev, method); + call_dev_aml_func(DEVICE(bus->devices[devfn]), dev); + /* add _DSM if device has acpi-index set */ + if (pdev->acpi_index && + !object_property_get_bool(OBJECT(pdev), "hotpluggable", + &error_abort)) { + aml_append(dev, aml_pci_static_endpoint_dsm(pdev)); } - if (pc->class_id == PCI_CLASS_DISPLAY_VGA) { - /* add VGA specific AML methods */ - int s3d; - - if (object_dynamic_cast(OBJECT(pdev), "qxl-vga")) { - s3d = 3; - } else { - s3d = 0; - } - - method = aml_method("_S1D", 0, AML_NOTSERIALIZED); - aml_append(method, aml_return(aml_int(0))); - aml_append(dev, method); - - method = aml_method("_S2D", 0, AML_NOTSERIALIZED); - aml_append(method, aml_return(aml_int(0))); - aml_append(dev, method); - - method = aml_method("_S3D", 0, AML_NOTSERIALIZED); - aml_append(method, aml_return(aml_int(s3d))); - aml_append(dev, method); - } else if (hotplug_enabled_dev) { - aml_append(dev, aml_name_decl("_SUN", aml_int(slot))); - /* add _EJ0 to make slot hotpluggable */ - method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); - aml_append(method, - aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN")) - ); - aml_append(dev, method); - - if (bsel) { - build_append_pcihp_notify_entry(notify_method, slot); - } - } else if (bridge_in_acpi) { - /* - * device is coldplugged bridge, - * add child device descriptions into its scope - */ - PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev)); - - build_append_pci_bus_devices(dev, sec_bus, pcihp_bridge_en); - } /* device descriptor has been composed, add it into parent context */ aml_append(parent_scope, dev); } - - if (bsel) { - aml_append(parent_scope, notify_method); - } - - /* Append PCNT method to notify about events on local and child buses. - * Add this method for root bus only when hotplug is enabled since DSDT - * expects it. - */ - if (bsel || pcihp_bridge_en) { - method = aml_method("PCNT", 0, AML_NOTSERIALIZED); - - /* If bus supports hotplug select it and notify about local events */ - if (bsel) { - uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); - - aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM"))); - aml_append(method, aml_call2("DVNT", aml_name("PCIU"), - aml_int(1))); /* Device Check */ - aml_append(method, aml_call2("DVNT", aml_name("PCID"), - aml_int(3))); /* Eject Request */ - } - - /* Notify about child bus events in any case */ - if (pcihp_bridge_en) { - QLIST_FOREACH(sec, &bus->child, sibling) { - if (pci_bus_is_root(sec)) { - continue; - } - - aml_append(method, aml_name("^S%.02X.PCNT", - sec->parent_dev->devfn)); - } - } - - aml_append(parent_scope, method); - } - qobject_unref(bsel); } -Aml *aml_pci_device_dsm(void) +static bool build_append_notfication_callback(Aml *parent_scope, + const PCIBus *bus) { - Aml *method, *UUID, *ifctx, *ifctx1, *ifctx2, *ifctx3, *elsectx; - Aml *acpi_index = aml_local(0); - Aml *zero = aml_int(0); - Aml *bnum = aml_arg(4); - Aml *func = aml_arg(2); - Aml *rev = aml_arg(1); - Aml *sunum = aml_arg(5); + Aml *method; + PCIBus *sec; + QObject *bsel; + int nr_notifiers = 0; + GQueue *pcnt_bus_list = g_queue_new(); - method = aml_method("PDSM", 6, AML_SERIALIZED); + QLIST_FOREACH(sec, &bus->child, sibling) { + Aml *br_scope = aml_scope("S%.02X", sec->parent_dev->devfn); + if (pci_bus_is_root(sec)) { + continue; + } + nr_notifiers = nr_notifiers + + build_append_notfication_callback(br_scope, sec); + /* + * add new child scope to parent + * and keep track of bus that have PCNT, + * bus list is used later to call children PCNTs from this level PCNT + */ + if (nr_notifiers) { + g_queue_push_tail(pcnt_bus_list, sec); + aml_append(parent_scope, br_scope); + } + } /* - * PCI Firmware Specification 3.1 - * 4.6. _DSM Definitions for PCI + * Append PCNT method to notify about events on local and child buses. + * ps: hostbridge might not have hotplug (bsel) enabled but might have + * child bridges that do have bsel. */ - UUID = aml_touuid("E5C937D0-3553-4D7A-9117-EA4D19C3434D"); - ifctx = aml_if(aml_equal(aml_arg(0), UUID)); - { - aml_append(ifctx, aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); - ifctx1 = aml_if(aml_equal(func, zero)); - { - uint8_t byte_list[1]; + method = aml_method("PCNT", 0, AML_NOTSERIALIZED); - ifctx2 = aml_if(aml_equal(rev, aml_int(2))); - { - /* - * advertise function 7 if device has acpi-index - * acpi_index values: - * 0: not present (default value) - * FFFFFFFF: not supported (old QEMU without PIDX reg) - * other: device's acpi-index - */ - ifctx3 = aml_if(aml_lnot( - aml_or(aml_equal(acpi_index, zero), - aml_equal(acpi_index, aml_int(0xFFFFFFFF)), NULL) - )); - { - byte_list[0] = - 1 /* have supported functions */ | - 1 << 7 /* support for function 7 */ - ; - aml_append(ifctx3, aml_return(aml_buffer(1, byte_list))); - } - aml_append(ifctx2, ifctx3); - } - aml_append(ifctx1, ifctx2); + /* If bus supports hotplug select it and notify about local events */ + bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); + if (bsel) { + uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); - byte_list[0] = 0; /* nothing supported */ - aml_append(ifctx1, aml_return(aml_buffer(1, byte_list))); - } - aml_append(ifctx, ifctx1); - elsectx = aml_else(); - /* - * PCI Firmware Specification 3.1 - * 4.6.7. _DSM for Naming a PCI or PCI Express Device Under - * Operating Systems - */ - ifctx1 = aml_if(aml_equal(func, aml_int(7))); - { - Aml *pkg = aml_package(2); - Aml *ret = aml_local(1); - - aml_append(pkg, zero); - /* - * optional, if not impl. should return null string - */ - aml_append(pkg, aml_string("%s", "")); - aml_append(ifctx1, aml_store(pkg, ret)); - /* - * update acpi-index to actual value - */ - aml_append(ifctx1, aml_store(acpi_index, aml_index(ret, zero))); - aml_append(ifctx1, aml_return(ret)); - } - aml_append(elsectx, ifctx1); - aml_append(ifctx, elsectx); + aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM"))); + aml_append(method, aml_call2("DVNT", aml_name("PCIU"), + aml_int(1))); /* Device Check */ + aml_append(method, aml_call2("DVNT", aml_name("PCID"), + aml_int(3))); /* Eject Request */ + nr_notifiers++; } + + /* Notify about child bus events in any case */ + while ((sec = g_queue_pop_head(pcnt_bus_list))) { + aml_append(method, aml_name("^S%.02X.PCNT", sec->parent_dev->devfn)); + } + + aml_append(parent_scope, method); + qobject_unref(bsel); + g_queue_free(pcnt_bus_list); + return !!nr_notifiers; +} + +static Aml *aml_pci_pdsm(void) +{ + Aml *method, *ifctx, *ifctx1; + Aml *ret = aml_local(0); + Aml *caps = aml_local(1); + Aml *acpi_index = aml_local(2); + Aml *zero = aml_int(0); + Aml *one = aml_int(1); + Aml *func = aml_arg(2); + Aml *params = aml_arg(4); + Aml *bnum = aml_derefof(aml_index(params, aml_int(0))); + Aml *sunum = aml_derefof(aml_index(params, aml_int(1))); + + method = aml_method("PDSM", 5, AML_SERIALIZED); + + /* get supported functions */ + ifctx = aml_if(aml_equal(func, zero)); + { + build_append_pci_dsm_func0_common(ifctx, ret); + + aml_append(ifctx, aml_store(zero, caps)); + aml_append(ifctx, + aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); + /* + * advertise function 7 if device has acpi-index + * acpi_index values: + * 0: not present (default value) + * FFFFFFFF: not supported (old QEMU without PIDX reg) + * other: device's acpi-index + */ + ifctx1 = aml_if(aml_lnot( + aml_or(aml_equal(acpi_index, zero), + aml_equal(acpi_index, aml_int(0xFFFFFFFF)), NULL) + )); + { + /* have supported functions */ + aml_append(ifctx1, aml_or(caps, one, caps)); + /* support for function 7 */ + aml_append(ifctx1, + aml_or(caps, aml_shiftleft(one, aml_int(7)), caps)); + } + aml_append(ifctx, ifctx1); + + aml_append(ifctx, aml_store(caps, aml_index(ret, zero))); + aml_append(ifctx, aml_return(ret)); + } + aml_append(method, ifctx); + + /* handle specific functions requests */ + /* + * PCI Firmware Specification 3.1 + * 4.6.7. _DSM for Naming a PCI or PCI Express Device Under + * Operating Systems + */ + ifctx = aml_if(aml_equal(func, aml_int(7))); + { + Aml *pkg = aml_package(2); + + aml_append(pkg, zero); + /* + * optional, if not impl. should return null string + */ + aml_append(pkg, aml_string("%s", "")); + aml_append(ifctx, aml_store(pkg, ret)); + + aml_append(ifctx, aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); + /* + * update acpi-index to actual value + */ + aml_append(ifctx, aml_store(acpi_index, aml_index(ret, zero))); + aml_append(ifctx, aml_return(ret)); + } + aml_append(method, ifctx); return method; } @@ -699,7 +764,7 @@ static Aml *initialize_route(Aml *route, const char *link_name, * * Returns an array of 128 routes, one for each device, * based on device location. - * The main goal is to equaly distribute the interrupts + * The main goal is to equally distribute the interrupts * over the 4 existing ACPI links (works only for i440fx). * The hash function is (slot + pin) & 3 -> "LNK[D|A|B|C]". * @@ -865,21 +930,6 @@ static Aml *build_vmbus_device_aml(VMBusBridge *vmbus_bridge) return dev; } -static void build_isa_devices_aml(Aml *table) -{ - bool ambiguous; - Object *obj = object_resolve_path_type("", TYPE_ISA_BUS, &ambiguous); - Aml *scope; - - assert(obj && !ambiguous); - - scope = aml_scope("_SB.PCI0.ISA"); - build_acpi_ipmi_devices(scope, BUS(obj), "\\_SB.PCI0.ISA"); - isa_build_aml(ISA_BUS(obj), scope); - - aml_append(table, scope); -} - static void build_dbg_aml(Aml *table) { Aml *field; @@ -1029,7 +1079,6 @@ static void build_piix4_pci0_int(Aml *table) { Aml *dev; Aml *crs; - Aml *field; Aml *method; uint32_t irqs; Aml *sb_scope = aml_scope("_SB"); @@ -1038,13 +1087,6 @@ static void build_piix4_pci0_int(Aml *table) aml_append(pci0_scope, build_prt(true)); aml_append(sb_scope, pci0_scope); - field = aml_field("PCI0.ISA.P40C", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); - aml_append(field, aml_named_field("PRQ0", 8)); - aml_append(field, aml_named_field("PRQ1", 8)); - aml_append(field, aml_named_field("PRQ2", 8)); - aml_append(field, aml_named_field("PRQ3", 8)); - aml_append(sb_scope, field); - aml_append(sb_scope, build_irq_status_method()); aml_append(sb_scope, build_iqcr_method(true)); @@ -1148,7 +1190,6 @@ static Aml *build_q35_routing_table(const char *str) static void build_q35_pci0_int(Aml *table) { - Aml *field; Aml *method; Aml *sb_scope = aml_scope("_SB"); Aml *pci0_scope = aml_scope("PCI0"); @@ -1185,18 +1226,6 @@ static void build_q35_pci0_int(Aml *table) aml_append(pci0_scope, method); aml_append(sb_scope, pci0_scope); - field = aml_field("PCI0.ISA.PIRQ", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); - aml_append(field, aml_named_field("PRQA", 8)); - aml_append(field, aml_named_field("PRQB", 8)); - aml_append(field, aml_named_field("PRQC", 8)); - aml_append(field, aml_named_field("PRQD", 8)); - aml_append(field, aml_reserved_field(0x20)); - aml_append(field, aml_named_field("PRQE", 8)); - aml_append(field, aml_named_field("PRQF", 8)); - aml_append(field, aml_named_field("PRQG", 8)); - aml_append(field, aml_named_field("PRQH", 8)); - aml_append(sb_scope, field); - aml_append(sb_scope, build_irq_status_method()); aml_append(sb_scope, build_iqcr_method(false)); @@ -1261,40 +1290,6 @@ static Aml *build_q35_dram_controller(const AcpiMcfgInfo *mcfg) return dev; } -static void build_q35_isa_bridge(Aml *table) -{ - Aml *dev; - Aml *scope; - - scope = aml_scope("_SB.PCI0"); - dev = aml_device("ISA"); - aml_append(dev, aml_name_decl("_ADR", aml_int(0x001F0000))); - - /* ICH9 PCI to ISA irq remapping */ - aml_append(dev, aml_operation_region("PIRQ", AML_PCI_CONFIG, - aml_int(0x60), 0x0C)); - - aml_append(scope, dev); - aml_append(table, scope); -} - -static void build_piix4_isa_bridge(Aml *table) -{ - Aml *dev; - Aml *scope; - - scope = aml_scope("_SB.PCI0"); - dev = aml_device("ISA"); - aml_append(dev, aml_name_decl("_ADR", aml_int(0x00010000))); - - /* PIIX PCI to ISA irq remapping */ - aml_append(dev, aml_operation_region("P40C", AML_PCI_CONFIG, - aml_int(0x60), 0x04)); - - aml_append(scope, dev); - aml_append(table, scope); -} - static void build_x86_acpi_pci_hotplug(Aml *table, uint64_t pcihp_addr) { Aml *scope; @@ -1346,7 +1341,7 @@ static void build_x86_acpi_pci_hotplug(Aml *table, uint64_t pcihp_addr) aml_append(method, aml_return(aml_local(0))); aml_append(scope, method); - aml_append(scope, aml_pci_device_dsm()); + aml_append(scope, aml_pci_pdsm()); aml_append(table, scope); } @@ -1401,17 +1396,6 @@ static Aml *build_q35_osc_method(bool enable_native_pcie_hotplug) return method; } -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("_ADR", aml_int(devnr << 16 | func))); - build_acpi_ipmi_devices(dev, BUS(smbus), "\\_SB.PCI0.SMB0"); - aml_append(scope, dev); - aml_append(table, scope); -} - static void build_acpi0017(Aml *table) { Aml *dev, *scope, *method; @@ -1421,8 +1405,9 @@ static void build_acpi0017(Aml *table) aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0017"))); method = aml_method("_STA", 0, AML_NOTSERIALIZED); - aml_append(method, aml_return(aml_int(0x01))); + aml_append(method, aml_return(aml_int(0x0B))); aml_append(dev, method); + build_cxl_dsm_method(dev); aml_append(scope, dev); aml_append(table, scope); @@ -1433,6 +1418,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm, AcpiMiscInfo *misc, Range *pci_hole, Range *pci_hole64, MachineState *machine) { + Object *i440fx = object_resolve_type_unambiguous(TYPE_I440FX_PCI_HOST_BRIDGE, + NULL); + Object *q35 = object_resolve_type_unambiguous(TYPE_Q35_HOST_DEVICE, NULL); CrsRangeEntry *entry; Aml *dsdt, *sb_scope, *scope, *dev, *method, *field, *pkg, *crs; CrsRangeSet crs_range_set; @@ -1453,36 +1441,33 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = x86ms->oem_id, .oem_table_id = x86ms->oem_table_id }; + assert(!!i440fx != !!q35); + acpi_table_begin(&table, table_data); dsdt = init_aml_allocator(); build_dbg_aml(dsdt); - if (misc->is_piix4) { + if (i440fx) { sb_scope = aml_scope("_SB"); dev = aml_device("PCI0"); aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03"))); - aml_append(dev, aml_name_decl("_ADR", aml_int(0))); aml_append(dev, aml_name_decl("_UID", aml_int(pcmc->pci_root_uid))); + aml_append(dev, aml_pci_edsm()); aml_append(sb_scope, dev); aml_append(dsdt, sb_scope); - if (misc->has_hpet) { - build_hpet_aml(dsdt); - } - build_piix4_isa_bridge(dsdt); - build_isa_devices_aml(dsdt); if (pm->pcihp_bridge_en || pm->pcihp_root_en) { build_x86_acpi_pci_hotplug(dsdt, pm->pcihp_io_base); } build_piix4_pci0_int(dsdt); - } else { + } else if (q35) { sb_scope = aml_scope("_SB"); dev = aml_device("PCI0"); aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08"))); aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03"))); - aml_append(dev, aml_name_decl("_ADR", aml_int(0))); aml_append(dev, aml_name_decl("_UID", aml_int(pcmc->pci_root_uid))); aml_append(dev, build_q35_osc_method(!pm->pcihp_bridge_en)); + aml_append(dev, aml_pci_edsm()); aml_append(sb_scope, dev); if (mcfg_valid) { aml_append(sb_scope, build_q35_dram_controller(&mcfg)); @@ -1497,14 +1482,14 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(crs, aml_io( AML_DECODE16, - ACPI_PORT_SMI_CMD, - ACPI_PORT_SMI_CMD, + pm->fadt.smi_cmd, + pm->fadt.smi_cmd, 1, 2) ); aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(dev, aml_operation_region("SMIR", AML_SYSTEM_IO, - aml_int(ACPI_PORT_SMI_CMD), 2)); + aml_int(pm->fadt.smi_cmd), 2)); field = aml_field("SMIR", AML_BYTE_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); aml_append(field, aml_named_field("SMIC", 8)); @@ -1515,18 +1500,14 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dsdt, sb_scope); - if (misc->has_hpet) { - build_hpet_aml(dsdt); - } - build_q35_isa_bridge(dsdt); - build_isa_devices_aml(dsdt); if (pm->pcihp_bridge_en) { build_x86_acpi_pci_hotplug(dsdt, pm->pcihp_io_base); } build_q35_pci0_int(dsdt); - if (pcms->smbus && !pcmc->do_not_add_smb_acpi) { - build_smb0(dsdt, pcms->smbus, ICH9_SMB_DEV, ICH9_SMB_FUNC); - } + } + + if (misc->has_hpet) { + build_hpet_aml(dsdt); } if (vmbus_bridge) { @@ -1535,37 +1516,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dsdt, sb_scope); } - if (pcmc->legacy_cpu_hotplug) { - build_legacy_cpu_hotplug_aml(dsdt, machine, pm->cpu_hp_io_base); - } else { - CPUHotplugFeatures opts = { - .acpi_1_compatible = true, .has_legacy_cphp = true, - .smi_path = pm->smi_on_cpuhp ? "\\_SB.PCI0.SMI0.SMIC" : NULL, - .fw_unplugs_cpu = pm->smi_on_cpu_unplug, - }; - build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base, - "\\_SB.PCI0", "\\_GPE._E02"); - } - - if (pcms->memhp_io_base && nr_mem) { - build_memory_hotplug_aml(dsdt, nr_mem, "\\_SB.PCI0", - "\\_GPE._E03", AML_SYSTEM_IO, - pcms->memhp_io_base); - } - scope = aml_scope("_GPE"); { aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006"))); - - if (pm->pcihp_bridge_en || pm->pcihp_root_en) { - method = aml_method("_E01", 0, AML_NOTSERIALIZED); - aml_append(method, - aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF)); - aml_append(method, aml_call0("\\_SB.PCI0.PCNT")); - aml_append(method, aml_release(aml_name("\\_SB.PCI0.BLCK"))); - aml_append(scope, method); - } - if (machine->nvdimms_state->is_enabled) { method = aml_method("_E04", 0, AML_NOTSERIALIZED); aml_append(method, aml_notify(aml_name("\\_SB.NVDR"), @@ -1575,8 +1528,26 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, } aml_append(dsdt, scope); + if (pcmc->legacy_cpu_hotplug) { + build_legacy_cpu_hotplug_aml(dsdt, machine, pm->cpu_hp_io_base); + } else { + CPUHotplugFeatures opts = { + .acpi_1_compatible = true, .has_legacy_cphp = true, + .smi_path = pm->smi_on_cpuhp ? "\\_SB.PCI0.SMI0.SMIC" : NULL, + .fw_unplugs_cpu = pm->smi_on_cpu_unplug, + }; + build_cpus_aml(dsdt, machine, opts, pc_madt_cpu_entry, + pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02"); + } + + if (pcms->memhp_io_base && nr_mem) { + build_memory_hotplug_aml(dsdt, nr_mem, "\\_SB.PCI0", + "\\_GPE._E03", AML_SYSTEM_IO, + pcms->memhp_io_base); + } + crs_range_set_init(&crs_range_set); - bus = PC_MACHINE(machine)->bus; + bus = PC_MACHINE(machine)->pcibus; if (bus) { QLIST_FOREACH(bus, &bus->child, sibling) { uint8_t bus_num = pci_bus_num(bus); @@ -1601,14 +1572,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dev, aml_name_decl("_UID", aml_int(bus_num))); aml_append(dev, aml_name_decl("_BBN", aml_int(bus_num))); if (pci_bus_is_cxl(bus)) { - struct Aml *pkg = aml_package(2); + struct Aml *aml_pkg = aml_package(2); aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0016"))); - aml_append(pkg, aml_eisaid("PNP0A08")); - aml_append(pkg, aml_eisaid("PNP0A03")); - aml_append(dev, aml_name_decl("_CID", pkg)); - aml_append(dev, aml_name_decl("_ADR", aml_int(0))); - aml_append(dev, aml_name_decl("_UID", aml_int(bus_num))); + aml_append(aml_pkg, aml_eisaid("PNP0A08")); + aml_append(aml_pkg, aml_eisaid("PNP0A03")); + aml_append(dev, aml_name_decl("_CID", aml_pkg)); build_cxl_osc_method(dev); } else if (pci_bus_is_express(bus)) { aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08"))); @@ -1633,7 +1602,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, /* Handle the ranges for the PXB expanders */ if (pci_bus_is_cxl(bus)) { - MemoryRegion *mr = &machine->cxl_devices_state->host_mr; + MemoryRegion *mr = &pcms->cxl_devices_state.host_mr; uint64_t base = mr->addr; cxl_present = true; @@ -1796,111 +1765,19 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dsdt, scope); } - if (misc->applesmc_io_base) { - scope = aml_scope("\\_SB.PCI0.ISA"); - dev = aml_device("SMC"); - - aml_append(dev, aml_name_decl("_HID", aml_eisaid("APP0001"))); - /* device present, functioning, decoding, not shown in UI */ - aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); - - crs = aml_resource_template(); - aml_append(crs, - aml_io(AML_DECODE16, misc->applesmc_io_base, misc->applesmc_io_base, - 0x01, APPLESMC_MAX_DATA_LENGTH) - ); - aml_append(crs, aml_irq_no_flags(6)); - aml_append(dev, aml_name_decl("_CRS", crs)); - - aml_append(scope, dev); - aml_append(dsdt, scope); - } - - if (misc->pvpanic_port) { - scope = aml_scope("\\_SB.PCI0.ISA"); - - dev = aml_device("PEVT"); - aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0001"))); - - crs = aml_resource_template(); - aml_append(crs, - aml_io(AML_DECODE16, misc->pvpanic_port, misc->pvpanic_port, 1, 1) - ); - aml_append(dev, aml_name_decl("_CRS", crs)); - - aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO, - aml_int(misc->pvpanic_port), 1)); - field = aml_field("PEOR", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); - aml_append(field, aml_named_field("PEPT", 8)); - aml_append(dev, field); - - /* device present, functioning, decoding, shown in UI */ - aml_append(dev, aml_name_decl("_STA", aml_int(0xF))); - - method = aml_method("RDPT", 0, AML_NOTSERIALIZED); - aml_append(method, aml_store(aml_name("PEPT"), aml_local(0))); - aml_append(method, aml_return(aml_local(0))); - aml_append(dev, method); - - method = aml_method("WRPT", 1, AML_NOTSERIALIZED); - aml_append(method, aml_store(aml_arg(0), aml_name("PEPT"))); - aml_append(dev, method); - - aml_append(scope, dev); - aml_append(dsdt, scope); - } - sb_scope = aml_scope("\\_SB"); { - Object *pci_host; - PCIBus *bus = NULL; - - pci_host = acpi_get_i386_pci_host(); + Object *pci_host = acpi_get_i386_pci_host(); if (pci_host) { - bus = PCI_HOST_BRIDGE(pci_host)->bus; - } - - if (bus) { - Aml *scope = aml_scope("PCI0"); + PCIBus *pbus = PCI_HOST_BRIDGE(pci_host)->bus; + Aml *ascope = aml_scope("PCI0"); /* Scan all PCI buses. Generate tables to support hotplug. */ - build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en); - -#ifdef CONFIG_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", - aml_string("MSFT0101"))); - aml_append(dev, - aml_name_decl("_STR", - aml_string("TPM 2.0 Device"))); - } else { - dev = aml_device("ISA.TPM"); - aml_append(dev, aml_name_decl("_HID", - aml_eisaid("PNP0C31"))); - } - aml_append(dev, aml_name_decl("_UID", aml_int(1))); - - aml_append(dev, aml_name_decl("_STA", aml_int(0xF))); - crs = aml_resource_template(); - aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE, - TPM_TIS_ADDR_SIZE, AML_READ_WRITE)); - /* - FIXME: TPM_TIS_IRQ=5 conflicts with PNP0C0F irqs, - Rewrite to take IRQ from TPM device model and - fix default IRQ value there to use some unused IRQ - */ - /* aml_append(crs, aml_irq_no_flags(TPM_TIS_IRQ)); */ - aml_append(dev, aml_name_decl("_CRS", crs)); - - tpm_build_ppi_acpi(tpm, dev); - - aml_append(scope, dev); + build_append_pci_bus_devices(ascope, pbus); + if (object_property_find(OBJECT(pbus), ACPI_PCIHP_PROP_BSEL)) { + build_append_pcihp_slots(ascope, pbus); } -#endif - - aml_append(sb_scope, scope); + aml_append(sb_scope, ascope); } } @@ -1948,6 +1825,32 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, } aml_append(dsdt, sb_scope); + if (pm->pcihp_bridge_en || pm->pcihp_root_en) { + bool has_pcnt; + + Object *pci_host = acpi_get_i386_pci_host(); + PCIBus *b = PCI_HOST_BRIDGE(pci_host)->bus; + + scope = aml_scope("\\_SB.PCI0"); + has_pcnt = build_append_notfication_callback(scope, b); + if (has_pcnt) { + aml_append(dsdt, scope); + } + + scope = aml_scope("_GPE"); + { + method = aml_method("_E01", 0, AML_NOTSERIALIZED); + if (has_pcnt) { + aml_append(method, + aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF)); + aml_append(method, aml_call0("\\_SB.PCI0.PCNT")); + aml_append(method, aml_release(aml_name("\\_SB.PCI0.BLCK"))); + } + aml_append(scope, method); + } + aml_append(dsdt, scope); + } + /* copy AML table into ACPI tables blob and patch header there */ g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); acpi_table_end(linker, &table); @@ -2035,12 +1938,8 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) MachineClass *mc = MACHINE_GET_CLASS(machine); X86MachineState *x86ms = X86_MACHINE(machine); const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine); - PCMachineState *pcms = PC_MACHINE(machine); int nb_numa_nodes = machine->numa_state->num_nodes; NodeInfo *numa_info = machine->numa_state->nodes; - ram_addr_t hotpluggable_address_space_size = - object_property_get_int(OBJECT(pcms), PC_MACHINE_DEVMEM_REGION_SIZE, - NULL); AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = x86ms->oem_id, .oem_table_id = x86ms->oem_table_id }; @@ -2120,7 +2019,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) build_srat_memory(table_data, mem_base, mem_len, i - 1, MEM_AFFINITY_ENABLED); } - mem_base = 1ULL << 32; + mem_base = x86ms->above_4g_mem_start; mem_len = next_base - x86ms->below_4g_mem_size; next_base = mem_base + mem_len; } @@ -2148,6 +2047,8 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) build_srat_memory(table_data, 0, 0, 0, MEM_AFFINITY_NOFLAGS); } + build_srat_generic_pci_initiator(table_data); + /* * Entry is required for Windows to enable memory hotplug in OS * and for Linux to enable SWIOTLB when booted with less than @@ -2156,9 +2057,10 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) * Memory devices may override proximity set by this entry, * providing _PXM method if necessary. */ - if (hotpluggable_address_space_size) { + if (machine->device_memory) { build_srat_memory(table_data, machine->device_memory->base, - hotpluggable_address_space_size, nb_numa_nodes - 1, + memory_region_size(&machine->device_memory->mr), + nb_numa_nodes - 1, MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); } @@ -2166,7 +2068,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) } /* - * Insert DMAR scope for PCI bridges and endpoint devcie + * Insert DMAR scope for PCI bridges and endpoint devices */ static void insert_scope(PCIBus *bus, PCIDevice *dev, void *opaque) @@ -2424,30 +2326,23 @@ static void build_amd_iommu(GArray *table_data, BIOSLinker *linker, const char *oem_id, const char *oem_table_id) { - int ivhd_table_len = 24; AMDVIState *s = AMD_IOMMU_DEVICE(x86_iommu_get_default()); GArray *ivhd_blob = g_array_new(false, true, 1); AcpiTable table = { .sig = "IVRS", .rev = 1, .oem_id = oem_id, .oem_table_id = oem_table_id }; + uint64_t feature_report; acpi_table_begin(&table, table_data); /* IVinfo - IO virtualization information common to all * IOMMU units in a system */ - build_append_int_noprefix(table_data, 40UL << 8/* PASize */, 4); + build_append_int_noprefix(table_data, + (1UL << 0) | /* EFRSup */ + (40UL << 8), /* PASize */ + 4); /* reserved */ build_append_int_noprefix(table_data, 0, 8); - /* IVHD definition - type 10h */ - build_append_int_noprefix(table_data, 0x10, 1); - /* virtualization flags */ - build_append_int_noprefix(table_data, - (1UL << 0) | /* HtTunEn */ - (1UL << 4) | /* iotblSup */ - (1UL << 6) | /* PrefSup */ - (1UL << 7), /* PPRSup */ - 1); - /* * A PCI bus walk, for each PCI host bridge, is necessary to create a * complete set of IVHD entries. Do this into a separate blob so that we @@ -2467,22 +2362,40 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker, const char *oem_id, build_append_int_noprefix(ivhd_blob, 0x0000001, 4); } - ivhd_table_len += ivhd_blob->len; - /* * When interrupt remapping is supported, we add a special IVHD device - * for type IO-APIC. + * for type IO-APIC + * Refer to spec - Table 95: IVHD device entry type codes + * + * Linux IOMMU driver checks for the special IVHD device (type IO-APIC). + * See Linux kernel commit 'c2ff5cf5294bcbd7fa50f7d860e90a66db7e5059' */ if (x86_iommu_ir_supported(x86_iommu_get_default())) { - ivhd_table_len += 8; + build_append_int_noprefix(ivhd_blob, + (0x1ull << 56) | /* type IOAPIC */ + (IOAPIC_SB_DEVID << 40) | /* IOAPIC devid */ + 0x48, /* special device */ + 8); } + /* IVHD definition - type 10h */ + build_append_int_noprefix(table_data, 0x10, 1); + /* virtualization flags */ + build_append_int_noprefix(table_data, + (1UL << 0) | /* HtTunEn */ + (1UL << 4) | /* iotblSup */ + (1UL << 6) | /* PrefSup */ + (1UL << 7), /* PPRSup */ + 1); + /* IVHD length */ - build_append_int_noprefix(table_data, ivhd_table_len, 2); + build_append_int_noprefix(table_data, ivhd_blob->len + 24, 2); /* DeviceID */ - build_append_int_noprefix(table_data, s->devid, 2); + build_append_int_noprefix(table_data, + object_property_get_int(OBJECT(&s->pci), "addr", + &error_abort), 2); /* Capability offset */ - build_append_int_noprefix(table_data, s->capab_offset, 2); + build_append_int_noprefix(table_data, s->pci.capab_offset, 2); /* IOMMU base address */ build_append_int_noprefix(table_data, s->mmio.addr, 8); /* PCI Segment Group */ @@ -2490,31 +2403,53 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker, const char *oem_id, /* IOMMU info */ build_append_int_noprefix(table_data, 0, 2); /* IOMMU Feature Reporting */ - build_append_int_noprefix(table_data, - (48UL << 30) | /* HATS */ - (48UL << 28) | /* GATS */ - (1UL << 2) | /* GTSup */ - (1UL << 6), /* GASup */ - 4); + feature_report = (48UL << 30) | /* HATS */ + (48UL << 28) | /* GATS */ + (1UL << 2) | /* GTSup */ + (1UL << 6); /* GASup */ + if (s->xtsup) { + feature_report |= (1UL << 0); /* XTSup */ + } + build_append_int_noprefix(table_data, feature_report, 4); /* IVHD entries as found above */ g_array_append_vals(table_data, ivhd_blob->data, ivhd_blob->len); - g_array_free(ivhd_blob, TRUE); - /* - * Add a special IVHD device type. - * Refer to spec - Table 95: IVHD device entry type codes - * - * Linux IOMMU driver checks for the special IVHD device (type IO-APIC). - * See Linux kernel commit 'c2ff5cf5294bcbd7fa50f7d860e90a66db7e5059' - */ - if (x86_iommu_ir_supported(x86_iommu_get_default())) { - build_append_int_noprefix(table_data, - (0x1ull << 56) | /* type IOAPIC */ - (IOAPIC_SB_DEVID << 40) | /* IOAPIC devid */ - 0x48, /* special device */ - 8); - } + /* IVHD definition - type 11h */ + build_append_int_noprefix(table_data, 0x11, 1); + /* virtualization flags */ + build_append_int_noprefix(table_data, + (1UL << 0) | /* HtTunEn */ + (1UL << 4), /* iotblSup */ + 1); + + /* IVHD length */ + build_append_int_noprefix(table_data, ivhd_blob->len + 40, 2); + /* DeviceID */ + build_append_int_noprefix(table_data, + object_property_get_int(OBJECT(&s->pci), "addr", + &error_abort), 2); + /* Capability offset */ + build_append_int_noprefix(table_data, s->pci.capab_offset, 2); + /* IOMMU base address */ + build_append_int_noprefix(table_data, s->mmio.addr, 8); + /* PCI Segment Group */ + build_append_int_noprefix(table_data, 0, 2); + /* IOMMU info */ + build_append_int_noprefix(table_data, 0, 2); + /* IOMMU Attributes */ + build_append_int_noprefix(table_data, 0, 4); + /* EFR Register Image */ + build_append_int_noprefix(table_data, + amdvi_extended_feature_register(s), + 8); + /* EFR Register Image 2 */ + build_append_int_noprefix(table_data, 0, 8); + + /* IVHD entries as found above */ + g_array_append_vals(table_data, ivhd_blob->data, ivhd_blob->len); + + g_array_free(ivhd_blob, TRUE); acpi_table_end(linker, &table); } @@ -2632,8 +2567,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) acpi_add_table(table_offsets, tables_blob); acpi_build_madt(tables_blob, tables->linker, x86ms, - ACPI_DEVICE_IF(x86ms->acpi_dev), x86ms->oem_id, - x86ms->oem_table_id); + x86ms->oem_id, x86ms->oem_table_id); #ifdef CONFIG_ACPI_ERST { @@ -2711,9 +2645,9 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) machine->nvdimms_state, machine->ram_slots, x86ms->oem_id, x86ms->oem_table_id); } - if (machine->cxl_devices_state->is_enabled) { - cxl_build_cedt(machine, table_offsets, tables_blob, tables->linker, - x86ms->oem_id, x86ms->oem_table_id); + if (pcms->cxl_devices_state.is_enabled) { + cxl_build_cedt(table_offsets, tables_blob, tables->linker, + x86ms->oem_id, x86ms->oem_table_id, &pcms->cxl_devices_state); } acpi_add_table(table_offsets, tables_blob); @@ -2780,24 +2714,26 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) int legacy_table_size = ROUND_UP(tables_blob->len - aml_len + legacy_aml_len, ACPI_BUILD_ALIGN_SIZE); - if (tables_blob->len > legacy_table_size) { + if ((tables_blob->len > legacy_table_size) && + !pcmc->resizable_acpi_blob) { /* Should happen only with PCI bridges and -M pc-i440fx-2.0. */ warn_report("ACPI table size %u exceeds %d bytes," " migration may not work", tables_blob->len, legacy_table_size); error_printf("Try removing CPUs, NUMA nodes, memory slots" - " or PCI bridges."); + " or PCI bridges.\n"); } g_array_set_size(tables_blob, legacy_table_size); } else { /* Make sure we have a buffer in case we need to resize the tables. */ - if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) { + if ((tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) && + !pcmc->resizable_acpi_blob) { /* As of QEMU 2.1, this fires with 160 VCPUs and 255 memory slots. */ warn_report("ACPI table size %u exceeds %d bytes," " migration may not work", tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2); error_printf("Try removing CPUs, NUMA nodes, memory slots" - " or PCI bridges."); + " or PCI bridges.\n"); } acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE); } @@ -2858,7 +2794,7 @@ static const VMStateDescription vmstate_acpi_build = { .name = "acpi_build", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(patched, AcpiBuildState), VMSTATE_END_OF_LIST() }, diff --git a/hw/i386/acpi-common.c b/hw/i386/acpi-common.c index 4aaafbdd7b..20f19269da 100644 --- a/hw/i386/acpi-common.c +++ b/hw/i386/acpi-common.c @@ -27,15 +27,13 @@ #include "hw/acpi/acpi.h" #include "hw/acpi/aml-build.h" #include "hw/acpi/utils.h" -#include "hw/i386/pc.h" #include "target/i386/cpu.h" #include "acpi-build.h" #include "acpi-common.h" -void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, - const CPUArchIdList *apic_ids, GArray *entry, - bool force_enabled) +void pc_madt_cpu_entry(int uid, const CPUArchIdList *apic_ids, + GArray *entry, bool force_enabled) { uint32_t apic_id = apic_ids->cpus[uid].arch_id; /* Flags – Local APIC Flags */ @@ -95,15 +93,15 @@ build_xrupt_override(GArray *entry, uint8_t src, uint32_t gsi, uint16_t flags) * 5.2.8 Multiple APIC Description Table */ void acpi_build_madt(GArray *table_data, BIOSLinker *linker, - X86MachineState *x86ms, AcpiDeviceIf *adev, + X86MachineState *x86ms, const char *oem_id, const char *oem_table_id) { int i; bool x2apic_mode = false; MachineClass *mc = MACHINE_GET_CLASS(x86ms); + X86MachineClass *x86mc = X86_MACHINE_GET_CLASS(x86ms); const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(MACHINE(x86ms)); - AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(adev); - AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = oem_id, + AcpiTable table = { .sig = "APIC", .rev = 3, .oem_id = oem_id, .oem_table_id = oem_table_id }; acpi_table_begin(&table, table_data); @@ -112,7 +110,7 @@ void acpi_build_madt(GArray *table_data, BIOSLinker *linker, build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */ for (i = 0; i < apic_ids->len; i++) { - adevc->madt_cpu(adev, i, apic_ids, table_data, false); + pc_madt_cpu_entry(i, apic_ids, table_data, false); if (apic_ids->cpus[i].arch_id > 254) { x2apic_mode = true; } @@ -124,7 +122,7 @@ void acpi_build_madt(GArray *table_data, BIOSLinker *linker, IO_APIC_SECONDARY_ADDRESS, IO_APIC_SECONDARY_IRQBASE); } - if (x86ms->apic_xrupt_override) { + if (x86mc->apic_xrupt_override) { build_xrupt_override(table_data, 0, 2, 0 /* Flags: Conforms to the specifications of the bus */); } diff --git a/hw/i386/acpi-common.h b/hw/i386/acpi-common.h index a68825acf5..e305aaac15 100644 --- a/hw/i386/acpi-common.h +++ b/hw/i386/acpi-common.h @@ -1,15 +1,17 @@ #ifndef HW_I386_ACPI_COMMON_H #define HW_I386_ACPI_COMMON_H -#include "hw/acpi/acpi_dev_interface.h" +#include "hw/boards.h" #include "hw/acpi/bios-linker-loader.h" #include "hw/i386/x86.h" /* Default IOAPIC ID */ #define ACPI_BUILD_IOAPIC_ID 0x0 +void pc_madt_cpu_entry(int uid, const CPUArchIdList *apic_ids, + GArray *entry, bool force_enabled); void acpi_build_madt(GArray *table_data, BIOSLinker *linker, - X86MachineState *x86ms, AcpiDeviceIf *adev, + X86MachineState *x86ms, const char *oem_id, const char *oem_table_id); #endif diff --git a/hw/i386/acpi-microvm.c b/hw/i386/acpi-microvm.c index fb09185cbd..279da6b4aa 100644 --- a/hw/i386/acpi-microvm.c +++ b/hw/i386/acpi-microvm.c @@ -26,6 +26,7 @@ #include "exec/memory.h" #include "hw/acpi/acpi.h" +#include "hw/acpi/acpi_aml_interface.h" #include "hw/acpi/aml-build.h" #include "hw/acpi/bios-linker-loader.h" #include "hw/acpi/generic_event_device.h" @@ -36,6 +37,7 @@ #include "hw/pci/pci.h" #include "hw/pci/pcie_host.h" #include "hw/usb/xhci.h" +#include "hw/virtio/virtio-acpi.h" #include "hw/virtio/virtio-mmio.h" #include "hw/input/i8042.h" @@ -54,8 +56,8 @@ static void acpi_dsdt_add_virtio(Aml *scope, bus = sysbus_get_default(); QTAILQ_FOREACH(kid, &bus->children, sibling) { - DeviceState *dev = kid->child; - Object *obj = object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MMIO); + Object *obj = object_dynamic_cast(OBJECT(kid->child), + TYPE_VIRTIO_MMIO); if (obj) { VirtIOMMIOProxy *mmio = VIRTIO_MMIO(obj); @@ -76,19 +78,7 @@ static void acpi_dsdt_add_virtio(Aml *scope, uint32_t irq = mms->virtio_irq_base + index; hwaddr base = VIRTIO_MMIO_BASE + index * 512; hwaddr size = 512; - - Aml *dev = aml_device("VR%02u", (unsigned)index); - aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005"))); - aml_append(dev, aml_name_decl("_UID", aml_int(index))); - aml_append(dev, aml_name_decl("_CCA", aml_int(1))); - - Aml *crs = aml_resource_template(); - aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE)); - aml_append(crs, - aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, - AML_EXCLUSIVE, &irq, 1)); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(scope, dev); + virtio_acpi_dsdt_add(scope, base, size, irq, index, 1); } } } @@ -129,7 +119,7 @@ build_dsdt_microvm(GArray *table_data, BIOSLinker *linker, sb_scope = aml_scope("_SB"); fw_cfg_add_acpi_dsdt(sb_scope, x86ms->fw_cfg); - isa_build_aml(ISA_BUS(isabus), sb_scope); + qbus_build_aml(BUS(isabus), sb_scope); build_ged_aml(sb_scope, GED_DEVICE, x86ms->acpi_dev, GED_MMIO_IRQ, AML_SYSTEM_MEMORY, GED_MMIO_BASE); acpi_dsdt_add_power_button(sb_scope); @@ -213,8 +203,7 @@ static void acpi_build_microvm(AcpiBuildTables *tables, acpi_add_table(table_offsets, tables_blob); acpi_build_madt(tables_blob, tables->linker, X86_MACHINE(machine), - ACPI_DEVICE_IF(x86ms->acpi_dev), x86ms->oem_id, - x86ms->oem_table_id); + x86ms->oem_id, x86ms->oem_table_id); #ifdef CONFIG_ACPI_ERST { diff --git a/hw/i386/amd_iommu-stub.c b/hw/i386/amd_iommu-stub.c new file mode 100644 index 0000000000..d62a3732e6 --- /dev/null +++ b/hw/i386/amd_iommu-stub.c @@ -0,0 +1,26 @@ +/* + * Stubs for AMD IOMMU emulation + * + * Copyright (C) 2023 Bui Quang Minh + * + * 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 "amd_iommu.h" + +uint64_t amdvi_extended_feature_register(AMDVIState *s) +{ + return AMDVI_DEFAULT_EXT_FEATURES; +} diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 725f69095b..6d4fde72f9 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -31,6 +31,7 @@ #include "hw/i386/apic_internal.h" #include "trace.h" #include "hw/i386/apic-msidef.h" +#include "hw/qdev-properties.h" /* used AMD-Vi MMIO registers */ const char *amdvi_mmio_low[] = { @@ -74,6 +75,16 @@ typedef struct AMDVIIOTLBEntry { uint64_t page_mask; /* physical page size */ } AMDVIIOTLBEntry; +uint64_t amdvi_extended_feature_register(AMDVIState *s) +{ + uint64_t feature = AMDVI_DEFAULT_EXT_FEATURES; + if (s->xtsup) { + feature |= AMDVI_FEATURE_XT; + } + + return feature; +} + /* configure MMIO registers at startup/reset */ static void amdvi_set_quad(AMDVIState *s, hwaddr addr, uint64_t val, uint64_t romask, uint64_t w1cmask) @@ -259,7 +270,7 @@ static void amdvi_log_command_error(AMDVIState *s, hwaddr addr) pci_word_test_and_set_mask(s->pci.dev.config + PCI_STATUS, PCI_STATUS_SIG_TARGET_ABORT); } -/* log an illegal comand event +/* log an illegal command event * @addr : address of illegal command */ static void amdvi_log_illegalcom_error(AMDVIState *s, uint16_t info, @@ -767,7 +778,7 @@ static void amdvi_mmio_write(void *opaque, hwaddr addr, uint64_t val, break; case AMDVI_MMIO_COMMAND_BASE: amdvi_mmio_reg_write(s, size, val, addr); - /* FIXME - make sure System Software has finished writing incase + /* FIXME - make sure System Software has finished writing in case * it writes in chucks less than 8 bytes in a robust way.As for * now, this hacks works for the linux driver */ @@ -1155,7 +1166,12 @@ static int amdvi_int_remap_ga(AMDVIState *iommu, irq->vector = irte.hi.fields.vector; irq->dest_mode = irte.lo.fields_remap.dm; irq->redir_hint = irte.lo.fields_remap.rq_eoi; - irq->dest = irte.lo.fields_remap.destination; + if (iommu->xtsup) { + irq->dest = irte.lo.fields_remap.destination | + (irte.hi.fields.destination_hi << 24); + } else { + irq->dest = irte.lo.fields_remap.destination & 0xff; + } return 0; } @@ -1246,13 +1262,8 @@ static int amdvi_int_remap_msi(AMDVIState *iommu, return -AMDVI_IR_ERR; } - if (origin->address & AMDVI_MSI_ADDR_HI_MASK) { - trace_amdvi_err("MSI address high 32 bits non-zero when " - "Interrupt Remapping enabled."); - return -AMDVI_IR_ERR; - } - - if ((origin->address & AMDVI_MSI_ADDR_LO_MASK) != APIC_DEFAULT_ADDRESS) { + if (origin->address < AMDVI_INT_ADDR_FIRST || + origin->address + sizeof(origin->data) > AMDVI_INT_ADDR_LAST + 1) { trace_amdvi_err("MSI is not from IOAPIC."); return -AMDVI_IR_ERR; } @@ -1368,7 +1379,7 @@ static MemTxResult amdvi_mem_ir_write(void *opaque, hwaddr addr, return MEMTX_ERROR; } - apic_get_class()->send_msi(&to); + apic_get_class(NULL)->send_msi(&to); trace_amdvi_mem_ir_write(to.address, to.data); return MEMTX_OK; @@ -1455,6 +1466,10 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &iommu_as[devfn]->as; } +static const PCIIOMMUOps amdvi_iommu_ops = { + .get_address_space = amdvi_host_dma_iommu, +}; + static const MemoryRegionOps mmio_mem_ops = { .read = amdvi_mmio_read, .write = amdvi_mmio_write, @@ -1506,26 +1521,52 @@ static void amdvi_init(AMDVIState *s) /* reset MMIO */ memset(s->mmior, 0, AMDVI_MMIO_SIZE); - amdvi_set_quad(s, AMDVI_MMIO_EXT_FEATURES, AMDVI_EXT_FEATURES, - 0xffffffffffffffef, 0); + amdvi_set_quad(s, AMDVI_MMIO_EXT_FEATURES, + amdvi_extended_feature_register(s), + 0xffffffffffffffef, 0); amdvi_set_quad(s, AMDVI_MMIO_STATUS, 0, 0x98, 0x67); +} + +static void amdvi_pci_realize(PCIDevice *pdev, Error **errp) +{ + AMDVIPCIState *s = AMD_IOMMU_PCI(pdev); + int ret; + + ret = pci_add_capability(pdev, AMDVI_CAPAB_ID_SEC, 0, + AMDVI_CAPAB_SIZE, errp); + if (ret < 0) { + return; + } + s->capab_offset = ret; + + ret = pci_add_capability(pdev, PCI_CAP_ID_MSI, 0, + AMDVI_CAPAB_REG_SIZE, errp); + if (ret < 0) { + return; + } + ret = pci_add_capability(pdev, PCI_CAP_ID_HT, 0, + AMDVI_CAPAB_REG_SIZE, errp); + if (ret < 0) { + return; + } + + if (msi_init(pdev, 0, 1, true, false, errp) < 0) { + return; + } /* reset device ident */ - pci_config_set_vendor_id(s->pci.dev.config, PCI_VENDOR_ID_AMD); - pci_config_set_prog_interface(s->pci.dev.config, 00); - pci_config_set_device_id(s->pci.dev.config, s->devid); - pci_config_set_class(s->pci.dev.config, 0x0806); + pci_config_set_prog_interface(pdev->config, 0); /* reset AMDVI specific capabilities, all r/o */ - pci_set_long(s->pci.dev.config + s->capab_offset, AMDVI_CAPAB_FEATURES); - pci_set_long(s->pci.dev.config + s->capab_offset + AMDVI_CAPAB_BAR_LOW, - s->mmio.addr & ~(0xffff0000)); - pci_set_long(s->pci.dev.config + s->capab_offset + AMDVI_CAPAB_BAR_HIGH, - (s->mmio.addr & ~(0xffff)) >> 16); - pci_set_long(s->pci.dev.config + s->capab_offset + AMDVI_CAPAB_RANGE, + pci_set_long(pdev->config + s->capab_offset, AMDVI_CAPAB_FEATURES); + pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_BAR_LOW, + AMDVI_BASE_ADDR & ~(0xffff0000)); + pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_BAR_HIGH, + (AMDVI_BASE_ADDR & ~(0xffff)) >> 16); + pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_RANGE, 0xff000000); - pci_set_long(s->pci.dev.config + s->capab_offset + AMDVI_CAPAB_MISC, 0); - pci_set_long(s->pci.dev.config + s->capab_offset + AMDVI_CAPAB_MISC, + pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_MISC, 0); + pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_MISC, AMDVI_MAX_PH_ADDR | AMDVI_MAX_GVA_ADDR | AMDVI_MAX_VA_ADDR); } @@ -1539,12 +1580,11 @@ static void amdvi_sysbus_reset(DeviceState *dev) static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) { - int ret = 0; AMDVIState *s = AMD_IOMMU_DEVICE(dev); MachineState *ms = MACHINE(qdev_get_machine()); PCMachineState *pcms = PC_MACHINE(ms); X86MachineState *x86ms = X86_MACHINE(ms); - PCIBus *bus = pcms->bus; + PCIBus *bus = pcms->pcibus; s->iotlb = g_hash_table_new_full(amdvi_uint64_hash, amdvi_uint64_equal, g_free, g_free); @@ -1553,23 +1593,6 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) if (!qdev_realize(DEVICE(&s->pci), &bus->qbus, errp)) { return; } - ret = pci_add_capability(&s->pci.dev, AMDVI_CAPAB_ID_SEC, 0, - 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, errp); - if (ret < 0) { - return; - } - ret = pci_add_capability(&s->pci.dev, PCI_CAP_ID_HT, 0, - AMDVI_CAPAB_REG_SIZE, errp); - if (ret < 0) { - return; - } /* Pseudo address space under root PCI bus. */ x86ms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID); @@ -1577,15 +1600,17 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) /* set up MMIO */ memory_region_init_io(&s->mmio, OBJECT(s), &mmio_mem_ops, s, "amdvi-mmio", AMDVI_MMIO_SIZE); - - 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", &error_abort); - msi_init(&s->pci.dev, 0, 1, true, false, errp); + memory_region_add_subregion(get_system_memory(), AMDVI_BASE_ADDR, + &s->mmio); + pci_setup_iommu(bus, &amdvi_iommu_ops, s); amdvi_init(s); } +static Property amdvi_properties[] = { + DEFINE_PROP_BOOL("xtsup", AMDVIState, xtsup, false), + DEFINE_PROP_END_OF_LIST(), +}; + static const VMStateDescription vmstate_amdvi_sysbus = { .name = "amd-iommu", .unmigratable = 1 @@ -1612,6 +1637,7 @@ static void amdvi_sysbus_class_init(ObjectClass *klass, void *data) dc->user_creatable = true; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->desc = "AMD IOMMU (AMD-Vi) DMA Remapping device"; + device_class_set_props(dc, amdvi_properties); } static const TypeInfo amdvi_sysbus = { @@ -1625,6 +1651,11 @@ static const TypeInfo amdvi_sysbus = { static void amdvi_pci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->vendor_id = PCI_VENDOR_ID_AMD; + k->class_id = 0x0806; + k->realize = amdvi_pci_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->desc = "AMD IOMMU (AMD-Vi) DMA Remapping device"; diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 79d38a3e41..73619fe9ea 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -154,6 +154,7 @@ #define AMDVI_FEATURE_PREFETCH (1ULL << 0) /* page prefetch */ #define AMDVI_FEATURE_PPR (1ULL << 1) /* PPR Support */ +#define AMDVI_FEATURE_XT (1ULL << 2) /* x2APIC Support */ #define AMDVI_FEATURE_GT (1ULL << 4) /* Guest Translation */ #define AMDVI_FEATURE_IA (1ULL << 6) /* inval all support */ #define AMDVI_FEATURE_GA (1ULL << 7) /* guest VAPIC support */ @@ -173,8 +174,9 @@ #define AMDVI_IOTLB_MAX_SIZE 1024 #define AMDVI_DEVID_SHIFT 36 -/* extended feature support */ -#define AMDVI_EXT_FEATURES (AMDVI_FEATURE_PREFETCH | AMDVI_FEATURE_PPR | \ +/* default extended feature */ +#define AMDVI_DEFAULT_EXT_FEATURES \ + (AMDVI_FEATURE_PREFETCH | AMDVI_FEATURE_PPR | \ AMDVI_FEATURE_IA | AMDVI_FEATURE_GT | AMDVI_FEATURE_HE | \ AMDVI_GATS_MODE | AMDVI_HATS_MODE | AMDVI_FEATURE_GA) @@ -210,8 +212,6 @@ #define AMDVI_INT_ADDR_FIRST 0xfee00000 #define AMDVI_INT_ADDR_LAST 0xfeefffff #define AMDVI_INT_ADDR_SIZE (AMDVI_INT_ADDR_LAST - AMDVI_INT_ADDR_FIRST + 1) -#define AMDVI_MSI_ADDR_HI_MASK (0xffffffff00000000ULL) -#define AMDVI_MSI_ADDR_LO_MASK (0x00000000ffffffffULL) /* SB IOAPIC is always on this device in AMD systems */ #define AMDVI_IOAPIC_SB_DEVID PCI_BUILD_BDF(0, PCI_DEVFN(0x14, 0)) @@ -278,8 +278,8 @@ union irte_ga_lo { dm:1, /* ------ */ guest_mode:1, - destination:8, - rsvd_1:48; + destination:24, + rsvd_1:32; } fields_remap; }; @@ -287,7 +287,8 @@ union irte_ga_hi { uint64_t val; struct { uint64_t vector:8, - rsvd_2:56; + rsvd_2:48, + destination_hi:8; } fields; }; @@ -300,27 +301,26 @@ struct irte_ga { OBJECT_DECLARE_SIMPLE_TYPE(AMDVIState, AMD_IOMMU_DEVICE) #define TYPE_AMD_IOMMU_PCI "AMDVI-PCI" +OBJECT_DECLARE_SIMPLE_TYPE(AMDVIPCIState, AMD_IOMMU_PCI) #define TYPE_AMD_IOMMU_MEMORY_REGION "amd-iommu-iommu-memory-region" typedef struct AMDVIAddressSpace AMDVIAddressSpace; /* functions to steal PCI config space */ -typedef struct AMDVIPCIState { +struct AMDVIPCIState { PCIDevice dev; /* The PCI device itself */ -} AMDVIPCIState; + uint32_t capab_offset; /* capability offset pointer */ +}; struct AMDVIState { X86IOMMUState iommu; /* IOMMU bus device */ AMDVIPCIState pci; /* IOMMU PCI device */ uint32_t version; - uint32_t capab_offset; /* capability offset pointer */ uint64_t mmio_addr; - uint32_t devid; /* auto-assigned devid */ - bool enabled; /* IOMMU enabled */ bool ats_enabled; /* address translation enabled */ bool cmdbuf_enabled; /* command buffer enabled */ @@ -367,6 +367,9 @@ struct AMDVIState { /* Interrupt remapping */ bool ga_enabled; + bool xtsup; }; +uint64_t amdvi_extended_feature_register(AMDVIState *s); + #endif diff --git a/hw/i386/e820_memory_layout.c b/hw/i386/e820_memory_layout.c index bcf9eaf837..06970ac44a 100644 --- a/hw/i386/e820_memory_layout.c +++ b/hw/i386/e820_memory_layout.c @@ -11,29 +11,11 @@ #include "e820_memory_layout.h" static size_t e820_entries; -struct e820_table e820_reserve; struct e820_entry *e820_table; int e820_add_entry(uint64_t address, uint64_t length, uint32_t type) { - int index = le32_to_cpu(e820_reserve.count); - struct e820_entry *entry; - - if (type != E820_RAM) { - /* old FW_CFG_E820_TABLE entry -- reservations only */ - if (index >= E820_NR_ENTRIES) { - return -EBUSY; - } - entry = &e820_reserve.entry[index++]; - - entry->address = cpu_to_le64(address); - entry->length = cpu_to_le64(length); - entry->type = cpu_to_le32(type); - - e820_reserve.count = cpu_to_le32(index); - } - - /* new "etc/e820" file -- include ram too */ + /* new "etc/e820" file -- include ram and reserved entries */ e820_table = g_renew(struct e820_entry, e820_table, e820_entries + 1); e820_table[e820_entries].address = cpu_to_le64(address); e820_table[e820_entries].length = cpu_to_le64(length); diff --git a/hw/i386/e820_memory_layout.h b/hw/i386/e820_memory_layout.h index 04f93780f9..7c239aa033 100644 --- a/hw/i386/e820_memory_layout.h +++ b/hw/i386/e820_memory_layout.h @@ -16,20 +16,12 @@ #define E820_NVS 4 #define E820_UNUSABLE 5 -#define E820_NR_ENTRIES 16 - struct e820_entry { uint64_t address; uint64_t length; uint32_t type; } QEMU_PACKED __attribute((__aligned__(4))); -struct e820_table { - uint32_t count; - struct e820_entry entry[E820_NR_ENTRIES]; -} QEMU_PACKED __attribute((__aligned__(4))); - -extern struct e820_table e820_reserve; extern struct e820_entry *e820_table; int e820_add_entry(uint64_t address, uint64_t length, uint32_t type); diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index a283785a8d..d802d2787f 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -24,6 +24,7 @@ #include "kvm/kvm_i386.h" #include "qapi/error.h" #include CONFIG_DEVICES +#include "target/i386/cpu.h" struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX}; @@ -36,7 +37,6 @@ const char *fw_cfg_arch_key_name(uint16_t key) {FW_CFG_ACPI_TABLES, "acpi_tables"}, {FW_CFG_SMBIOS_ENTRIES, "smbios_entries"}, {FW_CFG_IRQ0_OVERRIDE, "irq0_override"}, - {FW_CFG_E820_TABLE, "e820_table"}, {FW_CFG_HPET, "hpet"}, }; @@ -48,22 +48,34 @@ const char *fw_cfg_arch_key_name(uint16_t key) return NULL; } -void fw_cfg_build_smbios(MachineState *ms, FWCfgState *fw_cfg) +void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg, + SmbiosEntryPointType ep_type) { #ifdef CONFIG_SMBIOS uint8_t *smbios_tables, *smbios_anchor; size_t smbios_tables_len, smbios_anchor_len; struct smbios_phys_mem_area *mem_array; unsigned i, array_count; + MachineState *ms = MACHINE(pcms); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + MachineClass *mc = MACHINE_GET_CLASS(pcms); X86CPU *cpu = X86_CPU(ms->possible_cpus->cpus[0].cpu); + if (pcmc->smbios_defaults) { + /* These values are guest ABI, do not change */ + smbios_set_defaults("QEMU", mc->desc, mc->name, + pcmc->smbios_uuid_encoded); + } + /* tell smbios about cpuid version and features */ smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]); - smbios_tables = smbios_get_table_legacy(ms, &smbios_tables_len); - if (smbios_tables) { + if (pcmc->smbios_legacy_mode) { + smbios_tables = smbios_get_table_legacy(&smbios_tables_len, + &error_fatal); fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, smbios_tables, smbios_tables_len); + return; } /* build the array of physical mem area from e820 table */ @@ -77,7 +89,7 @@ void fw_cfg_build_smbios(MachineState *ms, FWCfgState *fw_cfg) array_count++; } } - smbios_get_tables(ms, mem_array, array_count, + smbios_get_tables(ms, ep_type, mem_array, array_count, &smbios_tables, &smbios_tables_len, &smbios_anchor, &smbios_anchor_len, &error_fatal); @@ -127,8 +139,6 @@ FWCfgState *fw_cfg_arch_create(MachineState *ms, #endif fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, 1); - fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, - &e820_reserve, sizeof(e820_reserve)); fw_cfg_add_file(fw_cfg, "etc/e820", e820_table, sizeof(struct e820_entry) * e820_get_num_entries()); diff --git a/hw/i386/fw_cfg.h b/hw/i386/fw_cfg.h index 275f15c1c5..92e310f5fd 100644 --- a/hw/i386/fw_cfg.h +++ b/hw/i386/fw_cfg.h @@ -10,6 +10,7 @@ #define HW_I386_FW_CFG_H #include "hw/boards.h" +#include "hw/i386/pc.h" #include "hw/nvram/fw_cfg.h" #define FW_CFG_IO_BASE 0x510 @@ -17,13 +18,13 @@ #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) -#define FW_CFG_E820_TABLE (FW_CFG_ARCH_LOCAL + 3) #define FW_CFG_HPET (FW_CFG_ARCH_LOCAL + 4) FWCfgState *fw_cfg_arch_create(MachineState *ms, uint16_t boot_cpus, uint16_t apic_id_limit); -void fw_cfg_build_smbios(MachineState *ms, FWCfgState *fw_cfg); +void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg, + SmbiosEntryPointType ep_type); void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg); void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg); diff --git a/hw/i386/generic_event_device_x86.c b/hw/i386/generic_event_device_x86.c deleted file mode 100644 index e26fb02a2e..0000000000 --- a/hw/i386/generic_event_device_x86.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * x86 variant of the generic event device for hw reduced acpi - * - * 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. - */ - -#include "qemu/osdep.h" -#include "hw/acpi/generic_event_device.h" -#include "hw/i386/pc.h" - -static void acpi_ged_x86_class_init(ObjectClass *class, void *data) -{ - AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class); - - adevc->madt_cpu = pc_madt_cpu_entry; -} - -static const TypeInfo acpi_ged_x86_info = { - .name = TYPE_ACPI_GED_X86, - .parent = TYPE_ACPI_GED, - .class_init = acpi_ged_x86_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { TYPE_ACPI_DEVICE_IF }, - { } - } -}; - -static void acpi_ged_x86_register_types(void) -{ - type_register_static(&acpi_ged_x86_info); -} - -type_init(acpi_ged_x86_register_types) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 2162394e08..cc8e59674e 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -49,17 +49,24 @@ /* pe operations */ #define VTD_PE_GET_TYPE(pe) ((pe)->val[0] & VTD_SM_PASID_ENTRY_PGTT) #define VTD_PE_GET_LEVEL(pe) (2 + (((pe)->val[0] >> 2) & VTD_SM_PASID_ENTRY_AW)) -#define VTD_PE_GET_FPD_ERR(ret_fr, is_fpd_set, s, source_id, addr, is_write) {\ - if (ret_fr) { \ - ret_fr = -ret_fr; \ - if (is_fpd_set && vtd_is_qualified_fault(ret_fr)) { \ - trace_vtd_fault_disabled(); \ - } else { \ - vtd_report_dmar_fault(s, source_id, addr, ret_fr, is_write); \ - } \ - goto error; \ - } \ -} + +/* + * PCI bus number (or SID) is not reliable since the device is usaully + * initialized before guest can configure the PCI bridge + * (SECONDARY_BUS_NUMBER). + */ +struct vtd_as_key { + PCIBus *bus; + uint8_t devfn; + uint32_t pasid; +}; + +struct vtd_iotlb_key { + uint64_t gfn; + uint32_t pasid; + uint16_t sid; + uint8_t level; +}; static void vtd_address_space_refresh_all(IntelIOMMUState *s); static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n); @@ -200,14 +207,47 @@ static inline gboolean vtd_as_has_map_notifier(VTDAddressSpace *as) } /* GHashTable functions */ -static gboolean vtd_uint64_equal(gconstpointer v1, gconstpointer v2) +static gboolean vtd_iotlb_equal(gconstpointer v1, gconstpointer v2) { - return *((const uint64_t *)v1) == *((const uint64_t *)v2); + const struct vtd_iotlb_key *key1 = v1; + const struct vtd_iotlb_key *key2 = v2; + + return key1->sid == key2->sid && + key1->pasid == key2->pasid && + key1->level == key2->level && + key1->gfn == key2->gfn; } -static guint vtd_uint64_hash(gconstpointer v) +static guint vtd_iotlb_hash(gconstpointer v) { - return (guint)*(const uint64_t *)v; + const struct vtd_iotlb_key *key = v; + uint64_t hash64 = key->gfn | ((uint64_t)(key->sid) << VTD_IOTLB_SID_SHIFT) | + (uint64_t)(key->level - 1) << VTD_IOTLB_LVL_SHIFT | + (uint64_t)(key->pasid) << VTD_IOTLB_PASID_SHIFT; + + return (guint)((hash64 >> 32) ^ (hash64 & 0xffffffffU)); +} + +static gboolean vtd_as_equal(gconstpointer v1, gconstpointer v2) +{ + const struct vtd_as_key *key1 = v1; + const struct vtd_as_key *key2 = v2; + + return (key1->bus == key2->bus) && (key1->devfn == key2->devfn) && + (key1->pasid == key2->pasid); +} + +/* + * Note that we use pointer to PCIBus as the key, so hashing/shifting + * based on the pointer value is intended. Note that we deal with + * collisions through vtd_as_equal(). + */ +static guint vtd_as_hash(gconstpointer v) +{ + const struct vtd_as_key *key = v; + guint value = (guint)(uintptr_t)key->bus; + + return (guint)(value << 8 | key->devfn); } static gboolean vtd_hash_remove_by_domain(gpointer key, gpointer value, @@ -248,22 +288,14 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value, static void vtd_reset_context_cache_locked(IntelIOMMUState *s) { VTDAddressSpace *vtd_as; - VTDBus *vtd_bus; - GHashTableIter bus_it; - uint32_t devfn_it; + GHashTableIter as_it; trace_vtd_context_cache_reset(); - g_hash_table_iter_init(&bus_it, s->vtd_as_by_busptr); + g_hash_table_iter_init(&as_it, s->vtd_address_spaces); - while (g_hash_table_iter_next (&bus_it, NULL, (void**)&vtd_bus)) { - for (devfn_it = 0; devfn_it < PCI_DEVFN_MAX; ++devfn_it) { - vtd_as = vtd_bus->dev_as[devfn_it]; - if (!vtd_as) { - continue; - } - vtd_as->context_cache_entry.context_cache_gen = 0; - } + while (g_hash_table_iter_next(&as_it, NULL, (void **)&vtd_as)) { + vtd_as->context_cache_entry.context_cache_gen = 0; } s->context_cache_gen = 1; } @@ -290,13 +322,6 @@ static void vtd_reset_caches(IntelIOMMUState *s) vtd_iommu_unlock(s); } -static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint16_t source_id, - uint32_t level) -{ - return gfn | ((uint64_t)(source_id) << VTD_IOTLB_SID_SHIFT) | - ((uint64_t)(level) << VTD_IOTLB_LVL_SHIFT); -} - static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level) { return (addr & vtd_slpt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K; @@ -304,15 +329,17 @@ static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level) /* Must be called with IOMMU lock held */ static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id, - hwaddr addr) + uint32_t pasid, hwaddr addr) { + struct vtd_iotlb_key key; VTDIOTLBEntry *entry; - uint64_t key; int level; for (level = VTD_SL_PT_LEVEL; level < VTD_SL_PML4_LEVEL; level++) { - key = vtd_get_iotlb_key(vtd_get_iotlb_gfn(addr, level), - source_id, level); + key.gfn = vtd_get_iotlb_gfn(addr, level); + key.level = level; + key.sid = source_id; + key.pasid = pasid; entry = g_hash_table_lookup(s->iotlb, &key); if (entry) { goto out; @@ -326,10 +353,11 @@ out: /* Must be with IOMMU lock held */ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, uint16_t domain_id, hwaddr addr, uint64_t slpte, - uint8_t access_flags, uint32_t level) + uint8_t access_flags, uint32_t level, + uint32_t pasid) { VTDIOTLBEntry *entry = g_malloc(sizeof(*entry)); - uint64_t *key = g_malloc(sizeof(*key)); + struct vtd_iotlb_key *key = g_malloc(sizeof(*key)); uint64_t gfn = vtd_get_iotlb_gfn(addr, level); trace_vtd_iotlb_page_update(source_id, addr, slpte, domain_id); @@ -343,7 +371,13 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, entry->slpte = slpte; entry->access_flags = access_flags; entry->mask = vtd_slpt_level_page_mask(level); - *key = vtd_get_iotlb_key(gfn, source_id, level); + entry->pasid = pasid; + + key->gfn = gfn; + key->sid = source_id; + key->level = level; + key->pasid = pasid; + g_hash_table_replace(s->iotlb, key, entry); } @@ -363,7 +397,7 @@ static void vtd_generate_interrupt(IntelIOMMUState *s, hwaddr mesg_addr_reg, trace_vtd_irq_generate(msi.address, msi.data); - apic_get_class()->send_msi(&msi); + apic_get_class(NULL)->send_msi(&msi); } /* Generate a fault event to software via MSI if conditions are met. @@ -435,19 +469,12 @@ static void vtd_set_frcd_and_update_ppf(IntelIOMMUState *s, uint16_t index) /* Must not update F field now, should be done later */ static void vtd_record_frcd(IntelIOMMUState *s, uint16_t index, - uint16_t source_id, hwaddr addr, - VTDFaultReason fault, bool is_write) + uint64_t hi, uint64_t lo) { - uint64_t hi = 0, lo; hwaddr frcd_reg_addr = DMAR_FRCD_REG_OFFSET + (((uint64_t)index) << 4); assert(index < DMAR_FRCD_REG_NR); - lo = VTD_FRCD_FI(addr); - hi = VTD_FRCD_SID(source_id) | VTD_FRCD_FR(fault); - if (!is_write) { - hi |= VTD_FRCD_T; - } vtd_set_quad_raw(s, frcd_reg_addr, lo); vtd_set_quad_raw(s, frcd_reg_addr + 8, hi); @@ -473,16 +500,11 @@ static bool vtd_try_collapse_fault(IntelIOMMUState *s, uint16_t source_id) } /* Log and report an DMAR (address translation) fault to software */ -static void vtd_report_dmar_fault(IntelIOMMUState *s, uint16_t source_id, - hwaddr addr, VTDFaultReason fault, - bool is_write) +static void vtd_report_frcd_fault(IntelIOMMUState *s, uint64_t source_id, + uint64_t hi, uint64_t lo) { uint32_t fsts_reg = vtd_get_long_raw(s, DMAR_FSTS_REG); - assert(fault < VTD_FR_MAX); - - trace_vtd_dmar_fault(source_id, fault, addr, is_write); - if (fsts_reg & VTD_FSTS_PFO) { error_report_once("New fault is not recorded due to " "Primary Fault Overflow"); @@ -502,7 +524,7 @@ static void vtd_report_dmar_fault(IntelIOMMUState *s, uint16_t source_id, return; } - vtd_record_frcd(s, s->next_frcd_reg, source_id, addr, fault, is_write); + vtd_record_frcd(s, s->next_frcd_reg, hi, lo); if (fsts_reg & VTD_FSTS_PPF) { error_report_once("There are pending faults already, " @@ -527,6 +549,40 @@ static void vtd_report_dmar_fault(IntelIOMMUState *s, uint16_t source_id, } } +/* Log and report an DMAR (address translation) fault to software */ +static void vtd_report_dmar_fault(IntelIOMMUState *s, uint16_t source_id, + hwaddr addr, VTDFaultReason fault, + bool is_write, bool is_pasid, + uint32_t pasid) +{ + uint64_t hi, lo; + + assert(fault < VTD_FR_MAX); + + trace_vtd_dmar_fault(source_id, fault, addr, is_write); + + lo = VTD_FRCD_FI(addr); + hi = VTD_FRCD_SID(source_id) | VTD_FRCD_FR(fault) | + VTD_FRCD_PV(pasid) | VTD_FRCD_PP(is_pasid); + if (!is_write) { + hi |= VTD_FRCD_T; + } + + vtd_report_frcd_fault(s, source_id, hi, lo); +} + + +static void vtd_report_ir_fault(IntelIOMMUState *s, uint64_t source_id, + VTDFaultReason fault, uint16_t index) +{ + uint64_t hi, lo; + + lo = VTD_FRCD_IR_IDX(index); + hi = VTD_FRCD_SID(source_id) | VTD_FRCD_FR(fault); + + vtd_report_frcd_fault(s, source_id, hi, lo); +} + /* Handle Invalidation Queue Errors of queued invalidation interface error * conditions. */ @@ -718,6 +774,8 @@ static int vtd_get_pdire_from_pdir_table(dma_addr_t pasid_dir_base, return -VTD_FR_PASID_TABLE_INV; } + pdire->val = le64_to_cpu(pdire->val); + return 0; } @@ -742,6 +800,9 @@ static int vtd_get_pe_in_pasid_leaf_table(IntelIOMMUState *s, pe, entry_size, MEMTXATTRS_UNSPECIFIED)) { return -VTD_FR_PASID_TABLE_INV; } + for (size_t i = 0; i < ARRAY_SIZE(pe->val); i++) { + pe->val[i] = le64_to_cpu(pe->val[i]); + } /* Do translation type check */ if (!vtd_pe_type_check(x86_iommu, pe)) { @@ -807,13 +868,15 @@ static int vtd_get_pe_from_pasid_table(IntelIOMMUState *s, static int vtd_ce_get_rid2pasid_entry(IntelIOMMUState *s, VTDContextEntry *ce, - VTDPASIDEntry *pe) + VTDPASIDEntry *pe, + uint32_t pasid) { - uint32_t pasid; dma_addr_t pasid_dir_base; int ret = 0; - pasid = VTD_CE_GET_RID2PASID(ce); + if (pasid == PCI_NO_PASID) { + pasid = VTD_CE_GET_RID2PASID(ce); + } pasid_dir_base = VTD_CE_GET_PASID_DIR_TABLE(ce); ret = vtd_get_pe_from_pasid_table(s, pasid_dir_base, pasid, pe); @@ -822,15 +885,17 @@ static int vtd_ce_get_rid2pasid_entry(IntelIOMMUState *s, static int vtd_ce_get_pasid_fpd(IntelIOMMUState *s, VTDContextEntry *ce, - bool *pe_fpd_set) + bool *pe_fpd_set, + uint32_t pasid) { int ret; - uint32_t pasid; dma_addr_t pasid_dir_base; VTDPASIDDirEntry pdire; VTDPASIDEntry pe; - pasid = VTD_CE_GET_RID2PASID(ce); + if (pasid == PCI_NO_PASID) { + pasid = VTD_CE_GET_RID2PASID(ce); + } pasid_dir_base = VTD_CE_GET_PASID_DIR_TABLE(ce); /* @@ -876,12 +941,13 @@ static inline uint32_t vtd_ce_get_level(VTDContextEntry *ce) } static uint32_t vtd_get_iova_level(IntelIOMMUState *s, - VTDContextEntry *ce) + VTDContextEntry *ce, + uint32_t pasid) { VTDPASIDEntry pe; if (s->root_scalable) { - vtd_ce_get_rid2pasid_entry(s, ce, &pe); + vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid); return VTD_PE_GET_LEVEL(&pe); } @@ -894,12 +960,13 @@ static inline uint32_t vtd_ce_get_agaw(VTDContextEntry *ce) } static uint32_t vtd_get_iova_agaw(IntelIOMMUState *s, - VTDContextEntry *ce) + VTDContextEntry *ce, + uint32_t pasid) { VTDPASIDEntry pe; if (s->root_scalable) { - vtd_ce_get_rid2pasid_entry(s, ce, &pe); + vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid); return 30 + ((pe.val[0] >> 2) & VTD_SM_PASID_ENTRY_AW) * 9; } @@ -941,31 +1008,33 @@ static inline bool vtd_ce_type_check(X86IOMMUState *x86_iommu, } static inline uint64_t vtd_iova_limit(IntelIOMMUState *s, - VTDContextEntry *ce, uint8_t aw) + VTDContextEntry *ce, uint8_t aw, + uint32_t pasid) { - uint32_t ce_agaw = vtd_get_iova_agaw(s, ce); + uint32_t ce_agaw = vtd_get_iova_agaw(s, ce, pasid); return 1ULL << MIN(ce_agaw, aw); } /* Return true if IOVA passes range check, otherwise false. */ static inline bool vtd_iova_range_check(IntelIOMMUState *s, uint64_t iova, VTDContextEntry *ce, - uint8_t aw) + uint8_t aw, uint32_t pasid) { /* * Check if @iova is above 2^X-1, where X is the minimum of MGAW * in CAP_REG and AW in context-entry. */ - return !(iova & ~(vtd_iova_limit(s, ce, aw) - 1)); + return !(iova & ~(vtd_iova_limit(s, ce, aw, pasid) - 1)); } static dma_addr_t vtd_get_iova_pgtbl_base(IntelIOMMUState *s, - VTDContextEntry *ce) + VTDContextEntry *ce, + uint32_t pasid) { VTDPASIDEntry pe; if (s->root_scalable) { - vtd_ce_get_rid2pasid_entry(s, ce, &pe); + vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid); return pe.val[0] & VTD_SM_PASID_ENTRY_SLPTPTR; } @@ -976,67 +1045,59 @@ static dma_addr_t vtd_get_iova_pgtbl_base(IntelIOMMUState *s, * Rsvd field masks for spte: * vtd_spte_rsvd 4k pages * vtd_spte_rsvd_large large pages + * + * We support only 3-level and 4-level page tables (see vtd_init() which + * sets only VTD_CAP_SAGAW_39bit and maybe VTD_CAP_SAGAW_48bit bits in s->cap). */ -static uint64_t vtd_spte_rsvd[5]; -static uint64_t vtd_spte_rsvd_large[5]; +#define VTD_SPTE_RSVD_LEN 5 +static uint64_t vtd_spte_rsvd[VTD_SPTE_RSVD_LEN]; +static uint64_t vtd_spte_rsvd_large[VTD_SPTE_RSVD_LEN]; static bool vtd_slpte_nonzero_rsvd(uint64_t slpte, uint32_t level) { - uint64_t rsvd_mask = vtd_spte_rsvd[level]; + uint64_t rsvd_mask; + + /* + * We should have caught a guest-mis-programmed level earlier, + * via vtd_is_level_supported. + */ + assert(level < VTD_SPTE_RSVD_LEN); + /* + * Zero level doesn't exist. The smallest level is VTD_SL_PT_LEVEL=1 and + * checked by vtd_is_last_slpte(). + */ + assert(level); if ((level == VTD_SL_PD_LEVEL || level == VTD_SL_PDP_LEVEL) && (slpte & VTD_SL_PT_PAGE_SIZE_MASK)) { /* large page */ rsvd_mask = vtd_spte_rsvd_large[level]; + } else { + rsvd_mask = vtd_spte_rsvd[level]; } return slpte & rsvd_mask; } -/* Find the VTD address space associated with a given bus number */ -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]; - GHashTableIter iter; - - 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 NULL; -} - /* Given the @iova, get relevant @slptep. @slpte_level will be the last level * of the translation, can be used for deciding the size of large page. */ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, uint64_t iova, bool is_write, uint64_t *slptep, uint32_t *slpte_level, - bool *reads, bool *writes, uint8_t aw_bits) + bool *reads, bool *writes, uint8_t aw_bits, + uint32_t pasid) { - dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce); - uint32_t level = vtd_get_iova_level(s, ce); + dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce, pasid); + uint32_t level = vtd_get_iova_level(s, ce, pasid); uint32_t offset; uint64_t slpte; uint64_t access_right_check; uint64_t xlat, size; - if (!vtd_iova_range_check(s, iova, ce, aw_bits)) { - error_report_once("%s: detected IOVA overflow (iova=0x%" PRIx64 ")", - __func__, iova); + if (!vtd_iova_range_check(s, iova, ce, aw_bits, pasid)) { + error_report_once("%s: detected IOVA overflow (iova=0x%" PRIx64 "," + "pasid=0x%" PRIx32 ")", __func__, iova, pasid); return -VTD_FR_ADDR_BEYOND_MGAW; } @@ -1049,8 +1110,9 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, if (slpte == (uint64_t)-1) { error_report_once("%s: detected read error on DMAR slpte " - "(iova=0x%" PRIx64 ")", __func__, iova); - if (level == vtd_get_iova_level(s, ce)) { + "(iova=0x%" PRIx64 ", pasid=0x%" PRIx32 ")", + __func__, iova, pasid); + if (level == vtd_get_iova_level(s, ce, pasid)) { /* Invalid programming of context-entry */ return -VTD_FR_CONTEXT_ENTRY_INV; } else { @@ -1062,15 +1124,16 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, if (!(slpte & access_right_check)) { error_report_once("%s: detected slpte permission error " "(iova=0x%" PRIx64 ", level=0x%" PRIx32 ", " - "slpte=0x%" PRIx64 ", write=%d)", __func__, - iova, level, slpte, is_write); + "slpte=0x%" PRIx64 ", write=%d, pasid=0x%" + PRIx32 ")", __func__, iova, level, + slpte, is_write, pasid); return is_write ? -VTD_FR_WRITE : -VTD_FR_READ; } if (vtd_slpte_nonzero_rsvd(slpte, level)) { error_report_once("%s: detected splte reserve non-zero " "iova=0x%" PRIx64 ", level=0x%" PRIx32 - "slpte=0x%" PRIx64 ")", __func__, iova, - level, slpte); + "slpte=0x%" PRIx64 ", pasid=0x%" PRIX32 ")", + __func__, iova, level, slpte, pasid); return -VTD_FR_PAGING_ENTRY_RSVD; } @@ -1098,9 +1161,10 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, error_report_once("%s: xlat address is in interrupt range " "(iova=0x%" PRIx64 ", level=0x%" PRIx32 ", " "slpte=0x%" PRIx64 ", write=%d, " - "xlat=0x%" PRIx64 ", size=0x%" PRIx64 ")", + "xlat=0x%" PRIx64 ", size=0x%" PRIx64 ", " + "pasid=0x%" PRIx32 ")", __func__, iova, level, slpte, is_write, - xlat, size); + xlat, size, pasid); return s->scalable_mode ? -VTD_FR_SM_INTERRUPT_ADDR : -VTD_FR_INTERRUPT_ADDR; } @@ -1187,7 +1251,7 @@ static int vtd_page_walk_one(IOMMUTLBEvent *event, vtd_page_walk_info *info) return ret; } /* Drop any existing mapping */ - iova_tree_remove(as->iova_tree, &target); + iova_tree_remove(as->iova_tree, target); /* Recover the correct type */ event->type = IOMMU_NOTIFIER_MAP; entry->perm = cache_perm; @@ -1200,7 +1264,7 @@ static int vtd_page_walk_one(IOMMUTLBEvent *event, vtd_page_walk_info *info) trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask); return 0; } - iova_tree_remove(as->iova_tree, &target); + iova_tree_remove(as->iova_tree, target); } trace_vtd_page_walk_one(info->domain_id, entry->iova, @@ -1314,18 +1378,19 @@ next: */ static int vtd_page_walk(IntelIOMMUState *s, VTDContextEntry *ce, uint64_t start, uint64_t end, - vtd_page_walk_info *info) + vtd_page_walk_info *info, + uint32_t pasid) { - dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce); - uint32_t level = vtd_get_iova_level(s, ce); + dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce, pasid); + uint32_t level = vtd_get_iova_level(s, ce, pasid); - if (!vtd_iova_range_check(s, start, ce, info->aw)) { + if (!vtd_iova_range_check(s, start, ce, info->aw, pasid)) { return -VTD_FR_ADDR_BEYOND_MGAW; } - if (!vtd_iova_range_check(s, end, ce, info->aw)) { + if (!vtd_iova_range_check(s, end, ce, info->aw, pasid)) { /* Fix end so that it reaches the maximum */ - end = vtd_iova_limit(s, ce, info->aw); + end = vtd_iova_limit(s, ce, info->aw, pasid); } return vtd_page_walk_level(addr, start, end, level, true, true, info); @@ -1393,7 +1458,7 @@ static int vtd_ce_rid2pasid_check(IntelIOMMUState *s, * has valid rid2pasid setting, which includes valid * rid2pasid field and corresponding pasid entry setting */ - return vtd_ce_get_rid2pasid_entry(s, ce, &pe); + return vtd_ce_get_rid2pasid_entry(s, ce, &pe, PCI_NO_PASID); } /* Map a device to its corresponding domain (context-entry) */ @@ -1476,12 +1541,13 @@ static int vtd_sync_shadow_page_hook(IOMMUTLBEvent *event, } static uint16_t vtd_get_domain_id(IntelIOMMUState *s, - VTDContextEntry *ce) + VTDContextEntry *ce, + uint32_t pasid) { VTDPASIDEntry pe; if (s->root_scalable) { - vtd_ce_get_rid2pasid_entry(s, ce, &pe); + vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid); return VTD_SM_PASID_ENTRY_DID(pe.val[1]); } @@ -1499,19 +1565,23 @@ static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as, .notify_unmap = true, .aw = s->aw_bits, .as = vtd_as, - .domain_id = vtd_get_domain_id(s, ce), + .domain_id = vtd_get_domain_id(s, ce, vtd_as->pasid), }; - return vtd_page_walk(s, ce, addr, addr + size, &info); + return vtd_page_walk(s, ce, addr, addr + size, &info, vtd_as->pasid); } -static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as) +static int vtd_address_space_sync(VTDAddressSpace *vtd_as) { int ret; VTDContextEntry ce; IOMMUNotifier *n; - if (!(vtd_as->iommu.iommu_notify_flags & IOMMU_NOTIFIER_IOTLB_EVENTS)) { + /* If no MAP notifier registered, we simply invalidate all the cache */ + if (!vtd_as_has_map_notifier(vtd_as)) { + IOMMU_NOTIFIER_FOREACH(n, &vtd_as->iommu) { + memory_region_unmap_iommu_notifier_range(n); + } return 0; } @@ -1546,16 +1616,19 @@ static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as) * 1st-level translation or 2nd-level translation, it depends * on PGTT setting. */ -static bool vtd_dev_pt_enabled(IntelIOMMUState *s, VTDContextEntry *ce) +static bool vtd_dev_pt_enabled(IntelIOMMUState *s, VTDContextEntry *ce, + uint32_t pasid) { VTDPASIDEntry pe; int ret; if (s->root_scalable) { - ret = vtd_ce_get_rid2pasid_entry(s, ce, &pe); + ret = vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid); if (ret) { - error_report_once("%s: vtd_ce_get_rid2pasid_entry error: %"PRId32, - __func__, ret); + /* + * This error is guest triggerable. We should assumt PT + * not enabled for safety. + */ return false; } return (VTD_PE_GET_TYPE(&pe) == VTD_SM_PASID_ENTRY_PT); @@ -1569,14 +1642,12 @@ static bool vtd_as_pt_enabled(VTDAddressSpace *as) { IntelIOMMUState *s; VTDContextEntry ce; - int ret; assert(as); s = as->iommu_state; - ret = vtd_dev_to_context_entry(s, pci_bus_num(as->bus), - as->devfn, &ce); - if (ret) { + if (vtd_dev_to_context_entry(s, pci_bus_num(as->bus), as->devfn, + &ce)) { /* * Possibly failed to parse the context entry for some reason * (e.g., during init, or any guest configuration errors on @@ -1586,19 +1657,20 @@ static bool vtd_as_pt_enabled(VTDAddressSpace *as) return false; } - return vtd_dev_pt_enabled(s, &ce); + return vtd_dev_pt_enabled(s, &ce, as->pasid); } /* Return whether the device is using IOMMU translation. */ static bool vtd_switch_address_space(VTDAddressSpace *as) { - bool use_iommu; + bool use_iommu, pt; /* Whether we need to take the BQL on our own */ - bool take_bql = !qemu_mutex_iothread_locked(); + bool take_bql = !bql_locked(); assert(as); use_iommu = as->iommu_state->dmar_enabled && !vtd_as_pt_enabled(as); + pt = as->iommu_state->dmar_enabled && vtd_as_pt_enabled(as); trace_vtd_switch_address_space(pci_bus_num(as->bus), VTD_PCI_SLOT(as->devfn), @@ -1611,20 +1683,62 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) * it. We'd better make sure we have had it already, or, take it. */ if (take_bql) { - qemu_mutex_lock_iothread(); + bql_lock(); } /* Turn off first then on the other */ if (use_iommu) { memory_region_set_enabled(&as->nodmar, false); memory_region_set_enabled(MEMORY_REGION(&as->iommu), true); + /* + * vt-d spec v3.4 3.14: + * + * """ + * Requests-with-PASID with input address in range 0xFEEx_xxxx + * are translated normally like any other request-with-PASID + * through DMA-remapping hardware. + * """ + * + * Need to disable ir for as with PASID. + */ + if (as->pasid != PCI_NO_PASID) { + memory_region_set_enabled(&as->iommu_ir, false); + } else { + memory_region_set_enabled(&as->iommu_ir, true); + } } else { memory_region_set_enabled(MEMORY_REGION(&as->iommu), false); memory_region_set_enabled(&as->nodmar, true); } + /* + * vtd-spec v3.4 3.14: + * + * """ + * Requests-with-PASID with input address in range 0xFEEx_xxxx are + * translated normally like any other request-with-PASID through + * DMA-remapping hardware. However, if such a request is processed + * using pass-through translation, it will be blocked as described + * in the paragraph below. + * + * Software must not program paging-structure entries to remap any + * address to the interrupt address range. Untranslated requests + * and translation requests that result in an address in the + * interrupt range will be blocked with condition code LGN.4 or + * SGN.8. + * """ + * + * We enable per as memory region (iommu_ir_fault) for catching + * the translation for interrupt range through PASID + PT. + */ + if (pt && as->pasid != PCI_NO_PASID) { + memory_region_set_enabled(&as->iommu_ir_fault, true); + } else { + memory_region_set_enabled(&as->iommu_ir_fault, false); + } + if (take_bql) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } return use_iommu; @@ -1632,26 +1746,15 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) static void vtd_switch_address_space_all(IntelIOMMUState *s) { + VTDAddressSpace *vtd_as; GHashTableIter iter; - VTDBus *vtd_bus; - int i; - g_hash_table_iter_init(&iter, s->vtd_as_by_busptr); - while (g_hash_table_iter_next(&iter, NULL, (void **)&vtd_bus)) { - for (i = 0; i < PCI_DEVFN_MAX; i++) { - if (!vtd_bus->dev_as[i]) { - continue; - } - vtd_switch_address_space(vtd_bus->dev_as[i]); - } + g_hash_table_iter_init(&iter, s->vtd_address_spaces); + while (g_hash_table_iter_next(&iter, NULL, (void **)&vtd_as)) { + vtd_switch_address_space(vtd_as); } } -static inline uint16_t vtd_make_source_id(uint8_t bus_num, uint8_t devfn) -{ - return ((bus_num & 0xffUL) << 8) | (devfn & 0xffUL); -} - static const bool vtd_qualified_faults[] = { [VTD_FR_RESERVED] = false, [VTD_FR_ROOT_ENTRY_P] = false, @@ -1686,18 +1789,37 @@ static inline bool vtd_is_interrupt_addr(hwaddr addr) return VTD_INTERRUPT_ADDR_FIRST <= addr && addr <= VTD_INTERRUPT_ADDR_LAST; } +static gboolean vtd_find_as_by_sid(gpointer key, gpointer value, + gpointer user_data) +{ + struct vtd_as_key *as_key = (struct vtd_as_key *)key; + uint16_t target_sid = *(uint16_t *)user_data; + uint16_t sid = PCI_BUILD_BDF(pci_bus_num(as_key->bus), as_key->devfn); + return sid == target_sid; +} + +static VTDAddressSpace *vtd_get_as_by_sid(IntelIOMMUState *s, uint16_t sid) +{ + uint8_t bus_num = PCI_BUS_NUM(sid); + VTDAddressSpace *vtd_as = s->vtd_as_cache[bus_num]; + + if (vtd_as && + (sid == PCI_BUILD_BDF(pci_bus_num(vtd_as->bus), vtd_as->devfn))) { + return vtd_as; + } + + vtd_as = g_hash_table_find(s->vtd_address_spaces, vtd_find_as_by_sid, &sid); + s->vtd_as_cache[bus_num] = vtd_as; + + return vtd_as; +} + static void vtd_pt_enable_fast_path(IntelIOMMUState *s, uint16_t source_id) { - VTDBus *vtd_bus; VTDAddressSpace *vtd_as; bool success = false; - vtd_bus = vtd_find_as_from_bus_num(s, VTD_SID_TO_BUS(source_id)); - if (!vtd_bus) { - goto out; - } - - vtd_as = vtd_bus->dev_as[VTD_SID_TO_DEVFN(source_id)]; + vtd_as = vtd_get_as_by_sid(s, source_id); if (!vtd_as) { goto out; } @@ -1711,6 +1833,22 @@ out: trace_vtd_pt_enable_fast_path(source_id, success); } +static void vtd_report_fault(IntelIOMMUState *s, + int err, bool is_fpd_set, + uint16_t source_id, + hwaddr addr, + bool is_write, + bool is_pasid, + uint32_t pasid) +{ + if (is_fpd_set && vtd_is_qualified_fault(err)) { + trace_vtd_fault_disabled(); + } else { + vtd_report_dmar_fault(s, source_id, addr, err, is_write, + is_pasid, pasid); + } +} + /* Map dev to context-entry then do a paging-structures walk to do a iommu * translation. * @@ -1732,13 +1870,14 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, uint8_t bus_num = pci_bus_num(bus); VTDContextCacheEntry *cc_entry; uint64_t slpte, page_mask; - uint32_t level; - uint16_t source_id = vtd_make_source_id(bus_num, devfn); + uint32_t level, pasid = vtd_as->pasid; + uint16_t source_id = PCI_BUILD_BDF(bus_num, devfn); int ret_fr; bool is_fpd_set = false; bool reads = true; bool writes = true; uint8_t access_flags; + bool rid2pasid = (pasid == PCI_NO_PASID) && s->root_scalable; VTDIOTLBEntry *iotlb_entry; /* @@ -1751,15 +1890,17 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, cc_entry = &vtd_as->context_cache_entry; - /* Try to fetch slpte form IOTLB */ - iotlb_entry = vtd_lookup_iotlb(s, source_id, addr); - if (iotlb_entry) { - trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->slpte, - iotlb_entry->domain_id); - slpte = iotlb_entry->slpte; - access_flags = iotlb_entry->access_flags; - page_mask = iotlb_entry->mask; - goto out; + /* Try to fetch slpte form IOTLB, we don't need RID2PASID logic */ + if (!rid2pasid) { + iotlb_entry = vtd_lookup_iotlb(s, source_id, pasid, addr); + if (iotlb_entry) { + trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->slpte, + iotlb_entry->domain_id); + slpte = iotlb_entry->slpte; + access_flags = iotlb_entry->access_flags; + page_mask = iotlb_entry->mask; + goto out; + } } /* Try to fetch context-entry from cache first */ @@ -1770,16 +1911,26 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, ce = cc_entry->context_entry; is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD; if (!is_fpd_set && s->root_scalable) { - ret_fr = vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set); - VTD_PE_GET_FPD_ERR(ret_fr, is_fpd_set, s, source_id, addr, is_write); + ret_fr = vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set, pasid); + if (ret_fr) { + vtd_report_fault(s, -ret_fr, is_fpd_set, + source_id, addr, is_write, + false, 0); + goto error; + } } } else { ret_fr = vtd_dev_to_context_entry(s, bus_num, devfn, &ce); is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD; if (!ret_fr && !is_fpd_set && s->root_scalable) { - ret_fr = vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set); + ret_fr = vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set, pasid); + } + if (ret_fr) { + vtd_report_fault(s, -ret_fr, is_fpd_set, + source_id, addr, is_write, + false, 0); + goto error; } - VTD_PE_GET_FPD_ERR(ret_fr, is_fpd_set, s, source_id, addr, is_write); /* Update context-cache */ trace_vtd_iotlb_cc_update(bus_num, devfn, ce.hi, ce.lo, cc_entry->context_cache_gen, @@ -1788,11 +1939,15 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, cc_entry->context_cache_gen = s->context_cache_gen; } + if (rid2pasid) { + pasid = VTD_CE_GET_RID2PASID(&ce); + } + /* * We don't need to translate for pass-through context entries. * Also, let's ignore IOTLB caching as well for PT devices. */ - if (vtd_dev_pt_enabled(s, &ce)) { + if (vtd_dev_pt_enabled(s, &ce, pasid)) { entry->iova = addr & VTD_PAGE_MASK_4K; entry->translated_addr = entry->iova; entry->addr_mask = ~VTD_PAGE_MASK_4K; @@ -1813,14 +1968,31 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, return true; } + /* Try to fetch slpte form IOTLB for RID2PASID slow path */ + if (rid2pasid) { + iotlb_entry = vtd_lookup_iotlb(s, source_id, pasid, addr); + if (iotlb_entry) { + trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->slpte, + iotlb_entry->domain_id); + slpte = iotlb_entry->slpte; + access_flags = iotlb_entry->access_flags; + page_mask = iotlb_entry->mask; + goto out; + } + } + ret_fr = vtd_iova_to_slpte(s, &ce, addr, is_write, &slpte, &level, - &reads, &writes, s->aw_bits); - VTD_PE_GET_FPD_ERR(ret_fr, is_fpd_set, s, source_id, addr, is_write); + &reads, &writes, s->aw_bits, pasid); + if (ret_fr) { + vtd_report_fault(s, -ret_fr, is_fpd_set, source_id, + addr, is_write, pasid != PCI_NO_PASID, pasid); + goto error; + } page_mask = vtd_slpt_level_page_mask(level); access_flags = IOMMU_ACCESS_FLAG(reads, writes); - vtd_update_iotlb(s, source_id, vtd_get_domain_id(s, &ce), addr, slpte, - access_flags, level); + vtd_update_iotlb(s, source_id, vtd_get_domain_id(s, &ce, pasid), + addr, slpte, access_flags, level, pasid); out: vtd_iommu_unlock(s); entry->iova = addr & page_mask; @@ -1873,7 +2045,7 @@ static void vtd_iommu_replay_all(IntelIOMMUState *s) VTDAddressSpace *vtd_as; QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { - vtd_sync_shadow_page_table(vtd_as); + vtd_address_space_sync(vtd_as); } } @@ -1905,11 +2077,10 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, uint16_t source_id, uint16_t func_mask) { + GHashTableIter as_it; uint16_t mask; - VTDBus *vtd_bus; VTDAddressSpace *vtd_as; uint8_t bus_n, devfn; - uint16_t devfn_it; trace_vtd_inv_desc_cc_devices(source_id, func_mask); @@ -1932,32 +2103,31 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, mask = ~mask; bus_n = VTD_SID_TO_BUS(source_id); - vtd_bus = vtd_find_as_from_bus_num(s, bus_n); - if (vtd_bus) { - devfn = VTD_SID_TO_DEVFN(source_id); - for (devfn_it = 0; devfn_it < PCI_DEVFN_MAX; ++devfn_it) { - vtd_as = vtd_bus->dev_as[devfn_it]; - if (vtd_as && ((devfn_it & mask) == (devfn & mask))) { - trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(devfn_it), - VTD_PCI_FUNC(devfn_it)); - vtd_iommu_lock(s); - vtd_as->context_cache_entry.context_cache_gen = 0; - vtd_iommu_unlock(s); - /* - * Do switch address space when needed, in case if the - * device passthrough bit is switched. - */ - vtd_switch_address_space(vtd_as); - /* - * So a device is moving out of (or moving into) a - * domain, resync the shadow page table. - * This won't bring bad even if we have no such - * notifier registered - the IOMMU notification - * framework will skip MAP notifications if that - * happened. - */ - vtd_sync_shadow_page_table(vtd_as); - } + devfn = VTD_SID_TO_DEVFN(source_id); + + g_hash_table_iter_init(&as_it, s->vtd_address_spaces); + while (g_hash_table_iter_next(&as_it, NULL, (void **)&vtd_as)) { + if ((pci_bus_num(vtd_as->bus) == bus_n) && + (vtd_as->devfn & mask) == (devfn & mask)) { + trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(vtd_as->devfn), + VTD_PCI_FUNC(vtd_as->devfn)); + vtd_iommu_lock(s); + vtd_as->context_cache_entry.context_cache_gen = 0; + vtd_iommu_unlock(s); + /* + * Do switch address space when needed, in case if the + * device passthrough bit is switched. + */ + vtd_switch_address_space(vtd_as); + /* + * So a device is moving out of (or moving into) a + * domain, resync the shadow page table. + * This won't bring bad even if we have no such + * notifier registered - the IOMMU notification + * framework will skip MAP notifications if that + * happened. + */ + vtd_address_space_sync(vtd_as); } } } @@ -2014,15 +2184,15 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce) && - domain_id == vtd_get_domain_id(s, &ce)) { - vtd_sync_shadow_page_table(vtd_as); + domain_id == vtd_get_domain_id(s, &ce, vtd_as->pasid)) { + vtd_address_space_sync(vtd_as); } } } static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, uint16_t domain_id, hwaddr addr, - uint8_t am) + uint8_t am, uint32_t pasid) { VTDAddressSpace *vtd_as; VTDContextEntry ce; @@ -2030,9 +2200,12 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, hwaddr size = (1 << am) * VTD_PAGE_SIZE; QLIST_FOREACH(vtd_as, &(s->vtd_as_with_notifiers), next) { + if (pasid != PCI_NO_PASID && pasid != vtd_as->pasid) { + continue; + } ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce); - if (!ret && domain_id == vtd_get_domain_id(s, &ce)) { + if (!ret && domain_id == vtd_get_domain_id(s, &ce, vtd_as->pasid)) { if (vtd_as_has_map_notifier(vtd_as)) { /* * As long as we have MAP notifications registered in @@ -2076,7 +2249,7 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, vtd_iommu_lock(s); g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info); vtd_iommu_unlock(s); - vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am); + vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am, PCI_NO_PASID); } /* Flush IOTLB @@ -2473,18 +2646,13 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, { VTDAddressSpace *vtd_dev_as; IOMMUTLBEvent event; - struct VTDBus *vtd_bus; hwaddr addr; uint64_t sz; uint16_t sid; - uint8_t devfn; bool size; - uint8_t bus_num; addr = VTD_INV_DESC_DEVICE_IOTLB_ADDR(inv_desc->hi); sid = VTD_INV_DESC_DEVICE_IOTLB_SID(inv_desc->lo); - devfn = sid & 0xff; - bus_num = sid >> 8; size = VTD_INV_DESC_DEVICE_IOTLB_SIZE(inv_desc->hi); if ((inv_desc->lo & VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO) || @@ -2495,12 +2663,11 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, return false; } - vtd_bus = vtd_find_as_from_bus_num(s, bus_num); - if (!vtd_bus) { - goto done; - } - - vtd_dev_as = vtd_bus->dev_as[devfn]; + /* + * Using sid is OK since the guest should have finished the + * initialization of both the bus and device. + */ + vtd_dev_as = vtd_get_as_by_sid(s, sid); if (!vtd_dev_as) { goto done; } @@ -3057,6 +3224,7 @@ static int vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, { VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); IntelIOMMUState *s = vtd_as->iommu_state; + X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); /* TODO: add support for VFIO and vhost users */ if (s->snoop_control) { @@ -3064,6 +3232,20 @@ static int vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, "Snoop Control with vhost or VFIO is not supported"); return -ENOTSUP; } + if (!s->caching_mode && (new & IOMMU_NOTIFIER_MAP)) { + error_setg_errno(errp, ENOTSUP, + "device %02x.%02x.%x requires caching mode", + pci_bus_num(vtd_as->bus), PCI_SLOT(vtd_as->devfn), + PCI_FUNC(vtd_as->devfn)); + return -ENOTSUP; + } + if (!x86_iommu->dt_supported && (new & IOMMU_NOTIFIER_DEVIOTLB_UNMAP)) { + error_setg_errno(errp, ENOTSUP, + "device %02x.%02x.%x requires device IOTLB mode", + pci_bus_num(vtd_as->bus), PCI_SLOT(vtd_as->devfn), + PCI_FUNC(vtd_as->devfn)); + return -ENOTSUP; + } /* Update per-address-space notifier flags */ vtd_as->notifier_flags = new; @@ -3107,7 +3289,7 @@ static const VMStateDescription vtd_vmstate = { .minimum_version_id = 1, .priority = MIG_PRI_IOMMU, .post_load = vtd_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(root, IntelIOMMUState), VMSTATE_UINT64(intr_root, IntelIOMMUState), VMSTATE_UINT64(iq, IntelIOMMUState), @@ -3151,14 +3333,16 @@ static Property vtd_properties[] = { DEFINE_PROP_BOOL("caching-mode", IntelIOMMUState, caching_mode, FALSE), DEFINE_PROP_BOOL("x-scalable-mode", IntelIOMMUState, scalable_mode, FALSE), DEFINE_PROP_BOOL("snoop-control", IntelIOMMUState, snoop_control, false), + DEFINE_PROP_BOOL("x-pasid-mode", IntelIOMMUState, pasid, false), DEFINE_PROP_BOOL("dma-drain", IntelIOMMUState, dma_drain, true), DEFINE_PROP_BOOL("dma-translation", IntelIOMMUState, dma_translation, true), DEFINE_PROP_END_OF_LIST(), }; /* Read IRTE entry with specific index */ -static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, - VTD_IR_TableEntry *entry, uint16_t sid) +static bool vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, + VTD_IR_TableEntry *entry, uint16_t sid, + bool do_fault) { static const uint16_t vtd_svt_mask[VTD_SQ_MAX] = \ {0xffff, 0xfffb, 0xfff9, 0xfff8}; @@ -3169,7 +3353,10 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, if (index >= iommu->intr_size) { error_report_once("%s: index too large: ind=0x%x", __func__, index); - return -VTD_FR_IR_INDEX_OVER; + if (do_fault) { + vtd_report_ir_fault(iommu, sid, VTD_FR_IR_INDEX_OVER, index); + } + return false; } addr = iommu->intr_root + index * sizeof(*entry); @@ -3177,32 +3364,51 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, entry, sizeof(*entry), MEMTXATTRS_UNSPECIFIED)) { error_report_once("%s: read failed: ind=0x%x addr=0x%" PRIx64, __func__, index, addr); - return -VTD_FR_IR_ROOT_INVAL; + if (do_fault) { + vtd_report_ir_fault(iommu, sid, VTD_FR_IR_ROOT_INVAL, index); + } + return false; } - trace_vtd_ir_irte_get(index, le64_to_cpu(entry->data[1]), - le64_to_cpu(entry->data[0])); + entry->data[0] = le64_to_cpu(entry->data[0]); + entry->data[1] = le64_to_cpu(entry->data[1]); + + trace_vtd_ir_irte_get(index, entry->data[1], entry->data[0]); + + /* + * The remaining potential fault conditions are "qualified" by the + * Fault Processing Disable bit in the IRTE. Even "not present". + * So just clear the do_fault flag if PFD is set, which will + * prevent faults being raised. + */ + if (entry->irte.fault_disable) { + do_fault = false; + } if (!entry->irte.present) { error_report_once("%s: detected non-present IRTE " "(index=%u, high=0x%" PRIx64 ", low=0x%" PRIx64 ")", - __func__, index, le64_to_cpu(entry->data[1]), - le64_to_cpu(entry->data[0])); - return -VTD_FR_IR_ENTRY_P; + __func__, index, entry->data[1], entry->data[0]); + if (do_fault) { + vtd_report_ir_fault(iommu, sid, VTD_FR_IR_ENTRY_P, index); + } + return false; } if (entry->irte.__reserved_0 || entry->irte.__reserved_1 || entry->irte.__reserved_2) { error_report_once("%s: detected non-zero reserved IRTE " "(index=%u, high=0x%" PRIx64 ", low=0x%" PRIx64 ")", - __func__, index, le64_to_cpu(entry->data[1]), - le64_to_cpu(entry->data[0])); - return -VTD_FR_IR_IRTE_RSVD; + __func__, index, entry->data[1], entry->data[0]); + if (do_fault) { + vtd_report_ir_fault(iommu, sid, VTD_FR_IR_IRTE_RSVD, index); + } + return false; } if (sid != X86_IOMMU_SID_INVALID) { /* Validate IRTE SID */ - source_id = le32_to_cpu(entry->irte.source_id); + source_id = entry->irte.source_id; switch (entry->irte.sid_vtype) { case VTD_SVT_NONE: break; @@ -3213,7 +3419,10 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, error_report_once("%s: invalid IRTE SID " "(index=%u, sid=%u, source_id=%u)", __func__, index, sid, source_id); - return -VTD_FR_IR_SID_ERR; + if (do_fault) { + vtd_report_ir_fault(iommu, sid, VTD_FR_IR_SID_ERR, index); + } + return false; } break; @@ -3225,7 +3434,10 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, error_report_once("%s: invalid SVT_BUS " "(index=%u, bus=%u, min=%u, max=%u)", __func__, index, bus, bus_min, bus_max); - return -VTD_FR_IR_SID_ERR; + if (do_fault) { + vtd_report_ir_fault(iommu, sid, VTD_FR_IR_SID_ERR, index); + } + return false; } break; @@ -3234,29 +3446,30 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, "(index=%u, type=%d)", __func__, index, entry->irte.sid_vtype); /* Take this as verification failure. */ - return -VTD_FR_IR_SID_ERR; + if (do_fault) { + vtd_report_ir_fault(iommu, sid, VTD_FR_IR_SID_ERR, index); + } + return false; } } - return 0; + return true; } /* Fetch IRQ information of specific IR index */ -static int vtd_remap_irq_get(IntelIOMMUState *iommu, uint16_t index, - X86IOMMUIrq *irq, uint16_t sid) +static bool vtd_remap_irq_get(IntelIOMMUState *iommu, uint16_t index, + X86IOMMUIrq *irq, uint16_t sid, bool do_fault) { VTD_IR_TableEntry irte = {}; - int ret = 0; - ret = vtd_irte_get(iommu, index, &irte, sid); - if (ret) { - return ret; + if (!vtd_irte_get(iommu, index, &irte, sid, do_fault)) { + return false; } irq->trigger_mode = irte.irte.trigger_mode; irq->vector = irte.irte.vector; irq->delivery_mode = irte.irte.delivery_mode; - irq->dest = le32_to_cpu(irte.irte.dest_id); + irq->dest = irte.irte.dest_id; if (!iommu->intr_eime) { #define VTD_IR_APIC_DEST_MASK (0xff00ULL) #define VTD_IR_APIC_DEST_SHIFT (8) @@ -3269,16 +3482,15 @@ static int vtd_remap_irq_get(IntelIOMMUState *iommu, uint16_t index, trace_vtd_ir_remap(index, irq->trigger_mode, irq->vector, irq->delivery_mode, irq->dest, irq->dest_mode); - return 0; + return true; } /* Interrupt remapping for MSI/MSI-X entry */ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, MSIMessage *origin, MSIMessage *translated, - uint16_t sid) + uint16_t sid, bool do_fault) { - int ret = 0; VTD_IR_MSIAddress addr; uint16_t index; X86IOMMUIrq irq = {}; @@ -3295,14 +3507,20 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, if (origin->address & VTD_MSI_ADDR_HI_MASK) { error_report_once("%s: MSI address high 32 bits non-zero detected: " "address=0x%" PRIx64, __func__, origin->address); - return -VTD_FR_IR_REQ_RSVD; + if (do_fault) { + vtd_report_ir_fault(iommu, sid, VTD_FR_IR_REQ_RSVD, 0); + } + return -EINVAL; } addr.data = origin->address & VTD_MSI_ADDR_LO_MASK; if (addr.addr.__head != 0xfee) { error_report_once("%s: MSI address low 32 bit invalid: 0x%" PRIx32, __func__, addr.data); - return -VTD_FR_IR_REQ_RSVD; + if (do_fault) { + vtd_report_ir_fault(iommu, sid, VTD_FR_IR_REQ_RSVD, 0); + } + return -EINVAL; } /* This is compatible mode. */ @@ -3311,7 +3529,7 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, goto out; } - index = addr.addr.index_h << 15 | le16_to_cpu(addr.addr.index_l); + index = addr.addr.index_h << 15 | addr.addr.index_l; #define VTD_IR_MSI_DATA_SUBHANDLE (0x0000ffff) #define VTD_IR_MSI_DATA_RESERVED (0xffff0000) @@ -3321,9 +3539,8 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, index += origin->data & VTD_IR_MSI_DATA_SUBHANDLE; } - ret = vtd_remap_irq_get(iommu, index, &irq, sid); - if (ret) { - return ret; + if (!vtd_remap_irq_get(iommu, index, &irq, sid, do_fault)) { + return -EINVAL; } if (addr.addr.sub_valid) { @@ -3333,7 +3550,10 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, "(sid=%u, address=0x%" PRIx64 ", data=0x%" PRIx32 ")", __func__, sid, origin->address, origin->data); - return -VTD_FR_IR_REQ_RSVD; + if (do_fault) { + vtd_report_ir_fault(iommu, sid, VTD_FR_IR_REQ_RSVD, 0); + } + return -EINVAL; } } else { uint8_t vector = origin->data & 0xff; @@ -3373,7 +3593,7 @@ static int vtd_int_remap(X86IOMMUState *iommu, MSIMessage *src, MSIMessage *dst, uint16_t sid) { return vtd_interrupt_remap_msi(INTEL_IOMMU_DEVICE(iommu), - src, dst, sid); + src, dst, sid, false); } static MemTxResult vtd_mem_ir_read(void *opaque, hwaddr addr, @@ -3399,14 +3619,13 @@ static MemTxResult vtd_mem_ir_write(void *opaque, hwaddr addr, sid = attrs.requester_id; } - ret = vtd_interrupt_remap_msi(opaque, &from, &to, sid); + ret = vtd_interrupt_remap_msi(opaque, &from, &to, sid, true); if (ret) { - /* TODO: report error */ /* Drop this interrupt */ return MEMTX_ERROR; } - apic_get_class()->send_msi(&to); + apic_get_class(NULL)->send_msi(&to); return MEMTX_OK; } @@ -3425,32 +3644,98 @@ static const MemoryRegionOps vtd_mem_ir_ops = { }, }; -VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) +static void vtd_report_ir_illegal_access(VTDAddressSpace *vtd_as, + hwaddr addr, bool is_write) { - uintptr_t key = (uintptr_t)bus; - VTDBus *vtd_bus = g_hash_table_lookup(s->vtd_as_by_busptr, &key); + IntelIOMMUState *s = vtd_as->iommu_state; + uint8_t bus_n = pci_bus_num(vtd_as->bus); + uint16_t sid = PCI_BUILD_BDF(bus_n, vtd_as->devfn); + bool is_fpd_set = false; + VTDContextEntry ce; + + assert(vtd_as->pasid != PCI_NO_PASID); + + /* Try out best to fetch FPD, we can't do anything more */ + if (vtd_dev_to_context_entry(s, bus_n, vtd_as->devfn, &ce) == 0) { + is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD; + if (!is_fpd_set && s->root_scalable) { + vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set, vtd_as->pasid); + } + } + + vtd_report_fault(s, VTD_FR_SM_INTERRUPT_ADDR, + is_fpd_set, sid, addr, is_write, + true, vtd_as->pasid); +} + +static MemTxResult vtd_mem_ir_fault_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + vtd_report_ir_illegal_access(opaque, addr, false); + + return MEMTX_ERROR; +} + +static MemTxResult vtd_mem_ir_fault_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ + vtd_report_ir_illegal_access(opaque, addr, true); + + return MEMTX_ERROR; +} + +static const MemoryRegionOps vtd_mem_ir_fault_ops = { + .read_with_attrs = vtd_mem_ir_fault_read, + .write_with_attrs = vtd_mem_ir_fault_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, + int devfn, unsigned int pasid) +{ + /* + * We can't simply use sid here since the bus number might not be + * initialized by the guest. + */ + struct vtd_as_key key = { + .bus = bus, + .devfn = devfn, + .pasid = pasid, + }; VTDAddressSpace *vtd_dev_as; char name[128]; - if (!vtd_bus) { - uintptr_t *new_key = g_malloc(sizeof(*new_key)); - *new_key = (uintptr_t)bus; - /* No corresponding free() */ - vtd_bus = g_malloc0(sizeof(VTDBus) + sizeof(VTDAddressSpace *) * \ - PCI_DEVFN_MAX); - vtd_bus->bus = bus; - g_hash_table_insert(s->vtd_as_by_busptr, new_key, vtd_bus); - } - - vtd_dev_as = vtd_bus->dev_as[devfn]; - + vtd_dev_as = g_hash_table_lookup(s->vtd_address_spaces, &key); if (!vtd_dev_as) { - snprintf(name, sizeof(name), "vtd-%02x.%x", PCI_SLOT(devfn), - PCI_FUNC(devfn)); - vtd_bus->dev_as[devfn] = vtd_dev_as = g_new0(VTDAddressSpace, 1); + struct vtd_as_key *new_key = g_malloc(sizeof(*new_key)); + + new_key->bus = bus; + new_key->devfn = devfn; + new_key->pasid = pasid; + + if (pasid == PCI_NO_PASID) { + snprintf(name, sizeof(name), "vtd-%02x.%x", PCI_SLOT(devfn), + PCI_FUNC(devfn)); + } else { + snprintf(name, sizeof(name), "vtd-%02x.%x-pasid-%x", PCI_SLOT(devfn), + PCI_FUNC(devfn), pasid); + } + + vtd_dev_as = g_new0(VTDAddressSpace, 1); vtd_dev_as->bus = bus; vtd_dev_as->devfn = (uint8_t)devfn; + vtd_dev_as->pasid = pasid; vtd_dev_as->iommu_state = s; vtd_dev_as->context_cache_entry.context_cache_gen = 0; vtd_dev_as->iova_tree = iova_tree_new(); @@ -3491,6 +3776,24 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) VTD_INTERRUPT_ADDR_FIRST, &vtd_dev_as->iommu_ir, 1); + /* + * This region is used for catching fault to access interrupt + * range via passthrough + PASID. See also + * vtd_switch_address_space(). We can't use alias since we + * need to know the sid which is valid for MSI who uses + * bus_master_as (see msi_send_message()). + */ + memory_region_init_io(&vtd_dev_as->iommu_ir_fault, OBJECT(s), + &vtd_mem_ir_fault_ops, vtd_dev_as, "vtd-no-ir", + VTD_INTERRUPT_ADDR_SIZE); + /* + * Hook to root since when PT is enabled vtd_dev_as->iommu + * will be disabled. + */ + memory_region_add_subregion_overlap(MEMORY_REGION(&vtd_dev_as->root), + VTD_INTERRUPT_ADDR_FIRST, + &vtd_dev_as->iommu_ir_fault, 2); + /* * Hook both the containers under the root container, we * switch between DMAR & noDMAR by enable/disable @@ -3503,6 +3806,8 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) &vtd_dev_as->nodmar, 0); vtd_switch_address_space(vtd_dev_as); + + g_hash_table_insert(s->vtd_address_spaces, new_key, vtd_dev_as); } return vtd_dev_as; } @@ -3510,7 +3815,7 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) /* Unmap the whole range in the notifier's scope. */ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) { - hwaddr size, remain; + hwaddr total, remain; hwaddr start = n->start; hwaddr end = n->end; IntelIOMMUState *s = as->iommu_state; @@ -3531,7 +3836,7 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) } assert(start <= end); - size = remain = end - start + 1; + total = remain = end - start + 1; while (remain >= VTD_PAGE_SIZE) { IOMMUTLBEvent event; @@ -3559,11 +3864,11 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) trace_vtd_as_unmap_whole(pci_bus_num(as->bus), VTD_PCI_SLOT(as->devfn), VTD_PCI_FUNC(as->devfn), - n->start, size); + n->start, total); map.iova = n->start; - map.size = size; - iova_tree_remove(as->iova_tree, &map); + map.size = total - 1; /* Inclusive */ + iova_tree_remove(as->iova_tree, map); } static void vtd_address_space_unmap_all(IntelIOMMUState *s) @@ -3596,22 +3901,19 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) IntelIOMMUState *s = vtd_as->iommu_state; uint8_t bus_n = pci_bus_num(vtd_as->bus); VTDContextEntry ce; + DMAMap map = { .iova = 0, .size = HWADDR_MAX }; - /* - * The replay can be triggered by either a invalidation or a newly - * created entry. No matter what, we release existing mappings - * (it means flushing caches for UNMAP-only registers). - */ - vtd_address_space_unmap(vtd_as, n); + /* replay is protected by BQL, page walk will re-setup it safely */ + iova_tree_remove(vtd_as->iova_tree, map); if (vtd_dev_to_context_entry(s, bus_n, vtd_as->devfn, &ce) == 0) { trace_vtd_replay_ce_valid(s->root_scalable ? "scalable mode" : "legacy mode", bus_n, PCI_SLOT(vtd_as->devfn), PCI_FUNC(vtd_as->devfn), - vtd_get_domain_id(s, &ce), + vtd_get_domain_id(s, &ce, vtd_as->pasid), ce.hi, ce.lo); - if (vtd_as_has_map_notifier(vtd_as)) { + if (n->notifier_flags & IOMMU_NOTIFIER_MAP) { /* This is required only for MAP typed notifiers */ vtd_page_walk_info info = { .hook_fn = vtd_replay_hook, @@ -3619,10 +3921,10 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) .notify_unmap = false, .aw = s->aw_bits, .as = vtd_as, - .domain_id = vtd_get_domain_id(s, &ce), + .domain_id = vtd_get_domain_id(s, &ce, vtd_as->pasid), }; - vtd_page_walk(s, &ce, 0, ~0ULL, &info); + vtd_page_walk(s, &ce, 0, ~0ULL, &info, vtd_as->pasid); } } else { trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn), @@ -3722,6 +4024,10 @@ static void vtd_init(IntelIOMMUState *s) s->ecap |= VTD_ECAP_SC; } + if (s->pasid) { + s->ecap |= VTD_ECAP_PASID; + } + vtd_reset_caches(s); /* Define registers with default values and bit semantics */ @@ -3795,10 +4101,14 @@ static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) assert(0 <= devfn && devfn < PCI_DEVFN_MAX); - vtd_as = vtd_find_add_as(s, bus, devfn); + vtd_as = vtd_find_add_as(s, bus, devfn, PCI_NO_PASID); return &vtd_as->as; } +static PCIIOMMUOps vtd_iommu_ops = { + .get_address_space = vtd_host_dma_iommu, +}; + static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) { X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); @@ -3814,8 +4124,9 @@ static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; } if (s->intr_eim == ON_OFF_AUTO_ON && !s->buggy_eim) { - if (!kvm_irqchip_is_split()) { - error_setg(errp, "eim=on requires accel=kvm,kernel-irqchip=split"); + if (kvm_irqchip_is_split() && !kvm_enable_x2apic()) { + error_setg(errp, "eim=on requires support on the KVM side" + "(X2APIC_API, first shipped in v4.7)"); return false; } } @@ -3833,6 +4144,11 @@ static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) return false; } + if (s->pasid && !s->scalable_mode) { + error_setg(errp, "Need to set scalable mode for PASID"); + return false; + } + return true; } @@ -3867,8 +4183,19 @@ static void vtd_realize(DeviceState *dev, Error **errp) MachineState *ms = MACHINE(qdev_get_machine()); PCMachineState *pcms = PC_MACHINE(ms); X86MachineState *x86ms = X86_MACHINE(ms); - PCIBus *bus = pcms->bus; + PCIBus *bus = pcms->pcibus; IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev); + X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); + + if (s->pasid && x86_iommu->dt_supported) { + /* + * PASID-based-Device-TLB Invalidate Descriptor is not + * implemented and it requires support from vhost layer which + * needs to be implemented in the future. + */ + error_setg(errp, "PASID based device IOTLB is not supported"); + return; + } if (!vtd_decide_config(s, errp)) { return; @@ -3876,9 +4203,10 @@ static void vtd_realize(DeviceState *dev, Error **errp) QLIST_INIT(&s->vtd_as_with_notifiers); qemu_mutex_init(&s->iommu_lock); - memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num)); memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s, "intel_iommu", DMAR_REG_SIZE); + memory_region_add_subregion(get_system_memory(), + Q35_HOST_BRIDGE_IOMMU_ADDR, &s->csrmem); /* Create the shared memory regions by all devices */ memory_region_init(&s->mr_nodmar, OBJECT(s), "vtd-nodmar", @@ -3893,16 +4221,13 @@ static void vtd_realize(DeviceState *dev, Error **errp) memory_region_add_subregion_overlap(&s->mr_nodmar, VTD_INTERRUPT_ADDR_FIRST, &s->mr_ir, 1); - - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->csrmem); /* No corresponding destroy */ - s->iotlb = g_hash_table_new_full(vtd_uint64_hash, vtd_uint64_equal, + s->iotlb = g_hash_table_new_full(vtd_iotlb_hash, vtd_iotlb_equal, g_free, g_free); - s->vtd_as_by_busptr = g_hash_table_new_full(vtd_uint64_hash, vtd_uint64_equal, - g_free, g_free); + s->vtd_address_spaces = g_hash_table_new_full(vtd_as_hash, vtd_as_equal, + g_free, g_free); vtd_init(s); - sysbus_mmio_map(SYS_BUS_DEVICE(s), 0, Q35_HOST_BRIDGE_IOMMU_ADDR); - pci_setup_iommu(bus, vtd_host_dma_iommu, dev); + pci_setup_iommu(bus, &vtd_iommu_ops, dev); /* Pseudo address space under root PCI bus. */ x86ms->ioapic_as = vtd_host_dma_iommu(bus, s, Q35_PSEUDO_DEVFN_IOAPIC); qemu_add_machine_init_done_notifier(&vtd_machine_done_notify); diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 930ce61feb..f8cf99bddf 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -114,8 +114,9 @@ VTD_INTERRUPT_ADDR_FIRST + 1) /* The shift of source_id in the key of IOTLB hash table */ -#define VTD_IOTLB_SID_SHIFT 36 -#define VTD_IOTLB_LVL_SHIFT 52 +#define VTD_IOTLB_SID_SHIFT 26 +#define VTD_IOTLB_LVL_SHIFT 42 +#define VTD_IOTLB_PASID_SHIFT 44 #define VTD_IOTLB_MAX_SIZE 1024 /* Max size of the hash table */ /* IOTLB_REG */ @@ -191,6 +192,7 @@ #define VTD_ECAP_SC (1ULL << 7) #define VTD_ECAP_MHMV (15ULL << 20) #define VTD_ECAP_SRS (1ULL << 31) +#define VTD_ECAP_PASID (1ULL << 40) #define VTD_ECAP_SMTS (1ULL << 43) #define VTD_ECAP_SLTS (1ULL << 46) @@ -211,6 +213,8 @@ #define VTD_CAP_DRAIN_READ (1ULL << 55) #define VTD_CAP_DRAIN (VTD_CAP_DRAIN_READ | VTD_CAP_DRAIN_WRITE) #define VTD_CAP_CM (1ULL << 7) +#define VTD_PASID_ID_SHIFT 20 +#define VTD_PASID_ID_MASK ((1ULL << VTD_PASID_ID_SHIFT) - 1) /* Supported Adjusted Guest Address Widths */ #define VTD_CAP_SAGAW_SHIFT 8 @@ -262,6 +266,9 @@ #define VTD_FRCD_SID(val) ((val) & VTD_FRCD_SID_MASK) /* For the low 64-bit of 128-bit */ #define VTD_FRCD_FI(val) ((val) & ~0xfffULL) +#define VTD_FRCD_PV(val) (((val) & 0xffffULL) << 40) +#define VTD_FRCD_PP(val) (((val) & 0x1) << 31) +#define VTD_FRCD_IR_IDX(val) (((val) & 0xffffULL) << 48) /* DMA Remapping Fault Conditions */ typedef enum VTDFaultReason { @@ -315,12 +322,21 @@ typedef enum VTDFaultReason { /* Interrupt Entry Cache Invalidation Descriptor: VT-d 6.5.2.7. */ struct VTDInvDescIEC { +#if HOST_BIG_ENDIAN + uint64_t reserved_2:16; + uint64_t index:16; /* Start index to invalidate */ + uint64_t index_mask:5; /* 2^N for continuous int invalidation */ + uint64_t resved_1:22; + uint64_t granularity:1; /* If set, it's global IR invalidation */ + uint64_t type:4; /* Should always be 0x4 */ +#else uint32_t type:4; /* Should always be 0x4 */ uint32_t granularity:1; /* If set, it's global IR invalidation */ uint32_t resved_1:22; uint32_t index_mask:5; /* 2^N for continuous int invalidation */ uint32_t index:16; /* Start index to invalidate */ uint32_t reserved_2:16; +#endif }; typedef struct VTDInvDescIEC VTDInvDescIEC; @@ -379,6 +395,11 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_IOTLB_AM(val) ((val) & 0x3fULL) #define VTD_INV_DESC_IOTLB_RSVD_LO 0xffffffff0000ff00ULL #define VTD_INV_DESC_IOTLB_RSVD_HI 0xf80ULL +#define VTD_INV_DESC_IOTLB_PASID_PASID (2ULL << 4) +#define VTD_INV_DESC_IOTLB_PASID_PAGE (3ULL << 4) +#define VTD_INV_DESC_IOTLB_PASID(val) (((val) >> 32) & VTD_PASID_ID_MASK) +#define VTD_INV_DESC_IOTLB_PASID_RSVD_LO 0xfff00000000001c0ULL +#define VTD_INV_DESC_IOTLB_PASID_RSVD_HI 0xf80ULL /* Mask for Device IOTLB Invalidate Descriptor */ #define VTD_INV_DESC_DEVICE_IOTLB_ADDR(val) ((val) & 0xfffffffffffff000ULL) @@ -413,6 +434,7 @@ typedef union VTDInvDesc VTDInvDesc; /* Information about page-selective IOTLB invalidate */ struct VTDIOTLBPageInvInfo { uint16_t domain_id; + uint32_t pasid; uint64_t addr; uint8_t mask; }; diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c index 1e89ca0899..a72c28e8a7 100644 --- a/hw/i386/kvm/apic.c +++ b/hw/i386/kvm/apic.c @@ -95,9 +95,10 @@ void kvm_get_apic_state(DeviceState *dev, struct kvm_lapic_state *kapic) apic_next_timer(s, s->initial_count_load_time); } -static void kvm_apic_set_base(APICCommonState *s, uint64_t val) +static int kvm_apic_set_base(APICCommonState *s, uint64_t val) { s->apicbase = val; + return 0; } static void kvm_apic_set_tpr(APICCommonState *s, uint8_t val) diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index df70b4a033..40aa9a32c3 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -22,7 +22,7 @@ #include "kvm/kvm_i386.h" #include "migration/vmstate.h" #include "hw/sysbus.h" -#include "hw/kvm/clock.h" +#include "hw/i386/kvm/clock.h" #include "hw/qdev-properties.h" #include "qapi/error.h" @@ -66,7 +66,7 @@ struct pvclock_vcpu_time_info { static uint64_t kvmclock_current_nsec(KVMClockState *s) { CPUState *cpu = first_cpu; - CPUX86State *env = cpu->env_ptr; + CPUX86State *env = cpu_env(cpu); hwaddr kvmclock_struct_pa; uint64_t migration_tsc = env->tsc; struct pvclock_vcpu_time_info time; @@ -245,7 +245,7 @@ static const VMStateDescription kvmclock_reliable_get_clock = { .version_id = 1, .minimum_version_id = 1, .needed = kvmclock_clock_is_reliable_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(clock_is_reliable, KVMClockState), VMSTATE_END_OF_LIST() } @@ -295,11 +295,11 @@ static const VMStateDescription kvmclock_vmsd = { .minimum_version_id = 1, .pre_load = kvmclock_pre_load, .pre_save = kvmclock_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(clock, KVMClockState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &kvmclock_reliable_get_clock, NULL } @@ -332,9 +332,7 @@ void kvmclock_create(bool create_always) { X86CPU *cpu = X86_CPU(first_cpu); - if (!kvm_enabled() || !kvm_has_adjust_clock()) - return; - + assert(kvm_enabled()); if (create_always || cpu->env.features[FEAT_KVM] & ((1ULL << KVM_FEATURE_CLOCKSOURCE) | (1ULL << KVM_FEATURE_CLOCKSOURCE2))) { diff --git a/include/hw/kvm/clock.h b/hw/i386/kvm/clock.h similarity index 65% rename from include/hw/kvm/clock.h rename to hw/i386/kvm/clock.h index 7994071c4f..401c7e445b 100644 --- a/include/hw/kvm/clock.h +++ b/hw/i386/kvm/clock.h @@ -10,19 +10,9 @@ * See the COPYING file in the top-level directory. */ -#ifndef HW_KVM_CLOCK_H -#define HW_KVM_CLOCK_H - -#ifdef CONFIG_KVM +#ifndef HW_I386_KVM_CLOCK_H +#define HW_I386_KVM_CLOCK_H void kvmclock_create(bool create_always); -#else /* CONFIG_KVM */ - -static inline void kvmclock_create(bool create_always) -{ -} - -#endif /* !CONFIG_KVM */ - #endif diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index 191a26fa57..e49b9c4b56 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -34,6 +34,7 @@ #include "hw/timer/i8254_internal.h" #include "hw/qdev-properties-system.h" #include "sysemu/kvm.h" +#include "target/i386/kvm/kvm_i386.h" #include "qom/object.h" #define KVM_PIT_REINJECT_BIT 0 @@ -96,24 +97,12 @@ static void kvm_pit_get(PITCommonState *pit) return; } - if (kvm_has_pit_state2()) { - ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit); - if (ret < 0) { - fprintf(stderr, "KVM_GET_PIT2 failed: %s\n", strerror(-ret)); - abort(); - } - pit->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY; - } else { - /* - * kvm_pit_state2 is superset of kvm_pit_state struct, - * so we can use it for KVM_GET_PIT as well. - */ - ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT, &kpit); - if (ret < 0) { - fprintf(stderr, "KVM_GET_PIT failed: %s\n", strerror(-ret)); - abort(); - } + ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit); + if (ret < 0) { + fprintf(stderr, "KVM_GET_PIT2 failed: %s\n", strerror(-ret)); + abort(); } + pit->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY; for (i = 0; i < 3; i++) { kchan = &kpit.channels[i]; sc = &pit->channels[i]; @@ -169,12 +158,9 @@ static void kvm_pit_put(PITCommonState *pit) kchan->count_load_time = sc->count_load_time - s->kernel_clock_offset; } - ret = kvm_vm_ioctl(kvm_state, - kvm_has_pit_state2() ? KVM_SET_PIT2 : KVM_SET_PIT, - &kpit); + ret = kvm_vm_ioctl(kvm_state, KVM_SET_PIT2, &kpit); if (ret < 0) { - fprintf(stderr, "%s failed: %s\n", - kvm_has_pit_state2() ? "KVM_SET_PIT2" : "KVM_SET_PIT", + fprintf(stderr, "KVM_SET_PIT2 failed: %s\n", strerror(-ret)); abort(); } @@ -260,11 +246,12 @@ static void kvm_pit_realizefn(DeviceState *dev, Error **errp) }; int ret; - if (kvm_check_extension(kvm_state, KVM_CAP_PIT2)) { - ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT2, &config); - } else { - ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT); + if (!kvm_check_extension(kvm_state, KVM_CAP_PIT_STATE2) || + !kvm_check_extension(kvm_state, KVM_CAP_PIT2)) { + error_setg(errp, "In-kernel PIT not available"); } + + ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT2, &config); if (ret < 0) { error_setg(errp, "Create kernel PIC irqchip failed: %s", strerror(-ret)); @@ -301,7 +288,6 @@ static void kvm_pit_realizefn(DeviceState *dev, Error **errp) } static Property kvm_pit_properties[] = { - DEFINE_PROP_UINT32("iobase", PITCommonState, iobase, -1), DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", KVMPITState, lost_tick_policy, LOST_TICK_POLICY_DELAY), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/i386/kvm/i8259.c b/hw/i386/kvm/i8259.c index d61bae4dc3..3ca0e1ff03 100644 --- a/hw/i386/kvm/i8259.c +++ b/hw/i386/kvm/i8259.c @@ -14,7 +14,7 @@ #include "hw/isa/i8259_internal.h" #include "hw/intc/i8259.h" #include "qemu/module.h" -#include "hw/i386/apic_internal.h" +#include "hw/intc/kvm_irqcount.h" #include "hw/irq.h" #include "sysemu/kvm.h" #include "qom/object.h" @@ -117,7 +117,7 @@ static void kvm_pic_set_irq(void *opaque, int irq, int level) pic_stat_update_irq(irq, level); delivered = kvm_set_irq(kvm_state, irq, level); - apic_report_irq_delivered(delivered); + kvm_report_irq_delivered(delivered); } static void kvm_pic_realize(DeviceState *dev, Error **errp) diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c index ee7c8ef68b..b96fe84eed 100644 --- a/hw/i386/kvm/ioapic.c +++ b/hw/i386/kvm/ioapic.c @@ -12,11 +12,11 @@ #include "qemu/osdep.h" #include "monitor/monitor.h" -#include "hw/i386/x86.h" #include "hw/qdev-properties.h" -#include "hw/i386/ioapic_internal.h" -#include "hw/i386/apic_internal.h" +#include "hw/intc/ioapic_internal.h" +#include "hw/intc/kvm_irqcount.h" #include "sysemu/kvm.h" +#include "kvm/kvm_i386.h" /* PC Utility function */ void kvm_pc_setup_irq_routing(bool pci_enabled) @@ -35,7 +35,7 @@ void kvm_pc_setup_irq_routing(bool pci_enabled) kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_PIC_SLAVE, i - 8); } if (pci_enabled) { - for (i = 0; i < 24; ++i) { + for (i = 0; i < KVM_IOAPIC_NUM_PINS; ++i) { if (i == 0) { kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_IOAPIC, 2); } else if (i != 2) { @@ -116,7 +116,7 @@ static void kvm_ioapic_set_irq(void *opaque, int irq, int level) ioapic_stat_update_irq(common, irq, level); delivered = kvm_set_irq(kvm_state, s->kvm_gsi_base + irq, level); - apic_report_irq_delivered(delivered); + kvm_report_irq_delivered(delivered); } static void kvm_ioapic_realize(DeviceState *dev, Error **errp) diff --git a/hw/i386/kvm/meson.build b/hw/i386/kvm/meson.build index 95467f1ded..a4a2e23c06 100644 --- a/hw/i386/kvm/meson.build +++ b/hw/i386/kvm/meson.build @@ -4,5 +4,20 @@ i386_kvm_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c')) i386_kvm_ss.add(when: 'CONFIG_I8254', if_true: files('i8254.c')) i386_kvm_ss.add(when: 'CONFIG_I8259', if_true: files('i8259.c')) i386_kvm_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic.c')) +i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files( + 'xen_overlay.c', + 'xen_evtchn.c', + 'xen_gnttab.c', + 'xen_xenstore.c', + 'xen_primary_console.c', + 'xenstore_impl.c', + )) i386_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss) + +xen_stubs_ss = ss.source_set() +xen_stubs_ss.add(when: 'CONFIG_XEN_EMU', if_false: files( + 'xen-stubs.c', +)) + +specific_ss.add_all(when: 'CONFIG_SYSTEM_ONLY', if_true: xen_stubs_ss) diff --git a/hw/i386/kvm/trace-events b/hw/i386/kvm/trace-events new file mode 100644 index 0000000000..67bf7f174e --- /dev/null +++ b/hw/i386/kvm/trace-events @@ -0,0 +1,22 @@ +kvm_xen_map_pirq(int pirq, int gsi) "pirq %d gsi %d" +kvm_xen_unmap_pirq(int pirq, int gsi) "pirq %d gsi %d" +kvm_xen_get_free_pirq(int pirq, int type) "pirq %d type %d" +kvm_xen_bind_pirq(int pirq, int port) "pirq %d port %d" +kvm_xen_unmask_pirq(int pirq, char *dev, int vector) "pirq %d dev %s vector %d" +xenstore_error(unsigned int id, unsigned int tx_id, const char *err) "req %u tx %u err %s" +xenstore_read(unsigned int tx_id, const char *path) "tx %u path %s" +xenstore_write(unsigned int tx_id, const char *path) "tx %u path %s" +xenstore_mkdir(unsigned int tx_id, const char *path) "tx %u path %s" +xenstore_directory(unsigned int tx_id, const char *path) "tx %u path %s" +xenstore_directory_part(unsigned int tx_id, const char *path, unsigned int offset) "tx %u path %s offset %u" +xenstore_transaction_start(unsigned int new_tx) "new_tx %u" +xenstore_transaction_end(unsigned int tx_id, bool commit) "tx %u commit %d" +xenstore_rm(unsigned int tx_id, const char *path) "tx %u path %s" +xenstore_get_perms(unsigned int tx_id, const char *path) "tx %u path %s" +xenstore_set_perms(unsigned int tx_id, const char *path) "tx %u path %s" +xenstore_watch(const char *path, const char *token) "path %s token %s" +xenstore_unwatch(const char *path, const char *token) "path %s token %s" +xenstore_reset_watches(void) "" +xenstore_watch_event(const char *path, const char *token) "path %s token %s" +xen_primary_console_create(void) "" +xen_primary_console_reset(int port) "port %u" diff --git a/hw/i386/kvm/trace.h b/hw/i386/kvm/trace.h new file mode 100644 index 0000000000..e55d0812fd --- /dev/null +++ b/hw/i386/kvm/trace.h @@ -0,0 +1 @@ +#include "trace/trace-hw_i386_kvm.h" diff --git a/hw/i386/kvm/xen-stubs.c b/hw/i386/kvm/xen-stubs.c new file mode 100644 index 0000000000..d03131e686 --- /dev/null +++ b/hw/i386/kvm/xen-stubs.c @@ -0,0 +1,52 @@ +/* + * QEMU Xen emulation: QMP stubs + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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/error.h" +#include "qapi/qapi-commands-misc-target.h" + +#include "xen_evtchn.h" +#include "xen_primary_console.h" + +void xen_evtchn_snoop_msi(PCIDevice *dev, bool is_msix, unsigned int vector, + uint64_t addr, uint32_t data, bool is_masked) +{ +} + +void xen_evtchn_remove_pci_device(PCIDevice *dev) +{ +} + +bool xen_evtchn_deliver_pirq_msi(uint64_t address, uint32_t data) +{ + return false; +} + +void xen_primary_console_create(void) +{ +} + +void xen_primary_console_set_be_port(uint16_t port) +{ +} +#ifdef TARGET_I386 +EvtchnInfoList *qmp_xen_event_list(Error **errp) +{ + error_setg(errp, "Xen event channel emulation not enabled"); + return NULL; +} + +void qmp_xen_event_inject(uint32_t port, Error **errp) +{ + error_setg(errp, "Xen event channel emulation not enabled"); +} +#endif diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c new file mode 100644 index 0000000000..07bd0c9ab8 --- /dev/null +++ b/hw/i386/kvm/xen_evtchn.c @@ -0,0 +1,2360 @@ +/* + * QEMU Xen emulation: Event channel support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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/host-utils.h" +#include "qemu/module.h" +#include "qemu/lockable.h" +#include "qemu/main-loop.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "monitor/monitor.h" +#include "monitor/hmp.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-target.h" +#include "qapi/qmp/qdict.h" +#include "qom/object.h" +#include "exec/target_page.h" +#include "exec/address-spaces.h" +#include "migration/vmstate.h" +#include "trace.h" + +#include "hw/sysbus.h" +#include "hw/xen/xen.h" +#include "hw/i386/x86.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" +#include "hw/irq.h" +#include "hw/xen/xen_backend_ops.h" + +#include "xen_evtchn.h" +#include "xen_overlay.h" +#include "xen_xenstore.h" + +#include "sysemu/kvm.h" +#include "sysemu/kvm_xen.h" +#include +#include + +#include "hw/xen/interface/memory.h" +#include "hw/xen/interface/hvm/params.h" + +/* XX: For kvm_update_msi_routes_all() */ +#include "target/i386/kvm/kvm_i386.h" + +#define TYPE_XEN_EVTCHN "xen-evtchn" +OBJECT_DECLARE_SIMPLE_TYPE(XenEvtchnState, XEN_EVTCHN) + +typedef struct XenEvtchnPort { + uint32_t vcpu; /* Xen/ACPI vcpu_id */ + uint16_t type; /* EVTCHNSTAT_xxxx */ + union { + uint16_t val; /* raw value for serialization etc. */ + uint16_t pirq; + uint16_t virq; + struct { + uint16_t port:15; + uint16_t to_qemu:1; /* Only two targets; qemu or loopback */ + } interdomain; + } u; +} XenEvtchnPort; + +/* 32-bit compatibility definitions, also used natively in 32-bit build */ +struct compat_arch_vcpu_info { + unsigned int cr2; + unsigned int pad[5]; +}; + +struct compat_vcpu_info { + uint8_t evtchn_upcall_pending; + uint8_t evtchn_upcall_mask; + uint16_t pad; + uint32_t evtchn_pending_sel; + struct compat_arch_vcpu_info arch; + struct vcpu_time_info time; +}; /* 64 bytes (x86) */ + +struct compat_arch_shared_info { + unsigned int max_pfn; + unsigned int pfn_to_mfn_frame_list_list; + unsigned int nmi_reason; + unsigned int p2m_cr3; + unsigned int p2m_vaddr; + unsigned int p2m_generation; + uint32_t wc_sec_hi; +}; + +struct compat_shared_info { + struct compat_vcpu_info vcpu_info[XEN_LEGACY_MAX_VCPUS]; + uint32_t evtchn_pending[32]; + uint32_t evtchn_mask[32]; + uint32_t wc_version; /* Version counter: see vcpu_time_info_t. */ + uint32_t wc_sec; + uint32_t wc_nsec; + struct compat_arch_shared_info arch; +}; + +#define COMPAT_EVTCHN_2L_NR_CHANNELS 1024 + +/* Local private implementation of struct xenevtchn_handle */ +struct xenevtchn_handle { + evtchn_port_t be_port; + evtchn_port_t guest_port; /* Or zero for unbound */ + int fd; +}; + +/* + * These 'emuirq' values are used by Xen in the LM stream... and yes, I am + * insane enough to think about guest-transparent live migration from actual + * Xen to QEMU, and ensuring that we can convert/consume the stream. + */ +#define IRQ_UNBOUND -1 +#define IRQ_PT -2 +#define IRQ_MSI_EMU -3 + + +struct pirq_info { + int gsi; + uint16_t port; + PCIDevice *dev; + int vector; + bool is_msix; + bool is_masked; + bool is_translated; +}; + +struct XenEvtchnState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + uint64_t callback_param; + bool evtchn_in_kernel; + uint32_t callback_gsi; + + QEMUBH *gsi_bh; + + QemuMutex port_lock; + uint32_t nr_ports; + XenEvtchnPort port_table[EVTCHN_2L_NR_CHANNELS]; + + /* Connected to the system GSIs for raising callback as GSI / INTx */ + unsigned int nr_callback_gsis; + qemu_irq *callback_gsis; + + struct xenevtchn_handle *be_handles[EVTCHN_2L_NR_CHANNELS]; + + uint32_t nr_pirqs; + + /* Bitmap of allocated PIRQs (serialized) */ + uint16_t nr_pirq_inuse_words; + uint64_t *pirq_inuse_bitmap; + + /* GSI → PIRQ mapping (serialized) */ + uint16_t gsi_pirq[IOAPIC_NUM_PINS]; + + /* Per-GSI assertion state (serialized) */ + uint32_t pirq_gsi_set; + + /* Per-PIRQ information (rebuilt on migration, protected by BQL) */ + struct pirq_info *pirq; +}; + +#define pirq_inuse_word(s, pirq) (s->pirq_inuse_bitmap[((pirq) / 64)]) +#define pirq_inuse_bit(pirq) (1ULL << ((pirq) & 63)) + +#define pirq_inuse(s, pirq) (pirq_inuse_word(s, pirq) & pirq_inuse_bit(pirq)) + +struct XenEvtchnState *xen_evtchn_singleton; + +/* Top bits of callback_param are the type (HVM_PARAM_CALLBACK_TYPE_xxx) */ +#define CALLBACK_VIA_TYPE_SHIFT 56 + +static void unbind_backend_ports(XenEvtchnState *s); + +static int xen_evtchn_pre_load(void *opaque) +{ + XenEvtchnState *s = opaque; + + /* Unbind all the backend-side ports; they need to rebind */ + unbind_backend_ports(s); + + /* It'll be leaked otherwise. */ + g_free(s->pirq_inuse_bitmap); + s->pirq_inuse_bitmap = NULL; + + return 0; +} + +static int xen_evtchn_post_load(void *opaque, int version_id) +{ + XenEvtchnState *s = opaque; + uint32_t i; + + if (s->callback_param) { + xen_evtchn_set_callback_param(s->callback_param); + } + + /* Rebuild s->pirq[].port mapping */ + for (i = 0; i < s->nr_ports; i++) { + XenEvtchnPort *p = &s->port_table[i]; + + if (p->type == EVTCHNSTAT_pirq) { + assert(p->u.pirq); + assert(p->u.pirq < s->nr_pirqs); + + /* + * Set the gsi to IRQ_UNBOUND; it may be changed to an actual + * GSI# below, or to IRQ_MSI_EMU when the MSI table snooping + * catches up with it. + */ + s->pirq[p->u.pirq].gsi = IRQ_UNBOUND; + s->pirq[p->u.pirq].port = i; + } + } + /* Rebuild s->pirq[].gsi mapping */ + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + if (s->gsi_pirq[i]) { + s->pirq[s->gsi_pirq[i]].gsi = i; + } + } + return 0; +} + +static bool xen_evtchn_is_needed(void *opaque) +{ + return xen_mode == XEN_EMULATE; +} + +static const VMStateDescription xen_evtchn_port_vmstate = { + .name = "xen_evtchn_port", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(vcpu, XenEvtchnPort), + VMSTATE_UINT16(type, XenEvtchnPort), + VMSTATE_UINT16(u.val, XenEvtchnPort), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription xen_evtchn_vmstate = { + .name = "xen_evtchn", + .version_id = 1, + .minimum_version_id = 1, + .needed = xen_evtchn_is_needed, + .pre_load = xen_evtchn_pre_load, + .post_load = xen_evtchn_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(callback_param, XenEvtchnState), + VMSTATE_UINT32(nr_ports, XenEvtchnState), + VMSTATE_STRUCT_VARRAY_UINT32(port_table, XenEvtchnState, nr_ports, 1, + xen_evtchn_port_vmstate, XenEvtchnPort), + VMSTATE_UINT16_ARRAY(gsi_pirq, XenEvtchnState, IOAPIC_NUM_PINS), + VMSTATE_VARRAY_UINT16_ALLOC(pirq_inuse_bitmap, XenEvtchnState, + nr_pirq_inuse_words, 0, + vmstate_info_uint64, uint64_t), + VMSTATE_UINT32(pirq_gsi_set, XenEvtchnState), + VMSTATE_END_OF_LIST() + } +}; + +static void xen_evtchn_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &xen_evtchn_vmstate; +} + +static const TypeInfo xen_evtchn_info = { + .name = TYPE_XEN_EVTCHN, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XenEvtchnState), + .class_init = xen_evtchn_class_init, +}; + +static struct evtchn_backend_ops emu_evtchn_backend_ops = { + .open = xen_be_evtchn_open, + .bind_interdomain = xen_be_evtchn_bind_interdomain, + .unbind = xen_be_evtchn_unbind, + .close = xen_be_evtchn_close, + .get_fd = xen_be_evtchn_fd, + .notify = xen_be_evtchn_notify, + .unmask = xen_be_evtchn_unmask, + .pending = xen_be_evtchn_pending, +}; + +static void gsi_assert_bh(void *opaque) +{ + struct vcpu_info *vi = kvm_xen_get_vcpu_info_hva(0); + if (vi) { + xen_evtchn_set_callback_level(!!vi->evtchn_upcall_pending); + } +} + +void xen_evtchn_create(unsigned int nr_gsis, qemu_irq *system_gsis) +{ + XenEvtchnState *s = XEN_EVTCHN(sysbus_create_simple(TYPE_XEN_EVTCHN, + -1, NULL)); + int i; + + xen_evtchn_singleton = s; + + qemu_mutex_init(&s->port_lock); + s->gsi_bh = aio_bh_new(qemu_get_aio_context(), gsi_assert_bh, s); + + /* + * These are the *output* GSI from event channel support, for + * signalling CPU0's events via GSI or PCI INTx instead of the + * per-CPU vector. We create a *set* of irqs and connect one to + * each of the system GSIs which were passed in from the platform + * code, and then just trigger the right one as appropriate from + * xen_evtchn_set_callback_level(). + */ + s->nr_callback_gsis = nr_gsis; + s->callback_gsis = g_new0(qemu_irq, nr_gsis); + for (i = 0; i < nr_gsis; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->callback_gsis[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(s), i, system_gsis[i]); + } + + /* + * The Xen scheme for encoding PIRQ# into an MSI message is not + * compatible with 32-bit MSI, as it puts the high bits of the + * PIRQ# into the high bits of the MSI message address, instead of + * using the Extended Destination ID in address bits 4-11 which + * perhaps would have been a better choice. + * + * To keep life simple, kvm_accel_instance_init() initialises the + * default to 256. which conveniently doesn't need to set anything + * outside the low 32 bits of the address. It can be increased by + * setting the xen-evtchn-max-pirq property. + */ + s->nr_pirqs = kvm_xen_get_evtchn_max_pirq(); + + s->nr_pirq_inuse_words = DIV_ROUND_UP(s->nr_pirqs, 64); + s->pirq_inuse_bitmap = g_new0(uint64_t, s->nr_pirq_inuse_words); + s->pirq = g_new0(struct pirq_info, s->nr_pirqs); + + /* Set event channel functions for backend drivers to use */ + xen_evtchn_ops = &emu_evtchn_backend_ops; +} + +static void xen_evtchn_register_types(void) +{ + type_register_static(&xen_evtchn_info); +} + +type_init(xen_evtchn_register_types) + +static int set_callback_pci_intx(XenEvtchnState *s, uint64_t param) +{ + PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); + uint8_t pin = param & 3; + uint8_t devfn = (param >> 8) & 0xff; + uint16_t bus = (param >> 16) & 0xffff; + uint16_t domain = (param >> 32) & 0xffff; + PCIDevice *pdev; + PCIINTxRoute r; + + if (domain || !pcms) { + return 0; + } + + pdev = pci_find_device(pcms->pcibus, bus, devfn); + if (!pdev) { + return 0; + } + + r = pci_device_route_intx_to_irq(pdev, pin); + if (r.mode != PCI_INTX_ENABLED) { + return 0; + } + + /* + * Hm, can we be notified of INTX routing changes? Not without + * *owning* the device and being allowed to overwrite its own + * ->intx_routing_notifier, AFAICT. So let's not. + */ + return r.irq; +} + +void xen_evtchn_set_callback_level(int level) +{ + XenEvtchnState *s = xen_evtchn_singleton; + if (!s) { + return; + } + + /* + * We get to this function in a number of ways: + * + * • From I/O context, via PV backend drivers sending a notification to + * the guest. + * + * • From guest vCPU context, via loopback interdomain event channels + * (or theoretically even IPIs but guests don't use those with GSI + * delivery because that's pointless. We don't want a malicious guest + * to be able to trigger a deadlock though, so we can't rule it out.) + * + * • From guest vCPU context when the HVM_PARAM_CALLBACK_IRQ is being + * configured. + * + * • From guest vCPU context in the KVM exit handler, if the upcall + * pending flag has been cleared and the GSI needs to be deasserted. + * + * • Maybe in future, in an interrupt ack/eoi notifier when the GSI has + * been acked in the irqchip. + * + * Whichever context we come from if we aren't already holding the BQL + * then e can't take it now, as we may already hold s->port_lock. So + * trigger the BH to set the IRQ for us instead of doing it immediately. + * + * In the HVM_PARAM_CALLBACK_IRQ and KVM exit handler cases, the caller + * will deliberately take the BQL because they want the change to take + * effect immediately. That just leaves interdomain loopback as the case + * which uses the BH. + */ + if (!bql_locked()) { + qemu_bh_schedule(s->gsi_bh); + return; + } + + if (s->callback_gsi && s->callback_gsi < s->nr_callback_gsis) { + qemu_set_irq(s->callback_gsis[s->callback_gsi], level); + if (level) { + /* Ensure the vCPU polls for deassertion */ + kvm_xen_set_callback_asserted(); + } + } +} + +int xen_evtchn_set_callback_param(uint64_t param) +{ + XenEvtchnState *s = xen_evtchn_singleton; + struct kvm_xen_hvm_attr xa = { + .type = KVM_XEN_ATTR_TYPE_UPCALL_VECTOR, + .u.vector = 0, + }; + bool in_kernel = false; + uint32_t gsi = 0; + int type = param >> CALLBACK_VIA_TYPE_SHIFT; + int ret; + + if (!s) { + return -ENOTSUP; + } + + /* + * We need the BQL because set_callback_pci_intx() may call into PCI code, + * and because we may need to manipulate the old and new GSI levels. + */ + assert(bql_locked()); + qemu_mutex_lock(&s->port_lock); + + switch (type) { + case HVM_PARAM_CALLBACK_TYPE_VECTOR: { + xa.u.vector = (uint8_t)param, + + ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &xa); + if (!ret && kvm_xen_has_cap(EVTCHN_SEND)) { + in_kernel = true; + } + gsi = 0; + break; + } + + case HVM_PARAM_CALLBACK_TYPE_PCI_INTX: + gsi = set_callback_pci_intx(s, param); + ret = gsi ? 0 : -EINVAL; + break; + + case HVM_PARAM_CALLBACK_TYPE_GSI: + gsi = (uint32_t)param; + ret = 0; + break; + + default: + /* Xen doesn't return error even if you set something bogus */ + ret = 0; + break; + } + + /* If the guest has set a per-vCPU callback vector, prefer that. */ + if (gsi && kvm_xen_has_vcpu_callback_vector()) { + in_kernel = kvm_xen_has_cap(EVTCHN_SEND); + gsi = 0; + } + + if (!ret) { + /* If vector delivery was turned *off* then tell the kernel */ + if ((s->callback_param >> CALLBACK_VIA_TYPE_SHIFT) == + HVM_PARAM_CALLBACK_TYPE_VECTOR && !xa.u.vector) { + kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &xa); + } + s->callback_param = param; + s->evtchn_in_kernel = in_kernel; + + if (gsi != s->callback_gsi) { + struct vcpu_info *vi = kvm_xen_get_vcpu_info_hva(0); + + xen_evtchn_set_callback_level(0); + s->callback_gsi = gsi; + + if (gsi && vi && vi->evtchn_upcall_pending) { + kvm_xen_inject_vcpu_callback_vector(0, type); + } + } + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + +static void inject_callback(XenEvtchnState *s, uint32_t vcpu) +{ + int type = s->callback_param >> CALLBACK_VIA_TYPE_SHIFT; + + kvm_xen_inject_vcpu_callback_vector(vcpu, type); +} + +static void deassign_kernel_port(evtchn_port_t port) +{ + struct kvm_xen_hvm_attr ha; + int ret; + + ha.type = KVM_XEN_ATTR_TYPE_EVTCHN; + ha.u.evtchn.send_port = port; + ha.u.evtchn.flags = KVM_XEN_EVTCHN_DEASSIGN; + + ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &ha); + if (ret) { + qemu_log_mask(LOG_GUEST_ERROR, "Failed to unbind kernel port %d: %s\n", + port, strerror(ret)); + } +} + +static int assign_kernel_port(uint16_t type, evtchn_port_t port, + uint32_t vcpu_id) +{ + CPUState *cpu = qemu_get_cpu(vcpu_id); + struct kvm_xen_hvm_attr ha; + + if (!cpu) { + return -ENOENT; + } + + ha.type = KVM_XEN_ATTR_TYPE_EVTCHN; + ha.u.evtchn.send_port = port; + ha.u.evtchn.type = type; + ha.u.evtchn.flags = 0; + ha.u.evtchn.deliver.port.port = port; + ha.u.evtchn.deliver.port.vcpu = kvm_arch_vcpu_id(cpu); + ha.u.evtchn.deliver.port.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL; + + return kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &ha); +} + +static int assign_kernel_eventfd(uint16_t type, evtchn_port_t port, int fd) +{ + struct kvm_xen_hvm_attr ha; + + ha.type = KVM_XEN_ATTR_TYPE_EVTCHN; + ha.u.evtchn.send_port = port; + ha.u.evtchn.type = type; + ha.u.evtchn.flags = 0; + ha.u.evtchn.deliver.eventfd.port = 0; + ha.u.evtchn.deliver.eventfd.fd = fd; + + return kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &ha); +} + +static bool valid_port(evtchn_port_t port) +{ + if (!port) { + return false; + } + + if (xen_is_long_mode()) { + return port < EVTCHN_2L_NR_CHANNELS; + } else { + return port < COMPAT_EVTCHN_2L_NR_CHANNELS; + } +} + +static bool valid_vcpu(uint32_t vcpu) +{ + return !!qemu_get_cpu(vcpu); +} + +static void unbind_backend_ports(XenEvtchnState *s) +{ + XenEvtchnPort *p; + int i; + + for (i = 1; i < s->nr_ports; i++) { + p = &s->port_table[i]; + if (p->type == EVTCHNSTAT_interdomain && p->u.interdomain.to_qemu) { + evtchn_port_t be_port = p->u.interdomain.port; + + if (s->be_handles[be_port]) { + /* This part will be overwritten on the load anyway. */ + p->type = EVTCHNSTAT_unbound; + p->u.interdomain.port = 0; + + /* Leave the backend port open and unbound too. */ + if (kvm_xen_has_cap(EVTCHN_SEND)) { + deassign_kernel_port(i); + } + s->be_handles[be_port]->guest_port = 0; + } + } + } +} + +int xen_evtchn_status_op(struct evtchn_status *status) +{ + XenEvtchnState *s = xen_evtchn_singleton; + XenEvtchnPort *p; + + if (!s) { + return -ENOTSUP; + } + + if (status->dom != DOMID_SELF && status->dom != xen_domid) { + return -ESRCH; + } + + if (!valid_port(status->port)) { + return -EINVAL; + } + + qemu_mutex_lock(&s->port_lock); + + p = &s->port_table[status->port]; + + status->status = p->type; + status->vcpu = p->vcpu; + + switch (p->type) { + case EVTCHNSTAT_unbound: + status->u.unbound.dom = p->u.interdomain.to_qemu ? DOMID_QEMU + : xen_domid; + break; + + case EVTCHNSTAT_interdomain: + status->u.interdomain.dom = p->u.interdomain.to_qemu ? DOMID_QEMU + : xen_domid; + status->u.interdomain.port = p->u.interdomain.port; + break; + + case EVTCHNSTAT_pirq: + status->u.pirq = p->u.pirq; + break; + + case EVTCHNSTAT_virq: + status->u.virq = p->u.virq; + break; + } + + qemu_mutex_unlock(&s->port_lock); + return 0; +} + +/* + * Never thought I'd hear myself say this, but C++ templates would be + * kind of nice here. + * + * template static int do_unmask_port(T *shinfo, ...); + */ +static int do_unmask_port_lm(XenEvtchnState *s, evtchn_port_t port, + bool do_unmask, struct shared_info *shinfo, + struct vcpu_info *vcpu_info) +{ + const int bits_per_word = BITS_PER_BYTE * sizeof(shinfo->evtchn_pending[0]); + typeof(shinfo->evtchn_pending[0]) mask; + int idx = port / bits_per_word; + int offset = port % bits_per_word; + + mask = 1UL << offset; + + if (idx >= bits_per_word) { + return -EINVAL; + } + + if (do_unmask) { + /* + * If this is a true unmask operation, clear the mask bit. If + * it was already unmasked, we have nothing further to do. + */ + if (!((qatomic_fetch_and(&shinfo->evtchn_mask[idx], ~mask) & mask))) { + return 0; + } + } else { + /* + * This is a pseudo-unmask for affinity changes. We don't + * change the mask bit, and if it's *masked* we have nothing + * else to do. + */ + if (qatomic_fetch_or(&shinfo->evtchn_mask[idx], 0) & mask) { + return 0; + } + } + + /* If the event was not pending, we're done. */ + if (!(qatomic_fetch_or(&shinfo->evtchn_pending[idx], 0) & mask)) { + return 0; + } + + /* Now on to the vcpu_info evtchn_pending_sel index... */ + mask = 1UL << idx; + + /* If a port in this word was already pending for this vCPU, all done. */ + if (qatomic_fetch_or(&vcpu_info->evtchn_pending_sel, mask) & mask) { + return 0; + } + + /* Set evtchn_upcall_pending for this vCPU */ + if (qatomic_fetch_or(&vcpu_info->evtchn_upcall_pending, 1)) { + return 0; + } + + inject_callback(s, s->port_table[port].vcpu); + + return 0; +} + +static int do_unmask_port_compat(XenEvtchnState *s, evtchn_port_t port, + bool do_unmask, + struct compat_shared_info *shinfo, + struct compat_vcpu_info *vcpu_info) +{ + const int bits_per_word = BITS_PER_BYTE * sizeof(shinfo->evtchn_pending[0]); + typeof(shinfo->evtchn_pending[0]) mask; + int idx = port / bits_per_word; + int offset = port % bits_per_word; + + mask = 1UL << offset; + + if (idx >= bits_per_word) { + return -EINVAL; + } + + if (do_unmask) { + /* + * If this is a true unmask operation, clear the mask bit. If + * it was already unmasked, we have nothing further to do. + */ + if (!((qatomic_fetch_and(&shinfo->evtchn_mask[idx], ~mask) & mask))) { + return 0; + } + } else { + /* + * This is a pseudo-unmask for affinity changes. We don't + * change the mask bit, and if it's *masked* we have nothing + * else to do. + */ + if (qatomic_fetch_or(&shinfo->evtchn_mask[idx], 0) & mask) { + return 0; + } + } + + /* If the event was not pending, we're done. */ + if (!(qatomic_fetch_or(&shinfo->evtchn_pending[idx], 0) & mask)) { + return 0; + } + + /* Now on to the vcpu_info evtchn_pending_sel index... */ + mask = 1UL << idx; + + /* If a port in this word was already pending for this vCPU, all done. */ + if (qatomic_fetch_or(&vcpu_info->evtchn_pending_sel, mask) & mask) { + return 0; + } + + /* Set evtchn_upcall_pending for this vCPU */ + if (qatomic_fetch_or(&vcpu_info->evtchn_upcall_pending, 1)) { + return 0; + } + + inject_callback(s, s->port_table[port].vcpu); + + return 0; +} + +static int unmask_port(XenEvtchnState *s, evtchn_port_t port, bool do_unmask) +{ + void *vcpu_info, *shinfo; + + if (s->port_table[port].type == EVTCHNSTAT_closed) { + return -EINVAL; + } + + shinfo = xen_overlay_get_shinfo_ptr(); + if (!shinfo) { + return -ENOTSUP; + } + + vcpu_info = kvm_xen_get_vcpu_info_hva(s->port_table[port].vcpu); + if (!vcpu_info) { + return -EINVAL; + } + + if (xen_is_long_mode()) { + return do_unmask_port_lm(s, port, do_unmask, shinfo, vcpu_info); + } else { + return do_unmask_port_compat(s, port, do_unmask, shinfo, vcpu_info); + } +} + +static int do_set_port_lm(XenEvtchnState *s, evtchn_port_t port, + struct shared_info *shinfo, + struct vcpu_info *vcpu_info) +{ + const int bits_per_word = BITS_PER_BYTE * sizeof(shinfo->evtchn_pending[0]); + typeof(shinfo->evtchn_pending[0]) mask; + int idx = port / bits_per_word; + int offset = port % bits_per_word; + + mask = 1UL << offset; + + if (idx >= bits_per_word) { + return -EINVAL; + } + + /* Update the pending bit itself. If it was already set, we're done. */ + if (qatomic_fetch_or(&shinfo->evtchn_pending[idx], mask) & mask) { + return 0; + } + + /* Check if it's masked. */ + if (qatomic_fetch_or(&shinfo->evtchn_mask[idx], 0) & mask) { + return 0; + } + + /* Now on to the vcpu_info evtchn_pending_sel index... */ + mask = 1UL << idx; + + /* If a port in this word was already pending for this vCPU, all done. */ + if (qatomic_fetch_or(&vcpu_info->evtchn_pending_sel, mask) & mask) { + return 0; + } + + /* Set evtchn_upcall_pending for this vCPU */ + if (qatomic_fetch_or(&vcpu_info->evtchn_upcall_pending, 1)) { + return 0; + } + + inject_callback(s, s->port_table[port].vcpu); + + return 0; +} + +static int do_set_port_compat(XenEvtchnState *s, evtchn_port_t port, + struct compat_shared_info *shinfo, + struct compat_vcpu_info *vcpu_info) +{ + const int bits_per_word = BITS_PER_BYTE * sizeof(shinfo->evtchn_pending[0]); + typeof(shinfo->evtchn_pending[0]) mask; + int idx = port / bits_per_word; + int offset = port % bits_per_word; + + mask = 1UL << offset; + + if (idx >= bits_per_word) { + return -EINVAL; + } + + /* Update the pending bit itself. If it was already set, we're done. */ + if (qatomic_fetch_or(&shinfo->evtchn_pending[idx], mask) & mask) { + return 0; + } + + /* Check if it's masked. */ + if (qatomic_fetch_or(&shinfo->evtchn_mask[idx], 0) & mask) { + return 0; + } + + /* Now on to the vcpu_info evtchn_pending_sel index... */ + mask = 1UL << idx; + + /* If a port in this word was already pending for this vCPU, all done. */ + if (qatomic_fetch_or(&vcpu_info->evtchn_pending_sel, mask) & mask) { + return 0; + } + + /* Set evtchn_upcall_pending for this vCPU */ + if (qatomic_fetch_or(&vcpu_info->evtchn_upcall_pending, 1)) { + return 0; + } + + inject_callback(s, s->port_table[port].vcpu); + + return 0; +} + +static int set_port_pending(XenEvtchnState *s, evtchn_port_t port) +{ + void *vcpu_info, *shinfo; + + if (s->port_table[port].type == EVTCHNSTAT_closed) { + return -EINVAL; + } + + if (s->evtchn_in_kernel) { + XenEvtchnPort *p = &s->port_table[port]; + CPUState *cpu = qemu_get_cpu(p->vcpu); + struct kvm_irq_routing_xen_evtchn evt; + + if (!cpu) { + return 0; + } + + evt.port = port; + evt.vcpu = kvm_arch_vcpu_id(cpu); + evt.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL; + + return kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_EVTCHN_SEND, &evt); + } + + shinfo = xen_overlay_get_shinfo_ptr(); + if (!shinfo) { + return -ENOTSUP; + } + + vcpu_info = kvm_xen_get_vcpu_info_hva(s->port_table[port].vcpu); + if (!vcpu_info) { + return -EINVAL; + } + + if (xen_is_long_mode()) { + return do_set_port_lm(s, port, shinfo, vcpu_info); + } else { + return do_set_port_compat(s, port, shinfo, vcpu_info); + } +} + +static int clear_port_pending(XenEvtchnState *s, evtchn_port_t port) +{ + void *p = xen_overlay_get_shinfo_ptr(); + + if (!p) { + return -ENOTSUP; + } + + if (xen_is_long_mode()) { + struct shared_info *shinfo = p; + const int bits_per_word = BITS_PER_BYTE * sizeof(shinfo->evtchn_pending[0]); + typeof(shinfo->evtchn_pending[0]) mask; + int idx = port / bits_per_word; + int offset = port % bits_per_word; + + mask = 1UL << offset; + + qatomic_fetch_and(&shinfo->evtchn_pending[idx], ~mask); + } else { + struct compat_shared_info *shinfo = p; + const int bits_per_word = BITS_PER_BYTE * sizeof(shinfo->evtchn_pending[0]); + typeof(shinfo->evtchn_pending[0]) mask; + int idx = port / bits_per_word; + int offset = port % bits_per_word; + + mask = 1UL << offset; + + qatomic_fetch_and(&shinfo->evtchn_pending[idx], ~mask); + } + return 0; +} + +static void free_port(XenEvtchnState *s, evtchn_port_t port) +{ + s->port_table[port].type = EVTCHNSTAT_closed; + s->port_table[port].u.val = 0; + s->port_table[port].vcpu = 0; + + if (s->nr_ports == port + 1) { + do { + s->nr_ports--; + } while (s->nr_ports && + s->port_table[s->nr_ports - 1].type == EVTCHNSTAT_closed); + } + + /* Clear pending event to avoid unexpected behavior on re-bind. */ + clear_port_pending(s, port); +} + +static int allocate_port(XenEvtchnState *s, uint32_t vcpu, uint16_t type, + uint16_t val, evtchn_port_t *port) +{ + evtchn_port_t p = 1; + + for (p = 1; valid_port(p); p++) { + if (s->port_table[p].type == EVTCHNSTAT_closed) { + s->port_table[p].vcpu = vcpu; + s->port_table[p].type = type; + s->port_table[p].u.val = val; + + *port = p; + + if (s->nr_ports < p + 1) { + s->nr_ports = p + 1; + } + + return 0; + } + } + return -ENOSPC; +} + +static bool virq_is_global(uint32_t virq) +{ + switch (virq) { + case VIRQ_TIMER: + case VIRQ_DEBUG: + case VIRQ_XENOPROF: + case VIRQ_XENPMU: + return false; + + default: + return true; + } +} + +static int close_port(XenEvtchnState *s, evtchn_port_t port, + bool *flush_kvm_routes) +{ + XenEvtchnPort *p = &s->port_table[port]; + + /* Because it *might* be a PIRQ port */ + assert(bql_locked()); + + switch (p->type) { + case EVTCHNSTAT_closed: + return -ENOENT; + + case EVTCHNSTAT_pirq: + s->pirq[p->u.pirq].port = 0; + if (s->pirq[p->u.pirq].is_translated) { + *flush_kvm_routes = true; + } + break; + + case EVTCHNSTAT_virq: + kvm_xen_set_vcpu_virq(virq_is_global(p->u.virq) ? 0 : p->vcpu, + p->u.virq, 0); + break; + + case EVTCHNSTAT_ipi: + if (s->evtchn_in_kernel) { + deassign_kernel_port(port); + } + break; + + case EVTCHNSTAT_interdomain: + if (p->u.interdomain.to_qemu) { + uint16_t be_port = p->u.interdomain.port; + struct xenevtchn_handle *xc = s->be_handles[be_port]; + if (xc) { + if (kvm_xen_has_cap(EVTCHN_SEND)) { + deassign_kernel_port(port); + } + xc->guest_port = 0; + } + } else { + /* Loopback interdomain */ + XenEvtchnPort *rp = &s->port_table[p->u.interdomain.port]; + if (!valid_port(p->u.interdomain.port) || + rp->u.interdomain.port != port || + rp->type != EVTCHNSTAT_interdomain) { + error_report("Inconsistent state for interdomain unbind"); + } else { + /* Set the other end back to unbound */ + rp->type = EVTCHNSTAT_unbound; + rp->u.interdomain.port = 0; + } + } + break; + + default: + break; + } + + free_port(s, port); + return 0; +} + +int xen_evtchn_soft_reset(void) +{ + XenEvtchnState *s = xen_evtchn_singleton; + bool flush_kvm_routes = false; + int i; + + if (!s) { + return -ENOTSUP; + } + + assert(bql_locked()); + + qemu_mutex_lock(&s->port_lock); + + for (i = 0; i < s->nr_ports; i++) { + close_port(s, i, &flush_kvm_routes); + } + + qemu_mutex_unlock(&s->port_lock); + + if (flush_kvm_routes) { + kvm_update_msi_routes_all(NULL, true, 0, 0); + } + + return 0; +} + +int xen_evtchn_reset_op(struct evtchn_reset *reset) +{ + if (reset->dom != DOMID_SELF && reset->dom != xen_domid) { + return -ESRCH; + } + + BQL_LOCK_GUARD(); + return xen_evtchn_soft_reset(); +} + +int xen_evtchn_close_op(struct evtchn_close *close) +{ + XenEvtchnState *s = xen_evtchn_singleton; + bool flush_kvm_routes = false; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_port(close->port)) { + return -EINVAL; + } + + BQL_LOCK_GUARD(); + qemu_mutex_lock(&s->port_lock); + + ret = close_port(s, close->port, &flush_kvm_routes); + + qemu_mutex_unlock(&s->port_lock); + + if (flush_kvm_routes) { + kvm_update_msi_routes_all(NULL, true, 0, 0); + } + + return ret; +} + +int xen_evtchn_unmask_op(struct evtchn_unmask *unmask) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_port(unmask->port)) { + return -EINVAL; + } + + qemu_mutex_lock(&s->port_lock); + + ret = unmask_port(s, unmask->port, true); + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + +int xen_evtchn_bind_vcpu_op(struct evtchn_bind_vcpu *vcpu) +{ + XenEvtchnState *s = xen_evtchn_singleton; + XenEvtchnPort *p; + int ret = -EINVAL; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_port(vcpu->port)) { + return -EINVAL; + } + + if (!valid_vcpu(vcpu->vcpu)) { + return -ENOENT; + } + + qemu_mutex_lock(&s->port_lock); + + p = &s->port_table[vcpu->port]; + + if (p->type == EVTCHNSTAT_interdomain || + p->type == EVTCHNSTAT_unbound || + p->type == EVTCHNSTAT_pirq || + (p->type == EVTCHNSTAT_virq && virq_is_global(p->u.virq))) { + /* + * unmask_port() with do_unmask==false will just raise the event + * on the new vCPU if the port was already pending. + */ + p->vcpu = vcpu->vcpu; + unmask_port(s, vcpu->port, false); + ret = 0; + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + +int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (virq->virq >= NR_VIRQS) { + return -EINVAL; + } + + /* Global VIRQ must be allocated on vCPU0 first */ + if (virq_is_global(virq->virq) && virq->vcpu != 0) { + return -EINVAL; + } + + if (!valid_vcpu(virq->vcpu)) { + return -ENOENT; + } + + qemu_mutex_lock(&s->port_lock); + + ret = allocate_port(s, virq->vcpu, EVTCHNSTAT_virq, virq->virq, + &virq->port); + if (!ret) { + ret = kvm_xen_set_vcpu_virq(virq->vcpu, virq->virq, virq->port); + if (ret) { + free_port(s, virq->port); + } + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + +int xen_evtchn_bind_pirq_op(struct evtchn_bind_pirq *pirq) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (pirq->pirq >= s->nr_pirqs) { + return -EINVAL; + } + + BQL_LOCK_GUARD(); + + if (s->pirq[pirq->pirq].port) { + return -EBUSY; + } + + qemu_mutex_lock(&s->port_lock); + + ret = allocate_port(s, 0, EVTCHNSTAT_pirq, pirq->pirq, + &pirq->port); + if (ret) { + qemu_mutex_unlock(&s->port_lock); + return ret; + } + + s->pirq[pirq->pirq].port = pirq->port; + trace_kvm_xen_bind_pirq(pirq->pirq, pirq->port); + + qemu_mutex_unlock(&s->port_lock); + + /* + * Need to do the unmask outside port_lock because it may call + * back into the MSI translate function. + */ + if (s->pirq[pirq->pirq].gsi == IRQ_MSI_EMU) { + if (s->pirq[pirq->pirq].is_masked) { + PCIDevice *dev = s->pirq[pirq->pirq].dev; + int vector = s->pirq[pirq->pirq].vector; + char *dev_path = qdev_get_dev_path(DEVICE(dev)); + + trace_kvm_xen_unmask_pirq(pirq->pirq, dev_path, vector); + g_free(dev_path); + + if (s->pirq[pirq->pirq].is_msix) { + msix_set_mask(dev, vector, false); + } else { + msi_set_mask(dev, vector, false, NULL); + } + } else if (s->pirq[pirq->pirq].is_translated) { + /* + * If KVM had attempted to translate this one before, make it try + * again. If we unmasked, then the notifier on the MSI(-X) vector + * will already have had the same effect. + */ + kvm_update_msi_routes_all(NULL, true, 0, 0); + } + } + + return ret; +} + +int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_vcpu(ipi->vcpu)) { + return -ENOENT; + } + + qemu_mutex_lock(&s->port_lock); + + ret = allocate_port(s, ipi->vcpu, EVTCHNSTAT_ipi, 0, &ipi->port); + if (!ret && s->evtchn_in_kernel) { + assign_kernel_port(EVTCHNSTAT_ipi, ipi->port, ipi->vcpu); + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + +int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (interdomain->remote_dom != DOMID_QEMU && + interdomain->remote_dom != DOMID_SELF && + interdomain->remote_dom != xen_domid) { + return -ESRCH; + } + + if (!valid_port(interdomain->remote_port)) { + return -EINVAL; + } + + qemu_mutex_lock(&s->port_lock); + + /* The newly allocated port starts out as unbound */ + ret = allocate_port(s, 0, EVTCHNSTAT_unbound, 0, &interdomain->local_port); + + if (ret) { + goto out; + } + + if (interdomain->remote_dom == DOMID_QEMU) { + struct xenevtchn_handle *xc = s->be_handles[interdomain->remote_port]; + XenEvtchnPort *lp = &s->port_table[interdomain->local_port]; + + if (!xc) { + ret = -ENOENT; + goto out_free_port; + } + + if (xc->guest_port) { + ret = -EBUSY; + goto out_free_port; + } + + assert(xc->be_port == interdomain->remote_port); + xc->guest_port = interdomain->local_port; + if (kvm_xen_has_cap(EVTCHN_SEND)) { + assign_kernel_eventfd(lp->type, xc->guest_port, xc->fd); + } + lp->type = EVTCHNSTAT_interdomain; + lp->u.interdomain.to_qemu = 1; + lp->u.interdomain.port = interdomain->remote_port; + ret = 0; + } else { + /* Loopback */ + XenEvtchnPort *rp = &s->port_table[interdomain->remote_port]; + XenEvtchnPort *lp = &s->port_table[interdomain->local_port]; + + /* + * The 'remote' port for loopback must be an unbound port allocated + * for communication with the local domain, and must *not* be the + * port that was just allocated for the local end. + */ + if (interdomain->local_port != interdomain->remote_port && + rp->type == EVTCHNSTAT_unbound && !rp->u.interdomain.to_qemu) { + + rp->type = EVTCHNSTAT_interdomain; + rp->u.interdomain.port = interdomain->local_port; + + lp->type = EVTCHNSTAT_interdomain; + lp->u.interdomain.port = interdomain->remote_port; + } else { + ret = -EINVAL; + } + } + + out_free_port: + if (ret) { + free_port(s, interdomain->local_port); + } + out: + qemu_mutex_unlock(&s->port_lock); + + return ret; + +} +int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (alloc->dom != DOMID_SELF && alloc->dom != xen_domid) { + return -ESRCH; + } + + if (alloc->remote_dom != DOMID_QEMU && + alloc->remote_dom != DOMID_SELF && + alloc->remote_dom != xen_domid) { + return -EPERM; + } + + qemu_mutex_lock(&s->port_lock); + + ret = allocate_port(s, 0, EVTCHNSTAT_unbound, 0, &alloc->port); + + if (!ret && alloc->remote_dom == DOMID_QEMU) { + XenEvtchnPort *p = &s->port_table[alloc->port]; + p->u.interdomain.to_qemu = 1; + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + +int xen_evtchn_send_op(struct evtchn_send *send) +{ + XenEvtchnState *s = xen_evtchn_singleton; + XenEvtchnPort *p; + int ret = 0; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_port(send->port)) { + return -EINVAL; + } + + qemu_mutex_lock(&s->port_lock); + + p = &s->port_table[send->port]; + + switch (p->type) { + case EVTCHNSTAT_interdomain: + if (p->u.interdomain.to_qemu) { + /* + * This is an event from the guest to qemu itself, which is + * serving as the driver domain. + */ + uint16_t be_port = p->u.interdomain.port; + struct xenevtchn_handle *xc = s->be_handles[be_port]; + if (xc) { + eventfd_write(xc->fd, 1); + ret = 0; + } else { + ret = -ENOENT; + } + } else { + /* Loopback interdomain ports; just a complex IPI */ + set_port_pending(s, p->u.interdomain.port); + } + break; + + case EVTCHNSTAT_ipi: + set_port_pending(s, send->port); + break; + + case EVTCHNSTAT_unbound: + /* Xen will silently drop these */ + break; + + default: + ret = -EINVAL; + break; + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + +int xen_evtchn_set_port(uint16_t port) +{ + XenEvtchnState *s = xen_evtchn_singleton; + XenEvtchnPort *p; + int ret = -EINVAL; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_port(port)) { + return -EINVAL; + } + + qemu_mutex_lock(&s->port_lock); + + p = &s->port_table[port]; + + /* QEMU has no business sending to anything but these */ + if (p->type == EVTCHNSTAT_virq || + (p->type == EVTCHNSTAT_interdomain && p->u.interdomain.to_qemu)) { + set_port_pending(s, port); + ret = 0; + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + +static int allocate_pirq(XenEvtchnState *s, int type, int gsi) +{ + uint16_t pirq; + + /* + * Preserve the allocation strategy that Xen has. It looks like + * we *never* give out PIRQ 0-15, we give out 16-nr_irqs_gsi only + * to GSIs (counting up from 16), and then we count backwards from + * the top for MSIs or when the GSI space is exhausted. + */ + if (type == MAP_PIRQ_TYPE_GSI) { + for (pirq = 16 ; pirq < IOAPIC_NUM_PINS; pirq++) { + if (pirq_inuse(s, pirq)) { + continue; + } + + /* Found it */ + goto found; + } + } + for (pirq = s->nr_pirqs - 1; pirq >= IOAPIC_NUM_PINS; pirq--) { + /* Skip whole words at a time when they're full */ + if (pirq_inuse_word(s, pirq) == UINT64_MAX) { + pirq &= ~63ULL; + continue; + } + if (pirq_inuse(s, pirq)) { + continue; + } + + goto found; + } + return -ENOSPC; + + found: + pirq_inuse_word(s, pirq) |= pirq_inuse_bit(pirq); + if (gsi >= 0) { + assert(gsi < IOAPIC_NUM_PINS); + s->gsi_pirq[gsi] = pirq; + } + s->pirq[pirq].gsi = gsi; + return pirq; +} + +bool xen_evtchn_set_gsi(int gsi, int level) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int pirq; + + assert(bql_locked()); + + if (!s || gsi < 0 || gsi >= IOAPIC_NUM_PINS) { + return false; + } + + /* + * Check that that it *isn't* the event channel GSI, and thus + * that we are not recursing and it's safe to take s->port_lock. + * + * Locking aside, it's perfectly sane to bail out early for that + * special case, as it would make no sense for the event channel + * GSI to be routed back to event channels, when the delivery + * method is to raise the GSI... that recursion wouldn't *just* + * be a locking issue. + */ + if (gsi && gsi == s->callback_gsi) { + return false; + } + + QEMU_LOCK_GUARD(&s->port_lock); + + pirq = s->gsi_pirq[gsi]; + if (!pirq) { + return false; + } + + if (level) { + int port = s->pirq[pirq].port; + + s->pirq_gsi_set |= (1U << gsi); + if (port) { + set_port_pending(s, port); + } + } else { + s->pirq_gsi_set &= ~(1U << gsi); + } + return true; +} + +static uint32_t msi_pirq_target(uint64_t addr, uint32_t data) +{ + /* The vector (in low 8 bits of data) must be zero */ + if (data & 0xff) { + return 0; + } + + uint32_t pirq = (addr & 0xff000) >> 12; + pirq |= (addr >> 32) & 0xffffff00; + + return pirq; +} + +static void do_remove_pci_vector(XenEvtchnState *s, PCIDevice *dev, int vector, + int except_pirq) +{ + uint32_t pirq; + + for (pirq = 0; pirq < s->nr_pirqs; pirq++) { + /* + * We could be cleverer here, but it isn't really a fast path, and + * this trivial optimisation is enough to let us skip the big gap + * in the middle a bit quicker (in terms of both loop iterations, + * and cache lines). + */ + if (!(pirq & 63) && !(pirq_inuse_word(s, pirq))) { + pirq += 64; + continue; + } + if (except_pirq && pirq == except_pirq) { + continue; + } + if (s->pirq[pirq].dev != dev) { + continue; + } + if (vector != -1 && s->pirq[pirq].vector != vector) { + continue; + } + + /* It could theoretically be bound to a port already, but that is OK. */ + s->pirq[pirq].dev = dev; + s->pirq[pirq].gsi = IRQ_UNBOUND; + s->pirq[pirq].is_msix = false; + s->pirq[pirq].vector = 0; + s->pirq[pirq].is_masked = false; + s->pirq[pirq].is_translated = false; + } +} + +void xen_evtchn_remove_pci_device(PCIDevice *dev) +{ + XenEvtchnState *s = xen_evtchn_singleton; + + if (!s) { + return; + } + + QEMU_LOCK_GUARD(&s->port_lock); + do_remove_pci_vector(s, dev, -1, 0); +} + +void xen_evtchn_snoop_msi(PCIDevice *dev, bool is_msix, unsigned int vector, + uint64_t addr, uint32_t data, bool is_masked) +{ + XenEvtchnState *s = xen_evtchn_singleton; + uint32_t pirq; + + if (!s) { + return; + } + + assert(bql_locked()); + + pirq = msi_pirq_target(addr, data); + + /* + * The PIRQ# must be sane, and there must be an allocated PIRQ in + * IRQ_UNBOUND or IRQ_MSI_EMU state to match it. + */ + if (!pirq || pirq >= s->nr_pirqs || !pirq_inuse(s, pirq) || + (s->pirq[pirq].gsi != IRQ_UNBOUND && + s->pirq[pirq].gsi != IRQ_MSI_EMU)) { + pirq = 0; + } + + if (pirq) { + s->pirq[pirq].dev = dev; + s->pirq[pirq].gsi = IRQ_MSI_EMU; + s->pirq[pirq].is_msix = is_msix; + s->pirq[pirq].vector = vector; + s->pirq[pirq].is_masked = is_masked; + } + + /* Remove any (other) entries for this {device, vector} */ + do_remove_pci_vector(s, dev, vector, pirq); +} + +int xen_evtchn_translate_pirq_msi(struct kvm_irq_routing_entry *route, + uint64_t address, uint32_t data) +{ + XenEvtchnState *s = xen_evtchn_singleton; + uint32_t pirq, port; + CPUState *cpu; + + if (!s) { + return 1; /* Not a PIRQ */ + } + + assert(bql_locked()); + + pirq = msi_pirq_target(address, data); + if (!pirq || pirq >= s->nr_pirqs) { + return 1; /* Not a PIRQ */ + } + + if (!kvm_xen_has_cap(EVTCHN_2LEVEL)) { + return -ENOTSUP; + } + + if (s->pirq[pirq].gsi != IRQ_MSI_EMU) { + return -EINVAL; + } + + /* Remember that KVM tried to translate this. It might need to try again. */ + s->pirq[pirq].is_translated = true; + + QEMU_LOCK_GUARD(&s->port_lock); + + port = s->pirq[pirq].port; + if (!valid_port(port)) { + return -EINVAL; + } + + cpu = qemu_get_cpu(s->port_table[port].vcpu); + if (!cpu) { + return -EINVAL; + } + + route->type = KVM_IRQ_ROUTING_XEN_EVTCHN; + route->u.xen_evtchn.port = port; + route->u.xen_evtchn.vcpu = kvm_arch_vcpu_id(cpu); + route->u.xen_evtchn.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL; + + return 0; /* Handled */ +} + +bool xen_evtchn_deliver_pirq_msi(uint64_t address, uint32_t data) +{ + XenEvtchnState *s = xen_evtchn_singleton; + uint32_t pirq, port; + + if (!s) { + return false; + } + + assert(bql_locked()); + + pirq = msi_pirq_target(address, data); + if (!pirq || pirq >= s->nr_pirqs) { + return false; + } + + QEMU_LOCK_GUARD(&s->port_lock); + + port = s->pirq[pirq].port; + if (!valid_port(port)) { + return false; + } + + set_port_pending(s, port); + return true; +} + +int xen_physdev_map_pirq(struct physdev_map_pirq *map) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int pirq = map->pirq; + int gsi = map->index; + + if (!s) { + return -ENOTSUP; + } + + BQL_LOCK_GUARD(); + QEMU_LOCK_GUARD(&s->port_lock); + + if (map->domid != DOMID_SELF && map->domid != xen_domid) { + return -EPERM; + } + if (map->type != MAP_PIRQ_TYPE_GSI) { + return -EINVAL; + } + if (gsi < 0 || gsi >= IOAPIC_NUM_PINS) { + return -EINVAL; + } + + if (pirq < 0) { + pirq = allocate_pirq(s, map->type, gsi); + if (pirq < 0) { + return pirq; + } + map->pirq = pirq; + } else if (pirq > s->nr_pirqs) { + return -EINVAL; + } else { + /* + * User specified a valid-looking PIRQ#. Allow it if it is + * allocated and not yet bound, or if it is unallocated + */ + if (pirq_inuse(s, pirq)) { + if (s->pirq[pirq].gsi != IRQ_UNBOUND) { + return -EBUSY; + } + } else { + /* If it was unused, mark it used now. */ + pirq_inuse_word(s, pirq) |= pirq_inuse_bit(pirq); + } + /* Set the mapping in both directions. */ + s->pirq[pirq].gsi = gsi; + s->gsi_pirq[gsi] = pirq; + } + + trace_kvm_xen_map_pirq(pirq, gsi); + return 0; +} + +int xen_physdev_unmap_pirq(struct physdev_unmap_pirq *unmap) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int pirq = unmap->pirq; + int gsi; + + if (!s) { + return -ENOTSUP; + } + + if (unmap->domid != DOMID_SELF && unmap->domid != xen_domid) { + return -EPERM; + } + if (pirq < 0 || pirq >= s->nr_pirqs) { + return -EINVAL; + } + + BQL_LOCK_GUARD(); + qemu_mutex_lock(&s->port_lock); + + if (!pirq_inuse(s, pirq)) { + qemu_mutex_unlock(&s->port_lock); + return -ENOENT; + } + + gsi = s->pirq[pirq].gsi; + + /* We can only unmap GSI PIRQs */ + if (gsi < 0) { + qemu_mutex_unlock(&s->port_lock); + return -EINVAL; + } + + s->gsi_pirq[gsi] = 0; + s->pirq[pirq].gsi = IRQ_UNBOUND; /* Doesn't actually matter because: */ + pirq_inuse_word(s, pirq) &= ~pirq_inuse_bit(pirq); + + trace_kvm_xen_unmap_pirq(pirq, gsi); + qemu_mutex_unlock(&s->port_lock); + + if (gsi == IRQ_MSI_EMU) { + kvm_update_msi_routes_all(NULL, true, 0, 0); + } + + return 0; +} + +int xen_physdev_eoi_pirq(struct physdev_eoi *eoi) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int pirq = eoi->irq; + int gsi; + + if (!s) { + return -ENOTSUP; + } + + BQL_LOCK_GUARD(); + QEMU_LOCK_GUARD(&s->port_lock); + + if (!pirq_inuse(s, pirq)) { + return -ENOENT; + } + + gsi = s->pirq[pirq].gsi; + if (gsi < 0) { + return -EINVAL; + } + + /* Reassert a level IRQ if needed */ + if (s->pirq_gsi_set & (1U << gsi)) { + int port = s->pirq[pirq].port; + if (port) { + set_port_pending(s, port); + } + } + + return 0; +} + +int xen_physdev_query_pirq(struct physdev_irq_status_query *query) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int pirq = query->irq; + + if (!s) { + return -ENOTSUP; + } + + BQL_LOCK_GUARD(); + QEMU_LOCK_GUARD(&s->port_lock); + + if (!pirq_inuse(s, pirq)) { + return -ENOENT; + } + + if (s->pirq[pirq].gsi >= 0) { + query->flags = XENIRQSTAT_needs_eoi; + } else { + query->flags = 0; + } + + return 0; +} + +int xen_physdev_get_free_pirq(struct physdev_get_free_pirq *get) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int pirq; + + if (!s) { + return -ENOTSUP; + } + + QEMU_LOCK_GUARD(&s->port_lock); + + pirq = allocate_pirq(s, get->type, IRQ_UNBOUND); + if (pirq < 0) { + return pirq; + } + + get->pirq = pirq; + trace_kvm_xen_get_free_pirq(pirq, get->type); + return 0; +} + +struct xenevtchn_handle *xen_be_evtchn_open(void) +{ + struct xenevtchn_handle *xc = g_new0(struct xenevtchn_handle, 1); + + xc->fd = eventfd(0, EFD_CLOEXEC); + if (xc->fd < 0) { + free(xc); + return NULL; + } + + return xc; +} + +static int find_be_port(XenEvtchnState *s, struct xenevtchn_handle *xc) +{ + int i; + + for (i = 1; i < EVTCHN_2L_NR_CHANNELS; i++) { + if (!s->be_handles[i]) { + s->be_handles[i] = xc; + xc->be_port = i; + return i; + } + } + return 0; +} + +int xen_be_evtchn_bind_interdomain(struct xenevtchn_handle *xc, uint32_t domid, + evtchn_port_t guest_port) +{ + XenEvtchnState *s = xen_evtchn_singleton; + XenEvtchnPort *gp; + uint16_t be_port = 0; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (!xc) { + return -EFAULT; + } + + if (domid != xen_domid) { + return -ESRCH; + } + + if (!valid_port(guest_port)) { + return -EINVAL; + } + + qemu_mutex_lock(&s->port_lock); + + /* The guest has to have an unbound port waiting for us to bind */ + gp = &s->port_table[guest_port]; + + switch (gp->type) { + case EVTCHNSTAT_interdomain: + /* Allow rebinding after migration, preserve port # if possible */ + be_port = gp->u.interdomain.port; + assert(be_port != 0); + if (!s->be_handles[be_port]) { + s->be_handles[be_port] = xc; + xc->guest_port = guest_port; + ret = xc->be_port = be_port; + if (kvm_xen_has_cap(EVTCHN_SEND)) { + assign_kernel_eventfd(gp->type, guest_port, xc->fd); + } + break; + } + /* fall through */ + + case EVTCHNSTAT_unbound: + be_port = find_be_port(s, xc); + if (!be_port) { + ret = -ENOSPC; + goto out; + } + + gp->type = EVTCHNSTAT_interdomain; + gp->u.interdomain.to_qemu = 1; + gp->u.interdomain.port = be_port; + xc->guest_port = guest_port; + if (kvm_xen_has_cap(EVTCHN_SEND)) { + assign_kernel_eventfd(gp->type, guest_port, xc->fd); + } + ret = be_port; + break; + + default: + ret = -EINVAL; + break; + } + + out: + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + +int xen_be_evtchn_unbind(struct xenevtchn_handle *xc, evtchn_port_t port) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (!xc) { + return -EFAULT; + } + + qemu_mutex_lock(&s->port_lock); + + if (port && port != xc->be_port) { + ret = -EINVAL; + goto out; + } + + if (xc->guest_port) { + XenEvtchnPort *gp = &s->port_table[xc->guest_port]; + + /* This should never *not* be true */ + if (gp->type == EVTCHNSTAT_interdomain) { + gp->type = EVTCHNSTAT_unbound; + gp->u.interdomain.port = 0; + } + + if (kvm_xen_has_cap(EVTCHN_SEND)) { + deassign_kernel_port(xc->guest_port); + } + xc->guest_port = 0; + } + + s->be_handles[xc->be_port] = NULL; + xc->be_port = 0; + ret = 0; + out: + qemu_mutex_unlock(&s->port_lock); + return ret; +} + +int xen_be_evtchn_close(struct xenevtchn_handle *xc) +{ + if (!xc) { + return -EFAULT; + } + + xen_be_evtchn_unbind(xc, 0); + + close(xc->fd); + free(xc); + return 0; +} + +int xen_be_evtchn_fd(struct xenevtchn_handle *xc) +{ + if (!xc) { + return -1; + } + return xc->fd; +} + +int xen_be_evtchn_notify(struct xenevtchn_handle *xc, evtchn_port_t port) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (!xc) { + return -EFAULT; + } + + qemu_mutex_lock(&s->port_lock); + + if (xc->guest_port) { + set_port_pending(s, xc->guest_port); + ret = 0; + } else { + ret = -ENOTCONN; + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} + +int xen_be_evtchn_pending(struct xenevtchn_handle *xc) +{ + uint64_t val; + + if (!xc) { + return -EFAULT; + } + + if (!xc->be_port) { + return 0; + } + + if (eventfd_read(xc->fd, &val)) { + return -errno; + } + + return val ? xc->be_port : 0; +} + +int xen_be_evtchn_unmask(struct xenevtchn_handle *xc, evtchn_port_t port) +{ + if (!xc) { + return -EFAULT; + } + + if (xc->be_port != port) { + return -EINVAL; + } + + /* + * We don't actually do anything to unmask it; the event was already + * consumed in xen_be_evtchn_pending(). + */ + return 0; +} + +int xen_be_evtchn_get_guest_port(struct xenevtchn_handle *xc) +{ + return xc->guest_port; +} + +EvtchnInfoList *qmp_xen_event_list(Error **errp) +{ + XenEvtchnState *s = xen_evtchn_singleton; + EvtchnInfoList *head = NULL, **tail = &head; + void *shinfo, *pending, *mask; + int i; + + if (!s) { + error_setg(errp, "Xen event channel emulation not enabled"); + return NULL; + } + + shinfo = xen_overlay_get_shinfo_ptr(); + if (!shinfo) { + error_setg(errp, "Xen shared info page not allocated"); + return NULL; + } + + if (xen_is_long_mode()) { + pending = shinfo + offsetof(struct shared_info, evtchn_pending); + mask = shinfo + offsetof(struct shared_info, evtchn_mask); + } else { + pending = shinfo + offsetof(struct compat_shared_info, evtchn_pending); + mask = shinfo + offsetof(struct compat_shared_info, evtchn_mask); + } + + QEMU_LOCK_GUARD(&s->port_lock); + + for (i = 0; i < s->nr_ports; i++) { + XenEvtchnPort *p = &s->port_table[i]; + EvtchnInfo *info; + + if (p->type == EVTCHNSTAT_closed) { + continue; + } + + info = g_new0(EvtchnInfo, 1); + + info->port = i; + qemu_build_assert(EVTCHN_PORT_TYPE_CLOSED == EVTCHNSTAT_closed); + qemu_build_assert(EVTCHN_PORT_TYPE_UNBOUND == EVTCHNSTAT_unbound); + qemu_build_assert(EVTCHN_PORT_TYPE_INTERDOMAIN == EVTCHNSTAT_interdomain); + qemu_build_assert(EVTCHN_PORT_TYPE_PIRQ == EVTCHNSTAT_pirq); + qemu_build_assert(EVTCHN_PORT_TYPE_VIRQ == EVTCHNSTAT_virq); + qemu_build_assert(EVTCHN_PORT_TYPE_IPI == EVTCHNSTAT_ipi); + + info->type = p->type; + if (p->type == EVTCHNSTAT_interdomain) { + info->remote_domain = g_strdup(p->u.interdomain.to_qemu ? + "qemu" : "loopback"); + info->target = p->u.interdomain.port; + } else { + info->target = p->u.val; /* pirq# or virq# */ + } + info->vcpu = p->vcpu; + info->pending = test_bit(i, pending); + info->masked = test_bit(i, mask); + + QAPI_LIST_APPEND(tail, info); + } + + return head; +} + +void qmp_xen_event_inject(uint32_t port, Error **errp) +{ + XenEvtchnState *s = xen_evtchn_singleton; + + if (!s) { + error_setg(errp, "Xen event channel emulation not enabled"); + return; + } + + if (!valid_port(port)) { + error_setg(errp, "Invalid port %u", port); + } + + QEMU_LOCK_GUARD(&s->port_lock); + + if (set_port_pending(s, port)) { + error_setg(errp, "Failed to set port %u", port); + return; + } +} + +void hmp_xen_event_list(Monitor *mon, const QDict *qdict) +{ + EvtchnInfoList *iter, *info_list; + Error *err = NULL; + + info_list = qmp_xen_event_list(&err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + for (iter = info_list; iter; iter = iter->next) { + EvtchnInfo *info = iter->value; + + monitor_printf(mon, "port %4u: vcpu: %d %s", info->port, info->vcpu, + EvtchnPortType_str(info->type)); + if (info->type != EVTCHN_PORT_TYPE_IPI) { + monitor_printf(mon, "("); + if (info->remote_domain) { + monitor_printf(mon, "%s:", info->remote_domain); + } + monitor_printf(mon, "%d)", info->target); + } + if (info->pending) { + monitor_printf(mon, " PENDING"); + } + if (info->masked) { + monitor_printf(mon, " MASKED"); + } + monitor_printf(mon, "\n"); + } + + qapi_free_EvtchnInfoList(info_list); +} + +void hmp_xen_event_inject(Monitor *mon, const QDict *qdict) +{ + int port = qdict_get_int(qdict, "port"); + Error *err = NULL; + + qmp_xen_event_inject(port, &err); + if (err) { + hmp_handle_error(mon, err); + } else { + monitor_printf(mon, "Delivered port %d\n", port); + } +} + diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h new file mode 100644 index 0000000000..b740acfc0d --- /dev/null +++ b/hw/i386/kvm/xen_evtchn.h @@ -0,0 +1,87 @@ +/* + * QEMU Xen emulation: Event channel support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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_XEN_EVTCHN_H +#define QEMU_XEN_EVTCHN_H + +#include "hw/sysbus.h" + +typedef uint32_t evtchn_port_t; + +void xen_evtchn_create(unsigned int nr_gsis, qemu_irq *system_gsis); +int xen_evtchn_soft_reset(void); +int xen_evtchn_set_callback_param(uint64_t param); +void xen_evtchn_set_callback_level(int level); + +int xen_evtchn_set_port(uint16_t port); + +bool xen_evtchn_set_gsi(int gsi, int level); +void xen_evtchn_snoop_msi(PCIDevice *dev, bool is_msix, unsigned int vector, + uint64_t addr, uint32_t data, bool is_masked); +void xen_evtchn_remove_pci_device(PCIDevice *dev); +struct kvm_irq_routing_entry; +int xen_evtchn_translate_pirq_msi(struct kvm_irq_routing_entry *route, + uint64_t address, uint32_t data); +bool xen_evtchn_deliver_pirq_msi(uint64_t address, uint32_t data); + + +/* + * These functions mirror the libxenevtchn library API, providing the QEMU + * backend side of "interdomain" event channels. + */ +struct xenevtchn_handle; +struct xenevtchn_handle *xen_be_evtchn_open(void); +int xen_be_evtchn_bind_interdomain(struct xenevtchn_handle *xc, uint32_t domid, + evtchn_port_t guest_port); +int xen_be_evtchn_unbind(struct xenevtchn_handle *xc, evtchn_port_t port); +int xen_be_evtchn_close(struct xenevtchn_handle *xc); +int xen_be_evtchn_fd(struct xenevtchn_handle *xc); +int xen_be_evtchn_notify(struct xenevtchn_handle *xc, evtchn_port_t port); +int xen_be_evtchn_unmask(struct xenevtchn_handle *xc, evtchn_port_t port); +int xen_be_evtchn_pending(struct xenevtchn_handle *xc); +/* Apart from this which is a local addition */ +int xen_be_evtchn_get_guest_port(struct xenevtchn_handle *xc); + +struct evtchn_status; +struct evtchn_close; +struct evtchn_unmask; +struct evtchn_bind_virq; +struct evtchn_bind_pirq; +struct evtchn_bind_ipi; +struct evtchn_send; +struct evtchn_alloc_unbound; +struct evtchn_bind_interdomain; +struct evtchn_bind_vcpu; +struct evtchn_reset; +int xen_evtchn_status_op(struct evtchn_status *status); +int xen_evtchn_close_op(struct evtchn_close *close); +int xen_evtchn_unmask_op(struct evtchn_unmask *unmask); +int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq); +int xen_evtchn_bind_pirq_op(struct evtchn_bind_pirq *pirq); +int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi); +int xen_evtchn_send_op(struct evtchn_send *send); +int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc); +int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain); +int xen_evtchn_bind_vcpu_op(struct evtchn_bind_vcpu *vcpu); +int xen_evtchn_reset_op(struct evtchn_reset *reset); + +struct physdev_map_pirq; +struct physdev_unmap_pirq; +struct physdev_eoi; +struct physdev_irq_status_query; +struct physdev_get_free_pirq; +int xen_physdev_map_pirq(struct physdev_map_pirq *map); +int xen_physdev_unmap_pirq(struct physdev_unmap_pirq *unmap); +int xen_physdev_eoi_pirq(struct physdev_eoi *eoi); +int xen_physdev_query_pirq(struct physdev_irq_status_query *query); +int xen_physdev_get_free_pirq(struct physdev_get_free_pirq *get); + +#endif /* QEMU_XEN_EVTCHN_H */ diff --git a/hw/i386/kvm/xen_gnttab.c b/hw/i386/kvm/xen_gnttab.c new file mode 100644 index 0000000000..245e4b15db --- /dev/null +++ b/hw/i386/kvm/xen_gnttab.c @@ -0,0 +1,550 @@ +/* + * QEMU Xen emulation: Grant table support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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/host-utils.h" +#include "qemu/module.h" +#include "qemu/lockable.h" +#include "qemu/main-loop.h" +#include "qapi/error.h" +#include "qom/object.h" +#include "exec/target_page.h" +#include "exec/address-spaces.h" +#include "migration/vmstate.h" + +#include "hw/sysbus.h" +#include "hw/xen/xen.h" +#include "hw/xen/xen_backend_ops.h" +#include "xen_overlay.h" +#include "xen_gnttab.h" +#include "xen_primary_console.h" + +#include "sysemu/kvm.h" +#include "sysemu/kvm_xen.h" + +#include "hw/xen/interface/memory.h" +#include "hw/xen/interface/grant_table.h" + +#define TYPE_XEN_GNTTAB "xen-gnttab" +OBJECT_DECLARE_SIMPLE_TYPE(XenGnttabState, XEN_GNTTAB) + +#define ENTRIES_PER_FRAME_V1 (XEN_PAGE_SIZE / sizeof(grant_entry_v1_t)) + +static struct gnttab_backend_ops emu_gnttab_backend_ops; + +struct XenGnttabState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + QemuMutex gnt_lock; + + uint32_t nr_frames; + uint32_t max_frames; + + union { + grant_entry_v1_t *v1; + /* Theoretically, v2 support could be added here. */ + } entries; + + MemoryRegion gnt_frames; + MemoryRegion *gnt_aliases; + uint64_t *gnt_frame_gpas; + + uint8_t *map_track; +}; + +struct XenGnttabState *xen_gnttab_singleton; + +static void xen_gnttab_realize(DeviceState *dev, Error **errp) +{ + XenGnttabState *s = XEN_GNTTAB(dev); + int i; + + if (xen_mode != XEN_EMULATE) { + error_setg(errp, "Xen grant table support is for Xen emulation"); + return; + } + s->max_frames = kvm_xen_get_gnttab_max_frames(); + memory_region_init_ram(&s->gnt_frames, OBJECT(dev), "xen:grant_table", + XEN_PAGE_SIZE * s->max_frames, &error_abort); + memory_region_set_enabled(&s->gnt_frames, true); + s->entries.v1 = memory_region_get_ram_ptr(&s->gnt_frames); + + /* Create individual page-sizes aliases for overlays */ + s->gnt_aliases = (void *)g_new0(MemoryRegion, s->max_frames); + s->gnt_frame_gpas = (void *)g_new(uint64_t, s->max_frames); + for (i = 0; i < s->max_frames; i++) { + memory_region_init_alias(&s->gnt_aliases[i], OBJECT(dev), + NULL, &s->gnt_frames, + i * XEN_PAGE_SIZE, XEN_PAGE_SIZE); + s->gnt_frame_gpas[i] = INVALID_GPA; + } + + s->nr_frames = 0; + memset(s->entries.v1, 0, XEN_PAGE_SIZE * s->max_frames); + s->entries.v1[GNTTAB_RESERVED_XENSTORE].flags = GTF_permit_access; + s->entries.v1[GNTTAB_RESERVED_XENSTORE].frame = XEN_SPECIAL_PFN(XENSTORE); + + qemu_mutex_init(&s->gnt_lock); + + xen_gnttab_singleton = s; + + s->map_track = g_new0(uint8_t, s->max_frames * ENTRIES_PER_FRAME_V1); + + xen_gnttab_ops = &emu_gnttab_backend_ops; +} + +static int xen_gnttab_post_load(void *opaque, int version_id) +{ + XenGnttabState *s = XEN_GNTTAB(opaque); + uint32_t i; + + for (i = 0; i < s->nr_frames; i++) { + if (s->gnt_frame_gpas[i] != INVALID_GPA) { + xen_overlay_do_map_page(&s->gnt_aliases[i], s->gnt_frame_gpas[i]); + } + } + return 0; +} + +static bool xen_gnttab_is_needed(void *opaque) +{ + return xen_mode == XEN_EMULATE; +} + +static const VMStateDescription xen_gnttab_vmstate = { + .name = "xen_gnttab", + .version_id = 1, + .minimum_version_id = 1, + .needed = xen_gnttab_is_needed, + .post_load = xen_gnttab_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(nr_frames, XenGnttabState), + VMSTATE_VARRAY_UINT32(gnt_frame_gpas, XenGnttabState, nr_frames, 0, + vmstate_info_uint64, uint64_t), + VMSTATE_END_OF_LIST() + } +}; + +static void xen_gnttab_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = xen_gnttab_realize; + dc->vmsd = &xen_gnttab_vmstate; +} + +static const TypeInfo xen_gnttab_info = { + .name = TYPE_XEN_GNTTAB, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XenGnttabState), + .class_init = xen_gnttab_class_init, +}; + +void xen_gnttab_create(void) +{ + xen_gnttab_singleton = XEN_GNTTAB(sysbus_create_simple(TYPE_XEN_GNTTAB, + -1, NULL)); +} + +static void xen_gnttab_register_types(void) +{ + type_register_static(&xen_gnttab_info); +} + +type_init(xen_gnttab_register_types) + +int xen_gnttab_map_page(uint64_t idx, uint64_t gfn) +{ + XenGnttabState *s = xen_gnttab_singleton; + uint64_t gpa = gfn << XEN_PAGE_SHIFT; + + if (!s) { + return -ENOTSUP; + } + + if (idx >= s->max_frames) { + return -EINVAL; + } + + BQL_LOCK_GUARD(); + QEMU_LOCK_GUARD(&s->gnt_lock); + + xen_overlay_do_map_page(&s->gnt_aliases[idx], gpa); + + s->gnt_frame_gpas[idx] = gpa; + + if (s->nr_frames <= idx) { + s->nr_frames = idx + 1; + } + + return 0; +} + +int xen_gnttab_set_version_op(struct gnttab_set_version *set) +{ + int ret; + + switch (set->version) { + case 1: + ret = 0; + break; + + case 2: + /* Behave as before set_version was introduced. */ + ret = -ENOSYS; + break; + + default: + ret = -EINVAL; + } + + set->version = 1; + return ret; +} + +int xen_gnttab_get_version_op(struct gnttab_get_version *get) +{ + if (get->dom != DOMID_SELF && get->dom != xen_domid) { + return -ESRCH; + } + + get->version = 1; + return 0; +} + +int xen_gnttab_query_size_op(struct gnttab_query_size *size) +{ + XenGnttabState *s = xen_gnttab_singleton; + + if (!s) { + return -ENOTSUP; + } + + if (size->dom != DOMID_SELF && size->dom != xen_domid) { + size->status = GNTST_bad_domain; + return 0; + } + + size->status = GNTST_okay; + size->nr_frames = s->nr_frames; + size->max_nr_frames = s->max_frames; + return 0; +} + +/* Track per-open refs, to allow close() to clean up. */ +struct active_ref { + MemoryRegionSection mrs; + void *virtaddr; + uint32_t refcnt; + int prot; +}; + +static void gnt_unref(XenGnttabState *s, grant_ref_t ref, + MemoryRegionSection *mrs, int prot) +{ + if (mrs && mrs->mr) { + if (prot & PROT_WRITE) { + memory_region_set_dirty(mrs->mr, mrs->offset_within_region, + XEN_PAGE_SIZE); + } + memory_region_unref(mrs->mr); + mrs->mr = NULL; + } + assert(s->map_track[ref] != 0); + + if (--s->map_track[ref] == 0) { + grant_entry_v1_t *gnt_p = &s->entries.v1[ref]; + qatomic_and(&gnt_p->flags, (uint16_t)~(GTF_reading | GTF_writing)); + } +} + +static uint64_t gnt_ref(XenGnttabState *s, grant_ref_t ref, int prot) +{ + uint16_t mask = GTF_type_mask | GTF_sub_page; + grant_entry_v1_t gnt, *gnt_p; + int retries = 0; + + if (ref >= s->max_frames * ENTRIES_PER_FRAME_V1 || + s->map_track[ref] == UINT8_MAX) { + return INVALID_GPA; + } + + if (prot & PROT_WRITE) { + mask |= GTF_readonly; + } + + gnt_p = &s->entries.v1[ref]; + + /* + * The guest can legitimately be changing the GTF_readonly flag. Allow + * that, but don't let a malicious guest cause a livelock. + */ + for (retries = 0; retries < 5; retries++) { + uint16_t new_flags; + + /* Read the entry before an atomic operation on its flags */ + gnt = *(volatile grant_entry_v1_t *)gnt_p; + + if ((gnt.flags & mask) != GTF_permit_access || + gnt.domid != DOMID_QEMU) { + return INVALID_GPA; + } + + new_flags = gnt.flags | GTF_reading; + if (prot & PROT_WRITE) { + new_flags |= GTF_writing; + } + + if (qatomic_cmpxchg(&gnt_p->flags, gnt.flags, new_flags) == gnt.flags) { + return (uint64_t)gnt.frame << XEN_PAGE_SHIFT; + } + } + + return INVALID_GPA; +} + +struct xengntdev_handle { + GHashTable *active_maps; +}; + +static int xen_be_gnttab_set_max_grants(struct xengntdev_handle *xgt, + uint32_t nr_grants) +{ + return 0; +} + +static void *xen_be_gnttab_map_refs(struct xengntdev_handle *xgt, + uint32_t count, uint32_t domid, + uint32_t *refs, int prot) +{ + XenGnttabState *s = xen_gnttab_singleton; + struct active_ref *act; + + if (!s) { + errno = ENOTSUP; + return NULL; + } + + if (domid != xen_domid) { + errno = EINVAL; + return NULL; + } + + if (!count || count > 4096) { + errno = EINVAL; + return NULL; + } + + /* + * Making a contiguous mapping from potentially discontiguous grant + * references would be... distinctly non-trivial. We don't support it. + * Even changing the API to return an array of pointers, one per page, + * wouldn't be simple to use in PV backends because some structures + * actually cross page boundaries (e.g. 32-bit blkif_response ring + * entries are 12 bytes). + */ + if (count != 1) { + errno = EINVAL; + return NULL; + } + + QEMU_LOCK_GUARD(&s->gnt_lock); + + act = g_hash_table_lookup(xgt->active_maps, GINT_TO_POINTER(refs[0])); + if (act) { + if ((prot & PROT_WRITE) && !(act->prot & PROT_WRITE)) { + if (gnt_ref(s, refs[0], prot) == INVALID_GPA) { + return NULL; + } + act->prot |= PROT_WRITE; + } + act->refcnt++; + } else { + uint64_t gpa = gnt_ref(s, refs[0], prot); + if (gpa == INVALID_GPA) { + errno = EINVAL; + return NULL; + } + + act = g_new0(struct active_ref, 1); + act->prot = prot; + act->refcnt = 1; + act->mrs = memory_region_find(get_system_memory(), gpa, XEN_PAGE_SIZE); + + if (act->mrs.mr && + !int128_lt(act->mrs.size, int128_make64(XEN_PAGE_SIZE)) && + memory_region_get_ram_addr(act->mrs.mr) != RAM_ADDR_INVALID) { + act->virtaddr = qemu_map_ram_ptr(act->mrs.mr->ram_block, + act->mrs.offset_within_region); + } + if (!act->virtaddr) { + gnt_unref(s, refs[0], &act->mrs, 0); + g_free(act); + errno = EINVAL; + return NULL; + } + + s->map_track[refs[0]]++; + g_hash_table_insert(xgt->active_maps, GINT_TO_POINTER(refs[0]), act); + } + + return act->virtaddr; +} + +static gboolean do_unmap(gpointer key, gpointer value, gpointer user_data) +{ + XenGnttabState *s = user_data; + grant_ref_t gref = GPOINTER_TO_INT(key); + struct active_ref *act = value; + + gnt_unref(s, gref, &act->mrs, act->prot); + g_free(act); + return true; +} + +static int xen_be_gnttab_unmap(struct xengntdev_handle *xgt, + void *start_address, uint32_t *refs, + uint32_t count) +{ + XenGnttabState *s = xen_gnttab_singleton; + struct active_ref *act; + + if (!s) { + return -ENOTSUP; + } + + if (count != 1) { + return -EINVAL; + } + + QEMU_LOCK_GUARD(&s->gnt_lock); + + act = g_hash_table_lookup(xgt->active_maps, GINT_TO_POINTER(refs[0])); + if (!act) { + return -ENOENT; + } + + if (act->virtaddr != start_address) { + return -EINVAL; + } + + if (!--act->refcnt) { + do_unmap(GINT_TO_POINTER(refs[0]), act, s); + g_hash_table_remove(xgt->active_maps, GINT_TO_POINTER(refs[0])); + } + + return 0; +} + +/* + * This looks a bit like the one for true Xen in xen-operations.c but + * in emulation we don't support multi-page mappings. And under Xen we + * *want* the multi-page mappings so we have fewer bounces through the + * kernel and the hypervisor. So the code paths end up being similar, + * but different. + */ +static int xen_be_gnttab_copy(struct xengntdev_handle *xgt, bool to_domain, + uint32_t domid, XenGrantCopySegment *segs, + uint32_t nr_segs, Error **errp) +{ + int prot = to_domain ? PROT_WRITE : PROT_READ; + unsigned int i; + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + void *page; + uint32_t ref = to_domain ? seg->dest.foreign.ref : + seg->source.foreign.ref; + + page = xen_be_gnttab_map_refs(xgt, 1, domid, &ref, prot); + if (!page) { + if (errp) { + error_setg_errno(errp, errno, + "xen_be_gnttab_map_refs failed"); + } + return -errno; + } + + if (to_domain) { + memcpy(page + seg->dest.foreign.offset, seg->source.virt, + seg->len); + } else { + memcpy(seg->dest.virt, page + seg->source.foreign.offset, + seg->len); + } + + if (xen_be_gnttab_unmap(xgt, page, &ref, 1)) { + if (errp) { + error_setg_errno(errp, errno, "xen_be_gnttab_unmap failed"); + } + return -errno; + } + } + + return 0; +} + +static struct xengntdev_handle *xen_be_gnttab_open(void) +{ + struct xengntdev_handle *xgt = g_new0(struct xengntdev_handle, 1); + + xgt->active_maps = g_hash_table_new(g_direct_hash, g_direct_equal); + return xgt; +} + +static int xen_be_gnttab_close(struct xengntdev_handle *xgt) +{ + XenGnttabState *s = xen_gnttab_singleton; + + if (!s) { + return -ENOTSUP; + } + + g_hash_table_foreach_remove(xgt->active_maps, do_unmap, s); + g_hash_table_destroy(xgt->active_maps); + g_free(xgt); + return 0; +} + +static struct gnttab_backend_ops emu_gnttab_backend_ops = { + .open = xen_be_gnttab_open, + .close = xen_be_gnttab_close, + .grant_copy = xen_be_gnttab_copy, + .set_max_grants = xen_be_gnttab_set_max_grants, + .map_refs = xen_be_gnttab_map_refs, + .unmap = xen_be_gnttab_unmap, +}; + +int xen_gnttab_reset(void) +{ + XenGnttabState *s = xen_gnttab_singleton; + + if (!s) { + return -ENOTSUP; + } + + QEMU_LOCK_GUARD(&s->gnt_lock); + + s->nr_frames = 0; + + memset(s->entries.v1, 0, XEN_PAGE_SIZE * s->max_frames); + s->entries.v1[GNTTAB_RESERVED_XENSTORE].flags = GTF_permit_access; + s->entries.v1[GNTTAB_RESERVED_XENSTORE].frame = XEN_SPECIAL_PFN(XENSTORE); + + if (xen_primary_console_get_pfn()) { + s->entries.v1[GNTTAB_RESERVED_CONSOLE].flags = GTF_permit_access; + s->entries.v1[GNTTAB_RESERVED_CONSOLE].frame = XEN_SPECIAL_PFN(CONSOLE); + } + + return 0; +} diff --git a/hw/i386/kvm/xen_gnttab.h b/hw/i386/kvm/xen_gnttab.h new file mode 100644 index 0000000000..ee215239b0 --- /dev/null +++ b/hw/i386/kvm/xen_gnttab.h @@ -0,0 +1,26 @@ +/* + * QEMU Xen emulation: Grant table support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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_XEN_GNTTAB_H +#define QEMU_XEN_GNTTAB_H + +void xen_gnttab_create(void); +int xen_gnttab_reset(void); +int xen_gnttab_map_page(uint64_t idx, uint64_t gfn); + +struct gnttab_set_version; +struct gnttab_get_version; +struct gnttab_query_size; +int xen_gnttab_set_version_op(struct gnttab_set_version *set); +int xen_gnttab_get_version_op(struct gnttab_get_version *get); +int xen_gnttab_query_size_op(struct gnttab_query_size *size); + +#endif /* QEMU_XEN_GNTTAB_H */ diff --git a/hw/i386/kvm/xen_overlay.c b/hw/i386/kvm/xen_overlay.c new file mode 100644 index 0000000000..c68e78ac5c --- /dev/null +++ b/hw/i386/kvm/xen_overlay.c @@ -0,0 +1,272 @@ +/* + * QEMU Xen emulation: Shared/overlay pages support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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/host-utils.h" +#include "qemu/module.h" +#include "qemu/main-loop.h" +#include "qapi/error.h" +#include "qom/object.h" +#include "exec/target_page.h" +#include "exec/address-spaces.h" +#include "migration/vmstate.h" + +#include "hw/sysbus.h" +#include "hw/xen/xen.h" +#include "xen_overlay.h" + +#include "sysemu/kvm.h" +#include "sysemu/kvm_xen.h" +#include + +#include "hw/xen/interface/memory.h" + + +#define TYPE_XEN_OVERLAY "xen-overlay" +OBJECT_DECLARE_SIMPLE_TYPE(XenOverlayState, XEN_OVERLAY) + +#define XEN_PAGE_SHIFT 12 +#define XEN_PAGE_SIZE (1ULL << XEN_PAGE_SHIFT) + +struct XenOverlayState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + MemoryRegion shinfo_mem; + void *shinfo_ptr; + uint64_t shinfo_gpa; + bool long_mode; +}; + +struct XenOverlayState *xen_overlay_singleton; + +void xen_overlay_do_map_page(MemoryRegion *page, uint64_t gpa) +{ + /* + * Xen allows guests to map the same page as many times as it likes + * into guest physical frames. We don't, because it would be hard + * to track and restore them all. One mapping of each page is + * perfectly sufficient for all known guests... and we've tested + * that theory on a few now in other implementations. dwmw2. + */ + if (memory_region_is_mapped(page)) { + if (gpa == INVALID_GPA) { + memory_region_del_subregion(get_system_memory(), page); + } else { + /* Just move it */ + memory_region_set_address(page, gpa); + } + } else if (gpa != INVALID_GPA) { + memory_region_add_subregion_overlap(get_system_memory(), gpa, page, 0); + } +} + +/* KVM is the only existing back end for now. Let's not overengineer it yet. */ +static int xen_overlay_set_be_shinfo(uint64_t gfn) +{ + struct kvm_xen_hvm_attr xa = { + .type = KVM_XEN_ATTR_TYPE_SHARED_INFO, + .u.shared_info.gfn = gfn, + }; + + return kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &xa); +} + + +static void xen_overlay_realize(DeviceState *dev, Error **errp) +{ + XenOverlayState *s = XEN_OVERLAY(dev); + + if (xen_mode != XEN_EMULATE) { + error_setg(errp, "Xen overlay page support is for Xen emulation"); + return; + } + + memory_region_init_ram(&s->shinfo_mem, OBJECT(dev), "xen:shared_info", + XEN_PAGE_SIZE, &error_abort); + memory_region_set_enabled(&s->shinfo_mem, true); + + s->shinfo_ptr = memory_region_get_ram_ptr(&s->shinfo_mem); + s->shinfo_gpa = INVALID_GPA; + s->long_mode = false; + memset(s->shinfo_ptr, 0, XEN_PAGE_SIZE); +} + +static int xen_overlay_pre_save(void *opaque) +{ + /* + * Fetch the kernel's idea of long_mode to avoid the race condition + * where the guest has set the hypercall page up in 64-bit mode but + * not yet made a hypercall by the time migration happens, so qemu + * hasn't yet noticed. + */ + return xen_sync_long_mode(); +} + +static int xen_overlay_post_load(void *opaque, int version_id) +{ + XenOverlayState *s = opaque; + + if (s->shinfo_gpa != INVALID_GPA) { + xen_overlay_do_map_page(&s->shinfo_mem, s->shinfo_gpa); + xen_overlay_set_be_shinfo(s->shinfo_gpa >> XEN_PAGE_SHIFT); + } + if (s->long_mode) { + xen_set_long_mode(true); + } + + return 0; +} + +static bool xen_overlay_is_needed(void *opaque) +{ + return xen_mode == XEN_EMULATE; +} + +static const VMStateDescription xen_overlay_vmstate = { + .name = "xen_overlay", + .version_id = 1, + .minimum_version_id = 1, + .needed = xen_overlay_is_needed, + .pre_save = xen_overlay_pre_save, + .post_load = xen_overlay_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(shinfo_gpa, XenOverlayState), + VMSTATE_BOOL(long_mode, XenOverlayState), + VMSTATE_END_OF_LIST() + } +}; + +static void xen_overlay_reset(DeviceState *dev) +{ + kvm_xen_soft_reset(); +} + +static void xen_overlay_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = xen_overlay_reset; + dc->realize = xen_overlay_realize; + dc->vmsd = &xen_overlay_vmstate; +} + +static const TypeInfo xen_overlay_info = { + .name = TYPE_XEN_OVERLAY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XenOverlayState), + .class_init = xen_overlay_class_init, +}; + +void xen_overlay_create(void) +{ + xen_overlay_singleton = XEN_OVERLAY(sysbus_create_simple(TYPE_XEN_OVERLAY, + -1, NULL)); + + /* If xen_domid wasn't explicitly set, at least make sure it isn't zero. */ + if (xen_domid == DOMID_QEMU) { + xen_domid = 1; + }; +} + +static void xen_overlay_register_types(void) +{ + type_register_static(&xen_overlay_info); +} + +type_init(xen_overlay_register_types) + +int xen_overlay_map_shinfo_page(uint64_t gpa) +{ + XenOverlayState *s = xen_overlay_singleton; + int ret; + + if (!s) { + return -ENOENT; + } + + assert(bql_locked()); + + if (s->shinfo_gpa) { + /* If removing shinfo page, turn the kernel magic off first */ + ret = xen_overlay_set_be_shinfo(INVALID_GFN); + if (ret) { + return ret; + } + } + + xen_overlay_do_map_page(&s->shinfo_mem, gpa); + if (gpa != INVALID_GPA) { + ret = xen_overlay_set_be_shinfo(gpa >> XEN_PAGE_SHIFT); + if (ret) { + return ret; + } + } + s->shinfo_gpa = gpa; + + return 0; +} + +void *xen_overlay_get_shinfo_ptr(void) +{ + XenOverlayState *s = xen_overlay_singleton; + + if (!s) { + return NULL; + } + + return s->shinfo_ptr; +} + +int xen_sync_long_mode(void) +{ + int ret; + struct kvm_xen_hvm_attr xa = { + .type = KVM_XEN_ATTR_TYPE_LONG_MODE, + }; + + if (!xen_overlay_singleton) { + return -ENOENT; + } + + ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_GET_ATTR, &xa); + if (!ret) { + xen_overlay_singleton->long_mode = xa.u.long_mode; + } + + return ret; +} + +int xen_set_long_mode(bool long_mode) +{ + int ret; + struct kvm_xen_hvm_attr xa = { + .type = KVM_XEN_ATTR_TYPE_LONG_MODE, + .u.long_mode = long_mode, + }; + + if (!xen_overlay_singleton) { + return -ENOENT; + } + + ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &xa); + if (!ret) { + xen_overlay_singleton->long_mode = xa.u.long_mode; + } + + return ret; +} + +bool xen_is_long_mode(void) +{ + return xen_overlay_singleton && xen_overlay_singleton->long_mode; +} diff --git a/hw/i386/kvm/xen_overlay.h b/hw/i386/kvm/xen_overlay.h new file mode 100644 index 0000000000..75ecb6b359 --- /dev/null +++ b/hw/i386/kvm/xen_overlay.h @@ -0,0 +1,26 @@ +/* + * QEMU Xen emulation: Shared/overlay pages support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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_XEN_OVERLAY_H +#define QEMU_XEN_OVERLAY_H + +void xen_overlay_create(void); + +int xen_overlay_map_shinfo_page(uint64_t gpa); +void *xen_overlay_get_shinfo_ptr(void); + +int xen_sync_long_mode(void); +int xen_set_long_mode(bool long_mode); +bool xen_is_long_mode(void); + +void xen_overlay_do_map_page(MemoryRegion *page, uint64_t gpa); + +#endif /* QEMU_XEN_OVERLAY_H */ diff --git a/hw/i386/kvm/xen_primary_console.c b/hw/i386/kvm/xen_primary_console.c new file mode 100644 index 0000000000..abe79f565b --- /dev/null +++ b/hw/i386/kvm/xen_primary_console.c @@ -0,0 +1,193 @@ +/* + * QEMU Xen emulation: Primary console support + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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/error.h" + +#include "hw/sysbus.h" +#include "hw/xen/xen.h" +#include "hw/xen/xen_backend_ops.h" +#include "xen_evtchn.h" +#include "xen_overlay.h" +#include "xen_primary_console.h" + +#include "sysemu/kvm.h" +#include "sysemu/kvm_xen.h" + +#include "trace.h" + +#include "hw/xen/interface/event_channel.h" +#include "hw/xen/interface/grant_table.h" + +#define TYPE_XEN_PRIMARY_CONSOLE "xen-primary-console" +OBJECT_DECLARE_SIMPLE_TYPE(XenPrimaryConsoleState, XEN_PRIMARY_CONSOLE) + +struct XenPrimaryConsoleState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + MemoryRegion console_page; + void *cp; + + evtchn_port_t guest_port; + evtchn_port_t be_port; + + struct xengntdev_handle *gt; + void *granted_xs; +}; + +struct XenPrimaryConsoleState *xen_primary_console_singleton; + +static void xen_primary_console_realize(DeviceState *dev, Error **errp) +{ + XenPrimaryConsoleState *s = XEN_PRIMARY_CONSOLE(dev); + + if (xen_mode != XEN_EMULATE) { + error_setg(errp, "Xen primary console support is for Xen emulation"); + return; + } + + memory_region_init_ram(&s->console_page, OBJECT(dev), "xen:console_page", + XEN_PAGE_SIZE, &error_abort); + memory_region_set_enabled(&s->console_page, true); + s->cp = memory_region_get_ram_ptr(&s->console_page); + memset(s->cp, 0, XEN_PAGE_SIZE); + + /* We can't map it this early as KVM isn't ready */ + xen_primary_console_singleton = s; +} + +static void xen_primary_console_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = xen_primary_console_realize; +} + +static const TypeInfo xen_primary_console_info = { + .name = TYPE_XEN_PRIMARY_CONSOLE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XenPrimaryConsoleState), + .class_init = xen_primary_console_class_init, +}; + + +void xen_primary_console_create(void) +{ + DeviceState *dev = sysbus_create_simple(TYPE_XEN_PRIMARY_CONSOLE, -1, NULL); + + trace_xen_primary_console_create(); + + xen_primary_console_singleton = XEN_PRIMARY_CONSOLE(dev); + + /* + * Defer the init (xen_primary_console_reset()) until KVM is set up and the + * overlay page can be mapped. + */ +} + +static void xen_primary_console_register_types(void) +{ + type_register_static(&xen_primary_console_info); +} + +type_init(xen_primary_console_register_types) + +uint16_t xen_primary_console_get_port(void) +{ + XenPrimaryConsoleState *s = xen_primary_console_singleton; + if (!s) { + return 0; + } + return s->guest_port; +} + +void xen_primary_console_set_be_port(uint16_t port) +{ + XenPrimaryConsoleState *s = xen_primary_console_singleton; + if (s) { + s->be_port = port; + } +} + +uint64_t xen_primary_console_get_pfn(void) +{ + XenPrimaryConsoleState *s = xen_primary_console_singleton; + if (!s) { + return 0; + } + return XEN_SPECIAL_PFN(CONSOLE); +} + +void *xen_primary_console_get_map(void) +{ + XenPrimaryConsoleState *s = xen_primary_console_singleton; + if (!s) { + return 0; + } + return s->cp; +} + +static void alloc_guest_port(XenPrimaryConsoleState *s) +{ + struct evtchn_alloc_unbound alloc = { + .dom = DOMID_SELF, + .remote_dom = DOMID_QEMU, + }; + + if (!xen_evtchn_alloc_unbound_op(&alloc)) { + s->guest_port = alloc.port; + } +} + +static void rebind_guest_port(XenPrimaryConsoleState *s) +{ + struct evtchn_bind_interdomain inter = { + .remote_dom = DOMID_QEMU, + .remote_port = s->be_port, + }; + + if (!xen_evtchn_bind_interdomain_op(&inter)) { + s->guest_port = inter.local_port; + } + + s->be_port = 0; +} + +int xen_primary_console_reset(void) +{ + XenPrimaryConsoleState *s = xen_primary_console_singleton; + if (!s) { + return 0; + } + + if (!memory_region_is_mapped(&s->console_page)) { + uint64_t gpa = XEN_SPECIAL_PFN(CONSOLE) << TARGET_PAGE_BITS; + xen_overlay_do_map_page(&s->console_page, gpa); + } + + if (s->be_port) { + rebind_guest_port(s); + } else { + alloc_guest_port(s); + } + + trace_xen_primary_console_reset(s->guest_port); + + s->gt = qemu_xen_gnttab_open(); + uint32_t xs_gntref = GNTTAB_RESERVED_CONSOLE; + s->granted_xs = qemu_xen_gnttab_map_refs(s->gt, 1, xen_domid, &xs_gntref, + PROT_READ | PROT_WRITE); + + return 0; +} diff --git a/hw/i386/kvm/xen_primary_console.h b/hw/i386/kvm/xen_primary_console.h new file mode 100644 index 0000000000..7e2989ea0d --- /dev/null +++ b/hw/i386/kvm/xen_primary_console.h @@ -0,0 +1,23 @@ +/* + * QEMU Xen emulation: Primary console support + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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_XEN_PRIMARY_CONSOLE_H +#define QEMU_XEN_PRIMARY_CONSOLE_H + +void xen_primary_console_create(void); +int xen_primary_console_reset(void); + +uint16_t xen_primary_console_get_port(void); +void xen_primary_console_set_be_port(uint16_t port); +uint64_t xen_primary_console_get_pfn(void); +void *xen_primary_console_get_map(void); + +#endif /* QEMU_XEN_PRIMARY_CONSOLE_H */ diff --git a/hw/i386/kvm/xen_xenstore.c b/hw/i386/kvm/xen_xenstore.c new file mode 100644 index 0000000000..1a9bc342b8 --- /dev/null +++ b/hw/i386/kvm/xen_xenstore.c @@ -0,0 +1,1751 @@ +/* + * QEMU Xen emulation: Shared/overlay pages support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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/host-utils.h" +#include "qemu/module.h" +#include "qemu/main-loop.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qom/object.h" +#include "migration/vmstate.h" + +#include "hw/sysbus.h" +#include "hw/xen/xen.h" +#include "hw/xen/xen_backend_ops.h" +#include "xen_overlay.h" +#include "xen_evtchn.h" +#include "xen_primary_console.h" +#include "xen_xenstore.h" + +#include "sysemu/kvm.h" +#include "sysemu/kvm_xen.h" + +#include "trace.h" + +#include "xenstore_impl.h" + +#include "hw/xen/interface/io/xs_wire.h" +#include "hw/xen/interface/event_channel.h" +#include "hw/xen/interface/grant_table.h" + +#define TYPE_XEN_XENSTORE "xen-xenstore" +OBJECT_DECLARE_SIMPLE_TYPE(XenXenstoreState, XEN_XENSTORE) + +#define ENTRIES_PER_FRAME_V1 (XEN_PAGE_SIZE / sizeof(grant_entry_v1_t)) +#define ENTRIES_PER_FRAME_V2 (XEN_PAGE_SIZE / sizeof(grant_entry_v2_t)) + +#define XENSTORE_HEADER_SIZE ((unsigned int)sizeof(struct xsd_sockmsg)) + +struct XenXenstoreState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + XenstoreImplState *impl; + GList *watch_events; /* for the guest */ + + MemoryRegion xenstore_page; + struct xenstore_domain_interface *xs; + uint8_t req_data[XENSTORE_HEADER_SIZE + XENSTORE_PAYLOAD_MAX]; + uint8_t rsp_data[XENSTORE_HEADER_SIZE + XENSTORE_PAYLOAD_MAX]; + uint32_t req_offset; + uint32_t rsp_offset; + bool rsp_pending; + bool fatal_error; + + evtchn_port_t guest_port; + evtchn_port_t be_port; + struct xenevtchn_handle *eh; + + uint8_t *impl_state; + uint32_t impl_state_size; + + struct xengntdev_handle *gt; + void *granted_xs; +}; + +struct XenXenstoreState *xen_xenstore_singleton; + +static void xen_xenstore_event(void *opaque); +static void fire_watch_cb(void *opaque, const char *path, const char *token); + +static struct xenstore_backend_ops emu_xenstore_backend_ops; + +static void G_GNUC_PRINTF (4, 5) relpath_printf(XenXenstoreState *s, + GList *perms, + const char *relpath, + const char *fmt, ...) +{ + gchar *abspath; + gchar *value; + va_list args; + GByteArray *data; + int err; + + abspath = g_strdup_printf("/local/domain/%u/%s", xen_domid, relpath); + va_start(args, fmt); + value = g_strdup_vprintf(fmt, args); + va_end(args); + + data = g_byte_array_new_take((void *)value, strlen(value)); + + err = xs_impl_write(s->impl, DOMID_QEMU, XBT_NULL, abspath, data); + assert(!err); + + g_byte_array_unref(data); + + err = xs_impl_set_perms(s->impl, DOMID_QEMU, XBT_NULL, abspath, perms); + assert(!err); + + g_free(abspath); +} + +static void xen_xenstore_realize(DeviceState *dev, Error **errp) +{ + XenXenstoreState *s = XEN_XENSTORE(dev); + GList *perms; + + if (xen_mode != XEN_EMULATE) { + error_setg(errp, "Xen xenstore support is for Xen emulation"); + return; + } + memory_region_init_ram(&s->xenstore_page, OBJECT(dev), "xen:xenstore_page", + XEN_PAGE_SIZE, &error_abort); + memory_region_set_enabled(&s->xenstore_page, true); + s->xs = memory_region_get_ram_ptr(&s->xenstore_page); + memset(s->xs, 0, XEN_PAGE_SIZE); + + /* We can't map it this early as KVM isn't ready */ + xen_xenstore_singleton = s; + + s->eh = xen_be_evtchn_open(); + if (!s->eh) { + error_setg(errp, "Xenstore evtchn port init failed"); + return; + } + aio_set_fd_handler(qemu_get_aio_context(), xen_be_evtchn_fd(s->eh), + xen_xenstore_event, NULL, NULL, NULL, s); + + s->impl = xs_impl_create(xen_domid); + + /* Populate the default nodes */ + + /* Nodes owned by 'dom0' but readable by the guest */ + perms = g_list_append(NULL, xs_perm_as_string(XS_PERM_NONE, DOMID_QEMU)); + perms = g_list_append(perms, xs_perm_as_string(XS_PERM_READ, xen_domid)); + + relpath_printf(s, perms, "", "%s", ""); + + relpath_printf(s, perms, "domid", "%u", xen_domid); + + relpath_printf(s, perms, "control/platform-feature-xs_reset_watches", "%u", 1); + relpath_printf(s, perms, "control/platform-feature-multiprocessor-suspend", "%u", 1); + + relpath_printf(s, perms, "platform/acpi", "%u", 1); + relpath_printf(s, perms, "platform/acpi_s3", "%u", 1); + relpath_printf(s, perms, "platform/acpi_s4", "%u", 1); + relpath_printf(s, perms, "platform/acpi_laptop_slate", "%u", 0); + + g_list_free_full(perms, g_free); + + /* Nodes owned by the guest */ + perms = g_list_append(NULL, xs_perm_as_string(XS_PERM_NONE, xen_domid)); + + relpath_printf(s, perms, "attr", "%s", ""); + + relpath_printf(s, perms, "control/shutdown", "%s", ""); + relpath_printf(s, perms, "control/feature-poweroff", "%u", 1); + relpath_printf(s, perms, "control/feature-reboot", "%u", 1); + relpath_printf(s, perms, "control/feature-suspend", "%u", 1); + relpath_printf(s, perms, "control/feature-s3", "%u", 1); + relpath_printf(s, perms, "control/feature-s4", "%u", 1); + + relpath_printf(s, perms, "data", "%s", ""); + relpath_printf(s, perms, "device", "%s", ""); + relpath_printf(s, perms, "drivers", "%s", ""); + relpath_printf(s, perms, "error", "%s", ""); + relpath_printf(s, perms, "feature", "%s", ""); + + g_list_free_full(perms, g_free); + + xen_xenstore_ops = &emu_xenstore_backend_ops; +} + +static bool xen_xenstore_is_needed(void *opaque) +{ + return xen_mode == XEN_EMULATE; +} + +static int xen_xenstore_pre_save(void *opaque) +{ + XenXenstoreState *s = opaque; + GByteArray *save; + + if (s->eh) { + s->guest_port = xen_be_evtchn_get_guest_port(s->eh); + } + + g_free(s->impl_state); + save = xs_impl_serialize(s->impl); + s->impl_state = save->data; + s->impl_state_size = save->len; + g_byte_array_free(save, false); + + return 0; +} + +static int xen_xenstore_post_load(void *opaque, int ver) +{ + XenXenstoreState *s = opaque; + GByteArray *save; + int ret; + + /* + * As qemu/dom0, rebind to the guest's port. The Windows drivers may + * unbind the XenStore evtchn and rebind to it, having obtained the + * "remote" port through EVTCHNOP_status. In the case that migration + * occurs while it's unbound, the "remote" port needs to be the same + * as before so that the guest can find it, but should remain unbound. + */ + if (s->guest_port) { + int be_port = xen_be_evtchn_bind_interdomain(s->eh, xen_domid, + s->guest_port); + if (be_port < 0) { + return be_port; + } + s->be_port = be_port; + } + + save = g_byte_array_new_take(s->impl_state, s->impl_state_size); + s->impl_state = NULL; + s->impl_state_size = 0; + + ret = xs_impl_deserialize(s->impl, save, xen_domid, fire_watch_cb, s); + return ret; +} + +static const VMStateDescription xen_xenstore_vmstate = { + .name = "xen_xenstore", + .unmigratable = 1, /* The PV back ends don't migrate yet */ + .version_id = 1, + .minimum_version_id = 1, + .needed = xen_xenstore_is_needed, + .pre_save = xen_xenstore_pre_save, + .post_load = xen_xenstore_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT8_ARRAY(req_data, XenXenstoreState, + sizeof_field(XenXenstoreState, req_data)), + VMSTATE_UINT8_ARRAY(rsp_data, XenXenstoreState, + sizeof_field(XenXenstoreState, rsp_data)), + VMSTATE_UINT32(req_offset, XenXenstoreState), + VMSTATE_UINT32(rsp_offset, XenXenstoreState), + VMSTATE_BOOL(rsp_pending, XenXenstoreState), + VMSTATE_UINT32(guest_port, XenXenstoreState), + VMSTATE_BOOL(fatal_error, XenXenstoreState), + VMSTATE_UINT32(impl_state_size, XenXenstoreState), + VMSTATE_VARRAY_UINT32_ALLOC(impl_state, XenXenstoreState, + impl_state_size, 0, + vmstate_info_uint8, uint8_t), + VMSTATE_END_OF_LIST() + } +}; + +static void xen_xenstore_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = xen_xenstore_realize; + dc->vmsd = &xen_xenstore_vmstate; +} + +static const TypeInfo xen_xenstore_info = { + .name = TYPE_XEN_XENSTORE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XenXenstoreState), + .class_init = xen_xenstore_class_init, +}; + +void xen_xenstore_create(void) +{ + DeviceState *dev = sysbus_create_simple(TYPE_XEN_XENSTORE, -1, NULL); + + xen_xenstore_singleton = XEN_XENSTORE(dev); + + /* + * Defer the init (xen_xenstore_reset()) until KVM is set up and the + * overlay page can be mapped. + */ +} + +static void xen_xenstore_register_types(void) +{ + type_register_static(&xen_xenstore_info); +} + +type_init(xen_xenstore_register_types) + +uint16_t xen_xenstore_get_port(void) +{ + XenXenstoreState *s = xen_xenstore_singleton; + if (!s) { + return 0; + } + return s->guest_port; +} + +static bool req_pending(XenXenstoreState *s) +{ + struct xsd_sockmsg *req = (struct xsd_sockmsg *)s->req_data; + + return s->req_offset == XENSTORE_HEADER_SIZE + req->len; +} + +static void reset_req(XenXenstoreState *s) +{ + memset(s->req_data, 0, sizeof(s->req_data)); + s->req_offset = 0; +} + +static void reset_rsp(XenXenstoreState *s) +{ + s->rsp_pending = false; + + memset(s->rsp_data, 0, sizeof(s->rsp_data)); + s->rsp_offset = 0; +} + +static void xs_error(XenXenstoreState *s, unsigned int id, + xs_transaction_t tx_id, int errnum) +{ + struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data; + const char *errstr = NULL; + + for (unsigned int i = 0; i < ARRAY_SIZE(xsd_errors); i++) { + const struct xsd_errors *xsd_error = &xsd_errors[i]; + + if (xsd_error->errnum == errnum) { + errstr = xsd_error->errstring; + break; + } + } + assert(errstr); + + trace_xenstore_error(id, tx_id, errstr); + + rsp->type = XS_ERROR; + rsp->req_id = id; + rsp->tx_id = tx_id; + rsp->len = (uint32_t)strlen(errstr) + 1; + + memcpy(&rsp[1], errstr, rsp->len); +} + +static void xs_ok(XenXenstoreState *s, unsigned int type, unsigned int req_id, + xs_transaction_t tx_id) +{ + struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data; + const char *okstr = "OK"; + + rsp->type = type; + rsp->req_id = req_id; + rsp->tx_id = tx_id; + rsp->len = (uint32_t)strlen(okstr) + 1; + + memcpy(&rsp[1], okstr, rsp->len); +} + +/* + * The correct request and response formats are documented in xen.git: + * docs/misc/xenstore.txt. A summary is given below for convenience. + * The '|' symbol represents a NUL character. + * + * ---------- Database read, write and permissions operations ---------- + * + * READ | + * WRITE | + * Store and read the octet string at . + * WRITE creates any missing parent paths, with empty values. + * + * MKDIR | + * Ensures that the exists, by necessary by creating + * it and any missing parents with empty values. If + * or any parent already exists, its value is left unchanged. + * + * RM | + * Ensures that the does not exist, by deleting + * it and all of its children. It is not an error if does + * not exist, but it _is_ an error if 's immediate parent + * does not exist either. + * + * DIRECTORY | |* + * Gives a list of the immediate children of , as only the + * leafnames. The resulting children are each named + * /. + * + * DIRECTORY_PART | ||* + * Same as DIRECTORY, but to be used for children lists longer than + * XENSTORE_PAYLOAD_MAX. Input are and the byte offset into + * the list of children to return. Return values are the generation + * count of the node (to be used to ensure the node hasn't + * changed between two reads: being the same for multiple + * reads guarantees the node hasn't changed) and the list of children + * starting at the specified of the complete list. + * + * GET_PERMS | |+ + * SET_PERMS ||+? + * is one of the following + * w write only + * r read only + * b both read and write + * n no access + * See https://wiki.xen.org/wiki/XenBus section + * `Permissions' for details of the permissions system. + * It is possible to set permissions for the special watch paths + * "@introduceDomain" and "@releaseDomain" to enable receiving those + * watches in unprivileged domains. + * + * ---------- Watches ---------- + * + * WATCH ||? + * Adds a watch. + * + * When a is modified (including path creation, removal, + * contents change or permissions change) this generates an event + * on the changed . Changes made in transactions cause an + * event only if and when committed. Each occurring event is + * matched against all the watches currently set up, and each + * matching watch results in a WATCH_EVENT message (see below). + * + * The event's path matches the watch's if it is an child + * of . + * + * can be a to watch or @. In the + * latter case may have any syntax but it matches + * (according to the rules above) only the following special + * events which are invented by xenstored: + * @introduceDomain occurs on INTRODUCE + * @releaseDomain occurs on any domain crash or + * shutdown, and also on RELEASE + * and domain destruction + * events are sent to privileged callers or explicitly + * via SET_PERMS enabled domains only. + * + * When a watch is first set up it is triggered once straight + * away, with equal to . Watches may be triggered + * spuriously. The tx_id in a WATCH request is ignored. + * + * Watches are supposed to be restricted by the permissions + * system but in practice the implementation is imperfect. + * Applications should not rely on being sent a notification for + * paths that they cannot read; however, an application may rely + * on being sent a watch when a path which it _is_ able to read + * is deleted even if that leaves only a nonexistent unreadable + * parent. A notification may omitted if a node's permissions + * are changed so as to make it unreadable, in which case future + * notifications may be suppressed (and if the node is later made + * readable, some notifications may have been lost). + * + * WATCH_EVENT || + * Unsolicited `reply' generated for matching modification events + * as described above. req_id and tx_id are both 0. + * + * is the event's path, ie the actual path that was + * modified; however if the event was the recursive removal of an + * parent of , is just + * (rather than the actual path which was removed). So + * is a child of , regardless. + * + * Iff for the watch was specified as a relative pathname, + * the path will also be relative (with the same base, + * obviously). + * + * UNWATCH ||? + * + * RESET_WATCHES | + * Reset all watches and transactions of the caller. + * + * ---------- Transactions ---------- + * + * TRANSACTION_START | | + * is an opaque uint32_t allocated by xenstored + * represented as unsigned decimal. After this, transaction may + * be referenced by using (as 32-bit binary) in the + * tx_id request header field. When transaction is started whole + * db is copied; reads and writes happen on the copy. + * It is not legal to send non-0 tx_id in TRANSACTION_START. + * + * TRANSACTION_END T| + * TRANSACTION_END F| + * tx_id must refer to existing transaction. After this + * request the tx_id is no longer valid and may be reused by + * xenstore. If F, the transaction is discarded. If T, + * it is committed: if there were any other intervening writes + * then our END gets get EAGAIN. + * + * The plan is that in the future only intervening `conflicting' + * writes cause EAGAIN, meaning only writes or other commits + * which changed paths which were read or written in the + * transaction at hand. + * + */ + +static void xs_read(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *req_data, unsigned int len) +{ + const char *path = (const char *)req_data; + struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data; + uint8_t *rsp_data = (uint8_t *)&rsp[1]; + g_autoptr(GByteArray) data = g_byte_array_new(); + int err; + + if (len == 0 || req_data[len - 1] != '\0') { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + trace_xenstore_read(tx_id, path); + err = xs_impl_read(s->impl, xen_domid, tx_id, path, data); + if (err) { + xs_error(s, req_id, tx_id, err); + return; + } + + rsp->type = XS_READ; + rsp->req_id = req_id; + rsp->tx_id = tx_id; + rsp->len = 0; + + len = data->len; + if (len > XENSTORE_PAYLOAD_MAX) { + xs_error(s, req_id, tx_id, E2BIG); + return; + } + + memcpy(&rsp_data[rsp->len], data->data, len); + rsp->len += len; +} + +static void xs_write(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *req_data, + unsigned int len) +{ + g_autoptr(GByteArray) data = g_byte_array_new(); + const char *path; + int err; + + if (len == 0) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + path = (const char *)req_data; + + while (len--) { + if (*req_data++ == '\0') { + break; + } + if (len == 0) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + } + + g_byte_array_append(data, req_data, len); + + trace_xenstore_write(tx_id, path); + err = xs_impl_write(s->impl, xen_domid, tx_id, path, data); + if (err) { + xs_error(s, req_id, tx_id, err); + return; + } + + xs_ok(s, XS_WRITE, req_id, tx_id); +} + +static void xs_mkdir(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *req_data, + unsigned int len) +{ + g_autoptr(GByteArray) data = g_byte_array_new(); + const char *path; + int err; + + if (len == 0 || req_data[len - 1] != '\0') { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + path = (const char *)req_data; + + trace_xenstore_mkdir(tx_id, path); + err = xs_impl_read(s->impl, xen_domid, tx_id, path, data); + if (err == ENOENT) { + err = xs_impl_write(s->impl, xen_domid, tx_id, path, data); + } + + if (!err) { + xs_error(s, req_id, tx_id, err); + return; + } + + xs_ok(s, XS_MKDIR, req_id, tx_id); +} + +static void xs_append_strings(XenXenstoreState *s, struct xsd_sockmsg *rsp, + GList *strings, unsigned int start, bool truncate) +{ + uint8_t *rsp_data = (uint8_t *)&rsp[1]; + GList *l; + + for (l = strings; l; l = l->next) { + size_t len = strlen(l->data) + 1; /* Including the NUL termination */ + char *str = l->data; + + if (rsp->len + len > XENSTORE_PAYLOAD_MAX) { + if (truncate) { + len = XENSTORE_PAYLOAD_MAX - rsp->len; + if (!len) { + return; + } + } else { + xs_error(s, rsp->req_id, rsp->tx_id, E2BIG); + return; + } + } + + if (start) { + if (start >= len) { + start -= len; + continue; + } + + str += start; + len -= start; + start = 0; + } + + memcpy(&rsp_data[rsp->len], str, len); + rsp->len += len; + } + /* XS_DIRECTORY_PART wants an extra NUL to indicate the end */ + if (truncate && rsp->len < XENSTORE_PAYLOAD_MAX) { + rsp_data[rsp->len++] = '\0'; + } +} + +static void xs_directory(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *req_data, + unsigned int len) +{ + struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data; + GList *items = NULL; + const char *path; + int err; + + if (len == 0 || req_data[len - 1] != '\0') { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + path = (const char *)req_data; + + trace_xenstore_directory(tx_id, path); + err = xs_impl_directory(s->impl, xen_domid, tx_id, path, NULL, &items); + if (err != 0) { + xs_error(s, req_id, tx_id, err); + return; + } + + rsp->type = XS_DIRECTORY; + rsp->req_id = req_id; + rsp->tx_id = tx_id; + rsp->len = 0; + + xs_append_strings(s, rsp, items, 0, false); + + g_list_free_full(items, g_free); +} + +static void xs_directory_part(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *req_data, + unsigned int len) +{ + const char *offset_str, *path = (const char *)req_data; + struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data; + char *rsp_data = (char *)&rsp[1]; + uint64_t gencnt = 0; + unsigned int offset; + GList *items = NULL; + int err; + + if (len == 0) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + while (len--) { + if (*req_data++ == '\0') { + break; + } + if (len == 0) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + } + + offset_str = (const char *)req_data; + while (len--) { + if (*req_data++ == '\0') { + break; + } + if (len == 0) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + } + + if (len) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + if (qemu_strtoui(offset_str, NULL, 10, &offset) < 0) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + trace_xenstore_directory_part(tx_id, path, offset); + err = xs_impl_directory(s->impl, xen_domid, tx_id, path, &gencnt, &items); + if (err != 0) { + xs_error(s, req_id, tx_id, err); + return; + } + + rsp->type = XS_DIRECTORY_PART; + rsp->req_id = req_id; + rsp->tx_id = tx_id; + rsp->len = snprintf(rsp_data, XENSTORE_PAYLOAD_MAX, "%" PRIu64, gencnt) + 1; + + xs_append_strings(s, rsp, items, offset, true); + + g_list_free_full(items, g_free); +} + +static void xs_transaction_start(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *req_data, + unsigned int len) +{ + struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data; + char *rsp_data = (char *)&rsp[1]; + int err; + + if (len != 1 || req_data[0] != '\0') { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + rsp->type = XS_TRANSACTION_START; + rsp->req_id = req_id; + rsp->tx_id = tx_id; + rsp->len = 0; + + err = xs_impl_transaction_start(s->impl, xen_domid, &tx_id); + if (err) { + xs_error(s, req_id, tx_id, err); + return; + } + + trace_xenstore_transaction_start(tx_id); + + rsp->len = snprintf(rsp_data, XENSTORE_PAYLOAD_MAX, "%u", tx_id); + assert(rsp->len < XENSTORE_PAYLOAD_MAX); + rsp->len++; +} + +static void xs_transaction_end(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *req_data, + unsigned int len) +{ + bool commit; + int err; + + if (len != 2 || req_data[1] != '\0') { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + switch (req_data[0]) { + case 'T': + commit = true; + break; + case 'F': + commit = false; + break; + default: + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + trace_xenstore_transaction_end(tx_id, commit); + err = xs_impl_transaction_end(s->impl, xen_domid, tx_id, commit); + if (err) { + xs_error(s, req_id, tx_id, err); + return; + } + + xs_ok(s, XS_TRANSACTION_END, req_id, tx_id); +} + +static void xs_rm(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *req_data, unsigned int len) +{ + const char *path = (const char *)req_data; + int err; + + if (len == 0 || req_data[len - 1] != '\0') { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + trace_xenstore_rm(tx_id, path); + err = xs_impl_rm(s->impl, xen_domid, tx_id, path); + if (err) { + xs_error(s, req_id, tx_id, err); + return; + } + + xs_ok(s, XS_RM, req_id, tx_id); +} + +static void xs_get_perms(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *req_data, + unsigned int len) +{ + const char *path = (const char *)req_data; + struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data; + GList *perms = NULL; + int err; + + if (len == 0 || req_data[len - 1] != '\0') { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + trace_xenstore_get_perms(tx_id, path); + err = xs_impl_get_perms(s->impl, xen_domid, tx_id, path, &perms); + if (err) { + xs_error(s, req_id, tx_id, err); + return; + } + + rsp->type = XS_GET_PERMS; + rsp->req_id = req_id; + rsp->tx_id = tx_id; + rsp->len = 0; + + xs_append_strings(s, rsp, perms, 0, false); + + g_list_free_full(perms, g_free); +} + +static void xs_set_perms(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *req_data, + unsigned int len) +{ + const char *path = (const char *)req_data; + uint8_t *perm; + GList *perms = NULL; + int err; + + if (len == 0) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + while (len--) { + if (*req_data++ == '\0') { + break; + } + if (len == 0) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + } + + perm = req_data; + while (len--) { + if (*req_data++ == '\0') { + perms = g_list_append(perms, perm); + perm = req_data; + } + } + + /* + * Note that there may be trailing garbage at the end of the buffer. + * This is explicitly permitted by the '?' at the end of the definition: + * + * SET_PERMS ||+? + */ + + trace_xenstore_set_perms(tx_id, path); + err = xs_impl_set_perms(s->impl, xen_domid, tx_id, path, perms); + g_list_free(perms); + if (err) { + xs_error(s, req_id, tx_id, err); + return; + } + + xs_ok(s, XS_SET_PERMS, req_id, tx_id); +} + +static void xs_watch(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *req_data, + unsigned int len) +{ + const char *token, *path = (const char *)req_data; + int err; + + if (len == 0) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + while (len--) { + if (*req_data++ == '\0') { + break; + } + if (len == 0) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + } + + token = (const char *)req_data; + while (len--) { + if (*req_data++ == '\0') { + break; + } + if (len == 0) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + } + + /* + * Note that there may be trailing garbage at the end of the buffer. + * This is explicitly permitted by the '?' at the end of the definition: + * + * WATCH ||? + */ + + trace_xenstore_watch(path, token); + err = xs_impl_watch(s->impl, xen_domid, path, token, fire_watch_cb, s); + if (err) { + xs_error(s, req_id, tx_id, err); + return; + } + + xs_ok(s, XS_WATCH, req_id, tx_id); +} + +static void xs_unwatch(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *req_data, + unsigned int len) +{ + const char *token, *path = (const char *)req_data; + int err; + + if (len == 0) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + while (len--) { + if (*req_data++ == '\0') { + break; + } + if (len == 0) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + } + + token = (const char *)req_data; + while (len--) { + if (*req_data++ == '\0') { + break; + } + if (len == 0) { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + } + + trace_xenstore_unwatch(path, token); + err = xs_impl_unwatch(s->impl, xen_domid, path, token, fire_watch_cb, s); + if (err) { + xs_error(s, req_id, tx_id, err); + return; + } + + xs_ok(s, XS_UNWATCH, req_id, tx_id); +} + +static void xs_reset_watches(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *req_data, + unsigned int len) +{ + if (len == 0 || req_data[len - 1] != '\0') { + xs_error(s, req_id, tx_id, EINVAL); + return; + } + + trace_xenstore_reset_watches(); + xs_impl_reset_watches(s->impl, xen_domid); + + xs_ok(s, XS_RESET_WATCHES, req_id, tx_id); +} + +static void xs_priv(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *data, + unsigned int len) +{ + xs_error(s, req_id, tx_id, EACCES); +} + +static void xs_unimpl(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *data, + unsigned int len) +{ + xs_error(s, req_id, tx_id, ENOSYS); +} + +typedef void (*xs_impl)(XenXenstoreState *s, unsigned int req_id, + xs_transaction_t tx_id, uint8_t *data, + unsigned int len); + +struct xsd_req { + const char *name; + xs_impl fn; +}; +#define XSD_REQ(_type, _fn) \ + [_type] = { .name = #_type, .fn = _fn } + +struct xsd_req xsd_reqs[] = { + XSD_REQ(XS_READ, xs_read), + XSD_REQ(XS_WRITE, xs_write), + XSD_REQ(XS_MKDIR, xs_mkdir), + XSD_REQ(XS_DIRECTORY, xs_directory), + XSD_REQ(XS_DIRECTORY_PART, xs_directory_part), + XSD_REQ(XS_TRANSACTION_START, xs_transaction_start), + XSD_REQ(XS_TRANSACTION_END, xs_transaction_end), + XSD_REQ(XS_RM, xs_rm), + XSD_REQ(XS_GET_PERMS, xs_get_perms), + XSD_REQ(XS_SET_PERMS, xs_set_perms), + XSD_REQ(XS_WATCH, xs_watch), + XSD_REQ(XS_UNWATCH, xs_unwatch), + XSD_REQ(XS_CONTROL, xs_priv), + XSD_REQ(XS_INTRODUCE, xs_priv), + XSD_REQ(XS_RELEASE, xs_priv), + XSD_REQ(XS_IS_DOMAIN_INTRODUCED, xs_priv), + XSD_REQ(XS_RESUME, xs_priv), + XSD_REQ(XS_SET_TARGET, xs_priv), + XSD_REQ(XS_RESET_WATCHES, xs_reset_watches), +}; + +static void process_req(XenXenstoreState *s) +{ + struct xsd_sockmsg *req = (struct xsd_sockmsg *)s->req_data; + xs_impl handler = NULL; + + assert(req_pending(s)); + assert(!s->rsp_pending); + + if (req->type < ARRAY_SIZE(xsd_reqs)) { + handler = xsd_reqs[req->type].fn; + } + if (!handler) { + handler = &xs_unimpl; + } + + handler(s, req->req_id, req->tx_id, (uint8_t *)&req[1], req->len); + + s->rsp_pending = true; + reset_req(s); +} + +static unsigned int copy_from_ring(XenXenstoreState *s, uint8_t *ptr, + unsigned int len) +{ + if (!len) { + return 0; + } + + XENSTORE_RING_IDX prod = qatomic_read(&s->xs->req_prod); + XENSTORE_RING_IDX cons = qatomic_read(&s->xs->req_cons); + unsigned int copied = 0; + + /* Ensure the ring contents don't cross the req_prod access. */ + smp_rmb(); + + while (len) { + unsigned int avail = prod - cons; + unsigned int offset = MASK_XENSTORE_IDX(cons); + unsigned int copylen = avail; + + if (avail > XENSTORE_RING_SIZE) { + error_report("XenStore ring handling error"); + s->fatal_error = true; + break; + } else if (avail == 0) { + break; + } + + if (copylen > len) { + copylen = len; + } + if (copylen > XENSTORE_RING_SIZE - offset) { + copylen = XENSTORE_RING_SIZE - offset; + } + + memcpy(ptr, &s->xs->req[offset], copylen); + copied += copylen; + + ptr += copylen; + len -= copylen; + + cons += copylen; + } + + /* + * Not sure this ever mattered except on Alpha, but this barrier + * is to ensure that the update to req_cons is globally visible + * only after we have consumed all the data from the ring, and we + * don't end up seeing data written to the ring *after* the other + * end sees the update and writes more to the ring. Xen's own + * xenstored has the same barrier here (although with no comment + * at all, obviously, because it's Xen code). + */ + smp_mb(); + + qatomic_set(&s->xs->req_cons, cons); + + return copied; +} + +static unsigned int copy_to_ring(XenXenstoreState *s, uint8_t *ptr, + unsigned int len) +{ + if (!len) { + return 0; + } + + XENSTORE_RING_IDX cons = qatomic_read(&s->xs->rsp_cons); + XENSTORE_RING_IDX prod = qatomic_read(&s->xs->rsp_prod); + unsigned int copied = 0; + + /* + * This matches the barrier in copy_to_ring() (or the guest's + * equivalent) between writing the data to the ring and updating + * rsp_prod. It protects against the pathological case (which + * again I think never happened except on Alpha) where our + * subsequent writes to the ring could *cross* the read of + * rsp_cons and the guest could see the new data when it was + * intending to read the old. + */ + smp_mb(); + + while (len) { + unsigned int avail = cons + XENSTORE_RING_SIZE - prod; + unsigned int offset = MASK_XENSTORE_IDX(prod); + unsigned int copylen = len; + + if (avail > XENSTORE_RING_SIZE) { + error_report("XenStore ring handling error"); + s->fatal_error = true; + break; + } else if (avail == 0) { + break; + } + + if (copylen > avail) { + copylen = avail; + } + if (copylen > XENSTORE_RING_SIZE - offset) { + copylen = XENSTORE_RING_SIZE - offset; + } + + + memcpy(&s->xs->rsp[offset], ptr, copylen); + copied += copylen; + + ptr += copylen; + len -= copylen; + + prod += copylen; + } + + /* Ensure the ring contents are seen before rsp_prod update. */ + smp_wmb(); + + qatomic_set(&s->xs->rsp_prod, prod); + + return copied; +} + +static unsigned int get_req(XenXenstoreState *s) +{ + unsigned int copied = 0; + + if (s->fatal_error) { + return 0; + } + + assert(!req_pending(s)); + + if (s->req_offset < XENSTORE_HEADER_SIZE) { + void *ptr = s->req_data + s->req_offset; + unsigned int len = XENSTORE_HEADER_SIZE; + unsigned int copylen = copy_from_ring(s, ptr, len); + + copied += copylen; + s->req_offset += copylen; + } + + if (s->req_offset >= XENSTORE_HEADER_SIZE) { + struct xsd_sockmsg *req = (struct xsd_sockmsg *)s->req_data; + + if (req->len > (uint32_t)XENSTORE_PAYLOAD_MAX) { + error_report("Illegal XenStore request"); + s->fatal_error = true; + return 0; + } + + void *ptr = s->req_data + s->req_offset; + unsigned int len = XENSTORE_HEADER_SIZE + req->len - s->req_offset; + unsigned int copylen = copy_from_ring(s, ptr, len); + + copied += copylen; + s->req_offset += copylen; + } + + return copied; +} + +static unsigned int put_rsp(XenXenstoreState *s) +{ + if (s->fatal_error) { + return 0; + } + + assert(s->rsp_pending); + + struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data; + assert(s->rsp_offset < XENSTORE_HEADER_SIZE + rsp->len); + + void *ptr = s->rsp_data + s->rsp_offset; + unsigned int len = XENSTORE_HEADER_SIZE + rsp->len - s->rsp_offset; + unsigned int copylen = copy_to_ring(s, ptr, len); + + s->rsp_offset += copylen; + + /* Have we produced a complete response? */ + if (s->rsp_offset == XENSTORE_HEADER_SIZE + rsp->len) { + reset_rsp(s); + } + + return copylen; +} + +static void deliver_watch(XenXenstoreState *s, const char *path, + const char *token) +{ + struct xsd_sockmsg *rsp = (struct xsd_sockmsg *)s->rsp_data; + uint8_t *rsp_data = (uint8_t *)&rsp[1]; + unsigned int len; + + assert(!s->rsp_pending); + + trace_xenstore_watch_event(path, token); + + rsp->type = XS_WATCH_EVENT; + rsp->req_id = 0; + rsp->tx_id = 0; + rsp->len = 0; + + len = strlen(path); + + /* XENSTORE_ABS/REL_PATH_MAX should ensure there can be no overflow */ + assert(rsp->len + len < XENSTORE_PAYLOAD_MAX); + + memcpy(&rsp_data[rsp->len], path, len); + rsp->len += len; + rsp_data[rsp->len] = '\0'; + rsp->len++; + + len = strlen(token); + /* + * It is possible for the guest to have chosen a token that will + * not fit (along with the patch) into a watch event. We have no + * choice but to drop the event if this is the case. + */ + if (rsp->len + len >= XENSTORE_PAYLOAD_MAX) { + return; + } + + memcpy(&rsp_data[rsp->len], token, len); + rsp->len += len; + rsp_data[rsp->len] = '\0'; + rsp->len++; + + s->rsp_pending = true; +} + +struct watch_event { + char *path; + char *token; +}; + +static void free_watch_event(struct watch_event *ev) +{ + if (ev) { + g_free(ev->path); + g_free(ev->token); + g_free(ev); + } +} + +static void queue_watch(XenXenstoreState *s, const char *path, + const char *token) +{ + struct watch_event *ev = g_new0(struct watch_event, 1); + + ev->path = g_strdup(path); + ev->token = g_strdup(token); + + s->watch_events = g_list_append(s->watch_events, ev); +} + +static void fire_watch_cb(void *opaque, const char *path, const char *token) +{ + XenXenstoreState *s = opaque; + + assert(bql_locked()); + + /* + * If there's a response pending, we obviously can't scribble over + * it. But if there's a request pending, it has dibs on the buffer + * too. + * + * In the common case of a watch firing due to backend activity + * when the ring was otherwise idle, we should be able to copy the + * strings directly into the rsp_data and thence the actual ring, + * without needing to perform any allocations and queue them. + */ + if (s->rsp_pending || req_pending(s)) { + queue_watch(s, path, token); + } else { + deliver_watch(s, path, token); + /* + * Attempt to queue the message into the actual ring, and send + * the event channel notification if any bytes are copied. + */ + if (s->rsp_pending && put_rsp(s) > 0) { + xen_be_evtchn_notify(s->eh, s->be_port); + } + } +} + +static void process_watch_events(XenXenstoreState *s) +{ + struct watch_event *ev = s->watch_events->data; + + deliver_watch(s, ev->path, ev->token); + + s->watch_events = g_list_remove(s->watch_events, ev); + free_watch_event(ev); +} + +static void xen_xenstore_event(void *opaque) +{ + XenXenstoreState *s = opaque; + evtchn_port_t port = xen_be_evtchn_pending(s->eh); + unsigned int copied_to, copied_from; + bool processed, notify = false; + + if (port != s->be_port) { + return; + } + + /* We know this is a no-op. */ + xen_be_evtchn_unmask(s->eh, port); + + do { + copied_to = copied_from = 0; + processed = false; + + if (!s->rsp_pending && s->watch_events) { + process_watch_events(s); + } + + if (s->rsp_pending) { + copied_to = put_rsp(s); + } + + if (!req_pending(s)) { + copied_from = get_req(s); + } + + if (req_pending(s) && !s->rsp_pending && !s->watch_events) { + process_req(s); + processed = true; + } + + notify |= copied_to || copied_from; + } while (copied_to || copied_from || processed); + + if (notify) { + xen_be_evtchn_notify(s->eh, s->be_port); + } +} + +static void alloc_guest_port(XenXenstoreState *s) +{ + struct evtchn_alloc_unbound alloc = { + .dom = DOMID_SELF, + .remote_dom = DOMID_QEMU, + }; + + if (!xen_evtchn_alloc_unbound_op(&alloc)) { + s->guest_port = alloc.port; + } +} + +int xen_xenstore_reset(void) +{ + XenXenstoreState *s = xen_xenstore_singleton; + int console_port; + GList *perms; + int err; + + if (!s) { + return -ENOTSUP; + } + + s->req_offset = s->rsp_offset = 0; + s->rsp_pending = false; + + if (!memory_region_is_mapped(&s->xenstore_page)) { + uint64_t gpa = XEN_SPECIAL_PFN(XENSTORE) << TARGET_PAGE_BITS; + xen_overlay_do_map_page(&s->xenstore_page, gpa); + } + + alloc_guest_port(s); + + /* + * As qemu/dom0, bind to the guest's port. For incoming migration, this + * will be unbound as the guest's evtchn table is overwritten. We then + * rebind to the correct guest port in xen_xenstore_post_load(). + */ + err = xen_be_evtchn_bind_interdomain(s->eh, xen_domid, s->guest_port); + if (err < 0) { + return err; + } + s->be_port = err; + + /* Create frontend store nodes */ + perms = g_list_append(NULL, xs_perm_as_string(XS_PERM_NONE, DOMID_QEMU)); + perms = g_list_append(perms, xs_perm_as_string(XS_PERM_READ, xen_domid)); + + relpath_printf(s, perms, "store/port", "%u", s->guest_port); + relpath_printf(s, perms, "store/ring-ref", "%lu", + XEN_SPECIAL_PFN(XENSTORE)); + + console_port = xen_primary_console_get_port(); + if (console_port) { + relpath_printf(s, perms, "console/ring-ref", "%lu", + XEN_SPECIAL_PFN(CONSOLE)); + relpath_printf(s, perms, "console/port", "%u", console_port); + relpath_printf(s, perms, "console/state", "%u", XenbusStateInitialised); + } + + g_list_free_full(perms, g_free); + + /* + * We don't actually access the guest's page through the grant, because + * this isn't real Xen, and we can just use the page we gave it in the + * first place. Map the grant anyway, mostly for cosmetic purposes so + * it *looks* like it's in use in the guest-visible grant table. + */ + s->gt = qemu_xen_gnttab_open(); + uint32_t xs_gntref = GNTTAB_RESERVED_XENSTORE; + s->granted_xs = qemu_xen_gnttab_map_refs(s->gt, 1, xen_domid, &xs_gntref, + PROT_READ | PROT_WRITE); + + return 0; +} + +struct qemu_xs_handle { + XenstoreImplState *impl; + GList *watches; + QEMUBH *watch_bh; +}; + +struct qemu_xs_watch { + struct qemu_xs_handle *h; + char *path; + xs_watch_fn fn; + void *opaque; + GList *events; +}; + +static char *xs_be_get_domain_path(struct qemu_xs_handle *h, unsigned int domid) +{ + return g_strdup_printf("/local/domain/%u", domid); +} + +static char **xs_be_directory(struct qemu_xs_handle *h, xs_transaction_t t, + const char *path, unsigned int *num) +{ + GList *items = NULL, *l; + unsigned int i = 0; + char **items_ret; + int err; + + err = xs_impl_directory(h->impl, DOMID_QEMU, t, path, NULL, &items); + if (err) { + errno = err; + return NULL; + } + + items_ret = g_new0(char *, g_list_length(items) + 1); + *num = 0; + for (l = items; l; l = l->next) { + items_ret[i++] = l->data; + (*num)++; + } + g_list_free(items); + return items_ret; +} + +static void *xs_be_read(struct qemu_xs_handle *h, xs_transaction_t t, + const char *path, unsigned int *len) +{ + GByteArray *data = g_byte_array_new(); + bool free_segment = false; + int err; + + err = xs_impl_read(h->impl, DOMID_QEMU, t, path, data); + if (err) { + free_segment = true; + errno = err; + } else { + if (len) { + *len = data->len; + } + /* The xen-bus-helper code expects to get NUL terminated string! */ + g_byte_array_append(data, (void *)"", 1); + } + + return g_byte_array_free(data, free_segment); +} + +static bool xs_be_write(struct qemu_xs_handle *h, xs_transaction_t t, + const char *path, const void *data, unsigned int len) +{ + GByteArray *gdata = g_byte_array_new(); + int err; + + g_byte_array_append(gdata, data, len); + err = xs_impl_write(h->impl, DOMID_QEMU, t, path, gdata); + g_byte_array_unref(gdata); + if (err) { + errno = err; + return false; + } + return true; +} + +static bool xs_be_create(struct qemu_xs_handle *h, xs_transaction_t t, + unsigned int owner, unsigned int domid, + unsigned int perms, const char *path) +{ + g_autoptr(GByteArray) data = g_byte_array_new(); + GList *perms_list = NULL; + int err; + + /* mkdir does this */ + err = xs_impl_read(h->impl, DOMID_QEMU, t, path, data); + if (err == ENOENT) { + err = xs_impl_write(h->impl, DOMID_QEMU, t, path, data); + } + if (err) { + errno = err; + return false; + } + + perms_list = g_list_append(perms_list, + xs_perm_as_string(XS_PERM_NONE, owner)); + perms_list = g_list_append(perms_list, + xs_perm_as_string(perms, domid)); + + err = xs_impl_set_perms(h->impl, DOMID_QEMU, t, path, perms_list); + g_list_free_full(perms_list, g_free); + if (err) { + errno = err; + return false; + } + return true; +} + +static bool xs_be_destroy(struct qemu_xs_handle *h, xs_transaction_t t, + const char *path) +{ + int err = xs_impl_rm(h->impl, DOMID_QEMU, t, path); + if (err) { + errno = err; + return false; + } + return true; +} + +static void be_watch_bh(void *_h) +{ + struct qemu_xs_handle *h = _h; + GList *l; + + for (l = h->watches; l; l = l->next) { + struct qemu_xs_watch *w = l->data; + + while (w->events) { + struct watch_event *ev = w->events->data; + + w->fn(w->opaque, ev->path); + + w->events = g_list_remove(w->events, ev); + free_watch_event(ev); + } + } +} + +static void xs_be_watch_cb(void *opaque, const char *path, const char *token) +{ + struct watch_event *ev = g_new0(struct watch_event, 1); + struct qemu_xs_watch *w = opaque; + + /* We don't care about the token */ + ev->path = g_strdup(path); + w->events = g_list_append(w->events, ev); + + qemu_bh_schedule(w->h->watch_bh); +} + +static struct qemu_xs_watch *xs_be_watch(struct qemu_xs_handle *h, + const char *path, xs_watch_fn fn, + void *opaque) +{ + struct qemu_xs_watch *w = g_new0(struct qemu_xs_watch, 1); + int err; + + w->h = h; + w->fn = fn; + w->opaque = opaque; + + err = xs_impl_watch(h->impl, DOMID_QEMU, path, NULL, xs_be_watch_cb, w); + if (err) { + errno = err; + g_free(w); + return NULL; + } + + w->path = g_strdup(path); + h->watches = g_list_append(h->watches, w); + return w; +} + +static void xs_be_unwatch(struct qemu_xs_handle *h, struct qemu_xs_watch *w) +{ + xs_impl_unwatch(h->impl, DOMID_QEMU, w->path, NULL, xs_be_watch_cb, w); + + h->watches = g_list_remove(h->watches, w); + g_list_free_full(w->events, (GDestroyNotify)free_watch_event); + g_free(w->path); + g_free(w); +} + +static xs_transaction_t xs_be_transaction_start(struct qemu_xs_handle *h) +{ + unsigned int new_tx = XBT_NULL; + int err = xs_impl_transaction_start(h->impl, DOMID_QEMU, &new_tx); + if (err) { + errno = err; + return XBT_NULL; + } + return new_tx; +} + +static bool xs_be_transaction_end(struct qemu_xs_handle *h, xs_transaction_t t, + bool abort) +{ + int err = xs_impl_transaction_end(h->impl, DOMID_QEMU, t, !abort); + if (err) { + errno = err; + return false; + } + return true; +} + +static struct qemu_xs_handle *xs_be_open(void) +{ + XenXenstoreState *s = xen_xenstore_singleton; + struct qemu_xs_handle *h; + + if (!s || !s->impl) { + errno = -ENOSYS; + return NULL; + } + + h = g_new0(struct qemu_xs_handle, 1); + h->impl = s->impl; + + h->watch_bh = aio_bh_new(qemu_get_aio_context(), be_watch_bh, h); + + return h; +} + +static void xs_be_close(struct qemu_xs_handle *h) +{ + while (h->watches) { + struct qemu_xs_watch *w = h->watches->data; + xs_be_unwatch(h, w); + } + + qemu_bh_delete(h->watch_bh); + g_free(h); +} + +static struct xenstore_backend_ops emu_xenstore_backend_ops = { + .open = xs_be_open, + .close = xs_be_close, + .get_domain_path = xs_be_get_domain_path, + .directory = xs_be_directory, + .read = xs_be_read, + .write = xs_be_write, + .create = xs_be_create, + .destroy = xs_be_destroy, + .watch = xs_be_watch, + .unwatch = xs_be_unwatch, + .transaction_start = xs_be_transaction_start, + .transaction_end = xs_be_transaction_end, +}; diff --git a/hw/i386/kvm/xen_xenstore.h b/hw/i386/kvm/xen_xenstore.h new file mode 100644 index 0000000000..8c3768e075 --- /dev/null +++ b/hw/i386/kvm/xen_xenstore.h @@ -0,0 +1,20 @@ +/* + * QEMU Xen emulation: Xenstore emulation + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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_XEN_XENSTORE_H +#define QEMU_XEN_XENSTORE_H + +void xen_xenstore_create(void); +int xen_xenstore_reset(void); + +uint16_t xen_xenstore_get_port(void); + +#endif /* QEMU_XEN_XENSTORE_H */ diff --git a/hw/i386/kvm/xenstore_impl.c b/hw/i386/kvm/xenstore_impl.c new file mode 100644 index 0000000000..1d134a6866 --- /dev/null +++ b/hw/i386/kvm/xenstore_impl.c @@ -0,0 +1,1937 @@ +/* + * QEMU Xen emulation: The actual implementation of XenStore + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse , Paul Durrant + * + * 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 "qom/object.h" + +#include "hw/xen/xen.h" + +#include "xen_xenstore.h" +#include "xenstore_impl.h" + +#include "hw/xen/interface/io/xs_wire.h" + +#define XS_MAX_WATCHES 128 +#define XS_MAX_DOMAIN_NODES 1000 +#define XS_MAX_NODE_SIZE 2048 +#define XS_MAX_TRANSACTIONS 10 +#define XS_MAX_PERMS_PER_NODE 5 + +#define XS_VALID_CHARS "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "0123456789-/_" + +typedef struct XsNode { + uint32_t ref; + GByteArray *content; + GList *perms; + GHashTable *children; + uint64_t gencnt; + bool deleted_in_tx; + bool modified_in_tx; + unsigned int serialized_tx; +#ifdef XS_NODE_UNIT_TEST + gchar *name; /* debug only */ +#endif +} XsNode; + +typedef struct XsWatch { + struct XsWatch *next; + xs_impl_watch_fn *cb; + void *cb_opaque; + char *token; + unsigned int dom_id; + int rel_prefix; +} XsWatch; + +typedef struct XsTransaction { + XsNode *root; + unsigned int nr_nodes; + unsigned int base_tx; + unsigned int tx_id; + unsigned int dom_id; +} XsTransaction; + +struct XenstoreImplState { + XsNode *root; + unsigned int nr_nodes; + GHashTable *watches; + unsigned int nr_domu_watches; + GHashTable *transactions; + unsigned int nr_domu_transactions; + unsigned int root_tx; + unsigned int last_tx; + bool serialized; +}; + + +static void nobble_tx(gpointer key, gpointer value, gpointer user_data) +{ + unsigned int *new_tx_id = user_data; + XsTransaction *tx = value; + + if (tx->base_tx == *new_tx_id) { + /* Transactions based on XBT_NULL will always fail */ + tx->base_tx = XBT_NULL; + } +} + +static inline unsigned int next_tx(struct XenstoreImplState *s) +{ + unsigned int tx_id; + + /* Find the next TX id which isn't either XBT_NULL or in use. */ + do { + tx_id = ++s->last_tx; + } while (tx_id == XBT_NULL || tx_id == s->root_tx || + g_hash_table_lookup(s->transactions, GINT_TO_POINTER(tx_id))); + + /* + * It is vanishingly unlikely, but ensure that no outstanding transaction + * is based on the (previous incarnation of the) newly-allocated TX id. + */ + g_hash_table_foreach(s->transactions, nobble_tx, &tx_id); + + return tx_id; +} + +static inline XsNode *xs_node_new(void) +{ + XsNode *n = g_new0(XsNode, 1); + n->ref = 1; + +#ifdef XS_NODE_UNIT_TEST + nr_xs_nodes++; + xs_node_list = g_list_prepend(xs_node_list, n); +#endif + return n; +} + +static inline XsNode *xs_node_ref(XsNode *n) +{ + /* With just 10 transactions, it can never get anywhere near this. */ + g_assert(n->ref < INT_MAX); + + g_assert(n->ref); + n->ref++; + return n; +} + +static inline void xs_node_unref(XsNode *n) +{ + if (!n) { + return; + } + g_assert(n->ref); + if (--n->ref) { + return; + } + + if (n->content) { + g_byte_array_unref(n->content); + } + if (n->perms) { + g_list_free_full(n->perms, g_free); + } + if (n->children) { + g_hash_table_unref(n->children); + } +#ifdef XS_NODE_UNIT_TEST + g_free(n->name); + nr_xs_nodes--; + xs_node_list = g_list_remove(xs_node_list, n); +#endif + g_free(n); +} + +char *xs_perm_as_string(unsigned int perm, unsigned int domid) +{ + char letter; + + switch (perm) { + case XS_PERM_READ | XS_PERM_WRITE: + letter = 'b'; + break; + case XS_PERM_READ: + letter = 'r'; + break; + case XS_PERM_WRITE: + letter = 'w'; + break; + case XS_PERM_NONE: + default: + letter = 'n'; + break; + } + + return g_strdup_printf("%c%u", letter, domid); +} + +static gpointer do_perm_copy(gconstpointer src, gpointer user_data) +{ + return g_strdup(src); +} + +static XsNode *xs_node_create(const char *name, GList *perms) +{ + XsNode *n = xs_node_new(); + +#ifdef XS_NODE_UNIT_TEST + if (name) { + n->name = g_strdup(name); + } +#endif + + n->perms = g_list_copy_deep(perms, do_perm_copy, NULL); + + return n; +} + +/* For copying from one hash table to another using g_hash_table_foreach() */ +static void do_child_insert(gpointer key, gpointer value, gpointer user_data) +{ + g_hash_table_insert(user_data, g_strdup(key), xs_node_ref(value)); +} + +static XsNode *xs_node_copy(XsNode *old) +{ + XsNode *n = xs_node_new(); + + n->gencnt = old->gencnt; + +#ifdef XS_NODE_UNIT_TEST + if (n->name) { + n->name = g_strdup(old->name); + } +#endif + + assert(old); + if (old->children) { + n->children = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)xs_node_unref); + g_hash_table_foreach(old->children, do_child_insert, n->children); + } + if (old->perms) { + n->perms = g_list_copy_deep(old->perms, do_perm_copy, NULL); + } + if (old->content) { + n->content = g_byte_array_ref(old->content); + } + return n; +} + +/* Returns true if it made a change to the hash table */ +static bool xs_node_add_child(XsNode *n, const char *path_elem, XsNode *child) +{ + assert(!strchr(path_elem, '/')); + + if (!child) { + assert(n->children); + return g_hash_table_remove(n->children, path_elem); + } + +#ifdef XS_NODE_UNIT_TEST + g_free(child->name); + child->name = g_strdup(path_elem); +#endif + if (!n->children) { + n->children = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)xs_node_unref); + } + + /* + * The documentation for g_hash_table_insert() says that it "returns a + * boolean value to indicate whether the newly added value was already + * in the hash table or not." + * + * It could perhaps be clearer that returning TRUE means it wasn't, + */ + return g_hash_table_insert(n->children, g_strdup(path_elem), child); +} + +struct walk_op { + struct XenstoreImplState *s; + char path[XENSTORE_ABS_PATH_MAX + 2]; /* Two NUL terminators */ + int (*op_fn)(XsNode **n, struct walk_op *op); + void *op_opaque; + void *op_opaque2; + + GList *watches; + unsigned int dom_id; + unsigned int tx_id; + + /* The number of nodes which will exist in the tree if this op succeeds. */ + unsigned int new_nr_nodes; + + /* + * This is maintained on the way *down* the walk to indicate + * whether nodes can be modified in place or whether COW is + * required. It starts off being true, as we're always going to + * replace the root node. If we walk into a shared subtree it + * becomes false. If we start *creating* new nodes for a write, + * it becomes true again. + * + * Do not use it on the way back up. + */ + bool inplace; + bool mutating; + bool create_dirs; + bool in_transaction; + + /* Tracking during recursion so we know which is first. */ + bool deleted_in_tx; +}; + +static void fire_watches(struct walk_op *op, bool parents) +{ + GList *l = NULL; + XsWatch *w; + + if (!op->mutating || op->in_transaction) { + return; + } + + if (parents) { + l = op->watches; + } + + w = g_hash_table_lookup(op->s->watches, op->path); + while (w || l) { + if (!w) { + /* Fire the parent nodes from 'op' if asked to */ + w = l->data; + l = l->next; + continue; + } + + assert(strlen(op->path) > w->rel_prefix); + w->cb(w->cb_opaque, op->path + w->rel_prefix, w->token); + + w = w->next; + } +} + +static int xs_node_add_content(XsNode **n, struct walk_op *op) +{ + GByteArray *data = op->op_opaque; + + if (op->dom_id) { + /* + * The real XenStored includes permissions and names of child nodes + * in the calculated datasize but life's too short. For a single + * tenant internal XenStore, we don't have to be quite as pedantic. + */ + if (data->len > XS_MAX_NODE_SIZE) { + return E2BIG; + } + } + /* We *are* the node to be written. Either this or a copy. */ + if (!op->inplace) { + XsNode *old = *n; + *n = xs_node_copy(old); + xs_node_unref(old); + } + + if ((*n)->content) { + g_byte_array_unref((*n)->content); + } + (*n)->content = g_byte_array_ref(data); + if (op->tx_id != XBT_NULL) { + (*n)->modified_in_tx = true; + } + return 0; +} + +static int xs_node_get_content(XsNode **n, struct walk_op *op) +{ + GByteArray *data = op->op_opaque; + GByteArray *node_data; + + assert(op->inplace); + assert(*n); + + node_data = (*n)->content; + if (node_data) { + g_byte_array_append(data, node_data->data, node_data->len); + } + + return 0; +} + +static int node_rm_recurse(gpointer key, gpointer value, gpointer user_data) +{ + struct walk_op *op = user_data; + int path_len = strlen(op->path); + int key_len = strlen(key); + XsNode *n = value; + bool this_inplace = op->inplace; + + if (n->ref != 1) { + op->inplace = 0; + } + + assert(key_len + path_len + 2 <= sizeof(op->path)); + op->path[path_len] = '/'; + memcpy(op->path + path_len + 1, key, key_len + 1); + + if (n->children) { + g_hash_table_foreach_remove(n->children, node_rm_recurse, op); + } + op->new_nr_nodes--; + + /* + * Fire watches on *this* node but not the parents because they are + * going to be deleted too, so the watch will fire for them anyway. + */ + fire_watches(op, false); + op->path[path_len] = '\0'; + + /* + * Actually deleting the child here is just an optimisation; if we + * don't then the final unref on the topmost victim will just have + * to cascade down again repeating all the g_hash_table_foreach() + * calls. + */ + return this_inplace; +} + +static XsNode *xs_node_copy_deleted(XsNode *old, struct walk_op *op); +static void copy_deleted_recurse(gpointer key, gpointer value, + gpointer user_data) +{ + struct walk_op *op = user_data; + GHashTable *siblings = op->op_opaque2; + XsNode *n = xs_node_copy_deleted(value, op); + + /* + * Reinsert the deleted_in_tx copy of the node into the parent's + * 'children' hash table. Having stashed it from op->op_opaque2 + * before the recursive call to xs_node_copy_deleted() scribbled + * over it. + */ + g_hash_table_insert(siblings, g_strdup(key), n); +} + +static XsNode *xs_node_copy_deleted(XsNode *old, struct walk_op *op) +{ + XsNode *n = xs_node_new(); + + n->gencnt = old->gencnt; + +#ifdef XS_NODE_UNIT_TEST + if (old->name) { + n->name = g_strdup(old->name); + } +#endif + + if (old->children) { + n->children = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)xs_node_unref); + op->op_opaque2 = n->children; + g_hash_table_foreach(old->children, copy_deleted_recurse, op); + } + if (old->perms) { + n->perms = g_list_copy_deep(old->perms, do_perm_copy, NULL); + } + n->deleted_in_tx = true; + /* If it gets resurrected we only fire a watch if it lost its content */ + if (old->content) { + n->modified_in_tx = true; + } + op->new_nr_nodes--; + return n; +} + +static int xs_node_rm(XsNode **n, struct walk_op *op) +{ + bool this_inplace = op->inplace; + + if (op->tx_id != XBT_NULL) { + /* It's not trivial to do inplace handling for this one */ + XsNode *old = *n; + *n = xs_node_copy_deleted(old, op); + xs_node_unref(old); + return 0; + } + + /* Fire watches for, and count, nodes in the subtree which get deleted */ + if ((*n)->children) { + g_hash_table_foreach_remove((*n)->children, node_rm_recurse, op); + } + op->new_nr_nodes--; + + if (this_inplace) { + xs_node_unref(*n); + } + *n = NULL; + return 0; +} + +static int xs_node_get_perms(XsNode **n, struct walk_op *op) +{ + GList **perms = op->op_opaque; + + assert(op->inplace); + assert(*n); + + *perms = g_list_copy_deep((*n)->perms, do_perm_copy, NULL); + return 0; +} + +static void parse_perm(const char *perm, char *letter, unsigned int *dom_id) +{ + unsigned int n = sscanf(perm, "%c%u", letter, dom_id); + + assert(n == 2); +} + +static bool can_access(unsigned int dom_id, GList *perms, const char *letters) +{ + unsigned int i, n; + char perm_letter; + unsigned int perm_dom_id; + bool access; + + if (dom_id == 0) { + return true; + } + + n = g_list_length(perms); + assert(n >= 1); + + /* + * The dom_id of the first perm is the owner, and the owner always has + * read-write access. + */ + parse_perm(g_list_nth_data(perms, 0), &perm_letter, &perm_dom_id); + if (dom_id == perm_dom_id) { + return true; + } + + /* + * The letter of the first perm specified the default access for all other + * domains. + */ + access = !!strchr(letters, perm_letter); + for (i = 1; i < n; i++) { + parse_perm(g_list_nth_data(perms, i), &perm_letter, &perm_dom_id); + if (dom_id != perm_dom_id) { + continue; + } + access = !!strchr(letters, perm_letter); + } + + return access; +} + +static int xs_node_set_perms(XsNode **n, struct walk_op *op) +{ + GList *perms = op->op_opaque; + + if (op->dom_id) { + unsigned int perm_dom_id; + char perm_letter; + + /* A guest may not change permissions on nodes it does not own */ + if (!can_access(op->dom_id, (*n)->perms, "")) { + return EPERM; + } + + /* A guest may not change the owner of a node it owns. */ + parse_perm(perms->data, &perm_letter, &perm_dom_id); + if (perm_dom_id != op->dom_id) { + return EPERM; + } + + if (g_list_length(perms) > XS_MAX_PERMS_PER_NODE) { + return ENOSPC; + } + } + + /* We *are* the node to be written. Either this or a copy. */ + if (!op->inplace) { + XsNode *old = *n; + *n = xs_node_copy(old); + xs_node_unref(old); + } + + if ((*n)->perms) { + g_list_free_full((*n)->perms, g_free); + } + (*n)->perms = g_list_copy_deep(perms, do_perm_copy, NULL); + if (op->tx_id != XBT_NULL) { + (*n)->modified_in_tx = true; + } + return 0; +} + +/* + * Passed a full reference in *n which it may free if it needs to COW. + * + * When changing the tree, the op->inplace flag indicates whether this + * node may be modified in place (i.e. it and all its parents had a + * refcount of one). If walking down the tree we find a node whose + * refcount is higher, we must clear op->inplace and COW from there + * down. Unless we are creating new nodes as scaffolding for a write + * (which works like 'mkdir -p' does). In which case those newly + * created nodes can (and must) be modified in place again. + */ +static int xs_node_walk(XsNode **n, struct walk_op *op) +{ + char *child_name = NULL; + size_t namelen; + XsNode *old = *n, *child = NULL; + bool stole_child = false; + bool this_inplace; + XsWatch *watch; + int err; + + namelen = strlen(op->path); + watch = g_hash_table_lookup(op->s->watches, op->path); + + /* Is there a child, or do we hit the double-NUL termination? */ + if (op->path[namelen + 1]) { + char *slash; + child_name = op->path + namelen + 1; + slash = strchr(child_name, '/'); + if (slash) { + *slash = '\0'; + } + op->path[namelen] = '/'; + } + + /* If we walk into a subtree which is shared, we must COW */ + if (op->mutating && old->ref != 1) { + op->inplace = false; + } + + if (!child_name) { + const char *letters = op->mutating ? "wb" : "rb"; + + if (!can_access(op->dom_id, old->perms, letters)) { + err = EACCES; + goto out; + } + + /* This is the actual node on which the operation shall be performed */ + err = op->op_fn(n, op); + if (!err) { + fire_watches(op, true); + } + goto out; + } + + /* op->inplace will be further modified during the recursion */ + this_inplace = op->inplace; + + if (old && old->children) { + child = g_hash_table_lookup(old->children, child_name); + /* This is a *weak* reference to 'child', owned by the hash table */ + } + + if (child) { + if (child->deleted_in_tx) { + assert(child->ref == 1); + /* Cannot actually set child->deleted_in_tx = false until later */ + } + xs_node_ref(child); + /* + * Now we own it too. But if we can modify inplace, that's going to + * foil the check and force it to COW. We want to be the *only* owner + * so that it can be modified in place, so remove it from the hash + * table in that case. We'll add it (or its replacement) back later. + */ + if (op->mutating && this_inplace) { + g_hash_table_remove(old->children, child_name); + stole_child = true; + } + } else if (op->create_dirs) { + assert(op->mutating); + + if (!can_access(op->dom_id, old->perms, "wb")) { + err = EACCES; + goto out; + } + + if (op->dom_id && op->new_nr_nodes >= XS_MAX_DOMAIN_NODES) { + err = ENOSPC; + goto out; + } + + child = xs_node_create(child_name, old->perms); + op->new_nr_nodes++; + + /* + * If we're creating a new child, we can clearly modify it (and its + * children) in place from here on down. + */ + op->inplace = true; + } else { + err = ENOENT; + goto out; + } + + /* + * If there's a watch on this node, add it to the list to be fired + * (with the correct full pathname for the modified node) at the end. + */ + if (watch) { + op->watches = g_list_append(op->watches, watch); + } + + /* + * Except for the temporary child-stealing as noted, our node has not + * changed yet. We don't yet know the overall operation will complete. + */ + err = xs_node_walk(&child, op); + + if (watch) { + op->watches = g_list_remove(op->watches, watch); + } + + if (err || !op->mutating) { + if (stole_child) { + /* Put it back as it was. */ + g_hash_table_replace(old->children, g_strdup(child_name), child); + } else { + xs_node_unref(child); + } + goto out; + } + + /* + * Now we know the operation has completed successfully and we're on + * the way back up. Make the change, substituting 'child' in the + * node at our level. + */ + if (!this_inplace) { + *n = xs_node_copy(old); + xs_node_unref(old); + } + + /* + * If we resurrected a deleted_in_tx node, we can mark it as no longer + * deleted now that we know the overall operation has succeeded. + */ + if (op->create_dirs && child && child->deleted_in_tx) { + op->new_nr_nodes++; + child->deleted_in_tx = false; + } + + /* + * The child may be NULL here, for a remove operation. Either way, + * xs_node_add_child() will do the right thing and return a value + * indicating whether it changed the parent's hash table or not. + * + * We bump the parent gencnt if it adds a child that we *didn't* + * steal from it in the first place, or if child==NULL and was + * thus removed (whether we stole it earlier and didn't put it + * back, or xs_node_add_child() actually removed it now). + */ + if ((xs_node_add_child(*n, child_name, child) && !stole_child) || !child) { + (*n)->gencnt++; + } + + out: + op->path[namelen] = '\0'; + if (!namelen) { + assert(!op->watches); + /* + * On completing the recursion back up the path walk and reaching the + * top, assign the new node count if the operation was successful. If + * the main tree was changed, bump its tx ID so that outstanding + * transactions correctly fail. But don't bump it every time; only + * if it makes a difference. + */ + if (!err && op->mutating) { + if (!op->in_transaction) { + if (op->s->root_tx != op->s->last_tx) { + op->s->root_tx = next_tx(op->s); + } + op->s->nr_nodes = op->new_nr_nodes; + } else { + XsTransaction *tx = g_hash_table_lookup(op->s->transactions, + GINT_TO_POINTER(op->tx_id)); + assert(tx); + tx->nr_nodes = op->new_nr_nodes; + } + } + } + return err; +} + +static void append_directory_item(gpointer key, gpointer value, + gpointer user_data) +{ + GList **items = user_data; + + *items = g_list_insert_sorted(*items, g_strdup(key), (GCompareFunc)strcmp); +} + +/* Populates items with char * names which caller must free. */ +static int xs_node_directory(XsNode **n, struct walk_op *op) +{ + GList **items = op->op_opaque; + + assert(op->inplace); + assert(*n); + + if ((*n)->children) { + g_hash_table_foreach((*n)->children, append_directory_item, items); + } + + if (op->op_opaque2) { + *(uint64_t *)op->op_opaque2 = (*n)->gencnt; + } + + return 0; +} + +static int validate_path(char *outpath, const char *userpath, + unsigned int dom_id) +{ + size_t i, pathlen = strlen(userpath); + + if (!pathlen || userpath[pathlen] == '/' || strstr(userpath, "//")) { + return EINVAL; + } + for (i = 0; i < pathlen; i++) { + if (!strchr(XS_VALID_CHARS, userpath[i])) { + return EINVAL; + } + } + if (userpath[0] == '/') { + if (pathlen > XENSTORE_ABS_PATH_MAX) { + return E2BIG; + } + memcpy(outpath, userpath, pathlen + 1); + } else { + if (pathlen > XENSTORE_REL_PATH_MAX) { + return E2BIG; + } + snprintf(outpath, XENSTORE_ABS_PATH_MAX, "/local/domain/%u/%s", dom_id, + userpath); + } + return 0; +} + + +static int init_walk_op(XenstoreImplState *s, struct walk_op *op, + xs_transaction_t tx_id, unsigned int dom_id, + const char *path, XsNode ***rootp) +{ + int ret = validate_path(op->path, path, dom_id); + if (ret) { + return ret; + } + + /* + * We use *two* NUL terminators at the end of the path, as during the walk + * we will temporarily turn each '/' into a NUL to allow us to use that + * path element for the lookup. + */ + op->path[strlen(op->path) + 1] = '\0'; + op->watches = NULL; + op->path[0] = '\0'; + op->inplace = true; + op->mutating = false; + op->create_dirs = false; + op->in_transaction = false; + op->dom_id = dom_id; + op->tx_id = tx_id; + op->s = s; + + if (tx_id == XBT_NULL) { + *rootp = &s->root; + op->new_nr_nodes = s->nr_nodes; + } else { + XsTransaction *tx = g_hash_table_lookup(s->transactions, + GINT_TO_POINTER(tx_id)); + if (!tx) { + return ENOENT; + } + *rootp = &tx->root; + op->new_nr_nodes = tx->nr_nodes; + op->in_transaction = true; + } + + return 0; +} + +int xs_impl_read(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t tx_id, const char *path, GByteArray *data) +{ + /* + * The data GByteArray shall exist, and will be freed by caller. + * Just g_byte_array_append() to it. + */ + struct walk_op op; + XsNode **n; + int ret; + + ret = init_walk_op(s, &op, tx_id, dom_id, path, &n); + if (ret) { + return ret; + } + op.op_fn = xs_node_get_content; + op.op_opaque = data; + return xs_node_walk(n, &op); +} + +int xs_impl_write(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t tx_id, const char *path, GByteArray *data) +{ + /* + * The data GByteArray shall exist, will be freed by caller. You are + * free to use g_byte_array_steal() and keep the data. Or just ref it. + */ + struct walk_op op; + XsNode **n; + int ret; + + ret = init_walk_op(s, &op, tx_id, dom_id, path, &n); + if (ret) { + return ret; + } + op.op_fn = xs_node_add_content; + op.op_opaque = data; + op.mutating = true; + op.create_dirs = true; + return xs_node_walk(n, &op); +} + +int xs_impl_directory(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t tx_id, const char *path, + uint64_t *gencnt, GList **items) +{ + /* + * The items are (char *) to be freed by caller. Although it's consumed + * immediately so if you want to change it to (const char *) and keep + * them, go ahead and change the caller. + */ + struct walk_op op; + XsNode **n; + int ret; + + ret = init_walk_op(s, &op, tx_id, dom_id, path, &n); + if (ret) { + return ret; + } + op.op_fn = xs_node_directory; + op.op_opaque = items; + op.op_opaque2 = gencnt; + return xs_node_walk(n, &op); +} + +int xs_impl_transaction_start(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t *tx_id) +{ + XsTransaction *tx; + + if (*tx_id != XBT_NULL) { + return EINVAL; + } + + if (dom_id && s->nr_domu_transactions >= XS_MAX_TRANSACTIONS) { + return ENOSPC; + } + + tx = g_new0(XsTransaction, 1); + + tx->nr_nodes = s->nr_nodes; + tx->tx_id = next_tx(s); + tx->base_tx = s->root_tx; + tx->root = xs_node_ref(s->root); + tx->dom_id = dom_id; + + g_hash_table_insert(s->transactions, GINT_TO_POINTER(tx->tx_id), tx); + if (dom_id) { + s->nr_domu_transactions++; + } + *tx_id = tx->tx_id; + return 0; +} + +static gboolean tx_commit_walk(gpointer key, gpointer value, + gpointer user_data) +{ + struct walk_op *op = user_data; + int path_len = strlen(op->path); + int key_len = strlen(key); + bool fire_parents = true; + XsWatch *watch; + XsNode *n = value; + + if (n->ref != 1) { + return false; + } + + if (n->deleted_in_tx) { + /* + * We fire watches on our parents if we are the *first* node + * to be deleted (the topmost one). This matches the behaviour + * when deleting in the live tree. + */ + fire_parents = !op->deleted_in_tx; + + /* Only used on the way down so no need to clear it later */ + op->deleted_in_tx = true; + } + + assert(key_len + path_len + 2 <= sizeof(op->path)); + op->path[path_len] = '/'; + memcpy(op->path + path_len + 1, key, key_len + 1); + + watch = g_hash_table_lookup(op->s->watches, op->path); + if (watch) { + op->watches = g_list_append(op->watches, watch); + } + + if (n->children) { + g_hash_table_foreach_remove(n->children, tx_commit_walk, op); + } + + if (watch) { + op->watches = g_list_remove(op->watches, watch); + } + + /* + * Don't fire watches if this node was only copied because a + * descendent was changed. The modified_in_tx flag indicates the + * ones which were really changed. + */ + if (n->modified_in_tx || n->deleted_in_tx) { + fire_watches(op, fire_parents); + n->modified_in_tx = false; + } + op->path[path_len] = '\0'; + + /* Deleted nodes really do get expunged when we commit */ + return n->deleted_in_tx; +} + +static int transaction_commit(XenstoreImplState *s, XsTransaction *tx) +{ + struct walk_op op; + XsNode **n; + int ret; + + if (s->root_tx != tx->base_tx) { + return EAGAIN; + } + xs_node_unref(s->root); + s->root = tx->root; + tx->root = NULL; + s->root_tx = tx->tx_id; + s->nr_nodes = tx->nr_nodes; + + ret = init_walk_op(s, &op, XBT_NULL, tx->dom_id, "/", &n); + /* + * There are two reasons why init_walk_op() may fail: an invalid tx_id, + * or an invalid path. We pass XBT_NULL and "/", and it cannot fail. + * If it does, the world is broken. And returning 'ret' would be weird + * because the transaction *was* committed, and all this tree walk is + * trying to do is fire the resulting watches on newly-committed nodes. + */ + g_assert(!ret); + + op.deleted_in_tx = false; + op.mutating = true; + + /* + * Walk the new root and fire watches on any node which has a + * refcount of one (which is therefore unique to this transaction). + */ + if (s->root->children) { + g_hash_table_foreach_remove(s->root->children, tx_commit_walk, &op); + } + + return 0; +} + +int xs_impl_transaction_end(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t tx_id, bool commit) +{ + int ret = 0; + XsTransaction *tx = g_hash_table_lookup(s->transactions, + GINT_TO_POINTER(tx_id)); + + if (!tx || tx->dom_id != dom_id) { + return ENOENT; + } + + if (commit) { + ret = transaction_commit(s, tx); + } + + g_hash_table_remove(s->transactions, GINT_TO_POINTER(tx_id)); + if (dom_id) { + assert(s->nr_domu_transactions); + s->nr_domu_transactions--; + } + return ret; +} + +int xs_impl_rm(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t tx_id, const char *path) +{ + struct walk_op op; + XsNode **n; + int ret; + + ret = init_walk_op(s, &op, tx_id, dom_id, path, &n); + if (ret) { + return ret; + } + op.op_fn = xs_node_rm; + op.mutating = true; + return xs_node_walk(n, &op); +} + +int xs_impl_get_perms(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t tx_id, const char *path, GList **perms) +{ + struct walk_op op; + XsNode **n; + int ret; + + ret = init_walk_op(s, &op, tx_id, dom_id, path, &n); + if (ret) { + return ret; + } + op.op_fn = xs_node_get_perms; + op.op_opaque = perms; + return xs_node_walk(n, &op); +} + +static void is_valid_perm(gpointer data, gpointer user_data) +{ + char *perm = data; + bool *valid = user_data; + char letter; + unsigned int dom_id; + + if (!*valid) { + return; + } + + if (sscanf(perm, "%c%u", &letter, &dom_id) != 2) { + *valid = false; + return; + } + + switch (letter) { + case 'n': + case 'r': + case 'w': + case 'b': + break; + + default: + *valid = false; + break; + } +} + +int xs_impl_set_perms(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t tx_id, const char *path, GList *perms) +{ + struct walk_op op; + XsNode **n; + bool valid = true; + int ret; + + if (!g_list_length(perms)) { + return EINVAL; + } + + g_list_foreach(perms, is_valid_perm, &valid); + if (!valid) { + return EINVAL; + } + + ret = init_walk_op(s, &op, tx_id, dom_id, path, &n); + if (ret) { + return ret; + } + op.op_fn = xs_node_set_perms; + op.op_opaque = perms; + op.mutating = true; + return xs_node_walk(n, &op); +} + +static int do_xs_impl_watch(XenstoreImplState *s, unsigned int dom_id, + const char *path, const char *token, + xs_impl_watch_fn fn, void *opaque) + +{ + char abspath[XENSTORE_ABS_PATH_MAX + 1]; + XsWatch *w, *l; + int ret; + + ret = validate_path(abspath, path, dom_id); + if (ret) { + return ret; + } + + /* Check for duplicates */ + l = w = g_hash_table_lookup(s->watches, abspath); + while (w) { + if (!g_strcmp0(token, w->token) && opaque == w->cb_opaque && + fn == w->cb && dom_id == w->dom_id) { + return EEXIST; + } + w = w->next; + } + + if (dom_id && s->nr_domu_watches >= XS_MAX_WATCHES) { + return E2BIG; + } + + w = g_new0(XsWatch, 1); + w->token = g_strdup(token); + w->cb = fn; + w->cb_opaque = opaque; + w->dom_id = dom_id; + w->rel_prefix = strlen(abspath) - strlen(path); + + /* l was looked up above when checking for duplicates */ + if (l) { + w->next = l->next; + l->next = w; + } else { + g_hash_table_insert(s->watches, g_strdup(abspath), w); + } + if (dom_id) { + s->nr_domu_watches++; + } + + return 0; +} + +int xs_impl_watch(XenstoreImplState *s, unsigned int dom_id, const char *path, + const char *token, xs_impl_watch_fn fn, void *opaque) +{ + int ret = do_xs_impl_watch(s, dom_id, path, token, fn, opaque); + + if (!ret) { + /* A new watch should fire immediately */ + fn(opaque, path, token); + } + + return ret; +} + +static XsWatch *free_watch(XenstoreImplState *s, XsWatch *w) +{ + XsWatch *next = w->next; + + if (w->dom_id) { + assert(s->nr_domu_watches); + s->nr_domu_watches--; + } + + g_free(w->token); + g_free(w); + + return next; +} + +int xs_impl_unwatch(XenstoreImplState *s, unsigned int dom_id, + const char *path, const char *token, + xs_impl_watch_fn fn, void *opaque) +{ + char abspath[XENSTORE_ABS_PATH_MAX + 1]; + XsWatch *w, **l; + int ret; + + ret = validate_path(abspath, path, dom_id); + if (ret) { + return ret; + } + + w = g_hash_table_lookup(s->watches, abspath); + if (!w) { + return ENOENT; + } + + /* + * The hash table contains the first element of a list of + * watches. Removing the first element in the list is a + * special case because we have to update the hash table to + * point to the next (or remove it if there's nothing left). + */ + if (!g_strcmp0(token, w->token) && fn == w->cb && opaque == w->cb_opaque && + dom_id == w->dom_id) { + if (w->next) { + /* Insert the previous 'next' into the hash table */ + g_hash_table_insert(s->watches, g_strdup(abspath), w->next); + } else { + /* Nothing left; remove from hash table */ + g_hash_table_remove(s->watches, abspath); + } + free_watch(s, w); + return 0; + } + + /* + * We're all done messing with the hash table because the element + * it points to has survived the cull. Now it's just a simple + * linked list removal operation. + */ + for (l = &w->next; *l; l = &w->next) { + w = *l; + + if (!g_strcmp0(token, w->token) && fn == w->cb && + opaque != w->cb_opaque && dom_id == w->dom_id) { + *l = free_watch(s, w); + return 0; + } + } + + return ENOENT; +} + +int xs_impl_reset_watches(XenstoreImplState *s, unsigned int dom_id) +{ + char **watch_paths; + guint nr_watch_paths; + guint i; + + watch_paths = (char **)g_hash_table_get_keys_as_array(s->watches, + &nr_watch_paths); + + for (i = 0; i < nr_watch_paths; i++) { + XsWatch *w1 = g_hash_table_lookup(s->watches, watch_paths[i]); + XsWatch *w2, *w, **l; + + /* + * w1 is the original list. The hash table has this pointer. + * w2 is the head of our newly-filtered list. + * w and l are temporary for processing. w is somewhat redundant + * with *l but makes my eyes bleed less. + */ + + w = w2 = w1; + l = &w; + while (w) { + if (w->dom_id == dom_id) { + /* If we're freeing the head of the list, bump w2 */ + if (w2 == w) { + w2 = w->next; + } + *l = free_watch(s, w); + } else { + l = &w->next; + } + w = *l; + } + /* + * If the head of the list survived the cull, we don't need to + * touch the hash table and we're done with this path. Else... + */ + if (w1 != w2) { + g_hash_table_steal(s->watches, watch_paths[i]); + + /* + * It was already freed. (Don't worry, this whole thing is + * single-threaded and nobody saw it in the meantime). And + * having *stolen* it, we now own the watch_paths[i] string + * so if we don't give it back to the hash table, we need + * to free it. + */ + if (w2) { + g_hash_table_insert(s->watches, watch_paths[i], w2); + } else { + g_free(watch_paths[i]); + } + } + } + g_free(watch_paths); + return 0; +} + +static void xs_tx_free(void *_tx) +{ + XsTransaction *tx = _tx; + if (tx->root) { + xs_node_unref(tx->root); + } + g_free(tx); +} + +XenstoreImplState *xs_impl_create(unsigned int dom_id) +{ + XenstoreImplState *s = g_new0(XenstoreImplState, 1); + GList *perms; + + s->watches = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + s->transactions = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, xs_tx_free); + + perms = g_list_append(NULL, xs_perm_as_string(XS_PERM_NONE, 0)); + s->root = xs_node_create("/", perms); + g_list_free_full(perms, g_free); + s->nr_nodes = 1; + + s->root_tx = s->last_tx = 1; + return s; +} + + +static void clear_serialized_tx(gpointer key, gpointer value, gpointer opaque) +{ + XsNode *n = value; + + n->serialized_tx = XBT_NULL; + if (n->children) { + g_hash_table_foreach(n->children, clear_serialized_tx, NULL); + } +} + +static void clear_tx_serialized_tx(gpointer key, gpointer value, + gpointer opaque) +{ + XsTransaction *t = value; + + clear_serialized_tx(NULL, t->root, NULL); +} + +static void write_be32(GByteArray *save, uint32_t val) +{ + uint32_t be = htonl(val); + g_byte_array_append(save, (void *)&be, sizeof(be)); +} + + +struct save_state { + GByteArray *bytes; + unsigned int tx_id; +}; + +#define MODIFIED_IN_TX (1U << 0) +#define DELETED_IN_TX (1U << 1) +#define NODE_REF (1U << 2) + +static void save_node(gpointer key, gpointer value, gpointer opaque) +{ + struct save_state *ss = opaque; + XsNode *n = value; + char *name = key; + uint8_t flag = 0; + + /* Child nodes (i.e. anything but the root) have a name */ + if (name) { + g_byte_array_append(ss->bytes, key, strlen(key) + 1); + } + + /* + * If we already wrote this node, refer to the previous copy. + * There's no rename/move in XenStore, so all we need to find + * it is the tx_id of the transaction in which it exists. Which + * may be the root tx. + */ + if (n->serialized_tx != XBT_NULL) { + flag = NODE_REF; + g_byte_array_append(ss->bytes, &flag, 1); + write_be32(ss->bytes, n->serialized_tx); + } else { + GList *l; + n->serialized_tx = ss->tx_id; + + if (n->modified_in_tx) { + flag |= MODIFIED_IN_TX; + } + if (n->deleted_in_tx) { + flag |= DELETED_IN_TX; + } + g_byte_array_append(ss->bytes, &flag, 1); + + if (n->content) { + write_be32(ss->bytes, n->content->len); + g_byte_array_append(ss->bytes, n->content->data, n->content->len); + } else { + write_be32(ss->bytes, 0); + } + + for (l = n->perms; l; l = l->next) { + g_byte_array_append(ss->bytes, l->data, strlen(l->data) + 1); + } + /* NUL termination after perms */ + g_byte_array_append(ss->bytes, (void *)"", 1); + + if (n->children) { + g_hash_table_foreach(n->children, save_node, ss); + } + /* NUL termination after children (child name is NUL) */ + g_byte_array_append(ss->bytes, (void *)"", 1); + } +} + +static void save_tree(struct save_state *ss, uint32_t tx_id, XsNode *root) +{ + write_be32(ss->bytes, tx_id); + ss->tx_id = tx_id; + save_node(NULL, root, ss); +} + +static void save_tx(gpointer key, gpointer value, gpointer opaque) +{ + uint32_t tx_id = GPOINTER_TO_INT(key); + struct save_state *ss = opaque; + XsTransaction *n = value; + + write_be32(ss->bytes, n->base_tx); + write_be32(ss->bytes, n->dom_id); + + save_tree(ss, tx_id, n->root); +} + +static void save_watch(gpointer key, gpointer value, gpointer opaque) +{ + struct save_state *ss = opaque; + XsWatch *w = value; + + /* We only save the *guest* watches. */ + if (w->dom_id) { + gpointer relpath = key + w->rel_prefix; + g_byte_array_append(ss->bytes, relpath, strlen(relpath) + 1); + g_byte_array_append(ss->bytes, (void *)w->token, strlen(w->token) + 1); + } +} + +GByteArray *xs_impl_serialize(XenstoreImplState *s) +{ + struct save_state ss; + + ss.bytes = g_byte_array_new(); + + /* + * node = flags [ real_node / node_ref ] + * flags = uint8_t (MODIFIED_IN_TX | DELETED_IN_TX | NODE_REF) + * node_ref = tx_id (in which the original version of this node exists) + * real_node = content perms child* NUL + * content = len data + * len = uint32_t + * data = uint8_t{len} + * perms = perm* NUL + * perm = asciiz + * child = name node + * name = asciiz + * + * tree = tx_id node + * tx_id = uint32_t + * + * transaction = base_tx_id dom_id tree + * base_tx_id = uint32_t + * dom_id = uint32_t + * + * tx_list = tree transaction* XBT_NULL + * + * watch = path token + * path = asciiz + * token = asciiz + * + * watch_list = watch* NUL + * + * xs_serialize_stream = last_tx tx_list watch_list + * last_tx = uint32_t + */ + + /* Clear serialized_tx in every node. */ + if (s->serialized) { + clear_serialized_tx(NULL, s->root, NULL); + g_hash_table_foreach(s->transactions, clear_tx_serialized_tx, NULL); + } + + s->serialized = true; + + write_be32(ss.bytes, s->last_tx); + save_tree(&ss, s->root_tx, s->root); + g_hash_table_foreach(s->transactions, save_tx, &ss); + + write_be32(ss.bytes, XBT_NULL); + + g_hash_table_foreach(s->watches, save_watch, &ss); + g_byte_array_append(ss.bytes, (void *)"", 1); + + return ss.bytes; +} + +struct unsave_state { + char path[XENSTORE_ABS_PATH_MAX + 1]; + XenstoreImplState *s; + GByteArray *bytes; + uint8_t *d; + size_t l; + bool root_walk; +}; + +static int consume_be32(struct unsave_state *us, unsigned int *val) +{ + uint32_t d; + + if (us->l < sizeof(d)) { + return -EINVAL; + } + memcpy(&d, us->d, sizeof(d)); + *val = ntohl(d); + us->d += sizeof(d); + us->l -= sizeof(d); + return 0; +} + +static int consume_string(struct unsave_state *us, char **str, size_t *len) +{ + size_t l; + + if (!us->l) { + return -EINVAL; + } + + l = strnlen((void *)us->d, us->l); + if (l == us->l) { + return -EINVAL; + } + + if (str) { + *str = (void *)us->d; + } + if (len) { + *len = l; + } + + us->d += l + 1; + us->l -= l + 1; + return 0; +} + +static XsNode *lookup_node(XsNode *n, char *path) +{ + char *slash = strchr(path, '/'); + XsNode *child; + + if (path[0] == '\0') { + return n; + } + + if (slash) { + *slash = '\0'; + } + + if (!n->children) { + return NULL; + } + child = g_hash_table_lookup(n->children, path); + if (!slash) { + return child; + } + + *slash = '/'; + if (!child) { + return NULL; + } + return lookup_node(child, slash + 1); +} + +static XsNode *lookup_tx_node(struct unsave_state *us, unsigned int tx_id) +{ + XsTransaction *t; + if (tx_id == us->s->root_tx) { + return lookup_node(us->s->root, us->path + 1); + } + + t = g_hash_table_lookup(us->s->transactions, GINT_TO_POINTER(tx_id)); + if (!t) { + return NULL; + } + g_assert(t->root); + return lookup_node(t->root, us->path + 1); +} + +static void count_child_nodes(gpointer key, gpointer value, gpointer user_data) +{ + unsigned int *nr_nodes = user_data; + XsNode *n = value; + + (*nr_nodes)++; + + if (n->children) { + g_hash_table_foreach(n->children, count_child_nodes, nr_nodes); + } +} + +static int consume_node(struct unsave_state *us, XsNode **nodep, + unsigned int *nr_nodes) +{ + XsNode *n = NULL; + uint8_t flags; + int ret; + + if (us->l < 1) { + return -EINVAL; + } + flags = us->d[0]; + us->d++; + us->l--; + + if (flags == NODE_REF) { + unsigned int tx; + + ret = consume_be32(us, &tx); + if (ret) { + return ret; + } + + n = lookup_tx_node(us, tx); + if (!n) { + return -EINVAL; + } + n->ref++; + if (n->children) { + g_hash_table_foreach(n->children, count_child_nodes, nr_nodes); + } + } else { + uint32_t datalen; + + if (flags & ~(DELETED_IN_TX | MODIFIED_IN_TX)) { + return -EINVAL; + } + n = xs_node_new(); + + if (flags & DELETED_IN_TX) { + n->deleted_in_tx = true; + } + if (flags & MODIFIED_IN_TX) { + n->modified_in_tx = true; + } + ret = consume_be32(us, &datalen); + if (ret) { + xs_node_unref(n); + return -EINVAL; + } + if (datalen) { + if (datalen > us->l) { + xs_node_unref(n); + return -EINVAL; + } + + GByteArray *node_data = g_byte_array_new(); + g_byte_array_append(node_data, us->d, datalen); + us->d += datalen; + us->l -= datalen; + n->content = node_data; + + if (us->root_walk) { + n->modified_in_tx = true; + } + } + while (1) { + char *perm = NULL; + size_t permlen = 0; + + ret = consume_string(us, &perm, &permlen); + if (ret) { + xs_node_unref(n); + return ret; + } + + if (!permlen) { + break; + } + + n->perms = g_list_append(n->perms, g_strdup(perm)); + } + + /* Now children */ + while (1) { + size_t childlen; + char *childname; + char *pathend; + XsNode *child = NULL; + + ret = consume_string(us, &childname, &childlen); + if (ret) { + xs_node_unref(n); + return ret; + } + + if (!childlen) { + break; + } + + pathend = us->path + strlen(us->path); + strncat(us->path, "/", sizeof(us->path) - 1); + strncat(us->path, childname, sizeof(us->path) - 1); + + ret = consume_node(us, &child, nr_nodes); + *pathend = '\0'; + if (ret) { + xs_node_unref(n); + return ret; + } + g_assert(child); + xs_node_add_child(n, childname, child); + } + + /* + * If the node has no data and no children we still want to fire + * a watch on it. + */ + if (us->root_walk && !n->children) { + n->modified_in_tx = true; + } + } + + if (!n->deleted_in_tx) { + (*nr_nodes)++; + } + + *nodep = n; + return 0; +} + +static int consume_tree(struct unsave_state *us, XsTransaction *t) +{ + int ret; + + ret = consume_be32(us, &t->tx_id); + if (ret) { + return ret; + } + + if (t->tx_id > us->s->last_tx) { + return -EINVAL; + } + + us->path[0] = '\0'; + + return consume_node(us, &t->root, &t->nr_nodes); +} + +int xs_impl_deserialize(XenstoreImplState *s, GByteArray *bytes, + unsigned int dom_id, xs_impl_watch_fn watch_fn, + void *watch_opaque) +{ + struct unsave_state us; + XsTransaction base_t = { 0 }; + int ret; + + us.s = s; + us.bytes = bytes; + us.d = bytes->data; + us.l = bytes->len; + + xs_impl_reset_watches(s, dom_id); + g_hash_table_remove_all(s->transactions); + + xs_node_unref(s->root); + s->root = NULL; + s->root_tx = s->last_tx = XBT_NULL; + + ret = consume_be32(&us, &s->last_tx); + if (ret) { + return ret; + } + + /* + * Consume the base tree into a transaction so that watches can be + * fired as we commit it. By setting us.root_walk we cause the nodes + * to be marked as 'modified_in_tx' as they are created, so that the + * watches are triggered on them. + */ + base_t.dom_id = dom_id; + base_t.base_tx = XBT_NULL; + us.root_walk = true; + ret = consume_tree(&us, &base_t); + if (ret) { + return ret; + } + us.root_walk = false; + + /* + * Commit the transaction now while the refcount on all nodes is 1. + * Note that we haven't yet reinstated the *guest* watches but that's + * OK because we don't want the guest to see any changes. Even any + * backend nodes which get recreated should be *precisely* as they + * were before the migration. Back ends may have been instantiated + * already, and will see the frontend magically blink into existence + * now (well, from the aio_bh which fires the watches). It's their + * responsibility to rebuild everything precisely as it was before. + */ + ret = transaction_commit(s, &base_t); + if (ret) { + return ret; + } + + while (1) { + unsigned int base_tx; + XsTransaction *t; + + ret = consume_be32(&us, &base_tx); + if (ret) { + return ret; + } + if (base_tx == XBT_NULL) { + break; + } + + t = g_new0(XsTransaction, 1); + t->base_tx = base_tx; + + ret = consume_be32(&us, &t->dom_id); + if (!ret) { + ret = consume_tree(&us, t); + } + if (ret) { + g_free(t); + return ret; + } + g_assert(t->root); + if (t->dom_id) { + s->nr_domu_transactions++; + } + g_hash_table_insert(s->transactions, GINT_TO_POINTER(t->tx_id), t); + } + + while (1) { + char *path, *token; + size_t pathlen, toklen; + + ret = consume_string(&us, &path, &pathlen); + if (ret) { + return ret; + } + if (!pathlen) { + break; + } + + ret = consume_string(&us, &token, &toklen); + if (ret) { + return ret; + } + + if (!watch_fn) { + continue; + } + + ret = do_xs_impl_watch(s, dom_id, path, token, watch_fn, watch_opaque); + if (ret) { + return ret; + } + } + + if (us.l) { + return -EINVAL; + } + + return 0; +} diff --git a/hw/i386/kvm/xenstore_impl.h b/hw/i386/kvm/xenstore_impl.h new file mode 100644 index 0000000000..0df2a91aae --- /dev/null +++ b/hw/i386/kvm/xenstore_impl.h @@ -0,0 +1,63 @@ +/* + * QEMU Xen emulation: The actual implementation of XenStore + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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_XENSTORE_IMPL_H +#define QEMU_XENSTORE_IMPL_H + +#include "hw/xen/xen_backend_ops.h" + +typedef struct XenstoreImplState XenstoreImplState; + +XenstoreImplState *xs_impl_create(unsigned int dom_id); + +char *xs_perm_as_string(unsigned int perm, unsigned int domid); + +/* + * These functions return *positive* error numbers. This is a little + * unconventional but it helps to keep us honest because there is + * also a very limited set of error numbers that they are permitted + * to return (those in xsd_errors). + */ + +int xs_impl_read(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t tx_id, const char *path, GByteArray *data); +int xs_impl_write(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t tx_id, const char *path, GByteArray *data); +int xs_impl_directory(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t tx_id, const char *path, + uint64_t *gencnt, GList **items); +int xs_impl_transaction_start(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t *tx_id); +int xs_impl_transaction_end(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t tx_id, bool commit); +int xs_impl_rm(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t tx_id, const char *path); +int xs_impl_get_perms(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t tx_id, const char *path, GList **perms); +int xs_impl_set_perms(XenstoreImplState *s, unsigned int dom_id, + xs_transaction_t tx_id, const char *path, GList *perms); + +/* This differs from xs_watch_fn because it has the token */ +typedef void(xs_impl_watch_fn)(void *opaque, const char *path, + const char *token); +int xs_impl_watch(XenstoreImplState *s, unsigned int dom_id, const char *path, + const char *token, xs_impl_watch_fn fn, void *opaque); +int xs_impl_unwatch(XenstoreImplState *s, unsigned int dom_id, + const char *path, const char *token, xs_impl_watch_fn fn, + void *opaque); +int xs_impl_reset_watches(XenstoreImplState *s, unsigned int dom_id); + +GByteArray *xs_impl_serialize(XenstoreImplState *s); +int xs_impl_deserialize(XenstoreImplState *s, GByteArray *bytes, + unsigned int dom_id, xs_impl_watch_fn watch_fn, + void *watch_opaque); + +#endif /* QEMU_XENSTORE_IMPL_H */ diff --git a/hw/i386/meson.build b/hw/i386/meson.build index 213e2e82b3..d8b70ef3e9 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -1,7 +1,7 @@ i386_ss = ss.source_set() i386_ss.add(files( 'fw_cfg.c', - 'kvmvapic.c', + 'vapic.c', 'e820_memory_layout.c', 'multiboot.c', 'x86.c', @@ -9,7 +9,8 @@ i386_ss.add(files( i386_ss.add(when: 'CONFIG_X86_IOMMU', if_true: files('x86-iommu.c'), if_false: files('x86-iommu-stub.c')) -i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd_iommu.c')) +i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd_iommu.c'), + if_false: files('amd_iommu-stub.c')) i386_ss.add(when: 'CONFIG_I440FX', if_true: files('pc_piix.c')) i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('microvm.c', 'acpi-microvm.c', 'microvm-dt.c')) i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c')) @@ -20,7 +21,6 @@ i386_ss.add(when: 'CONFIG_SGX', if_true: files('sgx-epc.c','sgx.c'), if_false: files('sgx-stub.c')) i386_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-common.c')) -i386_ss.add(when: 'CONFIG_ACPI_HW_REDUCED', if_true: files('generic_event_device_x86.c')) i386_ss.add(when: 'CONFIG_PC', if_true: files( 'pc.c', 'pc_sysfw.c', diff --git a/hw/i386/microvm-dt.c b/hw/i386/microvm-dt.c index 9c3c4995b4..b3049e4f9f 100644 --- a/hw/i386/microvm-dt.c +++ b/hw/i386/microvm-dt.c @@ -32,6 +32,7 @@ */ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qapi/error.h" #include "sysemu/device_tree.h" #include "hw/char/serial.h" #include "hw/i386/fw_cfg.h" @@ -187,8 +188,8 @@ static void dt_add_ioapic(MicrovmMachineState *mms, SysBusDevice *dev) static void dt_add_isa_serial(MicrovmMachineState *mms, ISADevice *dev) { const char compat[] = "ns16550"; - uint32_t irq = object_property_get_int(OBJECT(dev), "irq", NULL); - hwaddr base = object_property_get_int(OBJECT(dev), "iobase", NULL); + uint32_t irq = object_property_get_int(OBJECT(dev), "irq", &error_fatal); + hwaddr base = object_property_get_int(OBJECT(dev), "iobase", &error_fatal); hwaddr size = 8; char *nodename; @@ -208,8 +209,8 @@ static void dt_add_isa_serial(MicrovmMachineState *mms, ISADevice *dev) static void dt_add_isa_rtc(MicrovmMachineState *mms, ISADevice *dev) { const char compat[] = "motorola,mc146818"; - uint32_t irq = RTC_ISA_IRQ; - hwaddr base = RTC_ISA_BASE; + uint32_t irq = object_property_get_uint(OBJECT(dev), "irq", &error_fatal); + hwaddr base = object_property_get_uint(OBJECT(dev), "iobase", &error_fatal); hwaddr size = 8; char *nodename; diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 754f1d0593..61a772dfe6 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -32,7 +32,7 @@ #include "hw/loader.h" #include "hw/irq.h" -#include "hw/kvm/clock.h" +#include "hw/i386/kvm/clock.h" #include "hw/i386/microvm.h" #include "hw/i386/x86.h" #include "target/i386/cpu.h" @@ -57,14 +57,14 @@ #define MICROVM_QBOOT_FILENAME "qboot.rom" #define MICROVM_BIOS_FILENAME "bios-microvm.bin" -static void microvm_set_rtc(MicrovmMachineState *mms, ISADevice *s) +static void microvm_set_rtc(MicrovmMachineState *mms, MC146818RtcState *s) { X86MachineState *x86ms = X86_MACHINE(mms); int val; val = MIN(x86ms->below_4g_mem_size / KiB, 640); - rtc_set_memory(s, 0x15, val); - rtc_set_memory(s, 0x16, val >> 8); + mc146818rtc_set_cmos_data(s, 0x15, val); + mc146818rtc_set_cmos_data(s, 0x16, val >> 8); /* extended memory (next 64MiB) */ if (x86ms->below_4g_mem_size > 1 * MiB) { val = (x86ms->below_4g_mem_size - 1 * MiB) / KiB; @@ -74,10 +74,10 @@ static void microvm_set_rtc(MicrovmMachineState *mms, ISADevice *s) if (val > 65535) { val = 65535; } - rtc_set_memory(s, 0x17, val); - rtc_set_memory(s, 0x18, val >> 8); - rtc_set_memory(s, 0x30, val); - rtc_set_memory(s, 0x31, val >> 8); + mc146818rtc_set_cmos_data(s, 0x17, val); + mc146818rtc_set_cmos_data(s, 0x18, val >> 8); + mc146818rtc_set_cmos_data(s, 0x30, val); + mc146818rtc_set_cmos_data(s, 0x31, val >> 8); /* memory between 16MiB and 4GiB */ if (x86ms->below_4g_mem_size > 16 * MiB) { val = (x86ms->below_4g_mem_size - 16 * MiB) / (64 * KiB); @@ -87,13 +87,13 @@ static void microvm_set_rtc(MicrovmMachineState *mms, ISADevice *s) if (val > 65535) { val = 65535; } - rtc_set_memory(s, 0x34, val); - rtc_set_memory(s, 0x35, val >> 8); + mc146818rtc_set_cmos_data(s, 0x34, val); + mc146818rtc_set_cmos_data(s, 0x35, val >> 8); /* memory above 4GiB */ val = x86ms->above_4g_mem_size / 65536; - rtc_set_memory(s, 0x5b, val); - rtc_set_memory(s, 0x5c, val >> 8); - rtc_set_memory(s, 0x5d, val >> 16); + mc146818rtc_set_cmos_data(s, 0x5b, val); + mc146818rtc_set_cmos_data(s, 0x5c, val >> 8); + mc146818rtc_set_cmos_data(s, 0x5d, val >> 16); } static void create_gpex(MicrovmMachineState *mms) @@ -161,7 +161,6 @@ static void microvm_devices_init(MicrovmMachineState *mms) const char *default_firmware; X86MachineState *x86ms = X86_MACHINE(mms); ISABus *isa_bus; - ISADevice *rtc_state; GSIState *gsi_state; int ioapics; int i; @@ -174,14 +173,16 @@ static void microvm_devices_init(MicrovmMachineState *mms) isa_bus = isa_bus_new(NULL, get_system_memory(), get_system_io(), &error_abort); - isa_bus_irqs(isa_bus, x86ms->gsi); + isa_bus_register_input_irqs(isa_bus, x86ms->gsi); - ioapic_init_gsi(gsi_state, "machine"); + ioapic_init_gsi(gsi_state, OBJECT(mms)); if (ioapics > 1) { x86ms->ioapic2 = ioapic_init_secondary(gsi_state); } - kvmclock_create(true); + if (kvm_enabled()) { + kvmclock_create(true); + } mms->virtio_irq_base = 5; mms->virtio_num_transports = 8; @@ -203,14 +204,14 @@ static void microvm_devices_init(MicrovmMachineState *mms) /* Optional and legacy devices */ if (x86_machine_is_acpi_enabled(x86ms)) { - DeviceState *dev = qdev_new(TYPE_ACPI_GED_X86); + DeviceState *dev = qdev_new(TYPE_ACPI_GED); qdev_prop_set_uint32(dev, "ged-event", ACPI_GED_PWR_DOWN_EVT); + sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, GED_MMIO_BASE); /* sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, GED_MMIO_BASE_MEMHP); */ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, GED_MMIO_BASE_REGS); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, x86ms->gsi[GED_MMIO_IRQ]); - sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal); x86ms->acpi_dev = HOTPLUG_HANDLER(dev); } @@ -267,8 +268,7 @@ static void microvm_devices_init(MicrovmMachineState *mms) if (mms->rtc == ON_OFF_AUTO_ON || (mms->rtc == ON_OFF_AUTO_AUTO && !kvm_enabled())) { - rtc_state = mc146818_rtc_init(isa_bus, 2000, NULL); - microvm_set_rtc(mms, rtc_state); + microvm_set_rtc(mms, mc146818_rtc_init(isa_bus, 2000, NULL)); } if (mms->isa_serial) { @@ -324,8 +324,6 @@ static void microvm_memory_init(MicrovmMachineState *mms) fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, machine->smp.max_cpus); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)machine->ram_size); fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, 1); - fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, - &e820_reserve, sizeof(e820_reserve)); fw_cfg_add_file(fw_cfg, "etc/e820", e820_table, sizeof(struct e820_entry) * e820_get_num_entries()); @@ -393,9 +391,8 @@ static void microvm_fix_kernel_cmdline(MachineState *machine) bus = sysbus_get_default(); QTAILQ_FOREACH(kid, &bus->children, sibling) { DeviceState *dev = kid->child; - ObjectClass *class = object_get_class(OBJECT(dev)); - if (class == object_class_by_name(TYPE_VIRTIO_MMIO)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MMIO)) { VirtIOMMIOProxy *mmio = VIRTIO_MMIO(OBJECT(dev)); VirtioBusState *mmio_virtio_bus = &mmio->bus; BusState *mmio_bus = &mmio_virtio_bus->parent_obj; @@ -467,7 +464,7 @@ static void microvm_machine_state_init(MachineState *machine) microvm_devices_init(mms); } -static void microvm_machine_reset(MachineState *machine) +static void microvm_machine_reset(MachineState *machine, ShutdownCause reason) { MicrovmMachineState *mms = MICROVM_MACHINE(machine); CPUState *cs; @@ -480,14 +477,12 @@ static void microvm_machine_reset(MachineState *machine) mms->kernel_cmdline_fixed = true; } - qemu_devices_reset(); + qemu_devices_reset(reason); CPU_FOREACH(cs) { cpu = X86_CPU(cs); - if (cpu->apic_state) { - device_legacy_reset(cpu->apic_state); - } + x86_cpu_after_reset(cpu); } } @@ -631,6 +626,14 @@ static void microvm_machine_initfn(Object *obj) qemu_register_powerdown_notifier(&mms->powerdown_req); } +GlobalProperty microvm_properties[] = { + /* + * pcie host bridge (gpex) on microvm has no io address window, + * so reserving io space is not going to work. Turn it off. + */ + { "pcie-root-port", "io-reserve", "0" }, +}; + static void microvm_class_init(ObjectClass *oc, void *data) { X86MachineClass *x86mc = X86_MACHINE_CLASS(oc); @@ -707,6 +710,9 @@ static void microvm_class_init(ObjectClass *oc, void *data) "Set off to disable adding virtio-mmio devices to the kernel cmdline"); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); + + compat_props_add(mc->compat_props, microvm_properties, + G_N_ELEMENTS(microvm_properties)); } static const TypeInfo microvm_machine_info = { diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index 0a10089f14..3332712ab3 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -137,7 +137,7 @@ static void mb_add_mod(MultibootState *s, stl_p(p + MB_MOD_END, end); stl_p(p + MB_MOD_CMDLINE, cmdline_phys); - mb_debug("mod%02d: "TARGET_FMT_plx" - "TARGET_FMT_plx, + mb_debug("mod%02d: "HWADDR_FMT_plx" - "HWADDR_FMT_plx, s->mb_mods_count, start, end); s->mb_mods_count++; @@ -163,6 +163,7 @@ int load_multiboot(X86MachineState *x86ms, uint8_t *mb_bootinfo_data; uint32_t cmdline_len; GList *mods = NULL; + g_autofree char *kcmdline = NULL; /* Ok, let's see if it is a multiboot image. The header is 12x32bit long, so the latest entry may be 8192 - 48. */ @@ -352,7 +353,7 @@ int load_multiboot(X86MachineState *x86ms, mb_add_mod(&mbs, mbs.mb_buf_phys + offs, mbs.mb_buf_phys + offs + mb_mod_length, c); - mb_debug("mod_start: %p\nmod_end: %p\n cmdline: "TARGET_FMT_plx, + mb_debug("mod_start: %p\nmod_end: %p\n cmdline: "HWADDR_FMT_plx, (char *)mbs.mb_buf + offs, (char *)mbs.mb_buf + offs + mb_mod_length, c); g_free(one_file); @@ -362,9 +363,7 @@ int load_multiboot(X86MachineState *x86ms, } /* Commandline support */ - char kcmdline[strlen(kernel_filename) + strlen(kernel_cmdline) + 2]; - snprintf(kcmdline, sizeof(kcmdline), "%s %s", - kernel_filename, kernel_cmdline); + kcmdline = g_strdup_printf("%s %s", kernel_filename, kernel_cmdline); stl_p(bootinfo + MBI_CMDLINE, mb_add_cmdline(&mbs, kcmdline)); stl_p(bootinfo + MBI_BOOTLOADER, mb_add_bootloader(&mbs, bootloader_name)); @@ -383,8 +382,8 @@ int load_multiboot(X86MachineState *x86ms, stl_p(bootinfo + MBI_MMAP_ADDR, ADDR_E820_MAP); mb_debug("multiboot: entry_addr = %#x", mh_entry_addr); - mb_debug(" mb_buf_phys = "TARGET_FMT_plx, mbs.mb_buf_phys); - mb_debug(" mod_start = "TARGET_FMT_plx, + mb_debug(" mb_buf_phys = "HWADDR_FMT_plx, mbs.mb_buf_phys); + mb_debug(" mod_start = "HWADDR_FMT_plx, mbs.mb_buf_phys + mbs.offset_mods); mb_debug(" mb_mods_count = %d", mbs.mb_mods_count); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 7c39c91335..5c21b0c4db 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -24,78 +24,79 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "hw/i386/x86.h" #include "hw/i386/pc.h" #include "hw/char/serial.h" #include "hw/char/parallel.h" -#include "hw/i386/apic.h" -#include "hw/i386/topology.h" +#include "hw/hyperv/hv-balloon.h" #include "hw/i386/fw_cfg.h" #include "hw/i386/vmport.h" #include "sysemu/cpus.h" -#include "hw/block/fdc.h" -#include "hw/ide.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_bus.h" -#include "hw/nvram/fw_cfg.h" +#include "hw/ide/ide-bus.h" #include "hw/timer/hpet.h" -#include "hw/firmware/smbios.h" #include "hw/loader.h" -#include "elf.h" -#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" -#include "hw/irq.h" #include "hw/audio/pcspk.h" -#include "hw/pci/msi.h" -#include "hw/sysbus.h" #include "sysemu/sysemu.h" -#include "sysemu/tcg.h" -#include "sysemu/numa.h" -#include "sysemu/kvm.h" #include "sysemu/xen.h" #include "sysemu/reset.h" -#include "sysemu/runstate.h" #include "kvm/kvm_i386.h" #include "hw/xen/xen.h" -#include "hw/xen/start_info.h" -#include "ui/qemu-spice.h" -#include "exec/memory.h" -#include "qemu/bitmap.h" -#include "qemu/config-file.h" +#include "qapi/qmp/qlist.h" #include "qemu/error-report.h" -#include "qemu/option.h" -#include "qemu/cutils.h" -#include "hw/acpi/acpi.h" #include "hw/acpi/cpu_hotplug.h" #include "acpi-build.h" -#include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" -#include "hw/cxl/cxl.h" -#include "qapi/error.h" -#include "qapi/qapi-visit-common.h" -#include "qapi/qapi-visit-machine.h" -#include "qapi/visitor.h" -#include "hw/core/cpu.h" +#include "hw/cxl/cxl_host.h" #include "hw/usb.h" #include "hw/i386/intel_iommu.h" #include "hw/net/ne2000-isa.h" -#include "standard-headers/asm-x86/bootparam.h" #include "hw/virtio/virtio-iommu.h" -#include "hw/virtio/virtio-pmem-pci.h" -#include "hw/virtio/virtio-mem-pci.h" +#include "hw/virtio/virtio-md-pci.h" +#include "hw/i386/kvm/xen_overlay.h" +#include "hw/i386/kvm/xen_evtchn.h" +#include "hw/i386/kvm/xen_gnttab.h" +#include "hw/i386/kvm/xen_xenstore.h" #include "hw/mem/memory-device.h" -#include "sysemu/replay.h" -#include "qapi/qmp/qerror.h" #include "e820_memory_layout.h" -#include "fw_cfg.h" #include "trace.h" #include CONFIG_DEVICES +#ifdef CONFIG_XEN_EMU +#include "hw/xen/xen-legacy-backend.h" +#include "hw/xen/xen-bus.h" +#endif + +/* + * Helper for setting model-id for CPU models that changed model-id + * depending on QEMU versions up to QEMU 2.4. + */ +#define PC_CPU_MODEL_IDS(v) \ + { "qemu32-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ + { "qemu64-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ + { "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, }, + +GlobalProperty pc_compat_8_2[] = {}; +const size_t pc_compat_8_2_len = G_N_ELEMENTS(pc_compat_8_2); + +GlobalProperty pc_compat_8_1[] = {}; +const size_t pc_compat_8_1_len = G_N_ELEMENTS(pc_compat_8_1); + +GlobalProperty pc_compat_8_0[] = { + { "virtio-mem", "unplugged-inaccessible", "auto" }, +}; +const size_t pc_compat_8_0_len = G_N_ELEMENTS(pc_compat_8_0); + +GlobalProperty pc_compat_7_2[] = { + { "ICH9-LPC", "noreboot", "true" }, +}; +const size_t pc_compat_7_2_len = G_N_ELEMENTS(pc_compat_7_2); + +GlobalProperty pc_compat_7_1[] = {}; +const size_t pc_compat_7_1_len = G_N_ELEMENTS(pc_compat_7_1); + GlobalProperty pc_compat_7_0[] = {}; const size_t pc_compat_7_0_len = G_N_ELEMENTS(pc_compat_7_0); @@ -325,60 +326,6 @@ GlobalProperty pc_compat_2_0[] = { }; const size_t pc_compat_2_0_len = G_N_ELEMENTS(pc_compat_2_0); -GlobalProperty pc_compat_1_7[] = { - PC_CPU_MODEL_IDS("1.7.0") - { TYPE_USB_DEVICE, "msos-desc", "no" }, - { "PIIX4_PM", ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, "off" }, - { "hpet", HPET_INTCAP, "4" }, -}; -const size_t pc_compat_1_7_len = G_N_ELEMENTS(pc_compat_1_7); - -GlobalProperty pc_compat_1_6[] = { - PC_CPU_MODEL_IDS("1.6.0") - { "e1000", "mitigation", "off" }, - { "qemu64-" TYPE_X86_CPU, "model", "2" }, - { "qemu32-" TYPE_X86_CPU, "model", "3" }, - { "i440FX-pcihost", "short_root_bus", "1" }, - { "q35-pcihost", "short_root_bus", "1" }, -}; -const size_t pc_compat_1_6_len = G_N_ELEMENTS(pc_compat_1_6); - -GlobalProperty pc_compat_1_5[] = { - PC_CPU_MODEL_IDS("1.5.0") - { "Conroe-" TYPE_X86_CPU, "model", "2" }, - { "Conroe-" TYPE_X86_CPU, "min-level", "2" }, - { "Penryn-" TYPE_X86_CPU, "model", "2" }, - { "Penryn-" TYPE_X86_CPU, "min-level", "2" }, - { "Nehalem-" TYPE_X86_CPU, "model", "2" }, - { "Nehalem-" TYPE_X86_CPU, "min-level", "2" }, - { "virtio-net-pci", "any_layout", "off" }, - { TYPE_X86_CPU, "pmu", "on" }, - { "i440FX-pcihost", "short_root_bus", "0" }, - { "q35-pcihost", "short_root_bus", "0" }, -}; -const size_t pc_compat_1_5_len = G_N_ELEMENTS(pc_compat_1_5); - -GlobalProperty pc_compat_1_4[] = { - PC_CPU_MODEL_IDS("1.4.0") - { "scsi-hd", "discard_granularity", "0" }, - { "scsi-cd", "discard_granularity", "0" }, - { "ide-hd", "discard_granularity", "0" }, - { "ide-cd", "discard_granularity", "0" }, - { "virtio-blk-pci", "discard_granularity", "0" }, - /* DEV_NVECTORS_UNSPECIFIED as a uint32_t string: */ - { "virtio-serial-pci", "vectors", "0xFFFFFFFF" }, - { "virtio-net-pci", "ctrl_guest_offloads", "off" }, - { "e1000", "romfile", "pxe-e1000.rom" }, - { "ne2k_pci", "romfile", "pxe-ne2k_pci.rom" }, - { "pcnet", "romfile", "pxe-pcnet.rom" }, - { "rtl8139", "romfile", "pxe-rtl8139.rom" }, - { "virtio-net-pci", "romfile", "pxe-virtio.rom" }, - { "486-" TYPE_X86_CPU, "model", "0" }, - { "n270" "-" TYPE_X86_CPU, "movbe", "off" }, - { "Westmere" "-" TYPE_X86_CPU, "pclmulqdq", "off" }, -}; -const size_t pc_compat_1_4_len = G_N_ELEMENTS(pc_compat_1_4); - GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled) { GSIState *s; @@ -387,7 +334,7 @@ GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled) if (kvm_ioapic_in_kernel()) { kvm_pc_setup_irq_routing(pci_enabled); } - *irqs = qemu_allocate_irqs(gsi_handler, s, GSI_NUM_PINS); + *irqs = qemu_allocate_irqs(gsi_handler, s, IOAPIC_NUM_PINS); return s; } @@ -402,7 +349,7 @@ static uint64_t ioport80_read(void *opaque, hwaddr addr, unsigned size) return 0xffffffffffffffffULL; } -/* MSDOS compatibility mode FPU exception support */ +/* MS-DOS compatibility mode FPU exception support */ static void ioportF0_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { @@ -420,19 +367,19 @@ static uint64_t ioportF0_read(void *opaque, hwaddr addr, unsigned size) #define REG_EQUIPMENT_BYTE 0x14 -static void cmos_init_hd(ISADevice *s, int type_ofs, int info_ofs, +static void cmos_init_hd(MC146818RtcState *s, int type_ofs, int info_ofs, int16_t cylinders, int8_t heads, int8_t sectors) { - rtc_set_memory(s, type_ofs, 47); - rtc_set_memory(s, info_ofs, cylinders); - rtc_set_memory(s, info_ofs + 1, cylinders >> 8); - rtc_set_memory(s, info_ofs + 2, heads); - rtc_set_memory(s, info_ofs + 3, 0xff); - rtc_set_memory(s, info_ofs + 4, 0xff); - rtc_set_memory(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3)); - rtc_set_memory(s, info_ofs + 6, cylinders); - rtc_set_memory(s, info_ofs + 7, cylinders >> 8); - rtc_set_memory(s, info_ofs + 8, sectors); + mc146818rtc_set_cmos_data(s, type_ofs, 47); + mc146818rtc_set_cmos_data(s, info_ofs, cylinders); + mc146818rtc_set_cmos_data(s, info_ofs + 1, cylinders >> 8); + mc146818rtc_set_cmos_data(s, info_ofs + 2, heads); + mc146818rtc_set_cmos_data(s, info_ofs + 3, 0xff); + mc146818rtc_set_cmos_data(s, info_ofs + 4, 0xff); + mc146818rtc_set_cmos_data(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3)); + mc146818rtc_set_cmos_data(s, info_ofs + 6, cylinders); + mc146818rtc_set_cmos_data(s, info_ofs + 7, cylinders >> 8); + mc146818rtc_set_cmos_data(s, info_ofs + 8, sectors); } /* convert boot_device letter to something recognizable by the bios */ @@ -452,7 +399,8 @@ static int boot_device2nibble(char boot_device) return 0; } -static void set_boot_dev(ISADevice *s, const char *boot_device, Error **errp) +static void set_boot_dev(PCMachineState *pcms, MC146818RtcState *s, + const char *boot_device, Error **errp) { #define PC_MAX_BOOT_DEVICES 3 int nbds, bds[3] = { 0, }; @@ -471,16 +419,19 @@ static void set_boot_dev(ISADevice *s, const char *boot_device, Error **errp) return; } } - rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]); - rtc_set_memory(s, 0x38, (bds[2] << 4) | (fd_bootchk ? 0x0 : 0x1)); + mc146818rtc_set_cmos_data(s, 0x3d, (bds[1] << 4) | bds[0]); + mc146818rtc_set_cmos_data(s, 0x38, (bds[2] << 4) | !pcms->fd_bootchk); } static void pc_boot_set(void *opaque, const char *boot_device, Error **errp) { - set_boot_dev(opaque, boot_device, errp); + PCMachineState *pcms = opaque; + X86MachineState *x86ms = X86_MACHINE(pcms); + + set_boot_dev(pcms, MC146818_RTC(x86ms->rtc), boot_device, errp); } -static void pc_cmos_init_floppy(ISADevice *rtc_state, ISADevice *floppy) +static void pc_cmos_init_floppy(MC146818RtcState *rtc_state, ISADevice *floppy) { int val, nb, i; FloppyDriveType fd_type[2] = { FLOPPY_DRIVE_TYPE_NONE, @@ -494,9 +445,9 @@ static void pc_cmos_init_floppy(ISADevice *rtc_state, ISADevice *floppy) } val = (cmos_get_fd_drive_type(fd_type[0]) << 4) | cmos_get_fd_drive_type(fd_type[1]); - rtc_set_memory(rtc_state, 0x10, val); + mc146818rtc_set_cmos_data(rtc_state, 0x10, val); - val = rtc_get_memory(rtc_state, REG_EQUIPMENT_BYTE); + val = mc146818rtc_get_cmos_data(rtc_state, REG_EQUIPMENT_BYTE); nb = 0; if (fd_type[0] != FLOPPY_DRIVE_TYPE_NONE) { nb++; @@ -514,14 +465,9 @@ static void pc_cmos_init_floppy(ISADevice *rtc_state, ISADevice *floppy) val |= 0x41; /* 2 drives, ready for boot */ break; } - rtc_set_memory(rtc_state, REG_EQUIPMENT_BYTE, val); + mc146818rtc_set_cmos_data(rtc_state, REG_EQUIPMENT_BYTE, val); } -typedef struct pc_cmos_init_late_arg { - ISADevice *rtc_state; - BusState *idebus[2]; -} pc_cmos_init_late_arg; - typedef struct check_fdc_state { ISADevice *floppy; bool multiple; @@ -561,7 +507,7 @@ static const char * const fdc_container_path[] = { * Locate the FDC at IO address 0x3f0, in order to configure the CMOS registers * and ACPI objects. */ -ISADevice *pc_find_fdc0(void) +static ISADevice *pc_find_fdc0(void) { int i; Object *container; @@ -582,27 +528,29 @@ ISADevice *pc_find_fdc0(void) return state.floppy; } -static void pc_cmos_init_late(void *opaque) +static void pc_cmos_init_late(PCMachineState *pcms) { - pc_cmos_init_late_arg *arg = opaque; - ISADevice *s = arg->rtc_state; + X86MachineState *x86ms = X86_MACHINE(pcms); + MC146818RtcState *s = MC146818_RTC(x86ms->rtc); int16_t cylinders; int8_t heads, sectors; int val; int i, trans; val = 0; - if (arg->idebus[0] && ide_get_geometry(arg->idebus[0], 0, - &cylinders, &heads, §ors) >= 0) { + if (pcms->idebus[0] && + ide_get_geometry(pcms->idebus[0], 0, + &cylinders, &heads, §ors) >= 0) { cmos_init_hd(s, 0x19, 0x1b, cylinders, heads, sectors); val |= 0xf0; } - if (arg->idebus[0] && ide_get_geometry(arg->idebus[0], 1, - &cylinders, &heads, §ors) >= 0) { + if (pcms->idebus[0] && + ide_get_geometry(pcms->idebus[0], 1, + &cylinders, &heads, §ors) >= 0) { cmos_init_hd(s, 0x1a, 0x24, cylinders, heads, sectors); val |= 0x0f; } - rtc_set_memory(s, 0x12, val); + mc146818rtc_set_cmos_data(s, 0x12, val); val = 0; for (i = 0; i < 4; i++) { @@ -610,36 +558,26 @@ static void pc_cmos_init_late(void *opaque) geometry. It is always such that: 1 <= sects <= 63, 1 <= heads <= 16, 1 <= cylinders <= 16383. The BIOS geometry can be different if a translation is done. */ - if (arg->idebus[i / 2] && - ide_get_geometry(arg->idebus[i / 2], i % 2, + BusState *idebus = pcms->idebus[i / 2]; + if (idebus && + ide_get_geometry(idebus, i % 2, &cylinders, &heads, §ors) >= 0) { - trans = ide_get_bios_chs_trans(arg->idebus[i / 2], i % 2) - 1; + trans = ide_get_bios_chs_trans(idebus, i % 2) - 1; assert((trans & ~3) == 0); val |= trans << (i * 2); } } - rtc_set_memory(s, 0x39, val); + mc146818rtc_set_cmos_data(s, 0x39, val); pc_cmos_init_floppy(s, pc_find_fdc0()); - qemu_unregister_reset(pc_cmos_init_late, opaque); -} - -void pc_cmos_init(PCMachineState *pcms, - BusState *idebus0, BusState *idebus1, - ISADevice *s) -{ - int val; - static pc_cmos_init_late_arg arg; - X86MachineState *x86ms = X86_MACHINE(pcms); - /* various important CMOS locations needed by PC/Bochs bios */ /* memory size */ /* base memory (first MiB) */ val = MIN(x86ms->below_4g_mem_size / KiB, 640); - rtc_set_memory(s, 0x15, val); - rtc_set_memory(s, 0x16, val >> 8); + mc146818rtc_set_cmos_data(s, 0x15, val); + mc146818rtc_set_cmos_data(s, 0x16, val >> 8); /* extended memory (next 64MiB) */ if (x86ms->below_4g_mem_size > 1 * MiB) { val = (x86ms->below_4g_mem_size - 1 * MiB) / KiB; @@ -648,10 +586,10 @@ void pc_cmos_init(PCMachineState *pcms, } if (val > 65535) val = 65535; - rtc_set_memory(s, 0x17, val); - rtc_set_memory(s, 0x18, val >> 8); - rtc_set_memory(s, 0x30, val); - rtc_set_memory(s, 0x31, val >> 8); + mc146818rtc_set_cmos_data(s, 0x17, val); + mc146818rtc_set_cmos_data(s, 0x18, val >> 8); + mc146818rtc_set_cmos_data(s, 0x30, val); + mc146818rtc_set_cmos_data(s, 0x31, val >> 8); /* memory between 16MiB and 4GiB */ if (x86ms->below_4g_mem_size > 16 * MiB) { val = (x86ms->below_4g_mem_size - 16 * MiB) / (64 * KiB); @@ -660,34 +598,18 @@ void pc_cmos_init(PCMachineState *pcms, } if (val > 65535) val = 65535; - rtc_set_memory(s, 0x34, val); - rtc_set_memory(s, 0x35, val >> 8); + mc146818rtc_set_cmos_data(s, 0x34, val); + mc146818rtc_set_cmos_data(s, 0x35, val >> 8); /* memory above 4GiB */ val = x86ms->above_4g_mem_size / 65536; - rtc_set_memory(s, 0x5b, val); - rtc_set_memory(s, 0x5c, val >> 8); - rtc_set_memory(s, 0x5d, val >> 16); - - object_property_add_link(OBJECT(pcms), "rtc_state", - TYPE_ISA_DEVICE, - (Object **)&x86ms->rtc, - object_property_allow_set_link, - OBJ_PROP_LINK_STRONG); - object_property_set_link(OBJECT(pcms), "rtc_state", OBJECT(s), - &error_abort); - - set_boot_dev(s, MACHINE(pcms)->boot_config.order, &error_fatal); + mc146818rtc_set_cmos_data(s, 0x5b, val); + mc146818rtc_set_cmos_data(s, 0x5c, val >> 8); + mc146818rtc_set_cmos_data(s, 0x5d, val >> 16); val = 0; val |= 0x02; /* FPU is there */ val |= 0x04; /* PS/2 mouse installed */ - rtc_set_memory(s, REG_EQUIPMENT_BYTE, val); - - /* hard drives and FDC */ - arg.rtc_state = s; - arg.idebus[0] = idebus0; - arg.idebus[1] = idebus1; - qemu_register_reset(pc_cmos_init_late, &arg); + mc146818rtc_set_cmos_data(s, REG_EQUIPMENT_BYTE, val); } static void handle_a20_line_change(void *opaque, int irq, int level) @@ -705,15 +627,19 @@ static const int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 }; static const int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 }; -void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd) +static gboolean pc_init_ne2k_isa(ISABus *bus, NICInfo *nd, Error **errp) { static int nb_ne2k = 0; - if (nb_ne2k == NE2000_NB_MAX) - return; + if (nb_ne2k == NE2000_NB_MAX) { + error_setg(errp, + "maximum number of ISA NE2000 devices exceeded"); + return false; + } isa_ne2000_init(bus, ne2000_io[nb_ne2k], ne2000_irq[nb_ne2k], nd); nb_ne2k++; + return true; } void pc_acpi_smi_interrupt(void *opaque, int irq, int level) @@ -732,31 +658,31 @@ void pc_machine_done(Notifier *notifier, void *data) PCMachineState, machine_done); X86MachineState *x86ms = X86_MACHINE(pcms); + cxl_hook_up_pxb_registers(pcms->pcibus, &pcms->cxl_devices_state, + &error_fatal); + + if (pcms->cxl_devices_state.is_enabled) { + cxl_fmws_link_targets(&pcms->cxl_devices_state, &error_fatal); + } + /* set the number of CPUs */ x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); - fw_cfg_add_extra_pci_roots(pcms->bus, x86ms->fw_cfg); + fw_cfg_add_extra_pci_roots(pcms->pcibus, x86ms->fw_cfg); acpi_setup(); if (x86ms->fw_cfg) { - fw_cfg_build_smbios(MACHINE(pcms), x86ms->fw_cfg); + fw_cfg_build_smbios(pcms, x86ms->fw_cfg, pcms->smbios_entry_point_type); fw_cfg_build_feature_control(MACHINE(pcms), x86ms->fw_cfg); /* update FW_CFG_NB_CPUS to account for -device added CPUs */ fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); } -} -void pc_guest_info_init(PCMachineState *pcms) -{ - X86MachineState *x86ms = X86_MACHINE(pcms); - - x86ms->apic_xrupt_override = true; - pcms->machine_done.notify = pc_machine_done; - qemu_add_machine_init_done_notifier(&pcms->machine_done); + pc_cmos_init_late(pcms); } /* setup pci memory address space mapping into system address space */ -void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory, +void pc_pci_as_mapping_init(MemoryRegion *system_memory, MemoryRegion *pci_address_space) { /* Set to lower priority than RAM */ @@ -773,7 +699,8 @@ void xen_load_linux(PCMachineState *pcms) assert(MACHINE(pcms)->kernel_filename != NULL); - fw_cfg = fw_cfg_init_io(FW_CFG_IO_BASE); + fw_cfg = fw_cfg_init_io_dma(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4, + &address_space_memory); fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); rom_set_fw(fw_cfg); @@ -796,10 +723,149 @@ void xen_load_linux(PCMachineState *pcms) #define PC_ROM_ALIGN 0x800 #define PC_ROM_SIZE (PC_ROM_MAX - PC_ROM_MIN_VGA) +static hwaddr pc_above_4g_end(PCMachineState *pcms) +{ + X86MachineState *x86ms = X86_MACHINE(pcms); + + if (pcms->sgx_epc.size != 0) { + return sgx_epc_above_4g_end(&pcms->sgx_epc); + } + + return x86ms->above_4g_mem_start + x86ms->above_4g_mem_size; +} + +static void pc_get_device_memory_range(PCMachineState *pcms, + hwaddr *base, + ram_addr_t *device_mem_size) +{ + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + MachineState *machine = MACHINE(pcms); + ram_addr_t size; + hwaddr addr; + + size = machine->maxram_size - machine->ram_size; + addr = ROUND_UP(pc_above_4g_end(pcms), 1 * GiB); + + if (pcmc->enforce_aligned_dimm) { + /* size device region assuming 1G page max alignment per slot */ + size += (1 * GiB) * machine->ram_slots; + } + + *base = addr; + *device_mem_size = size; +} + +static uint64_t pc_get_cxl_range_start(PCMachineState *pcms) +{ + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + MachineState *ms = MACHINE(pcms); + hwaddr cxl_base; + ram_addr_t size; + + if (pcmc->has_reserved_memory && + (ms->ram_size < ms->maxram_size)) { + pc_get_device_memory_range(pcms, &cxl_base, &size); + cxl_base += size; + } else { + cxl_base = pc_above_4g_end(pcms); + } + + return cxl_base; +} + +static uint64_t pc_get_cxl_range_end(PCMachineState *pcms) +{ + uint64_t start = pc_get_cxl_range_start(pcms) + MiB; + + if (pcms->cxl_devices_state.fixed_windows) { + GList *it; + + start = ROUND_UP(start, 256 * MiB); + for (it = pcms->cxl_devices_state.fixed_windows; it; it = it->next) { + CXLFixedWindow *fw = it->data; + start += fw->size; + } + } + + return start; +} + +static hwaddr pc_max_used_gpa(PCMachineState *pcms, uint64_t pci_hole64_size) +{ + X86CPU *cpu = X86_CPU(first_cpu); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + MachineState *ms = MACHINE(pcms); + + if (cpu->env.features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { + /* 64-bit systems */ + return pc_pci_hole64_start() + pci_hole64_size - 1; + } + + /* 32-bit systems */ + if (pcmc->broken_32bit_mem_addr_check) { + /* old value for compatibility reasons */ + return ((hwaddr)1 << cpu->phys_bits) - 1; + } + + /* + * 32-bit systems don't have hole64 but they might have a region for + * memory devices. Even if additional hotplugged memory devices might + * not be usable by most guest OSes, we need to still consider them for + * calculating the highest possible GPA so that we can properly report + * if someone configures them on a CPU that cannot possibly address them. + */ + if (pcmc->has_reserved_memory && + (ms->ram_size < ms->maxram_size)) { + hwaddr devmem_start; + ram_addr_t devmem_size; + + pc_get_device_memory_range(pcms, &devmem_start, &devmem_size); + devmem_start += devmem_size; + return devmem_start - 1; + } + + /* configuration without any memory hotplug */ + return pc_above_4g_end(pcms) - 1; +} + +/* + * AMD systems with an IOMMU have an additional hole close to the + * 1Tb, which are special GPAs that cannot be DMA mapped. Depending + * on kernel version, VFIO may or may not let you DMA map those ranges. + * Starting Linux v5.4 we validate it, and can't create guests on AMD machines + * with certain memory sizes. It's also wrong to use those IOVA ranges + * in detriment of leading to IOMMU INVALID_DEVICE_REQUEST or worse. + * The ranges reserved for Hyper-Transport are: + * + * FD_0000_0000h - FF_FFFF_FFFFh + * + * The ranges represent the following: + * + * Base Address Top Address Use + * + * FD_0000_0000h FD_F7FF_FFFFh Reserved interrupt address space + * FD_F800_0000h FD_F8FF_FFFFh Interrupt/EOI IntCtl + * FD_F900_0000h FD_F90F_FFFFh Legacy PIC IACK + * FD_F910_0000h FD_F91F_FFFFh System Management + * FD_F920_0000h FD_FAFF_FFFFh Reserved Page Tables + * FD_FB00_0000h FD_FBFF_FFFFh Address Translation + * FD_FC00_0000h FD_FDFF_FFFFh I/O Space + * FD_FE00_0000h FD_FFFF_FFFFh Configuration + * FE_0000_0000h FE_1FFF_FFFFh Extended Configuration/Device Messages + * FE_2000_0000h FF_FFFF_FFFFh Reserved + * + * See AMD IOMMU spec, section 2.1.2 "IOMMU Logical Topology", + * Table 3: Special Address Controls (GPA) for more information. + */ +#define AMD_HT_START 0xfd00000000UL +#define AMD_HT_END 0xffffffffffUL +#define AMD_ABOVE_1TB_START (AMD_HT_END + 1) +#define AMD_HT_SIZE (AMD_ABOVE_1TB_START - AMD_HT_START) + void pc_memory_init(PCMachineState *pcms, MemoryRegion *system_memory, MemoryRegion *rom_memory, - MemoryRegion **ram_memory) + uint64_t pci_hole64_size) { int linux_boot, i; MemoryRegion *option_rom_mr; @@ -809,18 +875,53 @@ void pc_memory_init(PCMachineState *pcms, MachineClass *mc = MACHINE_GET_CLASS(machine); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); X86MachineState *x86ms = X86_MACHINE(pcms); + hwaddr maxphysaddr, maxusedaddr; hwaddr cxl_base, cxl_resv_end = 0; + X86CPU *cpu = X86_CPU(first_cpu); assert(machine->ram_size == x86ms->below_4g_mem_size + x86ms->above_4g_mem_size); linux_boot = (machine->kernel_filename != NULL); + /* + * The HyperTransport range close to the 1T boundary is unique to AMD + * hosts with IOMMUs enabled. Restrict the ram-above-4g relocation + * to above 1T to AMD vCPUs only. @enforce_amd_1tb_hole is only false in + * older machine types (<= 7.0) for compatibility purposes. + */ + if (IS_AMD_CPU(&cpu->env) && pcmc->enforce_amd_1tb_hole) { + /* Bail out if max possible address does not cross HT range */ + if (pc_max_used_gpa(pcms, pci_hole64_size) >= AMD_HT_START) { + x86ms->above_4g_mem_start = AMD_ABOVE_1TB_START; + } + + /* + * Advertise the HT region if address space covers the reserved + * region or if we relocate. + */ + if (cpu->phys_bits >= 40) { + e820_add_entry(AMD_HT_START, AMD_HT_SIZE, E820_RESERVED); + } + } + + /* + * phys-bits is required to be appropriately configured + * to make sure max used GPA is reachable. + */ + maxusedaddr = pc_max_used_gpa(pcms, pci_hole64_size); + maxphysaddr = ((hwaddr)1 << cpu->phys_bits) - 1; + if (maxphysaddr < maxusedaddr) { + error_report("Address space limit 0x%"PRIx64" < 0x%"PRIx64 + " phys-bits too low (%u)", + maxphysaddr, maxusedaddr, cpu->phys_bits); + exit(EXIT_FAILURE); + } + /* * Split single memory region and use aliases to address portions of it, * done for backwards compatibility with older qemus. */ - *ram_memory = machine->ram; ram_below_4g = g_malloc(sizeof(*ram_below_4g)); memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", machine->ram, 0, x86ms->below_4g_mem_size); @@ -832,9 +933,10 @@ void pc_memory_init(PCMachineState *pcms, machine->ram, x86ms->below_4g_mem_size, x86ms->above_4g_mem_size); - memory_region_add_subregion(system_memory, 0x100000000ULL, + memory_region_add_subregion(system_memory, x86ms->above_4g_mem_start, ram_above_4g); - e820_add_entry(0x100000000ULL, x86ms->above_4g_mem_size, E820_RAM); + e820_add_entry(x86ms->above_4g_mem_start, x86ms->above_4g_mem_size, + E820_RAM); } if (pcms->sgx_epc.size != 0) { @@ -850,13 +952,11 @@ void pc_memory_init(PCMachineState *pcms, exit(EXIT_FAILURE); } - /* always allocate the device memory information */ - machine->device_memory = g_malloc0(sizeof(*machine->device_memory)); - /* initialize device memory address space */ if (pcmc->has_reserved_memory && (machine->ram_size < machine->maxram_size)) { - ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size; + ram_addr_t device_mem_size; + hwaddr device_mem_base; if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) { error_report("unsupported amount of memory slots: %"PRIu64, @@ -871,66 +971,36 @@ void pc_memory_init(PCMachineState *pcms, exit(EXIT_FAILURE); } - if (pcms->sgx_epc.size != 0) { - machine->device_memory->base = sgx_epc_above_4g_end(&pcms->sgx_epc); - } else { - machine->device_memory->base = - 0x100000000ULL + x86ms->above_4g_mem_size; - } + pc_get_device_memory_range(pcms, &device_mem_base, &device_mem_size); - machine->device_memory->base = - ROUND_UP(machine->device_memory->base, 1 * GiB); - - if (pcmc->enforce_aligned_dimm) { - /* size device region assuming 1G page max alignment per slot */ - device_mem_size += (1 * GiB) * machine->ram_slots; - } - - if ((machine->device_memory->base + device_mem_size) < - device_mem_size) { + if (device_mem_base + device_mem_size < device_mem_size) { error_report("unsupported amount of maximum memory: " RAM_ADDR_FMT, machine->maxram_size); exit(EXIT_FAILURE); } - - memory_region_init(&machine->device_memory->mr, OBJECT(pcms), - "device-memory", device_mem_size); - memory_region_add_subregion(system_memory, machine->device_memory->base, - &machine->device_memory->mr); + machine_memory_devices_init(machine, device_mem_base, device_mem_size); } - if (machine->cxl_devices_state->is_enabled) { - MemoryRegion *mr = &machine->cxl_devices_state->host_mr; + if (pcms->cxl_devices_state.is_enabled) { + MemoryRegion *mr = &pcms->cxl_devices_state.host_mr; hwaddr cxl_size = MiB; - if (pcmc->has_reserved_memory && machine->device_memory->base) { - cxl_base = machine->device_memory->base; - if (!pcmc->broken_reserved_end) { - cxl_base += memory_region_size(&machine->device_memory->mr); - } - } else if (pcms->sgx_epc.size != 0) { - cxl_base = sgx_epc_above_4g_end(&pcms->sgx_epc); - } else { - cxl_base = 0x100000000ULL + x86ms->above_4g_mem_size; - } - - e820_add_entry(cxl_base, cxl_size, E820_RESERVED); + cxl_base = pc_get_cxl_range_start(pcms); memory_region_init(mr, OBJECT(machine), "cxl_host_reg", cxl_size); memory_region_add_subregion(system_memory, cxl_base, mr); cxl_resv_end = cxl_base + cxl_size; - if (machine->cxl_devices_state->fixed_windows) { + if (pcms->cxl_devices_state.fixed_windows) { hwaddr cxl_fmw_base; GList *it; cxl_fmw_base = ROUND_UP(cxl_base + cxl_size, 256 * MiB); - for (it = machine->cxl_devices_state->fixed_windows; it; it = it->next) { + for (it = pcms->cxl_devices_state.fixed_windows; it; it = it->next) { CXLFixedWindow *fw = it->data; fw->base = cxl_fmw_base; memory_region_init_io(&fw->mr, OBJECT(machine), &cfmws_ops, fw, "cxl-fixed-memory-region", fw->size); memory_region_add_subregion(system_memory, fw->base, &fw->mr); - e820_add_entry(fw->base, fw->size, E820_RESERVED); cxl_fmw_base += fw->size; cxl_resv_end = cxl_fmw_base; } @@ -956,16 +1026,15 @@ void pc_memory_init(PCMachineState *pcms, rom_set_fw(fw_cfg); - if (pcmc->has_reserved_memory && machine->device_memory->base) { + if (machine->device_memory) { uint64_t *val = g_malloc(sizeof(*val)); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); uint64_t res_mem_end = machine->device_memory->base; if (!pcmc->broken_reserved_end) { res_mem_end += memory_region_size(&machine->device_memory->mr); } - if (machine->cxl_devices_state->is_enabled) { + if (pcms->cxl_devices_state.is_enabled) { res_mem_end = cxl_resv_end; } *val = cpu_to_le64(ROUND_UP(res_mem_end, 1 * GiB)); @@ -998,28 +1067,18 @@ uint64_t pc_pci_hole64_start(void) PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); MachineState *ms = MACHINE(pcms); - X86MachineState *x86ms = X86_MACHINE(pcms); uint64_t hole64_start = 0; + ram_addr_t size = 0; - if (ms->cxl_devices_state->host_mr.addr) { - hole64_start = ms->cxl_devices_state->host_mr.addr + - memory_region_size(&ms->cxl_devices_state->host_mr); - if (ms->cxl_devices_state->fixed_windows) { - GList *it; - for (it = ms->cxl_devices_state->fixed_windows; it; it = it->next) { - CXLFixedWindow *fw = it->data; - hole64_start = fw->mr.addr + memory_region_size(&fw->mr); - } - } - } else if (pcmc->has_reserved_memory && ms->device_memory->base) { - hole64_start = ms->device_memory->base; + if (pcms->cxl_devices_state.is_enabled) { + hole64_start = pc_get_cxl_range_end(pcms); + } else if (pcmc->has_reserved_memory && (ms->ram_size < ms->maxram_size)) { + pc_get_device_memory_range(pcms, &hole64_start, &size); if (!pcmc->broken_reserved_end) { - hole64_start += memory_region_size(&ms->device_memory->mr); + hole64_start += size; } - } else if (pcms->sgx_epc.size != 0) { - hole64_start = sgx_epc_above_4g_end(&pcms->sgx_epc); } else { - hole64_start = 0x100000000ULL + x86ms->above_4g_mem_size; + hole64_start = pc_above_4g_end(pcms); } return ROUND_UP(hole64_start, 1 * GiB); @@ -1088,7 +1147,7 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, return; } - i8042 = isa_create_simple(isa_bus, "i8042"); + i8042 = isa_create_simple(isa_bus, TYPE_I8042); if (!no_vmport) { isa_create_simple(isa_bus, TYPE_VMPORT); vmmouse = isa_try_new("vmmouse"); @@ -1096,14 +1155,15 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, vmmouse = NULL; } if (vmmouse) { - object_property_set_link(OBJECT(vmmouse), "i8042", OBJECT(i8042), + object_property_set_link(OBJECT(vmmouse), TYPE_I8042, OBJECT(i8042), &error_abort); isa_realize_and_unref(vmmouse, isa_bus, &error_fatal); } 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]); + qdev_connect_gpio_out_named(DEVICE(i8042), + I8042_A20_LINE, 0, a20_line[0]); qdev_connect_gpio_out_named(DEVICE(port92), PORT92_A20_LINE, 0, a20_line[1]); g_free(a20_line); @@ -1111,7 +1171,7 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, void pc_basic_device_init(struct PCMachineState *pcms, ISABus *isa_bus, qemu_irq *gsi, - ISADevice **rtc_state, + ISADevice *rtc_state, bool create_fdctrl, uint32_t hpet_irqs) { @@ -1119,7 +1179,6 @@ void pc_basic_device_init(struct PCMachineState *pcms, DeviceState *hpet = NULL; int pit_isa_irq = 0; qemu_irq pit_alt_irq = NULL; - qemu_irq rtc_irq = NULL; ISADevice *pit = NULL; MemoryRegion *ioport80_io = g_new(MemoryRegion, 1); MemoryRegion *ioportF0_io = g_new(MemoryRegion, 1); @@ -1133,21 +1192,19 @@ void pc_basic_device_init(struct PCMachineState *pcms, /* * Check if an HPET shall be created. - * - * Without KVM_CAP_PIT_STATE2, we cannot switch off the in-kernel PIT - * when the HPET wants to take over. Thus we have to disable the latter. */ - if (pcms->hpet_enabled && (!kvm_irqchip_in_kernel() || - kvm_has_pit_state2())) { + if (pcms->hpet_enabled) { + qemu_irq rtc_irq; + hpet = qdev_try_new(TYPE_HPET); if (!hpet) { error_report("couldn't create HPET device"); exit(1); } /* - * For pc-piix-*, hpet's intcap is always IRQ2. For pc-q35-1.7 and - * earlier, use IRQ2 for compat. Otherwise, use IRQ16~23, IRQ8 and - * IRQ2. + * For pc-piix-*, hpet's intcap is always IRQ2. For pc-q35-*, + * use IRQ16~23, IRQ8 and IRQ2. If the user has already set + * the property, use whatever mask they specified. */ uint8_t compat = object_property_get_uint(OBJECT(hpet), HPET_INTCAP, NULL); @@ -1157,16 +1214,37 @@ void pc_basic_device_init(struct PCMachineState *pcms, sysbus_realize_and_unref(SYS_BUS_DEVICE(hpet), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(hpet), 0, HPET_BASE); - for (i = 0; i < GSI_NUM_PINS; i++) { + for (i = 0; i < IOAPIC_NUM_PINS; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(hpet), i, gsi[i]); } pit_isa_irq = -1; pit_alt_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_PIT_INT); rtc_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_RTC_INT); - } - *rtc_state = mc146818_rtc_init(isa_bus, 2000, rtc_irq); - qemu_register_boot_set(pc_boot_set, *rtc_state); + /* overwrite connection created by south bridge */ + qdev_connect_gpio_out(DEVICE(rtc_state), 0, rtc_irq); + } + + object_property_add_alias(OBJECT(pcms), "rtc-time", OBJECT(rtc_state), + "date"); + +#ifdef CONFIG_XEN_EMU + if (xen_mode == XEN_EMULATE) { + xen_overlay_create(); + xen_evtchn_create(IOAPIC_NUM_PINS, gsi); + xen_gnttab_create(); + xen_xenstore_create(); + if (pcms->pcibus) { + pci_create_simple(pcms->pcibus, -1, "xen-platform"); + } + xen_bus_init(); + xen_be_init(); + } +#endif + + qemu_register_boot_set(pc_boot_set, pcms); + set_boot_dev(pcms, MC146818_RTC(rtc_state), + MACHINE(pcms)->boot_config.order, &error_fatal); if (!xen_enabled() && (x86ms->pit == ON_OFF_AUTO_AUTO || x86ms->pit == ON_OFF_AUTO_ON)) { @@ -1179,11 +1257,11 @@ void pc_basic_device_init(struct PCMachineState *pcms, /* connect PIT to output control line of the HPET */ qdev_connect_gpio_out(hpet, 0, qdev_get_gpio_in(DEVICE(pit), 0)); } - pcspk_init(pcms->pcspk, isa_bus, pit); + object_property_set_link(OBJECT(pcms->pcspk), "pit", + OBJECT(pit), &error_fatal); + isa_realize_and_unref(pcms->pcspk, isa_bus, &error_fatal); } - i8257_dma_init(isa_bus, 0); - /* Super I/O */ pc_superio_init(isa_bus, create_fdctrl, pcms->i8042_enabled, pcms->vmport != ON_OFF_AUTO_ON); @@ -1191,19 +1269,19 @@ void pc_basic_device_init(struct PCMachineState *pcms, void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus) { - int i; + MachineClass *mc = MACHINE_CLASS(pcmc); + bool default_is_ne2k = g_str_equal(mc->default_nic, TYPE_ISA_NE2000); + NICInfo *nd; rom_set_order_override(FW_CFG_ORDER_OVERRIDE_NIC); - for (i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - const char *model = nd->model ? nd->model : pcmc->default_nic_model; - if (g_str_equal(model, "ne2k_isa")) { - pc_init_ne2k_isa(isa_bus, nd); - } else { - pci_nic_init_nofail(nd, pci_bus, model, NULL); - } + while ((nd = qemu_find_nic_info(TYPE_ISA_NE2000, default_is_ne2k, NULL))) { + pc_init_ne2k_isa(isa_bus, nd, &error_fatal); } + + /* Anything remaining should be a PCI NIC */ + pci_init_nic_devices(pci_bus, mc->default_nic); + rom_reset_order_override(); } @@ -1238,7 +1316,7 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error *local_err = NULL; /* - * When -no-acpi is used with Q35 machine type, no ACPI is built, + * When "acpi=off" is used with the Q35 machine type, no ACPI is built, * but pcms->acpi_dev is still created. Check !acpi_enabled in * addition to cover this case. */ @@ -1286,7 +1364,7 @@ static void pc_memory_unplug_request(HotplugHandler *hotplug_dev, X86MachineState *x86ms = X86_MACHINE(hotplug_dev); /* - * When -no-acpi is used with Q35 machine type, no ACPI is built, + * When "acpi=off" is used with the Q35 machine type, no ACPI is built, * but pcms->acpi_dev is still created. Check !acpi_enabled in * addition to cover this case. */ @@ -1323,66 +1401,19 @@ static void pc_memory_unplug(HotplugHandler *hotplug_dev, error_propagate(errp, local_err); } -static void pc_virtio_md_pci_pre_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void pc_hv_balloon_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - HotplugHandler *hotplug_dev2 = qdev_get_bus_hotplug_handler(dev); - Error *local_err = NULL; - - if (!hotplug_dev2 && dev->hotplugged) { - /* - * Without a bus hotplug handler, we cannot control the plug/unplug - * order. We should never reach this point when hotplugging on x86, - * however, better add a safety net. - */ - error_setg(errp, "hotplug of virtio based memory devices not supported" - " on this bus."); - return; - } - /* - * First, see if we can plug this memory device at all. If that - * succeeds, branch of to the actual hotplug handler. - */ + /* The vmbus handler has no hotplug handler; we should never end up here. */ + g_assert(!dev->hotplugged); memory_device_pre_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev), NULL, - &local_err); - if (!local_err && hotplug_dev2) { - hotplug_handler_pre_plug(hotplug_dev2, dev, &local_err); - } - error_propagate(errp, local_err); + errp); } -static void pc_virtio_md_pci_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void pc_hv_balloon_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - HotplugHandler *hotplug_dev2 = qdev_get_bus_hotplug_handler(dev); - Error *local_err = NULL; - - /* - * Plug the memory device first and then branch off to the actual - * hotplug handler. If that one fails, we can easily undo the memory - * device bits. - */ memory_device_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev)); - if (hotplug_dev2) { - hotplug_handler_plug(hotplug_dev2, dev, &local_err); - if (local_err) { - memory_device_unplug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev)); - } - } - error_propagate(errp, local_err); -} - -static void pc_virtio_md_pci_unplug_request(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - /* We don't support hot unplug of virtio based memory devices */ - error_setg(errp, "virtio based memory devices cannot be unplugged."); -} - -static void pc_virtio_md_pci_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - /* We don't support hot unplug of virtio based memory devices */ } static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, @@ -1392,17 +1423,17 @@ static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, pc_memory_pre_plug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { x86_cpu_pre_plug(hotplug_dev, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) || - object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) { - pc_virtio_md_pci_pre_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { + virtio_md_pci_pre_plug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { /* Declare the APIC range as the reserved MSI region */ char *resv_prop_str = g_strdup_printf("0xfee00000:0xfeefffff:%d", VIRTIO_IOMMU_RESV_MEM_T_MSI); + QList *reserved_regions = qlist_new(); + + qlist_append_str(reserved_regions, resv_prop_str); + qdev_prop_set_array(dev, "reserved-regions", reserved_regions); - object_property_set_uint(OBJECT(dev), "len-reserved-regions", 1, errp); - object_property_set_str(OBJECT(dev), "reserved-regions[0]", - resv_prop_str, errp); g_free(resv_prop_str); } @@ -1416,6 +1447,8 @@ static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, return; } pcms->iommu = dev; + } else if (object_dynamic_cast(OBJECT(dev), TYPE_HV_BALLOON)) { + pc_hv_balloon_pre_plug(hotplug_dev, dev, errp); } } @@ -1426,9 +1459,10 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev, pc_memory_plug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { x86_cpu_plug(hotplug_dev, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) || - object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) { - pc_virtio_md_pci_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { + virtio_md_pci_plug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_HV_BALLOON)) { + pc_hv_balloon_plug(hotplug_dev, dev, errp); } } @@ -1439,9 +1473,9 @@ static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev, pc_memory_unplug_request(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { x86_cpu_unplug_request_cb(hotplug_dev, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) || - object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) { - pc_virtio_md_pci_unplug_request(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { + virtio_md_pci_unplug_request(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), + errp); } else { error_setg(errp, "acpi: device unplug request for not supported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -1455,9 +1489,8 @@ static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev, pc_memory_unplug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { x86_cpu_unplug_cb(hotplug_dev, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) || - object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) { - pc_virtio_md_pci_unplug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { + virtio_md_pci_unplug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp); } else { error_setg(errp, "acpi: device unplug for not supported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -1469,9 +1502,9 @@ static HotplugHandler *pc_get_hotplug_handler(MachineState *machine, { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || object_dynamic_cast(OBJECT(dev), TYPE_CPU) || - object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) || - object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI) || object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) || + object_dynamic_cast(OBJECT(dev), TYPE_HV_BALLOON) || object_dynamic_cast(OBJECT(dev), TYPE_X86_IOMMU_DEVICE)) { return HOTPLUG_HANDLER(machine); } @@ -1479,21 +1512,6 @@ static HotplugHandler *pc_get_hotplug_handler(MachineState *machine, return NULL; } -static void -pc_machine_get_device_memory_region_size(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - MachineState *ms = MACHINE(obj); - int64_t value = 0; - - if (ms->device_memory) { - value = memory_region_size(&ms->device_memory->mr); - } - - visit_type_int(v, name, &value, errp); -} - static void pc_machine_get_vmport(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -1511,6 +1529,20 @@ static void pc_machine_set_vmport(Object *obj, Visitor *v, const char *name, visit_type_OnOffAuto(v, name, &pcms->vmport, errp); } +static bool pc_machine_get_fd_bootchk(Object *obj, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + + return pcms->fd_bootchk; +} + +static void pc_machine_set_fd_bootchk(Object *obj, bool value, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + + pcms->fd_bootchk = value; +} + static bool pc_machine_get_smbus(Object *obj, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); @@ -1649,26 +1681,23 @@ static void pc_machine_set_max_fw_size(Object *obj, Visitor *v, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); - Error *error = NULL; uint64_t value; - visit_type_size(v, name, &value, &error); - if (error) { - error_propagate(errp, error); + if (!visit_type_size(v, name, &value, errp)) { return; } /* - * We don't have a theoretically justifiable exact lower bound on the base - * address of any flash mapping. In practice, the IO-APIC MMIO range is - * [0xFEE00000..0xFEE01000] -- see IO_APIC_DEFAULT_ADDRESS --, leaving free - * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in - * size. - */ + * We don't have a theoretically justifiable exact lower bound on the base + * address of any flash mapping. In practice, the IO-APIC MMIO range is + * [0xFEE00000..0xFEE01000] -- see IO_APIC_DEFAULT_ADDRESS --, leaving free + * only 18MiB-4KiB below 4GiB. For now, restrict the cumulative mapping to + * 16MiB in size. + */ if (value > 16 * MiB) { error_setg(errp, "User specified max allowed firmware size %" PRIu64 " is " - "greater than 16MiB. If combined firwmare size exceeds " + "greater than 16MiB. If combined firmware size exceeds " "16MiB the system may not boot, or experience intermittent" "stability issues.", value); @@ -1682,6 +1711,7 @@ static void pc_machine_set_max_fw_size(Object *obj, Visitor *v, static void pc_machine_initfn(Object *obj) { PCMachineState *pcms = PC_MACHINE(obj); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); #ifdef CONFIG_VMPORT pcms->vmport = ON_OFF_AUTO_AUTO; @@ -1689,10 +1719,11 @@ static void pc_machine_initfn(Object *obj) pcms->vmport = ON_OFF_AUTO_OFF; #endif /* CONFIG_VMPORT */ pcms->max_ram_below_4g = 0; /* use default */ - pcms->smbios_entry_point_type = SMBIOS_ENTRY_POINT_TYPE_32; + pcms->smbios_entry_point_type = pcmc->default_smbios_ep_type; + pcms->south_bridge = pcmc->default_south_bridge; /* acpi build is enabled by default if machine supports it */ - pcms->acpi_build_enabled = PC_MACHINE_GET_CLASS(pcms)->has_acpi_build; + pcms->acpi_build_enabled = pcmc->has_acpi_build; pcms->smbus_enabled = true; pcms->sata_enabled = true; pcms->i8042_enabled = true; @@ -1700,20 +1731,27 @@ static void pc_machine_initfn(Object *obj) #ifdef CONFIG_HPET pcms->hpet_enabled = true; #endif + pcms->fd_bootchk = true; pcms->default_bus_bypass_iommu = false; pc_system_flash_create(pcms); pcms->pcspk = isa_new(TYPE_PC_SPEAKER); object_property_add_alias(OBJECT(pcms), "pcspk-audiodev", OBJECT(pcms->pcspk), "audiodev"); + if (pcmc->pci_enabled) { + cxl_machine_init(obj, &pcms->cxl_devices_state); + } + + pcms->machine_done.notify = pc_machine_done; + qemu_add_machine_init_done_notifier(&pcms->machine_done); } -static void pc_machine_reset(MachineState *machine) +static void pc_machine_reset(MachineState *machine, ShutdownCause reason) { CPUState *cs; X86CPU *cpu; - qemu_devices_reset(); + qemu_devices_reset(reason); /* Reset APIC after devices have been reset to cancel * any changes that qemu_devices_reset() might have done. @@ -1721,16 +1759,14 @@ static void pc_machine_reset(MachineState *machine) CPU_FOREACH(cs) { cpu = X86_CPU(cs); - if (cpu->apic_state) { - device_legacy_reset(cpu->apic_state); - } + x86_cpu_after_reset(cpu); } } static void pc_machine_wakeup(MachineState *machine) { cpu_synchronize_all_states(); - pc_machine_reset(machine); + pc_machine_reset(machine, SHUTDOWN_CAUSE_NONE); cpu_synchronize_all_post_reset(); } @@ -1756,6 +1792,7 @@ static bool pc_hotplug_allowed(MachineState *ms, DeviceState *dev, Error **errp) static void pc_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + X86MachineClass *x86mc = X86_MACHINE_CLASS(oc); PCMachineClass *pcmc = PC_MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); @@ -1766,13 +1803,15 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) pcmc->smbios_uuid_encoded = true; pcmc->gigabyte_align = true; pcmc->has_reserved_memory = true; - pcmc->kvmclock_enabled = true; pcmc->enforce_aligned_dimm = true; + pcmc->enforce_amd_1tb_hole = true; /* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported * to be used at the moment, 32K should be enough for a while. */ pcmc->acpi_data_size = 0x20000 + 0x8000; pcmc->pvh_enabled = true; pcmc->kvmclock_create_always = true; + pcmc->resizable_acpi_blob = true; + x86mc->apic_xrupt_override = true; assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = pc_get_hotplug_handler; mc->hotplug_allowed = pc_hotplug_allowed; @@ -1794,8 +1833,8 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; mc->nvdimm_supported = true; mc->smp_props.dies_supported = true; - mc->cxl_supported = true; mc->default_ram_id = "pc.ram"; + pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_AUTO; object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size", pc_machine_get_max_ram_below_4g, pc_machine_set_max_ram_below_4g, @@ -1803,10 +1842,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "Maximum ram below the 4G boundary (32bit boundary)"); - object_class_property_add(oc, PC_MACHINE_DEVMEM_REGION_SIZE, "int", - pc_machine_get_device_memory_region_size, NULL, - NULL, NULL); - object_class_property_add(oc, PC_MACHINE_VMPORT, "OnOffAuto", pc_machine_get_vmport, pc_machine_set_vmport, NULL, NULL); @@ -1846,6 +1881,10 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) NULL, NULL); object_class_property_set_description(oc, PC_MACHINE_SMBIOS_EP, "SMBIOS Entry Point type [32, 64]"); + + object_class_property_add_bool(oc, "fd-bootchk", + pc_machine_get_fd_bootchk, + pc_machine_set_fd_bootchk); } static const TypeInfo pc_machine_info = { diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 0e45521e74..18ba076609 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -26,25 +26,27 @@ #include CONFIG_DEVICES #include "qemu/units.h" +#include "hw/char/parallel-isa.h" +#include "hw/dma/i8257.h" #include "hw/loader.h" #include "hw/i386/x86.h" #include "hw/i386/pc.h" #include "hw/i386/apic.h" #include "hw/pci-host/i440fx.h" +#include "hw/rtc/mc146818rtc.h" #include "hw/southbridge/piix.h" #include "hw/display/ramfb.h" -#include "hw/firmware/smbios.h" #include "hw/pci/pci.h" #include "hw/pci/pci_ids.h" #include "hw/usb.h" #include "net/net.h" +#include "hw/ide/isa.h" #include "hw/ide/pci.h" #include "hw/irq.h" #include "sysemu/kvm.h" -#include "hw/kvm/clock.h" +#include "hw/i386/kvm/clock.h" #include "hw/sysbus.h" #include "hw/i2c/smbus_eeprom.h" -#include "hw/xen/xen-x86.h" #include "exec/memory.h" #include "hw/acpi/acpi.h" #include "qapi/error.h" @@ -53,16 +55,21 @@ #ifdef CONFIG_XEN #include #include "hw/xen/xen_pt.h" +#include "hw/xen/xen_igd.h" #endif +#include "hw/xen/xen-x86.h" +#include "hw/xen/xen.h" #include "migration/global_state.h" #include "migration/misc.h" +#include "sysemu/runstate.h" #include "sysemu/numa.h" #include "hw/hyperv/vmbus-bridge.h" #include "hw/mem/nvdimm.h" #include "hw/i386/acpi-build.h" #include "kvm/kvm-cpu.h" +#include "target/i386/cpu.h" -#define MAX_IDE_BUS 2 +#define XEN_IOAPIC_NUM_PIRQS 128ULL #ifdef CONFIG_IDE_ISA static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 }; @@ -70,27 +77,47 @@ static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; #endif +/* + * Return the global irq number corresponding to a given device irq + * pin. We could also use the bus number to have a more precise mapping. + */ +static int pc_pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx) +{ + int slot_addend; + slot_addend = PCI_SLOT(pci_dev->devfn) - 1; + return (pci_intx + slot_addend) & 3; +} + +static void piix_intx_routing_notifier_xen(PCIDevice *dev) +{ + int i; + + /* Scan for updates to PCI link routes. */ + for (i = 0; i < PIIX_NUM_PIRQS; i++) { + const PCIINTxRoute route = pci_device_route_intx_to_irq(dev, i); + const uint8_t v = route.mode == PCI_INTX_ENABLED ? route.irq : 0; + xen_set_pci_link_route(i, v); + } +} + /* PC hardware initialisation */ -static void pc_init1(MachineState *machine, - const char *host_type, const char *pci_type) +static void pc_init1(MachineState *machine, const char *pci_type) { PCMachineState *pcms = PC_MACHINE(machine); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); X86MachineState *x86ms = X86_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *system_io = get_system_io(); - PCIBus *pci_bus; + Object *phb = NULL; ISABus *isa_bus; - PCII440FXState *i440fx_state; - int piix3_devfn = -1; + Object *piix4_pm = NULL; qemu_irq smi_irq; GSIState *gsi_state; - BusState *idebus[MAX_IDE_BUS]; - ISADevice *rtc_state; MemoryRegion *ram_memory; - MemoryRegion *pci_memory; - MemoryRegion *rom_memory; + MemoryRegion *pci_memory = NULL; + MemoryRegion *rom_memory = system_memory; ram_addr_t lowmem; + uint64_t hole64_size = 0; /* * Calculate ram split, for memory below and above 4G. It's a bit @@ -126,6 +153,7 @@ static void pc_init1(MachineState *machine, if (xen_enabled()) { xen_hvm_init_pc(pcms, &ram_memory); } else { + ram_memory = machine->ram; if (!pcms->max_ram_below_4g) { pcms->max_ram_below_4g = 0xe0000000; /* default: 3.5G */ } @@ -156,7 +184,7 @@ static void pc_init1(MachineState *machine, pc_machine_init_sgx_epc(pcms); x86_cpus_init(x86ms, pcmc->default_cpu_version); - if (pcmc->kvmclock_enabled) { + if (kvm_enabled()) { kvmclock_create(pcmc->kvmclock_create_always); } @@ -164,27 +192,42 @@ static void pc_init1(MachineState *machine, pci_memory = g_new(MemoryRegion, 1); memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); rom_memory = pci_memory; - } else { - pci_memory = NULL; - rom_memory = system_memory; - } - pc_guest_info_init(pcms); + phb = OBJECT(qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE)); + object_property_add_child(OBJECT(machine), "i440fx", phb); + object_property_set_link(phb, PCI_HOST_PROP_RAM_MEM, + OBJECT(ram_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_PCI_MEM, + OBJECT(pci_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_SYSTEM_MEM, + OBJECT(system_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_IO_MEM, + OBJECT(system_io), &error_fatal); + object_property_set_uint(phb, PCI_HOST_BELOW_4G_MEM_SIZE, + x86ms->below_4g_mem_size, &error_fatal); + object_property_set_uint(phb, PCI_HOST_ABOVE_4G_MEM_SIZE, + x86ms->above_4g_mem_size, &error_fatal); + object_property_set_str(phb, I440FX_HOST_PROP_PCI_TYPE, pci_type, + &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal); - if (pcmc->smbios_defaults) { - MachineClass *mc = MACHINE_GET_CLASS(machine); - /* These values are guest ABI, do not change */ - smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)", - mc->name, pcmc->smbios_legacy_mode, - pcmc->smbios_uuid_encoded, - pcms->smbios_entry_point_type); + pcms->pcibus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pci.0")); + pci_bus_map_irqs(pcms->pcibus, + xen_enabled() ? xen_pci_slot_get_pirq + : pc_pci_slot_get_pirq); + + hole64_size = object_property_get_uint(phb, + PCI_HOST_PROP_PCI_HOLE64_SIZE, + &error_abort); } /* allocate ram and load rom/bios */ if (!xen_enabled()) { - pc_memory_init(pcms, system_memory, - rom_memory, &ram_memory); + pc_memory_init(pcms, system_memory, rom_memory, hole64_size); } else { + assert(machine->ram_size == x86ms->below_4g_mem_size + + x86ms->above_4g_mem_size); + pc_system_flash_cleanup_unused(pcms); if (machine->kernel_filename != NULL) { /* For xen HVM direct kernel boot, load linux here */ @@ -195,42 +238,78 @@ static void pc_init1(MachineState *machine, gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); if (pcmc->pci_enabled) { - PIIX3State *piix3; + PCIDevice *pci_dev; + DeviceState *dev; + size_t i; - pci_bus = i440fx_init(host_type, - pci_type, - &i440fx_state, - system_memory, system_io, machine->ram_size, - x86ms->below_4g_mem_size, - x86ms->above_4g_mem_size, - pci_memory, ram_memory); - pcms->bus = pci_bus; + pci_dev = pci_new_multifunction(-1, pcms->south_bridge); + object_property_set_bool(OBJECT(pci_dev), "has-usb", + machine_usb(machine), &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-acpi", + x86_machine_is_acpi_enabled(x86ms), + &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-pic", false, + &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-pit", false, + &error_abort); + qdev_prop_set_uint32(DEVICE(pci_dev), "smb_io_base", 0xb100); + object_property_set_bool(OBJECT(pci_dev), "smm-enabled", + x86_machine_is_smm_enabled(x86ms), + &error_abort); + dev = DEVICE(pci_dev); + for (i = 0; i < ISA_NUM_IRQS; i++) { + qdev_connect_gpio_out_named(dev, "isa-irqs", i, x86ms->gsi[i]); + } + pci_realize_and_unref(pci_dev, pcms->pcibus, &error_fatal); - piix3 = piix3_create(pci_bus, &isa_bus); - piix3->pic = x86ms->gsi; - piix3_devfn = piix3->dev.devfn; + if (xen_enabled()) { + pci_device_set_intx_routing_notifier( + pci_dev, piix_intx_routing_notifier_xen); + + /* + * Xen supports additional interrupt routes from the PCI devices to + * the IOAPIC: the four pins of each PCI device on the bus are also + * connected to the IOAPIC directly. + * These additional routes can be discovered through ACPI. + */ + pci_bus_irqs(pcms->pcibus, xen_intx_set_irq, pci_dev, + XEN_IOAPIC_NUM_PIRQS); + } + + isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci_dev), "isa.0")); + x86ms->rtc = ISA_DEVICE(object_resolve_path_component(OBJECT(pci_dev), + "rtc")); + piix4_pm = object_resolve_path_component(OBJECT(pci_dev), "pm"); + dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ide")); + pci_ide_create_devs(PCI_DEVICE(dev)); + pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0"); + pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1"); } else { - pci_bus = NULL; - i440fx_state = NULL; - isa_bus = isa_bus_new(NULL, get_system_memory(), system_io, + isa_bus = isa_bus_new(NULL, system_memory, system_io, &error_abort); + isa_bus_register_input_irqs(isa_bus, x86ms->gsi); + + x86ms->rtc = isa_new(TYPE_MC146818_RTC); + qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); + isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); + + i8257_dma_init(OBJECT(machine), isa_bus, 0); pcms->hpet_enabled = false; } - isa_bus_irqs(isa_bus, x86ms->gsi); if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { pc_i8259_create(isa_bus, gsi_state->i8259_irq); } - if (pcmc->pci_enabled) { - ioapic_init_gsi(gsi_state, "i440fx"); + if (phb) { + ioapic_init_gsi(gsi_state, phb); } if (tcg_enabled()) { x86_register_ferr_irq(x86ms->gsi[13]); } - pc_vga_init(isa_bus, pcmc->pci_enabled ? pci_bus : NULL); + pc_vga_init(isa_bus, pcmc->pci_enabled ? pcms->pcibus : NULL); assert(pcms->vmport != ON_OFF_AUTO__MAX); if (pcms->vmport == ON_OFF_AUTO_AUTO) { @@ -238,22 +317,13 @@ static void pc_init1(MachineState *machine, } /* init basic PC hardware */ - pc_basic_device_init(pcms, isa_bus, x86ms->gsi, &rtc_state, true, + pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, true, 0x4); - pc_nic_init(pcmc, isa_bus, pci_bus); + pc_nic_init(pcmc, isa_bus, pcms->pcibus); - if (pcmc->pci_enabled) { - PCIDevice *dev; - - dev = pci_create_simple(pci_bus, piix3_devfn + 1, "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 { + if (!pcmc->pci_enabled) { DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; int i; @@ -269,25 +339,17 @@ static void pc_init1(MachineState *machine, * second one. */ busname[4] = '0' + i; - idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); + pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); } - pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state); } #endif - if (pcmc->pci_enabled && machine_usb(machine)) { - pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci"); - } - - if (pcmc->pci_enabled && x86_machine_is_acpi_enabled(X86_MACHINE(pcms))) { - DeviceState *piix4_pm; - + if (piix4_pm) { smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); + + qdev_connect_gpio_out_named(DEVICE(piix4_pm), "smi-irq", 0, smi_irq); + pcms->smbus = I2C_BUS(qdev_get_child_bus(DEVICE(piix4_pm), "i2c")); /* TODO: Populate SPD eeprom data. */ - pcms->smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, - x86ms->gsi[9], smi_irq, - x86_machine_is_smm_enabled(x86ms), - &piix4_pm); smbus_eeprom_init(pcms->smbus, 8, NULL, 0); object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, @@ -296,7 +358,7 @@ static void pc_init1(MachineState *machine, object_property_allow_set_link, OBJ_PROP_LINK_STRONG); object_property_set_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, - OBJECT(piix4_pm), &error_abort); + piix4_pm, &error_abort); } if (machine->nvdimms_state->is_enabled) { @@ -306,6 +368,53 @@ static void pc_init1(MachineState *machine, } } +typedef enum PCSouthBridgeOption { + PC_SOUTH_BRIDGE_OPTION_PIIX3, + PC_SOUTH_BRIDGE_OPTION_PIIX4, + PC_SOUTH_BRIDGE_OPTION_MAX, +} PCSouthBridgeOption; + +static const QEnumLookup PCSouthBridgeOption_lookup = { + .array = (const char *const[]) { + [PC_SOUTH_BRIDGE_OPTION_PIIX3] = TYPE_PIIX3_DEVICE, + [PC_SOUTH_BRIDGE_OPTION_PIIX4] = TYPE_PIIX4_PCI_DEVICE, + }, + .size = PC_SOUTH_BRIDGE_OPTION_MAX +}; + +static int pc_get_south_bridge(Object *obj, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + int i; + + for (i = 0; i < PCSouthBridgeOption_lookup.size; i++) { + if (g_strcmp0(PCSouthBridgeOption_lookup.array[i], + pcms->south_bridge) == 0) { + return i; + } + } + + error_setg(errp, "Invalid south bridge value set"); + return 0; +} + +static void pc_set_south_bridge(Object *obj, int value, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + + if (value < 0) { + error_setg(errp, "Value can't be negative"); + return; + } + + if (value >= PCSouthBridgeOption_lookup.size) { + error_setg(errp, "Value too big"); + return; + } + + pcms->south_bridge = PCSouthBridgeOption_lookup.array[value]; +} + /* Looking for a pc_compat_2_4() function? It doesn't exist. * pc_compat_*() functions that run on machine-init time and * change global QEMU state are deprecated. Please don't create @@ -337,31 +446,10 @@ static void pc_compat_2_0_fn(MachineState *machine) pc_compat_2_1_fn(machine); } -static void pc_compat_1_7_fn(MachineState *machine) -{ - pc_compat_2_0_fn(machine); - x86_cpu_change_kvm_default("x2apic", NULL); -} - -static void pc_compat_1_6_fn(MachineState *machine) -{ - pc_compat_1_7_fn(machine); -} - -static void pc_compat_1_5_fn(MachineState *machine) -{ - pc_compat_1_6_fn(machine); -} - -static void pc_compat_1_4_fn(MachineState *machine) -{ - pc_compat_1_5_fn(machine); -} - #ifdef CONFIG_ISAPC static void pc_init_isa(MachineState *machine) { - pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, TYPE_I440FX_PCI_DEVICE); + pc_init1(machine, NULL); } #endif @@ -371,9 +459,7 @@ static void pc_xen_hvm_init_pci(MachineState *machine) const char *pci_type = xen_igd_gfx_pt_enabled() ? TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE : TYPE_I440FX_PCI_DEVICE; - pc_init1(machine, - TYPE_I440FX_PCI_HOST_BRIDGE, - pci_type); + pc_init1(machine, pci_type); } static void pc_xen_hvm_init(MachineState *machine) @@ -386,7 +472,8 @@ static void pc_xen_hvm_init(MachineState *machine) } pc_xen_hvm_init_pci(machine); - pci_create_simple(pcms->bus, -1, "xen-platform"); + xen_igd_reserve_slot(pcms->pcibus); + pci_create_simple(pcms->pcibus, -1, "xen-platform"); } #endif @@ -397,32 +484,106 @@ static void pc_xen_hvm_init(MachineState *machine) if (compat) { \ compat(machine); \ } \ - pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ - TYPE_I440FX_PCI_DEVICE); \ + pc_init1(machine, TYPE_I440FX_PCI_DEVICE); \ } \ DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn) static void pc_i440fx_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pcmc->default_nic_model = "e1000"; + ObjectClass *oc = OBJECT_CLASS(m); + pcmc->default_south_bridge = TYPE_PIIX3_DEVICE; pcmc->pci_root_uid = 0; + pcmc->default_cpu_version = 1; m->family = "pc_piix"; m->desc = "Standard PC (i440FX + PIIX, 1996)"; m->default_machine_opts = "firmware=bios-256k.bin"; m->default_display = "std"; + m->default_nic = "e1000"; + m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE); + + object_class_property_add_enum(oc, "x-south-bridge", "PCSouthBridgeOption", + &PCSouthBridgeOption_lookup, + pc_get_south_bridge, + pc_set_south_bridge); + object_class_property_set_description(oc, "x-south-bridge", + "Use a different south bridge than PIIX3"); } -static void pc_i440fx_7_1_machine_options(MachineClass *m) +static void pc_i440fx_9_0_machine_options(MachineClass *m) { - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = true; - pcmc->default_cpu_version = 1; +} + +DEFINE_I440FX_MACHINE(v9_0, "pc-i440fx-9.0", NULL, + pc_i440fx_9_0_machine_options); + +static void pc_i440fx_8_2_machine_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + + pc_i440fx_9_0_machine_options(m); + m->alias = NULL; + m->is_default = false; + + compat_props_add(m->compat_props, hw_compat_8_2, hw_compat_8_2_len); + compat_props_add(m->compat_props, pc_compat_8_2, pc_compat_8_2_len); + /* For pc-i44fx-8.2 and 8.1, use SMBIOS 3.X by default */ + pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_64; +} + +DEFINE_I440FX_MACHINE(v8_2, "pc-i440fx-8.2", NULL, + pc_i440fx_8_2_machine_options); + +static void pc_i440fx_8_1_machine_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + + pc_i440fx_8_2_machine_options(m); + pcmc->broken_32bit_mem_addr_check = true; + + compat_props_add(m->compat_props, hw_compat_8_1, hw_compat_8_1_len); + compat_props_add(m->compat_props, pc_compat_8_1, pc_compat_8_1_len); +} + +DEFINE_I440FX_MACHINE(v8_1, "pc-i440fx-8.1", NULL, + pc_i440fx_8_1_machine_options); + +static void pc_i440fx_8_0_machine_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + + pc_i440fx_8_1_machine_options(m); + compat_props_add(m->compat_props, hw_compat_8_0, hw_compat_8_0_len); + compat_props_add(m->compat_props, pc_compat_8_0, pc_compat_8_0_len); + + /* For pc-i44fx-8.0 and older, use SMBIOS 2.8 by default */ + pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_32; +} + +DEFINE_I440FX_MACHINE(v8_0, "pc-i440fx-8.0", NULL, + pc_i440fx_8_0_machine_options); + +static void pc_i440fx_7_2_machine_options(MachineClass *m) +{ + pc_i440fx_8_0_machine_options(m); + compat_props_add(m->compat_props, hw_compat_7_2, hw_compat_7_2_len); + compat_props_add(m->compat_props, pc_compat_7_2, pc_compat_7_2_len); +} + +DEFINE_I440FX_MACHINE(v7_2, "pc-i440fx-7.2", NULL, + pc_i440fx_7_2_machine_options); + +static void pc_i440fx_7_1_machine_options(MachineClass *m) +{ + pc_i440fx_7_2_machine_options(m); + compat_props_add(m->compat_props, hw_compat_7_1, hw_compat_7_1_len); + compat_props_add(m->compat_props, pc_compat_7_1, pc_compat_7_1_len); } DEFINE_I440FX_MACHINE(v7_1, "pc-i440fx-7.1", NULL, @@ -430,9 +591,9 @@ DEFINE_I440FX_MACHINE(v7_1, "pc-i440fx-7.1", NULL, static void pc_i440fx_7_0_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_7_1_machine_options(m); - m->alias = NULL; - m->is_default = false; + pcmc->enforce_amd_1tb_hole = false; compat_props_add(m->compat_props, hw_compat_7_0, hw_compat_7_0_len); compat_props_add(m->compat_props, pc_compat_7_0, pc_compat_7_0_len); } @@ -443,8 +604,6 @@ DEFINE_I440FX_MACHINE(v7_0, "pc-i440fx-7.0", NULL, static void pc_i440fx_6_2_machine_options(MachineClass *m) { pc_i440fx_7_0_machine_options(m); - m->alias = NULL; - m->is_default = false; compat_props_add(m->compat_props, hw_compat_6_2, hw_compat_6_2_len); compat_props_add(m->compat_props, pc_compat_6_2, pc_compat_6_2_len); } @@ -455,8 +614,6 @@ DEFINE_I440FX_MACHINE(v6_2, "pc-i440fx-6.2", NULL, static void pc_i440fx_6_1_machine_options(MachineClass *m) { pc_i440fx_6_2_machine_options(m); - m->alias = NULL; - m->is_default = false; compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len); compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len); m->smp_props.prefer_sockets = true; @@ -468,8 +625,6 @@ DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL, static void pc_i440fx_6_0_machine_options(MachineClass *m) { pc_i440fx_6_1_machine_options(m); - m->alias = NULL; - m->is_default = false; compat_props_add(m->compat_props, hw_compat_6_0, hw_compat_6_0_len); compat_props_add(m->compat_props, pc_compat_6_0, pc_compat_6_0_len); } @@ -480,8 +635,6 @@ DEFINE_I440FX_MACHINE(v6_0, "pc-i440fx-6.0", NULL, static void pc_i440fx_5_2_machine_options(MachineClass *m) { pc_i440fx_6_0_machine_options(m); - m->alias = NULL; - m->is_default = false; compat_props_add(m->compat_props, hw_compat_5_2, hw_compat_5_2_len); compat_props_add(m->compat_props, pc_compat_5_2, pc_compat_5_2_len); } @@ -494,8 +647,6 @@ static void pc_i440fx_5_1_machine_options(MachineClass *m) PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_5_2_machine_options(m); - m->alias = NULL; - m->is_default = false; compat_props_add(m->compat_props, hw_compat_5_1, hw_compat_5_1_len); compat_props_add(m->compat_props, pc_compat_5_1, pc_compat_5_1_len); pcmc->kvmclock_create_always = false; @@ -508,8 +659,6 @@ DEFINE_I440FX_MACHINE(v5_1, "pc-i440fx-5.1", NULL, static void pc_i440fx_5_0_machine_options(MachineClass *m) { pc_i440fx_5_1_machine_options(m); - m->alias = NULL; - m->is_default = false; m->numa_mem_supported = true; compat_props_add(m->compat_props, hw_compat_5_0, hw_compat_5_0_len); compat_props_add(m->compat_props, pc_compat_5_0, pc_compat_5_0_len); @@ -522,8 +671,6 @@ DEFINE_I440FX_MACHINE(v5_0, "pc-i440fx-5.0", NULL, 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); } @@ -534,8 +681,6 @@ DEFINE_I440FX_MACHINE(v4_2, "pc-i440fx-4.2", NULL, static void pc_i440fx_4_1_machine_options(MachineClass *m) { pc_i440fx_4_2_machine_options(m); - m->alias = NULL; - 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); } @@ -547,8 +692,6 @@ 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 = 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); @@ -562,10 +705,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 = false; - pcmc->do_not_add_smb_acpi = true; m->smbus_no_migration_support = true; - m->alias = NULL; pcmc->pvh_enabled = false; compat_props_add(m->compat_props, hw_compat_3_1, hw_compat_3_1_len); compat_props_add(m->compat_props, pc_compat_3_1, pc_compat_3_1_len); @@ -692,6 +832,7 @@ static void pc_i440fx_2_3_machine_options(MachineClass *m) { pc_i440fx_2_4_machine_options(m); m->hw_version = "2.3.0"; + m->deprecation_reason = "old and unattended - use a newer version instead"; compat_props_add(m->compat_props, hw_compat_2_3, hw_compat_2_3_len); compat_props_add(m->compat_props, pc_compat_2_3, pc_compat_2_3_len); } @@ -709,6 +850,7 @@ static void pc_i440fx_2_2_machine_options(MachineClass *m) compat_props_add(m->compat_props, hw_compat_2_2, hw_compat_2_2_len); compat_props_add(m->compat_props, pc_compat_2_2, pc_compat_2_2_len); pcmc->rsdp_in_ram = false; + pcmc->resizable_acpi_blob = false; } DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2_fn, @@ -762,58 +904,6 @@ static void pc_i440fx_2_0_machine_options(MachineClass *m) DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0_fn, pc_i440fx_2_0_machine_options); -static void pc_i440fx_1_7_machine_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_i440fx_2_0_machine_options(m); - m->hw_version = "1.7.0"; - m->default_machine_opts = NULL; - m->option_rom_has_mr = true; - m->deprecation_reason = "old and unattended - use a newer version instead"; - compat_props_add(m->compat_props, pc_compat_1_7, pc_compat_1_7_len); - pcmc->smbios_defaults = false; - pcmc->gigabyte_align = false; - pcmc->legacy_acpi_table_size = 6414; -} - -DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7_fn, - pc_i440fx_1_7_machine_options); - -static void pc_i440fx_1_6_machine_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_i440fx_1_7_machine_options(m); - m->hw_version = "1.6.0"; - m->rom_file_has_mr = false; - compat_props_add(m->compat_props, pc_compat_1_6, pc_compat_1_6_len); - pcmc->has_acpi_build = false; -} - -DEFINE_I440FX_MACHINE(v1_6, "pc-i440fx-1.6", pc_compat_1_6_fn, - pc_i440fx_1_6_machine_options); - -static void pc_i440fx_1_5_machine_options(MachineClass *m) -{ - pc_i440fx_1_6_machine_options(m); - m->hw_version = "1.5.0"; - compat_props_add(m->compat_props, pc_compat_1_5, pc_compat_1_5_len); -} - -DEFINE_I440FX_MACHINE(v1_5, "pc-i440fx-1.5", pc_compat_1_5_fn, - pc_i440fx_1_5_machine_options); - -static void pc_i440fx_1_4_machine_options(MachineClass *m) -{ - pc_i440fx_1_5_machine_options(m); - m->hw_version = "1.4.0"; - compat_props_add(m->compat_props, pc_compat_1_4, pc_compat_1_4_len); -} - -DEFINE_I440FX_MACHINE(v1_4, "pc-i440fx-1.4", pc_compat_1_4_fn, - pc_i440fx_1_4_machine_options); - #ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) { @@ -828,8 +918,9 @@ static void isapc_machine_options(MachineClass *m) pcmc->gigabyte_align = false; pcmc->smbios_legacy_mode = true; pcmc->has_reserved_memory = false; - pcmc->default_nic_model = "ne2k_isa"; + m->default_nic = "ne2k_isa"; m->default_cpu_type = X86_CPU_TYPE_NAME("486"); + m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); } DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 42eb8b9707..c7bc8a2041 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -30,34 +30,46 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "hw/acpi/acpi.h" +#include "hw/char/parallel-isa.h" #include "hw/loader.h" #include "hw/i2c/smbus_eeprom.h" #include "hw/rtc/mc146818rtc.h" +#include "sysemu/tcg.h" #include "sysemu/kvm.h" -#include "hw/kvm/clock.h" +#include "hw/i386/kvm/clock.h" #include "hw/pci-host/q35.h" #include "hw/pci/pcie_port.h" #include "hw/qdev-properties.h" #include "hw/i386/x86.h" #include "hw/i386/pc.h" -#include "hw/i386/ich9.h" #include "hw/i386/amd_iommu.h" #include "hw/i386/intel_iommu.h" +#include "hw/virtio/virtio-iommu.h" #include "hw/display/ramfb.h" -#include "hw/firmware/smbios.h" #include "hw/ide/pci.h" -#include "hw/ide/ahci.h" +#include "hw/ide/ahci-pci.h" +#include "hw/intc/ioapic.h" +#include "hw/southbridge/ich9.h" #include "hw/usb.h" +#include "hw/usb/hcd-uhci.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "sysemu/numa.h" #include "hw/hyperv/vmbus-bridge.h" #include "hw/mem/nvdimm.h" #include "hw/i386/acpi-build.h" +#include "target/i386/cpu.h" /* ICH9 AHCI has 6 ports */ #define MAX_SATA_PORTS 6 +static GlobalProperty pc_q35_compat_defaults[] = { + { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "39" }, +}; +static const size_t pc_q35_compat_defaults_len = + G_N_ELEMENTS(pc_q35_compat_defaults); + struct ehci_companions { const char *name; int func; @@ -65,15 +77,15 @@ struct ehci_companions { }; static const struct ehci_companions ich9_1d[] = { - { .name = "ich9-usb-uhci1", .func = 0, .port = 0 }, - { .name = "ich9-usb-uhci2", .func = 1, .port = 2 }, - { .name = "ich9-usb-uhci3", .func = 2, .port = 4 }, + { .name = TYPE_ICH9_USB_UHCI(1), .func = 0, .port = 0 }, + { .name = TYPE_ICH9_USB_UHCI(2), .func = 1, .port = 2 }, + { .name = TYPE_ICH9_USB_UHCI(3), .func = 2, .port = 4 }, }; static const struct ehci_companions ich9_1a[] = { - { .name = "ich9-usb-uhci4", .func = 0, .port = 0 }, - { .name = "ich9-usb-uhci5", .func = 1, .port = 2 }, - { .name = "ich9-usb-uhci6", .func = 2, .port = 4 }, + { .name = TYPE_ICH9_USB_UHCI(4), .func = 0, .port = 0 }, + { .name = TYPE_ICH9_USB_UHCI(5), .func = 1, .port = 2 }, + { .name = TYPE_ICH9_USB_UHCI(6), .func = 2, .port = 4 }, }; static int ehci_create_ich9_with_companions(PCIBus *bus, int slot) @@ -97,12 +109,12 @@ static int ehci_create_ich9_with_companions(PCIBus *bus, int slot) return -1; } - ehci = pci_new_multifunction(PCI_DEVFN(slot, 7), true, name); + ehci = pci_new_multifunction(PCI_DEVFN(slot, 7), name); pci_realize_and_unref(ehci, bus, &error_fatal); usbbus = QLIST_FIRST(&ehci->qdev.child_bus); for (i = 0; i < 3; i++) { - uhci = pci_new_multifunction(PCI_DEVFN(slot, comp[i].func), true, + uhci = pci_new_multifunction(PCI_DEVFN(slot, comp[i].func), comp[i].name); qdev_prop_set_string(&uhci->qdev, "masterbus", usbbus->name); qdev_prop_set_uint32(&uhci->qdev, "firstport", comp[i].port); @@ -117,27 +129,23 @@ static void pc_q35_init(MachineState *machine) PCMachineState *pcms = PC_MACHINE(machine); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); X86MachineState *x86ms = X86_MACHINE(machine); - Q35PCIHost *q35_host; - PCIHostState *phb; - PCIBus *host_bus; + Object *phb; PCIDevice *lpc; DeviceState *lpc_dev; - BusState *idebus[MAX_SATA_PORTS]; - ISADevice *rtc_state; + MemoryRegion *system_memory = get_system_memory(); MemoryRegion *system_io = get_system_io(); - MemoryRegion *pci_memory; - MemoryRegion *rom_memory; - MemoryRegion *ram_memory; + MemoryRegion *pci_memory = g_new(MemoryRegion, 1); GSIState *gsi_state; ISABus *isa_bus; int i; - ICH9LPCState *ich9_lpc; - PCIDevice *ahci; ram_addr_t lowmem; DriveInfo *hd[MAX_SATA_PORTS]; MachineClass *mc = MACHINE_GET_CLASS(machine); bool acpi_pcihp; bool keep_pci_slot_hpc; + uint64_t pci_hole64_size = 0; + + assert(pcmc->pci_enabled); /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory * and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping @@ -181,55 +189,56 @@ static void pc_q35_init(MachineState *machine) pc_machine_init_sgx_epc(pcms); x86_cpus_init(x86ms, pcmc->default_cpu_version); - kvmclock_create(pcmc->kvmclock_create_always); - - /* pci enabled */ - if (pcmc->pci_enabled) { - pci_memory = g_new(MemoryRegion, 1); - memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); - rom_memory = pci_memory; - } else { - pci_memory = NULL; - rom_memory = get_system_memory(); + if (kvm_enabled()) { + kvmclock_create(pcmc->kvmclock_create_always); } - pc_guest_info_init(pcms); - - if (pcmc->smbios_defaults) { - /* These values are guest ABI, do not change */ - smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)", - mc->name, pcmc->smbios_legacy_mode, - pcmc->smbios_uuid_encoded, - pcms->smbios_entry_point_type); - } - - /* allocate ram and load rom/bios */ - pc_memory_init(pcms, get_system_memory(), rom_memory, &ram_memory); - /* create pci host bus */ - q35_host = Q35_HOST_DEVICE(qdev_new(TYPE_Q35_HOST_DEVICE)); + phb = OBJECT(qdev_new(TYPE_Q35_HOST_DEVICE)); - object_property_add_child(qdev_get_machine(), "q35", OBJECT(q35_host)); - object_property_set_link(OBJECT(q35_host), MCH_HOST_PROP_RAM_MEM, - OBJECT(ram_memory), NULL); - object_property_set_link(OBJECT(q35_host), MCH_HOST_PROP_PCI_MEM, + pci_hole64_size = object_property_get_uint(phb, + PCI_HOST_PROP_PCI_HOLE64_SIZE, + &error_abort); + + /* allocate ram and load rom/bios */ + memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); + pc_memory_init(pcms, system_memory, pci_memory, pci_hole64_size); + + object_property_add_child(OBJECT(machine), "q35", phb); + object_property_set_link(phb, PCI_HOST_PROP_RAM_MEM, + OBJECT(machine->ram), NULL); + object_property_set_link(phb, PCI_HOST_PROP_PCI_MEM, OBJECT(pci_memory), NULL); - object_property_set_link(OBJECT(q35_host), MCH_HOST_PROP_SYSTEM_MEM, - OBJECT(get_system_memory()), NULL); - object_property_set_link(OBJECT(q35_host), MCH_HOST_PROP_IO_MEM, + object_property_set_link(phb, PCI_HOST_PROP_SYSTEM_MEM, + OBJECT(system_memory), NULL); + object_property_set_link(phb, PCI_HOST_PROP_IO_MEM, OBJECT(system_io), NULL); - object_property_set_int(OBJECT(q35_host), PCI_HOST_BELOW_4G_MEM_SIZE, + object_property_set_int(phb, PCI_HOST_BELOW_4G_MEM_SIZE, x86ms->below_4g_mem_size, NULL); - object_property_set_int(OBJECT(q35_host), PCI_HOST_ABOVE_4G_MEM_SIZE, + object_property_set_int(phb, PCI_HOST_ABOVE_4G_MEM_SIZE, x86ms->above_4g_mem_size, NULL); + object_property_set_bool(phb, PCI_HOST_BYPASS_IOMMU, + pcms->default_bus_bypass_iommu, NULL); + sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal); + /* pci */ - sysbus_realize_and_unref(SYS_BUS_DEVICE(q35_host), &error_fatal); - phb = PCI_HOST_BRIDGE(q35_host); - host_bus = phb->bus; + pcms->pcibus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pcie.0")); + + /* irq lines */ + gsi_state = pc_gsi_create(&x86ms->gsi, true); + /* create ISA bus */ - lpc = pci_create_simple_multifunction(host_bus, PCI_DEVFN(ICH9_LPC_DEV, - ICH9_LPC_FUNC), true, - TYPE_ICH9_LPC_DEVICE); + lpc = pci_new_multifunction(PCI_DEVFN(ICH9_LPC_DEV, ICH9_LPC_FUNC), + TYPE_ICH9_LPC_DEVICE); + lpc_dev = DEVICE(lpc); + qdev_prop_set_bit(lpc_dev, "smm-enabled", + x86_machine_is_smm_enabled(x86ms)); + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + qdev_connect_gpio_out_named(lpc_dev, ICH9_GPIO_GSI, i, x86ms->gsi[i]); + } + pci_realize_and_unref(lpc, pcms->pcibus, &error_fatal); + + x86ms->rtc = ISA_DEVICE(object_resolve_path_component(OBJECT(lpc), "rtc")); object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, TYPE_HOTPLUG_HANDLER, @@ -248,30 +257,18 @@ static void pc_q35_init(MachineState *machine) NULL); if (!keep_pci_slot_hpc && acpi_pcihp) { - object_register_sugar_prop(TYPE_PCIE_SLOT, "x-native-hotplug", - "false", true); + object_register_sugar_prop(TYPE_PCIE_SLOT, + "x-do-not-expose-native-hotplug-cap", + "true", true); } - /* irq lines */ - gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); - - ich9_lpc = ICH9_LPC_DEVICE(lpc); - lpc_dev = DEVICE(lpc); - for (i = 0; i < GSI_NUM_PINS; i++) { - qdev_connect_gpio_out_named(lpc_dev, ICH9_GPIO_GSI, i, x86ms->gsi[i]); - } - pci_bus_irqs(host_bus, ich9_lpc_set_irq, ich9_lpc_map_irq, ich9_lpc, - ICH9_LPC_NB_PIRQS); - pci_bus_set_route_irq_fn(host_bus, ich9_route_intx_pin_to_irq); - isa_bus = ich9_lpc->isa_bus; + isa_bus = ISA_BUS(qdev_get_child_bus(lpc_dev, "isa.0")); if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { pc_i8259_create(isa_bus, gsi_state->i8259_irq); } - if (pcmc->pci_enabled) { - ioapic_init_gsi(gsi_state, "q35"); - } + ioapic_init_gsi(gsi_state, OBJECT(phb)); if (tcg_enabled()) { x86_register_ferr_irq(x86ms->gsi[13]); @@ -283,45 +280,47 @@ static void pc_q35_init(MachineState *machine) } /* init basic PC hardware */ - pc_basic_device_init(pcms, isa_bus, x86ms->gsi, &rtc_state, !mc->no_floppy, + pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, !mc->no_floppy, 0xff0104); - /* connect pm stuff to lpc */ - ich9_lpc_pm_init(lpc, x86_machine_is_smm_enabled(x86ms)); - if (pcms->sata_enabled) { + PCIDevice *pdev; + AHCIPCIState *ich9; + /* ahci and SATA device, for q35 1 ahci controller is built-in */ - ahci = pci_create_simple_multifunction(host_bus, + pdev = pci_create_simple_multifunction(pcms->pcibus, PCI_DEVFN(ICH9_SATA1_DEV, ICH9_SATA1_FUNC), - true, "ich9-ahci"); - idebus[0] = qdev_get_child_bus(&ahci->qdev, "ide.0"); - idebus[1] = qdev_get_child_bus(&ahci->qdev, "ide.1"); - g_assert(MAX_SATA_PORTS == ahci_get_num_ports(ahci)); - ide_drive_get(hd, ahci_get_num_ports(ahci)); - ahci_ide_create_devs(ahci, hd); - } else { - idebus[0] = idebus[1] = NULL; + "ich9-ahci"); + ich9 = ICH9_AHCI(pdev); + pcms->idebus[0] = qdev_get_child_bus(DEVICE(pdev), "ide.0"); + pcms->idebus[1] = qdev_get_child_bus(DEVICE(pdev), "ide.1"); + g_assert(MAX_SATA_PORTS == ich9->ahci.ports); + ide_drive_get(hd, ich9->ahci.ports); + ahci_ide_create_devs(&ich9->ahci, hd); } if (machine_usb(machine)) { /* Should we create 6 UHCI according to ich9 spec? */ - ehci_create_ich9_with_companions(host_bus, 0x1d); + ehci_create_ich9_with_companions(pcms->pcibus, 0x1d); } if (pcms->smbus_enabled) { + PCIDevice *smb; + /* TODO: Populate SPD eeprom data. */ - pcms->smbus = ich9_smb_init(host_bus, - PCI_DEVFN(ICH9_SMB_DEV, ICH9_SMB_FUNC), - 0xb100); + smb = pci_create_simple_multifunction(pcms->pcibus, + PCI_DEVFN(ICH9_SMB_DEV, + ICH9_SMB_FUNC), + TYPE_ICH9_SMB_DEVICE); + pcms->smbus = I2C_BUS(qdev_get_child_bus(DEVICE(smb), "i2c")); + smbus_eeprom_init(pcms->smbus, 8, NULL, 0); } - pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state); - /* the rest devices to which pci devfn is automatically assigned */ - pc_vga_init(isa_bus, host_bus); - pc_nic_init(pcmc, isa_bus, host_bus); + pc_vga_init(isa_bus, pcms->pcibus); + pc_nic_init(pcmc, isa_bus, pcms->pcibus); if (machine->nvdimms_state->is_enabled) { nvdimm_init_acpi_state(machine->nvdimms_state, system_io, @@ -345,29 +344,94 @@ static void pc_q35_init(MachineState *machine) static void pc_q35_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pcmc->default_nic_model = "e1000e"; pcmc->pci_root_uid = 0; + pcmc->default_cpu_version = 1; m->family = "pc_q35"; m->desc = "Standard PC (Q35 + ICH9, 2009)"; m->units_per_default_bus = 1; m->default_machine_opts = "firmware=bios-256k.bin"; m->default_display = "std"; + m->default_nic = "e1000e"; m->default_kernel_irqchip_split = false; m->no_floppy = 1; + m->max_cpus = 4096; + m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); machine_class_allow_dynamic_sysbus_dev(m, TYPE_AMD_IOMMU_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_INTEL_IOMMU_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE); + compat_props_add(m->compat_props, + pc_q35_compat_defaults, pc_q35_compat_defaults_len); +} + +static void pc_q35_9_0_machine_options(MachineClass *m) +{ + pc_q35_machine_options(m); + m->alias = "q35"; +} + +DEFINE_Q35_MACHINE(v9_0, "pc-q35-9.0", NULL, + pc_q35_9_0_machine_options); + +static void pc_q35_8_2_machine_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_q35_9_0_machine_options(m); + m->alias = NULL; + m->max_cpus = 1024; + compat_props_add(m->compat_props, hw_compat_8_2, hw_compat_8_2_len); + compat_props_add(m->compat_props, pc_compat_8_2, pc_compat_8_2_len); + /* For pc-q35-8.2 and 8.1, use SMBIOS 3.X by default */ + pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_64; +} + +DEFINE_Q35_MACHINE(v8_2, "pc-q35-8.2", NULL, + pc_q35_8_2_machine_options); + +static void pc_q35_8_1_machine_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_q35_8_2_machine_options(m); + pcmc->broken_32bit_mem_addr_check = true; + compat_props_add(m->compat_props, hw_compat_8_1, hw_compat_8_1_len); + compat_props_add(m->compat_props, pc_compat_8_1, pc_compat_8_1_len); +} + +DEFINE_Q35_MACHINE(v8_1, "pc-q35-8.1", NULL, + pc_q35_8_1_machine_options); + +static void pc_q35_8_0_machine_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + + pc_q35_8_1_machine_options(m); + compat_props_add(m->compat_props, hw_compat_8_0, hw_compat_8_0_len); + compat_props_add(m->compat_props, pc_compat_8_0, pc_compat_8_0_len); + + /* For pc-q35-8.0 and older, use SMBIOS 2.8 by default */ + pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_32; m->max_cpus = 288; } +DEFINE_Q35_MACHINE(v8_0, "pc-q35-8.0", NULL, + pc_q35_8_0_machine_options); + +static void pc_q35_7_2_machine_options(MachineClass *m) +{ + pc_q35_8_0_machine_options(m); + compat_props_add(m->compat_props, hw_compat_7_2, hw_compat_7_2_len); + compat_props_add(m->compat_props, pc_compat_7_2, pc_compat_7_2_len); +} + +DEFINE_Q35_MACHINE(v7_2, "pc-q35-7.2", NULL, + pc_q35_7_2_machine_options); + static void pc_q35_7_1_machine_options(MachineClass *m) { - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_machine_options(m); - m->alias = "q35"; - pcmc->default_cpu_version = 1; + pc_q35_7_2_machine_options(m); + compat_props_add(m->compat_props, hw_compat_7_1, hw_compat_7_1_len); + compat_props_add(m->compat_props, pc_compat_7_1, pc_compat_7_1_len); } DEFINE_Q35_MACHINE(v7_1, "pc-q35-7.1", NULL, @@ -375,8 +439,9 @@ DEFINE_Q35_MACHINE(v7_1, "pc-q35-7.1", NULL, static void pc_q35_7_0_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_7_1_machine_options(m); - m->alias = NULL; + pcmc->enforce_amd_1tb_hole = false; compat_props_add(m->compat_props, hw_compat_7_0, hw_compat_7_0_len); compat_props_add(m->compat_props, pc_compat_7_0, pc_compat_7_0_len); } @@ -387,7 +452,6 @@ DEFINE_Q35_MACHINE(v7_0, "pc-q35-7.0", NULL, static void pc_q35_6_2_machine_options(MachineClass *m) { pc_q35_7_0_machine_options(m); - m->alias = NULL; compat_props_add(m->compat_props, hw_compat_6_2, hw_compat_6_2_len); compat_props_add(m->compat_props, pc_compat_6_2, pc_compat_6_2_len); } @@ -398,7 +462,6 @@ DEFINE_Q35_MACHINE(v6_2, "pc-q35-6.2", NULL, static void pc_q35_6_1_machine_options(MachineClass *m) { pc_q35_6_2_machine_options(m); - m->alias = NULL; compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len); compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len); m->smp_props.prefer_sockets = true; @@ -410,7 +473,6 @@ DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL, static void pc_q35_6_0_machine_options(MachineClass *m) { pc_q35_6_1_machine_options(m); - m->alias = NULL; compat_props_add(m->compat_props, hw_compat_6_0, hw_compat_6_0_len); compat_props_add(m->compat_props, pc_compat_6_0, pc_compat_6_0_len); } @@ -421,7 +483,6 @@ DEFINE_Q35_MACHINE(v6_0, "pc-q35-6.0", NULL, static void pc_q35_5_2_machine_options(MachineClass *m) { pc_q35_6_0_machine_options(m); - m->alias = NULL; compat_props_add(m->compat_props, hw_compat_5_2, hw_compat_5_2_len); compat_props_add(m->compat_props, pc_compat_5_2, pc_compat_5_2_len); } @@ -434,7 +495,6 @@ static void pc_q35_5_1_machine_options(MachineClass *m) PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_5_2_machine_options(m); - m->alias = NULL; compat_props_add(m->compat_props, hw_compat_5_1, hw_compat_5_1_len); compat_props_add(m->compat_props, pc_compat_5_1, pc_compat_5_1_len); pcmc->kvmclock_create_always = false; @@ -447,7 +507,6 @@ DEFINE_Q35_MACHINE(v5_1, "pc-q35-5.1", NULL, static void pc_q35_5_0_machine_options(MachineClass *m) { pc_q35_5_1_machine_options(m); - m->alias = NULL; m->numa_mem_supported = true; compat_props_add(m->compat_props, hw_compat_5_0, hw_compat_5_0_len); compat_props_add(m->compat_props, pc_compat_5_0, pc_compat_5_0_len); @@ -460,7 +519,6 @@ DEFINE_Q35_MACHINE(v5_0, "pc-q35-5.0", NULL, 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); } @@ -471,7 +529,6 @@ DEFINE_Q35_MACHINE(v4_2, "pc-q35-4.2", NULL, static void pc_q35_4_1_machine_options(MachineClass *m) { pc_q35_4_2_machine_options(m); - m->alias = NULL; 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); } @@ -483,7 +540,6 @@ static void pc_q35_4_0_1_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_4_1_machine_options(m); - m->alias = NULL; pcmc->default_cpu_version = CPU_VERSION_LEGACY; /* * This is the default machine for the 4.0-stable branch. It is basically @@ -501,7 +557,6 @@ static void pc_q35_4_0_machine_options(MachineClass *m) { pc_q35_4_0_1_machine_options(m); m->default_kernel_irqchip_split = true; - m->alias = NULL; /* Compat props are applied by the 4.0.1 machine */ } @@ -514,9 +569,7 @@ static void pc_q35_3_1_machine_options(MachineClass *m) pc_q35_4_0_machine_options(m); m->default_kernel_irqchip_split = false; - pcmc->do_not_add_smb_acpi = true; m->smbus_no_migration_support = true; - m->alias = NULL; pcmc->pvh_enabled = false; compat_props_add(m->compat_props, hw_compat_3_1, hw_compat_3_1_len); compat_props_add(m->compat_props, pc_compat_3_1, pc_compat_3_1_len); @@ -547,10 +600,8 @@ DEFINE_Q35_MACHINE(v2_12, "pc-q35-2.12", NULL, static void pc_q35_2_11_machine_options(MachineClass *m) { - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_2_12_machine_options(m); - pcmc->default_nic_model = "e1000"; + m->default_nic = "e1000"; compat_props_add(m->compat_props, hw_compat_2_11, hw_compat_2_11_len); compat_props_add(m->compat_props, pc_compat_2_11, pc_compat_2_11_len); } diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index c8d9e71b88..3efabbbab2 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -107,17 +107,15 @@ void pc_system_flash_cleanup_unused(PCMachineState *pcms) { char *prop_name; int i; - Object *dev_obj; assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled); for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) { - dev_obj = OBJECT(pcms->flash[i]); - if (!object_property_get_bool(dev_obj, "realized", &error_abort)) { + if (!qdev_is_realized(DEVICE(pcms->flash[i]))) { prop_name = g_strdup_printf("pflash%d", i); object_property_del(OBJECT(pcms), prop_name); g_free(prop_name); - object_unparent(dev_obj); + object_unparent(OBJECT(pcms->flash[i])); pcms->flash[i] = NULL; } } diff --git a/hw/i386/port92.c b/hw/i386/port92.c index e1379a4f98..b25157f6e4 100644 --- a/hw/i386/port92.c +++ b/hw/i386/port92.c @@ -10,6 +10,7 @@ #include "sysemu/runstate.h" #include "migration/vmstate.h" #include "hw/irq.h" +#include "hw/isa/isa.h" #include "hw/i386/pc.h" #include "trace.h" #include "qom/object.h" @@ -54,7 +55,7 @@ static const VMStateDescription vmstate_port92_isa = { .name = "port92", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(outport, Port92State), VMSTATE_END_OF_LIST() } diff --git a/hw/i386/sgx-stub.c b/hw/i386/sgx-stub.c index 26833eb233..16b1dfd90b 100644 --- a/hw/i386/sgx-stub.c +++ b/hw/i386/sgx-stub.c @@ -34,5 +34,5 @@ void pc_machine_init_sgx_epc(PCMachineState *pcms) bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) { - g_assert_not_reached(); + return true; } diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index a44d66ba2a..de76397bcf 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -18,6 +18,7 @@ #include "monitor/monitor.h" #include "monitor/hmp-target.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "qapi/qapi-commands-misc-target.h" #include "exec/address-spaces.h" #include "sysemu/hw_accel.h" @@ -83,7 +84,7 @@ static uint64_t sgx_calc_section_metric(uint64_t low, uint64_t high) ((high & MAKE_64BIT_MASK(0, 20)) << 32); } -static SGXEPCSectionList *sgx_calc_host_epc_sections(uint64_t *size) +static SGXEPCSectionList *sgx_calc_host_epc_sections(void) { SGXEPCSectionList *head = NULL, **tail = &head; SGXEPCSection *section; @@ -106,7 +107,6 @@ static SGXEPCSectionList *sgx_calc_host_epc_sections(uint64_t *size) section = g_new0(SGXEPCSection, 1); section->node = j++; section->size = sgx_calc_section_metric(ecx, edx); - *size += section->size; QAPI_LIST_APPEND(tail, section); } @@ -157,7 +157,6 @@ SGXInfo *qmp_query_sgx_capabilities(Error **errp) { SGXInfo *info = NULL; uint32_t eax, ebx, ecx, edx; - uint64_t size = 0; int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR); if (fd < 0) { @@ -175,8 +174,7 @@ SGXInfo *qmp_query_sgx_capabilities(Error **errp) info->sgx1 = eax & (1U << 0) ? true : false; info->sgx2 = eax & (1U << 1) ? true : false; - info->sections = sgx_calc_host_epc_sections(&size); - info->section_size = size; + info->sections = sgx_calc_host_epc_sections(); close(fd); @@ -223,14 +221,12 @@ SGXInfo *qmp_query_sgx(Error **errp) return NULL; } - SGXEPCState *sgx_epc = &pcms->sgx_epc; info = g_new0(SGXInfo, 1); info->sgx = true; info->sgx1 = true; info->sgx2 = true; info->flc = true; - info->section_size = sgx_epc->size; info->sections = sgx_get_epc_sections_list(); return info; @@ -241,6 +237,7 @@ void hmp_info_sgx(Monitor *mon, const QDict *qdict) Error *err = NULL; SGXEPCSectionList *section_list, *section; g_autoptr(SGXInfo) info = qmp_query_sgx(&err); + uint64_t size = 0; if (err) { error_report_err(err); @@ -254,8 +251,6 @@ void hmp_info_sgx(Monitor *mon, const QDict *qdict) info->sgx2 ? "enabled" : "disabled"); monitor_printf(mon, "FLC support: %s\n", info->flc ? "enabled" : "disabled"); - monitor_printf(mon, "size: %" PRIu64 "\n", - info->section_size); section_list = info->sections; for (section = section_list; section; section = section->next) { @@ -263,7 +258,10 @@ void hmp_info_sgx(Monitor *mon, const QDict *qdict) section->value->node); monitor_printf(mon, "size=%" PRIu64 "\n", section->value->size); + size += section->value->size; } + monitor_printf(mon, "total size=%" PRIu64 "\n", + size); } bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) @@ -288,30 +286,28 @@ void pc_machine_init_sgx_epc(PCMachineState *pcms) SGXEPCState *sgx_epc = &pcms->sgx_epc; X86MachineState *x86ms = X86_MACHINE(pcms); SgxEPCList *list = NULL; - Object *obj; memset(sgx_epc, 0, sizeof(SGXEPCState)); if (!x86ms->sgx_epc_list) { return; } - sgx_epc->base = 0x100000000ULL + x86ms->above_4g_mem_size; + sgx_epc->base = x86ms->above_4g_mem_start + x86ms->above_4g_mem_size; memory_region_init(&sgx_epc->mr, OBJECT(pcms), "sgx-epc", UINT64_MAX); memory_region_add_subregion(get_system_memory(), sgx_epc->base, &sgx_epc->mr); for (list = x86ms->sgx_epc_list; list; list = list->next) { - obj = object_new("sgx-epc"); + DeviceState *dev = qdev_new(TYPE_SGX_EPC); /* set the memdev link with memory backend */ - object_property_parse(obj, SGX_EPC_MEMDEV_PROP, list->value->memdev, - &error_fatal); + object_property_parse(OBJECT(dev), SGX_EPC_MEMDEV_PROP, + list->value->memdev, &error_fatal); /* set the numa node property for sgx epc object */ - object_property_set_uint(obj, SGX_EPC_NUMA_NODE_PROP, list->value->node, - &error_fatal); - object_property_set_bool(obj, "realized", true, &error_fatal); - object_unref(obj); + object_property_set_uint(OBJECT(dev), SGX_EPC_NUMA_NODE_PROP, + list->value->node, &error_fatal); + qdev_realize_and_unref(dev, NULL, &error_fatal); } if ((sgx_epc->base + sgx_epc->size) < sgx_epc->base) { diff --git a/hw/i386/trace-events b/hw/i386/trace-events index e49814dd64..53c02d7ac8 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -12,6 +12,8 @@ vtd_inv_desc_cc_devices(uint16_t sid, uint16_t fmask) "context invalidate device vtd_inv_desc_iotlb_global(void) "iotlb invalidate global" vtd_inv_desc_iotlb_domain(uint16_t domain) "iotlb invalidate whole domain 0x%"PRIx16 vtd_inv_desc_iotlb_pages(uint16_t domain, uint64_t addr, uint8_t mask) "iotlb invalidate domain 0x%"PRIx16" addr 0x%"PRIx64" mask 0x%"PRIx8 +vtd_inv_desc_iotlb_pasid_pages(uint16_t domain, uint64_t addr, uint8_t mask, uint32_t pasid) "iotlb invalidate domain 0x%"PRIx16" addr 0x%"PRIx64" mask 0x%"PRIx8" pasid 0x%"PRIx32 +vtd_inv_desc_iotlb_pasid(uint16_t domain, uint32_t pasid) "iotlb invalidate domain 0x%"PRIx16" pasid 0x%"PRIx32 vtd_inv_desc_wait_sw(uint64_t addr, uint32_t data) "wait invalidate status write addr 0x%"PRIx64" data 0x%"PRIx32 vtd_inv_desc_wait_irq(const char *msg) "%s" vtd_inv_desc_wait_write_fail(uint64_t hi, uint64_t lo) "write fail for wait desc hi 0x%"PRIx64" lo 0x%"PRIx64 @@ -119,3 +121,13 @@ 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" + +# vmmouse.c +vmmouse_get_status(void) "" +vmmouse_mouse_event(int x, int y, int dz, int buttons_state) "event: x=%d y=%d dz=%d state=%d" +vmmouse_init(void) "" +vmmouse_read_id(void) "" +vmmouse_request_relative(void) "" +vmmouse_request_absolute(void) "" +vmmouse_disable(void) "" +vmmouse_data(uint32_t size) "data: size=%" PRIu32 diff --git a/hw/i386/kvmvapic.c b/hw/i386/vapic.c similarity index 97% rename from hw/i386/kvmvapic.c rename to hw/i386/vapic.c index 43f8a8f679..f5b1db7e5f 100644 --- a/hw/i386/kvmvapic.c +++ b/hw/i386/vapic.c @@ -16,6 +16,7 @@ #include "sysemu/hw_accel.h" #include "sysemu/kvm.h" #include "sysemu/runstate.h" +#include "exec/address-spaces.h" #include "hw/i386/apic_internal.h" #include "hw/sysbus.h" #include "hw/boards.h" @@ -57,6 +58,7 @@ typedef struct GuestROMState { struct VAPICROMState { SysBusDevice busdev; + MemoryRegion io; MemoryRegion rom; uint32_t state; @@ -580,19 +582,17 @@ static int vapic_map_rom_writable(VAPICROMState *s) { hwaddr rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK; MemoryRegionSection section; - MemoryRegion *as; + MemoryRegion *mr = get_system_memory(); size_t rom_size; uint8_t *ram; - as = sysbus_address_space(&s->busdev); - if (s->rom_mapped_writable) { - memory_region_del_subregion(as, &s->rom); + memory_region_del_subregion(mr, &s->rom); object_unparent(OBJECT(&s->rom)); } /* grab RAM memory region (region @rom_paddr may still be pc.rom) */ - section = memory_region_find(as, 0, 1); + section = memory_region_find(mr, 0, 1); /* read ROM size from RAM region */ if (rom_paddr + 2 >= memory_region_size(section.mr)) { @@ -613,7 +613,7 @@ static int vapic_map_rom_writable(VAPICROMState *s) memory_region_init_alias(&s->rom, OBJECT(s), "kvmvapic-rom", section.mr, rom_paddr, rom_size); - memory_region_add_subregion_overlap(as, rom_paddr, &s->rom, 1000); + memory_region_add_subregion_overlap(mr, rom_paddr, &s->rom, 1000); s->rom_mapped_writable = true; memory_region_unref(section.mr); @@ -727,7 +727,7 @@ static void vapic_realize(DeviceState *dev, Error **errp) VAPICROMState *s = VAPIC(dev); memory_region_init_io(&s->io, OBJECT(s), &vapic_ops, s, "kvmvapic", 2); - sysbus_add_io(sbd, VAPIC_IO_PORT, &s->io); + memory_region_add_subregion(get_system_io(), VAPIC_IO_PORT, &s->io); sysbus_init_ioports(sbd, VAPIC_IO_PORT, 2); option_rom[nb_option_roms].name = "kvmvapic.bin"; @@ -747,8 +747,7 @@ static void do_vapic_enable(CPUState *cs, run_on_cpu_data data) s->state = VAPIC_ACTIVE; } -static void kvmvapic_vm_state_change(void *opaque, bool running, - RunState state) +static void vapic_vm_state_change(void *opaque, bool running, RunState state) { MachineState *ms = MACHINE(qdev_get_machine()); VAPICROMState *s = opaque; @@ -793,7 +792,7 @@ static int vapic_post_load(void *opaque, int version_id) if (!s->vmsentry) { s->vmsentry = - qemu_add_vm_change_state_handler(kvmvapic_vm_state_change, s); + qemu_add_vm_change_state_handler(vapic_vm_state_change, s); } return 0; } @@ -802,7 +801,7 @@ static const VMStateDescription vmstate_handlers = { .name = "kvmvapic-handlers", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(set_tpr, VAPICHandlers), VMSTATE_UINT32(set_tpr_eax, VAPICHandlers), VMSTATE_UINT32_ARRAY(get_tpr, VAPICHandlers, 8), @@ -815,7 +814,7 @@ static const VMStateDescription vmstate_guest_rom = { .name = "kvmvapic-guest-rom", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UNUSED(8), /* signature */ VMSTATE_UINT32(vaddr, GuestROMState), VMSTATE_UINT32(fixup_start, GuestROMState), @@ -835,7 +834,7 @@ static const VMStateDescription vmstate_vapic = { .version_id = 1, .minimum_version_id = 1, .post_load = vapic_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(rom_state, VAPICROMState, 0, vmstate_guest_rom, GuestROMState), VMSTATE_UINT32(state, VAPICROMState), diff --git a/hw/i386/vmmouse.c b/hw/i386/vmmouse.c index a56c185f15..a8d014d09a 100644 --- a/hw/i386/vmmouse.c +++ b/hw/i386/vmmouse.c @@ -32,6 +32,8 @@ #include "cpu.h" #include "qom/object.h" +#include "trace.h" + /* debug only vmmouse */ //#define DEBUG_VMMOUSE @@ -44,11 +46,16 @@ #define VMMOUSE_VERSION 0x3442554a -#ifdef DEBUG_VMMOUSE -#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) -#else -#define DPRINTF(fmt, ...) do { } while (0) -#endif +#define VMMOUSE_RELATIVE_PACKET 0x00010000 + +#define VMMOUSE_LEFT_BUTTON 0x20 +#define VMMOUSE_RIGHT_BUTTON 0x10 +#define VMMOUSE_MIDDLE_BUTTON 0x08 + +#define VMMOUSE_MIN_X 0 +#define VMMOUSE_MIN_Y 0 +#define VMMOUSE_MAX_X 0xFFFF +#define VMMOUSE_MAX_Y 0xFFFF #define TYPE_VMMOUSE "vmmouse" OBJECT_DECLARE_SIMPLE_TYPE(VMMouseState, VMMOUSE) @@ -87,7 +94,8 @@ static void vmmouse_set_data(const uint32_t *data) static uint32_t vmmouse_get_status(VMMouseState *s) { - DPRINTF("vmmouse_get_status()\n"); + trace_vmmouse_get_status(); + return (s->status << 16) | s->nb_queue; } @@ -99,19 +107,25 @@ static void vmmouse_mouse_event(void *opaque, int x, int y, int dz, int buttons_ if (s->nb_queue > (VMMOUSE_QUEUE_SIZE - 4)) return; - DPRINTF("vmmouse_mouse_event(%d, %d, %d, %d)\n", - x, y, dz, buttons_state); + trace_vmmouse_mouse_event(x, y, dz, buttons_state); if ((buttons_state & MOUSE_EVENT_LBUTTON)) - buttons |= 0x20; + buttons |= VMMOUSE_LEFT_BUTTON; if ((buttons_state & MOUSE_EVENT_RBUTTON)) - buttons |= 0x10; + buttons |= VMMOUSE_RIGHT_BUTTON; if ((buttons_state & MOUSE_EVENT_MBUTTON)) - buttons |= 0x08; + buttons |= VMMOUSE_MIDDLE_BUTTON; if (s->absolute) { - x <<= 1; - y <<= 1; + x = qemu_input_scale_axis(x, + INPUT_EVENT_ABS_MIN, INPUT_EVENT_ABS_MAX, + VMMOUSE_MIN_X, VMMOUSE_MAX_X); + y = qemu_input_scale_axis(y, + INPUT_EVENT_ABS_MIN, INPUT_EVENT_ABS_MAX, + VMMOUSE_MIN_Y, VMMOUSE_MAX_Y); + } else{ + /* add for guest vmmouse driver to judge this is a relative packet. */ + buttons |= VMMOUSE_RELATIVE_PACKET; } s->queue[s->nb_queue++] = buttons; @@ -151,7 +165,7 @@ static void vmmouse_update_handler(VMMouseState *s, int absolute) static void vmmouse_read_id(VMMouseState *s) { - DPRINTF("vmmouse_read_id()\n"); + trace_vmmouse_read_id(); if (s->nb_queue == VMMOUSE_QUEUE_SIZE) return; @@ -163,19 +177,22 @@ static void vmmouse_read_id(VMMouseState *s) static void vmmouse_request_relative(VMMouseState *s) { - DPRINTF("vmmouse_request_relative()\n"); + trace_vmmouse_request_relative(); + vmmouse_update_handler(s, 0); } static void vmmouse_request_absolute(VMMouseState *s) { - DPRINTF("vmmouse_request_absolute()\n"); + trace_vmmouse_request_absolute(); + vmmouse_update_handler(s, 1); } static void vmmouse_disable(VMMouseState *s) { - DPRINTF("vmmouse_disable()\n"); + trace_vmmouse_disable(); + s->status = 0xffff; vmmouse_remove_handler(s); } @@ -184,7 +201,7 @@ static void vmmouse_data(VMMouseState *s, uint32_t *data, uint32_t size) { int i; - DPRINTF("vmmouse_data(%d)\n", size); + trace_vmmouse_data(size); if (size == 0 || size > 6 || size > s->nb_queue) { printf("vmmouse: driver requested too much data %d\n", size); @@ -260,7 +277,7 @@ static const VMStateDescription vmstate_vmmouse = { .version_id = 0, .minimum_version_id = 0, .post_load = vmmouse_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32_EQUAL(queue_size, VMMouseState, NULL), VMSTATE_UINT32_ARRAY(queue, VMMouseState, VMMOUSE_QUEUE_SIZE), VMSTATE_UINT16(nb_queue, VMMouseState), @@ -284,7 +301,7 @@ static void vmmouse_realizefn(DeviceState *dev, Error **errp) { VMMouseState *s = VMMOUSE(dev); - DPRINTF("vmmouse_init\n"); + trace_vmmouse_init(); if (!s->i8042) { error_setg(errp, "'i8042' link is not set"); diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c index 01d11325a6..60af896225 100644 --- a/hw/i386/x86-iommu.c +++ b/hw/i386/x86-iommu.c @@ -63,7 +63,7 @@ void x86_iommu_irq_to_msi_message(X86IOMMUIrq *irq, MSIMessage *msg_out) msg.redir_hint = irq->redir_hint; msg.dest = irq->dest; msg.__addr_hi = irq->dest & 0xffffff00; - msg.__addr_head = cpu_to_le32(0xfee); + msg.__addr_head = 0xfee; /* Keep this from original MSI address bits */ msg.__not_used = irq->msi_addr_last_bits; @@ -101,7 +101,7 @@ static void x86_iommu_realize(DeviceState *dev, Error **errp) QLIST_INIT(&x86_iommu->iec_notifiers); bool irq_all_kernel = kvm_irqchip_in_kernel() && !kvm_irqchip_is_split(); - if (!pcms || !pcms->bus) { + if (!pcms || !pcms->pcibus) { error_setg(errp, "Machine-type '%s' not supported by IOMMU", mc->name); return; diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 78b05ab7a2..ffbda48917 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -27,7 +27,6 @@ #include "qemu/units.h" #include "qemu/datadir.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qapi/qapi-visit-common.h" #include "qapi/clone-visitor.h" #include "qapi/qapi-visit-machine.h" @@ -59,10 +58,15 @@ #include CONFIG_DEVICES #include "kvm/kvm_i386.h" +#ifdef CONFIG_XEN_EMU +#include "hw/xen/xen.h" +#include "hw/i386/kvm/xen_evtchn.h" +#endif + /* 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, +static void init_topo_info(X86CPUTopoInfo *topo_info, const X86MachineState *x86ms) { MachineState *ms = MACHINE(x86ms); @@ -125,36 +129,47 @@ void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version) ms->smp.max_cpus - 1) + 1; /* - * Can we support APIC ID 255 or higher? + * Can we support APIC ID 255 or higher? With KVM, that requires + * both in-kernel lapic and X2APIC userspace API. * - * Under Xen: yes. - * With userspace emulated lapic: no - * With KVM's in-kernel lapic: only if X2APIC API is enabled. + * kvm_enabled() must go first to ensure that kvm_* references are + * not emitted for the linker to consume (kvm_enabled() is + * a literal `0` in configurations where kvm_* aren't defined) */ - if (x86ms->apic_id_limit > 255 && !xen_enabled() && - (!kvm_irqchip_in_kernel() || !kvm_enable_x2apic())) { + if (kvm_enabled() && x86ms->apic_id_limit > 255 && + kvm_irqchip_in_kernel() && !kvm_enable_x2apic()) { error_report("current -smp configuration requires kernel " "irqchip and X2APIC API support."); exit(EXIT_FAILURE); } + if (kvm_enabled()) { + kvm_set_max_apic_id(x86ms->apic_id_limit); + } + + if (!kvm_irqchip_in_kernel()) { + apic_set_max_apic_id(x86ms->apic_id_limit); + } + possible_cpus = mc->possible_cpu_arch_ids(ms); for (i = 0; i < ms->smp.cpus; i++) { x86_cpu_new(x86ms, possible_cpus->cpus[i].arch_id, &error_fatal); } } -void x86_rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count) +void x86_rtc_set_cpus_count(ISADevice *s, uint16_t cpus_count) { + MC146818RtcState *rtc = MC146818_RTC(s); + if (cpus_count > 0xff) { /* * If the number of CPUs can't be represented in 8 bits, the * BIOS must use "FW_CFG_NB_CPUS". Set RTC field to 0 just * to make old BIOSes fail more predictably. */ - rtc_set_memory(rtc, 0x5f, 0); + mc146818rtc_set_cmos_data(rtc, 0x5f, 0); } else { - rtc_set_memory(rtc, 0x5f, cpus_count - 1); + mc146818rtc_set_cmos_data(rtc, 0x5f, cpus_count - 1); } } @@ -210,7 +225,7 @@ void x86_cpu_plug(HotplugHandler *hotplug_dev, } found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL); - found_cpu->cpu = OBJECT(dev); + found_cpu->cpu = CPU(dev); out: error_propagate(errp, local_err); } @@ -358,8 +373,6 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); if (!cpu_slot) { - MachineState *ms = MACHINE(x86ms); - x86_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" @@ -413,7 +426,12 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, } cpu->thread_id = topo_ids.smt_id; - if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) && + /* + * kvm_enabled() must go first to ensure that kvm_* references are + * not emitted for the linker to consume (kvm_enabled() is + * a literal `0` in configurations where kvm_* aren't defined) + */ + if (kvm_enabled() && hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) && !kvm_hv_vpindex_settable()) { error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX"); return; @@ -502,10 +520,10 @@ static void x86_nmi(NMIState *n, int cpu_index, Error **errp) CPU_FOREACH(cs) { X86CPU *cpu = X86_CPU(cs); - if (!cpu->apic_state) { - cpu_interrupt(cs, CPU_INTERRUPT_NMI); - } else { + if (cpu_is_apic_enabled(cpu->apic_state)) { apic_deliver_nmi(cpu->apic_state); + } else { + cpu_interrupt(cs, CPU_INTERRUPT_NMI); } } } @@ -537,7 +555,7 @@ static void pic_irq_request(void *opaque, int irq, int level) X86CPU *cpu = X86_CPU(cs); trace_x86_pic_interrupt(irq, level); - if (cpu->apic_state && !kvm_irqchip_in_kernel() && + if (cpu_is_apic_enabled(cpu->apic_state) && !kvm_irqchip_in_kernel() && !whpx_apic_in_platform()) { CPU_FOREACH(cs) { cpu = X86_CPU(cs); @@ -602,6 +620,17 @@ void gsi_handler(void *opaque, int n, int level) } /* fall through */ case ISA_NUM_IRQS ... IOAPIC_NUM_PINS - 1: +#ifdef CONFIG_XEN_EMU + /* + * Xen delivers the GSI to the Legacy PIC (not that Legacy PIC + * routing actually works properly under Xen). And then to + * *either* the PIRQ handling or the I/OAPIC depending on + * whether the former wants it. + */ + if (xen_mode == XEN_EMULATE && xen_evtchn_set_gsi(n, level)) { + break; + } +#endif qemu_set_irq(s->ioapic_irq[n], level); break; case IO_APIC_SECONDARY_IRQBASE @@ -611,20 +640,19 @@ void gsi_handler(void *opaque, int n, int level) } } -void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) +void ioapic_init_gsi(GSIState *gsi_state, Object *parent) { DeviceState *dev; SysBusDevice *d; unsigned int i; - assert(parent_name); + assert(parent); if (kvm_ioapic_in_kernel()) { dev = qdev_new(TYPE_KVM_IOAPIC); } else { dev = qdev_new(TYPE_IOAPIC); } - object_property_add_child(object_resolve_path(parent_name, NULL), - "ioapic", OBJECT(dev)); + object_property_add_child(parent, "ioapic", OBJECT(dev)); d = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(d, &error_fatal); sysbus_mmio_map(d, 0, IO_APIC_DEFAULT_ADDRESS); @@ -1115,7 +1143,7 @@ void x86_bios_rom_init(MachineState *ms, const char *default_firmware, char *filename; MemoryRegion *bios, *isa_bios; int bios_size, isa_bios_size; - int ret; + ssize_t ret; /* BIOS load */ bios_name = ms->firmware ?: default_firmware; @@ -1373,6 +1401,7 @@ static void x86_machine_initfn(Object *obj) x86ms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); x86ms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); x86ms->bus_lock_ratelimit = 0; + x86ms->above_4g_mem_start = 4 * GiB; } static void x86_machine_class_init(ObjectClass *oc, void *data) diff --git a/hw/i386/xen/meson.build b/hw/i386/xen/meson.build index be84130300..3f0df8bc07 100644 --- a/hw/i386/xen/meson.build +++ b/hw/i386/xen/meson.build @@ -1,7 +1,11 @@ i386_ss.add(when: 'CONFIG_XEN', if_true: files( - 'xen-hvm.c', - 'xen-mapcache.c', 'xen_apic.c', - 'xen_platform.c', 'xen_pvdevice.c', )) +i386_ss.add(when: ['CONFIG_XEN', xen], if_true: files( + 'xen-hvm.c', +)) + +i386_ss.add(when: 'CONFIG_XEN_BUS', if_true: files( + 'xen_platform.c', +)) diff --git a/hw/i386/xen/trace-events b/hw/i386/xen/trace-events index 5d6be61090..5d0a8d6dcf 100644 --- a/hw/i386/xen/trace-events +++ b/hw/i386/xen/trace-events @@ -7,22 +7,3 @@ xen_platform_log(char *s) "xen platform: %s" xen_pv_mmio_read(uint64_t addr) "WARNING: read from Xen PV Device MMIO space (address 0x%"PRIx64")" xen_pv_mmio_write(uint64_t addr) "WARNING: write to Xen PV Device MMIO space (address 0x%"PRIx64")" -# xen-hvm.c -xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: 0x%lx, size 0x%lx" -xen_client_set_memory(uint64_t start_addr, unsigned long size, bool log_dirty) "0x%"PRIx64" size 0x%lx, log_dirty %i" -handle_ioreq(void *req, uint32_t type, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p type=%d dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" -handle_ioreq_read(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p read type=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" -handle_ioreq_write(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p write type=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" -cpu_ioreq_pio(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p pio dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" -cpu_ioreq_pio_read_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio read reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" -cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" -cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" -xen_map_resource_ioreq(uint32_t id, void *addr) "id: %u addr: %p" -cpu_ioreq_config_read(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" -cpu_ioreq_config_write(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" - -# xen-mapcache.c -xen_map_cache(uint64_t phys_addr) "want 0x%"PRIx64 -xen_remap_bucket(uint64_t index) "index 0x%"PRIx64 -xen_map_cache_return(void* ptr) "%p" - diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 0731f70410..7745cb3963 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -10,53 +10,32 @@ #include "qemu/osdep.h" #include "qemu/units.h" - -#include "cpu.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "hw/i386/pc.h" -#include "hw/southbridge/piix.h" -#include "hw/irq.h" -#include "hw/hw.h" -#include "hw/i386/apic-msidef.h" -#include "hw/xen/xen_common.h" -#include "hw/xen/xen-legacy-backend.h" -#include "hw/xen/xen-bus.h" -#include "hw/xen/xen-x86.h" #include "qapi/error.h" #include "qapi/qapi-commands-migration.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" -#include "qemu/range.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" -#include "sysemu/xen.h" -#include "sysemu/xen-mapcache.h" #include "trace.h" -#include +#include "hw/i386/pc.h" +#include "hw/irq.h" +#include "hw/i386/apic-msidef.h" +#include "hw/xen/xen-x86.h" +#include "qemu/range.h" + +#include "hw/xen/xen-hvm-common.h" +#include "hw/xen/arch_hvm.h" #include +#include "exec/target_page.h" -//#define DEBUG_XEN_HVM - -#ifdef DEBUG_XEN_HVM -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "xen: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -static MemoryRegion ram_memory, ram_640k, ram_lo, ram_hi; +static MemoryRegion ram_640k, ram_lo, ram_hi; static MemoryRegion *framebuffer; static bool xen_in_migration; /* Compatibility with older version */ -/* This allows QEMU to build on a system that has Xen 4.5 or earlier - * installed. This here (not in hw/xen/xen_common.h) because xen/hvm/ioreq.h - * needs to be included before this block and hw/xen/xen_common.h needs to - * be included before xen/hvm/ioreq.h +/* + * This allows QEMU to build on a system that has Xen 4.5 or earlier installed. + * This is here (not in hw/xen/xen_native.h) because xen/hvm/ioreq.h needs to + * be included before this block and hw/xen/xen_native.h needs to be included + * before xen/hvm/ioreq.h */ #ifndef IOREQ_TYPE_VMWARE_PORT #define IOREQ_TYPE_VMWARE_PORT 3 @@ -75,66 +54,14 @@ struct shared_vmport_iopage { typedef struct shared_vmport_iopage shared_vmport_iopage_t; #endif -static inline uint32_t xen_vcpu_eport(shared_iopage_t *shared_page, int i) -{ - return shared_page->vcpu_ioreq[i].vp_eport; -} -static inline ioreq_t *xen_vcpu_ioreq(shared_iopage_t *shared_page, int vcpu) -{ - return &shared_page->vcpu_ioreq[vcpu]; -} - -#define BUFFER_IO_MAX_DELAY 100 - -typedef struct XenPhysmap { - hwaddr start_addr; - ram_addr_t size; - const char *name; - hwaddr phys_offset; - - QLIST_ENTRY(XenPhysmap) list; -} XenPhysmap; +static shared_vmport_iopage_t *shared_vmport_page; static QLIST_HEAD(, XenPhysmap) xen_physmap; - -typedef struct XenPciDevice { - PCIDevice *pci_dev; - uint32_t sbdf; - QLIST_ENTRY(XenPciDevice) entry; -} XenPciDevice; - -typedef struct XenIOState { - ioservid_t ioservid; - shared_iopage_t *shared_page; - shared_vmport_iopage_t *shared_vmport_page; - buffered_iopage_t *buffered_io_page; - xenforeignmemory_resource_handle *fres; - QEMUTimer *buffered_io_timer; - CPUState **cpu_by_vcpu_id; - /* the evtchn port for polling the notification, */ - evtchn_port_t *ioreq_local_port; - /* evtchn remote and local ports for buffered io */ - evtchn_port_t bufioreq_remote_port; - evtchn_port_t bufioreq_local_port; - /* the evtchn fd for polling */ - xenevtchn_handle *xce_handle; - /* which vcpu we are serving */ - int send_vcpu; - - struct xs_handle *xenstore; - MemoryListener memory_listener; - MemoryListener io_listener; - QLIST_HEAD(, XenPciDevice) dev_list; - DeviceListener device_listener; - hwaddr free_phys_offset; - const XenPhysmap *log_for_dirtybit; - /* Buffer used by xen_sync_dirty_bitmap */ - unsigned long *dirty_bitmap; - - Notifier exit; - Notifier suspend; - Notifier wakeup; -} XenIOState; +static const XenPhysmap *log_for_dirtybit; +/* Buffer used by xen_sync_dirty_bitmap */ +static unsigned long *dirty_bitmap; +static Notifier suspend; +static Notifier wakeup; /* Xen specific function for piix pci */ @@ -143,27 +70,15 @@ int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) return irq_num + (PCI_SLOT(pci_dev->devfn) << 2); } -void xen_piix3_set_irq(void *opaque, int irq_num, int level) +void xen_intx_set_irq(void *opaque, int irq_num, int level) { xen_set_pci_intx_level(xen_domid, 0, 0, irq_num >> 2, irq_num & 3, level); } -void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len) +int xen_set_pci_link_route(uint8_t link, uint8_t irq) { - int i; - - /* Scan for updates to PCI link routes (0x60-0x63). */ - for (i = 0; i < len; i++) { - uint8_t v = (val >> (8 * i)) & 0xff; - if (v & 0x80) { - v = 0; - } - v &= 0xf; - if (((address + i) >= PIIX_PIRQCA) && ((address + i) <= PIIX_PIRQCD)) { - xen_set_pci_link_route(xen_domid, address + i - PIIX_PIRQCA, v); - } - } + return xendevicemodel_set_pci_link_route(xen_dmod, xen_domid, link, irq); } int xen_is_pirq_msi(uint32_t msi_data) @@ -235,12 +150,12 @@ static void xen_ram_init(PCMachineState *pcms, */ block_len = (4 * GiB) + x86ms->above_4g_mem_size; } - memory_region_init_ram(&ram_memory, NULL, "xen.ram", block_len, + memory_region_init_ram(&xen_memory, NULL, "xen.ram", block_len, &error_fatal); - *ram_memory_p = &ram_memory; + *ram_memory_p = &xen_memory; memory_region_init_alias(&ram_640k, NULL, "xen.ram.640k", - &ram_memory, 0, 0xa0000); + &xen_memory, 0, 0xa0000); memory_region_add_subregion(sysmem, 0, &ram_640k); /* Skip of the VGA IO memory space, it will be registered later by the VGA * emulated device. @@ -249,58 +164,23 @@ static void xen_ram_init(PCMachineState *pcms, * the Options ROM, so it is registered here as RAM. */ memory_region_init_alias(&ram_lo, NULL, "xen.ram.lo", - &ram_memory, 0xc0000, + &xen_memory, 0xc0000, x86ms->below_4g_mem_size - 0xc0000); memory_region_add_subregion(sysmem, 0xc0000, &ram_lo); if (x86ms->above_4g_mem_size > 0) { memory_region_init_alias(&ram_hi, NULL, "xen.ram.hi", - &ram_memory, 0x100000000ULL, + &xen_memory, 0x100000000ULL, x86ms->above_4g_mem_size); memory_region_add_subregion(sysmem, 0x100000000ULL, &ram_hi); } } -void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr, - Error **errp) -{ - unsigned long nr_pfn; - xen_pfn_t *pfn_list; - int i; - - if (runstate_check(RUN_STATE_INMIGRATE)) { - /* RAM already populated in Xen */ - fprintf(stderr, "%s: do not alloc "RAM_ADDR_FMT - " bytes of ram at "RAM_ADDR_FMT" when runstate is INMIGRATE\n", - __func__, size, ram_addr); - return; - } - - if (mr == &ram_memory) { - return; - } - - trace_xen_ram_alloc(ram_addr, size); - - nr_pfn = size >> TARGET_PAGE_BITS; - pfn_list = g_malloc(sizeof (*pfn_list) * nr_pfn); - - for (i = 0; i < nr_pfn; i++) { - pfn_list[i] = (ram_addr >> TARGET_PAGE_BITS) + i; - } - - if (xc_domain_populate_physmap_exact(xen_xc, xen_domid, nr_pfn, 0, 0, pfn_list)) { - error_setg(errp, "xen: failed to populate ram at " RAM_ADDR_FMT, - ram_addr); - } - - g_free(pfn_list); -} - -static XenPhysmap *get_physmapping(hwaddr start_addr, ram_addr_t size) +static XenPhysmap *get_physmapping(hwaddr start_addr, ram_addr_t size, + int page_mask) { XenPhysmap *physmap = NULL; - start_addr &= TARGET_PAGE_MASK; + start_addr &= page_mask; QLIST_FOREACH(physmap, &xen_physmap, list) { if (range_covers_byte(physmap->start_addr, physmap->size, start_addr)) { @@ -310,9 +190,10 @@ static XenPhysmap *get_physmapping(hwaddr start_addr, ram_addr_t size) return NULL; } -static hwaddr xen_phys_offset_to_gaddr(hwaddr phys_offset, ram_addr_t size) +static hwaddr xen_phys_offset_to_gaddr(hwaddr phys_offset, ram_addr_t size, + int page_mask) { - hwaddr addr = phys_offset & TARGET_PAGE_MASK; + hwaddr addr = phys_offset & page_mask; XenPhysmap *physmap = NULL; QLIST_FOREACH(physmap, &xen_physmap, list) { @@ -367,6 +248,9 @@ static int xen_add_to_physmap(XenIOState *state, MemoryRegion *mr, hwaddr offset_within_region) { + unsigned target_page_bits = qemu_target_page_bits(); + int page_size = qemu_target_page_size(); + int page_mask = -page_size; unsigned long nr_pages; int rc = 0; XenPhysmap *physmap = NULL; @@ -374,7 +258,7 @@ static int xen_add_to_physmap(XenIOState *state, hwaddr phys_offset = memory_region_get_ram_addr(mr); const char *mr_name; - if (get_physmapping(start_addr, size)) { + if (get_physmapping(start_addr, size, page_mask)) { return 0; } if (size <= 0) { @@ -414,9 +298,9 @@ go_physmap: return 0; } - pfn = phys_offset >> TARGET_PAGE_BITS; - start_gpfn = start_addr >> TARGET_PAGE_BITS; - nr_pages = size >> TARGET_PAGE_BITS; + pfn = phys_offset >> target_page_bits; + start_gpfn = start_addr >> target_page_bits; + nr_pages = size >> target_page_bits; rc = xendevicemodel_relocate_memory(xen_dmod, xen_domid, nr_pages, pfn, start_gpfn); if (rc) { @@ -430,8 +314,8 @@ go_physmap: } rc = xendevicemodel_pin_memory_cacheattr(xen_dmod, xen_domid, - start_addr >> TARGET_PAGE_BITS, - (start_addr + size - 1) >> TARGET_PAGE_BITS, + start_addr >> target_page_bits, + (start_addr + size - 1) >> target_page_bits, XEN_DOMCTL_MEM_CACHEATTR_WB); if (rc) { error_report("pin_memory_cacheattr failed: %s", strerror(errno)); @@ -443,11 +327,14 @@ static int xen_remove_from_physmap(XenIOState *state, hwaddr start_addr, ram_addr_t size) { + unsigned target_page_bits = qemu_target_page_bits(); + int page_size = qemu_target_page_size(); + int page_mask = -page_size; int rc = 0; XenPhysmap *physmap = NULL; hwaddr phys_offset = 0; - physmap = get_physmapping(start_addr, size); + physmap = get_physmapping(start_addr, size, page_mask); if (physmap == NULL) { return -1; } @@ -458,9 +345,9 @@ static int xen_remove_from_physmap(XenIOState *state, DPRINTF("unmapping vram to %"HWADDR_PRIx" - %"HWADDR_PRIx", at " "%"HWADDR_PRIx"\n", start_addr, start_addr + size, phys_offset); - size >>= TARGET_PAGE_BITS; - start_addr >>= TARGET_PAGE_BITS; - phys_offset >>= TARGET_PAGE_BITS; + size >>= target_page_bits; + start_addr >>= target_page_bits; + phys_offset >>= target_page_bits; rc = xendevicemodel_relocate_memory(xen_dmod, xen_domid, size, start_addr, phys_offset); if (rc) { @@ -475,201 +362,65 @@ static int xen_remove_from_physmap(XenIOState *state, } QLIST_REMOVE(physmap, list); - if (state->log_for_dirtybit == physmap) { - state->log_for_dirtybit = NULL; - g_free(state->dirty_bitmap); - state->dirty_bitmap = NULL; + if (log_for_dirtybit == physmap) { + log_for_dirtybit = NULL; + g_free(dirty_bitmap); + dirty_bitmap = NULL; } g_free(physmap); return 0; } -static void xen_set_memory(struct MemoryListener *listener, - MemoryRegionSection *section, - bool add) -{ - XenIOState *state = container_of(listener, XenIOState, memory_listener); - hwaddr start_addr = section->offset_within_address_space; - ram_addr_t size = int128_get64(section->size); - bool log_dirty = memory_region_is_logging(section->mr, DIRTY_MEMORY_VGA); - hvmmem_type_t mem_type; - - if (section->mr == &ram_memory) { - return; - } else { - if (add) { - xen_map_memory_section(xen_domid, state->ioservid, - section); - } else { - xen_unmap_memory_section(xen_domid, state->ioservid, - section); - } - } - - if (!memory_region_is_ram(section->mr)) { - return; - } - - if (log_dirty != add) { - return; - } - - trace_xen_client_set_memory(start_addr, size, log_dirty); - - start_addr &= TARGET_PAGE_MASK; - size = TARGET_PAGE_ALIGN(size); - - if (add) { - if (!memory_region_is_rom(section->mr)) { - xen_add_to_physmap(state, start_addr, size, - section->mr, section->offset_within_region); - } else { - mem_type = HVMMEM_ram_ro; - if (xen_set_mem_type(xen_domid, mem_type, - start_addr >> TARGET_PAGE_BITS, - size >> TARGET_PAGE_BITS)) { - DPRINTF("xen_set_mem_type error, addr: "TARGET_FMT_plx"\n", - start_addr); - } - } - } else { - if (xen_remove_from_physmap(state, start_addr, size) < 0) { - DPRINTF("physmapping does not exist at "TARGET_FMT_plx"\n", start_addr); - } - } -} - -static void xen_region_add(MemoryListener *listener, - MemoryRegionSection *section) -{ - memory_region_ref(section->mr); - xen_set_memory(listener, section, true); -} - -static void xen_region_del(MemoryListener *listener, - MemoryRegionSection *section) -{ - xen_set_memory(listener, section, false); - memory_region_unref(section->mr); -} - -static void xen_io_add(MemoryListener *listener, - MemoryRegionSection *section) -{ - XenIOState *state = container_of(listener, XenIOState, io_listener); - MemoryRegion *mr = section->mr; - - if (mr->ops == &unassigned_io_ops) { - return; - } - - memory_region_ref(mr); - - xen_map_io_section(xen_domid, state->ioservid, section); -} - -static void xen_io_del(MemoryListener *listener, - MemoryRegionSection *section) -{ - XenIOState *state = container_of(listener, XenIOState, io_listener); - MemoryRegion *mr = section->mr; - - if (mr->ops == &unassigned_io_ops) { - return; - } - - xen_unmap_io_section(xen_domid, state->ioservid, section); - - memory_region_unref(mr); -} - -static void xen_device_realize(DeviceListener *listener, - DeviceState *dev) -{ - XenIOState *state = container_of(listener, XenIOState, device_listener); - - if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - PCIDevice *pci_dev = PCI_DEVICE(dev); - XenPciDevice *xendev = g_new(XenPciDevice, 1); - - xendev->pci_dev = pci_dev; - xendev->sbdf = PCI_BUILD_BDF(pci_dev_bus_num(pci_dev), - pci_dev->devfn); - QLIST_INSERT_HEAD(&state->dev_list, xendev, entry); - - xen_map_pcidev(xen_domid, state->ioservid, pci_dev); - } -} - -static void xen_device_unrealize(DeviceListener *listener, - DeviceState *dev) -{ - XenIOState *state = container_of(listener, XenIOState, device_listener); - - if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - PCIDevice *pci_dev = PCI_DEVICE(dev); - XenPciDevice *xendev, *next; - - xen_unmap_pcidev(xen_domid, state->ioservid, pci_dev); - - QLIST_FOREACH_SAFE(xendev, &state->dev_list, entry, next) { - if (xendev->pci_dev == pci_dev) { - QLIST_REMOVE(xendev, entry); - g_free(xendev); - break; - } - } - } -} - static void xen_sync_dirty_bitmap(XenIOState *state, hwaddr start_addr, ram_addr_t size) { - hwaddr npages = size >> TARGET_PAGE_BITS; + unsigned target_page_bits = qemu_target_page_bits(); + int page_size = qemu_target_page_size(); + int page_mask = -page_size; + hwaddr npages = size >> target_page_bits; const int width = sizeof(unsigned long) * 8; size_t bitmap_size = DIV_ROUND_UP(npages, width); int rc, i, j; const XenPhysmap *physmap = NULL; - physmap = get_physmapping(start_addr, size); + physmap = get_physmapping(start_addr, size, page_mask); if (physmap == NULL) { /* not handled */ return; } - if (state->log_for_dirtybit == NULL) { - state->log_for_dirtybit = physmap; - state->dirty_bitmap = g_new(unsigned long, bitmap_size); - } else if (state->log_for_dirtybit != physmap) { + if (log_for_dirtybit == NULL) { + log_for_dirtybit = physmap; + dirty_bitmap = g_new(unsigned long, bitmap_size); + } else if (log_for_dirtybit != physmap) { /* Only one range for dirty bitmap can be tracked. */ return; } - rc = xen_track_dirty_vram(xen_domid, start_addr >> TARGET_PAGE_BITS, - npages, state->dirty_bitmap); + rc = xen_track_dirty_vram(xen_domid, start_addr >> target_page_bits, + npages, dirty_bitmap); if (rc < 0) { #ifndef ENODATA #define ENODATA ENOENT #endif if (errno == ENODATA) { memory_region_set_dirty(framebuffer, 0, size); - DPRINTF("xen: track_dirty_vram failed (0x" TARGET_FMT_plx - ", 0x" TARGET_FMT_plx "): %s\n", + DPRINTF("xen: track_dirty_vram failed (0x" HWADDR_FMT_plx + ", 0x" HWADDR_FMT_plx "): %s\n", start_addr, start_addr + size, strerror(errno)); } return; } for (i = 0; i < bitmap_size; i++) { - unsigned long map = state->dirty_bitmap[i]; + unsigned long map = dirty_bitmap[i]; while (map != 0) { j = ctzl(map); map &= ~(1ul << j); memory_region_set_dirty(framebuffer, - (i * width + j) * TARGET_PAGE_SIZE, - TARGET_PAGE_SIZE); + (i * width + j) * page_size, page_size); }; } } @@ -689,12 +440,10 @@ static void xen_log_start(MemoryListener *listener, static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section, int old, int new) { - XenIOState *state = container_of(listener, XenIOState, memory_listener); - if (old & ~new & (1 << DIRTY_MEMORY_VGA)) { - state->log_for_dirtybit = NULL; - g_free(state->dirty_bitmap); - state->dirty_bitmap = NULL; + log_for_dirtybit = NULL; + g_free(dirty_bitmap); + dirty_bitmap = NULL; /* Disable dirty bit tracking */ xen_track_dirty_vram(xen_domid, 0, 0, NULL); } @@ -720,7 +469,7 @@ static void xen_log_global_stop(MemoryListener *listener) xen_in_migration = false; } -static MemoryListener xen_memory_listener = { +static const MemoryListener xen_memory_listener = { .name = "xen-memory", .region_add = xen_region_add, .region_del = xen_region_del, @@ -729,280 +478,9 @@ static MemoryListener xen_memory_listener = { .log_sync = xen_log_sync, .log_global_start = xen_log_global_start, .log_global_stop = xen_log_global_stop, - .priority = 10, + .priority = MEMORY_LISTENER_PRIORITY_ACCEL, }; -static MemoryListener xen_io_listener = { - .name = "xen-io", - .region_add = xen_io_add, - .region_del = xen_io_del, - .priority = 10, -}; - -static DeviceListener xen_device_listener = { - .realize = xen_device_realize, - .unrealize = xen_device_unrealize, -}; - -/* get the ioreq packets from share mem */ -static ioreq_t *cpu_get_ioreq_from_shared_memory(XenIOState *state, int vcpu) -{ - ioreq_t *req = xen_vcpu_ioreq(state->shared_page, vcpu); - - if (req->state != STATE_IOREQ_READY) { - DPRINTF("I/O request not ready: " - "%x, ptr: %x, port: %"PRIx64", " - "data: %"PRIx64", count: %u, size: %u\n", - req->state, req->data_is_ptr, req->addr, - req->data, req->count, req->size); - return NULL; - } - - xen_rmb(); /* see IOREQ_READY /then/ read contents of ioreq */ - - req->state = STATE_IOREQ_INPROCESS; - return req; -} - -/* use poll to get the port notification */ -/* ioreq_vec--out,the */ -/* retval--the number of ioreq packet */ -static ioreq_t *cpu_get_ioreq(XenIOState *state) -{ - MachineState *ms = MACHINE(qdev_get_machine()); - unsigned int max_cpus = ms->smp.max_cpus; - int i; - evtchn_port_t port; - - port = xenevtchn_pending(state->xce_handle); - if (port == state->bufioreq_local_port) { - timer_mod(state->buffered_io_timer, - BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); - return NULL; - } - - if (port != -1) { - for (i = 0; i < max_cpus; i++) { - if (state->ioreq_local_port[i] == port) { - break; - } - } - - if (i == max_cpus) { - hw_error("Fatal error while trying to get io event!\n"); - } - - /* unmask the wanted port again */ - xenevtchn_unmask(state->xce_handle, port); - - /* get the io packet from shared memory */ - state->send_vcpu = i; - return cpu_get_ioreq_from_shared_memory(state, i); - } - - /* read error or read nothing */ - return NULL; -} - -static uint32_t do_inp(uint32_t addr, unsigned long size) -{ - switch (size) { - case 1: - return cpu_inb(addr); - case 2: - return cpu_inw(addr); - case 4: - return cpu_inl(addr); - default: - hw_error("inp: bad size: %04x %lx", addr, size); - } -} - -static void do_outp(uint32_t addr, - unsigned long size, uint32_t val) -{ - switch (size) { - case 1: - return cpu_outb(addr, val); - case 2: - return cpu_outw(addr, val); - case 4: - return cpu_outl(addr, val); - default: - hw_error("outp: bad size: %04x %lx", addr, size); - } -} - -/* - * Helper functions which read/write an object from/to physical guest - * memory, as part of the implementation of an ioreq. - * - * Equivalent to - * cpu_physical_memory_rw(addr + (req->df ? -1 : +1) * req->size * i, - * val, req->size, 0/1) - * except without the integer overflow problems. - */ -static void rw_phys_req_item(hwaddr addr, - ioreq_t *req, uint32_t i, void *val, int rw) -{ - /* Do everything unsigned so overflow just results in a truncated result - * and accesses to undesired parts of guest memory, which is up - * to the guest */ - hwaddr offset = (hwaddr)req->size * i; - if (req->df) { - addr -= offset; - } else { - addr += offset; - } - cpu_physical_memory_rw(addr, val, req->size, rw); -} - -static inline void read_phys_req_item(hwaddr addr, - ioreq_t *req, uint32_t i, void *val) -{ - rw_phys_req_item(addr, req, i, val, 0); -} -static inline void write_phys_req_item(hwaddr addr, - ioreq_t *req, uint32_t i, void *val) -{ - rw_phys_req_item(addr, req, i, val, 1); -} - - -static void cpu_ioreq_pio(ioreq_t *req) -{ - uint32_t i; - - trace_cpu_ioreq_pio(req, req->dir, req->df, req->data_is_ptr, req->addr, - req->data, req->count, req->size); - - if (req->size > sizeof(uint32_t)) { - hw_error("PIO: bad size (%u)", req->size); - } - - if (req->dir == IOREQ_READ) { - if (!req->data_is_ptr) { - req->data = do_inp(req->addr, req->size); - trace_cpu_ioreq_pio_read_reg(req, req->data, req->addr, - req->size); - } else { - uint32_t tmp; - - for (i = 0; i < req->count; i++) { - tmp = do_inp(req->addr, req->size); - write_phys_req_item(req->data, req, i, &tmp); - } - } - } else if (req->dir == IOREQ_WRITE) { - if (!req->data_is_ptr) { - trace_cpu_ioreq_pio_write_reg(req, req->data, req->addr, - req->size); - do_outp(req->addr, req->size, req->data); - } else { - for (i = 0; i < req->count; i++) { - uint32_t tmp = 0; - - read_phys_req_item(req->data, req, i, &tmp); - do_outp(req->addr, req->size, tmp); - } - } - } -} - -static void cpu_ioreq_move(ioreq_t *req) -{ - uint32_t i; - - trace_cpu_ioreq_move(req, req->dir, req->df, req->data_is_ptr, req->addr, - req->data, req->count, req->size); - - if (req->size > sizeof(req->data)) { - hw_error("MMIO: bad size (%u)", req->size); - } - - if (!req->data_is_ptr) { - if (req->dir == IOREQ_READ) { - for (i = 0; i < req->count; i++) { - read_phys_req_item(req->addr, req, i, &req->data); - } - } else if (req->dir == IOREQ_WRITE) { - for (i = 0; i < req->count; i++) { - write_phys_req_item(req->addr, req, i, &req->data); - } - } - } else { - uint64_t tmp; - - if (req->dir == IOREQ_READ) { - for (i = 0; i < req->count; i++) { - read_phys_req_item(req->addr, req, i, &tmp); - write_phys_req_item(req->data, req, i, &tmp); - } - } else if (req->dir == IOREQ_WRITE) { - for (i = 0; i < req->count; i++) { - read_phys_req_item(req->data, req, i, &tmp); - write_phys_req_item(req->addr, req, i, &tmp); - } - } - } -} - -static void cpu_ioreq_config(XenIOState *state, ioreq_t *req) -{ - uint32_t sbdf = req->addr >> 32; - uint32_t reg = req->addr; - XenPciDevice *xendev; - - if (req->size != sizeof(uint8_t) && req->size != sizeof(uint16_t) && - req->size != sizeof(uint32_t)) { - hw_error("PCI config access: bad size (%u)", req->size); - } - - if (req->count != 1) { - hw_error("PCI config access: bad count (%u)", req->count); - } - - QLIST_FOREACH(xendev, &state->dev_list, entry) { - if (xendev->sbdf != sbdf) { - continue; - } - - if (!req->data_is_ptr) { - if (req->dir == IOREQ_READ) { - req->data = pci_host_config_read_common( - xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, - req->size); - trace_cpu_ioreq_config_read(req, xendev->sbdf, reg, - req->size, req->data); - } else if (req->dir == IOREQ_WRITE) { - trace_cpu_ioreq_config_write(req, xendev->sbdf, reg, - req->size, req->data); - pci_host_config_write_common( - xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, - req->data, req->size); - } - } else { - uint32_t tmp; - - if (req->dir == IOREQ_READ) { - tmp = pci_host_config_read_common( - xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, - req->size); - trace_cpu_ioreq_config_read(req, xendev->sbdf, reg, - req->size, tmp); - write_phys_req_item(req->data, req, 0, &tmp); - } else if (req->dir == IOREQ_WRITE) { - read_phys_req_item(req->data, req, 0, &tmp); - trace_cpu_ioreq_config_write(req, xendev->sbdf, reg, - req->size, tmp); - pci_host_config_write_common( - xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, - tmp, req->size); - } - } - } -} - static void regs_to_cpu(vmware_regs_t *vmport_regs, ioreq_t *req) { X86CPU *cpu; @@ -1034,9 +512,9 @@ static void handle_vmport_ioreq(XenIOState *state, ioreq_t *req) { vmware_regs_t *vmport_regs; - assert(state->shared_vmport_page); + assert(shared_vmport_page); vmport_regs = - &state->shared_vmport_page->vcpu_vmport_regs[state->send_vcpu]; + &shared_vmport_page->vcpu_vmport_regs[state->send_vcpu]; QEMU_BUILD_BUG_ON(sizeof(*req) < sizeof(*vmport_regs)); current_cpu = state->cpu_by_vcpu_id[state->send_vcpu]; @@ -1046,226 +524,6 @@ static void handle_vmport_ioreq(XenIOState *state, ioreq_t *req) current_cpu = NULL; } -static void handle_ioreq(XenIOState *state, ioreq_t *req) -{ - trace_handle_ioreq(req, req->type, req->dir, req->df, req->data_is_ptr, - req->addr, req->data, req->count, req->size); - - if (!req->data_is_ptr && (req->dir == IOREQ_WRITE) && - (req->size < sizeof (target_ulong))) { - req->data &= ((target_ulong) 1 << (8 * req->size)) - 1; - } - - if (req->dir == IOREQ_WRITE) - trace_handle_ioreq_write(req, req->type, req->df, req->data_is_ptr, - req->addr, req->data, req->count, req->size); - - switch (req->type) { - case IOREQ_TYPE_PIO: - cpu_ioreq_pio(req); - break; - case IOREQ_TYPE_COPY: - cpu_ioreq_move(req); - break; - case IOREQ_TYPE_VMWARE_PORT: - handle_vmport_ioreq(state, req); - break; - case IOREQ_TYPE_TIMEOFFSET: - break; - case IOREQ_TYPE_INVALIDATE: - xen_invalidate_map_cache(); - break; - case IOREQ_TYPE_PCI_CONFIG: - cpu_ioreq_config(state, req); - break; - default: - hw_error("Invalid ioreq type 0x%x\n", req->type); - } - if (req->dir == IOREQ_READ) { - trace_handle_ioreq_read(req, req->type, req->df, req->data_is_ptr, - req->addr, req->data, req->count, req->size); - } -} - -static bool handle_buffered_iopage(XenIOState *state) -{ - buffered_iopage_t *buf_page = state->buffered_io_page; - buf_ioreq_t *buf_req = NULL; - bool handled_ioreq = false; - ioreq_t req; - int qw; - - if (!buf_page) { - return 0; - } - - memset(&req, 0x00, sizeof(req)); - req.state = STATE_IOREQ_READY; - req.count = 1; - req.dir = IOREQ_WRITE; - - for (;;) { - uint32_t rdptr = buf_page->read_pointer, wrptr; - - xen_rmb(); - wrptr = buf_page->write_pointer; - xen_rmb(); - if (rdptr != buf_page->read_pointer) { - continue; - } - if (rdptr == wrptr) { - break; - } - buf_req = &buf_page->buf_ioreq[rdptr % IOREQ_BUFFER_SLOT_NUM]; - req.size = 1U << buf_req->size; - req.addr = buf_req->addr; - req.data = buf_req->data; - req.type = buf_req->type; - xen_rmb(); - qw = (req.size == 8); - if (qw) { - if (rdptr + 1 == wrptr) { - hw_error("Incomplete quad word buffered ioreq"); - } - buf_req = &buf_page->buf_ioreq[(rdptr + 1) % - IOREQ_BUFFER_SLOT_NUM]; - req.data |= ((uint64_t)buf_req->data) << 32; - xen_rmb(); - } - - handle_ioreq(state, &req); - - /* Only req.data may get updated by handle_ioreq(), albeit even that - * should not happen as such data would never make it to the guest (we - * can only usefully see writes here after all). - */ - assert(req.state == STATE_IOREQ_READY); - assert(req.count == 1); - assert(req.dir == IOREQ_WRITE); - assert(!req.data_is_ptr); - - qatomic_add(&buf_page->read_pointer, qw + 1); - handled_ioreq = true; - } - - return handled_ioreq; -} - -static void handle_buffered_io(void *opaque) -{ - XenIOState *state = opaque; - - if (handle_buffered_iopage(state)) { - timer_mod(state->buffered_io_timer, - BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); - } else { - timer_del(state->buffered_io_timer); - xenevtchn_unmask(state->xce_handle, state->bufioreq_local_port); - } -} - -static void cpu_handle_ioreq(void *opaque) -{ - XenIOState *state = opaque; - ioreq_t *req = cpu_get_ioreq(state); - - handle_buffered_iopage(state); - if (req) { - ioreq_t copy = *req; - - xen_rmb(); - handle_ioreq(state, ©); - req->data = copy.data; - - if (req->state != STATE_IOREQ_INPROCESS) { - fprintf(stderr, "Badness in I/O request ... not in service?!: " - "%x, ptr: %x, port: %"PRIx64", " - "data: %"PRIx64", count: %u, size: %u, type: %u\n", - req->state, req->data_is_ptr, req->addr, - req->data, req->count, req->size, req->type); - destroy_hvm_domain(false); - return; - } - - xen_wmb(); /* Update ioreq contents /then/ update state. */ - - /* - * We do this before we send the response so that the tools - * have the opportunity to pick up on the reset before the - * guest resumes and does a hlt with interrupts disabled which - * causes Xen to powerdown the domain. - */ - if (runstate_is_running()) { - ShutdownCause request; - - if (qemu_shutdown_requested_get()) { - destroy_hvm_domain(false); - } - request = qemu_reset_requested_get(); - if (request) { - qemu_system_reset(request); - destroy_hvm_domain(true); - } - } - - req->state = STATE_IORESP_READY; - xenevtchn_notify(state->xce_handle, - state->ioreq_local_port[state->send_vcpu]); - } -} - -static void xen_main_loop_prepare(XenIOState *state) -{ - int evtchn_fd = -1; - - if (state->xce_handle != NULL) { - evtchn_fd = xenevtchn_fd(state->xce_handle); - } - - state->buffered_io_timer = timer_new_ms(QEMU_CLOCK_REALTIME, handle_buffered_io, - state); - - if (evtchn_fd != -1) { - CPUState *cpu_state; - - DPRINTF("%s: Init cpu_by_vcpu_id\n", __func__); - CPU_FOREACH(cpu_state) { - DPRINTF("%s: cpu_by_vcpu_id[%d]=%p\n", - __func__, cpu_state->cpu_index, cpu_state); - state->cpu_by_vcpu_id[cpu_state->cpu_index] = cpu_state; - } - qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, state); - } -} - - -static void xen_hvm_change_state_handler(void *opaque, bool running, - RunState rstate) -{ - XenIOState *state = opaque; - - if (running) { - xen_main_loop_prepare(state); - } - - xen_set_ioreq_server_state(xen_domid, - state->ioservid, - (rstate == RUN_STATE_RUNNING)); -} - -static void xen_exit_notifier(Notifier *n, void *data) -{ - XenIOState *state = container_of(n, XenIOState, exit); - - xen_destroy_ioreq_server(xen_domid, state->ioservid); - if (state->fres != NULL) { - xenforeignmemory_unmap_resource(xen_fmem, state->fres); - } - - xenevtchn_close(state->xce_handle); - xs_daemon_close(state->xenstore); -} - #ifdef XEN_COMPAT_PHYSMAP static void xen_read_physmap(XenIOState *state) { @@ -1325,133 +583,34 @@ static void xen_wakeup_notifier(Notifier *notifier, void *data) xc_set_hvm_param(xen_xc, xen_domid, HVM_PARAM_ACPI_S_STATE, 0); } -static int xen_map_ioreq_server(XenIOState *state) -{ - void *addr = NULL; - xen_pfn_t ioreq_pfn; - xen_pfn_t bufioreq_pfn; - evtchn_port_t bufioreq_evtchn; - int rc; - - /* - * Attempt to map using the resource API and fall back to normal - * foreign mapping if this is not supported. - */ - QEMU_BUILD_BUG_ON(XENMEM_resource_ioreq_server_frame_bufioreq != 0); - QEMU_BUILD_BUG_ON(XENMEM_resource_ioreq_server_frame_ioreq(0) != 1); - state->fres = xenforeignmemory_map_resource(xen_fmem, xen_domid, - XENMEM_resource_ioreq_server, - state->ioservid, 0, 2, - &addr, - PROT_READ | PROT_WRITE, 0); - if (state->fres != NULL) { - trace_xen_map_resource_ioreq(state->ioservid, addr); - state->buffered_io_page = addr; - state->shared_page = addr + TARGET_PAGE_SIZE; - } else if (errno != EOPNOTSUPP) { - error_report("failed to map ioreq server resources: error %d handle=%p", - errno, xen_xc); - return -1; - } - - rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, - (state->shared_page == NULL) ? - &ioreq_pfn : NULL, - (state->buffered_io_page == NULL) ? - &bufioreq_pfn : NULL, - &bufioreq_evtchn); - if (rc < 0) { - error_report("failed to get ioreq server info: error %d handle=%p", - errno, xen_xc); - return rc; - } - - if (state->shared_page == NULL) { - DPRINTF("shared page at pfn %lx\n", ioreq_pfn); - - state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ | PROT_WRITE, - 1, &ioreq_pfn, NULL); - if (state->shared_page == NULL) { - error_report("map shared IO page returned error %d handle=%p", - errno, xen_xc); - } - } - - if (state->buffered_io_page == NULL) { - DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); - - state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ | PROT_WRITE, - 1, &bufioreq_pfn, - NULL); - if (state->buffered_io_page == NULL) { - error_report("map buffered IO page returned error %d", errno); - return -1; - } - } - - if (state->shared_page == NULL || state->buffered_io_page == NULL) { - return -1; - } - - DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); - - state->bufioreq_remote_port = bufioreq_evtchn; - - return 0; -} - void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) { MachineState *ms = MACHINE(pcms); unsigned int max_cpus = ms->smp.max_cpus; - int i, rc; + int rc; xen_pfn_t ioreq_pfn; XenIOState *state; state = g_new0(XenIOState, 1); - state->xce_handle = xenevtchn_open(NULL, 0); - if (state->xce_handle == NULL) { - perror("xen: event channel open"); - goto err; - } + xen_register_ioreq(state, max_cpus, &xen_memory_listener); - state->xenstore = xs_daemon_open(); - if (state->xenstore == NULL) { - perror("xen: xenstore open"); - goto err; - } + QLIST_INIT(&xen_physmap); + xen_read_physmap(state); - xen_create_ioreq_server(xen_domid, &state->ioservid); + suspend.notify = xen_suspend_notifier; + qemu_register_suspend_notifier(&suspend); - state->exit.notify = xen_exit_notifier; - qemu_add_exit_notifier(&state->exit); - - state->suspend.notify = xen_suspend_notifier; - qemu_register_suspend_notifier(&state->suspend); - - state->wakeup.notify = xen_wakeup_notifier; - qemu_register_wakeup_notifier(&state->wakeup); - - /* - * Register wake-up support in QMP query-current-machine API - */ - qemu_register_wakeup_support(); - - rc = xen_map_ioreq_server(state); - if (rc < 0) { - goto err; - } + wakeup.notify = xen_wakeup_notifier; + qemu_register_wakeup_notifier(&wakeup); rc = xen_get_vmport_regs_pfn(xen_xc, xen_domid, &ioreq_pfn); if (!rc) { DPRINTF("shared vmport page at pfn %lx\n", ioreq_pfn); - state->shared_vmport_page = + shared_vmport_page = xenforeignmemory_map(xen_fmem, xen_domid, PROT_READ|PROT_WRITE, 1, &ioreq_pfn, NULL); - if (state->shared_vmport_page == NULL) { + if (shared_vmport_page == NULL) { error_report("map shared vmport IO page returned error %d handle=%p", errno, xen_xc); goto err; @@ -1462,70 +621,8 @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) goto err; } - /* Note: cpus is empty at this point in init */ - state->cpu_by_vcpu_id = g_new0(CPUState *, max_cpus); - - rc = xen_set_ioreq_server_state(xen_domid, state->ioservid, true); - if (rc < 0) { - error_report("failed to enable ioreq server info: error %d handle=%p", - errno, xen_xc); - goto err; - } - - state->ioreq_local_port = g_new0(evtchn_port_t, max_cpus); - - /* FIXME: how about if we overflow the page here? */ - for (i = 0; i < max_cpus; i++) { - rc = xenevtchn_bind_interdomain(state->xce_handle, xen_domid, - xen_vcpu_eport(state->shared_page, i)); - if (rc == -1) { - error_report("shared evtchn %d bind error %d", i, errno); - goto err; - } - state->ioreq_local_port[i] = rc; - } - - rc = xenevtchn_bind_interdomain(state->xce_handle, xen_domid, - state->bufioreq_remote_port); - if (rc == -1) { - error_report("buffered evtchn bind error %d", errno); - goto err; - } - state->bufioreq_local_port = rc; - - /* Init RAM management */ -#ifdef XEN_COMPAT_PHYSMAP - xen_map_cache_init(xen_phys_offset_to_gaddr, state); -#else - xen_map_cache_init(NULL, state); -#endif xen_ram_init(pcms, ms->ram_size, ram_memory); - qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); - - state->memory_listener = xen_memory_listener; - memory_listener_register(&state->memory_listener, &address_space_memory); - state->log_for_dirtybit = NULL; - - state->io_listener = xen_io_listener; - memory_listener_register(&state->io_listener, &address_space_io); - - state->device_listener = xen_device_listener; - QLIST_INIT(&state->dev_list); - device_listener_register(&state->device_listener); - - xen_bus_init(); - - /* Initialize backend core & drivers */ - if (xen_be_init() != 0) { - error_report("xen backend core setup failed"); - goto err; - } - xen_be_register_common(); - - QLIST_INIT(&xen_physmap); - xen_read_physmap(state); - /* Disable ACPI build because Xen handles it */ pcms->acpi_build_enabled = false; @@ -1536,72 +633,28 @@ err: exit(1); } -void destroy_hvm_domain(bool reboot) -{ - xc_interface *xc_handle; - int sts; - int rc; - - unsigned int reason = reboot ? SHUTDOWN_reboot : SHUTDOWN_poweroff; - - if (xen_dmod) { - rc = xendevicemodel_shutdown(xen_dmod, xen_domid, reason); - if (!rc) { - return; - } - if (errno != ENOTTY /* old Xen */) { - perror("xendevicemodel_shutdown failed"); - } - /* well, try the old thing then */ - } - - xc_handle = xc_interface_open(0, 0, 0); - if (xc_handle == NULL) { - fprintf(stderr, "Cannot acquire xenctrl handle\n"); - } else { - sts = xc_domain_shutdown(xc_handle, xen_domid, reason); - if (sts != 0) { - fprintf(stderr, "xc_domain_shutdown failed to issue %s, " - "sts %d, %s\n", reboot ? "reboot" : "poweroff", - sts, strerror(errno)); - } else { - fprintf(stderr, "Issued domain %d %s\n", xen_domid, - reboot ? "reboot" : "poweroff"); - } - xc_interface_close(xc_handle); - } -} - void xen_register_framebuffer(MemoryRegion *mr) { framebuffer = mr; } -void xen_shutdown_fatal_error(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fprintf(stderr, "Will destroy the domain.\n"); - /* destroy the domain */ - qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_ERROR); -} - void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) { + unsigned target_page_bits = qemu_target_page_bits(); + int page_size = qemu_target_page_size(); + int page_mask = -page_size; + if (unlikely(xen_in_migration)) { int rc; ram_addr_t start_pfn, nb_pages; - start = xen_phys_offset_to_gaddr(start, length); + start = xen_phys_offset_to_gaddr(start, length, page_mask); if (length == 0) { - length = TARGET_PAGE_SIZE; + length = page_size; } - start_pfn = start >> TARGET_PAGE_BITS; - nb_pages = ((start + length + TARGET_PAGE_SIZE - 1) >> TARGET_PAGE_BITS) + start_pfn = start >> target_page_bits; + nb_pages = ((start + length + page_size - 1) >> target_page_bits) - start_pfn; rc = xen_modified_memory(xen_domid, start_pfn, nb_pages); if (rc) { @@ -1620,3 +673,60 @@ void qmp_xen_set_global_dirty_log(bool enable, Error **errp) memory_global_dirty_log_stop(GLOBAL_DIRTY_MIGRATION); } } + +void arch_xen_set_memory(XenIOState *state, MemoryRegionSection *section, + bool add) +{ + unsigned target_page_bits = qemu_target_page_bits(); + int page_size = qemu_target_page_size(); + int page_mask = -page_size; + hwaddr start_addr = section->offset_within_address_space; + ram_addr_t size = int128_get64(section->size); + bool log_dirty = memory_region_is_logging(section->mr, DIRTY_MEMORY_VGA); + hvmmem_type_t mem_type; + + if (!memory_region_is_ram(section->mr)) { + return; + } + + if (log_dirty != add) { + return; + } + + trace_xen_client_set_memory(start_addr, size, log_dirty); + + start_addr &= page_mask; + size = ROUND_UP(size, page_size); + + if (add) { + if (!memory_region_is_rom(section->mr)) { + xen_add_to_physmap(state, start_addr, size, + section->mr, section->offset_within_region); + } else { + mem_type = HVMMEM_ram_ro; + if (xen_set_mem_type(xen_domid, mem_type, + start_addr >> target_page_bits, + size >> target_page_bits)) { + DPRINTF("xen_set_mem_type error, addr: "HWADDR_FMT_plx"\n", + start_addr); + } + } + } else { + if (xen_remove_from_physmap(state, start_addr, size) < 0) { + DPRINTF("physmapping does not exist at "HWADDR_FMT_plx"\n", start_addr); + } + } +} + +void arch_handle_ioreq(XenIOState *state, ioreq_t *req) +{ + switch (req->type) { + case IOREQ_TYPE_VMWARE_PORT: + handle_vmport_ioreq(state, req); + break; + default: + hw_error("Invalid ioreq type 0x%x\n", req->type); + } + + return; +} diff --git a/hw/i386/xen/xen_apic.c b/hw/i386/xen/xen_apic.c index 7c7a60b166..101e16a766 100644 --- a/hw/i386/xen/xen_apic.c +++ b/hw/i386/xen/xen_apic.c @@ -49,8 +49,9 @@ static void xen_apic_realize(DeviceState *dev, Error **errp) msi_nonbroken = true; } -static void xen_apic_set_base(APICCommonState *s, uint64_t val) +static int xen_apic_set_base(APICCommonState *s, uint64_t val) { + return 0; } static void xen_apic_set_tpr(APICCommonState *s, uint8_t val) diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index a64265cca0..708488af32 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -25,12 +25,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "hw/ide.h" #include "hw/ide/pci.h" #include "hw/pci/pci.h" -#include "hw/xen/xen_common.h" #include "migration/vmstate.h" -#include "hw/xen/xen-legacy-backend.h" +#include "net/net.h" #include "trace.h" #include "sysemu/xen.h" #include "sysemu/block-backend.h" @@ -38,6 +36,13 @@ #include "qemu/module.h" #include "qom/object.h" +#ifdef CONFIG_XEN +#include "hw/xen/xen_native.h" +#endif + +/* The rule is that xen_native.h must come first */ +#include "hw/xen/xen.h" + //#define DEBUG_PLATFORM #ifdef DEBUG_PLATFORM @@ -109,12 +114,25 @@ static void log_writeb(PCIXenPlatformState *s, char val) #define _UNPLUG_NVME_DISKS 3 #define UNPLUG_NVME_DISKS (1u << _UNPLUG_NVME_DISKS) +static bool pci_device_is_passthrough(PCIDevice *d) +{ + if (!strcmp(d->name, "xen-pci-passthrough")) { + return true; + } + + if (xen_mode == XEN_EMULATE && !strcmp(d->name, "vfio-pci")) { + return true; + } + + return false; +} + static void unplug_nic(PCIBus *b, PCIDevice *d, void *o) { /* We have to ignore passthrough devices */ if (pci_get_word(d->config + PCI_CLASS_DEVICE) == PCI_CLASS_NETWORK_ETHERNET - && strcmp(d->name, "xen-pci-passthrough") != 0) { + && !pci_device_is_passthrough(d)) { object_unparent(OBJECT(d)); } } @@ -122,9 +140,14 @@ static void unplug_nic(PCIBus *b, PCIDevice *d, void *o) /* Remove the peer of the NIC device. Normally, this would be a tap device. */ static void del_nic_peer(NICState *nic, void *opaque) { - NetClientState *nc; + NetClientState *nc = qemu_get_queue(nic); + ObjectClass *klass = module_object_class_by_name(nc->model); + + /* Only delete peers of PCI NICs that we're about to delete */ + if (!klass || !object_class_dynamic_cast(klass, TYPE_PCI_DEVICE)) { + return; + } - nc = qemu_get_queue(nic); if (nc->peer) qemu_del_net_client(nc->peer); } @@ -146,38 +169,60 @@ static void pci_unplug_nics(PCIBus *bus) * * [1] https://xenbits.xen.org/gitweb/?p=xen.git;a=blob;f=docs/misc/hvm-emulated-unplug.pandoc */ -static void pci_xen_ide_unplug(DeviceState *dev, bool aux) +struct ide_unplug_state { + bool aux; + int nr_unplugged; +}; + +static int ide_dev_unplug(DeviceState *dev, void *_st) { - PCIIDEState *pci_ide; - int i; + struct ide_unplug_state *st = _st; IDEDevice *idedev; IDEBus *idebus; BlockBackend *blk; + int unit; - pci_ide = PCI_IDE(dev); - - for (i = aux ? 1 : 0; i < 4; i++) { - idebus = &pci_ide->bus[i / 2]; - blk = idebus->ifs[i % 2].blk; - - if (blk && idebus->ifs[i % 2].drive_kind != IDE_CD) { - if (!(i % 2)) { - idedev = idebus->master; - } else { - idedev = idebus->slave; - } - - blk_drain(blk); - blk_flush(blk); - - blk_detach_dev(blk, DEVICE(idedev)); - idebus->ifs[i % 2].blk = NULL; - idedev->conf.blk = NULL; - monitor_remove_blk(blk); - blk_unref(blk); - } + idedev = IDE_DEVICE(object_dynamic_cast(OBJECT(dev), "ide-hd")); + if (!idedev) { + return 0; + } + + idebus = IDE_BUS(qdev_get_parent_bus(dev)); + + unit = (idedev == idebus->slave); + assert(unit || idedev == idebus->master); + + if (st->aux && !unit && !strcmp(BUS(idebus)->name, "ide.0")) { + return 0; + } + + blk = idebus->ifs[unit].blk; + if (blk) { + blk_drain(blk); + blk_flush(blk); + + blk_detach_dev(blk, DEVICE(idedev)); + idebus->ifs[unit].blk = NULL; + idedev->conf.blk = NULL; + monitor_remove_blk(blk); + blk_unref(blk); + } + + object_unparent(OBJECT(dev)); + st->nr_unplugged++; + + return 0; +} + +static void pci_xen_ide_unplug(PCIDevice *d, bool aux) +{ + struct ide_unplug_state st = { aux, 0 }; + DeviceState *dev = DEVICE(d); + + qdev_walk_children(dev, NULL, NULL, ide_dev_unplug, NULL, &st); + if (st.nr_unplugged) { + pci_device_reset(d); } - qdev_reset_all(dev); } static void unplug_disks(PCIBus *b, PCIDevice *d, void *opaque) @@ -187,13 +232,13 @@ static void unplug_disks(PCIBus *b, PCIDevice *d, void *opaque) !(flags & UNPLUG_IDE_SCSI_DISKS); /* We have to ignore passthrough devices */ - if (!strcmp(d->name, "xen-pci-passthrough")) { + if (pci_device_is_passthrough(d)) return; - } switch (pci_get_word(d->config + PCI_CLASS_DEVICE)) { case PCI_CLASS_STORAGE_IDE: - pci_xen_ide_unplug(DEVICE(d), aux); + case PCI_CLASS_STORAGE_SATA: + pci_xen_ide_unplug(d, aux); break; case PCI_CLASS_STORAGE_SCSI: @@ -268,18 +313,26 @@ static void platform_fixed_ioport_writeb(void *opaque, uint32_t addr, uint32_t v PCIXenPlatformState *s = opaque; switch (addr) { - case 0: /* Platform flags */ { - hvmmem_type_t mem_type = (val & PFFLAG_ROM_LOCK) ? - HVMMEM_ram_ro : HVMMEM_ram_rw; - if (xen_set_mem_type(xen_domid, mem_type, 0xc0, 0x40)) { - DPRINTF("unable to change ro/rw state of ROM memory area!\n"); - } else { + case 0: /* Platform flags */ + if (xen_mode == XEN_EMULATE) { + /* XX: Use i440gx/q35 PAM setup to do this? */ s->flags = val & PFFLAG_ROM_LOCK; - DPRINTF("changed ro/rw state of ROM memory area. now is %s state.\n", - (mem_type == HVMMEM_ram_ro ? "ro":"rw")); +#ifdef CONFIG_XEN + } else { + hvmmem_type_t mem_type = (val & PFFLAG_ROM_LOCK) ? + HVMMEM_ram_ro : HVMMEM_ram_rw; + + if (xen_set_mem_type(xen_domid, mem_type, 0xc0, 0x40)) { + DPRINTF("unable to change ro/rw state of ROM memory area!\n"); + } else { + s->flags = val & PFFLAG_ROM_LOCK; + DPRINTF("changed ro/rw state of ROM memory area. now is %s state.\n", + (mem_type == HVMMEM_ram_ro ? "ro" : "rw")); + } +#endif } break; - } + case 2: log_writeb(s, val); break; @@ -445,7 +498,7 @@ static uint64_t platform_mmio_read(void *opaque, hwaddr addr, unsigned size) { DPRINTF("Warning: attempted read from physical address " - "0x" TARGET_FMT_plx " in xen platform mmio space\n", addr); + "0x" HWADDR_FMT_plx " in xen platform mmio space\n", addr); return 0; } @@ -454,7 +507,7 @@ static void platform_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { DPRINTF("Warning: attempted write of 0x%"PRIx64" to physical " - "address 0x" TARGET_FMT_plx " in xen platform mmio space\n", + "address 0x" HWADDR_FMT_plx " in xen platform mmio space\n", val, addr); } @@ -484,7 +537,7 @@ static const VMStateDescription vmstate_xen_platform = { .version_id = 4, .minimum_version_id = 4, .post_load = xen_platform_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PCIXenPlatformState), VMSTATE_UINT8(flags, PCIXenPlatformState), VMSTATE_END_OF_LIST() @@ -497,8 +550,8 @@ static void xen_platform_realize(PCIDevice *dev, Error **errp) uint8_t *pci_conf; /* Device will crash on reset if xen is not initialized */ - if (!xen_enabled()) { - error_setg(errp, "xen-platform device requires the Xen accelerator"); + if (xen_mode == XEN_DISABLED) { + error_setg(errp, "xen-platform device requires a Xen guest"); return; } diff --git a/hw/i386/xen/xen_pvdevice.c b/hw/i386/xen/xen_pvdevice.c index 1ea95fa601..ed621531d8 100644 --- a/hw/i386/xen/xen_pvdevice.c +++ b/hw/i386/xen/xen_pvdevice.c @@ -32,7 +32,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "trace.h" @@ -77,7 +77,7 @@ static const VMStateDescription vmstate_xen_pvdevice = { .name = "xen-pvdevice", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, XenPVDevice), VMSTATE_END_OF_LIST() } diff --git a/hw/ide/Kconfig b/hw/ide/Kconfig index dd85fa3619..6dfc5a2129 100644 --- a/hw/ide/Kconfig +++ b/hw/ide/Kconfig @@ -1,51 +1,58 @@ config IDE_CORE bool -config IDE_QDEV +config IDE_BUS bool select IDE_CORE +config IDE_DEV + bool + depends on IDE_BUS + config IDE_PCI bool depends on PCI - select IDE_QDEV + select IDE_BUS + select IDE_DEV config IDE_ISA bool depends on ISA_BUS - select IDE_QDEV + select IDE_BUS + select IDE_DEV config IDE_PIIX bool select IDE_PCI - select IDE_QDEV config IDE_CMD646 bool select IDE_PCI - select IDE_QDEV config IDE_MACIO bool - select IDE_QDEV + select IDE_BUS + select IDE_DEV config IDE_MMIO bool - select IDE_QDEV + select IDE_BUS + select IDE_DEV config IDE_VIA bool select IDE_PCI - select IDE_QDEV config MICRODRIVE bool - select IDE_QDEV + select IDE_BUS + select IDE_DEV depends on PCMCIA config AHCI bool - select IDE_QDEV + select IDE_BUS + select IDE_DEV config AHCI_ICH9 bool @@ -56,4 +63,7 @@ config AHCI_ICH9 config IDE_SII3112 bool select IDE_PCI - select IDE_QDEV + +config IDE_CF + bool + default y if IDE_BUS diff --git a/hw/ide/ahci-allwinner.c b/hw/ide/ahci-allwinner.c index 227e747ba7..9620de8ce8 100644 --- a/hw/ide/ahci-allwinner.c +++ b/hw/ide/ahci-allwinner.c @@ -19,9 +19,8 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "sysemu/dma.h" -#include "hw/ide/internal.h" #include "migration/vmstate.h" -#include "ahci_internal.h" +#include "hw/ide/ahci-sysbus.h" #include "trace.h" @@ -97,7 +96,7 @@ static const VMStateDescription vmstate_allwinner_ahci = { .name = "allwinner-ahci", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AllwinnerAHCIState, ALLWINNER_AHCI_MMIO_SIZE / 4), VMSTATE_END_OF_LIST() diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci-internal.h similarity index 98% rename from hw/ide/ahci_internal.h rename to hw/ide/ahci-internal.h index 109de9e2d1..7e63ea2310 100644 --- a/hw/ide/ahci_internal.h +++ b/hw/ide/ahci-internal.h @@ -25,8 +25,8 @@ #define HW_IDE_AHCI_INTERNAL_H #include "hw/ide/ahci.h" -#include "hw/ide/internal.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" +#include "ide-internal.h" #define AHCI_MEM_BAR_SIZE 0x1000 #define AHCI_MAX_PORTS 32 @@ -61,13 +61,13 @@ enum AHCIHostReg { AHCI_HOST_REG_CTL = 1, /* GHC: global host control */ AHCI_HOST_REG_IRQ_STAT = 2, /* IS: interrupt status */ AHCI_HOST_REG_PORTS_IMPL = 3, /* PI: bitmap of implemented ports */ - AHCI_HOST_REG_VERSION = 4, /* VS: AHCI spec. version compliancy */ + AHCI_HOST_REG_VERSION = 4, /* VS: AHCI spec. version compliance */ AHCI_HOST_REG_CCC_CTL = 5, /* CCC_CTL: CCC Control */ AHCI_HOST_REG_CCC_PORTS = 6, /* CCC_PORTS: CCC Ports */ AHCI_HOST_REG_EM_LOC = 7, /* EM_LOC: Enclosure Mgmt Location */ AHCI_HOST_REG_EM_CTL = 8, /* EM_CTL: Enclosure Mgmt Control */ AHCI_HOST_REG_CAP2 = 9, /* CAP2: host capabilities, extended */ - AHCI_HOST_REG_BOHC = 10, /* BOHC: firmare/os handoff ctrl & status */ + AHCI_HOST_REG_BOHC = 10, /* BOHC: firmware/os handoff ctrl & status */ AHCI_HOST_REG__COUNT = 11 }; @@ -321,14 +321,7 @@ struct AHCIDevice { bool init_d2h_sent; AHCICmdHdr *cur_cmd; NCQTransferState ncq_tfs[AHCI_MAX_CMDS]; -}; - -struct AHCIPCIState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - AHCIState ahci; + MemReentrancyGuard mem_reentrancy_guard; }; extern const VMStateDescription vmstate_ahci; @@ -384,7 +377,7 @@ typedef struct SDBFIS { uint32_t payload; } QEMU_PACKED SDBFIS; -void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports); +void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as); void ahci_init(AHCIState *s, DeviceState *qdev); void ahci_uninit(AHCIState *s); diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 7ce001cacd..bfefad2965 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -22,6 +22,7 @@ */ #include "qemu/osdep.h" +#include "hw/irq.h" #include "hw/pci/msi.h" #include "hw/pci/pci.h" #include "hw/qdev-properties.h" @@ -33,16 +34,19 @@ #include "qemu/module.h" #include "sysemu/block-backend.h" #include "sysemu/dma.h" -#include "hw/ide/internal.h" #include "hw/ide/pci.h" -#include "ahci_internal.h" +#include "hw/ide/ahci-pci.h" +#include "hw/ide/ahci-sysbus.h" +#include "ahci-internal.h" +#include "ide-internal.h" #include "trace.h" static void check_cmd(AHCIState *s, int port); -static int handle_cmd(AHCIState *s, int port, uint8_t slot); +static void handle_cmd(AHCIState *s, int port, uint8_t slot); static void ahci_reset_port(AHCIState *s, int port); -static bool ahci_write_fis_d2h(AHCIDevice *ad); +static bool ahci_write_fis_d2h(AHCIDevice *ad, bool d2h_fis_i); +static void ahci_clear_cmd_issue(AHCIDevice *ad, uint8_t slot); static void ahci_init_d2h(AHCIDevice *ad); static int ahci_dma_prepare_buf(const IDEDMA *dma, int32_t limit); static bool ahci_map_clb_address(AHCIDevice *ad); @@ -327,6 +331,11 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) ahci_check_irq(s); break; case AHCI_PORT_REG_CMD: + if ((pr->cmd & PORT_CMD_START) && !(val & PORT_CMD_START)) { + pr->scr_act = 0; + pr->cmd_issue = 0; + } + /* Block any Read-only fields from being set; * including LIST_ON and FIS_ON. * The spec requires to set ICC bits to zero after the ICC change @@ -590,9 +599,8 @@ static void check_cmd(AHCIState *s, int port) if ((pr->cmd & PORT_CMD_START) && pr->cmd_issue) { for (slot = 0; (slot < 32) && pr->cmd_issue; slot++) { - if ((pr->cmd_issue & (1U << slot)) && - !handle_cmd(s, port, slot)) { - pr->cmd_issue &= ~(1U << slot); + if (pr->cmd_issue & (1U << slot)) { + handle_cmd(s, port, slot); } } } @@ -617,9 +625,13 @@ static void ahci_init_d2h(AHCIDevice *ad) return; } - if (ahci_write_fis_d2h(ad)) { + /* + * For simplicity, do not call ahci_clear_cmd_issue() for this + * ahci_write_fis_d2h(). (The reset value for PxCI is 0.) + */ + if (ahci_write_fis_d2h(ad, true)) { ad->init_d2h_sent = true; - /* We're emulating receiving the first Reg H2D Fis from the device; + /* We're emulating receiving the first Reg D2H FIS from the device; * Update the SIG register, but otherwise proceed as normal. */ pr->sig = ((uint32_t)ide_state->hcyl << 24) | (ide_state->lcyl << 16) | @@ -657,6 +669,7 @@ static void ahci_reset_port(AHCIState *s, int port) pr->scr_act = 0; pr->tfdata = 0x7F; pr->sig = 0xFFFFFFFF; + pr->cmd_issue = 0; d->busy_slot = -1; d->init_d2h_sent = false; @@ -689,7 +702,7 @@ static void ahci_reset_port(AHCIState *s, int port) s->dev[port].port_state = STATE_RUN; if (ide_state->drive_kind == IDE_CD) { - ahci_set_signature(d, SATA_SIGNATURE_CDROM);\ + ahci_set_signature(d, SATA_SIGNATURE_CDROM); ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT; } else { ahci_set_signature(d, SATA_SIGNATURE_DISK); @@ -800,8 +813,14 @@ static void ahci_write_fis_sdb(AHCIState *s, NCQTransferState *ncq_tfs) pr->scr_act &= ~ad->finished; ad->finished = 0; - /* Trigger IRQ if interrupt bit is set (which currently, it always is) */ - if (sdb_fis->flags & 0x40) { + /* + * TFES IRQ is always raised if ERR_STAT is set, regardless of I bit. + * If ERR_STAT is not set, trigger SDBS IRQ if interrupt bit is set + * (which currently, it always is). + */ + if (sdb_fis->status & ERR_STAT) { + ahci_trigger_irq(s, ad, AHCI_PORT_IRQ_BIT_TFES); + } else if (sdb_fis->flags & 0x40) { ahci_trigger_irq(s, ad, AHCI_PORT_IRQ_BIT_SDBS); } } @@ -849,7 +868,7 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len, bool pio_fis_i) } } -static bool ahci_write_fis_d2h(AHCIDevice *ad) +static bool ahci_write_fis_d2h(AHCIDevice *ad, bool d2h_fis_i) { AHCIPortRegs *pr = &ad->port_regs; uint8_t *d2h_fis; @@ -863,7 +882,7 @@ static bool ahci_write_fis_d2h(AHCIDevice *ad) d2h_fis = &ad->res_fis[RES_FIS_RFIS]; d2h_fis[0] = SATA_FIS_TYPE_REGISTER_D2H; - d2h_fis[1] = (1 << 6); /* interrupt bit */ + d2h_fis[1] = d2h_fis_i ? (1 << 6) : 0; /* interrupt bit */ d2h_fis[2] = s->status; d2h_fis[3] = s->error; @@ -885,11 +904,13 @@ static bool ahci_write_fis_d2h(AHCIDevice *ad) pr->tfdata = (ad->port.ifs[0].error << 8) | ad->port.ifs[0].status; + /* TFES IRQ is always raised if ERR_STAT is set, regardless of I bit. */ if (d2h_fis[2] & ERR_STAT) { ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_TFES); + } else if (d2h_fis_i) { + ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_DHRS); } - ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_DHRS); return true; } @@ -997,7 +1018,6 @@ static void ncq_err(NCQTransferState *ncq_tfs) ide_state->error = ABRT_ERR; ide_state->status = READY_STAT | ERR_STAT; - ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag); qemu_sglist_destroy(&ncq_tfs->sglist); ncq_tfs->used = 0; } @@ -1007,7 +1027,7 @@ static void ncq_finish(NCQTransferState *ncq_tfs) /* If we didn't error out, set our finished bit. Errored commands * do not get a bit set for the SDB FIS ACT register, nor do they * clear the outstanding bit in scr_act (PxSACT). */ - if (!(ncq_tfs->drive->port_regs.scr_err & (1 << ncq_tfs->tag))) { + if (ncq_tfs->used) { ncq_tfs->drive->finished |= (1 << ncq_tfs->tag); } @@ -1085,8 +1105,8 @@ static void execute_ncq_command(NCQTransferState *ncq_tfs) ncq_cb, ncq_tfs); break; case WRITE_FPDMA_QUEUED: - trace_execute_ncq_command_read(ad->hba, port, ncq_tfs->tag, - ncq_tfs->sector_count, ncq_tfs->lba); + trace_execute_ncq_command_write(ad->hba, port, ncq_tfs->tag, + ncq_tfs->sector_count, ncq_tfs->lba); dma_acct_start(ide_state->blk, &ncq_tfs->acct, &ncq_tfs->sglist, BLOCK_ACCT_WRITE); ncq_tfs->aiocb = dma_blk_write(ide_state->blk, &ncq_tfs->sglist, @@ -1119,6 +1139,24 @@ static void process_ncq_command(AHCIState *s, int port, const uint8_t *cmd_fis, return; } + /* + * A NCQ command clears the bit in PxCI after the command has been QUEUED + * successfully (ERROR not set, BUSY and DRQ cleared). + * + * For NCQ commands, PxCI will always be cleared here. + * + * (Once the NCQ command is COMPLETED, the device will send a SDB FIS with + * the interrupt bit set, which will clear PxSACT and raise an interrupt.) + */ + ahci_clear_cmd_issue(ad, slot); + + /* + * In reality, for NCQ commands, PxCI is cleared after receiving a D2H FIS + * without the interrupt bit set, but since ahci_write_fis_d2h() can raise + * an IRQ on error, we need to call them in reverse order. + */ + ahci_write_fis_d2h(ad, false); + ncq_tfs->used = 1; ncq_tfs->drive = ad; ncq_tfs->slot = slot; @@ -1191,6 +1229,7 @@ static void handle_reg_h2d_fis(AHCIState *s, int port, { IDEState *ide_state = &s->dev[port].port.ifs[0]; AHCICmdHdr *cmd = get_cmd_header(s, port, slot); + AHCIDevice *ad = &s->dev[port]; uint16_t opts = le16_to_cpu(cmd->opts); if (cmd_fis[1] & 0x0F) { @@ -1210,10 +1249,30 @@ static void handle_reg_h2d_fis(AHCIState *s, int port, case STATE_RUN: if (cmd_fis[15] & ATA_SRST) { s->dev[port].port_state = STATE_RESET; + /* + * When setting SRST in the first H2D FIS in the reset sequence, + * the device does not send a D2H FIS. Host software thus has to + * set the "Clear Busy upon R_OK" bit such that PxCI (and BUSY) + * gets cleared. See AHCI 1.3.1, section 10.4.1 Software Reset. + */ + if (opts & AHCI_CMD_CLR_BUSY) { + ahci_clear_cmd_issue(ad, slot); + } } break; case STATE_RESET: if (!(cmd_fis[15] & ATA_SRST)) { + /* + * When clearing SRST in the second H2D FIS in the reset + * sequence, the device will execute diagnostics. When this is + * done, the device will send a D2H FIS with the good status. + * See SATA 3.5a Gold, section 11.4 Software reset protocol. + * + * This D2H FIS is the first D2H FIS received from the device, + * and is received regardless if the reset was performed by a + * COMRESET or by setting and clearing the SRST bit. Therefore, + * the logic for this is found in ahci_init_d2h() and not here. + */ ahci_reset_port(s, port); } break; @@ -1267,11 +1326,19 @@ static void handle_reg_h2d_fis(AHCIState *s, int port, /* Reset transferred byte counter */ cmd->status = 0; + /* + * A non-NCQ command clears the bit in PxCI after the command has COMPLETED + * successfully (ERROR not set, BUSY and DRQ cleared). + * + * For non-NCQ commands, PxCI will always be cleared by ahci_cmd_done(). + */ + ad->busy_slot = slot; + /* We're ready to process the command in FIS byte 2. */ - ide_exec_cmd(&s->dev[port].port, cmd_fis[2]); + ide_bus_exec_cmd(&s->dev[port].port, cmd_fis[2]); } -static int handle_cmd(AHCIState *s, int port, uint8_t slot) +static void handle_cmd(AHCIState *s, int port, uint8_t slot) { IDEState *ide_state; uint64_t tbl_addr; @@ -1282,12 +1349,12 @@ static int handle_cmd(AHCIState *s, int port, uint8_t slot) if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) { /* Engine currently busy, try again later */ trace_handle_cmd_busy(s, port); - return -1; + return; } if (!s->dev[port].lst) { trace_handle_cmd_nolist(s, port); - return -1; + return; } cmd = get_cmd_header(s, port, slot); /* remember current slot handle for later */ @@ -1297,7 +1364,7 @@ static int handle_cmd(AHCIState *s, int port, uint8_t slot) ide_state = &s->dev[port].port.ifs[0]; if (!ide_state->blk) { trace_handle_cmd_badport(s, port); - return -1; + return; } tbl_addr = le64_to_cpu(cmd->tbl_addr); @@ -1306,7 +1373,7 @@ static int handle_cmd(AHCIState *s, int port, uint8_t slot) DMA_DIRECTION_TO_DEVICE, MEMTXATTRS_UNSPECIFIED); if (!cmd_fis) { trace_handle_cmd_badfis(s, port); - return -1; + return; } else if (cmd_len != 0x80) { ahci_trigger_irq(s, &s->dev[port], AHCI_PORT_IRQ_BIT_HBFS); trace_handle_cmd_badmap(s, port, cmd_len); @@ -1330,15 +1397,6 @@ static int handle_cmd(AHCIState *s, int port, uint8_t slot) out: dma_memory_unmap(s->as, cmd_fis, cmd_len, DMA_DIRECTION_TO_DEVICE, cmd_len); - - if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) { - /* async command, complete later */ - s->dev[port].busy_slot = slot; - return -1; - } - - /* done handling the command */ - return 0; } /* Transfer PIO data between RAM and device */ @@ -1492,23 +1550,41 @@ static int ahci_dma_rw_buf(const IDEDMA *dma, bool is_write) return 1; } +static void ahci_clear_cmd_issue(AHCIDevice *ad, uint8_t slot) +{ + IDEState *ide_state = &ad->port.ifs[0]; + + if (!(ide_state->status & ERR_STAT) && + !(ide_state->status & (BUSY_STAT | DRQ_STAT))) { + ad->port_regs.cmd_issue &= ~(1 << slot); + } +} + +/* Non-NCQ command is done - This function is never called for NCQ commands. */ static void ahci_cmd_done(const IDEDMA *dma) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); + IDEState *ide_state = &ad->port.ifs[0]; trace_ahci_cmd_done(ad->hba, ad->port_no); /* no longer busy */ if (ad->busy_slot != -1) { - ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot); + ahci_clear_cmd_issue(ad, ad->busy_slot); ad->busy_slot = -1; } - /* update d2h status */ - ahci_write_fis_d2h(ad); + /* + * In reality, for non-NCQ commands, PxCI is cleared after receiving a D2H + * FIS with the interrupt bit set, but since ahci_write_fis_d2h() will raise + * an IRQ, we need to call them in reverse order. + */ + ahci_write_fis_d2h(ad, true); - if (ad->port_regs.cmd_issue && !ad->check_bh) { - ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad); + if (!(ide_state->status & ERR_STAT) && + ad->port_regs.cmd_issue && !ad->check_bh) { + ad->check_bh = qemu_bh_new_guarded(ahci_check_cmd_bh, ad, + &ad->mem_reentrancy_guard); qemu_bh_schedule(ad->check_bh); } } @@ -1539,27 +1615,27 @@ void ahci_init(AHCIState *s, DeviceState *qdev) "ahci-idp", 32); } -void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) +void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as) { qemu_irq *irqs; int i; s->as = as; - s->ports = ports; - s->dev = g_new0(AHCIDevice, ports); + assert(s->ports > 0); + s->dev = g_new0(AHCIDevice, s->ports); ahci_reg_init(s); irqs = qemu_allocate_irqs(ahci_irq_set, s, s->ports); for (i = 0; i < s->ports; i++) { AHCIDevice *ad = &s->dev[i]; ide_bus_init(&ad->port, sizeof(ad->port), qdev, i, 1); - ide_init2(&ad->port, irqs[i]); + ide_bus_init_output_irq(&ad->port, irqs[i]); ad->hba = s; ad->port_no = i; ad->port.dma = &ad->dma; ad->port.dma->ops = &ahci_dma_ops; - ide_register_restart_cb(&ad->port); + ide_bus_register_restart_cb(&ad->port); } g_free(irqs); } @@ -1572,9 +1648,7 @@ void ahci_uninit(AHCIState *s) AHCIDevice *ad = &s->dev[i]; for (j = 0; j < 2; j++) { - IDEState *s = &ad->port.ifs[j]; - - ide_exit(s); + ide_exit(&ad->port.ifs[j]); } object_unparent(OBJECT(&ad->port)); } @@ -1613,7 +1687,7 @@ void ahci_reset(AHCIState *s) static const VMStateDescription vmstate_ncq_tfs = { .name = "ncq state", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(sector_count, NCQTransferState), VMSTATE_UINT64(lba, NCQTransferState), VMSTATE_UINT8(tag, NCQTransferState), @@ -1628,7 +1702,7 @@ static const VMStateDescription vmstate_ncq_tfs = { static const VMStateDescription vmstate_ahci_device = { .name = "ahci port", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_IDE_BUS(port, AHCIDevice), VMSTATE_IDE_DRIVE(port.ifs[0], AHCIDevice), VMSTATE_UINT32(port_state, AHCIDevice), @@ -1745,8 +1819,8 @@ const VMStateDescription vmstate_ahci = { .name = "ahci", .version_id = 1, .post_load = ahci_state_post_load, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_VARRAY_POINTER_INT32(dev, AHCIState, ports, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(dev, AHCIState, ports, vmstate_ahci_device, AHCIDevice), VMSTATE_UINT32(control_regs.cap, AHCIState), VMSTATE_UINT32(control_regs.ghc, AHCIState), @@ -1754,14 +1828,14 @@ const VMStateDescription vmstate_ahci = { VMSTATE_UINT32(control_regs.impl, AHCIState), VMSTATE_UINT32(control_regs.version, AHCIState), VMSTATE_UINT32(idp_index, AHCIState), - VMSTATE_INT32_EQUAL(ports, AHCIState, NULL), + VMSTATE_UINT32_EQUAL(ports, AHCIState, NULL), VMSTATE_END_OF_LIST() }, }; static const VMStateDescription vmstate_sysbus_ahci = { .name = "sysbus-ahci", - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_AHCI(ahci, SysbusAHCIState), VMSTATE_END_OF_LIST() }, @@ -1789,11 +1863,11 @@ static void sysbus_ahci_realize(DeviceState *dev, Error **errp) { SysbusAHCIState *s = SYSBUS_AHCI(dev); - ahci_realize(&s->ahci, dev, &address_space_memory, s->num_ports); + ahci_realize(&s->ahci, dev, &address_space_memory); } static Property sysbus_ahci_properties[] = { - DEFINE_PROP_UINT32("num-ports", SysbusAHCIState, num_ports, 1), + DEFINE_PROP_UINT32("num-ports", SysbusAHCIState, ahci.ports, 1), DEFINE_PROP_END_OF_LIST(), }; @@ -1823,25 +1897,14 @@ static void sysbus_ahci_register_types(void) type_init(sysbus_ahci_register_types) -int32_t ahci_get_num_ports(PCIDevice *dev) +void ahci_ide_create_devs(AHCIState *ahci, DriveInfo **hd) { - AHCIPCIState *d = ICH9_AHCI(dev); - AHCIState *ahci = &d->ahci; - - return ahci->ports; -} - -void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd) -{ - AHCIPCIState *d = ICH9_AHCI(dev); - AHCIState *ahci = &d->ahci; int i; for (i = 0; i < ahci->ports; i++) { if (hd[i] == NULL) { continue; } - ide_create_drive(&ahci->dev[i].port, 0, hd[i]); + ide_bus_create_drive(&ahci->dev[i].port, 0, hd[i]); } - } diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index b626199e3d..73ec373184 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -24,9 +24,10 @@ */ #include "qemu/osdep.h" -#include "hw/ide/internal.h" #include "hw/scsi/scsi.h" #include "sysemu/block-backend.h" +#include "scsi/constants.h" +#include "ide-internal.h" #include "trace.h" #define ATAPI_SECTOR_BITS (2 + BDRV_SECTOR_BITS) @@ -98,11 +99,11 @@ cd_read_sector_sync(IDEState *s) switch (s->cd_sector_size) { case 2048: ret = blk_pread(s->blk, (int64_t)s->lba << ATAPI_SECTOR_BITS, - s->io_buffer, ATAPI_SECTOR_SIZE); + ATAPI_SECTOR_SIZE, s->io_buffer, 0); break; case 2352: ret = blk_pread(s->blk, (int64_t)s->lba << ATAPI_SECTOR_BITS, - s->io_buffer + 16, ATAPI_SECTOR_SIZE); + ATAPI_SECTOR_SIZE, s->io_buffer + 16, 0); if (ret >= 0) { cd_data_to_raw(s->io_buffer, s->lba); } @@ -178,7 +179,7 @@ void ide_atapi_cmd_ok(IDEState *s) s->status = READY_STAT | SEEK_STAT; s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; ide_transfer_stop(s); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc) @@ -190,7 +191,7 @@ void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc) s->sense_key = sense_key; s->asc = asc; ide_transfer_stop(s); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } void ide_atapi_io_error(IDEState *s, int ret) @@ -253,7 +254,7 @@ void ide_atapi_cmd_reply_end(IDEState *s) } else { /* a new transfer is needed */ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); byte_count_limit = atapi_byte_count_limit(s); trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit); size = s->packet_transfer_size; @@ -293,7 +294,7 @@ void ide_atapi_cmd_reply_end(IDEState *s) /* end of transfer */ trace_ide_atapi_cmd_reply_end_eot(s, s->status); ide_atapi_cmd_ok(s); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } /* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */ @@ -318,7 +319,7 @@ static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size) } } -/* start a CD-CDROM read command */ +/* start a CD-ROM read command */ static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors, int sector_size) { @@ -339,7 +340,7 @@ static void ide_atapi_cmd_check_status(IDEState *s) s->error = MC_ERR | (UNIT_ATTENTION << 4); s->status = ERR_STAT; s->nsector = 0; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } /* ATAPI DMA support */ @@ -383,7 +384,7 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) if (s->packet_transfer_size <= 0) { s->status = READY_STAT | SEEK_STAT; s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); goto eot; } @@ -417,7 +418,7 @@ eot: ide_set_inactive(s, false); } -/* start a CD-CDROM read command with DMA */ +/* start a CD-ROM read command with DMA */ /* XXX: test if DMA is available */ static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors, int sector_size) diff --git a/hw/ide/cf.c b/hw/ide/cf.c new file mode 100644 index 0000000000..2a425cb0f2 --- /dev/null +++ b/hw/ide/cf.c @@ -0,0 +1,58 @@ +/* + * ide CompactFlash support + * + * This code is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "hw/ide/ide-dev.h" +#include "qapi/qapi-types-block.h" + +static void ide_cf_realize(IDEDevice *dev, Error **errp) +{ + ide_dev_initfn(dev, IDE_CFATA, errp); +} + +static Property ide_cf_properties[] = { + DEFINE_IDE_DEV_PROPERTIES(), + DEFINE_BLOCK_CHS_PROPERTIES(IDEDrive, dev.conf), + DEFINE_PROP_BIOS_CHS_TRANS("bios-chs-trans", + IDEDrive, dev.chs_trans, BIOS_ATA_TRANSLATION_AUTO), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ide_cf_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IDEDeviceClass *k = IDE_DEVICE_CLASS(klass); + + k->realize = ide_cf_realize; + dc->fw_name = "drive"; + dc->desc = "virtual CompactFlash card"; + device_class_set_props(dc, ide_cf_properties); +} + +static const TypeInfo ide_cf_info = { + .name = "ide-cf", + .parent = TYPE_IDE_DEVICE, + .instance_size = sizeof(IDEDrive), + .class_init = ide_cf_class_init, +}; + +static void ide_cf_register_type(void) +{ + type_register_static(&ide_cf_info); +} + +type_init(ide_cf_register_type) diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 94c576262c..8cebd1b63d 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -33,23 +33,24 @@ #include "sysemu/reset.h" #include "hw/ide/pci.h" +#include "ide-internal.h" #include "trace.h" /* CMD646 specific */ -#define CFR 0x50 -#define CFR_INTR_CH0 0x04 -#define CNTRL 0x51 -#define CNTRL_EN_CH0 0x04 -#define CNTRL_EN_CH1 0x08 -#define ARTTIM23 0x57 -#define ARTTIM23_INTR_CH1 0x10 -#define MRDMODE 0x71 -#define MRDMODE_INTR_CH0 0x04 -#define MRDMODE_INTR_CH1 0x08 -#define MRDMODE_BLK_CH0 0x10 -#define MRDMODE_BLK_CH1 0x20 -#define UDIDETCR0 0x73 -#define UDIDETCR1 0x7B +#define CFR 0x50 +#define CFR_INTR_CH0 0x04 +#define CNTRL 0x51 +#define CNTRL_EN_CH0 0x04 +#define CNTRL_EN_CH1 0x08 +#define ARTTIM23 0x57 +#define ARTTIM23_INTR_CH1 0x10 +#define MRDMODE 0x71 +#define MRDMODE_INTR_CH0 0x04 +#define MRDMODE_INTR_CH1 0x08 +#define MRDMODE_BLK_CH0 0x10 +#define MRDMODE_BLK_CH1 0x20 +#define UDIDETCR0 0x73 +#define UDIDETCR1 0x7B static void cmd646_update_irq(PCIDevice *pd); @@ -144,7 +145,7 @@ static void bmdma_write(void *opaque, hwaddr addr, cmd646_update_irq(pci_dev); break; case 2: - bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06); + bmdma_status_writeb(bm, val); break; case 3: if (bm == &bm->pci_dev->bmdma[0]) { @@ -257,7 +258,7 @@ static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp) pci_conf[CNTRL] = CNTRL_EN_CH0; // enable IDE0 if (d->secondary) { - /* XXX: if not enabled, really disable the seconday IDE controller */ + /* XXX: if not enabled, really disable the secondary IDE controller */ pci_conf[CNTRL] |= CNTRL_EN_CH1; /* enable IDE1 */ } @@ -294,11 +295,10 @@ static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp) qdev_init_gpio_in(ds, cmd646_set_irq, 2); for (i = 0; i < 2; i++) { ide_bus_init(&d->bus[i], sizeof(d->bus[i]), ds, i, 2); - ide_init2(&d->bus[i], qdev_get_gpio_in(ds, i)); + ide_bus_init_output_irq(&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]); + ide_bus_register_restart_cb(&d->bus[i]); } } diff --git a/hw/ide/core.c b/hw/ide/core.c index c2caa54285..e8cb2dac92 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "hw/irq.h" #include "hw/isa/isa.h" #include "migration/vmstate.h" #include "qemu/error-report.h" @@ -40,7 +41,7 @@ #include "qemu/cutils.h" #include "sysemu/replay.h" #include "sysemu/runstate.h" -#include "hw/ide/internal.h" +#include "ide-internal.h" #include "trace.h" /* These values were based on a Seagate ST3500418AS but have been modified @@ -80,6 +81,18 @@ static const char *IDE_DMA_CMD_str(enum ide_dma_cmd enval) static void ide_dummy_transfer_stop(IDEState *s); +const MemoryRegionPortio ide_portio_list[] = { + { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write }, + { 0, 1, 2, .read = ide_data_readw, .write = ide_data_writew }, + { 0, 1, 4, .read = ide_data_readl, .write = ide_data_writel }, + PORTIO_END_OF_LIST(), +}; + +const MemoryRegionPortio ide_portio2_list[] = { + { 0, 1, 1, .read = ide_status_read, .write = ide_ctrl_write }, + PORTIO_END_OF_LIST(), +}; + static void padstr(char *str, const char *src, int len) { int i, v; @@ -317,52 +330,52 @@ static void ide_cfata_identify(IDEState *s) cur_sec = s->cylinders * s->heads * s->sectors; - put_le16(p + 0, 0x848a); /* CF Storage Card signature */ - put_le16(p + 1, s->cylinders); /* Default cylinders */ - put_le16(p + 3, s->heads); /* Default heads */ - put_le16(p + 6, s->sectors); /* Default sectors per track */ + put_le16(p + 0, 0x848a); /* CF Storage Card signature */ + put_le16(p + 1, s->cylinders); /* Default cylinders */ + put_le16(p + 3, s->heads); /* Default heads */ + put_le16(p + 6, s->sectors); /* Default sectors per track */ /* *(p + 7) := nb_sectors >> 16 -- see ide_cfata_identify_size */ /* *(p + 8) := nb_sectors -- see ide_cfata_identify_size */ padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */ - put_le16(p + 22, 0x0004); /* ECC bytes */ - padstr((char *) (p + 23), s->version, 8); /* Firmware Revision */ + put_le16(p + 22, 0x0004); /* ECC bytes */ + padstr((char *) (p + 23), s->version, 8); /* Firmware Revision */ padstr((char *) (p + 27), s->drive_model_str, 40);/* Model number */ #if MAX_MULT_SECTORS > 1 put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS); #else put_le16(p + 47, 0x0000); #endif - put_le16(p + 49, 0x0f00); /* Capabilities */ - put_le16(p + 51, 0x0002); /* PIO cycle timing mode */ - put_le16(p + 52, 0x0001); /* DMA cycle timing mode */ - put_le16(p + 53, 0x0003); /* Translation params valid */ - put_le16(p + 54, s->cylinders); /* Current cylinders */ - put_le16(p + 55, s->heads); /* Current heads */ - put_le16(p + 56, s->sectors); /* Current sectors */ - put_le16(p + 57, cur_sec); /* Current capacity */ - put_le16(p + 58, cur_sec >> 16); /* Current capacity */ - if (s->mult_sectors) /* Multiple sector setting */ + put_le16(p + 49, 0x0f00); /* Capabilities */ + put_le16(p + 51, 0x0002); /* PIO cycle timing mode */ + put_le16(p + 52, 0x0001); /* DMA cycle timing mode */ + put_le16(p + 53, 0x0003); /* Translation params valid */ + put_le16(p + 54, s->cylinders); /* Current cylinders */ + put_le16(p + 55, s->heads); /* Current heads */ + put_le16(p + 56, s->sectors); /* Current sectors */ + put_le16(p + 57, cur_sec); /* Current capacity */ + put_le16(p + 58, cur_sec >> 16); /* Current capacity */ + if (s->mult_sectors) /* Multiple sector setting */ put_le16(p + 59, 0x100 | s->mult_sectors); /* *(p + 60) := nb_sectors -- see ide_cfata_identify_size */ /* *(p + 61) := nb_sectors >> 16 -- see ide_cfata_identify_size */ - put_le16(p + 63, 0x0203); /* Multiword DMA capability */ - put_le16(p + 64, 0x0001); /* Flow Control PIO support */ - put_le16(p + 65, 0x0096); /* Min. Multiword DMA cycle */ - put_le16(p + 66, 0x0096); /* Rec. Multiword DMA cycle */ - put_le16(p + 68, 0x00b4); /* Min. PIO cycle time */ - put_le16(p + 82, 0x400c); /* Command Set supported */ - put_le16(p + 83, 0x7068); /* Command Set supported */ - put_le16(p + 84, 0x4000); /* Features supported */ - put_le16(p + 85, 0x000c); /* Command Set enabled */ - put_le16(p + 86, 0x7044); /* Command Set enabled */ - put_le16(p + 87, 0x4000); /* Features enabled */ - put_le16(p + 91, 0x4060); /* Current APM level */ - put_le16(p + 129, 0x0002); /* Current features option */ - put_le16(p + 130, 0x0005); /* Reassigned sectors */ - put_le16(p + 131, 0x0001); /* Initial power mode */ - put_le16(p + 132, 0x0000); /* User signature */ - put_le16(p + 160, 0x8100); /* Power requirement */ - put_le16(p + 161, 0x8001); /* CF command set */ + put_le16(p + 63, 0x0203); /* Multiword DMA capability */ + put_le16(p + 64, 0x0001); /* Flow Control PIO support */ + put_le16(p + 65, 0x0096); /* Min. Multiword DMA cycle */ + put_le16(p + 66, 0x0096); /* Rec. Multiword DMA cycle */ + put_le16(p + 68, 0x00b4); /* Min. PIO cycle time */ + put_le16(p + 82, 0x400c); /* Command Set supported */ + put_le16(p + 83, 0x7068); /* Command Set supported */ + put_le16(p + 84, 0x4000); /* Features supported */ + put_le16(p + 85, 0x000c); /* Command Set enabled */ + put_le16(p + 86, 0x7044); /* Command Set enabled */ + put_le16(p + 87, 0x4000); /* Features enabled */ + put_le16(p + 91, 0x4060); /* Current APM level */ + put_le16(p + 129, 0x0002); /* Current features option */ + put_le16(p + 130, 0x0005); /* Reassigned sectors */ + put_le16(p + 131, 0x0001); /* Initial power mode */ + put_le16(p + 132, 0x0000); /* User signature */ + put_le16(p + 160, 0x8100); /* Power requirement */ + put_le16(p + 161, 0x8001); /* CF command set */ ide_cfata_identify_size(s); s->identify_set = 1; @@ -512,6 +525,7 @@ BlockAIOCB *ide_issue_trim( BlockCompletionFunc *cb, void *cb_opaque, void *opaque) { IDEState *s = opaque; + IDEDevice *dev = s->unit ? s->bus->slave : s->bus->master; TrimAIOCB *iocb; /* Paired with a decrement in ide_trim_bh_cb() */ @@ -519,7 +533,8 @@ BlockAIOCB *ide_issue_trim( iocb = blk_aio_get(&trim_aiocb_info, s->blk, cb, cb_opaque); iocb->s = s; - iocb->bh = qemu_bh_new(ide_trim_bh_cb, iocb); + iocb->bh = qemu_bh_new_guarded(ide_trim_bh_cb, iocb, + &DEVICE(dev)->mem_reentrancy_guard); iocb->ret = 0; iocb->qiov = qiov; iocb->i = -1; @@ -530,9 +545,9 @@ BlockAIOCB *ide_issue_trim( void ide_abort_command(IDEState *s) { - ide_transfer_stop(s); s->status = READY_STAT | ERR_STAT; s->error = ABRT_ERR; + ide_transfer_stop(s); } static void ide_set_retry(IDEState *s) @@ -653,7 +668,7 @@ void ide_set_sector(IDEState *s, int64_t sector_num) static void ide_rw_error(IDEState *s) { ide_abort_command(s); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } static void ide_buffered_readv_cb(void *opaque, int ret) @@ -772,7 +787,7 @@ static void ide_sector_read_cb(void *opaque, int ret) s->nsector -= n; /* Allow the guest to read the io_buffer */ ide_transfer_start(s, s->io_buffer, n * BDRV_SECTOR_SIZE, ide_sector_read); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } static void ide_sector_read(IDEState *s) @@ -836,7 +851,7 @@ void ide_dma_error(IDEState *s) dma_buf_commit(s, 0); ide_abort_command(s); ide_set_inactive(s, false); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } int ide_handle_rw_error(IDEState *s, int error, int op) @@ -906,7 +921,7 @@ static void ide_dma_cb(void *opaque, int ret) /* end of transfer ? */ if (s->nsector == 0) { s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); goto eot; } @@ -1006,7 +1021,7 @@ static void ide_sector_write(IDEState *s); static void ide_sector_write_timer_cb(void *opaque) { IDEState *s = opaque; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } static void ide_sector_write_cb(void *opaque, int ret) @@ -1044,7 +1059,7 @@ static void ide_sector_write_cb(void *opaque, int ret) ide_sector_write); } - if (win2k_install_hack && ((++s->irq_count % 16) == 0)) { + if (s->win2k_install_hack && ((++s->irq_count % 16) == 0)) { /* It seems there is a bug in the Windows 2000 installer HDD IDE driver which fills the disk with empty logs when the IDE write IRQ comes too early. This hack tries to correct @@ -1054,7 +1069,7 @@ static void ide_sector_write_cb(void *opaque, int ret) timer_mod(s->sector_write_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (NANOSECONDS_PER_SECOND / 1000)); } else { - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } } @@ -1105,7 +1120,7 @@ static void ide_flush_cb(void *opaque, int ret) } s->status = READY_STAT | SEEK_STAT; ide_cmd_done(s); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } static void ide_flush_cache(IDEState *s) @@ -1130,13 +1145,13 @@ static void ide_cfata_metadata_inquiry(IDEState *s) memset(p, 0, 0x200); spd = ((s->mdata_size - 1) >> 9) + 1; - put_le16(p + 0, 0x0001); /* Data format revision */ - put_le16(p + 1, 0x0000); /* Media property: silicon */ - put_le16(p + 2, s->media_changed); /* Media status */ - put_le16(p + 3, s->mdata_size & 0xffff); /* Capacity in bytes (low) */ - put_le16(p + 4, s->mdata_size >> 16); /* Capacity in bytes (high) */ - put_le16(p + 5, spd & 0xffff); /* Sectors per device (low) */ - put_le16(p + 6, spd >> 16); /* Sectors per device (high) */ + put_le16(p + 0, 0x0001); /* Data format revision */ + put_le16(p + 1, 0x0000); /* Media property: silicon */ + put_le16(p + 2, s->media_changed); /* Media status */ + put_le16(p + 3, s->mdata_size & 0xffff); /* Capacity in bytes (low) */ + put_le16(p + 4, s->mdata_size >> 16); /* Capacity in bytes (high) */ + put_le16(p + 5, spd & 0xffff); /* Sectors per device (low) */ + put_le16(p + 6, spd >> 16); /* Sectors per device (high) */ } static void ide_cfata_metadata_read(IDEState *s) @@ -1152,7 +1167,7 @@ static void ide_cfata_metadata_read(IDEState *s) p = (uint16_t *) s->io_buffer; memset(p, 0, 0x200); - put_le16(p + 0, s->media_changed); /* Media status */ + put_le16(p + 0, s->media_changed); /* Media status */ memcpy(p + 1, s->mdata_storage + (((s->hcyl << 16) | s->lcyl) << 9), MIN(MIN(s->mdata_size - (((s->hcyl << 16) | s->lcyl) << 9), s->nsector << 9), 0x200 - 2)); @@ -1194,7 +1209,7 @@ static void ide_cd_change_cb(void *opaque, bool load, Error **errp) s->cdrom_changed = 1; s->events.new_media = true; s->events.eject_request = false; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } static void ide_cd_eject_request_cb(void *opaque, bool force) @@ -1205,7 +1220,7 @@ static void ide_cd_eject_request_cb(void *opaque, bool force) if (force) { s->tray_locked = false; } - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } static void ide_cmd_lba48_transform(IDEState *s, int lba48) @@ -1264,7 +1279,7 @@ const char *ATA_IOPORT_WR_lookup[ATA_IOPORT_WR_NUM_REGISTERS] = { void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) { IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); + IDEState *s = ide_bus_active_if(bus); int reg_num = addr & 7; trace_ide_ioport_write(addr, ATA_IOPORT_WR_lookup[reg_num], val, bus, s); @@ -1326,7 +1341,7 @@ void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) case ATA_IOPORT_WR_COMMAND: ide_clear_hob(bus); qemu_irq_lower(bus->irq); - ide_exec_cmd(bus, val); + ide_bus_exec_cmd(bus, val); break; } } @@ -1340,6 +1355,11 @@ static void ide_reset(IDEState *s) s->pio_aiocb = NULL; } + if (s->reset_reverts) { + s->reset_reverts = false; + s->heads = s->drive_heads; + s->sectors = s->drive_sectors; + } if (s->drive_kind == IDE_CFATA) s->mult_sectors = 0; else @@ -1434,7 +1454,7 @@ static bool cmd_identify(IDEState *s, uint8_t cmd) } s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; } else { if (s->drive_kind == IDE_CD) { @@ -1618,6 +1638,20 @@ static bool cmd_check_power_mode(IDEState *s, uint8_t cmd) return true; } +/* INITIALIZE DEVICE PARAMETERS */ +static bool cmd_specify(IDEState *s, uint8_t cmd) +{ + if (s->blk && s->drive_kind != IDE_CD) { + s->heads = (s->select & (ATA_DEV_HS)) + 1; + s->sectors = s->nsector; + ide_bus_set_irq(s->bus); + } else { + ide_abort_command(s); + } + + return true; +} + static bool cmd_set_features(IDEState *s, uint8_t cmd) { uint16_t *identify_data; @@ -1629,6 +1663,13 @@ static bool cmd_set_features(IDEState *s, uint8_t cmd) /* XXX: valid for CDROM ? */ switch (s->feature) { + case 0x01: /* 8-bit I/O enable (CompactFlash) */ + case 0x81: /* 8-bit I/O disable (CompactFlash) */ + if (s->drive_kind != IDE_CFATA) { + goto abort_cmd; + } + s->io8 = !(s->feature & 0x80); + return true; case 0x02: /* write cache enable */ blk_set_enable_write_cache(s->blk, true); identify_data = (uint16_t *)s->identify_data; @@ -1641,7 +1682,11 @@ static bool cmd_set_features(IDEState *s, uint8_t cmd) ide_flush_cache(s); return false; case 0xcc: /* reverting to power-on defaults enable */ + s->reset_reverts = true; + return true; case 0x66: /* reverting to power-on defaults disable */ + s->reset_reverts = false; + return true; case 0xaa: /* read look-ahead enable */ case 0x55: /* read look-ahead disable */ case 0x05: /* set advanced power management mode */ @@ -1665,7 +1710,7 @@ static bool cmd_set_features(IDEState *s, uint8_t cmd) put_le16(identify_data + 63, 0x07); put_le16(identify_data + 88, 0x3f); break; - case 0x02: /* sigle word dma mode*/ + case 0x02: /* single word dma mode */ put_le16(identify_data + 62, 0x07 | (1 << (val + 8))); put_le16(identify_data + 63, 0x07); put_le16(identify_data + 88, 0x3f); @@ -1700,12 +1745,18 @@ static bool cmd_identify_packet(IDEState *s, uint8_t cmd) ide_atapi_identify(s); s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; } +/* EXECUTE DEVICE DIAGNOSTIC */ static bool cmd_exec_dev_diagnostic(IDEState *s, uint8_t cmd) { + /* + * Clear the device register per the ATA (v6) specification, + * because ide_set_signature does not clear LBA or drive bits. + */ + s->select = (ATA_DEV_ALWAYS_ON); ide_set_signature(s); if (s->drive_kind == IDE_CD) { @@ -1719,7 +1770,7 @@ static bool cmd_exec_dev_diagnostic(IDEState *s, uint8_t cmd) * They are part of the regular output (this is why ERR_STAT isn't set) * Device 0 passed, Device 1 passed or not present. */ s->error = 0x01; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } return false; @@ -1751,7 +1802,7 @@ static bool cmd_cfa_req_ext_error_code(IDEState *s, uint8_t cmd) { s->error = 0x09; /* miscellaneous error */ s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; } @@ -1790,7 +1841,7 @@ static bool cmd_cfa_translate_sector(IDEState *s, uint8_t cmd) s->io_buffer[0x1a] = 0x01; /* Hot count */ ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; } @@ -1814,7 +1865,7 @@ static bool cmd_cfa_access_metadata_storage(IDEState *s, uint8_t cmd) ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); s->status = 0x00; /* NOTE: READY is _not_ set */ - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; } @@ -1897,7 +1948,7 @@ static bool cmd_smart(IDEState *s, uint8_t cmd) s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; case SMART_READ_DATA: @@ -1938,7 +1989,7 @@ static bool cmd_smart(IDEState *s, uint8_t cmd) s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; case SMART_READ_LOG: @@ -1977,7 +2028,7 @@ static bool cmd_smart(IDEState *s, uint8_t cmd) } s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return false; case SMART_EXECUTE_OFFLINE: @@ -2045,7 +2096,7 @@ static const struct { [WIN_SEEK] = { cmd_seek, HD_CFA_OK | SET_DSC }, [CFA_TRANSLATE_SECTOR] = { cmd_cfa_translate_sector, CFA_OK }, [WIN_DIAGNOSE] = { cmd_exec_dev_diagnostic, ALL_OK }, - [WIN_SPECIFY] = { cmd_nop, HD_CFA_OK | SET_DSC }, + [WIN_SPECIFY] = { cmd_specify, HD_CFA_OK | SET_DSC }, [WIN_STANDBYNOW2] = { cmd_nop, HD_CFA_OK }, [WIN_IDLEIMMEDIATE2] = { cmd_nop, HD_CFA_OK }, [WIN_STANDBY2] = { cmd_nop, HD_CFA_OK }, @@ -2086,13 +2137,13 @@ static bool ide_cmd_permitted(IDEState *s, uint32_t cmd) && (ide_cmd_table[cmd].flags & (1u << s->drive_kind)); } -void ide_exec_cmd(IDEBus *bus, uint32_t val) +void ide_bus_exec_cmd(IDEBus *bus, uint32_t val) { IDEState *s; bool complete; - s = idebus_active_if(bus); - trace_ide_exec_cmd(bus, s, val); + s = ide_bus_active_if(bus); + trace_ide_bus_exec_cmd(bus, s, val); /* ignore commands to non existent slave */ if (s != bus->ifs && !s->blk) { @@ -2109,7 +2160,7 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) if (!ide_cmd_permitted(s, val)) { ide_abort_command(s); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); return; } @@ -2127,7 +2178,7 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) } ide_cmd_done(s); - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } } @@ -2158,7 +2209,7 @@ const char *ATA_IOPORT_RR_lookup[ATA_IOPORT_RR_NUM_REGISTERS] = { uint32_t ide_ioport_read(void *opaque, uint32_t addr) { IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); + IDEState *s = ide_bus_active_if(bus); uint32_t reg_num; int ret, hob; @@ -2244,7 +2295,7 @@ uint32_t ide_ioport_read(void *opaque, uint32_t addr) uint32_t ide_status_read(void *opaque, uint32_t addr) { IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); + IDEState *s = ide_bus_active_if(bus); int ret; if ((!bus->ifs[0].blk && !bus->ifs[1].blk) || @@ -2333,7 +2384,7 @@ static bool ide_is_pio_out(IDEState *s) void ide_data_writew(void *opaque, uint32_t addr, uint32_t val) { IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); + IDEState *s = ide_bus_active_if(bus); uint8_t *p; trace_ide_data_writew(addr, val, bus, s); @@ -2345,12 +2396,20 @@ void ide_data_writew(void *opaque, uint32_t addr, uint32_t val) } p = s->data_ptr; - if (p + 2 > s->data_end) { - return; - } + if (s->io8) { + if (p + 1 > s->data_end) { + return; + } - *(uint16_t *)p = le16_to_cpu(val); - p += 2; + *p++ = val; + } else { + if (p + 2 > s->data_end) { + return; + } + + *(uint16_t *)p = le16_to_cpu(val); + p += 2; + } s->data_ptr = p; if (p >= s->data_end) { s->status &= ~DRQ_STAT; @@ -2361,7 +2420,7 @@ void ide_data_writew(void *opaque, uint32_t addr, uint32_t val) uint32_t ide_data_readw(void *opaque, uint32_t addr) { IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); + IDEState *s = ide_bus_active_if(bus); uint8_t *p; int ret; @@ -2372,12 +2431,20 @@ uint32_t ide_data_readw(void *opaque, uint32_t addr) } p = s->data_ptr; - if (p + 2 > s->data_end) { - return 0; - } + if (s->io8) { + if (p + 1 > s->data_end) { + return 0; + } - ret = cpu_to_le16(*(uint16_t *)p); - p += 2; + ret = *p++; + } else { + if (p + 2 > s->data_end) { + return 0; + } + + ret = cpu_to_le16(*(uint16_t *)p); + p += 2; + } s->data_ptr = p; if (p >= s->data_end) { s->status &= ~DRQ_STAT; @@ -2391,7 +2458,7 @@ uint32_t ide_data_readw(void *opaque, uint32_t addr) void ide_data_writel(void *opaque, uint32_t addr, uint32_t val) { IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); + IDEState *s = ide_bus_active_if(bus); uint8_t *p; trace_ide_data_writel(addr, val, bus, s); @@ -2419,7 +2486,7 @@ void ide_data_writel(void *opaque, uint32_t addr, uint32_t val) uint32_t ide_data_readl(void *opaque, uint32_t addr) { IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); + IDEState *s = ide_bus_active_if(bus); uint8_t *p; int ret; @@ -2460,19 +2527,19 @@ static void ide_dummy_transfer_stop(IDEState *s) void ide_bus_reset(IDEBus *bus) { - bus->unit = 0; - bus->cmd = 0; - ide_reset(&bus->ifs[0]); - ide_reset(&bus->ifs[1]); - ide_clear_hob(bus); - - /* pending async DMA */ + /* pending async DMA - needs the IDEState before it is reset */ if (bus->dma->aiocb) { trace_ide_bus_reset_aio(); blk_aio_cancel(bus->dma->aiocb); bus->dma->aiocb = NULL; } + bus->unit = 0; + bus->cmd = 0; + ide_reset(&bus->ifs[0]); + ide_reset(&bus->ifs[1]); + ide_clear_hob(bus); + /* reset dma provider too */ if (bus->dma->ops->reset) { bus->dma->ops->reset(bus->dma); @@ -2522,24 +2589,21 @@ static const BlockDevOps ide_hd_block_ops = { .resize_cb = ide_resize_cb, }; -int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind, - const char *version, const char *serial, const char *model, - uint64_t wwn, - uint32_t cylinders, uint32_t heads, uint32_t secs, - int chs_trans, Error **errp) +int ide_init_drive(IDEState *s, IDEDevice *dev, IDEDriveKind kind, Error **errp) { uint64_t nb_sectors; - s->blk = blk; + s->blk = dev->conf.blk; s->drive_kind = kind; - blk_get_geometry(blk, &nb_sectors); - s->cylinders = cylinders; - s->heads = heads; - s->sectors = secs; - s->chs_trans = chs_trans; + blk_get_geometry(s->blk, &nb_sectors); + s->win2k_install_hack = dev->win2k_install_hack; + s->cylinders = dev->conf.cyls; + s->heads = s->drive_heads = dev->conf.heads; + s->sectors = s->drive_sectors = dev->conf.secs; + s->chs_trans = dev->chs_trans; s->nb_sectors = nb_sectors; - s->wwn = wwn; + s->wwn = dev->wwn; /* The SMART values should be preserved across power cycles but they aren't. */ s->smart_enabled = 1; @@ -2547,27 +2611,26 @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind, s->smart_errors = 0; s->smart_selftest_count = 0; if (kind == IDE_CD) { - blk_set_dev_ops(blk, &ide_cd_block_ops, s); - blk_set_guest_block_size(blk, 2048); + blk_set_dev_ops(s->blk, &ide_cd_block_ops, s); } else { if (!blk_is_inserted(s->blk)) { error_setg(errp, "Device needs media, but drive is empty"); return -1; } - if (!blk_is_writable(blk)) { + if (!blk_is_writable(s->blk)) { error_setg(errp, "Can't use a read-only drive"); return -1; } - blk_set_dev_ops(blk, &ide_hd_block_ops, s); + blk_set_dev_ops(s->blk, &ide_hd_block_ops, s); } - if (serial) { - pstrcpy(s->drive_serial_str, sizeof(s->drive_serial_str), serial); + if (dev->serial) { + pstrcpy(s->drive_serial_str, sizeof(s->drive_serial_str), dev->serial); } else { snprintf(s->drive_serial_str, sizeof(s->drive_serial_str), "QM%05d", s->drive_serial); } - if (model) { - pstrcpy(s->drive_model_str, sizeof(s->drive_model_str), model); + if (dev->model) { + pstrcpy(s->drive_model_str, sizeof(s->drive_model_str), dev->model); } else { switch (kind) { case IDE_CD: @@ -2582,14 +2645,14 @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind, } } - if (version) { - pstrcpy(s->version, sizeof(s->version), version); + if (dev->version) { + pstrcpy(s->version, sizeof(s->version), dev->version); } else { pstrcpy(s->version, sizeof(s->version), qemu_hw_version()); } ide_reset(s); - blk_iostatus_enable(blk); + blk_iostatus_enable(s->blk); return 0; } @@ -2659,7 +2722,7 @@ static void ide_restart_bh(void *opaque) return; } - s = idebus_active_if(bus); + s = ide_bus_active_if(bus); is_read = (bus->error_status & IDE_RETRY_READ) != 0; /* The error status must be cleared before resubmitting the request: The @@ -2707,7 +2770,7 @@ static void ide_restart_cb(void *opaque, bool running, RunState state) } } -void ide_register_restart_cb(IDEBus *bus) +void ide_bus_register_restart_cb(IDEBus *bus) { if (bus->dma->ops->restart_dma) { bus->vmstate = qemu_add_vm_change_state_handler(ide_restart_cb, bus); @@ -2719,7 +2782,7 @@ static IDEDMA ide_dma_nop = { .aiocb = NULL, }; -void ide_init2(IDEBus *bus, qemu_irq irq) +void ide_bus_init_output_irq(IDEBus *bus, qemu_irq irq_out) { int i; @@ -2727,10 +2790,17 @@ void ide_init2(IDEBus *bus, qemu_irq irq) ide_init1(bus, i); ide_reset(&bus->ifs[i]); } - bus->irq = irq; + bus->irq = irq_out; bus->dma = &ide_dma_nop; } +void ide_bus_set_irq(IDEBus *bus) +{ + if (!(bus->cmd & IDE_CTRL_DISABLE_IRQ)) { + qemu_irq_raise(bus->irq); + } +} + void ide_exit(IDEState *s) { timer_free(s->sector_write_timer); @@ -2845,7 +2915,7 @@ static const VMStateDescription vmstate_ide_atapi_gesn_state = { .version_id = 1, .minimum_version_id = 1, .needed = ide_atapi_gesn_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(events.new_media, IDEState), VMSTATE_BOOL(events.eject_request, IDEState), VMSTATE_END_OF_LIST() @@ -2857,7 +2927,7 @@ static const VMStateDescription vmstate_ide_tray_state = { .version_id = 1, .minimum_version_id = 1, .needed = ide_tray_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(tray_open, IDEState), VMSTATE_BOOL(tray_locked, IDEState), VMSTATE_END_OF_LIST() @@ -2871,7 +2941,7 @@ static const VMStateDescription vmstate_ide_drive_pio_state = { .pre_save = ide_drive_pio_pre_save, .post_load = ide_drive_pio_post_load, .needed = ide_drive_pio_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(req_nb_sectors, IDEState), VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1, vmstate_info_uint8, uint8_t), @@ -2889,7 +2959,7 @@ const VMStateDescription vmstate_ide_drive = { .version_id = 3, .minimum_version_id = 0, .post_load = ide_drive_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(mult_sectors, IDEState), VMSTATE_INT32(identify_set, IDEState), VMSTATE_BUFFER_TEST(identify_data, IDEState, is_identify_set), @@ -2912,7 +2982,7 @@ const VMStateDescription vmstate_ide_drive = { VMSTATE_UINT8_V(cdrom_changed, IDEState, 3), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_ide_drive_pio_state, &vmstate_ide_tray_state, &vmstate_ide_atapi_gesn_state, @@ -2925,7 +2995,7 @@ static const VMStateDescription vmstate_ide_error_status = { .version_id = 2, .minimum_version_id = 1, .needed = ide_error_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(error_status, IDEBus), VMSTATE_INT64_V(retry_sector_num, IDEBus, 2), VMSTATE_UINT32_V(retry_nsector, IDEBus, 2), @@ -2938,12 +3008,12 @@ const VMStateDescription vmstate_ide_bus = { .name = "ide_bus", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(cmd, IDEBus), VMSTATE_UINT8(unit, IDEBus), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_ide_error_status, NULL } diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 1007a51fcb..9b909c87f3 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -61,6 +61,7 @@ */ #include "qemu/osdep.h" +#include "hw/irq.h" #include "hw/pci/msi.h" #include "hw/pci/pci.h" #include "migration/vmstate.h" @@ -68,7 +69,8 @@ #include "hw/isa/isa.h" #include "sysemu/dma.h" #include "hw/ide/pci.h" -#include "ahci_internal.h" +#include "hw/ide/ahci-pci.h" +#include "ahci-internal.h" #define ICH9_MSI_CAP_OFFSET 0x80 #define ICH9_SATA_CAP_OFFSET 0xA8 @@ -82,7 +84,7 @@ static const VMStateDescription vmstate_ich9_ahci = { .name = "ich9_ahci", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, AHCIPCIState), VMSTATE_AHCI(ahci, AHCIPCIState), VMSTATE_END_OF_LIST() @@ -98,20 +100,21 @@ static void pci_ich9_reset(DeviceState *dev) static void pci_ich9_ahci_init(Object *obj) { - struct AHCIPCIState *d = ICH9_AHCI(obj); + AHCIPCIState *d = ICH9_AHCI(obj); ahci_init(&d->ahci, DEVICE(obj)); } static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp) { - struct AHCIPCIState *d; + AHCIPCIState *d; int sata_cap_offset; uint8_t *sata_cap; d = ICH9_AHCI(dev); int ret; - ahci_realize(&d->ahci, DEVICE(dev), pci_get_address_space(dev), 6); + d->ahci.ports = 6; + ahci_realize(&d->ahci, DEVICE(dev), pci_get_address_space(dev)); pci_config_set_prog_interface(dev->config, AHCI_PROGMODE_MAJOR_REV_1); @@ -153,7 +156,7 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp) static void pci_ich9_uninit(PCIDevice *dev) { - struct AHCIPCIState *d; + AHCIPCIState *d; d = ICH9_AHCI(dev); msi_uninit(dev); diff --git a/hw/ide/ide-bus.c b/hw/ide/ide-bus.c new file mode 100644 index 0000000000..37d003dd9a --- /dev/null +++ b/hw/ide/ide-bus.c @@ -0,0 +1,111 @@ +/* + * ide bus support for qdev. + * + * Copyright (c) 2009 Gerd Hoffmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "sysemu/block-backend.h" +#include "sysemu/blockdev.h" +#include "sysemu/runstate.h" +#include "ide-internal.h" + +static char *idebus_get_fw_dev_path(DeviceState *dev); +static void idebus_unrealize(BusState *qdev); + +static void ide_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + k->get_fw_dev_path = idebus_get_fw_dev_path; + k->unrealize = idebus_unrealize; +} + +static void idebus_unrealize(BusState *bus) +{ + IDEBus *ibus = IDE_BUS(bus); + + if (ibus->vmstate) { + qemu_del_vm_change_state_handler(ibus->vmstate); + } +} + +static const TypeInfo ide_bus_info = { + .name = TYPE_IDE_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(IDEBus), + .class_init = ide_bus_class_init, +}; + +void ide_bus_init(IDEBus *idebus, size_t idebus_size, DeviceState *dev, + int bus_id, int max_units) +{ + qbus_init(idebus, idebus_size, TYPE_IDE_BUS, dev, NULL); + idebus->bus_id = bus_id; + idebus->max_units = max_units; +} + +static char *idebus_get_fw_dev_path(DeviceState *dev) +{ + char path[30]; + + snprintf(path, sizeof(path), "%s@%x", qdev_fw_name(dev), + ((IDEBus *)dev->parent_bus)->bus_id); + + return g_strdup(path); +} + +IDEDevice *ide_bus_create_drive(IDEBus *bus, int unit, DriveInfo *drive) +{ + DeviceState *dev; + + dev = qdev_new(drive->media_cd ? "ide-cd" : "ide-hd"); + qdev_prop_set_uint32(dev, "unit", unit); + qdev_prop_set_drive_err(dev, "drive", blk_by_legacy_dinfo(drive), + &error_fatal); + qdev_realize_and_unref(dev, &bus->qbus, &error_fatal); + return DO_UPCAST(IDEDevice, qdev, dev); +} + +int ide_get_geometry(BusState *bus, int unit, + int16_t *cyls, int8_t *heads, int8_t *secs) +{ + IDEState *s = &DO_UPCAST(IDEBus, qbus, bus)->ifs[unit]; + + if (s->drive_kind != IDE_HD || !s->blk) { + return -1; + } + + *cyls = s->cylinders; + *heads = s->heads; + *secs = s->sectors; + return 0; +} + +int ide_get_bios_chs_trans(BusState *bus, int unit) +{ + return DO_UPCAST(IDEBus, qbus, bus)->ifs[unit].chs_trans; +} + +static void ide_bus_register_type(void) +{ + type_register_static(&ide_bus_info); +} + +type_init(ide_bus_register_type) diff --git a/hw/ide/qdev.c b/hw/ide/ide-dev.c similarity index 71% rename from hw/ide/qdev.c rename to hw/ide/ide-dev.c index 618045b85a..03f7967798 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/ide-dev.c @@ -1,5 +1,5 @@ /* - * ide bus support for qdev. + * IDE device functions * * Copyright (c) 2009 Gerd Hoffmann * @@ -18,74 +18,23 @@ */ #include "qemu/osdep.h" -#include "sysemu/dma.h" #include "qapi/error.h" #include "qapi/qapi-types-block.h" #include "qemu/error-report.h" -#include "qemu/main-loop.h" #include "qemu/module.h" -#include "hw/ide/internal.h" -#include "hw/qdev-properties.h" -#include "hw/qdev-properties-system.h" +#include "hw/ide/ide-dev.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" -#include "hw/block/block.h" #include "sysemu/sysemu.h" -#include "sysemu/runstate.h" #include "qapi/visitor.h" - -/* --------------------------------- */ - -static char *idebus_get_fw_dev_path(DeviceState *dev); -static void idebus_unrealize(BusState *qdev); +#include "ide-internal.h" static Property ide_props[] = { DEFINE_PROP_UINT32("unit", IDEDevice, unit, -1), + DEFINE_PROP_BOOL("win2k-install-hack", IDEDevice, win2k_install_hack, false), DEFINE_PROP_END_OF_LIST(), }; -static void ide_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - - k->get_fw_dev_path = idebus_get_fw_dev_path; - k->unrealize = idebus_unrealize; -} - -static void idebus_unrealize(BusState *bus) -{ - IDEBus *ibus = IDE_BUS(bus); - - if (ibus->vmstate) { - qemu_del_vm_change_state_handler(ibus->vmstate); - } -} - -static const TypeInfo ide_bus_info = { - .name = TYPE_IDE_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(IDEBus), - .class_init = ide_bus_class_init, -}; - -void ide_bus_init(IDEBus *idebus, size_t idebus_size, DeviceState *dev, - int bus_id, int max_units) -{ - qbus_init(idebus, idebus_size, TYPE_IDE_BUS, dev, NULL); - idebus->bus_id = bus_id; - idebus->max_units = max_units; -} - -static char *idebus_get_fw_dev_path(DeviceState *dev) -{ - char path[30]; - - snprintf(path, sizeof(path), "%s@%x", qdev_fw_name(dev), - ((IDEBus*)dev->parent_bus)->bus_id); - - return g_strdup(path); -} - static void ide_qdev_realize(DeviceState *qdev, Error **errp) { IDEDevice *dev = IDE_DEVICE(qdev); @@ -124,45 +73,7 @@ static void ide_qdev_realize(DeviceState *qdev, Error **errp) dc->realize(dev, errp); } -IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive) -{ - DeviceState *dev; - - dev = qdev_new(drive->media_cd ? "ide-cd" : "ide-hd"); - qdev_prop_set_uint32(dev, "unit", unit); - qdev_prop_set_drive_err(dev, "drive", blk_by_legacy_dinfo(drive), - &error_fatal); - qdev_realize_and_unref(dev, &bus->qbus, &error_fatal); - return DO_UPCAST(IDEDevice, qdev, dev); -} - -int ide_get_geometry(BusState *bus, int unit, - int16_t *cyls, int8_t *heads, int8_t *secs) -{ - IDEState *s = &DO_UPCAST(IDEBus, qbus, bus)->ifs[unit]; - - if (s->drive_kind != IDE_HD || !s->blk) { - return -1; - } - - *cyls = s->cylinders; - *heads = s->heads; - *secs = s->sectors; - return 0; -} - -int ide_get_bios_chs_trans(BusState *bus, int unit) -{ - return DO_UPCAST(IDEBus, qbus, bus)->ifs[unit].chs_trans; -} - -/* --------------------------------- */ - -typedef struct IDEDrive { - IDEDevice dev; -} IDEDrive; - -static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp) +void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp) { IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus); IDEState *s = bus->ifs + dev->unit; @@ -208,10 +119,7 @@ static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp) return; } - if (ide_init_drive(s, dev->conf.blk, kind, - dev->version, dev->serial, dev->model, dev->wwn, - dev->conf.cyls, dev->conf.heads, dev->conf.secs, - dev->chs_trans, errp) < 0) { + if (ide_init_drive(s, dev, kind, errp) < 0) { return; } @@ -283,14 +191,6 @@ static void ide_cd_realize(IDEDevice *dev, Error **errp) ide_dev_initfn(dev, IDE_CD, errp); } -#define DEFINE_IDE_DEV_PROPERTIES() \ - DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), \ - DEFINE_BLOCK_ERROR_PROPERTIES(IDEDrive, dev.conf), \ - DEFINE_PROP_STRING("ver", IDEDrive, dev.version), \ - DEFINE_PROP_UINT64("wwn", IDEDrive, dev.wwn, 0), \ - DEFINE_PROP_STRING("serial", IDEDrive, dev.serial),\ - DEFINE_PROP_STRING("model", IDEDrive, dev.model) - static Property ide_hd_properties[] = { DEFINE_IDE_DEV_PROPERTIES(), DEFINE_BLOCK_CHS_PROPERTIES(IDEDrive, dev.conf), @@ -362,7 +262,6 @@ static const TypeInfo ide_device_type_info = { static void ide_register_types(void) { - type_register_static(&ide_bus_info); type_register_static(&ide_hd_info); type_register_static(&ide_cd_info); type_register_static(&ide_device_type_info); diff --git a/include/hw/ide/internal.h b/hw/ide/ide-internal.h similarity index 53% rename from include/hw/ide/internal.h rename to hw/ide/ide-internal.h index 97e7e59dc5..0d64805da2 100644 --- a/include/hw/ide/internal.h +++ b/hw/ide/ide-internal.h @@ -4,30 +4,13 @@ /* * QEMU IDE Emulation -- internal header file * only files in hw/ide/ are supposed to include this file. - * non-internal declarations are in hw/ide.h + * non-internal declarations are in hw/include/ide-*.h */ -#include "qapi/qapi-types-run-state.h" -#include "hw/ide.h" -#include "hw/irq.h" -#include "hw/isa/isa.h" -#include "sysemu/dma.h" -#include "hw/block/block.h" -#include "scsi/constants.h" +#include "hw/ide/ide-bus.h" /* debug IDE devices */ #define USE_DMA_CDROM -#include "qom/object.h" - -typedef struct IDEDevice IDEDevice; -typedef struct IDEState IDEState; -typedef struct IDEDMA IDEDMA; -typedef struct IDEDMAOps IDEDMAOps; - -#define TYPE_IDE_BUS "IDE" -OBJECT_DECLARE_SIMPLE_TYPE(IDEBus, IDE_BUS) - -#define MAX_IDE_DEVS 2 /* Device/Head ("select") Register */ #define ATA_DEV_SELECT 0x10 @@ -41,32 +24,32 @@ OBJECT_DECLARE_SIMPLE_TYPE(IDEBus, IDE_BUS) /* Bits of HD_STATUS */ -#define ERR_STAT 0x01 -#define INDEX_STAT 0x02 -#define ECC_STAT 0x04 /* Corrected error */ -#define DRQ_STAT 0x08 -#define SEEK_STAT 0x10 -#define SRV_STAT 0x10 -#define WRERR_STAT 0x20 -#define READY_STAT 0x40 -#define BUSY_STAT 0x80 +#define ERR_STAT 0x01 +#define INDEX_STAT 0x02 +#define ECC_STAT 0x04 /* Corrected error */ +#define DRQ_STAT 0x08 +#define SEEK_STAT 0x10 +#define SRV_STAT 0x10 +#define WRERR_STAT 0x20 +#define READY_STAT 0x40 +#define BUSY_STAT 0x80 /* Bits for HD_ERROR */ -#define MARK_ERR 0x01 /* Bad address mark */ -#define TRK0_ERR 0x02 /* couldn't find track 0 */ -#define ABRT_ERR 0x04 /* Command aborted */ -#define MCR_ERR 0x08 /* media change request */ -#define ID_ERR 0x10 /* ID field not found */ -#define MC_ERR 0x20 /* media changed */ -#define ECC_ERR 0x40 /* Uncorrectable ECC error */ -#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */ -#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */ +#define MARK_ERR 0x01 /* Bad address mark */ +#define TRK0_ERR 0x02 /* couldn't find track 0 */ +#define ABRT_ERR 0x04 /* Command aborted */ +#define MCR_ERR 0x08 /* media change request */ +#define ID_ERR 0x10 /* ID field not found */ +#define MC_ERR 0x20 /* media changed */ +#define ECC_ERR 0x40 /* Uncorrectable ECC error */ +#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */ +#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */ /* Bits of HD_NSECTOR */ -#define CD 0x01 -#define IO 0x02 -#define REL 0x04 -#define TAG_MASK 0xf8 +#define CD 0x01 +#define IO 0x02 +#define REL 0x04 +#define TAG_MASK 0xf8 /* Bits of Device Control register */ #define IDE_CTRL_HOB 0x80 @@ -74,50 +57,50 @@ OBJECT_DECLARE_SIMPLE_TYPE(IDEBus, IDE_BUS) #define IDE_CTRL_DISABLE_IRQ 0x02 /* ACS-2 T13/2015-D Table B.2 Command codes */ -#define WIN_NOP 0x00 +#define WIN_NOP 0x00 /* reserved 0x01..0x02 */ -#define CFA_REQ_EXT_ERROR_CODE 0x03 /* CFA Request Extended Error Code */ +#define CFA_REQ_EXT_ERROR_CODE 0x03 /* CFA Request Extended Error Code */ /* reserved 0x04..0x05 */ #define WIN_DSM 0x06 /* reserved 0x07 */ -#define WIN_DEVICE_RESET 0x08 +#define WIN_DEVICE_RESET 0x08 /* reserved 0x09..0x0a */ /* REQUEST SENSE DATA EXT 0x0B */ /* reserved 0x0C..0x0F */ #define WIN_RECAL 0x10 /* obsolete since ATA4 */ /* obsolete since ATA3, retired in ATA4 0x11..0x1F */ -#define WIN_READ 0x20 /* 28-Bit */ +#define WIN_READ 0x20 /* 28-Bit */ #define WIN_READ_ONCE 0x21 /* 28-Bit w/o retries, obsolete since ATA5 */ /* obsolete since ATA4 0x22..0x23 */ -#define WIN_READ_EXT 0x24 /* 48-Bit */ -#define WIN_READDMA_EXT 0x25 /* 48-Bit */ +#define WIN_READ_EXT 0x24 /* 48-Bit */ +#define WIN_READDMA_EXT 0x25 /* 48-Bit */ #define WIN_READDMA_QUEUED_EXT 0x26 /* 48-Bit, obsolete since ACS2 */ -#define WIN_READ_NATIVE_MAX_EXT 0x27 /* 48-Bit */ +#define WIN_READ_NATIVE_MAX_EXT 0x27 /* 48-Bit */ /* reserved 0x28 */ -#define WIN_MULTREAD_EXT 0x29 /* 48-Bit */ +#define WIN_MULTREAD_EXT 0x29 /* 48-Bit */ /* READ STREAM DMA EXT 0x2A */ /* READ STREAM EXT 0x2B */ /* reserved 0x2C..0x2E */ /* READ LOG EXT 0x2F */ -#define WIN_WRITE 0x30 /* 28-Bit */ +#define WIN_WRITE 0x30 /* 28-Bit */ #define WIN_WRITE_ONCE 0x31 /* 28-Bit w/o retries, obsolete since ATA5 */ /* obsolete since ATA4 0x32..0x33 */ -#define WIN_WRITE_EXT 0x34 /* 48-Bit */ -#define WIN_WRITEDMA_EXT 0x35 /* 48-Bit */ -#define WIN_WRITEDMA_QUEUED_EXT 0x36 /* 48-Bit */ +#define WIN_WRITE_EXT 0x34 /* 48-Bit */ +#define WIN_WRITEDMA_EXT 0x35 /* 48-Bit */ +#define WIN_WRITEDMA_QUEUED_EXT 0x36 /* 48-Bit */ #define WIN_SET_MAX_EXT 0x37 /* 48-Bit, obsolete since ACS2 */ -#define WIN_SET_MAX_EXT 0x37 /* 48-Bit */ -#define CFA_WRITE_SECT_WO_ERASE 0x38 /* CFA Write Sectors without erase */ -#define WIN_MULTWRITE_EXT 0x39 /* 48-Bit */ +#define WIN_SET_MAX_EXT 0x37 /* 48-Bit */ +#define CFA_WRITE_SECT_WO_ERASE 0x38 /* CFA Write Sectors without erase */ +#define WIN_MULTWRITE_EXT 0x39 /* 48-Bit */ /* WRITE STREAM DMA EXT 0x3A */ /* WRITE STREAM EXT 0x3B */ #define WIN_WRITE_VERIFY 0x3C /* 28-Bit, obsolete since ATA4 */ /* WRITE DMA FUA EXT 0x3D */ /* obsolete since ACS2 0x3E */ /* WRITE LOG EXT 0x3F */ -#define WIN_VERIFY 0x40 /* 28-Bit - Read Verify Sectors */ +#define WIN_VERIFY 0x40 /* 28-Bit - Read Verify Sectors */ #define WIN_VERIFY_ONCE 0x41 /* 28-Bit - w/o retries, obsolete since ATA5 */ -#define WIN_VERIFY_EXT 0x42 /* 48-Bit */ +#define WIN_VERIFY_EXT 0x42 /* 48-Bit */ /* reserved 0x43..0x44 */ /* WRITE UNCORRECTABLE EXT 0x45 */ /* reserved 0x46 */ @@ -139,11 +122,11 @@ OBJECT_DECLARE_SIMPLE_TYPE(IDEBus, IDE_BUS) #define WIN_SEEK 0x70 /* obsolete since ATA7 */ /* reserved 0x71-0x7F */ /* vendor specific 0x80-0x86 */ -#define CFA_TRANSLATE_SECTOR 0x87 /* CFA Translate Sector */ +#define CFA_TRANSLATE_SECTOR 0x87 /* CFA Translate Sector */ /* vendor specific 0x88-0x8F */ -#define WIN_DIAGNOSE 0x90 +#define WIN_DIAGNOSE 0x90 #define WIN_SPECIFY 0x91 /* set drive geometry translation, obsolete since ATA6 */ -#define WIN_DOWNLOAD_MICROCODE 0x92 +#define WIN_DOWNLOAD_MICROCODE 0x92 /* DOWNLOAD MICROCODE DMA 0x93 */ #define WIN_STANDBYNOW2 0x94 /* retired in ATA4 */ #define WIN_IDLEIMMEDIATE2 0x95 /* force drive to become "ready", retired in ATA4 */ @@ -153,31 +136,31 @@ OBJECT_DECLARE_SIMPLE_TYPE(IDEBus, IDE_BUS) #define WIN_SLEEPNOW2 0x99 /* retired in ATA4 */ /* vendor specific 0x9A */ /* reserved 0x9B..0x9F */ -#define WIN_PACKETCMD 0xA0 /* Send a packet command. */ -#define WIN_PIDENTIFY 0xA1 /* identify ATAPI device */ +#define WIN_PACKETCMD 0xA0 /* Send a packet command. */ +#define WIN_PIDENTIFY 0xA1 /* identify ATAPI device */ #define WIN_QUEUED_SERVICE 0xA2 /* obsolete since ACS2 */ /* reserved 0xA3..0xAF */ -#define WIN_SMART 0xB0 /* self-monitoring and reporting */ +#define WIN_SMART 0xB0 /* self-monitoring and reporting */ /* Device Configuration Overlay 0xB1 */ /* reserved 0xB2..0xB3 */ /* Sanitize Device 0xB4 */ /* reserved 0xB5 */ /* NV Cache 0xB6 */ /* reserved for CFA 0xB7..0xBB */ -#define CFA_ACCESS_METADATA_STORAGE 0xB8 +#define CFA_ACCESS_METADATA_STORAGE 0xB8 /* reserved 0xBC..0xBF */ -#define CFA_ERASE_SECTORS 0xC0 /* microdrives implement as NOP */ +#define CFA_ERASE_SECTORS 0xC0 /* microdrives implement as NOP */ /* vendor specific 0xC1..0xC3 */ -#define WIN_MULTREAD 0xC4 /* read sectors using multiple mode*/ -#define WIN_MULTWRITE 0xC5 /* write sectors using multiple mode */ -#define WIN_SETMULT 0xC6 /* enable/disable multiple mode */ +#define WIN_MULTREAD 0xC4 /* read sectors using multiple mode*/ +#define WIN_MULTWRITE 0xC5 /* write sectors using multiple mode */ +#define WIN_SETMULT 0xC6 /* enable/disable multiple mode */ #define WIN_READDMA_QUEUED 0xC7 /* read sectors using Queued DMA transfers, obsolete since ACS2 */ -#define WIN_READDMA 0xC8 /* read sectors using DMA transfers */ +#define WIN_READDMA 0xC8 /* read sectors using DMA transfers */ #define WIN_READDMA_ONCE 0xC9 /* 28-Bit - w/o retries, obsolete since ATA5 */ -#define WIN_WRITEDMA 0xCA /* write sectors using DMA transfers */ +#define WIN_WRITEDMA 0xCA /* write sectors using DMA transfers */ #define WIN_WRITEDMA_ONCE 0xCB /* 28-Bit - w/o retries, obsolete since ATA5 */ -#define WIN_WRITEDMA_QUEUED 0xCC /* write sectors using Queued DMA transfers, obsolete since ACS2 */ -#define CFA_WRITE_MULTI_WO_ERASE 0xCD /* CFA Write multiple without erase */ +#define WIN_WRITEDMA_QUEUED 0xCC /* write sectors using Queued DMA transfers, obsolete since ACS2 */ +#define CFA_WRITE_MULTI_WO_ERASE 0xCD /* CFA Write multiple without erase */ /* WRITE MULTIPLE FUA EXT 0xCE */ /* reserved 0xCF..0xDO */ /* CHECK MEDIA CARD TYPE 0xD1 */ @@ -187,33 +170,33 @@ OBJECT_DECLARE_SIMPLE_TYPE(IDEBus, IDE_BUS) /* obsolete since ATA3, retired in ATA4 0xDB..0xDD */ #define WIN_DOORLOCK 0xDE /* lock door on removable drives, obsolete since ATA8 */ #define WIN_DOORUNLOCK 0xDF /* unlock door on removable drives, obsolete since ATA8 */ -#define WIN_STANDBYNOW1 0xE0 -#define WIN_IDLEIMMEDIATE 0xE1 /* force drive to become "ready" */ -#define WIN_STANDBY 0xE2 /* Set device in Standby Mode */ -#define WIN_SETIDLE1 0xE3 -#define WIN_READ_BUFFER 0xE4 /* force read only 1 sector */ -#define WIN_CHECKPOWERMODE1 0xE5 -#define WIN_SLEEPNOW1 0xE6 -#define WIN_FLUSH_CACHE 0xE7 -#define WIN_WRITE_BUFFER 0xE8 /* force write only 1 sector */ +#define WIN_STANDBYNOW1 0xE0 +#define WIN_IDLEIMMEDIATE 0xE1 /* force drive to become "ready" */ +#define WIN_STANDBY 0xE2 /* Set device in Standby Mode */ +#define WIN_SETIDLE1 0xE3 +#define WIN_READ_BUFFER 0xE4 /* force read only 1 sector */ +#define WIN_CHECKPOWERMODE1 0xE5 +#define WIN_SLEEPNOW1 0xE6 +#define WIN_FLUSH_CACHE 0xE7 +#define WIN_WRITE_BUFFER 0xE8 /* force write only 1 sector */ /* READ BUFFER DMA 0xE9 */ -#define WIN_FLUSH_CACHE_EXT 0xEA /* 48-Bit */ +#define WIN_FLUSH_CACHE_EXT 0xEA /* 48-Bit */ /* WRITE BUFFER DMA 0xEB */ -#define WIN_IDENTIFY 0xEC /* ask drive to identify itself */ +#define WIN_IDENTIFY 0xEC /* ask drive to identify itself */ #define WIN_MEDIAEJECT 0xED /* obsolete since ATA8 */ /* obsolete since ATA4 0xEE */ -#define WIN_SETFEATURES 0xEF /* set special drive features */ +#define WIN_SETFEATURES 0xEF /* set special drive features */ #define IBM_SENSE_CONDITION 0xF0 /* measure disk temperature, vendor specific */ -#define WIN_SECURITY_SET_PASS 0xF1 -#define WIN_SECURITY_UNLOCK 0xF2 -#define WIN_SECURITY_ERASE_PREPARE 0xF3 -#define WIN_SECURITY_ERASE_UNIT 0xF4 -#define WIN_SECURITY_FREEZE_LOCK 0xF5 +#define WIN_SECURITY_SET_PASS 0xF1 +#define WIN_SECURITY_UNLOCK 0xF2 +#define WIN_SECURITY_ERASE_PREPARE 0xF3 +#define WIN_SECURITY_ERASE_UNIT 0xF4 +#define WIN_SECURITY_FREEZE_LOCK 0xF5 #define CFA_WEAR_LEVEL 0xF5 /* microdrives implement as NOP; not specified in T13! */ -#define WIN_SECURITY_DISABLE 0xF6 +#define WIN_SECURITY_DISABLE 0xF6 /* vendor specific 0xF7 */ -#define WIN_READ_NATIVE_MAX 0xF8 /* return the native maximum address */ -#define WIN_SET_MAX 0xF9 +#define WIN_READ_NATIVE_MAX 0xF8 /* return the native maximum address */ +#define WIN_SET_MAX 0xF9 /* vendor specific 0xFA..0xFF */ /* set to 1 set disable mult support */ @@ -234,68 +217,68 @@ OBJECT_DECLARE_SIMPLE_TYPE(IDEBus, IDE_BUS) /* The generic packet command opcodes for CD/DVD Logical Units, * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */ -#define GPCMD_BLANK 0xa1 -#define GPCMD_CLOSE_TRACK 0x5b -#define GPCMD_FLUSH_CACHE 0x35 -#define GPCMD_FORMAT_UNIT 0x04 -#define GPCMD_GET_CONFIGURATION 0x46 +#define GPCMD_BLANK 0xa1 +#define GPCMD_CLOSE_TRACK 0x5b +#define GPCMD_FLUSH_CACHE 0x35 +#define GPCMD_FORMAT_UNIT 0x04 +#define GPCMD_GET_CONFIGURATION 0x46 #define GPCMD_GET_EVENT_STATUS_NOTIFICATION 0x4a -#define GPCMD_GET_PERFORMANCE 0xac -#define GPCMD_INQUIRY 0x12 -#define GPCMD_LOAD_UNLOAD 0xa6 -#define GPCMD_MECHANISM_STATUS 0xbd -#define GPCMD_MODE_SELECT_10 0x55 -#define GPCMD_MODE_SENSE_10 0x5a -#define GPCMD_PAUSE_RESUME 0x4b -#define GPCMD_PLAY_AUDIO_10 0x45 -#define GPCMD_PLAY_AUDIO_MSF 0x47 -#define GPCMD_PLAY_AUDIO_TI 0x48 -#define GPCMD_PLAY_CD 0xbc +#define GPCMD_GET_PERFORMANCE 0xac +#define GPCMD_INQUIRY 0x12 +#define GPCMD_LOAD_UNLOAD 0xa6 +#define GPCMD_MECHANISM_STATUS 0xbd +#define GPCMD_MODE_SELECT_10 0x55 +#define GPCMD_MODE_SENSE_10 0x5a +#define GPCMD_PAUSE_RESUME 0x4b +#define GPCMD_PLAY_AUDIO_10 0x45 +#define GPCMD_PLAY_AUDIO_MSF 0x47 +#define GPCMD_PLAY_AUDIO_TI 0x48 +#define GPCMD_PLAY_CD 0xbc #define GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e -#define GPCMD_READ_10 0x28 -#define GPCMD_READ_12 0xa8 -#define GPCMD_READ_CDVD_CAPACITY 0x25 -#define GPCMD_READ_CD 0xbe -#define GPCMD_READ_CD_MSF 0xb9 -#define GPCMD_READ_DISC_INFO 0x51 -#define GPCMD_READ_DVD_STRUCTURE 0xad -#define GPCMD_READ_FORMAT_CAPACITIES 0x23 -#define GPCMD_READ_HEADER 0x44 -#define GPCMD_READ_TRACK_RZONE_INFO 0x52 -#define GPCMD_READ_SUBCHANNEL 0x42 -#define GPCMD_READ_TOC_PMA_ATIP 0x43 -#define GPCMD_REPAIR_RZONE_TRACK 0x58 -#define GPCMD_REPORT_KEY 0xa4 -#define GPCMD_REQUEST_SENSE 0x03 -#define GPCMD_RESERVE_RZONE_TRACK 0x53 -#define GPCMD_SCAN 0xba -#define GPCMD_SEEK 0x2b -#define GPCMD_SEND_DVD_STRUCTURE 0xad -#define GPCMD_SEND_EVENT 0xa2 -#define GPCMD_SEND_KEY 0xa3 -#define GPCMD_SEND_OPC 0x54 -#define GPCMD_SET_READ_AHEAD 0xa7 -#define GPCMD_SET_STREAMING 0xb6 -#define GPCMD_START_STOP_UNIT 0x1b -#define GPCMD_STOP_PLAY_SCAN 0x4e -#define GPCMD_TEST_UNIT_READY 0x00 -#define GPCMD_VERIFY_10 0x2f -#define GPCMD_WRITE_10 0x2a -#define GPCMD_WRITE_AND_VERIFY_10 0x2e +#define GPCMD_READ_10 0x28 +#define GPCMD_READ_12 0xa8 +#define GPCMD_READ_CDVD_CAPACITY 0x25 +#define GPCMD_READ_CD 0xbe +#define GPCMD_READ_CD_MSF 0xb9 +#define GPCMD_READ_DISC_INFO 0x51 +#define GPCMD_READ_DVD_STRUCTURE 0xad +#define GPCMD_READ_FORMAT_CAPACITIES 0x23 +#define GPCMD_READ_HEADER 0x44 +#define GPCMD_READ_TRACK_RZONE_INFO 0x52 +#define GPCMD_READ_SUBCHANNEL 0x42 +#define GPCMD_READ_TOC_PMA_ATIP 0x43 +#define GPCMD_REPAIR_RZONE_TRACK 0x58 +#define GPCMD_REPORT_KEY 0xa4 +#define GPCMD_REQUEST_SENSE 0x03 +#define GPCMD_RESERVE_RZONE_TRACK 0x53 +#define GPCMD_SCAN 0xba +#define GPCMD_SEEK 0x2b +#define GPCMD_SEND_DVD_STRUCTURE 0xad +#define GPCMD_SEND_EVENT 0xa2 +#define GPCMD_SEND_KEY 0xa3 +#define GPCMD_SEND_OPC 0x54 +#define GPCMD_SET_READ_AHEAD 0xa7 +#define GPCMD_SET_STREAMING 0xb6 +#define GPCMD_START_STOP_UNIT 0x1b +#define GPCMD_STOP_PLAY_SCAN 0x4e +#define GPCMD_TEST_UNIT_READY 0x00 +#define GPCMD_VERIFY_10 0x2f +#define GPCMD_WRITE_10 0x2a +#define GPCMD_WRITE_AND_VERIFY_10 0x2e /* This is listed as optional in ATAPI 2.6, but is (curiously) * missing from Mt. Fuji, Table 57. It _is_ mentioned in Mt. Fuji * Table 377 as an MMC command for SCSi devices though... Most ATAPI * drives support it. */ -#define GPCMD_SET_SPEED 0xbb +#define GPCMD_SET_SPEED 0xbb /* This seems to be a SCSI specific CD-ROM opcode * to play data at track/index */ -#define GPCMD_PLAYAUDIO_TI 0x48 +#define GPCMD_PLAYAUDIO_TI 0x48 /* * From MS Media Status Notification Support Specification. For * older drives only. */ -#define GPCMD_GET_MEDIA_STATUS 0xda -#define GPCMD_MODE_SENSE_6 0x1a +#define GPCMD_GET_MEDIA_STATUS 0xda +#define GPCMD_MODE_SENSE_6 0x1a #define ATAPI_INT_REASON_CD 0x01 /* 0 = data transfer */ #define ATAPI_INT_REASON_IO 0x02 /* 1 = transfer to the host */ @@ -331,32 +314,11 @@ OBJECT_DECLARE_SIMPLE_TYPE(IDEBus, IDE_BUS) #define SMART_DISABLE 0xd9 #define SMART_STATUS 0xda -typedef enum { IDE_HD, IDE_CD, IDE_CFATA } IDEDriveKind; - -typedef void EndTransferFunc(IDEState *); - -typedef void DMAStartFunc(const IDEDMA *, IDEState *, BlockCompletionFunc *); -typedef void DMAVoidFunc(const IDEDMA *); -typedef int DMAIntFunc(const IDEDMA *, bool); -typedef int32_t DMAInt32Func(const IDEDMA *, int32_t len); -typedef void DMAu32Func(const IDEDMA *, uint32_t); -typedef void DMAStopFunc(const IDEDMA *, bool); - -struct unreported_events { - bool eject_request; - bool new_media; -}; - -enum ide_dma_cmd { - IDE_DMA_READ = 0, - IDE_DMA_WRITE, - IDE_DMA_TRIM, - IDE_DMA_ATAPI, - IDE_DMA__COUNT -}; - extern const char *IDE_DMA_CMD_lookup[IDE_DMA__COUNT]; +extern const MemoryRegionPortio ide_portio_list[]; +extern const MemoryRegionPortio ide_portio2_list[]; + #define ide_cmd_is_read(s) \ ((s)->dma_cmd == IDE_DMA_READ) @@ -369,162 +331,6 @@ typedef struct IDEBufferedRequest { bool orphaned; } IDEBufferedRequest; -/* NOTE: IDEState represents in fact one drive */ -struct IDEState { - IDEBus *bus; - uint8_t unit; - /* ide config */ - IDEDriveKind drive_kind; - int cylinders, heads, sectors, chs_trans; - int64_t nb_sectors; - int mult_sectors; - int identify_set; - uint8_t identify_data[512]; - int drive_serial; - char drive_serial_str[21]; - char drive_model_str[41]; - uint64_t wwn; - /* ide regs */ - uint8_t feature; - uint8_t error; - uint32_t nsector; - uint8_t sector; - uint8_t lcyl; - uint8_t hcyl; - /* other part of tf for lba48 support */ - uint8_t hob_feature; - uint8_t hob_nsector; - uint8_t hob_sector; - uint8_t hob_lcyl; - uint8_t hob_hcyl; - - uint8_t select; - uint8_t status; - - /* set for lba48 access */ - uint8_t lba48; - BlockBackend *blk; - char version[9]; - /* ATAPI specific */ - struct unreported_events events; - uint8_t sense_key; - uint8_t asc; - bool tray_open; - bool tray_locked; - uint8_t cdrom_changed; - int packet_transfer_size; - int elementary_transfer_size; - int32_t io_buffer_index; - int lba; - int cd_sector_size; - int atapi_dma; /* true if dma is requested for the packet cmd */ - BlockAcctCookie acct; - BlockAIOCB *pio_aiocb; - QEMUIOVector qiov; - QLIST_HEAD(, IDEBufferedRequest) buffered_requests; - /* ATA DMA state */ - uint64_t io_buffer_offset; - int32_t io_buffer_size; - QEMUSGList sg; - /* PIO transfer handling */ - int req_nb_sectors; /* number of sectors per interrupt */ - EndTransferFunc *end_transfer_func; - uint8_t *data_ptr; - uint8_t *data_end; - uint8_t *io_buffer; - /* PIO save/restore */ - int32_t io_buffer_total_len; - int32_t cur_io_buffer_offset; - int32_t cur_io_buffer_len; - uint8_t end_transfer_fn_idx; - QEMUTimer *sector_write_timer; /* only used for win2k install hack */ - uint32_t irq_count; /* counts IRQs when using win2k install hack */ - /* CF-ATA extended error */ - uint8_t ext_error; - /* CF-ATA metadata storage */ - uint32_t mdata_size; - uint8_t *mdata_storage; - int media_changed; - enum ide_dma_cmd dma_cmd; - /* SMART */ - uint8_t smart_enabled; - uint8_t smart_autosave; - int smart_errors; - uint8_t smart_selftest_count; - uint8_t *smart_selftest_data; - /* AHCI */ - int ncq_queues; -}; - -struct IDEDMAOps { - DMAStartFunc *start_dma; - DMAVoidFunc *pio_transfer; - DMAInt32Func *prepare_buf; - DMAu32Func *commit_buf; - DMAIntFunc *rw_buf; - DMAVoidFunc *restart; - DMAVoidFunc *restart_dma; - DMAStopFunc *set_inactive; - DMAVoidFunc *cmd_done; - DMAVoidFunc *reset; -}; - -struct IDEDMA { - const struct IDEDMAOps *ops; - QEMUIOVector qiov; - BlockAIOCB *aiocb; -}; - -struct IDEBus { - BusState qbus; - IDEDevice *master; - IDEDevice *slave; - IDEState ifs[2]; - QEMUBH *bh; - - int bus_id; - int max_units; - IDEDMA *dma; - uint8_t unit; - uint8_t cmd; - qemu_irq irq; - - int error_status; - uint8_t retry_unit; - int64_t retry_sector_num; - uint32_t retry_nsector; - PortioList portio_list; - PortioList portio2_list; - VMChangeStateEntry *vmstate; -}; - -#define TYPE_IDE_DEVICE "ide-device" -OBJECT_DECLARE_TYPE(IDEDevice, IDEDeviceClass, IDE_DEVICE) - -struct IDEDeviceClass { - DeviceClass parent_class; - void (*realize)(IDEDevice *dev, Error **errp); -}; - -struct IDEDevice { - DeviceState qdev; - uint32_t unit; - BlockConf conf; - int chs_trans; - char *version; - char *serial; - char *model; - uint64_t wwn; - /* - * 0x0000 - rotation rate not reported - * 0x0001 - non-rotating medium (SSD) - * 0x0002-0x0400 - reserved - * 0x0401-0xffe - rotations per minute - * 0xffff - reserved - */ - uint16_t rotation_rate; -}; - /* These are used for the error_status field of IDEBus */ #define IDE_RETRY_MASK 0xf8 #define IDE_RETRY_DMA 0x08 @@ -565,18 +371,11 @@ static inline uint8_t ide_dma_cmd_to_retry(uint8_t dma_cmd) return 0; } -static inline IDEState *idebus_active_if(IDEBus *bus) +static inline IDEState *ide_bus_active_if(IDEBus *bus) { return bus->ifs + bus->unit; } -static inline void ide_set_irq(IDEBus *bus) -{ - if (!(bus->cmd & IDE_CTRL_DISABLE_IRQ)) { - qemu_irq_raise(bus->irq); - } -} - /* hw/ide/core.c */ extern const VMStateDescription vmstate_ide_bus; @@ -617,17 +416,14 @@ uint32_t ide_data_readw(void *opaque, uint32_t addr); void ide_data_writel(void *opaque, uint32_t addr, uint32_t val); uint32_t ide_data_readl(void *opaque, uint32_t addr); -int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind, - const char *version, const char *serial, const char *model, - uint64_t wwn, - uint32_t cylinders, uint32_t heads, uint32_t secs, - int chs_trans, Error **errp); -void ide_init2(IDEBus *bus, qemu_irq irq); +int ide_init_drive(IDEState *s, IDEDevice *dev, IDEDriveKind kind, Error **errp); void ide_exit(IDEState *s); +void ide_bus_init_output_irq(IDEBus *bus, qemu_irq irq_out); int ide_init_ioport(IDEBus *bus, ISADevice *isa, int iobase, int iobase2); -void ide_register_restart_cb(IDEBus *bus); +void ide_bus_set_irq(IDEBus *bus); +void ide_bus_register_restart_cb(IDEBus *bus); -void ide_exec_cmd(IDEBus *bus, uint32_t val); +void ide_bus_exec_cmd(IDEBus *bus, uint32_t val); void ide_transfer_start(IDEState *s, uint8_t *buf, int size, EndTransferFunc *end_transfer_func); @@ -647,11 +443,6 @@ void ide_cancel_dma_sync(IDEState *s); void ide_atapi_cmd(IDEState *s); void ide_atapi_cmd_reply_end(IDEState *s); -/* hw/ide/qdev.c */ -void ide_bus_init(IDEBus *idebus, size_t idebus_size, DeviceState *dev, - int bus_id, int max_units); -IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive); - int ide_handle_rw_error(IDEState *s, int error, int op); #endif /* HW_IDE_INTERNAL_H */ diff --git a/hw/ide/ioport.c b/hw/ide/ioport.c index e6caa537fa..a2f457f0bd 100644 --- a/hw/ide/ioport.c +++ b/hw/ide/ioport.c @@ -25,31 +25,9 @@ #include "qemu/osdep.h" #include "hw/isa/isa.h" -#include "qemu/error-report.h" -#include "qemu/timer.h" -#include "sysemu/blockdev.h" -#include "sysemu/dma.h" -#include "hw/block/block.h" -#include "sysemu/block-backend.h" -#include "qapi/error.h" -#include "qemu/cutils.h" -#include "sysemu/replay.h" - -#include "hw/ide/internal.h" +#include "ide-internal.h" #include "trace.h" -static const MemoryRegionPortio ide_portio_list[] = { - { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write }, - { 0, 1, 2, .read = ide_data_readw, .write = ide_data_writew }, - { 0, 1, 4, .read = ide_data_readl, .write = ide_data_writel }, - PORTIO_END_OF_LIST(), -}; - -static const MemoryRegionPortio ide_portio2_list[] = { - { 0, 1, 1, .read = ide_status_read, .write = ide_ctrl_write }, - PORTIO_END_OF_LIST(), -}; - int ide_init_ioport(IDEBus *bus, ISADevice *dev, int iobase, int iobase2) { int ret; diff --git a/hw/ide/isa.c b/hw/ide/isa.c index 8bedbd13f1..934c45887c 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -31,23 +31,20 @@ #include "qemu/module.h" #include "sysemu/dma.h" -#include "hw/ide/internal.h" +#include "hw/ide/isa.h" #include "qom/object.h" +#include "ide-internal.h" /***********************************************************/ /* ISA IDE definitions */ -#define TYPE_ISA_IDE "isa-ide" -OBJECT_DECLARE_SIMPLE_TYPE(ISAIDEState, ISA_IDE) - struct ISAIDEState { ISADevice parent_obj; IDEBus bus; uint32_t iobase; uint32_t iobase2; - uint32_t isairq; - qemu_irq irq; + uint32_t irqnum; }; static void isa_ide_reset(DeviceState *d) @@ -61,7 +58,7 @@ static const VMStateDescription vmstate_ide_isa = { .name = "isa-ide", .version_id = 3, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_IDE_BUS(bus, ISAIDEState), VMSTATE_IDE_DRIVES(bus.ifs, ISAIDEState), VMSTATE_END_OF_LIST() @@ -75,13 +72,12 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp) ide_bus_init(&s->bus, sizeof(s->bus), dev, 0, 2); ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2); - s->irq = isa_get_irq(isadev, s->isairq); - ide_init2(&s->bus, s->irq); - vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_isa, s); - ide_register_restart_cb(&s->bus); + ide_bus_init_output_irq(&s->bus, isa_get_irq(isadev, s->irqnum)); + vmstate_register_any(VMSTATE_IF(dev), &vmstate_ide_isa, s); + ide_bus_register_restart_cb(&s->bus); } -ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, +ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int irqnum, DriveInfo *hd0, DriveInfo *hd1) { DeviceState *dev; @@ -92,15 +88,15 @@ ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, dev = DEVICE(isadev); qdev_prop_set_uint32(dev, "iobase", iobase); qdev_prop_set_uint32(dev, "iobase2", iobase2); - qdev_prop_set_uint32(dev, "irq", isairq); + qdev_prop_set_uint32(dev, "irq", irqnum); isa_realize_and_unref(isadev, bus, &error_fatal); s = ISA_IDE(dev); if (hd0) { - ide_create_drive(&s->bus, 0, hd0); + ide_bus_create_drive(&s->bus, 0, hd0); } if (hd1) { - ide_create_drive(&s->bus, 1, hd1); + ide_bus_create_drive(&s->bus, 1, hd1); } return isadev; } @@ -108,7 +104,7 @@ ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, static Property isa_ide_properties[] = { DEFINE_PROP_UINT32("iobase", ISAIDEState, iobase, 0x1f0), DEFINE_PROP_UINT32("iobase2", ISAIDEState, iobase2, 0x3f6), - DEFINE_PROP_UINT32("irq", ISAIDEState, isairq, 14), + DEFINE_PROP_UINT32("irq", ISAIDEState, irqnum, 14), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 1c15c37ec5..aca90d04f0 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -24,7 +24,7 @@ */ #include "qemu/osdep.h" -#include "hw/ppc/mac.h" +#include "hw/irq.h" #include "hw/ppc/mac_dbdma.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -33,7 +33,7 @@ #include "sysemu/block-backend.h" #include "sysemu/dma.h" -#include "hw/ide/internal.h" +#include "ide-internal.h" /* debug MACIO */ // #define DEBUG_MACIO @@ -60,7 +60,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) { DBDMA_io *io = opaque; MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); + IDEState *s = ide_bus_active_if(&m->bus); int64_t offset; MACIO_DPRINTF("pmac_ide_atapi_transfer_cb\n"); @@ -136,7 +136,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) { DBDMA_io *io = opaque; MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); + IDEState *s = ide_bus_active_if(&m->bus); int64_t offset; MACIO_DPRINTF("pmac_ide_transfer_cb\n"); @@ -160,7 +160,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) MACIO_DPRINTF("End of IDE transfer\n"); qemu_sglist_destroy(&s->sg); s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); m->dma_active = false; goto done; } @@ -220,7 +220,7 @@ done: static void pmac_ide_transfer(DBDMA_io *io) { MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); + IDEState *s = ide_bus_active_if(&m->bus); MACIO_DPRINTF("\n"); @@ -251,7 +251,7 @@ static void pmac_ide_transfer(DBDMA_io *io) static void pmac_ide_flush(DBDMA_io *io) { MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); + IDEState *s = ide_bus_active_if(&m->bus); if (s->bus->dma->aiocb) { blk_drain(s->blk); @@ -361,7 +361,7 @@ static const VMStateDescription vmstate_pmac = { .name = "ide", .version_id = 5, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_IDE_BUS(bus, MACIOIDEState), VMSTATE_IDE_DRIVES(bus.ifs, MACIOIDEState), VMSTATE_BOOL(dma_active, MACIOIDEState), @@ -420,7 +420,7 @@ static void macio_ide_realizefn(DeviceState *dev, Error **errp) { MACIOIDEState *s = MACIO_IDE(dev); - ide_init2(&s->bus, s->ide_irq); + ide_bus_init_output_irq(&s->bus, s->ide_irq); /* Register DMA callbacks */ s->dma.ops = &dbdma_ops; @@ -501,7 +501,7 @@ void macio_ide_init_drives(MACIOIDEState *s, DriveInfo **hd_table) for (i = 0; i < 2; i++) { if (hd_table[i]) { - ide_create_drive(&s->bus, i, hd_table[i]); + ide_bus_create_drive(&s->bus, i, hd_table[i]); } } } diff --git a/hw/ide/meson.build b/hw/ide/meson.build index ddcb3b28d2..d09705cac0 100644 --- a/hw/ide/meson.build +++ b/hw/ide/meson.build @@ -1,14 +1,16 @@ -softmmu_ss.add(when: 'CONFIG_AHCI', if_true: files('ahci.c')) -softmmu_ss.add(when: 'CONFIG_AHCI_ICH9', if_true: files('ich.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('ahci-allwinner.c')) -softmmu_ss.add(when: 'CONFIG_IDE_CMD646', if_true: files('cmd646.c')) -softmmu_ss.add(when: 'CONFIG_IDE_CORE', if_true: files('core.c', 'atapi.c')) -softmmu_ss.add(when: 'CONFIG_IDE_ISA', if_true: files('isa.c', 'ioport.c')) -softmmu_ss.add(when: 'CONFIG_IDE_MACIO', if_true: files('macio.c')) -softmmu_ss.add(when: 'CONFIG_IDE_MMIO', if_true: files('mmio.c')) -softmmu_ss.add(when: 'CONFIG_IDE_PCI', if_true: files('pci.c')) -softmmu_ss.add(when: 'CONFIG_IDE_PIIX', if_true: files('piix.c', 'ioport.c')) -softmmu_ss.add(when: 'CONFIG_IDE_QDEV', if_true: files('qdev.c')) -softmmu_ss.add(when: 'CONFIG_IDE_SII3112', if_true: files('sii3112.c')) -softmmu_ss.add(when: 'CONFIG_IDE_VIA', if_true: files('via.c')) -softmmu_ss.add(when: 'CONFIG_MICRODRIVE', if_true: files('microdrive.c')) +system_ss.add(when: 'CONFIG_AHCI', if_true: files('ahci.c')) +system_ss.add(when: 'CONFIG_AHCI_ICH9', if_true: files('ich.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('ahci-allwinner.c')) +system_ss.add(when: 'CONFIG_IDE_BUS', if_true: files('ide-bus.c')) +system_ss.add(when: 'CONFIG_IDE_CF', if_true: files('cf.c')) +system_ss.add(when: 'CONFIG_IDE_CMD646', if_true: files('cmd646.c')) +system_ss.add(when: 'CONFIG_IDE_CORE', if_true: files('core.c', 'atapi.c')) +system_ss.add(when: 'CONFIG_IDE_DEV', if_true: files('ide-dev.c')) +system_ss.add(when: 'CONFIG_IDE_ISA', if_true: files('isa.c', 'ioport.c')) +system_ss.add(when: 'CONFIG_IDE_MACIO', if_true: files('macio.c')) +system_ss.add(when: 'CONFIG_IDE_MMIO', if_true: files('mmio.c')) +system_ss.add(when: 'CONFIG_IDE_PCI', if_true: files('pci.c')) +system_ss.add(when: 'CONFIG_IDE_PIIX', if_true: files('piix.c', 'ioport.c')) +system_ss.add(when: 'CONFIG_IDE_SII3112', if_true: files('sii3112.c')) +system_ss.add(when: 'CONFIG_IDE_VIA', if_true: files('via.c')) +system_ss.add(when: 'CONFIG_MICRODRIVE', if_true: files('microdrive.c')) diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c index 6df9b4cbbe..3bb152b5d3 100644 --- a/hw/ide/microdrive.c +++ b/hw/ide/microdrive.c @@ -29,9 +29,10 @@ #include "qapi/error.h" #include "qemu/module.h" #include "sysemu/dma.h" +#include "hw/irq.h" -#include "hw/ide/internal.h" #include "qom/object.h" +#include "ide-internal.h" #define TYPE_MICRODRIVE "microdrive" OBJECT_DECLARE_SIMPLE_TYPE(MicroDriveState, MICRODRIVE) @@ -39,7 +40,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(MicroDriveState, MICRODRIVE) /***********************************************************/ /* CF-ATA Microdrive */ -#define METADATA_SIZE 0x20 +#define METADATA_SIZE 0x20 /* DSCM-1XXXX Microdrive hard disk with CF+ II / PCMCIA interface. */ @@ -64,29 +65,29 @@ struct MicroDriveState { /* Register bitfields */ enum md_opt { - OPT_MODE_MMAP = 0, - OPT_MODE_IOMAP16 = 1, - OPT_MODE_IOMAP1 = 2, - OPT_MODE_IOMAP2 = 3, - OPT_MODE = 0x3f, - OPT_LEVIREQ = 0x40, - OPT_SRESET = 0x80, + OPT_MODE_MMAP = 0, + OPT_MODE_IOMAP16 = 1, + OPT_MODE_IOMAP1 = 2, + OPT_MODE_IOMAP2 = 3, + OPT_MODE = 0x3f, + OPT_LEVIREQ = 0x40, + OPT_SRESET = 0x80, }; enum md_cstat { - STAT_INT = 0x02, - STAT_PWRDWN = 0x04, - STAT_XE = 0x10, - STAT_IOIS8 = 0x20, - STAT_SIGCHG = 0x40, - STAT_CHANGED = 0x80, + STAT_INT = 0x02, + STAT_PWRDWN = 0x04, + STAT_XE = 0x10, + STAT_IOIS8 = 0x20, + STAT_SIGCHG = 0x40, + STAT_CHANGED = 0x80, }; enum md_pins { - PINS_MRDY = 0x02, - PINS_CRDY = 0x20, + PINS_MRDY = 0x02, + PINS_CRDY = 0x20, }; enum md_ctrl { - CTRL_IEN = 0x02, - CTRL_SRST = 0x04, + CTRL_IEN = 0x02, + CTRL_SRST = 0x04, }; static inline void md_interrupt_update(MicroDriveState *s) @@ -98,7 +99,7 @@ static inline void md_interrupt_update(MicroDriveState *s) } qemu_set_irq(card->slot->irq, - !(s->stat & STAT_INT) && /* Inverted */ + !(s->stat & STAT_INT) && /* Inverted */ !(s->ctrl & (CTRL_IEN | CTRL_SRST)) && !(s->opt & OPT_SRESET)); } @@ -144,17 +145,17 @@ static uint8_t md_attr_read(PCMCIACardState *card, uint32_t at) at -= s->attr_base; switch (at) { - case 0x00: /* Configuration Option Register */ + case 0x00: /* Configuration Option Register */ return s->opt; - case 0x02: /* Card Configuration Status Register */ + case 0x02: /* Card Configuration Status Register */ if (s->ctrl & CTRL_IEN) { return s->stat & ~STAT_INT; } else { return s->stat; } - case 0x04: /* Pin Replacement Register */ + case 0x04: /* Pin Replacement Register */ return (s->pins & PINS_CRDY) | 0x0c; - case 0x06: /* Socket and Copy Register */ + case 0x06: /* Socket and Copy Register */ return 0x00; #ifdef VERBOSE default: @@ -172,14 +173,14 @@ static void md_attr_write(PCMCIACardState *card, uint32_t at, uint8_t value) at -= s->attr_base; switch (at) { - case 0x00: /* Configuration Option Register */ + case 0x00: /* Configuration Option Register */ s->opt = value & 0xcf; if (value & OPT_SRESET) { - device_legacy_reset(DEVICE(s)); + device_cold_reset(DEVICE(s)); } md_interrupt_update(s); break; - case 0x02: /* Card Configuration Status Register */ + case 0x02: /* Card Configuration Status Register */ if ((s->stat ^ value) & STAT_PWRDWN) { s->pins |= PINS_CRDY; } @@ -188,11 +189,11 @@ static void md_attr_write(PCMCIACardState *card, uint32_t at, uint8_t value) md_interrupt_update(s); /* Word 170 in Identify Device must be equal to STAT_XE */ break; - case 0x04: /* Pin Replacement Register */ + case 0x04: /* Pin Replacement Register */ s->pins &= PINS_CRDY; s->pins |= value & PINS_MRDY; break; - case 0x06: /* Socket and Copy Register */ + case 0x06: /* Socket and Copy Register */ break; default: printf("%s: Bad attribute space register %02x\n", __func__, at); @@ -231,7 +232,7 @@ static uint16_t md_common_read(PCMCIACardState *card, uint32_t at) } switch (at) { - case 0x0: /* Even RD Data */ + case 0x0: /* Even RD Data */ case 0x8: return ide_data_readw(&s->bus, 0); @@ -244,19 +245,19 @@ static uint16_t md_common_read(PCMCIACardState *card, uint32_t at) } s->cycle = !s->cycle; return ret; - case 0x9: /* Odd RD Data */ + case 0x9: /* Odd RD Data */ return s->io >> 8; - case 0xd: /* Error */ + case 0xd: /* Error */ return ide_ioport_read(&s->bus, 0x1); - case 0xe: /* Alternate Status */ - ifs = idebus_active_if(&s->bus); + case 0xe: /* Alternate Status */ + ifs = ide_bus_active_if(&s->bus); if (ifs->blk) { return ifs->status; } else { return 0; } - case 0xf: /* Device Address */ - ifs = idebus_active_if(&s->bus); + case 0xf: /* Device Address */ + ifs = ide_bus_active_if(&s->bus); return 0xc2 | ((~ifs->select << 2) & 0x3c); default: return ide_ioport_read(&s->bus, at); @@ -295,7 +296,7 @@ static void md_common_write(PCMCIACardState *card, uint32_t at, uint16_t value) } switch (at) { - case 0x0: /* Even WR Data */ + case 0x0: /* Even WR Data */ case 0x8: ide_data_writew(&s->bus, 0, value); break; @@ -312,13 +313,13 @@ static void md_common_write(PCMCIACardState *card, uint32_t at, uint16_t value) s->io = value & 0xff; s->cycle = !s->cycle; break; - case 0xd: /* Features */ + case 0xd: /* Features */ ide_ioport_write(&s->bus, 0x1, value); break; - case 0xe: /* Device Control */ + case 0xe: /* Device Control */ s->ctrl = value; if (value & CTRL_SRST) { - device_legacy_reset(DEVICE(s)); + device_cold_reset(DEVICE(s)); } md_interrupt_update(s); break; @@ -335,7 +336,7 @@ static const VMStateDescription vmstate_microdrive = { .name = "microdrive", .version_id = 3, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(opt, MicroDriveState), VMSTATE_UINT8(stat, MicroDriveState), VMSTATE_UINT8(pins, MicroDriveState), @@ -349,35 +350,35 @@ static const VMStateDescription vmstate_microdrive = { }; static const uint8_t dscm1xxxx_cis[0x14a] = { - [0x000] = CISTPL_DEVICE, /* 5V Device Information */ - [0x002] = 0x03, /* Tuple length = 4 bytes */ - [0x004] = 0xdb, /* ID: DTYPE_FUNCSPEC, non WP, DSPEED_150NS */ - [0x006] = 0x01, /* Size = 2K bytes */ + [0x000] = CISTPL_DEVICE, /* 5V Device Information */ + [0x002] = 0x03, /* Tuple length = 4 bytes */ + [0x004] = 0xdb, /* ID: DTYPE_FUNCSPEC, non WP, DSPEED_150NS */ + [0x006] = 0x01, /* Size = 2K bytes */ [0x008] = CISTPL_ENDMARK, - [0x00a] = CISTPL_DEVICE_OC, /* Additional Device Information */ - [0x00c] = 0x04, /* Tuple length = 4 byest */ - [0x00e] = 0x03, /* Conditions: Ext = 0, Vcc 3.3V, MWAIT = 1 */ - [0x010] = 0xdb, /* ID: DTYPE_FUNCSPEC, non WP, DSPEED_150NS */ - [0x012] = 0x01, /* Size = 2K bytes */ + [0x00a] = CISTPL_DEVICE_OC, /* Additional Device Information */ + [0x00c] = 0x04, /* Tuple length = 4 byest */ + [0x00e] = 0x03, /* Conditions: Ext = 0, Vcc 3.3V, MWAIT = 1 */ + [0x010] = 0xdb, /* ID: DTYPE_FUNCSPEC, non WP, DSPEED_150NS */ + [0x012] = 0x01, /* Size = 2K bytes */ [0x014] = CISTPL_ENDMARK, - [0x016] = CISTPL_JEDEC_C, /* JEDEC ID */ - [0x018] = 0x02, /* Tuple length = 2 bytes */ - [0x01a] = 0xdf, /* PC Card ATA with no Vpp required */ + [0x016] = CISTPL_JEDEC_C, /* JEDEC ID */ + [0x018] = 0x02, /* Tuple length = 2 bytes */ + [0x01a] = 0xdf, /* PC Card ATA with no Vpp required */ [0x01c] = 0x01, - [0x01e] = CISTPL_MANFID, /* Manufacture ID */ - [0x020] = 0x04, /* Tuple length = 4 bytes */ - [0x022] = 0xa4, /* TPLMID_MANF = 00a4 (IBM) */ + [0x01e] = CISTPL_MANFID, /* Manufacture ID */ + [0x020] = 0x04, /* Tuple length = 4 bytes */ + [0x022] = 0xa4, /* TPLMID_MANF = 00a4 (IBM) */ [0x024] = 0x00, - [0x026] = 0x00, /* PLMID_CARD = 0000 */ + [0x026] = 0x00, /* PLMID_CARD = 0000 */ [0x028] = 0x00, - [0x02a] = CISTPL_VERS_1, /* Level 1 Version */ - [0x02c] = 0x12, /* Tuple length = 23 bytes */ - [0x02e] = 0x04, /* Major Version = JEIDA 4.2 / PCMCIA 2.1 */ - [0x030] = 0x01, /* Minor Version = 1 */ + [0x02a] = CISTPL_VERS_1, /* Level 1 Version */ + [0x02c] = 0x12, /* Tuple length = 23 bytes */ + [0x02e] = 0x04, /* Major Version = JEIDA 4.2 / PCMCIA 2.1 */ + [0x030] = 0x01, /* Minor Version = 1 */ [0x032] = 'I', [0x034] = 'B', [0x036] = 'M', @@ -395,142 +396,142 @@ static const uint8_t dscm1xxxx_cis[0x14a] = { [0x04e] = 0x00, [0x050] = CISTPL_ENDMARK, - [0x052] = CISTPL_FUNCID, /* Function ID */ - [0x054] = 0x02, /* Tuple length = 2 bytes */ - [0x056] = 0x04, /* TPLFID_FUNCTION = Fixed Disk */ - [0x058] = 0x01, /* TPLFID_SYSINIT: POST = 1, ROM = 0 */ + [0x052] = CISTPL_FUNCID, /* Function ID */ + [0x054] = 0x02, /* Tuple length = 2 bytes */ + [0x056] = 0x04, /* TPLFID_FUNCTION = Fixed Disk */ + [0x058] = 0x01, /* TPLFID_SYSINIT: POST = 1, ROM = 0 */ - [0x05a] = CISTPL_FUNCE, /* Function Extension */ - [0x05c] = 0x02, /* Tuple length = 2 bytes */ - [0x05e] = 0x01, /* TPLFE_TYPE = Disk Device Interface */ - [0x060] = 0x01, /* TPLFE_DATA = PC Card ATA Interface */ + [0x05a] = CISTPL_FUNCE, /* Function Extension */ + [0x05c] = 0x02, /* Tuple length = 2 bytes */ + [0x05e] = 0x01, /* TPLFE_TYPE = Disk Device Interface */ + [0x060] = 0x01, /* TPLFE_DATA = PC Card ATA Interface */ - [0x062] = CISTPL_FUNCE, /* Function Extension */ - [0x064] = 0x03, /* Tuple length = 3 bytes */ - [0x066] = 0x02, /* TPLFE_TYPE = Basic PC Card ATA Interface */ - [0x068] = 0x08, /* TPLFE_DATA: Rotating, Unique, Single */ - [0x06a] = 0x0f, /* TPLFE_DATA: Sleep, Standby, Idle, Auto */ + [0x062] = CISTPL_FUNCE, /* Function Extension */ + [0x064] = 0x03, /* Tuple length = 3 bytes */ + [0x066] = 0x02, /* TPLFE_TYPE = Basic PC Card ATA Interface */ + [0x068] = 0x08, /* TPLFE_DATA: Rotating, Unique, Single */ + [0x06a] = 0x0f, /* TPLFE_DATA: Sleep, Standby, Idle, Auto */ - [0x06c] = CISTPL_CONFIG, /* Configuration */ - [0x06e] = 0x05, /* Tuple length = 5 bytes */ - [0x070] = 0x01, /* TPCC_RASZ = 2 bytes, TPCC_RMSZ = 1 byte */ - [0x072] = 0x07, /* TPCC_LAST = 7 */ - [0x074] = 0x00, /* TPCC_RADR = 0200 */ + [0x06c] = CISTPL_CONFIG, /* Configuration */ + [0x06e] = 0x05, /* Tuple length = 5 bytes */ + [0x070] = 0x01, /* TPCC_RASZ = 2 bytes, TPCC_RMSZ = 1 byte */ + [0x072] = 0x07, /* TPCC_LAST = 7 */ + [0x074] = 0x00, /* TPCC_RADR = 0200 */ [0x076] = 0x02, - [0x078] = 0x0f, /* TPCC_RMSK = 200, 202, 204, 206 */ + [0x078] = 0x0f, /* TPCC_RMSK = 200, 202, 204, 206 */ - [0x07a] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x07c] = 0x0b, /* Tuple length = 11 bytes */ - [0x07e] = 0xc0, /* TPCE_INDX = Memory Mode, Default, Iface */ - [0x080] = 0xc0, /* TPCE_IF = Memory, no BVDs, no WP, READY */ - [0x082] = 0xa1, /* TPCE_FS = Vcc only, no I/O, Memory, Misc */ - [0x084] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ - [0x086] = 0x55, /* NomV: 5.0 V */ - [0x088] = 0x4d, /* MinV: 4.5 V */ - [0x08a] = 0x5d, /* MaxV: 5.5 V */ - [0x08c] = 0x4e, /* Peakl: 450 mA */ - [0x08e] = 0x08, /* TPCE_MS = 1 window, 1 byte, Host address */ - [0x090] = 0x00, /* Window descriptor: Window length = 0 */ - [0x092] = 0x20, /* TPCE_MI: support power down mode, RW */ + [0x07a] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x07c] = 0x0b, /* Tuple length = 11 bytes */ + [0x07e] = 0xc0, /* TPCE_INDX = Memory Mode, Default, Iface */ + [0x080] = 0xc0, /* TPCE_IF = Memory, no BVDs, no WP, READY */ + [0x082] = 0xa1, /* TPCE_FS = Vcc only, no I/O, Memory, Misc */ + [0x084] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ + [0x086] = 0x55, /* NomV: 5.0 V */ + [0x088] = 0x4d, /* MinV: 4.5 V */ + [0x08a] = 0x5d, /* MaxV: 5.5 V */ + [0x08c] = 0x4e, /* Peakl: 450 mA */ + [0x08e] = 0x08, /* TPCE_MS = 1 window, 1 byte, Host address */ + [0x090] = 0x00, /* Window descriptor: Window length = 0 */ + [0x092] = 0x20, /* TPCE_MI: support power down mode, RW */ - [0x094] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x096] = 0x06, /* Tuple length = 6 bytes */ - [0x098] = 0x00, /* TPCE_INDX = Memory Mode, no Default */ - [0x09a] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ - [0x09c] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ - [0x09e] = 0xb5, /* NomV: 3.3 V */ + [0x094] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x096] = 0x06, /* Tuple length = 6 bytes */ + [0x098] = 0x00, /* TPCE_INDX = Memory Mode, no Default */ + [0x09a] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ + [0x09c] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ + [0x09e] = 0xb5, /* NomV: 3.3 V */ [0x0a0] = 0x1e, - [0x0a2] = 0x3e, /* Peakl: 350 mA */ + [0x0a2] = 0x3e, /* Peakl: 350 mA */ - [0x0a4] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x0a6] = 0x0d, /* Tuple length = 13 bytes */ - [0x0a8] = 0xc1, /* TPCE_INDX = I/O and Memory Mode, Default */ - [0x0aa] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */ - [0x0ac] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */ - [0x0ae] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ - [0x0b0] = 0x55, /* NomV: 5.0 V */ - [0x0b2] = 0x4d, /* MinV: 4.5 V */ - [0x0b4] = 0x5d, /* MaxV: 5.5 V */ - [0x0b6] = 0x4e, /* Peakl: 450 mA */ - [0x0b8] = 0x64, /* TPCE_IO = 16-byte boundary, 16/8 accesses */ - [0x0ba] = 0xf0, /* TPCE_IR = MASK, Level, Pulse, Share */ - [0x0bc] = 0xff, /* IRQ0..IRQ7 supported */ - [0x0be] = 0xff, /* IRQ8..IRQ15 supported */ - [0x0c0] = 0x20, /* TPCE_MI = support power down mode */ + [0x0a4] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x0a6] = 0x0d, /* Tuple length = 13 bytes */ + [0x0a8] = 0xc1, /* TPCE_INDX = I/O and Memory Mode, Default */ + [0x0aa] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */ + [0x0ac] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */ + [0x0ae] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ + [0x0b0] = 0x55, /* NomV: 5.0 V */ + [0x0b2] = 0x4d, /* MinV: 4.5 V */ + [0x0b4] = 0x5d, /* MaxV: 5.5 V */ + [0x0b6] = 0x4e, /* Peakl: 450 mA */ + [0x0b8] = 0x64, /* TPCE_IO = 16-byte boundary, 16/8 accesses */ + [0x0ba] = 0xf0, /* TPCE_IR = MASK, Level, Pulse, Share */ + [0x0bc] = 0xff, /* IRQ0..IRQ7 supported */ + [0x0be] = 0xff, /* IRQ8..IRQ15 supported */ + [0x0c0] = 0x20, /* TPCE_MI = support power down mode */ - [0x0c2] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x0c4] = 0x06, /* Tuple length = 6 bytes */ - [0x0c6] = 0x01, /* TPCE_INDX = I/O and Memory Mode */ - [0x0c8] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ - [0x0ca] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ - [0x0cc] = 0xb5, /* NomV: 3.3 V */ + [0x0c2] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x0c4] = 0x06, /* Tuple length = 6 bytes */ + [0x0c6] = 0x01, /* TPCE_INDX = I/O and Memory Mode */ + [0x0c8] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ + [0x0ca] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ + [0x0cc] = 0xb5, /* NomV: 3.3 V */ [0x0ce] = 0x1e, - [0x0d0] = 0x3e, /* Peakl: 350 mA */ + [0x0d0] = 0x3e, /* Peakl: 350 mA */ - [0x0d2] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x0d4] = 0x12, /* Tuple length = 18 bytes */ - [0x0d6] = 0xc2, /* TPCE_INDX = I/O Primary Mode */ - [0x0d8] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */ - [0x0da] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */ - [0x0dc] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ - [0x0de] = 0x55, /* NomV: 5.0 V */ - [0x0e0] = 0x4d, /* MinV: 4.5 V */ - [0x0e2] = 0x5d, /* MaxV: 5.5 V */ - [0x0e4] = 0x4e, /* Peakl: 450 mA */ - [0x0e6] = 0xea, /* TPCE_IO = 1K boundary, 16/8 access, Range */ - [0x0e8] = 0x61, /* Range: 2 fields, 2 bytes addr, 1 byte len */ - [0x0ea] = 0xf0, /* Field 1 address = 0x01f0 */ + [0x0d2] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x0d4] = 0x12, /* Tuple length = 18 bytes */ + [0x0d6] = 0xc2, /* TPCE_INDX = I/O Primary Mode */ + [0x0d8] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */ + [0x0da] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */ + [0x0dc] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ + [0x0de] = 0x55, /* NomV: 5.0 V */ + [0x0e0] = 0x4d, /* MinV: 4.5 V */ + [0x0e2] = 0x5d, /* MaxV: 5.5 V */ + [0x0e4] = 0x4e, /* Peakl: 450 mA */ + [0x0e6] = 0xea, /* TPCE_IO = 1K boundary, 16/8 access, Range */ + [0x0e8] = 0x61, /* Range: 2 fields, 2 bytes addr, 1 byte len */ + [0x0ea] = 0xf0, /* Field 1 address = 0x01f0 */ [0x0ec] = 0x01, - [0x0ee] = 0x07, /* Address block length = 8 */ - [0x0f0] = 0xf6, /* Field 2 address = 0x03f6 */ + [0x0ee] = 0x07, /* Address block length = 8 */ + [0x0f0] = 0xf6, /* Field 2 address = 0x03f6 */ [0x0f2] = 0x03, - [0x0f4] = 0x01, /* Address block length = 2 */ - [0x0f6] = 0xee, /* TPCE_IR = IRQ E, Level, Pulse, Share */ - [0x0f8] = 0x20, /* TPCE_MI = support power down mode */ + [0x0f4] = 0x01, /* Address block length = 2 */ + [0x0f6] = 0xee, /* TPCE_IR = IRQ E, Level, Pulse, Share */ + [0x0f8] = 0x20, /* TPCE_MI = support power down mode */ - [0x0fa] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x0fc] = 0x06, /* Tuple length = 6 bytes */ - [0x0fe] = 0x02, /* TPCE_INDX = I/O Primary Mode, no Default */ - [0x100] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ - [0x102] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ - [0x104] = 0xb5, /* NomV: 3.3 V */ + [0x0fa] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x0fc] = 0x06, /* Tuple length = 6 bytes */ + [0x0fe] = 0x02, /* TPCE_INDX = I/O Primary Mode, no Default */ + [0x100] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ + [0x102] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ + [0x104] = 0xb5, /* NomV: 3.3 V */ [0x106] = 0x1e, - [0x108] = 0x3e, /* Peakl: 350 mA */ + [0x108] = 0x3e, /* Peakl: 350 mA */ - [0x10a] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x10c] = 0x12, /* Tuple length = 18 bytes */ - [0x10e] = 0xc3, /* TPCE_INDX = I/O Secondary Mode, Default */ - [0x110] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */ - [0x112] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */ - [0x114] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ - [0x116] = 0x55, /* NomV: 5.0 V */ - [0x118] = 0x4d, /* MinV: 4.5 V */ - [0x11a] = 0x5d, /* MaxV: 5.5 V */ - [0x11c] = 0x4e, /* Peakl: 450 mA */ - [0x11e] = 0xea, /* TPCE_IO = 1K boundary, 16/8 access, Range */ - [0x120] = 0x61, /* Range: 2 fields, 2 byte addr, 1 byte len */ - [0x122] = 0x70, /* Field 1 address = 0x0170 */ + [0x10a] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x10c] = 0x12, /* Tuple length = 18 bytes */ + [0x10e] = 0xc3, /* TPCE_INDX = I/O Secondary Mode, Default */ + [0x110] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */ + [0x112] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */ + [0x114] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ + [0x116] = 0x55, /* NomV: 5.0 V */ + [0x118] = 0x4d, /* MinV: 4.5 V */ + [0x11a] = 0x5d, /* MaxV: 5.5 V */ + [0x11c] = 0x4e, /* Peakl: 450 mA */ + [0x11e] = 0xea, /* TPCE_IO = 1K boundary, 16/8 access, Range */ + [0x120] = 0x61, /* Range: 2 fields, 2 byte addr, 1 byte len */ + [0x122] = 0x70, /* Field 1 address = 0x0170 */ [0x124] = 0x01, - [0x126] = 0x07, /* Address block length = 8 */ - [0x128] = 0x76, /* Field 2 address = 0x0376 */ + [0x126] = 0x07, /* Address block length = 8 */ + [0x128] = 0x76, /* Field 2 address = 0x0376 */ [0x12a] = 0x03, - [0x12c] = 0x01, /* Address block length = 2 */ - [0x12e] = 0xee, /* TPCE_IR = IRQ E, Level, Pulse, Share */ - [0x130] = 0x20, /* TPCE_MI = support power down mode */ + [0x12c] = 0x01, /* Address block length = 2 */ + [0x12e] = 0xee, /* TPCE_IR = IRQ E, Level, Pulse, Share */ + [0x130] = 0x20, /* TPCE_MI = support power down mode */ - [0x132] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x134] = 0x06, /* Tuple length = 6 bytes */ - [0x136] = 0x03, /* TPCE_INDX = I/O Secondary Mode */ - [0x138] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ - [0x13a] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ - [0x13c] = 0xb5, /* NomV: 3.3 V */ + [0x132] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ + [0x134] = 0x06, /* Tuple length = 6 bytes */ + [0x136] = 0x03, /* TPCE_INDX = I/O Secondary Mode */ + [0x138] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ + [0x13a] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ + [0x13c] = 0xb5, /* NomV: 3.3 V */ [0x13e] = 0x1e, - [0x140] = 0x3e, /* Peakl: 350 mA */ + [0x140] = 0x3e, /* Peakl: 350 mA */ - [0x142] = CISTPL_NO_LINK, /* No Link */ - [0x144] = 0x00, /* Tuple length = 0 bytes */ + [0x142] = CISTPL_NO_LINK, /* No Link */ + [0x144] = 0x00, /* Tuple length = 0 bytes */ - [0x146] = CISTPL_END, /* Tuple End */ + [0x146] = CISTPL_END, /* Tuple End */ }; #define TYPE_DSCM1XXXX "dscm1xxxx" @@ -543,7 +544,7 @@ static int dscm1xxxx_attach(PCMCIACardState *card) md->attr_base = pcc->cis[0x74] | (pcc->cis[0x76] << 8); md->io_base = 0x0; - device_legacy_reset(DEVICE(md)); + device_cold_reset(DEVICE(md)); md_interrupt_update(md); return 0; @@ -553,7 +554,7 @@ static int dscm1xxxx_detach(PCMCIACardState *card) { MicroDriveState *md = MICRODRIVE(card); - device_legacy_reset(DEVICE(md)); + device_cold_reset(DEVICE(md)); return 0; } @@ -565,7 +566,7 @@ PCMCIACardState *dscm1xxxx_init(DriveInfo *dinfo) qdev_realize(DEVICE(md), NULL, &error_fatal); if (dinfo != NULL) { - ide_create_drive(&md->bus, 0, dinfo); + ide_bus_create_drive(&md->bus, 0, dinfo); } md->bus.ifs[0].drive_kind = IDE_CFATA; md->bus.ifs[0].mdata_size = METADATA_SIZE; @@ -598,7 +599,7 @@ static void microdrive_realize(DeviceState *dev, Error **errp) { MicroDriveState *md = MICRODRIVE(dev); - ide_init2(&md->bus, qemu_allocate_irq(md_set_irq, md, 0)); + ide_bus_init_output_irq(&md->bus, qemu_allocate_irq(md_set_irq, md, 0)); } static void microdrive_init(Object *obj) diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index fb2ebd4847..8736281305 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -29,9 +29,9 @@ #include "qemu/module.h" #include "sysemu/dma.h" -#include "hw/ide/internal.h" +#include "hw/ide/mmio.h" #include "hw/qdev-properties.h" -#include "qom/object.h" +#include "ide-internal.h" /***********************************************************/ /* MMIO based ide port @@ -39,11 +39,6 @@ * dedicated ide controller, which is often seen on embedded boards. */ -#define TYPE_MMIO_IDE "mmio-ide" -typedef struct MMIOIDEState MMIOState; -DECLARE_INSTANCE_CHECKER(MMIOState, MMIO_IDE, - TYPE_MMIO_IDE) - struct MMIOIDEState { /*< private >*/ SysBusDevice parent_obj; @@ -58,7 +53,7 @@ struct MMIOIDEState { static void mmio_ide_reset(DeviceState *dev) { - MMIOState *s = MMIO_IDE(dev); + MMIOIDEState *s = MMIO_IDE(dev); ide_bus_reset(&s->bus); } @@ -66,7 +61,7 @@ static void mmio_ide_reset(DeviceState *dev) static uint64_t mmio_ide_read(void *opaque, hwaddr addr, unsigned size) { - MMIOState *s = opaque; + MMIOIDEState *s = opaque; addr >>= s->shift; if (addr & 7) return ide_ioport_read(&s->bus, addr); @@ -77,7 +72,7 @@ static uint64_t mmio_ide_read(void *opaque, hwaddr addr, static void mmio_ide_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - MMIOState *s = opaque; + MMIOIDEState *s = opaque; addr >>= s->shift; if (addr & 7) ide_ioport_write(&s->bus, addr, val); @@ -94,14 +89,14 @@ static const MemoryRegionOps mmio_ide_ops = { static uint64_t mmio_ide_status_read(void *opaque, hwaddr addr, unsigned size) { - MMIOState *s= opaque; + MMIOIDEState *s = opaque; return ide_status_read(&s->bus, 0); } static void mmio_ide_ctrl_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - MMIOState *s = opaque; + MMIOIDEState *s = opaque; ide_ctrl_write(&s->bus, 0, val); } @@ -115,9 +110,9 @@ static const VMStateDescription vmstate_ide_mmio = { .name = "mmio-ide", .version_id = 3, .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_IDE_BUS(bus, MMIOState), - VMSTATE_IDE_DRIVES(bus.ifs, MMIOState), + .fields = (const VMStateField[]) { + VMSTATE_IDE_BUS(bus, MMIOIDEState), + VMSTATE_IDE_DRIVES(bus.ifs, MMIOIDEState), VMSTATE_END_OF_LIST() } }; @@ -125,9 +120,9 @@ static const VMStateDescription vmstate_ide_mmio = { static void mmio_ide_realizefn(DeviceState *dev, Error **errp) { SysBusDevice *d = SYS_BUS_DEVICE(dev); - MMIOState *s = MMIO_IDE(dev); + MMIOIDEState *s = MMIO_IDE(dev); - ide_init2(&s->bus, s->irq); + ide_bus_init_output_irq(&s->bus, s->irq); memory_region_init_io(&s->iomem1, OBJECT(s), &mmio_ide_ops, s, "ide-mmio.1", 16 << s->shift); @@ -140,14 +135,14 @@ static void mmio_ide_realizefn(DeviceState *dev, Error **errp) static void mmio_ide_initfn(Object *obj) { SysBusDevice *d = SYS_BUS_DEVICE(obj); - MMIOState *s = MMIO_IDE(obj); + MMIOIDEState *s = MMIO_IDE(obj); ide_bus_init(&s->bus, sizeof(s->bus), DEVICE(obj), 0, 2); sysbus_init_irq(d, &s->irq); } static Property mmio_ide_properties[] = { - DEFINE_PROP_UINT32("shift", MMIOState, shift, 0), + DEFINE_PROP_UINT32("shift", MMIOIDEState, shift, 0), DEFINE_PROP_END_OF_LIST() }; @@ -164,7 +159,7 @@ static void mmio_ide_class_init(ObjectClass *oc, void *data) static const TypeInfo mmio_ide_type_info = { .name = TYPE_MMIO_IDE, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MMIOState), + .instance_size = sizeof(MMIOIDEState), .instance_init = mmio_ide_initfn, .class_init = mmio_ide_class_init, }; @@ -176,13 +171,13 @@ static void mmio_ide_register_types(void) void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1) { - MMIOState *s = MMIO_IDE(dev); + MMIOIDEState *s = MMIO_IDE(dev); if (hd0 != NULL) { - ide_create_drive(&s->bus, 0, hd0); + ide_bus_create_drive(&s->bus, 0, hd0); } if (hd1 != NULL) { - ide_create_drive(&s->bus, 1, hd1); + ide_bus_create_drive(&s->bus, 1, hd1); } } diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 84ba733548..4675d079a1 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -24,12 +24,14 @@ */ #include "qemu/osdep.h" +#include "hw/irq.h" #include "hw/pci/pci.h" #include "migration/vmstate.h" #include "sysemu/dma.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "hw/ide/pci.h" +#include "ide-internal.h" #include "trace.h" #define BMDMA_PAGE_SIZE 4096 @@ -103,6 +105,96 @@ const MemoryRegionOps pci_ide_data_le_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +void pci_ide_update_mode(PCIIDEState *s) +{ + PCIDevice *d = PCI_DEVICE(s); + uint8_t mode = d->config[PCI_CLASS_PROG]; + + /* + * This function only configures the BARs/ioports for now: PCI IDE + * controllers must manage their own IRQ routing + */ + + switch (mode & 0xf) { + case 0xa: + /* Both channels legacy mode */ + + /* + * TODO: according to the PCI IDE specification the BARs should + * be completely disabled, however Linux for the pegasos2 + * machine stil accesses the BAR addresses after switching to legacy + * mode. Hence we leave them active for now. + */ + + /* Clear interrupt pin */ + pci_config_set_interrupt_pin(d->config, 0); + + /* Add legacy IDE ports */ + if (!s->bus[0].portio_list.owner) { + portio_list_init(&s->bus[0].portio_list, OBJECT(d), + ide_portio_list, &s->bus[0], "ide"); + portio_list_add(&s->bus[0].portio_list, + pci_address_space_io(d), 0x1f0); + } + + if (!s->bus[0].portio2_list.owner) { + portio_list_init(&s->bus[0].portio2_list, OBJECT(d), + ide_portio2_list, &s->bus[0], "ide"); + portio_list_add(&s->bus[0].portio2_list, + pci_address_space_io(d), 0x3f6); + } + + if (!s->bus[1].portio_list.owner) { + portio_list_init(&s->bus[1].portio_list, OBJECT(d), + ide_portio_list, &s->bus[1], "ide"); + portio_list_add(&s->bus[1].portio_list, + pci_address_space_io(d), 0x170); + } + + if (!s->bus[1].portio2_list.owner) { + portio_list_init(&s->bus[1].portio2_list, OBJECT(d), + ide_portio2_list, &s->bus[1], "ide"); + portio_list_add(&s->bus[1].portio2_list, + pci_address_space_io(d), 0x376); + } + break; + + case 0xf: + /* Both channels native mode */ + + /* Set interrupt pin */ + pci_config_set_interrupt_pin(d->config, 1); + + /* Remove legacy IDE ports */ + if (s->bus[0].portio_list.owner) { + portio_list_del(&s->bus[0].portio_list); + portio_list_destroy(&s->bus[0].portio_list); + } + + if (s->bus[0].portio2_list.owner) { + portio_list_del(&s->bus[0].portio2_list); + portio_list_destroy(&s->bus[0].portio2_list); + } + + if (s->bus[1].portio_list.owner) { + portio_list_del(&s->bus[1].portio_list); + portio_list_destroy(&s->bus[1].portio_list); + } + + if (s->bus[1].portio2_list.owner) { + portio_list_del(&s->bus[1].portio2_list); + portio_list_destroy(&s->bus[1].portio2_list); + } + break; + } +} + +static IDEState *bmdma_active_if(BMDMAState *bmdma) +{ + assert(bmdma->bus->retry_unit != (uint8_t)-1); + return bmdma->bus->ifs + bmdma->bus->retry_unit; +} + static void bmdma_start_dma(const IDEDMA *dma, IDEState *s, BlockCompletionFunc *dma_cb) { @@ -295,7 +387,7 @@ void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val) /* Ignore writes to SSBM if it keeps the old value */ if ((val & BM_CMD_START) != (bm->cmd & BM_CMD_START)) { if (!(val & BM_CMD_START)) { - ide_cancel_dma_sync(idebus_active_if(bm->bus)); + ide_cancel_dma_sync(ide_bus_active_if(bm->bus)); bm->status &= ~BM_STATUS_DMAING; } else { bm->cur_addr = bm->addr; @@ -311,6 +403,12 @@ void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val) bm->cmd = val & 0x09; } +void bmdma_status_writeb(BMDMAState *bm, uint32_t val) +{ + bm->status = (val & 0x60) | (bm->status & BM_STATUS_DMAING) + | (bm->status & ~val & (BM_STATUS_ERROR | BM_STATUS_INT)); +} + static uint64_t bmdma_addr_read(void *opaque, hwaddr addr, unsigned width) { @@ -404,7 +502,7 @@ static const VMStateDescription vmstate_bmdma_current = { .version_id = 1, .minimum_version_id = 1, .needed = ide_bmdma_current_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(cur_addr, BMDMAState), VMSTATE_UINT32(cur_prd_last, BMDMAState), VMSTATE_UINT32(cur_prd_addr, BMDMAState), @@ -418,7 +516,7 @@ static const VMStateDescription vmstate_bmdma_status = { .version_id = 1, .minimum_version_id = 1, .needed = ide_bmdma_status_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(status, BMDMAState), VMSTATE_END_OF_LIST() } @@ -429,7 +527,7 @@ static const VMStateDescription vmstate_bmdma = { .version_id = 3, .minimum_version_id = 0, .pre_save = ide_bmdma_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(cmd, BMDMAState), VMSTATE_UINT8(migration_compat_status, BMDMAState), VMSTATE_UINT32(addr, BMDMAState), @@ -438,7 +536,7 @@ static const VMStateDescription vmstate_bmdma = { VMSTATE_UINT8(migration_retry_unit, BMDMAState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_bmdma_current, &vmstate_bmdma_status, NULL @@ -465,7 +563,7 @@ const VMStateDescription vmstate_ide_pci = { .version_id = 3, .minimum_version_id = 0, .post_load = ide_pci_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PCIIDEState), VMSTATE_STRUCT_ARRAY(bmdma, PCIIDEState, 2, 0, vmstate_bmdma, BMDMAState), @@ -488,7 +586,7 @@ void pci_ide_create_devs(PCIDevice *dev) ide_drive_get(hd_table, ARRAY_SIZE(hd_table)); for (i = 0; i < 4; i++) { if (hd_table[i]) { - ide_create_drive(d->bus + bus[i], unit[i], hd_table[i]); + ide_bus_create_drive(d->bus + bus[i], unit[i], hd_table[i]); } } } @@ -512,13 +610,23 @@ void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d) bus->dma = &bm->dma; bm->irq = bus->irq; bus->irq = qemu_allocate_irq(bmdma_irq, bm, 0); + bm->bus = bus; bm->pci_dev = d; } +static void pci_ide_init(Object *obj) +{ + PCIIDEState *d = PCI_IDE(obj); + + qdev_init_gpio_out_named(DEVICE(d), d->isa_irq, "isa-irq", + ARRAY_SIZE(d->isa_irq)); +} + static const TypeInfo pci_ide_type_info = { .name = TYPE_PCI_IDE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIIDEState), + .instance_init = pci_ide_init, .abstract = true, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, diff --git a/hw/ide/piix.c b/hw/ide/piix.c index 9a9b28078e..80efc633d3 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -21,18 +21,18 @@ * 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. + * + * References: + * [1] 82371FB (PIIX) AND 82371SB (PIIX3) PCI ISA IDE XCELERATOR, + * 290550-002, Intel Corporation, April 1997. */ #include "qemu/osdep.h" -#include "hw/pci/pci.h" -#include "migration/vmstate.h" #include "qapi/error.h" -#include "qemu/module.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/dma.h" - +#include "hw/pci/pci.h" +#include "hw/ide/piix.h" #include "hw/ide/pci.h" +#include "ide-internal.h" #include "trace.h" static uint64_t bmdma_read(void *opaque, hwaddr addr, unsigned size) @@ -76,7 +76,7 @@ static void bmdma_write(void *opaque, hwaddr addr, bmdma_cmd_writeb(bm, val); break; case 2: - bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06); + bmdma_status_writeb(bm, val); break; } } @@ -114,17 +114,14 @@ static void piix_ide_reset(DeviceState *dev) ide_bus_reset(&d->bus[i]); } - /* TODO: this is the default. do not override. */ - pci_conf[PCI_COMMAND] = 0x00; - /* TODO: this is the default. do not override. */ - pci_conf[PCI_COMMAND + 1] = 0x00; - /* TODO: use pci_set_word */ - pci_conf[PCI_STATUS] = PCI_STATUS_FAST_BACK; - pci_conf[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_MEDIUM >> 8; - pci_conf[0x20] = 0x01; /* BMIBA: 20-23h */ + /* PCI command register default value (0000h) per [1, p.48]. */ + pci_set_word(pci_conf + PCI_COMMAND, 0x0000); + pci_set_word(pci_conf + PCI_STATUS, + PCI_STATUS_DEVSEL_MEDIUM | PCI_STATUS_FAST_BACK); + pci_set_long(pci_conf + 0x20, 0x1); /* BMIBA: 20-23h */ } -static int pci_piix_init_ports(PCIIDEState *d) +static bool pci_piix_init_bus(PCIIDEState *d, unsigned i, Error **errp) { static const struct { int iobase; @@ -134,42 +131,38 @@ static int pci_piix_init_ports(PCIIDEState *d) {0x1f0, 0x3f6, 14}, {0x170, 0x376, 15}, }; - int i, ret; + int ret; - for (i = 0; i < 2; i++) { - ide_bus_init(&d->bus[i], sizeof(d->bus[i]), DEVICE(d), i, 2); - ret = ide_init_ioport(&d->bus[i], NULL, port_info[i].iobase, - port_info[i].iobase2); - if (ret) { - return ret; - } - ide_init2(&d->bus[i], isa_get_irq(NULL, port_info[i].isairq)); - - bmdma_init(&d->bus[i], &d->bmdma[i], d); - d->bmdma[i].bus = &d->bus[i]; - ide_register_restart_cb(&d->bus[i]); + ide_bus_init(&d->bus[i], sizeof(d->bus[i]), DEVICE(d), i, 2); + ret = ide_init_ioport(&d->bus[i], NULL, port_info[i].iobase, + port_info[i].iobase2); + if (ret) { + error_setg_errno(errp, -ret, "Failed to realize %s port %u", + object_get_typename(OBJECT(d)), i); + return false; } + ide_bus_init_output_irq(&d->bus[i], isa_get_irq(NULL, port_info[i].isairq)); - return 0; + bmdma_init(&d->bus[i], &d->bmdma[i], d); + ide_bus_register_restart_cb(&d->bus[i]); + + return true; } static void pci_piix_ide_realize(PCIDevice *dev, Error **errp) { PCIIDEState *d = PCI_IDE(dev); uint8_t *pci_conf = dev->config; - int rc; pci_conf[PCI_CLASS_PROG] = 0x80; // legacy ATA mode bmdma_setup_bar(d); pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); - vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_pci, d); - - rc = pci_piix_init_ports(d); - if (rc) { - error_setg_errno(errp, -rc, "Failed to realize %s", - object_get_typename(OBJECT(dev))); + for (unsigned i = 0; i < 2; i++) { + if (!pci_piix_init_bus(d, i, errp)) { + return; + } } } @@ -191,6 +184,7 @@ static void piix3_ide_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); dc->reset = piix_ide_reset; + dc->vmsd = &vmstate_ide_pci; k->realize = pci_piix_ide_realize; k->exit = pci_piix_ide_exitfn; k->vendor_id = PCI_VENDOR_ID_INTEL; @@ -201,7 +195,7 @@ static void piix3_ide_class_init(ObjectClass *klass, void *data) } static const TypeInfo piix3_ide_info = { - .name = "piix3-ide", + .name = TYPE_PIIX3_IDE, .parent = TYPE_PCI_IDE, .class_init = piix3_ide_class_init, }; @@ -213,6 +207,7 @@ static void piix4_ide_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); dc->reset = piix_ide_reset; + dc->vmsd = &vmstate_ide_pci; k->realize = pci_piix_ide_realize; k->exit = pci_piix_ide_exitfn; k->vendor_id = PCI_VENDOR_ID_INTEL; @@ -223,7 +218,7 @@ static void piix4_ide_class_init(ObjectClass *klass, void *data) } static const TypeInfo piix4_ide_info = { - .name = "piix4-ide", + .name = TYPE_PIIX4_IDE, .parent = TYPE_PCI_IDE, .class_init = piix4_ide_class_init, }; diff --git a/hw/ide/sii3112.c b/hw/ide/sii3112.c index 46204f10d7..af17384ff2 100644 --- a/hw/ide/sii3112.c +++ b/hw/ide/sii3112.c @@ -17,6 +17,7 @@ #include "qemu/module.h" #include "trace.h" #include "qom/object.h" +#include "ide-internal.h" #define TYPE_SII3112_PCI "sii3112" OBJECT_DECLARE_SIMPLE_TYPE(SiI3112PCIState, SII3112_PCI) @@ -149,8 +150,7 @@ static void sii3112_reg_write(void *opaque, hwaddr addr, break; case 0x02: case 0x12: - d->i.bmdma[0].status = (val & 0x60) | (d->i.bmdma[0].status & 1) | - (d->i.bmdma[0].status & ~val & 6); + bmdma_status_writeb(&d->i.bmdma[0], val); break; case 0x04 ... 0x07: bmdma_addr_ioport_ops.write(&d->i.bmdma[0], addr - 4, val, size); @@ -165,8 +165,7 @@ static void sii3112_reg_write(void *opaque, hwaddr addr, break; case 0x0a: case 0x1a: - d->i.bmdma[1].status = (val & 0x60) | (d->i.bmdma[1].status & 1) | - (d->i.bmdma[1].status & ~val & 6); + bmdma_status_writeb(&d->i.bmdma[1], val); break; case 0x0c ... 0x0f: bmdma_addr_ioport_ops.write(&d->i.bmdma[1], addr - 12, val, size); @@ -284,11 +283,10 @@ static void sii3112_pci_realize(PCIDevice *dev, Error **errp) qdev_init_gpio_in(ds, sii3112_set_irq, 2); for (i = 0; i < 2; i++) { ide_bus_init(&s->bus[i], sizeof(s->bus[i]), ds, i, 1); - ide_init2(&s->bus[i], qdev_get_gpio_in(ds, i)); + ide_bus_init_output_irq(&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]; - ide_register_restart_cb(&s->bus[i]); + ide_bus_register_restart_cb(&s->bus[i]); } } diff --git a/hw/ide/trace-events b/hw/ide/trace-events index 15d7921f15..57042cafdd 100644 --- a/hw/ide/trace-events +++ b/hw/ide/trace-events @@ -12,7 +12,7 @@ ide_data_writew(uint32_t addr, uint32_t val, void *bus, void *s) ide_data_readl(uint32_t addr, uint32_t val, void *bus, void *s) "IDE PIO rd @ 0x%"PRIx32" (Data: Long); val 0x%08"PRIx32"; bus %p; IDEState %p" ide_data_writel(uint32_t addr, uint32_t val, void *bus, void *s) "IDE PIO wr @ 0x%"PRIx32" (Data: Long); val 0x%08"PRIx32"; bus %p; IDEState %p" # misc -ide_exec_cmd(void *bus, void *state, uint32_t cmd) "IDE exec cmd: bus %p; state %p; cmd 0x%02x" +ide_bus_exec_cmd(void *bus, void *state, uint32_t cmd) "IDE exec cmd: bus %p; state %p; cmd 0x%02x" ide_cancel_dma_sync_buffered(void *fn, void *req) "invoking cb %p of buffered request %p with -ECANCELED" ide_cancel_dma_sync_remaining(void) "draining all remaining requests" ide_sector_read(int64_t sector_num, int nsectors) "sector=%"PRId64" nsectors=%d" @@ -91,6 +91,7 @@ ahci_populate_sglist_short_map(void *s, int port) "ahci(%p)[%d]: mapped less tha ahci_populate_sglist_bad_offset(void *s, int port, int off_idx, int64_t off_pos) "ahci(%p)[%d]: Incorrect offset! off_idx: %d, off_pos: %"PRId64 ncq_finish(void *s, int port, uint8_t tag) "ahci(%p)[%d][tag:%d]: NCQ transfer finished" execute_ncq_command_read(void *s, int port, uint8_t tag, int count, int64_t lba) "ahci(%p)[%d][tag:%d]: NCQ reading %d sectors from LBA %"PRId64 +execute_ncq_command_write(void *s, int port, uint8_t tag, int count, int64_t lba) "ahci(%p)[%d][tag:%d]: NCQ writing %d sectors to LBA %"PRId64 execute_ncq_command_unsup(void *s, int port, uint8_t tag, uint8_t cmd) "ahci(%p)[%d][tag:%d]: error: unsupported NCQ command (0x%02x) received" process_ncq_command_mismatch(void *s, int port, uint8_t tag, uint8_t slot) "ahci(%p)[%d][tag:%d]: Warning: NCQ slot (%d) did not match the given tag" process_ncq_command_aux(void *s, int port, uint8_t tag) "ahci(%p)[%d][tag:%d]: Warn: Attempt to use NCQ auxiliary fields" diff --git a/hw/ide/via.c b/hw/ide/via.c index 82def819c4..a32f56b0e7 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -28,9 +28,12 @@ #include "hw/pci/pci.h" #include "migration/vmstate.h" #include "qemu/module.h" +#include "qemu/range.h" #include "sysemu/dma.h" #include "hw/isa/vt82c686.h" #include "hw/ide/pci.h" +#include "hw/irq.h" +#include "ide-internal.h" #include "trace.h" static uint64_t bmdma_read(void *opaque, hwaddr addr, @@ -74,7 +77,7 @@ static void bmdma_write(void *opaque, hwaddr addr, bmdma_cmd_writeb(bm, val); break; case 2: - bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06); + bmdma_status_writeb(bm, val); break; default:; } @@ -90,7 +93,7 @@ static void bmdma_setup_bar(PCIIDEState *d) int i; memory_region_init(&d->bmdma_bar, OBJECT(d), "via-bmdma-container", 16); - for(i = 0;i < 2; i++) { + for (i = 0; i < ARRAY_SIZE(d->bmdma); i++) { BMDMAState *bm = &d->bmdma[i]; memory_region_init_io(&bm->extra_io, OBJECT(d), &via_bmdma_ops, bm, @@ -104,7 +107,8 @@ static void bmdma_setup_bar(PCIIDEState *d) static void via_ide_set_irq(void *opaque, int n, int level) { - PCIDevice *d = PCI_DEVICE(opaque); + PCIIDEState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); if (level) { d->config[0x70 + n * 8] |= 0x80; @@ -112,7 +116,7 @@ static void via_ide_set_irq(void *opaque, int n, int level) d->config[0x70 + n * 8] &= ~0x80; } - via_isa_set_irq(pci_get_function_0(d), 14 + n, level); + qemu_set_irq(s->isa_irq[n], level); } static void via_ide_reset(DeviceState *dev) @@ -122,20 +126,18 @@ static void via_ide_reset(DeviceState *dev) uint8_t *pci_conf = pd->config; int i; - for (i = 0; i < 2; i++) { + for (i = 0; i < ARRAY_SIZE(d->bus); i++) { ide_bus_reset(&d->bus[i]); } + pci_config_set_prog_interface(pci_conf, 0x8a); /* legacy mode */ + pci_ide_update_mode(d); + pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_WAIT); pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); - pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x000001f0); - pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x000003f4); - pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170); - pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374); - pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 20-23h */ - pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e); + pci_set_byte(pci_conf + PCI_INTERRUPT_LINE, 0xe); /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/ pci_set_long(pci_conf + 0x40, 0x0a090600); @@ -157,6 +159,41 @@ static void via_ide_reset(DeviceState *dev) pci_set_long(pci_conf + 0xc0, 0x00020001); } +static uint32_t via_ide_cfg_read(PCIDevice *pd, uint32_t addr, int len) +{ + uint32_t val = pci_default_read_config(pd, addr, len); + uint8_t mode = pd->config[PCI_CLASS_PROG]; + + if ((mode & 0xf) == 0xa) { + if (ranges_overlap(addr, len, PCI_BASE_ADDRESS_0, 16)) { + /* BARs 0-3 always read back zero in legacy mode */ + for (int i = addr; i < addr + len; i++) { + if (i >= PCI_BASE_ADDRESS_0 && i < PCI_BASE_ADDRESS_0 + 16) { + val &= ~(0xffULL << ((i - addr) << 3)); + } + } + } + if (addr == PCI_BASE_ADDRESS_4 && val == PCI_BASE_ADDRESS_SPACE_IO) { + /* BAR4 default value if unset */ + val = 0xcc00 | PCI_BASE_ADDRESS_SPACE_IO; + } + } + + return val; +} + +static void via_ide_cfg_write(PCIDevice *pd, uint32_t addr, + uint32_t val, int len) +{ + PCIIDEState *d = PCI_IDE(pd); + + pci_default_write_config(pd, addr, val, len); + + if (range_covers_byte(addr, len, PCI_CLASS_PROG)) { + pci_ide_update_mode(d); + } +} + static void via_ide_realize(PCIDevice *dev, Error **errp) { PCIIDEState *d = PCI_IDE(dev); @@ -164,7 +201,6 @@ static void via_ide_realize(PCIDevice *dev, Error **errp) uint8_t *pci_conf = dev->config; int i; - pci_config_set_prog_interface(pci_conf, 0x8a); /* legacy mode */ pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0); dev->wmask[PCI_INTERRUPT_LINE] = 0; dev->wmask[PCI_CLASS_PROG] = 5; @@ -188,14 +224,13 @@ 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); - qdev_init_gpio_in(ds, via_ide_set_irq, 2); - for (i = 0; i < 2; i++) { - ide_bus_init(&d->bus[i], sizeof(d->bus[i]), ds, i, 2); - ide_init2(&d->bus[i], qdev_get_gpio_in(ds, i)); + qdev_init_gpio_in(ds, via_ide_set_irq, ARRAY_SIZE(d->bus)); + for (i = 0; i < ARRAY_SIZE(d->bus); i++) { + ide_bus_init(&d->bus[i], sizeof(d->bus[i]), ds, i, MAX_IDE_DEVS); + ide_bus_init_output_irq(&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]); + ide_bus_register_restart_cb(&d->bus[i]); } } @@ -204,7 +239,7 @@ static void via_ide_exitfn(PCIDevice *dev) PCIIDEState *d = PCI_IDE(dev); unsigned i; - for (i = 0; i < 2; ++i) { + for (i = 0; i < ARRAY_SIZE(d->bmdma); ++i) { memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].extra_io); memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].addr_ioport); } @@ -220,6 +255,8 @@ static void via_ide_class_init(ObjectClass *klass, void *data) /* Reason: only works as function of VIA southbridge */ dc->user_creatable = false; + k->config_read = via_ide_cfg_read; + k->config_write = via_ide_cfg_write; k->realize = via_ide_realize; k->exit = via_ide_exitfn; k->vendor_id = PCI_VENDOR_ID_VIA; @@ -230,7 +267,7 @@ static void via_ide_class_init(ObjectClass *klass, void *data) } static const TypeInfo via_ide_info = { - .name = "via-ide", + .name = TYPE_VIA_IDE, .parent = TYPE_PCI_IDE, .class_init = via_ide_class_init, }; diff --git a/hw/input/Kconfig b/hw/input/Kconfig index 55865bb386..f86e98c829 100644 --- a/hw/input/Kconfig +++ b/hw/input/Kconfig @@ -20,7 +20,7 @@ config PL050 config PS2 bool -config STELLARIS_INPUT +config STELLARIS_GAMEPAD bool config TSC2005 diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index a9088c910c..758fa6d267 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -332,7 +332,7 @@ static const VMStateDescription vmstate_adb_kbd = { .name = "adb_kbd", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(parent_obj, KBDState, 0, vmstate_adb_device, ADBDevice), VMSTATE_BUFFER(data, KBDState), VMSTATE_INT32(rptr, KBDState), @@ -355,7 +355,7 @@ static void adb_kbd_reset(DeviceState *dev) s->count = 0; } -static QemuInputHandler adb_keyboard_handler = { +static const QemuInputHandler adb_keyboard_handler = { .name = "QEMU ADB Keyboard", .mask = INPUT_EVENT_MASK_KEY, .event = adb_keyboard_event, diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index e6b341f028..144a0ccce7 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -217,7 +217,7 @@ static const VMStateDescription vmstate_adb_mouse = { .name = "adb_mouse", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(parent_obj, MouseState, 0, vmstate_adb_device, ADBDevice), VMSTATE_INT32(buttons_state, MouseState), diff --git a/hw/input/adb.c b/hw/input/adb.c index 84331b9fce..98f39b4281 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -43,7 +43,7 @@ static const char *adb_commands[] = { static void adb_device_reset(ADBDevice *d) { - qdev_reset_all(DEVICE(d)); + device_cold_reset(DEVICE(d)); } static int do_adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, @@ -221,7 +221,7 @@ static const VMStateDescription vmstate_adb_bus = { .name = "adb_bus", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(autopoll_timer, ADBBusState), VMSTATE_BOOL(autopoll_enabled, ADBBusState), VMSTATE_UINT8(autopoll_rate_ms, ADBBusState), @@ -231,9 +231,9 @@ static const VMStateDescription vmstate_adb_bus = { } }; -static void adb_bus_reset(BusState *qbus) +static void adb_bus_reset_hold(Object *obj) { - ADBBusState *adb_bus = ADB_BUS(qbus); + ADBBusState *adb_bus = ADB_BUS(obj); adb_bus->autopoll_enabled = false; adb_bus->autopoll_mask = 0xffff; @@ -247,7 +247,7 @@ static void adb_bus_realize(BusState *qbus, Error **errp) adb_bus->autopoll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, adb_autopoll, adb_bus); - vmstate_register(NULL, -1, &vmstate_adb_bus, adb_bus); + vmstate_register_any(NULL, &vmstate_adb_bus, adb_bus); } static void adb_bus_unrealize(BusState *qbus) @@ -262,10 +262,11 @@ static void adb_bus_unrealize(BusState *qbus) static void adb_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); k->realize = adb_bus_realize; k->unrealize = adb_bus_unrealize; - k->reset = adb_bus_reset; + rc->phases.hold = adb_bus_reset_hold; } static const TypeInfo adb_bus_type_info = { @@ -279,7 +280,7 @@ const VMStateDescription vmstate_adb_device = { .name = "adb_device", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(devaddr, ADBDevice), VMSTATE_INT32(handler, ADBDevice), VMSTATE_END_OF_LIST() diff --git a/hw/input/ads7846.c b/hw/input/ads7846.c index 1d4e04a2dc..cde3892216 100644 --- a/hw/input/ads7846.c +++ b/hw/input/ads7846.c @@ -34,28 +34,28 @@ struct ADS7846State { OBJECT_DECLARE_SIMPLE_TYPE(ADS7846State, ADS7846) /* Control-byte bitfields */ -#define CB_PD0 (1 << 0) -#define CB_PD1 (1 << 1) -#define CB_SER (1 << 2) -#define CB_MODE (1 << 3) -#define CB_A0 (1 << 4) -#define CB_A1 (1 << 5) -#define CB_A2 (1 << 6) -#define CB_START (1 << 7) +#define CB_PD0 (1 << 0) +#define CB_PD1 (1 << 1) +#define CB_SER (1 << 2) +#define CB_MODE (1 << 3) +#define CB_A0 (1 << 4) +#define CB_A1 (1 << 5) +#define CB_A2 (1 << 6) +#define CB_START (1 << 7) -#define X_AXIS_DMAX 3470 -#define X_AXIS_MIN 290 -#define Y_AXIS_DMAX 3450 -#define Y_AXIS_MIN 200 +#define X_AXIS_DMAX 3470 +#define X_AXIS_MIN 290 +#define Y_AXIS_DMAX 3450 +#define Y_AXIS_MIN 200 -#define ADS_VBAT 2000 -#define ADS_VAUX 2000 -#define ADS_TEMP0 2000 -#define ADS_TEMP1 3000 -#define ADS_XPOS(x, y) (X_AXIS_MIN + ((X_AXIS_DMAX * (x)) >> 15)) -#define ADS_YPOS(x, y) (Y_AXIS_MIN + ((Y_AXIS_DMAX * (y)) >> 15)) -#define ADS_Z1POS(x, y) 600 -#define ADS_Z2POS(x, y) (600 + 6000 / ADS_XPOS(x, y)) +#define ADS_VBAT 2000 +#define ADS_VAUX 2000 +#define ADS_TEMP0 2000 +#define ADS_TEMP1 3000 +#define ADS_XPOS(x, y) (X_AXIS_MIN + ((X_AXIS_DMAX * (x)) >> 15)) +#define ADS_YPOS(x, y) (Y_AXIS_MIN + ((Y_AXIS_DMAX * (y)) >> 15)) +#define ADS_Z1POS(x, y) 600 +#define ADS_Z2POS(x, y) (600 + 6000 / ADS_XPOS(x, y)) static void ads7846_int_update(ADS7846State *s) { @@ -86,7 +86,7 @@ static uint32_t ads7846_transfer(SSIPeripheral *dev, uint32_t value) } if (value & CB_MODE) - s->output >>= 4; /* 8 bits instead of 12 */ + s->output >>= 4; /* 8 bits instead of 12 */ break; case 1: @@ -130,7 +130,7 @@ static const VMStateDescription vmstate_ads7846 = { .version_id = 1, .minimum_version_id = 1, .post_load = ads7856_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_SSI_PERIPHERAL(ssidev, ADS7846State), VMSTATE_INT32_ARRAY(input, ADS7846State, 8), VMSTATE_INT32(noise, ADS7846State), @@ -147,10 +147,10 @@ static void ads7846_realize(SSIPeripheral *d, Error **errp) qdev_init_gpio_out(dev, &s->interrupt, 1); - s->input[0] = ADS_TEMP0; /* TEMP0 */ - s->input[2] = ADS_VBAT; /* VBAT */ - s->input[6] = ADS_VAUX; /* VAUX */ - s->input[7] = ADS_TEMP1; /* TEMP1 */ + s->input[0] = ADS_TEMP0; /* TEMP0 */ + s->input[2] = ADS_VBAT; /* VBAT */ + s->input[6] = ADS_VAUX; /* VAUX */ + s->input[7] = ADS_TEMP1; /* TEMP1 */ /* We want absolute coordinates */ qemu_add_mouse_event_handler(ads7846_ts_event, s, 1, @@ -158,7 +158,7 @@ static void ads7846_realize(SSIPeripheral *d, Error **errp) ads7846_int_update(s); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_ads7846, s); + vmstate_register_any(NULL, &vmstate_ads7846, s); } static void ads7846_class_init(ObjectClass *klass, void *data) diff --git a/hw/input/hid.c b/hw/input/hid.c index e7ecebdf8f..76bedc1844 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -209,7 +209,7 @@ static void hid_pointer_sync(DeviceState *dev) prev->dz += curr->dz; curr->dz = 0; } else { - /* prepate next (clear rel, copy abs + btns) */ + /* prepare next (clear rel, copy abs + btns) */ if (hs->kind == HID_MOUSE) { next->xdx = 0; next->ydy = 0; @@ -510,20 +510,20 @@ void hid_free(HIDState *hs) hid_del_idle_timer(hs); } -static QemuInputHandler hid_keyboard_handler = { +static const QemuInputHandler hid_keyboard_handler = { .name = "QEMU HID Keyboard", .mask = INPUT_EVENT_MASK_KEY, .event = hid_keyboard_event, }; -static QemuInputHandler hid_mouse_handler = { +static const QemuInputHandler hid_mouse_handler = { .name = "QEMU HID Mouse", .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL, .event = hid_pointer_event, .sync = hid_pointer_sync, }; -static QemuInputHandler hid_tablet_handler = { +static const QemuInputHandler hid_tablet_handler = { .name = "QEMU HID Tablet", .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, .event = hid_pointer_event, @@ -581,7 +581,7 @@ static const VMStateDescription vmstate_hid_ptr_queue = { .name = "HIDPointerEventQueue", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(xdx, HIDPointerEvent), VMSTATE_INT32(ydy, HIDPointerEvent), VMSTATE_INT32(dz, HIDPointerEvent), @@ -595,7 +595,7 @@ const VMStateDescription vmstate_hid_ptr_device = { .version_id = 1, .minimum_version_id = 1, .post_load = hid_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(ptr.queue, HIDState, QUEUE_LENGTH, 0, vmstate_hid_ptr_queue, HIDPointerEvent), VMSTATE_UINT32(head, HIDState), @@ -611,7 +611,7 @@ const VMStateDescription vmstate_hid_keyboard_device = { .version_id = 1, .minimum_version_id = 1, .post_load = hid_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(kbd.keycodes, HIDState, QUEUE_LENGTH), VMSTATE_UINT32(head, HIDState), VMSTATE_UINT32(n, HIDState), diff --git a/hw/input/lasips2.c b/hw/input/lasips2.c index 94f18be4cd..d9f8c36778 100644 --- a/hw/input/lasips2.c +++ b/hw/input/lasips2.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "hw/qdev-properties.h" +#include "hw/sysbus.h" #include "hw/input/ps2.h" #include "hw/input/lasips2.h" #include "exec/hwaddr.h" @@ -31,37 +32,31 @@ #include "exec/address-spaces.h" #include "migration/vmstate.h" #include "hw/irq.h" +#include "qapi/error.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_port = { + .name = "lasips2-port", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT8(control, LASIPS2Port), + VMSTATE_UINT8(buf, LASIPS2Port), + VMSTATE_BOOL(loopback_rbne, LASIPS2Port), + VMSTATE_END_OF_LIST() + } +}; 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), + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT8(int_status, LASIPS2State), + VMSTATE_STRUCT(kbd_port.parent_obj, LASIPS2State, 1, + vmstate_lasips2_port, LASIPS2Port), + VMSTATE_STRUCT(mouse_port.parent_obj, LASIPS2State, 1, + vmstate_lasips2_port, LASIPS2Port), VMSTATE_END_OF_LIST() } }; @@ -135,36 +130,50 @@ static const char *lasips2_write_reg_name(uint64_t addr) 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); + int level = s->int_status ? 1 : 0; + + trace_lasips2_intr(level); + qemu_set_irq(s->irq, level); +} + +static void lasips2_set_irq(void *opaque, int n, int level) +{ + LASIPS2State *s = LASIPS2(opaque); + + if (level) { + s->int_status |= BIT(n); + } else { + s->int_status &= ~BIT(n); + } + + lasips2_update_irq(s); } static void lasips2_reg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - LASIPS2Port *port = opaque; + LASIPS2Port *lp = LASIPS2_PORT(opaque); - trace_lasips2_reg_write(size, port->id, addr, + trace_lasips2_reg_write(size, lp->id, addr, lasips2_write_reg_name(addr), val); switch (addr & 0xc) { case REG_PS2_CONTROL: - port->control = val; + lp->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); + if (lp->control & LASIPS2_CONTROL_LOOPBACK) { + lp->buf = val; + lp->loopback_rbne = true; + qemu_set_irq(lp->irq, 1); break; } - if (port->id) { - ps2_write_mouse(port->dev, val); + if (lp->id) { + ps2_write_mouse(PS2_MOUSE_DEVICE(lp->ps2dev), val); } else { - ps2_write_keyboard(port->dev, val); + ps2_write_keyboard(PS2_KBD_DEVICE(lp->ps2dev), val); } break; @@ -180,55 +189,53 @@ static void lasips2_reg_write(void *opaque, hwaddr addr, uint64_t val, static uint64_t lasips2_reg_read(void *opaque, hwaddr addr, unsigned size) { - LASIPS2Port *port = opaque; + LASIPS2Port *lp = LASIPS2_PORT(opaque); uint64_t ret = 0; switch (addr & 0xc) { case REG_PS2_ID: - ret = port->id; + ret = lp->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; + if (lp->control & LASIPS2_CONTROL_LOOPBACK) { + lp->loopback_rbne = false; + qemu_set_irq(lp->irq, 0); + ret = lp->buf; break; } - ret = ps2_read_data(port->dev); + ret = ps2_read_data(lp->ps2dev); break; case REG_PS2_CONTROL: - ret = port->control; + ret = lp->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)) { + if (lp->control & LASIPS2_CONTROL_DIAG) { + if (!(lp->control & LASIPS2_CONTROL_DATDIR)) { ret &= ~LASIPS2_STATUS_DATSHD; } - if (!(port->control & LASIPS2_CONTROL_CLKDIR)) { + if (!(lp->control & LASIPS2_CONTROL_CLKDIR)) { ret &= ~LASIPS2_STATUS_CLKSHD; } } - if (port->control & LASIPS2_CONTROL_LOOPBACK) { - if (port->loopback_rbne) { + if (lp->control & LASIPS2_CONTROL_LOOPBACK) { + if (lp->loopback_rbne) { ret |= LASIPS2_STATUS_RBNE; } } else { - if (!ps2_queue_empty(port->dev)) { + if (!ps2_queue_empty(lp->ps2dev)) { ret |= LASIPS2_STATUS_RBNE; } } - if (port->parent->kbd.irq || port->parent->mouse.irq) { + if (lp->lasips2->int_status) { ret |= LASIPS2_STATUS_CMPINTR; } break; @@ -238,9 +245,9 @@ static uint64_t lasips2_reg_read(void *opaque, hwaddr addr, unsigned size) __func__, addr); break; } - trace_lasips2_reg_read(size, port->id, addr, - lasips2_read_reg_name(addr), ret); + trace_lasips2_reg_read(size, lp->id, addr, + lasips2_read_reg_name(addr), ret); return ret; } @@ -251,38 +258,218 @@ static const MemoryRegionOps lasips2_reg_ops = { .min_access_size = 1, .max_access_size = 4, }, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; -static void ps2dev_update_irq(void *opaque, int level) +static void lasips2_realize(DeviceState *dev, Error **errp) { - LASIPS2Port *port = opaque; - port->irq = level; - lasips2_update_irq(port->parent); + LASIPS2State *s = LASIPS2(dev); + LASIPS2Port *lp; + + lp = LASIPS2_PORT(&s->kbd_port); + if (!(qdev_realize(DEVICE(lp), NULL, errp))) { + return; + } + + qdev_connect_gpio_out(DEVICE(lp), 0, + qdev_get_gpio_in_named(dev, "lasips2-port-input-irq", + lp->id)); + + lp = LASIPS2_PORT(&s->mouse_port); + if (!(qdev_realize(DEVICE(lp), NULL, errp))) { + return; + } + + qdev_connect_gpio_out(DEVICE(lp), 0, + qdev_get_gpio_in_named(dev, "lasips2-port-input-irq", + lp->id)); } -void lasips2_init(MemoryRegion *address_space, - hwaddr base, qemu_irq irq) +static void lasips2_init(Object *obj) { - LASIPS2State *s; + LASIPS2State *s = LASIPS2(obj); + LASIPS2Port *lp; - s = g_new0(LASIPS2State, 1); + object_initialize_child(obj, "lasips2-kbd-port", &s->kbd_port, + TYPE_LASIPS2_KBD_PORT); + object_initialize_child(obj, "lasips2-mouse-port", &s->mouse_port, + TYPE_LASIPS2_MOUSE_PORT); - s->irq = irq; - s->mouse.id = 1; - s->kbd.parent = s; - s->mouse.parent = s; + lp = LASIPS2_PORT(&s->kbd_port); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &lp->reg); + lp = LASIPS2_PORT(&s->mouse_port); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &lp->reg); - vmstate_register(NULL, base, &vmstate_lasips2, s); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); - 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); + qdev_init_gpio_in_named(DEVICE(obj), lasips2_set_irq, + "lasips2-port-input-irq", 2); } + +static void lasips2_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = lasips2_realize; + dc->vmsd = &vmstate_lasips2; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo lasips2_info = { + .name = TYPE_LASIPS2, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = lasips2_init, + .instance_size = sizeof(LASIPS2State), + .class_init = lasips2_class_init, +}; + +static void lasips2_port_set_irq(void *opaque, int n, int level) +{ + LASIPS2Port *s = LASIPS2_PORT(opaque); + + qemu_set_irq(s->irq, level); +} + +static void lasips2_port_realize(DeviceState *dev, Error **errp) +{ + LASIPS2Port *s = LASIPS2_PORT(dev); + + qdev_connect_gpio_out(DEVICE(s->ps2dev), PS2_DEVICE_IRQ, + qdev_get_gpio_in_named(dev, "ps2-input-irq", 0)); +} + +static void lasips2_port_init(Object *obj) +{ + LASIPS2Port *s = LASIPS2_PORT(obj); + + qdev_init_gpio_out(DEVICE(obj), &s->irq, 1); + qdev_init_gpio_in_named(DEVICE(obj), lasips2_port_set_irq, + "ps2-input-irq", 1); +} + +static void lasips2_port_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + /* + * The PS/2 mouse port is integreal part of LASI and can not be + * created by users without LASI. + */ + dc->user_creatable = false; + dc->realize = lasips2_port_realize; +} + +static const TypeInfo lasips2_port_info = { + .name = TYPE_LASIPS2_PORT, + .parent = TYPE_DEVICE, + .instance_init = lasips2_port_init, + .instance_size = sizeof(LASIPS2Port), + .class_init = lasips2_port_class_init, + .class_size = sizeof(LASIPS2PortDeviceClass), + .abstract = true, +}; + +static void lasips2_kbd_port_realize(DeviceState *dev, Error **errp) +{ + LASIPS2KbdPort *s = LASIPS2_KBD_PORT(dev); + LASIPS2Port *lp = LASIPS2_PORT(dev); + LASIPS2PortDeviceClass *lpdc = LASIPS2_PORT_GET_CLASS(lp); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->kbd), errp)) { + return; + } + + lp->ps2dev = PS2_DEVICE(&s->kbd); + lpdc->parent_realize(dev, errp); +} + +static void lasips2_kbd_port_init(Object *obj) +{ + LASIPS2KbdPort *s = LASIPS2_KBD_PORT(obj); + LASIPS2Port *lp = LASIPS2_PORT(obj); + + memory_region_init_io(&lp->reg, obj, &lasips2_reg_ops, lp, "lasips2-kbd", + 0x100); + + object_initialize_child(obj, "kbd", &s->kbd, TYPE_PS2_KBD_DEVICE); + + lp->id = 0; + lp->lasips2 = container_of(s, LASIPS2State, kbd_port); +} + +static void lasips2_kbd_port_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + LASIPS2PortDeviceClass *lpdc = LASIPS2_PORT_CLASS(klass); + + /* + * The PS/2 keyboard port is integreal part of LASI and can not be + * created by users without LASI. + */ + dc->user_creatable = false; + device_class_set_parent_realize(dc, lasips2_kbd_port_realize, + &lpdc->parent_realize); +} + +static const TypeInfo lasips2_kbd_port_info = { + .name = TYPE_LASIPS2_KBD_PORT, + .parent = TYPE_LASIPS2_PORT, + .instance_size = sizeof(LASIPS2KbdPort), + .instance_init = lasips2_kbd_port_init, + .class_init = lasips2_kbd_port_class_init, +}; + +static void lasips2_mouse_port_realize(DeviceState *dev, Error **errp) +{ + LASIPS2MousePort *s = LASIPS2_MOUSE_PORT(dev); + LASIPS2Port *lp = LASIPS2_PORT(dev); + LASIPS2PortDeviceClass *lpdc = LASIPS2_PORT_GET_CLASS(lp); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->mouse), errp)) { + return; + } + + lp->ps2dev = PS2_DEVICE(&s->mouse); + lpdc->parent_realize(dev, errp); +} + +static void lasips2_mouse_port_init(Object *obj) +{ + LASIPS2MousePort *s = LASIPS2_MOUSE_PORT(obj); + LASIPS2Port *lp = LASIPS2_PORT(obj); + + memory_region_init_io(&lp->reg, obj, &lasips2_reg_ops, lp, "lasips2-mouse", + 0x100); + + object_initialize_child(obj, "mouse", &s->mouse, TYPE_PS2_MOUSE_DEVICE); + + lp->id = 1; + lp->lasips2 = container_of(s, LASIPS2State, mouse_port); +} + +static void lasips2_mouse_port_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + LASIPS2PortDeviceClass *lpdc = LASIPS2_PORT_CLASS(klass); + + device_class_set_parent_realize(dc, lasips2_mouse_port_realize, + &lpdc->parent_realize); +} + +static const TypeInfo lasips2_mouse_port_info = { + .name = TYPE_LASIPS2_MOUSE_PORT, + .parent = TYPE_LASIPS2_PORT, + .instance_size = sizeof(LASIPS2MousePort), + .instance_init = lasips2_mouse_port_init, + .class_init = lasips2_mouse_port_class_init, +}; + +static void lasips2_register_types(void) +{ + type_register_static(&lasips2_info); + type_register_static(&lasips2_port_info); + type_register_static(&lasips2_kbd_port_info); + type_register_static(&lasips2_mouse_port_info); +} + +type_init(lasips2_register_types) diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c index 19a646d9bb..59e5567afd 100644 --- a/hw/input/lm832x.c +++ b/hw/input/lm832x.c @@ -441,7 +441,7 @@ static const VMStateDescription vmstate_lm_kbd = { .version_id = 0, .minimum_version_id = 0, .post_load = lm_kbd_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_I2C_SLAVE(parent_obj, LM823KbdState), VMSTATE_UINT8(i2c_dir, LM823KbdState), VMSTATE_UINT8(i2c_cycle, LM823KbdState), diff --git a/hw/input/meson.build b/hw/input/meson.build index 8deb011d4a..3cc8ab85f0 100644 --- a/hw/input/meson.build +++ b/hw/input/meson.build @@ -1,18 +1,17 @@ -softmmu_ss.add(files('hid.c')) -softmmu_ss.add(when: 'CONFIG_ADB', if_true: files('adb.c', 'adb-mouse.c', 'adb-kbd.c')) -softmmu_ss.add(when: 'CONFIG_ADS7846', if_true: files('ads7846.c')) -softmmu_ss.add(when: 'CONFIG_LM832X', if_true: files('lm832x.c')) -softmmu_ss.add(when: 'CONFIG_PCKBD', if_true: files('pckbd.c')) -softmmu_ss.add(when: 'CONFIG_PL050', if_true: files('pl050.c')) -softmmu_ss.add(when: 'CONFIG_PS2', if_true: files('ps2.c')) -softmmu_ss.add(when: 'CONFIG_STELLARIS_INPUT', if_true: files('stellaris_input.c')) -softmmu_ss.add(when: 'CONFIG_TSC2005', if_true: files('tsc2005.c')) +system_ss.add(files('hid.c')) +system_ss.add(when: 'CONFIG_ADB', if_true: files('adb.c', 'adb-mouse.c', 'adb-kbd.c')) +system_ss.add(when: 'CONFIG_ADS7846', if_true: files('ads7846.c')) +system_ss.add(when: 'CONFIG_LM832X', if_true: files('lm832x.c')) +system_ss.add(when: 'CONFIG_PCKBD', if_true: files('pckbd.c')) +system_ss.add(when: 'CONFIG_PL050', if_true: files('pl050.c')) +system_ss.add(when: 'CONFIG_PS2', if_true: files('ps2.c')) +system_ss.add(when: 'CONFIG_STELLARIS_GAMEPAD', if_true: files('stellaris_gamepad.c')) +system_ss.add(when: 'CONFIG_TSC2005', if_true: files('tsc2005.c')) -softmmu_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: files('virtio-input.c')) -softmmu_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: files('virtio-input-hid.c')) -softmmu_ss.add(when: 'CONFIG_VIRTIO_INPUT_HOST', if_true: files('virtio-input-host.c')) -softmmu_ss.add(when: 'CONFIG_VHOST_USER_INPUT', if_true: files('vhost-user-input.c')) +system_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: files('virtio-input.c')) +system_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: files('virtio-input-hid.c')) +system_ss.add(when: 'CONFIG_VIRTIO_INPUT_HOST', if_true: files('virtio-input-host.c')) -softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_keypad.c')) -softmmu_ss.add(when: 'CONFIG_TSC210X', if_true: files('tsc210x.c')) -softmmu_ss.add(when: 'CONFIG_LASIPS2', if_true: files('lasips2.c')) +system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_keypad.c')) +system_ss.add(when: 'CONFIG_TSC210X', if_true: files('tsc210x.c')) +system_ss.add(when: 'CONFIG_LASIPS2', if_true: files('lasips2.c')) diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index 4efdf75620..74f10b640f 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -29,7 +29,7 @@ #include "qapi/error.h" #include "hw/isa/isa.h" #include "migration/vmstate.h" -#include "hw/acpi/aml-build.h" +#include "hw/acpi/acpi_aml_interface.h" #include "hw/input/ps2.h" #include "hw/irq.h" #include "hw/input/i8042.h" @@ -39,49 +39,86 @@ #include "trace.h" -/* Keyboard Controller Commands */ -#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */ -#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */ -#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */ -#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */ -#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */ -#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */ -#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */ -#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */ -#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */ -#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */ -#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */ -#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */ -#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */ -#define KBD_CCMD_WRITE_OBUF 0xD2 -#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if - initiated by the auxiliary device */ -#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */ -#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */ -#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */ -#define KBD_CCMD_PULSE_BITS_3_0 0xF0 /* Pulse bits 3-0 of the output port P2. */ -#define KBD_CCMD_RESET 0xFE /* Pulse bit 0 of the output port P2 = CPU reset. */ -#define KBD_CCMD_NO_OP 0xFF /* Pulse no bits of the output port P2. */ +/* Keyboard Controller Commands */ + +/* Read mode bits */ +#define KBD_CCMD_READ_MODE 0x20 +/* Write mode bits */ +#define KBD_CCMD_WRITE_MODE 0x60 +/* Get controller version */ +#define KBD_CCMD_GET_VERSION 0xA1 +/* Disable mouse interface */ +#define KBD_CCMD_MOUSE_DISABLE 0xA7 +/* Enable mouse interface */ +#define KBD_CCMD_MOUSE_ENABLE 0xA8 +/* Mouse interface test */ +#define KBD_CCMD_TEST_MOUSE 0xA9 +/* Controller self test */ +#define KBD_CCMD_SELF_TEST 0xAA +/* Keyboard interface test */ +#define KBD_CCMD_KBD_TEST 0xAB +/* Keyboard interface disable */ +#define KBD_CCMD_KBD_DISABLE 0xAD +/* Keyboard interface enable */ +#define KBD_CCMD_KBD_ENABLE 0xAE +/* read input port */ +#define KBD_CCMD_READ_INPORT 0xC0 +/* read output port */ +#define KBD_CCMD_READ_OUTPORT 0xD0 +/* write output port */ +#define KBD_CCMD_WRITE_OUTPORT 0xD1 +#define KBD_CCMD_WRITE_OBUF 0xD2 +/* Write to output buffer as if initiated by the auxiliary device */ +#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 +/* Write the following byte to the mouse */ +#define KBD_CCMD_WRITE_MOUSE 0xD4 +/* HP vectra only ? */ +#define KBD_CCMD_DISABLE_A20 0xDD +/* HP vectra only ? */ +#define KBD_CCMD_ENABLE_A20 0xDF +/* Pulse bits 3-0 of the output port P2. */ +#define KBD_CCMD_PULSE_BITS_3_0 0xF0 +/* Pulse bit 0 of the output port P2 = CPU reset. */ +#define KBD_CCMD_RESET 0xFE +/* Pulse no bits of the output port P2. */ +#define KBD_CCMD_NO_OP 0xFF /* Status Register Bits */ -#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */ -#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */ -#define KBD_STAT_SELFTEST 0x04 /* Self test successful */ -#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */ -#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */ -#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */ -#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */ -#define KBD_STAT_PERR 0x80 /* Parity error */ + +/* Keyboard output buffer full */ +#define KBD_STAT_OBF 0x01 +/* Keyboard input buffer full */ +#define KBD_STAT_IBF 0x02 +/* Self test successful */ +#define KBD_STAT_SELFTEST 0x04 +/* Last write was a command write (0=data) */ +#define KBD_STAT_CMD 0x08 +/* Zero if keyboard locked */ +#define KBD_STAT_UNLOCKED 0x10 +/* Mouse output buffer full */ +#define KBD_STAT_MOUSE_OBF 0x20 +/* General receive/xmit timeout */ +#define KBD_STAT_GTO 0x40 +/* Parity error */ +#define KBD_STAT_PERR 0x80 /* Controller Mode Register Bits */ -#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */ -#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */ -#define KBD_MODE_SYS 0x04 /* The system flag (?) */ -#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */ -#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */ -#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */ -#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */ -#define KBD_MODE_RFU 0x80 + +/* Keyboard data generate IRQ1 */ +#define KBD_MODE_KBD_INT 0x01 +/* Mouse data generate IRQ12 */ +#define KBD_MODE_MOUSE_INT 0x02 +/* The system flag (?) */ +#define KBD_MODE_SYS 0x04 +/* The keylock doesn't affect the keyboard if set */ +#define KBD_MODE_NO_KEYLOCK 0x08 +/* Disable keyboard interface */ +#define KBD_MODE_DISABLE_KBD 0x10 +/* Disable mouse interface */ +#define KBD_MODE_DISABLE_MOUSE 0x20 +/* Scan code conversion to PC format */ +#define KBD_MODE_KCC 0x40 +#define KBD_MODE_RFU 0x80 /* Output Port Bits */ #define KBD_OUT_RESET 0x01 /* 1=normal mode, 0=reset */ @@ -89,7 +126,8 @@ #define KBD_OUT_OBF 0x10 /* Keyboard output buffer full */ #define KBD_OUT_MOUSE_OBF 0x20 /* Mouse output buffer full */ -/* OSes typically write 0xdd/0xdf to turn the A20 line off and on. +/* + * OSes typically write 0xdd/0xdf to turn the A20 line off and on. * We make the default value of the outport include these four bits, * so that the subsection is rarely necessary. */ @@ -108,33 +146,11 @@ #define KBD_OBSRC_MOUSE 0x02 #define KBD_OBSRC_CTRL 0x04 -typedef struct KBDState { - uint8_t write_cmd; /* if non zero, write data to port 60 is expected */ - uint8_t status; - uint8_t mode; - uint8_t outport; - uint32_t migration_flags; - uint32_t obsrc; - bool outport_present; - bool extended_state; - bool extended_state_loaded; - /* Bitmask of devices with data available. */ - uint8_t pending; - uint8_t obdata; - uint8_t cbdata; - uint8_t pending_tmp; - void *kbd; - void *mouse; - QEMUTimer *throttle_timer; - qemu_irq irq_kbd; - qemu_irq irq_mouse; - qemu_irq a20_out; - hwaddr mask; -} KBDState; - -/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be - incorrect, but it avoids having to simulate exact delays */ +/* + * XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be + * incorrect, but it avoids having to simulate exact delays + */ static void kbd_update_irq_lines(KBDState *s) { int irq_kbd_level, irq_mouse_level; @@ -154,8 +170,8 @@ static void kbd_update_irq_lines(KBDState *s) } } } - qemu_set_irq(s->irq_kbd, irq_kbd_level); - qemu_set_irq(s->irq_mouse, irq_mouse_level); + qemu_set_irq(s->irqs[I8042_KBD_IRQ], irq_kbd_level); + qemu_set_irq(s->irqs[I8042_MOUSE_IRQ], irq_mouse_level); } static void kbd_deassert_irq(KBDState *s) @@ -270,7 +286,7 @@ static void kbd_queue(KBDState *s, int b, int aux) s->pending |= aux ? KBD_PENDING_CTRL_AUX : KBD_PENDING_CTRL_KBD; kbd_safe_update_irq(s); } else { - ps2_queue(aux ? s->mouse : s->kbd, b); + ps2_queue(aux ? PS2_DEVICE(&s->ps2mouse) : PS2_DEVICE(&s->ps2kbd), b); } } @@ -302,21 +318,23 @@ static void kbd_write_command(void *opaque, hwaddr addr, trace_pckbd_kbd_write_command(val); - /* Bits 3-0 of the output port P2 of the keyboard controller may be pulsed + /* + * Bits 3-0 of the output port P2 of the keyboard controller may be pulsed * low for approximately 6 micro seconds. Bits 3-0 of the KBD_CCMD_PULSE * command specify the output port bits to be pulsed. * 0: Bit should be pulsed. 1: Bit should not be modified. * The only useful version of this command is pulsing bit 0, * which does a CPU reset. */ - if((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) { - if(!(val & 1)) + if ((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) { + if (!(val & 1)) { val = KBD_CCMD_RESET; - else + } else { val = KBD_CCMD_NO_OP; + } } - switch(val) { + switch (val) { case KBD_CCMD_READ_MODE: kbd_queue(s, s->mode, 0); break; @@ -390,9 +408,9 @@ static uint64_t kbd_read_data(void *opaque, hwaddr addr, timer_mod(s->throttle_timer, qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + 1000); } - s->obdata = ps2_read_data(s->kbd); + s->obdata = ps2_read_data(PS2_DEVICE(&s->ps2kbd)); } else if (s->obsrc & KBD_OBSRC_MOUSE) { - s->obdata = ps2_read_data(s->mouse); + s->obdata = ps2_read_data(PS2_DEVICE(&s->ps2mouse)); } else if (s->obsrc & KBD_OBSRC_CTRL) { s->obdata = kbd_dequeue(s); } @@ -409,16 +427,17 @@ static void kbd_write_data(void *opaque, hwaddr addr, trace_pckbd_kbd_write_data(val); - switch(s->write_cmd) { + switch (s->write_cmd) { case 0: - ps2_write_keyboard(s->kbd, val); + ps2_write_keyboard(&s->ps2kbd, val); /* sending data to the keyboard reenables PS/2 communication */ s->mode &= ~KBD_MODE_DISABLE_KBD; kbd_safe_update_irq(s); break; case KBD_CCMD_WRITE_MODE: s->mode = val; - ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0); + ps2_keyboard_set_translation(&s->ps2kbd, + (s->mode & KBD_MODE_KCC) != 0); /* * a write to the mode byte interrupt enable flags directly updates * the irq lines @@ -440,7 +459,7 @@ static void kbd_write_data(void *opaque, hwaddr addr, outport_write(s, val); break; case KBD_CCMD_WRITE_MOUSE: - ps2_write_mouse(s->mouse, val); + ps2_write_mouse(&s->ps2mouse, val); /* sending data to the mouse reenables PS/2 communication */ s->mode &= ~KBD_MODE_DISABLE_MOUSE; kbd_safe_update_irq(s); @@ -491,7 +510,7 @@ static const VMStateDescription vmstate_kbd_outport = { .minimum_version_id = 1, .post_load = kbd_outport_post_load, .needed = kbd_outport_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(outport, KBDState), VMSTATE_END_OF_LIST() } @@ -533,7 +552,7 @@ static const VMStateDescription vmstate_kbd_extended_state = { .post_load = kbd_extended_state_post_load, .pre_save = kbd_extended_state_pre_save, .needed = kbd_extended_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(migration_flags, KBDState), VMSTATE_UINT32(obsrc, KBDState), VMSTATE_UINT8(obdata, KBDState), @@ -600,14 +619,14 @@ static const VMStateDescription vmstate_kbd = { .pre_load = kbd_pre_load, .post_load = kbd_post_load, .pre_save = kbd_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(write_cmd, KBDState), VMSTATE_UINT8(status, KBDState), VMSTATE_UINT8(mode, KBDState), VMSTATE_UINT8(pending_tmp, KBDState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_kbd_outport, &vmstate_kbd_extended_state, NULL @@ -619,10 +638,11 @@ static uint64_t kbd_mm_readfn(void *opaque, hwaddr addr, unsigned size) { KBDState *s = opaque; - if (addr & s->mask) + if (addr & s->mask) { return kbd_read_status(s, 0, 1) & 0xff; - else + } else { return kbd_read_data(s, 0, 1) & 0xff; + } } static void kbd_mm_writefn(void *opaque, hwaddr addr, @@ -630,10 +650,11 @@ static void kbd_mm_writefn(void *opaque, hwaddr addr, { KBDState *s = opaque; - if (addr & s->mask) + if (addr & s->mask) { kbd_write_command(s, 0, value & 0xff, 1); - else + } else { kbd_write_data(s, 0, value & 0xff, 1); + } } @@ -645,54 +666,122 @@ static const MemoryRegionOps i8042_mmio_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, - MemoryRegion *region, ram_addr_t size, - hwaddr mask) +static void i8042_mmio_set_kbd_irq(void *opaque, int n, int level) { - KBDState *s = g_new0(KBDState, 1); + MMIOKBDState *s = I8042_MMIO(opaque); + KBDState *ks = &s->kbd; - s->irq_kbd = kbd_irq; - s->irq_mouse = mouse_irq; - s->mask = mask; - - s->extended_state = true; - - vmstate_register(NULL, 0, &vmstate_kbd, s); - - memory_region_init_io(region, NULL, &i8042_mmio_ops, s, "i8042", size); - - s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s); - s->mouse = ps2_mouse_init(kbd_update_aux_irq, s); - qemu_register_reset(kbd_reset, s); + kbd_update_kbd_irq(ks, level); } -struct ISAKBDState { - ISADevice parent_obj; +static void i8042_mmio_set_mouse_irq(void *opaque, int n, int level) +{ + MMIOKBDState *s = I8042_MMIO(opaque); + KBDState *ks = &s->kbd; - KBDState kbd; - bool kbd_throttle; - MemoryRegion io[2]; - uint8_t kbd_irq; - uint8_t mouse_irq; + kbd_update_aux_irq(ks, level); +} + +static void i8042_mmio_reset(DeviceState *dev) +{ + MMIOKBDState *s = I8042_MMIO(dev); + KBDState *ks = &s->kbd; + + kbd_reset(ks); +} + +static void i8042_mmio_realize(DeviceState *dev, Error **errp) +{ + MMIOKBDState *s = I8042_MMIO(dev); + KBDState *ks = &s->kbd; + + memory_region_init_io(&s->region, OBJECT(dev), &i8042_mmio_ops, ks, + "i8042", s->size); + + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->region); + + if (!sysbus_realize(SYS_BUS_DEVICE(&ks->ps2kbd), errp)) { + return; + } + + if (!sysbus_realize(SYS_BUS_DEVICE(&ks->ps2mouse), errp)) { + return; + } + + qdev_connect_gpio_out(DEVICE(&ks->ps2kbd), PS2_DEVICE_IRQ, + qdev_get_gpio_in_named(dev, "ps2-kbd-input-irq", + 0)); + + qdev_connect_gpio_out(DEVICE(&ks->ps2mouse), PS2_DEVICE_IRQ, + qdev_get_gpio_in_named(dev, "ps2-mouse-input-irq", + 0)); +} + +static void i8042_mmio_init(Object *obj) +{ + MMIOKBDState *s = I8042_MMIO(obj); + KBDState *ks = &s->kbd; + + ks->extended_state = true; + + object_initialize_child(obj, "ps2kbd", &ks->ps2kbd, TYPE_PS2_KBD_DEVICE); + object_initialize_child(obj, "ps2mouse", &ks->ps2mouse, + TYPE_PS2_MOUSE_DEVICE); + + qdev_init_gpio_out(DEVICE(obj), ks->irqs, 2); + qdev_init_gpio_in_named(DEVICE(obj), i8042_mmio_set_kbd_irq, + "ps2-kbd-input-irq", 1); + qdev_init_gpio_in_named(DEVICE(obj), i8042_mmio_set_mouse_irq, + "ps2-mouse-input-irq", 1); +} + +static Property i8042_mmio_properties[] = { + DEFINE_PROP_UINT64("mask", MMIOKBDState, kbd.mask, UINT64_MAX), + DEFINE_PROP_UINT32("size", MMIOKBDState, size, -1), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_kbd_mmio = { + .name = "pckbd-mmio", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT(kbd, MMIOKBDState, 0, vmstate_kbd, KBDState), + VMSTATE_END_OF_LIST() + } +}; + +static void i8042_mmio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = i8042_mmio_realize; + dc->reset = i8042_mmio_reset; + dc->vmsd = &vmstate_kbd_mmio; + device_class_set_props(dc, i8042_mmio_properties); + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo i8042_mmio_info = { + .name = TYPE_I8042_MMIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = i8042_mmio_init, + .instance_size = sizeof(MMIOKBDState), + .class_init = i8042_mmio_class_init }; void i8042_isa_mouse_fake_event(ISAKBDState *isa) { KBDState *s = &isa->kbd; - ps2_mouse_fake_event(s->mouse); -} - -void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out) -{ - qdev_connect_gpio_out_named(DEVICE(dev), I8042_A20_LINE, 0, a20_out); + ps2_mouse_fake_event(&s->ps2mouse); } static const VMStateDescription vmstate_kbd_isa = { .name = "pckbd", .version_id = 3, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(kbd, ISAKBDState, 0, vmstate_kbd, KBDState), VMSTATE_END_OF_LIST() } @@ -718,6 +807,31 @@ static const MemoryRegionOps i8042_cmd_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static void i8042_set_kbd_irq(void *opaque, int n, int level) +{ + ISAKBDState *s = I8042(opaque); + KBDState *ks = &s->kbd; + + kbd_update_kbd_irq(ks, level); +} + +static void i8042_set_mouse_irq(void *opaque, int n, int level) +{ + ISAKBDState *s = I8042(opaque); + KBDState *ks = &s->kbd; + + kbd_update_aux_irq(ks, level); +} + + +static void i8042_reset(DeviceState *dev) +{ + ISAKBDState *s = I8042(dev); + KBDState *ks = &s->kbd; + + kbd_reset(ks); +} + static void i8042_initfn(Object *obj) { ISAKBDState *isa_s = I8042(obj); @@ -728,7 +842,17 @@ static void i8042_initfn(Object *obj) memory_region_init_io(isa_s->io + 1, obj, &i8042_cmd_ops, s, "i8042-cmd", 1); + object_initialize_child(obj, "ps2kbd", &s->ps2kbd, TYPE_PS2_KBD_DEVICE); + object_initialize_child(obj, "ps2mouse", &s->ps2mouse, + TYPE_PS2_MOUSE_DEVICE); + qdev_init_gpio_out_named(DEVICE(obj), &s->a20_out, I8042_A20_LINE, 1); + + qdev_init_gpio_out(DEVICE(obj), s->irqs, 2); + qdev_init_gpio_in_named(DEVICE(obj), i8042_set_kbd_irq, + "ps2-kbd-input-irq", 1); + qdev_init_gpio_in_named(DEVICE(obj), i8042_set_mouse_irq, + "ps2-mouse-input-irq", 1); } static void i8042_realizefn(DeviceState *dev, Error **errp) @@ -749,14 +873,28 @@ static void i8042_realizefn(DeviceState *dev, Error **errp) return; } - s->irq_kbd = isa_get_irq(isadev, isa_s->kbd_irq); - s->irq_mouse = isa_get_irq(isadev, isa_s->mouse_irq); + isa_connect_gpio_out(isadev, I8042_KBD_IRQ, isa_s->kbd_irq); + isa_connect_gpio_out(isadev, I8042_MOUSE_IRQ, isa_s->mouse_irq); isa_register_ioport(isadev, isa_s->io + 0, 0x60); isa_register_ioport(isadev, isa_s->io + 1, 0x64); - s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s); - s->mouse = ps2_mouse_init(kbd_update_aux_irq, s); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ps2kbd), errp)) { + return; + } + + qdev_connect_gpio_out(DEVICE(&s->ps2kbd), PS2_DEVICE_IRQ, + qdev_get_gpio_in_named(dev, "ps2-kbd-input-irq", + 0)); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ps2mouse), errp)) { + return; + } + + qdev_connect_gpio_out(DEVICE(&s->ps2mouse), PS2_DEVICE_IRQ, + qdev_get_gpio_in_named(dev, "ps2-mouse-input-irq", + 0)); + if (isa_s->kbd_throttle && !isa_s->kbd.extended_state) { warn_report(TYPE_I8042 ": can't enable kbd-throttle without" " extended-state, disabling kbd-throttle"); @@ -764,12 +902,11 @@ static void i8042_realizefn(DeviceState *dev, Error **errp) s->throttle_timer = timer_new_us(QEMU_CLOCK_VIRTUAL, kbd_throttle_timeout, s); } - qemu_register_reset(kbd_reset, s); } -static void i8042_build_aml(ISADevice *isadev, Aml *scope) +static void i8042_build_aml(AcpiDevAmlIf *adev, Aml *scope) { - ISAKBDState *isa_s = I8042(isadev); + ISAKBDState *isa_s = I8042(adev); Aml *kbd; Aml *mou; Aml *crs; @@ -807,12 +944,13 @@ static Property i8042_properties[] = { static void i8042_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - ISADeviceClass *isa = ISA_DEVICE_CLASS(klass); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); device_class_set_props(dc, i8042_properties); + dc->reset = i8042_reset; dc->realize = i8042_realizefn; dc->vmsd = &vmstate_kbd_isa; - isa->build_aml = i8042_build_aml; + adevc->build_dev_aml = i8042_build_aml; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } @@ -822,11 +960,16 @@ static const TypeInfo i8042_info = { .instance_size = sizeof(ISAKBDState), .instance_init = i8042_initfn, .class_init = i8042_class_initfn, + .interfaces = (InterfaceInfo[]) { + { TYPE_ACPI_DEV_AML_IF }, + { }, + }, }; static void i8042_register_types(void) { type_register_static(&i8042_info); + type_register_static(&i8042_mmio_info); } type_init(i8042_register_types) diff --git a/hw/input/pl050.c b/hw/input/pl050.c index d279b6c148..6519e260ed 100644 --- a/hw/input/pl050.c +++ b/hw/input/pl050.c @@ -7,36 +7,30 @@ * This code is licensed under the GPL. */ +/* + * QEMU interface: + * + sysbus MMIO region 0: MemoryRegion defining the PL050 registers + * + Named GPIO input "ps2-input-irq": set to 1 if the downstream PS2 device + * has asserted its irq + * + sysbus IRQ 0: PL050 output irq + */ + #include "qemu/osdep.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "hw/input/ps2.h" +#include "hw/input/pl050.h" #include "hw/irq.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" -#define TYPE_PL050 "pl050" -OBJECT_DECLARE_SIMPLE_TYPE(PL050State, PL050) - -struct PL050State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - void *dev; - uint32_t cr; - uint32_t clk; - uint32_t last; - int pending; - qemu_irq irq; - bool is_mouse; -}; static const VMStateDescription vmstate_pl050 = { .name = "pl050", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(cr, PL050State), VMSTATE_UINT32(clk, PL050State), VMSTATE_UINT32(last, PL050State), @@ -53,26 +47,34 @@ static const VMStateDescription vmstate_pl050 = { #define PL050_KMIC (1 << 1) #define PL050_KMID (1 << 0) -static const unsigned char pl050_id[] = -{ 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; +static const unsigned char pl050_id[] = { + 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 +}; -static void pl050_update(void *opaque, int level) +static void pl050_update_irq(PL050State *s) +{ + int level = (s->pending && (s->cr & 0x10) != 0) + || (s->cr & 0x08) != 0; + + qemu_set_irq(s->irq, level); +} + +static void pl050_set_irq(void *opaque, int n, int level) { PL050State *s = (PL050State *)opaque; - int raise; s->pending = level; - raise = (s->pending && (s->cr & 0x10) != 0) - || (s->cr & 0x08) != 0; - qemu_set_irq(s->irq, raise); + pl050_update_irq(s); } static uint64_t pl050_read(void *opaque, hwaddr offset, unsigned size) { PL050State *s = (PL050State *)opaque; - if (offset >= 0xfe0 && offset < 0x1000) + + if (offset >= 0xfe0 && offset < 0x1000) { return pl050_id[(offset - 0xfe0) >> 2]; + } switch (offset >> 2) { case 0: /* KMICR */ @@ -88,16 +90,19 @@ static uint64_t pl050_read(void *opaque, hwaddr offset, val = (val ^ (val >> 1)) & 1; stat = PL050_TXEMPTY; - if (val) + if (val) { stat |= PL050_RXPARITY; - if (s->pending) + } + if (s->pending) { stat |= PL050_RXFULL; + } return stat; } case 2: /* KMIDATA */ - if (s->pending) - s->last = ps2_read_data(s->dev); + if (s->pending) { + s->last = ps2_read_data(s->ps2dev); + } return s->last; case 3: /* KMICLKDIV */ return s->clk; @@ -114,19 +119,20 @@ static void pl050_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { PL050State *s = (PL050State *)opaque; + switch (offset >> 2) { case 0: /* KMICR */ s->cr = value; - pl050_update(s, s->pending); + pl050_update_irq(s); /* ??? Need to implement the enable/disable bit. */ break; case 2: /* KMIDATA */ /* ??? This should toggle the TX interrupt line. */ /* ??? This means kbd/mouse can block each other. */ if (s->is_mouse) { - ps2_write_mouse(s->dev, value); + ps2_write_mouse(PS2_MOUSE_DEVICE(s->ps2dev), value); } else { - ps2_write_keyboard(s->dev, value); + ps2_write_keyboard(PS2_KBD_DEVICE(s->ps2dev), value); } break; case 3: /* KMICLKDIV */ @@ -146,44 +152,103 @@ static const MemoryRegionOps pl050_ops = { static void pl050_realize(DeviceState *dev, Error **errp) { PL050State *s = PL050(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - memory_region_init_io(&s->iomem, OBJECT(s), &pl050_ops, s, "pl050", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - if (s->is_mouse) { - s->dev = ps2_mouse_init(pl050_update, s); - } else { - s->dev = ps2_kbd_init(pl050_update, s); - } + qdev_connect_gpio_out(DEVICE(s->ps2dev), PS2_DEVICE_IRQ, + qdev_get_gpio_in_named(dev, "ps2-input-irq", 0)); } -static void pl050_keyboard_init(Object *obj) +static void pl050_kbd_realize(DeviceState *dev, Error **errp) { - PL050State *s = PL050(obj); + PL050DeviceClass *pdc = PL050_GET_CLASS(dev); + PL050KbdState *s = PL050_KBD_DEVICE(dev); + PL050State *ps = PL050(dev); - s->is_mouse = false; + if (!sysbus_realize(SYS_BUS_DEVICE(&s->kbd), errp)) { + return; + } + + ps->ps2dev = PS2_DEVICE(&s->kbd); + pdc->parent_realize(dev, errp); +} + +static void pl050_kbd_init(Object *obj) +{ + PL050KbdState *s = PL050_KBD_DEVICE(obj); + PL050State *ps = PL050(obj); + + ps->is_mouse = false; + object_initialize_child(obj, "kbd", &s->kbd, TYPE_PS2_KBD_DEVICE); +} + +static void pl050_mouse_realize(DeviceState *dev, Error **errp) +{ + PL050DeviceClass *pdc = PL050_GET_CLASS(dev); + PL050MouseState *s = PL050_MOUSE_DEVICE(dev); + PL050State *ps = PL050(dev); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->mouse), errp)) { + return; + } + + ps->ps2dev = PS2_DEVICE(&s->mouse); + pdc->parent_realize(dev, errp); } static void pl050_mouse_init(Object *obj) { - PL050State *s = PL050(obj); + PL050MouseState *s = PL050_MOUSE_DEVICE(obj); + PL050State *ps = PL050(obj); - s->is_mouse = true; + ps->is_mouse = true; + object_initialize_child(obj, "mouse", &s->mouse, TYPE_PS2_MOUSE_DEVICE); +} + +static void pl050_kbd_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PL050DeviceClass *pdc = PL050_CLASS(oc); + + device_class_set_parent_realize(dc, pl050_kbd_realize, + &pdc->parent_realize); } static const TypeInfo pl050_kbd_info = { - .name = "pl050_keyboard", + .name = TYPE_PL050_KBD_DEVICE, .parent = TYPE_PL050, - .instance_init = pl050_keyboard_init, + .instance_init = pl050_kbd_init, + .instance_size = sizeof(PL050KbdState), + .class_init = pl050_kbd_class_init, }; +static void pl050_mouse_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PL050DeviceClass *pdc = PL050_CLASS(oc); + + device_class_set_parent_realize(dc, pl050_mouse_realize, + &pdc->parent_realize); +} + static const TypeInfo pl050_mouse_info = { - .name = "pl050_mouse", + .name = TYPE_PL050_MOUSE_DEVICE, .parent = TYPE_PL050, .instance_init = pl050_mouse_init, + .instance_size = sizeof(PL050MouseState), + .class_init = pl050_mouse_class_init, }; +static void pl050_init(Object *obj) +{ + PL050State *s = PL050(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &pl050_ops, s, "pl050", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + + qdev_init_gpio_in_named(DEVICE(obj), pl050_set_irq, "ps2-input-irq", 1); +} + static void pl050_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -195,7 +260,10 @@ static void pl050_class_init(ObjectClass *oc, void *data) static const TypeInfo pl050_type_info = { .name = TYPE_PL050, .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = pl050_init, .instance_size = sizeof(PL050State), + .class_init = pl050_class_init, + .class_size = sizeof(PL050DeviceClass), .abstract = true, .class_init = pl050_class_init, }; diff --git a/hw/input/ps2.c b/hw/input/ps2.c index c16df1de7a..00b695a0b9 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -24,61 +24,59 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "hw/irq.h" +#include "hw/sysbus.h" #include "hw/input/ps2.h" #include "migration/vmstate.h" #include "ui/console.h" #include "ui/input.h" #include "sysemu/reset.h" #include "sysemu/runstate.h" +#include "qapi/error.h" #include "trace.h" /* Keyboard Commands */ -#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ -#define KBD_CMD_ECHO 0xEE -#define KBD_CMD_SCANCODE 0xF0 /* Get/set scancode set */ -#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */ -#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ -#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ -#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_LEDS 0xED /* Set keyboard leds */ +#define KBD_CMD_ECHO 0xEE +#define KBD_CMD_SCANCODE 0xF0 /* Get/set scancode set */ +#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */ +#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ +#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ +#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 */ -#define KBD_REPLY_ID 0xAB /* Keyboard ID */ -#define KBD_REPLY_ACK 0xFA /* Command ACK */ -#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ +#define KBD_REPLY_POR 0xAA /* Power on reset */ +#define KBD_REPLY_ID 0xAB /* Keyboard ID */ +#define KBD_REPLY_ACK 0xFA /* Command ACK */ +#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ /* Mouse Commands */ -#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ -#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ -#define AUX_SET_RES 0xE8 /* Set resolution */ -#define AUX_GET_SCALE 0xE9 /* Get scaling factor */ -#define AUX_SET_STREAM 0xEA /* Set stream mode */ -#define AUX_POLL 0xEB /* Poll */ -#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */ -#define AUX_SET_WRAP 0xEE /* Set wrap mode */ -#define AUX_SET_REMOTE 0xF0 /* Set remote mode */ -#define AUX_GET_TYPE 0xF2 /* Get type */ -#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */ -#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */ -#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */ -#define AUX_SET_DEFAULT 0xF6 -#define AUX_RESET 0xFF /* Reset aux device */ -#define AUX_ACK 0xFA /* Command byte ACK. */ +#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ +#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ +#define AUX_SET_RES 0xE8 /* Set resolution */ +#define AUX_GET_SCALE 0xE9 /* Get scaling factor */ +#define AUX_SET_STREAM 0xEA /* Set stream mode */ +#define AUX_POLL 0xEB /* Poll */ +#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */ +#define AUX_SET_WRAP 0xEE /* Set wrap mode */ +#define AUX_SET_REMOTE 0xF0 /* Set remote mode */ +#define AUX_GET_TYPE 0xF2 /* Get type */ +#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */ +#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */ +#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */ +#define AUX_SET_DEFAULT 0xF6 +#define AUX_RESET 0xFF /* Reset aux device */ +#define AUX_ACK 0xFA /* Command byte ACK. */ #define MOUSE_STATUS_REMOTE 0x40 #define MOUSE_STATUS_ENABLED 0x20 #define MOUSE_STATUS_SCALE21 0x10 -/* - * PS/2 buffer size. Keep 256 bytes for compatibility with - * older QEMU versions. - */ -#define PS2_BUFFER_SIZE 256 #define PS2_QUEUE_SIZE 16 /* Queue size required by PS/2 protocol */ #define PS2_QUEUE_HEADROOM 8 /* Queue size for keyboard command replies */ @@ -90,43 +88,6 @@ #define MOD_SHIFT_R (1 << 4) #define MOD_ALT_R (1 << 5) -typedef struct { - uint8_t data[PS2_BUFFER_SIZE]; - int rptr, wptr, cwptr, count; -} PS2Queue; - -struct PS2State { - PS2Queue queue; - int32_t write_cmd; - void (*update_irq)(void *, int); - void *update_arg; -}; - -typedef struct { - PS2State common; - int scan_enabled; - int translate; - int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */ - int ledstate; - bool need_high_bit; - unsigned int modifiers; /* bitmask of MOD_* constants above */ -} PS2KbdState; - -typedef struct { - PS2State common; - uint8_t mouse_status; - uint8_t mouse_resolution; - uint8_t mouse_sample_rate; - uint8_t mouse_wrap; - uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */ - uint8_t mouse_detect_state; - int mouse_dx; /* current values, needed for 'poll' mode */ - int mouse_dy; - int mouse_dz; - int mouse_dw; - uint8_t mouse_buttons; -} PS2MouseState; - static uint8_t translate_table[256] = { 0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58, 0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59, @@ -212,9 +173,14 @@ void ps2_queue_noirq(PS2State *s, int b) q->count++; } -void ps2_raise_irq(PS2State *s) +static void ps2_raise_irq(PS2State *s) { - s->update_irq(s->update_arg, 1); + qemu_set_irq(s->irq, 1); +} + +static void ps2_lower_irq(PS2State *s) +{ + qemu_set_irq(s->irq, 0); } void ps2_queue(PS2State *s, int b) @@ -324,6 +290,7 @@ static void ps2_cqueue_reset(PS2State *s) static void ps2_put_keycode(void *opaque, int keycode) { PS2KbdState *s = opaque; + PS2State *ps = PS2_DEVICE(s); trace_ps2_put_keycode(opaque, keycode); qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); @@ -332,13 +299,13 @@ static void ps2_put_keycode(void *opaque, int keycode) if (keycode == 0xf0) { s->need_high_bit = true; } else if (s->need_high_bit) { - ps2_queue(&s->common, translate_table[keycode] | 0x80); + ps2_queue(ps, translate_table[keycode] | 0x80); s->need_high_bit = false; } else { - ps2_queue(&s->common, translate_table[keycode]); + ps2_queue(ps, translate_table[keycode]); } } else { - ps2_queue(&s->common, keycode); + ps2_queue(ps, keycode); } } @@ -435,9 +402,13 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, ps2_put_keycode(s, 0xaa); } } + } else if ((qcode == Q_KEY_CODE_LANG1 || qcode == Q_KEY_CODE_LANG2) + && !key->down) { + /* Ignore release for these keys */ } else { - if (qcode < qemu_input_map_qcode_to_atset1_len) + if (qcode < qemu_input_map_qcode_to_atset1_len) { keycode = qemu_input_map_qcode_to_atset1[qcode]; + } if (keycode) { if (keycode & 0xff00) { ps2_put_keycode(s, keycode >> 8); @@ -529,9 +500,13 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, ps2_put_keycode(s, 0x12); } } + } else if ((qcode == Q_KEY_CODE_LANG1 || qcode == Q_KEY_CODE_LANG2) && + !key->down) { + /* Ignore release for these keys */ } else { - if (qcode < qemu_input_map_qcode_to_atset2_len) + if (qcode < qemu_input_map_qcode_to_atset2_len) { keycode = qemu_input_map_qcode_to_atset2[qcode]; + } if (keycode) { if (keycode & 0xff00) { ps2_put_keycode(s, keycode >> 8); @@ -546,8 +521,9 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, } } } else if (s->scancode_set == 3) { - if (qcode < qemu_input_map_qcode_to_atset3_len) + if (qcode < qemu_input_map_qcode_to_atset3_len) { keycode = qemu_input_map_qcode_to_atset3[qcode]; + } if (keycode) { /* FIXME: break code should be configured on a key by key basis */ if (!key->down) { @@ -569,8 +545,10 @@ uint32_t ps2_read_data(PS2State *s) trace_ps2_read_data(s); q = &s->queue; if (q->count == 0) { - /* NOTE: if no data left, we return the last keyboard one - (needed for EMM386) */ + /* + * NOTE: if no data left, we return the last keyboard one + * (needed for EMM386) + */ /* XXX: need a timer to do things correctly */ index = q->rptr - 1; if (index < 0) { @@ -588,10 +566,10 @@ uint32_t ps2_read_data(PS2State *s) q->cwptr = -1; } /* reading deasserts IRQ */ - s->update_irq(s->update_arg, 0); + ps2_lower_irq(s); /* reassert IRQs if data left */ if (q->count) { - s->update_irq(s->update_arg, 1); + ps2_raise_irq(s); } } return val; @@ -606,119 +584,123 @@ static void ps2_set_ledstate(PS2KbdState *s, int ledstate) static void ps2_reset_keyboard(PS2KbdState *s) { + PS2State *ps2 = PS2_DEVICE(s); + trace_ps2_reset_keyboard(s); s->scan_enabled = 1; s->scancode_set = 2; - ps2_reset_queue(&s->common); + ps2_reset_queue(ps2); ps2_set_ledstate(s, 0); } -void ps2_write_keyboard(void *opaque, int val) +void ps2_write_keyboard(PS2KbdState *s, int val) { - PS2KbdState *s = (PS2KbdState *)opaque; + PS2State *ps2 = PS2_DEVICE(s); - trace_ps2_write_keyboard(opaque, val); - ps2_cqueue_reset(&s->common); - switch(s->common.write_cmd) { + trace_ps2_write_keyboard(s, val); + ps2_cqueue_reset(ps2); + switch (ps2->write_cmd) { default: case -1: - switch(val) { + switch (val) { case 0x00: - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(ps2, KBD_REPLY_ACK); break; case 0x05: - ps2_cqueue_1(&s->common, KBD_REPLY_RESEND); + ps2_cqueue_1(ps2, KBD_REPLY_RESEND); break; case KBD_CMD_GET_ID: /* We emulate a MF2 AT keyboard here */ - ps2_cqueue_3(&s->common, KBD_REPLY_ACK, KBD_REPLY_ID, - s->translate ? 0x41 : 0x83); + ps2_cqueue_3(ps2, KBD_REPLY_ACK, KBD_REPLY_ID, + s->translate ? 0x41 : 0x83); break; case KBD_CMD_ECHO: - ps2_cqueue_1(&s->common, KBD_CMD_ECHO); + ps2_cqueue_1(ps2, KBD_CMD_ECHO); break; case KBD_CMD_ENABLE: s->scan_enabled = 1; - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(ps2, KBD_REPLY_ACK); break; 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_cqueue_1(&s->common, KBD_REPLY_ACK); + ps2->write_cmd = val; + ps2_cqueue_1(ps2, KBD_REPLY_ACK); break; case KBD_CMD_RESET_DISABLE: ps2_reset_keyboard(s); s->scan_enabled = 0; - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(ps2, KBD_REPLY_ACK); break; case KBD_CMD_RESET_ENABLE: ps2_reset_keyboard(s); s->scan_enabled = 1; - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(ps2, KBD_REPLY_ACK); break; case KBD_CMD_RESET: ps2_reset_keyboard(s); - ps2_cqueue_2(&s->common, - KBD_REPLY_ACK, - KBD_REPLY_POR); + ps2_cqueue_2(ps2, + KBD_REPLY_ACK, + KBD_REPLY_POR); break; case KBD_CMD_SET_TYPEMATIC: - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(ps2, KBD_REPLY_ACK); break; default: - ps2_cqueue_1(&s->common, KBD_REPLY_RESEND); + ps2_cqueue_1(ps2, KBD_REPLY_RESEND); break; } break; case KBD_CMD_SET_MAKE_BREAK: - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); - s->common.write_cmd = -1; + ps2_cqueue_1(ps2, KBD_REPLY_ACK); + ps2->write_cmd = -1; break; case KBD_CMD_SCANCODE: if (val == 0) { - ps2_cqueue_2(&s->common, KBD_REPLY_ACK, s->translate ? + ps2_cqueue_2(ps2, KBD_REPLY_ACK, s->translate ? translate_table[s->scancode_set] : s->scancode_set); } else if (val >= 1 && val <= 3) { s->scancode_set = val; - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); + ps2_cqueue_1(ps2, KBD_REPLY_ACK); } else { - ps2_cqueue_1(&s->common, KBD_REPLY_RESEND); + ps2_cqueue_1(ps2, KBD_REPLY_RESEND); } - s->common.write_cmd = -1; + ps2->write_cmd = -1; break; case KBD_CMD_SET_LEDS: ps2_set_ledstate(s, val); - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); - s->common.write_cmd = -1; + ps2_cqueue_1(ps2, KBD_REPLY_ACK); + ps2->write_cmd = -1; break; case KBD_CMD_SET_RATE: - ps2_cqueue_1(&s->common, KBD_REPLY_ACK); - s->common.write_cmd = -1; + ps2_cqueue_1(ps2, KBD_REPLY_ACK); + ps2->write_cmd = -1; break; } } -/* Set the scancode translation mode. - 0 = raw scancodes. - 1 = translated scancodes (used by qemu internally). */ +/* + * Set the scancode translation mode. + * 0 = raw scancodes. + * 1 = translated scancodes (used by qemu internally). + */ -void ps2_keyboard_set_translation(void *opaque, int mode) +void ps2_keyboard_set_translation(PS2KbdState *s, int mode) { - PS2KbdState *s = (PS2KbdState *)opaque; - trace_ps2_keyboard_set_translation(opaque, mode); + trace_ps2_keyboard_set_translation(s, mode); s->translate = mode; } static int ps2_mouse_send_packet(PS2MouseState *s) { + PS2State *ps2 = PS2_DEVICE(s); /* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */ const int needed = s->mouse_type ? 4 : 3; unsigned int b; int dx1, dy1, dz1, dw1; - if (PS2_QUEUE_SIZE - s->common.queue.count < needed) { + if (PS2_QUEUE_SIZE - ps2->queue.count < needed) { return 0; } @@ -727,31 +709,34 @@ static int ps2_mouse_send_packet(PS2MouseState *s) dz1 = s->mouse_dz; dw1 = s->mouse_dw; /* XXX: increase range to 8 bits ? */ - if (dx1 > 127) + if (dx1 > 127) { dx1 = 127; - else if (dx1 < -127) + } else if (dx1 < -127) { dx1 = -127; - if (dy1 > 127) + } + if (dy1 > 127) { dy1 = 127; - else if (dy1 < -127) + } else if (dy1 < -127) { dy1 = -127; + } b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07); - ps2_queue_noirq(&s->common, b); - ps2_queue_noirq(&s->common, dx1 & 0xff); - ps2_queue_noirq(&s->common, dy1 & 0xff); + ps2_queue_noirq(ps2, b); + ps2_queue_noirq(ps2, dx1 & 0xff); + ps2_queue_noirq(ps2, dy1 & 0xff); /* extra byte for IMPS/2 or IMEX */ - switch(s->mouse_type) { + switch (s->mouse_type) { default: /* Just ignore the wheels if not supported */ s->mouse_dz = 0; s->mouse_dw = 0; break; case 3: - if (dz1 > 127) + if (dz1 > 127) { dz1 = 127; - else if (dz1 < -127) - dz1 = -127; - ps2_queue_noirq(&s->common, dz1 & 0xff); + } else if (dz1 < -127) { + dz1 = -127; + } + ps2_queue_noirq(ps2, dz1 & 0xff); s->mouse_dz -= dz1; s->mouse_dw = 0; break; @@ -787,11 +772,11 @@ static int ps2_mouse_send_packet(PS2MouseState *s) b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1); s->mouse_dz -= dz1; } - ps2_queue_noirq(&s->common, b); + ps2_queue_noirq(ps2, b); break; } - ps2_raise_irq(&s->common); + ps2_raise_irq(ps2); trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b); /* update deltas */ @@ -816,8 +801,9 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src, InputBtnEvent *btn; /* check if deltas are recorded when disabled */ - if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) + if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) { return; + } switch (evt->type) { case INPUT_EVENT_KIND_REL: @@ -868,8 +854,10 @@ static void ps2_mouse_sync(DeviceState *dev) qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); } if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) { - /* if not remote, send event. Multiple events are sent if - too big deltas */ + /* + * if not remote, send event. Multiple events are sent if + * too big deltas + */ while (ps2_mouse_send_packet(s)) { if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 && s->mouse_dw == 0) { @@ -879,96 +867,95 @@ static void ps2_mouse_sync(DeviceState *dev) } } -void ps2_mouse_fake_event(void *opaque) +void ps2_mouse_fake_event(PS2MouseState *s) { - PS2MouseState *s = opaque; - trace_ps2_mouse_fake_event(opaque); + trace_ps2_mouse_fake_event(s); s->mouse_dx++; - ps2_mouse_sync(opaque); + ps2_mouse_sync(DEVICE(s)); } -void ps2_write_mouse(void *opaque, int val) +void ps2_write_mouse(PS2MouseState *s, int val) { - PS2MouseState *s = (PS2MouseState *)opaque; + PS2State *ps2 = PS2_DEVICE(s); - trace_ps2_write_mouse(opaque, val); - switch(s->common.write_cmd) { + trace_ps2_write_mouse(s, val); + switch (ps2->write_cmd) { default: case -1: /* mouse command */ if (s->mouse_wrap) { if (val == AUX_RESET_WRAP) { s->mouse_wrap = 0; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); return; } else if (val != AUX_RESET) { - ps2_queue(&s->common, val); + ps2_queue(ps2, val); return; } } - switch(val) { + switch (val) { case AUX_SET_SCALE11: s->mouse_status &= ~MOUSE_STATUS_SCALE21; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_SET_SCALE21: s->mouse_status |= MOUSE_STATUS_SCALE21; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_SET_STREAM: s->mouse_status &= ~MOUSE_STATUS_REMOTE; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_SET_WRAP: s->mouse_wrap = 1; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_SET_REMOTE: s->mouse_status |= MOUSE_STATUS_REMOTE; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_GET_TYPE: - ps2_queue_2(&s->common, + ps2_queue_2(ps2, AUX_ACK, s->mouse_type); break; case AUX_SET_RES: case AUX_SET_SAMPLE: - s->common.write_cmd = val; - ps2_queue(&s->common, AUX_ACK); + ps2->write_cmd = val; + ps2_queue(ps2, AUX_ACK); break; case AUX_GET_SCALE: - ps2_queue_4(&s->common, + ps2_queue_4(ps2, AUX_ACK, s->mouse_status, s->mouse_resolution, s->mouse_sample_rate); break; case AUX_POLL: - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); ps2_mouse_send_packet(s); break; case AUX_ENABLE_DEV: s->mouse_status |= MOUSE_STATUS_ENABLED; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_DISABLE_DEV: s->mouse_status &= ~MOUSE_STATUS_ENABLED; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_SET_DEFAULT: s->mouse_sample_rate = 100; s->mouse_resolution = 2; s->mouse_status = 0; - ps2_queue(&s->common, AUX_ACK); + ps2_queue(ps2, AUX_ACK); break; case AUX_RESET: s->mouse_sample_rate = 100; s->mouse_resolution = 2; s->mouse_status = 0; s->mouse_type = 0; - ps2_reset_queue(&s->common); - ps2_queue_3(&s->common, + ps2_reset_queue(ps2); + ps2_queue_3(ps2, AUX_ACK, 0xaa, s->mouse_type); @@ -980,47 +967,59 @@ void ps2_write_mouse(void *opaque, int val) case AUX_SET_SAMPLE: s->mouse_sample_rate = val; /* detect IMPS/2 or IMEX */ - switch(s->mouse_detect_state) { + switch (s->mouse_detect_state) { default: case 0: - if (val == 200) + if (val == 200) { s->mouse_detect_state = 1; + } break; case 1: - if (val == 100) + if (val == 100) { s->mouse_detect_state = 2; - else if (val == 200) + } else if (val == 200) { s->mouse_detect_state = 3; - else + } else { s->mouse_detect_state = 0; + } break; case 2: - if (val == 80) + if (val == 80) { s->mouse_type = 3; /* IMPS/2 */ + } s->mouse_detect_state = 0; break; case 3: - if (val == 80) + if (val == 80) { s->mouse_type = 4; /* IMEX */ + } s->mouse_detect_state = 0; break; } - ps2_queue(&s->common, AUX_ACK); - s->common.write_cmd = -1; + ps2_queue(ps2, AUX_ACK); + ps2->write_cmd = -1; break; case AUX_SET_RES: s->mouse_resolution = val; - ps2_queue(&s->common, AUX_ACK); - s->common.write_cmd = -1; + ps2_queue(ps2, AUX_ACK); + ps2->write_cmd = -1; break; } } -static void ps2_common_reset(PS2State *s) +static void ps2_reset_hold(Object *obj) { + PS2State *s = PS2_DEVICE(obj); + s->write_cmd = -1; ps2_reset_queue(s); - s->update_irq(s->update_arg, 0); +} + +static void ps2_reset_exit(Object *obj) +{ + PS2State *s = PS2_DEVICE(obj); + + ps2_lower_irq(s); } static void ps2_common_post_load(PS2State *s) @@ -1049,24 +1048,34 @@ static void ps2_common_post_load(PS2State *s) q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1; } -static void ps2_kbd_reset(void *opaque) +static void ps2_kbd_reset_hold(Object *obj) { - PS2KbdState *s = (PS2KbdState *) opaque; + PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(obj); + PS2KbdState *s = PS2_KBD_DEVICE(obj); + + trace_ps2_kbd_reset(s); + + if (ps2dc->parent_phases.hold) { + ps2dc->parent_phases.hold(obj); + } - trace_ps2_kbd_reset(opaque); - ps2_common_reset(&s->common); s->scan_enabled = 1; s->translate = 0; s->scancode_set = 2; s->modifiers = 0; } -static void ps2_mouse_reset(void *opaque) +static void ps2_mouse_reset_hold(Object *obj) { - PS2MouseState *s = (PS2MouseState *) opaque; + PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(obj); + PS2MouseState *s = PS2_MOUSE_DEVICE(obj); + + trace_ps2_mouse_reset(s); + + if (ps2dc->parent_phases.hold) { + ps2dc->parent_phases.hold(obj); + } - trace_ps2_mouse_reset(opaque); - ps2_common_reset(&s->common); s->mouse_status = 0; s->mouse_resolution = 0; s->mouse_sample_rate = 0; @@ -1084,7 +1093,7 @@ static const VMStateDescription vmstate_ps2_common = { .name = "PS2 Common State", .version_id = 3, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(write_cmd, PS2State), VMSTATE_INT32(queue.rptr, PS2State), VMSTATE_INT32(queue.wptr, PS2State), @@ -1115,7 +1124,7 @@ static const VMStateDescription vmstate_ps2_keyboard_ledstate = { .minimum_version_id = 2, .post_load = ps2_kbd_ledstate_post_load, .needed = ps2_keyboard_ledstate_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(ledstate, PS2KbdState), VMSTATE_END_OF_LIST() } @@ -1132,7 +1141,7 @@ static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = { .version_id = 1, .minimum_version_id = 1, .needed = ps2_keyboard_need_high_bit_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(need_high_bit, PS2KbdState), VMSTATE_END_OF_LIST() } @@ -1141,26 +1150,28 @@ static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = { static bool ps2_keyboard_cqueue_needed(void *opaque) { PS2KbdState *s = opaque; + PS2State *ps2 = PS2_DEVICE(s); - return s->common.queue.cwptr != -1; /* the queue is mostly empty */ + return ps2->queue.cwptr != -1; /* the queue is mostly empty */ } static const VMStateDescription vmstate_ps2_keyboard_cqueue = { .name = "ps2kbd/command_reply_queue", .needed = ps2_keyboard_cqueue_needed, - .fields = (VMStateField[]) { - VMSTATE_INT32(common.queue.cwptr, PS2KbdState), + .fields = (const VMStateField[]) { + VMSTATE_INT32(parent_obj.queue.cwptr, PS2KbdState), VMSTATE_END_OF_LIST() } }; -static int ps2_kbd_post_load(void* opaque, int version_id) +static int ps2_kbd_post_load(void *opaque, int version_id) { - PS2KbdState *s = (PS2KbdState*)opaque; - PS2State *ps2 = &s->common; + PS2KbdState *s = (PS2KbdState *)opaque; + PS2State *ps2 = PS2_DEVICE(s); - if (version_id == 2) - s->scancode_set=2; + if (version_id == 2) { + s->scancode_set = 2; + } ps2_common_post_load(ps2); @@ -1172,14 +1183,15 @@ static const VMStateDescription vmstate_ps2_keyboard = { .version_id = 3, .minimum_version_id = 2, .post_load = ps2_kbd_post_load, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State), + .fields = (const VMStateField[]) { + VMSTATE_STRUCT(parent_obj, PS2KbdState, 0, vmstate_ps2_common, + PS2State), VMSTATE_INT32(scan_enabled, PS2KbdState), VMSTATE_INT32(translate, PS2KbdState), - VMSTATE_INT32_V(scancode_set, PS2KbdState,3), + VMSTATE_INT32_V(scancode_set, PS2KbdState, 3), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_ps2_keyboard_ledstate, &vmstate_ps2_keyboard_need_high_bit, &vmstate_ps2_keyboard_cqueue, @@ -1190,7 +1202,7 @@ static const VMStateDescription vmstate_ps2_keyboard = { static int ps2_mouse_post_load(void *opaque, int version_id) { PS2MouseState *s = (PS2MouseState *)opaque; - PS2State *ps2 = &s->common; + PS2State *ps2 = PS2_DEVICE(s); ps2_common_post_load(ps2); @@ -1202,8 +1214,9 @@ static const VMStateDescription vmstate_ps2_mouse = { .version_id = 2, .minimum_version_id = 2, .post_load = ps2_mouse_post_load, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State), + .fields = (const VMStateField[]) { + VMSTATE_STRUCT(parent_obj, PS2MouseState, 0, vmstate_ps2_common, + PS2State), VMSTATE_UINT8(mouse_status, PS2MouseState), VMSTATE_UINT8(mouse_resolution, PS2MouseState), VMSTATE_UINT8(mouse_sample_rate, PS2MouseState), @@ -1218,44 +1231,99 @@ static const VMStateDescription vmstate_ps2_mouse = { } }; -static QemuInputHandler ps2_keyboard_handler = { +static const QemuInputHandler ps2_keyboard_handler = { .name = "QEMU PS/2 Keyboard", .mask = INPUT_EVENT_MASK_KEY, .event = ps2_keyboard_event, }; -void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg) +static void ps2_kbd_realize(DeviceState *dev, Error **errp) { - PS2KbdState *s = g_new0(PS2KbdState, 1); - - trace_ps2_kbd_init(s); - s->common.update_irq = update_irq; - s->common.update_arg = update_arg; - s->scancode_set = 2; - vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s); - qemu_input_handler_register((DeviceState *)s, - &ps2_keyboard_handler); - qemu_register_reset(ps2_kbd_reset, s); - return s; + qemu_input_handler_register(dev, &ps2_keyboard_handler); } -static QemuInputHandler ps2_mouse_handler = { +static const QemuInputHandler ps2_mouse_handler = { .name = "QEMU PS/2 Mouse", .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL, .event = ps2_mouse_event, .sync = ps2_mouse_sync, }; -void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg) +static void ps2_mouse_realize(DeviceState *dev, Error **errp) { - PS2MouseState *s = g_new0(PS2MouseState, 1); - - trace_ps2_mouse_init(s); - s->common.update_irq = update_irq; - s->common.update_arg = update_arg; - vmstate_register(NULL, 0, &vmstate_ps2_mouse, s); - qemu_input_handler_register((DeviceState *)s, - &ps2_mouse_handler); - qemu_register_reset(ps2_mouse_reset, s); - return s; + qemu_input_handler_register(dev, &ps2_mouse_handler); } + +static void ps2_kbd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + PS2DeviceClass *ps2dc = PS2_DEVICE_CLASS(klass); + + dc->realize = ps2_kbd_realize; + resettable_class_set_parent_phases(rc, NULL, ps2_kbd_reset_hold, NULL, + &ps2dc->parent_phases); + dc->vmsd = &vmstate_ps2_keyboard; +} + +static const TypeInfo ps2_kbd_info = { + .name = TYPE_PS2_KBD_DEVICE, + .parent = TYPE_PS2_DEVICE, + .instance_size = sizeof(PS2KbdState), + .class_init = ps2_kbd_class_init +}; + +static void ps2_mouse_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + PS2DeviceClass *ps2dc = PS2_DEVICE_CLASS(klass); + + dc->realize = ps2_mouse_realize; + resettable_class_set_parent_phases(rc, NULL, ps2_mouse_reset_hold, NULL, + &ps2dc->parent_phases); + dc->vmsd = &vmstate_ps2_mouse; +} + +static const TypeInfo ps2_mouse_info = { + .name = TYPE_PS2_MOUSE_DEVICE, + .parent = TYPE_PS2_DEVICE, + .instance_size = sizeof(PS2MouseState), + .class_init = ps2_mouse_class_init +}; + +static void ps2_init(Object *obj) +{ + PS2State *s = PS2_DEVICE(obj); + + qdev_init_gpio_out(DEVICE(obj), &s->irq, 1); +} + +static void ps2_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = ps2_reset_hold; + rc->phases.exit = ps2_reset_exit; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo ps2_info = { + .name = TYPE_PS2_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = ps2_init, + .instance_size = sizeof(PS2State), + .class_init = ps2_class_init, + .class_size = sizeof(PS2DeviceClass), + .abstract = true +}; + +static void ps2_register_types(void) +{ + type_register_static(&ps2_info); + type_register_static(&ps2_kbd_info); + type_register_static(&ps2_mouse_info); +} + +type_init(ps2_register_types) diff --git a/hw/input/pxa2xx_keypad.c b/hw/input/pxa2xx_keypad.c index 3dd03e8c9f..3858648d9f 100644 --- a/hw/input/pxa2xx_keypad.c +++ b/hw/input/pxa2xx_keypad.c @@ -288,7 +288,7 @@ static const VMStateDescription vmstate_pxa2xx_keypad = { .name = "pxa2xx_keypad", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(kpc, PXA2xxKeyPadState), VMSTATE_UINT32(kpdk, PXA2xxKeyPadState), VMSTATE_UINT32(kprec, PXA2xxKeyPadState), diff --git a/hw/input/stellaris_gamepad.c b/hw/input/stellaris_gamepad.c new file mode 100644 index 0000000000..17ee42b9fc --- /dev/null +++ b/hw/input/stellaris_gamepad.c @@ -0,0 +1,107 @@ +/* + * Gamepad style buttons connected to IRQ/GPIO lines + * + * Copyright (c) 2007 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the GPL. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/input/stellaris_gamepad.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "ui/console.h" + +static void stellaris_gamepad_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) +{ + StellarisGamepad *s = STELLARIS_GAMEPAD(dev); + InputKeyEvent *key = evt->u.key.data; + int qcode = qemu_input_key_value_to_qcode(key->key); + int i; + + for (i = 0; i < s->num_buttons; i++) { + if (s->keycodes[i] == qcode && s->pressed[i] != key->down) { + s->pressed[i] = key->down; + qemu_set_irq(s->irqs[i], key->down); + } + } +} + +static const VMStateDescription vmstate_stellaris_gamepad = { + .name = "stellaris_gamepad", + .version_id = 4, + .minimum_version_id = 4, + .fields = (const VMStateField[]) { + VMSTATE_VARRAY_UINT32(pressed, StellarisGamepad, num_buttons, + 0, vmstate_info_uint8, uint8_t), + VMSTATE_END_OF_LIST() + } +}; + +static const QemuInputHandler stellaris_gamepad_handler = { + .name = "Stellaris Gamepad", + .mask = INPUT_EVENT_MASK_KEY, + .event = stellaris_gamepad_event, +}; + +static void stellaris_gamepad_realize(DeviceState *dev, Error **errp) +{ + StellarisGamepad *s = STELLARIS_GAMEPAD(dev); + + if (s->num_buttons == 0) { + error_setg(errp, "keycodes property array must be set"); + return; + } + + s->irqs = g_new0(qemu_irq, s->num_buttons); + s->pressed = g_new0(uint8_t, s->num_buttons); + qdev_init_gpio_out(dev, s->irqs, s->num_buttons); + qemu_input_handler_register(dev, &stellaris_gamepad_handler); +} + +static void stellaris_gamepad_finalize(Object *obj) +{ + StellarisGamepad *s = STELLARIS_GAMEPAD(obj); + + g_free(s->keycodes); +} + +static void stellaris_gamepad_reset_enter(Object *obj, ResetType type) +{ + StellarisGamepad *s = STELLARIS_GAMEPAD(obj); + + memset(s->pressed, 0, s->num_buttons * sizeof(uint8_t)); +} + +static Property stellaris_gamepad_properties[] = { + DEFINE_PROP_ARRAY("keycodes", StellarisGamepad, num_buttons, + keycodes, qdev_prop_uint32, uint32_t), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stellaris_gamepad_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.enter = stellaris_gamepad_reset_enter; + dc->realize = stellaris_gamepad_realize; + dc->vmsd = &vmstate_stellaris_gamepad; + device_class_set_props(dc, stellaris_gamepad_properties); +} + +static const TypeInfo stellaris_gamepad_info[] = { + { + .name = TYPE_STELLARIS_GAMEPAD, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(StellarisGamepad), + .instance_finalize = stellaris_gamepad_finalize, + .class_init = stellaris_gamepad_class_init, + }, +}; + +DEFINE_TYPES(stellaris_gamepad_info); diff --git a/hw/input/stellaris_input.c b/hw/input/stellaris_input.c deleted file mode 100644 index e6ee5e11f1..0000000000 --- a/hw/input/stellaris_input.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Gamepad style buttons connected to IRQ/GPIO lines - * - * Copyright (c) 2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/input/gamepad.h" -#include "hw/irq.h" -#include "migration/vmstate.h" -#include "ui/console.h" - -typedef struct { - qemu_irq irq; - int keycode; - uint8_t pressed; -} gamepad_button; - -typedef struct { - gamepad_button *buttons; - int num_buttons; - int extension; -} gamepad_state; - -static void stellaris_gamepad_put_key(void * opaque, int keycode) -{ - gamepad_state *s = (gamepad_state *)opaque; - int i; - int down; - - if (keycode == 0xe0 && !s->extension) { - s->extension = 0x80; - return; - } - - down = (keycode & 0x80) == 0; - keycode = (keycode & 0x7f) | s->extension; - - for (i = 0; i < s->num_buttons; i++) { - if (s->buttons[i].keycode == keycode - && s->buttons[i].pressed != down) { - s->buttons[i].pressed = down; - qemu_set_irq(s->buttons[i].irq, down); - } - } - - s->extension = 0; -} - -static const VMStateDescription vmstate_stellaris_button = { - .name = "stellaris_button", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8(pressed, gamepad_button), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_stellaris_gamepad = { - .name = "stellaris_gamepad", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_INT32(extension, gamepad_state), - VMSTATE_STRUCT_VARRAY_POINTER_INT32(buttons, gamepad_state, - num_buttons, - vmstate_stellaris_button, - gamepad_button), - VMSTATE_END_OF_LIST() - } -}; - -/* Returns an array of 5 output slots. */ -void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode) -{ - gamepad_state *s; - int i; - - s = g_new0(gamepad_state, 1); - s->buttons = g_new0(gamepad_button, n); - for (i = 0; i < n; i++) { - s->buttons[i].irq = irq[i]; - s->buttons[i].keycode = keycode[i]; - } - s->num_buttons = n; - qemu_add_kbd_event_handler(stellaris_gamepad_put_key, 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 e0bfe7f3ee..29001a827d 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -41,8 +41,6 @@ ps2_mouse_fake_event(void *opaque) "%p" ps2_write_mouse(void *opaque, int val) "%p val %d" ps2_kbd_reset(void *opaque) "%p" ps2_mouse_reset(void *opaque) "%p" -ps2_kbd_init(void *s) "%p" -ps2_mouse_init(void *s) "%p" # hid.c hid_kbd_queue_full(void) "queue full" diff --git a/hw/input/tsc2005.c b/hw/input/tsc2005.c index 14698ce109..941f163d36 100644 --- a/hw/input/tsc2005.c +++ b/hw/input/tsc2005.c @@ -157,14 +157,14 @@ static uint16_t tsc2005_read(TSC2005State *s, int reg) s->reset = true; return ret; - case 0x8: /* AUX high treshold */ + case 0x8: /* AUX high threshold */ return s->aux_thr[1]; - case 0x9: /* AUX low treshold */ + case 0x9: /* AUX low threshold */ return s->aux_thr[0]; - case 0xa: /* TEMP high treshold */ + case 0xa: /* TEMP high threshold */ return s->temp_thr[1]; - case 0xb: /* TEMP low treshold */ + case 0xb: /* TEMP low threshold */ return s->temp_thr[0]; case 0xc: /* CFR0 */ @@ -186,17 +186,17 @@ static uint16_t tsc2005_read(TSC2005State *s, int reg) static void tsc2005_write(TSC2005State *s, int reg, uint16_t data) { switch (reg) { - case 0x8: /* AUX high treshold */ + case 0x8: /* AUX high threshold */ s->aux_thr[1] = data; break; - case 0x9: /* AUX low treshold */ + case 0x9: /* AUX low threshold */ s->aux_thr[0] = data; break; - case 0xa: /* TEMP high treshold */ + case 0xa: /* TEMP high threshold */ s->temp_thr[1] = data; break; - case 0xb: /* TEMP low treshold */ + case 0xb: /* TEMP low threshold */ s->temp_thr[0] = data; break; @@ -454,7 +454,7 @@ static const VMStateDescription vmstate_tsc2005 = { .version_id = 2, .minimum_version_id = 2, .post_load = tsc2005_post_load, - .fields = (VMStateField []) { + .fields = (const VMStateField []) { VMSTATE_BOOL(pressure, TSC2005State), VMSTATE_BOOL(irq, TSC2005State), VMSTATE_BOOL(command, TSC2005State), @@ -523,7 +523,7 @@ void *tsc2005_init(qemu_irq pintdav) * from the touchscreen. Assuming 12-bit precision was used during * tslib calibration. */ -void tsc2005_set_transform(void *opaque, MouseTransformInfo *info) +void tsc2005_set_transform(void *opaque, const MouseTransformInfo *info) { TSC2005State *s = (TSC2005State *) opaque; diff --git a/hw/input/tsc210x.c b/hw/input/tsc210x.c index df7313db5d..c4e32c7a42 100644 --- a/hw/input/tsc210x.c +++ b/hw/input/tsc210x.c @@ -20,7 +20,6 @@ */ #include "qemu/osdep.h" -#include "qemu/log.h" #include "hw/hw.h" #include "audio/audio.h" #include "qemu/timer.h" @@ -28,9 +27,11 @@ #include "sysemu/reset.h" #include "ui/console.h" #include "hw/arm/omap.h" /* For I2SCodec */ +#include "hw/boards.h" /* for current_machine */ #include "hw/input/tsc2xxx.h" #include "hw/irq.h" #include "migration/vmstate.h" +#include "qapi/error.h" #define TSC_DATA_REGISTERS_PAGE 0x0 #define TSC_CONTROL_REGISTERS_PAGE 0x1 @@ -1016,7 +1017,7 @@ static int tsc210x_post_load(void *opaque, int version_id) return 0; } -static VMStateField vmstatefields_tsc210x[] = { +static const VMStateField vmstatefields_tsc210x[] = { VMSTATE_BOOL(enabled, TSC210xState), VMSTATE_BOOL(host_mode, TSC210xState), VMSTATE_BOOL(irq, TSC210xState), @@ -1070,20 +1071,10 @@ static const VMStateDescription vmstate_tsc2301 = { .fields = vmstatefields_tsc210x, }; -uWireSlave *tsc2102_init(qemu_irq pint) +static void tsc210x_init(TSC210xState *s, + const char *name, + const VMStateDescription *vmsd) { - TSC210xState *s; - - s = g_new0(TSC210xState, 1); - s->x = 160; - s->y = 160; - s->pressure = 0; - s->precision = s->nextprecision = 0; - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tsc210x_timer_tick, s); - s->pint = pint; - s->model = 0x2102; - s->name = "tsc2102"; - s->tr[0] = 0; s->tr[1] = 1; s->tr[2] = 1; @@ -1105,13 +1096,33 @@ uWireSlave *tsc2102_init(qemu_irq pint) tsc210x_reset(s); - qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, - "QEMU TSC2102-driven Touchscreen"); + qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, name); - AUD_register_card(s->name, &s->card); + if (current_machine->audiodev) { + s->card.name = g_strdup(current_machine->audiodev); + s->card.state = audio_state_by_name(s->card.name, &error_fatal); + } + AUD_register_card(s->name, &s->card, &error_fatal); qemu_register_reset((void *) tsc210x_reset, s); - vmstate_register(NULL, 0, &vmstate_tsc2102, s); + vmstate_register(NULL, 0, vmsd, s); +} + +uWireSlave *tsc2102_init(qemu_irq pint) +{ + TSC210xState *s; + + s = g_new0(TSC210xState, 1); + s->x = 160; + s->y = 160; + s->pressure = 0; + s->precision = s->nextprecision = 0; + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tsc210x_timer_tick, s); + s->pint = pint; + s->model = 0x2102; + s->name = "tsc2102"; + + tsc210x_init(s, "QEMU TSC2102-driven Touchscreen", &vmstate_tsc2102); return &s->chip; } @@ -1132,34 +1143,7 @@ uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav) s->model = 0x2301; s->name = "tsc2301"; - s->tr[0] = 0; - s->tr[1] = 1; - s->tr[2] = 1; - s->tr[3] = 0; - s->tr[4] = 1; - s->tr[5] = 0; - s->tr[6] = 1; - s->tr[7] = 0; - - s->chip.opaque = s; - s->chip.send = (void *) tsc210x_write; - s->chip.receive = (void *) tsc210x_read; - - s->codec.opaque = s; - s->codec.tx_swallow = (void *) tsc210x_i2s_swallow; - s->codec.set_rate = (void *) tsc210x_i2s_set_rate; - s->codec.in.fifo = s->in_fifo; - s->codec.out.fifo = s->out_fifo; - - tsc210x_reset(s); - - qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, - "QEMU TSC2301-driven Touchscreen"); - - AUD_register_card(s->name, &s->card); - - qemu_register_reset((void *) tsc210x_reset, s); - vmstate_register(NULL, 0, &vmstate_tsc2301, s); + tsc210x_init(s, "QEMU TSC2301-driven Touchscreen", &vmstate_tsc2301); return &s->chip; } @@ -1176,8 +1160,7 @@ I2SCodec *tsc210x_codec(uWireSlave *chip) * from the touchscreen. Assuming 12-bit precision was used during * tslib calibration. */ -void tsc210x_set_transform(uWireSlave *chip, - MouseTransformInfo *info) +void tsc210x_set_transform(uWireSlave *chip, const MouseTransformInfo *info) { TSC210xState *s = (TSC210xState *) chip->opaque; #if 0 diff --git a/hw/input/vhost-user-input.c b/hw/input/vhost-user-input.c deleted file mode 100644 index 1352e372ff..0000000000 --- a/hw/input/vhost-user-input.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * This work is licensed under the terms of the GNU GPL, version 2 or - * (at your option) any later version. See the COPYING file in the - * top-level directory. - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qapi/error.h" - -#include "hw/virtio/virtio-input.h" - -static int vhost_input_config_change(struct vhost_dev *dev) -{ - error_report("vhost-user-input: unhandled backend config change"); - return -1; -} - -static const VhostDevConfigOps config_ops = { - .vhost_dev_config_notifier = vhost_input_config_change, -}; - -static void vhost_input_realize(DeviceState *dev, Error **errp) -{ - VHostUserInput *vhi = VHOST_USER_INPUT(dev); - VirtIOInput *vinput = VIRTIO_INPUT(dev); - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - - vhost_dev_set_config_notifier(&vhi->vhost->dev, &config_ops); - vinput->cfg_size = sizeof_field(virtio_input_config, u); - if (vhost_user_backend_dev_init(vhi->vhost, vdev, 2, errp) == -1) { - return; - } -} - -static void vhost_input_change_active(VirtIOInput *vinput) -{ - VHostUserInput *vhi = VHOST_USER_INPUT(vinput); - - if (vinput->active) { - vhost_user_backend_start(vhi->vhost); - } else { - vhost_user_backend_stop(vhi->vhost); - } -} - -static void vhost_input_get_config(VirtIODevice *vdev, uint8_t *config_data) -{ - VirtIOInput *vinput = VIRTIO_INPUT(vdev); - VHostUserInput *vhi = VHOST_USER_INPUT(vdev); - Error *local_err = NULL; - int ret; - - memset(config_data, 0, vinput->cfg_size); - - ret = vhost_dev_get_config(&vhi->vhost->dev, config_data, vinput->cfg_size, - &local_err); - if (ret) { - error_report_err(local_err); - return; - } -} - -static void vhost_input_set_config(VirtIODevice *vdev, - const uint8_t *config_data) -{ - VHostUserInput *vhi = VHOST_USER_INPUT(vdev); - int ret; - - ret = vhost_dev_set_config(&vhi->vhost->dev, config_data, - 0, sizeof(virtio_input_config), - VHOST_SET_CONFIG_TYPE_MASTER); - if (ret) { - error_report("vhost-user-input: set device config space failed"); - return; - } - - virtio_notify_config(vdev); -} - -static struct vhost_dev *vhost_input_get_vhost(VirtIODevice *vdev) -{ - VHostUserInput *vhi = VHOST_USER_INPUT(vdev); - return &vhi->vhost->dev; -} - -static const VMStateDescription vmstate_vhost_input = { - .name = "vhost-user-input", - .unmigratable = 1, -}; - -static void vhost_input_class_init(ObjectClass *klass, void *data) -{ - VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_vhost_input; - vdc->get_config = vhost_input_get_config; - vdc->set_config = vhost_input_set_config; - vdc->get_vhost = vhost_input_get_vhost; - vic->realize = vhost_input_realize; - vic->change_active = vhost_input_change_active; -} - -static void vhost_input_init(Object *obj) -{ - VHostUserInput *vhi = VHOST_USER_INPUT(obj); - - vhi->vhost = VHOST_USER_BACKEND(object_new(TYPE_VHOST_USER_BACKEND)); - object_property_add_alias(obj, "chardev", - OBJECT(vhi->vhost), "chardev"); -} - -static void vhost_input_finalize(Object *obj) -{ - VHostUserInput *vhi = VHOST_USER_INPUT(obj); - - object_unref(OBJECT(vhi->vhost)); -} - -static const TypeInfo vhost_input_info = { - .name = TYPE_VHOST_USER_INPUT, - .parent = TYPE_VIRTIO_INPUT, - .instance_size = sizeof(VHostUserInput), - .instance_init = vhost_input_init, - .instance_finalize = vhost_input_finalize, - .class_init = vhost_input_class_init, -}; - -static void vhost_input_register_types(void) -{ - type_register_static(&vhost_input_info); -} - -type_init(vhost_input_register_types) diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c index a7a244a95d..45e4d4c75d 100644 --- a/hw/input/virtio-input-hid.c +++ b/hw/input/virtio-input-hid.c @@ -16,9 +16,10 @@ #include "standard-headers/linux/input.h" -#define VIRTIO_ID_NAME_KEYBOARD "QEMU Virtio Keyboard" -#define VIRTIO_ID_NAME_MOUSE "QEMU Virtio Mouse" -#define VIRTIO_ID_NAME_TABLET "QEMU Virtio Tablet" +#define VIRTIO_ID_NAME_KEYBOARD "QEMU Virtio Keyboard" +#define VIRTIO_ID_NAME_MOUSE "QEMU Virtio Mouse" +#define VIRTIO_ID_NAME_TABLET "QEMU Virtio Tablet" +#define VIRTIO_ID_NAME_MULTITOUCH "QEMU Virtio MultiTouch" /* ----------------------------------------------------------------- */ @@ -30,6 +31,7 @@ static const unsigned short keymap_button[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_WHEEL_DOWN] = BTN_GEAR_DOWN, [INPUT_BUTTON_SIDE] = BTN_SIDE, [INPUT_BUTTON_EXTRA] = BTN_EXTRA, + [INPUT_BUTTON_TOUCH] = BTN_TOUCH, }; static const unsigned short axismap_rel[INPUT_AXIS__MAX] = { @@ -42,32 +44,38 @@ static const unsigned short axismap_abs[INPUT_AXIS__MAX] = { [INPUT_AXIS_Y] = ABS_Y, }; +static const unsigned short axismap_tch[INPUT_AXIS__MAX] = { + [INPUT_AXIS_X] = ABS_MT_POSITION_X, + [INPUT_AXIS_Y] = ABS_MT_POSITION_Y, +}; + /* ----------------------------------------------------------------- */ -static void virtio_input_key_config(VirtIOInput *vinput, - const unsigned short *keymap, - size_t mapsize) +static void virtio_input_extend_config(VirtIOInput *vinput, + const unsigned short *map, + size_t mapsize, + uint8_t select, uint8_t subsel) { - virtio_input_config keys; + virtio_input_config ext; int i, bit, byte, bmax = 0; - memset(&keys, 0, sizeof(keys)); + memset(&ext, 0, sizeof(ext)); for (i = 0; i < mapsize; i++) { - bit = keymap[i]; + bit = map[i]; if (!bit) { continue; } byte = bit / 8; bit = bit % 8; - keys.u.bitmap[byte] |= (1 << bit); + ext.u.bitmap[byte] |= (1 << bit); if (bmax < byte+1) { bmax = byte+1; } } - keys.select = VIRTIO_INPUT_CFG_EV_BITS; - keys.subsel = EV_KEY; - keys.size = bmax; - virtio_input_add_config(vinput, &keys); + ext.select = select; + ext.subsel = subsel; + ext.size = bmax; + virtio_input_add_config(vinput, &ext); } static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src, @@ -80,6 +88,7 @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src, InputKeyEvent *key; InputMoveEvent *move; InputBtnEvent *btn; + InputMultiTouchEvent *mtt; switch (evt->type) { case INPUT_EVENT_KIND_KEY: @@ -136,6 +145,24 @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src, event.value = cpu_to_le32(move->value); virtio_input_send(vinput, &event); break; + case INPUT_EVENT_KIND_MTT: + mtt = evt->u.mtt.data; + if (mtt->type == INPUT_MULTI_TOUCH_TYPE_DATA) { + event.type = cpu_to_le16(EV_ABS); + event.code = cpu_to_le16(axismap_tch[mtt->axis]); + event.value = cpu_to_le32(mtt->value); + virtio_input_send(vinput, &event); + } else { + event.type = cpu_to_le16(EV_ABS); + event.code = cpu_to_le16(ABS_MT_SLOT); + event.value = cpu_to_le32(mtt->slot); + virtio_input_send(vinput, &event); + event.type = cpu_to_le16(EV_ABS); + event.code = cpu_to_le16(ABS_MT_TRACKING_ID); + event.value = cpu_to_le32(mtt->tracking_id); + virtio_input_send(vinput, &event); + } + break; default: /* keep gcc happy */ break; @@ -238,7 +265,7 @@ static const TypeInfo virtio_input_hid_info = { /* ----------------------------------------------------------------- */ -static QemuInputHandler virtio_keyboard_handler = { +static const QemuInputHandler virtio_keyboard_handler = { .name = VIRTIO_ID_NAME_KEYBOARD, .mask = INPUT_EVENT_MASK_KEY, .event = virtio_input_handle_event, @@ -281,8 +308,9 @@ static void virtio_keyboard_init(Object *obj) vhid->handler = &virtio_keyboard_handler; virtio_input_init_config(vinput, virtio_keyboard_config); - virtio_input_key_config(vinput, qemu_input_map_qcode_to_linux, - qemu_input_map_qcode_to_linux_len); + virtio_input_extend_config(vinput, qemu_input_map_qcode_to_linux, + qemu_input_map_qcode_to_linux_len, + VIRTIO_INPUT_CFG_EV_BITS, EV_KEY); } static const TypeInfo virtio_keyboard_info = { @@ -294,7 +322,7 @@ static const TypeInfo virtio_keyboard_info = { /* ----------------------------------------------------------------- */ -static QemuInputHandler virtio_mouse_handler = { +static const QemuInputHandler virtio_mouse_handler = { .name = VIRTIO_ID_NAME_MOUSE, .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL, .event = virtio_input_handle_event, @@ -373,8 +401,9 @@ static void virtio_mouse_init(Object *obj) virtio_input_init_config(vinput, vhid->wheel_axis ? virtio_mouse_config_v2 : virtio_mouse_config_v1); - virtio_input_key_config(vinput, keymap_button, - ARRAY_SIZE(keymap_button)); + virtio_input_extend_config(vinput, keymap_button, + ARRAY_SIZE(keymap_button), + VIRTIO_INPUT_CFG_EV_BITS, EV_KEY); } static const TypeInfo virtio_mouse_info = { @@ -387,7 +416,7 @@ static const TypeInfo virtio_mouse_info = { /* ----------------------------------------------------------------- */ -static QemuInputHandler virtio_tablet_handler = { +static const QemuInputHandler virtio_tablet_handler = { .name = VIRTIO_ID_NAME_TABLET, .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, .event = virtio_input_handle_event, @@ -497,8 +526,9 @@ static void virtio_tablet_init(Object *obj) virtio_input_init_config(vinput, vhid->wheel_axis ? virtio_tablet_config_v2 : virtio_tablet_config_v1); - virtio_input_key_config(vinput, keymap_button, - ARRAY_SIZE(keymap_button)); + virtio_input_extend_config(vinput, keymap_button, + ARRAY_SIZE(keymap_button), + VIRTIO_INPUT_CFG_EV_BITS, EV_KEY); } static const TypeInfo virtio_tablet_info = { @@ -511,12 +541,98 @@ static const TypeInfo virtio_tablet_info = { /* ----------------------------------------------------------------- */ +static const QemuInputHandler virtio_multitouch_handler = { + .name = VIRTIO_ID_NAME_MULTITOUCH, + .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_MTT, + .event = virtio_input_handle_event, + .sync = virtio_input_handle_sync, +}; + +static struct virtio_input_config virtio_multitouch_config[] = { + { + .select = VIRTIO_INPUT_CFG_ID_NAME, + .size = sizeof(VIRTIO_ID_NAME_MULTITOUCH), + .u.string = VIRTIO_ID_NAME_MULTITOUCH, + },{ + .select = VIRTIO_INPUT_CFG_ID_DEVIDS, + .size = sizeof(struct virtio_input_devids), + .u.ids = { + .bustype = const_le16(BUS_VIRTUAL), + .vendor = const_le16(0x0627), /* same we use for usb hid devices */ + .product = const_le16(0x0003), + .version = const_le16(0x0001), + }, + },{ + .select = VIRTIO_INPUT_CFG_ABS_INFO, + .subsel = ABS_MT_SLOT, + .size = sizeof(virtio_input_absinfo), + .u.abs.min = const_le32(INPUT_EVENT_SLOTS_MIN), + .u.abs.max = const_le32(INPUT_EVENT_SLOTS_MAX), + },{ + .select = VIRTIO_INPUT_CFG_ABS_INFO, + .subsel = ABS_MT_TRACKING_ID, + .size = sizeof(virtio_input_absinfo), + .u.abs.min = const_le32(INPUT_EVENT_SLOTS_MIN), + .u.abs.max = const_le32(INPUT_EVENT_SLOTS_MAX), + },{ + .select = VIRTIO_INPUT_CFG_ABS_INFO, + .subsel = ABS_MT_POSITION_X, + .size = sizeof(virtio_input_absinfo), + .u.abs.min = const_le32(INPUT_EVENT_ABS_MIN), + .u.abs.max = const_le32(INPUT_EVENT_ABS_MAX), + },{ + .select = VIRTIO_INPUT_CFG_ABS_INFO, + .subsel = ABS_MT_POSITION_Y, + .size = sizeof(virtio_input_absinfo), + .u.abs.min = const_le32(INPUT_EVENT_ABS_MIN), + .u.abs.max = const_le32(INPUT_EVENT_ABS_MAX), + }, + { /* end of list */ }, +}; + +static void virtio_multitouch_init(Object *obj) +{ + VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj); + VirtIOInput *vinput = VIRTIO_INPUT(obj); + unsigned short abs_props[] = { + INPUT_PROP_DIRECT, + }; + unsigned short abs_bits[] = { + ABS_MT_SLOT, + ABS_MT_TRACKING_ID, + ABS_MT_POSITION_X, + ABS_MT_POSITION_Y, + }; + + vhid->handler = &virtio_multitouch_handler; + virtio_input_init_config(vinput, virtio_multitouch_config); + virtio_input_extend_config(vinput, keymap_button, + ARRAY_SIZE(keymap_button), + VIRTIO_INPUT_CFG_EV_BITS, EV_KEY); + virtio_input_extend_config(vinput, abs_props, + ARRAY_SIZE(abs_props), + VIRTIO_INPUT_CFG_PROP_BITS, 0); + virtio_input_extend_config(vinput, abs_bits, + ARRAY_SIZE(abs_bits), + VIRTIO_INPUT_CFG_EV_BITS, EV_ABS); +} + +static const TypeInfo virtio_multitouch_info = { + .name = TYPE_VIRTIO_MULTITOUCH, + .parent = TYPE_VIRTIO_INPUT_HID, + .instance_size = sizeof(VirtIOInputHID), + .instance_init = virtio_multitouch_init, +}; + +/* ----------------------------------------------------------------- */ + static void virtio_register_types(void) { type_register_static(&virtio_input_hid_info); type_register_static(&virtio_keyboard_info); type_register_static(&virtio_mouse_info); type_register_static(&virtio_tablet_info); + type_register_static(&virtio_multitouch_info); } type_init(virtio_register_types) diff --git a/hw/input/virtio-input.c b/hw/input/virtio-input.c index 5b5398b3ca..3bcdae41b2 100644 --- a/hw/input/virtio-input.c +++ b/hw/input/virtio-input.c @@ -293,7 +293,7 @@ static const VMStateDescription vmstate_virtio_input = { .name = "virtio-input", .minimum_version_id = VIRTIO_INPUT_VM_VERSION, .version_id = VIRTIO_INPUT_VM_VERSION, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index ecd2883ceb..2b5b2d2301 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -12,10 +12,6 @@ config IOAPIC bool select I8259 -config ARM_GIC - bool - select MSI_NONBROKEN - config OPENPIC bool select MSI_NONBROKEN @@ -25,14 +21,18 @@ config APIC select MSI_NONBROKEN select I8259 +config ARM_GIC + bool + select ARM_GICV3_TCG if TCG + select ARM_GIC_KVM if KVM + select MSI_NONBROKEN + config ARM_GICV3_TCG bool - default y depends on ARM_GIC && TCG config ARM_GIC_KVM bool - default y depends on ARM_GIC && KVM config XICS @@ -49,7 +49,6 @@ config S390_FLIC config S390_FLIC_KVM bool - default y depends on S390_FLIC && KVM config OMPIC @@ -72,12 +71,15 @@ config RISCV_ACLINT config RISCV_APLIC bool + select MSI_NONBROKEN config RISCV_IMSIC bool + select MSI_NONBROKEN config SIFIVE_PLIC bool + select MSI_NONBROKEN config GOLDFISH_PIC bool diff --git a/hw/intc/allwinner-a10-pic.c b/hw/intc/allwinner-a10-pic.c index 8cca124807..cea559c39d 100644 --- a/hw/intc/allwinner-a10-pic.c +++ b/hw/intc/allwinner-a10-pic.c @@ -49,12 +49,9 @@ static void aw_a10_pic_update(AwA10PICState *s) static void aw_a10_pic_set_irq(void *opaque, int irq, int level) { AwA10PICState *s = opaque; + uint32_t *pending_reg = &s->irq_pending[irq / 32]; - if (level) { - set_bit(irq % 32, (void *)&s->irq_pending[irq / 32]); - } else { - clear_bit(irq % 32, (void *)&s->irq_pending[irq / 32]); - } + *pending_reg = deposit32(*pending_reg, irq % 32, 1, !!level); aw_a10_pic_update(s); } @@ -145,7 +142,7 @@ static const VMStateDescription vmstate_aw_a10_pic = { .name = "a10.pic", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(vector, AwA10PICState), VMSTATE_UINT32(base_addr, AwA10PICState), VMSTATE_UINT32(protect, AwA10PICState), diff --git a/hw/intc/apic.c b/hw/intc/apic.c index 3df11c34d6..4186c57b34 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -18,10 +18,12 @@ */ #include "qemu/osdep.h" #include "qemu/thread.h" +#include "qemu/error-report.h" #include "hw/i386/apic_internal.h" #include "hw/i386/apic.h" -#include "hw/i386/ioapic.h" +#include "hw/intc/ioapic.h" #include "hw/intc/i8259.h" +#include "hw/intc/kvm_irqcount.h" #include "hw/pci/msi.h" #include "qemu/host-utils.h" #include "sysemu/kvm.h" @@ -30,14 +32,13 @@ #include "qapi/error.h" #include "qom/object.h" -#define MAX_APICS 255 -#define MAX_APIC_WORDS 8 - #define SYNC_FROM_VAPIC 0x1 #define SYNC_TO_VAPIC 0x2 #define SYNC_ISR_IRR_TO_VAPIC 0x4 -static APICCommonState *local_apics[MAX_APICS + 1]; +static APICCommonState **local_apics; +static uint32_t max_apics; +static uint32_t max_apic_words; #define TYPE_APIC "apic" /*This is reusing the APICCommonState typedef from APIC_COMMON */ @@ -47,7 +48,19 @@ DECLARE_INSTANCE_CHECKER(APICCommonState, APIC, static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode); static void apic_update_irq(APICCommonState *s); static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask, - uint8_t dest, uint8_t dest_mode); + uint32_t dest, uint8_t dest_mode); + +void apic_set_max_apic_id(uint32_t max_apic_id) +{ + int word_size = 32; + + /* round up the max apic id to next multiple of words */ + max_apics = (max_apic_id + word_size - 1) & ~(word_size - 1); + + local_apics = g_malloc0(sizeof(*local_apics) * max_apics); + max_apic_words = max_apics >> 5; +} + /* Find first bit starting from msb */ static int apic_fls_bit(uint32_t value) @@ -197,10 +210,10 @@ static void apic_external_nmi(APICCommonState *s) #define foreach_apic(apic, deliver_bitmask, code) \ {\ int __i, __j;\ - for(__i = 0; __i < MAX_APIC_WORDS; __i++) {\ + for (__i = 0; __i < max_apic_words; __i++) {\ uint32_t __mask = deliver_bitmask[__i];\ if (__mask) {\ - for(__j = 0; __j < 32; __j++) {\ + for (__j = 0; __j < 32; __j++) {\ if (__mask & (1U << __j)) {\ apic = local_apics[__i * 32 + __j];\ if (apic) {\ @@ -224,7 +237,7 @@ static void apic_bus_deliver(const uint32_t *deliver_bitmask, { int i, d; d = -1; - for(i = 0; i < MAX_APIC_WORDS; i++) { + for (i = 0; i < max_apic_words; i++) { if (deliver_bitmask[i]) { d = i * 32 + apic_ffs_bit(deliver_bitmask[i]); break; @@ -274,10 +287,11 @@ static void apic_bus_deliver(const uint32_t *deliver_bitmask, apic_set_irq(apic_iter, vector_num, trigger_mode) ); } -void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, - uint8_t vector_num, uint8_t trigger_mode) +static void apic_deliver_irq(uint32_t dest, uint8_t dest_mode, + uint8_t delivery_mode, uint8_t vector_num, + uint8_t trigger_mode) { - uint32_t deliver_bitmask[MAX_APIC_WORDS]; + g_autofree uint32_t *deliver_bitmask = g_new(uint32_t, max_apic_words); trace_apic_deliver_irq(dest, dest_mode, delivery_mode, vector_num, trigger_mode); @@ -286,8 +300,56 @@ void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, trigger_mode); } -static void apic_set_base(APICCommonState *s, uint64_t val) +bool is_x2apic_mode(DeviceState *dev) { + APICCommonState *s = APIC(dev); + + return s->apicbase & MSR_IA32_APICBASE_EXTD; +} + +static int apic_set_base_check(APICCommonState *s, uint64_t val) +{ + /* Enable x2apic when x2apic is not supported by CPU */ + if (!cpu_has_x2apic_feature(&s->cpu->env) && + val & MSR_IA32_APICBASE_EXTD) { + return -1; + } + + /* + * Transition into invalid state + * (s->apicbase & MSR_IA32_APICBASE_ENABLE == 0) && + * (s->apicbase & MSR_IA32_APICBASE_EXTD) == 1 + */ + if (!(val & MSR_IA32_APICBASE_ENABLE) && + (val & MSR_IA32_APICBASE_EXTD)) { + return -1; + } + + /* Invalid transition from disabled mode to x2APIC */ + if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE) && + !(s->apicbase & MSR_IA32_APICBASE_EXTD) && + (val & MSR_IA32_APICBASE_ENABLE) && + (val & MSR_IA32_APICBASE_EXTD)) { + return -1; + } + + /* Invalid transition from x2APIC to xAPIC */ + if ((s->apicbase & MSR_IA32_APICBASE_ENABLE) && + (s->apicbase & MSR_IA32_APICBASE_EXTD) && + (val & MSR_IA32_APICBASE_ENABLE) && + !(val & MSR_IA32_APICBASE_EXTD)) { + return -1; + } + + return 0; +} + +static int apic_set_base(APICCommonState *s, uint64_t val) +{ + if (apic_set_base_check(s, val) < 0) { + return -1; + } + s->apicbase = (val & 0xfffff000) | (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE)); /* if disabled, cannot be enabled again */ @@ -296,6 +358,25 @@ static void apic_set_base(APICCommonState *s, uint64_t val) cpu_clear_apic_feature(&s->cpu->env); s->spurious_vec &= ~APIC_SV_ENABLE; } + + /* Transition from disabled mode to xAPIC */ + if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE) && + (val & MSR_IA32_APICBASE_ENABLE)) { + s->apicbase |= MSR_IA32_APICBASE_ENABLE; + cpu_set_apic_feature(&s->cpu->env); + } + + /* Transition from xAPIC to x2APIC */ + if (cpu_has_x2apic_feature(&s->cpu->env) && + !(s->apicbase & MSR_IA32_APICBASE_EXTD) && + (val & MSR_IA32_APICBASE_EXTD)) { + s->apicbase |= MSR_IA32_APICBASE_EXTD; + + s->log_dest = ((s->initial_apic_id & 0xffff0) << 16) | + (1 << (s->initial_apic_id & 0xf)); + } + + return 0; } static void apic_set_tpr(APICCommonState *s, uint8_t val) @@ -399,7 +480,7 @@ void apic_poll_irq(DeviceState *dev) static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode) { - apic_report_irq_delivered(!apic_get_bit(s->irr, vector_num)); + kvm_report_irq_delivered(!apic_get_bit(s->irr, vector_num)); apic_set_bit(s->irr, vector_num); if (trigger_mode) @@ -433,57 +514,123 @@ static void apic_eoi(APICCommonState *s) apic_update_irq(s); } -static int apic_find_dest(uint8_t dest) +static bool apic_match_dest(APICCommonState *apic, uint32_t dest) { - APICCommonState *apic = local_apics[dest]; + if (is_x2apic_mode(&apic->parent_obj)) { + return apic->initial_apic_id == dest; + } else { + return apic->id == (uint8_t)dest; + } +} + +static void apic_find_dest(uint32_t *deliver_bitmask, uint32_t dest) +{ + APICCommonState *apic = NULL; int i; - if (apic && apic->id == dest) - return dest; /* shortcut in case apic->id == local_apics[dest]->id */ - - for (i = 0; i < MAX_APICS; i++) { + for (i = 0; i < max_apics; i++) { apic = local_apics[i]; - if (apic && apic->id == dest) - return i; - if (!apic) - break; + if (apic && apic_match_dest(apic, dest)) { + apic_set_bit(deliver_bitmask, i); + } } +} - return -1; +/* + * Deliver interrupt to x2APIC CPUs if it is x2APIC broadcast. + * Otherwise, deliver interrupt to xAPIC CPUs if it is xAPIC + * broadcast. + */ +static void apic_get_broadcast_bitmask(uint32_t *deliver_bitmask, + bool is_x2apic_broadcast) +{ + int i; + APICCommonState *apic_iter; + + for (i = 0; i < max_apics; i++) { + apic_iter = local_apics[i]; + if (apic_iter) { + bool apic_in_x2apic = is_x2apic_mode(&apic_iter->parent_obj); + + if (is_x2apic_broadcast && apic_in_x2apic) { + apic_set_bit(deliver_bitmask, i); + } else if (!is_x2apic_broadcast && !apic_in_x2apic) { + apic_set_bit(deliver_bitmask, i); + } + } + } } static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask, - uint8_t dest, uint8_t dest_mode) + uint32_t dest, uint8_t dest_mode) { - APICCommonState *apic_iter; + APICCommonState *apic; int i; - if (dest_mode == 0) { - if (dest == 0xff) { - memset(deliver_bitmask, 0xff, MAX_APIC_WORDS * sizeof(uint32_t)); + memset(deliver_bitmask, 0x00, max_apic_words * sizeof(uint32_t)); + + /* + * x2APIC broadcast is delivered to all x2APIC CPUs regardless of + * destination mode. In case the destination mode is physical, it is + * broadcasted to all xAPIC CPUs too. Otherwise, if the destination + * mode is logical, we need to continue checking if xAPIC CPUs accepts + * the interrupt. + */ + if (dest == 0xffffffff) { + if (dest_mode == APIC_DESTMODE_PHYSICAL) { + memset(deliver_bitmask, 0xff, max_apic_words * sizeof(uint32_t)); + return; } else { - int idx = apic_find_dest(dest); - memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t)); - if (idx >= 0) - apic_set_bit(deliver_bitmask, idx); + apic_get_broadcast_bitmask(deliver_bitmask, true); + } + } + + if (dest_mode == APIC_DESTMODE_PHYSICAL) { + apic_find_dest(deliver_bitmask, dest); + /* Any APIC in xAPIC mode will interpret 0xFF as broadcast */ + if (dest == 0xff) { + apic_get_broadcast_bitmask(deliver_bitmask, false); } } else { - /* XXX: cluster mode */ - memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t)); - for(i = 0; i < MAX_APICS; i++) { - apic_iter = local_apics[i]; - if (apic_iter) { - if (apic_iter->dest_mode == 0xf) { - if (dest & apic_iter->log_dest) - apic_set_bit(deliver_bitmask, i); - } else if (apic_iter->dest_mode == 0x0) { - if ((dest & 0xf0) == (apic_iter->log_dest & 0xf0) && - (dest & apic_iter->log_dest & 0x0f)) { + /* XXX: logical mode */ + for (i = 0; i < max_apics; i++) { + apic = local_apics[i]; + if (apic) { + /* x2APIC logical mode */ + if (apic->apicbase & MSR_IA32_APICBASE_EXTD) { + if ((dest >> 16) == (apic->extended_log_dest >> 16) && + (dest & apic->extended_log_dest & 0xffff)) { apic_set_bit(deliver_bitmask, i); } + continue; } - } else { - break; + + /* xAPIC logical mode */ + dest = (uint8_t)dest; + if (apic->dest_mode == APIC_DESTMODE_LOGICAL_FLAT) { + if (dest & apic->log_dest) { + apic_set_bit(deliver_bitmask, i); + } + } else if (apic->dest_mode == APIC_DESTMODE_LOGICAL_CLUSTER) { + /* + * In cluster model of xAPIC logical mode IPI, 4 higher + * bits are used as cluster address, 4 lower bits are + * the bitmask for local APICs in the cluster. The IPI + * is delivered to an APIC if the cluster address + * matches and the APIC's address bit in the cluster is + * set in bitmask of destination ID in IPI. + * + * The cluster address ranges from 0 - 14, the cluster + * address 15 (0xf) is the broadcast address to all + * clusters. + */ + if ((dest & 0xf0) == 0xf0 || + (dest & 0xf0) == (apic->log_dest & 0xf0)) { + if (dest & apic->log_dest & 0x0f) { + apic_set_bit(deliver_bitmask, i); + } + } + } } } } @@ -507,29 +654,36 @@ void apic_sipi(DeviceState *dev) s->wait_for_sipi = 0; } -static void apic_deliver(DeviceState *dev, uint8_t dest, uint8_t dest_mode, +static void apic_deliver(DeviceState *dev, uint32_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t vector_num, - uint8_t trigger_mode) + uint8_t trigger_mode, uint8_t dest_shorthand) { APICCommonState *s = APIC(dev); - uint32_t deliver_bitmask[MAX_APIC_WORDS]; - int dest_shorthand = (s->icr[0] >> 18) & 3; APICCommonState *apic_iter; + uint32_t deliver_bitmask_size = max_apic_words * sizeof(uint32_t); + g_autofree uint32_t *deliver_bitmask = g_new(uint32_t, max_apic_words); + uint32_t current_apic_id; + + if (is_x2apic_mode(dev)) { + current_apic_id = s->initial_apic_id; + } else { + current_apic_id = s->id; + } switch (dest_shorthand) { case 0: apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode); break; case 1: - memset(deliver_bitmask, 0x00, sizeof(deliver_bitmask)); - apic_set_bit(deliver_bitmask, s->id); + memset(deliver_bitmask, 0x00, deliver_bitmask_size); + apic_set_bit(deliver_bitmask, current_apic_id); break; case 2: - memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask)); + memset(deliver_bitmask, 0xff, deliver_bitmask_size); break; case 3: - memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask)); - apic_reset_bit(deliver_bitmask, s->id); + memset(deliver_bitmask, 0xff, deliver_bitmask_size); + apic_reset_bit(deliver_bitmask, current_apic_id); break; } @@ -634,27 +788,26 @@ static void apic_timer(void *opaque) apic_timer_update(s, s->next_time); } -static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size) +static int apic_register_read(int index, uint64_t *value) { DeviceState *dev; APICCommonState *s; uint32_t val; - int index; - - if (size < 4) { - return 0; - } + int ret = 0; dev = cpu_get_current_apic(); if (!dev) { - return 0; + return -1; } s = APIC(dev); - index = (addr >> 4) & 0xff; switch(index) { case 0x02: /* id */ - val = s->id << 24; + if (is_x2apic_mode(dev)) { + val = s->initial_apic_id; + } else { + val = s->id << 24; + } break; case 0x03: /* version */ val = s->version | ((APIC_LVT_NB - 1) << 16); @@ -677,10 +830,19 @@ static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size) val = 0; break; case 0x0d: - val = s->log_dest << 24; + if (is_x2apic_mode(dev)) { + val = s->extended_log_dest; + } else { + val = s->log_dest << 24; + } break; case 0x0e: - val = (s->dest_mode << 28) | 0xfffffff; + if (is_x2apic_mode(dev)) { + val = 0; + ret = -1; + } else { + val = (s->dest_mode << 28) | 0xfffffff; + } break; case 0x0f: val = s->spurious_vec; @@ -716,17 +878,56 @@ static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size) default: s->esr |= APIC_ESR_ILLEGAL_ADDRESS; val = 0; + ret = -1; break; } - trace_apic_mem_readl(addr, val); + + trace_apic_register_read(index, val); + *value = val; + return ret; +} + +static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t val; + int index; + + if (size < 4) { + return 0; + } + + index = (addr >> 4) & 0xff; + apic_register_read(index, &val); + return val; } +int apic_msr_read(int index, uint64_t *val) +{ + DeviceState *dev; + + dev = cpu_get_current_apic(); + if (!dev) { + return -1; + } + + if (!is_x2apic_mode(dev)) { + return -1; + } + + return apic_register_read(index, val); +} + static void apic_send_msi(MSIMessage *msi) { uint64_t addr = msi->address; uint32_t data = msi->data; - uint8_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT; + uint32_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT; + /* + * The higher 3 bytes of destination id is stored in higher word of + * msi address. See x86_iommu_irq_to_msi_message() + */ + dest = dest | (addr >> 32); uint8_t vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT; uint8_t dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1; uint8_t trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; @@ -735,38 +936,25 @@ static void apic_send_msi(MSIMessage *msi) apic_deliver_irq(dest, dest_mode, delivery, vector, trigger_mode); } -static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) +static int apic_register_write(int index, uint64_t val) { DeviceState *dev; APICCommonState *s; - int index = (addr >> 4) & 0xff; - - if (size < 4) { - return; - } - - if (addr > 0xfff || !index) { - /* MSI and MMIO APIC are at the same memory location, - * but actually not on the global bus: MSI is on PCI bus - * APIC is connected directly to the CPU. - * Mapping them on the global bus happens to work because - * MSI registers are reserved in APIC MMIO and vice versa. */ - MSIMessage msi = { .address = addr, .data = val }; - apic_send_msi(&msi); - return; - } dev = cpu_get_current_apic(); if (!dev) { - return; + return -1; } s = APIC(dev); - trace_apic_mem_writel(addr, val); + trace_apic_register_write(index, val); switch(index) { case 0x02: + if (is_x2apic_mode(dev)) { + return -1; + } + s->id = (val >> 24); break; case 0x03: @@ -786,9 +974,17 @@ static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val, apic_eoi(s); break; case 0x0d: + if (is_x2apic_mode(dev)) { + return -1; + } + s->log_dest = val >> 24; break; case 0x0e: + if (is_x2apic_mode(dev)) { + return -1; + } + s->dest_mode = val >> 28; break; case 0x0f: @@ -800,13 +996,27 @@ static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val, case 0x20 ... 0x27: case 0x28: break; - case 0x30: + case 0x30: { + uint32_t dest; + s->icr[0] = val; - apic_deliver(dev, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1, + if (is_x2apic_mode(dev)) { + s->icr[1] = val >> 32; + dest = s->icr[1]; + } else { + dest = (s->icr[1] >> 24) & 0xff; + } + + apic_deliver(dev, dest, (s->icr[0] >> 11) & 1, (s->icr[0] >> 8) & 7, (s->icr[0] & 0xff), - (s->icr[0] >> 15) & 1); + (s->icr[0] >> 15) & 1, (s->icr[0] >> 18) & 3); break; + } case 0x31: + if (is_x2apic_mode(dev)) { + return -1; + } + s->icr[1] = val; break; case 0x32 ... 0x37: @@ -835,10 +1045,70 @@ static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val, s->count_shift = (v + 1) & 7; } break; - default: - s->esr |= APIC_ESR_ILLEGAL_ADDRESS; + case 0x3f: { + int vector = val & 0xff; + + if (!is_x2apic_mode(dev)) { + return -1; + } + + /* + * Self IPI is identical to IPI with + * - Destination shorthand: 1 (Self) + * - Trigger mode: 0 (Edge) + * - Delivery mode: 0 (Fixed) + */ + apic_deliver(dev, 0, 0, APIC_DM_FIXED, vector, 0, 1); + break; } + default: + s->esr |= APIC_ESR_ILLEGAL_ADDRESS; + return -1; + } + + return 0; +} + +static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + int index = (addr >> 4) & 0xff; + + if (size < 4) { + return; + } + + if (addr > 0xfff || !index) { + /* + * MSI and MMIO APIC are at the same memory location, + * but actually not on the global bus: MSI is on PCI bus + * APIC is connected directly to the CPU. + * Mapping them on the global bus happens to work because + * MSI registers are reserved in APIC MMIO and vice versa. + */ + MSIMessage msi = { .address = addr, .data = val }; + apic_send_msi(&msi); + return; + } + + apic_register_write(index, val); +} + +int apic_msr_write(int index, uint64_t val) +{ + DeviceState *dev; + + dev = cpu_get_current_apic(); + if (!dev) { + return -1; + } + + if (!is_x2apic_mode(dev)) { + return -1; + } + + return apic_register_write(index, val); } static void apic_pre_save(APICCommonState *s) @@ -869,12 +1139,6 @@ static void apic_realize(DeviceState *dev, Error **errp) { APICCommonState *s = APIC(dev); - if (s->id >= MAX_APICS) { - error_setg(errp, "%s initialization failed. APIC ID %d is invalid", - object_get_typename(OBJECT(dev)), s->id); - return; - } - if (kvm_enabled()) { warn_report("Userspace local APIC is deprecated for KVM."); warn_report("Do not use kernel-irqchip except for the -M isapc machine type."); @@ -883,8 +1147,24 @@ static void apic_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->io_memory, OBJECT(s), &apic_io_ops, s, "apic-msi", APIC_SPACE_SIZE); + /* + * apic-msi's apic_mem_write can call into ioapic_eoi_broadcast, which can + * write back to apic-msi. As such mark the apic-msi region re-entrancy + * safe. + */ + s->io_memory.disable_reentrancy_guard = true; + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, apic_timer, s); - local_apics[s->id] = s; + + /* + * The --machine none does not call apic_set_max_apic_id before creating + * apic, so we need to call it here and set it to 1 which is the max cpus + * in machine none. + */ + if (!local_apics) { + apic_set_max_apic_id(1); + } + local_apics[s->initial_apic_id] = s; msi_nonbroken = true; } @@ -894,7 +1174,7 @@ static void apic_unrealize(DeviceState *dev) APICCommonState *s = APIC(dev); timer_free(s->timer); - local_apics[s->id] = NULL; + local_apics[s->initial_apic_id] = NULL; } static void apic_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 2a20982066..d8fc1e2815 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -25,31 +25,29 @@ #include "qapi/visitor.h" #include "hw/i386/apic.h" #include "hw/i386/apic_internal.h" +#include "hw/intc/kvm_irqcount.h" #include "trace.h" #include "hw/boards.h" -#include "sysemu/hax.h" #include "sysemu/kvm.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -static int apic_irq_delivered; bool apic_report_tpr_access; -void cpu_set_apic_base(DeviceState *dev, uint64_t val) +int cpu_set_apic_base(DeviceState *dev, uint64_t val) { trace_cpu_set_apic_base(val); if (dev) { APICCommonState *s = APIC_COMMON(dev); APICCommonClass *info = APIC_COMMON_GET_CLASS(s); - /* switching to x2APIC, reset possibly modified xAPIC ID */ - if (!(s->apicbase & MSR_IA32_APICBASE_EXTD) && - (val & MSR_IA32_APICBASE_EXTD)) { - s->id = s->initial_apic_id; - } - info->set_base(s, val); + /* Reset possibly modified xAPIC ID */ + s->id = s->initial_apic_id; + return info->set_base(s, val); } + + return 0; } uint64_t cpu_get_apic_base(DeviceState *dev) @@ -64,6 +62,19 @@ uint64_t cpu_get_apic_base(DeviceState *dev) } } +bool cpu_is_apic_enabled(DeviceState *dev) +{ + APICCommonState *s; + + if (!dev) { + return false; + } + + s = APIC_COMMON(dev); + + return s->apicbase & MSR_IA32_APICBASE_ENABLE; +} + void cpu_set_apic_tpr(DeviceState *dev, uint8_t val) { APICCommonState *s; @@ -122,32 +133,6 @@ void apic_handle_tpr_access_report(DeviceState *dev, target_ulong ip, vapic_report_tpr_access(s->vapic, CPU(s->cpu), ip, access); } -void apic_report_irq_delivered(int delivered) -{ - apic_irq_delivered += delivered; - - trace_apic_report_irq_delivered(apic_irq_delivered); -} - -void apic_reset_irq_delivered(void) -{ - /* Copy this into a local variable to encourage gcc to emit a plain - * register for a sys/sdt.h marker. For details on this workaround, see: - * https://sourceware.org/bugzilla/show_bug.cgi?id=13296 - */ - volatile int a_i_d = apic_irq_delivered; - trace_apic_reset_irq_delivered(a_i_d); - - apic_irq_delivered = 0; -} - -int apic_get_irq_delivered(void) -{ - trace_apic_get_irq_delivered(apic_irq_delivered); - - return apic_irq_delivered; -} - void apic_deliver_nmi(DeviceState *dev) { APICCommonState *s = APIC_COMMON(dev); @@ -272,7 +257,7 @@ static void apic_reset_common(DeviceState *dev) s->apicbase = APIC_DEFAULT_ADDRESS | bsp | MSR_IA32_APICBASE_ENABLE; s->id = s->initial_apic_id; - apic_reset_irq_delivered(); + kvm_reset_irq_delivered(); s->vapic_paddr = 0; info->vapic_base_update(s); @@ -284,6 +269,7 @@ static const VMStateDescription vmstate_apic_common; static void apic_common_realize(DeviceState *dev, Error **errp) { + ERRP_GUARD(); APICCommonState *s = APIC_COMMON(dev); APICCommonClass *info; static DeviceState *vapic; @@ -294,10 +280,13 @@ static void apic_common_realize(DeviceState *dev, Error **errp) info = APIC_COMMON_GET_CLASS(s); info->realize(dev, errp); + if (*errp) { + return; + } /* Note: We need at least 1M to map the VAPIC option ROM */ if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK && - !hax_enabled() && current_machine->ram_size >= 1024 * 1024) { + current_machine->ram_size >= 1024 * 1024) { vapic = sysbus_create_simple("kvmvapic", -1, NULL); } s->vapic = vapic; @@ -310,6 +299,10 @@ static void apic_common_realize(DeviceState *dev, Error **errp) } vmstate_register_with_alias_id(NULL, instance_id, &vmstate_apic_common, s, -1, 0, NULL); + + /* APIC LDR in x2APIC mode */ + s->extended_log_dest = ((s->initial_apic_id >> 4) << 16) | + (1 << (s->initial_apic_id & 0xf)); } static void apic_common_unrealize(DeviceState *dev) @@ -372,7 +365,7 @@ static const VMStateDescription vmstate_apic_common_sipi = { .version_id = 1, .minimum_version_id = 1, .needed = apic_common_sipi_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(sipi_vector, APICCommonState), VMSTATE_INT32(wait_for_sipi, APICCommonState), VMSTATE_END_OF_LIST() @@ -386,7 +379,7 @@ static const VMStateDescription vmstate_apic_common = { .pre_load = apic_pre_load, .pre_save = apic_dispatch_pre_save, .post_load = apic_dispatch_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(apicbase, APICCommonState), VMSTATE_UINT8(id, APICCommonState), VMSTATE_UINT8(arb_id, APICCommonState), @@ -409,7 +402,7 @@ static const VMStateDescription vmstate_apic_common = { APICCommonState), /* open-coded timer state */ VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_apic_common_sipi, NULL } @@ -450,6 +443,11 @@ static void apic_common_set_id(Object *obj, Visitor *v, const char *name, return; } + if (value >= 255 && !cpu_has_x2apic_feature(&s->cpu->env)) { + error_setg(errp, "APIC ID %d requires x2APIC feature in CPU", value); + return; + } + s->initial_apic_id = value; s->id = (uint8_t)value; } diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 492b2421ab..074cf50af2 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -239,7 +239,7 @@ static inline bool gic_lr_entry_is_free(uint32_t entry) } /* Return true if this LR should trigger an EOI maintenance interrupt, i.e. the - * corrsponding bit in EISR is set. + * corresponding bit in EISR is set. */ static inline bool gic_lr_entry_is_eoi(uint32_t entry) { @@ -941,7 +941,7 @@ static void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) gic_update(s); } -static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) +static uint8_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) { GICState *s = (GICState *)opaque; uint32_t res; @@ -955,6 +955,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) cm = 1 << cpu; if (offset < 0x100) { if (offset == 0) { /* GICD_CTLR */ + /* We rely here on the only non-zero bits being in byte 0 */ if (s->security_extn && !attrs.secure) { /* The NS bank of this register is just an alias of the * EnableGrp1 bit in the S bank version. @@ -964,13 +965,26 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) return s->ctlr; } } - if (offset == 4) - /* Interrupt Controller Type Register */ - return ((s->num_irq / 32) - 1) - | ((s->num_cpu - 1) << 5) - | (s->security_extn << 10); - if (offset < 0x08) + if (offset == 4) { + /* GICD_TYPER byte 0 */ + return ((s->num_irq / 32) - 1) | ((s->num_cpu - 1) << 5); + } + if (offset == 5) { + /* GICD_TYPER byte 1 */ + return (s->security_extn << 2); + } + if (offset == 8) { + /* GICD_IIDR byte 0 */ + return 0x3b; /* Arm JEP106 identity */ + } + if (offset == 9) { + /* GICD_IIDR byte 1 */ + return 0x04; /* Arm JEP106 identity */ + } + if (offset < 0x0c) { + /* All other bytes in this range are RAZ */ return 0; + } if (offset >= 0x80) { /* Interrupt Group Registers: these RAZ/WI if this is an NS * access to a GIC with the security extensions, or if the GIC @@ -1319,7 +1333,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, /* ??? This currently clears the pending bit for all CPUs, even for per-CPU interrupts. It's unclear whether this is the - corect behavior. */ + correct behavior. */ if (value & (1 << i)) { GIC_DIST_CLEAR_PENDING(irq + i, ALL_CPU_MASK); } diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 7b44d5625b..94c173cb07 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -21,10 +21,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" +#include "qemu/error-report.h" #include "gic_internal.h" #include "hw/arm/linux-boot-if.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" +#include "sysemu/kvm.h" static int gic_pre_save(void *opaque) { @@ -60,7 +62,7 @@ static const VMStateDescription vmstate_gic_irq_state = { .name = "arm_gic_irq_state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(enabled, gic_irq_state), VMSTATE_UINT8(pending, gic_irq_state), VMSTATE_UINT8(active, gic_irq_state), @@ -77,7 +79,7 @@ static const VMStateDescription vmstate_gic_virt_state = { .version_id = 1, .minimum_version_id = 1, .needed = gic_virt_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { /* Virtual interface */ VMSTATE_UINT32_ARRAY(h_hcr, GICState, GIC_NCPU), VMSTATE_UINT32_ARRAY(h_misr, GICState, GIC_NCPU), @@ -102,7 +104,7 @@ static const VMStateDescription vmstate_gic = { .minimum_version_id = 12, .pre_save = gic_pre_save, .post_load = gic_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ctlr, GICState), VMSTATE_UINT32_SUB_ARRAY(cpu_ctlr, GICState, 0, GIC_NCPU), VMSTATE_STRUCT_ARRAY(irq_state, GICState, GIC_MAXIRQ, 1, @@ -120,7 +122,7 @@ static const VMStateDescription vmstate_gic = { VMSTATE_UINT32_2DARRAY(nsapr, GICState, GIC_NR_APRS, GIC_NCPU), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_gic_virt_state, NULL } @@ -233,12 +235,12 @@ static void arm_gic_common_realize(DeviceState *dev, Error **errp) } } -static inline void arm_gic_common_reset_irq_state(GICState *s, int first_cpu, +static inline void arm_gic_common_reset_irq_state(GICState *s, int cidx, int resetprio) { int i, j; - for (i = first_cpu; i < first_cpu + s->num_cpu; i++) { + for (i = cidx; i < cidx + s->num_cpu; i++) { if (s->revision == REV_11MPCORE) { s->priority_mask[i] = 0xf0; } else { @@ -261,9 +263,9 @@ static inline void arm_gic_common_reset_irq_state(GICState *s, int first_cpu, } } -static void arm_gic_common_reset(DeviceState *dev) +static void arm_gic_common_reset_hold(Object *obj) { - GICState *s = ARM_GIC_COMMON(dev); + GICState *s = ARM_GIC_COMMON(obj); int i, j; int resetprio; @@ -364,9 +366,10 @@ static Property arm_gic_common_properties[] = { static void arm_gic_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_CLASS(klass); - dc->reset = arm_gic_common_reset; + rc->phases.hold = arm_gic_common_reset_hold; dc->realize = arm_gic_common_realize; device_class_set_props(dc, arm_gic_common_properties); dc->vmsd = &vmstate_gic; @@ -392,3 +395,8 @@ static void register_types(void) } type_init(register_types) + +const char *gic_class_name(void) +{ + return kvm_irqchip_in_kernel() ? "kvm-arm-gic" : "arm_gic"; +} diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 7d2a13273a..e0d9e512a3 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -38,7 +38,7 @@ DECLARE_OBJ_CHECKERS(GICState, KVMARMGICClass, struct KVMARMGICClass { ARMGICCommonClass parent_class; DeviceRealize parent_realize; - void (*parent_reset)(DeviceState *dev); + ResettablePhases parent_phases; }; void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level) @@ -473,12 +473,14 @@ static void kvm_arm_gic_get(GICState *s) } } -static void kvm_arm_gic_reset(DeviceState *dev) +static void kvm_arm_gic_reset_hold(Object *obj) { - GICState *s = ARM_GIC_COMMON(dev); + GICState *s = ARM_GIC_COMMON(obj); KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); - kgc->parent_reset(dev); + if (kgc->parent_phases.hold) { + kgc->parent_phases.hold(obj); + } if (kvm_arm_gic_can_save_restore(s)) { kvm_arm_gic_put(s); @@ -514,8 +516,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) if (!kvm_arm_gic_can_save_restore(s)) { error_setg(&s->migration_blocker, "This operating system kernel does " "not support vGICv2 migration"); - if (migrate_add_blocker(s->migration_blocker, errp) < 0) { - error_free(s->migration_blocker); + if (migrate_add_blocker(&s->migration_blocker, errp) < 0) { return; } } @@ -593,6 +594,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) static void kvm_arm_gic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); ARMGICCommonClass *agcc = ARM_GIC_COMMON_CLASS(klass); KVMARMGICClass *kgc = KVM_ARM_GIC_CLASS(klass); @@ -600,7 +602,8 @@ static void kvm_arm_gic_class_init(ObjectClass *klass, void *data) agcc->post_load = kvm_arm_gic_put; device_class_set_parent_realize(dc, kvm_arm_gic_realize, &kgc->parent_realize); - device_class_set_parent_reset(dc, kvm_arm_gic_reset, &kgc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, kvm_arm_gic_reset_hold, NULL, + &kgc->parent_phases); } static const TypeInfo kvm_arm_gic_info = { diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 351843db4a..cb55c72681 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" +#include "qemu/error-report.h" #include "hw/core/cpu.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/qdev-properties.h" @@ -104,7 +105,7 @@ static const VMStateDescription vmstate_gicv3_cpu_virt = { .version_id = 1, .minimum_version_id = 1, .needed = virt_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_2DARRAY(ich_apr, GICv3CPUState, 3, 4), VMSTATE_UINT64(ich_hcr_el2, GICv3CPUState), VMSTATE_UINT64_ARRAY(ich_lr_el2, GICv3CPUState, GICV3_LR_MAX), @@ -138,7 +139,7 @@ const VMStateDescription vmstate_gicv3_cpu_sre_el1 = { .version_id = 1, .minimum_version_id = 1, .needed = icc_sre_el1_reg_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(icc_sre_el1, GICv3CPUState), VMSTATE_END_OF_LIST() } @@ -156,7 +157,7 @@ const VMStateDescription vmstate_gicv3_gicv4 = { .version_id = 1, .minimum_version_id = 1, .needed = gicv4_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(gicr_vpropbaser, GICv3CPUState), VMSTATE_UINT64(gicr_vpendbaser, GICv3CPUState), VMSTATE_END_OF_LIST() @@ -168,7 +169,7 @@ static const VMStateDescription vmstate_gicv3_cpu = { .version_id = 1, .minimum_version_id = 1, .pre_load = vmstate_gicv3_cpu_pre_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(level, GICv3CPUState), VMSTATE_UINT32(gicr_ctlr, GICv3CPUState), VMSTATE_UINT32_ARRAY(gicr_statusr, GICv3CPUState, 2), @@ -191,7 +192,7 @@ static const VMStateDescription vmstate_gicv3_cpu = { VMSTATE_UINT64(icc_ctlr_el3, GICv3CPUState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_gicv3_cpu_virt, &vmstate_gicv3_cpu_sre_el1, &vmstate_gicv3_gicv4, @@ -231,7 +232,7 @@ const VMStateDescription vmstate_gicv3_gicd_no_migration_shift_bug = { .version_id = 1, .minimum_version_id = 1, .needed = needed_always, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(gicd_no_migration_shift_bug, GICv3State), VMSTATE_END_OF_LIST() } @@ -245,7 +246,7 @@ static const VMStateDescription vmstate_gicv3 = { .pre_save = gicv3_pre_save, .post_load = gicv3_post_load, .priority = MIG_PRI_GICV3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(gicd_ctlr, GICv3State), VMSTATE_UINT32_ARRAY(gicd_statusr, GICv3State, 2), VMSTATE_UINT32_ARRAY(group, GICv3State, GICV3_BMP_SIZE), @@ -263,7 +264,7 @@ static const VMStateDescription vmstate_gicv3 = { vmstate_gicv3_cpu, GICv3CPUState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_gicv3_gicd_no_migration_shift_bug, NULL } @@ -450,9 +451,9 @@ static void arm_gicv3_finalize(Object *obj) g_free(s->redist_region_count); } -static void arm_gicv3_common_reset(DeviceState *dev) +static void arm_gicv3_common_reset_hold(Object *obj) { - GICv3State *s = ARM_GICV3_COMMON(dev); + GICv3State *s = ARM_GICV3_COMMON(obj); int i; for (i = 0; i < s->num_cpu; i++) { @@ -578,9 +579,10 @@ static Property arm_gicv3_common_properties[] = { static void arm_gicv3_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_CLASS(klass); - dc->reset = arm_gicv3_common_reset; + rc->phases.hold = arm_gicv3_common_reset_hold; dc->realize = arm_gicv3_common_realize; device_class_set_props(dc, arm_gicv3_common_properties); dc->vmsd = &vmstate_gicv3; @@ -607,3 +609,16 @@ static void register_types(void) } type_init(register_types) + +const char *gicv3_class_name(void) +{ + if (kvm_irqchip_in_kernel()) { + return "kvm-arm-gicv3"; + } else { + if (kvm_enabled()) { + error_report("Userspace GICv3 is not supported with KVM"); + exit(1); + } + return "arm-gicv3"; + } +} diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 8ca630e5ad..67d8fd07b7 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -21,6 +21,8 @@ #include "hw/irq.h" #include "cpu.h" #include "target/arm/cpregs.h" +#include "sysemu/tcg.h" +#include "sysemu/qtest.h" /* * Special case return value from hppvi_index(); must be larger than @@ -144,7 +146,7 @@ static uint32_t icv_fullprio_mask(GICv3CPUState *cs) * with the group priority, whose mask depends on the value of VBPR * for the interrupt group.) */ - return ~0U << (8 - cs->vpribits); + return (~0U << (8 - cs->vpribits)) & 0xff; } static int ich_highest_active_virt_prio(GICv3CPUState *cs) @@ -801,7 +803,7 @@ static uint32_t icc_fullprio_mask(GICv3CPUState *cs) * with the group priority, whose mask depends on the value of BPR * for the interrupt group.) */ - return ~0U << (8 - cs->pribits); + return (~0U << (8 - cs->pribits)) & 0xff; } static inline int icc_min_bpr(GICv3CPUState *cs) @@ -932,7 +934,7 @@ void gicv3_cpuif_update(GICv3CPUState *cs) ARMCPU *cpu = ARM_CPU(cs->cpu); CPUARMState *env = &cpu->env; - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); trace_gicv3_cpuif_update(gicv3_redist_affid(cs), cs->hppi.irq, cs->hppi.grp, cs->hppi.prio); @@ -1016,8 +1018,6 @@ static void icc_pmr_write(CPUARMState *env, const ARMCPRegInfo *ri, trace_gicv3_icc_pmr_write(gicv3_redist_affid(cs), value); - value &= icc_fullprio_mask(cs); - if (arm_feature(env, ARM_FEATURE_EL3) && !arm_is_secure(env) && (env->cp15.scr_el3 & SCR_FIQ)) { /* NS access and Group 0 is inaccessible to NS: return the @@ -1029,6 +1029,7 @@ static void icc_pmr_write(CPUARMState *env, const ARMCPRegInfo *ri, } value = (value >> 1) | 0x80; } + value &= icc_fullprio_mask(cs); cs->icc_pmr_el1 = value; gicv3_cpuif_update(cs); } @@ -1066,7 +1067,7 @@ static uint64_t icc_hppir0_value(GICv3CPUState *cs, CPUARMState *env) */ bool irq_is_secure; - if (cs->hppi.prio == 0xff) { + if (icc_no_enabled_hppi(cs)) { return INTID_SPURIOUS; } @@ -1103,7 +1104,7 @@ static uint64_t icc_hppir1_value(GICv3CPUState *cs, CPUARMState *env) */ bool irq_is_secure; - if (cs->hppi.prio == 0xff) { + if (icc_no_enabled_hppi(cs)) { return INTID_SPURIOUS; } @@ -1433,16 +1434,25 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, idx = icv_find_active(cs, irq); if (idx < 0) { - /* No valid list register corresponding to EOI ID */ - icv_increment_eoicount(cs); + /* + * No valid list register corresponding to EOI ID; if this is a vLPI + * not in the list regs then do nothing; otherwise increment EOI count + */ + if (irq < GICV3_LPI_INTID_START) { + icv_increment_eoicount(cs); + } } else { uint64_t lr = cs->ich_lr_el2[idx]; int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; int lr_gprio = ich_lr_prio(lr) & icv_gprio_mask(cs, grp); if (thisgrp == grp && lr_gprio == dropprio) { - if (!icv_eoi_split(env, cs)) { - /* Priority drop and deactivate not split: deactivate irq now */ + if (!icv_eoi_split(env, cs) || irq >= GICV3_LPI_INTID_START) { + /* + * Priority drop and deactivate not split: deactivate irq now. + * LPIs always get their active state cleared immediately + * because no separate deactivate is expected. + */ icv_deactivate_irq(cs, idx); } } @@ -2377,6 +2387,7 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 6, .type = ARM_CP_IO | ARM_CP_NO_RAW, .access = PL1_RW, .accessfn = gicv3_fiq_access, + .fgt = FGT_ICC_IGRPENN_EL1, .readfn = icc_igrpen_read, .writefn = icc_igrpen_write, }, @@ -2385,6 +2396,7 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 7, .type = ARM_CP_IO | ARM_CP_NO_RAW, .access = PL1_RW, .accessfn = gicv3_irq_access, + .fgt = FGT_ICC_IGRPENN_EL1, .readfn = icc_igrpen_read, .writefn = icc_igrpen_write, }, @@ -2672,6 +2684,7 @@ static const ARMCPRegInfo gicv3_cpuif_hcr_reginfo[] = { { .name = "ICH_AP0R0_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 0, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x480, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -2679,6 +2692,7 @@ static const ARMCPRegInfo gicv3_cpuif_hcr_reginfo[] = { { .name = "ICH_AP1R0_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 0, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x4a0, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -2686,6 +2700,7 @@ static const ARMCPRegInfo gicv3_cpuif_hcr_reginfo[] = { { .name = "ICH_HCR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 0, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x4c0, .access = PL2_RW, .readfn = ich_hcr_read, .writefn = ich_hcr_write, @@ -2717,6 +2732,7 @@ static const ARMCPRegInfo gicv3_cpuif_hcr_reginfo[] = { { .name = "ICH_VMCR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 7, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x4c8, .access = PL2_RW, .readfn = ich_vmcr_read, .writefn = ich_vmcr_write, @@ -2727,6 +2743,7 @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr1_reginfo[] = { { .name = "ICH_AP0R1_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 1, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x488, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -2734,6 +2751,7 @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr1_reginfo[] = { { .name = "ICH_AP1R1_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 1, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x4a8, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -2744,6 +2762,7 @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = { { .name = "ICH_AP0R2_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 2, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x490, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -2751,6 +2770,7 @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = { { .name = "ICH_AP0R3_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 3, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x498, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -2758,6 +2778,7 @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = { { .name = "ICH_AP1R2_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 2, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x4b0, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -2765,6 +2786,7 @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = { { .name = "ICH_AP1R3_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 3, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x4b8, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -2811,6 +2833,8 @@ void gicv3_init_cpuif(GICv3State *s) * which case we'd get the wrong value. * So instead we define the regs with no ri->opaque info, and * get back to the GICv3CPUState from the CPUARMState. + * + * These CP regs callbacks can be called from either TCG or HVF code. */ define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); @@ -2884,6 +2908,7 @@ void gicv3_init_cpuif(GICv3State *s) .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 12 + (j >> 3), .opc2 = j & 7, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x400 + 8 * j, .access = PL2_RW, .readfn = ich_lr_read, .writefn = ich_lr_write, @@ -2906,6 +2931,16 @@ void gicv3_init_cpuif(GICv3State *s) define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr23_reginfo); } } - arm_register_el_change_hook(cpu, gicv3_cpuif_el_change_hook, cs); + if (tcg_enabled() || qtest_enabled()) { + /* + * We can only trap EL changes with TCG. However the GIC interrupt + * state only changes on EL changes involving EL2 or EL3, so for + * the non-TCG case this is OK, as EL2 and EL3 can't exist. + */ + arm_register_el_change_hook(cpu, gicv3_cpuif_el_change_hook, cs); + } else { + assert(!arm_feature(&cpu->env, ARM_FEATURE_EL2)); + assert(!arm_feature(&cpu->env, ARM_FEATURE_EL3)); + } } } diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c index eea0368118..35e850685c 100644 --- a/hw/intc/arm_gicv3_dist.c +++ b/hw/intc/arm_gicv3_dist.c @@ -390,9 +390,9 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, * MBIS == 0 (message-based SPIs not supported) * SecurityExtn == 1 if security extns supported * CPUNumber == 0 since for us ARE is always 1 - * ITLinesNumber == (num external irqs / 32) - 1 + * ITLinesNumber == (((max SPI IntID + 1) / 32) - 1) */ - int itlinesnumber = ((s->num_irq - GIC_INTERNAL) / 32) - 1; + int itlinesnumber = (s->num_irq / 32) - 1; /* * SecurityExtn must be RAZ if GICD_CTLR.DS == 1, and * "security extensions not supported" always implies DS == 1, @@ -564,7 +564,7 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, /* WO registers, return unknown value */ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest read from WO register at offset " - TARGET_FMT_plx "\n", __func__, offset); + HWADDR_FMT_plx "\n", __func__, offset); *data = 0; return true; default: @@ -773,7 +773,7 @@ static bool gicd_writel(GICv3State *s, hwaddr offset, /* RO registers, ignore the write */ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write to RO register at offset " - TARGET_FMT_plx "\n", __func__, offset); + HWADDR_FMT_plx "\n", __func__, offset); return true; default: return false; @@ -838,7 +838,7 @@ MemTxResult gicv3_dist_read(void *opaque, hwaddr offset, uint64_t *data, if (!r) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid guest read at offset " TARGET_FMT_plx + "%s: invalid guest read at offset " HWADDR_FMT_plx " size %u\n", __func__, offset, size); trace_gicv3_dist_badread(offset, size, attrs.secure); /* The spec requires that reserved registers are RAZ/WI; @@ -879,7 +879,7 @@ MemTxResult gicv3_dist_write(void *opaque, hwaddr offset, uint64_t data, if (!r) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid guest write at offset " TARGET_FMT_plx + "%s: invalid guest write at offset " HWADDR_FMT_plx " size %u\n", __func__, offset, size); trace_gicv3_dist_badwrite(offset, data, size, attrs.secure); /* The spec requires that reserved registers are RAZ/WI; diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 2ff21ed6bb..52e9aca9c6 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -27,7 +27,7 @@ DECLARE_OBJ_CHECKERS(GICv3ITSState, GICv3ITSClass, struct GICv3ITSClass { GICv3ITSCommonClass parent_class; - void (*parent_reset)(DeviceState *dev); + ResettablePhases parent_phases; }; /* @@ -330,23 +330,20 @@ static MemTxResult get_vte(GICv3ITSState *s, uint32_t vpeid, VTEntry *vte) if (entry_addr == -1) { /* No L2 table entry, i.e. no valid VTE, or a memory error */ vte->valid = false; - goto out; + trace_gicv3_its_vte_read_fault(vpeid); + return MEMTX_OK; } vteval = address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, &res); if (res != MEMTX_OK) { - goto out; + trace_gicv3_its_vte_read_fault(vpeid); + return res; } vte->valid = FIELD_EX64(vteval, VTE, VALID); vte->vptsize = FIELD_EX64(vteval, VTE, VPTSIZE); vte->vptaddr = FIELD_EX64(vteval, VTE, VPTADDR); vte->rdbase = FIELD_EX64(vteval, VTE, RDBASE); -out: - if (res != MEMTX_OK) { - trace_gicv3_its_vte_read_fault(vpeid); - } else { - trace_gicv3_its_vte_read(vpeid, vte->valid, vte->vptsize, - vte->vptaddr, vte->rdbase); - } + trace_gicv3_its_vte_read(vpeid, vte->valid, vte->vptsize, + vte->vptaddr, vte->rdbase); return res; } @@ -548,10 +545,10 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, } if (cmdres == CMD_CONTINUE_OK && cmd == DISCARD) { - ITEntry ite = {}; + ITEntry i = {}; /* remove mapping from interrupt translation table */ - ite.valid = false; - return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL; + i.valid = false; + return update_ite(s, eventid, &dte, &i) ? CMD_CONTINUE_OK : CMD_STALL; } return CMD_CONTINUE_OK; } @@ -1633,7 +1630,7 @@ static bool its_writel(GICv3ITSState *s, hwaddr offset, /* RO register, ignore the write */ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write to RO register at offset " - TARGET_FMT_plx "\n", __func__, offset); + HWADDR_FMT_plx "\n", __func__, offset); } break; case GITS_CREADR + 4: @@ -1643,7 +1640,7 @@ static bool its_writel(GICv3ITSState *s, hwaddr offset, /* RO register, ignore the write */ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write to RO register at offset " - TARGET_FMT_plx "\n", __func__, offset); + HWADDR_FMT_plx "\n", __func__, offset); } break; case GITS_BASER ... GITS_BASER + 0x3f: @@ -1675,7 +1672,7 @@ static bool its_writel(GICv3ITSState *s, hwaddr offset, /* RO registers, ignore the write */ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write to RO register at offset " - TARGET_FMT_plx "\n", __func__, offset); + HWADDR_FMT_plx "\n", __func__, offset); break; default: result = false; @@ -1785,14 +1782,14 @@ static bool its_writell(GICv3ITSState *s, hwaddr offset, /* RO register, ignore the write */ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write to RO register at offset " - TARGET_FMT_plx "\n", __func__, offset); + HWADDR_FMT_plx "\n", __func__, offset); } break; case GITS_TYPER: /* RO registers, ignore the write */ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write to RO register at offset " - TARGET_FMT_plx "\n", __func__, offset); + HWADDR_FMT_plx "\n", __func__, offset); break; default: result = false; @@ -1851,7 +1848,7 @@ static MemTxResult gicv3_its_read(void *opaque, hwaddr offset, uint64_t *data, if (!result) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid guest read at offset " TARGET_FMT_plx + "%s: invalid guest read at offset " HWADDR_FMT_plx " size %u\n", __func__, offset, size); trace_gicv3_its_badread(offset, size); /* @@ -1887,7 +1884,7 @@ static MemTxResult gicv3_its_write(void *opaque, hwaddr offset, uint64_t data, if (!result) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid guest write at offset " TARGET_FMT_plx + "%s: invalid guest write at offset " HWADDR_FMT_plx " size %u\n", __func__, offset, size); trace_gicv3_its_badwrite(offset, data, size); /* @@ -1953,12 +1950,14 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp) } } -static void gicv3_its_reset(DeviceState *dev) +static void gicv3_its_reset_hold(Object *obj) { - GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev); + GICv3ITSState *s = ARM_GICV3_ITS_COMMON(obj); GICv3ITSClass *c = ARM_GICV3_ITS_GET_CLASS(s); - c->parent_reset(dev); + if (c->parent_phases.hold) { + c->parent_phases.hold(obj); + } /* Quiescent bit reset to 1 */ s->ctlr = FIELD_DP32(s->ctlr, GITS_CTLR, QUIESCENT, 1); @@ -2012,12 +2011,14 @@ static Property gicv3_its_props[] = { static void gicv3_its_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); GICv3ITSClass *ic = ARM_GICV3_ITS_CLASS(klass); GICv3ITSCommonClass *icc = ARM_GICV3_ITS_COMMON_CLASS(klass); dc->realize = gicv3_arm_its_realize; device_class_set_props(dc, gicv3_its_props); - device_class_set_parent_reset(dc, gicv3_its_reset, &ic->parent_reset); + resettable_class_set_parent_phases(rc, NULL, gicv3_its_reset_hold, NULL, + &ic->parent_phases); icc->post_load = gicv3_its_post_load; } diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c index 90b85f1e25..331d6b93cc 100644 --- a/hw/intc/arm_gicv3_its_common.c +++ b/hw/intc/arm_gicv3_its_common.c @@ -24,6 +24,7 @@ #include "hw/intc/arm_gicv3_its_common.h" #include "qemu/log.h" #include "qemu/module.h" +#include "sysemu/kvm.h" static int gicv3_its_pre_save(void *opaque) { @@ -53,7 +54,7 @@ static const VMStateDescription vmstate_its = { .pre_save = gicv3_its_pre_save, .post_load = gicv3_its_post_load, .priority = MIG_PRI_GICV3_ITS, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ctlr, GICv3ITSState), VMSTATE_UINT32(iidr, GICv3ITSState), VMSTATE_UINT64(cbaser, GICv3ITSState), @@ -122,9 +123,9 @@ void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops, msi_nonbroken = true; } -static void gicv3_its_common_reset(DeviceState *dev) +static void gicv3_its_common_reset_hold(Object *obj) { - GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev); + GICv3ITSState *s = ARM_GICV3_ITS_COMMON(obj); s->ctlr = 0; s->cbaser = 0; @@ -137,8 +138,9 @@ static void gicv3_its_common_reset(DeviceState *dev) static void gicv3_its_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); - dc->reset = gicv3_its_common_reset; + rc->phases.hold = gicv3_its_common_reset_hold; dc->vmsd = &vmstate_its; } @@ -157,3 +159,13 @@ static void gicv3_its_common_register_types(void) } type_init(gicv3_its_common_register_types) + +const char *its_class_name(void) +{ + if (kvm_irqchip_in_kernel()) { + return "arm-its-kvm"; + } else { + /* Software emulation based model */ + return "arm-gicv3-its"; + } +} diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index 529c7bd494..3befc960db 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" +#include "qemu/error-report.h" #include "hw/intc/arm_gicv3_its_common.h" #include "hw/qdev-properties.h" #include "sysemu/runstate.h" @@ -37,7 +38,7 @@ DECLARE_OBJ_CHECKERS(GICv3ITSState, KVMARMITSClass, struct KVMARMITSClass { GICv3ITSCommonClass parent_class; - void (*parent_reset)(DeviceState *dev); + ResettablePhases parent_phases; }; @@ -114,8 +115,7 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp) GITS_CTLR)) { error_setg(&s->migration_blocker, "This operating system kernel " "does not support vITS migration"); - if (migrate_add_blocker(s->migration_blocker, errp) < 0) { - error_free(s->migration_blocker); + if (migrate_add_blocker(&s->migration_blocker, errp) < 0) { return; } } else { @@ -124,7 +124,7 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp) kvm_msi_use_devid = true; kvm_gsi_direct_mapping = false; - kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled(); + kvm_msi_via_irqfd_allowed = true; } /** @@ -197,13 +197,15 @@ static void kvm_arm_its_post_load(GICv3ITSState *s) GITS_CTLR, &s->ctlr, true, &error_abort); } -static void kvm_arm_its_reset(DeviceState *dev) +static void kvm_arm_its_reset_hold(Object *obj) { - GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev); + GICv3ITSState *s = ARM_GICV3_ITS_COMMON(obj); KVMARMITSClass *c = KVM_ARM_ITS_GET_CLASS(s); int i; - c->parent_reset(dev); + if (c->parent_phases.hold) { + c->parent_phases.hold(obj); + } if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, KVM_DEV_ARM_ITS_CTRL_RESET)) { @@ -241,12 +243,14 @@ static Property kvm_arm_its_props[] = { static void kvm_arm_its_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); GICv3ITSCommonClass *icc = ARM_GICV3_ITS_COMMON_CLASS(klass); KVMARMITSClass *ic = KVM_ARM_ITS_CLASS(klass); dc->realize = kvm_arm_its_realize; device_class_set_props(dc, kvm_arm_its_props); - device_class_set_parent_reset(dc, kvm_arm_its_reset, &ic->parent_reset); + resettable_class_set_parent_phases(rc, NULL, kvm_arm_its_reset_hold, NULL, + &ic->parent_phases); icc->send_msi = kvm_its_send_msi; icc->pre_save = kvm_arm_its_pre_save; icc->post_load = kvm_arm_its_post_load; diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 3ca643ecba..77eb37e131 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -77,7 +77,7 @@ DECLARE_OBJ_CHECKERS(GICv3State, KVMARMGICv3Class, struct KVMARMGICv3Class { ARMGICv3CommonClass parent_class; DeviceRealize parent_realize; - void (*parent_reset)(DeviceState *dev); + ResettablePhases parent_phases; }; static void kvm_arm_gicv3_set_irq(void *opaque, int irq, int level) @@ -703,14 +703,16 @@ static void arm_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri) c->icc_ctlr_el1[GICV3_S] = c->icc_ctlr_el1[GICV3_NS]; } -static void kvm_arm_gicv3_reset(DeviceState *dev) +static void kvm_arm_gicv3_reset_hold(Object *obj) { - GICv3State *s = ARM_GICV3_COMMON(dev); + GICv3State *s = ARM_GICV3_COMMON(obj); KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s); DPRINTF("Reset\n"); - kgc->parent_reset(dev); + if (kgc->parent_phases.hold) { + kgc->parent_phases.hold(obj); + } if (s->migration_blocker) { DPRINTF("Cannot put kernel gic state, no kernel interface\n"); @@ -876,8 +878,7 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) GICD_CTLR)) { error_setg(&s->migration_blocker, "This operating system kernel does " "not support vGICv3 migration"); - if (migrate_add_blocker(s->migration_blocker, errp) < 0) { - error_free(s->migration_blocker); + if (migrate_add_blocker(&s->migration_blocker, errp) < 0) { return; } } @@ -890,6 +891,7 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass); KVMARMGICv3Class *kgc = KVM_ARM_GICV3_CLASS(klass); @@ -897,7 +899,8 @@ static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data) agcc->post_load = kvm_arm_gicv3_put; device_class_set_parent_realize(dc, kvm_arm_gicv3_realize, &kgc->parent_realize); - device_class_set_parent_reset(dc, kvm_arm_gicv3_reset, &kgc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, kvm_arm_gicv3_reset_hold, NULL, + &kgc->parent_phases); } static const TypeInfo kvm_arm_gicv3_info = { diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index f1ecb2502b..8153525849 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -492,9 +492,9 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset, return MEMTX_OK; case GICR_WAKER: /* Only the ProcessorSleep bit is writable. When the guest sets - * it it requests that we transition the channel between the + * it, it requests that we transition the channel between the * redistributor and the cpu interface to quiescent, and that - * we set the ChildrenAsleep bit once the inteface has reached the + * we set the ChildrenAsleep bit once the interface has reached the * quiescent state. * Setting the ProcessorSleep to 0 reverses the quiescing, and * ChildrenAsleep is cleared once the transition is complete. @@ -601,7 +601,7 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset, /* RO registers, ignore the write */ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write to RO register at offset " - TARGET_FMT_plx "\n", __func__, offset); + HWADDR_FMT_plx "\n", __func__, offset); return MEMTX_OK; /* * VLPI frame registers. We don't need a version check for @@ -668,7 +668,7 @@ static MemTxResult gicr_writell(GICv3CPUState *cs, hwaddr offset, /* RO register, ignore the write */ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write to RO register at offset " - TARGET_FMT_plx "\n", __func__, offset); + HWADDR_FMT_plx "\n", __func__, offset); return MEMTX_OK; /* * VLPI frame registers. We don't need a version check for @@ -727,7 +727,7 @@ MemTxResult gicv3_redist_read(void *opaque, hwaddr offset, uint64_t *data, if (r != MEMTX_OK) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid guest read at offset " TARGET_FMT_plx + "%s: invalid guest read at offset " HWADDR_FMT_plx " size %u\n", __func__, offset, size); trace_gicv3_redist_badread(gicv3_redist_affid(cs), offset, size, attrs.secure); @@ -786,7 +786,7 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data, if (r != MEMTX_OK) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid guest write at offset " TARGET_FMT_plx + "%s: invalid guest write at offset " HWADDR_FMT_plx " size %u\n", __func__, offset, size); trace_gicv3_redist_badwrite(gicv3_redist_affid(cs), offset, data, size, attrs.secure); diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 13df002ce4..404a445138 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -18,8 +18,10 @@ #include "hw/intc/armv7m_nvic.h" #include "hw/irq.h" #include "hw/qdev-properties.h" +#include "sysemu/tcg.h" #include "sysemu/runstate.h" #include "target/arm/cpu.h" +#include "target/arm/cpu-features.h" #include "exec/exec-all.h" #include "exec/memop.h" #include "qemu/log.h" @@ -389,7 +391,7 @@ static inline int nvic_exec_prio(NVICState *s) return MIN(running, s->exception_prio); } -bool armv7m_nvic_neg_prio_requested(void *opaque, bool secure) +bool armv7m_nvic_neg_prio_requested(NVICState *s, bool secure) { /* Return true if the requested execution priority is negative * for the specified security state, ie that security state @@ -399,8 +401,6 @@ bool armv7m_nvic_neg_prio_requested(void *opaque, bool secure) * mean we don't allow FAULTMASK_NS to actually make the execution * priority negative). Compare pseudocode IsReqExcPriNeg(). */ - NVICState *s = opaque; - if (s->cpu->env.v7m.faultmask[secure]) { return true; } @@ -418,17 +418,13 @@ bool armv7m_nvic_neg_prio_requested(void *opaque, bool secure) return false; } -bool armv7m_nvic_can_take_pending_exception(void *opaque) +bool armv7m_nvic_can_take_pending_exception(NVICState *s) { - NVICState *s = opaque; - return nvic_exec_prio(s) > nvic_pending_prio(s); } -int armv7m_nvic_raw_execution_priority(void *opaque) +int armv7m_nvic_raw_execution_priority(NVICState *s) { - NVICState *s = opaque; - return s->exception_prio; } @@ -506,9 +502,8 @@ static void nvic_irq_update(NVICState *s) * if @secure is true and @irq does not specify one of the fixed set * of architecturally banked exceptions. */ -static void armv7m_nvic_clear_pending(void *opaque, int irq, bool secure) +static void armv7m_nvic_clear_pending(NVICState *s, int irq, bool secure) { - NVICState *s = (NVICState *)opaque; VecInfo *vec; assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq); @@ -584,7 +579,7 @@ static void do_armv7m_nvic_set_pending(void *opaque, int irq, bool secure, * which saves having to have an extra argument is_terminal * that we'd only use in one place. */ - cpu_abort(&s->cpu->parent_obj, + cpu_abort(CPU(s->cpu), "Lockup: can't take terminal derived exception " "(original exception priority %d)\n", s->vectpending_prio); @@ -650,7 +645,7 @@ static void do_armv7m_nvic_set_pending(void *opaque, int irq, bool secure, * Lockup condition due to a guest bug. We don't model * Lockup, so report via cpu_abort() instead. */ - cpu_abort(&s->cpu->parent_obj, + cpu_abort(CPU(s->cpu), "Lockup: can't escalate %d to HardFault " "(current priority %d)\n", irq, running); } @@ -666,17 +661,17 @@ static void do_armv7m_nvic_set_pending(void *opaque, int irq, bool secure, } } -void armv7m_nvic_set_pending(void *opaque, int irq, bool secure) +void armv7m_nvic_set_pending(NVICState *s, int irq, bool secure) { - do_armv7m_nvic_set_pending(opaque, irq, secure, false); + do_armv7m_nvic_set_pending(s, irq, secure, false); } -void armv7m_nvic_set_pending_derived(void *opaque, int irq, bool secure) +void armv7m_nvic_set_pending_derived(NVICState *s, int irq, bool secure) { - do_armv7m_nvic_set_pending(opaque, irq, secure, true); + do_armv7m_nvic_set_pending(s, irq, secure, true); } -void armv7m_nvic_set_pending_lazyfp(void *opaque, int irq, bool secure) +void armv7m_nvic_set_pending_lazyfp(NVICState *s, int irq, bool secure) { /* * Pend an exception during lazy FP stacking. This differs @@ -684,7 +679,6 @@ void armv7m_nvic_set_pending_lazyfp(void *opaque, int irq, bool secure) * whether we should escalate depends on the saved context * in the FPCCR register, not on the current state of the CPU/NVIC. */ - NVICState *s = (NVICState *)opaque; bool banked = exc_is_banked(irq); VecInfo *vec; bool targets_secure; @@ -749,7 +743,7 @@ void armv7m_nvic_set_pending_lazyfp(void *opaque, int irq, bool secure) * We want to escalate to HardFault but the context the * FP state belongs to prevents the exception pre-empting. */ - cpu_abort(&s->cpu->parent_obj, + cpu_abort(CPU(s->cpu), "Lockup: can't escalate to HardFault during " "lazy FP register stacking\n"); } @@ -773,9 +767,8 @@ void armv7m_nvic_set_pending_lazyfp(void *opaque, int irq, bool secure) } /* Make pending IRQ active. */ -void armv7m_nvic_acknowledge_irq(void *opaque) +void armv7m_nvic_acknowledge_irq(NVICState *s) { - NVICState *s = (NVICState *)opaque; CPUARMState *env = &s->cpu->env; const int pending = s->vectpending; const int running = nvic_exec_prio(s); @@ -814,10 +807,9 @@ static bool vectpending_targets_secure(NVICState *s) exc_targets_secure(s, s->vectpending); } -void armv7m_nvic_get_pending_irq_info(void *opaque, +void armv7m_nvic_get_pending_irq_info(NVICState *s, int *pirq, bool *ptargets_secure) { - NVICState *s = (NVICState *)opaque; const int pending = s->vectpending; bool targets_secure; @@ -831,9 +823,8 @@ void armv7m_nvic_get_pending_irq_info(void *opaque, *pirq = pending; } -int armv7m_nvic_complete_irq(void *opaque, int irq, bool secure) +int armv7m_nvic_complete_irq(NVICState *s, int irq, bool secure) { - NVICState *s = (NVICState *)opaque; VecInfo *vec = NULL; int ret = 0; @@ -904,7 +895,7 @@ int armv7m_nvic_complete_irq(void *opaque, int irq, bool secure) vec->active = 0; if (vec->level) { /* Re-pend the exception if it's still held high; only - * happens for extenal IRQs + * happens for external IRQs */ assert(irq >= NVIC_FIRST_IRQ); vec->pending = 1; @@ -915,7 +906,7 @@ int armv7m_nvic_complete_irq(void *opaque, int irq, bool secure) return ret; } -bool armv7m_nvic_get_ready_status(void *opaque, int irq, bool secure) +bool armv7m_nvic_get_ready_status(NVICState *s, int irq, bool secure) { /* * Return whether an exception is "ready", i.e. it is enabled and is @@ -926,7 +917,6 @@ bool armv7m_nvic_get_ready_status(void *opaque, int irq, bool secure) * for non-banked exceptions secure is always false; for banked exceptions * it indicates which of the exceptions is required. */ - NVICState *s = (NVICState *)opaque; bool banked = exc_is_banked(irq); VecInfo *vec; int running = nvic_exec_prio(s); @@ -2389,8 +2379,15 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr, startvec = 8 * (offset - 0x280) + NVIC_FIRST_IRQ; /* vector # */ for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) { + /* + * Note that if the input line is still held high and the interrupt + * is not active then rule R_CVJS requires that the Pending state + * remains set; in that case we mustn't let it be cleared. + */ if (value & (1 << i) && - (attrs.secure || s->itns[startvec + i])) { + (attrs.secure || s->itns[startvec + i]) && + !(setval == 0 && s->vectors[startvec + i].level && + !s->vectors[startvec + i].active)) { s->vectors[startvec + i].pending = setval; } } @@ -2459,8 +2456,10 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr, /* This is UNPREDICTABLE; treat as RAZ/WI */ exit_ok: - /* Ensure any changes made are reflected in the cached hflags. */ - arm_rebuild_hflags(&s->cpu->env); + if (tcg_enabled()) { + /* Ensure any changes made are reflected in the cached hflags. */ + arm_rebuild_hflags(&s->cpu->env); + } return MEMTX_OK; } @@ -2499,7 +2498,7 @@ static const VMStateDescription vmstate_VecInfo = { .name = "armv7m_nvic_info", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT16(prio, VecInfo), VMSTATE_UINT8(enabled, VecInfo), VMSTATE_UINT8(pending, VecInfo), @@ -2544,7 +2543,7 @@ static const VMStateDescription vmstate_nvic_security = { .minimum_version_id = 1, .needed = nvic_security_needed, .post_load = &nvic_security_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(sec_vectors, NVICState, NVIC_INTERNAL_VECTORS, 1, vmstate_VecInfo, VecInfo), VMSTATE_UINT32(prigroup[M_REG_S], NVICState), @@ -2558,13 +2557,13 @@ static const VMStateDescription vmstate_nvic = { .version_id = 4, .minimum_version_id = 4, .post_load = &nvic_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(vectors, NVICState, NVIC_MAX_VECTORS, 1, vmstate_VecInfo, VecInfo), VMSTATE_UINT32(prigroup[M_REG_NS], NVICState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_nvic_security, NULL } @@ -2573,6 +2572,11 @@ static const VMStateDescription vmstate_nvic = { static Property props_nvic[] = { /* Number of external IRQ lines (so excluding the 16 internal exceptions) */ DEFINE_PROP_UINT32("num-irq", NVICState, num_irq, 64), + /* + * Number of the maximum priority bits that can be used. 0 means + * to use a reasonable default. + */ + DEFINE_PROP_UINT8("num-prio-bits", NVICState, num_prio_bits, 0), DEFINE_PROP_END_OF_LIST() }; @@ -2641,11 +2645,14 @@ static void armv7m_nvic_reset(DeviceState *dev) } } - /* - * 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); + if (tcg_enabled()) { + /* + * 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) @@ -2683,7 +2690,23 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp) /* include space for internal exception vectors */ s->num_irq += NVIC_FIRST_IRQ; - s->num_prio_bits = arm_feature(&s->cpu->env, ARM_FEATURE_V7) ? 8 : 2; + if (s->num_prio_bits == 0) { + /* + * If left unspecified, use 2 bits by default on Cortex-M0/M0+/M1 + * and 8 bits otherwise. + */ + s->num_prio_bits = arm_feature(&s->cpu->env, ARM_FEATURE_V7) ? 8 : 2; + } else { + uint8_t min_prio_bits = + arm_feature(&s->cpu->env, ARM_FEATURE_V7) ? 3 : 2; + if (s->num_prio_bits < min_prio_bits || s->num_prio_bits > 8) { + error_setg(errp, + "num-prio-bits %d is outside " + "NVIC acceptable range [%d-8]", + s->num_prio_bits, min_prio_bits); + return; + } + } /* * This device provides a single memory region which covers the diff --git a/hw/intc/aspeed_vic.c b/hw/intc/aspeed_vic.c index 5ba06c5262..ba1d953c2c 100644 --- a/hw/intc/aspeed_vic.c +++ b/hw/intc/aspeed_vic.c @@ -326,7 +326,7 @@ static const VMStateDescription vmstate_aspeed_vic = { .name = "aspeed.new-vic", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(level, AspeedVICState), VMSTATE_UINT64(raw, AspeedVICState), VMSTATE_UINT64(select, AspeedVICState), diff --git a/hw/intc/bcm2835_ic.c b/hw/intc/bcm2835_ic.c index 4513fad16f..2c2e2b1822 100644 --- a/hw/intc/bcm2835_ic.c +++ b/hw/intc/bcm2835_ic.c @@ -208,7 +208,7 @@ static const VMStateDescription vmstate_bcm2835_ic = { .name = TYPE_BCM2835_IC, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(gpu_irq_level, BCM2835ICState), VMSTATE_UINT64(gpu_irq_enable, BCM2835ICState), VMSTATE_UINT8(arm_irq_level, BCM2835ICState), diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c index b0589df188..81faf032b0 100644 --- a/hw/intc/bcm2836_control.c +++ b/hw/intc/bcm2836_control.c @@ -369,7 +369,7 @@ static const VMStateDescription vmstate_bcm2836_control = { .name = TYPE_BCM2836_CONTROL, .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState, BCM2836_NCORES * BCM2836_MBPERCORE), VMSTATE_UINT8(route_gpu_irq, BCM2836ControlState), diff --git a/hw/intc/exynos4210_combiner.c b/hw/intc/exynos4210_combiner.c index a289510bdb..f0d310a0eb 100644 --- a/hw/intc/exynos4210_combiner.c +++ b/hw/intc/exynos4210_combiner.c @@ -54,7 +54,7 @@ static const VMStateDescription vmstate_exynos4210_combiner_group_state = { .name = "exynos4210.combiner.groupstate", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(src_mask, CombinerGroupState), VMSTATE_UINT8(src_pending, CombinerGroupState), VMSTATE_END_OF_LIST() @@ -65,7 +65,7 @@ static const VMStateDescription vmstate_exynos4210_combiner = { .name = "exynos4210.combiner", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(group, Exynos4210CombinerState, IIC_NGRP, 0, vmstate_exynos4210_combiner_group_state, CombinerGroupState), VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState, @@ -120,7 +120,7 @@ exynos4210_combiner_read(void *opaque, hwaddr offset, unsigned size) default: if (offset >> 2 >= IIC_REGSET_SIZE) { hw_error("exynos4210.combiner: overflow of reg_set by 0x" - TARGET_FMT_plx "offset\n", offset); + HWADDR_FMT_plx "offset\n", offset); } val = s->reg_set[offset >> 2]; } @@ -184,19 +184,19 @@ static void exynos4210_combiner_write(void *opaque, hwaddr offset, if (req_quad_base_n >= IIC_NGRP) { hw_error("exynos4210.combiner: unallowed write access at offset 0x" - TARGET_FMT_plx "\n", offset); + HWADDR_FMT_plx "\n", offset); return; } if (reg_n > 1) { hw_error("exynos4210.combiner: unallowed write access at offset 0x" - TARGET_FMT_plx "\n", offset); + HWADDR_FMT_plx "\n", offset); return; } if (offset >> 2 >= IIC_REGSET_SIZE) { hw_error("exynos4210.combiner: overflow of reg_set by 0x" - TARGET_FMT_plx "offset\n", offset); + HWADDR_FMT_plx "offset\n", offset); } s->reg_set[offset >> 2] = val; @@ -246,7 +246,7 @@ static void exynos4210_combiner_write(void *opaque, hwaddr offset, break; default: hw_error("exynos4210.combiner: unallowed write access at offset 0x" - TARGET_FMT_plx "\n", offset); + HWADDR_FMT_plx "\n", offset); break; } } diff --git a/hw/intc/goldfish_pic.c b/hw/intc/goldfish_pic.c index dfd53275f6..d662dfeb99 100644 --- a/hw/intc/goldfish_pic.c +++ b/hw/intc/goldfish_pic.c @@ -161,7 +161,7 @@ static const VMStateDescription vmstate_goldfish_pic = { .name = "goldfish_pic", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(pending, GoldfishPICState), VMSTATE_UINT32(enabled, GoldfishPICState), VMSTATE_END_OF_LIST() diff --git a/hw/intc/grlib_irqmp.c b/hw/intc/grlib_irqmp.c index 3bfe2544b7..c6c51a349c 100644 --- a/hw/intc/grlib_irqmp.c +++ b/hw/intc/grlib_irqmp.c @@ -1,9 +1,11 @@ /* * QEMU GRLIB IRQMP Emulator * - * (Multiprocessor and extended interrupt not supported) + * (Extended interrupt not supported) * - * Copyright (c) 2010-2019 AdaCore + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2010-2024 AdaCore * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +31,7 @@ #include "hw/sysbus.h" #include "hw/qdev-properties.h" -#include "hw/sparc/grlib.h" +#include "hw/intc/grlib_irqmp.h" #include "trace.h" #include "qapi/error.h" @@ -50,6 +52,10 @@ #define FORCE_OFFSET 0x80 #define EXTENDED_OFFSET 0xC0 +/* Multiprocessor Status Register */ +#define MP_STATUS_CPU_STATUS_MASK ((1 << IRQMP_MAX_CPU)-2) +#define MP_STATUS_NCPU_SHIFT 28 + #define MAX_PILS 16 OBJECT_DECLARE_SIMPLE_TYPE(IRQMP, GRLIB_IRQMP) @@ -61,14 +67,17 @@ struct IRQMP { MemoryRegion iomem; + unsigned int ncpus; IRQMPState *state; - qemu_irq irq; + qemu_irq start_signal[IRQMP_MAX_CPU]; + qemu_irq irq[IRQMP_MAX_CPU]; }; struct IRQMPState { uint32_t level; uint32_t pending; uint32_t clear; + uint32_t mpstatus; uint32_t broadcast; uint32_t mask[IRQMP_MAX_CPU]; @@ -80,37 +89,35 @@ struct IRQMPState { static void grlib_irqmp_check_irqs(IRQMPState *state) { - uint32_t pend = 0; - uint32_t level0 = 0; - uint32_t level1 = 0; + int i; assert(state != NULL); assert(state->parent != NULL); - /* IRQ for CPU 0 (no SMP support) */ - pend = (state->pending | state->force[0]) - & state->mask[0]; + for (i = 0; i < state->parent->ncpus; i++) { + uint32_t pend = (state->pending | state->force[i]) & state->mask[i]; + uint32_t level0 = pend & ~state->level; + uint32_t level1 = pend & state->level; - level0 = pend & ~state->level; - level1 = pend & state->level; + trace_grlib_irqmp_check_irqs(state->pending, state->force[i], + state->mask[i], level1, level0); - trace_grlib_irqmp_check_irqs(state->pending, state->force[0], - state->mask[0], level1, level0); - - /* Trigger level1 interrupt first and level0 if there is no level1 */ - qemu_set_irq(state->parent->irq, level1 ?: level0); + /* Trigger level1 interrupt first and level0 if there is no level1 */ + qemu_set_irq(state->parent->irq[i], level1 ?: level0); + } } -static void grlib_irqmp_ack_mask(IRQMPState *state, uint32_t mask) +static void grlib_irqmp_ack_mask(IRQMPState *state, unsigned int cpu, + uint32_t mask) { /* Clear registers */ state->pending &= ~mask; - state->force[0] &= ~mask; /* Only CPU 0 (No SMP support) */ + state->force[cpu] &= ~mask; grlib_irqmp_check_irqs(state); } -void grlib_irqmp_ack(DeviceState *dev, int intno) +void grlib_irqmp_ack(DeviceState *dev, unsigned int cpu, int intno) { IRQMP *irqmp = GRLIB_IRQMP(dev); IRQMPState *state; @@ -124,7 +131,7 @@ void grlib_irqmp_ack(DeviceState *dev, int intno) trace_grlib_irqmp_ack(intno); - grlib_irqmp_ack_mask(state, mask); + grlib_irqmp_ack_mask(state, cpu, mask); } static void grlib_irqmp_set_irq(void *opaque, int irq, int level) @@ -150,7 +157,6 @@ static void grlib_irqmp_set_irq(void *opaque, int irq, int level) s->pending |= 1 << irq; } grlib_irqmp_check_irqs(s); - } } @@ -179,10 +185,12 @@ static uint64_t grlib_irqmp_read(void *opaque, hwaddr addr, return state->force[0]; case CLEAR_OFFSET: - case MP_STATUS_OFFSET: /* Always read as 0 */ return 0; + case MP_STATUS_OFFSET: + return state->mpstatus; + case BROADCAST_OFFSET: return state->broadcast; @@ -221,8 +229,9 @@ static uint64_t grlib_irqmp_read(void *opaque, hwaddr addr, static void grlib_irqmp_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - IRQMP *irqmp = opaque; + IRQMP *irqmp = opaque; IRQMPState *state; + int i; assert(irqmp != NULL); state = irqmp->state; @@ -251,11 +260,24 @@ static void grlib_irqmp_write(void *opaque, hwaddr addr, case CLEAR_OFFSET: value &= ~1; /* clean up the value */ - grlib_irqmp_ack_mask(state, value); + for (i = 0; i < irqmp->ncpus; i++) { + grlib_irqmp_ack_mask(state, i, value); + } return; case MP_STATUS_OFFSET: - /* Read Only (no SMP support) */ + /* + * Writing and reading operations are reversed for the CPU status. + * Writing "1" will start the CPU, but reading "1" means that the CPU + * is power-down. + */ + value &= MP_STATUS_CPU_STATUS_MASK; + for (i = 0; i < irqmp->ncpus; i++) { + if ((value >> i) & 1) { + qemu_set_irq(irqmp->start_signal[i], 1); + state->mpstatus &= ~(1 << i); + } + } return; case BROADCAST_OFFSET: @@ -322,35 +344,56 @@ static void grlib_irqmp_reset(DeviceState *d) memset(irqmp->state, 0, sizeof *irqmp->state); irqmp->state->parent = irqmp; + irqmp->state->mpstatus = ((irqmp->ncpus - 1) << MP_STATUS_NCPU_SHIFT) | + ((1 << irqmp->ncpus) - 2); } -static void grlib_irqmp_init(Object *obj) +static void grlib_irqmp_realize(DeviceState *dev, Error **errp) { - IRQMP *irqmp = GRLIB_IRQMP(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); + IRQMP *irqmp = GRLIB_IRQMP(dev); - qdev_init_gpio_in(DEVICE(obj), grlib_irqmp_set_irq, MAX_PILS); - qdev_init_gpio_out_named(DEVICE(obj), &irqmp->irq, "grlib-irq", 1); - memory_region_init_io(&irqmp->iomem, obj, &grlib_irqmp_ops, irqmp, + if ((!irqmp->ncpus) || (irqmp->ncpus > IRQMP_MAX_CPU)) { + error_setg(errp, "Invalid ncpus properties: " + "%u, must be 0 < ncpus =< %u.", irqmp->ncpus, + IRQMP_MAX_CPU); + return; + } + + qdev_init_gpio_in(dev, grlib_irqmp_set_irq, MAX_PILS); + + /* + * Transitionning from 0 to 1 starts the CPUs. The opposite can't + * happen. + */ + qdev_init_gpio_out_named(dev, irqmp->start_signal, "grlib-start-cpu", + IRQMP_MAX_CPU); + qdev_init_gpio_out_named(dev, irqmp->irq, "grlib-irq", irqmp->ncpus); + memory_region_init_io(&irqmp->iomem, OBJECT(dev), &grlib_irqmp_ops, irqmp, "irqmp", IRQMP_REG_SIZE); irqmp->state = g_malloc0(sizeof *irqmp->state); - sysbus_init_mmio(dev, &irqmp->iomem); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &irqmp->iomem); } +static Property grlib_irqmp_properties[] = { + DEFINE_PROP_UINT32("ncpus", IRQMP, ncpus, 1), + DEFINE_PROP_END_OF_LIST(), +}; + static void grlib_irqmp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = grlib_irqmp_realize; dc->reset = grlib_irqmp_reset; + device_class_set_props(dc, grlib_irqmp_properties); } static const TypeInfo grlib_irqmp_info = { .name = TYPE_GRLIB_IRQMP, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IRQMP), - .instance_init = grlib_irqmp_init, .class_init = grlib_irqmp_class_init, }; diff --git a/hw/intc/heathrow_pic.c b/hw/intc/heathrow_pic.c index cb97c315da..c2946ba1ad 100644 --- a/hw/intc/heathrow_pic.c +++ b/hw/intc/heathrow_pic.c @@ -24,7 +24,6 @@ */ #include "qemu/osdep.h" -#include "hw/ppc/mac.h" #include "migration/vmstate.h" #include "qemu/module.h" #include "hw/intc/heathrow_pic.h" @@ -142,7 +141,7 @@ static const VMStateDescription vmstate_heathrow_pic_one = { .name = "heathrow_pic_one", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(events, HeathrowPICState), VMSTATE_UINT32(mask, HeathrowPICState), VMSTATE_UINT32(levels, HeathrowPICState), @@ -155,7 +154,7 @@ static const VMStateDescription vmstate_heathrow = { .name = "heathrow_pic", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(pics, HeathrowState, 2, 1, vmstate_heathrow_pic_one, HeathrowPICState), VMSTATE_END_OF_LIST() diff --git a/hw/intc/i8259.c b/hw/intc/i8259.c index cc4e21ffec..bbae2d87f4 100644 --- a/hw/intc/i8259.c +++ b/hw/intc/i8259.c @@ -55,7 +55,7 @@ struct PICClass { #ifdef DEBUG_IRQ_LATENCY static int64_t irq_time[16]; #endif -DeviceState *isa_pic; +PICCommonState *isa_pic; static PICCommonState *slave_pic; /* return the highest priority found in mask (highest = smallest @@ -133,7 +133,7 @@ static void pic_set_irq(void *opaque, int irq, int level) } #endif - if (s->elcr & mask) { + if (s->ltim || (s->elcr & mask)) { /* level triggered */ if (level) { s->irr |= mask; @@ -167,15 +167,14 @@ static void pic_intack(PICCommonState *s, int irq) s->isr |= (1 << irq); } /* We don't clear a level sensitive interrupt here */ - if (!(s->elcr & (1 << irq))) { + if (!s->ltim && !(s->elcr & (1 << irq))) { s->irr &= ~(1 << irq); } pic_update_irq(s); } -int pic_read_irq(DeviceState *d) +int pic_read_irq(PICCommonState *s) { - PICCommonState *s = PIC_COMMON(d); int irq, intno; irq = pic_get_irq(s); @@ -225,6 +224,7 @@ static void pic_reset(DeviceState *dev) PICCommonState *s = PIC_COMMON(dev); s->elcr = 0; + s->ltim = 0; pic_init_reset(s); } @@ -244,10 +244,7 @@ static void pic_ioport_write(void *opaque, hwaddr addr64, s->init_state = 1; s->init4 = val & 1; s->single_mode = val & 2; - if (val & 0x08) { - qemu_log_mask(LOG_UNIMP, - "i8259: level sensitive irq not supported\n"); - } + s->ltim = val & 8; } else if (val & 0x08) { if (val & 0x04) { s->poll = 1; @@ -354,10 +351,8 @@ static uint64_t pic_ioport_read(void *opaque, hwaddr addr, return ret; } -int pic_get_output(DeviceState *d) +int pic_get_output(PICCommonState *s) { - PICCommonState *s = PIC_COMMON(d); - return (pic_get_irq(s) >= 0); } @@ -409,7 +404,7 @@ static void pic_realize(DeviceState *dev, Error **errp) pc->parent_realize(dev, errp); } -qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq) +qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq_in) { qemu_irq *irq_set; DeviceState *dev; @@ -421,12 +416,12 @@ qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq) isadev = i8259_init_chip(TYPE_I8259, bus, true); dev = DEVICE(isadev); - qdev_connect_gpio_out(dev, 0, parent_irq); + qdev_connect_gpio_out(dev, 0, parent_irq_in); for (i = 0 ; i < 8; i++) { irq_set[i] = qdev_get_gpio_in(dev, i); } - isa_pic = dev; + isa_pic = PIC_COMMON(dev); isadev = i8259_init_chip(TYPE_I8259, bus, false); dev = DEVICE(isadev); diff --git a/hw/intc/i8259_common.c b/hw/intc/i8259_common.c index af2e4a2241..ee0041115c 100644 --- a/hw/intc/i8259_common.c +++ b/hw/intc/i8259_common.c @@ -51,7 +51,7 @@ void pic_reset_common(PICCommonState *s) s->special_fully_nested_mode = 0; s->init4 = 0; s->single_mode = 0; - /* Note: ELCR is not reset */ + /* Note: ELCR and LTIM are not reset */ } static int pic_dispatch_pre_save(void *opaque) @@ -144,13 +144,31 @@ static void pic_print_info(InterruptStatsProvider *obj, Monitor *mon) s->special_fully_nested_mode); } +static bool ltim_state_needed(void *opaque) +{ + PICCommonState *s = PIC_COMMON(opaque); + + return !!s->ltim; +} + +static const VMStateDescription vmstate_pic_ltim = { + .name = "i8259/ltim", + .version_id = 1, + .minimum_version_id = 1, + .needed = ltim_state_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT8(ltim, PICCommonState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_pic_common = { .name = "i8259", .version_id = 1, .minimum_version_id = 1, .pre_save = pic_dispatch_pre_save, .post_load = pic_dispatch_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(last_irr, PICCommonState), VMSTATE_UINT8(irr, PICCommonState), VMSTATE_UINT8(imr, PICCommonState), @@ -168,6 +186,10 @@ static const VMStateDescription vmstate_pic_common = { VMSTATE_UINT8(single_mode, PICCommonState), VMSTATE_UINT8(elcr, PICCommonState), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_pic_ltim, + NULL } }; diff --git a/hw/intc/imx_avic.c b/hw/intc/imx_avic.c index 63fc602a1a..aedc708bed 100644 --- a/hw/intc/imx_avic.c +++ b/hw/intc/imx_avic.c @@ -38,7 +38,7 @@ static const VMStateDescription vmstate_imx_avic = { .name = TYPE_IMX_AVIC, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(pending, IMXAVICState), VMSTATE_UINT64(enabled, IMXAVICState), VMSTATE_UINT64(is_fiq, IMXAVICState), diff --git a/hw/intc/imx_gpcv2.c b/hw/intc/imx_gpcv2.c index 237d5f97eb..af45e5194c 100644 --- a/hw/intc/imx_gpcv2.c +++ b/hw/intc/imx_gpcv2.c @@ -96,7 +96,7 @@ static const VMStateDescription vmstate_imx_gpcv2 = { .name = TYPE_IMX_GPCV2, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, IMXGPCv2State, GPC_NUM), VMSTATE_END_OF_LIST() }, diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index 264262959d..716ffc8bbb 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -24,10 +24,10 @@ #include "qapi/error.h" #include "monitor/monitor.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/intc/ioapic.h" +#include "hw/intc/ioapic_internal.h" #include "hw/pci/msi.h" #include "hw/qdev-properties.h" #include "sysemu/kvm.h" @@ -405,6 +405,7 @@ ioapic_mem_write(void *opaque, hwaddr addr, uint64_t val, s->ioredtbl[index] |= ro_bits; s->irq_eoi[index] = 0; ioapic_fix_edge_remote_irr(&s->ioredtbl[index]); + ioapic_update_kvm_routes(s); ioapic_service(s); } } @@ -417,8 +418,6 @@ ioapic_mem_write(void *opaque, hwaddr addr, uint64_t val, ioapic_eoi_broadcast(val); break; } - - ioapic_update_kvm_routes(s); } static const MemoryRegionOps ioapic_io_ops = { diff --git a/hw/intc/ioapic_common.c b/hw/intc/ioapic_common.c index aa5f760871..efbe6958c8 100644 --- a/hw/intc/ioapic_common.c +++ b/hw/intc/ioapic_common.c @@ -24,9 +24,9 @@ #include "qemu/module.h" #include "migration/vmstate.h" #include "monitor/monitor.h" -#include "hw/i386/ioapic.h" -#include "hw/i386/ioapic_internal.h" #include "hw/intc/intc.h" +#include "hw/intc/ioapic.h" +#include "hw/intc/ioapic_internal.h" #include "hw/sysbus.h" /* ioapic_no count start from 0 to MAX_IOAPICS, @@ -152,6 +152,7 @@ static int ioapic_dispatch_post_load(void *opaque, int version_id) static void ioapic_common_realize(DeviceState *dev, Error **errp) { + ERRP_GUARD(); IOAPICCommonState *s = IOAPIC_COMMON(dev); IOAPICCommonClass *info; @@ -162,6 +163,9 @@ static void ioapic_common_realize(DeviceState *dev, Error **errp) info = IOAPIC_COMMON_GET_CLASS(s); info->realize(dev, errp); + if (*errp) { + return; + } sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->io_memory); ioapic_no++; @@ -182,7 +186,7 @@ static const VMStateDescription vmstate_ioapic_common = { .minimum_version_id = 1, .pre_save = ioapic_dispatch_pre_save, .post_load = ioapic_dispatch_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(id, IOAPICCommonState), VMSTATE_UINT8(ioregsel, IOAPICCommonState), VMSTATE_UNUSED_V(2, 8), /* to account for qemu-kvm's v2 format */ diff --git a/include/hw/i386/ioapic_internal.h b/hw/intc/ioapic_internal.h similarity index 96% rename from include/hw/i386/ioapic_internal.h rename to hw/intc/ioapic_internal.h index 9880443cc7..37b8565539 100644 --- a/include/hw/i386/ioapic_internal.h +++ b/hw/intc/ioapic_internal.h @@ -19,10 +19,11 @@ * License along with this library; if not, see . */ -#ifndef QEMU_IOAPIC_INTERNAL_H -#define QEMU_IOAPIC_INTERNAL_H +#ifndef HW_INTC_IOAPIC_INTERNAL_H +#define HW_INTC_IOAPIC_INTERNAL_H #include "exec/memory.h" +#include "hw/intc/ioapic.h" #include "hw/sysbus.h" #include "qemu/notify.h" #include "qom/object.h" @@ -114,4 +115,4 @@ void ioapic_reset_common(DeviceState *dev); void ioapic_stat_update_irq(IOAPICCommonState *s, int irq, int level); -#endif /* QEMU_IOAPIC_INTERNAL_H */ +#endif /* HW_INTC_IOAPIC_INTERNAL_H */ diff --git a/hw/intc/kvm_irqcount.c b/hw/intc/kvm_irqcount.c new file mode 100644 index 0000000000..2ef8a83a7a --- /dev/null +++ b/hw/intc/kvm_irqcount.c @@ -0,0 +1,49 @@ +/* + * KVM PIC functions for counting the delivered IRQs. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "hw/intc/kvm_irqcount.h" +#include "trace.h" + +static int kvm_irq_delivered; + +void kvm_report_irq_delivered(int delivered) +{ + kvm_irq_delivered += delivered; + + trace_kvm_report_irq_delivered(kvm_irq_delivered); +} + +void kvm_reset_irq_delivered(void) +{ + /* + * Copy this into a local variable to encourage gcc to emit a plain + * register for a sys/sdt.h marker. For details on this workaround, see: + * https://sourceware.org/bugzilla/show_bug.cgi?id=13296 + */ + volatile int k_i_d = kvm_irq_delivered; + trace_kvm_reset_irq_delivered(k_i_d); + + kvm_irq_delivered = 0; +} + +int kvm_get_irq_delivered(void) +{ + trace_kvm_get_irq_delivered(kvm_irq_delivered); + + return kvm_irq_delivered; +} diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index 22803969bc..0b358548eb 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -8,6 +8,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "qemu/log.h" +#include "qapi/error.h" #include "hw/irq.h" #include "hw/sysbus.h" #include "hw/loongarch/virt.h" @@ -32,23 +33,23 @@ static void extioi_update_irq(LoongArchExtIOI *s, int irq, int level) if (((s->enable[irq_index]) & irq_mask) == 0) { return; } - s->coreisr[cpu][irq_index] |= irq_mask; - found = find_first_bit(s->sw_isr[cpu][ipnum], EXTIOI_IRQS); - set_bit(irq, s->sw_isr[cpu][ipnum]); + s->cpu[cpu].coreisr[irq_index] |= irq_mask; + found = find_first_bit(s->cpu[cpu].sw_isr[ipnum], EXTIOI_IRQS); + set_bit(irq, s->cpu[cpu].sw_isr[ipnum]); if (found < EXTIOI_IRQS) { /* other irq is handling, need not update parent irq level */ return; } } else { - s->coreisr[cpu][irq_index] &= ~irq_mask; - clear_bit(irq, s->sw_isr[cpu][ipnum]); - found = find_first_bit(s->sw_isr[cpu][ipnum], EXTIOI_IRQS); + s->cpu[cpu].coreisr[irq_index] &= ~irq_mask; + clear_bit(irq, s->cpu[cpu].sw_isr[ipnum]); + found = find_first_bit(s->cpu[cpu].sw_isr[ipnum], EXTIOI_IRQS); if (found < EXTIOI_IRQS) { /* other irq is handling, need not update parent irq level */ return; } } - qemu_set_irq(s->parent_irq[cpu][ipnum], level); + qemu_set_irq(s->cpu[cpu].parent_irq[ipnum], level); } static void extioi_setirq(void *opaque, int irq, int level) @@ -68,44 +69,46 @@ static void extioi_setirq(void *opaque, int irq, int level) extioi_update_irq(s, irq, level); } -static uint64_t extioi_readw(void *opaque, hwaddr addr, unsigned size) +static MemTxResult extioi_readw(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs) { LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); unsigned long offset = addr & 0xffff; - uint32_t index, cpu, ret = 0; + uint32_t index, cpu; switch (offset) { case EXTIOI_NODETYPE_START ... EXTIOI_NODETYPE_END - 1: index = (offset - EXTIOI_NODETYPE_START) >> 2; - ret = s->nodetype[index]; + *data = s->nodetype[index]; break; case EXTIOI_IPMAP_START ... EXTIOI_IPMAP_END - 1: index = (offset - EXTIOI_IPMAP_START) >> 2; - ret = s->ipmap[index]; + *data = s->ipmap[index]; break; case EXTIOI_ENABLE_START ... EXTIOI_ENABLE_END - 1: index = (offset - EXTIOI_ENABLE_START) >> 2; - ret = s->enable[index]; + *data = s->enable[index]; break; case EXTIOI_BOUNCE_START ... EXTIOI_BOUNCE_END - 1: index = (offset - EXTIOI_BOUNCE_START) >> 2; - ret = s->bounce[index]; + *data = s->bounce[index]; break; case EXTIOI_COREISR_START ... EXTIOI_COREISR_END - 1: - index = ((offset - EXTIOI_COREISR_START) & 0x1f) >> 2; - cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3; - ret = s->coreisr[cpu][index]; + index = (offset - EXTIOI_COREISR_START) >> 2; + /* using attrs to get current cpu index */ + cpu = attrs.requester_id; + *data = s->cpu[cpu].coreisr[index]; break; case EXTIOI_COREMAP_START ... EXTIOI_COREMAP_END - 1: index = (offset - EXTIOI_COREMAP_START) >> 2; - ret = s->coremap[index]; + *data = s->coremap[index]; break; default: break; } - trace_loongarch_extioi_readw(addr, ret); - return ret; + trace_loongarch_extioi_readw(addr, *data); + return MEMTX_OK; } static inline void extioi_enable_irq(LoongArchExtIOI *s, int index,\ @@ -127,11 +130,66 @@ static inline void extioi_enable_irq(LoongArchExtIOI *s, int index,\ } } -static void extioi_writew(void *opaque, hwaddr addr, - uint64_t val, unsigned size) +static inline void extioi_update_sw_coremap(LoongArchExtIOI *s, int irq, + uint64_t val, bool notify) +{ + int i, cpu; + + /* + * loongarch only support little endian, + * so we paresd the value with little endian. + */ + val = cpu_to_le64(val); + + for (i = 0; i < 4; i++) { + cpu = val & 0xff; + cpu = ctz32(cpu); + cpu = (cpu >= 4) ? 0 : cpu; + val = val >> 8; + + if (s->sw_coremap[irq + i] == cpu) { + continue; + } + + if (notify && test_bit(irq + i, (unsigned long *)s->isr)) { + /* + * lower irq at old cpu and raise irq at new cpu + */ + extioi_update_irq(s, irq + i, 0); + s->sw_coremap[irq + i] = cpu; + extioi_update_irq(s, irq + i, 1); + } else { + s->sw_coremap[irq + i] = cpu; + } + } +} + +static inline void extioi_update_sw_ipmap(LoongArchExtIOI *s, int index, + uint64_t val) +{ + int i; + uint8_t ipnum; + + /* + * loongarch only support little endian, + * so we paresd the value with little endian. + */ + val = cpu_to_le64(val); + for (i = 0; i < 4; i++) { + ipnum = val & 0xff; + ipnum = ctz32(ipnum); + ipnum = (ipnum >= 4) ? 0 : ipnum; + s->sw_ipmap[index * 4 + i] = ipnum; + val = val >> 8; + } +} + +static MemTxResult extioi_writew(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) { LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); - int i, cpu, index, old_data, irq; + int cpu, index, old_data, irq; uint32_t offset; trace_loongarch_extioi_writew(addr, val); @@ -149,20 +207,7 @@ static void extioi_writew(void *opaque, hwaddr addr, */ index = (offset - EXTIOI_IPMAP_START) >> 2; s->ipmap[index] = val; - /* - * loongarch only support little endian, - * so we paresd the value with little endian. - */ - val = cpu_to_le64(val); - for (i = 0; i < 4; i++) { - uint8_t ipnum; - ipnum = val & 0xff; - ipnum = ctz32(ipnum); - ipnum = (ipnum >= 4) ? 0 : ipnum; - s->sw_ipmap[index * 4 + i] = ipnum; - val = val >> 8; - } - + extioi_update_sw_ipmap(s, index, val); break; case EXTIOI_ENABLE_START ... EXTIOI_ENABLE_END - 1: index = (offset - EXTIOI_ENABLE_START) >> 2; @@ -183,11 +228,12 @@ static void extioi_writew(void *opaque, hwaddr addr, s->bounce[index] = val; break; case EXTIOI_COREISR_START ... EXTIOI_COREISR_END - 1: - index = ((offset - EXTIOI_COREISR_START) & 0x1f) >> 2; - cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3; - old_data = s->coreisr[cpu][index]; - s->coreisr[cpu][index] = old_data & ~val; - /* write 1 to clear interrrupt */ + index = (offset - EXTIOI_COREISR_START) >> 2; + /* using attrs to get current cpu index */ + cpu = attrs.requester_id; + old_data = s->cpu[cpu].coreisr[index]; + s->cpu[cpu].coreisr[index] = old_data & ~val; + /* write 1 to clear interrupt */ old_data &= val; irq = ctz32(old_data); while (irq != 32) { @@ -200,42 +246,18 @@ static void extioi_writew(void *opaque, hwaddr addr, irq = offset - EXTIOI_COREMAP_START; index = irq / 4; s->coremap[index] = val; - /* - * loongarch only support little endian, - * so we paresd the value with little endian. - */ - val = cpu_to_le64(val); - for (i = 0; i < 4; i++) { - cpu = val & 0xff; - cpu = ctz32(cpu); - cpu = (cpu >= 4) ? 0 : cpu; - val = val >> 8; - - if (s->sw_coremap[irq + i] == cpu) { - continue; - } - - if (test_bit(irq, (unsigned long *)s->isr)) { - /* - * lower irq at old cpu and raise irq at new cpu - */ - extioi_update_irq(s, irq + i, 0); - s->sw_coremap[irq + i] = cpu; - extioi_update_irq(s, irq + i, 1); - } else { - s->sw_coremap[irq + i] = cpu; - } - } + extioi_update_sw_coremap(s, irq, val, true); break; default: break; } + return MEMTX_OK; } static const MemoryRegionOps extioi_ops = { - .read = extioi_readw, - .write = extioi_writew, + .read_with_attrs = extioi_readw, + .write_with_attrs = extioi_writew, .impl.min_access_size = 4, .impl.max_access_size = 4, .valid.min_access_size = 4, @@ -243,65 +265,112 @@ static const MemoryRegionOps extioi_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static const VMStateDescription vmstate_loongarch_extioi = { - .name = TYPE_LOONGARCH_EXTIOI, +static void loongarch_extioi_realize(DeviceState *dev, Error **errp) +{ + LoongArchExtIOI *s = LOONGARCH_EXTIOI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + int i, pin; + + if (s->num_cpu == 0) { + error_setg(errp, "num-cpu must be at least 1"); + return; + } + + for (i = 0; i < EXTIOI_IRQS; i++) { + sysbus_init_irq(sbd, &s->irq[i]); + } + + qdev_init_gpio_in(dev, extioi_setirq, EXTIOI_IRQS); + memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops, + s, "extioi_system_mem", 0x900); + sysbus_init_mmio(sbd, &s->extioi_system_mem); + s->cpu = g_new0(ExtIOICore, s->num_cpu); + if (s->cpu == NULL) { + error_setg(errp, "Memory allocation for ExtIOICore faile"); + return; + } + + for (i = 0; i < s->num_cpu; i++) { + for (pin = 0; pin < LS3A_INTC_IP; pin++) { + qdev_init_gpio_out(dev, &s->cpu[i].parent_irq[pin], 1); + } + } +} + +static void loongarch_extioi_finalize(Object *obj) +{ + LoongArchExtIOI *s = LOONGARCH_EXTIOI(obj); + + g_free(s->cpu); +} + +static int vmstate_extioi_post_load(void *opaque, int version_id) +{ + LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); + int i, start_irq; + + for (i = 0; i < (EXTIOI_IRQS / 4); i++) { + start_irq = i * 4; + extioi_update_sw_coremap(s, start_irq, s->coremap[i], false); + } + + for (i = 0; i < (EXTIOI_IRQS_IPMAP_SIZE / 4); i++) { + extioi_update_sw_ipmap(s, i, s->ipmap[i]); + } + + return 0; +} + +static const VMStateDescription vmstate_extioi_core = { + .name = "extioi-core", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(coreisr, ExtIOICore, EXTIOI_IRQS_GROUP_COUNT), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_loongarch_extioi = { + .name = TYPE_LOONGARCH_EXTIOI, + .version_id = 2, + .minimum_version_id = 2, + .post_load = vmstate_extioi_post_load, + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOI, EXTIOI_IRQS_GROUP_COUNT), - VMSTATE_UINT32_2DARRAY(coreisr, LoongArchExtIOI, LOONGARCH_MAX_VCPUS, - EXTIOI_IRQS_GROUP_COUNT), VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOI, EXTIOI_IRQS_NODETYPE_COUNT / 2), VMSTATE_UINT32_ARRAY(enable, LoongArchExtIOI, EXTIOI_IRQS / 32), VMSTATE_UINT32_ARRAY(isr, LoongArchExtIOI, EXTIOI_IRQS / 32), VMSTATE_UINT32_ARRAY(ipmap, LoongArchExtIOI, EXTIOI_IRQS_IPMAP_SIZE / 4), VMSTATE_UINT32_ARRAY(coremap, LoongArchExtIOI, EXTIOI_IRQS / 4), - VMSTATE_UINT8_ARRAY(sw_ipmap, LoongArchExtIOI, EXTIOI_IRQS_IPMAP_SIZE), - VMSTATE_UINT8_ARRAY(sw_coremap, LoongArchExtIOI, EXTIOI_IRQS), + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOI, num_cpu, + vmstate_extioi_core, ExtIOICore), VMSTATE_END_OF_LIST() } }; -static void loongarch_extioi_instance_init(Object *obj) -{ - SysBusDevice *dev = SYS_BUS_DEVICE(obj); - LoongArchExtIOI *s = LOONGARCH_EXTIOI(obj); - int i, cpu, pin; - - for (i = 0; i < EXTIOI_IRQS; i++) { - sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]); - } - - qdev_init_gpio_in(DEVICE(obj), extioi_setirq, EXTIOI_IRQS); - - for (cpu = 0; cpu < LOONGARCH_MAX_VCPUS; cpu++) { - memory_region_init_io(&s->extioi_iocsr_mem[cpu], OBJECT(s), &extioi_ops, - s, "extioi_iocsr", 0x900); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->extioi_iocsr_mem[cpu]); - for (pin = 0; pin < LS3A_INTC_IP; pin++) { - qdev_init_gpio_out(DEVICE(obj), &s->parent_irq[cpu][pin], 1); - } - } - memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops, - s, "extioi_system_mem", 0x900); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->extioi_system_mem); -} +static Property extioi_properties[] = { + DEFINE_PROP_UINT32("num-cpu", LoongArchExtIOI, num_cpu, 1), + DEFINE_PROP_END_OF_LIST(), +}; static void loongarch_extioi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = loongarch_extioi_realize; + device_class_set_props(dc, extioi_properties); dc->vmsd = &vmstate_loongarch_extioi; } static const TypeInfo loongarch_extioi_info = { .name = TYPE_LOONGARCH_EXTIOI, .parent = TYPE_SYS_BUS_DEVICE, - .instance_init = loongarch_extioi_instance_init, .instance_size = sizeof(struct LoongArchExtIOI), .class_init = loongarch_extioi_class_init, + .instance_finalize = loongarch_extioi_finalize, }; static void loongarch_extioi_register_types(void) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 66bee93675..a184112b09 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -9,6 +9,7 @@ #include "hw/sysbus.h" #include "hw/intc/loongarch_ipi.h" #include "hw/irq.h" +#include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/log.h" #include "exec/address-spaces.h" @@ -17,12 +18,16 @@ #include "target/loongarch/internals.h" #include "trace.h" -static uint64_t loongarch_ipi_readl(void *opaque, hwaddr addr, unsigned size) +static MemTxResult loongarch_ipi_readl(void *opaque, hwaddr addr, + uint64_t *data, + unsigned size, MemTxAttrs attrs) { - IPICore *s = opaque; + IPICore *s; + LoongArchIPI *ipi = opaque; uint64_t ret = 0; int index = 0; + s = &ipi->cpu[attrs.requester_id]; addr &= 0xff; switch (addr) { case CORE_STATUS_OFF: @@ -47,81 +52,128 @@ static uint64_t loongarch_ipi_readl(void *opaque, hwaddr addr, unsigned size) } trace_loongarch_ipi_read(size, (uint64_t)addr, ret); - return ret; + *data = ret; + return MEMTX_OK; } -static int get_ipi_data(target_ulong val) +static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr, + MemTxAttrs attrs) { - int i, mask, data; + int i, mask = 0, data = 0; - data = val >> 32; - mask = (val >> 27) & 0xf; - - for (i = 0; i < 4; i++) { - if ((mask >> i) & 1) { - data &= ~(0xff << (i * 8)); + /* + * bit 27-30 is mask for byte writing, + * if the mask is 0, we need not to do anything. + */ + if ((val >> 27) & 0xf) { + data = address_space_ldl(env->address_space_iocsr, addr, + attrs, NULL); + for (i = 0; i < 4; i++) { + /* get mask for byte writing */ + if (val & (0x1 << (27 + i))) { + mask |= 0xff << (i * 8); + } } } - return data; + + data &= mask; + data |= (val >> 32) & ~mask; + address_space_stl(env->address_space_iocsr, addr, + data, attrs, NULL); } -static void ipi_send(uint64_t val) +static int archid_cmp(const void *a, const void *b) { - int cpuid, data; - CPULoongArchState *env; - - cpuid = (val >> 16) & 0x3ff; - /* IPI status vector */ - data = 1 << (val & 0x1f); - qemu_mutex_lock_iothread(); - CPUState *cs = qemu_get_cpu(cpuid); - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - env = &cpu->env; - loongarch_cpu_set_irq(cpu, IRQ_IPI, 1); - qemu_mutex_unlock_iothread(); - address_space_stl(&env->address_space_iocsr, 0x1008, - data, MEMTXATTRS_UNSPECIFIED, NULL); + CPUArchId *archid_a = (CPUArchId *)a; + CPUArchId *archid_b = (CPUArchId *)b; + return archid_a->arch_id - archid_b->arch_id; } -static void mail_send(uint64_t val) +static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id) { - int cpuid, data; + CPUArchId apic_id, *found_cpu; + + apic_id.arch_id = id; + found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus, + ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus), + archid_cmp); + + return found_cpu; +} + +static CPUState *ipi_getcpu(int arch_id) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + CPUArchId *archid; + + archid = find_cpu_by_archid(machine, arch_id); + if (archid) { + return CPU(archid->cpu); + } + + return NULL; +} + +static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs) +{ + uint32_t cpuid; hwaddr addr; - CPULoongArchState *env; + CPUState *cs; - cpuid = (val >> 16) & 0x3ff; - addr = 0x1020 + (val & 0x1c); - CPUState *cs = qemu_get_cpu(cpuid); - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - env = &cpu->env; - data = get_ipi_data(val); - address_space_stl(&env->address_space_iocsr, addr, - data, MEMTXATTRS_UNSPECIFIED, NULL); + cpuid = extract32(val, 16, 10); + if (cpuid >= LOONGARCH_MAX_CPUS) { + trace_loongarch_ipi_unsupported_cpuid("IOCSR_MAIL_SEND", cpuid); + return MEMTX_DECODE_ERROR; + } + + cs = ipi_getcpu(cpuid); + if (cs == NULL) { + return MEMTX_DECODE_ERROR; + } + + /* override requester_id */ + addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c); + attrs.requester_id = cs->cpu_index; + send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); + return MEMTX_OK; } -static void any_send(uint64_t val) +static MemTxResult any_send(uint64_t val, MemTxAttrs attrs) { - int cpuid, data; + uint32_t cpuid; hwaddr addr; - CPULoongArchState *env; + CPUState *cs; - cpuid = (val >> 16) & 0x3ff; + cpuid = extract32(val, 16, 10); + if (cpuid >= LOONGARCH_MAX_CPUS) { + trace_loongarch_ipi_unsupported_cpuid("IOCSR_ANY_SEND", cpuid); + return MEMTX_DECODE_ERROR; + } + + cs = ipi_getcpu(cpuid); + if (cs == NULL) { + return MEMTX_DECODE_ERROR; + } + + /* override requester_id */ addr = val & 0xffff; - CPUState *cs = qemu_get_cpu(cpuid); - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - env = &cpu->env; - data = get_ipi_data(val); - address_space_stl(&env->address_space_iocsr, addr, - data, MEMTXATTRS_UNSPECIFIED, NULL); + attrs.requester_id = cs->cpu_index; + send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); + return MEMTX_OK; } -static void loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, - unsigned size) +static MemTxResult loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) { - IPICore *s = opaque; + LoongArchIPI *ipi = opaque; + IPICore *s; int index = 0; + uint32_t cpuid; + uint8_t vector; + CPUState *cs; + s = &ipi->cpu[attrs.requester_id]; addr &= 0xff; trace_loongarch_ipi_write(size, (uint64_t)addr, val); switch (addr) { @@ -148,23 +200,34 @@ static void loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, s->buf[index] = val; break; case IOCSR_IPI_SEND: - ipi_send(val); - break; - case IOCSR_MAIL_SEND: - mail_send(val); - break; - case IOCSR_ANY_SEND: - any_send(val); + cpuid = extract32(val, 16, 10); + if (cpuid >= LOONGARCH_MAX_CPUS) { + trace_loongarch_ipi_unsupported_cpuid("IOCSR_IPI_SEND", cpuid); + return MEMTX_DECODE_ERROR; + } + + /* IPI status vector */ + vector = extract8(val, 0, 5); + cs = ipi_getcpu(cpuid); + if (cs == NULL) { + return MEMTX_DECODE_ERROR; + } + + /* override requester_id */ + attrs.requester_id = cs->cpu_index; + loongarch_ipi_writel(ipi, CORE_SET_OFF, BIT(vector), 4, attrs); break; default: qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr); break; } + + return MEMTX_OK; } static const MemoryRegionOps loongarch_ipi_ops = { - .read = loongarch_ipi_readl, - .write = loongarch_ipi_writel, + .read_with_attrs = loongarch_ipi_readl, + .write_with_attrs = loongarch_ipi_writel, .impl.min_access_size = 4, .impl.max_access_size = 4, .valid.min_access_size = 4, @@ -172,66 +235,123 @@ static const MemoryRegionOps loongarch_ipi_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void loongarch_ipi_init(Object *obj) +/* mail send and any send only support writeq */ +static MemTxResult loongarch_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) { - int cpu; - LoongArchMachineState *lams; - LoongArchIPI *s = LOONGARCH_IPI(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - Object *machine = qdev_get_machine(); - ObjectClass *mc = object_get_class(machine); - /* 'lams' should be initialized */ - if (!strcmp(MACHINE_CLASS(mc)->name, "none")) { + MemTxResult ret = MEMTX_OK; + + addr &= 0xfff; + switch (addr) { + case MAIL_SEND_OFFSET: + ret = mail_send(val, attrs); + break; + case ANY_SEND_OFFSET: + ret = any_send(val, attrs); + break; + default: + break; + } + + return ret; +} + +static const MemoryRegionOps loongarch_ipi64_ops = { + .write_with_attrs = loongarch_ipi_writeq, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void loongarch_ipi_realize(DeviceState *dev, Error **errp) +{ + LoongArchIPI *s = LOONGARCH_IPI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + int i; + + if (s->num_cpu == 0) { + error_setg(errp, "num-cpu must be at least 1"); return; } - lams = LOONGARCH_MACHINE(machine); - for (cpu = 0; cpu < MAX_IPI_CORE_NUM; cpu++) { - memory_region_init_io(&s->ipi_iocsr_mem[cpu], obj, &loongarch_ipi_ops, - &lams->ipi_core[cpu], "loongarch_ipi_iocsr", 0x100); - sysbus_init_mmio(sbd, &s->ipi_iocsr_mem[cpu]); - qdev_init_gpio_out(DEVICE(obj), &lams->ipi_core[cpu].irq, 1); + + memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), &loongarch_ipi_ops, + s, "loongarch_ipi_iocsr", 0x48); + + /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */ + s->ipi_iocsr_mem.disable_reentrancy_guard = true; + + sysbus_init_mmio(sbd, &s->ipi_iocsr_mem); + + memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev), + &loongarch_ipi64_ops, + s, "loongarch_ipi64_iocsr", 0x118); + sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); + + s->cpu = g_new0(IPICore, s->num_cpu); + if (s->cpu == NULL) { + error_setg(errp, "Memory allocation for ExtIOICore faile"); + return; + } + + for (i = 0; i < s->num_cpu; i++) { + qdev_init_gpio_out(dev, &s->cpu[i].irq, 1); } } static const VMStateDescription vmstate_ipi_core = { .name = "ipi-single", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { VMSTATE_UINT32(status, IPICore), VMSTATE_UINT32(en, IPICore), VMSTATE_UINT32(set, IPICore), VMSTATE_UINT32(clear, IPICore), - VMSTATE_UINT32_ARRAY(buf, IPICore, MAX_IPI_MBX_NUM * 2), + VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2), VMSTATE_END_OF_LIST() } }; static const VMStateDescription vmstate_loongarch_ipi = { .name = TYPE_LOONGARCH_IPI, - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(ipi_core, LoongArchMachineState, - MAX_IPI_CORE_NUM, 0, - vmstate_ipi_core, IPICore), + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchIPI, num_cpu, + vmstate_ipi_core, IPICore), VMSTATE_END_OF_LIST() } }; +static Property ipi_properties[] = { + DEFINE_PROP_UINT32("num-cpu", LoongArchIPI, num_cpu, 1), + DEFINE_PROP_END_OF_LIST(), +}; + static void loongarch_ipi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = loongarch_ipi_realize; + device_class_set_props(dc, ipi_properties); dc->vmsd = &vmstate_loongarch_ipi; } +static void loongarch_ipi_finalize(Object *obj) +{ + LoongArchIPI *s = LOONGARCH_IPI(obj); + + g_free(s->cpu); +} + static const TypeInfo loongarch_ipi_info = { .name = TYPE_LOONGARCH_IPI, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LoongArchIPI), - .instance_init = loongarch_ipi_init, .class_init = loongarch_ipi_class_init, + .instance_finalize = loongarch_ipi_finalize, }; static void loongarch_ipi_register_types(void) diff --git a/hw/intc/loongarch_pch_msi.c b/hw/intc/loongarch_pch_msi.c index 74bcdbdb48..ecf3ed0267 100644 --- a/hw/intc/loongarch_pch_msi.c +++ b/hw/intc/loongarch_pch_msi.c @@ -23,11 +23,16 @@ static uint64_t loongarch_msi_mem_read(void *opaque, hwaddr addr, unsigned size) static void loongarch_msi_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - LoongArchPCHMSI *s = LOONGARCH_PCH_MSI(opaque); - int irq_num = val & 0xff; + LoongArchPCHMSI *s = (LoongArchPCHMSI *)opaque; + int irq_num; + /* + * vector number is irq number from upper extioi intc + * need subtract irq base to get msi vector offset + */ + irq_num = (val & 0xff) - s->irq_base; trace_loongarch_msi_set_irq(irq_num); - assert(irq_num < PCH_MSI_IRQ_NUM); + assert(irq_num < s->irq_num); qemu_set_irq(s->pch_msi_irq[irq_num], 1); } @@ -44,6 +49,28 @@ static void pch_msi_irq_handler(void *opaque, int irq, int level) qemu_set_irq(s->pch_msi_irq[irq], level); } +static void loongarch_pch_msi_realize(DeviceState *dev, Error **errp) +{ + LoongArchPCHMSI *s = LOONGARCH_PCH_MSI(dev); + + if (!s->irq_num || s->irq_num > PCH_MSI_IRQ_NUM) { + error_setg(errp, "Invalid 'msi_irq_num'"); + return; + } + + s->pch_msi_irq = g_new(qemu_irq, s->irq_num); + + qdev_init_gpio_out(dev, s->pch_msi_irq, s->irq_num); + qdev_init_gpio_in(dev, pch_msi_irq_handler, s->irq_num); +} + +static void loongarch_pch_msi_unrealize(DeviceState *dev) +{ + LoongArchPCHMSI *s = LOONGARCH_PCH_MSI(dev); + + g_free(s->pch_msi_irq); +} + static void loongarch_pch_msi_init(Object *obj) { LoongArchPCHMSI *s = LOONGARCH_PCH_MSI(obj); @@ -54,8 +81,21 @@ static void loongarch_pch_msi_init(Object *obj) sysbus_init_mmio(sbd, &s->msi_mmio); msi_nonbroken = true; - qdev_init_gpio_out(DEVICE(obj), s->pch_msi_irq, PCH_MSI_IRQ_NUM); - qdev_init_gpio_in(DEVICE(obj), pch_msi_irq_handler, PCH_MSI_IRQ_NUM); +} + +static Property loongarch_msi_properties[] = { + DEFINE_PROP_UINT32("msi_irq_base", LoongArchPCHMSI, irq_base, 0), + DEFINE_PROP_UINT32("msi_irq_num", LoongArchPCHMSI, irq_num, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void loongarch_pch_msi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = loongarch_pch_msi_realize; + dc->unrealize = loongarch_pch_msi_unrealize; + device_class_set_props(dc, loongarch_msi_properties); } static const TypeInfo loongarch_pch_msi_info = { @@ -63,6 +103,7 @@ static const TypeInfo loongarch_pch_msi_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LoongArchPCHMSI), .instance_init = loongarch_pch_msi_init, + .class_init = loongarch_pch_msi_class_init, }; static void loongarch_pch_msi_register_types(void) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 3c9814a3b4..2d5e65abff 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -6,30 +6,38 @@ */ #include "qemu/osdep.h" +#include "qemu/bitops.h" #include "hw/sysbus.h" #include "hw/loongarch/virt.h" +#include "hw/pci-host/ls7a.h" #include "hw/irq.h" #include "hw/intc/loongarch_pch_pic.h" +#include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "trace.h" +#include "qapi/error.h" static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level) { - unsigned long val; + uint64_t val; int irq; if (level) { val = mask & s->intirr & ~s->int_mask; if (val) { - irq = find_first_bit(&val, 64); - s->intisr |= 0x1ULL << irq; + irq = ctz64(val); + s->intisr |= MAKE_64BIT_MASK(irq, 1); qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1); } } else { - val = mask & s->intisr; + /* + * intirr means requested pending irq + * do not clear pending irq for edge-triggered on lowering edge + */ + val = mask & s->intisr & ~s->intirr; if (val) { - irq = find_first_bit(&val, 64); - s->intisr &= ~(0x1ULL << irq); + irq = ctz64(val); + s->intisr &= ~MAKE_64BIT_MASK(irq, 1); qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0); } } @@ -40,13 +48,14 @@ static void pch_pic_irq_handler(void *opaque, int irq, int level) LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); uint64_t mask = 1ULL << irq; - assert(irq < PCH_PIC_IRQ_NUM); + assert(irq < s->irq_num); trace_loongarch_pch_pic_irq_handler(irq, level); if (s->intedge & mask) { /* Edge triggered */ if (level) { if ((s->last_intirr & mask) == 0) { + /* marked pending on a rising edge */ s->intirr |= mask; } s->last_intirr |= mask; @@ -78,7 +87,12 @@ static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr, val = PCH_PIC_INT_ID_VAL; break; case PCH_PIC_INT_ID_HI: - val = PCH_PIC_INT_ID_NUM; + /* + * With 7A1000 manual + * bit 0-15 pch irqchip version + * bit 16-31 irq number supported with pch irqchip + */ + val = deposit32(PCH_PIC_INT_ID_VER, 16, 16, s->irq_num - 1); break; case PCH_PIC_INT_MASK_LO: val = (uint32_t)s->int_mask; @@ -365,6 +379,19 @@ static void loongarch_pch_pic_reset(DeviceState *d) s->int_polarity = 0x0; } +static void loongarch_pch_pic_realize(DeviceState *dev, Error **errp) +{ + LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(dev); + + if (!s->irq_num || s->irq_num > VIRT_PCH_PIC_IRQ_NUM) { + error_setg(errp, "Invalid 'pic_irq_num'"); + return; + } + + qdev_init_gpio_out(dev, s->parent_irq, s->irq_num); + qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num); +} + static void loongarch_pch_pic_init(Object *obj) { LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(obj); @@ -382,15 +409,18 @@ static void loongarch_pch_pic_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem8); sysbus_init_mmio(sbd, &s->iomem32_high); - qdev_init_gpio_out(DEVICE(obj), s->parent_irq, PCH_PIC_IRQ_NUM); - qdev_init_gpio_in(DEVICE(obj), pch_pic_irq_handler, PCH_PIC_IRQ_NUM); } +static Property loongarch_pch_pic_properties[] = { + DEFINE_PROP_UINT32("pch_pic_irq_num", LoongArchPCHPIC, irq_num, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static const VMStateDescription vmstate_loongarch_pch_pic = { .name = TYPE_LOONGARCH_PCH_PIC, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(int_mask, LoongArchPCHPIC), VMSTATE_UINT64(htmsi_en, LoongArchPCHPIC), VMSTATE_UINT64(intedge, LoongArchPCHPIC), @@ -411,8 +441,10 @@ static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = loongarch_pch_pic_realize; dc->reset = loongarch_pch_pic_reset; dc->vmsd = &vmstate_loongarch_pch_pic; + device_class_set_props(dc, loongarch_pch_pic_properties); } static const TypeInfo loongarch_pch_pic_info = { diff --git a/hw/intc/loongson_liointc.c b/hw/intc/loongson_liointc.c index cc11b544cb..c10fb97a06 100644 --- a/hw/intc/loongson_liointc.c +++ b/hw/intc/loongson_liointc.c @@ -1,5 +1,5 @@ /* - * QEMU Loongson Local I/O interrupt controler. + * QEMU Loongson Local I/O interrupt controller. * * Copyright (c) 2020 Huacai Chen * Copyright (c) 2020 Jiaxun Yang diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c index 0c515e4ecb..4b11fb9f72 100644 --- a/hw/intc/m68k_irqc.c +++ b/hw/intc/m68k_irqc.c @@ -11,6 +11,7 @@ #include "cpu.h" #include "migration/vmstate.h" #include "monitor/monitor.h" +#include "hw/qdev-properties.h" #include "hw/nmi.h" #include "hw/intc/intc.h" #include "hw/intc/m68k_irqc.h" @@ -35,7 +36,7 @@ static void m68k_irqc_print_info(InterruptStatsProvider *obj, Monitor *mon) static void m68k_set_irq(void *opaque, int irq, int level) { M68KIRQCState *s = opaque; - M68kCPU *cpu = M68K_CPU(first_cpu); + M68kCPU *cpu = M68K_CPU(s->cpu); int i; if (level) { @@ -79,18 +80,25 @@ static const VMStateDescription vmstate_m68k_irqc = { .name = "m68k-irqc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(ipr, M68KIRQCState), VMSTATE_END_OF_LIST() } }; +static Property m68k_irqc_properties[] = { + DEFINE_PROP_LINK("m68k-cpu", M68KIRQCState, cpu, + TYPE_M68K_CPU, ArchCPU *), + DEFINE_PROP_END_OF_LIST(), +}; + static void m68k_irqc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc); + device_class_set_props(dc, m68k_irqc_properties); nc->nmi_monitor_handler = m68k_nmi; dc->reset = m68k_irqc_reset; dc->vmsd = &vmstate_m68k_irqc; diff --git a/hw/intc/meson.build b/hw/intc/meson.build index bcbf22ff51..ed355941d1 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -1,50 +1,57 @@ -softmmu_ss.add(files('intc.c')) -softmmu_ss.add(when: 'CONFIG_ARM_GIC', if_true: files( +system_ss.add(files('intc.c')) +system_ss.add(when: 'CONFIG_ARM_GIC', if_true: files( 'arm_gic.c', 'arm_gic_common.c', 'arm_gicv2m.c', 'arm_gicv3_common.c', 'arm_gicv3_its_common.c', )) -softmmu_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files( +system_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files( 'arm_gicv3.c', 'arm_gicv3_dist.c', 'arm_gicv3_its.c', 'arm_gicv3_redist.c', )) -softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_pic.c')) -softmmu_ss.add(when: 'CONFIG_HEATHROW_PIC', if_true: files('heathrow_pic.c')) -softmmu_ss.add(when: 'CONFIG_I8259', if_true: files('i8259_common.c', 'i8259.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_avic.c', 'imx_gpcv2.c')) -softmmu_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c')) -softmmu_ss.add(when: 'CONFIG_OPENPIC', if_true: files('openpic.c')) -softmmu_ss.add(when: 'CONFIG_PL190', if_true: files('pl190.c')) -softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview_gic.c')) -softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_intctl.c')) -softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_intc.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-ipi.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP_PMU', if_true: files('xlnx-pmu-iomod-intc.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_A10_PIC', if_true: files('allwinner-a10-pic.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_vic.c')) +system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_pic.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_gic.c', 'exynos4210_combiner.c')) +system_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c')) +system_ss.add(when: 'CONFIG_HEATHROW_PIC', if_true: files('heathrow_pic.c')) +system_ss.add(when: 'CONFIG_I8259', if_true: files('i8259_common.c', 'i8259.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_avic.c', 'imx_gpcv2.c')) +system_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c')) +system_ss.add(when: 'CONFIG_OPENPIC', if_true: files('openpic.c')) +system_ss.add(when: 'CONFIG_PL190', if_true: files('pl190.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_ic.c', 'bcm2836_control.c')) +system_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview_gic.c')) +system_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_intctl.c')) +system_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_intc.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-ipi.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP_PMU', if_true: files('xlnx-pmu-iomod-intc.c')) + +if config_all_devices.has_key('CONFIG_APIC') or \ + config_all_devices.has_key('CONFIG_I8259') or \ + config_all_devices.has_key('CONFIG_MC146818RTC') + system_ss.add(files('kvm_irqcount.c')) +endif -specific_ss.add(when: 'CONFIG_ALLWINNER_A10_PIC', if_true: files('allwinner-a10-pic.c')) specific_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c', 'apic_common.c')) specific_ss.add(when: 'CONFIG_ARM_GIC', if_true: files('arm_gicv3_cpuif_common.c')) specific_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files('arm_gicv3_cpuif.c')) specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gic_kvm.c')) specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'TARGET_AARCH64'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c')) specific_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c')) -specific_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_vic.c')) -specific_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_gic.c', 'exynos4210_combiner.c')) specific_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_irqmp.c')) specific_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic.c')) specific_ss.add(when: 'CONFIG_LOONGSON_LIOINTC', if_true: files('loongson_liointc.c')) specific_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gic.c')) -specific_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c')) specific_ss.add(when: 'CONFIG_OMPIC', if_true: files('ompic.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_OPENPIC'], if_true: files('openpic_kvm.c')) specific_ss.add(when: 'CONFIG_POWERNV', if_true: files('xics_pnv.c', 'pnv_xive.c', 'pnv_xive2.c')) specific_ss.add(when: 'CONFIG_PPC_UIC', if_true: files('ppc-uic.c')) -specific_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_ic.c', 'bcm2836_control.c')) specific_ss.add(when: 'CONFIG_RX_ICU', if_true: files('rx_icu.c')) specific_ss.add(when: 'CONFIG_S390_FLIC', if_true: files('s390_flic.c')) specific_ss.add(when: 'CONFIG_S390_FLIC_KVM', if_true: files('s390_flic_kvm.c')) @@ -60,7 +67,6 @@ specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('xics_spapr.c', 'spapr_xi specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], if_true: files('spapr_xive_kvm.c')) -specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c')) specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) specific_ss.add(when: 'CONFIG_NIOS2_VIC', if_true: files('nios2_vic.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) diff --git a/hw/intc/mips_gic.c b/hw/intc/mips_gic.c index bda4549925..77ba7348a3 100644 --- a/hw/intc/mips_gic.c +++ b/hw/intc/mips_gic.c @@ -423,7 +423,7 @@ static void mips_gic_realize(DeviceState *dev, Error **errp) /* Register the env for all VPs with the GIC */ for (i = 0; i < s->num_vps; i++) { if (cs != NULL) { - s->vps[i].env = cs->env_ptr; + s->vps[i].env = cpu_env(cs); cs = CPU_NEXT(cs); } else { error_setg(errp, @@ -439,8 +439,8 @@ static void mips_gic_realize(DeviceState *dev, Error **errp) } static Property mips_gic_properties[] = { - DEFINE_PROP_INT32("num-vp", MIPSGICState, num_vps, 1), - DEFINE_PROP_INT32("num-irq", MIPSGICState, num_irq, 256), + DEFINE_PROP_UINT32("num-vp", MIPSGICState, num_vps, 1), + DEFINE_PROP_UINT32("num-irq", MIPSGICState, num_irq, 256), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/intc/nios2_vic.c b/hw/intc/nios2_vic.c index cf63212a88..7e2d9d6327 100644 --- a/hw/intc/nios2_vic.c +++ b/hw/intc/nios2_vic.c @@ -275,7 +275,7 @@ static const VMStateDescription nios2_vic_vmstate = { .name = "nios2-vic", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]){ + .fields = (const VMStateField[]){ VMSTATE_UINT32_ARRAY(int_config, Nios2VIC, 32), VMSTATE_UINT32(vic_config, Nios2VIC), VMSTATE_UINT32(int_raw_status, Nios2VIC), diff --git a/hw/intc/omap_intc.c b/hw/intc/omap_intc.c index d7183d035e..435c47600f 100644 --- a/hw/intc/omap_intc.c +++ b/hw/intc/omap_intc.c @@ -38,7 +38,7 @@ struct omap_intr_handler_bank_s { unsigned char priority[32]; }; -struct omap_intr_handler_s { +struct OMAPIntcState { SysBusDevice parent_obj; qemu_irq *pins; @@ -60,7 +60,7 @@ struct omap_intr_handler_s { struct omap_intr_handler_bank_s bank[3]; }; -static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq) +static void omap_inth_sir_update(OMAPIntcState *s, int is_fiq) { int i, j, sir_intr, p_intr, p; uint32_t level; @@ -68,7 +68,7 @@ static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq) p_intr = 255; /* Find the interrupt line with the highest dynamic priority. - * Note: 0 denotes the hightest priority. + * Note: 0 denotes the highest priority. * If all interrupts have the same priority, the default order is IRQ_N, * IRQ_N-1,...,IRQ_0. */ for (j = 0; j < s->nbanks; ++j) { @@ -88,7 +88,7 @@ static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq) s->sir_intr[is_fiq] = sir_intr; } -static inline void omap_inth_update(struct omap_intr_handler_s *s, int is_fiq) +static inline void omap_inth_update(OMAPIntcState *s, int is_fiq) { int i; uint32_t has_intr = 0; @@ -109,7 +109,7 @@ static inline void omap_inth_update(struct omap_intr_handler_s *s, int is_fiq) static void omap_set_intr(void *opaque, int irq, int req) { - struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque; + OMAPIntcState *ih = opaque; uint32_t rise; struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; @@ -136,7 +136,7 @@ static void omap_set_intr(void *opaque, int irq, int req) /* Simplified version with no edge detection */ static void omap_set_intr_noedge(void *opaque, int irq, int req) { - struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque; + OMAPIntcState *ih = opaque; uint32_t rise; struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; @@ -156,7 +156,7 @@ static void omap_set_intr_noedge(void *opaque, int irq, int req) static uint64_t omap_inth_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; + OMAPIntcState *s = opaque; int i, offset = addr; int bank_no = offset >> 8; int line_no; @@ -234,7 +234,7 @@ static uint64_t omap_inth_read(void *opaque, hwaddr addr, static void omap_inth_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; + OMAPIntcState *s = opaque; int i, offset = addr; int bank_no = offset >> 8; struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; @@ -336,7 +336,7 @@ static const MemoryRegionOps omap_inth_mem_ops = { static void omap_inth_reset(DeviceState *dev) { - struct omap_intr_handler_s *s = OMAP_INTC(dev); + OMAPIntcState *s = OMAP_INTC(dev); int i; for (i = 0; i < s->nbanks; ++i){ @@ -366,7 +366,7 @@ static void omap_inth_reset(DeviceState *dev) static void omap_intc_init(Object *obj) { DeviceState *dev = DEVICE(obj); - struct omap_intr_handler_s *s = OMAP_INTC(obj); + OMAPIntcState *s = OMAP_INTC(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); s->nbanks = 1; @@ -380,25 +380,25 @@ static void omap_intc_init(Object *obj) static void omap_intc_realize(DeviceState *dev, Error **errp) { - struct omap_intr_handler_s *s = OMAP_INTC(dev); + OMAPIntcState *s = OMAP_INTC(dev); if (!s->iclk) { error_setg(errp, "omap-intc: clk not connected"); } } -void omap_intc_set_iclk(omap_intr_handler *intc, omap_clk clk) +void omap_intc_set_iclk(OMAPIntcState *intc, omap_clk clk) { intc->iclk = clk; } -void omap_intc_set_fclk(omap_intr_handler *intc, omap_clk clk) +void omap_intc_set_fclk(OMAPIntcState *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_UINT32("size", OMAPIntcState, size, 0x100), DEFINE_PROP_END_OF_LIST(), }; @@ -423,7 +423,7 @@ static const TypeInfo omap_intc_info = { static uint64_t omap2_inth_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; + OMAPIntcState *s = opaque; int offset = addr; int bank_no, line_no; struct omap_intr_handler_bank_s *bank = NULL; @@ -504,7 +504,7 @@ static uint64_t omap2_inth_read(void *opaque, hwaddr addr, static void omap2_inth_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; + OMAPIntcState *s = opaque; int offset = addr; int bank_no, line_no; struct omap_intr_handler_bank_s *bank = NULL; @@ -622,7 +622,7 @@ static const MemoryRegionOps omap2_inth_mem_ops = { static void omap2_intc_init(Object *obj) { DeviceState *dev = DEVICE(obj); - struct omap_intr_handler_s *s = OMAP_INTC(obj); + OMAPIntcState *s = OMAP_INTC(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); s->level_only = 1; @@ -637,7 +637,7 @@ static void omap2_intc_init(Object *obj) static void omap2_intc_realize(DeviceState *dev, Error **errp) { - struct omap_intr_handler_s *s = OMAP_INTC(dev); + OMAPIntcState *s = OMAP_INTC(dev); if (!s->iclk) { error_setg(errp, "omap2-intc: iclk not connected"); @@ -650,7 +650,7 @@ static void omap2_intc_realize(DeviceState *dev, Error **errp) } static Property omap2_intc_properties[] = { - DEFINE_PROP_UINT8("revision", struct omap_intr_handler_s, + DEFINE_PROP_UINT8("revision", OMAPIntcState, revision, 0x21), DEFINE_PROP_END_OF_LIST(), }; @@ -676,7 +676,7 @@ static const TypeInfo omap2_intc_info = { static const TypeInfo omap_intc_type_info = { .name = TYPE_OMAP_INTC, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(omap_intr_handler), + .instance_size = sizeof(OMAPIntcState), .abstract = true, }; diff --git a/hw/intc/ompic.c b/hw/intc/ompic.c index 1f10314807..99032ea7f7 100644 --- a/hw/intc/ompic.c +++ b/hw/intc/ompic.c @@ -137,7 +137,7 @@ static const VMStateDescription vmstate_or1k_ompic_cpu = { .name = "or1k_ompic_cpu", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(status, OR1KOMPICCPUState), VMSTATE_UINT32(control, OR1KOMPICCPUState), VMSTATE_END_OF_LIST() @@ -148,7 +148,7 @@ static const VMStateDescription vmstate_or1k_ompic = { .name = TYPE_OR1K_OMPIC, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(cpus, OR1KOMPICState, OMPIC_MAX_CPUS, 1, vmstate_or1k_ompic_cpu, OR1KOMPICCPUState), VMSTATE_UINT32(num_cpus, OR1KOMPICState), diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 49504e740f..9792a11224 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -32,7 +32,6 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/ppc/mac.h" #include "hw/pci/pci.h" #include "hw/ppc/openpic.h" #include "hw/ppc/ppc_e500.h" @@ -611,11 +610,8 @@ static void openpic_gbl_write(void *opaque, hwaddr addr, uint64_t val, case 0x10B0: case 0x10C0: case 0x10D0: - { - int idx; - idx = (addr - 0x10A0) >> 4; - write_IRQreg_ivpr(opp, opp->irq_ipi0 + idx, val); - } + idx = (addr - 0x10A0) >> 4; + write_IRQreg_ivpr(opp, opp->irq_ipi0 + idx, val); break; case 0x10E0: /* SPVE */ opp->spve = val & opp->vector_mask; @@ -729,7 +725,7 @@ static void openpic_tmr_set_tmr(OpenPICTimer *tmr, uint32_t val, bool enabled) } /* - * Returns the currrent tccr value, i.e., timer value (in clocks) with + * Returns the current tccr value, i.e., timer value (in clocks) with * appropriate TOG. */ static uint64_t openpic_tmr_get_timer(OpenPICTimer *tmr) @@ -1395,7 +1391,7 @@ static const VMStateDescription vmstate_openpic_irq_queue = { .name = "openpic_irq_queue", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BITMAP(queue, IRQQueue, 0, queue_size), VMSTATE_INT32(next, IRQQueue), VMSTATE_INT32(priority, IRQQueue), @@ -1407,7 +1403,7 @@ static const VMStateDescription vmstate_openpic_irqdest = { .name = "openpic_irqdest", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(ctpr, IRQDest), VMSTATE_STRUCT(raised, IRQDest, 0, vmstate_openpic_irq_queue, IRQQueue), @@ -1422,7 +1418,7 @@ static const VMStateDescription vmstate_openpic_irqsource = { .name = "openpic_irqsource", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ivpr, IRQSource), VMSTATE_UINT32(idr, IRQSource), VMSTATE_UINT32(destmask, IRQSource), @@ -1436,7 +1432,7 @@ static const VMStateDescription vmstate_openpic_timer = { .name = "openpic_timer", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(tccr, OpenPICTimer), VMSTATE_UINT32(tbcr, OpenPICTimer), VMSTATE_END_OF_LIST() @@ -1447,7 +1443,7 @@ static const VMStateDescription vmstate_openpic_msi = { .name = "openpic_msi", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(msir, OpenPICMSI), VMSTATE_END_OF_LIST() } @@ -1472,7 +1468,7 @@ static const VMStateDescription vmstate_openpic = { .version_id = 3, .minimum_version_id = 3, .post_load = openpic_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(gcr, OpenPICState), VMSTATE_UINT32(vir, OpenPICState), VMSTATE_UINT32(pir, OpenPICState), diff --git a/hw/intc/pl190.c b/hw/intc/pl190.c index cd88443601..d79e5d8076 100644 --- a/hw/intc/pl190.c +++ b/hw/intc/pl190.c @@ -258,7 +258,7 @@ static const VMStateDescription vmstate_pl190 = { .name = "pl190", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(level, PL190State), VMSTATE_UINT32(soft_level, PL190State), VMSTATE_UINT32(irq_enable, PL190State), diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index 1ce1d7b07d..da10deceb8 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -18,6 +18,7 @@ #include "monitor/monitor.h" #include "hw/ppc/fdt.h" #include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_chip.h" #include "hw/ppc/pnv_core.h" #include "hw/ppc/pnv_xscom.h" #include "hw/ppc/pnv_xive.h" @@ -66,26 +67,6 @@ static const XiveVstInfo vst_infos[] = { qemu_log_mask(LOG_GUEST_ERROR, "XIVE[%x] - " fmt "\n", \ (xive)->chip->chip_id, ## __VA_ARGS__); -/* - * QEMU version of the GETFIELD/SETFIELD macros - * - * TODO: It might be better to use the existing extract64() and - * deposit64() but this means that all the register definitions will - * change and become incompatible with the ones found in skiboot. - * - * Keep it as it is for now until we find a common ground. - */ -static inline uint64_t GETFIELD(uint64_t mask, uint64_t word) -{ - return (word & mask) >> ctz64(mask); -} - -static inline uint64_t SETFIELD(uint64_t mask, uint64_t word, - uint64_t value) -{ - return (word & ~mask) | ((value << ctz64(mask)) & mask); -} - /* * When PC_TCTXT_CHIPID_OVERRIDE is configured, the PC_TCTXT_CHIPID * field overrides the hardwired chip ID in the Powerbus operations @@ -103,28 +84,6 @@ static uint8_t pnv_xive_block_id(PnvXive *xive) 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_remote(uint8_t blk) -{ - PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); - int i; - - for (i = 0; i < pnv->num_chips; i++) { - Pnv9Chip *chip9 = PNV9_CHIP(pnv->chips[i]); - PnvXive *xive = &chip9->xive; - - if (pnv_xive_block_id(xive) == blk) { - return xive; - } - } - return NULL; -} - /* * VST accessors for SBE, EAT, ENDT, NVT * @@ -228,6 +187,42 @@ static uint64_t pnv_xive_vst_addr_indirect(PnvXive *xive, uint32_t type, return pnv_xive_vst_addr_direct(xive, type, vsd, (idx % vst_per_page)); } +/* + * This is a simplified model of operation forwarding on a remote IC. + * + * A PC MMIO address is built to identify the NVT structure. The load + * on the remote IC will return the address of the structure in RAM, + * which will then be used by pnv_xive_vst_write/read to perform the + * RAM operation. + */ +static uint64_t pnv_xive_vst_addr_remote(PnvXive *xive, uint32_t type, + uint64_t vsd, uint8_t blk, + uint32_t idx) +{ + const XiveVstInfo *info = &vst_infos[type]; + uint64_t remote_addr = vsd & VSD_ADDRESS_MASK; + uint64_t vst_addr; + MemTxResult result; + + if (type != VST_TSEL_VPDT) { + xive_error(xive, "VST: invalid access on remote VST %s %x/%x !?", + info->name, blk, idx); + return 0; + } + + remote_addr |= ((uint64_t)idx) << xive->pc_shift; + + vst_addr = address_space_ldq_be(&address_space_memory, remote_addr, + MEMTXATTRS_UNSPECIFIED, &result); + if (result != MEMTX_OK) { + xive_error(xive, "VST: read failed at @0x%" HWADDR_PRIx + " for NVT %x/%x\n", remote_addr, blk, idx); + return 0; + } + + return vst_addr; +} + static uint64_t pnv_xive_vst_addr(PnvXive *xive, uint32_t type, uint8_t blk, uint32_t idx) { @@ -244,9 +239,7 @@ 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_remote(blk); - - return xive ? pnv_xive_vst_addr(xive, type, blk, idx) : 0; + return pnv_xive_vst_addr_remote(xive, type, vsd, blk, idx); } if (VSD_INDIRECT & vsd) { @@ -261,12 +254,20 @@ static int pnv_xive_vst_read(PnvXive *xive, uint32_t type, uint8_t blk, { const XiveVstInfo *info = &vst_infos[type]; uint64_t addr = pnv_xive_vst_addr(xive, type, blk, idx); + MemTxResult result; if (!addr) { return -1; } - cpu_physical_memory_read(addr, data, info->size); + result = address_space_read(&address_space_memory, addr, + MEMTXATTRS_UNSPECIFIED, data, + info->size); + if (result != MEMTX_OK) { + xive_error(xive, "VST: read failed at @0x%" HWADDR_PRIx + " for VST %s %x/%x\n", addr, info->name, blk, idx); + return -1; + } return 0; } @@ -277,16 +278,27 @@ static int pnv_xive_vst_write(PnvXive *xive, uint32_t type, uint8_t blk, { const XiveVstInfo *info = &vst_infos[type]; uint64_t addr = pnv_xive_vst_addr(xive, type, blk, idx); + MemTxResult result; if (!addr) { return -1; } if (word_number == XIVE_VST_WORD_ALL) { - cpu_physical_memory_write(addr, data, info->size); + result = address_space_write(&address_space_memory, addr, + MEMTXATTRS_UNSPECIFIED, data, + info->size); } else { - cpu_physical_memory_write(addr + word_number * 4, - data + word_number * 4, 4); + result = address_space_write(&address_space_memory, + addr + word_number * 4, + MEMTXATTRS_UNSPECIFIED, + data + word_number * 4, 4); + } + + if (result != MEMTX_OK) { + xive_error(xive, "VST: write failed at @0x%" HWADDR_PRIx + "for VST %s %x/%x\n", addr, info->name, blk, idx); + return -1; } return 0; } @@ -294,12 +306,26 @@ static int pnv_xive_vst_write(PnvXive *xive, uint32_t type, uint8_t blk, static int pnv_xive_get_end(XiveRouter *xrtr, uint8_t blk, uint32_t idx, XiveEND *end) { + PnvXive *xive = PNV_XIVE(xrtr); + + if (pnv_xive_block_id(xive) != blk) { + xive_error(xive, "VST: END %x/%x is remote !?", blk, idx); + return -1; + } + return pnv_xive_vst_read(PNV_XIVE(xrtr), VST_TSEL_EQDT, blk, idx, end); } static int pnv_xive_write_end(XiveRouter *xrtr, uint8_t blk, uint32_t idx, XiveEND *end, uint8_t word_number) { + PnvXive *xive = PNV_XIVE(xrtr); + + if (pnv_xive_block_id(xive) != blk) { + xive_error(xive, "VST: END %x/%x is remote !?", blk, idx); + return -1; + } + return pnv_xive_vst_write(PNV_XIVE(xrtr), VST_TSEL_EQDT, blk, idx, end, word_number); } @@ -498,6 +524,16 @@ static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format, return count; } +static uint32_t pnv_xive_presenter_get_config(XivePresenter *xptr) +{ + uint32_t cfg = 0; + + /* TIMA GEN1 is all P9 knows */ + cfg |= XIVE_PRESENTER_GEN1_TIMA_OS; + + return cfg; +} + static uint8_t pnv_xive_get_block_id(XiveRouter *xrtr) { return pnv_xive_block_id(PNV_XIVE(xrtr)); @@ -952,7 +988,7 @@ static void pnv_xive_ic_reg_write(void *opaque, hwaddr offset, */ case VC_SBC_CONFIG: /* Store EOI configuration */ /* - * Configure store EOI if required by firwmare (skiboot has removed + * Configure store EOI if required by firmware (skiboot has removed * support recently though) */ if (val & (VC_SBC_CONF_CPLX_CIST | VC_SBC_CONF_CIST_BOTH)) { @@ -1358,6 +1394,50 @@ static const MemoryRegionOps pnv_xive_ic_reg_ops = { #define PNV_XIVE_SYNC_PUSH 0xf00 /* Sync push context */ #define PNV_XIVE_SYNC_VPC 0xf80 /* Sync remove VPC store */ +static void pnv_xive_end_notify(XiveRouter *xrtr, XiveEAS *eas) +{ + PnvXive *xive = PNV_XIVE(xrtr); + uint8_t end_blk = xive_get_field64(EAS_END_BLOCK, eas->w); + uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w); + uint32_t end_data = xive_get_field64(EAS_END_DATA, eas->w); + uint64_t end_vsd = xive->vsds[VST_TSEL_EQDT][end_blk]; + + switch (GETFIELD(VSD_MODE, end_vsd)) { + case VSD_MODE_EXCLUSIVE: + /* Perform the END notification on the local IC. */ + xive_router_end_notify(xrtr, eas); + break; + + case VSD_MODE_FORWARD: { + MemTxResult result; + uint64_t notif_port = end_vsd & VSD_ADDRESS_MASK; + uint64_t data = XIVE_TRIGGER_END | XIVE_TRIGGER_PQ | + be64_to_cpu(eas->w); + + /* Forward the store on the remote IC notify page. */ + address_space_stq_be(&address_space_memory, notif_port, data, + MEMTXATTRS_UNSPECIFIED, &result); + if (result != MEMTX_OK) { + xive_error(xive, "IC: Forward notif END %x/%x [%x] failed @%" + HWADDR_PRIx, end_blk, end_idx, end_data, notif_port); + return; + } + break; + } + + case VSD_MODE_INVALID: + default: + /* Set FIR */ + xive_error(xive, "IC: Invalid END VSD for block %x", end_blk); + return; + } +} + +/* + * The notify page can either be used to receive trigger events from + * the HW controllers (PHB, PSI) or to reroute interrupts between + * Interrupt controllers. + */ static void pnv_xive_ic_hw_trigger(PnvXive *xive, hwaddr addr, uint64_t val) { uint8_t blk; @@ -1366,8 +1446,8 @@ static void pnv_xive_ic_hw_trigger(PnvXive *xive, hwaddr addr, uint64_t val) trace_pnv_xive_ic_hw_trigger(addr, val); if (val & XIVE_TRIGGER_END) { - xive_error(xive, "IC: END trigger at @0x%"HWADDR_PRIx" data 0x%"PRIx64, - addr, val); + val = cpu_to_be64(val); + pnv_xive_end_notify(XIVE_ROUTER(xive), (XiveEAS *) &val); return; } @@ -1712,16 +1792,20 @@ static const MemoryRegionOps pnv_xive_vc_ops = { }; /* - * Presenter Controller MMIO region. The Virtualization Controller - * updates the IPB in the NVT table when required. Not modeled. + * Presenter Controller MMIO region. Points to the NVT sets. + * + * HW implements all possible mem ops to the underlying NVT structure + * but QEMU does not need to be so precise. The model implementation + * simply returns the RAM address of the NVT structure which is then + * used by pnv_xive_vst_write/read to perform the RAM operation. */ -static uint64_t pnv_xive_pc_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t pnv_xive_pc_read(void *opaque, hwaddr offset, unsigned size) { PnvXive *xive = PNV_XIVE(opaque); + uint32_t nvt_idx = offset >> xive->pc_shift; + uint8_t blk = pnv_xive_block_id(xive); /* TODO: VDT -> block xlate */ - xive_error(xive, "PC: invalid read @%"HWADDR_PRIx, addr); - return -1; + return pnv_xive_vst_addr(xive, VST_TSEL_VPDT, blk, nvt_idx); } static void pnv_xive_pc_write(void *opaque, hwaddr addr, @@ -1907,6 +1991,7 @@ static void pnv_xive_realize(DeviceState *dev, Error **errp) memory_region_init_io(&xive->ic_notify_mmio, OBJECT(dev), &pnv_xive_ic_notify_ops, xive, "xive-ic-notify", 1 << xive->ic_shift); + xive->ic_notify_mmio.disable_reentrancy_guard = true; /* The Pervasive LSI trigger and EOI pages (not modeled) */ memory_region_init_io(&xive->ic_lsi_mmio, OBJECT(dev), &pnv_xive_ic_lsi_ops, @@ -1942,6 +2027,7 @@ static void pnv_xive_realize(DeviceState *dev, Error **errp) /* Presenter Controller MMIO region (not modeled) */ memory_region_init_io(&xive->pc_mmio, OBJECT(xive), &pnv_xive_pc_ops, xive, "xive-pc", PNV9_XIVE_PC_SIZE); + xive->pc_mmio.disable_reentrancy_guard = true; /* Thread Interrupt Management Area (Direct) */ memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &pnv_xive_tm_ops, @@ -2007,9 +2093,11 @@ static void pnv_xive_class_init(ObjectClass *klass, void *data) xrc->get_nvt = pnv_xive_get_nvt; xrc->write_nvt = pnv_xive_write_nvt; xrc->get_block_id = pnv_xive_get_block_id; + xrc->end_notify = pnv_xive_end_notify; xnc->notify = pnv_xive_notify; xpc->match_nvt = pnv_xive_match_nvt; + xpc->get_config = pnv_xive_presenter_get_config; }; static const TypeInfo pnv_xive_info = { diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index a39e070e82..4b8d0a5d81 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -16,6 +16,7 @@ #include "monitor/monitor.h" #include "hw/ppc/fdt.h" #include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_chip.h" #include "hw/ppc/pnv_core.h" #include "hw/ppc/pnv_xscom.h" #include "hw/ppc/xive2.h" @@ -75,26 +76,6 @@ static const XiveVstInfo vst_infos[] = { qemu_log_mask(LOG_GUEST_ERROR, "XIVE[%x] - " fmt "\n", \ (xive)->chip->chip_id, ## __VA_ARGS__); -/* - * QEMU version of the GETFIELD/SETFIELD macros - * - * TODO: It might be better to use the existing extract64() and - * deposit64() but this means that all the register definitions will - * change and become incompatible with the ones found in skiboot. - * - * Keep it as it is for now until we find a common ground. - */ -static inline uint64_t GETFIELD(uint64_t mask, uint64_t word) -{ - return (word & mask) >> ctz64(mask); -} - -static inline uint64_t SETFIELD(uint64_t mask, uint64_t word, - uint64_t value) -{ - return (word & ~mask) | ((value << ctz64(mask)) & mask); -} - /* * TODO: Document block id override */ @@ -182,7 +163,9 @@ static uint64_t pnv_xive2_vst_addr_indirect(PnvXive2 *xive, uint32_t type, ldq_be_dma(&address_space_memory, vsd_addr, &vsd, MEMTXATTRS_UNSPECIFIED); if (!(vsd & VSD_ADDRESS_MASK)) { +#ifdef XIVE2_DEBUG xive2_error(xive, "VST: invalid %s entry %x !?", info->name, idx); +#endif return 0; } @@ -204,7 +187,9 @@ static uint64_t pnv_xive2_vst_addr_indirect(PnvXive2 *xive, uint32_t type, MEMTXATTRS_UNSPECIFIED); if (!(vsd & VSD_ADDRESS_MASK)) { +#ifdef XIVE2_DEBUG xive2_error(xive, "VST: invalid %s entry %x !?", info->name, idx); +#endif return 0; } @@ -255,12 +240,20 @@ static int pnv_xive2_vst_read(PnvXive2 *xive, uint32_t type, uint8_t blk, { const XiveVstInfo *info = &vst_infos[type]; uint64_t addr = pnv_xive2_vst_addr(xive, type, blk, idx); + MemTxResult result; if (!addr) { return -1; } - cpu_physical_memory_read(addr, data, info->size); + result = address_space_read(&address_space_memory, addr, + MEMTXATTRS_UNSPECIFIED, data, + info->size); + if (result != MEMTX_OK) { + xive2_error(xive, "VST: read failed at @0x%" HWADDR_PRIx + " for VST %s %x/%x\n", addr, info->name, blk, idx); + return -1; + } return 0; } @@ -271,16 +264,27 @@ static int pnv_xive2_vst_write(PnvXive2 *xive, uint32_t type, uint8_t blk, { const XiveVstInfo *info = &vst_infos[type]; uint64_t addr = pnv_xive2_vst_addr(xive, type, blk, idx); + MemTxResult result; if (!addr) { return -1; } if (word_number == XIVE_VST_WORD_ALL) { - cpu_physical_memory_write(addr, data, info->size); + result = address_space_write(&address_space_memory, addr, + MEMTXATTRS_UNSPECIFIED, data, + info->size); } else { - cpu_physical_memory_write(addr + word_number * 4, - data + word_number * 4, 4); + result = address_space_write(&address_space_memory, + addr + word_number * 4, + MEMTXATTRS_UNSPECIFIED, + data + word_number * 4, 4); + } + + if (result != MEMTX_OK) { + xive2_error(xive, "VST: write failed at @0x%" HWADDR_PRIx + "for VST %s %x/%x\n", addr, info->name, blk, idx); + return -1; } return 0; } @@ -516,6 +520,17 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, return count; } +static uint32_t pnv_xive2_presenter_get_config(XivePresenter *xptr) +{ + PnvXive2 *xive = PNV_XIVE2(xptr); + uint32_t cfg = 0; + + if (xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS) { + cfg |= XIVE_PRESENTER_GEN1_TIMA_OS; + } + return cfg; +} + static uint8_t pnv_xive2_get_block_id(Xive2Router *xrtr) { return pnv_xive2_block_id(PNV_XIVE2(xrtr)); @@ -974,6 +989,10 @@ static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset, val = xive->vc_regs[reg]; break; + case VC_ESBC_CFG: + val = xive->vc_regs[reg]; + break; + /* * EAS cache updates (not modeled) */ @@ -1065,6 +1084,9 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, /* ESB update */ break; + case VC_ESBC_CFG: + break; + /* * EAS cache updates (not modeled) */ @@ -1284,6 +1306,9 @@ static uint64_t pnv_xive2_ic_tctxt_read(void *opaque, hwaddr offset, case TCTXT_EN1_RESET: val = xive->tctxt_regs[TCTXT_EN1 >> 3]; break; + case TCTXT_CFG: + val = xive->tctxt_regs[reg]; + break; default: xive2_error(xive, "TCTXT: invalid read @%"HWADDR_PRIx, offset); } @@ -1295,6 +1320,7 @@ static void pnv_xive2_ic_tctxt_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + uint32_t reg = offset >> 3; switch (offset) { /* @@ -1302,6 +1328,7 @@ static void pnv_xive2_ic_tctxt_write(void *opaque, hwaddr offset, */ case TCTXT_EN0: /* Physical Thread Enable */ case TCTXT_EN1: /* Physical Thread Enable (fused core) */ + xive->tctxt_regs[reg] = val; break; case TCTXT_EN0_SET: @@ -1316,7 +1343,9 @@ static void pnv_xive2_ic_tctxt_write(void *opaque, hwaddr offset, case TCTXT_EN1_RESET: xive->tctxt_regs[TCTXT_EN1 >> 3] &= ~val; break; - + case TCTXT_CFG: + xive->tctxt_regs[reg] = val; + break; default: xive2_error(xive, "TCTXT: invalid write @%"HWADDR_PRIx, offset); return; @@ -1574,6 +1603,24 @@ static const MemoryRegionOps pnv_xive2_ic_sync_ops = { * When the TM direct pages of the IC controller are accessed, the * target HW thread is deduced from the page offset. */ +static uint32_t pnv_xive2_ic_tm_get_pir(PnvXive2 *xive, hwaddr offset) +{ + /* On P10, the node ID shift in the PIR register is 8 bits */ + return xive->chip->chip_id << 8 | offset >> xive->ic_shift; +} + +static uint32_t pnv_xive2_ic_tm_get_hw_page_offset(PnvXive2 *xive, + hwaddr offset) +{ + /* + * Indirect TIMA accesses are similar to direct accesses for + * privilege ring 0. So remove any traces of the hw thread ID from + * the offset in the IC BAR as it could be interpreted as the ring + * privilege when calling the underlying direct access functions. + */ + return offset & ((1ull << xive->ic_shift) - 1); +} + static XiveTCTX *pnv_xive2_get_indirect_tctx(PnvXive2 *xive, uint32_t pir) { PnvChip *chip = xive->chip; @@ -1596,12 +1643,17 @@ static uint64_t pnv_xive2_ic_tm_indirect_read(void *opaque, hwaddr offset, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); - uint32_t pir = offset >> xive->ic_shift; - XiveTCTX *tctx = pnv_xive2_get_indirect_tctx(xive, pir); + XivePresenter *xptr = XIVE_PRESENTER(xive); + hwaddr hw_page_offset; + uint32_t pir; + XiveTCTX *tctx; uint64_t val = -1; + pir = pnv_xive2_ic_tm_get_pir(xive, offset); + hw_page_offset = pnv_xive2_ic_tm_get_hw_page_offset(xive, offset); + tctx = pnv_xive2_get_indirect_tctx(xive, pir); if (tctx) { - val = xive_tctx_tm_read(NULL, tctx, offset, size); + val = xive_tctx_tm_read(xptr, tctx, hw_page_offset, size); } return val; @@ -1611,11 +1663,16 @@ static void pnv_xive2_ic_tm_indirect_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); - uint32_t pir = offset >> xive->ic_shift; - XiveTCTX *tctx = pnv_xive2_get_indirect_tctx(xive, pir); + XivePresenter *xptr = XIVE_PRESENTER(xive); + hwaddr hw_page_offset; + uint32_t pir; + XiveTCTX *tctx; + pir = pnv_xive2_ic_tm_get_pir(xive, offset); + hw_page_offset = pnv_xive2_ic_tm_get_hw_page_offset(xive, offset); + tctx = pnv_xive2_get_indirect_tctx(xive, pir); if (tctx) { - xive_tctx_tm_write(NULL, tctx, offset, val, size); + xive_tctx_tm_write(xptr, tctx, hw_page_offset, val, size); } } @@ -1624,11 +1681,11 @@ static const MemoryRegionOps pnv_xive2_ic_tm_indirect_ops = { .write = pnv_xive2_ic_tm_indirect_write, .endianness = DEVICE_BIG_ENDIAN, .valid = { - .min_access_size = 8, + .min_access_size = 1, .max_access_size = 8, }, .impl = { - .min_access_size = 8, + .min_access_size = 1, .max_access_size = 8, }, }; @@ -1636,17 +1693,6 @@ static const MemoryRegionOps pnv_xive2_ic_tm_indirect_ops = { /* * TIMA ops */ - -/* - * Special TIMA offsets to handle accesses in a POWER10 way. - * - * Only the CAM line updates done by the hypervisor should be handled - * specifically. - */ -#define HV_PAGE_OFFSET (XIVE_TM_HV_PAGE << TM_SHIFT) -#define HV_PUSH_OS_CTX_OFFSET (HV_PAGE_OFFSET | (TM_QW1_OS + TM_WORD2)) -#define HV_PULL_OS_CTX_OFFSET (HV_PAGE_OFFSET | TM_SPC_PULL_OS_CTX) - static void pnv_xive2_tm_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { @@ -1654,16 +1700,7 @@ static void pnv_xive2_tm_write(void *opaque, hwaddr offset, PnvXive2 *xive = pnv_xive2_tm_get_xive(cpu); XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc); XivePresenter *xptr = XIVE_PRESENTER(xive); - bool gen1_tima_os = - xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS; - /* TODO: should we switch the TM ops table instead ? */ - if (!gen1_tima_os && offset == HV_PUSH_OS_CTX_OFFSET) { - xive2_tm_push_os_ctx(xptr, tctx, offset, value, size); - return; - } - - /* Other TM ops are the same as XIVE1 */ xive_tctx_tm_write(xptr, tctx, offset, value, size); } @@ -1673,15 +1710,7 @@ static uint64_t pnv_xive2_tm_read(void *opaque, hwaddr offset, unsigned size) PnvXive2 *xive = pnv_xive2_tm_get_xive(cpu); XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc); XivePresenter *xptr = XIVE_PRESENTER(xive); - bool gen1_tima_os = - xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS; - /* TODO: should we switch the TM ops table instead ? */ - if (!gen1_tima_os && offset == HV_PULL_OS_CTX_OFFSET) { - return xive2_tm_pull_os_ctx(xptr, tctx, offset, size); - } - - /* Other TM ops are the same as XIVE1 */ return xive_tctx_tm_read(xptr, tctx, offset, size); } @@ -1974,6 +2003,7 @@ static void pnv_xive2_class_init(ObjectClass *klass, void *data) xnc->notify = pnv_xive2_notify; xpc->match_nvt = pnv_xive2_match_nvt; + xpc->get_config = pnv_xive2_presenter_get_config; }; static const TypeInfo pnv_xive2_info = { diff --git a/hw/intc/pnv_xive2_regs.h b/hw/intc/pnv_xive2_regs.h index 0c096e4adb..7165dc8704 100644 --- a/hw/intc/pnv_xive2_regs.h +++ b/hw/intc/pnv_xive2_regs.h @@ -232,6 +232,10 @@ #define VC_ESBC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(32, 35) #define VC_ESBC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(36, 63) /* 28-bit */ +/* ESBC configuration */ +#define X_VC_ESBC_CFG 0x148 +#define VC_ESBC_CFG 0x240 + /* EASC flush control register */ #define X_VC_EASC_FLUSH_CTRL 0x160 #define VC_EASC_FLUSH_CTRL 0x300 @@ -405,6 +409,10 @@ #define X_TCTXT_EN1_RESET 0x307 #define TCTXT_EN1_RESET 0x038 +/* TCTXT Config register */ +#define X_TCTXT_CFG 0x328 +#define TCTXT_CFG 0x140 + /* * VSD Tables */ diff --git a/hw/intc/pnv_xive_regs.h b/hw/intc/pnv_xive_regs.h index c78f030c02..793847638b 100644 --- a/hw/intc/pnv_xive_regs.h +++ b/hw/intc/pnv_xive_regs.h @@ -228,6 +228,7 @@ * VSD and is only meant to be used in indirect mode ! */ #define VSD_MODE PPC_BITMASK(0, 1) +#define VSD_MODE_INVALID 0 #define VSD_MODE_SHARED 1 #define VSD_MODE_EXCLUSIVE 2 #define VSD_MODE_FORWARD 3 diff --git a/hw/intc/ppc-uic.c b/hw/intc/ppc-uic.c index 60013f2dde..9a67f7f651 100644 --- a/hw/intc/ppc-uic.c +++ b/hw/intc/ppc-uic.c @@ -25,11 +25,8 @@ #include "qemu/osdep.h" #include "hw/intc/ppc-uic.h" #include "hw/irq.h" -#include "cpu.h" -#include "hw/ppc/ppc.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "qapi/error.h" enum { DCR_UICSR = 0x000, @@ -105,10 +102,9 @@ static void ppcuic_trigger_irq(PPCUIC *uic) static void ppcuic_set_irq(void *opaque, int irq_num, int level) { - PPCUIC *uic; + PPCUIC *uic = opaque; uint32_t mask, sr; - uic = opaque; mask = 1U << (31 - irq_num); LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32 " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n", @@ -144,10 +140,9 @@ static void ppcuic_set_irq(void *opaque, int irq_num, int level) static uint32_t dcr_read_uic(void *opaque, int dcrn) { - PPCUIC *uic; + PPCUIC *uic = opaque; uint32_t ret; - uic = opaque; dcrn -= uic->dcr_base; switch (dcrn) { case DCR_UICSR: @@ -192,9 +187,8 @@ static uint32_t dcr_read_uic(void *opaque, int dcrn) static void dcr_write_uic(void *opaque, int dcrn, uint32_t val) { - PPCUIC *uic; + PPCUIC *uic = opaque; - uic = opaque; dcrn -= uic->dcr_base; LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val); switch (dcrn) { @@ -251,19 +245,12 @@ static void ppc_uic_reset(DeviceState *dev) static void ppc_uic_realize(DeviceState *dev, Error **errp) { PPCUIC *uic = PPC_UIC(dev); + Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - PowerPCCPU *cpu; int i; - if (!uic->cpu) { - /* This is a programming error in the code using this device */ - error_setg(errp, "ppc-uic 'cpu' link property was not set"); - return; - } - - cpu = POWERPC_CPU(uic->cpu); for (i = 0; i < DCR_UICMAX; i++) { - ppc_dcr_register(&cpu->env, uic->dcr_base + i, uic, + ppc4xx_dcr_register(dcr, uic->dcr_base + i, uic, &dcr_read_uic, &dcr_write_uic); } @@ -273,7 +260,6 @@ static void ppc_uic_realize(DeviceState *dev, Error **errp) } static Property ppc_uic_properties[] = { - DEFINE_PROP_LINK("cpu", PPCUIC, cpu, TYPE_CPU, CPUState *), DEFINE_PROP_UINT32("dcr-base", PPCUIC, dcr_base, 0xc0), DEFINE_PROP_BOOL("use-vectors", PPCUIC, use_vectors, true), DEFINE_PROP_END_OF_LIST() @@ -283,7 +269,7 @@ static const VMStateDescription ppc_uic_vmstate = { .name = "ppc-uic", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(level, PPCUIC), VMSTATE_UINT32(uicsr, PPCUIC), VMSTATE_UINT32(uicer, PPCUIC), @@ -308,7 +294,7 @@ static void ppc_uic_class_init(ObjectClass *klass, void *data) static const TypeInfo ppc_uic_info = { .name = TYPE_PPC_UIC, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_PPC4xx_DCR_DEVICE, .instance_size = sizeof(PPCUIC), .class_init = ppc_uic_class_init, }; diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c index e7942c4e5a..e9f0536b1c 100644 --- a/hw/intc/riscv_aclint.c +++ b/hw/intc/riscv_aclint.c @@ -32,6 +32,7 @@ #include "hw/intc/riscv_aclint.h" #include "qemu/timer.h" #include "hw/irq.h" +#include "migration/vmstate.h" typedef struct riscv_aclint_mtimer_callback { RISCVAclintMTimerState *s; @@ -63,21 +64,24 @@ static void riscv_aclint_mtimer_write_timecmp(RISCVAclintMTimerState *mtimer, uint64_t next; uint64_t diff; - uint64_t rtc_r = cpu_riscv_read_rtc(mtimer); + uint64_t rtc = cpu_riscv_read_rtc(mtimer); - cpu->env.timecmp = value; - if (cpu->env.timecmp <= rtc_r) { + /* Compute the relative hartid w.r.t the socket */ + hartid = hartid - mtimer->hartid_base; + + mtimer->timecmp[hartid] = value; + if (mtimer->timecmp[hartid] <= rtc) { /* * If we're setting an MTIMECMP value in the "past", * immediately raise the timer interrupt */ - qemu_irq_raise(mtimer->timer_irqs[hartid - mtimer->hartid_base]); + qemu_irq_raise(mtimer->timer_irqs[hartid]); return; } /* otherwise, set up the future timer interrupt */ - qemu_irq_lower(mtimer->timer_irqs[hartid - mtimer->hartid_base]); - diff = cpu->env.timecmp - rtc_r; + qemu_irq_lower(mtimer->timer_irqs[hartid]); + diff = mtimer->timecmp[hartid] - rtc; /* back to ns (note args switched in muldiv64) */ uint64_t ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq); @@ -102,7 +106,7 @@ static void riscv_aclint_mtimer_write_timecmp(RISCVAclintMTimerState *mtimer, next = MIN(next, INT64_MAX); } - timer_mod(cpu->env.timer, next); + timer_mod(mtimer->timers[hartid], next); } /* @@ -126,18 +130,18 @@ static uint64_t riscv_aclint_mtimer_read(void *opaque, hwaddr addr, addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) { size_t hartid = mtimer->hartid_base + ((addr - mtimer->timecmp_base) >> 3); - CPUState *cpu = qemu_get_cpu(hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + CPUState *cpu = cpu_by_arch_id(hartid); + CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; if (!env) { qemu_log_mask(LOG_GUEST_ERROR, "aclint-mtimer: invalid hartid: %zu", hartid); } else if ((addr & 0x7) == 0) { /* timecmp_lo for RV32/RV64 or timecmp for RV64 */ - uint64_t timecmp = env->timecmp; + uint64_t timecmp = mtimer->timecmp[hartid]; return (size == 4) ? (timecmp & 0xFFFFFFFF) : timecmp; } else if ((addr & 0x7) == 4) { /* timecmp_hi */ - uint64_t timecmp = env->timecmp; + uint64_t timecmp = mtimer->timecmp[hartid]; return (timecmp >> 32) & 0xFFFFFFFF; } else { qemu_log_mask(LOG_UNIMP, @@ -169,15 +173,15 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr, addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) { size_t hartid = mtimer->hartid_base + ((addr - mtimer->timecmp_base) >> 3); - CPUState *cpu = qemu_get_cpu(hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + CPUState *cpu = cpu_by_arch_id(hartid); + CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; if (!env) { qemu_log_mask(LOG_GUEST_ERROR, "aclint-mtimer: invalid hartid: %zu", hartid); } else if ((addr & 0x7) == 0) { if (size == 4) { /* timecmp_lo for RV32/RV64 */ - uint64_t timecmp_hi = env->timecmp >> 32; + uint64_t timecmp_hi = mtimer->timecmp[hartid] >> 32; riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid, timecmp_hi << 32 | (value & 0xFFFFFFFF)); } else { @@ -188,7 +192,7 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr, } else if ((addr & 0x7) == 4) { if (size == 4) { /* timecmp_hi for RV32/RV64 */ - uint64_t timecmp_lo = env->timecmp; + uint64_t timecmp_lo = mtimer->timecmp[hartid]; riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid, value << 32 | (timecmp_lo & 0xFFFFFFFF)); } else { @@ -204,11 +208,12 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr, return; } else if (addr == mtimer->time_base || addr == mtimer->time_base + 4) { uint64_t rtc_r = cpu_riscv_read_rtc_raw(mtimer->timebase_freq); + uint64_t rtc = cpu_riscv_read_rtc(mtimer); if (addr == mtimer->time_base) { if (size == 4) { /* time_lo for RV32/RV64 */ - mtimer->time_delta = ((rtc_r & ~0xFFFFFFFFULL) | value) - rtc_r; + mtimer->time_delta = ((rtc & ~0xFFFFFFFFULL) | value) - rtc_r; } else { /* time for RV64 */ mtimer->time_delta = value - rtc_r; @@ -216,7 +221,7 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr, } else { if (size == 4) { /* time_hi for RV32/RV64 */ - mtimer->time_delta = (value << 32 | (rtc_r & 0xFFFFFFFF)) - rtc_r; + mtimer->time_delta = (value << 32 | (rtc & 0xFFFFFFFF)) - rtc_r; } else { qemu_log_mask(LOG_GUEST_ERROR, "aclint-mtimer: invalid time_hi write: %08x", @@ -227,14 +232,14 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr, /* Check if timer interrupt is triggered for each hart. */ for (i = 0; i < mtimer->num_harts; i++) { - CPUState *cpu = qemu_get_cpu(mtimer->hartid_base + i); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + CPUState *cpu = cpu_by_arch_id(mtimer->hartid_base + i); + CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; if (!env) { continue; } riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), mtimer->hartid_base + i, - env->timecmp); + mtimer->timecmp[i]); } return; } @@ -284,9 +289,11 @@ static void riscv_aclint_mtimer_realize(DeviceState *dev, Error **errp) s->timer_irqs = g_new(qemu_irq, s->num_harts); qdev_init_gpio_out(dev, s->timer_irqs, s->num_harts); + s->timers = g_new0(QEMUTimer *, s->num_harts); + s->timecmp = g_new0(uint64_t, s->num_harts); /* Claim timer interrupt bits */ for (i = 0; i < s->num_harts; i++) { - RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(s->hartid_base + i)); + RISCVCPU *cpu = RISCV_CPU(cpu_by_arch_id(s->hartid_base + i)); if (riscv_cpu_claim_interrupts(cpu, MIP_MTIP) < 0) { error_report("MTIP already claimed"); exit(1); @@ -310,6 +317,18 @@ static void riscv_aclint_mtimer_reset_enter(Object *obj, ResetType type) riscv_aclint_mtimer_write(mtimer, mtimer->time_base, 0, 8); } +static const VMStateDescription vmstate_riscv_mtimer = { + .name = "riscv_mtimer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_VARRAY_UINT32(timecmp, RISCVAclintMTimerState, + num_harts, 0, + vmstate_info_uint64, uint64_t), + VMSTATE_END_OF_LIST() + } +}; + static void riscv_aclint_mtimer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -317,6 +336,7 @@ static void riscv_aclint_mtimer_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, riscv_aclint_mtimer_properties); ResettableClass *rc = RESETTABLE_CLASS(klass); rc->phases.enter = riscv_aclint_mtimer_reset_enter; + dc->vmsd = &vmstate_riscv_mtimer; } static const TypeInfo riscv_aclint_mtimer_info = { @@ -336,6 +356,7 @@ DeviceState *riscv_aclint_mtimer_create(hwaddr addr, hwaddr size, { int i; DeviceState *dev = qdev_new(TYPE_RISCV_ACLINT_MTIMER); + RISCVAclintMTimerState *s = RISCV_ACLINT_MTIMER(dev); assert(num_harts <= RISCV_ACLINT_MAX_HARTS); assert(!(addr & 0x7)); @@ -352,9 +373,9 @@ DeviceState *riscv_aclint_mtimer_create(hwaddr addr, hwaddr size, sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); for (i = 0; i < num_harts; i++) { - CPUState *cpu = qemu_get_cpu(hartid_base + i); + CPUState *cpu = cpu_by_arch_id(hartid_base + i); RISCVCPU *rvcpu = RISCV_CPU(cpu); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; riscv_aclint_mtimer_callback *cb = g_new0(riscv_aclint_mtimer_callback, 1); @@ -366,11 +387,11 @@ DeviceState *riscv_aclint_mtimer_create(hwaddr addr, hwaddr size, riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, dev); } - cb->s = RISCV_ACLINT_MTIMER(dev); + cb->s = s; cb->num = i; - env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + s->timers[i] = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_aclint_mtimer_cb, cb); - env->timecmp = 0; + s->timecmp[i] = 0; qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(DEVICE(rvcpu), IRQ_M_TIMER)); @@ -387,8 +408,8 @@ static uint64_t riscv_aclint_swi_read(void *opaque, hwaddr addr, if (addr < (swi->num_harts << 2)) { size_t hartid = swi->hartid_base + (addr >> 2); - CPUState *cpu = qemu_get_cpu(hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + CPUState *cpu = cpu_by_arch_id(hartid); + CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; if (!env) { qemu_log_mask(LOG_GUEST_ERROR, "aclint-swi: invalid hartid: %zu", hartid); @@ -410,8 +431,8 @@ static void riscv_aclint_swi_write(void *opaque, hwaddr addr, uint64_t value, if (addr < (swi->num_harts << 2)) { size_t hartid = swi->hartid_base + (addr >> 2); - CPUState *cpu = qemu_get_cpu(hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + CPUState *cpu = cpu_by_arch_id(hartid); + CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; if (!env) { qemu_log_mask(LOG_GUEST_ERROR, "aclint-swi: invalid hartid: %zu", hartid); @@ -525,7 +546,7 @@ DeviceState *riscv_aclint_swi_create(hwaddr addr, uint32_t hartid_base, sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); for (i = 0; i < num_harts; i++) { - CPUState *cpu = qemu_get_cpu(hartid_base + i); + CPUState *cpu = cpu_by_arch_id(hartid_base + i); RISCVCPU *rvcpu = RISCV_CPU(cpu); qdev_connect_gpio_out(dev, i, diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index cfd007e629..fc5df0d598 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -31,6 +31,8 @@ #include "hw/irq.h" #include "target/riscv/cpu.h" #include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "kvm/kvm_riscv.h" #include "migration/vmstate.h" #define APLIC_MAX_IDC (1UL << 14) @@ -148,10 +150,19 @@ #define APLIC_IDC_CLAIMI 0x1c +/* + * KVM AIA only supports APLIC MSI, fallback to QEMU emulation if we want to use + * APLIC Wired. + */ +static bool is_kvm_aia(bool msimode) +{ + return kvm_irqchip_in_kernel() && msimode; +} + static uint32_t riscv_aplic_read_input_word(RISCVAPLICState *aplic, uint32_t word) { - uint32_t i, irq, ret = 0; + uint32_t i, irq, sourcecfg, sm, raw_input, irq_inverted, ret = 0; for (i = 0; i < 32; i++) { irq = word * 32 + i; @@ -159,7 +170,20 @@ static uint32_t riscv_aplic_read_input_word(RISCVAPLICState *aplic, continue; } - ret |= ((aplic->state[irq] & APLIC_ISTATE_INPUT) ? 1 : 0) << i; + sourcecfg = aplic->sourcecfg[irq]; + if (sourcecfg & APLIC_SOURCECFG_D) { + continue; + } + + sm = sourcecfg & APLIC_SOURCECFG_SM_MASK; + if (sm == APLIC_SOURCECFG_SM_INACTIVE) { + continue; + } + + raw_input = (aplic->state[irq] & APLIC_ISTATE_INPUT) ? 1 : 0; + irq_inverted = (sm == APLIC_SOURCECFG_SM_LEVEL_LOW || + sm == APLIC_SOURCECFG_SM_EDGE_FALL) ? 1 : 0; + ret |= (raw_input ^ irq_inverted) << i; } return ret; @@ -207,13 +231,25 @@ static void riscv_aplic_set_pending(RISCVAPLICState *aplic, } sm = sourcecfg & APLIC_SOURCECFG_SM_MASK; - if ((sm == APLIC_SOURCECFG_SM_INACTIVE) || - ((!aplic->msimode || (aplic->msimode && !pending)) && - ((sm == APLIC_SOURCECFG_SM_LEVEL_HIGH) || - (sm == APLIC_SOURCECFG_SM_LEVEL_LOW)))) { + if (sm == APLIC_SOURCECFG_SM_INACTIVE) { return; } + if ((sm == APLIC_SOURCECFG_SM_LEVEL_HIGH) || + (sm == APLIC_SOURCECFG_SM_LEVEL_LOW)) { + if (!aplic->msimode || (aplic->msimode && !pending)) { + return; + } + if ((aplic->state[irq] & APLIC_ISTATE_INPUT) && + (sm == APLIC_SOURCECFG_SM_LEVEL_LOW)) { + return; + } + if (!(aplic->state[irq] & APLIC_ISTATE_INPUT) && + (sm == APLIC_SOURCECFG_SM_LEVEL_HIGH)) { + return; + } + } + riscv_aplic_set_pending_raw(aplic, irq, pending); } @@ -452,6 +488,7 @@ static uint32_t riscv_aplic_idc_claimi(RISCVAPLICState *aplic, uint32_t idc) if (!topi) { aplic->iforce[idc] = 0; + riscv_aplic_idc_update(aplic, idc); return 0; } @@ -688,13 +725,13 @@ static void riscv_aplic_write(void *opaque, hwaddr addr, uint64_t value, * domains). */ if (aplic->num_children && - !(aplic->smsicfgaddrH & APLIC_xMSICFGADDRH_L)) { + !(aplic->mmsicfgaddrH & APLIC_xMSICFGADDRH_L)) { aplic->smsicfgaddr = value; } } else if (aplic->mmode && aplic->msimode && (addr == APLIC_SMSICFGADDRH)) { if (aplic->num_children && - !(aplic->smsicfgaddrH & APLIC_xMSICFGADDRH_L)) { + !(aplic->mmsicfgaddrH & APLIC_xMSICFGADDRH_L)) { aplic->smsicfgaddrH = value & APLIC_xMSICFGADDRH_VALID_MASK; } } else if ((APLIC_SETIP_BASE <= addr) && @@ -801,29 +838,35 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) uint32_t i; RISCVAPLICState *aplic = RISCV_APLIC(dev); - aplic->bitfield_words = (aplic->num_irqs + 31) >> 5; - aplic->sourcecfg = g_new0(uint32_t, aplic->num_irqs); - aplic->state = g_new(uint32_t, aplic->num_irqs); - aplic->target = g_new0(uint32_t, aplic->num_irqs); - if (!aplic->msimode) { - for (i = 0; i < aplic->num_irqs; i++) { - aplic->target[i] = 1; + if (!is_kvm_aia(aplic->msimode)) { + aplic->bitfield_words = (aplic->num_irqs + 31) >> 5; + aplic->sourcecfg = g_new0(uint32_t, aplic->num_irqs); + aplic->state = g_new0(uint32_t, aplic->num_irqs); + aplic->target = g_new0(uint32_t, aplic->num_irqs); + if (!aplic->msimode) { + for (i = 0; i < aplic->num_irqs; i++) { + aplic->target[i] = 1; + } } - } - aplic->idelivery = g_new0(uint32_t, aplic->num_harts); - aplic->iforce = g_new0(uint32_t, aplic->num_harts); - aplic->ithreshold = g_new0(uint32_t, aplic->num_harts); + aplic->idelivery = g_new0(uint32_t, aplic->num_harts); + aplic->iforce = g_new0(uint32_t, aplic->num_harts); + aplic->ithreshold = g_new0(uint32_t, aplic->num_harts); - memory_region_init_io(&aplic->mmio, OBJECT(dev), &riscv_aplic_ops, aplic, - TYPE_RISCV_APLIC, aplic->aperture_size); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &aplic->mmio); + memory_region_init_io(&aplic->mmio, OBJECT(dev), &riscv_aplic_ops, + aplic, TYPE_RISCV_APLIC, aplic->aperture_size); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &aplic->mmio); + } /* * Only root APLICs have hardware IRQ lines. All non-root APLICs * have IRQ lines delegated by their parent APLIC. */ if (!aplic->parent) { - qdev_init_gpio_in(dev, riscv_aplic_request, aplic->num_irqs); + if (kvm_enabled() && is_kvm_aia(aplic->msimode)) { + qdev_init_gpio_in(dev, riscv_kvm_aplic_request, aplic->num_irqs); + } else { + qdev_init_gpio_in(dev, riscv_aplic_request, aplic->num_irqs); + } } /* Create output IRQ lines for non-MSI mode */ @@ -833,7 +876,7 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) /* Claim the CPU interrupt to be triggered by this APLIC */ for (i = 0; i < aplic->num_harts; i++) { - RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(aplic->hartid_base + i)); + RISCVCPU *cpu = RISCV_CPU(cpu_by_arch_id(aplic->hartid_base + i)); if (riscv_cpu_claim_interrupts(cpu, (aplic->mmode) ? MIP_MEIP : MIP_SEIP) < 0) { error_report("%s already claimed", @@ -861,7 +904,7 @@ static const VMStateDescription vmstate_riscv_aplic = { .name = "riscv_aplic", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(domaincfg, RISCVAPLICState), VMSTATE_UINT32(mmsicfgaddr, RISCVAPLICState), VMSTATE_UINT32(mmsicfgaddrH, RISCVAPLICState), @@ -958,7 +1001,10 @@ DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, qdev_prop_set_bit(dev, "mmode", mmode); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); + + if (!is_kvm_aia(msimode)) { + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); + } if (parent) { riscv_aplic_add_child(parent, dev); @@ -966,7 +1012,7 @@ DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, if (!msimode) { for (i = 0; i < num_harts; i++) { - CPUState *cpu = qemu_get_cpu(hartid_base + i); + CPUState *cpu = cpu_by_arch_id(hartid_base + i); qdev_connect_gpio_out_named(dev, NULL, i, qdev_get_gpio_in(DEVICE(cpu), diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c index 8615e4cc1d..b90f0d731d 100644 --- a/hw/intc/riscv_imsic.c +++ b/hw/intc/riscv_imsic.c @@ -32,6 +32,7 @@ #include "target/riscv/cpu.h" #include "target/riscv/cpu_bits.h" #include "sysemu/sysemu.h" +#include "sysemu/kvm.h" #include "migration/vmstate.h" #define IMSIC_MMIO_PAGE_LE 0x00 @@ -283,6 +284,20 @@ static void riscv_imsic_write(void *opaque, hwaddr addr, uint64_t value, goto err; } +#if defined(CONFIG_KVM) + if (kvm_irqchip_in_kernel()) { + struct kvm_msi msi; + + msi.address_lo = extract64(imsic->mmio.addr + addr, 0, 32); + msi.address_hi = extract64(imsic->mmio.addr + addr, 32, 32); + msi.data = le32_to_cpu(value); + + kvm_vm_ioctl(kvm_state, KVM_SIGNAL_MSI, &msi); + + return; + } +#endif + /* Writes only supported for MSI little-endian registers */ page = addr >> IMSIC_MMIO_PAGE_SHIFT; if ((addr & (IMSIC_MMIO_PAGE_SZ - 1)) == IMSIC_MMIO_PAGE_LE) { @@ -316,14 +331,16 @@ static const MemoryRegionOps riscv_imsic_ops = { static void riscv_imsic_realize(DeviceState *dev, Error **errp) { RISCVIMSICState *imsic = RISCV_IMSIC(dev); - RISCVCPU *rcpu = RISCV_CPU(qemu_get_cpu(imsic->hartid)); - CPUState *cpu = qemu_get_cpu(imsic->hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + RISCVCPU *rcpu = RISCV_CPU(cpu_by_arch_id(imsic->hartid)); + CPUState *cpu = cpu_by_arch_id(imsic->hartid); + CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; - imsic->num_eistate = imsic->num_pages * imsic->num_irqs; - imsic->eidelivery = g_new0(uint32_t, imsic->num_pages); - imsic->eithreshold = g_new0(uint32_t, imsic->num_pages); - imsic->eistate = g_new0(uint32_t, imsic->num_eistate); + if (!kvm_irqchip_in_kernel()) { + imsic->num_eistate = imsic->num_pages * imsic->num_irqs; + imsic->eidelivery = g_new0(uint32_t, imsic->num_pages); + imsic->eithreshold = g_new0(uint32_t, imsic->num_pages); + imsic->eistate = g_new0(uint32_t, imsic->num_eistate); + } memory_region_init_io(&imsic->mmio, OBJECT(dev), &riscv_imsic_ops, imsic, TYPE_RISCV_IMSIC, @@ -344,9 +361,11 @@ static void riscv_imsic_realize(DeviceState *dev, Error **errp) /* Force select AIA feature and setup CSR read-modify-write callback */ if (env) { - riscv_set_feature(env, RISCV_FEATURE_AIA); if (!imsic->mmode) { + rcpu->cfg.ext_ssaia = true; riscv_cpu_set_geilen(env, imsic->num_pages - 1); + } else { + rcpu->cfg.ext_smaia = true; } riscv_cpu_set_aia_ireg_rmw_fn(env, (imsic->mmode) ? PRV_M : PRV_S, riscv_imsic_rmw, imsic); @@ -367,7 +386,7 @@ static const VMStateDescription vmstate_riscv_imsic = { .name = "riscv_imsic", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VARRAY_UINT32(eidelivery, RISCVIMSICState, num_pages, 0, vmstate_info_uint32, uint32_t), @@ -411,7 +430,7 @@ DeviceState *riscv_imsic_create(hwaddr addr, uint32_t hartid, bool mmode, uint32_t num_pages, uint32_t num_ids) { DeviceState *dev = qdev_new(TYPE_RISCV_IMSIC); - CPUState *cpu = qemu_get_cpu(hartid); + CPUState *cpu = cpu_by_arch_id(hartid); uint32_t i; assert(!(addr & (IMSIC_MMIO_PAGE_SZ - 1))); diff --git a/hw/intc/rx_icu.c b/hw/intc/rx_icu.c index e5c01807b9..b2d4338f61 100644 --- a/hw/intc/rx_icu.c +++ b/hw/intc/rx_icu.c @@ -345,7 +345,7 @@ static const VMStateDescription vmstate_rxicu = { .name = "rx-icu", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(ir, RXICUState, NR_IRQS), VMSTATE_UINT8_ARRAY(dtcer, RXICUState, NR_IRQS), VMSTATE_UINT8_ARRAY(ier, RXICUState, NR_IRQS / 8), diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 74e02858d4..f4a848460b 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -106,7 +106,7 @@ static int qemu_s390_clear_io_flic(S390FLICState *fs, uint16_t subchannel_id, QEMUS390FlicIO *cur, *next; uint8_t isc; - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); if (!(flic->pending & FLIC_PENDING_IO)) { return 0; } @@ -223,7 +223,7 @@ uint32_t qemu_s390_flic_dequeue_service(QEMUS390FLICState *flic) { uint32_t tmp; - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); g_assert(flic->pending & FLIC_PENDING_SERVICE); tmp = flic->service_param; flic->service_param = 0; @@ -238,7 +238,7 @@ QEMUS390FlicIO *qemu_s390_flic_dequeue_io(QEMUS390FLICState *flic, uint64_t cr6) QEMUS390FlicIO *io; uint8_t isc; - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); if (!(flic->pending & CR6_TO_PENDING_IO(cr6))) { return NULL; } @@ -262,7 +262,7 @@ QEMUS390FlicIO *qemu_s390_flic_dequeue_io(QEMUS390FLICState *flic, uint64_t cr6) void qemu_s390_flic_dequeue_crw_mchk(QEMUS390FLICState *flic) { - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); g_assert(flic->pending & FLIC_PENDING_MCHK_CR); flic->pending &= ~FLIC_PENDING_MCHK_CR; } @@ -271,7 +271,7 @@ static void qemu_s390_inject_service(S390FLICState *fs, uint32_t parm) { QEMUS390FLICState *flic = s390_get_qemu_flic(fs); - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); /* multiplexing is good enough for sclp - kvm does it internally as well */ flic->service_param |= parm; flic->pending |= FLIC_PENDING_SERVICE; @@ -287,7 +287,7 @@ static void qemu_s390_inject_io(S390FLICState *fs, uint16_t subchannel_id, QEMUS390FLICState *flic = s390_get_qemu_flic(fs); QEMUS390FlicIO *io; - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); io = g_new0(QEMUS390FlicIO, 1); io->id = subchannel_id; io->nr = subchannel_nr; @@ -304,7 +304,7 @@ static void qemu_s390_inject_crw_mchk(S390FLICState *fs) { QEMUS390FLICState *flic = s390_get_qemu_flic(fs); - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); flic->pending |= FLIC_PENDING_MCHK_CR; qemu_s390_flic_notify(FLIC_PENDING_MCHK_CR); @@ -330,7 +330,7 @@ bool qemu_s390_flic_has_crw_mchk(QEMUS390FLICState *flic) bool qemu_s390_flic_has_any(QEMUS390FLICState *flic) { - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); return !!flic->pending; } @@ -340,7 +340,7 @@ static void qemu_s390_flic_reset(DeviceState *dev) QEMUS390FlicIO *cur, *next; int isc; - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); flic->simm = 0; flic->nimm = 0; flic->pending = 0; @@ -366,7 +366,7 @@ static const VMStateDescription qemu_s390_flic_vmstate = { .version_id = 1, .minimum_version_id = 1, .needed = ais_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(simm, QEMUS390FLICState), VMSTATE_UINT8(nimm, QEMUS390FLICState), VMSTATE_END_OF_LIST() @@ -465,7 +465,7 @@ const VMStateDescription vmstate_adapter_info_so = { .version_id = 1, .minimum_version_id = 1, .needed = adapter_info_so_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(summary_offset, AdapterInfo), VMSTATE_END_OF_LIST() } @@ -475,7 +475,7 @@ const VMStateDescription vmstate_adapter_info = { .name = "s390_adapter_info", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(ind_offset, AdapterInfo), /* * We do not have to migrate neither the id nor the addresses. @@ -484,7 +484,7 @@ const VMStateDescription vmstate_adapter_info = { */ VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_adapter_info_so, NULL } @@ -495,7 +495,7 @@ const VMStateDescription vmstate_adapter_routes = { .name = "s390_adapter_routes", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(adapter, AdapterRoutes, 1, vmstate_adapter_info, AdapterInfo), VMSTATE_END_OF_LIST() diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index 4e86d2d436..baaa30dcb7 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -380,7 +380,7 @@ static void kvm_s390_release_adapter_routes(S390FLICState *fs, * @size: ignored * * Note: Pass buf and len to kernel. Start with one page and - * increase until buffer is sufficient or maxium size is + * increase until buffer is sufficient or maximum size is * reached */ static int kvm_flic_save(QEMUFile *f, void *opaque, size_t size, @@ -525,7 +525,7 @@ static const VMStateDescription kvm_s390_flic_ais_tmp = { .name = "s390-flic-ais-tmp", .pre_save = kvm_flic_ais_pre_save, .post_load = kvm_flic_ais_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(simm, KVMS390FLICStateMigTmp), VMSTATE_UINT8(nimm, KVMS390FLICStateMigTmp), VMSTATE_END_OF_LIST() @@ -537,7 +537,7 @@ static const VMStateDescription kvm_s390_flic_vmstate_ais = { .version_id = 1, .minimum_version_id = 1, .needed = ais_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_WITH_TMP(KVMS390FLICState, KVMS390FLICStateMigTmp, kvm_s390_flic_ais_tmp), VMSTATE_END_OF_LIST() @@ -550,7 +550,7 @@ static const VMStateDescription kvm_s390_flic_vmstate = { .name = "s390-flic", .version_id = FLIC_SAVEVM_VERSION, .minimum_version_id = FLIC_SAVEVM_VERSION, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { { .name = "irqs", .info = &(const VMStateInfo) { @@ -562,7 +562,7 @@ static const VMStateDescription kvm_s390_flic_vmstate = { }, VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &kvm_s390_flic_vmstate_ais, NULL } @@ -646,9 +646,10 @@ static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); + KVMS390FLICStateClass *kfsc = KVM_S390_FLIC_CLASS(oc); - KVM_S390_FLIC_CLASS(oc)->parent_realize = dc->realize; - dc->realize = kvm_s390_flic_realize; + device_class_set_parent_realize(dc, kvm_s390_flic_realize, + &kfsc->parent_realize); dc->vmsd = &kvm_s390_flic_vmstate; dc->reset = kvm_s390_flic_reset; fsc->register_io_adapter = kvm_s390_register_io_adapter; diff --git a/hw/intc/sifive_plic.c b/hw/intc/sifive_plic.c index eebbcf33d4..e559f11805 100644 --- a/hw/intc/sifive_plic.c +++ b/hw/intc/sifive_plic.c @@ -42,7 +42,6 @@ static PLICMode char_to_mode(char c) switch (c) { case 'U': return PLICMode_U; case 'S': return PLICMode_S; - case 'H': return PLICMode_H; case 'M': return PLICMode_M; default: error_report("plic: invalid mode '%c'", c); @@ -78,6 +77,7 @@ static uint32_t sifive_plic_claimed(SiFivePLICState *plic, uint32_t addrid) uint32_t max_irq = 0; uint32_t max_prio = plic->target_priority[addrid]; int i, j; + int num_irq_in_word = 32; for (i = 0; i < plic->bitfield_words; i++) { uint32_t pending_enabled_not_claimed = @@ -88,7 +88,16 @@ static uint32_t sifive_plic_claimed(SiFivePLICState *plic, uint32_t addrid) continue; } - for (j = 0; j < 32; j++) { + if (i == (plic->bitfield_words - 1)) { + /* + * If plic->num_sources is not multiple of 32, num-of-irq in last + * word is not 32. Compute the num-of-irq of last word to avoid + * out-of-bound access of source_priority array. + */ + num_irq_in_word = plic->num_sources - ((plic->bitfield_words - 1) << 5); + } + + for (j = 0; j < num_irq_in_word; j++) { int irq = (i << 5) + j; uint32_t prio = plic->source_priority[irq]; int enabled = pending_enabled_not_claimed & (1 << j); @@ -131,10 +140,11 @@ static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size) SiFivePLICState *plic = opaque; if (addr_between(addr, plic->priority_base, plic->num_sources << 2)) { - uint32_t irq = ((addr - plic->priority_base) >> 2) + 1; + uint32_t irq = (addr - plic->priority_base) >> 2; return plic->source_priority[irq]; - } else if (addr_between(addr, plic->pending_base, plic->num_sources >> 3)) { + } else if (addr_between(addr, plic->pending_base, + (plic->num_sources + 31) >> 3)) { uint32_t word = (addr - plic->pending_base) >> 2; return plic->pending[word]; @@ -178,12 +188,22 @@ static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value, SiFivePLICState *plic = opaque; if (addr_between(addr, plic->priority_base, plic->num_sources << 2)) { - uint32_t irq = ((addr - plic->priority_base) >> 2) + 1; + uint32_t irq = (addr - plic->priority_base) >> 2; - plic->source_priority[irq] = value & 7; - sifive_plic_update(plic); + if (((plic->num_priorities + 1) & plic->num_priorities) == 0) { + /* + * if "num_priorities + 1" is power-of-2, make each register bit of + * interrupt priority WARL (Write-Any-Read-Legal). Just filter + * out the access to unsupported priority bits. + */ + plic->source_priority[irq] = value % (plic->num_priorities + 1); + sifive_plic_update(plic); + } else if (value <= plic->num_priorities) { + plic->source_priority[irq] = value; + sifive_plic_update(plic); + } } else if (addr_between(addr, plic->pending_base, - plic->num_sources >> 3)) { + (plic->num_sources + 31) >> 3)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid pending write: 0x%" HWADDR_PRIx "", __func__, addr); @@ -205,7 +225,16 @@ static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value, uint32_t contextid = (addr & (plic->context_stride - 1)); if (contextid == 0) { - if (value <= plic->num_priorities) { + if (((plic->num_priorities + 1) & plic->num_priorities) == 0) { + /* + * if "num_priorities + 1" is power-of-2, each register bit of + * interrupt priority is WARL (Write-Any-Read-Legal). Just + * filter out the access to unsupported priority bits. + */ + plic->target_priority[addrid] = value % + (plic->num_priorities + 1); + sifive_plic_update(plic); + } else if (value <= plic->num_priorities) { plic->target_priority[addrid] = value; sifive_plic_update(plic); } @@ -262,7 +291,7 @@ static void sifive_plic_reset(DeviceState *dev) */ static void parse_hart_config(SiFivePLICState *plic) { - int addrid, hartid, modes; + int addrid, hartid, modes, m; const char *p; char c; @@ -271,11 +300,13 @@ static void parse_hart_config(SiFivePLICState *plic) p = plic->hart_config; while ((c = *p++)) { if (c == ',') { - addrid += ctpop8(modes); - modes = 0; - hartid++; + if (modes) { + addrid += ctpop8(modes); + hartid++; + modes = 0; + } } else { - int m = 1 << char_to_mode(c); + m = 1 << char_to_mode(c); if (modes == (modes | m)) { error_report("plic: duplicate mode '%c' in config: %s", c, plic->hart_config); @@ -286,8 +317,9 @@ static void parse_hart_config(SiFivePLICState *plic) } if (modes) { addrid += ctpop8(modes); + hartid++; + modes = 0; } - hartid++; plic->num_addrs = addrid; plic->num_harts = hartid; @@ -298,11 +330,16 @@ static void parse_hart_config(SiFivePLICState *plic) p = plic->hart_config; while ((c = *p++)) { if (c == ',') { - hartid++; + if (modes) { + hartid++; + modes = 0; + } } else { + m = char_to_mode(c); plic->addr_config[addrid].addrid = addrid; plic->addr_config[addrid].hartid = hartid; - plic->addr_config[addrid].mode = char_to_mode(c); + plic->addr_config[addrid].mode = m; + modes |= (1 << m); addrid++; } } @@ -327,6 +364,11 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp) parse_hart_config(s); + if (!s->num_sources) { + error_setg(errp, "plic: invalid number of interrupt sources"); + return; + } + s->bitfield_words = (s->num_sources + 31) >> 5; s->num_enables = s->bitfield_words * s->num_addrs; s->source_priority = g_new0(uint32_t, s->num_sources); @@ -343,7 +385,8 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp) s->m_external_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts); qdev_init_gpio_out(dev, s->m_external_irqs, s->num_harts); - /* We can't allow the supervisor to control SEIP as this would allow the + /* + * We can't allow the supervisor to control SEIP as this would allow the * supervisor to clear a pending external interrupt which will result in * lost a interrupt in the case a PLIC is attached. The SEIP bit must be * hardware controlled when a PLIC is attached. @@ -351,8 +394,8 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp) for (i = 0; i < s->num_harts; i++) { RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(s->hartid_base + i)); if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) { - error_report("SEIP already claimed"); - exit(1); + error_setg(errp, "SEIP already claimed"); + return; } } @@ -363,7 +406,7 @@ static const VMStateDescription vmstate_sifive_plic = { .name = "riscv_sifive_plic", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VARRAY_UINT32(source_priority, SiFivePLICState, num_sources, 0, vmstate_info_uint32, uint32_t), @@ -383,8 +426,10 @@ static const VMStateDescription vmstate_sifive_plic = { static Property sifive_plic_properties[] = { DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config), DEFINE_PROP_UINT32("hartid-base", SiFivePLICState, hartid_base, 0), - DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0), + /* number of interrupt sources including interrupt source 0 */ + DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 1), DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0), + /* interrupt priority register base starting from source 0 */ DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0), DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0), DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0), @@ -431,7 +476,7 @@ DeviceState *sifive_plic_create(hwaddr addr, char *hart_config, uint32_t context_stride, uint32_t aperture_size) { DeviceState *dev = qdev_new(TYPE_SIFIVE_PLIC); - int i, j = 0; + int i; SiFivePLICState *plic; assert(enable_stride == (enable_stride & -enable_stride)); @@ -451,18 +496,17 @@ DeviceState *sifive_plic_create(hwaddr addr, char *hart_config, sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); plic = SIFIVE_PLIC(dev); - for (i = 0; i < num_harts; i++) { - CPUState *cpu = qemu_get_cpu(hartid_base + i); - if (plic->addr_config[j].mode == PLICMode_M) { - j++; - qdev_connect_gpio_out(dev, num_harts + i, + for (i = 0; i < plic->num_addrs; i++) { + int cpu_num = plic->addr_config[i].hartid; + CPUState *cpu = qemu_get_cpu(cpu_num); + + if (plic->addr_config[i].mode == PLICMode_M) { + qdev_connect_gpio_out(dev, cpu_num - hartid_base + num_harts, qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT)); } - - if (plic->addr_config[j].mode == PLICMode_S) { - j++; - qdev_connect_gpio_out(dev, i, + if (plic->addr_config[i].mode == PLICMode_S) { + qdev_connect_gpio_out(dev, cpu_num - hartid_base, qdev_get_gpio_in(DEVICE(cpu), IRQ_S_EXT)); } } diff --git a/hw/intc/slavio_intctl.c b/hw/intc/slavio_intctl.c index f7e59ba643..36b4a12f60 100644 --- a/hw/intc/slavio_intctl.c +++ b/hw/intc/slavio_intctl.c @@ -353,7 +353,7 @@ static const VMStateDescription vmstate_intctl_cpu = { .name ="slavio_intctl_cpu", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(intreg_pending, SLAVIO_CPUINTCTLState), VMSTATE_END_OF_LIST() } @@ -364,7 +364,7 @@ static const VMStateDescription vmstate_intctl = { .version_id = 1, .minimum_version_id = 1, .post_load = vmstate_intctl_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(slaves, SLAVIO_INTCTLState, MAX_CPUS, 1, vmstate_intctl_cpu, SLAVIO_CPUINTCTLState), VMSTATE_UINT32(intregm_pending, SLAVIO_INTCTLState), diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index dc641cc604..d7e56bfb20 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -27,7 +27,7 @@ #include "trace.h" /* - * XIVE Virtualization Controller BAR and Thread Managment BAR that we + * XIVE Virtualization Controller BAR and Thread Management BAR that we * use for the ESB pages and the TIMA pages */ #define SPAPR_XIVE_VC_BASE 0x0006010000000000ull @@ -316,7 +316,6 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp) if (!qdev_realize(DEVICE(xsrc), NULL, errp)) { return; } - sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xsrc->esb_mmio); /* * Initialize the END ESB source @@ -328,7 +327,6 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp) if (!qdev_realize(DEVICE(end_xsrc), NULL, errp)) { return; } - sysbus_init_mmio(SYS_BUS_DEVICE(xive), &end_xsrc->esb_mmio); /* Set the mapping address of the END ESB pages after the source ESBs */ xive->end_base = xive->vc_base + xive_source_esb_len(xsrc); @@ -347,15 +345,17 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp) /* TIMA initialization */ 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); /* * Map all regions. These will be enabled or disabled at reset and * can also be overridden by KVM memory regions if active */ - sysbus_mmio_map(SYS_BUS_DEVICE(xive), 0, xive->vc_base); - sysbus_mmio_map(SYS_BUS_DEVICE(xive), 1, xive->end_base); - sysbus_mmio_map(SYS_BUS_DEVICE(xive), 2, xive->tm_base); + memory_region_add_subregion(get_system_memory(), xive->vc_base, + &xsrc->esb_mmio); + memory_region_add_subregion(get_system_memory(), xive->end_base, + &end_xsrc->esb_mmio); + memory_region_add_subregion(get_system_memory(), xive->tm_base, + &xive->tm_mmio); } static int spapr_xive_get_eas(XiveRouter *xrtr, uint8_t eas_blk, @@ -475,6 +475,21 @@ static int spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format, return count; } +static uint32_t spapr_xive_presenter_get_config(XivePresenter *xptr) +{ + uint32_t cfg = 0; + + /* + * Let's claim GEN1 TIMA format. If running with KVM on P10, the + * correct answer is deep in the hardware and not accessible to + * us. But it shouldn't matter as it only affects the presenter + * as seen by a guest OS. + */ + cfg |= XIVE_PRESENTER_GEN1_TIMA_OS; + + return cfg; +} + static uint8_t spapr_xive_get_block_id(XiveRouter *xrtr) { return SPAPR_XIVE_BLOCK_ID; @@ -507,7 +522,7 @@ static const VMStateDescription vmstate_spapr_xive_end = { .name = TYPE_SPAPR_XIVE "/end", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField []) { + .fields = (const VMStateField []) { VMSTATE_UINT32(w0, XiveEND), VMSTATE_UINT32(w1, XiveEND), VMSTATE_UINT32(w2, XiveEND), @@ -524,7 +539,7 @@ static const VMStateDescription vmstate_spapr_xive_eas = { .name = TYPE_SPAPR_XIVE "/eas", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField []) { + .fields = (const VMStateField []) { VMSTATE_UINT64(w, XiveEAS), VMSTATE_END_OF_LIST() }, @@ -562,7 +577,7 @@ static const VMStateDescription vmstate_spapr_xive = { .minimum_version_id = 1, .pre_save = vmstate_spapr_xive_pre_save, .post_load = NULL, /* handled at the machine level */ - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_EQUAL(nr_irqs, SpaprXive, NULL), VMSTATE_STRUCT_VARRAY_POINTER_UINT32(eat, SpaprXive, nr_irqs, vmstate_spapr_xive_eas, XiveEAS), @@ -832,6 +847,7 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data) sicc->post_load = spapr_xive_post_load; xpc->match_nvt = spapr_xive_match_nvt; + xpc->get_config = spapr_xive_presenter_get_config; xpc->in_kernel = spapr_xive_in_kernel_xptr; } diff --git a/hw/intc/spapr_xive_kvm.c b/hw/intc/spapr_xive_kvm.c index 61fe7bd2d3..5789062379 100644 --- a/hw/intc/spapr_xive_kvm.c +++ b/hw/intc/spapr_xive_kvm.c @@ -485,7 +485,7 @@ static int kvmppc_xive_get_queues(SpaprXive *xive, Error **errp) * * Whenever the VM is stopped, the VM change handler sets the source * PQs to PENDING to stop the flow of events and to possibly catch a - * triggered interrupt occuring while the VM is stopped. The previous + * triggered interrupt occurring while the VM is stopped. The previous * state is saved in anticipation of a migration. The XIVE controller * is then synced through KVM to flush any in-flight event * notification and stabilize the EQs. @@ -551,7 +551,7 @@ static void kvmppc_xive_change_state_handler(void *opaque, bool running, /* * PQ is set to PENDING to possibly catch a triggered - * interrupt occuring while the VM is stopped (hotplug event + * interrupt occurring while the VM is stopped (hotplug event * for instance) . */ if (pq != XIVE_ESB_OFF) { @@ -633,7 +633,7 @@ int kvmppc_xive_post_load(SpaprXive *xive, int version_id) /* The KVM XIVE device should be in use */ assert(xive->fd != -1); - /* Restore the ENDT first. The targetting depends on it. */ + /* Restore the ENDT first. The targeting depends on it. */ for (i = 0; i < xive->nr_ends; i++) { if (!xive_end_is_valid(&xive->endt[i])) { continue; diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 0a90c1cdec..1ef29d0256 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -10,16 +10,12 @@ pic_ioport_read(bool master, uint64_t addr, int val) "master %d addr 0x%"PRIx64" # apic_common.c cpu_set_apic_base(uint64_t val) "0x%016"PRIx64 cpu_get_apic_base(uint64_t val) "0x%016"PRIx64 -# coalescing -apic_report_irq_delivered(int apic_irq_delivered) "coalescing %d" -apic_reset_irq_delivered(int apic_irq_delivered) "old coalescing %d" -apic_get_irq_delivered(int apic_irq_delivered) "returning coalescing %d" # apic.c apic_local_deliver(int vector, uint32_t lvt) "vector %d delivery mode %d" apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t vector_num, uint8_t trigger_mode) "dest %d dest_mode %d delivery_mode %d vector %d trigger_mode %d" -apic_mem_readl(uint64_t addr, uint32_t val) "0x%"PRIx64" = 0x%08x" -apic_mem_writel(uint64_t addr, uint32_t val) "0x%"PRIx64" = 0x%08x" +apic_register_read(uint8_t reg, uint64_t val) "register 0x%02x = 0x%"PRIx64 +apic_register_write(uint8_t reg, uint64_t val) "register 0x%02x = 0x%"PRIx64 # ioapic.c ioapic_set_remote_irr(int n) "set remote irr for pin %d" @@ -30,6 +26,11 @@ ioapic_mem_read(uint8_t addr, uint8_t regsel, uint8_t size, uint32_t val) "ioapi ioapic_mem_write(uint8_t addr, uint8_t regsel, uint8_t size, uint32_t val) "ioapic mem write addr 0x%"PRIx8" regsel: 0x%"PRIx8" size 0x%"PRIx8" val 0x%"PRIx32 ioapic_set_irq(int vector, int level) "vector: %d level: %d" +# kvm_irqcount.c +kvm_report_irq_delivered(int irq_delivered) "coalescing %d" +kvm_reset_irq_delivered(int irq_delivered) "old coalescing %d" +kvm_get_irq_delivered(int irq_delivered) "returning coalescing %d" + # slavio_intctl.c slavio_intctl_mem_readl(uint32_t cpu, uint64_t addr, uint32_t ret) "read cpu %d reg 0x%"PRIx64" = 0x%x" slavio_intctl_mem_writel(uint32_t cpu, uint64_t addr, uint32_t val) "write cpu %d reg 0x%"PRIx64" = 0x%x" @@ -264,8 +265,8 @@ xive_source_esb_read(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64 xive_source_esb_write(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64" IRQ 0x%x val=0x%"PRIx64 xive_router_end_notify(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "END 0x%02x/0x%04x -> enqueue 0x%08x" xive_router_end_escalate(uint8_t end_blk, uint32_t end_idx, uint8_t esc_blk, uint32_t esc_idx, uint32_t end_data) "END 0x%02x/0x%04x -> escalate END 0x%02x/0x%04x data 0x%08x" -xive_tctx_tm_write(uint64_t offset, unsigned int size, uint64_t value) "@0x%"PRIx64" sz=%d val=0x%" PRIx64 -xive_tctx_tm_read(uint64_t offset, unsigned int size, uint64_t value) "@0x%"PRIx64" sz=%d val=0x%" PRIx64 +xive_tctx_tm_write(uint32_t index, uint64_t offset, unsigned int size, uint64_t value) "target=%d @0x%"PRIx64" sz=%d val=0x%" PRIx64 +xive_tctx_tm_read(uint32_t index, uint64_t offset, unsigned int size, uint64_t value) "target=%d @0x%"PRIx64" sz=%d val=0x%" PRIx64 xive_presenter_notify(uint8_t nvt_blk, uint32_t nvt_idx, uint8_t ring) "found NVT 0x%x/0x%x ring=0x%x" xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x/0x%x @0x%"PRIx64 @@ -291,6 +292,7 @@ sh_intc_set(int id, int enable) "setting interrupt group %d to %d" # loongarch_ipi.c loongarch_ipi_read(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64 loongarch_ipi_write(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64 +loongarch_ipi_unsupported_cpuid(const char *s, uint32_t cpuid) "%s unsupported cpuid 0x%" PRIx32 # loongarch_pch_pic.c loongarch_pch_pic_irq_handler(int irq, int level) "irq %d level %d" @@ -306,6 +308,5 @@ loongarch_msi_set_irq(int irq_num) "set msi irq %d" # loongarch_extioi.c loongarch_extioi_setirq(int irq, int level) "set extirq irq %d level %d" -loongarch_extioi_readw(uint64_t addr, uint32_t val) "addr: 0x%"PRIx64 "val: 0x%x" +loongarch_extioi_readw(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64 "val: 0x%" PRIx64 loongarch_extioi_writew(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64 "val: 0x%" PRIx64 - diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 24e67020db..700abfa7a6 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -40,6 +40,7 @@ #include "hw/irq.h" #include "sysemu/kvm.h" #include "sysemu/reset.h" +#include "target/ppc/cpu.h" void icp_pic_print_info(ICPState *icp, Monitor *mon) { @@ -273,7 +274,7 @@ static const VMStateDescription vmstate_icp_server = { .minimum_version_id = 1, .pre_save = icp_pre_save, .post_load = icp_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { /* Sanity check */ VMSTATE_UINT32(xirr, ICPState), VMSTATE_UINT8(pending_priority, ICPState), @@ -301,23 +302,25 @@ void icp_reset(ICPState *icp) static void icp_realize(DeviceState *dev, Error **errp) { ICPState *icp = ICP(dev); + PowerPCCPU *cpu; CPUPPCState *env; Error *err = NULL; assert(icp->xics); assert(icp->cs); - env = &POWERPC_CPU(icp->cs)->env; + cpu = POWERPC_CPU(icp->cs); + env = &cpu->env; switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_POWER7: - icp->output = env->irq_inputs[POWER7_INPUT_INT]; + icp->output = qdev_get_gpio_in(DEVICE(cpu), POWER7_INPUT_INT); break; case PPC_FLAGS_INPUT_POWER9: /* For SPAPR xics emulation */ - icp->output = env->irq_inputs[POWER9_INPUT_INT]; + icp->output = qdev_get_gpio_in(DEVICE(cpu), POWER9_INPUT_INT); break; case PPC_FLAGS_INPUT_970: - icp->output = env->irq_inputs[PPC970_INPUT_INT]; + icp->output = qdev_get_gpio_in(DEVICE(cpu), PPC970_INPUT_INT); break; default: @@ -333,8 +336,22 @@ static void icp_realize(DeviceState *dev, Error **errp) return; } } - - vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp); + /* + * The way that pre_2_10_icp is handling is really, really hacky. + * We used to have here this call: + * + * vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp); + * + * But we were doing: + * pre_2_10_vmstate_register_dummy_icp() + * this vmstate_register() + * pre_2_10_vmstate_unregister_dummy_icp() + * + * So for a short amount of time we had to vmstate entries with + * the same name. This fixes it. + */ + vmstate_replace_hack_for_ppc(NULL, icp->cs->cpu_index, + &vmstate_icp_server, icp); } static void icp_unrealize(DeviceState *dev) @@ -562,11 +579,11 @@ static void ics_reset_irq(ICSIRQState *irq) irq->saved_priority = 0xff; } -static void ics_reset(DeviceState *dev) +static void ics_reset_hold(Object *obj) { - ICSState *ics = ICS(dev); + ICSState *ics = ICS(obj); + g_autofree uint8_t *flags = g_malloc(ics->nr_irqs); int i; - uint8_t flags[ics->nr_irqs]; for (i = 0; i < ics->nr_irqs; i++) { flags[i] = ics->irqs[i].flags; @@ -582,7 +599,7 @@ static void ics_reset(DeviceState *dev) if (kvm_irqchip_in_kernel()) { Error *local_err = NULL; - ics_set_kvm_state(ICS(dev), &local_err); + ics_set_kvm_state(ics, &local_err); if (local_err) { error_report_err(local_err); } @@ -591,7 +608,7 @@ static void ics_reset(DeviceState *dev) static void ics_reset_handler(void *dev) { - ics_reset(dev); + device_cold_reset(dev); } static void ics_realize(DeviceState *dev, Error **errp) @@ -649,7 +666,7 @@ static const VMStateDescription vmstate_ics_irq = { .name = "ics/irq", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(server, ICSIRQState), VMSTATE_UINT8(priority, ICSIRQState), VMSTATE_UINT8(saved_priority, ICSIRQState), @@ -665,7 +682,7 @@ static const VMStateDescription vmstate_ics = { .minimum_version_id = 1, .pre_save = ics_pre_save, .post_load = ics_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { /* Sanity check */ VMSTATE_UINT32_EQUAL(nr_irqs, ICSState, NULL), @@ -686,16 +703,17 @@ static Property ics_properties[] = { static void ics_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); dc->realize = ics_realize; device_class_set_props(dc, ics_properties); - dc->reset = ics_reset; dc->vmsd = &vmstate_ics; /* * Reason: part of XICS interrupt controller, needs to be wired up, * e.g. by spapr_irq_init(). */ dc->user_creatable = false; + rc->phases.hold = ics_reset_hold; } static const TypeInfo ics_info = { diff --git a/hw/intc/xilinx_intc.c b/hw/intc/xilinx_intc.c index 4c4397b3d2..6e5012e66e 100644 --- a/hw/intc/xilinx_intc.c +++ b/hw/intc/xilinx_intc.c @@ -42,10 +42,10 @@ #define R_MAX 8 #define TYPE_XILINX_INTC "xlnx.xps-intc" -DECLARE_INSTANCE_CHECKER(struct xlx_pic, XILINX_INTC, - TYPE_XILINX_INTC) +typedef struct XpsIntc XpsIntc; +DECLARE_INSTANCE_CHECKER(XpsIntc, XILINX_INTC, TYPE_XILINX_INTC) -struct xlx_pic +struct XpsIntc { SysBusDevice parent_obj; @@ -62,7 +62,7 @@ struct xlx_pic uint32_t irq_pin_state; }; -static void update_irq(struct xlx_pic *p) +static void update_irq(XpsIntc *p) { uint32_t i; @@ -87,10 +87,9 @@ static void update_irq(struct xlx_pic *p) qemu_set_irq(p->parent_irq, (p->regs[R_MER] & 1) && p->regs[R_IPR]); } -static uint64_t -pic_read(void *opaque, hwaddr addr, unsigned int size) +static uint64_t pic_read(void *opaque, hwaddr addr, unsigned int size) { - struct xlx_pic *p = opaque; + XpsIntc *p = opaque; uint32_t r = 0; addr >>= 2; @@ -106,11 +105,10 @@ pic_read(void *opaque, hwaddr addr, unsigned int size) return r; } -static void -pic_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) +static void pic_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) { - struct xlx_pic *p = opaque; + XpsIntc *p = opaque; uint32_t value = val64; addr >>= 2; @@ -154,7 +152,7 @@ static const MemoryRegionOps pic_ops = { static void irq_handler(void *opaque, int irq, int level) { - struct xlx_pic *p = opaque; + XpsIntc *p = opaque; /* edge triggered interrupt */ if (p->c_kind_of_intr & (1 << irq) && p->regs[R_MER] & 2) { @@ -168,7 +166,7 @@ static void irq_handler(void *opaque, int irq, int level) static void xilinx_intc_init(Object *obj) { - struct xlx_pic *p = XILINX_INTC(obj); + XpsIntc *p = XILINX_INTC(obj); qdev_init_gpio_in(DEVICE(obj), irq_handler, 32); sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq); @@ -179,7 +177,7 @@ static void xilinx_intc_init(Object *obj) } static Property xilinx_intc_properties[] = { - DEFINE_PROP_UINT32("kind-of-intr", struct xlx_pic, c_kind_of_intr, 0), + DEFINE_PROP_UINT32("kind-of-intr", XpsIntc, c_kind_of_intr, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -193,7 +191,7 @@ static void xilinx_intc_class_init(ObjectClass *klass, void *data) static const TypeInfo xilinx_intc_info = { .name = TYPE_XILINX_INTC, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct xlx_pic), + .instance_size = sizeof(XpsIntc), .instance_init = xilinx_intc_init, .class_init = xilinx_intc_class_init, }; diff --git a/hw/intc/xive.c b/hw/intc/xive.c index ae221fed73..057b308ae9 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -20,6 +20,7 @@ #include "monitor/monitor.h" #include "hw/irq.h" #include "hw/ppc/xive.h" +#include "hw/ppc/xive2.h" #include "hw/ppc/xive_regs.h" #include "trace.h" @@ -249,7 +250,7 @@ static const uint8_t *xive_tm_views[] = { static uint64_t xive_tm_mask(hwaddr offset, unsigned size, bool write) { uint8_t page_offset = (offset >> TM_SHIFT) & 0x3; - uint8_t reg_offset = offset & 0x3F; + uint8_t reg_offset = offset & TM_REG_OFFSET; uint8_t reg_mask = write ? 0x1 : 0x2; uint64_t mask = 0x0; int i; @@ -266,8 +267,8 @@ static uint64_t xive_tm_mask(hwaddr offset, unsigned size, bool write) static void xive_tm_raw_write(XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size) { - uint8_t ring_offset = offset & 0x30; - uint8_t reg_offset = offset & 0x3F; + uint8_t ring_offset = offset & TM_RING_OFFSET; + uint8_t reg_offset = offset & TM_REG_OFFSET; uint64_t mask = xive_tm_mask(offset, size, true); int i; @@ -296,8 +297,8 @@ static void xive_tm_raw_write(XiveTCTX *tctx, hwaddr offset, uint64_t value, static uint64_t xive_tm_raw_read(XiveTCTX *tctx, hwaddr offset, unsigned size) { - uint8_t ring_offset = offset & 0x30; - uint8_t reg_offset = offset & 0x3F; + uint8_t ring_offset = offset & TM_RING_OFFSET; + uint8_t reg_offset = offset & TM_REG_OFFSET; uint64_t mask = xive_tm_mask(offset, size, false); uint64_t ret; int i; @@ -461,6 +462,13 @@ static void xive_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, } } +static uint32_t xive_presenter_get_config(XivePresenter *xptr) +{ + XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); + + return xpc->get_config(xptr); +} + /* * Define a mapping of "special" operations depending on the TIMA page * offset and the size of the operation. @@ -497,14 +505,47 @@ static const XiveTmOp xive_tm_operations[] = { { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, NULL, xive_tm_pull_pool_ctx }, }; -static const XiveTmOp *xive_tm_find_op(hwaddr offset, unsigned size, bool write) +static const XiveTmOp xive2_tm_operations[] = { + /* + * MMIOs below 2K : raw values and special operations without side + * 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, xive2_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 }, + + /* MMIOs above 2K : special operations with side effects */ + { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, NULL, xive_tm_ack_os_reg }, + { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, xive_tm_set_os_pending, NULL }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, NULL, xive2_tm_pull_os_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, NULL, xive2_tm_pull_os_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, NULL, xive_tm_ack_hv_reg }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, NULL, xive_tm_pull_pool_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, NULL, xive_tm_pull_pool_ctx }, +}; + +static const XiveTmOp *xive_tm_find_op(XivePresenter *xptr, hwaddr offset, + unsigned size, bool write) { uint8_t page_offset = (offset >> TM_SHIFT) & 0x3; - uint32_t op_offset = offset & 0xFFF; - int i; + uint32_t op_offset = offset & TM_ADDRESS_MASK; + const XiveTmOp *tm_ops; + int i, tm_ops_count; + uint32_t cfg; - for (i = 0; i < ARRAY_SIZE(xive_tm_operations); i++) { - const XiveTmOp *xto = &xive_tm_operations[i]; + cfg = xive_presenter_get_config(xptr); + if (cfg & XIVE_PRESENTER_GEN1_TIMA_OS) { + tm_ops = xive_tm_operations; + tm_ops_count = ARRAY_SIZE(xive_tm_operations); + } else { + tm_ops = xive2_tm_operations; + tm_ops_count = ARRAY_SIZE(xive2_tm_operations); + } + + for (i = 0; i < tm_ops_count; i++) { + const XiveTmOp *xto = &tm_ops[i]; /* Accesses done from a more privileged TIMA page is allowed */ if (xto->page_offset >= page_offset && @@ -525,7 +566,7 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, { const XiveTmOp *xto; - trace_xive_tctx_tm_write(offset, size, value); + trace_xive_tctx_tm_write(tctx->cs->cpu_index, offset, size, value); /* * TODO: check V bit in Q[0-3]W2 @@ -534,8 +575,8 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, /* * First, check for special operations in the 2K region */ - if (offset & 0x800) { - xto = xive_tm_find_op(offset, size, true); + if (offset & TM_SPECIAL_OP) { + xto = xive_tm_find_op(tctx->xptr, offset, size, true); if (!xto) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA " "@%"HWADDR_PRIx"\n", offset); @@ -548,7 +589,7 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, /* * Then, for special operations in the region below 2K. */ - xto = xive_tm_find_op(offset, size, true); + xto = xive_tm_find_op(tctx->xptr, offset, size, true); if (xto) { xto->write_handler(xptr, tctx, offset, value, size); return; @@ -573,8 +614,8 @@ uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, /* * First, check for special operations in the 2K region */ - if (offset & 0x800) { - xto = xive_tm_find_op(offset, size, false); + if (offset & TM_SPECIAL_OP) { + xto = xive_tm_find_op(tctx->xptr, offset, size, false); if (!xto) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid read access to TIMA" "@%"HWADDR_PRIx"\n", offset); @@ -587,7 +628,7 @@ uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, /* * Then, for special operations in the region below 2K. */ - xto = xive_tm_find_op(offset, size, false); + xto = xive_tm_find_op(tctx->xptr, offset, size, false); if (xto) { ret = xto->read_handler(xptr, tctx, offset, size); goto out; @@ -598,7 +639,7 @@ uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, */ ret = xive_tm_raw_read(tctx, offset, size); out: - trace_xive_tctx_tm_read(offset, size, ret); + trace_xive_tctx_tm_read(tctx->cs->cpu_index, offset, size, ret); return ret; } @@ -695,8 +736,8 @@ static void xive_tctx_realize(DeviceState *dev, Error **errp) env = &cpu->env; switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_POWER9: - tctx->hv_output = env->irq_inputs[POWER9_INPUT_HINT]; - tctx->os_output = env->irq_inputs[POWER9_INPUT_INT]; + tctx->hv_output = qdev_get_gpio_in(DEVICE(cpu), POWER9_INPUT_HINT); + tctx->os_output = qdev_get_gpio_in(DEVICE(cpu), POWER9_INPUT_INT); break; default: @@ -757,7 +798,7 @@ static const VMStateDescription vmstate_xive_tctx = { .minimum_version_id = 1, .pre_save = vmstate_xive_tctx_pre_save, .post_load = vmstate_xive_tctx_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BUFFER(regs, XiveTCTX), VMSTATE_END_OF_LIST() }, @@ -1134,11 +1175,11 @@ static const MemoryRegionOps xive_source_esb_ops = { .write = xive_source_esb_write, .endianness = DEVICE_BIG_ENDIAN, .valid = { - .min_access_size = 8, + .min_access_size = 1, .max_access_size = 8, }, .impl = { - .min_access_size = 8, + .min_access_size = 1, .max_access_size = 8, }, }; @@ -1191,8 +1232,7 @@ static void xive_source_reset(void *dev) /* Do not clear the LSI bitmap */ - /* PQs are initialized to 0b01 (Q=1) which corresponds to "ints off" */ - memset(xsrc->status, XIVE_ESB_OFF, xsrc->nr_irqs); + memset(xsrc->status, xsrc->reset_pq, xsrc->nr_irqs); } static void xive_source_realize(DeviceState *dev, Error **errp) @@ -1231,7 +1271,7 @@ static const VMStateDescription vmstate_xive_source = { .name = TYPE_XIVE_SOURCE, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_EQUAL(nr_irqs, XiveSource, NULL), VMSTATE_VBUFFER_UINT32(status, XiveSource, 1, NULL, nr_irqs), VMSTATE_END_OF_LIST() @@ -1246,6 +1286,11 @@ 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), + /* + * By default, PQs are initialized to 0b01 (Q=1) which corresponds + * to "ints off" + */ + DEFINE_PROP_UINT8("reset-pq", XiveSource, reset_pq, XIVE_ESB_OFF), DEFINE_PROP_LINK("xive", XiveSource, xive, TYPE_XIVE_NOTIFIER, XiveNotifier *), DEFINE_PROP_END_OF_LIST(), @@ -1473,6 +1518,13 @@ static void xive_router_realize(DeviceState *dev, Error **errp) assert(xrtr->xfb); } +static void xive_router_end_notify_handler(XiveRouter *xrtr, XiveEAS *eas) +{ + XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr); + + return xrc->end_notify(xrtr, eas); +} + /* * Encode the HW CAM line in the block group mode format : * @@ -1556,7 +1608,7 @@ int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, * * It receives notification requests sent by the IVRE to find one * matching NVT (or more) dispatched on the processor threads. In case - * of a single NVT notification, the process is abreviated and the + * of a single NVT notification, the process is abbreviated and the * thread is signaled if a match is found. In case of a logical server * notification (bits ignored at the end of the NVT identifier), the * IVPE and IVRE select a winning thread using different filters. This @@ -1619,8 +1671,7 @@ static bool xive_router_end_es_notify(XiveRouter *xrtr, uint8_t end_blk, * another chip. We don't model the PowerBus but the END trigger * message has the same parameters than in the function below. */ -static void xive_router_end_notify(XiveRouter *xrtr, uint8_t end_blk, - uint32_t end_idx, uint32_t end_data) +void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas) { XiveEND end; uint8_t priority; @@ -1630,6 +1681,10 @@ static void xive_router_end_notify(XiveRouter *xrtr, uint8_t end_blk, XiveNVT nvt; bool found; + uint8_t end_blk = xive_get_field64(EAS_END_BLOCK, eas->w); + uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w); + uint32_t end_data = xive_get_field64(EAS_END_DATA, eas->w); + /* END cache lookup */ if (xive_router_get_end(xrtr, end_blk, end_idx, &end)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No END %x/%x\n", end_blk, @@ -1772,10 +1827,7 @@ do_escalation: /* * The END trigger becomes an Escalation trigger */ - xive_router_end_notify(xrtr, - xive_get_field32(END_W4_ESC_END_BLOCK, end.w4), - xive_get_field32(END_W4_ESC_END_INDEX, end.w4), - xive_get_field32(END_W5_ESC_END_DATA, end.w5)); + xive_router_end_notify_handler(xrtr, (XiveEAS *) &end.w4); } void xive_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) @@ -1826,10 +1878,7 @@ void xive_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) /* * The event trigger becomes an END trigger */ - xive_router_end_notify(xrtr, - xive_get_field64(EAS_END_BLOCK, eas.w), - xive_get_field64(EAS_END_INDEX, eas.w), - xive_get_field64(EAS_END_DATA, eas.w)); + xive_router_end_notify_handler(xrtr, &eas); } static Property xive_router_properties[] = { @@ -1842,12 +1891,16 @@ static void xive_router_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); XiveNotifierClass *xnc = XIVE_NOTIFIER_CLASS(klass); + XiveRouterClass *xrc = XIVE_ROUTER_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; + + /* By default, the router handles END triggers locally */ + xrc->end_notify = xive_router_end_notify; } static const TypeInfo xive_router_info = { @@ -1961,11 +2014,11 @@ static const MemoryRegionOps xive_end_source_ops = { .write = xive_end_source_write, .endianness = DEVICE_BIG_ENDIAN, .valid = { - .min_access_size = 8, + .min_access_size = 1, .max_access_size = 8, }, .impl = { - .min_access_size = 8, + .min_access_size = 1, .max_access_size = 8, }, }; diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 4d9ff41956..98c0d8ba44 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -542,7 +542,7 @@ static void xive2_router_realize(DeviceState *dev, Error **errp) /* * Notification using the END ESe/ESn bit (Event State Buffer for - * escalation and notification). Profide futher coalescing in the + * escalation and notification). Profide further coalescing in the * Router. */ static bool xive2_router_end_es_notify(Xive2Router *xrtr, uint8_t end_blk, @@ -621,7 +621,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, /* * Check the END ESn (Event State Buffer for notification) for - * even futher coalescing in the Router + * even further coalescing in the Router */ if (!xive2_end_is_notify(&end)) { /* ESn[Q]=1 : end of notification */ @@ -702,7 +702,7 @@ do_escalation: /* * Check the END ESe (Event State Buffer for escalation) for even - * futher coalescing in the Router + * further coalescing in the Router */ if (!xive2_end_is_uncond_escalation(&end)) { /* ESe[Q]=1 : end of escalation notification */ @@ -954,11 +954,11 @@ static const MemoryRegionOps xive2_end_source_ops = { .write = xive2_end_source_write, .endianness = DEVICE_BIG_ENDIAN, .valid = { - .min_access_size = 8, + .min_access_size = 1, .max_access_size = 8, }, .impl = { - .min_access_size = 8, + .min_access_size = 1, .max_access_size = 8, }, }; diff --git a/hw/intc/xlnx-pmu-iomod-intc.c b/hw/intc/xlnx-pmu-iomod-intc.c index acaa1c3e6f..12bd1a3fff 100644 --- a/hw/intc/xlnx-pmu-iomod-intc.c +++ b/hw/intc/xlnx-pmu-iomod-intc.c @@ -526,7 +526,7 @@ static const VMStateDescription vmstate_xlnx_pmu_io_intc = { .name = TYPE_XLNX_PMU_IO_INTC, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxPMUIOIntc, XLNXPMUIOINTC_R_MAX), VMSTATE_END_OF_LIST(), } diff --git a/hw/intc/xlnx-zynqmp-ipi.c b/hw/intc/xlnx-zynqmp-ipi.c index adc1179014..509ee799cc 100644 --- a/hw/intc/xlnx-zynqmp-ipi.c +++ b/hw/intc/xlnx-zynqmp-ipi.c @@ -349,7 +349,7 @@ static const VMStateDescription vmstate_zynqmp_pmu_ipi = { .name = TYPE_XLNX_ZYNQMP_IPI, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPIPI, R_XLNX_ZYNQMP_IPI_MAX), VMSTATE_END_OF_LIST(), } diff --git a/hw/ipack/ipack.c b/hw/ipack/ipack.c index ae20f36da6..c39dbb481f 100644 --- a/hw/ipack/ipack.c +++ b/hw/ipack/ipack.c @@ -93,7 +93,7 @@ const VMStateDescription vmstate_ipack_device = { .name = "ipack_device", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(slot, IPackDevice), VMSTATE_END_OF_LIST() } diff --git a/hw/ipack/meson.build b/hw/ipack/meson.build index 3f8138b6f2..26567f1068 100644 --- a/hw/ipack/meson.build +++ b/hw/ipack/meson.build @@ -1 +1 @@ -softmmu_ss.add(when: 'CONFIG_IPACK', if_true: files('ipack.c', 'tpci200.c')) +system_ss.add(when: 'CONFIG_IPACK', if_true: files('ipack.c', 'tpci200.c')) diff --git a/hw/ipack/tpci200.c b/hw/ipack/tpci200.c index 1f764fc85b..88eef4b830 100644 --- a/hw/ipack/tpci200.c +++ b/hw/ipack/tpci200.c @@ -12,7 +12,7 @@ #include "qemu/units.h" #include "hw/ipack/ipack.h" #include "hw/irq.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "migration/vmstate.h" #include "qemu/bitops.h" #include "qemu/module.h" @@ -619,7 +619,7 @@ static const VMStateDescription vmstate_tpci200 = { .name = "tpci200", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, TPCI200State), VMSTATE_BOOL_ARRAY(big_endian, TPCI200State, 3), VMSTATE_UINT8_ARRAY(ctrl, TPCI200State, N_MODULES), diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index acf2bab35f..29c5af3cc3 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -301,7 +301,7 @@ static void handle_msg(IPMIBmcExtern *ibe) ipmi_debug("msg checksum failure\n"); return; } else { - ibe->inpos--; /* Remove checkum */ + ibe->inpos--; /* Remove checksum */ } timer_del(ibe->extern_timer); @@ -453,19 +453,6 @@ static void ipmi_bmc_extern_handle_reset(IPMIBmc *b) continue_send(ibe); } -static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) -{ - IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); - - if (!qemu_chr_fe_backend_connected(&ibe->chr)) { - error_setg(errp, "IPMI external bmc requires chardev attribute"); - return; - } - - qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, - chr_event, NULL, ibe, NULL, true); -} - static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id) { IPMIBmcExtern *ibe = opaque; @@ -492,19 +479,33 @@ static const VMStateDescription vmstate_ipmi_bmc_extern = { .version_id = 1, .minimum_version_id = 1, .post_load = ipmi_bmc_extern_post_migrate, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(send_reset, IPMIBmcExtern), VMSTATE_BOOL(waiting_rsp, IPMIBmcExtern), VMSTATE_END_OF_LIST() } }; +static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) +{ + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); + + if (!qemu_chr_fe_backend_connected(&ibe->chr)) { + error_setg(errp, "IPMI external bmc requires chardev attribute"); + return; + } + + qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, + chr_event, NULL, ibe, NULL, true); + + vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe); +} + static void ipmi_bmc_extern_init(Object *obj) { IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj); ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe); - vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe); } static void ipmi_bmc_extern_finalize(Object *obj) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 905e091094..33c839c65a 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -2103,7 +2103,7 @@ static const VMStateDescription vmstate_ipmi_sim = { .name = TYPE_IPMI_BMC_SIMULATOR, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(bmc_global_enables, IPMIBmcSim), VMSTATE_UINT8(msg_flags, IPMIBmcSim), VMSTATE_BOOL(watchdog_initialized, IPMIBmcSim), diff --git a/hw/ipmi/ipmi_bt.c b/hw/ipmi/ipmi_bt.c index 22f94fb98d..583fc64730 100644 --- a/hw/ipmi/ipmi_bt.c +++ b/hw/ipmi/ipmi_bt.c @@ -396,7 +396,7 @@ const VMStateDescription vmstate_IPMIBT = { .version_id = 1, .minimum_version_id = 1, .post_load = ipmi_bt_vmstate_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(obf_irq_set, IPMIBT), VMSTATE_BOOL(atn_irq_set, IPMIBT), VMSTATE_BOOL(irqs_enabled, IPMIBT), diff --git a/hw/ipmi/ipmi_kcs.c b/hw/ipmi/ipmi_kcs.c index a77612946a..c15977cab4 100644 --- a/hw/ipmi/ipmi_kcs.c +++ b/hw/ipmi/ipmi_kcs.c @@ -379,7 +379,7 @@ const VMStateDescription vmstate_IPMIKCS = { .version_id = 2, .minimum_version_id = 1, .post_load = ipmi_kcs_vmstate_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(obf_irq_set, IPMIKCS), VMSTATE_BOOL(atn_irq_set, IPMIKCS), VMSTATE_UNUSED_TEST(vmstate_kcs_before_version2, 1), /* Was use_irq */ diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index 88aa734e9e..7b36d51494 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -31,6 +31,7 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qom/object.h" +#include "hw/acpi/ipmi.h" #define TYPE_ISA_IPMI_BT "isa-ipmi-bt" OBJECT_DECLARE_SIMPLE_TYPE(ISAIPMIBTDevice, ISA_IPMI_BT) @@ -67,6 +68,21 @@ static void isa_ipmi_bt_lower_irq(IPMIBT *ib) qemu_irq_lower(iib->irq); } +static const VMStateDescription vmstate_ISAIPMIBTDevice = { + .name = TYPE_IPMI_INTERFACE_PREFIX "isa-bt", + .version_id = 2, + .minimum_version_id = 2, + /* + * Version 1 had messed up the array transfer, it's not even usable + * because it used VMSTATE_VBUFFER_UINT32, but it did not transfer + * the buffer length, so random things would happen. + */ + .fields = (const VMStateField[]) { + VMSTATE_STRUCT(bt, ISAIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT), + VMSTATE_END_OF_LIST() + } +}; + static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) { Error *err = NULL; @@ -101,30 +117,15 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length); isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base); -} -static const VMStateDescription vmstate_ISAIPMIBTDevice = { - .name = TYPE_IPMI_INTERFACE_PREFIX "isa-bt", - .version_id = 2, - .minimum_version_id = 2, - /* - * Version 1 had messed up the array transfer, it's not even usable - * because it used VMSTATE_VBUFFER_UINT32, but it did not transfer - * the buffer length, so random things would happen. - */ - .fields = (VMStateField[]) { - VMSTATE_STRUCT(bt, ISAIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT), - VMSTATE_END_OF_LIST() - } -}; + vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, dev); +} static void isa_ipmi_bt_init(Object *obj) { ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj); ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc); - - vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, iib); } static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii) @@ -144,6 +145,7 @@ static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(oc); dc->realize = isa_ipmi_bt_realize; device_class_set_props(dc, ipmi_isa_properties); @@ -151,6 +153,7 @@ static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data) iic->get_backend_data = isa_ipmi_bt_get_backend_data; ipmi_bt_class_init(iic); iic->get_fwinfo = isa_ipmi_bt_get_fwinfo; + adevc->build_dev_aml = build_ipmi_dev_aml; } static const TypeInfo isa_ipmi_bt_info = { @@ -161,6 +164,7 @@ static const TypeInfo isa_ipmi_bt_info = { .class_init = isa_ipmi_bt_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_IPMI_INTERFACE }, + { TYPE_ACPI_DEV_AML_IF }, { } } }; diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index afabb95ebe..f52b32e590 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -31,6 +31,7 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qom/object.h" +#include "hw/acpi/ipmi.h" #define TYPE_ISA_IPMI_KCS "isa-ipmi-kcs" OBJECT_DECLARE_SIMPLE_TYPE(ISAIPMIKCSDevice, ISA_IPMI_KCS) @@ -66,6 +67,24 @@ static void isa_ipmi_kcs_lower_irq(IPMIKCS *ik) qemu_irq_lower(iik->irq); } +static bool vmstate_kcs_before_version2(void *opaque, int version) +{ + return version <= 1; +} + +static const VMStateDescription vmstate_ISAIPMIKCSDevice = { + .name = TYPE_IPMI_INTERFACE, + .version_id = 2, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_VSTRUCT_TEST(kcs, ISAIPMIKCSDevice, vmstate_kcs_before_version2, + 0, vmstate_IPMIKCS, IPMIKCS, 1), + VMSTATE_VSTRUCT_V(kcs, ISAIPMIKCSDevice, 2, vmstate_IPMIKCS, + IPMIKCS, 2), + VMSTATE_END_OF_LIST() + } +}; + static void ipmi_isa_realize(DeviceState *dev, Error **errp) { Error *err = NULL; @@ -100,31 +119,6 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp) qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length); isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base); -} - -static bool vmstate_kcs_before_version2(void *opaque, int version) -{ - return version <= 1; -} - -static const VMStateDescription vmstate_ISAIPMIKCSDevice = { - .name = TYPE_IPMI_INTERFACE, - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_VSTRUCT_TEST(kcs, ISAIPMIKCSDevice, vmstate_kcs_before_version2, - 0, vmstate_IPMIKCS, IPMIKCS, 1), - VMSTATE_VSTRUCT_V(kcs, ISAIPMIKCSDevice, 2, vmstate_IPMIKCS, - IPMIKCS, 2), - VMSTATE_END_OF_LIST() - } -}; - -static void isa_ipmi_kcs_init(Object *obj) -{ - ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj); - - ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc); /* * Version 1 had an incorrect name, it clashed with the BT @@ -134,6 +128,13 @@ static void isa_ipmi_kcs_init(Object *obj) vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik); } +static void isa_ipmi_kcs_init(Object *obj) +{ + ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj); + + ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc); +} + static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii) { ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii); @@ -151,6 +152,7 @@ static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(oc); dc->realize = ipmi_isa_realize; device_class_set_props(dc, ipmi_isa_properties); @@ -158,6 +160,7 @@ static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data) iic->get_backend_data = isa_ipmi_kcs_get_backend_data; ipmi_kcs_class_init(iic); iic->get_fwinfo = isa_ipmi_kcs_get_fwinfo; + adevc->build_dev_aml = build_ipmi_dev_aml; } static const TypeInfo isa_ipmi_kcs_info = { @@ -168,6 +171,7 @@ static const TypeInfo isa_ipmi_kcs_info = { .class_init = isa_ipmi_kcs_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_IPMI_INTERFACE }, + { TYPE_ACPI_DEV_AML_IF }, { } } }; diff --git a/hw/ipmi/meson.build b/hw/ipmi/meson.build index 9622ea2a2c..07f109d365 100644 --- a/hw/ipmi/meson.build +++ b/hw/ipmi/meson.build @@ -8,4 +8,4 @@ ipmi_ss.add(when: 'CONFIG_ISA_IPMI_BT', if_true: files('isa_ipmi_bt.c')) ipmi_ss.add(when: 'CONFIG_PCI_IPMI_BT', if_true: files('pci_ipmi_bt.c')) ipmi_ss.add(when: 'CONFIG_IPMI_SSIF', if_true: files('smbus_ipmi.c')) -softmmu_ss.add_all(when: 'CONFIG_IPMI', if_true: ipmi_ss) +system_ss.add_all(when: 'CONFIG_IPMI', if_true: ipmi_ss) diff --git a/hw/ipmi/pci_ipmi_bt.c b/hw/ipmi/pci_ipmi_bt.c index b6e52730d3..afeea6f303 100644 --- a/hw/ipmi/pci_ipmi_bt.c +++ b/hw/ipmi/pci_ipmi_bt.c @@ -25,7 +25,7 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "hw/ipmi/ipmi_bt.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "qom/object.h" #define TYPE_PCI_IPMI_BT "pci-ipmi-bt" @@ -87,7 +87,7 @@ const VMStateDescription vmstate_PCIIPMIBTDevice = { .name = TYPE_IPMI_INTERFACE_PREFIX "pci-bt", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, PCIIPMIBTDevice), VMSTATE_STRUCT(bt, PCIIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT), VMSTATE_END_OF_LIST() diff --git a/hw/ipmi/pci_ipmi_kcs.c b/hw/ipmi/pci_ipmi_kcs.c index de13418862..05ba97ec58 100644 --- a/hw/ipmi/pci_ipmi_kcs.c +++ b/hw/ipmi/pci_ipmi_kcs.c @@ -25,7 +25,7 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "hw/ipmi/ipmi_kcs.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "qom/object.h" #define TYPE_PCI_IPMI_KCS "pci-ipmi-kcs" @@ -87,7 +87,7 @@ const VMStateDescription vmstate_PCIIPMIKCSDevice = { .name = TYPE_IPMI_INTERFACE_PREFIX "pci-kcs", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, PCIIPMIKCSDevice), VMSTATE_STRUCT(kcs, PCIIPMIKCSDevice, 1, vmstate_IPMIKCS, IPMIKCS), VMSTATE_END_OF_LIST() diff --git a/hw/ipmi/smbus_ipmi.c b/hw/ipmi/smbus_ipmi.c index 1fdf0a66b6..56865df7db 100644 --- a/hw/ipmi/smbus_ipmi.c +++ b/hw/ipmi/smbus_ipmi.c @@ -28,6 +28,7 @@ #include "qemu/error-report.h" #include "hw/ipmi/ipmi.h" #include "qom/object.h" +#include "hw/acpi/ipmi.h" #define TYPE_SMBUS_IPMI "smbus-ipmi" OBJECT_DECLARE_SIMPLE_TYPE(SMBusIPMIDevice, SMBUS_IPMI) @@ -280,7 +281,9 @@ static int ipmi_write_data(SMBusDevice *dev, uint8_t *buf, uint8_t len) */ send = true; } - memcpy(sid->inmsg + sid->inlen, buf, len); + if (len > 0) { + memcpy(sid->inmsg + sid->inlen, buf, len); + } sid->inlen += len; break; } @@ -296,7 +299,7 @@ static const VMStateDescription vmstate_smbus_ipmi = { .name = TYPE_SMBUS_IPMI, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_SMBUS_DEVICE(parent, SMBusIPMIDevice), VMSTATE_UINT8(waiting_rsp, SMBusIPMIDevice), VMSTATE_UINT32(outlen, SMBusIPMIDevice), @@ -353,6 +356,7 @@ static void smbus_ipmi_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(oc); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(oc); sc->receive_byte = ipmi_receive_byte; sc->write_data = ipmi_write_data; @@ -363,6 +367,7 @@ static void smbus_ipmi_class_init(ObjectClass *oc, void *data) iic->handle_if_event = smbus_ipmi_handle_event; iic->set_irq_enable = smbus_ipmi_set_irq_enable; iic->get_fwinfo = smbus_ipmi_get_fwinfo; + adevc->build_dev_aml = build_ipmi_dev_aml; } static const TypeInfo smbus_ipmi_info = { @@ -373,6 +378,7 @@ static const TypeInfo smbus_ipmi_info = { .class_init = smbus_ipmi_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_IPMI_INTERFACE }, + { TYPE_ACPI_DEV_AML_IF }, { } } }; diff --git a/hw/isa/Kconfig b/hw/isa/Kconfig index d42143a991..73c6470805 100644 --- a/hw/isa/Kconfig +++ b/hw/isa/Kconfig @@ -15,9 +15,17 @@ config I82378 config ISA_SUPERIO bool - select ISA_BUS + depends on ISA_BUS select PCKBD + select PARALLEL + select SERIAL_ISA select FDC_ISA + # Some users of ISA_SUPERIO do not use it + #select IDE_ISA + +config FDC37M81X + bool + select ISA_SUPERIO config PC87312 bool @@ -26,47 +34,44 @@ config PC87312 select I8254 select I8257 select MC146818RTC - select SERIAL_ISA - select PARALLEL - select FDC_ISA select IDE_ISA -config PIIX3 - bool - select ISA_BUS - -config PIIX4 +config PIIX bool # For historical reasons, SuperIO devices are created in the board # for PIIX4. + select ACPI_PIIX4 + select I8254 + select I8257 + select I8259 + select IDE_PIIX select ISA_BUS + select MC146818RTC select USB_UHCI config VT82C686 bool + select ISA_BUS select ISA_SUPERIO + select ACPI select ACPI_SMBUS - select SERIAL_ISA - select FDC_ISA select USB_UHCI select APM select I8254 select I8257 select I8259 + select IDE_VIA select MC146818RTC - select PARALLEL config SMC37C669 bool select ISA_SUPERIO - select SERIAL_ISA - select PARALLEL - select FDC_ISA config LPC_ICH9 bool # For historical reasons, SuperIO devices are created in the board # for ICH9. + select I8257 select ISA_BUS - select ACPI_SMBUS - select ACPI_X86_ICH + select ACPI_ICH9 + select MC146818RTC diff --git a/hw/isa/apm.c b/hw/isa/apm.c index dfe9020d30..e34edb864c 100644 --- a/hw/isa/apm.c +++ b/hw/isa/apm.c @@ -68,7 +68,7 @@ const VMStateDescription vmstate_apm = { .name = "APM State", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(apmc, APMState), VMSTATE_UINT8(apms, APMState), VMSTATE_END_OF_LIST() diff --git a/hw/isa/fdc37m81x-superio.c b/hw/isa/fdc37m81x-superio.c new file mode 100644 index 0000000000..55e91fbca1 --- /dev/null +++ b/hw/isa/fdc37m81x-superio.c @@ -0,0 +1,32 @@ +/* + * SMS FDC37M817 Super I/O + * + * Copyright (c) 2018 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 + */ + +#include "qemu/osdep.h" +#include "hw/isa/superio.h" + +static void fdc37m81x_class_init(ObjectClass *klass, void *data) +{ + ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); + + sc->serial.count = 2; /* NS16C550A */ + sc->parallel.count = 1; + sc->floppy.count = 1; /* SMSC 82077AA Compatible */ + sc->ide.count = 0; +} + +static const TypeInfo types[] = { + { + .name = TYPE_FDC37M81X_SUPERIO, + .parent = TYPE_ISA_SUPERIO, + .class_init = fdc37m81x_class_init, + }, +}; + +DEFINE_TYPES(types) diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c index 2a2ff05b93..cbaa152a89 100644 --- a/hw/isa/i82378.c +++ b/hw/isa/i82378.c @@ -18,7 +18,7 @@ */ #include "qemu/osdep.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/irq.h" #include "hw/intc/i8259.h" #include "hw/timer/i8254.h" @@ -32,16 +32,15 @@ OBJECT_DECLARE_SIMPLE_TYPE(I82378State, I82378) struct I82378State { PCIDevice parent_obj; - qemu_irq out[2]; - qemu_irq *i8259; - MemoryRegion io; + qemu_irq cpu_intr; + qemu_irq *isa_irqs_in; }; static const VMStateDescription vmstate_i82378 = { .name = "pci-i82378", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, I82378State), VMSTATE_END_OF_LIST() }, @@ -50,7 +49,7 @@ static const VMStateDescription vmstate_i82378 = { static void i82378_request_out0_irq(void *opaque, int irq, int level) { I82378State *s = opaque; - qemu_set_irq(s->out[0], level); + qemu_set_irq(s->cpu_intr, level); } static void i82378_request_pic_irq(void *opaque, int irq, int level) @@ -58,7 +57,7 @@ static void i82378_request_pic_irq(void *opaque, int irq, int level) DeviceState *dev = opaque; I82378State *s = I82378(dev); - qemu_set_irq(s->i8259[irq], level); + qemu_set_irq(s->isa_irqs_in[irq], level); } static void i82378_realize(PCIDevice *pci, Error **errp) @@ -68,6 +67,7 @@ static void i82378_realize(PCIDevice *pci, Error **errp) uint8_t *pci_conf; ISABus *isabus; ISADevice *pit; + ISADevice *pcspk; pci_conf = pci->config; pci_set_word(pci_conf + PCI_COMMAND, @@ -94,15 +94,20 @@ static void i82378_realize(PCIDevice *pci, Error **errp) */ /* 2 82C59 (irq) */ - s->i8259 = i8259_init(isabus, - qemu_allocate_irq(i82378_request_out0_irq, s, 0)); - isa_bus_irqs(isabus, s->i8259); + s->isa_irqs_in = i8259_init(isabus, + qemu_allocate_irq(i82378_request_out0_irq, + s, 0)); + isa_bus_register_input_irqs(isabus, s->isa_irqs_in); /* 1 82C54 (pit) */ pit = i8254_pit_init(isabus, 0x40, 0, NULL); /* speaker */ - pcspk_init(isa_new(TYPE_PC_SPEAKER), isabus, pit); + pcspk = isa_new(TYPE_PC_SPEAKER); + object_property_set_link(OBJECT(pcspk), "pit", OBJECT(pit), &error_fatal); + if (!isa_realize_and_unref(pcspk, isabus, errp)) { + return; + } /* 2 82C37 (dma) */ isa_create_simple(isabus, "i82374"); @@ -113,7 +118,7 @@ static void i82378_init(Object *obj) DeviceState *dev = DEVICE(obj); I82378State *s = I82378(obj); - qdev_init_gpio_out(dev, s->out, 1); + qdev_init_gpio_out(dev, &s->cpu_intr, 1); qdev_init_gpio_in(dev, i82378_request_pic_irq, 16); } diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index cd5ad3687d..f1e0f14007 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -52,28 +52,42 @@ static const TypeInfo isa_bus_info = { ISABus *isa_bus_new(DeviceState *dev, MemoryRegion* address_space, MemoryRegion *address_space_io, Error **errp) { + DeviceState *bridge = NULL; + if (isabus) { error_setg(errp, "Can't create a second ISA bus"); return NULL; } if (!dev) { - dev = qdev_new("isabus-bridge"); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + bridge = qdev_new("isabus-bridge"); + dev = bridge; } isabus = ISA_BUS(qbus_new(TYPE_ISA_BUS, dev, NULL)); isabus->address_space = address_space; isabus->address_space_io = address_space_io; + + if (bridge) { + sysbus_realize_and_unref(SYS_BUS_DEVICE(bridge), &error_fatal); + } + return isabus; } -void isa_bus_irqs(ISABus *bus, qemu_irq *irqs) +void isa_bus_register_input_irqs(ISABus *bus, qemu_irq *irqs_in) { - bus->irqs = irqs; + bus->irqs_in = irqs_in; +} + +qemu_irq isa_bus_get_irq(ISABus *bus, unsigned irqnum) +{ + assert(irqnum < ISA_NUM_IRQS); + assert(bus->irqs_in); + return bus->irqs_in[irqnum]; } /* - * isa_get_irq() returns the corresponding qemu_irq entry for the i8259. + * isa_get_irq() returns the corresponding input qemu_irq entry for the i8259. * * This function is only for special cases such as the 'ferr', and * temporary use for normal devices until they are converted to qdev. @@ -81,14 +95,13 @@ void isa_bus_irqs(ISABus *bus, qemu_irq *irqs) qemu_irq isa_get_irq(ISADevice *dev, unsigned isairq) { assert(!dev || ISA_BUS(qdev_get_parent_bus(DEVICE(dev))) == isabus); - assert(isairq < ISA_NUM_IRQS); - return isabus->irqs[isairq]; + return isa_bus_get_irq(isabus, isairq); } void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, unsigned isairq) { - qemu_irq irq = isa_get_irq(isadev, isairq); - qdev_connect_gpio_out(DEVICE(isadev), gpioirq, irq); + qemu_irq input_irq = isa_get_irq(isadev, isairq); + qdev_connect_gpio_out(DEVICE(isadev), gpioirq, input_irq); } void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16) @@ -99,7 +112,7 @@ void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16) bus->dma[1] = dma16; } -IsaDma *isa_get_dma(ISABus *bus, int nchan) +IsaDma *isa_bus_get_dma(ISABus *bus, int nchan) { assert(bus); return bus->dma[nchan > 3 ? 1 : 0]; @@ -114,7 +127,7 @@ static inline void isa_init_ioport(ISADevice *dev, uint16_t ioport) void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start) { - memory_region_add_subregion(isabus->address_space_io, start, io); + memory_region_add_subregion(isa_address_space_io(dev), start, io); isa_init_ioport(dev, start); } @@ -135,7 +148,7 @@ int isa_register_portio_list(ISADevice *dev, isa_init_ioport(dev, start); portio_list_init(piolist, OBJECT(dev), pio_start, opaque, name); - portio_list_add(piolist, isabus->address_space_io, start); + portio_list_add(piolist, isa_address_space_io(dev), start); return 0; } @@ -164,6 +177,11 @@ bool isa_realize_and_unref(ISADevice *dev, ISABus *bus, Error **errp) return qdev_realize_and_unref(&dev->parent_obj, &bus->parent_obj, errp); } +ISABus *isa_bus_from_device(ISADevice *dev) +{ + return ISA_BUS(qdev_get_parent_bus(DEVICE(dev))); +} + ISADevice *isa_vga_init(ISABus *bus) { vga_interface_created = true; @@ -187,21 +205,6 @@ ISADevice *isa_vga_init(ISABus *bus) } } -void isa_build_aml(ISABus *bus, Aml *scope) -{ - BusChild *kid; - ISADevice *dev; - ISADeviceClass *dc; - - QTAILQ_FOREACH(kid, &bus->parent_obj.children, sibling) { - dev = ISA_DEVICE(kid->child); - dc = ISA_DEVICE_GET_CLASS(dev); - if (dc->build_aml) { - dc->build_aml(dev, scope); - } - } -} - static void isabus_bridge_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -228,7 +231,6 @@ static const TypeInfo isa_device_type_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(ISADevice), .abstract = true, - .class_size = sizeof(ISADeviceClass), .class_init = isa_device_class_init, }; diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index c81bfe58ef..a8c8c58ef7 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -16,10 +16,12 @@ #include "qapi/error.h" #include "sysemu/blockdev.h" #include "chardev/char.h" +#include "hw/char/parallel.h" #include "hw/block/fdc.h" #include "hw/isa/superio.h" #include "hw/qdev-properties.h" #include "hw/input/i8042.h" +#include "hw/char/parallel-isa.h" #include "hw/char/serial.h" #include "trace.h" @@ -51,7 +53,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) } else { name = g_strdup_printf("parallel%d", i); } - isa = isa_new("isa-parallel"); + isa = isa_new(TYPE_ISA_PARALLEL); d = DEVICE(isa); qdev_prop_set_uint32(d, "index", i); if (k->parallel.get_iobase) { @@ -114,7 +116,9 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) } /* Floppy disc */ - if (!k->floppy.is_enabled || k->floppy.is_enabled(sio, 0)) { + assert(k->floppy.count <= 1); + if (k->floppy.count && + (!k->floppy.is_enabled || k->floppy.is_enabled(sio, 0))) { isa = isa_new(TYPE_ISA_FDC); d = DEVICE(isa); if (k->floppy.get_iobase) { @@ -183,30 +187,12 @@ static const TypeInfo isa_superio_type_info = { .abstract = true, .class_size = sizeof(ISASuperIOClass), .class_init = isa_superio_class_init, -}; - -/* SMS FDC37M817 Super I/O */ -static void fdc37m81x_class_init(ObjectClass *klass, void *data) -{ - ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); - - sc->serial.count = 2; /* NS16C550A */ - sc->parallel.count = 1; - sc->floppy.count = 1; /* SMSC 82077AA Compatible */ - sc->ide.count = 0; -} - -static const TypeInfo fdc37m81x_type_info = { - .name = TYPE_FDC37M81X_SUPERIO, - .parent = TYPE_ISA_SUPERIO, .instance_size = sizeof(ISASuperIODevice), - .class_init = fdc37m81x_class_init, }; static void isa_superio_register_types(void) { type_register_static(&isa_superio_type_info); - type_register_static(&fdc37m81x_type_info); } type_init(isa_superio_register_types) diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 5f143dca17..bd727b2320 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -34,13 +34,13 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "qemu/range.h" +#include "hw/dma/i8257.h" #include "hw/isa/isa.h" #include "migration/vmstate.h" #include "hw/irq.h" #include "hw/isa/apm.h" #include "hw/pci/pci.h" -#include "hw/pci/pci_bridge.h" -#include "hw/i386/ich9.h" +#include "hw/southbridge/ich9.h" #include "hw/acpi/acpi.h" #include "hw/acpi/ich9.h" #include "hw/pci/pci_bus.h" @@ -50,12 +50,12 @@ #include "hw/core/cpu.h" #include "hw/nvram/fw_cfg.h" #include "qemu/cutils.h" +#include "hw/acpi/acpi_aml_interface.h" +#include "trace.h" /*****************************************************************************/ /* ICH9 LPC PCI to ISA bridge */ -static void ich9_lpc_reset(DeviceState *qdev); - /* chipset configuration register * to access chipset configuration registers, pci_[sg]et_{byte, word, long} * are used. @@ -160,6 +160,7 @@ static void ich9_cc_write(void *opaque, hwaddr addr, { ICH9LPCState *lpc = (ICH9LPCState *)opaque; + trace_ich9_cc_write(addr, val, len); ich9_cc_addr_len(&addr, &len); memcpy(lpc->chip_config + addr, &val, len); pci_bus_fire_intx_routing_notifier(pci_get_bus(&lpc->d)); @@ -175,6 +176,7 @@ static uint64_t ich9_cc_read(void *opaque, hwaddr addr, uint32_t val = 0; ich9_cc_addr_len(&addr, &len); memcpy(&val, lpc->chip_config + addr, len); + trace_ich9_cc_read(addr, val, len); return val; } @@ -254,7 +256,7 @@ static void ich9_lpc_update_apic(ICH9LPCState *lpc, int gsi) qemu_set_irq(lpc->gsi[gsi], level); } -void ich9_lpc_set_irq(void *opaque, int pirq, int level) +static void ich9_lpc_set_irq(void *opaque, int pirq, int level) { ICH9LPCState *lpc = opaque; int pic_irq, pic_dis; @@ -270,7 +272,7 @@ void ich9_lpc_set_irq(void *opaque, int pirq, int level) /* return the pirq number (PIRQ[A-H]:0-7) corresponding to * a given device irq pin. */ -int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx) +static int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx) { BusState *bus = qdev_get_parent_bus(&pci_dev->qdev); PCIBus *pci_bus = PCI_BUS(bus); @@ -281,7 +283,7 @@ int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx) return lpc->irr[PCI_SLOT(pci_dev->devfn)][intx]; } -PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin) +static PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin) { ICH9LPCState *lpc = opaque; PCIINTxRoute route; @@ -301,6 +303,21 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin) route.irq = -1; } } else { + /* + * Strictly speaking, this is wrong. The PIRQ should be routed + * to *both* the I/O APIC and the PIC, on different pins. The + * I/O APIC has a fixed mapping to IRQ16-23, while the PIC is + * routed according to the PIRQx_ROUT configuration. But QEMU + * doesn't (yet) cope with the concept of pin numbers differing + * between PIC and I/O APIC, and neither does the in-kernel KVM + * irqchip support. So we route to the I/O APIC *only* if the + * routing to the PIC is disabled in the PIRQx_ROUT settings. + * + * This seems to work even if we boot a Linux guest with 'noapic' + * to make it use the legacy PIC, and then kexec directly into a + * new kernel which uses the I/O APIC. The new kernel explicitly + * disables the PIRQ routing even though it doesn't need to care. + */ route.irq = ich9_pirq_to_gsi(pirq_pin); } @@ -402,14 +419,13 @@ static void smi_features_ok_callback(void *opaque) lpc->smi_features_ok = 1; } -void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled) +static void ich9_lpc_pm_init(ICH9LPCState *lpc) { - ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci); qemu_irq sci_irq; FWCfgState *fw_cfg = fw_cfg_find(); sci_irq = qemu_allocate_irq(ich9_set_sci, lpc, 0); - ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, sci_irq); + ich9_pm_init(PCI_DEVICE(lpc), &lpc->pm, sci_irq); if (lpc->smi_host_features && fw_cfg) { uint64_t host_features_le; @@ -435,8 +451,6 @@ void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled) sizeof lpc->smi_features_ok, true); } - - ich9_lpc_reset(DEVICE(lpc)); } /* APM */ @@ -658,6 +672,11 @@ static void ich9_lpc_initfn(Object *obj) static const uint8_t acpi_enable_cmd = ICH9_APM_ACPI_ENABLE; static const uint8_t acpi_disable_cmd = ICH9_APM_ACPI_DISABLE; + object_initialize_child(obj, "rtc", &lpc->rtc, TYPE_MC146818_RTC); + + qdev_init_gpio_out_named(DEVICE(lpc), lpc->gsi, ICH9_GPIO_GSI, + IOAPIC_NUM_PINS); + object_property_add_uint8_ptr(obj, ACPI_PM_PROP_SCI_INT, &lpc->sci_gsi, OBJ_PROP_FLAG_READ); object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_ENABLE_CMD, @@ -674,8 +693,9 @@ static void ich9_lpc_initfn(Object *obj) static void ich9_lpc_realize(PCIDevice *d, Error **errp) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(d); - DeviceState *dev = DEVICE(d); + PCIBus *pci_bus = pci_get_bus(d); ISABus *isa_bus; + uint32_t irq; if ((lpc->smi_host_features & BIT_ULL(ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT)) && !(lpc->smi_host_features & BIT_ULL(ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT))) { @@ -704,8 +724,6 @@ static void ich9_lpc_realize(PCIDevice *d, Error **errp) memory_region_init_io(&lpc->rcrb_mem, OBJECT(d), &rcrb_mmio_ops, lpc, "lpc-rcrb-mmio", ICH9_CC_SIZE); - lpc->isa_bus = isa_bus; - ich9_cc_init(lpc); apm_init(d, &lpc->apm, ich9_apm_ctrl_changed, lpc); @@ -718,9 +736,23 @@ static void ich9_lpc_realize(PCIDevice *d, Error **errp) ICH9_RST_CNT_IOPORT, &lpc->rst_cnt_mem, 1); - qdev_init_gpio_out_named(dev, lpc->gsi, ICH9_GPIO_GSI, GSI_NUM_PINS); + isa_bus_register_input_irqs(isa_bus, lpc->gsi); - isa_bus_irqs(isa_bus, lpc->gsi); + i8257_dma_init(OBJECT(d), isa_bus, 0); + + /* RTC */ + qdev_prop_set_int32(DEVICE(&lpc->rtc), "base_year", 2000); + if (!qdev_realize(DEVICE(&lpc->rtc), BUS(isa_bus), errp)) { + return; + } + irq = object_property_get_uint(OBJECT(&lpc->rtc), "irq", &error_fatal); + isa_connect_gpio_out(ISA_DEVICE(&lpc->rtc), 0, irq); + + pci_bus_irqs(pci_bus, ich9_lpc_set_irq, d, ICH9_LPC_NB_PIRQS); + pci_bus_map_irqs(pci_bus, ich9_lpc_map_irq); + pci_bus_set_route_irq_fn(pci_bus, ich9_route_intx_pin_to_irq); + + ich9_lpc_pm_init(lpc); } static bool ich9_rst_cnt_needed(void *opaque) @@ -735,7 +767,7 @@ static const VMStateDescription vmstate_ich9_rst_cnt = { .version_id = 1, .minimum_version_id = 1, .needed = ich9_rst_cnt_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(rst_cnt, ICH9LPCState), VMSTATE_END_OF_LIST() } @@ -755,7 +787,7 @@ static const VMStateDescription vmstate_ich9_smi_feat = { .version_id = 1, .minimum_version_id = 1, .needed = ich9_smi_feat_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(smi_guest_features_le, ICH9LPCState, sizeof(uint64_t)), VMSTATE_UINT8(smi_features_ok, ICH9LPCState), @@ -769,7 +801,7 @@ static const VMStateDescription vmstate_ich9_lpc = { .version_id = 1, .minimum_version_id = 1, .post_load = ich9_lpc_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(d, ICH9LPCState), VMSTATE_STRUCT(apm, ICH9LPCState, 0, vmstate_apm, APMState), VMSTATE_STRUCT(pm, ICH9LPCState, 0, vmstate_ich9_pm, ICH9LPCPMRegs), @@ -777,7 +809,7 @@ static const VMStateDescription vmstate_ich9_lpc = { VMSTATE_UINT32(sci_level, ICH9LPCState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_ich9_rst_cnt, &vmstate_ich9_smi_feat, NULL @@ -785,8 +817,9 @@ static const VMStateDescription vmstate_ich9_lpc = { }; static Property ich9_lpc_properties[] = { - DEFINE_PROP_BOOL("noreboot", ICH9LPCState, pin_strap.spkr_hi, true), + DEFINE_PROP_BOOL("noreboot", ICH9LPCState, pin_strap.spkr_hi, false), DEFINE_PROP_BOOL("smm-compat", ICH9LPCState, pm.smm_compat, false), + DEFINE_PROP_BOOL("smm-enabled", ICH9LPCState, pm.smm_enabled, false), DEFINE_PROP_BIT64("x-smi-broadcast", ICH9LPCState, smi_host_features, ICH9_LPC_SMI_F_BROADCAST_BIT, true), DEFINE_PROP_BIT64("x-smi-cpu-hotplug", ICH9LPCState, smi_host_features, @@ -803,12 +836,39 @@ static void ich9_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev) acpi_send_gpe_event(&s->pm.acpi_regs, s->pm.irq, ev); } +static void build_ich9_isa_aml(AcpiDevAmlIf *adev, Aml *scope) +{ + Aml *field; + BusState *bus = qdev_get_child_bus(DEVICE(adev), "isa.0"); + Aml *sb_scope = aml_scope("\\_SB"); + + /* ICH9 PCI to ISA irq remapping */ + aml_append(scope, aml_operation_region("PIRQ", AML_PCI_CONFIG, + aml_int(0x60), 0x0C)); + /* Fields declarion has to happen *after* operation region */ + field = aml_field("PCI0.SF8.PIRQ", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("PRQA", 8)); + aml_append(field, aml_named_field("PRQB", 8)); + aml_append(field, aml_named_field("PRQC", 8)); + aml_append(field, aml_named_field("PRQD", 8)); + aml_append(field, aml_reserved_field(0x20)); + aml_append(field, aml_named_field("PRQE", 8)); + aml_append(field, aml_named_field("PRQF", 8)); + aml_append(field, aml_named_field("PRQG", 8)); + aml_append(field, aml_named_field("PRQH", 8)); + aml_append(sb_scope, field); + aml_append(scope, sb_scope); + + qbus_build_aml(bus, scope); +} + static void ich9_lpc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(klass); + AcpiDevAmlIfClass *amldevc = ACPI_DEV_AML_IF_CLASS(klass); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->reset = ich9_lpc_reset; @@ -830,9 +890,10 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data) hc->plug = ich9_pm_device_plug_cb; hc->unplug_request = ich9_pm_device_unplug_request_cb; hc->unplug = ich9_pm_device_unplug_cb; + hc->is_hotpluggable_bus = ich9_pm_is_hotpluggable_bus; adevc->ospm_status = ich9_pm_ospm_status; adevc->send_event = ich9_send_gpe; - adevc->madt_cpu = pc_madt_cpu_entry; + amldevc->build_dev_aml = build_ich9_isa_aml; } static const TypeInfo ich9_lpc_info = { @@ -845,6 +906,7 @@ static const TypeInfo ich9_lpc_info = { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { TYPE_ACPI_DEV_AML_IF }, { } } }; diff --git a/hw/isa/meson.build b/hw/isa/meson.build index 8bf678ca0a..3219282217 100644 --- a/hw/isa/meson.build +++ b/hw/isa/meson.build @@ -1,11 +1,11 @@ -softmmu_ss.add(when: 'CONFIG_APM', if_true: files('apm.c')) -softmmu_ss.add(when: 'CONFIG_I82378', if_true: files('i82378.c')) -softmmu_ss.add(when: 'CONFIG_ISA_BUS', if_true: files('isa-bus.c')) -softmmu_ss.add(when: 'CONFIG_ISA_SUPERIO', if_true: files('isa-superio.c')) -softmmu_ss.add(when: 'CONFIG_PC87312', if_true: files('pc87312.c')) -softmmu_ss.add(when: 'CONFIG_PIIX3', if_true: files('piix3.c')) -softmmu_ss.add(when: 'CONFIG_PIIX4', if_true: files('piix4.c')) -softmmu_ss.add(when: 'CONFIG_SMC37C669', if_true: files('smc37c669-superio.c')) -softmmu_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686.c')) +system_ss.add(when: 'CONFIG_APM', if_true: files('apm.c')) +system_ss.add(when: 'CONFIG_FDC37M81X', if_true: files('fdc37m81x-superio.c')) +system_ss.add(when: 'CONFIG_I82378', if_true: files('i82378.c')) +system_ss.add(when: 'CONFIG_ISA_BUS', if_true: files('isa-bus.c')) +system_ss.add(when: 'CONFIG_ISA_SUPERIO', if_true: files('isa-superio.c')) +system_ss.add(when: 'CONFIG_PC87312', if_true: files('pc87312.c')) +system_ss.add(when: 'CONFIG_PIIX', if_true: files('piix.c')) +system_ss.add(when: 'CONFIG_SMC37C669', if_true: files('smc37c669-superio.c')) +system_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686.c')) specific_ss.add(when: 'CONFIG_LPC_ICH9', if_true: files('lpc_ich9.c')) diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c index 8d7b8d3db2..64dd17b537 100644 --- a/hw/isa/pc87312.c +++ b/hw/isa/pc87312.c @@ -319,7 +319,7 @@ static const VMStateDescription vmstate_pc87312 = { .version_id = 1, .minimum_version_id = 1, .post_load = pc87312_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(read_id_step, PC87312State), VMSTATE_UINT8(selected_index, PC87312State), VMSTATE_UINT8_ARRAY(regs, PC87312State, 3), @@ -338,10 +338,10 @@ static void pc87312_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); - sc->parent_realize = dc->realize; - dc->realize = pc87312_realize; dc->reset = pc87312_reset; dc->vmsd = &vmstate_pc87312; + device_class_set_parent_realize(dc, pc87312_realize, + &sc->parent_realize); device_class_set_props(dc, pc87312_properties); sc->parallel = (ISASuperIOFuncs){ diff --git a/hw/isa/piix.c b/hw/isa/piix.c new file mode 100644 index 0000000000..2d30711b17 --- /dev/null +++ b/hw/isa/piix.c @@ -0,0 +1,522 @@ +/* + * QEMU PIIX PCI ISA Bridge Emulation + * + * Copyright (c) 2006 Fabrice Bellard + * Copyright (c) 2018 Hervé Poussineau + * + * 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/range.h" +#include "qapi/error.h" +#include "hw/dma/i8257.h" +#include "hw/southbridge/piix.h" +#include "hw/timer/i8254.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/ide/piix.h" +#include "hw/intc/i8259.h" +#include "hw/isa/isa.h" +#include "sysemu/runstate.h" +#include "migration/vmstate.h" +#include "hw/acpi/acpi_aml_interface.h" + +static void piix_set_irq_pic(PIIXState *s, int pic_irq) +{ + qemu_set_irq(s->isa_irqs_in[pic_irq], + !!(s->pic_levels & + (((1ULL << PIIX_NUM_PIRQS) - 1) << + (pic_irq * PIIX_NUM_PIRQS)))); +} + +static void piix_set_pci_irq_level_internal(PIIXState *s, int pirq, int level) +{ + int pic_irq; + uint64_t mask; + + pic_irq = s->dev.config[PIIX_PIRQCA + pirq]; + if (pic_irq >= ISA_NUM_IRQS) { + return; + } + + mask = 1ULL << ((pic_irq * PIIX_NUM_PIRQS) + pirq); + s->pic_levels &= ~mask; + s->pic_levels |= mask * !!level; +} + +static void piix_set_pci_irq_level(PIIXState *s, int pirq, int level) +{ + int pic_irq; + + pic_irq = s->dev.config[PIIX_PIRQCA + pirq]; + if (pic_irq >= ISA_NUM_IRQS) { + return; + } + + piix_set_pci_irq_level_internal(s, pirq, level); + + piix_set_irq_pic(s, pic_irq); +} + +static void piix_set_pci_irq(void *opaque, int pirq, int level) +{ + PIIXState *s = opaque; + piix_set_pci_irq_level(s, pirq, level); +} + +static void piix_request_i8259_irq(void *opaque, int irq, int level) +{ + PIIXState *s = opaque; + qemu_set_irq(s->cpu_intr, level); +} + +static PCIINTxRoute piix_route_intx_pin_to_irq(void *opaque, int pin) +{ + PCIDevice *pci_dev = opaque; + int irq = pci_dev->config[PIIX_PIRQCA + pin]; + PCIINTxRoute route; + + if (irq < ISA_NUM_IRQS) { + route.mode = PCI_INTX_ENABLED; + route.irq = irq; + } else { + route.mode = PCI_INTX_DISABLED; + route.irq = -1; + } + return route; +} + +/* irq routing is changed. so rebuild bitmap */ +static void piix_update_pci_irq_levels(PIIXState *s) +{ + PCIBus *bus = pci_get_bus(&s->dev); + int pirq; + + s->pic_levels = 0; + for (pirq = 0; pirq < PIIX_NUM_PIRQS; pirq++) { + piix_set_pci_irq_level(s, pirq, pci_bus_get_irq_level(bus, pirq)); + } +} + +static void piix_write_config(PCIDevice *dev, uint32_t address, uint32_t val, + int len) +{ + pci_default_write_config(dev, address, val, len); + if (ranges_overlap(address, len, PIIX_PIRQCA, 4)) { + PIIXState *s = PIIX_PCI_DEVICE(dev); + int pic_irq; + + pci_bus_fire_intx_routing_notifier(pci_get_bus(&s->dev)); + piix_update_pci_irq_levels(s); + for (pic_irq = 0; pic_irq < ISA_NUM_IRQS; pic_irq++) { + piix_set_irq_pic(s, pic_irq); + } + } +} + +static void piix_reset(DeviceState *dev) +{ + PIIXState *d = PIIX_PCI_DEVICE(dev); + uint8_t *pci_conf = d->dev.config; + + pci_conf[0x04] = 0x07; /* master, memory and I/O */ + pci_conf[0x05] = 0x00; + pci_conf[0x06] = 0x00; + pci_conf[0x07] = 0x02; /* PCI_status_devsel_medium */ + pci_conf[0x4c] = 0x4d; + pci_conf[0x4e] = 0x03; + pci_conf[0x4f] = 0x00; + pci_conf[0x60] = 0x80; + pci_conf[0x61] = 0x80; + pci_conf[0x62] = 0x80; + pci_conf[0x63] = 0x80; + pci_conf[0x69] = 0x02; + pci_conf[0x70] = 0x80; + pci_conf[0x76] = 0x0c; + pci_conf[0x77] = 0x0c; + pci_conf[0x78] = 0x02; + pci_conf[0x79] = 0x00; + pci_conf[0x80] = 0x00; + pci_conf[0x82] = 0x00; + pci_conf[0xa0] = 0x08; + pci_conf[0xa2] = 0x00; + pci_conf[0xa3] = 0x00; + pci_conf[0xa4] = 0x00; + pci_conf[0xa5] = 0x00; + pci_conf[0xa6] = 0x00; + pci_conf[0xa7] = 0x00; + pci_conf[0xa8] = 0x0f; + pci_conf[0xaa] = 0x00; + pci_conf[0xab] = 0x00; + pci_conf[0xac] = 0x00; + pci_conf[0xae] = 0x00; + + d->pic_levels = 0; + d->rcr = 0; +} + +static int piix_post_load(void *opaque, int version_id) +{ + PIIXState *s = opaque; + int pirq; + + /* + * Because the i8259 has not been deserialized yet, qemu_irq_raise + * might bring the system to a different state than the saved one; + * for example, the interrupt could be masked but the i8259 would + * not know that yet and would trigger an interrupt in the CPU. + * + * Here, we update irq levels without raising the interrupt. + * Interrupt state will be deserialized separately through the i8259. + */ + s->pic_levels = 0; + for (pirq = 0; pirq < PIIX_NUM_PIRQS; pirq++) { + piix_set_pci_irq_level_internal(s, pirq, + pci_bus_get_irq_level(pci_get_bus(&s->dev), pirq)); + } + return 0; +} + +static int piix4_post_load(void *opaque, int version_id) +{ + PIIXState *s = opaque; + + if (version_id == 2) { + s->rcr = 0; + } + + return piix_post_load(opaque, version_id); +} + +static int piix3_pre_save(void *opaque) +{ + int i; + PIIXState *piix3 = opaque; + + for (i = 0; i < ARRAY_SIZE(piix3->pci_irq_levels_vmstate); i++) { + piix3->pci_irq_levels_vmstate[i] = + pci_bus_get_irq_level(pci_get_bus(&piix3->dev), i); + } + + return 0; +} + +static bool piix3_rcr_needed(void *opaque) +{ + PIIXState *piix3 = opaque; + + return (piix3->rcr != 0); +} + +static const VMStateDescription vmstate_piix3_rcr = { + .name = "PIIX3/rcr", + .version_id = 1, + .minimum_version_id = 1, + .needed = piix3_rcr_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT8(rcr, PIIXState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_piix3 = { + .name = "PIIX3", + .version_id = 3, + .minimum_version_id = 2, + .post_load = piix_post_load, + .pre_save = piix3_pre_save, + .fields = (const VMStateField[]) { + VMSTATE_PCI_DEVICE(dev, PIIXState), + VMSTATE_INT32_ARRAY_V(pci_irq_levels_vmstate, PIIXState, + PIIX_NUM_PIRQS, 3), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_piix3_rcr, + NULL + } +}; + +static const VMStateDescription vmstate_piix4 = { + .name = "PIIX4", + .version_id = 3, + .minimum_version_id = 2, + .post_load = piix4_post_load, + .fields = (const VMStateField[]) { + VMSTATE_PCI_DEVICE(dev, PIIXState), + VMSTATE_UINT8_V(rcr, PIIXState, 3), + VMSTATE_END_OF_LIST() + } +}; + +static void rcr_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) +{ + PIIXState *d = opaque; + + if (val & 4) { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + return; + } + d->rcr = val & 2; /* keep System Reset type only */ +} + +static uint64_t rcr_read(void *opaque, hwaddr addr, unsigned len) +{ + PIIXState *d = opaque; + + return d->rcr; +} + +static const MemoryRegionOps rcr_ops = { + .read = rcr_read, + .write = rcr_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static void pci_piix_realize(PCIDevice *dev, const char *uhci_type, + Error **errp) +{ + PIIXState *d = PIIX_PCI_DEVICE(dev); + PCIBus *pci_bus = pci_get_bus(dev); + ISABus *isa_bus; + uint32_t irq; + + isa_bus = isa_bus_new(DEVICE(d), pci_address_space(dev), + pci_address_space_io(dev), errp); + if (!isa_bus) { + return; + } + + memory_region_init_io(&d->rcr_mem, OBJECT(dev), &rcr_ops, d, + "piix-reset-control", 1); + memory_region_add_subregion_overlap(pci_address_space_io(dev), + PIIX_RCR_IOPORT, &d->rcr_mem, 1); + + /* PIC */ + if (d->has_pic) { + qemu_irq *i8259_out_irq = qemu_allocate_irqs(piix_request_i8259_irq, d, + 1); + qemu_irq *i8259 = i8259_init(isa_bus, *i8259_out_irq); + size_t i; + + for (i = 0; i < ISA_NUM_IRQS; i++) { + d->isa_irqs_in[i] = i8259[i]; + } + + g_free(i8259); + + qdev_init_gpio_out_named(DEVICE(dev), &d->cpu_intr, "intr", 1); + } + + isa_bus_register_input_irqs(isa_bus, d->isa_irqs_in); + + /* PIT */ + if (d->has_pit) { + i8254_pit_init(isa_bus, 0x40, 0, NULL); + } + + i8257_dma_init(OBJECT(dev), isa_bus, 0); + + /* RTC */ + qdev_prop_set_int32(DEVICE(&d->rtc), "base_year", 2000); + if (!qdev_realize(DEVICE(&d->rtc), BUS(isa_bus), errp)) { + return; + } + irq = object_property_get_uint(OBJECT(&d->rtc), "irq", &error_fatal); + isa_connect_gpio_out(ISA_DEVICE(&d->rtc), 0, irq); + + /* IDE */ + qdev_prop_set_int32(DEVICE(&d->ide), "addr", dev->devfn + 1); + if (!qdev_realize(DEVICE(&d->ide), BUS(pci_bus), errp)) { + return; + } + + /* USB */ + if (d->has_usb) { + object_initialize_child(OBJECT(dev), "uhci", &d->uhci, uhci_type); + qdev_prop_set_int32(DEVICE(&d->uhci), "addr", dev->devfn + 2); + if (!qdev_realize(DEVICE(&d->uhci), BUS(pci_bus), errp)) { + return; + } + } + + /* Power Management */ + if (d->has_acpi) { + object_initialize_child(OBJECT(d), "pm", &d->pm, TYPE_PIIX4_PM); + qdev_prop_set_int32(DEVICE(&d->pm), "addr", dev->devfn + 3); + qdev_prop_set_uint32(DEVICE(&d->pm), "smb_io_base", d->smb_io_base); + qdev_prop_set_bit(DEVICE(&d->pm), "smm-enabled", d->smm_enabled); + if (!qdev_realize(DEVICE(&d->pm), BUS(pci_bus), errp)) { + return; + } + qdev_connect_gpio_out(DEVICE(&d->pm), 0, d->isa_irqs_in[9]); + } + + pci_bus_irqs(pci_bus, piix_set_pci_irq, d, PIIX_NUM_PIRQS); + pci_bus_set_route_irq_fn(pci_bus, piix_route_intx_pin_to_irq); +} + +static void build_pci_isa_aml(AcpiDevAmlIf *adev, Aml *scope) +{ + Aml *field; + Aml *sb_scope = aml_scope("\\_SB"); + BusState *bus = qdev_get_child_bus(DEVICE(adev), "isa.0"); + + /* PIIX PCI to ISA irq remapping */ + aml_append(scope, aml_operation_region("P40C", AML_PCI_CONFIG, + aml_int(0x60), 0x04)); + /* Fields declarion has to happen *after* operation region */ + field = aml_field("PCI0.S08.P40C", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("PRQ0", 8)); + aml_append(field, aml_named_field("PRQ1", 8)); + aml_append(field, aml_named_field("PRQ2", 8)); + aml_append(field, aml_named_field("PRQ3", 8)); + aml_append(sb_scope, field); + aml_append(scope, sb_scope); + + qbus_build_aml(bus, scope); +} + +static void pci_piix_init(Object *obj) +{ + PIIXState *d = PIIX_PCI_DEVICE(obj); + + qdev_init_gpio_out_named(DEVICE(obj), d->isa_irqs_in, "isa-irqs", + ISA_NUM_IRQS); + + object_initialize_child(obj, "rtc", &d->rtc, TYPE_MC146818_RTC); +} + +static Property pci_piix_props[] = { + DEFINE_PROP_UINT32("smb_io_base", PIIXState, smb_io_base, 0), + DEFINE_PROP_BOOL("has-acpi", PIIXState, has_acpi, true), + DEFINE_PROP_BOOL("has-pic", PIIXState, has_pic, true), + DEFINE_PROP_BOOL("has-pit", PIIXState, has_pit, true), + DEFINE_PROP_BOOL("has-usb", PIIXState, has_usb, true), + DEFINE_PROP_BOOL("smm-enabled", PIIXState, smm_enabled, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pci_piix_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); + + k->config_write = piix_write_config; + dc->reset = piix_reset; + dc->desc = "ISA bridge"; + dc->hotpluggable = false; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->class_id = PCI_CLASS_BRIDGE_ISA; + /* + * Reason: part of PIIX southbridge, needs to be wired up by e.g. + * pc_piix.c's pc_init1() + */ + dc->user_creatable = false; + device_class_set_props(dc, pci_piix_props); + adevc->build_dev_aml = build_pci_isa_aml; +} + +static const TypeInfo piix_pci_type_info = { + .name = TYPE_PIIX_PCI_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PIIXState), + .instance_init = pci_piix_init, + .abstract = true, + .class_init = pci_piix_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { TYPE_ACPI_DEV_AML_IF }, + { }, + }, +}; + +static void piix3_realize(PCIDevice *dev, Error **errp) +{ + pci_piix_realize(dev, TYPE_PIIX3_USB_UHCI, errp); +} + +static void piix3_init(Object *obj) +{ + PIIXState *d = PIIX_PCI_DEVICE(obj); + + object_initialize_child(obj, "ide", &d->ide, TYPE_PIIX3_IDE); +} + +static void piix3_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = piix3_realize; + /* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */ + k->device_id = PCI_DEVICE_ID_INTEL_82371SB_0; + dc->vmsd = &vmstate_piix3; +} + +static const TypeInfo piix3_info = { + .name = TYPE_PIIX3_DEVICE, + .parent = TYPE_PIIX_PCI_DEVICE, + .instance_init = piix3_init, + .class_init = piix3_class_init, +}; + +static void piix4_realize(PCIDevice *dev, Error **errp) +{ + pci_piix_realize(dev, TYPE_PIIX4_USB_UHCI, errp); +} + +static void piix4_init(Object *obj) +{ + PIIXState *s = PIIX_PCI_DEVICE(obj); + + object_initialize_child(obj, "ide", &s->ide, TYPE_PIIX4_IDE); +} + +static void piix4_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = piix4_realize; + k->device_id = PCI_DEVICE_ID_INTEL_82371AB_0; + dc->vmsd = &vmstate_piix4; +} + +static const TypeInfo piix4_info = { + .name = TYPE_PIIX4_PCI_DEVICE, + .parent = TYPE_PIIX_PCI_DEVICE, + .instance_init = piix4_init, + .class_init = piix4_class_init, +}; + +static void piix3_register_types(void) +{ + type_register_static(&piix_pci_type_info); + type_register_static(&piix3_info); + type_register_static(&piix4_info); +} + +type_init(piix3_register_types) diff --git a/hw/isa/piix3.c b/hw/isa/piix3.c deleted file mode 100644 index dab901c9ad..0000000000 --- a/hw/isa/piix3.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * QEMU PIIX PCI ISA Bridge Emulation - * - * Copyright (c) 2006 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/range.h" -#include "hw/southbridge/piix.h" -#include "hw/irq.h" -#include "hw/isa/isa.h" -#include "hw/xen/xen.h" -#include "sysemu/xen.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "migration/vmstate.h" - -#define XEN_PIIX_NUM_PIRQS 128ULL - -#define TYPE_PIIX3_DEVICE "PIIX3" -#define TYPE_PIIX3_XEN_DEVICE "PIIX3-xen" - -static void piix3_set_irq_pic(PIIX3State *piix3, int pic_irq) -{ - qemu_set_irq(piix3->pic[pic_irq], - !!(piix3->pic_levels & - (((1ULL << PIIX_NUM_PIRQS) - 1) << - (pic_irq * PIIX_NUM_PIRQS)))); -} - -static void piix3_set_irq_level_internal(PIIX3State *piix3, int pirq, int level) -{ - int pic_irq; - uint64_t mask; - - pic_irq = piix3->dev.config[PIIX_PIRQCA + pirq]; - if (pic_irq >= PIIX_NUM_PIC_IRQS) { - return; - } - - mask = 1ULL << ((pic_irq * PIIX_NUM_PIRQS) + pirq); - piix3->pic_levels &= ~mask; - piix3->pic_levels |= mask * !!level; -} - -static void piix3_set_irq_level(PIIX3State *piix3, int pirq, int level) -{ - int pic_irq; - - pic_irq = piix3->dev.config[PIIX_PIRQCA + pirq]; - if (pic_irq >= PIIX_NUM_PIC_IRQS) { - return; - } - - piix3_set_irq_level_internal(piix3, pirq, level); - - piix3_set_irq_pic(piix3, pic_irq); -} - -static void piix3_set_irq(void *opaque, int pirq, int level) -{ - PIIX3State *piix3 = opaque; - piix3_set_irq_level(piix3, pirq, level); -} - -static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pin) -{ - PIIX3State *piix3 = opaque; - int irq = piix3->dev.config[PIIX_PIRQCA + pin]; - PCIINTxRoute route; - - if (irq < PIIX_NUM_PIC_IRQS) { - route.mode = PCI_INTX_ENABLED; - route.irq = irq; - } else { - route.mode = PCI_INTX_DISABLED; - route.irq = -1; - } - return route; -} - -/* irq routing is changed. so rebuild bitmap */ -static void piix3_update_irq_levels(PIIX3State *piix3) -{ - PCIBus *bus = pci_get_bus(&piix3->dev); - int pirq; - - piix3->pic_levels = 0; - for (pirq = 0; pirq < PIIX_NUM_PIRQS; pirq++) { - piix3_set_irq_level(piix3, pirq, pci_bus_get_irq_level(bus, pirq)); - } -} - -static void piix3_write_config(PCIDevice *dev, - uint32_t address, uint32_t val, int len) -{ - pci_default_write_config(dev, address, val, len); - if (ranges_overlap(address, len, PIIX_PIRQCA, 4)) { - PIIX3State *piix3 = PIIX3_PCI_DEVICE(dev); - int pic_irq; - - pci_bus_fire_intx_routing_notifier(pci_get_bus(&piix3->dev)); - piix3_update_irq_levels(piix3); - for (pic_irq = 0; pic_irq < PIIX_NUM_PIC_IRQS; pic_irq++) { - piix3_set_irq_pic(piix3, pic_irq); - } - } -} - -static void piix3_write_config_xen(PCIDevice *dev, - uint32_t address, uint32_t val, int len) -{ - xen_piix_pci_write_config_client(address, val, len); - piix3_write_config(dev, address, val, len); -} - -static void piix3_reset(void *opaque) -{ - PIIX3State *d = opaque; - uint8_t *pci_conf = d->dev.config; - - pci_conf[0x04] = 0x07; /* master, memory and I/O */ - pci_conf[0x05] = 0x00; - pci_conf[0x06] = 0x00; - pci_conf[0x07] = 0x02; /* PCI_status_devsel_medium */ - pci_conf[0x4c] = 0x4d; - pci_conf[0x4e] = 0x03; - pci_conf[0x4f] = 0x00; - pci_conf[0x60] = 0x80; - pci_conf[0x61] = 0x80; - pci_conf[0x62] = 0x80; - pci_conf[0x63] = 0x80; - pci_conf[0x69] = 0x02; - pci_conf[0x70] = 0x80; - pci_conf[0x76] = 0x0c; - pci_conf[0x77] = 0x0c; - pci_conf[0x78] = 0x02; - pci_conf[0x79] = 0x00; - pci_conf[0x80] = 0x00; - pci_conf[0x82] = 0x00; - pci_conf[0xa0] = 0x08; - pci_conf[0xa2] = 0x00; - pci_conf[0xa3] = 0x00; - pci_conf[0xa4] = 0x00; - pci_conf[0xa5] = 0x00; - pci_conf[0xa6] = 0x00; - pci_conf[0xa7] = 0x00; - pci_conf[0xa8] = 0x0f; - pci_conf[0xaa] = 0x00; - pci_conf[0xab] = 0x00; - pci_conf[0xac] = 0x00; - pci_conf[0xae] = 0x00; - - d->pic_levels = 0; - d->rcr = 0; -} - -static int piix3_post_load(void *opaque, int version_id) -{ - PIIX3State *piix3 = opaque; - int pirq; - - /* - * Because the i8259 has not been deserialized yet, qemu_irq_raise - * might bring the system to a different state than the saved one; - * for example, the interrupt could be masked but the i8259 would - * not know that yet and would trigger an interrupt in the CPU. - * - * Here, we update irq levels without raising the interrupt. - * Interrupt state will be deserialized separately through the i8259. - */ - piix3->pic_levels = 0; - for (pirq = 0; pirq < PIIX_NUM_PIRQS; pirq++) { - piix3_set_irq_level_internal(piix3, pirq, - pci_bus_get_irq_level(pci_get_bus(&piix3->dev), pirq)); - } - return 0; -} - -static int piix3_pre_save(void *opaque) -{ - int i; - PIIX3State *piix3 = opaque; - - for (i = 0; i < ARRAY_SIZE(piix3->pci_irq_levels_vmstate); i++) { - piix3->pci_irq_levels_vmstate[i] = - pci_bus_get_irq_level(pci_get_bus(&piix3->dev), i); - } - - return 0; -} - -static bool piix3_rcr_needed(void *opaque) -{ - PIIX3State *piix3 = opaque; - - return (piix3->rcr != 0); -} - -static const VMStateDescription vmstate_piix3_rcr = { - .name = "PIIX3/rcr", - .version_id = 1, - .minimum_version_id = 1, - .needed = piix3_rcr_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(rcr, PIIX3State), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_piix3 = { - .name = "PIIX3", - .version_id = 3, - .minimum_version_id = 2, - .post_load = piix3_post_load, - .pre_save = piix3_pre_save, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, PIIX3State), - VMSTATE_INT32_ARRAY_V(pci_irq_levels_vmstate, PIIX3State, - PIIX_NUM_PIRQS, 3), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_piix3_rcr, - NULL - } -}; - - -static void rcr_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) -{ - PIIX3State *d = opaque; - - if (val & 4) { - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - return; - } - d->rcr = val & 2; /* keep System Reset type only */ -} - -static uint64_t rcr_read(void *opaque, hwaddr addr, unsigned len) -{ - PIIX3State *d = opaque; - - return d->rcr; -} - -static const MemoryRegionOps rcr_ops = { - .read = rcr_read, - .write = rcr_write, - .endianness = DEVICE_LITTLE_ENDIAN -}; - -static void piix3_realize(PCIDevice *dev, Error **errp) -{ - PIIX3State *d = PIIX3_PCI_DEVICE(dev); - - if (!isa_bus_new(DEVICE(d), get_system_memory(), - pci_address_space_io(dev), errp)) { - return; - } - - memory_region_init_io(&d->rcr_mem, OBJECT(dev), &rcr_ops, d, - "piix3-reset-control", 1); - memory_region_add_subregion_overlap(pci_address_space_io(dev), - PIIX_RCR_IOPORT, &d->rcr_mem, 1); - - qemu_register_reset(piix3_reset, d); -} - -static void pci_piix3_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - dc->desc = "ISA bridge"; - dc->vmsd = &vmstate_piix3; - dc->hotpluggable = false; - k->realize = piix3_realize; - k->vendor_id = PCI_VENDOR_ID_INTEL; - /* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */ - k->device_id = PCI_DEVICE_ID_INTEL_82371SB_0; - k->class_id = PCI_CLASS_BRIDGE_ISA; - /* - * Reason: part of PIIX3 southbridge, needs to be wired up by - * pc_piix.c's pc_init1() - */ - dc->user_creatable = false; -} - -static const TypeInfo piix3_pci_type_info = { - .name = TYPE_PIIX3_PCI_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PIIX3State), - .abstract = true, - .class_init = pci_piix3_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, - }, -}; - -static void piix3_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->config_write = piix3_write_config; -} - -static const TypeInfo piix3_info = { - .name = TYPE_PIIX3_DEVICE, - .parent = TYPE_PIIX3_PCI_DEVICE, - .class_init = piix3_class_init, -}; - -static void piix3_xen_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->config_write = piix3_write_config_xen; -}; - -static const TypeInfo piix3_xen_info = { - .name = TYPE_PIIX3_XEN_DEVICE, - .parent = TYPE_PIIX3_PCI_DEVICE, - .class_init = piix3_xen_class_init, -}; - -static void piix3_register_types(void) -{ - type_register_static(&piix3_pci_type_info); - type_register_static(&piix3_info); - type_register_static(&piix3_xen_info); -} - -type_init(piix3_register_types) - -/* - * Return the global irq number corresponding to a given device irq - * pin. We could also use the bus number to have a more precise mapping. - */ -static int pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx) -{ - int slot_addend; - slot_addend = PCI_SLOT(pci_dev->devfn) - 1; - return (pci_intx + slot_addend) & 3; -} - -PIIX3State *piix3_create(PCIBus *pci_bus, ISABus **isa_bus) -{ - PIIX3State *piix3; - PCIDevice *pci_dev; - - /* - * Xen supports additional interrupt routes from the PCI devices to - * the IOAPIC: the four pins of each PCI device on the bus are also - * connected to the IOAPIC directly. - * These additional routes can be discovered through ACPI. - */ - if (xen_enabled()) { - pci_dev = pci_create_simple_multifunction(pci_bus, -1, true, - TYPE_PIIX3_XEN_DEVICE); - piix3 = PIIX3_PCI_DEVICE(pci_dev); - pci_bus_irqs(pci_bus, xen_piix3_set_irq, xen_pci_slot_get_pirq, - piix3, XEN_PIIX_NUM_PIRQS); - } else { - pci_dev = pci_create_simple_multifunction(pci_bus, -1, true, - TYPE_PIIX3_DEVICE); - piix3 = PIIX3_PCI_DEVICE(pci_dev); - pci_bus_irqs(pci_bus, piix3_set_irq, pci_slot_get_pirq, - piix3, PIIX_NUM_PIRQS); - pci_bus_set_route_irq_fn(pci_bus, piix3_route_intx_pin_to_irq); - } - *isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(piix3), "isa.0")); - - return piix3; -} diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c deleted file mode 100644 index 8607e0ac36..0000000000 --- a/hw/isa/piix4.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * QEMU PIIX4 PCI Bridge Emulation - * - * Copyright (c) 2006 Fabrice Bellard - * Copyright (c) 2018 Hervé Poussineau - * - * 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/irq.h" -#include "hw/southbridge/piix.h" -#include "hw/pci/pci.h" -#include "hw/isa/isa.h" -#include "hw/intc/i8259.h" -#include "hw/dma/i8257.h" -#include "hw/timer/i8254.h" -#include "hw/rtc/mc146818rtc.h" -#include "hw/ide/pci.h" -#include "migration/vmstate.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "qom/object.h" - -struct PIIX4State { - PCIDevice dev; - qemu_irq cpu_intr; - qemu_irq *isa; - - RTCState rtc; - /* Reset Control Register */ - MemoryRegion rcr_mem; - uint8_t rcr; -}; - -OBJECT_DECLARE_SIMPLE_TYPE(PIIX4State, PIIX4_PCI_DEVICE) - -static void piix4_set_irq(void *opaque, int irq_num, int level) -{ - int i, pic_irq, pic_level; - PIIX4State *s = opaque; - PCIBus *bus = pci_get_bus(&s->dev); - - /* now we change the pic irq level according to the piix irq mappings */ - /* XXX: optimize */ - pic_irq = s->dev.config[PIIX_PIRQCA + irq_num]; - if (pic_irq < ISA_NUM_IRQS) { - /* The pic level is the logical OR of all the PCI irqs mapped to it. */ - pic_level = 0; - for (i = 0; i < PIIX_NUM_PIRQS; i++) { - if (pic_irq == s->dev.config[PIIX_PIRQCA + i]) { - pic_level |= pci_bus_get_irq_level(bus, i); - } - } - qemu_set_irq(s->isa[pic_irq], pic_level); - } -} - -static void piix4_isa_reset(DeviceState *dev) -{ - PIIX4State *d = PIIX4_PCI_DEVICE(dev); - uint8_t *pci_conf = d->dev.config; - - pci_conf[0x04] = 0x07; // master, memory and I/O - pci_conf[0x05] = 0x00; - pci_conf[0x06] = 0x00; - pci_conf[0x07] = 0x02; // PCI_status_devsel_medium - pci_conf[0x4c] = 0x4d; - pci_conf[0x4e] = 0x03; - pci_conf[0x4f] = 0x00; - pci_conf[0x60] = 0x0a; // PCI A -> IRQ 10 - pci_conf[0x61] = 0x0a; // PCI B -> IRQ 10 - pci_conf[0x62] = 0x0b; // PCI C -> IRQ 11 - pci_conf[0x63] = 0x0b; // PCI D -> IRQ 11 - pci_conf[0x69] = 0x02; - pci_conf[0x70] = 0x80; - pci_conf[0x76] = 0x0c; - pci_conf[0x77] = 0x0c; - pci_conf[0x78] = 0x02; - pci_conf[0x79] = 0x00; - pci_conf[0x80] = 0x00; - pci_conf[0x82] = 0x00; - pci_conf[0xa0] = 0x08; - pci_conf[0xa2] = 0x00; - pci_conf[0xa3] = 0x00; - pci_conf[0xa4] = 0x00; - pci_conf[0xa5] = 0x00; - pci_conf[0xa6] = 0x00; - pci_conf[0xa7] = 0x00; - pci_conf[0xa8] = 0x0f; - pci_conf[0xaa] = 0x00; - pci_conf[0xab] = 0x00; - pci_conf[0xac] = 0x00; - pci_conf[0xae] = 0x00; -} - -static int piix4_ide_post_load(void *opaque, int version_id) -{ - PIIX4State *s = opaque; - - if (version_id == 2) { - s->rcr = 0; - } - - return 0; -} - -static const VMStateDescription vmstate_piix4 = { - .name = "PIIX4", - .version_id = 3, - .minimum_version_id = 2, - .post_load = piix4_ide_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, PIIX4State), - VMSTATE_UINT8_V(rcr, PIIX4State, 3), - VMSTATE_END_OF_LIST() - } -}; - -static void piix4_request_i8259_irq(void *opaque, int irq, int level) -{ - PIIX4State *s = opaque; - qemu_set_irq(s->cpu_intr, level); -} - -static void piix4_set_i8259_irq(void *opaque, int irq, int level) -{ - PIIX4State *s = opaque; - qemu_set_irq(s->isa[irq], level); -} - -static void piix4_rcr_write(void *opaque, hwaddr addr, uint64_t val, - unsigned int len) -{ - PIIX4State *s = opaque; - - if (val & 4) { - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - return; - } - - s->rcr = val & 2; /* keep System Reset type only */ -} - -static uint64_t piix4_rcr_read(void *opaque, hwaddr addr, unsigned int len) -{ - PIIX4State *s = opaque; - - return s->rcr; -} - -static const MemoryRegionOps piix4_rcr_ops = { - .read = piix4_rcr_read, - .write = piix4_rcr_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void piix4_realize(PCIDevice *dev, Error **errp) -{ - PIIX4State *s = PIIX4_PCI_DEVICE(dev); - ISABus *isa_bus; - qemu_irq *i8259_out_irq; - - isa_bus = isa_bus_new(DEVICE(dev), pci_address_space(dev), - pci_address_space_io(dev), errp); - if (!isa_bus) { - return; - } - - qdev_init_gpio_in_named(DEVICE(dev), piix4_set_i8259_irq, - "isa", ISA_NUM_IRQS); - qdev_init_gpio_out_named(DEVICE(dev), &s->cpu_intr, - "intr", 1); - - memory_region_init_io(&s->rcr_mem, OBJECT(dev), &piix4_rcr_ops, s, - "reset-control", 1); - memory_region_add_subregion_overlap(pci_address_space_io(dev), - PIIX_RCR_IOPORT, &s->rcr_mem, 1); - - /* initialize i8259 pic */ - i8259_out_irq = qemu_allocate_irqs(piix4_request_i8259_irq, s, 1); - s->isa = i8259_init(isa_bus, *i8259_out_irq); - - /* initialize ISA irqs */ - isa_bus_irqs(isa_bus, s->isa); - - /* initialize pit */ - i8254_pit_init(isa_bus, 0x40, 0, NULL); - - /* DMA */ - i8257_dma_init(isa_bus, 0); - - /* RTC */ - qdev_prop_set_int32(DEVICE(&s->rtc), "base_year", 2000); - if (!qdev_realize(DEVICE(&s->rtc), BUS(isa_bus), errp)) { - return; - } - s->rtc.irq = isa_get_irq(ISA_DEVICE(&s->rtc), s->rtc.isairq); -} - -static void piix4_init(Object *obj) -{ - PIIX4State *s = PIIX4_PCI_DEVICE(obj); - - object_initialize(&s->rtc, sizeof(s->rtc), TYPE_MC146818_RTC); -} - -static void piix4_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = piix4_realize; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82371AB_0; - k->class_id = PCI_CLASS_BRIDGE_ISA; - dc->reset = piix4_isa_reset; - dc->desc = "ISA bridge"; - dc->vmsd = &vmstate_piix4; - /* - * Reason: part of PIIX4 southbridge, needs to be wired up, - * e.g. by mips_malta_init() - */ - dc->user_creatable = false; - dc->hotpluggable = false; -} - -static const TypeInfo piix4_info = { - .name = TYPE_PIIX4_PCI_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PIIX4State), - .instance_init = piix4_init, - .class_init = piix4_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, - }, -}; - -static void piix4_register_types(void) -{ - type_register_static(&piix4_info); -} - -type_init(piix4_register_types) - -static int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) -{ - int slot; - - slot = PCI_SLOT(pci_dev->devfn); - - switch (slot) { - /* PIIX4 USB */ - case 10: - return 3; - /* AMD 79C973 Ethernet */ - case 11: - return 1; - /* Crystal 4281 Sound */ - case 12: - return 2; - /* PCI slot 1 to 4 */ - case 18 ... 21: - return ((slot - 18) + irq_num) & 0x03; - /* Unknown device, don't do any translation */ - default: - return irq_num; - } -} - -DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus) -{ - PIIX4State *s; - PCIDevice *pci; - DeviceState *dev; - int devfn = PCI_DEVFN(10, 0); - - pci = pci_create_simple_multifunction(pci_bus, devfn, true, - TYPE_PIIX4_PCI_DEVICE); - dev = DEVICE(pci); - s = PIIX4_PCI_DEVICE(pci); - if (isa_bus) { - *isa_bus = ISA_BUS(qdev_get_child_bus(dev, "isa.0")); - } - - 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, devfn + 3, 0x1100, - qdev_get_gpio_in_named(dev, "isa", 9), - NULL, 0, NULL); - } - - pci_bus_irqs(pci_bus, piix4_set_irq, pci_slot_get_pirq, s, PIIX_NUM_PIRQS); - - return dev; -} diff --git a/hw/isa/smc37c669-superio.c b/hw/isa/smc37c669-superio.c index 18287741cb..d2e58c9a89 100644 --- a/hw/isa/smc37c669-superio.c +++ b/hw/isa/smc37c669-superio.c @@ -14,11 +14,6 @@ /* UARTs (compatible with NS16450 or PC16550) */ -static bool is_serial_enabled(ISASuperIODevice *sio, uint8_t index) -{ - return index < 2; -} - static uint16_t get_serial_iobase(ISASuperIODevice *sio, uint8_t index) { return index ? 0x2f8 : 0x3f8; @@ -31,11 +26,6 @@ static unsigned int get_serial_irq(ISASuperIODevice *sio, uint8_t index) /* Parallel port */ -static bool is_parallel_enabled(ISASuperIODevice *sio, uint8_t index) -{ - return index < 1; -} - static uint16_t get_parallel_iobase(ISASuperIODevice *sio, uint8_t index) { return 0x378; @@ -53,11 +43,6 @@ static unsigned int get_parallel_dma(ISASuperIODevice *sio, uint8_t index) /* Diskette controller (Software compatible with the Intel PC8477) */ -static bool is_fdc_enabled(ISASuperIODevice *sio, uint8_t index) -{ - return index < 1; -} - static uint16_t get_fdc_iobase(ISASuperIODevice *sio, uint8_t index) { return 0x3f0; @@ -79,20 +64,17 @@ static void smc37c669_class_init(ObjectClass *klass, void *data) sc->parallel = (ISASuperIOFuncs){ .count = 1, - .is_enabled = is_parallel_enabled, .get_iobase = get_parallel_iobase, .get_irq = get_parallel_irq, .get_dma = get_parallel_dma, }; sc->serial = (ISASuperIOFuncs){ .count = 2, - .is_enabled = is_serial_enabled, .get_iobase = get_serial_iobase, .get_irq = get_serial_irq, }; sc->floppy = (ISASuperIOFuncs){ .count = 1, - .is_enabled = is_fdc_enabled, .get_iobase = get_fdc_iobase, .get_irq = get_fdc_irq, .get_dma = get_fdc_dma, @@ -103,7 +85,6 @@ static void smc37c669_class_init(ObjectClass *klass, void *data) static const TypeInfo smc37c669_type_info = { .name = TYPE_SMC37C669_SUPERIO, .parent = TYPE_ISA_SUPERIO, - .instance_size = sizeof(ISASuperIODevice), .class_size = sizeof(ISASuperIOClass), .class_init = smc37c669_class_init, }; diff --git a/hw/isa/trace-events b/hw/isa/trace-events index b8f877e1ed..1816e8307a 100644 --- a/hw/isa/trace-events +++ b/hw/isa/trace-events @@ -16,8 +16,13 @@ apm_io_write(uint8_t addr, uint8_t val) "write addr=0x%x val=0x%02x" # vt82c686.c via_isa_write(uint32_t addr, uint32_t val, int len) "addr 0x%x val 0x%x len 0x%x" +via_pm_read(uint32_t addr, uint32_t val, int len) "addr 0x%x val 0x%x len 0x%x" via_pm_write(uint32_t addr, uint32_t val, int len) "addr 0x%x val 0x%x len 0x%x" via_pm_io_read(uint32_t addr, uint32_t val, int len) "addr 0x%x val 0x%x len 0x%x" via_pm_io_write(uint32_t addr, uint32_t val, int len) "addr 0x%x val 0x%x len 0x%x" via_superio_read(uint8_t addr, uint8_t val) "addr 0x%x val 0x%x" via_superio_write(uint8_t addr, uint32_t val) "addr 0x%x val 0x%x" + +# lpc_ich9.c +ich9_cc_write(uint64_t addr, uint64_t val, unsigned len) "addr=0x%"PRIx64 " val=0x%"PRIx64 " len=%u" +ich9_cc_read(uint64_t addr, uint64_t val, unsigned len) "addr=0x%"PRIx64 " val=0x%"PRIx64 " len=%u" diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 8f656251b8..8582ac0322 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -15,13 +15,18 @@ #include "qemu/osdep.h" #include "hw/isa/vt82c686.h" +#include "hw/block/fdc.h" +#include "hw/char/parallel-isa.h" +#include "hw/char/serial.h" #include "hw/pci/pci.h" #include "hw/qdev-properties.h" +#include "hw/ide/pci.h" #include "hw/isa/isa.h" #include "hw/isa/superio.h" #include "hw/intc/i8259.h" #include "hw/irq.h" #include "hw/dma/i8257.h" +#include "hw/usb/hcd-uhci.h" #include "hw/timer/i8254.h" #include "hw/rtc/mc146818rtc.h" #include "migration/vmstate.h" @@ -80,7 +85,7 @@ static const VMStateDescription vmstate_acpi = { .version_id = 1, .minimum_version_id = 1, .post_load = vmstate_acpi_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, ViaPMState), VMSTATE_UINT16(ar.pm1.evt.sts, ViaPMState), VMSTATE_UINT16(ar.pm1.evt.en, ViaPMState), @@ -248,6 +253,8 @@ static const ViaPMInitInfo vt82c686b_pm_init_info = { .device_id = PCI_DEVICE_ID_VIA_82C686B_PM, }; +#define TYPE_VT82C686B_PM "vt82c686b-pm" + static const TypeInfo vt82c686b_pm_info = { .name = TYPE_VT82C686B_PM, .parent = TYPE_VIA_PM, @@ -259,6 +266,8 @@ static const ViaPMInitInfo vt8231_pm_init_info = { .device_id = PCI_DEVICE_ID_VIA_8231_PM, }; +#define TYPE_VT8231_PM "vt8231-pm" + static const TypeInfo vt8231_pm_info = { .name = TYPE_VT8231_PM, .parent = TYPE_VIA_PM, @@ -317,13 +326,24 @@ static uint64_t via_superio_cfg_read(void *opaque, hwaddr addr, unsigned size) return val; } +static void via_superio_devices_enable(ViaSuperIOState *s, uint8_t data) +{ + ISASuperIOClass *ic = ISA_SUPERIO_GET_CLASS(s); + + isa_parallel_set_enabled(s->superio.parallel[0], (data & 0x3) != 3); + for (int i = 0; i < ic->serial.count; i++) { + isa_serial_set_enabled(s->superio.serial[i], data & BIT(i + 2)); + } + isa_fdc_set_enabled(s->superio.floppy, data & BIT(4)); +} + static void via_superio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); - sc->parent_realize = dc->realize; - dc->realize = via_superio_realize; + device_class_set_parent_realize(dc, via_superio_realize, + &sc->parent_realize); } static const TypeInfo via_superio_info = { @@ -362,7 +382,25 @@ static void vt82c686b_superio_cfg_write(void *opaque, hwaddr addr, case 0xfd ... 0xff: /* ignore write to read only registers */ return; - /* case 0xe6 ... 0xe8: Should set base port of parallel and serial */ + case 0xe2: + data &= 0x1f; + via_superio_devices_enable(sc, data); + break; + case 0xe3: + data &= 0xfc; + isa_fdc_set_iobase(sc->superio.floppy, data << 2); + break; + case 0xe6: + isa_parallel_set_iobase(sc->superio.parallel[0], data << 2); + break; + case 0xe7: + data &= 0xfe; + isa_serial_set_iobase(sc->superio.serial[0], data << 2); + break; + case 0xe8: + data &= 0xfe; + isa_serial_set_iobase(sc->superio.serial[1], data << 2); + break; default: qemu_log_mask(LOG_UNIMP, "via_superio_cfg: unimplemented register 0x%x\n", idx); @@ -389,9 +427,14 @@ static void vt82c686b_superio_reset(DeviceState *dev) /* Device ID */ vt82c686b_superio_cfg_write(s, 0, 0xe0, 1); vt82c686b_superio_cfg_write(s, 1, 0x3c, 1); - /* Function select - all disabled */ + /* + * Function select - only serial enabled + * Fuloong 2e's rescue-yl prints to the serial console w/o enabling it. This + * suggests that the serial ports are enabled by default, so override the + * datasheet. + */ vt82c686b_superio_cfg_write(s, 0, 0xe2, 1); - vt82c686b_superio_cfg_write(s, 1, 0x03, 1); + vt82c686b_superio_cfg_write(s, 1, 0x0f, 1); /* Floppy ctrl base addr 0x3f0-7 */ vt82c686b_superio_cfg_write(s, 0, 0xe3, 1); vt82c686b_superio_cfg_write(s, 1, 0xfc, 1); @@ -459,6 +502,21 @@ static void vt8231_superio_cfg_write(void *opaque, hwaddr addr, case 0xfd: /* ignore write to read only registers */ return; + case 0xf2: + data &= 0x17; + via_superio_devices_enable(sc, data); + break; + case 0xf4: + data &= 0xfe; + isa_serial_set_iobase(sc->superio.serial[0], data << 2); + break; + case 0xf6: + isa_parallel_set_iobase(sc->superio.parallel[0], data << 2); + break; + case 0xf7: + data &= 0xfc; + isa_fdc_set_iobase(sc->superio.floppy, data << 2); + break; default: qemu_log_mask(LOG_UNIMP, "via_superio_cfg: unimplemented register 0x%x\n", idx); @@ -507,12 +565,6 @@ static void vt8231_superio_init(Object *obj) VIA_SUPERIO(obj)->io_ops = &vt8231_superio_cfg_ops; } -static uint16_t vt8231_superio_serial_iobase(ISASuperIODevice *sio, - uint8_t index) -{ - return 0x2f8; /* FIXME: This should be settable via registers f2-f4 */ -} - static void vt8231_superio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -520,7 +572,6 @@ static void vt8231_superio_class_init(ObjectClass *klass, void *data) dc->reset = vt8231_superio_reset; sc->serial.count = 1; - sc->serial.get_iobase = vt8231_superio_serial_iobase; sc->parallel.count = 1; sc->ide.count = 0; /* emulated by via-ide */ sc->floppy.count = 1; @@ -542,25 +593,44 @@ OBJECT_DECLARE_SIMPLE_TYPE(ViaISAState, VIA_ISA) struct ViaISAState { PCIDevice dev; qemu_irq cpu_intr; - qemu_irq *isa_irqs; - ISABus *isa_bus; - ViaSuperIOState *via_sio; + qemu_irq *isa_irqs_in; + uint16_t irq_state[ISA_NUM_IRQS]; + ViaSuperIOState via_sio; + MC146818RtcState rtc; + PCIIDEState ide; + UHCIState uhci[2]; + ViaPMState pm; + ViaAC97State ac97; + PCIDevice mc97; }; static const VMStateDescription vmstate_via = { .name = "via-isa", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, ViaISAState), VMSTATE_END_OF_LIST() } }; +static void via_isa_init(Object *obj) +{ + ViaISAState *s = VIA_ISA(obj); + + object_initialize_child(obj, "rtc", &s->rtc, TYPE_MC146818_RTC); + object_initialize_child(obj, "ide", &s->ide, TYPE_VIA_IDE); + object_initialize_child(obj, "uhci1", &s->uhci[0], TYPE_VT82C686B_USB_UHCI); + object_initialize_child(obj, "uhci2", &s->uhci[1], TYPE_VT82C686B_USB_UHCI); + object_initialize_child(obj, "ac97", &s->ac97, TYPE_VIA_AC97); + object_initialize_child(obj, "mc97", &s->mc97, TYPE_VIA_MC97); +} + static const TypeInfo via_isa_info = { .name = TYPE_VIA_ISA, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(ViaISAState), + .instance_init = via_isa_init, .abstract = true, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, @@ -568,10 +638,70 @@ static const TypeInfo via_isa_info = { }, }; -void via_isa_set_irq(PCIDevice *d, int n, int level) +static int via_isa_get_pci_irq(const ViaISAState *s, int pin) { - ViaISAState *s = VIA_ISA(d); - qemu_set_irq(s->isa_irqs[n], level); + switch (pin) { + case 0: + return s->dev.config[0x55] >> 4; + case 1: + return s->dev.config[0x56] & 0xf; + case 2: + return s->dev.config[0x56] >> 4; + case 3: + return s->dev.config[0x57] >> 4; + } + return 0; +} + +void via_isa_set_irq(PCIDevice *d, int pin, int level) +{ + ViaISAState *s = VIA_ISA(pci_get_function_0(d)); + uint8_t irq = d->config[PCI_INTERRUPT_LINE], max_irq = 15; + int f = PCI_FUNC(d->devfn); + uint16_t mask; + + switch (f) { + case 0: /* PIRQ/PINT inputs */ + irq = via_isa_get_pci_irq(s, pin); + f = 8 + pin; /* Use function 8-11 for PCI interrupt inputs */ + break; + case 2: /* USB ports 0-1 */ + case 3: /* USB ports 2-3 */ + case 5: /* AC97 audio */ + max_irq = 14; + break; + } + + /* Keep track of the state of all sources */ + mask = BIT(f); + if (level) { + s->irq_state[0] |= mask; + } else { + s->irq_state[0] &= ~mask; + } + if (irq == 0 || irq == 0xff) { + return; /* disabled */ + } + if (unlikely(irq > max_irq || irq == 2)) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid ISA IRQ routing %d for %d", + irq, f); + return; + } + /* Record source state at mapped IRQ */ + if (level) { + s->irq_state[irq] |= mask; + } else { + s->irq_state[irq] &= ~mask; + } + /* Make sure there are no stuck bits if mapping has changed */ + s->irq_state[irq] &= s->irq_state[0]; + /* ISA IRQ level is the OR of all sources routed to it */ + qemu_set_irq(s->isa_irqs_in[irq], !!s->irq_state[irq]); +} + +static void via_isa_pirq(void *opaque, int pin, int level) +{ + via_isa_set_irq(opaque, pin, level); } static void via_isa_request_i8259_irq(void *opaque, int irq, int level) @@ -584,24 +714,79 @@ static void via_isa_realize(PCIDevice *d, Error **errp) { ViaISAState *s = VIA_ISA(d); DeviceState *dev = DEVICE(d); + PCIBus *pci_bus = pci_get_bus(d); qemu_irq *isa_irq; + ISABus *isa_bus; int i; qdev_init_gpio_out(dev, &s->cpu_intr, 1); + qdev_init_gpio_in_named(dev, via_isa_pirq, "pirq", PCI_NUM_PINS); isa_irq = qemu_allocate_irqs(via_isa_request_i8259_irq, s, 1); - s->isa_bus = isa_bus_new(dev, get_system_memory(), pci_address_space_io(d), - &error_fatal); - s->isa_irqs = i8259_init(s->isa_bus, *isa_irq); - isa_bus_irqs(s->isa_bus, s->isa_irqs); - i8254_pit_init(s->isa_bus, 0x40, 0, NULL); - i8257_dma_init(s->isa_bus, 0); - mc146818_rtc_init(s->isa_bus, 2000, NULL); + isa_bus = isa_bus_new(dev, pci_address_space(d), pci_address_space_io(d), + errp); + + if (!isa_bus) { + return; + } + + s->isa_irqs_in = i8259_init(isa_bus, *isa_irq); + isa_bus_register_input_irqs(isa_bus, s->isa_irqs_in); + i8254_pit_init(isa_bus, 0x40, 0, NULL); + i8257_dma_init(OBJECT(d), isa_bus, 0); + + /* RTC */ + qdev_prop_set_int32(DEVICE(&s->rtc), "base_year", 2000); + if (!qdev_realize(DEVICE(&s->rtc), BUS(isa_bus), errp)) { + return; + } + isa_connect_gpio_out(ISA_DEVICE(&s->rtc), 0, s->rtc.isairq); for (i = 0; i < PCI_CONFIG_HEADER_SIZE; i++) { if (i < PCI_COMMAND || i >= PCI_REVISION_ID) { d->wmask[i] = 0; } } + + /* Super I/O */ + if (!qdev_realize(DEVICE(&s->via_sio), BUS(isa_bus), errp)) { + return; + } + + /* Function 1: IDE */ + qdev_prop_set_int32(DEVICE(&s->ide), "addr", d->devfn + 1); + if (!qdev_realize(DEVICE(&s->ide), BUS(pci_bus), errp)) { + return; + } + for (i = 0; i < 2; i++) { + qdev_connect_gpio_out_named(DEVICE(&s->ide), "isa-irq", i, + s->isa_irqs_in[14 + i]); + } + + /* Functions 2-3: USB Ports */ + for (i = 0; i < ARRAY_SIZE(s->uhci); i++) { + qdev_prop_set_int32(DEVICE(&s->uhci[i]), "addr", d->devfn + 2 + i); + if (!qdev_realize(DEVICE(&s->uhci[i]), BUS(pci_bus), errp)) { + return; + } + } + + /* Function 4: Power Management */ + qdev_prop_set_int32(DEVICE(&s->pm), "addr", d->devfn + 4); + if (!qdev_realize(DEVICE(&s->pm), BUS(pci_bus), errp)) { + return; + } + + /* Function 5: AC97 Audio */ + qdev_prop_set_int32(DEVICE(&s->ac97), "addr", d->devfn + 5); + if (!qdev_realize(DEVICE(&s->ac97), BUS(pci_bus), errp)) { + return; + } + + /* Function 6: MC97 Modem */ + qdev_prop_set_int32(DEVICE(&s->mc97), "addr", d->devfn + 6); + if (!qdev_realize(DEVICE(&s->mc97), BUS(pci_bus), errp)) { + return; + } } /* TYPE_VT82C686B_ISA */ @@ -615,7 +800,7 @@ static void vt82c686b_write_config(PCIDevice *d, uint32_t addr, pci_default_write_config(d, addr, val, len); if (addr == 0x85) { /* BIT(1): enable or disable superio config io ports */ - via_superio_io_enable(s->via_sio, val & BIT(1)); + via_superio_io_enable(&s->via_sio, val & BIT(1)); } } @@ -639,13 +824,12 @@ static void vt82c686b_isa_reset(DeviceState *dev) pci_conf[0x77] = 0x10; /* GPIO Control 1/2/3/4 */ } -static void vt82c686b_realize(PCIDevice *d, Error **errp) +static void vt82c686b_init(Object *obj) { - ViaISAState *s = VIA_ISA(d); + ViaISAState *s = VIA_ISA(obj); - via_isa_realize(d, errp); - s->via_sio = VIA_SUPERIO(isa_create_simple(s->isa_bus, - TYPE_VT82C686B_SUPERIO)); + object_initialize_child(obj, "sio", &s->via_sio, TYPE_VT82C686B_SUPERIO); + object_initialize_child(obj, "pm", &s->pm, TYPE_VT82C686B_PM); } static void vt82c686b_class_init(ObjectClass *klass, void *data) @@ -653,7 +837,7 @@ static void vt82c686b_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->realize = vt82c686b_realize; + k->realize = via_isa_realize; k->config_write = vt82c686b_write_config; k->vendor_id = PCI_VENDOR_ID_VIA; k->device_id = PCI_DEVICE_ID_VIA_82C686B_ISA; @@ -670,6 +854,7 @@ static const TypeInfo vt82c686b_isa_info = { .name = TYPE_VT82C686B_ISA, .parent = TYPE_VIA_ISA, .instance_size = sizeof(ViaISAState), + .instance_init = vt82c686b_init, .class_init = vt82c686b_class_init, }; @@ -684,7 +869,7 @@ static void vt8231_write_config(PCIDevice *d, uint32_t addr, pci_default_write_config(d, addr, val, len); if (addr == 0x50) { /* BIT(2): enable or disable superio config io ports */ - via_superio_io_enable(s->via_sio, val & BIT(2)); + via_superio_io_enable(&s->via_sio, val & BIT(2)); } } @@ -698,18 +883,18 @@ static void vt8231_isa_reset(DeviceState *dev) PCI_COMMAND_MASTER | PCI_COMMAND_SPECIAL); pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM); + pci_conf[0x4c] = 0x04; /* IDE interrupt Routing */ pci_conf[0x58] = 0x40; /* Miscellaneous Control 0 */ pci_conf[0x67] = 0x08; /* Fast IR Config */ pci_conf[0x6b] = 0x01; /* Fast IR I/O Base */ } -static void vt8231_realize(PCIDevice *d, Error **errp) +static void vt8231_init(Object *obj) { - ViaISAState *s = VIA_ISA(d); + ViaISAState *s = VIA_ISA(obj); - via_isa_realize(d, errp); - s->via_sio = VIA_SUPERIO(isa_create_simple(s->isa_bus, - TYPE_VT8231_SUPERIO)); + object_initialize_child(obj, "sio", &s->via_sio, TYPE_VT8231_SUPERIO); + object_initialize_child(obj, "pm", &s->pm, TYPE_VT8231_PM); } static void vt8231_class_init(ObjectClass *klass, void *data) @@ -717,7 +902,7 @@ static void vt8231_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->realize = vt8231_realize; + k->realize = via_isa_realize; k->config_write = vt8231_write_config; k->vendor_id = PCI_VENDOR_ID_VIA; k->device_id = PCI_DEVICE_ID_VIA_8231_ISA; @@ -734,6 +919,7 @@ static const TypeInfo vt8231_isa_info = { .name = TYPE_VT8231_ISA, .parent = TYPE_VIA_ISA, .instance_size = sizeof(ViaISAState), + .instance_init = vt8231_init, .class_init = vt8231_class_init, }; diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index 35b6680772..5727efed6d 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -2,15 +2,21 @@ config LOONGARCH_VIRT bool select PCI select PCI_EXPRESS_GENERIC_BRIDGE - imply VGA_PCI imply VIRTIO_VGA imply PCI_DEVICES - select ISA_BUS + imply NVDIMM select SERIAL - select SERIAL_ISA select VIRTIO_PCI + select PLATFORM_BUS select LOONGARCH_IPI select LOONGARCH_PCH_PIC select LOONGARCH_PCH_MSI select LOONGARCH_EXTIOI select LS7A_RTC + select SMBIOS + select ACPI_PCI + select ACPI_HW_REDUCED + select FW_CFG_DMA + select DIMM + select PFLASH_CFI01 + select ACPI_HMAT diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c new file mode 100644 index 0000000000..e5ab1080af --- /dev/null +++ b/hw/loongarch/acpi-build.c @@ -0,0 +1,639 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for generating ACPI tables and passing them to Guests + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/bitmap.h" +#include "hw/pci/pci.h" +#include "hw/core/cpu.h" +#include "target/loongarch/cpu.h" +#include "hw/acpi/acpi-defs.h" +#include "hw/acpi/acpi.h" +#include "hw/nvram/fw_cfg.h" +#include "hw/acpi/bios-linker-loader.h" +#include "migration/vmstate.h" +#include "hw/mem/memory-device.h" +#include "sysemu/reset.h" + +/* Supported chipsets: */ +#include "hw/pci-host/ls7a.h" +#include "hw/loongarch/virt.h" + +#include "hw/acpi/utils.h" +#include "hw/acpi/pci.h" + +#include "qom/qom-qobject.h" + +#include "hw/acpi/generic_event_device.h" +#include "hw/pci-host/gpex.h" +#include "sysemu/tpm.h" +#include "hw/platform-bus.h" +#include "hw/acpi/aml-build.h" +#include "hw/acpi/hmat.h" + +#define ACPI_BUILD_ALIGN_SIZE 0x1000 +#define ACPI_BUILD_TABLE_SIZE 0x20000 + +#ifdef DEBUG_ACPI_BUILD +#define ACPI_BUILD_DPRINTF(fmt, ...) \ + do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0) +#else +#define ACPI_BUILD_DPRINTF(fmt, ...) +#endif + +/* build FADT */ +static void init_common_fadt_data(AcpiFadtData *data) +{ + AcpiFadtData fadt = { + /* ACPI 5.0: 4.1 Hardware-Reduced ACPI */ + .rev = 5, + .flags = ((1 << ACPI_FADT_F_HW_REDUCED_ACPI) | + (1 << ACPI_FADT_F_RESET_REG_SUP)), + + /* ACPI 5.0: 4.8.3.7 Sleep Control and Status Registers */ + .sleep_ctl = { + .space_id = AML_AS_SYSTEM_MEMORY, + .bit_width = 8, + .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_CTL, + }, + .sleep_sts = { + .space_id = AML_AS_SYSTEM_MEMORY, + .bit_width = 8, + .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_STS, + }, + + /* ACPI 5.0: 4.8.3.6 Reset Register */ + .reset_reg = { + .space_id = AML_AS_SYSTEM_MEMORY, + .bit_width = 8, + .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_RESET, + }, + .reset_val = ACPI_GED_RESET_VALUE, + }; + *data = fadt; +} + +static void acpi_align_size(GArray *blob, unsigned align) +{ + /* + * Align size to multiple of given size. This reduces the chance + * we need to change size in the future (breaking cross version migration). + */ + g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align)); +} + +/* build FACS */ +static void +build_facs(GArray *table_data) +{ + const char *sig = "FACS"; + const uint8_t reserved[40] = {}; + + g_array_append_vals(table_data, sig, 4); /* Signature */ + build_append_int_noprefix(table_data, 64, 4); /* Length */ + build_append_int_noprefix(table_data, 0, 4); /* Hardware Signature */ + build_append_int_noprefix(table_data, 0, 4); /* Firmware Waking Vector */ + build_append_int_noprefix(table_data, 0, 4); /* Global Lock */ + build_append_int_noprefix(table_data, 0, 4); /* Flags */ + g_array_append_vals(table_data, reserved, 40); /* Reserved */ +} + +/* build MADT */ +static void +build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams) +{ + MachineState *ms = MACHINE(lams); + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); + int i, arch_id; + AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lams->oem_id, + .oem_table_id = lams->oem_table_id }; + + acpi_table_begin(&table, table_data); + + /* Local APIC Address */ + build_append_int_noprefix(table_data, 0, 4); + build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */ + + for (i = 0; i < arch_ids->len; i++) { + /* Processor Core Interrupt Controller Structure */ + arch_id = arch_ids->cpus[i].arch_id; + + build_append_int_noprefix(table_data, 17, 1); /* Type */ + build_append_int_noprefix(table_data, 15, 1); /* Length */ + build_append_int_noprefix(table_data, 1, 1); /* Version */ + build_append_int_noprefix(table_data, i, 4); /* ACPI Processor ID */ + build_append_int_noprefix(table_data, arch_id, 4); /* Core ID */ + build_append_int_noprefix(table_data, 1, 4); /* Flags */ + } + + /* Extend I/O Interrupt Controller Structure */ + build_append_int_noprefix(table_data, 20, 1); /* Type */ + build_append_int_noprefix(table_data, 13, 1); /* Length */ + build_append_int_noprefix(table_data, 1, 1); /* Version */ + build_append_int_noprefix(table_data, 3, 1); /* Cascade */ + build_append_int_noprefix(table_data, 0, 1); /* Node */ + build_append_int_noprefix(table_data, 0xffff, 8); /* Node map */ + + /* MSI Interrupt Controller Structure */ + build_append_int_noprefix(table_data, 21, 1); /* Type */ + build_append_int_noprefix(table_data, 19, 1); /* Length */ + build_append_int_noprefix(table_data, 1, 1); /* Version */ + build_append_int_noprefix(table_data, VIRT_PCH_MSI_ADDR_LOW, 8);/* Address */ + build_append_int_noprefix(table_data, 0x40, 4); /* Start */ + build_append_int_noprefix(table_data, 0xc0, 4); /* Count */ + + /* Bridge I/O Interrupt Controller Structure */ + build_append_int_noprefix(table_data, 22, 1); /* Type */ + build_append_int_noprefix(table_data, 17, 1); /* Length */ + build_append_int_noprefix(table_data, 1, 1); /* Version */ + build_append_int_noprefix(table_data, VIRT_PCH_REG_BASE, 8);/* Address */ + build_append_int_noprefix(table_data, 0x1000, 2); /* Size */ + build_append_int_noprefix(table_data, 0, 2); /* Id */ + build_append_int_noprefix(table_data, 0x40, 2); /* Base */ + + acpi_table_end(linker, &table); +} + +/* build SRAT */ +static void +build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) +{ + int i, arch_id, node_id; + uint64_t mem_len, mem_base; + int nb_numa_nodes = machine->numa_state->num_nodes; + LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + MachineClass *mc = MACHINE_GET_CLASS(lams); + const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine); + AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lams->oem_id, + .oem_table_id = lams->oem_table_id }; + + acpi_table_begin(&table, table_data); + build_append_int_noprefix(table_data, 1, 4); /* Reserved */ + build_append_int_noprefix(table_data, 0, 8); /* Reserved */ + + for (i = 0; i < arch_ids->len; ++i) { + arch_id = arch_ids->cpus[i].arch_id; + node_id = arch_ids->cpus[i].props.node_id; + + /* Processor Local APIC/SAPIC Affinity Structure */ + build_append_int_noprefix(table_data, 0, 1); /* Type */ + build_append_int_noprefix(table_data, 16, 1); /* Length */ + /* Proximity Domain [7:0] */ + build_append_int_noprefix(table_data, node_id, 1); + build_append_int_noprefix(table_data, arch_id, 1); /* APIC ID */ + /* Flags, Table 5-36 */ + build_append_int_noprefix(table_data, 1, 4); + build_append_int_noprefix(table_data, 0, 1); /* Local SAPIC EID */ + /* Proximity Domain [31:8] */ + build_append_int_noprefix(table_data, 0, 3); + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + } + + /* Node0 */ + build_srat_memory(table_data, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, + 0, MEM_AFFINITY_ENABLED); + mem_base = VIRT_HIGHMEM_BASE; + if (!nb_numa_nodes) { + mem_len = machine->ram_size - VIRT_LOWMEM_SIZE; + } else { + mem_len = machine->numa_state->nodes[0].node_mem - VIRT_LOWMEM_SIZE; + } + if (mem_len) + build_srat_memory(table_data, mem_base, mem_len, 0, MEM_AFFINITY_ENABLED); + + /* Node1 - Nodemax */ + if (nb_numa_nodes) { + mem_base += mem_len; + for (i = 1; i < nb_numa_nodes; ++i) { + if (machine->numa_state->nodes[i].node_mem > 0) { + build_srat_memory(table_data, mem_base, + machine->numa_state->nodes[i].node_mem, i, + MEM_AFFINITY_ENABLED); + mem_base += machine->numa_state->nodes[i].node_mem; + } + } + } + + if (machine->device_memory) { + build_srat_memory(table_data, machine->device_memory->base, + memory_region_size(&machine->device_memory->mr), + nb_numa_nodes - 1, + MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); + } + + acpi_table_end(linker, &table); +} + +typedef +struct AcpiBuildState { + /* Copy of table in RAM (for patching). */ + MemoryRegion *table_mr; + /* Is table patched? */ + uint8_t patched; + void *rsdp; + MemoryRegion *rsdp_mr; + MemoryRegion *linker_mr; +} AcpiBuildState; + +static void build_uart_device_aml(Aml *table) +{ + Aml *dev; + Aml *crs; + Aml *pkg0, *pkg1, *pkg2; + uint32_t uart_irq = VIRT_UART_IRQ; + + Aml *scope = aml_scope("_SB"); + dev = aml_device("COMA"); + aml_append(dev, aml_name_decl("_HID", aml_string("PNP0501"))); + aml_append(dev, aml_name_decl("_UID", aml_int(0))); + aml_append(dev, aml_name_decl("_CCA", aml_int(1))); + crs = aml_resource_template(); + aml_append(crs, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, + AML_NON_CACHEABLE, AML_READ_WRITE, + 0, VIRT_UART_BASE, VIRT_UART_BASE + VIRT_UART_SIZE - 1, + 0, VIRT_UART_SIZE)); + aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, + AML_SHARED, &uart_irq, 1)); + aml_append(dev, aml_name_decl("_CRS", crs)); + pkg0 = aml_package(0x2); + aml_append(pkg0, aml_int(0x05F5E100)); + aml_append(pkg0, aml_string("clock-frenquency")); + pkg1 = aml_package(0x1); + aml_append(pkg1, pkg0); + pkg2 = aml_package(0x2); + aml_append(pkg2, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301")); + aml_append(pkg2, pkg1); + aml_append(dev, aml_name_decl("_DSD", pkg2)); + aml_append(scope, dev); + aml_append(table, scope); +} + +static void +build_la_ged_aml(Aml *dsdt, MachineState *machine) +{ + uint32_t event; + LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + + build_ged_aml(dsdt, "\\_SB."GED_DEVICE, + HOTPLUG_HANDLER(lams->acpi_ged), + VIRT_SCI_IRQ, AML_SYSTEM_MEMORY, + VIRT_GED_EVT_ADDR); + event = object_property_get_uint(OBJECT(lams->acpi_ged), + "ged-event", &error_abort); + if (event & ACPI_GED_MEM_HOTPLUG_EVT) { + build_memory_hotplug_aml(dsdt, machine->ram_slots, "\\_SB", NULL, + AML_SYSTEM_MEMORY, + VIRT_GED_MEM_ADDR); + } + acpi_dsdt_add_power_button(dsdt); +} + +static void build_pci_device_aml(Aml *scope, LoongArchMachineState *lams) +{ + struct GPEXConfig cfg = { + .mmio64.base = VIRT_PCI_MEM_BASE, + .mmio64.size = VIRT_PCI_MEM_SIZE, + .pio.base = VIRT_PCI_IO_BASE, + .pio.size = VIRT_PCI_IO_SIZE, + .ecam.base = VIRT_PCI_CFG_BASE, + .ecam.size = VIRT_PCI_CFG_SIZE, + .irq = VIRT_GSI_BASE + VIRT_DEVICE_IRQS, + .bus = lams->pci_bus, + }; + + acpi_dsdt_add_gpex(scope, &cfg); +} + +static void build_flash_aml(Aml *scope, LoongArchMachineState *lams) +{ + Aml *dev, *crs; + MemoryRegion *flash_mem; + + hwaddr flash0_base; + hwaddr flash0_size; + + hwaddr flash1_base; + hwaddr flash1_size; + + flash_mem = pflash_cfi01_get_memory(lams->flash[0]); + flash0_base = flash_mem->addr; + flash0_size = memory_region_size(flash_mem); + + flash_mem = pflash_cfi01_get_memory(lams->flash[1]); + flash1_base = flash_mem->addr; + flash1_size = memory_region_size(flash_mem); + + dev = aml_device("FLS0"); + aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015"))); + aml_append(dev, aml_name_decl("_UID", aml_int(0))); + + crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(flash0_base, flash0_size, + AML_READ_WRITE)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); + + dev = aml_device("FLS1"); + aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015"))); + aml_append(dev, aml_name_decl("_UID", aml_int(1))); + + crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(flash1_base, flash1_size, + AML_READ_WRITE)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); +} + +#ifdef CONFIG_TPM +static void acpi_dsdt_add_tpm(Aml *scope, LoongArchMachineState *vms) +{ + PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); + hwaddr pbus_base = VIRT_PLATFORM_BUS_BASEADDRESS; + SysBusDevice *sbdev = SYS_BUS_DEVICE(tpm_find()); + MemoryRegion *sbdev_mr; + hwaddr tpm_base; + + if (!sbdev) { + return; + } + + tpm_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); + assert(tpm_base != -1); + + tpm_base += pbus_base; + + sbdev_mr = sysbus_mmio_get_region(sbdev, 0); + + Aml *dev = aml_device("TPM0"); + aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101"))); + aml_append(dev, aml_name_decl("_STR", aml_string("TPM 2.0 Device"))); + aml_append(dev, aml_name_decl("_UID", aml_int(0))); + + Aml *crs = aml_resource_template(); + aml_append(crs, + aml_memory32_fixed(tpm_base, + (uint32_t)memory_region_size(sbdev_mr), + AML_READ_WRITE)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); +} +#endif + +/* build DSDT */ +static void +build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) +{ + Aml *dsdt, *scope, *pkg; + LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lams->oem_id, + .oem_table_id = lams->oem_table_id }; + + acpi_table_begin(&table, table_data); + dsdt = init_aml_allocator(); + build_uart_device_aml(dsdt); + build_pci_device_aml(dsdt, lams); + build_la_ged_aml(dsdt, machine); + build_flash_aml(dsdt, lams); +#ifdef CONFIG_TPM + acpi_dsdt_add_tpm(dsdt, lams); +#endif + /* System State Package */ + scope = aml_scope("\\"); + pkg = aml_package(4); + aml_append(pkg, aml_int(ACPI_GED_SLP_TYP_S5)); + aml_append(pkg, aml_int(0)); /* ignored */ + aml_append(pkg, aml_int(0)); /* reserved */ + aml_append(pkg, aml_int(0)); /* reserved */ + aml_append(scope, aml_name_decl("_S5", pkg)); + aml_append(dsdt, scope); + /* Copy AML table into ACPI tables blob and patch header there */ + g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); + acpi_table_end(linker, &table); + free_aml_allocator(); +} + +static void acpi_build(AcpiBuildTables *tables, MachineState *machine) +{ + LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + GArray *table_offsets; + AcpiFadtData fadt_data; + unsigned facs, rsdt, dsdt; + uint8_t *u; + GArray *tables_blob = tables->table_data; + + init_common_fadt_data(&fadt_data); + + table_offsets = g_array_new(false, true, sizeof(uint32_t)); + ACPI_BUILD_DPRINTF("init ACPI tables\n"); + + bios_linker_loader_alloc(tables->linker, + ACPI_BUILD_TABLE_FILE, tables_blob, + 64, false); + + /* + * FACS is pointed to by FADT. + * We place it first since it's the only table that has alignment + * requirements. + */ + facs = tables_blob->len; + build_facs(tables_blob); + + /* DSDT is pointed to by FADT */ + dsdt = tables_blob->len; + build_dsdt(tables_blob, tables->linker, machine); + + /* ACPI tables pointed to by RSDT */ + acpi_add_table(table_offsets, tables_blob); + fadt_data.facs_tbl_offset = &facs; + fadt_data.dsdt_tbl_offset = &dsdt; + fadt_data.xdsdt_tbl_offset = &dsdt; + build_fadt(tables_blob, tables->linker, &fadt_data, + lams->oem_id, lams->oem_table_id); + + acpi_add_table(table_offsets, tables_blob); + build_madt(tables_blob, tables->linker, lams); + + acpi_add_table(table_offsets, tables_blob); + build_pptt(tables_blob, tables->linker, machine, + lams->oem_id, lams->oem_table_id); + + acpi_add_table(table_offsets, tables_blob); + build_srat(tables_blob, tables->linker, machine); + + if (machine->numa_state->num_nodes) { + if (machine->numa_state->have_numa_distance) { + acpi_add_table(table_offsets, tables_blob); + build_slit(tables_blob, tables->linker, machine, lams->oem_id, + lams->oem_table_id); + } + if (machine->numa_state->hmat_enabled) { + acpi_add_table(table_offsets, tables_blob); + build_hmat(tables_blob, tables->linker, machine->numa_state, + lams->oem_id, lams->oem_table_id); + } + } + + acpi_add_table(table_offsets, tables_blob); + { + AcpiMcfgInfo mcfg = { + .base = cpu_to_le64(VIRT_PCI_CFG_BASE), + .size = cpu_to_le64(VIRT_PCI_CFG_SIZE), + }; + build_mcfg(tables_blob, tables->linker, &mcfg, lams->oem_id, + lams->oem_table_id); + } + +#ifdef CONFIG_TPM + /* TPM info */ + if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0) { + acpi_add_table(table_offsets, tables_blob); + build_tpm2(tables_blob, tables->linker, + tables->tcpalog, lams->oem_id, + lams->oem_table_id); + } +#endif + /* Add tables supplied by user (if any) */ + for (u = acpi_table_first(); u; u = acpi_table_next(u)) { + unsigned len = acpi_table_len(u); + + acpi_add_table(table_offsets, tables_blob); + g_array_append_vals(tables_blob, u, len); + } + + /* RSDT is pointed to by RSDP */ + rsdt = tables_blob->len; + build_rsdt(tables_blob, tables->linker, table_offsets, + lams->oem_id, lams->oem_table_id); + + /* RSDP is in FSEG memory, so allocate it separately */ + { + AcpiRsdpData rsdp_data = { + .revision = 0, + .oem_id = lams->oem_id, + .xsdt_tbl_offset = NULL, + .rsdt_tbl_offset = &rsdt, + }; + build_rsdp(tables->rsdp, tables->linker, &rsdp_data); + } + + /* + * The align size is 128, warn if 64k is not enough therefore + * the align size could be resized. + */ + if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) { + warn_report("ACPI table size %u exceeds %d bytes," + " migration may not work", + tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2); + error_printf("Try removing CPUs, NUMA nodes, memory slots" + " or PCI bridges.\n"); + } + + acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE); + + /* Cleanup memory that's no longer used. */ + g_array_free(table_offsets, true); +} + +static void acpi_ram_update(MemoryRegion *mr, GArray *data) +{ + uint32_t size = acpi_data_len(data); + + /* + * Make sure RAM size is correct - in case it got changed + * e.g. by migration + */ + memory_region_ram_resize(mr, size, &error_abort); + + memcpy(memory_region_get_ram_ptr(mr), data->data, size); + memory_region_set_dirty(mr, 0, size); +} + +static void acpi_build_update(void *build_opaque) +{ + AcpiBuildState *build_state = build_opaque; + AcpiBuildTables tables; + + /* No state to update or already patched? Nothing to do. */ + if (!build_state || build_state->patched) { + return; + } + build_state->patched = 1; + + acpi_build_tables_init(&tables); + + acpi_build(&tables, MACHINE(qdev_get_machine())); + + acpi_ram_update(build_state->table_mr, tables.table_data); + acpi_ram_update(build_state->rsdp_mr, tables.rsdp); + acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob); + + acpi_build_tables_cleanup(&tables, true); +} + +static void acpi_build_reset(void *build_opaque) +{ + AcpiBuildState *build_state = build_opaque; + build_state->patched = 0; +} + +static const VMStateDescription vmstate_acpi_build = { + .name = "acpi_build", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT8(patched, AcpiBuildState), + VMSTATE_END_OF_LIST() + }, +}; + +void loongarch_acpi_setup(LoongArchMachineState *lams) +{ + AcpiBuildTables tables; + AcpiBuildState *build_state; + + if (!lams->fw_cfg) { + ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); + return; + } + + if (!loongarch_is_acpi_enabled(lams)) { + ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n"); + return; + } + + build_state = g_malloc0(sizeof *build_state); + + acpi_build_tables_init(&tables); + acpi_build(&tables, MACHINE(lams)); + + /* Now expose it all to Guest */ + build_state->table_mr = acpi_add_rom_blob(acpi_build_update, + build_state, tables.table_data, + ACPI_BUILD_TABLE_FILE); + assert(build_state->table_mr != NULL); + + build_state->linker_mr = + acpi_add_rom_blob(acpi_build_update, build_state, + tables.linker->cmd_blob, ACPI_BUILD_LOADER_FILE); + + build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update, + build_state, tables.rsdp, + ACPI_BUILD_RSDP_FILE); + + qemu_register_reset(acpi_build_reset, build_state); + acpi_build_reset(build_state); + vmstate_register(NULL, 0, &vmstate_acpi_build, build_state); + + /* + * Cleanup tables but don't free the memory: we track it + * in build_state. + */ + acpi_build_tables_cleanup(&tables, false); +} diff --git a/hw/loongarch/fw_cfg.c b/hw/loongarch/fw_cfg.c new file mode 100644 index 0000000000..f15a17416c --- /dev/null +++ b/hw/loongarch/fw_cfg.c @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU fw_cfg helpers (LoongArch specific) + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "hw/loongarch/fw_cfg.h" +#include "hw/loongarch/virt.h" +#include "hw/nvram/fw_cfg.h" +#include "sysemu/sysemu.h" + +static void fw_cfg_boot_set(void *opaque, const char *boot_device, + Error **errp) +{ + fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); +} + +FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms) +{ + FWCfgState *fw_cfg; + int max_cpus = ms->smp.max_cpus; + int smp_cpus = ms->smp.cpus; + + fw_cfg = fw_cfg_init_mem_wide(VIRT_FWCFG_BASE + 8, VIRT_FWCFG_BASE, 8, + VIRT_FWCFG_BASE + 16, &address_space_memory); + 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_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); + + qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); + return fw_cfg; +} diff --git a/hw/loongarch/fw_cfg.h b/hw/loongarch/fw_cfg.h new file mode 100644 index 0000000000..7c0de4db4a --- /dev/null +++ b/hw/loongarch/fw_cfg.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU fw_cfg helpers (LoongArch specific) + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#ifndef HW_LOONGARCH_FW_CFG_H +#define HW_LOONGARCH_FW_CFG_H + +#include "hw/boards.h" +#include "hw/nvram/fw_cfg.h" + +FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms); +#endif diff --git a/hw/loongarch/loongson3.c b/hw/loongarch/loongson3.c deleted file mode 100644 index bd20ebbb78..0000000000 --- a/hw/loongarch/loongson3.c +++ /dev/null @@ -1,382 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * QEMU loongson 3a5000 develop board emulation - * - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "qemu/datadir.h" -#include "qapi/error.h" -#include "hw/boards.h" -#include "hw/char/serial.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/runstate.h" -#include "sysemu/reset.h" -#include "sysemu/rtc.h" -#include "hw/loongarch/virt.h" -#include "exec/address-spaces.h" -#include "hw/irq.h" -#include "net/net.h" -#include "hw/loader.h" -#include "elf.h" -#include "hw/intc/loongarch_ipi.h" -#include "hw/intc/loongarch_extioi.h" -#include "hw/intc/loongarch_pch_pic.h" -#include "hw/intc/loongarch_pch_msi.h" -#include "hw/pci-host/ls7a.h" -#include "hw/pci-host/gpex.h" -#include "hw/misc/unimp.h" - -#include "target/loongarch/cpu.h" - -#define PM_BASE 0x10080000 -#define PM_SIZE 0x100 -#define PM_CTRL 0x10 - -/* - * This is a placeholder for missing ACPI, - * and will eventually be replaced. - */ -static uint64_t loongarch_virt_pm_read(void *opaque, hwaddr addr, unsigned size) -{ - return 0; -} - -static void loongarch_virt_pm_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - if (addr != PM_CTRL) { - return; - } - - switch (val) { - case 0x00: - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - return; - case 0xff: - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); - return; - default: - return; - } -} - -static const MemoryRegionOps loongarch_virt_pm_ops = { - .read = loongarch_virt_pm_read, - .write = loongarch_virt_pm_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1 - } -}; - -static struct _loaderparams { - uint64_t ram_size; - const char *kernel_filename; -} loaderparams; - -static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) -{ - return addr & 0x1fffffffll; -} - -static int64_t load_kernel_info(void) -{ - uint64_t kernel_entry, kernel_low, kernel_high; - ssize_t kernel_size; - - kernel_size = load_elf(loaderparams.kernel_filename, NULL, - cpu_loongarch_virt_to_phys, NULL, - &kernel_entry, &kernel_low, - &kernel_high, NULL, 0, - EM_LOONGARCH, 1, 0); - - if (kernel_size < 0) { - error_report("could not load kernel '%s': %s", - loaderparams.kernel_filename, - load_elf_strerror(kernel_size)); - exit(1); - } - return kernel_entry; -} - -static void loongarch_devices_init(DeviceState *pch_pic) -{ - DeviceState *gpex_dev; - SysBusDevice *d; - PCIBus *pci_bus; - MemoryRegion *ecam_alias, *ecam_reg, *pio_alias, *pio_reg; - MemoryRegion *mmio_alias, *mmio_reg, *pm_mem; - int i; - - gpex_dev = qdev_new(TYPE_GPEX_HOST); - d = SYS_BUS_DEVICE(gpex_dev); - sysbus_realize_and_unref(d, &error_fatal); - pci_bus = PCI_HOST_BRIDGE(gpex_dev)->bus; - - /* Map only part size_ecam bytes of ECAM space */ - ecam_alias = g_new0(MemoryRegion, 1); - ecam_reg = sysbus_mmio_get_region(d, 0); - memory_region_init_alias(ecam_alias, OBJECT(gpex_dev), "pcie-ecam", - ecam_reg, 0, LS_PCIECFG_SIZE); - memory_region_add_subregion(get_system_memory(), LS_PCIECFG_BASE, - ecam_alias); - - /* Map PCI mem space */ - mmio_alias = g_new0(MemoryRegion, 1); - mmio_reg = sysbus_mmio_get_region(d, 1); - memory_region_init_alias(mmio_alias, OBJECT(gpex_dev), "pcie-mmio", - mmio_reg, LS7A_PCI_MEM_BASE, LS7A_PCI_MEM_SIZE); - memory_region_add_subregion(get_system_memory(), LS7A_PCI_MEM_BASE, - mmio_alias); - - /* Map PCI IO port space. */ - pio_alias = g_new0(MemoryRegion, 1); - pio_reg = sysbus_mmio_get_region(d, 2); - memory_region_init_alias(pio_alias, OBJECT(gpex_dev), "pcie-io", pio_reg, - LS7A_PCI_IO_OFFSET, LS7A_PCI_IO_SIZE); - memory_region_add_subregion(get_system_memory(), LS7A_PCI_IO_BASE, - pio_alias); - - for (i = 0; i < GPEX_NUM_IRQS; i++) { - sysbus_connect_irq(d, i, - qdev_get_gpio_in(pch_pic, 16 + i)); - gpex_set_irq_num(GPEX_HOST(gpex_dev), i, 16 + i); - } - - serial_mm_init(get_system_memory(), LS7A_UART_BASE, 0, - qdev_get_gpio_in(pch_pic, - LS7A_UART_IRQ - PCH_PIC_IRQ_OFFSET), - 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); - - /* Network init */ - for (i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - - if (!nd->model) { - nd->model = g_strdup("virtio"); - } - - pci_nic_init_nofail(nd, pci_bus, nd->model, NULL); - } - - /* VGA setup */ - pci_vga_init(pci_bus); - - /* - * There are some invalid guest memory access. - * Create some unimplemented devices to emulate this. - */ - create_unimplemented_device("pci-dma-cfg", 0x1001041c, 0x4); - sysbus_create_simple("ls7a_rtc", LS7A_RTC_REG_BASE, - qdev_get_gpio_in(pch_pic, - LS7A_RTC_IRQ - PCH_PIC_IRQ_OFFSET)); - - pm_mem = g_new(MemoryRegion, 1); - memory_region_init_io(pm_mem, NULL, &loongarch_virt_pm_ops, - NULL, "loongarch_virt_pm", PM_SIZE); - memory_region_add_subregion(get_system_memory(), PM_BASE, pm_mem); -} - -static void loongarch_irq_init(LoongArchMachineState *lams) -{ - MachineState *ms = MACHINE(lams); - DeviceState *pch_pic, *pch_msi, *cpudev; - DeviceState *ipi, *extioi; - SysBusDevice *d; - LoongArchCPU *lacpu; - CPULoongArchState *env; - CPUState *cpu_state; - int cpu, pin, i; - - ipi = qdev_new(TYPE_LOONGARCH_IPI); - sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); - - extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); - sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); - - /* - * The connection of interrupts: - * +-----+ +---------+ +-------+ - * | IPI |--> | CPUINTC | <-- | Timer | - * +-----+ +---------+ +-------+ - * ^ - * | - * +---------+ - * | EIOINTC | - * +---------+ - * ^ ^ - * | | - * +---------+ +---------+ - * | PCH-PIC | | PCH-MSI | - * +---------+ +---------+ - * ^ ^ ^ - * | | | - * +--------+ +---------+ +---------+ - * | UARTs | | Devices | | Devices | - * +--------+ +---------+ +---------+ - */ - for (cpu = 0; cpu < ms->smp.cpus; cpu++) { - cpu_state = qemu_get_cpu(cpu); - cpudev = DEVICE(cpu_state); - lacpu = LOONGARCH_CPU(cpu_state); - env = &(lacpu->env); - - /* connect ipi irq to cpu irq */ - qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI)); - /* IPI iocsr memory region */ - memory_region_add_subregion(&env->system_iocsr, SMP_IPI_MAILBOX, - sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), - cpu)); - /* extioi iocsr memory region */ - memory_region_add_subregion(&env->system_iocsr, APIC_BASE, - sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), - cpu)); - } - - /* - * connect ext irq to the cpu irq - * cpu_pin[9:2] <= intc_pin[7:0] - */ - for (cpu = 0; cpu < ms->smp.cpus; cpu++) { - cpudev = DEVICE(qemu_get_cpu(cpu)); - for (pin = 0; pin < LS3A_INTC_IP; pin++) { - qdev_connect_gpio_out(extioi, (cpu * 8 + pin), - qdev_get_gpio_in(cpudev, pin + 2)); - } - } - - pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC); - d = SYS_BUS_DEVICE(pch_pic); - sysbus_realize_and_unref(d, &error_fatal); - memory_region_add_subregion(get_system_memory(), LS7A_IOAPIC_REG_BASE, - sysbus_mmio_get_region(d, 0)); - memory_region_add_subregion(get_system_memory(), - LS7A_IOAPIC_REG_BASE + PCH_PIC_ROUTE_ENTRY_OFFSET, - sysbus_mmio_get_region(d, 1)); - memory_region_add_subregion(get_system_memory(), - LS7A_IOAPIC_REG_BASE + PCH_PIC_INT_STATUS_LO, - sysbus_mmio_get_region(d, 2)); - - /* Connect 64 pch_pic irqs to extioi */ - for (int i = 0; i < PCH_PIC_IRQ_NUM; i++) { - qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i)); - } - - pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); - d = SYS_BUS_DEVICE(pch_msi); - sysbus_realize_and_unref(d, &error_fatal); - sysbus_mmio_map(d, 0, LS7A_PCH_MSI_ADDR_LOW); - for (i = 0; i < PCH_MSI_IRQ_NUM; i++) { - /* Connect 192 pch_msi irqs to extioi */ - qdev_connect_gpio_out(DEVICE(d), i, - qdev_get_gpio_in(extioi, i + PCH_MSI_IRQ_START)); - } - - loongarch_devices_init(pch_pic); -} - -static void reset_load_elf(void *opaque) -{ - LoongArchCPU *cpu = opaque; - CPULoongArchState *env = &cpu->env; - - cpu_reset(CPU(cpu)); - if (env->load_elf) { - cpu_set_pc(CPU(cpu), env->elf_address); - } -} - -static void loongarch_init(MachineState *machine) -{ - const char *cpu_model = machine->cpu_type; - const char *kernel_filename = machine->kernel_filename; - ram_addr_t offset = 0; - ram_addr_t ram_size = machine->ram_size; - uint64_t highram_size = 0; - MemoryRegion *address_space_mem = get_system_memory(); - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); - LoongArchCPU *lacpu; - int i; - int64_t kernel_addr = 0; - - if (!cpu_model) { - cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); - } - - if (!strstr(cpu_model, "la464")) { - error_report("LoongArch/TCG needs cpu type la464"); - exit(1); - } - - if (ram_size < 1 * GiB) { - error_report("ram_size must be greater than 1G."); - exit(1); - } - - /* Init CPUs */ - for (i = 0; i < machine->smp.cpus; i++) { - cpu_create(machine->cpu_type); - } - - /* Add memory region */ - memory_region_init_alias(&lams->lowmem, NULL, "loongarch.lowram", - machine->ram, 0, 256 * MiB); - memory_region_add_subregion(address_space_mem, offset, &lams->lowmem); - offset += 256 * MiB; - highram_size = ram_size - 256 * MiB; - memory_region_init_alias(&lams->highmem, NULL, "loongarch.highmem", - machine->ram, offset, highram_size); - memory_region_add_subregion(address_space_mem, 0x90000000, &lams->highmem); - /* Add isa io region */ - memory_region_init_alias(&lams->isa_io, NULL, "isa-io", - get_system_io(), 0, LOONGARCH_ISA_IO_SIZE); - memory_region_add_subregion(address_space_mem, LOONGARCH_ISA_IO_BASE, - &lams->isa_io); - if (kernel_filename) { - loaderparams.ram_size = ram_size; - loaderparams.kernel_filename = kernel_filename; - kernel_addr = load_kernel_info(); - if (!machine->firmware) { - for (i = 0; i < machine->smp.cpus; i++) { - lacpu = LOONGARCH_CPU(qemu_get_cpu(i)); - lacpu->env.load_elf = true; - lacpu->env.elf_address = kernel_addr; - qemu_register_reset(reset_load_elf, lacpu); - } - } - } - /* Initialize the IO interrupt subsystem */ - loongarch_irq_init(lams); -} - -static void loongarch_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "Loongson-3A5000 LS7A1000 machine"; - mc->init = loongarch_init; - mc->default_ram_size = 1 * GiB; - mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("la464"); - mc->default_ram_id = "loongarch.ram"; - mc->max_cpus = LOONGARCH_MAX_VCPUS; - mc->is_default = 1; - mc->default_kernel_irqchip_split = false; - mc->block_default_type = IF_VIRTIO; - mc->default_boot_order = "c"; - mc->no_cdrom = 1; -} - -static const TypeInfo loongarch_machine_types[] = { - { - .name = TYPE_LOONGARCH_MACHINE, - .parent = TYPE_MACHINE, - .instance_size = sizeof(LoongArchMachineState), - .class_init = loongarch_class_init, - } -}; - -DEFINE_TYPES(loongarch_machine_types) diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build index cecb1a5d65..c0421502ab 100644 --- a/hw/loongarch/meson.build +++ b/hw/loongarch/meson.build @@ -1,4 +1,8 @@ loongarch_ss = ss.source_set() -loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('loongson3.c')) +loongarch_ss.add(files( + 'fw_cfg.c', +)) +loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: [files('virt.c'), fdt]) +loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-build.c')) hw_arch += {'loongarch': loongarch_ss} diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c new file mode 100644 index 0000000000..441d764843 --- /dev/null +++ b/hw/loongarch/virt.c @@ -0,0 +1,1219 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU loongson 3a5000 develop board emulation + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/datadir.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "hw/char/serial.h" +#include "sysemu/sysemu.h" +#include "sysemu/qtest.h" +#include "sysemu/runstate.h" +#include "sysemu/reset.h" +#include "sysemu/rtc.h" +#include "hw/loongarch/virt.h" +#include "exec/address-spaces.h" +#include "hw/irq.h" +#include "net/net.h" +#include "hw/loader.h" +#include "elf.h" +#include "hw/intc/loongarch_ipi.h" +#include "hw/intc/loongarch_extioi.h" +#include "hw/intc/loongarch_pch_pic.h" +#include "hw/intc/loongarch_pch_msi.h" +#include "hw/pci-host/ls7a.h" +#include "hw/pci-host/gpex.h" +#include "hw/misc/unimp.h" +#include "hw/loongarch/fw_cfg.h" +#include "target/loongarch/cpu.h" +#include "hw/firmware/smbios.h" +#include "hw/acpi/aml-build.h" +#include "qapi/qapi-visit-common.h" +#include "hw/acpi/generic_event_device.h" +#include "hw/mem/nvdimm.h" +#include "sysemu/device_tree.h" +#include +#include "hw/core/sysbus-fdt.h" +#include "hw/platform-bus.h" +#include "hw/display/ramfb.h" +#include "hw/mem/pc-dimm.h" +#include "sysemu/tpm.h" +#include "sysemu/block-backend.h" +#include "hw/block/flash.h" +#include "qemu/error-report.h" + + +struct loaderparams { + uint64_t ram_size; + const char *kernel_filename; + const char *kernel_cmdline; + const char *initrd_filename; +}; + +static PFlashCFI01 *virt_flash_create1(LoongArchMachineState *lams, + const char *name, + const char *alias_prop_name) +{ + DeviceState *dev = qdev_new(TYPE_PFLASH_CFI01); + + qdev_prop_set_uint64(dev, "sector-length", VIRT_FLASH_SECTOR_SIZE); + qdev_prop_set_uint8(dev, "width", 4); + qdev_prop_set_uint8(dev, "device-width", 2); + qdev_prop_set_bit(dev, "big-endian", false); + qdev_prop_set_uint16(dev, "id0", 0x89); + qdev_prop_set_uint16(dev, "id1", 0x18); + qdev_prop_set_uint16(dev, "id2", 0x00); + qdev_prop_set_uint16(dev, "id3", 0x00); + qdev_prop_set_string(dev, "name", name); + object_property_add_child(OBJECT(lams), name, OBJECT(dev)); + object_property_add_alias(OBJECT(lams), alias_prop_name, + OBJECT(dev), "drive"); + return PFLASH_CFI01(dev); +} + +static void virt_flash_create(LoongArchMachineState *lams) +{ + lams->flash[0] = virt_flash_create1(lams, "virt.flash0", "pflash0"); + lams->flash[1] = virt_flash_create1(lams, "virt.flash1", "pflash1"); +} + +static void virt_flash_map1(PFlashCFI01 *flash, + hwaddr base, hwaddr size, + MemoryRegion *sysmem) +{ + DeviceState *dev = DEVICE(flash); + BlockBackend *blk; + hwaddr real_size = size; + + blk = pflash_cfi01_get_blk(flash); + if (blk) { + real_size = blk_getlength(blk); + assert(real_size && real_size <= size); + } + + assert(QEMU_IS_ALIGNED(real_size, VIRT_FLASH_SECTOR_SIZE)); + assert(real_size / VIRT_FLASH_SECTOR_SIZE <= UINT32_MAX); + + qdev_prop_set_uint32(dev, "num-blocks", real_size / VIRT_FLASH_SECTOR_SIZE); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + memory_region_add_subregion(sysmem, base, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); +} + +static void virt_flash_map(LoongArchMachineState *lams, + MemoryRegion *sysmem) +{ + PFlashCFI01 *flash0 = lams->flash[0]; + PFlashCFI01 *flash1 = lams->flash[1]; + + virt_flash_map1(flash0, VIRT_FLASH0_BASE, VIRT_FLASH0_SIZE, sysmem); + virt_flash_map1(flash1, VIRT_FLASH1_BASE, VIRT_FLASH1_SIZE, sysmem); +} + +static void fdt_add_flash_node(LoongArchMachineState *lams) +{ + MachineState *ms = MACHINE(lams); + char *nodename; + MemoryRegion *flash_mem; + + hwaddr flash0_base; + hwaddr flash0_size; + + hwaddr flash1_base; + hwaddr flash1_size; + + flash_mem = pflash_cfi01_get_memory(lams->flash[0]); + flash0_base = flash_mem->addr; + flash0_size = memory_region_size(flash_mem); + + flash_mem = pflash_cfi01_get_memory(lams->flash[1]); + flash1_base = flash_mem->addr; + flash1_size = memory_region_size(flash_mem); + + nodename = g_strdup_printf("/flash@%" PRIx64, flash0_base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", + 2, flash0_base, 2, flash0_size, + 2, flash1_base, 2, flash1_size); + qemu_fdt_setprop_cell(ms->fdt, nodename, "bank-width", 4); + g_free(nodename); +} + +static void fdt_add_rtc_node(LoongArchMachineState *lams) +{ + char *nodename; + hwaddr base = VIRT_RTC_REG_BASE; + hwaddr size = VIRT_RTC_LEN; + MachineState *ms = MACHINE(lams); + + nodename = g_strdup_printf("/rtc@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "loongson,ls7a-rtc"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); + g_free(nodename); +} + +static void fdt_add_uart_node(LoongArchMachineState *lams) +{ + char *nodename; + hwaddr base = VIRT_UART_BASE; + hwaddr size = VIRT_UART_SIZE; + MachineState *ms = MACHINE(lams); + + nodename = g_strdup_printf("/serial@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "ns16550a"); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, base, 0x0, size); + qemu_fdt_setprop_cell(ms->fdt, nodename, "clock-frequency", 100000000); + qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); + g_free(nodename); +} + +static void create_fdt(LoongArchMachineState *lams) +{ + MachineState *ms = MACHINE(lams); + + ms->fdt = create_device_tree(&lams->fdt_size); + if (!ms->fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + /* Header */ + qemu_fdt_setprop_string(ms->fdt, "/", "compatible", + "linux,dummy-loongson3"); + qemu_fdt_setprop_cell(ms->fdt, "/", "#address-cells", 0x2); + qemu_fdt_setprop_cell(ms->fdt, "/", "#size-cells", 0x2); + qemu_fdt_add_subnode(ms->fdt, "/chosen"); +} + +static void fdt_add_cpu_nodes(const LoongArchMachineState *lams) +{ + int num; + const MachineState *ms = MACHINE(lams); + int smp_cpus = ms->smp.cpus; + + qemu_fdt_add_subnode(ms->fdt, "/cpus"); + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1); + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0); + + /* cpu nodes */ + for (num = smp_cpus - 1; num >= 0; num--) { + char *nodename = g_strdup_printf("/cpus/cpu@%d", num); + LoongArchCPU *cpu = LOONGARCH_CPU(qemu_get_cpu(num)); + CPUState *cs = CPU(cpu); + + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu"); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + cpu->dtb_compatible); + if (ms->possible_cpus->cpus[cs->cpu_index].props.has_node_id) { + qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", + ms->possible_cpus->cpus[cs->cpu_index].props.node_id); + } + qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", + qemu_fdt_alloc_phandle(ms->fdt)); + g_free(nodename); + } + + /*cpu map */ + qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map"); + + for (num = smp_cpus - 1; num >= 0; num--) { + char *cpu_path = g_strdup_printf("/cpus/cpu@%d", num); + char *map_path; + + if (ms->smp.threads > 1) { + map_path = g_strdup_printf( + "/cpus/cpu-map/socket%d/core%d/thread%d", + num / (ms->smp.cores * ms->smp.threads), + (num / ms->smp.threads) % ms->smp.cores, + num % ms->smp.threads); + } else { + map_path = g_strdup_printf( + "/cpus/cpu-map/socket%d/core%d", + num / ms->smp.cores, + num % ms->smp.cores); + } + qemu_fdt_add_path(ms->fdt, map_path); + qemu_fdt_setprop_phandle(ms->fdt, map_path, "cpu", cpu_path); + + g_free(map_path); + g_free(cpu_path); + } +} + +static void fdt_add_fw_cfg_node(const LoongArchMachineState *lams) +{ + char *nodename; + hwaddr base = VIRT_FWCFG_BASE; + const MachineState *ms = MACHINE(lams); + + nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, + "compatible", "qemu,fw-cfg-mmio"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", + 2, base, 2, 0x18); + qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); + g_free(nodename); +} + +static void fdt_add_pcie_node(const LoongArchMachineState *lams) +{ + char *nodename; + hwaddr base_mmio = VIRT_PCI_MEM_BASE; + hwaddr size_mmio = VIRT_PCI_MEM_SIZE; + hwaddr base_pio = VIRT_PCI_IO_BASE; + hwaddr size_pio = VIRT_PCI_IO_SIZE; + hwaddr base_pcie = VIRT_PCI_CFG_BASE; + hwaddr size_pcie = VIRT_PCI_CFG_SIZE; + hwaddr base = base_pcie; + + const MachineState *ms = MACHINE(lams); + + nodename = g_strdup_printf("/pcie@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, + "compatible", "pci-host-ecam-generic"); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "pci"); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 3); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 2); + qemu_fdt_setprop_cell(ms->fdt, nodename, "linux,pci-domain", 0); + qemu_fdt_setprop_cells(ms->fdt, nodename, "bus-range", 0, + PCIE_MMCFG_BUS(VIRT_PCI_CFG_SIZE - 1)); + qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", + 2, base_pcie, 2, size_pcie); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges", + 1, FDT_PCI_RANGE_IOPORT, 2, VIRT_PCI_IO_OFFSET, + 2, base_pio, 2, size_pio, + 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, + 2, base_mmio, 2, size_mmio); + g_free(nodename); +} + +static void fdt_add_irqchip_node(LoongArchMachineState *lams) +{ + MachineState *ms = MACHINE(lams); + char *nodename; + uint32_t irqchip_phandle; + + irqchip_phandle = qemu_fdt_alloc_phandle(ms->fdt); + qemu_fdt_setprop_cell(ms->fdt, "/", "interrupt-parent", irqchip_phandle); + + nodename = g_strdup_printf("/intc@%lx", VIRT_IOAPIC_REG_BASE); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 3); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 0x2); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 0x2); + qemu_fdt_setprop(ms->fdt, nodename, "ranges", NULL, 0); + + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongarch,ls7a"); + + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", + 2, VIRT_IOAPIC_REG_BASE, + 2, PCH_PIC_ROUTE_ENTRY_OFFSET); + + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", irqchip_phandle); + g_free(nodename); +} + +static void fdt_add_memory_node(MachineState *ms, + uint64_t base, uint64_t size, int node_id) +{ + char *nodename = g_strdup_printf("/memory@%" PRIx64, base); + + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 2, base, 2, size); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory"); + + if (ms->numa_state && ms->numa_state->num_nodes) { + qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", node_id); + } + + g_free(nodename); +} + +static void virt_build_smbios(LoongArchMachineState *lams) +{ + MachineState *ms = MACHINE(lams); + MachineClass *mc = MACHINE_GET_CLASS(lams); + uint8_t *smbios_tables, *smbios_anchor; + size_t smbios_tables_len, smbios_anchor_len; + const char *product = "QEMU Virtual Machine"; + + if (!lams->fw_cfg) { + return; + } + + smbios_set_defaults("QEMU", product, mc->name, true); + + smbios_get_tables(ms, SMBIOS_ENTRY_POINT_TYPE_64, + NULL, 0, + &smbios_tables, &smbios_tables_len, + &smbios_anchor, &smbios_anchor_len, &error_fatal); + + if (smbios_anchor) { + fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-tables", + smbios_tables, smbios_tables_len); + fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-anchor", + smbios_anchor, smbios_anchor_len); + } +} + +static void virt_machine_done(Notifier *notifier, void *data) +{ + LoongArchMachineState *lams = container_of(notifier, + LoongArchMachineState, machine_done); + virt_build_smbios(lams); + loongarch_acpi_setup(lams); +} + +static void virt_powerdown_req(Notifier *notifier, void *opaque) +{ + LoongArchMachineState *s = container_of(notifier, + LoongArchMachineState, powerdown_notifier); + + acpi_send_event(s->acpi_ged, ACPI_POWER_DOWN_STATUS); +} + +struct memmap_entry { + uint64_t address; + uint64_t length; + uint32_t type; + uint32_t reserved; +}; + +static struct memmap_entry *memmap_table; +static unsigned memmap_entries; + +static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) +{ + /* Ensure there are no duplicate entries. */ + for (unsigned i = 0; i < memmap_entries; i++) { + assert(memmap_table[i].address != address); + } + + memmap_table = g_renew(struct memmap_entry, memmap_table, + memmap_entries + 1); + memmap_table[memmap_entries].address = cpu_to_le64(address); + memmap_table[memmap_entries].length = cpu_to_le64(length); + memmap_table[memmap_entries].type = cpu_to_le32(type); + memmap_table[memmap_entries].reserved = 0; + memmap_entries++; +} + +static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) +{ + return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS); +} + +static int64_t load_kernel_info(const struct loaderparams *loaderparams) +{ + uint64_t kernel_entry, kernel_low, kernel_high; + ssize_t kernel_size; + + kernel_size = load_elf(loaderparams->kernel_filename, NULL, + cpu_loongarch_virt_to_phys, NULL, + &kernel_entry, &kernel_low, + &kernel_high, NULL, 0, + EM_LOONGARCH, 1, 0); + + if (kernel_size < 0) { + error_report("could not load kernel '%s': %s", + loaderparams->kernel_filename, + load_elf_strerror(kernel_size)); + exit(1); + } + return kernel_entry; +} + +static DeviceState *create_acpi_ged(DeviceState *pch_pic, LoongArchMachineState *lams) +{ + DeviceState *dev; + MachineState *ms = MACHINE(lams); + uint32_t event = ACPI_GED_PWR_DOWN_EVT; + + if (ms->ram_slots) { + event |= ACPI_GED_MEM_HOTPLUG_EVT; + } + dev = qdev_new(TYPE_ACPI_GED); + qdev_prop_set_uint32(dev, "ged-event", event); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + /* ged event */ + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, VIRT_GED_EVT_ADDR); + /* memory hotplug */ + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, VIRT_GED_MEM_ADDR); + /* ged regs used for reset and power down */ + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, VIRT_GED_REG_ADDR); + + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + qdev_get_gpio_in(pch_pic, VIRT_SCI_IRQ - VIRT_GSI_BASE)); + return dev; +} + +static DeviceState *create_platform_bus(DeviceState *pch_pic) +{ + DeviceState *dev; + SysBusDevice *sysbus; + int i, irq; + MemoryRegion *sysmem = get_system_memory(); + + dev = qdev_new(TYPE_PLATFORM_BUS_DEVICE); + dev->id = g_strdup(TYPE_PLATFORM_BUS_DEVICE); + qdev_prop_set_uint32(dev, "num_irqs", VIRT_PLATFORM_BUS_NUM_IRQS); + qdev_prop_set_uint32(dev, "mmio_size", VIRT_PLATFORM_BUS_SIZE); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + sysbus = SYS_BUS_DEVICE(dev); + for (i = 0; i < VIRT_PLATFORM_BUS_NUM_IRQS; i++) { + irq = VIRT_PLATFORM_BUS_IRQ - VIRT_GSI_BASE + i; + sysbus_connect_irq(sysbus, i, qdev_get_gpio_in(pch_pic, irq)); + } + + memory_region_add_subregion(sysmem, + VIRT_PLATFORM_BUS_BASEADDRESS, + sysbus_mmio_get_region(sysbus, 0)); + return dev; +} + +static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState *lams) +{ + MachineClass *mc = MACHINE_GET_CLASS(lams); + DeviceState *gpex_dev; + SysBusDevice *d; + PCIBus *pci_bus; + MemoryRegion *ecam_alias, *ecam_reg, *pio_alias, *pio_reg; + MemoryRegion *mmio_alias, *mmio_reg; + int i; + + gpex_dev = qdev_new(TYPE_GPEX_HOST); + d = SYS_BUS_DEVICE(gpex_dev); + sysbus_realize_and_unref(d, &error_fatal); + pci_bus = PCI_HOST_BRIDGE(gpex_dev)->bus; + lams->pci_bus = pci_bus; + + /* Map only part size_ecam bytes of ECAM space */ + ecam_alias = g_new0(MemoryRegion, 1); + ecam_reg = sysbus_mmio_get_region(d, 0); + memory_region_init_alias(ecam_alias, OBJECT(gpex_dev), "pcie-ecam", + ecam_reg, 0, VIRT_PCI_CFG_SIZE); + memory_region_add_subregion(get_system_memory(), VIRT_PCI_CFG_BASE, + ecam_alias); + + /* Map PCI mem space */ + mmio_alias = g_new0(MemoryRegion, 1); + mmio_reg = sysbus_mmio_get_region(d, 1); + memory_region_init_alias(mmio_alias, OBJECT(gpex_dev), "pcie-mmio", + mmio_reg, VIRT_PCI_MEM_BASE, VIRT_PCI_MEM_SIZE); + memory_region_add_subregion(get_system_memory(), VIRT_PCI_MEM_BASE, + mmio_alias); + + /* Map PCI IO port space. */ + pio_alias = g_new0(MemoryRegion, 1); + pio_reg = sysbus_mmio_get_region(d, 2); + memory_region_init_alias(pio_alias, OBJECT(gpex_dev), "pcie-io", pio_reg, + VIRT_PCI_IO_OFFSET, VIRT_PCI_IO_SIZE); + memory_region_add_subregion(get_system_memory(), VIRT_PCI_IO_BASE, + pio_alias); + + for (i = 0; i < GPEX_NUM_IRQS; i++) { + sysbus_connect_irq(d, i, + qdev_get_gpio_in(pch_pic, 16 + i)); + gpex_set_irq_num(GPEX_HOST(gpex_dev), i, 16 + i); + } + + serial_mm_init(get_system_memory(), VIRT_UART_BASE, 0, + qdev_get_gpio_in(pch_pic, + VIRT_UART_IRQ - VIRT_GSI_BASE), + 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); + fdt_add_uart_node(lams); + + /* Network init */ + pci_init_nic_devices(pci_bus, mc->default_nic); + + /* + * There are some invalid guest memory access. + * Create some unimplemented devices to emulate this. + */ + create_unimplemented_device("pci-dma-cfg", 0x1001041c, 0x4); + sysbus_create_simple("ls7a_rtc", VIRT_RTC_REG_BASE, + qdev_get_gpio_in(pch_pic, + VIRT_RTC_IRQ - VIRT_GSI_BASE)); + fdt_add_rtc_node(lams); + + /* acpi ged */ + lams->acpi_ged = create_acpi_ged(pch_pic, lams); + /* platform bus */ + lams->platform_bus_dev = create_platform_bus(pch_pic); +} + +static void loongarch_irq_init(LoongArchMachineState *lams) +{ + MachineState *ms = MACHINE(lams); + DeviceState *pch_pic, *pch_msi, *cpudev; + DeviceState *ipi, *extioi; + SysBusDevice *d; + LoongArchCPU *lacpu; + CPULoongArchState *env; + CPUState *cpu_state; + int cpu, pin, i, start, num; + + /* + * The connection of interrupts: + * +-----+ +---------+ +-------+ + * | IPI |--> | CPUINTC | <-- | Timer | + * +-----+ +---------+ +-------+ + * ^ + * | + * +---------+ + * | EIOINTC | + * +---------+ + * ^ ^ + * | | + * +---------+ +---------+ + * | PCH-PIC | | PCH-MSI | + * +---------+ +---------+ + * ^ ^ ^ + * | | | + * +--------+ +---------+ +---------+ + * | UARTs | | Devices | | Devices | + * +--------+ +---------+ +---------+ + */ + + /* Create IPI device */ + ipi = qdev_new(TYPE_LOONGARCH_IPI); + qdev_prop_set_uint32(ipi, "num-cpu", ms->smp.cpus); + sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); + + /* IPI iocsr memory region */ + memory_region_add_subregion(&lams->system_iocsr, SMP_IPI_MAILBOX, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0)); + memory_region_add_subregion(&lams->system_iocsr, MAIL_SEND_ADDR, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); + + for (cpu = 0; cpu < ms->smp.cpus; cpu++) { + cpu_state = qemu_get_cpu(cpu); + cpudev = DEVICE(cpu_state); + lacpu = LOONGARCH_CPU(cpu_state); + env = &(lacpu->env); + env->address_space_iocsr = &lams->as_iocsr; + + /* connect ipi irq to cpu irq */ + qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI)); + env->ipistate = ipi; + } + + /* Create EXTIOI device */ + extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); + qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus); + sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); + memory_region_add_subregion(&lams->system_iocsr, APIC_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); + + /* + * connect ext irq to the cpu irq + * cpu_pin[9:2] <= intc_pin[7:0] + */ + for (cpu = 0; cpu < ms->smp.cpus; cpu++) { + cpudev = DEVICE(qemu_get_cpu(cpu)); + for (pin = 0; pin < LS3A_INTC_IP; pin++) { + qdev_connect_gpio_out(extioi, (cpu * 8 + pin), + qdev_get_gpio_in(cpudev, pin + 2)); + } + } + + pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC); + num = VIRT_PCH_PIC_IRQ_NUM; + qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num); + d = SYS_BUS_DEVICE(pch_pic); + sysbus_realize_and_unref(d, &error_fatal); + memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE, + sysbus_mmio_get_region(d, 0)); + memory_region_add_subregion(get_system_memory(), + VIRT_IOAPIC_REG_BASE + PCH_PIC_ROUTE_ENTRY_OFFSET, + sysbus_mmio_get_region(d, 1)); + memory_region_add_subregion(get_system_memory(), + VIRT_IOAPIC_REG_BASE + PCH_PIC_INT_STATUS_LO, + sysbus_mmio_get_region(d, 2)); + + /* Connect pch_pic irqs to extioi */ + for (i = 0; i < num; i++) { + qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i)); + } + + pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); + start = num; + num = EXTIOI_IRQS - start; + qdev_prop_set_uint32(pch_msi, "msi_irq_base", start); + qdev_prop_set_uint32(pch_msi, "msi_irq_num", num); + d = SYS_BUS_DEVICE(pch_msi); + sysbus_realize_and_unref(d, &error_fatal); + sysbus_mmio_map(d, 0, VIRT_PCH_MSI_ADDR_LOW); + for (i = 0; i < num; i++) { + /* Connect pch_msi irqs to extioi */ + qdev_connect_gpio_out(DEVICE(d), i, + qdev_get_gpio_in(extioi, i + start)); + } + + loongarch_devices_init(pch_pic, lams); +} + +static void loongarch_firmware_init(LoongArchMachineState *lams) +{ + char *filename = MACHINE(lams)->firmware; + char *bios_name = NULL; + int bios_size, i; + BlockBackend *pflash_blk0; + MemoryRegion *mr; + + lams->bios_loaded = false; + + /* Map legacy -drive if=pflash to machine properties */ + for (i = 0; i < ARRAY_SIZE(lams->flash); i++) { + pflash_cfi01_legacy_drive(lams->flash[i], + drive_get(IF_PFLASH, 0, i)); + } + + virt_flash_map(lams, get_system_memory()); + + pflash_blk0 = pflash_cfi01_get_blk(lams->flash[0]); + + if (pflash_blk0) { + if (filename) { + error_report("cannot use both '-bios' and '-drive if=pflash'" + "options at once"); + exit(1); + } + lams->bios_loaded = true; + return; + } + + if (filename) { + bios_name = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename); + if (!bios_name) { + error_report("Could not find ROM image '%s'", filename); + exit(1); + } + + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(lams->flash[0]), 0); + bios_size = load_image_mr(bios_name, mr); + if (bios_size < 0) { + error_report("Could not load ROM image '%s'", bios_name); + exit(1); + } + g_free(bios_name); + lams->bios_loaded = true; + } +} + +static void reset_load_elf(void *opaque) +{ + LoongArchCPU *cpu = opaque; + CPULoongArchState *env = &cpu->env; + + cpu_reset(CPU(cpu)); + if (env->load_elf) { + cpu_set_pc(CPU(cpu), env->elf_address); + } +} + +static void fw_cfg_add_kernel_info(const struct loaderparams *loaderparams, + FWCfgState *fw_cfg) +{ + /* + * Expose the kernel, the command line, and the initrd in fw_cfg. + * We don't process them here at all, it's all left to the + * firmware. + */ + load_image_to_fw_cfg(fw_cfg, + FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA, + loaderparams->kernel_filename, + false); + + if (loaderparams->initrd_filename) { + load_image_to_fw_cfg(fw_cfg, + FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, + loaderparams->initrd_filename, false); + } + + if (loaderparams->kernel_cmdline) { + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(loaderparams->kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, + loaderparams->kernel_cmdline); + } +} + +static void loongarch_firmware_boot(LoongArchMachineState *lams, + const struct loaderparams *loaderparams) +{ + fw_cfg_add_kernel_info(loaderparams, lams->fw_cfg); +} + +static void loongarch_direct_kernel_boot(LoongArchMachineState *lams, + const struct loaderparams *loaderparams) +{ + MachineState *machine = MACHINE(lams); + int64_t kernel_addr = 0; + LoongArchCPU *lacpu; + int i; + + kernel_addr = load_kernel_info(loaderparams); + if (!machine->firmware) { + for (i = 0; i < machine->smp.cpus; i++) { + lacpu = LOONGARCH_CPU(qemu_get_cpu(i)); + lacpu->env.load_elf = true; + lacpu->env.elf_address = kernel_addr; + } + } +} + +static void loongarch_qemu_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ +} + +static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size) +{ + switch (addr) { + case VERSION_REG: + return 0x11ULL; + case FEATURE_REG: + return 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI | + 1ULL << IOCSRF_CSRIPI; + case VENDOR_REG: + return 0x6e6f73676e6f6f4cULL; /* "Loongson" */ + case CPUNAME_REG: + return 0x303030354133ULL; /* "3A5000" */ + case MISC_FUNC_REG: + return 1ULL << IOCSRM_EXTIOI_EN; + } + return 0ULL; +} + +static const MemoryRegionOps loongarch_qemu_ops = { + .read = loongarch_qemu_read, + .write = loongarch_qemu_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static void loongarch_init(MachineState *machine) +{ + LoongArchCPU *lacpu; + const char *cpu_model = machine->cpu_type; + ram_addr_t offset = 0; + ram_addr_t ram_size = machine->ram_size; + uint64_t highram_size = 0, phyAddr = 0; + MemoryRegion *address_space_mem = get_system_memory(); + LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + int nb_numa_nodes = machine->numa_state->num_nodes; + NodeInfo *numa_info = machine->numa_state->nodes; + int i; + hwaddr fdt_base; + const CPUArchIdList *possible_cpus; + MachineClass *mc = MACHINE_GET_CLASS(machine); + CPUState *cpu; + char *ramName = NULL; + struct loaderparams loaderparams = { }; + + if (!cpu_model) { + cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); + } + + if (ram_size < 1 * GiB) { + error_report("ram_size must be greater than 1G."); + exit(1); + } + create_fdt(lams); + + /* Create IOCSR space */ + memory_region_init_io(&lams->system_iocsr, OBJECT(machine), NULL, + machine, "iocsr", UINT64_MAX); + address_space_init(&lams->as_iocsr, &lams->system_iocsr, "IOCSR"); + memory_region_init_io(&lams->iocsr_mem, OBJECT(machine), + &loongarch_qemu_ops, + machine, "iocsr_misc", 0x428); + memory_region_add_subregion(&lams->system_iocsr, 0, &lams->iocsr_mem); + + /* Init CPUs */ + possible_cpus = mc->possible_cpu_arch_ids(machine); + for (i = 0; i < possible_cpus->len; i++) { + cpu = cpu_create(machine->cpu_type); + cpu->cpu_index = i; + machine->possible_cpus->cpus[i].cpu = cpu; + lacpu = LOONGARCH_CPU(cpu); + lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id; + } + fdt_add_cpu_nodes(lams); + + /* Node0 memory */ + memmap_add_entry(VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 1); + fdt_add_memory_node(machine, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 0); + memory_region_init_alias(&lams->lowmem, NULL, "loongarch.node0.lowram", + machine->ram, offset, VIRT_LOWMEM_SIZE); + memory_region_add_subregion(address_space_mem, phyAddr, &lams->lowmem); + + offset += VIRT_LOWMEM_SIZE; + if (nb_numa_nodes > 0) { + assert(numa_info[0].node_mem > VIRT_LOWMEM_SIZE); + highram_size = numa_info[0].node_mem - VIRT_LOWMEM_SIZE; + } else { + highram_size = ram_size - VIRT_LOWMEM_SIZE; + } + phyAddr = VIRT_HIGHMEM_BASE; + memmap_add_entry(phyAddr, highram_size, 1); + fdt_add_memory_node(machine, phyAddr, highram_size, 0); + memory_region_init_alias(&lams->highmem, NULL, "loongarch.node0.highram", + machine->ram, offset, highram_size); + memory_region_add_subregion(address_space_mem, phyAddr, &lams->highmem); + + /* Node1 - Nodemax memory */ + offset += highram_size; + phyAddr += highram_size; + + for (i = 1; i < nb_numa_nodes; i++) { + MemoryRegion *nodemem = g_new(MemoryRegion, 1); + ramName = g_strdup_printf("loongarch.node%d.ram", i); + memory_region_init_alias(nodemem, NULL, ramName, machine->ram, + offset, numa_info[i].node_mem); + memory_region_add_subregion(address_space_mem, phyAddr, nodemem); + memmap_add_entry(phyAddr, numa_info[i].node_mem, 1); + fdt_add_memory_node(machine, phyAddr, numa_info[i].node_mem, i); + offset += numa_info[i].node_mem; + phyAddr += numa_info[i].node_mem; + } + + /* initialize device memory address space */ + if (machine->ram_size < machine->maxram_size) { + ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size; + hwaddr device_mem_base; + + if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) { + error_report("unsupported amount of memory slots: %"PRIu64, + machine->ram_slots); + exit(EXIT_FAILURE); + } + + if (QEMU_ALIGN_UP(machine->maxram_size, + TARGET_PAGE_SIZE) != machine->maxram_size) { + error_report("maximum memory size must by aligned to multiple of " + "%d bytes", TARGET_PAGE_SIZE); + exit(EXIT_FAILURE); + } + /* device memory base is the top of high memory address. */ + device_mem_base = ROUND_UP(VIRT_HIGHMEM_BASE + highram_size, 1 * GiB); + machine_memory_devices_init(machine, device_mem_base, device_mem_size); + } + + /* load the BIOS image. */ + loongarch_firmware_init(lams); + + /* fw_cfg init */ + lams->fw_cfg = loongarch_fw_cfg_init(ram_size, machine); + rom_set_fw(lams->fw_cfg); + if (lams->fw_cfg != NULL) { + fw_cfg_add_file(lams->fw_cfg, "etc/memmap", + memmap_table, + sizeof(struct memmap_entry) * (memmap_entries)); + } + fdt_add_fw_cfg_node(lams); + loaderparams.ram_size = ram_size; + loaderparams.kernel_filename = machine->kernel_filename; + loaderparams.kernel_cmdline = machine->kernel_cmdline; + loaderparams.initrd_filename = machine->initrd_filename; + /* load the kernel. */ + if (loaderparams.kernel_filename) { + if (lams->bios_loaded) { + loongarch_firmware_boot(lams, &loaderparams); + } else { + loongarch_direct_kernel_boot(lams, &loaderparams); + } + } + fdt_add_flash_node(lams); + /* register reset function */ + for (i = 0; i < machine->smp.cpus; i++) { + lacpu = LOONGARCH_CPU(qemu_get_cpu(i)); + qemu_register_reset(reset_load_elf, lacpu); + } + /* Initialize the IO interrupt subsystem */ + loongarch_irq_init(lams); + fdt_add_irqchip_node(lams); + platform_bus_add_all_fdt_nodes(machine->fdt, "/intc", + VIRT_PLATFORM_BUS_BASEADDRESS, + VIRT_PLATFORM_BUS_SIZE, + VIRT_PLATFORM_BUS_IRQ); + lams->machine_done.notify = virt_machine_done; + qemu_add_machine_init_done_notifier(&lams->machine_done); + /* connect powerdown request */ + lams->powerdown_notifier.notify = virt_powerdown_req; + qemu_register_powerdown_notifier(&lams->powerdown_notifier); + + fdt_add_pcie_node(lams); + /* + * Since lowmem region starts from 0 and Linux kernel legacy start address + * at 2 MiB, FDT base address is located at 1 MiB to avoid NULL pointer + * access. FDT size limit with 1 MiB. + * Put the FDT into the memory map as a ROM image: this will ensure + * the FDT is copied again upon reset, even if addr points into RAM. + */ + fdt_base = 1 * MiB; + qemu_fdt_dumpdtb(machine->fdt, lams->fdt_size); + rom_add_blob_fixed("fdt", machine->fdt, lams->fdt_size, fdt_base); +} + +bool loongarch_is_acpi_enabled(LoongArchMachineState *lams) +{ + if (lams->acpi == ON_OFF_AUTO_OFF) { + return false; + } + return true; +} + +static void loongarch_get_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + OnOffAuto acpi = lams->acpi; + + visit_type_OnOffAuto(v, name, &acpi, errp); +} + +static void loongarch_set_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &lams->acpi, errp); +} + +static void loongarch_machine_initfn(Object *obj) +{ + LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + + lams->acpi = ON_OFF_AUTO_AUTO; + lams->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); + lams->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); + virt_flash_create(lams); +} + +static bool memhp_type_supported(DeviceState *dev) +{ + /* we only support pc dimm now */ + return object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) && + !object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); +} + +static void virt_mem_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp); +} + +static void virt_machine_device_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (memhp_type_supported(dev)) { + virt_mem_pre_plug(hotplug_dev, dev, errp); + } +} + +static void virt_mem_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); + + /* the acpi ged is always exist */ + hotplug_handler_unplug_request(HOTPLUG_HANDLER(lams->acpi_ged), dev, + errp); +} + +static void virt_machine_device_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (memhp_type_supported(dev)) { + virt_mem_unplug_request(hotplug_dev, dev, errp); + } +} + +static void virt_mem_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); + + hotplug_handler_unplug(HOTPLUG_HANDLER(lams->acpi_ged), dev, errp); + pc_dimm_unplug(PC_DIMM(dev), MACHINE(lams)); + qdev_unrealize(dev); +} + +static void virt_machine_device_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (memhp_type_supported(dev)) { + virt_mem_unplug(hotplug_dev, dev, errp); + } +} + +static void virt_mem_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); + + pc_dimm_plug(PC_DIMM(dev), MACHINE(lams)); + hotplug_handler_plug(HOTPLUG_HANDLER(lams->acpi_ged), + dev, &error_abort); +} + +static void loongarch_machine_device_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); + MachineClass *mc = MACHINE_GET_CLASS(lams); + + if (device_is_dynamic_sysbus(mc, dev)) { + if (lams->platform_bus_dev) { + platform_bus_link_device(PLATFORM_BUS_DEVICE(lams->platform_bus_dev), + SYS_BUS_DEVICE(dev)); + } + } else if (memhp_type_supported(dev)) { + virt_mem_plug(hotplug_dev, dev, errp); + } +} + +static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, + DeviceState *dev) +{ + MachineClass *mc = MACHINE_GET_CLASS(machine); + + if (device_is_dynamic_sysbus(mc, dev) || + memhp_type_supported(dev)) { + return HOTPLUG_HANDLER(machine); + } + return NULL; +} + +static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) +{ + int n; + unsigned int max_cpus = ms->smp.max_cpus; + + if (ms->possible_cpus) { + assert(ms->possible_cpus->len == max_cpus); + return ms->possible_cpus; + } + + ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) + + sizeof(CPUArchId) * max_cpus); + ms->possible_cpus->len = max_cpus; + for (n = 0; n < ms->possible_cpus->len; n++) { + ms->possible_cpus->cpus[n].type = ms->cpu_type; + ms->possible_cpus->cpus[n].arch_id = n; + + ms->possible_cpus->cpus[n].props.has_socket_id = true; + ms->possible_cpus->cpus[n].props.socket_id = + n / (ms->smp.cores * ms->smp.threads); + ms->possible_cpus->cpus[n].props.has_core_id = true; + ms->possible_cpus->cpus[n].props.core_id = + n / ms->smp.threads % ms->smp.cores; + ms->possible_cpus->cpus[n].props.has_thread_id = true; + ms->possible_cpus->cpus[n].props.thread_id = n % ms->smp.threads; + } + return ms->possible_cpus; +} + +static CpuInstanceProperties +virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); + + assert(cpu_index < possible_cpus->len); + return possible_cpus->cpus[cpu_index].props; +} + +static int64_t virt_get_default_cpu_node_id(const MachineState *ms, int idx) +{ + int64_t nidx = 0; + + if (ms->numa_state->num_nodes) { + nidx = idx / (ms->smp.cpus / ms->numa_state->num_nodes); + if (ms->numa_state->num_nodes <= nidx) { + nidx = ms->numa_state->num_nodes - 1; + } + } + return nidx; +} + +static void loongarch_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + + mc->desc = "Loongson-3A5000 LS7A1000 machine"; + mc->init = loongarch_init; + mc->default_ram_size = 1 * GiB; + mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("la464"); + mc->default_ram_id = "loongarch.ram"; + mc->max_cpus = LOONGARCH_MAX_CPUS; + mc->is_default = 1; + mc->default_kernel_irqchip_split = false; + mc->block_default_type = IF_VIRTIO; + mc->default_boot_order = "c"; + mc->no_cdrom = 1; + mc->possible_cpu_arch_ids = virt_possible_cpu_arch_ids; + mc->cpu_index_to_instance_props = virt_cpu_index_to_props; + mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; + mc->numa_mem_supported = true; + mc->auto_enable_numa_with_memhp = true; + mc->auto_enable_numa_with_memdev = true; + mc->get_hotplug_handler = virt_machine_get_hotplug_handler; + mc->default_nic = "virtio-net-pci"; + hc->plug = loongarch_machine_device_plug_cb; + hc->pre_plug = virt_machine_device_pre_plug; + hc->unplug_request = virt_machine_device_unplug_request; + hc->unplug = virt_machine_device_unplug; + + object_class_property_add(oc, "acpi", "OnOffAuto", + loongarch_get_acpi, loongarch_set_acpi, + NULL, NULL); + object_class_property_set_description(oc, "acpi", + "Enable ACPI"); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); +#ifdef CONFIG_TPM + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); +#endif +} + +static const TypeInfo loongarch_machine_types[] = { + { + .name = TYPE_LOONGARCH_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(LoongArchMachineState), + .class_init = loongarch_class_init, + .instance_init = loongarch_machine_initfn, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, + } +}; + +DEFINE_TYPES(loongarch_machine_types) diff --git a/hw/m68k/Kconfig b/hw/m68k/Kconfig index f839f8a030..d88741ec9d 100644 --- a/hw/m68k/Kconfig +++ b/hw/m68k/Kconfig @@ -23,6 +23,9 @@ config Q800 select ESP select DP8393X select OR_IRQ + select DJMEMC + select IOSB + select ASC config M68K_VIRT bool diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index 11ae4c9795..1e8e64f8bd 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -20,12 +20,14 @@ #define AN5206_MBAR_ADDR 0x10000000 #define AN5206_RAMBAR_ADDR 0x20000000 -static void mcf5206_init(MemoryRegion *sysmem, uint32_t base) +static void mcf5206_init(M68kCPU *cpu, MemoryRegion *sysmem, uint32_t base) { DeviceState *dev; SysBusDevice *s; dev = qdev_new(TYPE_MCF5206_MBAR); + object_property_set_link(OBJECT(dev), "m68k-cpu", + OBJECT(cpu), &error_abort); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); @@ -60,7 +62,7 @@ static void an5206_init(MachineState *machine) memory_region_init_ram(sram, NULL, "an5206.sram", 512, &error_fatal); memory_region_add_subregion(address_space_mem, AN5206_RAMBAR_ADDR, sram); - mcf5206_init(address_space_mem, AN5206_MBAR_ADDR); + mcf5206_init(cpu, address_space_mem, AN5206_MBAR_ADDR); /* Load kernel. */ if (!kernel_filename) { diff --git a/hw/m68k/bootinfo.h b/hw/m68k/bootinfo.h index adbf0c5521..0e6e3eea87 100644 --- a/hw/m68k/bootinfo.h +++ b/hw/m68k/bootinfo.h @@ -12,48 +12,64 @@ #ifndef HW_M68K_BOOTINFO_H #define HW_M68K_BOOTINFO_H -#define BOOTINFO0(as, base, id) \ +#define BOOTINFO0(base, id) \ do { \ - stw_phys(as, base, id); \ + stw_p(base, id); \ base += 2; \ - stw_phys(as, base, sizeof(struct bi_record)); \ + stw_p(base, sizeof(struct bi_record)); \ base += 2; \ } while (0) -#define BOOTINFO1(as, base, id, value) \ +#define BOOTINFO1(base, id, value) \ do { \ - stw_phys(as, base, id); \ + stw_p(base, id); \ base += 2; \ - stw_phys(as, base, sizeof(struct bi_record) + 4); \ + stw_p(base, sizeof(struct bi_record) + 4); \ base += 2; \ - stl_phys(as, base, value); \ + stl_p(base, value); \ base += 4; \ } while (0) -#define BOOTINFO2(as, base, id, value1, value2) \ +#define BOOTINFO2(base, id, value1, value2) \ do { \ - stw_phys(as, base, id); \ + stw_p(base, id); \ base += 2; \ - stw_phys(as, base, sizeof(struct bi_record) + 8); \ + stw_p(base, sizeof(struct bi_record) + 8); \ base += 2; \ - stl_phys(as, base, value1); \ + stl_p(base, value1); \ base += 4; \ - stl_phys(as, base, value2); \ + stl_p(base, value2); \ base += 4; \ } while (0) -#define BOOTINFOSTR(as, base, id, string) \ +#define BOOTINFOSTR(base, id, string) \ do { \ - int i; \ - stw_phys(as, base, id); \ + stw_p(base, id); \ base += 2; \ - stw_phys(as, base, \ - (sizeof(struct bi_record) + strlen(string) + 2) & ~1); \ + stw_p(base, \ + (sizeof(struct bi_record) + strlen(string) + \ + 1 /* null termination */ + 3 /* padding */) & ~3); \ base += 2; \ - for (i = 0; string[i]; i++) { \ - stb_phys(as, base++, string[i]); \ + for (unsigned i_ = 0; string[i_]; i_++) { \ + stb_p(base++, string[i_]); \ } \ - stb_phys(as, base++, 0); \ - base = (parameters_base + 1) & ~1; \ + stb_p(base++, 0); \ + base = QEMU_ALIGN_PTR_UP(base, 4); \ + } while (0) + +#define BOOTINFODATA(base, id, data, len) \ + do { \ + stw_p(base, id); \ + base += 2; \ + stw_p(base, \ + (sizeof(struct bi_record) + len + \ + 2 /* length field */ + 3 /* padding */) & ~3); \ + base += 2; \ + stw_p(base, len); \ + base += 2; \ + for (unsigned i_ = 0; i_ < len; ++i_) { \ + stb_p(base++, data[i_]); \ + } \ + base = QEMU_ALIGN_PTR_UP(base, 4); \ } while (0) #endif diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index 2ab1b4f059..183fd3cc08 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -10,6 +10,7 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "cpu.h" +#include "hw/qdev-properties.h" #include "hw/boards.h" #include "hw/irq.h" #include "hw/m68k/mcf.h" @@ -147,15 +148,11 @@ static void m5206_timer_write(m5206_timer_state *s, uint32_t addr, uint32_t val) m5206_timer_update(s); } -static m5206_timer_state *m5206_timer_init(qemu_irq irq) +static void m5206_timer_init(m5206_timer_state *s, qemu_irq irq) { - m5206_timer_state *s; - - s = g_new0(m5206_timer_state, 1); s->timer = ptimer_init(m5206_timer_trigger, s, PTIMER_POLICY_LEGACY); s->irq = irq; m5206_timer_reset(s); - return s; } /* System Integration Module. */ @@ -166,8 +163,8 @@ typedef struct { M68kCPU *cpu; MemoryRegion iomem; qemu_irq *pic; - m5206_timer_state *timer[2]; - void *uart[2]; + m5206_timer_state timer[2]; + DeviceState *uart[2]; uint8_t scr; uint8_t icr[14]; uint16_t imr; /* 1 == interrupt is masked. */ @@ -292,9 +289,9 @@ static uint64_t m5206_mbar_read(m5206_mbar_state *s, uint16_t offset, unsigned size) { if (offset >= 0x100 && offset < 0x120) { - return m5206_timer_read(s->timer[0], offset - 0x100); + return m5206_timer_read(&s->timer[0], offset - 0x100); } else if (offset >= 0x120 && offset < 0x140) { - return m5206_timer_read(s->timer[1], offset - 0x120); + return m5206_timer_read(&s->timer[1], offset - 0x120); } else if (offset >= 0x140 && offset < 0x160) { return mcf_uart_read(s->uart[0], offset - 0x140, size); } else if (offset >= 0x180 && offset < 0x1a0) { @@ -332,10 +329,10 @@ static void m5206_mbar_write(m5206_mbar_state *s, uint16_t offset, uint64_t value, unsigned size) { if (offset >= 0x100 && offset < 0x120) { - m5206_timer_write(s->timer[0], offset - 0x100, value); + m5206_timer_write(&s->timer[0], offset - 0x100, value); return; } else if (offset >= 0x120 && offset < 0x140) { - m5206_timer_write(s->timer[1], offset - 0x120, value); + m5206_timer_write(&s->timer[1], offset - 0x120, value); return; } else if (offset >= 0x140 && offset < 0x160) { mcf_uart_write(s->uart[0], offset - 0x140, value, size); @@ -597,17 +594,23 @@ static void mcf5206_mbar_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); s->pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14); - s->timer[0] = m5206_timer_init(s->pic[9]); - s->timer[1] = m5206_timer_init(s->pic[10]); - s->uart[0] = mcf_uart_init(s->pic[12], serial_hd(0)); - s->uart[1] = mcf_uart_init(s->pic[13], serial_hd(1)); - s->cpu = M68K_CPU(qemu_get_cpu(0)); + m5206_timer_init(&s->timer[0], s->pic[9]); + m5206_timer_init(&s->timer[1], s->pic[10]); + s->uart[0] = mcf_uart_create(s->pic[12], serial_hd(0)); + s->uart[1] = mcf_uart_create(s->pic[13], serial_hd(1)); } +static Property mcf5206_mbar_properties[] = { + DEFINE_PROP_LINK("m68k-cpu", m5206_mbar_state, cpu, + TYPE_M68K_CPU, M68kCPU *), + DEFINE_PROP_END_OF_LIST(), +}; + static void mcf5206_mbar_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + device_class_set_props(dc, mcf5206_mbar_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->desc = "MCF5206 system integration module"; dc->realize = mcf5206_mbar_realize; diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index be1033f84f..ec14096aa4 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -40,6 +40,8 @@ #define PCSR_PRE_SHIFT 8 #define PCSR_PRE_MASK 0x0f00 +#define RCR_SOFTRST 0x80 + typedef struct { MemoryRegion iomem; qemu_irq irq; @@ -185,12 +187,50 @@ static const MemoryRegionOps m5208_sys_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic) +static uint64_t m5208_rcm_read(void *opaque, hwaddr addr, + unsigned size) +{ + return 0; +} + +static void m5208_rcm_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + M68kCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + switch (addr) { + case 0x0: /* RCR */ + if (value & RCR_SOFTRST) { + cpu_reset(cs); + cpu->env.aregs[7] = ldl_phys(cs->as, 0); + cpu->env.pc = ldl_phys(cs->as, 4); + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\n", + __func__, addr); + break; + } +} + +static const MemoryRegionOps m5208_rcm_ops = { + .read = m5208_rcm_read, + .write = m5208_rcm_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic, + M68kCPU *cpu) { MemoryRegion *iomem = g_new(MemoryRegion, 1); + MemoryRegion *iomem_rcm = g_new(MemoryRegion, 1); m5208_timer_state *s; int i; + /* RCM */ + memory_region_init_io(iomem_rcm, NULL, &m5208_rcm_ops, cpu, + "m5208-rcm", 0x00000080); + memory_region_add_subregion(address_space, 0xfc0a0000, iomem_rcm); /* SDRAMC. */ memory_region_init_io(iomem, NULL, &m5208_sys_ops, NULL, "m5208-sys", 0x00004000); memory_region_add_subregion(address_space, 0xfc0a8000, iomem); @@ -206,16 +246,16 @@ static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic) } } -static void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd, hwaddr base, - qemu_irq *irqs) +static void mcf_fec_init(MemoryRegion *sysmem, hwaddr base, qemu_irq *irqs) { DeviceState *dev; SysBusDevice *s; int i; - qemu_check_nic_model(nd, TYPE_MCF_FEC_NET); - dev = qdev_new(TYPE_MCF_FEC_NET); - qdev_set_nic_properties(dev, nd); + dev = qemu_create_nic_device(TYPE_MCF_FEC_NET, true, NULL); + if (!dev) { + return; + } s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); @@ -261,20 +301,13 @@ static void mcf5208evb_init(MachineState *machine) /* Internal peripherals. */ pic = mcf_intc_init(address_space_mem, 0xfc048000, cpu); - mcf_uart_mm_init(0xfc060000, pic[26], serial_hd(0)); - mcf_uart_mm_init(0xfc064000, pic[27], serial_hd(1)); - mcf_uart_mm_init(0xfc068000, pic[28], serial_hd(2)); + mcf_uart_create_mmap(0xfc060000, pic[26], serial_hd(0)); + mcf_uart_create_mmap(0xfc064000, pic[27], serial_hd(1)); + mcf_uart_create_mmap(0xfc068000, pic[28], serial_hd(2)); - mcf5208_sys_init(address_space_mem, pic); + mcf5208_sys_init(address_space_mem, pic, cpu); - if (nb_nics > 1) { - error_report("Too many NICs"); - exit(1); - } - if (nd_table[0].used) { - mcf_fec_init(address_space_mem, &nd_table[0], - 0xfc030000, pic + 36); - } + mcf_fec_init(address_space_mem, 0xfc030000, pic + 36); g_free(pic); diff --git a/hw/m68k/mcf_intc.c b/hw/m68k/mcf_intc.c index 4cd30188c0..1d3b34e18c 100644 --- a/hw/m68k/mcf_intc.c +++ b/hw/m68k/mcf_intc.c @@ -14,6 +14,7 @@ #include "hw/irq.h" #include "hw/sysbus.h" #include "hw/m68k/mcf.h" +#include "hw/qdev-properties.h" #include "qom/object.h" #define TYPE_MCF_INTC "mcf-intc" @@ -173,12 +174,20 @@ static void mcf_intc_instance_init(Object *obj) mcf_intc_state *s = MCF_INTC(obj); memory_region_init_io(&s->iomem, obj, &mcf_intc_ops, s, "mcf", 0x100); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); } +static Property mcf_intc_properties[] = { + DEFINE_PROP_LINK("m68k-cpu", mcf_intc_state, cpu, + TYPE_M68K_CPU, M68kCPU *), + DEFINE_PROP_END_OF_LIST(), +}; + static void mcf_intc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + device_class_set_props(dc, mcf_intc_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->reset = mcf_intc_reset; } @@ -203,15 +212,13 @@ qemu_irq *mcf_intc_init(MemoryRegion *sysmem, M68kCPU *cpu) { DeviceState *dev; - mcf_intc_state *s; dev = qdev_new(TYPE_MCF_INTC); + object_property_set_link(OBJECT(dev), "m68k-cpu", + OBJECT(cpu), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + memory_region_add_subregion(sysmem, base, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); - s = MCF_INTC(dev); - s->cpu = cpu; - - memory_region_add_subregion(sysmem, base, &s->iomem); - - return qemu_allocate_irqs(mcf_intc_set_irq, s, 64); + return qemu_allocate_irqs(mcf_intc_set_irq, dev, 64); } diff --git a/hw/m68k/meson.build b/hw/m68k/meson.build index 31248641d3..84bc68fa4e 100644 --- a/hw/m68k/meson.build +++ b/hw/m68k/meson.build @@ -2,7 +2,7 @@ m68k_ss = ss.source_set() m68k_ss.add(when: 'CONFIG_AN5206', if_true: files('an5206.c', 'mcf5206.c')) m68k_ss.add(when: 'CONFIG_MCF5208', if_true: files('mcf5208.c', 'mcf_intc.c')) m68k_ss.add(when: 'CONFIG_NEXTCUBE', if_true: files('next-kbd.c', 'next-cube.c')) -m68k_ss.add(when: 'CONFIG_Q800', if_true: files('q800.c')) +m68k_ss.add(when: 'CONFIG_Q800', if_true: files('q800.c', 'q800-glue.c')) m68k_ss.add(when: 'CONFIG_M68K_VIRT', if_true: files('virt.c')) hw_arch += {'m68k': m68k_ss} diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index e0d4a94f9d..9f6f90d68b 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -24,6 +24,7 @@ #include "hw/block/fdc.h" #include "hw/qdev-properties.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "ui/console.h" #include "target/m68k/cpu.h" #include "migration/vmstate.h" @@ -61,6 +62,7 @@ typedef struct next_dma { } next_dma; typedef struct NextRtc { + int8_t phase; uint8_t ram[32]; uint8_t command; uint8_t value; @@ -72,6 +74,12 @@ typedef struct NextRtc { struct NeXTState { MachineState parent; + MemoryRegion rom; + MemoryRegion rom2; + MemoryRegion dmamem; + MemoryRegion bmapm1; + MemoryRegion bmapm2; + next_dma dma[10]; }; @@ -89,10 +97,15 @@ struct NeXTPC { uint32_t scr1; uint32_t scr2; - uint8_t scsi_csr_1; - uint8_t scsi_csr_2; + uint32_t old_scr2; uint32_t int_mask; uint32_t int_status; + uint32_t led; + uint8_t scsi_csr_1; + uint8_t scsi_csr_2; + + qemu_irq scsi_reset; + qemu_irq scsi_dma; NextRtc rtc; }; @@ -117,49 +130,46 @@ static const uint8_t rtc_ram2[32] = { #define SCR2_RTDATA 0x4 #define SCR2_TOBCD(x) (((x / 10) << 4) + (x % 10)) -static void nextscr2_write(NeXTPC *s, uint32_t val, int size) +static void next_scr2_led_update(NeXTPC *s) { - static int led; - static int phase; - static uint8_t old_scr2; - uint8_t scr2_2; - NextRtc *rtc = &s->rtc; - - if (size == 4) { - scr2_2 = (val >> 8) & 0xFF; - } else { - scr2_2 = val & 0xFF; - } - - if (val & 0x1) { + if (s->scr2 & 0x1) { DPRINTF("fault!\n"); - led++; - if (led == 10) { + s->led++; + if (s->led == 10) { DPRINTF("LED flashing, possible fault!\n"); - led = 0; + s->led = 0; } } +} + +static void next_scr2_rtc_update(NeXTPC *s) +{ + uint8_t old_scr2, scr2_2; + NextRtc *rtc = &s->rtc; + + old_scr2 = extract32(s->old_scr2, 8, 8); + scr2_2 = extract32(s->scr2, 8, 8); if (scr2_2 & 0x1) { - /* DPRINTF("RTC %x phase %i\n", scr2_2, phase); */ - if (phase == -1) { - phase = 0; + /* DPRINTF("RTC %x phase %i\n", scr2_2, rtc->phase); */ + if (rtc->phase == -1) { + rtc->phase = 0; } /* If we are in going down clock... do something */ if (((old_scr2 & SCR2_RTCLK) != (scr2_2 & SCR2_RTCLK)) && ((scr2_2 & SCR2_RTCLK) == 0)) { - if (phase < 8) { + if (rtc->phase < 8) { rtc->command = (rtc->command << 1) | ((scr2_2 & SCR2_RTDATA) ? 1 : 0); } - if (phase >= 8 && phase < 16) { + if (rtc->phase >= 8 && rtc->phase < 16) { rtc->value = (rtc->value << 1) | ((scr2_2 & SCR2_RTDATA) ? 1 : 0); /* if we read RAM register, output RT_DATA bit */ if (rtc->command <= 0x1F) { scr2_2 = scr2_2 & (~SCR2_RTDATA); - if (rtc->ram[rtc->command] & (0x80 >> (phase - 8))) { + if (rtc->ram[rtc->command] & (0x80 >> (rtc->phase - 8))) { scr2_2 |= SCR2_RTDATA; } @@ -170,7 +180,7 @@ static void nextscr2_write(NeXTPC *s, uint32_t val, int size) if (rtc->command == 0x30) { scr2_2 = scr2_2 & (~SCR2_RTDATA); /* for now status = 0x98 (new rtc + FTU) */ - if (rtc->status & (0x80 >> (phase - 8))) { + if (rtc->status & (0x80 >> (rtc->phase - 8))) { scr2_2 |= SCR2_RTDATA; } @@ -180,7 +190,7 @@ static void nextscr2_write(NeXTPC *s, uint32_t val, int size) /* read the status 0x31 */ if (rtc->command == 0x31) { scr2_2 = scr2_2 & (~SCR2_RTDATA); - if (rtc->control & (0x80 >> (phase - 8))) { + if (rtc->control & (0x80 >> (rtc->phase - 8))) { scr2_2 |= SCR2_RTDATA; } rtc->retval = (rtc->retval << 1) | @@ -216,7 +226,7 @@ static void nextscr2_write(NeXTPC *s, uint32_t val, int size) } - if (ret & (0x80 >> (phase - 8))) { + if (ret & (0x80 >> (rtc->phase - 8))) { scr2_2 |= SCR2_RTDATA; } rtc->retval = (rtc->retval << 1) | @@ -225,8 +235,8 @@ static void nextscr2_write(NeXTPC *s, uint32_t val, int size) } - phase++; - if (phase == 16) { + rtc->phase++; + if (rtc->phase == 16) { if (rtc->command >= 0x80 && rtc->command <= 0x9F) { rtc->ram[rtc->command - 0x80] = rtc->value; } @@ -242,207 +252,98 @@ static void nextscr2_write(NeXTPC *s, uint32_t val, int size) } } else { /* else end or abort */ - phase = -1; + rtc->phase = -1; rtc->command = 0; rtc->value = 0; } - s->scr2 = val & 0xFFFF00FF; - s->scr2 |= scr2_2 << 8; - old_scr2 = scr2_2; + + s->scr2 = deposit32(s->scr2, 8, 8, scr2_2); } -static uint32_t mmio_readb(NeXTPC *s, hwaddr addr) +static uint64_t next_mmio_read(void *opaque, hwaddr addr, unsigned size) { - switch (addr) { - case 0xc000: - return (s->scr1 >> 24) & 0xFF; - case 0xc001: - return (s->scr1 >> 16) & 0xFF; - case 0xc002: - return (s->scr1 >> 8) & 0xFF; - case 0xc003: - return (s->scr1 >> 0) & 0xFF; + NeXTPC *s = NEXT_PC(opaque); + uint64_t val; - case 0xd000: - return (s->scr2 >> 24) & 0xFF; - case 0xd001: - return (s->scr2 >> 16) & 0xFF; - case 0xd002: - return (s->scr2 >> 8) & 0xFF; - case 0xd003: - return (s->scr2 >> 0) & 0xFF; - case 0x14020: - DPRINTF("MMIO Read 0x4020\n"); - return 0x7f; - - default: - DPRINTF("MMIO Read B @ %"HWADDR_PRIx"\n", addr); - return 0x0; - } -} - -static uint32_t mmio_readw(NeXTPC *s, hwaddr addr) -{ - switch (addr) { - default: - DPRINTF("MMIO Read W @ %"HWADDR_PRIx"\n", addr); - return 0x0; - } -} - -static uint32_t mmio_readl(NeXTPC *s, hwaddr addr) -{ switch (addr) { case 0x7000: /* DPRINTF("Read INT status: %x\n", s->int_status); */ - return s->int_status; + val = s->int_status; + break; case 0x7800: DPRINTF("MMIO Read INT mask: %x\n", s->int_mask); - return s->int_mask; - - case 0xc000: - return s->scr1; - - case 0xd000: - return s->scr2; - - default: - DPRINTF("MMIO Read L @ %"HWADDR_PRIx"\n", addr); - return 0x0; - } -} - -static void mmio_writeb(NeXTPC *s, hwaddr addr, uint32_t val) -{ - switch (addr) { - case 0xd003: - nextscr2_write(s, val, 1); + val = s->int_mask; break; + + case 0xc000 ... 0xc003: + val = extract32(s->scr1, (4 - (addr - 0xc000) - size) << 3, + size << 3); + break; + + case 0xd000 ... 0xd003: + val = extract32(s->scr2, (4 - (addr - 0xd000) - size) << 3, + size << 3); + break; + + case 0x14020: + val = 0x7f; + break; + default: - DPRINTF("MMIO Write B @ %x with %x\n", (unsigned int)addr, val); + val = 0; + DPRINTF("MMIO Read @ 0x%"HWADDR_PRIx" size %d\n", addr, size); + break; } + return val; } -static void mmio_writew(NeXTPC *s, hwaddr addr, uint32_t val) +static void next_mmio_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) { - DPRINTF("MMIO Write W\n"); -} + NeXTPC *s = NEXT_PC(opaque); -static void mmio_writel(NeXTPC *s, hwaddr addr, uint32_t val) -{ switch (addr) { case 0x7000: - DPRINTF("INT Status old: %x new: %x\n", s->int_status, val); + DPRINTF("INT Status old: %x new: %x\n", s->int_status, + (unsigned int)val); s->int_status = val; break; + case 0x7800: - DPRINTF("INT Mask old: %x new: %x\n", s->int_mask, val); + DPRINTF("INT Mask old: %x new: %x\n", s->int_mask, (unsigned int)val); s->int_mask = val; break; - case 0xc000: - DPRINTF("SCR1 Write: %x\n", val); + + case 0xc000 ... 0xc003: + DPRINTF("SCR1 Write: %x\n", (unsigned int)val); + s->scr1 = deposit32(s->scr1, (4 - (addr - 0xc000) - size) << 3, + size << 3, val); break; - case 0xd000: - nextscr2_write(s, val, 4); + + case 0xd000 ... 0xd003: + s->scr2 = deposit32(s->scr2, (4 - (addr - 0xd000) - size) << 3, + size << 3, val); + next_scr2_led_update(s); + next_scr2_rtc_update(s); + s->old_scr2 = s->scr2; break; default: - DPRINTF("MMIO Write l @ %x with %x\n", (unsigned int)addr, val); + DPRINTF("MMIO Write @ 0x%"HWADDR_PRIx " with 0x%x size %u\n", addr, + (unsigned int)val, size); } } -static uint64_t mmio_readfn(void *opaque, hwaddr addr, unsigned size) -{ - NeXTPC *s = NEXT_PC(opaque); - - switch (size) { - case 1: - return mmio_readb(s, addr); - case 2: - return mmio_readw(s, addr); - case 4: - return mmio_readl(s, addr); - default: - g_assert_not_reached(); - } -} - -static void mmio_writefn(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - NeXTPC *s = NEXT_PC(opaque); - - switch (size) { - case 1: - mmio_writeb(s, addr, value); - break; - case 2: - mmio_writew(s, addr, value); - break; - case 4: - mmio_writel(s, addr, value); - break; - default: - g_assert_not_reached(); - } -} - -static const MemoryRegionOps mmio_ops = { - .read = mmio_readfn, - .write = mmio_writefn, +static const MemoryRegionOps next_mmio_ops = { + .read = next_mmio_read, + .write = next_mmio_write, .valid.min_access_size = 1, .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; -static uint32_t scr_readb(NeXTPC *s, hwaddr addr) -{ - switch (addr) { - case 0x14108: - DPRINTF("FD read @ %x\n", (unsigned int)addr); - return 0x40 | 0x04 | 0x2 | 0x1; - case 0x14020: - DPRINTF("SCSI 4020 STATUS READ %X\n", s->scsi_csr_1); - return s->scsi_csr_1; - - case 0x14021: - DPRINTF("SCSI 4021 STATUS READ %X\n", s->scsi_csr_2); - return 0x40; - - /* - * These 4 registers are the hardware timer, not sure which register - * is the latch instead of data, but no problems so far - */ - case 0x1a000: - return 0xff & (clock() >> 24); - case 0x1a001: - return 0xff & (clock() >> 16); - case 0x1a002: - return 0xff & (clock() >> 8); - case 0x1a003: - /* Hack: We need to have this change consistently to make it work */ - return 0xFF & clock(); - - default: - DPRINTF("BMAP Read B @ %x\n", (unsigned int)addr); - return 0; - } -} - -static uint32_t scr_readw(NeXTPC *s, hwaddr addr) -{ - DPRINTF("BMAP Read W @ %x\n", (unsigned int)addr); - return 0; -} - -static uint32_t scr_readl(NeXTPC *s, hwaddr addr) -{ - DPRINTF("BMAP Read L @ %x\n", (unsigned int)addr); - return 0; -} - #define SCSICSR_ENABLE 0x01 #define SCSICSR_RESET 0x02 /* reset scsi dma */ #define SCSICSR_FIFOFL 0x04 @@ -450,25 +351,73 @@ static uint32_t scr_readl(NeXTPC *s, hwaddr addr) #define SCSICSR_CPUDMA 0x10 /* if set, dma enabled */ #define SCSICSR_INTMASK 0x20 /* if set, interrupt enabled */ -static void scr_writeb(NeXTPC *s, hwaddr addr, uint32_t value) +static uint64_t next_scr_readfn(void *opaque, hwaddr addr, unsigned size) { + NeXTPC *s = NEXT_PC(opaque); + uint64_t val; + + switch (addr) { + case 0x14108: + DPRINTF("FD read @ %x\n", (unsigned int)addr); + val = 0x40 | 0x04 | 0x2 | 0x1; + break; + + case 0x14020: + DPRINTF("SCSI 4020 STATUS READ %X\n", s->scsi_csr_1); + val = s->scsi_csr_1; + break; + + case 0x14021: + DPRINTF("SCSI 4021 STATUS READ %X\n", s->scsi_csr_2); + val = 0x40; + break; + + /* + * These 4 registers are the hardware timer, not sure which register + * is the latch instead of data, but no problems so far. + * + * Hack: We need to have the LSB change consistently to make it work + */ + case 0x1a000 ... 0x1a003: + val = extract32(clock(), (4 - (addr - 0x1a000) - size) << 3, + size << 3); + break; + + /* For now return dummy byte to allow the Ethernet test to timeout */ + case 0x6000: + val = 0xff; + break; + + default: + DPRINTF("BMAP Read @ 0x%x size %u\n", (unsigned int)addr, size); + val = 0; + break; + } + + return val; +} + +static void next_scr_writefn(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + NeXTPC *s = NEXT_PC(opaque); + switch (addr) { case 0x14108: DPRINTF("FDCSR Write: %x\n", value); - - if (value == 0x0) { + if (val == 0x0) { /* qemu_irq_raise(s->fd_irq[0]); */ } break; + case 0x14020: /* SCSI Control Register */ - if (value & SCSICSR_FIFOFL) { + if (val & SCSICSR_FIFOFL) { DPRINTF("SCSICSR FIFO Flush\n"); /* will have to add another irq to the esp if this is needed */ /* esp_puflush_fifo(esp_g); */ - /* qemu_irq_pulse(s->scsi_dma); */ } - if (value & SCSICSR_ENABLE) { + if (val & SCSICSR_ENABLE) { DPRINTF("SCSICSR Enable\n"); /* * qemu_irq_raise(s->scsi_dma); @@ -482,29 +431,30 @@ static void scr_writeb(NeXTPC *s, hwaddr addr, uint32_t value) * s->scsi_csr_1 &= ~SCSICSR_ENABLE; */ - if (value & SCSICSR_RESET) { + if (val & SCSICSR_RESET) { DPRINTF("SCSICSR Reset\n"); /* I think this should set DMADIR. CPUDMA and INTMASK to 0 */ - /* qemu_irq_raise(s->scsi_reset); */ - /* s->scsi_csr_1 &= ~(SCSICSR_INTMASK |0x80|0x1); */ - + qemu_irq_raise(s->scsi_reset); + s->scsi_csr_1 &= ~(SCSICSR_INTMASK | 0x80 | 0x1); + qemu_irq_lower(s->scsi_reset); } - if (value & SCSICSR_DMADIR) { + if (val & SCSICSR_DMADIR) { DPRINTF("SCSICSR DMAdir\n"); } - if (value & SCSICSR_CPUDMA) { + if (val & SCSICSR_CPUDMA) { DPRINTF("SCSICSR CPUDMA\n"); /* qemu_irq_raise(s->scsi_dma); */ - s->int_status |= 0x4000000; } else { + /* fprintf(stderr,"SCSICSR CPUDMA disabled\n"); */ s->int_status &= ~(0x4000000); + /* qemu_irq_lower(s->scsi_dma); */ } - if (value & SCSICSR_INTMASK) { + if (val & SCSICSR_INTMASK) { DPRINTF("SCSICSR INTMASK\n"); /* * int_mask &= ~0x1000; - * s->scsi_csr_1 |= value; + * s->scsi_csr_1 |= val; * s->scsi_csr_1 &= ~SCSICSR_INTMASK; * if (s->scsi_queued) { * s->scsi_queued = 0; @@ -514,72 +464,28 @@ static void scr_writeb(NeXTPC *s, hwaddr addr, uint32_t value) } else { /* int_mask |= 0x1000; */ } - if (value & 0x80) { + if (val & 0x80) { /* int_mask |= 0x1000; */ /* s->scsi_csr_1 |= 0x80; */ } - DPRINTF("SCSICSR Write: %x\n", value); - /* s->scsi_csr_1 = value; */ - return; + DPRINTF("SCSICSR Write: %x\n", val); + /* s->scsi_csr_1 = val; */ + break; + /* Hardware timer latch - not implemented yet */ case 0x1a000: default: - DPRINTF("BMAP Write B @ %x with %x\n", (unsigned int)addr, value); + DPRINTF("BMAP Write @ 0x%x with 0x%x size %u\n", (unsigned int)addr, + val, size); } } -static void scr_writew(NeXTPC *s, hwaddr addr, uint32_t value) -{ - DPRINTF("BMAP Write W @ %x with %x\n", (unsigned int)addr, value); -} - -static void scr_writel(NeXTPC *s, hwaddr addr, uint32_t value) -{ - DPRINTF("BMAP Write L @ %x with %x\n", (unsigned int)addr, value); -} - -static uint64_t scr_readfn(void *opaque, hwaddr addr, unsigned size) -{ - NeXTPC *s = NEXT_PC(opaque); - - switch (size) { - case 1: - return scr_readb(s, addr); - case 2: - return scr_readw(s, addr); - case 4: - return scr_readl(s, addr); - default: - g_assert_not_reached(); - } -} - -static void scr_writefn(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - NeXTPC *s = NEXT_PC(opaque); - - switch (size) { - case 1: - scr_writeb(s, addr, value); - break; - case 2: - scr_writew(s, addr, value); - break; - case 4: - scr_writel(s, addr, value); - break; - default: - g_assert_not_reached(); - } -} - -static const MemoryRegionOps scr_ops = { - .read = scr_readfn, - .write = scr_writefn, +static const MemoryRegionOps next_scr_ops = { + .read = next_scr_readfn, + .write = next_scr_writefn, .valid.min_access_size = 1, .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; #define NEXTDMA_SCSI(x) (0x10 + x) @@ -594,59 +500,63 @@ static const MemoryRegionOps scr_ops = { #define NEXTDMA_NEXT_INIT 0x4200 #define NEXTDMA_SIZE 0x4204 -static void dma_writel(void *opaque, hwaddr addr, uint64_t value, - unsigned int size) +static void next_dma_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) { NeXTState *next_state = NEXT_MACHINE(opaque); switch (addr) { case NEXTDMA_ENRX(NEXTDMA_CSR): - if (value & DMA_DEV2M) { + if (val & DMA_DEV2M) { next_state->dma[NEXTDMA_ENRX].csr |= DMA_DEV2M; } - if (value & DMA_SETENABLE) { + if (val & DMA_SETENABLE) { /* DPRINTF("SCSI DMA ENABLE\n"); */ next_state->dma[NEXTDMA_ENRX].csr |= DMA_ENABLE; } - if (value & DMA_SETSUPDATE) { + if (val & DMA_SETSUPDATE) { next_state->dma[NEXTDMA_ENRX].csr |= DMA_SUPDATE; } - if (value & DMA_CLRCOMPLETE) { + if (val & DMA_CLRCOMPLETE) { next_state->dma[NEXTDMA_ENRX].csr &= ~DMA_COMPLETE; } - if (value & DMA_RESET) { + if (val & DMA_RESET) { next_state->dma[NEXTDMA_ENRX].csr &= ~(DMA_COMPLETE | DMA_SUPDATE | DMA_ENABLE | DMA_DEV2M); } /* DPRINTF("RXCSR \tWrite: %x\n",value); */ break; + case NEXTDMA_ENRX(NEXTDMA_NEXT_INIT): - next_state->dma[NEXTDMA_ENRX].next_initbuf = value; + next_state->dma[NEXTDMA_ENRX].next_initbuf = val; break; + case NEXTDMA_ENRX(NEXTDMA_NEXT): - next_state->dma[NEXTDMA_ENRX].next = value; + next_state->dma[NEXTDMA_ENRX].next = val; break; + case NEXTDMA_ENRX(NEXTDMA_LIMIT): - next_state->dma[NEXTDMA_ENRX].limit = value; + next_state->dma[NEXTDMA_ENRX].limit = val; break; + case NEXTDMA_SCSI(NEXTDMA_CSR): - if (value & DMA_DEV2M) { + if (val & DMA_DEV2M) { next_state->dma[NEXTDMA_SCSI].csr |= DMA_DEV2M; } - if (value & DMA_SETENABLE) { + if (val & DMA_SETENABLE) { /* DPRINTF("SCSI DMA ENABLE\n"); */ next_state->dma[NEXTDMA_SCSI].csr |= DMA_ENABLE; } - if (value & DMA_SETSUPDATE) { + if (val & DMA_SETSUPDATE) { next_state->dma[NEXTDMA_SCSI].csr |= DMA_SUPDATE; } - if (value & DMA_CLRCOMPLETE) { + if (val & DMA_CLRCOMPLETE) { next_state->dma[NEXTDMA_SCSI].csr &= ~DMA_COMPLETE; } - if (value & DMA_RESET) { + if (val & DMA_RESET) { next_state->dma[NEXTDMA_SCSI].csr &= ~(DMA_COMPLETE | DMA_SUPDATE | DMA_ENABLE | DMA_DEV2M); /* DPRINTF("SCSI DMA RESET\n"); */ @@ -655,23 +565,23 @@ static void dma_writel(void *opaque, hwaddr addr, uint64_t value, break; case NEXTDMA_SCSI(NEXTDMA_NEXT): - next_state->dma[NEXTDMA_SCSI].next = value; + next_state->dma[NEXTDMA_SCSI].next = val; break; case NEXTDMA_SCSI(NEXTDMA_LIMIT): - next_state->dma[NEXTDMA_SCSI].limit = value; + next_state->dma[NEXTDMA_SCSI].limit = val; break; case NEXTDMA_SCSI(NEXTDMA_START): - next_state->dma[NEXTDMA_SCSI].start = value; + next_state->dma[NEXTDMA_SCSI].start = val; break; case NEXTDMA_SCSI(NEXTDMA_STOP): - next_state->dma[NEXTDMA_SCSI].stop = value; + next_state->dma[NEXTDMA_SCSI].stop = val; break; case NEXTDMA_SCSI(NEXTDMA_NEXT_INIT): - next_state->dma[NEXTDMA_SCSI].next_initbuf = value; + next_state->dma[NEXTDMA_SCSI].next_initbuf = val; break; default: @@ -679,52 +589,73 @@ static void dma_writel(void *opaque, hwaddr addr, uint64_t value, } } -static uint64_t dma_readl(void *opaque, hwaddr addr, unsigned int size) +static uint64_t next_dma_read(void *opaque, hwaddr addr, unsigned int size) { NeXTState *next_state = NEXT_MACHINE(opaque); + uint64_t val; switch (addr) { case NEXTDMA_SCSI(NEXTDMA_CSR): DPRINTF("SCSI DMA CSR READ\n"); - return next_state->dma[NEXTDMA_SCSI].csr; + val = next_state->dma[NEXTDMA_SCSI].csr; + break; + case NEXTDMA_ENRX(NEXTDMA_CSR): - return next_state->dma[NEXTDMA_ENRX].csr; + val = next_state->dma[NEXTDMA_ENRX].csr; + break; + case NEXTDMA_ENRX(NEXTDMA_NEXT_INIT): - return next_state->dma[NEXTDMA_ENRX].next_initbuf; + val = next_state->dma[NEXTDMA_ENRX].next_initbuf; + break; + case NEXTDMA_ENRX(NEXTDMA_NEXT): - return next_state->dma[NEXTDMA_ENRX].next; + val = next_state->dma[NEXTDMA_ENRX].next; + break; + case NEXTDMA_ENRX(NEXTDMA_LIMIT): - return next_state->dma[NEXTDMA_ENRX].limit; + val = next_state->dma[NEXTDMA_ENRX].limit; + break; case NEXTDMA_SCSI(NEXTDMA_NEXT): - return next_state->dma[NEXTDMA_SCSI].next; + val = next_state->dma[NEXTDMA_SCSI].next; + break; + case NEXTDMA_SCSI(NEXTDMA_NEXT_INIT): - return next_state->dma[NEXTDMA_SCSI].next_initbuf; + val = next_state->dma[NEXTDMA_SCSI].next_initbuf; + break; + case NEXTDMA_SCSI(NEXTDMA_LIMIT): - return next_state->dma[NEXTDMA_SCSI].limit; + val = next_state->dma[NEXTDMA_SCSI].limit; + break; + case NEXTDMA_SCSI(NEXTDMA_START): - return next_state->dma[NEXTDMA_SCSI].start; + val = next_state->dma[NEXTDMA_SCSI].start; + break; + case NEXTDMA_SCSI(NEXTDMA_STOP): - return next_state->dma[NEXTDMA_SCSI].stop; + val = next_state->dma[NEXTDMA_SCSI].stop; + break; default: DPRINTF("DMA read @ %x\n", (unsigned int)addr); - return 0; + val = 0; } /* * once the csr's are done, subtract 0x3FEC from the addr, and that will * normalize the upper registers */ + + return val; } -static const MemoryRegionOps dma_ops = { - .read = dma_readl, - .write = dma_writel, +static const MemoryRegionOps next_dma_ops = { + .read = next_dma_read, + .write = next_dma_write, .impl.min_access_size = 4, .valid.min_access_size = 4, .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; static void next_irq(void *opaque, int number, int level) @@ -733,7 +664,7 @@ static void next_irq(void *opaque, int number, int level) M68kCPU *cpu = s->cpu; int shift = 0; - /* first switch sets interupt status */ + /* first switch sets interrupt status */ /* DPRINTF("IRQ %i\n",number); */ switch (number) { /* level 3 - floppy, kbd/mouse, power, ether rx/tx, scsi, clock */ @@ -827,6 +758,103 @@ static void next_irq(void *opaque, int number, int level) } } +static void nextdma_write(void *opaque, uint8_t *buf, int size, int type) +{ + uint32_t base_addr; + int irq = 0; + uint8_t align = 16; + NeXTState *next_state = NEXT_MACHINE(qdev_get_machine()); + + if (type == NEXTDMA_ENRX || type == NEXTDMA_ENTX) { + align = 32; + } + /* Most DMA is supposedly 16 byte aligned */ + if ((size % align) != 0) { + size -= size % align; + size += align; + } + + /* + * prom sets the dma start using initbuf while the bootloader uses next + * so we check to see if initbuf is 0 + */ + if (next_state->dma[type].next_initbuf == 0) { + base_addr = next_state->dma[type].next; + } else { + base_addr = next_state->dma[type].next_initbuf; + } + + cpu_physical_memory_write(base_addr, buf, size); + + next_state->dma[type].next_initbuf = 0; + + /* saved limit is checked to calculate packet size by both, rom and netbsd */ + next_state->dma[type].saved_limit = (next_state->dma[type].next + size); + next_state->dma[type].saved_next = (next_state->dma[type].next); + + /* + * 32 bytes under savedbase seems to be some kind of register + * of which the purpose is unknown as of yet + */ + /* stl_phys(s->rx_dma.base-32,0xFFFFFFFF); */ + + if (!(next_state->dma[type].csr & DMA_SUPDATE)) { + next_state->dma[type].next = next_state->dma[type].start; + next_state->dma[type].limit = next_state->dma[type].stop; + } + + /* Set dma registers and raise an irq */ + next_state->dma[type].csr |= DMA_COMPLETE; /* DON'T CHANGE THIS! */ + + switch (type) { + case NEXTDMA_SCSI: + irq = NEXT_SCSI_DMA_I; + break; + } + + next_irq(opaque, irq, 1); + next_irq(opaque, irq, 0); +} + +static void nextscsi_read(void *opaque, uint8_t *buf, int len) +{ + DPRINTF("SCSI READ: %x\n", len); + abort(); +} + +static void nextscsi_write(void *opaque, uint8_t *buf, int size) +{ + DPRINTF("SCSI WRITE: %i\n", size); + nextdma_write(opaque, buf, size, NEXTDMA_SCSI); +} + +static void next_scsi_init(DeviceState *pcdev, M68kCPU *cpu) +{ + struct NeXTPC *next_pc = NEXT_PC(pcdev); + DeviceState *dev; + SysBusDevice *sysbusdev; + SysBusESPState *sysbus_esp; + ESPState *esp; + + dev = qdev_new(TYPE_SYSBUS_ESP); + sysbus_esp = SYSBUS_ESP(dev); + esp = &sysbus_esp->esp; + esp->dma_memory_read = nextscsi_read; + esp->dma_memory_write = nextscsi_write; + esp->dma_opaque = pcdev; + sysbus_esp->it_shift = 0; + esp->dma_enabled = 1; + sysbusdev = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sysbusdev, &error_fatal); + sysbus_connect_irq(sysbusdev, 0, qdev_get_gpio_in(pcdev, NEXT_SCSI_I)); + sysbus_mmio_map(sysbusdev, 0, 0x2114000); + + next_pc->scsi_reset = qdev_get_gpio_in(dev, 0); + next_pc->scsi_dma = qdev_get_gpio_in(dev, 1); + + scsi_bus_legacy_handle_cmdline(&esp->bus); +} + static void next_escc_init(DeviceState *pcdev) { DeviceState *dev; @@ -857,6 +885,7 @@ static void next_pc_reset(DeviceState *dev) /* 0x0000XX00 << vital bits */ s->scr1 = 0x00011102; s->scr2 = 0x00ff0c80; + s->old_scr2 = s->scr2; s->rtc.status = 0x90; @@ -871,9 +900,9 @@ static void next_pc_realize(DeviceState *dev, Error **errp) qdev_init_gpio_in(dev, next_irq, NEXT_NUM_IRQS); - memory_region_init_io(&s->mmiomem, OBJECT(s), &mmio_ops, s, - "next.mmio", 0xD0000); - memory_region_init_io(&s->scrmem, OBJECT(s), &scr_ops, s, + memory_region_init_io(&s->mmiomem, OBJECT(s), &next_mmio_ops, s, + "next.mmio", 0xd0000); + memory_region_init_io(&s->scrmem, OBJECT(s), &next_scr_ops, s, "next.scr", 0x20000); sysbus_init_mmio(sbd, &s->mmiomem); sysbus_init_mmio(sbd, &s->scrmem); @@ -892,9 +921,10 @@ static Property next_pc_properties[] = { static const VMStateDescription next_rtc_vmstate = { .name = "next-rtc", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { + VMSTATE_INT8(phase, NextRtc), VMSTATE_UINT8_ARRAY(ram, NextRtc, 32), VMSTATE_UINT8(command, NextRtc), VMSTATE_UINT8(value, NextRtc), @@ -907,13 +937,15 @@ static const VMStateDescription next_rtc_vmstate = { static const VMStateDescription next_pc_vmstate = { .name = "next-pc", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { VMSTATE_UINT32(scr1, NeXTPC), VMSTATE_UINT32(scr2, NeXTPC), + VMSTATE_UINT32(old_scr2, NeXTPC), VMSTATE_UINT32(int_mask, NeXTPC), VMSTATE_UINT32(int_status, NeXTPC), + VMSTATE_UINT32(led, NeXTPC), VMSTATE_UINT8(scsi_csr_1, NeXTPC), VMSTATE_UINT8(scsi_csr_2, NeXTPC), VMSTATE_STRUCT(rtc, NeXTPC, 0, next_rtc_vmstate, NextRtc), @@ -941,15 +973,11 @@ static const TypeInfo next_pc_info = { static void next_cube_init(MachineState *machine) { + NeXTState *m = NEXT_MACHINE(machine); M68kCPU *cpu; CPUM68KState *env; - MemoryRegion *rom = g_new(MemoryRegion, 1); - MemoryRegion *dmamem = g_new(MemoryRegion, 1); - MemoryRegion *bmapm1 = g_new(MemoryRegion, 1); - MemoryRegion *bmapm2 = g_new(MemoryRegion, 1); MemoryRegion *sysmem = get_system_memory(); const char *bios_name = machine->firmware ?: ROM_FILE; - DeviceState *dev; DeviceState *pcdev; /* Initialize the cpu core */ @@ -973,9 +1001,7 @@ static void next_cube_init(MachineState *machine) memory_region_add_subregion(sysmem, 0x04000000, machine->ram); /* Framebuffer */ - dev = qdev_new(TYPE_NEXTFB); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0B000000); + sysbus_create_simple(TYPE_NEXTFB, 0x0B000000, NULL); /* MMIO */ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 0, 0x02000000); @@ -984,22 +1010,23 @@ static void next_cube_init(MachineState *machine) sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 1, 0x02100000); /* BMAP memory */ - memory_region_init_ram_flags_nomigrate(bmapm1, NULL, "next.bmapmem", 64, - RAM_SHARED, &error_fatal); - memory_region_add_subregion(sysmem, 0x020c0000, bmapm1); + memory_region_init_ram_flags_nomigrate(&m->bmapm1, NULL, "next.bmapmem", + 64, RAM_SHARED, &error_fatal); + memory_region_add_subregion(sysmem, 0x020c0000, &m->bmapm1); /* The Rev_2.5_v66.bin firmware accesses it at 0x820c0020, too */ - memory_region_init_alias(bmapm2, NULL, "next.bmapmem2", bmapm1, 0x0, 64); - memory_region_add_subregion(sysmem, 0x820c0000, bmapm2); + memory_region_init_alias(&m->bmapm2, NULL, "next.bmapmem2", &m->bmapm1, + 0x0, 64); + memory_region_add_subregion(sysmem, 0x820c0000, &m->bmapm2); /* KBD */ - dev = qdev_new(TYPE_NEXTKBD); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0200e000); + sysbus_create_simple(TYPE_NEXTKBD, 0x0200e000, NULL); /* Load ROM here */ - /* still not sure if the rom should also be mapped at 0x0*/ - memory_region_init_rom(rom, NULL, "next.rom", 0x20000, &error_fatal); - memory_region_add_subregion(sysmem, 0x01000000, rom); + memory_region_init_rom(&m->rom, NULL, "next.rom", 0x20000, &error_fatal); + memory_region_add_subregion(sysmem, 0x01000000, &m->rom); + memory_region_init_alias(&m->rom2, NULL, "next.rom2", &m->rom, 0x0, + 0x20000); + memory_region_add_subregion(sysmem, 0x0, &m->rom2); if (load_image_targphys(bios_name, 0x01000000, 0x20000) < 8) { if (!qtest_enabled()) { error_report("Failed to load firmware '%s'.", bios_name); @@ -1023,10 +1050,12 @@ static void next_cube_init(MachineState *machine) /* TODO: */ /* Network */ /* SCSI */ + next_scsi_init(pcdev, cpu); /* DMA */ - memory_region_init_io(dmamem, NULL, &dma_ops, machine, "next.dma", 0x5000); - memory_region_add_subregion(sysmem, 0x02000000, dmamem); + memory_region_init_io(&m->dmamem, NULL, &next_dma_ops, machine, + "next.dma", 0x5000); + memory_region_add_subregion(sysmem, 0x02000000, &m->dmamem); } static void next_machine_class_init(ObjectClass *oc, void *data) @@ -1035,6 +1064,7 @@ static void next_machine_class_init(ObjectClass *oc, void *data) mc->desc = "NeXT Cube"; mc->init = next_cube_init; + mc->block_default_type = IF_SCSI; 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/next-kbd.c b/hw/m68k/next-kbd.c index 0544160e91..0c348c18cf 100644 --- a/hw/m68k/next-kbd.c +++ b/hw/m68k/next-kbd.c @@ -37,7 +37,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(NextKBDState, NEXTKBD) -/* following defintions from next68k netbsd */ +/* following definitions from next68k netbsd */ #define CSR_INT 0x00800000 #define CSR_DATA 0x00400000 diff --git a/hw/m68k/q800-glue.c b/hw/m68k/q800-glue.c new file mode 100644 index 0000000000..b5a7713863 --- /dev/null +++ b/hw/m68k/q800-glue.c @@ -0,0 +1,259 @@ +/* + * QEMU q800 logic GLUE (General Logic Unit) + * + * 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 "cpu.h" +#include "hw/m68k/q800-glue.h" +#include "hw/boards.h" +#include "hw/irq.h" +#include "hw/nmi.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" + +/* + * The GLUE (General Logic Unit) is an Apple custom integrated circuit chip + * that performs a variety of functions (RAM management, clock generation, ...). + * The GLUE chip receives interrupt requests from various devices, + * assign priority to each, and asserts one or more interrupt line to the + * CPU. + */ + +/* + * The GLUE logic on the Quadra 800 supports 2 different IRQ routing modes + * controlled from the VIA1 auxmode GPIO (port B bit 6) which are documented + * in NetBSD as follows: + * + * A/UX mode (Linux, NetBSD, auxmode GPIO low) + * + * Level 0: Spurious: ignored + * Level 1: Software + * Level 2: VIA2 (except ethernet, sound) + * Level 3: Ethernet + * Level 4: Serial (SCC) + * Level 5: Sound + * Level 6: VIA1 + * Level 7: NMIs: parity errors, RESET button, YANCC error + * + * Classic mode (default: used by MacOS, A/UX 3.0.1, auxmode GPIO high) + * + * Level 0: Spurious: ignored + * Level 1: VIA1 (clock, ADB) + * Level 2: VIA2 (NuBus, SCSI) + * Level 3: + * Level 4: Serial (SCC) + * Level 5: + * Level 6: + * Level 7: Non-maskable: parity errors, RESET button + * + * Note that despite references to A/UX mode in Linux and NetBSD, at least + * A/UX 3.0.1 still uses Classic mode. + */ + +static void GLUE_set_irq(void *opaque, int irq, int level) +{ + GLUEState *s = opaque; + int i; + + if (s->auxmode) { + /* Classic mode */ + switch (irq) { + case GLUE_IRQ_IN_VIA1: + irq = 0; + break; + + case GLUE_IRQ_IN_VIA2: + irq = 1; + break; + + case GLUE_IRQ_IN_SONIC: + /* Route to VIA2 instead */ + qemu_set_irq(s->irqs[GLUE_IRQ_NUBUS_9], level); + return; + + case GLUE_IRQ_IN_ESCC: + irq = 3; + break; + + case GLUE_IRQ_IN_NMI: + irq = 6; + break; + + case GLUE_IRQ_IN_ASC: + /* Route to VIA2 instead, negative edge-triggered */ + qemu_set_irq(s->irqs[GLUE_IRQ_ASC], !level); + return; + + default: + g_assert_not_reached(); + } + } else { + /* A/UX mode */ + switch (irq) { + case GLUE_IRQ_IN_VIA1: + irq = 5; + break; + + case GLUE_IRQ_IN_VIA2: + irq = 1; + break; + + case GLUE_IRQ_IN_SONIC: + irq = 2; + break; + + case GLUE_IRQ_IN_ESCC: + irq = 3; + break; + + case GLUE_IRQ_IN_NMI: + irq = 6; + break; + + case GLUE_IRQ_IN_ASC: + irq = 4; + break; + + default: + g_assert_not_reached(); + } + } + + if (level) { + s->ipr |= 1 << irq; + } else { + s->ipr &= ~(1 << irq); + } + + for (i = 7; i >= 0; i--) { + if ((s->ipr >> i) & 1) { + m68k_set_irq_level(s->cpu, i + 1, i + 25); + return; + } + } + m68k_set_irq_level(s->cpu, 0, 0); +} + +static void glue_auxmode_set_irq(void *opaque, int irq, int level) +{ + GLUEState *s = GLUE(opaque); + + s->auxmode = level; +} + +static void glue_nmi(NMIState *n, int cpu_index, Error **errp) +{ + GLUEState *s = GLUE(n); + + /* Hold NMI active for 100ms */ + GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 1); + timer_mod(s->nmi_release, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 100); +} + +static void glue_nmi_release(void *opaque) +{ + GLUEState *s = GLUE(opaque); + + GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 0); +} + +static void glue_reset_hold(Object *obj) +{ + GLUEState *s = GLUE(obj); + + s->ipr = 0; + s->auxmode = 0; + + timer_del(s->nmi_release); +} + +static const VMStateDescription vmstate_glue = { + .name = "q800-glue", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT8(ipr, GLUEState), + VMSTATE_UINT8(auxmode, GLUEState), + VMSTATE_TIMER_PTR(nmi_release, GLUEState), + VMSTATE_END_OF_LIST(), + }, +}; + +/* + * If the m68k CPU implemented its inbound irq lines as GPIO lines + * rather than via the m68k_set_irq_level() function we would not need + * this cpu link property and could instead provide outbound IRQ lines + * that the board could wire up to the CPU. + */ +static Property glue_properties[] = { + DEFINE_PROP_LINK("cpu", GLUEState, cpu, TYPE_M68K_CPU, M68kCPU *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void glue_finalize(Object *obj) +{ + GLUEState *s = GLUE(obj); + + timer_free(s->nmi_release); +} + +static void glue_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + GLUEState *s = GLUE(dev); + + qdev_init_gpio_in(dev, GLUE_set_irq, 8); + qdev_init_gpio_in_named(dev, glue_auxmode_set_irq, "auxmode", 1); + + qdev_init_gpio_out(dev, s->irqs, 2); + + /* NMI release timer */ + s->nmi_release = timer_new_ms(QEMU_CLOCK_VIRTUAL, glue_nmi_release, s); +} + +static void glue_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + NMIClass *nc = NMI_CLASS(klass); + + dc->vmsd = &vmstate_glue; + device_class_set_props(dc, glue_properties); + rc->phases.hold = glue_reset_hold; + nc->nmi_monitor_handler = glue_nmi; +} + +static const TypeInfo glue_info_types[] = { + { + .name = TYPE_GLUE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(GLUEState), + .instance_init = glue_init, + .instance_finalize = glue_finalize, + .class_init = glue_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NMI }, + { } + }, + }, +}; + +DEFINE_TYPES(glue_info_types) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 099a758c6f..fa7683bf76 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -23,11 +23,11 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/datadir.h" +#include "qemu/guest-random.h" #include "sysemu/sysemu.h" #include "cpu.h" #include "hw/boards.h" #include "hw/or-irq.h" -#include "hw/nmi.h" #include "elf.h" #include "hw/loader.h" #include "ui/console.h" @@ -37,13 +37,20 @@ #include "standard-headers/asm-m68k/bootinfo.h" #include "standard-headers/asm-m68k/bootinfo-mac.h" #include "bootinfo.h" +#include "hw/m68k/q800.h" +#include "hw/m68k/q800-glue.h" #include "hw/misc/mac_via.h" +#include "hw/misc/djmemc.h" +#include "hw/misc/iosb.h" #include "hw/input/adb.h" +#include "hw/audio/asc.h" #include "hw/nubus/mac-nubus-bridge.h" #include "hw/display/macfb.h" #include "hw/block/swim.h" #include "net/net.h" +#include "net/util.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "sysemu/qtest.h" #include "sysemu/runstate.h" #include "sysemu/reset.h" @@ -56,15 +63,18 @@ #define IO_BASE 0x50000000 #define IO_SLICE 0x00040000 +#define IO_SLICE_MASK (IO_SLICE - 1) #define IO_SIZE 0x04000000 #define VIA_BASE (IO_BASE + 0x00000) #define SONIC_PROM_BASE (IO_BASE + 0x08000) #define SONIC_BASE (IO_BASE + 0x0a000) #define SCC_BASE (IO_BASE + 0x0c020) +#define DJMEMC_BASE (IO_BASE + 0x0e000) #define ESP_BASE (IO_BASE + 0x10000) #define ESP_PDMA (IO_BASE + 0x10100) #define ASC_BASE (IO_BASE + 0x14000) +#define IOSB_BASE (IO_BASE + 0x18000) #define SWIM_BASE (IO_BASE + 0x1E000) #define SONIC_PROM_SIZE 0x1000 @@ -78,6 +88,9 @@ #define MAC_CLOCK 3686418 +/* Size of whole RAM area */ +#define RAM_SIZE 0x40000000 + /* * Slot 0x9 is reserved for use by the in-built framebuffer whilst only * slots 0xc, 0xd and 0xe physically exist on the Quadra 800 @@ -85,240 +98,9 @@ #define Q800_NUBUS_SLOTS_AVAILABLE (BIT(0x9) | BIT(0xc) | BIT(0xd) | \ BIT(0xe)) -/* - * The GLUE (General Logic Unit) is an Apple custom integrated circuit chip - * that performs a variety of functions (RAM management, clock generation, ...). - * The GLUE chip receives interrupt requests from various devices, - * assign priority to each, and asserts one or more interrupt line to the - * CPU. - */ +/* Quadra 800 machine ID */ +#define Q800_MACHINE_ID 0xa55a2bad -#define TYPE_GLUE "q800-glue" -OBJECT_DECLARE_SIMPLE_TYPE(GLUEState, GLUE) - -struct GLUEState { - SysBusDevice parent_obj; - M68kCPU *cpu; - uint8_t ipr; - uint8_t auxmode; - qemu_irq irqs[1]; - QEMUTimer *nmi_release; -}; - -#define GLUE_IRQ_IN_VIA1 0 -#define GLUE_IRQ_IN_VIA2 1 -#define GLUE_IRQ_IN_SONIC 2 -#define GLUE_IRQ_IN_ESCC 3 -#define GLUE_IRQ_IN_NMI 4 - -#define GLUE_IRQ_NUBUS_9 0 - -/* - * The GLUE logic on the Quadra 800 supports 2 different IRQ routing modes - * controlled from the VIA1 auxmode GPIO (port B bit 6) which are documented - * in NetBSD as follows: - * - * A/UX mode (Linux, NetBSD, auxmode GPIO low) - * - * Level 0: Spurious: ignored - * Level 1: Software - * Level 2: VIA2 (except ethernet, sound) - * Level 3: Ethernet - * Level 4: Serial (SCC) - * Level 5: Sound - * Level 6: VIA1 - * Level 7: NMIs: parity errors, RESET button, YANCC error - * - * Classic mode (default: used by MacOS, A/UX 3.0.1, auxmode GPIO high) - * - * Level 0: Spurious: ignored - * Level 1: VIA1 (clock, ADB) - * Level 2: VIA2 (NuBus, SCSI) - * Level 3: - * Level 4: Serial (SCC) - * Level 5: - * Level 6: - * Level 7: Non-maskable: parity errors, RESET button - * - * Note that despite references to A/UX mode in Linux and NetBSD, at least - * A/UX 3.0.1 still uses Classic mode. - */ - -static void GLUE_set_irq(void *opaque, int irq, int level) -{ - GLUEState *s = opaque; - int i; - - if (s->auxmode) { - /* Classic mode */ - switch (irq) { - case GLUE_IRQ_IN_VIA1: - irq = 0; - break; - - case GLUE_IRQ_IN_VIA2: - irq = 1; - break; - - case GLUE_IRQ_IN_SONIC: - /* Route to VIA2 instead */ - qemu_set_irq(s->irqs[GLUE_IRQ_NUBUS_9], level); - return; - - case GLUE_IRQ_IN_ESCC: - irq = 3; - break; - - case GLUE_IRQ_IN_NMI: - irq = 6; - break; - - default: - g_assert_not_reached(); - } - } else { - /* A/UX mode */ - switch (irq) { - case GLUE_IRQ_IN_VIA1: - irq = 5; - break; - - case GLUE_IRQ_IN_VIA2: - irq = 1; - break; - - case GLUE_IRQ_IN_SONIC: - irq = 2; - break; - - case GLUE_IRQ_IN_ESCC: - irq = 3; - break; - - case GLUE_IRQ_IN_NMI: - irq = 6; - break; - - default: - g_assert_not_reached(); - } - } - - if (level) { - s->ipr |= 1 << irq; - } else { - s->ipr &= ~(1 << irq); - } - - for (i = 7; i >= 0; i--) { - if ((s->ipr >> i) & 1) { - m68k_set_irq_level(s->cpu, i + 1, i + 25); - return; - } - } - m68k_set_irq_level(s->cpu, 0, 0); -} - -static void glue_auxmode_set_irq(void *opaque, int irq, int level) -{ - GLUEState *s = GLUE(opaque); - - s->auxmode = level; -} - -static void glue_nmi(NMIState *n, int cpu_index, Error **errp) -{ - GLUEState *s = GLUE(n); - - /* Hold NMI active for 100ms */ - GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 1); - timer_mod(s->nmi_release, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 100); -} - -static void glue_nmi_release(void *opaque) -{ - GLUEState *s = GLUE(opaque); - - GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 0); -} - -static void glue_reset(DeviceState *dev) -{ - GLUEState *s = GLUE(dev); - - s->ipr = 0; - s->auxmode = 0; - - timer_del(s->nmi_release); -} - -static const VMStateDescription vmstate_glue = { - .name = "q800-glue", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8(ipr, GLUEState), - VMSTATE_UINT8(auxmode, GLUEState), - VMSTATE_TIMER_PTR(nmi_release, GLUEState), - VMSTATE_END_OF_LIST(), - }, -}; - -/* - * If the m68k CPU implemented its inbound irq lines as GPIO lines - * rather than via the m68k_set_irq_level() function we would not need - * this cpu link property and could instead provide outbound IRQ lines - * that the board could wire up to the CPU. - */ -static Property glue_properties[] = { - DEFINE_PROP_LINK("cpu", GLUEState, cpu, TYPE_M68K_CPU, M68kCPU *), - DEFINE_PROP_END_OF_LIST(), -}; - -static void glue_finalize(Object *obj) -{ - GLUEState *s = GLUE(obj); - - timer_free(s->nmi_release); -} - -static void glue_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - GLUEState *s = GLUE(dev); - - qdev_init_gpio_in(dev, GLUE_set_irq, 8); - qdev_init_gpio_in_named(dev, glue_auxmode_set_irq, "auxmode", 1); - - qdev_init_gpio_out(dev, s->irqs, 1); - - /* NMI release timer */ - s->nmi_release = timer_new_ms(QEMU_CLOCK_VIRTUAL, glue_nmi_release, s); -} - -static void glue_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - NMIClass *nc = NMI_CLASS(klass); - - dc->vmsd = &vmstate_glue; - dc->reset = glue_reset; - device_class_set_props(dc, glue_properties); - nc->nmi_monitor_handler = glue_nmi; -} - -static const TypeInfo glue_info = { - .name = TYPE_GLUE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(GLUEState), - .instance_init = glue_init, - .instance_finalize = glue_finalize, - .class_init = glue_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_NMI }, - { } - }, -}; static void main_cpu_reset(void *opaque) { @@ -330,6 +112,13 @@ static void main_cpu_reset(void *opaque) cpu->env.pc = ldl_phys(cs->as, 4); } +static void rerandomize_rng_seed(void *opaque) +{ + struct bi_record *rng_seed = opaque; + qemu_guest_getrandom_nofail((void *)rng_seed->data + 2, + be16_to_cpu(*(uint16_t *)rng_seed->data)); +} + static uint8_t fake_mac_rom[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -351,9 +140,113 @@ static uint8_t fake_mac_rom[] = { 0x60, 0xFE /* bras [self] */ }; -static void q800_init(MachineState *machine) +static MemTxResult macio_alias_read(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs) { - M68kCPU *cpu = NULL; + MemTxResult r; + uint32_t val; + + addr &= IO_SLICE_MASK; + addr |= IO_BASE; + + switch (size) { + case 4: + val = address_space_ldl_be(&address_space_memory, addr, attrs, &r); + break; + case 2: + val = address_space_lduw_be(&address_space_memory, addr, attrs, &r); + break; + case 1: + val = address_space_ldub(&address_space_memory, addr, attrs, &r); + break; + default: + g_assert_not_reached(); + } + + *data = val; + return r; +} + +static MemTxResult macio_alias_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size, MemTxAttrs attrs) +{ + MemTxResult r; + + addr &= IO_SLICE_MASK; + addr |= IO_BASE; + + switch (size) { + case 4: + address_space_stl_be(&address_space_memory, addr, value, attrs, &r); + break; + case 2: + address_space_stw_be(&address_space_memory, addr, value, attrs, &r); + break; + case 1: + address_space_stb(&address_space_memory, addr, value, attrs, &r); + break; + default: + g_assert_not_reached(); + } + + return r; +} + +static const MemoryRegionOps macio_alias_ops = { + .read_with_attrs = macio_alias_read, + .write_with_attrs = macio_alias_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static uint64_t machine_id_read(void *opaque, hwaddr addr, unsigned size) +{ + return Q800_MACHINE_ID; +} + +static void machine_id_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + return; +} + +static const MemoryRegionOps machine_id_ops = { + .read = machine_id_read, + .write = machine_id_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint64_t ramio_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0x0; +} + +static void ramio_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + return; +} + +static const MemoryRegionOps ramio_ops = { + .read = ramio_read, + .write = ramio_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void q800_machine_init(MachineState *machine) +{ + Q800MachineState *m = Q800_MACHINE(machine); int linux_boot; int32_t kernel_size; uint64_t elf_entry; @@ -361,11 +254,7 @@ static void q800_init(MachineState *machine) int bios_size; ram_addr_t initrd_base; int32_t initrd_size; - MemoryRegion *rom; - MemoryRegion *io; - MemoryRegion *dp8393x_prom = g_new(MemoryRegion, 1); uint8_t *prom; - const int io_slice_nb = (IO_SIZE / IO_SLICE) - 1; int i, checksum; MacFbMode *macfb_mode; ram_addr_t ram_size = machine->ram_size; @@ -376,15 +265,15 @@ static void q800_init(MachineState *machine) hwaddr parameters_base; CPUState *cs; DeviceState *dev; - DeviceState *via1_dev, *via2_dev; - DeviceState *escc_orgate; SysBusESPState *sysbus_esp; ESPState *esp; SysBusDevice *sysbus; BusState *adb_bus; NubusBus *nubus; - DeviceState *glue; DriveInfo *dinfo; + NICInfo *nd; + MACAddr mac; + uint8_t rng_seed[32]; linux_boot = (kernel_filename != NULL); @@ -395,68 +284,95 @@ static void q800_init(MachineState *machine) } /* init CPUs */ - cpu = M68K_CPU(cpu_create(machine->cpu_type)); - qemu_register_reset(main_cpu_reset, cpu); + object_initialize_child(OBJECT(machine), "cpu", &m->cpu, machine->cpu_type); + qdev_realize(DEVICE(&m->cpu), NULL, &error_fatal); + qemu_register_reset(main_cpu_reset, &m->cpu); /* RAM */ - memory_region_add_subregion(get_system_memory(), 0, machine->ram); + memory_region_init_io(&m->ramio, OBJECT(machine), &ramio_ops, &m->ramio, + "ram", RAM_SIZE); + memory_region_add_subregion(get_system_memory(), 0x0, &m->ramio); + + memory_region_add_subregion(&m->ramio, 0, machine->ram); + + /* + * Create container for all IO devices + */ + memory_region_init(&m->macio, OBJECT(machine), "mac-io", IO_SLICE); + memory_region_add_subregion(get_system_memory(), IO_BASE, &m->macio); /* * Memory from IO_BASE to IO_BASE + IO_SLICE is repeated * from IO_BASE + IO_SLICE to IO_BASE + IO_SIZE */ - io = g_new(MemoryRegion, io_slice_nb); - for (i = 0; i < io_slice_nb; i++) { - char *name = g_strdup_printf("mac_m68k.io[%d]", i + 1); + memory_region_init_io(&m->macio_alias, OBJECT(machine), &macio_alias_ops, + &m->macio, "mac-io.alias", IO_SIZE - IO_SLICE); + memory_region_add_subregion(get_system_memory(), IO_BASE + IO_SLICE, + &m->macio_alias); - memory_region_init_alias(&io[i], NULL, name, get_system_memory(), - IO_BASE, IO_SLICE); - memory_region_add_subregion(get_system_memory(), - IO_BASE + (i + 1) * IO_SLICE, &io[i]); - g_free(name); - } + memory_region_init_io(&m->machine_id, NULL, &machine_id_ops, NULL, + "Machine ID", 4); + memory_region_add_subregion(get_system_memory(), 0x5ffffffc, + &m->machine_id); /* IRQ Glue */ - glue = qdev_new(TYPE_GLUE); - object_property_set_link(OBJECT(glue), "cpu", OBJECT(cpu), &error_abort); - sysbus_realize_and_unref(SYS_BUS_DEVICE(glue), &error_fatal); + object_initialize_child(OBJECT(machine), "glue", &m->glue, TYPE_GLUE); + object_property_set_link(OBJECT(&m->glue), "cpu", OBJECT(&m->cpu), + &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&m->glue), &error_fatal); + + /* djMEMC memory controller */ + object_initialize_child(OBJECT(machine), "djmemc", &m->djmemc, + TYPE_DJMEMC); + sysbus = SYS_BUS_DEVICE(&m->djmemc); + sysbus_realize_and_unref(sysbus, &error_fatal); + memory_region_add_subregion(&m->macio, DJMEMC_BASE - IO_BASE, + sysbus_mmio_get_region(sysbus, 0)); + + /* IOSB subsystem */ + object_initialize_child(OBJECT(machine), "iosb", &m->iosb, TYPE_IOSB); + sysbus = SYS_BUS_DEVICE(&m->iosb); + sysbus_realize_and_unref(sysbus, &error_fatal); + memory_region_add_subregion(&m->macio, IOSB_BASE - IO_BASE, + sysbus_mmio_get_region(sysbus, 0)); /* VIA 1 */ - via1_dev = qdev_new(TYPE_MOS6522_Q800_VIA1); + object_initialize_child(OBJECT(machine), "via1", &m->via1, + TYPE_MOS6522_Q800_VIA1); dinfo = drive_get(IF_MTD, 0, 0); if (dinfo) { - qdev_prop_set_drive(via1_dev, "drive", blk_by_legacy_dinfo(dinfo)); + qdev_prop_set_drive(DEVICE(&m->via1), "drive", + blk_by_legacy_dinfo(dinfo)); } - sysbus = SYS_BUS_DEVICE(via1_dev); - sysbus_realize_and_unref(sysbus, &error_fatal); - sysbus_mmio_map(sysbus, 1, VIA_BASE); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, GLUE_IRQ_IN_VIA1)); + sysbus = SYS_BUS_DEVICE(&m->via1); + sysbus_realize(sysbus, &error_fatal); + memory_region_add_subregion(&m->macio, VIA_BASE - IO_BASE, + sysbus_mmio_get_region(sysbus, 1)); + sysbus_connect_irq(sysbus, 0, + qdev_get_gpio_in(DEVICE(&m->glue), GLUE_IRQ_IN_VIA1)); /* A/UX mode */ - qdev_connect_gpio_out(via1_dev, 0, - qdev_get_gpio_in_named(glue, "auxmode", 0)); + qdev_connect_gpio_out(DEVICE(&m->via1), 0, + qdev_get_gpio_in_named(DEVICE(&m->glue), + "auxmode", 0)); - adb_bus = qdev_get_child_bus(via1_dev, "adb.0"); + adb_bus = qdev_get_child_bus(DEVICE(&m->via1), "adb.0"); dev = qdev_new(TYPE_ADB_KEYBOARD); qdev_realize_and_unref(dev, adb_bus, &error_fatal); dev = qdev_new(TYPE_ADB_MOUSE); qdev_realize_and_unref(dev, adb_bus, &error_fatal); /* VIA 2 */ - via2_dev = qdev_new(TYPE_MOS6522_Q800_VIA2); - sysbus = SYS_BUS_DEVICE(via2_dev); - sysbus_realize_and_unref(sysbus, &error_fatal); - sysbus_mmio_map(sysbus, 1, VIA_BASE + VIA_SIZE); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, GLUE_IRQ_IN_VIA2)); + object_initialize_child(OBJECT(machine), "via2", &m->via2, + TYPE_MOS6522_Q800_VIA2); + sysbus = SYS_BUS_DEVICE(&m->via2); + sysbus_realize(sysbus, &error_fatal); + memory_region_add_subregion(&m->macio, VIA_BASE - IO_BASE + VIA_SIZE, + sysbus_mmio_get_region(sysbus, 1)); + sysbus_connect_irq(sysbus, 0, + qdev_get_gpio_in(DEVICE(&m->glue), GLUE_IRQ_IN_VIA2)); /* MACSONIC */ - if (nb_nics > 1) { - error_report("q800 can only have one ethernet interface"); - exit(1); - } - - qemu_check_nic_model(&nd_table[0], "dp83932"); - /* * MacSonic driver needs an Apple MAC address * Valid prefix are: @@ -466,38 +382,51 @@ static void q800_init(MachineState *machine) * 08:00:07 Apple * (Q800 use the last one) */ - nd_table[0].macaddr.a[0] = 0x08; - nd_table[0].macaddr.a[1] = 0x00; - nd_table[0].macaddr.a[2] = 0x07; + object_initialize_child(OBJECT(machine), "dp8393x", &m->dp8393x, + TYPE_DP8393X); + dev = DEVICE(&m->dp8393x); + nd = qemu_find_nic_info(TYPE_DP8393X, true, "dp83932"); + if (nd) { + qdev_set_nic_properties(dev, nd); + memcpy(mac.a, nd->macaddr.a, sizeof(mac.a)); + } else { + qemu_macaddr_default_if_unset(&mac); + } + mac.a[0] = 0x08; + mac.a[1] = 0x00; + mac.a[2] = 0x07; + qdev_prop_set_macaddr(dev, "mac", mac.a); - dev = qdev_new("dp8393x"); - qdev_set_nic_properties(dev, &nd_table[0]); qdev_prop_set_uint8(dev, "it_shift", 2); qdev_prop_set_bit(dev, "big_endian", true); object_property_set_link(OBJECT(dev), "dma_mr", OBJECT(get_system_memory()), &error_abort); sysbus = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(sysbus, &error_fatal); - sysbus_mmio_map(sysbus, 0, SONIC_BASE); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, GLUE_IRQ_IN_SONIC)); + sysbus_realize(sysbus, &error_fatal); + memory_region_add_subregion(&m->macio, SONIC_BASE - IO_BASE, + sysbus_mmio_get_region(sysbus, 0)); + sysbus_connect_irq(sysbus, 0, + qdev_get_gpio_in(DEVICE(&m->glue), GLUE_IRQ_IN_SONIC)); - memory_region_init_rom(dp8393x_prom, NULL, "dp8393x-q800.prom", + memory_region_init_rom(&m->dp8393x_prom, NULL, "dp8393x-q800.prom", SONIC_PROM_SIZE, &error_fatal); memory_region_add_subregion(get_system_memory(), SONIC_PROM_BASE, - dp8393x_prom); + &m->dp8393x_prom); /* Add MAC address with valid checksum to PROM */ - prom = memory_region_get_ram_ptr(dp8393x_prom); + prom = memory_region_get_ram_ptr(&m->dp8393x_prom); checksum = 0; for (i = 0; i < 6; i++) { - prom[i] = revbit8(nd_table[0].macaddr.a[i]); + prom[i] = revbit8(mac.a[i]); checksum ^= prom[i]; } prom[7] = 0xff - checksum; /* SCC */ - dev = qdev_new(TYPE_ESCC); + object_initialize_child(OBJECT(machine), "escc", &m->escc, + TYPE_ESCC); + dev = DEVICE(&m->escc); qdev_prop_set_uint32(dev, "disabled", 0); qdev_prop_set_uint32(dev, "frequency", MAC_CLOCK); qdev_prop_set_uint32(dev, "it_shift", 1); @@ -507,22 +436,34 @@ static void q800_init(MachineState *machine) qdev_prop_set_uint32(dev, "chnBtype", 0); qdev_prop_set_uint32(dev, "chnAtype", 0); sysbus = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(sysbus, &error_fatal); + sysbus_realize(sysbus, &error_fatal); /* Logically OR both its IRQs together */ - escc_orgate = DEVICE(object_new(TYPE_OR_IRQ)); - object_property_set_int(OBJECT(escc_orgate), "num-lines", 2, &error_fatal); - qdev_realize_and_unref(escc_orgate, NULL, &error_fatal); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(escc_orgate, 0)); - sysbus_connect_irq(sysbus, 1, qdev_get_gpio_in(escc_orgate, 1)); - qdev_connect_gpio_out(DEVICE(escc_orgate), 0, - qdev_get_gpio_in(glue, GLUE_IRQ_IN_ESCC)); - sysbus_mmio_map(sysbus, 0, SCC_BASE); + object_initialize_child(OBJECT(machine), "escc_orgate", &m->escc_orgate, + TYPE_OR_IRQ); + object_property_set_int(OBJECT(&m->escc_orgate), "num-lines", 2, + &error_fatal); + dev = DEVICE(&m->escc_orgate); + qdev_realize(dev, NULL, &error_fatal); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(dev, 0)); + sysbus_connect_irq(sysbus, 1, qdev_get_gpio_in(dev, 1)); + qdev_connect_gpio_out(dev, 0, + qdev_get_gpio_in(DEVICE(&m->glue), + GLUE_IRQ_IN_ESCC)); + memory_region_add_subregion(&m->macio, SCC_BASE - IO_BASE, + sysbus_mmio_get_region(sysbus, 0)); + + /* Create alias for NetBSD */ + memory_region_init_alias(&m->escc_alias, OBJECT(machine), "escc-alias", + sysbus_mmio_get_region(sysbus, 0), 0, 0x8); + memory_region_add_subregion(&m->macio, SCC_BASE - IO_BASE - 0x20, + &m->escc_alias); /* SCSI */ - dev = qdev_new(TYPE_SYSBUS_ESP); - sysbus_esp = SYSBUS_ESP(dev); + object_initialize_child(OBJECT(machine), "esp", &m->esp, + TYPE_SYSBUS_ESP); + sysbus_esp = SYSBUS_ESP(&m->esp); esp = &sysbus_esp->esp; esp->dma_memory_read = NULL; esp->dma_memory_write = NULL; @@ -530,40 +471,77 @@ static void q800_init(MachineState *machine) sysbus_esp->it_shift = 4; esp->dma_enabled = 1; - sysbus = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(sysbus, &error_fatal); + sysbus = SYS_BUS_DEVICE(&m->esp); + sysbus_realize(sysbus, &error_fatal); /* SCSI and SCSI data IRQs are negative edge triggered */ - sysbus_connect_irq(sysbus, 0, qemu_irq_invert(qdev_get_gpio_in(via2_dev, - VIA2_IRQ_SCSI_BIT))); - sysbus_connect_irq(sysbus, 1, qemu_irq_invert(qdev_get_gpio_in(via2_dev, - VIA2_IRQ_SCSI_DATA_BIT))); - sysbus_mmio_map(sysbus, 0, ESP_BASE); - sysbus_mmio_map(sysbus, 1, ESP_PDMA); + sysbus_connect_irq(sysbus, 0, + qemu_irq_invert( + qdev_get_gpio_in(DEVICE(&m->via2), + VIA2_IRQ_SCSI_BIT))); + sysbus_connect_irq(sysbus, 1, + qemu_irq_invert( + qdev_get_gpio_in(DEVICE(&m->via2), + VIA2_IRQ_SCSI_DATA_BIT))); + memory_region_add_subregion(&m->macio, ESP_BASE - IO_BASE, + sysbus_mmio_get_region(sysbus, 0)); + memory_region_add_subregion(&m->macio, ESP_PDMA - IO_BASE, + sysbus_mmio_get_region(sysbus, 1)); scsi_bus_legacy_handle_cmdline(&esp->bus); + /* Apple Sound Chip */ + + object_initialize_child(OBJECT(machine), "asc", &m->asc, TYPE_ASC); + qdev_prop_set_uint8(DEVICE(&m->asc), "asctype", m->easc ? ASC_TYPE_EASC + : ASC_TYPE_ASC); + if (machine->audiodev) { + qdev_prop_set_string(DEVICE(&m->asc), "audiodev", machine->audiodev); + } + sysbus = SYS_BUS_DEVICE(&m->asc); + sysbus_realize_and_unref(sysbus, &error_fatal); + memory_region_add_subregion(&m->macio, ASC_BASE - IO_BASE, + sysbus_mmio_get_region(sysbus, 0)); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(DEVICE(&m->glue), + GLUE_IRQ_IN_ASC)); + + /* Wire ASC IRQ via GLUE for use in classic mode */ + qdev_connect_gpio_out(DEVICE(&m->glue), GLUE_IRQ_ASC, + qdev_get_gpio_in(DEVICE(&m->via2), + VIA2_IRQ_ASC_BIT)); + /* SWIM floppy controller */ - dev = qdev_new(TYPE_SWIM); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, SWIM_BASE); + object_initialize_child(OBJECT(machine), "swim", &m->swim, + TYPE_SWIM); + sysbus = SYS_BUS_DEVICE(&m->swim); + sysbus_realize(sysbus, &error_fatal); + memory_region_add_subregion(&m->macio, SWIM_BASE - IO_BASE, + sysbus_mmio_get_region(sysbus, 0)); /* NuBus */ - dev = qdev_new(TYPE_MAC_NUBUS_BRIDGE); - qdev_prop_set_uint32(dev, "slot-available-mask", + object_initialize_child(OBJECT(machine), "mac-nubus-bridge", + &m->mac_nubus_bridge, + TYPE_MAC_NUBUS_BRIDGE); + sysbus = SYS_BUS_DEVICE(&m->mac_nubus_bridge); + dev = DEVICE(&m->mac_nubus_bridge); + qdev_prop_set_uint32(DEVICE(&m->mac_nubus_bridge), "slot-available-mask", Q800_NUBUS_SLOTS_AVAILABLE); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, - MAC_NUBUS_FIRST_SLOT * NUBUS_SUPER_SLOT_SIZE); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, NUBUS_SLOT_BASE + - MAC_NUBUS_FIRST_SLOT * NUBUS_SLOT_SIZE); + sysbus_realize(sysbus, &error_fatal); + memory_region_add_subregion(get_system_memory(), + MAC_NUBUS_FIRST_SLOT * NUBUS_SUPER_SLOT_SIZE, + sysbus_mmio_get_region(sysbus, 0)); + memory_region_add_subregion(get_system_memory(), + NUBUS_SLOT_BASE + + MAC_NUBUS_FIRST_SLOT * NUBUS_SLOT_SIZE, + sysbus_mmio_get_region(sysbus, 1)); qdev_connect_gpio_out(dev, 9, - qdev_get_gpio_in_named(via2_dev, "nubus-irq", + qdev_get_gpio_in_named(DEVICE(&m->via2), "nubus-irq", VIA2_NUBUS_IRQ_INTVIDEO)); for (i = 1; i < VIA2_NUBUS_IRQ_NB; i++) { qdev_connect_gpio_out(dev, 9 + i, - qdev_get_gpio_in_named(via2_dev, "nubus-irq", + qdev_get_gpio_in_named(DEVICE(&m->via2), + "nubus-irq", VIA2_NUBUS_IRQ_9 + i)); } @@ -571,15 +549,17 @@ static void q800_init(MachineState *machine) * Since the framebuffer in slot 0x9 uses a separate IRQ, wire the unused * IRQ via GLUE for use by SONIC Ethernet in classic mode */ - qdev_connect_gpio_out(glue, GLUE_IRQ_NUBUS_9, - qdev_get_gpio_in_named(via2_dev, "nubus-irq", + qdev_connect_gpio_out(DEVICE(&m->glue), GLUE_IRQ_NUBUS_9, + qdev_get_gpio_in_named(DEVICE(&m->via2), "nubus-irq", VIA2_NUBUS_IRQ_9)); - nubus = &NUBUS_BRIDGE(dev)->bus; + nubus = NUBUS_BUS(qdev_get_child_bus(dev, "nubus-bus.0")); /* framebuffer in nubus slot #9 */ - dev = qdev_new(TYPE_NUBUS_MACFB); + object_initialize_child(OBJECT(machine), "macfb", &m->macfb, + TYPE_NUBUS_MACFB); + dev = DEVICE(&m->macfb); qdev_prop_set_uint32(dev, "slot", 9); qdev_prop_set_uint32(dev, "width", graphic_width); qdev_prop_set_uint32(dev, "height", graphic_height); @@ -589,13 +569,21 @@ static void q800_init(MachineState *machine) } else { qdev_prop_set_uint8(dev, "display", MACFB_DISPLAY_VGA); } - qdev_realize_and_unref(dev, BUS(nubus), &error_fatal); + qdev_realize(dev, BUS(nubus), &error_fatal); macfb_mode = (NUBUS_MACFB(dev)->macfb).mode; - cs = CPU(cpu); + cs = CPU(&m->cpu); if (linux_boot) { uint64_t high; + void *param_blob, *param_ptr, *param_rng_seed; + + if (kernel_cmdline) { + param_blob = g_malloc(strlen(kernel_cmdline) + 1024); + } else { + param_blob = g_malloc(1024); + } + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, NULL, &high, NULL, 1, EM_68K, 0, 0); @@ -605,35 +593,41 @@ static void q800_init(MachineState *machine) } stl_phys(cs->as, 4, elf_entry); /* reset initial PC */ parameters_base = (high + 1) & ~1; + param_ptr = param_blob; - BOOTINFO1(cs->as, parameters_base, BI_MACHTYPE, MACH_MAC); - BOOTINFO1(cs->as, parameters_base, BI_FPUTYPE, FPU_68040); - BOOTINFO1(cs->as, parameters_base, BI_MMUTYPE, MMU_68040); - BOOTINFO1(cs->as, parameters_base, BI_CPUTYPE, CPU_68040); - BOOTINFO1(cs->as, parameters_base, BI_MAC_CPUID, CPUB_68040); - BOOTINFO1(cs->as, parameters_base, BI_MAC_MODEL, MAC_MODEL_Q800); - BOOTINFO1(cs->as, parameters_base, + BOOTINFO1(param_ptr, BI_MACHTYPE, MACH_MAC); + BOOTINFO1(param_ptr, BI_FPUTYPE, FPU_68040); + BOOTINFO1(param_ptr, BI_MMUTYPE, MMU_68040); + BOOTINFO1(param_ptr, BI_CPUTYPE, CPU_68040); + BOOTINFO1(param_ptr, BI_MAC_CPUID, CPUB_68040); + BOOTINFO1(param_ptr, BI_MAC_MODEL, MAC_MODEL_Q800); + BOOTINFO1(param_ptr, BI_MAC_MEMSIZE, ram_size >> 20); /* in MB */ - BOOTINFO2(cs->as, parameters_base, BI_MEMCHUNK, 0, ram_size); - BOOTINFO1(cs->as, parameters_base, BI_MAC_VADDR, + BOOTINFO2(param_ptr, BI_MEMCHUNK, 0, ram_size); + BOOTINFO1(param_ptr, BI_MAC_VADDR, VIDEO_BASE + macfb_mode->offset); - BOOTINFO1(cs->as, parameters_base, BI_MAC_VDEPTH, graphic_depth); - BOOTINFO1(cs->as, parameters_base, BI_MAC_VDIM, + BOOTINFO1(param_ptr, BI_MAC_VDEPTH, graphic_depth); + BOOTINFO1(param_ptr, BI_MAC_VDIM, (graphic_height << 16) | graphic_width); - BOOTINFO1(cs->as, parameters_base, BI_MAC_VROW, macfb_mode->stride); - BOOTINFO1(cs->as, parameters_base, BI_MAC_SCCBASE, SCC_BASE); + BOOTINFO1(param_ptr, BI_MAC_VROW, macfb_mode->stride); + BOOTINFO1(param_ptr, BI_MAC_SCCBASE, SCC_BASE); - rom = g_malloc(sizeof(*rom)); - memory_region_init_ram_ptr(rom, NULL, "m68k_fake_mac.rom", + memory_region_init_ram_ptr(&m->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); + memory_region_set_readonly(&m->rom, true); + memory_region_add_subregion(get_system_memory(), MACROM_ADDR, &m->rom); if (kernel_cmdline) { - BOOTINFOSTR(cs->as, parameters_base, BI_COMMAND_LINE, + BOOTINFOSTR(param_ptr, BI_COMMAND_LINE, kernel_cmdline); } + /* Pass seed to RNG. */ + param_rng_seed = param_ptr; + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + BOOTINFODATA(param_ptr, BI_RNG_SEED, + rng_seed, sizeof(rng_seed)); + /* load initrd */ if (initrd_filename) { initrd_size = get_image_size(initrd_filename); @@ -646,21 +640,32 @@ static void q800_init(MachineState *machine) initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK; load_image_targphys(initrd_filename, initrd_base, ram_size - initrd_base); - BOOTINFO2(cs->as, parameters_base, BI_RAMDISK, initrd_base, + BOOTINFO2(param_ptr, BI_RAMDISK, initrd_base, initrd_size); } else { initrd_base = 0; initrd_size = 0; } - BOOTINFO0(cs->as, parameters_base, BI_LAST); + BOOTINFO0(param_ptr, BI_LAST); + rom_add_blob_fixed_as("bootinfo", param_blob, param_ptr - param_blob, + parameters_base, cs->as); + qemu_register_reset_nosnapshotload(rerandomize_rng_seed, + rom_ptr_for_as(cs->as, parameters_base, + param_ptr - param_blob) + + (param_rng_seed - param_blob)); + g_free(param_blob); } else { uint8_t *ptr; /* allocate and load BIOS */ - rom = g_malloc(sizeof(*rom)); - memory_region_init_rom(rom, NULL, "m68k_mac.rom", MACROM_SIZE, + memory_region_init_rom(&m->rom, NULL, "m68k_mac.rom", MACROM_SIZE, &error_abort); filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - memory_region_add_subregion(get_system_memory(), MACROM_ADDR, rom); + memory_region_add_subregion(get_system_memory(), MACROM_ADDR, &m->rom); + + memory_region_init_alias(&m->rom_alias, NULL, "m68k_mac.rom-alias", + &m->rom, 0, MACROM_SIZE); + memory_region_add_subregion(get_system_memory(), 0x40000000, + &m->rom_alias); /* Load MacROM binary */ if (filename) { @@ -686,27 +691,77 @@ static void q800_init(MachineState *machine) } } +static bool q800_get_easc(Object *obj, Error **errp) +{ + Q800MachineState *ms = Q800_MACHINE(obj); + + return ms->easc; +} + +static void q800_set_easc(Object *obj, bool value, Error **errp) +{ + Q800MachineState *ms = Q800_MACHINE(obj); + + ms->easc = value; +} + +static void q800_init(Object *obj) +{ + Q800MachineState *ms = Q800_MACHINE(obj); + + /* Default to EASC */ + ms->easc = true; +} + +static GlobalProperty hw_compat_q800[] = { + { "scsi-hd", "quirk_mode_page_vendor_specific_apple", "on" }, + { "scsi-hd", "vendor", " SEAGATE" }, + { "scsi-hd", "product", " ST225N" }, + { "scsi-hd", "ver", "1.0 " }, + { "scsi-cd", "quirk_mode_page_apple_vendor", "on" }, + { "scsi-cd", "quirk_mode_sense_rom_use_dbd", "on" }, + { "scsi-cd", "quirk_mode_page_vendor_specific_apple", "on" }, + { "scsi-cd", "quirk_mode_page_truncated", "on" }, + { "scsi-cd", "vendor", "MATSHITA" }, + { "scsi-cd", "product", "CD-ROM CR-8005" }, + { "scsi-cd", "ver", "1.0k" }, +}; +static const size_t hw_compat_q800_len = G_N_ELEMENTS(hw_compat_q800); + static void q800_machine_class_init(ObjectClass *oc, void *data) { + static const char * const valid_cpu_types[] = { + M68K_CPU_TYPE_NAME("m68040"), + NULL + }; MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "Macintosh Quadra 800"; - mc->init = q800_init; + mc->init = q800_machine_init; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040"); + mc->valid_cpu_types = valid_cpu_types; mc->max_cpus = 1; mc->block_default_type = IF_SCSI; mc->default_ram_id = "m68k_mac.ram"; + machine_add_audiodev_property(mc); + compat_props_add(mc->compat_props, hw_compat_q800, hw_compat_q800_len); + + object_class_property_add_bool(oc, "easc", q800_get_easc, q800_set_easc); + object_class_property_set_description(oc, "easc", + "Set to off to use ASC rather than EASC"); } static const TypeInfo q800_machine_typeinfo = { .name = MACHINE_TYPE_NAME("q800"), .parent = TYPE_MACHINE, + .instance_init = q800_init, + .instance_size = sizeof(Q800MachineState), .class_init = q800_machine_class_init, }; static void q800_machine_register_types(void) { type_register_static(&q800_machine_typeinfo); - type_register_static(&glue_info); } type_init(q800_machine_register_types) diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index e215aa3d42..b8e5e102e6 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: GPL-2.0-or-later * - * QEMU Vitual M68K Machine + * QEMU Virtual M68K Machine * * (c) 2020 Laurent Vivier * @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/guest-random.h" #include "sysemu/sysemu.h" #include "cpu.h" #include "hw/boards.h" @@ -22,6 +23,7 @@ #include "bootinfo.h" #include "net/net.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "sysemu/qtest.h" #include "sysemu/runstate.h" #include "sysemu/reset.h" @@ -101,6 +103,13 @@ static void main_cpu_reset(void *opaque) cpu->env.pc = reset_info->initial_pc; } +static void rerandomize_rng_seed(void *opaque) +{ + struct bi_record *rng_seed = opaque; + qemu_guest_getrandom_nofail((void *)rng_seed->data + 2, + be16_to_cpu(*(uint16_t *)rng_seed->data)); +} + static void virt_init(MachineState *machine) { M68kCPU *cpu = NULL; @@ -120,6 +129,7 @@ static void virt_init(MachineState *machine) hwaddr io_base; int i; ResetInfo *reset_info; + uint8_t rng_seed[32]; if (ram_size > 3399672 * KiB) { /* @@ -145,6 +155,8 @@ static void virt_init(MachineState *machine) /* IRQ Controller */ irqc_dev = qdev_new(TYPE_M68K_IRQC); + object_property_set_link(OBJECT(irqc_dev), "m68k-cpu", + OBJECT(cpu), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(irqc_dev), &error_fatal); /* @@ -171,6 +183,7 @@ static void virt_init(MachineState *machine) io_base = VIRT_GF_RTC_MMIO_BASE; for (i = 0; i < VIRT_GF_RTC_NB; i++) { dev = qdev_new(TYPE_GOLDFISH_RTC); + qdev_prop_set_bit(dev, "big-endian", true); sysbus = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(sysbus, &error_fatal); sysbus_mmio_map(sysbus, 0, io_base); @@ -188,11 +201,8 @@ static void virt_init(MachineState *machine) sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_GF_TTY_IRQ_BASE)); /* virt controller */ - dev = qdev_new(TYPE_VIRT_CTRL); - sysbus = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(sysbus, &error_fatal); - sysbus_mmio_map(sysbus, 0, VIRT_CTRL_MMIO_BASE); - sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_CTRL_IRQ_BASE)); + dev = sysbus_create_simple(TYPE_VIRT_CTRL, VIRT_CTRL_MMIO_BASE, + PIC_GPIO(VIRT_CTRL_IRQ_BASE)); /* virtio-mmio */ io_base = VIRT_VIRTIO_MMIO_BASE; @@ -209,6 +219,13 @@ static void virt_init(MachineState *machine) if (kernel_filename) { CPUState *cs = CPU(cpu); uint64_t high; + void *param_blob, *param_ptr, *param_rng_seed; + + if (kernel_cmdline) { + param_blob = g_malloc(strlen(kernel_cmdline) + 1024); + } else { + param_blob = g_malloc(1024); + } kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, NULL, &high, NULL, 1, @@ -219,32 +236,50 @@ static void virt_init(MachineState *machine) } reset_info->initial_pc = elf_entry; parameters_base = (high + 1) & ~1; + param_ptr = param_blob; - BOOTINFO1(cs->as, parameters_base, BI_MACHTYPE, MACH_VIRT); - BOOTINFO1(cs->as, parameters_base, BI_FPUTYPE, FPU_68040); - BOOTINFO1(cs->as, parameters_base, BI_MMUTYPE, MMU_68040); - BOOTINFO1(cs->as, parameters_base, BI_CPUTYPE, CPU_68040); - BOOTINFO2(cs->as, parameters_base, BI_MEMCHUNK, 0, ram_size); + BOOTINFO1(param_ptr, BI_MACHTYPE, MACH_VIRT); + if (m68k_feature(&cpu->env, M68K_FEATURE_M68020)) { + BOOTINFO1(param_ptr, BI_CPUTYPE, CPU_68020); + } else if (m68k_feature(&cpu->env, M68K_FEATURE_M68030)) { + BOOTINFO1(param_ptr, BI_MMUTYPE, MMU_68030); + BOOTINFO1(param_ptr, BI_CPUTYPE, CPU_68030); + } else if (m68k_feature(&cpu->env, M68K_FEATURE_M68040)) { + BOOTINFO1(param_ptr, BI_FPUTYPE, FPU_68040); + BOOTINFO1(param_ptr, BI_MMUTYPE, MMU_68040); + BOOTINFO1(param_ptr, BI_CPUTYPE, CPU_68040); + } else if (m68k_feature(&cpu->env, M68K_FEATURE_M68060)) { + BOOTINFO1(param_ptr, BI_FPUTYPE, FPU_68060); + BOOTINFO1(param_ptr, BI_MMUTYPE, MMU_68060); + BOOTINFO1(param_ptr, BI_CPUTYPE, CPU_68060); + } + BOOTINFO2(param_ptr, BI_MEMCHUNK, 0, ram_size); - BOOTINFO1(cs->as, parameters_base, BI_VIRT_QEMU_VERSION, + BOOTINFO1(param_ptr, BI_VIRT_QEMU_VERSION, ((QEMU_VERSION_MAJOR << 24) | (QEMU_VERSION_MINOR << 16) | (QEMU_VERSION_MICRO << 8))); - BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_PIC_BASE, + BOOTINFO2(param_ptr, BI_VIRT_GF_PIC_BASE, VIRT_GF_PIC_MMIO_BASE, VIRT_GF_PIC_IRQ_BASE); - BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_RTC_BASE, + BOOTINFO2(param_ptr, BI_VIRT_GF_RTC_BASE, VIRT_GF_RTC_MMIO_BASE, VIRT_GF_RTC_IRQ_BASE); - BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_TTY_BASE, + BOOTINFO2(param_ptr, BI_VIRT_GF_TTY_BASE, VIRT_GF_TTY_MMIO_BASE, VIRT_GF_TTY_IRQ_BASE); - BOOTINFO2(cs->as, parameters_base, BI_VIRT_CTRL_BASE, + BOOTINFO2(param_ptr, BI_VIRT_CTRL_BASE, VIRT_CTRL_MMIO_BASE, VIRT_CTRL_IRQ_BASE); - BOOTINFO2(cs->as, parameters_base, BI_VIRT_VIRTIO_BASE, + BOOTINFO2(param_ptr, BI_VIRT_VIRTIO_BASE, VIRT_VIRTIO_MMIO_BASE, VIRT_VIRTIO_IRQ_BASE); if (kernel_cmdline) { - BOOTINFOSTR(cs->as, parameters_base, BI_COMMAND_LINE, + BOOTINFOSTR(param_ptr, BI_COMMAND_LINE, kernel_cmdline); } + /* Pass seed to RNG. */ + param_rng_seed = param_ptr; + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + BOOTINFODATA(param_ptr, BI_RNG_SEED, + rng_seed, sizeof(rng_seed)); + /* load initrd */ if (initrd_filename) { initrd_size = get_image_size(initrd_filename); @@ -257,13 +292,20 @@ static void virt_init(MachineState *machine) initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK; load_image_targphys(initrd_filename, initrd_base, ram_size - initrd_base); - BOOTINFO2(cs->as, parameters_base, BI_RAMDISK, initrd_base, + BOOTINFO2(param_ptr, BI_RAMDISK, initrd_base, initrd_size); } else { initrd_base = 0; initrd_size = 0; } - BOOTINFO0(cs->as, parameters_base, BI_LAST); + BOOTINFO0(param_ptr, BI_LAST); + rom_add_blob_fixed_as("bootinfo", param_blob, param_ptr - param_blob, + parameters_base, cs->as); + qemu_register_reset_nosnapshotload(rerandomize_rng_seed, + rom_ptr_for_as(cs->as, parameters_base, + param_ptr - param_blob) + + (param_rng_seed - param_blob)); + g_free(param_blob); } } @@ -315,10 +357,45 @@ type_init(virt_machine_register_types) } \ type_init(machvirt_machine_##major##_##minor##_init); -static void virt_machine_7_1_options(MachineClass *mc) +static void virt_machine_9_0_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE(7, 1, true) +DEFINE_VIRT_MACHINE(9, 0, true) + +static void virt_machine_8_2_options(MachineClass *mc) +{ + virt_machine_9_0_options(mc); + compat_props_add(mc->compat_props, hw_compat_8_2, hw_compat_8_2_len); +} +DEFINE_VIRT_MACHINE(8, 2, false) + +static void virt_machine_8_1_options(MachineClass *mc) +{ + virt_machine_8_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_8_1, hw_compat_8_1_len); +} +DEFINE_VIRT_MACHINE(8, 1, false) + +static void virt_machine_8_0_options(MachineClass *mc) +{ + virt_machine_8_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_8_0, hw_compat_8_0_len); +} +DEFINE_VIRT_MACHINE(8, 0, false) + +static void virt_machine_7_2_options(MachineClass *mc) +{ + virt_machine_8_0_options(mc); + compat_props_add(mc->compat_props, hw_compat_7_2, hw_compat_7_2_len); +} +DEFINE_VIRT_MACHINE(7, 2, false) + +static void virt_machine_7_1_options(MachineClass *mc) +{ + virt_machine_7_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_7_1, hw_compat_7_1_len); +} +DEFINE_VIRT_MACHINE(7, 1, false) static void virt_machine_7_0_options(MachineClass *mc) { diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 3bf2869573..b0a7e9f11b 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1,6 +1,18 @@ +/* + * CXL Type 3 (memory expander) device + * + * Copyright(C) 2020 Intel Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2. See the + * COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-v2-only + */ + #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/error-report.h" +#include "qapi/qapi-commands-cxl.h" #include "hw/mem/memory-device.h" #include "hw/mem/pc-dimm.h" #include "hw/pci/pci.h" @@ -11,28 +23,303 @@ #include "qemu/pmem.h" #include "qemu/range.h" #include "qemu/rcu.h" +#include "qemu/guest-random.h" #include "sysemu/hostmem.h" +#include "sysemu/numa.h" #include "hw/cxl/cxl.h" +#include "hw/pci/msix.h" + +#define DWORD_BYTE 4 + +/* Default CDAT entries for a memory region */ +enum { + CT3_CDAT_DSMAS, + CT3_CDAT_DSLBIS0, + CT3_CDAT_DSLBIS1, + CT3_CDAT_DSLBIS2, + CT3_CDAT_DSLBIS3, + CT3_CDAT_DSEMTS, + CT3_CDAT_NUM_ENTRIES +}; + +static void ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table, + int dsmad_handle, MemoryRegion *mr, + bool is_pmem, uint64_t dpa_base) +{ + CDATDsmas *dsmas; + CDATDslbis *dslbis0; + CDATDslbis *dslbis1; + CDATDslbis *dslbis2; + CDATDslbis *dslbis3; + CDATDsemts *dsemts; + + dsmas = g_malloc(sizeof(*dsmas)); + *dsmas = (CDATDsmas) { + .header = { + .type = CDAT_TYPE_DSMAS, + .length = sizeof(*dsmas), + }, + .DSMADhandle = dsmad_handle, + .flags = is_pmem ? CDAT_DSMAS_FLAG_NV : 0, + .DPA_base = dpa_base, + .DPA_length = memory_region_size(mr), + }; + + /* For now, no memory side cache, plausiblish numbers */ + dslbis0 = g_malloc(sizeof(*dslbis0)); + *dslbis0 = (CDATDslbis) { + .header = { + .type = CDAT_TYPE_DSLBIS, + .length = sizeof(*dslbis0), + }, + .handle = dsmad_handle, + .flags = HMAT_LB_MEM_MEMORY, + .data_type = HMAT_LB_DATA_READ_LATENCY, + .entry_base_unit = 10000, /* 10ns base */ + .entry[0] = 15, /* 150ns */ + }; + + dslbis1 = g_malloc(sizeof(*dslbis1)); + *dslbis1 = (CDATDslbis) { + .header = { + .type = CDAT_TYPE_DSLBIS, + .length = sizeof(*dslbis1), + }, + .handle = dsmad_handle, + .flags = HMAT_LB_MEM_MEMORY, + .data_type = HMAT_LB_DATA_WRITE_LATENCY, + .entry_base_unit = 10000, + .entry[0] = 25, /* 250ns */ + }; + + dslbis2 = g_malloc(sizeof(*dslbis2)); + *dslbis2 = (CDATDslbis) { + .header = { + .type = CDAT_TYPE_DSLBIS, + .length = sizeof(*dslbis2), + }, + .handle = dsmad_handle, + .flags = HMAT_LB_MEM_MEMORY, + .data_type = HMAT_LB_DATA_READ_BANDWIDTH, + .entry_base_unit = 1000, /* GB/s */ + .entry[0] = 16, + }; + + dslbis3 = g_malloc(sizeof(*dslbis3)); + *dslbis3 = (CDATDslbis) { + .header = { + .type = CDAT_TYPE_DSLBIS, + .length = sizeof(*dslbis3), + }, + .handle = dsmad_handle, + .flags = HMAT_LB_MEM_MEMORY, + .data_type = HMAT_LB_DATA_WRITE_BANDWIDTH, + .entry_base_unit = 1000, /* GB/s */ + .entry[0] = 16, + }; + + dsemts = g_malloc(sizeof(*dsemts)); + *dsemts = (CDATDsemts) { + .header = { + .type = CDAT_TYPE_DSEMTS, + .length = sizeof(*dsemts), + }, + .DSMAS_handle = dsmad_handle, + /* + * NV: Reserved - the non volatile from DSMAS matters + * V: EFI_MEMORY_SP + */ + .EFI_memory_type_attr = is_pmem ? 2 : 1, + .DPA_offset = 0, + .DPA_length = memory_region_size(mr), + }; + + /* Header always at start of structure */ + cdat_table[CT3_CDAT_DSMAS] = (CDATSubHeader *)dsmas; + cdat_table[CT3_CDAT_DSLBIS0] = (CDATSubHeader *)dslbis0; + cdat_table[CT3_CDAT_DSLBIS1] = (CDATSubHeader *)dslbis1; + cdat_table[CT3_CDAT_DSLBIS2] = (CDATSubHeader *)dslbis2; + cdat_table[CT3_CDAT_DSLBIS3] = (CDATSubHeader *)dslbis3; + cdat_table[CT3_CDAT_DSEMTS] = (CDATSubHeader *)dsemts; +} + +static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv) +{ + g_autofree CDATSubHeader **table = NULL; + CXLType3Dev *ct3d = priv; + MemoryRegion *volatile_mr = NULL, *nonvolatile_mr = NULL; + int dsmad_handle = 0; + int cur_ent = 0; + int len = 0; + + if (!ct3d->hostpmem && !ct3d->hostvmem) { + return 0; + } + + if (ct3d->hostvmem) { + volatile_mr = host_memory_backend_get_memory(ct3d->hostvmem); + if (!volatile_mr) { + return -EINVAL; + } + len += CT3_CDAT_NUM_ENTRIES; + } + + if (ct3d->hostpmem) { + nonvolatile_mr = host_memory_backend_get_memory(ct3d->hostpmem); + if (!nonvolatile_mr) { + return -EINVAL; + } + len += CT3_CDAT_NUM_ENTRIES; + } + + table = g_malloc0(len * sizeof(*table)); + + /* Now fill them in */ + if (volatile_mr) { + ct3_build_cdat_entries_for_mr(table, dsmad_handle++, volatile_mr, + false, 0); + cur_ent = CT3_CDAT_NUM_ENTRIES; + } + + if (nonvolatile_mr) { + uint64_t base = volatile_mr ? memory_region_size(volatile_mr) : 0; + ct3_build_cdat_entries_for_mr(&(table[cur_ent]), dsmad_handle++, + nonvolatile_mr, true, base); + cur_ent += CT3_CDAT_NUM_ENTRIES; + } + assert(len == cur_ent); + + *cdat_table = g_steal_pointer(&table); + + return len; +} + +static void ct3_free_cdat_table(CDATSubHeader **cdat_table, int num, void *priv) +{ + int i; + + for (i = 0; i < num; i++) { + g_free(cdat_table[i]); + } + g_free(cdat_table); +} + +static bool cxl_doe_cdat_rsp(DOECap *doe_cap) +{ + CDATObject *cdat = &CXL_TYPE3(doe_cap->pdev)->cxl_cstate.cdat; + uint16_t ent; + void *base; + uint32_t len; + CDATReq *req = pcie_doe_get_write_mbox_ptr(doe_cap); + CDATRsp rsp; + + assert(cdat->entry_len); + + /* Discard if request length mismatched */ + if (pcie_doe_get_obj_len(req) < + DIV_ROUND_UP(sizeof(CDATReq), DWORD_BYTE)) { + return false; + } + + ent = req->entry_handle; + base = cdat->entry[ent].base; + len = cdat->entry[ent].length; + + rsp = (CDATRsp) { + .header = { + .vendor_id = CXL_VENDOR_ID, + .data_obj_type = CXL_DOE_TABLE_ACCESS, + .reserved = 0x0, + .length = DIV_ROUND_UP((sizeof(rsp) + len), DWORD_BYTE), + }, + .rsp_code = CXL_DOE_TAB_RSP, + .table_type = CXL_DOE_TAB_TYPE_CDAT, + .entry_handle = (ent < cdat->entry_len - 1) ? + ent + 1 : CXL_DOE_TAB_ENT_MAX, + }; + + memcpy(doe_cap->read_mbox, &rsp, sizeof(rsp)); + memcpy(doe_cap->read_mbox + DIV_ROUND_UP(sizeof(rsp), DWORD_BYTE), + base, len); + + doe_cap->read_mbox_len += rsp.header.length; + + return true; +} + +static uint32_t ct3d_config_read(PCIDevice *pci_dev, uint32_t addr, int size) +{ + CXLType3Dev *ct3d = CXL_TYPE3(pci_dev); + uint32_t val; + + if (pcie_doe_read_config(&ct3d->doe_cdat, addr, size, &val)) { + return val; + } + + return pci_default_read_config(pci_dev, addr, size); +} + +static void ct3d_config_write(PCIDevice *pci_dev, uint32_t addr, uint32_t val, + int size) +{ + CXLType3Dev *ct3d = CXL_TYPE3(pci_dev); + + pcie_doe_write_config(&ct3d->doe_cdat, addr, val, size); + pci_default_write_config(pci_dev, addr, val, size); + pcie_aer_write_config(pci_dev, addr, val, size); +} + +/* + * Null value of all Fs suggested by IEEE RA guidelines for use of + * EU, OUI and CID + */ +#define UI64_NULL ~(0ULL) static void build_dvsecs(CXLType3Dev *ct3d) { CXLComponentState *cxl_cstate = &ct3d->cxl_cstate; uint8_t *dvsec; + uint32_t range1_size_hi, range1_size_lo, + range1_base_hi = 0, range1_base_lo = 0, + range2_size_hi = 0, range2_size_lo = 0, + range2_base_hi = 0, range2_base_lo = 0; + + /* + * Volatile memory is mapped as (0x0) + * Persistent memory is mapped at (volatile->size) + */ + if (ct3d->hostvmem) { + range1_size_hi = ct3d->hostvmem->size >> 32; + range1_size_lo = (2 << 5) | (2 << 2) | 0x3 | + (ct3d->hostvmem->size & 0xF0000000); + if (ct3d->hostpmem) { + range2_size_hi = ct3d->hostpmem->size >> 32; + range2_size_lo = (2 << 5) | (2 << 2) | 0x3 | + (ct3d->hostpmem->size & 0xF0000000); + } + } else { + range1_size_hi = ct3d->hostpmem->size >> 32; + range1_size_lo = (2 << 5) | (2 << 2) | 0x3 | + (ct3d->hostpmem->size & 0xF0000000); + } dvsec = (uint8_t *)&(CXLDVSECDevice){ .cap = 0x1e, .ctrl = 0x2, .status2 = 0x2, - .range1_size_hi = ct3d->hostmem->size >> 32, - .range1_size_lo = (2 << 5) | (2 << 2) | 0x3 | - (ct3d->hostmem->size & 0xF0000000), - .range1_base_hi = 0, - .range1_base_lo = 0, + .range1_size_hi = range1_size_hi, + .range1_size_lo = range1_size_lo, + .range1_base_hi = range1_base_hi, + .range1_base_lo = range1_base_lo, + .range2_size_hi = range2_size_hi, + .range2_size_lo = range2_size_lo, + .range2_base_hi = range2_base_hi, + .range2_base_lo = range2_base_lo, }; cxl_component_create_dvsec(cxl_cstate, CXL2_TYPE3_DEVICE, PCIE_CXL_DEVICE_DVSEC_LENGTH, PCIE_CXL_DEVICE_DVSEC, - PCIE_CXL2_DEVICE_DVSEC_REVID, dvsec); + PCIE_CXL31_DEVICE_DVSEC_REVID, dvsec); dvsec = (uint8_t *)&(CXLDVSECRegisterLocator){ .rsvd = 0, @@ -49,22 +336,109 @@ static void build_dvsecs(CXLType3Dev *ct3d) .phase2_power = 0x33, /* 0x33 miliwatts */ }; cxl_component_create_dvsec(cxl_cstate, CXL2_TYPE3_DEVICE, - GPF_DEVICE_DVSEC_LENGTH, GPF_PORT_DVSEC, + GPF_DEVICE_DVSEC_LENGTH, GPF_DEVICE_DVSEC, GPF_DEVICE_DVSEC_REVID, dvsec); + + dvsec = (uint8_t *)&(CXLDVSECPortFlexBus){ + .cap = 0x26, /* 68B, IO, Mem, non-MLD */ + .ctrl = 0x02, /* IO always enabled */ + .status = 0x26, /* same as capabilities */ + .rcvd_mod_ts_data_phase1 = 0xef, /* WTF? */ + }; + cxl_component_create_dvsec(cxl_cstate, CXL2_TYPE3_DEVICE, + PCIE_CXL3_FLEXBUS_PORT_DVSEC_LENGTH, + PCIE_FLEXBUS_PORT_DVSEC, + PCIE_CXL3_FLEXBUS_PORT_DVSEC_REVID, dvsec); } static void hdm_decoder_commit(CXLType3Dev *ct3d, int which) { + int hdm_inc = R_CXL_HDM_DECODER1_BASE_LO - R_CXL_HDM_DECODER0_BASE_LO; ComponentRegisters *cregs = &ct3d->cxl_cstate.crb; uint32_t *cache_mem = cregs->cache_mem_registers; + uint32_t ctrl; - assert(which == 0); - + ctrl = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL + which * hdm_inc); /* TODO: Sanity checks that the decoder is possible */ - ARRAY_FIELD_DP32(cache_mem, CXL_HDM_DECODER0_CTRL, COMMIT, 0); - ARRAY_FIELD_DP32(cache_mem, CXL_HDM_DECODER0_CTRL, ERR, 0); + ctrl = FIELD_DP32(ctrl, CXL_HDM_DECODER0_CTRL, ERR, 0); + ctrl = FIELD_DP32(ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED, 1); - ARRAY_FIELD_DP32(cache_mem, CXL_HDM_DECODER0_CTRL, COMMITTED, 1); + stl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL + which * hdm_inc, ctrl); +} + +static void hdm_decoder_uncommit(CXLType3Dev *ct3d, int which) +{ + int hdm_inc = R_CXL_HDM_DECODER1_BASE_LO - R_CXL_HDM_DECODER0_BASE_LO; + ComponentRegisters *cregs = &ct3d->cxl_cstate.crb; + uint32_t *cache_mem = cregs->cache_mem_registers; + uint32_t ctrl; + + ctrl = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL + which * hdm_inc); + + ctrl = FIELD_DP32(ctrl, CXL_HDM_DECODER0_CTRL, ERR, 0); + ctrl = FIELD_DP32(ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED, 0); + + stl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL + which * hdm_inc, ctrl); +} + +static int ct3d_qmp_uncor_err_to_cxl(CxlUncorErrorType qmp_err) +{ + switch (qmp_err) { + case CXL_UNCOR_ERROR_TYPE_CACHE_DATA_PARITY: + return CXL_RAS_UNC_ERR_CACHE_DATA_PARITY; + case CXL_UNCOR_ERROR_TYPE_CACHE_ADDRESS_PARITY: + return CXL_RAS_UNC_ERR_CACHE_ADDRESS_PARITY; + case CXL_UNCOR_ERROR_TYPE_CACHE_BE_PARITY: + return CXL_RAS_UNC_ERR_CACHE_BE_PARITY; + case CXL_UNCOR_ERROR_TYPE_CACHE_DATA_ECC: + return CXL_RAS_UNC_ERR_CACHE_DATA_ECC; + case CXL_UNCOR_ERROR_TYPE_MEM_DATA_PARITY: + return CXL_RAS_UNC_ERR_MEM_DATA_PARITY; + case CXL_UNCOR_ERROR_TYPE_MEM_ADDRESS_PARITY: + return CXL_RAS_UNC_ERR_MEM_ADDRESS_PARITY; + case CXL_UNCOR_ERROR_TYPE_MEM_BE_PARITY: + return CXL_RAS_UNC_ERR_MEM_BE_PARITY; + case CXL_UNCOR_ERROR_TYPE_MEM_DATA_ECC: + return CXL_RAS_UNC_ERR_MEM_DATA_ECC; + case CXL_UNCOR_ERROR_TYPE_REINIT_THRESHOLD: + return CXL_RAS_UNC_ERR_REINIT_THRESHOLD; + case CXL_UNCOR_ERROR_TYPE_RSVD_ENCODING: + return CXL_RAS_UNC_ERR_RSVD_ENCODING; + case CXL_UNCOR_ERROR_TYPE_POISON_RECEIVED: + return CXL_RAS_UNC_ERR_POISON_RECEIVED; + case CXL_UNCOR_ERROR_TYPE_RECEIVER_OVERFLOW: + return CXL_RAS_UNC_ERR_RECEIVER_OVERFLOW; + case CXL_UNCOR_ERROR_TYPE_INTERNAL: + return CXL_RAS_UNC_ERR_INTERNAL; + case CXL_UNCOR_ERROR_TYPE_CXL_IDE_TX: + return CXL_RAS_UNC_ERR_CXL_IDE_TX; + case CXL_UNCOR_ERROR_TYPE_CXL_IDE_RX: + return CXL_RAS_UNC_ERR_CXL_IDE_RX; + default: + return -EINVAL; + } +} + +static int ct3d_qmp_cor_err_to_cxl(CxlCorErrorType qmp_err) +{ + switch (qmp_err) { + case CXL_COR_ERROR_TYPE_CACHE_DATA_ECC: + return CXL_RAS_COR_ERR_CACHE_DATA_ECC; + case CXL_COR_ERROR_TYPE_MEM_DATA_ECC: + return CXL_RAS_COR_ERR_MEM_DATA_ECC; + case CXL_COR_ERROR_TYPE_CRC_THRESHOLD: + return CXL_RAS_COR_ERR_CRC_THRESHOLD; + case CXL_COR_ERROR_TYPE_RETRY_THRESHOLD: + return CXL_RAS_COR_ERR_RETRY_THRESHOLD; + case CXL_COR_ERROR_TYPE_CACHE_POISON_RECEIVED: + return CXL_RAS_COR_ERR_CACHE_POISON_RECEIVED; + case CXL_COR_ERROR_TYPE_MEM_POISON_RECEIVED: + return CXL_RAS_COR_ERR_MEM_POISON_RECEIVED; + case CXL_COR_ERROR_TYPE_PHYSICAL: + return CXL_RAS_COR_ERR_PHYSICAL; + default: + return -EINVAL; + } } static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value, @@ -75,6 +449,7 @@ static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value, CXLType3Dev *ct3d = container_of(cxl_cstate, CXLType3Dev, cxl_cstate); uint32_t *cache_mem = cregs->cache_mem_registers; bool should_commit = false; + bool should_uncommit = false; int which_hdm = -1; assert(size == 4); @@ -83,8 +458,103 @@ static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value, switch (offset) { case A_CXL_HDM_DECODER0_CTRL: should_commit = FIELD_EX32(value, CXL_HDM_DECODER0_CTRL, COMMIT); + should_uncommit = !should_commit; which_hdm = 0; break; + case A_CXL_HDM_DECODER1_CTRL: + should_commit = FIELD_EX32(value, CXL_HDM_DECODER0_CTRL, COMMIT); + should_uncommit = !should_commit; + which_hdm = 1; + break; + case A_CXL_HDM_DECODER2_CTRL: + should_commit = FIELD_EX32(value, CXL_HDM_DECODER0_CTRL, COMMIT); + should_uncommit = !should_commit; + which_hdm = 2; + break; + case A_CXL_HDM_DECODER3_CTRL: + should_commit = FIELD_EX32(value, CXL_HDM_DECODER0_CTRL, COMMIT); + should_uncommit = !should_commit; + which_hdm = 3; + break; + case A_CXL_RAS_UNC_ERR_STATUS: + { + uint32_t capctrl = ldl_le_p(cache_mem + R_CXL_RAS_ERR_CAP_CTRL); + uint32_t fe = FIELD_EX32(capctrl, CXL_RAS_ERR_CAP_CTRL, + FIRST_ERROR_POINTER); + CXLError *cxl_err; + uint32_t unc_err; + + /* + * If single bit written that corresponds to the first error + * pointer being cleared, update the status and header log. + */ + if (!QTAILQ_EMPTY(&ct3d->error_list)) { + if ((1 << fe) ^ value) { + CXLError *cxl_next; + /* + * Software is using wrong flow for multiple header recording + * Following behavior in PCIe r6.0 and assuming multiple + * header support. Implementation defined choice to clear all + * matching records if more than one bit set - which corresponds + * closest to behavior of hardware not capable of multiple + * header recording. + */ + QTAILQ_FOREACH_SAFE(cxl_err, &ct3d->error_list, node, + cxl_next) { + if ((1 << cxl_err->type) & value) { + QTAILQ_REMOVE(&ct3d->error_list, cxl_err, node); + g_free(cxl_err); + } + } + } else { + /* Done with previous FE, so drop from list */ + cxl_err = QTAILQ_FIRST(&ct3d->error_list); + QTAILQ_REMOVE(&ct3d->error_list, cxl_err, node); + g_free(cxl_err); + } + + /* + * If there is another FE, then put that in place and update + * the header log + */ + if (!QTAILQ_EMPTY(&ct3d->error_list)) { + uint32_t *header_log = &cache_mem[R_CXL_RAS_ERR_HEADER0]; + int i; + + cxl_err = QTAILQ_FIRST(&ct3d->error_list); + for (i = 0; i < CXL_RAS_ERR_HEADER_NUM; i++) { + stl_le_p(header_log + i, cxl_err->header[i]); + } + capctrl = FIELD_DP32(capctrl, CXL_RAS_ERR_CAP_CTRL, + FIRST_ERROR_POINTER, cxl_err->type); + } else { + /* + * If no more errors, then follow recommendation of PCI spec + * r6.0 6.2.4.2 to set the first error pointer to a status + * bit that will never be used. + */ + capctrl = FIELD_DP32(capctrl, CXL_RAS_ERR_CAP_CTRL, + FIRST_ERROR_POINTER, + CXL_RAS_UNC_ERR_CXL_UNUSED); + } + stl_le_p((uint8_t *)cache_mem + A_CXL_RAS_ERR_CAP_CTRL, capctrl); + } + unc_err = 0; + QTAILQ_FOREACH(cxl_err, &ct3d->error_list, node) { + unc_err |= 1 << cxl_err->type; + } + stl_le_p((uint8_t *)cache_mem + offset, unc_err); + + return; + } + case A_CXL_RAS_COR_ERR_STATUS: + { + uint32_t rw1c = value; + uint32_t temp = ldl_le_p((uint8_t *)cache_mem + offset); + temp &= ~rw1c; + stl_le_p((uint8_t *)cache_mem + offset, temp); + return; + } default: break; } @@ -92,64 +562,113 @@ static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value, stl_le_p((uint8_t *)cache_mem + offset, value); if (should_commit) { hdm_decoder_commit(ct3d, which_hdm); + } else if (should_uncommit) { + hdm_decoder_uncommit(ct3d, which_hdm); } } static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) { DeviceState *ds = DEVICE(ct3d); - MemoryRegion *mr; - char *name; - if (!ct3d->hostmem) { - error_setg(errp, "memdev property must be set"); + if (!ct3d->hostmem && !ct3d->hostvmem && !ct3d->hostpmem) { + error_setg(errp, "at least one memdev property must be set"); + return false; + } else if (ct3d->hostmem && ct3d->hostpmem) { + error_setg(errp, "[memdev] cannot be used with new " + "[persistent-memdev] property"); + return false; + } else if (ct3d->hostmem) { + /* Use of hostmem property implies pmem */ + ct3d->hostpmem = ct3d->hostmem; + ct3d->hostmem = NULL; + } + + if (ct3d->hostpmem && !ct3d->lsa) { + error_setg(errp, "lsa property must be set for persistent devices"); return false; } - mr = host_memory_backend_get_memory(ct3d->hostmem); - if (!mr) { - error_setg(errp, "memdev property must be set"); - return false; + if (ct3d->hostvmem) { + MemoryRegion *vmr; + char *v_name; + + vmr = host_memory_backend_get_memory(ct3d->hostvmem); + if (!vmr) { + error_setg(errp, "volatile memdev must have backing device"); + return false; + } + memory_region_set_nonvolatile(vmr, false); + memory_region_set_enabled(vmr, true); + host_memory_backend_set_mapped(ct3d->hostvmem, true); + if (ds->id) { + v_name = g_strdup_printf("cxl-type3-dpa-vmem-space:%s", ds->id); + } else { + v_name = g_strdup("cxl-type3-dpa-vmem-space"); + } + address_space_init(&ct3d->hostvmem_as, vmr, v_name); + ct3d->cxl_dstate.vmem_size = memory_region_size(vmr); + ct3d->cxl_dstate.mem_size += memory_region_size(vmr); + g_free(v_name); } - memory_region_set_nonvolatile(mr, true); - memory_region_set_enabled(mr, true); - host_memory_backend_set_mapped(ct3d->hostmem, true); - if (ds->id) { - name = g_strdup_printf("cxl-type3-dpa-space:%s", ds->id); - } else { - name = g_strdup("cxl-type3-dpa-space"); - } - address_space_init(&ct3d->hostmem_as, mr, name); - g_free(name); + if (ct3d->hostpmem) { + MemoryRegion *pmr; + char *p_name; - ct3d->cxl_dstate.pmem_size = ct3d->hostmem->size; - - if (!ct3d->lsa) { - error_setg(errp, "lsa property must be set"); - return false; + pmr = host_memory_backend_get_memory(ct3d->hostpmem); + if (!pmr) { + error_setg(errp, "persistent memdev must have backing device"); + return false; + } + memory_region_set_nonvolatile(pmr, true); + memory_region_set_enabled(pmr, true); + host_memory_backend_set_mapped(ct3d->hostpmem, true); + if (ds->id) { + p_name = g_strdup_printf("cxl-type3-dpa-pmem-space:%s", ds->id); + } else { + p_name = g_strdup("cxl-type3-dpa-pmem-space"); + } + address_space_init(&ct3d->hostpmem_as, pmr, p_name); + ct3d->cxl_dstate.pmem_size = memory_region_size(pmr); + ct3d->cxl_dstate.mem_size += memory_region_size(pmr); + g_free(p_name); } return true; } +static DOEProtocol doe_cdat_prot[] = { + { CXL_VENDOR_ID, CXL_DOE_TABLE_ACCESS, cxl_doe_cdat_rsp }, + { } +}; + static void ct3_realize(PCIDevice *pci_dev, Error **errp) { + ERRP_GUARD(); CXLType3Dev *ct3d = CXL_TYPE3(pci_dev); CXLComponentState *cxl_cstate = &ct3d->cxl_cstate; ComponentRegisters *regs = &cxl_cstate->crb; MemoryRegion *mr = ®s->component_registers; uint8_t *pci_conf = pci_dev->config; + unsigned short msix_num = 6; + int i, rc; + + QTAILQ_INIT(&ct3d->error_list); if (!cxl_setup_memory(ct3d, errp)) { return; } pci_config_set_prog_interface(pci_conf, 0x10); - pci_config_set_class(pci_conf, PCI_CLASS_MEMORY_CXL); pcie_endpoint_cap_init(pci_dev, 0x80); - cxl_cstate->dvsec_offset = 0x100; + if (ct3d->sn != UI64_NULL) { + pcie_dev_ser_num_init(pci_dev, 0x100, ct3d->sn); + cxl_cstate->dvsec_offset = 0x100 + 0x0c; + } else { + cxl_cstate->dvsec_offset = 0x100; + } ct3d->cxl_cstate.pdev = pci_dev; build_dvsecs(ct3d); @@ -164,11 +683,55 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) pci_dev, CXL_COMPONENT_REG_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, mr); - cxl_device_register_block_init(OBJECT(pci_dev), &ct3d->cxl_dstate); + cxl_device_register_block_init(OBJECT(pci_dev), &ct3d->cxl_dstate, + &ct3d->cci); pci_register_bar(pci_dev, CXL_DEVICE_REG_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, &ct3d->cxl_dstate.device_registers); + + /* MSI(-X) Initialization */ + rc = msix_init_exclusive_bar(pci_dev, msix_num, 4, NULL); + if (rc) { + goto err_address_space_free; + } + for (i = 0; i < msix_num; i++) { + msix_vector_use(pci_dev, i); + } + + /* DOE Initialization */ + pcie_doe_init(pci_dev, &ct3d->doe_cdat, 0x190, doe_cdat_prot, true, 0); + + cxl_cstate->cdat.build_cdat_table = ct3_build_cdat_table; + cxl_cstate->cdat.free_cdat_table = ct3_free_cdat_table; + cxl_cstate->cdat.private = ct3d; + cxl_doe_cdat_init(cxl_cstate, errp); + if (*errp) { + goto err_free_special_ops; + } + + pcie_cap_deverr_init(pci_dev); + /* Leave a bit of room for expansion */ + rc = pcie_aer_init(pci_dev, PCI_ERR_VER, 0x200, PCI_ERR_SIZEOF, NULL); + if (rc) { + goto err_release_cdat; + } + cxl_event_init(&ct3d->cxl_dstate, 2); + + return; + +err_release_cdat: + cxl_doe_cdat_release(cxl_cstate); +err_free_special_ops: + g_free(regs->special_ops); +err_address_space_free: + if (ct3d->hostpmem) { + address_space_destroy(&ct3d->hostpmem_as); + } + if (ct3d->hostvmem) { + address_space_destroy(&ct3d->hostvmem_as); + } + return; } static void ct3_exit(PCIDevice *pci_dev) @@ -177,87 +740,163 @@ static void ct3_exit(PCIDevice *pci_dev) CXLComponentState *cxl_cstate = &ct3d->cxl_cstate; ComponentRegisters *regs = &cxl_cstate->crb; + pcie_aer_exit(pci_dev); + cxl_doe_cdat_release(cxl_cstate); g_free(regs->special_ops); - address_space_destroy(&ct3d->hostmem_as); + if (ct3d->hostpmem) { + address_space_destroy(&ct3d->hostpmem_as); + } + if (ct3d->hostvmem) { + address_space_destroy(&ct3d->hostvmem_as); + } } -/* TODO: Support multiple HDM decoders and DPA skip */ static bool cxl_type3_dpa(CXLType3Dev *ct3d, hwaddr host_addr, uint64_t *dpa) { + int hdm_inc = R_CXL_HDM_DECODER1_BASE_LO - R_CXL_HDM_DECODER0_BASE_LO; uint32_t *cache_mem = ct3d->cxl_cstate.crb.cache_mem_registers; - uint64_t decoder_base, decoder_size, hpa_offset; - uint32_t hdm0_ctrl; - int ig, iw; + unsigned int hdm_count; + uint32_t cap; + uint64_t dpa_base = 0; + int i; - decoder_base = (((uint64_t)cache_mem[R_CXL_HDM_DECODER0_BASE_HI] << 32) | - cache_mem[R_CXL_HDM_DECODER0_BASE_LO]); - if ((uint64_t)host_addr < decoder_base) { - return false; + cap = ldl_le_p(cache_mem + R_CXL_HDM_DECODER_CAPABILITY); + hdm_count = cxl_decoder_count_dec(FIELD_EX32(cap, + CXL_HDM_DECODER_CAPABILITY, + DECODER_COUNT)); + + for (i = 0; i < hdm_count; i++) { + uint64_t decoder_base, decoder_size, hpa_offset, skip; + uint32_t hdm_ctrl, low, high; + int ig, iw; + + low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_BASE_LO + i * hdm_inc); + high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_BASE_HI + i * hdm_inc); + decoder_base = ((uint64_t)high << 32) | (low & 0xf0000000); + + low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_LO + i * hdm_inc); + high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_HI + i * hdm_inc); + decoder_size = ((uint64_t)high << 32) | (low & 0xf0000000); + + low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_DPA_SKIP_LO + + i * hdm_inc); + high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_DPA_SKIP_HI + + i * hdm_inc); + skip = ((uint64_t)high << 32) | (low & 0xf0000000); + dpa_base += skip; + + hpa_offset = (uint64_t)host_addr - decoder_base; + + hdm_ctrl = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL + i * hdm_inc); + iw = FIELD_EX32(hdm_ctrl, CXL_HDM_DECODER0_CTRL, IW); + ig = FIELD_EX32(hdm_ctrl, CXL_HDM_DECODER0_CTRL, IG); + if (!FIELD_EX32(hdm_ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED)) { + return false; + } + if (((uint64_t)host_addr < decoder_base) || + (hpa_offset >= decoder_size)) { + int decoded_iw = cxl_interleave_ways_dec(iw, &error_fatal); + + if (decoded_iw == 0) { + return false; + } + + dpa_base += decoder_size / decoded_iw; + continue; + } + + *dpa = dpa_base + + ((MAKE_64BIT_MASK(0, 8 + ig) & hpa_offset) | + ((MAKE_64BIT_MASK(8 + ig + iw, 64 - 8 - ig - iw) & hpa_offset) + >> iw)); + + return true; + } + return false; +} + +static int cxl_type3_hpa_to_as_and_dpa(CXLType3Dev *ct3d, + hwaddr host_addr, + unsigned int size, + AddressSpace **as, + uint64_t *dpa_offset) +{ + MemoryRegion *vmr = NULL, *pmr = NULL; + + if (ct3d->hostvmem) { + vmr = host_memory_backend_get_memory(ct3d->hostvmem); + } + if (ct3d->hostpmem) { + pmr = host_memory_backend_get_memory(ct3d->hostpmem); } - hpa_offset = (uint64_t)host_addr - decoder_base; - - decoder_size = ((uint64_t)cache_mem[R_CXL_HDM_DECODER0_SIZE_HI] << 32) | - cache_mem[R_CXL_HDM_DECODER0_SIZE_LO]; - if (hpa_offset >= decoder_size) { - return false; + if (!vmr && !pmr) { + return -ENODEV; } - hdm0_ctrl = cache_mem[R_CXL_HDM_DECODER0_CTRL]; - iw = FIELD_EX32(hdm0_ctrl, CXL_HDM_DECODER0_CTRL, IW); - ig = FIELD_EX32(hdm0_ctrl, CXL_HDM_DECODER0_CTRL, IG); + if (!cxl_type3_dpa(ct3d, host_addr, dpa_offset)) { + return -EINVAL; + } - *dpa = (MAKE_64BIT_MASK(0, 8 + ig) & hpa_offset) | - ((MAKE_64BIT_MASK(8 + ig + iw, 64 - 8 - ig - iw) & hpa_offset) >> iw); + if (*dpa_offset > ct3d->cxl_dstate.mem_size) { + return -EINVAL; + } - return true; + if (vmr) { + if (*dpa_offset < memory_region_size(vmr)) { + *as = &ct3d->hostvmem_as; + } else { + *as = &ct3d->hostpmem_as; + *dpa_offset -= memory_region_size(vmr); + } + } else { + *as = &ct3d->hostpmem_as; + } + + return 0; } MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data, unsigned size, MemTxAttrs attrs) { CXLType3Dev *ct3d = CXL_TYPE3(d); - uint64_t dpa_offset; - MemoryRegion *mr; + uint64_t dpa_offset = 0; + AddressSpace *as = NULL; + int res; - /* TODO support volatile region */ - mr = host_memory_backend_get_memory(ct3d->hostmem); - if (!mr) { + res = cxl_type3_hpa_to_as_and_dpa(ct3d, host_addr, size, + &as, &dpa_offset); + if (res) { return MEMTX_ERROR; } - if (!cxl_type3_dpa(ct3d, host_addr, &dpa_offset)) { - return MEMTX_ERROR; + if (sanitize_running(&ct3d->cci)) { + qemu_guest_getrandom_nofail(data, size); + return MEMTX_OK; } - if (dpa_offset > int128_get64(mr->size)) { - return MEMTX_ERROR; - } - - return address_space_read(&ct3d->hostmem_as, dpa_offset, attrs, data, size); + return address_space_read(as, dpa_offset, attrs, data, size); } MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data, unsigned size, MemTxAttrs attrs) { CXLType3Dev *ct3d = CXL_TYPE3(d); - uint64_t dpa_offset; - MemoryRegion *mr; + uint64_t dpa_offset = 0; + AddressSpace *as = NULL; + int res; - mr = host_memory_backend_get_memory(ct3d->hostmem); - if (!mr) { + res = cxl_type3_hpa_to_as_and_dpa(ct3d, host_addr, size, + &as, &dpa_offset); + if (res) { + return MEMTX_ERROR; + } + + if (sanitize_running(&ct3d->cci)) { return MEMTX_OK; } - if (!cxl_type3_dpa(ct3d, host_addr, &dpa_offset)) { - return MEMTX_OK; - } - - if (dpa_offset > int128_get64(mr->size)) { - return MEMTX_OK; - } - return address_space_write(&ct3d->hostmem_as, dpa_offset, attrs, - &data, size); + return address_space_write(as, dpa_offset, attrs, &data, size); } static void ct3d_reset(DeviceState *dev) @@ -267,14 +906,31 @@ static void ct3d_reset(DeviceState *dev) uint32_t *write_msk = ct3d->cxl_cstate.crb.cache_mem_regs_write_mask; cxl_component_register_init_common(reg_state, write_msk, CXL2_TYPE3_DEVICE); - cxl_device_register_init_common(&ct3d->cxl_dstate); + cxl_device_register_init_t3(ct3d); + + /* + * Bring up an endpoint to target with MCTP over VDM. + * This device is emulating an MLD with single LD for now. + */ + cxl_initialize_t3_fm_owned_ld_mctpcci(&ct3d->vdm_fm_owned_ld_mctp_cci, + DEVICE(ct3d), DEVICE(ct3d), + 512); /* Max payload made up */ + cxl_initialize_t3_ld_cci(&ct3d->ld0_cci, DEVICE(ct3d), DEVICE(ct3d), + 512); /* Max payload made up */ + } static Property ct3_props[] = { DEFINE_PROP_LINK("memdev", CXLType3Dev, hostmem, TYPE_MEMORY_BACKEND, - HostMemoryBackend *), + HostMemoryBackend *), /* for backward compatibility */ + DEFINE_PROP_LINK("persistent-memdev", CXLType3Dev, hostpmem, + TYPE_MEMORY_BACKEND, HostMemoryBackend *), + DEFINE_PROP_LINK("volatile-memdev", CXLType3Dev, hostvmem, + TYPE_MEMORY_BACKEND, HostMemoryBackend *), DEFINE_PROP_LINK("lsa", CXLType3Dev, lsa, TYPE_MEMORY_BACKEND, HostMemoryBackend *), + DEFINE_PROP_UINT64("sn", CXLType3Dev, sn, UI64_NULL), + DEFINE_PROP_STRING("cdat", CXLType3Dev, cxl_cstate.cdat.filename), DEFINE_PROP_END_OF_LIST(), }; @@ -282,6 +938,10 @@ static uint64_t get_lsa_size(CXLType3Dev *ct3d) { MemoryRegion *mr; + if (!ct3d->lsa) { + return 0; + } + mr = host_memory_backend_get_memory(ct3d->lsa); return memory_region_size(mr); } @@ -299,6 +959,10 @@ static uint64_t get_lsa(CXLType3Dev *ct3d, void *buf, uint64_t size, MemoryRegion *mr; void *lsa; + if (!ct3d->lsa) { + return 0; + } + mr = host_memory_backend_get_memory(ct3d->lsa); validate_lsa_access(mr, size, offset); @@ -314,6 +978,10 @@ static void set_lsa(CXLType3Dev *ct3d, const void *buf, uint64_t size, MemoryRegion *mr; void *lsa; + if (!ct3d->lsa) { + return; + } + mr = host_memory_backend_get_memory(ct3d->lsa); validate_lsa_access(mr, size, offset); @@ -327,6 +995,531 @@ static void set_lsa(CXLType3Dev *ct3d, const void *buf, uint64_t size, */ } +static bool set_cacheline(CXLType3Dev *ct3d, uint64_t dpa_offset, uint8_t *data) +{ + MemoryRegion *vmr = NULL, *pmr = NULL; + AddressSpace *as; + + if (ct3d->hostvmem) { + vmr = host_memory_backend_get_memory(ct3d->hostvmem); + } + if (ct3d->hostpmem) { + pmr = host_memory_backend_get_memory(ct3d->hostpmem); + } + + if (!vmr && !pmr) { + return false; + } + + if (dpa_offset + CXL_CACHE_LINE_SIZE > ct3d->cxl_dstate.mem_size) { + return false; + } + + if (vmr) { + if (dpa_offset < memory_region_size(vmr)) { + as = &ct3d->hostvmem_as; + } else { + as = &ct3d->hostpmem_as; + dpa_offset -= memory_region_size(vmr); + } + } else { + as = &ct3d->hostpmem_as; + } + + address_space_write(as, dpa_offset, MEMTXATTRS_UNSPECIFIED, &data, + CXL_CACHE_LINE_SIZE); + return true; +} + +void cxl_set_poison_list_overflowed(CXLType3Dev *ct3d) +{ + ct3d->poison_list_overflowed = true; + ct3d->poison_list_overflow_ts = + cxl_device_get_timestamp(&ct3d->cxl_dstate); +} + +void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length, + Error **errp) +{ + Object *obj = object_resolve_path(path, NULL); + CXLType3Dev *ct3d; + CXLPoison *p; + + if (length % 64) { + error_setg(errp, "Poison injection must be in multiples of 64 bytes"); + return; + } + if (start % 64) { + error_setg(errp, "Poison start address must be 64 byte aligned"); + return; + } + if (!obj) { + error_setg(errp, "Unable to resolve path"); + return; + } + if (!object_dynamic_cast(obj, TYPE_CXL_TYPE3)) { + error_setg(errp, "Path does not point to a CXL type 3 device"); + return; + } + + ct3d = CXL_TYPE3(obj); + + QLIST_FOREACH(p, &ct3d->poison_list, node) { + if (((start >= p->start) && (start < p->start + p->length)) || + ((start + length > p->start) && + (start + length <= p->start + p->length))) { + error_setg(errp, + "Overlap with existing poisoned region not supported"); + return; + } + } + + if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) { + cxl_set_poison_list_overflowed(ct3d); + return; + } + + p = g_new0(CXLPoison, 1); + p->length = length; + p->start = start; + /* Different from injected via the mbox */ + p->type = CXL_POISON_TYPE_INTERNAL; + + QLIST_INSERT_HEAD(&ct3d->poison_list, p, node); + ct3d->poison_list_cnt++; +} + +/* For uncorrectable errors include support for multiple header recording */ +void qmp_cxl_inject_uncorrectable_errors(const char *path, + CXLUncorErrorRecordList *errors, + Error **errp) +{ + Object *obj = object_resolve_path(path, NULL); + static PCIEAERErr err = {}; + CXLType3Dev *ct3d; + CXLError *cxl_err; + uint32_t *reg_state; + uint32_t unc_err; + bool first; + + if (!obj) { + error_setg(errp, "Unable to resolve path"); + return; + } + + if (!object_dynamic_cast(obj, TYPE_CXL_TYPE3)) { + error_setg(errp, "Path does not point to a CXL type 3 device"); + return; + } + + err.status = PCI_ERR_UNC_INTN; + err.source_id = pci_requester_id(PCI_DEVICE(obj)); + err.flags = 0; + + ct3d = CXL_TYPE3(obj); + + first = QTAILQ_EMPTY(&ct3d->error_list); + reg_state = ct3d->cxl_cstate.crb.cache_mem_registers; + while (errors) { + uint32List *header = errors->value->header; + uint8_t header_count = 0; + int cxl_err_code; + + cxl_err_code = ct3d_qmp_uncor_err_to_cxl(errors->value->type); + if (cxl_err_code < 0) { + error_setg(errp, "Unknown error code"); + return; + } + + /* If the error is masked, nothing to do here */ + if (!((1 << cxl_err_code) & + ~ldl_le_p(reg_state + R_CXL_RAS_UNC_ERR_MASK))) { + errors = errors->next; + continue; + } + + cxl_err = g_malloc0(sizeof(*cxl_err)); + + cxl_err->type = cxl_err_code; + while (header && header_count < 32) { + cxl_err->header[header_count++] = header->value; + header = header->next; + } + if (header_count > 32) { + error_setg(errp, "Header must be 32 DWORD or less"); + return; + } + QTAILQ_INSERT_TAIL(&ct3d->error_list, cxl_err, node); + + errors = errors->next; + } + + if (first && !QTAILQ_EMPTY(&ct3d->error_list)) { + uint32_t *cache_mem = ct3d->cxl_cstate.crb.cache_mem_registers; + uint32_t capctrl = ldl_le_p(cache_mem + R_CXL_RAS_ERR_CAP_CTRL); + uint32_t *header_log = &cache_mem[R_CXL_RAS_ERR_HEADER0]; + int i; + + cxl_err = QTAILQ_FIRST(&ct3d->error_list); + for (i = 0; i < CXL_RAS_ERR_HEADER_NUM; i++) { + stl_le_p(header_log + i, cxl_err->header[i]); + } + + capctrl = FIELD_DP32(capctrl, CXL_RAS_ERR_CAP_CTRL, + FIRST_ERROR_POINTER, cxl_err->type); + stl_le_p(cache_mem + R_CXL_RAS_ERR_CAP_CTRL, capctrl); + } + + unc_err = 0; + QTAILQ_FOREACH(cxl_err, &ct3d->error_list, node) { + unc_err |= (1 << cxl_err->type); + } + if (!unc_err) { + return; + } + + stl_le_p(reg_state + R_CXL_RAS_UNC_ERR_STATUS, unc_err); + pcie_aer_inject_error(PCI_DEVICE(obj), &err); + + return; +} + +void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type, + Error **errp) +{ + static PCIEAERErr err = {}; + Object *obj = object_resolve_path(path, NULL); + CXLType3Dev *ct3d; + uint32_t *reg_state; + uint32_t cor_err; + int cxl_err_type; + + if (!obj) { + error_setg(errp, "Unable to resolve path"); + return; + } + if (!object_dynamic_cast(obj, TYPE_CXL_TYPE3)) { + error_setg(errp, "Path does not point to a CXL type 3 device"); + return; + } + + err.status = PCI_ERR_COR_INTERNAL; + err.source_id = pci_requester_id(PCI_DEVICE(obj)); + err.flags = PCIE_AER_ERR_IS_CORRECTABLE; + + ct3d = CXL_TYPE3(obj); + reg_state = ct3d->cxl_cstate.crb.cache_mem_registers; + cor_err = ldl_le_p(reg_state + R_CXL_RAS_COR_ERR_STATUS); + + cxl_err_type = ct3d_qmp_cor_err_to_cxl(type); + if (cxl_err_type < 0) { + error_setg(errp, "Invalid COR error"); + return; + } + /* If the error is masked, nothting to do here */ + if (!((1 << cxl_err_type) & + ~ldl_le_p(reg_state + R_CXL_RAS_COR_ERR_MASK))) { + return; + } + + cor_err |= (1 << cxl_err_type); + stl_le_p(reg_state + R_CXL_RAS_COR_ERR_STATUS, cor_err); + + pcie_aer_inject_error(PCI_DEVICE(obj), &err); +} + +static void cxl_assign_event_header(CXLEventRecordHdr *hdr, + const QemuUUID *uuid, uint32_t flags, + uint8_t length, uint64_t timestamp) +{ + st24_le_p(&hdr->flags, flags); + hdr->length = length; + memcpy(&hdr->id, uuid, sizeof(hdr->id)); + stq_le_p(&hdr->timestamp, timestamp); +} + +static const QemuUUID gen_media_uuid = { + .data = UUID(0xfbcd0a77, 0xc260, 0x417f, + 0x85, 0xa9, 0x08, 0x8b, 0x16, 0x21, 0xeb, 0xa6), +}; + +static const QemuUUID dram_uuid = { + .data = UUID(0x601dcbb3, 0x9c06, 0x4eab, 0xb8, 0xaf, + 0x4e, 0x9b, 0xfb, 0x5c, 0x96, 0x24), +}; + +static const QemuUUID memory_module_uuid = { + .data = UUID(0xfe927475, 0xdd59, 0x4339, 0xa5, 0x86, + 0x79, 0xba, 0xb1, 0x13, 0xb7, 0x74), +}; + +#define CXL_GMER_VALID_CHANNEL BIT(0) +#define CXL_GMER_VALID_RANK BIT(1) +#define CXL_GMER_VALID_DEVICE BIT(2) +#define CXL_GMER_VALID_COMPONENT BIT(3) + +static int ct3d_qmp_cxl_event_log_enc(CxlEventLog log) +{ + switch (log) { + case CXL_EVENT_LOG_INFORMATIONAL: + return CXL_EVENT_TYPE_INFO; + case CXL_EVENT_LOG_WARNING: + return CXL_EVENT_TYPE_WARN; + case CXL_EVENT_LOG_FAILURE: + return CXL_EVENT_TYPE_FAIL; + case CXL_EVENT_LOG_FATAL: + return CXL_EVENT_TYPE_FATAL; +/* DCD not yet supported */ + default: + return -EINVAL; + } +} +/* Component ID is device specific. Define this as a string. */ +void qmp_cxl_inject_general_media_event(const char *path, CxlEventLog log, + uint8_t flags, uint64_t dpa, + uint8_t descriptor, uint8_t type, + uint8_t transaction_type, + bool has_channel, uint8_t channel, + bool has_rank, uint8_t rank, + bool has_device, uint32_t device, + const char *component_id, + Error **errp) +{ + Object *obj = object_resolve_path(path, NULL); + CXLEventGenMedia gem; + CXLEventRecordHdr *hdr = &gem.hdr; + CXLDeviceState *cxlds; + CXLType3Dev *ct3d; + uint16_t valid_flags = 0; + uint8_t enc_log; + int rc; + + if (!obj) { + error_setg(errp, "Unable to resolve path"); + return; + } + if (!object_dynamic_cast(obj, TYPE_CXL_TYPE3)) { + error_setg(errp, "Path does not point to a CXL type 3 device"); + return; + } + ct3d = CXL_TYPE3(obj); + cxlds = &ct3d->cxl_dstate; + + rc = ct3d_qmp_cxl_event_log_enc(log); + if (rc < 0) { + error_setg(errp, "Unhandled error log type"); + return; + } + enc_log = rc; + + memset(&gem, 0, sizeof(gem)); + cxl_assign_event_header(hdr, &gen_media_uuid, flags, sizeof(gem), + cxl_device_get_timestamp(&ct3d->cxl_dstate)); + + stq_le_p(&gem.phys_addr, dpa); + gem.descriptor = descriptor; + gem.type = type; + gem.transaction_type = transaction_type; + + if (has_channel) { + gem.channel = channel; + valid_flags |= CXL_GMER_VALID_CHANNEL; + } + + if (has_rank) { + gem.rank = rank; + valid_flags |= CXL_GMER_VALID_RANK; + } + + if (has_device) { + st24_le_p(gem.device, device); + valid_flags |= CXL_GMER_VALID_DEVICE; + } + + if (component_id) { + strncpy((char *)gem.component_id, component_id, + sizeof(gem.component_id) - 1); + valid_flags |= CXL_GMER_VALID_COMPONENT; + } + + stw_le_p(&gem.validity_flags, valid_flags); + + if (cxl_event_insert(cxlds, enc_log, (CXLEventRecordRaw *)&gem)) { + cxl_event_irq_assert(ct3d); + } +} + +#define CXL_DRAM_VALID_CHANNEL BIT(0) +#define CXL_DRAM_VALID_RANK BIT(1) +#define CXL_DRAM_VALID_NIBBLE_MASK BIT(2) +#define CXL_DRAM_VALID_BANK_GROUP BIT(3) +#define CXL_DRAM_VALID_BANK BIT(4) +#define CXL_DRAM_VALID_ROW BIT(5) +#define CXL_DRAM_VALID_COLUMN BIT(6) +#define CXL_DRAM_VALID_CORRECTION_MASK BIT(7) + +void qmp_cxl_inject_dram_event(const char *path, CxlEventLog log, uint8_t flags, + uint64_t dpa, uint8_t descriptor, + uint8_t type, uint8_t transaction_type, + bool has_channel, uint8_t channel, + bool has_rank, uint8_t rank, + bool has_nibble_mask, uint32_t nibble_mask, + bool has_bank_group, uint8_t bank_group, + bool has_bank, uint8_t bank, + bool has_row, uint32_t row, + bool has_column, uint16_t column, + bool has_correction_mask, + uint64List *correction_mask, + Error **errp) +{ + Object *obj = object_resolve_path(path, NULL); + CXLEventDram dram; + CXLEventRecordHdr *hdr = &dram.hdr; + CXLDeviceState *cxlds; + CXLType3Dev *ct3d; + uint16_t valid_flags = 0; + uint8_t enc_log; + int rc; + + if (!obj) { + error_setg(errp, "Unable to resolve path"); + return; + } + if (!object_dynamic_cast(obj, TYPE_CXL_TYPE3)) { + error_setg(errp, "Path does not point to a CXL type 3 device"); + return; + } + ct3d = CXL_TYPE3(obj); + cxlds = &ct3d->cxl_dstate; + + rc = ct3d_qmp_cxl_event_log_enc(log); + if (rc < 0) { + error_setg(errp, "Unhandled error log type"); + return; + } + enc_log = rc; + + memset(&dram, 0, sizeof(dram)); + cxl_assign_event_header(hdr, &dram_uuid, flags, sizeof(dram), + cxl_device_get_timestamp(&ct3d->cxl_dstate)); + stq_le_p(&dram.phys_addr, dpa); + dram.descriptor = descriptor; + dram.type = type; + dram.transaction_type = transaction_type; + + if (has_channel) { + dram.channel = channel; + valid_flags |= CXL_DRAM_VALID_CHANNEL; + } + + if (has_rank) { + dram.rank = rank; + valid_flags |= CXL_DRAM_VALID_RANK; + } + + if (has_nibble_mask) { + st24_le_p(dram.nibble_mask, nibble_mask); + valid_flags |= CXL_DRAM_VALID_NIBBLE_MASK; + } + + if (has_bank_group) { + dram.bank_group = bank_group; + valid_flags |= CXL_DRAM_VALID_BANK_GROUP; + } + + if (has_bank) { + dram.bank = bank; + valid_flags |= CXL_DRAM_VALID_BANK; + } + + if (has_row) { + st24_le_p(dram.row, row); + valid_flags |= CXL_DRAM_VALID_ROW; + } + + if (has_column) { + stw_le_p(&dram.column, column); + valid_flags |= CXL_DRAM_VALID_COLUMN; + } + + if (has_correction_mask) { + int count = 0; + while (correction_mask && count < 4) { + stq_le_p(&dram.correction_mask[count], + correction_mask->value); + count++; + correction_mask = correction_mask->next; + } + valid_flags |= CXL_DRAM_VALID_CORRECTION_MASK; + } + + stw_le_p(&dram.validity_flags, valid_flags); + + if (cxl_event_insert(cxlds, enc_log, (CXLEventRecordRaw *)&dram)) { + cxl_event_irq_assert(ct3d); + } + return; +} + +void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, + uint8_t flags, uint8_t type, + uint8_t health_status, + uint8_t media_status, + uint8_t additional_status, + uint8_t life_used, + int16_t temperature, + uint32_t dirty_shutdown_count, + uint32_t corrected_volatile_error_count, + uint32_t corrected_persist_error_count, + Error **errp) +{ + Object *obj = object_resolve_path(path, NULL); + CXLEventMemoryModule module; + CXLEventRecordHdr *hdr = &module.hdr; + CXLDeviceState *cxlds; + CXLType3Dev *ct3d; + uint8_t enc_log; + int rc; + + if (!obj) { + error_setg(errp, "Unable to resolve path"); + return; + } + if (!object_dynamic_cast(obj, TYPE_CXL_TYPE3)) { + error_setg(errp, "Path does not point to a CXL type 3 device"); + return; + } + ct3d = CXL_TYPE3(obj); + cxlds = &ct3d->cxl_dstate; + + rc = ct3d_qmp_cxl_event_log_enc(log); + if (rc < 0) { + error_setg(errp, "Unhandled error log type"); + return; + } + enc_log = rc; + + memset(&module, 0, sizeof(module)); + cxl_assign_event_header(hdr, &memory_module_uuid, flags, sizeof(module), + cxl_device_get_timestamp(&ct3d->cxl_dstate)); + + module.type = type; + module.health_status = health_status; + module.media_status = media_status; + module.additional_status = additional_status; + module.life_used = life_used; + stw_le_p(&module.temperature, temperature); + stl_le_p(&module.dirty_shutdown_count, dirty_shutdown_count); + stl_le_p(&module.corrected_volatile_error_count, + corrected_volatile_error_count); + stl_le_p(&module.corrected_persistent_error_count, + corrected_persist_error_count); + + if (cxl_event_insert(cxlds, enc_log, (CXLEventRecordRaw *)&module)) { + cxl_event_irq_assert(ct3d); + } +} + static void ct3_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -335,19 +1528,23 @@ static void ct3_class_init(ObjectClass *oc, void *data) pc->realize = ct3_realize; pc->exit = ct3_exit; - pc->class_id = PCI_CLASS_STORAGE_EXPRESS; + pc->class_id = PCI_CLASS_MEMORY_CXL; pc->vendor_id = PCI_VENDOR_ID_INTEL; pc->device_id = 0xd93; /* LVF for now */ pc->revision = 1; + pc->config_write = ct3d_config_write; + pc->config_read = ct3d_config_read; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->desc = "CXL PMEM Device (Type 3)"; + dc->desc = "CXL Memory Device (Type 3)"; dc->reset = ct3d_reset; device_class_set_props(dc, ct3_props); cvc->get_lsa_size = get_lsa_size; cvc->get_lsa = get_lsa; cvc->set_lsa = set_lsa; + cvc->set_cacheline = set_cacheline; } static const TypeInfo ct3d_info = { diff --git a/hw/mem/cxl_type3_stubs.c b/hw/mem/cxl_type3_stubs.c new file mode 100644 index 0000000000..3e1851e32b --- /dev/null +++ b/hw/mem/cxl_type3_stubs.c @@ -0,0 +1,69 @@ +/* + * CXL Type 3 (memory expander) device QMP stubs + * + * Copyright(C) 2020 Intel Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2. See the + * COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-v2-only + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-cxl.h" + +void qmp_cxl_inject_general_media_event(const char *path, CxlEventLog log, + uint8_t flags, uint64_t dpa, + uint8_t descriptor, uint8_t type, + uint8_t transaction_type, + bool has_channel, uint8_t channel, + bool has_rank, uint8_t rank, + bool has_device, uint32_t device, + const char *component_id, + Error **errp) {} + +void qmp_cxl_inject_dram_event(const char *path, CxlEventLog log, uint8_t flags, + uint64_t dpa, uint8_t descriptor, + uint8_t type, uint8_t transaction_type, + bool has_channel, uint8_t channel, + bool has_rank, uint8_t rank, + bool has_nibble_mask, uint32_t nibble_mask, + bool has_bank_group, uint8_t bank_group, + bool has_bank, uint8_t bank, + bool has_row, uint32_t row, + bool has_column, uint16_t column, + bool has_correction_mask, + uint64List *correction_mask, + Error **errp) {} + +void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, + uint8_t flags, uint8_t type, + uint8_t health_status, + uint8_t media_status, + uint8_t additional_status, + uint8_t life_used, + int16_t temperature, + uint32_t dirty_shutdown_count, + uint32_t corrected_volatile_error_count, + uint32_t corrected_persist_error_count, + Error **errp) {} + +void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length, + Error **errp) +{ + error_setg(errp, "CXL Type 3 support is not compiled in"); +} + +void qmp_cxl_inject_uncorrectable_errors(const char *path, + CXLUncorErrorRecordList *errors, + Error **errp) +{ + error_setg(errp, "CXL Type 3 support is not compiled in"); +} + +void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type, + Error **errp) +{ + error_setg(errp, "CXL Type 3 support is not compiled in"); +} diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index d9f8301711..e098585cda 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -10,14 +10,32 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "hw/mem/memory-device.h" #include "qapi/error.h" #include "hw/boards.h" #include "qemu/range.h" #include "hw/virtio/vhost.h" #include "sysemu/kvm.h" +#include "exec/address-spaces.h" #include "trace.h" +static bool memory_device_is_empty(const MemoryDeviceState *md) +{ + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); + Error *local_err = NULL; + MemoryRegion *mr; + + /* dropping const here is fine as we don't touch the memory region */ + mr = mdc->get_memory_region((MemoryDeviceState *)md, &local_err); + if (local_err) { + /* Not empty, we'll report errors later when containing the MR again. */ + error_free(local_err); + return false; + } + return !mr; +} + static gint memory_device_addr_sort(gconstpointer a, gconstpointer b) { const MemoryDeviceState *md_a = MEMORY_DEVICE(a); @@ -50,40 +68,139 @@ static int memory_device_build_list(Object *obj, void *opaque) return 0; } -static int memory_device_used_region_size(Object *obj, void *opaque) +static unsigned int memory_device_get_memslots(MemoryDeviceState *md) { - uint64_t *size = opaque; + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); - if (object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) { - const DeviceState *dev = DEVICE(obj); - const MemoryDeviceState *md = MEMORY_DEVICE(obj); - - if (dev->realized) { - *size += memory_device_get_region_size(md, &error_abort); - } + if (mdc->get_memslots) { + return mdc->get_memslots(md); } - - object_child_foreach(obj, memory_device_used_region_size, opaque); - return 0; + return 1; } -static void memory_device_check_addable(MachineState *ms, uint64_t size, - Error **errp) +/* + * Memslots that are reserved by memory devices (required but still reported + * as free from KVM / vhost). + */ +static unsigned int get_reserved_memslots(MachineState *ms) { - uint64_t used_region_size = 0; + if (ms->device_memory->used_memslots > + ms->device_memory->required_memslots) { + /* This is unexpected, and we warned already in the memory notifier. */ + return 0; + } + return ms->device_memory->required_memslots - + ms->device_memory->used_memslots; +} - /* we will need a new memory slot for kvm and vhost */ - if (kvm_enabled() && !kvm_has_free_slot(ms)) { - error_setg(errp, "hypervisor has no free memory slots left"); +unsigned int memory_devices_get_reserved_memslots(void) +{ + if (!current_machine->device_memory) { + return 0; + } + return get_reserved_memslots(current_machine); +} + +bool memory_devices_memslot_auto_decision_active(void) +{ + if (!current_machine->device_memory) { + return false; + } + + return current_machine->device_memory->memslot_auto_decision_active; +} + +static unsigned int memory_device_memslot_decision_limit(MachineState *ms, + MemoryRegion *mr) +{ + const unsigned int reserved = get_reserved_memslots(ms); + const uint64_t size = memory_region_size(mr); + unsigned int max = vhost_get_max_memslots(); + unsigned int free = vhost_get_free_memslots(); + uint64_t available_space; + unsigned int memslots; + + if (kvm_enabled()) { + max = MIN(max, kvm_get_max_memslots()); + free = MIN(free, kvm_get_free_memslots()); + } + + /* + * If we only have less overall memslots than what we consider reasonable, + * just keep it to a minimum. + */ + if (max < MEMORY_DEVICES_SAFE_MAX_MEMSLOTS) { + return 1; + } + + /* + * Consider our soft-limit across all memory devices. We don't really + * expect to exceed this limit in reasonable configurations. + */ + if (MEMORY_DEVICES_SOFT_MEMSLOT_LIMIT <= + ms->device_memory->required_memslots) { + return 1; + } + memslots = MEMORY_DEVICES_SOFT_MEMSLOT_LIMIT - + ms->device_memory->required_memslots; + + /* + * Consider the actually still free memslots. This is only relevant if + * other memslot consumers would consume *significantly* more memslots than + * what we prepared for (> 253). Unlikely, but let's just handle it + * cleanly. + */ + memslots = MIN(memslots, free - reserved); + if (memslots < 1 || unlikely(free < reserved)) { + return 1; + } + + /* We cannot have any other memory devices? So give all to this device. */ + if (size == ms->maxram_size - ms->ram_size) { + return memslots; + } + + /* + * Simple heuristic: equally distribute the memslots over the space + * still available for memory devices. + */ + available_space = ms->maxram_size - ms->ram_size - + ms->device_memory->used_region_size; + memslots = (double)memslots * size / available_space; + return memslots < 1 ? 1 : memslots; +} + +static void memory_device_check_addable(MachineState *ms, MemoryDeviceState *md, + MemoryRegion *mr, Error **errp) +{ + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); + const uint64_t used_region_size = ms->device_memory->used_region_size; + const uint64_t size = memory_region_size(mr); + const unsigned int reserved_memslots = get_reserved_memslots(ms); + unsigned int required_memslots, memslot_limit; + + /* + * Instruct the device to decide how many memslots to use, if applicable, + * before we query the number of required memslots the first time. + */ + if (mdc->decide_memslots) { + memslot_limit = memory_device_memslot_decision_limit(ms, mr); + mdc->decide_memslots(md, memslot_limit); + } + required_memslots = memory_device_get_memslots(md); + + /* we will need memory slots for kvm and vhost */ + if (kvm_enabled() && + kvm_get_free_memslots() < required_memslots + reserved_memslots) { + error_setg(errp, "hypervisor has not enough free memory slots left"); return; } - if (!vhost_has_free_slot()) { - error_setg(errp, "a used vhost backend has no free memory slots left"); + if (vhost_get_free_memslots() < required_memslots + reserved_memslots) { + error_setg(errp, "a used vhost backend has not enough free memory slots left"); return; } /* will we exceed the total amount of memory specified */ - memory_device_used_region_size(OBJECT(ms), &used_region_size); if (used_region_size + size < used_region_size || used_region_size + size > ms->maxram_size - ms->ram_size) { error_setg(errp, "not enough space, currently 0x%" PRIx64 @@ -99,21 +216,9 @@ 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; - if (!ms->device_memory) { - error_setg(errp, "memory devices (e.g. for memory hotplug) are not " - "supported by the machine"); - return 0; - } - - if (!memory_region_size(&ms->device_memory->mr)) { - error_setg(errp, "memory devices (e.g. for memory hotplug) are not " - "enabled, please specify the maxmem option"); - return 0; - } range_init_nofail(&as, ms->device_memory->base, memory_region_size(&ms->device_memory->mr)); @@ -125,24 +230,12 @@ static uint64_t memory_device_get_free_addr(MachineState *ms, align); } - memory_device_check_addable(ms, size, &err); - if (err) { - error_propagate(errp, err); - return 0; - } - if (hint && !QEMU_IS_ALIGNED(*hint, align)) { error_setg(errp, "address must be aligned to 0x%" PRIx64 " bytes", align); return 0; } - if (!QEMU_IS_ALIGNED(size, align)) { - error_setg(errp, "backend memory size must be multiple of 0x%" - PRIx64, align); - return 0; - } - if (hint) { if (range_init(&new, *hint, size) || !range_contains_range(&as, &new)) { error_setg(errp, "can't add memory device [0x%" PRIx64 ":0x%" PRIx64 @@ -166,6 +259,10 @@ static uint64_t memory_device_get_free_addr(MachineState *ms, uint64_t next_addr; Range tmp; + if (memory_device_is_empty(md)) { + continue; + } + range_init_nofail(&tmp, mdc->get_addr(md), memory_device_get_region_size(md, &error_abort)); @@ -209,6 +306,7 @@ MemoryDeviceInfoList *qmp_memory_device_list(void) const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(item->data); MemoryDeviceInfo *info = g_new0(MemoryDeviceInfo, 1); + /* Let's query infotmation even for empty memory devices. */ mdc->fill_device_info(md, info); QAPI_LIST_APPEND(tail, info); @@ -228,7 +326,7 @@ static int memory_device_plugged_size(Object *obj, void *opaque) const MemoryDeviceState *md = MEMORY_DEVICE(obj); const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(obj); - if (dev->realized) { + if (dev->realized && !memory_device_is_empty(md)) { *size += mdc->get_plugged_size(md, &error_abort); } } @@ -254,11 +352,42 @@ void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms, uint64_t addr, align = 0; MemoryRegion *mr; + /* We support empty memory devices even without device memory. */ + if (memory_device_is_empty(md)) { + return; + } + + if (!ms->device_memory) { + error_setg(errp, "the configuration is not prepared for memory devices" + " (e.g., for memory hotplug), consider specifying the" + " maxmem option"); + return; + } + mr = mdc->get_memory_region(md, &local_err); if (local_err) { goto out; } + memory_device_check_addable(ms, md, mr, &local_err); + if (local_err) { + goto out; + } + + /* + * We always want the memory region size to be multiples of the memory + * region alignment: for example, DIMMs with 1G+1byte size don't make + * any sense. Note that we don't check that the size is multiples + * of any additional alignment requirements the memory device might + * have when it comes to the address in physical address space. + */ + if (!QEMU_IS_ALIGNED(memory_region_size(mr), + memory_region_get_alignment(mr))) { + error_setg(errp, "backend memory size must be multiple of 0x%" + PRIx64, memory_region_get_alignment(mr)); + return; + } + if (legacy_align) { align = *legacy_align; } else { @@ -285,9 +414,17 @@ out: void memory_device_plug(MemoryDeviceState *md, MachineState *ms) { const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); - const uint64_t addr = mdc->get_addr(md); + unsigned int memslots; + uint64_t addr; MemoryRegion *mr; + if (memory_device_is_empty(md)) { + return; + } + + memslots = memory_device_get_memslots(md); + addr = mdc->get_addr(md); + /* * We expect that a previous call to memory_device_pre_plug() succeeded, so * it can't fail at this point. @@ -295,6 +432,12 @@ void memory_device_plug(MemoryDeviceState *md, MachineState *ms) mr = mdc->get_memory_region(md, &error_abort); g_assert(ms->device_memory); + ms->device_memory->used_region_size += memory_region_size(mr); + ms->device_memory->required_memslots += memslots; + if (mdc->decide_memslots && memslots > 1) { + ms->device_memory->memslot_auto_decision_active++; + } + memory_region_add_subregion(&ms->device_memory->mr, addr - ms->device_memory->base, mr); trace_memory_device_plug(DEVICE(md)->id ? DEVICE(md)->id : "", addr); @@ -303,8 +446,13 @@ void memory_device_plug(MemoryDeviceState *md, MachineState *ms) void memory_device_unplug(MemoryDeviceState *md, MachineState *ms) { const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); + const unsigned int memslots = memory_device_get_memslots(md); MemoryRegion *mr; + if (memory_device_is_empty(md)) { + return; + } + /* * We expect that a previous call to memory_device_pre_plug() succeeded, so * it can't fail at this point. @@ -313,6 +461,12 @@ void memory_device_unplug(MemoryDeviceState *md, MachineState *ms) g_assert(ms->device_memory); memory_region_del_subregion(&ms->device_memory->mr, mr); + + if (mdc->decide_memslots && memslots > 1) { + ms->device_memory->memslot_auto_decision_active--; + } + ms->device_memory->used_region_size -= memory_region_size(mr); + ms->device_memory->required_memslots -= memslots; trace_memory_device_unplug(DEVICE(md)->id ? DEVICE(md)->id : "", mdc->get_addr(md)); } @@ -332,6 +486,71 @@ uint64_t memory_device_get_region_size(const MemoryDeviceState *md, return memory_region_size(mr); } +static void memory_devices_region_mod(MemoryListener *listener, + MemoryRegionSection *mrs, bool add) +{ + DeviceMemoryState *dms = container_of(listener, DeviceMemoryState, + listener); + + if (!memory_region_is_ram(mrs->mr)) { + warn_report("Unexpected memory region mapped into device memory region."); + return; + } + + /* + * The expectation is that each distinct RAM memory region section in + * our region for memory devices consumes exactly one memslot in KVM + * and in vhost. For vhost, this is true, except: + * * ROM memory regions don't consume a memslot. These get used very + * rarely for memory devices (R/O NVDIMMs). + * * Memslots without a fd (memory-backend-ram) don't necessarily + * consume a memslot. Such setups are quite rare and possibly bogus: + * the memory would be inaccessible by such vhost devices. + * + * So for vhost, in corner cases we might over-estimate the number of + * memslots that are currently used or that might still be reserved + * (required - used). + */ + dms->used_memslots += add ? 1 : -1; + + if (dms->used_memslots > dms->required_memslots) { + warn_report("Memory devices use more memory slots than indicated as required."); + } +} + +static void memory_devices_region_add(MemoryListener *listener, + MemoryRegionSection *mrs) +{ + return memory_devices_region_mod(listener, mrs, true); +} + +static void memory_devices_region_del(MemoryListener *listener, + MemoryRegionSection *mrs) +{ + return memory_devices_region_mod(listener, mrs, false); +} + +void machine_memory_devices_init(MachineState *ms, hwaddr base, uint64_t size) +{ + g_assert(size); + g_assert(!ms->device_memory); + ms->device_memory = g_new0(DeviceMemoryState, 1); + ms->device_memory->base = base; + + memory_region_init(&ms->device_memory->mr, OBJECT(ms), "device-memory", + size); + address_space_init(&ms->device_memory->as, &ms->device_memory->mr, + "device-memory"); + memory_region_add_subregion(get_system_memory(), ms->device_memory->base, + &ms->device_memory->mr); + + /* Track the number of memslots used by memory devices. */ + ms->device_memory->listener.region_add = memory_devices_region_add; + ms->device_memory->listener.region_del = memory_devices_region_del; + memory_listener_register(&ms->device_memory->listener, + &ms->device_memory->as); +} + static const TypeInfo memory_device_info = { .name = TYPE_MEMORY_DEVICE, .parent = TYPE_INTERFACE, diff --git a/hw/mem/meson.build b/hw/mem/meson.build index 609b2b36fc..faee1fe936 100644 --- a/hw/mem/meson.build +++ b/hw/mem/meson.build @@ -4,7 +4,8 @@ mem_ss.add(when: 'CONFIG_DIMM', if_true: files('pc-dimm.c')) mem_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_mc.c')) mem_ss.add(when: 'CONFIG_NVDIMM', if_true: files('nvdimm.c')) mem_ss.add(when: 'CONFIG_CXL_MEM_DEVICE', if_true: files('cxl_type3.c')) +system_ss.add(when: 'CONFIG_CXL_MEM_DEVICE', if_false: files('cxl_type3_stubs.c')) -softmmu_ss.add_all(when: 'CONFIG_MEM_DEVICE', if_true: mem_ss) +system_ss.add_all(when: 'CONFIG_MEM_DEVICE', if_true: mem_ss) -softmmu_ss.add(when: 'CONFIG_SPARSE_MEM', if_true: files('sparse-mem.c')) +system_ss.add(when: 'CONFIG_SPARSE_MEM', if_true: files('sparse-mem.c')) diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index 7c7d777781..1631a7d13f 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -149,11 +149,14 @@ static void nvdimm_prepare_memory_region(NVDIMMDevice *nvdimm, Error **errp) if (!nvdimm->unarmed && memory_region_is_rom(mr)) { HostMemoryBackend *hostmem = dimm->hostmem; - error_setg(errp, "'unarmed' property must be off since memdev %s " + error_setg(errp, "'unarmed' property must be 'on' since memdev %s " "is read-only", object_get_canonical_path_component(OBJECT(hostmem))); return; } + if (memory_region_is_rom(mr)) { + nvdimm->readonly = true; + } nvdimm->nvdimm_mr = g_new(MemoryRegion, 1); memory_region_init_alias(nvdimm->nvdimm_mr, OBJECT(dimm), @@ -207,15 +210,16 @@ static void nvdimm_unrealize(PCDIMMDevice *dimm) * label read/write functions. */ static void nvdimm_validate_rw_label_data(NVDIMMDevice *nvdimm, uint64_t size, - uint64_t offset) + uint64_t offset, bool is_write) { assert((nvdimm->label_size >= size + offset) && (offset + size > offset)); + assert(!is_write || !nvdimm->readonly); } static void nvdimm_read_label_data(NVDIMMDevice *nvdimm, void *buf, uint64_t size, uint64_t offset) { - nvdimm_validate_rw_label_data(nvdimm, size, offset); + nvdimm_validate_rw_label_data(nvdimm, size, offset, false); memcpy(buf, nvdimm->label_data + offset, size); } @@ -229,7 +233,7 @@ static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf, "pmem", NULL); uint64_t backend_offset; - nvdimm_validate_rw_label_data(nvdimm, size, offset); + nvdimm_validate_rw_label_data(nvdimm, size, offset, true); if (!is_pmem) { memcpy(nvdimm->label_data + offset, buf, size); diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index f27e1a11ba..37f1f4ccfd 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -81,6 +81,10 @@ void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine) memory_device_plug(MEMORY_DEVICE(dimm), machine); vmstate_register_ram(vmstate_mr, DEVICE(dimm)); + /* count only "real" DIMMs, not NVDIMMs */ + if (!object_dynamic_cast(OBJECT(dimm), TYPE_NVDIMM)) { + machine->device_memory->dimm_size += memory_region_size(vmstate_mr); + } } void pc_dimm_unplug(PCDIMMDevice *dimm, MachineState *machine) @@ -90,6 +94,9 @@ void pc_dimm_unplug(PCDIMMDevice *dimm, MachineState *machine) memory_device_unplug(MEMORY_DEVICE(dimm), machine); vmstate_unregister_ram(vmstate_mr, DEVICE(dimm)); + if (!object_dynamic_cast(OBJECT(dimm), TYPE_NVDIMM)) { + machine->device_memory->dimm_size -= memory_region_size(vmstate_mr); + } } static int pc_dimm_slot2bitmap(Object *obj, void *opaque) @@ -252,7 +259,6 @@ static void pc_dimm_md_fill_device_info(const MemoryDeviceState *md, const DeviceState *dev = DEVICE(md); if (dev->id) { - di->has_id = true; di->id = g_strdup(dev->id); } di->hotplugged = dev->hotplugged; diff --git a/hw/mem/sparse-mem.c b/hw/mem/sparse-mem.c index e6640eb8e7..6e8f4f84fb 100644 --- a/hw/mem/sparse-mem.c +++ b/hw/mem/sparse-mem.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" @@ -77,6 +78,13 @@ static void sparse_mem_write(void *opaque, hwaddr addr, uint64_t v, } +static void sparse_mem_enter_reset(Object *obj, ResetType type) +{ + SparseMemState *s = SPARSE_MEM(obj); + g_hash_table_remove_all(s->mapped); + return; +} + static const MemoryRegionOps sparse_mem_ops = { .read = sparse_mem_read, .write = sparse_mem_write, @@ -123,7 +131,8 @@ static void sparse_mem_realize(DeviceState *dev, Error **errp) assert(s->baseaddr + s->length > s->baseaddr); - s->mapped = g_hash_table_new(NULL, NULL); + s->mapped = g_hash_table_new_full(NULL, NULL, NULL, + (GDestroyNotify)g_free); memory_region_init_io(&s->mmio, OBJECT(s), &sparse_mem_ops, s, "sparse-mem", s->length); sysbus_init_mmio(sbd, &s->mmio); @@ -131,12 +140,15 @@ static void sparse_mem_realize(DeviceState *dev, Error **errp) static void sparse_mem_class_init(ObjectClass *klass, void *data) { + ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_props(dc, sparse_mem_properties); dc->desc = "Sparse Memory Device"; dc->realize = sparse_mem_realize; + + rc->phases.enter = sparse_mem_enter_reset; } static const TypeInfo sparse_mem_types[] = { diff --git a/hw/meson.build b/hw/meson.build index c7ac7d3d75..463d702683 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -37,12 +37,14 @@ subdir('smbios') subdir('ssi') subdir('timer') subdir('tpm') +subdir('ufs') subdir('usb') subdir('vfio') subdir('virtio') subdir('watchdog') subdir('xen') subdir('xenpv') +subdir('fsi') subdir('alpha') subdir('arm') diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index 8b92a9801a..ed61e483ee 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -30,6 +30,7 @@ #include "qemu/option.h" #include "qemu/config-file.h" #include "qemu/error-report.h" +#include "qemu/guest-random.h" #include "sysemu/device_tree.h" #include "sysemu/reset.h" #include "hw/boards.h" @@ -75,6 +76,7 @@ static int microblaze_load_dtb(hwaddr addr, int fdt_size; void *fdt = NULL; int r; + uint8_t rng_seed[32]; if (dtb_filename) { fdt = load_device_tree(dtb_filename, &fdt_size); @@ -83,6 +85,9 @@ static int microblaze_load_dtb(hwaddr addr, return 0; } + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); + if (kernel_cmdline) { r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); @@ -135,22 +140,17 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, int kernel_size; uint64_t entry, high; uint32_t base32; - int big_endian = 0; - -#if TARGET_BIG_ENDIAN - big_endian = 1; -#endif /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &entry, NULL, &high, NULL, - big_endian, EM_MICROBLAZE, 0, 0); + TARGET_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, NULL, - big_endian, EM_MICROBLAZE, 0, 0); + TARGET_BIG_ENDIAN, EM_MICROBLAZE, 0, 0); } /* Always boot into physical ram. */ boot_info.bootstrap_pc = (uint32_t)entry; diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index a24fadddca..0f5fabc32e 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -104,7 +104,7 @@ petalogix_ml605_init(MachineState *machine) dinfo = drive_get(IF_PFLASH, 0, 0); /* 5th parameter 2 means bank-width - * 10th paremeter 0 means little-endian */ + * 10th parameter 0 means little-endian */ pflash_cfi01_register(FLASH_BASEADDR, "petalogix_ml605.flash", FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, 64 * KiB, 2, 0x89, 0x18, 0x0000, 0x0, 0); @@ -133,7 +133,6 @@ petalogix_ml605_init(MachineState *machine) sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]); /* axi ethernet and dma initialization. */ - qemu_check_nic_model(&nd_table[0], "xlnx.axi-ethernet"); eth0 = qdev_new("xlnx.axi-ethernet"); dma = qdev_new("xlnx.axi-dma"); @@ -145,7 +144,7 @@ petalogix_ml605_init(MachineState *machine) "axistream-connected-target", NULL); cs = object_property_get_link(OBJECT(dma), "axistream-control-connected-target", NULL); - qdev_set_nic_properties(eth0, &nd_table[0]); + qemu_configure_nic_device(eth0, true, NULL); qdev_prop_set_uint32(eth0, "rxmem", 0x1000); qdev_prop_set_uint32(eth0, "txmem", 0x1000); object_property_set_link(OBJECT(eth0), "axistream-connected", ds, @@ -183,7 +182,7 @@ petalogix_ml605_init(MachineState *machine) spi = (SSIBus *)qdev_get_child_bus(dev, "spi"); for (i = 0; i < NUM_SPI_FLASHES; i++) { - DriveInfo *dinfo = drive_get(IF_MTD, 0, i); + dinfo = drive_get(IF_MTD, 0, i); qemu_irq cs_line; dev = qdev_new("n25q128"); @@ -192,6 +191,7 @@ petalogix_ml605_init(MachineState *machine) blk_by_legacy_dinfo(dinfo), &error_fatal); } + qdev_prop_set_uint8(dev, "cs", i); qdev_realize_and_unref(dev, BUS(spi), &error_fatal); cs_line = qdev_get_gpio_in_named(dev, SSI_GPIO_CS, 0); diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 9d959d1ad8..dad46bd7f9 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -100,8 +100,11 @@ petalogix_s3adsp1800_init(MachineState *machine) irq[i] = qdev_get_gpio_in(dev, i); } - xilinx_uartlite_create(UARTLITE_BASEADDR, irq[UARTLITE_IRQ], - serial_hd(0)); + dev = qdev_new(TYPE_XILINX_UARTLITE); + qdev_prop_set_chr(dev, "chardev", serial_hd(0)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, UARTLITE_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[UARTLITE_IRQ]); /* 2 timers at irq 2 @ 62 Mhz. */ dev = qdev_new("xlnx.xps-timer"); @@ -111,9 +114,8 @@ petalogix_s3adsp1800_init(MachineState *machine) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]); - qemu_check_nic_model(&nd_table[0], "xlnx.xps-ethernetlite"); dev = qdev_new("xlnx.xps-ethernetlite"); - qdev_set_nic_properties(dev, &nd_table[0]); + qemu_configure_nic_device(dev, true, NULL); qdev_prop_set_uint32(dev, "tx-ping-pong", 0); qdev_prop_set_uint32(dev, "rx-ping-pong", 0); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); diff --git a/hw/microblaze/xlnx-zynqmp-pmu.c b/hw/microblaze/xlnx-zynqmp-pmu.c index 5a2016672a..1bfc9641d2 100644 --- a/hw/microblaze/xlnx-zynqmp-pmu.c +++ b/hw/microblaze/xlnx-zynqmp-pmu.c @@ -125,6 +125,8 @@ static void xlnx_zynqmp_pmu_soc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + /* xlnx-zynqmp-pmu-soc causes crashes when cold-plugged twice */ + dc->user_creatable = false; dc->realize = xlnx_zynqmp_pmu_soc_realize; } diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index 725525358d..5c83ef49cf 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -1,11 +1,19 @@ config MALTA bool - select ISA_SUPERIO + imply PCNET_PCI + imply PCI_DEVICES + imply TEST_DEVICES + select FDC37M81X + select GT64120 + select MIPS_CPS + select PIIX + select PFLASH_CFI01 + select SERIAL + select SMBUS_EEPROM config MIPSSIM bool - select ISA_BUS - select SERIAL_ISA + select SERIAL select MIPSNET config JAZZ @@ -30,27 +38,39 @@ config JAZZ config FULOONG bool + imply PCI_DEVICES + imply TEST_DEVICES + imply ATI_VGA + imply RTL8139_PCI select PCI_BONITO + select SMBUS_EEPROM + select VT82C686 config LOONGSON3V bool + imply PCI_DEVICES + imply TEST_DEVICES + imply VIRTIO_PCI + imply VIRTIO_NET imply VIRTIO_VGA imply QXL if SPICE + imply USB_OHCI_PCI select SERIAL select GOLDFISH_RTC select LOONGSON_LIOINTC - select PCI_DEVICES select PCI_EXPRESS_GENERIC_BRIDGE select MSI_NONBROKEN select FW_CFG_MIPS + select UNIMP config MIPS_CPS bool - select PTIMER select MIPS_ITU config MIPS_BOSTON bool + imply PCI_DEVICES + imply TEST_DEVICES select FITLOADER select MIPS_CPS select PCI_EXPRESS_XILINX diff --git a/hw/mips/bootloader.c b/hw/mips/bootloader.c index 99991f8b2b..1dd6ef2096 100644 --- a/hw/mips/bootloader.c +++ b/hw/mips/bootloader.c @@ -54,17 +54,37 @@ static bool bootcpu_supports_isa(uint64_t isa_mask) return cpu_supports_isa(&MIPS_CPU(first_cpu)->env, isa_mask); } -/* Base types */ -static void bl_gen_nop(uint32_t **p) +static void st_nm32_p(void **ptr, uint32_t insn) { - stl_p(*p, 0); - *p = *p + 1; + uint16_t *p = *ptr; + + stw_p(p, insn >> 16); + p++; + stw_p(p, insn >> 0); + p++; + + *ptr = p; } -static void bl_gen_r_type(uint32_t **p, uint8_t opcode, +/* Base types */ +static void bl_gen_nop(void **ptr) +{ + if (bootcpu_supports_isa(ISA_NANOMIPS32)) { + st_nm32_p(ptr, 0x8000c000); + } else { + uint32_t *p = *ptr; + + stl_p(p, 0); + p++; + *ptr = p; + } +} + +static void bl_gen_r_type(void **ptr, uint8_t opcode, bl_reg rs, bl_reg rt, bl_reg rd, uint8_t shift, uint8_t funct) { + uint32_t *p = *ptr; uint32_t insn = 0; insn = deposit32(insn, 26, 6, opcode); @@ -74,13 +94,16 @@ static void bl_gen_r_type(uint32_t **p, uint8_t opcode, insn = deposit32(insn, 6, 5, shift); insn = deposit32(insn, 0, 6, funct); - stl_p(*p, insn); - *p = *p + 1; + stl_p(p, insn); + p++; + + *ptr = p; } -static void bl_gen_i_type(uint32_t **p, uint8_t opcode, +static void bl_gen_i_type(void **ptr, uint8_t opcode, bl_reg rs, bl_reg rt, uint16_t imm) { + uint32_t *p = *ptr; uint32_t insn = 0; insn = deposit32(insn, 26, 6, opcode); @@ -88,12 +111,14 @@ static void bl_gen_i_type(uint32_t **p, uint8_t opcode, insn = deposit32(insn, 16, 5, rt); insn = deposit32(insn, 0, 16, imm); - stl_p(*p, insn); - *p = *p + 1; + stl_p(p, insn); + p++; + + *ptr = p; } /* Single instructions */ -static void bl_gen_dsll(uint32_t **p, bl_reg rd, bl_reg rt, uint8_t sa) +static void bl_gen_dsll(void **p, bl_reg rd, bl_reg rt, uint8_t sa) { if (bootcpu_supports_isa(ISA_MIPS3)) { bl_gen_r_type(p, 0, 0, rt, rd, sa, 0x38); @@ -102,28 +127,83 @@ static void bl_gen_dsll(uint32_t **p, bl_reg rd, bl_reg rt, uint8_t sa) } } -static void bl_gen_jalr(uint32_t **p, bl_reg rs) +static void bl_gen_jalr(void **p, bl_reg rs) { - bl_gen_r_type(p, 0, rs, 0, BL_REG_RA, 0, 0x09); + if (bootcpu_supports_isa(ISA_NANOMIPS32)) { + uint32_t insn = 0; + + insn = deposit32(insn, 26, 6, 0b010010); /* JALRC */ + insn = deposit32(insn, 21, 5, BL_REG_RA); + insn = deposit32(insn, 16, 5, rs); + + st_nm32_p(p, insn); + } else { + bl_gen_r_type(p, 0, rs, 0, BL_REG_RA, 0, 0x09); + } } -static void bl_gen_lui(uint32_t **p, bl_reg rt, uint16_t imm) +static void bl_gen_lui_nm(void **ptr, bl_reg rt, uint32_t imm20) +{ + uint32_t insn = 0; + + assert(extract32(imm20, 0, 20) == imm20); + insn = deposit32(insn, 26, 6, 0b111000); + insn = deposit32(insn, 21, 5, rt); + insn = deposit32(insn, 12, 9, extract32(imm20, 0, 9)); + insn = deposit32(insn, 2, 10, extract32(imm20, 9, 10)); + insn = deposit32(insn, 0, 1, sextract32(imm20, 19, 1)); + + st_nm32_p(ptr, insn); +} + +static void bl_gen_lui(void **p, bl_reg rt, uint16_t imm) { /* R6: It's a alias of AUI with RS = 0 */ bl_gen_i_type(p, 0x0f, 0, rt, imm); } -static void bl_gen_ori(uint32_t **p, bl_reg rt, bl_reg rs, uint16_t imm) +static void bl_gen_ori_nm(void **ptr, bl_reg rt, bl_reg rs, uint16_t imm12) +{ + uint32_t insn = 0; + + assert(extract32(imm12, 0, 12) == imm12); + insn = deposit32(insn, 26, 6, 0b100000); + insn = deposit32(insn, 21, 5, rt); + insn = deposit32(insn, 16, 5, rs); + insn = deposit32(insn, 0, 12, imm12); + + st_nm32_p(ptr, insn); +} + +static void bl_gen_ori(void **p, bl_reg rt, bl_reg rs, uint16_t imm) { bl_gen_i_type(p, 0x0d, rs, rt, imm); } -static void bl_gen_sw(uint32_t **p, bl_reg rt, uint8_t base, uint16_t offset) +static void bl_gen_sw_nm(void **ptr, bl_reg rt, uint8_t rs, uint16_t ofs12) { - bl_gen_i_type(p, 0x2b, base, rt, offset); + uint32_t insn = 0; + + assert(extract32(ofs12, 0, 12) == ofs12); + insn = deposit32(insn, 26, 6, 0b100001); + insn = deposit32(insn, 21, 5, rt); + insn = deposit32(insn, 16, 5, rs); + insn = deposit32(insn, 12, 4, 0b1001); + insn = deposit32(insn, 0, 12, ofs12); + + st_nm32_p(ptr, insn); } -static void bl_gen_sd(uint32_t **p, bl_reg rt, uint8_t base, uint16_t offset) +static void bl_gen_sw(void **p, bl_reg rt, uint8_t base, uint16_t offset) +{ + if (bootcpu_supports_isa(ISA_NANOMIPS32)) { + bl_gen_sw_nm(p, rt, base, offset); + } else { + bl_gen_i_type(p, 0x2b, base, rt, offset); + } +} + +static void bl_gen_sd(void **p, bl_reg rt, uint8_t base, uint16_t offset) { if (bootcpu_supports_isa(ISA_MIPS3)) { bl_gen_i_type(p, 0x3f, base, rt, offset); @@ -133,13 +213,18 @@ static void bl_gen_sd(uint32_t **p, bl_reg rt, uint8_t base, uint16_t offset) } /* Pseudo instructions */ -static void bl_gen_li(uint32_t **p, bl_reg rt, uint32_t imm) +static void bl_gen_li(void **p, bl_reg rt, uint32_t imm) { - bl_gen_lui(p, rt, extract32(imm, 16, 16)); - bl_gen_ori(p, rt, rt, extract32(imm, 0, 16)); + if (bootcpu_supports_isa(ISA_NANOMIPS32)) { + bl_gen_lui_nm(p, rt, extract32(imm, 12, 20)); + bl_gen_ori_nm(p, rt, rt, extract32(imm, 0, 12)); + } else { + bl_gen_lui(p, rt, extract32(imm, 16, 16)); + bl_gen_ori(p, rt, rt, extract32(imm, 0, 16)); + } } -static void bl_gen_dli(uint32_t **p, bl_reg rt, uint64_t imm) +static void bl_gen_dli(void **p, bl_reg rt, uint64_t imm) { bl_gen_li(p, rt, extract64(imm, 32, 32)); bl_gen_dsll(p, rt, rt, 16); @@ -148,7 +233,7 @@ static void bl_gen_dli(uint32_t **p, bl_reg rt, uint64_t imm) bl_gen_ori(p, rt, rt, extract64(imm, 0, 16)); } -static void bl_gen_load_ulong(uint32_t **p, bl_reg rt, target_ulong imm) +static void bl_gen_load_ulong(void **p, bl_reg rt, target_ulong imm) { if (bootcpu_supports_isa(ISA_MIPS3)) { bl_gen_dli(p, rt, imm); /* 64bit */ @@ -158,27 +243,41 @@ static void bl_gen_load_ulong(uint32_t **p, bl_reg rt, target_ulong imm) } /* Helpers */ -void bl_gen_jump_to(uint32_t **p, target_ulong jump_addr) +void bl_gen_jump_to(void **p, target_ulong jump_addr) { bl_gen_load_ulong(p, BL_REG_T9, jump_addr); bl_gen_jalr(p, BL_REG_T9); bl_gen_nop(p); /* delay slot */ } -void bl_gen_jump_kernel(uint32_t **p, target_ulong sp, target_ulong a0, - target_ulong a1, target_ulong a2, target_ulong a3, +void bl_gen_jump_kernel(void **p, + bool set_sp, target_ulong sp, + bool set_a0, target_ulong a0, + bool set_a1, target_ulong a1, + bool set_a2, target_ulong a2, + bool set_a3, target_ulong a3, target_ulong kernel_addr) { - bl_gen_load_ulong(p, BL_REG_SP, sp); - bl_gen_load_ulong(p, BL_REG_A0, a0); - bl_gen_load_ulong(p, BL_REG_A1, a1); - bl_gen_load_ulong(p, BL_REG_A2, a2); - bl_gen_load_ulong(p, BL_REG_A3, a3); + if (set_sp) { + bl_gen_load_ulong(p, BL_REG_SP, sp); + } + if (set_a0) { + bl_gen_load_ulong(p, BL_REG_A0, a0); + } + if (set_a1) { + bl_gen_load_ulong(p, BL_REG_A1, a1); + } + if (set_a2) { + bl_gen_load_ulong(p, BL_REG_A2, a2); + } + if (set_a3) { + bl_gen_load_ulong(p, BL_REG_A3, a3); + } bl_gen_jump_to(p, kernel_addr); } -void bl_gen_write_ulong(uint32_t **p, target_ulong addr, target_ulong val) +void bl_gen_write_ulong(void **p, target_ulong addr, target_ulong val) { bl_gen_load_ulong(p, BL_REG_K0, val); bl_gen_load_ulong(p, BL_REG_K1, addr); @@ -189,14 +288,14 @@ void bl_gen_write_ulong(uint32_t **p, target_ulong addr, target_ulong val) } } -void bl_gen_write_u32(uint32_t **p, target_ulong addr, uint32_t val) +void bl_gen_write_u32(void **p, target_ulong addr, uint32_t val) { bl_gen_li(p, BL_REG_K0, val); bl_gen_load_ulong(p, BL_REG_K1, addr); bl_gen_sw(p, BL_REG_K0, BL_REG_K1, 0x0); } -void bl_gen_write_u64(uint32_t **p, target_ulong addr, uint64_t val) +void bl_gen_write_u64(void **p, target_ulong addr, uint64_t val) { bl_gen_dli(p, BL_REG_K0, val); bl_gen_load_ulong(p, BL_REG_K1, addr); diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 59ca08b93a..1b44fb354c 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -24,7 +24,7 @@ #include "hw/boards.h" #include "hw/char/serial.h" #include "hw/ide/pci.h" -#include "hw/ide/ahci.h" +#include "hw/ide/ahci-pci.h" #include "hw/loader.h" #include "hw/loader-fit.h" #include "hw/mips/bootloader.h" @@ -34,12 +34,14 @@ #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/error-report.h" +#include "qemu/guest-random.h" #include "qemu/log.h" #include "chardev/char.h" #include "sysemu/device_tree.h" #include "sysemu/sysemu.h" #include "sysemu/qtest.h" #include "sysemu/runstate.h" +#include "sysemu/reset.h" #include #include "qom/object.h" @@ -321,7 +323,7 @@ static void boston_register_types(void) } type_init(boston_register_types) -static void gen_firmware(uint32_t *p, hwaddr kernel_entry, hwaddr fdt_addr) +static void gen_firmware(void *p, hwaddr kernel_entry, hwaddr fdt_addr) { uint64_t regaddr; @@ -350,7 +352,10 @@ static void gen_firmware(uint32_t *p, hwaddr kernel_entry, hwaddr fdt_addr) * a2/$6 = 0 * a3/$7 = 0 */ - bl_gen_jump_kernel(&p, 0, (int32_t)-2, fdt_addr, 0, 0, kernel_entry); + bl_gen_jump_kernel(&p, + true, 0, true, (int32_t)-2, + true, fdt_addr, true, 0, true, 0, + kernel_entry); } static const void *boston_fdt_filter(void *opaque, const void *fdt_orig, @@ -363,6 +368,7 @@ static const void *boston_fdt_filter(void *opaque, const void *fdt_orig, size_t ram_low_sz, ram_high_sz; size_t fdt_sz = fdt_totalsize(fdt_orig) * 2; g_autofree void *fdt = g_malloc0(fdt_sz); + uint8_t rng_seed[32]; err = fdt_open_into(fdt_orig, fdt, fdt_sz); if (err) { @@ -370,6 +376,9 @@ static const void *boston_fdt_filter(void *opaque, const void *fdt_orig, return NULL; } + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); + cmdline = (machine->kernel_cmdline && machine->kernel_cmdline[0]) ? machine->kernel_cmdline : " "; err = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); @@ -419,7 +428,7 @@ static inline XilinxPCIEHost * xilinx_pcie_init(MemoryRegion *sys_mem, uint32_t bus_nr, hwaddr cfg_base, uint64_t cfg_size, hwaddr mmio_base, uint64_t mmio_size, - qemu_irq irq, bool link_up) + qemu_irq irq) { DeviceState *dev; MemoryRegion *cfg, *mmio; @@ -431,7 +440,6 @@ xilinx_pcie_init(MemoryRegion *sys_mem, uint32_t bus_nr, qdev_prop_set_uint64(dev, "cfg_size", cfg_size); qdev_prop_set_uint64(dev, "mmio_base", mmio_base); qdev_prop_set_uint64(dev, "mmio_size", mmio_size); - qdev_prop_set_bit(dev, "link_up", link_up); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -507,7 +515,7 @@ static const void *create_fdt(BostonState *s, { void *fdt; int cpu; - MachineState *mc = s->mach; + MachineState *ms = s->mach; uint32_t platreg_ph, gic_ph, clk_ph; char *name, *gic_name, *platreg_name, *stdout_name; static const char * const syscon_compat[2] = { @@ -534,7 +542,7 @@ static const void *create_fdt(BostonState *s, qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); - for (cpu = 0; cpu < mc->smp.cpus; cpu++) { + for (cpu = 0; cpu < ms->smp.cpus; cpu++) { name = g_strdup_printf("/cpus/cpu@%d", cpu); qemu_fdt_add_subnode(fdt, name); qemu_fdt_setprop_string(fdt, name, "compatible", "img,mips"); @@ -669,7 +677,8 @@ static void boston_mach_init(MachineState *machine) MemoryRegion *flash, *ddr_low_alias, *lcd, *platreg; MemoryRegion *sys_mem = get_system_memory(); XilinxPCIEHost *pcie2; - PCIDevice *ahci; + PCIDevice *pdev; + AHCIPCIState *ich9; DriveInfo *hd[6]; Chardev *chr; int fw_size, fit_err; @@ -694,7 +703,7 @@ static void boston_mach_init(MachineState *machine) object_initialize_child(OBJECT(machine), "cps", &s->cps, TYPE_MIPS_CPS); object_property_set_str(OBJECT(&s->cps), "cpu-type", machine->cpu_type, &error_fatal); - object_property_set_int(OBJECT(&s->cps), "num-vp", machine->smp.cpus, + object_property_set_uint(OBJECT(&s->cps), "num-vp", machine->smp.cpus, &error_fatal); qdev_connect_clock_in(DEVICE(&s->cps), "clk-in", qdev_get_clock_out(dev, "cpu-refclk")); @@ -724,21 +733,21 @@ static void boston_mach_init(MachineState *machine) boston_memmap[BOSTON_PCIE0].size, boston_memmap[BOSTON_PCIE0_MMIO].base, boston_memmap[BOSTON_PCIE0_MMIO].size, - get_cps_irq(&s->cps, 2), false); + get_cps_irq(&s->cps, 2)); xilinx_pcie_init(sys_mem, 1, boston_memmap[BOSTON_PCIE1].base, boston_memmap[BOSTON_PCIE1].size, boston_memmap[BOSTON_PCIE1_MMIO].base, boston_memmap[BOSTON_PCIE1_MMIO].size, - get_cps_irq(&s->cps, 1), false); + get_cps_irq(&s->cps, 1)); pcie2 = xilinx_pcie_init(sys_mem, 2, boston_memmap[BOSTON_PCIE2].base, boston_memmap[BOSTON_PCIE2].size, boston_memmap[BOSTON_PCIE2_MMIO].base, boston_memmap[BOSTON_PCIE2_MMIO].size, - get_cps_irq(&s->cps, 0), true); + get_cps_irq(&s->cps, 0)); platreg = g_new(MemoryRegion, 1); memory_region_init_io(platreg, NULL, &boston_platreg_ops, s, @@ -761,12 +770,12 @@ static void boston_mach_init(MachineState *machine) qemu_chr_fe_set_handlers(&s->lcd_display, NULL, NULL, boston_lcd_event, NULL, s, NULL, true); - ahci = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus, - PCI_DEVFN(0, 0), - true, TYPE_ICH9_AHCI); - g_assert(ARRAY_SIZE(hd) == ahci_get_num_ports(ahci)); - ide_drive_get(hd, ahci_get_num_ports(ahci)); - ahci_ide_create_devs(ahci, hd); + pdev = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus, + PCI_DEVFN(0, 0), TYPE_ICH9_AHCI); + ich9 = ICH9_AHCI(pdev); + g_assert(ARRAY_SIZE(hd) == ich9->ahci.ports); + ide_drive_get(hd, ich9->ahci.ports); + ahci_ide_create_devs(&ich9->ahci, hd); if (machine->firmware) { fw_size = load_image_targphys(machine->firmware, @@ -787,7 +796,8 @@ static void boston_mach_init(MachineState *machine) if (kernel_size > 0) { int dt_size; - g_autofree const void *dtb_file_data, *dtb_load_data; + g_autofree const void *dtb_file_data = NULL; + g_autofree const void *dtb_load_data = NULL; hwaddr dtb_paddr = QEMU_ALIGN_UP(kernel_high, 64 * KiB); hwaddr dtb_vaddr = cpu_mips_phys_to_kseg0(NULL, dtb_paddr); @@ -804,6 +814,8 @@ static void boston_mach_init(MachineState *machine) /* Calculate real fdt size after filter */ dt_size = fdt_totalsize(dtb_load_data); rom_add_blob_fixed("dtb", dtb_load_data, dt_size, dtb_paddr); + qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, + rom_ptr(dtb_paddr, dt_size)); } else { /* Try to load file as FIT */ fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s); diff --git a/hw/mips/cps.c b/hw/mips/cps.c index 2b436700ce..07b73b0a1f 100644 --- a/hw/mips/cps.c +++ b/hw/mips/cps.c @@ -24,7 +24,6 @@ #include "hw/mips/mips.h" #include "hw/qdev-clock.h" #include "hw/qdev-properties.h" -#include "hw/mips/cpudevs.h" #include "sysemu/kvm.h" #include "sysemu/reset.h" @@ -66,26 +65,22 @@ static bool cpu_mips_itu_supported(CPUMIPSState *env) static void mips_cps_realize(DeviceState *dev, Error **errp) { MIPSCPSState *s = MIPS_CPS(dev); - CPUMIPSState *env; - MIPSCPU *cpu; - int i; target_ulong gcr_base; bool itu_present = false; - bool saar_present = false; if (!clock_get(s->clock)) { error_setg(errp, "CPS input clock is not connected to an output clock"); return; } - for (i = 0; i < s->num_vp; i++) { - cpu = MIPS_CPU(object_new(s->cpu_type)); + for (int i = 0; i < s->num_vp; i++) { + MIPSCPU *cpu = MIPS_CPU(object_new(s->cpu_type)); + CPUMIPSState *env = &cpu->env; /* All VPs are halted on reset. Leave powering up to CPC. */ - if (!object_property_set_bool(OBJECT(cpu), "start-powered-off", true, - errp)) { - return; - } + object_property_set_bool(OBJECT(cpu), "start-powered-off", true, + &error_abort); + /* All cores use the same clock tree */ qdev_connect_clock_in(DEVICE(cpu), "clk-in", s->clock); @@ -97,32 +92,21 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) cpu_mips_irq_init_cpu(cpu); cpu_mips_clock_init(cpu); - env = &cpu->env; if (cpu_mips_itu_supported(env)) { itu_present = true; /* Attach ITC Tag to the VP */ env->itc_tag = mips_itu_get_tag_region(&s->itu); - env->itu = &s->itu; } qemu_register_reset(main_cpu_reset, cpu); } - cpu = MIPS_CPU(first_cpu); - env = &cpu->env; - saar_present = (bool)env->saarp; - /* Inter-Thread Communication Unit */ if (itu_present) { object_initialize_child(OBJECT(dev), "itu", &s->itu, TYPE_MIPS_ITU); - object_property_set_int(OBJECT(&s->itu), "num-fifo", 16, + object_property_set_uint(OBJECT(&s->itu), "num-fifo", 16, &error_abort); - object_property_set_int(OBJECT(&s->itu), "num-semaphores", 16, + object_property_set_uint(OBJECT(&s->itu), "num-semaphores", 16, &error_abort); - object_property_set_bool(OBJECT(&s->itu), "saar-present", saar_present, - &error_abort); - if (saar_present) { - s->itu.saar = &env->CP0_SAAR; - } if (!sysbus_realize(SYS_BUS_DEVICE(&s->itu), errp)) { return; } @@ -133,7 +117,7 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) /* Cluster Power Controller */ object_initialize_child(OBJECT(dev), "cpc", &s->cpc, TYPE_MIPS_CPC); - object_property_set_int(OBJECT(&s->cpc), "num-vp", s->num_vp, + object_property_set_uint(OBJECT(&s->cpc), "num-vp", s->num_vp, &error_abort); object_property_set_int(OBJECT(&s->cpc), "vp-start-running", 1, &error_abort); @@ -146,9 +130,9 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) /* Global Interrupt Controller */ object_initialize_child(OBJECT(dev), "gic", &s->gic, TYPE_MIPS_GIC); - object_property_set_int(OBJECT(&s->gic), "num-vp", s->num_vp, + object_property_set_uint(OBJECT(&s->gic), "num-vp", s->num_vp, &error_abort); - object_property_set_int(OBJECT(&s->gic), "num-irq", 128, + object_property_set_uint(OBJECT(&s->gic), "num-irq", 128, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->gic), errp)) { return; @@ -158,10 +142,10 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gic), 0)); /* Global Configuration Registers */ - gcr_base = env->CP0_CMGCRBase << 4; + gcr_base = MIPS_CPU(first_cpu)->env.CP0_CMGCRBase << 4; object_initialize_child(OBJECT(dev), "gcr", &s->gcr, TYPE_MIPS_GCR); - object_property_set_int(OBJECT(&s->gcr), "num-vp", s->num_vp, + object_property_set_uint(OBJECT(&s->gcr), "num-vp", s->num_vp, &error_abort); object_property_set_int(OBJECT(&s->gcr), "gcr-rev", 0x800, &error_abort); diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c index 5ee546f5f6..a45aac368c 100644 --- a/hw/mips/fuloong2e.c +++ b/hw/mips/fuloong2e.c @@ -30,7 +30,6 @@ #include "hw/block/flash.h" #include "hw/mips/mips.h" #include "hw/mips/bootloader.h" -#include "hw/mips/cpudevs.h" #include "hw/pci/pci.h" #include "hw/loader.h" #include "hw/ide/pci.h" @@ -49,7 +48,6 @@ /* Fuloong 2e has a 512k flash: Winbond W39L040AP70Z */ #define BIOS_SIZE (512 * KiB) -#define MAX_IDE_BUS 2 /* * PMON is not part of qemu and released with BSD license, anyone @@ -180,8 +178,12 @@ static void write_bootloader(CPUMIPSState *env, uint8_t *base, /* Second part of the bootloader */ p = (uint32_t *)(base + 0x040); - bl_gen_jump_kernel(&p, ENVP_VADDR - 64, 2, ENVP_VADDR, ENVP_VADDR + 8, - loaderparams.ram_size, kernel_addr); + bl_gen_jump_kernel((void **)&p, + true, ENVP_VADDR - 64, + true, 2, true, ENVP_VADDR, + true, ENVP_VADDR + 8, + true, loaderparams.ram_size, + kernel_addr); } static void main_cpu_reset(void *opaque) @@ -196,45 +198,12 @@ static void main_cpu_reset(void *opaque) } } -static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc, - I2CBus **i2c_bus) -{ - PCIDevice *dev; - - dev = pci_create_simple_multifunction(pci_bus, PCI_DEVFN(slot, 0), true, - TYPE_VT82C686B_ISA); - qdev_connect_gpio_out(DEVICE(dev), 0, intc); - - 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"); - - dev = pci_create_simple(pci_bus, PCI_DEVFN(slot, 4), TYPE_VT82C686B_PM); - *i2c_bus = I2C_BUS(qdev_get_child_bus(DEVICE(dev), "i2c")); - - /* Audio support */ - pci_create_simple(pci_bus, PCI_DEVFN(slot, 5), TYPE_VIA_AC97); - pci_create_simple(pci_bus, PCI_DEVFN(slot, 6), TYPE_VIA_MC97); -} - /* Network support */ static void network_init(PCIBus *pci_bus) { - int i; - - for (i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - const char *default_devaddr = NULL; - - if (i == 0 && (!nd->model || strcmp(nd->model, "rtl8139") == 0)) { - /* The Fuloong board has a RTL8139 card using PCI SLOT 7 */ - default_devaddr = "07"; - } - - pci_nic_init_nofail(nd, pci_bus, "rtl8139", default_devaddr); - } + /* The Fuloong board has a RTL8139 card using PCI SLOT 7 */ + pci_init_nic_in_slot(pci_bus, "rtl8139", NULL, "07"); + pci_init_nic_devices(pci_bus, "rtl8139"); } static void mips_fuloong2e_init(MachineState *machine) @@ -315,8 +284,28 @@ static void mips_fuloong2e_init(MachineState *machine) pci_bus = bonito_init((qemu_irq *)&(env->irq[2])); /* South bridge -> IP5 */ - vt82c686b_southbridge_init(pci_bus, FULOONG2E_VIA_SLOT, env->irq[5], - &smbus); + pci_dev = pci_new_multifunction(PCI_DEVFN(FULOONG2E_VIA_SLOT, 0), + TYPE_VT82C686B_ISA); + + /* Set properties on individual devices before realizing the south bridge */ + if (machine->audiodev) { + dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ac97")); + qdev_prop_set_string(dev, "audiodev", machine->audiodev); + } + + pci_realize_and_unref(pci_dev, pci_bus, &error_abort); + + object_property_add_alias(OBJECT(machine), "rtc-time", + object_resolve_path_component(OBJECT(pci_dev), + "rtc"), + "date"); + qdev_connect_gpio_out(DEVICE(pci_dev), 0, env->irq[5]); + + dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ide")); + pci_ide_create_devs(PCI_DEVICE(dev)); + + dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "pm")); + smbus = I2C_BUS(qdev_get_child_bus(dev, "i2c")); /* GPU */ if (vga_interface_type != VGA_NONE) { @@ -345,6 +334,7 @@ static void mips_fuloong2e_machine_init(MachineClass *mc) mc->default_ram_size = 256 * MiB; mc->default_ram_id = "fuloong2e.ram"; mc->minimum_page_bits = 14; + machine_add_audiodev_property(mc); } DEFINE_MACHINE("fuloong2e", mips_fuloong2e_machine_init) diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index 6598d7dddd..1bc17e69d3 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -26,7 +26,6 @@ #include "qemu/datadir.h" #include "hw/clock.h" #include "hw/mips/mips.h" -#include "hw/mips/cpudevs.h" #include "hw/intc/i8259.h" #include "hw/dma/i8257.h" #include "hw/char/serial.h" @@ -37,7 +36,6 @@ #include "hw/boards.h" #include "net/net.h" #include "hw/scsi/esp.h" -#include "hw/mips/bios.h" #include "hw/loader.h" #include "hw/rtc/mc146818rtc.h" #include "hw/timer/i8254.h" @@ -54,12 +52,19 @@ #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" #endif /* CONFIG_TCG */ +#include "cpu.h" enum jazz_model_e { JAZZ_MAGNUM, JAZZ_PICA61, }; +#if TARGET_BIG_ENDIAN +#define BIOS_FILENAME "mips_bios.bin" +#else +#define BIOS_FILENAME "mipsel_bios.bin" +#endif + static void main_cpu_reset(void *opaque) { MIPSCPU *cpu = opaque; @@ -114,6 +119,46 @@ static const MemoryRegionOps dma_dummy_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +static void mips_jazz_init_net(IOMMUMemoryRegion *rc4030_dma_mr, + DeviceState *rc4030, MemoryRegion *dp8393x_prom) +{ + DeviceState *dev; + SysBusDevice *sysbus; + int checksum, i; + uint8_t *prom; + NICInfo *nd; + + nd = qemu_find_nic_info("dp8393x", true, "dp82932"); + if (!nd) { + return; + } + + dev = qdev_new("dp8393x"); + qdev_set_nic_properties(dev, nd); + qdev_prop_set_uint8(dev, "it_shift", 2); + qdev_prop_set_bit(dev, "big_endian", TARGET_BIG_ENDIAN); + object_property_set_link(OBJECT(dev), "dma_mr", + OBJECT(rc4030_dma_mr), &error_abort); + sysbus = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sysbus, &error_fatal); + sysbus_mmio_map(sysbus, 0, 0x80001000); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 4)); + + /* Add MAC address with valid checksum to PROM */ + prom = memory_region_get_ram_ptr(dp8393x_prom); + checksum = 0; + for (i = 0; i < 6; i++) { + prom[i] = nd->macaddr.a[i]; + checksum += prom[i]; + if (checksum > 0xff) { + checksum = (checksum + 1) & 0xff; + } + } + prom[7] = 0xff - checksum; +} + +#define BIOS_SIZE (4 * MiB) + #define MAGNUM_BIOS_SIZE_MAX 0x7e000 #define MAGNUM_BIOS_SIZE \ (BIOS_SIZE < MAGNUM_BIOS_SIZE_MAX ? BIOS_SIZE : MAGNUM_BIOS_SIZE_MAX) @@ -125,7 +170,7 @@ static void mips_jazz_init(MachineState *machine, { MemoryRegion *address_space = get_system_memory(); char *filename; - int bios_size, n, big_endian; + int bios_size, n; Clock *cpuclk; MIPSCPU *cpu; MIPSCPUClass *mcc; @@ -136,14 +181,14 @@ static void mips_jazz_init(MachineState *machine, MemoryRegion *isa_mem = g_new(MemoryRegion, 1); MemoryRegion *isa_io = g_new(MemoryRegion, 1); MemoryRegion *rtc = g_new(MemoryRegion, 1); - MemoryRegion *i8042 = g_new(MemoryRegion, 1); MemoryRegion *dma_dummy = g_new(MemoryRegion, 1); MemoryRegion *dp8393x_prom = g_new(MemoryRegion, 1); - NICInfo *nd; DeviceState *dev, *rc4030; + MMIOKBDState *i8042; SysBusDevice *sysbus; ISABus *isa_bus; ISADevice *pit; + ISADevice *pcspk; DriveInfo *fds[MAX_FD]; MemoryRegion *bios = g_new(MemoryRegion, 1); MemoryRegion *bios2 = g_new(MemoryRegion, 1); @@ -157,12 +202,6 @@ static void mips_jazz_init(MachineState *machine, [JAZZ_PICA61] = {33333333, 4}, }; -#if TARGET_BIG_ENDIAN - big_endian = 1; -#else - big_endian = 0; -#endif - if (machine->ram_size > 256 * MiB) { error_report("RAM size more than 256Mb is not supported"); exit(EXIT_FAILURE); @@ -249,10 +288,12 @@ static void mips_jazz_init(MachineState *machine, /* ISA devices */ i8259 = i8259_init(isa_bus, env->irq[4]); - isa_bus_irqs(isa_bus, i8259); - i8257_dma_init(isa_bus, 0); + isa_bus_register_input_irqs(isa_bus, i8259); + i8257_dma_init(OBJECT(rc4030), isa_bus, 0); pit = i8254_pit_init(isa_bus, 0x40, 0, NULL); - pcspk_init(isa_new(TYPE_PC_SPEAKER), isa_bus, pit); + pcspk = isa_new(TYPE_PC_SPEAKER); + object_property_set_link(OBJECT(pcspk), "pit", OBJECT(pit), &error_fatal); + isa_realize_and_unref(pcspk, isa_bus, &error_fatal); /* Video card */ switch (jazz_model) { @@ -287,48 +328,7 @@ static void mips_jazz_init(MachineState *machine, } /* Network controller */ - for (n = 0; n < nb_nics; n++) { - nd = &nd_table[n]; - if (!nd->model) { - nd->model = g_strdup("dp83932"); - } - if (strcmp(nd->model, "dp83932") == 0) { - int checksum, i; - uint8_t *prom; - - qemu_check_nic_model(nd, "dp83932"); - - dev = qdev_new("dp8393x"); - qdev_set_nic_properties(dev, nd); - qdev_prop_set_uint8(dev, "it_shift", 2); - qdev_prop_set_bit(dev, "big_endian", big_endian > 0); - object_property_set_link(OBJECT(dev), "dma_mr", - OBJECT(rc4030_dma_mr), &error_abort); - sysbus = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(sysbus, &error_fatal); - sysbus_mmio_map(sysbus, 0, 0x80001000); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 4)); - - /* Add MAC address with valid checksum to PROM */ - prom = memory_region_get_ram_ptr(dp8393x_prom); - checksum = 0; - for (i = 0; i < 6; i++) { - prom[i] = nd->macaddr.a[i]; - checksum += prom[i]; - if (checksum > 0xff) { - checksum = (checksum + 1) & 0xff; - } - } - prom[7] = 0xff - checksum; - break; - } else if (is_help_option(nd->model)) { - error_report("Supported NICs: dp83932"); - exit(1); - } else { - error_report("Unsupported NIC: %s", nd->model); - exit(1); - } - } + mips_jazz_init_net(rc4030_dma_mr, rc4030, dp8393x_prom); /* SCSI adapter */ dev = qdev_new(TYPE_SYSBUS_ESP); @@ -353,7 +353,7 @@ static void mips_jazz_init(MachineState *machine, fds[n] = drive_get(IF_FLOPPY, 0, n); } /* FIXME: we should enable DMA with a custom IsaDma device */ - fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), -1, 0x80003000, fds); + fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), 0x80003000, fds); /* Real time clock */ mc146818_rtc_init(isa_bus, 1980, NULL); @@ -361,9 +361,19 @@ static void mips_jazz_init(MachineState *machine, memory_region_add_subregion(address_space, 0x80004000, rtc); /* Keyboard (i8042) */ - i8042_mm_init(qdev_get_gpio_in(rc4030, 6), qdev_get_gpio_in(rc4030, 7), - i8042, 0x1000, 0x1); - memory_region_add_subregion(address_space, 0x80005000, i8042); + i8042 = I8042_MMIO(qdev_new(TYPE_I8042_MMIO)); + qdev_prop_set_uint64(DEVICE(i8042), "mask", 1); + qdev_prop_set_uint32(DEVICE(i8042), "size", 0x1000); + sysbus_realize_and_unref(SYS_BUS_DEVICE(i8042), &error_fatal); + + qdev_connect_gpio_out(DEVICE(i8042), I8042_KBD_IRQ, + qdev_get_gpio_in(rc4030, 6)); + qdev_connect_gpio_out(DEVICE(i8042), I8042_MOUSE_IRQ, + qdev_get_gpio_in(rc4030, 7)); + + memory_region_add_subregion(address_space, 0x80005000, + sysbus_mmio_get_region(SYS_BUS_DEVICE(i8042), + 0)); /* Serial ports */ serial_mm_init(address_space, 0x80006000, 0, diff --git a/hw/mips/loongson3_bootp.h b/hw/mips/loongson3_bootp.h index d525ab745a..1b0dd3b591 100644 --- a/hw/mips/loongson3_bootp.h +++ b/hw/mips/loongson3_bootp.h @@ -25,7 +25,7 @@ struct efi_memory_map_loongson { uint16_t vers; /* version of efi_memory_map */ uint32_t nr_map; /* number of memory_maps */ - uint32_t mem_freq; /* memory frequence */ + uint32_t mem_freq; /* memory frequency */ struct mem_map { uint32_t node_id; /* node_id which memory attached to */ uint32_t mem_type; /* system memory, pci memory, pci io, etc. */ @@ -156,7 +156,7 @@ struct board_devices { struct loongson_special_attribute { uint16_t vers; /* version of this special */ - char special_name[64]; /* special_atribute_name */ + char special_name[64]; /* special_attribute_name */ uint32_t loongson_special_type; /* type of special device */ /* for each device's resource */ struct resource_loongson resource[MAX_RESOURCE_NUMBER]; diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 25534288dd..b10a611a98 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -29,11 +29,9 @@ #include "qemu/datadir.h" #include "qapi/error.h" #include "elf.h" -#include "kvm_mips.h" #include "hw/char/serial.h" #include "hw/intc/loongson_liointc.h" #include "hw/mips/mips.h" -#include "hw/mips/cpudevs.h" #include "hw/mips/fw_cfg.h" #include "hw/mips/loongson3_bootp.h" #include "hw/misc/unimp.h" @@ -406,6 +404,7 @@ static inline void loongson3_virt_devices_init(MachineState *machine, PCIBus *pci_bus; DeviceState *dev; MemoryRegion *mmio_reg, *ecam_reg; + MachineClass *mc = MACHINE_GET_CLASS(machine); LoongsonMachineState *s = LOONGSON_MACHINE(machine); dev = qdev_new(TYPE_GPEX_HOST); @@ -446,21 +445,17 @@ static inline void loongson3_virt_devices_init(MachineState *machine, pci_vga_init(pci_bus); - if (defaults_enabled()) { + if (defaults_enabled() && object_class_by_name("pci-ohci")) { + USBBus *usb_bus; + pci_create_simple(pci_bus, -1, "pci-ohci"); - usb_create_simple(usb_bus_find(-1), "usb-kbd"); - usb_create_simple(usb_bus_find(-1), "usb-tablet"); + usb_bus = USB_BUS(object_resolve_type_unambiguous(TYPE_USB_BUS, + &error_abort)); + usb_create_simple(usb_bus, "usb-kbd"); + usb_create_simple(usb_bus, "usb-tablet"); } - for (i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - - if (!nd->model) { - nd->model = g_strdup("virtio"); - } - - pci_nic_init_nofail(nd, pci_bus, nd->model, NULL); - } + pci_init_nic_devices(pci_bus, mc->default_nic); } static void mips_loongson3_virt_init(MachineState *machine) @@ -486,8 +481,8 @@ static void mips_loongson3_virt_init(MachineState *machine) if (!machine->cpu_type) { machine->cpu_type = MIPS_CPU_TYPE_NAME("Loongson-3A1000"); } - if (!strstr(machine->cpu_type, "Loongson-3A1000")) { - error_report("Loongson-3/TCG needs cpu type Loongson-3A1000"); + if (!cpu_type_supports_isa(machine->cpu_type, INSN_LOONGSON3A)) { + error_report("Loongson-3/TCG needs a Loongson-3 series cpu"); exit(1); } } else { @@ -617,8 +612,8 @@ static void loongson3v_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = LOONGSON_MAX_VCPUS; mc->default_ram_id = "loongson3.highram"; mc->default_ram_size = 1600 * MiB; - mc->kvm_type = mips_kvm_type; mc->minimum_page_bits = 14; + mc->default_nic = "virtio-net-pci"; } static const TypeInfo loongson3_machine_types[] = { diff --git a/hw/mips/malta.c b/hw/mips/malta.c index 9ffdc5b8f1..af74008c82 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -26,6 +26,7 @@ #include "qemu/units.h" #include "qemu/bitops.h" #include "qemu/datadir.h" +#include "qemu/guest-random.h" #include "hw/clock.h" #include "hw/southbridge/piix.h" #include "hw/isa/superio.h" @@ -35,11 +36,11 @@ #include "hw/i2c/smbus_eeprom.h" #include "hw/block/flash.h" #include "hw/mips/mips.h" -#include "hw/mips/cpudevs.h" +#include "hw/mips/bootloader.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" #include "qemu/log.h" -#include "hw/mips/bios.h" -#include "hw/ide.h" +#include "hw/ide/pci.h" #include "hw/irq.h" #include "hw/loader.h" #include "elf.h" @@ -51,11 +52,13 @@ #include "sysemu/runstate.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "hw/misc/empty_slot.h" #include "sysemu/kvm.h" #include "semihosting/semihost.h" #include "hw/mips/cps.h" #include "hw/qdev-clock.h" +#include "target/mips/internal.h" +#include "trace.h" +#include "cpu.h" #define ENVP_PADDR 0x2000 #define ENVP_VADDR cpu_mips_phys_to_kseg0(NULL, ENVP_PADDR) @@ -68,8 +71,9 @@ #define RESET_ADDRESS 0x1fc00000ULL #define FLASH_SIZE 0x400000 +#define BIOS_SIZE (4 * MiB) -#define MAX_IDE_BUS 2 +#define PIIX4_PCI_DEVFN PCI_DEVFN(10, 0) typedef struct { MemoryRegion iomem; @@ -88,6 +92,12 @@ typedef struct { bool display_inited; } MaltaFPGAState; +#if TARGET_BIG_ENDIAN +#define BIOS_FILENAME "mips_bios.bin" +#else +#define BIOS_FILENAME "mipsel_bios.bin" +#endif + #define TYPE_MIPS_MALTA "mips-malta" OBJECT_DECLARE_SIMPLE_TYPE(MaltaState, MIPS_MALTA) @@ -106,11 +116,10 @@ static struct _loaderparams { } loaderparams; /* Malta FPGA */ -static void malta_fpga_update_display(void *opaque) +static void malta_fpga_update_display_leds(MaltaFPGAState *s) { char leds_text[9]; int i; - MaltaFPGAState *s = opaque; for (i = 7 ; i >= 0 ; i--) { if (s->leds & (1 << i)) { @@ -121,8 +130,14 @@ static void malta_fpga_update_display(void *opaque) } leds_text[8] = '\0'; + trace_malta_fpga_leds(leds_text); qemu_chr_fe_printf(&s->display, "\e[H\n\n|\e[32m%-8.8s\e[00m|\r\n", leds_text); +} + +static void malta_fpga_update_display_ascii(MaltaFPGAState *s) +{ + trace_malta_fpga_display(s->display_text); qemu_chr_fe_printf(&s->display, "\n\n\n\n|\e[31m%-8.8s\e[00m|", s->display_text); } @@ -197,7 +212,7 @@ static eeprom24c0x_t spd_eeprom = { static void generate_eeprom_spd(uint8_t *eeprom, ram_addr_t ram_size) { - enum { SDR = 0x4, DDR2 = 0x8 } type; + enum sdram_type type; uint8_t *spd = spd_eeprom.contents; uint8_t nbanks = 0; uint16_t density = 0; @@ -457,13 +472,13 @@ static void malta_fpga_write(void *opaque, hwaddr addr, /* LEDBAR Register */ case 0x00408: s->leds = val & 0xff; - malta_fpga_update_display(s); + malta_fpga_update_display_leds(s); break; /* ASCIIWORD Register */ case 0x00410: snprintf(s->display_text, 9, "%08X", (uint32_t)val); - malta_fpga_update_display(s); + malta_fpga_update_display_ascii(s); break; /* ASCIIPOS0 to ASCIIPOS7 Registers */ @@ -476,7 +491,7 @@ static void malta_fpga_write(void *opaque, hwaddr addr, case 0x00448: case 0x00450: s->display_text[(saddr - 0x00418) >> 3] = (char) val; - malta_fpga_update_display(s); + malta_fpga_update_display_ascii(s); break; /* SOFTRES Register */ @@ -597,18 +612,81 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space, /* Network support */ static void network_init(PCIBus *pci_bus) { - int i; + /* The malta board has a PCNet card using PCI SLOT 11 */ + pci_init_nic_in_slot(pci_bus, "pcnet", NULL, "0b"); + pci_init_nic_devices(pci_bus, "pcnet"); +} - for (i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - const char *default_devaddr = NULL; +static void bl_setup_gt64120_jump_kernel(void **p, uint64_t run_addr, + uint64_t kernel_entry) +{ + static const char pci_pins_cfg[PCI_NUM_PINS] = { + 10, 10, 11, 11 /* PIIX IRQRC[A:D] */ + }; - if (i == 0 && (!nd->model || strcmp(nd->model, "pcnet") == 0)) - /* The malta board has a PCNet card using PCI SLOT 11 */ - default_devaddr = "0b"; + /* Bus endianness is always reversed */ +#if TARGET_BIG_ENDIAN +#define cpu_to_gt32(x) (x) +#else +#define cpu_to_gt32(x) bswap32(x) +#endif - pci_nic_init_nofail(nd, pci_bus, "pcnet", default_devaddr); - } + /* setup MEM-to-PCI0 mapping as done by YAMON */ + + /* move GT64120 registers from 0x14000000 to 0x1be00000 */ + bl_gen_write_u32(p, /* GT_ISD */ + cpu_mips_phys_to_kseg1(NULL, 0x14000000 + 0x68), + cpu_to_gt32(0x1be00000 << 3)); + + /* setup PCI0 io window to 0x18000000-0x181fffff */ + bl_gen_write_u32(p, /* GT_PCI0IOLD */ + cpu_mips_phys_to_kseg1(NULL, 0x1be00000 + 0x48), + cpu_to_gt32(0x18000000 << 3)); + bl_gen_write_u32(p, /* GT_PCI0IOHD */ + cpu_mips_phys_to_kseg1(NULL, 0x1be00000 + 0x50), + cpu_to_gt32(0x08000000 << 3)); + + /* setup PCI0 mem windows */ + bl_gen_write_u32(p, /* GT_PCI0M0LD */ + cpu_mips_phys_to_kseg1(NULL, 0x1be00000 + 0x58), + cpu_to_gt32(0x10000000 << 3)); + bl_gen_write_u32(p, /* GT_PCI0M0HD */ + cpu_mips_phys_to_kseg1(NULL, 0x1be00000 + 0x60), + cpu_to_gt32(0x07e00000 << 3)); + bl_gen_write_u32(p, /* GT_PCI0M1LD */ + cpu_mips_phys_to_kseg1(NULL, 0x1be00000 + 0x80), + cpu_to_gt32(0x18200000 << 3)); + bl_gen_write_u32(p, /* GT_PCI0M1HD */ + cpu_mips_phys_to_kseg1(NULL, 0x1be00000 + 0x88), + cpu_to_gt32(0x0bc00000 << 3)); + +#undef cpu_to_gt32 + + /* + * The PIIX ISA bridge is on PCI bus 0 dev 10 func 0. + * Load the PIIX IRQC[A:D] routing config address, then + * write routing configuration to the config data register. + */ + bl_gen_write_u32(p, /* GT_PCI0_CFGADDR */ + cpu_mips_phys_to_kseg1(NULL, 0x1be00000 + 0xcf8), + tswap32((1 << 31) /* ConfigEn */ + | PCI_BUILD_BDF(0, PIIX4_PCI_DEVFN) << 8 + | PIIX_PIRQCA)); + bl_gen_write_u32(p, /* GT_PCI0_CFGDATA */ + cpu_mips_phys_to_kseg1(NULL, 0x1be00000 + 0xcfc), + tswap32(ldl_be_p(pci_pins_cfg))); + + bl_gen_jump_kernel(p, + true, ENVP_VADDR - 64, + /* + * If semihosting is used, arguments have already + * been passed, so we preserve $a0. + */ + !semihosting_get_argc(), 2, + true, ENVP_VADDR, + true, ENVP_VADDR + 8, + true, loaderparams.ram_low_size, + kernel_entry); } static void write_bootloader_nanomips(uint8_t *base, uint64_t run_addr, @@ -619,11 +697,6 @@ static void write_bootloader_nanomips(uint8_t *base, uint64_t run_addr, /* Small bootloader */ p = (uint16_t *)base; -#define NM_HI1(VAL) (((VAL) >> 16) & 0x1f) -#define NM_HI2(VAL) \ - (((VAL) & 0xf000) | (((VAL) >> 19) & 0xffc) | (((VAL) >> 31) & 0x1)) -#define NM_LO(VAL) ((VAL) & 0xfff) - stw_p(p++, 0x2800); stw_p(p++, 0x001c); /* bc to_here */ stw_p(p++, 0x8000); stw_p(p++, 0xc000); @@ -642,175 +715,8 @@ static void write_bootloader_nanomips(uint8_t *base, uint64_t run_addr, /* nop */ /* to_here: */ - if (semihosting_get_argc()) { - /* Preserve a0 content as arguments have been passed */ - stw_p(p++, 0x8000); stw_p(p++, 0xc000); - /* nop */ - } else { - stw_p(p++, 0x0080); stw_p(p++, 0x0002); - /* li a0,2 */ - } - stw_p(p++, 0xe3a0 | NM_HI1(ENVP_VADDR - 64)); - - stw_p(p++, NM_HI2(ENVP_VADDR - 64)); - /* lui sp,%hi(ENVP_VADDR - 64) */ - - stw_p(p++, 0x83bd); stw_p(p++, NM_LO(ENVP_VADDR - 64)); - /* ori sp,sp,%lo(ENVP_VADDR - 64) */ - - stw_p(p++, 0xe0a0 | NM_HI1(ENVP_VADDR)); - - stw_p(p++, NM_HI2(ENVP_VADDR)); - /* lui a1,%hi(ENVP_VADDR) */ - - stw_p(p++, 0x80a5); stw_p(p++, NM_LO(ENVP_VADDR)); - /* ori a1,a1,%lo(ENVP_VADDR) */ - - stw_p(p++, 0xe0c0 | NM_HI1(ENVP_VADDR + 8)); - - stw_p(p++, NM_HI2(ENVP_VADDR + 8)); - /* lui a2,%hi(ENVP_VADDR + 8) */ - - stw_p(p++, 0x80c6); stw_p(p++, NM_LO(ENVP_VADDR + 8)); - /* ori a2,a2,%lo(ENVP_VADDR + 8) */ - - stw_p(p++, 0xe0e0 | NM_HI1(loaderparams.ram_low_size)); - - stw_p(p++, NM_HI2(loaderparams.ram_low_size)); - /* lui a3,%hi(loaderparams.ram_low_size) */ - - stw_p(p++, 0x80e7); stw_p(p++, NM_LO(loaderparams.ram_low_size)); - /* ori a3,a3,%lo(loaderparams.ram_low_size) */ - - /* - * Load BAR registers as done by YAMON: - * - * - set up PCI0 I/O BARs from 0x18000000 to 0x181fffff - * - set up PCI0 MEM0 at 0x10000000, size 0x8000000 - * - set up PCI0 MEM1 at 0x18200000, size 0xbe00000 - * - */ - stw_p(p++, 0xe040); stw_p(p++, 0x0681); - /* lui t1, %hi(0xb4000000) */ - -#if TARGET_BIG_ENDIAN - - stw_p(p++, 0xe020); stw_p(p++, 0x0be1); - /* lui t0, %hi(0xdf000000) */ - - /* 0x68 corresponds to GT_ISD (from hw/mips/gt64xxx_pci.c) */ - stw_p(p++, 0x8422); stw_p(p++, 0x9068); - /* sw t0, 0x68(t1) */ - - stw_p(p++, 0xe040); stw_p(p++, 0x077d); - /* lui t1, %hi(0xbbe00000) */ - - stw_p(p++, 0xe020); stw_p(p++, 0x0801); - /* lui t0, %hi(0xc0000000) */ - - /* 0x48 corresponds to GT_PCI0IOLD */ - stw_p(p++, 0x8422); stw_p(p++, 0x9048); - /* sw t0, 0x48(t1) */ - - stw_p(p++, 0xe020); stw_p(p++, 0x0800); - /* lui t0, %hi(0x40000000) */ - - /* 0x50 corresponds to GT_PCI0IOHD */ - stw_p(p++, 0x8422); stw_p(p++, 0x9050); - /* sw t0, 0x50(t1) */ - - stw_p(p++, 0xe020); stw_p(p++, 0x0001); - /* lui t0, %hi(0x80000000) */ - - /* 0x58 corresponds to GT_PCI0M0LD */ - stw_p(p++, 0x8422); stw_p(p++, 0x9058); - /* sw t0, 0x58(t1) */ - - stw_p(p++, 0xe020); stw_p(p++, 0x07e0); - /* lui t0, %hi(0x3f000000) */ - - /* 0x60 corresponds to GT_PCI0M0HD */ - stw_p(p++, 0x8422); stw_p(p++, 0x9060); - /* sw t0, 0x60(t1) */ - - stw_p(p++, 0xe020); stw_p(p++, 0x0821); - /* lui t0, %hi(0xc1000000) */ - - /* 0x80 corresponds to GT_PCI0M1LD */ - stw_p(p++, 0x8422); stw_p(p++, 0x9080); - /* sw t0, 0x80(t1) */ - - stw_p(p++, 0xe020); stw_p(p++, 0x0bc0); - /* lui t0, %hi(0x5e000000) */ - -#else - - stw_p(p++, 0x0020); stw_p(p++, 0x00df); - /* addiu[32] t0, $0, 0xdf */ - - /* 0x68 corresponds to GT_ISD */ - stw_p(p++, 0x8422); stw_p(p++, 0x9068); - /* sw t0, 0x68(t1) */ - - /* Use kseg2 remapped address 0x1be00000 */ - stw_p(p++, 0xe040); stw_p(p++, 0x077d); - /* lui t1, %hi(0xbbe00000) */ - - stw_p(p++, 0x0020); stw_p(p++, 0x00c0); - /* addiu[32] t0, $0, 0xc0 */ - - /* 0x48 corresponds to GT_PCI0IOLD */ - stw_p(p++, 0x8422); stw_p(p++, 0x9048); - /* sw t0, 0x48(t1) */ - - stw_p(p++, 0x0020); stw_p(p++, 0x0040); - /* addiu[32] t0, $0, 0x40 */ - - /* 0x50 corresponds to GT_PCI0IOHD */ - stw_p(p++, 0x8422); stw_p(p++, 0x9050); - /* sw t0, 0x50(t1) */ - - stw_p(p++, 0x0020); stw_p(p++, 0x0080); - /* addiu[32] t0, $0, 0x80 */ - - /* 0x58 corresponds to GT_PCI0M0LD */ - stw_p(p++, 0x8422); stw_p(p++, 0x9058); - /* sw t0, 0x58(t1) */ - - stw_p(p++, 0x0020); stw_p(p++, 0x003f); - /* addiu[32] t0, $0, 0x3f */ - - /* 0x60 corresponds to GT_PCI0M0HD */ - stw_p(p++, 0x8422); stw_p(p++, 0x9060); - /* sw t0, 0x60(t1) */ - - stw_p(p++, 0x0020); stw_p(p++, 0x00c1); - /* addiu[32] t0, $0, 0xc1 */ - - /* 0x80 corresponds to GT_PCI0M1LD */ - stw_p(p++, 0x8422); stw_p(p++, 0x9080); - /* sw t0, 0x80(t1) */ - - stw_p(p++, 0x0020); stw_p(p++, 0x005e); - /* addiu[32] t0, $0, 0x5e */ - -#endif - - /* 0x88 corresponds to GT_PCI0M1HD */ - stw_p(p++, 0x8422); stw_p(p++, 0x9088); - /* sw t0, 0x88(t1) */ - - stw_p(p++, 0xe320 | NM_HI1(kernel_entry)); - - stw_p(p++, NM_HI2(kernel_entry)); - /* lui t9,%hi(kernel_entry) */ - - stw_p(p++, 0x8339); stw_p(p++, NM_LO(kernel_entry)); - /* ori t9,t9,%lo(kernel_entry) */ - - stw_p(p++, 0x4bf9); stw_p(p++, 0x0000); - /* jalrc t8 */ + bl_setup_gt64120_jump_kernel((void **)&p, run_addr, kernel_entry); } /* @@ -866,88 +772,16 @@ static void write_bootloader(uint8_t *base, uint64_t run_addr, /* Second part of the bootloader */ p = (uint32_t *) (base + 0x580); - if (semihosting_get_argc()) { - /* Preserve a0 content as arguments have been passed */ - stl_p(p++, 0x00000000); /* nop */ - } else { - stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */ - } + /* + * Load BAR registers as done by YAMON: + * + * - set up PCI0 I/O BARs from 0x18000000 to 0x181fffff + * - set up PCI0 MEM0 at 0x10000000, size 0x7e00000 + * - set up PCI0 MEM1 at 0x18200000, size 0xbc00000 + * + */ - /* lui sp, high(ENVP_VADDR) */ - stl_p(p++, 0x3c1d0000 | (((ENVP_VADDR - 64) >> 16) & 0xffff)); - /* ori sp, sp, low(ENVP_VADDR) */ - stl_p(p++, 0x37bd0000 | ((ENVP_VADDR - 64) & 0xffff)); - /* lui a1, high(ENVP_VADDR) */ - stl_p(p++, 0x3c050000 | ((ENVP_VADDR >> 16) & 0xffff)); - /* ori a1, a1, low(ENVP_VADDR) */ - stl_p(p++, 0x34a50000 | (ENVP_VADDR & 0xffff)); - /* lui a2, high(ENVP_VADDR + 8) */ - stl_p(p++, 0x3c060000 | (((ENVP_VADDR + 8) >> 16) & 0xffff)); - /* ori a2, a2, low(ENVP_VADDR + 8) */ - stl_p(p++, 0x34c60000 | ((ENVP_VADDR + 8) & 0xffff)); - /* lui a3, high(ram_low_size) */ - stl_p(p++, 0x3c070000 | (loaderparams.ram_low_size >> 16)); - /* ori a3, a3, low(ram_low_size) */ - stl_p(p++, 0x34e70000 | (loaderparams.ram_low_size & 0xffff)); - - /* Load BAR registers as done by YAMON */ - stl_p(p++, 0x3c09b400); /* lui t1, 0xb400 */ - -#if TARGET_BIG_ENDIAN - stl_p(p++, 0x3c08df00); /* lui t0, 0xdf00 */ -#else - stl_p(p++, 0x340800df); /* ori t0, r0, 0x00df */ -#endif - stl_p(p++, 0xad280068); /* sw t0, 0x0068(t1) */ - - stl_p(p++, 0x3c09bbe0); /* lui t1, 0xbbe0 */ - -#if TARGET_BIG_ENDIAN - stl_p(p++, 0x3c08c000); /* lui t0, 0xc000 */ -#else - stl_p(p++, 0x340800c0); /* ori t0, r0, 0x00c0 */ -#endif - stl_p(p++, 0xad280048); /* sw t0, 0x0048(t1) */ -#if TARGET_BIG_ENDIAN - stl_p(p++, 0x3c084000); /* lui t0, 0x4000 */ -#else - stl_p(p++, 0x34080040); /* ori t0, r0, 0x0040 */ -#endif - stl_p(p++, 0xad280050); /* sw t0, 0x0050(t1) */ - -#if TARGET_BIG_ENDIAN - stl_p(p++, 0x3c088000); /* lui t0, 0x8000 */ -#else - stl_p(p++, 0x34080080); /* ori t0, r0, 0x0080 */ -#endif - stl_p(p++, 0xad280058); /* sw t0, 0x0058(t1) */ -#if TARGET_BIG_ENDIAN - stl_p(p++, 0x3c083f00); /* lui t0, 0x3f00 */ -#else - stl_p(p++, 0x3408003f); /* ori t0, r0, 0x003f */ -#endif - stl_p(p++, 0xad280060); /* sw t0, 0x0060(t1) */ - -#if TARGET_BIG_ENDIAN - stl_p(p++, 0x3c08c100); /* lui t0, 0xc100 */ -#else - stl_p(p++, 0x340800c1); /* ori t0, r0, 0x00c1 */ -#endif - stl_p(p++, 0xad280080); /* sw t0, 0x0080(t1) */ -#if TARGET_BIG_ENDIAN - stl_p(p++, 0x3c085e00); /* lui t0, 0x5e00 */ -#else - stl_p(p++, 0x3408005e); /* ori t0, r0, 0x005e */ -#endif - 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 */ + bl_setup_gt64120_jump_kernel((void **)&p, run_addr, kernel_entry); /* YAMON subroutines */ p = (uint32_t *) (base + 0x800); @@ -991,7 +825,6 @@ static void write_bootloader(uint8_t *base, uint64_t run_addr, stl_p(p++, 0x00000000); /* nop */ stl_p(p++, 0x03e00009); /* jalr ra */ stl_p(p++, 0xa1040000); /* sb a0,0(t0) */ - } static void G_GNUC_PRINTF(3, 4) prom_set(uint32_t *prom_buf, int index, @@ -1017,28 +850,34 @@ static void G_GNUC_PRINTF(3, 4) prom_set(uint32_t *prom_buf, int index, va_end(ap); } +static void reinitialize_rng_seed(void *opaque) +{ + char *rng_seed_hex = opaque; + uint8_t rng_seed[32]; + + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + for (size_t i = 0; i < sizeof(rng_seed); ++i) { + sprintf(rng_seed_hex + i * 2, "%02x", rng_seed[i]); + } +} + /* Kernel */ static uint64_t load_kernel(void) { uint64_t kernel_entry, kernel_high, initrd_size; long kernel_size; ram_addr_t initrd_offset; - int big_endian; uint32_t *prom_buf; long prom_size; int prom_index = 0; - uint64_t (*xlate_to_kseg0) (void *opaque, uint64_t addr); - -#if TARGET_BIG_ENDIAN - big_endian = 1; -#else - big_endian = 0; -#endif + uint8_t rng_seed[32]; + char rng_seed_hex[sizeof(rng_seed) * 2 + 1]; + size_t rng_seed_prom_offset; kernel_size = load_elf(loaderparams.kernel_filename, NULL, cpu_mips_kseg0_to_phys, NULL, &kernel_entry, NULL, - &kernel_high, NULL, big_endian, EM_MIPS, + &kernel_high, NULL, TARGET_BIG_ENDIAN, EM_MIPS, 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", @@ -1048,19 +887,10 @@ static uint64_t load_kernel(void) } /* Check where the kernel has been linked */ - if (kernel_entry & 0x80000000ll) { - if (kvm_enabled()) { - error_report("KVM guest kernels must be linked in useg. " - "Did you forget to enable CONFIG_KVM_GUEST?"); - exit(1); - } - - xlate_to_kseg0 = cpu_mips_phys_to_kseg0; - } else { - /* if kernel entry is in useg it is probably a KVM T&E kernel */ - mips_um_ksegs_enable(); - - xlate_to_kseg0 = cpu_mips_kvm_um_phys_to_kseg0; + if (kernel_entry <= USEG_LIMIT) { + error_report("Trap-and-Emul kernels (Linux CONFIG_KVM_GUEST)" + " are not supported"); + exit(1); } /* load initrd */ @@ -1101,7 +931,7 @@ static uint64_t load_kernel(void) if (initrd_size > 0) { prom_set(prom_buf, prom_index++, "rd_start=0x%" PRIx64 " rd_size=%" PRId64 " %s", - xlate_to_kseg0(NULL, initrd_offset), + cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size, loaderparams.kernel_cmdline); } else { prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_cmdline); @@ -1115,9 +945,21 @@ static uint64_t load_kernel(void) prom_set(prom_buf, prom_index++, "modetty0"); prom_set(prom_buf, prom_index++, "38400n8r"); + + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + for (size_t i = 0; i < sizeof(rng_seed); ++i) { + sprintf(rng_seed_hex + i * 2, "%02x", rng_seed[i]); + } + prom_set(prom_buf, prom_index++, "rngseed"); + rng_seed_prom_offset = prom_index * ENVP_ENTRY_SIZE + + sizeof(uint32_t) * ENVP_NB_ENTRIES; + prom_set(prom_buf, prom_index++, "%s", rng_seed_hex); + prom_set(prom_buf, prom_index++, NULL); rom_add_blob_fixed("prom", prom_buf, prom_size, ENVP_PADDR); + qemu_register_reset_nosnapshotload(reinitialize_rng_seed, + rom_ptr(ENVP_PADDR, prom_size) + rng_seed_prom_offset); g_free(prom_buf); return kernel_entry; @@ -1139,6 +981,31 @@ static void malta_mips_config(MIPSCPU *cpu) } } +static int malta_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) +{ + int slot; + + slot = PCI_SLOT(pci_dev->devfn); + + switch (slot) { + /* PIIX4 USB */ + case 10: + return 3; + /* AMD 79C973 Ethernet */ + case 11: + return 1; + /* Crystal 4281 Sound */ + case 12: + return 2; + /* PCI slot 1 to 4 */ + case 18 ... 21: + return ((slot - 18) + irq_num) & 0x03; + /* Unknown device, don't do any translation */ + default: + return irq_num; + } +} + static void main_cpu_reset(void *opaque) { MIPSCPU *cpu = opaque; @@ -1156,11 +1023,6 @@ static void main_cpu_reset(void *opaque) } malta_mips_config(cpu); - - if (kvm_enabled()) { - /* Start running from the bootloader we wrote to end of RAM */ - env->active_tc.PC = 0x40000000 + loaderparams.ram_low_size; - } } static void create_cpu_without_cps(MachineState *ms, MaltaState *s, @@ -1191,7 +1053,7 @@ static void create_cps(MachineState *ms, MaltaState *s, object_initialize_child(OBJECT(s), "cps", &s->cps, TYPE_MIPS_CPS); object_property_set_str(OBJECT(&s->cps), "cpu-type", ms->cpu_type, &error_fatal); - object_property_set_int(OBJECT(&s->cps), "num-vp", ms->smp.cpus, + object_property_set_uint(OBJECT(&s->cps), "num-vp", ms->smp.cpus, &error_fatal); qdev_connect_clock_in(DEVICE(&s->cps), "clk-in", s->cpuclk); sysbus_realize(SYS_BUS_DEVICE(&s->cps), &error_fatal); @@ -1235,8 +1097,8 @@ void mips_malta_init(MachineState *machine) I2CBus *smbus; DriveInfo *dinfo; int fl_idx = 0; - int be; MaltaState *s; + PCIDevice *piix4; DeviceState *dev; s = MIPS_MALTA(qdev_new(TYPE_MIPS_MALTA)); @@ -1271,12 +1133,6 @@ void mips_malta_init(MachineState *machine) ram_low_postio); } -#if TARGET_BIG_ENDIAN - be = 1; -#else - be = 0; -#endif - /* FPGA */ /* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */ @@ -1288,18 +1144,13 @@ void mips_malta_init(MachineState *machine) FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, 65536, - 4, 0x0000, 0x0000, 0x0000, 0x0000, be); + 4, 0x0000, 0x0000, 0x0000, 0x0000, + TARGET_BIG_ENDIAN); bios = pflash_cfi01_get_memory(fl); fl_idx++; if (kernel_filename) { ram_low_size = MIN(ram_size, 256 * MiB); - /* For KVM we reserve 1MB of RAM for running bootloader */ - if (kvm_enabled()) { - ram_low_size -= 0x100000; - bootloader_run_addr = cpu_mips_kvm_um_phys_to_kseg0(NULL, ram_low_size); - } else { - bootloader_run_addr = cpu_mips_phys_to_kseg0(NULL, RESET_ADDRESS); - } + bootloader_run_addr = cpu_mips_phys_to_kseg0(NULL, RESET_ADDRESS); /* Write a small bootloader to the flash location. */ loaderparams.ram_size = ram_size; @@ -1316,20 +1167,8 @@ void mips_malta_init(MachineState *machine) write_bootloader_nanomips(memory_region_get_ram_ptr(bios), bootloader_run_addr, kernel_entry); } - if (kvm_enabled()) { - /* Write the bootloader code @ the end of RAM, 1MB reserved */ - write_bootloader(memory_region_get_ram_ptr(ram_low_preio) + - ram_low_size, - bootloader_run_addr, kernel_entry); - } } else { target_long bios_size = FLASH_SIZE; - /* The flash region isn't executable from a KVM guest */ - if (kvm_enabled()) { - error_report("KVM enabled but no -kernel argument was specified. " - "Booting from flash is not supported with KVM."); - exit(1); - } /* Load firmware from flash. */ if (!dinfo) { /* Load a BIOS image. */ @@ -1389,22 +1228,27 @@ void mips_malta_init(MachineState *machine) stl_p(memory_region_get_ram_ptr(bios_copy) + 0x10, 0x00000420); /* Northbridge */ - dev = sysbus_create_simple("gt64120", -1, NULL); + dev = qdev_new("gt64120"); + qdev_prop_set_bit(dev, "cpu-little-endian", !TARGET_BIG_ENDIAN); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci")); - /* - * The whole address space decoded by the GT-64120A doesn't generate - * exception when accessing invalid memory. Create an empty slot to - * emulate this feature. - */ - empty_slot_init("GT64120", 0, 0x20000000); + pci_bus_map_irqs(pci_bus, malta_pci_slot_get_pirq); /* Southbridge */ - dev = piix4_create(pci_bus, &isa_bus, &smbus); + piix4 = pci_new_multifunction(PIIX4_PCI_DEVFN, TYPE_PIIX4_PCI_DEVICE); + qdev_prop_set_uint32(DEVICE(piix4), "smb_io_base", 0x1100); + pci_realize_and_unref(piix4, pci_bus, &error_fatal); + isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(piix4), "isa.0")); + + dev = DEVICE(object_resolve_path_component(OBJECT(piix4), "ide")); + pci_ide_create_devs(PCI_DEVICE(dev)); /* Interrupt controller */ - qdev_connect_gpio_out_named(dev, "intr", 0, i8259_irq); + qdev_connect_gpio_out_named(DEVICE(piix4), "intr", 0, i8259_irq); /* generate SPD EEPROM data */ + dev = DEVICE(object_resolve_path_component(OBJECT(piix4), "pm")); + smbus = I2C_BUS(qdev_get_child_bus(dev, "i2c")); generate_eeprom_spd(&smbus_eeprom_buf[0 * 256], ram_size); generate_eeprom_serial(&smbus_eeprom_buf[6 * 256]); smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size); @@ -1435,6 +1279,14 @@ static const TypeInfo mips_malta_device = { .instance_init = mips_malta_instance_init, }; +GlobalProperty malta_compat[] = { + { "PIIX4_PM", "memory-hotplug-support", "off" }, + { "PIIX4_PM", "acpi-pci-hotplug-with-bridge-support", "off" }, + { "PIIX4_PM", "acpi-root-pci-hotplug", "off" }, + { "PIIX4_PM", "x-not-migrate-acpi-index", "true" }, +}; +const size_t malta_compat_len = G_N_ELEMENTS(malta_compat); + static void mips_malta_machine_init(MachineClass *mc) { mc->desc = "MIPS Malta Core LV"; @@ -1448,6 +1300,7 @@ static void mips_malta_machine_init(MachineClass *mc) mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf"); #endif mc->default_ram_id = "mips_malta.ram"; + compat_props_add(mc->compat_props, malta_compat, malta_compat_len); } DEFINE_MACHINE("malta", mips_malta_machine_init) diff --git a/hw/mips/meson.build b/hw/mips/meson.build index dd0101ad4d..f06d88f343 100644 --- a/hw/mips/meson.build +++ b/hw/mips/meson.build @@ -2,10 +2,10 @@ mips_ss = ss.source_set() mips_ss.add(files('bootloader.c', 'mips_int.c')) mips_ss.add(when: 'CONFIG_FW_CFG_MIPS', if_true: files('fw_cfg.c')) mips_ss.add(when: 'CONFIG_LOONGSON3V', if_true: files('loongson3_bootp.c', 'loongson3_virt.c')) -mips_ss.add(when: 'CONFIG_MALTA', if_true: files('gt64xxx_pci.c', 'malta.c')) +mips_ss.add(when: 'CONFIG_MALTA', if_true: files('malta.c')) mips_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('cps.c')) -if 'CONFIG_TCG' in config_all +if 'CONFIG_TCG' in config_all_accel mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c')) mips_ss.add(when: 'CONFIG_MIPSSIM', if_true: files('mipssim.c')) mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c')) diff --git a/hw/mips/mips_int.c b/hw/mips/mips_int.c index 2db5e10fe0..eef2fd2cd1 100644 --- a/hw/mips/mips_int.c +++ b/hw/mips/mips_int.c @@ -23,7 +23,6 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "hw/irq.h" -#include "hw/mips/cpudevs.h" #include "sysemu/kvm.h" #include "kvm_mips.h" @@ -32,17 +31,12 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level) MIPSCPU *cpu = opaque; CPUMIPSState *env = &cpu->env; CPUState *cs = CPU(cpu); - bool locked = false; if (irq < 0 || irq > 7) { return; } - /* Make sure locking works even if BQL is already held by the caller */ - if (!qemu_mutex_iothread_locked()) { - locked = true; - qemu_mutex_lock_iothread(); - } + BQL_LOCK_GUARD(); if (level) { env->CP0_Cause |= 1 << (irq + CP0Ca_IP); @@ -59,10 +53,6 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level) } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } - - if (locked) { - qemu_mutex_unlock_iothread(); - } } void cpu_mips_irq_init_cpu(MIPSCPU *cpu) diff --git a/hw/mips/mipssim.c b/hw/mips/mipssim.c index 39f64448f2..9170d6c474 100644 --- a/hw/mips/mipssim.c +++ b/hw/mips/mipssim.c @@ -28,15 +28,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/datadir.h" +#include "exec/address-spaces.h" #include "hw/clock.h" #include "hw/mips/mips.h" -#include "hw/mips/cpudevs.h" #include "hw/char/serial.h" -#include "hw/isa/isa.h" #include "net/net.h" #include "sysemu/sysemu.h" #include "hw/boards.h" -#include "hw/mips/bios.h" #include "hw/loader.h" #include "elf.h" #include "hw/sysbus.h" @@ -44,6 +42,15 @@ #include "qemu/error-report.h" #include "sysemu/qtest.h" #include "sysemu/reset.h" +#include "cpu.h" + +#define BIOS_SIZE (4 * MiB) + +#if TARGET_BIG_ENDIAN +#define BIOS_FILENAME "mips_bios.bin" +#else +#define BIOS_FILENAME "mipsel_bios.bin" +#endif static struct _loaderparams { int ram_size; @@ -62,18 +69,11 @@ static uint64_t load_kernel(void) uint64_t entry, kernel_high, initrd_size; long kernel_size; ram_addr_t initrd_offset; - int big_endian; - -#if TARGET_BIG_ENDIAN - big_endian = 1; -#else - big_endian = 0; -#endif kernel_size = load_elf(loaderparams.kernel_filename, NULL, cpu_mips_kseg0_to_phys, NULL, &entry, NULL, - &kernel_high, NULL, big_endian, + &kernel_high, NULL, TARGET_BIG_ENDIAN, EM_MIPS, 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", @@ -118,13 +118,15 @@ static void main_cpu_reset(void *opaque) } } -static void mipsnet_init(int base, qemu_irq irq, NICInfo *nd) +static void mipsnet_init(int base, qemu_irq irq) { DeviceState *dev; SysBusDevice *s; - dev = qdev_new("mipsnet"); - qdev_set_nic_properties(dev, nd); + dev = qemu_create_nic_device("mipsnet", true, NULL); + if (!dev) { + return; + } s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); @@ -204,7 +206,11 @@ mips_mipssim_init(MachineState *machine) cpu_mips_irq_init_cpu(cpu); cpu_mips_clock_init(cpu); - /* Register 64 KB of ISA IO space at 0x1fd00000. */ + /* + * Register 64 KB of ISA IO space at 0x1fd00000. But without interrupts + * (except for the hardcoded serial port interrupt) -device cannot work, + * so do not expose the ISA bus to the user. + */ memory_region_init_alias(isa, NULL, "isa_mmio", get_system_io(), 0, 0x00010000); memory_region_add_subregion(get_system_memory(), 0x1fd00000, isa); @@ -221,13 +227,12 @@ mips_mipssim_init(MachineState *machine) qdev_prop_set_uint8(dev, "endianness", DEVICE_LITTLE_ENDIAN); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, env->irq[4]); - sysbus_add_io(SYS_BUS_DEVICE(dev), 0x3f8, + memory_region_add_subregion(get_system_io(), 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. */ - mipsnet_init(0x4200, env->irq[2], &nd_table[0]); + /* MIPSnet uses the MIPS CPU INT0, which is interrupt 2. */ + mipsnet_init(0x4200, env->irq[2]); } static void mips_mipssim_machine_init(MachineClass *mc) diff --git a/hw/mips/trace-events b/hw/mips/trace-events index 13ee731a48..4a4e5fe1a1 100644 --- a/hw/mips/trace-events +++ b/hw/mips/trace-events @@ -1,6 +1,3 @@ -# gt64xxx_pci.c -gt64120_read(uint64_t addr, uint64_t value) "gt64120 read 0x%03"PRIx64" value:0x%08" PRIx64 -gt64120_write(uint64_t addr, uint64_t value) "gt64120 write 0x%03"PRIx64" value:0x%08" PRIx64 -gt64120_read_intreg(const char *regname, unsigned size, uint64_t value) "gt64120 read %s size:%u value:0x%08" PRIx64 -gt64120_write_intreg(const char *regname, unsigned size, uint64_t value) "gt64120 write %s size:%u value:0x%08" PRIx64 -gt64120_isd_remap(uint64_t from_length, uint64_t from_addr, uint64_t to_length, uint64_t to_addr) "ISD: 0x%08" PRIx64 "@0x%08" PRIx64 " -> 0x%08" PRIx64 "@0x%08" PRIx64 +# malta.c +malta_fpga_leds(const char *text) "LEDs %s" +malta_fpga_display(const char *text) "ASCII '%s'" diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index cbabe9f78c..1e08785b83 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -15,10 +15,6 @@ config ISA_DEBUG bool depends on ISA_BUS -config SGA - bool - depends on ISA_BUS - config ISA_TESTDEV bool default y if TEST_DEVICES @@ -38,6 +34,15 @@ config PCA9552 bool depends on I2C +config PCA9554 + bool + depends on I2C + +config I2C_ECHO + bool + default y if TEST_DEVICES + depends on I2C + config PL310 bool @@ -86,6 +91,15 @@ config STM32F4XX_SYSCFG config STM32F4XX_EXTI bool +config STM32L4X5_EXTI + bool + +config STM32L4X5_SYSCFG + bool + +config STM32L4X5_RCC + bool + config MIPS_ITU bool @@ -162,6 +176,9 @@ config SIFIVE_TEST config SIFIVE_E_PRCI bool +config SIFIVE_E_AON + bool + config SIFIVE_U_OTP bool @@ -174,4 +191,26 @@ config VIRT_CTRL config LASI bool +config ALLWINNER_SRAMC + bool + +config ALLWINNER_A10_CCM + bool + +config ALLWINNER_A10_DRAMC + bool + +config AXP2XX_PMU + bool + depends on I2C + +config DJMEMC + bool + +config IOSB + bool + +config XLNX_VERSAL_TRNG + bool + source macio/Kconfig diff --git a/hw/misc/a9scu.c b/hw/misc/a9scu.c index a375ebc987..04225dfb78 100644 --- a/hw/misc/a9scu.c +++ b/hw/misc/a9scu.c @@ -116,7 +116,7 @@ static const VMStateDescription vmstate_a9_scu = { .name = "a9-scu", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(control, A9SCUState), VMSTATE_UINT32(status, A9SCUState), VMSTATE_END_OF_LIST() diff --git a/hw/misc/allwinner-a10-ccm.c b/hw/misc/allwinner-a10-ccm.c new file mode 100644 index 0000000000..575b018952 --- /dev/null +++ b/hw/misc/allwinner-a10-ccm.c @@ -0,0 +1,224 @@ +/* + * Allwinner A10 Clock Control Module emulation + * + * Copyright (C) 2022 Strahinja Jankovic + * + * This file is derived from Allwinner H3 CCU, + * by 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-a10-ccm.h" + +/* CCM register offsets */ +enum { + REG_PLL1_CFG = 0x0000, /* PLL1 Control */ + REG_PLL1_TUN = 0x0004, /* PLL1 Tuning */ + REG_PLL2_CFG = 0x0008, /* PLL2 Control */ + REG_PLL2_TUN = 0x000C, /* PLL2 Tuning */ + REG_PLL3_CFG = 0x0010, /* PLL3 Control */ + REG_PLL4_CFG = 0x0018, /* PLL4 Control */ + REG_PLL5_CFG = 0x0020, /* PLL5 Control */ + REG_PLL5_TUN = 0x0024, /* PLL5 Tuning */ + REG_PLL6_CFG = 0x0028, /* PLL6 Control */ + REG_PLL6_TUN = 0x002C, /* PLL6 Tuning */ + REG_PLL7_CFG = 0x0030, /* PLL7 Control */ + REG_PLL1_TUN2 = 0x0038, /* PLL1 Tuning2 */ + REG_PLL5_TUN2 = 0x003C, /* PLL5 Tuning2 */ + REG_PLL8_CFG = 0x0040, /* PLL8 Control */ + REG_OSC24M_CFG = 0x0050, /* OSC24M Control */ + REG_CPU_AHB_APB0_CFG = 0x0054, /* CPU, AHB and APB0 Divide Ratio */ +}; + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* CCM register reset values */ +enum { + REG_PLL1_CFG_RST = 0x21005000, + REG_PLL1_TUN_RST = 0x0A101000, + REG_PLL2_CFG_RST = 0x08100010, + REG_PLL2_TUN_RST = 0x00000000, + REG_PLL3_CFG_RST = 0x0010D063, + REG_PLL4_CFG_RST = 0x21009911, + REG_PLL5_CFG_RST = 0x11049280, + REG_PLL5_TUN_RST = 0x14888000, + REG_PLL6_CFG_RST = 0x21009911, + REG_PLL6_TUN_RST = 0x00000000, + REG_PLL7_CFG_RST = 0x0010D063, + REG_PLL1_TUN2_RST = 0x00000000, + REG_PLL5_TUN2_RST = 0x00000000, + REG_PLL8_CFG_RST = 0x21009911, + REG_OSC24M_CFG_RST = 0x00138013, + REG_CPU_AHB_APB0_CFG_RST = 0x00010010, +}; + +static uint64_t allwinner_a10_ccm_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwA10ClockCtlState *s = AW_A10_CCM(opaque); + const uint32_t idx = REG_INDEX(offset); + + switch (offset) { + case REG_PLL1_CFG: + case REG_PLL1_TUN: + case REG_PLL2_CFG: + case REG_PLL2_TUN: + case REG_PLL3_CFG: + case REG_PLL4_CFG: + case REG_PLL5_CFG: + case REG_PLL5_TUN: + case REG_PLL6_CFG: + case REG_PLL6_TUN: + case REG_PLL7_CFG: + case REG_PLL1_TUN2: + case REG_PLL5_TUN2: + case REG_PLL8_CFG: + case REG_OSC24M_CFG: + case REG_CPU_AHB_APB0_CFG: + break; + case 0x158 ... AW_A10_CCM_IOSIZE: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented read offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + return s->regs[idx]; +} + +static void allwinner_a10_ccm_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwA10ClockCtlState *s = AW_A10_CCM(opaque); + const uint32_t idx = REG_INDEX(offset); + + switch (offset) { + case REG_PLL1_CFG: + case REG_PLL1_TUN: + case REG_PLL2_CFG: + case REG_PLL2_TUN: + case REG_PLL3_CFG: + case REG_PLL4_CFG: + case REG_PLL5_CFG: + case REG_PLL5_TUN: + case REG_PLL6_CFG: + case REG_PLL6_TUN: + case REG_PLL7_CFG: + case REG_PLL1_TUN2: + case REG_PLL5_TUN2: + case REG_PLL8_CFG: + case REG_OSC24M_CFG: + case REG_CPU_AHB_APB0_CFG: + break; + case 0x158 ... AW_A10_CCM_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_a10_ccm_ops = { + .read = allwinner_a10_ccm_read, + .write = allwinner_a10_ccm_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_a10_ccm_reset_enter(Object *obj, ResetType type) +{ + AwA10ClockCtlState *s = AW_A10_CCM(obj); + + /* Set default values for registers */ + s->regs[REG_INDEX(REG_PLL1_CFG)] = REG_PLL1_CFG_RST; + s->regs[REG_INDEX(REG_PLL1_TUN)] = REG_PLL1_TUN_RST; + s->regs[REG_INDEX(REG_PLL2_CFG)] = REG_PLL2_CFG_RST; + s->regs[REG_INDEX(REG_PLL2_TUN)] = REG_PLL2_TUN_RST; + s->regs[REG_INDEX(REG_PLL3_CFG)] = REG_PLL3_CFG_RST; + s->regs[REG_INDEX(REG_PLL4_CFG)] = REG_PLL4_CFG_RST; + s->regs[REG_INDEX(REG_PLL5_CFG)] = REG_PLL5_CFG_RST; + s->regs[REG_INDEX(REG_PLL5_TUN)] = REG_PLL5_TUN_RST; + s->regs[REG_INDEX(REG_PLL6_CFG)] = REG_PLL6_CFG_RST; + s->regs[REG_INDEX(REG_PLL6_TUN)] = REG_PLL6_TUN_RST; + s->regs[REG_INDEX(REG_PLL7_CFG)] = REG_PLL7_CFG_RST; + s->regs[REG_INDEX(REG_PLL1_TUN2)] = REG_PLL1_TUN2_RST; + s->regs[REG_INDEX(REG_PLL5_TUN2)] = REG_PLL5_TUN2_RST; + s->regs[REG_INDEX(REG_PLL8_CFG)] = REG_PLL8_CFG_RST; + s->regs[REG_INDEX(REG_OSC24M_CFG)] = REG_OSC24M_CFG_RST; + s->regs[REG_INDEX(REG_CPU_AHB_APB0_CFG)] = REG_CPU_AHB_APB0_CFG_RST; +} + +static void allwinner_a10_ccm_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwA10ClockCtlState *s = AW_A10_CCM(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_a10_ccm_ops, s, + TYPE_AW_A10_CCM, AW_A10_CCM_IOSIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_a10_ccm_vmstate = { + .name = "allwinner-a10-ccm", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AwA10ClockCtlState, AW_A10_CCM_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_a10_ccm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.enter = allwinner_a10_ccm_reset_enter; + dc->vmsd = &allwinner_a10_ccm_vmstate; +} + +static const TypeInfo allwinner_a10_ccm_info = { + .name = TYPE_AW_A10_CCM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_a10_ccm_init, + .instance_size = sizeof(AwA10ClockCtlState), + .class_init = allwinner_a10_ccm_class_init, +}; + +static void allwinner_a10_ccm_register(void) +{ + type_register_static(&allwinner_a10_ccm_info); +} + +type_init(allwinner_a10_ccm_register) diff --git a/hw/misc/allwinner-a10-dramc.c b/hw/misc/allwinner-a10-dramc.c new file mode 100644 index 0000000000..a7c58fa6d0 --- /dev/null +++ b/hw/misc/allwinner-a10-dramc.c @@ -0,0 +1,179 @@ +/* + * Allwinner A10 DRAM Controller emulation + * + * Copyright (C) 2022 Strahinja Jankovic + * + * This file is derived from Allwinner H3 DRAMC, + * by 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-a10-dramc.h" + +/* DRAMC register offsets */ +enum { + REG_SDR_CCR = 0x0000, + REG_SDR_ZQCR0 = 0x00a8, + REG_SDR_ZQSR = 0x00b0 +}; + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* DRAMC register flags */ +enum { + REG_SDR_CCR_DATA_TRAINING = (1 << 30), + REG_SDR_CCR_DRAM_INIT = (1 << 31), +}; +enum { + REG_SDR_ZQSR_ZCAL = (1 << 31), +}; + +/* DRAMC register reset values */ +enum { + REG_SDR_CCR_RESET = 0x80020000, + REG_SDR_ZQCR0_RESET = 0x07b00000, + REG_SDR_ZQSR_RESET = 0x80000000 +}; + +static uint64_t allwinner_a10_dramc_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwA10DramControllerState *s = AW_A10_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + switch (offset) { + case REG_SDR_CCR: + case REG_SDR_ZQCR0: + case REG_SDR_ZQSR: + break; + case 0x2e4 ... AW_A10_DRAMC_IOSIZE: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented read offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + return s->regs[idx]; +} + +static void allwinner_a10_dramc_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwA10DramControllerState *s = AW_A10_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + switch (offset) { + case REG_SDR_CCR: + if (val & REG_SDR_CCR_DRAM_INIT) { + /* Clear DRAM_INIT to indicate process is done. */ + val &= ~REG_SDR_CCR_DRAM_INIT; + } + if (val & REG_SDR_CCR_DATA_TRAINING) { + /* Clear DATA_TRAINING to indicate process is done. */ + val &= ~REG_SDR_CCR_DATA_TRAINING; + } + break; + case REG_SDR_ZQCR0: + /* Set ZCAL in ZQSR to indicate calibration is done. */ + s->regs[REG_INDEX(REG_SDR_ZQSR)] |= REG_SDR_ZQSR_ZCAL; + break; + case 0x2e4 ... AW_A10_DRAMC_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_a10_dramc_ops = { + .read = allwinner_a10_dramc_read, + .write = allwinner_a10_dramc_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_a10_dramc_reset_enter(Object *obj, ResetType type) +{ + AwA10DramControllerState *s = AW_A10_DRAMC(obj); + + /* Set default values for registers */ + s->regs[REG_INDEX(REG_SDR_CCR)] = REG_SDR_CCR_RESET; + s->regs[REG_INDEX(REG_SDR_ZQCR0)] = REG_SDR_ZQCR0_RESET; + s->regs[REG_INDEX(REG_SDR_ZQSR)] = REG_SDR_ZQSR_RESET; +} + +static void allwinner_a10_dramc_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwA10DramControllerState *s = AW_A10_DRAMC(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_a10_dramc_ops, s, + TYPE_AW_A10_DRAMC, AW_A10_DRAMC_IOSIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_a10_dramc_vmstate = { + .name = "allwinner-a10-dramc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AwA10DramControllerState, + AW_A10_DRAMC_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_a10_dramc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.enter = allwinner_a10_dramc_reset_enter; + dc->vmsd = &allwinner_a10_dramc_vmstate; +} + +static const TypeInfo allwinner_a10_dramc_info = { + .name = TYPE_AW_A10_DRAMC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_a10_dramc_init, + .instance_size = sizeof(AwA10DramControllerState), + .class_init = allwinner_a10_dramc_class_init, +}; + +static void allwinner_a10_dramc_register(void) +{ + type_register_static(&allwinner_a10_dramc_info); +} + +type_init(allwinner_a10_dramc_register) diff --git a/hw/misc/allwinner-cpucfg.c b/hw/misc/allwinner-cpucfg.c index bbd33a7dac..31b9780969 100644 --- a/hw/misc/allwinner-cpucfg.c +++ b/hw/misc/allwinner-cpucfg.c @@ -250,7 +250,7 @@ static const VMStateDescription allwinner_cpucfg_vmstate = { .name = "allwinner-cpucfg", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(gen_ctrl, AwCpuCfgState), VMSTATE_UINT32(super_standby, AwCpuCfgState), VMSTATE_UINT32(entry_addr, AwCpuCfgState), diff --git a/hw/misc/allwinner-h3-ccu.c b/hw/misc/allwinner-h3-ccu.c index 18d1074545..cfc68522d3 100644 --- a/hw/misc/allwinner-h3-ccu.c +++ b/hw/misc/allwinner-h3-ccu.c @@ -212,7 +212,7 @@ static const VMStateDescription allwinner_h3_ccu_vmstate = { .name = "allwinner-h3-ccu", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AwH3ClockCtlState, AW_H3_CCU_REGS_NUM), VMSTATE_END_OF_LIST() } diff --git a/hw/misc/allwinner-h3-dramc.c b/hw/misc/allwinner-h3-dramc.c index 1d37cf422c..e168ffe623 100644 --- a/hw/misc/allwinner-h3-dramc.c +++ b/hw/misc/allwinner-h3-dramc.c @@ -324,7 +324,7 @@ static const VMStateDescription allwinner_h3_dramc_vmstate = { .name = "allwinner-h3-dramc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const 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), diff --git a/hw/misc/allwinner-h3-sysctrl.c b/hw/misc/allwinner-h3-sysctrl.c index 1d07efa880..2d29be83e3 100644 --- a/hw/misc/allwinner-h3-sysctrl.c +++ b/hw/misc/allwinner-h3-sysctrl.c @@ -110,7 +110,7 @@ static const VMStateDescription allwinner_h3_sysctrl_vmstate = { .name = "allwinner-h3-sysctrl", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AwH3SysCtrlState, AW_H3_SYSCTRL_REGS_NUM), VMSTATE_END_OF_LIST() } diff --git a/hw/misc/allwinner-r40-ccu.c b/hw/misc/allwinner-r40-ccu.c new file mode 100644 index 0000000000..33baf4429d --- /dev/null +++ b/hw/misc/allwinner-r40-ccu.c @@ -0,0 +1,209 @@ +/* + * Allwinner R40 Clock Control Unit emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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-r40-ccu.h" + +/* CCU register offsets */ +enum { + REG_PLL_CPUX_CTRL = 0x0000, + REG_PLL_AUDIO_CTRL = 0x0008, + REG_PLL_VIDEO0_CTRL = 0x0010, + REG_PLL_VE_CTRL = 0x0018, + REG_PLL_DDR0_CTRL = 0x0020, + REG_PLL_PERIPH0_CTRL = 0x0028, + REG_PLL_PERIPH1_CTRL = 0x002c, + REG_PLL_VIDEO1_CTRL = 0x0030, + REG_PLL_SATA_CTRL = 0x0034, + REG_PLL_GPU_CTRL = 0x0038, + REG_PLL_MIPI_CTRL = 0x0040, + REG_PLL_DE_CTRL = 0x0048, + REG_PLL_DDR1_CTRL = 0x004c, + REG_AHB1_APB1_CFG = 0x0054, + REG_APB2_CFG = 0x0058, + REG_MMC0_CLK = 0x0088, + REG_MMC1_CLK = 0x008c, + REG_MMC2_CLK = 0x0090, + REG_MMC3_CLK = 0x0094, + REG_USBPHY_CFG = 0x00cc, + REG_PLL_DDR_AUX = 0x00f0, + REG_DRAM_CFG = 0x00f4, + REG_PLL_DDR1_CFG = 0x00f8, + REG_DRAM_CLK_GATING = 0x0100, + REG_GMAC_CLK = 0x0164, + REG_SYS_32K_CLK = 0x0310, + REG_PLL_LOCK_CTRL = 0x0320, +}; + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* CCU register flags */ +enum { + REG_PLL_ENABLE = (1 << 31), + REG_PLL_LOCK = (1 << 28), +}; + +static uint64_t allwinner_r40_ccu_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwR40ClockCtlState *s = AW_R40_CCU(opaque); + const uint32_t idx = REG_INDEX(offset); + + switch (offset) { + case 0x324 ... AW_R40_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_r40_ccu_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwR40ClockCtlState *s = AW_R40_CCU(opaque); + + switch (offset) { + case REG_DRAM_CFG: /* DRAM Configuration(for DDR0) */ + /* bit16: SDRCLK_UPD (SDRCLK configuration 0 update) */ + val &= ~(1 << 16); + break; + case REG_PLL_DDR1_CTRL: /* DDR1 Control register */ + /* bit30: SDRPLL_UPD */ + val &= ~(1 << 30); + if (val & REG_PLL_ENABLE) { + val |= REG_PLL_LOCK; + } + break; + case REG_PLL_CPUX_CTRL: + case REG_PLL_AUDIO_CTRL: + case REG_PLL_VE_CTRL: + case REG_PLL_VIDEO0_CTRL: + case REG_PLL_DDR0_CTRL: + case REG_PLL_PERIPH0_CTRL: + case REG_PLL_PERIPH1_CTRL: + case REG_PLL_VIDEO1_CTRL: + case REG_PLL_SATA_CTRL: + case REG_PLL_GPU_CTRL: + case REG_PLL_MIPI_CTRL: + case REG_PLL_DE_CTRL: + if (val & REG_PLL_ENABLE) { + val |= REG_PLL_LOCK; + } + break; + case 0x324 ... AW_R40_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[REG_INDEX(offset)] = (uint32_t) val; +} + +static const MemoryRegionOps allwinner_r40_ccu_ops = { + .read = allwinner_r40_ccu_read, + .write = allwinner_r40_ccu_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_r40_ccu_reset(DeviceState *dev) +{ + AwR40ClockCtlState *s = AW_R40_CCU(dev); + + memset(s->regs, 0, sizeof(s->regs)); + + /* Set default values for registers */ + s->regs[REG_INDEX(REG_PLL_CPUX_CTRL)] = 0x00001000; + s->regs[REG_INDEX(REG_PLL_AUDIO_CTRL)] = 0x00035514; + s->regs[REG_INDEX(REG_PLL_VIDEO0_CTRL)] = 0x03006207; + s->regs[REG_INDEX(REG_PLL_VE_CTRL)] = 0x03006207; + s->regs[REG_INDEX(REG_PLL_DDR0_CTRL)] = 0x00001000, + s->regs[REG_INDEX(REG_PLL_PERIPH0_CTRL)] = 0x00041811; + s->regs[REG_INDEX(REG_PLL_PERIPH1_CTRL)] = 0x00041811; + s->regs[REG_INDEX(REG_PLL_VIDEO1_CTRL)] = 0x03006207; + s->regs[REG_INDEX(REG_PLL_SATA_CTRL)] = 0x00001811; + s->regs[REG_INDEX(REG_PLL_GPU_CTRL)] = 0x03006207; + s->regs[REG_INDEX(REG_PLL_MIPI_CTRL)] = 0x00000515; + s->regs[REG_INDEX(REG_PLL_DE_CTRL)] = 0x03006207; + s->regs[REG_INDEX(REG_PLL_DDR1_CTRL)] = 0x00001800; + s->regs[REG_INDEX(REG_AHB1_APB1_CFG)] = 0x00001010; + s->regs[REG_INDEX(REG_APB2_CFG)] = 0x01000000; + s->regs[REG_INDEX(REG_PLL_DDR_AUX)] = 0x00000001; + s->regs[REG_INDEX(REG_PLL_DDR1_CFG)] = 0x0ccca000; + s->regs[REG_INDEX(REG_SYS_32K_CLK)] = 0x0000000f; +} + +static void allwinner_r40_ccu_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwR40ClockCtlState *s = AW_R40_CCU(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_r40_ccu_ops, s, + TYPE_AW_R40_CCU, AW_R40_CCU_IOSIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_r40_ccu_vmstate = { + .name = "allwinner-r40-ccu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AwR40ClockCtlState, AW_R40_CCU_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_r40_ccu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_r40_ccu_reset; + dc->vmsd = &allwinner_r40_ccu_vmstate; +} + +static const TypeInfo allwinner_r40_ccu_info = { + .name = TYPE_AW_R40_CCU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_r40_ccu_init, + .instance_size = sizeof(AwR40ClockCtlState), + .class_init = allwinner_r40_ccu_class_init, +}; + +static void allwinner_r40_ccu_register(void) +{ + type_register_static(&allwinner_r40_ccu_info); +} + +type_init(allwinner_r40_ccu_register) diff --git a/hw/misc/allwinner-r40-dramc.c b/hw/misc/allwinner-r40-dramc.c new file mode 100644 index 0000000000..75b0bef4fd --- /dev/null +++ b/hw/misc/allwinner-r40-dramc.c @@ -0,0 +1,511 @@ +/* + * Allwinner R40 SDRAM Controller emulation + * + * CCopyright (C) 2023 qianfan Zhao + * + * 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 "qemu/bitops.h" +#include "hw/misc/allwinner-r40-dramc.h" +#include "trace.h" + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* DRAMCOM register offsets */ +enum { + REG_DRAMCOM_CR = 0x0000, /* Control Register */ +}; + +/* DRAMCOMM register flags */ +enum { + REG_DRAMCOM_CR_DUAL_RANK = (1 << 0), +}; + +/* 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 */ + REG_DRAMCTL_PGCR = 0x0100, /* PHY general configuration registers */ +}; + +/* DRAMCTL register flags */ +enum { + REG_DRAMCTL_PGSR_INITDONE = (1 << 0), + REG_DRAMCTL_PGSR_READ_TIMEOUT = (1 << 13), + REG_DRAMCTL_PGCR_ENABLE_READ_TIMEOUT = (1 << 25), +}; + +enum { + REG_DRAMCTL_STATR_ACTIVE = (1 << 0), +}; + +#define DRAM_MAX_ROW_BITS 16 +#define DRAM_MAX_COL_BITS 13 /* 8192 */ +#define DRAM_MAX_BANK 3 + +static uint64_t dram_autodetect_cells[DRAM_MAX_ROW_BITS] + [DRAM_MAX_BANK] + [DRAM_MAX_COL_BITS]; +struct VirtualDDRChip { + uint32_t ram_size; + uint8_t bank_bits; + uint8_t row_bits; + uint8_t col_bits; +}; + +/* + * Only power of 2 RAM sizes from 256MiB up to 2048MiB are supported, + * 2GiB memory is not supported due to dual rank feature. + */ +static const struct VirtualDDRChip dummy_ddr_chips[] = { + { + .ram_size = 256, + .bank_bits = 3, + .row_bits = 12, + .col_bits = 13, + }, { + .ram_size = 512, + .bank_bits = 3, + .row_bits = 13, + .col_bits = 13, + }, { + .ram_size = 1024, + .bank_bits = 3, + .row_bits = 14, + .col_bits = 13, + }, { + 0 + } +}; + +static const struct VirtualDDRChip *get_match_ddr(uint32_t ram_size) +{ + const struct VirtualDDRChip *ddr; + + for (ddr = &dummy_ddr_chips[0]; ddr->ram_size; ddr++) { + if (ddr->ram_size == ram_size) { + return ddr; + } + } + + return NULL; +} + +static uint64_t *address_to_autodetect_cells(AwR40DramCtlState *s, + const struct VirtualDDRChip *ddr, + uint32_t offset) +{ + int row_index = 0, bank_index = 0, col_index = 0; + uint32_t row_addr, bank_addr, col_addr; + + row_addr = extract32(offset, s->set_col_bits + s->set_bank_bits, + s->set_row_bits); + bank_addr = extract32(offset, s->set_col_bits, s->set_bank_bits); + col_addr = extract32(offset, 0, s->set_col_bits); + + for (int i = 0; i < ddr->row_bits; i++) { + if (row_addr & BIT(i)) { + row_index = i; + } + } + + for (int i = 0; i < ddr->bank_bits; i++) { + if (bank_addr & BIT(i)) { + bank_index = i; + } + } + + for (int i = 0; i < ddr->col_bits; i++) { + if (col_addr & BIT(i)) { + col_index = i; + } + } + + trace_allwinner_r40_dramc_offset_to_cell(offset, row_index, bank_index, + col_index); + return &dram_autodetect_cells[row_index][bank_index][col_index]; +} + +static void allwinner_r40_dramc_map_rows(AwR40DramCtlState *s, uint8_t row_bits, + uint8_t bank_bits, uint8_t col_bits) +{ + const struct VirtualDDRChip *ddr = get_match_ddr(s->ram_size); + bool enable_detect_cells; + + trace_allwinner_r40_dramc_map_rows(row_bits, bank_bits, col_bits); + + if (!ddr) { + return; + } + + s->set_row_bits = row_bits; + s->set_bank_bits = bank_bits; + s->set_col_bits = col_bits; + + enable_detect_cells = ddr->bank_bits != bank_bits + || ddr->row_bits != row_bits + || ddr->col_bits != col_bits; + + if (enable_detect_cells) { + trace_allwinner_r40_dramc_detect_cells_enable(); + } else { + trace_allwinner_r40_dramc_detect_cells_disable(); + } + + memory_region_set_enabled(&s->detect_cells, enable_detect_cells); +} + +static uint64_t allwinner_r40_dramcom_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_R40_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_r40_dramcom_read(offset, s->dramcom[idx], size); + return s->dramcom[idx]; +} + +static void allwinner_r40_dramcom_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + trace_allwinner_r40_dramcom_write(offset, val, size); + + if (idx >= AW_R40_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 */ + if (!(val & REG_DRAMCOM_CR_DUAL_RANK)) { + allwinner_r40_dramc_map_rows(s, ((val >> 4) & 0xf) + 1, + ((val >> 2) & 0x1) + 2, + (((val >> 8) & 0xf) + 3)); + } + break; + }; + + s->dramcom[idx] = (uint32_t) val; +} + +static uint64_t allwinner_r40_dramctl_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_R40_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_r40_dramctl_read(offset, s->dramctl[idx], size); + return s->dramctl[idx]; +} + +static void allwinner_r40_dramctl_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + trace_allwinner_r40_dramctl_write(offset, val, size); + + if (idx >= AW_R40_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; + } + + s->dramctl[idx] = (uint32_t) val; +} + +static uint64_t allwinner_r40_dramphy_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_R40_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_r40_dramphy_read(offset, s->dramphy[idx], size); + return s->dramphy[idx]; +} + +static void allwinner_r40_dramphy_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + trace_allwinner_r40_dramphy_write(offset, val, size); + + if (idx >= AW_R40_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_r40_dramcom_ops = { + .read = allwinner_r40_dramcom_read, + .write = allwinner_r40_dramcom_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static const MemoryRegionOps allwinner_r40_dramctl_ops = { + .read = allwinner_r40_dramctl_read, + .write = allwinner_r40_dramctl_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static const MemoryRegionOps allwinner_r40_dramphy_ops = { + .read = allwinner_r40_dramphy_read, + .write = allwinner_r40_dramphy_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static uint64_t allwinner_r40_detect_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const struct VirtualDDRChip *ddr = get_match_ddr(s->ram_size); + uint64_t data = 0; + + if (ddr) { + data = *address_to_autodetect_cells(s, ddr, (uint32_t)offset); + } + + trace_allwinner_r40_dramc_detect_cell_read(offset, data); + return data; +} + +static void allwinner_r40_detect_write(void *opaque, hwaddr offset, + uint64_t data, unsigned size) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + const struct VirtualDDRChip *ddr = get_match_ddr(s->ram_size); + + if (ddr) { + uint64_t *cell = address_to_autodetect_cells(s, ddr, (uint32_t)offset); + trace_allwinner_r40_dramc_detect_cell_write(offset, data); + *cell = data; + } +} + +static const MemoryRegionOps allwinner_r40_detect_ops = { + .read = allwinner_r40_detect_read, + .write = allwinner_r40_detect_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +/* + * mctl_r40_detect_rank_count in u-boot will write the high 1G of DDR + * to detect whether the board support dual_rank or not. Create a virtual memory + * if the board's ram_size less or equal than 1G, and set read time out flag of + * REG_DRAMCTL_PGSR when the user touch this high dram. + */ +static uint64_t allwinner_r40_dualrank_detect_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(opaque); + uint32_t reg; + + reg = s->dramctl[REG_INDEX(REG_DRAMCTL_PGCR)]; + if (reg & REG_DRAMCTL_PGCR_ENABLE_READ_TIMEOUT) { /* Enable read time out */ + /* + * this driver only support one rank, mark READ_TIMEOUT when try + * read the second rank. + */ + s->dramctl[REG_INDEX(REG_DRAMCTL_PGSR)] + |= REG_DRAMCTL_PGSR_READ_TIMEOUT; + } + + return 0; +} + +static const MemoryRegionOps allwinner_r40_dualrank_detect_ops = { + .read = allwinner_r40_dualrank_detect_read, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_r40_dramc_reset(DeviceState *dev) +{ + AwR40DramCtlState *s = AW_R40_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_r40_dramc_realize(DeviceState *dev, Error **errp) +{ + AwR40DramCtlState *s = AW_R40_DRAMC(dev); + + if (!get_match_ddr(s->ram_size)) { + error_report("%s: ram-size %u MiB is not supported", + __func__, s->ram_size); + exit(1); + } + + /* R40 support max 2G memory but we only support up to 1G now. */ + memory_region_init_io(&s->detect_cells, OBJECT(s), + &allwinner_r40_detect_ops, s, + "DRAMCELLS", 1 * GiB); + memory_region_add_subregion_overlap(get_system_memory(), s->ram_addr, + &s->detect_cells, 10); + memory_region_set_enabled(&s->detect_cells, false); + + /* + * We only support DRAM size up to 1G now, so prepare a high memory page + * after 1G for dualrank detect. + */ + memory_region_init_io(&s->dram_high, OBJECT(s), + &allwinner_r40_dualrank_detect_ops, s, + "DRAMHIGH", KiB); + memory_region_add_subregion(get_system_memory(), s->ram_addr + GiB, + &s->dram_high); +} + +static void allwinner_r40_dramc_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwR40DramCtlState *s = AW_R40_DRAMC(obj); + + /* DRAMCOM registers, index 0 */ + memory_region_init_io(&s->dramcom_iomem, OBJECT(s), + &allwinner_r40_dramcom_ops, s, + "DRAMCOM", 4 * KiB); + sysbus_init_mmio(sbd, &s->dramcom_iomem); + + /* DRAMCTL registers, index 1 */ + memory_region_init_io(&s->dramctl_iomem, OBJECT(s), + &allwinner_r40_dramctl_ops, s, + "DRAMCTL", 4 * KiB); + sysbus_init_mmio(sbd, &s->dramctl_iomem); + + /* DRAMPHY registers. index 2 */ + memory_region_init_io(&s->dramphy_iomem, OBJECT(s), + &allwinner_r40_dramphy_ops, s, + "DRAMPHY", 4 * KiB); + sysbus_init_mmio(sbd, &s->dramphy_iomem); +} + +static Property allwinner_r40_dramc_properties[] = { + DEFINE_PROP_UINT64("ram-addr", AwR40DramCtlState, ram_addr, 0x0), + DEFINE_PROP_UINT32("ram-size", AwR40DramCtlState, ram_size, 256), /* MiB */ + DEFINE_PROP_END_OF_LIST() +}; + +static const VMStateDescription allwinner_r40_dramc_vmstate = { + .name = "allwinner-r40-dramc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(dramcom, AwR40DramCtlState, + AW_R40_DRAMCOM_REGS_NUM), + VMSTATE_UINT32_ARRAY(dramctl, AwR40DramCtlState, + AW_R40_DRAMCTL_REGS_NUM), + VMSTATE_UINT32_ARRAY(dramphy, AwR40DramCtlState, + AW_R40_DRAMPHY_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_r40_dramc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_r40_dramc_reset; + dc->vmsd = &allwinner_r40_dramc_vmstate; + dc->realize = allwinner_r40_dramc_realize; + device_class_set_props(dc, allwinner_r40_dramc_properties); +} + +static const TypeInfo allwinner_r40_dramc_info = { + .name = TYPE_AW_R40_DRAMC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_r40_dramc_init, + .instance_size = sizeof(AwR40DramCtlState), + .class_init = allwinner_r40_dramc_class_init, +}; + +static void allwinner_r40_dramc_register(void) +{ + type_register_static(&allwinner_r40_dramc_info); +} + +type_init(allwinner_r40_dramc_register) diff --git a/hw/misc/allwinner-sid.c b/hw/misc/allwinner-sid.c index 6d61f55b1d..e5cd431743 100644 --- a/hw/misc/allwinner-sid.c +++ b/hw/misc/allwinner-sid.c @@ -136,7 +136,7 @@ static const VMStateDescription allwinner_sid_vmstate = { .name = "allwinner-sid", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(control, AwSidState), VMSTATE_UINT32(rdkey, AwSidState), VMSTATE_UINT8_ARRAY_V(identifier.data, AwSidState, sizeof(QemuUUID), 1), diff --git a/hw/misc/allwinner-sramc.c b/hw/misc/allwinner-sramc.c new file mode 100644 index 0000000000..cf10ca8ffe --- /dev/null +++ b/hw/misc/allwinner-sramc.c @@ -0,0 +1,185 @@ +/* + * Allwinner R40 SRAM controller emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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 "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/misc/allwinner-sramc.h" +#include "trace.h" + +/* + * register offsets + * https://linux-sunxi.org/SRAM_Controller_Register_Guide + */ +enum { + REG_SRAM_CTL1_CFG = 0x04, /* SRAM Control register 1 */ + REG_SRAM_VER = 0x24, /* SRAM Version register */ + REG_SRAM_R40_SOFT_ENTRY_REG0 = 0xbc, +}; + +/* REG_SRAMC_VERSION bit defines */ +#define SRAM_VER_READ_ENABLE (1 << 15) +#define SRAM_VER_VERSION_SHIFT 16 +#define SRAM_VERSION_SUN8I_R40 0x1701 + +static uint64_t allwinner_sramc_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwSRAMCState *s = AW_SRAMC(opaque); + AwSRAMCClass *sc = AW_SRAMC_GET_CLASS(s); + uint64_t val = 0; + + switch (offset) { + case REG_SRAM_CTL1_CFG: + val = s->sram_ctl1; + break; + case REG_SRAM_VER: + /* bit15: lock bit, set this bit before reading this register */ + if (s->sram_ver & SRAM_VER_READ_ENABLE) { + val = SRAM_VER_READ_ENABLE | + (sc->sram_version_code << SRAM_VER_VERSION_SHIFT); + } + break; + case REG_SRAM_R40_SOFT_ENTRY_REG0: + val = s->sram_soft_entry_reg0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_sramc_read(offset, val); + + return val; +} + +static void allwinner_sramc_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwSRAMCState *s = AW_SRAMC(opaque); + + trace_allwinner_sramc_write(offset, val); + + switch (offset) { + case REG_SRAM_CTL1_CFG: + s->sram_ctl1 = val; + break; + case REG_SRAM_VER: + /* Only the READ_ENABLE bit is writeable */ + s->sram_ver = val & SRAM_VER_READ_ENABLE; + break; + case REG_SRAM_R40_SOFT_ENTRY_REG0: + s->sram_soft_entry_reg0 = val; + 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_sramc_ops = { + .read = allwinner_sramc_read, + .write = allwinner_sramc_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static const VMStateDescription allwinner_sramc_vmstate = { + .name = "allwinner-sramc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(sram_ver, AwSRAMCState), + VMSTATE_UINT32(sram_soft_entry_reg0, AwSRAMCState), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_sramc_reset(DeviceState *dev) +{ + AwSRAMCState *s = AW_SRAMC(dev); + AwSRAMCClass *sc = AW_SRAMC_GET_CLASS(s); + + switch (sc->sram_version_code) { + case SRAM_VERSION_SUN8I_R40: + s->sram_ctl1 = 0x1300; + break; + } +} + +static void allwinner_sramc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_sramc_reset; + dc->vmsd = &allwinner_sramc_vmstate; +} + +static void allwinner_sramc_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwSRAMCState *s = AW_SRAMC(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_sramc_ops, s, + TYPE_AW_SRAMC, 1 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const TypeInfo allwinner_sramc_info = { + .name = TYPE_AW_SRAMC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_sramc_init, + .instance_size = sizeof(AwSRAMCState), + .class_size = sizeof(AwSRAMCClass), + .class_init = allwinner_sramc_class_init, +}; + +static void allwinner_r40_sramc_class_init(ObjectClass *klass, void *data) +{ + AwSRAMCClass *sc = AW_SRAMC_CLASS(klass); + + sc->sram_version_code = SRAM_VERSION_SUN8I_R40; +} + +static const TypeInfo allwinner_r40_sramc_info = { + .name = TYPE_AW_SRAMC_SUN8I_R40, + .parent = TYPE_AW_SRAMC, + .class_init = allwinner_r40_sramc_class_init, +}; + +static void allwinner_sramc_register(void) +{ + type_register_static(&allwinner_sramc_info); + type_register_static(&allwinner_r40_sramc_info); +} + +type_init(allwinner_sramc_register) diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 81cd6b6423..14e3ef667d 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -34,13 +34,18 @@ #include "hw/isa/isa.h" #include "hw/qdev-properties.h" #include "ui/console.h" +#include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/timer.h" #include "qom/object.h" +#include "hw/acpi/acpi_aml_interface.h" /* #define DEBUG_SMC */ #define APPLESMC_DEFAULT_IOBASE 0x300 +#define TYPE_APPLE_SMC "isa-applesmc" +#define APPLESMC_MAX_DATA_LENGTH 32 +#define APPLESMC_PROP_IO_BASE "iobase" enum { APPLESMC_DATA_PORT = 0x00, @@ -269,6 +274,7 @@ static void qdev_applesmc_isa_reset(DeviceState *dev) /* Remove existing entries */ QLIST_FOREACH_SAFE(d, &s->data_def, node, next) { QLIST_REMOVE(d, node); + g_free(d); } s->status = 0x00; s->status_1e = 0x00; @@ -337,7 +343,6 @@ static void applesmc_isa_realize(DeviceState *dev, Error **errp) } QLIST_INIT(&s->data_def); - qdev_applesmc_isa_reset(dev); } static Property applesmc_isa_properties[] = { @@ -347,14 +352,35 @@ static Property applesmc_isa_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void build_applesmc_aml(AcpiDevAmlIf *adev, Aml *scope) +{ + Aml *crs; + AppleSMCState *s = APPLE_SMC(adev); + uint32_t iobase = s->iobase; + Aml *dev = aml_device("SMC"); + + aml_append(dev, aml_name_decl("_HID", aml_eisaid("APP0001"))); + /* device present, functioning, decoding, not shown in UI */ + aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); + crs = aml_resource_template(); + aml_append(crs, + aml_io(AML_DECODE16, iobase, iobase, 0x01, APPLESMC_MAX_DATA_LENGTH) + ); + aml_append(crs, aml_irq_no_flags(6)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); +} + static void qdev_applesmc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); dc->realize = applesmc_isa_realize; dc->reset = qdev_applesmc_isa_reset; device_class_set_props(dc, applesmc_isa_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + adevc->build_dev_aml = build_applesmc_aml; } static const TypeInfo applesmc_isa_info = { @@ -362,6 +388,10 @@ static const TypeInfo applesmc_isa_info = { .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(AppleSMCState), .class_init = qdev_applesmc_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_ACPI_DEV_AML_IF }, + { }, + }, }; static void applesmc_register_types(void) diff --git a/hw/misc/arm_l2x0.c b/hw/misc/arm_l2x0.c index 75c3eb8982..b14d0a2676 100644 --- a/hw/misc/arm_l2x0.c +++ b/hw/misc/arm_l2x0.c @@ -49,7 +49,7 @@ static const VMStateDescription vmstate_l2x0 = { .name = "l2x0", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ctrl, L2x0State), VMSTATE_UINT32(aux_ctrl, L2x0State), VMSTATE_UINT32(data_ctrl, L2x0State), diff --git a/hw/misc/arm_sysctl.c b/hw/misc/arm_sysctl.c index 42d4693854..5108f3eda9 100644 --- a/hw/misc/arm_sysctl.c +++ b/hw/misc/arm_sysctl.c @@ -57,7 +57,7 @@ static const VMStateDescription vmstate_arm_sysctl = { .name = "realview_sysctl", .version_id = 4, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(leds, arm_sysctl_state), VMSTATE_UINT16(lockval, arm_sysctl_state), VMSTATE_UINT32(cfgdata1, arm_sysctl_state), @@ -534,12 +534,12 @@ static void arm_sysctl_write(void *opaque, hwaddr offset, s->sys_cfgstat |= 2; /* error */ } } else { - uint32_t val; + uint32_t data; if (!vexpress_cfgctrl_read(s, dcc, function, site, position, - device, &val)) { + device, &data)) { s->sys_cfgstat |= 2; /* error */ } else { - s->sys_cfgdata = val; + s->sys_cfgdata = data; } } } diff --git a/hw/misc/armsse-cpu-pwrctrl.c b/hw/misc/armsse-cpu-pwrctrl.c index 42fc38879f..bfc51d175c 100644 --- a/hw/misc/armsse-cpu-pwrctrl.c +++ b/hw/misc/armsse-cpu-pwrctrl.c @@ -109,7 +109,7 @@ static const VMStateDescription pwrctrl_vmstate = { .name = "armsse-cpu-pwrctrl", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(cpupwrcfg, ARMSSECPUPwrCtrl), VMSTATE_END_OF_LIST() }, diff --git a/hw/misc/armsse-mhu.c b/hw/misc/armsse-mhu.c index 0be7f0fc87..55625b2cca 100644 --- a/hw/misc/armsse-mhu.c +++ b/hw/misc/armsse-mhu.c @@ -157,7 +157,7 @@ static const VMStateDescription armsse_mhu_vmstate = { .name = "armsse-mhu", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(cpu0intr, ARMSSEMHU), VMSTATE_UINT32(cpu1intr, ARMSSEMHU), VMSTATE_END_OF_LIST() diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 4b5997e18f..c06c04ddc6 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -189,10 +189,11 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, bool acc_mode) { struct iovec iov[ASPEED_HACE_MAX_SG]; - g_autofree uint8_t *digest_buf; + g_autofree uint8_t *digest_buf = NULL; size_t digest_len = 0; int niov = 0; int i; + void *haddr; if (sg_mode) { uint32_t len = 0; @@ -217,9 +218,13 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, addr &= SG_LIST_ADDR_MASK; plen = len & SG_LIST_LEN_MASK; - iov[i].iov_base = address_space_map(&s->dram_as, addr, &plen, false, - MEMTXATTRS_UNSPECIFIED); - + haddr = address_space_map(&s->dram_as, addr, &plen, false, + MEMTXATTRS_UNSPECIFIED); + if (haddr == NULL) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto failed\n", __func__); + return; + } + iov[i].iov_base = haddr; if (acc_mode) { niov = gen_acc_mode_iov(s, iov, i, &plen); @@ -230,10 +235,14 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, } else { hwaddr len = s->regs[R_HASH_SRC_LEN]; + haddr = address_space_map(&s->dram_as, s->regs[R_HASH_SRC], + &len, false, MEMTXATTRS_UNSPECIFIED); + if (haddr == NULL) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto failed\n", __func__); + return; + } + iov[0].iov_base = haddr; iov[0].iov_len = len; - iov[0].iov_base = address_space_map(&s->dram_as, s->regs[R_HASH_SRC], - &len, false, - MEMTXATTRS_UNSPECIFIED); i = 1; if (s->iov_count) { @@ -338,14 +347,14 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, int algo; data &= ahc->hash_mask; - if ((data & HASH_HMAC_MASK)) { + if ((data & HASH_DIGEST_HMAC)) { qemu_log_mask(LOG_UNIMP, - "%s: HMAC engine command mode %"PRIx64" not implemented", - __func__, (data & HASH_HMAC_MASK) >> 8); + "%s: HMAC mode not implemented\n", + __func__); } if (data & BIT(1)) { qemu_log_mask(LOG_UNIMP, - "%s: Cascaded mode not implemented", + "%s: Cascaded mode not implemented\n", __func__); } algo = hash_algo_lookup(data); @@ -424,7 +433,7 @@ static const VMStateDescription vmstate_aspeed_hace = { .name = TYPE_ASPEED_HACE, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedHACEState, ASPEED_HACE_NR_REGS), VMSTATE_UINT32(total_req_len, AspeedHACEState), VMSTATE_UINT32(iov_count, AspeedHACEState), diff --git a/hw/misc/aspeed_i3c.c b/hw/misc/aspeed_i3c.c index f54f5da522..827c9e522d 100644 --- a/hw/misc/aspeed_i3c.c +++ b/hw/misc/aspeed_i3c.c @@ -168,7 +168,7 @@ static const VMStateDescription aspeed_i3c_device_vmstate = { .name = TYPE_ASPEED_I3C, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]){ + .fields = (const VMStateField[]){ VMSTATE_UINT32_ARRAY(regs, AspeedI3CDevice, ASPEED_I3C_DEVICE_NR_REGS), VMSTATE_END_OF_LIST(), } @@ -296,13 +296,13 @@ static void aspeed_i3c_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem); for (i = 0; i < ASPEED_I3C_NR_DEVICES; ++i) { - Object *dev = OBJECT(&s->devices[i]); + Object *i3c_dev = OBJECT(&s->devices[i]); - if (!object_property_set_uint(dev, "device-id", i, errp)) { + if (!object_property_set_uint(i3c_dev, "device-id", i, errp)) { return; } - if (!sysbus_realize(SYS_BUS_DEVICE(dev), errp)) { + if (!sysbus_realize(SYS_BUS_DEVICE(i3c_dev), errp)) { return; } @@ -349,7 +349,7 @@ static const VMStateDescription vmstate_aspeed_i3c = { .name = TYPE_ASPEED_I3C, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedI3CState, ASPEED_I3C_NR_REGS), VMSTATE_STRUCT_ARRAY(devices, AspeedI3CState, ASPEED_I3C_NR_DEVICES, 1, aspeed_i3c_device_vmstate, AspeedI3CDevice), diff --git a/hw/misc/aspeed_lpc.c b/hw/misc/aspeed_lpc.c index 2dddb27c35..193f0dea59 100644 --- a/hw/misc/aspeed_lpc.c +++ b/hw/misc/aspeed_lpc.c @@ -447,7 +447,7 @@ static const VMStateDescription vmstate_aspeed_lpc = { .name = TYPE_ASPEED_LPC, .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedLPCState, ASPEED_LPC_NR_REGS), VMSTATE_UINT32(subdevice_irqs_pending, AspeedLPCState), VMSTATE_END_OF_LIST(), diff --git a/hw/misc/aspeed_peci.c b/hw/misc/aspeed_peci.c new file mode 100644 index 0000000000..93cc672e96 --- /dev/null +++ b/hw/misc/aspeed_peci.c @@ -0,0 +1,152 @@ +/* + * Aspeed PECI Controller + * + * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com) + * + * 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 "hw/irq.h" +#include "hw/misc/aspeed_peci.h" +#include "hw/registerfields.h" +#include "trace.h" + +#define ASPEED_PECI_CC_RSP_SUCCESS (0x40U) + +/* Command Register */ +REG32(PECI_CMD, 0x08) + FIELD(PECI_CMD, FIRE, 0, 1) + +/* Interrupt Control Register */ +REG32(PECI_INT_CTRL, 0x18) + +/* Interrupt Status Register */ +REG32(PECI_INT_STS, 0x1C) + FIELD(PECI_INT_STS, CMD_DONE, 0, 1) + +/* Rx/Tx Data Buffer Registers */ +REG32(PECI_WR_DATA0, 0x20) +REG32(PECI_RD_DATA0, 0x30) + +static void aspeed_peci_raise_interrupt(AspeedPECIState *s, uint32_t status) +{ + trace_aspeed_peci_raise_interrupt(s->regs[R_PECI_INT_CTRL], status); + + s->regs[R_PECI_INT_STS] = s->regs[R_PECI_INT_CTRL] & status; + if (!s->regs[R_PECI_INT_STS]) { + return; + } + qemu_irq_raise(s->irq); +} + +static uint64_t aspeed_peci_read(void *opaque, hwaddr offset, unsigned size) +{ + AspeedPECIState *s = ASPEED_PECI(opaque); + uint64_t data; + + if (offset >= ASPEED_PECI_NR_REGS << 2) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; + } + data = s->regs[offset >> 2]; + + trace_aspeed_peci_read(offset, data); + return data; +} + +static void aspeed_peci_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedPECIState *s = ASPEED_PECI(opaque); + + trace_aspeed_peci_write(offset, data); + + if (offset >= ASPEED_PECI_NR_REGS << 2) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return; + } + + switch (offset) { + case A_PECI_INT_STS: + s->regs[R_PECI_INT_STS] &= ~data; + if (!s->regs[R_PECI_INT_STS]) { + qemu_irq_lower(s->irq); + } + break; + case A_PECI_CMD: + /* + * Only the FIRE bit is writable. Once the command is complete, it + * should be cleared. Since we complete the command immediately, the + * value is not stored in the register array. + */ + if (!FIELD_EX32(data, PECI_CMD, FIRE)) { + break; + } + if (s->regs[R_PECI_INT_STS]) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Interrupt status must be " + "cleared before firing another command: 0x%08x\n", + __func__, s->regs[R_PECI_INT_STS]); + break; + } + s->regs[R_PECI_RD_DATA0] = ASPEED_PECI_CC_RSP_SUCCESS; + s->regs[R_PECI_WR_DATA0] = ASPEED_PECI_CC_RSP_SUCCESS; + aspeed_peci_raise_interrupt(s, + FIELD_DP32(0, PECI_INT_STS, CMD_DONE, 1)); + break; + default: + s->regs[offset / sizeof(s->regs[0])] = data; + break; + } +} + +static const MemoryRegionOps aspeed_peci_ops = { + .read = aspeed_peci_read, + .write = aspeed_peci_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void aspeed_peci_realize(DeviceState *dev, Error **errp) +{ + AspeedPECIState *s = ASPEED_PECI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_peci_ops, s, + TYPE_ASPEED_PECI, 0x1000); + sysbus_init_mmio(sbd, &s->mmio); + sysbus_init_irq(sbd, &s->irq); +} + +static void aspeed_peci_reset(DeviceState *dev) +{ + AspeedPECIState *s = ASPEED_PECI(dev); + + memset(s->regs, 0, sizeof(s->regs)); +} + +static void aspeed_peci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = aspeed_peci_realize; + dc->reset = aspeed_peci_reset; + dc->desc = "Aspeed PECI Controller"; +} + +static const TypeInfo aspeed_peci_types[] = { + { + .name = TYPE_ASPEED_PECI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedPECIState), + .class_init = aspeed_peci_class_init, + .abstract = false, + }, +}; + +DEFINE_TYPES(aspeed_peci_types); diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c index bfa8b81d01..8bb1f90e4e 100644 --- a/hw/misc/aspeed_sbc.c +++ b/hw/misc/aspeed_sbc.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/error-report.h" +#include "hw/qdev-properties.h" #include "hw/misc/aspeed_sbc.h" #include "qapi/error.h" #include "migration/vmstate.h" @@ -19,6 +20,27 @@ #define R_STATUS (0x014 / 4) #define R_QSR (0x040 / 4) +/* R_STATUS */ +#define ABR_EN BIT(14) /* Mirrors SCU510[11] */ +#define ABR_IMAGE_SOURCE BIT(13) +#define SPI_ABR_IMAGE_SOURCE BIT(12) +#define SB_CRYPTO_KEY_EXP_DONE BIT(11) +#define SB_CRYPTO_BUSY BIT(10) +#define OTP_WP_EN BIT(9) +#define OTP_ADDR_WP_EN BIT(8) +#define LOW_SEC_KEY_EN BIT(7) +#define SECURE_BOOT_EN BIT(6) +#define UART_BOOT_EN BIT(5) +/* bit 4 reserved*/ +#define OTP_CHARGE_PUMP_READY BIT(3) +#define OTP_IDLE BIT(2) +#define OTP_MEM_IDLE BIT(1) +#define OTP_COMPARE_STATUS BIT(0) + +/* QSR */ +#define QSR_RSA_MASK (0x3 << 12) +#define QSR_HASH_MASK (0x3 << 10) + static uint64_t aspeed_sbc_read(void *opaque, hwaddr addr, unsigned int size) { AspeedSBCState *s = ASPEED_SBC(opaque); @@ -80,8 +102,17 @@ static void aspeed_sbc_reset(DeviceState *dev) memset(s->regs, 0, sizeof(s->regs)); /* Set secure boot enabled with RSA4096_SHA256 and enable eMMC ABR */ - s->regs[R_STATUS] = 0x000044C6; - s->regs[R_QSR] = 0x07C07C89; + s->regs[R_STATUS] = OTP_IDLE | OTP_MEM_IDLE; + + if (s->emmc_abr) { + s->regs[R_STATUS] &= ABR_EN; + } + + if (s->signing_settings) { + s->regs[R_STATUS] &= SECURE_BOOT_EN; + } + + s->regs[R_QSR] = s->signing_settings; } static void aspeed_sbc_realize(DeviceState *dev, Error **errp) @@ -99,12 +130,18 @@ static const VMStateDescription vmstate_aspeed_sbc = { .name = TYPE_ASPEED_SBC, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedSBCState, ASPEED_SBC_NR_REGS), VMSTATE_END_OF_LIST(), } }; +static Property aspeed_sbc_properties[] = { + DEFINE_PROP_BOOL("emmc-abr", AspeedSBCState, emmc_abr, 0), + DEFINE_PROP_UINT32("signing-settings", AspeedSBCState, signing_settings, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static void aspeed_sbc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -112,6 +149,7 @@ static void aspeed_sbc_class_init(ObjectClass *klass, void *data) dc->realize = aspeed_sbc_realize; dc->reset = aspeed_sbc_reset; dc->vmsd = &vmstate_aspeed_sbc; + device_class_set_props(dc, aspeed_sbc_properties); } static const TypeInfo aspeed_sbc_info = { diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 19b03471fc..1ac04b6cb0 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -270,6 +270,7 @@ static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size) break; } + trace_aspeed_scu_read(offset, size, s->regs[reg]); return s->regs[reg]; } @@ -530,7 +531,7 @@ static const VMStateDescription vmstate_aspeed_scu = { .name = "aspeed.scu", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedSCUState, ASPEED_AST2600_SCU_NR_REGS), VMSTATE_END_OF_LIST() } @@ -637,6 +638,7 @@ static uint64_t aspeed_ast2600_scu_read(void *opaque, hwaddr offset, break; } + trace_aspeed_scu_read(offset, size, s->regs[reg]); return s->regs[reg]; } diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index d2a3931033..64cd1a81dc 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -12,7 +12,6 @@ #include "qemu/module.h" #include "qemu/error-report.h" #include "hw/misc/aspeed_sdmc.h" -#include "hw/misc/aspeed_scu.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qapi/error.h" @@ -244,7 +243,7 @@ static const VMStateDescription vmstate_aspeed_sdmc = { .name = "aspeed.sdmc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedSDMCState, ASPEED_SDMC_NR_REGS), VMSTATE_END_OF_LIST() } diff --git a/hw/misc/aspeed_xdma.c b/hw/misc/aspeed_xdma.c index 1c21577c98..76ab8467dd 100644 --- a/hw/misc/aspeed_xdma.c +++ b/hw/misc/aspeed_xdma.c @@ -144,7 +144,7 @@ static void aspeed_xdma_reset(DeviceState *dev) static const VMStateDescription aspeed_xdma_vmstate = { .name = TYPE_ASPEED_XDMA, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedXDMAState, ASPEED_XDMA_NUM_REGS), VMSTATE_END_OF_LIST(), }, diff --git a/hw/misc/auxbus.c b/hw/misc/auxbus.c index 8a8012f5f0..28d50d9d09 100644 --- a/hw/misc/auxbus.c +++ b/hw/misc/auxbus.c @@ -299,7 +299,7 @@ static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent) s = AUX_SLAVE(dev); - monitor_printf(mon, "%*smemory " TARGET_FMT_plx "/" TARGET_FMT_plx "\n", + monitor_printf(mon, "%*smemory " HWADDR_FMT_plx "/" HWADDR_FMT_plx "\n", indent, "", object_property_get_uint(OBJECT(s->mmio), "addr", NULL), memory_region_size(s->mmio)); diff --git a/hw/misc/axp2xx.c b/hw/misc/axp2xx.c new file mode 100644 index 0000000000..af646878cd --- /dev/null +++ b/hw/misc/axp2xx.c @@ -0,0 +1,283 @@ +/* + * AXP-2XX PMU Emulation, supported lists: + * AXP209 + * AXP221 + * + * Copyright (C) 2022 Strahinja Jankovic + * Copyright (C) 2023 qianfan Zhao + * + * 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. + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qom/object.h" +#include "trace.h" +#include "hw/i2c/i2c.h" +#include "migration/vmstate.h" + +#define TYPE_AXP2XX "axp2xx_pmu" +#define TYPE_AXP209_PMU "axp209_pmu" +#define TYPE_AXP221_PMU "axp221_pmu" + +OBJECT_DECLARE_TYPE(AXP2xxI2CState, AXP2xxClass, AXP2XX) + +#define NR_REGS (0xff) + +/* A simple I2C slave which returns values of ID or CNT register. */ +typedef struct AXP2xxI2CState { + /*< private >*/ + I2CSlave i2c; + /*< public >*/ + uint8_t regs[NR_REGS]; /* peripheral registers */ + uint8_t ptr; /* current register index */ + uint8_t count; /* counter used for tx/rx */ +} AXP2xxI2CState; + +typedef struct AXP2xxClass { + /*< private >*/ + I2CSlaveClass parent_class; + /*< public >*/ + void (*reset_enter)(AXP2xxI2CState *s, ResetType type); +} AXP2xxClass; + +#define AXP209_CHIP_VERSION_ID (0x01) +#define AXP209_DC_DC2_OUT_V_CTRL_RESET (0x16) + +/* Reset all counters and load ID register */ +static void axp209_reset_enter(AXP2xxI2CState *s, ResetType type) +{ + memset(s->regs, 0, NR_REGS); + s->ptr = 0; + s->count = 0; + + s->regs[0x03] = AXP209_CHIP_VERSION_ID; + s->regs[0x23] = AXP209_DC_DC2_OUT_V_CTRL_RESET; + + s->regs[0x30] = 0x60; + s->regs[0x32] = 0x46; + s->regs[0x34] = 0x41; + s->regs[0x35] = 0x22; + s->regs[0x36] = 0x5d; + s->regs[0x37] = 0x08; + s->regs[0x38] = 0xa5; + s->regs[0x39] = 0x1f; + s->regs[0x3a] = 0x68; + s->regs[0x3b] = 0x5f; + s->regs[0x3c] = 0xfc; + s->regs[0x3d] = 0x16; + s->regs[0x40] = 0xd8; + s->regs[0x42] = 0xff; + s->regs[0x43] = 0x3b; + s->regs[0x80] = 0xe0; + s->regs[0x82] = 0x83; + s->regs[0x83] = 0x80; + s->regs[0x84] = 0x32; + s->regs[0x86] = 0xff; + s->regs[0x90] = 0x07; + s->regs[0x91] = 0xa0; + s->regs[0x92] = 0x07; + s->regs[0x93] = 0x07; +} + +#define AXP221_PWR_STATUS_ACIN_PRESENT BIT(7) +#define AXP221_PWR_STATUS_ACIN_AVAIL BIT(6) +#define AXP221_PWR_STATUS_VBUS_PRESENT BIT(5) +#define AXP221_PWR_STATUS_VBUS_USED BIT(4) +#define AXP221_PWR_STATUS_BAT_CHARGING BIT(2) +#define AXP221_PWR_STATUS_ACIN_VBUS_POWERED BIT(1) + +/* Reset all counters and load ID register */ +static void axp221_reset_enter(AXP2xxI2CState *s, ResetType type) +{ + memset(s->regs, 0, NR_REGS); + s->ptr = 0; + s->count = 0; + + /* input power status register */ + s->regs[0x00] = AXP221_PWR_STATUS_ACIN_PRESENT + | AXP221_PWR_STATUS_ACIN_AVAIL + | AXP221_PWR_STATUS_ACIN_VBUS_POWERED; + + s->regs[0x01] = 0x00; /* no battery is connected */ + + /* + * CHIPID register, no documented on datasheet, but it is checked in + * u-boot spl. I had read it from AXP221s and got 0x06 value. + * So leave 06h here. + */ + s->regs[0x03] = 0x06; + + s->regs[0x10] = 0xbf; + s->regs[0x13] = 0x01; + s->regs[0x30] = 0x60; + s->regs[0x31] = 0x03; + s->regs[0x32] = 0x43; + s->regs[0x33] = 0xc6; + s->regs[0x34] = 0x45; + s->regs[0x35] = 0x0e; + s->regs[0x36] = 0x5d; + s->regs[0x37] = 0x08; + s->regs[0x38] = 0xa5; + s->regs[0x39] = 0x1f; + s->regs[0x3c] = 0xfc; + s->regs[0x3d] = 0x16; + s->regs[0x80] = 0x80; + s->regs[0x82] = 0xe0; + s->regs[0x84] = 0x32; + s->regs[0x8f] = 0x01; + + s->regs[0x90] = 0x07; + s->regs[0x91] = 0x1f; + s->regs[0x92] = 0x07; + s->regs[0x93] = 0x1f; + + s->regs[0x40] = 0xd8; + s->regs[0x41] = 0xff; + s->regs[0x42] = 0x03; + s->regs[0x43] = 0x03; + + s->regs[0xb8] = 0xc0; + s->regs[0xb9] = 0x64; + s->regs[0xe6] = 0xa0; +} + +static void axp2xx_reset_enter(Object *obj, ResetType type) +{ + AXP2xxI2CState *s = AXP2XX(obj); + AXP2xxClass *sc = AXP2XX_GET_CLASS(s); + + sc->reset_enter(s, type); +} + +/* Handle events from master. */ +static int axp2xx_event(I2CSlave *i2c, enum i2c_event event) +{ + AXP2xxI2CState *s = AXP2XX(i2c); + + s->count = 0; + + return 0; +} + +/* Called when master requests read */ +static uint8_t axp2xx_rx(I2CSlave *i2c) +{ + AXP2xxI2CState *s = AXP2XX(i2c); + uint8_t ret = 0xff; + + if (s->ptr < NR_REGS) { + ret = s->regs[s->ptr++]; + } + + trace_axp2xx_rx(s->ptr - 1, ret); + + return ret; +} + +/* + * Called when master sends write. + * Update ptr with byte 0, then perform write with second byte. + */ +static int axp2xx_tx(I2CSlave *i2c, uint8_t data) +{ + AXP2xxI2CState *s = AXP2XX(i2c); + + if (s->count == 0) { + /* Store register address */ + s->ptr = data; + s->count++; + trace_axp2xx_select(data); + } else { + trace_axp2xx_tx(s->ptr, data); + s->regs[s->ptr++] = data; + } + + return 0; +} + +static const VMStateDescription vmstate_axp2xx = { + .name = TYPE_AXP2XX, + .version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT8_ARRAY(regs, AXP2xxI2CState, NR_REGS), + VMSTATE_UINT8(ptr, AXP2xxI2CState), + VMSTATE_UINT8(count, AXP2xxI2CState), + VMSTATE_END_OF_LIST() + } +}; + +static void axp2xx_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); + + rc->phases.enter = axp2xx_reset_enter; + dc->vmsd = &vmstate_axp2xx; + isc->event = axp2xx_event; + isc->recv = axp2xx_rx; + isc->send = axp2xx_tx; +} + +static const TypeInfo axp2xx_info = { + .name = TYPE_AXP2XX, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(AXP2xxI2CState), + .class_size = sizeof(AXP2xxClass), + .class_init = axp2xx_class_init, + .abstract = true, +}; + +static void axp209_class_init(ObjectClass *oc, void *data) +{ + AXP2xxClass *sc = AXP2XX_CLASS(oc); + + sc->reset_enter = axp209_reset_enter; +} + +static const TypeInfo axp209_info = { + .name = TYPE_AXP209_PMU, + .parent = TYPE_AXP2XX, + .class_init = axp209_class_init +}; + +static void axp221_class_init(ObjectClass *oc, void *data) +{ + AXP2xxClass *sc = AXP2XX_CLASS(oc); + + sc->reset_enter = axp221_reset_enter; +} + +static const TypeInfo axp221_info = { + .name = TYPE_AXP221_PMU, + .parent = TYPE_AXP2XX, + .class_init = axp221_class_init, +}; + +static void axp2xx_register_devices(void) +{ + type_register_static(&axp2xx_info); + type_register_static(&axp209_info); + type_register_static(&axp221_info); +} + +type_init(axp2xx_register_devices); diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c index 75e6c574d4..91c8f7bd17 100644 --- a/hw/misc/bcm2835_cprman.c +++ b/hw/misc/bcm2835_cprman.c @@ -125,7 +125,7 @@ static const VMStateDescription pll_vmstate = { .name = TYPE_CPRMAN_PLL, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CLOCK(xosc_in, CprmanPllState), VMSTATE_END_OF_LIST() } @@ -229,7 +229,7 @@ static const VMStateDescription pll_channel_vmstate = { .name = TYPE_CPRMAN_PLL_CHANNEL, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CLOCK(pll_in, CprmanPllChannelState), VMSTATE_END_OF_LIST() } @@ -349,7 +349,7 @@ static const VMStateDescription clock_mux_vmstate = { .name = TYPE_CPRMAN_CLOCK_MUX, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_ARRAY_CLOCK(srcs, CprmanClockMuxState, CPRMAN_NUM_CLOCK_MUX_SRC), VMSTATE_END_OF_LIST() @@ -404,7 +404,7 @@ static const VMStateDescription dsi0hsck_mux_vmstate = { .name = TYPE_CPRMAN_DSI0HSCK_MUX, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CLOCK(plla_in, CprmanDsi0HsckMuxState), VMSTATE_CLOCK(plld_in, CprmanDsi0HsckMuxState), VMSTATE_END_OF_LIST() @@ -772,7 +772,7 @@ static const VMStateDescription cprman_vmstate = { .name = TYPE_BCM2835_CPRMAN, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, BCM2835CprmanState, CPRMAN_NUM_REGS), VMSTATE_END_OF_LIST() } diff --git a/hw/misc/bcm2835_mbox.c b/hw/misc/bcm2835_mbox.c index 1e4e061bc1..67bfc3bd71 100644 --- a/hw/misc/bcm2835_mbox.c +++ b/hw/misc/bcm2835_mbox.c @@ -257,7 +257,7 @@ static const VMStateDescription vmstate_bcm2835_mbox_box = { .name = TYPE_BCM2835_MBOX "_box", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(reg, BCM2835Mbox, MBOX_SIZE), VMSTATE_UINT32(count, BCM2835Mbox), VMSTATE_UINT32(status, BCM2835Mbox), @@ -271,7 +271,7 @@ static const VMStateDescription vmstate_bcm2835_mbox = { .name = TYPE_BCM2835_MBOX, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL_ARRAY(available, BCM2835MboxState, MBOX_CHAN_COUNT), VMSTATE_STRUCT_ARRAY(mbox, BCM2835MboxState, 2, 1, vmstate_bcm2835_mbox_box, BCM2835Mbox), diff --git a/hw/misc/bcm2835_mphi.c b/hw/misc/bcm2835_mphi.c index 0428e10ba5..f1eeda2786 100644 --- a/hw/misc/bcm2835_mphi.c +++ b/hw/misc/bcm2835_mphi.c @@ -156,7 +156,7 @@ const VMStateDescription vmstate_mphi_state = { .name = "mphi", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(outdda, BCM2835MphiState), VMSTATE_UINT32(outddb, BCM2835MphiState), VMSTATE_UINT32(ctrl, BCM2835MphiState), diff --git a/hw/misc/bcm2835_powermgt.c b/hw/misc/bcm2835_powermgt.c index 976f3d34e5..1649da8668 100644 --- a/hw/misc/bcm2835_powermgt.c +++ b/hw/misc/bcm2835_powermgt.c @@ -109,7 +109,7 @@ static const VMStateDescription vmstate_bcm2835_powermgt = { .name = TYPE_BCM2835_POWERMGT, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(rstc, BCM2835PowerMgtState), VMSTATE_UINT32(rsts, BCM2835PowerMgtState), VMSTATE_UINT32(wdog, BCM2835PowerMgtState), diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index e94e951057..bdd9a6bbce 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -12,10 +12,14 @@ #include "migration/vmstate.h" #include "hw/irq.h" #include "hw/misc/bcm2835_mbox_defs.h" +#include "hw/arm/raspberrypi-fw-defs.h" #include "sysemu/dma.h" #include "qemu/log.h" #include "qemu/module.h" #include "trace.h" +#include "hw/arm/raspi_platform.h" + +#define VCHI_BUSADDR_SIZE sizeof(uint32_t) /* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ @@ -51,48 +55,48 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* @(value + 8) : Request/response indicator */ resplen = 0; switch (tag) { - case 0x00000000: /* End tag */ + case RPI_FWREQ_PROPERTY_END: break; - case 0x00000001: /* Get firmware revision */ + case RPI_FWREQ_GET_FIRMWARE_REVISION: stl_le_phys(&s->dma_as, value + 12, 346337); resplen = 4; break; - case 0x00010001: /* Get board model */ + case RPI_FWREQ_GET_BOARD_MODEL: qemu_log_mask(LOG_UNIMP, "bcm2835_property: 0x%08x get board model NYI\n", tag); resplen = 4; break; - case 0x00010002: /* Get board revision */ + case RPI_FWREQ_GET_BOARD_REVISION: stl_le_phys(&s->dma_as, value + 12, s->board_rev); resplen = 4; break; - case 0x00010003: /* Get board MAC address */ + case RPI_FWREQ_GET_BOARD_MAC_ADDRESS: resplen = sizeof(s->macaddr.a); dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen, MEMTXATTRS_UNSPECIFIED); break; - case 0x00010004: /* Get board serial */ + case RPI_FWREQ_GET_BOARD_SERIAL: qemu_log_mask(LOG_UNIMP, "bcm2835_property: 0x%08x get board serial NYI\n", tag); resplen = 8; break; - case 0x00010005: /* Get ARM memory */ + case RPI_FWREQ_GET_ARM_MEMORY: /* base */ stl_le_phys(&s->dma_as, value + 12, 0); /* size */ stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base); resplen = 8; break; - case 0x00010006: /* Get VC memory */ + case RPI_FWREQ_GET_VC_MEMORY: /* base */ stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base); /* size */ stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size); resplen = 8; break; - case 0x00028001: /* Set power state */ + case RPI_FWREQ_SET_POWER_STATE: /* Assume that whatever device they asked for exists, * and we'll just claim we set it to the desired state */ @@ -103,38 +107,49 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* Clocks */ - case 0x00030001: /* Get clock state */ + case RPI_FWREQ_GET_CLOCK_STATE: stl_le_phys(&s->dma_as, value + 16, 0x1); resplen = 8; break; - case 0x00038001: /* Set clock state */ + case RPI_FWREQ_SET_CLOCK_STATE: qemu_log_mask(LOG_UNIMP, "bcm2835_property: 0x%08x set clock state NYI\n", tag); resplen = 8; break; - case 0x00030002: /* Get clock rate */ - case 0x00030004: /* Get max clock rate */ - case 0x00030007: /* Get min clock rate */ + case RPI_FWREQ_GET_CLOCK_RATE: + case RPI_FWREQ_GET_MAX_CLOCK_RATE: + case RPI_FWREQ_GET_MIN_CLOCK_RATE: switch (ldl_le_phys(&s->dma_as, value + 12)) { - case 1: /* EMMC */ - stl_le_phys(&s->dma_as, value + 16, 50000000); + case RPI_FIRMWARE_EMMC_CLK_ID: + stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_EMMC_CLK_RATE); break; - case 2: /* UART */ - stl_le_phys(&s->dma_as, value + 16, 3000000); + case RPI_FIRMWARE_UART_CLK_ID: + stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_UART_CLK_RATE); + break; + case RPI_FIRMWARE_CORE_CLK_ID: + stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_CORE_CLK_RATE); break; default: - stl_le_phys(&s->dma_as, value + 16, 700000000); + stl_le_phys(&s->dma_as, value + 16, + RPI_FIRMWARE_DEFAULT_CLK_RATE); break; } resplen = 8; break; - case 0x00038002: /* Set clock rate */ - case 0x00038004: /* Set max clock rate */ - case 0x00038007: /* Set min clock rate */ + case RPI_FWREQ_GET_CLOCKS: + /* TODO: add more clock IDs if needed */ + stl_le_phys(&s->dma_as, value + 12, 0); + stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_ARM_CLK_ID); + resplen = 8; + break; + + case RPI_FWREQ_SET_CLOCK_RATE: + case RPI_FWREQ_SET_MAX_CLOCK_RATE: + case RPI_FWREQ_SET_MIN_CLOCK_RATE: qemu_log_mask(LOG_UNIMP, "bcm2835_property: 0x%08x set clock rate NYI\n", tag); @@ -143,121 +158,121 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* Temperature */ - case 0x00030006: /* Get temperature */ + case RPI_FWREQ_GET_TEMPERATURE: stl_le_phys(&s->dma_as, value + 16, 25000); resplen = 8; break; - case 0x0003000A: /* Get max temperature */ + case RPI_FWREQ_GET_MAX_TEMPERATURE: stl_le_phys(&s->dma_as, value + 16, 99000); resplen = 8; break; /* Frame buffer */ - case 0x00040001: /* Allocate buffer */ + case RPI_FWREQ_FRAMEBUFFER_ALLOCATE: stl_le_phys(&s->dma_as, value + 12, fbconfig.base); stl_le_phys(&s->dma_as, value + 16, bcm2835_fb_get_size(&fbconfig)); resplen = 8; break; - case 0x00048001: /* Release buffer */ + case RPI_FWREQ_FRAMEBUFFER_RELEASE: resplen = 0; break; - case 0x00040002: /* Blank screen */ + case RPI_FWREQ_FRAMEBUFFER_BLANK: resplen = 4; break; - case 0x00044003: /* Test physical display width/height */ - case 0x00044004: /* Test virtual display width/height */ + case RPI_FWREQ_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT: + case RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT: resplen = 8; break; - case 0x00048003: /* Set physical display width/height */ + case RPI_FWREQ_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT: fbconfig.xres = ldl_le_phys(&s->dma_as, value + 12); fbconfig.yres = ldl_le_phys(&s->dma_as, value + 16); bcm2835_fb_validate_config(&fbconfig); fbconfig_updated = true; /* fall through */ - case 0x00040003: /* Get physical display width/height */ + case RPI_FWREQ_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT: stl_le_phys(&s->dma_as, value + 12, fbconfig.xres); stl_le_phys(&s->dma_as, value + 16, fbconfig.yres); resplen = 8; break; - case 0x00048004: /* Set virtual display width/height */ + case RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT: fbconfig.xres_virtual = ldl_le_phys(&s->dma_as, value + 12); fbconfig.yres_virtual = ldl_le_phys(&s->dma_as, value + 16); bcm2835_fb_validate_config(&fbconfig); fbconfig_updated = true; /* fall through */ - case 0x00040004: /* Get virtual display width/height */ + case RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_WIDTH_HEIGHT: stl_le_phys(&s->dma_as, value + 12, fbconfig.xres_virtual); stl_le_phys(&s->dma_as, value + 16, fbconfig.yres_virtual); resplen = 8; break; - case 0x00044005: /* Test depth */ + case RPI_FWREQ_FRAMEBUFFER_TEST_DEPTH: resplen = 4; break; - case 0x00048005: /* Set depth */ + case RPI_FWREQ_FRAMEBUFFER_SET_DEPTH: fbconfig.bpp = ldl_le_phys(&s->dma_as, value + 12); bcm2835_fb_validate_config(&fbconfig); fbconfig_updated = true; /* fall through */ - case 0x00040005: /* Get depth */ + case RPI_FWREQ_FRAMEBUFFER_GET_DEPTH: stl_le_phys(&s->dma_as, value + 12, fbconfig.bpp); resplen = 4; break; - case 0x00044006: /* Test pixel order */ + case RPI_FWREQ_FRAMEBUFFER_TEST_PIXEL_ORDER: resplen = 4; break; - case 0x00048006: /* Set pixel order */ + case RPI_FWREQ_FRAMEBUFFER_SET_PIXEL_ORDER: fbconfig.pixo = ldl_le_phys(&s->dma_as, value + 12); bcm2835_fb_validate_config(&fbconfig); fbconfig_updated = true; /* fall through */ - case 0x00040006: /* Get pixel order */ + case RPI_FWREQ_FRAMEBUFFER_GET_PIXEL_ORDER: stl_le_phys(&s->dma_as, value + 12, fbconfig.pixo); resplen = 4; break; - case 0x00044007: /* Test pixel alpha */ + case RPI_FWREQ_FRAMEBUFFER_TEST_ALPHA_MODE: resplen = 4; break; - case 0x00048007: /* Set alpha */ + case RPI_FWREQ_FRAMEBUFFER_SET_ALPHA_MODE: fbconfig.alpha = ldl_le_phys(&s->dma_as, value + 12); bcm2835_fb_validate_config(&fbconfig); fbconfig_updated = true; /* fall through */ - case 0x00040007: /* Get alpha */ + case RPI_FWREQ_FRAMEBUFFER_GET_ALPHA_MODE: stl_le_phys(&s->dma_as, value + 12, fbconfig.alpha); resplen = 4; break; - case 0x00040008: /* Get pitch */ + case RPI_FWREQ_FRAMEBUFFER_GET_PITCH: stl_le_phys(&s->dma_as, value + 12, bcm2835_fb_get_pitch(&fbconfig)); resplen = 4; break; - case 0x00044009: /* Test virtual offset */ + case RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_OFFSET: resplen = 8; break; - case 0x00048009: /* Set virtual offset */ + case RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_OFFSET: fbconfig.xoffset = ldl_le_phys(&s->dma_as, value + 12); fbconfig.yoffset = ldl_le_phys(&s->dma_as, value + 16); bcm2835_fb_validate_config(&fbconfig); fbconfig_updated = true; /* fall through */ - case 0x00040009: /* Get virtual offset */ + case RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_OFFSET: stl_le_phys(&s->dma_as, value + 12, fbconfig.xoffset); stl_le_phys(&s->dma_as, value + 16, fbconfig.yoffset); resplen = 8; break; - case 0x0004000a: /* Get/Test/Set overscan */ - case 0x0004400a: - case 0x0004800a: + case RPI_FWREQ_FRAMEBUFFER_GET_OVERSCAN: + case RPI_FWREQ_FRAMEBUFFER_TEST_OVERSCAN: + case RPI_FWREQ_FRAMEBUFFER_SET_OVERSCAN: stl_le_phys(&s->dma_as, value + 12, 0); stl_le_phys(&s->dma_as, value + 16, 0); stl_le_phys(&s->dma_as, value + 20, 0); stl_le_phys(&s->dma_as, value + 24, 0); resplen = 16; break; - case 0x0004800b: /* Set palette */ + case RPI_FWREQ_FRAMEBUFFER_SET_PALETTE: offset = ldl_le_phys(&s->dma_as, value + 12); length = ldl_le_phys(&s->dma_as, value + 16); n = 0; @@ -271,16 +286,42 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 4; break; - case 0x00060001: /* Get DMA channels */ + case RPI_FWREQ_FRAMEBUFFER_GET_NUM_DISPLAYS: + stl_le_phys(&s->dma_as, value + 12, 1); + resplen = 4; + break; + + case RPI_FWREQ_GET_DMA_CHANNELS: /* channels 2-5 */ stl_le_phys(&s->dma_as, value + 12, 0x003C); resplen = 4; break; - case 0x00050001: /* Get command line */ - resplen = 0; + case RPI_FWREQ_GET_COMMAND_LINE: + /* + * We follow the firmware behaviour: no NUL terminator is + * written to the buffer, and if the buffer is too short + * we report the required length in the response header + * and copy nothing to the buffer. + */ + resplen = strlen(s->command_line); + if (bufsize >= resplen) + address_space_write(&s->dma_as, value + 12, + MEMTXATTRS_UNSPECIFIED, s->command_line, + resplen); break; + case RPI_FWREQ_GET_THROTTLED: + stl_le_phys(&s->dma_as, value + 12, 0); + resplen = 4; + break; + + case RPI_FWREQ_VCHIQ_INIT: + stl_le_phys(&s->dma_as, + value + offsetof(rpi_firmware_prop_request_t, payload), + 0); + resplen = VCHI_BUSADDR_SIZE; + break; default: qemu_log_mask(LOG_UNIMP, "bcm2835_property: unhandled tag 0x%08x\n", tag); @@ -364,7 +405,7 @@ static const VMStateDescription vmstate_bcm2835_property = { .name = TYPE_BCM2835_PROPERTY, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_MACADDR(macaddr, BCM2835PropertyState), VMSTATE_UINT32(addr, BCM2835PropertyState), VMSTATE_BOOL(pending, BCM2835PropertyState), @@ -378,6 +419,13 @@ static void bcm2835_property_init(Object *obj) memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s, TYPE_BCM2835_PROPERTY, 0x10); + + /* + * bcm2835_property_ops call into bcm2835_mbox, which in-turn reads from + * iomem. As such, mark iomem as re-entracy safe. + */ + s->iomem.disable_reentrancy_guard = true; + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq); } @@ -409,6 +457,7 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp) static Property bcm2835_property_props[] = { DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0), + DEFINE_PROP_STRING("command-line", BCM2835PropertyState, command_line), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/misc/bcm2835_rng.c b/hw/misc/bcm2835_rng.c index b3c80cf186..10e741b11d 100644 --- a/hw/misc/bcm2835_rng.c +++ b/hw/misc/bcm2835_rng.c @@ -99,7 +99,7 @@ static const VMStateDescription vmstate_bcm2835_rng = { .name = TYPE_BCM2835_RNG, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(rng_ctrl, BCM2835RngState), VMSTATE_UINT32(rng_status, BCM2835RngState), VMSTATE_END_OF_LIST() diff --git a/hw/misc/bcm2835_thermal.c b/hw/misc/bcm2835_thermal.c index c6f3b1ad60..ee7816b8a5 100644 --- a/hw/misc/bcm2835_thermal.c +++ b/hw/misc/bcm2835_thermal.c @@ -105,7 +105,7 @@ static const VMStateDescription bcm2835_thermal_vmstate = { .name = "bcm2835_thermal", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ctl, Bcm2835ThermalState), VMSTATE_END_OF_LIST() } diff --git a/hw/misc/cbus.c b/hw/misc/cbus.c index 3c3721ad2d..653e8ddcd5 100644 --- a/hw/misc/cbus.c +++ b/hw/misc/cbus.c @@ -133,7 +133,7 @@ static void cbus_sel(void *opaque, int line, int level) CBus *cbus_init(qemu_irq dat) { - CBusPriv *s = (CBusPriv *) g_malloc0(sizeof(*s)); + CBusPriv *s = g_malloc0(sizeof(*s)); s->dat_out = dat; s->cbus.clk = qemu_allocate_irq(cbus_clk, s, 0); @@ -388,7 +388,7 @@ static void retu_io(void *opaque, int rw, int reg, uint16_t *val) void *retu_init(qemu_irq irq, int vilma) { - CBusRetu *s = (CBusRetu *) g_malloc0(sizeof(*s)); + CBusRetu *s = g_malloc0(sizeof(*s)); s->irq = irq; s->irqen = 0xffff; @@ -604,7 +604,7 @@ static void tahvo_io(void *opaque, int rw, int reg, uint16_t *val) void *tahvo_init(qemu_irq irq, int betty) { - CBusTahvo *s = (CBusTahvo *) g_malloc0(sizeof(*s)); + CBusTahvo *s = g_malloc0(sizeof(*s)); s->irq = irq; s->irqen = 0xffff; diff --git a/hw/misc/djmemc.c b/hw/misc/djmemc.c new file mode 100644 index 0000000000..9b69656c3a --- /dev/null +++ b/hw/misc/djmemc.c @@ -0,0 +1,135 @@ +/* + * djMEMC, macintosh memory and interrupt controller + * (Quadra 610/650/800 & Centris 610/650) + * + * https://mac68k.info/wiki/display/mac68k/djMEMC+Information + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "migration/vmstate.h" +#include "hw/misc/djmemc.h" +#include "hw/qdev-properties.h" +#include "trace.h" + + +#define DJMEMC_INTERLEAVECONF 0x0 +#define DJMEMC_BANK0CONF 0x4 +#define DJMEMC_BANK1CONF 0x8 +#define DJMEMC_BANK2CONF 0xc +#define DJMEMC_BANK3CONF 0x10 +#define DJMEMC_BANK4CONF 0x14 +#define DJMEMC_BANK5CONF 0x18 +#define DJMEMC_BANK6CONF 0x1c +#define DJMEMC_BANK7CONF 0x20 +#define DJMEMC_BANK8CONF 0x24 +#define DJMEMC_BANK9CONF 0x28 +#define DJMEMC_MEMTOP 0x2c +#define DJMEMC_CONFIG 0x30 +#define DJMEMC_REFRESH 0x34 + + +static uint64_t djmemc_read(void *opaque, hwaddr addr, unsigned size) +{ + DJMEMCState *s = opaque; + uint64_t val = 0; + + switch (addr) { + case DJMEMC_INTERLEAVECONF: + case DJMEMC_BANK0CONF ... DJMEMC_BANK9CONF: + case DJMEMC_MEMTOP: + case DJMEMC_CONFIG: + case DJMEMC_REFRESH: + val = s->regs[addr >> 2]; + break; + default: + qemu_log_mask(LOG_UNIMP, "djMEMC: unimplemented read addr=0x%"PRIx64 + " val=0x%"PRIx64 " size=%d\n", + addr, val, size); + } + + trace_djmemc_read(addr, val, size); + return val; +} + +static void djmemc_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + DJMEMCState *s = opaque; + + trace_djmemc_write(addr, val, size); + + switch (addr) { + case DJMEMC_INTERLEAVECONF: + case DJMEMC_BANK0CONF ... DJMEMC_BANK9CONF: + case DJMEMC_MEMTOP: + case DJMEMC_CONFIG: + case DJMEMC_REFRESH: + s->regs[addr >> 2] = val; + break; + default: + qemu_log_mask(LOG_UNIMP, "djMEMC: unimplemented write addr=0x%"PRIx64 + " val=0x%"PRIx64 " size=%d\n", + addr, val, size); + } +} + +static const MemoryRegionOps djmemc_mmio_ops = { + .read = djmemc_read, + .write = djmemc_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void djmemc_init(Object *obj) +{ + DJMEMCState *s = DJMEMC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->mem_regs, obj, &djmemc_mmio_ops, s, "djMEMC", + DJMEMC_SIZE); + sysbus_init_mmio(sbd, &s->mem_regs); +} + +static void djmemc_reset_hold(Object *obj) +{ + DJMEMCState *s = DJMEMC(obj); + + memset(s->regs, 0, sizeof(s->regs)); +} + +static const VMStateDescription vmstate_djmemc = { + .name = "djMEMC", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, DJMEMCState, DJMEMC_NUM_REGS), + VMSTATE_END_OF_LIST() + } +}; + +static void djmemc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); + + dc->vmsd = &vmstate_djmemc; + rc->phases.hold = djmemc_reset_hold; +} + +static const TypeInfo djmemc_info_types[] = { + { + .name = TYPE_DJMEMC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(DJMEMCState), + .instance_init = djmemc_init, + .class_init = djmemc_class_init, + }, +}; + +DEFINE_TYPES(djmemc_info_types) diff --git a/hw/misc/eccmemctl.c b/hw/misc/eccmemctl.c index c65806e3d9..5a14a48999 100644 --- a/hw/misc/eccmemctl.c +++ b/hw/misc/eccmemctl.c @@ -272,7 +272,7 @@ static const VMStateDescription vmstate_ecc = { .name ="ECC", .version_id = 3, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, ECCState, ECC_NREGS), VMSTATE_BUFFER(diag, ECCState), VMSTATE_UINT32(version, ECCState), diff --git a/hw/misc/edu.c b/hw/misc/edu.c index e935c418d4..2a976ca2b1 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -115,7 +115,7 @@ static void edu_check_range(uint64_t addr, uint64_t size1, uint64_t start, uint64_t end2 = start + size2; if (within(addr, start, end2) && - end1 > addr && within(end1, start, end2)) { + end1 > addr && end1 <= end2) { return; } @@ -267,6 +267,8 @@ static void edu_mmio_write(void *opaque, hwaddr addr, uint64_t val, case 0x20: if (val & EDU_STATUS_IRQFACT) { qatomic_or(&edu->status, EDU_STATUS_IRQFACT); + /* Order check of the COMPUTING flag after setting IRQFACT. */ + smp_mb__after_rmw(); } else { qatomic_and(&edu->status, ~EDU_STATUS_IRQFACT); } @@ -349,10 +351,13 @@ static void *edu_fact_thread(void *opaque) qemu_mutex_unlock(&edu->thr_mutex); qatomic_and(&edu->status, ~EDU_STATUS_COMPUTING); + /* Clear COMPUTING flag before checking IRQFACT. */ + smp_mb__after_rmw(); + if (qatomic_read(&edu->status) & EDU_STATUS_IRQFACT) { - qemu_mutex_lock_iothread(); + bql_lock(); edu_raise_irq(edu, FACT_IRQ); - qemu_mutex_unlock_iothread(); + bql_unlock(); } } diff --git a/hw/misc/exynos4210_clk.c b/hw/misc/exynos4210_clk.c index 58cec282f7..4566a426fa 100644 --- a/hw/misc/exynos4210_clk.c +++ b/hw/misc/exynos4210_clk.c @@ -135,7 +135,7 @@ static const VMStateDescription exynos4210_clk_vmstate = { .name = TYPE_EXYNOS4210_CLK, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(reg, Exynos4210ClkState, EXYNOS4210_REGS_NUM), VMSTATE_END_OF_LIST() } diff --git a/hw/misc/exynos4210_pmu.c b/hw/misc/exynos4210_pmu.c index e24139c630..7e28e790d7 100644 --- a/hw/misc/exynos4210_pmu.c +++ b/hw/misc/exynos4210_pmu.c @@ -492,7 +492,7 @@ static const VMStateDescription exynos4210_pmu_vmstate = { .name = "exynos4210.pmu", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(reg, Exynos4210PmuState, PMU_NUM_OF_REGISTERS), VMSTATE_END_OF_LIST() } diff --git a/hw/misc/exynos4210_rng.c b/hw/misc/exynos4210_rng.c index 1b9e8347a1..0756bd3205 100644 --- a/hw/misc/exynos4210_rng.c +++ b/hw/misc/exynos4210_rng.c @@ -1,5 +1,5 @@ /* - * Exynos4210 Pseudo Random Nubmer Generator Emulation + * Exynos4210 Pseudo Random Number Generator Emulation * * Copyright (c) 2017 Krzysztof Kozlowski * @@ -243,7 +243,7 @@ static const VMStateDescription exynos4210_rng_vmstate = { .name = TYPE_EXYNOS4210_RNG, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32_ARRAY(randr_value, Exynos4210RngState, EXYNOS4210_RNG_PRNG_NUM), VMSTATE_UINT32(seed_set, Exynos4210RngState), diff --git a/hw/misc/grlib_ahb_apb_pnp.c b/hw/misc/grlib_ahb_apb_pnp.c index 43e001c3c7..5b05f15859 100644 --- a/hw/misc/grlib_ahb_apb_pnp.c +++ b/hw/misc/grlib_ahb_apb_pnp.c @@ -136,7 +136,8 @@ static uint64_t grlib_ahb_pnp_read(void *opaque, hwaddr offset, unsigned size) uint32_t val; val = ahb_pnp->regs[offset >> 2]; - trace_grlib_ahb_pnp_read(offset, val); + val = extract32(val, (4 - (offset & 3) - size) * 8, size * 8); + trace_grlib_ahb_pnp_read(offset, size, val); return val; } @@ -152,7 +153,7 @@ static const MemoryRegionOps grlib_ahb_pnp_ops = { .write = grlib_ahb_pnp_write, .endianness = DEVICE_BIG_ENDIAN, .impl = { - .min_access_size = 4, + .min_access_size = 1, .max_access_size = 4, }, }; @@ -247,7 +248,8 @@ static uint64_t grlib_apb_pnp_read(void *opaque, hwaddr offset, unsigned size) uint32_t val; val = apb_pnp->regs[offset >> 2]; - trace_grlib_apb_pnp_read(offset, val); + val = extract32(val, (4 - (offset & 3) - size) * 8, size * 8); + trace_grlib_apb_pnp_read(offset, size, val); return val; } @@ -263,7 +265,7 @@ static const MemoryRegionOps grlib_apb_pnp_ops = { .write = grlib_apb_pnp_write, .endianness = DEVICE_BIG_ENDIAN, .impl = { - .min_access_size = 4, + .min_access_size = 1, .max_access_size = 4, }, }; diff --git a/hw/misc/i2c-echo.c b/hw/misc/i2c-echo.c new file mode 100644 index 0000000000..5ae3d0817e --- /dev/null +++ b/hw/misc/i2c-echo.c @@ -0,0 +1,166 @@ +/* + * Example I2C device using asynchronous I2C send. + * + * Copyright (C) 2023 Samsung Electronics Co., Ltd. All Rights Reserved. + * + * 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/timer.h" +#include "qemu/main-loop.h" +#include "block/aio.h" +#include "hw/i2c/i2c.h" + +#define TYPE_I2C_ECHO "i2c-echo" +OBJECT_DECLARE_SIMPLE_TYPE(I2CEchoState, I2C_ECHO) + +enum i2c_echo_state { + I2C_ECHO_STATE_IDLE, + I2C_ECHO_STATE_START_SEND, + I2C_ECHO_STATE_ACK, +}; + +typedef struct I2CEchoState { + I2CSlave parent_obj; + + I2CBus *bus; + + enum i2c_echo_state state; + QEMUBH *bh; + + unsigned int pos; + uint8_t data[3]; +} I2CEchoState; + +static void i2c_echo_bh(void *opaque) +{ + I2CEchoState *state = opaque; + + switch (state->state) { + case I2C_ECHO_STATE_IDLE: + return; + + case I2C_ECHO_STATE_START_SEND: + if (i2c_start_send_async(state->bus, state->data[0])) { + goto release_bus; + } + + state->pos++; + state->state = I2C_ECHO_STATE_ACK; + return; + + case I2C_ECHO_STATE_ACK: + if (state->pos > 2) { + break; + } + + if (i2c_send_async(state->bus, state->data[state->pos++])) { + break; + } + + return; + } + + + i2c_end_transfer(state->bus); +release_bus: + i2c_bus_release(state->bus); + + state->state = I2C_ECHO_STATE_IDLE; +} + +static int i2c_echo_event(I2CSlave *s, enum i2c_event event) +{ + I2CEchoState *state = I2C_ECHO(s); + + switch (event) { + case I2C_START_RECV: + state->pos = 0; + + break; + + case I2C_START_SEND: + state->pos = 0; + + break; + + case I2C_FINISH: + state->pos = 0; + state->state = I2C_ECHO_STATE_START_SEND; + i2c_bus_master(state->bus, state->bh); + + break; + + case I2C_NACK: + break; + + default: + return -1; + } + + return 0; +} + +static uint8_t i2c_echo_recv(I2CSlave *s) +{ + I2CEchoState *state = I2C_ECHO(s); + + if (state->pos > 2) { + return 0xff; + } + + return state->data[state->pos++]; +} + +static int i2c_echo_send(I2CSlave *s, uint8_t data) +{ + I2CEchoState *state = I2C_ECHO(s); + + if (state->pos > 2) { + return -1; + } + + state->data[state->pos++] = data; + + return 0; +} + +static void i2c_echo_realize(DeviceState *dev, Error **errp) +{ + I2CEchoState *state = I2C_ECHO(dev); + BusState *bus = qdev_get_parent_bus(dev); + + state->bus = I2C_BUS(bus); + state->bh = qemu_bh_new(i2c_echo_bh, state); + + return; +} + +static void i2c_echo_class_init(ObjectClass *oc, void *data) +{ + I2CSlaveClass *sc = I2C_SLAVE_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = i2c_echo_realize; + + sc->event = i2c_echo_event; + sc->recv = i2c_echo_recv; + sc->send = i2c_echo_send; +} + +static const TypeInfo i2c_echo = { + .name = TYPE_I2C_ECHO, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(I2CEchoState), + .class_init = i2c_echo_class_init, +}; + +static void register_types(void) +{ + type_register_static(&i2c_echo); +} + +type_init(register_types); diff --git a/hw/misc/imx25_ccm.c b/hw/misc/imx25_ccm.c index ff996e2f2c..d888966014 100644 --- a/hw/misc/imx25_ccm.c +++ b/hw/misc/imx25_ccm.c @@ -101,7 +101,7 @@ static const VMStateDescription vmstate_imx25_ccm = { .name = TYPE_IMX25_CCM, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(reg, IMX25CCMState, IMX25_CCM_MAX_REG), VMSTATE_END_OF_LIST() }, diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c index ad30a4b2c0..a9059bb1f7 100644 --- a/hw/misc/imx31_ccm.c +++ b/hw/misc/imx31_ccm.c @@ -98,7 +98,7 @@ static const VMStateDescription vmstate_imx31_ccm = { .name = TYPE_IMX31_CCM, .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(reg, IMX31CCMState, IMX31_CCM_MAX_REG), VMSTATE_END_OF_LIST() }, diff --git a/hw/misc/imx6_ccm.c b/hw/misc/imx6_ccm.c index 4c830fd89a..56489d8b57 100644 --- a/hw/misc/imx6_ccm.c +++ b/hw/misc/imx6_ccm.c @@ -15,18 +15,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" - -#ifndef DEBUG_IMX6_CCM -#define DEBUG_IMX6_CCM 0 -#endif - -#define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_IMX6_CCM) { \ - fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_CCM, \ - __func__, ##args); \ - } \ - } while (0) +#include "trace.h" static const char *imx6_ccm_reg_name(uint32_t reg) { @@ -246,7 +235,7 @@ static const VMStateDescription vmstate_imx6_ccm = { .name = TYPE_IMX6_CCM, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(ccm, IMX6CCMState, CCM_MAX), VMSTATE_UINT32_ARRAY(analog, IMX6CCMState, CCM_ANALOG_MAX), VMSTATE_END_OF_LIST() @@ -263,7 +252,7 @@ static uint64_t imx6_analog_get_pll2_clk(IMX6CCMState *dev) freq *= 20; } - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_analog_get_pll2_clk(freq); return freq; } @@ -275,7 +264,7 @@ static uint64_t imx6_analog_get_pll2_pfd0_clk(IMX6CCMState *dev) freq = imx6_analog_get_pll2_clk(dev) * 18 / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD0_FRAC); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_analog_get_pll2_pfd0_clk(freq); return freq; } @@ -287,7 +276,7 @@ static uint64_t imx6_analog_get_pll2_pfd2_clk(IMX6CCMState *dev) freq = imx6_analog_get_pll2_clk(dev) * 18 / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD2_FRAC); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_analog_get_pll2_pfd2_clk(freq); return freq; } @@ -315,7 +304,7 @@ static uint64_t imx6_analog_get_periph_clk(IMX6CCMState *dev) break; } - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_analog_get_periph_clk(freq); return freq; } @@ -327,7 +316,7 @@ static uint64_t imx6_ccm_get_ahb_clk(IMX6CCMState *dev) freq = imx6_analog_get_periph_clk(dev) / (1 + EXTRACT(dev->ccm[CCM_CBCDR], AHB_PODF)); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_ccm_get_ahb_clk(freq); return freq; } @@ -339,7 +328,7 @@ static uint64_t imx6_ccm_get_ipg_clk(IMX6CCMState *dev) freq = imx6_ccm_get_ahb_clk(dev) / (1 + EXTRACT(dev->ccm[CCM_CBCDR], IPG_PODF)); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_ccm_get_ipg_clk(freq); return freq; } @@ -351,7 +340,7 @@ static uint64_t imx6_ccm_get_per_clk(IMX6CCMState *dev) freq = imx6_ccm_get_ipg_clk(dev) / (1 + EXTRACT(dev->ccm[CCM_CSCMR1], PERCLK_PODF)); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_ccm_get_per_clk(freq); return freq; } @@ -385,7 +374,7 @@ static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) break; } - DPRINTF("Clock = %d) = %u\n", clock, freq); + trace_imx6_ccm_get_clock_frequency(clock, freq); return freq; } @@ -394,7 +383,7 @@ static void imx6_ccm_reset(DeviceState *dev) { IMX6CCMState *s = IMX6_CCM(dev); - DPRINTF("\n"); + trace_imx6_ccm_reset(); s->ccm[CCM_CCR] = 0x040116FF; s->ccm[CCM_CCDR] = 0x00000000; @@ -483,7 +472,7 @@ static uint64_t imx6_ccm_read(void *opaque, hwaddr offset, unsigned size) value = s->ccm[index]; - DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), value); + trace_imx6_ccm_read(imx6_ccm_reg_name(index), value); return (uint64_t)value; } @@ -494,8 +483,7 @@ static void imx6_ccm_write(void *opaque, hwaddr offset, uint64_t value, uint32_t index = offset >> 2; IMX6CCMState *s = (IMX6CCMState *)opaque; - DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), - (uint32_t)value); + trace_imx6_ccm_write(imx6_ccm_reg_name(index), (uint32_t)value); /* * We will do a better implementation later. In particular some bits @@ -591,7 +579,7 @@ static uint64_t imx6_analog_read(void *opaque, hwaddr offset, unsigned size) break; } - DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_analog_reg_name(index), value); + trace_imx6_analog_read(imx6_analog_reg_name(index), value); return (uint64_t)value; } @@ -602,8 +590,7 @@ static void imx6_analog_write(void *opaque, hwaddr offset, uint64_t value, uint32_t index = offset >> 2; IMX6CCMState *s = (IMX6CCMState *)opaque; - DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_analog_reg_name(index), - (uint32_t)value); + trace_imx6_analog_write(imx6_analog_reg_name(index), (uint32_t)value); switch (index) { case CCM_ANALOG_PLL_ARM_SET: diff --git a/hw/misc/imx6_src.c b/hw/misc/imx6_src.c index 7b0e968804..0c6003559f 100644 --- a/hw/misc/imx6_src.c +++ b/hw/misc/imx6_src.c @@ -15,7 +15,7 @@ #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/module.h" -#include "arm-powerctl.h" +#include "target/arm/arm-powerctl.h" #include "hw/core/cpu.h" #ifndef DEBUG_IMX6_SRC @@ -77,7 +77,7 @@ static const VMStateDescription vmstate_imx6_src = { .name = TYPE_IMX6_SRC, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, IMX6SRCState, SRC_MAX), VMSTATE_END_OF_LIST() }, @@ -131,7 +131,7 @@ static void imx6_clear_reset_bit(CPUState *cpu, run_on_cpu_data data) struct SRCSCRResetInfo *ri = data.host_ptr; IMX6SRCState *s = ri->s; - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); s->regs[SRC_SCR] = deposit32(s->regs[SRC_SCR], ri->reset_bit, 1, 0); DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", diff --git a/hw/misc/imx6ul_ccm.c b/hw/misc/imx6ul_ccm.c index a65d031455..bbc0be9921 100644 --- a/hw/misc/imx6ul_ccm.c +++ b/hw/misc/imx6ul_ccm.c @@ -285,7 +285,7 @@ static const VMStateDescription vmstate_imx6ul_ccm = { .name = TYPE_IMX6UL_CCM, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(ccm, IMX6ULCCMState, CCM_MAX), VMSTATE_UINT32_ARRAY(analog, IMX6ULCCMState, CCM_ANALOG_MAX), VMSTATE_END_OF_LIST() @@ -522,12 +522,6 @@ static uint32_t imx6ul_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) case CLK_32k: freq = CKIL_FREQ; break; - case CLK_HIGH: - freq = CKIH_FREQ; - break; - case CLK_HIGH_DIV: - freq = CKIH_FREQ / 8; - break; default: qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", TYPE_IMX6UL_CCM, __func__, clock); diff --git a/hw/misc/imx7_ccm.c b/hw/misc/imx7_ccm.c index 075159e497..88354f020e 100644 --- a/hw/misc/imx7_ccm.c +++ b/hw/misc/imx7_ccm.c @@ -16,6 +16,10 @@ #include "hw/misc/imx7_ccm.h" #include "migration/vmstate.h" +#include "trace.h" + +#define CKIH_FREQ 24000000 /* 24MHz crystal input */ + static void imx7_analog_reset(DeviceState *dev) { IMX7AnalogState *s = IMX7_ANALOG(dev); @@ -210,7 +214,7 @@ static const VMStateDescription vmstate_imx7_ccm = { .name = TYPE_IMX7_CCM, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(ccm, IMX7CCMState, CCM_MAX), VMSTATE_END_OF_LIST() }, @@ -219,16 +223,43 @@ static const VMStateDescription vmstate_imx7_ccm = { static uint32_t imx7_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) { /* - * This function is "consumed" by GPT emulation code, however on - * i.MX7 each GPT block can have their own clock root. This means - * that this functions needs somehow to know requester's identity - * and the way to pass it: be it via additional IMXClk constants - * or by adding another argument to this method needs to be - * figured out + * This function is "consumed" by GPT emulation code. Some clocks + * have fixed frequencies and we can provide requested frequency + * easily. However for CCM provided clocks (like IPG) each GPT + * timer can have its own clock root. + * This means we need additional information when calling this + * function to know the requester's identity. */ - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Not implemented\n", - TYPE_IMX7_CCM, __func__); - return 0; + uint32_t freq = 0; + + switch (clock) { + case CLK_NONE: + break; + case CLK_32k: + freq = CKIL_FREQ; + break; + case CLK_HIGH: + freq = CKIH_FREQ; + break; + case CLK_IPG: + case CLK_IPG_HIGH: + /* + * For now we don't have a way to figure out the device this + * function is called for. Until then the IPG derived clocks + * are left unimplemented. + */ + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Clock %d Not implemented\n", + TYPE_IMX7_CCM, __func__, clock); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", + TYPE_IMX7_CCM, __func__, clock); + break; + } + + trace_ccm_clock_freq(clock, freq); + + return freq; } static void imx7_ccm_class_init(ObjectClass *klass, void *data) @@ -255,7 +286,7 @@ static const VMStateDescription vmstate_imx7_analog = { .name = TYPE_IMX7_ANALOG, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(analog, IMX7AnalogState, ANALOG_MAX), VMSTATE_UINT32_ARRAY(pmu, IMX7AnalogState, PMU_MAX), VMSTATE_END_OF_LIST() diff --git a/hw/misc/imx7_snvs.c b/hw/misc/imx7_snvs.c index ee7698bd9c..edb2df215a 100644 --- a/hw/misc/imx7_snvs.c +++ b/hw/misc/imx7_snvs.c @@ -13,23 +13,100 @@ */ #include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/timer.h" +#include "migration/vmstate.h" #include "hw/misc/imx7_snvs.h" +#include "qemu/cutils.h" #include "qemu/module.h" +#include "sysemu/sysemu.h" +#include "sysemu/rtc.h" #include "sysemu/runstate.h" +#include "trace.h" + +#define RTC_FREQ 32768ULL + +static const VMStateDescription vmstate_imx7_snvs = { + .name = TYPE_IMX7_SNVS, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(tick_offset, IMX7SNVSState), + VMSTATE_UINT64(lpcr, IMX7SNVSState), + VMSTATE_END_OF_LIST() + } +}; + +static uint64_t imx7_snvs_get_count(IMX7SNVSState *s) +{ + uint64_t ticks = muldiv64(qemu_clock_get_ns(rtc_clock), RTC_FREQ, + NANOSECONDS_PER_SECOND); + return s->tick_offset + ticks; +} static uint64_t imx7_snvs_read(void *opaque, hwaddr offset, unsigned size) { - return 0; + IMX7SNVSState *s = IMX7_SNVS(opaque); + uint64_t ret = 0; + + switch (offset) { + case SNVS_LPSRTCMR: + ret = extract64(imx7_snvs_get_count(s), 32, 15); + break; + case SNVS_LPSRTCLR: + ret = extract64(imx7_snvs_get_count(s), 0, 32); + break; + case SNVS_LPCR: + ret = s->lpcr; + break; + } + + trace_imx7_snvs_read(offset, ret, size); + + return ret; +} + +static void imx7_snvs_reset(DeviceState *dev) +{ + IMX7SNVSState *s = IMX7_SNVS(dev); + + s->lpcr = 0; } static void imx7_snvs_write(void *opaque, hwaddr offset, uint64_t v, unsigned size) { - const uint32_t value = v; - const uint32_t mask = SNVS_LPCR_TOP | SNVS_LPCR_DP_EN; + trace_imx7_snvs_write(offset, v, size); - if (offset == SNVS_LPCR && ((value & mask) == mask)) { - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + IMX7SNVSState *s = IMX7_SNVS(opaque); + + uint64_t new_value = 0, snvs_count = 0; + + if (offset == SNVS_LPSRTCMR || offset == SNVS_LPSRTCLR) { + snvs_count = imx7_snvs_get_count(s); + } + + switch (offset) { + case SNVS_LPSRTCMR: + new_value = deposit64(snvs_count, 32, 32, v); + break; + case SNVS_LPSRTCLR: + new_value = deposit64(snvs_count, 0, 32, v); + break; + case SNVS_LPCR: { + s->lpcr = v; + + const uint32_t mask = SNVS_LPCR_TOP | SNVS_LPCR_DP_EN; + + if ((v & mask) == mask) { + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + } + break; + } + } + + if (offset == SNVS_LPSRTCMR || offset == SNVS_LPSRTCLR) { + s->tick_offset += new_value - snvs_count; } } @@ -54,17 +131,24 @@ static void imx7_snvs_init(Object *obj) { SysBusDevice *sd = SYS_BUS_DEVICE(obj); IMX7SNVSState *s = IMX7_SNVS(obj); + struct tm tm; memory_region_init_io(&s->mmio, obj, &imx7_snvs_ops, s, TYPE_IMX7_SNVS, 0x1000); sysbus_init_mmio(sd, &s->mmio); + + qemu_get_timedate(&tm, 0); + s->tick_offset = mktimegm(&tm) - + qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND; } static void imx7_snvs_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->reset = imx7_snvs_reset; + dc->vmsd = &vmstate_imx7_snvs; dc->desc = "i.MX7 Secure Non-Volatile Storage Module"; } diff --git a/hw/misc/imx7_src.c b/hw/misc/imx7_src.c new file mode 100644 index 0000000000..b3725ff6e7 --- /dev/null +++ b/hw/misc/imx7_src.c @@ -0,0 +1,276 @@ +/* + * IMX7 System Reset Controller + * + * Copyright (c) 2023 Jean-Christophe Dubois + * + * 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/misc/imx7_src.h" +#include "migration/vmstate.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "qemu/module.h" +#include "target/arm/arm-powerctl.h" +#include "hw/core/cpu.h" +#include "hw/registerfields.h" + +#include "trace.h" + +static const char *imx7_src_reg_name(uint32_t reg) +{ + static char unknown[20]; + + switch (reg) { + case SRC_SCR: + return "SRC_SCR"; + case SRC_A7RCR0: + return "SRC_A7RCR0"; + case SRC_A7RCR1: + return "SRC_A7RCR1"; + case SRC_M4RCR: + return "SRC_M4RCR"; + case SRC_ERCR: + return "SRC_ERCR"; + case SRC_HSICPHY_RCR: + return "SRC_HSICPHY_RCR"; + case SRC_USBOPHY1_RCR: + return "SRC_USBOPHY1_RCR"; + case SRC_USBOPHY2_RCR: + return "SRC_USBOPHY2_RCR"; + case SRC_PCIEPHY_RCR: + return "SRC_PCIEPHY_RCR"; + case SRC_SBMR1: + return "SRC_SBMR1"; + case SRC_SRSR: + return "SRC_SRSR"; + case SRC_SISR: + return "SRC_SISR"; + case SRC_SIMR: + return "SRC_SIMR"; + case SRC_SBMR2: + return "SRC_SBMR2"; + case SRC_GPR1: + return "SRC_GPR1"; + case SRC_GPR2: + return "SRC_GPR2"; + case SRC_GPR3: + return "SRC_GPR3"; + case SRC_GPR4: + return "SRC_GPR4"; + case SRC_GPR5: + return "SRC_GPR5"; + case SRC_GPR6: + return "SRC_GPR6"; + case SRC_GPR7: + return "SRC_GPR7"; + case SRC_GPR8: + return "SRC_GPR8"; + case SRC_GPR9: + return "SRC_GPR9"; + case SRC_GPR10: + return "SRC_GPR10"; + default: + sprintf(unknown, "%u ?", reg); + return unknown; + } +} + +static const VMStateDescription vmstate_imx7_src = { + .name = TYPE_IMX7_SRC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, IMX7SRCState, SRC_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx7_src_reset(DeviceState *dev) +{ + IMX7SRCState *s = IMX7_SRC(dev); + + memset(s->regs, 0, sizeof(s->regs)); + + /* Set reset values */ + s->regs[SRC_SCR] = 0xA0; + s->regs[SRC_SRSR] = 0x1; + s->regs[SRC_SIMR] = 0x1F; +} + +static uint64_t imx7_src_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t value = 0; + IMX7SRCState *s = (IMX7SRCState *)opaque; + uint32_t index = offset >> 2; + + if (index < SRC_MAX) { + value = s->regs[index]; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX7_SRC, __func__, offset); + } + + trace_imx7_src_read(imx7_src_reg_name(index), value); + + return value; +} + + +/* + * The reset is asynchronous so we need to defer clearing the reset + * bit until the work is completed. + */ + +struct SRCSCRResetInfo { + IMX7SRCState *s; + uint32_t reset_bit; +}; + +static void imx7_clear_reset_bit(CPUState *cpu, run_on_cpu_data data) +{ + struct SRCSCRResetInfo *ri = data.host_ptr; + IMX7SRCState *s = ri->s; + + assert(bql_locked()); + + s->regs[SRC_A7RCR0] = deposit32(s->regs[SRC_A7RCR0], ri->reset_bit, 1, 0); + + trace_imx7_src_write(imx7_src_reg_name(SRC_A7RCR0), s->regs[SRC_A7RCR0]); + + g_free(ri); +} + +static void imx7_defer_clear_reset_bit(uint32_t cpuid, + IMX7SRCState *s, + uint32_t reset_shift) +{ + struct SRCSCRResetInfo *ri; + CPUState *cpu = arm_get_cpu_by_id(cpuid); + + if (!cpu) { + return; + } + + ri = g_new(struct SRCSCRResetInfo, 1); + ri->s = s; + ri->reset_bit = reset_shift; + + async_run_on_cpu(cpu, imx7_clear_reset_bit, RUN_ON_CPU_HOST_PTR(ri)); +} + + +static void imx7_src_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMX7SRCState *s = (IMX7SRCState *)opaque; + uint32_t index = offset >> 2; + long unsigned int change_mask; + uint32_t current_value = value; + + if (index >= SRC_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX7_SRC, __func__, offset); + return; + } + + trace_imx7_src_write(imx7_src_reg_name(SRC_A7RCR0), s->regs[SRC_A7RCR0]); + + change_mask = s->regs[index] ^ (uint32_t)current_value; + + switch (index) { + case SRC_A7RCR0: + if (FIELD_EX32(change_mask, CORE0, RST)) { + arm_reset_cpu(0); + imx7_defer_clear_reset_bit(0, s, R_CORE0_RST_SHIFT); + } + if (FIELD_EX32(change_mask, CORE1, RST)) { + arm_reset_cpu(1); + imx7_defer_clear_reset_bit(1, s, R_CORE1_RST_SHIFT); + } + s->regs[index] = current_value; + break; + case SRC_A7RCR1: + /* + * On real hardware when the system reset controller starts a + * secondary CPU it runs through some boot ROM code which reads + * the SRC_GPRX registers controlling the start address and branches + * to it. + * Here we are taking a short cut and branching directly to the + * requested address (we don't want to run the boot ROM code inside + * QEMU) + */ + if (FIELD_EX32(change_mask, CORE1, ENABLE)) { + if (FIELD_EX32(current_value, CORE1, ENABLE)) { + /* CORE 1 is brought up */ + arm_set_cpu_on(1, s->regs[SRC_GPR3], s->regs[SRC_GPR4], + 3, false); + } else { + /* CORE 1 is shut down */ + arm_set_cpu_off(1); + } + /* We clear the reset bits as the processor changed state */ + imx7_defer_clear_reset_bit(1, s, R_CORE1_RST_SHIFT); + clear_bit(R_CORE1_RST_SHIFT, &change_mask); + } + s->regs[index] = current_value; + break; + default: + s->regs[index] = current_value; + break; + } +} + +static const struct MemoryRegionOps imx7_src_ops = { + .read = imx7_src_read, + .write = imx7_src_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 imx7_src_realize(DeviceState *dev, Error **errp) +{ + IMX7SRCState *s = IMX7_SRC(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &imx7_src_ops, s, + TYPE_IMX7_SRC, 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); +} + +static void imx7_src_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = imx7_src_realize; + dc->reset = imx7_src_reset; + dc->vmsd = &vmstate_imx7_src; + dc->desc = "i.MX6 System Reset Controller"; +} + +static const TypeInfo imx7_src_info = { + .name = TYPE_IMX7_SRC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMX7SRCState), + .class_init = imx7_src_class_init, +}; + +static void imx7_src_register_types(void) +{ + type_register_static(&imx7_src_info); +} + +type_init(imx7_src_register_types) diff --git a/hw/misc/imx_rngc.c b/hw/misc/imx_rngc.c index 632c03779c..ab7775e095 100644 --- a/hw/misc/imx_rngc.c +++ b/hw/misc/imx_rngc.c @@ -228,8 +228,10 @@ static void imx_rngc_realize(DeviceState *dev, Error **errp) 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); + s->self_test_bh = qemu_bh_new_guarded(imx_rngc_self_test, s, + &dev->mem_reentrancy_guard); + s->seed_bh = qemu_bh_new_guarded(imx_rngc_seed, s, + &dev->mem_reentrancy_guard); } static void imx_rngc_reset(DeviceState *dev) @@ -243,7 +245,7 @@ static const VMStateDescription vmstate_imx_rngc = { .name = RNGC_NAME, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(op_self_test, IMXRNGCState), VMSTATE_UINT8(op_seed, IMXRNGCState), VMSTATE_UINT8(mask, IMXRNGCState), diff --git a/hw/misc/iosb.c b/hw/misc/iosb.c new file mode 100644 index 0000000000..e20305e801 --- /dev/null +++ b/hw/misc/iosb.c @@ -0,0 +1,133 @@ +/* + * QEMU IOSB emulation + * + * Copyright (c) 2019 Laurent Vivier + * Copyright (c) 2022 Mark Cave-Ayland + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "migration/vmstate.h" +#include "hw/sysbus.h" +#include "hw/misc/iosb.h" +#include "trace.h" + +#define IOSB_SIZE 0x2000 + +#define IOSB_CONFIG 0x0 +#define IOSB_CONFIG2 0x100 +#define IOSB_SONIC_SCSI 0x200 +#define IOSB_REVISION 0x300 +#define IOSB_SCSI_RESID 0x400 +#define IOSB_BRIGHTNESS 0x500 +#define IOSB_TIMEOUT 0x600 + + +static uint64_t iosb_read(void *opaque, hwaddr addr, + unsigned size) +{ + IOSBState *s = IOSB(opaque); + uint64_t val = 0; + + switch (addr) { + case IOSB_CONFIG: + case IOSB_CONFIG2: + case IOSB_SONIC_SCSI: + case IOSB_REVISION: + case IOSB_SCSI_RESID: + case IOSB_BRIGHTNESS: + case IOSB_TIMEOUT: + val = s->regs[addr >> 8]; + break; + default: + qemu_log_mask(LOG_UNIMP, "IOSB: unimplemented read addr=0x%"PRIx64 + " val=0x%"PRIx64 " size=%d\n", + addr, val, size); + } + + trace_iosb_read(addr, val, size); + return val; +} + +static void iosb_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + IOSBState *s = IOSB(opaque); + + switch (addr) { + case IOSB_CONFIG: + case IOSB_CONFIG2: + case IOSB_SONIC_SCSI: + case IOSB_REVISION: + case IOSB_SCSI_RESID: + case IOSB_BRIGHTNESS: + case IOSB_TIMEOUT: + s->regs[addr >> 8] = val; + break; + default: + qemu_log_mask(LOG_UNIMP, "IOSB: unimplemented write addr=0x%"PRIx64 + " val=0x%"PRIx64 " size=%d\n", + addr, val, size); + } + + trace_iosb_write(addr, val, size); +} + +static const MemoryRegionOps iosb_mmio_ops = { + .read = iosb_read, + .write = iosb_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void iosb_reset_hold(Object *obj) +{ + IOSBState *s = IOSB(obj); + + memset(s->regs, 0, sizeof(s->regs)); + + /* BCLK 33 MHz */ + s->regs[IOSB_CONFIG >> 8] = 1; +} + +static void iosb_init(Object *obj) +{ + IOSBState *s = IOSB(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->mem_regs, obj, &iosb_mmio_ops, s, "IOSB", + IOSB_SIZE); + sysbus_init_mmio(sbd, &s->mem_regs); +} + +static const VMStateDescription vmstate_iosb = { + .name = "IOSB", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, IOSBState, IOSB_REGS), + VMSTATE_END_OF_LIST() + } +}; + +static void iosb_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); + + dc->vmsd = &vmstate_iosb; + rc->phases.hold = iosb_reset_hold; +} + +static const TypeInfo iosb_info_types[] = { + { + .name = TYPE_IOSB, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IOSBState), + .instance_init = iosb_init, + .class_init = iosb_class_init, + }, +}; + +DEFINE_TYPES(iosb_info_types) diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index 7b41cfa8fc..f9c45f60bf 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -114,7 +114,7 @@ static const uint8_t iotkit_secctl_ns_sse300_idregs[] = { * AHB expansion, APB expansion) are all set up so that they are * in 16-aligned blocks so offsets 0xN0, 0xN4, 0xN8, 0xNC are PPCs * 0, 1, 2, 3 of that type, so we can convert a register address offset - * into an an index into a PPC array easily. + * into an index into a PPC array easily. */ static inline int offset_to_ppc_idx(uint32_t offset) { @@ -753,7 +753,7 @@ static const VMStateDescription iotkit_secctl_ppc_vmstate = { .name = "iotkit-secctl-ppc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ns, IoTKitSecCtlPPC), VMSTATE_UINT32(sp, IoTKitSecCtlPPC), VMSTATE_UINT32(nsp, IoTKitSecCtlPPC), @@ -765,7 +765,7 @@ static const VMStateDescription iotkit_secctl_mpcintstatus_vmstate = { .name = "iotkit-secctl-mpcintstatus", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(mpcintstatus, IoTKitSecCtl), VMSTATE_END_OF_LIST() } @@ -781,7 +781,7 @@ static const VMStateDescription iotkit_secctl_msc_vmstate = { .version_id = 1, .minimum_version_id = 1, .needed = needed_always, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(secmscintstat, IoTKitSecCtl), VMSTATE_UINT32(secmscinten, IoTKitSecCtl), VMSTATE_UINT32(nsmscexp, IoTKitSecCtl), @@ -793,7 +793,7 @@ static const VMStateDescription iotkit_secctl_vmstate = { .name = "iotkit-secctl", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(secppcintstat, IoTKitSecCtl), VMSTATE_UINT32(secppcinten, IoTKitSecCtl), VMSTATE_UINT32(secrespcfg, IoTKitSecCtl), @@ -807,7 +807,7 @@ static const VMStateDescription iotkit_secctl_vmstate = { iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &iotkit_secctl_mpcintstatus_vmstate, &iotkit_secctl_msc_vmstate, NULL diff --git a/hw/misc/iotkit-sysctl.c b/hw/misc/iotkit-sysctl.c index 9ee8fe8495..45393e84ba 100644 --- a/hw/misc/iotkit-sysctl.c +++ b/hw/misc/iotkit-sysctl.c @@ -30,7 +30,6 @@ #include "hw/qdev-properties.h" #include "hw/arm/armsse-version.h" #include "target/arm/arm-powerctl.h" -#include "target/arm/cpu.h" REG32(SECDBGSTAT, 0x0) REG32(SECDBGSET, 0x4) @@ -237,7 +236,7 @@ static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset, r = s->ewctrl; break; case ARMSSE_SSE300: - /* In SSE300 this offset is is NMI_ENABLE */ + /* In SSE300 this offset is NMI_ENABLE */ r = s->nmi_enable; break; default: @@ -555,7 +554,7 @@ static void iotkit_sysctl_write(void *opaque, hwaddr offset, s->ewctrl = value; break; case ARMSSE_SSE300: - /* In SSE300 this offset is is NMI_ENABLE */ + /* In SSE300 this offset is NMI_ENABLE */ qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl NMI_ENABLE unimplemented\n"); s->nmi_enable = value; break; @@ -778,7 +777,7 @@ static const VMStateDescription iotkit_sysctl_sse300_vmstate = { .version_id = 1, .minimum_version_id = 1, .needed = sse300_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(pwrctrl, IoTKitSysCtl), VMSTATE_UINT32(pdcm_pd_cpu0_sense, IoTKitSysCtl), VMSTATE_UINT32(pdcm_pd_vmr0_sense, IoTKitSysCtl), @@ -799,7 +798,7 @@ static const VMStateDescription iotkit_sysctl_sse200_vmstate = { .version_id = 1, .minimum_version_id = 1, .needed = sse200_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(scsecctrl, IoTKitSysCtl), VMSTATE_UINT32(fclk_div, IoTKitSysCtl), VMSTATE_UINT32(sysclk_div, IoTKitSysCtl), @@ -819,7 +818,7 @@ static const VMStateDescription iotkit_sysctl_vmstate = { .name = "iotkit-sysctl", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(secure_debug, IoTKitSysCtl), VMSTATE_UINT32(reset_syndrome, IoTKitSysCtl), VMSTATE_UINT32(reset_mask, IoTKitSysCtl), @@ -829,7 +828,7 @@ static const VMStateDescription iotkit_sysctl_vmstate = { VMSTATE_UINT32(wicctrl, IoTKitSysCtl), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &iotkit_sysctl_sse200_vmstate, &iotkit_sysctl_sse300_vmstate, NULL diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 8270db53cd..de49d1b8a8 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -179,7 +179,7 @@ static void ivshmem_io_write(void *opaque, hwaddr addr, addr &= 0xfc; - IVSHMEM_DPRINTF("writing to addr " TARGET_FMT_plx "\n", addr); + IVSHMEM_DPRINTF("writing to addr " HWADDR_FMT_plx "\n", addr); switch (addr) { case INTRMASK: @@ -207,7 +207,7 @@ static void ivshmem_io_write(void *opaque, hwaddr addr, } break; default: - IVSHMEM_DPRINTF("Unhandled write " TARGET_FMT_plx "\n", addr); + IVSHMEM_DPRINTF("Unhandled write " HWADDR_FMT_plx "\n", addr); } } @@ -233,7 +233,7 @@ static uint64_t ivshmem_io_read(void *opaque, hwaddr addr, break; default: - IVSHMEM_DPRINTF("why are we reading " TARGET_FMT_plx "\n", addr); + IVSHMEM_DPRINTF("why are we reading " HWADDR_FMT_plx "\n", addr); ret = 0; } @@ -476,7 +476,6 @@ static void setup_interrupt(IVShmemState *s, int vector, Error **errp) static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) { - Error *local_err = NULL; struct stat buf; size_t size; @@ -496,10 +495,9 @@ static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) size = buf.st_size; /* mmap the region and map into the BAR2 */ - memory_region_init_ram_from_fd(&s->server_bar2, OBJECT(s), "ivshmem.bar2", - size, RAM_SHARED, fd, 0, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!memory_region_init_ram_from_fd(&s->server_bar2, OBJECT(s), + "ivshmem.bar2", size, RAM_SHARED, + fd, 0, errp)) { return; } @@ -834,6 +832,7 @@ static void ivshmem_write_config(PCIDevice *pdev, uint32_t address, static void ivshmem_common_realize(PCIDevice *dev, Error **errp) { + ERRP_GUARD(); IVShmemState *s = IVSHMEM_COMMON(dev); Error *err = NULL; uint8_t *pci_conf; @@ -903,8 +902,7 @@ 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'"); - if (migrate_add_blocker(s->migration_blocker, errp) < 0) { - error_free(s->migration_blocker); + if (migrate_add_blocker(&s->migration_blocker, errp) < 0) { return; } } @@ -922,10 +920,7 @@ static void ivshmem_exit(PCIDevice *dev) IVShmemState *s = IVSHMEM_COMMON(dev); int i; - if (s->migration_blocker) { - migrate_del_blocker(s->migration_blocker); - error_free(s->migration_blocker); - } + migrate_del_blocker(&s->migration_blocker); if (memory_region_is_mapped(s->ivshmem_bar2)) { if (!s->hostmem) { @@ -1019,7 +1014,7 @@ static const VMStateDescription ivshmem_plain_vmsd = { .minimum_version_id = 0, .pre_load = ivshmem_pre_load, .post_load = ivshmem_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, IVShmemState), VMSTATE_UINT32(intrstatus, IVShmemState), VMSTATE_UINT32(intrmask, IVShmemState), @@ -1073,7 +1068,7 @@ static const VMStateDescription ivshmem_doorbell_vmsd = { .minimum_version_id = 0, .pre_load = ivshmem_pre_load, .post_load = ivshmem_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, IVShmemState), VMSTATE_MSIX(parent_obj, IVShmemState), VMSTATE_UINT32(intrstatus, IVShmemState), diff --git a/hw/misc/lasi.c b/hw/misc/lasi.c index 23a7634a8c..970fc98b5c 100644 --- a/hw/misc/lasi.c +++ b/hw/misc/lasi.c @@ -36,9 +36,13 @@ static bool lasi_chip_mem_valid(void *opaque, hwaddr addr, case LASI_IAR: case LASI_LPT: + case LASI_AUDIO: + case LASI_AUDIO + 4: case LASI_UART: case LASI_LAN: + case LASI_LAN + 12: /* LASI LAN MAC */ case LASI_RTC: + case LASI_FDC: case LASI_PCR ... LASI_AMR: ret = true; @@ -78,6 +82,8 @@ static MemTxResult lasi_chip_read_with_attrs(void *opaque, hwaddr addr, case LASI_LPT: case LASI_UART: case LASI_LAN: + case LASI_LAN + 12: + case LASI_FDC: val = 0; break; case LASI_RTC: @@ -143,12 +149,19 @@ static MemTxResult lasi_chip_write_with_attrs(void *opaque, hwaddr addr, case LASI_LPT: /* XXX: reset parallel port */ break; + case LASI_AUDIO: + case LASI_AUDIO + 4: + /* XXX: reset audio port */ + break; case LASI_UART: /* XXX: reset serial port */ break; case LASI_LAN: /* XXX: reset LAN card */ break; + case LASI_FDC: + /* XXX: reset Floppy controller */ + break; case LASI_RTC: s->rtc_ref = val - time(NULL); break; @@ -194,9 +207,9 @@ static const MemoryRegionOps lasi_chip_ops = { static const VMStateDescription vmstate_lasi = { .name = "Lasi", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(irr, LasiState), VMSTATE_UINT32(imr, LasiState), VMSTATE_UINT32(ipr, LasiState), @@ -204,6 +217,7 @@ static const VMStateDescription vmstate_lasi = { VMSTATE_UINT32(iar, LasiState), VMSTATE_UINT32(errlog, LasiState), VMSTATE_UINT32(amr, LasiState), + VMSTATE_UINT32_V(rtc_ref, LasiState, 2), VMSTATE_END_OF_LIST() } }; @@ -233,7 +247,6 @@ static void lasi_reset(DeviceState *dev) s->iar = 0xFFFB0000 + 3; /* CPU_HPA + 3 */ /* Real time clock (RTC), it's only one 32-bit counter @9000 */ - s->rtc = time(NULL); s->rtc_ref = 0; } diff --git a/hw/misc/led.c b/hw/misc/led.c index f6d6d68bce..d9998ab895 100644 --- a/hw/misc/led.c +++ b/hw/misc/led.c @@ -63,7 +63,7 @@ static void led_set_state_gpio_handler(void *opaque, int line, int new_state) LEDState *s = LED(opaque); assert(line == 0); - led_set_state(s, !!new_state != s->gpio_active_high); + led_set_state(s, !!new_state == s->gpio_active_high); } static void led_reset(DeviceState *dev) @@ -77,7 +77,7 @@ static const VMStateDescription vmstate_led = { .name = TYPE_LED, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(intensity_percent, LEDState), VMSTATE_END_OF_LIST() } diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index 525e38ce93..db6142b5f4 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -16,6 +16,7 @@ */ #include "qemu/osdep.h" +#include "exec/address-spaces.h" #include "migration/vmstate.h" #include "hw/sysbus.h" #include "hw/irq.h" @@ -114,6 +115,9 @@ #define VIA1A_CPUID1 0x04 /* CPU id bit 0 on RBV, others */ #define VIA1A_CPUID2 0x10 /* CPU id bit 0 on RBV, others */ #define VIA1A_CPUID3 0x40 /* CPU id bit 0 on RBV, others */ +#define VIA1A_CPUID_MASK (VIA1A_CPUID0 | VIA1A_CPUID1 | \ + VIA1A_CPUID2 | VIA1A_CPUID3) +#define VIA1A_CPUID_Q800 (VIA1A_CPUID0 | VIA1A_CPUID2) /* * Info on VIA1B is from Macintosh Family Hardware & MkLinux. @@ -246,7 +250,7 @@ #define vT2CL 0x1000 /* [VIA only] Timer two counter low. */ #define vT2CH 0x1200 /* [VIA only] Timer two counter high. */ #define vSR 0x1400 /* [VIA only] Shift register. */ -#define vACR 0x1600 /* [VIA only] Auxilary control register. */ +#define vACR 0x1600 /* [VIA only] Auxiliary control register. */ #define vPCR 0x1800 /* [VIA only] Peripheral control register. */ /* * CHRP sez never ever to *write* this. @@ -351,7 +355,7 @@ static void via1_one_second(void *opaque) static void pram_update(MOS6522Q800VIA1State *v1s) { if (v1s->blk) { - if (blk_pwrite(v1s->blk, 0, v1s->PRAM, sizeof(v1s->PRAM), 0) < 0) { + if (blk_pwrite(v1s->blk, 0, sizeof(v1s->PRAM), v1s->PRAM, 0) < 0) { qemu_log("pram_update: cannot write to file\n"); } } @@ -362,10 +366,10 @@ static void pram_update(MOS6522Q800VIA1State *v1s) * * 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) + * z00x0001 Seconds register 0 (lowest-order byte) + * z00x0101 Seconds register 1 + * z00x1001 Seconds register 2 + * z00x1101 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) @@ -373,6 +377,7 @@ static void pram_update(MOS6522Q800VIA1State *v1s) * z0111aaa Extended memory designator and sector number * * For a read request, z=1, for a write z=0 + * The letter x indicates don't care * The letter a indicates bits whose value depend on what parameter * RAM byte you want to address */ @@ -389,7 +394,7 @@ static int via1_rtc_compact_cmd(uint8_t value) } if ((value & 0x03) == 0x01) { value >>= 2; - if ((value & 0x1c) == 0) { + if ((value & 0x18) == 0) { /* seconds registers */ return read | (REG_0 + (value & 0x03)); } else if ((value == 0x0c) && !read) { @@ -399,7 +404,7 @@ static int via1_rtc_compact_cmd(uint8_t value) } else if ((value & 0x1c) == 0x08) { /* RAM address 0x10 to 0x13 */ return read | (REG_PRAM_ADDR + 0x10 + (value & 0x03)); - } else if ((value & 0x43) == 0x41) { + } else if ((value & 0x10) == 0x10) { /* RAM address 0x00 to 0x0f */ return read | (REG_PRAM_ADDR + (value & 0x0f)); } @@ -587,7 +592,7 @@ static void adb_via_poll(void *opaque) /* * For older Linux kernels that switch to IDLE mode after sending the * ADB command, detect if there is an existing response and return that - * as a a "fake" autopoll reply or bus timeout accordingly + * as a "fake" autopoll reply or bus timeout accordingly */ *data = v1s->adb_data_out[0]; olen = v1s->adb_data_in_size; @@ -697,6 +702,12 @@ static void adb_via_send(MOS6522Q800VIA1State *v1s, int state, uint8_t data) break; case ADB_STATE_IDLE: + ms->b |= VIA1B_vADBInt; + adb_autopoll_unblock(adb_bus); + + trace_via1_adb_send("IDLE", data, + (ms->b & VIA1B_vADBInt) ? "+" : "-"); + return; } @@ -864,6 +875,159 @@ static void via1_auxmode_update(MOS6522Q800VIA1State *v1s) if (irq != oldirq) { trace_via1_auxmode(irq); qemu_set_irq(v1s->auxmode_irq, irq); + + /* + * Clear the ADB interrupt. MacOS can leave VIA1B_vADBInt asserted + * (low) if a poll sequence doesn't complete before NetBSD disables + * interrupts upon boot. Fortunately NetBSD switches to the so-called + * "A/UX" interrupt mode after it initialises, so we can use this as + * a convenient place to clear the ADB interrupt for now. + */ + s->b |= VIA1B_vADBInt; + } +} + +/* + * Addresses and real values for TimeDBRA/TimeSCCB to allow timer calibration + * to succeed (NOTE: both values have been multiplied by 3 to cope with the + * speed of QEMU execution on a modern host + */ +#define MACOS_TIMEDBRA 0xd00 +#define MACOS_TIMESCCB 0xd02 + +#define MACOS_TIMEDBRA_VALUE (0x2a00 * 3) +#define MACOS_TIMESCCB_VALUE (0x079d * 3) + +static bool via1_is_toolbox_timer_calibrated(void) +{ + /* + * Indicate whether the MacOS toolbox has been calibrated by checking + * for the value of our magic constants + */ + uint16_t timedbra = lduw_be_phys(&address_space_memory, MACOS_TIMEDBRA); + uint16_t timesccdb = lduw_be_phys(&address_space_memory, MACOS_TIMESCCB); + + return (timedbra == MACOS_TIMEDBRA_VALUE && + timesccdb == MACOS_TIMESCCB_VALUE); +} + +static void via1_timer_calibration_hack(MOS6522Q800VIA1State *v1s, int addr, + uint64_t val, int size) +{ + /* + * Work around timer calibration to ensure we that we have non-zero and + * known good values for TIMEDRBA and TIMESCCDB. + * + * This works by attempting to detect the reset and calibration sequence + * of writes to VIA1 + */ + int old_timer_hack_state = v1s->timer_hack_state; + + switch (v1s->timer_hack_state) { + case 0: + if (addr == VIA_REG_PCR && val == 0x22) { + /* VIA_REG_PCR: configure VIA1 edge triggering */ + v1s->timer_hack_state = 1; + } + break; + case 1: + if (addr == VIA_REG_T2CL && val == 0xc) { + /* VIA_REG_T2CL: low byte of 1ms counter */ + if (!via1_is_toolbox_timer_calibrated()) { + v1s->timer_hack_state = 2; + } else { + v1s->timer_hack_state = 0; + } + } + break; + case 2: + if (addr == VIA_REG_T2CH && val == 0x3) { + /* + * VIA_REG_T2CH: high byte of 1ms counter (very likely at the + * start of SETUPTIMEK) + */ + if (!via1_is_toolbox_timer_calibrated()) { + v1s->timer_hack_state = 3; + } else { + v1s->timer_hack_state = 0; + } + } + break; + case 3: + if (addr == VIA_REG_IER && val == 0x20) { + /* + * VIA_REG_IER: update at end of SETUPTIMEK + * + * Timer calibration has finished: unfortunately the values in + * TIMEDBRA (0xd00) and TIMESCCDB (0xd02) are so far out they + * cause divide by zero errors. + * + * Update them with values obtained from a real Q800 but with + * a x3 scaling factor which seems to work well + */ + stw_be_phys(&address_space_memory, MACOS_TIMEDBRA, + MACOS_TIMEDBRA_VALUE); + stw_be_phys(&address_space_memory, MACOS_TIMESCCB, + MACOS_TIMESCCB_VALUE); + + v1s->timer_hack_state = 4; + } + break; + case 4: + /* + * This is the normal post-calibration timer state: we should + * generally remain here unless we detect the A/UX calibration + * loop, or a write to VIA_REG_PCR suggesting a reset + */ + if (addr == VIA_REG_PCR && val == 0x22) { + /* Looks like there has been a reset? */ + v1s->timer_hack_state = 1; + } + + if (addr == VIA_REG_T2CL && val == 0xf0) { + /* VIA_REG_T2CL: low byte of counter (A/UX) */ + v1s->timer_hack_state = 5; + } + break; + case 5: + if (addr == VIA_REG_T2CH && val == 0x3c) { + /* + * VIA_REG_T2CH: high byte of counter (A/UX). We are now extremely + * likely to be in the A/UX timer calibration routine, so move to + * the next state where we enable the calibration hack. + */ + v1s->timer_hack_state = 6; + } else if ((addr == VIA_REG_IER && val == 0x20) || + addr == VIA_REG_T2CH) { + /* We're doing something else with the timer, not calibration */ + v1s->timer_hack_state = 0; + } + break; + case 6: + if ((addr == VIA_REG_IER && val == 0x20) || addr == VIA_REG_T2CH) { + /* End of A/UX timer calibration routine, or another write */ + v1s->timer_hack_state = 7; + } else { + v1s->timer_hack_state = 0; + } + break; + case 7: + /* + * This is the normal post-calibration timer state once both the + * MacOS toolbox and A/UX have been calibrated, until we see a write + * to VIA_REG_PCR to suggest a reset + */ + if (addr == VIA_REG_PCR && val == 0x22) { + /* Looks like there has been a reset? */ + v1s->timer_hack_state = 1; + } + break; + default: + g_assert_not_reached(); + } + + if (old_timer_hack_state != v1s->timer_hack_state) { + trace_via1_timer_hack_state(v1s->timer_hack_state); } } @@ -871,9 +1035,36 @@ static uint64_t mos6522_q800_via1_read(void *opaque, hwaddr addr, unsigned size) { MOS6522Q800VIA1State *s = MOS6522_Q800_VIA1(opaque); MOS6522State *ms = MOS6522(s); + uint64_t ret; + int64_t now; addr = (addr >> 9) & 0xf; - return mos6522_read(ms, addr, size); + ret = mos6522_read(ms, addr, size); + switch (addr) { + case VIA_REG_A: + case VIA_REG_ANH: + /* Quadra 800 Id */ + ret = (ret & ~VIA1A_CPUID_MASK) | VIA1A_CPUID_Q800; + break; + case VIA_REG_T2CH: + if (s->timer_hack_state == 6) { + /* + * The A/UX timer calibration loop runs continuously until 2 + * consecutive iterations differ by at least 0x492 timer ticks. + * Modern hosts execute the timer calibration loop so fast that + * this situation never occurs causing a hang on boot. Use a + * similar method to Shoebill which is to randomly add 0x500 to + * the T2 counter value during calibration to enable it to + * eventually succeed. + */ + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + if (now & 1) { + ret += 0x5; + } + } + break; + } + return ret; } static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val, @@ -881,8 +1072,13 @@ static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val, { MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(opaque); MOS6522State *ms = MOS6522(v1s); + int oldstate, state; + int oldsr = ms->sr; addr = (addr >> 9) & 0xf; + + via1_timer_calibration_hack(v1s, addr, val, size); + mos6522_write(ms, addr, val, size); switch (addr) { @@ -893,6 +1089,38 @@ static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val, v1s->last_b = ms->b; break; + + case VIA_REG_SR: + { + /* + * NetBSD assumes it can send its first ADB command after sending + * the ADB_BUSRESET command in ADB_STATE_NEW without changing the + * state back to ADB_STATE_IDLE first as detailed in the ADB + * protocol. + * + * Add a workaround to detect this condition at the start of ADB + * enumeration and send the next command written to SR after a + * ADB_BUSRESET onto the bus regardless, even if we don't detect a + * state transition to ADB_STATE_NEW. + * + * Note that in my tests the NetBSD state machine takes one ADB + * operation to recover which means the probe for an ADB device at + * address 1 always fails. However since the first device is at + * address 2 then this will work fine, without having to come up + * with a more complicated and invasive solution. + */ + oldstate = (v1s->last_b & VIA1B_vADB_StateMask) >> + VIA1B_vADB_StateShift; + state = (ms->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift; + + if (oldstate == ADB_STATE_NEW && state == ADB_STATE_NEW && + (ms->acr & VIA1ACR_vShiftOut) && + oldsr == 0 /* ADB_BUSRESET */) { + trace_via1_adb_netbsd_enum_hack(); + adb_via_send(v1s, state, ms->sr); + } + } + break; } } @@ -975,14 +1203,16 @@ static int via1_post_load(void *opaque, int version_id) } /* VIA 1 */ -static void mos6522_q800_via1_reset(DeviceState *dev) +static void mos6522_q800_via1_reset_hold(Object *obj) { - MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(dev); + MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(obj); MOS6522State *ms = MOS6522(v1s); MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); ADBBusState *adb_bus = &v1s->adb_bus; - mdc->parent_reset(dev); + if (mdc->parent_phases.hold) { + mdc->parent_phases.hold(obj); + } ms->timers[0].frequency = VIA_TIMER_FREQ; ms->timers[1].frequency = VIA_TIMER_FREQ; @@ -993,6 +1223,9 @@ static void mos6522_q800_via1_reset(DeviceState *dev) adb_set_autopoll_enabled(adb_bus, true); v1s->cmd = REG_EMPTY; v1s->alt = REG_EMPTY; + + /* Timer calibration hack */ + v1s->timer_hack_state = 0; } static void mos6522_q800_via1_realize(DeviceState *dev, Error **errp) @@ -1029,8 +1262,8 @@ static void mos6522_q800_via1_realize(DeviceState *dev, Error **errp) return; } - len = blk_pread(v1s->blk, 0, v1s->PRAM, sizeof(v1s->PRAM)); - if (len != sizeof(v1s->PRAM)) { + ret = blk_pread(v1s->blk, 0, sizeof(v1s->PRAM), v1s->PRAM, 0); + if (ret < 0) { error_setg(errp, "can't read PRAM contents"); return; } @@ -1059,7 +1292,7 @@ static const VMStateDescription vmstate_q800_via1 = { .version_id = 0, .minimum_version_id = 0, .post_load = via1_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(parent_obj, MOS6522Q800VIA1State, 0, vmstate_mos6522, MOS6522State), VMSTATE_UINT8(last_b, MOS6522Q800VIA1State), @@ -1085,6 +1318,8 @@ static const VMStateDescription vmstate_q800_via1 = { VMSTATE_INT64(next_second, MOS6522Q800VIA1State), VMSTATE_TIMER_PTR(sixty_hz_timer, MOS6522Q800VIA1State), VMSTATE_INT64(next_sixty_hz, MOS6522Q800VIA1State), + /* Timer hack */ + VMSTATE_INT32(timer_hack_state, MOS6522Q800VIA1State), VMSTATE_END_OF_LIST() } }; @@ -1097,11 +1332,12 @@ static Property mos6522_q800_via1_properties[] = { static void mos6522_q800_via1_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_CLASS(oc); dc->realize = mos6522_q800_via1_realize; - device_class_set_parent_reset(dc, mos6522_q800_via1_reset, - &mdc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, mos6522_q800_via1_reset_hold, + NULL, &mdc->parent_phases); dc->vmsd = &vmstate_q800_via1; device_class_set_props(dc, mos6522_q800_via1_properties); } @@ -1123,12 +1359,14 @@ static void mos6522_q800_via2_portB_write(MOS6522State *s) } } -static void mos6522_q800_via2_reset(DeviceState *dev) +static void mos6522_q800_via2_reset_hold(Object *obj) { - MOS6522State *ms = MOS6522(dev); + MOS6522State *ms = MOS6522(obj); MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); - mdc->parent_reset(dev); + if (mdc->parent_phases.hold) { + mdc->parent_phases.hold(obj); + } ms->timers[0].frequency = VIA_TIMER_FREQ; ms->timers[1].frequency = VIA_TIMER_FREQ; @@ -1173,7 +1411,7 @@ static const VMStateDescription vmstate_q800_via2 = { .name = "q800-via2", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(parent_obj, MOS6522Q800VIA2State, 0, vmstate_mos6522, MOS6522State), VMSTATE_END_OF_LIST() @@ -1183,10 +1421,11 @@ static const VMStateDescription vmstate_q800_via2 = { static void mos6522_q800_via2_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_CLASS(oc); - device_class_set_parent_reset(dc, mos6522_q800_via2_reset, - &mdc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, mos6522_q800_via2_reset_hold, + NULL, &mdc->parent_phases); dc->vmsd = &vmstate_q800_via2; mdc->portB_write = mos6522_q800_via2_portB_write; } diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 1498113cfc..41934e2cf8 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -25,13 +25,9 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/ppc/mac.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "hw/input/adb.h" -#include "hw/misc/mos6522.h" #include "hw/misc/macio/cuda.h" -#include "qapi/error.h" #include "qemu/timer.h" #include "sysemu/runstate.h" #include "sysemu/rtc.h" @@ -491,7 +487,7 @@ static const VMStateDescription vmstate_cuda = { .name = "cuda", .version_id = 6, .minimum_version_id = 6, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(mos6522_cuda.parent_obj, CUDAState, 0, vmstate_mos6522, MOS6522State), VMSTATE_UINT8(last_b, CUDAState), @@ -590,12 +586,14 @@ static void mos6522_cuda_portB_write(MOS6522State *s) cuda_update(cs); } -static void mos6522_cuda_reset(DeviceState *dev) +static void mos6522_cuda_reset_hold(Object *obj) { - MOS6522State *ms = MOS6522(dev); + MOS6522State *ms = MOS6522(obj); MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); - mdc->parent_reset(dev); + if (mdc->parent_phases.hold) { + mdc->parent_phases.hold(obj); + } ms->timers[0].frequency = CUDA_TIMER_FREQ; ms->timers[1].frequency = (SCALE_US * 6000) / 4700; @@ -603,11 +601,11 @@ static void mos6522_cuda_reset(DeviceState *dev) static void mos6522_cuda_class_init(ObjectClass *oc, void *data) { - DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_CLASS(oc); - device_class_set_parent_reset(dc, mos6522_cuda_reset, - &mdc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, mos6522_cuda_reset_hold, + NULL, &mdc->parent_phases); mdc->portB_write = mos6522_cuda_portB_write; mdc->get_timer1_counter_value = cuda_get_counter_value; mdc->get_timer2_counter_value = cuda_get_counter_value; diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c index b1bcf830c3..549563747d 100644 --- a/hw/misc/macio/gpio.c +++ b/hw/misc/macio/gpio.c @@ -24,11 +24,11 @@ */ #include "qemu/osdep.h" -#include "hw/ppc/mac.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "hw/misc/macio/macio.h" #include "hw/misc/macio/gpio.h" +#include "hw/irq.h" #include "hw/nmi.h" #include "qemu/log.h" #include "qemu/module.h" @@ -168,7 +168,7 @@ static const VMStateDescription vmstate_macio_gpio = { .name = "macio_gpio", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(gpio_levels, MacIOGPIOState, 8), VMSTATE_UINT8_ARRAY(gpio_regs, MacIOGPIOState, 36), VMSTATE_END_OF_LIST() diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index efcc02609f..2a528ea08c 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -704,7 +704,7 @@ static void dbdma_write(void *opaque, hwaddr addr, DBDMA_channel *ch = &s->channels[channel]; int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2; - DBDMA_DPRINTFCH(ch, "writel 0x" TARGET_FMT_plx " <= 0x%08"PRIx64"\n", + DBDMA_DPRINTFCH(ch, "writel 0x" HWADDR_FMT_plx " <= 0x%08"PRIx64"\n", addr, value); DBDMA_DPRINTFCH(ch, "channel 0x%x reg 0x%x\n", (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg); @@ -786,7 +786,7 @@ static uint64_t dbdma_read(void *opaque, hwaddr addr, break; } - DBDMA_DPRINTFCH(ch, "readl 0x" TARGET_FMT_plx " => 0x%08x\n", addr, value); + DBDMA_DPRINTFCH(ch, "readl 0x" HWADDR_FMT_plx " => 0x%08x\n", addr, value); DBDMA_DPRINTFCH(ch, "channel 0x%x reg 0x%x\n", (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg); @@ -807,7 +807,7 @@ static const VMStateDescription vmstate_dbdma_io = { .name = "dbdma_io", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(addr, struct DBDMA_io), VMSTATE_INT32(len, struct DBDMA_io), VMSTATE_INT32(is_last, struct DBDMA_io), @@ -821,7 +821,7 @@ static const VMStateDescription vmstate_dbdma_cmd = { .name = "dbdma_cmd", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(req_count, dbdma_cmd), VMSTATE_UINT16(command, dbdma_cmd), VMSTATE_UINT32(phy_addr, dbdma_cmd), @@ -836,7 +836,7 @@ static const VMStateDescription vmstate_dbdma_channel = { .name = "dbdma_channel", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, struct DBDMA_channel, DBDMA_REGS), VMSTATE_STRUCT(io, struct DBDMA_channel, 0, vmstate_dbdma_io, DBDMA_io), VMSTATE_STRUCT(current, struct DBDMA_channel, 0, vmstate_dbdma_cmd, @@ -849,7 +849,7 @@ static const VMStateDescription vmstate_dbdma = { .name = "dbdma", .version_id = 3, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(channels, DBDMAState, DBDMA_CHANNELS, 1, vmstate_dbdma_channel, DBDMA_channel), VMSTATE_END_OF_LIST() @@ -914,7 +914,7 @@ static void mac_dbdma_realize(DeviceState *dev, Error **errp) { DBDMAState *s = MAC_DBDMA(dev); - s->bh = qemu_bh_new(DBDMA_run_bh, s); + s->bh = qemu_bh_new_guarded(DBDMA_run_bh, s, &dev->mem_reentrancy_guard); } static void mac_dbdma_class_init(ObjectClass *oc, void *data) diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index c1fad43f6c..3f449f91c0 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -26,7 +26,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" -#include "hw/ppc/mac.h" #include "hw/misc/macio/cuda.h" #include "hw/pci/pci.h" #include "hw/ppc/mac_dbdma.h" @@ -37,8 +36,9 @@ #include "hw/intc/heathrow_pic.h" #include "trace.h" -/* Note: this code is strongly inspirated from the corresponding code - * in PearPC */ +#define ESCC_CLOCK 3686400 + +/* Note: this code is strongly inspired by the corresponding code in PearPC */ /* * The mac-io has two interfaces to the ESCC. One is called "escc-legacy", @@ -53,10 +53,8 @@ */ static void macio_escc_legacy_setup(MacIOState *s) { - ESCCState *escc = ESCC(&s->escc); - SysBusDevice *sbd = SYS_BUS_DEVICE(escc); + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->escc); MemoryRegion *escc_legacy = g_new(MemoryRegion, 1); - MemoryRegion *bar = &s->bar; int i; static const int maps[] = { 0x00, 0x00, /* Command B */ @@ -80,30 +78,29 @@ static void macio_escc_legacy_setup(MacIOState *s) memory_region_add_subregion(escc_legacy, maps[i], port); } - memory_region_add_subregion(bar, 0x12000, escc_legacy); + memory_region_add_subregion(&s->bar, 0x12000, escc_legacy); } static void macio_bar_setup(MacIOState *s) { - ESCCState *escc = ESCC(&s->escc); - SysBusDevice *sbd = SYS_BUS_DEVICE(escc); - MemoryRegion *bar = &s->bar; + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->escc); + MemoryRegion *bar = sysbus_mmio_get_region(sbd, 0); - memory_region_add_subregion(bar, 0x13000, sysbus_mmio_get_region(sbd, 0)); + memory_region_add_subregion(&s->bar, 0x13000, bar); macio_escc_legacy_setup(s); } -static void macio_common_realize(PCIDevice *d, Error **errp) +static bool macio_common_realize(PCIDevice *d, Error **errp) { MacIOState *s = MACIO(d); - SysBusDevice *sysbus_dev; + SysBusDevice *sbd; if (!qdev_realize(DEVICE(&s->dbdma), BUS(&s->macio_bus), errp)) { - return; + return false; } - sysbus_dev = SYS_BUS_DEVICE(&s->dbdma); + sbd = SYS_BUS_DEVICE(&s->dbdma); memory_region_add_subregion(&s->bar, 0x08000, - sysbus_mmio_get_region(sysbus_dev, 0)); + sysbus_mmio_get_region(sbd, 0)); qdev_prop_set_uint32(DEVICE(&s->escc), "disabled", 0); qdev_prop_set_uint32(DEVICE(&s->escc), "frequency", ESCC_CLOCK); @@ -111,28 +108,32 @@ static void macio_common_realize(PCIDevice *d, Error **errp) qdev_prop_set_uint32(DEVICE(&s->escc), "chnBtype", escc_serial); qdev_prop_set_uint32(DEVICE(&s->escc), "chnAtype", escc_serial); if (!qdev_realize(DEVICE(&s->escc), BUS(&s->macio_bus), errp)) { - return; + return false; } macio_bar_setup(s); pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar); + + return true; } -static void macio_realize_ide(MacIOState *s, MACIOIDEState *ide, +static bool macio_realize_ide(MacIOState *s, MACIOIDEState *ide, qemu_irq irq0, qemu_irq irq1, int dmaid, Error **errp) { - SysBusDevice *sysbus_dev; + SysBusDevice *sbd = SYS_BUS_DEVICE(ide); - sysbus_dev = SYS_BUS_DEVICE(ide); - sysbus_connect_irq(sysbus_dev, 0, irq0); - sysbus_connect_irq(sysbus_dev, 1, irq1); qdev_prop_set_uint32(DEVICE(ide), "channel", dmaid); object_property_set_link(OBJECT(ide), "dbdma", OBJECT(&s->dbdma), &error_abort); macio_ide_register_dma(ide); + if (!qdev_realize(DEVICE(ide), BUS(&s->macio_bus), errp)) { + return false; + } + sysbus_connect_irq(sbd, 0, irq0); + sysbus_connect_irq(sbd, 1, irq1); - qdev_realize(DEVICE(ide), BUS(&s->macio_bus), errp); + return true; } static void macio_oldworld_realize(PCIDevice *d, Error **errp) @@ -140,12 +141,9 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp) MacIOState *s = MACIO(d); OldWorldMacIOState *os = OLDWORLD_MACIO(d); DeviceState *pic_dev = DEVICE(&os->pic); - Error *err = NULL; - SysBusDevice *sysbus_dev; + SysBusDevice *sbd; - macio_common_realize(d, &err); - if (err) { - error_propagate(errp, err); + if (!macio_common_realize(d, errp)) { return; } @@ -153,51 +151,44 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp) if (!qdev_realize(DEVICE(&os->pic), BUS(&s->macio_bus), errp)) { return; } - sysbus_dev = SYS_BUS_DEVICE(&os->pic); + sbd = SYS_BUS_DEVICE(&os->pic); memory_region_add_subregion(&s->bar, 0x0, - sysbus_mmio_get_region(sysbus_dev, 0)); + sysbus_mmio_get_region(sbd, 0)); qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency", s->frequency); if (!qdev_realize(DEVICE(&s->cuda), BUS(&s->macio_bus), errp)) { return; } - sysbus_dev = SYS_BUS_DEVICE(&s->cuda); + sbd = SYS_BUS_DEVICE(&s->cuda); memory_region_add_subregion(&s->bar, 0x16000, - sysbus_mmio_get_region(sysbus_dev, 0)); - sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, - OLDWORLD_CUDA_IRQ)); + sysbus_mmio_get_region(sbd, 0)); + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(pic_dev, OLDWORLD_CUDA_IRQ)); - sysbus_dev = SYS_BUS_DEVICE(&s->escc); - sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, - OLDWORLD_ESCCB_IRQ)); - sysbus_connect_irq(sysbus_dev, 1, qdev_get_gpio_in(pic_dev, - OLDWORLD_ESCCA_IRQ)); + sbd = SYS_BUS_DEVICE(&s->escc); + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(pic_dev, OLDWORLD_ESCCB_IRQ)); + sysbus_connect_irq(sbd, 1, qdev_get_gpio_in(pic_dev, OLDWORLD_ESCCA_IRQ)); if (!qdev_realize(DEVICE(&os->nvram), BUS(&s->macio_bus), errp)) { return; } - sysbus_dev = SYS_BUS_DEVICE(&os->nvram); + sbd = SYS_BUS_DEVICE(&os->nvram); memory_region_add_subregion(&s->bar, 0x60000, - sysbus_mmio_get_region(sysbus_dev, 0)); + sysbus_mmio_get_region(sbd, 0)); pmac_format_nvram_partition(&os->nvram, os->nvram.size); /* IDE buses */ - macio_realize_ide(s, &os->ide[0], - qdev_get_gpio_in(pic_dev, OLDWORLD_IDE0_IRQ), - qdev_get_gpio_in(pic_dev, OLDWORLD_IDE0_DMA_IRQ), - 0x16, &err); - if (err) { - error_propagate(errp, err); + if (!macio_realize_ide(s, &os->ide[0], + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE0_IRQ), + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE0_DMA_IRQ), + 0x16, errp)) { return; } - macio_realize_ide(s, &os->ide[1], - qdev_get_gpio_in(pic_dev, OLDWORLD_IDE1_IRQ), - qdev_get_gpio_in(pic_dev, OLDWORLD_IDE1_DMA_IRQ), - 0x1a, &err); - if (err) { - error_propagate(errp, err); + if (!macio_realize_ide(s, &os->ide[1], + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE1_IRQ), + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE1_DMA_IRQ), + 0x1a, errp)) { return; } } @@ -220,13 +211,13 @@ static void macio_oldworld_init(Object *obj) DeviceState *dev; int i; - object_initialize_child(OBJECT(s), "pic", &os->pic, TYPE_HEATHROW); + object_initialize_child(obj, "pic", &os->pic, TYPE_HEATHROW); - object_initialize_child(OBJECT(s), "cuda", &s->cuda, TYPE_CUDA); + object_initialize_child(obj, "cuda", &s->cuda, TYPE_CUDA); - object_initialize_child(OBJECT(s), "nvram", &os->nvram, TYPE_MACIO_NVRAM); + object_initialize_child(obj, "nvram", &os->nvram, TYPE_MACIO_NVRAM); dev = DEVICE(&os->nvram); - qdev_prop_set_uint32(dev, "size", 0x2000); + qdev_prop_set_uint32(dev, "size", MACIO_NVRAM_SIZE); qdev_prop_set_uint32(dev, "it_shift", 4); for (i = 0; i < 2; i++) { @@ -273,45 +264,36 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) MacIOState *s = MACIO(d); NewWorldMacIOState *ns = NEWWORLD_MACIO(d); DeviceState *pic_dev = DEVICE(&ns->pic); - Error *err = NULL; - SysBusDevice *sysbus_dev; + SysBusDevice *sbd; MemoryRegion *timer_memory = NULL; - macio_common_realize(d, &err); - if (err) { - error_propagate(errp, err); + if (!macio_common_realize(d, errp)) { return; } /* OpenPIC */ qdev_prop_set_uint32(pic_dev, "model", OPENPIC_MODEL_KEYLARGO); - sysbus_dev = SYS_BUS_DEVICE(&ns->pic); - sysbus_realize_and_unref(sysbus_dev, &error_fatal); + sbd = SYS_BUS_DEVICE(&ns->pic); + sysbus_realize_and_unref(sbd, &error_fatal); memory_region_add_subregion(&s->bar, 0x40000, - sysbus_mmio_get_region(sysbus_dev, 0)); + sysbus_mmio_get_region(sbd, 0)); - sysbus_dev = SYS_BUS_DEVICE(&s->escc); - sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, - NEWWORLD_ESCCB_IRQ)); - sysbus_connect_irq(sysbus_dev, 1, qdev_get_gpio_in(pic_dev, - NEWWORLD_ESCCA_IRQ)); + sbd = SYS_BUS_DEVICE(&s->escc); + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(pic_dev, NEWWORLD_ESCCB_IRQ)); + sysbus_connect_irq(sbd, 1, qdev_get_gpio_in(pic_dev, NEWWORLD_ESCCA_IRQ)); /* IDE buses */ - macio_realize_ide(s, &ns->ide[0], - qdev_get_gpio_in(pic_dev, NEWWORLD_IDE0_IRQ), - qdev_get_gpio_in(pic_dev, NEWWORLD_IDE0_DMA_IRQ), - 0x16, &err); - if (err) { - error_propagate(errp, err); + if (!macio_realize_ide(s, &ns->ide[0], + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE0_IRQ), + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE0_DMA_IRQ), + 0x16, errp)) { return; } - macio_realize_ide(s, &ns->ide[1], - qdev_get_gpio_in(pic_dev, NEWWORLD_IDE1_IRQ), - qdev_get_gpio_in(pic_dev, NEWWORLD_IDE1_DMA_IRQ), - 0x1a, &err); - if (err) { - error_propagate(errp, err); + if (!macio_realize_ide(s, &ns->ide[1], + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE1_IRQ), + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE1_DMA_IRQ), + 0x1a, errp)) { return; } @@ -326,27 +308,26 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) if (!qdev_realize(DEVICE(&ns->gpio), BUS(&s->macio_bus), errp)) { return; } - sysbus_dev = SYS_BUS_DEVICE(&ns->gpio); - sysbus_connect_irq(sysbus_dev, 1, qdev_get_gpio_in(pic_dev, + sbd = SYS_BUS_DEVICE(&ns->gpio); + sysbus_connect_irq(sbd, 1, qdev_get_gpio_in(pic_dev, NEWWORLD_EXTING_GPIO1)); - sysbus_connect_irq(sysbus_dev, 9, qdev_get_gpio_in(pic_dev, + sysbus_connect_irq(sbd, 9, qdev_get_gpio_in(pic_dev, NEWWORLD_EXTING_GPIO9)); memory_region_add_subregion(&s->bar, 0x50, - sysbus_mmio_get_region(sysbus_dev, 0)); + sysbus_mmio_get_region(sbd, 0)); /* PMU */ object_initialize_child(OBJECT(s), "pmu", &s->pmu, TYPE_VIA_PMU); - object_property_set_link(OBJECT(&s->pmu), "gpio", OBJECT(sysbus_dev), + object_property_set_link(OBJECT(&s->pmu), "gpio", OBJECT(sbd), &error_abort); qdev_prop_set_bit(DEVICE(&s->pmu), "has-adb", ns->has_adb); if (!qdev_realize(DEVICE(&s->pmu), BUS(&s->macio_bus), errp)) { return; } - sysbus_dev = SYS_BUS_DEVICE(&s->pmu); - sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, - NEWWORLD_PMU_IRQ)); + sbd = SYS_BUS_DEVICE(&s->pmu); + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(pic_dev, NEWWORLD_PMU_IRQ)); memory_region_add_subregion(&s->bar, 0x16000, - sysbus_mmio_get_region(sysbus_dev, 0)); + sysbus_mmio_get_region(sbd, 0)); } else { object_unparent(OBJECT(&ns->gpio)); @@ -358,11 +339,10 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) if (!qdev_realize(DEVICE(&s->cuda), BUS(&s->macio_bus), errp)) { return; } - sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, - NEWWORLD_CUDA_IRQ)); + sbd = SYS_BUS_DEVICE(&s->cuda); + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(pic_dev, NEWWORLD_CUDA_IRQ)); memory_region_add_subregion(&s->bar, 0x16000, - sysbus_mmio_get_region(sysbus_dev, 0)); + sysbus_mmio_get_region(sbd, 0)); } } @@ -372,9 +352,9 @@ static void macio_newworld_init(Object *obj) NewWorldMacIOState *ns = NEWWORLD_MACIO(obj); int i; - object_initialize_child(OBJECT(s), "pic", &ns->pic, TYPE_OPENPIC); + object_initialize_child(obj, "pic", &ns->pic, TYPE_OPENPIC); - object_initialize_child(OBJECT(s), "gpio", &ns->gpio, TYPE_MACIO_GPIO); + object_initialize_child(obj, "gpio", &ns->gpio, TYPE_MACIO_GPIO); for (i = 0; i < 2; i++) { macio_init_ide(s, &ns->ide[i], i); @@ -390,16 +370,16 @@ static void macio_instance_init(Object *obj) qbus_init(&s->macio_bus, sizeof(s->macio_bus), TYPE_MACIO_BUS, DEVICE(obj), "macio.0"); - object_initialize_child(OBJECT(s), "dbdma", &s->dbdma, TYPE_MAC_DBDMA); + object_initialize_child(obj, "dbdma", &s->dbdma, TYPE_MAC_DBDMA); - object_initialize_child(OBJECT(s), "escc", &s->escc, TYPE_ESCC); + object_initialize_child(obj, "escc", &s->escc, TYPE_ESCC); } static const VMStateDescription vmstate_macio_oldworld = { .name = "macio-oldworld", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj.parent, OldWorldMacIOState), VMSTATE_END_OF_LIST() } @@ -419,7 +399,7 @@ static const VMStateDescription vmstate_macio_newworld = { .name = "macio-newworld", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj.parent, NewWorldMacIOState), VMSTATE_END_OF_LIST() } diff --git a/hw/misc/macio/meson.build b/hw/misc/macio/meson.build index 17282da20a..8984b818b0 100644 --- a/hw/misc/macio/meson.build +++ b/hw/misc/macio/meson.build @@ -5,4 +5,4 @@ macio_ss.add(when: 'CONFIG_MACIO_GPIO', if_true: files('gpio.c')) macio_ss.add(when: 'CONFIG_MAC_DBDMA', if_true: files('mac_dbdma.c')) macio_ss.add(when: 'CONFIG_MAC_PMU', if_true: files('pmu.c')) -softmmu_ss.add_all(when: 'CONFIG_MACIO', if_true: macio_ss) +system_ss.add_all(when: 'CONFIG_MACIO', if_true: macio_ss) diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index 336502a84b..e40c51bf52 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -29,15 +29,10 @@ */ #include "qemu/osdep.h" -#include "hw/ppc/mac.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "hw/input/adb.h" #include "hw/irq.h" -#include "hw/misc/mos6522.h" -#include "hw/misc/macio/gpio.h" #include "hw/misc/macio/pmu.h" -#include "qapi/error.h" #include "qemu/timer.h" #include "sysemu/runstate.h" #include "sysemu/rtc.h" @@ -673,7 +668,7 @@ static const VMStateDescription vmstate_pmu_adb = { .version_id = 1, .minimum_version_id = 1, .needed = pmu_adb_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(adb_reply_size, PMUState), VMSTATE_BUFFER(adb_reply, PMUState), VMSTATE_END_OF_LIST() @@ -684,7 +679,7 @@ static const VMStateDescription vmstate_pmu = { .name = "pmu", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(mos6522_pmu.parent_obj, PMUState, 0, vmstate_mos6522, MOS6522State), VMSTATE_UINT8(last_b, PMUState), @@ -703,7 +698,7 @@ static const VMStateDescription vmstate_pmu = { VMSTATE_INT64(one_sec_target, PMUState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_pmu_adb, NULL } @@ -742,8 +737,7 @@ static void pmu_realize(DeviceState *dev, Error **errp) timer_mod(s->one_sec_timer, s->one_sec_target); if (s->has_adb) { - qbus_init(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, - dev, "adb.0"); + qbus_init(adb_bus, sizeof(*adb_bus), TYPE_ADB_BUS, dev, "adb.0"); adb_register_autopoll_callback(adb_bus, pmu_adb_poll, s); } } @@ -798,14 +792,16 @@ static void mos6522_pmu_portB_write(MOS6522State *s) pmu_update(ps); } -static void mos6522_pmu_reset(DeviceState *dev) +static void mos6522_pmu_reset_hold(Object *obj) { - MOS6522State *ms = MOS6522(dev); + MOS6522State *ms = MOS6522(obj); MOS6522PMUState *mps = container_of(ms, MOS6522PMUState, parent_obj); PMUState *s = container_of(mps, PMUState, mos6522_pmu); MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); - mdc->parent_reset(dev); + if (mdc->parent_phases.hold) { + mdc->parent_phases.hold(obj); + } ms->timers[0].frequency = VIA_TIMER_FREQ; ms->timers[1].frequency = (SCALE_US * 6000) / 4700; @@ -815,11 +811,11 @@ static void mos6522_pmu_reset(DeviceState *dev) static void mos6522_pmu_class_init(ObjectClass *oc, void *data) { - DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_CLASS(oc); - device_class_set_parent_reset(dc, mos6522_pmu_reset, - &mdc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, mos6522_pmu_reset_hold, + NULL, &mdc->parent_phases); mdc->portB_write = mos6522_pmu_portB_write; } diff --git a/hw/misc/mchp_pfsoc_ioscb.c b/hw/misc/mchp_pfsoc_ioscb.c index f4fd55a0e5..a71d134295 100644 --- a/hw/misc/mchp_pfsoc_ioscb.c +++ b/hw/misc/mchp_pfsoc_ioscb.c @@ -24,6 +24,7 @@ #include "qemu/bitops.h" #include "qemu/log.h" #include "qapi/error.h" +#include "hw/irq.h" #include "hw/sysbus.h" #include "hw/misc/mchp_pfsoc_ioscb.h" @@ -33,6 +34,10 @@ */ #define IOSCB_WHOLE_REG_SIZE 0x10000000 #define IOSCB_SUBMOD_REG_SIZE 0x1000 +#define IOSCB_CCC_REG_SIZE 0x2000000 +#define IOSCB_CTRL_REG_SIZE 0x800 +#define IOSCB_QSPIXIP_REG_SIZE 0x200 + /* * There are many sub-modules in the IOSCB module. @@ -44,7 +49,10 @@ #define IOSCB_LANE01_BASE 0x06500000 #define IOSCB_LANE23_BASE 0x06510000 #define IOSCB_CTRL_BASE 0x07020000 +#define IOSCB_QSPIXIP_BASE 0x07020100 +#define IOSCB_MAILBOX_BASE 0x07020800 #define IOSCB_CFG_BASE 0x07080000 +#define IOSCB_CCC_BASE 0x08000000 #define IOSCB_PLL_MSS_BASE 0x0E001000 #define IOSCB_CFM_MSS_BASE 0x0E002000 #define IOSCB_PLL_DDR_BASE 0x0E010000 @@ -141,6 +149,58 @@ static const MemoryRegionOps mchp_pfsoc_io_calib_ddr_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +#define SERVICES_CR 0x50 +#define SERVICES_SR 0x54 +#define SERVICES_STATUS_SHIFT 16 + +static uint64_t mchp_pfsoc_ctrl_read(void *opaque, hwaddr offset, + unsigned size) +{ + uint32_t val = 0; + + switch (offset) { + case SERVICES_SR: + /* + * Although some services have no error codes, most do. All services + * that do implement errors, begin their error codes at 1. Treat all + * service requests as failures & return 1. + * See the "PolarFire® FPGA and PolarFire SoC FPGA System Services" + * user guide for more information on service error codes. + */ + val = 1u << SERVICES_STATUS_SHIFT; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read " + "(size %d, offset 0x%" HWADDR_PRIx ")\n", + __func__, size, offset); + } + + return val; +} + +static void mchp_pfsoc_ctrl_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + MchpPfSoCIoscbState *s = opaque; + + switch (offset) { + case SERVICES_CR: + qemu_irq_raise(s->irq); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, value, offset); + } +} + +static const MemoryRegionOps mchp_pfsoc_ctrl_ops = { + .read = mchp_pfsoc_ctrl_read, + .write = mchp_pfsoc_ctrl_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + static void mchp_pfsoc_ioscb_realize(DeviceState *dev, Error **errp) { MchpPfSoCIoscbState *s = MCHP_PFSOC_IOSCB(dev); @@ -160,14 +220,26 @@ static void mchp_pfsoc_ioscb_realize(DeviceState *dev, Error **errp) "mchp.pfsoc.ioscb.lane23", IOSCB_SUBMOD_REG_SIZE); memory_region_add_subregion(&s->container, IOSCB_LANE23_BASE, &s->lane23); - memory_region_init_io(&s->ctrl, OBJECT(s), &mchp_pfsoc_dummy_ops, s, - "mchp.pfsoc.ioscb.ctrl", IOSCB_SUBMOD_REG_SIZE); + memory_region_init_io(&s->ctrl, OBJECT(s), &mchp_pfsoc_ctrl_ops, s, + "mchp.pfsoc.ioscb.ctrl", IOSCB_CTRL_REG_SIZE); memory_region_add_subregion(&s->container, IOSCB_CTRL_BASE, &s->ctrl); + memory_region_init_io(&s->qspixip, OBJECT(s), &mchp_pfsoc_dummy_ops, s, + "mchp.pfsoc.ioscb.qspixip", IOSCB_QSPIXIP_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_QSPIXIP_BASE, &s->qspixip); + + memory_region_init_io(&s->mailbox, OBJECT(s), &mchp_pfsoc_dummy_ops, s, + "mchp.pfsoc.ioscb.mailbox", IOSCB_SUBMOD_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_MAILBOX_BASE, &s->mailbox); + memory_region_init_io(&s->cfg, OBJECT(s), &mchp_pfsoc_dummy_ops, s, "mchp.pfsoc.ioscb.cfg", IOSCB_SUBMOD_REG_SIZE); memory_region_add_subregion(&s->container, IOSCB_CFG_BASE, &s->cfg); + memory_region_init_io(&s->ccc, OBJECT(s), &mchp_pfsoc_dummy_ops, s, + "mchp.pfsoc.ioscb.ccc", IOSCB_CCC_REG_SIZE); + memory_region_add_subregion(&s->container, IOSCB_CCC_BASE, &s->ccc); + memory_region_init_io(&s->pll_mss, OBJECT(s), &mchp_pfsoc_pll_ops, s, "mchp.pfsoc.ioscb.pll_mss", IOSCB_SUBMOD_REG_SIZE); memory_region_add_subregion(&s->container, IOSCB_PLL_MSS_BASE, &s->pll_mss); @@ -216,6 +288,8 @@ static void mchp_pfsoc_ioscb_realize(DeviceState *dev, Error **errp) IOSCB_SUBMOD_REG_SIZE); memory_region_add_subregion(&s->container, IOSCB_IO_CALIB_SGMII_BASE, &s->io_calib_sgmii); + + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); } static void mchp_pfsoc_ioscb_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/mchp_pfsoc_sysreg.c b/hw/misc/mchp_pfsoc_sysreg.c index 89571eded5..7876fe0c5b 100644 --- a/hw/misc/mchp_pfsoc_sysreg.c +++ b/hw/misc/mchp_pfsoc_sysreg.c @@ -24,10 +24,12 @@ #include "qemu/bitops.h" #include "qemu/log.h" #include "qapi/error.h" +#include "hw/irq.h" #include "hw/sysbus.h" #include "hw/misc/mchp_pfsoc_sysreg.h" #define ENVM_CR 0xb8 +#define MESSAGE_INT 0x118c static uint64_t mchp_pfsoc_sysreg_read(void *opaque, hwaddr offset, unsigned size) @@ -52,10 +54,17 @@ static uint64_t mchp_pfsoc_sysreg_read(void *opaque, hwaddr offset, static void mchp_pfsoc_sysreg_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { - qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " - "(size %d, value 0x%" PRIx64 - ", offset 0x%" HWADDR_PRIx ")\n", - __func__, size, value, offset); + MchpPfSoCSysregState *s = opaque; + switch (offset) { + case MESSAGE_INT: + qemu_irq_lower(s->irq); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write " + "(size %d, value 0x%" PRIx64 + ", offset 0x%" HWADDR_PRIx ")\n", + __func__, size, value, offset); + } } static const MemoryRegionOps mchp_pfsoc_sysreg_ops = { @@ -73,6 +82,7 @@ static void mchp_pfsoc_sysreg_realize(DeviceState *dev, Error **errp) "mchp.pfsoc.sysreg", MCHP_PFSOC_SYSREG_REG_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->sysreg); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); } static void mchp_pfsoc_sysreg_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 132b7b7344..86596a3888 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -1,79 +1,88 @@ -softmmu_ss.add(when: 'CONFIG_APPLESMC', if_true: files('applesmc.c')) -softmmu_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c')) -softmmu_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c')) -softmmu_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c')) -softmmu_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c')) -softmmu_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c')) -softmmu_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c')) -softmmu_ss.add(when: 'CONFIG_SGA', if_true: files('sga.c')) -softmmu_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c')) -softmmu_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c')) -softmmu_ss.add(when: 'CONFIG_LED', if_true: files('led.c')) -softmmu_ss.add(when: 'CONFIG_PVPANIC_COMMON', if_true: files('pvpanic.c')) +system_ss.add(when: 'CONFIG_APPLESMC', if_true: files('applesmc.c')) +system_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c')) +system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c')) +system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c')) +system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c')) +system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c')) +system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c')) +system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c')) +system_ss.add(when: 'CONFIG_LED', if_true: files('led.c')) +system_ss.add(when: 'CONFIG_PVPANIC_COMMON', if_true: files('pvpanic.c')) # ARM devices -softmmu_ss.add(when: 'CONFIG_PL310', if_true: files('arm_l2x0.c')) -softmmu_ss.add(when: 'CONFIG_INTEGRATOR_DEBUG', if_true: files('arm_integrator_debug.c')) -softmmu_ss.add(when: 'CONFIG_A9SCU', if_true: files('a9scu.c')) -softmmu_ss.add(when: 'CONFIG_ARM11SCU', if_true: files('arm11scu.c')) +system_ss.add(when: 'CONFIG_PL310', if_true: files('arm_l2x0.c')) +system_ss.add(when: 'CONFIG_INTEGRATOR_DEBUG', if_true: files('arm_integrator_debug.c')) +system_ss.add(when: 'CONFIG_A9SCU', if_true: files('a9scu.c')) +system_ss.add(when: 'CONFIG_ARM11SCU', if_true: files('arm11scu.c')) -softmmu_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_ras.c')) +system_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_ras.c')) # Mac devices -softmmu_ss.add(when: 'CONFIG_MOS6522', if_true: files('mos6522.c')) +system_ss.add(when: 'CONFIG_MOS6522', if_true: files('mos6522.c')) +system_ss.add(when: 'CONFIG_DJMEMC', if_true: files('djmemc.c')) +system_ss.add(when: 'CONFIG_IOSB', if_true: files('iosb.c')) # virt devices -softmmu_ss.add(when: 'CONFIG_VIRT_CTRL', if_true: files('virt_ctrl.c')) +system_ss.add(when: 'CONFIG_VIRT_CTRL', if_true: files('virt_ctrl.c')) # RISC-V devices -softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_DMC', if_true: files('mchp_pfsoc_dmc.c')) -softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_ioscb.c')) -softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_SYSREG', if_true: files('mchp_pfsoc_sysreg.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_TEST', if_true: files('sifive_test.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c')) +system_ss.add(when: 'CONFIG_MCHP_PFSOC_DMC', if_true: files('mchp_pfsoc_dmc.c')) +system_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_ioscb.c')) +system_ss.add(when: 'CONFIG_MCHP_PFSOC_SYSREG', if_true: files('mchp_pfsoc_sysreg.c')) +system_ss.add(when: 'CONFIG_SIFIVE_TEST', if_true: files('sifive_test.c')) +system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c')) +system_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true: files('sifive_e_aon.c')) +system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c')) +system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c')) subdir('macio') -softmmu_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c')) +system_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-ccu.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_SRAMC', if_true: files('allwinner-sramc.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_A10_CCM', if_true: files('allwinner-a10-ccm.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_A10_DRAMC', if_true: files('allwinner-a10-dramc.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-ccu.c')) specific_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-cpucfg.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-dramc.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-sysctrl.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sid.c')) -softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c')) -softmmu_ss.add(when: 'CONFIG_NSERIES', if_true: files('cbus.c')) -softmmu_ss.add(when: 'CONFIG_ECCMEMCTL', if_true: files('eccmemctl.c')) -softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pmu.c', 'exynos4210_clk.c', 'exynos4210_rng.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files( +system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-dramc.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3-sysctrl.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sid.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-ccu.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-dramc.c')) +system_ss.add(when: 'CONFIG_AXP2XX_PMU', if_true: files('axp2xx.c')) +system_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c')) +system_ss.add(when: 'CONFIG_NSERIES', if_true: files('cbus.c')) +system_ss.add(when: 'CONFIG_ECCMEMCTL', if_true: files('eccmemctl.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pmu.c', 'exynos4210_clk.c', 'exynos4210_rng.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files( 'imx25_ccm.c', 'imx31_ccm.c', 'imx6_ccm.c', + 'imx6_src.c', 'imx6ul_ccm.c', 'imx7_ccm.c', + 'imx7_src.c', 'imx7_gpr.c', 'imx7_snvs.c', 'imx_ccm.c', 'imx_rngc.c', )) -softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mst_fpga.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files( +system_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mst_fpga.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files( 'npcm7xx_clk.c', 'npcm7xx_gcr.c', 'npcm7xx_mft.c', 'npcm7xx_pwm.c', 'npcm7xx_rng.c', )) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files( +system_ss.add(when: 'CONFIG_OMAP', if_true: files( 'omap_clk.c', 'omap_gpmc.c', 'omap_l4.c', 'omap_sdrc.c', 'omap_tap.c', )) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files( +system_ss.add(when: 'CONFIG_RASPI', if_true: files( 'bcm2835_mbox.c', 'bcm2835_mphi.c', 'bcm2835_property.c', @@ -82,58 +91,68 @@ softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files( 'bcm2835_cprman.c', 'bcm2835_powermgt.c', )) -softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c')) -softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c')) -specific_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp-crf.c')) -specific_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp-apu-ctrl.c')) -specific_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-crl.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files( +system_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c')) +system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp-crf.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp-apu-ctrl.c')) +system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files( + 'xlnx-versal-crl.c', 'xlnx-versal-xramc.c', 'xlnx-versal-pmc-iou-slcr.c', + 'xlnx-versal-cfu.c', + 'xlnx-cfi-if.c', + 'xlnx-versal-cframe-reg.c', )) -softmmu_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c')) -softmmu_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c')) -softmmu_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c')) -softmmu_ss.add(when: 'CONFIG_MPS2_FPGAIO', if_true: files('mps2-fpgaio.c')) -softmmu_ss.add(when: 'CONFIG_MPS2_SCC', if_true: files('mps2-scc.c')) +system_ss.add(when: 'CONFIG_XLNX_VERSAL_TRNG', if_true: files( + 'xlnx-versal-trng.c', +)) +system_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c')) +system_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c')) +system_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c')) +system_ss.add(when: 'CONFIG_STM32L4X5_EXTI', if_true: files('stm32l4x5_exti.c')) +system_ss.add(when: 'CONFIG_STM32L4X5_SYSCFG', if_true: files('stm32l4x5_syscfg.c')) +system_ss.add(when: 'CONFIG_STM32L4X5_RCC', if_true: files('stm32l4x5_rcc.c')) +system_ss.add(when: 'CONFIG_MPS2_FPGAIO', if_true: files('mps2-fpgaio.c')) +system_ss.add(when: 'CONFIG_MPS2_SCC', if_true: files('mps2-scc.c')) -softmmu_ss.add(when: 'CONFIG_TZ_MPC', if_true: files('tz-mpc.c')) -softmmu_ss.add(when: 'CONFIG_TZ_MSC', if_true: files('tz-msc.c')) -softmmu_ss.add(when: 'CONFIG_TZ_PPC', if_true: files('tz-ppc.c')) -softmmu_ss.add(when: 'CONFIG_IOTKIT_SECCTL', if_true: files('iotkit-secctl.c')) -softmmu_ss.add(when: 'CONFIG_IOTKIT_SYSINFO', if_true: files('iotkit-sysinfo.c')) -softmmu_ss.add(when: 'CONFIG_ARMSSE_CPU_PWRCTRL', if_true: files('armsse-cpu-pwrctrl.c')) -softmmu_ss.add(when: 'CONFIG_ARMSSE_CPUID', if_true: files('armsse-cpuid.c')) -softmmu_ss.add(when: 'CONFIG_ARMSSE_MHU', if_true: files('armsse-mhu.c')) +system_ss.add(when: 'CONFIG_TZ_MPC', if_true: files('tz-mpc.c')) +system_ss.add(when: 'CONFIG_TZ_MSC', if_true: files('tz-msc.c')) +system_ss.add(when: 'CONFIG_TZ_PPC', if_true: files('tz-ppc.c')) +system_ss.add(when: 'CONFIG_IOTKIT_SECCTL', if_true: files('iotkit-secctl.c')) +system_ss.add(when: 'CONFIG_IOTKIT_SYSCTL', if_true: files('iotkit-sysctl.c')) +system_ss.add(when: 'CONFIG_IOTKIT_SYSINFO', if_true: files('iotkit-sysinfo.c')) +system_ss.add(when: 'CONFIG_ARMSSE_CPU_PWRCTRL', if_true: files('armsse-cpu-pwrctrl.c')) +system_ss.add(when: 'CONFIG_ARMSSE_CPUID', if_true: files('armsse-cpuid.c')) +system_ss.add(when: 'CONFIG_ARMSSE_MHU', if_true: files('armsse-mhu.c')) -softmmu_ss.add(when: 'CONFIG_PVPANIC_ISA', if_true: files('pvpanic-isa.c')) -softmmu_ss.add(when: 'CONFIG_PVPANIC_PCI', if_true: files('pvpanic-pci.c')) -softmmu_ss.add(when: 'CONFIG_AUX', if_true: files('auxbus.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( +system_ss.add(when: 'CONFIG_PVPANIC_ISA', if_true: files('pvpanic-isa.c')) +system_ss.add(when: 'CONFIG_PVPANIC_PCI', if_true: files('pvpanic-pci.c')) +system_ss.add(when: 'CONFIG_AUX', if_true: files('auxbus.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_hace.c', 'aspeed_i3c.c', 'aspeed_lpc.c', 'aspeed_scu.c', 'aspeed_sbc.c', 'aspeed_sdmc.c', - 'aspeed_xdma.c')) + 'aspeed_xdma.c', + 'aspeed_peci.c')) -softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-sysreg.c')) -softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_rng.c')) +system_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-sysreg.c')) +system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_rng.c')) -softmmu_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_ahb_apb_pnp.c')) +system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_ahb_apb_pnp.c')) + +system_ss.add(when: 'CONFIG_I2C_ECHO', if_true: files('i2c-echo.c')) specific_ss.add(when: 'CONFIG_AVR_POWER', if_true: files('avr_power.c')) -specific_ss.add(when: 'CONFIG_IMX', if_true: files('imx6_src.c')) -specific_ss.add(when: 'CONFIG_IOTKIT_SYSCTL', if_true: files('iotkit-sysctl.c')) - specific_ss.add(when: 'CONFIG_MAC_VIA', if_true: files('mac_via.c')) specific_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_cmgcr.c', 'mips_cpc.c')) specific_ss.add(when: 'CONFIG_MIPS_ITU', if_true: files('mips_itu.c')) -specific_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa_ec.c')) +system_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa_ec.c')) # HPPA devices -softmmu_ss.add(when: 'CONFIG_LASI', if_true: files('lasi.c')) +system_ss.add(when: 'CONFIG_LASI', if_true: files('lasi.c')) diff --git a/hw/misc/mips_cmgcr.c b/hw/misc/mips_cmgcr.c index 3c8b37f700..2703040f45 100644 --- a/hw/misc/mips_cmgcr.c +++ b/hw/misc/mips_cmgcr.c @@ -205,14 +205,14 @@ static const VMStateDescription vmstate_mips_gcr = { .name = "mips-gcr", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(cpc_base, MIPSGCRState), VMSTATE_END_OF_LIST() }, }; static Property mips_gcr_properties[] = { - DEFINE_PROP_INT32("num-vp", MIPSGCRState, num_vps, 1), + DEFINE_PROP_UINT32("num-vp", MIPSGCRState, num_vps, 1), DEFINE_PROP_INT32("gcr-rev", MIPSGCRState, gcr_rev, 0x800), DEFINE_PROP_UINT64("gcr-base", MIPSGCRState, gcr_base, GCR_BASE_ADDR), DEFINE_PROP_LINK("gic", MIPSGCRState, gic_mr, TYPE_MEMORY_REGION, diff --git a/hw/misc/mips_cpc.c b/hw/misc/mips_cpc.c index 4a94c87054..1e8fd2e699 100644 --- a/hw/misc/mips_cpc.c +++ b/hw/misc/mips_cpc.c @@ -157,7 +157,7 @@ static const VMStateDescription vmstate_mips_cpc = { .name = "mips-cpc", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(vp_running, MIPSCPCState), VMSTATE_END_OF_LIST() }, diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index badef5c214..f8acfb3ee2 100644 --- a/hw/misc/mips_itu.c +++ b/hw/misc/mips_itu.c @@ -22,9 +22,10 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qapi/error.h" -#include "exec/exec-all.h" +#include "hw/core/cpu.h" #include "hw/misc/mips_itu.h" #include "hw/qdev-properties.h" +#include "target/mips/cpu.h" #define ITC_TAG_ADDRSPACE_SZ (ITC_ADDRESSMAP_NUM * 8) /* Initialize as 4kB area to fit all 32 cells with default 128B grain. @@ -85,7 +86,7 @@ static uint64_t itc_tag_read(void *opaque, hwaddr addr, unsigned size) return tag->ITCAddressMap[index]; } -void itc_reconfigure(MIPSITUState *tag) +static void itc_reconfigure(MIPSITUState *tag) { uint64_t *am = &tag->ITCAddressMap[0]; MemoryRegion *mr = &tag->storage_io; @@ -93,12 +94,6 @@ void itc_reconfigure(MIPSITUState *tag) uint64_t size = (1 * KiB) + (am[1] & ITC_AM1_ADDR_MASK_MASK); bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0; - if (tag->saar_present) { - address = ((*(uint64_t *) tag->saar) & 0xFFFFFFFFE000ULL) << 4; - size = 1ULL << ((*(uint64_t *) tag->saar >> 1) & 0x1f); - is_enabled = *(uint64_t *) tag->saar & 1; - } - memory_region_transaction_begin(); if (!(size & (size - 1))) { memory_region_set_size(mr, size); @@ -157,12 +152,7 @@ static inline ITCView get_itc_view(hwaddr addr) static inline int get_cell_stride_shift(const MIPSITUState *s) { /* Minimum interval (for EntryGain = 0) is 128 B */ - if (s->saar_present) { - return 7 + ((s->icr0 >> ITC_ICR0_BLK_GRAIN) & - ITC_ICR0_BLK_GRAIN_MASK); - } else { - return 7 + (s->ITCAddressMap[1] & ITC_AM1_ENTRY_GRAIN_MASK); - } + return 7 + (s->ITCAddressMap[1] & ITC_AM1_ENTRY_GRAIN_MASK); } static inline ITCStorageCell *get_cell(MIPSITUState *s, @@ -534,26 +524,20 @@ static void mips_itu_reset(DeviceState *dev) { MIPSITUState *s = MIPS_ITU(dev); - if (s->saar_present) { - *(uint64_t *) s->saar = 0x11 << 1; - s->icr0 = get_num_cells(s) << ITC_ICR0_CELL_NUM; - } else { - s->ITCAddressMap[0] = 0; - s->ITCAddressMap[1] = + s->ITCAddressMap[0] = 0; + s->ITCAddressMap[1] = ((ITC_STORAGE_ADDRSPACE_SZ - 1) & ITC_AM1_ADDR_MASK_MASK) | (get_num_cells(s) << ITC_AM1_NUMENTRIES_OFS); - } itc_reconfigure(s); itc_reset_cells(s); } static Property mips_itu_properties[] = { - DEFINE_PROP_INT32("num-fifo", MIPSITUState, num_fifo, + DEFINE_PROP_UINT32("num-fifo", MIPSITUState, num_fifo, ITC_FIFO_NUM_MAX), - DEFINE_PROP_INT32("num-semaphores", MIPSITUState, num_semaphores, + DEFINE_PROP_UINT32("num-semaphores", MIPSITUState, num_semaphores, ITC_SEMAPH_NUM_MAX), - DEFINE_PROP_BOOL("saar-present", MIPSITUState, saar_present, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index f9e646350e..e3fe87c20c 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -25,7 +25,6 @@ */ #include "qemu/osdep.h" -#include "hw/input/adb.h" #include "hw/irq.h" #include "hw/misc/mos6522.h" #include "hw/qdev-properties.h" @@ -595,7 +594,7 @@ void hmp_info_via(Monitor *mon, const QDict *qdict) if (hmp_handle_error(mon, err)) { return; } - monitor_printf(mon, "%s", info->human_readable_text); + monitor_puts(mon, info->human_readable_text); } static const MemoryRegionOps mos6522_ops = { @@ -612,7 +611,7 @@ static const VMStateDescription vmstate_mos6522_timer = { .name = "mos6522_timer", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(latch, MOS6522Timer), VMSTATE_UINT16(counter_value, MOS6522Timer), VMSTATE_INT64(load_time, MOS6522Timer), @@ -626,7 +625,7 @@ const VMStateDescription vmstate_mos6522 = { .name = "mos6522", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(a, MOS6522State), VMSTATE_UINT8(b, MOS6522State), VMSTATE_UINT8(dira, MOS6522State), @@ -643,9 +642,9 @@ const VMStateDescription vmstate_mos6522 = { } }; -static void mos6522_reset(DeviceState *dev) +static void mos6522_reset_hold(Object *obj) { - MOS6522State *s = MOS6522(dev); + MOS6522State *s = MOS6522(obj); s->b = 0; s->a = 0; @@ -705,9 +704,10 @@ static Property mos6522_properties[] = { static void mos6522_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_CLASS(oc); - dc->reset = mos6522_reset; + rc->phases.hold = mos6522_reset_hold; dc->vmsd = &vmstate_mos6522; device_class_set_props(dc, mos6522_properties); mdc->portB_write = mos6522_portB_write; diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c index 07b8cbdad2..aa1bb83e72 100644 --- a/hw/misc/mps2-fpgaio.c +++ b/hw/misc/mps2-fpgaio.c @@ -305,7 +305,7 @@ static const VMStateDescription mps2_fpgaio_vmstate = { .name = "mps2-fpgaio", .version_id = 3, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(led0, MPS2FPGAIO), VMSTATE_UINT32(prescale, MPS2FPGAIO), VMSTATE_UINT32(misc, MPS2FPGAIO), diff --git a/hw/misc/mps2-scc.c b/hw/misc/mps2-scc.c index b3b42a792c..18be74157e 100644 --- a/hw/misc/mps2-scc.c +++ b/hw/misc/mps2-scc.c @@ -37,6 +37,7 @@ REG32(CFG3, 0xc) REG32(CFG4, 0x10) REG32(CFG5, 0x14) REG32(CFG6, 0x18) +REG32(CFG7, 0x1c) REG32(CFGDATA_RTN, 0xa0) REG32(CFGDATA_OUT, 0xa4) REG32(CFGCTRL, 0xa8) @@ -59,6 +60,51 @@ static int scc_partno(MPS2SCC *s) return extract32(s->id, 4, 8); } +/* Is CFG_REG2 present? */ +static bool have_cfg2(MPS2SCC *s) +{ + return scc_partno(s) == 0x524 || scc_partno(s) == 0x547 || + scc_partno(s) == 0x536; +} + +/* Is CFG_REG3 present? */ +static bool have_cfg3(MPS2SCC *s) +{ + return scc_partno(s) != 0x524 && scc_partno(s) != 0x547 && + scc_partno(s) != 0x536; +} + +/* Is CFG_REG5 present? */ +static bool have_cfg5(MPS2SCC *s) +{ + return scc_partno(s) == 0x524 || scc_partno(s) == 0x547 || + scc_partno(s) == 0x536; +} + +/* Is CFG_REG6 present? */ +static bool have_cfg6(MPS2SCC *s) +{ + return scc_partno(s) == 0x524 || scc_partno(s) == 0x536; +} + +/* Is CFG_REG7 present? */ +static bool have_cfg7(MPS2SCC *s) +{ + return scc_partno(s) == 0x536; +} + +/* Does CFG_REG0 drive the 'remap' GPIO output? */ +static bool cfg0_is_remap(MPS2SCC *s) +{ + return scc_partno(s) != 0x536; +} + +/* Is CFG_REG1 driving a set of LEDs? */ +static bool cfg1_is_leds(MPS2SCC *s) +{ + return scc_partno(s) != 0x536; +} + /* Handle a write via the SYS_CFG channel to the specified function/device. * Return false on error (reported to guest via SYS_CFGCTRL ERROR bit). */ @@ -111,19 +157,25 @@ static uint64_t mps2_scc_read(void *opaque, hwaddr offset, unsigned size) r = s->cfg1; break; case A_CFG2: - if (scc_partno(s) != 0x524 && scc_partno(s) != 0x547) { - /* CFG2 reserved on other boards */ + if (!have_cfg2(s)) { goto bad_offset; } r = s->cfg2; break; case A_CFG3: - if (scc_partno(s) == 0x524 && scc_partno(s) == 0x547) { - /* CFG3 reserved on AN524 */ + if (!have_cfg3(s)) { goto bad_offset; } - /* These are user-settable DIP switches on the board. We don't + /* + * These are user-settable DIP switches on the board. We don't * model that, so just return zeroes. + * + * TODO: for AN536 this is MCC_MSB_ADDR "additional MCC addressing + * bits". These change which part of the DDR4 the motherboard + * configuration controller can see in its memory map (see the + * appnote section 2.4). QEMU doesn't model the MCC at all, so these + * bits are not interesting to us; read-as-zero is as good as anything + * else. */ r = 0; break; @@ -131,19 +183,23 @@ static uint64_t mps2_scc_read(void *opaque, hwaddr offset, unsigned size) r = s->cfg4; break; case A_CFG5: - if (scc_partno(s) != 0x524 && scc_partno(s) != 0x547) { - /* CFG5 reserved on other boards */ + if (!have_cfg5(s)) { goto bad_offset; } r = s->cfg5; break; case A_CFG6: - if (scc_partno(s) != 0x524) { - /* CFG6 reserved on other boards */ + if (!have_cfg6(s)) { goto bad_offset; } r = s->cfg6; break; + case A_CFG7: + if (!have_cfg7(s)) { + goto bad_offset; + } + r = s->cfg7; + break; case A_CFGDATA_RTN: r = s->cfgdata_rtn; break; @@ -191,38 +247,58 @@ static void mps2_scc_write(void *opaque, hwaddr offset, uint64_t value, * we always reflect bit 0 in the 'remap' GPIO output line, * and let the board wire it up or not as it chooses. * TODO on some boards bit 1 is CPU_WAIT. + * + * TODO: on the AN536 this register controls reset and halt + * for both CPUs. For the moment we don't implement this, so the + * register just reads as written. */ s->cfg0 = value; - qemu_set_irq(s->remap, s->cfg0 & 1); + if (cfg0_is_remap(s)) { + qemu_set_irq(s->remap, s->cfg0 & 1); + } break; case A_CFG1: s->cfg1 = value; - for (size_t i = 0; i < ARRAY_SIZE(s->led); i++) { - led_set_state(s->led[i], extract32(value, i, 1)); + /* + * On most boards this register drives LEDs. + * + * TODO: for AN536 this controls whether flash and ATCM are + * enabled or disabled on reset. QEMU doesn't model this, and + * always wires up RAM in the ATCM area and ROM in the flash area. + */ + if (cfg1_is_leds(s)) { + for (size_t i = 0; i < ARRAY_SIZE(s->led); i++) { + led_set_state(s->led[i], extract32(value, i, 1)); + } } break; case A_CFG2: - if (scc_partno(s) != 0x524 && scc_partno(s) != 0x547) { - /* CFG2 reserved on other boards */ + if (!have_cfg2(s)) { goto bad_offset; } - /* AN524: QSPI Select signal */ + /* AN524, AN536: QSPI Select signal */ s->cfg2 = value; break; case A_CFG5: - if (scc_partno(s) != 0x524 && scc_partno(s) != 0x547) { - /* CFG5 reserved on other boards */ + if (!have_cfg5(s)) { goto bad_offset; } - /* AN524: ACLK frequency in Hz */ + /* AN524, AN536: ACLK frequency in Hz */ s->cfg5 = value; break; case A_CFG6: - if (scc_partno(s) != 0x524) { - /* CFG6 reserved on other boards */ + if (!have_cfg6(s)) { goto bad_offset; } /* AN524: Clock divider for BRAM */ + /* AN536: Core 0 vector table base address */ + s->cfg6 = value; + break; + case A_CFG7: + if (!have_cfg7(s)) { + goto bad_offset; + } + /* AN536: Core 1 vector table base address */ s->cfg6 = value; break; case A_CFGDATA_OUT: @@ -329,11 +405,36 @@ static void mps2_scc_realize(DeviceState *dev, Error **errp) s->oscclk = g_new0(uint32_t, s->num_oscclk); } +static void mps2_scc_finalize(Object *obj) +{ + MPS2SCC *s = MPS2_SCC(obj); + + g_free(s->oscclk_reset); +} + +static bool cfg7_needed(void *opaque) +{ + MPS2SCC *s = opaque; + + return have_cfg7(s); +} + +static const VMStateDescription vmstate_cfg7 = { + .name = "mps2-scc/cfg7", + .version_id = 1, + .minimum_version_id = 1, + .needed = cfg7_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(cfg7, MPS2SCC), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription mps2_scc_vmstate = { .name = "mps2-scc", .version_id = 3, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(cfg0, MPS2SCC), VMSTATE_UINT32(cfg1, MPS2SCC), VMSTATE_UINT32(cfg2, MPS2SCC), @@ -348,6 +449,10 @@ static const VMStateDescription mps2_scc_vmstate = { VMSTATE_VARRAY_UINT32(oscclk, MPS2SCC, num_oscclk, 0, vmstate_info_uint32, uint32_t), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_cfg7, + NULL } }; @@ -385,6 +490,7 @@ static const TypeInfo mps2_scc_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MPS2SCC), .instance_init = mps2_scc_init, + .instance_finalize = mps2_scc_finalize, .class_init = mps2_scc_class_init, }; diff --git a/hw/misc/msf2-sysreg.c b/hw/misc/msf2-sysreg.c index 2dce55c364..f54382a816 100644 --- a/hw/misc/msf2-sysreg.c +++ b/hw/misc/msf2-sysreg.c @@ -112,7 +112,7 @@ static const VMStateDescription vmstate_msf2_sysreg = { .name = TYPE_MSF2_SYSREG, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, MSF2SysregState, MSF2_SYSREG_MMIO_SIZE / 4), VMSTATE_END_OF_LIST() } diff --git a/hw/misc/mst_fpga.c b/hw/misc/mst_fpga.c index 2aaadfa966..2d7bfa5ad9 100644 --- a/hw/misc/mst_fpga.c +++ b/hw/misc/mst_fpga.c @@ -131,7 +131,7 @@ mst_fpga_readb(void *opaque, hwaddr addr, unsigned size) return s->pcmcia1; default: printf("Mainstone - mst_fpga_readb: Bad register offset " - "0x" TARGET_FMT_plx "\n", addr); + "0x" HWADDR_FMT_plx "\n", addr); } return 0; } @@ -185,7 +185,7 @@ mst_fpga_writeb(void *opaque, hwaddr addr, uint64_t value, break; default: printf("Mainstone - mst_fpga_writeb: Bad register offset " - "0x" TARGET_FMT_plx "\n", addr); + "0x" HWADDR_FMT_plx "\n", addr); } } @@ -227,7 +227,7 @@ static const VMStateDescription vmstate_mst_fpga_regs = { .version_id = 0, .minimum_version_id = 0, .post_load = mst_fpga_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(prev_level, mst_irq_state), VMSTATE_UINT32(leddat1, mst_irq_state), VMSTATE_UINT32(leddat2, mst_irq_state), diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c index bc2b879feb..ac1622c38a 100644 --- a/hw/misc/npcm7xx_clk.c +++ b/hw/misc/npcm7xx_clk.c @@ -976,7 +976,7 @@ static const VMStateDescription vmstate_npcm7xx_clk_pll = { .name = "npcm7xx-clock-pll", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CLOCK(clock_in, NPCM7xxClockPLLState), VMSTATE_END_OF_LIST(), }, @@ -986,7 +986,7 @@ static const VMStateDescription vmstate_npcm7xx_clk_sel = { .name = "npcm7xx-clock-sel", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(clock_in, NPCM7xxClockSELState, NPCM7XX_CLK_SEL_MAX_INPUT, 0, vmstate_clock, Clock), VMSTATE_END_OF_LIST(), @@ -997,7 +997,7 @@ static const VMStateDescription vmstate_npcm7xx_clk_divider = { .name = "npcm7xx-clock-divider", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CLOCK(clock_in, NPCM7xxClockDividerState), VMSTATE_END_OF_LIST(), }, @@ -1008,7 +1008,7 @@ static const VMStateDescription vmstate_npcm7xx_clk = { .version_id = 1, .minimum_version_id = 1, .post_load = npcm7xx_clk_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS), VMSTATE_INT64(ref_ns, NPCM7xxCLKState), VMSTATE_CLOCK(clkref, NPCM7xxCLKState), diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c index eace9e1967..9252f9d148 100644 --- a/hw/misc/npcm7xx_gcr.c +++ b/hw/misc/npcm7xx_gcr.c @@ -227,7 +227,7 @@ static const VMStateDescription vmstate_npcm7xx_gcr = { .name = "npcm7xx-gcr", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, NPCM7xxGCRState, NPCM7XX_GCR_NR_REGS), VMSTATE_END_OF_LIST(), }, diff --git a/hw/misc/npcm7xx_mft.c b/hw/misc/npcm7xx_mft.c index a30583a1b0..9a848584e1 100644 --- a/hw/misc/npcm7xx_mft.c +++ b/hw/misc/npcm7xx_mft.c @@ -503,7 +503,7 @@ static const VMStateDescription vmstate_npcm7xx_mft = { .name = "npcm7xx-mft-module", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CLOCK(clock_in, NPCM7xxMFTState), VMSTATE_CLOCK(clock_1, NPCM7xxMFTState), VMSTATE_CLOCK(clock_2, NPCM7xxMFTState), diff --git a/hw/misc/npcm7xx_pwm.c b/hw/misc/npcm7xx_pwm.c index 2be5bd25c6..fca2dd2e5a 100644 --- a/hw/misc/npcm7xx_pwm.c +++ b/hw/misc/npcm7xx_pwm.c @@ -511,7 +511,7 @@ static const VMStateDescription vmstate_npcm7xx_pwm = { .name = "npcm7xx-pwm", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(running, NPCM7xxPWM), VMSTATE_BOOL(inverted, NPCM7xxPWM), VMSTATE_UINT8(index, NPCM7xxPWM), @@ -529,7 +529,7 @@ static const VMStateDescription vmstate_npcm7xx_pwm_module = { .name = "npcm7xx-pwm-module", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CLOCK(clock, NPCM7xxPWMState), VMSTATE_STRUCT_ARRAY(pwm, NPCM7xxPWMState, NPCM7XX_PWM_PER_MODULE, 0, vmstate_npcm7xx_pwm, diff --git a/hw/misc/npcm7xx_rng.c b/hw/misc/npcm7xx_rng.c index b01df7cdb2..7f7e5eca62 100644 --- a/hw/misc/npcm7xx_rng.c +++ b/hw/misc/npcm7xx_rng.c @@ -150,7 +150,7 @@ static const VMStateDescription vmstate_npcm7xx_rng = { .name = "npcm7xx-rng", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(rngcs, NPCM7xxRNGState), VMSTATE_UINT8(rngd, NPCM7xxRNGState), VMSTATE_UINT8(rngmode, NPCM7xxRNGState), diff --git a/hw/misc/nrf51_rng.c b/hw/misc/nrf51_rng.c index fc86e1b697..2d76c45718 100644 --- a/hw/misc/nrf51_rng.c +++ b/hw/misc/nrf51_rng.c @@ -231,7 +231,7 @@ static const VMStateDescription vmstate_rng = { .name = "nrf51_soc.rng", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(active, NRF51RNGState), VMSTATE_UINT32(event_valrdy, NRF51RNGState), VMSTATE_UINT32(shortcut_stop_on_valrdy, NRF51RNGState), diff --git a/hw/misc/omap_gpmc.c b/hw/misc/omap_gpmc.c index 10de7a5523..67158eb164 100644 --- a/hw/misc/omap_gpmc.c +++ b/hw/misc/omap_gpmc.c @@ -126,7 +126,7 @@ static void omap_gpmc_dma_update(struct omap_gpmc_s *s, int value) static uint64_t omap_nand_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_gpmc_cs_file_s *f = (struct omap_gpmc_cs_file_s *)opaque; + struct omap_gpmc_cs_file_s *f = opaque; uint64_t v; nand_setpins(f->dev, 0, 0, 0, 1, 0); switch (omap_gpmc_devsize(f)) { @@ -205,7 +205,7 @@ static void omap_nand_setio(DeviceState *dev, uint64_t value, static void omap_nand_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_gpmc_cs_file_s *f = (struct omap_gpmc_cs_file_s *)opaque; + struct omap_gpmc_cs_file_s *f = opaque; nand_setpins(f->dev, 0, 0, 0, 1, 0); omap_nand_setio(f->dev, value, omap_gpmc_devsize(f), size); } @@ -290,7 +290,7 @@ static void fill_prefetch_fifo(struct omap_gpmc_s *s) static uint64_t omap_gpmc_prefetch_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; + struct omap_gpmc_s *s = opaque; uint32_t data; if (s->prefetch.config1 & 1) { /* The TRM doesn't define the behaviour if you read from the @@ -320,7 +320,7 @@ static uint64_t omap_gpmc_prefetch_read(void *opaque, hwaddr addr, static void omap_gpmc_prefetch_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; + struct omap_gpmc_s *s = opaque; int cs = prefetch_cs(s->prefetch.config1); if ((s->prefetch.config1 & 1) == 0) { /* The TRM doesn't define the behaviour of writing to the @@ -509,7 +509,7 @@ static int gpmc_wordaccess_only(hwaddr addr) static uint64_t omap_gpmc_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; + struct omap_gpmc_s *s = opaque; int cs; struct omap_gpmc_cs_file_s *f; @@ -621,7 +621,7 @@ static uint64_t omap_gpmc_read(void *opaque, hwaddr addr, static void omap_gpmc_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; + struct omap_gpmc_s *s = opaque; int cs; struct omap_gpmc_cs_file_s *f; diff --git a/hw/misc/omap_l4.c b/hw/misc/omap_l4.c index 54aeaecd69..b7875489da 100644 --- a/hw/misc/omap_l4.c +++ b/hw/misc/omap_l4.c @@ -52,10 +52,9 @@ hwaddr omap_l4_region_size(struct omap_target_agent_s *ta, return ta->start[region].size; } -static uint64_t omap_l4ta_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_l4ta_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque; + struct omap_target_agent_s *s = opaque; if (size != 2) { return omap_badwidth_read16(opaque, addr); @@ -79,7 +78,7 @@ static uint64_t omap_l4ta_read(void *opaque, hwaddr addr, static void omap_l4ta_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque; + struct omap_target_agent_s *s = opaque; if (size != 4) { omap_badwidth_write32(opaque, addr, value); diff --git a/hw/misc/omap_sdrc.c b/hw/misc/omap_sdrc.c index f2f72f6810..6aa1b3ef7f 100644 --- a/hw/misc/omap_sdrc.c +++ b/hw/misc/omap_sdrc.c @@ -31,10 +31,9 @@ void omap_sdrc_reset(struct omap_sdrc_s *s) s->config = 0x10; } -static uint64_t omap_sdrc_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_sdrc_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque; + struct omap_sdrc_s *s = opaque; if (size != 4) { return omap_badwidth_read32(opaque, addr); @@ -89,7 +88,7 @@ static uint64_t omap_sdrc_read(void *opaque, hwaddr addr, static void omap_sdrc_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque; + struct omap_sdrc_s *s = opaque; if (size != 4) { omap_badwidth_write32(opaque, addr, value); diff --git a/hw/misc/omap_tap.c b/hw/misc/omap_tap.c index 3f595e8df7..4d7fb7d85f 100644 --- a/hw/misc/omap_tap.c +++ b/hw/misc/omap_tap.c @@ -23,10 +23,9 @@ #include "hw/arm/omap.h" /* TEST-Chip-level TAP */ -static uint64_t omap_tap_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_tap_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + struct omap_mpu_state_s *s = opaque; if (size != 4) { return omap_badwidth_read32(opaque, addr); diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index 03845c8de3..acedd0f82b 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -19,7 +19,7 @@ */ #include "qemu/osdep.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "qemu/event_notifier.h" #include "qemu/module.h" @@ -245,7 +245,6 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp) uint8_t *pci_conf; char *name; int r, i; - bool fastmmio = kvm_ioeventfd_any_length_enabled(); pci_conf = pci_dev->config; @@ -279,7 +278,7 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp) g_free(name); test->hdr->offset = cpu_to_le32(IOTEST_SIZE(i) + i * IOTEST_ACCESS_WIDTH); test->match_data = strcmp(IOTEST_TEST(i), "wildcard-eventfd"); - if (fastmmio && IOTEST_IS_MEM(i) && !test->match_data) { + if (IOTEST_IS_MEM(i) && !test->match_data) { test->size = 0; } else { test->size = IOTEST_ACCESS_WIDTH; diff --git a/hw/misc/pvpanic-isa.c b/hw/misc/pvpanic-isa.c index b84d4d458d..ccec50f61b 100644 --- a/hw/misc/pvpanic-isa.c +++ b/hw/misc/pvpanic-isa.c @@ -22,6 +22,7 @@ #include "qom/object.h" #include "hw/isa/isa.h" #include "standard-headers/linux/pvpanic.h" +#include "hw/acpi/acpi_aml_interface.h" OBJECT_DECLARE_SIMPLE_TYPE(PVPanicISAState, PVPANIC_ISA_DEVICE) @@ -63,6 +64,41 @@ static void pvpanic_isa_realizefn(DeviceState *dev, Error **errp) isa_register_ioport(d, &ps->mr, s->ioport); } +static void build_pvpanic_isa_aml(AcpiDevAmlIf *adev, Aml *scope) +{ + Aml *crs, *field, *method; + PVPanicISAState *s = PVPANIC_ISA_DEVICE(adev); + Aml *dev = aml_device("PEVT"); + + aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0001"))); + + crs = aml_resource_template(); + aml_append(crs, + aml_io(AML_DECODE16, s->ioport, s->ioport, 1, 1) + ); + aml_append(dev, aml_name_decl("_CRS", crs)); + + aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO, + aml_int(s->ioport), 1)); + field = aml_field("PEOR", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("PEPT", 8)); + aml_append(dev, field); + + /* device present, functioning, decoding, shown in UI */ + aml_append(dev, aml_name_decl("_STA", aml_int(0xF))); + + method = aml_method("RDPT", 0, AML_NOTSERIALIZED); + aml_append(method, aml_store(aml_name("PEPT"), aml_local(0))); + aml_append(method, aml_return(aml_local(0))); + aml_append(dev, method); + + method = aml_method("WRPT", 1, AML_NOTSERIALIZED); + aml_append(method, aml_store(aml_arg(0), aml_name("PEPT"))); + aml_append(dev, method); + + aml_append(scope, dev); +} + static Property pvpanic_isa_properties[] = { DEFINE_PROP_UINT16(PVPANIC_IOPORT_PROP, PVPanicISAState, ioport, 0x505), DEFINE_PROP_UINT8("events", PVPanicISAState, pvpanic.events, @@ -73,10 +109,12 @@ static Property pvpanic_isa_properties[] = { static void pvpanic_isa_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); dc->realize = pvpanic_isa_realizefn; device_class_set_props(dc, pvpanic_isa_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + adevc->build_dev_aml = build_pvpanic_isa_aml; } static const TypeInfo pvpanic_isa_info = { @@ -85,6 +123,10 @@ static const TypeInfo pvpanic_isa_info = { .instance_size = sizeof(PVPanicISAState), .instance_init = pvpanic_isa_initfn, .class_init = pvpanic_isa_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_ACPI_DEV_AML_IF }, + { }, + }, }; static void pvpanic_register_types(void) diff --git a/hw/misc/pvpanic-pci.c b/hw/misc/pvpanic-pci.c index 99cf7e2041..83be95d0d2 100644 --- a/hw/misc/pvpanic-pci.c +++ b/hw/misc/pvpanic-pci.c @@ -20,7 +20,7 @@ #include "migration/vmstate.h" #include "hw/misc/pvpanic.h" #include "qom/object.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "standard-headers/linux/pvpanic.h" OBJECT_DECLARE_SIMPLE_TYPE(PVPanicPCIState, PVPANIC_PCI_DEVICE) @@ -37,7 +37,7 @@ static const VMStateDescription vmstate_pvpanic_pci = { .name = "pvpanic-pci", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, PVPanicPCIState), VMSTATE_END_OF_LIST() } @@ -48,7 +48,7 @@ static void pvpanic_pci_realizefn(PCIDevice *dev, Error **errp) PVPanicPCIState *s = PVPANIC_PCI_DEVICE(dev); PVPanicState *ps = &s->pvpanic; - pvpanic_setup_io(&s->pvpanic, DEVICE(s), 2); + pvpanic_setup_io(ps, DEVICE(s), 2); pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &ps->mr); } diff --git a/hw/misc/sbsa_ec.c b/hw/misc/sbsa_ec.c index 8d939fe31b..86b23a5372 100644 --- a/hw/misc/sbsa_ec.c +++ b/hw/misc/sbsa_ec.c @@ -15,13 +15,13 @@ #include "hw/sysbus.h" #include "sysemu/runstate.h" -typedef struct { +typedef struct SECUREECState { SysBusDevice parent_obj; MemoryRegion iomem; } SECUREECState; -#define TYPE_SBSA_EC "sbsa-ec" -#define SECURE_EC(obj) OBJECT_CHECK(SECUREECState, (obj), TYPE_SBSA_EC) +#define TYPE_SBSA_SECURE_EC "sbsa-ec" +OBJECT_DECLARE_SIMPLE_TYPE(SECUREECState, SBSA_SECURE_EC) enum sbsa_ec_powerstates { SBSA_EC_CMD_POWEROFF = 0x01, @@ -36,7 +36,7 @@ static uint64_t sbsa_ec_read(void *opaque, hwaddr offset, unsigned size) } static void sbsa_ec_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) + uint64_t value, unsigned size) { if (offset == 0) { /* PSCI machine power command register */ switch (value) { @@ -65,7 +65,7 @@ static const MemoryRegionOps sbsa_ec_ops = { static void sbsa_ec_init(Object *obj) { - SECUREECState *s = SECURE_EC(obj); + SECUREECState *s = SBSA_SECURE_EC(obj); SysBusDevice *dev = SYS_BUS_DEVICE(obj); memory_region_init_io(&s->iomem, obj, &sbsa_ec_ops, s, "sbsa-ec", @@ -82,7 +82,7 @@ static void sbsa_ec_class_init(ObjectClass *klass, void *data) } static const TypeInfo sbsa_ec_info = { - .name = TYPE_SBSA_EC, + .name = TYPE_SBSA_SECURE_EC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SECUREECState), .instance_init = sbsa_ec_init, diff --git a/hw/misc/sga.c b/hw/misc/sga.c deleted file mode 100644 index 1d04672b01..0000000000 --- a/hw/misc/sga.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * QEMU dummy ISA device for loading sgabios option rom. - * - * Copyright (c) 2011 Glauber Costa, Red Hat Inc. - * - * 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. - * - * sgabios code originally available at code.google.com/p/sgabios - * - */ - -#include "qemu/osdep.h" -#include "hw/isa/isa.h" -#include "hw/loader.h" -#include "qemu/module.h" -#include "qom/object.h" -#include "qemu/error-report.h" - -#define SGABIOS_FILENAME "sgabios.bin" - -#define TYPE_SGA "sga" -OBJECT_DECLARE_SIMPLE_TYPE(ISASGAState, SGA) - -struct ISASGAState { - ISADevice parent_obj; -}; - -static void sga_realizefn(DeviceState *dev, Error **errp) -{ - warn_report("-device sga is deprecated, use -machine graphics=off"); - rom_add_vga(SGABIOS_FILENAME); -} - -static void sga_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->realize = sga_realizefn; - dc->desc = "Serial Graphics Adapter"; -} - -static const TypeInfo sga_info = { - .name = TYPE_SGA, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISASGAState), - .class_init = sga_class_initfn, -}; - -static void sga_register_types(void) -{ - type_register_static(&sga_info); -} - -type_init(sga_register_types) diff --git a/hw/misc/sifive_e_aon.c b/hw/misc/sifive_e_aon.c new file mode 100644 index 0000000000..4656457d0b --- /dev/null +++ b/hw/misc/sifive_e_aon.c @@ -0,0 +1,319 @@ +/* + * SiFive HiFive1 AON (Always On Domain) for QEMU. + * + * Copyright (c) 2022 SiFive, Inc. All rights reserved. + * + * 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/timer.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/registerfields.h" +#include "hw/misc/sifive_e_aon.h" +#include "qapi/visitor.h" +#include "qapi/error.h" +#include "sysemu/watchdog.h" +#include "hw/qdev-properties.h" + +REG32(AON_WDT_WDOGCFG, 0x0) + FIELD(AON_WDT_WDOGCFG, SCALE, 0, 4) + FIELD(AON_WDT_WDOGCFG, RSVD0, 4, 4) + FIELD(AON_WDT_WDOGCFG, RSTEN, 8, 1) + FIELD(AON_WDT_WDOGCFG, ZEROCMP, 9, 1) + FIELD(AON_WDT_WDOGCFG, RSVD1, 10, 2) + FIELD(AON_WDT_WDOGCFG, EN_ALWAYS, 12, 1) + FIELD(AON_WDT_WDOGCFG, EN_CORE_AWAKE, 13, 1) + FIELD(AON_WDT_WDOGCFG, RSVD2, 14, 14) + FIELD(AON_WDT_WDOGCFG, IP0, 28, 1) + FIELD(AON_WDT_WDOGCFG, RSVD3, 29, 3) +REG32(AON_WDT_WDOGCOUNT, 0x8) + FIELD(AON_WDT_WDOGCOUNT, VALUE, 0, 31) +REG32(AON_WDT_WDOGS, 0x10) +REG32(AON_WDT_WDOGFEED, 0x18) +REG32(AON_WDT_WDOGKEY, 0x1c) +REG32(AON_WDT_WDOGCMP0, 0x20) + +static void sifive_e_aon_wdt_update_wdogcount(SiFiveEAONState *r) +{ + int64_t now; + if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS) == 0 && + FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE) == 0) { + return; + } + + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + r->wdogcount += muldiv64(now - r->wdog_restart_time, + r->wdogclk_freq, NANOSECONDS_PER_SECOND); + + /* Clean the most significant bit. */ + r->wdogcount &= R_AON_WDT_WDOGCOUNT_VALUE_MASK; + r->wdog_restart_time = now; +} + +static void sifive_e_aon_wdt_update_state(SiFiveEAONState *r) +{ + uint16_t wdogs; + bool cmp_signal = false; + sifive_e_aon_wdt_update_wdogcount(r); + wdogs = (uint16_t)(r->wdogcount >> + FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, SCALE)); + + if (wdogs >= r->wdogcmp0) { + cmp_signal = true; + if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, ZEROCMP) == 1) { + r->wdogcount = 0; + wdogs = 0; + } + } + + if (cmp_signal) { + if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, RSTEN) == 1) { + watchdog_perform_action(); + } + r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, IP0, 1); + } + + qemu_set_irq(r->wdog_irq, FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, IP0)); + + if (wdogs < r->wdogcmp0 && + (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS) == 1 || + FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE) == 1)) { + int64_t next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + next += muldiv64((r->wdogcmp0 - wdogs) << + FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, SCALE), + NANOSECONDS_PER_SECOND, r->wdogclk_freq); + timer_mod(r->wdog_timer, next); + } else { + timer_mod(r->wdog_timer, INT64_MAX); + } +} + +/* + * Callback used when the timer set using timer_mod expires. + */ +static void sifive_e_aon_wdt_expired_cb(void *opaque) +{ + SiFiveEAONState *r = SIFIVE_E_AON(opaque); + sifive_e_aon_wdt_update_state(r); +} + +static uint64_t +sifive_e_aon_wdt_read(void *opaque, hwaddr addr, unsigned int size) +{ + SiFiveEAONState *r = SIFIVE_E_AON(opaque); + + switch (addr) { + case A_AON_WDT_WDOGCFG: + return r->wdogcfg; + case A_AON_WDT_WDOGCOUNT: + sifive_e_aon_wdt_update_wdogcount(r); + return r->wdogcount; + case A_AON_WDT_WDOGS: + sifive_e_aon_wdt_update_wdogcount(r); + return r->wdogcount >> + FIELD_EX32(r->wdogcfg, + AON_WDT_WDOGCFG, + SCALE); + case A_AON_WDT_WDOGFEED: + return 0; + case A_AON_WDT_WDOGKEY: + return r->wdogunlock; + case A_AON_WDT_WDOGCMP0: + return r->wdogcmp0; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n", + __func__, (int)addr); + } + + return 0; +} + +static void +sifive_e_aon_wdt_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + SiFiveEAONState *r = SIFIVE_E_AON(opaque); + uint32_t value = val64; + + switch (addr) { + case A_AON_WDT_WDOGCFG: { + uint8_t new_en_always; + uint8_t new_en_core_awake; + uint8_t old_en_always; + uint8_t old_en_core_awake; + if (r->wdogunlock == 0) { + return; + } + + new_en_always = FIELD_EX32(value, AON_WDT_WDOGCFG, EN_ALWAYS); + new_en_core_awake = FIELD_EX32(value, AON_WDT_WDOGCFG, EN_CORE_AWAKE); + old_en_always = FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS); + old_en_core_awake = FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, + EN_CORE_AWAKE); + + if ((old_en_always || + old_en_core_awake) == 1 && + (new_en_always || + new_en_core_awake) == 0) { + sifive_e_aon_wdt_update_wdogcount(r); + } else if ((old_en_always || + old_en_core_awake) == 0 && + (new_en_always || + new_en_core_awake) == 1) { + r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + } + r->wdogcfg = value; + r->wdogunlock = 0; + break; + } + case A_AON_WDT_WDOGCOUNT: + if (r->wdogunlock == 0) { + return; + } + r->wdogcount = value & R_AON_WDT_WDOGCOUNT_VALUE_MASK; + r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + r->wdogunlock = 0; + break; + case A_AON_WDT_WDOGS: + return; + case A_AON_WDT_WDOGFEED: + if (r->wdogunlock == 0) { + return; + } + if (value == SIFIVE_E_AON_WDOGFEED) { + r->wdogcount = 0; + r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + } + r->wdogunlock = 0; + break; + case A_AON_WDT_WDOGKEY: + if (value == SIFIVE_E_AON_WDOGKEY) { + r->wdogunlock = 1; + } + break; + case A_AON_WDT_WDOGCMP0: + if (r->wdogunlock == 0) { + return; + } + r->wdogcmp0 = (uint16_t) value; + r->wdogunlock = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n", + __func__, (int)addr, (int)value); + } + sifive_e_aon_wdt_update_state(r); +} + +static uint64_t +sifive_e_aon_read(void *opaque, hwaddr addr, unsigned int size) +{ + if (addr < SIFIVE_E_AON_RTC) { + return sifive_e_aon_wdt_read(opaque, addr, size); + } else if (addr < SIFIVE_E_AON_MAX) { + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented read: addr=0x%x\n", + __func__, (int)addr); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n", + __func__, (int)addr); + } + return 0; +} + +static void +sifive_e_aon_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + if (addr < SIFIVE_E_AON_RTC) { + sifive_e_aon_wdt_write(opaque, addr, val64, size); + } else if (addr < SIFIVE_E_AON_MAX) { + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented write: addr=0x%x\n", + __func__, (int)addr); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x\n", + __func__, (int)addr); + } +} + +static const MemoryRegionOps sifive_e_aon_ops = { + .read = sifive_e_aon_read, + .write = sifive_e_aon_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4 + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void sifive_e_aon_reset(DeviceState *dev) +{ + SiFiveEAONState *r = SIFIVE_E_AON(dev); + + r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, RSTEN, 0); + r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS, 0); + r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE, 0); + r->wdogcmp0 = 0xbeef; + + sifive_e_aon_wdt_update_state(r); +} + +static void sifive_e_aon_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + SiFiveEAONState *r = SIFIVE_E_AON(obj); + + memory_region_init_io(&r->mmio, OBJECT(r), &sifive_e_aon_ops, r, + TYPE_SIFIVE_E_AON, SIFIVE_E_AON_MAX); + sysbus_init_mmio(sbd, &r->mmio); + + /* watchdog timer */ + r->wdog_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + sifive_e_aon_wdt_expired_cb, r); + r->wdogclk_freq = SIFIVE_E_LFCLK_DEFAULT_FREQ; + sysbus_init_irq(sbd, &r->wdog_irq); +} + +static Property sifive_e_aon_properties[] = { + DEFINE_PROP_UINT64("wdogclk-frequency", SiFiveEAONState, wdogclk_freq, + SIFIVE_E_LFCLK_DEFAULT_FREQ), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sifive_e_aon_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->reset = sifive_e_aon_reset; + device_class_set_props(dc, sifive_e_aon_properties); +} + +static const TypeInfo sifive_e_aon_info = { + .name = TYPE_SIFIVE_E_AON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFiveEAONState), + .instance_init = sifive_e_aon_init, + .class_init = sifive_e_aon_class_init, +}; + +static void sifive_e_aon_register_types(void) +{ + type_register_static(&sifive_e_aon_info); +} + +type_init(sifive_e_aon_register_types) diff --git a/hw/misc/sifive_test.c b/hw/misc/sifive_test.c index 56df45bfe5..ad688079c4 100644 --- a/hw/misc/sifive_test.c +++ b/hw/misc/sifive_test.c @@ -25,6 +25,7 @@ #include "qemu/module.h" #include "sysemu/runstate.h" #include "hw/misc/sifive_test.h" +#include "sysemu/sysemu.h" static uint64_t sifive_test_read(void *opaque, hwaddr addr, unsigned int size) { @@ -39,9 +40,13 @@ static void sifive_test_write(void *opaque, hwaddr addr, int code = (val64 >> 16) & 0xffff; switch (status) { case FINISHER_FAIL: - exit(code); + qemu_system_shutdown_request_with_code( + SHUTDOWN_CAUSE_GUEST_PANIC, code); + return; case FINISHER_PASS: - exit(0); + qemu_system_shutdown_request_with_code( + SHUTDOWN_CAUSE_GUEST_SHUTDOWN, code); + return; case FINISHER_RESET: qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); return; diff --git a/hw/misc/sifive_u_otp.c b/hw/misc/sifive_u_otp.c index 6d5f84e6c2..8965f5c22a 100644 --- a/hw/misc/sifive_u_otp.c +++ b/hw/misc/sifive_u_otp.c @@ -64,8 +64,8 @@ static uint64_t sifive_u_otp_read(void *opaque, hwaddr addr, unsigned int size) if (s->blk) { int32_t buf; - if (blk_pread(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD, &buf, - SIFIVE_U_OTP_FUSE_WORD) < 0) { + if (blk_pread(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD, + SIFIVE_U_OTP_FUSE_WORD, &buf, 0) < 0) { error_report("read error index<%d>", s->pa); return 0xff; } @@ -167,8 +167,8 @@ static void sifive_u_otp_write(void *opaque, hwaddr addr, /* write to backend */ if (s->blk) { if (blk_pwrite(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD, - &s->fuse[s->pa], SIFIVE_U_OTP_FUSE_WORD, - 0) < 0) { + SIFIVE_U_OTP_FUSE_WORD, &s->fuse[s->pa], 0) + < 0) { error_report("write error index<%d>", s->pa); } } @@ -210,13 +210,6 @@ static void sifive_u_otp_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); dinfo = drive_get(IF_PFLASH, 0, 0); - if (!dinfo) { - dinfo = drive_get(IF_NONE, 0, 0); - if (dinfo) { - warn_report("using \"-drive if=none\" for the OTP is deprecated, " - "use \"-drive if=pflash\" instead."); - } - } if (dinfo) { int ret; uint64_t perm; @@ -240,7 +233,7 @@ static void sifive_u_otp_realize(DeviceState *dev, Error **errp) return; } - if (blk_pread(s->blk, 0, s->fuse, filesize) != filesize) { + if (blk_pread(s->blk, 0, filesize, s->fuse, 0) < 0) { error_setg(errp, "failed to read the initial flash content"); return; } @@ -261,14 +254,14 @@ static void sifive_u_otp_realize(DeviceState *dev, Error **errp) serial_data = s->serial; if (blk_pwrite(s->blk, index * SIFIVE_U_OTP_FUSE_WORD, - &serial_data, SIFIVE_U_OTP_FUSE_WORD, 0) < 0) { + SIFIVE_U_OTP_FUSE_WORD, &serial_data, 0) < 0) { error_setg(errp, "failed to write index<%d>", index); return; } serial_data = ~(s->serial); if (blk_pwrite(s->blk, (index + 1) * SIFIVE_U_OTP_FUSE_WORD, - &serial_data, SIFIVE_U_OTP_FUSE_WORD, 0) < 0) { + SIFIVE_U_OTP_FUSE_WORD, &serial_data, 0) < 0) { error_setg(errp, "failed to write index<%d>", index + 1); return; } diff --git a/hw/misc/slavio_misc.c b/hw/misc/slavio_misc.c index e8eb71570a..94369e4cc8 100644 --- a/hw/misc/slavio_misc.c +++ b/hw/misc/slavio_misc.c @@ -408,7 +408,7 @@ static const VMStateDescription vmstate_misc = { .name ="slavio_misc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(dummy, MiscState), VMSTATE_UINT8(config, MiscState), VMSTATE_UINT8(aux1, MiscState), diff --git a/hw/misc/stm32f2xx_syscfg.c b/hw/misc/stm32f2xx_syscfg.c index 04c22c2850..19c1e86424 100644 --- a/hw/misc/stm32f2xx_syscfg.c +++ b/hw/misc/stm32f2xx_syscfg.c @@ -94,12 +94,12 @@ static void stm32f2xx_syscfg_write(void *opaque, hwaddr addr, switch (addr) { case SYSCFG_MEMRMP: qemu_log_mask(LOG_UNIMP, - "%s: Changeing the memory mapping isn't supported " \ + "%s: Changing the memory mapping isn't supported " \ "in QEMU\n", __func__); return; case SYSCFG_PMC: qemu_log_mask(LOG_UNIMP, - "%s: Changeing the memory mapping isn't supported " \ + "%s: Changing the memory mapping isn't supported " \ "in QEMU\n", __func__); return; case SYSCFG_EXTICR1: diff --git a/hw/misc/stm32f4xx_exti.c b/hw/misc/stm32f4xx_exti.c index 02e7810046..7bd3afcd7c 100644 --- a/hw/misc/stm32f4xx_exti.c +++ b/hw/misc/stm32f4xx_exti.c @@ -153,7 +153,7 @@ static const VMStateDescription vmstate_stm32f4xx_exti = { .name = TYPE_STM32F4XX_EXTI, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(exti_imr, STM32F4xxExtiState), VMSTATE_UINT32(exti_emr, STM32F4xxExtiState), VMSTATE_UINT32(exti_rtsr, STM32F4xxExtiState), diff --git a/hw/misc/stm32f4xx_syscfg.c b/hw/misc/stm32f4xx_syscfg.c index f960e4ea1e..854fce6a95 100644 --- a/hw/misc/stm32f4xx_syscfg.c +++ b/hw/misc/stm32f4xx_syscfg.c @@ -137,7 +137,7 @@ static const VMStateDescription vmstate_stm32f4xx_syscfg = { .name = TYPE_STM32F4XX_SYSCFG, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(syscfg_memrmp, STM32F4xxSyscfgState), VMSTATE_UINT32(syscfg_pmc, STM32F4xxSyscfgState), VMSTATE_UINT32_ARRAY(syscfg_exticr, STM32F4xxSyscfgState, diff --git a/hw/misc/stm32l4x5_exti.c b/hw/misc/stm32l4x5_exti.c new file mode 100644 index 0000000000..9fd859160d --- /dev/null +++ b/hw/misc/stm32l4x5_exti.c @@ -0,0 +1,290 @@ +/* + * STM32L4x5 EXTI (Extended interrupts and events controller) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Samuel Tardieu + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This work is based on the stm32f4xx_exti by Alistair Francis. + * Original code is licensed under the MIT License: + * + * Copyright (c) 2014 Alistair Francis + */ + +/* + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "hw/misc/stm32l4x5_exti.h" + +#define EXTI_IMR1 0x00 +#define EXTI_EMR1 0x04 +#define EXTI_RTSR1 0x08 +#define EXTI_FTSR1 0x0C +#define EXTI_SWIER1 0x10 +#define EXTI_PR1 0x14 +#define EXTI_IMR2 0x20 +#define EXTI_EMR2 0x24 +#define EXTI_RTSR2 0x28 +#define EXTI_FTSR2 0x2C +#define EXTI_SWIER2 0x30 +#define EXTI_PR2 0x34 + +#define EXTI_NUM_GPIO_EVENT_IN_LINES 16 +#define EXTI_MAX_IRQ_PER_BANK 32 +#define EXTI_IRQS_BANK0 32 +#define EXTI_IRQS_BANK1 8 + +static const unsigned irqs_per_bank[EXTI_NUM_REGISTER] = { + EXTI_IRQS_BANK0, + EXTI_IRQS_BANK1, +}; + +static const uint32_t exti_romask[EXTI_NUM_REGISTER] = { + 0xff820000, /* 0b11111111_10000010_00000000_00000000 */ + 0x00000087, /* 0b00000000_00000000_00000000_10000111 */ +}; + +static unsigned regbank_index_by_irq(unsigned irq) +{ + return irq >= EXTI_MAX_IRQ_PER_BANK ? 1 : 0; +} + +static unsigned regbank_index_by_addr(hwaddr addr) +{ + return addr >= EXTI_IMR2 ? 1 : 0; +} + +static unsigned valid_mask(unsigned bank) +{ + return MAKE_64BIT_MASK(0, irqs_per_bank[bank]); +} + +static unsigned configurable_mask(unsigned bank) +{ + return valid_mask(bank) & ~exti_romask[bank]; +} + +static void stm32l4x5_exti_reset_hold(Object *obj) +{ + Stm32l4x5ExtiState *s = STM32L4X5_EXTI(obj); + + for (unsigned bank = 0; bank < EXTI_NUM_REGISTER; bank++) { + s->imr[bank] = exti_romask[bank]; + s->emr[bank] = 0x00000000; + s->rtsr[bank] = 0x00000000; + s->ftsr[bank] = 0x00000000; + s->swier[bank] = 0x00000000; + s->pr[bank] = 0x00000000; + } +} + +static void stm32l4x5_exti_set_irq(void *opaque, int irq, int level) +{ + Stm32l4x5ExtiState *s = opaque; + const unsigned bank = regbank_index_by_irq(irq); + const int oirq = irq; + + trace_stm32l4x5_exti_set_irq(irq, level); + + /* Shift the value to enable access in x2 registers. */ + irq %= EXTI_MAX_IRQ_PER_BANK; + + /* If the interrupt is masked, pr won't be raised */ + if (!extract32(s->imr[bank], irq, 1)) { + return; + } + + if (((1 << irq) & s->rtsr[bank]) && level) { + /* Rising Edge */ + s->pr[bank] |= 1 << irq; + qemu_irq_pulse(s->irq[oirq]); + } else if (((1 << irq) & s->ftsr[bank]) && !level) { + /* Falling Edge */ + s->pr[bank] |= 1 << irq; + qemu_irq_pulse(s->irq[oirq]); + } + /* + * In the following situations : + * - falling edge but rising trigger selected + * - rising edge but falling trigger selected + * - no trigger selected + * No action is required + */ +} + +static uint64_t stm32l4x5_exti_read(void *opaque, hwaddr addr, + unsigned int size) +{ + Stm32l4x5ExtiState *s = opaque; + uint32_t r = 0; + const unsigned bank = regbank_index_by_addr(addr); + + switch (addr) { + case EXTI_IMR1: + case EXTI_IMR2: + r = s->imr[bank]; + break; + case EXTI_EMR1: + case EXTI_EMR2: + r = s->emr[bank]; + break; + case EXTI_RTSR1: + case EXTI_RTSR2: + r = s->rtsr[bank]; + break; + case EXTI_FTSR1: + case EXTI_FTSR2: + r = s->ftsr[bank]; + break; + case EXTI_SWIER1: + case EXTI_SWIER2: + r = s->swier[bank]; + break; + case EXTI_PR1: + case EXTI_PR2: + r = s->pr[bank]; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "STM32L4X5_exti_read: Bad offset 0x%" HWADDR_PRIx "\n", + addr); + break; + } + + trace_stm32l4x5_exti_read(addr, r); + + return r; +} + +static void stm32l4x5_exti_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + Stm32l4x5ExtiState *s = opaque; + const unsigned bank = regbank_index_by_addr(addr); + + trace_stm32l4x5_exti_write(addr, val64); + + switch (addr) { + case EXTI_IMR1: + case EXTI_IMR2: + s->imr[bank] = val64 & valid_mask(bank); + return; + case EXTI_EMR1: + case EXTI_EMR2: + s->emr[bank] = val64 & valid_mask(bank); + return; + case EXTI_RTSR1: + case EXTI_RTSR2: + s->rtsr[bank] = val64 & configurable_mask(bank); + return; + case EXTI_FTSR1: + case EXTI_FTSR2: + s->ftsr[bank] = val64 & configurable_mask(bank); + return; + case EXTI_SWIER1: + case EXTI_SWIER2: { + const uint32_t set = val64 & configurable_mask(bank); + const uint32_t pend = set & ~s->swier[bank] & s->imr[bank] & + ~s->pr[bank]; + s->swier[bank] = set; + s->pr[bank] |= pend; + for (unsigned i = 0; i < irqs_per_bank[bank]; i++) { + if (extract32(pend, i, 1)) { + qemu_irq_pulse(s->irq[i + 32 * bank]); + } + } + return; + } + case EXTI_PR1: + case EXTI_PR2: { + const uint32_t cleared = s->pr[bank] & val64 & configurable_mask(bank); + /* This bit is cleared by writing a 1 to it */ + s->pr[bank] &= ~cleared; + /* Software triggered interrupts are cleared as well */ + s->swier[bank] &= ~cleared; + return; + } + default: + qemu_log_mask(LOG_GUEST_ERROR, + "STM32L4X5_exti_write: Bad offset 0x%" HWADDR_PRIx "\n", + addr); + } +} + +static const MemoryRegionOps stm32l4x5_exti_ops = { + .read = stm32l4x5_exti_read, + .write = stm32l4x5_exti_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .impl.unaligned = false, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static void stm32l4x5_exti_init(Object *obj) +{ + Stm32l4x5ExtiState *s = STM32L4X5_EXTI(obj); + + for (size_t i = 0; i < EXTI_NUM_INTERRUPT_OUT_LINES; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq[i]); + } + + memory_region_init_io(&s->mmio, obj, &stm32l4x5_exti_ops, s, + TYPE_STM32L4X5_EXTI, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + qdev_init_gpio_in(DEVICE(obj), stm32l4x5_exti_set_irq, + EXTI_NUM_GPIO_EVENT_IN_LINES); +} + +static const VMStateDescription vmstate_stm32l4x5_exti = { + .name = TYPE_STM32L4X5_EXTI, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(imr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), + VMSTATE_UINT32_ARRAY(emr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), + VMSTATE_UINT32_ARRAY(rtsr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), + VMSTATE_UINT32_ARRAY(ftsr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), + VMSTATE_UINT32_ARRAY(swier, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), + VMSTATE_UINT32_ARRAY(pr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), + VMSTATE_END_OF_LIST() + } +}; + +static void stm32l4x5_exti_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->vmsd = &vmstate_stm32l4x5_exti; + rc->phases.hold = stm32l4x5_exti_reset_hold; +} + +static const TypeInfo stm32l4x5_exti_types[] = { + { + .name = TYPE_STM32L4X5_EXTI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Stm32l4x5ExtiState), + .instance_init = stm32l4x5_exti_init, + .class_init = stm32l4x5_exti_class_init, + } +}; + +DEFINE_TYPES(stm32l4x5_exti_types) diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c new file mode 100644 index 0000000000..ed2dbd9dc3 --- /dev/null +++ b/hw/misc/stm32l4x5_rcc.c @@ -0,0 +1,1462 @@ +/* + * STM32L4X5 RCC (Reset and clock control) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * + * Inspired by the BCM2835 CPRMAN clock manager implementation by Luc Michel. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "hw/misc/stm32l4x5_rcc.h" +#include "hw/misc/stm32l4x5_rcc_internals.h" +#include "hw/clock.h" +#include "hw/irq.h" +#include "hw/qdev-clock.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/registerfields.h" +#include "trace.h" + +#define HSE_DEFAULT_FRQ 48000000ULL +#define HSI_FRQ 16000000ULL +#define MSI_DEFAULT_FRQ 4000000ULL +#define LSE_FRQ 32768ULL +#define LSI_FRQ 32000ULL + +/* + * Function to simply acknowledge and propagate changes in a clock mux + * frequency. + * `bypass_source` allows to bypass the period of the current source and just + * consider it equal to 0. This is useful during the hold phase of reset. + */ +static void clock_mux_update(RccClockMuxState *mux, bool bypass_source) +{ + uint64_t src_freq; + Clock *current_source = mux->srcs[mux->src]; + uint32_t freq_multiplier = 0; + bool clk_changed = false; + + /* + * To avoid rounding errors, we use the clock period instead of the + * frequency. + * This means that the multiplier of the mux becomes the divider of + * the clock and the divider of the mux becomes the multiplier of the + * clock. + */ + if (!bypass_source && mux->enabled && mux->divider) { + freq_multiplier = mux->divider; + } + + clk_changed |= clock_set_mul_div(mux->out, freq_multiplier, mux->multiplier); + clk_changed |= clock_set(mux->out, clock_get(current_source)); + if (clk_changed) { + clock_propagate(mux->out); + } + + src_freq = clock_get_hz(current_source); + /* TODO: can we simply detect if the config changed so that we reduce log spam ? */ + trace_stm32l4x5_rcc_mux_update(mux->id, mux->src, src_freq, + mux->multiplier, mux->divider); +} + +static void clock_mux_src_update(void *opaque, ClockEvent event) +{ + RccClockMuxState **backref = opaque; + RccClockMuxState *s = *backref; + /* + * The backref value is equal to: + * s->backref + (sizeof(RccClockMuxState *) * update_src). + * By subtracting we can get back the index of the updated clock. + */ + const uint32_t update_src = backref - s->backref; + /* Only update if the clock that was updated is the current source */ + if (update_src == s->src) { + clock_mux_update(s, false); + } +} + +static void clock_mux_init(Object *obj) +{ + RccClockMuxState *s = RCC_CLOCK_MUX(obj); + size_t i; + + for (i = 0; i < RCC_NUM_CLOCK_MUX_SRC; i++) { + char *name = g_strdup_printf("srcs[%zu]", i); + s->backref[i] = s; + s->srcs[i] = qdev_init_clock_in(DEVICE(s), name, + clock_mux_src_update, + &s->backref[i], + ClockUpdate); + g_free(name); + } + + s->out = qdev_init_clock_out(DEVICE(s), "out"); +} + +static void clock_mux_reset_enter(Object *obj, ResetType type) +{ + RccClockMuxState *s = RCC_CLOCK_MUX(obj); + set_clock_mux_init_info(s, s->id); +} + +static void clock_mux_reset_hold(Object *obj) +{ + RccClockMuxState *s = RCC_CLOCK_MUX(obj); + clock_mux_update(s, true); +} + +static void clock_mux_reset_exit(Object *obj) +{ + RccClockMuxState *s = RCC_CLOCK_MUX(obj); + clock_mux_update(s, false); +} + +static const VMStateDescription clock_mux_vmstate = { + .name = TYPE_RCC_CLOCK_MUX, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(id, RccClockMuxState), + VMSTATE_ARRAY_CLOCK(srcs, RccClockMuxState, + RCC_NUM_CLOCK_MUX_SRC), + VMSTATE_BOOL(enabled, RccClockMuxState), + VMSTATE_UINT32(src, RccClockMuxState), + VMSTATE_UINT32(multiplier, RccClockMuxState), + VMSTATE_UINT32(divider, RccClockMuxState), + VMSTATE_END_OF_LIST() + } +}; + +static void clock_mux_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.enter = clock_mux_reset_enter; + rc->phases.hold = clock_mux_reset_hold; + rc->phases.exit = clock_mux_reset_exit; + dc->vmsd = &clock_mux_vmstate; +} + +static void clock_mux_set_enable(RccClockMuxState *mux, bool enabled) +{ + if (mux->enabled == enabled) { + return; + } + + if (enabled) { + trace_stm32l4x5_rcc_mux_enable(mux->id); + } else { + trace_stm32l4x5_rcc_mux_disable(mux->id); + } + + mux->enabled = enabled; + clock_mux_update(mux, false); +} + +static void clock_mux_set_factor(RccClockMuxState *mux, + uint32_t multiplier, uint32_t divider) +{ + if (mux->multiplier == multiplier && mux->divider == divider) { + return; + } + trace_stm32l4x5_rcc_mux_set_factor(mux->id, + mux->multiplier, multiplier, mux->divider, divider); + + mux->multiplier = multiplier; + mux->divider = divider; + clock_mux_update(mux, false); +} + +static void clock_mux_set_source(RccClockMuxState *mux, RccClockMuxSource src) +{ + if (mux->src == src) { + return; + } + + trace_stm32l4x5_rcc_mux_set_src(mux->id, mux->src, src); + mux->src = src; + clock_mux_update(mux, false); +} + +/* + * Acknowledge and propagate changes in a PLL frequency. + * `bypass_source` allows to bypass the period of the current source and just + * consider it equal to 0. This is useful during the hold phase of reset. + */ +static void pll_update(RccPllState *pll, bool bypass_source) +{ + uint64_t vco_freq, old_channel_freq, channel_freq; + int i; + + /* The common PLLM factor is handled by the PLL mux */ + vco_freq = muldiv64(clock_get_hz(pll->in), pll->vco_multiplier, 1); + + for (i = 0; i < RCC_NUM_CHANNEL_PLL_OUT; i++) { + if (!pll->channel_exists[i]) { + continue; + } + + old_channel_freq = clock_get_hz(pll->channels[i]); + if (bypass_source || + !pll->enabled || + !pll->channel_enabled[i] || + !pll->channel_divider[i]) { + channel_freq = 0; + } else { + channel_freq = muldiv64(vco_freq, + 1, + pll->channel_divider[i]); + } + + /* No change, early continue to avoid log spam and useless propagation */ + if (old_channel_freq == channel_freq) { + continue; + } + + clock_update_hz(pll->channels[i], channel_freq); + trace_stm32l4x5_rcc_pll_update(pll->id, i, vco_freq, + old_channel_freq, channel_freq); + } +} + +static void pll_src_update(void *opaque, ClockEvent event) +{ + RccPllState *s = opaque; + pll_update(s, false); +} + +static void pll_init(Object *obj) +{ + RccPllState *s = RCC_PLL(obj); + size_t i; + + s->in = qdev_init_clock_in(DEVICE(s), "in", + pll_src_update, s, ClockUpdate); + + const char *names[] = { + "out-p", "out-q", "out-r", + }; + + for (i = 0; i < RCC_NUM_CHANNEL_PLL_OUT; i++) { + s->channels[i] = qdev_init_clock_out(DEVICE(s), names[i]); + } +} + +static void pll_reset_enter(Object *obj, ResetType type) +{ + RccPllState *s = RCC_PLL(obj); + set_pll_init_info(s, s->id); +} + +static void pll_reset_hold(Object *obj) +{ + RccPllState *s = RCC_PLL(obj); + pll_update(s, true); +} + +static void pll_reset_exit(Object *obj) +{ + RccPllState *s = RCC_PLL(obj); + pll_update(s, false); +} + +static const VMStateDescription pll_vmstate = { + .name = TYPE_RCC_PLL, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(id, RccPllState), + VMSTATE_CLOCK(in, RccPllState), + VMSTATE_ARRAY_CLOCK(channels, RccPllState, + RCC_NUM_CHANNEL_PLL_OUT), + VMSTATE_BOOL(enabled, RccPllState), + VMSTATE_UINT32(vco_multiplier, RccPllState), + VMSTATE_BOOL_ARRAY(channel_enabled, RccPllState, RCC_NUM_CHANNEL_PLL_OUT), + VMSTATE_BOOL_ARRAY(channel_exists, RccPllState, RCC_NUM_CHANNEL_PLL_OUT), + VMSTATE_UINT32_ARRAY(channel_divider, RccPllState, RCC_NUM_CHANNEL_PLL_OUT), + VMSTATE_END_OF_LIST() + } +}; + +static void pll_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.enter = pll_reset_enter; + rc->phases.hold = pll_reset_hold; + rc->phases.exit = pll_reset_exit; + dc->vmsd = &pll_vmstate; +} + +static void pll_set_vco_multiplier(RccPllState *pll, uint32_t vco_multiplier) +{ + if (pll->vco_multiplier == vco_multiplier) { + return; + } + + if (vco_multiplier < 8 || vco_multiplier > 86) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: VCO multiplier is out of bound (%u) for PLL %u\n", + __func__, vco_multiplier, pll->id); + return; + } + + trace_stm32l4x5_rcc_pll_set_vco_multiplier(pll->id, + pll->vco_multiplier, vco_multiplier); + + pll->vco_multiplier = vco_multiplier; + pll_update(pll, false); +} + +static void pll_set_enable(RccPllState *pll, bool enabled) +{ + if (pll->enabled == enabled) { + return; + } + + pll->enabled = enabled; + pll_update(pll, false); +} + +static void pll_set_channel_enable(RccPllState *pll, + PllCommonChannels channel, + bool enabled) +{ + if (pll->channel_enabled[channel] == enabled) { + return; + } + + if (enabled) { + trace_stm32l4x5_rcc_pll_channel_enable(pll->id, channel); + } else { + trace_stm32l4x5_rcc_pll_channel_disable(pll->id, channel); + } + + pll->channel_enabled[channel] = enabled; + pll_update(pll, false); +} + +static void pll_set_channel_divider(RccPllState *pll, + PllCommonChannels channel, + uint32_t divider) +{ + if (pll->channel_divider[channel] == divider) { + return; + } + + trace_stm32l4x5_rcc_pll_set_channel_divider(pll->id, + channel, pll->channel_divider[channel], divider); + + pll->channel_divider[channel] = divider; + pll_update(pll, false); +} + +static void rcc_update_irq(Stm32l4x5RccState *s) +{ + /* + * TODO: Handle LSECSSF and CSSF flags when the CSS is implemented. + */ + if (s->cifr & CIFR_IRQ_MASK) { + qemu_irq_raise(s->irq); + } else { + qemu_irq_lower(s->irq); + } +} + +static void rcc_update_msi(Stm32l4x5RccState *s, uint32_t previous_value) +{ + uint32_t val; + + static const uint32_t msirange[] = { + 100000, 200000, 400000, 800000, 1000000, 2000000, + 4000000, 8000000, 16000000, 24000000, 32000000, 48000000 + }; + /* MSIRANGE and MSIRGSEL */ + val = extract32(s->cr, R_CR_MSIRGSEL_SHIFT, R_CR_MSIRGSEL_LENGTH); + if (val) { + /* MSIRGSEL is set, use the MSIRANGE field */ + val = extract32(s->cr, R_CR_MSIRANGE_SHIFT, R_CR_MSIRANGE_LENGTH); + } else { + /* MSIRGSEL is not set, use the MSISRANGE field */ + val = extract32(s->csr, R_CSR_MSISRANGE_SHIFT, R_CSR_MSISRANGE_LENGTH); + } + + if (val < ARRAY_SIZE(msirange)) { + clock_update_hz(s->msi_rc, msirange[val]); + } else { + /* + * There is a hardware write protection if the value is out of bound. + * Restore the previous value. + */ + s->cr = (s->cr & ~R_CSR_MSISRANGE_MASK) | + (previous_value & R_CSR_MSISRANGE_MASK); + } +} + +/* + * TODO: Add write-protection for all registers: + * DONE: CR + */ + +static void rcc_update_cr_register(Stm32l4x5RccState *s, uint32_t previous_value) +{ + int val; + const RccClockMuxSource current_pll_src = + CLOCK_MUX_INIT_INFO[RCC_CLOCK_MUX_PLL_INPUT].src_mapping[ + s->clock_muxes[RCC_CLOCK_MUX_PLL_INPUT].src]; + + /* PLLSAI2ON and update PLLSAI2RDY */ + val = FIELD_EX32(s->cr, CR, PLLSAI2ON); + pll_set_enable(&s->plls[RCC_PLL_PLLSAI2], val); + s->cr = (s->cr & ~R_CR_PLLSAI2RDY_MASK) | + (val << R_CR_PLLSAI2RDY_SHIFT); + if (s->cier & R_CIER_PLLSAI2RDYIE_MASK) { + s->cifr |= R_CIFR_PLLSAI2RDYF_MASK; + } + + /* PLLSAI1ON and update PLLSAI1RDY */ + val = FIELD_EX32(s->cr, CR, PLLSAI1ON); + pll_set_enable(&s->plls[RCC_PLL_PLLSAI1], val); + s->cr = (s->cr & ~R_CR_PLLSAI1RDY_MASK) | + (val << R_CR_PLLSAI1RDY_SHIFT); + if (s->cier & R_CIER_PLLSAI1RDYIE_MASK) { + s->cifr |= R_CIFR_PLLSAI1RDYF_MASK; + } + + /* + * PLLON and update PLLRDY + * PLLON cannot be reset if the PLL clock is used as the system clock. + */ + val = FIELD_EX32(s->cr, CR, PLLON); + if (FIELD_EX32(s->cfgr, CFGR, SWS) != 0b11) { + pll_set_enable(&s->plls[RCC_PLL_PLL], val); + s->cr = (s->cr & ~R_CR_PLLRDY_MASK) | + (val << R_CR_PLLRDY_SHIFT); + if (s->cier & R_CIER_PLLRDYIE_MASK) { + s->cifr |= R_CIFR_PLLRDYF_MASK; + } + } else { + s->cr |= R_CR_PLLON_MASK; + } + + /* CSSON: TODO */ + /* HSEBYP: TODO */ + + /* + * HSEON and update HSERDY. + * HSEON cannot be reset if the HSE oscillator is used directly or + * indirectly as the system clock. + */ + val = FIELD_EX32(s->cr, CR, HSEON); + if (FIELD_EX32(s->cfgr, CFGR, SWS) != 0b10 && + current_pll_src != RCC_CLOCK_MUX_SRC_HSE) { + s->cr = (s->cr & ~R_CR_HSERDY_MASK) | + (val << R_CR_HSERDY_SHIFT); + if (val) { + clock_update_hz(s->hse, s->hse_frequency); + if (s->cier & R_CIER_HSERDYIE_MASK) { + s->cifr |= R_CIFR_HSERDYF_MASK; + } + } else { + clock_update(s->hse, 0); + } + } else { + s->cr |= R_CR_HSEON_MASK; + } + + /* HSIAFS: TODO*/ + /* HSIKERON: TODO*/ + + /* + * HSION and update HSIRDY + * HSION is set by hardware if the HSI16 is used directly + * or indirectly as system clock. + */ + if (FIELD_EX32(s->cfgr, CFGR, SWS) == 0b01 || + current_pll_src == RCC_CLOCK_MUX_SRC_HSI) { + s->cr |= (R_CR_HSION_MASK | R_CR_HSIRDY_MASK); + clock_update_hz(s->hsi16_rc, HSI_FRQ); + if (s->cier & R_CIER_HSIRDYIE_MASK) { + s->cifr |= R_CIFR_HSIRDYF_MASK; + } + } else { + val = FIELD_EX32(s->cr, CR, HSION); + if (val) { + clock_update_hz(s->hsi16_rc, HSI_FRQ); + s->cr |= R_CR_HSIRDY_MASK; + if (s->cier & R_CIER_HSIRDYIE_MASK) { + s->cifr |= R_CIFR_HSIRDYF_MASK; + } + } else { + clock_update(s->hsi16_rc, 0); + s->cr &= ~R_CR_HSIRDY_MASK; + } + } + + /* MSIPLLEN: TODO */ + + /* + * MSION and update MSIRDY + * Set by hardware when used directly or indirectly as system clock. + */ + if (FIELD_EX32(s->cfgr, CFGR, SWS) == 0b00 || + current_pll_src == RCC_CLOCK_MUX_SRC_MSI) { + s->cr |= (R_CR_MSION_MASK | R_CR_MSIRDY_MASK); + if (!(previous_value & R_CR_MSION_MASK) && (s->cier & R_CIER_MSIRDYIE_MASK)) { + s->cifr |= R_CIFR_MSIRDYF_MASK; + } + rcc_update_msi(s, previous_value); + } else { + val = FIELD_EX32(s->cr, CR, MSION); + if (val) { + s->cr |= R_CR_MSIRDY_MASK; + rcc_update_msi(s, previous_value); + if (s->cier & R_CIER_MSIRDYIE_MASK) { + s->cifr |= R_CIFR_MSIRDYF_MASK; + } + } else { + s->cr &= ~R_CR_MSIRDY_MASK; + clock_update(s->msi_rc, 0); + } + } + rcc_update_irq(s); +} + +static void rcc_update_cfgr_register(Stm32l4x5RccState *s) +{ + uint32_t val; + /* MCOPRE */ + val = FIELD_EX32(s->cfgr, CFGR, MCOPRE); + assert(val <= 0b100); + clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_MCO], + 1, 1 << val); + + /* MCOSEL */ + val = FIELD_EX32(s->cfgr, CFGR, MCOSEL); + assert(val <= 0b111); + if (val == 0) { + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], false); + } else { + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], true); + clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_MCO], + val - 1); + } + + /* STOPWUCK */ + /* TODO */ + + /* PPRE2 */ + val = FIELD_EX32(s->cfgr, CFGR, PPRE2); + if (val < 0b100) { + clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_PCLK2], + 1, 1); + } else { + clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_PCLK2], + 1, 1 << (val - 0b11)); + } + + /* PPRE1 */ + val = FIELD_EX32(s->cfgr, CFGR, PPRE1); + if (val < 0b100) { + clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_PCLK1], + 1, 1); + } else { + clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_PCLK1], + 1, 1 << (val - 0b11)); + } + + /* HPRE */ + val = FIELD_EX32(s->cfgr, CFGR, HPRE); + if (val < 0b1000) { + clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_HCLK], + 1, 1); + } else { + clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_HCLK], + 1, 1 << (val - 0b111)); + } + + /* Update SWS */ + val = FIELD_EX32(s->cfgr, CFGR, SW); + clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_SYSCLK], + val); + s->cfgr &= ~R_CFGR_SWS_MASK; + s->cfgr |= val << R_CFGR_SWS_SHIFT; +} + +static void rcc_update_ahb1enr(Stm32l4x5RccState *s) +{ + #define AHB1ENR_SET_ENABLE(_peripheral_name) \ + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_##_peripheral_name], \ + FIELD_EX32(s->ahb1enr, AHB1ENR, _peripheral_name##EN)) + + /* DMA2DEN: reserved for STM32L475xx */ + AHB1ENR_SET_ENABLE(TSC); + AHB1ENR_SET_ENABLE(CRC); + AHB1ENR_SET_ENABLE(FLASH); + AHB1ENR_SET_ENABLE(DMA2); + AHB1ENR_SET_ENABLE(DMA1); + + #undef AHB1ENR_SET_ENABLE +} + +static void rcc_update_ahb2enr(Stm32l4x5RccState *s) +{ + #define AHB2ENR_SET_ENABLE(_peripheral_name) \ + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_##_peripheral_name], \ + FIELD_EX32(s->ahb2enr, AHB2ENR, _peripheral_name##EN)) + + AHB2ENR_SET_ENABLE(RNG); + /* HASHEN: reserved for STM32L475xx */ + AHB2ENR_SET_ENABLE(AES); + /* DCMIEN: reserved for STM32L475xx */ + AHB2ENR_SET_ENABLE(ADC); + AHB2ENR_SET_ENABLE(OTGFS); + /* GPIOIEN: reserved for STM32L475xx */ + AHB2ENR_SET_ENABLE(GPIOA); + AHB2ENR_SET_ENABLE(GPIOB); + AHB2ENR_SET_ENABLE(GPIOC); + AHB2ENR_SET_ENABLE(GPIOD); + AHB2ENR_SET_ENABLE(GPIOE); + AHB2ENR_SET_ENABLE(GPIOF); + AHB2ENR_SET_ENABLE(GPIOG); + AHB2ENR_SET_ENABLE(GPIOH); + + #undef AHB2ENR_SET_ENABLE +} + +static void rcc_update_ahb3enr(Stm32l4x5RccState *s) +{ + #define AHB3ENR_SET_ENABLE(_peripheral_name) \ + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_##_peripheral_name], \ + FIELD_EX32(s->ahb3enr, AHB3ENR, _peripheral_name##EN)) + + AHB3ENR_SET_ENABLE(QSPI); + AHB3ENR_SET_ENABLE(FMC); + + #undef AHB3ENR_SET_ENABLE +} + +static void rcc_update_apb1enr(Stm32l4x5RccState *s) +{ + #define APB1ENR1_SET_ENABLE(_peripheral_name) \ + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_##_peripheral_name], \ + FIELD_EX32(s->apb1enr1, APB1ENR1, _peripheral_name##EN)) + #define APB1ENR2_SET_ENABLE(_peripheral_name) \ + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_##_peripheral_name], \ + FIELD_EX32(s->apb1enr2, APB1ENR2, _peripheral_name##EN)) + + /* APB1ENR1 */ + APB1ENR1_SET_ENABLE(LPTIM1); + APB1ENR1_SET_ENABLE(OPAMP); + APB1ENR1_SET_ENABLE(DAC1); + APB1ENR1_SET_ENABLE(PWR); + /* CAN2: reserved for STM32L4x5 */ + APB1ENR1_SET_ENABLE(CAN1); + /* CRSEN: reserved for STM32L4x5 */ + APB1ENR1_SET_ENABLE(I2C3); + APB1ENR1_SET_ENABLE(I2C2); + APB1ENR1_SET_ENABLE(I2C1); + APB1ENR1_SET_ENABLE(UART5); + APB1ENR1_SET_ENABLE(UART4); + APB1ENR1_SET_ENABLE(USART3); + APB1ENR1_SET_ENABLE(USART2); + APB1ENR1_SET_ENABLE(SPI3); + APB1ENR1_SET_ENABLE(SPI2); + APB1ENR1_SET_ENABLE(WWDG); + /* RTCAPB: reserved for STM32L4x5 */ + APB1ENR1_SET_ENABLE(LCD); + APB1ENR1_SET_ENABLE(TIM7); + APB1ENR1_SET_ENABLE(TIM6); + APB1ENR1_SET_ENABLE(TIM5); + APB1ENR1_SET_ENABLE(TIM4); + APB1ENR1_SET_ENABLE(TIM3); + APB1ENR1_SET_ENABLE(TIM2); + + /* APB1ENR2 */ + APB1ENR2_SET_ENABLE(LPTIM2); + APB1ENR2_SET_ENABLE(SWPMI1); + /* I2C4EN: reserved for STM32L4x5 */ + APB1ENR2_SET_ENABLE(LPUART1); + + #undef APB1ENR1_SET_ENABLE + #undef APB1ENR2_SET_ENABLE +} + +static void rcc_update_apb2enr(Stm32l4x5RccState *s) +{ + #define APB2ENR_SET_ENABLE(_peripheral_name) \ + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_##_peripheral_name], \ + FIELD_EX32(s->apb2enr, APB2ENR, _peripheral_name##EN)) + + APB2ENR_SET_ENABLE(DFSDM1); + APB2ENR_SET_ENABLE(SAI2); + APB2ENR_SET_ENABLE(SAI1); + APB2ENR_SET_ENABLE(TIM17); + APB2ENR_SET_ENABLE(TIM16); + APB2ENR_SET_ENABLE(TIM15); + APB2ENR_SET_ENABLE(USART1); + APB2ENR_SET_ENABLE(TIM8); + APB2ENR_SET_ENABLE(SPI1); + APB2ENR_SET_ENABLE(TIM1); + APB2ENR_SET_ENABLE(SDMMC1); + APB2ENR_SET_ENABLE(FW); + APB2ENR_SET_ENABLE(SYSCFG); + + #undef APB2ENR_SET_ENABLE +} + +/* + * The 3 PLLs share the same register layout + * so we can use the same function for all of them + * Note: no frequency bounds checking is done here. + */ +static void rcc_update_pllsaixcfgr(Stm32l4x5RccState *s, RccPll pll_id) +{ + uint32_t reg, val; + switch (pll_id) { + case RCC_PLL_PLL: + reg = s->pllcfgr; + break; + case RCC_PLL_PLLSAI1: + reg = s->pllsai1cfgr; + break; + case RCC_PLL_PLLSAI2: + reg = s->pllsai2cfgr; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid PLL ID: %u\n", __func__, pll_id); + return; + } + + /* PLLPDIV */ + val = FIELD_EX32(reg, PLLCFGR, PLLPDIV); + /* 1 is a reserved value */ + if (val == 0) { + /* Get PLLP value */ + val = FIELD_EX32(reg, PLLCFGR, PLLP); + pll_set_channel_divider(&s->plls[pll_id], RCC_PLL_COMMON_CHANNEL_P, + (val ? 17 : 7)); + } else if (val > 1) { + pll_set_channel_divider(&s->plls[pll_id], RCC_PLL_COMMON_CHANNEL_P, + val); + } + + + /* PLLR */ + val = FIELD_EX32(reg, PLLCFGR, PLLR); + pll_set_channel_divider(&s->plls[pll_id], RCC_PLL_COMMON_CHANNEL_R, + 2 * (val + 1)); + + /* PLLREN */ + val = FIELD_EX32(reg, PLLCFGR, PLLREN); + pll_set_channel_enable(&s->plls[pll_id], RCC_PLL_COMMON_CHANNEL_R, val); + + /* PLLQ */ + val = FIELD_EX32(reg, PLLCFGR, PLLQ); + pll_set_channel_divider(&s->plls[pll_id], RCC_PLL_COMMON_CHANNEL_Q, + 2 * (val + 1)); + + /* PLLQEN */ + val = FIELD_EX32(reg, PLLCFGR, PLLQEN); + pll_set_channel_enable(&s->plls[pll_id], RCC_PLL_COMMON_CHANNEL_Q, val); + + /* PLLPEN */ + val = FIELD_EX32(reg, PLLCFGR, PLLPEN); + pll_set_channel_enable(&s->plls[pll_id], RCC_PLL_COMMON_CHANNEL_P, val); + + /* PLLN */ + val = FIELD_EX32(reg, PLLCFGR, PLLN); + pll_set_vco_multiplier(&s->plls[pll_id], val); +} + +static void rcc_update_pllcfgr(Stm32l4x5RccState *s) +{ + int val; + + /* Use common layout */ + rcc_update_pllsaixcfgr(s, RCC_PLL_PLL); + + /* Fetch specific fields for pllcfgr */ + + /* PLLM */ + val = FIELD_EX32(s->pllcfgr, PLLCFGR, PLLM); + clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_PLL_INPUT], 1, (val + 1)); + + /* PLLSRC */ + val = FIELD_EX32(s->pllcfgr, PLLCFGR, PLLSRC); + if (val == 0) { + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_PLL_INPUT], false); + } else { + clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_PLL_INPUT], val - 1); + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_PLL_INPUT], true); + } +} + +static void rcc_update_ccipr(Stm32l4x5RccState *s) +{ + #define CCIPR_SET_SOURCE(_peripheral_name) \ + clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_##_peripheral_name], \ + FIELD_EX32(s->ccipr, CCIPR, _peripheral_name##SEL)) + + CCIPR_SET_SOURCE(DFSDM1); + CCIPR_SET_SOURCE(SWPMI1); + CCIPR_SET_SOURCE(ADC); + CCIPR_SET_SOURCE(CLK48); + CCIPR_SET_SOURCE(SAI2); + CCIPR_SET_SOURCE(SAI1); + CCIPR_SET_SOURCE(LPTIM2); + CCIPR_SET_SOURCE(LPTIM1); + CCIPR_SET_SOURCE(I2C3); + CCIPR_SET_SOURCE(I2C2); + CCIPR_SET_SOURCE(I2C1); + CCIPR_SET_SOURCE(LPUART1); + CCIPR_SET_SOURCE(UART5); + CCIPR_SET_SOURCE(UART4); + CCIPR_SET_SOURCE(USART3); + CCIPR_SET_SOURCE(USART2); + CCIPR_SET_SOURCE(USART1); + + #undef CCIPR_SET_SOURCE +} + +static void rcc_update_bdcr(Stm32l4x5RccState *s) +{ + int val; + + /* LSCOSEL */ + val = FIELD_EX32(s->bdcr, BDCR, LSCOSEL); + clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_LSCO], val); + + val = FIELD_EX32(s->bdcr, BDCR, LSCOEN); + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_LSCO], val); + + /* BDRST */ + /* + * The documentation is not clear if the RTCEN flag disables the RTC and + * the LCD common mux or if it only affects the RTC. + * As the LCDEN flag exists, we assume here that it only affects the RTC. + */ + val = FIELD_EX32(s->bdcr, BDCR, RTCEN); + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_RTC], val); + /* LCD and RTC share the same clock */ + val = FIELD_EX32(s->bdcr, BDCR, RTCSEL); + clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_LCD_AND_RTC_COMMON], val); + + /* LSECSSON */ + /* LSEDRV[1:0] */ + /* LSEBYP */ + + /* LSEON: Update LSERDY at the same time */ + val = FIELD_EX32(s->bdcr, BDCR, LSEON); + if (val) { + clock_update_hz(s->lse_crystal, LSE_FRQ); + s->bdcr |= R_BDCR_LSERDY_MASK; + if (s->cier & R_CIER_LSERDYIE_MASK) { + s->cifr |= R_CIFR_LSERDYF_MASK; + } + } else { + clock_update(s->lse_crystal, 0); + s->bdcr &= ~R_BDCR_LSERDY_MASK; + } + + rcc_update_irq(s); +} + +static void rcc_update_csr(Stm32l4x5RccState *s) +{ + int val; + + /* Reset flags: Not implemented */ + /* MSISRANGE: Not implemented after reset */ + + /* LSION: Update LSIRDY at the same time */ + val = FIELD_EX32(s->csr, CSR, LSION); + if (val) { + clock_update_hz(s->lsi_rc, LSI_FRQ); + s->csr |= R_CSR_LSIRDY_MASK; + if (s->cier & R_CIER_LSIRDYIE_MASK) { + s->cifr |= R_CIFR_LSIRDYF_MASK; + } + } else { + /* + * TODO: Handle when the LSI is set independently of LSION. + * E.g. when the LSI is set by the RTC. + * See the reference manual for more details. + */ + clock_update(s->lsi_rc, 0); + s->csr &= ~R_CSR_LSIRDY_MASK; + } + + rcc_update_irq(s); +} + +static void stm32l4x5_rcc_reset_hold(Object *obj) +{ + Stm32l4x5RccState *s = STM32L4X5_RCC(obj); + s->cr = 0x00000063; + /* + * Factory-programmed calibration data + * From the reference manual: 0x10XX 00XX + * Value taken from a real card. + */ + s->icscr = 0x106E0082; + s->cfgr = 0x0; + s->pllcfgr = 0x00001000; + s->pllsai1cfgr = 0x00001000; + s->pllsai2cfgr = 0x00001000; + s->cier = 0x0; + s->cifr = 0x0; + s->ahb1rstr = 0x0; + s->ahb2rstr = 0x0; + s->ahb3rstr = 0x0; + s->apb1rstr1 = 0x0; + s->apb1rstr2 = 0x0; + s->apb2rstr = 0x0; + s->ahb1enr = 0x00000100; + s->ahb2enr = 0x0; + s->ahb3enr = 0x0; + s->apb1enr1 = 0x0; + s->apb1enr2 = 0x0; + s->apb2enr = 0x0; + s->ahb1smenr = 0x00011303; + s->ahb2smenr = 0x000532FF; + s->ahb3smenr = 0x00000101; + s->apb1smenr1 = 0xF2FECA3F; + s->apb1smenr2 = 0x00000025; + s->apb2smenr = 0x01677C01; + s->ccipr = 0x0; + s->bdcr = 0x0; + s->csr = 0x0C000600; +} + +static uint64_t stm32l4x5_rcc_read(void *opaque, hwaddr addr, + unsigned int size) +{ + Stm32l4x5RccState *s = opaque; + uint64_t retvalue = 0; + + switch (addr) { + case A_CR: + retvalue = s->cr; + break; + case A_ICSCR: + retvalue = s->icscr; + break; + case A_CFGR: + retvalue = s->cfgr; + break; + case A_PLLCFGR: + retvalue = s->pllcfgr; + break; + case A_PLLSAI1CFGR: + retvalue = s->pllsai1cfgr; + break; + case A_PLLSAI2CFGR: + retvalue = s->pllsai2cfgr; + break; + case A_CIER: + retvalue = s->cier; + break; + case A_CIFR: + retvalue = s->cifr; + break; + case A_CICR: + /* CICR is write only, return the reset value = 0 */ + break; + case A_AHB1RSTR: + retvalue = s->ahb1rstr; + break; + case A_AHB2RSTR: + retvalue = s->ahb2rstr; + break; + case A_AHB3RSTR: + retvalue = s->ahb3rstr; + break; + case A_APB1RSTR1: + retvalue = s->apb1rstr1; + break; + case A_APB1RSTR2: + retvalue = s->apb1rstr2; + break; + case A_APB2RSTR: + retvalue = s->apb2rstr; + break; + case A_AHB1ENR: + retvalue = s->ahb1enr; + break; + case A_AHB2ENR: + retvalue = s->ahb2enr; + break; + case A_AHB3ENR: + retvalue = s->ahb3enr; + break; + case A_APB1ENR1: + retvalue = s->apb1enr1; + break; + case A_APB1ENR2: + retvalue = s->apb1enr2; + break; + case A_APB2ENR: + retvalue = s->apb2enr; + break; + case A_AHB1SMENR: + retvalue = s->ahb1smenr; + break; + case A_AHB2SMENR: + retvalue = s->ahb2smenr; + break; + case A_AHB3SMENR: + retvalue = s->ahb3smenr; + break; + case A_APB1SMENR1: + retvalue = s->apb1smenr1; + break; + case A_APB1SMENR2: + retvalue = s->apb1smenr2; + break; + case A_APB2SMENR: + retvalue = s->apb2smenr; + break; + case A_CCIPR: + retvalue = s->ccipr; + break; + case A_BDCR: + retvalue = s->bdcr; + break; + case A_CSR: + retvalue = s->csr; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + break; + } + + trace_stm32l4x5_rcc_read(addr, retvalue); + + return retvalue; +} + +static void stm32l4x5_rcc_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + Stm32l4x5RccState *s = opaque; + uint32_t previous_value = 0; + const uint32_t value = val64; + + trace_stm32l4x5_rcc_write(addr, value); + + switch (addr) { + case A_CR: + previous_value = s->cr; + s->cr = (s->cr & CR_READ_SET_MASK) | + (value & (CR_READ_SET_MASK | ~CR_READ_ONLY_MASK)); + rcc_update_cr_register(s, previous_value); + break; + case A_ICSCR: + s->icscr = value & ~ICSCR_READ_ONLY_MASK; + qemu_log_mask(LOG_UNIMP, + "%s: Side-effects not implemented for ICSCR\n", __func__); + break; + case A_CFGR: + s->cfgr = value & ~CFGR_READ_ONLY_MASK; + rcc_update_cfgr_register(s); + break; + case A_PLLCFGR: + s->pllcfgr = value; + rcc_update_pllcfgr(s); + break; + case A_PLLSAI1CFGR: + s->pllsai1cfgr = value; + rcc_update_pllsaixcfgr(s, RCC_PLL_PLLSAI1); + break; + case A_PLLSAI2CFGR: + s->pllsai2cfgr = value; + rcc_update_pllsaixcfgr(s, RCC_PLL_PLLSAI2); + break; + case A_CIER: + s->cier = value; + qemu_log_mask(LOG_UNIMP, + "%s: Side-effects not implemented for CIER\n", __func__); + break; + case A_CIFR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Write attempt into read-only register (CIFR) 0x%"PRIx32"\n", + __func__, value); + break; + case A_CICR: + /* Clear interrupt flags by writing a 1 to the CICR register */ + s->cifr &= ~value; + rcc_update_irq(s); + break; + /* Reset behaviors are not implemented */ + case A_AHB1RSTR: + s->ahb1rstr = value; + qemu_log_mask(LOG_UNIMP, + "%s: Side-effects not implemented for AHB1RSTR\n", __func__); + break; + case A_AHB2RSTR: + s->ahb2rstr = value; + qemu_log_mask(LOG_UNIMP, + "%s: Side-effects not implemented for AHB2RSTR\n", __func__); + break; + case A_AHB3RSTR: + s->ahb3rstr = value; + qemu_log_mask(LOG_UNIMP, + "%s: Side-effects not implemented for AHB3RSTR\n", __func__); + break; + case A_APB1RSTR1: + s->apb1rstr1 = value; + qemu_log_mask(LOG_UNIMP, + "%s: Side-effects not implemented for APB1RSTR1\n", __func__); + break; + case A_APB1RSTR2: + s->apb1rstr2 = value; + qemu_log_mask(LOG_UNIMP, + "%s: Side-effects not implemented for APB1RSTR2\n", __func__); + break; + case A_APB2RSTR: + s->apb2rstr = value; + qemu_log_mask(LOG_UNIMP, + "%s: Side-effects not implemented for APB2RSTR\n", __func__); + break; + case A_AHB1ENR: + s->ahb1enr = value; + rcc_update_ahb1enr(s); + break; + case A_AHB2ENR: + s->ahb2enr = value; + rcc_update_ahb2enr(s); + break; + case A_AHB3ENR: + s->ahb3enr = value; + rcc_update_ahb3enr(s); + break; + case A_APB1ENR1: + s->apb1enr1 = value; + rcc_update_apb1enr(s); + break; + case A_APB1ENR2: + s->apb1enr2 = value; + rcc_update_apb1enr(s); + break; + case A_APB2ENR: + s->apb2enr = (s->apb2enr & APB2ENR_READ_SET_MASK) | value; + rcc_update_apb2enr(s); + break; + /* Behaviors for Sleep and Stop modes are not implemented */ + case A_AHB1SMENR: + s->ahb1smenr = value; + qemu_log_mask(LOG_UNIMP, + "%s: Side-effects not implemented for AHB1SMENR\n", __func__); + break; + case A_AHB2SMENR: + s->ahb2smenr = value; + qemu_log_mask(LOG_UNIMP, + "%s: Side-effects not implemented for AHB2SMENR\n", __func__); + break; + case A_AHB3SMENR: + s->ahb3smenr = value; + qemu_log_mask(LOG_UNIMP, + "%s: Side-effects not implemented for AHB3SMENR\n", __func__); + break; + case A_APB1SMENR1: + s->apb1smenr1 = value; + qemu_log_mask(LOG_UNIMP, + "%s: Side-effects not implemented for APB1SMENR1\n", __func__); + break; + case A_APB1SMENR2: + s->apb1smenr2 = value; + qemu_log_mask(LOG_UNIMP, + "%s: Side-effects not implemented for APB1SMENR2\n", __func__); + break; + case A_APB2SMENR: + s->apb2smenr = value; + qemu_log_mask(LOG_UNIMP, + "%s: Side-effects not implemented for APB2SMENR\n", __func__); + break; + case A_CCIPR: + s->ccipr = value; + rcc_update_ccipr(s); + break; + case A_BDCR: + s->bdcr = value & ~BDCR_READ_ONLY_MASK; + rcc_update_bdcr(s); + break; + case A_CSR: + s->csr = value & ~CSR_READ_ONLY_MASK; + rcc_update_csr(s); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + } +} + +static const MemoryRegionOps stm32l4x5_rcc_ops = { + .read = stm32l4x5_rcc_read, + .write = stm32l4x5_rcc_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .max_access_size = 4, + .min_access_size = 4, + .unaligned = false + }, + .impl = { + .max_access_size = 4, + .min_access_size = 4, + .unaligned = false + }, +}; + +static const ClockPortInitArray stm32l4x5_rcc_clocks = { + QDEV_CLOCK_IN(Stm32l4x5RccState, hsi16_rc, NULL, 0), + QDEV_CLOCK_IN(Stm32l4x5RccState, msi_rc, NULL, 0), + QDEV_CLOCK_IN(Stm32l4x5RccState, hse, NULL, 0), + QDEV_CLOCK_IN(Stm32l4x5RccState, lsi_rc, NULL, 0), + QDEV_CLOCK_IN(Stm32l4x5RccState, lse_crystal, NULL, 0), + QDEV_CLOCK_IN(Stm32l4x5RccState, sai1_extclk, NULL, 0), + QDEV_CLOCK_IN(Stm32l4x5RccState, sai2_extclk, NULL, 0), + QDEV_CLOCK_END +}; + + +static void stm32l4x5_rcc_init(Object *obj) +{ + Stm32l4x5RccState *s = STM32L4X5_RCC(obj); + size_t i; + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->mmio, obj, &stm32l4x5_rcc_ops, s, + TYPE_STM32L4X5_RCC, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + qdev_init_clocks(DEVICE(s), stm32l4x5_rcc_clocks); + + for (i = 0; i < RCC_NUM_PLL; i++) { + object_initialize_child(obj, PLL_INIT_INFO[i].name, + &s->plls[i], TYPE_RCC_PLL); + set_pll_init_info(&s->plls[i], i); + } + + for (i = 0; i < RCC_NUM_CLOCK_MUX; i++) { + char *alias; + + object_initialize_child(obj, CLOCK_MUX_INIT_INFO[i].name, + &s->clock_muxes[i], + TYPE_RCC_CLOCK_MUX); + set_clock_mux_init_info(&s->clock_muxes[i], i); + + if (!CLOCK_MUX_INIT_INFO[i].hidden) { + /* Expose muxes output as RCC outputs */ + alias = g_strdup_printf("%s-out", CLOCK_MUX_INIT_INFO[i].name); + qdev_alias_clock(DEVICE(&s->clock_muxes[i]), "out", DEVICE(obj), alias); + g_free(alias); + } + } + + s->gnd = clock_new(obj, "gnd"); +} + +static void connect_mux_sources(Stm32l4x5RccState *s, + RccClockMuxState *mux, + const RccClockMuxSource *clk_mapping) +{ + size_t i; + + Clock * const CLK_SRC_MAPPING[] = { + [RCC_CLOCK_MUX_SRC_GND] = s->gnd, + [RCC_CLOCK_MUX_SRC_HSI] = s->hsi16_rc, + [RCC_CLOCK_MUX_SRC_HSE] = s->hse, + [RCC_CLOCK_MUX_SRC_MSI] = s->msi_rc, + [RCC_CLOCK_MUX_SRC_LSI] = s->lsi_rc, + [RCC_CLOCK_MUX_SRC_LSE] = s->lse_crystal, + [RCC_CLOCK_MUX_SRC_SAI1_EXTCLK] = s->sai1_extclk, + [RCC_CLOCK_MUX_SRC_SAI2_EXTCLK] = s->sai2_extclk, + [RCC_CLOCK_MUX_SRC_PLL] = + s->plls[RCC_PLL_PLL].channels[RCC_PLL_CHANNEL_PLLCLK], + [RCC_CLOCK_MUX_SRC_PLLSAI1] = + s->plls[RCC_PLL_PLLSAI1].channels[RCC_PLLSAI1_CHANNEL_PLLSAI1CLK], + [RCC_CLOCK_MUX_SRC_PLLSAI2] = + s->plls[RCC_PLL_PLLSAI2].channels[RCC_PLLSAI2_CHANNEL_PLLSAI2CLK], + [RCC_CLOCK_MUX_SRC_PLLSAI3] = + s->plls[RCC_PLL_PLL].channels[RCC_PLL_CHANNEL_PLLSAI3CLK], + [RCC_CLOCK_MUX_SRC_PLL48M1] = + s->plls[RCC_PLL_PLL].channels[RCC_PLL_CHANNEL_PLL48M1CLK], + [RCC_CLOCK_MUX_SRC_PLL48M2] = + s->plls[RCC_PLL_PLLSAI1].channels[RCC_PLLSAI1_CHANNEL_PLL48M2CLK], + [RCC_CLOCK_MUX_SRC_PLLADC1] = + s->plls[RCC_PLL_PLLSAI1].channels[RCC_PLLSAI1_CHANNEL_PLLADC1CLK], + [RCC_CLOCK_MUX_SRC_PLLADC2] = + s->plls[RCC_PLL_PLLSAI2] .channels[RCC_PLLSAI2_CHANNEL_PLLADC2CLK], + [RCC_CLOCK_MUX_SRC_SYSCLK] = s->clock_muxes[RCC_CLOCK_MUX_SYSCLK].out, + [RCC_CLOCK_MUX_SRC_HCLK] = s->clock_muxes[RCC_CLOCK_MUX_HCLK].out, + [RCC_CLOCK_MUX_SRC_PCLK1] = s->clock_muxes[RCC_CLOCK_MUX_PCLK1].out, + [RCC_CLOCK_MUX_SRC_PCLK2] = s->clock_muxes[RCC_CLOCK_MUX_PCLK2].out, + [RCC_CLOCK_MUX_SRC_HSE_OVER_32] = s->clock_muxes[RCC_CLOCK_MUX_HSE_OVER_32].out, + [RCC_CLOCK_MUX_SRC_LCD_AND_RTC_COMMON] = + s->clock_muxes[RCC_CLOCK_MUX_LCD_AND_RTC_COMMON].out, + }; + + assert(ARRAY_SIZE(CLK_SRC_MAPPING) == RCC_CLOCK_MUX_SRC_NUMBER); + + for (i = 0; i < RCC_NUM_CLOCK_MUX_SRC; i++) { + RccClockMuxSource mapping = clk_mapping[i]; + clock_set_source(mux->srcs[i], CLK_SRC_MAPPING[mapping]); + } +} + + +static const VMStateDescription vmstate_stm32l4x5_rcc = { + .name = TYPE_STM32L4X5_RCC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr, Stm32l4x5RccState), + VMSTATE_UINT32(icscr, Stm32l4x5RccState), + VMSTATE_UINT32(cfgr, Stm32l4x5RccState), + VMSTATE_UINT32(pllcfgr, Stm32l4x5RccState), + VMSTATE_UINT32(pllsai1cfgr, Stm32l4x5RccState), + VMSTATE_UINT32(pllsai2cfgr, Stm32l4x5RccState), + VMSTATE_UINT32(cier, Stm32l4x5RccState), + VMSTATE_UINT32(cifr, Stm32l4x5RccState), + VMSTATE_UINT32(ahb1rstr, Stm32l4x5RccState), + VMSTATE_UINT32(ahb2rstr, Stm32l4x5RccState), + VMSTATE_UINT32(ahb3rstr, Stm32l4x5RccState), + VMSTATE_UINT32(apb1rstr1, Stm32l4x5RccState), + VMSTATE_UINT32(apb1rstr2, Stm32l4x5RccState), + VMSTATE_UINT32(apb2rstr, Stm32l4x5RccState), + VMSTATE_UINT32(ahb1enr, Stm32l4x5RccState), + VMSTATE_UINT32(ahb2enr, Stm32l4x5RccState), + VMSTATE_UINT32(ahb3enr, Stm32l4x5RccState), + VMSTATE_UINT32(apb1enr1, Stm32l4x5RccState), + VMSTATE_UINT32(apb1enr2, Stm32l4x5RccState), + VMSTATE_UINT32(apb2enr, Stm32l4x5RccState), + VMSTATE_UINT32(ahb1smenr, Stm32l4x5RccState), + VMSTATE_UINT32(ahb2smenr, Stm32l4x5RccState), + VMSTATE_UINT32(ahb3smenr, Stm32l4x5RccState), + VMSTATE_UINT32(apb1smenr1, Stm32l4x5RccState), + VMSTATE_UINT32(apb1smenr2, Stm32l4x5RccState), + VMSTATE_UINT32(apb2smenr, Stm32l4x5RccState), + VMSTATE_UINT32(ccipr, Stm32l4x5RccState), + VMSTATE_UINT32(bdcr, Stm32l4x5RccState), + VMSTATE_UINT32(csr, Stm32l4x5RccState), + VMSTATE_CLOCK(hsi16_rc, Stm32l4x5RccState), + VMSTATE_CLOCK(msi_rc, Stm32l4x5RccState), + VMSTATE_CLOCK(hse, Stm32l4x5RccState), + VMSTATE_CLOCK(lsi_rc, Stm32l4x5RccState), + VMSTATE_CLOCK(lse_crystal, Stm32l4x5RccState), + VMSTATE_CLOCK(sai1_extclk, Stm32l4x5RccState), + VMSTATE_CLOCK(sai2_extclk, Stm32l4x5RccState), + VMSTATE_END_OF_LIST() + } +}; + + +static void stm32l4x5_rcc_realize(DeviceState *dev, Error **errp) +{ + Stm32l4x5RccState *s = STM32L4X5_RCC(dev); + size_t i; + + if (s->hse_frequency < 4000000ULL || + s->hse_frequency > 48000000ULL) { + error_setg(errp, + "HSE frequency is outside of the allowed [4-48]Mhz range: %" PRIx64 "", + s->hse_frequency); + return; + } + + for (i = 0; i < RCC_NUM_PLL; i++) { + RccPllState *pll = &s->plls[i]; + + clock_set_source(pll->in, s->clock_muxes[RCC_CLOCK_MUX_PLL_INPUT].out); + + if (!qdev_realize(DEVICE(pll), NULL, errp)) { + return; + } + } + + for (i = 0; i < RCC_NUM_CLOCK_MUX; i++) { + RccClockMuxState *clock_mux = &s->clock_muxes[i]; + + connect_mux_sources(s, clock_mux, CLOCK_MUX_INIT_INFO[i].src_mapping); + + if (!qdev_realize(DEVICE(clock_mux), NULL, errp)) { + return; + } + } + + /* + * Start clocks after everything is connected + * to propagate the frequencies along the tree. + */ + clock_update_hz(s->msi_rc, MSI_DEFAULT_FRQ); + clock_update_hz(s->sai1_extclk, s->sai1_extclk_frequency); + clock_update_hz(s->sai2_extclk, s->sai2_extclk_frequency); + clock_update(s->gnd, 0); +} + +static Property stm32l4x5_rcc_properties[] = { + DEFINE_PROP_UINT64("hse_frequency", Stm32l4x5RccState, + hse_frequency, HSE_DEFAULT_FRQ), + DEFINE_PROP_UINT64("sai1_extclk_frequency", Stm32l4x5RccState, + sai1_extclk_frequency, 0), + DEFINE_PROP_UINT64("sai2_extclk_frequency", Stm32l4x5RccState, + sai2_extclk_frequency, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stm32l4x5_rcc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + assert(ARRAY_SIZE(CLOCK_MUX_INIT_INFO) == RCC_NUM_CLOCK_MUX); + + rc->phases.hold = stm32l4x5_rcc_reset_hold; + device_class_set_props(dc, stm32l4x5_rcc_properties); + dc->realize = stm32l4x5_rcc_realize; + dc->vmsd = &vmstate_stm32l4x5_rcc; +} + +static const TypeInfo stm32l4x5_rcc_types[] = { + { + .name = TYPE_STM32L4X5_RCC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Stm32l4x5RccState), + .instance_init = stm32l4x5_rcc_init, + .class_init = stm32l4x5_rcc_class_init, + }, { + .name = TYPE_RCC_CLOCK_MUX, + .parent = TYPE_DEVICE, + .instance_size = sizeof(RccClockMuxState), + .instance_init = clock_mux_init, + .class_init = clock_mux_class_init, + }, { + .name = TYPE_RCC_PLL, + .parent = TYPE_DEVICE, + .instance_size = sizeof(RccPllState), + .instance_init = pll_init, + .class_init = pll_class_init, + } +}; + +DEFINE_TYPES(stm32l4x5_rcc_types) diff --git a/hw/misc/stm32l4x5_syscfg.c b/hw/misc/stm32l4x5_syscfg.c new file mode 100644 index 0000000000..3dafc00b49 --- /dev/null +++ b/hw/misc/stm32l4x5_syscfg.c @@ -0,0 +1,267 @@ +/* + * STM32L4x5 SYSCFG (System Configuration Controller) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This work is based on the stm32f4xx_syscfg by Alistair Francis. + * Original code is licensed under the MIT License: + * + * Copyright (c) 2014 Alistair Francis + */ + +/* + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "hw/misc/stm32l4x5_syscfg.h" +#include "hw/gpio/stm32l4x5_gpio.h" + +#define SYSCFG_MEMRMP 0x00 +#define SYSCFG_CFGR1 0x04 +#define SYSCFG_EXTICR1 0x08 +#define SYSCFG_EXTICR2 0x0C +#define SYSCFG_EXTICR3 0x10 +#define SYSCFG_EXTICR4 0x14 +#define SYSCFG_SCSR 0x18 +#define SYSCFG_CFGR2 0x1C +#define SYSCFG_SWPR 0x20 +#define SYSCFG_SKR 0x24 +#define SYSCFG_SWPR2 0x28 + +/* 00000000_00000000_00000001_00000111 */ +#define ACTIVABLE_BITS_MEMRP 0x00000107 + +/* 11111100_11111111_00000001_00000000 */ +#define ACTIVABLE_BITS_CFGR1 0xFCFF0100 +/* 00000000_00000000_00000000_00000001 */ +#define FIREWALL_DISABLE_CFGR1 0x00000001 + +/* 00000000_00000000_11111111_11111111 */ +#define ACTIVABLE_BITS_EXTICR 0x0000FFFF + +/* 00000000_00000000_00000000_00000011 */ +/* #define ACTIVABLE_BITS_SCSR 0x00000003 */ + +/* 00000000_00000000_00000000_00001111 */ +#define ECC_LOCK_CFGR2 0x0000000F +/* 00000000_00000000_00000001_00000000 */ +#define SRAM2_PARITY_ERROR_FLAG_CFGR2 0x00000100 + +/* 00000000_00000000_00000000_11111111 */ +#define ACTIVABLE_BITS_SKR 0x000000FF + +#define NUM_LINES_PER_EXTICR_REG 4 + +static void stm32l4x5_syscfg_hold_reset(Object *obj) +{ + Stm32l4x5SyscfgState *s = STM32L4X5_SYSCFG(obj); + + s->memrmp = 0x00000000; + s->cfgr1 = 0x7C000001; + s->exticr[0] = 0x00000000; + s->exticr[1] = 0x00000000; + s->exticr[2] = 0x00000000; + s->exticr[3] = 0x00000000; + s->scsr = 0x00000000; + s->cfgr2 = 0x00000000; + s->swpr = 0x00000000; + s->skr = 0x00000000; + s->swpr2 = 0x00000000; +} + +static void stm32l4x5_syscfg_set_irq(void *opaque, int irq, int level) +{ + Stm32l4x5SyscfgState *s = opaque; + const uint8_t gpio = irq / GPIO_NUM_PINS; + const int line = irq % GPIO_NUM_PINS; + + const int exticr_reg = line / NUM_LINES_PER_EXTICR_REG; + const int startbit = (line % NUM_LINES_PER_EXTICR_REG) * 4; + + g_assert(gpio < NUM_GPIOS); + trace_stm32l4x5_syscfg_set_irq(gpio, line, level); + + if (extract32(s->exticr[exticr_reg], startbit, 4) == gpio) { + trace_stm32l4x5_syscfg_forward_exti(line); + qemu_set_irq(s->gpio_out[line], level); + } +} + +static uint64_t stm32l4x5_syscfg_read(void *opaque, hwaddr addr, + unsigned int size) +{ + Stm32l4x5SyscfgState *s = opaque; + + trace_stm32l4x5_syscfg_read(addr); + + switch (addr) { + case SYSCFG_MEMRMP: + return s->memrmp; + case SYSCFG_CFGR1: + return s->cfgr1; + case SYSCFG_EXTICR1...SYSCFG_EXTICR4: + return s->exticr[(addr - SYSCFG_EXTICR1) / 4]; + case SYSCFG_SCSR: + return s->scsr; + case SYSCFG_CFGR2: + return s->cfgr2; + case SYSCFG_SWPR: + return s->swpr; + case SYSCFG_SKR: + return s->skr; + case SYSCFG_SWPR2: + return s->swpr2; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); + return 0; + } +} +static void stm32l4x5_syscfg_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + Stm32l4x5SyscfgState *s = opaque; + + trace_stm32l4x5_syscfg_write(addr, value); + + switch (addr) { + case SYSCFG_MEMRMP: + qemu_log_mask(LOG_UNIMP, + "%s: Changing the memory mapping isn't supported\n", + __func__); + s->memrmp = value & ACTIVABLE_BITS_MEMRP; + return; + case SYSCFG_CFGR1: + qemu_log_mask(LOG_UNIMP, + "%s: Functions in CFGRx aren't supported\n", + __func__); + /* bit 0 (firewall dis.) is cleared by software, set only by reset. */ + s->cfgr1 = (s->cfgr1 & value & FIREWALL_DISABLE_CFGR1) | + (value & ACTIVABLE_BITS_CFGR1); + return; + case SYSCFG_EXTICR1...SYSCFG_EXTICR4: + s->exticr[(addr - SYSCFG_EXTICR1) / 4] = + (value & ACTIVABLE_BITS_EXTICR); + return; + case SYSCFG_SCSR: + qemu_log_mask(LOG_UNIMP, + "%s: Erasing SRAM2 isn't supported\n", + __func__); + /* + * only non reserved bits are : + * bit 0 (write-protected by a passkey), bit 1 (meant to be read) + * so it serves no purpose yet to add : + * s->scsr = value & 0x3; + */ + return; + case SYSCFG_CFGR2: + qemu_log_mask(LOG_UNIMP, + "%s: Functions in CFGRx aren't supported\n", + __func__); + /* bit 8 (SRAM2 PEF) is cleared by software by writing a '1'.*/ + /* bits[3:0] (ECC Lock) are set by software, cleared only by reset.*/ + s->cfgr2 = (s->cfgr2 | (value & ECC_LOCK_CFGR2)) & + ~(value & SRAM2_PARITY_ERROR_FLAG_CFGR2); + return; + case SYSCFG_SWPR: + qemu_log_mask(LOG_UNIMP, + "%s: Write protecting SRAM2 isn't supported\n", + __func__); + /* These bits are set by software and cleared only by reset.*/ + s->swpr |= value; + return; + case SYSCFG_SKR: + qemu_log_mask(LOG_UNIMP, + "%s: Erasing SRAM2 isn't supported\n", + __func__); + s->skr = value & ACTIVABLE_BITS_SKR; + return; + case SYSCFG_SWPR2: + qemu_log_mask(LOG_UNIMP, + "%s: Write protecting SRAM2 isn't supported\n", + __func__); + /* These bits are set by software and cleared only by reset.*/ + s->swpr2 |= value; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); + } +} + +static const MemoryRegionOps stm32l4x5_syscfg_ops = { + .read = stm32l4x5_syscfg_read, + .write = stm32l4x5_syscfg_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .impl.unaligned = false, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static void stm32l4x5_syscfg_init(Object *obj) +{ + Stm32l4x5SyscfgState *s = STM32L4X5_SYSCFG(obj); + + memory_region_init_io(&s->mmio, obj, &stm32l4x5_syscfg_ops, s, + TYPE_STM32L4X5_SYSCFG, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + qdev_init_gpio_in(DEVICE(obj), stm32l4x5_syscfg_set_irq, + GPIO_NUM_PINS * NUM_GPIOS); + qdev_init_gpio_out(DEVICE(obj), s->gpio_out, GPIO_NUM_PINS); +} + +static const VMStateDescription vmstate_stm32l4x5_syscfg = { + .name = TYPE_STM32L4X5_SYSCFG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(memrmp, Stm32l4x5SyscfgState), + VMSTATE_UINT32(cfgr1, Stm32l4x5SyscfgState), + VMSTATE_UINT32_ARRAY(exticr, Stm32l4x5SyscfgState, + SYSCFG_NUM_EXTICR), + VMSTATE_UINT32(scsr, Stm32l4x5SyscfgState), + VMSTATE_UINT32(cfgr2, Stm32l4x5SyscfgState), + VMSTATE_UINT32(swpr, Stm32l4x5SyscfgState), + VMSTATE_UINT32(skr, Stm32l4x5SyscfgState), + VMSTATE_UINT32(swpr2, Stm32l4x5SyscfgState), + VMSTATE_END_OF_LIST() + } +}; + +static void stm32l4x5_syscfg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->vmsd = &vmstate_stm32l4x5_syscfg; + rc->phases.hold = stm32l4x5_syscfg_hold_reset; +} + +static const TypeInfo stm32l4x5_syscfg_info[] = { + { + .name = TYPE_STM32L4X5_SYSCFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Stm32l4x5SyscfgState), + .instance_init = stm32l4x5_syscfg_init, + .class_init = stm32l4x5_syscfg_class_init, + } +}; + +DEFINE_TYPES(stm32l4x5_syscfg_info) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index c5e37b0154..5d241cb40a 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -15,14 +15,37 @@ allwinner_h3_dramctl_write(uint64_t offset, uint64_t data, unsigned size) "Write 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-r40-dramc.c +allwinner_r40_dramc_detect_cells_disable(void) "Disable detect cells" +allwinner_r40_dramc_detect_cells_enable(void) "Enable detect cells" +allwinner_r40_dramc_map_rows(uint8_t row_bits, uint8_t bank_bits, uint8_t col_bits) "DRAM layout: row_bits %d, bank_bits %d, col_bits %d" +allwinner_r40_dramc_offset_to_cell(uint64_t offset, int row, int bank, int col) "offset 0x%" PRIx64 " row %d bank %d col %d" +allwinner_r40_dramc_detect_cell_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 "" +allwinner_r40_dramc_detect_cell_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 "" +allwinner_r40_dramcom_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_dramcom_write(uint64_t offset, uint64_t data, unsigned size) "Write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_dramctl_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_dramctl_write(uint64_t offset, uint64_t data, unsigned size) "Write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_dramphy_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_r40_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 +# allwinner-sramc.c +allwinner_sramc_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 +allwinner_sramc_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 + # avr_power.c avr_power_read(uint8_t value) "power_reduc read value:%u" avr_power_write(uint8_t value) "power_reduc write value:%u" +# axp2xx +axp2xx_rx(uint8_t reg, uint8_t data) "Read reg 0x%" PRIx8 " : 0x%" PRIx8 +axp2xx_select(uint8_t reg) "Accessing reg 0x%" PRIx8 +axp2xx_tx(uint8_t reg, uint8_t data) "Write reg 0x%" PRIx8 " : 0x%" PRIx8 + # 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" @@ -69,6 +92,7 @@ slavio_led_mem_readw(uint32_t ret) "Read diagnostic LED 0x%04x" # aspeed_scu.c aspeed_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_scu_read(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 # mps2-scc.c mps2_scc_read(uint64_t offset, uint64_t data, unsigned size) "MPS2 SCC read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" @@ -91,6 +115,10 @@ msf2_sysreg_write_pll_status(void) "Invalid write to read only PLL status regist imx7_gpr_read(uint64_t offset) "addr 0x%08" PRIx64 imx7_gpr_write(uint64_t offset, uint64_t value) "addr 0x%08" PRIx64 "value 0x%08" PRIx64 +# imx7_snvs.c +imx7_snvs_read(uint64_t offset, uint64_t value, unsigned size) "i.MX SNVS read: offset 0x%08" PRIx64 " value 0x%08" PRIx64 " size %u" +imx7_snvs_write(uint64_t offset, uint64_t value, unsigned size) "i.MX SNVS write: offset 0x%08" PRIx64 " value 0x%08" PRIx64 " size %u" + # mos6522.c mos6522_set_counter(int index, unsigned int val) "T%d.counter=%d" mos6522_get_next_irq_time(uint16_t latch, int64_t d, int64_t delta) "latch=%d counter=0x%"PRIx64 " delta_next=0x%"PRIx64 @@ -131,10 +159,35 @@ 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.c -stm32f4xx_exti_set_irq(int irq, int leve) "Set EXTI: %d to %d" +stm32f4xx_exti_set_irq(int irq, int level) "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 "" +# stm32l4x5_syscfg.c +stm32l4x5_syscfg_set_irq(int gpio, int line, int level) "irq from GPIO: %d, line: %d, level: %d" +stm32l4x5_syscfg_forward_exti(int irq) "irq %d forwarded to EXTI" +stm32l4x5_syscfg_read(uint64_t addr) "reg read: addr: 0x%" PRIx64 " " +stm32l4x5_syscfg_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" + +# stm32l4x5_exti.c +stm32l4x5_exti_set_irq(int irq, int level) "Set EXTI: %d to %d" +stm32l4x5_exti_read(uint64_t addr, uint64_t data) "reg read: addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" +stm32l4x5_exti_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" + +# stm32l4x5_rcc.c +stm32l4x5_rcc_read(uint64_t addr, uint32_t data) "RCC: Read <0x%" PRIx64 "> -> 0x%" PRIx32 +stm32l4x5_rcc_write(uint64_t addr, uint32_t data) "RCC: Write <0x%" PRIx64 "> <- 0x%" PRIx32 +stm32l4x5_rcc_mux_enable(uint32_t mux_id) "RCC: Mux %d enabled" +stm32l4x5_rcc_mux_disable(uint32_t mux_id) "RCC: Mux %d disabled" +stm32l4x5_rcc_mux_set_factor(uint32_t mux_id, uint32_t old_multiplier, uint32_t new_multiplier, uint32_t old_divider, uint32_t new_divider) "RCC: Mux %d factor changed: multiplier (%u -> %u), divider (%u -> %u)" +stm32l4x5_rcc_mux_set_src(uint32_t mux_id, uint32_t old_src, uint32_t new_src) "RCC: Mux %d source changed: from %u to %u" +stm32l4x5_rcc_mux_update(uint32_t mux_id, uint32_t src, uint64_t src_freq, uint32_t multiplier, uint32_t divider) "RCC: Mux %d src %d update: src_freq %" PRIu64 " multiplier %" PRIu32 " divider %" PRIu32 +stm32l4x5_rcc_pll_set_vco_multiplier(uint32_t pll_id, uint32_t old_multiplier, uint32_t new_multiplier) "RCC: PLL %u: vco_multiplier changed (%u -> %u)" +stm32l4x5_rcc_pll_channel_enable(uint32_t pll_id, uint32_t channel_id) "RCC: PLL %u, channel %u enabled" +stm32l4x5_rcc_pll_channel_disable(uint32_t pll_id, uint32_t channel_id) "RCC: PLL %u, channel %u disabled" +stm32l4x5_rcc_pll_set_channel_divider(uint32_t pll_id, uint32_t channel_id, uint32_t old_divider, uint32_t new_divider) "RCC: PLL %u, channel %u: divider changed (%u -> %u)" +stm32l4x5_rcc_pll_update(uint32_t pll_id, uint32_t channel_id, uint64_t vco_freq, uint64_t old_freq, uint64_t new_freq) "RCC: PLL %d channel %d update: vco_freq %" PRIu64 " old_freq %" PRIu64 " new_freq %" PRIu64 + # 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" @@ -168,6 +221,21 @@ iotkit_secctl_s_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit Sec iotkit_secctl_ns_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs read: offset 0x%x data 0x%" PRIx64 " size %u" iotkit_secctl_ns_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs write: offset 0x%x data 0x%" PRIx64 " size %u" +# imx6_ccm.c +imx6_analog_get_periph_clk(uint32_t freq) "freq = %u Hz" +imx6_analog_get_pll2_clk(uint32_t freq) "freq = %u Hz" +imx6_analog_get_pll2_pfd0_clk(uint32_t freq) "freq = %u Hz" +imx6_analog_get_pll2_pfd2_clk(uint32_t freq) "freq = %u Hz" +imx6_analog_read(const char *reg, uint32_t value) "reg[%s] => 0x%" PRIx32 +imx6_analog_write(const char *reg, uint32_t value) "reg[%s] <= 0x%" PRIx32 +imx6_ccm_get_ahb_clk(uint32_t freq) "freq = %u Hz" +imx6_ccm_get_ipg_clk(uint32_t freq) "freq = %u Hz" +imx6_ccm_get_per_clk(uint32_t freq) "freq = %u Hz" +imx6_ccm_get_clock_frequency(unsigned clock, uint32_t freq) "(Clock = %d) = %u" +imx6_ccm_read(const char *reg, uint32_t value) "reg[%s] => 0x%" PRIx32 +imx6_ccm_reset(void) "" +imx6_ccm_write(const char *reg, uint32_t value) "reg[%s] <= 0x%" PRIx32 + # imx6ul_ccm.c ccm_entry(void) "" ccm_freq(uint32_t freq) "freq = %d" @@ -175,6 +243,10 @@ ccm_clock_freq(uint32_t clock, uint32_t freq) "(Clock = %d) = %d" ccm_read_reg(const char *reg_name, uint32_t value) "reg[%s] <= 0x%" PRIx32 ccm_write_reg(const char *reg_name, uint32_t value) "reg[%s] => 0x%" PRIx32 +# imx7_src.c +imx7_src_read(const char *reg_name, uint32_t value) "reg[%s] => 0x%" PRIx32 +imx7_src_write(const char *reg_name, uint32_t value) "reg[%s] <= 0x%" PRIx32 + # iotkit-sysinfo.c iotkit_sysinfo_read(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysInfo read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" iotkit_sysinfo_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysInfo write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" @@ -209,6 +281,11 @@ aspeed_i3c_device_write(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C aspeed_sdmc_write(uint64_t reg, uint64_t data) "reg @0x%" PRIx64 " data: 0x%" PRIx64 aspeed_sdmc_read(uint64_t reg, uint64_t data) "reg @0x%" PRIx64 " data: 0x%" PRIx64 +# aspeed_peci.c +aspeed_peci_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 +aspeed_peci_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 +aspeed_peci_raise_interrupt(uint32_t ctrl, uint32_t status) "ctrl 0x%" PRIx32 " status 0x%" PRIx32 + # bcm2835_property.c bcm2835_mbox_property(uint32_t tag, uint32_t bufsize, size_t resplen) "mbox property tag:0x%08x in_sz:%u out_sz:%zu" @@ -238,20 +315,18 @@ via1_rtc_cmd_pram_sect_write(int sector, int offset, int addr, int value) "secto via1_adb_send(const char *state, uint8_t data, const char *vadbint) "state %s data=0x%02x vADBInt=%s" via1_adb_receive(const char *state, uint8_t data, const char *vadbint, int status, int index, int size) "state %s data=0x%02x vADBInt=%s status=0x%x index=%d size=%d" via1_adb_poll(uint8_t data, const char *vadbint, int status, int index, int size) "data=0x%02x vADBInt=%s status=0x%x index=%d size=%d" +via1_adb_netbsd_enum_hack(void) "using NetBSD enum hack" via1_auxmode(int mode) "setting auxmode to %d" +via1_timer_hack_state(int state) "setting timer_hack_state to %d" # grlib_ahb_apb_pnp.c -grlib_ahb_pnp_read(uint64_t addr, uint32_t value) "AHB PnP read addr:0x%03"PRIx64" data:0x%08x" -grlib_apb_pnp_read(uint64_t addr, uint32_t value) "APB PnP read addr:0x%03"PRIx64" data:0x%08x" +grlib_ahb_pnp_read(uint64_t addr, unsigned size, uint32_t value) "AHB PnP read addr:0x%03"PRIx64" size:%u data:0x%08x" +grlib_apb_pnp_read(uint64_t addr, unsigned size, uint32_t value) "APB PnP read addr:0x%03"PRIx64" size:%u data:0x%08x" # led.c led_set_intensity(const char *color, const char *desc, uint8_t intensity_percent) "LED desc:'%s' color:%s intensity: %u%%" led_change_intensity(const char *color, const char *desc, uint8_t old_intensity_percent, uint8_t new_intensity_percent) "LED desc:'%s' color:%s intensity %u%% -> %u%%" -# pca9552.c -pca955x_gpio_status(const char *description, const char *buf) "%s GPIOs 0-15 [%s]" -pca955x_gpio_change(const char *description, unsigned id, unsigned prev_state, unsigned current_state) "%s GPIO id:%u status: %u -> %u" - # bcm2835_cprman.c bcm2835_cprman_read(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64 bcm2835_cprman_write(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64 @@ -268,3 +343,11 @@ virt_ctrl_instance_init(void *dev) "ctrl: %p" 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" + +# djmemc.c +djmemc_read(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRIx64" size=%u" +djmemc_write(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRIx64" size=%u" + +# iosb.c +iosb_read(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRIx64" size=%u" +iosb_write(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRIx64" size=%u" diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index 30481e1c90..92b994919b 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -574,7 +574,7 @@ static const VMStateDescription tz_mpc_vmstate = { .version_id = 1, .minimum_version_id = 1, .post_load = tz_mpc_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ctrl, TZMPC), VMSTATE_UINT32(blk_idx, TZMPC), VMSTATE_UINT32(int_stat, TZMPC), diff --git a/hw/misc/tz-msc.c b/hw/misc/tz-msc.c index acbe94400b..de5a3126cc 100644 --- a/hw/misc/tz-msc.c +++ b/hw/misc/tz-msc.c @@ -269,7 +269,7 @@ static const VMStateDescription tz_msc_vmstate = { .name = "tz-msc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(cfg_nonsec, TZMSC), VMSTATE_BOOL(cfg_sec_resp, TZMSC), VMSTATE_BOOL(irq_clear, TZMSC), diff --git a/hw/misc/tz-ppc.c b/hw/misc/tz-ppc.c index 36495c68e7..6450778720 100644 --- a/hw/misc/tz-ppc.c +++ b/hw/misc/tz-ppc.c @@ -290,7 +290,7 @@ static const VMStateDescription tz_ppc_vmstate = { .name = "tz-ppc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL_ARRAY(cfg_nonsec, TZPPC, 16), VMSTATE_BOOL_ARRAY(cfg_ap, TZPPC, 16), VMSTATE_BOOL(cfg_sec_resp, TZPPC), diff --git a/hw/misc/virt_ctrl.c b/hw/misc/virt_ctrl.c index e75d1e7e17..1a6c744bac 100644 --- a/hw/misc/virt_ctrl.c +++ b/hw/misc/virt_ctrl.c @@ -108,7 +108,7 @@ static const VMStateDescription vmstate_virt_ctrl = { .name = "virt-ctrl", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(irq_enabled, VirtCtrlState), VMSTATE_END_OF_LIST() } diff --git a/hw/misc/vmcoreinfo.c b/hw/misc/vmcoreinfo.c index a9d718fc23..833773ade5 100644 --- a/hw/misc/vmcoreinfo.c +++ b/hw/misc/vmcoreinfo.c @@ -73,7 +73,7 @@ static const VMStateDescription vmstate_vmcoreinfo = { .name = "vmcoreinfo", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(has_vmcoreinfo, VMCoreInfoState), VMSTATE_UINT16(vmcoreinfo.host_format, VMCoreInfoState), VMSTATE_UINT16(vmcoreinfo.guest_format, VMCoreInfoState), diff --git a/hw/misc/xlnx-cfi-if.c b/hw/misc/xlnx-cfi-if.c new file mode 100644 index 0000000000..c45f05c4aa --- /dev/null +++ b/hw/misc/xlnx-cfi-if.c @@ -0,0 +1,34 @@ +/* + * Xilinx CFI interface + * + * Copyright (C) 2023, Advanced Micro Devices, Inc. + * + * Written by Francisco Iglesias + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "hw/misc/xlnx-cfi-if.h" + +void xlnx_cfi_transfer_packet(XlnxCfiIf *cfi_if, XlnxCfiPacket *pkt) +{ + XlnxCfiIfClass *xcic = XLNX_CFI_IF_GET_CLASS(cfi_if); + + if (xcic->cfi_transfer_packet) { + xcic->cfi_transfer_packet(cfi_if, pkt); + } +} + +static const TypeInfo xlnx_cfi_if_info = { + .name = TYPE_XLNX_CFI_IF, + .parent = TYPE_INTERFACE, + .class_size = sizeof(XlnxCfiIfClass), +}; + +static void xlnx_cfi_if_register_types(void) +{ + type_register_static(&xlnx_cfi_if_info); +} + +type_init(xlnx_cfi_if_register_types) + diff --git a/hw/misc/xlnx-versal-cframe-reg.c b/hw/misc/xlnx-versal-cframe-reg.c new file mode 100644 index 0000000000..a6ab287b01 --- /dev/null +++ b/hw/misc/xlnx-versal-cframe-reg.c @@ -0,0 +1,858 @@ +/* + * QEMU model of the Configuration Frame Control module + * + * Copyright (C) 2023, Advanced Micro Devices, Inc. + * + * Written by Francisco Iglesias + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/register.h" +#include "hw/registerfields.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "hw/irq.h" +#include "hw/misc/xlnx-versal-cframe-reg.h" + +#ifndef XLNX_VERSAL_CFRAME_REG_ERR_DEBUG +#define XLNX_VERSAL_CFRAME_REG_ERR_DEBUG 0 +#endif + +#define KEYHOLE_STREAM_4K (4 * KiB) +#define N_WORDS_128BIT 4 + +#define MAX_BLOCKTYPE 6 +#define MAX_BLOCKTYPE_FRAMES 0xFFFFF + +enum { + CFRAME_CMD_WCFG = 1, + CFRAME_CMD_ROWON = 2, + CFRAME_CMD_ROWOFF = 3, + CFRAME_CMD_RCFG = 4, + CFRAME_CMD_DLPARK = 5, +}; + +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 cfrm_imr_update_irq(XlnxVersalCFrameReg *s) +{ + bool pending = s->regs[R_CFRM_ISR0] & ~s->regs[R_CFRM_IMR0]; + qemu_set_irq(s->irq_cfrm_imr, pending); +} + +static void cfrm_isr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); + cfrm_imr_update_irq(s); +} + +static uint64_t cfrm_ier_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); + + s->regs[R_CFRM_IMR0] &= ~s->regs[R_CFRM_IER0]; + s->regs[R_CFRM_IER0] = 0; + cfrm_imr_update_irq(s); + return 0; +} + +static uint64_t cfrm_idr_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); + + s->regs[R_CFRM_IMR0] |= s->regs[R_CFRM_IDR0]; + s->regs[R_CFRM_IDR0] = 0; + cfrm_imr_update_irq(s); + return 0; +} + +static uint64_t cfrm_itr_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); + + s->regs[R_CFRM_ISR0] |= s->regs[R_CFRM_ITR0]; + s->regs[R_CFRM_ITR0] = 0; + cfrm_imr_update_irq(s); + return 0; +} + +static void cframe_incr_far(XlnxVersalCFrameReg *s) +{ + uint32_t faddr = ARRAY_FIELD_EX32(s->regs, FAR0, FRAME_ADDR); + uint32_t blktype = ARRAY_FIELD_EX32(s->regs, FAR0, BLOCKTYPE); + + assert(blktype <= MAX_BLOCKTYPE); + + faddr++; + if (faddr > s->cfg.blktype_num_frames[blktype]) { + /* Restart from 0 and increment block type */ + faddr = 0; + blktype++; + + assert(blktype <= MAX_BLOCKTYPE); + + ARRAY_FIELD_DP32(s->regs, FAR0, BLOCKTYPE, blktype); + } + + ARRAY_FIELD_DP32(s->regs, FAR0, FRAME_ADDR, faddr); +} + +static void cfrm_fdri_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); + + if (s->row_configured && s->rowon && s->wcfg) { + + if (fifo32_num_free(&s->new_f_data) >= N_WORDS_128BIT) { + fifo32_push(&s->new_f_data, s->regs[R_FDRI0]); + fifo32_push(&s->new_f_data, s->regs[R_FDRI1]); + fifo32_push(&s->new_f_data, s->regs[R_FDRI2]); + fifo32_push(&s->new_f_data, s->regs[R_FDRI3]); + } + + if (fifo32_is_full(&s->new_f_data)) { + uint32_t addr = extract32(s->regs[R_FAR0], 0, 23); + XlnxCFrame *f = g_new(XlnxCFrame, 1); + + for (int i = 0; i < FRAME_NUM_WORDS; i++) { + f->data[i] = fifo32_pop(&s->new_f_data); + } + + g_tree_replace(s->cframes, GUINT_TO_POINTER(addr), f); + + cframe_incr_far(s); + + fifo32_reset(&s->new_f_data); + } + } +} + +static void cfrm_readout_frames(XlnxVersalCFrameReg *s, uint32_t start_addr, + uint32_t end_addr) +{ + /* + * NB: when our minimum glib version is at least 2.68 we can improve the + * performance of the cframe traversal by using g_tree_lookup_node and + * g_tree_node_next (instead of calling g_tree_lookup for finding each + * cframe). + */ + for (uint32_t addr = start_addr; addr < end_addr; addr++) { + XlnxCFrame *f = g_tree_lookup(s->cframes, GUINT_TO_POINTER(addr)); + + /* Transmit the data if a frame was found */ + if (f) { + for (int i = 0; i < FRAME_NUM_WORDS; i += 4) { + XlnxCfiPacket pkt = {}; + + pkt.data[0] = f->data[i]; + pkt.data[1] = f->data[i + 1]; + pkt.data[2] = f->data[i + 2]; + pkt.data[3] = f->data[i + 3]; + + if (s->cfg.cfu_fdro) { + xlnx_cfi_transfer_packet(s->cfg.cfu_fdro, &pkt); + } + } + } + } +} + +static void cfrm_frcnt_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); + + if (s->row_configured && s->rowon && s->rcfg) { + uint32_t start_addr = extract32(s->regs[R_FAR0], 0, 23); + uint32_t end_addr = start_addr + s->regs[R_FRCNT0] / FRAME_NUM_QWORDS; + + cfrm_readout_frames(s, start_addr, end_addr); + } +} + +static void cfrm_cmd_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); + + if (s->row_configured) { + uint8_t cmd = ARRAY_FIELD_EX32(s->regs, CMD0, CMD); + + switch (cmd) { + case CFRAME_CMD_WCFG: + s->wcfg = true; + break; + case CFRAME_CMD_ROWON: + s->rowon = true; + break; + case CFRAME_CMD_ROWOFF: + s->rowon = false; + break; + case CFRAME_CMD_RCFG: + s->rcfg = true; + break; + case CFRAME_CMD_DLPARK: + s->wcfg = false; + s->rcfg = false; + break; + default: + break; + }; + } +} + +static uint64_t cfrm_last_frame_bot_post_read(RegisterInfo *reg, + uint64_t val64) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); + uint64_t val = 0; + + switch (reg->access->addr) { + case A_LAST_FRAME_BOT0: + val = FIELD_DP32(val, LAST_FRAME_BOT0, BLOCKTYPE1_LAST_FRAME_LSB, + s->cfg.blktype_num_frames[1]); + val = FIELD_DP32(val, LAST_FRAME_BOT0, BLOCKTYPE0_LAST_FRAME, + s->cfg.blktype_num_frames[0]); + break; + case A_LAST_FRAME_BOT1: + val = FIELD_DP32(val, LAST_FRAME_BOT1, BLOCKTYPE3_LAST_FRAME_LSB, + s->cfg.blktype_num_frames[3]); + val = FIELD_DP32(val, LAST_FRAME_BOT1, BLOCKTYPE2_LAST_FRAME, + s->cfg.blktype_num_frames[2]); + val = FIELD_DP32(val, LAST_FRAME_BOT1, BLOCKTYPE1_LAST_FRAME_MSB, + (s->cfg.blktype_num_frames[1] >> 12)); + break; + case A_LAST_FRAME_BOT2: + val = FIELD_DP32(val, LAST_FRAME_BOT2, BLOCKTYPE3_LAST_FRAME_MSB, + (s->cfg.blktype_num_frames[3] >> 4)); + break; + case A_LAST_FRAME_BOT3: + default: + break; + } + + return val; +} + +static uint64_t cfrm_last_frame_top_post_read(RegisterInfo *reg, + uint64_t val64) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); + uint64_t val = 0; + + switch (reg->access->addr) { + case A_LAST_FRAME_TOP0: + val = FIELD_DP32(val, LAST_FRAME_TOP0, BLOCKTYPE5_LAST_FRAME_LSB, + s->cfg.blktype_num_frames[5]); + val = FIELD_DP32(val, LAST_FRAME_TOP0, BLOCKTYPE4_LAST_FRAME, + s->cfg.blktype_num_frames[4]); + break; + case A_LAST_FRAME_TOP1: + val = FIELD_DP32(val, LAST_FRAME_TOP1, BLOCKTYPE6_LAST_FRAME, + s->cfg.blktype_num_frames[6]); + val = FIELD_DP32(val, LAST_FRAME_TOP1, BLOCKTYPE5_LAST_FRAME_MSB, + (s->cfg.blktype_num_frames[5] >> 12)); + break; + case A_LAST_FRAME_TOP2: + case A_LAST_FRAME_BOT3: + default: + break; + } + + return val; +} + +static void cfrm_far_sfr_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(reg->opaque); + + if (s->row_configured && s->rowon && s->rcfg) { + uint32_t start_addr = extract32(s->regs[R_FAR_SFR0], 0, 23); + + /* Readback 1 frame */ + cfrm_readout_frames(s, start_addr, start_addr + 1); + } +} + +static const RegisterAccessInfo cframe_reg_regs_info[] = { + { .name = "CRC0", .addr = A_CRC0, + .rsvd = 0x00000000, + },{ .name = "CRC1", .addr = A_CRC0, + .rsvd = 0xffffffff, + },{ .name = "CRC2", .addr = A_CRC0, + .rsvd = 0xffffffff, + },{ .name = "CRC3", .addr = A_CRC0, + .rsvd = 0xffffffff, + },{ .name = "FAR0", .addr = A_FAR0, + .rsvd = 0xfe000000, + },{ .name = "FAR1", .addr = A_FAR1, + .rsvd = 0xffffffff, + },{ .name = "FAR2", .addr = A_FAR2, + .rsvd = 0xffffffff, + },{ .name = "FAR3", .addr = A_FAR3, + .rsvd = 0xffffffff, + },{ .name = "FAR_SFR0", .addr = A_FAR_SFR0, + .rsvd = 0xff800000, + },{ .name = "FAR_SFR1", .addr = A_FAR_SFR1, + .rsvd = 0xffffffff, + },{ .name = "FAR_SFR2", .addr = A_FAR_SFR2, + .rsvd = 0xffffffff, + },{ .name = "FAR_SFR3", .addr = A_FAR_SFR3, + .rsvd = 0xffffffff, + .post_write = cfrm_far_sfr_post_write, + },{ .name = "FDRI0", .addr = A_FDRI0, + },{ .name = "FDRI1", .addr = A_FDRI1, + },{ .name = "FDRI2", .addr = A_FDRI2, + },{ .name = "FDRI3", .addr = A_FDRI3, + .post_write = cfrm_fdri_post_write, + },{ .name = "FRCNT0", .addr = A_FRCNT0, + .rsvd = 0x00000000, + },{ .name = "FRCNT1", .addr = A_FRCNT1, + .rsvd = 0xffffffff, + },{ .name = "FRCNT2", .addr = A_FRCNT2, + .rsvd = 0xffffffff, + },{ .name = "FRCNT3", .addr = A_FRCNT3, + .rsvd = 0xffffffff, + .post_write = cfrm_frcnt_post_write + },{ .name = "CMD0", .addr = A_CMD0, + .rsvd = 0xffffffe0, + },{ .name = "CMD1", .addr = A_CMD1, + .rsvd = 0xffffffff, + },{ .name = "CMD2", .addr = A_CMD2, + .rsvd = 0xffffffff, + },{ .name = "CMD3", .addr = A_CMD3, + .rsvd = 0xffffffff, + .post_write = cfrm_cmd_post_write + },{ .name = "CR_MASK0", .addr = A_CR_MASK0, + .rsvd = 0x00000000, + },{ .name = "CR_MASK1", .addr = A_CR_MASK1, + .rsvd = 0x00000000, + },{ .name = "CR_MASK2", .addr = A_CR_MASK2, + .rsvd = 0x00000000, + },{ .name = "CR_MASK3", .addr = A_CR_MASK3, + .rsvd = 0xffffffff, + },{ .name = "CTL0", .addr = A_CTL0, + .rsvd = 0xfffffff8, + },{ .name = "CTL1", .addr = A_CTL1, + .rsvd = 0xffffffff, + },{ .name = "CTL2", .addr = A_CTL2, + .rsvd = 0xffffffff, + },{ .name = "CTL3", .addr = A_CTL3, + .rsvd = 0xffffffff, + },{ .name = "CFRM_ISR0", .addr = A_CFRM_ISR0, + .rsvd = 0xffc04000, + .w1c = 0x3bfff, + },{ .name = "CFRM_ISR1", .addr = A_CFRM_ISR1, + .rsvd = 0xffffffff, + },{ .name = "CFRM_ISR2", .addr = A_CFRM_ISR2, + .rsvd = 0xffffffff, + },{ .name = "CFRM_ISR3", .addr = A_CFRM_ISR3, + .rsvd = 0xffffffff, + .post_write = cfrm_isr_postw, + },{ .name = "CFRM_IMR0", .addr = A_CFRM_IMR0, + .rsvd = 0xffc04000, + .ro = 0xfffff, + .reset = 0x3bfff, + },{ .name = "CFRM_IMR1", .addr = A_CFRM_IMR1, + .rsvd = 0xffffffff, + },{ .name = "CFRM_IMR2", .addr = A_CFRM_IMR2, + .rsvd = 0xffffffff, + },{ .name = "CFRM_IMR3", .addr = A_CFRM_IMR3, + .rsvd = 0xffffffff, + },{ .name = "CFRM_IER0", .addr = A_CFRM_IER0, + .rsvd = 0xffc04000, + },{ .name = "CFRM_IER1", .addr = A_CFRM_IER1, + .rsvd = 0xffffffff, + },{ .name = "CFRM_IER2", .addr = A_CFRM_IER2, + .rsvd = 0xffffffff, + },{ .name = "CFRM_IER3", .addr = A_CFRM_IER3, + .rsvd = 0xffffffff, + .pre_write = cfrm_ier_prew, + },{ .name = "CFRM_IDR0", .addr = A_CFRM_IDR0, + .rsvd = 0xffc04000, + },{ .name = "CFRM_IDR1", .addr = A_CFRM_IDR1, + .rsvd = 0xffffffff, + },{ .name = "CFRM_IDR2", .addr = A_CFRM_IDR2, + .rsvd = 0xffffffff, + },{ .name = "CFRM_IDR3", .addr = A_CFRM_IDR3, + .rsvd = 0xffffffff, + .pre_write = cfrm_idr_prew, + },{ .name = "CFRM_ITR0", .addr = A_CFRM_ITR0, + .rsvd = 0xffc04000, + },{ .name = "CFRM_ITR1", .addr = A_CFRM_ITR1, + .rsvd = 0xffffffff, + },{ .name = "CFRM_ITR2", .addr = A_CFRM_ITR2, + .rsvd = 0xffffffff, + },{ .name = "CFRM_ITR3", .addr = A_CFRM_ITR3, + .rsvd = 0xffffffff, + .pre_write = cfrm_itr_prew, + },{ .name = "SEU_SYNDRM00", .addr = A_SEU_SYNDRM00, + },{ .name = "SEU_SYNDRM01", .addr = A_SEU_SYNDRM01, + },{ .name = "SEU_SYNDRM02", .addr = A_SEU_SYNDRM02, + },{ .name = "SEU_SYNDRM03", .addr = A_SEU_SYNDRM03, + },{ .name = "SEU_SYNDRM10", .addr = A_SEU_SYNDRM10, + },{ .name = "SEU_SYNDRM11", .addr = A_SEU_SYNDRM11, + },{ .name = "SEU_SYNDRM12", .addr = A_SEU_SYNDRM12, + },{ .name = "SEU_SYNDRM13", .addr = A_SEU_SYNDRM13, + },{ .name = "SEU_SYNDRM20", .addr = A_SEU_SYNDRM20, + },{ .name = "SEU_SYNDRM21", .addr = A_SEU_SYNDRM21, + },{ .name = "SEU_SYNDRM22", .addr = A_SEU_SYNDRM22, + },{ .name = "SEU_SYNDRM23", .addr = A_SEU_SYNDRM23, + },{ .name = "SEU_SYNDRM30", .addr = A_SEU_SYNDRM30, + },{ .name = "SEU_SYNDRM31", .addr = A_SEU_SYNDRM31, + },{ .name = "SEU_SYNDRM32", .addr = A_SEU_SYNDRM32, + },{ .name = "SEU_SYNDRM33", .addr = A_SEU_SYNDRM33, + },{ .name = "SEU_VIRTUAL_SYNDRM0", .addr = A_SEU_VIRTUAL_SYNDRM0, + },{ .name = "SEU_VIRTUAL_SYNDRM1", .addr = A_SEU_VIRTUAL_SYNDRM1, + },{ .name = "SEU_VIRTUAL_SYNDRM2", .addr = A_SEU_VIRTUAL_SYNDRM2, + },{ .name = "SEU_VIRTUAL_SYNDRM3", .addr = A_SEU_VIRTUAL_SYNDRM3, + },{ .name = "SEU_CRC0", .addr = A_SEU_CRC0, + },{ .name = "SEU_CRC1", .addr = A_SEU_CRC1, + },{ .name = "SEU_CRC2", .addr = A_SEU_CRC2, + },{ .name = "SEU_CRC3", .addr = A_SEU_CRC3, + },{ .name = "CFRAME_FAR_BOT0", .addr = A_CFRAME_FAR_BOT0, + },{ .name = "CFRAME_FAR_BOT1", .addr = A_CFRAME_FAR_BOT1, + },{ .name = "CFRAME_FAR_BOT2", .addr = A_CFRAME_FAR_BOT2, + },{ .name = "CFRAME_FAR_BOT3", .addr = A_CFRAME_FAR_BOT3, + },{ .name = "CFRAME_FAR_TOP0", .addr = A_CFRAME_FAR_TOP0, + },{ .name = "CFRAME_FAR_TOP1", .addr = A_CFRAME_FAR_TOP1, + },{ .name = "CFRAME_FAR_TOP2", .addr = A_CFRAME_FAR_TOP2, + },{ .name = "CFRAME_FAR_TOP3", .addr = A_CFRAME_FAR_TOP3, + },{ .name = "LAST_FRAME_BOT0", .addr = A_LAST_FRAME_BOT0, + .ro = 0xffffffff, + .post_read = cfrm_last_frame_bot_post_read, + },{ .name = "LAST_FRAME_BOT1", .addr = A_LAST_FRAME_BOT1, + .ro = 0xffffffff, + .post_read = cfrm_last_frame_bot_post_read, + },{ .name = "LAST_FRAME_BOT2", .addr = A_LAST_FRAME_BOT2, + .ro = 0xffffffff, + .post_read = cfrm_last_frame_bot_post_read, + },{ .name = "LAST_FRAME_BOT3", .addr = A_LAST_FRAME_BOT3, + .ro = 0xffffffff, + .post_read = cfrm_last_frame_bot_post_read, + },{ .name = "LAST_FRAME_TOP0", .addr = A_LAST_FRAME_TOP0, + .ro = 0xffffffff, + .post_read = cfrm_last_frame_top_post_read, + },{ .name = "LAST_FRAME_TOP1", .addr = A_LAST_FRAME_TOP1, + .ro = 0xffffffff, + .post_read = cfrm_last_frame_top_post_read, + },{ .name = "LAST_FRAME_TOP2", .addr = A_LAST_FRAME_TOP2, + .ro = 0xffffffff, + .post_read = cfrm_last_frame_top_post_read, + },{ .name = "LAST_FRAME_TOP3", .addr = A_LAST_FRAME_TOP3, + .ro = 0xffffffff, + .post_read = cfrm_last_frame_top_post_read, + } +}; + +static void cframe_reg_cfi_transfer_packet(XlnxCfiIf *cfi_if, + XlnxCfiPacket *pkt) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(cfi_if); + uint64_t we = MAKE_64BIT_MASK(0, 4 * 8); + + if (!s->row_configured) { + return; + } + + switch (pkt->reg_addr) { + case CFRAME_FAR: + s->regs[R_FAR0] = pkt->data[0]; + break; + case CFRAME_SFR: + s->regs[R_FAR_SFR0] = pkt->data[0]; + register_write(&s->regs_info[R_FAR_SFR3], 0, + we, object_get_typename(OBJECT(s)), + XLNX_VERSAL_CFRAME_REG_ERR_DEBUG); + break; + case CFRAME_FDRI: + s->regs[R_FDRI0] = pkt->data[0]; + s->regs[R_FDRI1] = pkt->data[1]; + s->regs[R_FDRI2] = pkt->data[2]; + register_write(&s->regs_info[R_FDRI3], pkt->data[3], + we, object_get_typename(OBJECT(s)), + XLNX_VERSAL_CFRAME_REG_ERR_DEBUG); + break; + case CFRAME_CMD: + ARRAY_FIELD_DP32(s->regs, CMD0, CMD, pkt->data[0]); + + register_write(&s->regs_info[R_CMD3], 0, + we, object_get_typename(OBJECT(s)), + XLNX_VERSAL_CFRAME_REG_ERR_DEBUG); + break; + default: + break; + } +} + +static uint64_t cframe_reg_fdri_read(void *opaque, hwaddr addr, unsigned size) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported read from addr=%" + HWADDR_PRIx "\n", __func__, addr); + return 0; +} + +static void cframe_reg_fdri_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(opaque); + uint32_t wfifo[WFIFO_SZ]; + + if (update_wfifo(addr, value, s->wfifo, wfifo)) { + uint64_t we = MAKE_64BIT_MASK(0, 4 * 8); + + s->regs[R_FDRI0] = wfifo[0]; + s->regs[R_FDRI1] = wfifo[1]; + s->regs[R_FDRI2] = wfifo[2]; + register_write(&s->regs_info[R_FDRI3], wfifo[3], + we, object_get_typename(OBJECT(s)), + XLNX_VERSAL_CFRAME_REG_ERR_DEBUG); + } +} + +static void cframe_reg_reset_enter(Object *obj, ResetType type) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(obj); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + register_reset(&s->regs_info[i]); + } + memset(s->wfifo, 0, WFIFO_SZ * sizeof(uint32_t)); + fifo32_reset(&s->new_f_data); + + if (g_tree_nnodes(s->cframes)) { + /* + * Take a reference so when g_tree_destroy() unrefs it we keep the + * GTree and only destroy its contents. NB: when our minimum + * glib version is at least 2.70 we could use g_tree_remove_all(). + */ + g_tree_ref(s->cframes); + g_tree_destroy(s->cframes); + } +} + +static void cframe_reg_reset_hold(Object *obj) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(obj); + + cfrm_imr_update_irq(s); +} + +static const MemoryRegionOps cframe_reg_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static const MemoryRegionOps cframe_reg_fdri_ops = { + .read = cframe_reg_fdri_read, + .write = cframe_reg_fdri_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint64_t cframes_bcast_reg_read(void *opaque, hwaddr addr, unsigned size) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported read from addr=%" + HWADDR_PRIx "\n", __func__, addr); + return 0; +} + +static void cframes_bcast_write(XlnxVersalCFrameBcastReg *s, uint8_t reg_addr, + uint32_t *wfifo) +{ + XlnxCfiPacket pkt = { + .reg_addr = reg_addr, + .data[0] = wfifo[0], + .data[1] = wfifo[1], + .data[2] = wfifo[2], + .data[3] = wfifo[3] + }; + + for (int i = 0; i < ARRAY_SIZE(s->cfg.cframe); i++) { + if (s->cfg.cframe[i]) { + xlnx_cfi_transfer_packet(s->cfg.cframe[i], &pkt); + } + } +} + +static void cframes_bcast_reg_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + XlnxVersalCFrameBcastReg *s = XLNX_VERSAL_CFRAME_BCAST_REG(opaque); + uint32_t wfifo[WFIFO_SZ]; + + if (update_wfifo(addr, value, s->wfifo, wfifo)) { + uint8_t reg_addr = extract32(addr, 4, 6); + + cframes_bcast_write(s, reg_addr, wfifo); + } +} + +static uint64_t cframes_bcast_fdri_read(void *opaque, hwaddr addr, + unsigned size) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported read from addr=%" + HWADDR_PRIx "\n", __func__, addr); + return 0; +} + +static void cframes_bcast_fdri_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + XlnxVersalCFrameBcastReg *s = XLNX_VERSAL_CFRAME_BCAST_REG(opaque); + uint32_t wfifo[WFIFO_SZ]; + + if (update_wfifo(addr, value, s->wfifo, wfifo)) { + cframes_bcast_write(s, CFRAME_FDRI, wfifo); + } +} + +static const MemoryRegionOps cframes_bcast_reg_reg_ops = { + .read = cframes_bcast_reg_read, + .write = cframes_bcast_reg_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static const MemoryRegionOps cframes_bcast_reg_fdri_ops = { + .read = cframes_bcast_fdri_read, + .write = cframes_bcast_fdri_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void cframe_reg_realize(DeviceState *dev, Error **errp) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(dev); + + for (int i = 0; i < ARRAY_SIZE(s->cfg.blktype_num_frames); i++) { + if (s->cfg.blktype_num_frames[i] > MAX_BLOCKTYPE_FRAMES) { + error_setg(errp, + "blktype-frames%d > 0xFFFFF (max frame per block)", + i); + return; + } + if (s->cfg.blktype_num_frames[i]) { + s->row_configured = true; + } + } +} + +static void cframe_reg_init(Object *obj) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; + + memory_region_init(&s->iomem, obj, TYPE_XLNX_VERSAL_CFRAME_REG, + CFRAME_REG_R_MAX * 4); + reg_array = + register_init_block32(DEVICE(obj), cframe_reg_regs_info, + ARRAY_SIZE(cframe_reg_regs_info), + s->regs_info, s->regs, + &cframe_reg_ops, + XLNX_VERSAL_CFRAME_REG_ERR_DEBUG, + CFRAME_REG_R_MAX * 4); + memory_region_add_subregion(&s->iomem, + 0x0, + ®_array->mem); + sysbus_init_mmio(sbd, &s->iomem); + memory_region_init_io(&s->iomem_fdri, obj, &cframe_reg_fdri_ops, s, + TYPE_XLNX_VERSAL_CFRAME_REG "-fdri", + KEYHOLE_STREAM_4K); + sysbus_init_mmio(sbd, &s->iomem_fdri); + sysbus_init_irq(sbd, &s->irq_cfrm_imr); + + s->cframes = g_tree_new_full((GCompareDataFunc)int_cmp, NULL, + NULL, (GDestroyNotify)g_free); + fifo32_create(&s->new_f_data, FRAME_NUM_WORDS); +} + +static const VMStateDescription vmstate_cframe = { + .name = "cframe", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(data, XlnxCFrame, FRAME_NUM_WORDS), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_cframe_reg = { + .name = TYPE_XLNX_VERSAL_CFRAME_REG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(wfifo, XlnxVersalCFrameReg, 4), + VMSTATE_UINT32_ARRAY(regs, XlnxVersalCFrameReg, CFRAME_REG_R_MAX), + VMSTATE_BOOL(rowon, XlnxVersalCFrameReg), + VMSTATE_BOOL(wcfg, XlnxVersalCFrameReg), + VMSTATE_BOOL(rcfg, XlnxVersalCFrameReg), + VMSTATE_GTREE_DIRECT_KEY_V(cframes, XlnxVersalCFrameReg, 1, + &vmstate_cframe, XlnxCFrame), + VMSTATE_FIFO32(new_f_data, XlnxVersalCFrameReg), + VMSTATE_END_OF_LIST(), + } +}; + +static Property cframe_regs_props[] = { + DEFINE_PROP_LINK("cfu-fdro", XlnxVersalCFrameReg, cfg.cfu_fdro, + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_UINT32("blktype0-frames", XlnxVersalCFrameReg, + cfg.blktype_num_frames[0], 0), + DEFINE_PROP_UINT32("blktype1-frames", XlnxVersalCFrameReg, + cfg.blktype_num_frames[1], 0), + DEFINE_PROP_UINT32("blktype2-frames", XlnxVersalCFrameReg, + cfg.blktype_num_frames[2], 0), + DEFINE_PROP_UINT32("blktype3-frames", XlnxVersalCFrameReg, + cfg.blktype_num_frames[3], 0), + DEFINE_PROP_UINT32("blktype4-frames", XlnxVersalCFrameReg, + cfg.blktype_num_frames[4], 0), + DEFINE_PROP_UINT32("blktype5-frames", XlnxVersalCFrameReg, + cfg.blktype_num_frames[5], 0), + DEFINE_PROP_UINT32("blktype6-frames", XlnxVersalCFrameReg, + cfg.blktype_num_frames[6], 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void cframe_bcast_reg_init(Object *obj) +{ + XlnxVersalCFrameBcastReg *s = XLNX_VERSAL_CFRAME_BCAST_REG(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem_reg, obj, &cframes_bcast_reg_reg_ops, s, + TYPE_XLNX_VERSAL_CFRAME_BCAST_REG, KEYHOLE_STREAM_4K); + memory_region_init_io(&s->iomem_fdri, obj, &cframes_bcast_reg_fdri_ops, s, + TYPE_XLNX_VERSAL_CFRAME_BCAST_REG "-fdri", + KEYHOLE_STREAM_4K); + sysbus_init_mmio(sbd, &s->iomem_reg); + sysbus_init_mmio(sbd, &s->iomem_fdri); +} + +static void cframe_bcast_reg_reset_enter(Object *obj, ResetType type) +{ + XlnxVersalCFrameBcastReg *s = XLNX_VERSAL_CFRAME_BCAST_REG(obj); + + memset(s->wfifo, 0, WFIFO_SZ * sizeof(uint32_t)); +} + +static const VMStateDescription vmstate_cframe_bcast_reg = { + .name = TYPE_XLNX_VERSAL_CFRAME_BCAST_REG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(wfifo, XlnxVersalCFrameBcastReg, 4), + VMSTATE_END_OF_LIST(), + } +}; + +static Property cframe_bcast_regs_props[] = { + DEFINE_PROP_LINK("cframe0", XlnxVersalCFrameBcastReg, cfg.cframe[0], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe1", XlnxVersalCFrameBcastReg, cfg.cframe[1], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe2", XlnxVersalCFrameBcastReg, cfg.cframe[2], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe3", XlnxVersalCFrameBcastReg, cfg.cframe[3], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe4", XlnxVersalCFrameBcastReg, cfg.cframe[4], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe5", XlnxVersalCFrameBcastReg, cfg.cframe[5], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe6", XlnxVersalCFrameBcastReg, cfg.cframe[6], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe7", XlnxVersalCFrameBcastReg, cfg.cframe[7], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe8", XlnxVersalCFrameBcastReg, cfg.cframe[8], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe9", XlnxVersalCFrameBcastReg, cfg.cframe[9], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe10", XlnxVersalCFrameBcastReg, cfg.cframe[10], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe11", XlnxVersalCFrameBcastReg, cfg.cframe[11], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe12", XlnxVersalCFrameBcastReg, cfg.cframe[12], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe13", XlnxVersalCFrameBcastReg, cfg.cframe[13], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe14", XlnxVersalCFrameBcastReg, cfg.cframe[14], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void cframe_reg_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + XlnxCfiIfClass *xcic = XLNX_CFI_IF_CLASS(klass); + + dc->vmsd = &vmstate_cframe_reg; + dc->realize = cframe_reg_realize; + rc->phases.enter = cframe_reg_reset_enter; + rc->phases.hold = cframe_reg_reset_hold; + device_class_set_props(dc, cframe_regs_props); + xcic->cfi_transfer_packet = cframe_reg_cfi_transfer_packet; +} + +static void cframe_bcast_reg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->vmsd = &vmstate_cframe_bcast_reg; + device_class_set_props(dc, cframe_bcast_regs_props); + rc->phases.enter = cframe_bcast_reg_reset_enter; +} + +static const TypeInfo cframe_reg_info = { + .name = TYPE_XLNX_VERSAL_CFRAME_REG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalCFrameReg), + .class_init = cframe_reg_class_init, + .instance_init = cframe_reg_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_XLNX_CFI_IF }, + { } + } +}; + +static const TypeInfo cframe_bcast_reg_info = { + .name = TYPE_XLNX_VERSAL_CFRAME_BCAST_REG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalCFrameBcastReg), + .class_init = cframe_bcast_reg_class_init, + .instance_init = cframe_bcast_reg_init, +}; + +static void cframe_reg_register_types(void) +{ + type_register_static(&cframe_reg_info); + type_register_static(&cframe_bcast_reg_info); +} + +type_init(cframe_reg_register_types) diff --git a/hw/misc/xlnx-versal-cfu.c b/hw/misc/xlnx-versal-cfu.c new file mode 100644 index 0000000000..6bb82e51c1 --- /dev/null +++ b/hw/misc/xlnx-versal-cfu.c @@ -0,0 +1,563 @@ +/* + * QEMU model of the CFU Configuration Unit. + * + * Copyright (C) 2023, Advanced Micro Devices, Inc. + * + * Written by Edgar E. Iglesias , + * Sai Pavan Boddu , + * Francisco Iglesias + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/register.h" +#include "hw/irq.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/units.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/misc/xlnx-versal-cfu.h" + +#ifndef XLNX_VERSAL_CFU_APB_ERR_DEBUG +#define XLNX_VERSAL_CFU_APB_ERR_DEBUG 0 +#endif + +#define KEYHOLE_STREAM_4K (4 * KiB) +#define KEYHOLE_STREAM_256K (256 * KiB) +#define CFRAME_BROADCAST_ROW 0x1F + +bool update_wfifo(hwaddr addr, uint64_t value, + uint32_t *wfifo, uint32_t *wfifo_ret) +{ + unsigned int idx = extract32(addr, 2, 2); + + wfifo[idx] = value; + + if (idx == 3) { + memcpy(wfifo_ret, wfifo, WFIFO_SZ * sizeof(uint32_t)); + memset(wfifo, 0, WFIFO_SZ * sizeof(uint32_t)); + return true; + } + + return false; +} + +static void cfu_imr_update_irq(XlnxVersalCFUAPB *s) +{ + bool pending = s->regs[R_CFU_ISR] & ~s->regs[R_CFU_IMR]; + qemu_set_irq(s->irq_cfu_imr, pending); +} + +static void cfu_isr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(reg->opaque); + cfu_imr_update_irq(s); +} + +static uint64_t cfu_ier_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(reg->opaque); + uint32_t val = val64; + + s->regs[R_CFU_IMR] &= ~val; + cfu_imr_update_irq(s); + return 0; +} + +static uint64_t cfu_idr_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(reg->opaque); + uint32_t val = val64; + + s->regs[R_CFU_IMR] |= val; + cfu_imr_update_irq(s); + return 0; +} + +static uint64_t cfu_itr_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(reg->opaque); + uint32_t val = val64; + + s->regs[R_CFU_ISR] |= val; + cfu_imr_update_irq(s); + return 0; +} + +static void cfu_fgcr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(reg->opaque); + uint32_t val = (uint32_t)val64; + + /* Do a scan. It always looks good. */ + if (FIELD_EX32(val, CFU_FGCR, SC_HBC_TRIGGER)) { + ARRAY_FIELD_DP32(s->regs, CFU_STATUS, SCAN_CLEAR_PASS, 1); + ARRAY_FIELD_DP32(s->regs, CFU_STATUS, SCAN_CLEAR_DONE, 1); + } +} + +static const RegisterAccessInfo cfu_apb_regs_info[] = { + { .name = "CFU_ISR", .addr = A_CFU_ISR, + .rsvd = 0xfffffc00, + .w1c = 0x3ff, + .post_write = cfu_isr_postw, + },{ .name = "CFU_IMR", .addr = A_CFU_IMR, + .reset = 0x3ff, + .rsvd = 0xfffffc00, + .ro = 0x3ff, + },{ .name = "CFU_IER", .addr = A_CFU_IER, + .rsvd = 0xfffffc00, + .pre_write = cfu_ier_prew, + },{ .name = "CFU_IDR", .addr = A_CFU_IDR, + .rsvd = 0xfffffc00, + .pre_write = cfu_idr_prew, + },{ .name = "CFU_ITR", .addr = A_CFU_ITR, + .rsvd = 0xfffffc00, + .pre_write = cfu_itr_prew, + },{ .name = "CFU_PROTECT", .addr = A_CFU_PROTECT, + .reset = 0x1, + },{ .name = "CFU_FGCR", .addr = A_CFU_FGCR, + .rsvd = 0xffff8000, + .post_write = cfu_fgcr_postw, + },{ .name = "CFU_CTL", .addr = A_CFU_CTL, + .rsvd = 0xffff0000, + },{ .name = "CFU_CRAM_RW", .addr = A_CFU_CRAM_RW, + .reset = 0x401f7d9, + .rsvd = 0xf8000000, + },{ .name = "CFU_MASK", .addr = A_CFU_MASK, + },{ .name = "CFU_CRC_EXPECT", .addr = A_CFU_CRC_EXPECT, + },{ .name = "CFU_CFRAME_LEFT_T0", .addr = A_CFU_CFRAME_LEFT_T0, + .rsvd = 0xfff00000, + },{ .name = "CFU_CFRAME_LEFT_T1", .addr = A_CFU_CFRAME_LEFT_T1, + .rsvd = 0xfff00000, + },{ .name = "CFU_CFRAME_LEFT_T2", .addr = A_CFU_CFRAME_LEFT_T2, + .rsvd = 0xfff00000, + },{ .name = "CFU_ROW_RANGE", .addr = A_CFU_ROW_RANGE, + .rsvd = 0xffffffc0, + .ro = 0x3f, + },{ .name = "CFU_STATUS", .addr = A_CFU_STATUS, + .rsvd = 0x80000000, + .ro = 0x7fffffff, + },{ .name = "CFU_INTERNAL_STATUS", .addr = A_CFU_INTERNAL_STATUS, + .rsvd = 0xff800000, + .ro = 0x7fffff, + },{ .name = "CFU_QWORD_CNT", .addr = A_CFU_QWORD_CNT, + .ro = 0xffffffff, + },{ .name = "CFU_CRC_LIVE", .addr = A_CFU_CRC_LIVE, + .ro = 0xffffffff, + },{ .name = "CFU_PENDING_READ_CNT", .addr = A_CFU_PENDING_READ_CNT, + .rsvd = 0xfe000000, + .ro = 0x1ffffff, + },{ .name = "CFU_FDRI_CNT", .addr = A_CFU_FDRI_CNT, + .ro = 0xffffffff, + },{ .name = "CFU_ECO1", .addr = A_CFU_ECO1, + },{ .name = "CFU_ECO2", .addr = A_CFU_ECO2, + } +}; + +static void cfu_apb_reset(DeviceState *dev) +{ + XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + register_reset(&s->regs_info[i]); + } + memset(s->wfifo, 0, WFIFO_SZ * sizeof(uint32_t)); + + s->regs[R_CFU_STATUS] |= R_CFU_STATUS_HC_COMPLETE_MASK; + cfu_imr_update_irq(s); +} + +static const MemoryRegionOps cfu_apb_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void cfu_transfer_cfi_packet(XlnxVersalCFUAPB *s, uint8_t row_addr, + XlnxCfiPacket *pkt) +{ + if (row_addr == CFRAME_BROADCAST_ROW) { + for (int i = 0; i < ARRAY_SIZE(s->cfg.cframe); i++) { + if (s->cfg.cframe[i]) { + xlnx_cfi_transfer_packet(s->cfg.cframe[i], pkt); + } + } + } else { + assert(row_addr < ARRAY_SIZE(s->cfg.cframe)); + + if (s->cfg.cframe[row_addr]) { + xlnx_cfi_transfer_packet(s->cfg.cframe[row_addr], pkt); + } + } +} + +static uint64_t cfu_stream_read(void *opaque, hwaddr addr, unsigned size) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported read from addr=%" + HWADDR_PRIx "\n", __func__, addr); + return 0; +} + +static void cfu_stream_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(opaque); + uint32_t wfifo[WFIFO_SZ]; + + if (update_wfifo(addr, value, s->wfifo, wfifo)) { + uint8_t packet_type, row_addr, reg_addr; + + packet_type = extract32(wfifo[0], 24, 8); + row_addr = extract32(wfifo[0], 16, 5); + reg_addr = extract32(wfifo[0], 8, 6); + + /* Compressed bitstreams are not supported yet. */ + if (ARRAY_FIELD_EX32(s->regs, CFU_CTL, DECOMPRESS) == 0) { + if (s->regs[R_CFU_FDRI_CNT]) { + XlnxCfiPacket pkt = { + .reg_addr = CFRAME_FDRI, + .data[0] = wfifo[0], + .data[1] = wfifo[1], + .data[2] = wfifo[2], + .data[3] = wfifo[3] + }; + + cfu_transfer_cfi_packet(s, s->fdri_row_addr, &pkt); + + s->regs[R_CFU_FDRI_CNT]--; + + } else if (packet_type == PACKET_TYPE_CFU && + reg_addr == CFRAME_FDRI) { + + /* Load R_CFU_FDRI_CNT, must be multiple of 25 */ + s->regs[R_CFU_FDRI_CNT] = wfifo[1]; + + /* Store target row_addr */ + s->fdri_row_addr = row_addr; + + if (wfifo[1] % 25 != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "CFU FDRI_CNT is not loaded with " + "a multiple of 25 value\n"); + } + + } else if (packet_type == PACKET_TYPE_CFRAME) { + XlnxCfiPacket pkt = { + .reg_addr = reg_addr, + .data[0] = wfifo[1], + .data[1] = wfifo[2], + .data[2] = wfifo[3], + }; + cfu_transfer_cfi_packet(s, row_addr, &pkt); + } + } + } +} + +static uint64_t cfu_sfr_read(void *opaque, hwaddr addr, unsigned size) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported read from addr=%" + HWADDR_PRIx "\n", __func__, addr); + return 0; +} + +static void cfu_sfr_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + XlnxVersalCFUSFR *s = XLNX_VERSAL_CFU_SFR(opaque); + uint32_t wfifo[WFIFO_SZ]; + + if (update_wfifo(addr, value, s->wfifo, wfifo)) { + uint8_t row_addr = extract32(wfifo[0], 23, 5); + uint32_t frame_addr = extract32(wfifo[0], 0, 23); + XlnxCfiPacket pkt = { .reg_addr = CFRAME_SFR, + .data[0] = frame_addr }; + + if (s->cfg.cfu) { + cfu_transfer_cfi_packet(s->cfg.cfu, row_addr, &pkt); + } + } +} + +static uint64_t cfu_fdro_read(void *opaque, hwaddr addr, unsigned size) +{ + XlnxVersalCFUFDRO *s = XLNX_VERSAL_CFU_FDRO(opaque); + uint64_t ret = 0; + + if (!fifo32_is_empty(&s->fdro_data)) { + ret = fifo32_pop(&s->fdro_data); + } + + return ret; +} + +static void cfu_fdro_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Unsupported write from addr=%" + HWADDR_PRIx "\n", __func__, addr); +} + +static const MemoryRegionOps cfu_stream_ops = { + .read = cfu_stream_read, + .write = cfu_stream_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, +}; + +static const MemoryRegionOps cfu_sfr_ops = { + .read = cfu_sfr_read, + .write = cfu_sfr_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static const MemoryRegionOps cfu_fdro_ops = { + .read = cfu_fdro_read, + .write = cfu_fdro_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void cfu_apb_init(Object *obj) +{ + XlnxVersalCFUAPB *s = XLNX_VERSAL_CFU_APB(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; + unsigned int i; + char *name; + + memory_region_init(&s->iomem, obj, TYPE_XLNX_VERSAL_CFU_APB, R_MAX * 4); + reg_array = + register_init_block32(DEVICE(obj), cfu_apb_regs_info, + ARRAY_SIZE(cfu_apb_regs_info), + s->regs_info, s->regs, + &cfu_apb_ops, + XLNX_VERSAL_CFU_APB_ERR_DEBUG, + R_MAX * 4); + memory_region_add_subregion(&s->iomem, + 0x0, + ®_array->mem); + sysbus_init_mmio(sbd, &s->iomem); + for (i = 0; i < NUM_STREAM; i++) { + name = g_strdup_printf(TYPE_XLNX_VERSAL_CFU_APB "-stream%d", i); + memory_region_init_io(&s->iomem_stream[i], obj, &cfu_stream_ops, s, + name, i == 0 ? KEYHOLE_STREAM_4K : + KEYHOLE_STREAM_256K); + sysbus_init_mmio(sbd, &s->iomem_stream[i]); + g_free(name); + } + sysbus_init_irq(sbd, &s->irq_cfu_imr); +} + +static void cfu_sfr_init(Object *obj) +{ + XlnxVersalCFUSFR *s = XLNX_VERSAL_CFU_SFR(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem_sfr, obj, &cfu_sfr_ops, s, + TYPE_XLNX_VERSAL_CFU_SFR, KEYHOLE_STREAM_4K); + sysbus_init_mmio(sbd, &s->iomem_sfr); +} + +static void cfu_sfr_reset_enter(Object *obj, ResetType type) +{ + XlnxVersalCFUSFR *s = XLNX_VERSAL_CFU_SFR(obj); + + memset(s->wfifo, 0, WFIFO_SZ * sizeof(uint32_t)); +} + +static void cfu_fdro_init(Object *obj) +{ + XlnxVersalCFUFDRO *s = XLNX_VERSAL_CFU_FDRO(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem_fdro, obj, &cfu_fdro_ops, s, + TYPE_XLNX_VERSAL_CFU_FDRO, KEYHOLE_STREAM_4K); + sysbus_init_mmio(sbd, &s->iomem_fdro); + fifo32_create(&s->fdro_data, 8 * KiB / sizeof(uint32_t)); +} + +static void cfu_fdro_reset_enter(Object *obj, ResetType type) +{ + XlnxVersalCFUFDRO *s = XLNX_VERSAL_CFU_FDRO(obj); + + fifo32_reset(&s->fdro_data); +} + +static void cfu_fdro_cfi_transfer_packet(XlnxCfiIf *cfi_if, XlnxCfiPacket *pkt) +{ + XlnxVersalCFUFDRO *s = XLNX_VERSAL_CFU_FDRO(cfi_if); + + if (fifo32_num_free(&s->fdro_data) >= ARRAY_SIZE(pkt->data)) { + for (int i = 0; i < ARRAY_SIZE(pkt->data); i++) { + fifo32_push(&s->fdro_data, pkt->data[i]); + } + } else { + /* It is a programming error to fill the fifo. */ + qemu_log_mask(LOG_GUEST_ERROR, + "CFU_FDRO: CFI data dropped due to full read fifo\n"); + } +} + +static Property cfu_props[] = { + DEFINE_PROP_LINK("cframe0", XlnxVersalCFUAPB, cfg.cframe[0], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe1", XlnxVersalCFUAPB, cfg.cframe[1], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe2", XlnxVersalCFUAPB, cfg.cframe[2], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe3", XlnxVersalCFUAPB, cfg.cframe[3], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe4", XlnxVersalCFUAPB, cfg.cframe[4], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe5", XlnxVersalCFUAPB, cfg.cframe[5], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe6", XlnxVersalCFUAPB, cfg.cframe[6], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe7", XlnxVersalCFUAPB, cfg.cframe[7], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe8", XlnxVersalCFUAPB, cfg.cframe[8], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe9", XlnxVersalCFUAPB, cfg.cframe[9], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe10", XlnxVersalCFUAPB, cfg.cframe[10], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe11", XlnxVersalCFUAPB, cfg.cframe[11], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe12", XlnxVersalCFUAPB, cfg.cframe[12], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe13", XlnxVersalCFUAPB, cfg.cframe[13], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_LINK("cframe14", XlnxVersalCFUAPB, cfg.cframe[14], + TYPE_XLNX_CFI_IF, XlnxCfiIf *), + DEFINE_PROP_END_OF_LIST(), +}; + +static Property cfu_sfr_props[] = { + DEFINE_PROP_LINK("cfu", XlnxVersalCFUSFR, cfg.cfu, + TYPE_XLNX_VERSAL_CFU_APB, XlnxVersalCFUAPB *), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_cfu_apb = { + .name = TYPE_XLNX_VERSAL_CFU_APB, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(wfifo, XlnxVersalCFUAPB, 4), + VMSTATE_UINT32_ARRAY(regs, XlnxVersalCFUAPB, R_MAX), + VMSTATE_UINT8(fdri_row_addr, XlnxVersalCFUAPB), + VMSTATE_END_OF_LIST(), + } +}; + +static const VMStateDescription vmstate_cfu_fdro = { + .name = TYPE_XLNX_VERSAL_CFU_FDRO, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_FIFO32(fdro_data, XlnxVersalCFUFDRO), + VMSTATE_END_OF_LIST(), + } +}; + +static const VMStateDescription vmstate_cfu_sfr = { + .name = TYPE_XLNX_VERSAL_CFU_SFR, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(wfifo, XlnxVersalCFUSFR, 4), + VMSTATE_END_OF_LIST(), + } +}; + +static void cfu_apb_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = cfu_apb_reset; + dc->vmsd = &vmstate_cfu_apb; + device_class_set_props(dc, cfu_props); +} + +static void cfu_fdro_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + XlnxCfiIfClass *xcic = XLNX_CFI_IF_CLASS(klass); + + dc->vmsd = &vmstate_cfu_fdro; + xcic->cfi_transfer_packet = cfu_fdro_cfi_transfer_packet; + rc->phases.enter = cfu_fdro_reset_enter; +} + +static void cfu_sfr_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + device_class_set_props(dc, cfu_sfr_props); + dc->vmsd = &vmstate_cfu_sfr; + rc->phases.enter = cfu_sfr_reset_enter; +} + +static const TypeInfo cfu_apb_info = { + .name = TYPE_XLNX_VERSAL_CFU_APB, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalCFUAPB), + .class_init = cfu_apb_class_init, + .instance_init = cfu_apb_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_XLNX_CFI_IF }, + { } + } +}; + +static const TypeInfo cfu_fdro_info = { + .name = TYPE_XLNX_VERSAL_CFU_FDRO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalCFUFDRO), + .class_init = cfu_fdro_class_init, + .instance_init = cfu_fdro_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_XLNX_CFI_IF }, + { } + } +}; + +static const TypeInfo cfu_sfr_info = { + .name = TYPE_XLNX_VERSAL_CFU_SFR, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalCFUSFR), + .class_init = cfu_sfr_class_init, + .instance_init = cfu_sfr_init, +}; + +static void cfu_apb_register_types(void) +{ + type_register_static(&cfu_apb_info); + type_register_static(&cfu_fdro_info); + type_register_static(&cfu_sfr_info); +} + +type_init(cfu_apb_register_types) diff --git a/hw/misc/xlnx-versal-crl.c b/hw/misc/xlnx-versal-crl.c index 767106b7a3..1f1762ef16 100644 --- a/hw/misc/xlnx-versal-crl.c +++ b/hw/misc/xlnx-versal-crl.c @@ -19,6 +19,7 @@ #include "hw/resettable.h" #include "target/arm/arm-powerctl.h" +#include "target/arm/multiprocessing.h" #include "hw/misc/xlnx-versal-crl.h" #ifndef XLNX_VERSAL_CRL_ERR_DEBUG @@ -67,9 +68,9 @@ static void crl_reset_cpu(XlnxVersalCRL *s, ARMCPU *armcpu, bool rst_old, bool rst_new) { if (rst_new) { - arm_set_cpu_off(armcpu->mp_affinity); + arm_set_cpu_off(arm_cpu_mp_affinity(armcpu)); } else { - arm_set_cpu_on_and_reset(armcpu->mp_affinity); + arm_set_cpu_on_and_reset(arm_cpu_mp_affinity(armcpu)); } } @@ -387,7 +388,7 @@ static const VMStateDescription vmstate_crl = { .name = TYPE_XLNX_VERSAL_CRL, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxVersalCRL, CRL_R_MAX), VMSTATE_END_OF_LIST(), } diff --git a/hw/misc/xlnx-versal-pmc-iou-slcr.c b/hw/misc/xlnx-versal-pmc-iou-slcr.c index 07b7ebc217..60e13a78ab 100644 --- a/hw/misc/xlnx-versal-pmc-iou-slcr.c +++ b/hw/misc/xlnx-versal-pmc-iou-slcr.c @@ -1412,7 +1412,7 @@ static const VMStateDescription vmstate_pmc_iou_slcr = { .name = TYPE_XILINX_VERSAL_PMC_IOU_SLCR, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxVersalPmcIouSlcr, XILINX_VERSAL_PMC_IOU_SLCR_R_MAX), VMSTATE_END_OF_LIST(), diff --git a/hw/misc/xlnx-versal-trng.c b/hw/misc/xlnx-versal-trng.c new file mode 100644 index 0000000000..6495188dc7 --- /dev/null +++ b/hw/misc/xlnx-versal-trng.c @@ -0,0 +1,716 @@ +/* + * Non-crypto strength model of the True Random Number Generator + * in the AMD/Xilinx Versal device family. + * + * Copyright (c) 2017-2020 Xilinx Inc. + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * + * Written by Edgar E. Iglesias + * + * 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/misc/xlnx-versal-trng.h" + +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qemu/timer.h" +#include "qapi/visitor.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" + +#ifndef XLNX_VERSAL_TRNG_ERR_DEBUG +#define XLNX_VERSAL_TRNG_ERR_DEBUG 0 +#endif + +REG32(INT_CTRL, 0x0) + FIELD(INT_CTRL, CERTF_RST, 5, 1) + FIELD(INT_CTRL, DTF_RST, 4, 1) + FIELD(INT_CTRL, DONE_RST, 3, 1) + FIELD(INT_CTRL, CERTF_EN, 2, 1) + FIELD(INT_CTRL, DTF_EN, 1, 1) + FIELD(INT_CTRL, DONE_EN, 0, 1) +REG32(STATUS, 0x4) + FIELD(STATUS, QCNT, 9, 3) + FIELD(STATUS, EAT, 4, 5) + FIELD(STATUS, CERTF, 3, 1) + FIELD(STATUS, DTF, 1, 1) + FIELD(STATUS, DONE, 0, 1) +REG32(CTRL, 0x8) + FIELD(CTRL, PERSODISABLE, 10, 1) + FIELD(CTRL, SINGLEGENMODE, 9, 1) + FIELD(CTRL, EUMODE, 8, 1) + FIELD(CTRL, PRNGMODE, 7, 1) + FIELD(CTRL, TSTMODE, 6, 1) + FIELD(CTRL, PRNGSTART, 5, 1) + FIELD(CTRL, EATAU, 4, 1) + FIELD(CTRL, PRNGXS, 3, 1) + FIELD(CTRL, TRSSEN, 2, 1) + FIELD(CTRL, QERTUEN, 1, 1) + FIELD(CTRL, PRNGSRST, 0, 1) +REG32(CTRL_2, 0xc) + FIELD(CTRL_2, REPCOUNTTESTCUTOFF, 8, 9) + FIELD(CTRL_2, RESERVED_7_5, 5, 3) + FIELD(CTRL_2, DIT, 0, 5) +REG32(CTRL_3, 0x10) + FIELD(CTRL_3, ADAPTPROPTESTCUTOFF, 8, 10) + FIELD(CTRL_3, DLEN, 0, 8) +REG32(CTRL_4, 0x14) + FIELD(CTRL_4, SINGLEBITRAW, 0, 1) +REG32(EXT_SEED_0, 0x40) +REG32(EXT_SEED_1, 0x44) +REG32(EXT_SEED_2, 0x48) +REG32(EXT_SEED_3, 0x4c) +REG32(EXT_SEED_4, 0x50) +REG32(EXT_SEED_5, 0x54) +REG32(EXT_SEED_6, 0x58) +REG32(EXT_SEED_7, 0x5c) +REG32(EXT_SEED_8, 0x60) +REG32(EXT_SEED_9, 0x64) +REG32(EXT_SEED_10, 0x68) +REG32(EXT_SEED_11, 0x6c) +REG32(PER_STRNG_0, 0x80) +REG32(PER_STRNG_1, 0x84) +REG32(PER_STRNG_2, 0x88) +REG32(PER_STRNG_3, 0x8c) +REG32(PER_STRNG_4, 0x90) +REG32(PER_STRNG_5, 0x94) +REG32(PER_STRNG_6, 0x98) +REG32(PER_STRNG_7, 0x9c) +REG32(PER_STRNG_8, 0xa0) +REG32(PER_STRNG_9, 0xa4) +REG32(PER_STRNG_10, 0xa8) +REG32(PER_STRNG_11, 0xac) +REG32(CORE_OUTPUT, 0xc0) +REG32(RESET, 0xd0) + FIELD(RESET, VAL, 0, 1) +REG32(OSC_EN, 0xd4) + FIELD(OSC_EN, VAL, 0, 1) +REG32(TRNG_ISR, 0xe0) + FIELD(TRNG_ISR, SLVERR, 1, 1) + FIELD(TRNG_ISR, CORE_INT, 0, 1) +REG32(TRNG_IMR, 0xe4) + FIELD(TRNG_IMR, SLVERR, 1, 1) + FIELD(TRNG_IMR, CORE_INT, 0, 1) +REG32(TRNG_IER, 0xe8) + FIELD(TRNG_IER, SLVERR, 1, 1) + FIELD(TRNG_IER, CORE_INT, 0, 1) +REG32(TRNG_IDR, 0xec) + FIELD(TRNG_IDR, SLVERR, 1, 1) + FIELD(TRNG_IDR, CORE_INT, 0, 1) +REG32(SLV_ERR_CTRL, 0xf0) + FIELD(SLV_ERR_CTRL, ENABLE, 0, 1) + +#define R_MAX (R_SLV_ERR_CTRL + 1) + +QEMU_BUILD_BUG_ON(R_MAX * 4 != sizeof_field(XlnxVersalTRng, regs)); + +#define TRNG_GUEST_ERROR(D, FMT, ...) \ + do { \ + g_autofree char *p = object_get_canonical_path(OBJECT(D)); \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: " FMT, p, ## __VA_ARGS__); \ + } while (0) + +#define TRNG_WARN(D, FMT, ...) \ + do { \ + g_autofree char *p = object_get_canonical_path(OBJECT(D)); \ + warn_report("%s: " FMT, p, ## __VA_ARGS__); \ + } while (0) + +static bool trng_older_than_v2(XlnxVersalTRng *s) +{ + return s->hw_version < 0x0200; +} + +static bool trng_in_reset(XlnxVersalTRng *s) +{ + if (ARRAY_FIELD_EX32(s->regs, RESET, VAL)) { + return true; + } + if (ARRAY_FIELD_EX32(s->regs, CTRL, PRNGSRST)) { + return true; + } + + return false; +} + +static bool trng_test_enabled(XlnxVersalTRng *s) +{ + return ARRAY_FIELD_EX32(s->regs, CTRL, TSTMODE); +} + +static bool trng_trss_enabled(XlnxVersalTRng *s) +{ + if (trng_in_reset(s)) { + return false; + } + if (!ARRAY_FIELD_EX32(s->regs, CTRL, TRSSEN)) { + return false; + } + if (!ARRAY_FIELD_EX32(s->regs, OSC_EN, VAL)) { + return false; + } + + return true; +} + +static void trng_seed_128(uint32_t *seed, uint64_t h00, uint64_t h64) +{ + seed[0] = extract64(h00, 0, 32); + seed[1] = extract64(h00, 32, 32); + seed[2] = extract64(h64, 0, 32); + seed[3] = extract64(h64, 32, 32); +} + +static void trng_reseed(XlnxVersalTRng *s) +{ + bool ext_seed = ARRAY_FIELD_EX32(s->regs, CTRL, PRNGXS); + bool pers_disabled = ARRAY_FIELD_EX32(s->regs, CTRL, PERSODISABLE); + + enum { + U384_U8 = 384 / 8, + U384_U32 = 384 / 32, + }; + + /* + * Maximum seed length is len(personalized string) + len(ext seed). + * + * g_rand_set_seed_array() takes array of uint32 in host endian. + */ + guint32 gs[U384_U32 * 2], *seed = &gs[U384_U32]; + + /* + * A disabled personalized string is the same as + * a string with all zeros. + * + * The device's hardware spec defines 3 modes (all selectable + * by guest at will and at anytime): + * 1) External seeding + * This is a PRNG mode, in which the produced sequence shall + * be reproducible if reseeded by the same 384-bit seed, as + * supplied by guest software. + * 2) Test seeding + * This is a PRNG mode, in which the produced sequence shall + * be reproducible if reseeded by a 128-bit test seed, as + * supplied by guest software. + * 3) Truly-random seeding + * This is the TRNG mode, in which the produced sequence is + * periodically reseeded by a crypto-strength entropy source. + * + * To assist debugging of certain classes of software defects, + * this QEMU model implements a 4th mode, + * 4) Forced PRNG + * When in this mode, a reproducible sequence is generated + * if software has selected the TRNG mode (mode 2). + * + * This emulation-only mode can only be selected by setting + * the uint64 property 'forced-prng' to a non-zero value. + * Guest software cannot select this mode. + */ + memset(gs, 0, sizeof(gs)); + + if (!pers_disabled) { + memcpy(gs, &s->regs[R_PER_STRNG_0], U384_U8); + } + + if (ext_seed) { + memcpy(seed, &s->regs[R_EXT_SEED_0], U384_U8); + } else if (trng_test_enabled(s)) { + trng_seed_128(seed, s->tst_seed[0], s->tst_seed[1]); + } else if (s->forced_prng_seed) { + s->forced_prng_count++; + trng_seed_128(seed, s->forced_prng_count, s->forced_prng_seed); + } else { + qemu_guest_getrandom_nofail(seed, U384_U8); + } + + g_rand_set_seed_array(s->prng, gs, ARRAY_SIZE(gs)); + + s->rand_count = 0; + s->rand_reseed = 1ULL << 48; +} + +static void trng_regen(XlnxVersalTRng *s) +{ + if (s->rand_reseed == 0) { + TRNG_GUEST_ERROR(s, "Too many generations without a reseed"); + trng_reseed(s); + } + s->rand_reseed--; + + /* + * In real hardware, each regen creates 256 bits, but QCNT + * reports a max of 4. + */ + ARRAY_FIELD_DP32(s->regs, STATUS, QCNT, 4); + s->rand_count = 256 / 32; +} + +static uint32_t trng_rdout(XlnxVersalTRng *s) +{ + assert(s->rand_count); + + s->rand_count--; + if (s->rand_count < 4) { + ARRAY_FIELD_DP32(s->regs, STATUS, QCNT, s->rand_count); + } + + return g_rand_int(s->prng); +} + +static void trng_irq_update(XlnxVersalTRng *s) +{ + bool pending = s->regs[R_TRNG_ISR] & ~s->regs[R_TRNG_IMR]; + qemu_set_irq(s->irq, pending); +} + +static void trng_isr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + trng_irq_update(s); +} + +static uint64_t trng_ier_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + uint32_t val = val64; + + s->regs[R_TRNG_IMR] &= ~val; + trng_irq_update(s); + return 0; +} + +static uint64_t trng_idr_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + uint32_t val = val64; + + s->regs[R_TRNG_IMR] |= val; + trng_irq_update(s); + return 0; +} + +static void trng_core_int_update(XlnxVersalTRng *s) +{ + bool pending = false; + uint32_t st = s->regs[R_STATUS]; + uint32_t en = s->regs[R_INT_CTRL]; + + if (FIELD_EX32(st, STATUS, CERTF) && FIELD_EX32(en, INT_CTRL, CERTF_EN)) { + pending = true; + } + + if (FIELD_EX32(st, STATUS, DTF) && FIELD_EX32(en, INT_CTRL, DTF_EN)) { + pending = true; + } + + if (FIELD_EX32(st, STATUS, DONE) && FIELD_EX32(en, INT_CTRL, DONE_EN)) { + pending = true; + } + + ARRAY_FIELD_DP32(s->regs, TRNG_ISR, CORE_INT, pending); + trng_irq_update(s); +} + +static void trng_int_ctrl_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + uint32_t v32 = val64; + uint32_t clr_mask = 0; + + if (FIELD_EX32(v32, INT_CTRL, CERTF_RST)) { + clr_mask |= R_STATUS_CERTF_MASK; + } + if (FIELD_EX32(v32, INT_CTRL, DTF_RST)) { + clr_mask |= R_STATUS_DTF_MASK; + } + if (FIELD_EX32(v32, INT_CTRL, DONE_RST)) { + clr_mask |= R_STATUS_DONE_MASK; + } + + s->regs[R_STATUS] &= ~clr_mask; + trng_core_int_update(s); +} + +static void trng_done(XlnxVersalTRng *s) +{ + ARRAY_FIELD_DP32(s->regs, STATUS, DONE, true); + trng_core_int_update(s); +} + +static void trng_fault_event_set(XlnxVersalTRng *s, uint32_t events) +{ + bool pending = false; + + /* Disabled TRSS cannot generate any fault event */ + if (!trng_trss_enabled(s)) { + return; + } + + if (FIELD_EX32(events, STATUS, CERTF)) { + /* In older version, ERTU must be enabled explicitly to get CERTF */ + if (trng_older_than_v2(s) && + !ARRAY_FIELD_EX32(s->regs, CTRL, QERTUEN)) { + TRNG_WARN(s, "CERTF injection ignored: ERTU disabled"); + } else { + ARRAY_FIELD_DP32(s->regs, STATUS, CERTF, true); + pending = true; + } + } + + if (FIELD_EX32(events, STATUS, DTF)) { + ARRAY_FIELD_DP32(s->regs, STATUS, DTF, true); + pending = true; + } + + if (pending) { + trng_core_int_update(s); + } +} + +static void trng_soft_reset(XlnxVersalTRng *s) +{ + s->rand_count = 0; + s->regs[R_STATUS] = 0; + + ARRAY_FIELD_DP32(s->regs, TRNG_ISR, CORE_INT, 0); +} + +static void trng_ctrl_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + + if (trng_in_reset(s)) { + return; + } + + if (FIELD_EX32(val64, CTRL, PRNGSRST)) { + trng_soft_reset(s); + trng_irq_update(s); + return; + } + + if (!FIELD_EX32(val64, CTRL, PRNGSTART)) { + return; + } + + if (FIELD_EX32(val64, CTRL, PRNGMODE)) { + trng_regen(s); + } else { + trng_reseed(s); + } + + trng_done(s); +} + +static void trng_ctrl4_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + + /* Only applies to test mode with TRSS enabled */ + if (!trng_test_enabled(s) || !trng_trss_enabled(s)) { + return; + } + + /* Shift in a single bit. */ + s->tst_seed[1] <<= 1; + s->tst_seed[1] |= s->tst_seed[0] >> 63; + s->tst_seed[0] <<= 1; + s->tst_seed[0] |= val64 & 1; + + trng_reseed(s); + trng_regen(s); +} + +static uint64_t trng_core_out_postr(RegisterInfo *reg, uint64_t val) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + bool oneshot = ARRAY_FIELD_EX32(s->regs, CTRL, SINGLEGENMODE); + bool start = ARRAY_FIELD_EX32(s->regs, CTRL, PRNGSTART); + uint32_t r = 0xbad; + + if (trng_in_reset(s)) { + TRNG_GUEST_ERROR(s, "Reading random number while in reset!"); + return r; + } + + if (s->rand_count == 0) { + TRNG_GUEST_ERROR(s, "Reading random number when unavailable!"); + return r; + } + + r = trng_rdout(s); + + /* Automatic mode regenerates when half the output reg is empty. */ + if (!oneshot && start && s->rand_count <= 3) { + trng_regen(s); + } + + return r; +} + +static void trng_reset(XlnxVersalTRng *s) +{ + unsigned int i; + + s->forced_prng_count = 0; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + register_reset(&s->regs_info[i]); + } + trng_soft_reset(s); + trng_irq_update(s); +} + +static uint64_t trng_reset_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + + if (!ARRAY_FIELD_EX32(s->regs, RESET, VAL) && + FIELD_EX32(val64, RESET, VAL)) { + trng_reset(s); + } + + return val64; +} + +static uint64_t trng_register_read(void *opaque, hwaddr addr, unsigned size) +{ + /* + * Guest provided seed and personalized strings cannot be + * read back, and read attempts return value of A_STATUS. + */ + switch (addr) { + case A_EXT_SEED_0 ... A_PER_STRNG_11: + addr = A_STATUS; + break; + } + + return register_read_memory(opaque, addr, size); +} + +static void trng_register_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + RegisterInfoArray *reg_array = opaque; + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg_array->r[0]->opaque); + + if (trng_older_than_v2(s)) { + switch (addr) { + case A_CTRL: + value = FIELD_DP64(value, CTRL, PERSODISABLE, 0); + value = FIELD_DP64(value, CTRL, SINGLEGENMODE, 0); + break; + case A_CTRL_2: + case A_CTRL_3: + case A_CTRL_4: + return; + } + } else { + switch (addr) { + case A_CTRL: + value = FIELD_DP64(value, CTRL, EATAU, 0); + value = FIELD_DP64(value, CTRL, QERTUEN, 0); + break; + } + } + + register_write_memory(opaque, addr, value, size); +} + +static RegisterAccessInfo trng_regs_info[] = { + { .name = "INT_CTRL", .addr = A_INT_CTRL, + .post_write = trng_int_ctrl_postw, + },{ .name = "STATUS", .addr = A_STATUS, + .ro = 0xfff, + },{ .name = "CTRL", .addr = A_CTRL, + .post_write = trng_ctrl_postw, + },{ .name = "CTRL_2", .addr = A_CTRL_2, + .reset = 0x210c, + },{ .name = "CTRL_3", .addr = A_CTRL_3, + .reset = 0x26f09, + },{ .name = "CTRL_4", .addr = A_CTRL_4, + .post_write = trng_ctrl4_postw, + },{ .name = "EXT_SEED_0", .addr = A_EXT_SEED_0, + },{ .name = "EXT_SEED_1", .addr = A_EXT_SEED_1, + },{ .name = "EXT_SEED_2", .addr = A_EXT_SEED_2, + },{ .name = "EXT_SEED_3", .addr = A_EXT_SEED_3, + },{ .name = "EXT_SEED_4", .addr = A_EXT_SEED_4, + },{ .name = "EXT_SEED_5", .addr = A_EXT_SEED_5, + },{ .name = "EXT_SEED_6", .addr = A_EXT_SEED_6, + },{ .name = "EXT_SEED_7", .addr = A_EXT_SEED_7, + },{ .name = "EXT_SEED_8", .addr = A_EXT_SEED_8, + },{ .name = "EXT_SEED_9", .addr = A_EXT_SEED_9, + },{ .name = "EXT_SEED_10", .addr = A_EXT_SEED_10, + },{ .name = "EXT_SEED_11", .addr = A_EXT_SEED_11, + },{ .name = "PER_STRNG_0", .addr = A_PER_STRNG_0, + },{ .name = "PER_STRNG_1", .addr = A_PER_STRNG_1, + },{ .name = "PER_STRNG_2", .addr = A_PER_STRNG_2, + },{ .name = "PER_STRNG_3", .addr = A_PER_STRNG_3, + },{ .name = "PER_STRNG_4", .addr = A_PER_STRNG_4, + },{ .name = "PER_STRNG_5", .addr = A_PER_STRNG_5, + },{ .name = "PER_STRNG_6", .addr = A_PER_STRNG_6, + },{ .name = "PER_STRNG_7", .addr = A_PER_STRNG_7, + },{ .name = "PER_STRNG_8", .addr = A_PER_STRNG_8, + },{ .name = "PER_STRNG_9", .addr = A_PER_STRNG_9, + },{ .name = "PER_STRNG_10", .addr = A_PER_STRNG_10, + },{ .name = "PER_STRNG_11", .addr = A_PER_STRNG_11, + },{ .name = "CORE_OUTPUT", .addr = A_CORE_OUTPUT, + .ro = 0xffffffff, + .post_read = trng_core_out_postr, + },{ .name = "RESET", .addr = A_RESET, + .reset = 0x1, + .pre_write = trng_reset_prew, + },{ .name = "OSC_EN", .addr = A_OSC_EN, + },{ .name = "TRNG_ISR", .addr = A_TRNG_ISR, + .w1c = 0x3, + .post_write = trng_isr_postw, + },{ .name = "TRNG_IMR", .addr = A_TRNG_IMR, + .reset = 0x3, + .ro = 0x3, + },{ .name = "TRNG_IER", .addr = A_TRNG_IER, + .pre_write = trng_ier_prew, + },{ .name = "TRNG_IDR", .addr = A_TRNG_IDR, + .pre_write = trng_idr_prew, + },{ .name = "SLV_ERR_CTRL", .addr = A_SLV_ERR_CTRL, + } +}; + +static const MemoryRegionOps trng_ops = { + .read = trng_register_read, + .write = trng_register_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void trng_init(Object *obj) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; + + reg_array = + register_init_block32(DEVICE(obj), trng_regs_info, + ARRAY_SIZE(trng_regs_info), + s->regs_info, s->regs, + &trng_ops, + XLNX_VERSAL_TRNG_ERR_DEBUG, + R_MAX * 4); + + sysbus_init_mmio(sbd, ®_array->mem); + sysbus_init_irq(sbd, &s->irq); + + s->prng = g_rand_new(); +} + +static void trng_unrealize(DeviceState *dev) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(dev); + + g_rand_free(s->prng); + s->prng = NULL; +} + +static void trng_reset_hold(Object *obj) +{ + trng_reset(XLNX_VERSAL_TRNG(obj)); +} + +static void trng_prop_fault_event_set(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + uint32_t *events = object_field_prop_ptr(obj, prop); + + if (!visit_type_uint32(v, name, events, errp)) { + return; + } + + trng_fault_event_set(XLNX_VERSAL_TRNG(obj), *events); +} + +static const PropertyInfo trng_prop_fault_events = { + .name = "uint32:bits", + .description = "Set to trigger TRNG fault events", + .set = trng_prop_fault_event_set, + .realized_set_allowed = true, +}; + +static PropertyInfo trng_prop_uint64; /* to extend qdev_prop_uint64 */ + +static Property trng_props[] = { + DEFINE_PROP_UINT64("forced-prng", XlnxVersalTRng, forced_prng_seed, 0), + DEFINE_PROP_UINT32("hw-version", XlnxVersalTRng, hw_version, 0x0200), + DEFINE_PROP("fips-fault-events", XlnxVersalTRng, forced_faults, + trng_prop_fault_events, uint32_t), + + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_trng = { + .name = TYPE_XLNX_VERSAL_TRNG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(rand_count, XlnxVersalTRng), + VMSTATE_UINT64(rand_reseed, XlnxVersalTRng), + VMSTATE_UINT64(forced_prng_count, XlnxVersalTRng), + VMSTATE_UINT64_ARRAY(tst_seed, XlnxVersalTRng, 2), + VMSTATE_UINT32_ARRAY(regs, XlnxVersalTRng, R_MAX), + VMSTATE_END_OF_LIST(), + } +}; + +static void trng_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->vmsd = &vmstate_trng; + dc->unrealize = trng_unrealize; + rc->phases.hold = trng_reset_hold; + + /* Clone uint64 property with set allowed after realized */ + trng_prop_uint64 = qdev_prop_uint64; + trng_prop_uint64.realized_set_allowed = true; + trng_props[0].info = &trng_prop_uint64; + + device_class_set_props(dc, trng_props); +} + +static const TypeInfo trng_info = { + .name = TYPE_XLNX_VERSAL_TRNG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalTRng), + .class_init = trng_class_init, + .instance_init = trng_init, +}; + +static void trng_register_types(void) +{ + type_register_static(&trng_info); +} + +type_init(trng_register_types) diff --git a/hw/misc/xlnx-versal-xramc.c b/hw/misc/xlnx-versal-xramc.c index e5b719a0ed..a5f78c190e 100644 --- a/hw/misc/xlnx-versal-xramc.c +++ b/hw/misc/xlnx-versal-xramc.c @@ -212,7 +212,7 @@ static const VMStateDescription vmstate_xram_ctrl = { .name = TYPE_XLNX_XRAM_CTRL, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxXramCtrl, XRAM_CTRL_R_MAX), VMSTATE_END_OF_LIST(), } diff --git a/hw/misc/xlnx-zynqmp-apu-ctrl.c b/hw/misc/xlnx-zynqmp-apu-ctrl.c index 20de23cf67..1d441b41df 100644 --- a/hw/misc/xlnx-zynqmp-apu-ctrl.c +++ b/hw/misc/xlnx-zynqmp-apu-ctrl.c @@ -18,7 +18,6 @@ #include "hw/register.h" #include "qemu/bitops.h" -#include "qapi/qmp/qerror.h" #include "hw/misc/xlnx-zynqmp-apu-ctrl.h" @@ -219,7 +218,7 @@ static const VMStateDescription vmstate_zynqmp_apu = { .name = TYPE_XLNX_ZYNQMP_APU_CTRL, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPAPUCtrl, APU_R_MAX), VMSTATE_END_OF_LIST(), } diff --git a/hw/misc/xlnx-zynqmp-crf.c b/hw/misc/xlnx-zynqmp-crf.c index 57bc8cf49a..a83efb44e3 100644 --- a/hw/misc/xlnx-zynqmp-crf.c +++ b/hw/misc/xlnx-zynqmp-crf.c @@ -233,7 +233,7 @@ static const VMStateDescription vmstate_crf = { .name = TYPE_XLNX_ZYNQMP_CRF, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCRF, CRF_R_MAX), VMSTATE_END_OF_LIST(), } diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index 8b70285961..d2ac2e77f2 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -285,7 +285,7 @@ static void zynq_slcr_compute_clocks_internal(ZynqSLCRState *s, uint64_t ps_clk) } /** - * Compute and set the ouputs clocks periods. + * Compute and set the outputs clocks periods. * But do not propagate them further. Connected clocks * will not receive any updates (See zynq_slcr_compute_clocks()) */ @@ -603,7 +603,7 @@ static const VMStateDescription vmstate_zynq_slcr = { .name = "zynq_slcr", .version_id = 3, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, ZynqSLCRState, ZYNQ_SLCR_NUM_REGS), VMSTATE_CLOCK_V(ps_clk, ZynqSLCRState, 3), VMSTATE_END_OF_LIST() diff --git a/hw/net/Kconfig b/hw/net/Kconfig index 6d795ec752..7fcc0d7faa 100644 --- a/hw/net/Kconfig +++ b/hw/net/Kconfig @@ -41,7 +41,12 @@ config E1000_PCI config E1000E_PCI_EXPRESS bool - default y if PCI_DEVICES + default y if PCI_DEVICES || PCIE_DEVICES + depends on PCI_EXPRESS && MSI_NONBROKEN + +config IGB_PCI_EXPRESS + bool + default y if PCI_DEVICES || PCIE_DEVICES depends on PCI_EXPRESS && MSI_NONBROKEN config RTL8139_PCI diff --git a/hw/net/allwinner-sun8i-emac.c b/hw/net/allwinner-sun8i-emac.c index ecc0245fe8..108ae9c853 100644 --- a/hw/net/allwinner-sun8i-emac.c +++ b/hw/net/allwinner-sun8i-emac.c @@ -350,8 +350,13 @@ static void allwinner_sun8i_emac_get_desc(AwSun8iEmacState *s, FrameDescriptor *desc, uint32_t phys_addr) { - dma_memory_read(&s->dma_as, phys_addr, desc, sizeof(*desc), + uint32_t desc_words[4]; + dma_memory_read(&s->dma_as, phys_addr, &desc_words, sizeof(desc_words), MEMTXATTRS_UNSPECIFIED); + desc->status = le32_to_cpu(desc_words[0]); + desc->status2 = le32_to_cpu(desc_words[1]); + desc->addr = le32_to_cpu(desc_words[2]); + desc->next = le32_to_cpu(desc_words[3]); } static uint32_t allwinner_sun8i_emac_next_desc(AwSun8iEmacState *s, @@ -400,10 +405,15 @@ static uint32_t allwinner_sun8i_emac_tx_desc(AwSun8iEmacState *s, } static void allwinner_sun8i_emac_flush_desc(AwSun8iEmacState *s, - FrameDescriptor *desc, + const FrameDescriptor *desc, uint32_t phys_addr) { - dma_memory_write(&s->dma_as, phys_addr, desc, sizeof(*desc), + uint32_t desc_words[4]; + desc_words[0] = cpu_to_le32(desc->status); + desc_words[1] = cpu_to_le32(desc->status2); + desc_words[2] = cpu_to_le32(desc->addr); + desc_words[3] = cpu_to_le32(desc->next); + dma_memory_write(&s->dma_as, phys_addr, &desc_words, sizeof(desc_words), MEMTXATTRS_UNSPECIFIED); } @@ -638,8 +648,7 @@ static uint64_t allwinner_sun8i_emac_read(void *opaque, hwaddr offset, break; case REG_TX_CUR_BUF: /* Transmit Current Buffer */ if (s->tx_desc_curr != 0) { - dma_memory_read(&s->dma_as, s->tx_desc_curr, &desc, sizeof(desc), - MEMTXATTRS_UNSPECIFIED); + allwinner_sun8i_emac_get_desc(s, &desc, s->tx_desc_curr); value = desc.addr; } else { value = 0; @@ -652,8 +661,7 @@ static uint64_t allwinner_sun8i_emac_read(void *opaque, hwaddr offset, break; case REG_RX_CUR_BUF: /* Receive Current Buffer */ if (s->rx_desc_curr != 0) { - dma_memory_read(&s->dma_as, s->rx_desc_curr, &desc, sizeof(desc), - MEMTXATTRS_UNSPECIFIED); + allwinner_sun8i_emac_get_desc(s, &desc, s->rx_desc_curr); value = desc.addr; } else { value = 0; @@ -663,7 +671,7 @@ static uint64_t allwinner_sun8i_emac_read(void *opaque, hwaddr offset, break; default: qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to unknown " - "EMAC register 0x" TARGET_FMT_plx "\n", + "EMAC register 0x" HWADDR_FMT_plx "\n", offset); } @@ -760,7 +768,7 @@ static void allwinner_sun8i_emac_write(void *opaque, hwaddr offset, break; default: qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access to unknown " - "EMAC register 0x" TARGET_FMT_plx "\n", + "EMAC register 0x" HWADDR_FMT_plx "\n", offset); } } @@ -816,7 +824,8 @@ static void allwinner_sun8i_emac_realize(DeviceState *dev, Error **errp) 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); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } @@ -842,7 +851,7 @@ static const VMStateDescription vmstate_aw_emac = { .version_id = 1, .minimum_version_id = 1, .post_load = allwinner_sun8i_emac_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(mii_phy_addr, AwSun8iEmacState), VMSTATE_UINT32(mii_cmd, AwSun8iEmacState), VMSTATE_UINT32(mii_data, AwSun8iEmacState), diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c index ddddf35c45..989839784a 100644 --- a/hw/net/allwinner_emac.c +++ b/hw/net/allwinner_emac.c @@ -304,7 +304,7 @@ static uint64_t aw_emac_read(void *opaque, hwaddr offset, unsigned size) default: qemu_log_mask(LOG_UNIMP, "allwinner_emac: read access to unknown register 0x" - TARGET_FMT_plx "\n", offset); + HWADDR_FMT_plx "\n", offset); ret = 0; } @@ -407,7 +407,7 @@ static void aw_emac_write(void *opaque, hwaddr offset, uint64_t value, default: qemu_log_mask(LOG_UNIMP, "allwinner_emac: write access to unknown register 0x" - TARGET_FMT_plx "\n", offset); + HWADDR_FMT_plx "\n", offset); } } @@ -453,7 +453,8 @@ static void aw_emac_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_aw_emac_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); fifo8_create(&s->rx_fifo, RX_FIFO_SIZE); @@ -471,7 +472,7 @@ static const VMStateDescription vmstate_mii = { .name = "rtl8201cp", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(bmcr, RTL8201CPState), VMSTATE_UINT16(bmsr, RTL8201CPState), VMSTATE_UINT16(anar, RTL8201CPState), @@ -494,7 +495,7 @@ static const VMStateDescription vmstate_aw_emac = { .version_id = 1, .minimum_version_id = 1, .post_load = aw_emac_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(mii, AwEmacState, 1, vmstate_mii, RTL8201CPState), VMSTATE_UINT32(ctl, AwEmacState), VMSTATE_UINT32(tx_mode, AwEmacState), diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 24b3a0ff66..ec7bf562e5 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -28,6 +28,7 @@ #include "hw/irq.h" #include "hw/net/cadence_gem.h" #include "hw/qdev-properties.h" +#include "hw/registerfields.h" #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/log.h" @@ -44,210 +45,310 @@ } \ } while (0) -#define GEM_NWCTRL (0x00000000 / 4) /* Network Control reg */ -#define GEM_NWCFG (0x00000004 / 4) /* Network Config reg */ -#define GEM_NWSTATUS (0x00000008 / 4) /* Network Status reg */ -#define GEM_USERIO (0x0000000C / 4) /* User IO reg */ -#define GEM_DMACFG (0x00000010 / 4) /* DMA Control reg */ -#define GEM_TXSTATUS (0x00000014 / 4) /* TX Status reg */ -#define GEM_RXQBASE (0x00000018 / 4) /* RX Q Base address reg */ -#define GEM_TXQBASE (0x0000001C / 4) /* TX Q Base address reg */ -#define GEM_RXSTATUS (0x00000020 / 4) /* RX Status reg */ -#define GEM_ISR (0x00000024 / 4) /* Interrupt Status reg */ -#define GEM_IER (0x00000028 / 4) /* Interrupt Enable reg */ -#define GEM_IDR (0x0000002C / 4) /* Interrupt Disable reg */ -#define GEM_IMR (0x00000030 / 4) /* Interrupt Mask reg */ -#define GEM_PHYMNTNC (0x00000034 / 4) /* Phy Maintenance reg */ -#define GEM_RXPAUSE (0x00000038 / 4) /* RX Pause Time reg */ -#define GEM_TXPAUSE (0x0000003C / 4) /* TX Pause Time reg */ -#define GEM_TXPARTIALSF (0x00000040 / 4) /* TX Partial Store and Forward */ -#define GEM_RXPARTIALSF (0x00000044 / 4) /* RX Partial Store and Forward */ -#define GEM_JUMBO_MAX_LEN (0x00000048 / 4) /* Max Jumbo Frame Size */ -#define GEM_HASHLO (0x00000080 / 4) /* Hash Low address reg */ -#define GEM_HASHHI (0x00000084 / 4) /* Hash High address reg */ -#define GEM_SPADDR1LO (0x00000088 / 4) /* Specific addr 1 low reg */ -#define GEM_SPADDR1HI (0x0000008C / 4) /* Specific addr 1 high reg */ -#define GEM_SPADDR2LO (0x00000090 / 4) /* Specific addr 2 low reg */ -#define GEM_SPADDR2HI (0x00000094 / 4) /* Specific addr 2 high reg */ -#define GEM_SPADDR3LO (0x00000098 / 4) /* Specific addr 3 low reg */ -#define GEM_SPADDR3HI (0x0000009C / 4) /* Specific addr 3 high reg */ -#define GEM_SPADDR4LO (0x000000A0 / 4) /* Specific addr 4 low reg */ -#define GEM_SPADDR4HI (0x000000A4 / 4) /* Specific addr 4 high reg */ -#define GEM_TIDMATCH1 (0x000000A8 / 4) /* Type ID1 Match reg */ -#define GEM_TIDMATCH2 (0x000000AC / 4) /* Type ID2 Match reg */ -#define GEM_TIDMATCH3 (0x000000B0 / 4) /* Type ID3 Match reg */ -#define GEM_TIDMATCH4 (0x000000B4 / 4) /* Type ID4 Match reg */ -#define GEM_WOLAN (0x000000B8 / 4) /* Wake on LAN reg */ -#define GEM_IPGSTRETCH (0x000000BC / 4) /* IPG Stretch reg */ -#define GEM_SVLAN (0x000000C0 / 4) /* Stacked VLAN reg */ -#define GEM_MODID (0x000000FC / 4) /* Module ID reg */ -#define GEM_OCTTXLO (0x00000100 / 4) /* Octects transmitted Low reg */ -#define GEM_OCTTXHI (0x00000104 / 4) /* Octects transmitted High reg */ -#define GEM_TXCNT (0x00000108 / 4) /* Error-free Frames transmitted */ -#define GEM_TXBCNT (0x0000010C / 4) /* Error-free Broadcast Frames */ -#define GEM_TXMCNT (0x00000110 / 4) /* Error-free Multicast Frame */ -#define GEM_TXPAUSECNT (0x00000114 / 4) /* Pause Frames Transmitted */ -#define GEM_TX64CNT (0x00000118 / 4) /* Error-free 64 TX */ -#define GEM_TX65CNT (0x0000011C / 4) /* Error-free 65-127 TX */ -#define GEM_TX128CNT (0x00000120 / 4) /* Error-free 128-255 TX */ -#define GEM_TX256CNT (0x00000124 / 4) /* Error-free 256-511 */ -#define GEM_TX512CNT (0x00000128 / 4) /* Error-free 512-1023 TX */ -#define GEM_TX1024CNT (0x0000012C / 4) /* Error-free 1024-1518 TX */ -#define GEM_TX1519CNT (0x00000130 / 4) /* Error-free larger than 1519 TX */ -#define GEM_TXURUNCNT (0x00000134 / 4) /* TX under run error counter */ -#define GEM_SINGLECOLLCNT (0x00000138 / 4) /* Single Collision Frames */ -#define GEM_MULTCOLLCNT (0x0000013C / 4) /* Multiple Collision Frames */ -#define GEM_EXCESSCOLLCNT (0x00000140 / 4) /* Excessive Collision Frames */ -#define GEM_LATECOLLCNT (0x00000144 / 4) /* Late Collision Frames */ -#define GEM_DEFERTXCNT (0x00000148 / 4) /* Deferred Transmission Frames */ -#define GEM_CSENSECNT (0x0000014C / 4) /* Carrier Sense Error Counter */ -#define GEM_OCTRXLO (0x00000150 / 4) /* Octects Received register Low */ -#define GEM_OCTRXHI (0x00000154 / 4) /* Octects Received register High */ -#define GEM_RXCNT (0x00000158 / 4) /* Error-free Frames Received */ -#define GEM_RXBROADCNT (0x0000015C / 4) /* Error-free Broadcast Frames RX */ -#define GEM_RXMULTICNT (0x00000160 / 4) /* Error-free Multicast Frames RX */ -#define GEM_RXPAUSECNT (0x00000164 / 4) /* Pause Frames Received Counter */ -#define GEM_RX64CNT (0x00000168 / 4) /* Error-free 64 byte Frames RX */ -#define GEM_RX65CNT (0x0000016C / 4) /* Error-free 65-127B Frames RX */ -#define GEM_RX128CNT (0x00000170 / 4) /* Error-free 128-255B Frames RX */ -#define GEM_RX256CNT (0x00000174 / 4) /* Error-free 256-512B Frames RX */ -#define GEM_RX512CNT (0x00000178 / 4) /* Error-free 512-1023B Frames RX */ -#define GEM_RX1024CNT (0x0000017C / 4) /* Error-free 1024-1518B Frames RX */ -#define GEM_RX1519CNT (0x00000180 / 4) /* Error-free 1519-max Frames RX */ -#define GEM_RXUNDERCNT (0x00000184 / 4) /* Undersize Frames Received */ -#define GEM_RXOVERCNT (0x00000188 / 4) /* Oversize Frames Received */ -#define GEM_RXJABCNT (0x0000018C / 4) /* Jabbers Received Counter */ -#define GEM_RXFCSCNT (0x00000190 / 4) /* Frame Check seq. Error Counter */ -#define GEM_RXLENERRCNT (0x00000194 / 4) /* Length Field Error Counter */ -#define GEM_RXSYMERRCNT (0x00000198 / 4) /* Symbol Error Counter */ -#define GEM_RXALIGNERRCNT (0x0000019C / 4) /* Alignment Error Counter */ -#define GEM_RXRSCERRCNT (0x000001A0 / 4) /* Receive Resource Error Counter */ -#define GEM_RXORUNCNT (0x000001A4 / 4) /* Receive Overrun Counter */ -#define GEM_RXIPCSERRCNT (0x000001A8 / 4) /* IP header Checksum Err Counter */ -#define GEM_RXTCPCCNT (0x000001AC / 4) /* TCP Checksum Error Counter */ -#define GEM_RXUDPCCNT (0x000001B0 / 4) /* UDP Checksum Error Counter */ +REG32(NWCTRL, 0x0) /* Network Control reg */ + FIELD(NWCTRL, LOOPBACK , 0, 1) + FIELD(NWCTRL, LOOPBACK_LOCAL , 1, 1) + FIELD(NWCTRL, ENABLE_RECEIVE, 2, 1) + FIELD(NWCTRL, ENABLE_TRANSMIT, 3, 1) + FIELD(NWCTRL, MAN_PORT_EN , 4, 1) + FIELD(NWCTRL, CLEAR_ALL_STATS_REGS , 5, 1) + FIELD(NWCTRL, INC_ALL_STATS_REGS, 6, 1) + FIELD(NWCTRL, STATS_WRITE_EN, 7, 1) + FIELD(NWCTRL, BACK_PRESSURE, 8, 1) + FIELD(NWCTRL, TRANSMIT_START , 9, 1) + FIELD(NWCTRL, TRANSMIT_HALT, 10, 1) + FIELD(NWCTRL, TX_PAUSE_FRAME_RE, 11, 1) + FIELD(NWCTRL, TX_PAUSE_FRAME_ZE, 12, 1) + FIELD(NWCTRL, STATS_TAKE_SNAP, 13, 1) + FIELD(NWCTRL, STATS_READ_SNAP, 14, 1) + FIELD(NWCTRL, STORE_RX_TS, 15, 1) + FIELD(NWCTRL, PFC_ENABLE, 16, 1) + FIELD(NWCTRL, PFC_PRIO_BASED, 17, 1) + FIELD(NWCTRL, FLUSH_RX_PKT_PCLK , 18, 1) + FIELD(NWCTRL, TX_LPI_EN, 19, 1) + FIELD(NWCTRL, PTP_UNICAST_ENA, 20, 1) + FIELD(NWCTRL, ALT_SGMII_MODE, 21, 1) + FIELD(NWCTRL, STORE_UDP_OFFSET, 22, 1) + FIELD(NWCTRL, EXT_TSU_PORT_EN, 23, 1) + FIELD(NWCTRL, ONE_STEP_SYNC_MO, 24, 1) + FIELD(NWCTRL, PFC_CTRL , 25, 1) + FIELD(NWCTRL, EXT_RXQ_SEL_EN , 26, 1) + FIELD(NWCTRL, OSS_CORRECTION_FIELD, 27, 1) + FIELD(NWCTRL, SEL_MII_ON_RGMII, 28, 1) + FIELD(NWCTRL, TWO_PT_FIVE_GIG, 29, 1) + FIELD(NWCTRL, IFG_EATS_QAV_CREDIT, 30, 1) -#define GEM_1588S (0x000001D0 / 4) /* 1588 Timer Seconds */ -#define GEM_1588NS (0x000001D4 / 4) /* 1588 Timer Nanoseconds */ -#define GEM_1588ADJ (0x000001D8 / 4) /* 1588 Timer Adjust */ -#define GEM_1588INC (0x000001DC / 4) /* 1588 Timer Increment */ -#define GEM_PTPETXS (0x000001E0 / 4) /* PTP Event Frame Transmitted (s) */ -#define GEM_PTPETXNS (0x000001E4 / 4) /* - * PTP Event Frame Transmitted (ns) - */ -#define GEM_PTPERXS (0x000001E8 / 4) /* PTP Event Frame Received (s) */ -#define GEM_PTPERXNS (0x000001EC / 4) /* PTP Event Frame Received (ns) */ -#define GEM_PTPPTXS (0x000001E0 / 4) /* PTP Peer Frame Transmitted (s) */ -#define GEM_PTPPTXNS (0x000001E4 / 4) /* PTP Peer Frame Transmitted (ns) */ -#define GEM_PTPPRXS (0x000001E8 / 4) /* PTP Peer Frame Received (s) */ -#define GEM_PTPPRXNS (0x000001EC / 4) /* PTP Peer Frame Received (ns) */ +REG32(NWCFG, 0x4) /* Network Config reg */ + FIELD(NWCFG, SPEED, 0, 1) + FIELD(NWCFG, FULL_DUPLEX, 1, 1) + FIELD(NWCFG, DISCARD_NON_VLAN_FRAMES, 2, 1) + FIELD(NWCFG, JUMBO_FRAMES, 3, 1) + FIELD(NWCFG, PROMISC, 4, 1) + FIELD(NWCFG, NO_BROADCAST, 5, 1) + FIELD(NWCFG, MULTICAST_HASH_EN, 6, 1) + FIELD(NWCFG, UNICAST_HASH_EN, 7, 1) + FIELD(NWCFG, RECV_1536_BYTE_FRAMES, 8, 1) + FIELD(NWCFG, EXTERNAL_ADDR_MATCH_EN, 9, 1) + FIELD(NWCFG, GIGABIT_MODE_ENABLE, 10, 1) + FIELD(NWCFG, PCS_SELECT, 11, 1) + FIELD(NWCFG, RETRY_TEST, 12, 1) + FIELD(NWCFG, PAUSE_ENABLE, 13, 1) + FIELD(NWCFG, RECV_BUF_OFFSET, 14, 2) + FIELD(NWCFG, LEN_ERR_DISCARD, 16, 1) + FIELD(NWCFG, FCS_REMOVE, 17, 1) + FIELD(NWCFG, MDC_CLOCK_DIV, 18, 3) + FIELD(NWCFG, DATA_BUS_WIDTH, 21, 2) + FIELD(NWCFG, DISABLE_COPY_PAUSE_FRAMES, 23, 1) + FIELD(NWCFG, RECV_CSUM_OFFLOAD_EN, 24, 1) + FIELD(NWCFG, EN_HALF_DUPLEX_RX, 25, 1) + FIELD(NWCFG, IGNORE_RX_FCS, 26, 1) + FIELD(NWCFG, SGMII_MODE_ENABLE, 27, 1) + FIELD(NWCFG, IPG_STRETCH_ENABLE, 28, 1) + FIELD(NWCFG, NSP_ACCEPT, 29, 1) + FIELD(NWCFG, IGNORE_IPG_RX_ER, 30, 1) + FIELD(NWCFG, UNI_DIRECTION_ENABLE, 31, 1) + +REG32(NWSTATUS, 0x8) /* Network Status reg */ +REG32(USERIO, 0xc) /* User IO reg */ + +REG32(DMACFG, 0x10) /* DMA Control reg */ + FIELD(DMACFG, SEND_BCAST_TO_ALL_QS, 31, 1) + FIELD(DMACFG, DMA_ADDR_BUS_WIDTH, 30, 1) + FIELD(DMACFG, TX_BD_EXT_MODE_EN , 29, 1) + FIELD(DMACFG, RX_BD_EXT_MODE_EN , 28, 1) + FIELD(DMACFG, FORCE_MAX_AMBA_BURST_TX, 26, 1) + FIELD(DMACFG, FORCE_MAX_AMBA_BURST_RX, 25, 1) + FIELD(DMACFG, FORCE_DISCARD_ON_ERR, 24, 1) + FIELD(DMACFG, RX_BUF_SIZE, 16, 8) + FIELD(DMACFG, CRC_ERROR_REPORT, 13, 1) + FIELD(DMACFG, INF_LAST_DBUF_SIZE_EN, 12, 1) + FIELD(DMACFG, TX_PBUF_CSUM_OFFLOAD, 11, 1) + FIELD(DMACFG, TX_PBUF_SIZE, 10, 1) + FIELD(DMACFG, RX_PBUF_SIZE, 8, 2) + FIELD(DMACFG, ENDIAN_SWAP_PACKET, 7, 1) + FIELD(DMACFG, ENDIAN_SWAP_MGNT, 6, 1) + FIELD(DMACFG, HDR_DATA_SPLIT_EN, 5, 1) + FIELD(DMACFG, AMBA_BURST_LEN , 0, 5) +#define GEM_DMACFG_RBUFSZ_MUL 64 /* DMA RX Buffer Size multiplier */ + +REG32(TXSTATUS, 0x14) /* TX Status reg */ + FIELD(TXSTATUS, TX_USED_BIT_READ_MIDFRAME, 12, 1) + FIELD(TXSTATUS, TX_FRAME_TOO_LARGE, 11, 1) + FIELD(TXSTATUS, TX_DMA_LOCKUP, 10, 1) + FIELD(TXSTATUS, TX_MAC_LOCKUP, 9, 1) + FIELD(TXSTATUS, RESP_NOT_OK, 8, 1) + FIELD(TXSTATUS, LATE_COLLISION, 7, 1) + FIELD(TXSTATUS, TRANSMIT_UNDER_RUN, 6, 1) + FIELD(TXSTATUS, TRANSMIT_COMPLETE, 5, 1) + FIELD(TXSTATUS, AMBA_ERROR, 4, 1) + FIELD(TXSTATUS, TRANSMIT_GO, 3, 1) + FIELD(TXSTATUS, RETRY_LIMIT, 2, 1) + FIELD(TXSTATUS, COLLISION, 1, 1) + FIELD(TXSTATUS, USED_BIT_READ, 0, 1) + +REG32(RXQBASE, 0x18) /* RX Q Base address reg */ +REG32(TXQBASE, 0x1c) /* TX Q Base address reg */ +REG32(RXSTATUS, 0x20) /* RX Status reg */ + FIELD(RXSTATUS, RX_DMA_LOCKUP, 5, 1) + FIELD(RXSTATUS, RX_MAC_LOCKUP, 4, 1) + FIELD(RXSTATUS, RESP_NOT_OK, 3, 1) + FIELD(RXSTATUS, RECEIVE_OVERRUN, 2, 1) + FIELD(RXSTATUS, FRAME_RECEIVED, 1, 1) + FIELD(RXSTATUS, BUF_NOT_AVAILABLE, 0, 1) + +REG32(ISR, 0x24) /* Interrupt Status reg */ + FIELD(ISR, TX_LOCKUP, 31, 1) + FIELD(ISR, RX_LOCKUP, 30, 1) + FIELD(ISR, TSU_TIMER, 29, 1) + FIELD(ISR, WOL, 28, 1) + FIELD(ISR, RECV_LPI, 27, 1) + FIELD(ISR, TSU_SEC_INCR, 26, 1) + FIELD(ISR, PTP_PDELAY_RESP_XMIT, 25, 1) + FIELD(ISR, PTP_PDELAY_REQ_XMIT, 24, 1) + FIELD(ISR, PTP_PDELAY_RESP_RECV, 23, 1) + FIELD(ISR, PTP_PDELAY_REQ_RECV, 22, 1) + FIELD(ISR, PTP_SYNC_XMIT, 21, 1) + FIELD(ISR, PTP_DELAY_REQ_XMIT, 20, 1) + FIELD(ISR, PTP_SYNC_RECV, 19, 1) + FIELD(ISR, PTP_DELAY_REQ_RECV, 18, 1) + FIELD(ISR, PCS_LP_PAGE_RECV, 17, 1) + FIELD(ISR, PCS_AN_COMPLETE, 16, 1) + FIELD(ISR, EXT_IRQ, 15, 1) + FIELD(ISR, PAUSE_FRAME_XMIT, 14, 1) + FIELD(ISR, PAUSE_TIME_ELAPSED, 13, 1) + FIELD(ISR, PAUSE_FRAME_RECV, 12, 1) + FIELD(ISR, RESP_NOT_OK, 11, 1) + FIELD(ISR, RECV_OVERRUN, 10, 1) + FIELD(ISR, LINK_CHANGE, 9, 1) + FIELD(ISR, USXGMII_INT, 8, 1) + FIELD(ISR, XMIT_COMPLETE, 7, 1) + FIELD(ISR, AMBA_ERROR, 6, 1) + FIELD(ISR, RETRY_EXCEEDED, 5, 1) + FIELD(ISR, XMIT_UNDER_RUN, 4, 1) + FIELD(ISR, TX_USED, 3, 1) + FIELD(ISR, RX_USED, 2, 1) + FIELD(ISR, RECV_COMPLETE, 1, 1) + FIELD(ISR, MGNT_FRAME_SENT, 0, 1) +REG32(IER, 0x28) /* Interrupt Enable reg */ +REG32(IDR, 0x2c) /* Interrupt Disable reg */ +REG32(IMR, 0x30) /* Interrupt Mask reg */ + +REG32(PHYMNTNC, 0x34) /* Phy Maintenance reg */ + FIELD(PHYMNTNC, DATA, 0, 16) + FIELD(PHYMNTNC, REG_ADDR, 18, 5) + FIELD(PHYMNTNC, PHY_ADDR, 23, 5) + FIELD(PHYMNTNC, OP, 28, 2) + FIELD(PHYMNTNC, ST, 30, 2) +#define MDIO_OP_READ 0x2 +#define MDIO_OP_WRITE 0x1 + +REG32(RXPAUSE, 0x38) /* RX Pause Time reg */ +REG32(TXPAUSE, 0x3c) /* TX Pause Time reg */ +REG32(TXPARTIALSF, 0x40) /* TX Partial Store and Forward */ +REG32(RXPARTIALSF, 0x44) /* RX Partial Store and Forward */ +REG32(JUMBO_MAX_LEN, 0x48) /* Max Jumbo Frame Size */ +REG32(HASHLO, 0x80) /* Hash Low address reg */ +REG32(HASHHI, 0x84) /* Hash High address reg */ +REG32(SPADDR1LO, 0x88) /* Specific addr 1 low reg */ +REG32(SPADDR1HI, 0x8c) /* Specific addr 1 high reg */ +REG32(SPADDR2LO, 0x90) /* Specific addr 2 low reg */ +REG32(SPADDR2HI, 0x94) /* Specific addr 2 high reg */ +REG32(SPADDR3LO, 0x98) /* Specific addr 3 low reg */ +REG32(SPADDR3HI, 0x9c) /* Specific addr 3 high reg */ +REG32(SPADDR4LO, 0xa0) /* Specific addr 4 low reg */ +REG32(SPADDR4HI, 0xa4) /* Specific addr 4 high reg */ +REG32(TIDMATCH1, 0xa8) /* Type ID1 Match reg */ +REG32(TIDMATCH2, 0xac) /* Type ID2 Match reg */ +REG32(TIDMATCH3, 0xb0) /* Type ID3 Match reg */ +REG32(TIDMATCH4, 0xb4) /* Type ID4 Match reg */ +REG32(WOLAN, 0xb8) /* Wake on LAN reg */ +REG32(IPGSTRETCH, 0xbc) /* IPG Stretch reg */ +REG32(SVLAN, 0xc0) /* Stacked VLAN reg */ +REG32(MODID, 0xfc) /* Module ID reg */ +REG32(OCTTXLO, 0x100) /* Octets transmitted Low reg */ +REG32(OCTTXHI, 0x104) /* Octets transmitted High reg */ +REG32(TXCNT, 0x108) /* Error-free Frames transmitted */ +REG32(TXBCNT, 0x10c) /* Error-free Broadcast Frames */ +REG32(TXMCNT, 0x110) /* Error-free Multicast Frame */ +REG32(TXPAUSECNT, 0x114) /* Pause Frames Transmitted */ +REG32(TX64CNT, 0x118) /* Error-free 64 TX */ +REG32(TX65CNT, 0x11c) /* Error-free 65-127 TX */ +REG32(TX128CNT, 0x120) /* Error-free 128-255 TX */ +REG32(TX256CNT, 0x124) /* Error-free 256-511 */ +REG32(TX512CNT, 0x128) /* Error-free 512-1023 TX */ +REG32(TX1024CNT, 0x12c) /* Error-free 1024-1518 TX */ +REG32(TX1519CNT, 0x130) /* Error-free larger than 1519 TX */ +REG32(TXURUNCNT, 0x134) /* TX under run error counter */ +REG32(SINGLECOLLCNT, 0x138) /* Single Collision Frames */ +REG32(MULTCOLLCNT, 0x13c) /* Multiple Collision Frames */ +REG32(EXCESSCOLLCNT, 0x140) /* Excessive Collision Frames */ +REG32(LATECOLLCNT, 0x144) /* Late Collision Frames */ +REG32(DEFERTXCNT, 0x148) /* Deferred Transmission Frames */ +REG32(CSENSECNT, 0x14c) /* Carrier Sense Error Counter */ +REG32(OCTRXLO, 0x150) /* Octets Received register Low */ +REG32(OCTRXHI, 0x154) /* Octets Received register High */ +REG32(RXCNT, 0x158) /* Error-free Frames Received */ +REG32(RXBROADCNT, 0x15c) /* Error-free Broadcast Frames RX */ +REG32(RXMULTICNT, 0x160) /* Error-free Multicast Frames RX */ +REG32(RXPAUSECNT, 0x164) /* Pause Frames Received Counter */ +REG32(RX64CNT, 0x168) /* Error-free 64 byte Frames RX */ +REG32(RX65CNT, 0x16c) /* Error-free 65-127B Frames RX */ +REG32(RX128CNT, 0x170) /* Error-free 128-255B Frames RX */ +REG32(RX256CNT, 0x174) /* Error-free 256-512B Frames RX */ +REG32(RX512CNT, 0x178) /* Error-free 512-1023B Frames RX */ +REG32(RX1024CNT, 0x17c) /* Error-free 1024-1518B Frames RX */ +REG32(RX1519CNT, 0x180) /* Error-free 1519-max Frames RX */ +REG32(RXUNDERCNT, 0x184) /* Undersize Frames Received */ +REG32(RXOVERCNT, 0x188) /* Oversize Frames Received */ +REG32(RXJABCNT, 0x18c) /* Jabbers Received Counter */ +REG32(RXFCSCNT, 0x190) /* Frame Check seq. Error Counter */ +REG32(RXLENERRCNT, 0x194) /* Length Field Error Counter */ +REG32(RXSYMERRCNT, 0x198) /* Symbol Error Counter */ +REG32(RXALIGNERRCNT, 0x19c) /* Alignment Error Counter */ +REG32(RXRSCERRCNT, 0x1a0) /* Receive Resource Error Counter */ +REG32(RXORUNCNT, 0x1a4) /* Receive Overrun Counter */ +REG32(RXIPCSERRCNT, 0x1a8) /* IP header Checksum Err Counter */ +REG32(RXTCPCCNT, 0x1ac) /* TCP Checksum Error Counter */ +REG32(RXUDPCCNT, 0x1b0) /* UDP Checksum Error Counter */ + +REG32(1588S, 0x1d0) /* 1588 Timer Seconds */ +REG32(1588NS, 0x1d4) /* 1588 Timer Nanoseconds */ +REG32(1588ADJ, 0x1d8) /* 1588 Timer Adjust */ +REG32(1588INC, 0x1dc) /* 1588 Timer Increment */ +REG32(PTPETXS, 0x1e0) /* PTP Event Frame Transmitted (s) */ +REG32(PTPETXNS, 0x1e4) /* PTP Event Frame Transmitted (ns) */ +REG32(PTPERXS, 0x1e8) /* PTP Event Frame Received (s) */ +REG32(PTPERXNS, 0x1ec) /* PTP Event Frame Received (ns) */ +REG32(PTPPTXS, 0x1e0) /* PTP Peer Frame Transmitted (s) */ +REG32(PTPPTXNS, 0x1e4) /* PTP Peer Frame Transmitted (ns) */ +REG32(PTPPRXS, 0x1e8) /* PTP Peer Frame Received (s) */ +REG32(PTPPRXNS, 0x1ec) /* PTP Peer Frame Received (ns) */ /* Design Configuration Registers */ -#define GEM_DESCONF (0x00000280 / 4) -#define GEM_DESCONF2 (0x00000284 / 4) -#define GEM_DESCONF3 (0x00000288 / 4) -#define GEM_DESCONF4 (0x0000028C / 4) -#define GEM_DESCONF5 (0x00000290 / 4) -#define GEM_DESCONF6 (0x00000294 / 4) -#define GEM_DESCONF6_64B_MASK (1U << 23) -#define GEM_DESCONF7 (0x00000298 / 4) +REG32(DESCONF, 0x280) +REG32(DESCONF2, 0x284) +REG32(DESCONF3, 0x288) +REG32(DESCONF4, 0x28c) +REG32(DESCONF5, 0x290) +REG32(DESCONF6, 0x294) + FIELD(DESCONF6, DMA_ADDR_64B, 23, 1) +REG32(DESCONF7, 0x298) -#define GEM_INT_Q1_STATUS (0x00000400 / 4) -#define GEM_INT_Q1_MASK (0x00000640 / 4) +REG32(INT_Q1_STATUS, 0x400) +REG32(INT_Q1_MASK, 0x640) -#define GEM_TRANSMIT_Q1_PTR (0x00000440 / 4) -#define GEM_TRANSMIT_Q7_PTR (GEM_TRANSMIT_Q1_PTR + 6) +REG32(TRANSMIT_Q1_PTR, 0x440) +REG32(TRANSMIT_Q7_PTR, 0x458) -#define GEM_RECEIVE_Q1_PTR (0x00000480 / 4) -#define GEM_RECEIVE_Q7_PTR (GEM_RECEIVE_Q1_PTR + 6) +REG32(RECEIVE_Q1_PTR, 0x480) +REG32(RECEIVE_Q7_PTR, 0x498) -#define GEM_TBQPH (0x000004C8 / 4) -#define GEM_RBQPH (0x000004D4 / 4) +REG32(TBQPH, 0x4c8) +REG32(RBQPH, 0x4d4) -#define GEM_INT_Q1_ENABLE (0x00000600 / 4) -#define GEM_INT_Q7_ENABLE (GEM_INT_Q1_ENABLE + 6) +REG32(INT_Q1_ENABLE, 0x600) +REG32(INT_Q7_ENABLE, 0x618) -#define GEM_INT_Q1_DISABLE (0x00000620 / 4) -#define GEM_INT_Q7_DISABLE (GEM_INT_Q1_DISABLE + 6) +REG32(INT_Q1_DISABLE, 0x620) +REG32(INT_Q7_DISABLE, 0x638) -#define GEM_INT_Q1_MASK (0x00000640 / 4) -#define GEM_INT_Q7_MASK (GEM_INT_Q1_MASK + 6) +REG32(SCREENING_TYPE1_REG0, 0x500) + FIELD(SCREENING_TYPE1_REG0, QUEUE_NUM, 0, 4) + FIELD(SCREENING_TYPE1_REG0, DSTC_MATCH, 4, 8) + FIELD(SCREENING_TYPE1_REG0, UDP_PORT_MATCH, 12, 16) + FIELD(SCREENING_TYPE1_REG0, DSTC_ENABLE, 28, 1) + FIELD(SCREENING_TYPE1_REG0, UDP_PORT_MATCH_EN, 29, 1) + FIELD(SCREENING_TYPE1_REG0, DROP_ON_MATCH, 30, 1) -#define GEM_SCREENING_TYPE1_REGISTER_0 (0x00000500 / 4) +REG32(SCREENING_TYPE2_REG0, 0x540) + FIELD(SCREENING_TYPE2_REG0, QUEUE_NUM, 0, 4) + FIELD(SCREENING_TYPE2_REG0, VLAN_PRIORITY, 4, 3) + FIELD(SCREENING_TYPE2_REG0, VLAN_ENABLE, 8, 1) + FIELD(SCREENING_TYPE2_REG0, ETHERTYPE_REG_INDEX, 9, 3) + FIELD(SCREENING_TYPE2_REG0, ETHERTYPE_ENABLE, 12, 1) + FIELD(SCREENING_TYPE2_REG0, COMPARE_A, 13, 5) + FIELD(SCREENING_TYPE2_REG0, COMPARE_A_ENABLE, 18, 1) + FIELD(SCREENING_TYPE2_REG0, COMPARE_B, 19, 5) + FIELD(SCREENING_TYPE2_REG0, COMPARE_B_ENABLE, 24, 1) + FIELD(SCREENING_TYPE2_REG0, COMPARE_C, 25, 5) + FIELD(SCREENING_TYPE2_REG0, COMPARE_C_ENABLE, 30, 1) + FIELD(SCREENING_TYPE2_REG0, DROP_ON_MATCH, 31, 1) -#define GEM_ST1R_UDP_PORT_MATCH_ENABLE (1 << 29) -#define GEM_ST1R_DSTC_ENABLE (1 << 28) -#define GEM_ST1R_UDP_PORT_MATCH_SHIFT (12) -#define GEM_ST1R_UDP_PORT_MATCH_WIDTH (27 - GEM_ST1R_UDP_PORT_MATCH_SHIFT + 1) -#define GEM_ST1R_DSTC_MATCH_SHIFT (4) -#define GEM_ST1R_DSTC_MATCH_WIDTH (11 - GEM_ST1R_DSTC_MATCH_SHIFT + 1) -#define GEM_ST1R_QUEUE_SHIFT (0) -#define GEM_ST1R_QUEUE_WIDTH (3 - GEM_ST1R_QUEUE_SHIFT + 1) +REG32(SCREENING_TYPE2_ETHERTYPE_REG0, 0x6e0) -#define GEM_SCREENING_TYPE2_REGISTER_0 (0x00000540 / 4) +REG32(TYPE2_COMPARE_0_WORD_0, 0x700) + FIELD(TYPE2_COMPARE_0_WORD_0, MASK_VALUE, 0, 16) + FIELD(TYPE2_COMPARE_0_WORD_0, COMPARE_VALUE, 16, 16) -#define GEM_ST2R_COMPARE_A_ENABLE (1 << 18) -#define GEM_ST2R_COMPARE_A_SHIFT (13) -#define GEM_ST2R_COMPARE_WIDTH (17 - GEM_ST2R_COMPARE_A_SHIFT + 1) -#define GEM_ST2R_ETHERTYPE_ENABLE (1 << 12) -#define GEM_ST2R_ETHERTYPE_INDEX_SHIFT (9) -#define GEM_ST2R_ETHERTYPE_INDEX_WIDTH (11 - GEM_ST2R_ETHERTYPE_INDEX_SHIFT \ - + 1) -#define GEM_ST2R_QUEUE_SHIFT (0) -#define GEM_ST2R_QUEUE_WIDTH (3 - GEM_ST2R_QUEUE_SHIFT + 1) - -#define GEM_SCREENING_TYPE2_ETHERTYPE_REG_0 (0x000006e0 / 4) -#define GEM_TYPE2_COMPARE_0_WORD_0 (0x00000700 / 4) - -#define GEM_T2CW1_COMPARE_OFFSET_SHIFT (7) -#define GEM_T2CW1_COMPARE_OFFSET_WIDTH (8 - GEM_T2CW1_COMPARE_OFFSET_SHIFT + 1) -#define GEM_T2CW1_OFFSET_VALUE_SHIFT (0) -#define GEM_T2CW1_OFFSET_VALUE_WIDTH (6 - GEM_T2CW1_OFFSET_VALUE_SHIFT + 1) +REG32(TYPE2_COMPARE_0_WORD_1, 0x704) + FIELD(TYPE2_COMPARE_0_WORD_1, OFFSET_VALUE, 0, 7) + FIELD(TYPE2_COMPARE_0_WORD_1, COMPARE_OFFSET, 7, 2) + FIELD(TYPE2_COMPARE_0_WORD_1, DISABLE_MASK, 9, 1) + FIELD(TYPE2_COMPARE_0_WORD_1, COMPARE_VLAN_ID, 10, 1) /*****************************************/ -#define GEM_NWCTRL_TXSTART 0x00000200 /* Transmit Enable */ -#define GEM_NWCTRL_TXENA 0x00000008 /* Transmit Enable */ -#define GEM_NWCTRL_RXENA 0x00000004 /* Receive Enable */ -#define GEM_NWCTRL_LOCALLOOP 0x00000002 /* Local Loopback */ -#define GEM_NWCFG_STRIP_FCS 0x00020000 /* Strip FCS field */ -#define GEM_NWCFG_LERR_DISC 0x00010000 /* Discard RX frames with len err */ -#define GEM_NWCFG_BUFF_OFST_M 0x0000C000 /* Receive buffer offset mask */ -#define GEM_NWCFG_BUFF_OFST_S 14 /* Receive buffer offset shift */ -#define GEM_NWCFG_RCV_1538 0x00000100 /* Receive 1538 bytes frame */ -#define GEM_NWCFG_UCAST_HASH 0x00000080 /* accept unicast if hash match */ -#define GEM_NWCFG_MCAST_HASH 0x00000040 /* accept multicast if hash match */ -#define GEM_NWCFG_BCAST_REJ 0x00000020 /* Reject broadcast packets */ -#define GEM_NWCFG_PROMISC 0x00000010 /* Accept all packets */ -#define GEM_NWCFG_JUMBO_FRAME 0x00000008 /* Jumbo Frames enable */ -#define GEM_DMACFG_ADDR_64B (1U << 30) -#define GEM_DMACFG_TX_BD_EXT (1U << 29) -#define GEM_DMACFG_RX_BD_EXT (1U << 28) -#define GEM_DMACFG_RBUFSZ_M 0x00FF0000 /* DMA RX Buffer Size mask */ -#define GEM_DMACFG_RBUFSZ_S 16 /* DMA RX Buffer Size shift */ -#define GEM_DMACFG_RBUFSZ_MUL 64 /* DMA RX Buffer Size multiplier */ -#define GEM_DMACFG_TXCSUM_OFFL 0x00000800 /* Transmit checksum offload */ - -#define GEM_TXSTATUS_TXCMPL 0x00000020 /* Transmit Complete */ -#define GEM_TXSTATUS_USED 0x00000001 /* sw owned descriptor encountered */ - -#define GEM_RXSTATUS_FRMRCVD 0x00000002 /* Frame received */ -#define GEM_RXSTATUS_NOBUF 0x00000001 /* Buffer unavailable */ - -/* GEM_ISR GEM_IER GEM_IDR GEM_IMR */ -#define GEM_INT_TXCMPL 0x00000080 /* Transmit Complete */ -#define GEM_INT_AMBA_ERR 0x00000040 -#define GEM_INT_TXUSED 0x00000008 -#define GEM_INT_RXUSED 0x00000004 -#define GEM_INT_RXCMPL 0x00000002 - -#define GEM_PHYMNTNC_OP_R 0x20000000 /* read operation */ -#define GEM_PHYMNTNC_OP_W 0x10000000 /* write operation */ -#define GEM_PHYMNTNC_ADDR 0x0F800000 /* Address bits */ -#define GEM_PHYMNTNC_ADDR_SHFT 23 -#define GEM_PHYMNTNC_REG 0x007C0000 /* register bits */ -#define GEM_PHYMNTNC_REG_SHIFT 18 /* Marvell PHY definitions */ #define BOARD_PHY_ADDRESS 0 /* PHY address we will emulate a device at */ @@ -325,7 +426,7 @@ static inline uint64_t tx_desc_get_buffer(CadenceGEMState *s, uint32_t *desc) { uint64_t ret = desc[0]; - if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) { + if (FIELD_EX32(s->regs[R_DMACFG], DMACFG, DMA_ADDR_BUS_WIDTH)) { ret |= (uint64_t)desc[2] << 32; } return ret; @@ -370,7 +471,7 @@ static inline uint64_t rx_desc_get_buffer(CadenceGEMState *s, uint32_t *desc) { uint64_t ret = desc[0] & ~0x3UL; - if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) { + if (FIELD_EX32(s->regs[R_DMACFG], DMACFG, DMA_ADDR_BUS_WIDTH)) { ret |= (uint64_t)desc[2] << 32; } return ret; @@ -380,11 +481,11 @@ static inline int gem_get_desc_len(CadenceGEMState *s, bool rx_n_tx) { int ret = 2; - if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) { + if (FIELD_EX32(s->regs[R_DMACFG], DMACFG, DMA_ADDR_BUS_WIDTH)) { ret += 2; } - if (s->regs[GEM_DMACFG] & (rx_n_tx ? GEM_DMACFG_RX_BD_EXT - : GEM_DMACFG_TX_BD_EXT)) { + if (s->regs[R_DMACFG] & (rx_n_tx ? R_DMACFG_RX_BD_EXT_MODE_EN_MASK + : R_DMACFG_TX_BD_EXT_MODE_EN_MASK)) { ret += 2; } @@ -456,8 +557,8 @@ static const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static uint32_t gem_get_max_buf_len(CadenceGEMState *s, bool tx) { uint32_t size; - if (s->regs[GEM_NWCFG] & GEM_NWCFG_JUMBO_FRAME) { - size = s->regs[GEM_JUMBO_MAX_LEN]; + if (FIELD_EX32(s->regs[R_NWCFG], NWCFG, JUMBO_FRAMES)) { + size = s->regs[R_JUMBO_MAX_LEN]; if (size > s->jumbo_max_len) { size = s->jumbo_max_len; qemu_log_mask(LOG_GUEST_ERROR, "GEM_JUMBO_MAX_LEN reg cannot be" @@ -466,7 +567,8 @@ static uint32_t gem_get_max_buf_len(CadenceGEMState *s, bool tx) } else if (tx) { size = 1518; } else { - size = s->regs[GEM_NWCFG] & GEM_NWCFG_RCV_1538 ? 1538 : 1518; + size = FIELD_EX32(s->regs[R_NWCFG], + NWCFG, RECV_1536_BYTE_FRAMES) ? 1538 : 1518; } return size; } @@ -474,10 +576,10 @@ static uint32_t gem_get_max_buf_len(CadenceGEMState *s, bool tx) static void gem_set_isr(CadenceGEMState *s, int q, uint32_t flag) { if (q == 0) { - s->regs[GEM_ISR] |= flag & ~(s->regs[GEM_IMR]); + s->regs[R_ISR] |= flag & ~(s->regs[R_IMR]); } else { - s->regs[GEM_INT_Q1_STATUS + q - 1] |= flag & - ~(s->regs[GEM_INT_Q1_MASK + q - 1]); + s->regs[R_INT_Q1_STATUS + q - 1] |= flag & + ~(s->regs[R_INT_Q1_MASK + q - 1]); } } @@ -491,43 +593,43 @@ static void gem_init_register_masks(CadenceGEMState *s) unsigned int i; /* Mask of register bits which are read only */ memset(&s->regs_ro[0], 0, sizeof(s->regs_ro)); - s->regs_ro[GEM_NWCTRL] = 0xFFF80000; - s->regs_ro[GEM_NWSTATUS] = 0xFFFFFFFF; - s->regs_ro[GEM_DMACFG] = 0x8E00F000; - s->regs_ro[GEM_TXSTATUS] = 0xFFFFFE08; - s->regs_ro[GEM_RXQBASE] = 0x00000003; - s->regs_ro[GEM_TXQBASE] = 0x00000003; - s->regs_ro[GEM_RXSTATUS] = 0xFFFFFFF0; - s->regs_ro[GEM_ISR] = 0xFFFFFFFF; - s->regs_ro[GEM_IMR] = 0xFFFFFFFF; - s->regs_ro[GEM_MODID] = 0xFFFFFFFF; + s->regs_ro[R_NWCTRL] = 0xFFF80000; + s->regs_ro[R_NWSTATUS] = 0xFFFFFFFF; + s->regs_ro[R_DMACFG] = 0x8E00F000; + s->regs_ro[R_TXSTATUS] = 0xFFFFFE08; + s->regs_ro[R_RXQBASE] = 0x00000003; + s->regs_ro[R_TXQBASE] = 0x00000003; + s->regs_ro[R_RXSTATUS] = 0xFFFFFFF0; + s->regs_ro[R_ISR] = 0xFFFFFFFF; + s->regs_ro[R_IMR] = 0xFFFFFFFF; + s->regs_ro[R_MODID] = 0xFFFFFFFF; for (i = 0; i < s->num_priority_queues; i++) { - s->regs_ro[GEM_INT_Q1_STATUS + i] = 0xFFFFFFFF; - s->regs_ro[GEM_INT_Q1_ENABLE + i] = 0xFFFFF319; - s->regs_ro[GEM_INT_Q1_DISABLE + i] = 0xFFFFF319; - s->regs_ro[GEM_INT_Q1_MASK + i] = 0xFFFFFFFF; + s->regs_ro[R_INT_Q1_STATUS + i] = 0xFFFFFFFF; + s->regs_ro[R_INT_Q1_ENABLE + i] = 0xFFFFF319; + s->regs_ro[R_INT_Q1_DISABLE + i] = 0xFFFFF319; + s->regs_ro[R_INT_Q1_MASK + i] = 0xFFFFFFFF; } /* Mask of register bits which are clear on read */ memset(&s->regs_rtc[0], 0, sizeof(s->regs_rtc)); - s->regs_rtc[GEM_ISR] = 0xFFFFFFFF; + s->regs_rtc[R_ISR] = 0xFFFFFFFF; for (i = 0; i < s->num_priority_queues; i++) { - s->regs_rtc[GEM_INT_Q1_STATUS + i] = 0x00000CE6; + s->regs_rtc[R_INT_Q1_STATUS + i] = 0x00000CE6; } /* Mask of register bits which are write 1 to clear */ memset(&s->regs_w1c[0], 0, sizeof(s->regs_w1c)); - s->regs_w1c[GEM_TXSTATUS] = 0x000001F7; - s->regs_w1c[GEM_RXSTATUS] = 0x0000000F; + s->regs_w1c[R_TXSTATUS] = 0x000001F7; + s->regs_w1c[R_RXSTATUS] = 0x0000000F; /* Mask of register bits which are write only */ memset(&s->regs_wo[0], 0, sizeof(s->regs_wo)); - s->regs_wo[GEM_NWCTRL] = 0x00073E60; - s->regs_wo[GEM_IER] = 0x07FFFFFF; - s->regs_wo[GEM_IDR] = 0x07FFFFFF; + s->regs_wo[R_NWCTRL] = 0x00073E60; + s->regs_wo[R_IER] = 0x07FFFFFF; + s->regs_wo[R_IDR] = 0x07FFFFFF; for (i = 0; i < s->num_priority_queues; i++) { - s->regs_wo[GEM_INT_Q1_ENABLE + i] = 0x00000CE6; - s->regs_wo[GEM_INT_Q1_DISABLE + i] = 0x00000CE6; + s->regs_wo[R_INT_Q1_ENABLE + i] = 0x00000CE6; + s->regs_wo[R_INT_Q1_DISABLE + i] = 0x00000CE6; } } @@ -561,7 +663,7 @@ static bool gem_can_receive(NetClientState *nc) s = qemu_get_nic_opaque(nc); /* Do nothing if receive is not enabled. */ - if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) { + if (!FIELD_EX32(s->regs[R_NWCTRL], NWCTRL, ENABLE_RECEIVE)) { if (s->can_rx_state != 1) { s->can_rx_state = 1; DB_PRINT("can't receive - no enable\n"); @@ -598,10 +700,10 @@ static void gem_update_int_status(CadenceGEMState *s) { int i; - qemu_set_irq(s->irq[0], !!s->regs[GEM_ISR]); + qemu_set_irq(s->irq[0], !!s->regs[R_ISR]); for (i = 1; i < s->num_priority_queues; ++i) { - qemu_set_irq(s->irq[i], !!s->regs[GEM_INT_Q1_STATUS + i - 1]); + qemu_set_irq(s->irq[i], !!s->regs[R_INT_Q1_STATUS + i - 1]); } } @@ -615,39 +717,39 @@ static void gem_receive_updatestats(CadenceGEMState *s, const uint8_t *packet, uint64_t octets; /* Total octets (bytes) received */ - octets = ((uint64_t)(s->regs[GEM_OCTRXLO]) << 32) | - s->regs[GEM_OCTRXHI]; + octets = ((uint64_t)(s->regs[R_OCTRXLO]) << 32) | + s->regs[R_OCTRXHI]; octets += bytes; - s->regs[GEM_OCTRXLO] = octets >> 32; - s->regs[GEM_OCTRXHI] = octets; + s->regs[R_OCTRXLO] = octets >> 32; + s->regs[R_OCTRXHI] = octets; /* Error-free Frames received */ - s->regs[GEM_RXCNT]++; + s->regs[R_RXCNT]++; /* Error-free Broadcast Frames counter */ if (!memcmp(packet, broadcast_addr, 6)) { - s->regs[GEM_RXBROADCNT]++; + s->regs[R_RXBROADCNT]++; } /* Error-free Multicast Frames counter */ if (packet[0] == 0x01) { - s->regs[GEM_RXMULTICNT]++; + s->regs[R_RXMULTICNT]++; } if (bytes <= 64) { - s->regs[GEM_RX64CNT]++; + s->regs[R_RX64CNT]++; } else if (bytes <= 127) { - s->regs[GEM_RX65CNT]++; + s->regs[R_RX65CNT]++; } else if (bytes <= 255) { - s->regs[GEM_RX128CNT]++; + s->regs[R_RX128CNT]++; } else if (bytes <= 511) { - s->regs[GEM_RX256CNT]++; + s->regs[R_RX256CNT]++; } else if (bytes <= 1023) { - s->regs[GEM_RX512CNT]++; + s->regs[R_RX512CNT]++; } else if (bytes <= 1518) { - s->regs[GEM_RX1024CNT]++; + s->regs[R_RX1024CNT]++; } else { - s->regs[GEM_RX1519CNT]++; + s->regs[R_RX1519CNT]++; } } @@ -706,13 +808,13 @@ static int gem_mac_address_filter(CadenceGEMState *s, const uint8_t *packet) int i, is_mc; /* Promiscuous mode? */ - if (s->regs[GEM_NWCFG] & GEM_NWCFG_PROMISC) { + if (FIELD_EX32(s->regs[R_NWCFG], NWCFG, PROMISC)) { return GEM_RX_PROMISCUOUS_ACCEPT; } if (!memcmp(packet, broadcast_addr, 6)) { /* Reject broadcast packets? */ - if (s->regs[GEM_NWCFG] & GEM_NWCFG_BCAST_REJ) { + if (FIELD_EX32(s->regs[R_NWCFG], NWCFG, NO_BROADCAST)) { return GEM_RX_REJECT; } return GEM_RX_BROADCAST_ACCEPT; @@ -720,13 +822,13 @@ static int gem_mac_address_filter(CadenceGEMState *s, const uint8_t *packet) /* Accept packets -w- hash match? */ is_mc = is_multicast_ether_addr(packet); - if ((is_mc && (s->regs[GEM_NWCFG] & GEM_NWCFG_MCAST_HASH)) || - (!is_mc && (s->regs[GEM_NWCFG] & GEM_NWCFG_UCAST_HASH))) { + if ((is_mc && (FIELD_EX32(s->regs[R_NWCFG], NWCFG, MULTICAST_HASH_EN))) || + (!is_mc && FIELD_EX32(s->regs[R_NWCFG], NWCFG, UNICAST_HASH_EN))) { uint64_t buckets; unsigned hash_index; hash_index = calc_mac_hash(packet); - buckets = ((uint64_t)s->regs[GEM_HASHHI] << 32) | s->regs[GEM_HASHLO]; + buckets = ((uint64_t)s->regs[R_HASHHI] << 32) | s->regs[R_HASHLO]; if ((buckets >> hash_index) & 1) { return is_mc ? GEM_RX_MULTICAST_HASH_ACCEPT : GEM_RX_UNICAST_HASH_ACCEPT; @@ -734,7 +836,7 @@ static int gem_mac_address_filter(CadenceGEMState *s, const uint8_t *packet) } /* Check all 4 specific addresses */ - gem_spaddr = (uint8_t *)&(s->regs[GEM_SPADDR1LO]); + gem_spaddr = (uint8_t *)&(s->regs[R_SPADDR1LO]); for (i = 3; i >= 0; i--) { if (s->sar_active[i] && !memcmp(packet, gem_spaddr + 8 * i, 6)) { return GEM_RX_SAR_ACCEPT + i; @@ -754,15 +856,14 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, int i, j; for (i = 0; i < s->num_type1_screeners; i++) { - reg = s->regs[GEM_SCREENING_TYPE1_REGISTER_0 + i]; + reg = s->regs[R_SCREENING_TYPE1_REG0 + i]; matched = false; mismatched = false; /* Screening is based on UDP Port */ - if (reg & GEM_ST1R_UDP_PORT_MATCH_ENABLE) { + if (FIELD_EX32(reg, SCREENING_TYPE1_REG0, UDP_PORT_MATCH_EN)) { uint16_t udp_port = rxbuf_ptr[14 + 22] << 8 | rxbuf_ptr[14 + 23]; - if (udp_port == extract32(reg, GEM_ST1R_UDP_PORT_MATCH_SHIFT, - GEM_ST1R_UDP_PORT_MATCH_WIDTH)) { + if (udp_port == FIELD_EX32(reg, SCREENING_TYPE1_REG0, UDP_PORT_MATCH)) { matched = true; } else { mismatched = true; @@ -770,10 +871,9 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, } /* Screening is based on DS/TC */ - if (reg & GEM_ST1R_DSTC_ENABLE) { + if (FIELD_EX32(reg, SCREENING_TYPE1_REG0, DSTC_ENABLE)) { uint8_t dscp = rxbuf_ptr[14 + 1]; - if (dscp == extract32(reg, GEM_ST1R_DSTC_MATCH_SHIFT, - GEM_ST1R_DSTC_MATCH_WIDTH)) { + if (dscp == FIELD_EX32(reg, SCREENING_TYPE1_REG0, DSTC_MATCH)) { matched = true; } else { mismatched = true; @@ -781,25 +881,25 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, } if (matched && !mismatched) { - return extract32(reg, GEM_ST1R_QUEUE_SHIFT, GEM_ST1R_QUEUE_WIDTH); + return FIELD_EX32(reg, SCREENING_TYPE1_REG0, QUEUE_NUM); } } for (i = 0; i < s->num_type2_screeners; i++) { - reg = s->regs[GEM_SCREENING_TYPE2_REGISTER_0 + i]; + reg = s->regs[R_SCREENING_TYPE2_REG0 + i]; matched = false; mismatched = false; - if (reg & GEM_ST2R_ETHERTYPE_ENABLE) { + if (FIELD_EX32(reg, SCREENING_TYPE2_REG0, ETHERTYPE_ENABLE)) { uint16_t type = rxbuf_ptr[12] << 8 | rxbuf_ptr[13]; - int et_idx = extract32(reg, GEM_ST2R_ETHERTYPE_INDEX_SHIFT, - GEM_ST2R_ETHERTYPE_INDEX_WIDTH); + int et_idx = FIELD_EX32(reg, SCREENING_TYPE2_REG0, + ETHERTYPE_REG_INDEX); if (et_idx > s->num_type2_screeners) { qemu_log_mask(LOG_GUEST_ERROR, "Out of range ethertype " "register index: %d\n", et_idx); } - if (type == s->regs[GEM_SCREENING_TYPE2_ETHERTYPE_REG_0 + + if (type == s->regs[R_SCREENING_TYPE2_ETHERTYPE_REG0 + et_idx]) { matched = true; } else { @@ -809,27 +909,27 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, /* Compare A, B, C */ for (j = 0; j < 3; j++) { - uint32_t cr0, cr1, mask; + uint32_t cr0, cr1, mask, compare; uint16_t rx_cmp; int offset; - int cr_idx = extract32(reg, GEM_ST2R_COMPARE_A_SHIFT + j * 6, - GEM_ST2R_COMPARE_WIDTH); + int cr_idx = extract32(reg, R_SCREENING_TYPE2_REG0_COMPARE_A_SHIFT + j * 6, + R_SCREENING_TYPE2_REG0_COMPARE_A_LENGTH); - if (!(reg & (GEM_ST2R_COMPARE_A_ENABLE << (j * 6)))) { + if (!extract32(reg, R_SCREENING_TYPE2_REG0_COMPARE_A_ENABLE_SHIFT + j * 6, + R_SCREENING_TYPE2_REG0_COMPARE_A_ENABLE_LENGTH)) { continue; } + if (cr_idx > s->num_type2_screeners) { qemu_log_mask(LOG_GUEST_ERROR, "Out of range compare " "register index: %d\n", cr_idx); } - cr0 = s->regs[GEM_TYPE2_COMPARE_0_WORD_0 + cr_idx * 2]; - cr1 = s->regs[GEM_TYPE2_COMPARE_0_WORD_0 + cr_idx * 2 + 1]; - offset = extract32(cr1, GEM_T2CW1_OFFSET_VALUE_SHIFT, - GEM_T2CW1_OFFSET_VALUE_WIDTH); + cr0 = s->regs[R_TYPE2_COMPARE_0_WORD_0 + cr_idx * 2]; + cr1 = s->regs[R_TYPE2_COMPARE_0_WORD_1 + cr_idx * 2]; + offset = FIELD_EX32(cr1, TYPE2_COMPARE_0_WORD_1, OFFSET_VALUE); - switch (extract32(cr1, GEM_T2CW1_COMPARE_OFFSET_SHIFT, - GEM_T2CW1_COMPARE_OFFSET_WIDTH)) { + switch (FIELD_EX32(cr1, TYPE2_COMPARE_0_WORD_1, COMPARE_OFFSET)) { case 3: /* Skip UDP header */ qemu_log_mask(LOG_UNIMP, "TCP compare offsets" "unimplemented - assuming UDP\n"); @@ -847,9 +947,10 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, } rx_cmp = rxbuf_ptr[offset] << 8 | rxbuf_ptr[offset]; - mask = extract32(cr0, 0, 16); + mask = FIELD_EX32(cr0, TYPE2_COMPARE_0_WORD_0, MASK_VALUE); + compare = FIELD_EX32(cr0, TYPE2_COMPARE_0_WORD_0, COMPARE_VALUE); - if ((rx_cmp & mask) == (extract32(cr0, 16, 16) & mask)) { + if ((rx_cmp & mask) == (compare & mask)) { matched = true; } else { mismatched = true; @@ -857,7 +958,7 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, } if (matched && !mismatched) { - return extract32(reg, GEM_ST2R_QUEUE_SHIFT, GEM_ST2R_QUEUE_WIDTH); + return FIELD_EX32(reg, SCREENING_TYPE2_REG0, QUEUE_NUM); } } @@ -871,11 +972,11 @@ static uint32_t gem_get_queue_base_addr(CadenceGEMState *s, bool tx, int q) switch (q) { case 0: - base_addr = s->regs[tx ? GEM_TXQBASE : GEM_RXQBASE]; + base_addr = s->regs[tx ? R_TXQBASE : R_RXQBASE]; break; case 1 ... (MAX_PRIORITY_QUEUES - 1): - base_addr = s->regs[(tx ? GEM_TRANSMIT_Q1_PTR : - GEM_RECEIVE_Q1_PTR) + q - 1]; + base_addr = s->regs[(tx ? R_TRANSMIT_Q1_PTR : + R_RECEIVE_Q1_PTR) + q - 1]; break; default: g_assert_not_reached(); @@ -898,8 +999,8 @@ static hwaddr gem_get_desc_addr(CadenceGEMState *s, bool tx, int q) { hwaddr desc_addr = 0; - if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) { - desc_addr = s->regs[tx ? GEM_TBQPH : GEM_RBQPH]; + if (FIELD_EX32(s->regs[R_DMACFG], DMACFG, DMA_ADDR_BUS_WIDTH)) { + desc_addr = s->regs[tx ? R_TBQPH : R_RBQPH]; } desc_addr <<= 32; desc_addr |= tx ? s->tx_desc_addr[q] : s->rx_desc_addr[q]; @@ -930,8 +1031,8 @@ static void gem_get_rx_desc(CadenceGEMState *s, int q) /* Descriptor owned by software ? */ if (rx_desc_get_ownership(s->rx_desc[q]) == 1) { DB_PRINT("descriptor 0x%" HWADDR_PRIx " owned by sw.\n", desc_addr); - s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF; - gem_set_isr(s, q, GEM_INT_RXUSED); + s->regs[R_RXSTATUS] |= R_RXSTATUS_BUF_NOT_AVAILABLE_MASK; + gem_set_isr(s, q, R_ISR_RX_USED_MASK); /* Handle interrupt consequences */ gem_update_int_status(s); } @@ -954,11 +1055,11 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) /* Is this destination MAC address "for us" ? */ maf = gem_mac_address_filter(s, buf); if (maf == GEM_RX_REJECT) { - return size; /* no, drop siliently b/c it's not an error */ + return size; /* no, drop silently b/c it's not an error */ } /* Discard packets with receive length error enabled ? */ - if (s->regs[GEM_NWCFG] & GEM_NWCFG_LERR_DISC) { + if (FIELD_EX32(s->regs[R_NWCFG], NWCFG, LEN_ERR_DISCARD)) { unsigned type_len; /* Fish the ethertype / length field out of the RX packet */ @@ -975,14 +1076,14 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) /* * Determine configured receive buffer offset (probably 0) */ - rxbuf_offset = (s->regs[GEM_NWCFG] & GEM_NWCFG_BUFF_OFST_M) >> - GEM_NWCFG_BUFF_OFST_S; + rxbuf_offset = FIELD_EX32(s->regs[R_NWCFG], NWCFG, RECV_BUF_OFFSET); /* The configure size of each receive buffer. Determines how many * buffers needed to hold this packet. */ - rxbufsize = ((s->regs[GEM_DMACFG] & GEM_DMACFG_RBUFSZ_M) >> - GEM_DMACFG_RBUFSZ_S) * GEM_DMACFG_RBUFSZ_MUL; + rxbufsize = FIELD_EX32(s->regs[R_DMACFG], DMACFG, RX_BUF_SIZE); + rxbufsize *= GEM_DMACFG_RBUFSZ_MUL; + bytes_to_copy = size; /* Hardware allows a zero value here but warns against it. To avoid QEMU @@ -1001,10 +1102,10 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) } /* Strip of FCS field ? (usually yes) */ - if (s->regs[GEM_NWCFG] & GEM_NWCFG_STRIP_FCS) { + if (FIELD_EX32(s->regs[R_NWCFG], NWCFG, FCS_REMOVE)) { rxbuf_ptr = (void *)buf; } else { - unsigned crc_val; + uint32_t crc_val; if (size > MAX_FRAME_SIZE - sizeof(crc_val)) { size = MAX_FRAME_SIZE - sizeof(crc_val); @@ -1031,7 +1132,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) if (size > gem_get_max_buf_len(s, false)) { qemu_log_mask(LOG_GUEST_ERROR, "rx frame too long\n"); - gem_set_isr(s, q, GEM_INT_AMBA_ERR); + gem_set_isr(s, q, R_ISR_AMBA_ERROR_MASK); return -1; } @@ -1107,8 +1208,8 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) /* Count it */ gem_receive_updatestats(s, buf, size); - s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_FRMRCVD; - gem_set_isr(s, q, GEM_INT_RXCMPL); + s->regs[R_RXSTATUS] |= R_RXSTATUS_FRAME_RECEIVED_MASK; + gem_set_isr(s, q, R_ISR_RECV_COMPLETE_MASK); /* Handle interrupt consequences */ gem_update_int_status(s); @@ -1126,39 +1227,39 @@ static void gem_transmit_updatestats(CadenceGEMState *s, const uint8_t *packet, uint64_t octets; /* Total octets (bytes) transmitted */ - octets = ((uint64_t)(s->regs[GEM_OCTTXLO]) << 32) | - s->regs[GEM_OCTTXHI]; + octets = ((uint64_t)(s->regs[R_OCTTXLO]) << 32) | + s->regs[R_OCTTXHI]; octets += bytes; - s->regs[GEM_OCTTXLO] = octets >> 32; - s->regs[GEM_OCTTXHI] = octets; + s->regs[R_OCTTXLO] = octets >> 32; + s->regs[R_OCTTXHI] = octets; /* Error-free Frames transmitted */ - s->regs[GEM_TXCNT]++; + s->regs[R_TXCNT]++; /* Error-free Broadcast Frames counter */ if (!memcmp(packet, broadcast_addr, 6)) { - s->regs[GEM_TXBCNT]++; + s->regs[R_TXBCNT]++; } /* Error-free Multicast Frames counter */ if (packet[0] == 0x01) { - s->regs[GEM_TXMCNT]++; + s->regs[R_TXMCNT]++; } if (bytes <= 64) { - s->regs[GEM_TX64CNT]++; + s->regs[R_TX64CNT]++; } else if (bytes <= 127) { - s->regs[GEM_TX65CNT]++; + s->regs[R_TX65CNT]++; } else if (bytes <= 255) { - s->regs[GEM_TX128CNT]++; + s->regs[R_TX128CNT]++; } else if (bytes <= 511) { - s->regs[GEM_TX256CNT]++; + s->regs[R_TX256CNT]++; } else if (bytes <= 1023) { - s->regs[GEM_TX512CNT]++; + s->regs[R_TX512CNT]++; } else if (bytes <= 1518) { - s->regs[GEM_TX1024CNT]++; + s->regs[R_TX1024CNT]++; } else { - s->regs[GEM_TX1519CNT]++; + s->regs[R_TX1519CNT]++; } } @@ -1175,7 +1276,7 @@ static void gem_transmit(CadenceGEMState *s) int q = 0; /* Do nothing if transmit is not enabled. */ - if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) { + if (!FIELD_EX32(s->regs[R_NWCTRL], NWCTRL, ENABLE_TRANSMIT)) { return; } @@ -1200,7 +1301,7 @@ static void gem_transmit(CadenceGEMState *s) while (tx_desc_get_used(desc) == 0) { /* Do nothing if transmit is not enabled. */ - if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) { + if (!FIELD_EX32(s->regs[R_NWCTRL], NWCTRL, ENABLE_TRANSMIT)) { return; } print_gem_tx_desc(desc, q); @@ -1221,7 +1322,7 @@ static void gem_transmit(CadenceGEMState *s) HWADDR_PRIx " too large: size 0x%x space 0x%zx\n", packet_desc_addr, tx_desc_get_length(desc), gem_get_max_buf_len(s, true) - (p - s->tx_packet)); - gem_set_isr(s, q, GEM_INT_AMBA_ERR); + gem_set_isr(s, q, R_ISR_AMBA_ERROR_MASK); break; } @@ -1258,14 +1359,14 @@ static void gem_transmit(CadenceGEMState *s) } DB_PRINT("TX descriptor next: 0x%08x\n", s->tx_desc_addr[q]); - s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_TXCMPL; - gem_set_isr(s, q, GEM_INT_TXCMPL); + s->regs[R_TXSTATUS] |= R_TXSTATUS_TRANSMIT_COMPLETE_MASK; + gem_set_isr(s, q, R_ISR_XMIT_COMPLETE_MASK); /* Handle interrupt consequences */ gem_update_int_status(s); /* Is checksum offload enabled? */ - if (s->regs[GEM_DMACFG] & GEM_DMACFG_TXCSUM_OFFL) { + if (FIELD_EX32(s->regs[R_DMACFG], DMACFG, TX_PBUF_CSUM_OFFLOAD)) { net_checksum_calculate(s->tx_packet, total_bytes, CSUM_ALL); } @@ -1273,8 +1374,8 @@ static void gem_transmit(CadenceGEMState *s) gem_transmit_updatestats(s, s->tx_packet, total_bytes); /* Send the packet somewhere */ - if (s->phy_loop || (s->regs[GEM_NWCTRL] & - GEM_NWCTRL_LOCALLOOP)) { + if (s->phy_loop || FIELD_EX32(s->regs[R_NWCTRL], NWCTRL, + LOOPBACK_LOCAL)) { qemu_receive_packet(qemu_get_queue(s->nic), s->tx_packet, total_bytes); } else { @@ -1289,9 +1390,8 @@ static void gem_transmit(CadenceGEMState *s) /* read next descriptor */ if (tx_desc_get_wrap(desc)) { - - if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) { - packet_desc_addr = s->regs[GEM_TBQPH]; + if (FIELD_EX32(s->regs[R_DMACFG], DMACFG, DMA_ADDR_BUS_WIDTH)) { + packet_desc_addr = s->regs[R_TBQPH]; packet_desc_addr <<= 32; } else { packet_desc_addr = 0; @@ -1307,10 +1407,10 @@ static void gem_transmit(CadenceGEMState *s) } if (tx_desc_get_used(desc)) { - s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_USED; + s->regs[R_TXSTATUS] |= R_TXSTATUS_USED_BIT_READ_MASK; /* IRQ TXUSED is defined only for queue 0 */ if (q == 0) { - gem_set_isr(s, 0, GEM_INT_TXUSED); + gem_set_isr(s, 0, R_ISR_TX_USED_MASK); } gem_update_int_status(s); } @@ -1353,30 +1453,30 @@ static void gem_reset(DeviceState *d) /* Set post reset register values */ memset(&s->regs[0], 0, sizeof(s->regs)); - s->regs[GEM_NWCFG] = 0x00080000; - s->regs[GEM_NWSTATUS] = 0x00000006; - s->regs[GEM_DMACFG] = 0x00020784; - s->regs[GEM_IMR] = 0x07ffffff; - s->regs[GEM_TXPAUSE] = 0x0000ffff; - s->regs[GEM_TXPARTIALSF] = 0x000003ff; - s->regs[GEM_RXPARTIALSF] = 0x000003ff; - s->regs[GEM_MODID] = s->revision; - s->regs[GEM_DESCONF] = 0x02D00111; - s->regs[GEM_DESCONF2] = 0x2ab10000 | s->jumbo_max_len; - s->regs[GEM_DESCONF5] = 0x002f2045; - s->regs[GEM_DESCONF6] = GEM_DESCONF6_64B_MASK; - s->regs[GEM_INT_Q1_MASK] = 0x00000CE6; - s->regs[GEM_JUMBO_MAX_LEN] = s->jumbo_max_len; + s->regs[R_NWCFG] = 0x00080000; + s->regs[R_NWSTATUS] = 0x00000006; + s->regs[R_DMACFG] = 0x00020784; + s->regs[R_IMR] = 0x07ffffff; + s->regs[R_TXPAUSE] = 0x0000ffff; + s->regs[R_TXPARTIALSF] = 0x000003ff; + s->regs[R_RXPARTIALSF] = 0x000003ff; + s->regs[R_MODID] = s->revision; + s->regs[R_DESCONF] = 0x02D00111; + s->regs[R_DESCONF2] = 0x2ab10000 | s->jumbo_max_len; + s->regs[R_DESCONF5] = 0x002f2045; + s->regs[R_DESCONF6] = R_DESCONF6_DMA_ADDR_64B_MASK; + s->regs[R_INT_Q1_MASK] = 0x00000CE6; + s->regs[R_JUMBO_MAX_LEN] = s->jumbo_max_len; if (s->num_priority_queues > 1) { queues_mask = MAKE_64BIT_MASK(1, s->num_priority_queues - 1); - s->regs[GEM_DESCONF6] |= queues_mask; + s->regs[R_DESCONF6] |= queues_mask; } /* Set MAC address */ a = &s->conf.macaddr.a[0]; - s->regs[GEM_SPADDR1LO] = a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24); - s->regs[GEM_SPADDR1HI] = a[4] | (a[5] << 8); + s->regs[R_SPADDR1LO] = a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24); + s->regs[R_SPADDR1HI] = a[4] | (a[5] << 8); for (i = 0; i < 4; i++) { s->sar_active[i] = false; @@ -1421,6 +1521,38 @@ static void gem_phy_write(CadenceGEMState *s, unsigned reg_num, uint16_t val) s->phy_regs[reg_num] = val; } +static void gem_handle_phy_access(CadenceGEMState *s) +{ + uint32_t val = s->regs[R_PHYMNTNC]; + uint32_t phy_addr, reg_num; + + phy_addr = FIELD_EX32(val, PHYMNTNC, PHY_ADDR); + + if (phy_addr != s->phy_addr) { + /* no phy at this address */ + if (FIELD_EX32(val, PHYMNTNC, OP) == MDIO_OP_READ) { + s->regs[R_PHYMNTNC] = FIELD_DP32(val, PHYMNTNC, DATA, 0xffff); + } + return; + } + + reg_num = FIELD_EX32(val, PHYMNTNC, REG_ADDR); + + switch (FIELD_EX32(val, PHYMNTNC, OP)) { + case MDIO_OP_READ: + s->regs[R_PHYMNTNC] = FIELD_DP32(val, PHYMNTNC, DATA, + gem_phy_read(s, reg_num)); + break; + + case MDIO_OP_WRITE: + gem_phy_write(s, reg_num, val); + break; + + default: + break; /* only clause 22 operations are supported */ + } +} + /* * gem_read32: * Read a GEM register. @@ -1429,7 +1561,7 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) { CadenceGEMState *s; uint32_t retval; - s = (CadenceGEMState *)opaque; + s = opaque; offset >>= 2; retval = s->regs[offset]; @@ -1437,24 +1569,10 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset*4, retval); switch (offset) { - case GEM_ISR: + case R_ISR: DB_PRINT("lowering irqs on ISR read\n"); /* The interrupts get updated at the end of the function. */ break; - case GEM_PHYMNTNC: - if (retval & GEM_PHYMNTNC_OP_R) { - uint32_t phy_addr, reg_num; - - phy_addr = (retval & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT; - if (phy_addr == s->phy_addr) { - reg_num = (retval & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT; - retval &= 0xFFFF0000; - retval |= gem_phy_read(s, reg_num); - } else { - retval |= 0xFFFF; /* No device at this address */ - } - } - break; } /* Squash read to clear bits */ @@ -1495,16 +1613,16 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, /* Handle register write side effects */ switch (offset) { - case GEM_NWCTRL: - if (val & GEM_NWCTRL_RXENA) { + case R_NWCTRL: + if (FIELD_EX32(val, NWCTRL, ENABLE_RECEIVE)) { for (i = 0; i < s->num_priority_queues; ++i) { gem_get_rx_desc(s, i); } } - if (val & GEM_NWCTRL_TXSTART) { + if (FIELD_EX32(val, NWCTRL, TRANSMIT_START)) { gem_transmit(s); } - if (!(val & GEM_NWCTRL_TXENA)) { + if (!(FIELD_EX32(val, NWCTRL, ENABLE_TRANSMIT))) { /* Reset to start of Q when transmit disabled. */ for (i = 0; i < s->num_priority_queues; i++) { s->tx_desc_addr[i] = gem_get_tx_queue_base_addr(s, i); @@ -1515,65 +1633,57 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, } break; - case GEM_TXSTATUS: + case R_TXSTATUS: gem_update_int_status(s); break; - case GEM_RXQBASE: + case R_RXQBASE: s->rx_desc_addr[0] = val; break; - case GEM_RECEIVE_Q1_PTR ... GEM_RECEIVE_Q7_PTR: - s->rx_desc_addr[offset - GEM_RECEIVE_Q1_PTR + 1] = val; + case R_RECEIVE_Q1_PTR ... R_RECEIVE_Q7_PTR: + s->rx_desc_addr[offset - R_RECEIVE_Q1_PTR + 1] = val; break; - case GEM_TXQBASE: + case R_TXQBASE: s->tx_desc_addr[0] = val; break; - case GEM_TRANSMIT_Q1_PTR ... GEM_TRANSMIT_Q7_PTR: - s->tx_desc_addr[offset - GEM_TRANSMIT_Q1_PTR + 1] = val; + case R_TRANSMIT_Q1_PTR ... R_TRANSMIT_Q7_PTR: + s->tx_desc_addr[offset - R_TRANSMIT_Q1_PTR + 1] = val; break; - case GEM_RXSTATUS: + case R_RXSTATUS: gem_update_int_status(s); break; - case GEM_IER: - s->regs[GEM_IMR] &= ~val; + case R_IER: + s->regs[R_IMR] &= ~val; gem_update_int_status(s); break; - case GEM_JUMBO_MAX_LEN: - s->regs[GEM_JUMBO_MAX_LEN] = val & MAX_JUMBO_FRAME_SIZE_MASK; + case R_JUMBO_MAX_LEN: + s->regs[R_JUMBO_MAX_LEN] = val & MAX_JUMBO_FRAME_SIZE_MASK; break; - case GEM_INT_Q1_ENABLE ... GEM_INT_Q7_ENABLE: - s->regs[GEM_INT_Q1_MASK + offset - GEM_INT_Q1_ENABLE] &= ~val; + case R_INT_Q1_ENABLE ... R_INT_Q7_ENABLE: + s->regs[R_INT_Q1_MASK + offset - R_INT_Q1_ENABLE] &= ~val; gem_update_int_status(s); break; - case GEM_IDR: - s->regs[GEM_IMR] |= val; + case R_IDR: + s->regs[R_IMR] |= val; gem_update_int_status(s); break; - case GEM_INT_Q1_DISABLE ... GEM_INT_Q7_DISABLE: - s->regs[GEM_INT_Q1_MASK + offset - GEM_INT_Q1_DISABLE] |= val; + case R_INT_Q1_DISABLE ... R_INT_Q7_DISABLE: + s->regs[R_INT_Q1_MASK + offset - R_INT_Q1_DISABLE] |= val; gem_update_int_status(s); break; - case GEM_SPADDR1LO: - case GEM_SPADDR2LO: - case GEM_SPADDR3LO: - case GEM_SPADDR4LO: - s->sar_active[(offset - GEM_SPADDR1LO) / 2] = false; + case R_SPADDR1LO: + case R_SPADDR2LO: + case R_SPADDR3LO: + case R_SPADDR4LO: + s->sar_active[(offset - R_SPADDR1LO) / 2] = false; break; - case GEM_SPADDR1HI: - case GEM_SPADDR2HI: - case GEM_SPADDR3HI: - case GEM_SPADDR4HI: - s->sar_active[(offset - GEM_SPADDR1HI) / 2] = true; + case R_SPADDR1HI: + case R_SPADDR2HI: + case R_SPADDR3HI: + case R_SPADDR4HI: + s->sar_active[(offset - R_SPADDR1HI) / 2] = true; break; - case GEM_PHYMNTNC: - if (val & GEM_PHYMNTNC_OP_W) { - uint32_t phy_addr, reg_num; - - phy_addr = (val & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT; - if (phy_addr == s->phy_addr) { - reg_num = (val & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT; - gem_phy_write(s, reg_num, val); - } - } + case R_PHYMNTNC: + gem_handle_phy_access(s); break; } @@ -1633,7 +1743,8 @@ static void gem_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_gem_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); if (s->jumbo_max_len > MAX_FRAME_SIZE) { error_setg(errp, "jumbo-max-len is greater than %d", @@ -1654,18 +1765,13 @@ static void gem_init(Object *obj) "enet", sizeof(s->regs)); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); - - object_property_add_link(obj, "dma", TYPE_MEMORY_REGION, - (Object **)&s->dma_mr, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_STRONG); } static const VMStateDescription vmstate_cadence_gem = { .name = "cadence_gem", .version_id = 4, .minimum_version_id = 4, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, CadenceGEMState, CADENCE_GEM_MAXREG), VMSTATE_UINT16_ARRAY(phy_regs, CadenceGEMState, 32), VMSTATE_UINT8(phy_loop, CadenceGEMState), @@ -1691,6 +1797,8 @@ static Property gem_properties[] = { num_type2_screeners, 4), DEFINE_PROP_UINT16("jumbo-max-len", CadenceGEMState, jumbo_max_len, 10240), + DEFINE_PROP_LINK("dma", CadenceGEMState, dma_mr, + TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/can/can_kvaser_pci.c b/hw/net/can/can_kvaser_pci.c index 94b3a534f8..bf41e6b261 100644 --- a/hw/net/can/can_kvaser_pci.c +++ b/hw/net/can/can_kvaser_pci.c @@ -37,7 +37,7 @@ #include "qapi/error.h" #include "chardev/char.h" #include "hw/irq.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "net/can_emu.h" @@ -266,7 +266,7 @@ static const VMStateDescription vmstate_kvaser_pci = { .name = "kvaser_pci", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, KvaserPCIState), /* Load this before sja_state. */ VMSTATE_UINT32(s5920_intcsr, KvaserPCIState), diff --git a/hw/net/can/can_mioe3680_pci.c b/hw/net/can/can_mioe3680_pci.c index 29dc696f7c..308b17e0c0 100644 --- a/hw/net/can/can_mioe3680_pci.c +++ b/hw/net/can/can_mioe3680_pci.c @@ -33,7 +33,7 @@ #include "qapi/error.h" #include "chardev/char.h" #include "hw/irq.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "net/can_emu.h" @@ -203,7 +203,7 @@ static const VMStateDescription vmstate_mioe3680_pci = { .name = "mioe3680_pci", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, Mioe3680PCIState), VMSTATE_STRUCT(sja_state[0], Mioe3680PCIState, 0, vmstate_can_sja, CanSJA1000State), diff --git a/hw/net/can/can_pcm3680_pci.c b/hw/net/can/can_pcm3680_pci.c index e8e57f4f33..e4c8d93b98 100644 --- a/hw/net/can/can_pcm3680_pci.c +++ b/hw/net/can/can_pcm3680_pci.c @@ -33,7 +33,7 @@ #include "qapi/error.h" #include "chardev/char.h" #include "hw/irq.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "net/can_emu.h" @@ -204,7 +204,7 @@ static const VMStateDescription vmstate_pcm3680i_pci = { .name = "pcm3680i_pci", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, Pcm3680iPCIState), VMSTATE_STRUCT(sja_state[0], Pcm3680iPCIState, 0, vmstate_can_sja, CanSJA1000State), diff --git a/hw/net/can/can_sja1000.c b/hw/net/can/can_sja1000.c index e0f76d3eb3..6694d7bfd8 100644 --- a/hw/net/can/can_sja1000.c +++ b/hw/net/can/can_sja1000.c @@ -108,7 +108,7 @@ void can_sja_single_filter(struct qemu_can_filter *filter, } filter->can_mask = (uint32_t)amr[0] << 3; - filter->can_mask |= (uint32_t)amr[1] << 5; + filter->can_mask |= (uint32_t)amr[1] >> 5; filter->can_mask = ~filter->can_mask & QEMU_CAN_SFF_MASK; if (!(amr[1] & 0x10)) { filter->can_mask |= QEMU_CAN_RTR_FLAG; @@ -431,7 +431,7 @@ void can_sja_mem_write(CanSJA1000State *s, hwaddr addr, uint64_t val, (unsigned long long)val, (unsigned int)addr); if (addr > CAN_SJA_MEM_SIZE) { - return ; + return; } if (s->clock & 0x80) { /* PeliCAN Mode */ @@ -929,7 +929,7 @@ const VMStateDescription vmstate_qemu_can_filter = { .name = "qemu_can_filter", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(can_id, qemu_can_filter), VMSTATE_UINT32(can_mask, qemu_can_filter), VMSTATE_END_OF_LIST() @@ -953,7 +953,7 @@ const VMStateDescription vmstate_can_sja = { .version_id = 1, .minimum_version_id = 1, .post_load = can_sja_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(mode, CanSJA1000State), VMSTATE_UINT8(status_pel, CanSJA1000State), diff --git a/hw/net/can/ctu_can_fd_frame.h b/hw/net/can/ctu_can_fd_frame.h index 459c4a0ada..8c43fd99dd 100644 --- a/hw/net/can/ctu_can_fd_frame.h +++ b/hw/net/can/ctu_can_fd_frame.h @@ -34,156 +34,156 @@ /* CAN_Frame_format memory map */ enum ctu_can_fd_can_frame_format { - CTU_CAN_FD_FRAME_FORM_W = 0x0, - CTU_CAN_FD_IDENTIFIER_W = 0x4, - CTU_CAN_FD_TIMESTAMP_L_W = 0x8, - CTU_CAN_FD_TIMESTAMP_U_W = 0xc, - CTU_CAN_FD_DATA_1_4_W = 0x10, - CTU_CAN_FD_DATA_5_8_W = 0x14, - CTU_CAN_FD_DATA_61_64_W = 0x4c, + CTU_CAN_FD_FRAME_FORM_W = 0x0, + CTU_CAN_FD_IDENTIFIER_W = 0x4, + CTU_CAN_FD_TIMESTAMP_L_W = 0x8, + CTU_CAN_FD_TIMESTAMP_U_W = 0xc, + CTU_CAN_FD_DATA_1_4_W = 0x10, + CTU_CAN_FD_DATA_5_8_W = 0x14, + CTU_CAN_FD_DATA_61_64_W = 0x4c, }; /* Register descriptions: */ union ctu_can_fd_frame_form_w { - uint32_t u32; - struct ctu_can_fd_frame_form_w_s { + uint32_t u32; + struct ctu_can_fd_frame_form_w_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* FRAME_FORM_W */ - uint32_t dlc : 4; - uint32_t reserved_4 : 1; - uint32_t rtr : 1; - uint32_t ide : 1; - uint32_t fdf : 1; - uint32_t reserved_8 : 1; - uint32_t brs : 1; - uint32_t esi_rsv : 1; - uint32_t rwcnt : 5; - uint32_t reserved_31_16 : 16; + uint32_t dlc : 4; + uint32_t reserved_4 : 1; + uint32_t rtr : 1; + uint32_t ide : 1; + uint32_t fdf : 1; + uint32_t reserved_8 : 1; + uint32_t brs : 1; + uint32_t esi_rsv : 1; + uint32_t rwcnt : 5; + uint32_t reserved_31_16 : 16; #else - uint32_t reserved_31_16 : 16; - uint32_t rwcnt : 5; - uint32_t esi_rsv : 1; - uint32_t brs : 1; - uint32_t reserved_8 : 1; - uint32_t fdf : 1; - uint32_t ide : 1; - uint32_t rtr : 1; - uint32_t reserved_4 : 1; - uint32_t dlc : 4; + uint32_t reserved_31_16 : 16; + uint32_t rwcnt : 5; + uint32_t esi_rsv : 1; + uint32_t brs : 1; + uint32_t reserved_8 : 1; + uint32_t fdf : 1; + uint32_t ide : 1; + uint32_t rtr : 1; + uint32_t reserved_4 : 1; + uint32_t dlc : 4; #endif - } s; + } s; }; enum ctu_can_fd_frame_form_w_rtr { - NO_RTR_FRAME = 0x0, - RTR_FRAME = 0x1, + NO_RTR_FRAME = 0x0, + RTR_FRAME = 0x1, }; enum ctu_can_fd_frame_form_w_ide { - BASE = 0x0, - EXTENDED = 0x1, + BASE = 0x0, + EXTENDED = 0x1, }; enum ctu_can_fd_frame_form_w_fdf { - NORMAL_CAN = 0x0, - FD_CAN = 0x1, + NORMAL_CAN = 0x0, + FD_CAN = 0x1, }; enum ctu_can_fd_frame_form_w_brs { - BR_NO_SHIFT = 0x0, - BR_SHIFT = 0x1, + BR_NO_SHIFT = 0x0, + BR_SHIFT = 0x1, }; enum ctu_can_fd_frame_form_w_esi_rsv { - ESI_ERR_ACTIVE = 0x0, - ESI_ERR_PASIVE = 0x1, + ESI_ERR_ACTIVE = 0x0, + ESI_ERR_PASIVE = 0x1, }; union ctu_can_fd_identifier_w { - uint32_t u32; - struct ctu_can_fd_identifier_w_s { + uint32_t u32; + struct ctu_can_fd_identifier_w_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* IDENTIFIER_W */ - uint32_t identifier_ext : 18; - uint32_t identifier_base : 11; - uint32_t reserved_31_29 : 3; + uint32_t identifier_ext : 18; + uint32_t identifier_base : 11; + uint32_t reserved_31_29 : 3; #else - uint32_t reserved_31_29 : 3; - uint32_t identifier_base : 11; - uint32_t identifier_ext : 18; + uint32_t reserved_31_29 : 3; + uint32_t identifier_base : 11; + uint32_t identifier_ext : 18; #endif - } s; + } s; }; union ctu_can_fd_timestamp_l_w { - uint32_t u32; - struct ctu_can_fd_timestamp_l_w_s { + uint32_t u32; + struct ctu_can_fd_timestamp_l_w_s { /* TIMESTAMP_L_W */ - uint32_t time_stamp_31_0 : 32; - } s; + uint32_t time_stamp_31_0 : 32; + } s; }; union ctu_can_fd_timestamp_u_w { - uint32_t u32; - struct ctu_can_fd_timestamp_u_w_s { + uint32_t u32; + struct ctu_can_fd_timestamp_u_w_s { /* TIMESTAMP_U_W */ - uint32_t timestamp_l_w : 32; - } s; + uint32_t timestamp_l_w : 32; + } s; }; union ctu_can_fd_data_1_4_w { - uint32_t u32; - struct ctu_can_fd_data_1_4_w_s { + uint32_t u32; + struct ctu_can_fd_data_1_4_w_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* DATA_1_4_W */ - uint32_t data_1 : 8; - uint32_t data_2 : 8; - uint32_t data_3 : 8; - uint32_t data_4 : 8; + uint32_t data_1 : 8; + uint32_t data_2 : 8; + uint32_t data_3 : 8; + uint32_t data_4 : 8; #else - uint32_t data_4 : 8; - uint32_t data_3 : 8; - uint32_t data_2 : 8; - uint32_t data_1 : 8; + uint32_t data_4 : 8; + uint32_t data_3 : 8; + uint32_t data_2 : 8; + uint32_t data_1 : 8; #endif - } s; + } s; }; union ctu_can_fd_data_5_8_w { - uint32_t u32; - struct ctu_can_fd_data_5_8_w_s { + uint32_t u32; + struct ctu_can_fd_data_5_8_w_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* DATA_5_8_W */ - uint32_t data_5 : 8; - uint32_t data_6 : 8; - uint32_t data_7 : 8; - uint32_t data_8 : 8; + uint32_t data_5 : 8; + uint32_t data_6 : 8; + uint32_t data_7 : 8; + uint32_t data_8 : 8; #else - uint32_t data_8 : 8; - uint32_t data_7 : 8; - uint32_t data_6 : 8; - uint32_t data_5 : 8; + uint32_t data_8 : 8; + uint32_t data_7 : 8; + uint32_t data_6 : 8; + uint32_t data_5 : 8; #endif - } s; + } s; }; union ctu_can_fd_data_61_64_w { - uint32_t u32; - struct ctu_can_fd_data_61_64_w_s { + uint32_t u32; + struct ctu_can_fd_data_61_64_w_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* DATA_61_64_W */ - uint32_t data_61 : 8; - uint32_t data_62 : 8; - uint32_t data_63 : 8; - uint32_t data_64 : 8; + uint32_t data_61 : 8; + uint32_t data_62 : 8; + uint32_t data_63 : 8; + uint32_t data_64 : 8; #else - uint32_t data_64 : 8; - uint32_t data_63 : 8; - uint32_t data_62 : 8; - uint32_t data_61 : 8; + uint32_t data_64 : 8; + uint32_t data_63 : 8; + uint32_t data_62 : 8; + uint32_t data_61 : 8; #endif - } s; + } s; }; #endif diff --git a/hw/net/can/ctu_can_fd_regs.h b/hw/net/can/ctu_can_fd_regs.h index 57859b87bc..d2d88cdd1a 100644 --- a/hw/net/can/ctu_can_fd_regs.h +++ b/hw/net/can/ctu_can_fd_regs.h @@ -34,938 +34,938 @@ /* CAN_Registers memory map */ enum ctu_can_fd_can_registers { - CTU_CAN_FD_DEVICE_ID = 0x0, - CTU_CAN_FD_VERSION = 0x2, - CTU_CAN_FD_MODE = 0x4, - CTU_CAN_FD_SETTINGS = 0x6, - CTU_CAN_FD_STATUS = 0x8, - CTU_CAN_FD_COMMAND = 0xc, - CTU_CAN_FD_INT_STAT = 0x10, - CTU_CAN_FD_INT_ENA_SET = 0x14, - CTU_CAN_FD_INT_ENA_CLR = 0x18, - CTU_CAN_FD_INT_MASK_SET = 0x1c, - CTU_CAN_FD_INT_MASK_CLR = 0x20, - CTU_CAN_FD_BTR = 0x24, - CTU_CAN_FD_BTR_FD = 0x28, - CTU_CAN_FD_EWL = 0x2c, - CTU_CAN_FD_ERP = 0x2d, - CTU_CAN_FD_FAULT_STATE = 0x2e, - CTU_CAN_FD_REC = 0x30, - CTU_CAN_FD_TEC = 0x32, - CTU_CAN_FD_ERR_NORM = 0x34, - CTU_CAN_FD_ERR_FD = 0x36, - CTU_CAN_FD_CTR_PRES = 0x38, - CTU_CAN_FD_FILTER_A_MASK = 0x3c, - CTU_CAN_FD_FILTER_A_VAL = 0x40, - CTU_CAN_FD_FILTER_B_MASK = 0x44, - CTU_CAN_FD_FILTER_B_VAL = 0x48, - CTU_CAN_FD_FILTER_C_MASK = 0x4c, - CTU_CAN_FD_FILTER_C_VAL = 0x50, - CTU_CAN_FD_FILTER_RAN_LOW = 0x54, - CTU_CAN_FD_FILTER_RAN_HIGH = 0x58, - CTU_CAN_FD_FILTER_CONTROL = 0x5c, - CTU_CAN_FD_FILTER_STATUS = 0x5e, - CTU_CAN_FD_RX_MEM_INFO = 0x60, - CTU_CAN_FD_RX_POINTERS = 0x64, - CTU_CAN_FD_RX_STATUS = 0x68, - CTU_CAN_FD_RX_SETTINGS = 0x6a, - CTU_CAN_FD_RX_DATA = 0x6c, - CTU_CAN_FD_TX_STATUS = 0x70, - CTU_CAN_FD_TX_COMMAND = 0x74, - CTU_CAN_FD_TX_PRIORITY = 0x78, - CTU_CAN_FD_ERR_CAPT = 0x7c, - CTU_CAN_FD_ALC = 0x7e, - CTU_CAN_FD_TRV_DELAY = 0x80, - CTU_CAN_FD_SSP_CFG = 0x82, - CTU_CAN_FD_RX_FR_CTR = 0x84, - CTU_CAN_FD_TX_FR_CTR = 0x88, - CTU_CAN_FD_DEBUG_REGISTER = 0x8c, - CTU_CAN_FD_YOLO_REG = 0x90, - CTU_CAN_FD_TIMESTAMP_LOW = 0x94, - CTU_CAN_FD_TIMESTAMP_HIGH = 0x98, - CTU_CAN_FD_TXTB1_DATA_1 = 0x100, - CTU_CAN_FD_TXTB1_DATA_2 = 0x104, - CTU_CAN_FD_TXTB1_DATA_20 = 0x14c, - CTU_CAN_FD_TXTB2_DATA_1 = 0x200, - CTU_CAN_FD_TXTB2_DATA_2 = 0x204, - CTU_CAN_FD_TXTB2_DATA_20 = 0x24c, - CTU_CAN_FD_TXTB3_DATA_1 = 0x300, - CTU_CAN_FD_TXTB3_DATA_2 = 0x304, - CTU_CAN_FD_TXTB3_DATA_20 = 0x34c, - CTU_CAN_FD_TXTB4_DATA_1 = 0x400, - CTU_CAN_FD_TXTB4_DATA_2 = 0x404, - CTU_CAN_FD_TXTB4_DATA_20 = 0x44c, + CTU_CAN_FD_DEVICE_ID = 0x0, + CTU_CAN_FD_VERSION = 0x2, + CTU_CAN_FD_MODE = 0x4, + CTU_CAN_FD_SETTINGS = 0x6, + CTU_CAN_FD_STATUS = 0x8, + CTU_CAN_FD_COMMAND = 0xc, + CTU_CAN_FD_INT_STAT = 0x10, + CTU_CAN_FD_INT_ENA_SET = 0x14, + CTU_CAN_FD_INT_ENA_CLR = 0x18, + CTU_CAN_FD_INT_MASK_SET = 0x1c, + CTU_CAN_FD_INT_MASK_CLR = 0x20, + CTU_CAN_FD_BTR = 0x24, + CTU_CAN_FD_BTR_FD = 0x28, + CTU_CAN_FD_EWL = 0x2c, + CTU_CAN_FD_ERP = 0x2d, + CTU_CAN_FD_FAULT_STATE = 0x2e, + CTU_CAN_FD_REC = 0x30, + CTU_CAN_FD_TEC = 0x32, + CTU_CAN_FD_ERR_NORM = 0x34, + CTU_CAN_FD_ERR_FD = 0x36, + CTU_CAN_FD_CTR_PRES = 0x38, + CTU_CAN_FD_FILTER_A_MASK = 0x3c, + CTU_CAN_FD_FILTER_A_VAL = 0x40, + CTU_CAN_FD_FILTER_B_MASK = 0x44, + CTU_CAN_FD_FILTER_B_VAL = 0x48, + CTU_CAN_FD_FILTER_C_MASK = 0x4c, + CTU_CAN_FD_FILTER_C_VAL = 0x50, + CTU_CAN_FD_FILTER_RAN_LOW = 0x54, + CTU_CAN_FD_FILTER_RAN_HIGH = 0x58, + CTU_CAN_FD_FILTER_CONTROL = 0x5c, + CTU_CAN_FD_FILTER_STATUS = 0x5e, + CTU_CAN_FD_RX_MEM_INFO = 0x60, + CTU_CAN_FD_RX_POINTERS = 0x64, + CTU_CAN_FD_RX_STATUS = 0x68, + CTU_CAN_FD_RX_SETTINGS = 0x6a, + CTU_CAN_FD_RX_DATA = 0x6c, + CTU_CAN_FD_TX_STATUS = 0x70, + CTU_CAN_FD_TX_COMMAND = 0x74, + CTU_CAN_FD_TX_PRIORITY = 0x78, + CTU_CAN_FD_ERR_CAPT = 0x7c, + CTU_CAN_FD_ALC = 0x7e, + CTU_CAN_FD_TRV_DELAY = 0x80, + CTU_CAN_FD_SSP_CFG = 0x82, + CTU_CAN_FD_RX_FR_CTR = 0x84, + CTU_CAN_FD_TX_FR_CTR = 0x88, + CTU_CAN_FD_DEBUG_REGISTER = 0x8c, + CTU_CAN_FD_YOLO_REG = 0x90, + CTU_CAN_FD_TIMESTAMP_LOW = 0x94, + CTU_CAN_FD_TIMESTAMP_HIGH = 0x98, + CTU_CAN_FD_TXTB1_DATA_1 = 0x100, + CTU_CAN_FD_TXTB1_DATA_2 = 0x104, + CTU_CAN_FD_TXTB1_DATA_20 = 0x14c, + CTU_CAN_FD_TXTB2_DATA_1 = 0x200, + CTU_CAN_FD_TXTB2_DATA_2 = 0x204, + CTU_CAN_FD_TXTB2_DATA_20 = 0x24c, + CTU_CAN_FD_TXTB3_DATA_1 = 0x300, + CTU_CAN_FD_TXTB3_DATA_2 = 0x304, + CTU_CAN_FD_TXTB3_DATA_20 = 0x34c, + CTU_CAN_FD_TXTB4_DATA_1 = 0x400, + CTU_CAN_FD_TXTB4_DATA_2 = 0x404, + CTU_CAN_FD_TXTB4_DATA_20 = 0x44c, }; /* Register descriptions: */ union ctu_can_fd_device_id_version { - uint32_t u32; - struct ctu_can_fd_device_id_version_s { + uint32_t u32; + struct ctu_can_fd_device_id_version_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* DEVICE_ID */ - uint32_t device_id : 16; + uint32_t device_id : 16; /* VERSION */ - uint32_t ver_minor : 8; - uint32_t ver_major : 8; + uint32_t ver_minor : 8; + uint32_t ver_major : 8; #else - uint32_t ver_major : 8; - uint32_t ver_minor : 8; - uint32_t device_id : 16; + uint32_t ver_major : 8; + uint32_t ver_minor : 8; + uint32_t device_id : 16; #endif - } s; + } s; }; enum ctu_can_fd_device_id_device_id { - CTU_CAN_FD_ID = 0xcafd, + CTU_CAN_FD_ID = 0xcafd, }; union ctu_can_fd_mode_settings { - uint32_t u32; - struct ctu_can_fd_mode_settings_s { + uint32_t u32; + struct ctu_can_fd_mode_settings_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* MODE */ - uint32_t rst : 1; - uint32_t lom : 1; - uint32_t stm : 1; - uint32_t afm : 1; - uint32_t fde : 1; - uint32_t reserved_6_5 : 2; - uint32_t acf : 1; - uint32_t tstm : 1; - uint32_t reserved_15_9 : 7; + uint32_t rst : 1; + uint32_t lom : 1; + uint32_t stm : 1; + uint32_t afm : 1; + uint32_t fde : 1; + uint32_t reserved_6_5 : 2; + uint32_t acf : 1; + uint32_t tstm : 1; + uint32_t reserved_15_9 : 7; /* SETTINGS */ - uint32_t rtrle : 1; - uint32_t rtrth : 4; - uint32_t ilbp : 1; - uint32_t ena : 1; - uint32_t nisofd : 1; - uint32_t pex : 1; - uint32_t reserved_31_25 : 7; + uint32_t rtrle : 1; + uint32_t rtrth : 4; + uint32_t ilbp : 1; + uint32_t ena : 1; + uint32_t nisofd : 1; + uint32_t pex : 1; + uint32_t reserved_31_25 : 7; #else - uint32_t reserved_31_25 : 7; - uint32_t pex : 1; - uint32_t nisofd : 1; - uint32_t ena : 1; - uint32_t ilbp : 1; - uint32_t rtrth : 4; - uint32_t rtrle : 1; - uint32_t reserved_15_9 : 7; - uint32_t tstm : 1; - uint32_t acf : 1; - uint32_t reserved_6_5 : 2; - uint32_t fde : 1; - uint32_t afm : 1; - uint32_t stm : 1; - uint32_t lom : 1; - uint32_t rst : 1; + uint32_t reserved_31_25 : 7; + uint32_t pex : 1; + uint32_t nisofd : 1; + uint32_t ena : 1; + uint32_t ilbp : 1; + uint32_t rtrth : 4; + uint32_t rtrle : 1; + uint32_t reserved_15_9 : 7; + uint32_t tstm : 1; + uint32_t acf : 1; + uint32_t reserved_6_5 : 2; + uint32_t fde : 1; + uint32_t afm : 1; + uint32_t stm : 1; + uint32_t lom : 1; + uint32_t rst : 1; #endif - } s; + } s; }; enum ctu_can_fd_mode_lom { - LOM_DISABLED = 0x0, - LOM_ENABLED = 0x1, + LOM_DISABLED = 0x0, + LOM_ENABLED = 0x1, }; enum ctu_can_fd_mode_stm { - STM_DISABLED = 0x0, - STM_ENABLED = 0x1, + STM_DISABLED = 0x0, + STM_ENABLED = 0x1, }; enum ctu_can_fd_mode_afm { - AFM_DISABLED = 0x0, - AFM_ENABLED = 0x1, + AFM_DISABLED = 0x0, + AFM_ENABLED = 0x1, }; enum ctu_can_fd_mode_fde { - FDE_DISABLE = 0x0, - FDE_ENABLE = 0x1, + FDE_DISABLE = 0x0, + FDE_ENABLE = 0x1, }; enum ctu_can_fd_mode_acf { - ACF_DISABLED = 0x0, - ACF_ENABLED = 0x1, + ACF_DISABLED = 0x0, + ACF_ENABLED = 0x1, }; enum ctu_can_fd_settings_rtrle { - RTRLE_DISABLED = 0x0, - RTRLE_ENABLED = 0x1, + RTRLE_DISABLED = 0x0, + RTRLE_ENABLED = 0x1, }; enum ctu_can_fd_settings_ilbp { - INT_LOOP_DISABLED = 0x0, - INT_LOOP_ENABLED = 0x1, + INT_LOOP_DISABLED = 0x0, + INT_LOOP_ENABLED = 0x1, }; enum ctu_can_fd_settings_ena { - CTU_CAN_DISABLED = 0x0, - CTU_CAN_ENABLED = 0x1, + CTU_CAN_DISABLED = 0x0, + CTU_CAN_ENABLED = 0x1, }; enum ctu_can_fd_settings_nisofd { - ISO_FD = 0x0, - NON_ISO_FD = 0x1, + ISO_FD = 0x0, + NON_ISO_FD = 0x1, }; enum ctu_can_fd_settings_pex { - PROTOCOL_EXCEPTION_DISABLED = 0x0, - PROTOCOL_EXCEPTION_ENABLED = 0x1, + PROTOCOL_EXCEPTION_DISABLED = 0x0, + PROTOCOL_EXCEPTION_ENABLED = 0x1, }; union ctu_can_fd_status { - uint32_t u32; - struct ctu_can_fd_status_s { + uint32_t u32; + struct ctu_can_fd_status_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* STATUS */ - uint32_t rxne : 1; - uint32_t dor : 1; - uint32_t txnf : 1; - uint32_t eft : 1; - uint32_t rxs : 1; - uint32_t txs : 1; - uint32_t ewl : 1; - uint32_t idle : 1; - uint32_t reserved_31_8 : 24; + uint32_t rxne : 1; + uint32_t dor : 1; + uint32_t txnf : 1; + uint32_t eft : 1; + uint32_t rxs : 1; + uint32_t txs : 1; + uint32_t ewl : 1; + uint32_t idle : 1; + uint32_t reserved_31_8 : 24; #else - uint32_t reserved_31_8 : 24; - uint32_t idle : 1; - uint32_t ewl : 1; - uint32_t txs : 1; - uint32_t rxs : 1; - uint32_t eft : 1; - uint32_t txnf : 1; - uint32_t dor : 1; - uint32_t rxne : 1; + uint32_t reserved_31_8 : 24; + uint32_t idle : 1; + uint32_t ewl : 1; + uint32_t txs : 1; + uint32_t rxs : 1; + uint32_t eft : 1; + uint32_t txnf : 1; + uint32_t dor : 1; + uint32_t rxne : 1; #endif - } s; + } s; }; union ctu_can_fd_command { - uint32_t u32; - struct ctu_can_fd_command_s { + uint32_t u32; + struct ctu_can_fd_command_s { #ifdef __LITTLE_ENDIAN_BITFIELD - uint32_t reserved_1_0 : 2; + uint32_t reserved_1_0 : 2; /* COMMAND */ - uint32_t rrb : 1; - uint32_t cdo : 1; - uint32_t ercrst : 1; - uint32_t rxfcrst : 1; - uint32_t txfcrst : 1; - uint32_t reserved_31_7 : 25; + uint32_t rrb : 1; + uint32_t cdo : 1; + uint32_t ercrst : 1; + uint32_t rxfcrst : 1; + uint32_t txfcrst : 1; + uint32_t reserved_31_7 : 25; #else - uint32_t reserved_31_7 : 25; - uint32_t txfcrst : 1; - uint32_t rxfcrst : 1; - uint32_t ercrst : 1; - uint32_t cdo : 1; - uint32_t rrb : 1; - uint32_t reserved_1_0 : 2; + uint32_t reserved_31_7 : 25; + uint32_t txfcrst : 1; + uint32_t rxfcrst : 1; + uint32_t ercrst : 1; + uint32_t cdo : 1; + uint32_t rrb : 1; + uint32_t reserved_1_0 : 2; #endif - } s; + } s; }; union ctu_can_fd_int_stat { - uint32_t u32; - struct ctu_can_fd_int_stat_s { + uint32_t u32; + struct ctu_can_fd_int_stat_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* INT_STAT */ - uint32_t rxi : 1; - uint32_t txi : 1; - uint32_t ewli : 1; - uint32_t doi : 1; - uint32_t fcsi : 1; - uint32_t ali : 1; - uint32_t bei : 1; - uint32_t ofi : 1; - uint32_t rxfi : 1; - uint32_t bsi : 1; - uint32_t rbnei : 1; - uint32_t txbhci : 1; - uint32_t reserved_31_12 : 20; + uint32_t rxi : 1; + uint32_t txi : 1; + uint32_t ewli : 1; + uint32_t doi : 1; + uint32_t fcsi : 1; + uint32_t ali : 1; + uint32_t bei : 1; + uint32_t ofi : 1; + uint32_t rxfi : 1; + uint32_t bsi : 1; + uint32_t rbnei : 1; + uint32_t txbhci : 1; + uint32_t reserved_31_12 : 20; #else - uint32_t reserved_31_12 : 20; - uint32_t txbhci : 1; - uint32_t rbnei : 1; - uint32_t bsi : 1; - uint32_t rxfi : 1; - uint32_t ofi : 1; - uint32_t bei : 1; - uint32_t ali : 1; - uint32_t fcsi : 1; - uint32_t doi : 1; - uint32_t ewli : 1; - uint32_t txi : 1; - uint32_t rxi : 1; + uint32_t reserved_31_12 : 20; + uint32_t txbhci : 1; + uint32_t rbnei : 1; + uint32_t bsi : 1; + uint32_t rxfi : 1; + uint32_t ofi : 1; + uint32_t bei : 1; + uint32_t ali : 1; + uint32_t fcsi : 1; + uint32_t doi : 1; + uint32_t ewli : 1; + uint32_t txi : 1; + uint32_t rxi : 1; #endif - } s; + } s; }; union ctu_can_fd_int_ena_set { - uint32_t u32; - struct ctu_can_fd_int_ena_set_s { + uint32_t u32; + struct ctu_can_fd_int_ena_set_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* INT_ENA_SET */ - uint32_t int_ena_set : 12; - uint32_t reserved_31_12 : 20; + uint32_t int_ena_set : 12; + uint32_t reserved_31_12 : 20; #else - uint32_t reserved_31_12 : 20; - uint32_t int_ena_set : 12; + uint32_t reserved_31_12 : 20; + uint32_t int_ena_set : 12; #endif - } s; + } s; }; union ctu_can_fd_int_ena_clr { - uint32_t u32; - struct ctu_can_fd_int_ena_clr_s { + uint32_t u32; + struct ctu_can_fd_int_ena_clr_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* INT_ENA_CLR */ - uint32_t int_ena_clr : 12; - uint32_t reserved_31_12 : 20; + uint32_t int_ena_clr : 12; + uint32_t reserved_31_12 : 20; #else - uint32_t reserved_31_12 : 20; - uint32_t int_ena_clr : 12; + uint32_t reserved_31_12 : 20; + uint32_t int_ena_clr : 12; #endif - } s; + } s; }; union ctu_can_fd_int_mask_set { - uint32_t u32; - struct ctu_can_fd_int_mask_set_s { + uint32_t u32; + struct ctu_can_fd_int_mask_set_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* INT_MASK_SET */ - uint32_t int_mask_set : 12; - uint32_t reserved_31_12 : 20; + uint32_t int_mask_set : 12; + uint32_t reserved_31_12 : 20; #else - uint32_t reserved_31_12 : 20; - uint32_t int_mask_set : 12; + uint32_t reserved_31_12 : 20; + uint32_t int_mask_set : 12; #endif - } s; + } s; }; union ctu_can_fd_int_mask_clr { - uint32_t u32; - struct ctu_can_fd_int_mask_clr_s { + uint32_t u32; + struct ctu_can_fd_int_mask_clr_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* INT_MASK_CLR */ - uint32_t int_mask_clr : 12; - uint32_t reserved_31_12 : 20; + uint32_t int_mask_clr : 12; + uint32_t reserved_31_12 : 20; #else - uint32_t reserved_31_12 : 20; - uint32_t int_mask_clr : 12; + uint32_t reserved_31_12 : 20; + uint32_t int_mask_clr : 12; #endif - } s; + } s; }; union ctu_can_fd_btr { - uint32_t u32; - struct ctu_can_fd_btr_s { + uint32_t u32; + struct ctu_can_fd_btr_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* BTR */ - uint32_t prop : 7; - uint32_t ph1 : 6; - uint32_t ph2 : 6; - uint32_t brp : 8; - uint32_t sjw : 5; + uint32_t prop : 7; + uint32_t ph1 : 6; + uint32_t ph2 : 6; + uint32_t brp : 8; + uint32_t sjw : 5; #else - uint32_t sjw : 5; - uint32_t brp : 8; - uint32_t ph2 : 6; - uint32_t ph1 : 6; - uint32_t prop : 7; + uint32_t sjw : 5; + uint32_t brp : 8; + uint32_t ph2 : 6; + uint32_t ph1 : 6; + uint32_t prop : 7; #endif - } s; + } s; }; union ctu_can_fd_btr_fd { - uint32_t u32; - struct ctu_can_fd_btr_fd_s { + uint32_t u32; + struct ctu_can_fd_btr_fd_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* BTR_FD */ - uint32_t prop_fd : 6; - uint32_t reserved_6 : 1; - uint32_t ph1_fd : 5; - uint32_t reserved_12 : 1; - uint32_t ph2_fd : 5; - uint32_t reserved_18 : 1; - uint32_t brp_fd : 8; - uint32_t sjw_fd : 5; + uint32_t prop_fd : 6; + uint32_t reserved_6 : 1; + uint32_t ph1_fd : 5; + uint32_t reserved_12 : 1; + uint32_t ph2_fd : 5; + uint32_t reserved_18 : 1; + uint32_t brp_fd : 8; + uint32_t sjw_fd : 5; #else - uint32_t sjw_fd : 5; - uint32_t brp_fd : 8; - uint32_t reserved_18 : 1; - uint32_t ph2_fd : 5; - uint32_t reserved_12 : 1; - uint32_t ph1_fd : 5; - uint32_t reserved_6 : 1; - uint32_t prop_fd : 6; + uint32_t sjw_fd : 5; + uint32_t brp_fd : 8; + uint32_t reserved_18 : 1; + uint32_t ph2_fd : 5; + uint32_t reserved_12 : 1; + uint32_t ph1_fd : 5; + uint32_t reserved_6 : 1; + uint32_t prop_fd : 6; #endif - } s; + } s; }; union ctu_can_fd_ewl_erp_fault_state { - uint32_t u32; - struct ctu_can_fd_ewl_erp_fault_state_s { + uint32_t u32; + struct ctu_can_fd_ewl_erp_fault_state_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* EWL */ - uint32_t ew_limit : 8; + uint32_t ew_limit : 8; /* ERP */ - uint32_t erp_limit : 8; + uint32_t erp_limit : 8; /* FAULT_STATE */ - uint32_t era : 1; - uint32_t erp : 1; - uint32_t bof : 1; - uint32_t reserved_31_19 : 13; + uint32_t era : 1; + uint32_t erp : 1; + uint32_t bof : 1; + uint32_t reserved_31_19 : 13; #else - uint32_t reserved_31_19 : 13; - uint32_t bof : 1; - uint32_t erp : 1; - uint32_t era : 1; - uint32_t erp_limit : 8; - uint32_t ew_limit : 8; + uint32_t reserved_31_19 : 13; + uint32_t bof : 1; + uint32_t erp : 1; + uint32_t era : 1; + uint32_t erp_limit : 8; + uint32_t ew_limit : 8; #endif - } s; + } s; }; union ctu_can_fd_rec_tec { - uint32_t u32; - struct ctu_can_fd_rec_tec_s { + uint32_t u32; + struct ctu_can_fd_rec_tec_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* REC */ - uint32_t rec_val : 9; - uint32_t reserved_15_9 : 7; + uint32_t rec_val : 9; + uint32_t reserved_15_9 : 7; /* TEC */ - uint32_t tec_val : 9; - uint32_t reserved_31_25 : 7; + uint32_t tec_val : 9; + uint32_t reserved_31_25 : 7; #else - uint32_t reserved_31_25 : 7; - uint32_t tec_val : 9; - uint32_t reserved_15_9 : 7; - uint32_t rec_val : 9; + uint32_t reserved_31_25 : 7; + uint32_t tec_val : 9; + uint32_t reserved_15_9 : 7; + uint32_t rec_val : 9; #endif - } s; + } s; }; union ctu_can_fd_err_norm_err_fd { - uint32_t u32; - struct ctu_can_fd_err_norm_err_fd_s { + uint32_t u32; + struct ctu_can_fd_err_norm_err_fd_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* ERR_NORM */ - uint32_t err_norm_val : 16; + uint32_t err_norm_val : 16; /* ERR_FD */ - uint32_t err_fd_val : 16; + uint32_t err_fd_val : 16; #else - uint32_t err_fd_val : 16; - uint32_t err_norm_val : 16; + uint32_t err_fd_val : 16; + uint32_t err_norm_val : 16; #endif - } s; + } s; }; union ctu_can_fd_ctr_pres { - uint32_t u32; - struct ctu_can_fd_ctr_pres_s { + uint32_t u32; + struct ctu_can_fd_ctr_pres_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* CTR_PRES */ - uint32_t ctpv : 9; - uint32_t ptx : 1; - uint32_t prx : 1; - uint32_t enorm : 1; - uint32_t efd : 1; - uint32_t reserved_31_13 : 19; + uint32_t ctpv : 9; + uint32_t ptx : 1; + uint32_t prx : 1; + uint32_t enorm : 1; + uint32_t efd : 1; + uint32_t reserved_31_13 : 19; #else - uint32_t reserved_31_13 : 19; - uint32_t efd : 1; - uint32_t enorm : 1; - uint32_t prx : 1; - uint32_t ptx : 1; - uint32_t ctpv : 9; + uint32_t reserved_31_13 : 19; + uint32_t efd : 1; + uint32_t enorm : 1; + uint32_t prx : 1; + uint32_t ptx : 1; + uint32_t ctpv : 9; #endif - } s; + } s; }; union ctu_can_fd_filter_a_mask { - uint32_t u32; - struct ctu_can_fd_filter_a_mask_s { + uint32_t u32; + struct ctu_can_fd_filter_a_mask_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* FILTER_A_MASK */ - uint32_t bit_mask_a_val : 29; - uint32_t reserved_31_29 : 3; + uint32_t bit_mask_a_val : 29; + uint32_t reserved_31_29 : 3; #else - uint32_t reserved_31_29 : 3; - uint32_t bit_mask_a_val : 29; + uint32_t reserved_31_29 : 3; + uint32_t bit_mask_a_val : 29; #endif - } s; + } s; }; union ctu_can_fd_filter_a_val { - uint32_t u32; - struct ctu_can_fd_filter_a_val_s { + uint32_t u32; + struct ctu_can_fd_filter_a_val_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* FILTER_A_VAL */ - uint32_t bit_val_a_val : 29; - uint32_t reserved_31_29 : 3; + uint32_t bit_val_a_val : 29; + uint32_t reserved_31_29 : 3; #else - uint32_t reserved_31_29 : 3; - uint32_t bit_val_a_val : 29; + uint32_t reserved_31_29 : 3; + uint32_t bit_val_a_val : 29; #endif - } s; + } s; }; union ctu_can_fd_filter_b_mask { - uint32_t u32; - struct ctu_can_fd_filter_b_mask_s { + uint32_t u32; + struct ctu_can_fd_filter_b_mask_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* FILTER_B_MASK */ - uint32_t bit_mask_b_val : 29; - uint32_t reserved_31_29 : 3; + uint32_t bit_mask_b_val : 29; + uint32_t reserved_31_29 : 3; #else - uint32_t reserved_31_29 : 3; - uint32_t bit_mask_b_val : 29; + uint32_t reserved_31_29 : 3; + uint32_t bit_mask_b_val : 29; #endif - } s; + } s; }; union ctu_can_fd_filter_b_val { - uint32_t u32; - struct ctu_can_fd_filter_b_val_s { + uint32_t u32; + struct ctu_can_fd_filter_b_val_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* FILTER_B_VAL */ - uint32_t bit_val_b_val : 29; - uint32_t reserved_31_29 : 3; + uint32_t bit_val_b_val : 29; + uint32_t reserved_31_29 : 3; #else - uint32_t reserved_31_29 : 3; - uint32_t bit_val_b_val : 29; + uint32_t reserved_31_29 : 3; + uint32_t bit_val_b_val : 29; #endif - } s; + } s; }; union ctu_can_fd_filter_c_mask { - uint32_t u32; - struct ctu_can_fd_filter_c_mask_s { + uint32_t u32; + struct ctu_can_fd_filter_c_mask_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* FILTER_C_MASK */ - uint32_t bit_mask_c_val : 29; - uint32_t reserved_31_29 : 3; + uint32_t bit_mask_c_val : 29; + uint32_t reserved_31_29 : 3; #else - uint32_t reserved_31_29 : 3; - uint32_t bit_mask_c_val : 29; + uint32_t reserved_31_29 : 3; + uint32_t bit_mask_c_val : 29; #endif - } s; + } s; }; union ctu_can_fd_filter_c_val { - uint32_t u32; - struct ctu_can_fd_filter_c_val_s { + uint32_t u32; + struct ctu_can_fd_filter_c_val_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* FILTER_C_VAL */ - uint32_t bit_val_c_val : 29; - uint32_t reserved_31_29 : 3; + uint32_t bit_val_c_val : 29; + uint32_t reserved_31_29 : 3; #else - uint32_t reserved_31_29 : 3; - uint32_t bit_val_c_val : 29; + uint32_t reserved_31_29 : 3; + uint32_t bit_val_c_val : 29; #endif - } s; + } s; }; union ctu_can_fd_filter_ran_low { - uint32_t u32; - struct ctu_can_fd_filter_ran_low_s { + uint32_t u32; + struct ctu_can_fd_filter_ran_low_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* FILTER_RAN_LOW */ - uint32_t bit_ran_low_val : 29; - uint32_t reserved_31_29 : 3; + uint32_t bit_ran_low_val : 29; + uint32_t reserved_31_29 : 3; #else - uint32_t reserved_31_29 : 3; - uint32_t bit_ran_low_val : 29; + uint32_t reserved_31_29 : 3; + uint32_t bit_ran_low_val : 29; #endif - } s; + } s; }; union ctu_can_fd_filter_ran_high { - uint32_t u32; - struct ctu_can_fd_filter_ran_high_s { + uint32_t u32; + struct ctu_can_fd_filter_ran_high_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* FILTER_RAN_HIGH */ - uint32_t bit_ran_high_val : 29; - uint32_t reserved_31_29 : 3; + uint32_t bit_ran_high_val : 29; + uint32_t reserved_31_29 : 3; #else - uint32_t reserved_31_29 : 3; - uint32_t bit_ran_high_val : 29; + uint32_t reserved_31_29 : 3; + uint32_t bit_ran_high_val : 29; #endif - } s; + } s; }; union ctu_can_fd_filter_control_filter_status { - uint32_t u32; - struct ctu_can_fd_filter_control_filter_status_s { + uint32_t u32; + struct ctu_can_fd_filter_control_filter_status_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* FILTER_CONTROL */ - uint32_t fanb : 1; - uint32_t fane : 1; - uint32_t fafb : 1; - uint32_t fafe : 1; - uint32_t fbnb : 1; - uint32_t fbne : 1; - uint32_t fbfb : 1; - uint32_t fbfe : 1; - uint32_t fcnb : 1; - uint32_t fcne : 1; - uint32_t fcfb : 1; - uint32_t fcfe : 1; - uint32_t frnb : 1; - uint32_t frne : 1; - uint32_t frfb : 1; - uint32_t frfe : 1; + uint32_t fanb : 1; + uint32_t fane : 1; + uint32_t fafb : 1; + uint32_t fafe : 1; + uint32_t fbnb : 1; + uint32_t fbne : 1; + uint32_t fbfb : 1; + uint32_t fbfe : 1; + uint32_t fcnb : 1; + uint32_t fcne : 1; + uint32_t fcfb : 1; + uint32_t fcfe : 1; + uint32_t frnb : 1; + uint32_t frne : 1; + uint32_t frfb : 1; + uint32_t frfe : 1; /* FILTER_STATUS */ - uint32_t sfa : 1; - uint32_t sfb : 1; - uint32_t sfc : 1; - uint32_t sfr : 1; - uint32_t reserved_31_20 : 12; + uint32_t sfa : 1; + uint32_t sfb : 1; + uint32_t sfc : 1; + uint32_t sfr : 1; + uint32_t reserved_31_20 : 12; #else - uint32_t reserved_31_20 : 12; - uint32_t sfr : 1; - uint32_t sfc : 1; - uint32_t sfb : 1; - uint32_t sfa : 1; - uint32_t frfe : 1; - uint32_t frfb : 1; - uint32_t frne : 1; - uint32_t frnb : 1; - uint32_t fcfe : 1; - uint32_t fcfb : 1; - uint32_t fcne : 1; - uint32_t fcnb : 1; - uint32_t fbfe : 1; - uint32_t fbfb : 1; - uint32_t fbne : 1; - uint32_t fbnb : 1; - uint32_t fafe : 1; - uint32_t fafb : 1; - uint32_t fane : 1; - uint32_t fanb : 1; + uint32_t reserved_31_20 : 12; + uint32_t sfr : 1; + uint32_t sfc : 1; + uint32_t sfb : 1; + uint32_t sfa : 1; + uint32_t frfe : 1; + uint32_t frfb : 1; + uint32_t frne : 1; + uint32_t frnb : 1; + uint32_t fcfe : 1; + uint32_t fcfb : 1; + uint32_t fcne : 1; + uint32_t fcnb : 1; + uint32_t fbfe : 1; + uint32_t fbfb : 1; + uint32_t fbne : 1; + uint32_t fbnb : 1; + uint32_t fafe : 1; + uint32_t fafb : 1; + uint32_t fane : 1; + uint32_t fanb : 1; #endif - } s; + } s; }; union ctu_can_fd_rx_mem_info { - uint32_t u32; - struct ctu_can_fd_rx_mem_info_s { + uint32_t u32; + struct ctu_can_fd_rx_mem_info_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* RX_MEM_INFO */ - uint32_t rx_buff_size : 13; - uint32_t reserved_15_13 : 3; - uint32_t rx_mem_free : 13; - uint32_t reserved_31_29 : 3; + uint32_t rx_buff_size : 13; + uint32_t reserved_15_13 : 3; + uint32_t rx_mem_free : 13; + uint32_t reserved_31_29 : 3; #else - uint32_t reserved_31_29 : 3; - uint32_t rx_mem_free : 13; - uint32_t reserved_15_13 : 3; - uint32_t rx_buff_size : 13; + uint32_t reserved_31_29 : 3; + uint32_t rx_mem_free : 13; + uint32_t reserved_15_13 : 3; + uint32_t rx_buff_size : 13; #endif - } s; + } s; }; union ctu_can_fd_rx_pointers { - uint32_t u32; - struct ctu_can_fd_rx_pointers_s { + uint32_t u32; + struct ctu_can_fd_rx_pointers_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* RX_POINTERS */ - uint32_t rx_wpp : 12; - uint32_t reserved_15_12 : 4; - uint32_t rx_rpp : 12; - uint32_t reserved_31_28 : 4; + uint32_t rx_wpp : 12; + uint32_t reserved_15_12 : 4; + uint32_t rx_rpp : 12; + uint32_t reserved_31_28 : 4; #else - uint32_t reserved_31_28 : 4; - uint32_t rx_rpp : 12; - uint32_t reserved_15_12 : 4; - uint32_t rx_wpp : 12; + uint32_t reserved_31_28 : 4; + uint32_t rx_rpp : 12; + uint32_t reserved_15_12 : 4; + uint32_t rx_wpp : 12; #endif - } s; + } s; }; union ctu_can_fd_rx_status_rx_settings { - uint32_t u32; - struct ctu_can_fd_rx_status_rx_settings_s { + uint32_t u32; + struct ctu_can_fd_rx_status_rx_settings_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* RX_STATUS */ - uint32_t rxe : 1; - uint32_t rxf : 1; - uint32_t reserved_3_2 : 2; - uint32_t rxfrc : 11; - uint32_t reserved_15 : 1; + uint32_t rxe : 1; + uint32_t rxf : 1; + uint32_t reserved_3_2 : 2; + uint32_t rxfrc : 11; + uint32_t reserved_15 : 1; /* RX_SETTINGS */ - uint32_t rtsop : 1; - uint32_t reserved_31_17 : 15; + uint32_t rtsop : 1; + uint32_t reserved_31_17 : 15; #else - uint32_t reserved_31_17 : 15; - uint32_t rtsop : 1; - uint32_t reserved_15 : 1; - uint32_t rxfrc : 11; - uint32_t reserved_3_2 : 2; - uint32_t rxf : 1; - uint32_t rxe : 1; + uint32_t reserved_31_17 : 15; + uint32_t rtsop : 1; + uint32_t reserved_15 : 1; + uint32_t rxfrc : 11; + uint32_t reserved_3_2 : 2; + uint32_t rxf : 1; + uint32_t rxe : 1; #endif - } s; + } s; }; enum ctu_can_fd_rx_settings_rtsop { - RTS_END = 0x0, - RTS_BEG = 0x1, + RTS_END = 0x0, + RTS_BEG = 0x1, }; union ctu_can_fd_rx_data { - uint32_t u32; - struct ctu_can_fd_rx_data_s { + uint32_t u32; + struct ctu_can_fd_rx_data_s { /* RX_DATA */ - uint32_t rx_data : 32; - } s; + uint32_t rx_data : 32; + } s; }; union ctu_can_fd_tx_status { - uint32_t u32; - struct ctu_can_fd_tx_status_s { + uint32_t u32; + struct ctu_can_fd_tx_status_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* TX_STATUS */ - uint32_t tx1s : 4; - uint32_t tx2s : 4; - uint32_t tx3s : 4; - uint32_t tx4s : 4; - uint32_t reserved_31_16 : 16; + uint32_t tx1s : 4; + uint32_t tx2s : 4; + uint32_t tx3s : 4; + uint32_t tx4s : 4; + uint32_t reserved_31_16 : 16; #else - uint32_t reserved_31_16 : 16; - uint32_t tx4s : 4; - uint32_t tx3s : 4; - uint32_t tx2s : 4; - uint32_t tx1s : 4; + uint32_t reserved_31_16 : 16; + uint32_t tx4s : 4; + uint32_t tx3s : 4; + uint32_t tx2s : 4; + uint32_t tx1s : 4; #endif - } s; + } s; }; enum ctu_can_fd_tx_status_tx1s { - TXT_RDY = 0x1, - TXT_TRAN = 0x2, - TXT_ABTP = 0x3, - TXT_TOK = 0x4, - TXT_ERR = 0x6, - TXT_ABT = 0x7, - TXT_ETY = 0x8, + TXT_RDY = 0x1, + TXT_TRAN = 0x2, + TXT_ABTP = 0x3, + TXT_TOK = 0x4, + TXT_ERR = 0x6, + TXT_ABT = 0x7, + TXT_ETY = 0x8, }; union ctu_can_fd_tx_command { - uint32_t u32; - struct ctu_can_fd_tx_command_s { + uint32_t u32; + struct ctu_can_fd_tx_command_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* TX_COMMAND */ - uint32_t txce : 1; - uint32_t txcr : 1; - uint32_t txca : 1; - uint32_t reserved_7_3 : 5; - uint32_t txb1 : 1; - uint32_t txb2 : 1; - uint32_t txb3 : 1; - uint32_t txb4 : 1; - uint32_t reserved_31_12 : 20; + uint32_t txce : 1; + uint32_t txcr : 1; + uint32_t txca : 1; + uint32_t reserved_7_3 : 5; + uint32_t txb1 : 1; + uint32_t txb2 : 1; + uint32_t txb3 : 1; + uint32_t txb4 : 1; + uint32_t reserved_31_12 : 20; #else - uint32_t reserved_31_12 : 20; - uint32_t txb4 : 1; - uint32_t txb3 : 1; - uint32_t txb2 : 1; - uint32_t txb1 : 1; - uint32_t reserved_7_3 : 5; - uint32_t txca : 1; - uint32_t txcr : 1; - uint32_t txce : 1; + uint32_t reserved_31_12 : 20; + uint32_t txb4 : 1; + uint32_t txb3 : 1; + uint32_t txb2 : 1; + uint32_t txb1 : 1; + uint32_t reserved_7_3 : 5; + uint32_t txca : 1; + uint32_t txcr : 1; + uint32_t txce : 1; #endif - } s; + } s; }; union ctu_can_fd_tx_priority { - uint32_t u32; - struct ctu_can_fd_tx_priority_s { + uint32_t u32; + struct ctu_can_fd_tx_priority_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* TX_PRIORITY */ - uint32_t txt1p : 3; - uint32_t reserved_3 : 1; - uint32_t txt2p : 3; - uint32_t reserved_7 : 1; - uint32_t txt3p : 3; - uint32_t reserved_11 : 1; - uint32_t txt4p : 3; - uint32_t reserved_31_15 : 17; + uint32_t txt1p : 3; + uint32_t reserved_3 : 1; + uint32_t txt2p : 3; + uint32_t reserved_7 : 1; + uint32_t txt3p : 3; + uint32_t reserved_11 : 1; + uint32_t txt4p : 3; + uint32_t reserved_31_15 : 17; #else - uint32_t reserved_31_15 : 17; - uint32_t txt4p : 3; - uint32_t reserved_11 : 1; - uint32_t txt3p : 3; - uint32_t reserved_7 : 1; - uint32_t txt2p : 3; - uint32_t reserved_3 : 1; - uint32_t txt1p : 3; + uint32_t reserved_31_15 : 17; + uint32_t txt4p : 3; + uint32_t reserved_11 : 1; + uint32_t txt3p : 3; + uint32_t reserved_7 : 1; + uint32_t txt2p : 3; + uint32_t reserved_3 : 1; + uint32_t txt1p : 3; #endif - } s; + } s; }; union ctu_can_fd_err_capt_alc { - uint32_t u32; - struct ctu_can_fd_err_capt_alc_s { + uint32_t u32; + struct ctu_can_fd_err_capt_alc_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* ERR_CAPT */ - uint32_t err_pos : 5; - uint32_t err_type : 3; - uint32_t reserved_15_8 : 8; + uint32_t err_pos : 5; + uint32_t err_type : 3; + uint32_t reserved_15_8 : 8; /* ALC */ - uint32_t alc_bit : 5; - uint32_t alc_id_field : 3; - uint32_t reserved_31_24 : 8; + uint32_t alc_bit : 5; + uint32_t alc_id_field : 3; + uint32_t reserved_31_24 : 8; #else - uint32_t reserved_31_24 : 8; - uint32_t alc_id_field : 3; - uint32_t alc_bit : 5; - uint32_t reserved_15_8 : 8; - uint32_t err_type : 3; - uint32_t err_pos : 5; + uint32_t reserved_31_24 : 8; + uint32_t alc_id_field : 3; + uint32_t alc_bit : 5; + uint32_t reserved_15_8 : 8; + uint32_t err_type : 3; + uint32_t err_pos : 5; #endif - } s; + } s; }; enum ctu_can_fd_err_capt_err_pos { - ERC_POS_SOF = 0x0, - ERC_POS_ARB = 0x1, - ERC_POS_CTRL = 0x2, - ERC_POS_DATA = 0x3, - ERC_POS_CRC = 0x4, - ERC_POS_ACK = 0x5, - ERC_POS_EOF = 0x6, - ERC_POS_ERR = 0x7, - ERC_POS_OVRL = 0x8, - ERC_POS_OTHER = 0x1f, + ERC_POS_SOF = 0x0, + ERC_POS_ARB = 0x1, + ERC_POS_CTRL = 0x2, + ERC_POS_DATA = 0x3, + ERC_POS_CRC = 0x4, + ERC_POS_ACK = 0x5, + ERC_POS_EOF = 0x6, + ERC_POS_ERR = 0x7, + ERC_POS_OVRL = 0x8, + ERC_POS_OTHER = 0x1f, }; enum ctu_can_fd_err_capt_err_type { - ERC_BIT_ERR = 0x0, - ERC_CRC_ERR = 0x1, - ERC_FRM_ERR = 0x2, - ERC_ACK_ERR = 0x3, - ERC_STUF_ERR = 0x4, + ERC_BIT_ERR = 0x0, + ERC_CRC_ERR = 0x1, + ERC_FRM_ERR = 0x2, + ERC_ACK_ERR = 0x3, + ERC_STUF_ERR = 0x4, }; enum ctu_can_fd_alc_alc_id_field { - ALC_RSVD = 0x0, - ALC_BASE_ID = 0x1, - ALC_SRR_RTR = 0x2, - ALC_IDE = 0x3, - ALC_EXTENSION = 0x4, - ALC_RTR = 0x5, + ALC_RSVD = 0x0, + ALC_BASE_ID = 0x1, + ALC_SRR_RTR = 0x2, + ALC_IDE = 0x3, + ALC_EXTENSION = 0x4, + ALC_RTR = 0x5, }; union ctu_can_fd_trv_delay_ssp_cfg { - uint32_t u32; - struct ctu_can_fd_trv_delay_ssp_cfg_s { + uint32_t u32; + struct ctu_can_fd_trv_delay_ssp_cfg_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* TRV_DELAY */ - uint32_t trv_delay_value : 7; - uint32_t reserved_15_7 : 9; + uint32_t trv_delay_value : 7; + uint32_t reserved_15_7 : 9; /* SSP_CFG */ - uint32_t ssp_offset : 8; - uint32_t ssp_src : 2; - uint32_t reserved_31_26 : 6; + uint32_t ssp_offset : 8; + uint32_t ssp_src : 2; + uint32_t reserved_31_26 : 6; #else - uint32_t reserved_31_26 : 6; - uint32_t ssp_src : 2; - uint32_t ssp_offset : 8; - uint32_t reserved_15_7 : 9; - uint32_t trv_delay_value : 7; + uint32_t reserved_31_26 : 6; + uint32_t ssp_src : 2; + uint32_t ssp_offset : 8; + uint32_t reserved_15_7 : 9; + uint32_t trv_delay_value : 7; #endif - } s; + } s; }; enum ctu_can_fd_ssp_cfg_ssp_src { - SSP_SRC_MEAS_N_OFFSET = 0x0, - SSP_SRC_NO_SSP = 0x1, - SSP_SRC_OFFSET = 0x2, + SSP_SRC_MEAS_N_OFFSET = 0x0, + SSP_SRC_NO_SSP = 0x1, + SSP_SRC_OFFSET = 0x2, }; union ctu_can_fd_rx_fr_ctr { - uint32_t u32; - struct ctu_can_fd_rx_fr_ctr_s { + uint32_t u32; + struct ctu_can_fd_rx_fr_ctr_s { /* RX_FR_CTR */ - uint32_t rx_fr_ctr_val : 32; - } s; + uint32_t rx_fr_ctr_val : 32; + } s; }; union ctu_can_fd_tx_fr_ctr { - uint32_t u32; - struct ctu_can_fd_tx_fr_ctr_s { + uint32_t u32; + struct ctu_can_fd_tx_fr_ctr_s { /* TX_FR_CTR */ - uint32_t tx_fr_ctr_val : 32; - } s; + uint32_t tx_fr_ctr_val : 32; + } s; }; union ctu_can_fd_debug_register { - uint32_t u32; - struct ctu_can_fd_debug_register_s { + uint32_t u32; + struct ctu_can_fd_debug_register_s { #ifdef __LITTLE_ENDIAN_BITFIELD /* DEBUG_REGISTER */ - uint32_t stuff_count : 3; - uint32_t destuff_count : 3; - uint32_t pc_arb : 1; - uint32_t pc_con : 1; - uint32_t pc_dat : 1; - uint32_t pc_stc : 1; - uint32_t pc_crc : 1; - uint32_t pc_crcd : 1; - uint32_t pc_ack : 1; - uint32_t pc_ackd : 1; - uint32_t pc_eof : 1; - uint32_t pc_int : 1; - uint32_t pc_susp : 1; - uint32_t pc_ovr : 1; - uint32_t pc_sof : 1; - uint32_t reserved_31_19 : 13; + uint32_t stuff_count : 3; + uint32_t destuff_count : 3; + uint32_t pc_arb : 1; + uint32_t pc_con : 1; + uint32_t pc_dat : 1; + uint32_t pc_stc : 1; + uint32_t pc_crc : 1; + uint32_t pc_crcd : 1; + uint32_t pc_ack : 1; + uint32_t pc_ackd : 1; + uint32_t pc_eof : 1; + uint32_t pc_int : 1; + uint32_t pc_susp : 1; + uint32_t pc_ovr : 1; + uint32_t pc_sof : 1; + uint32_t reserved_31_19 : 13; #else - uint32_t reserved_31_19 : 13; - uint32_t pc_sof : 1; - uint32_t pc_ovr : 1; - uint32_t pc_susp : 1; - uint32_t pc_int : 1; - uint32_t pc_eof : 1; - uint32_t pc_ackd : 1; - uint32_t pc_ack : 1; - uint32_t pc_crcd : 1; - uint32_t pc_crc : 1; - uint32_t pc_stc : 1; - uint32_t pc_dat : 1; - uint32_t pc_con : 1; - uint32_t pc_arb : 1; - uint32_t destuff_count : 3; - uint32_t stuff_count : 3; + uint32_t reserved_31_19 : 13; + uint32_t pc_sof : 1; + uint32_t pc_ovr : 1; + uint32_t pc_susp : 1; + uint32_t pc_int : 1; + uint32_t pc_eof : 1; + uint32_t pc_ackd : 1; + uint32_t pc_ack : 1; + uint32_t pc_crcd : 1; + uint32_t pc_crc : 1; + uint32_t pc_stc : 1; + uint32_t pc_dat : 1; + uint32_t pc_con : 1; + uint32_t pc_arb : 1; + uint32_t destuff_count : 3; + uint32_t stuff_count : 3; #endif - } s; + } s; }; union ctu_can_fd_yolo_reg { - uint32_t u32; - struct ctu_can_fd_yolo_reg_s { + uint32_t u32; + struct ctu_can_fd_yolo_reg_s { /* YOLO_REG */ - uint32_t yolo_val : 32; - } s; + uint32_t yolo_val : 32; + } s; }; union ctu_can_fd_timestamp_low { - uint32_t u32; - struct ctu_can_fd_timestamp_low_s { + uint32_t u32; + struct ctu_can_fd_timestamp_low_s { /* TIMESTAMP_LOW */ - uint32_t timestamp_low : 32; - } s; + uint32_t timestamp_low : 32; + } s; }; union ctu_can_fd_timestamp_high { - uint32_t u32; - struct ctu_can_fd_timestamp_high_s { + uint32_t u32; + struct ctu_can_fd_timestamp_high_s { /* TIMESTAMP_HIGH */ - uint32_t timestamp_high : 32; - } s; + uint32_t timestamp_high : 32; + } s; }; #endif diff --git a/hw/net/can/ctucan_core.c b/hw/net/can/ctucan_core.c index f2c3b6a706..812b83e93e 100644 --- a/hw/net/can/ctucan_core.c +++ b/hw/net/can/ctucan_core.c @@ -617,7 +617,7 @@ const VMStateDescription vmstate_qemu_ctucan_tx_buffer = { .name = "qemu_ctucan_tx_buffer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(data, CtuCanCoreMsgBuffer, CTUCAN_CORE_MSG_MAX_LEN), VMSTATE_END_OF_LIST() } @@ -636,7 +636,7 @@ const VMStateDescription vmstate_ctucan = { .version_id = 1, .minimum_version_id = 1, .post_load = ctucan_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(mode_settings.u32, CtuCanCoreState), VMSTATE_UINT32(status.u32, CtuCanCoreState), VMSTATE_UINT32(int_stat.u32, CtuCanCoreState), diff --git a/hw/net/can/ctucan_pci.c b/hw/net/can/ctucan_pci.c index 50f4ea6cd6..d8f7344ddc 100644 --- a/hw/net/can/ctucan_pci.c +++ b/hw/net/can/ctucan_pci.c @@ -34,7 +34,7 @@ #include "qapi/error.h" #include "chardev/char.h" #include "hw/irq.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "net/can_emu.h" @@ -215,7 +215,7 @@ static const VMStateDescription vmstate_ctucan_pci = { .name = "ctucan_pci", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, CtuCanPCIState), VMSTATE_STRUCT(ctucan_state[0], CtuCanPCIState, 0, vmstate_ctucan, CtuCanCoreState), diff --git a/hw/net/can/meson.build b/hw/net/can/meson.build index 8fabbd9ee6..7382344628 100644 --- a/hw/net/can/meson.build +++ b/hw/net/can/meson.build @@ -1,7 +1,8 @@ -softmmu_ss.add(when: 'CONFIG_CAN_SJA1000', if_true: files('can_sja1000.c')) -softmmu_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_kvaser_pci.c')) -softmmu_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_pcm3680_pci.c')) -softmmu_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_mioe3680_pci.c')) -softmmu_ss.add(when: 'CONFIG_CAN_CTUCANFD', if_true: files('ctucan_core.c')) -softmmu_ss.add(when: 'CONFIG_CAN_CTUCANFD_PCI', if_true: files('ctucan_pci.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-can.c')) +system_ss.add(when: 'CONFIG_CAN_SJA1000', if_true: files('can_sja1000.c')) +system_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_kvaser_pci.c')) +system_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_pcm3680_pci.c')) +system_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_mioe3680_pci.c')) +system_ss.add(when: 'CONFIG_CAN_CTUCANFD', if_true: files('ctucan_core.c')) +system_ss.add(when: 'CONFIG_CAN_CTUCANFD_PCI', if_true: files('ctucan_pci.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-can.c')) +system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-canfd.c')) diff --git a/hw/net/can/trace-events b/hw/net/can/trace-events index 8346a98ab5..de64ac1b31 100644 --- a/hw/net/can/trace-events +++ b/hw/net/can/trace-events @@ -7,3 +7,10 @@ xlnx_can_filter_mask_pre_write(uint8_t filter_num, uint32_t value) "Filter%d MAS xlnx_can_tx_data(uint32_t id, uint8_t dlc, uint8_t db0, uint8_t db1, uint8_t db2, uint8_t db3, uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7) "Frame: ID: 0x%08x DLC: 0x%02x DATA: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x" xlnx_can_rx_data(uint32_t id, uint32_t dlc, uint8_t db0, uint8_t db1, uint8_t db2, uint8_t db3, uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7) "Frame: ID: 0x%08x DLC: 0x%02x DATA: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x" xlnx_can_rx_discard(uint32_t status) "Controller is not enabled for bus communication. Status Register: 0x%08x" + +# xlnx-versal-canfd.c +xlnx_canfd_update_irq(char *path, uint32_t isr, uint32_t ier, uint32_t irq) "%s: ISR: 0x%08x IER: 0x%08x IRQ: 0x%08x" +xlnx_canfd_rx_fifo_filter_reject(char *path, uint32_t id, uint8_t dlc) "%s: Frame: ID: 0x%08x DLC: 0x%02x" +xlnx_canfd_rx_data(char *path, uint32_t id, uint8_t dlc, uint8_t flags) "%s: Frame: ID: 0x%08x DLC: 0x%02x CANFD Flag: 0x%02x" +xlnx_canfd_tx_data(char *path, uint32_t id, uint8_t dlc, uint8_t flgas) "%s: Frame: ID: 0x%08x DLC: 0x%02x CANFD Flag: 0x%02x" +xlnx_canfd_reset(char *path, uint32_t val) "%s: Resetting controller with value = 0x%08x" diff --git a/hw/net/can/xlnx-versal-canfd.c b/hw/net/can/xlnx-versal-canfd.c new file mode 100644 index 0000000000..47a14cfe63 --- /dev/null +++ b/hw/net/can/xlnx-versal-canfd.c @@ -0,0 +1,2107 @@ +/* + * QEMU model of the Xilinx Versal CANFD device. + * + * This implementation is based on the following datasheet: + * https://docs.xilinx.com/v/u/2.0-English/pg223-canfd + * + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * + * Written-by: Vikram Garhwal + * + * Based on QEMU CANFD Device emulation implemented by Jin Yang, Deniz Eren and + * Pavel Pisa + * + * 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/sysbus.h" +#include "hw/irq.h" +#include "hw/register.h" +#include "qapi/error.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/cutils.h" +#include "qemu/event_notifier.h" +#include "hw/qdev-properties.h" +#include "qom/object_interfaces.h" +#include "migration/vmstate.h" +#include "hw/net/xlnx-versal-canfd.h" +#include "trace.h" + +REG32(SOFTWARE_RESET_REGISTER, 0x0) + FIELD(SOFTWARE_RESET_REGISTER, CEN, 1, 1) + FIELD(SOFTWARE_RESET_REGISTER, SRST, 0, 1) +REG32(MODE_SELECT_REGISTER, 0x4) + FIELD(MODE_SELECT_REGISTER, ITO, 8, 8) + FIELD(MODE_SELECT_REGISTER, ABR, 7, 1) + FIELD(MODE_SELECT_REGISTER, SBR, 6, 1) + FIELD(MODE_SELECT_REGISTER, DPEE, 5, 1) + FIELD(MODE_SELECT_REGISTER, DAR, 4, 1) + FIELD(MODE_SELECT_REGISTER, BRSD, 3, 1) + FIELD(MODE_SELECT_REGISTER, SNOOP, 2, 1) + FIELD(MODE_SELECT_REGISTER, LBACK, 1, 1) + FIELD(MODE_SELECT_REGISTER, SLEEP, 0, 1) +REG32(ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, 0x8) + FIELD(ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, BRP, 0, 8) +REG32(ARBITRATION_PHASE_BIT_TIMING_REGISTER, 0xc) + FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, SJW, 16, 7) + FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, TS2, 8, 7) + FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, TS1, 0, 8) +REG32(ERROR_COUNTER_REGISTER, 0x10) + FIELD(ERROR_COUNTER_REGISTER, REC, 8, 8) + FIELD(ERROR_COUNTER_REGISTER, TEC, 0, 8) +REG32(ERROR_STATUS_REGISTER, 0x14) + FIELD(ERROR_STATUS_REGISTER, F_BERR, 11, 1) + FIELD(ERROR_STATUS_REGISTER, F_STER, 10, 1) + FIELD(ERROR_STATUS_REGISTER, F_FMER, 9, 1) + FIELD(ERROR_STATUS_REGISTER, F_CRCER, 8, 1) + FIELD(ERROR_STATUS_REGISTER, ACKER, 4, 1) + FIELD(ERROR_STATUS_REGISTER, BERR, 3, 1) + FIELD(ERROR_STATUS_REGISTER, STER, 2, 1) + FIELD(ERROR_STATUS_REGISTER, FMER, 1, 1) + FIELD(ERROR_STATUS_REGISTER, CRCER, 0, 1) +REG32(STATUS_REGISTER, 0x18) + FIELD(STATUS_REGISTER, TDCV, 16, 7) + FIELD(STATUS_REGISTER, SNOOP, 12, 1) + FIELD(STATUS_REGISTER, BSFR_CONFIG, 10, 1) + FIELD(STATUS_REGISTER, PEE_CONFIG, 9, 1) + FIELD(STATUS_REGISTER, ESTAT, 7, 2) + FIELD(STATUS_REGISTER, ERRWRN, 6, 1) + FIELD(STATUS_REGISTER, BBSY, 5, 1) + FIELD(STATUS_REGISTER, BIDLE, 4, 1) + FIELD(STATUS_REGISTER, NORMAL, 3, 1) + FIELD(STATUS_REGISTER, SLEEP, 2, 1) + FIELD(STATUS_REGISTER, LBACK, 1, 1) + FIELD(STATUS_REGISTER, CONFIG, 0, 1) +REG32(INTERRUPT_STATUS_REGISTER, 0x1c) + FIELD(INTERRUPT_STATUS_REGISTER, TXEWMFLL, 31, 1) + FIELD(INTERRUPT_STATUS_REGISTER, TXEOFLW, 30, 1) + FIELD(INTERRUPT_STATUS_REGISTER, RXBOFLW_BI, 24, 6) + FIELD(INTERRUPT_STATUS_REGISTER, RXLRM_BI, 18, 6) + FIELD(INTERRUPT_STATUS_REGISTER, RXMNF, 17, 1) + FIELD(INTERRUPT_STATUS_REGISTER, RXFWMFLL_1, 16, 1) + FIELD(INTERRUPT_STATUS_REGISTER, RXFOFLW_1, 15, 1) + FIELD(INTERRUPT_STATUS_REGISTER, TXCRS, 14, 1) + FIELD(INTERRUPT_STATUS_REGISTER, TXRRS, 13, 1) + FIELD(INTERRUPT_STATUS_REGISTER, RXFWMFLL, 12, 1) + FIELD(INTERRUPT_STATUS_REGISTER, WKUP, 11, 1) + FIELD(INTERRUPT_STATUS_REGISTER, SLP, 10, 1) + FIELD(INTERRUPT_STATUS_REGISTER, BSOFF, 9, 1) + /* + * In the original HW description below bit is named as ERROR but an ERROR + * field name collides with a macro in Windows build. To avoid Windows build + * failures, the bit is renamed to ERROR_BIT. + */ + FIELD(INTERRUPT_STATUS_REGISTER, ERROR_BIT, 8, 1) + FIELD(INTERRUPT_STATUS_REGISTER, RXFOFLW, 6, 1) + FIELD(INTERRUPT_STATUS_REGISTER, TSCNT_OFLW, 5, 1) + FIELD(INTERRUPT_STATUS_REGISTER, RXOK, 4, 1) + FIELD(INTERRUPT_STATUS_REGISTER, BSFRD, 3, 1) + FIELD(INTERRUPT_STATUS_REGISTER, PEE, 2, 1) + FIELD(INTERRUPT_STATUS_REGISTER, TXOK, 1, 1) + FIELD(INTERRUPT_STATUS_REGISTER, ARBLST, 0, 1) +REG32(INTERRUPT_ENABLE_REGISTER, 0x20) + FIELD(INTERRUPT_ENABLE_REGISTER, ETXEWMFLL, 31, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ETXEOFLW, 30, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ERXMNF, 17, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ERXFWMFLL_1, 16, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ERXFOFLW_1, 15, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ETXCRS, 14, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ETXRRS, 13, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ERXFWMFLL, 12, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, EWKUP, 11, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ESLP, 10, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, EBSOFF, 9, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, EERROR, 8, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ERFXOFLW, 6, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ETSCNT_OFLW, 5, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ERXOK, 4, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, EBSFRD, 3, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, EPEE, 2, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, ETXOK, 1, 1) + FIELD(INTERRUPT_ENABLE_REGISTER, EARBLOST, 0, 1) +REG32(INTERRUPT_CLEAR_REGISTER, 0x24) + FIELD(INTERRUPT_CLEAR_REGISTER, CTXEWMFLL, 31, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CTXEOFLW, 30, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CRXMNF, 17, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CRXFWMFLL_1, 16, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CRXFOFLW_1, 15, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CTXCRS, 14, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CTXRRS, 13, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CRXFWMFLL, 12, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CWKUP, 11, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CSLP, 10, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CBSOFF, 9, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CERROR, 8, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CRFXOFLW, 6, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CTSCNT_OFLW, 5, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CRXOK, 4, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CBSFRD, 3, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CPEE, 2, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CTXOK, 1, 1) + FIELD(INTERRUPT_CLEAR_REGISTER, CARBLOST, 0, 1) +REG32(TIMESTAMP_REGISTER, 0x28) + FIELD(TIMESTAMP_REGISTER, TIMESTAMP_CNT, 16, 16) + FIELD(TIMESTAMP_REGISTER, CTS, 0, 1) +REG32(DATA_PHASE_BAUD_RATE_PRESCALER_REGISTER, 0x88) + FIELD(DATA_PHASE_BAUD_RATE_PRESCALER_REGISTER, TDC, 16, 1) + FIELD(DATA_PHASE_BAUD_RATE_PRESCALER_REGISTER, TDCOFF, 8, 6) + FIELD(DATA_PHASE_BAUD_RATE_PRESCALER_REGISTER, DP_BRP, 0, 8) +REG32(DATA_PHASE_BIT_TIMING_REGISTER, 0x8c) + FIELD(DATA_PHASE_BIT_TIMING_REGISTER, DP_SJW, 16, 4) + FIELD(DATA_PHASE_BIT_TIMING_REGISTER, DP_TS2, 8, 4) + FIELD(DATA_PHASE_BIT_TIMING_REGISTER, DP_TS1, 0, 5) +REG32(TX_BUFFER_READY_REQUEST_REGISTER, 0x90) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR31, 31, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR30, 30, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR29, 29, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR28, 28, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR27, 27, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR26, 26, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR25, 25, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR24, 24, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR23, 23, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR22, 22, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR21, 21, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR20, 20, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR19, 19, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR18, 18, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR17, 17, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR16, 16, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR15, 15, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR14, 14, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR13, 13, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR12, 12, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR11, 11, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR10, 10, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR9, 9, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR8, 8, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR7, 7, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR6, 6, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR5, 5, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR4, 4, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR3, 3, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR2, 2, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR1, 1, 1) + FIELD(TX_BUFFER_READY_REQUEST_REGISTER, RR0, 0, 1) +REG32(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, 0x94) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS31, 31, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS30, 30, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS29, 29, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS28, 28, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS27, 27, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS26, 26, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS25, 25, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS24, 24, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS23, 23, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS22, 22, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS21, 21, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS20, 20, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS19, 19, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS18, 18, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS17, 17, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS16, 16, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS15, 15, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS14, 14, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS13, 13, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS12, 12, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS11, 11, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS10, 10, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS9, 9, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS8, 8, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS7, 7, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS6, 6, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS5, 5, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS4, 4, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS3, 3, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS2, 2, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS1, 1, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, ERRS0, 0, 1) +REG32(TX_BUFFER_CANCEL_REQUEST_REGISTER, 0x98) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR31, 31, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR30, 30, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR29, 29, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR28, 28, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR27, 27, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR26, 26, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR25, 25, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR24, 24, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR23, 23, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR22, 22, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR21, 21, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR20, 20, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR19, 19, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR18, 18, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR17, 17, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR16, 16, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR15, 15, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR14, 14, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR13, 13, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR12, 12, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR11, 11, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR10, 10, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR9, 9, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR8, 8, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR7, 7, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR6, 6, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR5, 5, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR4, 4, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR3, 3, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR2, 2, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR1, 1, 1) + FIELD(TX_BUFFER_CANCEL_REQUEST_REGISTER, CR0, 0, 1) +REG32(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, 0x9c) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS31, 31, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS30, 30, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS29, 29, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS28, 28, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS27, 27, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS26, 26, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS25, 25, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS24, 24, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS23, 23, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS22, 22, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS21, 21, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS20, 20, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS19, 19, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS18, 18, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS17, 17, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS16, 16, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS15, 15, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS14, 14, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS13, 13, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS12, 12, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS11, 11, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS10, 10, + 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS9, 9, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS8, 8, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS7, 7, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS6, 6, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS5, 5, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS4, 4, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS3, 3, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS2, 2, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS1, 1, 1) + FIELD(INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, ECRS0, 0, 1) +REG32(TX_EVENT_FIFO_STATUS_REGISTER, 0xa0) + FIELD(TX_EVENT_FIFO_STATUS_REGISTER, TXE_FL, 8, 6) + FIELD(TX_EVENT_FIFO_STATUS_REGISTER, TXE_IRI, 7, 1) + FIELD(TX_EVENT_FIFO_STATUS_REGISTER, TXE_RI, 0, 5) +REG32(TX_EVENT_FIFO_WATERMARK_REGISTER, 0xa4) + FIELD(TX_EVENT_FIFO_WATERMARK_REGISTER, TXE_FWM, 0, 5) +REG32(ACCEPTANCE_FILTER_CONTROL_REGISTER, 0xe0) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF31, 31, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF30, 30, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF29, 29, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF28, 28, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF27, 27, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF26, 26, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF25, 25, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF24, 24, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF23, 23, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF22, 22, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF21, 21, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF20, 20, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF19, 19, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF18, 18, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF17, 17, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF16, 16, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF15, 15, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF14, 14, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF13, 13, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF12, 12, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF11, 11, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF10, 10, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF9, 9, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF8, 8, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF7, 7, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF6, 6, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF5, 5, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF4, 4, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF3, 3, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF2, 2, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF1, 1, 1) + FIELD(ACCEPTANCE_FILTER_CONTROL_REGISTER, UAF0, 0, 1) +REG32(RX_FIFO_STATUS_REGISTER, 0xe8) + FIELD(RX_FIFO_STATUS_REGISTER, FL_1, 24, 7) + FIELD(RX_FIFO_STATUS_REGISTER, IRI_1, 23, 1) + FIELD(RX_FIFO_STATUS_REGISTER, RI_1, 16, 6) + FIELD(RX_FIFO_STATUS_REGISTER, FL, 8, 7) + FIELD(RX_FIFO_STATUS_REGISTER, IRI, 7, 1) + FIELD(RX_FIFO_STATUS_REGISTER, RI, 0, 6) +REG32(RX_FIFO_WATERMARK_REGISTER, 0xec) + FIELD(RX_FIFO_WATERMARK_REGISTER, RXFP, 16, 5) + FIELD(RX_FIFO_WATERMARK_REGISTER, RXFWM_1, 8, 6) + FIELD(RX_FIFO_WATERMARK_REGISTER, RXFWM, 0, 6) +REG32(TB_ID_REGISTER, 0x100) + FIELD(TB_ID_REGISTER, ID, 21, 11) + FIELD(TB_ID_REGISTER, SRR_RTR_RRS, 20, 1) + FIELD(TB_ID_REGISTER, IDE, 19, 1) + FIELD(TB_ID_REGISTER, ID_EXT, 1, 18) + FIELD(TB_ID_REGISTER, RTR_RRS, 0, 1) +REG32(TB0_DLC_REGISTER, 0x104) + FIELD(TB0_DLC_REGISTER, DLC, 28, 4) + FIELD(TB0_DLC_REGISTER, FDF, 27, 1) + FIELD(TB0_DLC_REGISTER, BRS, 26, 1) + FIELD(TB0_DLC_REGISTER, RSVD2, 25, 1) + FIELD(TB0_DLC_REGISTER, EFC, 24, 1) + FIELD(TB0_DLC_REGISTER, MM, 16, 8) + FIELD(TB0_DLC_REGISTER, RSVD1, 0, 16) +REG32(TB_DW0_REGISTER, 0x108) + FIELD(TB_DW0_REGISTER, DATA_BYTES0, 24, 8) + FIELD(TB_DW0_REGISTER, DATA_BYTES1, 16, 8) + FIELD(TB_DW0_REGISTER, DATA_BYTES2, 8, 8) + FIELD(TB_DW0_REGISTER, DATA_BYTES3, 0, 8) +REG32(TB_DW1_REGISTER, 0x10c) + FIELD(TB_DW1_REGISTER, DATA_BYTES4, 24, 8) + FIELD(TB_DW1_REGISTER, DATA_BYTES5, 16, 8) + FIELD(TB_DW1_REGISTER, DATA_BYTES6, 8, 8) + FIELD(TB_DW1_REGISTER, DATA_BYTES7, 0, 8) +REG32(TB_DW2_REGISTER, 0x110) + FIELD(TB_DW2_REGISTER, DATA_BYTES8, 24, 8) + FIELD(TB_DW2_REGISTER, DATA_BYTES9, 16, 8) + FIELD(TB_DW2_REGISTER, DATA_BYTES10, 8, 8) + FIELD(TB_DW2_REGISTER, DATA_BYTES11, 0, 8) +REG32(TB_DW3_REGISTER, 0x114) + FIELD(TB_DW3_REGISTER, DATA_BYTES12, 24, 8) + FIELD(TB_DW3_REGISTER, DATA_BYTES13, 16, 8) + FIELD(TB_DW3_REGISTER, DATA_BYTES14, 8, 8) + FIELD(TB_DW3_REGISTER, DATA_BYTES15, 0, 8) +REG32(TB_DW4_REGISTER, 0x118) + FIELD(TB_DW4_REGISTER, DATA_BYTES16, 24, 8) + FIELD(TB_DW4_REGISTER, DATA_BYTES17, 16, 8) + FIELD(TB_DW4_REGISTER, DATA_BYTES18, 8, 8) + FIELD(TB_DW4_REGISTER, DATA_BYTES19, 0, 8) +REG32(TB_DW5_REGISTER, 0x11c) + FIELD(TB_DW5_REGISTER, DATA_BYTES20, 24, 8) + FIELD(TB_DW5_REGISTER, DATA_BYTES21, 16, 8) + FIELD(TB_DW5_REGISTER, DATA_BYTES22, 8, 8) + FIELD(TB_DW5_REGISTER, DATA_BYTES23, 0, 8) +REG32(TB_DW6_REGISTER, 0x120) + FIELD(TB_DW6_REGISTER, DATA_BYTES24, 24, 8) + FIELD(TB_DW6_REGISTER, DATA_BYTES25, 16, 8) + FIELD(TB_DW6_REGISTER, DATA_BYTES26, 8, 8) + FIELD(TB_DW6_REGISTER, DATA_BYTES27, 0, 8) +REG32(TB_DW7_REGISTER, 0x124) + FIELD(TB_DW7_REGISTER, DATA_BYTES28, 24, 8) + FIELD(TB_DW7_REGISTER, DATA_BYTES29, 16, 8) + FIELD(TB_DW7_REGISTER, DATA_BYTES30, 8, 8) + FIELD(TB_DW7_REGISTER, DATA_BYTES31, 0, 8) +REG32(TB_DW8_REGISTER, 0x128) + FIELD(TB_DW8_REGISTER, DATA_BYTES32, 24, 8) + FIELD(TB_DW8_REGISTER, DATA_BYTES33, 16, 8) + FIELD(TB_DW8_REGISTER, DATA_BYTES34, 8, 8) + FIELD(TB_DW8_REGISTER, DATA_BYTES35, 0, 8) +REG32(TB_DW9_REGISTER, 0x12c) + FIELD(TB_DW9_REGISTER, DATA_BYTES36, 24, 8) + FIELD(TB_DW9_REGISTER, DATA_BYTES37, 16, 8) + FIELD(TB_DW9_REGISTER, DATA_BYTES38, 8, 8) + FIELD(TB_DW9_REGISTER, DATA_BYTES39, 0, 8) +REG32(TB_DW10_REGISTER, 0x130) + FIELD(TB_DW10_REGISTER, DATA_BYTES40, 24, 8) + FIELD(TB_DW10_REGISTER, DATA_BYTES41, 16, 8) + FIELD(TB_DW10_REGISTER, DATA_BYTES42, 8, 8) + FIELD(TB_DW10_REGISTER, DATA_BYTES43, 0, 8) +REG32(TB_DW11_REGISTER, 0x134) + FIELD(TB_DW11_REGISTER, DATA_BYTES44, 24, 8) + FIELD(TB_DW11_REGISTER, DATA_BYTES45, 16, 8) + FIELD(TB_DW11_REGISTER, DATA_BYTES46, 8, 8) + FIELD(TB_DW11_REGISTER, DATA_BYTES47, 0, 8) +REG32(TB_DW12_REGISTER, 0x138) + FIELD(TB_DW12_REGISTER, DATA_BYTES48, 24, 8) + FIELD(TB_DW12_REGISTER, DATA_BYTES49, 16, 8) + FIELD(TB_DW12_REGISTER, DATA_BYTES50, 8, 8) + FIELD(TB_DW12_REGISTER, DATA_BYTES51, 0, 8) +REG32(TB_DW13_REGISTER, 0x13c) + FIELD(TB_DW13_REGISTER, DATA_BYTES52, 24, 8) + FIELD(TB_DW13_REGISTER, DATA_BYTES53, 16, 8) + FIELD(TB_DW13_REGISTER, DATA_BYTES54, 8, 8) + FIELD(TB_DW13_REGISTER, DATA_BYTES55, 0, 8) +REG32(TB_DW14_REGISTER, 0x140) + FIELD(TB_DW14_REGISTER, DATA_BYTES56, 24, 8) + FIELD(TB_DW14_REGISTER, DATA_BYTES57, 16, 8) + FIELD(TB_DW14_REGISTER, DATA_BYTES58, 8, 8) + FIELD(TB_DW14_REGISTER, DATA_BYTES59, 0, 8) +REG32(TB_DW15_REGISTER, 0x144) + FIELD(TB_DW15_REGISTER, DATA_BYTES60, 24, 8) + FIELD(TB_DW15_REGISTER, DATA_BYTES61, 16, 8) + FIELD(TB_DW15_REGISTER, DATA_BYTES62, 8, 8) + FIELD(TB_DW15_REGISTER, DATA_BYTES63, 0, 8) +REG32(AFMR_REGISTER, 0xa00) + FIELD(AFMR_REGISTER, AMID, 21, 11) + FIELD(AFMR_REGISTER, AMSRR, 20, 1) + FIELD(AFMR_REGISTER, AMIDE, 19, 1) + FIELD(AFMR_REGISTER, AMID_EXT, 1, 18) + FIELD(AFMR_REGISTER, AMRTR, 0, 1) +REG32(AFIR_REGISTER, 0xa04) + FIELD(AFIR_REGISTER, AIID, 21, 11) + FIELD(AFIR_REGISTER, AISRR, 20, 1) + FIELD(AFIR_REGISTER, AIIDE, 19, 1) + FIELD(AFIR_REGISTER, AIID_EXT, 1, 18) + FIELD(AFIR_REGISTER, AIRTR, 0, 1) +REG32(TXE_FIFO_TB_ID_REGISTER, 0x2000) + FIELD(TXE_FIFO_TB_ID_REGISTER, ID, 21, 11) + FIELD(TXE_FIFO_TB_ID_REGISTER, SRR_RTR_RRS, 20, 1) + FIELD(TXE_FIFO_TB_ID_REGISTER, IDE, 19, 1) + FIELD(TXE_FIFO_TB_ID_REGISTER, ID_EXT, 1, 18) + FIELD(TXE_FIFO_TB_ID_REGISTER, RTR_RRS, 0, 1) +REG32(TXE_FIFO_TB_DLC_REGISTER, 0x2004) + FIELD(TXE_FIFO_TB_DLC_REGISTER, DLC, 28, 4) + FIELD(TXE_FIFO_TB_DLC_REGISTER, FDF, 27, 1) + FIELD(TXE_FIFO_TB_DLC_REGISTER, BRS, 26, 1) + FIELD(TXE_FIFO_TB_DLC_REGISTER, ET, 24, 2) + FIELD(TXE_FIFO_TB_DLC_REGISTER, MM, 16, 8) + FIELD(TXE_FIFO_TB_DLC_REGISTER, TIMESTAMP, 0, 16) +REG32(RB_ID_REGISTER, 0x2100) + FIELD(RB_ID_REGISTER, ID, 21, 11) + FIELD(RB_ID_REGISTER, SRR_RTR_RRS, 20, 1) + FIELD(RB_ID_REGISTER, IDE, 19, 1) + FIELD(RB_ID_REGISTER, ID_EXT, 1, 18) + FIELD(RB_ID_REGISTER, RTR_RRS, 0, 1) +REG32(RB_DLC_REGISTER, 0x2104) + FIELD(RB_DLC_REGISTER, DLC, 28, 4) + FIELD(RB_DLC_REGISTER, FDF, 27, 1) + FIELD(RB_DLC_REGISTER, BRS, 26, 1) + FIELD(RB_DLC_REGISTER, ESI, 25, 1) + FIELD(RB_DLC_REGISTER, MATCHED_FILTER_INDEX, 16, 5) + FIELD(RB_DLC_REGISTER, TIMESTAMP, 0, 16) +REG32(RB_DW0_REGISTER, 0x2108) + FIELD(RB_DW0_REGISTER, DATA_BYTES0, 24, 8) + FIELD(RB_DW0_REGISTER, DATA_BYTES1, 16, 8) + FIELD(RB_DW0_REGISTER, DATA_BYTES2, 8, 8) + FIELD(RB_DW0_REGISTER, DATA_BYTES3, 0, 8) +REG32(RB_DW1_REGISTER, 0x210c) + FIELD(RB_DW1_REGISTER, DATA_BYTES4, 24, 8) + FIELD(RB_DW1_REGISTER, DATA_BYTES5, 16, 8) + FIELD(RB_DW1_REGISTER, DATA_BYTES6, 8, 8) + FIELD(RB_DW1_REGISTER, DATA_BYTES7, 0, 8) +REG32(RB_DW2_REGISTER, 0x2110) + FIELD(RB_DW2_REGISTER, DATA_BYTES8, 24, 8) + FIELD(RB_DW2_REGISTER, DATA_BYTES9, 16, 8) + FIELD(RB_DW2_REGISTER, DATA_BYTES10, 8, 8) + FIELD(RB_DW2_REGISTER, DATA_BYTES11, 0, 8) +REG32(RB_DW3_REGISTER, 0x2114) + FIELD(RB_DW3_REGISTER, DATA_BYTES12, 24, 8) + FIELD(RB_DW3_REGISTER, DATA_BYTES13, 16, 8) + FIELD(RB_DW3_REGISTER, DATA_BYTES14, 8, 8) + FIELD(RB_DW3_REGISTER, DATA_BYTES15, 0, 8) +REG32(RB_DW4_REGISTER, 0x2118) + FIELD(RB_DW4_REGISTER, DATA_BYTES16, 24, 8) + FIELD(RB_DW4_REGISTER, DATA_BYTES17, 16, 8) + FIELD(RB_DW4_REGISTER, DATA_BYTES18, 8, 8) + FIELD(RB_DW4_REGISTER, DATA_BYTES19, 0, 8) +REG32(RB_DW5_REGISTER, 0x211c) + FIELD(RB_DW5_REGISTER, DATA_BYTES20, 24, 8) + FIELD(RB_DW5_REGISTER, DATA_BYTES21, 16, 8) + FIELD(RB_DW5_REGISTER, DATA_BYTES22, 8, 8) + FIELD(RB_DW5_REGISTER, DATA_BYTES23, 0, 8) +REG32(RB_DW6_REGISTER, 0x2120) + FIELD(RB_DW6_REGISTER, DATA_BYTES24, 24, 8) + FIELD(RB_DW6_REGISTER, DATA_BYTES25, 16, 8) + FIELD(RB_DW6_REGISTER, DATA_BYTES26, 8, 8) + FIELD(RB_DW6_REGISTER, DATA_BYTES27, 0, 8) +REG32(RB_DW7_REGISTER, 0x2124) + FIELD(RB_DW7_REGISTER, DATA_BYTES28, 24, 8) + FIELD(RB_DW7_REGISTER, DATA_BYTES29, 16, 8) + FIELD(RB_DW7_REGISTER, DATA_BYTES30, 8, 8) + FIELD(RB_DW7_REGISTER, DATA_BYTES31, 0, 8) +REG32(RB_DW8_REGISTER, 0x2128) + FIELD(RB_DW8_REGISTER, DATA_BYTES32, 24, 8) + FIELD(RB_DW8_REGISTER, DATA_BYTES33, 16, 8) + FIELD(RB_DW8_REGISTER, DATA_BYTES34, 8, 8) + FIELD(RB_DW8_REGISTER, DATA_BYTES35, 0, 8) +REG32(RB_DW9_REGISTER, 0x212c) + FIELD(RB_DW9_REGISTER, DATA_BYTES36, 24, 8) + FIELD(RB_DW9_REGISTER, DATA_BYTES37, 16, 8) + FIELD(RB_DW9_REGISTER, DATA_BYTES38, 8, 8) + FIELD(RB_DW9_REGISTER, DATA_BYTES39, 0, 8) +REG32(RB_DW10_REGISTER, 0x2130) + FIELD(RB_DW10_REGISTER, DATA_BYTES40, 24, 8) + FIELD(RB_DW10_REGISTER, DATA_BYTES41, 16, 8) + FIELD(RB_DW10_REGISTER, DATA_BYTES42, 8, 8) + FIELD(RB_DW10_REGISTER, DATA_BYTES43, 0, 8) +REG32(RB_DW11_REGISTER, 0x2134) + FIELD(RB_DW11_REGISTER, DATA_BYTES44, 24, 8) + FIELD(RB_DW11_REGISTER, DATA_BYTES45, 16, 8) + FIELD(RB_DW11_REGISTER, DATA_BYTES46, 8, 8) + FIELD(RB_DW11_REGISTER, DATA_BYTES47, 0, 8) +REG32(RB_DW12_REGISTER, 0x2138) + FIELD(RB_DW12_REGISTER, DATA_BYTES48, 24, 8) + FIELD(RB_DW12_REGISTER, DATA_BYTES49, 16, 8) + FIELD(RB_DW12_REGISTER, DATA_BYTES50, 8, 8) + FIELD(RB_DW12_REGISTER, DATA_BYTES51, 0, 8) +REG32(RB_DW13_REGISTER, 0x213c) + FIELD(RB_DW13_REGISTER, DATA_BYTES52, 24, 8) + FIELD(RB_DW13_REGISTER, DATA_BYTES53, 16, 8) + FIELD(RB_DW13_REGISTER, DATA_BYTES54, 8, 8) + FIELD(RB_DW13_REGISTER, DATA_BYTES55, 0, 8) +REG32(RB_DW14_REGISTER, 0x2140) + FIELD(RB_DW14_REGISTER, DATA_BYTES56, 24, 8) + FIELD(RB_DW14_REGISTER, DATA_BYTES57, 16, 8) + FIELD(RB_DW14_REGISTER, DATA_BYTES58, 8, 8) + FIELD(RB_DW14_REGISTER, DATA_BYTES59, 0, 8) +REG32(RB_DW15_REGISTER, 0x2144) + FIELD(RB_DW15_REGISTER, DATA_BYTES60, 24, 8) + FIELD(RB_DW15_REGISTER, DATA_BYTES61, 16, 8) + FIELD(RB_DW15_REGISTER, DATA_BYTES62, 8, 8) + FIELD(RB_DW15_REGISTER, DATA_BYTES63, 0, 8) +REG32(RB_ID_REGISTER_1, 0x4100) + FIELD(RB_ID_REGISTER_1, ID, 21, 11) + FIELD(RB_ID_REGISTER_1, SRR_RTR_RRS, 20, 1) + FIELD(RB_ID_REGISTER_1, IDE, 19, 1) + FIELD(RB_ID_REGISTER_1, ID_EXT, 1, 18) + FIELD(RB_ID_REGISTER_1, RTR_RRS, 0, 1) +REG32(RB_DLC_REGISTER_1, 0x4104) + FIELD(RB_DLC_REGISTER_1, DLC, 28, 4) + FIELD(RB_DLC_REGISTER_1, FDF, 27, 1) + FIELD(RB_DLC_REGISTER_1, BRS, 26, 1) + FIELD(RB_DLC_REGISTER_1, ESI, 25, 1) + FIELD(RB_DLC_REGISTER_1, MATCHED_FILTER_INDEX, 16, 5) + FIELD(RB_DLC_REGISTER_1, TIMESTAMP, 0, 16) +REG32(RB0_DW0_REGISTER_1, 0x4108) + FIELD(RB0_DW0_REGISTER_1, DATA_BYTES0, 24, 8) + FIELD(RB0_DW0_REGISTER_1, DATA_BYTES1, 16, 8) + FIELD(RB0_DW0_REGISTER_1, DATA_BYTES2, 8, 8) + FIELD(RB0_DW0_REGISTER_1, DATA_BYTES3, 0, 8) +REG32(RB_DW1_REGISTER_1, 0x410c) + FIELD(RB_DW1_REGISTER_1, DATA_BYTES4, 24, 8) + FIELD(RB_DW1_REGISTER_1, DATA_BYTES5, 16, 8) + FIELD(RB_DW1_REGISTER_1, DATA_BYTES6, 8, 8) + FIELD(RB_DW1_REGISTER_1, DATA_BYTES7, 0, 8) +REG32(RB_DW2_REGISTER_1, 0x4110) + FIELD(RB_DW2_REGISTER_1, DATA_BYTES8, 24, 8) + FIELD(RB_DW2_REGISTER_1, DATA_BYTES9, 16, 8) + FIELD(RB_DW2_REGISTER_1, DATA_BYTES10, 8, 8) + FIELD(RB_DW2_REGISTER_1, DATA_BYTES11, 0, 8) +REG32(RB_DW3_REGISTER_1, 0x4114) + FIELD(RB_DW3_REGISTER_1, DATA_BYTES12, 24, 8) + FIELD(RB_DW3_REGISTER_1, DATA_BYTES13, 16, 8) + FIELD(RB_DW3_REGISTER_1, DATA_BYTES14, 8, 8) + FIELD(RB_DW3_REGISTER_1, DATA_BYTES15, 0, 8) +REG32(RB_DW4_REGISTER_1, 0x4118) + FIELD(RB_DW4_REGISTER_1, DATA_BYTES16, 24, 8) + FIELD(RB_DW4_REGISTER_1, DATA_BYTES17, 16, 8) + FIELD(RB_DW4_REGISTER_1, DATA_BYTES18, 8, 8) + FIELD(RB_DW4_REGISTER_1, DATA_BYTES19, 0, 8) +REG32(RB_DW5_REGISTER_1, 0x411c) + FIELD(RB_DW5_REGISTER_1, DATA_BYTES20, 24, 8) + FIELD(RB_DW5_REGISTER_1, DATA_BYTES21, 16, 8) + FIELD(RB_DW5_REGISTER_1, DATA_BYTES22, 8, 8) + FIELD(RB_DW5_REGISTER_1, DATA_BYTES23, 0, 8) +REG32(RB_DW6_REGISTER_1, 0x4120) + FIELD(RB_DW6_REGISTER_1, DATA_BYTES24, 24, 8) + FIELD(RB_DW6_REGISTER_1, DATA_BYTES25, 16, 8) + FIELD(RB_DW6_REGISTER_1, DATA_BYTES26, 8, 8) + FIELD(RB_DW6_REGISTER_1, DATA_BYTES27, 0, 8) +REG32(RB_DW7_REGISTER_1, 0x4124) + FIELD(RB_DW7_REGISTER_1, DATA_BYTES28, 24, 8) + FIELD(RB_DW7_REGISTER_1, DATA_BYTES29, 16, 8) + FIELD(RB_DW7_REGISTER_1, DATA_BYTES30, 8, 8) + FIELD(RB_DW7_REGISTER_1, DATA_BYTES31, 0, 8) +REG32(RB_DW8_REGISTER_1, 0x4128) + FIELD(RB_DW8_REGISTER_1, DATA_BYTES32, 24, 8) + FIELD(RB_DW8_REGISTER_1, DATA_BYTES33, 16, 8) + FIELD(RB_DW8_REGISTER_1, DATA_BYTES34, 8, 8) + FIELD(RB_DW8_REGISTER_1, DATA_BYTES35, 0, 8) +REG32(RB_DW9_REGISTER_1, 0x412c) + FIELD(RB_DW9_REGISTER_1, DATA_BYTES36, 24, 8) + FIELD(RB_DW9_REGISTER_1, DATA_BYTES37, 16, 8) + FIELD(RB_DW9_REGISTER_1, DATA_BYTES38, 8, 8) + FIELD(RB_DW9_REGISTER_1, DATA_BYTES39, 0, 8) +REG32(RB_DW10_REGISTER_1, 0x4130) + FIELD(RB_DW10_REGISTER_1, DATA_BYTES40, 24, 8) + FIELD(RB_DW10_REGISTER_1, DATA_BYTES41, 16, 8) + FIELD(RB_DW10_REGISTER_1, DATA_BYTES42, 8, 8) + FIELD(RB_DW10_REGISTER_1, DATA_BYTES43, 0, 8) +REG32(RB_DW11_REGISTER_1, 0x4134) + FIELD(RB_DW11_REGISTER_1, DATA_BYTES44, 24, 8) + FIELD(RB_DW11_REGISTER_1, DATA_BYTES45, 16, 8) + FIELD(RB_DW11_REGISTER_1, DATA_BYTES46, 8, 8) + FIELD(RB_DW11_REGISTER_1, DATA_BYTES47, 0, 8) +REG32(RB_DW12_REGISTER_1, 0x4138) + FIELD(RB_DW12_REGISTER_1, DATA_BYTES48, 24, 8) + FIELD(RB_DW12_REGISTER_1, DATA_BYTES49, 16, 8) + FIELD(RB_DW12_REGISTER_1, DATA_BYTES50, 8, 8) + FIELD(RB_DW12_REGISTER_1, DATA_BYTES51, 0, 8) +REG32(RB_DW13_REGISTER_1, 0x413c) + FIELD(RB_DW13_REGISTER_1, DATA_BYTES52, 24, 8) + FIELD(RB_DW13_REGISTER_1, DATA_BYTES53, 16, 8) + FIELD(RB_DW13_REGISTER_1, DATA_BYTES54, 8, 8) + FIELD(RB_DW13_REGISTER_1, DATA_BYTES55, 0, 8) +REG32(RB_DW14_REGISTER_1, 0x4140) + FIELD(RB_DW14_REGISTER_1, DATA_BYTES56, 24, 8) + FIELD(RB_DW14_REGISTER_1, DATA_BYTES57, 16, 8) + FIELD(RB_DW14_REGISTER_1, DATA_BYTES58, 8, 8) + FIELD(RB_DW14_REGISTER_1, DATA_BYTES59, 0, 8) +REG32(RB_DW15_REGISTER_1, 0x4144) + FIELD(RB_DW15_REGISTER_1, DATA_BYTES60, 24, 8) + FIELD(RB_DW15_REGISTER_1, DATA_BYTES61, 16, 8) + FIELD(RB_DW15_REGISTER_1, DATA_BYTES62, 8, 8) + FIELD(RB_DW15_REGISTER_1, DATA_BYTES63, 0, 8) + +static uint8_t canfd_dlc_array[8] = {8, 12, 16, 20, 24, 32, 48, 64}; + +static void canfd_update_irq(XlnxVersalCANFDState *s) +{ + unsigned int irq = s->regs[R_INTERRUPT_STATUS_REGISTER] & + s->regs[R_INTERRUPT_ENABLE_REGISTER]; + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + /* RX watermark interrupts. */ + if (ARRAY_FIELD_EX32(s->regs, RX_FIFO_STATUS_REGISTER, FL) > + ARRAY_FIELD_EX32(s->regs, RX_FIFO_WATERMARK_REGISTER, RXFWM)) { + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFWMFLL, 1); + } + + if (ARRAY_FIELD_EX32(s->regs, RX_FIFO_STATUS_REGISTER, FL_1) > + ARRAY_FIELD_EX32(s->regs, RX_FIFO_WATERMARK_REGISTER, RXFWM_1)) { + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFWMFLL_1, 1); + } + + /* TX watermark interrupt. */ + if (ARRAY_FIELD_EX32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, TXE_FL) > + ARRAY_FIELD_EX32(s->regs, TX_EVENT_FIFO_WATERMARK_REGISTER, TXE_FWM)) { + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXEWMFLL, 1); + } + + trace_xlnx_canfd_update_irq(path, s->regs[R_INTERRUPT_STATUS_REGISTER], + s->regs[R_INTERRUPT_ENABLE_REGISTER], irq); + + qemu_set_irq(s->irq_canfd_int, irq); +} + +static void canfd_ier_post_write(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + + canfd_update_irq(s); +} + +static uint64_t canfd_icr_pre_write(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t val = val64; + + s->regs[R_INTERRUPT_STATUS_REGISTER] &= ~val; + + /* + * RXBOFLW_BI field is automatically cleared to default if RXBOFLW bit is + * cleared in ISR. + */ + if (ARRAY_FIELD_EX32(s->regs, INTERRUPT_STATUS_REGISTER, RXFWMFLL_1)) { + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXBOFLW_BI, 0); + } + + canfd_update_irq(s); + + return 0; +} + +static void canfd_config_reset(XlnxVersalCANFDState *s) +{ + + unsigned int i; + + /* Reset all the configuration registers. */ + for (i = 0; i < R_RX_FIFO_WATERMARK_REGISTER; ++i) { + register_reset(&s->reg_info[i]); + } + + canfd_update_irq(s); +} + +static void canfd_config_mode(XlnxVersalCANFDState *s) +{ + register_reset(&s->reg_info[R_ERROR_COUNTER_REGISTER]); + register_reset(&s->reg_info[R_ERROR_STATUS_REGISTER]); + register_reset(&s->reg_info[R_STATUS_REGISTER]); + + /* Put XlnxVersalCANFDState in configuration mode. */ + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 1); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, BSOFF, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ERROR_BIT, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFOFLW, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFOFLW_1, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 0); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ARBLST, 0); + + /* Clear the time stamp. */ + ptimer_transaction_begin(s->canfd_timer); + ptimer_set_count(s->canfd_timer, 0); + ptimer_transaction_commit(s->canfd_timer); + + canfd_update_irq(s); +} + +static void update_status_register_mode_bits(XlnxVersalCANFDState *s) +{ + bool sleep_status = ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP); + bool sleep_mode = ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SLEEP); + /* Wake up interrupt bit. */ + bool wakeup_irq_val = !sleep_mode && sleep_status; + /* Sleep interrupt bit. */ + bool sleep_irq_val = sleep_mode && !sleep_status; + + /* Clear previous core mode status bits. */ + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, LBACK, 0); + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SLEEP, 0); + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SNOOP, 0); + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 0); + + /* set current mode bit and generate irqs accordingly. */ + if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, LBACK)) { + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, LBACK, 1); + } else if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SLEEP)) { + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SLEEP, 1); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, + sleep_irq_val); + } else if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SNOOP)) { + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SNOOP, 1); + } else { + /* If all bits are zero, XlnxVersalCANFDState is set in normal mode. */ + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 1); + /* Set wakeup interrupt bit. */ + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP, + wakeup_irq_val); + } + + /* Put the CANFD in error active state. */ + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ESTAT, 1); + + canfd_update_irq(s); +} + +static uint64_t canfd_msr_pre_write(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t val = val64; + uint8_t multi_mode = 0; + + /* + * Multiple mode set check. This is done to make sure user doesn't set + * multiple modes. + */ + multi_mode = FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK) + + FIELD_EX32(val, MODE_SELECT_REGISTER, SLEEP) + + FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP); + + if (multi_mode > 1) { + qemu_log_mask(LOG_GUEST_ERROR, "Attempting to configure several modes" + " simultaneously. One mode will be selected according to" + " their priority: LBACK > SLEEP > SNOOP.\n"); + } + + if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) { + /* In configuration mode, any mode can be selected. */ + s->regs[R_MODE_SELECT_REGISTER] = val; + } else { + bool sleep_mode_bit = FIELD_EX32(val, MODE_SELECT_REGISTER, SLEEP); + + ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, sleep_mode_bit); + + if (FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK)) { + qemu_log_mask(LOG_GUEST_ERROR, "Attempting to set LBACK mode" + " without setting CEN bit as 0\n"); + } else if (FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP)) { + qemu_log_mask(LOG_GUEST_ERROR, "Attempting to set SNOOP mode" + " without setting CEN bit as 0\n"); + } + + update_status_register_mode_bits(s); + } + + return s->regs[R_MODE_SELECT_REGISTER]; +} + +static void canfd_exit_sleep_mode(XlnxVersalCANFDState *s) +{ + ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, 0); + update_status_register_mode_bits(s); +} + +static void regs2frame(XlnxVersalCANFDState *s, qemu_can_frame *frame, + uint32_t reg_num) +{ + uint32_t i = 0; + uint32_t j = 0; + uint32_t val = 0; + uint32_t dlc_reg_val = 0; + uint32_t dlc_value = 0; + + /* Check that reg_num should be within TX register space. */ + assert(reg_num <= R_TB_ID_REGISTER + (NUM_REGS_PER_MSG_SPACE * + s->cfg.tx_fifo)); + + dlc_reg_val = s->regs[reg_num + 1]; + dlc_value = FIELD_EX32(dlc_reg_val, TB0_DLC_REGISTER, DLC); + + frame->can_id = s->regs[reg_num]; + + if (FIELD_EX32(dlc_reg_val, TB0_DLC_REGISTER, FDF)) { + /* + * CANFD frame. + * Converting dlc(0 to 15) 4 Byte data to plain length(i.e. 0 to 64) + * 1 Byte data. This is done to make it work with SocketCAN. + * On actual CANFD frame, this value can't be more than 0xF. + * Conversion table for DLC to plain length: + * + * DLC Plain Length + * 0 - 8 0 - 8 + * 9 9 - 12 + * 10 13 - 16 + * 11 17 - 20 + * 12 21 - 24 + * 13 25 - 32 + * 14 33 - 48 + * 15 49 - 64 + */ + + frame->flags = QEMU_CAN_FRMF_TYPE_FD; + + if (dlc_value < 8) { + frame->can_dlc = dlc_value; + } else { + assert((dlc_value - 8) < ARRAY_SIZE(canfd_dlc_array)); + frame->can_dlc = canfd_dlc_array[dlc_value - 8]; + } + } else { + /* + * FD Format bit not set that means it is a CAN Frame. + * Conversion table for classic CAN: + * + * DLC Plain Length + * 0 - 7 0 - 7 + * 8 - 15 8 + */ + + if (dlc_value > 8) { + frame->can_dlc = 8; + qemu_log_mask(LOG_GUEST_ERROR, "Maximum DLC value for Classic CAN" + " frame is 8. Only 8 byte data will be sent.\n"); + } else { + frame->can_dlc = dlc_value; + } + } + + for (j = 0; j < frame->can_dlc; j++) { + val = 8 * i; + + frame->data[j] = extract32(s->regs[reg_num + 2 + (j / 4)], val, 8); + i++; + + if (i % 4 == 0) { + i = 0; + } + } +} + +static void process_cancellation_requests(XlnxVersalCANFDState *s) +{ + uint32_t clear_mask = s->regs[R_TX_BUFFER_READY_REQUEST_REGISTER] & + s->regs[R_TX_BUFFER_CANCEL_REQUEST_REGISTER]; + + s->regs[R_TX_BUFFER_READY_REQUEST_REGISTER] &= ~clear_mask; + s->regs[R_TX_BUFFER_CANCEL_REQUEST_REGISTER] &= ~clear_mask; + + canfd_update_irq(s); +} + +static void store_rx_sequential(XlnxVersalCANFDState *s, + const qemu_can_frame *frame, + uint32_t fill_level, uint32_t read_index, + uint32_t store_location, uint8_t rx_fifo, + bool rx_fifo_id, uint8_t filter_index) +{ + int i; + bool is_canfd_frame; + uint8_t dlc = frame->can_dlc; + uint8_t rx_reg_num = 0; + uint32_t dlc_reg_val = 0; + uint32_t data_reg_val = 0; + + /* Getting RX0/1 fill level */ + if ((fill_level) > rx_fifo - 1) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: RX%d Buffer is full. Discarding the" + " message\n", path, rx_fifo_id); + + /* Set the corresponding RF buffer overflow interrupt. */ + if (rx_fifo_id == 0) { + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFOFLW, 1); + } else { + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFOFLW_1, 1); + } + } else { + uint16_t rx_timestamp = CANFD_TIMER_MAX - + ptimer_get_count(s->canfd_timer); + + if (rx_timestamp == 0xFFFF) { + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TSCNT_OFLW, 1); + } else { + ARRAY_FIELD_DP32(s->regs, TIMESTAMP_REGISTER, TIMESTAMP_CNT, + rx_timestamp); + } + + if (rx_fifo_id == 0) { + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, FL, + fill_level + 1); + assert(store_location <= + R_RB_ID_REGISTER + (s->cfg.rx0_fifo * + NUM_REGS_PER_MSG_SPACE)); + } else { + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, FL_1, + fill_level + 1); + assert(store_location <= + R_RB_ID_REGISTER_1 + (s->cfg.rx1_fifo * + NUM_REGS_PER_MSG_SPACE)); + } + + s->regs[store_location] = frame->can_id; + + dlc = frame->can_dlc; + + if (frame->flags == QEMU_CAN_FRMF_TYPE_FD) { + is_canfd_frame = true; + + /* Store dlc value in Xilinx specific format. */ + for (i = 0; i < ARRAY_SIZE(canfd_dlc_array); i++) { + if (canfd_dlc_array[i] == frame->can_dlc) { + dlc_reg_val = FIELD_DP32(0, RB_DLC_REGISTER, DLC, 8 + i); + } + } + } else { + is_canfd_frame = false; + + if (frame->can_dlc > 8) { + dlc = 8; + } + + dlc_reg_val = FIELD_DP32(0, RB_DLC_REGISTER, DLC, dlc); + } + + dlc_reg_val |= FIELD_DP32(0, RB_DLC_REGISTER, FDF, is_canfd_frame); + dlc_reg_val |= FIELD_DP32(0, RB_DLC_REGISTER, TIMESTAMP, rx_timestamp); + dlc_reg_val |= FIELD_DP32(0, RB_DLC_REGISTER, MATCHED_FILTER_INDEX, + filter_index); + s->regs[store_location + 1] = dlc_reg_val; + + for (i = 0; i < dlc; i++) { + /* Register size is 4 byte but frame->data each is 1 byte. */ + switch (i % 4) { + case 0: + rx_reg_num = i / 4; + + data_reg_val = FIELD_DP32(0, RB_DW0_REGISTER, DATA_BYTES3, + frame->data[i]); + break; + case 1: + data_reg_val |= FIELD_DP32(0, RB_DW0_REGISTER, DATA_BYTES2, + frame->data[i]); + break; + case 2: + data_reg_val |= FIELD_DP32(0, RB_DW0_REGISTER, DATA_BYTES1, + frame->data[i]); + break; + case 3: + data_reg_val |= FIELD_DP32(0, RB_DW0_REGISTER, DATA_BYTES0, + frame->data[i]); + /* + * Last Bytes data which means we have all 4 bytes ready to + * store in one rx regs. + */ + s->regs[store_location + rx_reg_num + 2] = data_reg_val; + break; + } + } + + if (i % 4) { + /* + * In case DLC is not multiplier of 4, data is not saved to RX FIFO + * in above switch case. Store the remaining bytes here. + */ + s->regs[store_location + rx_reg_num + 2] = data_reg_val; + } + + /* set the interrupt as RXOK. */ + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 1); + } +} + +static void update_rx_sequential(XlnxVersalCANFDState *s, + const qemu_can_frame *frame) +{ + bool filter_pass = false; + uint8_t filter_index = 0; + int i; + int filter_partition = ARRAY_FIELD_EX32(s->regs, + RX_FIFO_WATERMARK_REGISTER, RXFP); + uint32_t store_location; + uint32_t fill_level; + uint32_t read_index; + uint8_t store_index = 0; + g_autofree char *path = NULL; + /* + * If all UAF bits are set to 0, then received messages are not stored + * in the RX buffers. + */ + if (s->regs[R_ACCEPTANCE_FILTER_CONTROL_REGISTER]) { + uint32_t acceptance_filter_status = + s->regs[R_ACCEPTANCE_FILTER_CONTROL_REGISTER]; + + for (i = 0; i < 32; i++) { + if (acceptance_filter_status & 0x1) { + uint32_t msg_id_masked = s->regs[R_AFMR_REGISTER + 2 * i] & + frame->can_id; + uint32_t afir_id_masked = s->regs[R_AFIR_REGISTER + 2 * i] & + s->regs[R_AFMR_REGISTER + 2 * i]; + uint16_t std_msg_id_masked = FIELD_EX32(msg_id_masked, + AFIR_REGISTER, AIID); + uint16_t std_afir_id_masked = FIELD_EX32(afir_id_masked, + AFIR_REGISTER, AIID); + uint32_t ext_msg_id_masked = FIELD_EX32(msg_id_masked, + AFIR_REGISTER, + AIID_EXT); + uint32_t ext_afir_id_masked = FIELD_EX32(afir_id_masked, + AFIR_REGISTER, + AIID_EXT); + bool ext_ide = FIELD_EX32(s->regs[R_AFMR_REGISTER + 2 * i], + AFMR_REGISTER, AMIDE); + + if (std_msg_id_masked == std_afir_id_masked) { + if (ext_ide) { + /* Extended message ID message. */ + if (ext_msg_id_masked == ext_afir_id_masked) { + filter_pass = true; + filter_index = i; + + break; + } + } else { + /* Standard message ID. */ + filter_pass = true; + filter_index = i; + + break; + } + } + } + acceptance_filter_status >>= 1; + } + } + + if (!filter_pass) { + path = object_get_canonical_path(OBJECT(s)); + + trace_xlnx_canfd_rx_fifo_filter_reject(path, frame->can_id, + frame->can_dlc); + } else { + if (filter_index <= filter_partition) { + fill_level = ARRAY_FIELD_EX32(s->regs, RX_FIFO_STATUS_REGISTER, FL); + read_index = ARRAY_FIELD_EX32(s->regs, RX_FIFO_STATUS_REGISTER, RI); + store_index = read_index + fill_level; + + if (read_index == s->cfg.rx0_fifo - 1) { + /* + * When ri is s->cfg.rx0_fifo - 1 i.e. max, it goes cyclic that + * means we reset the ri to 0x0. + */ + read_index = 0; + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, RI, + read_index); + } + + if (store_index > s->cfg.rx0_fifo - 1) { + store_index -= s->cfg.rx0_fifo - 1; + } + + store_location = R_RB_ID_REGISTER + + (store_index * NUM_REGS_PER_MSG_SPACE); + + store_rx_sequential(s, frame, fill_level, read_index, + store_location, s->cfg.rx0_fifo, 0, + filter_index); + } else { + /* RX 1 fill level message */ + fill_level = ARRAY_FIELD_EX32(s->regs, RX_FIFO_STATUS_REGISTER, + FL_1); + read_index = ARRAY_FIELD_EX32(s->regs, RX_FIFO_STATUS_REGISTER, + RI_1); + store_index = read_index + fill_level; + + if (read_index == s->cfg.rx1_fifo - 1) { + /* + * When ri is s->cfg.rx1_fifo - 1 i.e. max, it goes cyclic that + * means we reset the ri to 0x0. + */ + read_index = 0; + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, RI_1, + read_index); + } + + if (store_index > s->cfg.rx1_fifo - 1) { + store_index -= s->cfg.rx1_fifo - 1; + } + + store_location = R_RB_ID_REGISTER_1 + + (store_index * NUM_REGS_PER_MSG_SPACE); + + store_rx_sequential(s, frame, fill_level, read_index, + store_location, s->cfg.rx1_fifo, 1, + filter_index); + } + + path = object_get_canonical_path(OBJECT(s)); + + trace_xlnx_canfd_rx_data(path, frame->can_id, frame->can_dlc, + frame->flags); + canfd_update_irq(s); + } +} + +static bool tx_ready_check(XlnxVersalCANFDState *s) +{ + if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer data while" + " XlnxVersalCANFDState is in reset mode\n", path); + + return false; + } + + if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer data while" + " XlnxVersalCANFDState is in configuration mode." + " Reset the core so operations can start fresh\n", + path); + return false; + } + + if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SNOOP)) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer data while" + " XlnxVersalCANFDState is in SNOOP MODE\n", + path); + return false; + } + + return true; +} + +static void tx_fifo_stamp(XlnxVersalCANFDState *s, uint32_t tb0_regid) +{ + /* + * If EFC bit in DLC message is set, this means we will store the + * event of this transmitted message with time stamp. + */ + uint32_t dlc_reg_val = 0; + + if (FIELD_EX32(s->regs[tb0_regid + 1], TB0_DLC_REGISTER, EFC)) { + uint8_t dlc_val = FIELD_EX32(s->regs[tb0_regid + 1], TB0_DLC_REGISTER, + DLC); + bool fdf_val = FIELD_EX32(s->regs[tb0_regid + 1], TB0_DLC_REGISTER, + FDF); + bool brs_val = FIELD_EX32(s->regs[tb0_regid + 1], TB0_DLC_REGISTER, + BRS); + uint8_t mm_val = FIELD_EX32(s->regs[tb0_regid + 1], TB0_DLC_REGISTER, + MM); + uint8_t fill_level = ARRAY_FIELD_EX32(s->regs, + TX_EVENT_FIFO_STATUS_REGISTER, + TXE_FL); + uint8_t read_index = ARRAY_FIELD_EX32(s->regs, + TX_EVENT_FIFO_STATUS_REGISTER, + TXE_RI); + uint8_t store_index = fill_level + read_index; + + if ((fill_level) > s->cfg.tx_fifo - 1) { + qemu_log_mask(LOG_GUEST_ERROR, "TX Event Buffer is full." + " Discarding the message\n"); + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXEOFLW, 1); + } else { + if (read_index == s->cfg.tx_fifo - 1) { + /* + * When ri is s->cfg.tx_fifo - 1 i.e. max, it goes cyclic that + * means we reset the ri to 0x0. + */ + read_index = 0; + ARRAY_FIELD_DP32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, TXE_RI, + read_index); + } + + if (store_index > s->cfg.tx_fifo - 1) { + store_index -= s->cfg.tx_fifo - 1; + } + + assert(store_index < s->cfg.tx_fifo); + + uint32_t tx_event_reg0_id = R_TXE_FIFO_TB_ID_REGISTER + + (store_index * 2); + + /* Store message ID in TX event register. */ + s->regs[tx_event_reg0_id] = s->regs[tb0_regid]; + + uint16_t tx_timestamp = CANFD_TIMER_MAX - + ptimer_get_count(s->canfd_timer); + + /* Store DLC with time stamp in DLC regs. */ + dlc_reg_val = FIELD_DP32(0, TXE_FIFO_TB_DLC_REGISTER, DLC, dlc_val); + dlc_reg_val |= FIELD_DP32(0, TXE_FIFO_TB_DLC_REGISTER, FDF, + fdf_val); + dlc_reg_val |= FIELD_DP32(0, TXE_FIFO_TB_DLC_REGISTER, BRS, + brs_val); + dlc_reg_val |= FIELD_DP32(0, TXE_FIFO_TB_DLC_REGISTER, ET, 0x3); + dlc_reg_val |= FIELD_DP32(0, TXE_FIFO_TB_DLC_REGISTER, MM, mm_val); + dlc_reg_val |= FIELD_DP32(0, TXE_FIFO_TB_DLC_REGISTER, TIMESTAMP, + tx_timestamp); + s->regs[tx_event_reg0_id + 1] = dlc_reg_val; + + ARRAY_FIELD_DP32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, TXE_FL, + fill_level + 1); + } + } +} + +static gint g_cmp_ids(gconstpointer data1, gconstpointer data2) +{ + tx_ready_reg_info *tx_reg_1 = (tx_ready_reg_info *) data1; + tx_ready_reg_info *tx_reg_2 = (tx_ready_reg_info *) data2; + + return tx_reg_1->can_id - tx_reg_2->can_id; +} + +static void free_list(GSList *list) +{ + GSList *iterator = NULL; + + for (iterator = list; iterator != NULL; iterator = iterator->next) { + g_free((tx_ready_reg_info *)iterator->data); + } + + g_slist_free(list); + + return; +} + +static GSList *prepare_tx_data(XlnxVersalCANFDState *s) +{ + uint8_t i = 0; + GSList *list = NULL; + uint32_t reg_num = 0; + uint32_t reg_ready = s->regs[R_TX_BUFFER_READY_REQUEST_REGISTER]; + + /* First find the messages which are ready for transmission. */ + for (i = 0; i < s->cfg.tx_fifo; i++) { + if (reg_ready & 1) { + reg_num = R_TB_ID_REGISTER + (NUM_REGS_PER_MSG_SPACE * i); + tx_ready_reg_info *temp = g_new(tx_ready_reg_info, 1); + + temp->can_id = s->regs[reg_num]; + temp->reg_num = reg_num; + list = g_slist_prepend(list, temp); + list = g_slist_sort(list, g_cmp_ids); + } + + reg_ready >>= 1; + } + + s->regs[R_TX_BUFFER_READY_REQUEST_REGISTER] = 0; + s->regs[R_TX_BUFFER_CANCEL_REQUEST_REGISTER] = 0; + + return list; +} + +static void transfer_data(XlnxVersalCANFDState *s) +{ + bool canfd_tx = tx_ready_check(s); + GSList *list, *iterator = NULL; + qemu_can_frame frame; + + if (!canfd_tx) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller not enabled for data" + " transfer\n", path); + return; + } + + list = prepare_tx_data(s); + if (list == NULL) { + return; + } + + for (iterator = list; iterator != NULL; iterator = iterator->next) { + regs2frame(s, &frame, + ((tx_ready_reg_info *)iterator->data)->reg_num); + + if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) { + update_rx_sequential(s, &frame); + tx_fifo_stamp(s, ((tx_ready_reg_info *)iterator->data)->reg_num); + + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 1); + } else { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + trace_xlnx_canfd_tx_data(path, frame.can_id, frame.can_dlc, + frame.flags); + can_bus_client_send(&s->bus_client, &frame, 1); + tx_fifo_stamp(s, + ((tx_ready_reg_info *)iterator->data)->reg_num); + + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXRRS, 1); + + if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) { + canfd_exit_sleep_mode(s); + } + } + } + + ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 1); + free_list(list); + + canfd_update_irq(s); +} + +static uint64_t canfd_srr_pre_write(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t val = val64; + + ARRAY_FIELD_DP32(s->regs, SOFTWARE_RESET_REGISTER, CEN, + FIELD_EX32(val, SOFTWARE_RESET_REGISTER, CEN)); + + if (FIELD_EX32(val, SOFTWARE_RESET_REGISTER, SRST)) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + trace_xlnx_canfd_reset(path, val64); + + /* First, core will do software reset then will enter in config mode. */ + canfd_config_reset(s); + } else if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) { + canfd_config_mode(s); + } else { + /* + * Leave config mode. Now XlnxVersalCANFD core will enter Normal, Sleep, + * snoop or Loopback mode depending upon LBACK, SLEEP, SNOOP register + * states. + */ + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0); + + ptimer_transaction_begin(s->canfd_timer); + ptimer_set_count(s->canfd_timer, 0); + ptimer_transaction_commit(s->canfd_timer); + update_status_register_mode_bits(s); + transfer_data(s); + } + + return s->regs[R_SOFTWARE_RESET_REGISTER]; +} + +static uint64_t filter_mask(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t reg_idx = (reg->access->addr) / 4; + uint32_t val = val64; + uint32_t filter_offset = (reg_idx - R_AFMR_REGISTER) / 2; + + if (!(s->regs[R_ACCEPTANCE_FILTER_CONTROL_REGISTER] & + (1 << filter_offset))) { + s->regs[reg_idx] = val; + } else { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d not enabled\n", + path, filter_offset + 1); + } + + return s->regs[reg_idx]; +} + +static uint64_t filter_id(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + hwaddr reg_idx = (reg->access->addr) / 4; + uint32_t val = val64; + uint32_t filter_offset = (reg_idx - R_AFIR_REGISTER) / 2; + + if (!(s->regs[R_ACCEPTANCE_FILTER_CONTROL_REGISTER] & + (1 << filter_offset))) { + s->regs[reg_idx] = val; + } else { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d not enabled\n", + path, filter_offset + 1); + } + + return s->regs[reg_idx]; +} + +static uint64_t canfd_tx_fifo_status_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t val = val64; + uint8_t read_ind = 0; + uint8_t fill_ind = ARRAY_FIELD_EX32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, + TXE_FL); + + if (FIELD_EX32(val, TX_EVENT_FIFO_STATUS_REGISTER, TXE_IRI) && fill_ind) { + read_ind = ARRAY_FIELD_EX32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, + TXE_RI) + 1; + + if (read_ind > s->cfg.tx_fifo - 1) { + read_ind = 0; + } + + /* + * Increase the read index by 1 and decrease the fill level by 1. + */ + ARRAY_FIELD_DP32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, TXE_RI, + read_ind); + ARRAY_FIELD_DP32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, TXE_FL, + fill_ind - 1); + } + + return s->regs[R_TX_EVENT_FIFO_STATUS_REGISTER]; +} + +static uint64_t canfd_rx_fifo_status_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t val = val64; + uint8_t read_ind = 0; + uint8_t fill_ind = 0; + + if (FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, IRI)) { + /* FL index is zero, setting IRI bit has no effect. */ + if (FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, FL) != 0) { + read_ind = FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, RI) + 1; + + if (read_ind > s->cfg.rx0_fifo - 1) { + read_ind = 0; + } + + fill_ind = FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, FL) - 1; + + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, RI, read_ind); + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, FL, fill_ind); + } + } + + if (FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, IRI_1)) { + /* FL_1 index is zero, setting IRI_1 bit has no effect. */ + if (FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, FL_1) != 0) { + read_ind = FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, RI_1) + 1; + + if (read_ind > s->cfg.rx1_fifo - 1) { + read_ind = 0; + } + + fill_ind = FIELD_EX32(val, RX_FIFO_STATUS_REGISTER, FL_1) - 1; + + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, RI_1, read_ind); + ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, FL_1, fill_ind); + } + } + + return s->regs[R_RX_FIFO_STATUS_REGISTER]; +} + +static uint64_t canfd_tsr_pre_write(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t val = val64; + + if (FIELD_EX32(val, TIMESTAMP_REGISTER, CTS)) { + ARRAY_FIELD_DP32(s->regs, TIMESTAMP_REGISTER, TIMESTAMP_CNT, 0); + ptimer_transaction_begin(s->canfd_timer); + ptimer_set_count(s->canfd_timer, 0); + ptimer_transaction_commit(s->canfd_timer); + } + + return 0; +} + +static uint64_t canfd_trr_reg_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + + if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SNOOP)) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is in SNOOP mode." + " tx_ready_register will stay in reset mode\n", path); + return 0; + } else { + return val64; + } +} + +static void canfd_trr_reg_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + + transfer_data(s); +} + +static void canfd_cancel_reg_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + + process_cancellation_requests(s); +} + +static uint64_t canfd_write_check_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(reg->opaque); + uint32_t val = val64; + + if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) { + return val; + } + return 0; +} + +static const RegisterAccessInfo canfd_tx_regs[] = { + { .name = "TB_ID_REGISTER", .addr = A_TB_ID_REGISTER, + },{ .name = "TB0_DLC_REGISTER", .addr = A_TB0_DLC_REGISTER, + },{ .name = "TB_DW0_REGISTER", .addr = A_TB_DW0_REGISTER, + },{ .name = "TB_DW1_REGISTER", .addr = A_TB_DW1_REGISTER, + },{ .name = "TB_DW2_REGISTER", .addr = A_TB_DW2_REGISTER, + },{ .name = "TB_DW3_REGISTER", .addr = A_TB_DW3_REGISTER, + },{ .name = "TB_DW4_REGISTER", .addr = A_TB_DW4_REGISTER, + },{ .name = "TB_DW5_REGISTER", .addr = A_TB_DW5_REGISTER, + },{ .name = "TB_DW6_REGISTER", .addr = A_TB_DW6_REGISTER, + },{ .name = "TB_DW7_REGISTER", .addr = A_TB_DW7_REGISTER, + },{ .name = "TB_DW8_REGISTER", .addr = A_TB_DW8_REGISTER, + },{ .name = "TB_DW9_REGISTER", .addr = A_TB_DW9_REGISTER, + },{ .name = "TB_DW10_REGISTER", .addr = A_TB_DW10_REGISTER, + },{ .name = "TB_DW11_REGISTER", .addr = A_TB_DW11_REGISTER, + },{ .name = "TB_DW12_REGISTER", .addr = A_TB_DW12_REGISTER, + },{ .name = "TB_DW13_REGISTER", .addr = A_TB_DW13_REGISTER, + },{ .name = "TB_DW14_REGISTER", .addr = A_TB_DW14_REGISTER, + },{ .name = "TB_DW15_REGISTER", .addr = A_TB_DW15_REGISTER, + } +}; + +static const RegisterAccessInfo canfd_rx0_regs[] = { + { .name = "RB_ID_REGISTER", .addr = A_RB_ID_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DLC_REGISTER", .addr = A_RB_DLC_REGISTER, + .ro = 0xfe1fffff, + },{ .name = "RB_DW0_REGISTER", .addr = A_RB_DW0_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW1_REGISTER", .addr = A_RB_DW1_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW2_REGISTER", .addr = A_RB_DW2_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW3_REGISTER", .addr = A_RB_DW3_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW4_REGISTER", .addr = A_RB_DW4_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW5_REGISTER", .addr = A_RB_DW5_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW6_REGISTER", .addr = A_RB_DW6_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW7_REGISTER", .addr = A_RB_DW7_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW8_REGISTER", .addr = A_RB_DW8_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW9_REGISTER", .addr = A_RB_DW9_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW10_REGISTER", .addr = A_RB_DW10_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW11_REGISTER", .addr = A_RB_DW11_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW12_REGISTER", .addr = A_RB_DW12_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW13_REGISTER", .addr = A_RB_DW13_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW14_REGISTER", .addr = A_RB_DW14_REGISTER, + .ro = 0xffffffff, + },{ .name = "RB_DW15_REGISTER", .addr = A_RB_DW15_REGISTER, + .ro = 0xffffffff, + } +}; + +static const RegisterAccessInfo canfd_rx1_regs[] = { + { .name = "RB_ID_REGISTER_1", .addr = A_RB_ID_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DLC_REGISTER_1", .addr = A_RB_DLC_REGISTER_1, + .ro = 0xfe1fffff, + },{ .name = "RB0_DW0_REGISTER_1", .addr = A_RB0_DW0_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW1_REGISTER_1", .addr = A_RB_DW1_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW2_REGISTER_1", .addr = A_RB_DW2_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW3_REGISTER_1", .addr = A_RB_DW3_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW4_REGISTER_1", .addr = A_RB_DW4_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW5_REGISTER_1", .addr = A_RB_DW5_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW6_REGISTER_1", .addr = A_RB_DW6_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW7_REGISTER_1", .addr = A_RB_DW7_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW8_REGISTER_1", .addr = A_RB_DW8_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW9_REGISTER_1", .addr = A_RB_DW9_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW10_REGISTER_1", .addr = A_RB_DW10_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW11_REGISTER_1", .addr = A_RB_DW11_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW12_REGISTER_1", .addr = A_RB_DW12_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW13_REGISTER_1", .addr = A_RB_DW13_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW14_REGISTER_1", .addr = A_RB_DW14_REGISTER_1, + .ro = 0xffffffff, + },{ .name = "RB_DW15_REGISTER_1", .addr = A_RB_DW15_REGISTER_1, + .ro = 0xffffffff, + } +}; + +/* Acceptance filter registers. */ +static const RegisterAccessInfo canfd_af_regs[] = { + { .name = "AFMR_REGISTER", .addr = A_AFMR_REGISTER, + .pre_write = filter_mask, + },{ .name = "AFIR_REGISTER", .addr = A_AFIR_REGISTER, + .pre_write = filter_id, + } +}; + +static const RegisterAccessInfo canfd_txe_regs[] = { + { .name = "TXE_FIFO_TB_ID_REGISTER", .addr = A_TXE_FIFO_TB_ID_REGISTER, + .ro = 0xffffffff, + },{ .name = "TXE_FIFO_TB_DLC_REGISTER", .addr = A_TXE_FIFO_TB_DLC_REGISTER, + .ro = 0xffffffff, + } +}; + +static const RegisterAccessInfo canfd_regs_info[] = { + { .name = "SOFTWARE_RESET_REGISTER", .addr = A_SOFTWARE_RESET_REGISTER, + .pre_write = canfd_srr_pre_write, + },{ .name = "MODE_SELECT_REGISTER", .addr = A_MODE_SELECT_REGISTER, + .pre_write = canfd_msr_pre_write, + },{ .name = "ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER", + .addr = A_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, + .pre_write = canfd_write_check_prew, + },{ .name = "ARBITRATION_PHASE_BIT_TIMING_REGISTER", + .addr = A_ARBITRATION_PHASE_BIT_TIMING_REGISTER, + .pre_write = canfd_write_check_prew, + },{ .name = "ERROR_COUNTER_REGISTER", .addr = A_ERROR_COUNTER_REGISTER, + .ro = 0xffff, + },{ .name = "ERROR_STATUS_REGISTER", .addr = A_ERROR_STATUS_REGISTER, + .w1c = 0xf1f, + },{ .name = "STATUS_REGISTER", .addr = A_STATUS_REGISTER, + .reset = 0x1, + .ro = 0x7f17ff, + },{ .name = "INTERRUPT_STATUS_REGISTER", + .addr = A_INTERRUPT_STATUS_REGISTER, + .ro = 0xffffff7f, + },{ .name = "INTERRUPT_ENABLE_REGISTER", + .addr = A_INTERRUPT_ENABLE_REGISTER, + .post_write = canfd_ier_post_write, + },{ .name = "INTERRUPT_CLEAR_REGISTER", + .addr = A_INTERRUPT_CLEAR_REGISTER, .pre_write = canfd_icr_pre_write, + },{ .name = "TIMESTAMP_REGISTER", .addr = A_TIMESTAMP_REGISTER, + .ro = 0xffff0000, + .pre_write = canfd_tsr_pre_write, + },{ .name = "DATA_PHASE_BAUD_RATE_PRESCALER_REGISTER", + .addr = A_DATA_PHASE_BAUD_RATE_PRESCALER_REGISTER, + .pre_write = canfd_write_check_prew, + },{ .name = "DATA_PHASE_BIT_TIMING_REGISTER", + .addr = A_DATA_PHASE_BIT_TIMING_REGISTER, + .pre_write = canfd_write_check_prew, + },{ .name = "TX_BUFFER_READY_REQUEST_REGISTER", + .addr = A_TX_BUFFER_READY_REQUEST_REGISTER, + .pre_write = canfd_trr_reg_prew, + .post_write = canfd_trr_reg_postw, + },{ .name = "INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER", + .addr = A_INTERRUPT_ENABLE_TX_BUFFER_READY_REQUEST_REGISTER, + },{ .name = "TX_BUFFER_CANCEL_REQUEST_REGISTER", + .addr = A_TX_BUFFER_CANCEL_REQUEST_REGISTER, + .post_write = canfd_cancel_reg_postw, + },{ .name = "INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER", + .addr = A_INTERRUPT_ENABLE_TX_BUFFER_CANCELLATION_REQUEST_REGISTER, + },{ .name = "TX_EVENT_FIFO_STATUS_REGISTER", + .addr = A_TX_EVENT_FIFO_STATUS_REGISTER, + .ro = 0x3f1f, .pre_write = canfd_tx_fifo_status_prew, + },{ .name = "TX_EVENT_FIFO_WATERMARK_REGISTER", + .addr = A_TX_EVENT_FIFO_WATERMARK_REGISTER, + .reset = 0xf, + .pre_write = canfd_write_check_prew, + },{ .name = "ACCEPTANCE_FILTER_CONTROL_REGISTER", + .addr = A_ACCEPTANCE_FILTER_CONTROL_REGISTER, + },{ .name = "RX_FIFO_STATUS_REGISTER", .addr = A_RX_FIFO_STATUS_REGISTER, + .ro = 0x7f3f7f3f, .pre_write = canfd_rx_fifo_status_prew, + },{ .name = "RX_FIFO_WATERMARK_REGISTER", + .addr = A_RX_FIFO_WATERMARK_REGISTER, + .reset = 0x1f0f0f, + .pre_write = canfd_write_check_prew, + } +}; + +static void xlnx_versal_canfd_ptimer_cb(void *opaque) +{ + /* No action required on the timer rollover. */ +} + +static const MemoryRegionOps canfd_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void canfd_reset(DeviceState *dev) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->reg_info); ++i) { + register_reset(&s->reg_info[i]); + } + + ptimer_transaction_begin(s->canfd_timer); + ptimer_set_count(s->canfd_timer, 0); + ptimer_transaction_commit(s->canfd_timer); +} + +static bool can_xilinx_canfd_receive(CanBusClientState *client) +{ + XlnxVersalCANFDState *s = container_of(client, XlnxVersalCANFDState, + bus_client); + + bool reset_state = ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST); + bool can_enabled = ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN); + + return !reset_state && can_enabled; +} + +static ssize_t canfd_xilinx_receive(CanBusClientState *client, + const qemu_can_frame *buf, + size_t buf_size) +{ + XlnxVersalCANFDState *s = container_of(client, XlnxVersalCANFDState, + bus_client); + const qemu_can_frame *frame = buf; + + assert(buf_size > 0); + + if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) { + /* + * XlnxVersalCANFDState will not participate in normal bus communication + * and does not receive any messages transmitted by other CAN nodes. + */ + return 1; + } + + /* Update the status register that we are receiving message. */ + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, BBSY, 1); + + if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) { + /* Snoop Mode: Just keep the data. no response back. */ + update_rx_sequential(s, frame); + } else { + if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP))) { + /* + * XlnxVersalCANFDState is in sleep mode. Any data on bus will bring + * it to the wake up state. + */ + canfd_exit_sleep_mode(s); + } + + update_rx_sequential(s, frame); + } + + /* Message processing done. Update the status back to !busy */ + ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, BBSY, 0); + return 1; +} + +static CanBusClientInfo canfd_xilinx_bus_client_info = { + .can_receive = can_xilinx_canfd_receive, + .receive = canfd_xilinx_receive, +}; + +static int xlnx_canfd_connect_to_bus(XlnxVersalCANFDState *s, + CanBusState *bus) +{ + s->bus_client.info = &canfd_xilinx_bus_client_info; + + return can_bus_insert_client(bus, &s->bus_client); +} + +#define NUM_REG_PER_AF ARRAY_SIZE(canfd_af_regs) +#define NUM_AF 32 +#define NUM_REG_PER_TXE ARRAY_SIZE(canfd_txe_regs) +#define NUM_TXE 32 + +static int canfd_populate_regarray(XlnxVersalCANFDState *s, + RegisterInfoArray *r_array, int pos, + const RegisterAccessInfo *rae, + int num_rae) +{ + int i; + + for (i = 0; i < num_rae; i++) { + int index = rae[i].addr / 4; + RegisterInfo *r = &s->reg_info[index]; + + object_initialize(r, sizeof(*r), TYPE_REGISTER); + + *r = (RegisterInfo) { + .data = &s->regs[index], + .data_size = sizeof(uint32_t), + .access = &rae[i], + .opaque = OBJECT(s), + }; + + r_array->r[i + pos] = r; + } + return i + pos; +} + +static void canfd_create_rai(RegisterAccessInfo *rai_array, + const RegisterAccessInfo *canfd_regs, + int template_rai_array_sz, + int num_template_to_copy) +{ + int i; + int reg_num; + + for (reg_num = 0; reg_num < num_template_to_copy; reg_num++) { + int pos = reg_num * template_rai_array_sz; + + memcpy(rai_array + pos, canfd_regs, + template_rai_array_sz * sizeof(RegisterAccessInfo)); + + for (i = 0; i < template_rai_array_sz; i++) { + const char *name = canfd_regs[i].name; + uint64_t addr = canfd_regs[i].addr; + rai_array[i + pos].name = g_strdup_printf("%s%d", name, reg_num); + rai_array[i + pos].addr = addr + pos * 4; + } + } +} + +static RegisterInfoArray *canfd_create_regarray(XlnxVersalCANFDState *s) +{ + const char *device_prefix = object_get_typename(OBJECT(s)); + uint64_t memory_size = XLNX_VERSAL_CANFD_R_MAX * 4; + int num_regs; + int pos = 0; + RegisterInfoArray *r_array; + + num_regs = ARRAY_SIZE(canfd_regs_info) + + s->cfg.tx_fifo * NUM_REGS_PER_MSG_SPACE + + s->cfg.rx0_fifo * NUM_REGS_PER_MSG_SPACE + + NUM_AF * NUM_REG_PER_AF + + NUM_TXE * NUM_REG_PER_TXE; + + s->tx_regs = g_new0(RegisterAccessInfo, + s->cfg.tx_fifo * ARRAY_SIZE(canfd_tx_regs)); + + canfd_create_rai(s->tx_regs, canfd_tx_regs, + ARRAY_SIZE(canfd_tx_regs), s->cfg.tx_fifo); + + s->rx0_regs = g_new0(RegisterAccessInfo, + s->cfg.rx0_fifo * ARRAY_SIZE(canfd_rx0_regs)); + + canfd_create_rai(s->rx0_regs, canfd_rx0_regs, + ARRAY_SIZE(canfd_rx0_regs), s->cfg.rx0_fifo); + + s->af_regs = g_new0(RegisterAccessInfo, + NUM_AF * ARRAY_SIZE(canfd_af_regs)); + + canfd_create_rai(s->af_regs, canfd_af_regs, + ARRAY_SIZE(canfd_af_regs), NUM_AF); + + s->txe_regs = g_new0(RegisterAccessInfo, + NUM_TXE * ARRAY_SIZE(canfd_txe_regs)); + + canfd_create_rai(s->txe_regs, canfd_txe_regs, + ARRAY_SIZE(canfd_txe_regs), NUM_TXE); + + if (s->cfg.enable_rx_fifo1) { + num_regs += s->cfg.rx1_fifo * NUM_REGS_PER_MSG_SPACE; + + s->rx1_regs = g_new0(RegisterAccessInfo, + s->cfg.rx1_fifo * ARRAY_SIZE(canfd_rx1_regs)); + + canfd_create_rai(s->rx1_regs, canfd_rx1_regs, + ARRAY_SIZE(canfd_rx1_regs), s->cfg.rx1_fifo); + } + + r_array = g_new0(RegisterInfoArray, 1); + r_array->r = g_new0(RegisterInfo * , num_regs); + r_array->num_elements = num_regs; + r_array->prefix = device_prefix; + + pos = canfd_populate_regarray(s, r_array, pos, + canfd_regs_info, + ARRAY_SIZE(canfd_regs_info)); + pos = canfd_populate_regarray(s, r_array, pos, + s->tx_regs, s->cfg.tx_fifo * + NUM_REGS_PER_MSG_SPACE); + pos = canfd_populate_regarray(s, r_array, pos, + s->rx0_regs, s->cfg.rx0_fifo * + NUM_REGS_PER_MSG_SPACE); + if (s->cfg.enable_rx_fifo1) { + pos = canfd_populate_regarray(s, r_array, pos, + s->rx1_regs, s->cfg.rx1_fifo * + NUM_REGS_PER_MSG_SPACE); + } + pos = canfd_populate_regarray(s, r_array, pos, + s->af_regs, NUM_AF * NUM_REG_PER_AF); + pos = canfd_populate_regarray(s, r_array, pos, + s->txe_regs, NUM_TXE * NUM_REG_PER_TXE); + + memory_region_init_io(&r_array->mem, OBJECT(s), &canfd_ops, r_array, + device_prefix, memory_size); + return r_array; +} + +static void canfd_realize(DeviceState *dev, Error **errp) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(dev); + RegisterInfoArray *reg_array; + + reg_array = canfd_create_regarray(s); + memory_region_add_subregion(&s->iomem, 0x00, ®_array->mem); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq_canfd_int); + + if (s->canfdbus) { + if (xlnx_canfd_connect_to_bus(s, s->canfdbus) < 0) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); + + error_setg(errp, "%s: xlnx_canfd_connect_to_bus failed", path); + return; + } + + } + + /* Allocate a new timer. */ + s->canfd_timer = ptimer_init(xlnx_versal_canfd_ptimer_cb, s, + PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | + PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT | + PTIMER_POLICY_NO_IMMEDIATE_RELOAD); + + ptimer_transaction_begin(s->canfd_timer); + + ptimer_set_freq(s->canfd_timer, s->cfg.ext_clk_freq); + ptimer_set_limit(s->canfd_timer, CANFD_TIMER_MAX, 1); + ptimer_run(s->canfd_timer, 0); + ptimer_transaction_commit(s->canfd_timer); +} + +static void canfd_init(Object *obj) +{ + XlnxVersalCANFDState *s = XILINX_CANFD(obj); + + memory_region_init(&s->iomem, obj, TYPE_XILINX_CANFD, + XLNX_VERSAL_CANFD_R_MAX * 4); +} + +static const VMStateDescription vmstate_canfd = { + .name = TYPE_XILINX_CANFD, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, XlnxVersalCANFDState, + XLNX_VERSAL_CANFD_R_MAX), + VMSTATE_PTIMER(canfd_timer, XlnxVersalCANFDState), + VMSTATE_END_OF_LIST(), + } +}; + +static Property canfd_core_properties[] = { + DEFINE_PROP_UINT8("rx-fifo0", XlnxVersalCANFDState, cfg.rx0_fifo, 0x40), + DEFINE_PROP_UINT8("rx-fifo1", XlnxVersalCANFDState, cfg.rx1_fifo, 0x40), + DEFINE_PROP_UINT8("tx-fifo", XlnxVersalCANFDState, cfg.tx_fifo, 0x20), + DEFINE_PROP_BOOL("enable-rx-fifo1", XlnxVersalCANFDState, + cfg.enable_rx_fifo1, true), + DEFINE_PROP_UINT32("ext_clk_freq", XlnxVersalCANFDState, cfg.ext_clk_freq, + CANFD_DEFAULT_CLOCK), + DEFINE_PROP_LINK("canfdbus", XlnxVersalCANFDState, canfdbus, TYPE_CAN_BUS, + CanBusState *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void canfd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = canfd_reset; + dc->realize = canfd_realize; + device_class_set_props(dc, canfd_core_properties); + dc->vmsd = &vmstate_canfd; +} + +static const TypeInfo canfd_info = { + .name = TYPE_XILINX_CANFD, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalCANFDState), + .class_init = canfd_class_init, + .instance_init = canfd_init, +}; + +static void canfd_register_types(void) +{ + type_register_static(&canfd_info); +} + +type_init(canfd_register_types) diff --git a/hw/net/can/xlnx-zynqmp-can.c b/hw/net/can/xlnx-zynqmp-can.c index 82ac48cee2..ca0ce4e8bb 100644 --- a/hw/net/can/xlnx-zynqmp-can.c +++ b/hw/net/can/xlnx-zynqmp-can.c @@ -434,6 +434,52 @@ static bool tx_ready_check(XlnxZynqMPCANState *s) return true; } +static void read_tx_frame(XlnxZynqMPCANState *s, Fifo32 *fifo, uint32_t *data) +{ + unsigned used = fifo32_num_used(fifo); + bool is_txhpb = fifo == &s->txhpb_fifo; + + assert(used > 0); + used %= CAN_FRAME_SIZE; + + /* + * Frame Message Format + * + * Each frame includes four words (16 bytes). Software must read and write + * all four words regardless of the actual number of data bytes and valid + * fields in the message. + * If software misbehave (not writing all four words), we use the previous + * registers content to initialize each missing word. + * + * If used is 1 then ID, DLC and DATA1 are missing. + * if used is 2 then ID and DLC are missing. + * if used is 3 then only ID is missing. + */ + if (used > 0) { + data[0] = s->regs[is_txhpb ? R_TXHPB_ID : R_TXFIFO_ID]; + } else { + data[0] = fifo32_pop(fifo); + } + if (used == 1 || used == 2) { + data[1] = s->regs[is_txhpb ? R_TXHPB_DLC : R_TXFIFO_DLC]; + } else { + data[1] = fifo32_pop(fifo); + } + if (used == 1) { + data[2] = s->regs[is_txhpb ? R_TXHPB_DATA1 : R_TXFIFO_DATA1]; + } else { + data[2] = fifo32_pop(fifo); + } + /* DATA2 triggered the transfer thus is always available */ + data[3] = fifo32_pop(fifo); + + if (used) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Incomplete CAN frame (only %u/%u slots used)\n", + TYPE_XLNX_ZYNQMP_CAN, used, CAN_FRAME_SIZE); + } +} + static void transfer_fifo(XlnxZynqMPCANState *s, Fifo32 *fifo) { qemu_can_frame frame; @@ -451,9 +497,7 @@ static void transfer_fifo(XlnxZynqMPCANState *s, Fifo32 *fifo) } while (!fifo32_is_empty(fifo)) { - for (i = 0; i < CAN_FRAME_SIZE; i++) { - data[i] = fifo32_pop(fifo); - } + read_tx_frame(s, fifo, data); if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) { /* @@ -696,30 +740,30 @@ static void update_rx_fifo(XlnxZynqMPCANState *s, const qemu_can_frame *frame) timestamp)); /* First 32 bit of the data. */ - fifo32_push(&s->rx_fifo, deposit32(0, R_TXFIFO_DATA1_DB3_SHIFT, - R_TXFIFO_DATA1_DB3_LENGTH, + fifo32_push(&s->rx_fifo, deposit32(0, R_RXFIFO_DATA1_DB3_SHIFT, + R_RXFIFO_DATA1_DB3_LENGTH, frame->data[0]) | - deposit32(0, R_TXFIFO_DATA1_DB2_SHIFT, - R_TXFIFO_DATA1_DB2_LENGTH, + deposit32(0, R_RXFIFO_DATA1_DB2_SHIFT, + R_RXFIFO_DATA1_DB2_LENGTH, frame->data[1]) | - deposit32(0, R_TXFIFO_DATA1_DB1_SHIFT, - R_TXFIFO_DATA1_DB1_LENGTH, + deposit32(0, R_RXFIFO_DATA1_DB1_SHIFT, + R_RXFIFO_DATA1_DB1_LENGTH, frame->data[2]) | - deposit32(0, R_TXFIFO_DATA1_DB0_SHIFT, - R_TXFIFO_DATA1_DB0_LENGTH, + deposit32(0, R_RXFIFO_DATA1_DB0_SHIFT, + R_RXFIFO_DATA1_DB0_LENGTH, frame->data[3])); /* Last 32 bit of the data. */ - fifo32_push(&s->rx_fifo, deposit32(0, R_TXFIFO_DATA2_DB7_SHIFT, - R_TXFIFO_DATA2_DB7_LENGTH, + fifo32_push(&s->rx_fifo, deposit32(0, R_RXFIFO_DATA2_DB7_SHIFT, + R_RXFIFO_DATA2_DB7_LENGTH, frame->data[4]) | - deposit32(0, R_TXFIFO_DATA2_DB6_SHIFT, - R_TXFIFO_DATA2_DB6_LENGTH, + deposit32(0, R_RXFIFO_DATA2_DB6_SHIFT, + R_RXFIFO_DATA2_DB6_LENGTH, frame->data[5]) | - deposit32(0, R_TXFIFO_DATA2_DB5_SHIFT, - R_TXFIFO_DATA2_DB5_LENGTH, + deposit32(0, R_RXFIFO_DATA2_DB5_SHIFT, + R_RXFIFO_DATA2_DB5_LENGTH, frame->data[6]) | - deposit32(0, R_TXFIFO_DATA2_DB4_SHIFT, - R_TXFIFO_DATA2_DB4_LENGTH, + deposit32(0, R_RXFIFO_DATA2_DB4_SHIFT, + R_RXFIFO_DATA2_DB4_LENGTH, frame->data[7])); ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 1); @@ -734,14 +778,18 @@ static void update_rx_fifo(XlnxZynqMPCANState *s, const qemu_can_frame *frame) } } -static uint64_t can_rxfifo_pre_read(RegisterInfo *reg, uint64_t val) +static uint64_t can_rxfifo_post_read_id(RegisterInfo *reg, uint64_t val) { XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); + unsigned used = fifo32_num_used(&s->rx_fifo); - if (!fifo32_is_empty(&s->rx_fifo)) { - val = fifo32_pop(&s->rx_fifo); - } else { + if (used < CAN_FRAME_SIZE) { ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXUFLW, 1); + } else { + val = s->regs[R_RXFIFO_ID] = fifo32_pop(&s->rx_fifo); + s->regs[R_RXFIFO_DLC] = fifo32_pop(&s->rx_fifo); + s->regs[R_RXFIFO_DATA1] = fifo32_pop(&s->rx_fifo); + s->regs[R_RXFIFO_DATA2] = fifo32_pop(&s->rx_fifo); } can_update_irq(s); @@ -902,14 +950,11 @@ static const RegisterAccessInfo can_regs_info[] = { .post_write = can_tx_post_write, },{ .name = "RXFIFO_ID", .addr = A_RXFIFO_ID, .ro = 0xffffffff, - .post_read = can_rxfifo_pre_read, + .post_read = can_rxfifo_post_read_id, },{ .name = "RXFIFO_DLC", .addr = A_RXFIFO_DLC, .rsvd = 0xfff0000, - .post_read = can_rxfifo_pre_read, },{ .name = "RXFIFO_DATA1", .addr = A_RXFIFO_DATA1, - .post_read = can_rxfifo_pre_read, },{ .name = "RXFIFO_DATA2", .addr = A_RXFIFO_DATA2, - .post_read = can_rxfifo_pre_read, },{ .name = "AFR", .addr = A_AFR, .rsvd = 0xfffffff0, .post_write = can_filter_enable_post_write, @@ -1114,7 +1159,7 @@ static const VMStateDescription vmstate_can = { .name = TYPE_XLNX_ZYNQMP_CAN, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_FIFO32(rx_fifo, XlnxZynqMPCANState), VMSTATE_FIFO32(tx_fifo, XlnxZynqMPCANState), VMSTATE_FIFO32(txhpb_fifo, XlnxZynqMPCANState), diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index 45b954e46c..bf0652da1b 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "hw/irq.h" #include "hw/qdev-properties.h" +#include "hw/net/dp8393x.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "net/net.h" @@ -85,7 +86,6 @@ static const char *reg_names[] = { #define SONIC_MPT 0x2e #define SONIC_MDT 0x2f #define SONIC_DCR2 0x3f -#define SONIC_REG_COUNT 0x40 #define SONIC_CR_HTX 0x0001 #define SONIC_CR_TXP 0x0002 @@ -139,36 +139,6 @@ static const char *reg_names[] = { #define SONIC_DESC_EOL 0x0001 #define SONIC_DESC_ADDR 0xFFFE -#define TYPE_DP8393X "dp8393x" -OBJECT_DECLARE_SIMPLE_TYPE(dp8393xState, DP8393X) - -struct dp8393xState { - SysBusDevice parent_obj; - - /* Hardware */ - uint8_t it_shift; - bool big_endian; - bool last_rba_is_full; - qemu_irq irq; - int irq_level; - QEMUTimer *watchdog; - int64_t wt_last_update; - NICConf conf; - NICState *nic; - MemoryRegion mmio; - - /* Registers */ - uint16_t cam[16][3]; - uint16_t regs[SONIC_REG_COUNT]; - - /* Temporaries */ - uint8_t tx_buffer[0x10000]; - int loopback_packet; - - /* Memory access */ - MemoryRegion *dma_mr; - AddressSpace as; -}; /* * Accessor functions for values which are formed by @@ -581,7 +551,7 @@ static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size) val = s->cam[s->regs[SONIC_CEP] & 0xf][SONIC_CAP0 - reg]; } break; - /* All other registers have no special contraints */ + /* All other registers have no special constraints */ default: val = s->regs[reg]; } @@ -943,7 +913,8 @@ static void dp8393x_realize(DeviceState *dev, Error **errp) "dp8393x-regs", SONIC_REG_COUNT << s->it_shift); s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); 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); @@ -953,7 +924,7 @@ static const VMStateDescription vmstate_dp8393x = { .name = "dp8393x", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField []) { + .fields = (const VMStateField []) { VMSTATE_UINT16_2DARRAY(cam, dp8393xState, 16, 3), VMSTATE_UINT16_ARRAY(regs, dp8393xState, SONIC_REG_COUNT), VMSTATE_END_OF_LIST() diff --git a/hw/net/e1000.c b/hw/net/e1000.c index f5bc81296d..43f3a4a701 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -26,7 +26,8 @@ #include "qemu/osdep.h" -#include "hw/pci/pci.h" +#include "hw/net/mii.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "net/eth.h" @@ -38,12 +39,11 @@ #include "qemu/module.h" #include "qemu/range.h" +#include "e1000_common.h" #include "e1000x_common.h" #include "trace.h" #include "qom/object.h" -static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - /* #define E1000_DEBUG */ #ifdef E1000_DEBUG @@ -66,9 +66,8 @@ static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL); #define IOPORT_SIZE 0x40 #define PNPMMIO_SIZE 0x20000 -#define MIN_BUF_SIZE 60 /* Min. octets in an ethernet frame sans FCS */ -#define MAXIMUM_ETHERNET_HDR_LEN (14+4) +#define MAXIMUM_ETHERNET_HDR_LEN (ETH_HLEN + 4) /* * HW models: @@ -128,13 +127,9 @@ struct E1000State_st { QEMUTimer *flush_queue_timer; /* Compatibility flags for migration to/from qemu 1.3.0 and older */ -#define E1000_FLAG_AUTONEG_BIT 0 -#define E1000_FLAG_MIT_BIT 1 #define E1000_FLAG_MAC_BIT 2 #define E1000_FLAG_TSO_BIT 3 #define E1000_FLAG_VET_BIT 4 -#define E1000_FLAG_AUTONEG (1 << E1000_FLAG_AUTONEG_BIT) -#define E1000_FLAG_MIT (1 << E1000_FLAG_MIT_BIT) #define E1000_FLAG_MAC (1 << E1000_FLAG_MAC_BIT) #define E1000_FLAG_TSO (1 << E1000_FLAG_TSO_BIT) #define E1000_FLAG_VET (1 << E1000_FLAG_VET_BIT) @@ -181,67 +176,73 @@ e1000_autoneg_done(E1000State *s) static bool have_autoneg(E1000State *s) { - return chkflag(AUTONEG) && (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN); + return (s->phy_reg[MII_BMCR] & MII_BMCR_AUTOEN); } static void set_phy_ctrl(E1000State *s, int index, uint16_t val) { - /* bits 0-5 reserved; MII_CR_[RESTART_AUTO_NEG,RESET] are self clearing */ - s->phy_reg[PHY_CTRL] = val & ~(0x3f | - MII_CR_RESET | - MII_CR_RESTART_AUTO_NEG); + /* bits 0-5 reserved; MII_BMCR_[ANRESTART,RESET] are self clearing */ + s->phy_reg[MII_BMCR] = val & ~(0x3f | + MII_BMCR_RESET | + MII_BMCR_ANRESTART); /* * QEMU 1.3 does not support link auto-negotiation emulation, so if we * migrate during auto negotiation, after migration the link will be * down. */ - if (have_autoneg(s) && (val & MII_CR_RESTART_AUTO_NEG)) { + if (have_autoneg(s) && (val & MII_BMCR_ANRESTART)) { e1000x_restart_autoneg(s->mac_reg, s->phy_reg, s->autoneg_timer); } } static void (*phyreg_writeops[])(E1000State *, int, uint16_t) = { - [PHY_CTRL] = set_phy_ctrl, + [MII_BMCR] = set_phy_ctrl, }; enum { NPHYWRITEOPS = ARRAY_SIZE(phyreg_writeops) }; enum { PHY_R = 1, PHY_W = 2, PHY_RW = PHY_R | PHY_W }; static const char phy_regcap[0x20] = { - [PHY_STATUS] = PHY_R, [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW, - [PHY_ID1] = PHY_R, [M88E1000_PHY_SPEC_CTRL] = PHY_RW, - [PHY_CTRL] = PHY_RW, [PHY_1000T_CTRL] = PHY_RW, - [PHY_LP_ABILITY] = PHY_R, [PHY_1000T_STATUS] = PHY_R, - [PHY_AUTONEG_ADV] = PHY_RW, [M88E1000_RX_ERR_CNTR] = PHY_R, - [PHY_ID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R, - [PHY_AUTONEG_EXP] = PHY_R, + [MII_BMSR] = PHY_R, [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW, + [MII_PHYID1] = PHY_R, [M88E1000_PHY_SPEC_CTRL] = PHY_RW, + [MII_BMCR] = PHY_RW, [MII_CTRL1000] = PHY_RW, + [MII_ANLPAR] = PHY_R, [MII_STAT1000] = PHY_R, + [MII_ANAR] = PHY_RW, [M88E1000_RX_ERR_CNTR] = PHY_R, + [MII_PHYID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R, + [MII_ANER] = PHY_R, }; -/* PHY_ID2 documented in 8254x_GBe_SDM.pdf, pp. 250 */ +/* MII_PHYID2 documented in 8254x_GBe_SDM.pdf, pp. 250 */ static const uint16_t phy_reg_init[] = { - [PHY_CTRL] = MII_CR_SPEED_SELECT_MSB | - MII_CR_FULL_DUPLEX | - MII_CR_AUTO_NEG_EN, + [MII_BMCR] = MII_BMCR_SPEED1000 | + MII_BMCR_FD | + MII_BMCR_AUTOEN, - [PHY_STATUS] = MII_SR_EXTENDED_CAPS | - MII_SR_LINK_STATUS | /* link initially up */ - MII_SR_AUTONEG_CAPS | - /* MII_SR_AUTONEG_COMPLETE: initially NOT completed */ - MII_SR_PREAMBLE_SUPPRESS | - MII_SR_EXTENDED_STATUS | - MII_SR_10T_HD_CAPS | - MII_SR_10T_FD_CAPS | - MII_SR_100X_HD_CAPS | - MII_SR_100X_FD_CAPS, + [MII_BMSR] = MII_BMSR_EXTCAP | + MII_BMSR_LINK_ST | /* link initially up */ + MII_BMSR_AUTONEG | + /* MII_BMSR_AN_COMP: initially NOT completed */ + MII_BMSR_MFPS | + MII_BMSR_EXTSTAT | + MII_BMSR_10T_HD | + MII_BMSR_10T_FD | + MII_BMSR_100TX_HD | + MII_BMSR_100TX_FD, - [PHY_ID1] = 0x141, - /* [PHY_ID2] configured per DevId, from e1000_reset() */ - [PHY_AUTONEG_ADV] = 0xde1, - [PHY_LP_ABILITY] = 0x1e0, - [PHY_1000T_CTRL] = 0x0e00, - [PHY_1000T_STATUS] = 0x3c00, + [MII_PHYID1] = 0x141, + /* [MII_PHYID2] configured per DevId, from e1000_reset() */ + [MII_ANAR] = MII_ANAR_CSMACD | MII_ANAR_10 | + MII_ANAR_10FD | MII_ANAR_TX | + MII_ANAR_TXFD | MII_ANAR_PAUSE | + MII_ANAR_PAUSE_ASYM, + [MII_ANLPAR] = MII_ANLPAR_10 | MII_ANLPAR_10FD | + MII_ANLPAR_TX | MII_ANLPAR_TXFD, + [MII_CTRL1000] = MII_CTRL1000_FULL | MII_CTRL1000_PORT | + MII_CTRL1000_MASTER, + [MII_STAT1000] = MII_STAT1000_HALF | MII_STAT1000_FULL | + MII_STAT1000_ROK | MII_STAT1000_LOK, [M88E1000_PHY_SPEC_CTRL] = 0x360, [M88E1000_PHY_SPEC_STATUS] = 0xac00, [M88E1000_EXT_PHY_SPEC_CTRL] = 0x0d60, @@ -303,35 +304,34 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val) if (s->mit_timer_on) { return; } - if (chkflag(MIT)) { - /* Compute the next mitigation delay according to pending - * interrupts and the current values of RADV (provided - * RDTR!=0), TADV and ITR. - * Then rearm the timer. - */ - mit_delay = 0; - if (s->mit_ide && - (pending_ints & (E1000_ICR_TXQE | E1000_ICR_TXDW))) { - mit_update_delay(&mit_delay, s->mac_reg[TADV] * 4); - } - if (s->mac_reg[RDTR] && (pending_ints & E1000_ICS_RXT0)) { - mit_update_delay(&mit_delay, s->mac_reg[RADV] * 4); - } - mit_update_delay(&mit_delay, s->mac_reg[ITR]); - /* - * According to e1000 SPEC, the Ethernet controller guarantees - * a maximum observable interrupt rate of 7813 interrupts/sec. - * Thus if mit_delay < 500 then the delay should be set to the - * minimum delay possible which is 500. - */ - mit_delay = (mit_delay < 500) ? 500 : mit_delay; - - s->mit_timer_on = 1; - timer_mod(s->mit_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - mit_delay * 256); - s->mit_ide = 0; + /* Compute the next mitigation delay according to pending + * interrupts and the current values of RADV (provided + * RDTR!=0), TADV and ITR. + * Then rearm the timer. + */ + mit_delay = 0; + if (s->mit_ide && + (pending_ints & (E1000_ICR_TXQE | E1000_ICR_TXDW))) { + mit_update_delay(&mit_delay, s->mac_reg[TADV] * 4); } + if (s->mac_reg[RDTR] && (pending_ints & E1000_ICS_RXT0)) { + mit_update_delay(&mit_delay, s->mac_reg[RADV] * 4); + } + mit_update_delay(&mit_delay, s->mac_reg[ITR]); + + /* + * According to e1000 SPEC, the Ethernet controller guarantees + * a maximum observable interrupt rate of 7813 interrupts/sec. + * Thus if mit_delay < 500 then the delay should be set to the + * minimum delay possible which is 500. + */ + mit_delay = (mit_delay < 500) ? 500 : mit_delay; + + s->mit_timer_on = 1; + timer_mod(s->mit_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + mit_delay * 256); + s->mit_ide = 0; } s->mit_irq_level = (pending_ints != 0); @@ -373,9 +373,9 @@ static bool e1000_vet_init_need(void *opaque) return chkflag(VET); } -static void e1000_reset(void *opaque) +static void e1000_reset_hold(Object *obj) { - E1000State *d = opaque; + E1000State *d = E1000(obj); E1000BaseClass *edc = E1000_GET_CLASS(d); uint8_t *macaddr = d->conf.macaddr.a; @@ -386,10 +386,10 @@ static void e1000_reset(void *opaque) d->mit_irq_level = 0; d->mit_ide = 0; memset(d->phy_reg, 0, sizeof d->phy_reg); - memmove(d->phy_reg, phy_reg_init, sizeof phy_reg_init); - d->phy_reg[PHY_ID2] = edc->phy_id2; + memcpy(d->phy_reg, phy_reg_init, sizeof phy_reg_init); + d->phy_reg[MII_PHYID2] = edc->phy_id2; memset(d->mac_reg, 0, sizeof d->mac_reg); - memmove(d->mac_reg, mac_reg_init, sizeof mac_reg_init); + memcpy(d->mac_reg, mac_reg_init, sizeof mac_reg_init); d->rxbuf_min_shift = 1; memset(&d->tx, 0, sizeof d->tx); @@ -547,9 +547,9 @@ putsum(uint8_t *data, uint32_t n, uint32_t sloc, uint32_t css, uint32_t cse) static inline void inc_tx_bcast_or_mcast_count(E1000State *s, const unsigned char *arr) { - if (!memcmp(arr, bcast, sizeof bcast)) { + if (is_broadcast_ether_addr(arr)) { e1000x_inc_reg_if_not_full(s->mac_reg, BPTC); - } else if (arr[0] & 1) { + } else if (is_multicast_ether_addr(arr)) { e1000x_inc_reg_if_not_full(s->mac_reg, MPTC); } } @@ -561,13 +561,13 @@ e1000_send_packet(E1000State *s, const uint8_t *buf, int size) PTC1023, PTC1522 }; NetClientState *nc = qemu_get_queue(s->nic); - if (s->phy_reg[PHY_CTRL] & MII_CR_LOOPBACK) { + if (s->phy_reg[MII_BMCR] & MII_BMCR_LOOPBACK) { qemu_receive_packet(nc, buf, size); } else { qemu_send_packet(nc, buf, size); } inc_tx_bcast_or_mcast_count(s, buf); - e1000x_increase_size_stats(s->mac_reg, PTCregs, size); + e1000x_increase_size_stats(s->mac_reg, PTCregs, size + 4); } static void @@ -631,10 +631,9 @@ xmit_seg(E1000State *s) } e1000x_inc_reg_if_not_full(s->mac_reg, TPT); - e1000x_grow_8reg_if_not_full(s->mac_reg, TOTL, s->tx.size); - s->mac_reg[GPTC] = s->mac_reg[TPT]; - s->mac_reg[GOTCL] = s->mac_reg[TOTL]; - s->mac_reg[GOTCH] = s->mac_reg[TOTH]; + e1000x_grow_8reg_if_not_full(s->mac_reg, TOTL, s->tx.size + 4); + e1000x_inc_reg_if_not_full(s->mac_reg, GPTC); + e1000x_grow_8reg_if_not_full(s->mac_reg, GOTCL, s->tx.size + 4); } static void @@ -800,35 +799,11 @@ start_xmit(E1000State *s) } static int -receive_filter(E1000State *s, const uint8_t *buf, int size) +receive_filter(E1000State *s, const void *buf) { - uint32_t rctl = s->mac_reg[RCTL]; - int isbcast = !memcmp(buf, bcast, sizeof bcast), ismcast = (buf[0] & 1); - - if (e1000x_is_vlan_packet(buf, le16_to_cpu(s->mac_reg[VET])) && - e1000x_vlan_rx_filter_enabled(s->mac_reg)) { - uint16_t vid = lduw_be_p(buf + 14); - uint32_t vfta = ldl_le_p((uint32_t*)(s->mac_reg + VFTA) + - ((vid >> 5) & 0x7f)); - if ((vfta & (1 << (vid & 0x1f))) == 0) - return 0; - } - - if (!isbcast && !ismcast && (rctl & E1000_RCTL_UPE)) { /* promiscuous ucast */ - return 1; - } - - if (ismcast && (rctl & E1000_RCTL_MPE)) { /* promiscuous mcast */ - e1000x_inc_reg_if_not_full(s->mac_reg, MPRC); - return 1; - } - - if (isbcast && (rctl & E1000_RCTL_BAM)) { /* broadcast enabled */ - e1000x_inc_reg_if_not_full(s->mac_reg, BPRC); - return 1; - } - - return e1000x_rx_group_filter(s->mac_reg, buf); + return (!e1000x_is_vlan_packet(buf, s->mac_reg[VET]) || + e1000x_rx_vlan_filter(s->mac_reg, PKT_GET_VLAN_HDR(buf))) && + e1000x_rx_group_filter(s->mac_reg, buf); } static void @@ -841,7 +816,7 @@ e1000_set_link_status(NetClientState *nc) e1000x_update_regs_on_link_down(s->mac_reg, s->phy_reg); } else { if (have_autoneg(s) && - !(s->phy_reg[PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { + !(s->phy_reg[MII_BMSR] & MII_BMSR_AN_COMP)) { e1000x_restart_autoneg(s->mac_reg, s->phy_reg, s->autoneg_timer); } else { e1000_link_up(s); @@ -907,14 +882,14 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) uint32_t rdh_start; uint16_t vlan_special = 0; uint8_t vlan_status = 0; - uint8_t min_buf[MIN_BUF_SIZE]; - struct iovec min_iov; + uint8_t min_buf[ETH_ZLEN]; uint8_t *filter_buf = iov->iov_base; size_t size = iov_size(iov, iovcnt); size_t iov_ofs = 0; size_t desc_offset; size_t desc_size; size_t total_size; + eth_pkt_types_e pkt_type; if (!e1000x_hw_rx_enabled(s->mac_reg)) { return -1; @@ -924,15 +899,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) return 0; } - /* Pad to minimum Ethernet frame length */ - if (size < sizeof(min_buf)) { - iov_to_buf(iov, iovcnt, 0, min_buf, size); - memset(&min_buf[size], 0, sizeof(min_buf) - size); - min_iov.iov_base = filter_buf = min_buf; - min_iov.iov_len = size = sizeof(min_buf); - iovcnt = 1; - iov = &min_iov; - } else if (iov->iov_len < MAXIMUM_ETHERNET_HDR_LEN) { + if (iov->iov_len < MAXIMUM_ETHERNET_HDR_LEN) { /* This is very unlikely, but may happen. */ iov_to_buf(iov, iovcnt, 0, min_buf, MAXIMUM_ETHERNET_HDR_LEN); filter_buf = min_buf; @@ -943,7 +910,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) return size; } - if (!receive_filter(s, filter_buf, size)) { + if (!receive_filter(s, filter_buf)) { return size; } @@ -964,6 +931,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) size -= 4; } + pkt_type = get_eth_packet_type(PKT_GET_ETH_HDR(filter_buf)); rdh_start = s->mac_reg[RDH]; desc_offset = 0; total_size = size + e1000x_fcs_len(s->mac_reg); @@ -979,7 +947,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) base = rx_desc_base(s) + sizeof(desc) * s->mac_reg[RDH]; pci_dma_read(d, base, &desc, sizeof(desc)); desc.special = vlan_special; - desc.status |= (vlan_status | E1000_RXD_STAT_DD); + desc.status &= ~E1000_RXD_STAT_DD; if (desc.buffer_addr) { if (desc_offset < size) { size_t iov_copy; @@ -1013,6 +981,9 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) DBGOUT(RX, "Null RX descriptor!!\n"); } pci_dma_write(d, base, &desc, sizeof(desc)); + desc.status |= (vlan_status | E1000_RXD_STAT_DD); + pci_dma_write(d, base + offsetof(struct e1000_rx_desc, status), + &desc.status, sizeof(desc.status)); if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN]) s->mac_reg[RDH] = 0; @@ -1026,7 +997,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) } } while (desc_offset < total_size); - e1000x_update_rx_total_stats(s->mac_reg, size, total_size); + e1000x_update_rx_total_stats(s->mac_reg, pkt_type, size, total_size); n = E1000_ICS_RXT0; if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH]) @@ -1057,30 +1028,6 @@ mac_readreg(E1000State *s, int index) return s->mac_reg[index]; } -static uint32_t -mac_low4_read(E1000State *s, int index) -{ - return s->mac_reg[index] & 0xf; -} - -static uint32_t -mac_low11_read(E1000State *s, int index) -{ - return s->mac_reg[index] & 0x7ff; -} - -static uint32_t -mac_low13_read(E1000State *s, int index) -{ - return s->mac_reg[index] & 0x1fff; -} - -static uint32_t -mac_low16_read(E1000State *s, int index) -{ - return s->mac_reg[index] & 0xffff; -} - static uint32_t mac_icr_read(E1000State *s, int index) { @@ -1133,11 +1080,17 @@ set_rdt(E1000State *s, int index, uint32_t val) } } -static void -set_16bit(E1000State *s, int index, uint32_t val) -{ - s->mac_reg[index] = val & 0xffff; -} +#define LOW_BITS_SET_FUNC(num) \ + static void \ + set_##num##bit(E1000State *s, int index, uint32_t val) \ + { \ + s->mac_reg[index] = val & (BIT(num) - 1); \ + } + +LOW_BITS_SET_FUNC(4) +LOW_BITS_SET_FUNC(11) +LOW_BITS_SET_FUNC(13) +LOW_BITS_SET_FUNC(16) static void set_dlen(E1000State *s, int index, uint32_t val) @@ -1191,7 +1144,9 @@ static const readops macreg_readops[] = { getreg(XONRXC), getreg(XONTXC), getreg(XOFFRXC), getreg(XOFFTXC), getreg(RFC), getreg(RJC), getreg(RNBC), getreg(TSCTFC), getreg(MGTPRC), getreg(MGTPDC), getreg(MGTPTC), getreg(GORCL), - getreg(GOTCL), + getreg(GOTCL), getreg(RDFH), getreg(RDFT), getreg(RDFHS), + getreg(RDFTS), getreg(RDFPC), getreg(TDFH), getreg(TDFT), + getreg(TDFHS), getreg(TDFTS), getreg(TDFPC), getreg(AIT), [TOTH] = mac_read_clr8, [TORH] = mac_read_clr8, [GOTCH] = mac_read_clr8, [GORCH] = mac_read_clr8, @@ -1209,24 +1164,17 @@ static const readops macreg_readops[] = { [MPTC] = mac_read_clr4, [ICR] = mac_icr_read, [EECD] = get_eecd, [EERD] = flash_eerd_read, - [RDFH] = mac_low13_read, [RDFT] = mac_low13_read, - [RDFHS] = mac_low13_read, [RDFTS] = mac_low13_read, - [RDFPC] = mac_low13_read, - [TDFH] = mac_low11_read, [TDFT] = mac_low11_read, - [TDFHS] = mac_low13_read, [TDFTS] = mac_low13_read, - [TDFPC] = mac_low13_read, - [AIT] = mac_low16_read, - [CRCERRS ... MPC] = &mac_readreg, - [IP6AT ... IP6AT+3] = &mac_readreg, [IP4AT ... IP4AT+6] = &mac_readreg, - [FFLT ... FFLT+6] = &mac_low11_read, - [RA ... RA+31] = &mac_readreg, - [WUPM ... WUPM+31] = &mac_readreg, - [MTA ... MTA+127] = &mac_readreg, - [VFTA ... VFTA+127] = &mac_readreg, - [FFMT ... FFMT+254] = &mac_low4_read, - [FFVT ... FFVT+254] = &mac_readreg, - [PBM ... PBM+16383] = &mac_readreg, + [CRCERRS ... MPC] = &mac_readreg, + [IP6AT ... IP6AT + 3] = &mac_readreg, [IP4AT ... IP4AT + 6] = &mac_readreg, + [FFLT ... FFLT + 6] = &mac_readreg, + [RA ... RA + 31] = &mac_readreg, + [WUPM ... WUPM + 31] = &mac_readreg, + [MTA ... MTA + E1000_MC_TBL_SIZE - 1] = &mac_readreg, + [VFTA ... VFTA + E1000_VLAN_FILTER_TBL_SIZE - 1] = &mac_readreg, + [FFMT ... FFMT + 254] = &mac_readreg, + [FFVT ... FFVT + 254] = &mac_readreg, + [PBM ... PBM + 16383] = &mac_readreg, }; enum { NREADOPS = ARRAY_SIZE(macreg_readops) }; @@ -1236,27 +1184,28 @@ 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), - putreg(TDFH), putreg(TDFT), putreg(TDFHS), putreg(TDFTS), - putreg(TDFPC), putreg(RDFH), putreg(RDFT), putreg(RDFHS), - putreg(RDFTS), putreg(RDFPC), putreg(IPAV), putreg(WUC), - putreg(WUS), putreg(AIT), + putreg(IPAV), putreg(WUC), + putreg(WUS), - [TDLEN] = set_dlen, [RDLEN] = set_dlen, [TCTL] = set_tctl, - [TDT] = set_tctl, [MDIC] = set_mdic, [ICS] = set_ics, - [TDH] = set_16bit, [RDH] = set_16bit, [RDT] = set_rdt, - [IMC] = set_imc, [IMS] = set_ims, [ICR] = set_icr, - [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl, - [RDTR] = set_16bit, [RADV] = set_16bit, [TADV] = set_16bit, - [ITR] = set_16bit, + [TDLEN] = set_dlen, [RDLEN] = set_dlen, [TCTL] = set_tctl, + [TDT] = set_tctl, [MDIC] = set_mdic, [ICS] = set_ics, + [TDH] = set_16bit, [RDH] = set_16bit, [RDT] = set_rdt, + [IMC] = set_imc, [IMS] = set_ims, [ICR] = set_icr, + [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl, + [RDTR] = set_16bit, [RADV] = set_16bit, [TADV] = set_16bit, + [ITR] = set_16bit, [TDFH] = set_11bit, [TDFT] = set_11bit, + [TDFHS] = set_13bit, [TDFTS] = set_13bit, [TDFPC] = set_13bit, + [RDFH] = set_13bit, [RDFT] = set_13bit, [RDFHS] = set_13bit, + [RDFTS] = set_13bit, [RDFPC] = set_13bit, [AIT] = set_16bit, - [IP6AT ... IP6AT+3] = &mac_writereg, [IP4AT ... IP4AT+6] = &mac_writereg, - [FFLT ... FFLT+6] = &mac_writereg, - [RA ... RA+31] = &mac_writereg, - [WUPM ... WUPM+31] = &mac_writereg, - [MTA ... MTA+127] = &mac_writereg, - [VFTA ... VFTA+127] = &mac_writereg, - [FFMT ... FFMT+254] = &mac_writereg, [FFVT ... FFVT+254] = &mac_writereg, - [PBM ... PBM+16383] = &mac_writereg, + [IP6AT ... IP6AT + 3] = &mac_writereg, [IP4AT ... IP4AT + 6] = &mac_writereg, + [FFLT ... FFLT + 6] = &set_11bit, + [RA ... RA + 31] = &mac_writereg, + [WUPM ... WUPM + 31] = &mac_writereg, + [MTA ... MTA + E1000_MC_TBL_SIZE - 1] = &mac_writereg, + [VFTA ... VFTA + E1000_VLAN_FILTER_TBL_SIZE - 1] = &mac_writereg, + [FFMT ... FFMT + 254] = &set_4bit, [FFVT ... FFVT + 254] = &mac_writereg, + [PBM ... PBM + 16383] = &mac_writereg, }; enum { NWRITEOPS = ARRAY_SIZE(macreg_writeops) }; @@ -1269,9 +1218,6 @@ enum { MAC_ACCESS_PARTIAL = 1, MAC_ACCESS_FLAG_NEEDED = 2 }; * n - flag needed * p - partially implenented */ static const uint8_t mac_reg_access[0x8000] = { - [RDTR] = markflag(MIT), [TADV] = markflag(MIT), - [RADV] = markflag(MIT), [ITR] = markflag(MIT), - [IPAV] = markflag(MAC), [WUC] = markflag(MAC), [IP6AT] = markflag(MAC), [IP4AT] = markflag(MAC), [FFVT] = markflag(MAC), [WUPM] = markflag(MAC), @@ -1412,10 +1358,10 @@ static int e1000_pre_save(void *opaque) /* * If link is down and auto-negotiation is supported and ongoing, * complete auto-negotiation immediately. This allows us to look - * at MII_SR_AUTONEG_COMPLETE to infer link status on load. + * at MII_BMSR_AN_COMP to infer link status on load. */ if (nc->link_down && have_autoneg(s)) { - s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; + s->phy_reg[MII_BMSR] |= MII_BMSR_AN_COMP; } /* Decide which set of props to migrate in the main structure */ @@ -1440,11 +1386,6 @@ static int e1000_post_load(void *opaque, int version_id) E1000State *s = opaque; NetClientState *nc = qemu_get_queue(s->nic); - if (!chkflag(MIT)) { - s->mac_reg[ITR] = s->mac_reg[RDTR] = s->mac_reg[RADV] = - s->mac_reg[TADV] = 0; - s->mit_irq_level = false; - } s->mit_ide = 0; s->mit_timer_on = true; timer_mod(s->mit_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 1); @@ -1454,8 +1395,7 @@ static int e1000_post_load(void *opaque, int version_id) * Alternatively, restart link negotiation if it was in progress. */ nc->link_down = (s->mac_reg[STATUS] & E1000_STATUS_LU) == 0; - if (have_autoneg(s) && - !(s->phy_reg[PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { + if (have_autoneg(s) && !(s->phy_reg[MII_BMSR] & MII_BMSR_AN_COMP)) { nc->link_down = false; timer_mod(s->autoneg_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); @@ -1479,13 +1419,6 @@ static int e1000_tx_tso_post_load(void *opaque, int version_id) return 0; } -static bool e1000_mit_state_needed(void *opaque) -{ - E1000State *s = opaque; - - return chkflag(MIT); -} - static bool e1000_full_mac_needed(void *opaque) { E1000State *s = opaque; @@ -1504,8 +1437,7 @@ static const VMStateDescription vmstate_e1000_mit_state = { .name = "e1000/mit_state", .version_id = 1, .minimum_version_id = 1, - .needed = e1000_mit_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(mac_reg[RDTR], E1000State), VMSTATE_UINT32(mac_reg[RADV], E1000State), VMSTATE_UINT32(mac_reg[TADV], E1000State), @@ -1520,7 +1452,7 @@ static const VMStateDescription vmstate_e1000_full_mac_state = { .version_id = 1, .minimum_version_id = 1, .needed = e1000_full_mac_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(mac_reg, E1000State, 0x8000), VMSTATE_END_OF_LIST() } @@ -1532,7 +1464,7 @@ static const VMStateDescription vmstate_e1000_tx_tso_state = { .minimum_version_id = 1, .needed = e1000_tso_state_needed, .post_load = e1000_tx_tso_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(tx.tso_props.ipcss, E1000State), VMSTATE_UINT8(tx.tso_props.ipcso, E1000State), VMSTATE_UINT16(tx.tso_props.ipcse, E1000State), @@ -1554,7 +1486,7 @@ static const VMStateDescription vmstate_e1000 = { .minimum_version_id = 1, .pre_save = e1000_pre_save, .post_load = e1000_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, E1000State), VMSTATE_UNUSED_TEST(is_version_1, 4), /* was instance id */ VMSTATE_UNUSED(4), /* Was mmio_base. */ @@ -1621,11 +1553,12 @@ static const VMStateDescription vmstate_e1000 = { VMSTATE_UINT32(mac_reg[WUFC], E1000State), VMSTATE_UINT32(mac_reg[VET], E1000State), VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, RA, 32), - VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, MTA, 128), - VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, VFTA, 128), + VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, MTA, E1000_MC_TBL_SIZE), + VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, VFTA, + E1000_VLAN_FILTER_TBL_SIZE), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_e1000_mit_state, &vmstate_e1000_full_mac_state, &vmstate_e1000_tx_tso_state, @@ -1733,7 +1666,8 @@ static void pci_e1000_realize(PCIDevice *pci_dev, Error **errp) macaddr); d->nic = qemu_new_nic(&net_e1000_info, &d->conf, - object_get_typename(OBJECT(d)), dev->id, d); + object_get_typename(OBJECT(d)), dev->id, + &dev->mem_reentrancy_guard, d); qemu_format_nic_info_str(qemu_get_queue(d->nic), macaddr); @@ -1743,18 +1677,8 @@ static void pci_e1000_realize(PCIDevice *pci_dev, Error **errp) e1000_flush_queue_timer, d); } -static void qdev_e1000_reset(DeviceState *dev) -{ - E1000State *d = E1000(dev); - e1000_reset(d); -} - static Property e1000_properties[] = { DEFINE_NIC_PROPERTIES(E1000State, conf), - DEFINE_PROP_BIT("autonegotiation", E1000State, - compat_flags, E1000_FLAG_AUTONEG_BIT, true), - DEFINE_PROP_BIT("mitigation", E1000State, - compat_flags, E1000_FLAG_MIT_BIT, true), DEFINE_PROP_BIT("extra_mac_registers", E1000State, compat_flags, E1000_FLAG_MAC_BIT, true), DEFINE_PROP_BIT("migrate_tso_props", E1000State, @@ -1774,6 +1698,7 @@ typedef struct E1000Info { static void e1000_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); E1000BaseClass *e = E1000_CLASS(klass); const E1000Info *info = data; @@ -1786,9 +1711,9 @@ static void e1000_class_init(ObjectClass *klass, void *data) k->revision = info->revision; e->phy_id2 = info->phy_id2; k->class_id = PCI_CLASS_NETWORK_ETHERNET; + rc->phases.hold = e1000_reset_hold; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->desc = "Intel Gigabit Ethernet"; - dc->reset = qdev_e1000_reset; dc->vmsd = &vmstate_e1000; device_class_set_props(dc, e1000_properties); } diff --git a/hw/net/e1000_common.h b/hw/net/e1000_common.h new file mode 100644 index 0000000000..48feda7404 --- /dev/null +++ b/hw/net/e1000_common.h @@ -0,0 +1,102 @@ +/* + * QEMU e1000(e) emulation - shared definitions + * + * Copyright (c) 2008 Qumranet + * + * Based on work done by: + * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. + * Copyright (c) 2007 Dan Aloni + * Copyright (c) 2004 Antony T Curtis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 HW_NET_E1000_COMMON_H +#define HW_NET_E1000_COMMON_H + +#include "e1000_regs.h" + +#define defreg(x) x = (E1000_##x >> 2) +enum { + defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC), + defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC), + defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC), + defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH0), + defreg(RDBAL0), defreg(RDH0), defreg(RDLEN0), defreg(RDT0), + defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH), + defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT), + defreg(TDLEN1), defreg(TDBAL1), defreg(TDBAH1), defreg(TDH1), + defreg(TDT1), defreg(TORH), defreg(TORL), defreg(TOTH), + defreg(TOTL), defreg(TPR), defreg(TPT), defreg(TXDCTL), + defreg(WUFC), defreg(RA), defreg(MTA), defreg(CRCERRS), + defreg(VFTA), defreg(VET), defreg(RDTR), defreg(RADV), + defreg(TADV), defreg(ITR), defreg(SCC), defreg(ECOL), + defreg(MCC), defreg(LATECOL), defreg(COLC), defreg(DC), + defreg(TNCRS), defreg(SEQEC), defreg(CEXTERR), defreg(RLEC), + defreg(XONRXC), defreg(XONTXC), defreg(XOFFRXC), defreg(XOFFTXC), + defreg(FCRUC), defreg(AIT), defreg(TDFH), defreg(TDFT), + defreg(TDFHS), defreg(TDFTS), defreg(TDFPC), defreg(WUC), + defreg(WUS), defreg(POEMB), defreg(PBS), defreg(RDFH), + defreg(RDFT), defreg(RDFHS), defreg(RDFTS), defreg(RDFPC), + defreg(PBM), defreg(IPAV), defreg(IP4AT), defreg(IP6AT), + defreg(WUPM), defreg(FFLT), defreg(FFMT), defreg(FFVT), + defreg(TARC0), defreg(TARC1), defreg(IAM), defreg(EXTCNF_CTRL), + defreg(GCR), defreg(TIMINCA), defreg(EIAC), defreg(CTRL_EXT), + defreg(IVAR), defreg(MFUTP01), defreg(MFUTP23), defreg(MANC2H), + defreg(MFVAL), defreg(MDEF), defreg(FACTPS), defreg(FTFT), + defreg(RUC), defreg(ROC), defreg(RFC), defreg(RJC), + defreg(PRC64), defreg(PRC127), defreg(PRC255), defreg(PRC511), + defreg(PRC1023), defreg(PRC1522), defreg(PTC64), defreg(PTC127), + defreg(PTC255), defreg(PTC511), defreg(PTC1023), defreg(PTC1522), + defreg(GORCL), defreg(GORCH), defreg(GOTCL), defreg(GOTCH), + defreg(RNBC), defreg(BPRC), defreg(MPRC), defreg(RFCTL), + defreg(PSRCTL), defreg(MPTC), defreg(BPTC), defreg(TSCTFC), + defreg(IAC), defreg(MGTPRC), defreg(MGTPDC), defreg(MGTPTC), + defreg(TSCTC), defreg(RXCSUM), defreg(FUNCTAG), defreg(GSCL_1), + defreg(GSCL_2), defreg(GSCL_3), defreg(GSCL_4), defreg(GSCN_0), + defreg(GSCN_1), defreg(GSCN_2), defreg(GSCN_3), defreg(GCR2), + defreg(RAID), defreg(RSRPD), defreg(TIDV), defreg(EITR), + defreg(MRQC), defreg(RETA), defreg(RSSRK), defreg(RDBAH1), + defreg(RDBAL1), defreg(RDLEN1), defreg(RDH1), defreg(RDT1), + defreg(PBACLR), defreg(FCAL), defreg(FCAH), defreg(FCT), + defreg(FCRTH), defreg(FCRTL), defreg(FCTTV), defreg(FCRTV), + defreg(FLA), defreg(EEWR), defreg(FLOP), defreg(FLOL), + defreg(FLSWCTL), defreg(FLSWCNT), defreg(RXDCTL), defreg(RXDCTL1), + defreg(MAVTV0), defreg(MAVTV1), defreg(MAVTV2), defreg(MAVTV3), + defreg(TXSTMPL), defreg(TXSTMPH), defreg(SYSTIML), defreg(SYSTIMH), + defreg(RXCFGL), defreg(RXUDP), defreg(TIMADJL), defreg(TIMADJH), + defreg(RXSTMPH), defreg(RXSTMPL), defreg(RXSATRL), defreg(RXSATRH), + defreg(FLASHT), defreg(TIPG), defreg(RDH), defreg(RDT), + defreg(RDLEN), defreg(RDBAH), defreg(RDBAL), + defreg(TXDCTL1), + defreg(FLSWDATA), + defreg(CTRL_DUP), + defreg(EXTCNF_SIZE), + defreg(EEMNGCTL), + defreg(EEMNGDATA), + defreg(FLMNGCTL), + defreg(FLMNGDATA), + defreg(FLMNGCNT), + defreg(TSYNCRXCTL), + defreg(TSYNCTXCTL), + + /* Aliases */ + defreg(RDH0_A), defreg(RDT0_A), defreg(RDTR_A), defreg(RDFH_A), + defreg(RDFT_A), defreg(TDH_A), defreg(TDT_A), defreg(TIDV_A), + defreg(TDFH_A), defreg(TDFT_A), defreg(RA_A), defreg(RDBAL0_A), + defreg(TDBAL_A), defreg(TDLEN_A), defreg(VFTA_A), defreg(RDLEN0_A), + defreg(FCRTL_A), defreg(FCRTH_A) +}; + +#endif diff --git a/hw/net/e1000_regs.h b/hw/net/e1000_regs.h index ae99f58bab..39f4882510 100644 --- a/hw/net/e1000_regs.h +++ b/hw/net/e1000_regs.h @@ -32,157 +32,35 @@ #ifndef HW_E1000_REGS_H #define HW_E1000_REGS_H -/* PCI Device IDs */ -#define E1000_DEV_ID_82542 0x1000 -#define E1000_DEV_ID_82543GC_FIBER 0x1001 -#define E1000_DEV_ID_82543GC_COPPER 0x1004 -#define E1000_DEV_ID_82544EI_COPPER 0x1008 -#define E1000_DEV_ID_82544EI_FIBER 0x1009 -#define E1000_DEV_ID_82544GC_COPPER 0x100C -#define E1000_DEV_ID_82544GC_LOM 0x100D -#define E1000_DEV_ID_82540EM 0x100E -#define E1000_DEV_ID_82540EM_LOM 0x1015 -#define E1000_DEV_ID_82540EP_LOM 0x1016 -#define E1000_DEV_ID_82540EP 0x1017 -#define E1000_DEV_ID_82540EP_LP 0x101E -#define E1000_DEV_ID_82545EM_COPPER 0x100F -#define E1000_DEV_ID_82545EM_FIBER 0x1011 -#define E1000_DEV_ID_82545GM_COPPER 0x1026 -#define E1000_DEV_ID_82545GM_FIBER 0x1027 -#define E1000_DEV_ID_82545GM_SERDES 0x1028 -#define E1000_DEV_ID_82546EB_COPPER 0x1010 -#define E1000_DEV_ID_82546EB_FIBER 0x1012 -#define E1000_DEV_ID_82546EB_QUAD_COPPER 0x101D -#define E1000_DEV_ID_82541EI 0x1013 -#define E1000_DEV_ID_82541EI_MOBILE 0x1018 -#define E1000_DEV_ID_82541ER_LOM 0x1014 -#define E1000_DEV_ID_82541ER 0x1078 -#define E1000_DEV_ID_82547GI 0x1075 -#define E1000_DEV_ID_82541GI 0x1076 -#define E1000_DEV_ID_82541GI_MOBILE 0x1077 -#define E1000_DEV_ID_82541GI_LF 0x107C -#define E1000_DEV_ID_82546GB_COPPER 0x1079 -#define E1000_DEV_ID_82546GB_FIBER 0x107A -#define E1000_DEV_ID_82546GB_SERDES 0x107B -#define E1000_DEV_ID_82546GB_PCIE 0x108A -#define E1000_DEV_ID_82546GB_QUAD_COPPER 0x1099 -#define E1000_DEV_ID_82547EI 0x1019 -#define E1000_DEV_ID_82547EI_MOBILE 0x101A -#define E1000_DEV_ID_82571EB_COPPER 0x105E -#define E1000_DEV_ID_82571EB_FIBER 0x105F -#define E1000_DEV_ID_82571EB_SERDES 0x1060 -#define E1000_DEV_ID_82571EB_QUAD_COPPER 0x10A4 -#define E1000_DEV_ID_82571PT_QUAD_COPPER 0x10D5 -#define E1000_DEV_ID_82571EB_QUAD_FIBER 0x10A5 -#define E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE 0x10BC -#define E1000_DEV_ID_82571EB_SERDES_DUAL 0x10D9 -#define E1000_DEV_ID_82571EB_SERDES_QUAD 0x10DA -#define E1000_DEV_ID_82572EI_COPPER 0x107D -#define E1000_DEV_ID_82572EI_FIBER 0x107E -#define E1000_DEV_ID_82572EI_SERDES 0x107F -#define E1000_DEV_ID_82572EI 0x10B9 -#define E1000_DEV_ID_82573E 0x108B -#define E1000_DEV_ID_82573E_IAMT 0x108C -#define E1000_DEV_ID_82573L 0x109A -#define E1000_DEV_ID_82574L 0x10D3 -#define E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3 0x10B5 -#define E1000_DEV_ID_80003ES2LAN_COPPER_DPT 0x1096 -#define E1000_DEV_ID_80003ES2LAN_SERDES_DPT 0x1098 -#define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA -#define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB +#include "e1000x_regs.h" -#define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049 -#define E1000_DEV_ID_ICH8_IGP_AMT 0x104A -#define E1000_DEV_ID_ICH8_IGP_C 0x104B -#define E1000_DEV_ID_ICH8_IFE 0x104C -#define E1000_DEV_ID_ICH8_IFE_GT 0x10C4 -#define E1000_DEV_ID_ICH8_IFE_G 0x10C5 -#define E1000_DEV_ID_ICH8_IGP_M 0x104D - -/* Device Specific Register Defaults */ -#define E1000_PHY_ID2_82541x 0x380 -#define E1000_PHY_ID2_82544x 0xC30 -#define E1000_PHY_ID2_8254xx_DEFAULT 0xC20 /* 82540x, 82545x, and 82546x */ -#define E1000_PHY_ID2_82573x 0xCC0 -#define E1000_PHY_ID2_82574x 0xCB1 - -/* Register Set. (82543, 82544) - * - * Registers are defined to be 32 bits and should be accessed as 32 bit values. - * These registers are physically located on the NIC, but are mapped into the - * host memory address space. - * - * RW - register is both readable and writable - * RO - register is read only - * WO - register is write only - * R/clr - register is read only and is cleared when read - * A - register array - */ -#define E1000_CTRL 0x00000 /* Device Control - RW */ -#define E1000_CTRL_DUP 0x00004 /* Device Control Duplicate (Shadow) - RW */ -#define E1000_STATUS 0x00008 /* Device Status - RO */ -#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ -#define E1000_EERD 0x00014 /* EEPROM Read - RW */ -#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ -#define E1000_FLA 0x0001C /* Flash Access - RW */ -#define E1000_MDIC 0x00020 /* MDI Control - RW */ -#define E1000_SCTL 0x00024 /* SerDes Control - RW */ -#define E1000_FEXTNVM 0x00028 /* Future Extended NVM register */ -#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ -#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ -#define E1000_FCT 0x00030 /* Flow Control Type - RW */ -#define E1000_VET 0x00038 /* VLAN Ether Type - RW */ -#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */ #define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */ -#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ -#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ #define E1000_EIAC 0x000DC /* Ext. Interrupt Auto Clear - RW */ -#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ -#define E1000_IAM 0x000E0 /* Interrupt Acknowledge Auto Mask */ #define E1000_IVAR 0x000E4 /* Interrupt Vector Allocation Register - RW */ #define E1000_EITR 0x000E8 /* Extended Interrupt Throttling Rate - RW */ -#define E1000_RCTL 0x00100 /* RX Control - RW */ -#define E1000_RDTR1 0x02820 /* RX Delay Timer (1) - RW */ #define E1000_RDBAL1 0x02900 /* RX Descriptor Base Address Low (1) - RW */ #define E1000_RDBAH1 0x02904 /* RX Descriptor Base Address High (1) - RW */ #define E1000_RDLEN1 0x02908 /* RX Descriptor Length (1) - RW */ #define E1000_RDH1 0x02910 /* RX Descriptor Head (1) - RW */ #define E1000_RDT1 0x02918 /* RX Descriptor Tail (1) - RW */ -#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */ #define E1000_FCRTV 0x05F40 /* Flow Control Refresh Timer Value - RW */ #define E1000_TXCW 0x00178 /* TX Configuration Word - RW */ #define E1000_RXCW 0x00180 /* RX Configuration Word - RO */ -#define E1000_TCTL 0x00400 /* TX Control - RW */ -#define E1000_TCTL_EXT 0x00404 /* Extended TX Control - RW */ -#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ #define E1000_TBT 0x00448 /* TX Burst Timer - RW */ #define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */ -#define E1000_LEDCTL 0x00E00 /* LED Control - RW */ #define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */ #define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */ #define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */ -#define FEXTNVM_SW_CONFIG 0x0001 #define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ #define E1000_PBM 0x10000 /* Packet Buffer Memory - RW */ #define E1000_PBS 0x01008 /* Packet Buffer Size - RW */ -#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ -#define E1000_EEMNGDATA 0x01014 /* MNG EEPROM Read/Write data */ -#define E1000_FLMNGCTL 0x01018 /* MNG Flash Control */ -#define E1000_FLMNGDATA 0x0101C /* MNG FLASH Read data */ -#define E1000_FLMNGCNT 0x01020 /* MNG FLASH Read Counter */ -#define E1000_FLASH_UPDATES 1000 -#define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */ #define E1000_FLASHT 0x01028 /* FLASH Timer Register */ #define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ #define E1000_FLSWCTL 0x01030 /* FLASH control register */ #define E1000_FLSWDATA 0x01034 /* FLASH data register */ #define E1000_FLSWCNT 0x01038 /* FLASH Access Counter */ -#define E1000_FLOP 0x0103C /* FLASH Opcode Register */ #define E1000_FLOL 0x01050 /* FEEP Auto Load */ #define E1000_ERT 0x02008 /* Early Rx Threshold - RW */ -#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */ -#define E1000_FCRTL_A 0x00168 /* Alias to FCRTL */ -#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */ #define E1000_FCRTH_A 0x00160 /* Alias to FCRTH */ #define E1000_PSRCTL 0x02170 /* Packet Split Receive Control - RW */ #define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ @@ -208,23 +86,7 @@ #define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - RW */ #define E1000_RSRPD 0x02C00 /* RX Small Packet Detect - RW */ #define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */ -#define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */ -#define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */ #define E1000_POEMB 0x00F10 /* PHY OEM Bits Register - RW */ -#define E1000_RDFH 0x02410 /* Receive Data FIFO Head Register - RW */ -#define E1000_RDFH_A 0x08000 /* Alias to RDFH */ -#define E1000_RDFT 0x02418 /* Receive Data FIFO Tail Register - RW */ -#define E1000_RDFT_A 0x08008 /* Alias to RDFT */ -#define E1000_RDFHS 0x02420 /* Receive Data FIFO Head Saved Register - RW */ -#define E1000_RDFTS 0x02428 /* Receive Data FIFO Tail Saved Register - RW */ -#define E1000_RDFPC 0x02430 /* Receive Data FIFO Packet Count - RW */ -#define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */ -#define E1000_TDFH_A 0x08010 /* Alias to TDFH */ -#define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */ -#define E1000_TDFT_A 0x08018 /* Alias to TDFT */ -#define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */ -#define E1000_TDFTS 0x03428 /* TX Data FIFO Tail Saved - RW */ -#define E1000_TDFPC 0x03430 /* TX Data FIFO Packet Count - RW */ #define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ #define E1000_TDBAL_A 0x00420 /* Alias to TDBAL */ #define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */ @@ -248,174 +110,40 @@ #define E1000_TDT1 0x03918 /* TX Desc Tail (1) - RW */ #define E1000_TXDCTL1 0x03928 /* TX Descriptor Control (1) - RW */ #define E1000_TARC1 0x03940 /* TX Arbitration Count (1) */ -#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */ -#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */ -#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */ -#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */ -#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */ -#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */ -#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */ -#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */ -#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */ -#define E1000_COLC 0x04028 /* Collision Count - R/clr */ -#define E1000_DC 0x04030 /* Defer Count - R/clr */ -#define E1000_TNCRS 0x04034 /* TX-No CRS - R/clr */ #define E1000_SEQEC 0x04038 /* Sequence Error Count - R/clr */ #define E1000_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */ -#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */ -#define E1000_XONRXC 0x04048 /* XON RX Count - R/clr */ -#define E1000_XONTXC 0x0404C /* XON TX Count - R/clr */ -#define E1000_XOFFRXC 0x04050 /* XOFF RX Count - R/clr */ -#define E1000_XOFFTXC 0x04054 /* XOFF TX Count - R/clr */ -#define E1000_FCRUC 0x04058 /* Flow Control RX Unsupported Count- R/clr */ -#define E1000_PRC64 0x0405C /* Packets RX (64 bytes) - R/clr */ -#define E1000_PRC127 0x04060 /* Packets RX (65-127 bytes) - R/clr */ -#define E1000_PRC255 0x04064 /* Packets RX (128-255 bytes) - R/clr */ -#define E1000_PRC511 0x04068 /* Packets RX (255-511 bytes) - R/clr */ -#define E1000_PRC1023 0x0406C /* Packets RX (512-1023 bytes) - R/clr */ -#define E1000_PRC1522 0x04070 /* Packets RX (1024-1522 bytes) - R/clr */ -#define E1000_GPRC 0x04074 /* Good Packets RX Count - R/clr */ -#define E1000_BPRC 0x04078 /* Broadcast Packets RX Count - R/clr */ -#define E1000_MPRC 0x0407C /* Multicast Packets RX Count - R/clr */ -#define E1000_GPTC 0x04080 /* Good Packets TX Count - R/clr */ -#define E1000_GORCL 0x04088 /* Good Octets RX Count Low - R/clr */ -#define E1000_GORCH 0x0408C /* Good Octets RX Count High - R/clr */ -#define E1000_GOTCL 0x04090 /* Good Octets TX Count Low - R/clr */ -#define E1000_GOTCH 0x04094 /* Good Octets TX Count High - R/clr */ -#define E1000_RNBC 0x040A0 /* RX No Buffers Count - R/clr */ -#define E1000_RUC 0x040A4 /* RX Undersize Count - R/clr */ -#define E1000_RFC 0x040A8 /* RX Fragment Count - R/clr */ -#define E1000_ROC 0x040AC /* RX Oversize Count - R/clr */ -#define E1000_RJC 0x040B0 /* RX Jabber Count - R/clr */ -#define E1000_MGTPRC 0x040B4 /* Management Packets RX Count - R/clr */ -#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */ -#define E1000_MGTPTC 0x040BC /* Management Packets TX Count - R/clr */ -#define E1000_TORL 0x040C0 /* Total Octets RX Low - R/clr */ -#define E1000_TORH 0x040C4 /* Total Octets RX High - R/clr */ -#define E1000_TOTL 0x040C8 /* Total Octets TX Low - R/clr */ -#define E1000_TOTH 0x040CC /* Total Octets TX High - R/clr */ -#define E1000_TPR 0x040D0 /* Total Packets RX - R/clr */ -#define E1000_TPT 0x040D4 /* Total Packets TX - R/clr */ -#define E1000_PTC64 0x040D8 /* Packets TX (64 bytes) - R/clr */ -#define E1000_PTC127 0x040DC /* Packets TX (65-127 bytes) - R/clr */ -#define E1000_PTC255 0x040E0 /* Packets TX (128-255 bytes) - R/clr */ -#define E1000_PTC511 0x040E4 /* Packets TX (256-511 bytes) - R/clr */ -#define E1000_PTC1023 0x040E8 /* Packets TX (512-1023 bytes) - R/clr */ -#define E1000_PTC1522 0x040EC /* Packets TX (1024-1522 Bytes) - R/clr */ -#define E1000_MPTC 0x040F0 /* Multicast Packets TX Count - R/clr */ -#define E1000_BPTC 0x040F4 /* Broadcast Packets TX Count - R/clr */ -#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context TX - R/clr */ #define E1000_TSCTFC 0x040FC /* TCP Segmentation Context TX Fail - R/clr */ -#define E1000_IAC 0x04100 /* Interrupt Assertion Count */ -#define E1000_ICRXPTC 0x04104 /* Interrupt Cause Rx Packet Timer Expire Count */ #define E1000_ICRXATC 0x04108 /* Interrupt Cause Rx Absolute Timer Expire Count */ #define E1000_ICTXPTC 0x0410C /* Interrupt Cause Tx Packet Timer Expire Count */ #define E1000_ICTXATC 0x04110 /* Interrupt Cause Tx Absolute Timer Expire Count */ #define E1000_ICTXQEC 0x04118 /* Interrupt Cause Tx Queue Empty Count */ #define E1000_ICTXQMTC 0x0411C /* Interrupt Cause Tx Queue Minimum Threshold Count */ -#define E1000_ICRXDMTC 0x04120 /* Interrupt Cause Rx Descriptor Minimum Threshold Count */ #define E1000_ICRXOC 0x04124 /* Interrupt Cause Receiver Overrun Count */ -#define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */ -#define E1000_RFCTL 0x05008 /* Receive Filter Control*/ -#define E1000_MAVTV0 0x05010 /* Management VLAN TAG Value 0 */ -#define E1000_MAVTV1 0x05014 /* Management VLAN TAG Value 1 */ -#define E1000_MAVTV2 0x05018 /* Management VLAN TAG Value 2 */ -#define E1000_MAVTV3 0x0501c /* Management VLAN TAG Value 3 */ -#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ -#define E1000_RA 0x05400 /* Receive Address - RW Array */ -#define E1000_RA_A 0x00040 /* Alias to RA */ -#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */ -#define E1000_VFTA_A 0x00600 /* Alias to VFTA */ -#define E1000_WUC 0x05800 /* Wakeup Control - RW */ -#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */ -#define E1000_WUS 0x05810 /* Wakeup Status - RO */ -#define E1000_MANC 0x05820 /* Management Control - RW */ -#define E1000_IPAV 0x05838 /* IP Address Valid - RW */ -#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */ -#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */ -#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */ -#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */ #define E1000_MFUTP01 0x05828 /* Management Flex UDP/TCP Ports 0/1 - RW */ #define E1000_MFUTP23 0x05830 /* Management Flex UDP/TCP Ports 2/3 - RW */ -#define E1000_MFVAL 0x05824 /* Manageability Filters Valid - RW */ -#define E1000_MDEF 0x05890 /* Manageability Decision Filters - RW Array */ #define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */ #define E1000_HOST_IF 0x08800 /* Host Interface */ -#define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ -#define E1000_FTFT 0x09400 /* Flexible TCO Filter Table - RW Array */ #define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */ #define E1000_KUMCTRLSTA 0x00034 /* MAC-PHY interface - RW */ #define E1000_MDPHYA 0x0003C /* PHY address - RW */ -#define E1000_MANC2H 0x05860 /* Management Control To Host - RW */ -#define E1000_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */ -#define E1000_GCR 0x05B00 /* PCI-Ex Control */ -#define E1000_FUNCTAG 0x05B08 /* Function-Tag Register */ -#define E1000_GSCL_1 0x05B10 /* PCI-Ex Statistic Control #1 */ -#define E1000_GSCL_2 0x05B14 /* PCI-Ex Statistic Control #2 */ -#define E1000_GSCL_3 0x05B18 /* PCI-Ex Statistic Control #3 */ -#define E1000_GSCL_4 0x05B1C /* PCI-Ex Statistic Control #4 */ -#define E1000_GSCN_0 0x05B20 /* 3GIO Statistic Counter Register #0 */ -#define E1000_GSCN_1 0x05B24 /* 3GIO Statistic Counter Register #1 */ -#define E1000_GSCN_2 0x05B28 /* 3GIO Statistic Counter Register #2 */ -#define E1000_GSCN_3 0x05B2C /* 3GIO Statistic Counter Register #3 */ -#define E1000_FACTPS 0x05B30 /* Function Active and Power State to MNG */ -#define E1000_SWSM 0x05B50 /* SW Semaphore */ #define E1000_GCR2 0x05B64 /* 3GIO Control Register 2 */ -#define E1000_FWSM 0x05B54 /* FW Semaphore */ -#define E1000_PBACLR 0x05B68 /* MSI-X PBA Clear */ #define E1000_FFLT_DBG 0x05F04 /* Debug Register */ -#define E1000_HICR 0x08F00 /* Host Inteface Control */ +#define E1000_HICR 0x08F00 /* Host Interface Control */ -#define E1000_TSYNCRXCTL 0x0B620 /* Rx Time Sync Control register - RW */ -#define E1000_TSYNCTXCTL 0x0B614 /* Tx Time Sync Control register - RW */ -#define E1000_TIMINCA 0x0B608 /* Increment attributes register - RW */ -#define E1000_RXSTMPL 0x0B624 /* Rx timestamp Low - RO */ -#define E1000_RXSTMPH 0x0B628 /* Rx timestamp High - RO */ -#define E1000_TXSTMPL 0x0B618 /* Tx timestamp value Low - RO */ -#define E1000_TXSTMPH 0x0B61C /* Tx timestamp value High - RO */ -#define E1000_SYSTIML 0x0B600 /* System time register Low - RO */ -#define E1000_SYSTIMH 0x0B604 /* System time register High - RO */ -#define E1000_TIMINCA 0x0B608 /* Increment attributes register - RW */ #define E1000_RXMTRL 0x0B634 /* Time sync Rx EtherType and Msg Type - RW */ #define E1000_RXUDP 0x0B638 /* Time Sync Rx UDP Port - RW */ -#define E1000_RXSATRL 0x0B62C /* Rx timestamp attribute low - RO */ -#define E1000_RXSATRH 0x0B630 /* Rx timestamp attribute high - RO */ -#define E1000_TIMADJL 0x0B60C /* Time Adjustment Offset register Low - RW */ -#define E1000_TIMADJH 0x0B610 /* Time Adjustment Offset register High - RW */ #define E1000_RXCFGL 0x0B634 /* RX Ethertype and Message Type - RW*/ -/* RSS registers */ -#define E1000_CPUVEC 0x02C10 /* CPU Vector Register - RW */ -#define E1000_MRQC 0x05818 /* Multiple Receive Control - RW */ -#define E1000_RETA 0x05C00 /* Redirection Table - RW Array */ -#define E1000_RSSRK 0x05C80 /* RSS Random Key - RW Array */ -#define E1000_RSSIM 0x05864 /* RSS Interrupt Mask */ -#define E1000_RSSIR 0x05868 /* RSS Interrupt Request */ - #define E1000_MRQC_ENABLED(mrqc) (((mrqc) & (BIT(0) | BIT(1))) == BIT(0)) -#define E1000_RETA_IDX(hash) ((hash) & (BIT(7) - 1)) -#define E1000_RETA_VAL(reta, hash) (((uint8_t *)(reta))[E1000_RETA_IDX(hash)]) +#define E1000_CPUVEC 0x02C10 /* CPU Vector Register - RW */ +#define E1000_RSSIM 0x05864 /* RSS Interrupt Mask */ +#define E1000_RSSIR 0x05868 /* RSS Interrupt Request */ + #define E1000_RSS_QUEUE(reta, hash) ((E1000_RETA_VAL(reta, hash) & BIT(7)) >> 7) -#define E1000_MRQC_EN_TCPIPV4(mrqc) ((mrqc) & BIT(16)) -#define E1000_MRQC_EN_IPV4(mrqc) ((mrqc) & BIT(17)) -#define E1000_MRQC_EN_TCPIPV6(mrqc) ((mrqc) & BIT(18)) -#define E1000_MRQC_EN_IPV6EX(mrqc) ((mrqc) & BIT(19)) -#define E1000_MRQC_EN_IPV6(mrqc) ((mrqc) & BIT(20)) - -#define E1000_MRQ_RSS_TYPE_NONE (0) -#define E1000_MRQ_RSS_TYPE_IPV4TCP (1) -#define E1000_MRQ_RSS_TYPE_IPV4 (2) -#define E1000_MRQ_RSS_TYPE_IPV6TCP (3) -#define E1000_MRQ_RSS_TYPE_IPV6EX (4) -#define E1000_MRQ_RSS_TYPE_IPV6 (5) - -#define E1000_ICR_ASSERTED BIT(31) -#define E1000_EIAC_MASK 0x01F00000 - /* [TR]DBAL and [TR]DLEN masks */ #define E1000_XDBAL_MASK (~(BIT(4) - 1)) #define E1000_XDLEN_MASK ((BIT(20) - 1) & (~(BIT(7) - 1))) @@ -444,18 +172,8 @@ #define E1000_IVAR_TX_INT_EVERY_WB BIT(31) -/* RFCTL register bits */ -#define E1000_RFCTL_ISCSI_DIS 0x00000001 -#define E1000_RFCTL_NFSW_DIS 0x00000040 -#define E1000_RFCTL_NFSR_DIS 0x00000080 -#define E1000_RFCTL_IPV6_DIS 0x00000400 -#define E1000_RFCTL_IPV6_XSUM_DIS 0x00000800 #define E1000_RFCTL_ACK_DIS 0x00001000 #define E1000_RFCTL_ACK_DATA_DIS 0x00002000 -#define E1000_RFCTL_IPFRSP_DIS 0x00004000 -#define E1000_RFCTL_EXTEN 0x00008000 -#define E1000_RFCTL_IPV6_EX_DIS 0x00010000 -#define E1000_RFCTL_NEW_IPV6_EXT_DIS 0x00020000 /* PSRCTL parsing */ #define E1000_PSRCTL_BSIZE0_MASK 0x0000007F @@ -470,24 +188,7 @@ #define E1000_PSRCTL_BUFFS_PER_DESC 4 -/* TARC* parsing */ -#define E1000_TARC_ENABLE BIT(10) - /* PHY 1000 MII Register/Bit Definitions */ -/* PHY Registers defined by IEEE */ -#define PHY_CTRL 0x00 /* Control Register */ -#define PHY_STATUS 0x01 /* Status Regiser */ -#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ -#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ -#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ -#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ -#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ -#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ -#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ -#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ -#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ -#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ - /* 82574-specific registers */ #define PHY_COPPER_CTRL1 0x10 /* Copper Specific Control Register 1 */ #define PHY_COPPER_STAT1 0x11 /* Copper Specific Status Register 1 */ @@ -539,286 +240,6 @@ #define M88E1000_PHY_VCO_REG_BIT8 0x100 /* Bits 8 & 11 are adjusted for */ #define M88E1000_PHY_VCO_REG_BIT11 0x800 /* improved BER performance */ -/* PHY Control Register */ -#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ -#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ -#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ -#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ -#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ -#define MII_CR_POWER_DOWN 0x0800 /* Power down */ -#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ -#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ -#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ -#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ - -/* PHY Status Register */ -#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ -#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ -#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ -#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ -#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ -#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ -#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ -#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ -#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ -#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ -#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ -#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ -#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ -#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ -#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ - -/* PHY Link Partner Ability Register */ -#define MII_LPAR_LPACK 0x4000 /* Acked by link partner */ - -/* Interrupt Cause Read */ -#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ -#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */ -#define E1000_ICR_LSC 0x00000004 /* Link Status Change */ -#define E1000_ICR_RXSEQ 0x00000008 /* rx sequence error */ -#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */ -#define E1000_ICR_RXO 0x00000040 /* rx overrun */ -#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */ -#define E1000_ICR_MDAC 0x00000200 /* MDIO access complete */ -#define E1000_ICR_RXCFG 0x00000400 /* RX /c/ ordered set */ -#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */ -#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */ -#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */ -#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */ -#define E1000_ICR_TXD_LOW 0x00008000 -#define E1000_ICR_SRPD 0x00010000 -#define E1000_ICR_ACK 0x00020000 /* Receive Ack frame */ -#define E1000_ICR_MNG 0x00040000 /* Manageability event */ -#define E1000_ICR_DOCK 0x00080000 /* Dock/Undock */ -#define E1000_ICR_INT_ASSERTED 0x80000000 /* If this bit asserted, the driver should claim the interrupt */ -#define E1000_ICR_RXD_FIFO_PAR0 0x00100000 /* queue 0 Rx descriptor FIFO parity error */ -#define E1000_ICR_TXD_FIFO_PAR0 0x00200000 /* queue 0 Tx descriptor FIFO parity error */ -#define E1000_ICR_HOST_ARB_PAR 0x00400000 /* host arb read buffer parity error */ -#define E1000_ICR_PB_PAR 0x00800000 /* packet buffer parity error */ -#define E1000_ICR_RXD_FIFO_PAR1 0x01000000 /* queue 1 Rx descriptor FIFO parity error */ -#define E1000_ICR_TXD_FIFO_PAR1 0x02000000 /* queue 1 Tx descriptor FIFO parity error */ -#define E1000_ICR_ALL_PARITY 0x03F00000 /* all parity error bits */ -#define E1000_ICR_DSW 0x00000020 /* FW changed the status of DISSW bit in the FWSM */ -#define E1000_ICR_PHYINT 0x00001000 /* LAN connected device generates an interrupt */ -#define E1000_ICR_EPRST 0x00100000 /* ME handware reset occurs */ -#define E1000_ICR_RXQ0 0x00100000 /* Rx Queue 0 Interrupt */ -#define E1000_ICR_RXQ1 0x00200000 /* Rx Queue 1 Interrupt */ -#define E1000_ICR_TXQ0 0x00400000 /* Tx Queue 0 Interrupt */ -#define E1000_ICR_TXQ1 0x00800000 /* Tx Queue 1 Interrupt */ -#define E1000_ICR_OTHER 0x01000000 /* Other Interrupts */ - -#define E1000_ICR_OTHER_CAUSES (E1000_ICR_LSC | \ - E1000_ICR_RXO | \ - E1000_ICR_MDAC | \ - E1000_ICR_SRPD | \ - E1000_ICR_ACK | \ - E1000_ICR_MNG) - -/* Interrupt Cause Set */ -#define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ -#define E1000_ICS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ -#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ -#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ -#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ -#define E1000_ICS_RXO E1000_ICR_RXO /* rx overrun */ -#define E1000_ICS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ -#define E1000_ICS_MDAC E1000_ICR_MDAC /* MDIO access complete */ -#define E1000_ICS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ -#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ -#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ -#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ -#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ -#define E1000_ICS_TXD_LOW E1000_ICR_TXD_LOW -#define E1000_ICS_SRPD E1000_ICR_SRPD -#define E1000_ICS_ACK E1000_ICR_ACK /* Receive Ack frame */ -#define E1000_ICS_MNG E1000_ICR_MNG /* Manageability event */ -#define E1000_ICS_DOCK E1000_ICR_DOCK /* Dock/Undock */ -#define E1000_ICS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ -#define E1000_ICS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ -#define E1000_ICS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ -#define E1000_ICS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ -#define E1000_ICS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ -#define E1000_ICS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ -#define E1000_ICS_DSW E1000_ICR_DSW -#define E1000_ICS_PHYINT E1000_ICR_PHYINT -#define E1000_ICS_EPRST E1000_ICR_EPRST - -/* Interrupt Mask Set */ -#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ -#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ -#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */ -#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ -#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ -#define E1000_IMS_RXO E1000_ICR_RXO /* rx overrun */ -#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ -#define E1000_IMS_MDAC E1000_ICR_MDAC /* MDIO access complete */ -#define E1000_IMS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ -#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ -#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ -#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ -#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ -#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW -#define E1000_IMS_SRPD E1000_ICR_SRPD -#define E1000_IMS_ACK E1000_ICR_ACK /* Receive Ack frame */ -#define E1000_IMS_MNG E1000_ICR_MNG /* Manageability event */ -#define E1000_IMS_RXQ0 E1000_ICR_RXQ0 -#define E1000_IMS_RXQ1 E1000_ICR_RXQ1 -#define E1000_IMS_TXQ0 E1000_ICR_TXQ0 -#define E1000_IMS_TXQ1 E1000_ICR_TXQ1 -#define E1000_IMS_OTHER E1000_ICR_OTHER -#define E1000_IMS_DOCK E1000_ICR_DOCK /* Dock/Undock */ -#define E1000_IMS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ -#define E1000_IMS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ -#define E1000_IMS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ -#define E1000_IMS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ -#define E1000_IMS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ -#define E1000_IMS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ -#define E1000_IMS_DSW E1000_ICR_DSW -#define E1000_IMS_PHYINT E1000_ICR_PHYINT -#define E1000_IMS_EPRST E1000_ICR_EPRST - -/* Interrupt Mask Clear */ -#define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */ -#define E1000_IMC_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ -#define E1000_IMC_LSC E1000_ICR_LSC /* Link Status Change */ -#define E1000_IMC_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ -#define E1000_IMC_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ -#define E1000_IMC_RXO E1000_ICR_RXO /* rx overrun */ -#define E1000_IMC_RXT0 E1000_ICR_RXT0 /* rx timer intr */ -#define E1000_IMC_MDAC E1000_ICR_MDAC /* MDIO access complete */ -#define E1000_IMC_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ -#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ -#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ -#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ -#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ -#define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW -#define E1000_IMC_SRPD E1000_ICR_SRPD -#define E1000_IMC_ACK E1000_ICR_ACK /* Receive Ack frame */ -#define E1000_IMC_MNG E1000_ICR_MNG /* Manageability event */ -#define E1000_IMC_DOCK E1000_ICR_DOCK /* Dock/Undock */ -#define E1000_IMC_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ -#define E1000_IMC_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ -#define E1000_IMC_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ -#define E1000_IMC_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ -#define E1000_IMC_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ -#define E1000_IMC_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ -#define E1000_IMC_DSW E1000_ICR_DSW -#define E1000_IMC_PHYINT E1000_ICR_PHYINT -#define E1000_IMC_EPRST E1000_ICR_EPRST - -/* Receive Control */ -#define E1000_RCTL_RST 0x00000001 /* Software reset */ -#define E1000_RCTL_EN 0x00000002 /* enable */ -#define E1000_RCTL_SBP 0x00000004 /* store bad packet */ -#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */ -#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */ -#define E1000_RCTL_LPE 0x00000020 /* long packet enable */ -#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */ -#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */ -#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */ -#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */ -#define E1000_RCTL_DTYP_MASK 0x00000C00 /* Descriptor type mask */ -#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */ -#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */ -#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */ -#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */ -#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */ -#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */ -#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */ -#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */ -#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */ -#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */ -#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ -/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */ -#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */ -#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */ -#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */ -#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */ -/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */ -#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */ -#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */ -#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */ -#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */ -#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */ -#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */ -#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */ -#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */ -#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */ -#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ -#define E1000_RCTL_FLXBUF_MASK 0x78000000 /* Flexible buffer size */ -#define E1000_RCTL_FLXBUF_SHIFT 27 /* Flexible buffer shift */ - - -#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */ -#define E1000_EEPROM_LED_LOGIC 0x0020 /* Led Logic Word */ -#define E1000_EEPROM_RW_REG_DATA 16 /* Offset to data in EEPROM read/write registers */ -#define E1000_EEPROM_RW_REG_DONE 0x10 /* Offset to READ/WRITE done bit */ -#define E1000_EEPROM_RW_REG_START 1 /* First bit for telling part to start operation */ -#define E1000_EEPROM_RW_ADDR_SHIFT 8 /* Shift to the address bits */ -#define E1000_EEPROM_POLL_WRITE 1 /* Flag for polling for write complete */ -#define E1000_EEPROM_POLL_READ 0 /* Flag for polling for read complete */ - -/* 82574 EERD/EEWR registers layout */ -#define E1000_EERW_START BIT(0) -#define E1000_EERW_DONE BIT(1) -#define E1000_EERW_ADDR_SHIFT 2 -#define E1000_EERW_ADDR_MASK ((1L << 14) - 1) -#define E1000_EERW_DATA_SHIFT 16 -#define E1000_EERW_DATA_MASK ((1L << 16) - 1) - -/* Register Bit Masks */ -/* Device Control */ -#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ -#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */ -#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */ -#define E1000_CTRL_GIO_MASTER_DISABLE 0x00000004 /*Blocks new Master requests */ -#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */ -#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */ -#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */ -#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */ -#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */ -#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */ -#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */ -#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */ -#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */ -#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */ -#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */ -#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */ -#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */ -#define E1000_CTRL_D_UD_EN 0x00002000 /* Dock/Undock enable */ -#define E1000_CTRL_D_UD_POLARITY 0x00004000 /* Defined polarity of Dock/Undock indication in SDP[0] */ -#define E1000_CTRL_FORCE_PHY_RESET 0x00008000 /* Reset both PHY ports, through PHYRST_N pin */ -#define E1000_CTRL_SPD_SHIFT 8 /* Speed Select Shift */ - -#define E1000_CTRL_EXT_ASDCHK 0x00001000 /* auto speed detection check */ -#define E1000_CTRL_EXT_EE_RST 0x00002000 /* EEPROM reset */ -#define E1000_CTRL_EXT_LINK_EN 0x00010000 /* enable link status from external LINK_0 and LINK_1 pins */ -#define E1000_CTRL_EXT_EIAME 0x01000000 -#define E1000_CTRL_EXT_IAME 0x08000000 /* Int ACK Auto-mask */ -#define E1000_CTRL_EXT_PBA_CLR 0x80000000 /* PBA Clear */ -#define E1000_CTRL_EXT_INT_TIMERS_CLEAR_ENA 0x20000000 -#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ - -#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ -#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ -#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */ -#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */ -#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */ -#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */ -#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */ -#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */ -#define E1000_CTRL_ADVD3WUC 0x00100000 /* D3 WUC */ -#define E1000_CTRL_RST 0x04000000 /* Global reset */ -#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */ -#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */ -#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */ -#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ -#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ -#define E1000_CTRL_SW2FW_INT 0x02000000 /* Initiate an interrupt to manageability engine */ - -/* Device Status */ -#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */ -#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */ #define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */ #define E1000_STATUS_FUNC_SHIFT 2 #define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */ @@ -826,9 +247,6 @@ #define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */ #define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */ #define E1000_STATUS_SPEED_MASK 0x000000C0 -#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */ -#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */ -#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */ #define E1000_STATUS_LAN_INIT_DONE 0x00000200 /* Lan Init Completion by EEPROM/Flash */ #define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */ @@ -836,9 +254,7 @@ #define E1000_STATUS_ASDV_100 0x00000100 /* ASDV 100Mb */ #define E1000_STATUS_ASDV_1000 0x00000200 /* ASDV 1Gb */ #define E1000_STATUS_DOCK_CI 0x00000800 /* Change in Dock/Undock state. Clear on write '0'. */ -#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Status of Master requests. */ #define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */ -#define E1000_STATUS_PHYRA 0x00000400 /* PHY Reset Asserted */ #define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */ #define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */ #define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ @@ -856,111 +272,6 @@ #define E1000_STATUS_SPEED_SHIFT 6 #define E1000_STATUS_ASDV_SHIFT 8 -/* EEPROM/Flash Control */ -#define E1000_EECD_SK 0x00000001 /* EEPROM Clock */ -#define E1000_EECD_CS 0x00000002 /* EEPROM Chip Select */ -#define E1000_EECD_DI 0x00000004 /* EEPROM Data In */ -#define E1000_EECD_DO 0x00000008 /* EEPROM Data Out */ -#define E1000_EECD_FWE_MASK 0x00000030 -#define E1000_EECD_FWE_DIS 0x00000010 /* Disable FLASH writes */ -#define E1000_EECD_FWE_EN 0x00000020 /* Enable FLASH writes */ -#define E1000_EECD_FWE_SHIFT 4 -#define E1000_EECD_REQ 0x00000040 /* EEPROM Access Request */ -#define E1000_EECD_GNT 0x00000080 /* EEPROM Access Grant */ -#define E1000_EECD_PRES 0x00000100 /* EEPROM Present */ -#define E1000_EECD_SIZE 0x00000200 /* EEPROM Size (0=64 word 1=256 word) */ -#define E1000_EECD_ADDR_BITS 0x00000400 /* EEPROM Addressing bits based on type - * (0-small, 1-large) */ -#define E1000_EECD_TYPE 0x00002000 /* EEPROM Type (1-SPI, 0-Microwire) */ -#ifndef E1000_EEPROM_GRANT_ATTEMPTS -#define E1000_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */ -#endif -#define E1000_EECD_AUTO_RD 0x00000200 /* EEPROM Auto Read done */ -#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* EEprom Size */ -#define E1000_EECD_SIZE_EX_SHIFT 11 -#define E1000_EECD_NVADDS 0x00018000 /* NVM Address Size */ -#define E1000_EECD_SELSHAD 0x00020000 /* Select Shadow RAM */ -#define E1000_EECD_INITSRAM 0x00040000 /* Initialize Shadow RAM */ -#define E1000_EECD_FLUPD 0x00080000 /* Update FLASH */ -#define E1000_EECD_AUPDEN 0x00100000 /* Enable Autonomous FLASH update */ -#define E1000_EECD_SHADV 0x00200000 /* Shadow RAM Data Valid */ -#define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */ - - -#define E1000_EECD_SECVAL_SHIFT 22 -#define E1000_STM_OPCODE 0xDB00 -#define E1000_HICR_FW_RESET 0xC0 - -#define E1000_SHADOW_RAM_WORDS 2048 -#define E1000_ICH_NVM_SIG_WORD 0x13 -#define E1000_ICH_NVM_SIG_MASK 0xC0 - -/* MDI Control */ -#define E1000_MDIC_DATA_MASK 0x0000FFFF -#define E1000_MDIC_REG_MASK 0x001F0000 -#define E1000_MDIC_REG_SHIFT 16 -#define E1000_MDIC_PHY_MASK 0x03E00000 -#define E1000_MDIC_PHY_SHIFT 21 -#define E1000_MDIC_OP_WRITE 0x04000000 -#define E1000_MDIC_OP_READ 0x08000000 -#define E1000_MDIC_READY 0x10000000 -#define E1000_MDIC_INT_EN 0x20000000 -#define E1000_MDIC_ERROR 0x40000000 - -/* Rx Interrupt Delay Timer */ -#define E1000_RDTR_FPD BIT(31) - -/* Tx Interrupt Delay Timer */ -#define E1000_TIDV_FPD BIT(31) - -/* Delay increments in nanoseconds for delayed interrupts registers */ -#define E1000_INTR_DELAY_NS_RES (1024) - -/* Delay increments in nanoseconds for interrupt throttling registers */ -#define E1000_INTR_THROTTLING_NS_RES (256) - -/* EEPROM Commands - Microwire */ -#define EEPROM_READ_OPCODE_MICROWIRE 0x6 /* EEPROM read opcode */ -#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5 /* EEPROM write opcode */ -#define EEPROM_ERASE_OPCODE_MICROWIRE 0x7 /* EEPROM erase opcode */ -#define EEPROM_EWEN_OPCODE_MICROWIRE 0x13 /* EEPROM erase/write enable */ -#define EEPROM_EWDS_OPCODE_MICROWIRE 0x10 /* EEPROM erast/write disable */ - -/* EEPROM Word Offsets */ -#define EEPROM_COMPAT 0x0003 -#define EEPROM_ID_LED_SETTINGS 0x0004 -#define EEPROM_VERSION 0x0005 -#define EEPROM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude adjustment. */ -#define EEPROM_PHY_CLASS_WORD 0x0007 -#define EEPROM_INIT_CONTROL1_REG 0x000A -#define EEPROM_INIT_CONTROL2_REG 0x000F -#define EEPROM_SWDEF_PINS_CTRL_PORT_1 0x0010 -#define EEPROM_INIT_CONTROL3_PORT_B 0x0014 -#define EEPROM_INIT_3GIO_3 0x001A -#define EEPROM_SWDEF_PINS_CTRL_PORT_0 0x0020 -#define EEPROM_INIT_CONTROL3_PORT_A 0x0024 -#define EEPROM_CFG 0x0012 -#define EEPROM_FLASH_VERSION 0x0032 -#define EEPROM_CHECKSUM_REG 0x003F - -#define E1000_EEPROM_CFG_DONE 0x00040000 /* MNG config cycle done */ -#define E1000_EEPROM_CFG_DONE_PORT_1 0x00080000 /* ...for second port */ - -/* PCI Express Control */ -/* 3GIO Control Register - GCR (0x05B00; RW) */ -#define E1000_L0S_ADJUST (1 << 9) -#define E1000_L1_ENTRY_LATENCY_MSB (1 << 23) -#define E1000_L1_ENTRY_LATENCY_LSB (1 << 25 | 1 << 26) - -#define E1000_L0S_ADJUST (1 << 9) -#define E1000_L1_ENTRY_LATENCY_MSB (1 << 23) -#define E1000_L1_ENTRY_LATENCY_LSB (1 << 25 | 1 << 26) - -#define E1000_GCR_RO_BITS (1 << 23 | 1 << 25 | 1 << 26) - -/* MSI-X PBA Clear register */ -#define E1000_PBACLR_VALID_MASK (BIT(5) - 1) - /* Transmit Descriptor */ struct e1000_tx_desc { uint64_t buffer_addr; /* Address of the descriptor's data buffer */ @@ -982,269 +293,7 @@ struct e1000_tx_desc { } upper; }; -/* Transmit Descriptor bit definitions */ -#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ -#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ #define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */ #define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */ -#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ -#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ -#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */ -#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ -#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */ -#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */ -#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ -#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */ -#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ -#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ -#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ -#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ -#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ -#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ -#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ -#define E1000_TXD_CMD_SNAP 0x40000000 /* Update SNAP header */ -#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ -#define E1000_TXD_EXTCMD_TSTAMP 0x00000010 /* IEEE1588 Timestamp packet */ - -/* Transmit Control */ -#define E1000_TCTL_RST 0x00000001 /* software reset */ -#define E1000_TCTL_EN 0x00000002 /* enable tx */ -#define E1000_TCTL_BCE 0x00000004 /* busy check enable */ -#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ -#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ -#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ -#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ -#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */ -#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ -#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ -#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */ - -/* Legacy Receive Descriptor */ -struct e1000_rx_desc { - uint64_t buffer_addr; /* Address of the descriptor's data buffer */ - uint16_t length; /* Length of data DMAed into data buffer */ - uint16_t csum; /* Packet checksum */ - uint8_t status; /* Descriptor status */ - uint8_t errors; /* Descriptor Errors */ - uint16_t special; -}; - -/* Extended Receive Descriptor */ -union e1000_rx_desc_extended { - struct { - uint64_t buffer_addr; - uint64_t reserved; - } read; - struct { - struct { - uint32_t mrq; /* Multiple Rx Queues */ - union { - uint32_t rss; /* RSS Hash */ - struct { - uint16_t ip_id; /* IP id */ - uint16_t csum; /* Packet Checksum */ - } csum_ip; - } hi_dword; - } lower; - struct { - uint32_t status_error; /* ext status/error */ - uint16_t length; - uint16_t vlan; /* VLAN tag */ - } upper; - } wb; /* writeback */ -}; - -#define MAX_PS_BUFFERS 4 - -/* Number of packet split data buffers (not including the header buffer) */ -#define PS_PAGE_BUFFERS (MAX_PS_BUFFERS - 1) - -/* Receive Descriptor - Packet Split */ -union e1000_rx_desc_packet_split { - struct { - /* one buffer for protocol header(s), three data buffers */ - uint64_t buffer_addr[MAX_PS_BUFFERS]; - } read; - struct { - struct { - uint32_t mrq; /* Multiple Rx Queues */ - union { - uint32_t rss; /* RSS Hash */ - struct { - uint16_t ip_id; /* IP id */ - uint16_t csum; /* Packet Checksum */ - } csum_ip; - } hi_dword; - } lower; - struct { - uint32_t status_error; /* ext status/error */ - uint16_t length0; /* length of buffer 0 */ - uint16_t vlan; /* VLAN tag */ - } middle; - struct { - uint16_t header_status; - /* length of buffers 1-3 */ - uint16_t length[PS_PAGE_BUFFERS]; - } upper; - uint64_t reserved; - } wb; /* writeback */ -}; - -/* Receive Checksum Control bits */ -#define E1000_RXCSUM_IPOFLD 0x100 /* IP Checksum Offload Enable */ -#define E1000_RXCSUM_TUOFLD 0x200 /* TCP/UDP Checksum Offload Enable */ -#define E1000_RXCSUM_PCSD 0x2000 /* Packet Checksum Disable */ - -#define E1000_RING_DESC_LEN (16) -#define E1000_RING_DESC_LEN_SHIFT (4) - -#define E1000_MIN_RX_DESC_LEN E1000_RING_DESC_LEN -#define E1000_MAX_RX_DESC_LEN (sizeof(union e1000_rx_desc_packet_split)) - -/* Receive Descriptor bit definitions */ -#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ -#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ -#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */ -#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */ -#define E1000_RXD_STAT_UDPCS 0x10 /* UDP xsum caculated */ -#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */ -#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */ -#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */ -#define E1000_RXD_STAT_IPIDV 0x200 /* IP identification valid */ -#define E1000_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */ -#define E1000_RXD_STAT_ACK 0x8000 /* ACK Packet indication */ -#define E1000_RXD_ERR_CE 0x01 /* CRC Error */ -#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */ -#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */ -#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */ -#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */ -#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */ -#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */ -#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */ -#define E1000_RXD_SPC_PRI_MASK 0xE000 /* Priority is in upper 3 bits */ -#define E1000_RXD_SPC_PRI_SHIFT 13 -#define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */ -#define E1000_RXD_SPC_CFI_SHIFT 12 - -/* RX packet types */ -#define E1000_RXD_PKT_MAC (0) -#define E1000_RXD_PKT_IP4 (1) -#define E1000_RXD_PKT_IP4_XDP (2) -#define E1000_RXD_PKT_IP6 (5) -#define E1000_RXD_PKT_IP6_XDP (6) - -#define E1000_RXD_PKT_TYPE(t) ((t) << 16) - -#define E1000_RXDEXT_STATERR_CE 0x01000000 -#define E1000_RXDEXT_STATERR_SE 0x02000000 -#define E1000_RXDEXT_STATERR_SEQ 0x04000000 -#define E1000_RXDEXT_STATERR_CXE 0x10000000 -#define E1000_RXDEXT_STATERR_TCPE 0x20000000 -#define E1000_RXDEXT_STATERR_IPE 0x40000000 -#define E1000_RXDEXT_STATERR_RXE 0x80000000 - -#define E1000_RXDPS_HDRSTAT_HDRSP 0x00008000 -#define E1000_RXDPS_HDRSTAT_HDRLEN_MASK 0x000003FF - -/* Receive Address */ -#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ - -/* Offload Context Descriptor */ -struct e1000_context_desc { - union { - uint32_t ip_config; - struct { - uint8_t ipcss; /* IP checksum start */ - uint8_t ipcso; /* IP checksum offset */ - uint16_t ipcse; /* IP checksum end */ - } ip_fields; - } lower_setup; - union { - uint32_t tcp_config; - struct { - uint8_t tucss; /* TCP checksum start */ - uint8_t tucso; /* TCP checksum offset */ - uint16_t tucse; /* TCP checksum end */ - } tcp_fields; - } upper_setup; - uint32_t cmd_and_length; /* */ - union { - uint32_t data; - struct { - uint8_t status; /* Descriptor status */ - uint8_t hdr_len; /* Header length */ - uint16_t mss; /* Maximum segment size */ - } fields; - } tcp_seg_setup; -}; - -/* Offload data descriptor */ -struct e1000_data_desc { - uint64_t buffer_addr; /* Address of the descriptor's buffer address */ - union { - uint32_t data; - struct { - uint16_t length; /* Data buffer length */ - uint8_t typ_len_ext; /* */ - uint8_t cmd; /* */ - } flags; - } lower; - union { - uint32_t data; - struct { - uint8_t status; /* Descriptor status */ - uint8_t popts; /* Packet Options */ - uint16_t special; /* */ - } fields; - } upper; -}; - -/* Management Control */ -#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */ -#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */ -#define E1000_MANC_R_ON_FORCE 0x00000004 /* Reset on Force TCO - RO */ -#define E1000_MANC_RMCP_EN 0x00000100 /* Enable RCMP 026Fh Filtering */ -#define E1000_MANC_0298_EN 0x00000200 /* Enable RCMP 0298h Filtering */ -#define E1000_MANC_IPV4_EN 0x00000400 /* Enable IPv4 */ -#define E1000_MANC_IPV6_EN 0x00000800 /* Enable IPv6 */ -#define E1000_MANC_SNAP_EN 0x00001000 /* Accept LLC/SNAP */ -#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */ -#define E1000_MANC_NEIGHBOR_EN 0x00004000 /* Enable Neighbor Discovery - * Filtering */ -#define E1000_MANC_ARP_RES_EN 0x00008000 /* Enable ARP response Filtering */ -#define E1000_MANC_DIS_IP_CHK_ARP 0x10000000 /* Disable IP address chacking */ - /*for ARP packets - in 82574 */ -#define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */ -#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */ -#define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */ -#define E1000_MANC_RCV_ALL 0x00080000 /* Receive All Enabled */ -#define E1000_MANC_BLK_PHY_RST_ON_IDE 0x00040000 /* Block phy resets */ -#define E1000_MANC_EN_MAC_ADDR_FILTER 0x00100000 /* Enable MAC address - * filtering */ -#define E1000_MANC_EN_MNG2HOST 0x00200000 /* Enable MNG packets to host - * memory */ -#define E1000_MANC_EN_IP_ADDR_FILTER 0x00400000 /* Enable IP address - * filtering */ -#define E1000_MANC_EN_XSUM_FILTER 0x00800000 /* Enable checksum filtering */ -#define E1000_MANC_BR_EN 0x01000000 /* Enable broadcast filtering */ -#define E1000_MANC_SMB_REQ 0x01000000 /* SMBus Request */ -#define E1000_MANC_SMB_GNT 0x02000000 /* SMBus Grant */ -#define E1000_MANC_SMB_CLK_IN 0x04000000 /* SMBus Clock In */ -#define E1000_MANC_SMB_DATA_IN 0x08000000 /* SMBus Data In */ -#define E1000_MANC_SMB_DATA_OUT 0x10000000 /* SMBus Data Out */ -#define E1000_MANC_SMB_CLK_OUT 0x20000000 /* SMBus Clock Out */ - -#define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */ -#define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */ - -/* FACTPS Control */ -#define E1000_FACTPS_LAN0_ON 0x00000004 /* Lan 0 enable */ - -/* For checksumming, the sum of all words in the EEPROM should equal 0xBABA. */ -#define EEPROM_SUM 0xBABA - -/* I/O-Mapped Access to Internal Registers, Memories, and Flash */ -#define E1000_IOADDR 0x00 -#define E1000_IODATA 0x04 #endif /* HW_E1000_REGS_H */ diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index ac96f7665a..7c6f602951 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -1,37 +1,37 @@ /* -* QEMU INTEL 82574 GbE NIC emulation -* -* Software developer's manuals: -* http://www.intel.com/content/dam/doc/datasheet/82574l-gbe-controller-datasheet.pdf -* -* Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) -* Developed by Daynix Computing LTD (http://www.daynix.com) -* -* Authors: -* Dmitry Fleytman -* Leonid Bloch -* Yan Vugenfirer -* -* Based on work done by: -* Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. -* Copyright (c) 2008 Qumranet -* Based on work done by: -* Copyright (c) 2007 Dan Aloni -* Copyright (c) 2004 Antony T Curtis -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; either -* version 2.1 of the License, or (at your option) any later version. -* -* 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 . -*/ + * QEMU INTEL 82574 GbE NIC emulation + * + * Software developer's manuals: + * http://www.intel.com/content/dam/doc/datasheet/82574l-gbe-controller-datasheet.pdf + * + * Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman + * Leonid Bloch + * Yan Vugenfirer + * + * Based on work done by: + * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. + * Copyright (c) 2008 Qumranet + * Based on work done by: + * Copyright (c) 2007 Dan Aloni + * Copyright (c) 2004 Antony T Curtis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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" @@ -42,13 +42,13 @@ #include "qemu/range.h" #include "sysemu/sysemu.h" #include "hw/hw.h" +#include "hw/net/mii.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "e1000_regs.h" - +#include "e1000_common.h" #include "e1000x_common.h" #include "e1000e_core.h" @@ -81,6 +81,7 @@ struct E1000EState { E1000ECore core; bool init_vet; + bool timadj; }; #define E1000E_MMIO_IDX 0 @@ -239,9 +240,9 @@ static NetClientInfo net_e1000e_info = { }; /* -* EEPROM (NVM) contents documented in Table 36, section 6.1 -* and generally 6.1.2 Software accessed words. -*/ + * EEPROM (NVM) contents documented in Table 36, section 6.1 + * and generally 6.1.2 Software accessed words. + */ static const uint16_t e1000e_eeprom_template[64] = { /* Address | Compat. | ImVer | Compat. */ 0x0000, 0x0000, 0x0000, 0x0420, 0xf746, 0x2010, 0xffff, 0xffff, @@ -276,25 +277,18 @@ e1000e_unuse_msix_vectors(E1000EState *s, int num_vectors) } } -static bool +static void e1000e_use_msix_vectors(E1000EState *s, int num_vectors) { int i; for (i = 0; i < num_vectors; i++) { - int res = msix_vector_use(PCI_DEVICE(s), i); - if (res < 0) { - trace_e1000e_msix_use_vector_fail(i, res); - e1000e_unuse_msix_vectors(s, i); - return false; - } + msix_vector_use(PCI_DEVICE(s), i); } - return true; } static void e1000e_init_msix(E1000EState *s) { - PCIDevice *d = PCI_DEVICE(s); int res = msix_init(PCI_DEVICE(s), E1000E_MSIX_VEC_NUM, &s->msix, E1000E_MSIX_IDX, E1000E_MSIX_TABLE, @@ -305,9 +299,7 @@ e1000e_init_msix(E1000EState *s) if (res < 0) { trace_e1000e_msix_init_fail(res); } else { - if (!e1000e_use_msix_vectors(s, E1000E_MSIX_VEC_NUM)) { - msix_uninit(d, &s->msix, &s->msix); - } + e1000e_use_msix_vectors(s, E1000E_MSIX_VEC_NUM); } } @@ -328,7 +320,7 @@ e1000e_init_net_peer(E1000EState *s, PCIDevice *pci_dev, uint8_t *macaddr) int i; s->nic = qemu_new_nic(&net_e1000e_info, &s->conf, - object_get_typename(OBJECT(s)), dev->id, s); + object_get_typename(OBJECT(s)), dev->id, &dev->mem_reentrancy_guard, s); s->core.max_queue_num = s->conf.peers.queues ? s->conf.peers.queues - 1 : 0; @@ -521,11 +513,11 @@ static void e1000e_pci_uninit(PCIDevice *pci_dev) msi_uninit(pci_dev); } -static void e1000e_qdev_reset(DeviceState *dev) +static void e1000e_qdev_reset_hold(Object *obj) { - E1000EState *s = E1000E(dev); + E1000EState *s = E1000E(obj); - trace_e1000e_cb_qdev_reset(); + trace_e1000e_cb_qdev_reset_hold(); e1000e_core_reset(&s->core); @@ -562,11 +554,17 @@ static int e1000e_post_load(void *opaque, int version_id) return e1000e_core_post_load(&s->core); } +static bool e1000e_migrate_timadj(void *opaque, int version_id) +{ + E1000EState *s = opaque; + return s->timadj; +} + static const VMStateDescription e1000e_vmstate_tx = { .name = "e1000e-tx", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(sum_needed, struct e1000e_tx), VMSTATE_UINT8(props.ipcss, struct e1000e_tx), VMSTATE_UINT8(props.ipcso, struct e1000e_tx), @@ -590,7 +588,7 @@ static const VMStateDescription e1000e_vmstate_intr_timer = { .name = "e1000e-intr-timer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(timer, E1000IntrDelayTimer), VMSTATE_BOOL(running, E1000IntrDelayTimer), VMSTATE_END_OF_LIST() @@ -611,7 +609,7 @@ static const VMStateDescription e1000e_vmstate = { .minimum_version_id = 1, .pre_save = e1000e_pre_save, .post_load = e1000e_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, E1000EState), VMSTATE_MSIX(parent_obj, E1000EState), @@ -639,12 +637,11 @@ static const VMStateDescription e1000e_vmstate = { VMSTATE_E1000E_INTR_DELAY_TIMER(core.tidv, E1000EState), VMSTATE_E1000E_INTR_DELAY_TIMER(core.itr, E1000EState), - VMSTATE_BOOL(core.itr_intr_pending, E1000EState), + VMSTATE_UNUSED(1), VMSTATE_E1000E_INTR_DELAY_TIMER_ARRAY(core.eitr, E1000EState, E1000E_MSIX_VEC_NUM), - VMSTATE_BOOL_ARRAY(core.eitr_intr_pending, E1000EState, - E1000E_MSIX_VEC_NUM), + VMSTATE_UNUSED(E1000E_MSIX_VEC_NUM), VMSTATE_UINT32(core.itr_guest_value, E1000EState), VMSTATE_UINT32_ARRAY(core.eitr_guest_value, E1000EState, @@ -654,6 +651,9 @@ static const VMStateDescription e1000e_vmstate = { VMSTATE_STRUCT_ARRAY(core.tx, E1000EState, E1000E_NUM_QUEUES, 0, e1000e_vmstate_tx, struct e1000e_tx), + + VMSTATE_INT64_TEST(core.timadj, E1000EState, e1000e_migrate_timadj), + VMSTATE_END_OF_LIST() } }; @@ -672,12 +672,14 @@ static Property e1000e_properties[] = { DEFINE_PROP_SIGNED("subsys", E1000EState, subsys, 0, e1000e_prop_subsys, uint16_t), DEFINE_PROP_BOOL("init-vet", E1000EState, init_vet, true), + DEFINE_PROP_BOOL("migrate-timadj", E1000EState, timadj, true), DEFINE_PROP_END_OF_LIST(), }; static void e1000e_class_init(ObjectClass *class, void *data) { DeviceClass *dc = DEVICE_CLASS(class); + ResettableClass *rc = RESETTABLE_CLASS(class); PCIDeviceClass *c = PCI_DEVICE_CLASS(class); c->realize = e1000e_pci_realize; @@ -688,8 +690,9 @@ static void e1000e_class_init(ObjectClass *class, void *data) c->romfile = "efi-e1000e.rom"; c->class_id = PCI_CLASS_NETWORK_ETHERNET; + rc->phases.hold = e1000e_qdev_reset_hold; + dc->desc = "Intel 82574L GbE Controller"; - dc->reset = e1000e_qdev_reset; dc->vmsd = &e1000e_vmstate; e1000e_prop_disable_vnet = qdev_prop_uint8; diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index 2c51089a82..3ae2a184d5 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -1,42 +1,43 @@ /* -* Core code for QEMU e1000e emulation -* -* Software developer's manuals: -* http://www.intel.com/content/dam/doc/datasheet/82574l-gbe-controller-datasheet.pdf -* -* Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) -* Developed by Daynix Computing LTD (http://www.daynix.com) -* -* Authors: -* Dmitry Fleytman -* Leonid Bloch -* Yan Vugenfirer -* -* Based on work done by: -* Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. -* Copyright (c) 2008 Qumranet -* Based on work done by: -* Copyright (c) 2007 Dan Aloni -* Copyright (c) 2004 Antony T Curtis -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; either -* version 2.1 of the License, or (at your option) any later version. -* -* 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 . -*/ + * Core code for QEMU e1000e emulation + * + * Software developer's manuals: + * http://www.intel.com/content/dam/doc/datasheet/82574l-gbe-controller-datasheet.pdf + * + * Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman + * Leonid Bloch + * Yan Vugenfirer + * + * Based on work done by: + * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. + * Copyright (c) 2008 Qumranet + * Based on work done by: + * Copyright (c) 2007 Dan Aloni + * Copyright (c) 2004 Antony T Curtis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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/log.h" #include "net/net.h" #include "net/tap.h" +#include "hw/net/mii.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "sysemu/runstate.h" @@ -44,18 +45,32 @@ #include "net_tx_pkt.h" #include "net_rx_pkt.h" +#include "e1000_common.h" #include "e1000x_common.h" #include "e1000e_core.h" #include "trace.h" -#define E1000E_MIN_XITR (500) /* No more then 7813 interrupts per - second according to spec 10.2.4.2 */ +/* No more then 7813 interrupts per second according to spec 10.2.4.2 */ +#define E1000E_MIN_XITR (500) + #define E1000E_MAX_TX_FRAGS (64) +union e1000_rx_desc_union { + struct e1000_rx_desc legacy; + union e1000_rx_desc_extended extended; + union e1000_rx_desc_packet_split packet_split; +}; + +static ssize_t +e1000e_receive_internal(E1000ECore *core, const struct iovec *iov, int iovcnt, + bool has_vnet); + static inline void e1000e_set_interrupt_cause(E1000ECore *core, uint32_t val); +static void e1000e_reset(E1000ECore *core, bool sw); + static inline void e1000e_process_ts_option(E1000ECore *core, struct e1000_tx_desc *dp) { @@ -108,14 +123,6 @@ e1000e_intmgr_timer_resume(E1000IntrDelayTimer *timer) } } -static void -e1000e_intmgr_timer_pause(E1000IntrDelayTimer *timer) -{ - if (timer->running) { - timer_del(timer->timer); - } -} - static inline void e1000e_intrmgr_stop_timer(E1000IntrDelayTimer *timer) { @@ -148,21 +155,16 @@ e1000e_intrmgr_on_throttling_timer(void *opaque) { E1000IntrDelayTimer *timer = opaque; - assert(!msix_enabled(timer->core->owner)); - timer->running = false; - if (!timer->core->itr_intr_pending) { - trace_e1000e_irq_throttling_no_pending_interrupts(); - return; - } - - if (msi_enabled(timer->core->owner)) { - trace_e1000e_irq_msi_notify_postponed(); - e1000e_set_interrupt_cause(timer->core, 0); - } else { - trace_e1000e_irq_legacy_notify_postponed(); - e1000e_set_interrupt_cause(timer->core, 0); + if (timer->core->mac[IMS] & timer->core->mac[ICR]) { + if (msi_enabled(timer->core->owner)) { + trace_e1000e_irq_msi_notify_postponed(); + msi_notify(timer->core->owner, 0); + } else { + trace_e1000e_irq_legacy_notify_postponed(); + e1000e_raise_legacy_irq(timer->core); + } } } @@ -172,15 +174,8 @@ e1000e_intrmgr_on_msix_throttling_timer(void *opaque) E1000IntrDelayTimer *timer = opaque; int idx = timer - &timer->core->eitr[0]; - assert(msix_enabled(timer->core->owner)); - timer->running = false; - if (!timer->core->eitr_intr_pending[idx]) { - trace_e1000e_irq_throttling_no_pending_vec(idx); - return; - } - trace_e1000e_irq_msix_notify_postponed_vec(idx); msix_notify(timer->core->owner, idx); } @@ -280,14 +275,18 @@ e1000e_intrmgr_delay_rx_causes(E1000ECore *core, uint32_t *causes) core->delayed_causes |= *causes & delayable_causes; *causes &= ~delayable_causes; - /* Check if delayed RX interrupts disabled by client - or if there are causes that cannot be delayed */ + /* + * Check if delayed RX interrupts disabled by client + * or if there are causes that cannot be delayed + */ if ((rdtr == 0) || (*causes != 0)) { return false; } - /* Check if delayed RX ACK interrupts disabled by client - and there is an ACK packet received */ + /* + * Check if delayed RX ACK interrupts disabled by client + * and there is an ACK packet received + */ if ((raid == 0) && (core->delayed_causes & E1000_ICR_ACK)) { return false; } @@ -359,10 +358,6 @@ static void e1000e_intrmgr_fire_all_timers(E1000ECore *core) { int i; - uint32_t val = e1000e_intmgr_collect_delayed_causes(core); - - trace_e1000e_irq_adding_delayed_causes(val, core->mac[ICR]); - core->mac[ICR] |= val; if (core->itr.running) { timer_del(core->itr.timer); @@ -395,24 +390,6 @@ e1000e_intrmgr_resume(E1000ECore *core) } } -static void -e1000e_intrmgr_pause(E1000ECore *core) -{ - int i; - - e1000e_intmgr_timer_pause(&core->radv); - e1000e_intmgr_timer_pause(&core->rdtr); - e1000e_intmgr_timer_pause(&core->raid); - e1000e_intmgr_timer_pause(&core->tidv); - e1000e_intmgr_timer_pause(&core->tadv); - - e1000e_intmgr_timer_pause(&core->itr); - - for (i = 0; i < E1000E_MSIX_VEC_NUM; i++) { - e1000e_intmgr_timer_pause(&core->eitr[i]); - } -} - static void e1000e_intrmgr_reset(E1000ECore *core) { @@ -491,27 +468,27 @@ typedef struct E1000E_RSSInfo_st { static uint32_t e1000e_rss_get_hash_type(E1000ECore *core, struct NetRxPkt *pkt) { - bool isip4, isip6, isudp, istcp; + bool hasip4, hasip6; + EthL4HdrProto l4hdr_proto; assert(e1000e_rss_enabled(core)); - net_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); + net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto); - if (isip4) { - bool fragment = net_rx_pkt_get_ip4_info(pkt)->fragment; - - trace_e1000e_rx_rss_ip4(fragment, istcp, core->mac[MRQC], + if (hasip4) { + trace_e1000e_rx_rss_ip4(l4hdr_proto, core->mac[MRQC], E1000_MRQC_EN_TCPIPV4(core->mac[MRQC]), E1000_MRQC_EN_IPV4(core->mac[MRQC])); - if (!fragment && istcp && E1000_MRQC_EN_TCPIPV4(core->mac[MRQC])) { + if (l4hdr_proto == ETH_L4_HDR_PROTO_TCP && + E1000_MRQC_EN_TCPIPV4(core->mac[MRQC])) { return E1000_MRQ_RSS_TYPE_IPV4TCP; } if (E1000_MRQC_EN_IPV4(core->mac[MRQC])) { return E1000_MRQ_RSS_TYPE_IPV4; } - } else if (isip6) { + } else if (hasip6) { eth_ip6_hdr_info *ip6info = net_rx_pkt_get_ip6_info(pkt); bool ex_dis = core->mac[RFCTL] & E1000_RFCTL_IPV6_EX_DIS; @@ -525,12 +502,12 @@ e1000e_rss_get_hash_type(E1000ECore *core, struct NetRxPkt *pkt) * backends like these. */ trace_e1000e_rx_rss_ip6_rfctl(core->mac[RFCTL]); - trace_e1000e_rx_rss_ip6(ex_dis, new_ex_dis, istcp, + trace_e1000e_rx_rss_ip6(ex_dis, new_ex_dis, l4hdr_proto, ip6info->has_ext_hdrs, ip6info->rss_ex_dst_valid, ip6info->rss_ex_src_valid, core->mac[MRQC], - E1000_MRQC_EN_TCPIPV6(core->mac[MRQC]), + E1000_MRQC_EN_TCPIPV6EX(core->mac[MRQC]), E1000_MRQC_EN_IPV6EX(core->mac[MRQC]), E1000_MRQC_EN_IPV6(core->mac[MRQC])); @@ -538,9 +515,9 @@ e1000e_rss_get_hash_type(E1000ECore *core, struct NetRxPkt *pkt) (!new_ex_dis || !(ip6info->rss_ex_dst_valid || ip6info->rss_ex_src_valid))) { - if (istcp && !ip6info->fragment && - E1000_MRQC_EN_TCPIPV6(core->mac[MRQC])) { - return E1000_MRQ_RSS_TYPE_IPV6TCP; + if (l4hdr_proto == ETH_L4_HDR_PROTO_TCP && + E1000_MRQC_EN_TCPIPV6EX(core->mac[MRQC])) { + return E1000_MRQ_RSS_TYPE_IPV6TCPEX; } if (E1000_MRQC_EN_IPV6EX(core->mac[MRQC])) { @@ -574,7 +551,7 @@ e1000e_rss_calc_hash(E1000ECore *core, case E1000_MRQ_RSS_TYPE_IPV4TCP: type = NetPktRssIpV4Tcp; break; - case E1000_MRQ_RSS_TYPE_IPV6TCP: + case E1000_MRQ_RSS_TYPE_IPV6TCPEX: type = NetPktRssIpV6TcpEx; break; case E1000_MRQ_RSS_TYPE_IPV6: @@ -623,23 +600,39 @@ e1000e_rss_parse_packet(E1000ECore *core, info->queue = E1000_RSS_QUEUE(&core->mac[RETA], info->hash); } -static void +static bool e1000e_setup_tx_offloads(E1000ECore *core, struct e1000e_tx *tx) { if (tx->props.tse && tx->cptse) { - net_tx_pkt_build_vheader(tx->tx_pkt, true, true, tx->props.mss); + if (!net_tx_pkt_build_vheader(tx->tx_pkt, true, true, tx->props.mss)) { + return false; + } + net_tx_pkt_update_ip_checksums(tx->tx_pkt); e1000x_inc_reg_if_not_full(core->mac, TSCTC); - return; + return true; } if (tx->sum_needed & E1000_TXD_POPTS_TXSM) { - net_tx_pkt_build_vheader(tx->tx_pkt, false, true, 0); + if (!net_tx_pkt_build_vheader(tx->tx_pkt, false, true, 0)) { + return false; + } } if (tx->sum_needed & E1000_TXD_POPTS_IXSM) { net_tx_pkt_update_ip_hdr_checksum(tx->tx_pkt); } + + return true; +} + +static void e1000e_tx_pkt_callback(void *core, + const struct iovec *iov, + int iovcnt, + const struct iovec *virt_iov, + int virt_iovcnt) +{ + e1000e_receive_internal(core, virt_iov, virt_iovcnt, true); } static bool @@ -648,13 +641,16 @@ e1000e_tx_pkt_send(E1000ECore *core, struct e1000e_tx *tx, int queue_index) int target_queue = MIN(core->max_queue_num, queue_index); NetClientState *queue = qemu_get_subqueue(core->owner_nic, target_queue); - e1000e_setup_tx_offloads(core, tx); + if (!e1000e_setup_tx_offloads(core, tx)) { + return false; + } net_tx_pkt_dump(tx->tx_pkt); - if ((core->phy[0][PHY_CTRL] & MII_CR_LOOPBACK) || + if ((core->phy[0][MII_BMCR] & MII_BMCR_LOOPBACK) || ((core->mac[RCTL] & E1000_RCTL_LBM_MAC) == E1000_RCTL_LBM_MAC)) { - return net_tx_pkt_send_loopback(tx->tx_pkt, queue); + return net_tx_pkt_send_custom(tx->tx_pkt, false, + e1000e_tx_pkt_callback, core); } else { return net_tx_pkt_send(tx->tx_pkt, queue); } @@ -666,7 +662,7 @@ e1000e_on_tx_done_update_stats(E1000ECore *core, struct NetTxPkt *tx_pkt) static const int PTCregs[6] = { PTC64, PTC127, PTC255, PTC511, PTC1023, PTC1522 }; - size_t tot_len = net_tx_pkt_get_total_len(tx_pkt); + size_t tot_len = net_tx_pkt_get_total_len(tx_pkt) + 4; e1000x_increase_size_stats(core->mac, PTCregs, tot_len); e1000x_inc_reg_if_not_full(core->mac, TPT); @@ -685,9 +681,8 @@ e1000e_on_tx_done_update_stats(E1000ECore *core, struct NetTxPkt *tx_pkt) g_assert_not_reached(); } - core->mac[GPTC] = core->mac[TPT]; - core->mac[GOTCL] = core->mac[TOTL]; - core->mac[GOTCH] = core->mac[TOTH]; + e1000x_inc_reg_if_not_full(core->mac, GPTC); + e1000x_grow_8reg_if_not_full(core->mac, GOTCL, tot_len); } static void @@ -721,7 +716,8 @@ e1000e_process_tx_desc(E1000ECore *core, addr = le64_to_cpu(dp->buffer_addr); if (!tx->skip_cp) { - if (!net_tx_pkt_add_raw_fragment(tx->tx_pkt, addr, split_size)) { + if (!net_tx_pkt_add_raw_fragment_pci(tx->tx_pkt, core->owner, + addr, split_size)) { tx->skip_cp = true; } } @@ -739,7 +735,7 @@ e1000e_process_tx_desc(E1000ECore *core, } tx->skip_cp = false; - net_tx_pkt_reset(tx->tx_pkt); + net_tx_pkt_reset(tx->tx_pkt, net_tx_pkt_unmap_frag_pci, core->owner); tx->sum_needed = 0; tx->cptse = 0; @@ -788,24 +784,24 @@ e1000e_txdesc_writeback(E1000ECore *core, dma_addr_t base, return e1000e_tx_wb_interrupt_cause(core, queue_idx); } -typedef struct E1000E_RingInfo_st { +typedef struct E1000ERingInfo { int dbah; int dbal; int dlen; int dh; int dt; int idx; -} E1000E_RingInfo; +} E1000ERingInfo; static inline bool -e1000e_ring_empty(E1000ECore *core, const E1000E_RingInfo *r) +e1000e_ring_empty(E1000ECore *core, const E1000ERingInfo *r) { return core->mac[r->dh] == core->mac[r->dt] || core->mac[r->dt] >= core->mac[r->dlen] / E1000_RING_DESC_LEN; } static inline uint64_t -e1000e_ring_base(E1000ECore *core, const E1000E_RingInfo *r) +e1000e_ring_base(E1000ECore *core, const E1000ERingInfo *r) { uint64_t bah = core->mac[r->dbah]; uint64_t bal = core->mac[r->dbal]; @@ -814,13 +810,13 @@ e1000e_ring_base(E1000ECore *core, const E1000E_RingInfo *r) } static inline uint64_t -e1000e_ring_head_descr(E1000ECore *core, const E1000E_RingInfo *r) +e1000e_ring_head_descr(E1000ECore *core, const E1000ERingInfo *r) { return e1000e_ring_base(core, r) + E1000_RING_DESC_LEN * core->mac[r->dh]; } static inline void -e1000e_ring_advance(E1000ECore *core, const E1000E_RingInfo *r, uint32_t count) +e1000e_ring_advance(E1000ECore *core, const E1000ERingInfo *r, uint32_t count) { core->mac[r->dh] += count; @@ -830,7 +826,7 @@ e1000e_ring_advance(E1000ECore *core, const E1000E_RingInfo *r, uint32_t count) } static inline uint32_t -e1000e_ring_free_descr_num(E1000ECore *core, const E1000E_RingInfo *r) +e1000e_ring_free_descr_num(E1000ECore *core, const E1000ERingInfo *r) { trace_e1000e_ring_free_space(r->idx, core->mac[r->dlen], core->mac[r->dh], core->mac[r->dt]); @@ -849,19 +845,19 @@ e1000e_ring_free_descr_num(E1000ECore *core, const E1000E_RingInfo *r) } static inline bool -e1000e_ring_enabled(E1000ECore *core, const E1000E_RingInfo *r) +e1000e_ring_enabled(E1000ECore *core, const E1000ERingInfo *r) { return core->mac[r->dlen] > 0; } static inline uint32_t -e1000e_ring_len(E1000ECore *core, const E1000E_RingInfo *r) +e1000e_ring_len(E1000ECore *core, const E1000ERingInfo *r) { return core->mac[r->dlen]; } typedef struct E1000E_TxRing_st { - const E1000E_RingInfo *i; + const E1000ERingInfo *i; struct e1000e_tx *tx; } E1000E_TxRing; @@ -874,7 +870,7 @@ e1000e_mq_queue_idx(int base_reg_idx, int reg_idx) static inline void e1000e_tx_ring_init(E1000ECore *core, E1000E_TxRing *txr, int idx) { - static const E1000E_RingInfo i[E1000E_NUM_QUEUES] = { + static const E1000ERingInfo i[E1000E_NUM_QUEUES] = { { TDBAH, TDBAL, TDLEN, TDH, TDT, 0 }, { TDBAH1, TDBAL1, TDLEN1, TDH1, TDT1, 1 } }; @@ -886,13 +882,13 @@ e1000e_tx_ring_init(E1000ECore *core, E1000E_TxRing *txr, int idx) } typedef struct E1000E_RxRing_st { - const E1000E_RingInfo *i; + const E1000ERingInfo *i; } E1000E_RxRing; static inline void e1000e_rx_ring_init(E1000ECore *core, E1000E_RxRing *rxr, int idx) { - static const E1000E_RingInfo i[E1000E_NUM_QUEUES] = { + static const E1000ERingInfo i[E1000E_NUM_QUEUES] = { { RDBAH0, RDBAL0, RDLEN0, RDH0, RDT0, 0 }, { RDBAH1, RDBAL1, RDLEN1, RDH1, RDT1, 1 } }; @@ -908,7 +904,7 @@ e1000e_start_xmit(E1000ECore *core, const E1000E_TxRing *txr) dma_addr_t base; struct e1000_tx_desc desc; bool ide = false; - const E1000E_RingInfo *txi = txr->i; + const E1000ERingInfo *txi = txr->i; uint32_t cause = E1000_ICS_TXQE; if (!(core->mac[TCTL] & E1000_TCTL_EN)) { @@ -933,10 +929,12 @@ e1000e_start_xmit(E1000ECore *core, const E1000E_TxRing *txr) if (!ide || !e1000e_intrmgr_delay_tx_causes(core, &cause)) { e1000e_set_interrupt_cause(core, cause); } + + net_tx_pkt_reset(txr->tx->tx_pkt, net_tx_pkt_unmap_frag_pci, core->owner); } static bool -e1000e_has_rxbufs(E1000ECore *core, const E1000E_RingInfo *r, +e1000e_has_rxbufs(E1000ECore *core, const E1000ERingInfo *r, size_t total_size) { uint32_t bufs = e1000e_ring_free_descr_num(core, r); @@ -1008,92 +1006,55 @@ e1000e_rx_l4_cso_enabled(E1000ECore *core) } static bool -e1000e_receive_filter(E1000ECore *core, const uint8_t *buf, int size) +e1000e_receive_filter(E1000ECore *core, const void *buf) { - uint32_t rctl = core->mac[RCTL]; - - if (e1000x_is_vlan_packet(buf, core->mac[VET]) && - e1000x_vlan_rx_filter_enabled(core->mac)) { - uint16_t vid = lduw_be_p(buf + 14); - uint32_t vfta = ldl_le_p((uint32_t *)(core->mac + VFTA) + - ((vid >> 5) & 0x7f)); - if ((vfta & (1 << (vid & 0x1f))) == 0) { - trace_e1000e_rx_flt_vlan_mismatch(vid); - return false; - } else { - trace_e1000e_rx_flt_vlan_match(vid); - } - } - - switch (net_rx_pkt_get_packet_type(core->rx_pkt)) { - case ETH_PKT_UCAST: - if (rctl & E1000_RCTL_UPE) { - return true; /* promiscuous ucast */ - } - break; - - case ETH_PKT_BCAST: - if (rctl & E1000_RCTL_BAM) { - return true; /* broadcast enabled */ - } - break; - - case ETH_PKT_MCAST: - if (rctl & E1000_RCTL_MPE) { - return true; /* promiscuous mcast */ - } - break; - - default: - g_assert_not_reached(); - } - - return e1000x_rx_group_filter(core->mac, buf); + return (!e1000x_is_vlan_packet(buf, core->mac[VET]) || + e1000x_rx_vlan_filter(core->mac, PKT_GET_VLAN_HDR(buf))) && + e1000x_rx_group_filter(core->mac, buf); } static inline void -e1000e_read_lgcy_rx_descr(E1000ECore *core, uint8_t *desc, hwaddr *buff_addr) +e1000e_read_lgcy_rx_descr(E1000ECore *core, struct e1000_rx_desc *desc, + hwaddr *buff_addr) { - struct e1000_rx_desc *d = (struct e1000_rx_desc *) desc; - *buff_addr = le64_to_cpu(d->buffer_addr); + *buff_addr = le64_to_cpu(desc->buffer_addr); } static inline void -e1000e_read_ext_rx_descr(E1000ECore *core, uint8_t *desc, hwaddr *buff_addr) +e1000e_read_ext_rx_descr(E1000ECore *core, union e1000_rx_desc_extended *desc, + hwaddr *buff_addr) { - union e1000_rx_desc_extended *d = (union e1000_rx_desc_extended *) desc; - *buff_addr = le64_to_cpu(d->read.buffer_addr); + *buff_addr = le64_to_cpu(desc->read.buffer_addr); } static inline void -e1000e_read_ps_rx_descr(E1000ECore *core, uint8_t *desc, - hwaddr (*buff_addr)[MAX_PS_BUFFERS]) +e1000e_read_ps_rx_descr(E1000ECore *core, + union e1000_rx_desc_packet_split *desc, + hwaddr buff_addr[MAX_PS_BUFFERS]) { int i; - union e1000_rx_desc_packet_split *d = - (union e1000_rx_desc_packet_split *) desc; for (i = 0; i < MAX_PS_BUFFERS; i++) { - (*buff_addr)[i] = le64_to_cpu(d->read.buffer_addr[i]); + buff_addr[i] = le64_to_cpu(desc->read.buffer_addr[i]); } - trace_e1000e_rx_desc_ps_read((*buff_addr)[0], (*buff_addr)[1], - (*buff_addr)[2], (*buff_addr)[3]); + trace_e1000e_rx_desc_ps_read(buff_addr[0], buff_addr[1], + buff_addr[2], buff_addr[3]); } static inline void -e1000e_read_rx_descr(E1000ECore *core, uint8_t *desc, - hwaddr (*buff_addr)[MAX_PS_BUFFERS]) +e1000e_read_rx_descr(E1000ECore *core, union e1000_rx_desc_union *desc, + hwaddr buff_addr[MAX_PS_BUFFERS]) { if (e1000e_rx_use_legacy_descriptor(core)) { - e1000e_read_lgcy_rx_descr(core, desc, &(*buff_addr)[0]); - (*buff_addr)[1] = (*buff_addr)[2] = (*buff_addr)[3] = 0; + e1000e_read_lgcy_rx_descr(core, &desc->legacy, &buff_addr[0]); + buff_addr[1] = buff_addr[2] = buff_addr[3] = 0; } else { if (core->mac[RCTL] & E1000_RCTL_DTYP_PS) { - e1000e_read_ps_rx_descr(core, desc, buff_addr); + e1000e_read_ps_rx_descr(core, &desc->packet_split, buff_addr); } else { - e1000e_read_ext_rx_descr(core, desc, &(*buff_addr)[0]); - (*buff_addr)[1] = (*buff_addr)[2] = (*buff_addr)[3] = 0; + e1000e_read_ext_rx_descr(core, &desc->extended, &buff_addr[0]); + buff_addr[1] = buff_addr[2] = buff_addr[3] = 0; } } } @@ -1102,7 +1063,7 @@ static void e1000e_verify_csum_in_sw(E1000ECore *core, struct NetRxPkt *pkt, uint32_t *status_flags, - bool istcp, bool isudp) + EthL4HdrProto l4hdr_proto) { bool csum_valid; uint32_t csum_error; @@ -1123,20 +1084,21 @@ e1000e_verify_csum_in_sw(E1000ECore *core, return; } + if (l4hdr_proto != ETH_L4_HDR_PROTO_TCP && + l4hdr_proto != ETH_L4_HDR_PROTO_UDP) { + return; + } + if (!net_rx_pkt_validate_l4_csum(pkt, &csum_valid)) { trace_e1000e_rx_metadata_l4_csum_validation_failed(); return; } csum_error = csum_valid ? 0 : E1000_RXDEXT_STATERR_TCPE; + *status_flags |= E1000_RXD_STAT_TCPCS | csum_error; - if (istcp) { - *status_flags |= E1000_RXD_STAT_TCPCS | - csum_error; - } else if (isudp) { - *status_flags |= E1000_RXD_STAT_TCPCS | - E1000_RXD_STAT_UDPCS | - csum_error; + if (l4hdr_proto == ETH_L4_HDR_PROTO_UDP) { + *status_flags |= E1000_RXD_STAT_UDPCS; } } @@ -1165,7 +1127,8 @@ e1000e_build_rx_metadata(E1000ECore *core, uint16_t *vlan_tag) { struct virtio_net_hdr *vhdr; - bool isip4, isip6, istcp, isudp; + bool hasip4, hasip6; + EthL4HdrProto l4hdr_proto; uint32_t pkt_type; *status_flags = E1000_RXD_STAT_DD; @@ -1177,8 +1140,8 @@ e1000e_build_rx_metadata(E1000ECore *core, *status_flags |= E1000_RXD_STAT_EOP; - net_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); - trace_e1000e_rx_metadata_protocols(isip4, isip6, isudp, istcp); + net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto); + trace_e1000e_rx_metadata_protocols(hasip4, hasip6, l4hdr_proto); /* VLAN state */ if (net_rx_pkt_is_vlan_stripped(pkt)) { @@ -1194,24 +1157,25 @@ e1000e_build_rx_metadata(E1000ECore *core, *mrq = cpu_to_le32(rss_info->type | (rss_info->queue << 8)); trace_e1000e_rx_metadata_rss(*rss, *mrq); } - } else if (isip4) { + } else if (hasip4) { *status_flags |= E1000_RXD_STAT_IPIDV; *ip_id = cpu_to_le16(net_rx_pkt_get_ip_id(pkt)); trace_e1000e_rx_metadata_ip_id(*ip_id); } - if (istcp && e1000e_is_tcp_ack(core, pkt)) { + if (l4hdr_proto == ETH_L4_HDR_PROTO_TCP && e1000e_is_tcp_ack(core, pkt)) { *status_flags |= E1000_RXD_STAT_ACK; trace_e1000e_rx_metadata_ack(); } - if (isip6 && (core->mac[RFCTL] & E1000_RFCTL_IPV6_DIS)) { + if (hasip6 && (core->mac[RFCTL] & E1000_RFCTL_IPV6_DIS)) { trace_e1000e_rx_metadata_ipv6_filtering_disabled(); pkt_type = E1000_RXD_PKT_MAC; - } else if (istcp || isudp) { - pkt_type = isip4 ? E1000_RXD_PKT_IP4_XDP : E1000_RXD_PKT_IP6_XDP; - } else if (isip4 || isip6) { - pkt_type = isip4 ? E1000_RXD_PKT_IP4 : E1000_RXD_PKT_IP6; + } else if (l4hdr_proto == ETH_L4_HDR_PROTO_TCP || + l4hdr_proto == ETH_L4_HDR_PROTO_UDP) { + pkt_type = hasip4 ? E1000_RXD_PKT_IP4_XDP : E1000_RXD_PKT_IP6_XDP; + } else if (hasip4 || hasip6) { + pkt_type = hasip4 ? E1000_RXD_PKT_IP4 : E1000_RXD_PKT_IP6; } else { pkt_type = E1000_RXD_PKT_MAC; } @@ -1220,50 +1184,50 @@ e1000e_build_rx_metadata(E1000ECore *core, trace_e1000e_rx_metadata_pkt_type(pkt_type); /* RX CSO information */ - if (isip6 && (core->mac[RFCTL] & E1000_RFCTL_IPV6_XSUM_DIS)) { + if (hasip6 && (core->mac[RFCTL] & E1000_RFCTL_IPV6_XSUM_DIS)) { trace_e1000e_rx_metadata_ipv6_sum_disabled(); goto func_exit; } - if (!net_rx_pkt_has_virt_hdr(pkt)) { - trace_e1000e_rx_metadata_no_virthdr(); - e1000e_verify_csum_in_sw(core, pkt, status_flags, istcp, isudp); - goto func_exit; - } - vhdr = net_rx_pkt_get_vhdr(pkt); if (!(vhdr->flags & VIRTIO_NET_HDR_F_DATA_VALID) && !(vhdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) { trace_e1000e_rx_metadata_virthdr_no_csum_info(); - e1000e_verify_csum_in_sw(core, pkt, status_flags, istcp, isudp); + e1000e_verify_csum_in_sw(core, pkt, status_flags, l4hdr_proto); goto func_exit; } if (e1000e_rx_l3_cso_enabled(core)) { - *status_flags |= isip4 ? E1000_RXD_STAT_IPCS : 0; + *status_flags |= hasip4 ? E1000_RXD_STAT_IPCS : 0; } else { trace_e1000e_rx_metadata_l3_cso_disabled(); } if (e1000e_rx_l4_cso_enabled(core)) { - if (istcp) { + switch (l4hdr_proto) { + case ETH_L4_HDR_PROTO_TCP: *status_flags |= E1000_RXD_STAT_TCPCS; - } else if (isudp) { + break; + + case ETH_L4_HDR_PROTO_UDP: *status_flags |= E1000_RXD_STAT_TCPCS | E1000_RXD_STAT_UDPCS; + break; + + default: + break; } } else { trace_e1000e_rx_metadata_l4_cso_disabled(); } - trace_e1000e_rx_metadata_status_flags(*status_flags); - func_exit: + trace_e1000e_rx_metadata_status_flags(*status_flags); *status_flags = cpu_to_le32(*status_flags); } static inline void -e1000e_write_lgcy_rx_descr(E1000ECore *core, uint8_t *desc, +e1000e_write_lgcy_rx_descr(E1000ECore *core, struct e1000_rx_desc *desc, struct NetRxPkt *pkt, const E1000E_RSSInfo *rss_info, uint16_t length) @@ -1271,71 +1235,66 @@ e1000e_write_lgcy_rx_descr(E1000ECore *core, uint8_t *desc, uint32_t status_flags, rss, mrq; uint16_t ip_id; - struct e1000_rx_desc *d = (struct e1000_rx_desc *) desc; - assert(!rss_info->enabled); - d->length = cpu_to_le16(length); - d->csum = 0; + desc->length = cpu_to_le16(length); + desc->csum = 0; e1000e_build_rx_metadata(core, pkt, pkt != NULL, rss_info, &rss, &mrq, &status_flags, &ip_id, - &d->special); - d->errors = (uint8_t) (le32_to_cpu(status_flags) >> 24); - d->status = (uint8_t) le32_to_cpu(status_flags); + &desc->special); + desc->errors = (uint8_t) (le32_to_cpu(status_flags) >> 24); + desc->status = (uint8_t) le32_to_cpu(status_flags); } static inline void -e1000e_write_ext_rx_descr(E1000ECore *core, uint8_t *desc, +e1000e_write_ext_rx_descr(E1000ECore *core, union e1000_rx_desc_extended *desc, struct NetRxPkt *pkt, const E1000E_RSSInfo *rss_info, uint16_t length) { - union e1000_rx_desc_extended *d = (union e1000_rx_desc_extended *) desc; + memset(&desc->wb, 0, sizeof(desc->wb)); - memset(&d->wb, 0, sizeof(d->wb)); - - d->wb.upper.length = cpu_to_le16(length); + desc->wb.upper.length = cpu_to_le16(length); e1000e_build_rx_metadata(core, pkt, pkt != NULL, rss_info, - &d->wb.lower.hi_dword.rss, - &d->wb.lower.mrq, - &d->wb.upper.status_error, - &d->wb.lower.hi_dword.csum_ip.ip_id, - &d->wb.upper.vlan); + &desc->wb.lower.hi_dword.rss, + &desc->wb.lower.mrq, + &desc->wb.upper.status_error, + &desc->wb.lower.hi_dword.csum_ip.ip_id, + &desc->wb.upper.vlan); } static inline void -e1000e_write_ps_rx_descr(E1000ECore *core, uint8_t *desc, +e1000e_write_ps_rx_descr(E1000ECore *core, + union e1000_rx_desc_packet_split *desc, struct NetRxPkt *pkt, const E1000E_RSSInfo *rss_info, size_t ps_hdr_len, uint16_t(*written)[MAX_PS_BUFFERS]) { int i; - union e1000_rx_desc_packet_split *d = - (union e1000_rx_desc_packet_split *) desc; - memset(&d->wb, 0, sizeof(d->wb)); + memset(&desc->wb, 0, sizeof(desc->wb)); - d->wb.middle.length0 = cpu_to_le16((*written)[0]); + desc->wb.middle.length0 = cpu_to_le16((*written)[0]); for (i = 0; i < PS_PAGE_BUFFERS; i++) { - d->wb.upper.length[i] = cpu_to_le16((*written)[i + 1]); + desc->wb.upper.length[i] = cpu_to_le16((*written)[i + 1]); } e1000e_build_rx_metadata(core, pkt, pkt != NULL, rss_info, - &d->wb.lower.hi_dword.rss, - &d->wb.lower.mrq, - &d->wb.middle.status_error, - &d->wb.lower.hi_dword.csum_ip.ip_id, - &d->wb.middle.vlan); + &desc->wb.lower.hi_dword.rss, + &desc->wb.lower.mrq, + &desc->wb.middle.status_error, + &desc->wb.lower.hi_dword.csum_ip.ip_id, + &desc->wb.middle.vlan); - d->wb.upper.header_status = + desc->wb.upper.header_status = cpu_to_le16(ps_hdr_len | (ps_hdr_len ? E1000_RXDPS_HDRSTAT_HDRSP : 0)); trace_e1000e_rx_desc_ps_write((*written)[0], (*written)[1], @@ -1343,51 +1302,101 @@ e1000e_write_ps_rx_descr(E1000ECore *core, uint8_t *desc, } static inline void -e1000e_write_rx_descr(E1000ECore *core, uint8_t *desc, +e1000e_write_rx_descr(E1000ECore *core, union e1000_rx_desc_union *desc, struct NetRxPkt *pkt, const E1000E_RSSInfo *rss_info, size_t ps_hdr_len, uint16_t(*written)[MAX_PS_BUFFERS]) { if (e1000e_rx_use_legacy_descriptor(core)) { assert(ps_hdr_len == 0); - e1000e_write_lgcy_rx_descr(core, desc, pkt, rss_info, (*written)[0]); + e1000e_write_lgcy_rx_descr(core, &desc->legacy, pkt, rss_info, + (*written)[0]); } else { if (core->mac[RCTL] & E1000_RCTL_DTYP_PS) { - e1000e_write_ps_rx_descr(core, desc, pkt, rss_info, + e1000e_write_ps_rx_descr(core, &desc->packet_split, pkt, rss_info, ps_hdr_len, written); } else { assert(ps_hdr_len == 0); - e1000e_write_ext_rx_descr(core, desc, pkt, rss_info, + e1000e_write_ext_rx_descr(core, &desc->extended, pkt, rss_info, (*written)[0]); } } } -typedef struct e1000e_ba_state_st { +static inline void +e1000e_pci_dma_write_rx_desc(E1000ECore *core, dma_addr_t addr, + union e1000_rx_desc_union *desc, dma_addr_t len) +{ + PCIDevice *dev = core->owner; + + if (e1000e_rx_use_legacy_descriptor(core)) { + struct e1000_rx_desc *d = &desc->legacy; + size_t offset = offsetof(struct e1000_rx_desc, status); + uint8_t status = d->status; + + d->status &= ~E1000_RXD_STAT_DD; + pci_dma_write(dev, addr, desc, len); + + if (status & E1000_RXD_STAT_DD) { + d->status = status; + pci_dma_write(dev, addr + offset, &status, sizeof(status)); + } + } else { + if (core->mac[RCTL] & E1000_RCTL_DTYP_PS) { + union e1000_rx_desc_packet_split *d = &desc->packet_split; + size_t offset = offsetof(union e1000_rx_desc_packet_split, + wb.middle.status_error); + uint32_t status = d->wb.middle.status_error; + + d->wb.middle.status_error &= ~E1000_RXD_STAT_DD; + pci_dma_write(dev, addr, desc, len); + + if (status & E1000_RXD_STAT_DD) { + d->wb.middle.status_error = status; + pci_dma_write(dev, addr + offset, &status, sizeof(status)); + } + } else { + union e1000_rx_desc_extended *d = &desc->extended; + size_t offset = offsetof(union e1000_rx_desc_extended, + wb.upper.status_error); + uint32_t status = d->wb.upper.status_error; + + d->wb.upper.status_error &= ~E1000_RXD_STAT_DD; + pci_dma_write(dev, addr, desc, len); + + if (status & E1000_RXD_STAT_DD) { + d->wb.upper.status_error = status; + pci_dma_write(dev, addr + offset, &status, sizeof(status)); + } + } + } +} + +typedef struct E1000EBAState { uint16_t written[MAX_PS_BUFFERS]; uint8_t cur_idx; -} e1000e_ba_state; +} E1000EBAState; static inline void -e1000e_write_hdr_to_rx_buffers(E1000ECore *core, - hwaddr (*ba)[MAX_PS_BUFFERS], - e1000e_ba_state *bastate, - const char *data, - dma_addr_t data_len) +e1000e_write_hdr_frag_to_rx_buffers(E1000ECore *core, + hwaddr ba[MAX_PS_BUFFERS], + E1000EBAState *bastate, + const char *data, + dma_addr_t data_len) { assert(data_len <= core->rxbuf_sizes[0] - bastate->written[0]); - pci_dma_write(core->owner, (*ba)[0] + bastate->written[0], data, data_len); + pci_dma_write(core->owner, ba[0] + bastate->written[0], data, data_len); bastate->written[0] += data_len; bastate->cur_idx = 1; } static void -e1000e_write_to_rx_buffers(E1000ECore *core, - hwaddr (*ba)[MAX_PS_BUFFERS], - e1000e_ba_state *bastate, - const char *data, - dma_addr_t data_len) +e1000e_write_payload_frag_to_rx_buffers(E1000ECore *core, + hwaddr ba[MAX_PS_BUFFERS], + E1000EBAState *bastate, + const char *data, + dma_addr_t data_len) { while (data_len > 0) { uint32_t cur_buf_len = core->rxbuf_sizes[bastate->cur_idx]; @@ -1396,13 +1405,13 @@ e1000e_write_to_rx_buffers(E1000ECore *core, uint32_t bytes_to_write = MIN(data_len, cur_buf_bytes_left); trace_e1000e_rx_desc_buff_write(bastate->cur_idx, - (*ba)[bastate->cur_idx], + ba[bastate->cur_idx], bastate->written[bastate->cur_idx], data, bytes_to_write); pci_dma_write(core->owner, - (*ba)[bastate->cur_idx] + bastate->written[bastate->cur_idx], + ba[bastate->cur_idx] + bastate->written[bastate->cur_idx], data, bytes_to_write); bastate->written[bastate->cur_idx] += bytes_to_write; @@ -1418,28 +1427,14 @@ e1000e_write_to_rx_buffers(E1000ECore *core, } static void -e1000e_update_rx_stats(E1000ECore *core, - size_t data_size, - size_t data_fcs_size) +e1000e_update_rx_stats(E1000ECore *core, size_t pkt_size, size_t pkt_fcs_size) { - e1000x_update_rx_total_stats(core->mac, data_size, data_fcs_size); - - switch (net_rx_pkt_get_packet_type(core->rx_pkt)) { - case ETH_PKT_BCAST: - e1000x_inc_reg_if_not_full(core->mac, BPRC); - break; - - case ETH_PKT_MCAST: - e1000x_inc_reg_if_not_full(core->mac, MPRC); - break; - - default: - break; - } + eth_pkt_types_e pkt_type = net_rx_pkt_get_packet_type(core->rx_pkt); + e1000x_update_rx_total_stats(core->mac, pkt_type, pkt_size, pkt_fcs_size); } static inline bool -e1000e_rx_descr_threshold_hit(E1000ECore *core, const E1000E_RingInfo *rxi) +e1000e_rx_descr_threshold_hit(E1000ECore *core, const E1000ERingInfo *rxi) { return e1000e_ring_free_descr_num(core, rxi) == e1000e_ring_len(core, rxi) >> core->rxbuf_min_shift; @@ -1448,18 +1443,19 @@ e1000e_rx_descr_threshold_hit(E1000ECore *core, const E1000E_RingInfo *rxi) static bool e1000e_do_ps(E1000ECore *core, struct NetRxPkt *pkt, size_t *hdr_len) { - bool isip4, isip6, isudp, istcp; + bool hasip4, hasip6; + EthL4HdrProto l4hdr_proto; bool fragment; if (!e1000e_rx_use_ps_descriptor(core)) { return false; } - net_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); + net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto); - if (isip4) { + if (hasip4) { fragment = net_rx_pkt_get_ip4_info(pkt)->fragment; - } else if (isip6) { + } else if (hasip6) { fragment = net_rx_pkt_get_ip6_info(pkt)->fragment; } else { return false; @@ -1469,7 +1465,8 @@ e1000e_do_ps(E1000ECore *core, struct NetRxPkt *pkt, size_t *hdr_len) return false; } - if (!fragment && (isudp || istcp)) { + if (l4hdr_proto == ETH_L4_HDR_PROTO_TCP || + l4hdr_proto == ETH_L4_HDR_PROTO_UDP) { *hdr_len = net_rx_pkt_get_l5_hdr_offset(pkt); } else { *hdr_len = net_rx_pkt_get_l4_hdr_offset(pkt); @@ -1490,7 +1487,7 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, { PCIDevice *d = core->owner; dma_addr_t base; - uint8_t desc[E1000_MAX_RX_DESC_LEN]; + union e1000_rx_desc_union desc; size_t desc_size; size_t desc_offset = 0; size_t iov_ofs = 0; @@ -1498,7 +1495,7 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, struct iovec *iov = net_rx_pkt_get_iovec(pkt); size_t size = net_rx_pkt_get_total_len(pkt); size_t total_size = size + e1000x_fcs_len(core->mac); - const E1000E_RingInfo *rxi; + const E1000ERingInfo *rxi; size_t ps_hdr_len = 0; bool do_ps = e1000e_do_ps(core, pkt, &ps_hdr_len); bool is_first = true; @@ -1507,7 +1504,7 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, do { hwaddr ba[MAX_PS_BUFFERS]; - e1000e_ba_state bastate = { { 0 } }; + E1000EBAState bastate = { { 0 } }; bool is_last = false; desc_size = total_size - desc_offset; @@ -1526,7 +1523,7 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, trace_e1000e_rx_descr(rxi->idx, base, core->rx_desc_len); - e1000e_read_rx_descr(core, desc, &ba); + e1000e_read_rx_descr(core, &desc, ba); if (ba[0]) { if (desc_offset < size) { @@ -1545,8 +1542,10 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, iov_copy = MIN(ps_hdr_len - ps_hdr_copied, iov->iov_len - iov_ofs); - e1000e_write_hdr_to_rx_buffers(core, &ba, &bastate, - iov->iov_base, iov_copy); + e1000e_write_hdr_frag_to_rx_buffers(core, ba, + &bastate, + iov->iov_base, + iov_copy); copy_size -= iov_copy; ps_hdr_copied += iov_copy; @@ -1562,8 +1561,8 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, } else { /* Leave buffer 0 of each descriptor except first */ /* empty as per spec 7.1.5.1 */ - e1000e_write_hdr_to_rx_buffers(core, &ba, &bastate, - NULL, 0); + e1000e_write_hdr_frag_to_rx_buffers(core, ba, &bastate, + NULL, 0); } } @@ -1571,8 +1570,10 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, while (copy_size) { iov_copy = MIN(copy_size, iov->iov_len - iov_ofs); - e1000e_write_to_rx_buffers(core, &ba, &bastate, - iov->iov_base + iov_ofs, iov_copy); + e1000e_write_payload_frag_to_rx_buffers(core, ba, &bastate, + iov->iov_base + + iov_ofs, + iov_copy); copy_size -= iov_copy; iov_ofs += iov_copy; @@ -1584,7 +1585,7 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, if (desc_offset + desc_size >= total_size) { /* Simulate FCS checksum presence in the last descriptor */ - e1000e_write_to_rx_buffers(core, &ba, &bastate, + e1000e_write_payload_frag_to_rx_buffers(core, ba, &bastate, (const char *) &fcs_pad, e1000x_fcs_len(core->mac)); } } @@ -1596,9 +1597,9 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, is_last = true; } - e1000e_write_rx_descr(core, desc, is_last ? core->rx_pkt : NULL, + e1000e_write_rx_descr(core, &desc, is_last ? core->rx_pkt : NULL, rss_info, do_ps ? ps_hdr_len : 0, &bastate.written); - pci_dma_write(d, base, &desc, core->rx_desc_len); + e1000e_pci_dma_write_rx_desc(core, base, &desc, core->rx_desc_len); e1000e_ring_advance(core, rxi, core->rx_desc_len / E1000_MIN_RX_DESC_LEN); @@ -1611,26 +1612,26 @@ e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, static inline void e1000e_rx_fix_l4_csum(E1000ECore *core, struct NetRxPkt *pkt) { - if (net_rx_pkt_has_virt_hdr(pkt)) { - struct virtio_net_hdr *vhdr = net_rx_pkt_get_vhdr(pkt); + struct virtio_net_hdr *vhdr = net_rx_pkt_get_vhdr(pkt); - if (vhdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { - net_rx_pkt_fix_l4_csum(pkt); - } + if (vhdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { + net_rx_pkt_fix_l4_csum(pkt); } } ssize_t e1000e_receive_iov(E1000ECore *core, const struct iovec *iov, int iovcnt) { - static const int maximum_ethernet_hdr_len = (14 + 4); - /* Min. octets in an ethernet frame sans FCS */ - static const int min_buf_size = 60; + return e1000e_receive_internal(core, iov, iovcnt, core->has_vnet); +} - uint32_t n = 0; - uint8_t min_buf[min_buf_size]; +static ssize_t +e1000e_receive_internal(E1000ECore *core, const struct iovec *iov, int iovcnt, + bool has_vnet) +{ + uint32_t causes = 0; + uint8_t buf[ETH_ZLEN]; struct iovec min_iov; - uint8_t *filter_buf; size_t size, orig_size; size_t iov_ofs = 0; E1000E_RxRing rxr; @@ -1646,29 +1647,28 @@ e1000e_receive_iov(E1000ECore *core, const struct iovec *iov, int iovcnt) } /* Pull virtio header in */ - if (core->has_vnet) { + if (has_vnet) { net_rx_pkt_set_vhdr_iovec(core->rx_pkt, iov, iovcnt); iov_ofs = sizeof(struct virtio_net_hdr); + } else { + net_rx_pkt_unset_vhdr(core->rx_pkt); } - filter_buf = iov->iov_base + iov_ofs; orig_size = iov_size(iov, iovcnt); size = orig_size - iov_ofs; /* Pad to minimum Ethernet frame length */ - if (size < sizeof(min_buf)) { - iov_to_buf(iov, iovcnt, iov_ofs, min_buf, size); - memset(&min_buf[size], 0, sizeof(min_buf) - size); + if (size < sizeof(buf)) { + iov_to_buf(iov, iovcnt, iov_ofs, buf, size); + memset(&buf[size], 0, sizeof(buf) - size); e1000x_inc_reg_if_not_full(core->mac, RUC); - min_iov.iov_base = filter_buf = min_buf; - min_iov.iov_len = size = sizeof(min_buf); + min_iov.iov_base = buf; + min_iov.iov_len = size = sizeof(buf); iovcnt = 1; iov = &min_iov; iov_ofs = 0; - } else if (iov->iov_len < maximum_ethernet_hdr_len) { - /* This is very unlikely, but may happen. */ - iov_to_buf(iov, iovcnt, iov_ofs, min_buf, maximum_ethernet_hdr_len); - filter_buf = min_buf; + } else { + iov_to_buf(iov, iovcnt, iov_ofs, buf, ETH_HLEN + 4); } /* Discard oversized packets if !LPE and !SBP. */ @@ -1677,21 +1677,20 @@ e1000e_receive_iov(E1000ECore *core, const struct iovec *iov, int iovcnt) } net_rx_pkt_set_packet_type(core->rx_pkt, - get_eth_packet_type(PKT_GET_ETH_HDR(filter_buf))); + get_eth_packet_type(PKT_GET_ETH_HDR(buf))); - if (!e1000e_receive_filter(core, filter_buf, size)) { + if (!e1000e_receive_filter(core, buf)) { trace_e1000e_rx_flt_dropped(); return orig_size; } net_rx_pkt_attach_iovec_ex(core->rx_pkt, iov, iovcnt, iov_ofs, - e1000x_vlan_enabled(core->mac), core->mac[VET]); + e1000x_vlan_enabled(core->mac) ? 0 : -1, + core->mac[VET], 0); e1000e_rss_parse_packet(core, core->rx_pkt, &rss_info); e1000e_rx_ring_init(core, &rxr, rss_info.queue); - trace_e1000e_rx_rss_dispatched_to_queue(rxr.i->idx); - total_size = net_rx_pkt_get_total_len(core->rx_pkt) + e1000x_fcs_len(core->mac); @@ -1704,32 +1703,32 @@ e1000e_receive_iov(E1000ECore *core, const struct iovec *iov, int iovcnt) /* Perform small receive detection (RSRPD) */ if (total_size < core->mac[RSRPD]) { - n |= E1000_ICS_SRPD; + causes |= E1000_ICS_SRPD; } /* Perform ACK receive detection */ if (!(core->mac[RFCTL] & E1000_RFCTL_ACK_DIS) && (e1000e_is_tcp_ack(core, core->rx_pkt))) { - n |= E1000_ICS_ACK; + causes |= E1000_ICS_ACK; } /* Check if receive descriptor minimum threshold hit */ rdmts_hit = e1000e_rx_descr_threshold_hit(core, rxr.i); - n |= e1000e_rx_wb_interrupt_cause(core, rxr.i->idx, rdmts_hit); + causes |= e1000e_rx_wb_interrupt_cause(core, rxr.i->idx, rdmts_hit); - trace_e1000e_rx_written_to_guest(n); + trace_e1000e_rx_written_to_guest(rxr.i->idx); } else { - n |= E1000_ICS_RXO; + causes |= E1000_ICS_RXO; retval = 0; - trace_e1000e_rx_not_written_to_guest(n); + trace_e1000e_rx_not_written_to_guest(rxr.i->idx); } - if (!e1000e_intrmgr_delay_rx_causes(core, &n)) { - trace_e1000e_rx_interrupt_set(n); - e1000e_set_interrupt_cause(core, n); + if (!e1000e_intrmgr_delay_rx_causes(core, &causes)) { + trace_e1000e_rx_interrupt_set(causes); + e1000e_set_interrupt_cause(core, causes); } else { - trace_e1000e_rx_interrupt_delayed(n); + trace_e1000e_rx_interrupt_delayed(causes); } return retval; @@ -1738,13 +1737,13 @@ e1000e_receive_iov(E1000ECore *core, const struct iovec *iov, int iovcnt) static inline bool e1000e_have_autoneg(E1000ECore *core) { - return core->phy[0][PHY_CTRL] & MII_CR_AUTO_NEG_EN; + return core->phy[0][MII_BMCR] & MII_BMCR_AUTOEN; } static void e1000e_update_flowctl_status(E1000ECore *core) { if (e1000e_have_autoneg(core) && - core->phy[0][PHY_STATUS] & MII_SR_AUTONEG_COMPLETE) { + core->phy[0][MII_BMSR] & MII_BMSR_AN_COMP) { trace_e1000e_link_autoneg_flowctl(true); core->mac[CTRL] |= E1000_CTRL_TFCE | E1000_CTRL_RFCE; } else { @@ -1762,12 +1761,12 @@ e1000e_link_down(E1000ECore *core) static inline void e1000e_set_phy_ctrl(E1000ECore *core, int index, uint16_t val) { - /* bits 0-5 reserved; MII_CR_[RESTART_AUTO_NEG,RESET] are self clearing */ - core->phy[0][PHY_CTRL] = val & ~(0x3f | - MII_CR_RESET | - MII_CR_RESTART_AUTO_NEG); + /* bits 0-5 reserved; MII_BMCR_[ANRESTART,RESET] are self clearing */ + core->phy[0][MII_BMCR] = val & ~(0x3f | + MII_BMCR_RESET | + MII_BMCR_ANRESTART); - if ((val & MII_CR_RESTART_AUTO_NEG) && + if ((val & MII_BMCR_ANRESTART) && e1000e_have_autoneg(core)) { e1000x_restart_autoneg(core->mac, core->phy[0], core->autoneg_timer); } @@ -1801,7 +1800,7 @@ e1000e_core_set_link_status(E1000ECore *core) e1000x_update_regs_on_link_down(core->mac, core->phy[0]); } else { if (e1000e_have_autoneg(core) && - !(core->phy[0][PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { + !(core->phy[0][MII_BMSR] & MII_BMSR_AN_COMP)) { e1000x_restart_autoneg(core->mac, core->phy[0], core->autoneg_timer); } else { @@ -1834,7 +1833,7 @@ e1000e_set_ctrl(E1000ECore *core, int index, uint32_t val) if (val & E1000_CTRL_RST) { trace_e1000e_core_ctrl_sw_reset(); - e1000x_reset_mac_addr(core->owner_nic, core->mac, core->permanent_mac); + e1000e_reset(core, true); } if (val & E1000_CTRL_PHY_RST) { @@ -1943,27 +1942,18 @@ static void(*e1000e_phyreg_writeops[E1000E_PHY_PAGES][E1000E_PHY_PAGE_SIZE]) (E1000ECore *, int, uint16_t) = { [0] = { - [PHY_CTRL] = e1000e_set_phy_ctrl, + [MII_BMCR] = e1000e_set_phy_ctrl, [PHY_PAGE] = e1000e_set_phy_page, [PHY_OEM_BITS] = e1000e_set_phy_oem_bits } }; -static inline void -e1000e_clear_ims_bits(E1000ECore *core, uint32_t bits) -{ - trace_e1000e_irq_clear_ims(bits, core->mac[IMS], core->mac[IMS] & ~bits); - core->mac[IMS] &= ~bits; -} - static inline bool -e1000e_postpone_interrupt(bool *interrupt_pending, - E1000IntrDelayTimer *timer) +e1000e_postpone_interrupt(E1000IntrDelayTimer *timer) { if (timer->running) { trace_e1000e_irq_postponed_by_xitr(timer->delay_reg << 2); - *interrupt_pending = true; return true; } @@ -1977,14 +1967,13 @@ e1000e_postpone_interrupt(bool *interrupt_pending, static inline bool e1000e_itr_should_postpone(E1000ECore *core) { - return e1000e_postpone_interrupt(&core->itr_intr_pending, &core->itr); + return e1000e_postpone_interrupt(&core->itr); } static inline bool e1000e_eitr_should_postpone(E1000ECore *core, int idx) { - return e1000e_postpone_interrupt(&core->eitr_intr_pending[idx], - &core->eitr[idx]); + return e1000e_postpone_interrupt(&core->eitr[idx]); } static void @@ -2016,7 +2005,6 @@ e1000e_msix_notify_one(E1000ECore *core, uint32_t cause, uint32_t int_cfg) effective_eiac = core->mac[EIAC] & cause; core->mac[ICR] &= ~effective_eiac; - core->msi_causes_pending &= ~effective_eiac; if (!(core->mac[CTRL_EXT] & E1000_CTRL_EXT_IAME)) { core->mac[IMS] &= ~effective_eiac; @@ -2108,33 +2096,17 @@ e1000e_fix_icr_asserted(E1000ECore *core) trace_e1000e_irq_fix_icr_asserted(core->mac[ICR]); } -static void -e1000e_send_msi(E1000ECore *core, bool msix) +static void e1000e_raise_interrupts(E1000ECore *core, + size_t index, uint32_t causes) { - uint32_t causes = core->mac[ICR] & core->mac[IMS] & ~E1000_ICR_ASSERTED; - - core->msi_causes_pending &= causes; - causes ^= core->msi_causes_pending; - if (causes == 0) { - return; - } - core->msi_causes_pending |= causes; - - if (msix) { - e1000e_msix_notify(core, causes); - } else { - if (!e1000e_itr_should_postpone(core)) { - trace_e1000e_irq_msi_notify(causes); - msi_notify(core->owner, 0); - } - } -} - -static void -e1000e_update_interrupt_state(E1000ECore *core) -{ - bool interrupts_pending; bool is_msix = msix_enabled(core->owner); + uint32_t old_causes = core->mac[IMS] & core->mac[ICR]; + uint32_t raised_causes; + + trace_e1000e_irq_set(index << 2, + core->mac[index], core->mac[index] | causes); + + core->mac[index] |= causes; /* Set ICR[OTHER] for MSI-X */ if (is_msix) { @@ -2156,40 +2128,58 @@ e1000e_update_interrupt_state(E1000ECore *core) */ core->mac[ICS] = core->mac[ICR]; - interrupts_pending = (core->mac[IMS] & core->mac[ICR]) ? true : false; - if (!interrupts_pending) { - core->msi_causes_pending = 0; + trace_e1000e_irq_pending_interrupts(core->mac[ICR] & core->mac[IMS], + core->mac[ICR], core->mac[IMS]); + + raised_causes = core->mac[IMS] & core->mac[ICR] & ~old_causes; + if (!raised_causes) { + return; } + if (is_msix) { + e1000e_msix_notify(core, raised_causes & ~E1000_ICR_ASSERTED); + } else if (!e1000e_itr_should_postpone(core)) { + if (msi_enabled(core->owner)) { + trace_e1000e_irq_msi_notify(raised_causes); + msi_notify(core->owner, 0); + } else { + e1000e_raise_legacy_irq(core); + } + } +} + +static void e1000e_lower_interrupts(E1000ECore *core, + size_t index, uint32_t causes) +{ + trace_e1000e_irq_clear(index << 2, + core->mac[index], core->mac[index] & ~causes); + + core->mac[index] &= ~causes; + + /* + * Make sure ICR and ICS registers have the same value. + * The spec says that the ICS register is write-only. However in practice, + * on real hardware ICS is readable, and for reads it has the same value as + * ICR (except that ICS does not have the clear on read behaviour of ICR). + * + * The VxWorks PRO/1000 driver uses this behaviour. + */ + core->mac[ICS] = core->mac[ICR]; + trace_e1000e_irq_pending_interrupts(core->mac[ICR] & core->mac[IMS], core->mac[ICR], core->mac[IMS]); - if (is_msix || msi_enabled(core->owner)) { - if (interrupts_pending) { - e1000e_send_msi(core, is_msix); - } - } else { - if (interrupts_pending) { - if (!e1000e_itr_should_postpone(core)) { - e1000e_raise_legacy_irq(core); - } - } else { - e1000e_lower_legacy_irq(core); - } + if (!(core->mac[IMS] & core->mac[ICR]) && + !msix_enabled(core->owner) && !msi_enabled(core->owner)) { + e1000e_lower_legacy_irq(core); } } static void e1000e_set_interrupt_cause(E1000ECore *core, uint32_t val) { - trace_e1000e_irq_set_cause_entry(val, core->mac[ICR]); - val |= e1000e_intmgr_collect_delayed_causes(core); - core->mac[ICR] |= val; - - trace_e1000e_irq_set_cause_exit(val, core->mac[ICR]); - - e1000e_update_interrupt_state(core); + e1000e_raise_interrupts(core, ICR, val); } static inline void @@ -2215,19 +2205,19 @@ e1000e_get_reg_index_with_offset(const uint16_t *mac_reg_access, hwaddr addr) static const char e1000e_phy_regcap[E1000E_PHY_PAGES][0x20] = { [0] = { - [PHY_CTRL] = PHY_ANYPAGE | PHY_RW, - [PHY_STATUS] = PHY_ANYPAGE | PHY_R, - [PHY_ID1] = PHY_ANYPAGE | PHY_R, - [PHY_ID2] = PHY_ANYPAGE | PHY_R, - [PHY_AUTONEG_ADV] = PHY_ANYPAGE | PHY_RW, - [PHY_LP_ABILITY] = PHY_ANYPAGE | PHY_R, - [PHY_AUTONEG_EXP] = PHY_ANYPAGE | PHY_R, - [PHY_NEXT_PAGE_TX] = PHY_ANYPAGE | PHY_RW, - [PHY_LP_NEXT_PAGE] = PHY_ANYPAGE | PHY_R, - [PHY_1000T_CTRL] = PHY_ANYPAGE | PHY_RW, - [PHY_1000T_STATUS] = PHY_ANYPAGE | PHY_R, - [PHY_EXT_STATUS] = PHY_ANYPAGE | PHY_R, - [PHY_PAGE] = PHY_ANYPAGE | PHY_RW, + [MII_BMCR] = PHY_ANYPAGE | PHY_RW, + [MII_BMSR] = PHY_ANYPAGE | PHY_R, + [MII_PHYID1] = PHY_ANYPAGE | PHY_R, + [MII_PHYID2] = PHY_ANYPAGE | PHY_R, + [MII_ANAR] = PHY_ANYPAGE | PHY_RW, + [MII_ANLPAR] = PHY_ANYPAGE | PHY_R, + [MII_ANER] = PHY_ANYPAGE | PHY_R, + [MII_ANNP] = PHY_ANYPAGE | PHY_RW, + [MII_ANLPRNP] = PHY_ANYPAGE | PHY_R, + [MII_CTRL1000] = PHY_ANYPAGE | PHY_RW, + [MII_STAT1000] = PHY_ANYPAGE | PHY_R, + [MII_EXTSTAT] = PHY_ANYPAGE | PHY_R, + [PHY_PAGE] = PHY_ANYPAGE | PHY_RW, [PHY_COPPER_CTRL1] = PHY_RW, [PHY_COPPER_STAT1] = PHY_R, @@ -2380,17 +2370,19 @@ e1000e_set_fcrtl(E1000ECore *core, int index, uint32_t val) core->mac[FCRTL] = val & 0x8000FFF8; } -static inline void -e1000e_set_16bit(E1000ECore *core, int index, uint32_t val) -{ - core->mac[index] = val & 0xffff; -} +#define E1000E_LOW_BITS_SET_FUNC(num) \ + static void \ + e1000e_set_##num##bit(E1000ECore *core, int index, uint32_t val) \ + { \ + core->mac[index] = val & (BIT(num) - 1); \ + } -static void -e1000e_set_12bit(E1000ECore *core, int index, uint32_t val) -{ - core->mac[index] = val & 0xfff; -} +E1000E_LOW_BITS_SET_FUNC(4) +E1000E_LOW_BITS_SET_FUNC(6) +E1000E_LOW_BITS_SET_FUNC(11) +E1000E_LOW_BITS_SET_FUNC(12) +E1000E_LOW_BITS_SET_FUNC(13) +E1000E_LOW_BITS_SET_FUNC(16) static void e1000e_set_vet(E1000ECore *core, int index, uint32_t val) @@ -2453,29 +2445,27 @@ e1000e_set_ics(E1000ECore *core, int index, uint32_t val) static void e1000e_set_icr(E1000ECore *core, int index, uint32_t val) { - uint32_t icr = 0; if ((core->mac[ICR] & E1000_ICR_ASSERTED) && (core->mac[CTRL_EXT] & E1000_CTRL_EXT_IAME)) { trace_e1000e_irq_icr_process_iame(); - e1000e_clear_ims_bits(core, core->mac[IAM]); + e1000e_lower_interrupts(core, IMS, core->mac[IAM]); } - icr = core->mac[ICR] & ~val; - /* Windows driver expects that the "receive overrun" bit and other + /* + * Windows driver expects that the "receive overrun" bit and other * ones to be cleared when the "Other" bit (#24) is cleared. */ - icr = (val & E1000_ICR_OTHER) ? (icr & ~E1000_ICR_OTHER_CAUSES) : icr; - trace_e1000e_irq_icr_write(val, core->mac[ICR], icr); - core->mac[ICR] = icr; - e1000e_update_interrupt_state(core); + if (val & E1000_ICR_OTHER) { + val |= E1000_ICR_OTHER_CAUSES; + } + e1000e_lower_interrupts(core, ICR, val); } static void e1000e_set_imc(E1000ECore *core, int index, uint32_t val) { trace_e1000e_irq_ims_clear_set_imc(val); - e1000e_clear_ims_bits(core, val); - e1000e_update_interrupt_state(core); + e1000e_lower_interrupts(core, IMS, val); } static void @@ -2496,9 +2486,6 @@ e1000e_set_ims(E1000ECore *core, int index, uint32_t val) uint32_t valid_val = val & ims_valid_mask; - trace_e1000e_irq_set_ims(val, core->mac[IMS], core->mac[IMS] | valid_val); - core->mac[IMS] |= valid_val; - if ((valid_val & ims_ext_mask) && (core->mac[CTRL_EXT] & E1000_CTRL_EXT_PBA_CLR) && msix_enabled(core->owner)) { @@ -2511,7 +2498,7 @@ e1000e_set_ims(E1000ECore *core, int index, uint32_t val) e1000e_intrmgr_fire_all_timers(core); } - e1000e_update_interrupt_state(core); + e1000e_raise_interrupts(core, IMS, valid_val); } static void @@ -2560,27 +2547,11 @@ e1000e_mac_ims_read(E1000ECore *core, int index) return core->mac[IMS]; } -#define E1000E_LOW_BITS_READ_FUNC(num) \ - static uint32_t \ - e1000e_mac_low##num##_read(E1000ECore *core, int index) \ - { \ - return core->mac[index] & (BIT(num) - 1); \ - } \ - -#define E1000E_LOW_BITS_READ(num) \ - e1000e_mac_low##num##_read - -E1000E_LOW_BITS_READ_FUNC(4); -E1000E_LOW_BITS_READ_FUNC(6); -E1000E_LOW_BITS_READ_FUNC(11); -E1000E_LOW_BITS_READ_FUNC(13); -E1000E_LOW_BITS_READ_FUNC(16); - static uint32_t e1000e_mac_swsm_read(E1000ECore *core, int index) { uint32_t val = core->mac[SWSM]; - core->mac[SWSM] = val | 1; + core->mac[SWSM] = val | E1000_SWSM_SMBI; return val; } @@ -2600,28 +2571,51 @@ static uint32_t e1000e_mac_icr_read(E1000ECore *core, int index) { uint32_t ret = core->mac[ICR]; - trace_e1000e_irq_icr_read_entry(ret); if (core->mac[IMS] == 0) { trace_e1000e_irq_icr_clear_zero_ims(); - core->mac[ICR] = 0; + e1000e_lower_interrupts(core, ICR, 0xffffffff); } if (!msix_enabled(core->owner)) { trace_e1000e_irq_icr_clear_nonmsix_icr_read(); - core->mac[ICR] = 0; + e1000e_lower_interrupts(core, ICR, 0xffffffff); } - if ((core->mac[ICR] & E1000_ICR_ASSERTED) && - (core->mac[CTRL_EXT] & E1000_CTRL_EXT_IAME)) { - trace_e1000e_irq_icr_clear_iame(); - core->mac[ICR] = 0; - trace_e1000e_irq_icr_process_iame(); - e1000e_clear_ims_bits(core, core->mac[IAM]); + if (core->mac[ICR] & E1000_ICR_ASSERTED) { + if (core->mac[CTRL_EXT] & E1000_CTRL_EXT_IAME) { + trace_e1000e_irq_icr_clear_iame(); + e1000e_lower_interrupts(core, ICR, 0xffffffff); + trace_e1000e_irq_icr_process_iame(); + e1000e_lower_interrupts(core, IMS, core->mac[IAM]); + } + + /* + * The datasheet does not say what happens when interrupt was asserted + * (ICR.INT_ASSERT=1) and auto mask is *not* active. + * However, section of 13.3.27 the PCIe* GbE Controllers Open Source + * Software Developer’s Manual, which were written for older devices, + * namely 631xESB/632xESB, 82563EB/82564EB, 82571EB/82572EI & + * 82573E/82573V/82573L, does say: + * > If IMS = 0b, then the ICR register is always clear-on-read. If IMS + * > is not 0b, but some ICR bit is set where the corresponding IMS bit + * > is not set, then a read does not clear the ICR register. For + * > example, if IMS = 10101010b and ICR = 01010101b, then a read to the + * > ICR register does not clear it. If IMS = 10101010b and + * > ICR = 0101011b, then a read to the ICR register clears it entirely + * > (ICR.INT_ASSERTED = 1b). + * + * Linux does no longer activate auto mask since commit + * 0a8047ac68e50e4ccbadcfc6b6b070805b976885 and the real hardware + * clears ICR even in such a case so we also should do so. + */ + if (core->mac[ICR] & core->mac[IMS]) { + trace_e1000e_irq_icr_clear_icr_bit_ims(core->mac[ICR], + core->mac[IMS]); + e1000e_lower_interrupts(core, ICR, 0xffffffff); + } } - trace_e1000e_irq_icr_read_exit(core->mac[ICR]); - e1000e_update_interrupt_state(core); return ret; } @@ -2836,7 +2830,7 @@ e1000e_update_rx_offloads(E1000ECore *core) if (core->has_vnet) { qemu_set_offload(qemu_get_queue(core->owner_nic)->peer, - cso_state, 0, 0, 0, 0); + cso_state, 0, 0, 0, 0, 0, 0); } } @@ -2854,6 +2848,35 @@ e1000e_set_gcr(E1000ECore *core, int index, uint32_t val) core->mac[GCR] = (val & ~E1000_GCR_RO_BITS) | ro_bits; } +static uint32_t e1000e_get_systiml(E1000ECore *core, int index) +{ + e1000x_timestamp(core->mac, core->timadj, SYSTIML, SYSTIMH); + return core->mac[SYSTIML]; +} + +static uint32_t e1000e_get_rxsatrh(E1000ECore *core, int index) +{ + core->mac[TSYNCRXCTL] &= ~E1000_TSYNCRXCTL_VALID; + return core->mac[RXSATRH]; +} + +static uint32_t e1000e_get_txstmph(E1000ECore *core, int index) +{ + core->mac[TSYNCTXCTL] &= ~E1000_TSYNCTXCTL_VALID; + return core->mac[TXSTMPH]; +} + +static void e1000e_set_timinca(E1000ECore *core, int index, uint32_t val) +{ + e1000x_set_timinca(core->mac, &core->timadj, val); +} + +static void e1000e_set_timadjh(E1000ECore *core, int index, uint32_t val) +{ + core->mac[TIMADJH] = val; + core->timadj += core->mac[TIMADJL] | ((int64_t)core->mac[TIMADJH] << 32); +} + #define e1000e_getreg(x) [x] = e1000e_mac_readreg typedef uint32_t (*readops)(E1000ECore *, int); static const readops e1000e_macreg_readops[] = { @@ -2869,7 +2892,19 @@ static const readops e1000e_macreg_readops[] = { e1000e_getreg(LATECOL), e1000e_getreg(SEQEC), e1000e_getreg(XONTXC), + e1000e_getreg(AIT), + e1000e_getreg(TDFH), + e1000e_getreg(TDFT), + e1000e_getreg(TDFHS), + e1000e_getreg(TDFTS), + e1000e_getreg(TDFPC), e1000e_getreg(WUS), + e1000e_getreg(PBS), + e1000e_getreg(RDFH), + e1000e_getreg(RDFT), + e1000e_getreg(RDFHS), + e1000e_getreg(RDFTS), + e1000e_getreg(RDFPC), e1000e_getreg(GORCL), e1000e_getreg(MGTPRC), e1000e_getreg(EERD), @@ -2897,7 +2932,6 @@ static const readops e1000e_macreg_readops[] = { e1000e_getreg(GSCL_2), e1000e_getreg(RDBAH1), e1000e_getreg(FLSWDATA), - e1000e_getreg(RXSATRH), e1000e_getreg(TIPG), e1000e_getreg(FLMNGCTL), e1000e_getreg(FLMNGCNT), @@ -2938,7 +2972,6 @@ static const readops e1000e_macreg_readops[] = { e1000e_getreg(FLSWCTL), e1000e_getreg(RXDCTL1), e1000e_getreg(RXSATRL), - e1000e_getreg(SYSTIML), e1000e_getreg(RXUDP), e1000e_getreg(TORL), e1000e_getreg(TDLEN1), @@ -2978,7 +3011,6 @@ static const readops e1000e_macreg_readops[] = { e1000e_getreg(FLOL), e1000e_getreg(RXDCTL), e1000e_getreg(RXSTMPL), - e1000e_getreg(TXSTMPH), e1000e_getreg(TIMADJH), e1000e_getreg(FCRTL), e1000e_getreg(TDBAH), @@ -3005,16 +3037,9 @@ static const readops e1000e_macreg_readops[] = { [MPTC] = e1000e_mac_read_clr4, [IAC] = e1000e_mac_read_clr4, [ICR] = e1000e_mac_icr_read, - [RDFH] = E1000E_LOW_BITS_READ(13), - [RDFHS] = E1000E_LOW_BITS_READ(13), - [RDFPC] = E1000E_LOW_BITS_READ(13), - [TDFH] = E1000E_LOW_BITS_READ(13), - [TDFHS] = E1000E_LOW_BITS_READ(13), [STATUS] = e1000e_get_status, [TARC0] = e1000e_get_tarc, - [PBS] = E1000E_LOW_BITS_READ(6), [ICS] = e1000e_mac_ics_read, - [AIT] = E1000E_LOW_BITS_READ(16), [TORH] = e1000e_mac_read_clr8, [GORCH] = e1000e_mac_read_clr8, [PRC127] = e1000e_mac_read_clr4, @@ -3030,27 +3055,25 @@ static const readops e1000e_macreg_readops[] = { [BPTC] = e1000e_mac_read_clr4, [TSCTC] = e1000e_mac_read_clr4, [ITR] = e1000e_mac_itr_read, - [RDFT] = E1000E_LOW_BITS_READ(13), - [RDFTS] = E1000E_LOW_BITS_READ(13), - [TDFPC] = E1000E_LOW_BITS_READ(13), - [TDFT] = E1000E_LOW_BITS_READ(13), - [TDFTS] = E1000E_LOW_BITS_READ(13), [CTRL] = e1000e_get_ctrl, [TARC1] = e1000e_get_tarc, [SWSM] = e1000e_mac_swsm_read, [IMS] = e1000e_mac_ims_read, + [SYSTIML] = e1000e_get_systiml, + [RXSATRH] = e1000e_get_rxsatrh, + [TXSTMPH] = e1000e_get_txstmph, [CRCERRS ... MPC] = e1000e_mac_readreg, [IP6AT ... IP6AT + 3] = e1000e_mac_readreg, [IP4AT ... IP4AT + 6] = e1000e_mac_readreg, [RA ... RA + 31] = e1000e_mac_readreg, [WUPM ... WUPM + 31] = e1000e_mac_readreg, - [MTA ... MTA + 127] = e1000e_mac_readreg, - [VFTA ... VFTA + 127] = e1000e_mac_readreg, - [FFMT ... FFMT + 254] = E1000E_LOW_BITS_READ(4), + [MTA ... MTA + E1000_MC_TBL_SIZE - 1] = e1000e_mac_readreg, + [VFTA ... VFTA + E1000_VLAN_FILTER_TBL_SIZE - 1] = e1000e_mac_readreg, + [FFMT ... FFMT + 254] = e1000e_mac_readreg, [FFVT ... FFVT + 254] = e1000e_mac_readreg, [MDEF ... MDEF + 7] = e1000e_mac_readreg, - [FFLT ... FFLT + 10] = E1000E_LOW_BITS_READ(11), + [FFLT ... FFLT + 10] = e1000e_mac_readreg, [FTFT ... FTFT + 254] = e1000e_mac_readreg, [PBM ... PBM + 10239] = e1000e_mac_readreg, [RETA ... RETA + 31] = e1000e_mac_readreg, @@ -3073,22 +3096,10 @@ static const writeops e1000e_macreg_writeops[] = { e1000e_putreg(LEDCTL), e1000e_putreg(FCAL), e1000e_putreg(FCRUC), - e1000e_putreg(AIT), - e1000e_putreg(TDFH), - e1000e_putreg(TDFT), - e1000e_putreg(TDFHS), - e1000e_putreg(TDFTS), - e1000e_putreg(TDFPC), e1000e_putreg(WUC), e1000e_putreg(WUS), - e1000e_putreg(RDFH), - e1000e_putreg(RDFT), - e1000e_putreg(RDFHS), - e1000e_putreg(RDFTS), - e1000e_putreg(RDFPC), e1000e_putreg(IPAV), e1000e_putreg(TDBAH1), - e1000e_putreg(TIMINCA), e1000e_putreg(IAM), e1000e_putreg(EIAC), e1000e_putreg(IVAR), @@ -3096,7 +3107,6 @@ static const writeops e1000e_macreg_writeops[] = { e1000e_putreg(TARC1), e1000e_putreg(FLSWDATA), e1000e_putreg(POEMB), - e1000e_putreg(PBS), e1000e_putreg(MFUTP01), e1000e_putreg(MFUTP23), e1000e_putreg(MANC), @@ -3132,7 +3142,6 @@ static const writeops e1000e_macreg_writeops[] = { e1000e_putreg(SYSTIML), e1000e_putreg(SYSTIMH), e1000e_putreg(TIMADJL), - e1000e_putreg(TIMADJH), e1000e_putreg(RXUDP), e1000e_putreg(RXCFGL), e1000e_putreg(TSYNCRXCTL), @@ -3161,6 +3170,18 @@ static const writeops e1000e_macreg_writeops[] = { [TADV] = e1000e_set_16bit, [ITR] = e1000e_set_itr, [EERD] = e1000e_set_eerd, + [AIT] = e1000e_set_16bit, + [TDFH] = e1000e_set_13bit, + [TDFT] = e1000e_set_13bit, + [TDFHS] = e1000e_set_13bit, + [TDFTS] = e1000e_set_13bit, + [TDFPC] = e1000e_set_13bit, + [RDFH] = e1000e_set_13bit, + [RDFHS] = e1000e_set_13bit, + [RDFT] = e1000e_set_13bit, + [RDFTS] = e1000e_set_13bit, + [RDFPC] = e1000e_set_13bit, + [PBS] = e1000e_set_6bit, [GCR] = e1000e_set_gcr, [PSRCTL] = e1000e_set_psrctl, [RXCSUM] = e1000e_set_rxcsum, @@ -3193,18 +3214,20 @@ static const writeops e1000e_macreg_writeops[] = { [CTRL_DUP] = e1000e_set_ctrl, [RFCTL] = e1000e_set_rfctl, [RA + 1] = e1000e_mac_setmacaddr, + [TIMINCA] = e1000e_set_timinca, + [TIMADJH] = e1000e_set_timadjh, [IP6AT ... IP6AT + 3] = e1000e_mac_writereg, [IP4AT ... IP4AT + 6] = e1000e_mac_writereg, [RA + 2 ... RA + 31] = e1000e_mac_writereg, [WUPM ... WUPM + 31] = e1000e_mac_writereg, - [MTA ... MTA + 127] = e1000e_mac_writereg, - [VFTA ... VFTA + 127] = e1000e_mac_writereg, - [FFMT ... FFMT + 254] = e1000e_mac_writereg, + [MTA ... MTA + E1000_MC_TBL_SIZE - 1] = e1000e_mac_writereg, + [VFTA ... VFTA + E1000_VLAN_FILTER_TBL_SIZE - 1] = e1000e_mac_writereg, + [FFMT ... FFMT + 254] = e1000e_set_4bit, [FFVT ... FFVT + 254] = e1000e_mac_writereg, [PBM ... PBM + 10239] = e1000e_mac_writereg, [MDEF ... MDEF + 7] = e1000e_mac_writereg, - [FFLT ... FFLT + 10] = e1000e_mac_writereg, + [FFLT ... FFLT + 10] = e1000e_set_11bit, [FTFT ... FTFT + 254] = e1000e_mac_writereg, [RETA ... RETA + 31] = e1000e_mac_writereg, [RSSRK ... RSSRK + 31] = e1000e_mac_writereg, @@ -3215,10 +3238,12 @@ enum { E1000E_NWRITEOPS = ARRAY_SIZE(e1000e_macreg_writeops) }; enum { MAC_ACCESS_PARTIAL = 1 }; -/* The array below combines alias offsets of the index values for the +/* + * The array below combines alias offsets of the index values for the * MAC registers that have aliases, with the indication of not fully * implemented registers (lowest bit). This combination is possible - * because all of the offsets are even. */ + * because all of the offsets are even. + */ static const uint16_t mac_reg_access[E1000E_MAC_SIZE] = { /* Alias index offsets */ [FCRTL_A] = 0x07fe, [FCRTH_A] = 0x0802, @@ -3227,7 +3252,7 @@ static const uint16_t mac_reg_access[E1000E_MAC_SIZE] = { [TDH_A] = 0x0cf8, [TDT_A] = 0x0cf8, [TIDV_A] = 0x0cf8, [TDFH_A] = 0xed00, [TDFT_A] = 0xed00, [RA_A ... RA_A + 31] = 0x14f0, - [VFTA_A ... VFTA_A + 127] = 0x1400, + [VFTA_A ... VFTA_A + E1000_VLAN_FILTER_TBL_SIZE - 1] = 0x1400, [RDBAL0_A ... RDLEN0_A] = 0x09bc, [TDBAL_A ... TDLEN_A] = 0x0cf8, /* Access options */ @@ -3283,39 +3308,17 @@ e1000e_core_read(E1000ECore *core, hwaddr addr, unsigned size) return 0; } -static inline void -e1000e_autoneg_pause(E1000ECore *core) -{ - timer_del(core->autoneg_timer); -} - static void e1000e_autoneg_resume(E1000ECore *core) { if (e1000e_have_autoneg(core) && - !(core->phy[0][PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { + !(core->phy[0][MII_BMSR] & MII_BMSR_AN_COMP)) { qemu_get_queue(core->owner_nic)->link_down = false; timer_mod(core->autoneg_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); } } -static void -e1000e_vm_state_change(void *opaque, bool running, RunState state) -{ - E1000ECore *core = opaque; - - if (running) { - trace_e1000e_vm_state_running(); - e1000e_intrmgr_resume(core); - e1000e_autoneg_resume(core); - } else { - trace_e1000e_vm_state_stopped(); - e1000e_autoneg_pause(core); - e1000e_intrmgr_pause(core); - } -} - void e1000e_core_pci_realize(E1000ECore *core, const uint16_t *eeprom_templ, @@ -3328,15 +3331,11 @@ e1000e_core_pci_realize(E1000ECore *core, e1000e_autoneg_timer, core); e1000e_intrmgr_pci_realize(core); - core->vmstate = - qemu_add_vm_change_state_handler(e1000e_vm_state_change, core); - for (i = 0; i < E1000E_NUM_QUEUES; i++) { - net_tx_pkt_init(&core->tx[i].tx_pkt, core->owner, - E1000E_MAX_TX_FRAGS, core->has_vnet); + net_tx_pkt_init(&core->tx[i].tx_pkt, E1000E_MAX_TX_FRAGS); } - net_rx_pkt_init(&core->rx_pkt, core->has_vnet); + net_rx_pkt_init(&core->rx_pkt); e1000x_core_prepare_eeprom(core->eeprom, eeprom_templ, @@ -3355,10 +3354,7 @@ e1000e_core_pci_uninit(E1000ECore *core) e1000e_intrmgr_pci_unint(core); - qemu_del_vm_change_state_handler(core->vmstate); - for (i = 0; i < E1000E_NUM_QUEUES; i++) { - net_tx_pkt_reset(core->tx[i].tx_pkt); net_tx_pkt_uninit(core->tx[i].tx_pkt); } @@ -3368,29 +3364,36 @@ e1000e_core_pci_uninit(E1000ECore *core) static const uint16_t e1000e_phy_reg_init[E1000E_PHY_PAGES][E1000E_PHY_PAGE_SIZE] = { [0] = { - [PHY_CTRL] = MII_CR_SPEED_SELECT_MSB | - MII_CR_FULL_DUPLEX | - MII_CR_AUTO_NEG_EN, + [MII_BMCR] = MII_BMCR_SPEED1000 | + MII_BMCR_FD | + MII_BMCR_AUTOEN, - [PHY_STATUS] = MII_SR_EXTENDED_CAPS | - MII_SR_LINK_STATUS | - MII_SR_AUTONEG_CAPS | - MII_SR_PREAMBLE_SUPPRESS | - MII_SR_EXTENDED_STATUS | - MII_SR_10T_HD_CAPS | - MII_SR_10T_FD_CAPS | - MII_SR_100X_HD_CAPS | - MII_SR_100X_FD_CAPS, + [MII_BMSR] = MII_BMSR_EXTCAP | + MII_BMSR_LINK_ST | + MII_BMSR_AUTONEG | + MII_BMSR_MFPS | + MII_BMSR_EXTSTAT | + MII_BMSR_10T_HD | + MII_BMSR_10T_FD | + MII_BMSR_100TX_HD | + MII_BMSR_100TX_FD, - [PHY_ID1] = 0x141, - [PHY_ID2] = E1000_PHY_ID2_82574x, - [PHY_AUTONEG_ADV] = 0xde1, - [PHY_LP_ABILITY] = 0x7e0, - [PHY_AUTONEG_EXP] = BIT(2), - [PHY_NEXT_PAGE_TX] = BIT(0) | BIT(13), - [PHY_1000T_CTRL] = BIT(8) | BIT(9) | BIT(10) | BIT(11), - [PHY_1000T_STATUS] = 0x3c00, - [PHY_EXT_STATUS] = BIT(12) | BIT(13), + [MII_PHYID1] = 0x141, + [MII_PHYID2] = E1000_PHY_ID2_82574x, + [MII_ANAR] = MII_ANAR_CSMACD | MII_ANAR_10 | + MII_ANAR_10FD | MII_ANAR_TX | + MII_ANAR_TXFD | MII_ANAR_PAUSE | + MII_ANAR_PAUSE_ASYM, + [MII_ANLPAR] = MII_ANLPAR_10 | MII_ANLPAR_10FD | + MII_ANLPAR_TX | MII_ANLPAR_TXFD | + MII_ANLPAR_T4 | MII_ANLPAR_PAUSE, + [MII_ANER] = MII_ANER_NP | MII_ANER_NWAY, + [MII_ANNP] = 1 | MII_ANNP_MP, + [MII_CTRL1000] = MII_CTRL1000_HALF | MII_CTRL1000_FULL | + MII_CTRL1000_PORT | MII_CTRL1000_MASTER, + [MII_STAT1000] = MII_STAT1000_HALF | MII_STAT1000_FULL | + MII_STAT1000_ROK | MII_STAT1000_LOK, + [MII_EXTSTAT] = MII_EXTSTAT_1000T_HD | MII_EXTSTAT_1000T_FD, [PHY_COPPER_CTRL1] = BIT(5) | BIT(6) | BIT(8) | BIT(9) | BIT(12) | BIT(13), @@ -3447,8 +3450,7 @@ static const uint32_t e1000e_mac_reg_init[] = { [EITR...EITR + E1000E_MSIX_VEC_NUM - 1] = E1000E_MIN_XITR, }; -void -e1000e_core_reset(E1000ECore *core) +static void e1000e_reset(E1000ECore *core, bool sw) { int i; @@ -3457,9 +3459,16 @@ e1000e_core_reset(E1000ECore *core) e1000e_intrmgr_reset(core); memset(core->phy, 0, sizeof core->phy); - memmove(core->phy, e1000e_phy_reg_init, sizeof e1000e_phy_reg_init); - memset(core->mac, 0, sizeof core->mac); - memmove(core->mac, e1000e_mac_reg_init, sizeof e1000e_mac_reg_init); + memcpy(core->phy, e1000e_phy_reg_init, sizeof e1000e_phy_reg_init); + + for (i = 0; i < E1000E_MAC_SIZE; i++) { + if (sw && (i == PBA || i == PBS || i == FLA)) { + continue; + } + + core->mac[i] = i < ARRAY_SIZE(e1000e_mac_reg_init) ? + e1000e_mac_reg_init[i] : 0; + } core->rxbuf_min_shift = 1 + E1000_RING_DESC_LEN_SHIFT; @@ -3470,24 +3479,29 @@ e1000e_core_reset(E1000ECore *core) e1000x_reset_mac_addr(core->owner_nic, core->mac, core->permanent_mac); for (i = 0; i < ARRAY_SIZE(core->tx); i++) { - net_tx_pkt_reset(core->tx[i].tx_pkt); memset(&core->tx[i].props, 0, sizeof(core->tx[i].props)); core->tx[i].skip_cp = false; } } +void +e1000e_core_reset(E1000ECore *core) +{ + e1000e_reset(core, false); +} + void e1000e_core_pre_save(E1000ECore *core) { int i; NetClientState *nc = qemu_get_queue(core->owner_nic); /* - * If link is down and auto-negotiation is supported and ongoing, - * complete auto-negotiation immediately. This allows us to look - * at MII_SR_AUTONEG_COMPLETE to infer link status on load. - */ + * If link is down and auto-negotiation is supported and ongoing, + * complete auto-negotiation immediately. This allows us to look + * at MII_BMSR_AN_COMP to infer link status on load. + */ if (nc->link_down && e1000e_have_autoneg(core)) { - core->phy[0][PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; + core->phy[0][MII_BMSR] |= MII_BMSR_AN_COMP; e1000e_update_flowctl_status(core); } @@ -3503,10 +3517,18 @@ e1000e_core_post_load(E1000ECore *core) { NetClientState *nc = qemu_get_queue(core->owner_nic); - /* nc.link_down can't be migrated, so infer link_down according + /* + * nc.link_down can't be migrated, so infer link_down according * to link status bit in core.mac[STATUS]. */ nc->link_down = (core->mac[STATUS] & E1000_STATUS_LU) == 0; + /* + * we need to restart intrmgr timers, as an older version of + * QEMU can have stopped them before migration + */ + e1000e_intrmgr_resume(core); + e1000e_autoneg_resume(core); + return 0; } diff --git a/hw/net/e1000e_core.h b/hw/net/e1000e_core.h index 4ddb4d2c39..01510ca78b 100644 --- a/hw/net/e1000e_core.h +++ b/hw/net/e1000e_core.h @@ -1,37 +1,37 @@ /* -* Core code for QEMU e1000e emulation -* -* Software developer's manuals: -* http://www.intel.com/content/dam/doc/datasheet/82574l-gbe-controller-datasheet.pdf -* -* Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) -* Developed by Daynix Computing LTD (http://www.daynix.com) -* -* Authors: -* Dmitry Fleytman -* Leonid Bloch -* Yan Vugenfirer -* -* Based on work done by: -* Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. -* Copyright (c) 2008 Qumranet -* Based on work done by: -* Copyright (c) 2007 Dan Aloni -* Copyright (c) 2004 Antony T Curtis -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; either -* version 2.1 of the License, or (at your option) any later version. -* -* 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 . -*/ + * Core code for QEMU e1000e emulation + * + * Software developer's manuals: + * http://www.intel.com/content/dam/doc/datasheet/82574l-gbe-controller-datasheet.pdf + * + * Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman + * Leonid Bloch + * Yan Vugenfirer + * + * Based on work done by: + * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. + * Copyright (c) 2008 Qumranet + * Based on work done by: + * Copyright (c) 2007 Dan Aloni + * Copyright (c) 2004 Antony T Curtis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 HW_NET_E1000E_CORE_H #define HW_NET_E1000E_CORE_H @@ -95,12 +95,8 @@ struct E1000Core { E1000IntrDelayTimer tidv; E1000IntrDelayTimer itr; - bool itr_intr_pending; E1000IntrDelayTimer eitr[E1000E_MSIX_VEC_NUM]; - bool eitr_intr_pending[E1000E_MSIX_VEC_NUM]; - - VMChangeStateEntry *vmstate; uint32_t itr_guest_value; uint32_t eitr_guest_value[E1000E_MSIX_VEC_NUM]; @@ -113,7 +109,7 @@ struct E1000Core { PCIDevice *owner; void (*owner_start_recv)(PCIDevice *d); - uint32_t msi_causes_pending; + int64_t timadj; }; void diff --git a/hw/net/e1000x_common.c b/hw/net/e1000x_common.c index a8d93870b5..212873fd77 100644 --- a/hw/net/e1000x_common.c +++ b/hw/net/e1000x_common.c @@ -24,9 +24,12 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "hw/pci/pci.h" +#include "hw/net/mii.h" +#include "hw/pci/pci_device.h" +#include "net/eth.h" #include "net/net.h" +#include "e1000_common.h" #include "e1000x_common.h" #include "trace.h" @@ -45,9 +48,9 @@ bool e1000x_rx_ready(PCIDevice *d, uint32_t *mac) return true; } -bool e1000x_is_vlan_packet(const uint8_t *buf, uint16_t vet) +bool e1000x_is_vlan_packet(const void *buf, uint16_t vet) { - uint16_t eth_proto = lduw_be_p(buf + 12); + uint16_t eth_proto = lduw_be_p(&PKT_GET_ETH_HDR(buf)->h_proto); bool res = (eth_proto == vet); trace_e1000x_vlan_is_vlan_pkt(res, eth_proto, vet); @@ -55,33 +58,64 @@ bool e1000x_is_vlan_packet(const uint8_t *buf, uint16_t vet) return res; } -bool e1000x_rx_group_filter(uint32_t *mac, const uint8_t *buf) +bool e1000x_rx_vlan_filter(uint32_t *mac, const struct vlan_header *vhdr) +{ + if (e1000x_vlan_rx_filter_enabled(mac)) { + uint16_t vid = lduw_be_p(&vhdr->h_tci); + uint32_t vfta = + ldl_le_p((uint32_t *)(mac + VFTA) + + ((vid >> E1000_VFTA_ENTRY_SHIFT) & E1000_VFTA_ENTRY_MASK)); + if ((vfta & (1 << (vid & E1000_VFTA_ENTRY_BIT_SHIFT_MASK))) == 0) { + trace_e1000x_rx_flt_vlan_mismatch(vid); + return false; + } + + trace_e1000x_rx_flt_vlan_match(vid); + } + + return true; +} + +bool e1000x_rx_group_filter(uint32_t *mac, const struct eth_header *ehdr) { static const int mta_shift[] = { 4, 3, 2, 0 }; uint32_t f, ra[2], *rp, rctl = mac[RCTL]; + if (is_broadcast_ether_addr(ehdr->h_dest)) { + if (rctl & E1000_RCTL_BAM) { + return true; + } + } else if (is_multicast_ether_addr(ehdr->h_dest)) { + if (rctl & E1000_RCTL_MPE) { + return true; + } + } else { + if (rctl & E1000_RCTL_UPE) { + return true; + } + } + for (rp = mac + RA; rp < mac + RA + 32; rp += 2) { if (!(rp[1] & E1000_RAH_AV)) { continue; } ra[0] = cpu_to_le32(rp[0]); ra[1] = cpu_to_le32(rp[1]); - if (!memcmp(buf, (uint8_t *)ra, 6)) { + if (!memcmp(ehdr->h_dest, (uint8_t *)ra, ETH_ALEN)) { trace_e1000x_rx_flt_ucast_match((int)(rp - mac - RA) / 2, - MAC_ARG(buf)); + MAC_ARG(ehdr->h_dest)); return true; } } - trace_e1000x_rx_flt_ucast_mismatch(MAC_ARG(buf)); + trace_e1000x_rx_flt_ucast_mismatch(MAC_ARG(ehdr->h_dest)); f = mta_shift[(rctl >> E1000_RCTL_MO_SHIFT) & 3]; - f = (((buf[5] << 8) | buf[4]) >> f) & 0xfff; + f = (((ehdr->h_dest[5] << 8) | ehdr->h_dest[4]) >> f) & 0xfff; if (mac[MTA + (f >> 5)] & (1 << (f & 0x1f))) { - e1000x_inc_reg_if_not_full(mac, MPRC); return true; } - trace_e1000x_rx_flt_inexact_mismatch(MAC_ARG(buf), + trace_e1000x_rx_flt_inexact_mismatch(MAC_ARG(ehdr->h_dest), (rctl >> E1000_RCTL_MO_SHIFT) & 3, f >> 5, mac[MTA + (f >> 5)]); @@ -106,16 +140,16 @@ bool e1000x_hw_rx_enabled(uint32_t *mac) bool e1000x_is_oversized(uint32_t *mac, size_t size) { + size_t header_size = sizeof(struct eth_header) + sizeof(struct vlan_header); /* this is the size past which hardware will drop packets when setting LPE=0 */ - static const int maximum_ethernet_vlan_size = 1522; + size_t maximum_short_size = header_size + ETH_MTU; /* this is the size past which hardware will drop packets when setting LPE=1 */ - static const int maximum_ethernet_lpe_size = 16 * KiB; + size_t maximum_large_size = 16 * KiB - ETH_FCS_LEN; - if ((size > maximum_ethernet_lpe_size || - (size > maximum_ethernet_vlan_size - && !(mac[RCTL] & E1000_RCTL_LPE))) + if ((size > maximum_large_size || + (size > maximum_short_size && !(mac[RCTL] & E1000_RCTL_LPE))) && !(mac[RCTL] & E1000_RCTL_SBP)) { e1000x_inc_reg_if_not_full(mac, ROC); trace_e1000x_rx_oversized(size); @@ -152,8 +186,8 @@ void e1000x_reset_mac_addr(NICState *nic, uint32_t *mac_regs, void e1000x_update_regs_on_autoneg_done(uint32_t *mac, uint16_t *phy) { e1000x_update_regs_on_link_up(mac, phy); - phy[PHY_LP_ABILITY] |= MII_LPAR_LPACK; - phy[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; + phy[MII_ANLPAR] |= MII_ANLPAR_ACK; + phy[MII_BMSR] |= MII_BMSR_AN_COMP; trace_e1000x_link_negotiation_done(); } @@ -209,23 +243,36 @@ e1000x_rxbufsize(uint32_t rctl) void e1000x_update_rx_total_stats(uint32_t *mac, - size_t data_size, - size_t data_fcs_size) + eth_pkt_types_e pkt_type, + size_t pkt_size, + size_t pkt_fcs_size) { static const int PRCregs[6] = { PRC64, PRC127, PRC255, PRC511, PRC1023, PRC1522 }; - e1000x_increase_size_stats(mac, PRCregs, data_fcs_size); + e1000x_increase_size_stats(mac, PRCregs, pkt_fcs_size); e1000x_inc_reg_if_not_full(mac, TPR); - mac[GPRC] = mac[TPR]; + e1000x_inc_reg_if_not_full(mac, GPRC); /* TOR - Total Octets Received: * This register includes bytes received in a packet from the field through the field, inclusively. * Always include FCS length (4) in size. */ - e1000x_grow_8reg_if_not_full(mac, TORL, data_size + 4); - mac[GORCL] = mac[TORL]; - mac[GORCH] = mac[TORH]; + e1000x_grow_8reg_if_not_full(mac, TORL, pkt_size + 4); + e1000x_grow_8reg_if_not_full(mac, GORCL, pkt_size + 4); + + switch (pkt_type) { + case ETH_PKT_BCAST: + e1000x_inc_reg_if_not_full(mac, BPRC); + break; + + case ETH_PKT_MCAST: + e1000x_inc_reg_if_not_full(mac, MPRC); + break; + + default: + break; + } } void @@ -265,3 +312,28 @@ e1000x_read_tx_ctx_descr(struct e1000_context_desc *d, props->tcp = (op & E1000_TXD_CMD_TCP) ? 1 : 0; props->tse = (op & E1000_TXD_CMD_TSE) ? 1 : 0; } + +void e1000x_timestamp(uint32_t *mac, int64_t timadj, size_t lo, size_t hi) +{ + int64_t ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint32_t timinca = mac[TIMINCA]; + uint32_t incvalue = timinca & E1000_TIMINCA_INCVALUE_MASK; + uint32_t incperiod = MAX(timinca >> E1000_TIMINCA_INCPERIOD_SHIFT, 1); + int64_t timestamp = timadj + muldiv64(ns, incvalue, incperiod * 16); + + mac[lo] = timestamp & 0xffffffff; + mac[hi] = timestamp >> 32; +} + +void e1000x_set_timinca(uint32_t *mac, int64_t *timadj, uint32_t val) +{ + int64_t ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint32_t old_val = mac[TIMINCA]; + uint32_t old_incvalue = old_val & E1000_TIMINCA_INCVALUE_MASK; + uint32_t old_incperiod = MAX(old_val >> E1000_TIMINCA_INCPERIOD_SHIFT, 1); + uint32_t incvalue = val & E1000_TIMINCA_INCVALUE_MASK; + uint32_t incperiod = MAX(val >> E1000_TIMINCA_INCPERIOD_SHIFT, 1); + + mac[TIMINCA] = val; + *timadj += (muldiv64(ns, incvalue, incperiod) - muldiv64(ns, old_incvalue, old_incperiod)) / 16; +} diff --git a/hw/net/e1000x_common.h b/hw/net/e1000x_common.h index b7742775c4..be291684de 100644 --- a/hw/net/e1000x_common.h +++ b/hw/net/e1000x_common.h @@ -1,108 +1,34 @@ /* -* QEMU e1000(e) emulation - shared code -* -* Copyright (c) 2008 Qumranet -* -* Based on work done by: -* Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. -* Copyright (c) 2007 Dan Aloni -* Copyright (c) 2004 Antony T Curtis -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; either -* version 2.1 of the License, or (at your option) any later version. -* -* 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 . -*/ + * QEMU e1000(e) emulation - shared code + * + * Copyright (c) 2008 Qumranet + * + * Based on work done by: + * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. + * Copyright (c) 2007 Dan Aloni + * Copyright (c) 2004 Antony T Curtis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 HW_NET_E1000X_COMMON_H #define HW_NET_E1000X_COMMON_H -#include "e1000_regs.h" - -#define defreg(x) x = (E1000_##x >> 2) -enum { - defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC), - defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC), - defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC), - defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH0), - defreg(RDBAL0), defreg(RDH0), defreg(RDLEN0), defreg(RDT0), - defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH), - defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT), - defreg(TDLEN1), defreg(TDBAL1), defreg(TDBAH1), defreg(TDH1), - defreg(TDT1), defreg(TORH), defreg(TORL), defreg(TOTH), - defreg(TOTL), defreg(TPR), defreg(TPT), defreg(TXDCTL), - defreg(WUFC), defreg(RA), defreg(MTA), defreg(CRCERRS), - defreg(VFTA), defreg(VET), defreg(RDTR), defreg(RADV), - defreg(TADV), defreg(ITR), defreg(SCC), defreg(ECOL), - defreg(MCC), defreg(LATECOL), defreg(COLC), defreg(DC), - defreg(TNCRS), defreg(SEQEC), defreg(CEXTERR), defreg(RLEC), - defreg(XONRXC), defreg(XONTXC), defreg(XOFFRXC), defreg(XOFFTXC), - defreg(FCRUC), defreg(AIT), defreg(TDFH), defreg(TDFT), - defreg(TDFHS), defreg(TDFTS), defreg(TDFPC), defreg(WUC), - defreg(WUS), defreg(POEMB), defreg(PBS), defreg(RDFH), - defreg(RDFT), defreg(RDFHS), defreg(RDFTS), defreg(RDFPC), - defreg(PBM), defreg(IPAV), defreg(IP4AT), defreg(IP6AT), - defreg(WUPM), defreg(FFLT), defreg(FFMT), defreg(FFVT), - defreg(TARC0), defreg(TARC1), defreg(IAM), defreg(EXTCNF_CTRL), - defreg(GCR), defreg(TIMINCA), defreg(EIAC), defreg(CTRL_EXT), - defreg(IVAR), defreg(MFUTP01), defreg(MFUTP23), defreg(MANC2H), - defreg(MFVAL), defreg(MDEF), defreg(FACTPS), defreg(FTFT), - defreg(RUC), defreg(ROC), defreg(RFC), defreg(RJC), - defreg(PRC64), defreg(PRC127), defreg(PRC255), defreg(PRC511), - defreg(PRC1023), defreg(PRC1522), defreg(PTC64), defreg(PTC127), - defreg(PTC255), defreg(PTC511), defreg(PTC1023), defreg(PTC1522), - defreg(GORCL), defreg(GORCH), defreg(GOTCL), defreg(GOTCH), - defreg(RNBC), defreg(BPRC), defreg(MPRC), defreg(RFCTL), - defreg(PSRCTL), defreg(MPTC), defreg(BPTC), defreg(TSCTFC), - defreg(IAC), defreg(MGTPRC), defreg(MGTPDC), defreg(MGTPTC), - defreg(TSCTC), defreg(RXCSUM), defreg(FUNCTAG), defreg(GSCL_1), - defreg(GSCL_2), defreg(GSCL_3), defreg(GSCL_4), defreg(GSCN_0), - defreg(GSCN_1), defreg(GSCN_2), defreg(GSCN_3), defreg(GCR2), - defreg(RAID), defreg(RSRPD), defreg(TIDV), defreg(EITR), - defreg(MRQC), defreg(RETA), defreg(RSSRK), defreg(RDBAH1), - defreg(RDBAL1), defreg(RDLEN1), defreg(RDH1), defreg(RDT1), - defreg(PBACLR), defreg(FCAL), defreg(FCAH), defreg(FCT), - defreg(FCRTH), defreg(FCRTL), defreg(FCTTV), defreg(FCRTV), - defreg(FLA), defreg(EEWR), defreg(FLOP), defreg(FLOL), - defreg(FLSWCTL), defreg(FLSWCNT), defreg(RXDCTL), defreg(RXDCTL1), - defreg(MAVTV0), defreg(MAVTV1), defreg(MAVTV2), defreg(MAVTV3), - defreg(TXSTMPL), defreg(TXSTMPH), defreg(SYSTIML), defreg(SYSTIMH), - defreg(RXCFGL), defreg(RXUDP), defreg(TIMADJL), defreg(TIMADJH), - defreg(RXSTMPH), defreg(RXSTMPL), defreg(RXSATRL), defreg(RXSATRH), - defreg(FLASHT), defreg(TIPG), defreg(RDH), defreg(RDT), - defreg(RDLEN), defreg(RDBAH), defreg(RDBAL), - defreg(TXDCTL1), - defreg(FLSWDATA), - defreg(CTRL_DUP), - defreg(EXTCNF_SIZE), - defreg(EEMNGCTL), - defreg(EEMNGDATA), - defreg(FLMNGCTL), - defreg(FLMNGDATA), - defreg(FLMNGCNT), - defreg(TSYNCRXCTL), - defreg(TSYNCTXCTL), - - /* Aliases */ - defreg(RDH0_A), defreg(RDT0_A), defreg(RDTR_A), defreg(RDFH_A), - defreg(RDFT_A), defreg(TDH_A), defreg(TDT_A), defreg(TIDV_A), - defreg(TDFH_A), defreg(TDFT_A), defreg(RA_A), defreg(RDBAL0_A), - defreg(TDBAL_A), defreg(TDLEN_A), defreg(VFTA_A), defreg(RDLEN0_A), - defreg(FCRTL_A), defreg(FCRTH_A) -}; - static inline void e1000x_inc_reg_if_not_full(uint32_t *mac, int index) { - if (mac[index] != 0xffffffff) { + if (mac[index] != UINT32_MAX) { mac[index]++; } } @@ -152,21 +78,22 @@ static inline void e1000x_update_regs_on_link_down(uint32_t *mac, uint16_t *phy) { mac[STATUS] &= ~E1000_STATUS_LU; - phy[PHY_STATUS] &= ~MII_SR_LINK_STATUS; - phy[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE; - phy[PHY_LP_ABILITY] &= ~MII_LPAR_LPACK; + phy[MII_BMSR] &= ~MII_BMSR_LINK_ST; + phy[MII_BMSR] &= ~MII_BMSR_AN_COMP; + phy[MII_ANLPAR] &= ~MII_ANLPAR_ACK; } static inline void e1000x_update_regs_on_link_up(uint32_t *mac, uint16_t *phy) { mac[STATUS] |= E1000_STATUS_LU; - phy[PHY_STATUS] |= MII_SR_LINK_STATUS; + phy[MII_BMSR] |= MII_BMSR_LINK_ST; } void e1000x_update_rx_total_stats(uint32_t *mac, - size_t data_size, - size_t data_fcs_size); + eth_pkt_types_e pkt_type, + size_t pkt_size, + size_t pkt_fcs_size); void e1000x_core_prepare_eeprom(uint16_t *eeprom, const uint16_t *templ, @@ -178,9 +105,11 @@ uint32_t e1000x_rxbufsize(uint32_t rctl); bool e1000x_rx_ready(PCIDevice *d, uint32_t *mac); -bool e1000x_is_vlan_packet(const uint8_t *buf, uint16_t vet); +bool e1000x_is_vlan_packet(const void *buf, uint16_t vet); -bool e1000x_rx_group_filter(uint32_t *mac, const uint8_t *buf); +bool e1000x_rx_vlan_filter(uint32_t *mac, const struct vlan_header *vhdr); + +bool e1000x_rx_group_filter(uint32_t *mac, const struct eth_header *ehdr); bool e1000x_hw_rx_enabled(uint32_t *mac); @@ -213,4 +142,7 @@ typedef struct e1000x_txd_props { void e1000x_read_tx_ctx_descr(struct e1000_context_desc *d, e1000x_txd_props *props); +void e1000x_timestamp(uint32_t *mac, int64_t timadj, size_t lo, size_t hi); +void e1000x_set_timinca(uint32_t *mac, int64_t *timadj, uint32_t val); + #endif diff --git a/hw/net/e1000x_regs.h b/hw/net/e1000x_regs.h new file mode 100644 index 0000000000..cd896fc0ca --- /dev/null +++ b/hw/net/e1000x_regs.h @@ -0,0 +1,971 @@ +/******************************************************************************* + + Intel PRO/1000 Linux driver + Copyright(c) 1999 - 2006 Intel Corporation. + + 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, 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 . + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +/* e1000_hw.h + * Structures, enums, and macros for the MAC + */ + +#ifndef HW_E1000X_REGS_H +#define HW_E1000X_REGS_H + +/* PCI Device IDs */ +#define E1000_DEV_ID_82542 0x1000 +#define E1000_DEV_ID_82543GC_FIBER 0x1001 +#define E1000_DEV_ID_82543GC_COPPER 0x1004 +#define E1000_DEV_ID_82544EI_COPPER 0x1008 +#define E1000_DEV_ID_82544EI_FIBER 0x1009 +#define E1000_DEV_ID_82544GC_COPPER 0x100C +#define E1000_DEV_ID_82544GC_LOM 0x100D +#define E1000_DEV_ID_82540EM 0x100E +#define E1000_DEV_ID_82540EM_LOM 0x1015 +#define E1000_DEV_ID_82540EP_LOM 0x1016 +#define E1000_DEV_ID_82540EP 0x1017 +#define E1000_DEV_ID_82540EP_LP 0x101E +#define E1000_DEV_ID_82545EM_COPPER 0x100F +#define E1000_DEV_ID_82545EM_FIBER 0x1011 +#define E1000_DEV_ID_82545GM_COPPER 0x1026 +#define E1000_DEV_ID_82545GM_FIBER 0x1027 +#define E1000_DEV_ID_82545GM_SERDES 0x1028 +#define E1000_DEV_ID_82546EB_COPPER 0x1010 +#define E1000_DEV_ID_82546EB_FIBER 0x1012 +#define E1000_DEV_ID_82546EB_QUAD_COPPER 0x101D +#define E1000_DEV_ID_82541EI 0x1013 +#define E1000_DEV_ID_82541EI_MOBILE 0x1018 +#define E1000_DEV_ID_82541ER_LOM 0x1014 +#define E1000_DEV_ID_82541ER 0x1078 +#define E1000_DEV_ID_82547GI 0x1075 +#define E1000_DEV_ID_82541GI 0x1076 +#define E1000_DEV_ID_82541GI_MOBILE 0x1077 +#define E1000_DEV_ID_82541GI_LF 0x107C +#define E1000_DEV_ID_82546GB_COPPER 0x1079 +#define E1000_DEV_ID_82546GB_FIBER 0x107A +#define E1000_DEV_ID_82546GB_SERDES 0x107B +#define E1000_DEV_ID_82546GB_PCIE 0x108A +#define E1000_DEV_ID_82546GB_QUAD_COPPER 0x1099 +#define E1000_DEV_ID_82547EI 0x1019 +#define E1000_DEV_ID_82547EI_MOBILE 0x101A +#define E1000_DEV_ID_82571EB_COPPER 0x105E +#define E1000_DEV_ID_82571EB_FIBER 0x105F +#define E1000_DEV_ID_82571EB_SERDES 0x1060 +#define E1000_DEV_ID_82571EB_QUAD_COPPER 0x10A4 +#define E1000_DEV_ID_82571PT_QUAD_COPPER 0x10D5 +#define E1000_DEV_ID_82571EB_QUAD_FIBER 0x10A5 +#define E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE 0x10BC +#define E1000_DEV_ID_82571EB_SERDES_DUAL 0x10D9 +#define E1000_DEV_ID_82571EB_SERDES_QUAD 0x10DA +#define E1000_DEV_ID_82572EI_COPPER 0x107D +#define E1000_DEV_ID_82572EI_FIBER 0x107E +#define E1000_DEV_ID_82572EI_SERDES 0x107F +#define E1000_DEV_ID_82572EI 0x10B9 +#define E1000_DEV_ID_82573E 0x108B +#define E1000_DEV_ID_82573E_IAMT 0x108C +#define E1000_DEV_ID_82573L 0x109A +#define E1000_DEV_ID_82574L 0x10D3 +#define E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3 0x10B5 +#define E1000_DEV_ID_80003ES2LAN_COPPER_DPT 0x1096 +#define E1000_DEV_ID_80003ES2LAN_SERDES_DPT 0x1098 +#define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA +#define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB + +#define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049 +#define E1000_DEV_ID_ICH8_IGP_AMT 0x104A +#define E1000_DEV_ID_ICH8_IGP_C 0x104B +#define E1000_DEV_ID_ICH8_IFE 0x104C +#define E1000_DEV_ID_ICH8_IFE_GT 0x10C4 +#define E1000_DEV_ID_ICH8_IFE_G 0x10C5 +#define E1000_DEV_ID_ICH8_IGP_M 0x104D + +/* Device Specific Register Defaults */ +#define E1000_PHY_ID2_82541x 0x380 +#define E1000_PHY_ID2_82544x 0xC30 +#define E1000_PHY_ID2_8254xx_DEFAULT 0xC20 /* 82540x, 82545x, and 82546x */ +#define E1000_PHY_ID2_82573x 0xCC0 +#define E1000_PHY_ID2_82574x 0xCB1 + +/* Register Set. (82543, 82544) + * + * Registers are defined to be 32 bits and should be accessed as 32 bit values. + * These registers are physically located on the NIC, but are mapped into the + * host memory address space. + * + * RW - register is both readable and writable + * RO - register is read only + * WO - register is write only + * R/clr - register is read only and is cleared when read + * A - register array + */ +#define E1000_CTRL 0x00000 /* Device Control - RW */ +#define E1000_CTRL_DUP 0x00004 /* Device Control Duplicate (Shadow) - RW */ +#define E1000_STATUS 0x00008 /* Device Status - RO */ +#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ +#define E1000_EERD 0x00014 /* EEPROM Read - RW */ +#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ +#define E1000_FLA 0x0001C /* Flash Access - RW */ +#define E1000_MDIC 0x00020 /* MDI Control - RW */ +#define E1000_SCTL 0x00024 /* SerDes Control - RW */ +#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ +#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ +#define E1000_FCT 0x00030 /* Flow Control Type - RW */ +#define E1000_VET 0x00038 /* VLAN Ether Type - RW */ +#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */ +#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ +#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ +#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ +#define E1000_IAM 0x000E0 /* Interrupt Acknowledge Auto Mask */ +#define E1000_RCTL 0x00100 /* RX Control - RW */ +#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ +#define E1000_TCTL_EXT 0x00404 /* Extended TX Control - RW */ +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ +#define E1000_LEDCTL 0x00E00 /* LED Control - RW */ +#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ +#define E1000_EEMNGDATA 0x01014 /* MNG EEPROM Read/Write data */ +#define E1000_FLMNGCTL 0x01018 /* MNG Flash Control */ +#define E1000_FLMNGDATA 0x0101C /* MNG FLASH Read data */ +#define E1000_FLMNGCNT 0x01020 /* MNG FLASH Read Counter */ +#define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */ +#define E1000_FLOP 0x0103C /* FLASH Opcode Register */ +#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */ +#define E1000_FCRTL_A 0x00168 /* Alias to FCRTL */ +#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */ +#define E1000_RDFH 0x02410 /* Receive Data FIFO Head Register - RW */ +#define E1000_RDFH_A 0x08000 /* Alias to RDFH */ +#define E1000_RDFT 0x02418 /* Receive Data FIFO Tail Register - RW */ +#define E1000_RDFT_A 0x08008 /* Alias to RDFT */ +#define E1000_RDFHS 0x02420 /* Receive Data FIFO Head Saved Register - RW */ +#define E1000_RDFTS 0x02428 /* Receive Data FIFO Tail Saved Register - RW */ +#define E1000_RDFPC 0x02430 /* Receive Data FIFO Packet Count - RW */ +#define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */ +#define E1000_TDFH_A 0x08010 /* Alias to TDFH */ +#define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */ +#define E1000_TDFT_A 0x08018 /* Alias to TDFT */ +#define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */ +#define E1000_TDFTS 0x03428 /* TX Data FIFO Tail Saved - RW */ +#define E1000_TDFPC 0x03430 /* TX Data FIFO Packet Count - RW */ +#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */ +#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */ +#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */ +#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */ +#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */ +#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */ +#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */ +#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */ +#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */ +#define E1000_COLC 0x04028 /* Collision Count - R/clr */ +#define E1000_DC 0x04030 /* Defer Count - R/clr */ +#define E1000_TNCRS 0x04034 /* TX-No CRS - R/clr */ +#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */ +#define E1000_XONRXC 0x04048 /* XON RX Count - R/clr */ +#define E1000_XONTXC 0x0404C /* XON TX Count - R/clr */ +#define E1000_XOFFRXC 0x04050 /* XOFF RX Count - R/clr */ +#define E1000_XOFFTXC 0x04054 /* XOFF TX Count - R/clr */ +#define E1000_FCRUC 0x04058 /* Flow Control RX Unsupported Count- R/clr */ +#define E1000_PRC64 0x0405C /* Packets RX (64 bytes) - R/clr */ +#define E1000_PRC127 0x04060 /* Packets RX (65-127 bytes) - R/clr */ +#define E1000_PRC255 0x04064 /* Packets RX (128-255 bytes) - R/clr */ +#define E1000_PRC511 0x04068 /* Packets RX (255-511 bytes) - R/clr */ +#define E1000_PRC1023 0x0406C /* Packets RX (512-1023 bytes) - R/clr */ +#define E1000_PRC1522 0x04070 /* Packets RX (1024-1522 bytes) - R/clr */ +#define E1000_GPRC 0x04074 /* Good Packets RX Count - R/clr */ +#define E1000_BPRC 0x04078 /* Broadcast Packets RX Count - R/clr */ +#define E1000_MPRC 0x0407C /* Multicast Packets RX Count - R/clr */ +#define E1000_GPTC 0x04080 /* Good Packets TX Count - R/clr */ +#define E1000_GORCL 0x04088 /* Good Octets RX Count Low - R/clr */ +#define E1000_GORCH 0x0408C /* Good Octets RX Count High - R/clr */ +#define E1000_GOTCL 0x04090 /* Good Octets TX Count Low - R/clr */ +#define E1000_GOTCH 0x04094 /* Good Octets TX Count High - R/clr */ +#define E1000_RNBC 0x040A0 /* RX No Buffers Count - R/clr */ +#define E1000_RUC 0x040A4 /* RX Undersize Count - R/clr */ +#define E1000_RFC 0x040A8 /* RX Fragment Count - R/clr */ +#define E1000_ROC 0x040AC /* RX Oversize Count - R/clr */ +#define E1000_RJC 0x040B0 /* RX Jabber Count - R/clr */ +#define E1000_MGTPRC 0x040B4 /* Management Packets RX Count - R/clr */ +#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */ +#define E1000_MGTPTC 0x040BC /* Management Packets TX Count - R/clr */ +#define E1000_TORL 0x040C0 /* Total Octets RX Low - R/clr */ +#define E1000_TORH 0x040C4 /* Total Octets RX High - R/clr */ +#define E1000_TOTL 0x040C8 /* Total Octets TX Low - R/clr */ +#define E1000_TOTH 0x040CC /* Total Octets TX High - R/clr */ +#define E1000_TPR 0x040D0 /* Total Packets RX - R/clr */ +#define E1000_TPT 0x040D4 /* Total Packets TX - R/clr */ +#define E1000_PTC64 0x040D8 /* Packets TX (64 bytes) - R/clr */ +#define E1000_PTC127 0x040DC /* Packets TX (65-127 bytes) - R/clr */ +#define E1000_PTC255 0x040E0 /* Packets TX (128-255 bytes) - R/clr */ +#define E1000_PTC511 0x040E4 /* Packets TX (256-511 bytes) - R/clr */ +#define E1000_PTC1023 0x040E8 /* Packets TX (512-1023 bytes) - R/clr */ +#define E1000_PTC1522 0x040EC /* Packets TX (1024-1522 Bytes) - R/clr */ +#define E1000_MPTC 0x040F0 /* Multicast Packets TX Count - R/clr */ +#define E1000_BPTC 0x040F4 /* Broadcast Packets TX Count - R/clr */ +#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context TX - R/clr */ +#define E1000_IAC 0x04100 /* Interrupt Assertion Count */ +#define E1000_ICRXPTC 0x04104 /* Interrupt Cause Rx Packet Timer Expire Count */ +#define E1000_ICRXDMTC 0x04120 /* Interrupt Cause Rx Descriptor Minimum Threshold Count */ +#define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */ +#define E1000_RFCTL 0x05008 /* Receive Filter Control*/ +#define E1000_MAVTV0 0x05010 /* Management VLAN TAG Value 0 */ +#define E1000_MAVTV1 0x05014 /* Management VLAN TAG Value 1 */ +#define E1000_MAVTV2 0x05018 /* Management VLAN TAG Value 2 */ +#define E1000_MAVTV3 0x0501c /* Management VLAN TAG Value 3 */ +#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ +#define E1000_RA 0x05400 /* Receive Address - RW Array */ +#define E1000_RA_A 0x00040 /* Alias to RA */ +#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */ +#define E1000_VFTA_A 0x00600 /* Alias to VFTA */ +#define E1000_WUC 0x05800 /* Wakeup Control - RW */ +#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */ +#define E1000_WUS 0x05810 /* Wakeup Status - RO */ +#define E1000_MANC 0x05820 /* Management Control - RW */ +#define E1000_IPAV 0x05838 /* IP Address Valid - RW */ +#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */ +#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */ +#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */ +#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */ +#define E1000_MFVAL 0x05824 /* Manageability Filters Valid - RW */ +#define E1000_MDEF 0x05890 /* Manageability Decision Filters - RW Array */ +#define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ +#define E1000_FTFT 0x09400 /* Flexible TCO Filter Table - RW Array */ + +#define E1000_MANC2H 0x05860 /* Management Control To Host - RW */ +#define E1000_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */ + +#define E1000_GCR 0x05B00 /* PCI-Ex Control */ +#define E1000_FUNCTAG 0x05B08 /* Function-Tag Register */ +#define E1000_GSCL_1 0x05B10 /* PCI-Ex Statistic Control #1 */ +#define E1000_GSCL_2 0x05B14 /* PCI-Ex Statistic Control #2 */ +#define E1000_GSCL_3 0x05B18 /* PCI-Ex Statistic Control #3 */ +#define E1000_GSCL_4 0x05B1C /* PCI-Ex Statistic Control #4 */ +#define E1000_GSCN_0 0x05B20 /* 3GIO Statistic Counter Register #0 */ +#define E1000_GSCN_1 0x05B24 /* 3GIO Statistic Counter Register #1 */ +#define E1000_GSCN_2 0x05B28 /* 3GIO Statistic Counter Register #2 */ +#define E1000_GSCN_3 0x05B2C /* 3GIO Statistic Counter Register #3 */ +#define E1000_FACTPS 0x05B30 /* Function Active and Power State to MNG */ +#define E1000_SWSM 0x05B50 /* SW Semaphore */ +#define E1000_FWSM 0x05B54 /* FW Semaphore */ +#define E1000_PBACLR 0x05B68 /* MSI-X PBA Clear */ + +#define E1000_TSYNCRXCTL 0x0B620 /* Rx Time Sync Control register - RW */ +#define E1000_TSYNCTXCTL 0x0B614 /* Tx Time Sync Control register - RW */ +#define E1000_TIMINCA 0x0B608 /* Increment attributes register - RW */ +#define E1000_RXSTMPL 0x0B624 /* Rx timestamp Low - RO */ +#define E1000_RXSTMPH 0x0B628 /* Rx timestamp High - RO */ +#define E1000_TXSTMPL 0x0B618 /* Tx timestamp value Low - RO */ +#define E1000_TXSTMPH 0x0B61C /* Tx timestamp value High - RO */ +#define E1000_SYSTIML 0x0B600 /* System time register Low - RO */ +#define E1000_SYSTIMH 0x0B604 /* System time register High - RO */ +#define E1000_TIMINCA 0x0B608 /* Increment attributes register - RW */ +#define E1000_RXSATRL 0x0B62C /* Rx timestamp attribute low - RO */ +#define E1000_RXSATRH 0x0B630 /* Rx timestamp attribute high - RO */ +#define E1000_TIMADJL 0x0B60C /* Time Adjustment Offset register Low - RW */ +#define E1000_TIMADJH 0x0B610 /* Time Adjustment Offset register High - RW */ + +/* RSS registers */ +#define E1000_MRQC 0x05818 /* Multiple Receive Control - RW */ +#define E1000_RETA 0x05C00 /* Redirection Table - RW Array */ +#define E1000_RSSRK 0x05C80 /* RSS Random Key - RW Array */ + +#define E1000_RETA_IDX(hash) ((hash) & (BIT(7) - 1)) +#define E1000_RETA_VAL(reta, hash) (((uint8_t *)(reta))[E1000_RETA_IDX(hash)]) + +#define E1000_MRQC_EN_TCPIPV4(mrqc) ((mrqc) & BIT(16)) +#define E1000_MRQC_EN_IPV4(mrqc) ((mrqc) & BIT(17)) +#define E1000_MRQC_EN_TCPIPV6EX(mrqc) ((mrqc) & BIT(18)) +#define E1000_MRQC_EN_IPV6EX(mrqc) ((mrqc) & BIT(19)) +#define E1000_MRQC_EN_IPV6(mrqc) ((mrqc) & BIT(20)) + +#define E1000_MRQ_RSS_TYPE_NONE (0) +#define E1000_MRQ_RSS_TYPE_IPV4TCP (1) +#define E1000_MRQ_RSS_TYPE_IPV4 (2) +#define E1000_MRQ_RSS_TYPE_IPV6TCPEX (3) +#define E1000_MRQ_RSS_TYPE_IPV6EX (4) +#define E1000_MRQ_RSS_TYPE_IPV6 (5) + +#define E1000_ICR_ASSERTED BIT(31) +#define E1000_EIAC_MASK 0x01F00000 + +/* RFCTL register bits */ +#define E1000_RFCTL_ISCSI_DIS 0x00000001 +#define E1000_RFCTL_NFSW_DIS 0x00000040 +#define E1000_RFCTL_NFSR_DIS 0x00000080 +#define E1000_RFCTL_IPV6_DIS 0x00000400 +#define E1000_RFCTL_IPV6_XSUM_DIS 0x00000800 +#define E1000_RFCTL_IPFRSP_DIS 0x00004000 +#define E1000_RFCTL_EXTEN 0x00008000 +#define E1000_RFCTL_IPV6_EX_DIS 0x00010000 +#define E1000_RFCTL_NEW_IPV6_EXT_DIS 0x00020000 + +/* TARC* parsing */ +#define E1000_TARC_ENABLE BIT(10) + +/* SW Semaphore Register */ +#define E1000_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */ +#define E1000_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */ +#define E1000_SWSM_DRV_LOAD 0x00000008 /* Driver Loaded Bit */ + +#define E1000_SWSM2_LOCK 0x00000002 /* Secondary driver semaphore bit */ + +/* Interrupt Cause Read */ +#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ +#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */ +#define E1000_ICR_LSC 0x00000004 /* Link Status Change */ +#define E1000_ICR_RXSEQ 0x00000008 /* rx sequence error */ +#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */ +#define E1000_ICR_RXO 0x00000040 /* rx overrun */ +#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */ +#define E1000_ICR_RXDW 0x00000080 /* rx desc written back */ +#define E1000_ICR_MDAC 0x00000200 /* MDIO access complete */ +#define E1000_ICR_RXCFG 0x00000400 /* RX /c/ ordered set */ +#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */ +#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */ +#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */ +#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */ +#define E1000_ICR_TXD_LOW 0x00008000 +#define E1000_ICR_SRPD 0x00010000 +#define E1000_ICR_ACK 0x00020000 /* Receive Ack frame */ +#define E1000_ICR_MNG 0x00040000 /* Manageability event */ +#define E1000_ICR_DOCK 0x00080000 /* Dock/Undock */ +#define E1000_ICR_INT_ASSERTED 0x80000000 /* If this bit asserted, the driver should claim the interrupt */ +#define E1000_ICR_RXD_FIFO_PAR0 0x00100000 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_ICR_TXD_FIFO_PAR0 0x00200000 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_ICR_HOST_ARB_PAR 0x00400000 /* host arb read buffer parity error */ +#define E1000_ICR_PB_PAR 0x00800000 /* packet buffer parity error */ +#define E1000_ICR_RXD_FIFO_PAR1 0x01000000 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_ICR_TXD_FIFO_PAR1 0x02000000 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_ICR_ALL_PARITY 0x03F00000 /* all parity error bits */ +#define E1000_ICR_DSW 0x00000020 /* FW changed the status of DISSW bit in the FWSM */ +#define E1000_ICR_PHYINT 0x00001000 /* LAN connected device generates an interrupt */ +#define E1000_ICR_EPRST 0x00100000 /* ME handware reset occurs */ +#define E1000_ICR_RXQ0 0x00100000 /* Rx Queue 0 Interrupt */ +#define E1000_ICR_RXQ1 0x00200000 /* Rx Queue 1 Interrupt */ +#define E1000_ICR_TXQ0 0x00400000 /* Tx Queue 0 Interrupt */ +#define E1000_ICR_TXQ1 0x00800000 /* Tx Queue 1 Interrupt */ +#define E1000_ICR_OTHER 0x01000000 /* Other Interrupts */ + +#define E1000_ICR_OTHER_CAUSES (E1000_ICR_LSC | \ + E1000_ICR_RXO | \ + E1000_ICR_MDAC | \ + E1000_ICR_SRPD | \ + E1000_ICR_ACK | \ + E1000_ICR_MNG) + +/* Interrupt Cause Set */ +#define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_ICS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_ICS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_ICS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_ICS_RXDW E1000_ICR_RXDW /* rx desc written back */ +#define E1000_ICS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_ICS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_ICS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_ICS_SRPD E1000_ICR_SRPD +#define E1000_ICS_ACK E1000_ICR_ACK /* Receive Ack frame */ +#define E1000_ICS_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_ICS_DOCK E1000_ICR_DOCK /* Dock/Undock */ +#define E1000_ICS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_ICS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_ICS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ +#define E1000_ICS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ +#define E1000_ICS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_ICS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_ICS_DSW E1000_ICR_DSW +#define E1000_ICS_PHYINT E1000_ICR_PHYINT +#define E1000_ICS_EPRST E1000_ICR_EPRST + +/* Interrupt Mask Set */ +#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMS_RXDW E1000_ICR_RXDW /* rx desc written back */ +#define E1000_IMS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMS_SRPD E1000_ICR_SRPD +#define E1000_IMS_ACK E1000_ICR_ACK /* Receive Ack frame */ +#define E1000_IMS_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_IMS_RXQ0 E1000_ICR_RXQ0 +#define E1000_IMS_RXQ1 E1000_ICR_RXQ1 +#define E1000_IMS_TXQ0 E1000_ICR_TXQ0 +#define E1000_IMS_TXQ1 E1000_ICR_TXQ1 +#define E1000_IMS_OTHER E1000_ICR_OTHER +#define E1000_IMS_DOCK E1000_ICR_DOCK /* Dock/Undock */ +#define E1000_IMS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_IMS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_IMS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ +#define E1000_IMS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ +#define E1000_IMS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_IMS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_IMS_DSW E1000_ICR_DSW +#define E1000_IMS_PHYINT E1000_ICR_PHYINT +#define E1000_IMS_EPRST E1000_ICR_EPRST + +/* Interrupt Mask Clear */ +#define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMC_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMC_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMC_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMC_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMC_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMC_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMC_RXDW E1000_ICR_RXDW /* rx desc written back */ +#define E1000_IMC_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMC_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMC_SRPD E1000_ICR_SRPD +#define E1000_IMC_ACK E1000_ICR_ACK /* Receive Ack frame */ +#define E1000_IMC_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_IMC_DOCK E1000_ICR_DOCK /* Dock/Undock */ +#define E1000_IMC_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_IMC_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_IMC_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ +#define E1000_IMC_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ +#define E1000_IMC_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_IMC_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_IMC_DSW E1000_ICR_DSW +#define E1000_IMC_PHYINT E1000_ICR_PHYINT +#define E1000_IMC_EPRST E1000_ICR_EPRST + +/* Receive Control */ +#define E1000_RCTL_RST 0x00000001 /* Software reset */ +#define E1000_RCTL_EN 0x00000002 /* enable */ +#define E1000_RCTL_SBP 0x00000004 /* store bad packet */ +#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */ +#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */ +#define E1000_RCTL_LPE 0x00000020 /* long packet enable */ +#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */ +#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */ +#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */ +#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */ +#define E1000_RCTL_DTYP_MASK 0x00000C00 /* Descriptor type mask */ +#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */ +#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */ +#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */ +#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */ +#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */ +#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */ +#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */ +#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */ +#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */ +#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */ +#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */ +#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */ +#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */ +#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */ +#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */ +#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */ +#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */ +#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */ +#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */ +#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */ +#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */ +#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */ +#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ +#define E1000_RCTL_FLXBUF_MASK 0x78000000 /* Flexible buffer size */ +#define E1000_RCTL_FLXBUF_SHIFT 27 /* Flexible buffer shift */ + + +#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */ +#define E1000_EEPROM_LED_LOGIC 0x0020 /* Led Logic Word */ +#define E1000_EEPROM_RW_REG_DATA 16 /* Offset to data in EEPROM read/write registers */ +#define E1000_EEPROM_RW_REG_DONE 0x10 /* Offset to READ/WRITE done bit */ +#define E1000_EEPROM_RW_REG_START 1 /* First bit for telling part to start operation */ +#define E1000_EEPROM_RW_ADDR_SHIFT 8 /* Shift to the address bits */ +#define E1000_EEPROM_POLL_WRITE 1 /* Flag for polling for write complete */ +#define E1000_EEPROM_POLL_READ 0 /* Flag for polling for read complete */ + +/* 82574 EERD/EEWR registers layout */ +#define E1000_EERW_START BIT(0) +#define E1000_EERW_DONE BIT(1) +#define E1000_EERW_ADDR_SHIFT 2 +#define E1000_EERW_ADDR_MASK ((1L << 14) - 1) +#define E1000_EERW_DATA_SHIFT 16 +#define E1000_EERW_DATA_MASK ((1L << 16) - 1) + +/* Register Bit Masks */ +/* Device Control */ +#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ +#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */ +#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */ +#define E1000_CTRL_GIO_MASTER_DISABLE 0x00000004 /*Blocks new Master requests */ +#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */ +#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */ +#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */ +#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */ +#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */ +#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */ +#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */ +#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */ +#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */ +#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */ +#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */ +#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */ +#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */ +#define E1000_CTRL_D_UD_EN 0x00002000 /* Dock/Undock enable */ +#define E1000_CTRL_D_UD_POLARITY 0x00004000 /* Defined polarity of Dock/Undock indication in SDP[0] */ +#define E1000_CTRL_FORCE_PHY_RESET 0x00008000 /* Reset both PHY ports, through PHYRST_N pin */ +#define E1000_CTRL_SPD_SHIFT 8 /* Speed Select Shift */ + +#define E1000_CTRL_EXT_ASDCHK 0x00001000 /* auto speed detection check */ +#define E1000_CTRL_EXT_EE_RST 0x00002000 /* EEPROM reset */ +#define E1000_CTRL_EXT_LINK_EN 0x00010000 /* enable link status from external LINK_0 and LINK_1 pins */ +#define E1000_CTRL_EXT_DRV_LOAD 0x10000000 /* Driver loaded bit for FW */ +#define E1000_CTRL_EXT_EIAME 0x01000000 +#define E1000_CTRL_EXT_IAME 0x08000000 /* Int ACK Auto-mask */ +#define E1000_CTRL_EXT_PBA_CLR 0x80000000 /* PBA Clear */ +#define E1000_CTRL_EXT_INT_TIMERS_CLEAR_ENA 0x20000000 +#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ + +#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ +#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ +#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */ +#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */ +#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */ +#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */ +#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */ +#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */ +#define E1000_CTRL_ADVD3WUC 0x00100000 /* D3 WUC */ +#define E1000_CTRL_RST 0x04000000 /* Global reset */ +#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */ +#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */ +#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */ +#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ +#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ +#define E1000_CTRL_SW2FW_INT 0x02000000 /* Initiate an interrupt to manageability engine */ + +/* Device Status */ +#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */ +#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */ +#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */ +#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */ +#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */ +#define E1000_STATUS_PHYRA 0x00000400 /* PHY Reset Asserted */ +#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 + +/* EEPROM/Flash Control */ +#define E1000_EECD_SK 0x00000001 /* EEPROM Clock */ +#define E1000_EECD_CS 0x00000002 /* EEPROM Chip Select */ +#define E1000_EECD_DI 0x00000004 /* EEPROM Data In */ +#define E1000_EECD_DO 0x00000008 /* EEPROM Data Out */ +#define E1000_EECD_FWE_MASK 0x00000030 +#define E1000_EECD_FWE_DIS 0x00000010 /* Disable FLASH writes */ +#define E1000_EECD_FWE_EN 0x00000020 /* Enable FLASH writes */ +#define E1000_EECD_FWE_SHIFT 4 +#define E1000_EECD_REQ 0x00000040 /* EEPROM Access Request */ +#define E1000_EECD_GNT 0x00000080 /* EEPROM Access Grant */ +#define E1000_EECD_PRES 0x00000100 /* EEPROM Present */ +#define E1000_EECD_SIZE 0x00000200 /* EEPROM Size (0=64 word 1=256 word) */ +#define E1000_EECD_ADDR_BITS 0x00000400 /* EEPROM Addressing bits based on type + * (0-small, 1-large) */ +#define E1000_EECD_TYPE 0x00002000 /* EEPROM Type (1-SPI, 0-Microwire) */ +#ifndef E1000_EEPROM_GRANT_ATTEMPTS +#define E1000_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */ +#endif +#define E1000_EECD_AUTO_RD 0x00000200 /* EEPROM Auto Read done */ +#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* EEprom Size */ +#define E1000_EECD_SIZE_EX_SHIFT 11 +#define E1000_EECD_NVADDS 0x00018000 /* NVM Address Size */ +#define E1000_EECD_SELSHAD 0x00020000 /* Select Shadow RAM */ +#define E1000_EECD_INITSRAM 0x00040000 /* Initialize Shadow RAM */ +#define E1000_EECD_FLUPD 0x00080000 /* Update FLASH */ +#define E1000_EECD_AUPDEN 0x00100000 /* Enable Autonomous FLASH update */ +#define E1000_EECD_SHADV 0x00200000 /* Shadow RAM Data Valid */ +#define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */ + + +#define E1000_EECD_SECVAL_SHIFT 22 +#define E1000_STM_OPCODE 0xDB00 +#define E1000_HICR_FW_RESET 0xC0 + +#define E1000_SHADOW_RAM_WORDS 2048 +#define E1000_ICH_NVM_SIG_WORD 0x13 +#define E1000_ICH_NVM_SIG_MASK 0xC0 + +/* MDI Control */ +#define E1000_MDIC_DATA_MASK 0x0000FFFF +#define E1000_MDIC_REG_MASK 0x001F0000 +#define E1000_MDIC_REG_SHIFT 16 +#define E1000_MDIC_PHY_MASK 0x03E00000 +#define E1000_MDIC_PHY_SHIFT 21 +#define E1000_MDIC_OP_WRITE 0x04000000 +#define E1000_MDIC_OP_READ 0x08000000 +#define E1000_MDIC_READY 0x10000000 +#define E1000_MDIC_INT_EN 0x20000000 +#define E1000_MDIC_ERROR 0x40000000 + +/* Rx Interrupt Delay Timer */ +#define E1000_RDTR_FPD BIT(31) + +/* Tx Interrupt Delay Timer */ +#define E1000_TIDV_FPD BIT(31) + +/* Delay increments in nanoseconds for delayed interrupts registers */ +#define E1000_INTR_DELAY_NS_RES (1024) + +/* Delay increments in nanoseconds for interrupt throttling registers */ +#define E1000_INTR_THROTTLING_NS_RES (256) + +/* EEPROM Commands - Microwire */ +#define EEPROM_READ_OPCODE_MICROWIRE 0x6 /* EEPROM read opcode */ +#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5 /* EEPROM write opcode */ +#define EEPROM_ERASE_OPCODE_MICROWIRE 0x7 /* EEPROM erase opcode */ +#define EEPROM_EWEN_OPCODE_MICROWIRE 0x13 /* EEPROM erase/write enable */ +#define EEPROM_EWDS_OPCODE_MICROWIRE 0x10 /* EEPROM erast/write disable */ + +/* EEPROM Word Offsets */ +#define EEPROM_COMPAT 0x0003 +#define EEPROM_ID_LED_SETTINGS 0x0004 +#define EEPROM_VERSION 0x0005 +#define EEPROM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude adjustment. */ +#define EEPROM_PHY_CLASS_WORD 0x0007 +#define EEPROM_INIT_CONTROL1_REG 0x000A +#define EEPROM_INIT_CONTROL2_REG 0x000F +#define EEPROM_SWDEF_PINS_CTRL_PORT_1 0x0010 +#define EEPROM_INIT_CONTROL3_PORT_B 0x0014 +#define EEPROM_INIT_3GIO_3 0x001A +#define EEPROM_SWDEF_PINS_CTRL_PORT_0 0x0020 +#define EEPROM_INIT_CONTROL3_PORT_A 0x0024 +#define EEPROM_CFG 0x0012 +#define EEPROM_FLASH_VERSION 0x0032 +#define EEPROM_CHECKSUM_REG 0x003F + +#define E1000_EEPROM_CFG_DONE 0x00040000 /* MNG config cycle done */ +#define E1000_EEPROM_CFG_DONE_PORT_1 0x00080000 /* ...for second port */ + +/* HH Time Sync */ +#define E1000_TSYNCTXCTL_MAX_ALLOWED_DLY_MASK 0x0000F000 /* max delay */ +#define E1000_TSYNCTXCTL_SYNC_COMP 0x40000000 /* sync complete */ +#define E1000_TSYNCTXCTL_START_SYNC 0x80000000 /* initiate sync */ + +#define E1000_TSYNCTXCTL_VALID 0x00000001 /* Tx timestamp valid */ +#define E1000_TSYNCTXCTL_ENABLED 0x00000010 /* enable Tx timestamping */ + +#define E1000_TSYNCRXCTL_VALID 0x00000001 /* Rx timestamp valid */ +#define E1000_TSYNCRXCTL_TYPE_MASK 0x0000000E /* Rx type mask */ +#define E1000_TSYNCRXCTL_TYPE_L2_V2 0x00 +#define E1000_TSYNCRXCTL_TYPE_L4_V1 0x02 +#define E1000_TSYNCRXCTL_TYPE_L2_L4_V2 0x04 +#define E1000_TSYNCRXCTL_TYPE_ALL 0x08 +#define E1000_TSYNCRXCTL_TYPE_EVENT_V2 0x0A +#define E1000_TSYNCRXCTL_ENABLED 0x00000010 /* enable Rx timestamping */ +#define E1000_TSYNCRXCTL_SYSCFI 0x00000020 /* Sys clock frequency */ + +#define E1000_RXMTRL_PTP_V1_SYNC_MESSAGE 0x00000000 +#define E1000_RXMTRL_PTP_V1_DELAY_REQ_MESSAGE 0x00010000 + +#define E1000_RXMTRL_PTP_V2_SYNC_MESSAGE 0x00000000 +#define E1000_RXMTRL_PTP_V2_DELAY_REQ_MESSAGE 0x01000000 + +#define E1000_TIMINCA_INCPERIOD_SHIFT 24 +#define E1000_TIMINCA_INCVALUE_MASK 0x00FFFFFF + +/* PCI Express Control */ +/* 3GIO Control Register - GCR (0x05B00; RW) */ +#define E1000_L0S_ADJUST (1 << 9) +#define E1000_L1_ENTRY_LATENCY_MSB (1 << 23) +#define E1000_L1_ENTRY_LATENCY_LSB (1 << 25 | 1 << 26) + +#define E1000_L0S_ADJUST (1 << 9) +#define E1000_L1_ENTRY_LATENCY_MSB (1 << 23) +#define E1000_L1_ENTRY_LATENCY_LSB (1 << 25 | 1 << 26) + +#define E1000_GCR_RO_BITS (1 << 23 | 1 << 25 | 1 << 26) + +/* MSI-X PBA Clear register */ +#define E1000_PBACLR_VALID_MASK (BIT(5) - 1) + +/* Transmit Descriptor bit definitions */ +#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ +#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ +#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ +#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */ +#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ +#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */ +#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */ +#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ +#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */ +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ +#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ +#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ +#define E1000_TXD_CMD_SNAP 0x40000000 /* Update SNAP header */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ +#define E1000_TXD_EXTCMD_TSTAMP 0x00000010 /* IEEE1588 Timestamp packet */ + +/* Transmit Control */ +#define E1000_TCTL_RST 0x00000001 /* software reset */ +#define E1000_TCTL_EN 0x00000002 /* enable tx */ +#define E1000_TCTL_BCE 0x00000004 /* busy check enable */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ +#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ +#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */ + +/* Legacy Receive Descriptor */ +struct e1000_rx_desc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + uint16_t length; /* Length of data DMAed into data buffer */ + uint16_t csum; /* Packet checksum */ + uint8_t status; /* Descriptor status */ + uint8_t errors; /* Descriptor Errors */ + uint16_t special; +}; + +/* Extended Receive Descriptor */ +union e1000_rx_desc_extended { + struct { + uint64_t buffer_addr; + uint64_t reserved; + } read; + struct { + struct { + uint32_t mrq; /* Multiple Rx Queues */ + union { + uint32_t rss; /* RSS Hash */ + struct { + uint16_t ip_id; /* IP id */ + uint16_t csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + uint32_t status_error; /* ext status/error */ + uint16_t length; + uint16_t vlan; /* VLAN tag */ + } upper; + } wb; /* writeback */ +}; + +#define MAX_PS_BUFFERS 4 + +/* Number of packet split data buffers (not including the header buffer) */ +#define PS_PAGE_BUFFERS (MAX_PS_BUFFERS - 1) + +/* Receive Descriptor - Packet Split */ +union e1000_rx_desc_packet_split { + struct { + /* one buffer for protocol header(s), three data buffers */ + uint64_t buffer_addr[MAX_PS_BUFFERS]; + } read; + struct { + struct { + uint32_t mrq; /* Multiple Rx Queues */ + union { + uint32_t rss; /* RSS Hash */ + struct { + uint16_t ip_id; /* IP id */ + uint16_t csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + uint32_t status_error; /* ext status/error */ + uint16_t length0; /* length of buffer 0 */ + uint16_t vlan; /* VLAN tag */ + } middle; + struct { + uint16_t header_status; + /* length of buffers 1-3 */ + uint16_t length[PS_PAGE_BUFFERS]; + } upper; + uint64_t reserved; + } wb; /* writeback */ +}; + +/* Receive Checksum Control bits */ +#define E1000_RXCSUM_IPOFLD 0x100 /* IP Checksum Offload Enable */ +#define E1000_RXCSUM_TUOFLD 0x200 /* TCP/UDP Checksum Offload Enable */ +#define E1000_RXCSUM_PCSD 0x2000 /* Packet Checksum Disable */ + +#define E1000_RING_DESC_LEN (16) +#define E1000_RING_DESC_LEN_SHIFT (4) + +#define E1000_MIN_RX_DESC_LEN E1000_RING_DESC_LEN + +/* Receive Descriptor bit definitions */ +#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ +#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ +#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */ +#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */ +#define E1000_RXD_STAT_UDPCS 0x10 /* UDP xsum calculated */ +#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */ +#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */ +#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */ +#define E1000_RXD_STAT_IPIDV 0x200 /* IP identification valid */ +#define E1000_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */ +#define E1000_RXD_STAT_ACK 0x8000 /* ACK Packet indication */ +#define E1000_RXD_ERR_CE 0x01 /* CRC Error */ +#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */ +#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */ +#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */ +#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */ +#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */ +#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */ +#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */ +#define E1000_RXD_SPC_PRI_MASK 0xE000 /* Priority is in upper 3 bits */ +#define E1000_RXD_SPC_PRI_SHIFT 13 +#define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */ +#define E1000_RXD_SPC_CFI_SHIFT 12 + +/* RX packet types */ +#define E1000_RXD_PKT_MAC (0) +#define E1000_RXD_PKT_IP4 (1) +#define E1000_RXD_PKT_IP4_XDP (2) +#define E1000_RXD_PKT_IP6 (5) +#define E1000_RXD_PKT_IP6_XDP (6) + +#define E1000_RXD_PKT_TYPE(t) ((t) << 16) + +#define E1000_RXDEXT_STATERR_CE 0x01000000 +#define E1000_RXDEXT_STATERR_SE 0x02000000 +#define E1000_RXDEXT_STATERR_SEQ 0x04000000 +#define E1000_RXDEXT_STATERR_CXE 0x10000000 +#define E1000_RXDEXT_STATERR_TCPE 0x20000000 +#define E1000_RXDEXT_STATERR_IPE 0x40000000 +#define E1000_RXDEXT_STATERR_RXE 0x80000000 + +#define E1000_RXDPS_HDRSTAT_HDRSP 0x00008000 +#define E1000_RXDPS_HDRSTAT_HDRLEN_MASK 0x000003FF + +/* Receive Address */ +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ + +/* Offload Context Descriptor */ +struct e1000_context_desc { + union { + uint32_t ip_config; + struct { + uint8_t ipcss; /* IP checksum start */ + uint8_t ipcso; /* IP checksum offset */ + uint16_t ipcse; /* IP checksum end */ + } ip_fields; + } lower_setup; + union { + uint32_t tcp_config; + struct { + uint8_t tucss; /* TCP checksum start */ + uint8_t tucso; /* TCP checksum offset */ + uint16_t tucse; /* TCP checksum end */ + } tcp_fields; + } upper_setup; + uint32_t cmd_and_length; /* */ + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t hdr_len; /* Header length */ + uint16_t mss; /* Maximum segment size */ + } fields; + } tcp_seg_setup; +}; + +/* Filters */ +#define E1000_NUM_UNICAST 16 /* Unicast filter entries */ +#define E1000_MC_TBL_SIZE 128 /* Multicast Filter Table (4096 bits) */ +#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */ + +/* Management Control */ +#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */ +#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */ +#define E1000_MANC_R_ON_FORCE 0x00000004 /* Reset on Force TCO - RO */ +#define E1000_MANC_RMCP_EN 0x00000100 /* Enable RCMP 026Fh Filtering */ +#define E1000_MANC_0298_EN 0x00000200 /* Enable RCMP 0298h Filtering */ +#define E1000_MANC_IPV4_EN 0x00000400 /* Enable IPv4 */ +#define E1000_MANC_IPV6_EN 0x00000800 /* Enable IPv6 */ +#define E1000_MANC_SNAP_EN 0x00001000 /* Accept LLC/SNAP */ +#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */ +#define E1000_MANC_NEIGHBOR_EN 0x00004000 /* Enable Neighbor Discovery + * Filtering */ +#define E1000_MANC_ARP_RES_EN 0x00008000 /* Enable ARP response Filtering */ +#define E1000_MANC_DIS_IP_CHK_ARP 0x10000000 /* Disable IP address chacking */ + /*for ARP packets - in 82574 */ +#define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */ +#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */ +#define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */ +#define E1000_MANC_RCV_ALL 0x00080000 /* Receive All Enabled */ +#define E1000_MANC_BLK_PHY_RST_ON_IDE 0x00040000 /* Block phy resets */ +#define E1000_MANC_EN_MAC_ADDR_FILTER 0x00100000 /* Enable MAC address + * filtering */ +#define E1000_MANC_EN_MNG2HOST 0x00200000 /* Enable MNG packets to host + * memory */ +#define E1000_MANC_EN_IP_ADDR_FILTER 0x00400000 /* Enable IP address + * filtering */ +#define E1000_MANC_EN_XSUM_FILTER 0x00800000 /* Enable checksum filtering */ +#define E1000_MANC_BR_EN 0x01000000 /* Enable broadcast filtering */ +#define E1000_MANC_SMB_REQ 0x01000000 /* SMBus Request */ +#define E1000_MANC_SMB_GNT 0x02000000 /* SMBus Grant */ +#define E1000_MANC_SMB_CLK_IN 0x04000000 /* SMBus Clock In */ +#define E1000_MANC_SMB_DATA_IN 0x08000000 /* SMBus Data In */ +#define E1000_MANC_SMB_DATA_OUT 0x10000000 /* SMBus Data Out */ +#define E1000_MANC_SMB_CLK_OUT 0x20000000 /* SMBus Clock Out */ + +#define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */ +#define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */ + +/* FACTPS Control */ +#define E1000_FACTPS_LAN0_ON 0x00000004 /* Lan 0 enable */ + +/* For checksumming, the sum of all words in the EEPROM should equal 0xBABA. */ +#define EEPROM_SUM 0xBABA + +/* I/O-Mapped Access to Internal Registers, Memories, and Flash */ +#define E1000_IOADDR 0x00 +#define E1000_IODATA 0x04 + +#define E1000_VFTA_ENTRY_SHIFT 5 +#define E1000_VFTA_ENTRY_MASK 0x7F +#define E1000_VFTA_ENTRY_BIT_SHIFT_MASK 0x1F + +#endif /* HW_E1000_REGS_H */ diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index 679f52f80f..d9a70c4544 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -42,7 +42,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "net/net.h" @@ -1772,7 +1772,7 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) static const VMStateDescription vmstate_eepro100 = { .version_id = 3, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, EEPRO100State), VMSTATE_UNUSED(32), VMSTATE_BUFFER(mult, EEPRO100State), @@ -1874,7 +1874,9 @@ static void e100_nic_realize(PCIDevice *pci_dev, Error **errp) nic_reset(s); s->nic = qemu_new_nic(&net_eepro100_info, &s->conf, - object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s); + object_get_typename(OBJECT(pci_dev)), + pci_dev->qdev.id, + &pci_dev->qdev.mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str)); @@ -1883,8 +1885,7 @@ 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(VMSTATE_IF(&pci_dev->qdev), VMSTATE_INSTANCE_ID_ANY, - s->vmstate, s); + vmstate_register_any(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s); } static void eepro100_instance_init(Object *obj) diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c index 1b82aec794..5faf20c782 100644 --- a/hw/net/etraxfs_eth.c +++ b/hw/net/etraxfs_eth.c @@ -618,7 +618,8 @@ static void etraxfs_eth_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf, - object_get_typename(OBJECT(s)), dev->id, s); + object_get_typename(OBJECT(s)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->phy.read = tdk_read; @@ -646,15 +647,14 @@ static void etraxfs_eth_class_init(ObjectClass *klass, void *data) /* Instantiate an ETRAXFS Ethernet MAC. */ DeviceState * -etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr, +etraxfs_eth_init(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_new("etraxfs-eth"); - qdev_set_nic_properties(dev, nd); + qemu_configure_nic_device(dev, true, "fseth"); qdev_prop_set_uint32(dev, "phyaddr", phyaddr); /* diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index 4e6cc708de..00315f305d 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -29,6 +29,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/irq.h" +#include "hw/net/mii.h" #include "hw/ptimer.h" #include "hw/qdev-properties.h" #include "etsec.h" @@ -99,7 +100,7 @@ static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size) break; } - DPRINTF("Read 0x%08x @ 0x" TARGET_FMT_plx + DPRINTF("Read 0x%08x @ 0x" HWADDR_FMT_plx " : %s (%s)\n", ret, addr, reg->name, reg->desc); @@ -276,7 +277,7 @@ static void etsec_write(void *opaque, } } - DPRINTF("Write 0x%08x @ 0x" TARGET_FMT_plx + DPRINTF("Write 0x%08x @ 0x" HWADDR_FMT_plx " val:0x%08x->0x%08x : %s (%s)\n", (unsigned int)value, addr, before, reg->value, reg->name, reg->desc); @@ -339,11 +340,11 @@ static void etsec_reset(DeviceState *d) etsec->rx_buffer_len = 0; etsec->phy_status = - MII_SR_EXTENDED_CAPS | MII_SR_LINK_STATUS | MII_SR_AUTONEG_CAPS | - MII_SR_AUTONEG_COMPLETE | MII_SR_PREAMBLE_SUPPRESS | - MII_SR_EXTENDED_STATUS | MII_SR_100T2_HD_CAPS | MII_SR_100T2_FD_CAPS | - MII_SR_10T_HD_CAPS | MII_SR_10T_FD_CAPS | MII_SR_100X_HD_CAPS | - MII_SR_100X_FD_CAPS | MII_SR_100T4_CAPS; + MII_BMSR_EXTCAP | MII_BMSR_LINK_ST | MII_BMSR_AUTONEG | + MII_BMSR_AN_COMP | MII_BMSR_MFPS | MII_BMSR_EXTSTAT | + MII_BMSR_100T2_HD | MII_BMSR_100T2_FD | + MII_BMSR_10T_HD | MII_BMSR_10T_FD | + MII_BMSR_100TX_HD | MII_BMSR_100TX_FD | MII_BMSR_100T4; etsec_update_irq(etsec); } @@ -390,7 +391,8 @@ static void etsec_realize(DeviceState *dev, Error **errp) eTSEC *etsec = ETSEC_COMMON(dev); etsec->nic = qemu_new_nic(&net_etsec_info, &etsec->conf, - object_get_typename(OBJECT(dev)), dev->id, etsec); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, etsec); qemu_format_nic_info_str(qemu_get_queue(etsec->nic), etsec->conf.macaddr.a); etsec->ptimer = ptimer_init(etsec_timer_hit, etsec, PTIMER_POLICY_LEGACY); @@ -443,26 +445,3 @@ static void etsec_register_types(void) } type_init(etsec_register_types) - -DeviceState *etsec_create(hwaddr base, - MemoryRegion * mr, - NICInfo * nd, - qemu_irq tx_irq, - qemu_irq rx_irq, - qemu_irq err_irq) -{ - DeviceState *dev; - - dev = qdev_new("eTSEC"); - qdev_set_nic_properties(dev, nd); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, tx_irq); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, rx_irq); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, err_irq); - - memory_region_add_subregion(mr, base, - SYS_BUS_DEVICE(dev)->mmio[0].memory); - - return dev; -} diff --git a/hw/net/fsl_etsec/etsec.h b/hw/net/fsl_etsec/etsec.h index fddf551544..3860864a3f 100644 --- a/hw/net/fsl_etsec/etsec.h +++ b/hw/net/fsl_etsec/etsec.h @@ -76,23 +76,6 @@ typedef struct eTSEC_rxtx_bd { #define FCB_TX_CTU (1 << 1) #define FCB_TX_NPH (1 << 0) -/* PHY Status Register */ -#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ -#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ -#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ -#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ -#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ -#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ -#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ -#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ -#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ -#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ -#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ -#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ -#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ -#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ -#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ - /* eTSEC */ /* Number of register in the device */ @@ -155,13 +138,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(eTSEC, ETSEC_COMMON) #define eTSEC_TRANSMIT 1 #define eTSEC_RECEIVE 2 -DeviceState *etsec_create(hwaddr base, - MemoryRegion *mr, - NICInfo *nd, - qemu_irq tx_irq, - qemu_irq rx_irq, - qemu_irq err_irq); - void etsec_update_irq(eTSEC *etsec); void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr); diff --git a/hw/net/fsl_etsec/miim.c b/hw/net/fsl_etsec/miim.c index 6bba01c82a..b48d2cb57b 100644 --- a/hw/net/fsl_etsec/miim.c +++ b/hw/net/fsl_etsec/miim.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "hw/net/mii.h" #include "etsec.h" #include "registers.h" @@ -140,8 +141,8 @@ void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc) { /* Set link status */ if (nc->link_down) { - etsec->phy_status &= ~MII_SR_LINK_STATUS; + etsec->phy_status &= ~MII_BMSR_LINK_ST; } else { - etsec->phy_status |= MII_SR_LINK_STATUS; + etsec->phy_status |= MII_BMSR_LINK_ST; } } diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c index a32589e33b..42216de6c9 100644 --- a/hw/net/fsl_etsec/rings.c +++ b/hw/net/fsl_etsec/rings.c @@ -109,7 +109,7 @@ static void read_buffer_descriptor(eTSEC *etsec, { assert(bd != NULL); - RING_DEBUG("READ Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr); + RING_DEBUG("READ Buffer Descriptor @ 0x" HWADDR_FMT_plx"\n", addr); cpu_physical_memory_read(addr, bd, sizeof(eTSEC_rxtx_bd)); @@ -141,7 +141,7 @@ static void write_buffer_descriptor(eTSEC *etsec, stl_be_p(&bd->bufptr, bd->bufptr); } - RING_DEBUG("Write Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr); + RING_DEBUG("Write Buffer Descriptor @ 0x" HWADDR_FMT_plx"\n", addr); cpu_physical_memory_write(addr, bd, sizeof(eTSEC_rxtx_bd)); @@ -365,13 +365,19 @@ void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr) } while (TRUE); /* Save the Buffer Descriptor Pointers to last bd that was not - * succesfully closed */ + * successfully closed */ etsec->regs[TBPTR0 + ring_nbr].value = bd_addr; /* Set transmit halt THLTx */ etsec->regs[TSTAT].value |= 1 << (31 - ring_nbr); } +/* + * rx_init_frame() ensures we never do more padding than this + * (checksum plus minimum data packet size) + */ +#define MAX_RX_PADDING 64 + static void fill_rx_bd(eTSEC *etsec, eTSEC_rxtx_bd *bd, const uint8_t **buf, @@ -380,9 +386,11 @@ static void fill_rx_bd(eTSEC *etsec, uint16_t to_write; hwaddr bufptr = bd->bufptr + ((hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32); - uint8_t padd[etsec->rx_padding]; + uint8_t padd[MAX_RX_PADDING]; uint8_t rem; + assert(etsec->rx_padding <= MAX_RX_PADDING); + RING_DEBUG("eTSEC fill Rx buffer @ 0x%016" HWADDR_PRIx " size:%zu(padding + crc:%u) + fcb:%u\n", bufptr, *size, etsec->rx_padding, etsec->rx_fcb_size); @@ -426,7 +434,7 @@ static void fill_rx_bd(eTSEC *etsec, rem = MIN(etsec->regs[MRBLR].value - bd->length, etsec->rx_padding); if (rem > 0) { - memset(padd, 0x0, sizeof(padd)); + memset(padd, 0x0, rem); etsec->rx_padding -= rem; *size -= rem; bd->length += rem; diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 83ef0a783e..74b6c3d9a7 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -968,21 +968,13 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, return -1; } - /* TODO : Pad to minimum Ethernet frame length */ - /* handle small packets. */ - if (size < 10) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: dropped frame of %zd bytes\n", - __func__, size); - return size; - } - if (!ftgmac100_filter(s, buf, size)) { return size; } - /* 4 bytes for the CRC. */ - size += 4; crc = cpu_to_be32(crc32(~0, buf, size)); + /* Increase size by 4, loop below reads the last 4 bytes from crc_ptr. */ + size += 4; crc_ptr = (uint8_t *) &crc; /* Huge frames are truncated. */ @@ -1118,7 +1110,8 @@ static void ftgmac100_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } @@ -1126,7 +1119,7 @@ static const VMStateDescription vmstate_ftgmac100 = { .name = TYPE_FTGMAC100, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(irq_state, FTGMAC100State), VMSTATE_UINT32(isr, FTGMAC100State), VMSTATE_UINT32(ier, FTGMAC100State), @@ -1311,7 +1304,7 @@ static const VMStateDescription vmstate_aspeed_mii = { .name = TYPE_ASPEED_MII, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(phycr, FTGMAC100State), VMSTATE_UINT32(phydata, FTGMAC100State), VMSTATE_END_OF_LIST() diff --git a/hw/net/i82596.c b/hw/net/i82596.c index ec21e2699a..6cc8292a65 100644 --- a/hw/net/i82596.c +++ b/hw/net/i82596.c @@ -15,6 +15,7 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" +#include "exec/address-spaces.h" #include "qemu/module.h" #include "trace.h" #include "i82596.h" @@ -72,10 +73,6 @@ enum commands { #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) @@ -488,8 +485,6 @@ bool i82596_can_receive(NetClientState *nc) 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); @@ -500,7 +495,6 @@ ssize_t i82596_receive(NetClientState *nc, const uint8_t *buf, size_t sz) 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 }; @@ -583,17 +577,6 @@ ssize_t i82596_receive(NetClientState *nc, const uint8_t *buf, size_t sz) } } - /* 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)); @@ -730,7 +713,7 @@ const VMStateDescription vmstate_i82596 = { .name = "i82596", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(lnkst, I82596State), VMSTATE_TIMER_PTR(flush_queue_timer, I82596State), VMSTATE_END_OF_LIST() @@ -743,7 +726,7 @@ void i82596_common_init(DeviceState *dev, I82596State *s, NetClientInfo *info) qemu_macaddr_default_if_unset(&s->conf.macaddr); } s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), - dev->id, s); + dev->id, &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); if (USE_TIMER) { diff --git a/hw/net/igb.c b/hw/net/igb.c new file mode 100644 index 0000000000..9b37523d6d --- /dev/null +++ b/hw/net/igb.c @@ -0,0 +1,648 @@ +/* + * QEMU Intel 82576 SR/IOV Ethernet Controller Emulation + * + * Datasheet: + * https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82576eg-gbe-datasheet.pdf + * + * Copyright (c) 2020-2023 Red Hat, Inc. + * Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Akihiko Odaki + * Gal Hammmer + * Marcel Apfelbaum + * Dmitry Fleytman + * Leonid Bloch + * Yan Vugenfirer + * + * Based on work done by: + * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. + * Copyright (c) 2008 Qumranet + * Based on work done by: + * Copyright (c) 2007 Dan Aloni + * Copyright (c) 2004 Antony T Curtis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "net/eth.h" +#include "net/net.h" +#include "net/tap.h" +#include "qemu/module.h" +#include "qemu/range.h" +#include "sysemu/sysemu.h" +#include "hw/hw.h" +#include "hw/net/mii.h" +#include "hw/pci/pci.h" +#include "hw/pci/pcie.h" +#include "hw/pci/pcie_sriov.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" + +#include "igb_common.h" +#include "igb_core.h" + +#include "trace.h" +#include "qapi/error.h" +#include "qom/object.h" + +#define TYPE_IGB "igb" +OBJECT_DECLARE_SIMPLE_TYPE(IGBState, IGB) + +struct IGBState { + PCIDevice parent_obj; + NICState *nic; + NICConf conf; + + MemoryRegion mmio; + MemoryRegion flash; + MemoryRegion io; + MemoryRegion msix; + + uint32_t ioaddr; + + IGBCore core; + bool has_flr; +}; + +#define IGB_CAP_SRIOV_OFFSET (0x160) +#define IGB_VF_OFFSET (0x80) +#define IGB_VF_STRIDE (2) + +#define E1000E_MMIO_IDX 0 +#define E1000E_FLASH_IDX 1 +#define E1000E_IO_IDX 2 +#define E1000E_MSIX_IDX 3 + +#define E1000E_MMIO_SIZE (128 * KiB) +#define E1000E_FLASH_SIZE (128 * KiB) +#define E1000E_IO_SIZE (32) +#define E1000E_MSIX_SIZE (16 * KiB) + +static void igb_write_config(PCIDevice *dev, uint32_t addr, + uint32_t val, int len) +{ + IGBState *s = IGB(dev); + + trace_igb_write_config(addr, val, len); + pci_default_write_config(dev, addr, val, len); + if (s->has_flr) { + pcie_cap_flr_write_config(dev, addr, val, len); + } + + if (range_covers_byte(addr, len, PCI_COMMAND) && + (dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { + igb_start_recv(&s->core); + } +} + +uint64_t +igb_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + IGBState *s = opaque; + return igb_core_read(&s->core, addr, size); +} + +void +igb_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + IGBState *s = opaque; + igb_core_write(&s->core, addr, val, size); +} + +void igb_vf_reset(void *opaque, uint16_t vfn) +{ + IGBState *s = opaque; + igb_core_vf_reset(&s->core, vfn); +} + +static bool +igb_io_get_reg_index(IGBState *s, uint32_t *idx) +{ + if (s->ioaddr < 0x1FFFF) { + *idx = s->ioaddr; + return true; + } + + if (s->ioaddr < 0x7FFFF) { + trace_e1000e_wrn_io_addr_undefined(s->ioaddr); + return false; + } + + if (s->ioaddr < 0xFFFFF) { + trace_e1000e_wrn_io_addr_flash(s->ioaddr); + return false; + } + + trace_e1000e_wrn_io_addr_unknown(s->ioaddr); + return false; +} + +static uint64_t +igb_io_read(void *opaque, hwaddr addr, unsigned size) +{ + IGBState *s = opaque; + uint32_t idx = 0; + uint64_t val; + + switch (addr) { + case E1000_IOADDR: + trace_e1000e_io_read_addr(s->ioaddr); + return s->ioaddr; + case E1000_IODATA: + if (igb_io_get_reg_index(s, &idx)) { + val = igb_core_read(&s->core, idx, sizeof(val)); + trace_e1000e_io_read_data(idx, val); + return val; + } + return 0; + default: + trace_e1000e_wrn_io_read_unknown(addr); + return 0; + } +} + +static void +igb_io_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + IGBState *s = opaque; + uint32_t idx = 0; + + switch (addr) { + case E1000_IOADDR: + trace_e1000e_io_write_addr(val); + s->ioaddr = (uint32_t) val; + return; + case E1000_IODATA: + if (igb_io_get_reg_index(s, &idx)) { + trace_e1000e_io_write_data(idx, val); + igb_core_write(&s->core, idx, val, sizeof(val)); + } + return; + default: + trace_e1000e_wrn_io_write_unknown(addr); + return; + } +} + +static const MemoryRegionOps mmio_ops = { + .read = igb_mmio_read, + .write = igb_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static const MemoryRegionOps io_ops = { + .read = igb_io_read, + .write = igb_io_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static bool +igb_nc_can_receive(NetClientState *nc) +{ + IGBState *s = qemu_get_nic_opaque(nc); + return igb_can_receive(&s->core); +} + +static ssize_t +igb_nc_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) +{ + IGBState *s = qemu_get_nic_opaque(nc); + return igb_receive_iov(&s->core, iov, iovcnt); +} + +static ssize_t +igb_nc_receive(NetClientState *nc, const uint8_t *buf, size_t size) +{ + IGBState *s = qemu_get_nic_opaque(nc); + return igb_receive(&s->core, buf, size); +} + +static void +igb_set_link_status(NetClientState *nc) +{ + IGBState *s = qemu_get_nic_opaque(nc); + igb_core_set_link_status(&s->core); +} + +static NetClientInfo net_igb_info = { + .type = NET_CLIENT_DRIVER_NIC, + .size = sizeof(NICState), + .can_receive = igb_nc_can_receive, + .receive = igb_nc_receive, + .receive_iov = igb_nc_receive_iov, + .link_status_changed = igb_set_link_status, +}; + +/* + * EEPROM (NVM) contents documented in section 6.1, table 6-1: + * and in 6.10 Software accessed words. + */ +static const uint16_t igb_eeprom_template[] = { + /* Address |Compat.|OEM sp.| ImRev | OEM sp. */ + 0x0000, 0x0000, 0x0000, 0x0d34, 0xffff, 0x2010, 0xffff, 0xffff, + /* PBA |ICtrl1 | SSID | SVID | DevID |-------|ICtrl2 */ + 0x1040, 0xffff, 0x002b, 0x0000, 0x8086, 0x10c9, 0x0000, 0x70c3, + /* SwPin0| DevID | EESZ |-------|ICtrl3 |PCI-tc | MSIX | APtr */ + 0x0004, 0x10c9, 0x5c00, 0x0000, 0x2880, 0x0014, 0x4a40, 0x0060, + /* PCIe Init. Conf 1,2,3 |PCICtrl| LD1,3 |DDevID |DevRev | LD0,2 */ + 0x6cfb, 0xc7b0, 0x0abe, 0x0403, 0x0783, 0x10a6, 0x0001, 0x0602, + /* SwPin1| FunC |LAN-PWR|ManHwC |ICtrl3 | IOVct |VDevID |-------*/ + 0x0004, 0x0020, 0x0000, 0x004a, 0x2080, 0x00f5, 0x10ca, 0x0000, + /*---------------| LD1,3 | LD0,2 | ROEnd | ROSta | Wdog | VPD */ + 0x0000, 0x0000, 0x4784, 0x4602, 0x0000, 0x0000, 0x1000, 0xffff, + /* PCSet0| Ccfg0 |PXEver |IBAcap |PCSet1 | Ccfg1 |iSCVer | ?? */ + 0x0100, 0x4000, 0x131f, 0x4013, 0x0100, 0x4000, 0xffff, 0xffff, + /* PCSet2| Ccfg2 |PCSet3 | Ccfg3 | ?? |AltMacP| ?? |CHKSUM */ + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x00e0, 0xffff, 0x0000, + /* NC-SIC */ + 0x0003, +}; + +static void igb_core_realize(IGBState *s) +{ + s->core.owner = &s->parent_obj; + s->core.owner_nic = s->nic; +} + +static void +igb_init_msix(IGBState *s) +{ + int i, res; + + res = msix_init(PCI_DEVICE(s), IGB_MSIX_VEC_NUM, + &s->msix, + E1000E_MSIX_IDX, 0, + &s->msix, + E1000E_MSIX_IDX, 0x2000, + 0x70, NULL); + + if (res < 0) { + trace_e1000e_msix_init_fail(res); + } else { + for (i = 0; i < IGB_MSIX_VEC_NUM; i++) { + msix_vector_use(PCI_DEVICE(s), i); + } + } +} + +static void +igb_cleanup_msix(IGBState *s) +{ + msix_unuse_all_vectors(PCI_DEVICE(s)); + msix_uninit(PCI_DEVICE(s), &s->msix, &s->msix); +} + +static void +igb_init_net_peer(IGBState *s, PCIDevice *pci_dev, uint8_t *macaddr) +{ + DeviceState *dev = DEVICE(pci_dev); + NetClientState *nc; + int i; + + s->nic = qemu_new_nic(&net_igb_info, &s->conf, + object_get_typename(OBJECT(s)), dev->id, &dev->mem_reentrancy_guard, s); + + 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)); + + qemu_format_nic_info_str(qemu_get_queue(s->nic), macaddr); + + /* Setup virtio headers */ + for (i = 0; i < s->conf.peers.queues; i++) { + nc = qemu_get_subqueue(s->nic, i); + if (!nc->peer || !qemu_has_vnet_hdr(nc->peer)) { + trace_e1000e_cfg_support_virtio(false); + return; + } + } + + trace_e1000e_cfg_support_virtio(true); + s->core.has_vnet = true; + + for (i = 0; i < s->conf.peers.queues; i++) { + nc = qemu_get_subqueue(s->nic, i); + qemu_set_vnet_hdr_len(nc->peer, sizeof(struct virtio_net_hdr)); + qemu_using_vnet_hdr(nc->peer, true); + } +} + +static int +igb_add_pm_capability(PCIDevice *pdev, uint8_t offset, uint16_t pmc) +{ + Error *local_err = NULL; + int ret = pci_add_capability(pdev, PCI_CAP_ID_PM, offset, + PCI_PM_SIZEOF, &local_err); + + if (local_err) { + error_report_err(local_err); + return ret; + } + + pci_set_word(pdev->config + offset + PCI_PM_PMC, + PCI_PM_CAP_VER_1_1 | + pmc); + + pci_set_word(pdev->wmask + offset + PCI_PM_CTRL, + PCI_PM_CTRL_STATE_MASK | + PCI_PM_CTRL_PME_ENABLE | + PCI_PM_CTRL_DATA_SEL_MASK); + + pci_set_word(pdev->w1cmask + offset + PCI_PM_CTRL, + PCI_PM_CTRL_PME_STATUS); + + return ret; +} + +static void igb_pci_realize(PCIDevice *pci_dev, Error **errp) +{ + IGBState *s = IGB(pci_dev); + uint8_t *macaddr; + int ret; + + trace_e1000e_cb_pci_realize(); + + pci_dev->config_write = igb_write_config; + + pci_dev->config[PCI_CACHE_LINE_SIZE] = 0x10; + pci_dev->config[PCI_INTERRUPT_PIN] = 1; + + /* Define IO/MMIO regions */ + memory_region_init_io(&s->mmio, OBJECT(s), &mmio_ops, s, + "igb-mmio", E1000E_MMIO_SIZE); + pci_register_bar(pci_dev, E1000E_MMIO_IDX, + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); + + /* + * We provide a dummy implementation for the flash BAR + * for drivers that may theoretically probe for its presence. + */ + memory_region_init(&s->flash, OBJECT(s), + "igb-flash", E1000E_FLASH_SIZE); + pci_register_bar(pci_dev, E1000E_FLASH_IDX, + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->flash); + + memory_region_init_io(&s->io, OBJECT(s), &io_ops, s, + "igb-io", E1000E_IO_SIZE); + pci_register_bar(pci_dev, E1000E_IO_IDX, + PCI_BASE_ADDRESS_SPACE_IO, &s->io); + + memory_region_init(&s->msix, OBJECT(s), "igb-msix", + E1000E_MSIX_SIZE); + pci_register_bar(pci_dev, E1000E_MSIX_IDX, + PCI_BASE_ADDRESS_MEM_TYPE_64, &s->msix); + + /* Create networking backend */ + qemu_macaddr_default_if_unset(&s->conf.macaddr); + macaddr = s->conf.macaddr.a; + + /* Add PCI capabilities in reverse order */ + assert(pcie_endpoint_cap_init(pci_dev, 0xa0) > 0); + + igb_init_msix(s); + + ret = msi_init(pci_dev, 0x50, 1, true, true, NULL); + if (ret) { + trace_e1000e_msi_init_fail(ret); + } + + if (igb_add_pm_capability(pci_dev, 0x40, PCI_PM_CAP_DSI) < 0) { + hw_error("Failed to initialize PM capability"); + } + + /* PCIe extended capabilities (in order) */ + if (s->has_flr) { + pcie_cap_flr_init(pci_dev); + } + + if (pcie_aer_init(pci_dev, 1, 0x100, 0x40, errp) < 0) { + hw_error("Failed to initialize AER capability"); + } + + pcie_ari_init(pci_dev, 0x150); + + pcie_sriov_pf_init(pci_dev, IGB_CAP_SRIOV_OFFSET, TYPE_IGBVF, + IGB_82576_VF_DEV_ID, IGB_MAX_VF_FUNCTIONS, IGB_MAX_VF_FUNCTIONS, + IGB_VF_OFFSET, IGB_VF_STRIDE); + + pcie_sriov_pf_init_vf_bar(pci_dev, IGBVF_MMIO_BAR_IDX, + PCI_BASE_ADDRESS_MEM_TYPE_64 | PCI_BASE_ADDRESS_MEM_PREFETCH, + IGBVF_MMIO_SIZE); + pcie_sriov_pf_init_vf_bar(pci_dev, IGBVF_MSIX_BAR_IDX, + PCI_BASE_ADDRESS_MEM_TYPE_64 | PCI_BASE_ADDRESS_MEM_PREFETCH, + IGBVF_MSIX_SIZE); + + igb_init_net_peer(s, pci_dev, macaddr); + + /* Initialize core */ + igb_core_realize(s); + + igb_core_pci_realize(&s->core, + igb_eeprom_template, + sizeof(igb_eeprom_template), + macaddr); +} + +static void igb_pci_uninit(PCIDevice *pci_dev) +{ + IGBState *s = IGB(pci_dev); + + trace_e1000e_cb_pci_uninit(); + + igb_core_pci_uninit(&s->core); + + pcie_sriov_pf_exit(pci_dev); + pcie_cap_exit(pci_dev); + + qemu_del_nic(s->nic); + + igb_cleanup_msix(s); + msi_uninit(pci_dev); +} + +static void igb_qdev_reset_hold(Object *obj) +{ + IGBState *s = IGB(obj); + + trace_e1000e_cb_qdev_reset_hold(); + + igb_core_reset(&s->core); +} + +static int igb_pre_save(void *opaque) +{ + IGBState *s = opaque; + + trace_e1000e_cb_pre_save(); + + igb_core_pre_save(&s->core); + + return 0; +} + +static int igb_post_load(void *opaque, int version_id) +{ + IGBState *s = opaque; + + trace_e1000e_cb_post_load(); + return igb_core_post_load(&s->core); +} + +static const VMStateDescription igb_vmstate_tx_ctx = { + .name = "igb-tx-ctx", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(vlan_macip_lens, struct e1000_adv_tx_context_desc), + VMSTATE_UINT32(seqnum_seed, struct e1000_adv_tx_context_desc), + VMSTATE_UINT32(type_tucmd_mlhl, struct e1000_adv_tx_context_desc), + VMSTATE_UINT32(mss_l4len_idx, struct e1000_adv_tx_context_desc), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription igb_vmstate_tx = { + .name = "igb-tx", + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT_ARRAY(ctx, struct igb_tx, 2, 0, igb_vmstate_tx_ctx, + struct e1000_adv_tx_context_desc), + VMSTATE_UINT32(first_cmd_type_len, struct igb_tx), + VMSTATE_UINT32(first_olinfo_status, struct igb_tx), + VMSTATE_BOOL(first, struct igb_tx), + VMSTATE_BOOL(skip_cp, struct igb_tx), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription igb_vmstate_intr_timer = { + .name = "igb-intr-timer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_TIMER_PTR(timer, IGBIntrDelayTimer), + VMSTATE_BOOL(running, IGBIntrDelayTimer), + VMSTATE_END_OF_LIST() + } +}; + +#define VMSTATE_IGB_INTR_DELAY_TIMER(_f, _s) \ + VMSTATE_STRUCT(_f, _s, 0, \ + igb_vmstate_intr_timer, IGBIntrDelayTimer) + +#define VMSTATE_IGB_INTR_DELAY_TIMER_ARRAY(_f, _s, _num) \ + VMSTATE_STRUCT_ARRAY(_f, _s, _num, 0, \ + igb_vmstate_intr_timer, IGBIntrDelayTimer) + +static const VMStateDescription igb_vmstate = { + .name = "igb", + .version_id = 1, + .minimum_version_id = 1, + .pre_save = igb_pre_save, + .post_load = igb_post_load, + .fields = (const VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, IGBState), + VMSTATE_MSIX(parent_obj, IGBState), + + VMSTATE_UINT32(ioaddr, IGBState), + VMSTATE_UINT8(core.rx_desc_len, IGBState), + VMSTATE_UINT16_ARRAY(core.eeprom, IGBState, IGB_EEPROM_SIZE), + VMSTATE_UINT16_ARRAY(core.phy, IGBState, MAX_PHY_REG_ADDRESS + 1), + VMSTATE_UINT32_ARRAY(core.mac, IGBState, E1000E_MAC_SIZE), + VMSTATE_UINT8_ARRAY(core.permanent_mac, IGBState, ETH_ALEN), + + VMSTATE_IGB_INTR_DELAY_TIMER_ARRAY(core.eitr, IGBState, + IGB_INTR_NUM), + + VMSTATE_UINT32_ARRAY(core.eitr_guest_value, IGBState, IGB_INTR_NUM), + + VMSTATE_STRUCT_ARRAY(core.tx, IGBState, IGB_NUM_QUEUES, 0, + igb_vmstate_tx, struct igb_tx), + + VMSTATE_INT64(core.timadj, IGBState), + + VMSTATE_END_OF_LIST() + } +}; + +static Property igb_properties[] = { + DEFINE_NIC_PROPERTIES(IGBState, conf), + DEFINE_PROP_BOOL("x-pcie-flr-init", IGBState, has_flr, true), + DEFINE_PROP_END_OF_LIST(), +}; + +static void igb_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(class); + ResettableClass *rc = RESETTABLE_CLASS(class); + PCIDeviceClass *c = PCI_DEVICE_CLASS(class); + + c->realize = igb_pci_realize; + c->exit = igb_pci_uninit; + c->vendor_id = PCI_VENDOR_ID_INTEL; + c->device_id = E1000_DEV_ID_82576; + c->revision = 1; + c->class_id = PCI_CLASS_NETWORK_ETHERNET; + + rc->phases.hold = igb_qdev_reset_hold; + + dc->desc = "Intel 82576 Gigabit Ethernet Controller"; + dc->vmsd = &igb_vmstate; + + device_class_set_props(dc, igb_properties); + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); +} + +static void igb_instance_init(Object *obj) +{ + IGBState *s = IGB(obj); + device_add_bootindex_property(obj, &s->conf.bootindex, + "bootindex", "/ethernet-phy@0", + DEVICE(obj)); +} + +static const TypeInfo igb_info = { + .name = TYPE_IGB, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(IGBState), + .class_init = igb_class_init, + .instance_init = igb_instance_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, +}; + +static void igb_register_types(void) +{ + type_register_static(&igb_info); +} + +type_init(igb_register_types) diff --git a/hw/net/igb_common.h b/hw/net/igb_common.h new file mode 100644 index 0000000000..b316a5bcfa --- /dev/null +++ b/hw/net/igb_common.h @@ -0,0 +1,157 @@ +/* + * QEMU igb emulation - shared definitions + * + * Copyright (c) 2020-2023 Red Hat, Inc. + * Copyright (c) 2008 Qumranet + * + * Based on work done by: + * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. + * Copyright (c) 2007 Dan Aloni + * Copyright (c) 2004 Antony T Curtis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 HW_NET_IGB_COMMON_H +#define HW_NET_IGB_COMMON_H + +#include "igb_regs.h" + +#define TYPE_IGBVF "igbvf" + +#define IGBVF_MMIO_BAR_IDX (0) +#define IGBVF_MSIX_BAR_IDX (3) + +#define IGBVF_MMIO_SIZE (16 * 1024) +#define IGBVF_MSIX_SIZE (16 * 1024) + +#define defreg(x) x = (E1000_##x >> 2) +#define defreg_indexed(x, i) x##i = (E1000_##x(i) >> 2) +#define defreg_indexeda(x, i) x##i##_A = (E1000_##x##_A(i) >> 2) + +#define defregd(x) defreg_indexed(x, 0), defreg_indexed(x, 1), \ + defreg_indexed(x, 2), defreg_indexed(x, 3), \ + defreg_indexed(x, 4), defreg_indexed(x, 5), \ + defreg_indexed(x, 6), defreg_indexed(x, 7), \ + defreg_indexed(x, 8), defreg_indexed(x, 9), \ + defreg_indexed(x, 10), defreg_indexed(x, 11), \ + defreg_indexed(x, 12), defreg_indexed(x, 13), \ + defreg_indexed(x, 14), defreg_indexed(x, 15), \ + defreg_indexeda(x, 0), defreg_indexeda(x, 1), \ + defreg_indexeda(x, 2), defreg_indexeda(x, 3) + +#define defreg8(x) defreg_indexed(x, 0), defreg_indexed(x, 1), \ + defreg_indexed(x, 2), defreg_indexed(x, 3), \ + defreg_indexed(x, 4), defreg_indexed(x, 5), \ + defreg_indexed(x, 6), defreg_indexed(x, 7) + +enum { + defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC), + defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC), + defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC), + defreg(MPC), defreg(RCTL), + defreg(STATUS), defreg(SWSM), defreg(TCTL), + defreg(TORH), defreg(TORL), defreg(TOTH), + defreg(TOTL), defreg(TPR), defreg(TPT), + defreg(WUFC), defreg(RA), defreg(MTA), defreg(CRCERRS), + defreg(VFTA), defreg(VET), + defreg(SCC), defreg(ECOL), + defreg(MCC), defreg(LATECOL), defreg(COLC), defreg(DC), + defreg(TNCRS), defreg(RLEC), + defreg(XONRXC), defreg(XONTXC), defreg(XOFFRXC), defreg(XOFFTXC), + defreg(FCRUC), defreg(TDFH), defreg(TDFT), + defreg(TDFHS), defreg(TDFTS), defreg(TDFPC), defreg(WUC), + defreg(WUS), defreg(RDFH), + defreg(RDFT), defreg(RDFHS), defreg(RDFTS), defreg(RDFPC), + defreg(IPAV), defreg(IP4AT), defreg(IP6AT), + defreg(WUPM), defreg(FFMT), + defreg(IAM), + defreg(GCR), defreg(TIMINCA), defreg(EIAC), defreg(CTRL_EXT), + defreg(IVAR0), defreg(MANC2H), + defreg(MFVAL), defreg(MDEF), defreg(FACTPS), defreg(FTFT), + defreg(RUC), defreg(ROC), defreg(RFC), defreg(RJC), + defreg(PRC64), defreg(PRC127), defreg(PRC255), defreg(PRC511), + defreg(PRC1023), defreg(PRC1522), defreg(PTC64), defreg(PTC127), + defreg(PTC255), defreg(PTC511), defreg(PTC1023), defreg(PTC1522), + defreg(GORCL), defreg(GORCH), defreg(GOTCL), defreg(GOTCH), + defreg(RNBC), defreg(BPRC), defreg(MPRC), defreg(RFCTL), + defreg(MPTC), defreg(BPTC), + defreg(IAC), defreg(MGTPRC), defreg(MGTPDC), defreg(MGTPTC), + defreg(TSCTC), defreg(RXCSUM), defreg(FUNCTAG), defreg(GSCL_1), + defreg(GSCL_2), defreg(GSCL_3), defreg(GSCL_4), defreg(GSCN_0), + defreg(GSCN_1), defreg(GSCN_2), defreg(GSCN_3), + defreg_indexed(EITR, 0), + defreg(MRQC), defreg(RETA), defreg(RSSRK), + defreg(PBACLR), defreg(FCAL), defreg(FCAH), defreg(FCT), + defreg(FCRTH), defreg(FCRTL), defreg(FCTTV), defreg(FCRTV), + defreg(FLA), defreg(FLOP), + defreg(MAVTV0), defreg(MAVTV1), defreg(MAVTV2), defreg(MAVTV3), + defreg(TXSTMPL), defreg(TXSTMPH), defreg(SYSTIML), defreg(SYSTIMH), + defreg(TIMADJL), defreg(TIMADJH), + defreg(RXSTMPH), defreg(RXSTMPL), defreg(RXSATRL), defreg(RXSATRH), + defreg(TIPG), + defreg(CTRL_DUP), + defreg(EEMNGCTL), + defreg(EEMNGDATA), + defreg(FLMNGCTL), + defreg(FLMNGDATA), + defreg(FLMNGCNT), + defreg(TSYNCRXCTL), + defreg(TSYNCTXCTL), + defreg(RLPML), + defreg(UTA), + + /* Aliases */ + defreg(RDFH_A), defreg(RDFT_A), defreg(TDFH_A), defreg(TDFT_A), + defreg(RA_A), defreg(VFTA_A), defreg(FCRTL_A), + + /* Additional regs used by IGB */ + defreg(FWSM), defreg(SW_FW_SYNC), + + defreg(EICS), defreg(EIMS), defreg(EIMC), defreg(EIAM), + defreg(EICR), defreg(IVAR_MISC), defreg(GPIE), + + defreg(TSYNCRXCFG), defreg8(ETQF), + + defreg(RXPBS), defregd(RDBAL), defregd(RDBAH), defregd(RDLEN), + defregd(SRRCTL), defregd(RDH), defregd(RDT), + defregd(RXDCTL), defregd(RXCTL), defregd(RQDPC), defreg(RA2), + + defreg(TXPBS), defreg(TCTL_EXT), defreg(DTXCTL), defreg(HTCBDPC), + defregd(TDBAL), defregd(TDBAH), defregd(TDLEN), defregd(TDH), + defregd(TDT), defregd(TXDCTL), defregd(TXCTL), + defregd(TDWBAL), defregd(TDWBAH), + + defreg(VT_CTL), + + defreg8(P2VMAILBOX), defreg8(V2PMAILBOX), defreg(MBVFICR), defreg(MBVFIMR), + defreg(VFLRE), defreg(VFRE), defreg(VFTE), defreg(WVBR), + defreg(QDE), defreg(DTXSWC), defreg_indexed(VLVF, 0), + defreg8(VMOLR), defreg(RPLOLR), defreg8(VMBMEM), defreg8(VMVIR), + + defreg8(PVTCTRL), defreg8(PVTEICS), defreg8(PVTEIMS), defreg8(PVTEIMC), + defreg8(PVTEIAC), defreg8(PVTEIAM), defreg8(PVTEICR), defreg8(PVFGPRC), + defreg8(PVFGPTC), defreg8(PVFGORC), defreg8(PVFGOTC), defreg8(PVFMPRC), + defreg8(PVFGPRLBC), defreg8(PVFGPTLBC), defreg8(PVFGORLBC), defreg8(PVFGOTLBC), + + defreg(MTA_A), + + defreg(VTIVAR), defreg(VTIVAR_MISC), +}; + +uint64_t igb_mmio_read(void *opaque, hwaddr addr, unsigned size); +void igb_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size); +void igb_vf_reset(void *opaque, uint16_t vfn); + +#endif diff --git a/hw/net/igb_core.c b/hw/net/igb_core.c new file mode 100644 index 0000000000..bcd5f6cd9c --- /dev/null +++ b/hw/net/igb_core.c @@ -0,0 +1,4553 @@ +/* + * Core code for QEMU igb emulation + * + * Datasheet: + * https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82576eg-gbe-datasheet.pdf + * + * Copyright (c) 2020-2023 Red Hat, Inc. + * Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Akihiko Odaki + * Gal Hammmer + * Marcel Apfelbaum + * Dmitry Fleytman + * Leonid Bloch + * Yan Vugenfirer + * + * Based on work done by: + * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. + * Copyright (c) 2008 Qumranet + * Based on work done by: + * Copyright (c) 2007 Dan Aloni + * Copyright (c) 2004 Antony T Curtis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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/log.h" +#include "net/net.h" +#include "net/tap.h" +#include "hw/net/mii.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" +#include "sysemu/runstate.h" + +#include "net_tx_pkt.h" +#include "net_rx_pkt.h" + +#include "igb_common.h" +#include "e1000x_common.h" +#include "igb_core.h" + +#include "trace.h" + +#define E1000E_MAX_TX_FRAGS (64) + +union e1000_rx_desc_union { + struct e1000_rx_desc legacy; + union e1000_adv_rx_desc adv; +}; + +typedef struct IGBTxPktVmdqCallbackContext { + IGBCore *core; + NetClientState *nc; +} IGBTxPktVmdqCallbackContext; + +typedef struct L2Header { + struct eth_header eth; + struct vlan_header vlan[2]; +} L2Header; + +typedef struct PTP2 { + uint8_t message_id_transport_specific; + uint8_t version_ptp; + uint16_t message_length; + uint8_t subdomain_number; + uint8_t reserved0; + uint16_t flags; + uint64_t correction; + uint8_t reserved1[5]; + uint8_t source_communication_technology; + uint32_t source_uuid_lo; + uint16_t source_uuid_hi; + uint16_t source_port_id; + uint16_t sequence_id; + uint8_t control; + uint8_t log_message_period; +} PTP2; + +static ssize_t +igb_receive_internal(IGBCore *core, const struct iovec *iov, int iovcnt, + bool has_vnet, bool *external_tx); + +static void igb_raise_interrupts(IGBCore *core, size_t index, uint32_t causes); +static void igb_reset(IGBCore *core, bool sw); + +static inline void +igb_raise_legacy_irq(IGBCore *core) +{ + trace_e1000e_irq_legacy_notify(true); + e1000x_inc_reg_if_not_full(core->mac, IAC); + pci_set_irq(core->owner, 1); +} + +static inline void +igb_lower_legacy_irq(IGBCore *core) +{ + trace_e1000e_irq_legacy_notify(false); + pci_set_irq(core->owner, 0); +} + +static void igb_msix_notify(IGBCore *core, unsigned int cause) +{ + PCIDevice *dev = core->owner; + uint16_t vfn; + uint32_t effective_eiac; + unsigned int vector; + + vfn = 8 - (cause + 2) / IGBVF_MSIX_VEC_NUM; + if (vfn < pcie_sriov_num_vfs(core->owner)) { + dev = pcie_sriov_get_vf_at_index(core->owner, vfn); + assert(dev); + vector = (cause + 2) % IGBVF_MSIX_VEC_NUM; + } else if (cause >= IGB_MSIX_VEC_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, + "igb: Tried to use vector unavailable for PF"); + return; + } else { + vector = cause; + } + + msix_notify(dev, vector); + + trace_e1000e_irq_icr_clear_eiac(core->mac[EICR], core->mac[EIAC]); + effective_eiac = core->mac[EIAC] & BIT(cause); + core->mac[EICR] &= ~effective_eiac; +} + +static inline void +igb_intrmgr_rearm_timer(IGBIntrDelayTimer *timer) +{ + int64_t delay_ns = (int64_t) timer->core->mac[timer->delay_reg] * + timer->delay_resolution_ns; + + trace_e1000e_irq_rearm_timer(timer->delay_reg << 2, delay_ns); + + timer_mod(timer->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + delay_ns); + + timer->running = true; +} + +static void +igb_intmgr_timer_resume(IGBIntrDelayTimer *timer) +{ + if (timer->running) { + igb_intrmgr_rearm_timer(timer); + } +} + +static void +igb_intrmgr_on_msix_throttling_timer(void *opaque) +{ + IGBIntrDelayTimer *timer = opaque; + int idx = timer - &timer->core->eitr[0]; + + timer->running = false; + + trace_e1000e_irq_msix_notify_postponed_vec(idx); + igb_msix_notify(timer->core, idx); +} + +static void +igb_intrmgr_initialize_all_timers(IGBCore *core, bool create) +{ + int i; + + for (i = 0; i < IGB_INTR_NUM; i++) { + core->eitr[i].core = core; + core->eitr[i].delay_reg = EITR0 + i; + core->eitr[i].delay_resolution_ns = E1000_INTR_DELAY_NS_RES; + } + + if (!create) { + return; + } + + for (i = 0; i < IGB_INTR_NUM; i++) { + core->eitr[i].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + igb_intrmgr_on_msix_throttling_timer, + &core->eitr[i]); + } +} + +static void +igb_intrmgr_resume(IGBCore *core) +{ + int i; + + for (i = 0; i < IGB_INTR_NUM; i++) { + igb_intmgr_timer_resume(&core->eitr[i]); + } +} + +static void +igb_intrmgr_reset(IGBCore *core) +{ + int i; + + for (i = 0; i < IGB_INTR_NUM; i++) { + if (core->eitr[i].running) { + timer_del(core->eitr[i].timer); + igb_intrmgr_on_msix_throttling_timer(&core->eitr[i]); + } + } +} + +static void +igb_intrmgr_pci_unint(IGBCore *core) +{ + int i; + + for (i = 0; i < IGB_INTR_NUM; i++) { + timer_free(core->eitr[i].timer); + } +} + +static void +igb_intrmgr_pci_realize(IGBCore *core) +{ + igb_intrmgr_initialize_all_timers(core, true); +} + +static inline bool +igb_rx_csum_enabled(IGBCore *core) +{ + return (core->mac[RXCSUM] & E1000_RXCSUM_PCSD) ? false : true; +} + +static inline bool +igb_rx_use_legacy_descriptor(IGBCore *core) +{ + /* + * TODO: If SRRCTL[n],DESCTYPE = 000b, the 82576 uses the legacy Rx + * descriptor. + */ + return false; +} + +typedef struct E1000ERingInfo { + int dbah; + int dbal; + int dlen; + int dh; + int dt; + int idx; +} E1000ERingInfo; + +static uint32_t +igb_rx_queue_desctyp_get(IGBCore *core, const E1000ERingInfo *r) +{ + return core->mac[E1000_SRRCTL(r->idx) >> 2] & E1000_SRRCTL_DESCTYPE_MASK; +} + +static bool +igb_rx_use_ps_descriptor(IGBCore *core, const E1000ERingInfo *r) +{ + uint32_t desctyp = igb_rx_queue_desctyp_get(core, r); + return desctyp == E1000_SRRCTL_DESCTYPE_HDR_SPLIT || + desctyp == E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS; +} + +static inline bool +igb_rss_enabled(IGBCore *core) +{ + return (core->mac[MRQC] & 3) == E1000_MRQC_ENABLE_RSS_MQ && + !igb_rx_csum_enabled(core) && + !igb_rx_use_legacy_descriptor(core); +} + +typedef struct E1000E_RSSInfo_st { + bool enabled; + uint32_t hash; + uint32_t queue; + uint32_t type; +} E1000E_RSSInfo; + +static uint32_t +igb_rss_get_hash_type(IGBCore *core, struct NetRxPkt *pkt) +{ + bool hasip4, hasip6; + EthL4HdrProto l4hdr_proto; + + assert(igb_rss_enabled(core)); + + net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto); + + if (hasip4) { + trace_e1000e_rx_rss_ip4(l4hdr_proto, core->mac[MRQC], + E1000_MRQC_EN_TCPIPV4(core->mac[MRQC]), + E1000_MRQC_EN_IPV4(core->mac[MRQC])); + + if (l4hdr_proto == ETH_L4_HDR_PROTO_TCP && + E1000_MRQC_EN_TCPIPV4(core->mac[MRQC])) { + return E1000_MRQ_RSS_TYPE_IPV4TCP; + } + + if (l4hdr_proto == ETH_L4_HDR_PROTO_UDP && + (core->mac[MRQC] & E1000_MRQC_RSS_FIELD_IPV4_UDP)) { + return E1000_MRQ_RSS_TYPE_IPV4UDP; + } + + if (E1000_MRQC_EN_IPV4(core->mac[MRQC])) { + return E1000_MRQ_RSS_TYPE_IPV4; + } + } else if (hasip6) { + eth_ip6_hdr_info *ip6info = net_rx_pkt_get_ip6_info(pkt); + + bool ex_dis = core->mac[RFCTL] & E1000_RFCTL_IPV6_EX_DIS; + bool new_ex_dis = core->mac[RFCTL] & E1000_RFCTL_NEW_IPV6_EXT_DIS; + + /* + * Following two traces must not be combined because resulting + * event will have 11 arguments totally and some trace backends + * (at least "ust") have limitation of maximum 10 arguments per + * event. Events with more arguments fail to compile for + * backends like these. + */ + trace_e1000e_rx_rss_ip6_rfctl(core->mac[RFCTL]); + trace_e1000e_rx_rss_ip6(ex_dis, new_ex_dis, l4hdr_proto, + ip6info->has_ext_hdrs, + ip6info->rss_ex_dst_valid, + ip6info->rss_ex_src_valid, + core->mac[MRQC], + E1000_MRQC_EN_TCPIPV6EX(core->mac[MRQC]), + E1000_MRQC_EN_IPV6EX(core->mac[MRQC]), + E1000_MRQC_EN_IPV6(core->mac[MRQC])); + + if ((!ex_dis || !ip6info->has_ext_hdrs) && + (!new_ex_dis || !(ip6info->rss_ex_dst_valid || + ip6info->rss_ex_src_valid))) { + + if (l4hdr_proto == ETH_L4_HDR_PROTO_TCP && + E1000_MRQC_EN_TCPIPV6EX(core->mac[MRQC])) { + return E1000_MRQ_RSS_TYPE_IPV6TCPEX; + } + + if (l4hdr_proto == ETH_L4_HDR_PROTO_UDP && + (core->mac[MRQC] & E1000_MRQC_RSS_FIELD_IPV6_UDP)) { + return E1000_MRQ_RSS_TYPE_IPV6UDP; + } + + if (E1000_MRQC_EN_IPV6EX(core->mac[MRQC])) { + return E1000_MRQ_RSS_TYPE_IPV6EX; + } + + } + + if (E1000_MRQC_EN_IPV6(core->mac[MRQC])) { + return E1000_MRQ_RSS_TYPE_IPV6; + } + + } + + return E1000_MRQ_RSS_TYPE_NONE; +} + +static uint32_t +igb_rss_calc_hash(IGBCore *core, struct NetRxPkt *pkt, E1000E_RSSInfo *info) +{ + NetRxPktRssType type; + + assert(igb_rss_enabled(core)); + + switch (info->type) { + case E1000_MRQ_RSS_TYPE_IPV4: + type = NetPktRssIpV4; + break; + case E1000_MRQ_RSS_TYPE_IPV4TCP: + type = NetPktRssIpV4Tcp; + break; + case E1000_MRQ_RSS_TYPE_IPV6TCPEX: + type = NetPktRssIpV6TcpEx; + break; + case E1000_MRQ_RSS_TYPE_IPV6: + type = NetPktRssIpV6; + break; + case E1000_MRQ_RSS_TYPE_IPV6EX: + type = NetPktRssIpV6Ex; + break; + case E1000_MRQ_RSS_TYPE_IPV4UDP: + type = NetPktRssIpV4Udp; + break; + case E1000_MRQ_RSS_TYPE_IPV6UDP: + type = NetPktRssIpV6Udp; + break; + default: + assert(false); + return 0; + } + + return net_rx_pkt_calc_rss_hash(pkt, type, (uint8_t *) &core->mac[RSSRK]); +} + +static void +igb_rss_parse_packet(IGBCore *core, struct NetRxPkt *pkt, bool tx, + E1000E_RSSInfo *info) +{ + trace_e1000e_rx_rss_started(); + + if (tx || !igb_rss_enabled(core)) { + info->enabled = false; + info->hash = 0; + info->queue = 0; + info->type = 0; + trace_e1000e_rx_rss_disabled(); + return; + } + + info->enabled = true; + + info->type = igb_rss_get_hash_type(core, pkt); + + trace_e1000e_rx_rss_type(info->type); + + if (info->type == E1000_MRQ_RSS_TYPE_NONE) { + info->hash = 0; + info->queue = 0; + return; + } + + info->hash = igb_rss_calc_hash(core, pkt, info); + info->queue = E1000_RSS_QUEUE(&core->mac[RETA], info->hash); +} + +static void +igb_tx_insert_vlan(IGBCore *core, uint16_t qn, struct igb_tx *tx, + uint16_t vlan, bool insert_vlan) +{ + if (core->mac[MRQC] & 1) { + uint16_t pool = qn % IGB_NUM_VM_POOLS; + + if (core->mac[VMVIR0 + pool] & E1000_VMVIR_VLANA_DEFAULT) { + /* always insert default VLAN */ + insert_vlan = true; + vlan = core->mac[VMVIR0 + pool] & 0xffff; + } else if (core->mac[VMVIR0 + pool] & E1000_VMVIR_VLANA_NEVER) { + insert_vlan = false; + } + } + + if (insert_vlan) { + net_tx_pkt_setup_vlan_header_ex(tx->tx_pkt, vlan, + core->mac[VET] & 0xffff); + } +} + +static bool +igb_setup_tx_offloads(IGBCore *core, struct igb_tx *tx) +{ + uint32_t idx = (tx->first_olinfo_status >> 4) & 1; + + if (tx->first_cmd_type_len & E1000_ADVTXD_DCMD_TSE) { + uint32_t mss = tx->ctx[idx].mss_l4len_idx >> E1000_ADVTXD_MSS_SHIFT; + if (!net_tx_pkt_build_vheader(tx->tx_pkt, true, true, mss)) { + return false; + } + + net_tx_pkt_update_ip_checksums(tx->tx_pkt); + e1000x_inc_reg_if_not_full(core->mac, TSCTC); + return true; + } + + if ((tx->first_olinfo_status & E1000_ADVTXD_POTS_TXSM) && + !((tx->ctx[idx].type_tucmd_mlhl & E1000_ADVTXD_TUCMD_L4T_SCTP) ? + net_tx_pkt_update_sctp_checksum(tx->tx_pkt) : + net_tx_pkt_build_vheader(tx->tx_pkt, false, true, 0))) { + return false; + } + + if (tx->first_olinfo_status & E1000_ADVTXD_POTS_IXSM) { + net_tx_pkt_update_ip_hdr_checksum(tx->tx_pkt); + } + + return true; +} + +static void igb_tx_pkt_mac_callback(void *core, + const struct iovec *iov, + int iovcnt, + const struct iovec *virt_iov, + int virt_iovcnt) +{ + igb_receive_internal(core, virt_iov, virt_iovcnt, true, NULL); +} + +static void igb_tx_pkt_vmdq_callback(void *opaque, + const struct iovec *iov, + int iovcnt, + const struct iovec *virt_iov, + int virt_iovcnt) +{ + IGBTxPktVmdqCallbackContext *context = opaque; + bool external_tx; + + igb_receive_internal(context->core, virt_iov, virt_iovcnt, true, + &external_tx); + + if (external_tx) { + if (context->core->has_vnet) { + qemu_sendv_packet(context->nc, virt_iov, virt_iovcnt); + } else { + qemu_sendv_packet(context->nc, iov, iovcnt); + } + } +} + +/* TX Packets Switching (7.10.3.6) */ +static bool igb_tx_pkt_switch(IGBCore *core, struct igb_tx *tx, + NetClientState *nc) +{ + IGBTxPktVmdqCallbackContext context; + + /* TX switching is only used to serve VM to VM traffic. */ + if (!(core->mac[MRQC] & 1)) { + goto send_out; + } + + /* TX switching requires DTXSWC.Loopback_en bit enabled. */ + if (!(core->mac[DTXSWC] & E1000_DTXSWC_VMDQ_LOOPBACK_EN)) { + goto send_out; + } + + context.core = core; + context.nc = nc; + + return net_tx_pkt_send_custom(tx->tx_pkt, false, + igb_tx_pkt_vmdq_callback, &context); + +send_out: + return net_tx_pkt_send(tx->tx_pkt, nc); +} + +static bool +igb_tx_pkt_send(IGBCore *core, struct igb_tx *tx, int queue_index) +{ + int target_queue = MIN(core->max_queue_num, queue_index); + NetClientState *queue = qemu_get_subqueue(core->owner_nic, target_queue); + + if (!igb_setup_tx_offloads(core, tx)) { + return false; + } + + net_tx_pkt_dump(tx->tx_pkt); + + if ((core->phy[MII_BMCR] & MII_BMCR_LOOPBACK) || + ((core->mac[RCTL] & E1000_RCTL_LBM_MAC) == E1000_RCTL_LBM_MAC)) { + return net_tx_pkt_send_custom(tx->tx_pkt, false, + igb_tx_pkt_mac_callback, core); + } else { + return igb_tx_pkt_switch(core, tx, queue); + } +} + +static void +igb_on_tx_done_update_stats(IGBCore *core, struct NetTxPkt *tx_pkt, int qn) +{ + static const int PTCregs[6] = { PTC64, PTC127, PTC255, PTC511, + PTC1023, PTC1522 }; + + size_t tot_len = net_tx_pkt_get_total_len(tx_pkt) + 4; + + e1000x_increase_size_stats(core->mac, PTCregs, tot_len); + e1000x_inc_reg_if_not_full(core->mac, TPT); + e1000x_grow_8reg_if_not_full(core->mac, TOTL, tot_len); + + switch (net_tx_pkt_get_packet_type(tx_pkt)) { + case ETH_PKT_BCAST: + e1000x_inc_reg_if_not_full(core->mac, BPTC); + break; + case ETH_PKT_MCAST: + e1000x_inc_reg_if_not_full(core->mac, MPTC); + break; + case ETH_PKT_UCAST: + break; + default: + g_assert_not_reached(); + } + + e1000x_inc_reg_if_not_full(core->mac, GPTC); + e1000x_grow_8reg_if_not_full(core->mac, GOTCL, tot_len); + + if (core->mac[MRQC] & 1) { + uint16_t pool = qn % IGB_NUM_VM_POOLS; + + core->mac[PVFGOTC0 + (pool * 64)] += tot_len; + core->mac[PVFGPTC0 + (pool * 64)]++; + } +} + +static void +igb_process_tx_desc(IGBCore *core, + PCIDevice *dev, + struct igb_tx *tx, + union e1000_adv_tx_desc *tx_desc, + int queue_index) +{ + struct e1000_adv_tx_context_desc *tx_ctx_desc; + uint32_t cmd_type_len; + uint32_t idx; + uint64_t buffer_addr; + uint16_t length; + + cmd_type_len = le32_to_cpu(tx_desc->read.cmd_type_len); + + if (cmd_type_len & E1000_ADVTXD_DCMD_DEXT) { + if ((cmd_type_len & E1000_ADVTXD_DTYP_DATA) == + E1000_ADVTXD_DTYP_DATA) { + /* advanced transmit data descriptor */ + if (tx->first) { + tx->first_cmd_type_len = cmd_type_len; + tx->first_olinfo_status = le32_to_cpu(tx_desc->read.olinfo_status); + tx->first = false; + } + } else if ((cmd_type_len & E1000_ADVTXD_DTYP_CTXT) == + E1000_ADVTXD_DTYP_CTXT) { + /* advanced transmit context descriptor */ + tx_ctx_desc = (struct e1000_adv_tx_context_desc *)tx_desc; + idx = (le32_to_cpu(tx_ctx_desc->mss_l4len_idx) >> 4) & 1; + tx->ctx[idx].vlan_macip_lens = le32_to_cpu(tx_ctx_desc->vlan_macip_lens); + tx->ctx[idx].seqnum_seed = le32_to_cpu(tx_ctx_desc->seqnum_seed); + tx->ctx[idx].type_tucmd_mlhl = le32_to_cpu(tx_ctx_desc->type_tucmd_mlhl); + tx->ctx[idx].mss_l4len_idx = le32_to_cpu(tx_ctx_desc->mss_l4len_idx); + return; + } else { + /* unknown descriptor type */ + return; + } + } else { + /* legacy descriptor */ + + /* TODO: Implement a support for legacy descriptors (7.2.2.1). */ + } + + buffer_addr = le64_to_cpu(tx_desc->read.buffer_addr); + length = cmd_type_len & 0xFFFF; + + if (!tx->skip_cp) { + if (!net_tx_pkt_add_raw_fragment_pci(tx->tx_pkt, dev, + buffer_addr, length)) { + tx->skip_cp = true; + } + } + + if (cmd_type_len & E1000_TXD_CMD_EOP) { + if (!tx->skip_cp && net_tx_pkt_parse(tx->tx_pkt)) { + idx = (tx->first_olinfo_status >> 4) & 1; + igb_tx_insert_vlan(core, queue_index, tx, + tx->ctx[idx].vlan_macip_lens >> IGB_TX_FLAGS_VLAN_SHIFT, + !!(tx->first_cmd_type_len & E1000_TXD_CMD_VLE)); + + if ((tx->first_cmd_type_len & E1000_ADVTXD_MAC_TSTAMP) && + (core->mac[TSYNCTXCTL] & E1000_TSYNCTXCTL_ENABLED) && + !(core->mac[TSYNCTXCTL] & E1000_TSYNCTXCTL_VALID)) { + core->mac[TSYNCTXCTL] |= E1000_TSYNCTXCTL_VALID; + e1000x_timestamp(core->mac, core->timadj, TXSTMPL, TXSTMPH); + } + + if (igb_tx_pkt_send(core, tx, queue_index)) { + igb_on_tx_done_update_stats(core, tx->tx_pkt, queue_index); + } + } + + tx->first = true; + tx->skip_cp = false; + net_tx_pkt_reset(tx->tx_pkt, net_tx_pkt_unmap_frag_pci, dev); + } +} + +static uint32_t igb_tx_wb_eic(IGBCore *core, int queue_idx) +{ + uint32_t n, ent = 0; + + n = igb_ivar_entry_tx(queue_idx); + ent = (core->mac[IVAR0 + n / 4] >> (8 * (n % 4))) & 0xff; + + return (ent & E1000_IVAR_VALID) ? BIT(ent & 0x1f) : 0; +} + +static uint32_t igb_rx_wb_eic(IGBCore *core, int queue_idx) +{ + uint32_t n, ent = 0; + + n = igb_ivar_entry_rx(queue_idx); + ent = (core->mac[IVAR0 + n / 4] >> (8 * (n % 4))) & 0xff; + + return (ent & E1000_IVAR_VALID) ? BIT(ent & 0x1f) : 0; +} + +static inline bool +igb_ring_empty(IGBCore *core, const E1000ERingInfo *r) +{ + return core->mac[r->dh] == core->mac[r->dt] || + core->mac[r->dt] >= core->mac[r->dlen] / E1000_RING_DESC_LEN; +} + +static inline uint64_t +igb_ring_base(IGBCore *core, const E1000ERingInfo *r) +{ + uint64_t bah = core->mac[r->dbah]; + uint64_t bal = core->mac[r->dbal]; + + return (bah << 32) + bal; +} + +static inline uint64_t +igb_ring_head_descr(IGBCore *core, const E1000ERingInfo *r) +{ + return igb_ring_base(core, r) + E1000_RING_DESC_LEN * core->mac[r->dh]; +} + +static inline void +igb_ring_advance(IGBCore *core, const E1000ERingInfo *r, uint32_t count) +{ + core->mac[r->dh] += count; + + if (core->mac[r->dh] * E1000_RING_DESC_LEN >= core->mac[r->dlen]) { + core->mac[r->dh] = 0; + } +} + +static inline uint32_t +igb_ring_free_descr_num(IGBCore *core, const E1000ERingInfo *r) +{ + trace_e1000e_ring_free_space(r->idx, core->mac[r->dlen], + core->mac[r->dh], core->mac[r->dt]); + + if (core->mac[r->dh] <= core->mac[r->dt]) { + return core->mac[r->dt] - core->mac[r->dh]; + } + + if (core->mac[r->dh] > core->mac[r->dt]) { + return core->mac[r->dlen] / E1000_RING_DESC_LEN + + core->mac[r->dt] - core->mac[r->dh]; + } + + g_assert_not_reached(); + return 0; +} + +static inline bool +igb_ring_enabled(IGBCore *core, const E1000ERingInfo *r) +{ + return core->mac[r->dlen] > 0; +} + +typedef struct IGB_TxRing_st { + const E1000ERingInfo *i; + struct igb_tx *tx; +} IGB_TxRing; + +static inline int +igb_mq_queue_idx(int base_reg_idx, int reg_idx) +{ + return (reg_idx - base_reg_idx) / 16; +} + +static inline void +igb_tx_ring_init(IGBCore *core, IGB_TxRing *txr, int idx) +{ + static const E1000ERingInfo i[IGB_NUM_QUEUES] = { + { TDBAH0, TDBAL0, TDLEN0, TDH0, TDT0, 0 }, + { TDBAH1, TDBAL1, TDLEN1, TDH1, TDT1, 1 }, + { TDBAH2, TDBAL2, TDLEN2, TDH2, TDT2, 2 }, + { TDBAH3, TDBAL3, TDLEN3, TDH3, TDT3, 3 }, + { TDBAH4, TDBAL4, TDLEN4, TDH4, TDT4, 4 }, + { TDBAH5, TDBAL5, TDLEN5, TDH5, TDT5, 5 }, + { TDBAH6, TDBAL6, TDLEN6, TDH6, TDT6, 6 }, + { TDBAH7, TDBAL7, TDLEN7, TDH7, TDT7, 7 }, + { TDBAH8, TDBAL8, TDLEN8, TDH8, TDT8, 8 }, + { TDBAH9, TDBAL9, TDLEN9, TDH9, TDT9, 9 }, + { TDBAH10, TDBAL10, TDLEN10, TDH10, TDT10, 10 }, + { TDBAH11, TDBAL11, TDLEN11, TDH11, TDT11, 11 }, + { TDBAH12, TDBAL12, TDLEN12, TDH12, TDT12, 12 }, + { TDBAH13, TDBAL13, TDLEN13, TDH13, TDT13, 13 }, + { TDBAH14, TDBAL14, TDLEN14, TDH14, TDT14, 14 }, + { TDBAH15, TDBAL15, TDLEN15, TDH15, TDT15, 15 } + }; + + assert(idx < ARRAY_SIZE(i)); + + txr->i = &i[idx]; + txr->tx = &core->tx[idx]; +} + +typedef struct E1000E_RxRing_st { + const E1000ERingInfo *i; +} E1000E_RxRing; + +static inline void +igb_rx_ring_init(IGBCore *core, E1000E_RxRing *rxr, int idx) +{ + static const E1000ERingInfo i[IGB_NUM_QUEUES] = { + { RDBAH0, RDBAL0, RDLEN0, RDH0, RDT0, 0 }, + { RDBAH1, RDBAL1, RDLEN1, RDH1, RDT1, 1 }, + { RDBAH2, RDBAL2, RDLEN2, RDH2, RDT2, 2 }, + { RDBAH3, RDBAL3, RDLEN3, RDH3, RDT3, 3 }, + { RDBAH4, RDBAL4, RDLEN4, RDH4, RDT4, 4 }, + { RDBAH5, RDBAL5, RDLEN5, RDH5, RDT5, 5 }, + { RDBAH6, RDBAL6, RDLEN6, RDH6, RDT6, 6 }, + { RDBAH7, RDBAL7, RDLEN7, RDH7, RDT7, 7 }, + { RDBAH8, RDBAL8, RDLEN8, RDH8, RDT8, 8 }, + { RDBAH9, RDBAL9, RDLEN9, RDH9, RDT9, 9 }, + { RDBAH10, RDBAL10, RDLEN10, RDH10, RDT10, 10 }, + { RDBAH11, RDBAL11, RDLEN11, RDH11, RDT11, 11 }, + { RDBAH12, RDBAL12, RDLEN12, RDH12, RDT12, 12 }, + { RDBAH13, RDBAL13, RDLEN13, RDH13, RDT13, 13 }, + { RDBAH14, RDBAL14, RDLEN14, RDH14, RDT14, 14 }, + { RDBAH15, RDBAL15, RDLEN15, RDH15, RDT15, 15 } + }; + + assert(idx < ARRAY_SIZE(i)); + + rxr->i = &i[idx]; +} + +static uint32_t +igb_txdesc_writeback(IGBCore *core, dma_addr_t base, + union e1000_adv_tx_desc *tx_desc, + const E1000ERingInfo *txi) +{ + PCIDevice *d; + uint32_t cmd_type_len = le32_to_cpu(tx_desc->read.cmd_type_len); + uint64_t tdwba; + + tdwba = core->mac[E1000_TDWBAL(txi->idx) >> 2]; + tdwba |= (uint64_t)core->mac[E1000_TDWBAH(txi->idx) >> 2] << 32; + + if (!(cmd_type_len & E1000_TXD_CMD_RS)) { + return 0; + } + + d = pcie_sriov_get_vf_at_index(core->owner, txi->idx % 8); + if (!d) { + d = core->owner; + } + + if (tdwba & 1) { + uint32_t buffer = cpu_to_le32(core->mac[txi->dh]); + pci_dma_write(d, tdwba & ~3, &buffer, sizeof(buffer)); + } else { + uint32_t status = le32_to_cpu(tx_desc->wb.status) | E1000_TXD_STAT_DD; + + tx_desc->wb.status = cpu_to_le32(status); + pci_dma_write(d, base + offsetof(union e1000_adv_tx_desc, wb), + &tx_desc->wb, sizeof(tx_desc->wb)); + } + + return igb_tx_wb_eic(core, txi->idx); +} + +static inline bool +igb_tx_enabled(IGBCore *core, const E1000ERingInfo *txi) +{ + bool vmdq = core->mac[MRQC] & 1; + uint16_t qn = txi->idx; + uint16_t pool = qn % IGB_NUM_VM_POOLS; + + return (core->mac[TCTL] & E1000_TCTL_EN) && + (!vmdq || core->mac[VFTE] & BIT(pool)) && + (core->mac[TXDCTL0 + (qn * 16)] & E1000_TXDCTL_QUEUE_ENABLE); +} + +static void +igb_start_xmit(IGBCore *core, const IGB_TxRing *txr) +{ + PCIDevice *d; + dma_addr_t base; + union e1000_adv_tx_desc desc; + const E1000ERingInfo *txi = txr->i; + uint32_t eic = 0; + + if (!igb_tx_enabled(core, txi)) { + trace_e1000e_tx_disabled(); + return; + } + + d = pcie_sriov_get_vf_at_index(core->owner, txi->idx % 8); + if (!d) { + d = core->owner; + } + + while (!igb_ring_empty(core, txi)) { + base = igb_ring_head_descr(core, txi); + + pci_dma_read(d, base, &desc, sizeof(desc)); + + trace_e1000e_tx_descr((void *)(intptr_t)desc.read.buffer_addr, + desc.read.cmd_type_len, desc.wb.status); + + igb_process_tx_desc(core, d, txr->tx, &desc, txi->idx); + igb_ring_advance(core, txi, 1); + eic |= igb_txdesc_writeback(core, base, &desc, txi); + } + + if (eic) { + igb_raise_interrupts(core, EICR, eic); + igb_raise_interrupts(core, ICR, E1000_ICR_TXDW); + } + + net_tx_pkt_reset(txr->tx->tx_pkt, net_tx_pkt_unmap_frag_pci, d); +} + +static uint32_t +igb_rxbufsize(IGBCore *core, const E1000ERingInfo *r) +{ + uint32_t srrctl = core->mac[E1000_SRRCTL(r->idx) >> 2]; + uint32_t bsizepkt = srrctl & E1000_SRRCTL_BSIZEPKT_MASK; + if (bsizepkt) { + return bsizepkt << E1000_SRRCTL_BSIZEPKT_SHIFT; + } + + return e1000x_rxbufsize(core->mac[RCTL]); +} + +static bool +igb_has_rxbufs(IGBCore *core, const E1000ERingInfo *r, size_t total_size) +{ + uint32_t bufs = igb_ring_free_descr_num(core, r); + uint32_t bufsize = igb_rxbufsize(core, r); + + trace_e1000e_rx_has_buffers(r->idx, bufs, total_size, bufsize); + + return total_size <= bufs / (core->rx_desc_len / E1000_MIN_RX_DESC_LEN) * + bufsize; +} + +static uint32_t +igb_rxhdrbufsize(IGBCore *core, const E1000ERingInfo *r) +{ + uint32_t srrctl = core->mac[E1000_SRRCTL(r->idx) >> 2]; + return (srrctl & E1000_SRRCTL_BSIZEHDRSIZE_MASK) >> + E1000_SRRCTL_BSIZEHDRSIZE_SHIFT; +} + +void +igb_start_recv(IGBCore *core) +{ + int i; + + trace_e1000e_rx_start_recv(); + + for (i = 0; i <= core->max_queue_num; i++) { + qemu_flush_queued_packets(qemu_get_subqueue(core->owner_nic, i)); + } +} + +bool +igb_can_receive(IGBCore *core) +{ + int i; + + if (!e1000x_rx_ready(core->owner, core->mac)) { + return false; + } + + for (i = 0; i < IGB_NUM_QUEUES; i++) { + E1000E_RxRing rxr; + if (!(core->mac[RXDCTL0 + (i * 16)] & E1000_RXDCTL_QUEUE_ENABLE)) { + continue; + } + + igb_rx_ring_init(core, &rxr, i); + if (igb_ring_enabled(core, rxr.i) && igb_has_rxbufs(core, rxr.i, 1)) { + trace_e1000e_rx_can_recv(); + return true; + } + } + + trace_e1000e_rx_can_recv_rings_full(); + return false; +} + +ssize_t +igb_receive(IGBCore *core, const uint8_t *buf, size_t size) +{ + const struct iovec iov = { + .iov_base = (uint8_t *)buf, + .iov_len = size + }; + + return igb_receive_iov(core, &iov, 1); +} + +static inline bool +igb_rx_l3_cso_enabled(IGBCore *core) +{ + return !!(core->mac[RXCSUM] & E1000_RXCSUM_IPOFLD); +} + +static inline bool +igb_rx_l4_cso_enabled(IGBCore *core) +{ + return !!(core->mac[RXCSUM] & E1000_RXCSUM_TUOFLD); +} + +static bool igb_rx_is_oversized(IGBCore *core, const struct eth_header *ehdr, + size_t size, size_t vlan_num, + bool lpe, uint16_t rlpml) +{ + size_t vlan_header_size = sizeof(struct vlan_header) * vlan_num; + size_t header_size = sizeof(struct eth_header) + vlan_header_size; + return lpe ? size + ETH_FCS_LEN > rlpml : size > header_size + ETH_MTU; +} + +static uint16_t igb_receive_assign(IGBCore *core, const struct iovec *iov, + size_t iovcnt, size_t iov_ofs, + const L2Header *l2_header, size_t size, + E1000E_RSSInfo *rss_info, + uint16_t *etqf, bool *ts, bool *external_tx) +{ + static const int ta_shift[] = { 4, 3, 2, 0 }; + const struct eth_header *ehdr = &l2_header->eth; + uint32_t f, ra[2], *macp, rctl = core->mac[RCTL]; + uint16_t queues = 0; + uint16_t oversized = 0; + size_t vlan_num = 0; + PTP2 ptp2; + bool lpe; + uint16_t rlpml; + int i; + + memset(rss_info, 0, sizeof(E1000E_RSSInfo)); + *ts = false; + + if (external_tx) { + *external_tx = true; + } + + if (core->mac[CTRL_EXT] & BIT(26)) { + if (be16_to_cpu(ehdr->h_proto) == core->mac[VET] >> 16 && + be16_to_cpu(l2_header->vlan[0].h_proto) == (core->mac[VET] & 0xffff)) { + vlan_num = 2; + } + } else { + if (be16_to_cpu(ehdr->h_proto) == (core->mac[VET] & 0xffff)) { + vlan_num = 1; + } + } + + lpe = !!(core->mac[RCTL] & E1000_RCTL_LPE); + rlpml = core->mac[RLPML]; + if (!(core->mac[RCTL] & E1000_RCTL_SBP) && + igb_rx_is_oversized(core, ehdr, size, vlan_num, lpe, rlpml)) { + trace_e1000x_rx_oversized(size); + return queues; + } + + for (*etqf = 0; *etqf < 8; (*etqf)++) { + if ((core->mac[ETQF0 + *etqf] & E1000_ETQF_FILTER_ENABLE) && + be16_to_cpu(ehdr->h_proto) == (core->mac[ETQF0 + *etqf] & E1000_ETQF_ETYPE_MASK)) { + if ((core->mac[ETQF0 + *etqf] & E1000_ETQF_1588) && + (core->mac[TSYNCRXCTL] & E1000_TSYNCRXCTL_ENABLED) && + !(core->mac[TSYNCRXCTL] & E1000_TSYNCRXCTL_VALID) && + iov_to_buf(iov, iovcnt, iov_ofs + ETH_HLEN, &ptp2, sizeof(ptp2)) >= sizeof(ptp2) && + (ptp2.version_ptp & 15) == 2 && + ptp2.message_id_transport_specific == ((core->mac[TSYNCRXCFG] >> 8) & 255)) { + e1000x_timestamp(core->mac, core->timadj, RXSTMPL, RXSTMPH); + *ts = true; + core->mac[TSYNCRXCTL] |= E1000_TSYNCRXCTL_VALID; + core->mac[RXSATRL] = le32_to_cpu(ptp2.source_uuid_lo); + core->mac[RXSATRH] = le16_to_cpu(ptp2.source_uuid_hi) | + (le16_to_cpu(ptp2.sequence_id) << 16); + } + break; + } + } + + if (vlan_num && + !e1000x_rx_vlan_filter(core->mac, l2_header->vlan + vlan_num - 1)) { + return queues; + } + + if (core->mac[MRQC] & 1) { + if (is_broadcast_ether_addr(ehdr->h_dest)) { + for (i = 0; i < IGB_NUM_VM_POOLS; i++) { + if (core->mac[VMOLR0 + i] & E1000_VMOLR_BAM) { + queues |= BIT(i); + } + } + } else { + for (macp = core->mac + RA; macp < core->mac + RA + 32; macp += 2) { + if (!(macp[1] & E1000_RAH_AV)) { + continue; + } + ra[0] = cpu_to_le32(macp[0]); + ra[1] = cpu_to_le32(macp[1]); + if (!memcmp(ehdr->h_dest, (uint8_t *)ra, ETH_ALEN)) { + queues |= (macp[1] & E1000_RAH_POOL_MASK) / E1000_RAH_POOL_1; + } + } + + for (macp = core->mac + RA2; macp < core->mac + RA2 + 16; macp += 2) { + if (!(macp[1] & E1000_RAH_AV)) { + continue; + } + ra[0] = cpu_to_le32(macp[0]); + ra[1] = cpu_to_le32(macp[1]); + if (!memcmp(ehdr->h_dest, (uint8_t *)ra, ETH_ALEN)) { + queues |= (macp[1] & E1000_RAH_POOL_MASK) / E1000_RAH_POOL_1; + } + } + + if (!queues) { + macp = core->mac + (is_multicast_ether_addr(ehdr->h_dest) ? MTA : UTA); + + f = ta_shift[(rctl >> E1000_RCTL_MO_SHIFT) & 3]; + f = (((ehdr->h_dest[5] << 8) | ehdr->h_dest[4]) >> f) & 0xfff; + if (macp[f >> 5] & (1 << (f & 0x1f))) { + for (i = 0; i < IGB_NUM_VM_POOLS; i++) { + if (core->mac[VMOLR0 + i] & E1000_VMOLR_ROMPE) { + queues |= BIT(i); + } + } + } + } else if (is_unicast_ether_addr(ehdr->h_dest) && external_tx) { + *external_tx = false; + } + } + + if (e1000x_vlan_rx_filter_enabled(core->mac)) { + uint16_t mask = 0; + + if (vlan_num) { + uint16_t vid = be16_to_cpu(l2_header->vlan[vlan_num - 1].h_tci) & VLAN_VID_MASK; + + for (i = 0; i < E1000_VLVF_ARRAY_SIZE; i++) { + if ((core->mac[VLVF0 + i] & E1000_VLVF_VLANID_MASK) == vid && + (core->mac[VLVF0 + i] & E1000_VLVF_VLANID_ENABLE)) { + uint32_t poolsel = core->mac[VLVF0 + i] & E1000_VLVF_POOLSEL_MASK; + mask |= poolsel >> E1000_VLVF_POOLSEL_SHIFT; + } + } + } else { + for (i = 0; i < IGB_NUM_VM_POOLS; i++) { + if (core->mac[VMOLR0 + i] & E1000_VMOLR_AUPE) { + mask |= BIT(i); + } + } + } + + queues &= mask; + } + + if (is_unicast_ether_addr(ehdr->h_dest) && !queues && !external_tx && + !(core->mac[VT_CTL] & E1000_VT_CTL_DISABLE_DEF_POOL)) { + uint32_t def_pl = core->mac[VT_CTL] & E1000_VT_CTL_DEFAULT_POOL_MASK; + queues = BIT(def_pl >> E1000_VT_CTL_DEFAULT_POOL_SHIFT); + } + + queues &= core->mac[VFRE]; + if (queues) { + for (i = 0; i < IGB_NUM_VM_POOLS; i++) { + lpe = !!(core->mac[VMOLR0 + i] & E1000_VMOLR_LPE); + rlpml = core->mac[VMOLR0 + i] & E1000_VMOLR_RLPML_MASK; + if ((queues & BIT(i)) && + igb_rx_is_oversized(core, ehdr, size, vlan_num, + lpe, rlpml)) { + oversized |= BIT(i); + } + } + /* 8.19.37 increment ROC if packet is oversized for all queues */ + if (oversized == queues) { + trace_e1000x_rx_oversized(size); + e1000x_inc_reg_if_not_full(core->mac, ROC); + } + queues &= ~oversized; + } + + if (queues) { + igb_rss_parse_packet(core, core->rx_pkt, + external_tx != NULL, rss_info); + /* Sec 8.26.1: PQn = VFn + VQn*8 */ + if (rss_info->queue & 1) { + for (i = 0; i < IGB_NUM_VM_POOLS; i++) { + if ((queues & BIT(i)) && + (core->mac[VMOLR0 + i] & E1000_VMOLR_RSSE)) { + queues |= BIT(i + IGB_NUM_VM_POOLS); + queues &= ~BIT(i); + } + } + } + } + } else { + bool accepted = e1000x_rx_group_filter(core->mac, ehdr); + if (!accepted) { + for (macp = core->mac + RA2; macp < core->mac + RA2 + 16; macp += 2) { + if (!(macp[1] & E1000_RAH_AV)) { + continue; + } + ra[0] = cpu_to_le32(macp[0]); + ra[1] = cpu_to_le32(macp[1]); + if (!memcmp(ehdr->h_dest, (uint8_t *)ra, ETH_ALEN)) { + trace_e1000x_rx_flt_ucast_match((int)(macp - core->mac - RA2) / 2, + MAC_ARG(ehdr->h_dest)); + + accepted = true; + break; + } + } + } + + if (accepted) { + igb_rss_parse_packet(core, core->rx_pkt, false, rss_info); + queues = BIT(rss_info->queue); + } + } + + return queues; +} + +static inline void +igb_read_lgcy_rx_descr(IGBCore *core, struct e1000_rx_desc *desc, + hwaddr *buff_addr) +{ + *buff_addr = le64_to_cpu(desc->buffer_addr); +} + +static inline void +igb_read_adv_rx_single_buf_descr(IGBCore *core, union e1000_adv_rx_desc *desc, + hwaddr *buff_addr) +{ + *buff_addr = le64_to_cpu(desc->read.pkt_addr); +} + +static inline void +igb_read_adv_rx_split_buf_descr(IGBCore *core, union e1000_adv_rx_desc *desc, + hwaddr *buff_addr) +{ + buff_addr[0] = le64_to_cpu(desc->read.hdr_addr); + buff_addr[1] = le64_to_cpu(desc->read.pkt_addr); +} + +typedef struct IGBBAState { + uint16_t written[IGB_MAX_PS_BUFFERS]; + uint8_t cur_idx; +} IGBBAState; + +typedef struct IGBSplitDescriptorData { + bool sph; + bool hbo; + size_t hdr_len; +} IGBSplitDescriptorData; + +typedef struct IGBPacketRxDMAState { + size_t size; + size_t total_size; + size_t ps_hdr_len; + size_t desc_size; + size_t desc_offset; + uint32_t rx_desc_packet_buf_size; + uint32_t rx_desc_header_buf_size; + struct iovec *iov; + size_t iov_ofs; + bool do_ps; + bool is_first; + IGBBAState bastate; + hwaddr ba[IGB_MAX_PS_BUFFERS]; + IGBSplitDescriptorData ps_desc_data; +} IGBPacketRxDMAState; + +static inline void +igb_read_rx_descr(IGBCore *core, + union e1000_rx_desc_union *desc, + IGBPacketRxDMAState *pdma_st, + const E1000ERingInfo *r) +{ + uint32_t desc_type; + + if (igb_rx_use_legacy_descriptor(core)) { + igb_read_lgcy_rx_descr(core, &desc->legacy, &pdma_st->ba[1]); + pdma_st->ba[0] = 0; + return; + } + + /* advanced header split descriptor */ + if (igb_rx_use_ps_descriptor(core, r)) { + igb_read_adv_rx_split_buf_descr(core, &desc->adv, &pdma_st->ba[0]); + return; + } + + /* descriptor replication modes not supported */ + desc_type = igb_rx_queue_desctyp_get(core, r); + if (desc_type != E1000_SRRCTL_DESCTYPE_ADV_ONEBUF) { + trace_igb_wrn_rx_desc_modes_not_supp(desc_type); + } + + /* advanced single buffer descriptor */ + igb_read_adv_rx_single_buf_descr(core, &desc->adv, &pdma_st->ba[1]); + pdma_st->ba[0] = 0; +} + +static void +igb_verify_csum_in_sw(IGBCore *core, + struct NetRxPkt *pkt, + uint32_t *status_flags, + EthL4HdrProto l4hdr_proto) +{ + bool csum_valid; + uint32_t csum_error; + + if (igb_rx_l3_cso_enabled(core)) { + if (!net_rx_pkt_validate_l3_csum(pkt, &csum_valid)) { + trace_e1000e_rx_metadata_l3_csum_validation_failed(); + } else { + csum_error = csum_valid ? 0 : E1000_RXDEXT_STATERR_IPE; + *status_flags |= E1000_RXD_STAT_IPCS | csum_error; + } + } else { + trace_e1000e_rx_metadata_l3_cso_disabled(); + } + + if (!igb_rx_l4_cso_enabled(core)) { + trace_e1000e_rx_metadata_l4_cso_disabled(); + return; + } + + if (!net_rx_pkt_validate_l4_csum(pkt, &csum_valid)) { + trace_e1000e_rx_metadata_l4_csum_validation_failed(); + return; + } + + csum_error = csum_valid ? 0 : E1000_RXDEXT_STATERR_TCPE; + *status_flags |= E1000_RXD_STAT_TCPCS | csum_error; + + if (l4hdr_proto == ETH_L4_HDR_PROTO_UDP) { + *status_flags |= E1000_RXD_STAT_UDPCS; + } +} + +static void +igb_build_rx_metadata_common(IGBCore *core, + struct NetRxPkt *pkt, + bool is_eop, + uint32_t *status_flags, + uint16_t *vlan_tag) +{ + struct virtio_net_hdr *vhdr; + bool hasip4, hasip6, csum_valid; + EthL4HdrProto l4hdr_proto; + + *status_flags = E1000_RXD_STAT_DD; + + /* No additional metadata needed for non-EOP descriptors */ + if (!is_eop) { + goto func_exit; + } + + *status_flags |= E1000_RXD_STAT_EOP; + + net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto); + trace_e1000e_rx_metadata_protocols(hasip4, hasip6, l4hdr_proto); + + /* VLAN state */ + if (net_rx_pkt_is_vlan_stripped(pkt)) { + *status_flags |= E1000_RXD_STAT_VP; + *vlan_tag = cpu_to_le16(net_rx_pkt_get_vlan_tag(pkt)); + trace_e1000e_rx_metadata_vlan(*vlan_tag); + } + + /* RX CSO information */ + if (hasip6 && (core->mac[RFCTL] & E1000_RFCTL_IPV6_XSUM_DIS)) { + trace_e1000e_rx_metadata_ipv6_sum_disabled(); + goto func_exit; + } + + vhdr = net_rx_pkt_get_vhdr(pkt); + + if (!(vhdr->flags & VIRTIO_NET_HDR_F_DATA_VALID) && + !(vhdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) { + trace_e1000e_rx_metadata_virthdr_no_csum_info(); + igb_verify_csum_in_sw(core, pkt, status_flags, l4hdr_proto); + goto func_exit; + } + + if (igb_rx_l3_cso_enabled(core)) { + *status_flags |= hasip4 ? E1000_RXD_STAT_IPCS : 0; + } else { + trace_e1000e_rx_metadata_l3_cso_disabled(); + } + + if (igb_rx_l4_cso_enabled(core)) { + switch (l4hdr_proto) { + case ETH_L4_HDR_PROTO_SCTP: + if (!net_rx_pkt_validate_l4_csum(pkt, &csum_valid)) { + trace_e1000e_rx_metadata_l4_csum_validation_failed(); + goto func_exit; + } + if (!csum_valid) { + *status_flags |= E1000_RXDEXT_STATERR_TCPE; + } + /* fall through */ + case ETH_L4_HDR_PROTO_TCP: + *status_flags |= E1000_RXD_STAT_TCPCS; + break; + + case ETH_L4_HDR_PROTO_UDP: + *status_flags |= E1000_RXD_STAT_TCPCS | E1000_RXD_STAT_UDPCS; + break; + + default: + break; + } + } else { + trace_e1000e_rx_metadata_l4_cso_disabled(); + } + +func_exit: + trace_e1000e_rx_metadata_status_flags(*status_flags); + *status_flags = cpu_to_le32(*status_flags); +} + +static inline void +igb_write_lgcy_rx_descr(IGBCore *core, struct e1000_rx_desc *desc, + struct NetRxPkt *pkt, + const E1000E_RSSInfo *rss_info, + uint16_t length) +{ + uint32_t status_flags; + + assert(!rss_info->enabled); + + memset(desc, 0, sizeof(*desc)); + desc->length = cpu_to_le16(length); + igb_build_rx_metadata_common(core, pkt, pkt != NULL, + &status_flags, + &desc->special); + + desc->errors = (uint8_t) (le32_to_cpu(status_flags) >> 24); + desc->status = (uint8_t) le32_to_cpu(status_flags); +} + +static bool +igb_rx_ps_descriptor_split_always(IGBCore *core, const E1000ERingInfo *r) +{ + uint32_t desctyp = igb_rx_queue_desctyp_get(core, r); + return desctyp == E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS; +} + +static uint16_t +igb_rx_desc_get_packet_type(IGBCore *core, struct NetRxPkt *pkt, uint16_t etqf) +{ + uint16_t pkt_type; + bool hasip4, hasip6; + EthL4HdrProto l4hdr_proto; + + if (etqf < 8) { + pkt_type = BIT(11) | etqf; + return pkt_type; + } + + net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto); + + if (hasip6 && !(core->mac[RFCTL] & E1000_RFCTL_IPV6_DIS)) { + eth_ip6_hdr_info *ip6hdr_info = net_rx_pkt_get_ip6_info(pkt); + pkt_type = ip6hdr_info->has_ext_hdrs ? E1000_ADVRXD_PKT_IP6E : + E1000_ADVRXD_PKT_IP6; + } else if (hasip4) { + pkt_type = E1000_ADVRXD_PKT_IP4; + } else { + pkt_type = 0; + } + + switch (l4hdr_proto) { + case ETH_L4_HDR_PROTO_TCP: + pkt_type |= E1000_ADVRXD_PKT_TCP; + break; + case ETH_L4_HDR_PROTO_UDP: + pkt_type |= E1000_ADVRXD_PKT_UDP; + break; + case ETH_L4_HDR_PROTO_SCTP: + pkt_type |= E1000_ADVRXD_PKT_SCTP; + break; + default: + break; + } + + return pkt_type; +} + +static inline void +igb_write_adv_rx_descr(IGBCore *core, union e1000_adv_rx_desc *desc, + struct NetRxPkt *pkt, + const E1000E_RSSInfo *rss_info, uint16_t etqf, bool ts, + uint16_t length) +{ + bool hasip4, hasip6; + EthL4HdrProto l4hdr_proto; + uint16_t rss_type = 0, pkt_type; + bool eop = (pkt != NULL); + uint32_t adv_desc_status_error = 0; + memset(&desc->wb, 0, sizeof(desc->wb)); + + desc->wb.upper.length = cpu_to_le16(length); + igb_build_rx_metadata_common(core, pkt, eop, + &desc->wb.upper.status_error, + &desc->wb.upper.vlan); + + if (!eop) { + return; + } + + net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto); + + if ((core->mac[RXCSUM] & E1000_RXCSUM_PCSD) != 0) { + if (rss_info->enabled) { + desc->wb.lower.hi_dword.rss = cpu_to_le32(rss_info->hash); + rss_type = rss_info->type; + trace_igb_rx_metadata_rss(desc->wb.lower.hi_dword.rss, rss_type); + } + } else if (hasip4) { + adv_desc_status_error |= E1000_RXD_STAT_IPIDV; + desc->wb.lower.hi_dword.csum_ip.ip_id = + cpu_to_le16(net_rx_pkt_get_ip_id(pkt)); + trace_e1000e_rx_metadata_ip_id( + desc->wb.lower.hi_dword.csum_ip.ip_id); + } + + if (ts) { + adv_desc_status_error |= BIT(16); + } + + pkt_type = igb_rx_desc_get_packet_type(core, pkt, etqf); + trace_e1000e_rx_metadata_pkt_type(pkt_type); + desc->wb.lower.lo_dword.pkt_info = cpu_to_le16(rss_type | (pkt_type << 4)); + desc->wb.upper.status_error |= cpu_to_le32(adv_desc_status_error); +} + +static inline void +igb_write_adv_ps_rx_descr(IGBCore *core, + union e1000_adv_rx_desc *desc, + struct NetRxPkt *pkt, + const E1000E_RSSInfo *rss_info, + const E1000ERingInfo *r, + uint16_t etqf, + bool ts, + IGBPacketRxDMAState *pdma_st) +{ + size_t pkt_len; + uint16_t hdr_info = 0; + + if (pdma_st->do_ps) { + pkt_len = pdma_st->bastate.written[1]; + } else { + pkt_len = pdma_st->bastate.written[0] + pdma_st->bastate.written[1]; + } + + igb_write_adv_rx_descr(core, desc, pkt, rss_info, etqf, ts, pkt_len); + + hdr_info = (pdma_st->ps_desc_data.hdr_len << E1000_ADVRXD_HDR_LEN_OFFSET) & + E1000_ADVRXD_ADV_HDR_LEN_MASK; + hdr_info |= pdma_st->ps_desc_data.sph ? E1000_ADVRXD_HDR_SPH : 0; + desc->wb.lower.lo_dword.hdr_info = cpu_to_le16(hdr_info); + + desc->wb.upper.status_error |= cpu_to_le32( + pdma_st->ps_desc_data.hbo ? E1000_ADVRXD_ST_ERR_HBO_OFFSET : 0); +} + +static inline void +igb_write_rx_descr(IGBCore *core, + union e1000_rx_desc_union *desc, + struct NetRxPkt *pkt, + const E1000E_RSSInfo *rss_info, + uint16_t etqf, + bool ts, + IGBPacketRxDMAState *pdma_st, + const E1000ERingInfo *r) +{ + if (igb_rx_use_legacy_descriptor(core)) { + igb_write_lgcy_rx_descr(core, &desc->legacy, pkt, rss_info, + pdma_st->bastate.written[1]); + } else if (igb_rx_use_ps_descriptor(core, r)) { + igb_write_adv_ps_rx_descr(core, &desc->adv, pkt, rss_info, r, etqf, ts, + pdma_st); + } else { + igb_write_adv_rx_descr(core, &desc->adv, pkt, rss_info, + etqf, ts, pdma_st->bastate.written[1]); + } +} + +static inline void +igb_pci_dma_write_rx_desc(IGBCore *core, PCIDevice *dev, dma_addr_t addr, + union e1000_rx_desc_union *desc, dma_addr_t len) +{ + if (igb_rx_use_legacy_descriptor(core)) { + struct e1000_rx_desc *d = &desc->legacy; + size_t offset = offsetof(struct e1000_rx_desc, status); + uint8_t status = d->status; + + d->status &= ~E1000_RXD_STAT_DD; + pci_dma_write(dev, addr, desc, len); + + if (status & E1000_RXD_STAT_DD) { + d->status = status; + pci_dma_write(dev, addr + offset, &status, sizeof(status)); + } + } else { + union e1000_adv_rx_desc *d = &desc->adv; + size_t offset = + offsetof(union e1000_adv_rx_desc, wb.upper.status_error); + uint32_t status = d->wb.upper.status_error; + + d->wb.upper.status_error &= ~E1000_RXD_STAT_DD; + pci_dma_write(dev, addr, desc, len); + + if (status & E1000_RXD_STAT_DD) { + d->wb.upper.status_error = status; + pci_dma_write(dev, addr + offset, &status, sizeof(status)); + } + } +} + +static void +igb_update_rx_stats(IGBCore *core, const E1000ERingInfo *rxi, + size_t pkt_size, size_t pkt_fcs_size) +{ + eth_pkt_types_e pkt_type = net_rx_pkt_get_packet_type(core->rx_pkt); + e1000x_update_rx_total_stats(core->mac, pkt_type, pkt_size, pkt_fcs_size); + + if (core->mac[MRQC] & 1) { + uint16_t pool = rxi->idx % IGB_NUM_VM_POOLS; + + core->mac[PVFGORC0 + (pool * 64)] += pkt_size + 4; + core->mac[PVFGPRC0 + (pool * 64)]++; + if (pkt_type == ETH_PKT_MCAST) { + core->mac[PVFMPRC0 + (pool * 64)]++; + } + } +} + +static inline bool +igb_rx_descr_threshold_hit(IGBCore *core, const E1000ERingInfo *rxi) +{ + return igb_ring_free_descr_num(core, rxi) == + ((core->mac[E1000_SRRCTL(rxi->idx) >> 2] >> 20) & 31) * 16; +} + +static bool +igb_do_ps(IGBCore *core, + const E1000ERingInfo *r, + struct NetRxPkt *pkt, + IGBPacketRxDMAState *pdma_st) +{ + bool hasip4, hasip6; + EthL4HdrProto l4hdr_proto; + bool fragment; + bool split_always; + size_t bheader_size; + size_t total_pkt_len; + + if (!igb_rx_use_ps_descriptor(core, r)) { + return false; + } + + total_pkt_len = net_rx_pkt_get_total_len(pkt); + bheader_size = igb_rxhdrbufsize(core, r); + split_always = igb_rx_ps_descriptor_split_always(core, r); + if (split_always && total_pkt_len <= bheader_size) { + pdma_st->ps_hdr_len = total_pkt_len; + pdma_st->ps_desc_data.hdr_len = total_pkt_len; + return true; + } + + net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto); + + if (hasip4) { + fragment = net_rx_pkt_get_ip4_info(pkt)->fragment; + } else if (hasip6) { + fragment = net_rx_pkt_get_ip6_info(pkt)->fragment; + } else { + pdma_st->ps_desc_data.hdr_len = bheader_size; + goto header_not_handled; + } + + if (fragment && (core->mac[RFCTL] & E1000_RFCTL_IPFRSP_DIS)) { + pdma_st->ps_desc_data.hdr_len = bheader_size; + goto header_not_handled; + } + + /* no header splitting for SCTP */ + if (!fragment && (l4hdr_proto == ETH_L4_HDR_PROTO_UDP || + l4hdr_proto == ETH_L4_HDR_PROTO_TCP)) { + pdma_st->ps_hdr_len = net_rx_pkt_get_l5_hdr_offset(pkt); + } else { + pdma_st->ps_hdr_len = net_rx_pkt_get_l4_hdr_offset(pkt); + } + + pdma_st->ps_desc_data.sph = true; + pdma_st->ps_desc_data.hdr_len = pdma_st->ps_hdr_len; + + if (pdma_st->ps_hdr_len > bheader_size) { + pdma_st->ps_desc_data.hbo = true; + goto header_not_handled; + } + + return true; + +header_not_handled: + if (split_always) { + pdma_st->ps_hdr_len = bheader_size; + return true; + } + + return false; +} + +static void +igb_truncate_to_descriptor_size(IGBPacketRxDMAState *pdma_st, size_t *size) +{ + if (pdma_st->do_ps && pdma_st->is_first) { + if (*size > pdma_st->rx_desc_packet_buf_size + pdma_st->ps_hdr_len) { + *size = pdma_st->rx_desc_packet_buf_size + pdma_st->ps_hdr_len; + } + } else { + if (*size > pdma_st->rx_desc_packet_buf_size) { + *size = pdma_st->rx_desc_packet_buf_size; + } + } +} + +static inline void +igb_write_hdr_frag_to_rx_buffers(IGBCore *core, + PCIDevice *d, + IGBPacketRxDMAState *pdma_st, + const char *data, + dma_addr_t data_len) +{ + assert(data_len <= pdma_st->rx_desc_header_buf_size - + pdma_st->bastate.written[0]); + pci_dma_write(d, + pdma_st->ba[0] + pdma_st->bastate.written[0], + data, data_len); + pdma_st->bastate.written[0] += data_len; + pdma_st->bastate.cur_idx = 1; +} + +static void +igb_write_header_to_rx_buffers(IGBCore *core, + struct NetRxPkt *pkt, + PCIDevice *d, + IGBPacketRxDMAState *pdma_st, + size_t *copy_size) +{ + size_t iov_copy; + size_t ps_hdr_copied = 0; + + if (!pdma_st->is_first) { + /* Leave buffer 0 of each descriptor except first */ + /* empty */ + pdma_st->bastate.cur_idx = 1; + return; + } + + do { + iov_copy = MIN(pdma_st->ps_hdr_len - ps_hdr_copied, + pdma_st->iov->iov_len - pdma_st->iov_ofs); + + igb_write_hdr_frag_to_rx_buffers(core, d, pdma_st, + pdma_st->iov->iov_base, + iov_copy); + + *copy_size -= iov_copy; + ps_hdr_copied += iov_copy; + + pdma_st->iov_ofs += iov_copy; + if (pdma_st->iov_ofs == pdma_st->iov->iov_len) { + pdma_st->iov++; + pdma_st->iov_ofs = 0; + } + } while (ps_hdr_copied < pdma_st->ps_hdr_len); + + pdma_st->is_first = false; +} + +static void +igb_write_payload_frag_to_rx_buffers(IGBCore *core, + PCIDevice *d, + IGBPacketRxDMAState *pdma_st, + const char *data, + dma_addr_t data_len) +{ + while (data_len > 0) { + assert(pdma_st->bastate.cur_idx < IGB_MAX_PS_BUFFERS); + + uint32_t cur_buf_bytes_left = + pdma_st->rx_desc_packet_buf_size - + pdma_st->bastate.written[pdma_st->bastate.cur_idx]; + uint32_t bytes_to_write = MIN(data_len, cur_buf_bytes_left); + + trace_igb_rx_desc_buff_write( + pdma_st->bastate.cur_idx, + pdma_st->ba[pdma_st->bastate.cur_idx], + pdma_st->bastate.written[pdma_st->bastate.cur_idx], + data, + bytes_to_write); + + pci_dma_write(d, + pdma_st->ba[pdma_st->bastate.cur_idx] + + pdma_st->bastate.written[pdma_st->bastate.cur_idx], + data, bytes_to_write); + + pdma_st->bastate.written[pdma_st->bastate.cur_idx] += bytes_to_write; + data += bytes_to_write; + data_len -= bytes_to_write; + + if (pdma_st->bastate.written[pdma_st->bastate.cur_idx] == + pdma_st->rx_desc_packet_buf_size) { + pdma_st->bastate.cur_idx++; + } + } +} + +static void +igb_write_payload_to_rx_buffers(IGBCore *core, + struct NetRxPkt *pkt, + PCIDevice *d, + IGBPacketRxDMAState *pdma_st, + size_t *copy_size) +{ + static const uint32_t fcs_pad; + size_t iov_copy; + + /* Copy packet payload */ + while (*copy_size) { + iov_copy = MIN(*copy_size, pdma_st->iov->iov_len - pdma_st->iov_ofs); + igb_write_payload_frag_to_rx_buffers(core, d, + pdma_st, + pdma_st->iov->iov_base + + pdma_st->iov_ofs, + iov_copy); + + *copy_size -= iov_copy; + pdma_st->iov_ofs += iov_copy; + if (pdma_st->iov_ofs == pdma_st->iov->iov_len) { + pdma_st->iov++; + pdma_st->iov_ofs = 0; + } + } + + if (pdma_st->desc_offset + pdma_st->desc_size >= pdma_st->total_size) { + /* Simulate FCS checksum presence in the last descriptor */ + igb_write_payload_frag_to_rx_buffers(core, d, + pdma_st, + (const char *) &fcs_pad, + e1000x_fcs_len(core->mac)); + } +} + +static void +igb_write_to_rx_buffers(IGBCore *core, + struct NetRxPkt *pkt, + PCIDevice *d, + IGBPacketRxDMAState *pdma_st) +{ + size_t copy_size; + + if (!(pdma_st->ba)[1] || (pdma_st->do_ps && !(pdma_st->ba[0]))) { + /* as per intel docs; skip descriptors with null buf addr */ + trace_e1000e_rx_null_descriptor(); + return; + } + + if (pdma_st->desc_offset >= pdma_st->size) { + return; + } + + pdma_st->desc_size = pdma_st->total_size - pdma_st->desc_offset; + igb_truncate_to_descriptor_size(pdma_st, &pdma_st->desc_size); + copy_size = pdma_st->size - pdma_st->desc_offset; + igb_truncate_to_descriptor_size(pdma_st, ©_size); + + /* For PS mode copy the packet header first */ + if (pdma_st->do_ps) { + igb_write_header_to_rx_buffers(core, pkt, d, pdma_st, ©_size); + } else { + pdma_st->bastate.cur_idx = 1; + } + + igb_write_payload_to_rx_buffers(core, pkt, d, pdma_st, ©_size); +} + +static void +igb_write_packet_to_guest(IGBCore *core, struct NetRxPkt *pkt, + const E1000E_RxRing *rxr, + const E1000E_RSSInfo *rss_info, + uint16_t etqf, bool ts) +{ + PCIDevice *d; + dma_addr_t base; + union e1000_rx_desc_union desc; + const E1000ERingInfo *rxi; + size_t rx_desc_len; + + IGBPacketRxDMAState pdma_st = {0}; + pdma_st.is_first = true; + pdma_st.size = net_rx_pkt_get_total_len(pkt); + pdma_st.total_size = pdma_st.size + e1000x_fcs_len(core->mac); + + rxi = rxr->i; + rx_desc_len = core->rx_desc_len; + pdma_st.rx_desc_packet_buf_size = igb_rxbufsize(core, rxi); + pdma_st.rx_desc_header_buf_size = igb_rxhdrbufsize(core, rxi); + pdma_st.iov = net_rx_pkt_get_iovec(pkt); + d = pcie_sriov_get_vf_at_index(core->owner, rxi->idx % 8); + if (!d) { + d = core->owner; + } + + pdma_st.do_ps = igb_do_ps(core, rxi, pkt, &pdma_st); + + do { + memset(&pdma_st.bastate, 0, sizeof(IGBBAState)); + bool is_last = false; + + if (igb_ring_empty(core, rxi)) { + return; + } + + base = igb_ring_head_descr(core, rxi); + pci_dma_read(d, base, &desc, rx_desc_len); + trace_e1000e_rx_descr(rxi->idx, base, rx_desc_len); + + igb_read_rx_descr(core, &desc, &pdma_st, rxi); + + igb_write_to_rx_buffers(core, pkt, d, &pdma_st); + pdma_st.desc_offset += pdma_st.desc_size; + if (pdma_st.desc_offset >= pdma_st.total_size) { + is_last = true; + } + + igb_write_rx_descr(core, &desc, + is_last ? pkt : NULL, + rss_info, + etqf, ts, + &pdma_st, + rxi); + igb_pci_dma_write_rx_desc(core, d, base, &desc, rx_desc_len); + igb_ring_advance(core, rxi, rx_desc_len / E1000_MIN_RX_DESC_LEN); + } while (pdma_st.desc_offset < pdma_st.total_size); + + igb_update_rx_stats(core, rxi, pdma_st.size, pdma_st.total_size); +} + +static bool +igb_rx_strip_vlan(IGBCore *core, const E1000ERingInfo *rxi) +{ + if (core->mac[MRQC] & 1) { + uint16_t pool = rxi->idx % IGB_NUM_VM_POOLS; + /* Sec 7.10.3.8: CTRL.VME is ignored, only VMOLR/RPLOLR is used */ + return (net_rx_pkt_get_packet_type(core->rx_pkt) == ETH_PKT_MCAST) ? + core->mac[RPLOLR] & E1000_RPLOLR_STRVLAN : + core->mac[VMOLR0 + pool] & E1000_VMOLR_STRVLAN; + } + + return e1000x_vlan_enabled(core->mac); +} + +static inline void +igb_rx_fix_l4_csum(IGBCore *core, struct NetRxPkt *pkt) +{ + struct virtio_net_hdr *vhdr = net_rx_pkt_get_vhdr(pkt); + + if (vhdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { + net_rx_pkt_fix_l4_csum(pkt); + } +} + +ssize_t +igb_receive_iov(IGBCore *core, const struct iovec *iov, int iovcnt) +{ + return igb_receive_internal(core, iov, iovcnt, core->has_vnet, NULL); +} + +static ssize_t +igb_receive_internal(IGBCore *core, const struct iovec *iov, int iovcnt, + bool has_vnet, bool *external_tx) +{ + uint16_t queues = 0; + uint32_t causes = 0; + uint32_t ecauses = 0; + union { + L2Header l2_header; + uint8_t octets[ETH_ZLEN]; + } buf; + struct iovec min_iov; + size_t size, orig_size; + size_t iov_ofs = 0; + E1000E_RxRing rxr; + E1000E_RSSInfo rss_info; + uint16_t etqf; + bool ts; + size_t total_size; + int strip_vlan_index; + int i; + + trace_e1000e_rx_receive_iov(iovcnt); + + if (external_tx) { + *external_tx = true; + } + + if (!e1000x_hw_rx_enabled(core->mac)) { + return -1; + } + + /* Pull virtio header in */ + if (has_vnet) { + net_rx_pkt_set_vhdr_iovec(core->rx_pkt, iov, iovcnt); + iov_ofs = sizeof(struct virtio_net_hdr); + } else { + net_rx_pkt_unset_vhdr(core->rx_pkt); + } + + orig_size = iov_size(iov, iovcnt); + size = orig_size - iov_ofs; + + /* Pad to minimum Ethernet frame length */ + if (size < sizeof(buf)) { + iov_to_buf(iov, iovcnt, iov_ofs, &buf, size); + memset(&buf.octets[size], 0, sizeof(buf) - size); + e1000x_inc_reg_if_not_full(core->mac, RUC); + min_iov.iov_base = &buf; + min_iov.iov_len = size = sizeof(buf); + iovcnt = 1; + iov = &min_iov; + iov_ofs = 0; + } else { + iov_to_buf(iov, iovcnt, iov_ofs, &buf, sizeof(buf.l2_header)); + } + + net_rx_pkt_set_packet_type(core->rx_pkt, + get_eth_packet_type(&buf.l2_header.eth)); + net_rx_pkt_set_protocols(core->rx_pkt, iov, iovcnt, iov_ofs); + + queues = igb_receive_assign(core, iov, iovcnt, iov_ofs, + &buf.l2_header, size, + &rss_info, &etqf, &ts, external_tx); + if (!queues) { + trace_e1000e_rx_flt_dropped(); + return orig_size; + } + + for (i = 0; i < IGB_NUM_QUEUES; i++) { + if (!(queues & BIT(i)) || + !(core->mac[RXDCTL0 + (i * 16)] & E1000_RXDCTL_QUEUE_ENABLE)) { + continue; + } + + igb_rx_ring_init(core, &rxr, i); + + if (!igb_rx_strip_vlan(core, rxr.i)) { + strip_vlan_index = -1; + } else if (core->mac[CTRL_EXT] & BIT(26)) { + strip_vlan_index = 1; + } else { + strip_vlan_index = 0; + } + + net_rx_pkt_attach_iovec_ex(core->rx_pkt, iov, iovcnt, iov_ofs, + strip_vlan_index, + core->mac[VET] & 0xffff, + core->mac[VET] >> 16); + + total_size = net_rx_pkt_get_total_len(core->rx_pkt) + + e1000x_fcs_len(core->mac); + + if (!igb_has_rxbufs(core, rxr.i, total_size)) { + causes |= E1000_ICS_RXO; + trace_e1000e_rx_not_written_to_guest(rxr.i->idx); + continue; + } + + causes |= E1000_ICR_RXDW; + + igb_rx_fix_l4_csum(core, core->rx_pkt); + igb_write_packet_to_guest(core, core->rx_pkt, &rxr, &rss_info, etqf, ts); + + /* Check if receive descriptor minimum threshold hit */ + if (igb_rx_descr_threshold_hit(core, rxr.i)) { + causes |= E1000_ICS_RXDMT0; + } + + ecauses |= igb_rx_wb_eic(core, rxr.i->idx); + + trace_e1000e_rx_written_to_guest(rxr.i->idx); + } + + trace_e1000e_rx_interrupt_set(causes); + igb_raise_interrupts(core, EICR, ecauses); + igb_raise_interrupts(core, ICR, causes); + + return orig_size; +} + +static inline bool +igb_have_autoneg(IGBCore *core) +{ + return core->phy[MII_BMCR] & MII_BMCR_AUTOEN; +} + +static void igb_update_flowctl_status(IGBCore *core) +{ + if (igb_have_autoneg(core) && core->phy[MII_BMSR] & MII_BMSR_AN_COMP) { + trace_e1000e_link_autoneg_flowctl(true); + core->mac[CTRL] |= E1000_CTRL_TFCE | E1000_CTRL_RFCE; + } else { + trace_e1000e_link_autoneg_flowctl(false); + } +} + +static inline void +igb_link_down(IGBCore *core) +{ + e1000x_update_regs_on_link_down(core->mac, core->phy); + igb_update_flowctl_status(core); +} + +static inline void +igb_set_phy_ctrl(IGBCore *core, uint16_t val) +{ + /* bits 0-5 reserved; MII_BMCR_[ANRESTART,RESET] are self clearing */ + core->phy[MII_BMCR] = val & ~(0x3f | MII_BMCR_RESET | MII_BMCR_ANRESTART); + + if ((val & MII_BMCR_ANRESTART) && igb_have_autoneg(core)) { + e1000x_restart_autoneg(core->mac, core->phy, core->autoneg_timer); + } +} + +void igb_core_set_link_status(IGBCore *core) +{ + NetClientState *nc = qemu_get_queue(core->owner_nic); + uint32_t old_status = core->mac[STATUS]; + + trace_e1000e_link_status_changed(nc->link_down ? false : true); + + if (nc->link_down) { + e1000x_update_regs_on_link_down(core->mac, core->phy); + } else { + if (igb_have_autoneg(core) && + !(core->phy[MII_BMSR] & MII_BMSR_AN_COMP)) { + e1000x_restart_autoneg(core->mac, core->phy, + core->autoneg_timer); + } else { + e1000x_update_regs_on_link_up(core->mac, core->phy); + igb_start_recv(core); + } + } + + if (core->mac[STATUS] != old_status) { + igb_raise_interrupts(core, ICR, E1000_ICR_LSC); + } +} + +static void +igb_set_ctrl(IGBCore *core, int index, uint32_t val) +{ + trace_e1000e_core_ctrl_write(index, val); + + /* RST is self clearing */ + core->mac[CTRL] = val & ~E1000_CTRL_RST; + core->mac[CTRL_DUP] = core->mac[CTRL]; + + trace_e1000e_link_set_params( + !!(val & E1000_CTRL_ASDE), + (val & E1000_CTRL_SPD_SEL) >> E1000_CTRL_SPD_SHIFT, + !!(val & E1000_CTRL_FRCSPD), + !!(val & E1000_CTRL_FRCDPX), + !!(val & E1000_CTRL_RFCE), + !!(val & E1000_CTRL_TFCE)); + + if (val & E1000_CTRL_RST) { + trace_e1000e_core_ctrl_sw_reset(); + igb_reset(core, true); + } + + if (val & E1000_CTRL_PHY_RST) { + trace_e1000e_core_ctrl_phy_reset(); + core->mac[STATUS] |= E1000_STATUS_PHYRA; + } +} + +static void +igb_set_rfctl(IGBCore *core, int index, uint32_t val) +{ + trace_e1000e_rx_set_rfctl(val); + + if (!(val & E1000_RFCTL_ISCSI_DIS)) { + trace_e1000e_wrn_iscsi_filtering_not_supported(); + } + + if (!(val & E1000_RFCTL_NFSW_DIS)) { + trace_e1000e_wrn_nfsw_filtering_not_supported(); + } + + if (!(val & E1000_RFCTL_NFSR_DIS)) { + trace_e1000e_wrn_nfsr_filtering_not_supported(); + } + + core->mac[RFCTL] = val; +} + +static void +igb_calc_rxdesclen(IGBCore *core) +{ + if (igb_rx_use_legacy_descriptor(core)) { + core->rx_desc_len = sizeof(struct e1000_rx_desc); + } else { + core->rx_desc_len = sizeof(union e1000_adv_rx_desc); + } + trace_e1000e_rx_desc_len(core->rx_desc_len); +} + +static void +igb_set_rx_control(IGBCore *core, int index, uint32_t val) +{ + core->mac[RCTL] = val; + trace_e1000e_rx_set_rctl(core->mac[RCTL]); + + if (val & E1000_RCTL_DTYP_MASK) { + qemu_log_mask(LOG_GUEST_ERROR, + "igb: RCTL.DTYP must be zero for compatibility"); + } + + if (val & E1000_RCTL_EN) { + igb_calc_rxdesclen(core); + igb_start_recv(core); + } +} + +static inline bool +igb_postpone_interrupt(IGBIntrDelayTimer *timer) +{ + if (timer->running) { + trace_e1000e_irq_postponed_by_xitr(timer->delay_reg << 2); + + return true; + } + + if (timer->core->mac[timer->delay_reg] != 0) { + igb_intrmgr_rearm_timer(timer); + } + + return false; +} + +static inline bool +igb_eitr_should_postpone(IGBCore *core, int idx) +{ + return igb_postpone_interrupt(&core->eitr[idx]); +} + +static void igb_send_msix(IGBCore *core, uint32_t causes) +{ + int vector; + + for (vector = 0; vector < IGB_INTR_NUM; ++vector) { + if ((causes & BIT(vector)) && !igb_eitr_should_postpone(core, vector)) { + + trace_e1000e_irq_msix_notify_vec(vector); + igb_msix_notify(core, vector); + } + } +} + +static inline void +igb_fix_icr_asserted(IGBCore *core) +{ + core->mac[ICR] &= ~E1000_ICR_ASSERTED; + if (core->mac[ICR]) { + core->mac[ICR] |= E1000_ICR_ASSERTED; + } + + trace_e1000e_irq_fix_icr_asserted(core->mac[ICR]); +} + +static void igb_raise_interrupts(IGBCore *core, size_t index, uint32_t causes) +{ + uint32_t old_causes = core->mac[ICR] & core->mac[IMS]; + uint32_t old_ecauses = core->mac[EICR] & core->mac[EIMS]; + uint32_t raised_causes; + uint32_t raised_ecauses; + uint32_t int_alloc; + + trace_e1000e_irq_set(index << 2, + core->mac[index], core->mac[index] | causes); + + core->mac[index] |= causes; + + if (core->mac[GPIE] & E1000_GPIE_MSIX_MODE) { + raised_causes = core->mac[ICR] & core->mac[IMS] & ~old_causes; + + if (raised_causes & E1000_ICR_DRSTA) { + int_alloc = core->mac[IVAR_MISC] & 0xff; + if (int_alloc & E1000_IVAR_VALID) { + core->mac[EICR] |= BIT(int_alloc & 0x1f); + } + } + /* Check if other bits (excluding the TCP Timer) are enabled. */ + if (raised_causes & ~E1000_ICR_DRSTA) { + int_alloc = (core->mac[IVAR_MISC] >> 8) & 0xff; + if (int_alloc & E1000_IVAR_VALID) { + core->mac[EICR] |= BIT(int_alloc & 0x1f); + } + } + + raised_ecauses = core->mac[EICR] & core->mac[EIMS] & ~old_ecauses; + if (!raised_ecauses) { + return; + } + + igb_send_msix(core, raised_ecauses); + } else { + igb_fix_icr_asserted(core); + + raised_causes = core->mac[ICR] & core->mac[IMS] & ~old_causes; + if (!raised_causes) { + return; + } + + core->mac[EICR] |= (raised_causes & E1000_ICR_DRSTA) | E1000_EICR_OTHER; + + if (msix_enabled(core->owner)) { + trace_e1000e_irq_msix_notify_vec(0); + msix_notify(core->owner, 0); + } else if (msi_enabled(core->owner)) { + trace_e1000e_irq_msi_notify(raised_causes); + msi_notify(core->owner, 0); + } else { + igb_raise_legacy_irq(core); + } + } +} + +static void igb_lower_interrupts(IGBCore *core, size_t index, uint32_t causes) +{ + trace_e1000e_irq_clear(index << 2, + core->mac[index], core->mac[index] & ~causes); + + core->mac[index] &= ~causes; + + trace_e1000e_irq_pending_interrupts(core->mac[ICR] & core->mac[IMS], + core->mac[ICR], core->mac[IMS]); + + if (!(core->mac[ICR] & core->mac[IMS]) && + !(core->mac[GPIE] & E1000_GPIE_MSIX_MODE)) { + core->mac[EICR] &= ~E1000_EICR_OTHER; + + if (!msix_enabled(core->owner) && !msi_enabled(core->owner)) { + igb_lower_legacy_irq(core); + } + } +} + +static void igb_set_eics(IGBCore *core, int index, uint32_t val) +{ + bool msix = !!(core->mac[GPIE] & E1000_GPIE_MSIX_MODE); + uint32_t mask = msix ? E1000_EICR_MSIX_MASK : E1000_EICR_LEGACY_MASK; + + trace_igb_irq_write_eics(val, msix); + igb_raise_interrupts(core, EICR, val & mask); +} + +static void igb_set_eims(IGBCore *core, int index, uint32_t val) +{ + bool msix = !!(core->mac[GPIE] & E1000_GPIE_MSIX_MODE); + uint32_t mask = msix ? E1000_EICR_MSIX_MASK : E1000_EICR_LEGACY_MASK; + + trace_igb_irq_write_eims(val, msix); + igb_raise_interrupts(core, EIMS, val & mask); +} + +static void mailbox_interrupt_to_vf(IGBCore *core, uint16_t vfn) +{ + uint32_t ent = core->mac[VTIVAR_MISC + vfn]; + uint32_t causes; + + if ((ent & E1000_IVAR_VALID)) { + causes = (ent & 0x3) << (22 - vfn * IGBVF_MSIX_VEC_NUM); + igb_raise_interrupts(core, EICR, causes); + } +} + +static void mailbox_interrupt_to_pf(IGBCore *core) +{ + igb_raise_interrupts(core, ICR, E1000_ICR_VMMB); +} + +static void igb_set_pfmailbox(IGBCore *core, int index, uint32_t val) +{ + uint16_t vfn = index - P2VMAILBOX0; + + trace_igb_set_pfmailbox(vfn, val); + + if (val & E1000_P2VMAILBOX_STS) { + core->mac[V2PMAILBOX0 + vfn] |= E1000_V2PMAILBOX_PFSTS; + mailbox_interrupt_to_vf(core, vfn); + } + + if (val & E1000_P2VMAILBOX_ACK) { + core->mac[V2PMAILBOX0 + vfn] |= E1000_V2PMAILBOX_PFACK; + mailbox_interrupt_to_vf(core, vfn); + } + + /* Buffer Taken by PF (can be set only if the VFU is cleared). */ + if (val & E1000_P2VMAILBOX_PFU) { + if (!(core->mac[index] & E1000_P2VMAILBOX_VFU)) { + core->mac[index] |= E1000_P2VMAILBOX_PFU; + core->mac[V2PMAILBOX0 + vfn] |= E1000_V2PMAILBOX_PFU; + } + } else { + core->mac[index] &= ~E1000_P2VMAILBOX_PFU; + core->mac[V2PMAILBOX0 + vfn] &= ~E1000_V2PMAILBOX_PFU; + } + + if (val & E1000_P2VMAILBOX_RVFU) { + core->mac[V2PMAILBOX0 + vfn] &= ~E1000_V2PMAILBOX_VFU; + core->mac[MBVFICR] &= ~((E1000_MBVFICR_VFACK_VF1 << vfn) | + (E1000_MBVFICR_VFREQ_VF1 << vfn)); + } +} + +static void igb_set_vfmailbox(IGBCore *core, int index, uint32_t val) +{ + uint16_t vfn = index - V2PMAILBOX0; + + trace_igb_set_vfmailbox(vfn, val); + + if (val & E1000_V2PMAILBOX_REQ) { + core->mac[MBVFICR] |= E1000_MBVFICR_VFREQ_VF1 << vfn; + mailbox_interrupt_to_pf(core); + } + + if (val & E1000_V2PMAILBOX_ACK) { + core->mac[MBVFICR] |= E1000_MBVFICR_VFACK_VF1 << vfn; + mailbox_interrupt_to_pf(core); + } + + /* Buffer Taken by VF (can be set only if the PFU is cleared). */ + if (val & E1000_V2PMAILBOX_VFU) { + if (!(core->mac[index] & E1000_V2PMAILBOX_PFU)) { + core->mac[index] |= E1000_V2PMAILBOX_VFU; + core->mac[P2VMAILBOX0 + vfn] |= E1000_P2VMAILBOX_VFU; + } + } else { + core->mac[index] &= ~E1000_V2PMAILBOX_VFU; + core->mac[P2VMAILBOX0 + vfn] &= ~E1000_P2VMAILBOX_VFU; + } +} + +void igb_core_vf_reset(IGBCore *core, uint16_t vfn) +{ + uint16_t qn0 = vfn; + uint16_t qn1 = vfn + IGB_NUM_VM_POOLS; + + trace_igb_core_vf_reset(vfn); + + /* disable Rx and Tx for the VF*/ + core->mac[RXDCTL0 + (qn0 * 16)] &= ~E1000_RXDCTL_QUEUE_ENABLE; + core->mac[RXDCTL0 + (qn1 * 16)] &= ~E1000_RXDCTL_QUEUE_ENABLE; + core->mac[TXDCTL0 + (qn0 * 16)] &= ~E1000_TXDCTL_QUEUE_ENABLE; + core->mac[TXDCTL0 + (qn1 * 16)] &= ~E1000_TXDCTL_QUEUE_ENABLE; + core->mac[VFRE] &= ~BIT(vfn); + core->mac[VFTE] &= ~BIT(vfn); + /* indicate VF reset to PF */ + core->mac[VFLRE] |= BIT(vfn); + /* VFLRE and mailbox use the same interrupt cause */ + mailbox_interrupt_to_pf(core); +} + +static void igb_w1c(IGBCore *core, int index, uint32_t val) +{ + core->mac[index] &= ~val; +} + +static void igb_set_eimc(IGBCore *core, int index, uint32_t val) +{ + bool msix = !!(core->mac[GPIE] & E1000_GPIE_MSIX_MODE); + uint32_t mask = msix ? E1000_EICR_MSIX_MASK : E1000_EICR_LEGACY_MASK; + + trace_igb_irq_write_eimc(val, msix); + + /* Interrupts are disabled via a write to EIMC and reflected in EIMS. */ + igb_lower_interrupts(core, EIMS, val & mask); +} + +static void igb_set_eiac(IGBCore *core, int index, uint32_t val) +{ + bool msix = !!(core->mac[GPIE] & E1000_GPIE_MSIX_MODE); + + if (msix) { + trace_igb_irq_write_eiac(val); + + /* + * TODO: When using IOV, the bits that correspond to MSI-X vectors + * that are assigned to a VF are read-only. + */ + core->mac[EIAC] |= (val & E1000_EICR_MSIX_MASK); + } +} + +static void igb_set_eiam(IGBCore *core, int index, uint32_t val) +{ + bool msix = !!(core->mac[GPIE] & E1000_GPIE_MSIX_MODE); + + /* + * TODO: When using IOV, the bits that correspond to MSI-X vectors that + * are assigned to a VF are read-only. + */ + core->mac[EIAM] |= + ~(val & (msix ? E1000_EICR_MSIX_MASK : E1000_EICR_LEGACY_MASK)); + + trace_igb_irq_write_eiam(val, msix); +} + +static void igb_set_eicr(IGBCore *core, int index, uint32_t val) +{ + bool msix = !!(core->mac[GPIE] & E1000_GPIE_MSIX_MODE); + + /* + * TODO: In IOV mode, only bit zero of this vector is available for the PF + * function. + */ + uint32_t mask = msix ? E1000_EICR_MSIX_MASK : E1000_EICR_LEGACY_MASK; + + trace_igb_irq_write_eicr(val, msix); + igb_lower_interrupts(core, EICR, val & mask); +} + +static void igb_set_vtctrl(IGBCore *core, int index, uint32_t val) +{ + uint16_t vfn; + + if (val & E1000_CTRL_RST) { + vfn = (index - PVTCTRL0) / 0x40; + igb_core_vf_reset(core, vfn); + } +} + +static void igb_set_vteics(IGBCore *core, int index, uint32_t val) +{ + uint16_t vfn = (index - PVTEICS0) / 0x40; + + core->mac[index] = val; + igb_set_eics(core, EICS, (val & 0x7) << (22 - vfn * IGBVF_MSIX_VEC_NUM)); +} + +static void igb_set_vteims(IGBCore *core, int index, uint32_t val) +{ + uint16_t vfn = (index - PVTEIMS0) / 0x40; + + core->mac[index] = val; + igb_set_eims(core, EIMS, (val & 0x7) << (22 - vfn * IGBVF_MSIX_VEC_NUM)); +} + +static void igb_set_vteimc(IGBCore *core, int index, uint32_t val) +{ + uint16_t vfn = (index - PVTEIMC0) / 0x40; + + core->mac[index] = val; + igb_set_eimc(core, EIMC, (val & 0x7) << (22 - vfn * IGBVF_MSIX_VEC_NUM)); +} + +static void igb_set_vteiac(IGBCore *core, int index, uint32_t val) +{ + uint16_t vfn = (index - PVTEIAC0) / 0x40; + + core->mac[index] = val; + igb_set_eiac(core, EIAC, (val & 0x7) << (22 - vfn * IGBVF_MSIX_VEC_NUM)); +} + +static void igb_set_vteiam(IGBCore *core, int index, uint32_t val) +{ + uint16_t vfn = (index - PVTEIAM0) / 0x40; + + core->mac[index] = val; + igb_set_eiam(core, EIAM, (val & 0x7) << (22 - vfn * IGBVF_MSIX_VEC_NUM)); +} + +static void igb_set_vteicr(IGBCore *core, int index, uint32_t val) +{ + uint16_t vfn = (index - PVTEICR0) / 0x40; + + core->mac[index] = val; + igb_set_eicr(core, EICR, (val & 0x7) << (22 - vfn * IGBVF_MSIX_VEC_NUM)); +} + +static void igb_set_vtivar(IGBCore *core, int index, uint32_t val) +{ + uint16_t vfn = (index - VTIVAR); + uint16_t qn = vfn; + uint8_t ent; + int n; + + core->mac[index] = val; + + /* Get assigned vector associated with queue Rx#0. */ + if ((val & E1000_IVAR_VALID)) { + n = igb_ivar_entry_rx(qn); + ent = E1000_IVAR_VALID | (24 - vfn * IGBVF_MSIX_VEC_NUM - (2 - (val & 0x7))); + core->mac[IVAR0 + n / 4] |= ent << 8 * (n % 4); + } + + /* Get assigned vector associated with queue Tx#0 */ + ent = val >> 8; + if ((ent & E1000_IVAR_VALID)) { + n = igb_ivar_entry_tx(qn); + ent = E1000_IVAR_VALID | (24 - vfn * IGBVF_MSIX_VEC_NUM - (2 - (ent & 0x7))); + core->mac[IVAR0 + n / 4] |= ent << 8 * (n % 4); + } + + /* + * Ignoring assigned vectors associated with queues Rx#1 and Tx#1 for now. + */ +} + +static inline void +igb_autoneg_timer(void *opaque) +{ + IGBCore *core = opaque; + if (!qemu_get_queue(core->owner_nic)->link_down) { + e1000x_update_regs_on_autoneg_done(core->mac, core->phy); + igb_start_recv(core); + + igb_update_flowctl_status(core); + /* signal link status change to the guest */ + igb_raise_interrupts(core, ICR, E1000_ICR_LSC); + } +} + +static inline uint16_t +igb_get_reg_index_with_offset(const uint16_t *mac_reg_access, hwaddr addr) +{ + uint16_t index = (addr & 0x1ffff) >> 2; + return index + (mac_reg_access[index] & 0xfffe); +} + +static const char igb_phy_regcap[MAX_PHY_REG_ADDRESS + 1] = { + [MII_BMCR] = PHY_RW, + [MII_BMSR] = PHY_R, + [MII_PHYID1] = PHY_R, + [MII_PHYID2] = PHY_R, + [MII_ANAR] = PHY_RW, + [MII_ANLPAR] = PHY_R, + [MII_ANER] = PHY_R, + [MII_ANNP] = PHY_RW, + [MII_ANLPRNP] = PHY_R, + [MII_CTRL1000] = PHY_RW, + [MII_STAT1000] = PHY_R, + [MII_EXTSTAT] = PHY_R, + + [IGP01E1000_PHY_PORT_CONFIG] = PHY_RW, + [IGP01E1000_PHY_PORT_STATUS] = PHY_R, + [IGP01E1000_PHY_PORT_CTRL] = PHY_RW, + [IGP01E1000_PHY_LINK_HEALTH] = PHY_R, + [IGP02E1000_PHY_POWER_MGMT] = PHY_RW, + [IGP01E1000_PHY_PAGE_SELECT] = PHY_W +}; + +static void +igb_phy_reg_write(IGBCore *core, uint32_t addr, uint16_t data) +{ + assert(addr <= MAX_PHY_REG_ADDRESS); + + if (addr == MII_BMCR) { + igb_set_phy_ctrl(core, data); + } else { + core->phy[addr] = data; + } +} + +static void +igb_set_mdic(IGBCore *core, int index, uint32_t val) +{ + uint32_t data = val & E1000_MDIC_DATA_MASK; + uint32_t addr = ((val & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT); + + if ((val & E1000_MDIC_PHY_MASK) >> E1000_MDIC_PHY_SHIFT != 1) { /* phy # */ + val = core->mac[MDIC] | E1000_MDIC_ERROR; + } else if (val & E1000_MDIC_OP_READ) { + if (!(igb_phy_regcap[addr] & PHY_R)) { + trace_igb_core_mdic_read_unhandled(addr); + val |= E1000_MDIC_ERROR; + } else { + val = (val ^ data) | core->phy[addr]; + trace_igb_core_mdic_read(addr, val); + } + } else if (val & E1000_MDIC_OP_WRITE) { + if (!(igb_phy_regcap[addr] & PHY_W)) { + trace_igb_core_mdic_write_unhandled(addr); + val |= E1000_MDIC_ERROR; + } else { + trace_igb_core_mdic_write(addr, data); + igb_phy_reg_write(core, addr, data); + } + } + core->mac[MDIC] = val | E1000_MDIC_READY; + + if (val & E1000_MDIC_INT_EN) { + igb_raise_interrupts(core, ICR, E1000_ICR_MDAC); + } +} + +static void +igb_set_rdt(IGBCore *core, int index, uint32_t val) +{ + core->mac[index] = val & 0xffff; + trace_e1000e_rx_set_rdt(igb_mq_queue_idx(RDT0, index), val); + igb_start_recv(core); +} + +static void +igb_set_status(IGBCore *core, int index, uint32_t val) +{ + if ((val & E1000_STATUS_PHYRA) == 0) { + core->mac[index] &= ~E1000_STATUS_PHYRA; + } +} + +static void +igb_set_ctrlext(IGBCore *core, int index, uint32_t val) +{ + trace_igb_link_set_ext_params(!!(val & E1000_CTRL_EXT_ASDCHK), + !!(val & E1000_CTRL_EXT_SPD_BYPS), + !!(val & E1000_CTRL_EXT_PFRSTD)); + + /* Zero self-clearing bits */ + val &= ~(E1000_CTRL_EXT_ASDCHK | E1000_CTRL_EXT_EE_RST); + core->mac[CTRL_EXT] = val; + + if (core->mac[CTRL_EXT] & E1000_CTRL_EXT_PFRSTD) { + for (int vfn = 0; vfn < IGB_MAX_VF_FUNCTIONS; vfn++) { + core->mac[V2PMAILBOX0 + vfn] &= ~E1000_V2PMAILBOX_RSTI; + core->mac[V2PMAILBOX0 + vfn] |= E1000_V2PMAILBOX_RSTD; + } + } +} + +static void +igb_set_pbaclr(IGBCore *core, int index, uint32_t val) +{ + int i; + + core->mac[PBACLR] = val & E1000_PBACLR_VALID_MASK; + + if (!msix_enabled(core->owner)) { + return; + } + + for (i = 0; i < IGB_INTR_NUM; i++) { + if (core->mac[PBACLR] & BIT(i)) { + msix_clr_pending(core->owner, i); + } + } +} + +static void +igb_set_fcrth(IGBCore *core, int index, uint32_t val) +{ + core->mac[FCRTH] = val & 0xFFF8; +} + +static void +igb_set_fcrtl(IGBCore *core, int index, uint32_t val) +{ + core->mac[FCRTL] = val & 0x8000FFF8; +} + +#define IGB_LOW_BITS_SET_FUNC(num) \ + static void \ + igb_set_##num##bit(IGBCore *core, int index, uint32_t val) \ + { \ + core->mac[index] = val & (BIT(num) - 1); \ + } + +IGB_LOW_BITS_SET_FUNC(4) +IGB_LOW_BITS_SET_FUNC(13) +IGB_LOW_BITS_SET_FUNC(16) + +static void +igb_set_dlen(IGBCore *core, int index, uint32_t val) +{ + core->mac[index] = val & 0xffff0; +} + +static void +igb_set_dbal(IGBCore *core, int index, uint32_t val) +{ + core->mac[index] = val & E1000_XDBAL_MASK; +} + +static void +igb_set_tdt(IGBCore *core, int index, uint32_t val) +{ + IGB_TxRing txr; + int qn = igb_mq_queue_idx(TDT0, index); + + core->mac[index] = val & 0xffff; + + igb_tx_ring_init(core, &txr, qn); + igb_start_xmit(core, &txr); +} + +static void +igb_set_ics(IGBCore *core, int index, uint32_t val) +{ + trace_e1000e_irq_write_ics(val); + igb_raise_interrupts(core, ICR, val); +} + +static void +igb_set_imc(IGBCore *core, int index, uint32_t val) +{ + trace_e1000e_irq_ims_clear_set_imc(val); + igb_lower_interrupts(core, IMS, val); +} + +static void +igb_set_ims(IGBCore *core, int index, uint32_t val) +{ + igb_raise_interrupts(core, IMS, val & 0x77D4FBFD); +} + +static void igb_nsicr(IGBCore *core) +{ + /* + * If GPIE.NSICR = 0, then the clear of IMS will occur only if at + * least one bit is set in the IMS and there is a true interrupt as + * reflected in ICR.INTA. + */ + if ((core->mac[GPIE] & E1000_GPIE_NSICR) || + (core->mac[IMS] && (core->mac[ICR] & E1000_ICR_INT_ASSERTED))) { + igb_lower_interrupts(core, IMS, core->mac[IAM]); + } +} + +static void igb_set_icr(IGBCore *core, int index, uint32_t val) +{ + igb_nsicr(core); + igb_lower_interrupts(core, ICR, val); +} + +static uint32_t +igb_mac_readreg(IGBCore *core, int index) +{ + return core->mac[index]; +} + +static uint32_t +igb_mac_ics_read(IGBCore *core, int index) +{ + trace_e1000e_irq_read_ics(core->mac[ICS]); + return core->mac[ICS]; +} + +static uint32_t +igb_mac_ims_read(IGBCore *core, int index) +{ + trace_e1000e_irq_read_ims(core->mac[IMS]); + return core->mac[IMS]; +} + +static uint32_t +igb_mac_swsm_read(IGBCore *core, int index) +{ + uint32_t val = core->mac[SWSM]; + core->mac[SWSM] = val | E1000_SWSM_SMBI; + return val; +} + +static uint32_t +igb_mac_eitr_read(IGBCore *core, int index) +{ + return core->eitr_guest_value[index - EITR0]; +} + +static uint32_t igb_mac_vfmailbox_read(IGBCore *core, int index) +{ + uint32_t val = core->mac[index]; + + core->mac[index] &= ~(E1000_V2PMAILBOX_PFSTS | E1000_V2PMAILBOX_PFACK | + E1000_V2PMAILBOX_RSTD); + + return val; +} + +static uint32_t +igb_mac_icr_read(IGBCore *core, int index) +{ + uint32_t ret = core->mac[ICR]; + + if (core->mac[GPIE] & E1000_GPIE_NSICR) { + trace_igb_irq_icr_clear_gpie_nsicr(); + igb_lower_interrupts(core, ICR, 0xffffffff); + } else if (core->mac[IMS] == 0) { + trace_e1000e_irq_icr_clear_zero_ims(); + igb_lower_interrupts(core, ICR, 0xffffffff); + } else if (core->mac[ICR] & E1000_ICR_INT_ASSERTED) { + igb_lower_interrupts(core, ICR, 0xffffffff); + } else if (!msix_enabled(core->owner)) { + trace_e1000e_irq_icr_clear_nonmsix_icr_read(); + igb_lower_interrupts(core, ICR, 0xffffffff); + } + + igb_nsicr(core); + return ret; +} + +static uint32_t +igb_mac_read_clr4(IGBCore *core, int index) +{ + uint32_t ret = core->mac[index]; + + core->mac[index] = 0; + return ret; +} + +static uint32_t +igb_mac_read_clr8(IGBCore *core, int index) +{ + uint32_t ret = core->mac[index]; + + core->mac[index] = 0; + core->mac[index - 1] = 0; + return ret; +} + +static uint32_t +igb_get_ctrl(IGBCore *core, int index) +{ + uint32_t val = core->mac[CTRL]; + + trace_e1000e_link_read_params( + !!(val & E1000_CTRL_ASDE), + (val & E1000_CTRL_SPD_SEL) >> E1000_CTRL_SPD_SHIFT, + !!(val & E1000_CTRL_FRCSPD), + !!(val & E1000_CTRL_FRCDPX), + !!(val & E1000_CTRL_RFCE), + !!(val & E1000_CTRL_TFCE)); + + return val; +} + +static uint32_t igb_get_status(IGBCore *core, int index) +{ + uint32_t res = core->mac[STATUS]; + uint16_t num_vfs = pcie_sriov_num_vfs(core->owner); + + if (core->mac[CTRL] & E1000_CTRL_FRCDPX) { + res |= (core->mac[CTRL] & E1000_CTRL_FD) ? E1000_STATUS_FD : 0; + } else { + res |= E1000_STATUS_FD; + } + + if ((core->mac[CTRL] & E1000_CTRL_FRCSPD) || + (core->mac[CTRL_EXT] & E1000_CTRL_EXT_SPD_BYPS)) { + switch (core->mac[CTRL] & E1000_CTRL_SPD_SEL) { + case E1000_CTRL_SPD_10: + res |= E1000_STATUS_SPEED_10; + break; + case E1000_CTRL_SPD_100: + res |= E1000_STATUS_SPEED_100; + break; + case E1000_CTRL_SPD_1000: + default: + res |= E1000_STATUS_SPEED_1000; + break; + } + } else { + res |= E1000_STATUS_SPEED_1000; + } + + if (num_vfs) { + res |= num_vfs << E1000_STATUS_NUM_VFS_SHIFT; + res |= E1000_STATUS_IOV_MODE; + } + + if (!(core->mac[CTRL] & E1000_CTRL_GIO_MASTER_DISABLE)) { + res |= E1000_STATUS_GIO_MASTER_ENABLE; + } + + return res; +} + +static void +igb_mac_writereg(IGBCore *core, int index, uint32_t val) +{ + core->mac[index] = val; +} + +static void +igb_mac_setmacaddr(IGBCore *core, int index, uint32_t val) +{ + uint32_t macaddr[2]; + + core->mac[index] = val; + + macaddr[0] = cpu_to_le32(core->mac[RA]); + macaddr[1] = cpu_to_le32(core->mac[RA + 1]); + qemu_format_nic_info_str(qemu_get_queue(core->owner_nic), + (uint8_t *) macaddr); + + trace_e1000e_mac_set_sw(MAC_ARG(macaddr)); +} + +static void +igb_set_eecd(IGBCore *core, int index, uint32_t val) +{ + static const uint32_t ro_bits = E1000_EECD_PRES | + E1000_EECD_AUTO_RD | + E1000_EECD_SIZE_EX_MASK; + + core->mac[EECD] = (core->mac[EECD] & ro_bits) | (val & ~ro_bits); +} + +static void +igb_set_eerd(IGBCore *core, int index, uint32_t val) +{ + uint32_t addr = (val >> E1000_EERW_ADDR_SHIFT) & E1000_EERW_ADDR_MASK; + uint32_t flags = 0; + uint32_t data = 0; + + if ((addr < IGB_EEPROM_SIZE) && (val & E1000_EERW_START)) { + data = core->eeprom[addr]; + flags = E1000_EERW_DONE; + } + + core->mac[EERD] = flags | + (addr << E1000_EERW_ADDR_SHIFT) | + (data << E1000_EERW_DATA_SHIFT); +} + +static void +igb_set_eitr(IGBCore *core, int index, uint32_t val) +{ + uint32_t eitr_num = index - EITR0; + + trace_igb_irq_eitr_set(eitr_num, val); + + core->eitr_guest_value[eitr_num] = val & ~E1000_EITR_CNT_IGNR; + core->mac[index] = val & 0x7FFE; +} + +static void +igb_update_rx_offloads(IGBCore *core) +{ + int cso_state = igb_rx_l4_cso_enabled(core); + + trace_e1000e_rx_set_cso(cso_state); + + if (core->has_vnet) { + qemu_set_offload(qemu_get_queue(core->owner_nic)->peer, + cso_state, 0, 0, 0, 0, 0, 0); + } +} + +static void +igb_set_rxcsum(IGBCore *core, int index, uint32_t val) +{ + core->mac[RXCSUM] = val; + igb_update_rx_offloads(core); +} + +static void +igb_set_gcr(IGBCore *core, int index, uint32_t val) +{ + uint32_t ro_bits = core->mac[GCR] & E1000_GCR_RO_BITS; + core->mac[GCR] = (val & ~E1000_GCR_RO_BITS) | ro_bits; +} + +static uint32_t igb_get_systiml(IGBCore *core, int index) +{ + e1000x_timestamp(core->mac, core->timadj, SYSTIML, SYSTIMH); + return core->mac[SYSTIML]; +} + +static uint32_t igb_get_rxsatrh(IGBCore *core, int index) +{ + core->mac[TSYNCRXCTL] &= ~E1000_TSYNCRXCTL_VALID; + return core->mac[RXSATRH]; +} + +static uint32_t igb_get_txstmph(IGBCore *core, int index) +{ + core->mac[TSYNCTXCTL] &= ~E1000_TSYNCTXCTL_VALID; + return core->mac[TXSTMPH]; +} + +static void igb_set_timinca(IGBCore *core, int index, uint32_t val) +{ + e1000x_set_timinca(core->mac, &core->timadj, val); +} + +static void igb_set_timadjh(IGBCore *core, int index, uint32_t val) +{ + core->mac[TIMADJH] = val; + core->timadj += core->mac[TIMADJL] | ((int64_t)core->mac[TIMADJH] << 32); +} + +#define igb_getreg(x) [x] = igb_mac_readreg +typedef uint32_t (*readops)(IGBCore *, int); +static const readops igb_macreg_readops[] = { + igb_getreg(WUFC), + igb_getreg(MANC), + igb_getreg(TOTL), + igb_getreg(RDT0), + igb_getreg(RDT1), + igb_getreg(RDT2), + igb_getreg(RDT3), + igb_getreg(RDT4), + igb_getreg(RDT5), + igb_getreg(RDT6), + igb_getreg(RDT7), + igb_getreg(RDT8), + igb_getreg(RDT9), + igb_getreg(RDT10), + igb_getreg(RDT11), + igb_getreg(RDT12), + igb_getreg(RDT13), + igb_getreg(RDT14), + igb_getreg(RDT15), + igb_getreg(RDBAH0), + igb_getreg(RDBAH1), + igb_getreg(RDBAH2), + igb_getreg(RDBAH3), + igb_getreg(RDBAH4), + igb_getreg(RDBAH5), + igb_getreg(RDBAH6), + igb_getreg(RDBAH7), + igb_getreg(RDBAH8), + igb_getreg(RDBAH9), + igb_getreg(RDBAH10), + igb_getreg(RDBAH11), + igb_getreg(RDBAH12), + igb_getreg(RDBAH13), + igb_getreg(RDBAH14), + igb_getreg(RDBAH15), + igb_getreg(TDBAL0), + igb_getreg(TDBAL1), + igb_getreg(TDBAL2), + igb_getreg(TDBAL3), + igb_getreg(TDBAL4), + igb_getreg(TDBAL5), + igb_getreg(TDBAL6), + igb_getreg(TDBAL7), + igb_getreg(TDBAL8), + igb_getreg(TDBAL9), + igb_getreg(TDBAL10), + igb_getreg(TDBAL11), + igb_getreg(TDBAL12), + igb_getreg(TDBAL13), + igb_getreg(TDBAL14), + igb_getreg(TDBAL15), + igb_getreg(RDLEN0), + igb_getreg(RDLEN1), + igb_getreg(RDLEN2), + igb_getreg(RDLEN3), + igb_getreg(RDLEN4), + igb_getreg(RDLEN5), + igb_getreg(RDLEN6), + igb_getreg(RDLEN7), + igb_getreg(RDLEN8), + igb_getreg(RDLEN9), + igb_getreg(RDLEN10), + igb_getreg(RDLEN11), + igb_getreg(RDLEN12), + igb_getreg(RDLEN13), + igb_getreg(RDLEN14), + igb_getreg(RDLEN15), + igb_getreg(SRRCTL0), + igb_getreg(SRRCTL1), + igb_getreg(SRRCTL2), + igb_getreg(SRRCTL3), + igb_getreg(SRRCTL4), + igb_getreg(SRRCTL5), + igb_getreg(SRRCTL6), + igb_getreg(SRRCTL7), + igb_getreg(SRRCTL8), + igb_getreg(SRRCTL9), + igb_getreg(SRRCTL10), + igb_getreg(SRRCTL11), + igb_getreg(SRRCTL12), + igb_getreg(SRRCTL13), + igb_getreg(SRRCTL14), + igb_getreg(SRRCTL15), + igb_getreg(LATECOL), + igb_getreg(XONTXC), + igb_getreg(TDFH), + igb_getreg(TDFT), + igb_getreg(TDFHS), + igb_getreg(TDFTS), + igb_getreg(TDFPC), + igb_getreg(WUS), + igb_getreg(RDFH), + igb_getreg(RDFT), + igb_getreg(RDFHS), + igb_getreg(RDFTS), + igb_getreg(RDFPC), + igb_getreg(GORCL), + igb_getreg(MGTPRC), + igb_getreg(EERD), + igb_getreg(EIAC), + igb_getreg(MANC2H), + igb_getreg(RXCSUM), + igb_getreg(GSCL_3), + igb_getreg(GSCN_2), + igb_getreg(FCAH), + igb_getreg(FCRTH), + igb_getreg(FLOP), + igb_getreg(RXSTMPH), + igb_getreg(TXSTMPL), + igb_getreg(TIMADJL), + igb_getreg(RDH0), + igb_getreg(RDH1), + igb_getreg(RDH2), + igb_getreg(RDH3), + igb_getreg(RDH4), + igb_getreg(RDH5), + igb_getreg(RDH6), + igb_getreg(RDH7), + igb_getreg(RDH8), + igb_getreg(RDH9), + igb_getreg(RDH10), + igb_getreg(RDH11), + igb_getreg(RDH12), + igb_getreg(RDH13), + igb_getreg(RDH14), + igb_getreg(RDH15), + igb_getreg(TDT0), + igb_getreg(TDT1), + igb_getreg(TDT2), + igb_getreg(TDT3), + igb_getreg(TDT4), + igb_getreg(TDT5), + igb_getreg(TDT6), + igb_getreg(TDT7), + igb_getreg(TDT8), + igb_getreg(TDT9), + igb_getreg(TDT10), + igb_getreg(TDT11), + igb_getreg(TDT12), + igb_getreg(TDT13), + igb_getreg(TDT14), + igb_getreg(TDT15), + igb_getreg(TNCRS), + igb_getreg(RJC), + igb_getreg(IAM), + igb_getreg(GSCL_2), + igb_getreg(TIPG), + igb_getreg(FLMNGCTL), + igb_getreg(FLMNGCNT), + igb_getreg(TSYNCTXCTL), + igb_getreg(EEMNGDATA), + igb_getreg(CTRL_EXT), + igb_getreg(SYSTIMH), + igb_getreg(EEMNGCTL), + igb_getreg(FLMNGDATA), + igb_getreg(TSYNCRXCTL), + igb_getreg(LEDCTL), + igb_getreg(TCTL), + igb_getreg(TCTL_EXT), + igb_getreg(DTXCTL), + igb_getreg(RXPBS), + igb_getreg(TDH0), + igb_getreg(TDH1), + igb_getreg(TDH2), + igb_getreg(TDH3), + igb_getreg(TDH4), + igb_getreg(TDH5), + igb_getreg(TDH6), + igb_getreg(TDH7), + igb_getreg(TDH8), + igb_getreg(TDH9), + igb_getreg(TDH10), + igb_getreg(TDH11), + igb_getreg(TDH12), + igb_getreg(TDH13), + igb_getreg(TDH14), + igb_getreg(TDH15), + igb_getreg(ECOL), + igb_getreg(DC), + igb_getreg(RLEC), + igb_getreg(XOFFTXC), + igb_getreg(RFC), + igb_getreg(RNBC), + igb_getreg(MGTPTC), + igb_getreg(TIMINCA), + igb_getreg(FACTPS), + igb_getreg(GSCL_1), + igb_getreg(GSCN_0), + igb_getreg(PBACLR), + igb_getreg(FCTTV), + igb_getreg(RXSATRL), + igb_getreg(TORL), + igb_getreg(TDLEN0), + igb_getreg(TDLEN1), + igb_getreg(TDLEN2), + igb_getreg(TDLEN3), + igb_getreg(TDLEN4), + igb_getreg(TDLEN5), + igb_getreg(TDLEN6), + igb_getreg(TDLEN7), + igb_getreg(TDLEN8), + igb_getreg(TDLEN9), + igb_getreg(TDLEN10), + igb_getreg(TDLEN11), + igb_getreg(TDLEN12), + igb_getreg(TDLEN13), + igb_getreg(TDLEN14), + igb_getreg(TDLEN15), + igb_getreg(MCC), + igb_getreg(WUC), + igb_getreg(EECD), + igb_getreg(FCRTV), + igb_getreg(TXDCTL0), + igb_getreg(TXDCTL1), + igb_getreg(TXDCTL2), + igb_getreg(TXDCTL3), + igb_getreg(TXDCTL4), + igb_getreg(TXDCTL5), + igb_getreg(TXDCTL6), + igb_getreg(TXDCTL7), + igb_getreg(TXDCTL8), + igb_getreg(TXDCTL9), + igb_getreg(TXDCTL10), + igb_getreg(TXDCTL11), + igb_getreg(TXDCTL12), + igb_getreg(TXDCTL13), + igb_getreg(TXDCTL14), + igb_getreg(TXDCTL15), + igb_getreg(TXCTL0), + igb_getreg(TXCTL1), + igb_getreg(TXCTL2), + igb_getreg(TXCTL3), + igb_getreg(TXCTL4), + igb_getreg(TXCTL5), + igb_getreg(TXCTL6), + igb_getreg(TXCTL7), + igb_getreg(TXCTL8), + igb_getreg(TXCTL9), + igb_getreg(TXCTL10), + igb_getreg(TXCTL11), + igb_getreg(TXCTL12), + igb_getreg(TXCTL13), + igb_getreg(TXCTL14), + igb_getreg(TXCTL15), + igb_getreg(TDWBAL0), + igb_getreg(TDWBAL1), + igb_getreg(TDWBAL2), + igb_getreg(TDWBAL3), + igb_getreg(TDWBAL4), + igb_getreg(TDWBAL5), + igb_getreg(TDWBAL6), + igb_getreg(TDWBAL7), + igb_getreg(TDWBAL8), + igb_getreg(TDWBAL9), + igb_getreg(TDWBAL10), + igb_getreg(TDWBAL11), + igb_getreg(TDWBAL12), + igb_getreg(TDWBAL13), + igb_getreg(TDWBAL14), + igb_getreg(TDWBAL15), + igb_getreg(TDWBAH0), + igb_getreg(TDWBAH1), + igb_getreg(TDWBAH2), + igb_getreg(TDWBAH3), + igb_getreg(TDWBAH4), + igb_getreg(TDWBAH5), + igb_getreg(TDWBAH6), + igb_getreg(TDWBAH7), + igb_getreg(TDWBAH8), + igb_getreg(TDWBAH9), + igb_getreg(TDWBAH10), + igb_getreg(TDWBAH11), + igb_getreg(TDWBAH12), + igb_getreg(TDWBAH13), + igb_getreg(TDWBAH14), + igb_getreg(TDWBAH15), + igb_getreg(PVTCTRL0), + igb_getreg(PVTCTRL1), + igb_getreg(PVTCTRL2), + igb_getreg(PVTCTRL3), + igb_getreg(PVTCTRL4), + igb_getreg(PVTCTRL5), + igb_getreg(PVTCTRL6), + igb_getreg(PVTCTRL7), + igb_getreg(PVTEIMS0), + igb_getreg(PVTEIMS1), + igb_getreg(PVTEIMS2), + igb_getreg(PVTEIMS3), + igb_getreg(PVTEIMS4), + igb_getreg(PVTEIMS5), + igb_getreg(PVTEIMS6), + igb_getreg(PVTEIMS7), + igb_getreg(PVTEIAC0), + igb_getreg(PVTEIAC1), + igb_getreg(PVTEIAC2), + igb_getreg(PVTEIAC3), + igb_getreg(PVTEIAC4), + igb_getreg(PVTEIAC5), + igb_getreg(PVTEIAC6), + igb_getreg(PVTEIAC7), + igb_getreg(PVTEIAM0), + igb_getreg(PVTEIAM1), + igb_getreg(PVTEIAM2), + igb_getreg(PVTEIAM3), + igb_getreg(PVTEIAM4), + igb_getreg(PVTEIAM5), + igb_getreg(PVTEIAM6), + igb_getreg(PVTEIAM7), + igb_getreg(PVFGPRC0), + igb_getreg(PVFGPRC1), + igb_getreg(PVFGPRC2), + igb_getreg(PVFGPRC3), + igb_getreg(PVFGPRC4), + igb_getreg(PVFGPRC5), + igb_getreg(PVFGPRC6), + igb_getreg(PVFGPRC7), + igb_getreg(PVFGPTC0), + igb_getreg(PVFGPTC1), + igb_getreg(PVFGPTC2), + igb_getreg(PVFGPTC3), + igb_getreg(PVFGPTC4), + igb_getreg(PVFGPTC5), + igb_getreg(PVFGPTC6), + igb_getreg(PVFGPTC7), + igb_getreg(PVFGORC0), + igb_getreg(PVFGORC1), + igb_getreg(PVFGORC2), + igb_getreg(PVFGORC3), + igb_getreg(PVFGORC4), + igb_getreg(PVFGORC5), + igb_getreg(PVFGORC6), + igb_getreg(PVFGORC7), + igb_getreg(PVFGOTC0), + igb_getreg(PVFGOTC1), + igb_getreg(PVFGOTC2), + igb_getreg(PVFGOTC3), + igb_getreg(PVFGOTC4), + igb_getreg(PVFGOTC5), + igb_getreg(PVFGOTC6), + igb_getreg(PVFGOTC7), + igb_getreg(PVFMPRC0), + igb_getreg(PVFMPRC1), + igb_getreg(PVFMPRC2), + igb_getreg(PVFMPRC3), + igb_getreg(PVFMPRC4), + igb_getreg(PVFMPRC5), + igb_getreg(PVFMPRC6), + igb_getreg(PVFMPRC7), + igb_getreg(PVFGPRLBC0), + igb_getreg(PVFGPRLBC1), + igb_getreg(PVFGPRLBC2), + igb_getreg(PVFGPRLBC3), + igb_getreg(PVFGPRLBC4), + igb_getreg(PVFGPRLBC5), + igb_getreg(PVFGPRLBC6), + igb_getreg(PVFGPRLBC7), + igb_getreg(PVFGPTLBC0), + igb_getreg(PVFGPTLBC1), + igb_getreg(PVFGPTLBC2), + igb_getreg(PVFGPTLBC3), + igb_getreg(PVFGPTLBC4), + igb_getreg(PVFGPTLBC5), + igb_getreg(PVFGPTLBC6), + igb_getreg(PVFGPTLBC7), + igb_getreg(PVFGORLBC0), + igb_getreg(PVFGORLBC1), + igb_getreg(PVFGORLBC2), + igb_getreg(PVFGORLBC3), + igb_getreg(PVFGORLBC4), + igb_getreg(PVFGORLBC5), + igb_getreg(PVFGORLBC6), + igb_getreg(PVFGORLBC7), + igb_getreg(PVFGOTLBC0), + igb_getreg(PVFGOTLBC1), + igb_getreg(PVFGOTLBC2), + igb_getreg(PVFGOTLBC3), + igb_getreg(PVFGOTLBC4), + igb_getreg(PVFGOTLBC5), + igb_getreg(PVFGOTLBC6), + igb_getreg(PVFGOTLBC7), + igb_getreg(RCTL), + igb_getreg(MDIC), + igb_getreg(FCRUC), + igb_getreg(VET), + igb_getreg(RDBAL0), + igb_getreg(RDBAL1), + igb_getreg(RDBAL2), + igb_getreg(RDBAL3), + igb_getreg(RDBAL4), + igb_getreg(RDBAL5), + igb_getreg(RDBAL6), + igb_getreg(RDBAL7), + igb_getreg(RDBAL8), + igb_getreg(RDBAL9), + igb_getreg(RDBAL10), + igb_getreg(RDBAL11), + igb_getreg(RDBAL12), + igb_getreg(RDBAL13), + igb_getreg(RDBAL14), + igb_getreg(RDBAL15), + igb_getreg(TDBAH0), + igb_getreg(TDBAH1), + igb_getreg(TDBAH2), + igb_getreg(TDBAH3), + igb_getreg(TDBAH4), + igb_getreg(TDBAH5), + igb_getreg(TDBAH6), + igb_getreg(TDBAH7), + igb_getreg(TDBAH8), + igb_getreg(TDBAH9), + igb_getreg(TDBAH10), + igb_getreg(TDBAH11), + igb_getreg(TDBAH12), + igb_getreg(TDBAH13), + igb_getreg(TDBAH14), + igb_getreg(TDBAH15), + igb_getreg(SCC), + igb_getreg(COLC), + igb_getreg(XOFFRXC), + igb_getreg(IPAV), + igb_getreg(GOTCL), + igb_getreg(MGTPDC), + igb_getreg(GCR), + igb_getreg(MFVAL), + igb_getreg(FUNCTAG), + igb_getreg(GSCL_4), + igb_getreg(GSCN_3), + igb_getreg(MRQC), + igb_getreg(FCT), + igb_getreg(FLA), + igb_getreg(RXDCTL0), + igb_getreg(RXDCTL1), + igb_getreg(RXDCTL2), + igb_getreg(RXDCTL3), + igb_getreg(RXDCTL4), + igb_getreg(RXDCTL5), + igb_getreg(RXDCTL6), + igb_getreg(RXDCTL7), + igb_getreg(RXDCTL8), + igb_getreg(RXDCTL9), + igb_getreg(RXDCTL10), + igb_getreg(RXDCTL11), + igb_getreg(RXDCTL12), + igb_getreg(RXDCTL13), + igb_getreg(RXDCTL14), + igb_getreg(RXDCTL15), + igb_getreg(RXSTMPL), + igb_getreg(TIMADJH), + igb_getreg(FCRTL), + igb_getreg(XONRXC), + igb_getreg(RFCTL), + igb_getreg(GSCN_1), + igb_getreg(FCAL), + igb_getreg(GPIE), + igb_getreg(TXPBS), + igb_getreg(RLPML), + + [TOTH] = igb_mac_read_clr8, + [GOTCH] = igb_mac_read_clr8, + [PRC64] = igb_mac_read_clr4, + [PRC255] = igb_mac_read_clr4, + [PRC1023] = igb_mac_read_clr4, + [PTC64] = igb_mac_read_clr4, + [PTC255] = igb_mac_read_clr4, + [PTC1023] = igb_mac_read_clr4, + [GPRC] = igb_mac_read_clr4, + [TPT] = igb_mac_read_clr4, + [RUC] = igb_mac_read_clr4, + [BPRC] = igb_mac_read_clr4, + [MPTC] = igb_mac_read_clr4, + [IAC] = igb_mac_read_clr4, + [ICR] = igb_mac_icr_read, + [STATUS] = igb_get_status, + [ICS] = igb_mac_ics_read, + /* + * 8.8.10: Reading the IMC register returns the value of the IMS register. + */ + [IMC] = igb_mac_ims_read, + [TORH] = igb_mac_read_clr8, + [GORCH] = igb_mac_read_clr8, + [PRC127] = igb_mac_read_clr4, + [PRC511] = igb_mac_read_clr4, + [PRC1522] = igb_mac_read_clr4, + [PTC127] = igb_mac_read_clr4, + [PTC511] = igb_mac_read_clr4, + [PTC1522] = igb_mac_read_clr4, + [GPTC] = igb_mac_read_clr4, + [TPR] = igb_mac_read_clr4, + [ROC] = igb_mac_read_clr4, + [MPRC] = igb_mac_read_clr4, + [BPTC] = igb_mac_read_clr4, + [TSCTC] = igb_mac_read_clr4, + [CTRL] = igb_get_ctrl, + [SWSM] = igb_mac_swsm_read, + [IMS] = igb_mac_ims_read, + [SYSTIML] = igb_get_systiml, + [RXSATRH] = igb_get_rxsatrh, + [TXSTMPH] = igb_get_txstmph, + + [CRCERRS ... MPC] = igb_mac_readreg, + [IP6AT ... IP6AT + 3] = igb_mac_readreg, + [IP4AT ... IP4AT + 6] = igb_mac_readreg, + [RA ... RA + 31] = igb_mac_readreg, + [RA2 ... RA2 + 31] = igb_mac_readreg, + [WUPM ... WUPM + 31] = igb_mac_readreg, + [MTA ... MTA + E1000_MC_TBL_SIZE - 1] = igb_mac_readreg, + [VFTA ... VFTA + E1000_VLAN_FILTER_TBL_SIZE - 1] = igb_mac_readreg, + [FFMT ... FFMT + 254] = igb_mac_readreg, + [MDEF ... MDEF + 7] = igb_mac_readreg, + [FTFT ... FTFT + 254] = igb_mac_readreg, + [RETA ... RETA + 31] = igb_mac_readreg, + [RSSRK ... RSSRK + 9] = igb_mac_readreg, + [MAVTV0 ... MAVTV3] = igb_mac_readreg, + [EITR0 ... EITR0 + IGB_INTR_NUM - 1] = igb_mac_eitr_read, + [PVTEICR0] = igb_mac_read_clr4, + [PVTEICR1] = igb_mac_read_clr4, + [PVTEICR2] = igb_mac_read_clr4, + [PVTEICR3] = igb_mac_read_clr4, + [PVTEICR4] = igb_mac_read_clr4, + [PVTEICR5] = igb_mac_read_clr4, + [PVTEICR6] = igb_mac_read_clr4, + [PVTEICR7] = igb_mac_read_clr4, + + /* IGB specific: */ + [FWSM] = igb_mac_readreg, + [SW_FW_SYNC] = igb_mac_readreg, + [HTCBDPC] = igb_mac_read_clr4, + [EICR] = igb_mac_read_clr4, + [EIMS] = igb_mac_readreg, + [EIAM] = igb_mac_readreg, + [IVAR0 ... IVAR0 + 7] = igb_mac_readreg, + igb_getreg(IVAR_MISC), + igb_getreg(TSYNCRXCFG), + [ETQF0 ... ETQF0 + 7] = igb_mac_readreg, + igb_getreg(VT_CTL), + [P2VMAILBOX0 ... P2VMAILBOX7] = igb_mac_readreg, + [V2PMAILBOX0 ... V2PMAILBOX7] = igb_mac_vfmailbox_read, + igb_getreg(MBVFICR), + [VMBMEM0 ... VMBMEM0 + 127] = igb_mac_readreg, + igb_getreg(MBVFIMR), + igb_getreg(VFLRE), + igb_getreg(VFRE), + igb_getreg(VFTE), + igb_getreg(QDE), + igb_getreg(DTXSWC), + igb_getreg(RPLOLR), + [VLVF0 ... VLVF0 + E1000_VLVF_ARRAY_SIZE - 1] = igb_mac_readreg, + [VMVIR0 ... VMVIR7] = igb_mac_readreg, + [VMOLR0 ... VMOLR7] = igb_mac_readreg, + [WVBR] = igb_mac_read_clr4, + [RQDPC0] = igb_mac_read_clr4, + [RQDPC1] = igb_mac_read_clr4, + [RQDPC2] = igb_mac_read_clr4, + [RQDPC3] = igb_mac_read_clr4, + [RQDPC4] = igb_mac_read_clr4, + [RQDPC5] = igb_mac_read_clr4, + [RQDPC6] = igb_mac_read_clr4, + [RQDPC7] = igb_mac_read_clr4, + [RQDPC8] = igb_mac_read_clr4, + [RQDPC9] = igb_mac_read_clr4, + [RQDPC10] = igb_mac_read_clr4, + [RQDPC11] = igb_mac_read_clr4, + [RQDPC12] = igb_mac_read_clr4, + [RQDPC13] = igb_mac_read_clr4, + [RQDPC14] = igb_mac_read_clr4, + [RQDPC15] = igb_mac_read_clr4, + [VTIVAR ... VTIVAR + 7] = igb_mac_readreg, + [VTIVAR_MISC ... VTIVAR_MISC + 7] = igb_mac_readreg, +}; +enum { IGB_NREADOPS = ARRAY_SIZE(igb_macreg_readops) }; + +#define igb_putreg(x) [x] = igb_mac_writereg +typedef void (*writeops)(IGBCore *, int, uint32_t); +static const writeops igb_macreg_writeops[] = { + igb_putreg(SWSM), + igb_putreg(WUFC), + igb_putreg(RDBAH0), + igb_putreg(RDBAH1), + igb_putreg(RDBAH2), + igb_putreg(RDBAH3), + igb_putreg(RDBAH4), + igb_putreg(RDBAH5), + igb_putreg(RDBAH6), + igb_putreg(RDBAH7), + igb_putreg(RDBAH8), + igb_putreg(RDBAH9), + igb_putreg(RDBAH10), + igb_putreg(RDBAH11), + igb_putreg(RDBAH12), + igb_putreg(RDBAH13), + igb_putreg(RDBAH14), + igb_putreg(RDBAH15), + igb_putreg(SRRCTL0), + igb_putreg(SRRCTL1), + igb_putreg(SRRCTL2), + igb_putreg(SRRCTL3), + igb_putreg(SRRCTL4), + igb_putreg(SRRCTL5), + igb_putreg(SRRCTL6), + igb_putreg(SRRCTL7), + igb_putreg(SRRCTL8), + igb_putreg(SRRCTL9), + igb_putreg(SRRCTL10), + igb_putreg(SRRCTL11), + igb_putreg(SRRCTL12), + igb_putreg(SRRCTL13), + igb_putreg(SRRCTL14), + igb_putreg(SRRCTL15), + igb_putreg(RXDCTL0), + igb_putreg(RXDCTL1), + igb_putreg(RXDCTL2), + igb_putreg(RXDCTL3), + igb_putreg(RXDCTL4), + igb_putreg(RXDCTL5), + igb_putreg(RXDCTL6), + igb_putreg(RXDCTL7), + igb_putreg(RXDCTL8), + igb_putreg(RXDCTL9), + igb_putreg(RXDCTL10), + igb_putreg(RXDCTL11), + igb_putreg(RXDCTL12), + igb_putreg(RXDCTL13), + igb_putreg(RXDCTL14), + igb_putreg(RXDCTL15), + igb_putreg(LEDCTL), + igb_putreg(TCTL), + igb_putreg(TCTL_EXT), + igb_putreg(DTXCTL), + igb_putreg(RXPBS), + igb_putreg(RQDPC0), + igb_putreg(FCAL), + igb_putreg(FCRUC), + igb_putreg(WUC), + igb_putreg(WUS), + igb_putreg(IPAV), + igb_putreg(TDBAH0), + igb_putreg(TDBAH1), + igb_putreg(TDBAH2), + igb_putreg(TDBAH3), + igb_putreg(TDBAH4), + igb_putreg(TDBAH5), + igb_putreg(TDBAH6), + igb_putreg(TDBAH7), + igb_putreg(TDBAH8), + igb_putreg(TDBAH9), + igb_putreg(TDBAH10), + igb_putreg(TDBAH11), + igb_putreg(TDBAH12), + igb_putreg(TDBAH13), + igb_putreg(TDBAH14), + igb_putreg(TDBAH15), + igb_putreg(IAM), + igb_putreg(MANC), + igb_putreg(MANC2H), + igb_putreg(MFVAL), + igb_putreg(FACTPS), + igb_putreg(FUNCTAG), + igb_putreg(GSCL_1), + igb_putreg(GSCL_2), + igb_putreg(GSCL_3), + igb_putreg(GSCL_4), + igb_putreg(GSCN_0), + igb_putreg(GSCN_1), + igb_putreg(GSCN_2), + igb_putreg(GSCN_3), + igb_putreg(MRQC), + igb_putreg(FLOP), + igb_putreg(FLA), + igb_putreg(TXDCTL0), + igb_putreg(TXDCTL1), + igb_putreg(TXDCTL2), + igb_putreg(TXDCTL3), + igb_putreg(TXDCTL4), + igb_putreg(TXDCTL5), + igb_putreg(TXDCTL6), + igb_putreg(TXDCTL7), + igb_putreg(TXDCTL8), + igb_putreg(TXDCTL9), + igb_putreg(TXDCTL10), + igb_putreg(TXDCTL11), + igb_putreg(TXDCTL12), + igb_putreg(TXDCTL13), + igb_putreg(TXDCTL14), + igb_putreg(TXDCTL15), + igb_putreg(TXCTL0), + igb_putreg(TXCTL1), + igb_putreg(TXCTL2), + igb_putreg(TXCTL3), + igb_putreg(TXCTL4), + igb_putreg(TXCTL5), + igb_putreg(TXCTL6), + igb_putreg(TXCTL7), + igb_putreg(TXCTL8), + igb_putreg(TXCTL9), + igb_putreg(TXCTL10), + igb_putreg(TXCTL11), + igb_putreg(TXCTL12), + igb_putreg(TXCTL13), + igb_putreg(TXCTL14), + igb_putreg(TXCTL15), + igb_putreg(TDWBAL0), + igb_putreg(TDWBAL1), + igb_putreg(TDWBAL2), + igb_putreg(TDWBAL3), + igb_putreg(TDWBAL4), + igb_putreg(TDWBAL5), + igb_putreg(TDWBAL6), + igb_putreg(TDWBAL7), + igb_putreg(TDWBAL8), + igb_putreg(TDWBAL9), + igb_putreg(TDWBAL10), + igb_putreg(TDWBAL11), + igb_putreg(TDWBAL12), + igb_putreg(TDWBAL13), + igb_putreg(TDWBAL14), + igb_putreg(TDWBAL15), + igb_putreg(TDWBAH0), + igb_putreg(TDWBAH1), + igb_putreg(TDWBAH2), + igb_putreg(TDWBAH3), + igb_putreg(TDWBAH4), + igb_putreg(TDWBAH5), + igb_putreg(TDWBAH6), + igb_putreg(TDWBAH7), + igb_putreg(TDWBAH8), + igb_putreg(TDWBAH9), + igb_putreg(TDWBAH10), + igb_putreg(TDWBAH11), + igb_putreg(TDWBAH12), + igb_putreg(TDWBAH13), + igb_putreg(TDWBAH14), + igb_putreg(TDWBAH15), + igb_putreg(TIPG), + igb_putreg(RXSTMPH), + igb_putreg(RXSTMPL), + igb_putreg(RXSATRL), + igb_putreg(RXSATRH), + igb_putreg(TXSTMPL), + igb_putreg(TXSTMPH), + igb_putreg(SYSTIML), + igb_putreg(SYSTIMH), + igb_putreg(TIMADJL), + igb_putreg(TSYNCRXCTL), + igb_putreg(TSYNCTXCTL), + igb_putreg(EEMNGCTL), + igb_putreg(GPIE), + igb_putreg(TXPBS), + igb_putreg(RLPML), + igb_putreg(VET), + + [TDH0] = igb_set_16bit, + [TDH1] = igb_set_16bit, + [TDH2] = igb_set_16bit, + [TDH3] = igb_set_16bit, + [TDH4] = igb_set_16bit, + [TDH5] = igb_set_16bit, + [TDH6] = igb_set_16bit, + [TDH7] = igb_set_16bit, + [TDH8] = igb_set_16bit, + [TDH9] = igb_set_16bit, + [TDH10] = igb_set_16bit, + [TDH11] = igb_set_16bit, + [TDH12] = igb_set_16bit, + [TDH13] = igb_set_16bit, + [TDH14] = igb_set_16bit, + [TDH15] = igb_set_16bit, + [TDT0] = igb_set_tdt, + [TDT1] = igb_set_tdt, + [TDT2] = igb_set_tdt, + [TDT3] = igb_set_tdt, + [TDT4] = igb_set_tdt, + [TDT5] = igb_set_tdt, + [TDT6] = igb_set_tdt, + [TDT7] = igb_set_tdt, + [TDT8] = igb_set_tdt, + [TDT9] = igb_set_tdt, + [TDT10] = igb_set_tdt, + [TDT11] = igb_set_tdt, + [TDT12] = igb_set_tdt, + [TDT13] = igb_set_tdt, + [TDT14] = igb_set_tdt, + [TDT15] = igb_set_tdt, + [MDIC] = igb_set_mdic, + [ICS] = igb_set_ics, + [RDH0] = igb_set_16bit, + [RDH1] = igb_set_16bit, + [RDH2] = igb_set_16bit, + [RDH3] = igb_set_16bit, + [RDH4] = igb_set_16bit, + [RDH5] = igb_set_16bit, + [RDH6] = igb_set_16bit, + [RDH7] = igb_set_16bit, + [RDH8] = igb_set_16bit, + [RDH9] = igb_set_16bit, + [RDH10] = igb_set_16bit, + [RDH11] = igb_set_16bit, + [RDH12] = igb_set_16bit, + [RDH13] = igb_set_16bit, + [RDH14] = igb_set_16bit, + [RDH15] = igb_set_16bit, + [RDT0] = igb_set_rdt, + [RDT1] = igb_set_rdt, + [RDT2] = igb_set_rdt, + [RDT3] = igb_set_rdt, + [RDT4] = igb_set_rdt, + [RDT5] = igb_set_rdt, + [RDT6] = igb_set_rdt, + [RDT7] = igb_set_rdt, + [RDT8] = igb_set_rdt, + [RDT9] = igb_set_rdt, + [RDT10] = igb_set_rdt, + [RDT11] = igb_set_rdt, + [RDT12] = igb_set_rdt, + [RDT13] = igb_set_rdt, + [RDT14] = igb_set_rdt, + [RDT15] = igb_set_rdt, + [IMC] = igb_set_imc, + [IMS] = igb_set_ims, + [ICR] = igb_set_icr, + [EECD] = igb_set_eecd, + [RCTL] = igb_set_rx_control, + [CTRL] = igb_set_ctrl, + [EERD] = igb_set_eerd, + [TDFH] = igb_set_13bit, + [TDFT] = igb_set_13bit, + [TDFHS] = igb_set_13bit, + [TDFTS] = igb_set_13bit, + [TDFPC] = igb_set_13bit, + [RDFH] = igb_set_13bit, + [RDFT] = igb_set_13bit, + [RDFHS] = igb_set_13bit, + [RDFTS] = igb_set_13bit, + [RDFPC] = igb_set_13bit, + [GCR] = igb_set_gcr, + [RXCSUM] = igb_set_rxcsum, + [TDLEN0] = igb_set_dlen, + [TDLEN1] = igb_set_dlen, + [TDLEN2] = igb_set_dlen, + [TDLEN3] = igb_set_dlen, + [TDLEN4] = igb_set_dlen, + [TDLEN5] = igb_set_dlen, + [TDLEN6] = igb_set_dlen, + [TDLEN7] = igb_set_dlen, + [TDLEN8] = igb_set_dlen, + [TDLEN9] = igb_set_dlen, + [TDLEN10] = igb_set_dlen, + [TDLEN11] = igb_set_dlen, + [TDLEN12] = igb_set_dlen, + [TDLEN13] = igb_set_dlen, + [TDLEN14] = igb_set_dlen, + [TDLEN15] = igb_set_dlen, + [RDLEN0] = igb_set_dlen, + [RDLEN1] = igb_set_dlen, + [RDLEN2] = igb_set_dlen, + [RDLEN3] = igb_set_dlen, + [RDLEN4] = igb_set_dlen, + [RDLEN5] = igb_set_dlen, + [RDLEN6] = igb_set_dlen, + [RDLEN7] = igb_set_dlen, + [RDLEN8] = igb_set_dlen, + [RDLEN9] = igb_set_dlen, + [RDLEN10] = igb_set_dlen, + [RDLEN11] = igb_set_dlen, + [RDLEN12] = igb_set_dlen, + [RDLEN13] = igb_set_dlen, + [RDLEN14] = igb_set_dlen, + [RDLEN15] = igb_set_dlen, + [TDBAL0] = igb_set_dbal, + [TDBAL1] = igb_set_dbal, + [TDBAL2] = igb_set_dbal, + [TDBAL3] = igb_set_dbal, + [TDBAL4] = igb_set_dbal, + [TDBAL5] = igb_set_dbal, + [TDBAL6] = igb_set_dbal, + [TDBAL7] = igb_set_dbal, + [TDBAL8] = igb_set_dbal, + [TDBAL9] = igb_set_dbal, + [TDBAL10] = igb_set_dbal, + [TDBAL11] = igb_set_dbal, + [TDBAL12] = igb_set_dbal, + [TDBAL13] = igb_set_dbal, + [TDBAL14] = igb_set_dbal, + [TDBAL15] = igb_set_dbal, + [RDBAL0] = igb_set_dbal, + [RDBAL1] = igb_set_dbal, + [RDBAL2] = igb_set_dbal, + [RDBAL3] = igb_set_dbal, + [RDBAL4] = igb_set_dbal, + [RDBAL5] = igb_set_dbal, + [RDBAL6] = igb_set_dbal, + [RDBAL7] = igb_set_dbal, + [RDBAL8] = igb_set_dbal, + [RDBAL9] = igb_set_dbal, + [RDBAL10] = igb_set_dbal, + [RDBAL11] = igb_set_dbal, + [RDBAL12] = igb_set_dbal, + [RDBAL13] = igb_set_dbal, + [RDBAL14] = igb_set_dbal, + [RDBAL15] = igb_set_dbal, + [STATUS] = igb_set_status, + [PBACLR] = igb_set_pbaclr, + [CTRL_EXT] = igb_set_ctrlext, + [FCAH] = igb_set_16bit, + [FCT] = igb_set_16bit, + [FCTTV] = igb_set_16bit, + [FCRTV] = igb_set_16bit, + [FCRTH] = igb_set_fcrth, + [FCRTL] = igb_set_fcrtl, + [CTRL_DUP] = igb_set_ctrl, + [RFCTL] = igb_set_rfctl, + [TIMINCA] = igb_set_timinca, + [TIMADJH] = igb_set_timadjh, + + [IP6AT ... IP6AT + 3] = igb_mac_writereg, + [IP4AT ... IP4AT + 6] = igb_mac_writereg, + [RA] = igb_mac_writereg, + [RA + 1] = igb_mac_setmacaddr, + [RA + 2 ... RA + 31] = igb_mac_writereg, + [RA2 ... RA2 + 31] = igb_mac_writereg, + [WUPM ... WUPM + 31] = igb_mac_writereg, + [MTA ... MTA + E1000_MC_TBL_SIZE - 1] = igb_mac_writereg, + [VFTA ... VFTA + E1000_VLAN_FILTER_TBL_SIZE - 1] = igb_mac_writereg, + [FFMT ... FFMT + 254] = igb_set_4bit, + [MDEF ... MDEF + 7] = igb_mac_writereg, + [FTFT ... FTFT + 254] = igb_mac_writereg, + [RETA ... RETA + 31] = igb_mac_writereg, + [RSSRK ... RSSRK + 9] = igb_mac_writereg, + [MAVTV0 ... MAVTV3] = igb_mac_writereg, + [EITR0 ... EITR0 + IGB_INTR_NUM - 1] = igb_set_eitr, + + /* IGB specific: */ + [FWSM] = igb_mac_writereg, + [SW_FW_SYNC] = igb_mac_writereg, + [EICR] = igb_set_eicr, + [EICS] = igb_set_eics, + [EIAC] = igb_set_eiac, + [EIAM] = igb_set_eiam, + [EIMC] = igb_set_eimc, + [EIMS] = igb_set_eims, + [IVAR0 ... IVAR0 + 7] = igb_mac_writereg, + igb_putreg(IVAR_MISC), + igb_putreg(TSYNCRXCFG), + [ETQF0 ... ETQF0 + 7] = igb_mac_writereg, + igb_putreg(VT_CTL), + [P2VMAILBOX0 ... P2VMAILBOX7] = igb_set_pfmailbox, + [V2PMAILBOX0 ... V2PMAILBOX7] = igb_set_vfmailbox, + [MBVFICR] = igb_w1c, + [VMBMEM0 ... VMBMEM0 + 127] = igb_mac_writereg, + igb_putreg(MBVFIMR), + [VFLRE] = igb_w1c, + igb_putreg(VFRE), + igb_putreg(VFTE), + igb_putreg(QDE), + igb_putreg(DTXSWC), + igb_putreg(RPLOLR), + [VLVF0 ... VLVF0 + E1000_VLVF_ARRAY_SIZE - 1] = igb_mac_writereg, + [VMVIR0 ... VMVIR7] = igb_mac_writereg, + [VMOLR0 ... VMOLR7] = igb_mac_writereg, + [UTA ... UTA + E1000_MC_TBL_SIZE - 1] = igb_mac_writereg, + [PVTCTRL0] = igb_set_vtctrl, + [PVTCTRL1] = igb_set_vtctrl, + [PVTCTRL2] = igb_set_vtctrl, + [PVTCTRL3] = igb_set_vtctrl, + [PVTCTRL4] = igb_set_vtctrl, + [PVTCTRL5] = igb_set_vtctrl, + [PVTCTRL6] = igb_set_vtctrl, + [PVTCTRL7] = igb_set_vtctrl, + [PVTEICS0] = igb_set_vteics, + [PVTEICS1] = igb_set_vteics, + [PVTEICS2] = igb_set_vteics, + [PVTEICS3] = igb_set_vteics, + [PVTEICS4] = igb_set_vteics, + [PVTEICS5] = igb_set_vteics, + [PVTEICS6] = igb_set_vteics, + [PVTEICS7] = igb_set_vteics, + [PVTEIMS0] = igb_set_vteims, + [PVTEIMS1] = igb_set_vteims, + [PVTEIMS2] = igb_set_vteims, + [PVTEIMS3] = igb_set_vteims, + [PVTEIMS4] = igb_set_vteims, + [PVTEIMS5] = igb_set_vteims, + [PVTEIMS6] = igb_set_vteims, + [PVTEIMS7] = igb_set_vteims, + [PVTEIMC0] = igb_set_vteimc, + [PVTEIMC1] = igb_set_vteimc, + [PVTEIMC2] = igb_set_vteimc, + [PVTEIMC3] = igb_set_vteimc, + [PVTEIMC4] = igb_set_vteimc, + [PVTEIMC5] = igb_set_vteimc, + [PVTEIMC6] = igb_set_vteimc, + [PVTEIMC7] = igb_set_vteimc, + [PVTEIAC0] = igb_set_vteiac, + [PVTEIAC1] = igb_set_vteiac, + [PVTEIAC2] = igb_set_vteiac, + [PVTEIAC3] = igb_set_vteiac, + [PVTEIAC4] = igb_set_vteiac, + [PVTEIAC5] = igb_set_vteiac, + [PVTEIAC6] = igb_set_vteiac, + [PVTEIAC7] = igb_set_vteiac, + [PVTEIAM0] = igb_set_vteiam, + [PVTEIAM1] = igb_set_vteiam, + [PVTEIAM2] = igb_set_vteiam, + [PVTEIAM3] = igb_set_vteiam, + [PVTEIAM4] = igb_set_vteiam, + [PVTEIAM5] = igb_set_vteiam, + [PVTEIAM6] = igb_set_vteiam, + [PVTEIAM7] = igb_set_vteiam, + [PVTEICR0] = igb_set_vteicr, + [PVTEICR1] = igb_set_vteicr, + [PVTEICR2] = igb_set_vteicr, + [PVTEICR3] = igb_set_vteicr, + [PVTEICR4] = igb_set_vteicr, + [PVTEICR5] = igb_set_vteicr, + [PVTEICR6] = igb_set_vteicr, + [PVTEICR7] = igb_set_vteicr, + [VTIVAR ... VTIVAR + 7] = igb_set_vtivar, + [VTIVAR_MISC ... VTIVAR_MISC + 7] = igb_mac_writereg +}; +enum { IGB_NWRITEOPS = ARRAY_SIZE(igb_macreg_writeops) }; + +enum { MAC_ACCESS_PARTIAL = 1 }; + +/* + * The array below combines alias offsets of the index values for the + * MAC registers that have aliases, with the indication of not fully + * implemented registers (lowest bit). This combination is possible + * because all of the offsets are even. + */ +static const uint16_t mac_reg_access[E1000E_MAC_SIZE] = { + /* Alias index offsets */ + [FCRTL_A] = 0x07fe, + [RDFH_A] = 0xe904, [RDFT_A] = 0xe904, + [TDFH_A] = 0xed00, [TDFT_A] = 0xed00, + [RA_A ... RA_A + 31] = 0x14f0, + [VFTA_A ... VFTA_A + E1000_VLAN_FILTER_TBL_SIZE - 1] = 0x1400, + + [RDBAL0_A] = 0x2600, + [RDBAH0_A] = 0x2600, + [RDLEN0_A] = 0x2600, + [SRRCTL0_A] = 0x2600, + [RDH0_A] = 0x2600, + [RDT0_A] = 0x2600, + [RXDCTL0_A] = 0x2600, + [RXCTL0_A] = 0x2600, + [RQDPC0_A] = 0x2600, + [RDBAL1_A] = 0x25D0, + [RDBAL2_A] = 0x25A0, + [RDBAL3_A] = 0x2570, + [RDBAH1_A] = 0x25D0, + [RDBAH2_A] = 0x25A0, + [RDBAH3_A] = 0x2570, + [RDLEN1_A] = 0x25D0, + [RDLEN2_A] = 0x25A0, + [RDLEN3_A] = 0x2570, + [SRRCTL1_A] = 0x25D0, + [SRRCTL2_A] = 0x25A0, + [SRRCTL3_A] = 0x2570, + [RDH1_A] = 0x25D0, + [RDH2_A] = 0x25A0, + [RDH3_A] = 0x2570, + [RDT1_A] = 0x25D0, + [RDT2_A] = 0x25A0, + [RDT3_A] = 0x2570, + [RXDCTL1_A] = 0x25D0, + [RXDCTL2_A] = 0x25A0, + [RXDCTL3_A] = 0x2570, + [RXCTL1_A] = 0x25D0, + [RXCTL2_A] = 0x25A0, + [RXCTL3_A] = 0x2570, + [RQDPC1_A] = 0x25D0, + [RQDPC2_A] = 0x25A0, + [RQDPC3_A] = 0x2570, + [TDBAL0_A] = 0x2A00, + [TDBAH0_A] = 0x2A00, + [TDLEN0_A] = 0x2A00, + [TDH0_A] = 0x2A00, + [TDT0_A] = 0x2A00, + [TXCTL0_A] = 0x2A00, + [TDWBAL0_A] = 0x2A00, + [TDWBAH0_A] = 0x2A00, + [TDBAL1_A] = 0x29D0, + [TDBAL2_A] = 0x29A0, + [TDBAL3_A] = 0x2970, + [TDBAH1_A] = 0x29D0, + [TDBAH2_A] = 0x29A0, + [TDBAH3_A] = 0x2970, + [TDLEN1_A] = 0x29D0, + [TDLEN2_A] = 0x29A0, + [TDLEN3_A] = 0x2970, + [TDH1_A] = 0x29D0, + [TDH2_A] = 0x29A0, + [TDH3_A] = 0x2970, + [TDT1_A] = 0x29D0, + [TDT2_A] = 0x29A0, + [TDT3_A] = 0x2970, + [TXDCTL0_A] = 0x2A00, + [TXDCTL1_A] = 0x29D0, + [TXDCTL2_A] = 0x29A0, + [TXDCTL3_A] = 0x2970, + [TXCTL1_A] = 0x29D0, + [TXCTL2_A] = 0x29A0, + [TXCTL3_A] = 0x29D0, + [TDWBAL1_A] = 0x29D0, + [TDWBAL2_A] = 0x29A0, + [TDWBAL3_A] = 0x2970, + [TDWBAH1_A] = 0x29D0, + [TDWBAH2_A] = 0x29A0, + [TDWBAH3_A] = 0x2970, + + /* Access options */ + [RDFH] = MAC_ACCESS_PARTIAL, [RDFT] = MAC_ACCESS_PARTIAL, + [RDFHS] = MAC_ACCESS_PARTIAL, [RDFTS] = MAC_ACCESS_PARTIAL, + [RDFPC] = MAC_ACCESS_PARTIAL, + [TDFH] = MAC_ACCESS_PARTIAL, [TDFT] = MAC_ACCESS_PARTIAL, + [TDFHS] = MAC_ACCESS_PARTIAL, [TDFTS] = MAC_ACCESS_PARTIAL, + [TDFPC] = MAC_ACCESS_PARTIAL, [EECD] = MAC_ACCESS_PARTIAL, + [FLA] = MAC_ACCESS_PARTIAL, + [FCAL] = MAC_ACCESS_PARTIAL, [FCAH] = MAC_ACCESS_PARTIAL, + [FCT] = MAC_ACCESS_PARTIAL, [FCTTV] = MAC_ACCESS_PARTIAL, + [FCRTV] = MAC_ACCESS_PARTIAL, [FCRTL] = MAC_ACCESS_PARTIAL, + [FCRTH] = MAC_ACCESS_PARTIAL, + [MAVTV0 ... MAVTV3] = MAC_ACCESS_PARTIAL +}; + +void +igb_core_write(IGBCore *core, hwaddr addr, uint64_t val, unsigned size) +{ + uint16_t index = igb_get_reg_index_with_offset(mac_reg_access, addr); + + if (index < IGB_NWRITEOPS && igb_macreg_writeops[index]) { + if (mac_reg_access[index] & MAC_ACCESS_PARTIAL) { + trace_e1000e_wrn_regs_write_trivial(index << 2); + } + trace_e1000e_core_write(index << 2, size, val); + igb_macreg_writeops[index](core, index, val); + } else if (index < IGB_NREADOPS && igb_macreg_readops[index]) { + trace_e1000e_wrn_regs_write_ro(index << 2, size, val); + } else { + trace_e1000e_wrn_regs_write_unknown(index << 2, size, val); + } +} + +uint64_t +igb_core_read(IGBCore *core, hwaddr addr, unsigned size) +{ + uint64_t val; + uint16_t index = igb_get_reg_index_with_offset(mac_reg_access, addr); + + if (index < IGB_NREADOPS && igb_macreg_readops[index]) { + if (mac_reg_access[index] & MAC_ACCESS_PARTIAL) { + trace_e1000e_wrn_regs_read_trivial(index << 2); + } + val = igb_macreg_readops[index](core, index); + trace_e1000e_core_read(index << 2, size, val); + return val; + } else { + trace_e1000e_wrn_regs_read_unknown(index << 2, size); + } + return 0; +} + +static void +igb_autoneg_resume(IGBCore *core) +{ + if (igb_have_autoneg(core) && + !(core->phy[MII_BMSR] & MII_BMSR_AN_COMP)) { + qemu_get_queue(core->owner_nic)->link_down = false; + timer_mod(core->autoneg_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); + } +} + +void +igb_core_pci_realize(IGBCore *core, + const uint16_t *eeprom_templ, + uint32_t eeprom_size, + const uint8_t *macaddr) +{ + int i; + + core->autoneg_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, + igb_autoneg_timer, core); + igb_intrmgr_pci_realize(core); + + for (i = 0; i < IGB_NUM_QUEUES; i++) { + net_tx_pkt_init(&core->tx[i].tx_pkt, E1000E_MAX_TX_FRAGS); + } + + net_rx_pkt_init(&core->rx_pkt); + + e1000x_core_prepare_eeprom(core->eeprom, + eeprom_templ, + eeprom_size, + PCI_DEVICE_GET_CLASS(core->owner)->device_id, + macaddr); + igb_update_rx_offloads(core); +} + +void +igb_core_pci_uninit(IGBCore *core) +{ + int i; + + timer_free(core->autoneg_timer); + + igb_intrmgr_pci_unint(core); + + for (i = 0; i < IGB_NUM_QUEUES; i++) { + net_tx_pkt_uninit(core->tx[i].tx_pkt); + } + + net_rx_pkt_uninit(core->rx_pkt); +} + +static const uint16_t +igb_phy_reg_init[] = { + [MII_BMCR] = MII_BMCR_SPEED1000 | + MII_BMCR_FD | + MII_BMCR_AUTOEN, + + [MII_BMSR] = MII_BMSR_EXTCAP | + MII_BMSR_LINK_ST | + MII_BMSR_AUTONEG | + MII_BMSR_MFPS | + MII_BMSR_EXTSTAT | + MII_BMSR_10T_HD | + MII_BMSR_10T_FD | + MII_BMSR_100TX_HD | + MII_BMSR_100TX_FD, + + [MII_PHYID1] = IGP03E1000_E_PHY_ID >> 16, + [MII_PHYID2] = (IGP03E1000_E_PHY_ID & 0xfff0) | 1, + [MII_ANAR] = MII_ANAR_CSMACD | MII_ANAR_10 | + MII_ANAR_10FD | MII_ANAR_TX | + MII_ANAR_TXFD | MII_ANAR_PAUSE | + MII_ANAR_PAUSE_ASYM, + [MII_ANLPAR] = MII_ANLPAR_10 | MII_ANLPAR_10FD | + MII_ANLPAR_TX | MII_ANLPAR_TXFD | + MII_ANLPAR_T4 | MII_ANLPAR_PAUSE, + [MII_ANER] = MII_ANER_NP | MII_ANER_NWAY, + [MII_ANNP] = 0x1 | MII_ANNP_MP, + [MII_CTRL1000] = MII_CTRL1000_HALF | MII_CTRL1000_FULL | + MII_CTRL1000_PORT | MII_CTRL1000_MASTER, + [MII_STAT1000] = MII_STAT1000_HALF | MII_STAT1000_FULL | + MII_STAT1000_ROK | MII_STAT1000_LOK, + [MII_EXTSTAT] = MII_EXTSTAT_1000T_HD | MII_EXTSTAT_1000T_FD, + + [IGP01E1000_PHY_PORT_CONFIG] = BIT(5) | BIT(8), + [IGP01E1000_PHY_PORT_STATUS] = IGP01E1000_PSSR_SPEED_1000MBPS, + [IGP02E1000_PHY_POWER_MGMT] = BIT(0) | BIT(3) | IGP02E1000_PM_D3_LPLU | + IGP01E1000_PSCFR_SMART_SPEED +}; + +static const uint32_t igb_mac_reg_init[] = { + [LEDCTL] = 2 | (3 << 8) | BIT(15) | (6 << 16) | (7 << 24), + [EEMNGCTL] = BIT(31), + [TXDCTL0] = E1000_TXDCTL_QUEUE_ENABLE, + [RXDCTL0] = E1000_RXDCTL_QUEUE_ENABLE | (1 << 16), + [RXDCTL1] = 1 << 16, + [RXDCTL2] = 1 << 16, + [RXDCTL3] = 1 << 16, + [RXDCTL4] = 1 << 16, + [RXDCTL5] = 1 << 16, + [RXDCTL6] = 1 << 16, + [RXDCTL7] = 1 << 16, + [RXDCTL8] = 1 << 16, + [RXDCTL9] = 1 << 16, + [RXDCTL10] = 1 << 16, + [RXDCTL11] = 1 << 16, + [RXDCTL12] = 1 << 16, + [RXDCTL13] = 1 << 16, + [RXDCTL14] = 1 << 16, + [RXDCTL15] = 1 << 16, + [TIPG] = 0x08 | (0x04 << 10) | (0x06 << 20), + [CTRL] = E1000_CTRL_FD | E1000_CTRL_LRST | E1000_CTRL_SPD_1000 | + E1000_CTRL_ADVD3WUC, + [STATUS] = E1000_STATUS_PHYRA | BIT(31), + [EECD] = E1000_EECD_FWE_DIS | E1000_EECD_PRES | + (2 << E1000_EECD_SIZE_EX_SHIFT), + [GCR] = E1000_L0S_ADJUST | + E1000_GCR_CMPL_TMOUT_RESEND | + E1000_GCR_CAP_VER2 | + E1000_L1_ENTRY_LATENCY_MSB | + E1000_L1_ENTRY_LATENCY_LSB, + [RXCSUM] = E1000_RXCSUM_IPOFLD | E1000_RXCSUM_TUOFLD, + [TXPBS] = 0x28, + [RXPBS] = 0x40, + [TCTL] = E1000_TCTL_PSP | (0xF << E1000_CT_SHIFT) | + (0x40 << E1000_COLD_SHIFT) | (0x1 << 26) | (0xA << 28), + [TCTL_EXT] = 0x40 | (0x42 << 10), + [DTXCTL] = E1000_DTXCTL_8023LL | E1000_DTXCTL_SPOOF_INT, + [VET] = ETH_P_VLAN | (ETH_P_VLAN << 16), + + [V2PMAILBOX0 ... V2PMAILBOX0 + IGB_MAX_VF_FUNCTIONS - 1] = E1000_V2PMAILBOX_RSTI, + [MBVFIMR] = 0xFF, + [VFRE] = 0xFF, + [VFTE] = 0xFF, + [VMOLR0 ... VMOLR0 + 7] = 0x2600 | E1000_VMOLR_STRCRC, + [RPLOLR] = E1000_RPLOLR_STRCRC, + [RLPML] = 0x2600, + [TXCTL0] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL1] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL2] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL3] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL4] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL5] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL6] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL7] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL8] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL9] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL10] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL11] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL12] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL13] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL14] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, + [TXCTL15] = E1000_DCA_TXCTRL_DATA_RRO_EN | + E1000_DCA_TXCTRL_TX_WB_RO_EN | + E1000_DCA_TXCTRL_DESC_RRO_EN, +}; + +static void igb_reset(IGBCore *core, bool sw) +{ + struct igb_tx *tx; + int i; + + timer_del(core->autoneg_timer); + + igb_intrmgr_reset(core); + + memset(core->phy, 0, sizeof core->phy); + memcpy(core->phy, igb_phy_reg_init, sizeof igb_phy_reg_init); + + for (i = 0; i < E1000E_MAC_SIZE; i++) { + if (sw && + (i == RXPBS || i == TXPBS || + (i >= EITR0 && i < EITR0 + IGB_INTR_NUM))) { + continue; + } + + core->mac[i] = i < ARRAY_SIZE(igb_mac_reg_init) ? + igb_mac_reg_init[i] : 0; + } + + if (qemu_get_queue(core->owner_nic)->link_down) { + igb_link_down(core); + } + + e1000x_reset_mac_addr(core->owner_nic, core->mac, core->permanent_mac); + + for (int vfn = 0; vfn < IGB_MAX_VF_FUNCTIONS; vfn++) { + /* Set RSTI, so VF can identify a PF reset is in progress */ + core->mac[V2PMAILBOX0 + vfn] |= E1000_V2PMAILBOX_RSTI; + } + + for (i = 0; i < ARRAY_SIZE(core->tx); i++) { + tx = &core->tx[i]; + memset(tx->ctx, 0, sizeof(tx->ctx)); + tx->first = true; + tx->skip_cp = false; + } +} + +void +igb_core_reset(IGBCore *core) +{ + igb_reset(core, false); +} + +void igb_core_pre_save(IGBCore *core) +{ + int i; + NetClientState *nc = qemu_get_queue(core->owner_nic); + + /* + * If link is down and auto-negotiation is supported and ongoing, + * complete auto-negotiation immediately. This allows us to look + * at MII_BMSR_AN_COMP to infer link status on load. + */ + if (nc->link_down && igb_have_autoneg(core)) { + core->phy[MII_BMSR] |= MII_BMSR_AN_COMP; + igb_update_flowctl_status(core); + } + + for (i = 0; i < ARRAY_SIZE(core->tx); i++) { + if (net_tx_pkt_has_fragments(core->tx[i].tx_pkt)) { + core->tx[i].skip_cp = true; + } + } +} + +int +igb_core_post_load(IGBCore *core) +{ + NetClientState *nc = qemu_get_queue(core->owner_nic); + + /* + * nc.link_down can't be migrated, so infer link_down according + * to link status bit in core.mac[STATUS]. + */ + nc->link_down = (core->mac[STATUS] & E1000_STATUS_LU) == 0; + + /* + * we need to restart intrmgr timers, as an older version of + * QEMU can have stopped them before migration + */ + igb_intrmgr_resume(core); + igb_autoneg_resume(core); + + return 0; +} diff --git a/hw/net/igb_core.h b/hw/net/igb_core.h new file mode 100644 index 0000000000..d70b54e318 --- /dev/null +++ b/hw/net/igb_core.h @@ -0,0 +1,146 @@ +/* + * Core code for QEMU igb emulation + * + * Datasheet: + * https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82576eg-gbe-datasheet.pdf + * + * Copyright (c) 2020-2023 Red Hat, Inc. + * Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Akihiko Odaki + * Gal Hammmer + * Marcel Apfelbaum + * Dmitry Fleytman + * Leonid Bloch + * Yan Vugenfirer + * + * Based on work done by: + * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. + * Copyright (c) 2008 Qumranet + * Based on work done by: + * Copyright (c) 2007 Dan Aloni + * Copyright (c) 2004 Antony T Curtis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 HW_NET_IGB_CORE_H +#define HW_NET_IGB_CORE_H + +#define E1000E_MAC_SIZE (0x8000) +#define IGB_EEPROM_SIZE (1024) + +#define IGB_INTR_NUM (25) +#define IGB_MSIX_VEC_NUM (10) +#define IGBVF_MSIX_VEC_NUM (3) +#define IGB_NUM_QUEUES (16) +#define IGB_NUM_VM_POOLS (8) + +typedef struct IGBCore IGBCore; + +enum { PHY_R = BIT(0), + PHY_W = BIT(1), + PHY_RW = PHY_R | PHY_W }; + +typedef struct IGBIntrDelayTimer_st { + QEMUTimer *timer; + bool running; + uint32_t delay_reg; + uint32_t delay_resolution_ns; + IGBCore *core; +} IGBIntrDelayTimer; + +struct IGBCore { + uint32_t mac[E1000E_MAC_SIZE]; + uint16_t phy[MAX_PHY_REG_ADDRESS + 1]; + uint16_t eeprom[IGB_EEPROM_SIZE]; + + uint8_t rx_desc_len; + + QEMUTimer *autoneg_timer; + + struct igb_tx { + struct e1000_adv_tx_context_desc ctx[2]; + uint32_t first_cmd_type_len; + uint32_t first_olinfo_status; + + bool first; + bool skip_cp; + + struct NetTxPkt *tx_pkt; + } tx[IGB_NUM_QUEUES]; + + struct NetRxPkt *rx_pkt; + + bool has_vnet; + int max_queue_num; + + IGBIntrDelayTimer eitr[IGB_INTR_NUM]; + + uint32_t eitr_guest_value[IGB_INTR_NUM]; + + uint8_t permanent_mac[ETH_ALEN]; + + NICState *owner_nic; + PCIDevice *owner; + void (*owner_start_recv)(PCIDevice *d); + + int64_t timadj; +}; + +void +igb_core_write(IGBCore *core, hwaddr addr, uint64_t val, unsigned size); + +uint64_t +igb_core_read(IGBCore *core, hwaddr addr, unsigned size); + +void +igb_core_pci_realize(IGBCore *regs, + const uint16_t *eeprom_templ, + uint32_t eeprom_size, + const uint8_t *macaddr); + +void +igb_core_reset(IGBCore *core); + +void +igb_core_pre_save(IGBCore *core); + +int +igb_core_post_load(IGBCore *core); + +void +igb_core_set_link_status(IGBCore *core); + +void +igb_core_pci_uninit(IGBCore *core); + +void +igb_core_vf_reset(IGBCore *core, uint16_t vfn); + +bool +igb_can_receive(IGBCore *core); + +ssize_t +igb_receive(IGBCore *core, const uint8_t *buf, size_t size); + +ssize_t +igb_receive_iov(IGBCore *core, const struct iovec *iov, int iovcnt); + +void +igb_start_recv(IGBCore *core); + +#endif diff --git a/hw/net/igb_regs.h b/hw/net/igb_regs.h new file mode 100644 index 0000000000..e5a47eab64 --- /dev/null +++ b/hw/net/igb_regs.h @@ -0,0 +1,721 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This is copied + edited from kernel header files in + * drivers/net/ethernet/intel/igb + */ + +#ifndef HW_IGB_REGS_H_ +#define HW_IGB_REGS_H_ + +#include "e1000x_regs.h" + +/* from igb/e1000_hw.h */ + +#define E1000_DEV_ID_82576 0x10C9 +#define E1000_DEV_ID_82576_FIBER 0x10E6 +#define E1000_DEV_ID_82576_SERDES 0x10E7 +#define E1000_DEV_ID_82576_QUAD_COPPER 0x10E8 +#define E1000_DEV_ID_82576_QUAD_COPPER_ET2 0x1526 +#define E1000_DEV_ID_82576_NS 0x150A +#define E1000_DEV_ID_82576_NS_SERDES 0x1518 +#define E1000_DEV_ID_82576_SERDES_QUAD 0x150D + +/* Context Descriptor */ +struct e1000_adv_tx_context_desc { + uint32_t vlan_macip_lens; + uint32_t seqnum_seed; + uint32_t type_tucmd_mlhl; + uint32_t mss_l4len_idx; +}; + +/* Advanced Transmit Descriptor */ +union e1000_adv_tx_desc { + struct { + uint64_t buffer_addr; /* Address of descriptor's data buffer */ + uint32_t cmd_type_len; + uint32_t olinfo_status; + } read; + struct { + uint64_t rsvd; /* Reserved */ + uint32_t nxtseq_seed; + uint32_t status; + } wb; +}; + +#define E1000_ADVTXD_POTS_IXSM 0x00000100 /* Insert TCP/UDP Checksum */ +#define E1000_ADVTXD_POTS_TXSM 0x00000200 /* Insert TCP/UDP Checksum */ + +#define E1000_TXD_POPTS_IXSM 0x00000001 /* Insert IP checksum */ +#define E1000_TXD_POPTS_TXSM 0x00000002 /* Insert TCP/UDP checksum */ + +/* Receive Descriptor - Advanced */ +union e1000_adv_rx_desc { + struct { + uint64_t pkt_addr; /* Packet Buffer Address */ + uint64_t hdr_addr; /* Header Buffer Address */ + } read; + struct { + struct { + struct { + uint16_t pkt_info; /* RSS Type, Packet Type */ + uint16_t hdr_info; /* Split Head, Buffer Length */ + } lo_dword; + union { + uint32_t rss; /* RSS Hash */ + struct { + uint16_t ip_id; /* IP Id */ + uint16_t csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + uint32_t status_error; /* Ext Status/Error */ + uint16_t length; /* Packet Length */ + uint16_t vlan; /* VLAN tag */ + } upper; + } wb; /* writeback */ +}; + +/* from igb/e1000_phy.h */ + +/* IGP01E1000 Specific Registers */ +#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* Port Config */ +#define IGP01E1000_PHY_PORT_STATUS 0x11 /* Status */ +#define IGP01E1000_PHY_PORT_CTRL 0x12 /* Control */ +#define IGP01E1000_PHY_LINK_HEALTH 0x13 /* PHY Link Health */ +#define IGP02E1000_PHY_POWER_MGMT 0x19 /* Power Management */ +#define IGP01E1000_PHY_PAGE_SELECT 0x1F /* Page Select */ +#define IGP01E1000_PHY_PCS_INIT_REG 0x00B4 +#define IGP01E1000_PHY_POLARITY_MASK 0x0078 +#define IGP01E1000_PSCR_AUTO_MDIX 0x1000 +#define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0=MDI, 1=MDIX */ +#define IGP01E1000_PSCFR_SMART_SPEED 0x0080 + +/* Enable flexible speed on link-up */ +#define IGP02E1000_PM_D0_LPLU 0x0002 /* For D0a states */ +#define IGP02E1000_PM_D3_LPLU 0x0004 /* For all other states */ +#define IGP01E1000_PLHR_SS_DOWNGRADE 0x8000 +#define IGP01E1000_PSSR_POLARITY_REVERSED 0x0002 +#define IGP01E1000_PSSR_MDIX 0x0800 +#define IGP01E1000_PSSR_SPEED_MASK 0xC000 +#define IGP01E1000_PSSR_SPEED_1000MBPS 0xC000 +#define IGP02E1000_PHY_CHANNEL_NUM 4 +#define IGP02E1000_PHY_AGC_A 0x11B1 +#define IGP02E1000_PHY_AGC_B 0x12B1 +#define IGP02E1000_PHY_AGC_C 0x14B1 +#define IGP02E1000_PHY_AGC_D 0x18B1 +#define IGP02E1000_AGC_LENGTH_SHIFT 9 /* Course - 15:13, Fine - 12:9 */ +#define IGP02E1000_AGC_LENGTH_MASK 0x7F +#define IGP02E1000_AGC_RANGE 15 + +/* from igb/igb.h */ + +#define E1000_PCS_CFG_IGN_SD 1 + +/* Interrupt defines */ +#define IGB_START_ITR 648 /* ~6000 ints/sec */ +#define IGB_4K_ITR 980 +#define IGB_20K_ITR 196 +#define IGB_70K_ITR 56 + +/* TX/RX descriptor defines */ +#define IGB_DEFAULT_TXD 256 +#define IGB_DEFAULT_TX_WORK 128 +#define IGB_MIN_TXD 80 +#define IGB_MAX_TXD 4096 + +#define IGB_DEFAULT_RXD 256 +#define IGB_MIN_RXD 80 +#define IGB_MAX_RXD 4096 + +#define IGB_DEFAULT_ITR 3 /* dynamic */ +#define IGB_MAX_ITR_USECS 10000 +#define IGB_MIN_ITR_USECS 10 +#define NON_Q_VECTORS 1 +#define MAX_Q_VECTORS 8 +#define MAX_MSIX_ENTRIES 10 + +/* Transmit and receive queues */ +#define IGB_MAX_RX_QUEUES 8 +#define IGB_MAX_RX_QUEUES_82575 4 +#define IGB_MAX_RX_QUEUES_I211 2 +#define IGB_MAX_TX_QUEUES 8 +#define IGB_MAX_VF_MC_ENTRIES 30 +#define IGB_MAX_VF_FUNCTIONS 8 +#define IGB_MAX_VFTA_ENTRIES 128 +#define IGB_82576_VF_DEV_ID 0x10CA +#define IGB_I350_VF_DEV_ID 0x1520 + +/* VLAN info */ +#define IGB_TX_FLAGS_VLAN_MASK 0xffff0000 +#define IGB_TX_FLAGS_VLAN_SHIFT 16 + +/* from igb/e1000_82575.h */ + +#define E1000_MRQC_ENABLE_RSS_MQ 0x00000002 +#define E1000_MRQC_ENABLE_VMDQ 0x00000003 +#define E1000_MRQC_RSS_FIELD_IPV4_UDP 0x00400000 +#define E1000_MRQC_ENABLE_VMDQ_RSS_MQ 0x00000005 +#define E1000_MRQC_RSS_FIELD_IPV6_UDP 0x00800000 +#define E1000_MRQC_RSS_FIELD_IPV6_UDP_EX 0x01000000 + +/* Adv Transmit Descriptor Config Masks */ +#define E1000_ADVTXD_MAC_TSTAMP 0x00080000 /* IEEE1588 Timestamp packet */ +#define E1000_ADVTXD_DTYP_CTXT 0x00200000 /* Advanced Context Descriptor */ +#define E1000_ADVTXD_DTYP_DATA 0x00300000 /* Advanced Data Descriptor */ +#define E1000_ADVTXD_DCMD_EOP 0x01000000 /* End of Packet */ +#define E1000_ADVTXD_DCMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_ADVTXD_DCMD_RS 0x08000000 /* Report Status */ +#define E1000_ADVTXD_DCMD_DEXT 0x20000000 /* Descriptor extension (1=Adv) */ +#define E1000_ADVTXD_DCMD_VLE 0x40000000 /* VLAN pkt enable */ +#define E1000_ADVTXD_DCMD_TSE 0x80000000 /* TCP Seg enable */ +#define E1000_ADVTXD_PAYLEN_SHIFT 14 /* Adv desc PAYLEN shift */ + +#define E1000_ADVTXD_MACLEN_SHIFT 9 /* Adv ctxt desc mac len shift */ +#define E1000_ADVTXD_TUCMD_L4T_UDP 0x00000000 /* L4 Packet TYPE of UDP */ +#define E1000_ADVTXD_TUCMD_IPV4 0x00000400 /* IP Packet Type: 1=IPv4 */ +#define E1000_ADVTXD_TUCMD_L4T_TCP 0x00000800 /* L4 Packet TYPE of TCP */ +#define E1000_ADVTXD_TUCMD_L4T_SCTP 0x00001000 /* L4 packet TYPE of SCTP */ +/* IPSec Encrypt Enable for ESP */ +#define E1000_ADVTXD_L4LEN_SHIFT 8 /* Adv ctxt L4LEN shift */ +#define E1000_ADVTXD_MSS_SHIFT 16 /* Adv ctxt MSS shift */ +/* Adv ctxt IPSec SA IDX mask */ +/* Adv ctxt IPSec ESP len mask */ + +/* Additional Transmit Descriptor Control definitions */ +#define E1000_TXDCTL_QUEUE_ENABLE 0x02000000 /* Enable specific Tx Queue */ + +/* Additional Receive Descriptor Control definitions */ +#define E1000_RXDCTL_QUEUE_ENABLE 0x02000000 /* Enable specific Rx Queue */ + +/* Direct Cache Access (DCA) definitions */ +#define E1000_DCA_CTRL_DCA_MODE_DISABLE 0x01 /* DCA Disable */ +#define E1000_DCA_CTRL_DCA_MODE_CB2 0x02 /* DCA Mode CB2 */ + +#define E1000_DCA_RXCTRL_CPUID_MASK 0x0000001F /* Rx CPUID Mask */ +#define E1000_DCA_RXCTRL_DESC_DCA_EN BIT(5) /* DCA Rx Desc enable */ +#define E1000_DCA_RXCTRL_HEAD_DCA_EN BIT(6) /* DCA Rx Desc header enable */ +#define E1000_DCA_RXCTRL_DATA_DCA_EN BIT(7) /* DCA Rx Desc payload enable */ +#define E1000_DCA_RXCTRL_DESC_RRO_EN BIT(9) /* DCA Rx rd Desc Relax Order */ + +#define E1000_DCA_TXCTRL_CPUID_MASK 0x0000001F /* Tx CPUID Mask */ +#define E1000_DCA_TXCTRL_DESC_DCA_EN BIT(5) /* DCA Tx Desc enable */ +#define E1000_DCA_TXCTRL_DESC_RRO_EN BIT(9) /* Tx rd Desc Relax Order */ +#define E1000_DCA_TXCTRL_TX_WB_RO_EN BIT(11) /* Tx Desc writeback RO bit */ +#define E1000_DCA_TXCTRL_DATA_RRO_EN BIT(13) /* Tx rd data Relax Order */ + +/* Additional DCA related definitions, note change in position of CPUID */ +#define E1000_DCA_TXCTRL_CPUID_MASK_82576 0xFF000000 /* Tx CPUID Mask */ +#define E1000_DCA_RXCTRL_CPUID_MASK_82576 0xFF000000 /* Rx CPUID Mask */ +#define E1000_DCA_TXCTRL_CPUID_SHIFT 24 /* Tx CPUID now in the last byte */ +#define E1000_DCA_RXCTRL_CPUID_SHIFT 24 /* Rx CPUID now in the last byte */ + +/* ETQF register bit definitions */ +#define E1000_ETQF_FILTER_ENABLE BIT(26) +#define E1000_ETQF_1588 BIT(30) +#define E1000_ETQF_IMM_INT BIT(29) +#define E1000_ETQF_QUEUE_ENABLE BIT(31) +#define E1000_ETQF_QUEUE_SHIFT 16 +#define E1000_ETQF_QUEUE_MASK 0x00070000 +#define E1000_ETQF_ETYPE_MASK 0x0000FFFF + +#define E1000_DTXSWC_MAC_SPOOF_MASK 0x000000FF /* Per VF MAC spoof control */ +#define E1000_DTXSWC_VLAN_SPOOF_MASK 0x0000FF00 /* Per VF VLAN spoof control */ +#define E1000_DTXSWC_LLE_MASK 0x00FF0000 /* Per VF Local LB enables */ +#define E1000_DTXSWC_VLAN_SPOOF_SHIFT 8 +#define E1000_DTXSWC_VMDQ_LOOPBACK_EN BIT(31) /* global VF LB enable */ + +/* Easy defines for setting default pool, would normally be left a zero */ +#define E1000_VT_CTL_DEFAULT_POOL_SHIFT 7 +#define E1000_VT_CTL_DEFAULT_POOL_MASK (0x7 << E1000_VT_CTL_DEFAULT_POOL_SHIFT) + +/* Other useful VMD_CTL register defines */ +#define E1000_VT_CTL_IGNORE_MAC BIT(28) +#define E1000_VT_CTL_DISABLE_DEF_POOL BIT(29) +#define E1000_VT_CTL_VM_REPL_EN BIT(30) + +/* Per VM Offload register setup */ +#define E1000_VMOLR_RLPML_MASK 0x00003FFF /* Long Packet Maximum Length mask */ +#define E1000_VMOLR_LPE 0x00010000 /* Accept Long packet */ +#define E1000_VMOLR_RSSE 0x00020000 /* Enable RSS */ +#define E1000_VMOLR_AUPE 0x01000000 /* Accept untagged packets */ +#define E1000_VMOLR_ROMPE 0x02000000 /* Accept overflow multicast */ +#define E1000_VMOLR_ROPE 0x04000000 /* Accept overflow unicast */ +#define E1000_VMOLR_BAM 0x08000000 /* Accept Broadcast packets */ +#define E1000_VMOLR_MPME 0x10000000 /* Multicast promiscuous mode */ +#define E1000_VMOLR_STRVLAN 0x40000000 /* Vlan stripping enable */ +#define E1000_VMOLR_STRCRC 0x80000000 /* CRC stripping enable */ + +#define E1000_DVMOLR_HIDEVLAN 0x20000000 /* Hide vlan enable */ +#define E1000_DVMOLR_STRVLAN 0x40000000 /* Vlan stripping enable */ +#define E1000_DVMOLR_STRCRC 0x80000000 /* CRC stripping enable */ + +#define E1000_VLVF_ARRAY_SIZE 32 +#define E1000_VLVF_VLANID_MASK 0x00000FFF +#define E1000_VLVF_POOLSEL_SHIFT 12 +#define E1000_VLVF_POOLSEL_MASK (0xFF << E1000_VLVF_POOLSEL_SHIFT) +#define E1000_VLVF_LVLAN 0x00100000 +#define E1000_VLVF_VLANID_ENABLE 0x80000000 + +#define E1000_VMVIR_VLANA_DEFAULT 0x40000000 /* Always use default VLAN */ +#define E1000_VMVIR_VLANA_NEVER 0x80000000 /* Never insert VLAN tag */ + +#define E1000_IOVCTL 0x05BBC +#define E1000_IOVCTL_REUSE_VFQ 0x00000001 + +#define E1000_RPLOLR_STRVLAN 0x40000000 +#define E1000_RPLOLR_STRCRC 0x80000000 + +#define E1000_DTXCTL_8023LL 0x0004 +#define E1000_DTXCTL_VLAN_ADDED 0x0008 +#define E1000_DTXCTL_OOS_ENABLE 0x0010 +#define E1000_DTXCTL_MDP_EN 0x0020 +#define E1000_DTXCTL_SPOOF_INT 0x0040 + +/* from igb/e1000_defines.h */ + +/* Physical Func Reset Done Indication */ +#define E1000_CTRL_EXT_PFRSTD 0x00004000 + +#define E1000_IVAR_VALID 0x80 +#define E1000_GPIE_NSICR 0x00000001 +#define E1000_GPIE_MSIX_MODE 0x00000010 +#define E1000_GPIE_EIAME 0x40000000 +#define E1000_GPIE_PBA 0x80000000 + +/* Transmit Control */ +#define E1000_TCTL_EN 0x00000002 /* enable tx */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ + +/* Collision related configuration parameters */ +#define E1000_COLLISION_THRESHOLD 15 +#define E1000_CT_SHIFT 4 +#define E1000_COLLISION_DISTANCE 63 +#define E1000_COLD_SHIFT 12 + +#define E1000_RAH_POOL_MASK 0x03FC0000 +#define E1000_RAH_POOL_1 0x00040000 + +#define E1000_ICR_VMMB 0x00000100 /* VM MB event */ +#define E1000_ICR_TS 0x00080000 /* Time Sync Interrupt */ +#define E1000_ICR_DRSTA 0x40000000 /* Device Reset Asserted */ +/* If this bit asserted, the driver should claim the interrupt */ +#define E1000_ICR_INT_ASSERTED 0x80000000 +/* LAN connected device generates an interrupt */ +#define E1000_ICR_DOUTSYNC 0x10000000 /* NIC DMA out of sync */ + +/* Extended Interrupt Cause Read */ +#define E1000_EICR_RX_QUEUE0 0x00000001 /* Rx Queue 0 Interrupt */ +#define E1000_EICR_RX_QUEUE1 0x00000002 /* Rx Queue 1 Interrupt */ +#define E1000_EICR_RX_QUEUE2 0x00000004 /* Rx Queue 2 Interrupt */ +#define E1000_EICR_RX_QUEUE3 0x00000008 /* Rx Queue 3 Interrupt */ +#define E1000_EICR_TX_QUEUE0 0x00000100 /* Tx Queue 0 Interrupt */ +#define E1000_EICR_TX_QUEUE1 0x00000200 /* Tx Queue 1 Interrupt */ +#define E1000_EICR_TX_QUEUE2 0x00000400 /* Tx Queue 2 Interrupt */ +#define E1000_EICR_TX_QUEUE3 0x00000800 /* Tx Queue 3 Interrupt */ +#define E1000_EICR_OTHER 0x80000000 /* Interrupt Cause Active */ + +/* Extended Interrupt Cause Set */ +/* E1000_EITR_CNT_IGNR is only for 82576 and newer */ +#define E1000_EITR_CNT_IGNR 0x80000000 /* Don't reset counters on write */ + +#define E1000_TSYNCTXCTL_VALID 0x00000001 /* tx timestamp valid */ +#define E1000_TSYNCTXCTL_ENABLED 0x00000010 /* enable tx timestampping */ + +/* PCI Express Control */ +#define E1000_GCR_CMPL_TMOUT_MASK 0x0000F000 +#define E1000_GCR_CMPL_TMOUT_10ms 0x00001000 +#define E1000_GCR_CMPL_TMOUT_RESEND 0x00010000 +#define E1000_GCR_CAP_VER2 0x00040000 + +#define PHY_REVISION_MASK 0xFFFFFFF0 +#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ +#define MAX_PHY_MULTI_PAGE_REG 0xF + +#define IGP03E1000_E_PHY_ID 0x02A80390 + +/* from igb/e1000_mbox.h */ + +#define E1000_P2VMAILBOX_STS 0x00000001 /* Initiate message send to VF */ +#define E1000_P2VMAILBOX_ACK 0x00000002 /* Ack message recv'd from VF */ +#define E1000_P2VMAILBOX_VFU 0x00000004 /* VF owns the mailbox buffer */ +#define E1000_P2VMAILBOX_PFU 0x00000008 /* PF owns the mailbox buffer */ +#define E1000_P2VMAILBOX_RVFU 0x00000010 /* Reset VFU - used when VF stuck */ + +#define E1000_MBVFICR_VFREQ_MASK 0x000000FF /* bits for VF messages */ +#define E1000_MBVFICR_VFREQ_VF1 0x00000001 /* bit for VF 1 message */ +#define E1000_MBVFICR_VFACK_MASK 0x00FF0000 /* bits for VF acks */ +#define E1000_MBVFICR_VFACK_VF1 0x00010000 /* bit for VF 1 ack */ + +#define E1000_V2PMAILBOX_SIZE 16 /* 16 32 bit words - 64 bytes */ + +/* + * If it's a E1000_VF_* msg then it originates in the VF and is sent to the + * PF. The reverse is true if it is E1000_PF_*. + * Message ACK's are the value or'd with 0xF0000000 + */ +/* Messages below or'd with this are the ACK */ +#define E1000_VT_MSGTYPE_ACK 0x80000000 +/* Messages below or'd with this are the NACK */ +#define E1000_VT_MSGTYPE_NACK 0x40000000 +/* Indicates that VF is still clear to send requests */ +#define E1000_VT_MSGTYPE_CTS 0x20000000 +#define E1000_VT_MSGINFO_SHIFT 16 +/* bits 23:16 are used for extra info for certain messages */ +#define E1000_VT_MSGINFO_MASK (0xFF << E1000_VT_MSGINFO_SHIFT) + +#define E1000_VF_RESET 0x01 /* VF requests reset */ +#define E1000_VF_SET_MAC_ADDR 0x02 /* VF requests to set MAC addr */ +/* VF requests to clear all unicast MAC filters */ +#define E1000_VF_MAC_FILTER_CLR (0x01 << E1000_VT_MSGINFO_SHIFT) +/* VF requests to add unicast MAC filter */ +#define E1000_VF_MAC_FILTER_ADD (0x02 << E1000_VT_MSGINFO_SHIFT) +#define E1000_VF_SET_MULTICAST 0x03 /* VF requests to set MC addr */ +#define E1000_VF_SET_VLAN 0x04 /* VF requests to set VLAN */ +#define E1000_VF_SET_LPE 0x05 /* VF requests to set VMOLR.LPE */ +#define E1000_VF_SET_PROMISC 0x06 /*VF requests to clear VMOLR.ROPE/MPME*/ +#define E1000_VF_SET_PROMISC_MULTICAST (0x02 << E1000_VT_MSGINFO_SHIFT) + +#define E1000_PF_CONTROL_MSG 0x0100 /* PF control message */ + +/* from igb/e1000_regs.h */ + +#define E1000_EICR 0x01580 /* Ext. Interrupt Cause Read - R/clr */ +#define E1000_EITR(_n) (0x01680 + (0x4 * (_n))) +#define E1000_EICS 0x01520 /* Ext. Interrupt Cause Set - W0 */ +#define E1000_EIMS 0x01524 /* Ext. Interrupt Mask Set/Read - RW */ +#define E1000_EIMC 0x01528 /* Ext. Interrupt Mask Clear - WO */ +#define E1000_EIAC 0x0152C /* Ext. Interrupt Auto Clear - RW */ +#define E1000_EIAM 0x01530 /* Ext. Interrupt Ack Auto Clear Mask - RW */ +#define E1000_GPIE 0x01514 /* General Purpose Interrupt Enable; RW */ +#define E1000_IVAR0 0x01700 /* Interrupt Vector Allocation Register - RW */ +#define E1000_IVAR_MISC 0x01740 /* Interrupt Vector Allocation Register (last) - RW */ +#define E1000_FRTIMER 0x01048 /* Free Running Timer - RW */ +#define E1000_FCRTV 0x02460 /* Flow Control Refresh Timer Value - RW */ + +#define E1000_TSYNCRXCFG 0x05F50 /* Time Sync Rx Configuration - RW */ + +/* Filtering Registers */ +#define E1000_SAQF(_n) (0x5980 + 4 * (_n)) +#define E1000_DAQF(_n) (0x59A0 + 4 * (_n)) +#define E1000_SPQF(_n) (0x59C0 + 4 * (_n)) +#define E1000_FTQF(_n) (0x59E0 + 4 * (_n)) +#define E1000_SAQF0 E1000_SAQF(0) +#define E1000_DAQF0 E1000_DAQF(0) +#define E1000_SPQF0 E1000_SPQF(0) +#define E1000_FTQF0 E1000_FTQF(0) +#define E1000_SYNQF(_n) (0x055FC + (4 * (_n))) /* SYN Packet Queue Fltr */ +#define E1000_ETQF(_n) (0x05CB0 + (4 * (_n))) /* EType Queue Fltr */ + +#define E1000_RQDPC(_n) (0x0C030 + ((_n) * 0x40)) + +#define E1000_RXPBS 0x02404 /* Rx Packet Buffer Size - RW */ +#define E1000_TXPBS 0x03404 /* Tx Packet Buffer Size - RW */ + +#define E1000_DTXCTL 0x03590 /* DMA TX Control - RW */ + +#define E1000_HTCBDPC 0x04124 /* Host TX Circuit Breaker Dropped Count */ +#define E1000_RLPML 0x05004 /* RX Long Packet Max Length */ +#define E1000_RA2 0x054E0 /* 2nd half of Rx address array - RW Array */ +#define E1000_PSRTYPE(_i) (0x05480 + ((_i) * 4)) +#define E1000_VT_CTL 0x0581C /* VMDq Control - RW */ + +/* VT Registers */ +#define E1000_MBVFICR 0x00C80 /* Mailbox VF Cause - RWC */ +#define E1000_MBVFIMR 0x00C84 /* Mailbox VF int Mask - RW */ +#define E1000_VFLRE 0x00C88 /* VF Register Events - RWC */ +#define E1000_VFRE 0x00C8C /* VF Receive Enables */ +#define E1000_VFTE 0x00C90 /* VF Transmit Enables */ +#define E1000_QDE 0x02408 /* Queue Drop Enable - RW */ +#define E1000_DTXSWC 0x03500 /* DMA Tx Switch Control - RW */ +#define E1000_WVBR 0x03554 /* VM Wrong Behavior - RWS */ +#define E1000_RPLOLR 0x05AF0 /* Replication Offload - RW */ +#define E1000_UTA 0x0A000 /* Unicast Table Array - RW */ +#define E1000_IOVTCL 0x05BBC /* IOV Control Register */ +#define E1000_TXSWC 0x05ACC /* Tx Switch Control */ +#define E1000_LVMMC 0x03548 /* Last VM Misbehavior cause */ +/* These act per VF so an array friendly macro is used */ +#define E1000_P2VMAILBOX(_n) (0x00C00 + (4 * (_n))) +#define E1000_VMBMEM(_n) (0x00800 + (64 * (_n))) +#define E1000_VMOLR(_n) (0x05AD0 + (4 * (_n))) +#define E1000_DVMOLR(_n) (0x0C038 + (64 * (_n))) +#define E1000_VLVF(_n) (0x05D00 + (4 * (_n))) /* VLAN VM Filter */ +#define E1000_VMVIR(_n) (0x03700 + (4 * (_n))) + +/* from igbvf/defines.h */ + +/* SRRCTL bit definitions */ +#define E1000_SRRCTL_BSIZEPKT_SHIFT 10 /* Shift _right_ */ +#define E1000_SRRCTL_BSIZEHDRSIZE_MASK 0x00000F00 +#define E1000_SRRCTL_BSIZEHDRSIZE_SHIFT 2 /* Shift _left_ */ +#define E1000_SRRCTL_DESCTYPE_ADV_ONEBUF 0x02000000 +#define E1000_SRRCTL_DESCTYPE_HDR_SPLIT 0x04000000 +#define E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS 0x0A000000 +#define E1000_SRRCTL_DESCTYPE_MASK 0x0E000000 +#define E1000_SRRCTL_DROP_EN 0x80000000 + +#define E1000_SRRCTL_BSIZEPKT_MASK 0x0000007F +#define E1000_SRRCTL_BSIZEHDR_MASK 0x00003F00 + +/* from igbvf/mbox.h */ + +#define E1000_V2PMAILBOX_REQ 0x00000001 /* Request for PF Ready bit */ +#define E1000_V2PMAILBOX_ACK 0x00000002 /* Ack PF message received */ +#define E1000_V2PMAILBOX_VFU 0x00000004 /* VF owns the mailbox buffer */ +#define E1000_V2PMAILBOX_PFU 0x00000008 /* PF owns the mailbox buffer */ +#define E1000_V2PMAILBOX_PFSTS 0x00000010 /* PF wrote a message in the MB */ +#define E1000_V2PMAILBOX_PFACK 0x00000020 /* PF ack the previous VF msg */ +#define E1000_V2PMAILBOX_RSTI 0x00000040 /* PF has reset indication */ +#define E1000_V2PMAILBOX_RSTD 0x00000080 /* PF has indicated reset done */ +#define E1000_V2PMAILBOX_R2C_BITS 0x000000B0 /* All read to clear bits */ + +#define E1000_VFMAILBOX_SIZE 16 /* 16 32 bit words - 64 bytes */ + +/* + * If it's a E1000_VF_* msg then it originates in the VF and is sent to the + * PF. The reverse is true if it is E1000_PF_*. + * Message ACK's are the value or'd with 0xF0000000 + */ +/* Messages below or'd with this are the ACK */ +#define E1000_VT_MSGTYPE_ACK 0x80000000 +/* Messages below or'd with this are the NACK */ +#define E1000_VT_MSGTYPE_NACK 0x40000000 +/* Indicates that VF is still clear to send requests */ +#define E1000_VT_MSGTYPE_CTS 0x20000000 + +/* We have a total wait time of 1s for vf mailbox posted messages */ +#define E1000_VF_MBX_INIT_TIMEOUT 2000 /* retry count for mbx timeout */ +#define E1000_VF_MBX_INIT_DELAY 500 /* usec delay between retries */ + +#define E1000_VT_MSGINFO_SHIFT 16 +/* bits 23:16 are used for extra info for certain messages */ +#define E1000_VT_MSGINFO_MASK (0xFF << E1000_VT_MSGINFO_SHIFT) + +#define E1000_VF_RESET 0x01 /* VF requests reset */ +#define E1000_VF_SET_MAC_ADDR 0x02 /* VF requests PF to set MAC addr */ +/* VF requests PF to clear all unicast MAC filters */ +#define E1000_VF_MAC_FILTER_CLR (0x01 << E1000_VT_MSGINFO_SHIFT) +/* VF requests PF to add unicast MAC filter */ +#define E1000_VF_MAC_FILTER_ADD (0x02 << E1000_VT_MSGINFO_SHIFT) +#define E1000_VF_SET_MULTICAST 0x03 /* VF requests PF to set MC addr */ +#define E1000_VF_SET_VLAN 0x04 /* VF requests PF to set VLAN */ +#define E1000_VF_SET_LPE 0x05 /* VF requests PF to set VMOLR.LPE */ + +#define E1000_PF_CONTROL_MSG 0x0100 /* PF control message */ + +/* from igbvf/regs.h */ + +/* Statistics registers */ +#define E1000_VFGPRC 0x00F10 +#define E1000_VFGORC 0x00F18 +#define E1000_VFMPRC 0x00F3C +#define E1000_VFGPTC 0x00F14 +#define E1000_VFGOTC 0x00F34 +#define E1000_VFGOTLBC 0x00F50 +#define E1000_VFGPTLBC 0x00F44 +#define E1000_VFGORLBC 0x00F48 +#define E1000_VFGPRLBC 0x00F40 + +/* These act per VF so an array friendly macro is used */ +#define E1000_V2PMAILBOX(_n) (0x00C40 + (4 * (_n))) +#define E1000_VMBMEM(_n) (0x00800 + (64 * (_n))) + +/* from igbvf/vf.h */ + +#define E1000_DEV_ID_82576_VF 0x10CA + +/* new */ + +/* Receive Registers */ + +/* RX Descriptor Base Low; RW */ +#define E1000_RDBAL(_n) (0x0C000 + (0x40 * (_n))) +#define E1000_RDBAL_A(_n) (0x02800 + (0x100 * (_n))) + +/* RX Descriptor Base High; RW */ +#define E1000_RDBAH(_n) (0x0C004 + (0x40 * (_n))) +#define E1000_RDBAH_A(_n) (0x02804 + (0x100 * (_n))) + +/* RX Descriptor Ring Length; RW */ +#define E1000_RDLEN(_n) (0x0C008 + (0x40 * (_n))) +#define E1000_RDLEN_A(_n) (0x02808 + (0x100 * (_n))) + +/* Split and Replication Receive Control; RW */ +#define E1000_SRRCTL(_n) (0x0C00C + (0x40 * (_n))) +#define E1000_SRRCTL_A(_n) (0x0280C + (0x100 * (_n))) + +/* RX Descriptor Head; RW */ +#define E1000_RDH(_n) (0x0C010 + (0x40 * (_n))) +#define E1000_RDH_A(_n) (0x02810 + (0x100 * (_n))) + +/* RX DCA Control; RW */ +#define E1000_RXCTL(_n) (0x0C014 + (0x40 * (_n))) +#define E1000_RXCTL_A(_n) (0x02814 + (0x100 * (_n))) + +/* RX Descriptor Tail; RW */ +#define E1000_RDT(_n) (0x0C018 + (0x40 * (_n))) +#define E1000_RDT_A(_n) (0x02818 + (0x100 * (_n))) + +/* RX Descriptor Control; RW */ +#define E1000_RXDCTL(_n) (0x0C028 + (0x40 * (_n))) +#define E1000_RXDCTL_A(_n) (0x02828 + (0x100 * (_n))) + +/* RX Queue Drop Packet Count; RC */ +#define E1000_RQDPC_A(_n) (0x02830 + (0x100 * (_n))) + +/* Transmit Registers */ + +/* TX Descriptor Base Low; RW */ +#define E1000_TDBAL(_n) (0x0E000 + (0x40 * (_n))) +#define E1000_TDBAL_A(_n) (0x03800 + (0x100 * (_n))) + +/* TX Descriptor Base High; RW */ +#define E1000_TDBAH(_n) (0x0E004 + (0x40 * (_n))) +#define E1000_TDBAH_A(_n) (0x03804 + (0x100 * (_n))) + +/* TX Descriptor Ring Length; RW */ +#define E1000_TDLEN(_n) (0x0E008 + (0x40 * (_n))) +#define E1000_TDLEN_A(_n) (0x03808 + (0x100 * (_n))) + +/* TX Descriptor Head; RW */ +#define E1000_TDH(_n) (0x0E010 + (0x40 * (_n))) +#define E1000_TDH_A(_n) (0x03810 + (0x100 * (_n))) + +/* TX DCA Control; RW */ +#define E1000_TXCTL(_n) (0x0E014 + (0x40 * (_n))) +#define E1000_TXCTL_A(_n) (0x03814 + (0x100 * (_n))) + +/* TX Descriptor Tail; RW */ +#define E1000_TDT(_n) (0x0E018 + (0x40 * (_n))) +#define E1000_TDT_A(_n) (0x03818 + (0x100 * (_n))) + +/* TX Descriptor Control; RW */ +#define E1000_TXDCTL(_n) (0x0E028 + (0x40 * (_n))) +#define E1000_TXDCTL_A(_n) (0x03828 + (0x100 * (_n))) + +/* TX Descriptor Completion Write–Back Address Low; RW */ +#define E1000_TDWBAL(_n) (0x0E038 + (0x40 * (_n))) +#define E1000_TDWBAL_A(_n) (0x03838 + (0x100 * (_n))) + +/* TX Descriptor Completion Write–Back Address High; RW */ +#define E1000_TDWBAH(_n) (0x0E03C + (0x40 * (_n))) +#define E1000_TDWBAH_A(_n) (0x0383C + (0x100 * (_n))) + +#define E1000_MTA_A 0x0200 + +#define E1000_XDBAL_MASK (~(BIT(5) - 1)) /* TDBAL and RDBAL Registers Mask */ + +#define E1000_ICR_MACSEC 0x00000020 /* MACSec */ +#define E1000_ICR_RX0 0x00000040 /* Receiver Overrun */ +#define E1000_ICR_GPI_SDP0 0x00000800 /* General Purpose, SDP0 pin */ +#define E1000_ICR_GPI_SDP1 0x00001000 /* General Purpose, SDP1 pin */ +#define E1000_ICR_GPI_SDP2 0x00002000 /* General Purpose, SDP2 pin */ +#define E1000_ICR_GPI_SDP3 0x00004000 /* General Purpose, SDP3 pin */ +#define E1000_ICR_PTRAP 0x00008000 /* Probe Trap */ +#define E1000_ICR_MNG 0x00040000 /* Management Event */ +#define E1000_ICR_OMED 0x00100000 /* Other Media Energy Detected */ +#define E1000_ICR_FER 0x00400000 /* Fatal Error */ +#define E1000_ICR_NFER 0x00800000 /* Non Fatal Error */ +#define E1000_ICR_CSRTO 0x01000000 /* CSR access Time Out Indication */ +#define E1000_ICR_SCE 0x02000000 /* Storm Control Event */ +#define E1000_ICR_SW_WD 0x04000000 /* Software Watchdog */ + +/* Extended Interrupts */ + +#define E1000_EICR_MSIX_MASK 0x01FFFFFF /* Bits used in MSI-X mode */ +#define E1000_EICR_LEGACY_MASK 0x4000FFFF /* Bits used in non MSI-X mode */ + +/* Mirror VF Control (only RST bit); RW */ +#define E1000_PVTCTRL(_n) (0x10000 + (_n) * 0x100) + +/* Mirror Good Packets Received Count; RO */ +#define E1000_PVFGPRC(_n) (0x10010 + (_n) * 0x100) + +/* Mirror Good Packets Transmitted Count; RO */ +#define E1000_PVFGPTC(_n) (0x10014 + (_n) * 0x100) + +/* Mirror Good Octets Received Count; RO */ +#define E1000_PVFGORC(_n) (0x10018 + (_n) * 0x100) + +/* Mirror Extended Interrupt Cause Set; WO */ +#define E1000_PVTEICS(_n) (0x10020 + (_n) * 0x100) + +/* Mirror Extended Interrupt Mask Set/Read; RW */ +#define E1000_PVTEIMS(_n) (0x10024 + (_n) * 0x100) + +/* Mirror Extended Interrupt Mask Clear; WO */ +#define E1000_PVTEIMC(_n) (0x10028 + (_n) * 0x100) + +/* Mirror Extended Interrupt Auto Clear; RW */ +#define E1000_PVTEIAC(_n) (0x1002C + (_n) * 0x100) + +/* Mirror Extended Interrupt Auto Mask Enable; RW */ +#define E1000_PVTEIAM(_n) (0x10030 + (_n) * 0x100) + +/* Mirror Good Octets Transmitted Count; RO */ +#define E1000_PVFGOTC(_n) (0x10034 + (_n) * 0x100) + +/* Mirror Multicast Packets Received Count; RO */ +#define E1000_PVFMPRC(_n) (0x1003C + (_n) * 0x100) + +/* Mirror Good RX Packets loopback Count; RO */ +#define E1000_PVFGPRLBC(_n) (0x10040 + (_n) * 0x100) + +/* Mirror Good TX packets loopback Count; RO */ +#define E1000_PVFGPTLBC(_n) (0x10044 + (_n) * 0x100) + +/* Mirror Good RX Octets loopback Count; RO */ +#define E1000_PVFGORLBC(_n) (0x10048 + (_n) * 0x100) + +/* Mirror Good TX Octets loopback Count; RO */ +#define E1000_PVFGOTLBC(_n) (0x10050 + (_n) * 0x100) + +/* Mirror Extended Interrupt Cause Set; RC/W1C */ +#define E1000_PVTEICR(_n) (0x10080 + (_n) * 0x100) + +/* + * These are fake addresses that, according to the specification, the device + * is not using. They are used to distinguish between the PF and the VFs + * accessing their VTIVAR register (which is the same address, 0x1700) + */ +#define E1000_VTIVAR 0x11700 +#define E1000_VTIVAR_MISC 0x11720 + +#define E1000_RSS_QUEUE(reta, hash) (E1000_RETA_VAL(reta, hash) & 0x0F) + +#define E1000_MRQ_RSS_TYPE_IPV4UDP 7 +#define E1000_MRQ_RSS_TYPE_IPV6UDP 8 + +#define E1000_STATUS_IOV_MODE 0x00040000 + +#define E1000_STATUS_NUM_VFS_SHIFT 14 + +#define E1000_ADVRXD_PKT_IP4 BIT(0) +#define E1000_ADVRXD_PKT_IP6 BIT(2) +#define E1000_ADVRXD_PKT_IP6E BIT(3) +#define E1000_ADVRXD_PKT_TCP BIT(4) +#define E1000_ADVRXD_PKT_UDP BIT(5) +#define E1000_ADVRXD_PKT_SCTP BIT(6) + +#define IGB_MAX_PS_BUFFERS 2 + +#define E1000_ADVRXD_HDR_LEN_OFFSET (21 - 16) +#define E1000_ADVRXD_ADV_HDR_LEN_MASK ((BIT(10) - 1) << \ + E1000_ADVRXD_HDR_LEN_OFFSET) +#define E1000_ADVRXD_HDR_SPH BIT(15) +#define E1000_ADVRXD_ST_ERR_HBO_OFFSET BIT(3 + 20) + +static inline uint8_t igb_ivar_entry_rx(uint8_t i) +{ + return i < 8 ? i * 4 : (i - 8) * 4 + 2; +} + +static inline uint8_t igb_ivar_entry_tx(uint8_t i) +{ + return i < 8 ? i * 4 + 1 : (i - 8) * 4 + 3; +} + +#endif diff --git a/hw/net/igbvf.c b/hw/net/igbvf.c new file mode 100644 index 0000000000..94a4e885f2 --- /dev/null +++ b/hw/net/igbvf.c @@ -0,0 +1,339 @@ +/* + * QEMU Intel 82576 SR/IOV Ethernet Controller Emulation + * + * Datasheet: + * https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82576eg-gbe-datasheet.pdf + * + * Copyright (c) 2020-2023 Red Hat, Inc. + * Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Akihiko Odaki + * Gal Hammmer + * Marcel Apfelbaum + * Dmitry Fleytman + * Leonid Bloch + * Yan Vugenfirer + * + * Based on work done by: + * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. + * Copyright (c) 2008 Qumranet + * Based on work done by: + * Copyright (c) 2007 Dan Aloni + * Copyright (c) 2004 Antony T Curtis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "hw/hw.h" +#include "hw/net/mii.h" +#include "hw/pci/pci_device.h" +#include "hw/pci/pcie.h" +#include "hw/pci/msix.h" +#include "net/eth.h" +#include "net/net.h" +#include "igb_common.h" +#include "igb_core.h" +#include "trace.h" +#include "qapi/error.h" + +OBJECT_DECLARE_SIMPLE_TYPE(IgbVfState, IGBVF) + +struct IgbVfState { + PCIDevice parent_obj; + + MemoryRegion mmio; + MemoryRegion msix; +}; + +static hwaddr vf_to_pf_addr(hwaddr addr, uint16_t vfn, bool write) +{ + switch (addr) { + case E1000_CTRL: + case E1000_CTRL_DUP: + return E1000_PVTCTRL(vfn); + case E1000_EICS: + return E1000_PVTEICS(vfn); + case E1000_EIMS: + return E1000_PVTEIMS(vfn); + case E1000_EIMC: + return E1000_PVTEIMC(vfn); + case E1000_EIAC: + return E1000_PVTEIAC(vfn); + case E1000_EIAM: + return E1000_PVTEIAM(vfn); + case E1000_EICR: + return E1000_PVTEICR(vfn); + case E1000_EITR(0): + case E1000_EITR(1): + case E1000_EITR(2): + return E1000_EITR(22) + (addr - E1000_EITR(0)) - vfn * 0xC; + case E1000_IVAR0: + return E1000_VTIVAR + vfn * 4; + case E1000_IVAR_MISC: + return E1000_VTIVAR_MISC + vfn * 4; + case 0x0F04: /* PBACL */ + return E1000_PBACLR; + case 0x0F0C: /* PSRTYPE */ + return E1000_PSRTYPE(vfn); + case E1000_V2PMAILBOX(0): + return E1000_V2PMAILBOX(vfn); + case E1000_VMBMEM(0) ... E1000_VMBMEM(0) + 0x3F: + return addr + vfn * 0x40; + case E1000_RDBAL_A(0): + return E1000_RDBAL(vfn); + case E1000_RDBAL_A(1): + return E1000_RDBAL(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_RDBAH_A(0): + return E1000_RDBAH(vfn); + case E1000_RDBAH_A(1): + return E1000_RDBAH(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_RDLEN_A(0): + return E1000_RDLEN(vfn); + case E1000_RDLEN_A(1): + return E1000_RDLEN(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_SRRCTL_A(0): + return E1000_SRRCTL(vfn); + case E1000_SRRCTL_A(1): + return E1000_SRRCTL(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_RDH_A(0): + return E1000_RDH(vfn); + case E1000_RDH_A(1): + return E1000_RDH(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_RXCTL_A(0): + return E1000_RXCTL(vfn); + case E1000_RXCTL_A(1): + return E1000_RXCTL(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_RDT_A(0): + return E1000_RDT(vfn); + case E1000_RDT_A(1): + return E1000_RDT(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_RXDCTL_A(0): + return E1000_RXDCTL(vfn); + case E1000_RXDCTL_A(1): + return E1000_RXDCTL(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_RQDPC_A(0): + return E1000_RQDPC(vfn); + case E1000_RQDPC_A(1): + return E1000_RQDPC(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_TDBAL_A(0): + return E1000_TDBAL(vfn); + case E1000_TDBAL_A(1): + return E1000_TDBAL(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_TDBAH_A(0): + return E1000_TDBAH(vfn); + case E1000_TDBAH_A(1): + return E1000_TDBAH(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_TDLEN_A(0): + return E1000_TDLEN(vfn); + case E1000_TDLEN_A(1): + return E1000_TDLEN(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_TDH_A(0): + return E1000_TDH(vfn); + case E1000_TDH_A(1): + return E1000_TDH(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_TXCTL_A(0): + return E1000_TXCTL(vfn); + case E1000_TXCTL_A(1): + return E1000_TXCTL(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_TDT_A(0): + return E1000_TDT(vfn); + case E1000_TDT_A(1): + return E1000_TDT(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_TXDCTL_A(0): + return E1000_TXDCTL(vfn); + case E1000_TXDCTL_A(1): + return E1000_TXDCTL(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_TDWBAL_A(0): + return E1000_TDWBAL(vfn); + case E1000_TDWBAL_A(1): + return E1000_TDWBAL(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_TDWBAH_A(0): + return E1000_TDWBAH(vfn); + case E1000_TDWBAH_A(1): + return E1000_TDWBAH(vfn + IGB_MAX_VF_FUNCTIONS); + case E1000_VFGPRC: + return E1000_PVFGPRC(vfn); + case E1000_VFGPTC: + return E1000_PVFGPTC(vfn); + case E1000_VFGORC: + return E1000_PVFGORC(vfn); + case E1000_VFGOTC: + return E1000_PVFGOTC(vfn); + case E1000_VFMPRC: + return E1000_PVFMPRC(vfn); + case E1000_VFGPRLBC: + return E1000_PVFGPRLBC(vfn); + case E1000_VFGPTLBC: + return E1000_PVFGPTLBC(vfn); + case E1000_VFGORLBC: + return E1000_PVFGORLBC(vfn); + case E1000_VFGOTLBC: + return E1000_PVFGOTLBC(vfn); + case E1000_STATUS: + case E1000_FRTIMER: + if (write) { + return HWADDR_MAX; + } + /* fallthrough */ + case 0x34E8: /* PBTWAC */ + case 0x24E8: /* PBRWAC */ + return addr; + } + + trace_igbvf_wrn_io_addr_unknown(addr); + + return HWADDR_MAX; +} + +static void igbvf_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, + int len) +{ + trace_igbvf_write_config(addr, val, len); + pci_default_write_config(dev, addr, val, len); + if (object_property_get_bool(OBJECT(pcie_sriov_get_pf(dev)), + "x-pcie-flr-init", &error_abort)) { + pcie_cap_flr_write_config(dev, addr, val, len); + } +} + +static uint64_t igbvf_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + PCIDevice *vf = PCI_DEVICE(opaque); + PCIDevice *pf = pcie_sriov_get_pf(vf); + + addr = vf_to_pf_addr(addr, pcie_sriov_vf_number(vf), false); + return addr == HWADDR_MAX ? 0 : igb_mmio_read(pf, addr, size); +} + +static void igbvf_mmio_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + PCIDevice *vf = PCI_DEVICE(opaque); + PCIDevice *pf = pcie_sriov_get_pf(vf); + + addr = vf_to_pf_addr(addr, pcie_sriov_vf_number(vf), true); + if (addr != HWADDR_MAX) { + igb_mmio_write(pf, addr, val, size); + } +} + +static const MemoryRegionOps mmio_ops = { + .read = igbvf_mmio_read, + .write = igbvf_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void igbvf_pci_realize(PCIDevice *dev, Error **errp) +{ + IgbVfState *s = IGBVF(dev); + int ret; + int i; + + dev->config_write = igbvf_write_config; + + memory_region_init_io(&s->mmio, OBJECT(dev), &mmio_ops, s, "igbvf-mmio", + IGBVF_MMIO_SIZE); + pcie_sriov_vf_register_bar(dev, IGBVF_MMIO_BAR_IDX, &s->mmio); + + memory_region_init(&s->msix, OBJECT(dev), "igbvf-msix", IGBVF_MSIX_SIZE); + pcie_sriov_vf_register_bar(dev, IGBVF_MSIX_BAR_IDX, &s->msix); + + ret = msix_init(dev, IGBVF_MSIX_VEC_NUM, &s->msix, IGBVF_MSIX_BAR_IDX, 0, + &s->msix, IGBVF_MSIX_BAR_IDX, 0x2000, 0x70, errp); + if (ret) { + return; + } + + for (i = 0; i < IGBVF_MSIX_VEC_NUM; i++) { + msix_vector_use(dev, i); + } + + if (pcie_endpoint_cap_init(dev, 0xa0) < 0) { + hw_error("Failed to initialize PCIe capability"); + } + + if (object_property_get_bool(OBJECT(pcie_sriov_get_pf(dev)), + "x-pcie-flr-init", &error_abort)) { + pcie_cap_flr_init(dev); + } + + if (pcie_aer_init(dev, 1, 0x100, 0x40, errp) < 0) { + hw_error("Failed to initialize AER capability"); + } + + pcie_ari_init(dev, 0x150); +} + +static void igbvf_qdev_reset_hold(Object *obj) +{ + PCIDevice *vf = PCI_DEVICE(obj); + + igb_vf_reset(pcie_sriov_get_pf(vf), pcie_sriov_vf_number(vf)); +} + +static void igbvf_pci_uninit(PCIDevice *dev) +{ + IgbVfState *s = IGBVF(dev); + + pcie_aer_exit(dev); + pcie_cap_exit(dev); + msix_unuse_all_vectors(dev); + msix_uninit(dev, &s->msix, &s->msix); +} + +static void igbvf_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(class); + PCIDeviceClass *c = PCI_DEVICE_CLASS(class); + ResettableClass *rc = RESETTABLE_CLASS(class); + + c->realize = igbvf_pci_realize; + c->exit = igbvf_pci_uninit; + c->vendor_id = PCI_VENDOR_ID_INTEL; + c->device_id = E1000_DEV_ID_82576_VF; + c->revision = 1; + c->class_id = PCI_CLASS_NETWORK_ETHERNET; + + rc->phases.hold = igbvf_qdev_reset_hold; + + dc->desc = "Intel 82576 Virtual Function"; + dc->user_creatable = false; + + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); +} + +static const TypeInfo igbvf_info = { + .name = TYPE_IGBVF, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(IgbVfState), + .class_init = igbvf_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, +}; + +static void igb_register_types(void) +{ + type_register_static(&igbvf_info); +} + +type_init(igb_register_types) diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 0db9aaf76a..cee84af7ba 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -195,7 +195,7 @@ static const VMStateDescription vmstate_imx_eth_txdescs = { .version_id = 1, .minimum_version_id = 1, .needed = imx_eth_is_multi_tx_ring, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(tx_descriptor[1], IMXFECState), VMSTATE_UINT32(tx_descriptor[2], IMXFECState), VMSTATE_END_OF_LIST() @@ -206,7 +206,7 @@ static const VMStateDescription vmstate_imx_eth = { .name = TYPE_IMX_FEC, .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, IMXFECState, ENET_MAX), VMSTATE_UINT32(rx_descriptor, IMXFECState), VMSTATE_UINT32(tx_descriptor[0], IMXFECState), @@ -217,7 +217,7 @@ static const VMStateDescription vmstate_imx_eth = { VMSTATE_UINT32(phy_int_mask, IMXFECState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_imx_eth_txdescs, NULL }, @@ -282,11 +282,19 @@ static uint32_t imx_phy_read(IMXFECState *s, int reg) uint32_t val; uint32_t phy = reg / 32; - if (phy != s->phy_num) { - trace_imx_phy_read_num(phy, s->phy_num); + if (!s->phy_connected) { return 0xffff; } + if (phy != s->phy_num) { + if (s->phy_consumer && phy == s->phy_consumer->phy_num) { + s = s->phy_consumer; + } else { + trace_imx_phy_read_num(phy, s->phy_num); + return 0xffff; + } + } + reg %= 32; switch (reg) { @@ -343,11 +351,19 @@ static void imx_phy_write(IMXFECState *s, int reg, uint32_t val) { uint32_t phy = reg / 32; - if (phy != s->phy_num) { - trace_imx_phy_write_num(phy, s->phy_num); + if (!s->phy_connected) { return; } + if (phy != s->phy_num) { + if (s->phy_consumer && phy == s->phy_consumer->phy_num) { + s = s->phy_consumer; + } else { + trace_imx_phy_write_num(phy, s->phy_num); + return; + } + } + reg %= 32; trace_imx_phy_write(val, phy, reg); @@ -438,7 +454,7 @@ static void imx_eth_update(IMXFECState *s) * assignment fail. * * To ensure that all versions of Linux work, generate ENET_INT_MAC - * interrrupts on both interrupt lines. This should be changed if and when + * interrupts on both interrupt lines. This should be changed if and when * qemu supports IOMUX. */ if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] & @@ -1068,9 +1084,9 @@ static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf, return 0; } - /* 4 bytes for the CRC. */ - size += 4; crc = cpu_to_be32(crc32(~0, buf, size)); + /* Increase size by 4, loop below reads the last 4 bytes from crc_ptr. */ + size += 4; crc_ptr = (uint8_t *) &crc; /* Huge frames are truncated. */ @@ -1164,9 +1180,9 @@ static ssize_t imx_enet_receive(NetClientState *nc, const uint8_t *buf, return 0; } - /* 4 bytes for the CRC. */ - size += 4; crc = cpu_to_be32(crc32(~0, buf, size)); + /* Increase size by 4, loop below reads the last 4 bytes from crc_ptr. */ + size += 4; crc_ptr = (uint8_t *) &crc; if (shift16) { @@ -1318,7 +1334,7 @@ static void imx_eth_realize(DeviceState *dev, Error **errp) s->nic = qemu_new_nic(&imx_eth_net_info, &s->conf, object_get_typename(OBJECT(dev)), - dev->id, s); + dev->id, &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } @@ -1327,6 +1343,9 @@ static Property imx_eth_properties[] = { DEFINE_NIC_PROPERTIES(IMXFECState, conf), DEFINE_PROP_UINT32("tx-ring-num", IMXFECState, tx_ring_num, 1), DEFINE_PROP_UINT32("phy-num", IMXFECState, phy_num, 0), + DEFINE_PROP_BOOL("phy-connected", IMXFECState, phy_connected, true), + DEFINE_PROP_LINK("phy-consumer", IMXFECState, phy_consumer, TYPE_IMX_FEC, + IMXFECState *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index 456ae38107..91d81b410b 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -15,7 +15,6 @@ #include "migration/vmstate.h" #include "net/net.h" #include "net/eth.h" -#include "hw/hw.h" #include "hw/irq.h" #include "hw/net/lan9118.h" #include "hw/ptimer.h" @@ -32,12 +31,8 @@ #ifdef DEBUG_LAN9118 #define DPRINTF(fmt, ...) \ do { printf("lan9118: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { hw_error("lan9118: error: " fmt , ## __VA_ARGS__);} while (0) #else #define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0) #endif /* The tx and rx fifo ports are a range of aliased 32-bit registers */ @@ -155,6 +150,12 @@ do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0) #define GPT_TIMER_EN 0x20000000 +/* + * The MAC Interface Layer (MIL), within the MAC, contains a 2K Byte transmit + * and a 128 Byte receive FIFO which is separate from the TX and RX FIFOs. + */ +#define MIL_TXFIFO_SIZE 2048 + enum tx_state { TX_IDLE, TX_B, @@ -171,14 +172,14 @@ typedef struct { int32_t pad; int32_t fifo_used; int32_t len; - uint8_t data[2048]; + uint8_t data[MIL_TXFIFO_SIZE]; } LAN9118Packet; static const VMStateDescription vmstate_lan9118_packet = { .name = "lan9118_packet", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(state, LAN9118Packet), VMSTATE_UINT32(cmd_a, LAN9118Packet), VMSTATE_UINT32(cmd_b, LAN9118Packet), @@ -187,7 +188,7 @@ static const VMStateDescription vmstate_lan9118_packet = { VMSTATE_INT32(pad, LAN9118Packet), VMSTATE_INT32(fifo_used, LAN9118Packet), VMSTATE_INT32(len, LAN9118Packet), - VMSTATE_UINT8_ARRAY(data, LAN9118Packet, 2048), + VMSTATE_UINT8_ARRAY(data, LAN9118Packet, MIL_TXFIFO_SIZE), VMSTATE_END_OF_LIST() } }; @@ -276,7 +277,7 @@ static const VMStateDescription vmstate_lan9118 = { .name = "lan9118", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PTIMER(timer, lan9118_state), VMSTATE_UINT32(irq_cfg, lan9118_state), VMSTATE_UINT32(int_sts, lan9118_state), @@ -549,7 +550,7 @@ static ssize_t lan9118_receive(NetClientState *nc, const uint8_t *buf, return -1; } - if (size >= 2048 || size < 14) { + if (size >= MIL_TXFIFO_SIZE || size < 14) { return -1; } @@ -696,6 +697,14 @@ static void do_tx_packet(lan9118_state *s) n = (s->tx_status_fifo_head + s->tx_status_fifo_used) & 511; s->tx_status_fifo[n] = status; s->tx_status_fifo_used++; + + /* + * Generate TSFL interrupt if TX FIFO level exceeds the level + * specified in the FIFO_INT TX Status Level field. + */ + if (s->tx_status_fifo_used > ((s->fifo_int >> 16) & 0xff)) { + s->int_sts |= TSFL_INT; + } if (s->tx_status_fifo_used == 512) { s->int_sts |= TSFF_INT; /* TODO: Stop transmission. */ @@ -790,8 +799,22 @@ static void tx_fifo_push(lan9118_state *s, uint32_t val) /* Documentation is somewhat unclear on the ordering of bytes in FIFO words. Empirical results show it to be little-endian. */ - /* TODO: FIFO overflow checking. */ while (n--) { + if (s->txp->len == MIL_TXFIFO_SIZE) { + /* + * No more space in the FIFO. The datasheet is not + * precise about this case. We choose what is easiest + * to model: the packet is truncated, and TXE is raised. + * + * Note, it could be a fragmented packet, but we currently + * do not handle that (see earlier TX_B case). + */ + qemu_log_mask(LOG_GUEST_ERROR, + "MIL TX FIFO overrun, discarding %u byte%s\n", + n, n > 1 ? "s" : ""); + s->int_sts |= TXE_INT; + break; + } s->txp->data[s->txp->len] = val & 0xff; s->txp->len++; val >>= 8; @@ -840,7 +863,8 @@ static uint32_t do_phy_read(lan9118_state *s, int reg) case 30: /* Interrupt mask */ return s->phy_int_mask; default: - BADF("PHY read reg %d\n", reg); + qemu_log_mask(LOG_GUEST_ERROR, + "do_phy_read: PHY read reg %d\n", reg); return 0; } } @@ -868,7 +892,8 @@ static void do_phy_write(lan9118_state *s, int reg, uint32_t val) phy_update_irq(s); break; default: - BADF("PHY write reg %d = 0x%04x\n", reg, val); + qemu_log_mask(LOG_GUEST_ERROR, + "do_phy_write: PHY write reg %d = 0x%04x\n", reg, val); } } @@ -1201,7 +1226,8 @@ static void lan9118_16bit_mode_write(void *opaque, hwaddr offset, return; } - hw_error("lan9118_write: Bad size 0x%x\n", size); + qemu_log_mask(LOG_GUEST_ERROR, + "lan9118_16bit_mode_write: Bad size 0x%x\n", size); } static uint64_t lan9118_readl(void *opaque, hwaddr offset, @@ -1316,7 +1342,8 @@ static uint64_t lan9118_16bit_mode_read(void *opaque, hwaddr offset, return lan9118_readl(opaque, offset, size); } - hw_error("lan9118_read: Bad size 0x%x\n", size); + qemu_log_mask(LOG_GUEST_ERROR, + "lan9118_16bit_mode_read: Bad size 0x%x\n", size); return 0; } @@ -1354,7 +1381,8 @@ static void lan9118_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_lan9118_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->eeprom[0] = 0xa5; for (i = 0; i < 6; i++) { @@ -1400,14 +1428,13 @@ static void lan9118_register_types(void) /* Legacy helper function. Should go away when machine config files are implemented. */ -void lan9118_init(NICInfo *nd, uint32_t base, qemu_irq irq) +void lan9118_init(uint32_t base, qemu_irq irq) { DeviceState *dev; SysBusDevice *s; - qemu_check_nic_model(nd, "lan9118"); dev = qdev_new(TYPE_LAN9118); - qdev_set_nic_properties(dev, nd); + qemu_configure_nic_device(dev, true, NULL); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); sysbus_mmio_map(s, 0, base); diff --git a/hw/net/lance.c b/hw/net/lance.c index 4c5f01baad..e1ed24c2ce 100644 --- a/hw/net/lance.c +++ b/hw/net/lance.c @@ -94,7 +94,7 @@ static const VMStateDescription vmstate_lance = { .name = "pcnet", .version_id = 3, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(state, SysBusPCNetState, 0, vmstate_pcnet, PCNetState), VMSTATE_END_OF_LIST() } diff --git a/hw/net/lasi_i82596.c b/hw/net/lasi_i82596.c index e37f7fabe9..fcf7fae941 100644 --- a/hw/net/lasi_i82596.c +++ b/hw/net/lasi_i82596.c @@ -14,6 +14,7 @@ #include "qapi/error.h" #include "qemu/timer.h" #include "hw/sysbus.h" +#include "sysemu/sysemu.h" #include "net/eth.h" #include "hw/net/lasi_82596.h" #include "hw/net/i82596.h" @@ -99,7 +100,7 @@ static const VMStateDescription vmstate_lasi_82596 = { .name = "i82596", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(state, SysBusI82596State, 0, vmstate_i82596, I82596State), VMSTATE_END_OF_LIST() @@ -117,19 +118,21 @@ static void lasi_82596_realize(DeviceState *dev, Error **errp) i82596_common_init(dev, s, &net_lasi_82596_info); } -SysBusI82596State *lasi_82596_init(MemoryRegion *addr_space, - hwaddr hpa, qemu_irq lan_irq) +SysBusI82596State *lasi_82596_init(MemoryRegion *addr_space, hwaddr hpa, + qemu_irq lan_irq, gboolean match_default) { 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_new(TYPE_LASI_82596); + dev = qemu_create_nic_device(TYPE_LASI_82596, match_default, "lasi"); + if (!dev) { + return NULL; + } + s = SYSBUS_I82596(dev); s->state.irq = lan_irq; - qdev_set_nic_properties(dev, &nd_table[0]); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); s->state.conf.macaddr = HP_MAC; /* set HP MAC prefix */ diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c index 25e3e453ab..e6902716bd 100644 --- a/hw/net/mcf_fec.c +++ b/hw/net/mcf_fec.c @@ -313,10 +313,10 @@ static void mcf_fec_reset(DeviceState *dev) s->rfsr = 0x500; } -#define MMFR_WRITE_OP (1 << 28) -#define MMFR_READ_OP (2 << 28) -#define MMFR_PHYADDR(v) (((v) >> 23) & 0x1f) -#define MMFR_REGNUM(v) (((v) >> 18) & 0x1f) +#define MMFR_WRITE_OP (1 << 28) +#define MMFR_READ_OP (2 << 28) +#define MMFR_PHYADDR(v) (((v) >> 23) & 0x1f) +#define MMFR_REGNUM(v) (((v) >> 18) & 0x1f) static uint64_t mcf_fec_read_mdio(mcf_fec_state *s) { @@ -571,7 +571,7 @@ static ssize_t mcf_fec_receive(NetClientState *nc, const uint8_t *buf, size_t si size += 4; crc = cpu_to_be32(crc32(~0, buf, size)); crc_ptr = (uint8_t *)&crc; - /* Huge frames are truncted. */ + /* Huge frames are truncated. */ if (size > FEC_MAX_FRAME_SIZE) { size = FEC_MAX_FRAME_SIZE; flags |= FEC_BD_TR | FEC_BD_LG; @@ -643,7 +643,8 @@ static void mcf_fec_realize(DeviceState *dev, Error **errp) mcf_fec_state *s = MCF_FEC_NET(dev); s->nic = qemu_new_nic(&net_mcf_fec_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/net/meson.build b/hw/net/meson.build index ebac261542..b7426870e8 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -1,72 +1,73 @@ -softmmu_ss.add(when: 'CONFIG_DP8393X', if_true: files('dp8393x.c')) -softmmu_ss.add(when: 'CONFIG_XEN', if_true: files('xen_nic.c')) -softmmu_ss.add(when: 'CONFIG_NE2000_COMMON', if_true: files('ne2000.c')) +system_ss.add(when: 'CONFIG_DP8393X', if_true: files('dp8393x.c')) +system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen_nic.c')) +system_ss.add(when: 'CONFIG_NE2000_COMMON', if_true: files('ne2000.c')) # PCI network cards -softmmu_ss.add(when: 'CONFIG_NE2000_PCI', if_true: files('ne2000-pci.c')) -softmmu_ss.add(when: 'CONFIG_EEPRO100_PCI', if_true: files('eepro100.c')) -softmmu_ss.add(when: 'CONFIG_PCNET_PCI', if_true: files('pcnet-pci.c')) -softmmu_ss.add(when: 'CONFIG_PCNET_COMMON', if_true: files('pcnet.c')) -softmmu_ss.add(when: 'CONFIG_E1000_PCI', if_true: files('e1000.c', 'e1000x_common.c')) -softmmu_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) -softmmu_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('e1000e.c', 'e1000e_core.c', 'e1000x_common.c')) -softmmu_ss.add(when: 'CONFIG_RTL8139_PCI', if_true: files('rtl8139.c')) -softmmu_ss.add(when: 'CONFIG_TULIP', if_true: files('tulip.c')) -softmmu_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) -softmmu_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('vmxnet3.c')) +system_ss.add(when: 'CONFIG_NE2000_PCI', if_true: files('ne2000-pci.c')) +system_ss.add(when: 'CONFIG_EEPRO100_PCI', if_true: files('eepro100.c')) +system_ss.add(when: 'CONFIG_PCNET_PCI', if_true: files('pcnet-pci.c')) +system_ss.add(when: 'CONFIG_PCNET_COMMON', if_true: files('pcnet.c')) +system_ss.add(when: 'CONFIG_E1000_PCI', if_true: files('e1000.c', 'e1000x_common.c')) +system_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) +system_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('e1000e.c', 'e1000e_core.c', 'e1000x_common.c')) +system_ss.add(when: 'CONFIG_IGB_PCI_EXPRESS', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) +system_ss.add(when: 'CONFIG_IGB_PCI_EXPRESS', if_true: files('igb.c', 'igbvf.c', 'igb_core.c')) +system_ss.add(when: 'CONFIG_RTL8139_PCI', if_true: files('rtl8139.c')) +system_ss.add(when: 'CONFIG_TULIP', if_true: files('tulip.c')) +system_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) +system_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('vmxnet3.c')) -softmmu_ss.add(when: 'CONFIG_SMC91C111', if_true: files('smc91c111.c')) -softmmu_ss.add(when: 'CONFIG_LAN9118', if_true: files('lan9118.c')) -softmmu_ss.add(when: 'CONFIG_NE2000_ISA', if_true: files('ne2000-isa.c')) -softmmu_ss.add(when: 'CONFIG_OPENCORES_ETH', if_true: files('opencores_eth.c')) -softmmu_ss.add(when: 'CONFIG_XGMAC', if_true: files('xgmac.c')) -softmmu_ss.add(when: 'CONFIG_MIPSNET', if_true: files('mipsnet.c')) -softmmu_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('xilinx_axienet.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_EMAC', if_true: files('allwinner_emac.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_SUN8I_EMAC', if_true: files('allwinner-sun8i-emac.c')) -softmmu_ss.add(when: 'CONFIG_IMX_FEC', if_true: files('imx_fec.c')) -softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-emac.c')) -softmmu_ss.add(when: 'CONFIG_MARVELL_88W8618', if_true: files('mv88w8618_eth.c')) +system_ss.add(when: 'CONFIG_SMC91C111', if_true: files('smc91c111.c')) +system_ss.add(when: 'CONFIG_LAN9118', if_true: files('lan9118.c')) +system_ss.add(when: 'CONFIG_NE2000_ISA', if_true: files('ne2000-isa.c')) +system_ss.add(when: 'CONFIG_OPENCORES_ETH', if_true: files('opencores_eth.c')) +system_ss.add(when: 'CONFIG_XGMAC', if_true: files('xgmac.c')) +system_ss.add(when: 'CONFIG_MIPSNET', if_true: files('mipsnet.c')) +system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('xilinx_axienet.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_EMAC', if_true: files('allwinner_emac.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_SUN8I_EMAC', if_true: files('allwinner-sun8i-emac.c')) +system_ss.add(when: 'CONFIG_IMX_FEC', if_true: files('imx_fec.c')) +system_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-emac.c')) +system_ss.add(when: 'CONFIG_MARVELL_88W8618', if_true: files('mv88w8618_eth.c')) -softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_gem.c')) -softmmu_ss.add(when: 'CONFIG_STELLARIS_ENET', if_true: files('stellaris_enet.c')) -softmmu_ss.add(when: 'CONFIG_LANCE', if_true: files('lance.c')) -softmmu_ss.add(when: 'CONFIG_LASI_I82596', if_true: files('lasi_i82596.c')) -softmmu_ss.add(when: 'CONFIG_I82596_COMMON', if_true: files('i82596.c')) -softmmu_ss.add(when: 'CONFIG_SUNHME', if_true: files('sunhme.c')) -softmmu_ss.add(when: 'CONFIG_FTGMAC100', if_true: files('ftgmac100.c')) -softmmu_ss.add(when: 'CONFIG_SUNGEM', if_true: files('sungem.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_emc.c')) +system_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_gem.c')) +system_ss.add(when: 'CONFIG_STELLARIS_ENET', if_true: files('stellaris_enet.c')) +system_ss.add(when: 'CONFIG_LANCE', if_true: files('lance.c')) +system_ss.add(when: 'CONFIG_LASI_82596', if_true: files('lasi_i82596.c')) +system_ss.add(when: 'CONFIG_I82596_COMMON', if_true: files('i82596.c')) +system_ss.add(when: 'CONFIG_SUNHME', if_true: files('sunhme.c')) +system_ss.add(when: 'CONFIG_FTGMAC100', if_true: files('ftgmac100.c')) +system_ss.add(when: 'CONFIG_SUNGEM', if_true: files('sungem.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_emc.c', 'npcm_gmac.c')) -softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_eth.c')) -softmmu_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_fec.c')) +system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_eth.c')) +system_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_fec.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_llan.c')) -specific_ss.add(when: 'CONFIG_XILINX_ETHLITE', if_true: files('xilinx_ethlite.c')) +system_ss.add(when: 'CONFIG_XILINX_ETHLITE', if_true: files('xilinx_ethlite.c')) -softmmu_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('net_rx_pkt.c')) +system_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('net_rx_pkt.c')) specific_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('virtio-net.c')) if have_vhost_net - softmmu_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost_net.c'), if_false: files('vhost_net-stub.c')) - softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost_net-stub.c')) + system_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost_net.c'), if_false: files('vhost_net-stub.c')) else - softmmu_ss.add(files('vhost_net-stub.c')) + system_ss.add(files('vhost_net-stub.c')) endif -softmmu_ss.add(when: 'CONFIG_ETSEC', if_true: files( +system_ss.add(when: 'CONFIG_ETSEC', if_true: files( 'fsl_etsec/etsec.c', 'fsl_etsec/miim.c', 'fsl_etsec/registers.c', 'fsl_etsec/rings.c', )) -softmmu_ss.add(when: 'CONFIG_ROCKER', if_true: files( +system_ss.add(when: 'CONFIG_ROCKER', if_true: files( 'rocker/rocker.c', 'rocker/rocker_desc.c', 'rocker/rocker_fp.c', 'rocker/rocker_of_dpa.c', 'rocker/rocker_world.c', ), if_false: files('rocker/qmp-norocker.c')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('rocker/qmp-norocker.c')) +system_ss.add(files('rocker/rocker-hmp-cmds.c')) subdir('can') diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c index 2ade72dea0..df5101aed7 100644 --- a/hw/net/mipsnet.c +++ b/hw/net/mipsnet.c @@ -218,7 +218,7 @@ static const VMStateDescription vmstate_mipsnet = { .name = "mipsnet", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(busy, MIPSnetState), VMSTATE_UINT32(rx_count, MIPSnetState), VMSTATE_UINT32(rx_read, MIPSnetState), @@ -255,7 +255,8 @@ static void mipsnet_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->irq); s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/net/msf2-emac.c b/hw/net/msf2-emac.c index 7ccd3e5142..c1fc10de2a 100644 --- a/hw/net/msf2-emac.c +++ b/hw/net/msf2-emac.c @@ -118,14 +118,18 @@ static void emac_load_desc(MSF2EmacState *s, EmacDesc *d, hwaddr desc) d->next = le32_to_cpu(d->next); } -static void emac_store_desc(MSF2EmacState *s, EmacDesc *d, hwaddr desc) +static void emac_store_desc(MSF2EmacState *s, const EmacDesc *d, hwaddr desc) { - /* Convert from host endianness into LE. */ - d->pktaddr = cpu_to_le32(d->pktaddr); - d->pktsize = cpu_to_le32(d->pktsize); - d->next = cpu_to_le32(d->next); + EmacDesc outd; + /* + * Convert from host endianness into LE. We use a local struct because + * calling code may still want to look at the fields afterwards. + */ + outd.pktaddr = cpu_to_le32(d->pktaddr); + outd.pktsize = cpu_to_le32(d->pktsize); + outd.next = cpu_to_le32(d->next); - address_space_write(&s->dma_as, desc, MEMTXATTRS_UNSPECIFIED, d, sizeof *d); + address_space_write(&s->dma_as, desc, MEMTXATTRS_UNSPECIFIED, &outd, sizeof outd); } static void msf2_dma_tx(MSF2EmacState *s) @@ -526,7 +530,8 @@ static void msf2_emac_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_msf2_emac_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } @@ -552,7 +557,7 @@ static const VMStateDescription vmstate_msf2_emac = { .name = TYPE_MSS_EMAC, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(mac_addr, MSF2EmacState, ETH_ALEN), VMSTATE_UINT32(rx_desc, MSF2EmacState), VMSTATE_UINT16_ARRAY(phy_regs, MSF2EmacState, PHY_MAX_REGS), diff --git a/hw/net/mv88w8618_eth.c b/hw/net/mv88w8618_eth.c index ef30b0d4a6..96c65f4d46 100644 --- a/hw/net/mv88w8618_eth.c +++ b/hw/net/mv88w8618_eth.c @@ -350,14 +350,15 @@ static void mv88w8618_eth_realize(DeviceState *dev, Error **errp) address_space_init(&s->dma_as, s->dma_mr, "emac-dma"); s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); } static const VMStateDescription mv88w8618_eth_vmsd = { .name = "mv88w8618_eth", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(smir, mv88w8618_eth_state), VMSTATE_UINT32(icr, mv88w8618_eth_state), VMSTATE_UINT32(imr, mv88w8618_eth_state), diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index 6ced6775ff..26980e087e 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -53,7 +53,7 @@ static const VMStateDescription vmstate_isa_ne2000 = { .name = "ne2000", .version_id = 2, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(ne2000, ISANE2000State, 0, vmstate_ne2000, NE2000State), VMSTATE_END_OF_LIST() } @@ -74,7 +74,8 @@ static void isa_ne2000_realizefn(DeviceState *dev, Error **errp) ne2000_reset(s); s->nic = qemu_new_nic(&net_ne2000_isa_info, &s->c, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); } diff --git a/hw/net/ne2000-pci.c b/hw/net/ne2000-pci.c index 9e5d10859a..74773069c6 100644 --- a/hw/net/ne2000-pci.c +++ b/hw/net/ne2000-pci.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "ne2000.h" @@ -39,7 +39,7 @@ static const VMStateDescription vmstate_pci_ne2000 = { .name = "ne2000", .version_id = 3, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, PCINE2000State), VMSTATE_STRUCT(ne2000, PCINE2000State, 0, vmstate_ne2000, NE2000State), VMSTATE_END_OF_LIST() @@ -71,7 +71,8 @@ static void pci_ne2000_realize(PCIDevice *pci_dev, Error **errp) s->nic = qemu_new_nic(&net_ne2000_info, &s->c, object_get_typename(OBJECT(pci_dev)), - pci_dev->qdev.id, s); + pci_dev->qdev.id, + &pci_dev->qdev.mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); } diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index 6c17ee1ae2..b482c5f3af 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -36,89 +36,89 @@ #define MAX_ETH_FRAME_SIZE 1514 -#define E8390_CMD 0x00 /* The command register (for all pages) */ +#define E8390_CMD 0x00 /* The command register (for all pages) */ /* Page 0 register offsets. */ -#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */ -#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */ -#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */ -#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */ -#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */ -#define EN0_TSR 0x04 /* Transmit status reg RD */ -#define EN0_TPSR 0x04 /* Transmit starting page WR */ -#define EN0_NCR 0x05 /* Number of collision reg RD */ -#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */ -#define EN0_FIFO 0x06 /* FIFO RD */ -#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */ -#define EN0_ISR 0x07 /* Interrupt status reg RD WR */ -#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */ -#define EN0_RSARLO 0x08 /* Remote start address reg 0 */ -#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */ -#define EN0_RSARHI 0x09 /* Remote start address reg 1 */ -#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */ -#define EN0_RTL8029ID0 0x0a /* Realtek ID byte #1 RD */ -#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */ -#define EN0_RTL8029ID1 0x0b /* Realtek ID byte #2 RD */ -#define EN0_RSR 0x0c /* rx status reg RD */ -#define EN0_RXCR 0x0c /* RX configuration reg WR */ -#define EN0_TXCR 0x0d /* TX configuration reg WR */ -#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */ -#define EN0_DCFG 0x0e /* Data configuration reg WR */ -#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */ -#define EN0_IMR 0x0f /* Interrupt mask reg WR */ -#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */ +#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */ +#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */ +#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */ +#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */ +#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */ +#define EN0_TSR 0x04 /* Transmit status reg RD */ +#define EN0_TPSR 0x04 /* Transmit starting page WR */ +#define EN0_NCR 0x05 /* Number of collision reg RD */ +#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */ +#define EN0_FIFO 0x06 /* FIFO RD */ +#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */ +#define EN0_ISR 0x07 /* Interrupt status reg RD WR */ +#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */ +#define EN0_RSARLO 0x08 /* Remote start address reg 0 */ +#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */ +#define EN0_RSARHI 0x09 /* Remote start address reg 1 */ +#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */ +#define EN0_RTL8029ID0 0x0a /* Realtek ID byte #1 RD */ +#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */ +#define EN0_RTL8029ID1 0x0b /* Realtek ID byte #2 RD */ +#define EN0_RSR 0x0c /* rx status reg RD */ +#define EN0_RXCR 0x0c /* RX configuration reg WR */ +#define EN0_TXCR 0x0d /* TX configuration reg WR */ +#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */ +#define EN0_DCFG 0x0e /* Data configuration reg WR */ +#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */ +#define EN0_IMR 0x0f /* Interrupt mask reg WR */ +#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */ #define EN1_PHYS 0x11 #define EN1_CURPAG 0x17 #define EN1_MULT 0x18 -#define EN2_STARTPG 0x21 /* Starting page of ring bfr RD */ -#define EN2_STOPPG 0x22 /* Ending page +1 of ring bfr RD */ +#define EN2_STARTPG 0x21 /* Starting page of ring bfr RD */ +#define EN2_STOPPG 0x22 /* Ending page +1 of ring bfr RD */ -#define EN3_CONFIG0 0x33 -#define EN3_CONFIG1 0x34 -#define EN3_CONFIG2 0x35 -#define EN3_CONFIG3 0x36 +#define EN3_CONFIG0 0x33 +#define EN3_CONFIG1 0x34 +#define EN3_CONFIG2 0x35 +#define EN3_CONFIG3 0x36 /* Register accessed at EN_CMD, the 8390 base addr. */ -#define E8390_STOP 0x01 /* Stop and reset the chip */ -#define E8390_START 0x02 /* Start the chip, clear reset */ -#define E8390_TRANS 0x04 /* Transmit a frame */ -#define E8390_RREAD 0x08 /* Remote read */ -#define E8390_RWRITE 0x10 /* Remote write */ -#define E8390_NODMA 0x20 /* Remote DMA */ -#define E8390_PAGE0 0x00 /* Select page chip registers */ -#define E8390_PAGE1 0x40 /* using the two high-order bits */ -#define E8390_PAGE2 0x80 /* Page 3 is invalid. */ +#define E8390_STOP 0x01 /* Stop and reset the chip */ +#define E8390_START 0x02 /* Start the chip, clear reset */ +#define E8390_TRANS 0x04 /* Transmit a frame */ +#define E8390_RREAD 0x08 /* Remote read */ +#define E8390_RWRITE 0x10 /* Remote write */ +#define E8390_NODMA 0x20 /* Remote DMA */ +#define E8390_PAGE0 0x00 /* Select page chip registers */ +#define E8390_PAGE1 0x40 /* using the two high-order bits */ +#define E8390_PAGE2 0x80 /* Page 3 is invalid. */ /* Bits in EN0_ISR - Interrupt status register */ -#define ENISR_RX 0x01 /* Receiver, no error */ -#define ENISR_TX 0x02 /* Transmitter, no error */ -#define ENISR_RX_ERR 0x04 /* Receiver, with error */ -#define ENISR_TX_ERR 0x08 /* Transmitter, with error */ -#define ENISR_OVER 0x10 /* Receiver overwrote the ring */ -#define ENISR_COUNTERS 0x20 /* Counters need emptying */ -#define ENISR_RDC 0x40 /* remote dma complete */ -#define ENISR_RESET 0x80 /* Reset completed */ -#define ENISR_ALL 0x3f /* Interrupts we will enable */ +#define ENISR_RX 0x01 /* Receiver, no error */ +#define ENISR_TX 0x02 /* Transmitter, no error */ +#define ENISR_RX_ERR 0x04 /* Receiver, with error */ +#define ENISR_TX_ERR 0x08 /* Transmitter, with error */ +#define ENISR_OVER 0x10 /* Receiver overwrote the ring */ +#define ENISR_COUNTERS 0x20 /* Counters need emptying */ +#define ENISR_RDC 0x40 /* remote dma complete */ +#define ENISR_RESET 0x80 /* Reset completed */ +#define ENISR_ALL 0x3f /* Interrupts we will enable */ /* Bits in received packet status byte and EN0_RSR*/ -#define ENRSR_RXOK 0x01 /* Received a good packet */ -#define ENRSR_CRC 0x02 /* CRC error */ -#define ENRSR_FAE 0x04 /* frame alignment error */ -#define ENRSR_FO 0x08 /* FIFO overrun */ -#define ENRSR_MPA 0x10 /* missed pkt */ -#define ENRSR_PHY 0x20 /* physical/multicast address */ -#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */ -#define ENRSR_DEF 0x80 /* deferring */ +#define ENRSR_RXOK 0x01 /* Received a good packet */ +#define ENRSR_CRC 0x02 /* CRC error */ +#define ENRSR_FAE 0x04 /* frame alignment error */ +#define ENRSR_FO 0x08 /* FIFO overrun */ +#define ENRSR_MPA 0x10 /* missed pkt */ +#define ENRSR_PHY 0x20 /* physical/multicast address */ +#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */ +#define ENRSR_DEF 0x80 /* deferring */ /* Transmitted packet status, EN0_TSR. */ -#define ENTSR_PTX 0x01 /* Packet transmitted without error */ -#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */ -#define ENTSR_COL 0x04 /* The transmit collided at least once. */ +#define ENTSR_PTX 0x01 /* Packet transmitted without error */ +#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */ +#define ENTSR_COL 0x04 /* The transmit collided at least once. */ #define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */ -#define ENTSR_CRS 0x10 /* The carrier sense was lost. */ +#define ENTSR_CRS 0x10 /* The carrier sense was lost. */ #define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */ -#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */ +#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */ #define ENTSR_OWC 0x80 /* There was an out-of-window collision. */ void ne2000_reset(NE2000State *s) @@ -167,15 +167,12 @@ static int ne2000_buffer_full(NE2000State *s) return 0; } -#define MIN_BUF_SIZE 60 - ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_) { NE2000State *s = qemu_get_nic_opaque(nc); size_t size = size_; uint8_t *p; unsigned int total_len, next, avail, len, index, mcast_idx; - uint8_t buf1[60]; static const uint8_t broadcast_macaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -213,15 +210,6 @@ ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_) } } - - /* if too small buffer, then expand it */ - if (size < MIN_BUF_SIZE) { - memcpy(buf1, buf, size); - memset(buf1 + size, 0, MIN_BUF_SIZE - size); - buf = buf1; - size = MIN_BUF_SIZE; - } - index = s->curpag << 8; if (index >= NE2000_PMEM_END) { index = s->start; @@ -425,13 +413,13 @@ static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr) ret = 0x43; break; case EN3_CONFIG0: - ret = 0; /* 10baseT media */ + ret = 0; /* 10baseT media */ break; case EN3_CONFIG2: - ret = 0x40; /* 10baseT active */ + ret = 0x40; /* 10baseT active */ break; case EN3_CONFIG3: - ret = 0x40; /* Full duplex */ + ret = 0x40; /* Full duplex */ break; default: ret = 0x00; @@ -618,7 +606,7 @@ const VMStateDescription vmstate_ne2000 = { .version_id = 2, .minimum_version_id = 0, .post_load = ne2000_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_V(rxcr, NE2000State, 2), VMSTATE_UINT8(cmd, NE2000State), VMSTATE_UINT32(start, NE2000State), diff --git a/hw/net/net_rx_pkt.c b/hw/net/net_rx_pkt.c index 1e1c504e42..32e5f3f9cf 100644 --- a/hw/net/net_rx_pkt.c +++ b/hw/net/net_rx_pkt.c @@ -16,6 +16,7 @@ */ #include "qemu/osdep.h" +#include "qemu/crc32c.h" #include "trace.h" #include "net_rx_pkt.h" #include "net/checksum.h" @@ -23,21 +24,21 @@ struct NetRxPkt { struct virtio_net_hdr virt_hdr; - uint8_t ehdr_buf[sizeof(struct eth_header) + sizeof(struct vlan_header)]; + struct { + struct eth_header eth; + struct vlan_header vlan; + } ehdr_buf; struct iovec *vec; uint16_t vec_len_total; uint16_t vec_len; uint32_t tot_len; uint16_t tci; size_t ehdr_buf_len; - bool has_virt_hdr; eth_pkt_types_e packet_type; /* Analysis results */ - bool isip4; - bool isip6; - bool isudp; - bool istcp; + bool hasip4; + bool hasip6; size_t l3hdr_off; size_t l4hdr_off; @@ -48,10 +49,9 @@ struct NetRxPkt { eth_l4_hdr_info l4hdr_info; }; -void net_rx_pkt_init(struct NetRxPkt **pkt, bool has_virt_hdr) +void net_rx_pkt_init(struct NetRxPkt **pkt) { struct NetRxPkt *p = g_malloc0(sizeof *p); - p->has_virt_hdr = has_virt_hdr; p->vec = NULL; p->vec_len_total = 0; *pkt = p; @@ -93,7 +93,7 @@ net_rx_pkt_pull_data(struct NetRxPkt *pkt, if (pkt->ehdr_buf_len) { net_rx_pkt_iovec_realloc(pkt, iovcnt + 1); - pkt->vec[0].iov_base = pkt->ehdr_buf; + pkt->vec[0].iov_base = &pkt->ehdr_buf; pkt->vec[0].iov_len = pkt->ehdr_buf_len; pkt->tot_len = pllen + pkt->ehdr_buf_len; @@ -107,12 +107,11 @@ net_rx_pkt_pull_data(struct NetRxPkt *pkt, iov, iovcnt, ploff, pkt->tot_len); } - eth_get_protocols(pkt->vec, pkt->vec_len, &pkt->isip4, &pkt->isip6, - &pkt->isudp, &pkt->istcp, + eth_get_protocols(pkt->vec, pkt->vec_len, 0, &pkt->hasip4, &pkt->hasip6, &pkt->l3hdr_off, &pkt->l4hdr_off, &pkt->l5hdr_off, &pkt->ip6hdr_info, &pkt->ip4hdr_info, &pkt->l4hdr_info); - trace_net_rx_pkt_parsed(pkt->isip4, pkt->isip6, pkt->isudp, pkt->istcp, + trace_net_rx_pkt_parsed(pkt->hasip4, pkt->hasip6, pkt->l4hdr_info.proto, pkt->l3hdr_off, pkt->l4hdr_off, pkt->l5hdr_off); } @@ -125,7 +124,7 @@ void net_rx_pkt_attach_iovec(struct NetRxPkt *pkt, assert(pkt); if (strip_vlan) { - pkt->ehdr_buf_len = eth_strip_vlan(iov, iovcnt, iovoff, pkt->ehdr_buf, + pkt->ehdr_buf_len = eth_strip_vlan(iov, iovcnt, iovoff, &pkt->ehdr_buf, &ploff, &tci); } else { pkt->ehdr_buf_len = 0; @@ -138,20 +137,17 @@ void net_rx_pkt_attach_iovec(struct NetRxPkt *pkt, void net_rx_pkt_attach_iovec_ex(struct NetRxPkt *pkt, const struct iovec *iov, int iovcnt, - size_t iovoff, bool strip_vlan, - uint16_t vet) + size_t iovoff, int strip_vlan_index, + uint16_t vet, uint16_t vet_ext) { uint16_t tci = 0; uint16_t ploff = iovoff; assert(pkt); - if (strip_vlan) { - pkt->ehdr_buf_len = eth_strip_vlan_ex(iov, iovcnt, iovoff, vet, - pkt->ehdr_buf, - &ploff, &tci); - } else { - pkt->ehdr_buf_len = 0; - } + pkt->ehdr_buf_len = eth_strip_vlan_ex(iov, iovcnt, iovoff, + strip_vlan_index, vet, vet_ext, + &pkt->ehdr_buf, + &ploff, &tci); pkt->tci = tci; @@ -191,32 +187,26 @@ size_t net_rx_pkt_get_total_len(struct NetRxPkt *pkt) return pkt->tot_len; } -void net_rx_pkt_set_protocols(struct NetRxPkt *pkt, const void *data, - size_t len) +void net_rx_pkt_set_protocols(struct NetRxPkt *pkt, + const struct iovec *iov, size_t iovcnt, + size_t iovoff) { - const struct iovec iov = { - .iov_base = (void *)data, - .iov_len = len - }; - assert(pkt); - eth_get_protocols(&iov, 1, &pkt->isip4, &pkt->isip6, - &pkt->isudp, &pkt->istcp, + eth_get_protocols(iov, iovcnt, iovoff, &pkt->hasip4, &pkt->hasip6, &pkt->l3hdr_off, &pkt->l4hdr_off, &pkt->l5hdr_off, &pkt->ip6hdr_info, &pkt->ip4hdr_info, &pkt->l4hdr_info); } void net_rx_pkt_get_protocols(struct NetRxPkt *pkt, - bool *isip4, bool *isip6, - bool *isudp, bool *istcp) + bool *hasip4, bool *hasip6, + EthL4HdrProto *l4hdr_proto) { assert(pkt); - *isip4 = pkt->isip4; - *isip6 = pkt->isip6; - *isudp = pkt->isudp; - *istcp = pkt->istcp; + *hasip4 = pkt->hasip4; + *hasip6 = pkt->hasip6; + *l4hdr_proto = pkt->l4hdr_info.proto; } size_t net_rx_pkt_get_l3_hdr_offset(struct NetRxPkt *pkt) @@ -247,11 +237,6 @@ eth_ip4_hdr_info *net_rx_pkt_get_ip4_info(struct NetRxPkt *pkt) return &pkt->ip4hdr_info; } -eth_l4_hdr_info *net_rx_pkt_get_l4_info(struct NetRxPkt *pkt) -{ - return &pkt->l4hdr_info; -} - static inline void _net_rx_rss_add_chunk(uint8_t *rss_input, size_t *bytes_written, void *ptr, size_t size) @@ -333,58 +318,58 @@ net_rx_pkt_calc_rss_hash(struct NetRxPkt *pkt, switch (type) { case NetPktRssIpV4: - assert(pkt->isip4); + assert(pkt->hasip4); trace_net_rx_pkt_rss_ip4(); _net_rx_rss_prepare_ip4(&rss_input[0], pkt, &rss_length); break; case NetPktRssIpV4Tcp: - assert(pkt->isip4); - assert(pkt->istcp); + assert(pkt->hasip4); + assert(pkt->l4hdr_info.proto == ETH_L4_HDR_PROTO_TCP); trace_net_rx_pkt_rss_ip4_tcp(); _net_rx_rss_prepare_ip4(&rss_input[0], pkt, &rss_length); _net_rx_rss_prepare_tcp(&rss_input[0], pkt, &rss_length); break; case NetPktRssIpV6Tcp: - assert(pkt->isip6); - assert(pkt->istcp); + assert(pkt->hasip6); + assert(pkt->l4hdr_info.proto == ETH_L4_HDR_PROTO_TCP); trace_net_rx_pkt_rss_ip6_tcp(); _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: - assert(pkt->isip6); + assert(pkt->hasip6); trace_net_rx_pkt_rss_ip6(); _net_rx_rss_prepare_ip6(&rss_input[0], pkt, false, &rss_length); break; case NetPktRssIpV6Ex: - assert(pkt->isip6); + assert(pkt->hasip6); 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); + assert(pkt->hasip6); + assert(pkt->l4hdr_info.proto == ETH_L4_HDR_PROTO_TCP); 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); + assert(pkt->hasip4); + assert(pkt->l4hdr_info.proto == ETH_L4_HDR_PROTO_UDP); 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); + assert(pkt->hasip6); + assert(pkt->l4hdr_info.proto == ETH_L4_HDR_PROTO_UDP); 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); + assert(pkt->hasip6); + assert(pkt->l4hdr_info.proto == ETH_L4_HDR_PROTO_UDP); 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); @@ -406,7 +391,7 @@ uint16_t net_rx_pkt_get_ip_id(struct NetRxPkt *pkt) { assert(pkt); - if (pkt->isip4) { + if (pkt->hasip4) { return be16_to_cpu(pkt->ip4hdr_info.ip4_hdr.ip_id); } @@ -417,7 +402,7 @@ bool net_rx_pkt_is_tcp_ack(struct NetRxPkt *pkt) { assert(pkt); - if (pkt->istcp) { + if (pkt->l4hdr_info.proto == ETH_L4_HDR_PROTO_TCP) { return TCP_HEADER_FLAGS(&pkt->l4hdr_info.hdr.tcp) & TCP_FLAG_ACK; } @@ -428,7 +413,7 @@ bool net_rx_pkt_has_tcp_data(struct NetRxPkt *pkt) { assert(pkt); - if (pkt->istcp) { + if (pkt->l4hdr_info.proto == ETH_L4_HDR_PROTO_TCP) { return pkt->l4hdr_info.has_tcp_data; } @@ -465,6 +450,13 @@ void net_rx_pkt_set_vhdr_iovec(struct NetRxPkt *pkt, iov_to_buf(iov, iovcnt, 0, &pkt->virt_hdr, sizeof pkt->virt_hdr); } +void net_rx_pkt_unset_vhdr(struct NetRxPkt *pkt) +{ + assert(pkt); + + memset(&pkt->virt_hdr, 0, sizeof(pkt->virt_hdr)); +} + bool net_rx_pkt_is_vlan_stripped(struct NetRxPkt *pkt) { assert(pkt); @@ -472,13 +464,6 @@ bool net_rx_pkt_is_vlan_stripped(struct NetRxPkt *pkt) return pkt->ehdr_buf_len ? true : false; } -bool net_rx_pkt_has_virt_hdr(struct NetRxPkt *pkt) -{ - assert(pkt); - - return pkt->has_virt_hdr; -} - uint16_t net_rx_pkt_get_vlan_tag(struct NetRxPkt *pkt) { assert(pkt); @@ -494,7 +479,7 @@ bool net_rx_pkt_validate_l3_csum(struct NetRxPkt *pkt, bool *csum_valid) trace_net_rx_pkt_l3_csum_validate_entry(); - if (!pkt->isip4) { + if (!pkt->hasip4) { trace_net_rx_pkt_l3_csum_validate_not_ip4(); return false; } @@ -525,8 +510,8 @@ _net_rx_pkt_calc_l4_csum(struct NetRxPkt *pkt) trace_net_rx_pkt_l4_csum_calc_entry(); - if (pkt->isip4) { - if (pkt->isudp) { + if (pkt->hasip4) { + if (pkt->l4hdr_info.proto == ETH_L4_HDR_PROTO_UDP) { csl = be16_to_cpu(pkt->l4hdr_info.hdr.udp.uh_ulen); trace_net_rx_pkt_l4_csum_calc_ip4_udp(); } else { @@ -539,7 +524,7 @@ _net_rx_pkt_calc_l4_csum(struct NetRxPkt *pkt) csl, &cso); trace_net_rx_pkt_l4_csum_calc_ph_csum(cntr, csl); } else { - if (pkt->isudp) { + if (pkt->l4hdr_info.proto == ETH_L4_HDR_PROTO_UDP) { csl = be16_to_cpu(pkt->l4hdr_info.hdr.udp.uh_ulen); trace_net_rx_pkt_l4_csum_calc_ip6_udp(); } else { @@ -567,30 +552,73 @@ _net_rx_pkt_calc_l4_csum(struct NetRxPkt *pkt) return csum; } +static bool +_net_rx_pkt_validate_sctp_sum(struct NetRxPkt *pkt) +{ + size_t csum_off; + size_t off = pkt->l4hdr_off; + size_t vec_len = pkt->vec_len; + struct iovec *vec; + uint32_t calculated = 0; + uint32_t original; + bool valid; + + for (vec = pkt->vec; vec->iov_len < off; vec++) { + off -= vec->iov_len; + vec_len--; + } + + csum_off = off + 8; + + if (!iov_to_buf(vec, vec_len, csum_off, &original, sizeof(original))) { + return false; + } + + if (!iov_from_buf(vec, vec_len, csum_off, + &calculated, sizeof(calculated))) { + return false; + } + + calculated = crc32c(0xffffffff, + (uint8_t *)vec->iov_base + off, vec->iov_len - off); + calculated = iov_crc32c(calculated ^ 0xffffffff, vec + 1, vec_len - 1); + valid = calculated == le32_to_cpu(original); + iov_from_buf(vec, vec_len, csum_off, &original, sizeof(original)); + + return valid; +} + bool net_rx_pkt_validate_l4_csum(struct NetRxPkt *pkt, bool *csum_valid) { - uint16_t csum; + uint32_t csum; trace_net_rx_pkt_l4_csum_validate_entry(); - if (!pkt->istcp && !pkt->isudp) { - trace_net_rx_pkt_l4_csum_validate_not_xxp(); - return false; - } - - if (pkt->isudp && (pkt->l4hdr_info.hdr.udp.uh_sum == 0)) { - trace_net_rx_pkt_l4_csum_validate_udp_with_no_checksum(); - return false; - } - - if (pkt->isip4 && pkt->ip4hdr_info.fragment) { + if (pkt->hasip4 && pkt->ip4hdr_info.fragment) { trace_net_rx_pkt_l4_csum_validate_ip4_fragment(); return false; } - csum = _net_rx_pkt_calc_l4_csum(pkt); + switch (pkt->l4hdr_info.proto) { + case ETH_L4_HDR_PROTO_UDP: + if (pkt->l4hdr_info.hdr.udp.uh_sum == 0) { + trace_net_rx_pkt_l4_csum_validate_udp_with_no_checksum(); + return false; + } + /* fall through */ + case ETH_L4_HDR_PROTO_TCP: + csum = _net_rx_pkt_calc_l4_csum(pkt); + *csum_valid = ((csum == 0) || (csum == 0xFFFF)); + break; - *csum_valid = ((csum == 0) || (csum == 0xFFFF)); + case ETH_L4_HDR_PROTO_SCTP: + *csum_valid = _net_rx_pkt_validate_sctp_sum(pkt); + break; + + default: + trace_net_rx_pkt_l4_csum_validate_not_xxp(); + return false; + } trace_net_rx_pkt_l4_csum_validate_csum(*csum_valid); @@ -604,22 +632,27 @@ bool net_rx_pkt_fix_l4_csum(struct NetRxPkt *pkt) trace_net_rx_pkt_l4_csum_fix_entry(); - if (pkt->istcp) { + switch (pkt->l4hdr_info.proto) { + case ETH_L4_HDR_PROTO_TCP: l4_cso = offsetof(struct tcp_header, th_sum); trace_net_rx_pkt_l4_csum_fix_tcp(l4_cso); - } else if (pkt->isudp) { + break; + + case ETH_L4_HDR_PROTO_UDP: if (pkt->l4hdr_info.hdr.udp.uh_sum == 0) { trace_net_rx_pkt_l4_csum_fix_udp_with_no_checksum(); return false; } l4_cso = offsetof(struct udp_header, uh_sum); trace_net_rx_pkt_l4_csum_fix_udp(l4_cso); - } else { + break; + + default: trace_net_rx_pkt_l4_csum_fix_not_xxp(); return false; } - if (pkt->isip4 && pkt->ip4hdr_info.fragment) { + if (pkt->hasip4 && pkt->ip4hdr_info.fragment) { trace_net_rx_pkt_l4_csum_fix_ip4_fragment(); return false; } diff --git a/hw/net/net_rx_pkt.h b/hw/net/net_rx_pkt.h index 048e3461f0..55ec67a1a7 100644 --- a/hw/net/net_rx_pkt.h +++ b/hw/net/net_rx_pkt.h @@ -37,10 +37,9 @@ void net_rx_pkt_uninit(struct NetRxPkt *pkt); * Init function for rx packet functionality * * @pkt: packet pointer - * @has_virt_hdr: device uses virtio header * */ -void net_rx_pkt_init(struct NetRxPkt **pkt, bool has_virt_hdr); +void net_rx_pkt_init(struct NetRxPkt **pkt); /** * returns total length of data attached to rx context @@ -56,26 +55,27 @@ size_t net_rx_pkt_get_total_len(struct NetRxPkt *pkt); * parse and set packet analysis results * * @pkt: packet - * @data: pointer to the data buffer to be parsed - * @len: data length + * @iov: received data scatter-gather list + * @iovcnt: number of elements in iov + * @iovoff: data start offset in the iov * */ -void net_rx_pkt_set_protocols(struct NetRxPkt *pkt, const void *data, - size_t len); +void net_rx_pkt_set_protocols(struct NetRxPkt *pkt, + const struct iovec *iov, size_t iovcnt, + size_t iovoff); /** * fetches packet analysis results * * @pkt: packet - * @isip4: whether the packet given is IPv4 - * @isip6: whether the packet given is IPv6 - * @isudp: whether the packet given is UDP - * @istcp: whether the packet given is TCP + * @hasip4: whether the packet has an IPv4 header + * @hasip6: whether the packet has an IPv6 header + * @l4hdr_proto: protocol of L4 header * */ void net_rx_pkt_get_protocols(struct NetRxPkt *pkt, - bool *isip4, bool *isip6, - bool *isudp, bool *istcp); + bool *hasip4, bool *hasip6, + EthL4HdrProto *l4hdr_proto); /** * fetches L3 header offset @@ -119,15 +119,6 @@ eth_ip6_hdr_info *net_rx_pkt_get_ip6_info(struct NetRxPkt *pkt); */ eth_ip4_hdr_info *net_rx_pkt_get_ip4_info(struct NetRxPkt *pkt); -/** - * fetches L4 header analysis results - * - * Return: pointer to analysis results structure which is stored in internal - * packet area. - * - */ -eth_l4_hdr_info *net_rx_pkt_get_l4_info(struct NetRxPkt *pkt); - typedef enum { NetPktRssIpV4, NetPktRssIpV4Tcp, @@ -214,15 +205,6 @@ uint16_t net_rx_pkt_get_vlan_tag(struct NetRxPkt *pkt); */ bool net_rx_pkt_is_vlan_stripped(struct NetRxPkt *pkt); -/** - * notifies caller if the packet has virtio header - * - * @pkt: packet - * @ret: true if packet has virtio header, false otherwize - * - */ -bool net_rx_pkt_has_virt_hdr(struct NetRxPkt *pkt); - /** * attach scatter-gather data to rx packet * @@ -241,18 +223,19 @@ void net_rx_pkt_attach_iovec(struct NetRxPkt *pkt, /** * attach scatter-gather data to rx packet * -* @pkt: packet -* @iov: received data scatter-gather list -* @iovcnt number of elements in iov -* @iovoff data start offset in the iov -* @strip_vlan: should the module strip vlan from data -* @vet: VLAN tag Ethernet type +* @pkt: packet +* @iov: received data scatter-gather list +* @iovcnt: number of elements in iov +* @iovoff: data start offset in the iov +* @strip_vlan_index: index of Q tag if it is to be stripped. negative otherwise. +* @vet: VLAN tag Ethernet type +* @vet_ext: outer VLAN tag Ethernet type * */ void net_rx_pkt_attach_iovec_ex(struct NetRxPkt *pkt, - const struct iovec *iov, int iovcnt, - size_t iovoff, bool strip_vlan, - uint16_t vet); + const struct iovec *iov, int iovcnt, + size_t iovoff, int strip_vlan_index, + uint16_t vet, uint16_t vet_ext); /** * attach data to rx packet @@ -322,6 +305,14 @@ void net_rx_pkt_set_vhdr(struct NetRxPkt *pkt, void net_rx_pkt_set_vhdr_iovec(struct NetRxPkt *pkt, const struct iovec *iov, int iovcnt); +/** + * unset vhdr data from packet context + * + * @pkt: packet + * + */ +void net_rx_pkt_unset_vhdr(struct NetRxPkt *pkt); + /** * save packet type in packet context * diff --git a/hw/net/net_tx_pkt.c b/hw/net/net_tx_pkt.c index 1cb1125d9f..b7b1de816d 100644 --- a/hw/net/net_tx_pkt.c +++ b/hw/net/net_tx_pkt.c @@ -16,12 +16,13 @@ */ #include "qemu/osdep.h" -#include "net_tx_pkt.h" +#include "qemu/crc32c.h" #include "net/eth.h" #include "net/checksum.h" #include "net/tap.h" #include "net/net.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" +#include "net_tx_pkt.h" enum { NET_TX_PKT_VHDR_FRAG = 0, @@ -32,10 +33,7 @@ enum { /* TX packet private context */ struct NetTxPkt { - PCIDevice *pci_dev; - struct virtio_net_hdr virt_hdr; - bool has_virt_hdr; struct iovec *raw; uint32_t raw_frags; @@ -43,8 +41,15 @@ struct NetTxPkt { struct iovec *vec; - uint8_t l2_hdr[ETH_MAX_L2_HDR_LEN]; - uint8_t l3_hdr[ETH_MAX_IP_DGRAM_LEN]; + struct { + struct eth_header eth; + struct vlan_header vlan[3]; + } l2_hdr; + union { + struct ip_header ip; + struct ip6_header ip6; + uint8_t octets[ETH_MAX_IP_DGRAM_LEN]; + } l3_hdr; uint32_t payload_len; @@ -54,27 +59,20 @@ struct NetTxPkt { uint16_t hdr_len; eth_pkt_types_e packet_type; uint8_t l4proto; - - bool is_loopback; }; -void net_tx_pkt_init(struct NetTxPkt **pkt, PCIDevice *pci_dev, - uint32_t max_frags, bool has_virt_hdr) +void net_tx_pkt_init(struct NetTxPkt **pkt, uint32_t max_frags) { struct NetTxPkt *p = g_malloc0(sizeof *p); - p->pci_dev = pci_dev; - p->vec = g_new(struct iovec, max_frags + NET_TX_PKT_PL_START_FRAG); p->raw = g_new(struct iovec, max_frags); p->max_payload_frags = max_frags; p->max_raw_frags = max_frags; - p->has_virt_hdr = has_virt_hdr; p->vec[NET_TX_PKT_VHDR_FRAG].iov_base = &p->virt_hdr; - p->vec[NET_TX_PKT_VHDR_FRAG].iov_len = - p->has_virt_hdr ? sizeof p->virt_hdr : 0; + p->vec[NET_TX_PKT_VHDR_FRAG].iov_len = sizeof p->virt_hdr; p->vec[NET_TX_PKT_L2HDR_FRAG].iov_base = &p->l2_hdr; p->vec[NET_TX_PKT_L3HDR_FRAG].iov_base = &p->l3_hdr; @@ -94,16 +92,14 @@ void net_tx_pkt_update_ip_hdr_checksum(struct NetTxPkt *pkt) { uint16_t csum; assert(pkt); - struct ip_header *ip_hdr; - ip_hdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; - ip_hdr->ip_len = cpu_to_be16(pkt->payload_len + + pkt->l3_hdr.ip.ip_len = cpu_to_be16(pkt->payload_len + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); - ip_hdr->ip_sum = 0; - csum = net_raw_checksum((uint8_t *)ip_hdr, + pkt->l3_hdr.ip.ip_sum = 0; + csum = net_raw_checksum(pkt->l3_hdr.octets, pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); - ip_hdr->ip_sum = cpu_to_be16(csum); + pkt->l3_hdr.ip.ip_sum = cpu_to_be16(csum); } void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt) @@ -140,6 +136,27 @@ void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt) pkt->virt_hdr.csum_offset, &csum, sizeof(csum)); } +bool net_tx_pkt_update_sctp_checksum(struct NetTxPkt *pkt) +{ + uint32_t csum = 0; + struct iovec *pl_start_frag = pkt->vec + NET_TX_PKT_PL_START_FRAG; + + if (iov_size(pl_start_frag, pkt->payload_frags) < 8 + sizeof(csum)) { + return false; + } + + if (iov_from_buf(pl_start_frag, pkt->payload_frags, 8, &csum, sizeof(csum)) < sizeof(csum)) { + return false; + } + + csum = cpu_to_le32(iov_crc32c(0xffffffff, pl_start_frag, pkt->payload_frags)); + if (iov_from_buf(pl_start_frag, pkt->payload_frags, 8, &csum, sizeof(csum)) < sizeof(csum)) { + return false; + } + + return true; +} + static void net_tx_pkt_calculate_hdr_len(struct NetTxPkt *pkt) { pkt->hdr_len = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len + @@ -304,10 +321,11 @@ func_exit: return rc; } -void net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool tso_enable, +bool net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool tso_enable, bool csum_enable, uint32_t gso_size) { struct tcp_hdr l4hdr; + size_t bytes_read; assert(pkt); /* csum has to be enabled if tso is. */ @@ -328,8 +346,13 @@ void net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool tso_enable, case VIRTIO_NET_HDR_GSO_TCPV4: case VIRTIO_NET_HDR_GSO_TCPV6: - iov_to_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], pkt->payload_frags, - 0, &l4hdr, sizeof(l4hdr)); + bytes_read = iov_to_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], + pkt->payload_frags, 0, &l4hdr, sizeof(l4hdr)); + if (bytes_read < sizeof(l4hdr) || + l4hdr.th_off * sizeof(uint32_t) < sizeof(l4hdr)) { + return false; + } + pkt->virt_hdr.hdr_len = pkt->hdr_len + l4hdr.th_off * sizeof(uint32_t); pkt->virt_hdr.gso_size = gso_size; break; @@ -341,11 +364,17 @@ void net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool tso_enable, if (csum_enable) { switch (pkt->l4proto) { case IP_PROTO_TCP: + if (pkt->payload_len < sizeof(struct tcp_hdr)) { + return false; + } pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; pkt->virt_hdr.csum_start = pkt->hdr_len; pkt->virt_hdr.csum_offset = offsetof(struct tcp_hdr, th_sum); break; case IP_PROTO_UDP: + if (pkt->payload_len < sizeof(struct udp_hdr)) { + return false; + } pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; pkt->virt_hdr.csum_start = pkt->hdr_len; pkt->virt_hdr.csum_offset = offsetof(struct udp_hdr, uh_sum); @@ -354,29 +383,24 @@ void net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool tso_enable, break; } } + + return true; } void net_tx_pkt_setup_vlan_header_ex(struct NetTxPkt *pkt, uint16_t vlan, uint16_t vlan_ethtype) { - bool is_new; assert(pkt); - eth_setup_vlan_headers_ex(pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base, - vlan, vlan_ethtype, &is_new); + eth_setup_vlan_headers(pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base, + &pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len, + vlan, vlan_ethtype); - /* update l2hdrlen */ - if (is_new) { - pkt->hdr_len += sizeof(struct vlan_header); - pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len += - sizeof(struct vlan_header); - } + pkt->hdr_len += sizeof(struct vlan_header); } -bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, hwaddr pa, - size_t len) +bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, void *base, size_t len) { - hwaddr mapped_len = 0; struct iovec *ventry; assert(pkt); @@ -384,23 +408,12 @@ bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, hwaddr pa, return false; } - if (!len) { - return true; - } - ventry = &pkt->raw[pkt->raw_frags]; - mapped_len = len; + ventry->iov_base = base; + ventry->iov_len = len; + pkt->raw_frags++; - ventry->iov_base = pci_dma_map(pkt->pci_dev, pa, - &mapped_len, DMA_DIRECTION_TO_DEVICE); - - if ((ventry->iov_base != NULL) && (len == mapped_len)) { - ventry->iov_len = mapped_len; - pkt->raw_frags++; - return true; - } else { - return false; - } + return true; } bool net_tx_pkt_has_fragments(struct NetTxPkt *pkt) @@ -434,7 +447,8 @@ void net_tx_pkt_dump(struct NetTxPkt *pkt) #endif } -void net_tx_pkt_reset(struct NetTxPkt *pkt) +void net_tx_pkt_reset(struct NetTxPkt *pkt, + NetTxPktFreeFrag callback, void *context) { int i; @@ -454,8 +468,7 @@ void net_tx_pkt_reset(struct NetTxPkt *pkt) assert(pkt->raw); for (i = 0; i < pkt->raw_frags; i++) { assert(pkt->raw[i].iov_base); - pci_dma_unmap(pkt->pci_dev, pkt->raw[i].iov_base, - pkt->raw[i].iov_len, DMA_DIRECTION_TO_DEVICE, 0); + callback(context, pkt->raw[i].iov_base, pkt->raw[i].iov_len); } } pkt->raw_frags = 0; @@ -464,15 +477,36 @@ void net_tx_pkt_reset(struct NetTxPkt *pkt) pkt->l4proto = 0; } -static void net_tx_pkt_do_sw_csum(struct NetTxPkt *pkt) +void net_tx_pkt_unmap_frag_pci(void *context, void *base, size_t len) +{ + pci_dma_unmap(context, base, len, DMA_DIRECTION_TO_DEVICE, 0); +} + +bool net_tx_pkt_add_raw_fragment_pci(struct NetTxPkt *pkt, PCIDevice *pci_dev, + dma_addr_t pa, size_t len) +{ + dma_addr_t mapped_len = len; + void *base = pci_dma_map(pci_dev, pa, &mapped_len, DMA_DIRECTION_TO_DEVICE); + if (!base) { + return false; + } + + if (mapped_len != len || !net_tx_pkt_add_raw_fragment(pkt, base, len)) { + net_tx_pkt_unmap_frag_pci(pci_dev, base, mapped_len); + return false; + } + + return true; +} + +static void net_tx_pkt_do_sw_csum(struct NetTxPkt *pkt, + struct iovec *iov, uint32_t iov_len, + uint16_t csl) { - struct iovec *iov = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; uint32_t csum_cntr; uint16_t csum = 0; uint32_t cso; /* num of iovec without vhdr */ - uint32_t iov_len = pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - 1; - uint16_t csl; size_t csum_offset = pkt->virt_hdr.csum_start + pkt->virt_hdr.csum_offset; uint16_t l3_proto = eth_get_l3_proto(iov, 1, iov->iov_len); @@ -480,8 +514,6 @@ static void net_tx_pkt_do_sw_csum(struct NetTxPkt *pkt) iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); /* Calculate L4 TCP/UDP checksum */ - csl = pkt->payload_len; - csum_cntr = 0; cso = 0; /* add pseudo header to csum */ @@ -504,23 +536,16 @@ static void net_tx_pkt_do_sw_csum(struct NetTxPkt *pkt) iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); } -enum { - NET_TX_PKT_FRAGMENT_L2_HDR_POS = 0, - NET_TX_PKT_FRAGMENT_L3_HDR_POS, - NET_TX_PKT_FRAGMENT_HEADER_NUM -}; - #define NET_MAX_FRAG_SG_LIST (64) static size_t net_tx_pkt_fetch_fragment(struct NetTxPkt *pkt, - int *src_idx, size_t *src_offset, struct iovec *dst, int *dst_idx) + int *src_idx, size_t *src_offset, size_t src_len, + struct iovec *dst, int *dst_idx) { size_t fetched = 0; struct iovec *src = pkt->vec; - *dst_idx = NET_TX_PKT_FRAGMENT_HEADER_NUM; - - while (fetched < IP_FRAG_ALIGN_SIZE(pkt->virt_hdr.gso_size)) { + while (fetched < src_len) { /* no more place in fragment iov */ if (*dst_idx == NET_MAX_FRAG_SG_LIST) { @@ -535,7 +560,7 @@ static size_t net_tx_pkt_fetch_fragment(struct NetTxPkt *pkt, dst[*dst_idx].iov_base = src[*src_idx].iov_base + *src_offset; dst[*dst_idx].iov_len = MIN(src[*src_idx].iov_len - *src_offset, - IP_FRAG_ALIGN_SIZE(pkt->virt_hdr.gso_size) - fetched); + src_len - fetched); *src_offset += dst[*dst_idx].iov_len; fetched += dst[*dst_idx].iov_len; @@ -551,77 +576,258 @@ static size_t net_tx_pkt_fetch_fragment(struct NetTxPkt *pkt, return fetched; } -static inline void net_tx_pkt_sendv(struct NetTxPkt *pkt, - NetClientState *nc, const struct iovec *iov, int iov_cnt) +static void net_tx_pkt_sendv( + void *opaque, const struct iovec *iov, int iov_cnt, + const struct iovec *virt_iov, int virt_iov_cnt) { - if (pkt->is_loopback) { - qemu_receive_packet_iov(nc, iov, iov_cnt); + NetClientState *nc = opaque; + + if (qemu_get_using_vnet_hdr(nc->peer)) { + qemu_sendv_packet(nc, virt_iov, virt_iov_cnt); } else { qemu_sendv_packet(nc, iov, iov_cnt); } } -static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt, - NetClientState *nc) +static bool net_tx_pkt_tcp_fragment_init(struct NetTxPkt *pkt, + struct iovec *fragment, + int *pl_idx, + size_t *l4hdr_len, + int *src_idx, + size_t *src_offset, + size_t *src_len) { + struct iovec *l4 = fragment + NET_TX_PKT_PL_START_FRAG; + size_t bytes_read = 0; + struct tcp_hdr *th; + + if (!pkt->payload_frags) { + return false; + } + + l4->iov_len = pkt->virt_hdr.hdr_len - pkt->hdr_len; + l4->iov_base = g_malloc(l4->iov_len); + + *src_idx = NET_TX_PKT_PL_START_FRAG; + while (pkt->vec[*src_idx].iov_len < l4->iov_len - bytes_read) { + memcpy((char *)l4->iov_base + bytes_read, pkt->vec[*src_idx].iov_base, + pkt->vec[*src_idx].iov_len); + + bytes_read += pkt->vec[*src_idx].iov_len; + + (*src_idx)++; + if (*src_idx >= pkt->payload_frags + NET_TX_PKT_PL_START_FRAG) { + g_free(l4->iov_base); + return false; + } + } + + *src_offset = l4->iov_len - bytes_read; + memcpy((char *)l4->iov_base + bytes_read, pkt->vec[*src_idx].iov_base, + *src_offset); + + th = l4->iov_base; + th->th_flags &= ~(TH_FIN | TH_PUSH); + + *pl_idx = NET_TX_PKT_PL_START_FRAG + 1; + *l4hdr_len = l4->iov_len; + *src_len = pkt->virt_hdr.gso_size; + + return true; +} + +static void net_tx_pkt_tcp_fragment_deinit(struct iovec *fragment) +{ + g_free(fragment[NET_TX_PKT_PL_START_FRAG].iov_base); +} + +static void net_tx_pkt_tcp_fragment_fix(struct NetTxPkt *pkt, + struct iovec *fragment, + size_t fragment_len, + uint8_t gso_type) +{ + struct iovec *l3hdr = fragment + NET_TX_PKT_L3HDR_FRAG; + struct iovec *l4hdr = fragment + NET_TX_PKT_PL_START_FRAG; + struct ip_header *ip = l3hdr->iov_base; + struct ip6_header *ip6 = l3hdr->iov_base; + size_t len = l3hdr->iov_len + l4hdr->iov_len + fragment_len; + + switch (gso_type) { + case VIRTIO_NET_HDR_GSO_TCPV4: + ip->ip_len = cpu_to_be16(len); + eth_fix_ip4_checksum(l3hdr->iov_base, l3hdr->iov_len); + break; + + case VIRTIO_NET_HDR_GSO_TCPV6: + len -= sizeof(struct ip6_header); + ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = cpu_to_be16(len); + break; + } +} + +static void net_tx_pkt_tcp_fragment_advance(struct NetTxPkt *pkt, + struct iovec *fragment, + size_t fragment_len, + uint8_t gso_type) +{ + struct iovec *l3hdr = fragment + NET_TX_PKT_L3HDR_FRAG; + struct iovec *l4hdr = fragment + NET_TX_PKT_PL_START_FRAG; + struct ip_header *ip = l3hdr->iov_base; + struct tcp_hdr *th = l4hdr->iov_base; + + if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4) { + ip->ip_id = cpu_to_be16(be16_to_cpu(ip->ip_id) + 1); + } + + th->th_seq = cpu_to_be32(be32_to_cpu(th->th_seq) + fragment_len); + th->th_flags &= ~TH_CWR; +} + +static void net_tx_pkt_udp_fragment_init(struct NetTxPkt *pkt, + int *pl_idx, + size_t *l4hdr_len, + int *src_idx, size_t *src_offset, + size_t *src_len) +{ + *pl_idx = NET_TX_PKT_PL_START_FRAG; + *l4hdr_len = 0; + *src_idx = NET_TX_PKT_PL_START_FRAG; + *src_offset = 0; + *src_len = IP_FRAG_ALIGN_SIZE(pkt->virt_hdr.gso_size); +} + +static void net_tx_pkt_udp_fragment_fix(struct NetTxPkt *pkt, + struct iovec *fragment, + size_t fragment_offset, + size_t fragment_len) +{ + bool more_frags = fragment_offset + fragment_len < pkt->payload_len; + uint16_t orig_flags; + struct iovec *l3hdr = fragment + NET_TX_PKT_L3HDR_FRAG; + struct ip_header *ip = l3hdr->iov_base; + uint16_t frag_off_units = fragment_offset / IP_FRAG_UNIT_SIZE; + uint16_t new_ip_off; + + assert(fragment_offset % IP_FRAG_UNIT_SIZE == 0); + assert((frag_off_units & ~IP_OFFMASK) == 0); + + orig_flags = be16_to_cpu(ip->ip_off) & ~(IP_OFFMASK | IP_MF); + new_ip_off = frag_off_units | orig_flags | (more_frags ? IP_MF : 0); + ip->ip_off = cpu_to_be16(new_ip_off); + ip->ip_len = cpu_to_be16(l3hdr->iov_len + fragment_len); + + eth_fix_ip4_checksum(l3hdr->iov_base, l3hdr->iov_len); +} + +static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt, + NetTxPktSend callback, + void *context) +{ + uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; + struct iovec fragment[NET_MAX_FRAG_SG_LIST]; - size_t fragment_len = 0; - bool more_frags = false; + size_t fragment_len; + size_t l4hdr_len; + size_t src_len; - /* some pointers for shorter code */ - void *l2_iov_base, *l3_iov_base; - size_t l2_iov_len, l3_iov_len; - int src_idx = NET_TX_PKT_PL_START_FRAG, dst_idx; - size_t src_offset = 0; + int src_idx, dst_idx, pl_idx; + size_t src_offset; size_t fragment_offset = 0; - - l2_iov_base = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base; - l2_iov_len = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len; - l3_iov_base = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; - l3_iov_len = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len; + struct virtio_net_hdr virt_hdr = { + .flags = pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM ? + VIRTIO_NET_HDR_F_DATA_VALID : 0 + }; /* Copy headers */ - fragment[NET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_base = l2_iov_base; - fragment[NET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_len = l2_iov_len; - fragment[NET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_base = l3_iov_base; - fragment[NET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_len = l3_iov_len; + fragment[NET_TX_PKT_VHDR_FRAG].iov_base = &virt_hdr; + fragment[NET_TX_PKT_VHDR_FRAG].iov_len = sizeof(virt_hdr); + fragment[NET_TX_PKT_L2HDR_FRAG] = pkt->vec[NET_TX_PKT_L2HDR_FRAG]; + fragment[NET_TX_PKT_L3HDR_FRAG] = pkt->vec[NET_TX_PKT_L3HDR_FRAG]; + switch (gso_type) { + case VIRTIO_NET_HDR_GSO_TCPV4: + case VIRTIO_NET_HDR_GSO_TCPV6: + if (!net_tx_pkt_tcp_fragment_init(pkt, fragment, &pl_idx, &l4hdr_len, + &src_idx, &src_offset, &src_len)) { + return false; + } + break; + + case VIRTIO_NET_HDR_GSO_UDP: + net_tx_pkt_do_sw_csum(pkt, &pkt->vec[NET_TX_PKT_L2HDR_FRAG], + pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - 1, + pkt->payload_len); + net_tx_pkt_udp_fragment_init(pkt, &pl_idx, &l4hdr_len, + &src_idx, &src_offset, &src_len); + break; + + default: + abort(); + } /* Put as much data as possible and send */ - do { - fragment_len = net_tx_pkt_fetch_fragment(pkt, &src_idx, &src_offset, - fragment, &dst_idx); + while (true) { + dst_idx = pl_idx; + fragment_len = net_tx_pkt_fetch_fragment(pkt, + &src_idx, &src_offset, src_len, fragment, &dst_idx); + if (!fragment_len) { + break; + } - more_frags = (fragment_offset + fragment_len < pkt->payload_len); + switch (gso_type) { + case VIRTIO_NET_HDR_GSO_TCPV4: + case VIRTIO_NET_HDR_GSO_TCPV6: + net_tx_pkt_tcp_fragment_fix(pkt, fragment, fragment_len, gso_type); + net_tx_pkt_do_sw_csum(pkt, fragment + NET_TX_PKT_L2HDR_FRAG, + dst_idx - NET_TX_PKT_L2HDR_FRAG, + l4hdr_len + fragment_len); + break; - eth_setup_ip4_fragmentation(l2_iov_base, l2_iov_len, l3_iov_base, - l3_iov_len, fragment_len, fragment_offset, more_frags); + case VIRTIO_NET_HDR_GSO_UDP: + net_tx_pkt_udp_fragment_fix(pkt, fragment, fragment_offset, + fragment_len); + break; + } - eth_fix_ip4_checksum(l3_iov_base, l3_iov_len); + callback(context, + fragment + NET_TX_PKT_L2HDR_FRAG, dst_idx - NET_TX_PKT_L2HDR_FRAG, + fragment + NET_TX_PKT_VHDR_FRAG, dst_idx - NET_TX_PKT_VHDR_FRAG); - net_tx_pkt_sendv(pkt, nc, fragment, dst_idx); + if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4 || + gso_type == VIRTIO_NET_HDR_GSO_TCPV6) { + net_tx_pkt_tcp_fragment_advance(pkt, fragment, fragment_len, + gso_type); + } fragment_offset += fragment_len; + } - } while (fragment_len && more_frags); + if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4 || + gso_type == VIRTIO_NET_HDR_GSO_TCPV6) { + net_tx_pkt_tcp_fragment_deinit(fragment); + } return true; } bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc) +{ + bool offload = qemu_get_using_vnet_hdr(nc->peer); + return net_tx_pkt_send_custom(pkt, offload, net_tx_pkt_sendv, nc); +} + +bool net_tx_pkt_send_custom(struct NetTxPkt *pkt, bool offload, + NetTxPktSend callback, void *context) { assert(pkt); - if (!pkt->has_virt_hdr && - pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { - net_tx_pkt_do_sw_csum(pkt); - } + uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; /* * Since underlying infrastructure does not support IP datagrams longer * than 64K we should drop such packets and don't even try to send */ - if (VIRTIO_NET_HDR_GSO_NONE != pkt->virt_hdr.gso_type) { + if (VIRTIO_NET_HDR_GSO_NONE != gso_type) { if (pkt->payload_len > ETH_MAX_IP_DGRAM_LEN - pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len) { @@ -629,41 +835,37 @@ bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc) } } - if (pkt->has_virt_hdr || - pkt->virt_hdr.gso_type == VIRTIO_NET_HDR_GSO_NONE) { + if (offload || gso_type == VIRTIO_NET_HDR_GSO_NONE) { + if (!offload && pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { + pkt->virt_hdr.flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM; + net_tx_pkt_do_sw_csum(pkt, &pkt->vec[NET_TX_PKT_L2HDR_FRAG], + pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - 1, + pkt->payload_len); + } + net_tx_pkt_fix_ip6_payload_len(pkt); - net_tx_pkt_sendv(pkt, nc, pkt->vec, - pkt->payload_frags + NET_TX_PKT_PL_START_FRAG); + callback(context, pkt->vec + NET_TX_PKT_L2HDR_FRAG, + pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - NET_TX_PKT_L2HDR_FRAG, + pkt->vec + NET_TX_PKT_VHDR_FRAG, + pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - NET_TX_PKT_VHDR_FRAG); return true; } - return net_tx_pkt_do_sw_fragmentation(pkt, nc); -} - -bool net_tx_pkt_send_loopback(struct NetTxPkt *pkt, NetClientState *nc) -{ - bool res; - - pkt->is_loopback = true; - res = net_tx_pkt_send(pkt, nc); - pkt->is_loopback = false; - - return res; + return net_tx_pkt_do_sw_fragmentation(pkt, callback, context); } void net_tx_pkt_fix_ip6_payload_len(struct NetTxPkt *pkt) { struct iovec *l2 = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; if (eth_get_l3_proto(l2, 1, l2->iov_len) == ETH_P_IPV6) { - struct ip6_header *ip6 = (struct ip6_header *) pkt->l3_hdr; /* * TODO: if qemu would support >64K packets - add jumbo option check * something like that: * 'if (ip6->ip6_plen == 0 && !has_jumbo_option(ip6)) {' */ - if (ip6->ip6_plen == 0) { + if (pkt->l3_hdr.ip6.ip6_plen == 0) { if (pkt->payload_len <= ETH_MAX_IP_DGRAM_LEN) { - ip6->ip6_plen = htons(pkt->payload_len); + pkt->l3_hdr.ip6.ip6_plen = htons(pkt->payload_len); } /* * TODO: if qemu would support >64K packets diff --git a/hw/net/net_tx_pkt.h b/hw/net/net_tx_pkt.h index 4ec8bbe9bd..0a716e74a5 100644 --- a/hw/net/net_tx_pkt.h +++ b/hw/net/net_tx_pkt.h @@ -26,16 +26,16 @@ struct NetTxPkt; +typedef void (*NetTxPktFreeFrag)(void *, void *, size_t); +typedef void (*NetTxPktSend)(void *, const struct iovec *, int, const struct iovec *, int); + /** * Init function for tx packet functionality * * @pkt: packet pointer - * @pci_dev: PCI device processing this packet * @max_frags: max tx ip fragments - * @has_virt_hdr: device uses virtio header. */ -void net_tx_pkt_init(struct NetTxPkt **pkt, PCIDevice *pci_dev, - uint32_t max_frags, bool has_virt_hdr); +void net_tx_pkt_init(struct NetTxPkt **pkt, uint32_t max_frags); /** * Clean all tx packet resources. @@ -59,9 +59,10 @@ struct virtio_net_hdr *net_tx_pkt_get_vhdr(struct NetTxPkt *pkt); * @tso_enable: TSO enabled * @csum_enable: CSO enabled * @gso_size: MSS size for TSO + * @ret: operation result * */ -void net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool tso_enable, +bool net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool tso_enable, bool csum_enable, uint32_t gso_size); /** @@ -93,12 +94,11 @@ net_tx_pkt_setup_vlan_header(struct NetTxPkt *pkt, uint16_t vlan) * populate data fragment into pkt context. * * @pkt: packet - * @pa: physical address of fragment + * @base: pointer to fragment * @len: length of fragment * */ -bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, hwaddr pa, - size_t len); +bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, void *base, size_t len); /** * Fix ip header fields and calculate IP header and pseudo header checksums. @@ -116,6 +116,14 @@ void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt); */ void net_tx_pkt_update_ip_hdr_checksum(struct NetTxPkt *pkt); +/** + * Calculate the SCTP checksum. + * + * @pkt: packet + * + */ +bool net_tx_pkt_update_sctp_checksum(struct NetTxPkt *pkt); + /** * get length of all populated data. * @@ -146,9 +154,30 @@ void net_tx_pkt_dump(struct NetTxPkt *pkt); * reset tx packet private context (needed to be called between packets) * * @pkt: packet - * + * @callback: function to free the fragments + * @context: pointer to be passed to the callback */ -void net_tx_pkt_reset(struct NetTxPkt *pkt); +void net_tx_pkt_reset(struct NetTxPkt *pkt, + NetTxPktFreeFrag callback, void *context); + +/** + * Unmap a fragment mapped from a PCI device. + * + * @context: PCI device owning fragment + * @base: pointer to fragment + * @len: length of fragment + */ +void net_tx_pkt_unmap_frag_pci(void *context, void *base, size_t len); + +/** + * map data fragment from PCI device and populate it into pkt context. + * + * @pci_dev: PCI device owning fragment + * @pa: physical address of fragment + * @len: length of fragment + */ +bool net_tx_pkt_add_raw_fragment_pci(struct NetTxPkt *pkt, PCIDevice *pci_dev, + dma_addr_t pa, size_t len); /** * Send packet to qemu. handles sw offloads if vhdr is not supported. @@ -161,15 +190,16 @@ void net_tx_pkt_reset(struct NetTxPkt *pkt); bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc); /** -* Redirect packet directly to receive path (emulate loopback phy). -* Handles sw offloads if vhdr is not supported. -* -* @pkt: packet -* @nc: NetClientState -* @ret: operation result -* -*/ -bool net_tx_pkt_send_loopback(struct NetTxPkt *pkt, NetClientState *nc); + * Send packet with a custom function. + * + * @pkt: packet + * @offload: whether the callback implements offloading + * @callback: a function to be called back for each transformed packet + * @context: a pointer to be passed to the callback. + * @ret: operation result + */ +bool net_tx_pkt_send_custom(struct NetTxPkt *pkt, bool offload, + NetTxPktSend callback, void *context); /** * parse raw packet data and analyze offload requirements. diff --git a/hw/net/npcm7xx_emc.c b/hw/net/npcm7xx_emc.c index 7c86bb52e5..d1583b6f9b 100644 --- a/hw/net/npcm7xx_emc.c +++ b/hw/net/npcm7xx_emc.c @@ -98,6 +98,8 @@ static const char *emc_reg_name(int regno) static void emc_reset(NPCM7xxEMCState *emc) { + uint32_t value; + trace_npcm7xx_emc_reset(emc->emc_num); memset(&emc->regs[0], 0, sizeof(emc->regs)); @@ -112,6 +114,16 @@ static void emc_reset(NPCM7xxEMCState *emc) emc->tx_active = false; emc->rx_active = false; + + /* Set the MAC address in the register space. */ + value = (emc->conf.macaddr.a[0] << 24) | + (emc->conf.macaddr.a[1] << 16) | + (emc->conf.macaddr.a[2] << 8) | + emc->conf.macaddr.a[3]; + emc->regs[REG_CAMM_BASE] = value; + + value = (emc->conf.macaddr.a[4] << 24) | (emc->conf.macaddr.a[5] << 16); + emc->regs[REG_CAML_BASE] = value; } static void npcm7xx_emc_reset(DeviceState *dev) @@ -432,13 +444,25 @@ static bool emc_receive_filter1(NPCM7xxEMCState *emc, const uint8_t *buf, } case ETH_PKT_UCAST: { bool matches; + uint32_t value; + struct MACAddr mac; if (emc->regs[REG_CAMCMR] & REG_CAMCMR_AUP) { return true; } + + value = emc->regs[REG_CAMM_BASE]; + mac.a[0] = value >> 24; + mac.a[1] = value >> 16; + mac.a[2] = value >> 8; + mac.a[3] = value >> 0; + value = emc->regs[REG_CAML_BASE]; + mac.a[4] = value >> 24; + mac.a[5] = value >> 16; + matches = ((emc->regs[REG_CAMCMR] & REG_CAMCMR_ECMP) && /* We only support one CAM register, CAM0. */ (emc->regs[REG_CAMEN] & (1 << 0)) && - memcmp(buf, emc->conf.macaddr.a, ETH_ALEN) == 0); + memcmp(buf, mac.a, ETH_ALEN) == 0); if (emc->regs[REG_CAMCMR] & REG_CAMCMR_CCAM) { *fail_reason = "MACADDR matched, comparison complemented"; return !matches; @@ -661,15 +685,9 @@ static void npcm7xx_emc_write(void *opaque, hwaddr offset, break; case REG_CAMM_BASE + 0: emc->regs[reg] = value; - emc->conf.macaddr.a[0] = value >> 24; - emc->conf.macaddr.a[1] = value >> 16; - emc->conf.macaddr.a[2] = value >> 8; - emc->conf.macaddr.a[3] = value >> 0; break; case REG_CAML_BASE + 0: emc->regs[reg] = value; - emc->conf.macaddr.a[4] = value >> 24; - emc->conf.macaddr.a[5] = value >> 16; break; case REG_MCMDR: { uint32_t prev; @@ -803,7 +821,8 @@ static void npcm7xx_emc_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&emc->conf.macaddr); emc->nic = qemu_new_nic(&net_npcm7xx_emc_info, &emc->conf, - object_get_typename(OBJECT(dev)), dev->id, emc); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, emc); qemu_format_nic_info_str(qemu_get_queue(emc->nic), emc->conf.macaddr.a); } @@ -818,7 +837,7 @@ static const VMStateDescription vmstate_npcm7xx_emc = { .name = TYPE_NPCM7XX_EMC, .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(emc_num, NPCM7xxEMCState), VMSTATE_UINT32_ARRAY(regs, NPCM7xxEMCState, NPCM7XX_NUM_EMC_REGS), VMSTATE_BOOL(tx_active, NPCM7xxEMCState), diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c new file mode 100644 index 0000000000..1b71e2526e --- /dev/null +++ b/hw/net/npcm_gmac.c @@ -0,0 +1,942 @@ +/* + * Nuvoton NPCM7xx/8xx GMAC Module + * + * Copyright 2024 Google LLC + * Authors: + * Hao Wu + * Nabih Estefan + * + * 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. + * + * Unsupported/unimplemented features: + * - MII is not implemented, MII_ADDR.BUSY and MII_DATA always return zero + * - Precision timestamp (PTP) is not implemented. + */ + +#include "qemu/osdep.h" + +#include "hw/registerfields.h" +#include "hw/net/mii.h" +#include "hw/net/npcm_gmac.h" +#include "migration/vmstate.h" +#include "net/checksum.h" +#include "net/eth.h" +#include "net/net.h" +#include "qemu/cutils.h" +#include "qemu/log.h" +#include "qemu/units.h" +#include "sysemu/dma.h" +#include "trace.h" + +REG32(NPCM_DMA_BUS_MODE, 0x1000) +REG32(NPCM_DMA_XMT_POLL_DEMAND, 0x1004) +REG32(NPCM_DMA_RCV_POLL_DEMAND, 0x1008) +REG32(NPCM_DMA_RX_BASE_ADDR, 0x100c) +REG32(NPCM_DMA_TX_BASE_ADDR, 0x1010) +REG32(NPCM_DMA_STATUS, 0x1014) +REG32(NPCM_DMA_CONTROL, 0x1018) +REG32(NPCM_DMA_INTR_ENA, 0x101c) +REG32(NPCM_DMA_MISSED_FRAME_CTR, 0x1020) +REG32(NPCM_DMA_HOST_TX_DESC, 0x1048) +REG32(NPCM_DMA_HOST_RX_DESC, 0x104c) +REG32(NPCM_DMA_CUR_TX_BUF_ADDR, 0x1050) +REG32(NPCM_DMA_CUR_RX_BUF_ADDR, 0x1054) +REG32(NPCM_DMA_HW_FEATURE, 0x1058) + +REG32(NPCM_GMAC_MAC_CONFIG, 0x0) +REG32(NPCM_GMAC_FRAME_FILTER, 0x4) +REG32(NPCM_GMAC_HASH_HIGH, 0x8) +REG32(NPCM_GMAC_HASH_LOW, 0xc) +REG32(NPCM_GMAC_MII_ADDR, 0x10) +REG32(NPCM_GMAC_MII_DATA, 0x14) +REG32(NPCM_GMAC_FLOW_CTRL, 0x18) +REG32(NPCM_GMAC_VLAN_FLAG, 0x1c) +REG32(NPCM_GMAC_VERSION, 0x20) +REG32(NPCM_GMAC_WAKEUP_FILTER, 0x28) +REG32(NPCM_GMAC_PMT, 0x2c) +REG32(NPCM_GMAC_LPI_CTRL, 0x30) +REG32(NPCM_GMAC_TIMER_CTRL, 0x34) +REG32(NPCM_GMAC_INT_STATUS, 0x38) +REG32(NPCM_GMAC_INT_MASK, 0x3c) +REG32(NPCM_GMAC_MAC0_ADDR_HI, 0x40) +REG32(NPCM_GMAC_MAC0_ADDR_LO, 0x44) +REG32(NPCM_GMAC_MAC1_ADDR_HI, 0x48) +REG32(NPCM_GMAC_MAC1_ADDR_LO, 0x4c) +REG32(NPCM_GMAC_MAC2_ADDR_HI, 0x50) +REG32(NPCM_GMAC_MAC2_ADDR_LO, 0x54) +REG32(NPCM_GMAC_MAC3_ADDR_HI, 0x58) +REG32(NPCM_GMAC_MAC3_ADDR_LO, 0x5c) +REG32(NPCM_GMAC_RGMII_STATUS, 0xd8) +REG32(NPCM_GMAC_WATCHDOG, 0xdc) +REG32(NPCM_GMAC_PTP_TCR, 0x700) +REG32(NPCM_GMAC_PTP_SSIR, 0x704) +REG32(NPCM_GMAC_PTP_STSR, 0x708) +REG32(NPCM_GMAC_PTP_STNSR, 0x70c) +REG32(NPCM_GMAC_PTP_STSUR, 0x710) +REG32(NPCM_GMAC_PTP_STNSUR, 0x714) +REG32(NPCM_GMAC_PTP_TAR, 0x718) +REG32(NPCM_GMAC_PTP_TTSR, 0x71c) + +/* Register Fields */ +#define NPCM_GMAC_MII_ADDR_BUSY BIT(0) +#define NPCM_GMAC_MII_ADDR_WRITE BIT(1) +#define NPCM_GMAC_MII_ADDR_GR(rv) extract16((rv), 6, 5) +#define NPCM_GMAC_MII_ADDR_PA(rv) extract16((rv), 11, 5) + +#define NPCM_GMAC_INT_MASK_LPIIM BIT(10) +#define NPCM_GMAC_INT_MASK_PMTM BIT(3) +#define NPCM_GMAC_INT_MASK_RGIM BIT(0) + +#define NPCM_DMA_BUS_MODE_SWR BIT(0) + +static const uint32_t npcm_gmac_cold_reset_values[NPCM_GMAC_NR_REGS] = { + /* Reduce version to 3.2 so that the kernel can enable interrupt. */ + [R_NPCM_GMAC_VERSION] = 0x00001032, + [R_NPCM_GMAC_TIMER_CTRL] = 0x03e80000, + [R_NPCM_GMAC_MAC0_ADDR_HI] = 0x8000ffff, + [R_NPCM_GMAC_MAC0_ADDR_LO] = 0xffffffff, + [R_NPCM_GMAC_MAC1_ADDR_HI] = 0x0000ffff, + [R_NPCM_GMAC_MAC1_ADDR_LO] = 0xffffffff, + [R_NPCM_GMAC_MAC2_ADDR_HI] = 0x0000ffff, + [R_NPCM_GMAC_MAC2_ADDR_LO] = 0xffffffff, + [R_NPCM_GMAC_MAC3_ADDR_HI] = 0x0000ffff, + [R_NPCM_GMAC_MAC3_ADDR_LO] = 0xffffffff, + [R_NPCM_GMAC_PTP_TCR] = 0x00002000, + [R_NPCM_DMA_BUS_MODE] = 0x00020101, + [R_NPCM_DMA_HW_FEATURE] = 0x100d4f37, +}; + +static const uint16_t phy_reg_init[] = { + [MII_BMCR] = MII_BMCR_AUTOEN | MII_BMCR_FD | MII_BMCR_SPEED1000, + [MII_BMSR] = MII_BMSR_100TX_FD | MII_BMSR_100TX_HD | MII_BMSR_10T_FD | + MII_BMSR_10T_HD | MII_BMSR_EXTSTAT | MII_BMSR_AUTONEG | + MII_BMSR_LINK_ST | MII_BMSR_EXTCAP, + [MII_PHYID1] = 0x0362, + [MII_PHYID2] = 0x5e6a, + [MII_ANAR] = MII_ANAR_TXFD | MII_ANAR_TX | MII_ANAR_10FD | + MII_ANAR_10 | MII_ANAR_CSMACD, + [MII_ANLPAR] = MII_ANLPAR_ACK | MII_ANLPAR_PAUSE | + MII_ANLPAR_TXFD | MII_ANLPAR_TX | MII_ANLPAR_10FD | + MII_ANLPAR_10 | MII_ANLPAR_CSMACD, + [MII_ANER] = 0x64 | MII_ANER_NWAY, + [MII_ANNP] = 0x2001, + [MII_CTRL1000] = MII_CTRL1000_FULL, + [MII_STAT1000] = MII_STAT1000_FULL, + [MII_EXTSTAT] = 0x3000, /* 1000BASTE_T full-duplex capable */ +}; + +static void npcm_gmac_soft_reset(NPCMGMACState *gmac) +{ + memcpy(gmac->regs, npcm_gmac_cold_reset_values, + NPCM_GMAC_NR_REGS * sizeof(uint32_t)); + /* Clear reset bits */ + gmac->regs[R_NPCM_DMA_BUS_MODE] &= ~NPCM_DMA_BUS_MODE_SWR; +} + +static void gmac_phy_set_link(NPCMGMACState *gmac, bool active) +{ + /* Autonegotiation status mirrors link status. */ + if (active) { + gmac->phy_regs[0][MII_BMSR] |= (MII_BMSR_LINK_ST | MII_BMSR_AN_COMP); + } else { + gmac->phy_regs[0][MII_BMSR] &= ~(MII_BMSR_LINK_ST | MII_BMSR_AN_COMP); + } +} + +static bool gmac_can_receive(NetClientState *nc) +{ + NPCMGMACState *gmac = NPCM_GMAC(qemu_get_nic_opaque(nc)); + + /* If GMAC receive is disabled. */ + if (!(gmac->regs[R_NPCM_GMAC_MAC_CONFIG] & NPCM_GMAC_MAC_CONFIG_RX_EN)) { + return false; + } + + /* If GMAC DMA RX is stopped. */ + if (!(gmac->regs[R_NPCM_DMA_CONTROL] & NPCM_DMA_CONTROL_START_STOP_RX)) { + return false; + } + return true; +} + +/* + * Function that updates the GMAC IRQ + * It find the logical OR of the enabled bits for NIS (if enabled) + * It find the logical OR of the enabled bits for AIS (if enabled) + */ +static void gmac_update_irq(NPCMGMACState *gmac) +{ + /* + * Check if the normal interrupts summary is enabled + * if so, add the bits for the summary that are enabled + */ + if (gmac->regs[R_NPCM_DMA_INTR_ENA] & gmac->regs[R_NPCM_DMA_STATUS] & + (NPCM_DMA_INTR_ENAB_NIE_BITS)) { + gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_NIS; + } + /* + * Check if the abnormal interrupts summary is enabled + * if so, add the bits for the summary that are enabled + */ + if (gmac->regs[R_NPCM_DMA_INTR_ENA] & gmac->regs[R_NPCM_DMA_STATUS] & + (NPCM_DMA_INTR_ENAB_AIE_BITS)) { + gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_AIS; + } + + /* Get the logical OR of both normal and abnormal interrupts */ + int level = !!((gmac->regs[R_NPCM_DMA_STATUS] & + gmac->regs[R_NPCM_DMA_INTR_ENA] & + NPCM_DMA_STATUS_NIS) | + (gmac->regs[R_NPCM_DMA_STATUS] & + gmac->regs[R_NPCM_DMA_INTR_ENA] & + NPCM_DMA_STATUS_AIS)); + + /* Set the IRQ */ + trace_npcm_gmac_update_irq(DEVICE(gmac)->canonical_path, + gmac->regs[R_NPCM_DMA_STATUS], + gmac->regs[R_NPCM_DMA_INTR_ENA], + level); + qemu_set_irq(gmac->irq, level); +} + +static int gmac_read_rx_desc(dma_addr_t addr, struct NPCMGMACRxDesc *desc) +{ + if (dma_memory_read(&address_space_memory, addr, desc, + sizeof(*desc), MEMTXATTRS_UNSPECIFIED)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to read descriptor @ 0x%" + HWADDR_PRIx "\n", __func__, addr); + return -1; + } + desc->rdes0 = le32_to_cpu(desc->rdes0); + desc->rdes1 = le32_to_cpu(desc->rdes1); + desc->rdes2 = le32_to_cpu(desc->rdes2); + desc->rdes3 = le32_to_cpu(desc->rdes3); + return 0; +} + +static int gmac_write_rx_desc(dma_addr_t addr, struct NPCMGMACRxDesc *desc) +{ + struct NPCMGMACRxDesc le_desc; + le_desc.rdes0 = cpu_to_le32(desc->rdes0); + le_desc.rdes1 = cpu_to_le32(desc->rdes1); + le_desc.rdes2 = cpu_to_le32(desc->rdes2); + le_desc.rdes3 = cpu_to_le32(desc->rdes3); + if (dma_memory_write(&address_space_memory, addr, &le_desc, + sizeof(le_desc), MEMTXATTRS_UNSPECIFIED)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to write descriptor @ 0x%" + HWADDR_PRIx "\n", __func__, addr); + return -1; + } + return 0; +} + +static int gmac_read_tx_desc(dma_addr_t addr, struct NPCMGMACTxDesc *desc) +{ + if (dma_memory_read(&address_space_memory, addr, desc, + sizeof(*desc), MEMTXATTRS_UNSPECIFIED)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to read descriptor @ 0x%" + HWADDR_PRIx "\n", __func__, addr); + return -1; + } + desc->tdes0 = le32_to_cpu(desc->tdes0); + desc->tdes1 = le32_to_cpu(desc->tdes1); + desc->tdes2 = le32_to_cpu(desc->tdes2); + desc->tdes3 = le32_to_cpu(desc->tdes3); + return 0; +} + +static int gmac_write_tx_desc(dma_addr_t addr, struct NPCMGMACTxDesc *desc) +{ + struct NPCMGMACTxDesc le_desc; + le_desc.tdes0 = cpu_to_le32(desc->tdes0); + le_desc.tdes1 = cpu_to_le32(desc->tdes1); + le_desc.tdes2 = cpu_to_le32(desc->tdes2); + le_desc.tdes3 = cpu_to_le32(desc->tdes3); + if (dma_memory_write(&address_space_memory, addr, &le_desc, + sizeof(le_desc), MEMTXATTRS_UNSPECIFIED)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to write descriptor @ 0x%" + HWADDR_PRIx "\n", __func__, addr); + return -1; + } + return 0; +} + +static int gmac_rx_transfer_frame_to_buffer(uint32_t rx_buf_len, + uint32_t *left_frame, + uint32_t rx_buf_addr, + bool *eof_transferred, + const uint8_t **frame_ptr, + uint16_t *transferred) +{ + uint32_t to_transfer; + /* + * Check that buffer is bigger than the frame being transfered + * If bigger then transfer only whats left of frame + * Else, fill frame with all the content possible + */ + if (rx_buf_len >= *left_frame) { + to_transfer = *left_frame; + *eof_transferred = true; + } else { + to_transfer = rx_buf_len; + } + + /* write frame part to memory */ + if (dma_memory_write(&address_space_memory, (uint64_t) rx_buf_addr, + *frame_ptr, to_transfer, MEMTXATTRS_UNSPECIFIED)) { + return -1; + } + + /* update frame pointer and size of whats left of frame */ + *frame_ptr += to_transfer; + *left_frame -= to_transfer; + *transferred += to_transfer; + + return 0; +} + +static void gmac_dma_set_state(NPCMGMACState *gmac, int shift, uint32_t state) +{ + gmac->regs[R_NPCM_DMA_STATUS] = deposit32(gmac->regs[R_NPCM_DMA_STATUS], + shift, 3, state); +} + +static ssize_t gmac_receive(NetClientState *nc, const uint8_t *buf, size_t len) +{ + /* + * Comments have steps that relate to the + * receiving process steps in pg 386 + */ + NPCMGMACState *gmac = NPCM_GMAC(qemu_get_nic_opaque(nc)); + uint32_t left_frame = len; + const uint8_t *frame_ptr = buf; + uint32_t desc_addr; + uint32_t rx_buf_len, rx_buf_addr; + struct NPCMGMACRxDesc rx_desc; + uint16_t transferred = 0; + bool eof_transferred = false; + + trace_npcm_gmac_packet_receive(DEVICE(gmac)->canonical_path, len); + if (!gmac_can_receive(nc)) { + qemu_log_mask(LOG_GUEST_ERROR, "GMAC Currently is not able for Rx"); + return -1; + } + if (!gmac->regs[R_NPCM_DMA_HOST_RX_DESC]) { + gmac->regs[R_NPCM_DMA_HOST_RX_DESC] = + NPCM_DMA_HOST_RX_DESC_MASK(gmac->regs[R_NPCM_DMA_RX_BASE_ADDR]); + } + desc_addr = NPCM_DMA_HOST_RX_DESC_MASK(gmac->regs[R_NPCM_DMA_HOST_RX_DESC]); + + /* step 1 */ + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_RX_RUNNING_FETCHING_STATE); + trace_npcm_gmac_packet_desc_read(DEVICE(gmac)->canonical_path, desc_addr); + if (gmac_read_rx_desc(desc_addr, &rx_desc)) { + qemu_log_mask(LOG_GUEST_ERROR, "RX Descriptor @ 0x%x cant be read\n", + desc_addr); + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_RX_SUSPENDED_STATE); + return -1; + } + + /* step 2 */ + if (!(rx_desc.rdes0 & RX_DESC_RDES0_OWN)) { + qemu_log_mask(LOG_GUEST_ERROR, + "RX Descriptor @ 0x%x is owned by software\n", + desc_addr); + gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_RU; + gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_RI; + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_RX_SUSPENDED_STATE); + gmac_update_irq(gmac); + return len; + } + /* step 3 */ + /* + * TODO -- + * Implement all frame filtering and processing (with its own interrupts) + */ + trace_npcm_gmac_debug_desc_data(DEVICE(gmac)->canonical_path, &rx_desc, + rx_desc.rdes0, rx_desc.rdes1, rx_desc.rdes2, + rx_desc.rdes3); + /* Clear rdes0 for the incoming descriptor and set FS in first descriptor.*/ + rx_desc.rdes0 = RX_DESC_RDES0_FIRST_DESC_MASK; + + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_RX_RUNNING_TRANSFERRING_STATE); + + /* Pad the frame with FCS as the kernel driver will strip it away. */ + left_frame += ETH_FCS_LEN; + + /* repeat while we still have frame to transfer to memory */ + while (!eof_transferred) { + /* Return descriptor no matter what happens */ + rx_desc.rdes0 &= ~RX_DESC_RDES0_OWN; + /* Set the frame to be an IPv4/IPv6 frame. */ + rx_desc.rdes0 |= RX_DESC_RDES0_FRM_TYPE_MASK; + + /* step 4 */ + rx_buf_len = RX_DESC_RDES1_BFFR1_SZ_MASK(rx_desc.rdes1); + rx_buf_addr = rx_desc.rdes2; + gmac->regs[R_NPCM_DMA_CUR_RX_BUF_ADDR] = rx_buf_addr; + gmac_rx_transfer_frame_to_buffer(rx_buf_len, &left_frame, rx_buf_addr, + &eof_transferred, &frame_ptr, + &transferred); + + trace_npcm_gmac_packet_receiving_buffer(DEVICE(gmac)->canonical_path, + rx_buf_len, rx_buf_addr); + /* if we still have frame left and the second buffer is not chained */ + if (!(rx_desc.rdes1 & RX_DESC_RDES1_SEC_ADDR_CHND_MASK) && \ + !eof_transferred) { + /* repeat process from above on buffer 2 */ + rx_buf_len = RX_DESC_RDES1_BFFR2_SZ_MASK(rx_desc.rdes1); + rx_buf_addr = rx_desc.rdes3; + gmac->regs[R_NPCM_DMA_CUR_RX_BUF_ADDR] = rx_buf_addr; + gmac_rx_transfer_frame_to_buffer(rx_buf_len, &left_frame, + rx_buf_addr, &eof_transferred, + &frame_ptr, &transferred); + trace_npcm_gmac_packet_receiving_buffer( \ + DEVICE(gmac)->canonical_path, + rx_buf_len, rx_buf_addr); + } + /* update address for descriptor */ + gmac->regs[R_NPCM_DMA_HOST_RX_DESC] = rx_buf_addr; + /* Return descriptor */ + rx_desc.rdes0 &= ~RX_DESC_RDES0_OWN; + /* Update frame length transferred */ + rx_desc.rdes0 |= ((uint32_t)transferred) + << RX_DESC_RDES0_FRAME_LEN_SHIFT; + trace_npcm_gmac_debug_desc_data(DEVICE(gmac)->canonical_path, &rx_desc, + rx_desc.rdes0, rx_desc.rdes1, + rx_desc.rdes2, rx_desc.rdes3); + + /* step 5 */ + gmac_write_rx_desc(desc_addr, &rx_desc); + trace_npcm_gmac_debug_desc_data(DEVICE(gmac)->canonical_path, + &rx_desc, rx_desc.rdes0, + rx_desc.rdes1, rx_desc.rdes2, + rx_desc.rdes3); + /* read new descriptor into rx_desc if needed*/ + if (!eof_transferred) { + /* Get next descriptor address (chained or sequential) */ + if (rx_desc.rdes1 & RX_DESC_RDES1_RC_END_RING_MASK) { + desc_addr = gmac->regs[R_NPCM_DMA_RX_BASE_ADDR]; + } else if (rx_desc.rdes1 & RX_DESC_RDES1_SEC_ADDR_CHND_MASK) { + desc_addr = rx_desc.rdes3; + } else { + desc_addr += sizeof(rx_desc); + } + trace_npcm_gmac_packet_desc_read(DEVICE(gmac)->canonical_path, + desc_addr); + if (gmac_read_rx_desc(desc_addr, &rx_desc)) { + qemu_log_mask(LOG_GUEST_ERROR, + "RX Descriptor @ 0x%x cant be read\n", + desc_addr); + gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_RU; + gmac_update_irq(gmac); + return len; + } + + /* step 6 */ + if (!(rx_desc.rdes0 & RX_DESC_RDES0_OWN)) { + if (!(gmac->regs[R_NPCM_DMA_CONTROL] & \ + NPCM_DMA_CONTROL_FLUSH_MASK)) { + rx_desc.rdes0 |= RX_DESC_RDES0_DESC_ERR_MASK; + } + eof_transferred = true; + } + /* Clear rdes0 for the incoming descriptor */ + rx_desc.rdes0 = 0; + } + } + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_RX_RUNNING_CLOSING_STATE); + + rx_desc.rdes0 |= RX_DESC_RDES0_LAST_DESC_MASK; + if (!(rx_desc.rdes1 & RX_DESC_RDES1_DIS_INTR_COMP_MASK)) { + gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_RI; + gmac_update_irq(gmac); + } + trace_npcm_gmac_debug_desc_data(DEVICE(gmac)->canonical_path, &rx_desc, + rx_desc.rdes0, rx_desc.rdes1, rx_desc.rdes2, + rx_desc.rdes3); + + /* step 8 */ + gmac->regs[R_NPCM_DMA_CONTROL] |= NPCM_DMA_CONTROL_FLUSH_MASK; + + /* step 9 */ + trace_npcm_gmac_packet_received(DEVICE(gmac)->canonical_path, left_frame); + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_RX_RUNNING_WAITING_STATE); + gmac_write_rx_desc(desc_addr, &rx_desc); + + /* Get next descriptor address (chained or sequential) */ + if (rx_desc.rdes1 & RX_DESC_RDES1_RC_END_RING_MASK) { + desc_addr = gmac->regs[R_NPCM_DMA_RX_BASE_ADDR]; + } else if (rx_desc.rdes1 & RX_DESC_RDES1_SEC_ADDR_CHND_MASK) { + desc_addr = rx_desc.rdes3; + } else { + desc_addr += sizeof(rx_desc); + } + gmac->regs[R_NPCM_DMA_HOST_RX_DESC] = desc_addr; + return len; +} + +static int gmac_tx_get_csum(uint32_t tdes1) +{ + uint32_t mask = TX_DESC_TDES1_CHKSM_INS_CTRL_MASK(tdes1); + int csum = 0; + + if (likely(mask > 0)) { + csum |= CSUM_IP; + } + if (likely(mask > 1)) { + csum |= CSUM_TCP | CSUM_UDP; + } + + return csum; +} + +static void gmac_try_send_next_packet(NPCMGMACState *gmac) +{ + /* + * Comments about steps refer to steps for + * transmitting in page 384 of datasheet + */ + uint16_t tx_buffer_size = 2048; + g_autofree uint8_t *tx_send_buffer = g_malloc(tx_buffer_size); + uint32_t desc_addr; + struct NPCMGMACTxDesc tx_desc; + uint32_t tx_buf_addr, tx_buf_len; + uint16_t length = 0; + uint8_t *buf = tx_send_buffer; + uint32_t prev_buf_size = 0; + int csum = 0; + + /* steps 1&2 */ + if (!gmac->regs[R_NPCM_DMA_HOST_TX_DESC]) { + gmac->regs[R_NPCM_DMA_HOST_TX_DESC] = + NPCM_DMA_HOST_TX_DESC_MASK(gmac->regs[R_NPCM_DMA_TX_BASE_ADDR]); + } + desc_addr = gmac->regs[R_NPCM_DMA_HOST_TX_DESC]; + + while (true) { + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_TX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_TX_RUNNING_FETCHING_STATE); + if (gmac_read_tx_desc(desc_addr, &tx_desc)) { + qemu_log_mask(LOG_GUEST_ERROR, + "TX Descriptor @ 0x%x can't be read\n", + desc_addr); + return; + } + /* step 3 */ + + trace_npcm_gmac_packet_desc_read(DEVICE(gmac)->canonical_path, + desc_addr); + trace_npcm_gmac_debug_desc_data(DEVICE(gmac)->canonical_path, &tx_desc, + tx_desc.tdes0, tx_desc.tdes1, tx_desc.tdes2, tx_desc.tdes3); + + /* 1 = DMA Owned, 0 = Software Owned */ + if (!(tx_desc.tdes0 & TX_DESC_TDES0_OWN)) { + qemu_log_mask(LOG_GUEST_ERROR, + "TX Descriptor @ 0x%x is owned by software\n", + desc_addr); + gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_TU; + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_TX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_TX_SUSPENDED_STATE); + gmac_update_irq(gmac); + return; + } + + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_TX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_TX_RUNNING_READ_STATE); + /* Give the descriptor back regardless of what happens. */ + tx_desc.tdes0 &= ~TX_DESC_TDES0_OWN; + + if (tx_desc.tdes1 & TX_DESC_TDES1_FIRST_SEG_MASK) { + csum = gmac_tx_get_csum(tx_desc.tdes1); + } + + /* step 4 */ + tx_buf_addr = tx_desc.tdes2; + gmac->regs[R_NPCM_DMA_CUR_TX_BUF_ADDR] = tx_buf_addr; + tx_buf_len = TX_DESC_TDES1_BFFR1_SZ_MASK(tx_desc.tdes1); + buf = &tx_send_buffer[prev_buf_size]; + + if ((prev_buf_size + tx_buf_len) > sizeof(buf)) { + tx_buffer_size = prev_buf_size + tx_buf_len; + tx_send_buffer = g_realloc(tx_send_buffer, tx_buffer_size); + buf = &tx_send_buffer[prev_buf_size]; + } + + /* step 5 */ + if (dma_memory_read(&address_space_memory, tx_buf_addr, buf, + tx_buf_len, MEMTXATTRS_UNSPECIFIED)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to read packet @ 0x%x\n", + __func__, tx_buf_addr); + return; + } + length += tx_buf_len; + prev_buf_size += tx_buf_len; + + /* If not chained we'll have a second buffer. */ + if (!(tx_desc.tdes1 & TX_DESC_TDES1_SEC_ADDR_CHND_MASK)) { + tx_buf_addr = tx_desc.tdes3; + gmac->regs[R_NPCM_DMA_CUR_TX_BUF_ADDR] = tx_buf_addr; + tx_buf_len = TX_DESC_TDES1_BFFR2_SZ_MASK(tx_desc.tdes1); + buf = &tx_send_buffer[prev_buf_size]; + + if ((prev_buf_size + tx_buf_len) > sizeof(buf)) { + tx_buffer_size = prev_buf_size + tx_buf_len; + tx_send_buffer = g_realloc(tx_send_buffer, tx_buffer_size); + buf = &tx_send_buffer[prev_buf_size]; + } + + if (dma_memory_read(&address_space_memory, tx_buf_addr, buf, + tx_buf_len, MEMTXATTRS_UNSPECIFIED)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to read packet @ 0x%x\n", + __func__, tx_buf_addr); + return; + } + length += tx_buf_len; + prev_buf_size += tx_buf_len; + } + if (tx_desc.tdes1 & TX_DESC_TDES1_LAST_SEG_MASK) { + net_checksum_calculate(tx_send_buffer, length, csum); + qemu_send_packet(qemu_get_queue(gmac->nic), tx_send_buffer, length); + trace_npcm_gmac_packet_sent(DEVICE(gmac)->canonical_path, length); + buf = tx_send_buffer; + length = 0; + } + + /* step 6 */ + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_TX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_TX_RUNNING_CLOSING_STATE); + gmac_write_tx_desc(desc_addr, &tx_desc); + if (tx_desc.tdes1 & TX_DESC_TDES1_TX_END_RING_MASK) { + desc_addr = gmac->regs[R_NPCM_DMA_TX_BASE_ADDR]; + } else if (tx_desc.tdes1 & TX_DESC_TDES1_SEC_ADDR_CHND_MASK) { + desc_addr = tx_desc.tdes3; + } else { + desc_addr += sizeof(tx_desc); + } + gmac->regs[R_NPCM_DMA_HOST_TX_DESC] = desc_addr; + + /* step 7 */ + if (tx_desc.tdes1 & TX_DESC_TDES1_INTERR_COMP_MASK) { + gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_TI; + gmac_update_irq(gmac); + } + } +} + +static void gmac_cleanup(NetClientState *nc) +{ + /* Nothing to do yet. */ +} + +static void gmac_set_link(NetClientState *nc) +{ + NPCMGMACState *gmac = qemu_get_nic_opaque(nc); + + trace_npcm_gmac_set_link(!nc->link_down); + gmac_phy_set_link(gmac, !nc->link_down); +} + +static void npcm_gmac_mdio_access(NPCMGMACState *gmac, uint16_t v) +{ + bool busy = v & NPCM_GMAC_MII_ADDR_BUSY; + uint8_t is_write; + uint8_t pa, gr; + uint16_t data; + + if (busy) { + is_write = v & NPCM_GMAC_MII_ADDR_WRITE; + pa = NPCM_GMAC_MII_ADDR_PA(v); + gr = NPCM_GMAC_MII_ADDR_GR(v); + /* Both pa and gr are 5 bits, so they are less than 32. */ + g_assert(pa < NPCM_GMAC_MAX_PHYS); + g_assert(gr < NPCM_GMAC_MAX_PHY_REGS); + + + if (v & NPCM_GMAC_MII_ADDR_WRITE) { + data = gmac->regs[R_NPCM_GMAC_MII_DATA]; + /* Clear reset bit for BMCR register */ + switch (gr) { + case MII_BMCR: + data &= ~MII_BMCR_RESET; + /* Autonegotiation is a W1C bit*/ + if (data & MII_BMCR_ANRESTART) { + /* Tells autonegotiation to not restart again */ + data &= ~MII_BMCR_ANRESTART; + } + if ((data & MII_BMCR_AUTOEN) && + !(gmac->phy_regs[pa][MII_BMSR] & MII_BMSR_AN_COMP)) { + /* sets autonegotiation as complete */ + gmac->phy_regs[pa][MII_BMSR] |= MII_BMSR_AN_COMP; + /* Resolve AN automatically->need to set this */ + gmac->phy_regs[0][MII_ANLPAR] = 0x0000; + } + } + gmac->phy_regs[pa][gr] = data; + } else { + data = gmac->phy_regs[pa][gr]; + gmac->regs[R_NPCM_GMAC_MII_DATA] = data; + } + trace_npcm_gmac_mdio_access(DEVICE(gmac)->canonical_path, is_write, pa, + gr, data); + } + gmac->regs[R_NPCM_GMAC_MII_ADDR] = v & ~NPCM_GMAC_MII_ADDR_BUSY; +} + +static uint64_t npcm_gmac_read(void *opaque, hwaddr offset, unsigned size) +{ + NPCMGMACState *gmac = opaque; + uint32_t v = 0; + + switch (offset) { + /* Write only registers */ + case A_NPCM_DMA_XMT_POLL_DEMAND: + case A_NPCM_DMA_RCV_POLL_DEMAND: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Read of write-only reg: offset: 0x%04" HWADDR_PRIx + "\n", DEVICE(gmac)->canonical_path, offset); + break; + + default: + v = gmac->regs[offset / sizeof(uint32_t)]; + } + + trace_npcm_gmac_reg_read(DEVICE(gmac)->canonical_path, offset, v); + return v; +} + +static void npcm_gmac_write(void *opaque, hwaddr offset, + uint64_t v, unsigned size) +{ + NPCMGMACState *gmac = opaque; + + trace_npcm_gmac_reg_write(DEVICE(gmac)->canonical_path, offset, v); + + switch (offset) { + /* Read only registers */ + case A_NPCM_GMAC_VERSION: + case A_NPCM_GMAC_INT_STATUS: + case A_NPCM_GMAC_RGMII_STATUS: + case A_NPCM_GMAC_PTP_STSR: + case A_NPCM_GMAC_PTP_STNSR: + case A_NPCM_DMA_MISSED_FRAME_CTR: + case A_NPCM_DMA_HOST_TX_DESC: + case A_NPCM_DMA_HOST_RX_DESC: + case A_NPCM_DMA_CUR_TX_BUF_ADDR: + case A_NPCM_DMA_CUR_RX_BUF_ADDR: + case A_NPCM_DMA_HW_FEATURE: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Write of read-only reg: offset: 0x%04" HWADDR_PRIx + ", value: 0x%04" PRIx64 "\n", + DEVICE(gmac)->canonical_path, offset, v); + break; + + case A_NPCM_GMAC_MAC_CONFIG: + gmac->regs[offset / sizeof(uint32_t)] = v; + break; + + case A_NPCM_GMAC_MII_ADDR: + npcm_gmac_mdio_access(gmac, v); + break; + + case A_NPCM_GMAC_MAC0_ADDR_HI: + gmac->regs[offset / sizeof(uint32_t)] = v; + gmac->conf.macaddr.a[0] = v >> 8; + gmac->conf.macaddr.a[1] = v >> 0; + break; + + case A_NPCM_GMAC_MAC0_ADDR_LO: + gmac->regs[offset / sizeof(uint32_t)] = v; + gmac->conf.macaddr.a[2] = v >> 24; + gmac->conf.macaddr.a[3] = v >> 16; + gmac->conf.macaddr.a[4] = v >> 8; + gmac->conf.macaddr.a[5] = v >> 0; + break; + + case A_NPCM_GMAC_MAC1_ADDR_HI: + case A_NPCM_GMAC_MAC1_ADDR_LO: + case A_NPCM_GMAC_MAC2_ADDR_HI: + case A_NPCM_GMAC_MAC2_ADDR_LO: + case A_NPCM_GMAC_MAC3_ADDR_HI: + case A_NPCM_GMAC_MAC3_ADDR_LO: + gmac->regs[offset / sizeof(uint32_t)] = v; + qemu_log_mask(LOG_UNIMP, + "%s: Only MAC Address 0 is supported. This request " + "is ignored.\n", DEVICE(gmac)->canonical_path); + break; + + case A_NPCM_DMA_BUS_MODE: + gmac->regs[offset / sizeof(uint32_t)] = v; + if (v & NPCM_DMA_BUS_MODE_SWR) { + npcm_gmac_soft_reset(gmac); + } + break; + + case A_NPCM_DMA_RCV_POLL_DEMAND: + /* We dont actually care about the value */ + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_RX_RUNNING_WAITING_STATE); + break; + + case A_NPCM_DMA_XMT_POLL_DEMAND: + /* We dont actually care about the value */ + gmac_try_send_next_packet(gmac); + break; + + case A_NPCM_DMA_CONTROL: + gmac->regs[offset / sizeof(uint32_t)] = v; + if (v & NPCM_DMA_CONTROL_START_STOP_TX) { + gmac_try_send_next_packet(gmac); + } else { + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_TX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_TX_STOPPED_STATE); + } + if (v & NPCM_DMA_CONTROL_START_STOP_RX) { + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_RX_RUNNING_WAITING_STATE); + qemu_flush_queued_packets(qemu_get_queue(gmac->nic)); + } else { + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_RX_STOPPED_STATE); + } + break; + + case A_NPCM_DMA_STATUS: + /* Check that RO bits are not written to */ + if (NPCM_DMA_STATUS_RO_MASK(v)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Write of read-only bits of reg: offset: 0x%04" + HWADDR_PRIx ", value: 0x%04" PRIx64 "\n", + DEVICE(gmac)->canonical_path, offset, v); + } + /* for W1C bits, implement W1C */ + gmac->regs[offset / sizeof(uint32_t)] &= ~NPCM_DMA_STATUS_W1C_MASK(v); + if (v & NPCM_DMA_STATUS_RU) { + /* Clearing RU bit indicates descriptor is owned by DMA again. */ + gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, + NPCM_DMA_STATUS_RX_RUNNING_WAITING_STATE); + qemu_flush_queued_packets(qemu_get_queue(gmac->nic)); + } + break; + + default: + gmac->regs[offset / sizeof(uint32_t)] = v; + break; + } + + gmac_update_irq(gmac); +} + +static void npcm_gmac_reset(DeviceState *dev) +{ + NPCMGMACState *gmac = NPCM_GMAC(dev); + + npcm_gmac_soft_reset(gmac); + memcpy(gmac->phy_regs[0], phy_reg_init, sizeof(phy_reg_init)); + + trace_npcm_gmac_reset(DEVICE(gmac)->canonical_path, + gmac->phy_regs[0][MII_BMSR]); +} + +static NetClientInfo net_npcm_gmac_info = { + .type = NET_CLIENT_DRIVER_NIC, + .size = sizeof(NICState), + .can_receive = gmac_can_receive, + .receive = gmac_receive, + .cleanup = gmac_cleanup, + .link_status_changed = gmac_set_link, +}; + +static const struct MemoryRegionOps npcm_gmac_ops = { + .read = npcm_gmac_read, + .write = npcm_gmac_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void npcm_gmac_realize(DeviceState *dev, Error **errp) +{ + NPCMGMACState *gmac = NPCM_GMAC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&gmac->iomem, OBJECT(gmac), &npcm_gmac_ops, gmac, + TYPE_NPCM_GMAC, 8 * KiB); + sysbus_init_mmio(sbd, &gmac->iomem); + sysbus_init_irq(sbd, &gmac->irq); + + qemu_macaddr_default_if_unset(&gmac->conf.macaddr); + + gmac->nic = qemu_new_nic(&net_npcm_gmac_info, &gmac->conf, TYPE_NPCM_GMAC, + dev->id, &dev->mem_reentrancy_guard, gmac); + qemu_format_nic_info_str(qemu_get_queue(gmac->nic), gmac->conf.macaddr.a); + gmac->regs[R_NPCM_GMAC_MAC0_ADDR_HI] = (gmac->conf.macaddr.a[0] << 8) + \ + gmac->conf.macaddr.a[1]; + gmac->regs[R_NPCM_GMAC_MAC0_ADDR_LO] = (gmac->conf.macaddr.a[2] << 24) + \ + (gmac->conf.macaddr.a[3] << 16) + \ + (gmac->conf.macaddr.a[4] << 8) + \ + gmac->conf.macaddr.a[5]; +} + +static void npcm_gmac_unrealize(DeviceState *dev) +{ + NPCMGMACState *gmac = NPCM_GMAC(dev); + + qemu_del_nic(gmac->nic); +} + +static const VMStateDescription vmstate_npcm_gmac = { + .name = TYPE_NPCM_GMAC, + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, NPCMGMACState, NPCM_GMAC_NR_REGS), + VMSTATE_END_OF_LIST(), + }, +}; + +static Property npcm_gmac_properties[] = { + DEFINE_NIC_PROPERTIES(NPCMGMACState, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void npcm_gmac_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); + dc->desc = "NPCM GMAC Controller"; + dc->realize = npcm_gmac_realize; + dc->unrealize = npcm_gmac_unrealize; + dc->reset = npcm_gmac_reset; + dc->vmsd = &vmstate_npcm_gmac; + device_class_set_props(dc, npcm_gmac_properties); +} + +static const TypeInfo npcm_gmac_types[] = { + { + .name = TYPE_NPCM_GMAC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NPCMGMACState), + .class_init = npcm_gmac_class_init, + }, +}; +DEFINE_TYPES(npcm_gmac_types) diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c index 0b3dc3146e..f96d6ea2cc 100644 --- a/hw/net/opencores_eth.c +++ b/hw/net/opencores_eth.c @@ -732,7 +732,8 @@ static void sysbus_open_eth_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->irq); s->nic = qemu_new_nic(&net_open_eth_info, &s->conf, - object_get_typename(OBJECT(s)), dev->id, s); + object_get_typename(OBJECT(s)), dev->id, + &dev->mem_reentrancy_guard, s); } static void qdev_open_eth_reset(DeviceState *dev) diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index 95d27102aa..fe1a845b2b 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -29,7 +29,7 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "net/net.h" @@ -147,7 +147,7 @@ static const VMStateDescription vmstate_pci_pcnet = { .name = "pcnet", .version_id = 3, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PCIPCNetState), VMSTATE_STRUCT(state, PCIPCNetState, 0, vmstate_pcnet, PCNetState), VMSTATE_END_OF_LIST() diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c index dcd3fc4948..ad675ab29d 100644 --- a/hw/net/pcnet.c +++ b/hw/net/pcnet.c @@ -370,7 +370,7 @@ static inline void pcnet_rmd_load(PCNetState *s, struct pcnet_RMD *rmd, uint32_t rbadr; int16_t buf_length; int16_t msg_length; - } rda; + } rda; s->phys_mem_read(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0); rmd->rbadr = le32_to_cpu(rda.rbadr) & 0xffffff; rmd->buf_length = le16_to_cpu(rda.buf_length); @@ -524,77 +524,77 @@ static inline void pcnet_rmd_store(PCNetState *s, struct pcnet_RMD *rmd, be16_to_cpu(hdr->ether_type)); \ } while (0) -#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff]) +#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff]) /* generated using the AUTODIN II polynomial - * x^32 + x^26 + x^23 + x^22 + x^16 + - * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 + * x^32 + x^26 + x^23 + x^22 + x^16 + + * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 */ static const uint32_t crctab[256] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, }; static inline int padr_match(PCNetState *s, const uint8_t *buf, int size) @@ -632,7 +632,7 @@ static inline int ladr_match(PCNetState *s, const uint8_t *buf, int size) { struct qemu_ether_header *hdr = (void *)buf; if ((*(hdr->ether_dhost)&0x01) && - ((uint64_t *)&s->csr[8])[0] != 0LL) { + (s->csr[8] | s->csr[9] | s->csr[10] | s->csr[11]) != 0) { uint8_t ladr[8] = { s->csr[8] & 0xff, s->csr[8] >> 8, s->csr[9] & 0xff, s->csr[9] >> 8, @@ -908,11 +908,11 @@ static void pcnet_rdte_poll(PCNetState *s) s->csr[37] = nnrd >> 16; #ifdef PCNET_DEBUG if (bad) { - printf("pcnet: BAD RMD RECORDS AFTER 0x" TARGET_FMT_plx "\n", + printf("pcnet: BAD RMD RECORDS AFTER 0x" HWADDR_FMT_plx "\n", crda); } } else { - printf("pcnet: BAD RMD RDA=0x" TARGET_FMT_plx "\n", crda); + printf("pcnet: BAD RMD RDA=0x" HWADDR_FMT_plx "\n", crda); #endif } } @@ -987,7 +987,6 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) { PCNetState *s = qemu_get_nic_opaque(nc); int is_padr = 0, is_bcast = 0, is_ladr = 0; - uint8_t buf1[60]; int remaining; int crc_err = 0; size_t size = size_; @@ -1000,14 +999,6 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) printf("pcnet_receive size=%zu\n", size); #endif - /* if too small buffer, then expand it */ - if (size < MIN_BUF_SIZE) { - memcpy(buf1, buf, size); - memset(buf1 + size, 0, MIN_BUF_SIZE - size); - buf = buf1; - size = MIN_BUF_SIZE; - } - if (CSR_PROM(s) || (is_padr=padr_match(s, buf, size)) || (is_bcast=padr_bcast(s, buf, size)) @@ -1691,7 +1682,7 @@ const VMStateDescription vmstate_pcnet = { .name = "pcnet", .version_id = 3, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(rap, PCNetState), VMSTATE_INT32(isr, PCNetState), VMSTATE_INT32(lnkst, PCNetState), @@ -1718,7 +1709,8 @@ void pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info) s->poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pcnet_poll_timer, s); qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s); + s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), + dev->id, &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); /* Initialize the PROM */ diff --git a/hw/net/pcnet.h b/hw/net/pcnet.h index f49b213c57..eb7f46aab3 100644 --- a/hw/net/pcnet.h +++ b/hw/net/pcnet.h @@ -4,8 +4,8 @@ #define PCNET_IOPORT_SIZE 0x20 #define PCNET_PNPMMIO_SIZE 0x20 -#define PCNET_LOOPTEST_CRC 1 -#define PCNET_LOOPTEST_NOCRC 2 +#define PCNET_LOOPTEST_CRC 1 +#define PCNET_LOOPTEST_NOCRC 2 #include "exec/memory.h" #include "hw/irq.h" diff --git a/hw/net/rocker/qmp-norocker.c b/hw/net/rocker/qmp-norocker.c index 5ef4f9324c..f6c1196a24 100644 --- a/hw/net/rocker/qmp-norocker.c +++ b/hw/net/rocker/qmp-norocker.c @@ -1,6 +1,5 @@ /* - * QMP Target options - Commands handled based on a target config - * versus a host config + * QMP command stubs * * Copyright (c) 2015 David Ahern * @@ -18,17 +17,16 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qapi-commands-rocker.h" -#include "qapi/qmp/qerror.h" RockerSwitch *qmp_query_rocker(const char *name, Error **errp) { - error_setg(errp, QERR_FEATURE_DISABLED, "rocker"); + error_setg(errp, "rocker %s not found", name); return NULL; }; RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp) { - error_setg(errp, QERR_FEATURE_DISABLED, "rocker"); + error_setg(errp, "rocker %s not found", name); return NULL; }; @@ -37,7 +35,7 @@ RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name, uint32_t tbl_id, Error **errp) { - error_setg(errp, QERR_FEATURE_DISABLED, "rocker"); + error_setg(errp, "rocker %s not found", name); return NULL; }; @@ -46,6 +44,6 @@ RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name, uint8_t type, Error **errp) { - error_setg(errp, QERR_FEATURE_DISABLED, "rocker"); + error_setg(errp, "rocker %s not found", name); return NULL; }; diff --git a/hw/net/rocker/rocker-hmp-cmds.c b/hw/net/rocker/rocker-hmp-cmds.c new file mode 100644 index 0000000000..197c6e28dc --- /dev/null +++ b/hw/net/rocker/rocker-hmp-cmds.c @@ -0,0 +1,316 @@ +/* + * Human Monitor Interface commands + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "net/eth.h" +#include "qapi/qapi-commands-rocker.h" +#include "qapi/qmp/qdict.h" + +void hmp_rocker(Monitor *mon, const QDict *qdict) +{ + const char *name = qdict_get_str(qdict, "name"); + RockerSwitch *rocker; + Error *err = NULL; + + rocker = qmp_query_rocker(name, &err); + if (hmp_handle_error(mon, err)) { + return; + } + + monitor_printf(mon, "name: %s\n", rocker->name); + monitor_printf(mon, "id: 0x%" PRIx64 "\n", rocker->id); + monitor_printf(mon, "ports: %d\n", rocker->ports); + + qapi_free_RockerSwitch(rocker); +} + +void hmp_rocker_ports(Monitor *mon, const QDict *qdict) +{ + RockerPortList *list, *port; + const char *name = qdict_get_str(qdict, "name"); + Error *err = NULL; + + list = qmp_query_rocker_ports(name, &err); + if (hmp_handle_error(mon, err)) { + return; + } + + monitor_printf(mon, " ena/ speed/ auto\n"); + monitor_printf(mon, " port link duplex neg?\n"); + + for (port = list; port; port = port->next) { + monitor_printf(mon, "%10s %-4s %-3s %2s %s\n", + port->value->name, + port->value->enabled ? port->value->link_up ? + "up" : "down" : "!ena", + port->value->speed == 10000 ? "10G" : "??", + port->value->duplex ? "FD" : "HD", + port->value->autoneg ? "Yes" : "No"); + } + + qapi_free_RockerPortList(list); +} + +void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict) +{ + RockerOfDpaFlowList *list, *info; + const char *name = qdict_get_str(qdict, "name"); + uint32_t tbl_id = qdict_get_try_int(qdict, "tbl_id", -1); + Error *err = NULL; + + list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &err); + if (hmp_handle_error(mon, err)) { + return; + } + + monitor_printf(mon, "prio tbl hits key(mask) --> actions\n"); + + for (info = list; info; info = info->next) { + RockerOfDpaFlow *flow = info->value; + RockerOfDpaFlowKey *key = flow->key; + RockerOfDpaFlowMask *mask = flow->mask; + RockerOfDpaFlowAction *action = flow->action; + + if (flow->hits) { + monitor_printf(mon, "%-4d %-3d %-4" PRIu64, + key->priority, key->tbl_id, flow->hits); + } else { + monitor_printf(mon, "%-4d %-3d ", + key->priority, key->tbl_id); + } + + if (key->has_in_pport) { + monitor_printf(mon, " pport %d", key->in_pport); + if (mask->has_in_pport) { + monitor_printf(mon, "(0x%x)", mask->in_pport); + } + } + + if (key->has_vlan_id) { + monitor_printf(mon, " vlan %d", + key->vlan_id & VLAN_VID_MASK); + if (mask->has_vlan_id) { + monitor_printf(mon, "(0x%x)", mask->vlan_id); + } + } + + if (key->has_tunnel_id) { + monitor_printf(mon, " tunnel %d", key->tunnel_id); + if (mask->has_tunnel_id) { + monitor_printf(mon, "(0x%x)", mask->tunnel_id); + } + } + + if (key->has_eth_type) { + switch (key->eth_type) { + case 0x0806: + monitor_printf(mon, " ARP"); + break; + case 0x0800: + monitor_printf(mon, " IP"); + break; + case 0x86dd: + monitor_printf(mon, " IPv6"); + break; + case 0x8809: + monitor_printf(mon, " LACP"); + break; + case 0x88cc: + monitor_printf(mon, " LLDP"); + break; + default: + monitor_printf(mon, " eth type 0x%04x", key->eth_type); + break; + } + } + + if (key->eth_src) { + if ((strcmp(key->eth_src, "01:00:00:00:00:00") == 0) && + mask->eth_src && + (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " src "); + } else if ((strcmp(key->eth_src, "00:00:00:00:00:00") == 0) && + mask->eth_src && + (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " src "); + } else { + monitor_printf(mon, " src %s", key->eth_src); + if (mask->eth_src) { + monitor_printf(mon, "(%s)", mask->eth_src); + } + } + } + + if (key->eth_dst) { + if ((strcmp(key->eth_dst, "01:00:00:00:00:00") == 0) && + mask->eth_dst && + (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " dst "); + } else if ((strcmp(key->eth_dst, "00:00:00:00:00:00") == 0) && + mask->eth_dst && + (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " dst "); + } else { + monitor_printf(mon, " dst %s", key->eth_dst); + if (mask->eth_dst) { + monitor_printf(mon, "(%s)", mask->eth_dst); + } + } + } + + if (key->has_ip_proto) { + monitor_printf(mon, " proto %d", key->ip_proto); + if (mask->has_ip_proto) { + monitor_printf(mon, "(0x%x)", mask->ip_proto); + } + } + + if (key->has_ip_tos) { + monitor_printf(mon, " TOS %d", key->ip_tos); + if (mask->has_ip_tos) { + monitor_printf(mon, "(0x%x)", mask->ip_tos); + } + } + + if (key->ip_dst) { + monitor_printf(mon, " dst %s", key->ip_dst); + } + + if (action->has_goto_tbl || action->has_group_id || + action->has_new_vlan_id) { + monitor_printf(mon, " -->"); + } + + if (action->has_new_vlan_id) { + monitor_printf(mon, " apply new vlan %d", + ntohs(action->new_vlan_id)); + } + + if (action->has_group_id) { + monitor_printf(mon, " write group 0x%08x", action->group_id); + } + + if (action->has_goto_tbl) { + monitor_printf(mon, " goto tbl %d", action->goto_tbl); + } + + monitor_printf(mon, "\n"); + } + + qapi_free_RockerOfDpaFlowList(list); +} + +void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict) +{ + RockerOfDpaGroupList *list, *g; + const char *name = qdict_get_str(qdict, "name"); + uint8_t type = qdict_get_try_int(qdict, "type", 9); + Error *err = NULL; + + list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &err); + if (hmp_handle_error(mon, err)) { + return; + } + + monitor_printf(mon, "id (decode) --> buckets\n"); + + for (g = list; g; g = g->next) { + RockerOfDpaGroup *group = g->value; + bool set = false; + + monitor_printf(mon, "0x%08x", group->id); + + monitor_printf(mon, " (type %s", group->type == 0 ? "L2 interface" : + group->type == 1 ? "L2 rewrite" : + group->type == 2 ? "L3 unicast" : + group->type == 3 ? "L2 multicast" : + group->type == 4 ? "L2 flood" : + group->type == 5 ? "L3 interface" : + group->type == 6 ? "L3 multicast" : + group->type == 7 ? "L3 ECMP" : + group->type == 8 ? "L2 overlay" : + "unknown"); + + if (group->has_vlan_id) { + monitor_printf(mon, " vlan %d", group->vlan_id); + } + + if (group->has_pport) { + monitor_printf(mon, " pport %d", group->pport); + } + + if (group->has_index) { + monitor_printf(mon, " index %d", group->index); + } + + monitor_printf(mon, ") -->"); + + if (group->has_set_vlan_id && group->set_vlan_id) { + set = true; + monitor_printf(mon, " set vlan %d", + group->set_vlan_id & VLAN_VID_MASK); + } + + if (group->set_eth_src) { + if (!set) { + set = true; + monitor_printf(mon, " set"); + } + monitor_printf(mon, " src %s", group->set_eth_src); + } + + if (group->set_eth_dst) { + if (!set) { + monitor_printf(mon, " set"); + } + monitor_printf(mon, " dst %s", group->set_eth_dst); + } + + if (group->has_ttl_check && group->ttl_check) { + monitor_printf(mon, " check TTL"); + } + + if (group->has_group_id && group->group_id) { + monitor_printf(mon, " group id 0x%08x", group->group_id); + } + + if (group->has_pop_vlan && group->pop_vlan) { + monitor_printf(mon, " pop vlan"); + } + + if (group->has_out_pport) { + monitor_printf(mon, " out pport %d", group->out_pport); + } + + if (group->has_group_ids) { + struct uint32List *id; + + monitor_printf(mon, " groups ["); + for (id = group->group_ids; id; id = id->next) { + monitor_printf(mon, "0x%08x", id->value); + if (id->next) { + monitor_printf(mon, ","); + } + } + monitor_printf(mon, "]"); + } + + monitor_printf(mon, "\n"); + } + + qapi_free_RockerOfDpaGroupList(list); +} diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index 31f2340fb9..7ea8eb6ba5 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -16,7 +16,7 @@ */ #include "qemu/osdep.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" @@ -815,7 +815,7 @@ static void rocker_io_writel(void *opaque, hwaddr addr, uint32_t val) } break; default: - DPRINTF("not implemented dma reg write(l) addr=0x" TARGET_FMT_plx + DPRINTF("not implemented dma reg write(l) addr=0x" HWADDR_FMT_plx " val=0x%08x (ring %d, addr=0x%02x)\n", addr, val, index, offset); break; @@ -857,7 +857,7 @@ static void rocker_io_writel(void *opaque, hwaddr addr, uint32_t val) r->lower32 = 0; break; default: - DPRINTF("not implemented write(l) addr=0x" TARGET_FMT_plx + DPRINTF("not implemented write(l) addr=0x" HWADDR_FMT_plx " val=0x%08x\n", addr, val); break; } @@ -876,8 +876,8 @@ static void rocker_io_writeq(void *opaque, hwaddr addr, uint64_t val) desc_ring_set_base_addr(r->rings[index], val); break; default: - DPRINTF("not implemented dma reg write(q) addr=0x" TARGET_FMT_plx - " val=0x" TARGET_FMT_plx " (ring %d, offset=0x%02x)\n", + DPRINTF("not implemented dma reg write(q) addr=0x" HWADDR_FMT_plx + " val=0x" HWADDR_FMT_plx " (ring %d, offset=0x%02x)\n", addr, val, index, offset); break; } @@ -895,8 +895,8 @@ static void rocker_io_writeq(void *opaque, hwaddr addr, uint64_t val) rocker_port_phys_enable_write(r, val); break; default: - DPRINTF("not implemented write(q) addr=0x" TARGET_FMT_plx - " val=0x" TARGET_FMT_plx "\n", addr, val); + DPRINTF("not implemented write(q) addr=0x" HWADDR_FMT_plx + " val=0x" HWADDR_FMT_plx "\n", addr, val); break; } } @@ -987,8 +987,8 @@ static const char *rocker_reg_name(void *opaque, hwaddr addr) static void rocker_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - DPRINTF("Write %s addr " TARGET_FMT_plx - ", size %u, val " TARGET_FMT_plx "\n", + DPRINTF("Write %s addr " HWADDR_FMT_plx + ", size %u, val " HWADDR_FMT_plx "\n", rocker_reg_name(opaque, addr), addr, size, val); switch (size) { @@ -1010,7 +1010,7 @@ static uint64_t rocker_port_phys_link_status(Rocker *r) FpPort *port = r->fp_port[i]; if (fp_port_get_link_up(port)) { - status |= 1 << (i + 1); + status |= 1ULL << (i + 1); } } return status; @@ -1025,7 +1025,7 @@ static uint64_t rocker_port_phys_enable_read(Rocker *r) FpPort *port = r->fp_port[i]; if (fp_port_enabled(port)) { - ret |= 1 << (i + 1); + ret |= 1ULL << (i + 1); } } return ret; @@ -1060,7 +1060,7 @@ static uint32_t rocker_io_readl(void *opaque, hwaddr addr) ret = desc_ring_get_credits(r->rings[index]); break; default: - DPRINTF("not implemented dma reg read(l) addr=0x" TARGET_FMT_plx + DPRINTF("not implemented dma reg read(l) addr=0x" HWADDR_FMT_plx " (ring %d, addr=0x%02x)\n", addr, index, offset); ret = 0; break; @@ -1115,7 +1115,7 @@ static uint32_t rocker_io_readl(void *opaque, hwaddr addr) ret = (uint32_t)(r->switch_id >> 32); break; default: - DPRINTF("not implemented read(l) addr=0x" TARGET_FMT_plx "\n", addr); + DPRINTF("not implemented read(l) addr=0x" HWADDR_FMT_plx "\n", addr); ret = 0; break; } @@ -1136,7 +1136,7 @@ static uint64_t rocker_io_readq(void *opaque, hwaddr addr) ret = desc_ring_get_base_addr(r->rings[index]); break; default: - DPRINTF("not implemented dma reg read(q) addr=0x" TARGET_FMT_plx + DPRINTF("not implemented dma reg read(q) addr=0x" HWADDR_FMT_plx " (ring %d, addr=0x%02x)\n", addr, index, offset); ret = 0; break; @@ -1165,7 +1165,7 @@ static uint64_t rocker_io_readq(void *opaque, hwaddr addr) ret = r->switch_id; break; default: - DPRINTF("not implemented read(q) addr=0x" TARGET_FMT_plx "\n", addr); + DPRINTF("not implemented read(q) addr=0x" HWADDR_FMT_plx "\n", addr); ret = 0; break; } @@ -1174,7 +1174,7 @@ static uint64_t rocker_io_readq(void *opaque, hwaddr addr) static uint64_t rocker_mmio_read(void *opaque, hwaddr addr, unsigned size) { - DPRINTF("Read %s addr " TARGET_FMT_plx ", size %u\n", + DPRINTF("Read %s addr " HWADDR_FMT_plx ", size %u\n", rocker_reg_name(opaque, addr), addr, size); switch (size) { @@ -1212,24 +1212,14 @@ static void rocker_msix_vectors_unuse(Rocker *r, } } -static int rocker_msix_vectors_use(Rocker *r, - unsigned int num_vectors) +static void rocker_msix_vectors_use(Rocker *r, unsigned int num_vectors) { PCIDevice *dev = PCI_DEVICE(r); - int err; int i; for (i = 0; i < num_vectors; i++) { - err = msix_vector_use(dev, i); - if (err) { - goto rollback; - } + msix_vector_use(dev, i); } - return 0; - -rollback: - rocker_msix_vectors_unuse(r, i); - return err; } static int rocker_msix_init(Rocker *r, Error **errp) @@ -1247,16 +1237,9 @@ static int rocker_msix_init(Rocker *r, Error **errp) return err; } - err = rocker_msix_vectors_use(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports)); - if (err) { - goto err_msix_vectors_use; - } + rocker_msix_vectors_use(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports)); return 0; - -err_msix_vectors_use: - msix_uninit(dev, &r->msix_bar, &r->msix_bar); - return err; } static void rocker_msix_uninit(Rocker *r) diff --git a/hw/net/rocker/rocker_desc.c b/hw/net/rocker/rocker_desc.c index 01845f1157..675383db36 100644 --- a/hw/net/rocker/rocker_desc.c +++ b/hw/net/rocker/rocker_desc.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "net/net.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "rocker.h" #include "rocker_hw.h" @@ -104,7 +104,7 @@ static bool desc_ring_empty(DescRing *ring) bool desc_ring_set_base_addr(DescRing *ring, uint64_t base_addr) { if (base_addr & 0x7) { - DPRINTF("ERROR: ring[%d] desc base addr (0x" TARGET_FMT_plx + DPRINTF("ERROR: ring[%d] desc base addr (0x" HWADDR_FMT_plx ") not 8-byte aligned\n", ring->index, base_addr); return false; } diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c index cbeed65bd5..cb87ff752a 100644 --- a/hw/net/rocker/rocker_fp.c +++ b/hw/net/rocker/rocker_fp.c @@ -134,7 +134,7 @@ static ssize_t fp_port_receive_iov(NetClientState *nc, const struct iovec *iov, FpPort *port = qemu_get_nic_opaque(nc); /* If the port is disabled, we want to drop this pkt - * now rather than queing it for later. We don't want + * now rather than queueing it for later. We don't want * any stale pkts getting into the device when the port * transitions to enabled. */ @@ -241,8 +241,8 @@ FpPort *fp_port_alloc(Rocker *r, char *sw_name, port->conf.bootindex = -1; port->conf.peers = *peers; - port->nic = qemu_new_nic(&fp_port_info, &port->conf, - sw_name, NULL, port); + port->nic = qemu_new_nic(&fp_port_info, &port->conf, sw_name, NULL, + &DEVICE(r)->mem_reentrancy_guard, port); qemu_format_nic_info_str(qemu_get_queue(port->nic), port->conf.macaddr.a); diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c index b3b8c5bb6d..5e16056be6 100644 --- a/hw/net/rocker/rocker_of_dpa.c +++ b/hw/net/rocker/rocker_of_dpa.c @@ -1043,7 +1043,7 @@ static void of_dpa_flow_ig_tbl(OfDpaFlowContext *fc, uint32_t tbl_id) static ssize_t of_dpa_ig(World *world, uint32_t pport, const struct iovec *iov, int iovcnt) { - struct iovec iov_copy[iovcnt + 2]; + g_autofree struct iovec *iov_copy = g_new(struct iovec, iovcnt + 2); OfDpaFlowContext fc = { .of_dpa = world_private(world), .in_pport = pport, @@ -2348,23 +2348,19 @@ static void of_dpa_flow_fill(void *cookie, void *value, void *user_data) if (memcmp(key->eth.src.a, zero_mac.a, ETH_ALEN) || memcmp(mask->eth.src.a, zero_mac.a, ETH_ALEN)) { - nkey->has_eth_src = true; nkey->eth_src = qemu_mac_strdup_printf(key->eth.src.a); } - if (nkey->has_eth_src && memcmp(mask->eth.src.a, ff_mac.a, ETH_ALEN)) { - nmask->has_eth_src = true; + if (nkey->eth_src && memcmp(mask->eth.src.a, ff_mac.a, ETH_ALEN)) { nmask->eth_src = qemu_mac_strdup_printf(mask->eth.src.a); } if (memcmp(key->eth.dst.a, zero_mac.a, ETH_ALEN) || memcmp(mask->eth.dst.a, zero_mac.a, ETH_ALEN)) { - nkey->has_eth_dst = true; nkey->eth_dst = qemu_mac_strdup_printf(key->eth.dst.a); } - if (nkey->has_eth_dst && memcmp(mask->eth.dst.a, ff_mac.a, ETH_ALEN)) { - nmask->has_eth_dst = true; + if (nkey->eth_dst && memcmp(mask->eth.dst.a, ff_mac.a, ETH_ALEN)) { nmask->eth_dst = qemu_mac_strdup_printf(mask->eth.dst.a); } @@ -2400,7 +2396,6 @@ static void of_dpa_flow_fill(void *cookie, void *value, void *user_data) if (key->ipv4.addr.dst || mask->ipv4.addr.dst) { char *dst = inet_ntoa(*(struct in_addr *)&key->ipv4.addr.dst); int dst_len = of_dpa_mask2prefix(mask->ipv4.addr.dst); - nkey->has_ip_dst = true; nkey->ip_dst = g_strdup_printf("%s/%d", dst, dst_len); } break; @@ -2501,12 +2496,10 @@ static void of_dpa_group_fill(void *key, void *value, void *user_data) ngroup->set_vlan_id = ntohs(group->l2_rewrite.vlan_id); } if (memcmp(group->l2_rewrite.src_mac.a, zero_mac.a, ETH_ALEN)) { - ngroup->has_set_eth_src = true; ngroup->set_eth_src = qemu_mac_strdup_printf(group->l2_rewrite.src_mac.a); } if (memcmp(group->l2_rewrite.dst_mac.a, zero_mac.a, ETH_ALEN)) { - ngroup->has_set_eth_dst = true; ngroup->set_eth_dst = qemu_mac_strdup_printf(group->l2_rewrite.dst_mac.a); } @@ -2532,12 +2525,10 @@ static void of_dpa_group_fill(void *key, void *value, void *user_data) ngroup->set_vlan_id = ntohs(group->l3_unicast.vlan_id); } if (memcmp(group->l3_unicast.src_mac.a, zero_mac.a, ETH_ALEN)) { - ngroup->has_set_eth_src = true; ngroup->set_eth_src = qemu_mac_strdup_printf(group->l3_unicast.src_mac.a); } if (memcmp(group->l3_unicast.dst_mac.a, zero_mac.a, ETH_ALEN)) { - ngroup->has_set_eth_dst = true; ngroup->set_eth_dst = qemu_mac_strdup_printf(group->l3_unicast.dst_mac.a); } diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 6b65823b4b..897c86ec41 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -53,7 +53,7 @@ #include "qemu/osdep.h" #include -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "sysemu/dma.h" @@ -77,7 +77,6 @@ ( ( input ) & ( size - 1 ) ) #define ETHER_TYPE_LEN 2 -#define ETH_MTU 1500 #define VLAN_TCI_LEN 2 #define VLAN_HLEN (ETHER_TYPE_LEN + VLAN_TCI_LEN) @@ -101,7 +100,7 @@ enum RTL8139_registers { MAC0 = 0, /* Ethernet hardware address. */ MAR0 = 8, /* Multicast filter. */ TxStatus0 = 0x10,/* Transmit status (Four 32bit registers). C mode only */ - /* Dump Tally Conter control register(64bit). C+ mode only */ + /* Dump Tally Counter control register(64bit). C+ mode only */ TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */ RxBuf = 0x30, ChipCmd = 0x37, @@ -827,7 +826,6 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t uint32_t packet_header = 0; - uint8_t buf1[MIN_BUF_SIZE + VLAN_HLEN]; static const uint8_t broadcast_macaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -939,17 +937,6 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t } } - /* if too small buffer, then expand it - * Include some tailroom in case a vlan tag is later removed. */ - if (size < MIN_BUF_SIZE + VLAN_HLEN) { - memcpy(buf1, buf, size); - memset(buf1 + size, 0, MIN_BUF_SIZE + VLAN_HLEN - size); - buf = buf1; - if (size < MIN_BUF_SIZE) { - size = MIN_BUF_SIZE; - } - } - if (rtl8139_cp_receiver_enabled(s)) { if (!rtl8139_cp_rx_valid(s)) { @@ -1934,8 +1921,9 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) #define CP_TX_LS (1<<28) /* large send packet flag */ #define CP_TX_LGSEN (1<<27) -/* large send MSS mask, bits 16...25 */ -#define CP_TC_LGSEN_MSS_MASK ((1 << 12) - 1) +/* large send MSS mask, bits 16...26 */ +#define CP_TC_LGSEN_MSS_SHIFT 16 +#define CP_TC_LGSEN_MSS_MASK ((1 << 11) - 1) /* IP checksum offload flag */ #define CP_TX_IPCS (1<<18) @@ -2027,18 +2015,21 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) s->currCPlusTxDesc = 0; } + /* Build the Tx Status Descriptor */ + uint32_t tx_status = txdw0; + /* transfer ownership to target */ - txdw0 &= ~CP_TX_OWN; + tx_status &= ~CP_TX_OWN; /* reset error indicator bits */ - txdw0 &= ~CP_TX_STATUS_UNF; - txdw0 &= ~CP_TX_STATUS_TES; - txdw0 &= ~CP_TX_STATUS_OWC; - txdw0 &= ~CP_TX_STATUS_LNKF; - txdw0 &= ~CP_TX_STATUS_EXC; + tx_status &= ~CP_TX_STATUS_UNF; + tx_status &= ~CP_TX_STATUS_TES; + tx_status &= ~CP_TX_STATUS_OWC; + tx_status &= ~CP_TX_STATUS_LNKF; + tx_status &= ~CP_TX_STATUS_EXC; /* update ring data */ - val = cpu_to_le32(txdw0); + val = cpu_to_le32(tx_status); pci_dma_write(d, cplus_tx_ring_desc, (uint8_t *)&val, 4); /* Now decide if descriptor being processed is holding the last segment of packet */ @@ -2132,7 +2123,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) } ip_data_len -= hlen; - if (txdw0 & CP_TX_IPCS) + if (!(txdw0 & CP_TX_LGSEN) && (txdw0 & CP_TX_IPCS)) { DPRINTF("+++ C+ mode need IP checksum\n"); @@ -2149,14 +2140,17 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) goto skip_offload; } - int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK; + int large_send_mss = (txdw0 >> CP_TC_LGSEN_MSS_SHIFT) & + CP_TC_LGSEN_MSS_MASK; + if (large_send_mss == 0) { + goto skip_offload; + } - DPRINTF("+++ C+ mode offloaded task TSO MTU=%d IP data %d " - "frame data %d specified MSS=%d\n", ETH_MTU, + DPRINTF("+++ C+ mode offloaded task TSO IP data %d " + "frame data %d specified MSS=%d\n", ip_data_len, saved_size - ETH_HLEN, large_send_mss); int tcp_send_offset = 0; - int send_count = 0; /* maximum IP header length is 60 bytes */ uint8_t saved_ip_header[60]; @@ -2178,25 +2172,22 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) goto skip_offload; } - /* ETH_MTU = ip header len + tcp header len + payload */ int tcp_data_len = ip_data_len - tcp_hlen; - int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen; DPRINTF("+++ C+ mode TSO IP data len %d TCP hlen %d TCP " - "data len %d TCP chunk size %d\n", ip_data_len, - tcp_hlen, tcp_data_len, tcp_chunk_size); + "data len %d\n", ip_data_len, tcp_hlen, tcp_data_len); /* note the cycle below overwrites IP header data, but restores it from saved_ip_header before sending packet */ int is_last_frame = 0; - for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size) + for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += large_send_mss) { - uint16_t chunk_size = tcp_chunk_size; + uint16_t chunk_size = large_send_mss; /* check if this is the last frame */ - if (tcp_send_offset + tcp_chunk_size >= tcp_data_len) + if (tcp_send_offset + large_send_mss >= tcp_data_len) { is_last_frame = 1; chunk_size = tcp_data_len - tcp_send_offset; @@ -2245,7 +2236,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size); /* increment IP id for subsequent frames */ - ip->ip_id = cpu_to_be16(tcp_send_offset/tcp_chunk_size + be16_to_cpu(ip->ip_id)); + ip->ip_id = cpu_to_be16(tcp_send_offset/large_send_mss + be16_to_cpu(ip->ip_id)); ip->ip_sum = 0; ip->ip_sum = ip_checksum(eth_payload_data, hlen); @@ -2261,13 +2252,12 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) /* add transferred count to TCP sequence number */ stl_be_p(&p_tcp_hdr->th_seq, chunk_size + ldl_be_p(&p_tcp_hdr->th_seq)); - ++send_count; } /* Stop sending this frame */ saved_size = 0; } - else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS)) + else if (!(txdw0 & CP_TX_LGSEN) && (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS))) { DPRINTF("+++ C+ mode need TCP or UDP checksum\n"); @@ -3160,7 +3150,7 @@ static const VMStateDescription vmstate_rtl8139_hotplug_ready ={ .version_id = 1, .minimum_version_id = 1, .needed = rtl8139_hotplug_ready_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_END_OF_LIST() } }; @@ -3183,7 +3173,7 @@ static const VMStateDescription vmstate_rtl8139 = { .minimum_version_id = 3, .post_load = rtl8139_post_load, .pre_save = rtl8139_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, RTL8139State), VMSTATE_PARTIAL_BUFFER(phys, RTL8139State, 6), VMSTATE_BUFFER(mult, RTL8139State), @@ -3267,7 +3257,7 @@ static const VMStateDescription vmstate_rtl8139 = { VMSTATE_UINT32_V(cplus_enabled, RTL8139State, 4), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_rtl8139_hotplug_ready, NULL } @@ -3398,7 +3388,8 @@ static void pci_rtl8139_realize(PCIDevice *dev, Error **errp) s->eeprom.contents[9] = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8; s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf, - object_get_typename(OBJECT(dev)), d->id, s); + object_get_typename(OBJECT(dev)), d->id, + &d->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->cplus_txbuffer = NULL; diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index ad778cd8fc..702d0e8e83 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -62,7 +62,7 @@ static const VMStateDescription vmstate_smc91c111 = { .name = "smc91c111", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(tcr, smc91c111_state), VMSTATE_UINT16(rcr, smc91c111_state), VMSTATE_UINT16(cr, smc91c111_state), @@ -361,7 +361,7 @@ static void smc91c111_writeb(void *opaque, hwaddr offset, case 4: case 5: case 6: case 7: case 8: case 9: /* IA */ /* Not implemented. */ return; - case 10: /* Genral Purpose */ + case 10: /* General Purpose */ SET_LOW(gpr, value); return; case 11: @@ -783,7 +783,8 @@ static void smc91c111_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->irq); qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); /* ??? Save/restore. */ } @@ -817,14 +818,13 @@ static void smc91c111_register_types(void) /* Legacy helper function. Should go away when machine config files are implemented. */ -void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq) +void smc91c111_init(uint32_t base, qemu_irq irq) { DeviceState *dev; SysBusDevice *s; - qemu_check_nic_model(nd, "smc91c111"); dev = qdev_new(TYPE_SMC91C111); - qdev_set_nic_properties(dev, nd); + qemu_configure_nic_device(dev, true, NULL); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); sysbus_mmio_map(s, 0, base); diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index a6876a936d..ecb30b7c76 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -325,7 +325,8 @@ static void spapr_vlan_realize(SpaprVioDevice *sdev, Error **errp) memcpy(&dev->perm_mac.a, &dev->nicconf.macaddr.a, sizeof(dev->perm_mac.a)); dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf, - object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev); + object_get_typename(OBJECT(sdev)), sdev->qdev.id, + &sdev->qdev.mem_reentrancy_guard, dev); qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a); dev->rxp_timer = timer_new_us(QEMU_CLOCK_VIRTUAL, spapr_vlan_flush_rx_queue, @@ -799,7 +800,7 @@ static const VMStateDescription vmstate_rx_buffer_pool = { .version_id = 1, .minimum_version_id = 1, .needed = spapr_vlan_rx_buffer_pools_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(bufsize, RxBufPool), VMSTATE_INT32(count, RxBufPool), VMSTATE_UINT64_ARRAY(bds, RxBufPool, RX_POOL_MAX_BDS), @@ -812,7 +813,7 @@ static const VMStateDescription vmstate_rx_pools = { .version_id = 1, .minimum_version_id = 1, .needed = spapr_vlan_rx_buffer_pools_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(rx_pool, SpaprVioVlan, RX_MAX_POOLS, 1, vmstate_rx_buffer_pool, RxBufPool), @@ -824,7 +825,7 @@ static const VMStateDescription vmstate_spapr_llan = { .name = "spapr_llan", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_SPAPR_VIO(sdev, SpaprVioVlan), /* LLAN state */ VMSTATE_BOOL(isopen, SpaprVioVlan), @@ -836,7 +837,7 @@ static const VMStateDescription vmstate_spapr_llan = { VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_rx_pools, NULL } diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index 8dd60783d8..db95766e29 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -88,7 +88,7 @@ static const VMStateDescription vmstate_rx_frame = { .name = "stellaris_enet/rx_frame", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(data, StellarisEnetRxFrame, 2048), VMSTATE_UINT32(len, StellarisEnetRxFrame), VMSTATE_END_OF_LIST() @@ -133,7 +133,7 @@ static const VMStateDescription vmstate_stellaris_enet = { .version_id = 2, .minimum_version_id = 2, .post_load = stellaris_enet_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ris, stellaris_enet_state), VMSTATE_UINT32(im, stellaris_enet_state), VMSTATE_UINT32(rctl, stellaris_enet_state), @@ -492,7 +492,8 @@ static void stellaris_enet_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/net/sungem.c b/hw/net/sungem.c index 3684a4d733..dd1b4a1344 100644 --- a/hw/net/sungem.c +++ b/hw/net/sungem.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/log.h" @@ -107,6 +107,15 @@ OBJECT_DECLARE_SIMPLE_TYPE(SunGEMState, SUNGEM) #define RXDMA_FTAG 0x0110UL /* RX FIFO Tag */ #define RXDMA_FSZ 0x0120UL /* RX FIFO Size */ +/* WOL Registers */ +#define SUNGEM_MMIO_WOL_SIZE 0x14 + +#define WOL_MATCH0 0x0000UL +#define WOL_MATCH1 0x0004UL +#define WOL_MATCH2 0x0008UL +#define WOL_MCOUNT 0x000CUL +#define WOL_WAKECSR 0x0010UL + /* MAC Registers */ #define SUNGEM_MMIO_MAC_SIZE 0x200 @@ -168,6 +177,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(SunGEMState, SUNGEM) #define SUNGEM_MMIO_PCS_SIZE 0x60 #define PCS_MIISTAT 0x0004UL /* PCS MII Status Register */ #define PCS_ISTAT 0x0018UL /* PCS Interrupt Status Reg */ + #define PCS_SSTATE 0x005CUL /* Serialink State Register */ /* Descriptors */ @@ -200,6 +210,7 @@ struct SunGEMState { MemoryRegion greg; MemoryRegion txdma; MemoryRegion rxdma; + MemoryRegion wol; MemoryRegion mac; MemoryRegion mif; MemoryRegion pcs; @@ -550,7 +561,6 @@ static ssize_t sungem_receive(NetClientState *nc, const uint8_t *buf, PCIDevice *d = PCI_DEVICE(s); uint32_t mac_crc, done, kick, max_fsize; uint32_t fcs_size, ints, rxdma_cfg, rxmac_cfg, csum, coff; - uint8_t smallbuf[60]; struct gem_rxd desc; uint64_t dbase, baddr; unsigned int rx_cond; @@ -584,19 +594,6 @@ static ssize_t sungem_receive(NetClientState *nc, const uint8_t *buf, return size; } - /* We don't drop too small frames since we get them in qemu, we pad - * them instead. We should probably use the min frame size register - * but I don't want to use a variable size staging buffer and I - * know both MacOS and Linux use the default 64 anyway. We use 60 - * here to account for the non-existent FCS. - */ - if (size < 60) { - memcpy(smallbuf, buf, size); - memset(&smallbuf[size], 0, 60 - size); - buf = smallbuf; - size = 60; - } - /* Get MAC crc */ mac_crc = net_crc32_le(buf, ETH_ALEN); @@ -1076,6 +1073,43 @@ static const MemoryRegionOps sungem_mmio_rxdma_ops = { }, }; +static void sungem_mmio_wol_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + trace_sungem_mmio_wol_write(addr, val); + + switch (addr) { + case WOL_WAKECSR: + if (val != 0) { + qemu_log_mask(LOG_UNIMP, "sungem: WOL not supported\n"); + } + break; + default: + qemu_log_mask(LOG_UNIMP, "sungem: WOL not supported\n"); + } +} + +static uint64_t sungem_mmio_wol_read(void *opaque, hwaddr addr, unsigned size) +{ + uint32_t val = -1; + + qemu_log_mask(LOG_UNIMP, "sungem: WOL not supported\n"); + + trace_sungem_mmio_wol_read(addr, val); + + return val; +} + +static const MemoryRegionOps sungem_mmio_wol_ops = { + .read = sungem_mmio_wol_read, + .write = sungem_mmio_wol_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + static void sungem_mmio_mac_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -1194,7 +1228,7 @@ static void sungem_mmio_mif_write(void *opaque, hwaddr addr, uint64_t val, case MIF_SMACHINE: return; /* No actual write */ case MIF_CFG: - /* Maintain the RO MDI bits to advertize an MDIO PHY on MDI0 */ + /* Maintain the RO MDI bits to advertise an MDIO PHY on MDI0 */ val &= ~MIF_CFG_MDI1; val |= MIF_CFG_MDI0; break; @@ -1344,6 +1378,10 @@ static void sungem_realize(PCIDevice *pci_dev, Error **errp) "sungem.rxdma", SUNGEM_MMIO_RXDMA_SIZE); memory_region_add_subregion(&s->sungem, 0x4000, &s->rxdma); + memory_region_init_io(&s->wol, OBJECT(s), &sungem_mmio_wol_ops, s, + "sungem.wol", SUNGEM_MMIO_WOL_SIZE); + memory_region_add_subregion(&s->sungem, 0x3000, &s->wol); + memory_region_init_io(&s->mac, OBJECT(s), &sungem_mmio_mac_ops, s, "sungem.mac", SUNGEM_MMIO_MAC_SIZE); memory_region_add_subregion(&s->sungem, 0x6000, &s->mac); @@ -1361,7 +1399,7 @@ static void sungem_realize(PCIDevice *pci_dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_sungem_info, &s->conf, object_get_typename(OBJECT(dev)), - dev->id, s); + dev->id, &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } @@ -1396,7 +1434,7 @@ static const VMStateDescription vmstate_sungem = { .name = "sungem", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(pdev, SunGEMState), VMSTATE_MACADDR(conf.macaddr, SunGEMState), VMSTATE_UINT32(phy_addr, SunGEMState), diff --git a/hw/net/sunhme.c b/hw/net/sunhme.c index fc34905f87..ae8452e5f9 100644 --- a/hw/net/sunhme.c +++ b/hw/net/sunhme.c @@ -23,7 +23,7 @@ */ #include "qemu/osdep.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "hw/net/mii.h" @@ -714,8 +714,6 @@ static inline void sunhme_set_rx_ring_nr(SunHMEState *s, int i) s->erxregs[HME_ERXI_RING >> 2] = ring; } -#define MIN_BUF_SIZE 60 - static ssize_t sunhme_receive(NetClientState *nc, const uint8_t *buf, size_t size) { @@ -724,7 +722,6 @@ static ssize_t sunhme_receive(NetClientState *nc, const uint8_t *buf, dma_addr_t rb, addr; uint32_t intstatus, status, buffer, buffersize, sum; uint16_t csum; - uint8_t buf1[60]; int nr, cr, len, rxoffset, csum_offset; trace_sunhme_rx_incoming(size); @@ -775,14 +772,6 @@ static ssize_t sunhme_receive(NetClientState *nc, const uint8_t *buf, trace_sunhme_rx_filter_accept(); - /* If too small buffer, then expand it */ - if (size < MIN_BUF_SIZE) { - memcpy(buf1, buf, size); - memset(buf1 + size, 0, MIN_BUF_SIZE - size); - buf = buf1; - size = MIN_BUF_SIZE; - } - rb = s->erxregs[HME_ERXI_RING >> 2] & HME_ERXI_RING_ADDR; nr = sunhme_get_rx_ring_count(s); cr = sunhme_get_rx_ring_nr(s); @@ -892,7 +881,8 @@ static void sunhme_realize(PCIDevice *pci_dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_sunhme_info, &s->conf, - object_get_typename(OBJECT(d)), d->id, s); + object_get_typename(OBJECT(d)), d->id, + &d->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } @@ -912,7 +902,7 @@ static void sunhme_reset(DeviceState *ds) /* Configure internal transceiver */ s->mifregs[HME_MIFI_CFG >> 2] |= HME_MIF_CFG_MDI0; - /* Advetise auto, 100Mbps FD */ + /* Advertise auto, 100Mbps FD */ s->miiregs[MII_ANAR] = MII_ANAR_TXFD; s->miiregs[MII_BMSR] = MII_BMSR_AUTONEG | MII_BMSR_100TX_FD | MII_BMSR_AN_COMP; @@ -935,7 +925,7 @@ static const VMStateDescription vmstate_hme = { .name = "sunhme", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, SunHMEState), VMSTATE_MACADDR(conf.macaddr, SunHMEState), VMSTATE_UINT32_ARRAY(sebregs, SunHMEState, (HME_SEB_REG_SIZE >> 2)), diff --git a/hw/net/trace-events b/hw/net/trace-events index 4c0ec3fda1..78efa2ec2c 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -61,7 +61,7 @@ pcnet_ioport_read(void *opaque, uint64_t addr, unsigned size) "opaque=%p addr=0x pcnet_ioport_write(void *opaque, uint64_t addr, uint64_t data, unsigned size) "opaque=%p addr=0x%"PRIx64" data=0x%"PRIx64" size=%d" # net_rx_pkt.c -net_rx_pkt_parsed(bool ip4, bool ip6, bool udp, bool tcp, size_t l3o, size_t l4o, size_t l5o) "RX packet parsed: ip4: %d, ip6: %d, udp: %d, tcp: %d, l3 offset: %zu, l4 offset: %zu, l5 offset: %zu" +net_rx_pkt_parsed(bool ip4, bool ip6, int l4proto, size_t l3o, size_t l4o, size_t l5o) "RX packet parsed: ip4: %d, ip6: %d, l4 protocol: %d, l3 offset: %zu, l4 offset: %zu, l5 offset: %zu" net_rx_pkt_l4_csum_validate_entry(void) "Starting L4 checksum validation" net_rx_pkt_l4_csum_validate_not_xxp(void) "Not a TCP/UDP packet" net_rx_pkt_l4_csum_validate_udp_with_no_checksum(void) "UDP packet without checksum" @@ -106,6 +106,8 @@ e1000_receiver_overrun(size_t s, uint32_t rdh, uint32_t rdt) "Receiver overrun: # e1000x_common.c e1000x_rx_can_recv_disabled(bool link_up, bool rx_enabled, bool pci_master) "link_up: %d, rx_enabled %d, pci_master %d" e1000x_vlan_is_vlan_pkt(bool is_vlan_pkt, uint16_t eth_proto, uint16_t vet) "Is VLAN packet: %d, ETH proto: 0x%X, VET: 0x%X" +e1000x_rx_flt_vlan_mismatch(uint16_t vid) "VID mismatch: 0x%X" +e1000x_rx_flt_vlan_match(uint16_t vid) "VID match: 0x%X" e1000x_rx_flt_ucast_match(uint32_t idx, uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "unicast match[%d]: %02x:%02x:%02x:%02x:%02x:%02x" e1000x_rx_flt_ucast_mismatch(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "unicast mismatch: %02x:%02x:%02x:%02x:%02x:%02x" e1000x_rx_flt_inexact_mismatch(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint32_t mo, uint32_t mta, uint32_t mta_val) "inexact mismatch: %02x:%02x:%02x:%02x:%02x:%02x MO %d MTA[%d] 0x%x" @@ -154,8 +156,6 @@ e1000e_rx_can_recv_rings_full(void) "Cannot receive: all rings are full" e1000e_rx_can_recv(void) "Can receive" e1000e_rx_has_buffers(int ridx, uint32_t free_desc, size_t total_size, uint32_t desc_buf_size) "ring #%d: free descr: %u, packet size %zu, descr buffer size %u" e1000e_rx_null_descriptor(void) "Null RX descriptor!!" -e1000e_rx_flt_vlan_mismatch(uint16_t vid) "VID mismatch: 0x%X" -e1000e_rx_flt_vlan_match(uint16_t vid) "VID match: 0x%X" e1000e_rx_desc_ps_read(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3) "buffers: [0x%"PRIx64", 0x%"PRIx64", 0x%"PRIx64", 0x%"PRIx64"]" e1000e_rx_desc_ps_write(uint16_t a0, uint16_t a1, uint16_t a2, uint16_t a3) "bytes written: [%u, %u, %u, %u]" e1000e_rx_desc_buff_sizes(uint32_t b0, uint32_t b1, uint32_t b2, uint32_t b3) "buffer sizes: [%u, %u, %u, %u]" @@ -165,8 +165,8 @@ e1000e_rx_descr(int ridx, uint64_t base, uint8_t len) "Next RX descriptor: ring e1000e_rx_set_rctl(uint32_t rctl) "RCTL = 0x%x" e1000e_rx_receive_iov(int iovcnt) "Received vector of %d fragments" e1000e_rx_flt_dropped(void) "Received packet dropped by RX filter" -e1000e_rx_written_to_guest(uint32_t causes) "Received packet written to guest (ICR causes %u)" -e1000e_rx_not_written_to_guest(uint32_t causes) "Received packet NOT written to guest (ICR causes %u)" +e1000e_rx_written_to_guest(int queue_idx) "Received packet written to guest (queue %d)" +e1000e_rx_not_written_to_guest(int queue_idx) "Received packet NOT written to guest (queue %d)" e1000e_rx_interrupt_set(uint32_t causes) "Receive interrupt set (ICR causes %u)" e1000e_rx_interrupt_delayed(uint32_t causes) "Receive interrupt delayed (ICR causes %u)" e1000e_rx_set_cso(int cso_state) "RX CSO state set to %d" @@ -177,18 +177,16 @@ e1000e_rx_start_recv(void) e1000e_rx_rss_started(void) "Starting RSS processing" e1000e_rx_rss_disabled(void) "RSS is disabled" e1000e_rx_rss_type(uint32_t type) "RSS type is %u" -e1000e_rx_rss_ip4(bool isfragment, bool istcp, uint32_t mrqc, bool tcpipv4_enabled, bool ipv4_enabled) "RSS IPv4: fragment %d, tcp %d, mrqc 0x%X, tcpipv4 enabled %d, ipv4 enabled %d" +e1000e_rx_rss_ip4(int l4hdr_proto, uint32_t mrqc, bool tcpipv4_enabled, bool ipv4_enabled) "RSS IPv4: L4 header protocol %d, mrqc 0x%X, tcpipv4 enabled %d, ipv4 enabled %d" e1000e_rx_rss_ip6_rfctl(uint32_t rfctl) "RSS IPv6: rfctl 0x%X" -e1000e_rx_rss_ip6(bool ex_dis, bool new_ex_dis, bool istcp, bool has_ext_headers, bool ex_dst_valid, bool ex_src_valid, uint32_t mrqc, bool tcpipv6_enabled, bool ipv6ex_enabled, bool ipv6_enabled) "RSS IPv6: ex_dis: %d, new_ex_dis: %d, tcp %d, has_ext_headers %d, ex_dst_valid %d, ex_src_valid %d, mrqc 0x%X, tcpipv6 enabled %d, ipv6ex enabled %d, ipv6 enabled %d" -e1000e_rx_rss_dispatched_to_queue(int queue_idx) "Packet being dispatched to queue %d" +e1000e_rx_rss_ip6(bool ex_dis, bool new_ex_dis, int l4hdr_proto, bool has_ext_headers, bool ex_dst_valid, bool ex_src_valid, uint32_t mrqc, bool tcpipv6ex_enabled, bool ipv6ex_enabled, bool ipv6_enabled) "RSS IPv6: ex_dis: %d, new_ex_dis: %d, L4 header protocol %d, has_ext_headers %d, ex_dst_valid %d, ex_src_valid %d, mrqc 0x%X, tcpipv6ex enabled %d, ipv6ex enabled %d, ipv6 enabled %d" -e1000e_rx_metadata_protocols(bool isip4, bool isip6, bool isudp, bool istcp) "protocols: ip4: %d, ip6: %d, udp: %d, tcp: %d" +e1000e_rx_metadata_protocols(bool hasip4, bool hasip6, int l4hdr_protocol) "protocols: ip4: %d, ip6: %d, l4hdr: %d" e1000e_rx_metadata_vlan(uint16_t vlan_tag) "VLAN tag is 0x%X" e1000e_rx_metadata_rss(uint32_t rss, uint32_t mrq) "RSS data: rss: 0x%X, mrq: 0x%X" e1000e_rx_metadata_ip_id(uint16_t ip_id) "the IPv4 ID is 0x%X" e1000e_rx_metadata_ack(void) "the packet is TCP ACK" e1000e_rx_metadata_pkt_type(uint32_t pkt_type) "the packet type is %u" -e1000e_rx_metadata_no_virthdr(void) "the packet has no virt-header" e1000e_rx_metadata_virthdr_no_csum_info(void) "virt-header does not contain checksum info" e1000e_rx_metadata_l3_cso_disabled(void) "IP4 CSO is disabled" e1000e_rx_metadata_l4_cso_disabled(void) "TCP/UDP CSO is disabled" @@ -201,31 +199,25 @@ e1000e_rx_metadata_ipv6_filtering_disabled(void) "IPv6 RX filtering disabled by e1000e_vlan_vet(uint16_t vet) "Setting VLAN ethernet type 0x%X" e1000e_irq_msi_notify(uint32_t cause) "MSI notify 0x%x" -e1000e_irq_throttling_no_pending_interrupts(void) "No pending interrupts to notify" e1000e_irq_msi_notify_postponed(void) "Sending MSI postponed by ITR" e1000e_irq_legacy_notify_postponed(void) "Raising legacy IRQ postponed by ITR" -e1000e_irq_throttling_no_pending_vec(int idx) "No pending interrupts for vector %d" e1000e_irq_msix_notify_postponed_vec(int idx) "Sending MSI-X postponed by EITR[%d]" e1000e_irq_legacy_notify(bool level) "IRQ line state: %d" e1000e_irq_msix_notify_vec(uint32_t vector) "MSI-X notify vector 0x%x" e1000e_irq_postponed_by_xitr(uint32_t reg) "Interrupt postponed by [E]ITR register 0x%x" -e1000e_irq_clear_ims(uint32_t bits, uint32_t old_ims, uint32_t new_ims) "Clearing IMS bits 0x%x: 0x%x --> 0x%x" -e1000e_irq_set_ims(uint32_t bits, uint32_t old_ims, uint32_t new_ims) "Setting IMS bits 0x%x: 0x%x --> 0x%x" +e1000e_irq_clear(uint32_t offset, uint32_t old, uint32_t new) "Clearing interrupt register 0x%x: 0x%x --> 0x%x" +e1000e_irq_set(uint32_t offset, uint32_t old, uint32_t new) "Setting interrupt register 0x%x: 0x%x --> 0x%x" e1000e_irq_fix_icr_asserted(uint32_t new_val) "ICR_ASSERTED bit fixed: 0x%x" e1000e_irq_add_msi_other(uint32_t new_val) "ICR_OTHER bit added: 0x%x" e1000e_irq_pending_interrupts(uint32_t pending, uint32_t icr, uint32_t ims) "ICR PENDING: 0x%x (ICR: 0x%x, IMS: 0x%x)" -e1000e_irq_set_cause_entry(uint32_t val, uint32_t icr) "Going to set IRQ cause 0x%x, ICR: 0x%x" -e1000e_irq_set_cause_exit(uint32_t val, uint32_t icr) "Set IRQ cause 0x%x, ICR: 0x%x" -e1000e_irq_icr_write(uint32_t bits, uint32_t old_icr, uint32_t new_icr) "Clearing ICR bits 0x%x: 0x%x --> 0x%x" e1000e_irq_write_ics(uint32_t val) "Adding ICR bits 0x%x" e1000e_irq_icr_process_iame(void) "Clearing IMS bits due to IAME" e1000e_irq_read_ics(uint32_t ics) "Current ICS: 0x%x" e1000e_irq_read_ims(uint32_t ims) "Current IMS: 0x%x" e1000e_irq_icr_clear_nonmsix_icr_read(void) "Clearing ICR on read due to non MSI-X int" -e1000e_irq_icr_read_entry(uint32_t icr) "Starting ICR read. Current ICR: 0x%x" -e1000e_irq_icr_read_exit(uint32_t icr) "Ending ICR read. Current ICR: 0x%x" e1000e_irq_icr_clear_zero_ims(void) "Clearing ICR on read due to zero IMS" e1000e_irq_icr_clear_iame(void) "Clearing ICR on read due to IAME" +e1000e_irq_icr_clear_icr_bit_ims(uint32_t icr, uint32_t ims) "Clearing ICR on read due corresponding IMS bit: 0x%x & 0x%x" e1000e_irq_iam_clear_eiame(uint32_t iam, uint32_t cause) "Clearing IMS due to EIAME, IAM: 0x%X, cause: 0x%X" e1000e_irq_icr_clear_eiac(uint32_t icr, uint32_t eiac) "Clearing ICR bits due to EIAC, ICR: 0x%X, EIAC: 0x%X" e1000e_irq_ims_clear_set_imc(uint32_t val) "Clearing IMS bits due to IMC write 0x%x" @@ -239,7 +231,6 @@ e1000e_irq_tidv_fpd_not_running(void) "FPD written while TIDV was not running" e1000e_irq_eitr_set(uint32_t eitr_num, uint32_t val) "EITR[%u] = %u" e1000e_irq_itr_set(uint32_t val) "ITR = %u" e1000e_irq_fire_all_timers(uint32_t val) "Firing all delay/throttling timers on all interrupts enable (0x%X written to IMS)" -e1000e_irq_adding_delayed_causes(uint32_t val, uint32_t icr) "Merging delayed causes 0x%X to ICR 0x%X" e1000e_irq_msix_pending_clearing(uint32_t cause, uint32_t int_cfg, uint32_t vec) "Clearing MSI-X pending bit for cause 0x%x, IVAR config 0x%x, vector %u" e1000e_wrn_msix_vec_wrong(uint32_t cause, uint32_t cfg) "Invalid configuration for cause 0x%x: 0x%x" @@ -253,7 +244,7 @@ e1000e_vm_state_stopped(void) "VM state is stopped" # e1000e.c e1000e_cb_pci_realize(void) "E1000E PCI realize entry" e1000e_cb_pci_uninit(void) "E1000E PCI unit entry" -e1000e_cb_qdev_reset(void) "E1000E qdev reset entry" +e1000e_cb_qdev_reset_hold(void) "E1000E qdev reset hold" e1000e_cb_pre_save(void) "E1000E pre save entry" e1000e_cb_post_load(void) "E1000E post load entry" @@ -274,6 +265,42 @@ e1000e_msix_use_vector_fail(uint32_t vec, int32_t res) "Failed to use MSI-X vect e1000e_mac_set_permanent(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "Set permanent MAC: %02x:%02x:%02x:%02x:%02x:%02x" e1000e_cfg_support_virtio(bool support) "Virtio header supported: %d" +# igb.c +igb_write_config(uint32_t address, uint32_t val, int len) "CONFIG write 0x%"PRIx32", value: 0x%"PRIx32", len: %"PRId32 +igbvf_write_config(uint32_t address, uint32_t val, int len) "CONFIG write 0x%"PRIx32", value: 0x%"PRIx32", len: %"PRId32 + +# igb_core.c +igb_core_mdic_read(uint32_t addr, uint32_t data) "MDIC READ: PHY[%u] = 0x%x" +igb_core_mdic_read_unhandled(uint32_t addr) "MDIC READ: PHY[%u] UNHANDLED" +igb_core_mdic_write(uint32_t addr, uint32_t data) "MDIC WRITE: PHY[%u] = 0x%x" +igb_core_mdic_write_unhandled(uint32_t addr) "MDIC WRITE: PHY[%u] UNHANDLED" +igb_core_vf_reset(uint16_t vfn) "VF%d" + +igb_link_set_ext_params(bool asd_check, bool speed_select_bypass, bool pfrstd) "Set extended link params: ASD check: %d, Speed select bypass: %d, PF reset done: %d" + +igb_rx_desc_buff_size(uint32_t b) "buffer size: %u" +igb_rx_desc_buff_write(uint8_t idx, uint64_t addr, uint16_t offset, const void* source, uint32_t len) "buffer %u, addr: 0x%"PRIx64", offset: %u, from: %p, length: %u" + +igb_rx_metadata_rss(uint32_t rss, uint16_t rss_pkt_type) "RSS data: rss: 0x%X, rss_pkt_type: 0x%X" + +igb_irq_icr_clear_gpie_nsicr(void) "Clearing ICR on read due to GPIE.NSICR enabled" +igb_irq_set_iam(uint32_t icr) "Update IAM: 0x%x" +igb_irq_read_iam(uint32_t icr) "Current IAM: 0x%x" +igb_irq_write_eics(uint32_t val, bool msix) "Update EICS: 0x%x MSI-X: %d" +igb_irq_write_eims(uint32_t val, bool msix) "Update EIMS: 0x%x MSI-X: %d" +igb_irq_write_eimc(uint32_t val, bool msix) "Update EIMC: 0x%x MSI-X: %d" +igb_irq_write_eiac(uint32_t val) "Update EIAC: 0x%x" +igb_irq_write_eiam(uint32_t val, bool msix) "Update EIAM: 0x%x MSI-X: %d" +igb_irq_write_eicr(uint32_t val, bool msix) "Update EICR: 0x%x MSI-X: %d" +igb_irq_eitr_set(uint32_t eitr_num, uint32_t val) "EITR[%u] = 0x%x" +igb_set_pfmailbox(uint32_t vf_num, uint32_t val) "PFMailbox[%d]: 0x%x" +igb_set_vfmailbox(uint32_t vf_num, uint32_t val) "VFMailbox[%d]: 0x%x" + +igb_wrn_rx_desc_modes_not_supp(int desc_type) "Not supported descriptor type: %d" + +# igbvf.c +igbvf_wrn_io_addr_unknown(uint64_t addr) "IO unknown register 0x%"PRIx64 + # spapr_llan.c spapr_vlan_get_rx_bd_from_pool_found(int pool, int32_t count, uint32_t rx_bufs) "pool=%d count=%"PRId32" rxbufs=%"PRIu32 spapr_vlan_get_rx_bd_from_page(int buf_ptr, uint64_t bd) "use_buf_ptr=%d bd=0x%016"PRIx64 @@ -327,6 +354,8 @@ sungem_mmio_txdma_write(uint64_t addr, uint64_t val) "MMIO txdma write to 0x%"PR sungem_mmio_txdma_read(uint64_t addr, uint64_t val) "MMIO txdma read from 0x%"PRIx64" val=0x%"PRIx64 sungem_mmio_rxdma_write(uint64_t addr, uint64_t val) "MMIO rxdma write to 0x%"PRIx64" val=0x%"PRIx64 sungem_mmio_rxdma_read(uint64_t addr, uint64_t val) "MMIO rxdma read from 0x%"PRIx64" val=0x%"PRIx64 +sungem_mmio_wol_write(uint64_t addr, uint64_t val) "MMIO wol write to 0x%"PRIx64" val=0x%"PRIx64 +sungem_mmio_wol_read(uint64_t addr, uint64_t val) "MMIO wol read from 0x%"PRIx64" val=0x%"PRIx64 sungem_mmio_mac_write(uint64_t addr, uint64_t val) "MMIO mac write to 0x%"PRIx64" val=0x%"PRIx64 sungem_mmio_mac_read(uint64_t addr, uint64_t val) "MMIO mac read from 0x%"PRIx64" val=0x%"PRIx64 sungem_mmio_mif_write(uint64_t addr, uint64_t val) "MMIO mif write to 0x%"PRIx64" val=0x%"PRIx64 @@ -438,6 +467,25 @@ npcm7xx_emc_rx_done(uint32_t crxdsa) "RX done, CRXDSA=0x%x" npcm7xx_emc_reg_read(int emc_num, uint32_t result, const char *name, int regno) "emc%d: 0x%x = reg[%s/%d]" npcm7xx_emc_reg_write(int emc_num, const char *name, int regno, uint32_t value) "emc%d: reg[%s/%d] = 0x%x" +# npcm_gmac.c +npcm_gmac_reg_read(const char *name, uint64_t offset, uint32_t value) "%s: offset: 0x%04" PRIx64 " value: 0x%04" PRIx32 +npcm_gmac_reg_write(const char *name, uint64_t offset, uint32_t value) "%s: offset: 0x%04" PRIx64 " value: 0x%04" PRIx32 +npcm_gmac_mdio_access(const char *name, uint8_t is_write, uint8_t pa, uint8_t gr, uint16_t val) "%s: is_write: %" PRIu8 " pa: %" PRIu8 " gr: %" PRIu8 " val: 0x%04" PRIx16 +npcm_gmac_reset(const char *name, uint16_t value) "%s: phy_regs[0][1]: 0x%04" PRIx16 +npcm_gmac_set_link(bool active) "Set link: active=%u" +npcm_gmac_update_irq(const char *name, uint32_t status, uint32_t intr_en, int level) "%s: Status Reg: 0x%04" PRIX32 " Interrupt Enable Reg: 0x%04" PRIX32 " IRQ Set: %d" +npcm_gmac_packet_desc_read(const char* name, uint32_t desc_addr) "%s: attempting to read descriptor @0x%04" PRIX32 +npcm_gmac_packet_receive(const char* name, uint32_t len) "%s: RX packet length: 0x%04" PRIX32 +npcm_gmac_packet_receiving_buffer(const char* name, uint32_t buf_len, uint32_t rx_buf_addr) "%s: Receiving into Buffer size: 0x%04" PRIX32 " at address 0x%04" PRIX32 +npcm_gmac_packet_received(const char* name, uint32_t len) "%s: Reception finished, packet left: 0x%04" PRIX32 +npcm_gmac_packet_sent(const char* name, uint16_t len) "%s: TX packet sent!, length: 0x%04" PRIX16 +npcm_gmac_debug_desc_data(const char* name, void* addr, uint32_t des0, uint32_t des1, uint32_t des2, uint32_t des3)"%s: Address: %p Descriptor 0: 0x%04" PRIX32 " Descriptor 1: 0x%04" PRIX32 "Descriptor 2: 0x%04" PRIX32 " Descriptor 3: 0x%04" PRIX32 +npcm_gmac_packet_tx_desc_data(const char* name, uint32_t tdes0, uint32_t tdes1) "%s: Tdes0: 0x%04" PRIX32 " Tdes1: 0x%04" PRIX32 + +# npcm_pcs.c +npcm_pcs_reg_read(const char *name, uint16_t indirect_access_baes, uint64_t offset, uint16_t value) "%s: IND: 0x%02" PRIx16 " offset: 0x%04" PRIx64 " value: 0x%04" PRIx16 +npcm_pcs_reg_write(const char *name, uint16_t indirect_access_baes, uint64_t offset, uint16_t value) "%s: IND: 0x%02" PRIx16 " offset: 0x%04" PRIx64 " value: 0x%04" PRIx16 + # dp8398x.c dp8393x_raise_irq(int isr) "raise irq, isr is 0x%04x" dp8393x_lower_irq(void) "lower irq" @@ -454,3 +502,14 @@ dp8393x_receive_oversize(int size) "oversize packet, pkt_size is %d" dp8393x_receive_not_netcard(void) "packet not for netcard" dp8393x_receive_packet(int crba) "Receive packet at 0x%"PRIx32 dp8393x_receive_write_status(int crba) "Write status at 0x%"PRIx32 + +# xen_nic.c +xen_netdev_realize(int dev, const char *info, const char *peer) "vif%u info '%s' peer '%s'" +xen_netdev_unrealize(int dev) "vif%u" +xen_netdev_create(int dev) "vif%u" +xen_netdev_destroy(int dev) "vif%u" +xen_netdev_disconnect(int dev) "vif%u" +xen_netdev_connect(int dev, unsigned int tx, unsigned int rx, int port) "vif%u tx %u rx %u port %u" +xen_netdev_frontend_changed(const char *dev, int state) "vif%s state %d" +xen_netdev_tx(int dev, int ref, int off, int len, unsigned int flags, const char *c, const char *d, const char *m, const char *e) "vif%u ref %u off %u len %u flags 0x%x%s%s%s%s" +xen_netdev_rx(int dev, int idx, int status, int flags) "vif%u idx %d status %d flags 0x%x" diff --git a/hw/net/tulip.c b/hw/net/tulip.c index 097e905bec..1f2ef20977 100644 --- a/hw/net/tulip.c +++ b/hw/net/tulip.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "hw/irq.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "hw/nvram/eeprom93xx.h" #include "migration/vmstate.h" @@ -48,7 +48,7 @@ struct TULIPState { static const VMStateDescription vmstate_pci_tulip = { .name = "tulip", - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, TULIPState), VMSTATE_UINT32_ARRAY(csr, TULIPState, 16), VMSTATE_UINT32(old_csr9, TULIPState), @@ -70,7 +70,7 @@ static const VMStateDescription vmstate_pci_tulip = { static void tulip_desc_read(TULIPState *s, hwaddr p, struct tulip_descriptor *desc) { - const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; + const MemTxAttrs attrs = { .memory = true }; if (s->csr[0] & CSR0_DBO) { ldl_be_pci_dma(&s->dev, p, &desc->status, attrs); @@ -88,7 +88,7 @@ static void tulip_desc_read(TULIPState *s, hwaddr p, static void tulip_desc_write(TULIPState *s, hwaddr p, struct tulip_descriptor *desc) { - const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; + const MemTxAttrs attrs = { .memory = true }; if (s->csr[0] & CSR0_DBO) { stl_be_pci_dma(&s->dev, p, desc->status, attrs); @@ -421,7 +421,7 @@ static uint16_t tulip_mdi_default[] = { /* MDI Registers 8 - 15 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* MDI Registers 16 - 31 */ - 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0000, 0x3b40, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; @@ -429,7 +429,7 @@ static uint16_t tulip_mdi_default[] = { static const uint16_t tulip_mdi_mask[] = { 0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0x0fff, 0x0000, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; @@ -870,11 +870,10 @@ static const MemoryRegionOps tulip_ops = { static void tulip_idblock_crc(TULIPState *s, uint16_t *srom) { - int word, n; + int word; int bit; unsigned char bitval, crc; const int len = 9; - n = 0; crc = -1; for (word = 0; word < len; word++) { @@ -887,7 +886,6 @@ static void tulip_idblock_crc(TULIPState *s, uint16_t *srom) srom[len - 1] = (srom[len - 1] & 0xff00) | (unsigned short)crc; break; } - n++; bitval = ((srom[word] >> bit) & 1) ^ ((crc >> 7) & 1); crc = crc << 1; if (bitval == 1) { @@ -985,7 +983,8 @@ static void pci_tulip_realize(PCIDevice *pci_dev, Error **errp) s->nic = qemu_new_nic(&net_tulip_info, &s->c, object_get_typename(OBJECT(pci_dev)), - pci_dev->qdev.id, s); + pci_dev->qdev.id, + &pci_dev->qdev.mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); } @@ -1022,7 +1021,7 @@ static void tulip_class_init(ObjectClass *klass, void *data) k->exit = pci_tulip_exit; k->vendor_id = PCI_VENDOR_ID_DEC; k->device_id = PCI_DEVICE_ID_DEC_21143; - k->subsystem_vendor_id = 0x103c; + k->subsystem_vendor_id = PCI_VENDOR_ID_HP; k->subsystem_id = 0x104f; k->class_id = PCI_CLASS_NETWORK_ETHERNET; dc->vmsd = &vmstate_pci_tulip; diff --git a/hw/net/vhost_net-stub.c b/hw/net/vhost_net-stub.c index 89d71cfb8e..72df6d757e 100644 --- a/hw/net/vhost_net-stub.c +++ b/hw/net/vhost_net-stub.c @@ -82,6 +82,15 @@ void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, { } +bool vhost_net_config_pending(VHostNetState *net) +{ + return false; +} + +void vhost_net_config_mask(VHostNetState *net, VirtIODevice *dev, bool mask) +{ +} + int vhost_net_notify_migration_done(struct vhost_net *net, char* mac_addr) { return -1; @@ -101,3 +110,20 @@ int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu) { return 0; } + +void vhost_net_virtqueue_reset(VirtIODevice *vdev, NetClientState *nc, + int vq_index) +{ + +} + +int vhost_net_virtqueue_restart(VirtIODevice *vdev, NetClientState *nc, + int vq_index) +{ + return 0; +} + +void vhost_net_save_acked_features(NetClientState *nc) +{ + +} diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index ccac5b7a64..fd1a93701a 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -34,6 +34,7 @@ #include "standard-headers/linux/virtio_ring.h" #include "hw/virtio/vhost.h" #include "hw/virtio/virtio-bus.h" +#include "linux-headers/linux/vhost.h" /* Features supported by host kernel. */ @@ -46,6 +47,7 @@ static const int kernel_feature_bits[] = { VIRTIO_NET_F_MTU, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_PACKED, + VIRTIO_F_RING_RESET, VIRTIO_NET_F_HASH_REPORT, VHOST_INVALID_FEATURE_BIT }; @@ -73,8 +75,12 @@ static const int user_feature_bits[] = { VIRTIO_NET_F_MTU, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_PACKED, + VIRTIO_F_RING_RESET, VIRTIO_NET_F_RSS, VIRTIO_NET_F_HASH_REPORT, + VIRTIO_NET_F_GUEST_USO4, + VIRTIO_NET_F_GUEST_USO6, + VIRTIO_NET_F_HOST_USO, /* This bit implies RARP isn't sent by QEMU out of band */ VIRTIO_NET_F_GUEST_ANNOUNCE, @@ -141,6 +147,15 @@ uint64_t vhost_net_get_acked_features(VHostNetState *net) return net->dev.acked_features; } +void vhost_net_save_acked_features(NetClientState *nc) +{ +#ifdef CONFIG_VHOST_NET_USER + if (nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) { + vhost_user_save_acked_features(nc); + } +#endif +} + static int vhost_net_get_fd(NetClientState *backend) { switch (backend->info->type) { @@ -244,12 +259,19 @@ static int vhost_net_start_one(struct vhost_net *net, struct vhost_vring_file file = { }; int r; + if (net->nc->info->start) { + r = net->nc->info->start(net->nc); + if (r < 0) { + return r; + } + } + r = vhost_dev_enable_notifiers(&net->dev, dev); if (r < 0) { goto fail_notifiers; } - r = vhost_dev_start(&net->dev, dev); + r = vhost_dev_start(&net->dev, dev, false); if (r < 0) { goto fail_start; } @@ -274,6 +296,13 @@ static int vhost_net_start_one(struct vhost_net *net, } } } + + if (net->nc->info->load) { + r = net->nc->info->load(net->nc); + if (r < 0) { + goto fail; + } + } return 0; fail: file.fd = -1; @@ -284,14 +313,14 @@ fail: /* Queue might not be ready for start */ continue; } - int r = vhost_net_set_backend(&net->dev, &file); - assert(r >= 0); + int ret = vhost_net_set_backend(&net->dev, &file); + assert(ret >= 0); } } if (net->nc->info->poll) { net->nc->info->poll(net->nc, true); } - vhost_dev_stop(&net->dev, dev); + vhost_dev_stop(&net->dev, dev, false); fail_start: vhost_dev_disable_notifiers(&net->dev, dev); fail_notifiers: @@ -312,7 +341,10 @@ static void vhost_net_stop_one(struct vhost_net *net, if (net->nc->info->poll) { net->nc->info->poll(net->nc, true); } - vhost_dev_stop(&net->dev, dev); + vhost_dev_stop(&net->dev, dev, false); + if (net->nc->info->stop) { + net->nc->info->stop(net->nc); + } vhost_dev_disable_notifiers(&net->dev, dev); } @@ -370,21 +402,20 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, } else { peer = qemu_get_peer(ncs, n->max_queue_pairs); } - r = vhost_net_start_one(get_vhost_net(peer), dev); - - if (r < 0) { - goto err_start; - } if (peer->vring_enable) { /* restore vring enable state */ r = vhost_set_vring_enable(peer, peer->vring_enable); if (r < 0) { - vhost_net_stop_one(get_vhost_net(peer), dev); goto err_start; } } + + r = vhost_net_start_one(get_vhost_net(peer), dev); + if (r < 0) { + goto err_start; + } } return 0; @@ -459,6 +490,15 @@ void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, vhost_virtqueue_mask(&net->dev, dev, idx, mask); } +bool vhost_net_config_pending(VHostNetState *net) +{ + return vhost_config_pending(&net->dev); +} + +void vhost_net_config_mask(VHostNetState *net, VirtIODevice *dev, bool mask) +{ + vhost_config_mask(&net->dev, dev, mask); +} VHostNetState *get_vhost_net(NetClientState *nc) { VHostNetState *vhost_net = 0; @@ -470,6 +510,12 @@ VHostNetState *get_vhost_net(NetClientState *nc) switch (nc->info->type) { case NET_CLIENT_DRIVER_TAP: vhost_net = tap_get_vhost_net(nc); + /* + * tap_get_vhost_net() can return NULL if a tap net-device backend is + * created with 'vhost=off' option, 'vhostforce=off' or no vhost or + * vhostforce or vhostfd options at all. Please see net_init_tap_one(). + * Hence, we omit the assertion here. + */ break; #ifdef CONFIG_VHOST_NET_USER case NET_CLIENT_DRIVER_VHOST_USER: @@ -495,6 +541,16 @@ int vhost_set_vring_enable(NetClientState *nc, int enable) VHostNetState *net = get_vhost_net(nc); const VhostOps *vhost_ops = net->dev.vhost_ops; + /* + * vhost-vdpa network devices need to enable dataplane virtqueues after + * DRIVER_OK, so they can recover device state before starting dataplane. + * Because of that, we don't enable virtqueues here and leave it to + * net/vhost-vdpa.c. + */ + if (nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) { + return 0; + } + nc->vring_enable = enable; if (vhost_ops && vhost_ops->vhost_set_vring_enable) { @@ -514,3 +570,80 @@ int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu) return vhost_ops->vhost_net_set_mtu(&net->dev, mtu); } + +void vhost_net_virtqueue_reset(VirtIODevice *vdev, NetClientState *nc, + int vq_index) +{ + VHostNetState *net = get_vhost_net(nc->peer); + const VhostOps *vhost_ops = net->dev.vhost_ops; + struct vhost_vring_file file = { .fd = -1 }; + int idx; + + /* should only be called after backend is connected */ + assert(vhost_ops); + + idx = vhost_ops->vhost_get_vq_index(&net->dev, vq_index); + + if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) { + file.index = idx; + int r = vhost_net_set_backend(&net->dev, &file); + assert(r >= 0); + } + + vhost_virtqueue_stop(&net->dev, + vdev, + net->dev.vqs + idx, + net->dev.vq_index + idx); +} + +int vhost_net_virtqueue_restart(VirtIODevice *vdev, NetClientState *nc, + int vq_index) +{ + VHostNetState *net = get_vhost_net(nc->peer); + const VhostOps *vhost_ops = net->dev.vhost_ops; + struct vhost_vring_file file = { }; + int idx, r; + + if (!net->dev.started) { + return -EBUSY; + } + + /* should only be called after backend is connected */ + assert(vhost_ops); + + idx = vhost_ops->vhost_get_vq_index(&net->dev, vq_index); + + r = vhost_virtqueue_start(&net->dev, + vdev, + net->dev.vqs + idx, + net->dev.vq_index + idx); + if (r < 0) { + goto err_start; + } + + if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) { + file.index = idx; + file.fd = net->backend; + r = vhost_net_set_backend(&net->dev, &file); + if (r < 0) { + r = -errno; + goto err_start; + } + } + + return 0; + +err_start: + error_report("Error when restarting the queue."); + + if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) { + file.fd = VHOST_FILE_UNBIND; + file.index = idx; + int ret = vhost_net_set_backend(&net->dev, &file); + assert(ret >= 0); + } + + vhost_dev_stop(&net->dev, vdev, false); + + return r; +} diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 7ad948ee7c..24e5e7d347 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -42,16 +42,14 @@ #include "sysemu/sysemu.h" #include "trace.h" #include "monitor/qdev.h" -#include "hw/pci/pci.h" +#include "monitor/monitor.h" +#include "hw/pci/pci_device.h" #include "net_rx_pkt.h" #include "hw/virtio/vhost.h" #include "sysemu/qtest.h" #define VIRTIO_NET_VM_VERSION 11 -#define MAC_TABLE_ENTRIES 64 -#define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ - /* previously fixed value */ #define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256 #define VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 256 @@ -107,6 +105,12 @@ static const VirtIOFeature feature_sizes[] = { {} }; +static const VirtIOConfigSizeParams cfg_size_params = { + .min_size = endof(struct virtio_net_config, mac), + .max_size = sizeof(struct virtio_net_config), + .feature_sizes = feature_sizes +}; + static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc) { VirtIONet *n = qemu_get_nic_opaque(nc); @@ -119,6 +123,16 @@ static int vq2q(int queue_index) return queue_index / 2; } +static void flush_or_purge_queued_packets(NetClientState *nc) +{ + if (!nc->peer) { + return; + } + + qemu_flush_or_purge_queued_packets(nc->peer, true); + assert(!virtio_net_get_subqueue(nc)->async_tx.elem); +} + /* TODO * - we could suppress RX interrupt if we were so inclined. */ @@ -153,20 +167,24 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) { ret = vhost_net_get_config(get_vhost_net(nc->peer), (uint8_t *)&netcfg, n->config_size); - if (ret != -1) { - /* - * Some NIC/kernel combinations present 0 as the mac address. As - * that is not a legal address, try to proceed with the - * address from the QEMU command line in the hope that the - * address has been configured correctly elsewhere - just not - * reported by the device. - */ - if (memcmp(&netcfg.mac, &zero, sizeof(zero)) == 0) { - info_report("Zero hardware mac address detected. Ignoring."); - memcpy(netcfg.mac, n->mac, ETH_ALEN); - } - memcpy(config, &netcfg, n->config_size); + if (ret == -1) { + return; } + + /* + * Some NIC/kernel combinations present 0 as the mac address. As that + * is not a legal address, try to proceed with the address from the + * QEMU command line in the hope that the address has been configured + * correctly elsewhere - just not reported by the device. + */ + if (memcmp(&netcfg.mac, &zero, sizeof(zero)) == 0) { + info_report("Zero hardware mac address detected. Ignoring."); + memcpy(netcfg.mac, n->mac, ETH_ALEN); + } + + netcfg.status |= virtio_tswap16(vdev, + n->status & VIRTIO_NET_S_ANNOUNCE); + memcpy(config, &netcfg, n->config_size); } } @@ -192,7 +210,7 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) { vhost_net_set_config(get_vhost_net(nc->peer), (uint8_t *)&netcfg, 0, n->config_size, - VHOST_SET_CONFIG_TYPE_MASTER); + VHOST_SET_CONFIG_TYPE_FRONTEND); } } @@ -442,8 +460,7 @@ static void rxfilter_notify(NetClientState *nc) if (nc->rxfilter_notify_enabled) { char *path = object_get_canonical_path(OBJECT(n->qdev)); - qapi_event_send_nic_rx_filter_changed(!!n->netclient_name, - n->netclient_name, path); + qapi_event_send_nic_rx_filter_changed(n->netclient_name, path); g_free(path); /* disable event notification to avoid events flooding */ @@ -531,6 +548,57 @@ static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc) return info; } +static void virtio_net_queue_reset(VirtIODevice *vdev, uint32_t queue_index) +{ + VirtIONet *n = VIRTIO_NET(vdev); + NetClientState *nc; + + /* validate queue_index and skip for cvq */ + if (queue_index >= n->max_queue_pairs * 2) { + return; + } + + nc = qemu_get_subqueue(n->nic, vq2q(queue_index)); + + if (!nc->peer) { + return; + } + + if (get_vhost_net(nc->peer) && + nc->peer->info->type == NET_CLIENT_DRIVER_TAP) { + vhost_net_virtqueue_reset(vdev, nc, queue_index); + } + + flush_or_purge_queued_packets(nc); +} + +static void virtio_net_queue_enable(VirtIODevice *vdev, uint32_t queue_index) +{ + VirtIONet *n = VIRTIO_NET(vdev); + NetClientState *nc; + int r; + + /* validate queue_index and skip for cvq */ + if (queue_index >= n->max_queue_pairs * 2) { + return; + } + + nc = qemu_get_subqueue(n->nic, vq2q(queue_index)); + + if (!nc->peer || !vdev->vhost_started) { + return; + } + + if (get_vhost_net(nc->peer) && + nc->peer->info->type == NET_CLIENT_DRIVER_TAP) { + r = vhost_net_virtqueue_restart(vdev, nc, queue_index); + if (r < 0) { + error_report("unable to restart vhost net virtqueue: %d, " + "when resetting the queue", queue_index); + } + } +} + static void virtio_net_reset(VirtIODevice *vdev) { VirtIONet *n = VIRTIO_NET(vdev); @@ -561,12 +629,7 @@ static void virtio_net_reset(VirtIODevice *vdev) /* Flush any async TX */ for (i = 0; i < n->max_queue_pairs; i++) { - NetClientState *nc = qemu_get_subqueue(n->nic, i); - - if (nc->peer) { - qemu_flush_or_purge_queued_packets(nc->peer, true); - assert(!virtio_net_get_subqueue(nc)->async_tx.elem); - } + flush_or_purge_queued_packets(qemu_get_subqueue(n->nic, i)); } } @@ -595,6 +658,15 @@ static int peer_has_ufo(VirtIONet *n) return n->has_ufo; } +static int peer_has_uso(VirtIONet *n) +{ + if (!peer_has_vnet_hdr(n)) { + return 0; + } + + return qemu_has_uso(qemu_get_queue(n->nic)->peer); +} + static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, int version_1, int hash_report) { @@ -603,6 +675,11 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, n->mergeable_rx_bufs = mergeable_rx_bufs; + /* + * Note: when extending the vnet header, please make sure to + * change the vnet header copying logic in virtio_net_flush_tx() + * as well. + */ if (version_1) { n->guest_hdr_len = hash_report ? sizeof(struct virtio_net_hdr_v1_hash) : @@ -732,6 +809,10 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6); virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN); + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_USO); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO4); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO6); + virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT); } @@ -740,6 +821,12 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO); } + if (!peer_has_uso(n)) { + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_USO); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO4); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO6); + } + if (!get_vhost_net(nc->peer)) { return features; } @@ -755,6 +842,21 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, features |= (1ULL << VIRTIO_NET_F_MTU); } + /* + * Since GUEST_ANNOUNCE is emulated the feature bit could be set without + * enabled. This happens in the vDPA case. + * + * Make sure the feature set is not incoherent, as the driver could refuse + * to start. + * + * TODO: QEMU is able to emulate a CVQ just for guest_announce purposes, + * helping guest to notify the new location with vDPA devices that does not + * support it. + */ + if (!virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_CTRL_VQ)) { + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ANNOUNCE); + } + return features; } @@ -780,22 +882,26 @@ static void virtio_net_apply_guest_offloads(VirtIONet *n) !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)), !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)), !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_ECN)), - !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO))); + !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO)), + !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO4)), + !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_USO6))); } -static uint64_t virtio_net_guest_offloads_by_features(uint32_t features) +static uint64_t virtio_net_guest_offloads_by_features(uint64_t features) { static const uint64_t guest_offloads_mask = (1ULL << VIRTIO_NET_F_GUEST_CSUM) | (1ULL << VIRTIO_NET_F_GUEST_TSO4) | (1ULL << VIRTIO_NET_F_GUEST_TSO6) | (1ULL << VIRTIO_NET_F_GUEST_ECN) | - (1ULL << VIRTIO_NET_F_GUEST_UFO); + (1ULL << VIRTIO_NET_F_GUEST_UFO) | + (1ULL << VIRTIO_NET_F_GUEST_USO4) | + (1ULL << VIRTIO_NET_F_GUEST_USO6); return guest_offloads_mask & features; } -static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n) +uint64_t virtio_net_supported_guest_offloads(const VirtIONet *n) { VirtIODevice *vdev = VIRTIO_DEVICE(n); return virtio_net_guest_offloads_by_features(vdev->guest_features); @@ -919,11 +1025,15 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) continue; } vhost_net_ack_features(get_vhost_net(nc->peer), features); + + /* + * keep acked_features in NetVhostUserState up-to-date so it + * can't miss any features configured by guest virtio driver. + */ + vhost_net_save_acked_features(nc->peer); } - if (virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) { - memset(n->vlans, 0, MAX_VLAN >> 3); - } else { + if (!virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) { memset(n->vlans, 0xff, MAX_VLAN >> 3); } @@ -1219,14 +1329,53 @@ static void virtio_net_detach_epbf_rss(VirtIONet *n) virtio_net_attach_ebpf_to_backend(n->nic, -1); } -static bool virtio_net_load_ebpf(VirtIONet *n) +static bool virtio_net_load_ebpf_fds(VirtIONet *n, Error **errp) { - if (!virtio_net_attach_ebpf_to_backend(n->nic, -1)) { - /* backend does't support steering ebpf */ - return false; + int fds[EBPF_RSS_MAX_FDS] = { [0 ... EBPF_RSS_MAX_FDS - 1] = -1}; + int ret = true; + int i = 0; + + ERRP_GUARD(); + + if (n->nr_ebpf_rss_fds != EBPF_RSS_MAX_FDS) { + error_setg(errp, + "Expected %d file descriptors but got %d", + EBPF_RSS_MAX_FDS, n->nr_ebpf_rss_fds); + return false; + } + + for (i = 0; i < n->nr_ebpf_rss_fds; i++) { + fds[i] = monitor_fd_param(monitor_cur(), n->ebpf_rss_fds[i], errp); + if (*errp) { + ret = false; + goto exit; + } } - return ebpf_rss_load(&n->ebpf_rss); + ret = ebpf_rss_load_fds(&n->ebpf_rss, fds[0], fds[1], fds[2], fds[3]); + +exit: + if (!ret || *errp) { + for (i = 0; i < n->nr_ebpf_rss_fds && fds[i] != -1; i++) { + close(fds[i]); + } + } + + return ret; +} + +static bool virtio_net_load_ebpf(VirtIONet *n, Error **errp) +{ + bool ret = false; + + if (virtio_net_attach_ebpf_to_backend(n->nic, -1)) { + if (!(n->ebpf_rss_fds + && virtio_net_load_ebpf_fds(n, errp))) { + ret = ebpf_rss_load(&n->ebpf_rss); + } + } + + return ret; } static void virtio_net_unload_ebpf(VirtIONet *n) @@ -1413,19 +1562,14 @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_ERR; } - /* Avoid changing the number of queue_pairs for vdpa device in - * userspace handler. A future fix is needed to handle the mq - * change in userspace handler with vhost-vdpa. Let's disable - * the mq handling from userspace for now and only allow get - * done through the kernel. Ripples may be seen when falling - * back to userspace, but without doing it qemu process would - * crash on a recursive entry to virtio_net_set_status(). - */ - if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) { - return VIRTIO_NET_ERR; - } - n->curr_queue_pairs = queue_pairs; + if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) { + /* + * Avoid updating the backend for a vdpa device: We're only interested + * in updating the device model queues. + */ + return VIRTIO_NET_OK; + } /* stop the backend before changing the number of queue_pairs to avoid handling a * disabled queue */ virtio_net_set_status(vdev, vdev->status); @@ -1434,57 +1578,71 @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_OK; } -static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) +size_t virtio_net_handle_ctrl_iov(VirtIODevice *vdev, + const struct iovec *in_sg, unsigned in_num, + const struct iovec *out_sg, + unsigned out_num) { VirtIONet *n = VIRTIO_NET(vdev); struct virtio_net_ctrl_hdr ctrl; virtio_net_ctrl_ack status = VIRTIO_NET_ERR; - VirtQueueElement *elem; size_t s; struct iovec *iov, *iov2; - unsigned int iov_cnt; + + if (iov_size(in_sg, in_num) < sizeof(status) || + iov_size(out_sg, out_num) < sizeof(ctrl)) { + virtio_error(vdev, "virtio-net ctrl missing headers"); + return 0; + } + + iov2 = iov = g_memdup2(out_sg, sizeof(struct iovec) * out_num); + s = iov_to_buf(iov, out_num, 0, &ctrl, sizeof(ctrl)); + iov_discard_front(&iov, &out_num, sizeof(ctrl)); + if (s != sizeof(ctrl)) { + status = VIRTIO_NET_ERR; + } else if (ctrl.class == VIRTIO_NET_CTRL_RX) { + status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, out_num); + } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) { + status = virtio_net_handle_mac(n, ctrl.cmd, iov, out_num); + } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) { + status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, out_num); + } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) { + status = virtio_net_handle_announce(n, ctrl.cmd, iov, out_num); + } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) { + status = virtio_net_handle_mq(n, ctrl.cmd, iov, out_num); + } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) { + status = virtio_net_handle_offloads(n, ctrl.cmd, iov, out_num); + } + + s = iov_from_buf(in_sg, in_num, 0, &status, sizeof(status)); + assert(s == sizeof(status)); + + g_free(iov2); + return sizeof(status); +} + +static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtQueueElement *elem; for (;;) { + size_t written; elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { break; } - if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) || - iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) { - virtio_error(vdev, "virtio-net ctrl missing headers"); + + written = virtio_net_handle_ctrl_iov(vdev, elem->in_sg, elem->in_num, + elem->out_sg, elem->out_num); + if (written > 0) { + virtqueue_push(vq, elem, written); + virtio_notify(vdev, vq); + g_free(elem); + } else { virtqueue_detach_element(vq, elem, 0); g_free(elem); break; } - - iov_cnt = elem->out_num; - iov2 = iov = g_memdup2(elem->out_sg, - sizeof(struct iovec) * elem->out_num); - s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl)); - iov_discard_front(&iov, &iov_cnt, sizeof(ctrl)); - if (s != sizeof(ctrl)) { - status = VIRTIO_NET_ERR; - } else if (ctrl.class == VIRTIO_NET_CTRL_RX) { - status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) { - status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) { - status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) { - status = virtio_net_handle_announce(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) { - status = virtio_net_handle_mq(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) { - status = virtio_net_handle_offloads(n, ctrl.cmd, iov, iov_cnt); - } - - s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status)); - assert(s == sizeof(status)); - - virtqueue_push(vq, elem, sizeof(status)); - virtio_notify(vdev, vq); - g_free(iov2); - g_free(elem); } } @@ -1651,39 +1809,61 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) return 0; } -static uint8_t virtio_net_get_hash_type(bool isip4, - bool isip6, - bool isudp, - bool istcp, +static uint8_t virtio_net_get_hash_type(bool hasip4, + bool hasip6, + EthL4HdrProto l4hdr_proto, uint32_t types) { - if (isip4) { - if (istcp && (types & VIRTIO_NET_RSS_HASH_TYPE_TCPv4)) { - return NetPktRssIpV4Tcp; - } - if (isudp && (types & VIRTIO_NET_RSS_HASH_TYPE_UDPv4)) { - return NetPktRssIpV4Udp; + if (hasip4) { + switch (l4hdr_proto) { + case ETH_L4_HDR_PROTO_TCP: + if (types & VIRTIO_NET_RSS_HASH_TYPE_TCPv4) { + return NetPktRssIpV4Tcp; + } + break; + + case ETH_L4_HDR_PROTO_UDP: + if (types & VIRTIO_NET_RSS_HASH_TYPE_UDPv4) { + return NetPktRssIpV4Udp; + } + break; + + default: + break; } + if (types & VIRTIO_NET_RSS_HASH_TYPE_IPv4) { return NetPktRssIpV4; } - } else if (isip6) { - uint32_t mask = VIRTIO_NET_RSS_HASH_TYPE_TCP_EX | - VIRTIO_NET_RSS_HASH_TYPE_TCPv6; + } else if (hasip6) { + switch (l4hdr_proto) { + case ETH_L4_HDR_PROTO_TCP: + if (types & VIRTIO_NET_RSS_HASH_TYPE_TCP_EX) { + return NetPktRssIpV6TcpEx; + } + if (types & VIRTIO_NET_RSS_HASH_TYPE_TCPv6) { + return NetPktRssIpV6Tcp; + } + break; - if (istcp && (types & mask)) { - return (types & VIRTIO_NET_RSS_HASH_TYPE_TCP_EX) ? - NetPktRssIpV6TcpEx : NetPktRssIpV6Tcp; + case ETH_L4_HDR_PROTO_UDP: + if (types & VIRTIO_NET_RSS_HASH_TYPE_UDP_EX) { + return NetPktRssIpV6UdpEx; + } + if (types & VIRTIO_NET_RSS_HASH_TYPE_UDPv6) { + return NetPktRssIpV6Udp; + } + break; + + default: + break; } - mask = VIRTIO_NET_RSS_HASH_TYPE_UDP_EX | VIRTIO_NET_RSS_HASH_TYPE_UDPv6; - if (isudp && (types & mask)) { - return (types & VIRTIO_NET_RSS_HASH_TYPE_UDP_EX) ? - NetPktRssIpV6UdpEx : NetPktRssIpV6Udp; + + if (types & VIRTIO_NET_RSS_HASH_TYPE_IP_EX) { + return NetPktRssIpV6Ex; } - mask = VIRTIO_NET_RSS_HASH_TYPE_IP_EX | VIRTIO_NET_RSS_HASH_TYPE_IPv6; - if (types & mask) { - return (types & VIRTIO_NET_RSS_HASH_TYPE_IP_EX) ? - NetPktRssIpV6Ex : NetPktRssIpV6; + if (types & VIRTIO_NET_RSS_HASH_TYPE_IPv6) { + return NetPktRssIpV6; } } return 0xff; @@ -1705,7 +1885,8 @@ static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf, struct NetRxPkt *pkt = n->rx_pkt; uint8_t net_hash_type; uint32_t hash; - bool isip4, isip6, isudp, istcp; + bool hasip4, hasip6; + EthL4HdrProto l4hdr_proto; static const uint8_t reports[NetPktRssIpV6UdpEx + 1] = { VIRTIO_NET_HASH_REPORT_IPv4, VIRTIO_NET_HASH_REPORT_TCPv4, @@ -1717,17 +1898,14 @@ static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf, VIRTIO_NET_HASH_REPORT_UDPv6, VIRTIO_NET_HASH_REPORT_UDPv6_EX }; + struct iovec iov = { + .iov_base = (void *)buf, + .iov_len = size + }; - net_rx_pkt_set_protocols(pkt, buf + n->host_hdr_len, - size - n->host_hdr_len); - net_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); - if (isip4 && (net_rx_pkt_get_ip4_info(pkt)->fragment)) { - istcp = isudp = false; - } - if (isip6 && (net_rx_pkt_get_ip6_info(pkt)->fragment)) { - istcp = isudp = false; - } - net_hash_type = virtio_net_get_hash_type(isip4, isip6, isudp, istcp, + net_rx_pkt_set_protocols(pkt, &iov, 1, n->host_hdr_len); + net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto); + net_hash_type = virtio_net_get_hash_type(hasip4, hasip6, l4hdr_proto, n->rss_data.hash_types); if (net_hash_type > NetPktRssIpV6UdpEx) { if (n->rss_data.populate_hash) { @@ -1932,7 +2110,7 @@ static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain, + sizeof(struct ip6_header)); unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10; - /* There is a difference between payload lenght in ipv4 and v6, + /* There is a difference between payload length in ipv4 and v6, ip header is excluded in ipv6 */ unit->payload = htons(*unit->ip_plen) - unit->tcp_hdrlen; } @@ -2401,7 +2579,7 @@ static size_t virtio_net_rsc_receive6(void *opq, NetClientState *nc, VirtioNetRscChain *chain; VirtioNetRscUnit unit; - chain = (VirtioNetRscChain *)opq; + chain = opq; hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len; if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip6_header) @@ -2512,6 +2690,7 @@ static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) VirtIONet *n = qemu_get_nic_opaque(nc); VirtIONetQueue *q = virtio_net_get_subqueue(nc); VirtIODevice *vdev = VIRTIO_DEVICE(n); + int ret; virtqueue_push(q->tx_vq, q->async_tx.elem, 0); virtio_notify(vdev, q->tx_vq); @@ -2520,7 +2699,22 @@ static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) q->async_tx.elem = NULL; virtio_queue_set_notification(q->tx_vq, 1); - virtio_net_flush_tx(q); + ret = virtio_net_flush_tx(q); + if (ret >= n->tx_burst) { + /* + * the flush has been stopped by tx_burst + * we will not receive notification for the + * remainining part, so re-schedule + */ + virtio_queue_set_notification(q->tx_vq, 0); + if (q->tx_bh) { + qemu_bh_schedule(q->tx_bh); + } else { + timer_mod(q->tx_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); + } + q->tx_waiting = 1; + } } /* TX */ @@ -2544,7 +2738,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) ssize_t ret; unsigned int out_num; struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg; - struct virtio_net_hdr_mrg_rxbuf mhdr; + struct virtio_net_hdr_v1_hash vhdr; elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement)); if (!elem) { @@ -2561,7 +2755,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) } if (n->has_vnet_hdr) { - if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) < + if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) < n->guest_hdr_len) { virtio_error(vdev, "virtio-net header incorrect"); virtqueue_detach_element(q->tx_vq, elem, 0); @@ -2569,8 +2763,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) return -EINVAL; } if (n->needs_vnet_hdr_swap) { - virtio_net_hdr_swap(vdev, (void *) &mhdr); - sg2[0].iov_base = &mhdr; + virtio_net_hdr_swap(vdev, (void *) &vhdr); + sg2[0].iov_base = &vhdr; sg2[0].iov_len = n->guest_hdr_len; out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1, out_sg, out_num, @@ -2619,6 +2813,8 @@ drop: return num_packets; } +static void virtio_net_tx_timer(void *opaque); + static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) { VirtIONet *n = VIRTIO_NET(vdev); @@ -2636,15 +2832,13 @@ static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) } if (q->tx_waiting) { - virtio_queue_set_notification(vq, 1); + /* We already have queued packets, immediately flush */ timer_del(q->tx_timer); - q->tx_waiting = 0; - if (virtio_net_flush_tx(q) == -EINVAL) { - return; - } + virtio_net_tx_timer(q); } else { + /* re-arm timer to flush it (and more) on next tick */ timer_mod(q->tx_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); q->tx_waiting = 1; virtio_queue_set_notification(vq, 0); } @@ -2655,6 +2849,10 @@ static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) VirtIONet *n = VIRTIO_NET(vdev); VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; + if (unlikely(n->vhost_started)) { + return; + } + if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) { virtio_net_drop_tx_queue_data(vdev, vq); return; @@ -2677,6 +2875,8 @@ static void virtio_net_tx_timer(void *opaque) VirtIONetQueue *q = opaque; VirtIONet *n = q->n; VirtIODevice *vdev = VIRTIO_DEVICE(n); + int ret; + /* This happens when device was stopped but BH wasn't. */ if (!vdev->vm_running) { /* Make sure tx waiting is set, so we'll run when restarted. */ @@ -2691,8 +2891,33 @@ static void virtio_net_tx_timer(void *opaque) return; } + ret = virtio_net_flush_tx(q); + if (ret == -EBUSY || ret == -EINVAL) { + return; + } + /* + * If we flush a full burst of packets, assume there are + * more coming and immediately rearm + */ + if (ret >= n->tx_burst) { + q->tx_waiting = 1; + timer_mod(q->tx_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); + return; + } + /* + * If less than a full burst, re-enable notification and flush + * anything that may have come in while we weren't looking. If + * we find something, assume the guest is still active and rearm + */ virtio_queue_set_notification(q->tx_vq, 1); - virtio_net_flush_tx(q); + ret = virtio_net_flush_tx(q); + if (ret > 0) { + virtio_queue_set_notification(q->tx_vq, 0); + q->tx_waiting = 1; + timer_mod(q->tx_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); + } } static void virtio_net_tx_bh(void *opaque) @@ -2762,7 +2987,8 @@ static void virtio_net_add_queue(VirtIONet *n, int index) n->vqs[index].tx_vq = virtio_add_queue(vdev, n->net_conf.tx_queue_size, virtio_net_handle_tx_bh); - n->vqs[index].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[index]); + n->vqs[index].tx_bh = qemu_bh_new_guarded(virtio_net_tx_bh, &n->vqs[index], + &DEVICE(vdev)->mem_reentrancy_guard); } n->vqs[index].tx_waiting = 0; @@ -2936,7 +3162,7 @@ static int virtio_net_post_load_virtio(VirtIODevice *vdev) /* tx_waiting field of a VirtIONetQueue */ static const VMStateDescription vmstate_virtio_net_queue_tx_waiting = { .name = "virtio-net-queue-tx_waiting", - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(tx_waiting, VirtIONetQueue), VMSTATE_END_OF_LIST() }, @@ -3014,7 +3240,7 @@ static const VMStateDescription vmstate_virtio_net_tx_waiting = { .name = "virtio-net-tx_waiting", .pre_load = virtio_net_tx_waiting_pre_load, .pre_save = virtio_net_tx_waiting_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_VARRAY_POINTER_UINT16(vqs_1, struct VirtIONetMigTmp, curr_queue_pairs_1, vmstate_virtio_net_queue_tx_waiting, @@ -3051,7 +3277,7 @@ static const VMStateDescription vmstate_virtio_net_has_ufo = { .name = "virtio-net-ufo", .post_load = virtio_net_ufo_post_load, .pre_save = virtio_net_ufo_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(has_ufo, struct VirtIONetMigTmp), VMSTATE_END_OF_LIST() }, @@ -3085,7 +3311,7 @@ static const VMStateDescription vmstate_virtio_net_has_vnet = { .name = "virtio-net-vnet", .post_load = virtio_net_vnet_post_load, .pre_save = virtio_net_vnet_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(has_vnet_hdr, struct VirtIONetMigTmp), VMSTATE_END_OF_LIST() }, @@ -3101,7 +3327,7 @@ static const VMStateDescription vmstate_virtio_net_rss = { .version_id = 1, .minimum_version_id = 1, .needed = virtio_net_rss_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(rss_data.enabled, VirtIONet), VMSTATE_BOOL(rss_data.redirect, VirtIONet), VMSTATE_BOOL(rss_data.populate_hash, VirtIONet), @@ -3122,7 +3348,7 @@ static const VMStateDescription vmstate_virtio_net_device = { .version_id = VIRTIO_NET_VM_VERSION, .minimum_version_id = VIRTIO_NET_VM_VERSION, .post_load = virtio_net_post_load_device, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(mac, VirtIONet, ETH_ALEN), VMSTATE_STRUCT_POINTER(vqs, VirtIONet, vmstate_virtio_net_queue_tx_waiting, @@ -3166,8 +3392,8 @@ static const VMStateDescription vmstate_virtio_net_device = { VMSTATE_UINT64_TEST(curr_guest_offloads, VirtIONet, has_ctrl_guest_offloads), VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription * []) { + }, + .subsections = (const VMStateDescription * const []) { &vmstate_virtio_net_rss, NULL } @@ -3188,7 +3414,7 @@ static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx) VirtIONet *n = VIRTIO_NET(vdev); NetClientState *nc; assert(n->vhost_started); - if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_MQ) && idx == 2) { + if (!n->multiqueue && idx == 2) { /* Must guard against invalid features and bogus queue index * from being set by malicious guest, or penetrated through * buggy migration stream. @@ -3202,6 +3428,15 @@ static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx) } else { nc = qemu_get_subqueue(n->nic, vq2q(idx)); } + /* + * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1 + * as the macro of configure interrupt's IDX, If this driver does not + * support, the function will return false + */ + + if (idx == VIRTIO_CONFIG_IRQ_IDX) { + return vhost_net_config_pending(get_vhost_net(nc->peer)); + } return vhost_net_virtqueue_pending(get_vhost_net(nc->peer), idx); } @@ -3211,7 +3446,7 @@ static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, VirtIONet *n = VIRTIO_NET(vdev); NetClientState *nc; assert(n->vhost_started); - if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_MQ) && idx == 2) { + if (!n->multiqueue && idx == 2) { /* Must guard against invalid features and bogus queue index * from being set by malicious guest, or penetrated through * buggy migration stream. @@ -3225,16 +3460,24 @@ static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, } else { nc = qemu_get_subqueue(n->nic, vq2q(idx)); } - vhost_net_virtqueue_mask(get_vhost_net(nc->peer), - vdev, idx, mask); + /* + *Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1 + * as the macro of configure interrupt's IDX, If this driver does not + * support, the function will return + */ + + if (idx == VIRTIO_CONFIG_IRQ_IDX) { + vhost_net_config_mask(get_vhost_net(nc->peer), vdev, mask); + return; + } + vhost_net_virtqueue_mask(get_vhost_net(nc->peer), vdev, idx, mask); } static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features) { virtio_add_feature(&host_features, VIRTIO_NET_F_MAC); - n->config_size = virtio_feature_get_config_size(feature_sizes, - host_features); + n->config_size = virtio_get_config_size(&cfg_size_params, host_features); } void virtio_net_set_netclient_name(VirtIONet *n, const char *name, @@ -3305,7 +3548,7 @@ out: return !err; } -static void virtio_net_handle_migration_primary(VirtIONet *n, MigrationState *s) +static void virtio_net_handle_migration_primary(VirtIONet *n, MigrationEvent *e) { bool should_be_hidden; Error *err = NULL; @@ -3317,7 +3560,7 @@ static void virtio_net_handle_migration_primary(VirtIONet *n, MigrationState *s) should_be_hidden = qatomic_read(&n->failover_primary_hidden); - if (migration_in_setup(s) && !should_be_hidden) { + if (e->type == MIG_EVENT_PRECOPY_SETUP && !should_be_hidden) { if (failover_unplug_primary(n, dev)) { vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev); qapi_event_send_unplug_primary(dev->id); @@ -3325,7 +3568,7 @@ static void virtio_net_handle_migration_primary(VirtIONet *n, MigrationState *s) } else { warn_report("couldn't unplug primary device"); } - } else if (migration_has_failed(s)) { + } else if (e->type == MIG_EVENT_PRECOPY_FAILED) { /* We already unplugged the device let's plug it back */ if (!failover_replug_primary(n, dev, &err)) { if (err) { @@ -3335,11 +3578,12 @@ static void virtio_net_handle_migration_primary(VirtIONet *n, MigrationState *s) } } -static void virtio_net_migration_state_notifier(Notifier *notifier, void *data) +static int virtio_net_migration_state_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, Error **errp) { - MigrationState *s = data; VirtIONet *n = container_of(notifier, VirtIONet, migration_state); - virtio_net_handle_migration_primary(n, s); + virtio_net_handle_migration_primary(n, e); + return 0; } static bool failover_hide_primary_device(DeviceListener *listener, @@ -3430,8 +3674,8 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) n->primary_listener.hide_device = failover_hide_primary_device; qatomic_set(&n->failover_primary_hidden, true); device_listener_register(&n->primary_listener); - n->migration_state.notify = virtio_net_migration_state_notifier; - add_migration_state_change_notifier(&n->migration_state); + migration_add_notifier(&n->migration_state, + virtio_net_migration_state_notifier); n->host_features |= (1ULL << VIRTIO_NET_F_STANDBY); } @@ -3455,12 +3699,12 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) } if (n->net_conf.tx_queue_size < VIRTIO_NET_TX_QUEUE_MIN_SIZE || - n->net_conf.tx_queue_size > VIRTQUEUE_MAX_SIZE || + n->net_conf.tx_queue_size > virtio_net_max_tx_queue_size(n) || !is_power_of_2(n->net_conf.tx_queue_size)) { error_setg(errp, "Invalid tx_queue_size (= %" PRIu16 "), " "must be a power of 2 between %d and %d", n->net_conf.tx_queue_size, VIRTIO_NET_TX_QUEUE_MIN_SIZE, - VIRTQUEUE_MAX_SIZE); + virtio_net_max_tx_queue_size(n)); virtio_cleanup(vdev); return; } @@ -3520,10 +3764,12 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) * Happen when virtio_net_set_netclient_name has been called. */ n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf, - n->netclient_type, n->netclient_name, n); + n->netclient_type, n->netclient_name, + &dev->mem_reentrancy_guard, n); } else { n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf, - object_get_typename(OBJECT(dev)), dev->id, n); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, n); } for (i = 0; i < n->max_queue_pairs; i++) { @@ -3558,15 +3804,15 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) struct virtio_net_config netcfg = {}; memcpy(&netcfg.mac, &n->nic_conf.macaddr, ETH_ALEN); vhost_net_set_config(get_vhost_net(nc->peer), - (uint8_t *)&netcfg, 0, ETH_ALEN, VHOST_SET_CONFIG_TYPE_MASTER); + (uint8_t *)&netcfg, 0, ETH_ALEN, VHOST_SET_CONFIG_TYPE_FRONTEND); } QTAILQ_INIT(&n->rsc_chains); n->qdev = dev; - net_rx_pkt_init(&n->rx_pkt, false); + net_rx_pkt_init(&n->rx_pkt); if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) { - virtio_net_load_ebpf(n); + virtio_net_load_ebpf(n, errp); } } @@ -3594,7 +3840,7 @@ static void virtio_net_device_unrealize(DeviceState *dev) if (n->failover) { qobject_unref(n->primary_opts); device_listener_unregister(&n->primary_listener); - remove_migration_state_change_notifier(&n->migration_state); + migration_remove_notifier(&n->migration_state); } else { assert(n->primary_opts == NULL); } @@ -3620,7 +3866,7 @@ static void virtio_net_instance_init(Object *obj) /* * The default config_size is sizeof(struct virtio_net_config). - * Can be overriden with virtio_net_set_config_size. + * Can be overridden with virtio_net_set_config_size. */ n->config_size = sizeof(struct virtio_net_config); device_add_bootindex_property(obj, &n->nic_conf.bootindex, @@ -3675,7 +3921,7 @@ static const VMStateDescription vmstate_virtio_net = { .name = "virtio-net", .minimum_version_id = VIRTIO_NET_VM_VERSION, .version_id = VIRTIO_NET_VM_VERSION, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, @@ -3728,6 +3974,8 @@ static Property virtio_net_properties[] = { VIRTIO_NET_F_RSS, false), DEFINE_PROP_BIT64("hash", VirtIONet, host_features, VIRTIO_NET_F_HASH_REPORT, false), + DEFINE_PROP_ARRAY("ebpf-rss-fds", VirtIONet, nr_ebpf_rss_fds, + ebpf_rss_fds, qdev_prop_string, char*), DEFINE_PROP_BIT64("guest_rsc_ext", VirtIONet, host_features, VIRTIO_NET_F_RSC_EXT, false), DEFINE_PROP_UINT32("rsc_interval", VirtIONet, rsc_timeout, @@ -3747,6 +3995,12 @@ static Property virtio_net_properties[] = { DEFINE_PROP_INT32("speed", VirtIONet, net_conf.speed, SPEED_UNKNOWN), DEFINE_PROP_STRING("duplex", VirtIONet, net_conf.duplex_str), DEFINE_PROP_BOOL("failover", VirtIONet, failover, false), + DEFINE_PROP_BIT64("guest_uso4", VirtIONet, host_features, + VIRTIO_NET_F_GUEST_USO4, true), + DEFINE_PROP_BIT64("guest_uso6", VirtIONet, host_features, + VIRTIO_NET_F_GUEST_USO6, true), + DEFINE_PROP_BIT64("host_uso", VirtIONet, host_features, + VIRTIO_NET_F_HOST_USO, true), DEFINE_PROP_END_OF_LIST(), }; @@ -3766,6 +4020,8 @@ static void virtio_net_class_init(ObjectClass *klass, void *data) vdc->set_features = virtio_net_set_features; vdc->bad_features = virtio_net_bad_features; vdc->reset = virtio_net_reset; + vdc->queue_reset = virtio_net_queue_reset; + vdc->queue_enable = virtio_net_queue_enable; vdc->set_status = virtio_net_set_status; vdc->guest_notifier_mask = virtio_net_guest_notifier_mask; vdc->guest_notifier_pending = virtio_net_guest_notifier_pending; @@ -3774,6 +4030,7 @@ static void virtio_net_class_init(ObjectClass *klass, void *data) vdc->vmsd = &vmstate_virtio_net_device; vdc->primary_unplug_pending = primary_unplug_pending; vdc->get_vhost = virtio_net_get_vhost; + vdc->toggle_device_iotlb = vhost_toggle_device_iotlb; } static const TypeInfo virtio_net_info = { diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 0b7acf7f89..707487c636 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -40,7 +40,6 @@ #define PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION 0x1 #define VMXNET3_MSIX_BAR_SIZE 0x2000 -#define MIN_BUF_SIZE 60 /* Compatibility flags for migration */ #define VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT 0 @@ -440,19 +439,19 @@ vmxnet3_setup_tx_offloads(VMXNET3State *s) { switch (s->offload_mode) { case VMXNET3_OM_NONE: - net_tx_pkt_build_vheader(s->tx_pkt, false, false, 0); - break; + return net_tx_pkt_build_vheader(s->tx_pkt, false, false, 0); case VMXNET3_OM_CSUM: - net_tx_pkt_build_vheader(s->tx_pkt, false, true, 0); VMW_PKPRN("L4 CSO requested\n"); - break; + return net_tx_pkt_build_vheader(s->tx_pkt, false, true, 0); case VMXNET3_OM_TSO: - net_tx_pkt_build_vheader(s->tx_pkt, true, true, - s->cso_or_gso_size); - net_tx_pkt_update_ip_checksums(s->tx_pkt); VMW_PKPRN("GSO offload requested."); + if (!net_tx_pkt_build_vheader(s->tx_pkt, true, true, + s->cso_or_gso_size)) { + return false; + } + net_tx_pkt_update_ip_checksums(s->tx_pkt); break; default: @@ -651,9 +650,8 @@ static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx) data_len = (txd.len > 0) ? txd.len : VMXNET3_MAX_TX_BUF_SIZE; data_pa = txd.addr; - if (!net_tx_pkt_add_raw_fragment(s->tx_pkt, - data_pa, - data_len)) { + if (!net_tx_pkt_add_raw_fragment_pci(s->tx_pkt, PCI_DEVICE(s), + data_pa, data_len)) { s->skip_current_tx_pkt = true; } } @@ -678,9 +676,12 @@ static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx) vmxnet3_complete_packet(s, qidx, txd_idx); s->tx_sop = true; s->skip_current_tx_pkt = false; - net_tx_pkt_reset(s->tx_pkt); + net_tx_pkt_reset(s->tx_pkt, + net_tx_pkt_unmap_frag_pci, PCI_DEVICE(s)); } } + + net_tx_pkt_reset(s->tx_pkt, net_tx_pkt_unmap_frag_pci, PCI_DEVICE(s)); } static inline void @@ -847,21 +848,20 @@ static void vmxnet3_rx_need_csum_calculate(struct NetRxPkt *pkt, size_t pkt_len) { struct virtio_net_hdr *vhdr; - bool isip4, isip6, istcp, isudp; + bool hasip4, hasip6; + EthL4HdrProto l4hdr_proto; uint8_t *data; int len; - if (!net_rx_pkt_has_virt_hdr(pkt)) { - return; - } - vhdr = net_rx_pkt_get_vhdr(pkt); if (!VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_NEEDS_CSUM)) { return; } - net_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); - if (!(isip4 || isip6) || !(istcp || isudp)) { + net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto); + if (!(hasip4 || hasip6) || + (l4hdr_proto != ETH_L4_HDR_PROTO_TCP && + l4hdr_proto != ETH_L4_HDR_PROTO_UDP)) { return; } @@ -889,7 +889,8 @@ static void vmxnet3_rx_update_descr(struct NetRxPkt *pkt, struct Vmxnet3_RxCompDesc *rxcd) { int csum_ok, is_gso; - bool isip4, isip6, istcp, isudp; + bool hasip4, hasip6; + EthL4HdrProto l4hdr_proto; struct virtio_net_hdr *vhdr; uint8_t offload_type; @@ -898,10 +899,6 @@ static void vmxnet3_rx_update_descr(struct NetRxPkt *pkt, rxcd->tci = net_rx_pkt_get_vlan_tag(pkt); } - if (!net_rx_pkt_has_virt_hdr(pkt)) { - goto nocsum; - } - vhdr = net_rx_pkt_get_vhdr(pkt); /* * Checksum is valid when lower level tell so or when lower level @@ -919,16 +916,18 @@ static void vmxnet3_rx_update_descr(struct NetRxPkt *pkt, goto nocsum; } - net_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); - if ((!istcp && !isudp) || (!isip4 && !isip6)) { + net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto); + if ((l4hdr_proto != ETH_L4_HDR_PROTO_TCP && + l4hdr_proto != ETH_L4_HDR_PROTO_UDP) || + (!hasip4 && !hasip6)) { goto nocsum; } rxcd->cnc = 0; - rxcd->v4 = isip4 ? 1 : 0; - rxcd->v6 = isip6 ? 1 : 0; - rxcd->tcp = istcp ? 1 : 0; - rxcd->udp = isudp ? 1 : 0; + rxcd->v4 = hasip4 ? 1 : 0; + rxcd->v6 = hasip6 ? 1 : 0; + rxcd->tcp = l4hdr_proto == ETH_L4_HDR_PROTO_TCP; + rxcd->udp = l4hdr_proto == ETH_L4_HDR_PROTO_UDP; rxcd->fcs = rxcd->tuc = rxcd->ipc = 1; return; @@ -1161,7 +1160,6 @@ static void vmxnet3_deactivate_device(VMXNET3State *s) { if (s->device_active) { VMW_CBPRN("Deactivating vmxnet3..."); - net_tx_pkt_reset(s->tx_pkt); net_tx_pkt_uninit(s->tx_pkt); net_rx_pkt_uninit(s->rx_pkt); s->device_active = false; @@ -1343,6 +1341,8 @@ static void vmxnet3_update_features(VMXNET3State *s) s->lro_supported, s->lro_supported, 0, + 0, + 0, 0); } } @@ -1441,7 +1441,10 @@ static void vmxnet3_activate_device(VMXNET3State *s) vmxnet3_setup_rx_filtering(s); /* Cache fields from shared memory */ s->mtu = VMXNET3_READ_DRV_SHARED32(d, s->drv_shmem, devRead.misc.mtu); - assert(VMXNET3_MIN_MTU <= s->mtu && s->mtu < VMXNET3_MAX_MTU); + if (s->mtu < VMXNET3_MIN_MTU || s->mtu > VMXNET3_MAX_MTU) { + qemu_log_mask(LOG_GUEST_ERROR, "vmxnet3: Bad MTU size: %u\n", s->mtu); + return; + } VMW_CFPRN("MTU is %u", s->mtu); s->max_rx_frags = @@ -1521,9 +1524,8 @@ static void vmxnet3_activate_device(VMXNET3State *s) /* Preallocate TX packet wrapper */ VMW_CFPRN("Max TX fragments is %u", s->max_tx_frags); - net_tx_pkt_init(&s->tx_pkt, PCI_DEVICE(s), - s->max_tx_frags, s->peer_has_vhdr); - net_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr); + net_tx_pkt_init(&s->tx_pkt, s->max_tx_frags); + net_rx_pkt_init(&s->rx_pkt); /* Read rings memory locations for RX queues */ for (i = 0; i < s->rxq_num; i++) { @@ -1887,7 +1889,7 @@ vmxnet3_io_bar1_read(void *opaque, hwaddr addr, unsigned size) break; default: - VMW_CBPRN("Unknow read BAR1[%" PRIx64 "], %d bytes", addr, size); + VMW_CBPRN("Unknown read BAR1[%" PRIx64 "], %d bytes", addr, size); break; } @@ -1979,7 +1981,6 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size) { VMXNET3State *s = qemu_get_nic_opaque(nc); size_t bytes_indicated; - uint8_t min_buf[MIN_BUF_SIZE]; if (!vmxnet3_can_receive(nc)) { VMW_PKPRN("Cannot receive now"); @@ -1992,19 +1993,16 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size) size -= sizeof(struct virtio_net_hdr); } - /* Pad to minimum Ethernet frame length */ - if (size < sizeof(min_buf)) { - memcpy(min_buf, buf, size); - memset(&min_buf[size], 0, sizeof(min_buf) - size); - buf = min_buf; - size = sizeof(min_buf); - } - net_rx_pkt_set_packet_type(s->rx_pkt, get_eth_packet_type(PKT_GET_ETH_HDR(buf))); if (vmxnet3_rx_filter_may_indicate(s, buf, size)) { - net_rx_pkt_set_protocols(s->rx_pkt, buf, size); + struct iovec iov = { + .iov_base = (void *)buf, + .iov_len = size + }; + + net_rx_pkt_set_protocols(s->rx_pkt, &iov, 1, 0); vmxnet3_rx_need_csum_calculate(s->rx_pkt, buf, size); net_rx_pkt_attach_data(s->rx_pkt, buf, size, s->rx_vlan_stripping); bytes_indicated = vmxnet3_indicate_packet(s) ? size : -1; @@ -2080,7 +2078,7 @@ static void vmxnet3_net_init(VMXNET3State *s) s->nic = qemu_new_nic(&net_vmxnet3_info, &s->conf, object_get_typename(OBJECT(s)), - d->id, s); + d->id, &d->mem_reentrancy_guard, s); s->peer_has_vhdr = vmxnet3_peer_has_vnet_hdr(s); s->tx_sop = true; @@ -2110,20 +2108,14 @@ vmxnet3_unuse_msix_vectors(VMXNET3State *s, int num_vectors) } } -static bool +static void vmxnet3_use_msix_vectors(VMXNET3State *s, int num_vectors) { PCIDevice *d = PCI_DEVICE(s); int i; for (i = 0; i < num_vectors; i++) { - int res = msix_vector_use(d, i); - if (0 > res) { - VMW_WRPRN("Failed to use MSI-X vector %d, error %d", i, res); - vmxnet3_unuse_msix_vectors(s, i); - return false; - } + msix_vector_use(d, i); } - return true; } static bool @@ -2141,13 +2133,8 @@ vmxnet3_init_msix(VMXNET3State *s) VMW_WRPRN("Failed to initialize MSI-X, error %d", res); s->msix_used = false; } else { - if (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) { - VMW_WRPRN("Failed to use MSI-X vectors, error %d", res); - msix_uninit(d, &s->msix_bar, &s->msix_bar); - s->msix_used = false; - } else { - s->msix_used = true; - } + vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS); + s->msix_used = true; } return s->msix_used; } @@ -2320,7 +2307,7 @@ static const VMStateDescription vmxstate_vmxnet3_mcast_list = { .minimum_version_id = 1, .pre_load = vmxnet3_mcast_list_pre_load, .needed = vmxnet3_mc_list_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VBUFFER_UINT32(mcast_list, VMXNET3State, 0, NULL, mcast_list_buff_size), VMSTATE_END_OF_LIST() @@ -2330,7 +2317,7 @@ static const VMStateDescription vmxstate_vmxnet3_mcast_list = { static const VMStateDescription vmstate_vmxnet3_ring = { .name = "vmxnet3-ring", .version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(pa, Vmxnet3Ring), VMSTATE_UINT32(size, Vmxnet3Ring), VMSTATE_UINT32(cell_size, Vmxnet3Ring), @@ -2343,7 +2330,7 @@ static const VMStateDescription vmstate_vmxnet3_ring = { static const VMStateDescription vmstate_vmxnet3_tx_stats = { .name = "vmxnet3-tx-stats", .version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(TSOPktsTxOK, struct UPT1_TxStats), VMSTATE_UINT64(TSOBytesTxOK, struct UPT1_TxStats), VMSTATE_UINT64(ucastPktsTxOK, struct UPT1_TxStats), @@ -2361,7 +2348,7 @@ static const VMStateDescription vmstate_vmxnet3_tx_stats = { static const VMStateDescription vmstate_vmxnet3_txq_descr = { .name = "vmxnet3-txq-descr", .version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(tx_ring, Vmxnet3TxqDescr, 0, vmstate_vmxnet3_ring, Vmxnet3Ring), VMSTATE_STRUCT(comp_ring, Vmxnet3TxqDescr, 0, vmstate_vmxnet3_ring, @@ -2377,7 +2364,7 @@ static const VMStateDescription vmstate_vmxnet3_txq_descr = { static const VMStateDescription vmstate_vmxnet3_rx_stats = { .name = "vmxnet3-rx-stats", .version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(LROPktsRxOK, struct UPT1_RxStats), VMSTATE_UINT64(LROBytesRxOK, struct UPT1_RxStats), VMSTATE_UINT64(ucastPktsRxOK, struct UPT1_RxStats), @@ -2395,7 +2382,7 @@ static const VMStateDescription vmstate_vmxnet3_rx_stats = { static const VMStateDescription vmstate_vmxnet3_rxq_descr = { .name = "vmxnet3-rxq-descr", .version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(rx_ring, Vmxnet3RxqDescr, VMXNET3_RX_RINGS_PER_QUEUE, 0, vmstate_vmxnet3_ring, Vmxnet3Ring), @@ -2412,19 +2399,12 @@ static const VMStateDescription vmstate_vmxnet3_rxq_descr = { static int vmxnet3_post_load(void *opaque, int version_id) { VMXNET3State *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - net_tx_pkt_init(&s->tx_pkt, PCI_DEVICE(s), - s->max_tx_frags, s->peer_has_vhdr); - net_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr); + net_tx_pkt_init(&s->tx_pkt, s->max_tx_frags); + net_rx_pkt_init(&s->rx_pkt); if (s->msix_used) { - if (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) { - VMW_WRPRN("Failed to re-use MSI-X vectors"); - msix_uninit(d, &s->msix_bar, &s->msix_bar); - s->msix_used = false; - return -1; - } + vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS); } if (!vmxnet3_validate_queues(s)) { @@ -2438,7 +2418,7 @@ static int vmxnet3_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_vmxnet3_int_state = { .name = "vmxnet3-int-state", .version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(is_masked, Vmxnet3IntState), VMSTATE_BOOL(is_pending, Vmxnet3IntState), VMSTATE_BOOL(is_asserted, Vmxnet3IntState), @@ -2452,7 +2432,7 @@ static const VMStateDescription vmstate_vmxnet3 = { .minimum_version_id = 1, .pre_save = vmxnet3_pre_save, .post_load = vmxnet3_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, VMXNET3State), VMSTATE_MSIX(parent_obj, VMXNET3State), VMSTATE_BOOL(rx_packets_compound, VMXNET3State), @@ -2488,7 +2468,7 @@ static const VMStateDescription vmstate_vmxnet3 = { VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmxstate_vmxnet3_mcast_list, NULL } diff --git a/hw/net/vmxnet3.h b/hw/net/vmxnet3.h index bf4f6de74a..f9283f9e7b 100644 --- a/hw/net/vmxnet3.h +++ b/hw/net/vmxnet3.h @@ -733,7 +733,7 @@ struct Vmxnet3_TxQueueDesc { struct Vmxnet3_RxQueueDesc { struct Vmxnet3_RxQueueCtrl ctrl; struct Vmxnet3_RxQueueConf conf; - /* Driver read after a GET commad */ + /* Driver read after a GET command */ struct Vmxnet3_QueueStatus status; struct UPT1_RxStats stats; u8 __pad[88]; /* 128 aligned */ diff --git a/hw/net/vmxnet3_defs.h b/hw/net/vmxnet3_defs.h index 71440509ca..64034af6d5 100644 --- a/hw/net/vmxnet3_defs.h +++ b/hw/net/vmxnet3_defs.h @@ -19,7 +19,7 @@ #include "net/net.h" #include "hw/net/vmxnet3.h" -#include "qom/object.h" +#include "hw/pci/pci_device.h" #define TYPE_VMXNET3 "vmxnet3" typedef struct VMXNET3State VMXNET3State; diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 5c815b4f0c..89487b49ba 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -20,6 +20,13 @@ */ #include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qemu/cutils.h" +#include "qemu/log.h" +#include "qemu/qemu-print.h" +#include "qapi/qmp/qdict.h" +#include "qapi/error.h" + #include #include #include @@ -27,18 +34,26 @@ #include "net/net.h" #include "net/checksum.h" #include "net/util.h" -#include "hw/xen/xen-legacy-backend.h" + +#include "hw/xen/xen-backend.h" +#include "hw/xen/xen-bus-helper.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "hw/xen/interface/io/netif.h" +#include "hw/xen/interface/io/xs_wire.h" + +#include "trace.h" /* ------------------------------------------------------------- */ struct XenNetDev { - struct XenLegacyDevice xendev; /* must be first */ - char *mac; + struct XenDevice xendev; /* must be first */ + XenEventChannel *event_channel; + int dev; int tx_work; - int tx_ring_ref; - int rx_ring_ref; + unsigned int tx_ring_ref; + unsigned int rx_ring_ref; struct netif_tx_sring *txs; struct netif_rx_sring *rxs; netif_tx_back_ring_t tx_ring; @@ -47,6 +62,11 @@ struct XenNetDev { NICState *nic; }; +typedef struct XenNetDev XenNetDev; + +#define TYPE_XEN_NET_DEVICE "xen-net-device" +OBJECT_DECLARE_SIMPLE_TYPE(XenNetDev, XEN_NET_DEVICE) + /* ------------------------------------------------------------- */ static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st) @@ -68,7 +88,8 @@ static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, i netdev->tx_ring.rsp_prod_pvt = ++i; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify); if (notify) { - xen_pv_send_notify(&netdev->xendev); + xen_device_notify_event_channel(XEN_DEVICE(netdev), + netdev->event_channel, NULL); } if (i == netdev->tx_ring.req_cons) { @@ -104,13 +125,16 @@ static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING #endif } -static void net_tx_packets(struct XenNetDev *netdev) +static bool net_tx_packets(struct XenNetDev *netdev) { + bool done_something = false; netif_tx_request_t txreq; RING_IDX rc, rp; void *page; void *tmpbuf = NULL; + assert(bql_locked()); + for (;;) { rc = netdev->tx_ring.req_cons; rp = netdev->tx_ring.sring->req_prod; @@ -122,56 +146,59 @@ static void net_tx_packets(struct XenNetDev *netdev) } memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq)); netdev->tx_ring.req_cons = ++rc; + done_something = true; #if 1 /* should not happen in theory, we don't announce the * * feature-{sg,gso,whatelse} flags in xenstore (yet?) */ if (txreq.flags & NETTXF_extra_info) { - xen_pv_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); + qemu_log_mask(LOG_UNIMP, "vif%u: FIXME: extra info flag\n", + netdev->dev); net_tx_error(netdev, &txreq, rc); continue; } if (txreq.flags & NETTXF_more_data) { - xen_pv_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); + qemu_log_mask(LOG_UNIMP, "vif%u: FIXME: more data flag\n", + netdev->dev); net_tx_error(netdev, &txreq, rc); continue; } #endif if (txreq.size < 14) { - xen_pv_printf(&netdev->xendev, 0, "bad packet size: %d\n", - txreq.size); + qemu_log_mask(LOG_GUEST_ERROR, "vif%u: bad packet size: %d\n", + netdev->dev, txreq.size); net_tx_error(netdev, &txreq, rc); continue; } - if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) { - xen_pv_printf(&netdev->xendev, 0, "error: page crossing\n"); + if ((txreq.offset + txreq.size) > XEN_PAGE_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, "vif%u: error: page crossing\n", + netdev->dev); net_tx_error(netdev, &txreq, rc); continue; } - xen_pv_printf(&netdev->xendev, 3, - "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", - txreq.gref, txreq.offset, txreq.size, txreq.flags, - (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", - (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", - (txreq.flags & NETTXF_more_data) ? " more_data" : "", - (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); + trace_xen_netdev_tx(netdev->dev, txreq.gref, txreq.offset, + txreq.size, txreq.flags, + (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", + (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", + (txreq.flags & NETTXF_more_data) ? " more_data" : "", + (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); - page = xen_be_map_grant_ref(&netdev->xendev, txreq.gref, - PROT_READ); + page = xen_device_map_grant_refs(&netdev->xendev, &txreq.gref, 1, + PROT_READ, NULL); if (page == NULL) { - xen_pv_printf(&netdev->xendev, 0, - "error: tx gref dereference failed (%d)\n", - txreq.gref); + qemu_log_mask(LOG_GUEST_ERROR, + "vif%u: tx gref dereference failed (%d)\n", + netdev->dev, txreq.gref); net_tx_error(netdev, &txreq, rc); continue; } if (txreq.flags & NETTXF_csum_blank) { /* have read-only mapping -> can't fill checksum in-place */ if (!tmpbuf) { - tmpbuf = g_malloc(XC_PAGE_SIZE); + tmpbuf = g_malloc(XEN_PAGE_SIZE); } memcpy(tmpbuf, page + txreq.offset, txreq.size); net_checksum_calculate(tmpbuf, txreq.size, CSUM_ALL); @@ -181,7 +208,8 @@ static void net_tx_packets(struct XenNetDev *netdev) qemu_send_packet(qemu_get_queue(netdev->nic), page + txreq.offset, txreq.size); } - xen_be_unmap_grant_ref(&netdev->xendev, page); + xen_device_unmap_grant_refs(&netdev->xendev, page, &txreq.gref, 1, + NULL); net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); } if (!netdev->tx_work) { @@ -190,6 +218,7 @@ static void net_tx_packets(struct XenNetDev *netdev) netdev->tx_work = 0; } g_free(tmpbuf); + return done_something; } /* ------------------------------------------------------------- */ @@ -212,14 +241,13 @@ static void net_rx_response(struct XenNetDev *netdev, resp->status = (int16_t)st; } - xen_pv_printf(&netdev->xendev, 3, - "rx response: idx %d, status %d, flags 0x%x\n", - i, resp->status, resp->flags); + trace_xen_netdev_rx(netdev->dev, i, resp->status, resp->flags); netdev->rx_ring.rsp_prod_pvt = ++i; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify); if (notify) { - xen_pv_send_notify(&netdev->xendev); + xen_device_notify_event_channel(XEN_DEVICE(netdev), + netdev->event_channel, NULL); } } @@ -232,7 +260,9 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size RING_IDX rc, rp; void *page; - if (netdev->xendev.be_state != XenbusStateConnected) { + assert(bql_locked()); + + if (xen_device_backend_get_state(&netdev->xendev) != XenbusStateConnected) { return -1; } @@ -243,25 +273,27 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { return 0; } - if (size > XC_PAGE_SIZE - NET_IP_ALIGN) { - xen_pv_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)", - (unsigned long)size, XC_PAGE_SIZE - NET_IP_ALIGN); + if (size > XEN_PAGE_SIZE - NET_IP_ALIGN) { + qemu_log_mask(LOG_GUEST_ERROR, "vif%u: packet too big (%lu > %ld)", + netdev->dev, (unsigned long)size, + XEN_PAGE_SIZE - NET_IP_ALIGN); return -1; } memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); netdev->rx_ring.req_cons = ++rc; - page = xen_be_map_grant_ref(&netdev->xendev, rxreq.gref, PROT_WRITE); + page = xen_device_map_grant_refs(&netdev->xendev, &rxreq.gref, 1, + PROT_WRITE, NULL); if (page == NULL) { - xen_pv_printf(&netdev->xendev, 0, - "error: rx gref dereference failed (%d)\n", - rxreq.gref); + qemu_log_mask(LOG_GUEST_ERROR, + "vif%u: rx gref dereference failed (%d)\n", + netdev->dev, rxreq.gref); net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0); return -1; } memcpy(page + NET_IP_ALIGN, buf, size); - xen_be_unmap_grant_ref(&netdev->xendev, page); + xen_device_unmap_grant_refs(&netdev->xendev, page, &rxreq.gref, 1, NULL); net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); return size; @@ -275,137 +307,363 @@ static NetClientInfo net_xen_info = { .receive = net_rx_packet, }; -static int net_init(struct XenLegacyDevice *xendev) +static void xen_netdev_realize(XenDevice *xendev, Error **errp) { - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + ERRP_GUARD(); + XenNetDev *netdev = XEN_NET_DEVICE(xendev); + NetClientState *nc; - /* read xenstore entries */ - if (netdev->mac == NULL) { - netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac"); - } + qemu_macaddr_default_if_unset(&netdev->conf.macaddr); - /* do we have all we need? */ - if (netdev->mac == NULL) { - return -1; - } - - if (net_parse_macaddr(netdev->conf.macaddr.a, netdev->mac) < 0) { - return -1; - } + xen_device_frontend_printf(xendev, "mac", "%02x:%02x:%02x:%02x:%02x:%02x", + netdev->conf.macaddr.a[0], + netdev->conf.macaddr.a[1], + netdev->conf.macaddr.a[2], + netdev->conf.macaddr.a[3], + netdev->conf.macaddr.a[4], + netdev->conf.macaddr.a[5]); netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf, - "xen", NULL, netdev); + object_get_typename(OBJECT(xendev)), + DEVICE(xendev)->id, + &xendev->qdev.mem_reentrancy_guard, netdev); - snprintf(qemu_get_queue(netdev->nic)->info_str, - sizeof(qemu_get_queue(netdev->nic)->info_str), - "nic: xenbus vif macaddr=%s", netdev->mac); + nc = qemu_get_queue(netdev->nic); + qemu_format_nic_info_str(nc, netdev->conf.macaddr.a); /* fill info */ - xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1); - xenstore_write_be_int(&netdev->xendev, "feature-rx-flip", 0); + xen_device_backend_printf(xendev, "feature-rx-copy", "%u", 1); + xen_device_backend_printf(xendev, "feature-rx-flip", "%u", 0); - return 0; + trace_xen_netdev_realize(netdev->dev, nc->info_str, nc->peer ? + nc->peer->name : "(none)"); } -static int net_connect(struct XenLegacyDevice *xendev) +static bool net_event(void *_xendev) { - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); - int rx_copy; + XenNetDev *netdev = XEN_NET_DEVICE(_xendev); + bool done_something; - if (xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref", - &netdev->tx_ring_ref) == -1) { - return -1; - } - if (xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref", - &netdev->rx_ring_ref) == -1) { - return 1; - } - if (xenstore_read_fe_int(&netdev->xendev, "event-channel", - &netdev->xendev.remote_port) == -1) { - return -1; + done_something = net_tx_packets(netdev); + qemu_flush_queued_packets(qemu_get_queue(netdev->nic)); + return done_something; +} + +static bool xen_netdev_connect(XenDevice *xendev, Error **errp) +{ + ERRP_GUARD(); + XenNetDev *netdev = XEN_NET_DEVICE(xendev); + unsigned int port, rx_copy; + + assert(bql_locked()); + + if (xen_device_frontend_scanf(xendev, "tx-ring-ref", "%u", + &netdev->tx_ring_ref) != 1) { + error_setg(errp, "failed to read tx-ring-ref"); + return false; } - if (xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy) == -1) { + if (xen_device_frontend_scanf(xendev, "rx-ring-ref", "%u", + &netdev->rx_ring_ref) != 1) { + error_setg(errp, "failed to read rx-ring-ref"); + return false; + } + + if (xen_device_frontend_scanf(xendev, "event-channel", "%u", + &port) != 1) { + error_setg(errp, "failed to read event-channel"); + return false; + } + + if (xen_device_frontend_scanf(xendev, "request-rx-copy", "%u", + &rx_copy) != 1) { rx_copy = 0; } if (rx_copy == 0) { - xen_pv_printf(&netdev->xendev, 0, - "frontend doesn't support rx-copy.\n"); - return -1; + error_setg(errp, "frontend doesn't support rx-copy"); + return false; } - netdev->txs = xen_be_map_grant_ref(&netdev->xendev, - netdev->tx_ring_ref, - PROT_READ | PROT_WRITE); + netdev->txs = xen_device_map_grant_refs(xendev, + &netdev->tx_ring_ref, 1, + PROT_READ | PROT_WRITE, + errp); if (!netdev->txs) { - return -1; + error_prepend(errp, "failed to map tx grant ref: "); + return false; } - netdev->rxs = xen_be_map_grant_ref(&netdev->xendev, - netdev->rx_ring_ref, - PROT_READ | PROT_WRITE); + + netdev->rxs = xen_device_map_grant_refs(xendev, + &netdev->rx_ring_ref, 1, + PROT_READ | PROT_WRITE, + errp); if (!netdev->rxs) { - xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs); - netdev->txs = NULL; - return -1; + error_prepend(errp, "failed to map rx grant ref: "); + return false; } - BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE); - BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XC_PAGE_SIZE); - xen_be_bind_evtchn(&netdev->xendev); + BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XEN_PAGE_SIZE); + BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XEN_PAGE_SIZE); - xen_pv_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, " - "remote port %d, local port %d\n", - netdev->tx_ring_ref, netdev->rx_ring_ref, - netdev->xendev.remote_port, netdev->xendev.local_port); + netdev->event_channel = xen_device_bind_event_channel(xendev, port, + net_event, + netdev, + errp); + if (!netdev->event_channel) { + return false; + } + + trace_xen_netdev_connect(netdev->dev, netdev->tx_ring_ref, + netdev->rx_ring_ref, port); net_tx_packets(netdev); - return 0; + return true; } -static void net_disconnect(struct XenLegacyDevice *xendev) +static void xen_netdev_disconnect(XenDevice *xendev, Error **errp) { - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + XenNetDev *netdev = XEN_NET_DEVICE(xendev); - xen_pv_unbind_evtchn(&netdev->xendev); + trace_xen_netdev_disconnect(netdev->dev); + assert(bql_locked()); + + netdev->tx_ring.sring = NULL; + netdev->rx_ring.sring = NULL; + + if (netdev->event_channel) { + xen_device_unbind_event_channel(xendev, netdev->event_channel, + errp); + netdev->event_channel = NULL; + } if (netdev->txs) { - xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs); + xen_device_unmap_grant_refs(xendev, netdev->txs, + &netdev->tx_ring_ref, 1, errp); netdev->txs = NULL; } if (netdev->rxs) { - xen_be_unmap_grant_ref(&netdev->xendev, netdev->rxs); + xen_device_unmap_grant_refs(xendev, netdev->rxs, + &netdev->rx_ring_ref, 1, errp); netdev->rxs = NULL; } } -static void net_event(struct XenLegacyDevice *xendev) +/* -------------------------------------------------------------------- */ + + +static void xen_netdev_frontend_changed(XenDevice *xendev, + enum xenbus_state frontend_state, + Error **errp) { - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); - net_tx_packets(netdev); - qemu_flush_queued_packets(qemu_get_queue(netdev->nic)); + ERRP_GUARD(); + enum xenbus_state backend_state = xen_device_backend_get_state(xendev); + + trace_xen_netdev_frontend_changed(xendev->name, frontend_state); + + switch (frontend_state) { + case XenbusStateConnected: + if (backend_state == XenbusStateConnected) { + break; + } + + xen_netdev_disconnect(xendev, errp); + if (*errp) { + break; + } + + if (!xen_netdev_connect(xendev, errp)) { + xen_netdev_disconnect(xendev, NULL); + xen_device_backend_set_state(xendev, XenbusStateClosing); + break; + } + + xen_device_backend_set_state(xendev, XenbusStateConnected); + break; + + case XenbusStateClosing: + xen_device_backend_set_state(xendev, XenbusStateClosing); + break; + + case XenbusStateClosed: + case XenbusStateUnknown: + xen_netdev_disconnect(xendev, errp); + if (*errp) { + break; + } + + xen_device_backend_set_state(xendev, XenbusStateClosed); + break; + + case XenbusStateInitialised: + /* + * Linux netback does nothing on the frontend going (back) to + * XenbusStateInitialised, so do the same here. + */ + default: + break; + } } -static int net_free(struct XenLegacyDevice *xendev) +static char *xen_netdev_get_name(XenDevice *xendev, Error **errp) { - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + XenNetDev *netdev = XEN_NET_DEVICE(xendev); + + if (netdev->dev == -1) { + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + char fe_path[XENSTORE_ABS_PATH_MAX + 1]; + int idx = (xen_mode == XEN_EMULATE) ? 0 : 1; + char *value; + + /* Theoretically we could go up to INT_MAX here but that's overkill */ + while (idx < 100) { + snprintf(fe_path, sizeof(fe_path), + "/local/domain/%u/device/vif/%u", + xendev->frontend_id, idx); + value = qemu_xen_xs_read(xenbus->xsh, XBT_NULL, fe_path, NULL); + if (!value) { + if (errno == ENOENT) { + netdev->dev = idx; + goto found; + } + error_setg(errp, "cannot read %s: %s", fe_path, + strerror(errno)); + return NULL; + } + free(value); + idx++; + } + error_setg(errp, "cannot find device index for netdev device"); + return NULL; + } + found: + return g_strdup_printf("%u", netdev->dev); +} + +static void xen_netdev_unrealize(XenDevice *xendev) +{ + XenNetDev *netdev = XEN_NET_DEVICE(xendev); + + trace_xen_netdev_unrealize(netdev->dev); + + /* Disconnect from the frontend in case this has not already happened */ + xen_netdev_disconnect(xendev, NULL); if (netdev->nic) { qemu_del_nic(netdev->nic); - netdev->nic = NULL; } - g_free(netdev->mac); - netdev->mac = NULL; - return 0; } /* ------------------------------------------------------------- */ -struct XenDevOps xen_netdev_ops = { - .size = sizeof(struct XenNetDev), - .flags = DEVOPS_FLAG_NEED_GNTDEV, - .init = net_init, - .initialise = net_connect, - .event = net_event, - .disconnect = net_disconnect, - .free = net_free, +static Property xen_netdev_properties[] = { + DEFINE_NIC_PROPERTIES(XenNetDev, conf), + DEFINE_PROP_INT32("idx", XenNetDev, dev, -1), + DEFINE_PROP_END_OF_LIST(), }; + +static void xen_netdev_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dev_class = DEVICE_CLASS(class); + XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class); + + xendev_class->backend = "qnic"; + xendev_class->device = "vif"; + xendev_class->get_name = xen_netdev_get_name; + xendev_class->realize = xen_netdev_realize; + xendev_class->frontend_changed = xen_netdev_frontend_changed; + xendev_class->unrealize = xen_netdev_unrealize; + set_bit(DEVICE_CATEGORY_NETWORK, dev_class->categories); + dev_class->user_creatable = true; + + device_class_set_props(dev_class, xen_netdev_properties); +} + +static const TypeInfo xen_net_type_info = { + .name = TYPE_XEN_NET_DEVICE, + .parent = TYPE_XEN_DEVICE, + .instance_size = sizeof(XenNetDev), + .class_init = xen_netdev_class_init, +}; + +static void xen_net_register_types(void) +{ + type_register_static(&xen_net_type_info); +} + +type_init(xen_net_register_types) + +/* Called to instantiate a XenNetDev when the backend is detected. */ +static void xen_net_device_create(XenBackendInstance *backend, + QDict *opts, Error **errp) +{ + ERRP_GUARD(); + XenBus *xenbus = xen_backend_get_bus(backend); + const char *name = xen_backend_get_name(backend); + XenDevice *xendev = NULL; + unsigned long number; + const char *macstr; + XenNetDev *net; + MACAddr mac; + + if (qemu_strtoul(name, NULL, 10, &number) || number >= INT_MAX) { + error_setg(errp, "failed to parse name '%s'", name); + goto fail; + } + + trace_xen_netdev_create(number); + + macstr = qdict_get_try_str(opts, "mac"); + if (macstr == NULL) { + error_setg(errp, "no MAC address found"); + goto fail; + } + + if (net_parse_macaddr(mac.a, macstr) < 0) { + error_setg(errp, "failed to parse MAC address"); + goto fail; + } + + xendev = XEN_DEVICE(qdev_new(TYPE_XEN_NET_DEVICE)); + net = XEN_NET_DEVICE(xendev); + + net->dev = number; + memcpy(&net->conf.macaddr, &mac, sizeof(mac)); + + if (qdev_realize_and_unref(DEVICE(xendev), BUS(xenbus), errp)) { + xen_backend_set_device(backend, xendev); + return; + } + + error_prepend(errp, "realization of net device %lu failed: ", + number); + + fail: + if (xendev) { + object_unparent(OBJECT(xendev)); + } +} + +static void xen_net_device_destroy(XenBackendInstance *backend, + Error **errp) +{ + ERRP_GUARD(); + XenDevice *xendev = xen_backend_get_device(backend); + XenNetDev *netdev = XEN_NET_DEVICE(xendev); + + trace_xen_netdev_destroy(netdev->dev); + + object_unparent(OBJECT(xendev)); +} + +static const XenBackendInfo xen_net_backend_info = { + .type = "qnic", + .create = xen_net_device_create, + .destroy = xen_net_device_destroy, +}; + +static void xen_net_register_backend(void) +{ + xen_backend_register(&xen_net_backend_info); +} + +xen_backend_init(xen_net_register_backend); diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c index 0ab6ae91aa..ffe3fc8dbe 100644 --- a/hw/net/xgmac.c +++ b/hw/net/xgmac.c @@ -159,7 +159,7 @@ static const VMStateDescription vmstate_rxtx_stats = { .name = "xgmac_stats", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(rx_bytes, RxTxStats), VMSTATE_UINT64(tx_bytes, RxTxStats), VMSTATE_UINT64(rx, RxTxStats), @@ -173,7 +173,7 @@ static const VMStateDescription vmstate_xgmac = { .name = "xgmac", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(stats, XgmacState, 0, vmstate_rxtx_stats, RxTxStats), VMSTATE_UINT32_ARRAY(regs, XgmacState, R_MAX), VMSTATE_END_OF_LIST() @@ -402,7 +402,8 @@ static void xgmac_enet_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->regs[XGMAC_ADDR_HIGH(0)] = (s->conf.macaddr.a[5] << 8) | diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index 990ff3a1c2..7d1fd37b4a 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -31,7 +31,6 @@ #include "net/net.h" #include "net/checksum.h" -#include "hw/hw.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/stream.h" @@ -524,7 +523,7 @@ static uint64_t enet_read(void *opaque, hwaddr addr, unsigned size) if (addr < ARRAY_SIZE(s->regs)) { r = s->regs[addr]; } - DENET(qemu_log("%s addr=" TARGET_FMT_plx " v=%x\n", + DENET(qemu_log("%s addr=" HWADDR_FMT_plx " v=%x\n", __func__, addr * 4, r)); break; } @@ -630,7 +629,7 @@ static void enet_write(void *opaque, hwaddr addr, break; default: - DENET(qemu_log("%s addr=" TARGET_FMT_plx " v=%x\n", + DENET(qemu_log("%s addr=" HWADDR_FMT_plx " v=%x\n", __func__, addr * 4, (unsigned)value)); if (addr < ARRAY_SIZE(s->regs)) { s->regs[addr] = value; @@ -968,7 +967,8 @@ static void xilinx_enet_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); tdk_init(&s->TEMAC.phy); diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 6e09f7e422..989afaf037 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "qom/object.h" -#include "cpu.h" /* FIXME should not use tswap* */ +#include "exec/tswap.h" #include "hw/sysbus.h" #include "hw/irq.h" #include "hw/qdev-properties.h" @@ -99,7 +99,7 @@ eth_read(void *opaque, hwaddr addr, unsigned int size) case R_RX_CTRL1: case R_RX_CTRL0: r = s->regs[addr]; - D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr * 4, r)); + D(qemu_log("%s " HWADDR_FMT_plx "=%x\n", __func__, addr * 4, r)); break; default: @@ -125,7 +125,7 @@ eth_write(void *opaque, hwaddr addr, if (addr == R_TX_CTRL1) base = 0x800 / 4; - D(qemu_log("%s addr=" TARGET_FMT_plx " val=%x\n", + D(qemu_log("%s addr=" HWADDR_FMT_plx " val=%x\n", __func__, addr * 4, value)); if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { qemu_send_packet(qemu_get_queue(s->nic), @@ -155,7 +155,7 @@ eth_write(void *opaque, hwaddr addr, case R_TX_LEN0: case R_TX_LEN1: case R_TX_GIE0: - D(qemu_log("%s addr=" TARGET_FMT_plx " val=%x\n", + D(qemu_log("%s addr=" HWADDR_FMT_plx " val=%x\n", __func__, addr * 4, value)); s->regs[addr] = value; break; @@ -235,7 +235,8 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/nios2/10m50_devboard.c b/hw/nios2/10m50_devboard.c index 91383fb097..6cb32f777b 100644 --- a/hw/nios2/10m50_devboard.c +++ b/hw/nios2/10m50_devboard.c @@ -98,7 +98,7 @@ static void nios2_10m50_ghrd_init(MachineState *machine) qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); if (nms->vic) { - DeviceState *dev = qdev_new(TYPE_NIOS2_VIC); + dev = qdev_new(TYPE_NIOS2_VIC); MemoryRegion *dev_mr; qemu_irq cpu_irq; @@ -107,7 +107,7 @@ static void nios2_10m50_ghrd_init(MachineState *machine) cpu_irq = qdev_get_gpio_in_named(DEVICE(cpu), "EIC", 0); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irq); - for (int i = 0; i < 32; i++) { + for (i = 0; i < 32; i++) { irq[i] = qdev_get_gpio_in(dev, i); } @@ -160,6 +160,7 @@ static void nios2_10m50_ghrd_class_init(ObjectClass *oc, void *data) mc->desc = "Altera 10M50 GHRD Nios II design"; mc->init = nios2_10m50_ghrd_init; mc->is_default = true; + mc->deprecation_reason = "Nios II architecture is deprecated"; object_class_property_add_bool(oc, "vic", get_vic, set_vic); object_class_property_set_description(oc, "vic", diff --git a/hw/nios2/boot.c b/hw/nios2/boot.c index 07b8d87633..cd75803fc2 100644 --- a/hw/nios2/boot.c +++ b/hw/nios2/boot.c @@ -34,6 +34,7 @@ #include "qemu/option.h" #include "qemu/config-file.h" #include "qemu/error-report.h" +#include "qemu/guest-random.h" #include "sysemu/device_tree.h" #include "sysemu/reset.h" #include "hw/boards.h" @@ -42,6 +43,8 @@ #include "boot.h" +#include + #define NIOS2_MAGIC 0x534f494e static struct nios2_boot_info { @@ -80,9 +83,11 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr) static int nios2_load_dtb(struct nios2_boot_info bi, const uint32_t ramsize, const char *kernel_cmdline, const char *dtb_filename) { + MachineState *machine = MACHINE(qdev_get_machine()); int fdt_size; void *fdt = NULL; int r; + uint8_t rng_seed[32]; if (dtb_filename) { fdt = load_device_tree(dtb_filename, &fdt_size); @@ -91,6 +96,9 @@ static int nios2_load_dtb(struct nios2_boot_info bi, const uint32_t ramsize, return 0; } + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); + if (kernel_cmdline) { r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); @@ -108,7 +116,10 @@ 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); + + /* Set machine->fdt for 'dumpdtb' QMP/HMP command */ + machine->fdt = fdt; + return fdt_size; } @@ -137,16 +148,11 @@ void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, if (kernel_filename) { int kernel_size, fdt_size; uint64_t entry, high; - int big_endian = 0; - -#if TARGET_BIG_ENDIAN - big_endian = 1; -#endif /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &entry, NULL, &high, NULL, - big_endian, EM_ALTERA_NIOS2, 0, 0); + TARGET_BIG_ENDIAN, EM_ALTERA_NIOS2, 0, 0); if ((uint32_t)entry == 0xc0000000) { /* * The Nios II processor reference guide documents that the @@ -157,7 +163,7 @@ void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, NULL, &entry, NULL, NULL, NULL, - big_endian, EM_ALTERA_NIOS2, 0, 0); + TARGET_BIG_ENDIAN, EM_ALTERA_NIOS2, 0, 0); boot_info.bootstrap_pc = ddr_base + 0xc0000000 + (entry & 0x07ffffff); } else { diff --git a/hw/nios2/generic_nommu.c b/hw/nios2/generic_nommu.c index 48edb3ae37..defa16953f 100644 --- a/hw/nios2/generic_nommu.c +++ b/hw/nios2/generic_nommu.c @@ -95,6 +95,7 @@ static void nios2_generic_nommu_machine_init(struct MachineClass *mc) { mc->desc = "Generic NOMMU Nios II design"; mc->init = nios2_generic_nommu_init; + mc->deprecation_reason = "Nios II architecture is deprecated"; } DEFINE_MACHINE("nios2-generic-nommu", nios2_generic_nommu_machine_init); diff --git a/hw/nios2/meson.build b/hw/nios2/meson.build index 6c58e8082b..22277bd6c5 100644 --- a/hw/nios2/meson.build +++ b/hw/nios2/meson.build @@ -1,5 +1,5 @@ nios2_ss = ss.source_set() -nios2_ss.add(files('boot.c')) +nios2_ss.add(files('boot.c'), fdt) nios2_ss.add(when: 'CONFIG_NIOS2_10M50', if_true: files('10m50_devboard.c')) nios2_ss.add(when: 'CONFIG_NIOS2_GENERIC_NOMMU', if_true: files('generic_nommu.c')) diff --git a/hw/nubus/meson.build b/hw/nubus/meson.build index 9287c633aa..9a7a12ea68 100644 --- a/hw/nubus/meson.build +++ b/hw/nubus/meson.build @@ -2,6 +2,7 @@ nubus_ss = ss.source_set() nubus_ss.add(files('nubus-device.c')) nubus_ss.add(files('nubus-bus.c')) nubus_ss.add(files('nubus-bridge.c')) +nubus_ss.add(files('nubus-virtio-mmio.c')) nubus_ss.add(when: 'CONFIG_Q800', if_true: files('mac-nubus-bridge.c')) -softmmu_ss.add_all(when: 'CONFIG_NUBUS', if_true: nubus_ss) +system_ss.add_all(when: 'CONFIG_NUBUS', if_true: nubus_ss) diff --git a/hw/nubus/nubus-device.c b/hw/nubus/nubus-device.c index 0f1852f671..be4cb24696 100644 --- a/hw/nubus/nubus-device.c +++ b/hw/nubus/nubus-device.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" +#include "exec/target_page.h" #include "hw/irq.h" #include "hw/loader.h" #include "hw/nubus/nubus.h" @@ -30,7 +31,8 @@ static void nubus_device_realize(DeviceState *dev, Error **errp) NubusDevice *nd = NUBUS_DEVICE(dev); char *name, *path; hwaddr slot_offset; - int64_t size; + int64_t size, align_size; + uint8_t *rom_ptr; int ret; /* Super */ @@ -76,15 +78,24 @@ static void nubus_device_realize(DeviceState *dev, Error **errp) } name = g_strdup_printf("nubus-slot-%x-declaration-rom", nd->slot); - memory_region_init_rom(&nd->decl_rom, OBJECT(dev), name, size, + + /* + * Ensure ROM memory region is aligned to target page size regardless + * of the size of the Declaration ROM image + */ + align_size = ROUND_UP(size, qemu_target_page_size()); + memory_region_init_rom(&nd->decl_rom, OBJECT(dev), name, align_size, &error_abort); - ret = load_image_mr(path, &nd->decl_rom); + rom_ptr = memory_region_get_ram_ptr(&nd->decl_rom); + ret = load_image_size(path, rom_ptr + (uintptr_t)(align_size - size), + size); g_free(path); + g_free(name); if (ret < 0) { error_setg(errp, "could not load romfile \"%s\"", nd->romfile); return; } - memory_region_add_subregion(&nd->slot_mem, NUBUS_SLOT_SIZE - size, + memory_region_add_subregion(&nd->slot_mem, NUBUS_SLOT_SIZE - align_size, &nd->decl_rom); } } diff --git a/hw/nubus/nubus-virtio-mmio.c b/hw/nubus/nubus-virtio-mmio.c new file mode 100644 index 0000000000..58a63c84d0 --- /dev/null +++ b/hw/nubus/nubus-virtio-mmio.c @@ -0,0 +1,102 @@ +/* + * QEMU Macintosh Nubus Virtio MMIO card + * + * Copyright (c) 2024 Mark Cave-Ayland + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/nubus/nubus-virtio-mmio.h" + + +#define NUBUS_VIRTIO_MMIO_PIC_OFFSET 0 +#define NUBUS_VIRTIO_MMIO_DEV_OFFSET 0x200 + + +static void nubus_virtio_mmio_set_input_irq(void *opaque, int n, int level) +{ + NubusDevice *nd = NUBUS_DEVICE(opaque); + + nubus_set_irq(nd, level); +} + +static void nubus_virtio_mmio_realize(DeviceState *dev, Error **errp) +{ + NubusVirtioMMIODeviceClass *nvmdc = NUBUS_VIRTIO_MMIO_GET_CLASS(dev); + NubusVirtioMMIO *s = NUBUS_VIRTIO_MMIO(dev); + NubusDevice *nd = NUBUS_DEVICE(dev); + SysBusDevice *sbd; + int i, offset; + + nvmdc->parent_realize(dev, errp); + if (*errp) { + return; + } + + /* Goldfish PIC */ + sbd = SYS_BUS_DEVICE(&s->pic); + if (!sysbus_realize(sbd, errp)) { + return; + } + memory_region_add_subregion(&nd->slot_mem, NUBUS_VIRTIO_MMIO_PIC_OFFSET, + sysbus_mmio_get_region(sbd, 0)); + sysbus_connect_irq(sbd, 0, + qdev_get_gpio_in_named(dev, "pic-input-irq", 0)); + + /* virtio-mmio devices */ + offset = NUBUS_VIRTIO_MMIO_DEV_OFFSET; + for (i = 0; i < NUBUS_VIRTIO_MMIO_NUM_DEVICES; i++) { + sbd = SYS_BUS_DEVICE(&s->virtio_mmio[i]); + qdev_prop_set_bit(DEVICE(sbd), "force-legacy", false); + if (!sysbus_realize_and_unref(sbd, errp)) { + return; + } + + memory_region_add_subregion(&nd->slot_mem, offset, + sysbus_mmio_get_region(sbd, 0)); + offset += 0x200; + + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(DEVICE(&s->pic), i)); + } +} + +static void nubus_virtio_mmio_init(Object *obj) +{ + NubusVirtioMMIO *s = NUBUS_VIRTIO_MMIO(obj); + int i; + + object_initialize_child(obj, "pic", &s->pic, TYPE_GOLDFISH_PIC); + for (i = 0; i < NUBUS_VIRTIO_MMIO_NUM_DEVICES; i++) { + char *name = g_strdup_printf("virtio-mmio[%d]", i); + object_initialize_child(obj, name, &s->virtio_mmio[i], + TYPE_VIRTIO_MMIO); + g_free(name); + } + + /* Input from goldfish PIC */ + qdev_init_gpio_in_named(DEVICE(obj), nubus_virtio_mmio_set_input_irq, + "pic-input-irq", 1); +} + +static void nubus_virtio_mmio_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + NubusVirtioMMIODeviceClass *nvmdc = NUBUS_VIRTIO_MMIO_CLASS(oc); + + device_class_set_parent_realize(dc, nubus_virtio_mmio_realize, + &nvmdc->parent_realize); +} + +static const TypeInfo nubus_virtio_mmio_types[] = { + { + .name = TYPE_NUBUS_VIRTIO_MMIO, + .parent = TYPE_NUBUS_DEVICE, + .instance_init = nubus_virtio_mmio_init, + .instance_size = sizeof(NubusVirtioMMIO), + .class_init = nubus_virtio_mmio_class_init, + .class_size = sizeof(NubusVirtioMMIODeviceClass), + }, +}; + +DEFINE_TYPES(nubus_virtio_mmio_types) diff --git a/hw/nubus/trace-events b/hw/nubus/trace-events index e31833d694..9259d66725 100644 --- a/hw/nubus/trace-events +++ b/hw/nubus/trace-events @@ -1,4 +1,4 @@ -# See docs/devel/tracing.txt for syntax documentation. +# See docs/devel/tracing.rst for syntax documentation. # nubus-bus.c nubus_slot_read(uint64_t addr, int size) "reading unassigned addr 0x%"PRIx64 " size %d" diff --git a/hw/nvme/Kconfig b/hw/nvme/Kconfig index 8ac90942e5..cfa2ab0f9d 100644 --- a/hw/nvme/Kconfig +++ b/hw/nvme/Kconfig @@ -1,4 +1,4 @@ config NVME_PCI bool - default y if PCI_DEVICES + default y if PCI_DEVICES || PCIE_DEVICES depends on PCI diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 1e6e0fcad9..127c3d2383 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -17,7 +17,7 @@ * Notes on coding style * --------------------- * While QEMU coding style prefers lowercase hexadecimals in constants, the - * NVMe subsystem use thes format from the NVMe specifications in the comments + * NVMe subsystem use this format from the NVMe specifications in the comments * (i.e. 'h' suffix instead of '0x' prefix). * * Usage @@ -35,10 +35,22 @@ * mdts=,vsl=, \ * zoned.zasl=, \ * zoned.auto_transition=, \ + * sriov_max_vfs= \ + * sriov_vq_flexible= \ + * sriov_vi_flexible= \ + * sriov_max_vi_per_vf= \ + * sriov_max_vq_per_vf= \ * subsys= * -device nvme-ns,drive=,bus=,nsid=,\ * zoned=, \ - * subsys=,detached= + * subsys=,shared=, \ + * detached=, \ + * zoned.zone_size=, \ + * zoned.zone_capacity=, \ + * zoned.descr_ext_size=, \ + * zoned.max_active=, \ + * zoned.max_open=, \ + * zoned.cross_read= * * Note cmb_size_mb denotes size of CMB in MB. CMB is assumed to be at * offset 0 in BAR2 and supports only WDS, RDS and SQS for now. By default, the @@ -71,7 +83,7 @@ * the SUBNQN field in the controller will report the NQN of the subsystem * device. This also enables multi controller capability represented in * Identify Controller data structure in CMIC (Controller Multi-path I/O and - * Namesapce Sharing Capabilities). + * Namespace Sharing Capabilities). * * - `aerl` * The Asynchronous Event Request Limit (AERL). Indicates the maximum number @@ -106,6 +118,35 @@ * transitioned to zone state closed for resource management purposes. * Defaults to 'on'. * + * - `sriov_max_vfs` + * Indicates the maximum number of PCIe virtual functions supported + * by the controller. The default value is 0. Specifying a non-zero value + * enables reporting of both SR-IOV and ARI capabilities by the NVMe device. + * Virtual function controllers will not report SR-IOV capability. + * + * NOTE: Single Root I/O Virtualization support is experimental. + * All the related parameters may be subject to change. + * + * - `sriov_vq_flexible` + * Indicates the total number of flexible queue resources assignable to all + * the secondary controllers. Implicitly sets the number of primary + * controller's private resources to `(max_ioqpairs - sriov_vq_flexible)`. + * + * - `sriov_vi_flexible` + * Indicates the total number of flexible interrupt resources assignable to + * all the secondary controllers. Implicitly sets the number of primary + * controller's private resources to `(msix_qsize - sriov_vi_flexible)`. + * + * - `sriov_max_vi_per_vf` + * Indicates the maximum number of virtual interrupt resources assignable + * to a secondary controller. The default 0 resolves to + * `(sriov_vi_flexible / sriov_max_vfs)`. + * + * - `sriov_max_vq_per_vf` + * Indicates the maximum number of virtual queue resources assignable to + * a secondary controller. The default 0 resolves to + * `(sriov_vq_flexible / sriov_max_vfs)`. + * * nvme namespace device parameters * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * - `shared` @@ -154,12 +195,14 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/units.h" +#include "qemu/range.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" #include "sysemu/hostmem.h" #include "hw/pci/msix.h" +#include "hw/pci/pcie_sriov.h" #include "migration/vmstate.h" #include "nvme.h" @@ -176,6 +219,10 @@ #define NVME_TEMPERATURE_CRITICAL 0x175 #define NVME_NUM_FW_SLOTS 1 #define NVME_DEFAULT_MAX_ZA_SIZE (128 * KiB) +#define NVME_MAX_VFS 127 +#define NVME_VF_RES_GRANULARITY 1 +#define NVME_VF_OFFSET 0x1 +#define NVME_VF_STRIDE 1 #define NVME_GUEST_ERR(trace, fmt, ...) \ do { \ @@ -198,6 +245,8 @@ static const bool nvme_feature_support[NVME_FID_MAX] = { [NVME_TIMESTAMP] = true, [NVME_HOST_BEHAVIOR_SUPPORT] = true, [NVME_COMMAND_SET_PROFILE] = true, + [NVME_FDP_MODE] = true, + [NVME_FDP_EVENTS] = true, }; static const uint32_t nvme_feature_cap[NVME_FID_MAX] = { @@ -209,6 +258,8 @@ static const uint32_t nvme_feature_cap[NVME_FID_MAX] = { [NVME_TIMESTAMP] = NVME_FEAT_CAP_CHANGE, [NVME_HOST_BEHAVIOR_SUPPORT] = NVME_FEAT_CAP_CHANGE, [NVME_COMMAND_SET_PROFILE] = NVME_FEAT_CAP_CHANGE, + [NVME_FDP_MODE] = NVME_FEAT_CAP_CHANGE, + [NVME_FDP_EVENTS] = NVME_FEAT_CAP_CHANGE | NVME_FEAT_CAP_NS, }; static const uint32_t nvme_cse_acs[256] = { @@ -223,7 +274,11 @@ static const uint32_t nvme_cse_acs[256] = { [NVME_ADM_CMD_GET_FEATURES] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_ASYNC_EV_REQ] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_NS_ATTACHMENT] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_NIC, + [NVME_ADM_CMD_VIRT_MNGMT] = NVME_CMD_EFF_CSUPP, + [NVME_ADM_CMD_DBBUF_CONFIG] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_FORMAT_NVM] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, + [NVME_ADM_CMD_DIRECTIVE_RECV] = NVME_CMD_EFF_CSUPP, + [NVME_ADM_CMD_DIRECTIVE_SEND] = NVME_CMD_EFF_CSUPP, }; static const uint32_t nvme_cse_iocs_none[256]; @@ -237,6 +292,8 @@ static const uint32_t nvme_cse_iocs_nvm[256] = { [NVME_CMD_VERIFY] = NVME_CMD_EFF_CSUPP, [NVME_CMD_COPY] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_COMPARE] = NVME_CMD_EFF_CSUPP, + [NVME_CMD_IO_MGMT_RECV] = NVME_CMD_EFF_CSUPP, + [NVME_CMD_IO_MGMT_SEND] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, }; static const uint32_t nvme_cse_iocs_zoned[256] = { @@ -254,12 +311,67 @@ static const uint32_t nvme_cse_iocs_zoned[256] = { }; static void nvme_process_sq(void *opaque); +static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst); +static inline uint64_t nvme_get_timestamp(const NvmeCtrl *n); static uint16_t nvme_sqid(NvmeRequest *req) { return le16_to_cpu(req->sq->sqid); } +static inline uint16_t nvme_make_pid(NvmeNamespace *ns, uint16_t rg, + uint16_t ph) +{ + uint16_t rgif = ns->endgrp->fdp.rgif; + + if (!rgif) { + return ph; + } + + return (rg << (16 - rgif)) | ph; +} + +static inline bool nvme_ph_valid(NvmeNamespace *ns, uint16_t ph) +{ + return ph < ns->fdp.nphs; +} + +static inline bool nvme_rg_valid(NvmeEnduranceGroup *endgrp, uint16_t rg) +{ + return rg < endgrp->fdp.nrg; +} + +static inline uint16_t nvme_pid2ph(NvmeNamespace *ns, uint16_t pid) +{ + uint16_t rgif = ns->endgrp->fdp.rgif; + + if (!rgif) { + return pid; + } + + return pid & ((1 << (15 - rgif)) - 1); +} + +static inline uint16_t nvme_pid2rg(NvmeNamespace *ns, uint16_t pid) +{ + uint16_t rgif = ns->endgrp->fdp.rgif; + + if (!rgif) { + return 0; + } + + return pid >> (16 - rgif); +} + +static inline bool nvme_parse_pid(NvmeNamespace *ns, uint16_t pid, + uint16_t *ph, uint16_t *rg) +{ + *rg = nvme_pid2rg(ns, pid); + *ph = nvme_pid2ph(ns, pid); + + return nvme_ph_valid(ns, *ph) && nvme_rg_valid(ns->endgrp, *rg); +} + static void nvme_assign_zone_state(NvmeNamespace *ns, NvmeZone *zone, NvmeZoneState state) { @@ -333,6 +445,69 @@ static uint16_t nvme_aor_check(NvmeNamespace *ns, uint32_t act, uint32_t opn) return nvme_zns_check_resources(ns, act, opn, 0); } +static NvmeFdpEvent *nvme_fdp_alloc_event(NvmeCtrl *n, NvmeFdpEventBuffer *ebuf) +{ + NvmeFdpEvent *ret = NULL; + bool is_full = ebuf->next == ebuf->start && ebuf->nelems; + + ret = &ebuf->events[ebuf->next++]; + if (unlikely(ebuf->next == NVME_FDP_MAX_EVENTS)) { + ebuf->next = 0; + } + if (is_full) { + ebuf->start = ebuf->next; + } else { + ebuf->nelems++; + } + + memset(ret, 0, sizeof(NvmeFdpEvent)); + ret->timestamp = nvme_get_timestamp(n); + + return ret; +} + +static inline int log_event(NvmeRuHandle *ruh, uint8_t event_type) +{ + return (ruh->event_filter >> nvme_fdp_evf_shifts[event_type]) & 0x1; +} + +static bool nvme_update_ruh(NvmeCtrl *n, NvmeNamespace *ns, uint16_t pid) +{ + NvmeEnduranceGroup *endgrp = ns->endgrp; + NvmeRuHandle *ruh; + NvmeReclaimUnit *ru; + NvmeFdpEvent *e = NULL; + uint16_t ph, rg, ruhid; + + if (!nvme_parse_pid(ns, pid, &ph, &rg)) { + return false; + } + + ruhid = ns->fdp.phs[ph]; + + ruh = &endgrp->fdp.ruhs[ruhid]; + ru = &ruh->rus[rg]; + + if (ru->ruamw) { + if (log_event(ruh, FDP_EVT_RU_NOT_FULLY_WRITTEN)) { + e = nvme_fdp_alloc_event(n, &endgrp->fdp.host_events); + e->type = FDP_EVT_RU_NOT_FULLY_WRITTEN; + e->flags = FDPEF_PIV | FDPEF_NSIDV | FDPEF_LV; + e->pid = cpu_to_le16(pid); + e->nsid = cpu_to_le32(ns->params.nsid); + e->rgid = cpu_to_le16(rg); + e->ruhid = cpu_to_le16(ruhid); + } + + /* log (eventual) GC overhead of prematurely swapping the RU */ + nvme_fdp_stat_inc(&endgrp->fdp.mbmw, nvme_l2b(ns, ru->ruamw)); + } + + ru->ruamw = ruh->ruamw; + + return true; +} + static bool nvme_addr_is_cmb(NvmeCtrl *n, hwaddr addr) { hwaddr hi, lo; @@ -406,7 +581,7 @@ static int nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size) return 0; } - return pci_dma_read(&n->parent_obj, addr, buf, size); + return pci_dma_read(PCI_DEVICE(n), addr, buf, size); } static int nvme_addr_write(NvmeCtrl *n, hwaddr addr, const void *buf, int size) @@ -426,7 +601,7 @@ static int nvme_addr_write(NvmeCtrl *n, hwaddr addr, const void *buf, int size) return 0; } - return pci_dma_write(&n->parent_obj, addr, buf, size); + return pci_dma_write(PCI_DEVICE(n), addr, buf, size); } static bool nvme_nsid_valid(NvmeCtrl *n, uint32_t nsid) @@ -437,12 +612,12 @@ static bool nvme_nsid_valid(NvmeCtrl *n, uint32_t nsid) static int nvme_check_sqid(NvmeCtrl *n, uint16_t sqid) { - return sqid < n->params.max_ioqpairs + 1 && n->sq[sqid] != NULL ? 0 : -1; + return sqid < n->conf_ioqpairs + 1 && n->sq[sqid] != NULL ? 0 : -1; } static int nvme_check_cqid(NvmeCtrl *n, uint16_t cqid) { - return cqid < n->params.max_ioqpairs + 1 && n->cq[cqid] != NULL ? 0 : -1; + return cqid < n->conf_ioqpairs + 1 && n->cq[cqid] != NULL ? 0 : -1; } static void nvme_inc_cq_tail(NvmeCQueue *cq) @@ -471,24 +646,27 @@ static uint8_t nvme_sq_empty(NvmeSQueue *sq) static void nvme_irq_check(NvmeCtrl *n) { + PCIDevice *pci = PCI_DEVICE(n); uint32_t intms = ldl_le_p(&n->bar.intms); - if (msix_enabled(&(n->parent_obj))) { + if (msix_enabled(pci)) { return; } if (~intms & n->irq_status) { - pci_irq_assert(&n->parent_obj); + pci_irq_assert(pci); } else { - pci_irq_deassert(&n->parent_obj); + pci_irq_deassert(pci); } } static void nvme_irq_assert(NvmeCtrl *n, NvmeCQueue *cq) { + PCIDevice *pci = PCI_DEVICE(n); + if (cq->irq_enabled) { - if (msix_enabled(&(n->parent_obj))) { + if (msix_enabled(pci)) { trace_pci_nvme_irq_msix(cq->vector); - msix_notify(&(n->parent_obj), cq->vector); + msix_notify(pci, cq->vector); } else { trace_pci_nvme_irq_pin(); assert(cq->vector < 32); @@ -503,7 +681,7 @@ static void nvme_irq_assert(NvmeCtrl *n, NvmeCQueue *cq) static void nvme_irq_deassert(NvmeCtrl *n, NvmeCQueue *cq) { if (cq->irq_enabled) { - if (msix_enabled(&(n->parent_obj))) { + if (msix_enabled(PCI_DEVICE(n))) { return; } else { assert(cq->vector < 32); @@ -527,7 +705,7 @@ static void nvme_req_clear(NvmeRequest *req) static inline void nvme_sg_init(NvmeCtrl *n, NvmeSg *sg, bool dma) { if (dma) { - pci_dma_sglist_init(&sg->qsg, &n->parent_obj, 0); + pci_dma_sglist_init(&sg->qsg, PCI_DEVICE(n), 0); sg->flags = NVME_SG_DMA; } else { qemu_iovec_init(&sg->iov, 0); @@ -552,7 +730,7 @@ static inline void nvme_sg_unmap(NvmeSg *sg) } /* - * When metadata is transfered as extended LBAs, the DPTR mapped into `sg` + * When metadata is transferred as extended LBAs, the DPTR mapped into `sg` * holds both data and metadata. This function splits the data and metadata * into two separate QSG/IOVs. */ @@ -716,7 +894,7 @@ static uint16_t nvme_map_prp(NvmeCtrl *n, NvmeSg *sg, uint64_t prp1, len -= trans_len; if (len) { if (len > n->page_size) { - uint64_t prp_list[n->max_prp_ents]; + g_autofree uint64_t *prp_list = g_new(uint64_t, n->max_prp_ents); uint32_t nents, prp_trans; int i = 0; @@ -808,10 +986,6 @@ static uint16_t nvme_map_sgl_data(NvmeCtrl *n, NvmeSg *sg, uint8_t type = NVME_SGL_TYPE(segment[i].type); switch (type) { - case NVME_SGL_DESCR_TYPE_BIT_BUCKET: - if (cmd->opcode == NVME_CMD_WRITE) { - continue; - } case NVME_SGL_DESCR_TYPE_DATA_BLOCK: break; case NVME_SGL_DESCR_TYPE_SEGMENT: @@ -844,10 +1018,6 @@ static uint16_t nvme_map_sgl_data(NvmeCtrl *n, NvmeSg *sg, trans_len = MIN(*len, dlen); - if (type == NVME_SGL_DESCR_TYPE_BIT_BUCKET) { - goto next; - } - addr = le64_to_cpu(segment[i].addr); if (UINT64_MAX - addr < dlen) { @@ -859,7 +1029,6 @@ static uint16_t nvme_map_sgl_data(NvmeCtrl *n, NvmeSg *sg, return status; } -next: *len -= trans_len; } @@ -876,7 +1045,7 @@ static uint16_t nvme_map_sgl(NvmeCtrl *n, NvmeSg *sg, NvmeSglDescriptor sgl, * descriptors and segment chain) than the command transfer size, so it is * not bounded by MDTS. */ - const int SEG_CHUNK_SIZE = 256; +#define SEG_CHUNK_SIZE 256 NvmeSglDescriptor segment[SEG_CHUNK_SIZE], *sgld, *last_sgld; uint64_t nsgld; @@ -917,8 +1086,7 @@ static uint16_t nvme_map_sgl(NvmeCtrl *n, NvmeSg *sg, NvmeSglDescriptor sgl, seg_len = le32_to_cpu(sgld->len); /* check the length of the (Last) Segment descriptor */ - if ((!seg_len || seg_len & 0xf) && - (NVME_SGL_TYPE(sgld->type) != NVME_SGL_DESCR_TYPE_BIT_BUCKET)) { + if (!seg_len || seg_len & 0xf) { return NVME_INVALID_SGL_SEG_DESCR | NVME_DNR; } @@ -956,26 +1124,20 @@ static uint16_t nvme_map_sgl(NvmeCtrl *n, NvmeSg *sg, NvmeSglDescriptor sgl, last_sgld = &segment[nsgld - 1]; /* - * If the segment ends with a Data Block or Bit Bucket Descriptor Type, - * then we are done. + * If the segment ends with a Data Block, then we are done. */ - switch (NVME_SGL_TYPE(last_sgld->type)) { - case NVME_SGL_DESCR_TYPE_DATA_BLOCK: - case NVME_SGL_DESCR_TYPE_BIT_BUCKET: + if (NVME_SGL_TYPE(last_sgld->type) == NVME_SGL_DESCR_TYPE_DATA_BLOCK) { status = nvme_map_sgl_data(n, sg, segment, nsgld, &len, cmd); if (status) { goto unmap; } goto out; - - default: - break; } /* - * If the last descriptor was not a Data Block or Bit Bucket, then the - * current segment must not be a Last Segment. + * If the last descriptor was not a Data Block, then the current + * segment must not be a Last Segment. */ if (NVME_SGL_TYPE(sgld->type) == NVME_SGL_DESCR_TYPE_LAST_SEGMENT) { status = NVME_INVALID_SGL_SEG_DESCR | NVME_DNR; @@ -1279,31 +1441,47 @@ uint16_t nvme_bounce_mdata(NvmeCtrl *n, void *ptr, uint32_t len, } static inline void nvme_blk_read(BlockBackend *blk, int64_t offset, - BlockCompletionFunc *cb, NvmeRequest *req) + uint32_t align, BlockCompletionFunc *cb, + NvmeRequest *req) { assert(req->sg.flags & NVME_SG_ALLOC); if (req->sg.flags & NVME_SG_DMA) { - req->aiocb = dma_blk_read(blk, &req->sg.qsg, offset, BDRV_SECTOR_SIZE, - cb, req); + req->aiocb = dma_blk_read(blk, &req->sg.qsg, offset, align, cb, req); } else { req->aiocb = blk_aio_preadv(blk, offset, &req->sg.iov, 0, cb, req); } } static inline void nvme_blk_write(BlockBackend *blk, int64_t offset, - BlockCompletionFunc *cb, NvmeRequest *req) + uint32_t align, BlockCompletionFunc *cb, + NvmeRequest *req) { assert(req->sg.flags & NVME_SG_ALLOC); if (req->sg.flags & NVME_SG_DMA) { - req->aiocb = dma_blk_write(blk, &req->sg.qsg, offset, BDRV_SECTOR_SIZE, - cb, req); + req->aiocb = dma_blk_write(blk, &req->sg.qsg, offset, align, cb, req); } else { req->aiocb = blk_aio_pwritev(blk, offset, &req->sg.iov, 0, cb, req); } } +static void nvme_update_cq_eventidx(const NvmeCQueue *cq) +{ + trace_pci_nvme_update_cq_eventidx(cq->cqid, cq->head); + + stl_le_pci_dma(PCI_DEVICE(cq->ctrl), cq->ei_addr, cq->head, + MEMTXATTRS_UNSPECIFIED); +} + +static void nvme_update_cq_head(NvmeCQueue *cq) +{ + ldl_le_pci_dma(PCI_DEVICE(cq->ctrl), cq->db_addr, &cq->head, + MEMTXATTRS_UNSPECIFIED); + + trace_pci_nvme_update_cq_head(cq->cqid, cq->head); +} + static void nvme_post_cqes(void *opaque) { NvmeCQueue *cq = opaque; @@ -1316,6 +1494,11 @@ static void nvme_post_cqes(void *opaque) NvmeSQueue *sq; hwaddr addr; + if (n->dbbuf_enabled) { + nvme_update_cq_eventidx(cq); + nvme_update_cq_head(cq); + } + if (nvme_cq_full(cq)) { break; } @@ -1324,8 +1507,8 @@ static void nvme_post_cqes(void *opaque) req->cqe.status = cpu_to_le16((req->status << 1) | cq->phase); req->cqe.sq_id = cpu_to_le16(sq->sqid); req->cqe.sq_head = cpu_to_le16(sq->head); - addr = cq->dma_addr + cq->tail * n->cqe_size; - ret = pci_dma_write(&n->parent_obj, addr, (void *)&req->cqe, + addr = cq->dma_addr + (cq->tail << NVME_CQES); + ret = pci_dma_write(PCI_DEVICE(n), addr, (void *)&req->cqe, sizeof(req->cqe)); if (ret) { trace_pci_nvme_err_addr_write(addr); @@ -1362,7 +1545,8 @@ static void nvme_enqueue_req_completion(NvmeCQueue *cq, NvmeRequest *req) QTAILQ_REMOVE(&req->sq->out_req_list, req, entry); QTAILQ_INSERT_TAIL(&cq->req_list, req, entry); - timer_mod(cq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); + + qemu_bh_schedule(cq->bh); } static void nvme_process_aers(void *opaque) @@ -1567,6 +1751,7 @@ static void nvme_aio_err(NvmeRequest *req, int ret) case NVME_CMD_WRITE: case NVME_CMD_WRITE_ZEROES: case NVME_CMD_ZONE_APPEND: + case NVME_CMD_COPY: status = NVME_WRITE_FAULT; break; default: @@ -1945,11 +2130,6 @@ static inline bool nvme_is_write(NvmeRequest *req) rw->opcode == NVME_CMD_WRITE_ZEROES; } -static AioContext *nvme_get_aio_context(BlockAIOCB *acb) -{ - return qemu_get_aio_context(); -} - static void nvme_misc_cb(void *opaque, int ret) { NvmeRequest *req = opaque; @@ -2026,10 +2206,10 @@ static void nvme_rw_cb(void *opaque, int ret) } if (req->cmd.opcode == NVME_CMD_READ) { - return nvme_blk_read(blk, offset, nvme_rw_complete_cb, req); + return nvme_blk_read(blk, offset, 1, nvme_rw_complete_cb, req); } - return nvme_blk_write(blk, offset, nvme_rw_complete_cb, req); + return nvme_blk_write(blk, offset, 1, nvme_rw_complete_cb, req); } } @@ -2197,7 +2377,7 @@ static void nvme_compare_mdata_cb(void *opaque, int ret) for (bufp = buf; mbufp < end; bufp += ns->lbaf.ms, mbufp += ns->lbaf.ms) { if (memcmp(bufp + pil, mbufp + pil, ns->lbaf.ms - pil)) { - req->status = NVME_CMP_FAILURE; + req->status = NVME_CMP_FAILURE | NVME_DNR; goto out; } } @@ -2206,7 +2386,7 @@ static void nvme_compare_mdata_cb(void *opaque, int ret) } if (memcmp(buf, ctx->mdata.bounce, ctx->mdata.iov.size)) { - req->status = NVME_CMP_FAILURE; + req->status = NVME_CMP_FAILURE | NVME_DNR; goto out; } @@ -2255,7 +2435,7 @@ static void nvme_compare_data_cb(void *opaque, int ret) } if (memcmp(buf, ctx->data.bounce, ctx->data.iov.size)) { - req->status = NVME_CMP_FAILURE; + req->status = NVME_CMP_FAILURE | NVME_DNR; goto out; } @@ -2290,7 +2470,6 @@ typedef struct NvmeDSMAIOCB { BlockAIOCB common; BlockAIOCB *aiocb; NvmeRequest *req; - QEMUBH *bh; int ret; NvmeDsmRange *range; @@ -2312,7 +2491,7 @@ static void nvme_dsm_cancel(BlockAIOCB *aiocb) } else { /* * We only reach this if nvme_dsm_cancel() has already been called or - * the command ran to completion and nvme_dsm_bh is scheduled to run. + * the command ran to completion. */ assert(iocb->idx == iocb->nr); } @@ -2323,17 +2502,6 @@ static const AIOCBInfo nvme_dsm_aiocb_info = { .cancel_async = nvme_dsm_cancel, }; -static void nvme_dsm_bh(void *opaque) -{ - NvmeDSMAIOCB *iocb = opaque; - - iocb->common.cb(iocb->common.opaque, iocb->ret); - - qemu_bh_delete(iocb->bh); - iocb->bh = NULL; - qemu_aio_unref(iocb); -} - static void nvme_dsm_cb(void *opaque, int ret); static void nvme_dsm_md_cb(void *opaque, int ret) @@ -2345,16 +2513,10 @@ static void nvme_dsm_md_cb(void *opaque, int ret) uint64_t slba; uint32_t nlb; - if (ret < 0) { - iocb->ret = ret; + if (ret < 0 || iocb->ret < 0 || !ns->lbaf.ms) { goto done; } - if (!ns->lbaf.ms) { - nvme_dsm_cb(iocb, 0); - return; - } - range = &iocb->range[iocb->idx - 1]; slba = le64_to_cpu(range->slba); nlb = le32_to_cpu(range->nlb); @@ -2367,7 +2529,6 @@ static void nvme_dsm_md_cb(void *opaque, int ret) ret = nvme_block_status_all(ns, slba, nlb, BDRV_BLOCK_ZERO); if (ret) { if (ret < 0) { - iocb->ret = ret; goto done; } @@ -2381,8 +2542,7 @@ static void nvme_dsm_md_cb(void *opaque, int ret) return; done: - iocb->aiocb = NULL; - qemu_bh_schedule(iocb->bh); + nvme_dsm_cb(iocb, ret); } static void nvme_dsm_cb(void *opaque, int ret) @@ -2395,7 +2555,9 @@ static void nvme_dsm_cb(void *opaque, int ret) uint64_t slba; uint32_t nlb; - if (ret < 0) { + if (iocb->ret < 0) { + goto done; + } else if (ret < 0) { iocb->ret = ret; goto done; } @@ -2429,7 +2591,8 @@ next: done: iocb->aiocb = NULL; - qemu_bh_schedule(iocb->bh); + iocb->common.cb(iocb->common.opaque, iocb->ret); + qemu_aio_unref(iocb); } static uint16_t nvme_dsm(NvmeCtrl *n, NvmeRequest *req) @@ -2447,7 +2610,6 @@ static uint16_t nvme_dsm(NvmeCtrl *n, NvmeRequest *req) nvme_misc_cb, req); iocb->req = req; - iocb->bh = qemu_bh_new(nvme_dsm_bh, iocb); iocb->ret = 0; iocb->range = g_new(NvmeDsmRange, nr); iocb->nr = nr; @@ -2456,6 +2618,9 @@ static uint16_t nvme_dsm(NvmeCtrl *n, NvmeRequest *req) status = nvme_h2c(n, (uint8_t *)iocb->range, sizeof(NvmeDsmRange) * nr, req); if (status) { + g_free(iocb->range); + qemu_aio_unref(iocb); + return status; } @@ -2531,7 +2696,6 @@ typedef struct NvmeCopyAIOCB { BlockAIOCB common; BlockAIOCB *aiocb; NvmeRequest *req; - QEMUBH *bh; int ret; void *ranges; @@ -2569,9 +2733,8 @@ static const AIOCBInfo nvme_copy_aiocb_info = { .cancel_async = nvme_copy_cancel, }; -static void nvme_copy_bh(void *opaque) +static void nvme_copy_done(NvmeCopyAIOCB *iocb) { - NvmeCopyAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; NvmeNamespace *ns = req->ns; BlockAcctStats *stats = blk_get_stats(ns->blkconf.blk); @@ -2583,9 +2746,6 @@ static void nvme_copy_bh(void *opaque) qemu_iovec_destroy(&iocb->iov); g_free(iocb->bounce); - qemu_bh_delete(iocb->bh); - iocb->bh = NULL; - if (iocb->ret < 0) { block_acct_failed(stats, &iocb->acct.read); block_acct_failed(stats, &iocb->acct.write); @@ -2598,7 +2758,7 @@ static void nvme_copy_bh(void *opaque) qemu_aio_unref(iocb); } -static void nvme_copy_cb(void *opaque, int ret); +static void nvme_do_copy(NvmeCopyAIOCB *iocb); static void nvme_copy_source_range_parse_format0(void *ranges, int idx, uint64_t *slba, uint32_t *nlb, @@ -2686,6 +2846,25 @@ static void nvme_copy_source_range_parse(void *ranges, int idx, uint8_t format, } } +static inline uint16_t nvme_check_copy_mcl(NvmeNamespace *ns, + NvmeCopyAIOCB *iocb, uint16_t nr) +{ + uint32_t copy_len = 0; + + for (int idx = 0; idx < nr; idx++) { + uint32_t nlb; + nvme_copy_source_range_parse(iocb->ranges, idx, iocb->format, NULL, + &nlb, NULL, NULL, NULL); + copy_len += nlb; + } + + if (copy_len > ns->id_ns.mcl) { + return NVME_CMD_SIZE_LIMIT | NVME_DNR; + } + + return NVME_SUCCESS; +} + static void nvme_copy_out_completed_cb(void *opaque, int ret) { NvmeCopyAIOCB *iocb = opaque; @@ -2710,7 +2889,7 @@ static void nvme_copy_out_completed_cb(void *opaque, int ret) iocb->idx++; iocb->slba += nlb; out: - nvme_copy_cb(iocb, iocb->ret); + nvme_do_copy(iocb); } static void nvme_copy_out_cb(void *opaque, int ret) @@ -2722,16 +2901,8 @@ static void nvme_copy_out_cb(void *opaque, int ret) size_t mlen; uint8_t *mbounce; - if (ret < 0) { - iocb->ret = ret; + if (ret < 0 || iocb->ret < 0 || !ns->lbaf.ms) { goto out; - } else if (iocb->ret < 0) { - goto out; - } - - if (!ns->lbaf.ms) { - nvme_copy_out_completed_cb(iocb, 0); - return; } nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, NULL, @@ -2750,7 +2921,7 @@ static void nvme_copy_out_cb(void *opaque, int ret) return; out: - nvme_copy_cb(iocb, ret); + nvme_copy_out_completed_cb(iocb, ret); } static void nvme_copy_in_completed_cb(void *opaque, int ret) @@ -2844,15 +3015,9 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) invalid: req->status = status; - iocb->aiocb = NULL; - if (iocb->bh) { - qemu_bh_schedule(iocb->bh); - } - - return; - + iocb->ret = -1; out: - nvme_copy_cb(iocb, ret); + nvme_do_copy(iocb); } static void nvme_copy_in_cb(void *opaque, int ret) @@ -2863,16 +3028,8 @@ static void nvme_copy_in_cb(void *opaque, int ret) uint64_t slba; uint32_t nlb; - if (ret < 0) { - iocb->ret = ret; + if (ret < 0 || iocb->ret < 0 || !ns->lbaf.ms) { goto out; - } else if (iocb->ret < 0) { - goto out; - } - - if (!ns->lbaf.ms) { - nvme_copy_in_completed_cb(iocb, 0); - return; } nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba, @@ -2888,12 +3045,11 @@ static void nvme_copy_in_cb(void *opaque, int ret) return; out: - nvme_copy_cb(iocb, iocb->ret); + nvme_copy_in_completed_cb(iocb, ret); } -static void nvme_copy_cb(void *opaque, int ret) +static void nvme_do_copy(NvmeCopyAIOCB *iocb) { - NvmeCopyAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; NvmeNamespace *ns = req->ns; uint64_t slba; @@ -2901,10 +3057,7 @@ static void nvme_copy_cb(void *opaque, int ret) size_t len; uint16_t status; - if (ret < 0) { - iocb->ret = ret; - goto done; - } else if (iocb->ret < 0) { + if (iocb->ret < 0) { goto done; } @@ -2951,14 +3104,11 @@ static void nvme_copy_cb(void *opaque, int ret) invalid: req->status = status; + iocb->ret = -1; done: - iocb->aiocb = NULL; - if (iocb->bh) { - qemu_bh_schedule(iocb->bh); - } + nvme_copy_done(iocb); } - static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) { NvmeNamespace *ns = req->ns; @@ -2995,7 +3145,8 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) goto invalid; } - if (ns->pif && format != 0x1) { + if ((ns->pif == 0x0 && format != 0x0) || + (ns->pif != 0x0 && format != 0x1)) { status = NVME_INVALID_FORMAT | NVME_DNR; goto invalid; } @@ -3026,8 +3177,12 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) } } + status = nvme_check_copy_mcl(ns, iocb, nr); + if (status) { + goto invalid; + } + iocb->req = req; - iocb->bh = qemu_bh_new(nvme_copy_bh, iocb); iocb->ret = 0; iocb->nr = nr; iocb->idx = 0; @@ -3044,7 +3199,7 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) BLOCK_ACCT_WRITE); req->aiocb = &iocb->common; - nvme_copy_cb(iocb, 0); + nvme_do_copy(iocb); return NVME_NO_COMPLETE; @@ -3120,7 +3275,6 @@ typedef struct NvmeFlushAIOCB { BlockAIOCB common; BlockAIOCB *aiocb; NvmeRequest *req; - QEMUBH *bh; int ret; NvmeNamespace *ns; @@ -3136,15 +3290,17 @@ static void nvme_flush_cancel(BlockAIOCB *acb) if (iocb->aiocb) { blk_aio_cancel_async(iocb->aiocb); + iocb->aiocb = NULL; } } static const AIOCBInfo nvme_flush_aiocb_info = { .aiocb_size = sizeof(NvmeFlushAIOCB), .cancel_async = nvme_flush_cancel, - .get_aio_context = nvme_get_aio_context, }; +static void nvme_do_flush(NvmeFlushAIOCB *iocb); + static void nvme_flush_ns_cb(void *opaque, int ret) { NvmeFlushAIOCB *iocb = opaque; @@ -3166,13 +3322,11 @@ static void nvme_flush_ns_cb(void *opaque, int ret) } out: - iocb->aiocb = NULL; - qemu_bh_schedule(iocb->bh); + nvme_do_flush(iocb); } -static void nvme_flush_bh(void *opaque) +static void nvme_do_flush(NvmeFlushAIOCB *iocb) { - NvmeFlushAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; NvmeCtrl *n = nvme_ctrl(req); int i; @@ -3199,14 +3353,8 @@ static void nvme_flush_bh(void *opaque) return; done: - qemu_bh_delete(iocb->bh); - iocb->bh = NULL; - iocb->common.cb(iocb->common.opaque, iocb->ret); - qemu_aio_unref(iocb); - - return; } static uint16_t nvme_flush(NvmeCtrl *n, NvmeRequest *req) @@ -3218,7 +3366,6 @@ static uint16_t nvme_flush(NvmeCtrl *n, NvmeRequest *req) iocb = qemu_aio_get(&nvme_flush_aiocb_info, NULL, nvme_misc_cb, req); iocb->req = req; - iocb->bh = qemu_bh_new(nvme_flush_bh, iocb); iocb->ret = 0; iocb->ns = NULL; iocb->nsid = 0; @@ -3240,13 +3387,11 @@ static uint16_t nvme_flush(NvmeCtrl *n, NvmeRequest *req) } req->aiocb = &iocb->common; - qemu_bh_schedule(iocb->bh); + nvme_do_flush(iocb); return NVME_NO_COMPLETE; out: - qemu_bh_delete(iocb->bh); - iocb->bh = NULL; qemu_aio_unref(iocb); return status; @@ -3317,7 +3462,7 @@ static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req) block_acct_start(blk_get_stats(blk), &req->acct, data_size, BLOCK_ACCT_READ); - nvme_blk_read(blk, data_offset, nvme_rw_cb, req); + nvme_blk_read(blk, data_offset, BDRV_SECTOR_SIZE, nvme_rw_cb, req); return NVME_NO_COMPLETE; invalid: @@ -3325,6 +3470,41 @@ invalid: return status | NVME_DNR; } +static void nvme_do_write_fdp(NvmeCtrl *n, NvmeRequest *req, uint64_t slba, + uint32_t nlb) +{ + NvmeNamespace *ns = req->ns; + NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd; + uint64_t data_size = nvme_l2b(ns, nlb); + uint32_t dw12 = le32_to_cpu(req->cmd.cdw12); + uint8_t dtype = (dw12 >> 20) & 0xf; + uint16_t pid = le16_to_cpu(rw->dspec); + uint16_t ph, rg, ruhid; + NvmeReclaimUnit *ru; + + if (dtype != NVME_DIRECTIVE_DATA_PLACEMENT || + !nvme_parse_pid(ns, pid, &ph, &rg)) { + ph = 0; + rg = 0; + } + + ruhid = ns->fdp.phs[ph]; + ru = &ns->endgrp->fdp.ruhs[ruhid].rus[rg]; + + nvme_fdp_stat_inc(&ns->endgrp->fdp.hbmw, data_size); + nvme_fdp_stat_inc(&ns->endgrp->fdp.mbmw, data_size); + + while (nlb) { + if (nlb < ru->ruamw) { + ru->ruamw -= nlb; + break; + } + + nlb -= ru->ruamw; + nvme_update_ruh(n, ns, pid); + } +} + static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append, bool wrz) { @@ -3434,6 +3614,8 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append, if (!(zone->d.za & NVME_ZA_ZRWA_VALID)) { zone->w_ptr += nlb; } + } else if (ns->endgrp && ns->endgrp->fdp.enabled) { + nvme_do_write_fdp(n, req, slba, nlb); } data_offset = nvme_l2b(ns, slba); @@ -3450,7 +3632,7 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append, block_acct_start(blk_get_stats(blk), &req->acct, data_size, BLOCK_ACCT_WRITE); - nvme_blk_write(blk, data_offset, nvme_rw_cb, req); + nvme_blk_write(blk, data_offset, BDRV_SECTOR_SIZE, nvme_rw_cb, req); } else { req->aiocb = blk_aio_pwrite_zeroes(blk, data_offset, data_size, BDRV_REQ_MAY_UNMAP, nvme_rw_cb, @@ -3681,7 +3863,6 @@ typedef struct NvmeZoneResetAIOCB { BlockAIOCB common; BlockAIOCB *aiocb; NvmeRequest *req; - QEMUBH *bh; int ret; bool all; @@ -3710,17 +3891,6 @@ static const AIOCBInfo nvme_zone_reset_aiocb_info = { .cancel_async = nvme_zone_reset_cancel, }; -static void nvme_zone_reset_bh(void *opaque) -{ - NvmeZoneResetAIOCB *iocb = opaque; - - iocb->common.cb(iocb->common.opaque, iocb->ret); - - qemu_bh_delete(iocb->bh); - iocb->bh = NULL; - qemu_aio_unref(iocb); -} - static void nvme_zone_reset_cb(void *opaque, int ret); static void nvme_zone_reset_epilogue_cb(void *opaque, int ret) @@ -3731,14 +3901,8 @@ static void nvme_zone_reset_epilogue_cb(void *opaque, int ret) int64_t moff; int count; - if (ret < 0) { - nvme_zone_reset_cb(iocb, ret); - return; - } - - if (!ns->lbaf.ms) { - nvme_zone_reset_cb(iocb, 0); - return; + if (ret < 0 || iocb->ret < 0 || !ns->lbaf.ms) { + goto out; } moff = nvme_moff(ns, iocb->zone->d.zslba); @@ -3748,6 +3912,9 @@ static void nvme_zone_reset_epilogue_cb(void *opaque, int ret) BDRV_REQ_MAY_UNMAP, nvme_zone_reset_cb, iocb); return; + +out: + nvme_zone_reset_cb(iocb, ret); } static void nvme_zone_reset_cb(void *opaque, int ret) @@ -3756,7 +3923,9 @@ static void nvme_zone_reset_cb(void *opaque, int ret) NvmeRequest *req = iocb->req; NvmeNamespace *ns = req->ns; - if (ret < 0) { + if (iocb->ret < 0) { + goto done; + } else if (ret < 0) { iocb->ret = ret; goto done; } @@ -3804,9 +3973,9 @@ static void nvme_zone_reset_cb(void *opaque, int ret) done: iocb->aiocb = NULL; - if (iocb->bh) { - qemu_bh_schedule(iocb->bh); - } + + iocb->common.cb(iocb->common.opaque, iocb->ret); + qemu_aio_unref(iocb); } static uint16_t nvme_zone_mgmt_send_zrwa_flush(NvmeCtrl *n, NvmeZone *zone, @@ -3911,7 +4080,6 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req) nvme_misc_cb, req); iocb->req = req; - iocb->bh = qemu_bh_new(nvme_zone_reset_bh, iocb); iocb->ret = 0; iocb->all = all; iocb->idx = zone_idx; @@ -4064,14 +4232,14 @@ static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req) nr_zones++; } } - header = (NvmeZoneReportHeader *)buf; + header = buf; header->nr_zones = cpu_to_le64(nr_zones); buf_p = buf + sizeof(NvmeZoneReportHeader); for (; zone_idx < ns->num_zones && max_zones > 0; zone_idx++) { zone = &ns->zone_array[zone_idx]; if (nvme_zone_matches_filter(zrasf, zone)) { - z = (NvmeZoneDescr *)buf_p; + z = buf_p; buf_p += sizeof(NvmeZoneDescr); z->zt = zone->d.zt; @@ -4105,6 +4273,132 @@ static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req) return status; } +static uint16_t nvme_io_mgmt_recv_ruhs(NvmeCtrl *n, NvmeRequest *req, + size_t len) +{ + NvmeNamespace *ns = req->ns; + NvmeEnduranceGroup *endgrp; + NvmeRuhStatus *hdr; + NvmeRuhStatusDescr *ruhsd; + unsigned int nruhsd; + uint16_t rg, ph, *ruhid; + size_t trans_len; + g_autofree uint8_t *buf = NULL; + + if (!n->subsys) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + if (ns->params.nsid == 0 || ns->params.nsid == 0xffffffff) { + return NVME_INVALID_NSID | NVME_DNR; + } + + if (!n->subsys->endgrp.fdp.enabled) { + return NVME_FDP_DISABLED | NVME_DNR; + } + + endgrp = ns->endgrp; + + nruhsd = ns->fdp.nphs * endgrp->fdp.nrg; + trans_len = sizeof(NvmeRuhStatus) + nruhsd * sizeof(NvmeRuhStatusDescr); + buf = g_malloc(trans_len); + + trans_len = MIN(trans_len, len); + + hdr = (NvmeRuhStatus *)buf; + ruhsd = (NvmeRuhStatusDescr *)(buf + sizeof(NvmeRuhStatus)); + + hdr->nruhsd = cpu_to_le16(nruhsd); + + ruhid = ns->fdp.phs; + + for (ph = 0; ph < ns->fdp.nphs; ph++, ruhid++) { + NvmeRuHandle *ruh = &endgrp->fdp.ruhs[*ruhid]; + + for (rg = 0; rg < endgrp->fdp.nrg; rg++, ruhsd++) { + uint16_t pid = nvme_make_pid(ns, rg, ph); + + ruhsd->pid = cpu_to_le16(pid); + ruhsd->ruhid = *ruhid; + ruhsd->earutr = 0; + ruhsd->ruamw = cpu_to_le64(ruh->rus[rg].ruamw); + } + } + + return nvme_c2h(n, buf, trans_len, req); +} + +static uint16_t nvme_io_mgmt_recv(NvmeCtrl *n, NvmeRequest *req) +{ + NvmeCmd *cmd = &req->cmd; + uint32_t cdw10 = le32_to_cpu(cmd->cdw10); + uint32_t numd = le32_to_cpu(cmd->cdw11); + uint8_t mo = (cdw10 & 0xff); + size_t len = (numd + 1) << 2; + + switch (mo) { + case NVME_IOMR_MO_NOP: + return 0; + case NVME_IOMR_MO_RUH_STATUS: + return nvme_io_mgmt_recv_ruhs(n, req, len); + default: + return NVME_INVALID_FIELD | NVME_DNR; + }; +} + +static uint16_t nvme_io_mgmt_send_ruh_update(NvmeCtrl *n, NvmeRequest *req) +{ + NvmeCmd *cmd = &req->cmd; + NvmeNamespace *ns = req->ns; + uint32_t cdw10 = le32_to_cpu(cmd->cdw10); + uint16_t ret = NVME_SUCCESS; + uint32_t npid = (cdw10 >> 1) + 1; + unsigned int i = 0; + g_autofree uint16_t *pids = NULL; + uint32_t maxnpid; + + if (!ns->endgrp || !ns->endgrp->fdp.enabled) { + return NVME_FDP_DISABLED | NVME_DNR; + } + + maxnpid = n->subsys->endgrp.fdp.nrg * n->subsys->endgrp.fdp.nruh; + + if (unlikely(npid >= MIN(NVME_FDP_MAXPIDS, maxnpid))) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + pids = g_new(uint16_t, npid); + + ret = nvme_h2c(n, pids, npid * sizeof(uint16_t), req); + if (ret) { + return ret; + } + + for (; i < npid; i++) { + if (!nvme_update_ruh(n, ns, pids[i])) { + return NVME_INVALID_FIELD | NVME_DNR; + } + } + + return ret; +} + +static uint16_t nvme_io_mgmt_send(NvmeCtrl *n, NvmeRequest *req) +{ + NvmeCmd *cmd = &req->cmd; + uint32_t cdw10 = le32_to_cpu(cmd->cdw10); + uint8_t mo = (cdw10 & 0xff); + + switch (mo) { + case NVME_IOMS_MO_NOP: + return 0; + case NVME_IOMS_MO_RUH_UPDATE: + return nvme_io_mgmt_send_ruh_update(n, req); + default: + return NVME_INVALID_FIELD | NVME_DNR; + }; +} + static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) { NvmeNamespace *ns; @@ -4181,6 +4475,10 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) return nvme_zone_mgmt_send(n, req); case NVME_CMD_ZONE_MGMT_RECV: return nvme_zone_mgmt_recv(n, req); + case NVME_CMD_IO_MGMT_RECV: + return nvme_io_mgmt_recv(n, req); + case NVME_CMD_IO_MGMT_SEND: + return nvme_io_mgmt_send(n, req); default: assert(false); } @@ -4188,10 +4486,87 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) return NVME_INVALID_OPCODE | NVME_DNR; } +static void nvme_cq_notifier(EventNotifier *e) +{ + NvmeCQueue *cq = container_of(e, NvmeCQueue, notifier); + NvmeCtrl *n = cq->ctrl; + + if (!event_notifier_test_and_clear(e)) { + return; + } + + nvme_update_cq_head(cq); + + if (cq->tail == cq->head) { + if (cq->irq_enabled) { + n->cq_pending--; + } + + nvme_irq_deassert(n, cq); + } + + qemu_bh_schedule(cq->bh); +} + +static int nvme_init_cq_ioeventfd(NvmeCQueue *cq) +{ + NvmeCtrl *n = cq->ctrl; + uint16_t offset = (cq->cqid << 3) + (1 << 2); + int ret; + + ret = event_notifier_init(&cq->notifier, 0); + if (ret < 0) { + return ret; + } + + event_notifier_set_handler(&cq->notifier, nvme_cq_notifier); + memory_region_add_eventfd(&n->iomem, + 0x1000 + offset, 4, false, 0, &cq->notifier); + + return 0; +} + +static void nvme_sq_notifier(EventNotifier *e) +{ + NvmeSQueue *sq = container_of(e, NvmeSQueue, notifier); + + if (!event_notifier_test_and_clear(e)) { + return; + } + + nvme_process_sq(sq); +} + +static int nvme_init_sq_ioeventfd(NvmeSQueue *sq) +{ + NvmeCtrl *n = sq->ctrl; + uint16_t offset = sq->sqid << 3; + int ret; + + ret = event_notifier_init(&sq->notifier, 0); + if (ret < 0) { + return ret; + } + + event_notifier_set_handler(&sq->notifier, nvme_sq_notifier); + memory_region_add_eventfd(&n->iomem, + 0x1000 + offset, 4, false, 0, &sq->notifier); + + return 0; +} + static void nvme_free_sq(NvmeSQueue *sq, NvmeCtrl *n) { + uint16_t offset = sq->sqid << 3; + n->sq[sq->sqid] = NULL; - timer_free(sq->timer); + qemu_bh_delete(sq->bh); + if (sq->ioeventfd_enabled) { + memory_region_del_eventfd(&n->iomem, + 0x1000 + offset, 4, false, 0, &sq->notifier); + event_notifier_set_handler(&sq->notifier, NULL); + event_notifier_cleanup(&sq->notifier); + } g_free(sq->io_req); if (sq->sqid) { g_free(sq); @@ -4259,7 +4634,20 @@ static void nvme_init_sq(NvmeSQueue *sq, NvmeCtrl *n, uint64_t dma_addr, sq->io_req[i].sq = sq; QTAILQ_INSERT_TAIL(&(sq->req_list), &sq->io_req[i], entry); } - sq->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, nvme_process_sq, sq); + + sq->bh = qemu_bh_new_guarded(nvme_process_sq, sq, + &DEVICE(sq->ctrl)->mem_reentrancy_guard); + + if (n->dbbuf_enabled) { + sq->db_addr = n->dbbuf_dbs + (sqid << 3); + sq->ei_addr = n->dbbuf_eis + (sqid << 3); + + if (n->params.ioeventfd && sq->sqid != 0) { + if (!nvme_init_sq_ioeventfd(sq)) { + sq->ioeventfd_enabled = true; + } + } + } assert(n->cq[cqid]); cq = n->cq[cqid]; @@ -4284,8 +4672,7 @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_err_invalid_create_sq_cqid(cqid); return NVME_INVALID_CQID | NVME_DNR; } - if (unlikely(!sqid || sqid > n->params.max_ioqpairs || - n->sq[sqid] != NULL)) { + if (unlikely(!sqid || sqid > n->conf_ioqpairs || n->sq[sqid] != NULL)) { trace_pci_nvme_err_invalid_create_sq_sqid(sqid); return NVME_INVALID_QID | NVME_DNR; } @@ -4317,8 +4704,8 @@ static void nvme_set_blk_stats(NvmeNamespace *ns, struct nvme_stats *stats) { BlockAcctStats *s = blk_get_stats(ns->blkconf.blk); - stats->units_read += s->nr_bytes[BLOCK_ACCT_READ] >> BDRV_SECTOR_BITS; - stats->units_written += s->nr_bytes[BLOCK_ACCT_WRITE] >> BDRV_SECTOR_BITS; + stats->units_read += s->nr_bytes[BLOCK_ACCT_READ]; + stats->units_written += s->nr_bytes[BLOCK_ACCT_WRITE]; stats->read_commands += s->nr_ops[BLOCK_ACCT_READ]; stats->write_commands += s->nr_ops[BLOCK_ACCT_WRITE]; } @@ -4332,6 +4719,7 @@ static uint16_t nvme_smart_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, uint32_t trans_len; NvmeNamespace *ns; time_t current_ms; + uint64_t u_read, u_written; if (off >= sizeof(smart)) { return NVME_INVALID_FIELD | NVME_DNR; @@ -4358,10 +4746,11 @@ static uint16_t nvme_smart_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, trans_len = MIN(sizeof(smart) - off, buf_len); smart.critical_warning = n->smart_critical_warning; - smart.data_units_read[0] = cpu_to_le64(DIV_ROUND_UP(stats.units_read, - 1000)); - smart.data_units_written[0] = cpu_to_le64(DIV_ROUND_UP(stats.units_written, - 1000)); + u_read = DIV_ROUND_UP(stats.units_read >> BDRV_SECTOR_BITS, 1000); + u_written = DIV_ROUND_UP(stats.units_written >> BDRV_SECTOR_BITS, 1000); + + smart.data_units_read[0] = cpu_to_le64(u_read); + smart.data_units_written[0] = cpu_to_le64(u_written); smart.host_read_commands[0] = cpu_to_le64(stats.read_commands); smart.host_write_commands[0] = cpu_to_le64(stats.write_commands); @@ -4383,6 +4772,48 @@ static uint16_t nvme_smart_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, return nvme_c2h(n, (uint8_t *) &smart + off, trans_len, req); } +static uint16_t nvme_endgrp_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, + uint64_t off, NvmeRequest *req) +{ + uint32_t dw11 = le32_to_cpu(req->cmd.cdw11); + uint16_t endgrpid = (dw11 >> 16) & 0xffff; + struct nvme_stats stats = {}; + NvmeEndGrpLog info = {}; + int i; + + if (!n->subsys || endgrpid != 0x1) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + if (off >= sizeof(info)) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { + NvmeNamespace *ns = nvme_subsys_ns(n->subsys, i); + if (!ns) { + continue; + } + + nvme_set_blk_stats(ns, &stats); + } + + info.data_units_read[0] = + cpu_to_le64(DIV_ROUND_UP(stats.units_read / 1000000000, 1000000000)); + info.data_units_written[0] = + cpu_to_le64(DIV_ROUND_UP(stats.units_written / 1000000000, 1000000000)); + info.media_units_written[0] = + cpu_to_le64(DIV_ROUND_UP(stats.units_written / 1000000000, 1000000000)); + + info.host_read_commands[0] = cpu_to_le64(stats.read_commands); + info.host_write_commands[0] = cpu_to_le64(stats.write_commands); + + buf_len = MIN(sizeof(info) - off, buf_len); + + return nvme_c2h(n, (uint8_t *)&info + off, buf_len, req); +} + + static uint16_t nvme_fw_log_info(NvmeCtrl *n, uint32_t buf_len, uint64_t off, NvmeRequest *req) { @@ -4508,6 +4939,212 @@ static uint16_t nvme_cmd_effects(NvmeCtrl *n, uint8_t csi, uint32_t buf_len, return nvme_c2h(n, ((uint8_t *)&log) + off, trans_len, req); } +static size_t sizeof_fdp_conf_descr(size_t nruh, size_t vss) +{ + size_t entry_siz = sizeof(NvmeFdpDescrHdr) + nruh * sizeof(NvmeRuhDescr) + + vss; + return ROUND_UP(entry_siz, 8); +} + +static uint16_t nvme_fdp_confs(NvmeCtrl *n, uint32_t endgrpid, uint32_t buf_len, + uint64_t off, NvmeRequest *req) +{ + uint32_t log_size, trans_len; + g_autofree uint8_t *buf = NULL; + NvmeFdpDescrHdr *hdr; + NvmeRuhDescr *ruhd; + NvmeEnduranceGroup *endgrp; + NvmeFdpConfsHdr *log; + size_t nruh, fdp_descr_size; + int i; + + if (endgrpid != 1 || !n->subsys) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + endgrp = &n->subsys->endgrp; + + if (endgrp->fdp.enabled) { + nruh = endgrp->fdp.nruh; + } else { + nruh = 1; + } + + fdp_descr_size = sizeof_fdp_conf_descr(nruh, FDPVSS); + log_size = sizeof(NvmeFdpConfsHdr) + fdp_descr_size; + + if (off >= log_size) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + trans_len = MIN(log_size - off, buf_len); + + buf = g_malloc0(log_size); + log = (NvmeFdpConfsHdr *)buf; + hdr = (NvmeFdpDescrHdr *)(log + 1); + ruhd = (NvmeRuhDescr *)(buf + sizeof(*log) + sizeof(*hdr)); + + log->num_confs = cpu_to_le16(0); + log->size = cpu_to_le32(log_size); + + hdr->descr_size = cpu_to_le16(fdp_descr_size); + if (endgrp->fdp.enabled) { + hdr->fdpa = FIELD_DP8(hdr->fdpa, FDPA, VALID, 1); + hdr->fdpa = FIELD_DP8(hdr->fdpa, FDPA, RGIF, endgrp->fdp.rgif); + hdr->nrg = cpu_to_le16(endgrp->fdp.nrg); + hdr->nruh = cpu_to_le16(endgrp->fdp.nruh); + hdr->maxpids = cpu_to_le16(NVME_FDP_MAXPIDS - 1); + hdr->nnss = cpu_to_le32(NVME_MAX_NAMESPACES); + hdr->runs = cpu_to_le64(endgrp->fdp.runs); + + for (i = 0; i < nruh; i++) { + ruhd->ruht = NVME_RUHT_INITIALLY_ISOLATED; + ruhd++; + } + } else { + /* 1 bit for RUH in PIF -> 2 RUHs max. */ + hdr->nrg = cpu_to_le16(1); + hdr->nruh = cpu_to_le16(1); + hdr->maxpids = cpu_to_le16(NVME_FDP_MAXPIDS - 1); + hdr->nnss = cpu_to_le32(1); + hdr->runs = cpu_to_le64(96 * MiB); + + ruhd->ruht = NVME_RUHT_INITIALLY_ISOLATED; + } + + return nvme_c2h(n, (uint8_t *)buf + off, trans_len, req); +} + +static uint16_t nvme_fdp_ruh_usage(NvmeCtrl *n, uint32_t endgrpid, + uint32_t dw10, uint32_t dw12, + uint32_t buf_len, uint64_t off, + NvmeRequest *req) +{ + NvmeRuHandle *ruh; + NvmeRuhuLog *hdr; + NvmeRuhuDescr *ruhud; + NvmeEnduranceGroup *endgrp; + g_autofree uint8_t *buf = NULL; + uint32_t log_size, trans_len; + uint16_t i; + + if (endgrpid != 1 || !n->subsys) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + endgrp = &n->subsys->endgrp; + + if (!endgrp->fdp.enabled) { + return NVME_FDP_DISABLED | NVME_DNR; + } + + log_size = sizeof(NvmeRuhuLog) + endgrp->fdp.nruh * sizeof(NvmeRuhuDescr); + + if (off >= log_size) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + trans_len = MIN(log_size - off, buf_len); + + buf = g_malloc0(log_size); + hdr = (NvmeRuhuLog *)buf; + ruhud = (NvmeRuhuDescr *)(hdr + 1); + + ruh = endgrp->fdp.ruhs; + hdr->nruh = cpu_to_le16(endgrp->fdp.nruh); + + for (i = 0; i < endgrp->fdp.nruh; i++, ruhud++, ruh++) { + ruhud->ruha = ruh->ruha; + } + + return nvme_c2h(n, (uint8_t *)buf + off, trans_len, req); +} + +static uint16_t nvme_fdp_stats(NvmeCtrl *n, uint32_t endgrpid, uint32_t buf_len, + uint64_t off, NvmeRequest *req) +{ + NvmeEnduranceGroup *endgrp; + NvmeFdpStatsLog log = {}; + uint32_t trans_len; + + if (off >= sizeof(NvmeFdpStatsLog)) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + if (endgrpid != 1 || !n->subsys) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + if (!n->subsys->endgrp.fdp.enabled) { + return NVME_FDP_DISABLED | NVME_DNR; + } + + endgrp = &n->subsys->endgrp; + + trans_len = MIN(sizeof(log) - off, buf_len); + + /* spec value is 128 bit, we only use 64 bit */ + log.hbmw[0] = cpu_to_le64(endgrp->fdp.hbmw); + log.mbmw[0] = cpu_to_le64(endgrp->fdp.mbmw); + log.mbe[0] = cpu_to_le64(endgrp->fdp.mbe); + + return nvme_c2h(n, (uint8_t *)&log + off, trans_len, req); +} + +static uint16_t nvme_fdp_events(NvmeCtrl *n, uint32_t endgrpid, + uint32_t buf_len, uint64_t off, + NvmeRequest *req) +{ + NvmeEnduranceGroup *endgrp; + NvmeCmd *cmd = &req->cmd; + bool host_events = (cmd->cdw10 >> 8) & 0x1; + uint32_t log_size, trans_len; + NvmeFdpEventBuffer *ebuf; + g_autofree NvmeFdpEventsLog *elog = NULL; + NvmeFdpEvent *event; + + if (endgrpid != 1 || !n->subsys) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + endgrp = &n->subsys->endgrp; + + if (!endgrp->fdp.enabled) { + return NVME_FDP_DISABLED | NVME_DNR; + } + + if (host_events) { + ebuf = &endgrp->fdp.host_events; + } else { + ebuf = &endgrp->fdp.ctrl_events; + } + + log_size = sizeof(NvmeFdpEventsLog) + ebuf->nelems * sizeof(NvmeFdpEvent); + + if (off >= log_size) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + trans_len = MIN(log_size - off, buf_len); + elog = g_malloc0(log_size); + elog->num_events = cpu_to_le32(ebuf->nelems); + event = (NvmeFdpEvent *)(elog + 1); + + if (ebuf->nelems && ebuf->start == ebuf->next) { + unsigned int nelems = (NVME_FDP_MAX_EVENTS - ebuf->start); + /* wrap over, copy [start;NVME_FDP_MAX_EVENTS[ and [0; next[ */ + memcpy(event, &ebuf->events[ebuf->start], + sizeof(NvmeFdpEvent) * nelems); + memcpy(event + nelems, ebuf->events, + sizeof(NvmeFdpEvent) * ebuf->next); + } else if (ebuf->start < ebuf->next) { + memcpy(event, &ebuf->events[ebuf->start], + sizeof(NvmeFdpEvent) * (ebuf->next - ebuf->start)); + } + + return nvme_c2h(n, (uint8_t *)elog + off, trans_len, req); +} + static uint16_t nvme_get_log(NvmeCtrl *n, NvmeRequest *req) { NvmeCmd *cmd = &req->cmd; @@ -4520,13 +5157,14 @@ static uint16_t nvme_get_log(NvmeCtrl *n, NvmeRequest *req) uint8_t lsp = (dw10 >> 8) & 0xf; uint8_t rae = (dw10 >> 15) & 0x1; uint8_t csi = le32_to_cpu(cmd->cdw14) >> 24; - uint32_t numdl, numdu; + uint32_t numdl, numdu, lspi; uint64_t off, lpol, lpou; size_t len; uint16_t status; numdl = (dw10 >> 16); numdu = (dw11 & 0xffff); + lspi = (dw11 >> 16); lpol = dw12; lpou = dw13; @@ -4555,6 +5193,16 @@ static uint16_t nvme_get_log(NvmeCtrl *n, NvmeRequest *req) return nvme_changed_nslist(n, rae, len, off, req); case NVME_LOG_CMD_EFFECTS: return nvme_cmd_effects(n, csi, len, off, req); + case NVME_LOG_ENDGRP: + return nvme_endgrp_info(n, rae, len, off, req); + case NVME_LOG_FDP_CONFS: + return nvme_fdp_confs(n, lspi, len, off, req); + case NVME_LOG_FDP_RUH_USAGE: + return nvme_fdp_ruh_usage(n, lspi, dw10, dw12, len, off, req); + case NVME_LOG_FDP_STATS: + return nvme_fdp_stats(n, lspi, len, off, req); + case NVME_LOG_FDP_EVENTS: + return nvme_fdp_events(n, lspi, len, off, req); default: trace_pci_nvme_err_invalid_log_page(nvme_cid(req), lid); return NVME_INVALID_FIELD | NVME_DNR; @@ -4563,10 +5211,19 @@ static uint16_t nvme_get_log(NvmeCtrl *n, NvmeRequest *req) static void nvme_free_cq(NvmeCQueue *cq, NvmeCtrl *n) { + PCIDevice *pci = PCI_DEVICE(n); + uint16_t offset = (cq->cqid << 3) + (1 << 2); + n->cq[cq->cqid] = NULL; - timer_free(cq->timer); - if (msix_enabled(&n->parent_obj)) { - msix_vector_unuse(&n->parent_obj, cq->vector); + qemu_bh_delete(cq->bh); + if (cq->ioeventfd_enabled) { + memory_region_del_eventfd(&n->iomem, + 0x1000 + offset, 4, false, 0, &cq->notifier); + event_notifier_set_handler(&cq->notifier, NULL); + event_notifier_cleanup(&cq->notifier); + } + if (msix_enabled(pci)) { + msix_vector_unuse(pci, cq->vector); } if (cq->cqid) { g_free(cq); @@ -4604,11 +5261,10 @@ static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n, uint64_t dma_addr, uint16_t cqid, uint16_t vector, uint16_t size, uint16_t irq_enabled) { - int ret; + PCIDevice *pci = PCI_DEVICE(n); - if (msix_enabled(&n->parent_obj)) { - ret = msix_vector_use(&n->parent_obj, vector); - assert(ret == 0); + if (msix_enabled(pci)) { + msix_vector_use(pci, vector); } cq->ctrl = n; cq->cqid = cqid; @@ -4620,8 +5276,19 @@ static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n, uint64_t dma_addr, cq->head = cq->tail = 0; QTAILQ_INIT(&cq->req_list); QTAILQ_INIT(&cq->sq_list); + if (n->dbbuf_enabled) { + cq->db_addr = n->dbbuf_dbs + (cqid << 3) + (1 << 2); + cq->ei_addr = n->dbbuf_eis + (cqid << 3) + (1 << 2); + + if (n->params.ioeventfd && cqid != 0) { + if (!nvme_init_cq_ioeventfd(cq)) { + cq->ioeventfd_enabled = true; + } + } + } n->cq[cqid] = cq; - cq->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, nvme_post_cqes, cq); + cq->bh = qemu_bh_new_guarded(nvme_post_cqes, cq, + &DEVICE(cq->ctrl)->mem_reentrancy_guard); } static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeRequest *req) @@ -4633,12 +5300,19 @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeRequest *req) uint16_t qsize = le16_to_cpu(c->qsize); uint16_t qflags = le16_to_cpu(c->cq_flags); uint64_t prp1 = le64_to_cpu(c->prp1); + uint32_t cc = ldq_le_p(&n->bar.cc); + uint8_t iocqes = NVME_CC_IOCQES(cc); + uint8_t iosqes = NVME_CC_IOSQES(cc); trace_pci_nvme_create_cq(prp1, cqid, vector, qsize, qflags, NVME_CQ_FLAGS_IEN(qflags) != 0); - if (unlikely(!cqid || cqid > n->params.max_ioqpairs || - n->cq[cqid] != NULL)) { + if (iosqes != NVME_SQES || iocqes != NVME_CQES) { + trace_pci_nvme_err_invalid_create_cq_entry_size(iosqes, iocqes); + return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR; + } + + if (unlikely(!cqid || cqid > n->conf_ioqpairs || n->cq[cqid] != NULL)) { trace_pci_nvme_err_invalid_create_cq_cqid(cqid); return NVME_INVALID_QID | NVME_DNR; } @@ -4650,11 +5324,11 @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_err_invalid_create_cq_addr(prp1); return NVME_INVALID_PRP_OFFSET | NVME_DNR; } - if (unlikely(!msix_enabled(&n->parent_obj) && vector)) { + if (unlikely(!msix_enabled(PCI_DEVICE(n)) && vector)) { trace_pci_nvme_err_invalid_create_cq_vector(vector); return NVME_INVALID_IRQ_VECTOR | NVME_DNR; } - if (unlikely(vector >= n->params.msix_qsize)) { + if (unlikely(vector >= n->conf_msix_qsize)) { trace_pci_nvme_err_invalid_create_cq_vector(vector); return NVME_INVALID_IRQ_VECTOR | NVME_DNR; } @@ -4793,6 +5467,37 @@ static uint16_t nvme_identify_ctrl_list(NvmeCtrl *n, NvmeRequest *req, return nvme_c2h(n, (uint8_t *)list, sizeof(list), req); } +static uint16_t nvme_identify_pri_ctrl_cap(NvmeCtrl *n, NvmeRequest *req) +{ + trace_pci_nvme_identify_pri_ctrl_cap(le16_to_cpu(n->pri_ctrl_cap.cntlid)); + + return nvme_c2h(n, (uint8_t *)&n->pri_ctrl_cap, + sizeof(NvmePriCtrlCap), req); +} + +static uint16_t nvme_identify_sec_ctrl_list(NvmeCtrl *n, NvmeRequest *req) +{ + NvmeIdentify *c = (NvmeIdentify *)&req->cmd; + uint16_t pri_ctrl_id = le16_to_cpu(n->pri_ctrl_cap.cntlid); + uint16_t min_id = le16_to_cpu(c->ctrlid); + uint8_t num_sec_ctrl = n->sec_ctrl_list.numcntl; + NvmeSecCtrlList list = {0}; + uint8_t i; + + for (i = 0; i < num_sec_ctrl; i++) { + if (n->sec_ctrl_list.sec[i].scid >= min_id) { + list.numcntl = num_sec_ctrl - i; + memcpy(&list.sec, n->sec_ctrl_list.sec + i, + list.numcntl * sizeof(NvmeSecCtrlEntry)); + break; + } + } + + trace_pci_nvme_identify_sec_ctrl_list(pri_ctrl_id, list.numcntl); + + return nvme_c2h(n, (uint8_t *)&list, sizeof(list), req); +} + static uint16_t nvme_identify_ns_csi(NvmeCtrl *n, NvmeRequest *req, bool active) { @@ -4935,6 +5640,10 @@ static uint16_t nvme_identify_ns_descr_list(NvmeCtrl *n, NvmeRequest *req) NvmeIdNsDescr hdr; uint8_t v[NVME_NIDL_UUID]; } QEMU_PACKED uuid = {}; + struct { + NvmeIdNsDescr hdr; + uint8_t v[NVME_NIDL_NGUID]; + } QEMU_PACKED nguid = {}; struct { NvmeIdNsDescr hdr; uint64_t v; @@ -4963,6 +5672,14 @@ static uint16_t nvme_identify_ns_descr_list(NvmeCtrl *n, NvmeRequest *req) pos += sizeof(uuid); } + if (!nvme_nguid_is_null(&ns->params.nguid)) { + nguid.hdr.nidt = NVME_NIDT_NGUID; + nguid.hdr.nidl = NVME_NIDL_NGUID; + memcpy(nguid.v, ns->params.nguid.data, NVME_NIDL_NGUID); + memcpy(pos, &nguid, sizeof(nguid)); + pos += sizeof(nguid); + } + if (ns->params.eui64) { eui64.hdr.nidt = NVME_NIDT_EUI64; eui64.hdr.nidl = NVME_NIDL_EUI64; @@ -5009,6 +5726,10 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeRequest *req) return nvme_identify_ctrl_list(n, req, true); case NVME_ID_CNS_CTRL_LIST: return nvme_identify_ctrl_list(n, req, false); + case NVME_ID_CNS_PRIMARY_CTRL_CAP: + return nvme_identify_pri_ctrl_cap(n, req); + case NVME_ID_CNS_SECONDARY_CTRL_LIST: + return nvme_identify_sec_ctrl_list(n, req); case NVME_ID_CNS_CS_NS: return nvme_identify_ns_csi(n, req, true); case NVME_ID_CNS_CS_NS_PRESENT: @@ -5089,18 +5810,97 @@ static uint16_t nvme_get_feature_timestamp(NvmeCtrl *n, NvmeRequest *req) return nvme_c2h(n, (uint8_t *)×tamp, sizeof(timestamp), req); } +static int nvme_get_feature_fdp(NvmeCtrl *n, uint32_t endgrpid, + uint32_t *result) +{ + *result = 0; + + if (!n->subsys || !n->subsys->endgrp.fdp.enabled) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + *result = FIELD_DP16(0, FEAT_FDP, FDPE, 1); + *result = FIELD_DP16(*result, FEAT_FDP, CONF_NDX, 0); + + return NVME_SUCCESS; +} + +static uint16_t nvme_get_feature_fdp_events(NvmeCtrl *n, NvmeNamespace *ns, + NvmeRequest *req, uint32_t *result) +{ + NvmeCmd *cmd = &req->cmd; + uint32_t cdw11 = le32_to_cpu(cmd->cdw11); + uint16_t ph = cdw11 & 0xffff; + uint8_t noet = (cdw11 >> 16) & 0xff; + uint16_t ruhid, ret; + uint32_t nentries = 0; + uint8_t s_events_ndx = 0; + size_t s_events_siz = sizeof(NvmeFdpEventDescr) * noet; + g_autofree NvmeFdpEventDescr *s_events = g_malloc0(s_events_siz); + NvmeRuHandle *ruh; + NvmeFdpEventDescr *s_event; + + if (!n->subsys || !n->subsys->endgrp.fdp.enabled) { + return NVME_FDP_DISABLED | NVME_DNR; + } + + if (!nvme_ph_valid(ns, ph)) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + ruhid = ns->fdp.phs[ph]; + ruh = &n->subsys->endgrp.fdp.ruhs[ruhid]; + + assert(ruh); + + if (unlikely(noet == 0)) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + for (uint8_t event_type = 0; event_type < FDP_EVT_MAX; event_type++) { + uint8_t shift = nvme_fdp_evf_shifts[event_type]; + if (!shift && event_type) { + /* + * only first entry (event_type == 0) has a shift value of 0 + * other entries are simply unpopulated. + */ + continue; + } + + nentries++; + + s_event = &s_events[s_events_ndx]; + s_event->evt = event_type; + s_event->evta = (ruh->event_filter >> shift) & 0x1; + + /* break if all `noet` entries are filled */ + if ((++s_events_ndx) == noet) { + break; + } + } + + ret = nvme_c2h(n, s_events, s_events_siz, req); + if (ret) { + return ret; + } + + *result = nentries; + return NVME_SUCCESS; +} + static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeRequest *req) { NvmeCmd *cmd = &req->cmd; uint32_t dw10 = le32_to_cpu(cmd->cdw10); uint32_t dw11 = le32_to_cpu(cmd->cdw11); uint32_t nsid = le32_to_cpu(cmd->nsid); - uint32_t result; + uint32_t result = 0; uint8_t fid = NVME_GETSETFEAT_FID(dw10); NvmeGetFeatureSelect sel = NVME_GETFEAT_SELECT(dw10); uint16_t iv; NvmeNamespace *ns; int i; + uint16_t endgrpid = 0, ret = NVME_SUCCESS; static const uint32_t nvme_feature_default[NVME_FID_MAX] = { [NVME_ARBITRATION] = NVME_ARB_AB_NOLIMIT, @@ -5198,6 +5998,33 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeRequest *req) case NVME_HOST_BEHAVIOR_SUPPORT: return nvme_c2h(n, (uint8_t *)&n->features.hbs, sizeof(n->features.hbs), req); + case NVME_FDP_MODE: + endgrpid = dw11 & 0xff; + + if (endgrpid != 0x1) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + ret = nvme_get_feature_fdp(n, endgrpid, &result); + if (ret) { + return ret; + } + goto out; + case NVME_FDP_EVENTS: + if (!nvme_nsid_valid(n, nsid)) { + return NVME_INVALID_NSID | NVME_DNR; + } + + ns = nvme_ns(n, nsid); + if (unlikely(!ns)) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + ret = nvme_get_feature_fdp_events(n, ns, req, &result); + if (ret) { + return ret; + } + goto out; default: break; } @@ -5217,13 +6044,12 @@ defaults: break; case NVME_NUMBER_OF_QUEUES: - result = (n->params.max_ioqpairs - 1) | - ((n->params.max_ioqpairs - 1) << 16); + result = (n->conf_ioqpairs - 1) | ((n->conf_ioqpairs - 1) << 16); trace_pci_nvme_getfeat_numq(result); break; case NVME_INTERRUPT_VECTOR_CONF: iv = dw11 & 0xffff; - if (iv >= n->params.max_ioqpairs + 1) { + if (iv >= n->conf_ioqpairs + 1) { return NVME_INVALID_FIELD | NVME_DNR; } @@ -5231,6 +6057,20 @@ defaults: if (iv == n->admin_cq.vector) { result |= NVME_INTVC_NOCOALESCING; } + break; + case NVME_FDP_MODE: + endgrpid = dw11 & 0xff; + + if (endgrpid != 0x1) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + ret = nvme_get_feature_fdp(n, endgrpid, &result); + if (ret) { + return ret; + } + goto out; + break; default: result = nvme_feature_default[fid]; @@ -5239,7 +6079,7 @@ defaults: out: req->cqe.result = cpu_to_le32(result); - return NVME_SUCCESS; + return ret; } static uint16_t nvme_set_feature_timestamp(NvmeCtrl *n, NvmeRequest *req) @@ -5257,6 +6097,51 @@ static uint16_t nvme_set_feature_timestamp(NvmeCtrl *n, NvmeRequest *req) return NVME_SUCCESS; } +static uint16_t nvme_set_feature_fdp_events(NvmeCtrl *n, NvmeNamespace *ns, + NvmeRequest *req) +{ + NvmeCmd *cmd = &req->cmd; + uint32_t cdw11 = le32_to_cpu(cmd->cdw11); + uint16_t ph = cdw11 & 0xffff; + uint8_t noet = (cdw11 >> 16) & 0xff; + uint16_t ret, ruhid; + uint8_t enable = le32_to_cpu(cmd->cdw12) & 0x1; + uint8_t event_mask = 0; + unsigned int i; + g_autofree uint8_t *events = g_malloc0(noet); + NvmeRuHandle *ruh = NULL; + + assert(ns); + + if (!n->subsys || !n->subsys->endgrp.fdp.enabled) { + return NVME_FDP_DISABLED | NVME_DNR; + } + + if (!nvme_ph_valid(ns, ph)) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + ruhid = ns->fdp.phs[ph]; + ruh = &n->subsys->endgrp.fdp.ruhs[ruhid]; + + ret = nvme_h2c(n, events, noet, req); + if (ret) { + return ret; + } + + for (i = 0; i < noet; i++) { + event_mask |= (1 << nvme_fdp_evf_shifts[events[i]]); + } + + if (enable) { + ruh->event_filter |= event_mask; + } else { + ruh->event_filter = ruh->event_filter & ~event_mask; + } + + return NVME_SUCCESS; +} + static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req) { NvmeNamespace *ns = NULL; @@ -5379,10 +6264,10 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_setfeat_numq((dw11 & 0xffff) + 1, ((dw11 >> 16) & 0xffff) + 1, - n->params.max_ioqpairs, - n->params.max_ioqpairs); - req->cqe.result = cpu_to_le32((n->params.max_ioqpairs - 1) | - ((n->params.max_ioqpairs - 1) << 16)); + n->conf_ioqpairs, + n->conf_ioqpairs); + req->cqe.result = cpu_to_le32((n->conf_ioqpairs - 1) | + ((n->conf_ioqpairs - 1) << 16)); break; case NVME_ASYNCHRONOUS_EVENT_CONF: n->features.async_config = dw11; @@ -5416,6 +6301,11 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req) return NVME_CMD_SET_CMB_REJECTED | NVME_DNR; } break; + case NVME_FDP_MODE: + /* spec: abort with cmd seq err if there's one or more NS' in endgrp */ + return NVME_CMD_SEQ_ERROR | NVME_DNR; + case NVME_FDP_EVENTS: + return nvme_set_feature_fdp_events(n, ns, req); default: return NVME_FEAT_NOT_CHANGEABLE | NVME_DNR; } @@ -5565,7 +6455,6 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) typedef struct NvmeFormatAIOCB { BlockAIOCB common; BlockAIOCB *aiocb; - QEMUBH *bh; NvmeRequest *req; int ret; @@ -5580,21 +6469,21 @@ typedef struct NvmeFormatAIOCB { uint8_t pil; } NvmeFormatAIOCB; -static void nvme_format_bh(void *opaque); - static void nvme_format_cancel(BlockAIOCB *aiocb) { NvmeFormatAIOCB *iocb = container_of(aiocb, NvmeFormatAIOCB, common); + iocb->ret = -ECANCELED; + if (iocb->aiocb) { blk_aio_cancel_async(iocb->aiocb); + iocb->aiocb = NULL; } } static const AIOCBInfo nvme_format_aiocb_info = { .aiocb_size = sizeof(NvmeFormatAIOCB), .cancel_async = nvme_format_cancel, - .get_aio_context = nvme_get_aio_context, }; static void nvme_format_set(NvmeNamespace *ns, uint8_t lbaf, uint8_t mset, @@ -5611,13 +6500,17 @@ static void nvme_format_set(NvmeNamespace *ns, uint8_t lbaf, uint8_t mset, nvme_ns_init_format(ns); } +static void nvme_do_format(NvmeFormatAIOCB *iocb); + static void nvme_format_ns_cb(void *opaque, int ret) { NvmeFormatAIOCB *iocb = opaque; NvmeNamespace *ns = iocb->ns; int bytes; - if (ret < 0) { + if (iocb->ret < 0) { + goto done; + } else if (ret < 0) { iocb->ret = ret; goto done; } @@ -5641,8 +6534,7 @@ static void nvme_format_ns_cb(void *opaque, int ret) iocb->offset = 0; done: - iocb->aiocb = NULL; - qemu_bh_schedule(iocb->bh); + nvme_do_format(iocb); } static uint16_t nvme_format_check(NvmeNamespace *ns, uint8_t lbaf, uint8_t pi) @@ -5666,9 +6558,8 @@ static uint16_t nvme_format_check(NvmeNamespace *ns, uint8_t lbaf, uint8_t pi) return NVME_SUCCESS; } -static void nvme_format_bh(void *opaque) +static void nvme_do_format(NvmeFormatAIOCB *iocb) { - NvmeFormatAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; NvmeCtrl *n = nvme_ctrl(req); uint32_t dw10 = le32_to_cpu(req->cmd.cdw10); @@ -5706,11 +6597,7 @@ static void nvme_format_bh(void *opaque) return; done: - qemu_bh_delete(iocb->bh); - iocb->bh = NULL; - iocb->common.cb(iocb->common.opaque, iocb->ret); - qemu_aio_unref(iocb); } @@ -5729,7 +6616,6 @@ static uint16_t nvme_format(NvmeCtrl *n, NvmeRequest *req) iocb = qemu_aio_get(&nvme_format_aiocb_info, NULL, nvme_misc_cb, req); iocb->req = req; - iocb->bh = qemu_bh_new(nvme_format_bh, iocb); iocb->ret = 0; iocb->ns = NULL; iocb->nsid = 0; @@ -5758,17 +6644,290 @@ static uint16_t nvme_format(NvmeCtrl *n, NvmeRequest *req) } req->aiocb = &iocb->common; - qemu_bh_schedule(iocb->bh); + nvme_do_format(iocb); return NVME_NO_COMPLETE; out: - qemu_bh_delete(iocb->bh); - iocb->bh = NULL; qemu_aio_unref(iocb); + return status; } +static void nvme_get_virt_res_num(NvmeCtrl *n, uint8_t rt, int *num_total, + int *num_prim, int *num_sec) +{ + *num_total = le32_to_cpu(rt ? + n->pri_ctrl_cap.vifrt : n->pri_ctrl_cap.vqfrt); + *num_prim = le16_to_cpu(rt ? + n->pri_ctrl_cap.virfap : n->pri_ctrl_cap.vqrfap); + *num_sec = le16_to_cpu(rt ? n->pri_ctrl_cap.virfa : n->pri_ctrl_cap.vqrfa); +} + +static uint16_t nvme_assign_virt_res_to_prim(NvmeCtrl *n, NvmeRequest *req, + uint16_t cntlid, uint8_t rt, + int nr) +{ + int num_total, num_prim, num_sec; + + if (cntlid != n->cntlid) { + return NVME_INVALID_CTRL_ID | NVME_DNR; + } + + nvme_get_virt_res_num(n, rt, &num_total, &num_prim, &num_sec); + + if (nr > num_total) { + return NVME_INVALID_NUM_RESOURCES | NVME_DNR; + } + + if (nr > num_total - num_sec) { + return NVME_INVALID_RESOURCE_ID | NVME_DNR; + } + + if (rt) { + n->next_pri_ctrl_cap.virfap = cpu_to_le16(nr); + } else { + n->next_pri_ctrl_cap.vqrfap = cpu_to_le16(nr); + } + + req->cqe.result = cpu_to_le32(nr); + return req->status; +} + +static void nvme_update_virt_res(NvmeCtrl *n, NvmeSecCtrlEntry *sctrl, + uint8_t rt, int nr) +{ + int prev_nr, prev_total; + + if (rt) { + prev_nr = le16_to_cpu(sctrl->nvi); + prev_total = le32_to_cpu(n->pri_ctrl_cap.virfa); + sctrl->nvi = cpu_to_le16(nr); + n->pri_ctrl_cap.virfa = cpu_to_le32(prev_total + nr - prev_nr); + } else { + prev_nr = le16_to_cpu(sctrl->nvq); + prev_total = le32_to_cpu(n->pri_ctrl_cap.vqrfa); + sctrl->nvq = cpu_to_le16(nr); + n->pri_ctrl_cap.vqrfa = cpu_to_le32(prev_total + nr - prev_nr); + } +} + +static uint16_t nvme_assign_virt_res_to_sec(NvmeCtrl *n, NvmeRequest *req, + uint16_t cntlid, uint8_t rt, int nr) +{ + int num_total, num_prim, num_sec, num_free, diff, limit; + NvmeSecCtrlEntry *sctrl; + + sctrl = nvme_sctrl_for_cntlid(n, cntlid); + if (!sctrl) { + return NVME_INVALID_CTRL_ID | NVME_DNR; + } + + if (sctrl->scs) { + return NVME_INVALID_SEC_CTRL_STATE | NVME_DNR; + } + + limit = le16_to_cpu(rt ? n->pri_ctrl_cap.vifrsm : n->pri_ctrl_cap.vqfrsm); + if (nr > limit) { + return NVME_INVALID_NUM_RESOURCES | NVME_DNR; + } + + nvme_get_virt_res_num(n, rt, &num_total, &num_prim, &num_sec); + num_free = num_total - num_prim - num_sec; + diff = nr - le16_to_cpu(rt ? sctrl->nvi : sctrl->nvq); + + if (diff > num_free) { + return NVME_INVALID_RESOURCE_ID | NVME_DNR; + } + + nvme_update_virt_res(n, sctrl, rt, nr); + req->cqe.result = cpu_to_le32(nr); + + return req->status; +} + +static uint16_t nvme_virt_set_state(NvmeCtrl *n, uint16_t cntlid, bool online) +{ + PCIDevice *pci = PCI_DEVICE(n); + NvmeCtrl *sn = NULL; + NvmeSecCtrlEntry *sctrl; + int vf_index; + + sctrl = nvme_sctrl_for_cntlid(n, cntlid); + if (!sctrl) { + return NVME_INVALID_CTRL_ID | NVME_DNR; + } + + if (!pci_is_vf(pci)) { + vf_index = le16_to_cpu(sctrl->vfn) - 1; + sn = NVME(pcie_sriov_get_vf_at_index(pci, vf_index)); + } + + if (online) { + if (!sctrl->nvi || (le16_to_cpu(sctrl->nvq) < 2) || !sn) { + return NVME_INVALID_SEC_CTRL_STATE | NVME_DNR; + } + + if (!sctrl->scs) { + sctrl->scs = 0x1; + nvme_ctrl_reset(sn, NVME_RESET_FUNCTION); + } + } else { + nvme_update_virt_res(n, sctrl, NVME_VIRT_RES_INTERRUPT, 0); + nvme_update_virt_res(n, sctrl, NVME_VIRT_RES_QUEUE, 0); + + if (sctrl->scs) { + sctrl->scs = 0x0; + if (sn) { + nvme_ctrl_reset(sn, NVME_RESET_FUNCTION); + } + } + } + + return NVME_SUCCESS; +} + +static uint16_t nvme_virt_mngmt(NvmeCtrl *n, NvmeRequest *req) +{ + uint32_t dw10 = le32_to_cpu(req->cmd.cdw10); + uint32_t dw11 = le32_to_cpu(req->cmd.cdw11); + uint8_t act = dw10 & 0xf; + uint8_t rt = (dw10 >> 8) & 0x7; + uint16_t cntlid = (dw10 >> 16) & 0xffff; + int nr = dw11 & 0xffff; + + trace_pci_nvme_virt_mngmt(nvme_cid(req), act, cntlid, rt ? "VI" : "VQ", nr); + + if (rt != NVME_VIRT_RES_QUEUE && rt != NVME_VIRT_RES_INTERRUPT) { + return NVME_INVALID_RESOURCE_ID | NVME_DNR; + } + + switch (act) { + case NVME_VIRT_MNGMT_ACTION_SEC_ASSIGN: + return nvme_assign_virt_res_to_sec(n, req, cntlid, rt, nr); + case NVME_VIRT_MNGMT_ACTION_PRM_ALLOC: + return nvme_assign_virt_res_to_prim(n, req, cntlid, rt, nr); + case NVME_VIRT_MNGMT_ACTION_SEC_ONLINE: + return nvme_virt_set_state(n, cntlid, true); + case NVME_VIRT_MNGMT_ACTION_SEC_OFFLINE: + return nvme_virt_set_state(n, cntlid, false); + default: + return NVME_INVALID_FIELD | NVME_DNR; + } +} + +static uint16_t nvme_dbbuf_config(NvmeCtrl *n, const NvmeRequest *req) +{ + PCIDevice *pci = PCI_DEVICE(n); + uint64_t dbs_addr = le64_to_cpu(req->cmd.dptr.prp1); + uint64_t eis_addr = le64_to_cpu(req->cmd.dptr.prp2); + int i; + + /* Address should be page aligned */ + if (dbs_addr & (n->page_size - 1) || eis_addr & (n->page_size - 1)) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + /* Save shadow buffer base addr for use during queue creation */ + n->dbbuf_dbs = dbs_addr; + n->dbbuf_eis = eis_addr; + n->dbbuf_enabled = true; + + for (i = 0; i < n->params.max_ioqpairs + 1; i++) { + NvmeSQueue *sq = n->sq[i]; + NvmeCQueue *cq = n->cq[i]; + + if (sq) { + /* + * CAP.DSTRD is 0, so offset of ith sq db_addr is (i<<3) + * nvme_process_db() uses this hard-coded way to calculate + * doorbell offsets. Be consistent with that here. + */ + sq->db_addr = dbs_addr + (i << 3); + sq->ei_addr = eis_addr + (i << 3); + stl_le_pci_dma(pci, sq->db_addr, sq->tail, MEMTXATTRS_UNSPECIFIED); + + if (n->params.ioeventfd && sq->sqid != 0) { + if (!nvme_init_sq_ioeventfd(sq)) { + sq->ioeventfd_enabled = true; + } + } + } + + if (cq) { + /* CAP.DSTRD is 0, so offset of ith cq db_addr is (i<<3)+(1<<2) */ + cq->db_addr = dbs_addr + (i << 3) + (1 << 2); + cq->ei_addr = eis_addr + (i << 3) + (1 << 2); + stl_le_pci_dma(pci, cq->db_addr, cq->head, MEMTXATTRS_UNSPECIFIED); + + if (n->params.ioeventfd && cq->cqid != 0) { + if (!nvme_init_cq_ioeventfd(cq)) { + cq->ioeventfd_enabled = true; + } + } + } + } + + trace_pci_nvme_dbbuf_config(dbs_addr, eis_addr); + + return NVME_SUCCESS; +} + +static uint16_t nvme_directive_send(NvmeCtrl *n, NvmeRequest *req) +{ + return NVME_INVALID_FIELD | NVME_DNR; +} + +static uint16_t nvme_directive_receive(NvmeCtrl *n, NvmeRequest *req) +{ + NvmeNamespace *ns; + uint32_t dw10 = le32_to_cpu(req->cmd.cdw10); + uint32_t dw11 = le32_to_cpu(req->cmd.cdw11); + uint32_t nsid = le32_to_cpu(req->cmd.nsid); + uint8_t doper, dtype; + uint32_t numd, trans_len; + NvmeDirectiveIdentify id = { + .supported = 1 << NVME_DIRECTIVE_IDENTIFY, + .enabled = 1 << NVME_DIRECTIVE_IDENTIFY, + }; + + numd = dw10 + 1; + doper = dw11 & 0xff; + dtype = (dw11 >> 8) & 0xff; + + trans_len = MIN(sizeof(NvmeDirectiveIdentify), numd << 2); + + if (nsid == NVME_NSID_BROADCAST || dtype != NVME_DIRECTIVE_IDENTIFY || + doper != NVME_DIRECTIVE_RETURN_PARAMS) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + ns = nvme_ns(n, nsid); + if (!ns) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + switch (dtype) { + case NVME_DIRECTIVE_IDENTIFY: + switch (doper) { + case NVME_DIRECTIVE_RETURN_PARAMS: + if (ns->endgrp && ns->endgrp->fdp.enabled) { + id.supported |= 1 << NVME_DIRECTIVE_DATA_PLACEMENT; + id.enabled |= 1 << NVME_DIRECTIVE_DATA_PLACEMENT; + id.persistent |= 1 << NVME_DIRECTIVE_DATA_PLACEMENT; + } + + return nvme_c2h(n, (uint8_t *)&id, trans_len, req); + + default: + return NVME_INVALID_FIELD | NVME_DNR; + } + + default: + return NVME_INVALID_FIELD; + } +} + static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req) { trace_pci_nvme_admin_cmd(nvme_cid(req), nvme_sqid(req), req->cmd.opcode, @@ -5811,8 +6970,16 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req) return nvme_aer(n, req); case NVME_ADM_CMD_NS_ATTACHMENT: return nvme_ns_attachment(n, req); + case NVME_ADM_CMD_VIRT_MNGMT: + return nvme_virt_mngmt(n, req); + case NVME_ADM_CMD_DBBUF_CONFIG: + return nvme_dbbuf_config(n, req); case NVME_ADM_CMD_FORMAT_NVM: return nvme_format(n, req); + case NVME_ADM_CMD_DIRECTIVE_SEND: + return nvme_directive_send(n, req); + case NVME_ADM_CMD_DIRECTIVE_RECV: + return nvme_directive_receive(n, req); default: assert(false); } @@ -5820,6 +6987,22 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req) return NVME_INVALID_OPCODE | NVME_DNR; } +static void nvme_update_sq_eventidx(const NvmeSQueue *sq) +{ + trace_pci_nvme_update_sq_eventidx(sq->sqid, sq->tail); + + stl_le_pci_dma(PCI_DEVICE(sq->ctrl), sq->ei_addr, sq->tail, + MEMTXATTRS_UNSPECIFIED); +} + +static void nvme_update_sq_tail(NvmeSQueue *sq) +{ + ldl_le_pci_dma(PCI_DEVICE(sq->ctrl), sq->db_addr, &sq->tail, + MEMTXATTRS_UNSPECIFIED); + + trace_pci_nvme_update_sq_tail(sq->sqid, sq->tail); +} + static void nvme_process_sq(void *opaque) { NvmeSQueue *sq = opaque; @@ -5831,8 +7014,12 @@ static void nvme_process_sq(void *opaque) NvmeCmd cmd; NvmeRequest *req; + if (n->dbbuf_enabled) { + nvme_update_sq_tail(sq); + } + while (!(nvme_sq_empty(sq) || QTAILQ_EMPTY(&sq->req_list))) { - addr = sq->dma_addr + sq->head * n->sqe_size; + addr = sq->dma_addr + (sq->head << NVME_SQES); if (nvme_addr_read(n, addr, (void *)&cmd, sizeof(cmd))) { trace_pci_nvme_err_addr_read(addr); trace_pci_nvme_err_cfs(); @@ -5854,11 +7041,56 @@ static void nvme_process_sq(void *opaque) req->status = status; nvme_enqueue_req_completion(cq, req); } + + if (n->dbbuf_enabled) { + nvme_update_sq_eventidx(sq); + nvme_update_sq_tail(sq); + } } } -static void nvme_ctrl_reset(NvmeCtrl *n) +static void nvme_update_msixcap_ts(PCIDevice *pci_dev, uint32_t table_size) { + uint8_t *config; + + if (!msix_present(pci_dev)) { + return; + } + + assert(table_size > 0 && table_size <= pci_dev->msix_entries_nr); + + config = pci_dev->config + pci_dev->msix_cap; + pci_set_word_by_mask(config + PCI_MSIX_FLAGS, PCI_MSIX_FLAGS_QSIZE, + table_size - 1); +} + +static void nvme_activate_virt_res(NvmeCtrl *n) +{ + PCIDevice *pci_dev = PCI_DEVICE(n); + NvmePriCtrlCap *cap = &n->pri_ctrl_cap; + NvmeSecCtrlEntry *sctrl; + + /* -1 to account for the admin queue */ + if (pci_is_vf(pci_dev)) { + sctrl = nvme_sctrl(n); + cap->vqprt = sctrl->nvq; + cap->viprt = sctrl->nvi; + n->conf_ioqpairs = sctrl->nvq ? le16_to_cpu(sctrl->nvq) - 1 : 0; + n->conf_msix_qsize = sctrl->nvi ? le16_to_cpu(sctrl->nvi) : 1; + } else { + cap->vqrfap = n->next_pri_ctrl_cap.vqrfap; + cap->virfap = n->next_pri_ctrl_cap.virfap; + n->conf_ioqpairs = le16_to_cpu(cap->vqprt) + + le16_to_cpu(cap->vqrfap) - 1; + n->conf_msix_qsize = le16_to_cpu(cap->viprt) + + le16_to_cpu(cap->virfap); + } +} + +static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst) +{ + PCIDevice *pci_dev = PCI_DEVICE(n); + NvmeSecCtrlEntry *sctrl; NvmeNamespace *ns; int i; @@ -5888,9 +7120,41 @@ static void nvme_ctrl_reset(NvmeCtrl *n) g_free(event); } + if (n->params.sriov_max_vfs) { + if (!pci_is_vf(pci_dev)) { + for (i = 0; i < n->sec_ctrl_list.numcntl; i++) { + sctrl = &n->sec_ctrl_list.sec[i]; + nvme_virt_set_state(n, le16_to_cpu(sctrl->scid), false); + } + } + + if (rst != NVME_RESET_CONTROLLER) { + nvme_activate_virt_res(n); + } + } + n->aer_queued = 0; + n->aer_mask = 0; n->outstanding_aers = 0; n->qs_created = false; + + nvme_update_msixcap_ts(pci_dev, n->conf_msix_qsize); + + if (pci_is_vf(pci_dev)) { + sctrl = nvme_sctrl(n); + + stl_le_p(&n->bar.csts, sctrl->scs ? 0 : NVME_CSTS_FAILED); + } else { + stl_le_p(&n->bar.csts, 0); + } + + stl_le_p(&n->bar.intms, 0); + stl_le_p(&n->bar.intmc, 0); + stl_le_p(&n->bar.cc, 0); + + n->dbbuf_dbs = 0; + n->dbbuf_eis = 0; + n->dbbuf_enabled = false; } static void nvme_ctrl_shutdown(NvmeCtrl *n) @@ -5936,7 +7200,13 @@ static int nvme_start_ctrl(NvmeCtrl *n) uint64_t acq = ldq_le_p(&n->bar.acq); uint32_t page_bits = NVME_CC_MPS(cc) + 12; uint32_t page_size = 1 << page_bits; + NvmeSecCtrlEntry *sctrl = nvme_sctrl(n); + if (pci_is_vf(PCI_DEVICE(n)) && !sctrl->scs) { + trace_pci_nvme_err_startfail_virt_state(le16_to_cpu(sctrl->nvi), + le16_to_cpu(sctrl->nvq)); + return -1; + } if (unlikely(n->cq[0])) { trace_pci_nvme_err_startfail_cq(); return -1; @@ -5970,34 +7240,6 @@ static int nvme_start_ctrl(NvmeCtrl *n) NVME_CAP_MPSMAX(cap)); return -1; } - if (unlikely(NVME_CC_IOCQES(cc) < - NVME_CTRL_CQES_MIN(n->id_ctrl.cqes))) { - trace_pci_nvme_err_startfail_cqent_too_small( - NVME_CC_IOCQES(cc), - NVME_CTRL_CQES_MIN(cap)); - return -1; - } - if (unlikely(NVME_CC_IOCQES(cc) > - NVME_CTRL_CQES_MAX(n->id_ctrl.cqes))) { - trace_pci_nvme_err_startfail_cqent_too_large( - NVME_CC_IOCQES(cc), - NVME_CTRL_CQES_MAX(cap)); - return -1; - } - if (unlikely(NVME_CC_IOSQES(cc) < - NVME_CTRL_SQES_MIN(n->id_ctrl.sqes))) { - trace_pci_nvme_err_startfail_sqent_too_small( - NVME_CC_IOSQES(cc), - NVME_CTRL_SQES_MIN(cap)); - return -1; - } - if (unlikely(NVME_CC_IOSQES(cc) > - NVME_CTRL_SQES_MAX(n->id_ctrl.sqes))) { - trace_pci_nvme_err_startfail_sqent_too_large( - NVME_CC_IOSQES(cc), - NVME_CTRL_SQES_MAX(cap)); - return -1; - } if (unlikely(!NVME_AQA_ASQS(aqa))) { trace_pci_nvme_err_startfail_asqent_sz_zero(); return -1; @@ -6010,15 +7252,11 @@ static int nvme_start_ctrl(NvmeCtrl *n) n->page_bits = page_bits; n->page_size = page_size; n->max_prp_ents = n->page_size / sizeof(uint64_t); - n->cqe_size = 1 << NVME_CC_IOCQES(cc); - n->sqe_size = 1 << NVME_CC_IOSQES(cc); nvme_init_cq(&n->admin_cq, n, acq, 0, 0, NVME_AQA_ACQS(aqa) + 1, 1); nvme_init_sq(&n->admin_sq, n, asq, 0, 0, NVME_AQA_ASQS(aqa) + 1); nvme_set_timestamp(n, 0ULL); - QTAILQ_INIT(&n->aer_queue); - nvme_select_iocs(n); return 0; @@ -6047,6 +7285,7 @@ static void nvme_cmb_enable_regs(NvmeCtrl *n) static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, unsigned size) { + PCIDevice *pci = PCI_DEVICE(n); uint64_t cap = ldq_le_p(&n->bar.cap); uint32_t cc = ldl_le_p(&n->bar.cc); uint32_t intms = ldl_le_p(&n->bar.intms); @@ -6070,7 +7309,7 @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, switch (offset) { case NVME_REG_INTMS: - if (unlikely(msix_enabled(&(n->parent_obj)))) { + if (unlikely(msix_enabled(pci))) { NVME_GUEST_ERR(pci_nvme_ub_mmiowr_intmask_with_msix, "undefined access to interrupt mask set" " when MSI-X is enabled"); @@ -6083,7 +7322,7 @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, nvme_irq_check(n); break; case NVME_REG_INTMC: - if (unlikely(msix_enabled(&(n->parent_obj)))) { + if (unlikely(msix_enabled(pci))) { NVME_GUEST_ERR(pci_nvme_ub_mmiowr_intmask_with_msix, "undefined access to interrupt mask clr" " when MSI-X is enabled"); @@ -6096,20 +7335,21 @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, nvme_irq_check(n); break; case NVME_REG_CC: + stl_le_p(&n->bar.cc, data); + trace_pci_nvme_mmio_cfg(data & 0xffffffff); - /* Windows first sends data, then sends enable bit */ - if (!NVME_CC_EN(data) && !NVME_CC_EN(cc) && - !NVME_CC_SHN(data) && !NVME_CC_SHN(cc)) - { - cc = data; + if (NVME_CC_SHN(data) && !(NVME_CC_SHN(cc))) { + trace_pci_nvme_mmio_shutdown_set(); + nvme_ctrl_shutdown(n); + csts &= ~(CSTS_SHST_MASK << CSTS_SHST_SHIFT); + csts |= NVME_CSTS_SHST_COMPLETE; + } else if (!NVME_CC_SHN(data) && NVME_CC_SHN(cc)) { + trace_pci_nvme_mmio_shutdown_cleared(); + csts &= ~(CSTS_SHST_MASK << CSTS_SHST_SHIFT); } if (NVME_CC_EN(data) && !NVME_CC_EN(cc)) { - cc = data; - - /* flush CC since nvme_start_ctrl() needs the value */ - stl_le_p(&n->bar.cc, cc); if (unlikely(nvme_start_ctrl(n))) { trace_pci_nvme_err_startfail(); csts = NVME_CSTS_FAILED; @@ -6119,23 +7359,11 @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, } } else if (!NVME_CC_EN(data) && NVME_CC_EN(cc)) { trace_pci_nvme_mmio_stopped(); - nvme_ctrl_reset(n); - cc = 0; - csts &= ~NVME_CSTS_READY; + nvme_ctrl_reset(n, NVME_RESET_CONTROLLER); + + break; } - if (NVME_CC_SHN(data) && !(NVME_CC_SHN(cc))) { - trace_pci_nvme_mmio_shutdown_set(); - nvme_ctrl_shutdown(n); - cc = data; - csts |= NVME_CSTS_SHST_COMPLETE; - } else if (!NVME_CC_SHN(data) && NVME_CC_SHN(cc)) { - trace_pci_nvme_mmio_shutdown_cleared(); - csts &= ~NVME_CSTS_SHST_COMPLETE; - cc = data; - } - - stl_le_p(&n->bar.cc, cc); stl_le_p(&n->bar.csts, csts); break; @@ -6319,6 +7547,12 @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size) return 0; } + if (pci_is_vf(PCI_DEVICE(n)) && !nvme_sctrl(n)->scs && + addr != NVME_REG_CSTS) { + trace_pci_nvme_err_ignored_mmio_vf_offline(addr, size); + return 0; + } + /* * When PMRWBM bit 1 is set then read from * from PMRSTS should ensure prior writes @@ -6334,6 +7568,7 @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size) static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) { + PCIDevice *pci = PCI_DEVICE(n); uint32_t qid; if (unlikely(addr & ((1 << 2) - 1))) { @@ -6360,7 +7595,7 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) /* * NVM Express v1.3d, Section 4.1 state: "If host software writes * an invalid value to the Submission Queue Tail Doorbell or - * Completion Queue Head Doorbell regiter and an Asynchronous Event + * Completion Queue Head Doorbell register and an Asynchronous Event * Request command is outstanding, then an asynchronous event is * posted to the Admin Completion Queue with a status code of * Invalid Doorbell Write Value." @@ -6400,12 +7635,15 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) start_sqs = nvme_cq_full(cq) ? 1 : 0; cq->head = new_head; + if (!qid && n->dbbuf_enabled) { + stl_le_pci_dma(pci, cq->db_addr, cq->head, MEMTXATTRS_UNSPECIFIED); + } if (start_sqs) { NvmeSQueue *sq; QTAILQ_FOREACH(sq, &cq->sq_list, entry) { - timer_mod(sq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); + qemu_bh_schedule(sq->bh); } - timer_mod(cq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); + qemu_bh_schedule(cq->bh); } if (cq->tail == cq->head) { @@ -6457,7 +7695,24 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) trace_pci_nvme_mmio_doorbell_sq(sq->sqid, new_tail); sq->tail = new_tail; - timer_mod(sq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); + if (!qid && n->dbbuf_enabled) { + /* + * The spec states "the host shall also update the controller's + * corresponding doorbell property to match the value of that entry + * in the Shadow Doorbell buffer." + * + * Since this context is currently a VM trap, we can safely enforce + * the requirement from the device side in case the host is + * misbehaving. + * + * Note, we shouldn't have to do this, but various drivers + * including ones that run on Linux, are not updating Admin Queues, + * so we can't trust reading it for an appropriate sq tail. + */ + stl_le_pci_dma(pci, sq->db_addr, sq->tail, MEMTXATTRS_UNSPECIFIED); + } + + qemu_bh_schedule(sq->bh); } } @@ -6468,6 +7723,12 @@ static void nvme_mmio_write(void *opaque, hwaddr addr, uint64_t data, trace_pci_nvme_mmio_write(addr, data, size); + if (pci_is_vf(PCI_DEVICE(n)) && !nvme_sctrl(n)->scs && + addr != NVME_REG_CSTS) { + trace_pci_nvme_err_ignored_mmio_vf_offline(addr, size); + return; + } + if (addr < sizeof(n->bar)) { nvme_write_bar(n, addr, data, size); } else { @@ -6508,7 +7769,7 @@ static const MemoryRegionOps nvme_cmb_ops = { }, }; -static void nvme_check_constraints(NvmeCtrl *n, Error **errp) +static bool nvme_check_params(NvmeCtrl *n, Error **errp) { NvmeParams *params = &n->params; @@ -6522,38 +7783,43 @@ static void nvme_check_constraints(NvmeCtrl *n, Error **errp) if (n->namespace.blkconf.blk && n->subsys) { error_setg(errp, "subsystem support is unavailable with legacy " "namespace ('drive' property)"); - return; + return false; } if (params->max_ioqpairs < 1 || params->max_ioqpairs > NVME_MAX_IOQPAIRS) { error_setg(errp, "max_ioqpairs must be between 1 and %d", NVME_MAX_IOQPAIRS); - return; + return false; } if (params->msix_qsize < 1 || params->msix_qsize > PCI_MSIX_FLAGS_QSIZE + 1) { error_setg(errp, "msix_qsize must be between 1 and %d", PCI_MSIX_FLAGS_QSIZE + 1); - return; + return false; } if (!params->serial) { error_setg(errp, "serial property not set"); - return; + return false; } if (n->pmr.dev) { + if (params->msix_exclusive_bar) { + error_setg(errp, "not enough BARs available to enable PMR"); + return false; + } + if (host_memory_backend_is_mapped(n->pmr.dev)) { error_setg(errp, "can't use already busy memdev: %s", object_get_canonical_path_component(OBJECT(n->pmr.dev))); - return; + return false; } if (!is_power_of_2(n->pmr.dev->size)) { error_setg(errp, "pmr backend size needs to be power of 2 in size"); - return; + return false; } host_memory_backend_set_mapped(n->pmr.dev, true); @@ -6562,26 +7828,150 @@ static void nvme_check_constraints(NvmeCtrl *n, Error **errp) if (n->params.zasl > n->params.mdts) { error_setg(errp, "zoned.zasl (Zone Append Size Limit) must be less " "than or equal to mdts (Maximum Data Transfer Size)"); - return; + return false; } if (!n->params.vsl) { error_setg(errp, "vsl must be non-zero"); - return; + return false; } + + if (params->sriov_max_vfs) { + if (!n->subsys) { + error_setg(errp, "subsystem is required for the use of SR-IOV"); + return false; + } + + if (params->sriov_max_vfs > NVME_MAX_VFS) { + error_setg(errp, "sriov_max_vfs must be between 0 and %d", + NVME_MAX_VFS); + return false; + } + + if (params->cmb_size_mb) { + error_setg(errp, "CMB is not supported with SR-IOV"); + return false; + } + + if (n->pmr.dev) { + error_setg(errp, "PMR is not supported with SR-IOV"); + return false; + } + + if (!params->sriov_vq_flexible || !params->sriov_vi_flexible) { + error_setg(errp, "both sriov_vq_flexible and sriov_vi_flexible" + " must be set for the use of SR-IOV"); + return false; + } + + if (params->sriov_vq_flexible < params->sriov_max_vfs * 2) { + error_setg(errp, "sriov_vq_flexible must be greater than or equal" + " to %d (sriov_max_vfs * 2)", params->sriov_max_vfs * 2); + return false; + } + + if (params->max_ioqpairs < params->sriov_vq_flexible + 2) { + error_setg(errp, "(max_ioqpairs - sriov_vq_flexible) must be" + " greater than or equal to 2"); + return false; + } + + if (params->sriov_vi_flexible < params->sriov_max_vfs) { + error_setg(errp, "sriov_vi_flexible must be greater than or equal" + " to %d (sriov_max_vfs)", params->sriov_max_vfs); + return false; + } + + if (params->msix_qsize < params->sriov_vi_flexible + 1) { + error_setg(errp, "(msix_qsize - sriov_vi_flexible) must be" + " greater than or equal to 1"); + return false; + } + + if (params->sriov_max_vi_per_vf && + (params->sriov_max_vi_per_vf - 1) % NVME_VF_RES_GRANULARITY) { + error_setg(errp, "sriov_max_vi_per_vf must meet:" + " (sriov_max_vi_per_vf - 1) %% %d == 0 and" + " sriov_max_vi_per_vf >= 1", NVME_VF_RES_GRANULARITY); + return false; + } + + if (params->sriov_max_vq_per_vf && + (params->sriov_max_vq_per_vf < 2 || + (params->sriov_max_vq_per_vf - 1) % NVME_VF_RES_GRANULARITY)) { + error_setg(errp, "sriov_max_vq_per_vf must meet:" + " (sriov_max_vq_per_vf - 1) %% %d == 0 and" + " sriov_max_vq_per_vf >= 2", NVME_VF_RES_GRANULARITY); + return false; + } + } + + return true; } static void nvme_init_state(NvmeCtrl *n) { - /* add one to max_ioqpairs to account for the admin queue pair */ - n->reg_size = pow2ceil(sizeof(NvmeBar) + - 2 * (n->params.max_ioqpairs + 1) * NVME_DB_SIZE); + NvmePriCtrlCap *cap = &n->pri_ctrl_cap; + NvmeSecCtrlList *list = &n->sec_ctrl_list; + NvmeSecCtrlEntry *sctrl; + PCIDevice *pci = PCI_DEVICE(n); + uint8_t max_vfs; + int i; + + if (pci_is_vf(pci)) { + sctrl = nvme_sctrl(n); + max_vfs = 0; + n->conf_ioqpairs = sctrl->nvq ? le16_to_cpu(sctrl->nvq) - 1 : 0; + n->conf_msix_qsize = sctrl->nvi ? le16_to_cpu(sctrl->nvi) : 1; + } else { + max_vfs = n->params.sriov_max_vfs; + n->conf_ioqpairs = n->params.max_ioqpairs; + n->conf_msix_qsize = n->params.msix_qsize; + } + n->sq = g_new0(NvmeSQueue *, n->params.max_ioqpairs + 1); n->cq = g_new0(NvmeCQueue *, n->params.max_ioqpairs + 1); n->temperature = NVME_TEMPERATURE; n->features.temp_thresh_hi = NVME_TEMPERATURE_WARNING; n->starttime_ms = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); n->aer_reqs = g_new0(NvmeRequest *, n->params.aerl + 1); + QTAILQ_INIT(&n->aer_queue); + + list->numcntl = max_vfs; + for (i = 0; i < max_vfs; i++) { + sctrl = &list->sec[i]; + sctrl->pcid = cpu_to_le16(n->cntlid); + sctrl->vfn = cpu_to_le16(i + 1); + } + + cap->cntlid = cpu_to_le16(n->cntlid); + cap->crt = NVME_CRT_VQ | NVME_CRT_VI; + + if (pci_is_vf(pci)) { + cap->vqprt = cpu_to_le16(1 + n->conf_ioqpairs); + } else { + cap->vqprt = cpu_to_le16(1 + n->params.max_ioqpairs - + n->params.sriov_vq_flexible); + cap->vqfrt = cpu_to_le32(n->params.sriov_vq_flexible); + cap->vqrfap = cap->vqfrt; + cap->vqgran = cpu_to_le16(NVME_VF_RES_GRANULARITY); + cap->vqfrsm = n->params.sriov_max_vq_per_vf ? + cpu_to_le16(n->params.sriov_max_vq_per_vf) : + cap->vqfrt / MAX(max_vfs, 1); + } + + if (pci_is_vf(pci)) { + cap->viprt = cpu_to_le16(n->conf_msix_qsize); + } else { + cap->viprt = cpu_to_le16(n->params.msix_qsize - + n->params.sriov_vi_flexible); + cap->vifrt = cpu_to_le32(n->params.sriov_vi_flexible); + cap->virfap = cap->vifrt; + cap->vigran = cpu_to_le16(NVME_VF_RES_GRANULARITY); + cap->vifrsm = n->params.sriov_max_vi_per_vf ? + cpu_to_le16(n->params.sriov_max_vi_per_vf) : + cap->vifrt / MAX(max_vfs, 1); + } } static void nvme_init_cmb(NvmeCtrl *n, PCIDevice *pci_dev) @@ -6626,60 +8016,147 @@ static void nvme_init_pmr(NvmeCtrl *n, PCIDevice *pci_dev) memory_region_set_enabled(&n->pmr.dev->mr, false); } -static int nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) +static uint64_t nvme_mbar_size(unsigned total_queues, unsigned total_irqs, + unsigned *msix_table_offset, + unsigned *msix_pba_offset) { - uint8_t *pci_conf = pci_dev->config; - uint64_t bar_size, msix_table_size, msix_pba_size; - unsigned msix_table_offset, msix_pba_offset; + uint64_t bar_size, msix_table_size; + + bar_size = sizeof(NvmeBar) + 2 * total_queues * NVME_DB_SIZE; + + if (total_irqs == 0) { + goto out; + } + + bar_size = QEMU_ALIGN_UP(bar_size, 4 * KiB); + + if (msix_table_offset) { + *msix_table_offset = bar_size; + } + + msix_table_size = PCI_MSIX_ENTRY_SIZE * total_irqs; + bar_size += msix_table_size; + bar_size = QEMU_ALIGN_UP(bar_size, 4 * KiB); + + if (msix_pba_offset) { + *msix_pba_offset = bar_size; + } + + bar_size += QEMU_ALIGN_UP(total_irqs, 64) / 8; + +out: + return pow2ceil(bar_size); +} + +static void nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset) +{ + uint16_t vf_dev_id = n->params.use_intel_id ? + PCI_DEVICE_ID_INTEL_NVME : PCI_DEVICE_ID_REDHAT_NVME; + NvmePriCtrlCap *cap = &n->pri_ctrl_cap; + uint64_t bar_size = nvme_mbar_size(le16_to_cpu(cap->vqfrsm), + le16_to_cpu(cap->vifrsm), + NULL, NULL); + + pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id, + n->params.sriov_max_vfs, n->params.sriov_max_vfs, + NVME_VF_OFFSET, NVME_VF_STRIDE); + + pcie_sriov_pf_init_vf_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, bar_size); +} + +static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset) +{ + Error *err = NULL; int ret; - Error *err = NULL; + ret = pci_add_capability(pci_dev, PCI_CAP_ID_PM, offset, + PCI_PM_SIZEOF, &err); + if (err) { + error_report_err(err); + return ret; + } + + pci_set_word(pci_dev->config + offset + PCI_PM_PMC, + PCI_PM_CAP_VER_1_2); + pci_set_word(pci_dev->config + offset + PCI_PM_CTRL, + PCI_PM_CTRL_NO_SOFT_RESET); + pci_set_word(pci_dev->wmask + offset + PCI_PM_CTRL, + PCI_PM_CTRL_STATE_MASK); + + return 0; +} + +static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) +{ + ERRP_GUARD(); + uint8_t *pci_conf = pci_dev->config; + uint64_t bar_size; + unsigned msix_table_offset = 0, msix_pba_offset = 0; + int ret; pci_conf[PCI_INTERRUPT_PIN] = 1; pci_config_set_prog_interface(pci_conf, 0x2); if (n->params.use_intel_id) { pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); - pci_config_set_device_id(pci_conf, 0x5845); + pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_NVME); } else { pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REDHAT); pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_REDHAT_NVME); } pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_EXPRESS); + nvme_add_pm_capability(pci_dev, 0x60); pcie_endpoint_cap_init(pci_dev, 0x80); - - bar_size = QEMU_ALIGN_UP(n->reg_size, 4 * KiB); - msix_table_offset = bar_size; - msix_table_size = PCI_MSIX_ENTRY_SIZE * n->params.msix_qsize; - - bar_size += msix_table_size; - bar_size = QEMU_ALIGN_UP(bar_size, 4 * KiB); - msix_pba_offset = bar_size; - msix_pba_size = QEMU_ALIGN_UP(n->params.msix_qsize, 64) / 8; - - bar_size += msix_pba_size; - bar_size = pow2ceil(bar_size); - - memory_region_init(&n->bar0, OBJECT(n), "nvme-bar0", bar_size); - memory_region_init_io(&n->iomem, OBJECT(n), &nvme_mmio_ops, n, "nvme", - n->reg_size); - memory_region_add_subregion(&n->bar0, 0, &n->iomem); - - pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_TYPE_64, &n->bar0); - ret = msix_init(pci_dev, n->params.msix_qsize, - &n->bar0, 0, msix_table_offset, - &n->bar0, 0, msix_pba_offset, 0, &err); - if (ret < 0) { - if (ret == -ENOTSUP) { - warn_report_err(err); - } else { - error_propagate(errp, err); - return ret; - } + pcie_cap_flr_init(pci_dev); + if (n->params.sriov_max_vfs) { + pcie_ari_init(pci_dev, 0x100); } + if (n->params.msix_exclusive_bar && !pci_is_vf(pci_dev)) { + bar_size = nvme_mbar_size(n->params.max_ioqpairs + 1, 0, NULL, NULL); + memory_region_init_io(&n->iomem, OBJECT(n), &nvme_mmio_ops, n, "nvme", + bar_size); + pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, &n->iomem); + ret = msix_init_exclusive_bar(pci_dev, n->params.msix_qsize, 4, errp); + } else { + assert(n->params.msix_qsize >= 1); + + /* add one to max_ioqpairs to account for the admin queue pair */ + bar_size = nvme_mbar_size(n->params.max_ioqpairs + 1, + n->params.msix_qsize, &msix_table_offset, + &msix_pba_offset); + + memory_region_init(&n->bar0, OBJECT(n), "nvme-bar0", bar_size); + memory_region_init_io(&n->iomem, OBJECT(n), &nvme_mmio_ops, n, "nvme", + msix_table_offset); + memory_region_add_subregion(&n->bar0, 0, &n->iomem); + + if (pci_is_vf(pci_dev)) { + pcie_sriov_vf_register_bar(pci_dev, 0, &n->bar0); + } else { + pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, &n->bar0); + } + + ret = msix_init(pci_dev, n->params.msix_qsize, + &n->bar0, 0, msix_table_offset, + &n->bar0, 0, msix_pba_offset, 0, errp); + } + + if (ret == -ENOTSUP) { + /* report that msix is not supported, but do not error out */ + warn_report_err(*errp); + *errp = NULL; + } else if (ret < 0) { + /* propagate error to caller */ + return false; + } + + nvme_update_msixcap_ts(pci_dev, n->conf_msix_qsize); + if (n->params.cmb_size_mb) { nvme_init_cmb(n, pci_dev); } @@ -6688,7 +8165,11 @@ static int nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) nvme_init_pmr(n, pci_dev); } - return 0; + if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs) { + nvme_init_sriov(n, pci_dev, 0x120); + } + + return true; } static void nvme_init_subnqn(NvmeCtrl *n) @@ -6709,6 +8190,8 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) NvmeIdCtrl *id = &n->id_ctrl; uint8_t *pci_conf = pci_dev->config; uint64_t cap = ldq_le_p(&n->bar.cap); + NvmeSecCtrlEntry *sctrl = nvme_sctrl(n); + uint32_t ctratt; id->vid = cpu_to_le16(pci_get_word(pci_conf + PCI_VENDOR_ID)); id->ssvid = cpu_to_le16(pci_get_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID)); @@ -6719,7 +8202,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->cntlid = cpu_to_le16(n->cntlid); id->oaes = cpu_to_le32(NVME_OAES_NS_ATTR); - id->ctratt |= cpu_to_le32(NVME_CTRATT_ELBAS); + ctratt = NVME_CTRATT_ELBAS; id->rab = 6; @@ -6735,7 +8218,9 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->mdts = n->params.mdts; id->ver = cpu_to_le32(NVME_SPEC_VER); - id->oacs = cpu_to_le16(NVME_OACS_NS_MGMT | NVME_OACS_FORMAT); + id->oacs = + cpu_to_le16(NVME_OACS_NS_MGMT | NVME_OACS_FORMAT | NVME_OACS_DBBUF | + NVME_OACS_DIRECTIVES); id->cntrltype = 0x1; /* @@ -6758,8 +8243,8 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->wctemp = cpu_to_le16(NVME_TEMPERATURE_WARNING); id->cctemp = cpu_to_le16(NVME_TEMPERATURE_CRITICAL); - id->sqes = (0x6 << 4) | 0x6; - id->cqes = (0x4 << 4) | 0x4; + id->sqes = (NVME_SQES << 4) | NVME_SQES; + id->cqes = (NVME_CQES << 4) | NVME_CQES; id->nn = cpu_to_le32(NVME_MAX_NAMESPACES); id->oncs = cpu_to_le16(NVME_ONCS_WRITE_ZEROES | NVME_ONCS_TIMESTAMP | NVME_ONCS_FEATURES | NVME_ONCS_DSM | @@ -6775,8 +8260,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->vwc = NVME_VWC_NSID_BROADCAST_SUPPORT | NVME_VWC_PRESENT; id->ocfs = cpu_to_le16(NVME_OCFS_COPY_FORMAT_0 | NVME_OCFS_COPY_FORMAT_1); - id->sgls = cpu_to_le32(NVME_CTRL_SGLS_SUPPORT_NO_ALIGN | - NVME_CTRL_SGLS_BITBUCKET); + id->sgls = cpu_to_le32(NVME_CTRL_SGLS_SUPPORT_NO_ALIGN); nvme_init_subnqn(n); @@ -6786,8 +8270,17 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) if (n->subsys) { id->cmic |= NVME_CMIC_MULTI_CTRL; + ctratt |= NVME_CTRATT_ENDGRPS; + + id->endgidmax = cpu_to_le16(0x1); + + if (n->subsys->endgrp.fdp.enabled) { + ctratt |= NVME_CTRATT_FDPS; + } } + id->ctratt = cpu_to_le32(ctratt); + NVME_CAP_SET_MQES(cap, 0x7ff); NVME_CAP_SET_CQR(cap, 1); NVME_CAP_SET_TO(cap, 0xf); @@ -6801,6 +8294,10 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) stl_le_p(&n->bar.vs, NVME_SPEC_VER); n->bar.intmc = n->bar.intms = 0; + + if (pci_is_vf(pci_dev) && !sctrl->scs) { + stl_le_p(&n->bar.csts, NVME_CSTS_FAILED); + } } static int nvme_init_subsys(NvmeCtrl *n, Error **errp) @@ -6836,25 +8333,36 @@ void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns) static void nvme_realize(PCIDevice *pci_dev, Error **errp) { NvmeCtrl *n = NVME(pci_dev); + DeviceState *dev = DEVICE(pci_dev); NvmeNamespace *ns; - Error *local_err = NULL; + NvmeCtrl *pn = NVME(pcie_sriov_get_pf(pci_dev)); - nvme_check_constraints(n, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (pci_is_vf(pci_dev)) { + /* + * VFs derive settings from the parent. PF's lifespan exceeds + * that of VF's. + */ + memcpy(&n->params, &pn->params, sizeof(NvmeParams)); + + /* + * Set PF's serial value to a new string memory to prevent 'serial' + * property object release of PF when a VF is removed from the system. + */ + n->params.serial = g_strdup(pn->params.serial); + n->subsys = pn->subsys; + } + + if (!nvme_check_params(n, errp)) { return; } - qbus_init(&n->bus, sizeof(NvmeBus), TYPE_NVME_BUS, - &pci_dev->qdev, n->parent_obj.qdev.id); - - nvme_init_state(n); - if (nvme_init_pci(n, pci_dev, errp)) { - return; - } + qbus_init(&n->bus, sizeof(NvmeBus), TYPE_NVME_BUS, dev, dev->id); if (nvme_init_subsys(n, errp)) { - error_propagate(errp, local_err); + return; + } + nvme_init_state(n); + if (!nvme_init_pci(n, pci_dev, errp)) { return; } nvme_init_ctrl(n, pci_dev); @@ -6878,7 +8386,7 @@ static void nvme_exit(PCIDevice *pci_dev) NvmeNamespace *ns; int i; - nvme_ctrl_reset(n); + nvme_ctrl_reset(n, NVME_RESET_FUNCTION); if (n->subsys) { for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { @@ -6902,6 +8410,11 @@ static void nvme_exit(PCIDevice *pci_dev) if (n->pmr.dev) { host_memory_backend_set_mapped(n->pmr.dev, false); } + + if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs) { + pcie_sriov_pf_exit(pci_dev); + } + msix_uninit(pci_dev, &n->bar0, &n->bar0); memory_region_del_subregion(&n->bar0, &n->iomem); } @@ -6923,9 +8436,21 @@ static Property nvme_props[] = { DEFINE_PROP_UINT8("vsl", NvmeCtrl, params.vsl, 7), DEFINE_PROP_BOOL("use-intel-id", NvmeCtrl, params.use_intel_id, false), DEFINE_PROP_BOOL("legacy-cmb", NvmeCtrl, params.legacy_cmb, false), + DEFINE_PROP_BOOL("ioeventfd", NvmeCtrl, params.ioeventfd, false), DEFINE_PROP_UINT8("zoned.zasl", NvmeCtrl, params.zasl, 0), DEFINE_PROP_BOOL("zoned.auto_transition", NvmeCtrl, params.auto_transition_zones, true), + DEFINE_PROP_UINT8("sriov_max_vfs", NvmeCtrl, params.sriov_max_vfs, 0), + DEFINE_PROP_UINT16("sriov_vq_flexible", NvmeCtrl, + params.sriov_vq_flexible, 0), + DEFINE_PROP_UINT16("sriov_vi_flexible", NvmeCtrl, + params.sriov_vi_flexible, 0), + DEFINE_PROP_UINT8("sriov_max_vi_per_vf", NvmeCtrl, + params.sriov_max_vi_per_vf, 0), + DEFINE_PROP_UINT8("sriov_max_vq_per_vf", NvmeCtrl, + params.sriov_max_vq_per_vf, 0), + DEFINE_PROP_BOOL("msix-exclusive-bar", NvmeCtrl, params.msix_exclusive_bar, + false), DEFINE_PROP_END_OF_LIST(), }; @@ -6971,6 +8496,37 @@ static void nvme_set_smart_warning(Object *obj, Visitor *v, const char *name, } } +static void nvme_pci_reset(DeviceState *qdev) +{ + PCIDevice *pci_dev = PCI_DEVICE(qdev); + NvmeCtrl *n = NVME(pci_dev); + + trace_pci_nvme_pci_reset(); + nvme_ctrl_reset(n, NVME_RESET_FUNCTION); +} + +static void nvme_sriov_post_write_config(PCIDevice *dev, uint16_t old_num_vfs) +{ + NvmeCtrl *n = NVME(dev); + NvmeSecCtrlEntry *sctrl; + int i; + + for (i = pcie_sriov_num_vfs(dev); i < old_num_vfs; i++) { + sctrl = &n->sec_ctrl_list.sec[i]; + nvme_virt_set_state(n, le16_to_cpu(sctrl->scid), false); + } +} + +static void nvme_pci_write_config(PCIDevice *dev, uint32_t address, + uint32_t val, int len) +{ + uint16_t old_num_vfs = pcie_sriov_num_vfs(dev); + + pci_default_write_config(dev, address, val, len); + pcie_cap_flr_write_config(dev, address, val, len); + nvme_sriov_post_write_config(dev, old_num_vfs); +} + static const VMStateDescription nvme_vmstate = { .name = "nvme", .unmigratable = 1, @@ -6982,6 +8538,7 @@ static void nvme_class_init(ObjectClass *oc, void *data) PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); pc->realize = nvme_realize; + pc->config_write = nvme_pci_write_config; pc->exit = nvme_exit; pc->class_id = PCI_CLASS_STORAGE_EXPRESS; pc->revision = 2; @@ -6990,6 +8547,7 @@ static void nvme_class_init(ObjectClass *oc, void *data) dc->desc = "Non-Volatile Memory Express"; device_class_set_props(dc, nvme_props); dc->vmsd = &nvme_vmstate; + dc->reset = nvme_pci_reset; } static void nvme_instance_init(Object *obj) diff --git a/hw/nvme/dif.c b/hw/nvme/dif.c index 63c44c86ab..01b19c3373 100644 --- a/hw/nvme/dif.c +++ b/hw/nvme/dif.c @@ -115,7 +115,7 @@ static void nvme_dif_pract_generate_dif_crc64(NvmeNamespace *ns, uint8_t *buf, uint64_t crc = crc64_nvme(~0ULL, buf, ns->lbasz); if (pil) { - crc = crc64_nvme(crc, mbuf, pil); + crc = crc64_nvme(~crc, mbuf, pil); } dif->g64.guard = cpu_to_be64(crc); @@ -246,7 +246,7 @@ static uint16_t nvme_dif_prchk_crc64(NvmeNamespace *ns, NvmeDifTuple *dif, uint64_t crc = crc64_nvme(~0ULL, buf, ns->lbasz); if (pil) { - crc = crc64_nvme(crc, mbuf, pil); + crc = crc64_nvme(~crc, mbuf, pil); } trace_pci_nvme_dif_prchk_guard_crc64(be64_to_cpu(dif->g64.guard), crc); diff --git a/hw/nvme/meson.build b/hw/nvme/meson.build index 3cf40046ee..7d5caa53c2 100644 --- a/hw/nvme/meson.build +++ b/hw/nvme/meson.build @@ -1 +1 @@ -softmmu_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'subsys.c')) +system_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'subsys.c', 'nguid.c')) \ No newline at end of file diff --git a/hw/nvme/nguid.c b/hw/nvme/nguid.c new file mode 100644 index 0000000000..829832bd9f --- /dev/null +++ b/hw/nvme/nguid.c @@ -0,0 +1,187 @@ +/* + * QEMU NVMe NGUID functions + * + * Copyright 2024 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "qapi/visitor.h" +#include "qemu/ctype.h" +#include "nvme.h" + +#define NGUID_SEPARATOR '-' + +#define NGUID_VALUE_AUTO "auto" + +#define NGUID_FMT \ + "%02hhx%02hhx%02hhx%02hhx" \ + "%02hhx%02hhx%02hhx%02hhx" \ + "%02hhx%02hhx%02hhx%02hhx" \ + "%02hhx%02hhx%02hhx%02hhx" + +#define NGUID_STR_LEN (2 * NGUID_LEN + 1) + +bool nvme_nguid_is_null(const NvmeNGUID *nguid) +{ + static NvmeNGUID null_nguid; + return memcmp(nguid, &null_nguid, sizeof(NvmeNGUID)) == 0; +} + +static void nvme_nguid_generate(NvmeNGUID *out) +{ + int i; + uint32_t x; + + QEMU_BUILD_BUG_ON((NGUID_LEN % sizeof(x)) != 0); + + for (i = 0; i < NGUID_LEN; i += sizeof(x)) { + x = g_random_int(); + memcpy(&out->data[i], &x, sizeof(x)); + } +} + +/* + * The Linux Kernel typically prints the NGUID of an NVMe namespace using the + * same format as the UUID. For instance: + * + * $ cat /sys/class/block/nvme0n1/nguid + * e9accd3b-8390-4e13-167c-f0593437f57d + * + * When there is no UUID but there is NGUID the Kernel will print the NGUID as + * wwid and it won't use the UUID format: + * + * $ cat /sys/class/block/nvme0n1/wwid + * eui.e9accd3b83904e13167cf0593437f57d + * + * The NGUID has different fields compared to the UUID, so the grouping used in + * the UUID format has no relation with the 3 fields of the NGUID. + * + * This implementation won't expect a strict format as the UUID one and instead + * it will admit any string of hexadecimal digits. Byte groups could be created + * using the '-' separator. The number of bytes needs to be exactly 16 and the + * separator '-' has to be exactly in a byte boundary. The following are + * examples of accepted formats for the NGUID string: + * + * nguid="e9accd3b-8390-4e13-167c-f0593437f57d" + * nguid="e9accd3b83904e13167cf0593437f57d" + * nguid="FEDCBA9876543210-ABCDEF-0123456789" + */ +static bool nvme_nguid_is_valid(const char *str) +{ + int i; + int digit_count = 0; + + for (i = 0; i < strlen(str); i++) { + const char c = str[i]; + if (qemu_isxdigit(c)) { + digit_count++; + continue; + } + if (c == NGUID_SEPARATOR) { + /* + * We need to make sure the separator is in a byte boundary, the + * string does not start with the separator and they are not back to + * back "--". + */ + if ((i > 0) && (str[i - 1] != NGUID_SEPARATOR) && + (digit_count % 2) == 0) { + continue; + } + } + return false; + } + /* + * The string should have the correct byte length and not finish with the + * separator + */ + return (digit_count == (2 * NGUID_LEN)) && (str[i - 1] != NGUID_SEPARATOR); +} + +static int nvme_nguid_parse(const char *str, NvmeNGUID *nguid) +{ + uint8_t *id = &nguid->data[0]; + int ret = 0; + int i; + const char *ptr = str; + + if (!nvme_nguid_is_valid(str)) { + return -1; + } + + for (i = 0; i < NGUID_LEN; i++) { + ret = sscanf(ptr, "%02hhx", &id[i]); + if (ret != 1) { + return -1; + } + ptr += 2; + if (*ptr == NGUID_SEPARATOR) { + ptr++; + } + } + + return 0; +} + +/* + * When converted back to string this implementation will use a raw hex number + * with no separators, for instance: + * + * "e9accd3b83904e13167cf0593437f57d" + */ +static void nvme_nguid_stringify(const NvmeNGUID *nguid, char *out) +{ + const uint8_t *id = &nguid->data[0]; + snprintf(out, NGUID_STR_LEN, NGUID_FMT, + id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], + id[8], id[9], id[10], id[11], id[12], id[13], id[14], id[15]); +} + +static void get_nguid(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + NvmeNGUID *nguid = object_field_prop_ptr(obj, prop); + char buffer[NGUID_STR_LEN]; + char *p = buffer; + + nvme_nguid_stringify(nguid, buffer); + + visit_type_str(v, name, &p, errp); +} + +static void set_nguid(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + NvmeNGUID *nguid = object_field_prop_ptr(obj, prop); + char *str; + + if (!visit_type_str(v, name, &str, errp)) { + return; + } + + if (!strcmp(str, NGUID_VALUE_AUTO)) { + nvme_nguid_generate(nguid); + } else if (nvme_nguid_parse(str, nguid) < 0) { + error_set_from_qdev_prop_error(errp, EINVAL, obj, name, str); + } + g_free(str); +} + +const PropertyInfo qdev_prop_nguid = { + .name = "str", + .description = + "NGUID or \"" NGUID_VALUE_AUTO "\" for random value", + .get = get_nguid, + .set = set_nguid, +}; diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 1b9c9d1156..ea8db175db 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -14,8 +14,10 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/cutils.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "qemu/bitops.h" #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" @@ -87,6 +89,7 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp) id_ns->mcl = cpu_to_le32(ns->params.mcl); id_ns->msrc = ns->params.msrc; id_ns->eui64 = cpu_to_be64(ns->params.eui64); + memcpy(&id_ns->nguid, &ns->params.nguid.data, sizeof(id_ns->nguid)); ds = 31 - clz32(ns->blkconf.logical_block_size); ms = ns->params.ms; @@ -105,7 +108,7 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp) ns->pif = ns->params.pif; - static const NvmeLBAF lbaf[16] = { + static const NvmeLBAF defaults[16] = { [0] = { .ds = 9 }, [1] = { .ds = 9, .ms = 8 }, [2] = { .ds = 9, .ms = 16 }, @@ -118,7 +121,7 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp) ns->nlbaf = 8; - memcpy(&id_ns->lbaf, &lbaf, sizeof(lbaf)); + memcpy(&id_ns->lbaf, &defaults, sizeof(defaults)); for (i = 0; i < ns->nlbaf; i++) { NvmeLBAF *lbaf = &id_ns->lbaf[i]; @@ -377,6 +380,164 @@ static void nvme_zoned_ns_shutdown(NvmeNamespace *ns) assert(ns->nr_open_zones == 0); } +static NvmeRuHandle *nvme_find_ruh_by_attr(NvmeEnduranceGroup *endgrp, + uint8_t ruha, uint16_t *ruhid) +{ + for (uint16_t i = 0; i < endgrp->fdp.nruh; i++) { + NvmeRuHandle *ruh = &endgrp->fdp.ruhs[i]; + + if (ruh->ruha == ruha) { + *ruhid = i; + return ruh; + } + } + + return NULL; +} + +static bool nvme_ns_init_fdp(NvmeNamespace *ns, Error **errp) +{ + NvmeEnduranceGroup *endgrp = ns->endgrp; + NvmeRuHandle *ruh; + uint8_t lbafi = NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas); + g_autofree unsigned int *ruhids = NULL; + unsigned int n, m, *ruhid; + const char *endptr, *token; + char *r, *p; + uint16_t *ph; + + if (!ns->params.fdp.ruhs) { + ns->fdp.nphs = 1; + ph = ns->fdp.phs = g_new(uint16_t, 1); + + ruh = nvme_find_ruh_by_attr(endgrp, NVME_RUHA_CTRL, ph); + if (!ruh) { + ruh = nvme_find_ruh_by_attr(endgrp, NVME_RUHA_UNUSED, ph); + if (!ruh) { + error_setg(errp, "no unused reclaim unit handles left"); + return false; + } + + ruh->ruha = NVME_RUHA_CTRL; + ruh->lbafi = lbafi; + ruh->ruamw = endgrp->fdp.runs >> ns->lbaf.ds; + + for (uint16_t rg = 0; rg < endgrp->fdp.nrg; rg++) { + ruh->rus[rg].ruamw = ruh->ruamw; + } + } else if (ruh->lbafi != lbafi) { + error_setg(errp, "lba format index of controller assigned " + "reclaim unit handle does not match namespace lba " + "format index"); + return false; + } + + return true; + } + + ruhid = ruhids = g_new0(unsigned int, endgrp->fdp.nruh); + r = p = strdup(ns->params.fdp.ruhs); + + /* parse the placement handle identifiers */ + while ((token = qemu_strsep(&p, ";")) != NULL) { + if (qemu_strtoui(token, &endptr, 0, &n) < 0) { + error_setg(errp, "cannot parse reclaim unit handle identifier"); + free(r); + return false; + } + + m = n; + + /* parse range */ + if (*endptr == '-') { + token = endptr + 1; + + if (qemu_strtoui(token, NULL, 0, &m) < 0) { + error_setg(errp, "cannot parse reclaim unit handle identifier"); + free(r); + return false; + } + + if (m < n) { + error_setg(errp, "invalid reclaim unit handle identifier range"); + free(r); + return false; + } + } + + for (; n <= m; n++) { + if (ns->fdp.nphs++ == endgrp->fdp.nruh) { + error_setg(errp, "too many placement handles"); + free(r); + return false; + } + + *ruhid++ = n; + } + } + + free(r); + + /* verify that the ruhids are unique */ + for (unsigned int i = 0; i < ns->fdp.nphs; i++) { + for (unsigned int j = i + 1; j < ns->fdp.nphs; j++) { + if (ruhids[i] == ruhids[j]) { + error_setg(errp, "duplicate reclaim unit handle identifier: %u", + ruhids[i]); + return false; + } + } + } + + ph = ns->fdp.phs = g_new(uint16_t, ns->fdp.nphs); + + ruhid = ruhids; + + /* verify the identifiers */ + for (unsigned int i = 0; i < ns->fdp.nphs; i++, ruhid++, ph++) { + if (*ruhid >= endgrp->fdp.nruh) { + error_setg(errp, "invalid reclaim unit handle identifier"); + return false; + } + + ruh = &endgrp->fdp.ruhs[*ruhid]; + + switch (ruh->ruha) { + case NVME_RUHA_UNUSED: + ruh->ruha = NVME_RUHA_HOST; + ruh->lbafi = lbafi; + ruh->ruamw = endgrp->fdp.runs >> ns->lbaf.ds; + + for (uint16_t rg = 0; rg < endgrp->fdp.nrg; rg++) { + ruh->rus[rg].ruamw = ruh->ruamw; + } + + break; + + case NVME_RUHA_HOST: + if (ruh->lbafi != lbafi) { + error_setg(errp, "lba format index of host assigned" + "reclaim unit handle does not match namespace " + "lba format index"); + return false; + } + + break; + + case NVME_RUHA_CTRL: + error_setg(errp, "reclaim unit handle is controller assigned"); + return false; + + default: + abort(); + } + + *ph = *ruhid; + } + + return true; +} + static int nvme_ns_check_constraints(NvmeNamespace *ns, Error **errp) { unsigned int pi_size; @@ -417,6 +578,11 @@ static int nvme_ns_check_constraints(NvmeNamespace *ns, Error **errp) return -1; } + if (ns->params.zoned && ns->endgrp && ns->endgrp->fdp.enabled) { + error_setg(errp, "cannot be a zoned- in an FDP configuration"); + return -1; + } + if (ns->params.zoned) { if (ns->params.max_active_zones) { if (ns->params.max_open_zones > ns->params.max_active_zones) { @@ -502,6 +668,12 @@ int nvme_ns_setup(NvmeNamespace *ns, Error **errp) nvme_ns_init_zoned(ns); } + if (ns->endgrp && ns->endgrp->fdp.enabled) { + if (!nvme_ns_init_fdp(ns, errp)) { + return -1; + } + } + return 0; } @@ -525,6 +697,10 @@ void nvme_ns_cleanup(NvmeNamespace *ns) g_free(ns->zone_array); g_free(ns->zd_extensions); } + + if (ns->endgrp && ns->endgrp->fdp.enabled) { + g_free(ns->fdp.phs); + } } static void nvme_ns_unrealize(DeviceState *dev) @@ -546,6 +722,8 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) int i; if (!n->subsys) { + /* If no subsys, the ns cannot be attached to more than one ctrl. */ + ns->params.shared = false; if (ns->params.detached) { error_setg(errp, "detached requires that the nvme device is " "linked to an nvme-subsys device"); @@ -559,6 +737,8 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) if (!qdev_set_parent_bus(dev, &subsys->bus.parent_bus, errp)) { return; } + ns->subsys = subsys; + ns->endgrp = &subsys->endgrp; } if (nvme_ns_setup(ns, errp)) { @@ -589,6 +769,8 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) if (subsys) { subsys->namespaces[nsid] = ns; + ns->id_ns.endgid = cpu_to_le16(0x1); + if (ns->params.detached) { return; } @@ -597,13 +779,14 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) for (i = 0; i < ARRAY_SIZE(subsys->ctrls); i++) { NvmeCtrl *ctrl = subsys->ctrls[i]; - if (ctrl) { + if (ctrl && ctrl != SUBSYS_SLOT_RSVD) { nvme_attach_ns(ctrl, ns); } } return; } + } nvme_attach_ns(n, ns); @@ -615,6 +798,7 @@ static Property nvme_ns_props[] = { DEFINE_PROP_BOOL("shared", NvmeNamespace, params.shared, true), DEFINE_PROP_UINT32("nsid", NvmeNamespace, params.nsid, 0), DEFINE_PROP_UUID_NODEFAULT("uuid", NvmeNamespace, params.uuid), + DEFINE_PROP_NGUID_NODEFAULT("nguid", NvmeNamespace, params.nguid), DEFINE_PROP_UINT64("eui64", NvmeNamespace, params.eui64, 0), DEFINE_PROP_UINT16("ms", NvmeNamespace, params.ms, 0), DEFINE_PROP_UINT8("mset", NvmeNamespace, params.mset, 0), @@ -642,6 +826,7 @@ static Property nvme_ns_props[] = { DEFINE_PROP_SIZE("zoned.zrwafg", NvmeNamespace, params.zrwafg, -1), DEFINE_PROP_BOOL("eui64-default", NvmeNamespace, params.eui64_default, false), + DEFINE_PROP_STRING("fdp.ruhs", NvmeNamespace, params.fdp.ruhs), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index e41771604f..bed8191bd5 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -19,14 +19,23 @@ #define HW_NVME_NVME_H #include "qemu/uuid.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/block/block.h" #include "block/nvme.h" -#define NVME_MAX_CONTROLLERS 32 +#define NVME_MAX_CONTROLLERS 256 #define NVME_MAX_NAMESPACES 256 #define NVME_EUI64_DEFAULT ((uint64_t)0x5254000000000000) +#define NVME_FDP_MAX_EVENTS 63 +#define NVME_FDP_MAXPIDS 128 + +/* + * The controller only supports Submission and Completion Queue Entry Sizes of + * 64 and 16 bytes respectively. + */ +#define NVME_SQES 6 +#define NVME_CQES 4 QEMU_BUILD_BUG_ON(NVME_MAX_NAMESPACES > NVME_NSID_BROADCAST - 1); @@ -43,6 +52,50 @@ typedef struct NvmeBus { #define TYPE_NVME_SUBSYS "nvme-subsys" #define NVME_SUBSYS(obj) \ OBJECT_CHECK(NvmeSubsystem, (obj), TYPE_NVME_SUBSYS) +#define SUBSYS_SLOT_RSVD (void *)0xFFFF + +typedef struct NvmeReclaimUnit { + uint64_t ruamw; +} NvmeReclaimUnit; + +typedef struct NvmeRuHandle { + uint8_t ruht; + uint8_t ruha; + uint64_t event_filter; + uint8_t lbafi; + uint64_t ruamw; + + /* reclaim units indexed by reclaim group */ + NvmeReclaimUnit *rus; +} NvmeRuHandle; + +typedef struct NvmeFdpEventBuffer { + NvmeFdpEvent events[NVME_FDP_MAX_EVENTS]; + unsigned int nelems; + unsigned int start; + unsigned int next; +} NvmeFdpEventBuffer; + +typedef struct NvmeEnduranceGroup { + uint8_t event_conf; + + struct { + NvmeFdpEventBuffer host_events, ctrl_events; + + uint16_t nruh; + uint16_t nrg; + uint8_t rgif; + uint64_t runs; + + uint64_t hbmw; + uint64_t mbmw; + uint64_t mbe; + + bool enabled; + + NvmeRuHandle *ruhs; + } fdp; +} NvmeEnduranceGroup; typedef struct NvmeSubsystem { DeviceState parent_obj; @@ -50,11 +103,19 @@ typedef struct NvmeSubsystem { uint8_t subnqn[256]; char *serial; - NvmeCtrl *ctrls[NVME_MAX_CONTROLLERS]; - NvmeNamespace *namespaces[NVME_MAX_NAMESPACES + 1]; + NvmeCtrl *ctrls[NVME_MAX_CONTROLLERS]; + NvmeNamespace *namespaces[NVME_MAX_NAMESPACES + 1]; + NvmeEnduranceGroup endgrp; struct { char *nqn; + + struct { + bool enabled; + uint64_t runs; + uint16_t nruh; + uint32_t nrg; + } fdp; } params; } NvmeSubsystem; @@ -68,6 +129,10 @@ static inline NvmeCtrl *nvme_subsys_ctrl(NvmeSubsystem *subsys, return NULL; } + if (subsys->ctrls[cntlid] == SUBSYS_SLOT_RSVD) { + return NULL; + } + return subsys->ctrls[cntlid]; } @@ -91,13 +156,42 @@ typedef struct NvmeZone { QTAILQ_ENTRY(NvmeZone) entry; } NvmeZone; +#define FDP_EVT_MAX 0xff +#define NVME_FDP_MAX_NS_RUHS 32u +#define FDPVSS 0 + +static const uint8_t nvme_fdp_evf_shifts[FDP_EVT_MAX] = { + /* Host events */ + [FDP_EVT_RU_NOT_FULLY_WRITTEN] = 0, + [FDP_EVT_RU_ATL_EXCEEDED] = 1, + [FDP_EVT_CTRL_RESET_RUH] = 2, + [FDP_EVT_INVALID_PID] = 3, + /* CTRL events */ + [FDP_EVT_MEDIA_REALLOC] = 32, + [FDP_EVT_RUH_IMPLICIT_RU_CHANGE] = 33, +}; + +#define NGUID_LEN 16 + +typedef struct { + uint8_t data[NGUID_LEN]; +} NvmeNGUID; + +bool nvme_nguid_is_null(const NvmeNGUID *nguid); + +extern const PropertyInfo qdev_prop_nguid; + +#define DEFINE_PROP_NGUID_NODEFAULT(_name, _state, _field) \ + DEFINE_PROP(_name, _state, _field, qdev_prop_nguid, NvmeNGUID) + typedef struct NvmeNamespaceParams { - bool detached; - bool shared; - uint32_t nsid; - QemuUUID uuid; - uint64_t eui64; - bool eui64_default; + bool detached; + bool shared; + uint32_t nsid; + QemuUUID uuid; + NvmeNGUID nguid; + uint64_t eui64; + bool eui64_default; uint16_t ms; uint8_t mset; @@ -120,6 +214,10 @@ typedef struct NvmeNamespaceParams { uint32_t numzrwa; uint64_t zrwas; uint64_t zrwafg; + + struct { + char *ruhs; + } fdp; } NvmeNamespaceParams; typedef struct NvmeNamespace { @@ -162,10 +260,18 @@ typedef struct NvmeNamespace { int32_t nr_active_zones; NvmeNamespaceParams params; + NvmeSubsystem *subsys; + NvmeEnduranceGroup *endgrp; struct { uint32_t err_rec; } features; + + struct { + uint16_t nphs; + /* reclaim unit handle identifiers indexed by placement handle */ + uint16_t *phs; + } fdp; } NvmeNamespace; static inline uint32_t nvme_nsid(NvmeNamespace *ns) @@ -269,6 +375,12 @@ static inline void nvme_aor_dec_active(NvmeNamespace *ns) assert(ns->nr_active_zones >= 0); } +static inline void nvme_fdp_stat_inc(uint64_t *a, uint64_t b) +{ + uint64_t ret = *a + b; + *a = ret < *a ? UINT64_MAX : ret; +} + void nvme_ns_init_format(NvmeNamespace *ns); int nvme_ns_setup(NvmeNamespace *ns, Error **errp); void nvme_ns_drain(NvmeNamespace *ns); @@ -335,6 +447,10 @@ static inline const char *nvme_adm_opc_str(uint8_t opc) case NVME_ADM_CMD_GET_FEATURES: return "NVME_ADM_CMD_GET_FEATURES"; case NVME_ADM_CMD_ASYNC_EV_REQ: return "NVME_ADM_CMD_ASYNC_EV_REQ"; case NVME_ADM_CMD_NS_ATTACHMENT: return "NVME_ADM_CMD_NS_ATTACHMENT"; + case NVME_ADM_CMD_DIRECTIVE_SEND: return "NVME_ADM_CMD_DIRECTIVE_SEND"; + case NVME_ADM_CMD_VIRT_MNGMT: return "NVME_ADM_CMD_VIRT_MNGMT"; + case NVME_ADM_CMD_DIRECTIVE_RECV: return "NVME_ADM_CMD_DIRECTIVE_RECV"; + case NVME_ADM_CMD_DBBUF_CONFIG: return "NVME_ADM_CMD_DBBUF_CONFIG"; case NVME_ADM_CMD_FORMAT_NVM: return "NVME_ADM_CMD_FORMAT_NVM"; default: return "NVME_ADM_CMD_UNKNOWN"; } @@ -366,7 +482,11 @@ typedef struct NvmeSQueue { uint32_t tail; uint32_t size; uint64_t dma_addr; - QEMUTimer *timer; + uint64_t db_addr; + uint64_t ei_addr; + QEMUBH *bh; + EventNotifier notifier; + bool ioeventfd_enabled; NvmeRequest *io_req; QTAILQ_HEAD(, NvmeRequest) req_list; QTAILQ_HEAD(, NvmeRequest) out_req_list; @@ -383,7 +503,11 @@ typedef struct NvmeCQueue { uint32_t vector; uint32_t size; uint64_t dma_addr; - QEMUTimer *timer; + uint64_t db_addr; + uint64_t ei_addr; + QEMUBH *bh; + EventNotifier notifier; + bool ioeventfd_enabled; QTAILQ_HEAD(, NvmeSQueue) sq_list; QTAILQ_HEAD(, NvmeRequest) req_list; } NvmeCQueue; @@ -406,6 +530,13 @@ typedef struct NvmeParams { uint8_t zasl; bool auto_transition_zones; bool legacy_cmb; + bool ioeventfd; + uint8_t sriov_max_vfs; + uint16_t sriov_vq_flexible; + uint16_t sriov_vi_flexible; + uint8_t sriov_max_vq_per_vf; + uint8_t sriov_max_vi_per_vf; + bool msix_exclusive_bar; } NvmeParams; typedef struct NvmeCtrl { @@ -421,9 +552,6 @@ typedef struct NvmeCtrl { uint32_t page_size; uint16_t page_bits; uint16_t max_prp_ents; - uint16_t cqe_size; - uint16_t sqe_size; - uint32_t reg_size; uint32_t max_q_ents; uint8_t outstanding_aers; uint32_t irq_status; @@ -433,6 +561,11 @@ typedef struct NvmeCtrl { uint64_t starttime_ms; uint16_t temperature; uint8_t smart_critical_warning; + uint32_t conf_msix_qsize; + uint32_t conf_ioqpairs; + uint64_t dbbuf_dbs; + uint64_t dbbuf_eis; + bool dbbuf_enabled; struct { MemoryRegion mem; @@ -477,8 +610,20 @@ typedef struct NvmeCtrl { uint32_t async_config; NvmeHostBehaviorSupport hbs; } features; + + NvmePriCtrlCap pri_ctrl_cap; + NvmeSecCtrlList sec_ctrl_list; + struct { + uint16_t vqrfap; + uint16_t virfap; + } next_pri_ctrl_cap; /* These override pri_ctrl_cap after reset */ } NvmeCtrl; +typedef enum NvmeResetType { + NVME_RESET_FUNCTION = 0, + NVME_RESET_CONTROLLER = 1, +} NvmeResetType; + static inline NvmeNamespace *nvme_ns(NvmeCtrl *n, uint32_t nsid) { if (!nsid || nsid > NVME_MAX_NAMESPACES) { @@ -511,6 +656,33 @@ static inline uint16_t nvme_cid(NvmeRequest *req) return le16_to_cpu(req->cqe.cid); } +static inline NvmeSecCtrlEntry *nvme_sctrl(NvmeCtrl *n) +{ + PCIDevice *pci_dev = &n->parent_obj; + NvmeCtrl *pf = NVME(pcie_sriov_get_pf(pci_dev)); + + if (pci_is_vf(pci_dev)) { + return &pf->sec_ctrl_list.sec[pcie_sriov_vf_number(pci_dev)]; + } + + return NULL; +} + +static inline NvmeSecCtrlEntry *nvme_sctrl_for_cntlid(NvmeCtrl *n, + uint16_t cntlid) +{ + NvmeSecCtrlList *list = &n->sec_ctrl_list; + uint8_t i; + + for (i = 0; i < list->numcntl; i++) { + if (le16_to_cpu(list->sec[i].scid) == cntlid) { + return &list->sec[i]; + } + } + + return NULL; +} + void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns); uint16_t nvme_bounce_data(NvmeCtrl *n, void *ptr, uint32_t len, NvmeTxDirection dir, NvmeRequest *req); diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c index 691a90d209..d30bb8bfd5 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -7,24 +7,78 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "nvme.h" -int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp) +#define NVME_DEFAULT_RU_SIZE (96 * MiB) + +static int nvme_subsys_reserve_cntlids(NvmeCtrl *n, int start, int num) { NvmeSubsystem *subsys = n->subsys; - int cntlid, nsid; + NvmeSecCtrlList *list = &n->sec_ctrl_list; + NvmeSecCtrlEntry *sctrl; + int i, cnt = 0; - for (cntlid = 0; cntlid < ARRAY_SIZE(subsys->ctrls); cntlid++) { - if (!subsys->ctrls[cntlid]) { - break; + for (i = start; i < ARRAY_SIZE(subsys->ctrls) && cnt < num; i++) { + if (!subsys->ctrls[i]) { + sctrl = &list->sec[cnt]; + sctrl->scid = cpu_to_le16(i); + subsys->ctrls[i] = SUBSYS_SLOT_RSVD; + cnt++; } } - if (cntlid == ARRAY_SIZE(subsys->ctrls)) { - error_setg(errp, "no more free controller id"); - return -1; + return cnt; +} + +static void nvme_subsys_unreserve_cntlids(NvmeCtrl *n) +{ + NvmeSubsystem *subsys = n->subsys; + NvmeSecCtrlList *list = &n->sec_ctrl_list; + NvmeSecCtrlEntry *sctrl; + int i, cntlid; + + for (i = 0; i < n->params.sriov_max_vfs; i++) { + sctrl = &list->sec[i]; + cntlid = le16_to_cpu(sctrl->scid); + + if (cntlid) { + assert(subsys->ctrls[cntlid] == SUBSYS_SLOT_RSVD); + subsys->ctrls[cntlid] = NULL; + sctrl->scid = 0; + } + } +} + +int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp) +{ + NvmeSubsystem *subsys = n->subsys; + NvmeSecCtrlEntry *sctrl = nvme_sctrl(n); + int cntlid, nsid, num_rsvd, num_vfs = n->params.sriov_max_vfs; + + if (pci_is_vf(&n->parent_obj)) { + cntlid = le16_to_cpu(sctrl->scid); + } else { + for (cntlid = 0; cntlid < ARRAY_SIZE(subsys->ctrls); cntlid++) { + if (!subsys->ctrls[cntlid]) { + break; + } + } + + if (cntlid == ARRAY_SIZE(subsys->ctrls)) { + error_setg(errp, "no more free controller id"); + return -1; + } + + num_rsvd = nvme_subsys_reserve_cntlids(n, cntlid + 1, num_vfs); + if (num_rsvd != num_vfs) { + nvme_subsys_unreserve_cntlids(n); + error_setg(errp, + "no more free controller ids for secondary controllers"); + return -1; + } } if (!subsys->serial) { @@ -48,17 +102,107 @@ int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp) void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n) { - subsys->ctrls[n->cntlid] = NULL; + if (pci_is_vf(&n->parent_obj)) { + subsys->ctrls[n->cntlid] = SUBSYS_SLOT_RSVD; + } else { + subsys->ctrls[n->cntlid] = NULL; + nvme_subsys_unreserve_cntlids(n); + } + n->cntlid = -1; } -static void nvme_subsys_setup(NvmeSubsystem *subsys) +static bool nvme_calc_rgif(uint16_t nruh, uint16_t nrg, uint8_t *rgif) +{ + uint16_t val; + unsigned int i; + + if (unlikely(nrg == 1)) { + /* PIDRG_NORGI scenario, all of pid is used for PHID */ + *rgif = 0; + return true; + } + + val = nrg; + i = 0; + while (val) { + val >>= 1; + i++; + } + *rgif = i; + + /* ensure remaining bits suffice to represent number of phids in a RG */ + if (unlikely((UINT16_MAX >> i) < nruh)) { + *rgif = 0; + return false; + } + + return true; +} + +static bool nvme_subsys_setup_fdp(NvmeSubsystem *subsys, Error **errp) +{ + NvmeEnduranceGroup *endgrp = &subsys->endgrp; + + if (!subsys->params.fdp.runs) { + error_setg(errp, "fdp.runs must be non-zero"); + return false; + } + + endgrp->fdp.runs = subsys->params.fdp.runs; + + if (!subsys->params.fdp.nrg) { + error_setg(errp, "fdp.nrg must be non-zero"); + return false; + } + + endgrp->fdp.nrg = subsys->params.fdp.nrg; + + if (!subsys->params.fdp.nruh || + subsys->params.fdp.nruh > NVME_FDP_MAXPIDS) { + error_setg(errp, "fdp.nruh must be non-zero and less than %u", + NVME_FDP_MAXPIDS); + return false; + } + + endgrp->fdp.nruh = subsys->params.fdp.nruh; + + if (!nvme_calc_rgif(endgrp->fdp.nruh, endgrp->fdp.nrg, &endgrp->fdp.rgif)) { + error_setg(errp, + "cannot derive a valid rgif (nruh %"PRIu16" nrg %"PRIu32")", + endgrp->fdp.nruh, endgrp->fdp.nrg); + return false; + } + + endgrp->fdp.ruhs = g_new(NvmeRuHandle, endgrp->fdp.nruh); + + for (uint16_t ruhid = 0; ruhid < endgrp->fdp.nruh; ruhid++) { + endgrp->fdp.ruhs[ruhid] = (NvmeRuHandle) { + .ruht = NVME_RUHT_INITIALLY_ISOLATED, + .ruha = NVME_RUHA_UNUSED, + }; + + endgrp->fdp.ruhs[ruhid].rus = g_new(NvmeReclaimUnit, endgrp->fdp.nrg); + } + + endgrp->fdp.enabled = true; + + return true; +} + +static bool nvme_subsys_setup(NvmeSubsystem *subsys, Error **errp) { const char *nqn = subsys->params.nqn ? subsys->params.nqn : subsys->parent_obj.id; snprintf((char *)subsys->subnqn, sizeof(subsys->subnqn), "nqn.2019-08.org.qemu:%s", nqn); + + if (subsys->params.fdp.enabled && !nvme_subsys_setup_fdp(subsys, errp)) { + return false; + } + + return true; } static void nvme_subsys_realize(DeviceState *dev, Error **errp) @@ -67,11 +211,16 @@ static void nvme_subsys_realize(DeviceState *dev, Error **errp) qbus_init(&subsys->bus, sizeof(NvmeBus), TYPE_NVME_BUS, dev, dev->id); - nvme_subsys_setup(subsys); + nvme_subsys_setup(subsys, errp); } static Property nvme_subsystem_props[] = { DEFINE_PROP_STRING("nqn", NvmeSubsystem, params.nqn), + DEFINE_PROP_BOOL("fdp", NvmeSubsystem, params.fdp.enabled, false), + DEFINE_PROP_SIZE("fdp.runs", NvmeSubsystem, params.fdp.runs, + NVME_DEFAULT_RU_SIZE), + DEFINE_PROP_UINT32("fdp.nrg", NvmeSubsystem, params.fdp.nrg, 1), + DEFINE_PROP_UINT16("fdp.nruh", NvmeSubsystem, params.fdp.nruh, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/nvme/trace-events b/hw/nvme/trace-events index ff1b458969..3a67680c6a 100644 --- a/hw/nvme/trace-events +++ b/hw/nvme/trace-events @@ -3,6 +3,7 @@ pci_nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u" pci_nvme_irq_pin(void) "pulsing IRQ pin" pci_nvme_irq_masked(void) "IRQ is masked" pci_nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2=0x%"PRIx64"" +pci_nvme_dbbuf_config(uint64_t dbs_addr, uint64_t eis_addr) "dbs_addr=0x%"PRIx64" eis_addr=0x%"PRIx64"" pci_nvme_map_addr(uint64_t addr, uint64_t len) "addr 0x%"PRIx64" len %"PRIu64"" pci_nvme_map_addr_cmb(uint64_t addr, uint64_t len) "addr 0x%"PRIx64" len %"PRIu64"" pci_nvme_map_prp(uint64_t trans_len, uint32_t len, uint64_t prp1, uint64_t prp2, int num_prps) "trans_len %"PRIu64" len %"PRIu32" prp1 0x%"PRIx64" prp2 0x%"PRIx64" num_prps %d" @@ -56,6 +57,8 @@ pci_nvme_identify_ctrl(void) "identify controller" pci_nvme_identify_ctrl_csi(uint8_t csi) "identify controller, csi=0x%"PRIx8"" pci_nvme_identify_ns(uint32_t ns) "nsid %"PRIu32"" pci_nvme_identify_ctrl_list(uint8_t cns, uint16_t cntid) "cns 0x%"PRIx8" cntid %"PRIu16"" +pci_nvme_identify_pri_ctrl_cap(uint16_t cntlid) "identify primary controller capabilities cntlid=%"PRIu16"" +pci_nvme_identify_sec_ctrl_list(uint16_t cntlid, uint8_t numcntl) "identify secondary controller list cntlid=%"PRIu16" numcntl=%"PRIu8"" pci_nvme_identify_ns_csi(uint32_t ns, uint8_t csi) "nsid=%"PRIu32", csi=0x%"PRIx8"" pci_nvme_identify_nslist(uint32_t ns) "nsid %"PRIu32"" pci_nvme_identify_nslist_csi(uint16_t ns, uint8_t csi) "nsid=%"PRIu16", csi=0x%"PRIx8"" @@ -81,6 +84,8 @@ pci_nvme_enqueue_event_noqueue(int queued) "queued %d" pci_nvme_enqueue_event_masked(uint8_t typ) "type 0x%"PRIx8"" pci_nvme_no_outstanding_aers(void) "ignoring event; no outstanding AERs" pci_nvme_enqueue_req_completion(uint16_t cid, uint16_t cqid, uint32_t dw0, uint32_t dw1, uint16_t status) "cid %"PRIu16" cqid %"PRIu16" dw0 0x%"PRIx32" dw1 0x%"PRIx32" status 0x%"PRIx16"" +pci_nvme_update_cq_eventidx(uint16_t cqid, uint16_t new_eventidx) "cqid %"PRIu16" new_eventidx %"PRIu16"" +pci_nvme_update_sq_eventidx(uint16_t sqid, uint16_t new_eventidx) "sqid %"PRIu16" new_eventidx %"PRIu16"" pci_nvme_mmio_read(uint64_t addr, unsigned size) "addr 0x%"PRIx64" size %d" pci_nvme_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" pci_nvme_mmio_doorbell_cq(uint16_t cqid, uint16_t new_head) "cqid %"PRIu16" new_head %"PRIu16"" @@ -97,6 +102,8 @@ pci_nvme_mmio_start_success(void) "setting controller enable bit succeeded" pci_nvme_mmio_stopped(void) "cleared controller enable bit" pci_nvme_mmio_shutdown_set(void) "shutdown bit set" pci_nvme_mmio_shutdown_cleared(void) "shutdown bit cleared" +pci_nvme_update_cq_head(uint16_t cqid, uint16_t new_head) "cqid %"PRIu16" new_head %"PRIu16"" +pci_nvme_update_sq_tail(uint16_t sqid, uint16_t new_tail) "sqid %"PRIu16" new_tail %"PRIu16"" pci_nvme_open_zone(uint64_t slba, uint32_t zone_idx, int all) "open zone, slba=%"PRIu64", idx=%"PRIu32", all=%"PRIi32"" pci_nvme_close_zone(uint64_t slba, uint32_t zone_idx, int all) "close zone, slba=%"PRIu64", idx=%"PRIu32", all=%"PRIi32"" pci_nvme_finish_zone(uint64_t slba, uint32_t zone_idx, int all) "finish zone, slba=%"PRIu64", idx=%"PRIu32", all=%"PRIi32"" @@ -108,6 +115,9 @@ pci_nvme_zd_extension_set(uint32_t zone_idx) "set descriptor extension for zone_ pci_nvme_clear_ns_close(uint32_t state, uint64_t slba) "zone state=%"PRIu32", slba=%"PRIu64" transitioned to Closed state" pci_nvme_clear_ns_reset(uint32_t state, uint64_t slba) "zone state=%"PRIu32", slba=%"PRIu64" transitioned to Empty state" pci_nvme_zoned_zrwa_implicit_flush(uint64_t zslba, uint32_t nlb) "zslba 0x%"PRIx64" nlb %"PRIu32"" +pci_nvme_pci_reset(void) "PCI Function Level Reset" +pci_nvme_virt_mngmt(uint16_t cid, uint16_t act, uint16_t cntlid, const char* rt, uint16_t nr) "cid %"PRIu16", act=0x%"PRIx16", ctrlid=%"PRIu16" %s nr=%"PRIu16"" +pci_nvme_fdp_ruh_change(uint16_t rgid, uint16_t ruhid) "change RU on RUH rgid=%"PRIu16", ruhid=%"PRIu16"" # error conditions pci_nvme_err_mdts(size_t len) "len %zu" @@ -158,6 +168,7 @@ pci_nvme_err_invalid_create_cq_size(uint16_t size) "failed creating completion q pci_nvme_err_invalid_create_cq_addr(uint64_t addr) "failed creating completion queue, addr=0x%"PRIx64"" pci_nvme_err_invalid_create_cq_vector(uint16_t vector) "failed creating completion queue, vector=%"PRIu16"" pci_nvme_err_invalid_create_cq_qflags(uint16_t qflags) "failed creating completion queue, qflags=%"PRIu16"" +pci_nvme_err_invalid_create_cq_entry_size(uint8_t iosqes, uint8_t iocqes) "iosqes %"PRIu8" iocqes %"PRIu8"" pci_nvme_err_invalid_identify_cns(uint16_t cns) "identify, invalid cns=0x%"PRIx16"" pci_nvme_err_invalid_getfeat(int dw10) "invalid get features, dw10=0x%"PRIx32"" pci_nvme_err_invalid_setfeat(uint32_t dw10) "invalid set features, dw10=0x%"PRIx32"" @@ -177,7 +188,9 @@ pci_nvme_err_startfail_asqent_sz_zero(void) "nvme_start_ctrl failed because the pci_nvme_err_startfail_acqent_sz_zero(void) "nvme_start_ctrl failed because the admin completion queue size is zero" pci_nvme_err_startfail_zasl_too_small(uint32_t zasl, uint32_t pagesz) "nvme_start_ctrl failed because zone append size limit %"PRIu32" is too small, needs to be >= %"PRIu32"" pci_nvme_err_startfail(void) "setting controller enable bit failed" +pci_nvme_err_startfail_virt_state(uint16_t vq, uint16_t vi) "nvme_start_ctrl failed due to ctrl state: vi=%u vq=%u" pci_nvme_err_invalid_mgmt_action(uint8_t action) "action=0x%"PRIx8"" +pci_nvme_err_ignored_mmio_vf_offline(uint64_t addr, unsigned size) "addr 0x%"PRIx64" size %d" # undefined behavior pci_nvme_ub_mmiowr_misaligned32(uint64_t offset) "MMIO write not 32-bit aligned, offset=0x%"PRIx64"" diff --git a/hw/nvram/ds1225y.c b/hw/nvram/ds1225y.c index 3660a47c51..6d510dcc68 100644 --- a/hw/nvram/ds1225y.c +++ b/hw/nvram/ds1225y.c @@ -102,7 +102,7 @@ static const VMStateDescription vmstate_nvram = { .version_id = 0, .minimum_version_id = 0, .post_load = nvram_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VARRAY_UINT32(contents, NvRamState, chip_size, 0, vmstate_info_uint8, uint8_t), VMSTATE_END_OF_LIST() diff --git a/hw/nvram/eeprom93xx.c b/hw/nvram/eeprom93xx.c index a1b9c78844..a8fd60a8fb 100644 --- a/hw/nvram/eeprom93xx.c +++ b/hw/nvram/eeprom93xx.c @@ -131,7 +131,7 @@ static const VMStateDescription vmstate_eeprom = { .name = "eeprom", .version_id = EEPROM_VERSION, .minimum_version_id = OLD_EEPROM_VERSION, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(tick, eeprom_t), VMSTATE_UINT8(address, eeprom_t), VMSTATE_UINT8(command, eeprom_t), @@ -315,13 +315,13 @@ eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords) addrbits = 6; } - eeprom = (eeprom_t *)g_malloc0(sizeof(*eeprom) + nwords * 2); + eeprom = g_malloc0(sizeof(*eeprom) + nwords * 2); eeprom->size = nwords; eeprom->addrbits = addrbits; /* Output DO is tristate, read results in 1. */ eeprom->eedo = 1; logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords); - vmstate_register(VMSTATE_IF(dev), 0, &vmstate_eeprom, eeprom); + vmstate_register_any(VMSTATE_IF(dev), &vmstate_eeprom, eeprom); return eeprom; } diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index 01a3093600..3272068663 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -12,6 +12,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "hw/i2c/i2c.h" +#include "hw/nvram/eeprom_at24c.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "sysemu/block-backend.h" @@ -40,15 +41,25 @@ struct EEPROMState { uint16_t cur; /* total size in bytes */ uint32_t rsize; + /* + * address byte number + * for 24c01, 24c02 size <= 256 byte, use only 1 byte + * otherwise size > 256, use 2 byte + */ + uint8_t asize; + bool writable; /* cells changed since last START? */ bool changed; - /* during WRITE, # of address bytes transfered */ + /* during WRITE, # of address bytes transferred */ uint8_t haveaddr; uint8_t *mem; BlockBackend *blk; + + const uint8_t *init_rom; + uint32_t init_rom_size; }; static @@ -64,8 +75,8 @@ int at24c_eeprom_event(I2CSlave *s, enum i2c_event event) case I2C_START_RECV: DPRINTK("clear\n"); if (ee->blk && ee->changed) { - int len = blk_pwrite(ee->blk, 0, ee->mem, ee->rsize, 0); - if (len != ee->rsize) { + int ret = blk_pwrite(ee->blk, 0, ee->rsize, ee->mem, 0); + if (ret < 0) { ERR(TYPE_AT24C_EE " : failed to write backing file\n"); } @@ -75,6 +86,8 @@ int at24c_eeprom_event(I2CSlave *s, enum i2c_event event) break; case I2C_NACK: break; + default: + return -1; } return 0; } @@ -85,7 +98,11 @@ uint8_t at24c_eeprom_recv(I2CSlave *s) EEPROMState *ee = AT24C_EE(s); uint8_t ret; - if (ee->haveaddr == 1) { + /* + * If got the byte address but not completely with address size + * will return the invalid value + */ + if (ee->haveaddr > 0 && ee->haveaddr < ee->asize) { return 0xff; } @@ -102,11 +119,11 @@ int at24c_eeprom_send(I2CSlave *s, uint8_t data) { EEPROMState *ee = AT24C_EE(s); - if (ee->haveaddr < 2) { + if (ee->haveaddr < ee->asize) { ee->cur <<= 8; ee->cur |= data; ee->haveaddr++; - if (ee->haveaddr == 2) { + if (ee->haveaddr == ee->asize) { ee->cur %= ee->rsize; DPRINTK("Set pointer %04x\n", ee->cur); } @@ -126,10 +143,39 @@ int at24c_eeprom_send(I2CSlave *s, uint8_t data) return 0; } +I2CSlave *at24c_eeprom_init(I2CBus *bus, uint8_t address, uint32_t rom_size) +{ + return at24c_eeprom_init_rom(bus, address, rom_size, NULL, 0); +} + +I2CSlave *at24c_eeprom_init_rom(I2CBus *bus, uint8_t address, uint32_t rom_size, + const uint8_t *init_rom, uint32_t init_rom_size) +{ + EEPROMState *s; + + s = AT24C_EE(i2c_slave_new(TYPE_AT24C_EE, address)); + + qdev_prop_set_uint32(DEVICE(s), "rom-size", rom_size); + + /* TODO: Model init_rom with QOM properties. */ + s->init_rom = init_rom; + s->init_rom_size = init_rom_size; + + i2c_slave_realize_and_unref(I2C_SLAVE(s), bus, &error_abort); + + return I2C_SLAVE(s); +} + static void at24c_eeprom_realize(DeviceState *dev, Error **errp) { EEPROMState *ee = AT24C_EE(dev); + if (ee->init_rom_size > ee->rsize) { + error_setg(errp, "%s: init rom is larger than rom: %u > %u", + TYPE_AT24C_EE, ee->init_rom_size, ee->rsize); + return; + } + if (ee->blk) { int64_t len = blk_getlength(ee->blk); @@ -149,6 +195,33 @@ static void at24c_eeprom_realize(DeviceState *dev, Error **errp) } ee->mem = g_malloc0(ee->rsize); + memset(ee->mem, 0, ee->rsize); + + if (ee->init_rom) { + memcpy(ee->mem, ee->init_rom, MIN(ee->init_rom_size, ee->rsize)); + } + + if (ee->blk) { + int ret = blk_pread(ee->blk, 0, ee->rsize, ee->mem, 0); + + if (ret < 0) { + ERR(TYPE_AT24C_EE + " : Failed initial sync with backing file\n"); + } + DPRINTK("Reset read backing file\n"); + } + + /* + * If address size didn't define with property set + * value is 0 as default, setting it by Rom size detecting. + */ + if (ee->asize == 0) { + if (ee->rsize <= 256) { + ee->asize = 1; + } else { + ee->asize = 2; + } + } } static @@ -159,22 +232,11 @@ void at24c_eeprom_reset(DeviceState *state) ee->changed = false; ee->cur = 0; ee->haveaddr = 0; - - memset(ee->mem, 0, ee->rsize); - - if (ee->blk) { - int len = blk_pread(ee->blk, 0, ee->mem, ee->rsize); - - if (len != ee->rsize) { - ERR(TYPE_AT24C_EE - " : Failed initial sync with backing file\n"); - } - DPRINTK("Reset read backing file\n"); - } } static Property at24c_eeprom_props[] = { DEFINE_PROP_UINT32("rom-size", EEPROMState, rsize, 0), + DEFINE_PROP_UINT8("address-size", EEPROMState, asize, 0), DEFINE_PROP_BOOL("writable", EEPROMState, writable, true), DEFINE_PROP_DRIVE("drive", EEPROMState, blk), DEFINE_PROP_END_OF_LIST() diff --git a/hw/nvram/fw_cfg-acpi.c b/hw/nvram/fw_cfg-acpi.c new file mode 100644 index 0000000000..58cdcd3121 --- /dev/null +++ b/hw/nvram/fw_cfg-acpi.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Add fw_cfg device in DSDT + * + */ + +#include "qemu/osdep.h" +#include "hw/nvram/fw_cfg_acpi.h" +#include "hw/acpi/aml-build.h" + +void fw_cfg_acpi_dsdt_add(Aml *scope, const MemMapEntry *fw_cfg_memmap) +{ + Aml *dev = aml_device("FWCF"); + aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002"))); + /* device present, functioning, decoding, not shown in UI */ + aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); + aml_append(dev, aml_name_decl("_CCA", aml_int(1))); + + Aml *crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(fw_cfg_memmap->base, + fw_cfg_memmap->size, AML_READ_WRITE)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); +} diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index d605f3f45a..fc0263f349 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -27,6 +27,7 @@ #include "sysemu/sysemu.h" #include "sysemu/dma.h" #include "sysemu/reset.h" +#include "exec/address-spaces.h" #include "hw/boards.h" #include "hw/nvram/fw_cfg.h" #include "hw/qdev-properties.h" @@ -41,6 +42,7 @@ #include "qapi/error.h" #include "hw/acpi/aml-build.h" #include "hw/pci/pci_bus.h" +#include "hw/loader.h" #define FW_CFG_FILE_SLOTS_DFLT 0x20 @@ -200,7 +202,7 @@ static void fw_cfg_bootsplash(FWCfgState *s) } /* insert splash file if user configurated */ - if (current_machine->boot_config.has_splash) { + if (current_machine->boot_config.splash) { const char *boot_splash_filename = current_machine->boot_config.splash; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, boot_splash_filename); if (filename == NULL) { @@ -655,7 +657,7 @@ static int fw_cfg_acpi_mr_restore_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_fw_cfg_dma = { .name = "fw_cfg/dma", .needed = fw_cfg_dma_enabled, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(dma_addr, FWCfgState), VMSTATE_END_OF_LIST() }, @@ -667,7 +669,7 @@ static const VMStateDescription vmstate_fw_cfg_acpi_mr = { .minimum_version_id = 1, .needed = fw_cfg_acpi_mr_restore, .post_load = fw_cfg_acpi_mr_restore_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(table_mr_size, FWCfgState), VMSTATE_UINT64(linker_mr_size, FWCfgState), VMSTATE_UINT64(rsdp_mr_size, FWCfgState), @@ -679,13 +681,13 @@ static const VMStateDescription vmstate_fw_cfg = { .name = "fw_cfg", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(cur_entry, FWCfgState), VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1), VMSTATE_UINT32_V(cur_offset, FWCfgState, 2), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_fw_cfg_dma, &vmstate_fw_cfg_acpi_mr, NULL, @@ -876,7 +878,7 @@ static struct { /* * 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 + * In order to avoid the inconsistency in sizes save them separately and * migrate over in vmstate post_load(). */ static void fw_cfg_acpi_mr_save(FWCfgState *s, const char *filename, size_t len) @@ -1141,6 +1143,7 @@ FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase, SysBusDevice *sbd; FWCfgIoState *ios; FWCfgState *s; + MemoryRegion *iomem = get_system_io(); bool dma_requested = dma_iobase && dma_as; dev = qdev_new(TYPE_FW_CFG_IO); @@ -1154,7 +1157,7 @@ FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase, sbd = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(sbd, &error_fatal); ios = FW_CFG_IO(dev); - sysbus_add_io(sbd, iobase, &ios->comb_iomem); + memory_region_add_subregion(iomem, iobase, &ios->comb_iomem); s = FW_CFG(dev); @@ -1162,7 +1165,7 @@ FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase, /* 64 bits for the address field */ s->dma_as = dma_as; s->dma_addr = 0; - sysbus_add_io(sbd, dma_iobase, &s->dma_iomem); + memory_region_add_subregion(iomem, dma_iobase, &s->dma_iomem); } return s; @@ -1221,6 +1224,37 @@ FWCfgState *fw_cfg_find(void) return FW_CFG(object_resolve_path_type("", TYPE_FW_CFG, NULL)); } +void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key, + uint16_t data_key, const char *image_name, + bool try_decompress) +{ + size_t size = -1; + uint8_t *data; + + if (image_name == NULL) { + return; + } + + if (try_decompress) { + size = load_image_gzipped_buffer(image_name, + LOAD_IMAGE_MAX_GUNZIP_BYTES, &data); + } + + if (size == (size_t)-1) { + gchar *contents; + gsize length; + + if (!g_file_get_contents(image_name, &contents, &length, NULL)) { + error_report("failed to load \"%s\"", image_name); + exit(1); + } + size = length; + data = (uint8_t *)contents; + } + + fw_cfg_add_i32(fw_cfg, size_key, size); + fw_cfg_add_bytes(fw_cfg, data_key, data, size); +} static void fw_cfg_class_init(ObjectClass *klass, void *data) { diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c index 11f2d31cdb..fe9df9fa35 100644 --- a/hw/nvram/mac_nvram.c +++ b/hw/nvram/mac_nvram.c @@ -24,12 +24,16 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/nvram/chrp_nvram.h" -#include "hw/ppc/mac.h" +#include "hw/nvram/mac_nvram.h" #include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "sysemu/block-backend.h" #include "migration/vmstate.h" #include "qemu/cutils.h" #include "qemu/module.h" +#include "qemu/error-report.h" #include "trace.h" #include @@ -44,6 +48,12 @@ static void macio_nvram_writeb(void *opaque, hwaddr addr, addr = (addr >> s->it_shift) & (s->size - 1); trace_macio_nvram_write(addr, value); s->data[addr] = value; + if (s->blk) { + if (blk_pwrite(s->blk, addr, 1, &s->data[addr], 0) < 0) { + error_report("%s: write of NVRAM data to backing store failed", + blk_name(s->blk)); + } + } } static uint64_t macio_nvram_readb(void *opaque, hwaddr addr, @@ -73,7 +83,7 @@ static const VMStateDescription vmstate_macio_nvram = { .name = "macio_nvram", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VBUFFER_UINT32(data, MacIONVRAMState, 0, NULL, size), VMSTATE_END_OF_LIST() } @@ -91,6 +101,27 @@ static void macio_nvram_realizefn(DeviceState *dev, Error **errp) s->data = g_malloc0(s->size); + if (s->blk) { + int64_t len = blk_getlength(s->blk); + if (len < 0) { + error_setg_errno(errp, -len, + "could not get length of nvram backing image"); + return; + } else if (len != s->size) { + error_setg_errno(errp, -len, + "invalid size nvram backing image"); + return; + } + if (blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, + BLK_PERM_ALL, errp) < 0) { + return; + } + if (blk_pread(s->blk, 0, s->size, s->data, 0) < 0) { + error_setg(errp, "can't read-nvram contents"); + return; + } + } + memory_region_init_io(&s->mem, OBJECT(s), &macio_nvram_ops, s, "macio-nvram", s->size << s->it_shift); sysbus_init_mmio(d, &s->mem); @@ -106,6 +137,7 @@ static void macio_nvram_unrealizefn(DeviceState *dev) static Property macio_nvram_properties[] = { DEFINE_PROP_UINT32("size", MacIONVRAMState, size, 0), DEFINE_PROP_UINT32("it_shift", MacIONVRAMState, it_shift, 0), + DEFINE_PROP_DRIVE("drive", MacIONVRAMState, blk), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build index f5ee9f6b88..4996c72456 100644 --- a/hw/nvram/meson.build +++ b/hw/nvram/meson.build @@ -1,23 +1,20 @@ -if have_system or have_tools - # QOM interfaces must be available anytime QOM is used. - qom_ss.add(files('fw_cfg-interface.c')) -endif - -softmmu_ss.add(files('fw_cfg.c')) -softmmu_ss.add(when: 'CONFIG_CHRP_NVRAM', if_true: files('chrp_nvram.c')) -softmmu_ss.add(when: 'CONFIG_DS1225Y', if_true: files('ds1225y.c')) -softmmu_ss.add(when: 'CONFIG_NMC93XX_EEPROM', if_true: files('eeprom93xx.c')) -softmmu_ss.add(when: 'CONFIG_AT24C', if_true: files('eeprom_at24c.c')) -softmmu_ss.add(when: 'CONFIG_MAC_NVRAM', if_true: files('mac_nvram.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_otp.c')) -softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_nvm.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE_CRC', if_true: files('xlnx-efuse-crc.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE', if_true: files('xlnx-efuse.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE_VERSAL', if_true: files( +system_ss.add(files('fw_cfg-interface.c')) +system_ss.add(files('fw_cfg.c')) +system_ss.add(when: 'CONFIG_CHRP_NVRAM', if_true: files('chrp_nvram.c')) +system_ss.add(when: 'CONFIG_DS1225Y', if_true: files('ds1225y.c')) +system_ss.add(when: 'CONFIG_NMC93XX_EEPROM', if_true: files('eeprom93xx.c')) +system_ss.add(when: 'CONFIG_AT24C', if_true: files('eeprom_at24c.c')) +system_ss.add(when: 'CONFIG_MAC_NVRAM', if_true: files('mac_nvram.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_otp.c')) +system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_nvm.c')) +system_ss.add(when: 'CONFIG_XLNX_EFUSE_CRC', if_true: files('xlnx-efuse-crc.c')) +system_ss.add(when: 'CONFIG_XLNX_EFUSE', if_true: files('xlnx-efuse.c')) +system_ss.add(when: 'CONFIG_XLNX_EFUSE_VERSAL', if_true: files( 'xlnx-versal-efuse-cache.c', 'xlnx-versal-efuse-ctrl.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_EFUSE_ZYNQMP', if_true: files( +system_ss.add(when: 'CONFIG_XLNX_EFUSE_ZYNQMP', if_true: files( 'xlnx-zynqmp-efuse.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_BBRAM', if_true: files('xlnx-bbram.c')) +system_ss.add(when: 'CONFIG_XLNX_BBRAM', if_true: files('xlnx-bbram.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_nvram.c')) +specific_ss.add(when: 'CONFIG_ACPI', if_true: files('fw_cfg-acpi.c')) diff --git a/hw/nvram/npcm7xx_otp.c b/hw/nvram/npcm7xx_otp.c index c61f2fc1aa..f00ebfa931 100644 --- a/hw/nvram/npcm7xx_otp.c +++ b/hw/nvram/npcm7xx_otp.c @@ -384,7 +384,7 @@ static const VMStateDescription vmstate_npcm7xx_otp = { .name = "npcm7xx-otp", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, NPCM7xxOTPState, NPCM7XX_OTP_NR_REGS), VMSTATE_UINT8_ARRAY(array, NPCM7xxOTPState, NPCM7XX_OTP_ARRAY_BYTES), VMSTATE_END_OF_LIST(), diff --git a/hw/nvram/nrf51_nvm.c b/hw/nvram/nrf51_nvm.c index 7f1db8c423..73564f7e6e 100644 --- a/hw/nvram/nrf51_nvm.c +++ b/hw/nvram/nrf51_nvm.c @@ -336,12 +336,9 @@ static void nrf51_nvm_init(Object *obj) static void nrf51_nvm_realize(DeviceState *dev, Error **errp) { NRF51NVMState *s = NRF51_NVM(dev); - Error *err = NULL; - memory_region_init_rom_device(&s->flash, OBJECT(dev), &flash_ops, s, - "nrf51_soc.flash", s->flash_size, &err); - if (err) { - error_propagate(errp, err); + if (!memory_region_init_rom_device(&s->flash, OBJECT(dev), &flash_ops, s, + "nrf51_soc.flash", s->flash_size, errp)) { return; } @@ -366,7 +363,7 @@ static const VMStateDescription vmstate_nvm = { .name = "nrf51_soc.nvm", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(uicr_content, NRF51NVMState, NRF51_UICR_FIXTURE_SIZE), VMSTATE_UINT32(config, NRF51NVMState), diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 18b43be7f6..bfd8aa367e 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -103,7 +103,7 @@ static void rtas_nvram_store(PowerPCCPU *cpu, SpaprMachineState *spapr, { SpaprNvram *nvram = spapr->nvram; hwaddr offset, buffer, len; - int alen; + int ret; void *membuf; if ((nargs != 3) || (nret != 2)) { @@ -128,9 +128,9 @@ static void rtas_nvram_store(PowerPCCPU *cpu, SpaprMachineState *spapr, membuf = cpu_physical_memory_map(buffer, &len, false); - alen = len; + ret = 0; if (nvram->blk) { - alen = blk_pwrite(nvram->blk, offset, membuf, len, 0); + ret = blk_pwrite(nvram->blk, offset, len, membuf, 0); } assert(nvram->buf); @@ -138,8 +138,8 @@ static void rtas_nvram_store(PowerPCCPU *cpu, SpaprMachineState *spapr, cpu_physical_memory_unmap(membuf, len, 0, len); - rtas_st(rets, 0, (alen < len) ? RTAS_OUT_HW_ERROR : RTAS_OUT_SUCCESS); - rtas_st(rets, 1, (alen < 0) ? 0 : alen); + rtas_st(rets, 0, (ret < 0) ? RTAS_OUT_HW_ERROR : RTAS_OUT_SUCCESS); + rtas_st(rets, 1, (ret < 0) ? 0 : len); } static void spapr_nvram_realize(SpaprVioDevice *dev, Error **errp) @@ -179,9 +179,9 @@ static void spapr_nvram_realize(SpaprVioDevice *dev, Error **errp) } if (nvram->blk) { - int alen = blk_pread(nvram->blk, 0, nvram->buf, nvram->size); + ret = blk_pread(nvram->blk, 0, nvram->size, nvram->buf, 0); - if (alen != nvram->size) { + if (ret < 0) { error_setg(errp, "can't read spapr-nvram contents"); return; } @@ -224,7 +224,7 @@ static void postload_update_cb(void *opaque, bool running, RunState state) qemu_del_vm_change_state_handler(nvram->vmstate); nvram->vmstate = NULL; - blk_pwrite(nvram->blk, 0, nvram->buf, nvram->size, 0); + blk_pwrite(nvram->blk, 0, nvram->size, nvram->buf, 0); } static int spapr_nvram_post_load(void *opaque, int version_id) @@ -245,7 +245,7 @@ static const VMStateDescription vmstate_spapr_nvram = { .minimum_version_id = 1, .pre_load = spapr_nvram_pre_load, .post_load = spapr_nvram_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(size, SpaprNvram), VMSTATE_VBUFFER_ALLOC_UINT32(buf, SpaprNvram, 1, NULL, size), VMSTATE_END_OF_LIST() diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index 6ed32adad9..0a71a005c6 100644 --- a/hw/nvram/xlnx-bbram.c +++ b/hw/nvram/xlnx-bbram.c @@ -2,6 +2,7 @@ * QEMU model of the Xilinx BBRAM Battery Backed RAM * * Copyright (c) 2014-2021 Xilinx Inc. + * Copyright (c) 2023 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -124,7 +125,7 @@ static void bbram_bdrv_read(XlnxBBRam *s, Error **errp) blk_name(s->blk)); } - if (blk_pread(s->blk, 0, ram, nr) < 0) { + if (blk_pread(s->blk, 0, nr, ram, 0) < 0) { error_setg(errp, "%s: Failed to read %u bytes from BBRAM backstore.", blk_name(s->blk), nr); @@ -159,7 +160,7 @@ static void bbram_bdrv_sync(XlnxBBRam *s, uint64_t hwaddr) } offset = hwaddr - A_BBRAM_0; - rc = blk_pwrite(s->blk, offset, &le32, 4, 0); + rc = blk_pwrite(s->blk, offset, 4, &le32, 0); if (rc < 0) { bbram_bdrv_error(s, rc, g_strdup_printf("write to offset %u", offset)); } @@ -416,9 +417,9 @@ static RegisterAccessInfo bbram_ctrl_regs_info[] = { } }; -static void bbram_ctrl_reset(DeviceState *dev) +static void bbram_ctrl_reset_hold(Object *obj) { - XlnxBBRam *s = XLNX_BBRAM(dev); + XlnxBBRam *s = XLNX_BBRAM(obj); unsigned int i; for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { @@ -507,7 +508,7 @@ static const VMStateDescription vmstate_bbram_ctrl = { .name = TYPE_XLNX_BBRAM, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxBBRam, R_MAX), VMSTATE_END_OF_LIST(), } @@ -522,8 +523,9 @@ static Property bbram_ctrl_props[] = { static void bbram_ctrl_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); - dc->reset = bbram_ctrl_reset; + rc->phases.hold = bbram_ctrl_reset_hold; dc->realize = bbram_ctrl_realize; dc->vmsd = &vmstate_bbram_ctrl; device_class_set_props(dc, bbram_ctrl_props); diff --git a/hw/nvram/xlnx-efuse.c b/hw/nvram/xlnx-efuse.c index a0fd77b586..f7b849f7de 100644 --- a/hw/nvram/xlnx-efuse.c +++ b/hw/nvram/xlnx-efuse.c @@ -77,7 +77,7 @@ static int efuse_bdrv_read(XlnxEFuse *s, Error **errp) blk_name(s->blk)); } - if (blk_pread(s->blk, 0, ram, nr) < 0) { + if (blk_pread(s->blk, 0, nr, ram, 0) < 0) { error_setg(errp, "%s: Failed to read %u bytes from eFUSE backstore.", blk_name(s->blk), nr); return -1; @@ -105,7 +105,7 @@ static void efuse_bdrv_sync(XlnxEFuse *s, unsigned int bit) le32 = cpu_to_le32(xlnx_efuse_get_row(s, bit)); row_offset = (bit / 32) * 4; - if (blk_pwrite(s->blk, row_offset, &le32, 4, 0) < 0) { + if (blk_pwrite(s->blk, row_offset, 4, &le32, 0) < 0) { error_report("%s: Failed to write offset %u of eFUSE backstore.", blk_name(s->blk), row_offset); } @@ -143,6 +143,8 @@ static bool efuse_ro_bits_find(XlnxEFuse *s, uint32_t k) bool xlnx_efuse_set_bit(XlnxEFuse *s, unsigned int bit) { + uint32_t set, *row; + if (efuse_ro_bits_find(s, bit)) { g_autofree char *path = object_get_canonical_path(OBJECT(s)); @@ -152,8 +154,13 @@ bool xlnx_efuse_set_bit(XlnxEFuse *s, unsigned int bit) return false; } - s->fuse32[bit / 32] |= 1 << (bit % 32); - efuse_bdrv_sync(s, bit); + /* Avoid back-end write unless there is a real update */ + row = &s->fuse32[bit / 32]; + set = 1 << (bit % 32); + if (!(set & *row)) { + *row |= set; + efuse_bdrv_sync(s, bit); + } return true; } @@ -217,6 +224,13 @@ static void efuse_realize(DeviceState *dev, Error **errp) } } +static void efuse_finalize(Object *obj) +{ + XlnxEFuse *s = XLNX_EFUSE(obj); + + g_free(s->ro_bits); +} + static void efuse_prop_set_drive(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -273,6 +287,7 @@ static const TypeInfo efuse_info = { .name = TYPE_XLNX_EFUSE, .parent = TYPE_DEVICE, .instance_size = sizeof(XlnxEFuse), + .instance_finalize = efuse_finalize, .class_init = efuse_class_init, }; diff --git a/hw/nvram/xlnx-versal-efuse-ctrl.c b/hw/nvram/xlnx-versal-efuse-ctrl.c index b35ba65ab5..e4b9e11a3d 100644 --- a/hw/nvram/xlnx-versal-efuse-ctrl.c +++ b/hw/nvram/xlnx-versal-efuse-ctrl.c @@ -2,6 +2,7 @@ * QEMU model of the Versal eFuse controller * * Copyright (c) 2020 Xilinx Inc. + * Copyright (c) 2023 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -657,9 +658,9 @@ static void efuse_ctrl_register_reset(RegisterInfo *reg) register_reset(reg); } -static void efuse_ctrl_reset(DeviceState *dev) +static void efuse_ctrl_reset_hold(Object *obj) { - XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(dev); + XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(obj); unsigned int i; for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { @@ -725,11 +726,18 @@ static void efuse_ctrl_init(Object *obj) sysbus_init_irq(sbd, &s->irq_efuse_imr); } +static void efuse_ctrl_finalize(Object *obj) +{ + XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(obj); + + g_free(s->extra_pg0_lock_spec); +} + static const VMStateDescription vmstate_efuse_ctrl = { .name = TYPE_XLNX_VERSAL_EFUSE_CTRL, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxVersalEFuseCtrl, R_MAX), VMSTATE_END_OF_LIST(), } @@ -749,8 +757,9 @@ static Property efuse_ctrl_props[] = { static void efuse_ctrl_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); - dc->reset = efuse_ctrl_reset; + rc->phases.hold = efuse_ctrl_reset_hold; dc->realize = efuse_ctrl_realize; dc->vmsd = &vmstate_efuse_ctrl; device_class_set_props(dc, efuse_ctrl_props); @@ -762,6 +771,7 @@ static const TypeInfo efuse_ctrl_info = { .instance_size = sizeof(XlnxVersalEFuseCtrl), .class_init = efuse_ctrl_class_init, .instance_init = efuse_ctrl_init, + .instance_finalize = efuse_ctrl_finalize, }; static void efuse_ctrl_register_types(void) diff --git a/hw/nvram/xlnx-zynqmp-efuse.c b/hw/nvram/xlnx-zynqmp-efuse.c index 228ba0bbfa..ec98456e5d 100644 --- a/hw/nvram/xlnx-zynqmp-efuse.c +++ b/hw/nvram/xlnx-zynqmp-efuse.c @@ -2,6 +2,7 @@ * QEMU model of the ZynqMP eFuse * * Copyright (c) 2015 Xilinx Inc. + * Copyright (c) 2023 Advanced Micro Devices, Inc. * * Written by Edgar E. Iglesias * @@ -769,9 +770,9 @@ static void zynqmp_efuse_register_reset(RegisterInfo *reg) register_reset(reg); } -static void zynqmp_efuse_reset(DeviceState *dev) +static void zynqmp_efuse_reset_hold(Object *obj) { - XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(dev); + XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(obj); unsigned int i; for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { @@ -820,7 +821,7 @@ static const VMStateDescription vmstate_efuse = { .name = TYPE_XLNX_ZYNQMP_EFUSE, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPEFuse, R_MAX), VMSTATE_END_OF_LIST(), } @@ -837,8 +838,9 @@ static Property zynqmp_efuse_props[] = { static void zynqmp_efuse_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); - dc->reset = zynqmp_efuse_reset; + rc->phases.hold = zynqmp_efuse_reset_hold; dc->realize = zynqmp_efuse_realize; dc->vmsd = &vmstate_efuse; device_class_set_props(dc, zynqmp_efuse_props); diff --git a/hw/openrisc/Kconfig b/hw/openrisc/Kconfig index 8f284f3ba0..97af258b55 100644 --- a/hw/openrisc/Kconfig +++ b/hw/openrisc/Kconfig @@ -4,3 +4,15 @@ config OR1K_SIM select OPENCORES_ETH select OMPIC select SPLIT_IRQ + +config OR1K_VIRT + bool + imply PCI_DEVICES + imply VIRTIO_VGA + imply TEST_DEVICES + select PCI + select PCI_EXPRESS_GENERIC_BRIDGE + select GOLDFISH_RTC + select SERIAL + select SIFIVE_TEST + select VIRTIO_MMIO diff --git a/hw/openrisc/boot.c b/hw/openrisc/boot.c new file mode 100644 index 0000000000..55475aa6d6 --- /dev/null +++ b/hw/openrisc/boot.c @@ -0,0 +1,120 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * QEMU OpenRISC boot helpers. + * + * (c) 2022 Stafford Horne + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/cpu-defs.h" +#include "elf.h" +#include "hw/loader.h" +#include "hw/openrisc/boot.h" +#include "sysemu/device_tree.h" +#include "sysemu/qtest.h" +#include "sysemu/reset.h" +#include "qemu/error-report.h" + +#include + +#define KERNEL_LOAD_ADDR 0x100 + +hwaddr openrisc_load_kernel(ram_addr_t ram_size, + const char *kernel_filename, + uint32_t *bootstrap_pc) +{ + long kernel_size; + uint64_t elf_entry; + uint64_t high_addr; + hwaddr entry; + + if (kernel_filename && !qtest_enabled()) { + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, + &elf_entry, NULL, &high_addr, NULL, 1, + EM_OPENRISC, 1, 0); + entry = elf_entry; + if (kernel_size < 0) { + kernel_size = load_uimage(kernel_filename, + &entry, NULL, NULL, NULL, NULL); + high_addr = entry + kernel_size; + } + if (kernel_size < 0) { + kernel_size = load_image_targphys(kernel_filename, + KERNEL_LOAD_ADDR, + ram_size - KERNEL_LOAD_ADDR); + high_addr = KERNEL_LOAD_ADDR + kernel_size; + } + + if (entry <= 0) { + entry = KERNEL_LOAD_ADDR; + } + + if (kernel_size < 0) { + error_report("couldn't load the kernel '%s'", kernel_filename); + exit(1); + } + *bootstrap_pc = entry; + + return high_addr; + } + return 0; +} + +hwaddr openrisc_load_initrd(void *fdt, const char *filename, + hwaddr load_start, uint64_t mem_size) +{ + int size; + hwaddr start; + + /* We put the initrd right after the kernel; page aligned. */ + start = TARGET_PAGE_ALIGN(load_start); + + size = load_ramdisk(filename, start, mem_size - start); + if (size < 0) { + size = load_image_targphys(filename, start, mem_size - start); + if (size < 0) { + error_report("could not load ramdisk '%s'", filename); + exit(1); + } + } + + if (fdt) { + qemu_fdt_setprop_cell(fdt, "/chosen", + "linux,initrd-start", start); + qemu_fdt_setprop_cell(fdt, "/chosen", + "linux,initrd-end", start + size); + } + + return start + size; +} + +uint32_t openrisc_load_fdt(void *fdt, hwaddr load_start, + uint64_t mem_size) +{ + uint32_t fdt_addr; + int ret; + int fdtsize = fdt_totalsize(fdt); + + if (fdtsize <= 0) { + error_report("invalid device-tree"); + exit(1); + } + + /* We put fdt right after the kernel and/or initrd. */ + fdt_addr = TARGET_PAGE_ALIGN(load_start); + + ret = fdt_pack(fdt); + /* Should only fail if we've built a corrupted tree */ + g_assert(ret == 0); + /* copy in the device tree */ + qemu_fdt_dumpdtb(fdt, fdtsize); + + rom_add_blob_fixed_as("fdt", fdt, fdtsize, fdt_addr, + &address_space_memory); + qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, + rom_ptr_for_as(&address_space_memory, fdt_addr, fdtsize)); + + return fdt_addr; +} diff --git a/hw/openrisc/cputimer.c b/hw/openrisc/cputimer.c index 93268815d8..835986c4db 100644 --- a/hw/openrisc/cputimer.c +++ b/hw/openrisc/cputimer.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "migration/vmstate.h" #include "qemu/timer.h" +#include "sysemu/reset.h" #define TIMER_PERIOD 50 /* 50 ns period for 20 MHz timer */ @@ -122,11 +123,29 @@ static void openrisc_timer_cb(void *opaque) qemu_cpu_kick(CPU(cpu)); } +/* Reset the per CPU counter state. */ +static void openrisc_count_reset(void *opaque) +{ + OpenRISCCPU *cpu = opaque; + + if (cpu->env.is_counting) { + cpu_openrisc_count_stop(cpu); + } + cpu->env.ttmr = 0x00000000; +} + +/* Reset the global timer state. */ +static void openrisc_timer_reset(void *opaque) +{ + or1k_timer->ttcr = 0x00000000; + or1k_timer->last_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +} + static const VMStateDescription vmstate_or1k_timer = { .name = "or1k_timer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ttcr, OR1KTimerState), VMSTATE_UINT64(last_clk, OR1KTimerState), VMSTATE_END_OF_LIST() @@ -136,10 +155,11 @@ static const VMStateDescription vmstate_or1k_timer = { void cpu_openrisc_clock_init(OpenRISCCPU *cpu) { cpu->env.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &openrisc_timer_cb, cpu); - cpu->env.ttmr = 0x00000000; + qemu_register_reset(openrisc_count_reset, cpu); if (or1k_timer == NULL) { or1k_timer = g_new0(OR1KTimerState, 1); + qemu_register_reset(openrisc_timer_reset, cpu); vmstate_register(NULL, 0, &vmstate_or1k_timer, or1k_timer); } } diff --git a/hw/openrisc/meson.build b/hw/openrisc/meson.build index ec48172c9d..2dbc6365bb 100644 --- a/hw/openrisc/meson.build +++ b/hw/openrisc/meson.build @@ -1,5 +1,7 @@ openrisc_ss = ss.source_set() openrisc_ss.add(files('cputimer.c')) +openrisc_ss.add(files('boot.c')) openrisc_ss.add(when: 'CONFIG_OR1K_SIM', if_true: [files('openrisc_sim.c'), fdt]) +openrisc_ss.add(when: 'CONFIG_OR1K_VIRT', if_true: [files('virt.c'), fdt]) hw_arch += {'openrisc': openrisc_ss} diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index 35adce17ac..bffd6f721f 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -24,10 +24,9 @@ #include "cpu.h" #include "hw/irq.h" #include "hw/boards.h" -#include "elf.h" #include "hw/char/serial.h" #include "net/net.h" -#include "hw/loader.h" +#include "hw/openrisc/boot.h" #include "hw/qdev-properties.h" #include "exec/address-spaces.h" #include "sysemu/device_tree.h" @@ -171,7 +170,7 @@ static void openrisc_create_fdt(Or1ksimState *state, static void openrisc_sim_net_init(Or1ksimState *state, hwaddr base, hwaddr size, int num_cpus, OpenRISCCPU *cpus[], - int irq_pin, NICInfo *nd) + int irq_pin) { void *fdt = state->fdt; DeviceState *dev; @@ -179,8 +178,10 @@ static void openrisc_sim_net_init(Or1ksimState *state, hwaddr base, hwaddr size, char *nodename; int i; - dev = qdev_new("open_eth"); - qdev_set_nic_properties(dev, nd); + dev = qemu_create_nic_device("open_eth", true, NULL); + if (!dev) { + return; + } s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); @@ -283,101 +284,6 @@ static void openrisc_sim_serial_init(Or1ksimState *state, hwaddr base, g_free(nodename); } -static hwaddr openrisc_load_kernel(ram_addr_t ram_size, - const char *kernel_filename) -{ - long kernel_size; - uint64_t elf_entry; - uint64_t high_addr; - hwaddr entry; - - if (kernel_filename && !qtest_enabled()) { - kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &elf_entry, NULL, &high_addr, NULL, 1, - EM_OPENRISC, 1, 0); - entry = elf_entry; - if (kernel_size < 0) { - kernel_size = load_uimage(kernel_filename, - &entry, NULL, NULL, NULL, NULL); - high_addr = entry + kernel_size; - } - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, - KERNEL_LOAD_ADDR, - ram_size - KERNEL_LOAD_ADDR); - high_addr = KERNEL_LOAD_ADDR + kernel_size; - } - - if (entry <= 0) { - entry = KERNEL_LOAD_ADDR; - } - - if (kernel_size < 0) { - error_report("couldn't load the kernel '%s'", kernel_filename); - exit(1); - } - boot_info.bootstrap_pc = entry; - - return high_addr; - } - return 0; -} - -static hwaddr openrisc_load_initrd(Or1ksimState *state, const char *filename, - hwaddr load_start, uint64_t mem_size) -{ - void *fdt = state->fdt; - int size; - hwaddr start; - - /* We put the initrd right after the kernel; page aligned. */ - start = TARGET_PAGE_ALIGN(load_start); - - size = load_ramdisk(filename, start, mem_size - start); - if (size < 0) { - size = load_image_targphys(filename, start, mem_size - start); - if (size < 0) { - error_report("could not load ramdisk '%s'", filename); - exit(1); - } - } - - qemu_fdt_setprop_cell(fdt, "/chosen", - "linux,initrd-start", start); - qemu_fdt_setprop_cell(fdt, "/chosen", - "linux,initrd-end", start + size); - - return start + size; -} - -static uint32_t openrisc_load_fdt(Or1ksimState *state, hwaddr load_start, - uint64_t mem_size) -{ - void *fdt = state->fdt; - uint32_t fdt_addr; - int ret; - int fdtsize = fdt_totalsize(fdt); - - if (fdtsize <= 0) { - error_report("invalid device-tree"); - exit(1); - } - - /* We put fdt right after the kernel and/or initrd. */ - fdt_addr = TARGET_PAGE_ALIGN(load_start); - - ret = fdt_pack(fdt); - /* Should only fail if we've built a corrupted tree */ - g_assert(ret == 0); - /* copy in the device tree */ - qemu_fdt_dumpdtb(fdt, fdtsize); - - rom_add_blob_fixed_as("fdt", fdt, fdtsize, fdt_addr, - &address_space_memory); - - return fdt_addr; -} - static void openrisc_sim_init(MachineState *machine) { ram_addr_t ram_size = machine->ram_size; @@ -409,12 +315,10 @@ static void openrisc_sim_init(MachineState *machine) openrisc_create_fdt(state, or1ksim_memmap, smp_cpus, machine->ram_size, machine->kernel_cmdline); - if (nd_table[0].used) { - openrisc_sim_net_init(state, or1ksim_memmap[OR1KSIM_ETHOC].base, - or1ksim_memmap[OR1KSIM_ETHOC].size, - smp_cpus, cpus, - OR1KSIM_ETHOC_IRQ, nd_table); - } + openrisc_sim_net_init(state, or1ksim_memmap[OR1KSIM_ETHOC].base, + or1ksim_memmap[OR1KSIM_ETHOC].size, + smp_cpus, cpus, + OR1KSIM_ETHOC_IRQ); if (smp_cpus > 1) { openrisc_sim_ompic_init(state, or1ksim_memmap[OR1KSIM_OMPIC].base, @@ -428,13 +332,15 @@ static void openrisc_sim_init(MachineState *machine) or1ksim_memmap[OR1KSIM_UART].size, smp_cpus, cpus, OR1KSIM_UART_IRQ, n); - load_addr = openrisc_load_kernel(ram_size, kernel_filename); + load_addr = openrisc_load_kernel(ram_size, kernel_filename, + &boot_info.bootstrap_pc); if (load_addr > 0) { if (machine->initrd_filename) { - load_addr = openrisc_load_initrd(state, machine->initrd_filename, + load_addr = openrisc_load_initrd(state->fdt, + machine->initrd_filename, load_addr, machine->ram_size); } - boot_info.fdt_addr = openrisc_load_fdt(state, load_addr, + boot_info.fdt_addr = openrisc_load_fdt(state->fdt, load_addr, machine->ram_size); } } diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c new file mode 100644 index 0000000000..f8a68a6a6b --- /dev/null +++ b/hw/openrisc/virt.c @@ -0,0 +1,571 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * OpenRISC QEMU virtual machine. + * + * (c) 2022 Stafford Horne + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qapi/error.h" +#include "cpu.h" +#include "exec/address-spaces.h" +#include "hw/irq.h" +#include "hw/boards.h" +#include "hw/char/serial.h" +#include "hw/core/split-irq.h" +#include "hw/openrisc/boot.h" +#include "hw/misc/sifive_test.h" +#include "hw/pci/pci.h" +#include "hw/pci-host/gpex.h" +#include "hw/qdev-properties.h" +#include "hw/rtc/goldfish_rtc.h" +#include "hw/sysbus.h" +#include "hw/virtio/virtio-mmio.h" +#include "sysemu/device_tree.h" +#include "sysemu/sysemu.h" +#include "sysemu/qtest.h" +#include "sysemu/reset.h" + +#include + +#define VIRT_CPUS_MAX 4 +#define VIRT_CLK_MHZ 20000000 + +#define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt") +#define VIRT_MACHINE(obj) \ + OBJECT_CHECK(OR1KVirtState, (obj), TYPE_VIRT_MACHINE) + +typedef struct OR1KVirtState { + /*< private >*/ + MachineState parent_obj; + + /*< public >*/ + void *fdt; + int fdt_size; + +} OR1KVirtState; + +enum { + VIRT_DRAM, + VIRT_ECAM, + VIRT_MMIO, + VIRT_PIO, + VIRT_TEST, + VIRT_RTC, + VIRT_VIRTIO, + VIRT_UART, + VIRT_OMPIC, +}; + +enum { + VIRT_OMPIC_IRQ = 1, + VIRT_UART_IRQ = 2, + VIRT_RTC_IRQ = 3, + VIRT_VIRTIO_IRQ = 4, /* to 12 */ + VIRTIO_COUNT = 8, + VIRT_PCI_IRQ_BASE = 13, /* to 17 */ +}; + +static const struct MemmapEntry { + hwaddr base; + hwaddr size; +} virt_memmap[] = { + [VIRT_DRAM] = { 0x00000000, 0 }, + [VIRT_UART] = { 0x90000000, 0x100 }, + [VIRT_TEST] = { 0x96000000, 0x8 }, + [VIRT_RTC] = { 0x96005000, 0x1000 }, + [VIRT_VIRTIO] = { 0x97000000, 0x1000 }, + [VIRT_OMPIC] = { 0x98000000, VIRT_CPUS_MAX * 8 }, + [VIRT_ECAM] = { 0x9e000000, 0x1000000 }, + [VIRT_PIO] = { 0x9f000000, 0x1000000 }, + [VIRT_MMIO] = { 0xa0000000, 0x10000000 }, +}; + +static struct openrisc_boot_info { + uint32_t bootstrap_pc; + uint32_t fdt_addr; +} boot_info; + +static void main_cpu_reset(void *opaque) +{ + OpenRISCCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + + cpu_reset(CPU(cpu)); + + cpu_set_pc(cs, boot_info.bootstrap_pc); + cpu_set_gpr(&cpu->env, 3, boot_info.fdt_addr); +} + +static qemu_irq get_cpu_irq(OpenRISCCPU *cpus[], int cpunum, int irq_pin) +{ + return qdev_get_gpio_in_named(DEVICE(cpus[cpunum]), "IRQ", irq_pin); +} + +static qemu_irq get_per_cpu_irq(OpenRISCCPU *cpus[], int num_cpus, int irq_pin) +{ + int i; + + if (num_cpus > 1) { + DeviceState *splitter = qdev_new(TYPE_SPLIT_IRQ); + qdev_prop_set_uint32(splitter, "num-lines", num_cpus); + qdev_realize_and_unref(splitter, NULL, &error_fatal); + for (i = 0; i < num_cpus; i++) { + qdev_connect_gpio_out(splitter, i, get_cpu_irq(cpus, i, irq_pin)); + } + return qdev_get_gpio_in(splitter, 0); + } else { + return get_cpu_irq(cpus, 0, irq_pin); + } +} + +static void openrisc_create_fdt(OR1KVirtState *state, + const struct MemmapEntry *memmap, + int num_cpus, uint64_t mem_size, + const char *cmdline, + int32_t *pic_phandle) +{ + void *fdt; + int cpu; + char *nodename; + uint8_t rng_seed[32]; + + fdt = state->fdt = create_device_tree(&state->fdt_size); + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + qemu_fdt_setprop_string(fdt, "/", "compatible", "opencores,or1ksim"); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x1); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1); + + qemu_fdt_add_subnode(fdt, "/soc"); + qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus"); + qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x1); + qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x1); + + nodename = g_strdup_printf("/memory@%" HWADDR_PRIx, + memmap[VIRT_DRAM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + memmap[VIRT_DRAM].base, mem_size); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); + g_free(nodename); + + qemu_fdt_add_subnode(fdt, "/cpus"); + qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); + qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); + + for (cpu = 0; cpu < num_cpus; cpu++) { + nodename = g_strdup_printf("/cpus/cpu@%d", cpu); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "opencores,or1200-rtlsvn481"); + qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", + VIRT_CLK_MHZ); + g_free(nodename); + } + + nodename = (char *)"/pic"; + qemu_fdt_add_subnode(fdt, nodename); + *pic_phandle = qemu_fdt_alloc_phandle(fdt); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "opencores,or1k-pic-level"); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1); + qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", *pic_phandle); + + qemu_fdt_setprop_cell(fdt, "/", "interrupt-parent", *pic_phandle); + + qemu_fdt_add_subnode(fdt, "/chosen"); + if (cmdline) { + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + } + + /* Pass seed to RNG. */ + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); + + /* Create aliases node for use by devices. */ + qemu_fdt_add_subnode(fdt, "/aliases"); +} + +static void openrisc_virt_ompic_init(OR1KVirtState *state, hwaddr base, + hwaddr size, int num_cpus, + OpenRISCCPU *cpus[], int irq_pin) +{ + void *fdt = state->fdt; + DeviceState *dev; + SysBusDevice *s; + char *nodename; + int i; + + dev = qdev_new("or1k-ompic"); + qdev_prop_set_uint32(dev, "num-cpus", num_cpus); + + s = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(s, &error_fatal); + for (i = 0; i < num_cpus; i++) { + sysbus_connect_irq(s, i, get_cpu_irq(cpus, i, irq_pin)); + } + sysbus_mmio_map(s, 0, base); + + /* Add device tree node for ompic. */ + nodename = g_strdup_printf("/ompic@%" HWADDR_PRIx, base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "openrisc,ompic"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", base, size); + qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 0); + qemu_fdt_setprop_cell(fdt, nodename, "interrupts", irq_pin); + g_free(nodename); +} + +static void openrisc_virt_serial_init(OR1KVirtState *state, hwaddr base, + hwaddr size, int num_cpus, + OpenRISCCPU *cpus[], int irq_pin) +{ + void *fdt = state->fdt; + char *nodename; + qemu_irq serial_irq = get_per_cpu_irq(cpus, num_cpus, irq_pin); + + serial_mm_init(get_system_memory(), base, 0, serial_irq, 115200, + serial_hd(0), DEVICE_NATIVE_ENDIAN); + + /* Add device tree node for serial. */ + nodename = g_strdup_printf("/serial@%" HWADDR_PRIx, base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "ns16550a"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", base, size); + qemu_fdt_setprop_cell(fdt, nodename, "interrupts", irq_pin); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", VIRT_CLK_MHZ); + qemu_fdt_setprop(fdt, nodename, "big-endian", NULL, 0); + + /* The /chosen node is created during fdt creation. */ + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_setprop_string(fdt, "/aliases", "uart0", nodename); + g_free(nodename); +} + +static void openrisc_virt_test_init(OR1KVirtState *state, hwaddr base, + hwaddr size) +{ + void *fdt = state->fdt; + int test_ph; + char *nodename; + + /* SiFive Test MMIO device */ + sifive_test_create(base); + + /* SiFive Test MMIO Reset device FDT */ + nodename = g_strdup_printf("/soc/test@%" HWADDR_PRIx, base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "syscon"); + test_ph = qemu_fdt_alloc_phandle(fdt); + qemu_fdt_setprop_cells(fdt, nodename, "reg", base, size); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", test_ph); + qemu_fdt_setprop(fdt, nodename, "big-endian", NULL, 0); + g_free(nodename); + + nodename = g_strdup_printf("/soc/reboot"); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "syscon-reboot"); + qemu_fdt_setprop_cell(fdt, nodename, "regmap", test_ph); + qemu_fdt_setprop_cell(fdt, nodename, "offset", 0x0); + qemu_fdt_setprop_cell(fdt, nodename, "value", FINISHER_RESET); + g_free(nodename); + + nodename = g_strdup_printf("/soc/poweroff"); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "syscon-poweroff"); + qemu_fdt_setprop_cell(fdt, nodename, "regmap", test_ph); + qemu_fdt_setprop_cell(fdt, nodename, "offset", 0x0); + qemu_fdt_setprop_cell(fdt, nodename, "value", FINISHER_PASS); + g_free(nodename); + +} + +static void openrisc_virt_rtc_init(OR1KVirtState *state, hwaddr base, + hwaddr size, int num_cpus, + OpenRISCCPU *cpus[], int irq_pin) +{ + void *fdt = state->fdt; + char *nodename; + qemu_irq rtc_irq = get_per_cpu_irq(cpus, num_cpus, irq_pin); + + /* Goldfish RTC */ + sysbus_create_simple(TYPE_GOLDFISH_RTC, base, rtc_irq); + + /* Goldfish RTC FDT */ + nodename = g_strdup_printf("/soc/rtc@%" HWADDR_PRIx, base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "google,goldfish-rtc"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", base, size); + qemu_fdt_setprop_cell(fdt, nodename, "interrupts", irq_pin); + g_free(nodename); + +} + +static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base, + uint32_t irqchip_phandle) +{ + int pin, dev; + uint32_t irq_map_stride = 0; + uint32_t full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS * 6] = {}; + uint32_t *irq_map = full_irq_map; + + /* + * This code creates a standard swizzle of interrupts such that + * each device's first interrupt is based on it's PCI_SLOT number. + * (See pci_swizzle_map_irq_fn()) + * + * We only need one entry per interrupt in the table (not one per + * possible slot) seeing the interrupt-map-mask will allow the table + * to wrap to any number of devices. + */ + for (dev = 0; dev < GPEX_NUM_IRQS; dev++) { + int devfn = dev << 3; + + for (pin = 0; pin < GPEX_NUM_IRQS; pin++) { + int irq_nr = irq_base + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); + int i = 0; + + /* Fill PCI address cells */ + irq_map[i++] = cpu_to_be32(devfn << 8); + irq_map[i++] = 0; + irq_map[i++] = 0; + + /* Fill PCI Interrupt cells */ + irq_map[i++] = cpu_to_be32(pin + 1); + + /* Fill interrupt controller phandle and cells */ + irq_map[i++] = cpu_to_be32(irqchip_phandle); + irq_map[i++] = cpu_to_be32(irq_nr); + + if (!irq_map_stride) { + irq_map_stride = i; + } + irq_map += irq_map_stride; + } + } + + qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map, + GPEX_NUM_IRQS * GPEX_NUM_IRQS * + irq_map_stride * sizeof(uint32_t)); + + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask", + 0x1800, 0, 0, 0x7); +} + +static void openrisc_virt_pcie_init(OR1KVirtState *state, + hwaddr ecam_base, hwaddr ecam_size, + hwaddr pio_base, hwaddr pio_size, + hwaddr mmio_base, hwaddr mmio_size, + int num_cpus, OpenRISCCPU *cpus[], + int irq_base, int32_t pic_phandle) +{ + void *fdt = state->fdt; + char *nodename; + MemoryRegion *alias; + MemoryRegion *reg; + DeviceState *dev; + qemu_irq pcie_irq; + int i; + + dev = qdev_new(TYPE_GPEX_HOST); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + /* Map ECAM space. */ + alias = g_new0(MemoryRegion, 1); + reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + memory_region_init_alias(alias, OBJECT(dev), "pcie-ecam", + reg, 0, ecam_size); + memory_region_add_subregion(get_system_memory(), ecam_base, alias); + + /* + * Map the MMIO window into system address space so as to expose + * the section of PCI MMIO space which starts at the same base address + * (ie 1:1 mapping for that part of PCI MMIO space visible through + * the window). + */ + alias = g_new0(MemoryRegion, 1); + reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); + memory_region_init_alias(alias, OBJECT(dev), "pcie-mmio", + reg, mmio_base, mmio_size); + memory_region_add_subregion(get_system_memory(), mmio_base, alias); + + /* Map IO port space. */ + alias = g_new0(MemoryRegion, 1); + reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 2); + memory_region_init_alias(alias, OBJECT(dev), "pcie-pio", + reg, 0, pio_size); + memory_region_add_subregion(get_system_memory(), pio_base, alias); + + /* Connect IRQ lines. */ + for (i = 0; i < GPEX_NUM_IRQS; i++) { + pcie_irq = get_per_cpu_irq(cpus, num_cpus, irq_base + i); + + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pcie_irq); + gpex_set_irq_num(GPEX_HOST(dev), i, irq_base + i); + } + + nodename = g_strdup_printf("/soc/pci@%" HWADDR_PRIx, ecam_base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1); + qemu_fdt_setprop_cell(fdt, nodename, "#address-cells", 3); + qemu_fdt_setprop_cell(fdt, nodename, "#size-cells", 2); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "pci-host-ecam-generic"); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "pci"); + qemu_fdt_setprop_cell(fdt, nodename, "linux,pci-domain", 0); + qemu_fdt_setprop_cells(fdt, nodename, "bus-range", 0, + ecam_size / PCIE_MMCFG_SIZE_MIN - 1); + qemu_fdt_setprop(fdt, nodename, "dma-coherent", NULL, 0); + qemu_fdt_setprop_cells(fdt, nodename, "reg", ecam_base, ecam_size); + /* pci-address(3) cpu-address(1) pci-size(2) */ + qemu_fdt_setprop_cells(fdt, nodename, "ranges", + FDT_PCI_RANGE_IOPORT, 0, 0, + pio_base, 0, pio_size, + FDT_PCI_RANGE_MMIO, 0, mmio_base, + mmio_base, 0, mmio_size); + + create_pcie_irq_map(fdt, nodename, irq_base, pic_phandle); + g_free(nodename); +} + +static void openrisc_virt_virtio_init(OR1KVirtState *state, hwaddr base, + hwaddr size, int num_cpus, + OpenRISCCPU *cpus[], int irq_pin) +{ + void *fdt = state->fdt; + char *nodename; + DeviceState *dev; + SysBusDevice *sysbus; + qemu_irq virtio_irq = get_per_cpu_irq(cpus, num_cpus, irq_pin); + + /* VirtIO MMIO devices */ + dev = qdev_new(TYPE_VIRTIO_MMIO); + qdev_prop_set_bit(dev, "force-legacy", false); + sysbus = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sysbus, &error_fatal); + sysbus_connect_irq(sysbus, 0, virtio_irq); + sysbus_mmio_map(sysbus, 0, base); + + /* VirtIO MMIO devices FDT */ + nodename = g_strdup_printf("/soc/virtio_mmio@%" HWADDR_PRIx, base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "virtio,mmio"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", base, size); + qemu_fdt_setprop_cell(fdt, nodename, "interrupts", irq_pin); + g_free(nodename); +} + +static void openrisc_virt_init(MachineState *machine) +{ + ram_addr_t ram_size = machine->ram_size; + const char *kernel_filename = machine->kernel_filename; + OpenRISCCPU *cpus[VIRT_CPUS_MAX] = {}; + OR1KVirtState *state = VIRT_MACHINE(machine); + MemoryRegion *ram; + hwaddr load_addr; + int n; + unsigned int smp_cpus = machine->smp.cpus; + int32_t pic_phandle; + + assert(smp_cpus >= 1 && smp_cpus <= VIRT_CPUS_MAX); + for (n = 0; n < smp_cpus; n++) { + cpus[n] = OPENRISC_CPU(cpu_create(machine->cpu_type)); + if (cpus[n] == NULL) { + fprintf(stderr, "Unable to find CPU definition!\n"); + exit(1); + } + + cpu_openrisc_clock_init(cpus[n]); + + qemu_register_reset(main_cpu_reset, cpus[n]); + } + + ram = g_malloc(sizeof(*ram)); + memory_region_init_ram(ram, NULL, "openrisc.ram", ram_size, &error_fatal); + memory_region_add_subregion(get_system_memory(), 0, ram); + + openrisc_create_fdt(state, virt_memmap, smp_cpus, machine->ram_size, + machine->kernel_cmdline, &pic_phandle); + + if (smp_cpus > 1) { + openrisc_virt_ompic_init(state, virt_memmap[VIRT_OMPIC].base, + virt_memmap[VIRT_OMPIC].size, + smp_cpus, cpus, VIRT_OMPIC_IRQ); + } + + openrisc_virt_serial_init(state, virt_memmap[VIRT_UART].base, + virt_memmap[VIRT_UART].size, + smp_cpus, cpus, VIRT_UART_IRQ); + + openrisc_virt_test_init(state, virt_memmap[VIRT_TEST].base, + virt_memmap[VIRT_TEST].size); + + openrisc_virt_rtc_init(state, virt_memmap[VIRT_RTC].base, + virt_memmap[VIRT_RTC].size, smp_cpus, cpus, + VIRT_RTC_IRQ); + + openrisc_virt_pcie_init(state, virt_memmap[VIRT_ECAM].base, + virt_memmap[VIRT_ECAM].size, + virt_memmap[VIRT_PIO].base, + virt_memmap[VIRT_PIO].size, + virt_memmap[VIRT_MMIO].base, + virt_memmap[VIRT_MMIO].size, + smp_cpus, cpus, + VIRT_PCI_IRQ_BASE, pic_phandle); + + for (n = 0; n < VIRTIO_COUNT; n++) { + openrisc_virt_virtio_init(state, virt_memmap[VIRT_VIRTIO].base + + n * virt_memmap[VIRT_VIRTIO].size, + virt_memmap[VIRT_VIRTIO].size, + smp_cpus, cpus, VIRT_VIRTIO_IRQ + n); + } + + load_addr = openrisc_load_kernel(ram_size, kernel_filename, + &boot_info.bootstrap_pc); + if (load_addr > 0) { + if (machine->initrd_filename) { + load_addr = openrisc_load_initrd(state->fdt, + machine->initrd_filename, + load_addr, machine->ram_size); + } + boot_info.fdt_addr = openrisc_load_fdt(state->fdt, load_addr, + machine->ram_size); + } +} + +static void openrisc_virt_machine_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "or1k virtual machine"; + mc->init = openrisc_virt_init; + mc->max_cpus = VIRT_CPUS_MAX; + mc->is_default = false; + mc->default_cpu_type = OPENRISC_CPU_TYPE_NAME("or1200"); +} + +static const TypeInfo or1ksim_machine_typeinfo = { + .name = TYPE_VIRT_MACHINE, + .parent = TYPE_MACHINE, + .class_init = openrisc_virt_machine_init, + .instance_size = sizeof(OR1KVirtState), +}; + +static void or1ksim_machine_init_register_types(void) +{ + type_register_static(&or1ksim_machine_typeinfo); +} + +type_init(or1ksim_machine_init_register_types) diff --git a/hw/pci-bridge/Kconfig b/hw/pci-bridge/Kconfig index 02614f49aa..67077366cc 100644 --- a/hw/pci-bridge/Kconfig +++ b/hw/pci-bridge/Kconfig @@ -3,6 +3,11 @@ config PCIE_PORT default y if PCI_DEVICES depends on PCI_EXPRESS && MSI_NONBROKEN +config PCIE_PCI_BRIDGE + bool + default y if PCIE_PORT + depends on PCIE_PORT + config PXB bool default y if Q35 || ARM_VIRT diff --git a/hw/pci-bridge/cxl_downstream.c b/hw/pci-bridge/cxl_downstream.c new file mode 100644 index 0000000000..742da07a01 --- /dev/null +++ b/hw/pci-bridge/cxl_downstream.c @@ -0,0 +1,260 @@ +/* + * Emulated CXL Switch Downstream Port + * + * Copyright (c) 2022 Huawei Technologies. + * + * Based on xio3130_downstream.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/pci/msi.h" +#include "hw/pci/pcie.h" +#include "hw/pci/pcie_port.h" +#include "hw/cxl/cxl.h" +#include "qapi/error.h" + +typedef struct CXLDownstreamPort { + /*< private >*/ + PCIESlot parent_obj; + + /*< public >*/ + CXLComponentState cxl_cstate; +} CXLDownstreamPort; + +#define CXL_DOWNSTREAM_PORT_MSI_OFFSET 0x70 +#define CXL_DOWNSTREAM_PORT_MSI_NR_VECTOR 1 +#define CXL_DOWNSTREAM_PORT_EXP_OFFSET 0x90 +#define CXL_DOWNSTREAM_PORT_AER_OFFSET 0x100 +#define CXL_DOWNSTREAM_PORT_DVSEC_OFFSET \ + (CXL_DOWNSTREAM_PORT_AER_OFFSET + PCI_ERR_SIZEOF) + +static void latch_registers(CXLDownstreamPort *dsp) +{ + uint32_t *reg_state = dsp->cxl_cstate.crb.cache_mem_registers; + uint32_t *write_msk = dsp->cxl_cstate.crb.cache_mem_regs_write_mask; + + cxl_component_register_init_common(reg_state, write_msk, + CXL2_DOWNSTREAM_PORT); +} + +/* TODO: Look at sharing this code across all CXL port types */ +static void cxl_dsp_dvsec_write_config(PCIDevice *dev, uint32_t addr, + uint32_t val, int len) +{ + CXLDownstreamPort *dsp = CXL_DSP(dev); + CXLComponentState *cxl_cstate = &dsp->cxl_cstate; + + if (range_contains(&cxl_cstate->dvsecs[EXTENSIONS_PORT_DVSEC], addr)) { + uint8_t *reg = &dev->config[addr]; + addr -= cxl_cstate->dvsecs[EXTENSIONS_PORT_DVSEC].lob; + if (addr == PORT_CONTROL_OFFSET) { + if (pci_get_word(reg) & PORT_CONTROL_UNMASK_SBR) { + /* unmask SBR */ + qemu_log_mask(LOG_UNIMP, "SBR mask control is not supported\n"); + } + if (pci_get_word(reg) & PORT_CONTROL_ALT_MEMID_EN) { + /* Alt Memory & ID Space Enable */ + qemu_log_mask(LOG_UNIMP, + "Alt Memory & ID space is not supported\n"); + + } + } + } +} + +static void cxl_dsp_config_write(PCIDevice *d, uint32_t address, + uint32_t val, int len) +{ + uint16_t slt_ctl, slt_sta; + + pcie_cap_slot_get(d, &slt_ctl, &slt_sta); + pci_bridge_write_config(d, address, val, len); + pcie_cap_flr_write_config(d, address, val, len); + pcie_cap_slot_write_config(d, slt_ctl, slt_sta, address, val, len); + pcie_aer_write_config(d, address, val, len); + + cxl_dsp_dvsec_write_config(d, address, val, len); +} + +static void cxl_dsp_reset(DeviceState *qdev) +{ + PCIDevice *d = PCI_DEVICE(qdev); + CXLDownstreamPort *dsp = CXL_DSP(qdev); + + pcie_cap_deverr_reset(d); + pcie_cap_slot_reset(d); + pcie_cap_arifwd_reset(d); + pci_bridge_reset(qdev); + + latch_registers(dsp); +} + +static void build_dvsecs(CXLComponentState *cxl) +{ + uint8_t *dvsec; + + dvsec = (uint8_t *)&(CXLDVSECPortExt){ 0 }; + cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT, + EXTENSIONS_PORT_DVSEC_LENGTH, + EXTENSIONS_PORT_DVSEC, + EXTENSIONS_PORT_DVSEC_REVID, dvsec); + + dvsec = (uint8_t *)&(CXLDVSECPortFlexBus){ + .cap = 0x27, /* Cache, IO, Mem, non-MLD */ + .ctrl = 0x02, /* IO always enabled */ + .status = 0x26, /* same */ + .rcvd_mod_ts_data_phase1 = 0xef, /* WTF? */ + }; + cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT, + PCIE_CXL3_FLEXBUS_PORT_DVSEC_LENGTH, + PCIE_FLEXBUS_PORT_DVSEC, + PCIE_CXL3_FLEXBUS_PORT_DVSEC_REVID, dvsec); + + dvsec = (uint8_t *)&(CXLDVSECPortGPF){ + .rsvd = 0, + .phase1_ctrl = 1, /* 1μs timeout */ + .phase2_ctrl = 1, /* 1μs timeout */ + }; + cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT, + GPF_PORT_DVSEC_LENGTH, GPF_PORT_DVSEC, + GPF_PORT_DVSEC_REVID, dvsec); + + dvsec = (uint8_t *)&(CXLDVSECRegisterLocator){ + .rsvd = 0, + .reg0_base_lo = RBI_COMPONENT_REG | CXL_COMPONENT_REG_BAR_IDX, + .reg0_base_hi = 0, + }; + cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT, + REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC, + REG_LOC_DVSEC_REVID, dvsec); +} + +static void cxl_dsp_realize(PCIDevice *d, Error **errp) +{ + PCIEPort *p = PCIE_PORT(d); + PCIESlot *s = PCIE_SLOT(d); + CXLDownstreamPort *dsp = CXL_DSP(d); + CXLComponentState *cxl_cstate = &dsp->cxl_cstate; + ComponentRegisters *cregs = &cxl_cstate->crb; + MemoryRegion *component_bar = &cregs->component_registers; + int rc; + + pci_bridge_initfn(d, TYPE_PCIE_BUS); + pcie_port_init_reg(d); + + rc = msi_init(d, CXL_DOWNSTREAM_PORT_MSI_OFFSET, + CXL_DOWNSTREAM_PORT_MSI_NR_VECTOR, + true, true, errp); + if (rc) { + assert(rc == -ENOTSUP); + goto err_bridge; + } + + rc = pcie_cap_init(d, CXL_DOWNSTREAM_PORT_EXP_OFFSET, + PCI_EXP_TYPE_DOWNSTREAM, p->port, + errp); + if (rc < 0) { + goto err_msi; + } + + pcie_cap_flr_init(d); + pcie_cap_deverr_init(d); + pcie_cap_slot_init(d, s); + pcie_cap_arifwd_init(d); + + pcie_chassis_create(s->chassis); + rc = pcie_chassis_add_slot(s); + if (rc < 0) { + error_setg(errp, "Can't add chassis slot, error %d", rc); + goto err_pcie_cap; + } + + rc = pcie_aer_init(d, PCI_ERR_VER, CXL_DOWNSTREAM_PORT_AER_OFFSET, + PCI_ERR_SIZEOF, errp); + if (rc < 0) { + goto err_chassis; + } + + cxl_cstate->dvsec_offset = CXL_DOWNSTREAM_PORT_DVSEC_OFFSET; + cxl_cstate->pdev = d; + build_dvsecs(cxl_cstate); + cxl_component_register_block_init(OBJECT(d), cxl_cstate, TYPE_CXL_DSP); + pci_register_bar(d, CXL_COMPONENT_REG_BAR_IDX, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, + component_bar); + + return; + + err_chassis: + pcie_chassis_del_slot(s); + err_pcie_cap: + pcie_cap_exit(d); + err_msi: + msi_uninit(d); + err_bridge: + pci_bridge_exitfn(d); +} + +static void cxl_dsp_exitfn(PCIDevice *d) +{ + PCIESlot *s = PCIE_SLOT(d); + + pcie_aer_exit(d); + pcie_chassis_del_slot(s); + pcie_cap_exit(d); + msi_uninit(d); + pci_bridge_exitfn(d); +} + +static void cxl_dsp_instance_post_init(Object *obj) +{ + PCIESlot *s = PCIE_SLOT(obj); + + if (!s->speed) { + s->speed = QEMU_PCI_EXP_LNK_2_5GT; + } + + if (!s->width) { + s->width = QEMU_PCI_EXP_LNK_X1; + } +} + +static void cxl_dsp_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PCIDeviceClass *k = PCI_DEVICE_CLASS(oc); + + k->config_write = cxl_dsp_config_write; + k->realize = cxl_dsp_realize; + k->exit = cxl_dsp_exitfn; + k->vendor_id = 0x19e5; /* Huawei */ + k->device_id = 0xa129; /* Emulated CXL Switch Downstream Port */ + k->revision = 0; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->desc = "CXL Switch Downstream Port"; + dc->reset = cxl_dsp_reset; +} + +static const TypeInfo cxl_dsp_info = { + .name = TYPE_CXL_DSP, + .instance_size = sizeof(CXLDownstreamPort), + .parent = TYPE_PCIE_SLOT, + .instance_post_init = cxl_dsp_instance_post_init, + .class_init = cxl_dsp_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { INTERFACE_CXL_DEVICE }, + { } + }, +}; + +static void cxl_dsp_register_type(void) +{ + type_register_static(&cxl_dsp_info); +} + +type_init(cxl_dsp_register_type); diff --git a/hw/pci-bridge/cxl_root_port.c b/hw/pci-bridge/cxl_root_port.c index fb213fa06e..8a30da602c 100644 --- a/hw/pci-bridge/cxl_root_port.c +++ b/hw/pci-bridge/cxl_root_port.c @@ -22,6 +22,7 @@ #include "qemu/range.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pcie_port.h" +#include "hw/pci/msi.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "qapi/error.h" @@ -29,6 +30,10 @@ #define CXL_ROOT_PORT_DID 0x7075 +#define CXL_RP_MSI_OFFSET 0x60 +#define CXL_RP_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_MASKBIT +#define CXL_RP_MSI_NR_VECTOR 2 + /* Copied from the gen root port which we derive */ #define GEN_PCIE_ROOT_PORT_AER_OFFSET 0x100 #define GEN_PCIE_ROOT_PORT_ACS_OFFSET \ @@ -47,6 +52,49 @@ typedef struct CXLRootPort { #define TYPE_CXL_ROOT_PORT "cxl-rp" DECLARE_INSTANCE_CHECKER(CXLRootPort, CXL_ROOT_PORT, TYPE_CXL_ROOT_PORT) +/* + * If two MSI vector are allocated, Advanced Error Interrupt Message Number + * is 1. otherwise 0. + * 17.12.5.10 RPERRSTS, 32:27 bit Advanced Error Interrupt Message Number. + */ +static uint8_t cxl_rp_aer_vector(const PCIDevice *d) +{ + switch (msi_nr_vectors_allocated(d)) { + case 1: + return 0; + case 2: + return 1; + case 4: + case 8: + case 16: + case 32: + default: + break; + } + abort(); + return 0; +} + +static int cxl_rp_interrupts_init(PCIDevice *d, Error **errp) +{ + int rc; + + rc = msi_init(d, CXL_RP_MSI_OFFSET, CXL_RP_MSI_NR_VECTOR, + CXL_RP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, + CXL_RP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, + errp); + if (rc < 0) { + assert(rc == -ENOTSUP); + } + + return rc; +} + +static void cxl_rp_interrupts_uninit(PCIDevice *d) +{ + msi_uninit(d); +} + static void latch_registers(CXLRootPort *crp) { uint32_t *reg_state = crp->cxl_cstate.crb.cache_mem_registers; @@ -59,7 +107,7 @@ static void build_dvsecs(CXLComponentState *cxl) { uint8_t *dvsec; - dvsec = (uint8_t *)&(CXLDVSECPortExtensions){ 0 }; + dvsec = (uint8_t *)&(CXLDVSECPortExt){ 0 }; cxl_component_create_dvsec(cxl, CXL2_ROOT_PORT, EXTENSIONS_PORT_DVSEC_LENGTH, EXTENSIONS_PORT_DVSEC, @@ -81,9 +129,9 @@ static void build_dvsecs(CXLComponentState *cxl) .rcvd_mod_ts_data_phase1 = 0xef, }; cxl_component_create_dvsec(cxl, CXL2_ROOT_PORT, - PCIE_FLEXBUS_PORT_DVSEC_LENGTH_2_0, + PCIE_CXL3_FLEXBUS_PORT_DVSEC_LENGTH, PCIE_FLEXBUS_PORT_DVSEC, - PCIE_FLEXBUS_PORT_DVSEC_REVID_2_0, dvsec); + PCIE_CXL3_FLEXBUS_PORT_DVSEC_REVID, dvsec); dvsec = (uint8_t *)&(CXLDVSECRegisterLocator){ .rsvd = 0, @@ -127,7 +175,7 @@ static void cxl_rp_realize(DeviceState *dev, Error **errp) cxl_cstate->dvsec_offset = CXL_ROOT_PORT_DVSEC_OFFSET; cxl_cstate->pdev = pci_dev; - build_dvsecs(&crp->cxl_cstate); + build_dvsecs(cxl_cstate); cxl_component_register_block_init(OBJECT(pci_dev), cxl_cstate, TYPE_CXL_ROOT_PORT); @@ -138,12 +186,14 @@ static void cxl_rp_realize(DeviceState *dev, Error **errp) component_bar); } -static void cxl_rp_reset(DeviceState *dev) +static void cxl_rp_reset_hold(Object *obj) { - PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev); - CXLRootPort *crp = CXL_ROOT_PORT(dev); + PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(obj); + CXLRootPort *crp = CXL_ROOT_PORT(obj); - rpc->parent_reset(dev); + if (rpc->parent_phases.hold) { + rpc->parent_phases.hold(obj); + } latch_registers(crp); } @@ -181,16 +231,29 @@ static void cxl_rp_dvsec_write_config(PCIDevice *dev, uint32_t addr, } } +static void cxl_rp_aer_vector_update(PCIDevice *d) +{ + PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d); + + if (rpc->aer_vector) { + pcie_aer_root_set_vector(d, rpc->aer_vector(d)); + } +} + static void cxl_rp_write_config(PCIDevice *d, uint32_t address, uint32_t val, int len) { uint16_t slt_ctl, slt_sta; + uint32_t root_cmd = + pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND); pcie_cap_slot_get(d, &slt_ctl, &slt_sta); pci_bridge_write_config(d, address, val, len); + cxl_rp_aer_vector_update(d); pcie_cap_flr_write_config(d, address, val, len); pcie_cap_slot_write_config(d, slt_ctl, slt_sta, address, val, len); pcie_aer_write_config(d, address, val, len); + pcie_aer_root_write_config(d, address, val, len, root_cmd); cxl_rp_dvsec_write_config(d, address, val, len); } @@ -199,6 +262,7 @@ static void cxl_root_port_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *k = PCI_DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(oc); k->vendor_id = PCI_VENDOR_ID_INTEL; @@ -209,10 +273,14 @@ static void cxl_root_port_class_init(ObjectClass *oc, void *data) k->config_write = cxl_rp_write_config; device_class_set_parent_realize(dc, cxl_rp_realize, &rpc->parent_realize); - device_class_set_parent_reset(dc, cxl_rp_reset, &rpc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, cxl_rp_reset_hold, NULL, + &rpc->parent_phases); rpc->aer_offset = GEN_PCIE_ROOT_PORT_AER_OFFSET; rpc->acs_offset = GEN_PCIE_ROOT_PORT_ACS_OFFSET; + rpc->aer_vector = cxl_rp_aer_vector; + rpc->interrupts_init = cxl_rp_interrupts_init; + rpc->interrupts_uninit = cxl_rp_interrupts_uninit; dc->hotpluggable = false; } diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c new file mode 100644 index 0000000000..783fa6adac --- /dev/null +++ b/hw/pci-bridge/cxl_upstream.c @@ -0,0 +1,405 @@ +/* + * Emulated CXL Switch Upstream Port + * + * Copyright (c) 2022 Huawei Technologies. + * + * Based on xio3130_upstream.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/qdev-properties.h" +#include "hw/pci/msi.h" +#include "hw/pci/pcie.h" +#include "hw/pci/pcie_port.h" +#include "hw/pci-bridge/cxl_upstream_port.h" +/* + * Null value of all Fs suggested by IEEE RA guidelines for use of + * EU, OUI and CID + */ +#define UI64_NULL (~0ULL) + +#define CXL_UPSTREAM_PORT_MSI_NR_VECTOR 2 + +#define CXL_UPSTREAM_PORT_MSI_OFFSET 0x70 +#define CXL_UPSTREAM_PORT_PCIE_CAP_OFFSET 0x90 +#define CXL_UPSTREAM_PORT_AER_OFFSET 0x100 +#define CXL_UPSTREAM_PORT_SN_OFFSET \ + (CXL_UPSTREAM_PORT_AER_OFFSET + PCI_ERR_SIZEOF) +#define CXL_UPSTREAM_PORT_DVSEC_OFFSET \ + (CXL_UPSTREAM_PORT_SN_OFFSET + PCI_EXT_CAP_DSN_SIZEOF) + +CXLComponentState *cxl_usp_to_cstate(CXLUpstreamPort *usp) +{ + return &usp->cxl_cstate; +} + +static void cxl_usp_dvsec_write_config(PCIDevice *dev, uint32_t addr, + uint32_t val, int len) +{ + CXLUpstreamPort *usp = CXL_USP(dev); + + if (range_contains(&usp->cxl_cstate.dvsecs[EXTENSIONS_PORT_DVSEC], addr)) { + uint8_t *reg = &dev->config[addr]; + addr -= usp->cxl_cstate.dvsecs[EXTENSIONS_PORT_DVSEC].lob; + if (addr == PORT_CONTROL_OFFSET) { + if (pci_get_word(reg) & PORT_CONTROL_UNMASK_SBR) { + /* unmask SBR */ + qemu_log_mask(LOG_UNIMP, "SBR mask control is not supported\n"); + } + if (pci_get_word(reg) & PORT_CONTROL_ALT_MEMID_EN) { + /* Alt Memory & ID Space Enable */ + qemu_log_mask(LOG_UNIMP, + "Alt Memory & ID space is not supported\n"); + } + } + } +} + +static void cxl_usp_write_config(PCIDevice *d, uint32_t address, + uint32_t val, int len) +{ + CXLUpstreamPort *usp = CXL_USP(d); + + pcie_doe_write_config(&usp->doe_cdat, address, val, len); + pci_bridge_write_config(d, address, val, len); + pcie_cap_flr_write_config(d, address, val, len); + pcie_aer_write_config(d, address, val, len); + + cxl_usp_dvsec_write_config(d, address, val, len); +} + +static uint32_t cxl_usp_read_config(PCIDevice *d, uint32_t address, int len) +{ + CXLUpstreamPort *usp = CXL_USP(d); + uint32_t val; + + if (pcie_doe_read_config(&usp->doe_cdat, address, len, &val)) { + return val; + } + + return pci_default_read_config(d, address, len); +} + +static void latch_registers(CXLUpstreamPort *usp) +{ + uint32_t *reg_state = usp->cxl_cstate.crb.cache_mem_registers; + uint32_t *write_msk = usp->cxl_cstate.crb.cache_mem_regs_write_mask; + + cxl_component_register_init_common(reg_state, write_msk, + CXL2_UPSTREAM_PORT); + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, TARGET_COUNT, 8); +} + +static void cxl_usp_reset(DeviceState *qdev) +{ + PCIDevice *d = PCI_DEVICE(qdev); + CXLUpstreamPort *usp = CXL_USP(qdev); + + pci_bridge_reset(qdev); + pcie_cap_deverr_reset(d); + latch_registers(usp); +} + +static void build_dvsecs(CXLComponentState *cxl) +{ + uint8_t *dvsec; + + dvsec = (uint8_t *)&(CXLDVSECPortExt){ + .status = 0x1, /* Port Power Management Init Complete */ + }; + cxl_component_create_dvsec(cxl, CXL2_UPSTREAM_PORT, + EXTENSIONS_PORT_DVSEC_LENGTH, + EXTENSIONS_PORT_DVSEC, + EXTENSIONS_PORT_DVSEC_REVID, dvsec); + dvsec = (uint8_t *)&(CXLDVSECPortFlexBus){ + .cap = 0x27, /* Cache, IO, Mem, non-MLD */ + .ctrl = 0x27, /* Cache, IO, Mem */ + .status = 0x26, /* same */ + .rcvd_mod_ts_data_phase1 = 0xef, /* WTF? */ + }; + cxl_component_create_dvsec(cxl, CXL2_UPSTREAM_PORT, + PCIE_CXL3_FLEXBUS_PORT_DVSEC_LENGTH, + PCIE_FLEXBUS_PORT_DVSEC, + PCIE_CXL3_FLEXBUS_PORT_DVSEC_REVID, dvsec); + + dvsec = (uint8_t *)&(CXLDVSECRegisterLocator){ + .rsvd = 0, + .reg0_base_lo = RBI_COMPONENT_REG | CXL_COMPONENT_REG_BAR_IDX, + .reg0_base_hi = 0, + }; + cxl_component_create_dvsec(cxl, CXL2_UPSTREAM_PORT, + REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC, + REG_LOC_DVSEC_REVID, dvsec); +} + +static bool cxl_doe_cdat_rsp(DOECap *doe_cap) +{ + CDATObject *cdat = &CXL_USP(doe_cap->pdev)->cxl_cstate.cdat; + uint16_t ent; + void *base; + uint32_t len; + CDATReq *req = pcie_doe_get_write_mbox_ptr(doe_cap); + CDATRsp rsp; + + cxl_doe_cdat_update(&CXL_USP(doe_cap->pdev)->cxl_cstate, &error_fatal); + assert(cdat->entry_len); + + /* Discard if request length mismatched */ + if (pcie_doe_get_obj_len(req) < + DIV_ROUND_UP(sizeof(CDATReq), sizeof(uint32_t))) { + return false; + } + + ent = req->entry_handle; + base = cdat->entry[ent].base; + len = cdat->entry[ent].length; + + rsp = (CDATRsp) { + .header = { + .vendor_id = CXL_VENDOR_ID, + .data_obj_type = CXL_DOE_TABLE_ACCESS, + .reserved = 0x0, + .length = DIV_ROUND_UP((sizeof(rsp) + len), sizeof(uint32_t)), + }, + .rsp_code = CXL_DOE_TAB_RSP, + .table_type = CXL_DOE_TAB_TYPE_CDAT, + .entry_handle = (ent < cdat->entry_len - 1) ? + ent + 1 : CXL_DOE_TAB_ENT_MAX, + }; + + memcpy(doe_cap->read_mbox, &rsp, sizeof(rsp)); + memcpy(doe_cap->read_mbox + DIV_ROUND_UP(sizeof(rsp), sizeof(uint32_t)), + base, len); + + doe_cap->read_mbox_len += rsp.header.length; + + return true; +} + +static DOEProtocol doe_cdat_prot[] = { + { CXL_VENDOR_ID, CXL_DOE_TABLE_ACCESS, cxl_doe_cdat_rsp }, + { } +}; + +enum { + CXL_USP_CDAT_SSLBIS_LAT, + CXL_USP_CDAT_SSLBIS_BW, + CXL_USP_CDAT_NUM_ENTRIES +}; + +static int build_cdat_table(CDATSubHeader ***cdat_table, void *priv) +{ + CDATSslbis *sslbis_latency; + CDATSslbis *sslbis_bandwidth; + CXLUpstreamPort *us = CXL_USP(priv); + PCIBus *bus = &PCI_BRIDGE(us)->sec_bus; + int devfn, sslbis_size, i; + int count = 0; + uint16_t port_ids[256]; + + for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + PCIDevice *d = bus->devices[devfn]; + PCIEPort *port; + + if (!d || !pci_is_express(d) || !d->exp.exp_cap) { + continue; + } + + /* + * Whilst the PCI express spec doesn't allow anything other than + * downstream ports on this bus, let us be a little paranoid + */ + if (!object_dynamic_cast(OBJECT(d), TYPE_PCIE_PORT)) { + continue; + } + + port = PCIE_PORT(d); + port_ids[count] = port->port; + count++; + } + + /* May not yet have any ports - try again later */ + if (count == 0) { + return 0; + } + + sslbis_size = sizeof(CDATSslbis) + sizeof(*sslbis_latency->sslbe) * count; + sslbis_latency = g_malloc(sslbis_size); + *sslbis_latency = (CDATSslbis) { + .sslbis_header = { + .header = { + .type = CDAT_TYPE_SSLBIS, + .length = sslbis_size, + }, + .data_type = HMATLB_DATA_TYPE_ACCESS_LATENCY, + .entry_base_unit = 10000, + }, + }; + + for (i = 0; i < count; i++) { + sslbis_latency->sslbe[i] = (CDATSslbe) { + .port_x_id = CDAT_PORT_ID_USP, + .port_y_id = port_ids[i], + .latency_bandwidth = 15, /* 150ns */ + }; + } + + sslbis_bandwidth = g_malloc(sslbis_size); + *sslbis_bandwidth = (CDATSslbis) { + .sslbis_header = { + .header = { + .type = CDAT_TYPE_SSLBIS, + .length = sslbis_size, + }, + .data_type = HMATLB_DATA_TYPE_ACCESS_BANDWIDTH, + .entry_base_unit = 1024, + }, + }; + + for (i = 0; i < count; i++) { + sslbis_bandwidth->sslbe[i] = (CDATSslbe) { + .port_x_id = CDAT_PORT_ID_USP, + .port_y_id = port_ids[i], + .latency_bandwidth = 16, /* 16 GB/s */ + }; + } + + *cdat_table = g_new0(CDATSubHeader *, CXL_USP_CDAT_NUM_ENTRIES); + + /* Header always at start of structure */ + (*cdat_table)[CXL_USP_CDAT_SSLBIS_LAT] = (CDATSubHeader *)sslbis_latency; + (*cdat_table)[CXL_USP_CDAT_SSLBIS_BW] = (CDATSubHeader *)sslbis_bandwidth; + + return CXL_USP_CDAT_NUM_ENTRIES; +} + +static void free_default_cdat_table(CDATSubHeader **cdat_table, int num, + void *priv) +{ + int i; + + for (i = 0; i < num; i++) { + g_free(cdat_table[i]); + } + g_free(cdat_table); +} + +static void cxl_usp_realize(PCIDevice *d, Error **errp) +{ + ERRP_GUARD(); + PCIEPort *p = PCIE_PORT(d); + CXLUpstreamPort *usp = CXL_USP(d); + CXLComponentState *cxl_cstate = &usp->cxl_cstate; + ComponentRegisters *cregs = &cxl_cstate->crb; + MemoryRegion *component_bar = &cregs->component_registers; + int rc; + + pci_bridge_initfn(d, TYPE_PCIE_BUS); + pcie_port_init_reg(d); + + rc = msi_init(d, CXL_UPSTREAM_PORT_MSI_OFFSET, + CXL_UPSTREAM_PORT_MSI_NR_VECTOR, true, true, errp); + if (rc) { + assert(rc == -ENOTSUP); + goto err_bridge; + } + + rc = pcie_cap_init(d, CXL_UPSTREAM_PORT_PCIE_CAP_OFFSET, + PCI_EXP_TYPE_UPSTREAM, p->port, errp); + if (rc < 0) { + goto err_msi; + } + + pcie_cap_flr_init(d); + pcie_cap_deverr_init(d); + rc = pcie_aer_init(d, PCI_ERR_VER, CXL_UPSTREAM_PORT_AER_OFFSET, + PCI_ERR_SIZEOF, errp); + if (rc) { + goto err_cap; + } + if (usp->sn != UI64_NULL) { + pcie_dev_ser_num_init(d, CXL_UPSTREAM_PORT_SN_OFFSET, usp->sn); + } + cxl_cstate->dvsec_offset = CXL_UPSTREAM_PORT_DVSEC_OFFSET; + cxl_cstate->pdev = d; + build_dvsecs(cxl_cstate); + cxl_component_register_block_init(OBJECT(d), cxl_cstate, TYPE_CXL_USP); + pci_register_bar(d, CXL_COMPONENT_REG_BAR_IDX, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, + component_bar); + + pcie_doe_init(d, &usp->doe_cdat, cxl_cstate->dvsec_offset, doe_cdat_prot, + true, 1); + + cxl_cstate->cdat.build_cdat_table = build_cdat_table; + cxl_cstate->cdat.free_cdat_table = free_default_cdat_table; + cxl_cstate->cdat.private = d; + cxl_doe_cdat_init(cxl_cstate, errp); + if (*errp) { + goto err_cap; + } + + return; + +err_cap: + pcie_cap_exit(d); +err_msi: + msi_uninit(d); +err_bridge: + pci_bridge_exitfn(d); +} + +static void cxl_usp_exitfn(PCIDevice *d) +{ + pcie_aer_exit(d); + pcie_cap_exit(d); + msi_uninit(d); + pci_bridge_exitfn(d); +} + +static Property cxl_upstream_props[] = { + DEFINE_PROP_UINT64("sn", CXLUpstreamPort, sn, UI64_NULL), + DEFINE_PROP_STRING("cdat", CXLUpstreamPort, cxl_cstate.cdat.filename), + DEFINE_PROP_END_OF_LIST() +}; + +static void cxl_upstream_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PCIDeviceClass *k = PCI_DEVICE_CLASS(oc); + + k->config_write = cxl_usp_write_config; + k->config_read = cxl_usp_read_config; + k->realize = cxl_usp_realize; + k->exit = cxl_usp_exitfn; + k->vendor_id = 0x19e5; /* Huawei */ + k->device_id = 0xa128; /* Emulated CXL Switch Upstream Port */ + k->revision = 0; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->desc = "CXL Switch Upstream Port"; + dc->reset = cxl_usp_reset; + device_class_set_props(dc, cxl_upstream_props); +} + +static const TypeInfo cxl_usp_info = { + .name = TYPE_CXL_USP, + .parent = TYPE_PCIE_PORT, + .instance_size = sizeof(CXLUpstreamPort), + .class_init = cxl_upstream_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { INTERFACE_CXL_DEVICE }, + { } + }, +}; + +static void cxl_usp_register_type(void) +{ + type_register_static(&cxl_usp_info); +} + +type_init(cxl_usp_register_type); diff --git a/hw/pci-bridge/dec.c b/hw/pci-bridge/dec.c deleted file mode 100644 index 4773d07e6d..0000000000 --- a/hw/pci-bridge/dec.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * QEMU DEC 21154 PCI bridge - * - * Copyright (c) 2006-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * 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 "dec.h" -#include "hw/sysbus.h" -#include "qapi/error.h" -#include "qemu/module.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "hw/pci/pci_bridge.h" -#include "hw/pci/pci_bus.h" -#include "qom/object.h" - -OBJECT_DECLARE_SIMPLE_TYPE(DECState, DEC_21154) - -struct DECState { - PCIHostState parent_obj; -}; - -static int dec_map_irq(PCIDevice *pci_dev, int irq_num) -{ - return irq_num; -} - -static void dec_pci_bridge_realize(PCIDevice *pci_dev, Error **errp) -{ - pci_bridge_initfn(pci_dev, TYPE_PCI_BUS); -} - -static void dec_21154_pci_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); - k->realize = dec_pci_bridge_realize; - k->exit = pci_bridge_exitfn; - k->vendor_id = PCI_VENDOR_ID_DEC; - k->device_id = PCI_DEVICE_ID_DEC_21154; - k->config_write = pci_bridge_write_config; - k->is_bridge = true; - dc->desc = "DEC 21154 PCI-PCI bridge"; - dc->reset = pci_bridge_reset; - dc->vmsd = &vmstate_pci_device; -} - -static const TypeInfo dec_21154_pci_bridge_info = { - .name = "dec-21154-p2p-bridge", - .parent = TYPE_PCI_BRIDGE, - .instance_size = sizeof(PCIBridge), - .class_init = dec_21154_pci_bridge_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, - }, -}; - -PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn) -{ - PCIDevice *dev; - PCIBridge *br; - - dev = pci_new_multifunction(devfn, false, "dec-21154-p2p-bridge"); - br = PCI_BRIDGE(dev); - pci_bridge_map_irq(br, "DEC 21154 PCI-PCI bridge", dec_map_irq); - pci_realize_and_unref(dev, parent_bus, &error_fatal); - return pci_bridge_get_sec_bus(br); -} - -static void pci_dec_21154_device_realize(DeviceState *dev, Error **errp) -{ - PCIHostState *phb; - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - - phb = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&phb->conf_mem, OBJECT(dev), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&phb->data_mem, OBJECT(dev), &pci_host_data_le_ops, - dev, "pci-data-idx", 0x1000); - sysbus_init_mmio(sbd, &phb->conf_mem); - sysbus_init_mmio(sbd, &phb->data_mem); -} - -static void dec_21154_pci_host_realize(PCIDevice *d, Error **errp) -{ - /* PCI2PCI bridge same values as PearPC - check this */ -} - -static void dec_21154_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - k->realize = dec_21154_pci_host_realize; - k->vendor_id = PCI_VENDOR_ID_DEC; - k->device_id = PCI_DEVICE_ID_DEC_21154; - k->revision = 0x02; - k->class_id = PCI_CLASS_BRIDGE_PCI; - k->is_bridge = true; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->user_creatable = false; -} - -static const TypeInfo dec_21154_pci_host_info = { - .name = "dec-21154", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = dec_21154_pci_host_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, - }, -}; - -static void pci_dec_21154_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = pci_dec_21154_device_realize; -} - -static const TypeInfo pci_dec_21154_device_info = { - .name = TYPE_DEC_21154, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(DECState), - .class_init = pci_dec_21154_device_class_init, -}; - -static void dec_register_types(void) -{ - type_register_static(&pci_dec_21154_device_info); - type_register_static(&dec_21154_pci_host_info); - type_register_static(&dec_21154_pci_bridge_info); -} - -type_init(dec_register_types) diff --git a/hw/pci-bridge/dec.h b/hw/pci-bridge/dec.h deleted file mode 100644 index 869e90b136..0000000000 --- a/hw/pci-bridge/dec.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef HW_PCI_BRIDGE_DEC_H -#define HW_PCI_BRIDGE_DEC_H - - -#define TYPE_DEC_21154 "dec-21154-sysbus" - -PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn); - -#endif diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c index 20099a8ae3..784507c826 100644 --- a/hw/pci-bridge/gen_pcie_root_port.c +++ b/hw/pci-bridge/gen_pcie_root_port.c @@ -87,7 +87,12 @@ static void gen_rp_realize(DeviceState *dev, Error **errp) return; } - if (grp->res_reserve.io == -1 && s->hotplug && !s->native_hotplug) { + /* + * reserving IO space led to worse issues in 6.1, when this hunk was + * introduced. (see commit: 211afe5c69b59). Keep this broken for 6.1 + * machine type ABI compatibility only + */ + if (s->hide_native_hotplug_cap && grp->res_reserve.io == -1 && s->hotplug) { grp->res_reserve.io = GEN_PCIE_ROOT_DEFAULT_IO_RANGE; } int rc = pci_bridge_qemu_reserve_cap_init(d, 0, @@ -112,7 +117,7 @@ static const VMStateDescription vmstate_rp_dev = { .version_id = 1, .minimum_version_id = 1, .post_load = pcie_cap_slot_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot), VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log, PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog), diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c index f28181e210..c140919cbc 100644 --- a/hw/pci-bridge/i82801b11.c +++ b/hw/pci-bridge/i82801b11.c @@ -42,10 +42,10 @@ */ #include "qemu/osdep.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_bridge.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "hw/i386/ich9.h" +#include "hw/southbridge/ich9.h" /*****************************************************************************/ /* ICH9 DMI-to-PCI bridge */ @@ -81,7 +81,7 @@ err_bridge: static const VMStateDescription i82801b11_bridge_dev_vmstate = { .name = "i82801b11_bridge", .priority = MIG_PRI_PCI_BUS, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PCIBridge), VMSTATE_END_OF_LIST() } @@ -92,7 +92,6 @@ static void i82801b11_bridge_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->is_bridge = true; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82801BA_11; k->revision = ICH9_D2P_A2_REVISION; diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c index f1e16135a3..be752a4bda 100644 --- a/hw/pci-bridge/ioh3420.c +++ b/hw/pci-bridge/ioh3420.c @@ -88,7 +88,7 @@ static const VMStateDescription vmstate_ioh3420 = { .version_id = 1, .minimum_version_id = 1, .post_load = pcie_cap_slot_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot), VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log, PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog), diff --git a/hw/pci-bridge/meson.build b/hw/pci-bridge/meson.build index b6d26a03d5..f2a60434dd 100644 --- a/hw/pci-bridge/meson.build +++ b/hw/pci-bridge/meson.build @@ -2,14 +2,14 @@ pci_ss = ss.source_set() pci_ss.add(files('pci_bridge_dev.c')) pci_ss.add(when: 'CONFIG_I82801B11', if_true: files('i82801b11.c')) pci_ss.add(when: 'CONFIG_IOH3420', if_true: files('ioh3420.c')) -pci_ss.add(when: 'CONFIG_PCIE_PORT', if_true: files('pcie_root_port.c', 'gen_pcie_root_port.c', 'pcie_pci_bridge.c')) -pci_ss.add(when: 'CONFIG_PXB', if_true: files('pci_expander_bridge.c')) +pci_ss.add(when: 'CONFIG_PCIE_PORT', if_true: files('pcie_root_port.c', 'gen_pcie_root_port.c')) +pci_ss.add(when: 'CONFIG_PCIE_PCI_BRIDGE', if_true: files('pcie_pci_bridge.c')) +pci_ss.add(when: 'CONFIG_PXB', if_true: files('pci_expander_bridge.c'), + if_false: files('pci_expander_bridge_stubs.c')) pci_ss.add(when: 'CONFIG_XIO3130', if_true: files('xio3130_upstream.c', 'xio3130_downstream.c')) -pci_ss.add(when: 'CONFIG_CXL', if_true: files('cxl_root_port.c')) +pci_ss.add(when: 'CONFIG_CXL', if_true: files('cxl_root_port.c', 'cxl_upstream.c', 'cxl_downstream.c')) -# NewWorld PowerMac -pci_ss.add(when: 'CONFIG_DEC_PCI', if_true: files('dec.c')) # Sun4u pci_ss.add(when: 'CONFIG_SIMBA', if_true: files('simba.c')) -softmmu_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) +system_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 657a06ddbe..089f91efed 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -186,7 +186,6 @@ static Property pci_bridge_dev_properties[] = { res_reserve.mem_pref_32, -1), DEFINE_PROP_SIZE("pref64-reserve", PCIBridgeDev, res_reserve.mem_pref_64, -1), - DEFINE_PROP_END_OF_LIST(), }; @@ -200,7 +199,7 @@ static bool pci_device_shpc_present(void *opaque, int version_id) static const VMStateDescription pci_bridge_dev_vmstate = { .name = "pci_bridge", .priority = MIG_PRI_PCI_BUS, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PCIBridge), SHPC_VMSTATE(shpc, PCIDevice, pci_device_shpc_present), VMSTATE_END_OF_LIST() @@ -254,7 +253,6 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) k->vendor_id = PCI_VENDOR_ID_REDHAT; k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE; k->class_id = PCI_CLASS_BRIDGE_PCI; - k->is_bridge = true; dc->desc = "Standard PCI Bridge"; dc->reset = qdev_pci_bridge_dev_reset; device_class_set_props(dc, pci_bridge_dev_properties); diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 69244decdb..0411ad31ea 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -15,8 +15,10 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" +#include "hw/pci/pcie_port.h" #include "hw/qdev-properties.h" #include "hw/pci/pci_bridge.h" +#include "hw/pci-bridge/pci_expander_bridge.h" #include "hw/cxl/cxl.h" #include "qemu/range.h" #include "qemu/error-report.h" @@ -48,25 +50,8 @@ struct PXBBus { char bus_path[8]; }; -#define TYPE_PXB_DEVICE "pxb" -typedef struct PXBDev PXBDev; -DECLARE_INSTANCE_CHECKER(PXBDev, PXB_DEV, - TYPE_PXB_DEVICE) - -#define TYPE_PXB_PCIE_DEVICE "pxb-pcie" -DECLARE_INSTANCE_CHECKER(PXBDev, PXB_PCIE_DEV, - TYPE_PXB_PCIE_DEVICE) - -static PXBDev *convert_to_pxb(PCIDevice *dev) -{ - /* A CXL PXB's parent bus is PCIe, so the normal check won't work */ - if (object_dynamic_cast(OBJECT(dev), TYPE_PXB_CXL_DEVICE)) { - return PXB_CXL_DEV(dev); - } - - return pci_bus_is_express(pci_get_bus(dev)) - ? PXB_PCIE_DEV(dev) : PXB_DEV(dev); -} +#define TYPE_PXB_PCIE_DEV "pxb-pcie" +OBJECT_DECLARE_SIMPLE_TYPE(PXBPCIEDev, PXB_PCIE_DEV) static GList *pxb_dev_list; @@ -79,16 +64,23 @@ CXLComponentState *cxl_get_hb_cstate(PCIHostState *hb) return &host->cxl_cstate; } +bool cxl_get_hb_passthrough(PCIHostState *hb) +{ + CXLHost *host = PXB_CXL_HOST(hb); + + return host->passthrough; +} + static int pxb_bus_num(PCIBus *bus) { - PXBDev *pxb = convert_to_pxb(bus->parent_dev); + PXBDev *pxb = PXB_DEV(bus->parent_dev); return pxb->bus_nr; } static uint16_t pxb_bus_numa_node(PCIBus *bus) { - PXBDev *pxb = convert_to_pxb(bus->parent_dev); + PXBDev *pxb = PXB_DEV(bus->parent_dev); return pxb->numa_node; } @@ -146,7 +138,7 @@ static char *pxb_host_ofw_unit_address(const SysBusDevice *dev) pxb_host = PCI_HOST_BRIDGE(dev); pxb_bus = pxb_host->bus; - pxb_dev = convert_to_pxb(pxb_bus->parent_dev); + pxb_dev = PXB_DEV(pxb_bus->parent_dev); position = g_list_index(pxb_dev_list, pxb_dev); assert(position >= 0); @@ -155,7 +147,7 @@ static char *pxb_host_ofw_unit_address(const SysBusDevice *dev) main_host_sbd = SYS_BUS_DEVICE(main_host); if (main_host_sbd->num_mmio > 0) { - return g_strdup_printf(TARGET_FMT_plx ",%x", + return g_strdup_printf(HWADDR_FMT_plx ",%x", main_host_sbd->mmio[0].addr, position + 1); } if (main_host_sbd->num_pio > 0) { @@ -186,25 +178,38 @@ static const TypeInfo pxb_host_info = { static void pxb_cxl_realize(DeviceState *dev, Error **errp) { - MachineState *ms = MACHINE(qdev_get_machine()); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); CXLHost *cxl = PXB_CXL_HOST(dev); CXLComponentState *cxl_cstate = &cxl->cxl_cstate; struct MemoryRegion *mr = &cxl_cstate->crb.component_registers; - hwaddr offset; cxl_component_register_block_init(OBJECT(dev), cxl_cstate, TYPE_PXB_CXL_HOST); sysbus_init_mmio(sbd, mr); +} - offset = memory_region_size(mr) * ms->cxl_devices_state->next_mr_idx; - if (offset > memory_region_size(&ms->cxl_devices_state->host_mr)) { +/* + * Host bridge realization has no means of knowning state associated + * with a particular machine. As such, it is nececssary to delay + * final setup of the host bridge register space until later in the + * machine bring up. + */ +void pxb_cxl_hook_up_registers(CXLState *cxl_state, PCIBus *bus, Error **errp) +{ + PXBCXLDev *pxb = PXB_CXL_DEV(pci_bridge_get_device(bus)); + CXLHost *cxl = pxb->cxl_host_bridge; + CXLComponentState *cxl_cstate = &cxl->cxl_cstate; + struct MemoryRegion *mr = &cxl_cstate->crb.component_registers; + hwaddr offset; + + offset = memory_region_size(mr) * cxl_state->next_mr_idx; + if (offset > memory_region_size(&cxl_state->host_mr)) { error_setg(errp, "Insufficient space for pxb cxl host register space"); return; } - memory_region_add_subregion(&ms->cxl_devices_state->host_mr, offset, mr); - ms->cxl_devices_state->next_mr_idx++; + memory_region_add_subregion(&cxl_state->host_mr, offset, mr); + cxl_state->next_mr_idx++; } static void pxb_cxl_host_class_init(ObjectClass *class, void *data) @@ -258,7 +263,7 @@ static int pxb_map_irq_fn(PCIDevice *pci_dev, int pin) /* * First carry out normal swizzle to handle - * multple root ports on a pxb instance. + * multiple root ports on a pxb instance. */ pin = pci_swizzle_map_irq_fn(pci_dev, pin); @@ -276,15 +281,32 @@ static int pxb_map_irq_fn(PCIDevice *pci_dev, int pin) return pin - PCI_SLOT(pxb->devfn); } -static void pxb_dev_reset(DeviceState *dev) +static void pxb_cxl_dev_reset(DeviceState *dev) { - CXLHost *cxl = PXB_CXL_DEV(dev)->cxl.cxl_host_bridge; + CXLHost *cxl = PXB_CXL_DEV(dev)->cxl_host_bridge; CXLComponentState *cxl_cstate = &cxl->cxl_cstate; + PCIHostState *hb = PCI_HOST_BRIDGE(cxl); uint32_t *reg_state = cxl_cstate->crb.cache_mem_registers; uint32_t *write_msk = cxl_cstate->crb.cache_mem_regs_write_mask; + int dsp_count = 0; - cxl_component_register_init_common(reg_state, write_msk, CXL2_ROOT_PORT); - ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, TARGET_COUNT, 8); + cxl_component_register_init_common(reg_state, write_msk, CXL2_RC); + /* + * The CXL specification allows for host bridges with no HDM decoders + * if they only have a single root port. + */ + if (!PXB_CXL_DEV(dev)->hdm_for_passthrough) { + dsp_count = pcie_count_ds_ports(hb->bus); + } + /* Initial reset will have 0 dsp so wait until > 0 */ + if (dsp_count == 1) { + cxl->passthrough = true; + /* Set Capability ID in header to NONE */ + ARRAY_FIELD_DP32(reg_state, CXL_HDM_CAPABILITY_HEADER, ID, 0); + } else { + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, TARGET_COUNT, + 8); + } } static gint pxb_compare(gconstpointer a, gconstpointer b) @@ -299,7 +321,7 @@ static gint pxb_compare(gconstpointer a, gconstpointer b) static void pxb_dev_realize_common(PCIDevice *dev, enum BusType type, Error **errp) { - PXBDev *pxb = convert_to_pxb(dev); + PXBDev *pxb = PXB_DEV(dev); DeviceState *ds, *bds = NULL; PCIBus *bus; const char *dev_name = NULL; @@ -327,7 +349,7 @@ static void pxb_dev_realize_common(PCIDevice *dev, enum BusType type, } else if (type == CXL) { bus = pci_root_bus_new(ds, dev_name, NULL, NULL, 0, TYPE_PXB_CXL_BUS); bus->flags |= PCI_BUS_CXL; - PXB_CXL_DEV(dev)->cxl.cxl_host_bridge = PXB_CXL_HOST(ds); + PXB_CXL_DEV(dev)->cxl_host_bridge = PXB_CXL_HOST(ds); } else { bus = pci_root_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS); bds = qdev_new("pci-bridge"); @@ -380,7 +402,7 @@ static void pxb_dev_realize(PCIDevice *dev, Error **errp) static void pxb_dev_exitfn(PCIDevice *pci_dev) { - PXBDev *pxb = convert_to_pxb(pci_dev); + PXBDev *pxb = PXB_DEV(pci_dev); pxb_dev_list = g_list_remove(pxb_dev_list, pxb); } @@ -411,7 +433,7 @@ static void pxb_dev_class_init(ObjectClass *klass, void *data) } static const TypeInfo pxb_dev_info = { - .name = TYPE_PXB_DEVICE, + .name = TYPE_PXB_DEV, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PXBDev), .class_init = pxb_dev_class_init, @@ -443,15 +465,14 @@ static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_BRIDGE_HOST; dc->desc = "PCI Express Expander Bridge"; - device_class_set_props(dc, pxb_dev_properties); dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo pxb_pcie_dev_info = { - .name = TYPE_PXB_PCIE_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PXBDev), + .name = TYPE_PXB_PCIE_DEV, + .parent = TYPE_PXB_DEV, + .instance_size = sizeof(PXBPCIEDev), .class_init = pxb_pcie_dev_class_init, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, @@ -461,22 +482,21 @@ static const TypeInfo pxb_pcie_dev_info = { static void pxb_cxl_dev_realize(PCIDevice *dev, Error **errp) { - MachineState *ms = MACHINE(qdev_get_machine()); - /* A CXL PXB's parent bus is still PCIe */ if (!pci_bus_is_express(pci_get_bus(dev))) { error_setg(errp, "pxb-cxl devices cannot reside on a PCI bus"); return; } - if (!ms->cxl_devices_state->is_enabled) { - error_setg(errp, "Machine does not have cxl=on"); - return; - } pxb_dev_realize_common(dev, CXL, errp); - pxb_dev_reset(DEVICE(dev)); + pxb_cxl_dev_reset(DEVICE(dev)); } +static Property pxb_cxl_dev_properties[] = { + DEFINE_PROP_BOOL("hdm_for_passthrough", PXBCXLDev, hdm_for_passthrough, false), + DEFINE_PROP_END_OF_LIST(), +}; + static void pxb_cxl_dev_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -490,18 +510,18 @@ static void pxb_cxl_dev_class_init(ObjectClass *klass, void *data) */ dc->desc = "CXL Host Bridge"; - device_class_set_props(dc, pxb_dev_properties); + device_class_set_props(dc, pxb_cxl_dev_properties); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); /* Host bridges aren't hotpluggable. FIXME: spec reference */ dc->hotpluggable = false; - dc->reset = pxb_dev_reset; + dc->reset = pxb_cxl_dev_reset; } static const TypeInfo pxb_cxl_dev_info = { - .name = TYPE_PXB_CXL_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PXBDev), + .name = TYPE_PXB_CXL_DEV, + .parent = TYPE_PXB_PCIE_DEV, + .instance_size = sizeof(PXBCXLDev), .class_init = pxb_cxl_dev_class_init, .interfaces = (InterfaceInfo[]){ diff --git a/hw/pci-bridge/pci_expander_bridge_stubs.c b/hw/pci-bridge/pci_expander_bridge_stubs.c new file mode 100644 index 0000000000..b35180311f --- /dev/null +++ b/hw/pci-bridge/pci_expander_bridge_stubs.c @@ -0,0 +1,14 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Stubs for calls made from machines to handle the case where CONFIG_PXB + * is not enabled. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci-bridge/pci_expander_bridge.h" +#include "hw/cxl/cxl.h" + +void pxb_cxl_hook_up_registers(CXLState *state, PCIBus *bus, Error **errp) {}; diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c index 1cd917a459..7646ac2397 100644 --- a/hw/pci-bridge/pcie_pci_bridge.c +++ b/hw/pci-bridge/pcie_pci_bridge.c @@ -132,7 +132,7 @@ static Property pcie_pci_bridge_dev_properties[] = { static const VMStateDescription pcie_pci_bridge_dev_vmstate = { .name = TYPE_PCIE_PCI_BRIDGE_DEV, .priority = MIG_PRI_PCI_BUS, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PCIBridge), SHPC_VMSTATE(shpc, PCIDevice, NULL), VMSTATE_END_OF_LIST() @@ -145,7 +145,6 @@ static void pcie_pci_bridge_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - k->is_bridge = true; k->vendor_id = PCI_VENDOR_ID_REDHAT; k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE; k->realize = pcie_pci_bridge_realize; diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c index 460e48269d..efd96bf174 100644 --- a/hw/pci-bridge/pcie_root_port.c +++ b/hw/pci-bridge/pcie_root_port.c @@ -43,9 +43,10 @@ static void rp_write_config(PCIDevice *d, uint32_t address, pcie_aer_root_write_config(d, address, val, len, root_cmd); } -static void rp_reset(DeviceState *qdev) +static void rp_reset_hold(Object *obj) { - PCIDevice *d = PCI_DEVICE(qdev); + PCIDevice *d = PCI_DEVICE(obj); + DeviceState *qdev = DEVICE(obj); rp_aer_vector_update(d); pcie_cap_root_reset(d); @@ -171,13 +172,13 @@ static void rp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); - k->is_bridge = true; k->config_write = rp_write_config; k->realize = rp_realize; k->exit = rp_exit; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->reset = rp_reset; + rc->phases.hold = rp_reset_hold; device_class_set_props(dc, rp_props); } diff --git a/hw/pci-bridge/simba.c b/hw/pci-bridge/simba.c index ba55ab1939..17aa0d7b21 100644 --- a/hw/pci-bridge/simba.c +++ b/hw/pci-bridge/simba.c @@ -77,7 +77,6 @@ static void simba_pci_bridge_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_SUN_SIMBA; k->revision = 0x11; k->config_write = pci_bridge_write_config; - k->is_bridge = true; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->reset = pci_bridge_reset; dc->vmsd = &vmstate_pci_device; diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index 05e2b06c0c..907d5105b0 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -146,7 +146,7 @@ static const VMStateDescription vmstate_xio3130_downstream = { .version_id = 1, .minimum_version_id = 1, .post_load = pcie_cap_slot_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot), VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log, PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog), @@ -159,7 +159,6 @@ static void xio3130_downstream_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->is_bridge = true; k->config_write = xio3130_downstream_write_config; k->realize = xio3130_downstream_realize; k->exit = xio3130_downstream_exitfn; diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c index 5ff46ef050..2a6cff6e03 100644 --- a/hw/pci-bridge/xio3130_upstream.c +++ b/hw/pci-bridge/xio3130_upstream.c @@ -115,7 +115,7 @@ static const VMStateDescription vmstate_xio3130_upstream = { .priority = MIG_PRI_PCI_BUS, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj.parent_obj, PCIEPort), VMSTATE_STRUCT(parent_obj.parent_obj.exp.aer_log, PCIEPort, 0, vmstate_pcie_aer_log, PCIEAERLog), @@ -128,7 +128,6 @@ static void xio3130_upstream_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->is_bridge = true; k->config_write = xio3130_upstream_write_config; k->realize = xio3130_upstream_realize; k->exit = xio3130_upstream_exitfn; diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index 38fd2ee8f3..c91880b237 100644 --- a/hw/pci-host/Kconfig +++ b/hw/pci-host/Kconfig @@ -6,6 +6,14 @@ config XEN_IGD_PASSTHROUGH default y depends on XEN && PCI_I440FX +config PPC4XX_PCI + bool + select PCI + +config PPC440_PCIX + bool + select PCI + config RAVEN_PCI bool select PCI @@ -73,6 +81,11 @@ config SH_PCI bool select PCI +config ARTICIA + bool + select PCI + select I8259 + config MV64361 bool select PCI @@ -81,3 +94,13 @@ config MV64361 config DINO bool select PCI + +config ASTRO + bool + select PCI + +config GT64120 + bool + select PCI + select EMPTY_SLOT + select I8259 diff --git a/hw/pci-host/articia.c b/hw/pci-host/articia.c new file mode 100644 index 0000000000..f3fcc49f81 --- /dev/null +++ b/hw/pci-host/articia.c @@ -0,0 +1,293 @@ +/* + * Mai Logic Articia S emulation + * + * Copyright (c) 2023 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/pci/pci_device.h" +#include "hw/pci/pci_host.h" +#include "hw/irq.h" +#include "hw/i2c/bitbang_i2c.h" +#include "hw/intc/i8259.h" +#include "hw/pci-host/articia.h" + +/* + * This is a minimal emulation of this chip as used in AmigaOne board. + * Most features are missing but those are not needed by firmware and guests. + */ + +OBJECT_DECLARE_SIMPLE_TYPE(ArticiaState, ARTICIA) + +OBJECT_DECLARE_SIMPLE_TYPE(ArticiaHostState, ARTICIA_PCI_HOST) +struct ArticiaHostState { + PCIDevice parent_obj; + + ArticiaState *as; +}; + +/* TYPE_ARTICIA */ + +struct ArticiaState { + PCIHostState parent_obj; + + qemu_irq irq[PCI_NUM_PINS]; + MemoryRegion io; + MemoryRegion mem; + MemoryRegion reg; + + bitbang_i2c_interface smbus; + uint32_t gpio; /* bits 0-7 in, 8-15 out, 16-23 direction (0 in, 1 out) */ + hwaddr gpio_base; + MemoryRegion gpio_reg; +}; + +static uint64_t articia_gpio_read(void *opaque, hwaddr addr, unsigned int size) +{ + ArticiaState *s = opaque; + + return (s->gpio >> (addr * 8)) & 0xff; +} + +static void articia_gpio_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) +{ + ArticiaState *s = opaque; + uint32_t sh = addr * 8; + + if (addr == 0) { + /* in bits read only? */ + return; + } + + if ((s->gpio & (0xff << sh)) != (val & 0xff) << sh) { + s->gpio &= ~(0xff << sh | 0xff); + s->gpio |= (val & 0xff) << sh; + s->gpio |= bitbang_i2c_set(&s->smbus, BITBANG_I2C_SDA, + s->gpio & BIT(16) ? + !!(s->gpio & BIT(8)) : 1); + if ((s->gpio & BIT(17))) { + s->gpio &= ~BIT(0); + s->gpio |= bitbang_i2c_set(&s->smbus, BITBANG_I2C_SCL, + !!(s->gpio & BIT(9))); + } + } +} + +static const MemoryRegionOps articia_gpio_ops = { + .read = articia_gpio_read, + .write = articia_gpio_write, + .valid.min_access_size = 1, + .valid.max_access_size = 1, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t articia_reg_read(void *opaque, hwaddr addr, unsigned int size) +{ + ArticiaState *s = opaque; + uint64_t ret = UINT_MAX; + + switch (addr) { + case 0xc00cf8: + ret = pci_host_conf_le_ops.read(PCI_HOST_BRIDGE(s), 0, size); + break; + case 0xe00cfc ... 0xe00cff: + ret = pci_host_data_le_ops.read(PCI_HOST_BRIDGE(s), addr - 0xe00cfc, size); + break; + case 0xf00000: + ret = pic_read_irq(isa_pic); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register read 0x%" + HWADDR_PRIx " %d\n", __func__, addr, size); + break; + } + return ret; +} + +static void articia_reg_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) +{ + ArticiaState *s = opaque; + + switch (addr) { + case 0xc00cf8: + pci_host_conf_le_ops.write(PCI_HOST_BRIDGE(s), 0, val, size); + break; + case 0xe00cfc ... 0xe00cff: + pci_host_data_le_ops.write(PCI_HOST_BRIDGE(s), addr, val, size); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register write 0x%" + HWADDR_PRIx " %d <- %"PRIx64"\n", __func__, addr, size, val); + break; + } +} + +static const MemoryRegionOps articia_reg_ops = { + .read = articia_reg_read, + .write = articia_reg_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void articia_pcihost_set_irq(void *opaque, int n, int level) +{ + ArticiaState *s = opaque; + qemu_set_irq(s->irq[n], level); +} + +/* + * AmigaOne SE PCI slot to IRQ routing + * + * repository: https://source.denx.de/u-boot/custodians/u-boot-avr32.git + * refspec: v2010.06 + * file: board/MAI/AmigaOneG3SE/articiaS_pci.c + */ +static int amigaone_pcihost_bus0_map_irq(PCIDevice *pdev, int pin) +{ + int devfn_slot = PCI_SLOT(pdev->devfn); + + switch (devfn_slot) { + case 6: /* On board ethernet */ + return 3; + case 7: /* South bridge */ + return pin; + default: /* PCI Slot 1 Devfn slot 8, Slot 2 Devfn 9, Slot 3 Devfn 10 */ + return pci_swizzle(devfn_slot, pin); + } + +} + +static void articia_realize(DeviceState *dev, Error **errp) +{ + ArticiaState *s = ARTICIA(dev); + PCIHostState *h = PCI_HOST_BRIDGE(dev); + PCIDevice *pdev; + + bitbang_i2c_init(&s->smbus, i2c_init_bus(dev, "smbus")); + memory_region_init_io(&s->gpio_reg, OBJECT(s), &articia_gpio_ops, s, + TYPE_ARTICIA, 4); + + memory_region_init(&s->mem, OBJECT(dev), "pci-mem", UINT64_MAX); + memory_region_init(&s->io, OBJECT(dev), "pci-io", 0xc00000); + memory_region_init_io(&s->reg, OBJECT(s), &articia_reg_ops, s, + TYPE_ARTICIA, 0x1000000); + memory_region_add_subregion_overlap(&s->reg, 0, &s->io, 1); + + /* devfn_min is 8 that matches first PCI slot in AmigaOne */ + h->bus = pci_register_root_bus(dev, NULL, articia_pcihost_set_irq, + amigaone_pcihost_bus0_map_irq, dev, &s->mem, + &s->io, PCI_DEVFN(8, 0), 4, TYPE_PCI_BUS); + pdev = pci_create_simple_multifunction(h->bus, PCI_DEVFN(0, 0), + TYPE_ARTICIA_PCI_HOST); + ARTICIA_PCI_HOST(pdev)->as = s; + pci_create_simple(h->bus, PCI_DEVFN(0, 1), TYPE_ARTICIA_PCI_BRIDGE); + + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->reg); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mem); + qdev_init_gpio_out(dev, s->irq, ARRAY_SIZE(s->irq)); +} + +static void articia_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = articia_realize; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); +} + +/* TYPE_ARTICIA_PCI_HOST */ + +static void articia_pci_host_cfg_write(PCIDevice *d, uint32_t addr, + uint32_t val, int len) +{ + ArticiaState *s = ARTICIA_PCI_HOST(d)->as; + + pci_default_write_config(d, addr, val, len); + switch (addr) { + case 0x40: + s->gpio_base = val; + break; + case 0x44: + if (val != 0x11) { + /* FIXME what do the bits actually mean? */ + break; + } + if (memory_region_is_mapped(&s->gpio_reg)) { + memory_region_del_subregion(&s->io, &s->gpio_reg); + } + memory_region_add_subregion(&s->io, s->gpio_base + 0x38, &s->gpio_reg); + break; + } +} + +static void articia_pci_host_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->config_write = articia_pci_host_cfg_write; + k->vendor_id = 0x10cc; + k->device_id = 0x0660; + k->class_id = PCI_CLASS_BRIDGE_HOST; + /* + * PCI-facing part of the host bridge, + * not usable without the host-facing part + */ + dc->user_creatable = false; +} + +/* TYPE_ARTICIA_PCI_BRIDGE */ + +static void articia_pci_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->vendor_id = 0x10cc; + k->device_id = 0x0661; + k->class_id = PCI_CLASS_BRIDGE_HOST; + /* + * PCI-facing part of the host bridge, + * not usable without the host-facing part + */ + dc->user_creatable = false; +} + +static const TypeInfo articia_types[] = { + { + .name = TYPE_ARTICIA, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(ArticiaState), + .class_init = articia_class_init, + }, + { + .name = TYPE_ARTICIA_PCI_HOST, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(ArticiaHostState), + .class_init = articia_pci_host_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, + }, + { + .name = TYPE_ARTICIA_PCI_BRIDGE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = articia_pci_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, + }, +}; + +DEFINE_TYPES(articia_types) diff --git a/hw/pci-host/astro.c b/hw/pci-host/astro.c new file mode 100644 index 0000000000..e3e589ceac --- /dev/null +++ b/hw/pci-host/astro.c @@ -0,0 +1,907 @@ +/* + * HP-PARISC Astro/Pluto/Ike/REO system bus adapter (SBA) + * with Elroy PCI bus (LBA) adapter emulation + * Found in C3000 and similar machines + * + * (C) 2023 by Helge Deller + * + * This work is licensed under the GNU GPL license version 2 or later. + * + * Chip documentation is available at: + * https://parisc.wiki.kernel.org/index.php/Technical_Documentation + * + * TODO: + * - All user-added devices are currently attached to the first + * Elroy (PCI bus) only for now. To fix this additional work in + * SeaBIOS and this driver is needed. See "user_creatable" flag below. + * - GMMIO (Greater than 4 GB MMIO) register + */ + +#define TYPE_ASTRO_IOMMU_MEMORY_REGION "astro-iommu-memory-region" + +#define F_EXTEND(addr) ((addr) | MAKE_64BIT_MASK(32, 32)) + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "hw/irq.h" +#include "hw/pci/pci_device.h" +#include "hw/pci/pci_bus.h" +#include "hw/qdev-properties.h" +#include "hw/pci-host/astro.h" +#include "hw/hppa/hppa_hardware.h" +#include "migration/vmstate.h" +#include "target/hppa/cpu.h" +#include "trace.h" +#include "qom/object.h" + +/* + * Helper functions + */ + +static uint64_t mask_32bit_val(hwaddr addr, unsigned size, uint64_t val) +{ + if (size == 8) { + return val; + } + if (addr & 4) { + val >>= 32; + } else { + val = (uint32_t) val; + } + return val; +} + +static void put_val_in_int64(uint64_t *p, hwaddr addr, unsigned size, + uint64_t val) +{ + if (size == 8) { + *p = val; + } else if (size == 4) { + if (addr & 4) { + *p = ((*p << 32) >> 32) | (val << 32); + } else { + *p = ((*p >> 32) << 32) | (uint32_t) val; + } + } +} + +static void put_val_in_arrary(uint64_t *array, hwaddr start_addr, + hwaddr addr, unsigned size, uint64_t val) +{ + int index; + + index = (addr - start_addr) / 8; + put_val_in_int64(&array[index], addr, size, val); +} + + +/* + * The Elroy PCI host bridge. We have at least 4 of those under Astro. + */ + +static MemTxResult elroy_chip_read_with_attrs(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + MemTxResult ret = MEMTX_OK; + ElroyState *s = opaque; + uint64_t val = -1; + int index; + + switch ((addr >> 3) << 3) { + case 0x0008: + val = 0x6000005; /* func_class */ + break; + case 0x0058: + /* + * Scratch register, but firmware initializes it with the + * PCI BUS number and Linux/HP-UX uses it then. + */ + val = s->pci_bus_num; + /* Upper byte holds the end of this bus number */ + val |= s->pci_bus_num << 8; + break; + case 0x0080: + val = s->arb_mask; /* set ARB mask */ + break; + case 0x0108: + val = s->status_control; + break; + case 0x200 ... 0x250 - 1: /* LMMIO, GMMIO, WLMMIO, WGMMIO, ... */ + index = (addr - 0x200) / 8; + val = s->mmio_base[index]; + break; + case 0x0680: + val = s->error_config; + break; + case 0x0688: + val = 0; /* ERROR_STATUS */ + break; + case 0x0800: /* IOSAPIC_REG_SELECT */ + val = s->iosapic_reg_select; + break; + case 0x0810: /* IOSAPIC_REG_WINDOW */ + switch (s->iosapic_reg_select) { + case 0x01: /* IOSAPIC_REG_VERSION */ + val = (32 << 16) | 1; /* upper 16bit holds max entries */ + break; + default: + if (s->iosapic_reg_select < ARRAY_SIZE(s->iosapic_reg)) { + val = s->iosapic_reg[s->iosapic_reg_select]; + } else { + goto check_hf; + } + } + trace_iosapic_reg_read(s->iosapic_reg_select, size, val); + break; + default: + check_hf: + if (s->status_control & HF_ENABLE) { + val = 0; + ret = MEMTX_DECODE_ERROR; + } else { + /* return -1ULL if HardFail is disabled */ + val = ~0; + ret = MEMTX_OK; + } + } + trace_elroy_read(addr, size, val); + + /* for 32-bit accesses mask return value */ + val = mask_32bit_val(addr, size, val); + + trace_astro_chip_read(addr, size, val); + *data = val; + return ret; +} + + +static MemTxResult elroy_chip_write_with_attrs(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) +{ + ElroyState *s = opaque; + int i; + + trace_elroy_write(addr, size, val); + + switch ((addr >> 3) << 3) { + case 0x000: /* PCI_ID & PCI_COMMAND_STATUS_REG */ + break; + case 0x080: + put_val_in_int64(&s->arb_mask, addr, size, val); + break; + case 0x0108: + put_val_in_int64(&s->status_control, addr, size, val); + break; + case 0x200 ... 0x250 - 1: /* LMMIO, GMMIO, WLMMIO, WGMMIO, ... */ + put_val_in_arrary(s->mmio_base, 0x200, addr, size, val); + break; + case 0x300: /* ibase */ + case 0x308: /* imask */ + break; + case 0x0680: + put_val_in_int64(&s->error_config, addr, size, val); + break; + case 0x0800: /* IOSAPIC_REG_SELECT */ + s->iosapic_reg_select = val; + break; + case 0x0810: /* IOSAPIC_REG_WINDOW */ + trace_iosapic_reg_write(s->iosapic_reg_select, size, val); + if (s->iosapic_reg_select < ARRAY_SIZE(s->iosapic_reg)) { + s->iosapic_reg[s->iosapic_reg_select] = val; + } else { + goto check_hf; + } + break; + case 0x0840: /* IOSAPIC_REG_EOI */ + val = le64_to_cpu(val); + val &= 63; + for (i = 0; i < ELROY_IRQS; i++) { + if ((s->iosapic_reg[0x10 + 2 * i] & 63) == val) { + s->ilr &= ~(1ull << i); + } + } + break; + default: + check_hf: + if (s->status_control & HF_ENABLE) { + return MEMTX_DECODE_ERROR; + } + } + return MEMTX_OK; +} + +static const MemoryRegionOps elroy_chip_ops = { + .read_with_attrs = elroy_chip_read_with_attrs, + .write_with_attrs = elroy_chip_write_with_attrs, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, + }, +}; + + +/* Unlike pci_config_data_le_ops, no check of high bit set in config_reg. */ + +static uint64_t elroy_config_data_read(void *opaque, hwaddr addr, unsigned len) +{ + uint64_t val; + + PCIHostState *s = opaque; + val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); + trace_elroy_pci_config_data_read(s->config_reg | (addr & 3), len, val); + return val; +} + +static void elroy_config_data_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) +{ + PCIHostState *s = opaque; + pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); + trace_elroy_pci_config_data_write(s->config_reg | (addr & 3), len, val); +} + +static const MemoryRegionOps elroy_config_data_ops = { + .read = elroy_config_data_read, + .write = elroy_config_data_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t elroy_config_addr_read(void *opaque, hwaddr addr, unsigned len) +{ + ElroyState *s = opaque; + return s->config_reg_elroy; +} + +static void elroy_config_addr_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) +{ + PCIHostState *s = opaque; + ElroyState *es = opaque; + es->config_reg_elroy = val; /* keep a copy of original value */ + s->config_reg = val; +} + +static const MemoryRegionOps elroy_config_addr_ops = { + .read = elroy_config_addr_read, + .write = elroy_config_addr_write, + .valid.min_access_size = 4, + .valid.max_access_size = 8, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + + +/* Handle PCI-to-system address translation. */ +static IOMMUTLBEntry astro_translate_iommu(IOMMUMemoryRegion *iommu, + hwaddr addr, + IOMMUAccessFlags flag, + int iommu_idx) +{ + AstroState *s = container_of(iommu, AstroState, iommu); + hwaddr pdir_ptr, index, ibase; + hwaddr addr_mask = 0xfff; /* 4k translation */ + uint64_t entry; + +#define IOVP_SHIFT 12 /* equals PAGE_SHIFT */ +#define PDIR_INDEX(iovp) ((iovp) >> IOVP_SHIFT) +#define SBA_PDIR_VALID_BIT 0x8000000000000000ULL + + addr &= ~addr_mask; + + /* + * Default translation: "32-bit PCI Addressing on 40-bit Runway". + * For addresses in the 32-bit memory address range ... and then + * language which not-coincidentally matches the PSW.W=0 mapping. + */ + if (addr <= UINT32_MAX) { + entry = hppa_abs_to_phys_pa2_w0(addr); + } else { + entry = addr; + } + + /* "range enable" flag cleared? */ + if ((s->tlb_ibase & 1) == 0) { + goto skip; + } + + ibase = s->tlb_ibase & ~1ULL; + if ((addr & s->tlb_imask) != ibase) { + /* do not translate this one! */ + goto skip; + } + + index = PDIR_INDEX(addr); + pdir_ptr = s->tlb_pdir_base + index * sizeof(entry); + entry = ldq_le_phys(&address_space_memory, pdir_ptr); + + if (!(entry & SBA_PDIR_VALID_BIT)) { /* I/O PDIR entry valid ? */ + /* failure */ + return (IOMMUTLBEntry) { .perm = IOMMU_NONE }; + } + + entry &= ~SBA_PDIR_VALID_BIT; + entry >>= IOVP_SHIFT; + entry <<= 12; + + skip: + return (IOMMUTLBEntry) { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = entry, + .addr_mask = addr_mask, + .perm = IOMMU_RW, + }; +} + +static AddressSpace *elroy_pcihost_set_iommu(PCIBus *bus, void *opaque, + int devfn) +{ + ElroyState *s = opaque; + return &s->astro->iommu_as; +} + +static const PCIIOMMUOps elroy_pcihost_iommu_ops = { + .get_address_space = elroy_pcihost_set_iommu, +}; + +/* + * Encoding in IOSAPIC: + * base_addr == 0xfffa0000, we want to get 0xa0ff0000. + * eid 0x0ff00000 -> 0x00ff0000 + * id 0x000ff000 -> 0xff000000 + */ +#define SWIZZLE_HPA(a) \ + ((((a) & 0x0ff00000) >> 4) | (((a) & 0x000ff000) << 12)) +#define UNSWIZZLE_HPA(a) \ + (((((a) << 4) & 0x0ff00000) | (((a) >> 12) & 0x000ff000) | 0xf0000000)) + +/* bits in the "low" I/O Sapic IRdT entry */ +#define IOSAPIC_IRDT_DISABLE 0x10000 /* if bit is set, mask this irq */ +#define IOSAPIC_IRDT_PO_LOW 0x02000 +#define IOSAPIC_IRDT_LEVEL_TRIG 0x08000 +#define IOSAPIC_IRDT_MODE_LPRI 0x00100 + +#define CPU_IRQ_OFFSET 2 + +static void elroy_set_irq(void *opaque, int irq, int level) +{ + ElroyState *s = opaque; + uint32_t bit; + uint32_t old_ilr = s->ilr; + hwaddr cpu_hpa; + uint32_t val; + + val = s->iosapic_reg[0x10 + 2 * irq]; + cpu_hpa = s->iosapic_reg[0x11 + 2 * irq]; + /* low nibble of val has value to write into CPU irq reg */ + bit = 1u << (val & (ELROY_IRQS - 1)); + cpu_hpa = UNSWIZZLE_HPA(cpu_hpa); + + if (level && (!(val & IOSAPIC_IRDT_DISABLE)) && cpu_hpa) { + uint32_t ena = bit & ~old_ilr; + s->ilr = old_ilr | bit; + if (ena != 0) { + stl_be_phys(&address_space_memory, F_EXTEND(cpu_hpa), val & 63); + } + } else { + s->ilr = old_ilr & ~bit; + } +} + +static int elroy_pci_map_irq(PCIDevice *d, int irq_num) +{ + int slot = PCI_SLOT(d->devfn); + + assert(irq_num >= 0 && irq_num < ELROY_IRQS); + return slot & (ELROY_IRQS - 1); +} + +static void elroy_reset(DeviceState *dev) +{ + ElroyState *s = ELROY_PCI_HOST_BRIDGE(dev); + int irq; + + /* + * Make sure to disable interrupts at reboot, otherwise the Linux kernel + * serial8250_config_port() in drivers/tty/serial/8250/8250_port.c + * will hang during autoconfig(). + */ + s->ilr = 0; + for (irq = 0; irq < ELROY_IRQS; irq++) { + s->iosapic_reg[0x10 + 2 * irq] = IOSAPIC_IRDT_PO_LOW | + IOSAPIC_IRDT_LEVEL_TRIG | (irq + CPU_IRQ_OFFSET) | + IOSAPIC_IRDT_DISABLE; + s->iosapic_reg[0x11 + 2 * irq] = SWIZZLE_HPA(CPU_HPA); + } +} + +static void elroy_pcihost_init(Object *obj) +{ + ElroyState *s = ELROY_PCI_HOST_BRIDGE(obj); + PCIHostState *phb = PCI_HOST_BRIDGE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + /* Elroy config access from CPU. */ + memory_region_init_io(&s->this_mem, OBJECT(s), &elroy_chip_ops, + s, "elroy", 0x2000); + + /* Elroy PCI config. */ + memory_region_init_io(&phb->conf_mem, OBJECT(phb), + &elroy_config_addr_ops, DEVICE(s), + "pci-conf-idx", 8); + memory_region_init_io(&phb->data_mem, OBJECT(phb), + &elroy_config_data_ops, DEVICE(s), + "pci-conf-data", 8); + memory_region_add_subregion(&s->this_mem, 0x40, + &phb->conf_mem); + memory_region_add_subregion(&s->this_mem, 0x48, + &phb->data_mem); + + /* Elroy PCI bus memory. */ + memory_region_init(&s->pci_mmio, OBJECT(s), "pci-mmio", UINT64_MAX); + memory_region_init_io(&s->pci_io, OBJECT(s), &unassigned_io_ops, obj, + "pci-isa-mmio", + ((uint32_t) IOS_DIST_BASE_SIZE) / ROPES_PER_IOC); + + phb->bus = pci_register_root_bus(DEVICE(s), "pci", + elroy_set_irq, elroy_pci_map_irq, s, + &s->pci_mmio, &s->pci_io, + PCI_DEVFN(0, 0), ELROY_IRQS, TYPE_PCI_BUS); + + sysbus_init_mmio(sbd, &s->this_mem); + + qdev_init_gpio_in(DEVICE(obj), elroy_set_irq, ELROY_IRQS); +} + +static Property elroy_pcihost_properties[] = { + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_elroy = { + .name = "Elroy", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(hpa, ElroyState), + VMSTATE_UINT32(pci_bus_num, ElroyState), + VMSTATE_UINT64(config_address, ElroyState), + VMSTATE_UINT64(config_reg_elroy, ElroyState), + VMSTATE_UINT64(status_control, ElroyState), + VMSTATE_UINT64(arb_mask, ElroyState), + VMSTATE_UINT64_ARRAY(mmio_base, ElroyState, (0x0250 - 0x200) / 8), + VMSTATE_UINT64(error_config, ElroyState), + VMSTATE_UINT32(iosapic_reg_select, ElroyState), + VMSTATE_UINT64_ARRAY(iosapic_reg, ElroyState, 0x20), + VMSTATE_UINT32(ilr, ElroyState), + VMSTATE_END_OF_LIST() + } +}; + +static void elroy_pcihost_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = elroy_reset; + device_class_set_props(dc, elroy_pcihost_properties); + dc->vmsd = &vmstate_elroy; + dc->user_creatable = false; +} + +static const TypeInfo elroy_pcihost_info = { + .name = TYPE_ELROY_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_init = elroy_pcihost_init, + .instance_size = sizeof(ElroyState), + .class_init = elroy_pcihost_class_init, +}; + +static void elroy_register_types(void) +{ + type_register_static(&elroy_pcihost_info); +} + +type_init(elroy_register_types) + + +static ElroyState *elroy_init(int num) +{ + DeviceState *dev; + + dev = qdev_new(TYPE_ELROY_PCI_HOST_BRIDGE); + dev->id = g_strdup_printf("elroy%d", num); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + return ELROY_PCI_HOST_BRIDGE(dev); +} + +/* + * Astro Runway chip. + */ + +static MemTxResult astro_chip_read_with_attrs(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + AstroState *s = opaque; + MemTxResult ret = MEMTX_OK; + uint64_t val = -1; + int index; + + switch ((addr >> 3) << 3) { + /* R2I registers */ + case 0x0000: /* ID */ + val = (0x01 << 3) | 0x01ULL; + break; + case 0x0008: /* IOC_CTRL */ + val = s->ioc_ctrl; + break; + case 0x0010: /* TOC_CLIENT_ID */ + break; + case 0x0030: /* HP-UX 10.20 and 11.11 reads it. No idea. */ + val = -1; + break; + case 0x0078: /* NetBSD reads 0x78 ? */ + val = -1; + break; + case 0x0300 ... 0x03d8: /* LMMIO_DIRECT0_BASE... */ + index = (addr - 0x300) / 8; + val = s->ioc_ranges[index]; + break; + case 0x10200: + val = 0; + break; + case 0x10220: + case 0x10230: /* HP-UX 11.11 reads it. No idea. */ + val = -1; + break; + case 0x22108: /* IOC STATUS_CONTROL */ + val = s->ioc_status_ctrl; + break; + case 0x20200 ... 0x20240 - 1: /* IOC Rope0_Control ... */ + index = (addr - 0x20200) / 8; + val = s->ioc_rope_control[index]; + break; + case 0x20040: /* IOC Rope config */ + val = s->ioc_rope_config; + break; + case 0x20050: /* IOC Rope debug */ + val = 0; + break; + case 0x20108: /* IOC STATUS_CONTROL */ + val = s->ioc_status_control; + break; + case 0x20310: /* IOC_PCOM */ + val = s->tlb_pcom; + /* TODO: flush iommu */ + break; + case 0x20400: + val = s->ioc_flush_control; + break; + /* empty placeholders for non-existent elroys */ +#define EMPTY_PORT(x) case x: case x+8: val = 0; break; \ + case x+40: case x+48: val = UINT64_MAX; break; + EMPTY_PORT(0x30000) + EMPTY_PORT(0x32000) + EMPTY_PORT(0x34000) + EMPTY_PORT(0x36000) + EMPTY_PORT(0x38000) + EMPTY_PORT(0x3a000) + EMPTY_PORT(0x3c000) + EMPTY_PORT(0x3e000) +#undef EMPTY_PORT + + default: + val = 0; + ret = MEMTX_DECODE_ERROR; + } + + /* for 32-bit accesses mask return value */ + val = mask_32bit_val(addr, size, val); + + trace_astro_chip_read(addr, size, val); + *data = val; + return ret; +} + +static MemTxResult astro_chip_write_with_attrs(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) +{ + MemTxResult ret = MEMTX_OK; + AstroState *s = opaque; + + trace_astro_chip_write(addr, size, val); + + switch ((addr >> 3) << 3) { + case 0x0000: /* ID */ + break; + case 0x0008: /* IOC_CTRL */ + val &= 0x0ffffff; + put_val_in_int64(&s->ioc_ctrl, addr, size, val); + break; + case 0x0010: /* TOC_CLIENT_ID */ + break; + case 0x0030: /* HP-UX 10.20 and 11.11 reads it. No idea. */ + break; + case 0x0300 ... 0x03d8 - 1: /* LMMIO_DIRECT0_BASE... */ + put_val_in_arrary(s->ioc_ranges, 0x300, addr, size, val); + break; + case 0x10200: + case 0x10220: + case 0x10230: /* HP-UX 11.11 reads it. No idea. */ + break; + case 0x20200 ... 0x20240 - 1: /* IOC Rope0_Control ... */ + put_val_in_arrary(s->ioc_rope_control, 0x20200, addr, size, val); + break; + case 0x20040: /* IOC Rope config */ + case 0x22040: + put_val_in_int64(&s->ioc_rope_config, addr, size, val); + break; + case 0x20300: + case 0x22300: + put_val_in_int64(&s->tlb_ibase, addr, size, val); + break; + case 0x20308: + case 0x22308: + put_val_in_int64(&s->tlb_imask, addr, size, val); + break; + case 0x20310: + case 0x22310: + put_val_in_int64(&s->tlb_pcom, addr, size, val); + /* TODO: flush iommu */ + break; + case 0x20318: + case 0x22318: + put_val_in_int64(&s->tlb_tcnfg, addr, size, val); + break; + case 0x20320: + case 0x22320: + put_val_in_int64(&s->tlb_pdir_base, addr, size, val); + break; + case 0x22000: /* func_id */ + break; + case 0x22008: /* func_class */ + break; + case 0x22050: /* rope_debug */ + break; + case 0x22108: /* IOC STATUS_CONTROL */ + put_val_in_int64(&s->ioc_status_ctrl, addr, size, val); + break; + /* + * empty placeholders for non-existent elroys, e.g. + * func_class, pci config & data + */ +#define EMPTY_PORT(x) case x: case x+8: case x+0x40: case x+0x48: + EMPTY_PORT(0x30000) + EMPTY_PORT(0x32000) + EMPTY_PORT(0x34000) + EMPTY_PORT(0x36000) + EMPTY_PORT(0x38000) + EMPTY_PORT(0x3a000) + EMPTY_PORT(0x3c000) + EMPTY_PORT(0x3e000) + break; +#undef EMPTY_PORT + + default: + ret = MEMTX_DECODE_ERROR; + } + return ret; +} + +static const MemoryRegionOps astro_chip_ops = { + .read_with_attrs = astro_chip_read_with_attrs, + .write_with_attrs = astro_chip_write_with_attrs, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, + }, +}; + +static const VMStateDescription vmstate_astro = { + .name = "Astro", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(ioc_ctrl, AstroState), + VMSTATE_UINT64(ioc_status_ctrl, AstroState), + VMSTATE_UINT64_ARRAY(ioc_ranges, AstroState, (0x03d8 - 0x300) / 8), + VMSTATE_UINT64(ioc_rope_config, AstroState), + VMSTATE_UINT64(ioc_status_control, AstroState), + VMSTATE_UINT64(ioc_flush_control, AstroState), + VMSTATE_UINT64_ARRAY(ioc_rope_control, AstroState, 8), + VMSTATE_UINT64(tlb_ibase, AstroState), + VMSTATE_UINT64(tlb_imask, AstroState), + VMSTATE_UINT64(tlb_pcom, AstroState), + VMSTATE_UINT64(tlb_tcnfg, AstroState), + VMSTATE_UINT64(tlb_pdir_base, AstroState), + VMSTATE_END_OF_LIST() + } +}; + +static void astro_reset(DeviceState *dev) +{ + AstroState *s = ASTRO_CHIP(dev); + int i; + + s->ioc_ctrl = 0x29cf; + s->ioc_rope_config = 0xc5f; + s->ioc_flush_control = 0xb03; + s->ioc_status_control = 0; + memset(&s->ioc_rope_control, 0, sizeof(s->ioc_rope_control)); + + /* + * The SBA BASE/MASK registers control CPU -> IO routing. + * The LBA BASE/MASK registers control IO -> System routing (in Elroy) + */ + memset(&s->ioc_ranges, 0, sizeof(s->ioc_ranges)); + s->ioc_ranges[(0x360 - 0x300) / 8] = LMMIO_DIST_BASE_ADDR | 0x01; /* LMMIO_DIST_BASE (SBA) */ + s->ioc_ranges[(0x368 - 0x300) / 8] = 0xfc000000; /* LMMIO_DIST_MASK */ + s->ioc_ranges[(0x370 - 0x300) / 8] = 0; /* LMMIO_DIST_ROUTE */ + s->ioc_ranges[(0x390 - 0x300) / 8] = IOS_DIST_BASE_ADDR | 0x01; /* IOS_DIST_BASE */ + s->ioc_ranges[(0x398 - 0x300) / 8] = 0xffffff0000; /* IOS_DIST_MASK */ + s->ioc_ranges[(0x3a0 - 0x300) / 8] = 0x3400000000000000ULL; /* IOS_DIST_ROUTE */ + s->ioc_ranges[(0x3c0 - 0x300) / 8] = 0xfffee00000; /* IOS_DIRECT_BASE */ + s->ioc_ranges[(0x3c8 - 0x300) / 8] = 0xffffff0000; /* IOS_DIRECT_MASK */ + s->ioc_ranges[(0x3d0 - 0x300) / 8] = 0x0; /* IOS_DIRECT_ROUTE */ + + s->tlb_ibase = 0; + s->tlb_imask = 0; + s->tlb_pcom = 0; + s->tlb_tcnfg = 0; + s->tlb_pdir_base = 0; + + for (i = 0; i < ELROY_NUM; i++) { + elroy_reset(DEVICE(s->elroy[i])); + } +} + +static void astro_init(Object *obj) +{ +} + +static void astro_realize(DeviceState *obj, Error **errp) +{ + AstroState *s = ASTRO_CHIP(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + int i; + + memory_region_init_io(&s->this_mem, OBJECT(s), &astro_chip_ops, + s, "astro", 0x40000); + sysbus_init_mmio(sbd, &s->this_mem); + + /* Host memory as seen from Elroys PCI side, via the IOMMU. */ + memory_region_init_iommu(&s->iommu, sizeof(s->iommu), + TYPE_ASTRO_IOMMU_MEMORY_REGION, OBJECT(s), + "iommu-astro", UINT64_MAX); + address_space_init(&s->iommu_as, MEMORY_REGION(&s->iommu), + "bm-pci"); + + /* Create Elroys (PCI host bus chips). */ + for (i = 0; i < ELROY_NUM; i++) { + static const int elroy_hpa_offsets[ELROY_NUM] = { + 0x30000, 0x32000, 0x38000, 0x3c000 }; + static const char elroy_rope_nr[ELROY_NUM] = { + 0, 1, 4, 6 }; /* busnum path, e.g. [10:6] */ + int addr_offset; + ElroyState *elroy; + hwaddr map_addr; + uint64_t map_size; + int rope; + + addr_offset = elroy_hpa_offsets[i]; + rope = elroy_rope_nr[i]; + + elroy = elroy_init(i); + s->elroy[i] = elroy; + elroy->hpa = ASTRO_HPA + addr_offset; + elroy->pci_bus_num = i; + elroy->astro = s; + + /* + * NOTE: we only allow PCI devices on first Elroy for now. + * SeaBIOS will not find devices on the other busses. + */ + if (i > 0) { + qbus_mark_full(&PCI_HOST_BRIDGE(elroy)->bus->qbus); + } + + /* map elroy config addresses into Astro space */ + memory_region_add_subregion(&s->this_mem, addr_offset, + &elroy->this_mem); + + /* LMMIO */ + elroy->mmio_base[(0x0200 - 0x200) / 8] = 0xf0000001; + elroy->mmio_base[(0x0208 - 0x200) / 8] = 0xf8000000; + /* GMMIO */ + elroy->mmio_base[(0x0210 - 0x200) / 8] = 0x000000f800000001; + elroy->mmio_base[(0x0218 - 0x200) / 8] = 0x000000ff80000000; + /* WLMMIO */ + elroy->mmio_base[(0x0220 - 0x200) / 8] = 0xf0000001; + elroy->mmio_base[(0x0228 - 0x200) / 8] = 0xf0000000; + /* WGMMIO */ + elroy->mmio_base[(0x0230 - 0x200) / 8] = 0x000000f800000001; + elroy->mmio_base[(0x0238 - 0x200) / 8] = 0x000000fc00000000; + /* IOS_BASE */ + map_size = IOS_DIST_BASE_SIZE / ROPES_PER_IOC; + elroy->mmio_base[(0x0240 - 0x200) / 8] = rope * map_size | 0x01; + elroy->mmio_base[(0x0248 - 0x200) / 8] = 0x0000e000; + + /* map elroys mmio */ + map_size = LMMIO_DIST_BASE_SIZE / ROPES_PER_IOC; + map_addr = F_EXTEND(LMMIO_DIST_BASE_ADDR + rope * map_size); + memory_region_init_alias(&elroy->pci_mmio_alias, OBJECT(elroy), + "pci-mmio-alias", + &elroy->pci_mmio, (uint32_t) map_addr, map_size); + memory_region_add_subregion(get_system_memory(), map_addr, + &elroy->pci_mmio_alias); + + /* map elroys io */ + map_size = IOS_DIST_BASE_SIZE / ROPES_PER_IOC; + map_addr = F_EXTEND(IOS_DIST_BASE_ADDR + rope * map_size); + memory_region_add_subregion(get_system_memory(), map_addr, + &elroy->pci_io); + + /* Host memory as seen from the PCI side, via the IOMMU. */ + pci_setup_iommu(PCI_HOST_BRIDGE(elroy)->bus, &elroy_pcihost_iommu_ops, + elroy); + } +} + +static void astro_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = astro_reset; + dc->vmsd = &vmstate_astro; + dc->realize = astro_realize; + /* + * astro with elroys are hard part of the newer PA2.0 machines and can not + * be created without that hardware + */ + dc->user_creatable = false; +} + +static const TypeInfo astro_chip_info = { + .name = TYPE_ASTRO_CHIP, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = astro_init, + .instance_size = sizeof(AstroState), + .class_init = astro_class_init, +}; + +static void astro_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = astro_translate_iommu; +} + +static const TypeInfo astro_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_ASTRO_IOMMU_MEMORY_REGION, + .class_init = astro_iommu_memory_region_class_init, +}; + + +static void astro_register_types(void) +{ + type_register_static(&astro_chip_info); + type_register_static(&astro_iommu_memory_region_info); +} + +type_init(astro_register_types) diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index a57e81e3a9..1f0c435348 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -42,12 +42,12 @@ #include "qemu/units.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/irq.h" #include "hw/mips/mips.h" +#include "hw/pci-host/bonito.h" #include "hw/pci/pci_host.h" #include "migration/vmstate.h" -#include "sysemu/reset.h" #include "sysemu/runstate.h" #include "hw/misc/unimp.h" #include "hw/registerfields.h" @@ -62,7 +62,7 @@ #define DPRINTF(fmt, ...) #endif -/* from linux soure code. include/asm-mips/mips-boards/bonito64.h*/ +/* from linux source 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) @@ -239,9 +239,6 @@ struct BonitoState { MemoryRegion pci_mem; }; -#define TYPE_BONITO_PCI_HOST_BRIDGE "Bonito-pcihost" -OBJECT_DECLARE_SIMPLE_TYPE(BonitoState, BONITO_PCI_HOST_BRIDGE) - #define TYPE_PCI_BONITO "Bonito" OBJECT_DECLARE_SIMPLE_TYPE(PCIBonitoState, PCI_BONITO) @@ -254,7 +251,7 @@ static void bonito_writel(void *opaque, hwaddr addr, saddr = addr >> 2; - DPRINTF("bonito_writel "TARGET_FMT_plx" val %lx saddr %x\n", + DPRINTF("bonito_writel "HWADDR_FMT_plx" val %lx saddr %x\n", addr, val, saddr); switch (saddr) { case BONITO_BONPONCFG: @@ -317,7 +314,7 @@ static uint64_t bonito_readl(void *opaque, hwaddr addr, saddr = addr >> 2; - DPRINTF("bonito_readl "TARGET_FMT_plx"\n", addr); + DPRINTF("bonito_readl "HWADDR_FMT_plx"\n", addr); switch (saddr) { case BONITO_INTISR: return s->regs[saddr]; @@ -342,7 +339,7 @@ static void bonito_pciconf_writel(void *opaque, hwaddr addr, PCIBonitoState *s = opaque; PCIDevice *d = PCI_DEVICE(s); - DPRINTF("bonito_pciconf_writel "TARGET_FMT_plx" val %lx\n", addr, val); + DPRINTF("bonito_pciconf_writel "HWADDR_FMT_plx" val %lx\n", addr, val); d->config_write(d, addr, val, 4); } @@ -353,7 +350,7 @@ static uint64_t bonito_pciconf_readl(void *opaque, hwaddr addr, PCIBonitoState *s = opaque; PCIDevice *d = PCI_DEVICE(s); - DPRINTF("bonito_pciconf_readl "TARGET_FMT_plx"\n", addr); + DPRINTF("bonito_pciconf_readl "HWADDR_FMT_plx"\n", addr); return d->config_read(d, addr, 4); } @@ -469,7 +466,7 @@ static uint32_t bonito_sbridge_pciaddr(void *opaque, hwaddr addr) regno = (cfgaddr & BONITO_PCICONF_REG_MASK_HW) >> BONITO_PCICONF_REG_OFFSET; if (idsel == 0) { - error_report("error in bonito pci config address 0x" TARGET_FMT_plx + error_report("error in bonito pci config address 0x" HWADDR_FMT_plx ",pcimap_cfg=0x%x", addr, s->regs[BONITO_PCIMAP_CFG]); exit(1); } @@ -489,7 +486,7 @@ static void bonito_spciconf_write(void *opaque, hwaddr addr, uint64_t val, uint32_t pciaddr; uint16_t status; - DPRINTF("bonito_spciconf_write "TARGET_FMT_plx" size %d val %lx\n", + DPRINTF("bonito_spciconf_write "HWADDR_FMT_plx" size %d val %lx\n", addr, size, val); pciaddr = bonito_sbridge_pciaddr(s, addr); @@ -519,7 +516,7 @@ static uint64_t bonito_spciconf_read(void *opaque, hwaddr addr, unsigned size) uint32_t pciaddr; uint16_t status; - DPRINTF("bonito_spciconf_read "TARGET_FMT_plx" size %d\n", addr, size); + DPRINTF("bonito_spciconf_read "HWADDR_FMT_plx" size %d\n", addr, size); pciaddr = bonito_sbridge_pciaddr(s, addr); @@ -593,9 +590,9 @@ static int pci_bonito_map_irq(PCIDevice *pci_dev, int irq_num) } } -static void bonito_reset(void *opaque) +static void bonito_reset_hold(Object *obj) { - PCIBonitoState *s = opaque; + PCIBonitoState *s = PCI_BONITO(obj); uint32_t val = 0; /* set the default value of north bridge registers */ @@ -622,13 +619,13 @@ static const VMStateDescription vmstate_bonito = { .name = "Bonito", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, PCIBonitoState), VMSTATE_END_OF_LIST() } }; -static void bonito_pcihost_realize(DeviceState *dev, Error **errp) +static void bonito_host_realize(DeviceState *dev, Error **errp) { PCIHostState *phb = PCI_HOST_BRIDGE(dev); BonitoState *bs = BONITO_PCI_HOST_BRIDGE(dev); @@ -654,12 +651,12 @@ static void bonito_pcihost_realize(DeviceState *dev, Error **errp) create_unimplemented_device("pci.io", BONITO_PCIIO_BASE, 1 * MiB); } -static void bonito_realize(PCIDevice *dev, Error **errp) +static void bonito_pci_realize(PCIDevice *dev, Error **errp) { PCIBonitoState *s = PCI_BONITO(dev); - SysBusDevice *sysbus = SYS_BUS_DEVICE(s->pcihost); + MemoryRegion *host_mem = get_system_memory(); PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - BonitoState *bs = BONITO_PCI_HOST_BRIDGE(s->pcihost); + BonitoState *bs = s->pcihost; MemoryRegion *pcimem_alias = g_new(MemoryRegion, 1); /* @@ -671,48 +668,45 @@ static void bonito_realize(PCIDevice *dev, Error **errp) /* set the north bridge register mapping */ memory_region_init_io(&s->iomem, OBJECT(s), &bonito_ops, s, "north-bridge-register", BONITO_INTERNAL_REG_SIZE); - sysbus_init_mmio(sysbus, &s->iomem); - sysbus_mmio_map(sysbus, 0, BONITO_INTERNAL_REG_BASE); + memory_region_add_subregion(host_mem, BONITO_INTERNAL_REG_BASE, &s->iomem); /* set the north bridge pci configure mapping */ memory_region_init_io(&phb->conf_mem, OBJECT(s), &bonito_pciconf_ops, s, "north-bridge-pci-config", BONITO_PCICONFIG_SIZE); - sysbus_init_mmio(sysbus, &phb->conf_mem); - sysbus_mmio_map(sysbus, 1, BONITO_PCICONFIG_BASE); + memory_region_add_subregion(host_mem, BONITO_PCICONFIG_BASE, + &phb->conf_mem); /* set the south bridge pci configure mapping */ memory_region_init_io(&phb->data_mem, OBJECT(s), &bonito_spciconf_ops, s, "south-bridge-pci-config", BONITO_SPCICONFIG_SIZE); - sysbus_init_mmio(sysbus, &phb->data_mem); - sysbus_mmio_map(sysbus, 2, BONITO_SPCICONFIG_BASE); + memory_region_add_subregion(host_mem, BONITO_SPCICONFIG_BASE, + &phb->data_mem); create_unimplemented_device("bonito", BONITO_REG_BASE, BONITO_REG_SIZE); memory_region_init_io(&s->iomem_ldma, OBJECT(s), &bonito_ldma_ops, s, "ldma", 0x100); - sysbus_init_mmio(sysbus, &s->iomem_ldma); - sysbus_mmio_map(sysbus, 3, 0x1fe00200); + memory_region_add_subregion(host_mem, 0x1fe00200, &s->iomem_ldma); /* PCI copier */ memory_region_init_io(&s->iomem_cop, OBJECT(s), &bonito_cop_ops, s, "cop", 0x100); - sysbus_init_mmio(sysbus, &s->iomem_cop); - sysbus_mmio_map(sysbus, 4, 0x1fe00300); + memory_region_add_subregion(host_mem, 0x1fe00300, &s->iomem_cop); create_unimplemented_device("ROMCS", BONITO_FLASH_BASE, 60 * MiB); /* Map PCI IO Space 0x1fd0 0000 - 0x1fd1 0000 */ memory_region_init_alias(&s->bonito_pciio, OBJECT(s), "isa_mmio", get_system_io(), 0, BONITO_PCIIO_SIZE); - sysbus_init_mmio(sysbus, &s->bonito_pciio); - sysbus_mmio_map(sysbus, 5, BONITO_PCIIO_BASE); + memory_region_add_subregion(host_mem, BONITO_PCIIO_BASE, + &s->bonito_pciio); /* add pci local io mapping */ memory_region_init_alias(&s->bonito_localio, OBJECT(s), "IOCS[0]", get_system_io(), 0, 256 * KiB); - sysbus_init_mmio(sysbus, &s->bonito_localio); - sysbus_mmio_map(sysbus, 6, BONITO_DEV_BASE); + memory_region_add_subregion(host_mem, BONITO_DEV_BASE, + &s->bonito_localio); create_unimplemented_device("IOCS[1]", BONITO_DEV_BASE + 1 * 256 * KiB, 256 * KiB); create_unimplemented_device("IOCS[2]", BONITO_DEV_BASE + 2 * 256 * KiB, @@ -722,8 +716,7 @@ static void bonito_realize(PCIDevice *dev, Error **errp) memory_region_init_alias(pcimem_alias, NULL, "pci.mem.alias", &bs->pci_mem, 0, BONITO_PCIHI_SIZE); - memory_region_add_subregion(get_system_memory(), - BONITO_PCIHI_BASE, pcimem_alias); + memory_region_add_subregion(host_mem, BONITO_PCIHI_BASE, pcimem_alias); create_unimplemented_device("PCI_2", (hwaddr)BONITO_PCIHI_BASE + BONITO_PCIHI_SIZE, 2 * GiB); @@ -739,8 +732,6 @@ static void bonito_realize(PCIDevice *dev, Error **errp) pci_set_byte(dev->config + PCI_MIN_GNT, 0x3c); pci_set_byte(dev->config + PCI_MAX_LAT, 0x00); - - qemu_register_reset(bonito_reset, s); } PCIBus *bonito_init(qemu_irq *pic) @@ -766,12 +757,14 @@ PCIBus *bonito_init(qemu_irq *pic) return phb->bus; } -static void bonito_class_init(ObjectClass *klass, void *data) +static void bonito_pci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); - k->realize = bonito_realize; + rc->phases.hold = bonito_reset_hold; + k->realize = bonito_pci_realize; k->vendor_id = 0xdf53; k->device_id = 0x00d5; k->revision = 0x01; @@ -785,35 +778,35 @@ static void bonito_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; } -static const TypeInfo bonito_info = { +static const TypeInfo bonito_pci_info = { .name = TYPE_PCI_BONITO, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIBonitoState), - .class_init = bonito_class_init, + .class_init = bonito_pci_class_init, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static void bonito_pcihost_class_init(ObjectClass *klass, void *data) +static void bonito_host_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->realize = bonito_pcihost_realize; + dc->realize = bonito_host_realize; } -static const TypeInfo bonito_pcihost_info = { +static const TypeInfo bonito_host_info = { .name = TYPE_BONITO_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(BonitoState), - .class_init = bonito_pcihost_class_init, + .class_init = bonito_host_class_init, }; static void bonito_register_types(void) { - type_register_static(&bonito_pcihost_info); - type_register_static(&bonito_info); + type_register_static(&bonito_host_info); + type_register_static(&bonito_pci_info); } type_init(bonito_register_types) diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c index bde3a343a2..c25d50f1c6 100644 --- a/hw/pci-host/designware.c +++ b/hw/pci-host/designware.c @@ -340,6 +340,8 @@ static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address, break; case DESIGNWARE_PCIE_ATU_VIEWPORT: + val &= DESIGNWARE_PCIE_ATU_REGION_INBOUND | + (DESIGNWARE_PCIE_NUM_VIEWPORTS - 1); root->atu_viewport = val; break; @@ -488,7 +490,7 @@ static void designware_pcie_root_realize(PCIDevice *dev, Error **errp) /* * If no inbound iATU windows are configured, HW defaults to - * letting inbound TLPs to pass in. We emulate that by exlicitly + * letting inbound TLPs to pass in. We emulate that by explicitly * configuring first inbound window to cover all of target's * address space. * @@ -503,7 +505,7 @@ static void designware_pcie_root_realize(PCIDevice *dev, Error **errp) &designware_pci_host_msi_ops, root, "pcie-msi", 0x4); /* - * We initially place MSI interrupt I/O region a adress 0 and + * We initially place MSI interrupt I/O region at address 0 and * disable it. It'll be later moved to correct offset and enabled * in designware_pcie_root_update_msi_mapping() as a part of * initialization done by guest OS @@ -529,7 +531,7 @@ static const VMStateDescription vmstate_designware_pcie_msi_bank = { .name = "designware-pcie-msi-bank", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(enable, DesignwarePCIEMSIBank), VMSTATE_UINT32(mask, DesignwarePCIEMSIBank), VMSTATE_UINT32(status, DesignwarePCIEMSIBank), @@ -541,7 +543,7 @@ static const VMStateDescription vmstate_designware_pcie_msi = { .name = "designware-pcie-msi", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(base, DesignwarePCIEMSI), VMSTATE_STRUCT_ARRAY(intr, DesignwarePCIEMSI, @@ -557,7 +559,7 @@ static const VMStateDescription vmstate_designware_pcie_viewport = { .name = "designware-pcie-viewport", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(base, DesignwarePCIEViewport), VMSTATE_UINT64(target, DesignwarePCIEViewport), VMSTATE_UINT32(limit, DesignwarePCIEViewport), @@ -570,7 +572,7 @@ static const VMStateDescription vmstate_designware_pcie_root = { .name = "designware-pcie-root", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PCIBridge), VMSTATE_UINT32(atu_viewport, DesignwarePCIERoot), VMSTATE_STRUCT_2DARRAY(viewports, @@ -600,7 +602,6 @@ static void designware_pcie_root_class_init(ObjectClass *klass, void *data) k->device_id = 0xABCD; k->revision = 0; k->class_id = PCI_CLASS_BRIDGE_PCI; - k->is_bridge = true; k->exit = pci_bridge_exitfn; k->realize = designware_pcie_root_realize; k->config_read = designware_pcie_root_config_read; @@ -664,6 +665,10 @@ static AddressSpace *designware_pcie_host_set_iommu(PCIBus *bus, void *opaque, return &s->pci.address_space; } +static const PCIIOMMUOps designware_iommu_ops = { + .get_address_space = designware_pcie_host_set_iommu, +}; + static void designware_pcie_host_realize(DeviceState *dev, Error **errp) { PCIHostState *pci = PCI_HOST_BRIDGE(dev); @@ -695,6 +700,7 @@ static void designware_pcie_host_realize(DeviceState *dev, Error **errp) &s->pci.io, 0, 4, TYPE_PCIE_BUS); + pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; memory_region_init(&s->pci.address_space_root, OBJECT(s), @@ -705,7 +711,7 @@ static void designware_pcie_host_realize(DeviceState *dev, Error **errp) address_space_init(&s->pci.address_space, &s->pci.address_space_root, "pcie-bus-address-space"); - pci_setup_iommu(pci->bus, designware_pcie_host_set_iommu, s); + pci_setup_iommu(pci->bus, &designware_iommu_ops, s); qdev_realize(DEVICE(&s->root), BUS(pci->bus), &error_fatal); } @@ -714,7 +720,7 @@ static const VMStateDescription vmstate_designware_pcie_host = { .name = "designware-pcie-host", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(root, DesignwarePCIEHost, 1, diff --git a/hw/pci-host/dino.c b/hw/pci-host/dino.c index f257c24e64..d992c4bb69 100644 --- a/hw/pci-host/dino.c +++ b/hw/pci-host/dino.c @@ -1,5 +1,5 @@ /* - * HP-PARISC Dino PCI chipset emulation, as in B160L and similiar machines + * HP-PARISC Dino PCI chipset emulation, as in B160L and similar machines * * (C) 2017-2019 by Helge Deller * @@ -15,7 +15,7 @@ #include "qemu/units.h" #include "qapi/error.h" #include "hw/irq.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pci_bus.h" #include "hw/qdev-properties.h" #include "hw/pci-host/dino.h" @@ -287,7 +287,7 @@ static const VMStateDescription vmstate_dino = { .name = "Dino", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(iar0, DinoState), VMSTATE_UINT32(iar1, DinoState), VMSTATE_UINT32(imr, DinoState), @@ -354,6 +354,10 @@ static AddressSpace *dino_pcihost_set_iommu(PCIBus *bus, void *opaque, return &s->bm_as; } +static const PCIIOMMUOps dino_iommu_ops = { + .get_address_space = dino_pcihost_set_iommu, +}; + /* * Dino interrupts are connected as shown on Page 78, Table 23 * (Little-endian bit numbers) @@ -481,7 +485,7 @@ static void dino_pcihost_init(Object *obj) g_free(name); } - pci_setup_iommu(phb->bus, dino_pcihost_set_iommu, s); + pci_setup_iommu(phb->bus, &dino_iommu_ops, s); sysbus_init_mmio(sbd, &s->this_mem); diff --git a/hw/pci-host/gpex-acpi.c b/hw/pci-host/gpex-acpi.c index 7c7316bc96..f69413ea2c 100644 --- a/hw/pci-host/gpex-acpi.c +++ b/hw/pci-host/gpex-acpi.c @@ -177,7 +177,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) acpi_dsdt_add_pci_route_table(dev, cfg->irq); /* - * Resources defined for PXBs are composed by the folling parts: + * Resources defined for PXBs are composed of the following parts: * 1. The resources the pci-brige/pcie-root-port need. * 2. The resources the devices behind pxb need. */ @@ -281,3 +281,16 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) crs_range_set_free(&crs_range_set); } + +void acpi_dsdt_add_gpex_host(Aml *scope, uint32_t irq) +{ + bool ambig; + Object *obj = object_resolve_path_type("", TYPE_GPEX_HOST, &ambig); + + if (!obj || ambig) { + return; + } + + GPEX_HOST(obj)->gpex_cfg.irq = irq; + acpi_dsdt_add_gpex(scope, &GPEX_HOST(obj)->gpex_cfg); +} diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c index a6752fac5e..e9cf455bf5 100644 --- a/hw/pci-host/gpex.c +++ b/hw/pci-host/gpex.c @@ -154,6 +154,18 @@ static Property gpex_host_properties[] = { */ DEFINE_PROP_BOOL("allow-unmapped-accesses", GPEXHost, allow_unmapped_accesses, true), + DEFINE_PROP_UINT64(PCI_HOST_ECAM_BASE, GPEXHost, gpex_cfg.ecam.base, 0), + DEFINE_PROP_SIZE(PCI_HOST_ECAM_SIZE, GPEXHost, gpex_cfg.ecam.size, 0), + DEFINE_PROP_UINT64(PCI_HOST_PIO_BASE, GPEXHost, gpex_cfg.pio.base, 0), + DEFINE_PROP_SIZE(PCI_HOST_PIO_SIZE, GPEXHost, gpex_cfg.pio.size, 0), + DEFINE_PROP_UINT64(PCI_HOST_BELOW_4G_MMIO_BASE, GPEXHost, + gpex_cfg.mmio32.base, 0), + DEFINE_PROP_SIZE(PCI_HOST_BELOW_4G_MMIO_SIZE, GPEXHost, + gpex_cfg.mmio32.size, 0), + DEFINE_PROP_UINT64(PCI_HOST_ABOVE_4G_MMIO_BASE, GPEXHost, + gpex_cfg.mmio64.base, 0), + DEFINE_PROP_SIZE(PCI_HOST_ABOVE_4G_MMIO_SIZE, GPEXHost, + gpex_cfg.mmio64.size, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -195,7 +207,7 @@ static const VMStateDescription vmstate_gpex_root = { .name = "gpex_root", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, GPEXRootState), VMSTATE_END_OF_LIST() } diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index b05facf463..8e589ff2c9 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -24,27 +24,14 @@ */ #include "qemu/osdep.h" -#include "hw/pci/pci_host.h" -#include "hw/ppc/mac.h" #include "hw/qdev-properties.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/irq.h" #include "qapi/error.h" #include "qemu/module.h" #include "trace.h" #include "qom/object.h" - -OBJECT_DECLARE_SIMPLE_TYPE(GrackleState, GRACKLE_PCI_HOST_BRIDGE) - -struct GrackleState { - PCIHostState parent_obj; - - uint32_t ofw_addr; - qemu_irq irqs[4]; - MemoryRegion pci_mmio; - MemoryRegion pci_hole; - MemoryRegion pci_io; -}; +#include "hw/pci-host/grackle.h" /* Don't know if this matches real hardware, but it agrees with OHW. */ static int pci_grackle_map_irq(PCIDevice *pci_dev, int irq_num) @@ -104,7 +91,7 @@ static void grackle_init(Object *obj) static void grackle_pci_realize(PCIDevice *d, Error **errp) { - d->config[0x09] = 0x01; + d->config[PCI_CLASS_PROG] = 0x01; } static void grackle_pci_class_init(ObjectClass *klass, void *data) diff --git a/hw/mips/gt64xxx_pci.c b/hw/pci-host/gt64120.c similarity index 91% rename from hw/mips/gt64xxx_pci.c rename to hw/pci-host/gt64120.c index 19d0d9889f..e02efc9e2e 100644 --- a/hw/mips/gt64xxx_pci.c +++ b/hw/pci-host/gt64120.c @@ -26,8 +26,11 @@ #include "qapi/error.h" #include "qemu/units.h" #include "qemu/log.h" -#include "hw/pci/pci.h" +#include "hw/qdev-properties.h" +#include "hw/registerfields.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" +#include "hw/misc/empty_slot.h" #include "migration/vmstate.h" #include "hw/intc/i8259.h" #include "hw/irq.h" @@ -40,6 +43,9 @@ #define GT_CPU (0x000 >> 2) #define GT_MULTI (0x120 >> 2) +REG32(GT_CPU, 0x000) +FIELD(GT_CPU, Endianness, 12, 1) + /* CPU Address Decode */ #define GT_SCS10LD (0x008 >> 2) #define GT_SCS10HD (0x010 >> 2) @@ -209,6 +215,17 @@ #define GT_PCI0_CFGADDR (0xcf8 >> 2) #define GT_PCI0_CFGDATA (0xcfc >> 2) +REG32(GT_PCI0_CMD, 0xc00) +FIELD(GT_PCI0_CMD, MByteSwap, 0, 1) +FIELD(GT_PCI0_CMD, SByteSwap, 16, 1) +#define R_GT_PCI0_CMD_ByteSwap_MASK \ + (R_GT_PCI0_CMD_MByteSwap_MASK | R_GT_PCI0_CMD_SByteSwap_MASK) +REG32(GT_PCI1_CMD, 0xc80) +FIELD(GT_PCI1_CMD, MByteSwap, 0, 1) +FIELD(GT_PCI1_CMD, SByteSwap, 16, 1) +#define R_GT_PCI1_CMD_ByteSwap_MASK \ + (R_GT_PCI1_CMD_MByteSwap_MASK | R_GT_PCI1_CMD_SByteSwap_MASK) + /* Interrupts */ #define GT_INTRCAUSE (0xc18 >> 2) #define GT_INTRMASK (0xc1c >> 2) @@ -240,6 +257,9 @@ struct GT64120State { PCI_MAPPING_ENTRY(ISD); MemoryRegion pci0_mem; AddressSpace pci0_mem_as; + + /* properties */ + bool cpu_little_endian; }; /* Adjust range to avoid touching space which isn't mappable via PCI */ @@ -282,6 +302,8 @@ static void gt64120_isd_mapping(GT64120State *s) hwaddr start = ((hwaddr)s->regs[GT_ISD] << 21) & 0xFFFE00000ull; hwaddr length = 0x1000; + memory_region_transaction_begin(); + if (s->ISD_length) { memory_region_del_subregion(get_system_memory(), &s->ISD_mem); } @@ -292,10 +314,46 @@ static void gt64120_isd_mapping(GT64120State *s) s->ISD_start = start; s->ISD_length = length; memory_region_add_subregion(get_system_memory(), s->ISD_start, &s->ISD_mem); + + memory_region_transaction_commit(); +} + +static void gt64120_update_pci_cfgdata_mapping(GT64120State *s) +{ + /* Indexed on MByteSwap bit, see Table 158: PCI_0 Command, Offset: 0xc00 */ + static const MemoryRegionOps *pci_host_data_ops[] = { + &pci_host_data_be_ops, &pci_host_data_le_ops + }; + PCIHostState *phb = PCI_HOST_BRIDGE(s); + + memory_region_transaction_begin(); + + /* + * The setting of the MByteSwap bit and MWordSwap bit in the PCI Internal + * Command Register determines how data transactions from the CPU to/from + * PCI are handled along with the setting of the Endianness bit in the CPU + * Configuration Register. See: + * - Table 16: 32-bit PCI Transaction Endianness + * - Table 158: PCI_0 Command, Offset: 0xc00 + */ + + if (memory_region_is_mapped(&phb->data_mem)) { + memory_region_del_subregion(&s->ISD_mem, &phb->data_mem); + object_unparent(OBJECT(&phb->data_mem)); + } + memory_region_init_io(&phb->data_mem, OBJECT(phb), + pci_host_data_ops[s->regs[GT_PCI0_CMD] & 1], + s, "pci-conf-data", 4); + memory_region_add_subregion_overlap(&s->ISD_mem, GT_PCI0_CFGDATA << 2, + &phb->data_mem, 1); + + memory_region_transaction_commit(); } static void gt64120_pci_mapping(GT64120State *s) { + memory_region_transaction_begin(); + /* Update PCI0IO mapping */ if ((s->regs[GT_PCI0IOLD] & 0x7f) <= s->regs[GT_PCI0IOHD]) { /* Unmap old IO address */ @@ -354,6 +412,8 @@ static void gt64120_pci_mapping(GT64120State *s) &s->PCI0M1_mem); } } + + memory_region_transaction_commit(); } static int gt64120_post_load(void *opaque, int version_id) @@ -371,7 +431,7 @@ static const VMStateDescription vmstate_gt64120 = { .version_id = 1, .minimum_version_id = 1, .post_load = gt64120_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, GT64120State, GT_REGS), VMSTATE_END_OF_LIST() } @@ -381,7 +441,6 @@ static void gt64120_writel(void *opaque, hwaddr addr, uint64_t val, unsigned size) { GT64120State *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); uint32_t saddr = addr >> 2; trace_gt64120_write(addr, val); @@ -584,6 +643,7 @@ static void gt64120_writel(void *opaque, hwaddr addr, case GT_PCI0_CMD: case GT_PCI1_CMD: s->regs[saddr] = val & 0x0401fc0f; + gt64120_update_pci_cfgdata_mapping(s); break; case GT_PCI0_TOR: case GT_PCI0_BS_SCS10: @@ -624,15 +684,9 @@ static void gt64120_writel(void *opaque, hwaddr addr, saddr << 2, size, size << 1, val); break; case GT_PCI0_CFGADDR: - phb->config_reg = val & 0x80fffffc; - break; case GT_PCI0_CFGDATA: - if (!(s->regs[GT_PCI0_CMD] & 1) && (phb->config_reg & 0x00fff800)) { - val = bswap32(val); - } - if (phb->config_reg & (1u << 31)) { - pci_data_write(phb->bus, phb->config_reg, val, 4); - } + /* Mapped via in gt64120_pci_mapping() */ + g_assert_not_reached(); break; /* Interrupts */ @@ -690,7 +744,6 @@ static uint64_t gt64120_readl(void *opaque, hwaddr addr, unsigned size) { GT64120State *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); uint32_t val; uint32_t saddr = addr >> 2; @@ -875,17 +928,9 @@ static uint64_t gt64120_readl(void *opaque, /* PCI Internal */ case GT_PCI0_CFGADDR: - val = phb->config_reg; - break; case GT_PCI0_CFGDATA: - if (!(phb->config_reg & (1 << 31))) { - val = 0xffffffff; - } else { - val = pci_data_read(phb->bus, phb->config_reg, 4); - } - if (!(s->regs[GT_PCI0_CMD] & 1) && (phb->config_reg & 0x00fff800)) { - val = bswap32(val); - } + /* Mapped via in gt64120_pci_mapping() */ + g_assert_not_reached(); break; case GT_PCI0_CMD: @@ -986,11 +1031,7 @@ static void gt64120_reset(DeviceState *dev) /* FIXME: Malta specific hw assumptions ahead */ /* CPU Configuration */ -#if TARGET_BIG_ENDIAN - s->regs[GT_CPU] = 0x00000000; -#else - s->regs[GT_CPU] = 0x00001000; -#endif + s->regs[GT_CPU] = s->cpu_little_endian ? R_GT_CPU_Endianness_MASK : 0; s->regs[GT_MULTI] = 0x00000003; /* CPU Address decode */ @@ -1097,11 +1138,7 @@ static void gt64120_reset(DeviceState *dev) s->regs[GT_TC_CONTROL] = 0x00000000; /* PCI Internal */ -#if TARGET_BIG_ENDIAN - s->regs[GT_PCI0_CMD] = 0x00000000; -#else - s->regs[GT_PCI0_CMD] = 0x00010001; -#endif + s->regs[GT_PCI0_CMD] = s->cpu_little_endian ? R_GT_PCI0_CMD_ByteSwap_MASK : 0; s->regs[GT_PCI0_TOR] = 0x0000070f; s->regs[GT_PCI0_BS_SCS10] = 0x00fff000; s->regs[GT_PCI0_BS_SCS32] = 0x00fff000; @@ -1118,11 +1155,7 @@ static void gt64120_reset(DeviceState *dev) s->regs[GT_PCI0_SSCS10_BAR] = 0x00000000; s->regs[GT_PCI0_SSCS32_BAR] = 0x01000000; s->regs[GT_PCI0_SCS3BT_BAR] = 0x1f000000; -#if TARGET_BIG_ENDIAN - s->regs[GT_PCI1_CMD] = 0x00000000; -#else - s->regs[GT_PCI1_CMD] = 0x00010001; -#endif + s->regs[GT_PCI1_CMD] = s->cpu_little_endian ? R_GT_PCI1_CMD_ByteSwap_MASK : 0; s->regs[GT_PCI1_TOR] = 0x0000070f; s->regs[GT_PCI1_BS_SCS10] = 0x00fff000; s->regs[GT_PCI1_BS_SCS32] = 0x00fff000; @@ -1145,6 +1178,7 @@ static void gt64120_reset(DeviceState *dev) gt64120_isd_mapping(s); gt64120_pci_mapping(s); + gt64120_update_pci_cfgdata_mapping(s); } static void gt64120_realize(DeviceState *dev, Error **errp) @@ -1162,6 +1196,19 @@ static void gt64120_realize(DeviceState *dev, Error **errp) PCI_DEVFN(18, 0), TYPE_PCI_BUS); pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "gt64120_pci"); + memory_region_init_io(&phb->conf_mem, OBJECT(phb), + &pci_host_conf_le_ops, + s, "pci-conf-idx", 4); + memory_region_add_subregion_overlap(&s->ISD_mem, GT_PCI0_CFGADDR << 2, + &phb->conf_mem, 1); + + + /* + * 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. + */ + empty_slot_init("GT64120", 0, 0x20000000); } static void gt64120_pci_realize(PCIDevice *d, Error **errp) @@ -1208,11 +1255,18 @@ static const TypeInfo gt64120_pci_info = { }, }; +static Property gt64120_properties[] = { + DEFINE_PROP_BOOL("cpu-little-endian", GT64120State, + cpu_little_endian, false), + DEFINE_PROP_END_OF_LIST(), +}; + static void gt64120_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + device_class_set_props(dc, gt64120_properties); dc->realize = gt64120_realize; dc->reset = gt64120_reset; dc->vmsd = &vmstate_gt64120; diff --git a/hw/pci-host/i440fx.c b/hw/pci-host/i440fx.c index e08716142b..4f0a0438d7 100644 --- a/hw/pci-host/i440fx.c +++ b/hw/pci-host/i440fx.c @@ -46,10 +46,18 @@ OBJECT_DECLARE_SIMPLE_TYPE(I440FXState, I440FX_PCI_HOST_BRIDGE) struct I440FXState { PCIHostState parent_obj; + + MemoryRegion *system_memory; + MemoryRegion *io_memory; + MemoryRegion *pci_address_space; + MemoryRegion *ram_memory; Range pci_hole; + uint64_t below_4g_mem_size; + uint64_t above_4g_mem_size; uint64_t pci_hole64_size; bool pci_hole64_fix; - uint32_t short_root_bus; + + char *pci_type; }; #define I440FX_PAM 0x59 @@ -64,6 +72,15 @@ struct I440FXState { */ #define I440FX_COREBOOT_RAM_SIZE 0x57 +static void i440fx_realize(PCIDevice *dev, Error **errp) +{ + dev->config[I440FX_SMRAM] = 0x02; + + if (object_property_get_bool(qdev_get_machine(), "iommu", NULL)) { + warn_report("i440fx doesn't support emulated iommu"); + } +} + static void i440fx_update_memory_mappings(PCII440FXState *d) { int i; @@ -108,7 +125,7 @@ static const VMStateDescription vmstate_i440fx = { .version_id = 3, .minimum_version_id = 3, .post_load = i440fx_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PCII440FXState), /* Used to be smm_enabled, which was basically always zero because * SeaBIOS hardly uses SMM. SMRAM is now handled by CPU code. @@ -204,84 +221,69 @@ static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, static void i440fx_pcihost_initfn(Object *obj) { - PCIHostState *s = PCI_HOST_BRIDGE(obj); + I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); + PCIHostState *phb = PCI_HOST_BRIDGE(obj); - memory_region_init_io(&s->conf_mem, obj, &pci_host_conf_le_ops, s, + memory_region_init_io(&phb->conf_mem, obj, &pci_host_conf_le_ops, phb, "pci-conf-idx", 4); - memory_region_init_io(&s->data_mem, obj, &pci_host_data_le_ops, s, + memory_region_init_io(&phb->data_mem, obj, &pci_host_data_le_ops, phb, "pci-conf-data", 4); + + object_property_add_link(obj, PCI_HOST_PROP_RAM_MEM, TYPE_MEMORY_REGION, + (Object **) &s->ram_memory, + qdev_prop_allow_set_link_before_realize, 0); + + object_property_add_link(obj, PCI_HOST_PROP_PCI_MEM, TYPE_MEMORY_REGION, + (Object **) &s->pci_address_space, + qdev_prop_allow_set_link_before_realize, 0); + + object_property_add_link(obj, PCI_HOST_PROP_SYSTEM_MEM, TYPE_MEMORY_REGION, + (Object **) &s->system_memory, + qdev_prop_allow_set_link_before_realize, 0); + + object_property_add_link(obj, PCI_HOST_PROP_IO_MEM, TYPE_MEMORY_REGION, + (Object **) &s->io_memory, + qdev_prop_allow_set_link_before_realize, 0); } static void i440fx_pcihost_realize(DeviceState *dev, Error **errp) { - PCIHostState *s = PCI_HOST_BRIDGE(dev); + ERRP_GUARD(); + I440FXState *s = I440FX_PCI_HOST_BRIDGE(dev); + PCIHostState *phb = PCI_HOST_BRIDGE(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + PCIBus *b; + PCIDevice *d; + PCII440FXState *f; + unsigned i; - sysbus_add_io(sbd, 0xcf8, &s->conf_mem); + memory_region_add_subregion(s->io_memory, 0xcf8, &phb->conf_mem); sysbus_init_ioports(sbd, 0xcf8, 4); - sysbus_add_io(sbd, 0xcfc, &s->data_mem); + memory_region_add_subregion(s->io_memory, 0xcfc, &phb->data_mem); sysbus_init_ioports(sbd, 0xcfc, 4); /* register i440fx 0xcf8 port as coalesced pio */ - memory_region_set_flush_coalesced(&s->data_mem); - memory_region_add_coalescing(&s->conf_mem, 0, 4); -} + memory_region_set_flush_coalesced(&phb->data_mem); + memory_region_add_coalescing(&phb->conf_mem, 0, 4); -static void i440fx_realize(PCIDevice *dev, Error **errp) -{ - dev->config[I440FX_SMRAM] = 0x02; + b = pci_root_bus_new(dev, NULL, s->pci_address_space, + s->io_memory, 0, TYPE_PCI_BUS); + phb->bus = b; - if (object_property_get_bool(qdev_get_machine(), "iommu", NULL)) { - warn_report("i440fx doesn't support emulated iommu"); - } -} + d = pci_create_simple(b, 0, s->pci_type); + f = I440FX_PCI_DEVICE(d); -PCIBus *i440fx_init(const char *host_type, const char *pci_type, - PCII440FXState **pi440fx_state, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - ram_addr_t ram_size, - ram_addr_t below_4g_mem_size, - ram_addr_t above_4g_mem_size, - MemoryRegion *pci_address_space, - MemoryRegion *ram_memory) -{ - DeviceState *dev; - PCIBus *b; - PCIDevice *d; - PCIHostState *s; - PCII440FXState *f; - unsigned i; - I440FXState *i440fx; - - dev = qdev_new(host_type); - s = PCI_HOST_BRIDGE(dev); - b = pci_root_bus_new(dev, NULL, pci_address_space, - address_space_io, 0, TYPE_PCI_BUS); - s->bus = b; - object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev)); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - - d = pci_create_simple(b, 0, pci_type); - *pi440fx_state = I440FX_PCI_DEVICE(d); - f = *pi440fx_state; - f->system_memory = address_space_mem; - f->pci_address_space = pci_address_space; - f->ram_memory = ram_memory; - - i440fx = I440FX_PCI_HOST_BRIDGE(dev); - range_set_bounds(&i440fx->pci_hole, below_4g_mem_size, + range_set_bounds(&s->pci_hole, s->below_4g_mem_size, IO_APIC_DEFAULT_ADDRESS - 1); /* setup pci memory mapping */ - pc_pci_as_mapping_init(OBJECT(f), f->system_memory, - f->pci_address_space); + pc_pci_as_mapping_init(s->system_memory, s->pci_address_space); /* if *disabled* show SMRAM to all CPUs */ memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region", - f->pci_address_space, 0xa0000, 0x20000); - memory_region_add_subregion_overlap(f->system_memory, 0xa0000, + s->pci_address_space, SMRAM_C_BASE, SMRAM_C_SIZE); + memory_region_add_subregion_overlap(s->system_memory, SMRAM_C_BASE, &f->smram_region, 1); memory_region_set_enabled(&f->smram_region, true); @@ -289,20 +291,21 @@ PCIBus *i440fx_init(const char *host_type, const char *pci_type, memory_region_init(&f->smram, OBJECT(d), "smram", 4 * GiB); memory_region_set_enabled(&f->smram, true); memory_region_init_alias(&f->low_smram, OBJECT(d), "smram-low", - f->ram_memory, 0xa0000, 0x20000); + s->ram_memory, SMRAM_C_BASE, SMRAM_C_SIZE); memory_region_set_enabled(&f->low_smram, true); - memory_region_add_subregion(&f->smram, 0xa0000, &f->low_smram); + memory_region_add_subregion(&f->smram, SMRAM_C_BASE, &f->low_smram); object_property_add_const_link(qdev_get_machine(), "smram", OBJECT(&f->smram)); - init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space, - &f->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE); + init_pam(&f->pam_regions[0], OBJECT(d), s->ram_memory, s->system_memory, + s->pci_address_space, PAM_BIOS_BASE, PAM_BIOS_SIZE); for (i = 0; i < ARRAY_SIZE(f->pam_regions) - 1; ++i) { - init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space, - &f->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, - PAM_EXPAN_SIZE); + init_pam(&f->pam_regions[i + 1], OBJECT(d), s->ram_memory, + s->system_memory, s->pci_address_space, + PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); } + ram_addr_t ram_size = s->below_4g_mem_size + s->above_4g_mem_size; ram_size = ram_size / 8 / 1024 / 1024; if (ram_size > 255) { ram_size = 255; @@ -310,8 +313,6 @@ PCIBus *i440fx_init(const char *host_type, const char *pci_type, d->config[I440FX_COREBOOT_RAM_SIZE] = ram_size; i440fx_update_memory_mappings(f); - - return b; } static void i440fx_class_init(ObjectClass *klass, void *data) @@ -349,20 +350,18 @@ static const TypeInfo i440fx_info = { static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { - I440FXState *s = I440FX_PCI_HOST_BRIDGE(host_bridge); - - /* For backwards compat with old device paths */ - if (s->short_root_bus) { - return "0000"; - } return "0000:00"; } static Property i440fx_props[] = { DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, I440FXState, pci_hole64_size, I440FX_PCI_HOST_HOLE64_SIZE_DEFAULT), - DEFINE_PROP_UINT32("short_root_bus", I440FXState, short_root_bus, 0), + DEFINE_PROP_SIZE(PCI_HOST_BELOW_4G_MEM_SIZE, I440FXState, + below_4g_mem_size, 0), + DEFINE_PROP_SIZE(PCI_HOST_ABOVE_4G_MEM_SIZE, I440FXState, + above_4g_mem_size, 0), DEFINE_PROP_BOOL("x-pci-hole64-fix", I440FXState, pci_hole64_fix, true), + DEFINE_PROP_STRING(I440FX_HOST_PROP_PCI_TYPE, I440FXState, pci_type), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build index c07596d0d1..3001e93a43 100644 --- a/hw/pci-host/meson.build +++ b/hw/pci-host/meson.build @@ -1,6 +1,7 @@ pci_ss = ss.source_set() pci_ss.add(when: 'CONFIG_PAM', if_true: files('pam.c')) pci_ss.add(when: 'CONFIG_PCI_BONITO', if_true: files('bonito.c')) +pci_ss.add(when: 'CONFIG_GT64120', if_true: files('gt64120.c')) pci_ss.add(when: 'CONFIG_PCI_EXPRESS_DESIGNWARE', if_true: files('designware.c')) pci_ss.add(when: 'CONFIG_PCI_EXPRESS_GENERIC_BRIDGE', if_true: files('gpex.c')) pci_ss.add(when: ['CONFIG_PCI_EXPRESS_GENERIC_BRIDGE', 'CONFIG_ACPI'], if_true: files('gpex-acpi.c')) @@ -13,12 +14,16 @@ pci_ss.add(when: 'CONFIG_REMOTE_PCIHOST', if_true: files('remote.c')) pci_ss.add(when: 'CONFIG_SH_PCI', if_true: files('sh_pci.c')) # PPC devices +pci_ss.add(when: 'CONFIG_PPC4XX_PCI', if_true: files('ppc4xx_pci.c')) +pci_ss.add(when: 'CONFIG_PPC440_PCIX', if_true: files('ppc440_pcix.c')) pci_ss.add(when: 'CONFIG_RAVEN_PCI', if_true: files('raven.c')) pci_ss.add(when: 'CONFIG_GRACKLE_PCI', if_true: files('grackle.c')) # NewWorld PowerMac pci_ss.add(when: 'CONFIG_UNIN_PCI', if_true: files('uninorth.c')) # PowerPC E500 boards pci_ss.add(when: 'CONFIG_PPCE500_PCI', if_true: files('ppce500.c')) +# AmigaOne +pci_ss.add(when: 'CONFIG_ARTICIA', if_true: files('articia.c')) # Pegasos2 pci_ss.add(when: 'CONFIG_MV64361', if_true: files('mv64361.c')) @@ -26,14 +31,16 @@ pci_ss.add(when: 'CONFIG_MV64361', if_true: files('mv64361.c')) pci_ss.add(when: 'CONFIG_VERSATILE_PCI', if_true: files('versatile.c')) # HPPA devices +specific_ss.add(when: 'CONFIG_ASTRO', if_true: files('astro.c')) pci_ss.add(when: 'CONFIG_DINO', if_true: files('dino.c')) -softmmu_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) +system_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) specific_ss.add(when: 'CONFIG_PCI_POWERNV', if_true: files( 'pnv_phb3.c', 'pnv_phb3_msi.c', 'pnv_phb3_pbcq.c', 'pnv_phb4.c', - 'pnv_phb4_pec.c' + 'pnv_phb4_pec.c', + 'pnv_phb.c', )) diff --git a/hw/pci-host/mv64361.c b/hw/pci-host/mv64361.c index cc9c4d6d3b..01bd8c887f 100644 --- a/hw/pci-host/mv64361.c +++ b/hw/pci-host/mv64361.c @@ -11,9 +11,8 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "hw/hw.h" #include "hw/sysbus.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" #include "hw/irq.h" #include "hw/intc/i8259.h" @@ -72,11 +71,6 @@ struct MV64361PCIState { uint64_t remap[5]; }; -static int mv64361_pcihost_map_irq(PCIDevice *pci_dev, int n) -{ - return (n + PCI_SLOT(pci_dev->devfn)) % PCI_NUM_PINS; -} - static void mv64361_pcihost_set_irq(void *opaque, int n, int level) { MV64361PCIState *s = opaque; @@ -97,7 +91,7 @@ static void mv64361_pcihost_realize(DeviceState *dev, Error **errp) g_free(name); name = g_strdup_printf("pci.%d", s->index); h->bus = pci_register_root_bus(dev, name, mv64361_pcihost_set_irq, - mv64361_pcihost_map_irq, dev, + pci_swizzle_map_irq_fn, dev, &s->mem, &s->io, 0, 4, TYPE_PCI_BUS); g_free(name); pci_create_simple(h->bus, 0, TYPE_MV64361_PCI_BRIDGE); @@ -547,6 +541,12 @@ static uint64_t mv64361_read(void *opaque, hwaddr addr, unsigned int size) } } break; + case MV64340_ETH_PHY_ADDR: + ret = 0x98; + break; + case MV64340_ETH_SMI: + ret = BIT(27); + break; case MV64340_CUNIT_ARBITER_CONTROL_REG: ret = 0x11ff0000 | (s->gpp_int_level << 10); break; @@ -879,10 +879,6 @@ static void mv64361_realize(DeviceState *dev, Error **errp) } sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cpu_irq); qdev_init_gpio_in_named(dev, mv64361_gpp_irq, "gpp", 32); - /* FIXME: PCI IRQ connections may be board specific */ - for (i = 0; i < PCI_NUM_PINS; i++) { - s->pci[1].irq[i] = qdev_get_gpio_in_named(dev, "gpp", 12 + i); - } } static void mv64361_reset(DeviceState *dev) diff --git a/hw/pci-host/mv643xx.h b/hw/pci-host/mv643xx.h index cd26a43f18..f2e1baea88 100644 --- a/hw/pci-host/mv643xx.h +++ b/hw/pci-host/mv643xx.h @@ -656,6 +656,9 @@ /* Ethernet Unit Registers */ /****************************************/ +#define MV64340_ETH_PHY_ADDR 0x2000 +#define MV64340_ETH_SMI 0x2004 + /*******************************************/ /* CUNIT Registers */ /*******************************************/ diff --git a/hw/pci-host/pam.c b/hw/pci-host/pam.c index 454dd120db..68e9884d27 100644 --- a/hw/pci-host/pam.c +++ b/hw/pci-host/pam.c @@ -30,24 +30,24 @@ #include "qemu/osdep.h" #include "hw/pci-host/pam.h" -void init_pam(DeviceState *dev, MemoryRegion *ram_memory, +void init_pam(PAMMemoryRegion *mem, Object *owner, MemoryRegion *ram_memory, MemoryRegion *system_memory, MemoryRegion *pci_address_space, - PAMMemoryRegion *mem, uint32_t start, uint32_t size) + uint32_t start, uint32_t size) { int i; /* RAM */ - memory_region_init_alias(&mem->alias[3], OBJECT(dev), "pam-ram", ram_memory, + memory_region_init_alias(&mem->alias[3], owner, "pam-ram", ram_memory, start, size); /* ROM (XXX: not quite correct) */ - memory_region_init_alias(&mem->alias[1], OBJECT(dev), "pam-rom", ram_memory, + memory_region_init_alias(&mem->alias[1], owner, "pam-rom", ram_memory, start, size); memory_region_set_readonly(&mem->alias[1], true); /* XXX: should distinguish read/write cases */ - memory_region_init_alias(&mem->alias[0], OBJECT(dev), "pam-pci", pci_address_space, + memory_region_init_alias(&mem->alias[0], owner, "pam-pci", pci_address_space, start, size); - memory_region_init_alias(&mem->alias[2], OBJECT(dev), "pam-pci", ram_memory, + memory_region_init_alias(&mem->alias[2], owner, "pam-pci", ram_memory, start, size); memory_region_transaction_begin(); diff --git a/hw/pci-host/pnv_phb.c b/hw/pci-host/pnv_phb.c new file mode 100644 index 0000000000..157c00782c --- /dev/null +++ b/hw/pci-host/pnv_phb.c @@ -0,0 +1,356 @@ +/* + * QEMU PowerPC PowerNV Proxy PHB model + * + * Copyright (c) 2022, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/visitor.h" +#include "qapi/error.h" +#include "hw/pci-host/pnv_phb.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/pci-host/pnv_phb4.h" +#include "hw/ppc/pnv.h" +#include "hw/qdev-properties.h" +#include "qom/object.h" +#include "sysemu/sysemu.h" + + +/* + * Set the QOM parent and parent bus of an object child. If the device + * state associated with the child has an id, use it as QOM id. + * Otherwise use object_typename[index] as QOM id. + * + * This helper does both operations at the same time because setting + * a new QOM child will erase the bus parent of the device. This happens + * because object_unparent() will call object_property_del_child(), + * which in turn calls the property release callback prop->release if + * it's defined. In our case this callback is set to + * object_finalize_child_property(), which was assigned during the + * first object_property_add_child() call. This callback will end up + * calling device_unparent(), and this function removes the device + * from its parent bus. + * + * The QOM and parent bus to be set aren´t necessarily related, so + * let's receive both as arguments. + */ +static bool pnv_parent_fixup(Object *parent, BusState *parent_bus, + Object *child, int index, + Error **errp) +{ + g_autofree char *default_id = + g_strdup_printf("%s[%d]", object_get_typename(child), index); + const char *dev_id = DEVICE(child)->id; + + if (child->parent == parent) { + return true; + } + + object_ref(child); + object_unparent(child); + object_property_add_child(parent, dev_id ? dev_id : default_id, child); + object_unref(child); + + if (!qdev_set_parent_bus(DEVICE(child), parent_bus, errp)) { + return false; + } + + return true; +} + +static Object *pnv_phb_user_get_parent(PnvChip *chip, PnvPHB *phb, Error **errp) +{ + if (phb->version == 3) { + return OBJECT(pnv_chip_add_phb(chip, phb)); + } else { + return OBJECT(pnv_pec_add_phb(chip, phb, errp)); + } +} + +/* + * User created devices won't have the initial setup that default + * devices have. This setup consists of assigning a parent device + * (chip for PHB3, PEC for PHB4/5) that will be the QOM/bus parent + * of the PHB. + */ +static bool pnv_phb_user_device_init(PnvPHB *phb, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + PnvChip *chip = pnv_get_chip(pnv, phb->chip_id); + Object *parent = NULL; + + if (!chip) { + error_setg(errp, "invalid chip id: %d", phb->chip_id); + return false; + } + + parent = pnv_phb_user_get_parent(chip, phb, errp); + if (!parent) { + return false; + } + + /* + * Reparent user created devices to the chip to build + * correctly the device tree. pnv_xscom_dt() needs every + * PHB to be a child of the chip to build the DT correctly. + */ + if (!pnv_parent_fixup(parent, qdev_get_parent_bus(DEVICE(chip)), + OBJECT(phb), phb->phb_id, errp)) { + return false; + } + + return true; +} + +static void pnv_phb_realize(DeviceState *dev, Error **errp) +{ + PnvPHB *phb = PNV_PHB(dev); + PCIHostState *pci = PCI_HOST_BRIDGE(dev); + g_autofree char *phb_typename = NULL; + + if (!phb->version) { + error_setg(errp, "version not specified"); + return; + } + + switch (phb->version) { + case 3: + phb_typename = g_strdup(TYPE_PNV_PHB3); + break; + case 4: + phb_typename = g_strdup(TYPE_PNV_PHB4); + break; + case 5: + phb_typename = g_strdup(TYPE_PNV_PHB5); + break; + default: + g_assert_not_reached(); + } + + phb->backend = object_new(phb_typename); + object_property_add_child(OBJECT(dev), "phb-backend", phb->backend); + + /* Passthrough child device properties to the proxy device */ + object_property_set_uint(phb->backend, "index", phb->phb_id, errp); + object_property_set_uint(phb->backend, "chip-id", phb->chip_id, errp); + object_property_set_link(phb->backend, "phb-base", OBJECT(phb), errp); + + /* + * Handle user created devices. User devices will not have a + * pointer to a chip (PHB3) and a PEC (PHB4/5). + */ + if (!phb->chip && !phb->pec) { + if (!pnv_phb_user_device_init(phb, errp)) { + return; + } + } + + if (phb->version == 3) { + object_property_set_link(phb->backend, "chip", + OBJECT(phb->chip), errp); + } else { + object_property_set_link(phb->backend, "pec", OBJECT(phb->pec), errp); + } + + if (!qdev_realize(DEVICE(phb->backend), NULL, errp)) { + return; + } + + if (phb->version == 3) { + pnv_phb3_bus_init(dev, PNV_PHB3(phb->backend)); + } else { + pnv_phb4_bus_init(dev, PNV_PHB4(phb->backend)); + } + + if (defaults_enabled()) { + PCIDevice *root = pci_new(PCI_DEVFN(0, 0), TYPE_PNV_PHB_ROOT_PORT); + + pci_realize_and_unref(root, pci->bus, errp); + } +} + +static const char *pnv_phb_root_bus_path(PCIHostState *host_bridge, + PCIBus *rootbus) +{ + PnvPHB *phb = PNV_PHB(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_phb_properties[] = { + DEFINE_PROP_UINT32("index", PnvPHB, phb_id, 0), + DEFINE_PROP_UINT32("chip-id", PnvPHB, chip_id, 0), + DEFINE_PROP_UINT32("version", PnvPHB, version, 0), + + DEFINE_PROP_LINK("chip", PnvPHB, chip, TYPE_PNV_CHIP, PnvChip *), + + DEFINE_PROP_LINK("pec", PnvPHB, pec, TYPE_PNV_PHB4_PEC, + PnvPhb4PecState *), + + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_phb_class_init(ObjectClass *klass, void *data) +{ + PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + hc->root_bus_path = pnv_phb_root_bus_path; + dc->realize = pnv_phb_realize; + device_class_set_props(dc, pnv_phb_properties); + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->user_creatable = true; +} + +static void pnv_phb_root_port_reset_hold(Object *obj) +{ + PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(obj); + PnvPHBRootPort *phb_rp = PNV_PHB_ROOT_PORT(obj); + PCIDevice *d = PCI_DEVICE(obj); + uint8_t *conf = d->config; + + if (rpc->parent_phases.hold) { + rpc->parent_phases.hold(obj); + } + + if (phb_rp->version == 3) { + return; + } + + /* PHB4 and later requires these extra reset steps */ + 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); + pci_config_set_interrupt_pin(conf, 0); +} + +static void pnv_phb_root_port_realize(DeviceState *dev, Error **errp) +{ + PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev); + PnvPHBRootPort *phb_rp = PNV_PHB_ROOT_PORT(dev); + PCIBus *bus = PCI_BUS(qdev_get_parent_bus(dev)); + PCIDevice *pci = PCI_DEVICE(dev); + uint16_t device_id = 0; + Error *local_err = NULL; + int chip_id, index; + + /* + * 'index' will be used both as a PCIE slot value and to calculate + * QOM id. 'chip_id' is going to be used as PCIE chassis for the + * root port. + */ + chip_id = object_property_get_int(OBJECT(bus), "chip-id", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + index = object_property_get_int(OBJECT(bus), "phb-id", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Set unique chassis/slot values for the root port */ + qdev_prop_set_uint8(dev, "chassis", chip_id); + qdev_prop_set_uint16(dev, "slot", index); + + /* + * User created root ports are QOM parented to one of + * the peripheral containers but it's already at the right + * parent bus. Change the QOM parent to be the same as the + * parent bus it's already assigned to. + */ + if (!pnv_parent_fixup(OBJECT(bus), BUS(bus), OBJECT(dev), + index, errp)) { + return; + } + + rpc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + switch (phb_rp->version) { + case 3: + device_id = PNV_PHB3_DEVICE_ID; + break; + case 4: + device_id = PNV_PHB4_DEVICE_ID; + break; + case 5: + device_id = PNV_PHB5_DEVICE_ID; + break; + default: + g_assert_not_reached(); + } + + pci_config_set_device_id(pci->config, device_id); + pci_config_set_interrupt_pin(pci->config, 0); +} + +static Property pnv_phb_root_port_properties[] = { + DEFINE_PROP_UINT32("version", PnvPHBRootPort, version, 0), + + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_phb_root_port_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass); + + dc->desc = "IBM PHB PCIE Root Port"; + + device_class_set_props(dc, pnv_phb_root_port_properties); + device_class_set_parent_realize(dc, pnv_phb_root_port_realize, + &rpc->parent_realize); + resettable_class_set_parent_phases(rc, NULL, pnv_phb_root_port_reset_hold, + NULL, &rpc->parent_phases); + dc->user_creatable = true; + + k->vendor_id = PCI_VENDOR_ID_IBM; + /* device_id will be written during realize() */ + k->device_id = 0; + k->revision = 0; + + rpc->exp_offset = 0x48; + rpc->aer_offset = 0x100; +} + +static const TypeInfo pnv_phb_type_info = { + .name = TYPE_PNV_PHB, + .parent = TYPE_PCIE_HOST_BRIDGE, + .instance_size = sizeof(PnvPHB), + .class_init = pnv_phb_class_init, +}; + +static const TypeInfo pnv_phb_root_port_info = { + .name = TYPE_PNV_PHB_ROOT_PORT, + .parent = TYPE_PCIE_ROOT_PORT, + .instance_size = sizeof(PnvPHBRootPort), + .class_init = pnv_phb_root_port_class_init, +}; + +static void pnv_phb_register_types(void) +{ + type_register_static(&pnv_phb_type_info); + type_register_static(&pnv_phb_root_port_info); +} + +type_init(pnv_phb_register_types) diff --git a/hw/pci-host/pnv_phb.h b/hw/pci-host/pnv_phb.h new file mode 100644 index 0000000000..eb429d529f --- /dev/null +++ b/hw/pci-host/pnv_phb.h @@ -0,0 +1,55 @@ +/* + * QEMU PowerPC PowerNV Proxy PHB model + * + * Copyright (c) 2022, 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_PHB_H +#define PCI_HOST_PNV_PHB_H + +#include "hw/pci/pcie_host.h" +#include "hw/pci/pcie_port.h" +#include "hw/ppc/pnv.h" +#include "qom/object.h" + +typedef struct PnvPhb4PecState PnvPhb4PecState; + +struct PnvPHB { + PCIExpressHost parent_obj; + + uint32_t chip_id; + uint32_t phb_id; + uint32_t version; + char bus_path[8]; + + PnvChip *chip; + + PnvPhb4PecState *pec; + + /* The PHB backend (PnvPHB3, PnvPHB4 ...) being used */ + Object *backend; +}; + +#define TYPE_PNV_PHB "pnv-phb" +OBJECT_DECLARE_SIMPLE_TYPE(PnvPHB, PNV_PHB) + +/* + * PHB PCIe Root port + */ +#define PNV_PHB3_DEVICE_ID 0x03dc +#define PNV_PHB4_DEVICE_ID 0x04c1 +#define PNV_PHB5_DEVICE_ID 0x0652 + +typedef struct PnvPHBRootPort { + PCIESlot parent_obj; + + uint32_t version; +} PnvPHBRootPort; + +#define TYPE_PNV_PHB_ROOT_PORT "pnv-phb-root-port" +OBJECT_DECLARE_SIMPLE_TYPE(PnvPHBRootPort, PNV_PHB_ROOT_PORT) + +#endif /* PCI_HOST_PNV_PHB_H */ diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c index 3f03467dde..2a74dbe45f 100644 --- a/hw/pci-host/pnv_phb3.c +++ b/hw/pci-host/pnv_phb3.c @@ -11,10 +11,12 @@ #include "qapi/visitor.h" #include "qapi/error.h" #include "hw/pci-host/pnv_phb3_regs.h" +#include "hw/pci-host/pnv_phb.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/ppc/pnv_chip.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "qom/object.h" @@ -26,7 +28,7 @@ static PCIDevice *pnv_phb3_find_cfg_dev(PnvPHB3 *phb) { - PCIHostState *pci = PCI_HOST_BRIDGE(phb); + PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base); uint64_t addr = phb->regs[PHB_CONFIG_ADDRESS >> 3]; uint8_t bus, devfn; @@ -590,7 +592,7 @@ void pnv_phb3_reg_write(void *opaque, hwaddr off, uint64_t val, unsigned size) uint64_t pnv_phb3_reg_read(void *opaque, hwaddr off, unsigned size) { PnvPHB3 *phb = opaque; - PCIHostState *pci = PCI_HOST_BRIDGE(phb); + PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base); uint64_t val; if ((off & 0xfffc) == PHB_CONFIG_DATA) { @@ -755,7 +757,7 @@ static void pnv_phb3_translate_tve(PnvPhb3DMASpace *ds, hwaddr addr, * We only support non-translate in top window. * * TODO: Venice/Murano support it on bottom window above 4G and - * Naples suports it on everything + * Naples supports it on everything */ if (!(tve & PPC_BIT(51))) { phb3_error(phb, "xlate for invalid non-translate TVE"); @@ -966,6 +968,10 @@ static AddressSpace *pnv_phb3_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &ds->dma_as; } +static PCIIOMMUOps pnv_phb3_iommu_ops = { + .get_address_space = pnv_phb3_dma_iommu, +}; + static void pnv_phb3_instance_init(Object *obj) { PnvPHB3 *phb = PNV_PHB3(obj); @@ -986,10 +992,36 @@ static void pnv_phb3_instance_init(Object *obj) } +void pnv_phb3_bus_init(DeviceState *dev, PnvPHB3 *phb) +{ + PCIHostState *pci = PCI_HOST_BRIDGE(dev); + + /* + * 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, + dev->id ? dev->id : NULL, + pnv_phb3_set_irq, pnv_phb3_map_irq, phb, + &phb->pci_mmio, &phb->pci_io, + 0, 4, TYPE_PNV_PHB3_ROOT_BUS); + + object_property_set_int(OBJECT(pci->bus), "phb-id", phb->phb_id, + &error_abort); + object_property_set_int(OBJECT(pci->bus), "chip-id", phb->chip_id, + &error_abort); + + pci_setup_iommu(pci->bus, &pnv_phb3_iommu_ops, phb); +} + 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()); int i; @@ -1034,25 +1066,6 @@ static void pnv_phb3_realize(DeviceState *dev, Error **errp) /* 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, - dev->id ? dev->id : NULL, - 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); - - pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb), TYPE_PNV_PHB3_ROOT_PORT); } void pnv_phb3_update_regions(PnvPHB3 *phb) @@ -1077,47 +1090,80 @@ void pnv_phb3_update_regions(PnvPHB3 *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_LINK("chip", PnvPHB3, chip, TYPE_PNV_CHIP, PnvChip *), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_UINT32("index", PnvPHB3, phb_id, 0), + DEFINE_PROP_UINT32("chip-id", PnvPHB3, chip_id, 0), + DEFINE_PROP_LINK("chip", PnvPHB3, chip, TYPE_PNV_CHIP, PnvChip *), + DEFINE_PROP_LINK("phb-base", PnvPHB3, phb_base, TYPE_PNV_PHB, PnvPHB *), + 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, + .parent = TYPE_DEVICE, .instance_size = sizeof(PnvPHB3), .class_init = pnv_phb3_class_init, .instance_init = pnv_phb3_instance_init, }; +static void pnv_phb3_root_bus_get_prop(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + PnvPHB3RootBus *bus = PNV_PHB3_ROOT_BUS(obj); + uint64_t value = 0; + + if (strcmp(name, "phb-id") == 0) { + value = bus->phb_id; + } else { + value = bus->chip_id; + } + + visit_type_size(v, name, &value, errp); +} + +static void pnv_phb3_root_bus_set_prop(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) + +{ + PnvPHB3RootBus *bus = PNV_PHB3_ROOT_BUS(obj); + uint64_t value; + + if (!visit_type_size(v, name, &value, errp)) { + return; + } + + if (strcmp(name, "phb-id") == 0) { + bus->phb_id = value; + } else { + bus->chip_id = value; + } +} + static void pnv_phb3_root_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); + object_class_property_add(klass, "phb-id", "int", + pnv_phb3_root_bus_get_prop, + pnv_phb3_root_bus_set_prop, + NULL, NULL); + + object_class_property_add(klass, "chip-id", "int", + pnv_phb3_root_bus_get_prop, + pnv_phb3_root_bus_set_prop, + NULL, NULL); + /* * PHB3 has only a single root complex. Enforce the limit on the * parent bus @@ -1128,73 +1174,13 @@ static void pnv_phb3_root_bus_class_init(ObjectClass *klass, void *data) static const TypeInfo pnv_phb3_root_bus_info = { .name = TYPE_PNV_PHB3_ROOT_BUS, .parent = TYPE_PCIE_BUS, + .instance_size = sizeof(PnvPHB3RootBus), .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); - PCIDevice *pci = PCI_DEVICE(dev); - PCIBus *bus = pci_get_bus(pci); - PnvPHB3 *phb = NULL; - Error *local_err = NULL; - - phb = (PnvPHB3 *) object_dynamic_cast(OBJECT(bus->qbus.parent), - TYPE_PNV_PHB3); - - if (!phb) { - error_setg(errp, -"pnv_phb3_root_port devices must be connected to pnv-phb3 buses"); - return; - } - - /* Set unique chassis/slot values for the root port */ - qdev_prop_set_uint8(&pci->qdev, "chassis", phb->chip_id); - qdev_prop_set_uint16(&pci->qdev, "slot", phb->phb_id); - - rpc->parent_realize(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - pci_config_set_interrupt_pin(pci->config, 0); -} - -static void pnv_phb3_root_port_class_init(ObjectClass *klass, void *data) -{ - 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); } diff --git a/hw/pci-host/pnv_phb3_msi.c b/hw/pci-host/pnv_phb3_msi.c index 2f4112907b..dc8d8637f2 100644 --- a/hw/pci-host/pnv_phb3_msi.c +++ b/hw/pci-host/pnv_phb3_msi.c @@ -228,22 +228,19 @@ static void phb3_msi_resend(ICSState *ics) } } -static void phb3_msi_reset(DeviceState *dev) +static void phb3_msi_reset_hold(Object *obj) { - Phb3MsiState *msi = PHB3_MSI(dev); - ICSStateClass *icsc = ICS_GET_CLASS(dev); + Phb3MsiState *msi = PHB3_MSI(obj); + ICSStateClass *icsc = ICS_GET_CLASS(obj); - icsc->parent_reset(dev); + if (icsc->parent_phases.hold) { + icsc->parent_phases.hold(obj); + } 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) { @@ -272,8 +269,6 @@ static void phb3_msi_realize(DeviceState *dev, Error **errp) } 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) @@ -286,7 +281,7 @@ static void phb3_msi_instance_init(Object *obj) object_property_allow_set_link, OBJ_PROP_LINK_STRONG); - /* Will be overriden later */ + /* Will be overridden later */ ics->offset = 0; } @@ -294,11 +289,12 @@ static void phb3_msi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ICSStateClass *isc = ICS_CLASS(klass); + ResettableClass *rc = RESETTABLE_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); + resettable_class_set_parent_phases(rc, NULL, phb3_msi_reset_hold, NULL, + &isc->parent_phases); isc->reject = phb3_msi_reject; isc->resend = phb3_msi_resend; diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index 13ba9e45d8..075499d36d 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -31,25 +31,9 @@ qemu_log_mask(LOG_GUEST_ERROR, "phb4_pec[%d:%d]: " fmt "\n", \ (pec)->chip_id, (pec)->index, ## __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); + PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base); uint64_t addr = phb->regs[PHB_CONFIG_ADDRESS >> 3]; uint8_t bus, devfn; @@ -145,17 +129,17 @@ static uint64_t pnv_phb4_config_read(PnvPHB4 *phb, unsigned off, static void pnv_phb4_rc_config_write(PnvPHB4 *phb, unsigned off, unsigned size, uint64_t val) { - PCIHostState *pci = PCI_HOST_BRIDGE(phb); + PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base); PCIDevice *pdev; if (size != 4) { - phb_error(phb, "rc_config_write invalid size %d\n", size); + phb_error(phb, "rc_config_write invalid size %d", size); return; } pdev = pci_find_device(pci->bus, 0, 0); if (!pdev) { - phb_error(phb, "rc_config_write device not found\n"); + phb_error(phb, "rc_config_write device not found"); return; } @@ -166,18 +150,18 @@ static void pnv_phb4_rc_config_write(PnvPHB4 *phb, unsigned off, static uint64_t pnv_phb4_rc_config_read(PnvPHB4 *phb, unsigned off, unsigned size) { - PCIHostState *pci = PCI_HOST_BRIDGE(phb); + PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base); PCIDevice *pdev; uint64_t val; if (size != 4) { - phb_error(phb, "rc_config_read invalid size %d\n", size); + phb_error(phb, "rc_config_read invalid size %d", size); return ~0ull; } pdev = pci_find_device(pci->bus, 0, 0); if (!pdev) { - phb_error(phb, "rc_config_read device not found\n"); + phb_error(phb, "rc_config_read device not found"); return ~0ull; } @@ -223,7 +207,7 @@ static void pnv_phb4_check_mbt(PnvPHB4 *phb, uint32_t index) start = base | (phb->regs[PHB_M64_UPPER_BITS >> 3]); } - /* TODO: Figure out how to implemet/decode AOMASK */ + /* TODO: Figure out how to implement/decode AOMASK */ /* Check if it matches an enabled MMIO region in the PEC stack */ if (memory_region_is_mapped(&phb->mmbar0) && @@ -407,7 +391,7 @@ static void pnv_phb4_ioda_write(PnvPHB4 *phb, uint64_t val) case IODA3_TBL_MBT: *tptr = val; - /* Copy accross the valid bit to the other half */ + /* Copy across the valid bit to the other half */ phb->ioda_MBT[idx ^ 1] &= 0x7fffffffffffffffull; phb->ioda_MBT[idx ^ 1] |= 0x8000000000000000ull & val; @@ -871,7 +855,7 @@ static uint64_t pnv_pec_stk_nest_xscom_read(void *opaque, hwaddr addr, PnvPHB4 *phb = PNV_PHB4(opaque); uint32_t reg = addr >> 3; - /* TODO: add list of allowed registers and error out if not */ + /* All registers are read-able */ return phb->nest_regs[reg]; } @@ -1016,7 +1000,7 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, switch (reg) { case PEC_NEST_STK_PCI_NEST_FIR: - phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val; + phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val & PPC_BITMASK(0, 27); break; case PEC_NEST_STK_PCI_NEST_FIR_CLR: phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] &= val; @@ -1025,7 +1009,8 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] |= val; break; case PEC_NEST_STK_PCI_NEST_FIR_MSK: - phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val; + phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val & + PPC_BITMASK(0, 27); break; case PEC_NEST_STK_PCI_NEST_FIR_MSKC: phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] &= val; @@ -1035,7 +1020,7 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, break; case PEC_NEST_STK_PCI_NEST_FIR_ACT0: case PEC_NEST_STK_PCI_NEST_FIR_ACT1: - phb->nest_regs[reg] = val; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 27); break; case PEC_NEST_STK_PCI_NEST_FIR_WOF: phb->nest_regs[reg] = 0; @@ -1046,7 +1031,7 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, /* Flag error ? */ break; case PEC_NEST_STK_PBCQ_MODE: - phb->nest_regs[reg] = val & 0xff00000000000000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 7); break; case PEC_NEST_STK_MMIO_BAR0: case PEC_NEST_STK_MMIO_BAR0_MASK: @@ -1055,30 +1040,35 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, if (phb->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"); + phb_pec_error(pec, "Changing enabled BAR unsupported"); } - phb->nest_regs[reg] = val & 0xffffffffff000000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 39); break; case PEC_NEST_STK_PHB_REGS_BAR: if (phb->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_PHB) { - phb_pec_error(pec, "Changing enabled BAR unsupported\n"); + phb_pec_error(pec, "Changing enabled BAR unsupported"); } - phb->nest_regs[reg] = val & 0xffffffffffc00000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 41); break; case PEC_NEST_STK_INT_BAR: if (phb->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_INT) { - phb_pec_error(pec, "Changing enabled BAR unsupported\n"); + phb_pec_error(pec, "Changing enabled BAR unsupported"); } - phb->nest_regs[reg] = val & 0xfffffff000000000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 27); break; case PEC_NEST_STK_BAR_EN: - phb->nest_regs[reg] = val & 0xf000000000000000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 3); pnv_pec_phb_update_map(phb); break; case PEC_NEST_STK_DATA_FRZ_TYPE: - case PEC_NEST_STK_PBCQ_TUN_BAR: /* Not used for now */ - phb->nest_regs[reg] = val; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 27); + break; + case PEC_NEST_STK_PBCQ_SPARSE_PAGE: + phb->nest_regs[reg] = val & PPC_BITMASK(3, 5); + break; + case PEC_NEST_STK_PBCQ_CACHE_INJ: + phb->nest_regs[reg] = val & PPC_BITMASK(0, 7); break; default: qemu_log_mask(LOG_UNIMP, "phb4_pec: nest_xscom_write 0x%"HWADDR_PRIx @@ -1102,7 +1092,7 @@ static uint64_t pnv_pec_stk_pci_xscom_read(void *opaque, hwaddr addr, PnvPHB4 *phb = PNV_PHB4(opaque); uint32_t reg = addr >> 3; - /* TODO: add list of allowed registers and error out if not */ + /* All registers are read-able */ return phb->pci_regs[reg]; } @@ -1111,10 +1101,9 @@ static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr, { PnvPHB4 *phb = PNV_PHB4(opaque); uint32_t reg = addr >> 3; - switch (reg) { case PEC_PCI_STK_PCI_FIR: - phb->pci_regs[reg] = val; + phb->pci_regs[reg] = val & PPC_BITMASK(0, 5); break; case PEC_PCI_STK_PCI_FIR_CLR: phb->pci_regs[PEC_PCI_STK_PCI_FIR] &= val; @@ -1123,7 +1112,7 @@ static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr, phb->pci_regs[PEC_PCI_STK_PCI_FIR] |= val; break; case PEC_PCI_STK_PCI_FIR_MSK: - phb->pci_regs[reg] = val; + phb->pci_regs[reg] = val & PPC_BITMASK(0, 5); break; case PEC_PCI_STK_PCI_FIR_MSKC: phb->pci_regs[PEC_PCI_STK_PCI_FIR_MSK] &= val; @@ -1133,20 +1122,25 @@ static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr, break; case PEC_PCI_STK_PCI_FIR_ACT0: case PEC_PCI_STK_PCI_FIR_ACT1: - phb->pci_regs[reg] = val; + phb->pci_regs[reg] = val & PPC_BITMASK(0, 5); break; case PEC_PCI_STK_PCI_FIR_WOF: phb->pci_regs[reg] = 0; break; case PEC_PCI_STK_ETU_RESET: - phb->pci_regs[reg] = val & 0x8000000000000000ull; + phb->pci_regs[reg] = val & PPC_BIT(0); /* TODO: Implement reset */ break; case PEC_PCI_STK_PBAIB_ERR_REPORT: break; case PEC_PCI_STK_PBAIB_TX_CMD_CRED: + phb->pci_regs[reg] = val & + ((PPC_BITMASK(0, 2) | PPC_BITMASK(10, 18) + | PPC_BITMASK(26, 34) | PPC_BITMASK(41, 50) + | PPC_BITMASK(58, 63))); + break; case PEC_PCI_STK_PBAIB_TX_DAT_CRED: - phb->pci_regs[reg] = val; + phb->pci_regs[reg] = val & (PPC_BITMASK(33, 34) | PPC_BITMASK(44, 47)); break; default: qemu_log_mask(LOG_UNIMP, "phb4_pec_stk: pci_xscom_write 0x%"HWADDR_PRIx @@ -1424,7 +1418,7 @@ static void pnv_phb4_msi_write(void *opaque, hwaddr addr, return; } - /* TODO: check PE/MSI assignement */ + /* TODO: check PE/MSI assignment */ qemu_irq_pulse(phb->qirqs[src]); } @@ -1513,7 +1507,7 @@ static void pnv_phb4_xscom_realize(PnvPHB4 *phb) PHB4_PEC_PCI_STK_REGS_COUNT); /* PHB pass-through */ - snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-phb-%d", + snprintf(name, sizeof(name), "xscom-pec-%d.%d-phb-%d", pec->chip_id, pec->index, stack_no); pnv_xscom_region_init(&phb->phb_regs_mr, OBJECT(phb), &pnv_phb4_xscom_ops, phb, name, 0x40); @@ -1534,6 +1528,10 @@ static void pnv_phb4_xscom_realize(PnvPHB4 *phb) &phb->phb_regs_mr); } +static PCIIOMMUOps pnv_phb4_iommu_ops = { + .get_address_space = pnv_phb4_dma_iommu, +}; + static void pnv_phb4_instance_init(Object *obj) { PnvPHB4 *phb = PNV_PHB4(obj); @@ -1544,10 +1542,42 @@ static void pnv_phb4_instance_init(Object *obj) object_initialize_child(obj, "source", &phb->xsrc, TYPE_XIVE_SOURCE); } +void pnv_phb4_bus_init(DeviceState *dev, PnvPHB4 *phb) +{ + PCIHostState *pci = PCI_HOST_BRIDGE(dev); + char name[32]; + + /* + * 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, dev->id ? dev->id : NULL, + pnv_phb4_set_irq, pnv_phb4_map_irq, phb, + &phb->pci_mmio, &phb->pci_io, + 0, 4, TYPE_PNV_PHB4_ROOT_BUS); + + object_property_set_int(OBJECT(pci->bus), "phb-id", phb->phb_id, + &error_abort); + object_property_set_int(OBJECT(pci->bus), "chip-id", phb->chip_id, + &error_abort); + + pci_setup_iommu(pci->bus, &pnv_phb4_iommu_ops, phb); + pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; +} + static void pnv_phb4_realize(DeviceState *dev, Error **errp) { PnvPHB4 *phb = PNV_PHB4(dev); - PCIHostState *pci = PCI_HOST_BRIDGE(dev); XiveSource *xsrc = &phb->xsrc; int nr_irqs; char name[32]; @@ -1561,28 +1591,6 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp) 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, dev->id, - 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); - pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; - /* Setup XIVE Source */ if (phb->big_phb) { nr_irqs = PNV_PHB4_MAX_INTs; @@ -1602,16 +1610,6 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp) pnv_phb4_xscom_realize(phb); } -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; -} - /* * Address base trigger mode (POWER10) * @@ -1692,23 +1690,21 @@ static void pnv_phb4_xive_notify(XiveNotifier *xf, uint32_t srcno, } static Property pnv_phb4_properties[] = { - DEFINE_PROP_UINT32("index", PnvPHB4, phb_id, 0), - DEFINE_PROP_UINT32("chip-id", PnvPHB4, chip_id, 0), - DEFINE_PROP_LINK("pec", PnvPHB4, pec, TYPE_PNV_PHB4_PEC, - PnvPhb4PecState *), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_UINT32("index", PnvPHB4, phb_id, 0), + DEFINE_PROP_UINT32("chip-id", PnvPHB4, chip_id, 0), + DEFINE_PROP_LINK("pec", PnvPHB4, pec, TYPE_PNV_PHB4_PEC, + PnvPhb4PecState *), + DEFINE_PROP_LINK("phb-base", PnvPHB4, phb_base, TYPE_PNV_PHB, PnvPHB *), + 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; xfc->notify = pnv_phb4_xive_notify; @@ -1716,7 +1712,7 @@ static void pnv_phb4_class_init(ObjectClass *klass, void *data) static const TypeInfo pnv_phb4_type_info = { .name = TYPE_PNV_PHB4, - .parent = TYPE_PCIE_HOST_BRIDGE, + .parent = TYPE_DEVICE, .instance_init = pnv_phb4_instance_init, .instance_size = sizeof(PnvPHB4), .class_init = pnv_phb4_class_init, @@ -1732,10 +1728,55 @@ static const TypeInfo pnv_phb5_type_info = { .instance_size = sizeof(PnvPHB4), }; +static void pnv_phb4_root_bus_get_prop(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + PnvPHB4RootBus *bus = PNV_PHB4_ROOT_BUS(obj); + uint64_t value = 0; + + if (strcmp(name, "phb-id") == 0) { + value = bus->phb_id; + } else { + value = bus->chip_id; + } + + visit_type_size(v, name, &value, errp); +} + +static void pnv_phb4_root_bus_set_prop(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) + +{ + PnvPHB4RootBus *bus = PNV_PHB4_ROOT_BUS(obj); + uint64_t value; + + if (!visit_type_size(v, name, &value, errp)) { + return; + } + + if (strcmp(name, "phb-id") == 0) { + bus->phb_id = value; + } else { + bus->chip_id = value; + } +} + static void pnv_phb4_root_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); + object_class_property_add(klass, "phb-id", "int", + pnv_phb4_root_bus_get_prop, + pnv_phb4_root_bus_set_prop, + NULL, NULL); + + object_class_property_add(klass, "chip-id", "int", + pnv_phb4_root_bus_get_prop, + pnv_phb4_root_bus_set_prop, + NULL, NULL); + /* * PHB4 has only a single root complex. Enforce the limit on the * parent bus @@ -1746,116 +1787,13 @@ static void pnv_phb4_root_bus_class_init(ObjectClass *klass, void *data) static const TypeInfo pnv_phb4_root_bus_info = { .name = TYPE_PNV_PHB4_ROOT_BUS, .parent = TYPE_PCIE_BUS, + .instance_size = sizeof(PnvPHB4RootBus), .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); - pci_config_set_interrupt_pin(conf, 0); -} - -static void pnv_phb4_root_port_realize(DeviceState *dev, Error **errp) -{ - PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev); - PCIDevice *pci = PCI_DEVICE(dev); - PCIBus *bus = pci_get_bus(pci); - PnvPHB4 *phb = NULL; - Error *local_err = NULL; - - phb = (PnvPHB4 *) object_dynamic_cast(OBJECT(bus->qbus.parent), - TYPE_PNV_PHB4); - - if (!phb) { - error_setg(errp, "%s must be connected to pnv-phb4 buses", dev->id); - return; - } - - /* Set unique chassis/slot values for the root port */ - qdev_prop_set_uint8(&pci->qdev, "chassis", phb->chip_id); - qdev_prop_set_uint16(&pci->qdev, "slot", phb->phb_id); - - 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_phb5_root_port_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - dc->desc = "IBM PHB5 PCIE Root Port"; - dc->user_creatable = false; - - k->vendor_id = PCI_VENDOR_ID_IBM; - k->device_id = PNV_PHB5_DEVICE_ID; -} - -static const TypeInfo pnv_phb5_root_port_info = { - .name = TYPE_PNV_PHB5_ROOT_PORT, - .parent = TYPE_PNV_PHB4_ROOT_PORT, - .instance_size = sizeof(PnvPHB4RootPort), - .class_init = pnv_phb5_root_port_class_init, }; static void pnv_phb4_register_types(void) { type_register_static(&pnv_phb4_root_bus_info); - type_register_static(&pnv_phb5_root_port_info); - type_register_static(&pnv_phb4_root_port_info); type_register_static(&pnv_phb4_type_info); type_register_static(&pnv_phb5_type_info); type_register_static(&pnv_phb4_iommu_memory_region_info); diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c index 61bc0b503e..ce8e228f98 100644 --- a/hw/pci-host/pnv_phb4_pec.c +++ b/hw/pci-host/pnv_phb4_pec.c @@ -17,6 +17,7 @@ #include "hw/pci/pci_bridge.h" #include "hw/pci/pci_bus.h" #include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_chip.h" #include "hw/qdev-properties.h" #include "sysemu/sysemu.h" @@ -33,7 +34,7 @@ static uint64_t pnv_pec_nest_xscom_read(void *opaque, hwaddr addr, PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque); uint32_t reg = addr >> 3; - /* TODO: add list of allowed registers and error out if not */ + /* All registers are readable */ return pec->nest_regs[reg]; } @@ -44,18 +45,36 @@ static void pnv_pec_nest_xscom_write(void *opaque, hwaddr addr, uint32_t reg = addr >> 3; switch (reg) { - case PEC_NEST_PBCQ_HW_CONFIG: case PEC_NEST_DROP_PRIO_CTRL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 25); + break; case PEC_NEST_PBCQ_ERR_INJECT: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 11); + break; case PEC_NEST_PCI_NEST_CLK_TRACE_CTL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 16); + break; case PEC_NEST_PBCQ_PMON_CTRL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 37); + break; case PEC_NEST_PBCQ_PBUS_ADDR_EXT: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 6); + break; case PEC_NEST_PBCQ_PRED_VEC_TIMEOUT: - case PEC_NEST_CAPP_CTRL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 15); + break; case PEC_NEST_PBCQ_READ_STK_OVR: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 48); + break; case PEC_NEST_PBCQ_WRITE_STK_OVR: case PEC_NEST_PBCQ_STORE_STK_OVR: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 24); + break; case PEC_NEST_PBCQ_RETRY_BKOFF_CTRL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 41); + break; + case PEC_NEST_PBCQ_HW_CONFIG: + case PEC_NEST_CAPP_CTRL: pec->nest_regs[reg] = val; break; default: @@ -80,7 +99,7 @@ static uint64_t pnv_pec_pci_xscom_read(void *opaque, hwaddr addr, PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque); uint32_t reg = addr >> 3; - /* TODO: add list of allowed registers and error out if not */ + /* All registers are readable */ return pec->pci_regs[reg]; } @@ -92,8 +111,13 @@ static void pnv_pec_pci_xscom_write(void *opaque, hwaddr addr, switch (reg) { case PEC_PCI_PBAIB_HW_CONFIG: + pec->pci_regs[reg] = val & PPC_BITMASK(0, 42); + break; + case PEC_PCI_PBAIB_HW_OVR: + pec->pci_regs[reg] = val & PPC_BITMASK(0, 15); + break; case PEC_PCI_PBAIB_READ_STK_OVR: - pec->pci_regs[reg] = val; + pec->pci_regs[reg] = val & PPC_BITMASK(0, 48); break; default: phb_pec_error(pec, "%s @0x%"HWADDR_PRIx"=%"PRIx64"\n", __func__, @@ -111,12 +135,52 @@ static const MemoryRegionOps pnv_pec_pci_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_pec_default_phb_realize(PnvPhb4PecState *pec, - int stack_no, - Error **errp) +PnvPhb4PecState *pnv_pec_add_phb(PnvChip *chip, PnvPHB *phb, Error **errp) { - PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec); - PnvPHB4 *phb = PNV_PHB4(qdev_new(pecc->phb_type)); + PnvPhb4PecState *pecs = NULL; + int chip_id = phb->chip_id; + int index = phb->phb_id; + int i, j; + + if (phb->version == 4) { + Pnv9Chip *chip9 = PNV9_CHIP(chip); + + pecs = chip9->pecs; + } else if (phb->version == 5) { + Pnv10Chip *chip10 = PNV10_CHIP(chip); + + pecs = chip10->pecs; + } else { + g_assert_not_reached(); + } + + for (i = 0; i < chip->num_pecs; i++) { + /* + * For each PEC, check the amount of phbs it supports + * and see if the given phb4 index matches an index. + */ + PnvPhb4PecState *pec = &pecs[i]; + + for (j = 0; j < pec->num_phbs; j++) { + if (index == pnv_phb4_pec_get_phb_id(pec, j)) { + pec->phbs[j] = phb; + phb->pec = pec; + return pec; + } + } + } + error_setg(errp, + "pnv-phb4 chip-id %d index %d didn't match any existing PEC", + chip_id, index); + + return NULL; +} + +static PnvPHB *pnv_pec_default_phb_realize(PnvPhb4PecState *pec, + int stack_no, + Error **errp) +{ + PnvPHB *phb = PNV_PHB(qdev_new(TYPE_PNV_PHB)); int phb_id = pnv_phb4_pec_get_phb_id(pec, stack_no); object_property_add_child(OBJECT(pec), "phb[*]", OBJECT(phb)); @@ -128,11 +192,9 @@ static void pnv_pec_default_phb_realize(PnvPhb4PecState *pec, &error_fatal); if (!sysbus_realize(SYS_BUS_DEVICE(phb), errp)) { - return; + return NULL; } - - /* Add a single Root port if running with defaults */ - pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb), pecc->rp_model); + return phb; } static void pnv_pec_realize(DeviceState *dev, Error **errp) @@ -150,8 +212,11 @@ static void pnv_pec_realize(DeviceState *dev, Error **errp) pec->num_phbs = pecc->num_phbs[pec->index]; /* Create PHBs if running with defaults */ - for (i = 0; i < pec->num_phbs; i++) { - pnv_pec_default_phb_realize(pec, i, errp); + if (defaults_enabled()) { + g_assert(pec->num_phbs <= MAX_PHBS_PER_PEC); + for (i = 0; i < pec->num_phbs; i++) { + pec->phbs[i] = pnv_pec_default_phb_realize(pec, i, errp); + } } /* Initialize the XSCOM regions for the PEC registers */ @@ -198,9 +263,12 @@ static int pnv_pec_dt_xscom(PnvXScomInterface *dev, void *fdt, pecc->compat_size))); for (i = 0; i < pec->num_phbs; i++) { - int phb_id = pnv_phb4_pec_get_phb_id(pec, i); int stk_offset; + if (!pec->phbs[i]) { + continue; + } + name = g_strdup_printf("stack@%x", i); stk_offset = fdt_add_subnode(fdt, offset, name); _FDT(stk_offset); @@ -208,18 +276,19 @@ static int pnv_pec_dt_xscom(PnvXScomInterface *dev, void *fdt, _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_id))); + _FDT((fdt_setprop_cell(fdt, stk_offset, "ibm,phb-index", + pec->phbs[i]->phb_id))); } return 0; } static Property pnv_pec_properties[] = { - DEFINE_PROP_UINT32("index", PnvPhb4PecState, index, 0), - DEFINE_PROP_UINT32("chip-id", PnvPhb4PecState, chip_id, 0), - DEFINE_PROP_LINK("chip", PnvPhb4PecState, chip, TYPE_PNV_CHIP, - PnvChip *), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_UINT32("index", PnvPhb4PecState, index, 0), + DEFINE_PROP_UINT32("chip-id", PnvPhb4PecState, chip_id, 0), + DEFINE_PROP_LINK("chip", PnvPhb4PecState, chip, TYPE_PNV_CHIP, + PnvChip *), + DEFINE_PROP_END_OF_LIST(), }; static uint32_t pnv_pec_xscom_pci_base(PnvPhb4PecState *pec) @@ -264,7 +333,6 @@ static void pnv_pec_class_init(ObjectClass *klass, void *data) pecc->version = PNV_PHB4_VERSION; pecc->phb_type = TYPE_PNV_PHB4; pecc->num_phbs = pnv_pec_num_phbs; - pecc->rp_model = TYPE_PNV_PHB4_ROOT_PORT; } static const TypeInfo pnv_pec_type_info = { @@ -317,7 +385,6 @@ static void pnv_phb5_pec_class_init(ObjectClass *klass, void *data) pecc->version = PNV_PHB5_VERSION; pecc->phb_type = TYPE_PNV_PHB5; pecc->num_phbs = pnv_phb5_pec_num_stacks; - pecc->rp_model = TYPE_PNV_PHB5_ROOT_PORT; } static const TypeInfo pnv_phb5_pec_type_info = { diff --git a/hw/ppc/ppc440_pcix.c b/hw/pci-host/ppc440_pcix.c similarity index 93% rename from hw/ppc/ppc440_pcix.c rename to hw/pci-host/ppc440_pcix.c index 788d25514a..ef212d99aa 100644 --- a/hw/ppc/ppc440_pcix.c +++ b/hw/pci-host/ppc440_pcix.c @@ -23,10 +23,10 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/module.h" +#include "qemu/units.h" #include "hw/irq.h" -#include "hw/ppc/ppc.h" -#include "hw/ppc/ppc4xx.h" -#include "hw/pci/pci.h" +#include "hw/pci-host/ppc4xx.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" #include "trace.h" #include "qom/object.h" @@ -44,8 +44,7 @@ struct PLBInMap { MemoryRegion mr; }; -#define TYPE_PPC440_PCIX_HOST_BRIDGE "ppc440-pcix-host" -OBJECT_DECLARE_SIMPLE_TYPE(PPC440PCIXState, PPC440_PCIX_HOST_BRIDGE) +OBJECT_DECLARE_SIMPLE_TYPE(PPC440PCIXState, PPC440_PCIX_HOST) #define PPC440_PCIX_NR_POMS 3 #define PPC440_PCIX_NR_PIMS 3 @@ -53,7 +52,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(PPC440PCIXState, PPC440_PCIX_HOST_BRIDGE) struct PPC440PCIXState { PCIHostState parent_obj; - PCIDevice *dev; + uint8_t config[PCI_CONFIG_SPACE_SIZE]; struct PLBOutMap pom[PPC440_PCIX_NR_POMS]; struct PLBInMap pim[PPC440_PCIX_NR_PIMS]; uint32_t sts; @@ -64,6 +63,7 @@ struct PPC440PCIXState { MemoryRegion container; MemoryRegion iomem; MemoryRegion busmem; + MemoryRegion regs; }; #define PPC440_REG_BASE 0x80000 @@ -171,7 +171,7 @@ static void ppc440_pcix_reg_write4(void *opaque, hwaddr addr, trace_ppc440_pcix_reg_write(addr, val, size); switch (addr) { case PCI_VENDOR_ID ... PCI_MAX_LAT: - stl_le_p(s->dev->config + addr, val); + stl_le_p(s->config + addr, val); break; case PCIX0_POM0LAL: @@ -302,7 +302,7 @@ static uint64_t ppc440_pcix_reg_read4(void *opaque, hwaddr addr, switch (addr) { case PCI_VENDOR_ID ... PCI_MAX_LAT: - val = ldl_le_p(s->dev->config + addr); + val = ldl_le_p(s->config + addr); break; case PCIX0_POM0LAL: @@ -397,7 +397,7 @@ static const MemoryRegionOps pci_reg_ops = { static void ppc440_pcix_reset(DeviceState *dev) { - struct PPC440PCIXState *s = PPC440_PCIX_HOST_BRIDGE(dev); + struct PPC440PCIXState *s = PPC440_PCIX_HOST(dev); int i; for (i = 0; i < PPC440_PCIX_NR_POMS; i++) { @@ -448,6 +448,10 @@ static AddressSpace *ppc440_pcix_set_iommu(PCIBus *b, void *opaque, int devfn) return &s->bm_as; } +static const PCIIOMMUOps ppc440_iommu_ops = { + .get_address_space = ppc440_pcix_set_iommu, +}; + /* * Some guests on sam460ex write all kinds of garbage here such as * missing enable bit and low bits set and still expect this to work @@ -487,32 +491,32 @@ static void ppc440_pcix_realize(DeviceState *dev, Error **errp) PCIHostState *h; h = PCI_HOST_BRIDGE(dev); - s = PPC440_PCIX_HOST_BRIDGE(dev); + s = PPC440_PCIX_HOST(dev); sysbus_init_irq(sbd, &s->irq); - memory_region_init(&s->busmem, OBJECT(dev), "pci bus memory", UINT64_MAX); + memory_region_init(&s->busmem, OBJECT(dev), "pci-mem", UINT64_MAX); + memory_region_init(&s->iomem, OBJECT(dev), "pci-io", 64 * KiB); h->bus = pci_register_root_bus(dev, NULL, ppc440_pcix_set_irq, - ppc440_pcix_map_irq, &s->irq, &s->busmem, - get_system_io(), PCI_DEVFN(0, 0), 1, TYPE_PCI_BUS); - - s->dev = pci_create_simple(h->bus, PCI_DEVFN(0, 0), "ppc4xx-host-bridge"); + ppc440_pcix_map_irq, &s->irq, &s->busmem, &s->iomem, + PCI_DEVFN(1, 0), 1, TYPE_PCI_BUS); memory_region_init(&s->bm, OBJECT(s), "bm-ppc440-pcix", UINT64_MAX); memory_region_add_subregion(&s->bm, 0x0, &s->busmem); address_space_init(&s->bm_as, &s->bm, "pci-bm"); - pci_setup_iommu(h->bus, ppc440_pcix_set_iommu, s); + pci_setup_iommu(h->bus, &ppc440_iommu_ops, s); memory_region_init(&s->container, OBJECT(s), "pci-container", PCI_ALL_SIZE); memory_region_init_io(&h->conf_mem, OBJECT(s), &ppc440_pcix_host_conf_ops, h, "pci-conf-idx", 4); memory_region_init_io(&h->data_mem, OBJECT(s), &pci_host_data_le_ops, h, "pci-conf-data", 4); - memory_region_init_io(&s->iomem, OBJECT(s), &pci_reg_ops, s, - "pci.reg", PPC440_REG_SIZE); + memory_region_init_io(&s->regs, OBJECT(s), &pci_reg_ops, s, "pci-reg", + PPC440_REG_SIZE); memory_region_add_subregion(&s->container, PCIC0_CFGADDR, &h->conf_mem); memory_region_add_subregion(&s->container, PCIC0_CFGDATA, &h->data_mem); - memory_region_add_subregion(&s->container, PPC440_REG_BASE, &s->iomem); + memory_region_add_subregion(&s->container, PPC440_REG_BASE, &s->regs); sysbus_init_mmio(sbd, &s->container); + sysbus_init_mmio(sbd, &s->iomem); } static void ppc440_pcix_class_init(ObjectClass *klass, void *data) @@ -524,7 +528,7 @@ static void ppc440_pcix_class_init(ObjectClass *klass, void *data) } static const TypeInfo ppc440_pcix_info = { - .name = TYPE_PPC440_PCIX_HOST_BRIDGE, + .name = TYPE_PPC440_PCIX_HOST, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(PPC440PCIXState), .class_init = ppc440_pcix_class_init, diff --git a/hw/ppc/ppc4xx_pci.c b/hw/pci-host/ppc4xx_pci.c similarity index 91% rename from hw/ppc/ppc4xx_pci.c rename to hw/pci-host/ppc4xx_pci.c index 5df97e6d15..b6c6c8993c 100644 --- a/hw/ppc/ppc4xx_pci.c +++ b/hw/pci-host/ppc4xx_pci.c @@ -16,18 +16,19 @@ * Authors: Hollis Blanchard */ -/* This file implements emulation of the 32-bit PCI controller found in some - * 4xx SoCs, such as the 440EP. */ +/* + * This file implements emulation of the 32-bit PCI controller found in some + * 4xx SoCs, such as the 440EP. + */ #include "qemu/osdep.h" #include "qemu/log.h" #include "hw/irq.h" -#include "hw/ppc/ppc.h" -#include "hw/ppc/ppc4xx.h" +#include "hw/pci-host/ppc4xx.h" #include "migration/vmstate.h" #include "qemu/module.h" #include "sysemu/reset.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" #include "trace.h" #include "qom/object.h" @@ -44,7 +45,7 @@ struct PCITargetMap { uint32_t la; }; -OBJECT_DECLARE_SIMPLE_TYPE(PPC4xxPCIState, PPC4xx_PCI_HOST_BRIDGE) +OBJECT_DECLARE_SIMPLE_TYPE(PPC4xxPCIState, PPC4xx_PCI_HOST) #define PPC4xx_PCI_NR_PMMS 3 #define PPC4xx_PCI_NR_PTMS 2 @@ -65,8 +66,10 @@ struct PPC4xxPCIState { #define PCIC0_CFGADDR 0x0 #define PCIC0_CFGDATA 0x4 -/* PLB Memory Map (PMM) registers specify which PLB addresses are translated to - * PCI accesses. */ +/* + * PLB Memory Map (PMM) registers specify which PLB addresses are translated to + * PCI accesses. + */ #define PCIL0_PMM0LA 0x0 #define PCIL0_PMM0MA 0x4 #define PCIL0_PMM0PCILA 0x8 @@ -80,8 +83,10 @@ struct PPC4xxPCIState { #define PCIL0_PMM2PCILA 0x28 #define PCIL0_PMM2PCIHA 0x2c -/* PCI Target Map (PTM) registers specify which PCI addresses are translated to - * PLB accesses. */ +/* + * PCI Target Map (PTM) registers specify which PCI addresses are translated to + * PLB accesses. + */ #define PCIL0_PTM1MS 0x30 #define PCIL0_PTM1LA 0x34 #define PCIL0_PTM2MS 0x38 @@ -96,9 +101,10 @@ static void ppc4xx_pci_reg_write4(void *opaque, hwaddr offset, { struct PPC4xxPCIState *pci = opaque; - /* We ignore all target attempts at PCI configuration, effectively - * assuming a bidirectional 1:1 mapping of PLB and PCI space. */ - + /* + * We ignore all target attempts at PCI configuration, effectively + * assuming a bidirectional 1:1 mapping of PLB and PCI space. + */ switch (offset) { case PCIL0_PMM0LA: pci->pmm[0].la = value; @@ -243,8 +249,10 @@ static void ppc4xx_pci_reset(void *opaque) memset(pci->ptm, 0, sizeof(pci->ptm)); } -/* On Bamboo, all pins from each slot are tied to a single board IRQ. This - * may need further refactoring for other boards. */ +/* + * On Bamboo, all pins from each slot are tied to a single board IRQ. + * This may need further refactoring for other boards. + */ static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num) { int slot = PCI_SLOT(pci_dev->devfn); @@ -267,7 +275,7 @@ static const VMStateDescription vmstate_pci_master_map = { .name = "pci_master_map", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(la, struct PCIMasterMap), VMSTATE_UINT32(ma, struct PCIMasterMap), VMSTATE_UINT32(pcila, struct PCIMasterMap), @@ -280,7 +288,7 @@ static const VMStateDescription vmstate_pci_target_map = { .name = "pci_target_map", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ms, struct PCITargetMap), VMSTATE_UINT32(la, struct PCITargetMap), VMSTATE_END_OF_LIST() @@ -291,7 +299,7 @@ static const VMStateDescription vmstate_ppc4xx_pci = { .name = "ppc4xx_pci", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(pmm, PPC4xxPCIState, PPC4xx_PCI_NR_PMMS, 1, vmstate_pci_master_map, struct PCIMasterMap), @@ -312,7 +320,7 @@ static void ppc4xx_pcihost_realize(DeviceState *dev, Error **errp) int i; h = PCI_HOST_BRIDGE(dev); - s = PPC4xx_PCI_HOST_BRIDGE(dev); + s = PPC4xx_PCI_HOST(dev); for (i = 0; i < ARRAY_SIZE(s->irq); i++) { sysbus_init_irq(sbd, &s->irq[i]); @@ -324,7 +332,7 @@ static void ppc4xx_pcihost_realize(DeviceState *dev, Error **errp) TYPE_PCI_BUS); h->bus = b; - pci_create_simple(b, 0, "ppc4xx-host-bridge"); + pci_create_simple(b, 0, TYPE_PPC4xx_HOST_BRIDGE); /* XXX split into 2 memory regions, one for config space, one for regs */ memory_region_init(&s->container, OBJECT(s), "pci-container", PCI_ALL_SIZE); @@ -358,7 +366,7 @@ static void ppc4xx_host_bridge_class_init(ObjectClass *klass, void *data) } static const TypeInfo ppc4xx_host_bridge_info = { - .name = "ppc4xx-host-bridge", + .name = TYPE_PPC4xx_HOST_BRIDGE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = ppc4xx_host_bridge_class_init, @@ -377,7 +385,7 @@ static void ppc4xx_pcihost_class_init(ObjectClass *klass, void *data) } static const TypeInfo ppc4xx_pcihost_info = { - .name = TYPE_PPC4xx_PCI_HOST_BRIDGE, + .name = TYPE_PPC4xx_PCI_HOST, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(PPC4xxPCIState), .class_init = ppc4xx_pcihost_class_init, diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index 89c1b53dd7..95b983b2b3 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -5,7 +5,7 @@ * * Author: Yu Liu, * - * This file is derived from hw/ppc4xx_pci.c, + * This file is derived from ppc4xx_pci.c, * the copyright for that material belongs to the original owners. * * This is free software; you can redistribute it and/or modify @@ -19,7 +19,7 @@ #include "hw/ppc/e500-ccsr.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" #include "qemu/bswap.h" #include "qemu/module.h" @@ -189,7 +189,7 @@ static uint64_t pci_reg_read4(void *opaque, hwaddr addr, break; } - pci_debug("%s: win:%lx(addr:" TARGET_FMT_plx ") -> value:%x\n", __func__, + pci_debug("%s: win:%lx(addr:" HWADDR_FMT_plx ") -> value:%x\n", __func__, win, addr, value); return value; } @@ -268,7 +268,7 @@ static void pci_reg_write4(void *opaque, hwaddr addr, win = addr & 0xfe0; - pci_debug("%s: value:%x -> win:%lx(addr:" TARGET_FMT_plx ")\n", + pci_debug("%s: value:%x -> win:%lx(addr:" HWADDR_FMT_plx ")\n", __func__, (unsigned)value, win, addr); switch (win) { @@ -379,7 +379,7 @@ static const VMStateDescription vmstate_pci_outbound = { .name = "pci_outbound", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(potar, struct pci_outbound), VMSTATE_UINT32(potear, struct pci_outbound), VMSTATE_UINT32(powbar, struct pci_outbound), @@ -392,7 +392,7 @@ static const VMStateDescription vmstate_pci_inbound = { .name = "pci_inbound", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(pitar, struct pci_inbound), VMSTATE_UINT32(piwbar, struct pci_inbound), VMSTATE_UINT32(piwbear, struct pci_inbound), @@ -405,7 +405,7 @@ static const VMStateDescription vmstate_ppce500_pci = { .name = "ppce500_pci", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(pob, PPCE500PCIState, PPCE500_PCI_NR_POBS, 1, vmstate_pci_outbound, struct pci_outbound), VMSTATE_STRUCT_ARRAY(pib, PPCE500PCIState, PPCE500_PCI_NR_PIBS, 1, @@ -435,6 +435,10 @@ static AddressSpace *e500_pcihost_set_iommu(PCIBus *bus, void *opaque, return &s->bm_as; } +static const PCIIOMMUOps ppce500_iommu_ops = { + .get_address_space = e500_pcihost_set_iommu, +}; + static void e500_pcihost_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); @@ -469,7 +473,7 @@ static void e500_pcihost_realize(DeviceState *dev, Error **errp) memory_region_init(&s->bm, OBJECT(s), "bm-e500", UINT64_MAX); memory_region_add_subregion(&s->bm, 0x0, &s->busmem); address_space_init(&s->bm_as, &s->bm, "pci-bm"); - pci_setup_iommu(b, e500_pcihost_set_iommu, s); + pci_setup_iommu(b, &ppce500_iommu_ops, s); pci_create_simple(b, 0, "e500-host-bridge"); diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index ab5a47aff5..0d7d4e3f08 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -50,10 +50,12 @@ static void q35_host_realize(DeviceState *dev, Error **errp) Q35PCIHost *s = Q35_HOST_DEVICE(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - sysbus_add_io(sbd, MCH_HOST_BRIDGE_CONFIG_ADDR, &pci->conf_mem); + memory_region_add_subregion(s->mch.address_space_io, + MCH_HOST_BRIDGE_CONFIG_ADDR, &pci->conf_mem); sysbus_init_ioports(sbd, MCH_HOST_BRIDGE_CONFIG_ADDR, 4); - sysbus_add_io(sbd, MCH_HOST_BRIDGE_CONFIG_DATA, &pci->data_mem); + memory_region_add_subregion(s->mch.address_space_io, + MCH_HOST_BRIDGE_CONFIG_DATA, &pci->data_mem); sysbus_init_ioports(sbd, MCH_HOST_BRIDGE_CONFIG_DATA, 4); /* register q35 0xcf8 port as coalesced pio */ @@ -64,21 +66,13 @@ static void q35_host_realize(DeviceState *dev, Error **errp) s->mch.pci_address_space, s->mch.address_space_io, 0, TYPE_PCIE_BUS); - PC_MACHINE(qdev_get_machine())->bus = pci->bus; - pci->bypass_iommu = - PC_MACHINE(qdev_get_machine())->default_bus_bypass_iommu; + qdev_realize(DEVICE(&s->mch), BUS(pci->bus), &error_fatal); } static const char *q35_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { - Q35PCIHost *s = Q35_HOST_DEVICE(host_bridge); - - /* For backwards compat with old device paths */ - if (s->mch.short_root_bus) { - return "0000"; - } return "0000:00"; } @@ -181,7 +175,6 @@ static Property q35_host_props[] = { MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT), DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, Q35PCIHost, mch.pci_hole64_size, Q35_PCI_HOST_HOLE64_SIZE_DEFAULT), - DEFINE_PROP_UINT32("short_root_bus", Q35PCIHost, mch.short_root_bus, 0), DEFINE_PROP_SIZE(PCI_HOST_BELOW_4G_MEM_SIZE, Q35PCIHost, mch.below_4g_mem_size, 0), DEFINE_PROP_SIZE(PCI_HOST_ABOVE_4G_MEM_SIZE, Q35PCIHost, @@ -240,19 +233,19 @@ static void q35_host_initfn(Object *obj) object_property_add_uint64_ptr(obj, PCIE_HOST_MCFG_SIZE, &pehb->size, OBJ_PROP_FLAG_READ); - object_property_add_link(obj, MCH_HOST_PROP_RAM_MEM, TYPE_MEMORY_REGION, + object_property_add_link(obj, PCI_HOST_PROP_RAM_MEM, TYPE_MEMORY_REGION, (Object **) &s->mch.ram_memory, qdev_prop_allow_set_link_before_realize, 0); - object_property_add_link(obj, MCH_HOST_PROP_PCI_MEM, TYPE_MEMORY_REGION, + object_property_add_link(obj, PCI_HOST_PROP_PCI_MEM, TYPE_MEMORY_REGION, (Object **) &s->mch.pci_address_space, qdev_prop_allow_set_link_before_realize, 0); - object_property_add_link(obj, MCH_HOST_PROP_SYSTEM_MEM, TYPE_MEMORY_REGION, + object_property_add_link(obj, PCI_HOST_PROP_SYSTEM_MEM, TYPE_MEMORY_REGION, (Object **) &s->mch.system_memory, qdev_prop_allow_set_link_before_realize, 0); - object_property_add_link(obj, MCH_HOST_PROP_IO_MEM, TYPE_MEMORY_REGION, + object_property_add_link(obj, PCI_HOST_PROP_IO_MEM, TYPE_MEMORY_REGION, (Object **) &s->mch.address_space_io, qdev_prop_allow_set_link_before_realize, 0); } @@ -283,7 +276,6 @@ static void blackhole_write(void *opaque, hwaddr addr, uint64_t val, 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, .impl.min_access_size = 4, @@ -379,7 +371,8 @@ static void mch_update_smram(MCHPCIState *mch) memory_region_set_enabled(&mch->high_smram, false); } - if (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_T_EN) { + if ((pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_T_EN) && + (pd->config[MCH_HOST_BRIDGE_SMRAM] & SMRAM_G_SMRAME)) { switch (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK) { case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB: @@ -527,7 +520,7 @@ static const VMStateDescription vmstate_mch = { .version_id = 1, .minimum_version_id = 1, .post_load = mch_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, MCHPCIState), /* Used to be smm_enabled, which was basically always zero because * SeaBIOS hardly uses SMM. SMRAM is now handled by CPU code. @@ -573,8 +566,7 @@ static void mch_realize(PCIDevice *d, Error **errp) } /* setup pci memory mapping */ - pc_pci_as_mapping_init(OBJECT(mch), mch->system_memory, - mch->pci_address_space); + pc_pci_as_mapping_init(mch->system_memory, mch->pci_address_space); /* if *disabled* show SMRAM to all CPUs */ memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", @@ -643,12 +635,12 @@ static void mch_realize(PCIDevice *d, Error **errp) object_property_add_const_link(qdev_get_machine(), "smram", OBJECT(&mch->smram)); - init_pam(DEVICE(mch), mch->ram_memory, mch->system_memory, - mch->pci_address_space, &mch->pam_regions[0], + init_pam(&mch->pam_regions[0], OBJECT(mch), mch->ram_memory, + mch->system_memory, mch->pci_address_space, PAM_BIOS_BASE, PAM_BIOS_SIZE); for (i = 0; i < ARRAY_SIZE(mch->pam_regions) - 1; ++i) { - init_pam(DEVICE(mch), mch->ram_memory, mch->system_memory, - mch->pci_address_space, &mch->pam_regions[i+1], + init_pam(&mch->pam_regions[i + 1], OBJECT(mch), mch->ram_memory, + mch->system_memory, mch->pci_address_space, PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); } } diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index 7a105e4a63..a7dfddd69e 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -28,7 +28,7 @@ #include "qemu/units.h" #include "qemu/log.h" #include "qapi/error.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" #include "hw/qdev-properties.h" @@ -60,7 +60,7 @@ DECLARE_INSTANCE_CHECKER(PREPPCIState, RAVEN_PCI_HOST_BRIDGE, struct PRePPCIState { PCIHostState parent_obj; - qemu_or_irq *or_irq; + OrIRQState *or_irq; qemu_irq pci_irqs[PCI_NUM_PINS]; PCIBus pci_bus; AddressSpace pci_io_as; @@ -200,6 +200,7 @@ static const MemoryRegionOps raven_io_ops = { .write = raven_io_write, .endianness = DEVICE_LITTLE_ENDIAN, .impl.max_access_size = 4, + .impl.unaligned = true, .valid.unaligned = true, }; @@ -223,6 +224,10 @@ static AddressSpace *raven_pcihost_set_iommu(PCIBus *bus, void *opaque, return &s->bm_as; } +static const PCIIOMMUOps raven_iommu_ops = { + .get_address_space = raven_pcihost_set_iommu, +}; + static void raven_change_gpio(void *opaque, int n, int level) { PREPPCIState *s = opaque; @@ -258,7 +263,8 @@ static void raven_pcihost_realizefn(DeviceState *d, Error **errp) qdev_init_gpio_in(d, raven_change_gpio, 1); - pci_bus_irqs(&s->pci_bus, raven_set_irq, raven_map_irq, s, PCI_NUM_PINS); + pci_bus_irqs(&s->pci_bus, raven_set_irq, s, PCI_NUM_PINS); + pci_bus_map_irqs(&s->pci_bus, raven_map_irq); memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, s, "pci-conf-idx", 4); @@ -293,6 +299,13 @@ static void raven_pcihost_initfn(Object *obj) memory_region_init(&s->pci_memory, obj, "pci-memory", 0x3f000000); address_space_init(&s->pci_io_as, &s->pci_io, "raven-io"); + /* + * Raven's raven_io_ops use the address-space API to access pci-conf-idx + * (which is also owned by the raven device). As such, mark the + * pci_io_non_contiguous as re-entrancy safe. + */ + s->pci_io_non_contiguous.disable_reentrancy_guard = true; + /* CPU address space */ memory_region_add_subregion(address_space_mem, PCI_IO_BASE_ADDR, &s->pci_io); @@ -312,7 +325,7 @@ static void raven_pcihost_initfn(Object *obj) memory_region_add_subregion(&s->bm, 0 , &s->bm_pci_memory_alias); memory_region_add_subregion(&s->bm, 0x80000000, &s->bm_ram_alias); address_space_init(&s->bm_as, &s->bm, "raven-bm"); - pci_setup_iommu(&s->pci_bus, raven_pcihost_set_iommu, s); + pci_setup_iommu(&s->pci_bus, &raven_iommu_ops, s); h->bus = &s->pci_bus; @@ -329,12 +342,14 @@ static void raven_realize(PCIDevice *d, Error **errp) char *filename; int bios_size = -1; - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - d->config[0x34] = 0x00; // capabilities_pointer + d->config[PCI_CACHE_LINE_SIZE] = 0x08; + d->config[PCI_LATENCY_TIMER] = 0x10; + d->config[PCI_CAPABILITY_LIST] = 0x00; - memory_region_init_rom_nomigrate(&s->bios, OBJECT(s), "bios", BIOS_SIZE, - &error_fatal); + if (!memory_region_init_rom_nomigrate(&s->bios, OBJECT(s), "bios", + BIOS_SIZE, errp)) { + return; + } memory_region_add_subregion(get_system_memory(), (uint32_t)(-BIOS_SIZE), &s->bios); if (s->bios_name) { @@ -371,7 +386,7 @@ static const VMStateDescription vmstate_raven = { .name = "raven", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, RavenPCIState), VMSTATE_END_OF_LIST() }, diff --git a/hw/pci-host/sabre.c b/hw/pci-host/sabre.c index 949ecc21f2..d0851b48b0 100644 --- a/hw/pci-host/sabre.c +++ b/hw/pci-host/sabre.c @@ -112,6 +112,10 @@ static AddressSpace *sabre_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &is->iommu_as; } +static const PCIIOMMUOps sabre_iommu_ops = { + .get_address_space = sabre_pci_dma_iommu, +}; + static void sabre_config_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -384,17 +388,15 @@ static void sabre_realize(DeviceState *dev, Error **errp) /* IOMMU */ memory_region_add_subregion_overlap(&s->sabre_config, 0x200, sysbus_mmio_get_region(SYS_BUS_DEVICE(s->iommu), 0), 1); - pci_setup_iommu(phb->bus, sabre_pci_dma_iommu, s->iommu); + pci_setup_iommu(phb->bus, &sabre_iommu_ops, s->iommu); /* APB secondary busses */ - pci_dev = pci_new_multifunction(PCI_DEVFN(1, 0), true, - TYPE_SIMBA_PCI_BRIDGE); + pci_dev = pci_new_multifunction(PCI_DEVFN(1, 0), TYPE_SIMBA_PCI_BRIDGE); s->bridgeB = PCI_BRIDGE(pci_dev); pci_bridge_map_irq(s->bridgeB, "pciB", pci_simbaB_map_irq); pci_realize_and_unref(pci_dev, phb->bus, &error_fatal); - pci_dev = pci_new_multifunction(PCI_DEVFN(1, 1), true, - TYPE_SIMBA_PCI_BRIDGE); + pci_dev = pci_new_multifunction(PCI_DEVFN(1, 1), TYPE_SIMBA_PCI_BRIDGE); s->bridgeA = PCI_BRIDGE(pci_dev); pci_bridge_map_irq(s->bridgeA, "pciA", pci_simbaA_map_irq); pci_realize_and_unref(pci_dev, phb->bus, &error_fatal); diff --git a/hw/pci-host/sh_pci.c b/hw/pci-host/sh_pci.c index 719d6ca2a6..4edebced5e 100644 --- a/hw/pci-host/sh_pci.c +++ b/hw/pci-host/sh_pci.c @@ -26,7 +26,7 @@ #include "hw/sysbus.h" #include "hw/sh4/sh.h" #include "hw/irq.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" #include "qemu/bswap.h" #include "qemu/module.h" @@ -40,7 +40,7 @@ struct SHPCIState { PCIHostState parent_obj; PCIDevice *dev; - qemu_irq irq[4]; + qemu_irq irq[PCI_NUM_PINS]; MemoryRegion memconfig_p4; MemoryRegion memconfig_a7; MemoryRegion isa; @@ -116,7 +116,7 @@ static void sh_pci_set_irq(void *opaque, int irq_num, int level) qemu_set_irq(pic[irq_num], level); } -static void sh_pci_device_realize(DeviceState *dev, Error **errp) +static void sh_pcic_host_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); SHPCIState *s = SH_PCI_HOST_BRIDGE(dev); @@ -131,7 +131,8 @@ static void sh_pci_device_realize(DeviceState *dev, Error **errp) s->irq, get_system_memory(), get_system_io(), - PCI_DEVFN(0, 0), 4, TYPE_PCI_BUS); + PCI_DEVFN(0, 0), PCI_NUM_PINS, + TYPE_PCI_BUS); memory_region_init_io(&s->memconfig_p4, OBJECT(s), &sh_pci_reg_ops, s, "sh_pci", 0x224); memory_region_init_alias(&s->memconfig_a7, OBJECT(s), "sh_pci.2", @@ -145,19 +146,19 @@ static void sh_pci_device_realize(DeviceState *dev, Error **errp) s->dev = pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "sh_pci_host"); } -static void sh_pci_host_realize(PCIDevice *d, Error **errp) +static void sh_pcic_pci_realize(PCIDevice *d, Error **errp) { pci_set_word(d->config + PCI_COMMAND, PCI_COMMAND_WAIT); pci_set_word(d->config + PCI_STATUS, PCI_STATUS_CAP_LIST | PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); } -static void sh_pci_host_class_init(ObjectClass *klass, void *data) +static void sh_pcic_pci_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->realize = sh_pci_host_realize; + k->realize = sh_pcic_pci_realize; k->vendor_id = PCI_VENDOR_ID_HITACHI; k->device_id = PCI_DEVICE_ID_HITACHI_SH7751R; /* @@ -167,35 +168,29 @@ static void sh_pci_host_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; } -static const TypeInfo sh_pci_host_info = { - .name = "sh_pci_host", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = sh_pci_host_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, - }, -}; - -static void sh_pci_device_class_init(ObjectClass *klass, void *data) +static void sh_pcic_host_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->realize = sh_pci_device_realize; + dc->realize = sh_pcic_host_realize; } -static const TypeInfo sh_pci_device_info = { - .name = TYPE_SH_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(SHPCIState), - .class_init = sh_pci_device_class_init, +static const TypeInfo sh_pcic_types[] = { + { + .name = TYPE_SH_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(SHPCIState), + .class_init = sh_pcic_host_class_init, + }, { + .name = "sh_pci_host", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = sh_pcic_pci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, + }, }; -static void sh_pci_register_types(void) -{ - type_register_static(&sh_pci_device_info); - type_register_static(&sh_pci_host_info); -} - -type_init(sh_pci_register_types) +DEFINE_TYPES(sh_pcic_types) diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events index 437e66ff50..0a816b9aa1 100644 --- a/hw/pci-host/trace-events +++ b/hw/pci-host/trace-events @@ -6,6 +6,13 @@ bonito_spciconf_small_access(uint64_t addr, unsigned size) "PCI config address i # grackle.c grackle_set_irq(int irq_num, int level) "set_irq num %d level %d" +# gt64120.c +gt64120_read(uint64_t addr, uint64_t value) "gt64120 read 0x%03"PRIx64" value:0x%08" PRIx64 +gt64120_write(uint64_t addr, uint64_t value) "gt64120 write 0x%03"PRIx64" value:0x%08" PRIx64 +gt64120_read_intreg(const char *regname, unsigned size, uint64_t value) "gt64120 read %s size:%u value:0x%08" PRIx64 +gt64120_write_intreg(const char *regname, unsigned size, uint64_t value) "gt64120 write %s size:%u value:0x%08" PRIx64 +gt64120_isd_remap(uint64_t from_length, uint64_t from_addr, uint64_t to_length, uint64_t to_addr) "ISD: 0x%08" PRIx64 "@0x%08" PRIx64 " -> 0x%08" PRIx64 "@0x%08" PRIx64 + # mv64361.c mv64361_region_map(const char *name, uint64_t poffs, uint64_t size, uint64_t moffs) "Mapping %s 0x%"PRIx64"+0x%"PRIx64" @ 0x%"PRIx64 mv64361_region_enable(const char *op, int num) "Should %s region %d" @@ -30,6 +37,18 @@ unin_data_read(uint64_t addr, unsigned len, uint64_t val) "read addr 0x%"PRIx64 unin_write(uint64_t addr, uint64_t value) "addr=0x%" PRIx64 " val=0x%"PRIx64 unin_read(uint64_t addr, uint64_t value) "addr=0x%" PRIx64 " val=0x%"PRIx64 +# ppc4xx_pci.c +ppc4xx_pci_map_irq(int32_t devfn, int irq_num, int slot) "devfn 0x%x irq %d -> %d" +ppc4xx_pci_set_irq(int irq_num) "PCI irq %d" + +# ppc440_pcix.c +ppc440_pcix_map_irq(int32_t devfn, int irq_num, int slot) "devfn 0x%x irq %d -> %d" +ppc440_pcix_set_irq(int irq_num) "PCI irq %d" +ppc440_pcix_update_pim(int idx, uint64_t size, uint64_t la) "Added window %d of size=0x%" PRIx64 " to CPU=0x%" PRIx64 +ppc440_pcix_update_pom(int idx, uint32_t size, uint64_t la, uint64_t pcia) "Added window %d of size=0x%x from CPU=0x%" PRIx64 " to PCI=0x%" PRIx64 +ppc440_pcix_reg_read(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " = 0x%" PRIx32 +ppc440_pcix_reg_write(uint64_t addr, uint32_t val, uint32_t size) "addr 0x%" PRIx64 " = 0x%" PRIx32 " size 0x%" PRIx32 + # pnv_phb4.c pnv_phb4_xive_notify(uint64_t notif_port, uint64_t data) "notif=@0x%"PRIx64" data=0x%"PRIx64 pnv_phb4_xive_notify_ic(uint64_t addr, uint64_t data) "addr=@0x%"PRIx64" data=0x%"PRIx64 @@ -39,3 +58,14 @@ pnv_phb4_xive_notify_abt(uint64_t notif_port, uint64_t data) "notif=@0x%"PRIx64" 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" + +# astro.c +astro_chip_mem_valid(uint64_t addr, uint32_t val) "access to addr 0x%"PRIx64" is %d" +astro_chip_read(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64 +astro_chip_write(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64 +elroy_read(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64 +elroy_write(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64 +elroy_pci_config_data_read(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64 +elroy_pci_config_data_write(uint64_t addr, int size, uint64_t val) "addr 0x%"PRIx64" size %d val 0x%"PRIx64 +iosapic_reg_write(uint64_t reg_select, int size, uint64_t val) "reg_select 0x%"PRIx64" size %d val 0x%"PRIx64 +iosapic_reg_read(uint64_t reg_select, int size, uint64_t val) "reg_select 0x%"PRIx64" size %d val 0x%"PRIx64 diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index d25b62d6a5..e4c1abd871 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -24,10 +24,9 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/ppc/mac.h" #include "hw/qdev-properties.h" #include "qemu/module.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" #include "hw/pci-host/uninorth.h" #include "trace.h" @@ -129,11 +128,10 @@ static void pci_unin_main_realize(DeviceState *dev, Error **errp) pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-pci"); - /* DEC 21154 bridge */ -#if 0 - /* XXX: not activated as PPC BIOS doesn't handle multiple buses properly */ - pci_create_simple(h->bus, PCI_DEVFN(12, 0), "dec-21154"); -#endif + /* + * DEC 21154 bridge was unused for many years, this comment is + * a placeholder for whoever wishes to resurrect it + */ } static void pci_unin_main_init(Object *obj) @@ -278,12 +276,9 @@ static void pci_unin_internal_init(Object *obj) static void unin_main_pci_host_realize(PCIDevice *d, Error **errp) { - /* cache_line_size */ - d->config[0x0C] = 0x08; - /* latency_timer */ - d->config[0x0D] = 0x10; - /* capabilities_pointer */ - d->config[0x34] = 0x00; + d->config[PCI_CACHE_LINE_SIZE] = 0x08; + d->config[PCI_LATENCY_TIMER] = 0x10; + d->config[PCI_CAPABILITY_LIST] = 0x00; /* * Set kMacRISCPCIAddressSelect (0x48) register to indicate PCI @@ -298,30 +293,22 @@ static void unin_main_pci_host_realize(PCIDevice *d, Error **errp) static void unin_agp_pci_host_realize(PCIDevice *d, Error **errp) { - /* cache_line_size */ - d->config[0x0C] = 0x08; - /* latency_timer */ - d->config[0x0D] = 0x10; - /* capabilities_pointer - d->config[0x34] = 0x80; */ + d->config[PCI_CACHE_LINE_SIZE] = 0x08; + d->config[PCI_LATENCY_TIMER] = 0x10; + /* d->config[PCI_CAPABILITY_LIST] = 0x80; */ } static void u3_agp_pci_host_realize(PCIDevice *d, Error **errp) { - /* cache line size */ - d->config[0x0C] = 0x08; - /* latency timer */ - d->config[0x0D] = 0x10; + d->config[PCI_CACHE_LINE_SIZE] = 0x08; + d->config[PCI_LATENCY_TIMER] = 0x10; } static void unin_internal_pci_host_realize(PCIDevice *d, Error **errp) { - /* cache_line_size */ - d->config[0x0C] = 0x08; - /* latency_timer */ - d->config[0x0D] = 0x10; - /* capabilities_pointer */ - d->config[0x34] = 0x00; + d->config[PCI_CACHE_LINE_SIZE] = 0x08; + d->config[PCI_LATENCY_TIMER] = 0x10; + d->config[PCI_CAPABILITY_LIST] = 0x00; } static void unin_main_pci_host_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index f66384fa02..0e65deb3f9 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -12,7 +12,7 @@ #include "hw/sysbus.h" #include "migration/vmstate.h" #include "hw/irq.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" #include "hw/qdev-properties.h" @@ -147,7 +147,7 @@ static const VMStateDescription pci_vpb_vmstate = { .version_id = 1, .minimum_version_id = 1, .post_load = pci_vpb_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(imap, PCIVPBState, 3), VMSTATE_UINT32_ARRAY(smap, PCIVPBState, 3), VMSTATE_UINT32(selfid, PCIVPBState), @@ -422,7 +422,8 @@ static void pci_vpb_realize(DeviceState *dev, Error **errp) mapfn = pci_vpb_map_irq; } - pci_bus_irqs(&s->pci_bus, pci_vpb_set_irq, mapfn, s->irq, 4); + pci_bus_irqs(&s->pci_bus, pci_vpb_set_irq, s->irq, 4); + pci_bus_map_irqs(&s->pci_bus, mapfn); /* Our memory regions are: * 0 : our control registers diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index 38d5901a45..c9ab7052f4 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -298,7 +298,6 @@ static void xilinx_pcie_root_class_init(ObjectClass *klass, void *data) k->device_id = 0x7021; k->revision = 0; k->class_id = PCI_CLASS_BRIDGE_HOST; - k->is_bridge = true; k->realize = xilinx_pcie_root_realize; k->exit = pci_bridge_exitfn; dc->reset = pci_bridge_reset; diff --git a/hw/pci/Kconfig b/hw/pci/Kconfig index 77f8b005ff..fe70902cd8 100644 --- a/hw/pci/Kconfig +++ b/hw/pci/Kconfig @@ -8,6 +8,9 @@ config PCI_EXPRESS config PCI_DEVICES bool +config PCIE_DEVICES + bool + config MSI_NONBROKEN # selected by interrupt controllers that do not support MSI, # or support it and have a good implementation. See commit diff --git a/hw/pci/meson.build b/hw/pci/meson.build index bcc9c75919..b9c34b2acf 100644 --- a/hw/pci/meson.build +++ b/hw/pci/meson.build @@ -5,6 +5,8 @@ pci_ss.add(files( 'pci.c', 'pci_bridge.c', 'pci_host.c', + 'pci-hmp-cmds.c', + 'pci-qmp-cmds.c', 'pcie_sriov.c', 'shpc.c', 'slotid_cap.c' @@ -13,8 +15,8 @@ pci_ss.add(files( # allow plugging PCIe devices into PCI buses, include them even if # CONFIG_PCI_EXPRESS=n. pci_ss.add(files('pcie.c', 'pcie_aer.c')) -softmmu_ss.add(when: 'CONFIG_PCI_EXPRESS', if_true: files('pcie_port.c', 'pcie_host.c')) -softmmu_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) +pci_ss.add(files('pcie_doe.c')) +system_ss.add(when: 'CONFIG_PCI_EXPRESS', if_true: files('pcie_port.c', 'pcie_host.c')) +system_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) -softmmu_ss.add(when: 'CONFIG_PCI', if_false: files('pci-stub.c')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('pci-stub.c')) +system_ss.add(when: 'CONFIG_PCI', if_false: files('pci-stub.c')) diff --git a/hw/pci/msi.c b/hw/pci/msi.c index 47d2b0f33c..8104ac1d91 100644 --- a/hw/pci/msi.c +++ b/hw/pci/msi.c @@ -23,6 +23,9 @@ #include "hw/xen/xen.h" #include "qemu/range.h" #include "qapi/error.h" +#include "sysemu/xen.h" + +#include "hw/i386/kvm/xen_evtchn.h" /* PCI_MSI_ADDRESS_LO */ #define PCI_MSI_ADDRESS_LO_MASK (~0x3) @@ -134,7 +137,7 @@ void msi_set_message(PCIDevice *dev, MSIMessage msg) pci_set_word(dev->config + msi_data_off(dev, msi64bit), msg.data); } -MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector) +static MSIMessage msi_prepare_message(PCIDevice *dev, unsigned int vector) { uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; @@ -159,6 +162,11 @@ MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector) return msg; } +MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector) +{ + return dev->msi_prepare_message(dev, vector); +} + bool msi_enabled(const PCIDevice *dev) { return msi_present(dev) && @@ -241,6 +249,8 @@ int msi_init(struct PCIDevice *dev, uint8_t offset, 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors)); } + dev->msi_prepare_message = msi_prepare_message; + return 0; } @@ -256,6 +266,7 @@ void msi_uninit(struct PCIDevice *dev) cap_size = msi_cap_sizeof(flags); pci_del_capability(dev, PCI_CAP_ID_MSI, cap_size); dev->cap_present &= ~QEMU_PCI_CAP_MSI; + dev->msi_prepare_message = NULL; MSI_DEV_PRINTF(dev, "uninit\n"); } @@ -298,7 +309,7 @@ bool msi_is_masked(const PCIDevice *dev, unsigned int vector) } data = pci_get_word(dev->config + msi_data_off(dev, msi64bit)); - if (xen_is_pirq_msi(data)) { + if (xen_enabled() && xen_is_pirq_msi(data)) { return false; } @@ -307,6 +318,38 @@ bool msi_is_masked(const PCIDevice *dev, unsigned int vector) return mask & (1U << vector); } +void msi_set_mask(PCIDevice *dev, int vector, bool mask, Error **errp) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; + uint32_t irq_state, vector_mask, pending; + + if (vector >= PCI_MSI_VECTORS_MAX) { + error_setg(errp, "msi: vector %d not allocated. max vector is %d", + vector, (PCI_MSI_VECTORS_MAX - 1)); + return; + } + + vector_mask = (1U << vector); + + irq_state = pci_get_long(dev->config + msi_mask_off(dev, msi64bit)); + + if (mask) { + irq_state |= vector_mask; + } else { + irq_state &= ~vector_mask; + } + + pci_set_long(dev->config + msi_mask_off(dev, msi64bit), irq_state); + + pending = pci_get_long(dev->config + msi_pending_off(dev, msi64bit)); + if (!mask && (pending & vector_mask)) { + pending &= ~vector_mask; + pci_set_long(dev->config + msi_pending_off(dev, msi64bit), pending); + msi_notify(dev, vector); + } +} + void msi_notify(PCIDevice *dev, unsigned int vector) { uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); @@ -334,11 +377,7 @@ void msi_notify(PCIDevice *dev, unsigned int vector) void msi_send_message(PCIDevice *dev, MSIMessage msg) { - MemTxAttrs attrs = {}; - - attrs.requester_id = pci_requester_id(dev); - address_space_stl_le(&dev->bus_master_as, msg.address, msg.data, - attrs, NULL); + dev->msi_trigger(dev, msg); } /* Normally called by pci_default_write_config(). */ @@ -378,6 +417,15 @@ void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) fprintf(stderr, "\n"); #endif + if (xen_mode == XEN_EMULATE) { + for (vector = 0; vector < msi_nr_vectors(flags); vector++) { + MSIMessage msg = msi_prepare_message(dev, vector); + + xen_evtchn_snoop_msi(dev, false, vector, msg.address, msg.data, + msi_is_masked(dev, vector)); + } + } + if (!(flags & PCI_MSI_FLAGS_ENABLE)) { return; } diff --git a/hw/pci/msix.c b/hw/pci/msix.c index ae9331cd0b..487e49834e 100644 --- a/hw/pci/msix.c +++ b/hw/pci/msix.c @@ -26,12 +26,14 @@ #include "qapi/error.h" #include "trace.h" +#include "hw/i386/kvm/xen_evtchn.h" + /* MSI enable bit and maskall bit are in byte 1 in FLAGS register */ #define MSIX_CONTROL_OFFSET (PCI_MSIX_FLAGS + 1) #define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8) #define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8) -MSIMessage msix_get_message(PCIDevice *dev, unsigned vector) +static MSIMessage msix_prepare_message(PCIDevice *dev, unsigned vector) { uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE; MSIMessage msg; @@ -41,6 +43,11 @@ MSIMessage msix_get_message(PCIDevice *dev, unsigned vector) return msg; } +MSIMessage msix_get_message(PCIDevice *dev, unsigned vector) +{ + return dev->msix_prepare_message(dev, vector); +} + /* * Special API for POWER to configure the vectors through * a side channel. Should never be used by devices. @@ -119,6 +126,13 @@ static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked) { bool is_masked = msix_is_masked(dev, vector); + if (xen_mode == XEN_EMULATE) { + MSIMessage msg = msix_prepare_message(dev, vector); + + xen_evtchn_snoop_msi(dev, true, vector, msg.address, msg.data, + is_masked); + } + if (is_masked == was_masked) { return; } @@ -131,6 +145,26 @@ static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked) } } +void msix_set_mask(PCIDevice *dev, int vector, bool mask) +{ + unsigned offset; + bool was_masked; + + assert(vector < dev->msix_entries_nr); + + offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; + + was_masked = msix_is_masked(dev, vector); + + if (mask) { + dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT; + } else { + dev->msix_table[offset] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT; + } + + msix_handle_mask_update(dev, vector, was_masked); +} + static bool msix_masked(PCIDevice *dev) { return dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK; @@ -344,6 +378,8 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries, "msix-pba", pba_size); memory_region_add_subregion(pba_bar, pba_offset, &dev->msix_pba_mmio); + dev->msix_prepare_message = msix_prepare_message; + return 0; } @@ -429,6 +465,7 @@ void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar) g_free(dev->msix_entry_used); dev->msix_entry_used = NULL; dev->cap_present &= ~QEMU_PCI_CAP_MSIX; + dev->msix_prepare_message = NULL; } void msix_uninit_exclusive_bar(PCIDevice *dev) @@ -489,7 +526,9 @@ void msix_notify(PCIDevice *dev, unsigned vector) { MSIMessage msg; - if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) { + assert(vector < dev->msix_entries_nr); + + if (!dev->msix_entry_used[vector]) { return; } @@ -525,20 +564,17 @@ void msix_reset(PCIDevice *dev) * don't want to follow the spec suggestion can declare all vectors as used. */ /* Mark vector as used. */ -int msix_vector_use(PCIDevice *dev, unsigned vector) +void msix_vector_use(PCIDevice *dev, unsigned vector) { - if (vector >= dev->msix_entries_nr) { - return -EINVAL; - } - + assert(vector < dev->msix_entries_nr); dev->msix_entry_used[vector]++; - return 0; } /* Mark vector as unused. */ void msix_vector_unuse(PCIDevice *dev, unsigned vector) { - if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) { + assert(vector < dev->msix_entries_nr); + if (!dev->msix_entry_used[vector]) { return; } if (--dev->msix_entry_used[vector]) { @@ -612,6 +648,7 @@ undo: } dev->msix_vector_use_notifier = NULL; dev->msix_vector_release_notifier = NULL; + dev->msix_vector_poll_notifier = NULL; return ret; } @@ -648,7 +685,7 @@ static int get_msix_state(QEMUFile *f, void *pv, size_t size, return 0; } -static VMStateInfo vmstate_info_msix = { +static const VMStateInfo vmstate_info_msix = { .name = "msix state", .get = get_msix_state, .put = put_msix_state, @@ -656,7 +693,7 @@ static VMStateInfo vmstate_info_msix = { const VMStateDescription vmstate_msix = { .name = "msix", - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { { .name = "msix", .version_id = 0, diff --git a/hw/pci/pci-hmp-cmds.c b/hw/pci/pci-hmp-cmds.c new file mode 100644 index 0000000000..b09fce9377 --- /dev/null +++ b/hw/pci/pci-hmp-cmds.c @@ -0,0 +1,239 @@ +/* + * HMP commands related to PCI + * + * 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. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "pci-internal.h" +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qapi-commands-pci.h" +#include "qemu/cutils.h" + +static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev) +{ + PciMemoryRegionList *region; + + monitor_printf(mon, " Bus %2" PRId64 ", ", dev->bus); + monitor_printf(mon, "device %3" PRId64 ", function %" PRId64 ":\n", + dev->slot, dev->function); + monitor_printf(mon, " "); + + if (dev->class_info->desc) { + monitor_puts(mon, dev->class_info->desc); + } else { + monitor_printf(mon, "Class %04" PRId64, dev->class_info->q_class); + } + + monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n", + dev->id->vendor, dev->id->device); + if (dev->id->has_subsystem_vendor && dev->id->has_subsystem) { + monitor_printf(mon, " PCI subsystem %04" PRIx64 ":%04" PRIx64 "\n", + dev->id->subsystem_vendor, dev->id->subsystem); + } + + if (dev->has_irq) { + monitor_printf(mon, " IRQ %" PRId64 ", pin %c\n", + dev->irq, (char)('A' + dev->irq_pin - 1)); + } + + if (dev->pci_bridge) { + monitor_printf(mon, " BUS %" PRId64 ".\n", + dev->pci_bridge->bus->number); + monitor_printf(mon, " secondary bus %" PRId64 ".\n", + dev->pci_bridge->bus->secondary); + monitor_printf(mon, " subordinate bus %" PRId64 ".\n", + dev->pci_bridge->bus->subordinate); + + monitor_printf(mon, " IO range [0x%04"PRIx64", 0x%04"PRIx64"]\n", + dev->pci_bridge->bus->io_range->base, + dev->pci_bridge->bus->io_range->limit); + + monitor_printf(mon, + " memory range [0x%08"PRIx64", 0x%08"PRIx64"]\n", + dev->pci_bridge->bus->memory_range->base, + dev->pci_bridge->bus->memory_range->limit); + + monitor_printf(mon, " prefetchable memory range " + "[0x%08"PRIx64", 0x%08"PRIx64"]\n", + dev->pci_bridge->bus->prefetchable_range->base, + dev->pci_bridge->bus->prefetchable_range->limit); + } + + for (region = dev->regions; region; region = region->next) { + uint64_t addr, size; + + addr = region->value->address; + size = region->value->size; + + monitor_printf(mon, " BAR%" PRId64 ": ", region->value->bar); + + if (!strcmp(region->value->type, "io")) { + monitor_printf(mon, "I/O at 0x%04" PRIx64 + " [0x%04" PRIx64 "].\n", + addr, addr + size - 1); + } else { + monitor_printf(mon, "%d bit%s memory at 0x%08" PRIx64 + " [0x%08" PRIx64 "].\n", + region->value->mem_type_64 ? 64 : 32, + region->value->prefetch ? " prefetchable" : "", + addr, addr + size - 1); + } + } + + monitor_printf(mon, " id \"%s\"\n", dev->qdev_id); + + if (dev->pci_bridge) { + if (dev->pci_bridge->has_devices) { + PciDeviceInfoList *cdev; + for (cdev = dev->pci_bridge->devices; cdev; cdev = cdev->next) { + hmp_info_pci_device(mon, cdev->value); + } + } + } +} + +void hmp_info_pci(Monitor *mon, const QDict *qdict) +{ + PciInfoList *info_list, *info; + + info_list = qmp_query_pci(&error_abort); + + for (info = info_list; info; info = info->next) { + PciDeviceInfoList *dev; + + for (dev = info->value->devices; dev; dev = dev->next) { + hmp_info_pci_device(mon, dev->value); + } + } + + qapi_free_PciInfoList(info_list); +} + +void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent) +{ + PCIDevice *d = (PCIDevice *)dev; + int class = pci_get_word(d->config + PCI_CLASS_DEVICE); + const pci_class_desc *desc = get_class_desc(class); + char ctxt[64]; + PCIIORegion *r; + int i; + + if (desc->desc) { + snprintf(ctxt, sizeof(ctxt), "%s", desc->desc); + } else { + snprintf(ctxt, sizeof(ctxt), "Class %04x", class); + } + + monitor_printf(mon, "%*sclass %s, addr %02x:%02x.%x, " + "pci id %04x:%04x (sub %04x:%04x)\n", + indent, "", ctxt, pci_dev_bus_num(d), + PCI_SLOT(d->devfn), PCI_FUNC(d->devfn), + pci_get_word(d->config + PCI_VENDOR_ID), + pci_get_word(d->config + PCI_DEVICE_ID), + pci_get_word(d->config + PCI_SUBSYSTEM_VENDOR_ID), + pci_get_word(d->config + PCI_SUBSYSTEM_ID)); + for (i = 0; i < PCI_NUM_REGIONS; i++) { + r = &d->io_regions[i]; + if (!r->size) { + continue; + } + monitor_printf(mon, "%*sbar %d: %s at 0x%"FMT_PCIBUS + " [0x%"FMT_PCIBUS"]\n", + indent, "", + i, r->type & PCI_BASE_ADDRESS_SPACE_IO ? "i/o" : "mem", + r->addr, r->addr + r->size - 1); + } +} + +void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *id = qdict_get_str(qdict, "id"); + const char *error_name; + uint32_t error_status; + unsigned int num; + bool correctable; + PCIDevice *dev; + PCIEAERErr aer_err; + int ret; + + ret = pci_qdev_find_device(id, &dev); + if (ret == -ENODEV) { + error_setg(&err, "device '%s' not found", id); + goto out; + } + if (ret < 0 || !pci_is_express(dev)) { + error_setg(&err, "device '%s' is not a PCIe device", id); + goto out; + } + + error_name = qdict_get_str(qdict, "error_status"); + if (pcie_aer_parse_error_string(error_name, &error_status, &correctable)) { + if (qemu_strtoui(error_name, NULL, 0, &num) < 0) { + error_setg(&err, "invalid error status value '%s'", error_name); + goto out; + } + error_status = num; + correctable = qdict_get_try_bool(qdict, "correctable", false); + } else { + if (qdict_haskey(qdict, "correctable")) { + error_setg(&err, "-c is only valid with numeric error status"); + goto out; + } + } + aer_err.status = error_status; + aer_err.source_id = pci_requester_id(dev); + + aer_err.flags = 0; + if (correctable) { + aer_err.flags |= PCIE_AER_ERR_IS_CORRECTABLE; + } + if (qdict_get_try_bool(qdict, "advisory_non_fatal", false)) { + aer_err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY; + } + if (qdict_haskey(qdict, "header0")) { + aer_err.flags |= PCIE_AER_ERR_HEADER_VALID; + } + if (qdict_haskey(qdict, "prefix0")) { + aer_err.flags |= PCIE_AER_ERR_TLP_PREFIX_PRESENT; + } + + aer_err.header[0] = qdict_get_try_int(qdict, "header0", 0); + aer_err.header[1] = qdict_get_try_int(qdict, "header1", 0); + aer_err.header[2] = qdict_get_try_int(qdict, "header2", 0); + aer_err.header[3] = qdict_get_try_int(qdict, "header3", 0); + + aer_err.prefix[0] = qdict_get_try_int(qdict, "prefix0", 0); + aer_err.prefix[1] = qdict_get_try_int(qdict, "prefix1", 0); + aer_err.prefix[2] = qdict_get_try_int(qdict, "prefix2", 0); + aer_err.prefix[3] = qdict_get_try_int(qdict, "prefix3", 0); + + ret = pcie_aer_inject_error(dev, &aer_err); + if (ret < 0) { + error_setg_errno(&err, -ret, "failed to inject error"); + goto out; + } + + + monitor_printf(mon, "OK id: %s root bus: %s, bus: %x devfn: %x.%x\n", + id, pci_root_bus_path(dev), pci_dev_bus_num(dev), + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + +out: + hmp_handle_error(mon, err); +} diff --git a/hw/pci/pci-internal.h b/hw/pci/pci-internal.h new file mode 100644 index 0000000000..a7d6d8a732 --- /dev/null +++ b/hw/pci/pci-internal.h @@ -0,0 +1,24 @@ +#ifndef HW_PCI_PCI_INTERNAL_H +#define HW_PCI_PCI_INTERNAL_H + +#include "qemu/queue.h" + +typedef struct { + uint16_t class; + const char *desc; + const char *fw_name; + uint16_t fw_ign_bits; +} pci_class_desc; + +typedef QLIST_HEAD(, PCIHostState) PCIHostStateList; + +extern PCIHostStateList pci_host_bridges; + +const pci_class_desc *get_class_desc(int class); +PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num); +void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent); + +int pcie_aer_parse_error_string(const char *error_name, + uint32_t *status, bool *correctable); + +#endif diff --git a/hw/pci/pci-qmp-cmds.c b/hw/pci/pci-qmp-cmds.c new file mode 100644 index 0000000000..5d9f4817f5 --- /dev/null +++ b/hw/pci/pci-qmp-cmds.c @@ -0,0 +1,199 @@ +/* + * QMP commands related to PCI + * + * 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/pci/pci.h" +#include "hw/pci/pci_bridge.h" +#include "pci-internal.h" +#include "qapi/qapi-commands-pci.h" + +static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num); + +static PciMemoryRegionList *qmp_query_pci_regions(const PCIDevice *dev) +{ + PciMemoryRegionList *head = NULL, **tail = &head; + int i; + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + const PCIIORegion *r = &dev->io_regions[i]; + PciMemoryRegion *region; + + if (!r->size) { + continue; + } + + region = g_malloc0(sizeof(*region)); + + if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { + region->type = g_strdup("io"); + } else { + region->type = g_strdup("memory"); + region->has_prefetch = true; + region->prefetch = !!(r->type & PCI_BASE_ADDRESS_MEM_PREFETCH); + region->has_mem_type_64 = true; + region->mem_type_64 = !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64); + } + + region->bar = i; + region->address = r->addr; + region->size = r->size; + + QAPI_LIST_APPEND(tail, region); + } + + return head; +} + +static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus, + int bus_num) +{ + PciBridgeInfo *info; + PciMemoryRange *range; + + info = g_new0(PciBridgeInfo, 1); + + info->bus = g_new0(PciBusInfo, 1); + info->bus->number = dev->config[PCI_PRIMARY_BUS]; + info->bus->secondary = dev->config[PCI_SECONDARY_BUS]; + info->bus->subordinate = dev->config[PCI_SUBORDINATE_BUS]; + + range = info->bus->io_range = g_new0(PciMemoryRange, 1); + range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO); + range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO); + + range = info->bus->memory_range = g_new0(PciMemoryRange, 1); + range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); + range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); + + range = info->bus->prefetchable_range = g_new0(PciMemoryRange, 1); + range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + + if (dev->config[PCI_SECONDARY_BUS] != 0) { + PCIBus *child_bus = pci_find_bus_nr(bus, + dev->config[PCI_SECONDARY_BUS]); + if (child_bus) { + info->has_devices = true; + info->devices = qmp_query_pci_devices(child_bus, + dev->config[PCI_SECONDARY_BUS]); + } + } + + return info; +} + +static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus, + int bus_num) +{ + const pci_class_desc *desc; + PciDeviceInfo *info; + uint8_t type; + int class; + + info = g_new0(PciDeviceInfo, 1); + info->bus = bus_num; + info->slot = PCI_SLOT(dev->devfn); + info->function = PCI_FUNC(dev->devfn); + + info->class_info = g_new0(PciDeviceClass, 1); + class = pci_get_word(dev->config + PCI_CLASS_DEVICE); + info->class_info->q_class = class; + desc = get_class_desc(class); + if (desc->desc) { + info->class_info->desc = g_strdup(desc->desc); + } + + info->id = g_new0(PciDeviceId, 1); + info->id->vendor = pci_get_word(dev->config + PCI_VENDOR_ID); + info->id->device = pci_get_word(dev->config + PCI_DEVICE_ID); + info->regions = qmp_query_pci_regions(dev); + info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : ""); + + info->irq_pin = dev->config[PCI_INTERRUPT_PIN]; + if (dev->config[PCI_INTERRUPT_PIN] != 0) { + info->has_irq = true; + info->irq = dev->config[PCI_INTERRUPT_LINE]; + } + + type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; + if (type == PCI_HEADER_TYPE_BRIDGE) { + info->pci_bridge = qmp_query_pci_bridge(dev, bus, bus_num); + } else if (type == PCI_HEADER_TYPE_NORMAL) { + info->id->has_subsystem = info->id->has_subsystem_vendor = true; + info->id->subsystem = pci_get_word(dev->config + PCI_SUBSYSTEM_ID); + info->id->subsystem_vendor = + pci_get_word(dev->config + PCI_SUBSYSTEM_VENDOR_ID); + } else if (type == PCI_HEADER_TYPE_CARDBUS) { + info->id->has_subsystem = info->id->has_subsystem_vendor = true; + info->id->subsystem = pci_get_word(dev->config + PCI_CB_SUBSYSTEM_ID); + info->id->subsystem_vendor = + pci_get_word(dev->config + PCI_CB_SUBSYSTEM_VENDOR_ID); + } + + return info; +} + +static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num) +{ + PciDeviceInfoList *head = NULL, **tail = &head; + PCIDevice *dev; + int devfn; + + for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + dev = bus->devices[devfn]; + if (dev) { + QAPI_LIST_APPEND(tail, qmp_query_pci_device(dev, bus, bus_num)); + } + } + + return head; +} + +static PciInfo *qmp_query_pci_bus(PCIBus *bus, int bus_num) +{ + PciInfo *info = NULL; + + bus = pci_find_bus_nr(bus, bus_num); + if (bus) { + info = g_malloc0(sizeof(*info)); + info->bus = bus_num; + info->devices = qmp_query_pci_devices(bus, bus_num); + } + + return info; +} + +PciInfoList *qmp_query_pci(Error **errp) +{ + PciInfoList *head = NULL, **tail = &head; + PCIHostState *host_bridge; + + QLIST_FOREACH(host_bridge, &pci_host_bridges, next) { + QAPI_LIST_APPEND(tail, + qmp_query_pci_bus(host_bridge->bus, + pci_bus_num(host_bridge->bus))); + } + + return head; +} diff --git a/hw/pci/pci-stub.c b/hw/pci/pci-stub.c index 3a027c42e4..f0508682d2 100644 --- a/hw/pci/pci-stub.c +++ b/hw/pci/pci-stub.c @@ -19,11 +19,9 @@ */ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" #include "monitor/monitor.h" -#include "qapi/error.h" +#include "monitor/hmp.h" #include "qapi/qapi-commands-pci.h" -#include "qapi/qmp/qerror.h" #include "hw/pci/pci.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" @@ -33,10 +31,13 @@ bool pci_available; PciInfoList *qmp_query_pci(Error **errp) { - error_setg(errp, QERR_UNSUPPORTED); return NULL; } +void hmp_info_pci(Monitor *mon, const QDict *qdict) +{ +} + void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "PCI devices not supported\n"); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index a9b37f8000..e7a39cb203 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -34,9 +34,9 @@ #include "hw/qdev-properties-system.h" #include "migration/qemu-file-types.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "net/net.h" #include "sysemu/numa.h" +#include "sysemu/runstate.h" #include "sysemu/sysemu.h" #include "hw/loader.h" #include "qemu/error-report.h" @@ -47,8 +47,11 @@ #include "hw/hotplug.h" #include "hw/boards.h" #include "qapi/error.h" -#include "qapi/qapi-commands-pci.h" #include "qemu/cutils.h" +#include "pci-internal.h" + +#include "hw/xen/xen.h" +#include "hw/i386/kvm/xen_evtchn.h" //#define DEBUG_PCI #ifdef DEBUG_PCI @@ -59,10 +62,10 @@ bool pci_available = true; -static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent); static char *pcibus_get_dev_path(DeviceState *dev); static char *pcibus_get_fw_dev_path(DeviceState *dev); -static void pcibus_reset(BusState *qbus); +static void pcibus_reset_hold(Object *obj); +static bool pcie_has_upstream_port(PCIDevice *dev); static Property pci_props[] = { DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1), @@ -78,6 +81,10 @@ static Property pci_props[] = { DEFINE_PROP_STRING("failover_pair_id", PCIDevice, failover_pair_id), DEFINE_PROP_UINT32("acpi-index", PCIDevice, acpi_index, 0), + DEFINE_PROP_BIT("x-pcie-err-unc-mask", PCIDevice, cap_present, + QEMU_PCIE_ERR_UNC_MASK_BITNR, true), + DEFINE_PROP_BIT("x-pcie-ari-nextfn-1", PCIDevice, cap_present, + QEMU_PCIE_ARI_NEXTFN_1_BITNR, false), DEFINE_PROP_END_OF_LIST() }; @@ -85,7 +92,7 @@ static const VMStateDescription vmstate_pcibus = { .name = "PCIBUS", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32_EQUAL(nirq, PCIBus, NULL), VMSTATE_VARRAY_INT32(irq_count, PCIBus, nirq, 0, vmstate_info_int32, @@ -94,6 +101,21 @@ static const VMStateDescription vmstate_pcibus = { } }; +static gint g_cmp_uint32(gconstpointer a, gconstpointer b, gpointer user_data) +{ + return a - b; +} + +static GSequence *pci_acpi_index_list(void) +{ + static GSequence *used_acpi_index_list; + + if (!used_acpi_index_list) { + used_acpi_index_list = g_sequence_new(NULL); + } + return used_acpi_index_list; +} + static void pci_init_bus_master(PCIDevice *pci_dev) { AddressSpace *dma_as = pci_device_iommu_address_space(pci_dev); @@ -125,7 +147,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, VMSTATE_INSTANCE_ID_ANY, &vmstate_pcibus, bus); + vmstate_register_any(NULL, &vmstate_pcibus, bus); } static void pcie_bus_realize(BusState *qbus, Error **errp) @@ -180,13 +202,15 @@ static void pci_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); PCIBusClass *pbc = PCI_BUS_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); k->print_dev = pcibus_dev_print; k->get_dev_path = pcibus_get_dev_path; k->get_fw_dev_path = pcibus_get_fw_dev_path; k->realize = pci_bus_realize; k->unrealize = pci_bus_unrealize; - k->reset = pcibus_reset; + + rc->phases.hold = pcibus_reset_hold; pbc->bus_num = pcibus_num; pbc->numa_node = pcibus_numa_node; @@ -234,7 +258,6 @@ static const TypeInfo cxl_bus_info = { .class_init = pcie_bus_class_init, }; -static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num); static void pci_update_mappings(PCIDevice *d); static void pci_irq_handler(void *opaque, int irq_num, int level); static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, Error **); @@ -243,7 +266,7 @@ static void pci_del_option_rom(PCIDevice *pdev); static uint16_t pci_default_sub_vendor_id = PCI_SUBVENDOR_ID_REDHAT_QUMRANET; static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU; -static QLIST_HEAD(, PCIHostState) pci_host_bridges; +PCIHostStateList pci_host_bridges; int pci_bar(PCIDevice *d, int reg) { @@ -282,8 +305,13 @@ static void pci_change_irq_level(PCIDevice *pci_dev, int irq_num, int change) { PCIBus *bus; for (;;) { + int dev_irq = irq_num; bus = pci_get_bus(pci_dev); + assert(bus->map_irq); irq_num = bus->map_irq(pci_dev, irq_num); + trace_pci_route_irq(dev_irq, DEVICE(pci_dev)->canonical_path, irq_num, + pci_bus_is_root(bus) ? "root-complex" + : DEVICE(bus->parent_dev)->canonical_path); if (bus->set_irq) break; pci_dev = bus->parent_dev; @@ -317,6 +345,26 @@ void pci_device_deassert_intx(PCIDevice *dev) } } +static void pci_msi_trigger(PCIDevice *dev, MSIMessage msg) +{ + MemTxAttrs attrs = {}; + + /* + * Xen uses the high bits of the address to contain some of the bits + * of the PIRQ#. Therefore we can't just send the write cycle and + * trust that it's caught by the APIC at 0xfee00000 because the + * target of the write might be e.g. 0x0x1000fee46000 for PIRQ#4166. + * So we intercept the delivery here instead of in kvm_send_msi(). + */ + if (xen_mode == XEN_EMULATE && + xen_evtchn_deliver_pirq_msi(msg.address, msg.data)) { + return; + } + attrs.requester_id = pci_requester_id(dev); + address_space_stl_le(&dev->bus_master_as, msg.address, msg.data, + attrs, NULL); +} + static void pci_reset_regions(PCIDevice *dev) { int r; @@ -361,6 +409,7 @@ static void pci_do_device_reset(PCIDevice *dev) msi_reset(dev); msix_reset(dev); + pcie_sriov_pf_reset(dev); } /* @@ -369,18 +418,18 @@ static void pci_do_device_reset(PCIDevice *dev) */ void pci_device_reset(PCIDevice *dev) { - qdev_reset_all(&dev->qdev); + device_cold_reset(&dev->qdev); pci_do_device_reset(dev); } /* * Trigger pci bus reset under a given bus. - * Called via qbus_reset_all on RST# assert, after the devices - * have been reset qdev_reset_all-ed already. + * Called via bus_cold_reset on RST# assert, after the devices + * have been reset device_cold_reset-ed already. */ -static void pcibus_reset(BusState *qbus) +static void pcibus_reset_hold(Object *obj) { - PCIBus *bus = DO_UPCAST(PCIBus, qbus, qbus); + PCIBus *bus = PCI_BUS(obj); int i; for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { @@ -454,15 +503,14 @@ bool pci_bus_bypass_iommu(PCIBus *bus) } static void pci_root_bus_internal_init(PCIBus *bus, DeviceState *parent, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, + MemoryRegion *mem, MemoryRegion *io, uint8_t devfn_min) { assert(PCI_FUNC(devfn_min) == 0); bus->devfn_min = devfn_min; bus->slot_reserved_mask = 0x0; - bus->address_space_mem = address_space_mem; - bus->address_space_io = address_space_io; + bus->address_space_mem = mem; + bus->address_space_io = io; bus->flags |= PCI_BUS_IS_ROOT; /* host bridge */ @@ -476,32 +524,28 @@ static void pci_bus_uninit(PCIBus *bus) pci_host_bus_unregister(BUS(bus)->parent); } -bool pci_bus_is_express(PCIBus *bus) +bool pci_bus_is_express(const PCIBus *bus) { return object_dynamic_cast(OBJECT(bus), TYPE_PCIE_BUS); } void pci_root_bus_init(PCIBus *bus, size_t bus_size, DeviceState *parent, const char *name, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, + MemoryRegion *mem, MemoryRegion *io, uint8_t devfn_min, const char *typename) { qbus_init(bus, bus_size, typename, parent, name); - pci_root_bus_internal_init(bus, parent, address_space_mem, - address_space_io, devfn_min); + pci_root_bus_internal_init(bus, parent, mem, io, devfn_min); } PCIBus *pci_root_bus_new(DeviceState *parent, const char *name, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, + MemoryRegion *mem, MemoryRegion *io, uint8_t devfn_min, const char *typename) { PCIBus *bus; bus = PCI_BUS(qbus_new(typename, parent, name)); - pci_root_bus_internal_init(bus, parent, address_space_mem, - address_space_io, devfn_min); + pci_root_bus_internal_init(bus, parent, mem, io, devfn_min); return bus; } @@ -512,16 +556,21 @@ void pci_root_bus_cleanup(PCIBus *bus) qbus_unrealize(BUS(bus)); } -void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, +void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, void *irq_opaque, int nirq) { bus->set_irq = set_irq; - bus->map_irq = map_irq; bus->irq_opaque = irq_opaque; bus->nirq = nirq; + g_free(bus->irq_count); bus->irq_count = g_malloc0(nirq * sizeof(bus->irq_count[0])); } +void pci_bus_map_irqs(PCIBus *bus, pci_map_irq_fn map_irq) +{ + bus->map_irq = map_irq; +} + void pci_bus_irqs_cleanup(PCIBus *bus) { bus->set_irq = NULL; @@ -529,21 +578,21 @@ void pci_bus_irqs_cleanup(PCIBus *bus) bus->irq_opaque = NULL; bus->nirq = 0; g_free(bus->irq_count); + bus->irq_count = NULL; } PCIBus *pci_register_root_bus(DeviceState *parent, const char *name, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, + MemoryRegion *mem, MemoryRegion *io, uint8_t devfn_min, int nirq, const char *typename) { PCIBus *bus; - bus = pci_root_bus_new(parent, name, address_space_mem, - address_space_io, devfn_min, typename); - pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq); + bus = pci_root_bus_new(parent, name, mem, io, devfn_min, typename); + pci_bus_irqs(bus, set_irq, irq_opaque, nirq); + pci_bus_map_irqs(bus, map_irq); return bus; } @@ -567,7 +616,7 @@ void pci_bus_range(PCIBus *bus, int *min_bus, int *max_bus) for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { PCIDevice *dev = bus->devices[i]; - if (dev && PCI_DEVICE_GET_CLASS(dev)->is_bridge) { + if (dev && IS_PCI_BRIDGE(dev)) { *min_bus = MIN(*min_bus, dev->config[PCI_SECONDARY_BUS]); *max_bus = MAX(*max_bus, dev->config[PCI_SUBORDINATE_BUS]); } @@ -583,7 +632,6 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size, const VMStateField *field) { PCIDevice *s = container_of(pv, PCIDevice, config); - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(s); uint8_t *config; int i; @@ -605,9 +653,8 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size, memcpy(s->config, config, size); pci_update_mappings(s); - if (pc->is_bridge) { - PCIBridge *b = PCI_BRIDGE(s); - pci_bridge_update_mappings(b); + if (IS_PCI_BRIDGE(s)) { + pci_bridge_update_mappings(PCI_BRIDGE(s)); } memory_region_set_enabled(&s->bus_master_enable_region, @@ -629,7 +676,7 @@ static int put_pci_config_device(QEMUFile *f, void *pv, size_t size, return 0; } -static VMStateInfo vmstate_info_pci_config = { +static const VMStateInfo vmstate_info_pci_config = { .name = "pci config", .get = get_pci_config_device, .put = put_pci_config_device, @@ -670,7 +717,7 @@ static int put_pci_irq_state(QEMUFile *f, void *pv, size_t size, return 0; } -static VMStateInfo vmstate_info_pci_irq_state = { +static const VMStateInfo vmstate_info_pci_irq_state = { .name = "pci irq state", .get = get_pci_irq_state, .put = put_pci_irq_state, @@ -690,7 +737,7 @@ const VMStateDescription vmstate_pci_device = { .name = "PCIDevice", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32_POSITIVE_LE(version_id, PCIDevice), VMSTATE_BUFFER_UNSAFE_INFO_TEST(config, PCIDevice, migrate_is_not_pcie, @@ -842,7 +889,7 @@ static void pci_init_w1cmask(PCIDevice *dev) static void pci_init_mask_bridge(PCIDevice *d) { /* PCI_PRIMARY_BUS, PCI_SECONDARY_BUS, PCI_SUBORDINATE_BUS and - PCI_SEC_LETENCY_TIMER */ + PCI_SEC_LATENCY_TIMER */ memset(d->wmask + PCI_PRIMARY_BUS, 0xff, 4); /* base and limit */ @@ -978,6 +1025,9 @@ static void do_pci_unregister_device(PCIDevice *pci_dev) pci_get_bus(pci_dev)->devices[pci_dev->devfn] = NULL; pci_config_free(pci_dev); + if (xen_mode == XEN_EMULATE) { + xen_evtchn_remove_pci_device(pci_dev); + } if (memory_region_is_mapped(&pci_dev->bus_master_enable_region)) { memory_region_del_subregion(&pci_dev->bus_master_container_region, &pci_dev->bus_master_enable_region); @@ -1070,6 +1120,21 @@ static bool pci_bus_devfn_reserved(PCIBus *bus, int devfn) return bus->slot_reserved_mask & (1UL << PCI_SLOT(devfn)); } +uint32_t pci_bus_get_slot_reserved_mask(PCIBus *bus) +{ + return bus->slot_reserved_mask; +} + +void pci_bus_set_slot_reserved_mask(PCIBus *bus, uint32_t mask) +{ + bus->slot_reserved_mask |= mask; +} + +void pci_bus_clear_slot_reserved_mask(PCIBus *bus, uint32_t mask) +{ + bus->slot_reserved_mask &= ~mask; +} + /* -1 for devfn means auto assign */ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, const char *name, int devfn, @@ -1081,9 +1146,10 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, Error *local_err = NULL; DeviceState *dev = DEVICE(pci_dev); PCIBus *bus = pci_get_bus(pci_dev); + bool is_bridge = IS_PCI_BRIDGE(pci_dev); /* Only pci bridges can be attached to extra PCI root buses */ - if (pci_bus_is_root(bus) && bus->parent_dev && !pc->is_bridge) { + if (pci_bus_is_root(bus) && bus->parent_dev && !is_bridge) { error_setg(errp, "PCI: Only PCI/PCIe bridges can be plugged into %s", bus->parent_dev->name); @@ -1113,9 +1179,14 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCI_SLOT(devfn), PCI_FUNC(devfn), name, bus->devices[devfn]->name, bus->devices[devfn]->qdev.id); return NULL; - } else if (dev->hotplugged && - !pci_is_vf(pci_dev) && - pci_get_function_0(pci_dev)) { + } /* + * Populating function 0 triggers a scan from the guest that + * exposes other non-zero functions. Hence we need to ensure that + * function 0 wasn't added yet. + */ + else if (dev->hotplugged && + !pci_is_vf(pci_dev) && + pci_get_function_0(pci_dev)) { error_setg(errp, "PCI: slot %d function 0 already occupied by %s," " new func %s cannot be exposed to guest.", PCI_SLOT(pci_get_function_0(pci_dev)->devfn), @@ -1145,7 +1216,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, pci_config_set_revision(pci_dev->config, pc->revision); pci_config_set_class(pci_dev->config, pc->class_id); - if (!pc->is_bridge) { + if (!is_bridge) { if (pc->subsystem_vendor_id || pc->subsystem_id) { pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, pc->subsystem_vendor_id); @@ -1162,7 +1233,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, pci_init_cmask(pci_dev); pci_init_wmask(pci_dev); pci_init_w1cmask(pci_dev); - if (pc->is_bridge) { + if (is_bridge) { pci_init_mask_bridge(pci_dev); } pci_init_multifunction(bus, pci_dev, &local_err); @@ -1212,6 +1283,19 @@ static void pci_qdev_unrealize(DeviceState *dev) pci_device_deassert_intx(pci_dev); do_pci_unregister_device(pci_dev); + + pci_dev->msi_trigger = NULL; + + /* + * clean up acpi-index so it could reused by another device + */ + if (pci_dev->acpi_index) { + GSequence *used_indexes = pci_acpi_index_list(); + + g_sequence_remove(g_sequence_lookup(used_indexes, + GINT_TO_POINTER(pci_dev->acpi_index), + g_cmp_uint32, NULL)); + } } void pci_register_bar(PCIDevice *pci_dev, int region_num, @@ -1367,9 +1451,7 @@ pcibus_t pci_bar_address(PCIDevice *d, { pcibus_t new_addr, last_addr; uint16_t cmd = pci_get_word(d->config + PCI_COMMAND); - Object *machine = qdev_get_machine(); - ObjectClass *oc = object_get_class(machine); - MachineClass *mc = MACHINE_CLASS(oc); + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); bool allow_0_address = mc->pci_allow_0_address; if (type & PCI_BASE_ADDRESS_SPACE_IO) { @@ -1527,7 +1609,7 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int range_covers_byte(addr, l, PCI_COMMAND)) pci_update_mappings(d); - if (range_covers_byte(addr, l, PCI_COMMAND)) { + if (ranges_overlap(addr, l, PCI_COMMAND, 2)) { pci_update_irq_disabled(d, was_irq_disabled); memory_region_set_enabled(&d->bus_master_enable_region, (pci_get_word(d->config + PCI_COMMAND) @@ -1587,8 +1669,12 @@ PCIINTxRoute pci_device_route_intx_to_irq(PCIDevice *dev, int pin) PCIBus *bus; do { + int dev_irq = pin; bus = pci_get_bus(dev); pin = bus->map_irq(dev, pin); + trace_pci_route_irq(dev_irq, DEVICE(dev)->canonical_path, pin, + pci_bus_is_root(bus) ? "root-complex" + : DEVICE(bus->parent_dev)->canonical_path); dev = bus->parent_dev; } while (dev); @@ -1635,7 +1721,7 @@ void pci_device_set_intx_routing_notifier(PCIDevice *dev, * 9.1: Interrupt routing. Table 9-1 * * the PCI Express Base Specification, Revision 2.1 - * 2.2.8.1: INTx interrutp signaling - Rules + * 2.2.8.1: INTx interrupt signaling - Rules * the Implementation Note * Table 2-20 */ @@ -1651,13 +1737,6 @@ int pci_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin) /***********************************************************/ /* monitor info on PCI */ -typedef struct { - uint16_t class; - const char *desc; - const char *fw_name; - uint16_t fw_ign_bits; -} pci_class_desc; - static const pci_class_desc pci_class_descriptions[] = { { 0x0001, "VGA controller", "display"}, @@ -1765,7 +1844,7 @@ void pci_for_each_device(PCIBus *bus, int bus_num, } } -static const pci_class_desc *get_class_desc(int class) +const pci_class_desc *get_class_desc(int class) { const pci_class_desc *desc; @@ -1777,273 +1856,49 @@ static const pci_class_desc *get_class_desc(int class) return desc; } -static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num); - -static PciMemoryRegionList *qmp_query_pci_regions(const PCIDevice *dev) +void pci_init_nic_devices(PCIBus *bus, const char *default_model) { - PciMemoryRegionList *head = NULL, **tail = &head; - int i; - - for (i = 0; i < PCI_NUM_REGIONS; i++) { - const PCIIORegion *r = &dev->io_regions[i]; - PciMemoryRegion *region; - - if (!r->size) { - continue; - } - - region = g_malloc0(sizeof(*region)); - - if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { - region->type = g_strdup("io"); - } else { - region->type = g_strdup("memory"); - region->has_prefetch = true; - region->prefetch = !!(r->type & PCI_BASE_ADDRESS_MEM_PREFETCH); - region->has_mem_type_64 = true; - region->mem_type_64 = !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64); - } - - region->bar = i; - region->address = r->addr; - region->size = r->size; - - QAPI_LIST_APPEND(tail, region); - } - - return head; + qemu_create_nic_bus_devices(&bus->qbus, TYPE_PCI_DEVICE, default_model, + "virtio", "virtio-net-pci"); } -static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus, - int bus_num) +bool pci_init_nic_in_slot(PCIBus *rootbus, const char *model, + const char *alias, const char *devaddr) { - PciBridgeInfo *info; - PciMemoryRange *range; - - info = g_new0(PciBridgeInfo, 1); - - info->bus = g_new0(PciBusInfo, 1); - info->bus->number = dev->config[PCI_PRIMARY_BUS]; - info->bus->secondary = dev->config[PCI_SECONDARY_BUS]; - info->bus->subordinate = dev->config[PCI_SUBORDINATE_BUS]; - - range = info->bus->io_range = g_new0(PciMemoryRange, 1); - range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO); - range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO); - - range = info->bus->memory_range = g_new0(PciMemoryRange, 1); - range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - - range = info->bus->prefetchable_range = g_new0(PciMemoryRange, 1); - range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - - if (dev->config[PCI_SECONDARY_BUS] != 0) { - PCIBus *child_bus = pci_find_bus_nr(bus, dev->config[PCI_SECONDARY_BUS]); - if (child_bus) { - info->has_devices = true; - info->devices = qmp_query_pci_devices(child_bus, dev->config[PCI_SECONDARY_BUS]); - } - } - - return info; -} - -static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus, - int bus_num) -{ - const pci_class_desc *desc; - PciDeviceInfo *info; - uint8_t type; - int class; - - info = g_new0(PciDeviceInfo, 1); - info->bus = bus_num; - info->slot = PCI_SLOT(dev->devfn); - info->function = PCI_FUNC(dev->devfn); - - info->class_info = g_new0(PciDeviceClass, 1); - class = pci_get_word(dev->config + PCI_CLASS_DEVICE); - info->class_info->q_class = class; - desc = get_class_desc(class); - if (desc->desc) { - info->class_info->has_desc = true; - info->class_info->desc = g_strdup(desc->desc); - } - - info->id = g_new0(PciDeviceId, 1); - info->id->vendor = pci_get_word(dev->config + PCI_VENDOR_ID); - info->id->device = pci_get_word(dev->config + PCI_DEVICE_ID); - info->regions = qmp_query_pci_regions(dev); - info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : ""); - - info->irq_pin = dev->config[PCI_INTERRUPT_PIN]; - if (dev->config[PCI_INTERRUPT_PIN] != 0) { - info->has_irq = true; - info->irq = dev->config[PCI_INTERRUPT_LINE]; - } - - type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; - if (type == PCI_HEADER_TYPE_BRIDGE) { - info->has_pci_bridge = true; - info->pci_bridge = qmp_query_pci_bridge(dev, bus, bus_num); - } else if (type == PCI_HEADER_TYPE_NORMAL) { - info->id->has_subsystem = info->id->has_subsystem_vendor = true; - info->id->subsystem = pci_get_word(dev->config + PCI_SUBSYSTEM_ID); - info->id->subsystem_vendor = - pci_get_word(dev->config + PCI_SUBSYSTEM_VENDOR_ID); - } else if (type == PCI_HEADER_TYPE_CARDBUS) { - info->id->has_subsystem = info->id->has_subsystem_vendor = true; - info->id->subsystem = pci_get_word(dev->config + PCI_CB_SUBSYSTEM_ID); - info->id->subsystem_vendor = - pci_get_word(dev->config + PCI_CB_SUBSYSTEM_VENDOR_ID); - } - - return info; -} - -static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num) -{ - PciDeviceInfoList *head = NULL, **tail = &head; - PCIDevice *dev; - int devfn; - - for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { - dev = bus->devices[devfn]; - if (dev) { - QAPI_LIST_APPEND(tail, qmp_query_pci_device(dev, bus, bus_num)); - } - } - - return head; -} - -static PciInfo *qmp_query_pci_bus(PCIBus *bus, int bus_num) -{ - PciInfo *info = NULL; - - bus = pci_find_bus_nr(bus, bus_num); - if (bus) { - info = g_malloc0(sizeof(*info)); - info->bus = bus_num; - info->devices = qmp_query_pci_devices(bus, bus_num); - } - - return info; -} - -PciInfoList *qmp_query_pci(Error **errp) -{ - PciInfoList *head = NULL, **tail = &head; - PCIHostState *host_bridge; - - QLIST_FOREACH(host_bridge, &pci_host_bridges, next) { - QAPI_LIST_APPEND(tail, - qmp_query_pci_bus(host_bridge->bus, - pci_bus_num(host_bridge->bus))); - } - - return head; -} - -/* Initialize a PCI NIC. */ -PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus, - const char *default_model, - const char *default_devaddr) -{ - const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr; - GSList *list; - GPtrArray *pci_nic_models; - PCIBus *bus; + NICInfo *nd = qemu_find_nic_info(model, true, alias); + int dom, busnr, devfn; PCIDevice *pci_dev; - DeviceState *dev; - int devfn; - int i; - int dom, busnr; unsigned slot; + PCIBus *bus; - if (nd->model && !strcmp(nd->model, "virtio")) { - g_free(nd->model); - nd->model = g_strdup("virtio-net-pci"); + if (!nd) { + return false; } - list = object_class_get_list_sorted(TYPE_PCI_DEVICE, false); - pci_nic_models = g_ptr_array_new(); - while (list) { - DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, list->data, - TYPE_DEVICE); - GSList *next; - if (test_bit(DEVICE_CATEGORY_NETWORK, dc->categories) && - dc->user_creatable) { - const char *name = object_class_get_name(list->data); - /* - * A network device might also be something else than a NIC, see - * e.g. the "rocker" device. Thus we have to look for the "netdev" - * property, too. Unfortunately, some devices like virtio-net only - * create this property during instance_init, so we have to create - * a temporary instance here to be able to check it. - */ - Object *obj = object_new_with_class(OBJECT_CLASS(dc)); - if (object_property_find(obj, "netdev")) { - g_ptr_array_add(pci_nic_models, (gpointer)name); - } - object_unref(obj); - } - next = list->next; - g_slist_free_1(list); - list = next; - } - g_ptr_array_add(pci_nic_models, NULL); - - if (qemu_show_nic_models(nd->model, (const char **)pci_nic_models->pdata)) { - exit(0); - } - - i = qemu_find_nic_model(nd, (const char **)pci_nic_models->pdata, - default_model); - if (i < 0) { + if (!devaddr || pci_parse_devaddr(devaddr, &dom, &busnr, &slot, NULL) < 0) { + error_report("Invalid PCI device address %s for device %s", + devaddr, model); exit(1); } - if (!rootbus) { - error_report("No primary PCI bus"); + if (dom != 0) { + error_report("No support for non-zero PCI domains"); exit(1); } - assert(!rootbus->parent_dev); - - if (!devaddr) { - devfn = -1; - busnr = 0; - } else { - if (pci_parse_devaddr(devaddr, &dom, &busnr, &slot, NULL) < 0) { - error_report("Invalid PCI device address %s for device %s", - devaddr, nd->model); - exit(1); - } - - if (dom != 0) { - error_report("No support for non-zero PCI domains"); - exit(1); - } - - devfn = PCI_DEVFN(slot, 0); - } + devfn = PCI_DEVFN(slot, 0); bus = pci_find_bus_nr(rootbus, busnr); if (!bus) { error_report("Invalid PCI device address %s for device %s", - devaddr, nd->model); + devaddr, model); exit(1); } - pci_dev = pci_new(devfn, nd->model); - dev = &pci_dev->qdev; - qdev_set_nic_properties(dev, nd); + pci_dev = pci_new(devfn, model); + qdev_set_nic_properties(&pci_dev->qdev, nd); pci_realize_and_unref(pci_dev, bus, &error_fatal); - g_ptr_array_free(pci_nic_models, true); - return pci_dev; + return true; } PCIDevice *pci_vga_init(PCIBus *bus) @@ -2085,7 +1940,7 @@ static bool pci_root_bus_in_range(PCIBus *bus, int bus_num) for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { PCIDevice *dev = bus->devices[i]; - if (dev && PCI_DEVICE_GET_CLASS(dev)->is_bridge) { + if (dev && IS_PCI_BRIDGE(dev)) { if (pci_secondary_bus_in_range(dev, bus_num)) { return true; } @@ -2095,7 +1950,7 @@ static bool pci_root_bus_in_range(PCIBus *bus, int bus_num) return false; } -static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num) +PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num) { PCIBus *sec; @@ -2171,6 +2026,8 @@ PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn) return bus->devices[devfn]; } +#define ONBOARD_INDEX_MAX (16 * 1024 - 1) + static void pci_qdev_realize(DeviceState *qdev, Error **errp) { PCIDevice *pci_dev = (PCIDevice *)qdev; @@ -2180,6 +2037,35 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp) bool is_default_rom; uint16_t class_id; + /* + * capped by systemd (see: udev-builtin-net_id.c) + * as it's the only known user honor it to avoid users + * misconfigure QEMU and then wonder why acpi-index doesn't work + */ + if (pci_dev->acpi_index > ONBOARD_INDEX_MAX) { + error_setg(errp, "acpi-index should be less or equal to %u", + ONBOARD_INDEX_MAX); + return; + } + + /* + * make sure that acpi-index is unique across all present PCI devices + */ + if (pci_dev->acpi_index) { + GSequence *used_indexes = pci_acpi_index_list(); + + if (g_sequence_lookup(used_indexes, + GINT_TO_POINTER(pci_dev->acpi_index), + g_cmp_uint32, NULL)) { + error_setg(errp, "a PCI device with acpi-index = %" PRIu32 + " already exist", pci_dev->acpi_index); + return; + } + g_sequence_insert_sorted(used_indexes, + GINT_TO_POINTER(pci_dev->acpi_index), + g_cmp_uint32, NULL); + } + if (pci_dev->romsize != -1 && !is_power_of_2(pci_dev->romsize)) { error_setg(errp, "ROM size %u is not a power of two", pci_dev->romsize); return; @@ -2212,6 +2098,25 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp) } } + /* + * A PCIe Downstream Port that do not have ARI Forwarding enabled must + * associate only Device 0 with the device attached to the bus + * representing the Link from the Port (PCIe base spec rev 4.0 ver 0.3, + * sec 7.3.1). + * With ARI, PCI_SLOT() can return non-zero value as the traditional + * 5-bit Device Number and 3-bit Function Number fields in its associated + * Routing IDs, Requester IDs and Completer IDs are interpreted as a + * single 8-bit Function Number. Hence, ignore ARI capable devices. + */ + if (pci_is_express(pci_dev) && + !pcie_find_capability(pci_dev, PCI_EXT_CAP_ID_ARI) && + pcie_has_upstream_port(pci_dev) && + PCI_SLOT(pci_dev->devfn)) { + warn_report("PCI: slot %d is not valid for %s," + " parent device only allows plugging into slot 0.", + PCI_SLOT(pci_dev->devfn), pci_dev->name); + } + if (pci_dev->failover_pair_id) { if (!pci_bus_is_express(pci_get_bus(pci_dev))) { error_setg(errp, "failover primary device must be on " @@ -2251,10 +2156,12 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp) } pci_set_power(pci_dev, true); + + pci_dev->msi_trigger = pci_msi_trigger; } -PCIDevice *pci_new_multifunction(int devfn, bool multifunction, - const char *name) +static PCIDevice *pci_new_internal(int devfn, bool multifunction, + const char *name) { DeviceState *dev; @@ -2264,9 +2171,14 @@ PCIDevice *pci_new_multifunction(int devfn, bool multifunction, return PCI_DEVICE(dev); } +PCIDevice *pci_new_multifunction(int devfn, const char *name) +{ + return pci_new_internal(devfn, true, name); +} + PCIDevice *pci_new(int devfn, const char *name) { - return pci_new_multifunction(devfn, false, name); + return pci_new_internal(devfn, false, name); } bool pci_realize_and_unref(PCIDevice *dev, PCIBus *bus, Error **errp) @@ -2275,17 +2187,18 @@ bool pci_realize_and_unref(PCIDevice *dev, PCIBus *bus, Error **errp) } PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, - bool multifunction, const char *name) { - PCIDevice *dev = pci_new_multifunction(devfn, multifunction, name); + PCIDevice *dev = pci_new_multifunction(devfn, name); pci_realize_and_unref(dev, bus, &error_fatal); return dev; } PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name) { - return pci_create_simple_multifunction(bus, devfn, false, name); + PCIDevice *dev = pci_new(devfn, name); + pci_realize_and_unref(dev, bus, &error_fatal); + return dev; } static uint8_t pci_find_space(PCIDevice *pdev, uint8_t size) @@ -2398,16 +2311,21 @@ static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, uint32_t size) static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, Error **errp) { - int64_t size; - char *path; - void *ptr; + int64_t size = 0; + g_autofree char *path = NULL; char name[32]; const VMStateDescription *vmsd; - if (!pdev->romfile) - return; - if (strlen(pdev->romfile) == 0) + /* + * In case of incoming migration ROM will come with migration stream, no + * reason to load the file. Neither we want to fail if local ROM file + * mismatches with specified romsize. + */ + bool load_file = !runstate_check(RUN_STATE_INMIGRATE); + + if (!pdev->romfile || !strlen(pdev->romfile)) { return; + } if (!pdev->rom_bar) { /* @@ -2434,57 +2352,57 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, return; } - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, pdev->romfile); - if (path == NULL) { - path = g_strdup(pdev->romfile); - } + if (load_file || pdev->romsize == -1) { + path = qemu_find_file(QEMU_FILE_TYPE_BIOS, pdev->romfile); + if (path == NULL) { + path = g_strdup(pdev->romfile); + } - size = get_image_size(path); - if (size < 0) { - error_setg(errp, "failed to find romfile \"%s\"", pdev->romfile); - g_free(path); - return; - } else if (size == 0) { - error_setg(errp, "romfile \"%s\" is empty", pdev->romfile); - g_free(path); - return; - } else if (size > 2 * GiB) { - error_setg(errp, "romfile \"%s\" too large (size cannot exceed 2 GiB)", - pdev->romfile); - g_free(path); - return; - } - if (pdev->romsize != -1) { - if (size > pdev->romsize) { - error_setg(errp, "romfile \"%s\" (%u bytes) is too large for ROM size %u", - pdev->romfile, (uint32_t)size, pdev->romsize); - g_free(path); + size = get_image_size(path); + if (size < 0) { + error_setg(errp, "failed to find romfile \"%s\"", pdev->romfile); + return; + } else if (size == 0) { + error_setg(errp, "romfile \"%s\" is empty", pdev->romfile); + return; + } else if (size > 2 * GiB) { + error_setg(errp, + "romfile \"%s\" too large (size cannot exceed 2 GiB)", + pdev->romfile); return; } - } else { - pdev->romsize = pow2ceil(size); + if (pdev->romsize != -1) { + if (size > pdev->romsize) { + error_setg(errp, "romfile \"%s\" (%u bytes) " + "is too large for ROM size %u", + pdev->romfile, (uint32_t)size, pdev->romsize); + return; + } + } else { + pdev->romsize = pow2ceil(size); + } } vmsd = qdev_get_vmsd(DEVICE(pdev)); + snprintf(name, sizeof(name), "%s.rom", + vmsd ? vmsd->name : object_get_typename(OBJECT(pdev))); - if (vmsd) { - snprintf(name, sizeof(name), "%s.rom", vmsd->name); - } else { - snprintf(name, sizeof(name), "%s.rom", object_get_typename(OBJECT(pdev))); - } pdev->has_rom = true; - memory_region_init_rom(&pdev->rom, OBJECT(pdev), name, pdev->romsize, &error_fatal); - ptr = memory_region_get_ram_ptr(&pdev->rom); - if (load_image_size(path, ptr, size) < 0) { - error_setg(errp, "failed to load romfile \"%s\"", pdev->romfile); - g_free(path); - return; - } - g_free(path); + memory_region_init_rom(&pdev->rom, OBJECT(pdev), name, pdev->romsize, + &error_fatal); - if (is_default_rom) { - /* Only the default rom images will be patched (if needed). */ - pci_patch_ids(pdev, ptr, size); + if (load_file) { + void *ptr = memory_region_get_ram_ptr(&pdev->rom); + + if (load_image_size(path, ptr, size) < 0) { + error_setg(errp, "failed to load romfile \"%s\"", pdev->romfile); + return; + } + + if (is_default_rom) { + /* Only the default rom images will be patched (if needed). */ + pci_patch_ids(pdev, ptr, size); + } } pci_register_bar(pdev, PCI_ROM_SLOT, 0, &pdev->rom); @@ -2571,44 +2489,6 @@ uint8_t pci_find_capability(PCIDevice *pdev, uint8_t cap_id) return pci_find_capability_list(pdev, cap_id, NULL); } -static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent) -{ - PCIDevice *d = (PCIDevice *)dev; - const pci_class_desc *desc; - char ctxt[64]; - PCIIORegion *r; - int i, class; - - class = pci_get_word(d->config + PCI_CLASS_DEVICE); - desc = pci_class_descriptions; - while (desc->desc && class != desc->class) - desc++; - if (desc->desc) { - snprintf(ctxt, sizeof(ctxt), "%s", desc->desc); - } else { - snprintf(ctxt, sizeof(ctxt), "Class %04x", class); - } - - monitor_printf(mon, "%*sclass %s, addr %02x:%02x.%x, " - "pci id %04x:%04x (sub %04x:%04x)\n", - indent, "", ctxt, pci_dev_bus_num(d), - PCI_SLOT(d->devfn), PCI_FUNC(d->devfn), - pci_get_word(d->config + PCI_VENDOR_ID), - pci_get_word(d->config + PCI_DEVICE_ID), - pci_get_word(d->config + PCI_SUBSYSTEM_VENDOR_ID), - pci_get_word(d->config + PCI_SUBSYSTEM_ID)); - for (i = 0; i < PCI_NUM_REGIONS; i++) { - r = &d->io_regions[i]; - if (!r->size) - continue; - monitor_printf(mon, "%*sbar %d: %s at 0x%"FMT_PCIBUS - " [0x%"FMT_PCIBUS"]\n", - indent, "", - i, r->type & PCI_BASE_ADDRESS_SPACE_IO ? "i/o" : "mem", - r->addr, r->addr + r->size - 1); - } -} - static char *pci_dev_fw_name(DeviceState *dev, char *buf, int len) { PCIDevice *d = (PCIDevice *)dev; @@ -2640,15 +2520,15 @@ static char *pci_dev_fw_name(DeviceState *dev, char *buf, int len) static char *pcibus_get_fw_dev_path(DeviceState *dev) { PCIDevice *d = (PCIDevice *)dev; - char path[50], name[33]; - int off; + char name[33]; + int has_func = !!PCI_FUNC(d->devfn); - off = snprintf(path, sizeof(path), "%s@%x", - pci_dev_fw_name(dev, name, sizeof name), - PCI_SLOT(d->devfn)); - if (PCI_FUNC(d->devfn)) - snprintf(path + off, sizeof(path) + off, ",%x", PCI_FUNC(d->devfn)); - return g_strdup(path); + return g_strdup_printf("%s@%x%s%.*x", + pci_dev_fw_name(dev, name, sizeof(name)), + PCI_SLOT(d->devfn), + has_func ? "," : "", + has_func, + PCI_FUNC(d->devfn)); } static char *pcibus_get_dev_path(DeviceState *dev) @@ -2774,7 +2654,7 @@ AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) PCIBus *iommu_bus = bus; uint8_t devfn = dev->devfn; - while (iommu_bus && !iommu_bus->iommu_fn && iommu_bus->parent_dev) { + while (iommu_bus && !iommu_bus->iommu_ops && iommu_bus->parent_dev) { PCIBus *parent_bus = pci_get_bus(iommu_bus->parent_dev); /* @@ -2813,22 +2693,29 @@ AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) iommu_bus = parent_bus; } - if (!pci_bus_bypass_iommu(bus) && iommu_bus && iommu_bus->iommu_fn) { - return iommu_bus->iommu_fn(bus, iommu_bus->iommu_opaque, devfn); + if (!pci_bus_bypass_iommu(bus) && iommu_bus->iommu_ops) { + return iommu_bus->iommu_ops->get_address_space(bus, + iommu_bus->iommu_opaque, devfn); } return &address_space_memory; } -void pci_setup_iommu(PCIBus *bus, PCIIOMMUFunc fn, void *opaque) +void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque) { - bus->iommu_fn = fn; + /* + * If called, pci_setup_iommu() should provide a minimum set of + * useful callbacks for the bus. + */ + assert(ops); + assert(ops->get_address_space); + + bus->iommu_ops = ops; bus->iommu_opaque = opaque; } static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) { Range *range = opaque; - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); uint16_t cmd = pci_get_word(dev->config + PCI_COMMAND); int i; @@ -2836,7 +2723,7 @@ static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) return; } - if (pc->is_bridge) { + if (IS_PCI_BRIDGE(dev)) { pcibus_t base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); pcibus_t limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index da34c8ebcd..6a4e38856d 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -36,6 +36,9 @@ #include "qemu/module.h" #include "qemu/range.h" #include "qapi/error.h" +#include "hw/acpi/acpi_aml_interface.h" +#include "hw/acpi/pci.h" +#include "hw/qdev-properties.h" /* PCI bridge subsystem vendor ID helper functions */ #define PCI_SSVID_SIZEOF 8 @@ -182,11 +185,11 @@ static void pci_bridge_init_vga_aliases(PCIBridge *br, PCIBus *parent, } } -static PCIBridgeWindows *pci_bridge_region_init(PCIBridge *br) +static void pci_bridge_region_init(PCIBridge *br) { PCIDevice *pd = PCI_DEVICE(br); PCIBus *parent = pci_get_bus(pd); - PCIBridgeWindows *w = g_new(PCIBridgeWindows, 1); + PCIBridgeWindows *w = &br->windows; uint16_t cmd = pci_get_word(pd->config + PCI_COMMAND); pci_bridge_init_alias(br, &w->alias_pref_mem, @@ -209,8 +212,6 @@ static PCIBridgeWindows *pci_bridge_region_init(PCIBridge *br) cmd & PCI_COMMAND_IO); pci_bridge_init_vga_aliases(br, parent, w->alias_vga); - - return w; } static void pci_bridge_region_del(PCIBridge *br, PCIBridgeWindows *w) @@ -232,19 +233,18 @@ static void pci_bridge_region_cleanup(PCIBridge *br, PCIBridgeWindows *w) object_unparent(OBJECT(&w->alias_vga[QEMU_PCI_VGA_IO_LO])); object_unparent(OBJECT(&w->alias_vga[QEMU_PCI_VGA_IO_HI])); object_unparent(OBJECT(&w->alias_vga[QEMU_PCI_VGA_MEM])); - g_free(w); } void pci_bridge_update_mappings(PCIBridge *br) { - PCIBridgeWindows *w = br->windows; + PCIBridgeWindows *w = &br->windows; /* Make updates atomic to: handle the case of one VCPU updating the bridge * while another accesses an unaffected region. */ memory_region_transaction_begin(); - pci_bridge_region_del(br, br->windows); + pci_bridge_region_del(br, w); pci_bridge_region_cleanup(br, w); - br->windows = pci_bridge_region_init(br); + pci_bridge_region_init(br); memory_region_transaction_commit(); } @@ -275,7 +275,7 @@ void pci_bridge_write_config(PCIDevice *d, newctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL); if (~oldctl & newctl & PCI_BRIDGE_CTL_BUS_RESET) { /* Trigger hot reset on 0->1 transition. */ - qbus_reset_all(BUS(&s->sec_bus)); + bus_cold_reset(BUS(&s->sec_bus)); } } @@ -383,9 +383,14 @@ void pci_bridge_initfn(PCIDevice *dev, const char *typename) sec_bus->address_space_io = &br->address_space_io; memory_region_init(&br->address_space_io, OBJECT(br), "pci_bridge_io", 4 * GiB); - br->windows = pci_bridge_region_init(br); + pci_bridge_region_init(br); QLIST_INIT(&sec_bus->child); QLIST_INSERT_HEAD(&parent->child, sec_bus, sibling); + + /* For express secondary buses, secondary latency timer is RO 0 */ + if (pci_bus_is_express(sec_bus) && !br->pcie_writeable_slt_bug) { + dev->wmask[PCI_SEC_LATENCY_TIMER] = 0; + } } /* default qdev clean up function for PCI-to-PCI bridge */ @@ -394,8 +399,8 @@ void pci_bridge_exitfn(PCIDevice *pci_dev) PCIBridge *s = PCI_BRIDGE(pci_dev); assert(QLIST_EMPTY(&s->sec_bus.child)); QLIST_REMOVE(&s->sec_bus, sibling); - pci_bridge_region_del(s, s->windows); - pci_bridge_region_cleanup(s, s->windows); + pci_bridge_region_del(s, &s->windows); + pci_bridge_region_cleanup(s, &s->windows); /* object_unparent() is called automatically during device deletion */ } @@ -467,11 +472,31 @@ int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset, return 0; } +static Property pci_bridge_properties[] = { + DEFINE_PROP_BOOL("x-pci-express-writeable-slt-bug", PCIBridge, + pcie_writeable_slt_bug, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pci_bridge_class_init(ObjectClass *klass, void *data) +{ + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); + DeviceClass *k = DEVICE_CLASS(klass); + + device_class_set_props(k, pci_bridge_properties); + adevc->build_dev_aml = build_pci_bridge_aml; +} + static const TypeInfo pci_bridge_type_info = { .name = TYPE_PCI_BRIDGE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIBridge), + .class_init = pci_bridge_class_init, .abstract = true, + .interfaces = (InterfaceInfo[]) { + { TYPE_ACPI_DEV_AML_IF }, + { }, + }, }; static void pci_bridge_register_types(void) diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index eaf217ff55..dfe6fe6184 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -62,6 +62,17 @@ static void pci_adjust_config_limit(PCIBus *bus, uint32_t *limit) } } +static bool is_pci_dev_ejected(PCIDevice *pci_dev) +{ + /* + * device unplug was requested and the guest acked it, + * so we stop responding config accesses even if the + * device is not deleted (failover flow) + */ + return pci_dev && pci_dev->partially_hotplugged && + !pci_dev->qdev.pending_deleted_event; +} + void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, uint32_t limit, uint32_t val, uint32_t len) { @@ -75,7 +86,7 @@ void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, * allowing direct removal of unexposed functions. */ if ((pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) || - !pci_dev->has_power) { + !pci_dev->has_power || is_pci_dev_ejected(pci_dev)) { return; } @@ -100,7 +111,7 @@ uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, * allowing direct removal of unexposed functions. */ if ((pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) || - !pci_dev->has_power) { + !pci_dev->has_power || is_pci_dev_ejected(pci_dev)) { return ~0x0; } @@ -118,6 +129,9 @@ void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, unsigned len) uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); if (!pci_dev) { + trace_pci_cfg_write("empty", extract32(addr, 16, 8), + extract32(addr, 11, 5), extract32(addr, 8, 3), + config_addr, val); return; } @@ -131,6 +145,9 @@ uint32_t pci_data_read(PCIBus *s, uint32_t addr, unsigned len) uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); if (!pci_dev) { + trace_pci_cfg_read("empty", extract32(addr, 16, 8), + extract32(addr, 11, 5), extract32(addr, 8, 3), + config_addr, ~0x0); return ~0x0; } @@ -143,7 +160,7 @@ static void pci_host_config_write(void *opaque, hwaddr addr, { PCIHostState *s = opaque; - PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %"PRIx64"\n", + PCI_DPRINTF("%s addr " HWADDR_FMT_plx " len %d val %"PRIx64"\n", __func__, addr, len, val); if (addr != 0 || len != 4) { return; @@ -157,7 +174,7 @@ static uint64_t pci_host_config_read(void *opaque, hwaddr addr, PCIHostState *s = opaque; uint32_t val = s->config_reg; - PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %"PRIx32"\n", + PCI_DPRINTF("%s addr " HWADDR_FMT_plx " len %d val %"PRIx32"\n", __func__, addr, len, val); return val; } @@ -217,7 +234,7 @@ const VMStateDescription vmstate_pcihost = { .needed = pci_host_needed, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(config_reg, PCIHostState), VMSTATE_END_OF_LIST() } @@ -226,7 +243,7 @@ const VMStateDescription vmstate_pcihost = { static Property pci_host_properties_common[] = { DEFINE_PROP_BOOL("x-config-reg-migration-enabled", PCIHostState, mig_enabled, true), - DEFINE_PROP_BOOL("bypass-iommu", PCIHostState, bypass_iommu, false), + DEFINE_PROP_BOOL(PCI_HOST_BYPASS_IOMMU, PCIHostState, bypass_iommu, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 68a62da0b5..4b2f0805c6 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -28,6 +28,7 @@ #include "hw/pci/pcie_regs.h" #include "hw/pci/pcie_port.h" #include "qemu/range.h" +#include "trace.h" //#define DEBUG_PCIE #ifdef DEBUG_PCIE @@ -39,6 +40,28 @@ #define PCIE_DEV_PRINTF(dev, fmt, ...) \ PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) +static bool pcie_sltctl_powered_off(uint16_t sltctl) +{ + return (sltctl & PCI_EXP_SLTCTL_PCC) == PCI_EXP_SLTCTL_PWR_OFF + && (sltctl & PCI_EXP_SLTCTL_PIC) == PCI_EXP_SLTCTL_PWR_IND_OFF; +} + +static const char *pcie_led_state_to_str(uint16_t value) +{ + switch (value) { + case PCI_EXP_SLTCTL_PWR_IND_ON: + case PCI_EXP_SLTCTL_ATTN_IND_ON: + return "on"; + case PCI_EXP_SLTCTL_PWR_IND_BLINK: + case PCI_EXP_SLTCTL_ATTN_IND_BLINK: + return "blink"; + case PCI_EXP_SLTCTL_PWR_IND_OFF: + case PCI_EXP_SLTCTL_ATTN_IND_OFF: + return "off"; + default: + return "invalid"; + } +} /*************************************************************************** * pci express capability helper functions @@ -148,6 +171,14 @@ static void pcie_cap_fill_slot_lnk(PCIDevice *dev) pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2, PCI_EXP_LNKCAP2_SLS_16_0GB); } + if (s->speed > QEMU_PCI_EXP_LNK_16GT) { + pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2, + PCI_EXP_LNKCAP2_SLS_32_0GB); + } + if (s->speed > QEMU_PCI_EXP_LNK_32GT) { + pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2, + PCI_EXP_LNKCAP2_SLS_64_0GB); + } } } @@ -269,6 +300,13 @@ uint8_t pcie_cap_get_type(const PCIDevice *dev) PCI_EXP_FLAGS_TYPE) >> PCI_EXP_FLAGS_TYPE_SHIFT; } +uint8_t pcie_cap_get_version(const PCIDevice *dev) +{ + uint32_t pos = dev->exp.exp_cap; + assert(pos > 0); + return pci_get_word(dev->config + pos + PCI_EXP_FLAGS) & PCI_EXP_FLAGS_VERS; +} + /* MSI/MSI-X */ /* pci express interrupt message number */ /* 7.8.2 PCI Express Capabilities Register: Interrupt Message Number */ @@ -373,8 +411,8 @@ void pcie_cap_slot_enable_power(PCIDevice *dev) uint32_t sltcap = pci_get_long(exp_cap + PCI_EXP_SLTCAP); if (sltcap & PCI_EXP_SLTCAP_PCP) { - pci_set_word_by_mask(exp_cap + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PWR_ON); + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PCC); } } @@ -395,6 +433,7 @@ static void pcie_cap_update_power(PCIDevice *hotplug_dev) if (sltcap & PCI_EXP_SLTCAP_PCP) { power = (sltctl & PCI_EXP_SLTCTL_PCC) == PCI_EXP_SLTCTL_PWR_ON; + /* Don't we need to check also (sltctl & PCI_EXP_SLTCTL_PIC) ? */ } pci_for_each_device(sec_bus, pci_bus_num(sec_bus), @@ -579,8 +618,7 @@ void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, return; } - if (((sltctl & PCI_EXP_SLTCTL_PIC) == PCI_EXP_SLTCTL_PWR_IND_OFF) && - ((sltctl & PCI_EXP_SLTCTL_PCC) == PCI_EXP_SLTCTL_PWR_OFF)) { + if (pcie_sltctl_powered_off(sltctl)) { /* slot is powered off -> unplug without round-trip to the guest */ pcie_cap_slot_do_unplug(hotplug_pdev); hotplug_event_notify(hotplug_pdev); @@ -611,11 +649,11 @@ void pcie_cap_slot_init(PCIDevice *dev, PCIESlot *s) PCI_EXP_SLTCAP_ABP); /* - * Enable native hot-plug on all hot-plugged bridges unless - * hot-plug is disabled on the slot. + * Expose native hot-plug on all bridges if hot-plug is enabled on the slot. + * (unless broken 6.1 ABI is enforced for compat reasons) */ if (s->hotplug && - (s->native_hotplug || DEVICE(dev)->hotplugged)) { + (!s->hide_native_hotplug_cap || DEVICE(dev)->hotplugged)) { pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP, PCI_EXP_SLTCAP_HPS | PCI_EXP_SLTCAP_HPC); @@ -634,8 +672,8 @@ void pcie_cap_slot_init(PCIDevice *dev, PCIESlot *s) PCI_EXP_SLTCTL_PIC | PCI_EXP_SLTCTL_AIC); pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PIC_OFF | - PCI_EXP_SLTCTL_AIC_OFF); + PCI_EXP_SLTCTL_PWR_IND_OFF | + PCI_EXP_SLTCTL_ATTN_IND_OFF); pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PIC | PCI_EXP_SLTCTL_AIC | @@ -654,6 +692,10 @@ void pcie_cap_slot_init(PCIDevice *dev, PCIESlot *s) pci_word_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_SLTSTA, PCI_EXP_HP_EV_SUPPORTED); + /* Avoid migration abortion when this device hot-removed by guest */ + pci_word_test_and_clear_mask(dev->cmask + pos + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + dev->exp.hpev_notified = false; qbus_set_hotplug_handler(BUS(pci_bridge_get_sec_bus(PCI_BRIDGE(dev))), @@ -679,7 +721,8 @@ void pcie_cap_slot_reset(PCIDevice *dev) PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE); pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_AIC_OFF); + PCI_EXP_SLTCTL_PWR_IND_OFF | + PCI_EXP_SLTCTL_ATTN_IND_OFF); if (dev->cap_present & QEMU_PCIE_SLTCAP_PCP) { /* Downstream ports enforce device number 0. */ @@ -694,7 +737,8 @@ void pcie_cap_slot_reset(PCIDevice *dev) PCI_EXP_SLTCTL_PCC); } - pic = populated ? PCI_EXP_SLTCTL_PIC_ON : PCI_EXP_SLTCTL_PIC_OFF; + pic = populated ? + PCI_EXP_SLTCTL_PWR_IND_ON : PCI_EXP_SLTCTL_PWR_IND_OFF; pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL, pic); } @@ -717,6 +761,28 @@ void pcie_cap_slot_get(PCIDevice *dev, uint16_t *slt_ctl, uint16_t *slt_sta) *slt_sta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); } +static void find_child_fn(PCIBus *bus, PCIDevice *dev, void *opaque) +{ + PCIDevice **child = opaque; + + if (!*child) { + *child = dev; + } +} + +/* + * Returns the plugged device or first function of multifunction plugged device + */ +static PCIDevice *pcie_cap_slot_find_child(PCIDevice *dev) +{ + PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(dev)); + PCIDevice *child = NULL; + + pci_for_each_device(sec_bus, pci_bus_num(sec_bus), find_child_fn, &child); + + return child; +} + void pcie_cap_slot_write_config(PCIDevice *dev, uint16_t old_slt_ctl, uint16_t old_slt_sta, uint32_t addr, uint32_t val, int len) @@ -761,6 +827,22 @@ void pcie_cap_slot_write_config(PCIDevice *dev, sltsta); } + if (trace_event_get_state_backends(TRACE_PCIE_CAP_SLOT_WRITE_CONFIG)) { + DeviceState *parent = DEVICE(dev); + DeviceState *child = DEVICE(pcie_cap_slot_find_child(dev)); + + trace_pcie_cap_slot_write_config( + parent->canonical_path, + child ? child->canonical_path : "no-child", + (sltsta & PCI_EXP_SLTSTA_PDS) ? "present" : "not present", + pcie_led_state_to_str(old_slt_ctl & PCI_EXP_SLTCTL_PIC), + pcie_led_state_to_str(val & PCI_EXP_SLTCTL_PIC), + pcie_led_state_to_str(old_slt_ctl & PCI_EXP_SLTCTL_AIC), + pcie_led_state_to_str(val & PCI_EXP_SLTCTL_AIC), + (old_slt_ctl & PCI_EXP_SLTCTL_PWR_OFF) ? "off" : "on", + (val & PCI_EXP_SLTCTL_PWR_OFF) ? "off" : "on"); + } + /* * If the slot is populated, power indicator is off and power * controller is off, it is safe to detach the devices. @@ -769,10 +851,9 @@ void pcie_cap_slot_write_config(PCIDevice *dev, * this is a work around for guests that overwrite * control of powered off slots before powering them on. */ - if ((sltsta & PCI_EXP_SLTSTA_PDS) && (val & PCI_EXP_SLTCTL_PCC) && - (val & PCI_EXP_SLTCTL_PIC_OFF) == PCI_EXP_SLTCTL_PIC_OFF && - (!(old_slt_ctl & PCI_EXP_SLTCTL_PCC) || - (old_slt_ctl & PCI_EXP_SLTCTL_PIC_OFF) != PCI_EXP_SLTCTL_PIC_OFF)) { + if ((sltsta & PCI_EXP_SLTSTA_PDS) && pcie_sltctl_powered_off(val) && + !pcie_sltctl_powered_off(old_slt_ctl)) + { pcie_cap_slot_do_unplug(dev); } pcie_cap_update_power(dev); @@ -1022,8 +1103,10 @@ void pcie_sync_bridge_lnk(PCIDevice *bridge_dev) */ /* ARI */ -void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn) +void pcie_ari_init(PCIDevice *dev, uint16_t offset) { + uint16_t nextfn = dev->cap_present & QEMU_PCIE_ARI_NEXTFN_1 ? 1 : 0; + pcie_add_capability(dev, PCI_EXT_CAP_ID_ARI, PCI_ARI_VER, offset, PCI_ARI_SIZEOF); pci_set_long(dev->config + offset + PCI_ARI_CAP, (nextfn & 0xff) << 8); diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index 92bd0530dd..2c85a78fcd 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -19,17 +19,14 @@ */ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" -#include "qapi/qmp/qdict.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pcie.h" #include "hw/pci/msix.h" #include "hw/pci/msi.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pcie_regs.h" -#include "qapi/error.h" +#include "pci-internal.h" //#define DEBUG_PCIE #ifdef DEBUG_PCIE @@ -44,13 +41,6 @@ #define PCI_ERR_SRC_COR_OFFS 0 #define PCI_ERR_SRC_UNCOR_OFFS 2 -typedef struct PCIEErrorDetails { - const char *id; - const char *root_bus; - int bus; - int devfn; -} PCIEErrorDetails; - /* From 6.2.7 Error Listing and Rules. Table 6-2, 6-3 and 6-4 */ static uint32_t pcie_aer_uncor_default_severity(uint32_t status) { @@ -123,6 +113,13 @@ int pcie_aer_init(PCIDevice *dev, uint8_t cap_ver, uint16_t offset, pci_set_long(dev->w1cmask + offset + PCI_ERR_UNCOR_STATUS, PCI_ERR_UNC_SUPPORTED); + if (dev->cap_present & QEMU_PCIE_ERR_UNC_MASK) { + pci_set_long(dev->config + offset + PCI_ERR_UNCOR_MASK, + PCI_ERR_UNC_MASK_DEFAULT); + pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_MASK, + PCI_ERR_UNC_SUPPORTED); + } + pci_set_long(dev->config + offset + PCI_ERR_UNCOR_SEVER, PCI_ERR_UNC_SEVERITY_DEFAULT); pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_SEVER, @@ -198,8 +195,16 @@ static void pcie_aer_update_uncor_status(PCIDevice *dev) static bool pcie_aer_msg_alldev(PCIDevice *dev, const PCIEAERMsg *msg) { + uint16_t devctl = pci_get_word(dev->config + dev->exp.exp_cap + + PCI_EXP_DEVCTL); if (!(pcie_aer_msg_is_uncor(msg) && - (pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_SERR))) { + (pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_SERR)) && + !((msg->severity == PCI_ERR_ROOT_CMD_NONFATAL_EN) && + (devctl & PCI_EXP_DEVCTL_NFERE)) && + !((msg->severity == PCI_ERR_ROOT_CMD_COR_EN) && + (devctl & PCI_EXP_DEVCTL_CERE)) && + !((msg->severity == PCI_ERR_ROOT_CMD_FATAL_EN) && + (devctl & PCI_EXP_DEVCTL_FERE))) { return false; } @@ -319,11 +324,11 @@ static void pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) * it isn't implemented in qemu right now. * So just discard the error for now. * OS which cares of aer would receive errors via - * native aer mechanims, so this wouldn't matter. + * native aer mechanisms, so this wouldn't matter. */ } - /* Errro Message Received: Root Error Status register */ + /* Error Message Received: Root Error Status register */ switch (msg->severity) { case PCI_ERR_ROOT_CMD_COR_EN: if (root_status & PCI_ERR_ROOT_COR_RCV) { @@ -631,7 +636,7 @@ static bool pcie_aer_inject_uncor_error(PCIEAERInject *inj, bool is_fatal) * Figure 6-2: Flowchart Showing Sequence of Device Error Signaling and Logging * Operations */ -static int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err) +int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err) { uint8_t *aer_cap = NULL; uint16_t devctl = 0; @@ -792,7 +797,7 @@ static const VMStateDescription vmstate_pcie_aer_err = { .name = "PCIE_AER_ERROR", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(status, PCIEAERErr), VMSTATE_UINT16(source_id, PCIEAERErr), VMSTATE_UINT16(flags, PCIEAERErr), @@ -813,7 +818,7 @@ const VMStateDescription vmstate_pcie_aer_log = { .name = "PCIE_AER_ERROR_LOG", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(log_num, PCIEAERLog), VMSTATE_UINT16_EQUAL(log_max, PCIEAERLog, NULL), VMSTATE_VALIDATE("log_num <= log_max", pcie_aer_state_log_num_valid), @@ -933,8 +938,8 @@ static const struct PCIEAERErrorName pcie_aer_error_list[] = { }, }; -static int pcie_aer_parse_error_string(const char *error_name, - uint32_t *status, bool *correctable) +int pcie_aer_parse_error_string(const char *error_name, + uint32_t *status, bool *correctable) { int i; @@ -950,98 +955,3 @@ static int pcie_aer_parse_error_string(const char *error_name, } return -EINVAL; } - -/* - * Inject an error described by @qdict. - * On success, set @details to show where error was sent. - * Return negative errno if injection failed and a message was emitted. - */ -static int do_pcie_aer_inject_error(Monitor *mon, - const QDict *qdict, - PCIEErrorDetails *details) -{ - const char *id = qdict_get_str(qdict, "id"); - const char *error_name; - uint32_t error_status; - bool correctable; - PCIDevice *dev; - PCIEAERErr err; - int ret; - - ret = pci_qdev_find_device(id, &dev); - if (ret < 0) { - monitor_printf(mon, - "id or pci device path is invalid or device not " - "found. %s\n", id); - return ret; - } - if (!pci_is_express(dev)) { - monitor_printf(mon, "the device doesn't support pci express. %s\n", - id); - return -ENOSYS; - } - - error_name = qdict_get_str(qdict, "error_status"); - if (pcie_aer_parse_error_string(error_name, &error_status, &correctable)) { - char *e = NULL; - error_status = strtoul(error_name, &e, 0); - correctable = qdict_get_try_bool(qdict, "correctable", false); - if (!e || *e != '\0') { - monitor_printf(mon, "invalid error status value. \"%s\"", - error_name); - return -EINVAL; - } - } - err.status = error_status; - err.source_id = pci_requester_id(dev); - - err.flags = 0; - if (correctable) { - err.flags |= PCIE_AER_ERR_IS_CORRECTABLE; - } - if (qdict_get_try_bool(qdict, "advisory_non_fatal", false)) { - err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY; - } - if (qdict_haskey(qdict, "header0")) { - err.flags |= PCIE_AER_ERR_HEADER_VALID; - } - if (qdict_haskey(qdict, "prefix0")) { - err.flags |= PCIE_AER_ERR_TLP_PREFIX_PRESENT; - } - - err.header[0] = qdict_get_try_int(qdict, "header0", 0); - err.header[1] = qdict_get_try_int(qdict, "header1", 0); - err.header[2] = qdict_get_try_int(qdict, "header2", 0); - err.header[3] = qdict_get_try_int(qdict, "header3", 0); - - err.prefix[0] = qdict_get_try_int(qdict, "prefix0", 0); - err.prefix[1] = qdict_get_try_int(qdict, "prefix1", 0); - err.prefix[2] = qdict_get_try_int(qdict, "prefix2", 0); - err.prefix[3] = qdict_get_try_int(qdict, "prefix3", 0); - - ret = pcie_aer_inject_error(dev, &err); - if (ret < 0) { - monitor_printf(mon, "failed to inject error: %s\n", - strerror(-ret)); - return ret; - } - details->id = id; - details->root_bus = pci_root_bus_path(dev); - details->bus = pci_dev_bus_num(dev); - details->devfn = dev->devfn; - - return 0; -} - -void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict) -{ - PCIEErrorDetails data; - - if (do_pcie_aer_inject_error(mon, qdict, &data) < 0) { - return; - } - - monitor_printf(mon, "OK id: %s root bus: %s, bus: %x devfn: %x.%x\n", - data.id, data.root_bus, data.bus, - PCI_SLOT(data.devfn), PCI_FUNC(data.devfn)); -} diff --git a/hw/pci/pcie_doe.c b/hw/pci/pcie_doe.c new file mode 100644 index 0000000000..2210f86968 --- /dev/null +++ b/hw/pci/pcie_doe.c @@ -0,0 +1,367 @@ +/* + * PCIe Data Object Exchange + * + * Copyright (C) 2021 Avery Design Systems, 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 "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qemu/range.h" +#include "hw/pci/pci.h" +#include "hw/pci/pcie.h" +#include "hw/pci/pcie_doe.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" + +#define DWORD_BYTE 4 + +typedef struct DoeDiscoveryReq { + DOEHeader header; + uint8_t index; + uint8_t reserved[3]; +} QEMU_PACKED DoeDiscoveryReq; + +typedef struct DoeDiscoveryRsp { + DOEHeader header; + uint16_t vendor_id; + uint8_t data_obj_type; + uint8_t next_index; +} QEMU_PACKED DoeDiscoveryRsp; + +static bool pcie_doe_discovery(DOECap *doe_cap) +{ + DoeDiscoveryReq *req = pcie_doe_get_write_mbox_ptr(doe_cap); + DoeDiscoveryRsp rsp; + uint8_t index = req->index; + DOEProtocol *prot; + + /* Discard request if length does not match DoeDiscoveryReq */ + if (pcie_doe_get_obj_len(req) < + DIV_ROUND_UP(sizeof(DoeDiscoveryReq), DWORD_BYTE)) { + return false; + } + + rsp.header = (DOEHeader) { + .vendor_id = PCI_VENDOR_ID_PCI_SIG, + .data_obj_type = PCI_SIG_DOE_DISCOVERY, + .length = DIV_ROUND_UP(sizeof(DoeDiscoveryRsp), DWORD_BYTE), + }; + + /* Point to the requested protocol, index 0 must be Discovery */ + if (index == 0) { + rsp.vendor_id = PCI_VENDOR_ID_PCI_SIG; + rsp.data_obj_type = PCI_SIG_DOE_DISCOVERY; + } else { + if (index < doe_cap->protocol_num) { + prot = &doe_cap->protocols[index - 1]; + rsp.vendor_id = prot->vendor_id; + rsp.data_obj_type = prot->data_obj_type; + } else { + rsp.vendor_id = 0xFFFF; + rsp.data_obj_type = 0xFF; + } + } + + if (index + 1 == doe_cap->protocol_num) { + rsp.next_index = 0; + } else { + rsp.next_index = index + 1; + } + + pcie_doe_set_rsp(doe_cap, &rsp); + + return true; +} + +static void pcie_doe_reset_mbox(DOECap *st) +{ + st->read_mbox_idx = 0; + st->read_mbox_len = 0; + st->write_mbox_len = 0; + + memset(st->read_mbox, 0, PCI_DOE_DW_SIZE_MAX * DWORD_BYTE); + memset(st->write_mbox, 0, PCI_DOE_DW_SIZE_MAX * DWORD_BYTE); +} + +void pcie_doe_init(PCIDevice *dev, DOECap *doe_cap, uint16_t offset, + DOEProtocol *protocols, bool intr, uint16_t vec) +{ + pcie_add_capability(dev, PCI_EXT_CAP_ID_DOE, 0x1, offset, + PCI_DOE_SIZEOF); + + doe_cap->pdev = dev; + doe_cap->offset = offset; + + if (intr && (msi_present(dev) || msix_present(dev))) { + doe_cap->cap.intr = intr; + doe_cap->cap.vec = vec; + } + + doe_cap->write_mbox = g_malloc0(PCI_DOE_DW_SIZE_MAX * DWORD_BYTE); + doe_cap->read_mbox = g_malloc0(PCI_DOE_DW_SIZE_MAX * DWORD_BYTE); + + pcie_doe_reset_mbox(doe_cap); + + doe_cap->protocols = protocols; + for (; protocols->vendor_id; protocols++) { + doe_cap->protocol_num++; + } + assert(doe_cap->protocol_num < PCI_DOE_PROTOCOL_NUM_MAX); + + /* Increment to allow for the discovery protocol */ + doe_cap->protocol_num++; +} + +void pcie_doe_fini(DOECap *doe_cap) +{ + g_free(doe_cap->read_mbox); + g_free(doe_cap->write_mbox); + g_free(doe_cap); +} + +uint32_t pcie_doe_build_protocol(DOEProtocol *p) +{ + return DATA_OBJ_BUILD_HEADER1(p->vendor_id, p->data_obj_type); +} + +void *pcie_doe_get_write_mbox_ptr(DOECap *doe_cap) +{ + return doe_cap->write_mbox; +} + +/* + * Copy the response to read mailbox buffer + * This might be called in self-defined handle_request() if a DOE response is + * required in the corresponding protocol + */ +void pcie_doe_set_rsp(DOECap *doe_cap, void *rsp) +{ + uint32_t len = pcie_doe_get_obj_len(rsp); + + memcpy(doe_cap->read_mbox + doe_cap->read_mbox_len, rsp, len * DWORD_BYTE); + doe_cap->read_mbox_len += len; +} + +uint32_t pcie_doe_get_obj_len(void *obj) +{ + uint32_t len; + + if (!obj) { + return 0; + } + + /* Only lower 18 bits are valid */ + len = DATA_OBJ_LEN_MASK(((DOEHeader *)obj)->length); + + /* PCIe r6.0 Table 6.29: a value of 00000h indicates 2^18 DW */ + return (len) ? len : PCI_DOE_DW_SIZE_MAX; +} + +static void pcie_doe_irq_assert(DOECap *doe_cap) +{ + PCIDevice *dev = doe_cap->pdev; + + if (doe_cap->cap.intr && doe_cap->ctrl.intr) { + if (doe_cap->status.intr) { + return; + } + doe_cap->status.intr = 1; + + if (msix_enabled(dev)) { + msix_notify(dev, doe_cap->cap.vec); + } else if (msi_enabled(dev)) { + msi_notify(dev, doe_cap->cap.vec); + } + } +} + +static void pcie_doe_set_ready(DOECap *doe_cap, bool rdy) +{ + doe_cap->status.ready = rdy; + + if (rdy) { + pcie_doe_irq_assert(doe_cap); + } +} + +static void pcie_doe_set_error(DOECap *doe_cap, bool err) +{ + doe_cap->status.error = err; + + if (err) { + pcie_doe_irq_assert(doe_cap); + } +} + +/* + * Check incoming request in write_mbox for protocol format + */ +static void pcie_doe_prepare_rsp(DOECap *doe_cap) +{ + bool success = false; + int p; + bool (*handle_request)(DOECap *) = NULL; + + if (doe_cap->status.error) { + return; + } + + if (doe_cap->write_mbox[0] == + DATA_OBJ_BUILD_HEADER1(PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_DISCOVERY)) { + handle_request = pcie_doe_discovery; + } else { + for (p = 0; p < doe_cap->protocol_num - 1; p++) { + if (doe_cap->write_mbox[0] == + pcie_doe_build_protocol(&doe_cap->protocols[p])) { + handle_request = doe_cap->protocols[p].handle_request; + break; + } + } + } + + /* + * PCIe r6 DOE 6.30.1: + * If the number of DW transferred does not match the + * indicated Length for a data object, then the + * data object must be silently discarded. + */ + if (handle_request && (doe_cap->write_mbox_len == + pcie_doe_get_obj_len(pcie_doe_get_write_mbox_ptr(doe_cap)))) { + success = handle_request(doe_cap); + } + + if (success) { + pcie_doe_set_ready(doe_cap, 1); + } else { + pcie_doe_reset_mbox(doe_cap); + } +} + +/* + * Read from DOE config space. + * Return false if the address not within DOE_CAP range. + */ +bool pcie_doe_read_config(DOECap *doe_cap, uint32_t addr, int size, + uint32_t *buf) +{ + uint32_t shift; + uint16_t doe_offset = doe_cap->offset; + + if (!range_covers_byte(doe_offset + PCI_EXP_DOE_CAP, + PCI_DOE_SIZEOF - 4, addr)) { + return false; + } + + addr -= doe_offset; + *buf = 0; + + if (range_covers_byte(PCI_EXP_DOE_CAP, DWORD_BYTE, addr)) { + *buf = FIELD_DP32(*buf, PCI_DOE_CAP_REG, INTR_SUPP, + doe_cap->cap.intr); + *buf = FIELD_DP32(*buf, PCI_DOE_CAP_REG, DOE_INTR_MSG_NUM, + doe_cap->cap.vec); + } else if (range_covers_byte(PCI_EXP_DOE_CTRL, DWORD_BYTE, addr)) { + /* Must return ABORT=0 and GO=0 */ + *buf = FIELD_DP32(*buf, PCI_DOE_CAP_CONTROL, DOE_INTR_EN, + doe_cap->ctrl.intr); + } else if (range_covers_byte(PCI_EXP_DOE_STATUS, DWORD_BYTE, addr)) { + *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_BUSY, + doe_cap->status.busy); + *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_INTR_STATUS, + doe_cap->status.intr); + *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_ERROR, + doe_cap->status.error); + *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DATA_OBJ_RDY, + doe_cap->status.ready); + /* Mailbox should be DW accessed */ + } else if (addr == PCI_EXP_DOE_RD_DATA_MBOX && size == DWORD_BYTE) { + if (doe_cap->status.ready && !doe_cap->status.error) { + *buf = doe_cap->read_mbox[doe_cap->read_mbox_idx]; + } + } + + /* Process Alignment */ + shift = addr % DWORD_BYTE; + *buf = extract32(*buf, shift * 8, size * 8); + + return true; +} + +/* + * Write to DOE config space. + * Return if the address not within DOE_CAP range or receives an abort + */ +void pcie_doe_write_config(DOECap *doe_cap, + uint32_t addr, uint32_t val, int size) +{ + uint16_t doe_offset = doe_cap->offset; + uint32_t shift; + + if (!range_covers_byte(doe_offset + PCI_EXP_DOE_CAP, + PCI_DOE_SIZEOF - 4, addr)) { + return; + } + + /* Process Alignment */ + shift = addr % DWORD_BYTE; + addr -= (doe_offset + shift); + val = deposit32(val, shift * 8, size * 8, val); + + switch (addr) { + case PCI_EXP_DOE_CTRL: + if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_ABORT)) { + pcie_doe_set_ready(doe_cap, 0); + pcie_doe_set_error(doe_cap, 0); + pcie_doe_reset_mbox(doe_cap); + return; + } + + if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_GO)) { + pcie_doe_prepare_rsp(doe_cap); + } + + if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_INTR_EN)) { + doe_cap->ctrl.intr = 1; + /* Clear interrupt bit located within the first byte */ + } else if (shift == 0) { + doe_cap->ctrl.intr = 0; + } + break; + case PCI_EXP_DOE_STATUS: + if (FIELD_EX32(val, PCI_DOE_CAP_STATUS, DOE_INTR_STATUS)) { + doe_cap->status.intr = 0; + } + break; + case PCI_EXP_DOE_RD_DATA_MBOX: + /* Mailbox should be DW accessed */ + if (size != DWORD_BYTE) { + return; + } + doe_cap->read_mbox_idx++; + if (doe_cap->read_mbox_idx == doe_cap->read_mbox_len) { + pcie_doe_reset_mbox(doe_cap); + pcie_doe_set_ready(doe_cap, 0); + } else if (doe_cap->read_mbox_idx > doe_cap->read_mbox_len) { + /* Underflow */ + pcie_doe_set_error(doe_cap, 1); + } + break; + case PCI_EXP_DOE_WR_DATA_MBOX: + /* Mailbox should be DW accessed */ + if (size != DWORD_BYTE) { + return; + } + doe_cap->write_mbox[doe_cap->write_mbox_len] = val; + doe_cap->write_mbox_len++; + break; + case PCI_EXP_DOE_CAP: + /* fallthrough */ + default: + break; + } +} diff --git a/hw/pci/pcie_host.c b/hw/pci/pcie_host.c index 5abbe83220..3717e1a086 100644 --- a/hw/pci/pcie_host.c +++ b/hw/pci/pcie_host.c @@ -20,7 +20,7 @@ */ #include "qemu/osdep.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pcie_host.h" #include "qemu/module.h" diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c index 687e4e763a..20ff2b39e8 100644 --- a/hw/pci/pcie_port.c +++ b/hw/pci/pcie_port.c @@ -161,6 +161,51 @@ PCIDevice *pcie_find_port_by_pn(PCIBus *bus, uint8_t pn) return NULL; } +/* Find first port in devfn number order */ +PCIDevice *pcie_find_port_first(PCIBus *bus) +{ + int devfn; + + for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + PCIDevice *d = bus->devices[devfn]; + + if (!d || !pci_is_express(d) || !d->exp.exp_cap) { + continue; + } + + if (object_dynamic_cast(OBJECT(d), TYPE_PCIE_PORT)) { + return d; + } + } + + return NULL; +} + +int pcie_count_ds_ports(PCIBus *bus) +{ + int dsp_count = 0; + int devfn; + + for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + PCIDevice *d = bus->devices[devfn]; + + if (!d || !pci_is_express(d) || !d->exp.exp_cap) { + continue; + } + if (object_dynamic_cast(OBJECT(d), TYPE_PCIE_PORT)) { + dsp_count++; + } + } + return dsp_count; +} + +static bool pcie_slot_is_hotpluggbale_bus(HotplugHandler *plug_handler, + BusState *bus) +{ + PCIESlot *s = PCIE_SLOT(bus->parent); + return s->hotplug; +} + static const TypeInfo pcie_port_type_info = { .name = TYPE_PCIE_PORT, .parent = TYPE_PCI_BRIDGE, @@ -173,7 +218,8 @@ static Property pcie_slot_props[] = { DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0), DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0), DEFINE_PROP_BOOL("hotplug", PCIESlot, hotplug, true), - DEFINE_PROP_BOOL("x-native-hotplug", PCIESlot, native_hotplug, true), + DEFINE_PROP_BOOL("x-do-not-expose-native-hotplug-cap", PCIESlot, + hide_native_hotplug_cap, false), DEFINE_PROP_END_OF_LIST() }; @@ -187,6 +233,7 @@ static void pcie_slot_class_init(ObjectClass *oc, void *data) hc->plug = pcie_cap_slot_plug_cb; hc->unplug = pcie_cap_slot_unplug_cb; hc->unplug_request = pcie_cap_slot_unplug_request_cb; + hc->is_hotpluggable_bus = pcie_slot_is_hotpluggbale_bus; } static const TypeInfo pcie_slot_type_info = { diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 8e3faf1f59..e9b23221d7 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pcie.h" #include "hw/pci/pci_bus.h" #include "hw/qdev-properties.h" @@ -176,9 +176,11 @@ static void register_vfs(PCIDevice *dev) assert(sriov_cap > 0); num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); + if (num_vfs > pci_get_word(dev->config + sriov_cap + PCI_SRIOV_TOTAL_VF)) { + return; + } dev->exp.sriov_pf.vf = g_new(PCIDevice *, num_vfs); - assert(dev->exp.sriov_pf.vf); trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); @@ -196,26 +198,23 @@ static void register_vfs(PCIDevice *dev) static void unregister_vfs(PCIDevice *dev) { - Error *local_err = NULL; uint16_t num_vfs = dev->exp.sriov_pf.num_vfs; uint16_t i; trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), num_vfs); for (i = 0; i < num_vfs; i++) { + Error *err = NULL; PCIDevice *vf = dev->exp.sriov_pf.vf[i]; - object_property_set_bool(OBJECT(vf), "realized", false, &local_err); - if (local_err) { - fprintf(stderr, "Failed to unplug: %s\n", - error_get_pretty(local_err)); - error_free(local_err); + if (!object_property_set_bool(OBJECT(vf), "realized", false, &err)) { + error_reportf_err(err, "Failed to unplug: "); } object_unparent(OBJECT(vf)); + object_unref(OBJECT(vf)); } g_free(dev->exp.sriov_pf.vf); dev->exp.sriov_pf.vf = NULL; dev->exp.sriov_pf.num_vfs = 0; - pci_set_word(dev->config + dev->exp.sriov_cap + PCI_SRIOV_NUM_VF, 0); } void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, @@ -249,16 +248,28 @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, } -/* Reset SR/IOV VF Enable bit to trigger an unregister of all VFs */ -void pcie_sriov_pf_disable_vfs(PCIDevice *dev) +/* Reset SR/IOV */ +void pcie_sriov_pf_reset(PCIDevice *dev) { uint16_t sriov_cap = dev->exp.sriov_cap; - if (sriov_cap) { - uint32_t val = pci_get_byte(dev->config + sriov_cap + PCI_SRIOV_CTRL); - if (val & PCI_SRIOV_CTRL_VFE) { - val &= ~PCI_SRIOV_CTRL_VFE; - pcie_sriov_config_write(dev, sriov_cap + PCI_SRIOV_CTRL, val, 1); - } + if (!sriov_cap) { + return; + } + + pci_set_word(dev->config + sriov_cap + PCI_SRIOV_CTRL, 0); + unregister_vfs(dev); + + pci_set_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF, 0); + + /* + * Default is to use 4K pages, software can modify it + * to any of the supported bits + */ + pci_set_word(dev->config + sriov_cap + PCI_SRIOV_SYS_PGSIZE, 0x1); + + for (uint16_t i = 0; i < PCI_NUM_REGIONS; i++) { + pci_set_quad(dev->config + sriov_cap + PCI_SRIOV_BAR + i * 4, + dev->exp.sriov_pf.vf_bar_type[i]); } } @@ -300,3 +311,8 @@ PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n) } return NULL; } + +uint16_t pcie_sriov_num_vfs(PCIDevice *dev) +{ + return dev->exp.sriov_pf.num_vfs; +} diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index f822f18b98..aac6f2d034 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c @@ -8,6 +8,7 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" #include "hw/pci/msi.h" +#include "trace.h" /* TODO: model power only and disabled slot states. */ /* TODO: handle SERR and wakeups */ @@ -123,10 +124,41 @@ #define SHPC_PCI_TO_IDX(pci_slot) ((pci_slot) - 1) #define SHPC_IDX_TO_PHYSICAL(slot) ((slot) + 1) -static uint16_t shpc_get_status(SHPCDevice *shpc, int slot, uint16_t msk) +static const char *shpc_led_state_to_str(uint8_t value) +{ + switch (value) { + case SHPC_LED_ON: + return "on"; + case SHPC_LED_BLINK: + return "blink"; + case SHPC_LED_OFF: + return "off"; + default: + return "invalid"; + } +} + +static const char *shpc_slot_state_to_str(uint8_t value) +{ + switch (value) { + case SHPC_STATE_PWRONLY: + return "power-only"; + case SHPC_STATE_ENABLED: + return "enabled"; + case SHPC_STATE_DISABLED: + return "disabled"; + default: + return "invalid"; + } +} + +static uint8_t shpc_get_status(SHPCDevice *shpc, int slot, uint16_t msk) { uint8_t *status = shpc->config + SHPC_SLOT_STATUS(slot); - return (pci_get_word(status) & msk) >> ctz32(msk); + uint16_t result = (pci_get_word(status) & msk) >> ctz32(msk); + + assert(result <= UINT8_MAX); + return result; } static void shpc_set_status(SHPCDevice *shpc, @@ -223,6 +255,7 @@ void shpc_reset(PCIDevice *d) SHPC_SLOT_STATUS_PRSNT_MASK); shpc_set_status(shpc, i, SHPC_LED_OFF, SHPC_SLOT_PWR_LED_MASK); } + shpc_set_status(shpc, i, SHPC_LED_OFF, SHPC_SLOT_ATTN_LED_MASK); shpc_set_status(shpc, i, 0, SHPC_SLOT_STATUS_66); } shpc_set_sec_bus_speed(shpc, SHPC_SEC_BUS_33); @@ -254,60 +287,83 @@ static void shpc_free_devices_in_slot(SHPCDevice *shpc, int slot) } } -static void shpc_slot_command(SHPCDevice *shpc, uint8_t target, +static bool shpc_slot_is_off(uint8_t state, uint8_t power, uint8_t attn) +{ + return state == SHPC_STATE_DISABLED && power == SHPC_LED_OFF; +} + +static void shpc_slot_command(PCIDevice *d, uint8_t target, uint8_t state, uint8_t power, uint8_t attn) { - uint8_t current_state; + SHPCDevice *shpc = d->shpc; int slot = SHPC_LOGICAL_TO_IDX(target); + uint8_t old_state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK); + uint8_t old_power = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK); + uint8_t old_attn = shpc_get_status(shpc, slot, SHPC_SLOT_ATTN_LED_MASK); + if (target < SHPC_CMD_TRGT_MIN || slot >= shpc->nslots) { shpc_invalid_command(shpc); return; } - current_state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK); - if (current_state == SHPC_STATE_ENABLED && state == SHPC_STATE_PWRONLY) { + + if (old_state == SHPC_STATE_ENABLED && state == SHPC_STATE_PWRONLY) { shpc_invalid_command(shpc); return; } - switch (power) { - case SHPC_LED_NO: - break; - default: + if (power == SHPC_LED_NO) { + power = old_power; + } else { /* TODO: send event to monitor */ shpc_set_status(shpc, slot, power, SHPC_SLOT_PWR_LED_MASK); } - switch (attn) { - case SHPC_LED_NO: - break; - default: + + if (attn == SHPC_LED_NO) { + attn = old_attn; + } else { /* TODO: send event to monitor */ shpc_set_status(shpc, slot, attn, SHPC_SLOT_ATTN_LED_MASK); } - if ((current_state == SHPC_STATE_DISABLED && state == SHPC_STATE_PWRONLY) || - (current_state == SHPC_STATE_DISABLED && state == SHPC_STATE_ENABLED)) { + if (state == SHPC_STATE_NO) { + state = old_state; + } else { shpc_set_status(shpc, slot, state, SHPC_SLOT_STATE_MASK); - } else if ((current_state == SHPC_STATE_ENABLED || - current_state == SHPC_STATE_PWRONLY) && - state == SHPC_STATE_DISABLED) { - shpc_set_status(shpc, slot, state, SHPC_SLOT_STATE_MASK); - power = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK); - /* TODO: track what monitor requested. */ - /* Look at LED to figure out whether it's ok to remove the device. */ - if (power == SHPC_LED_OFF) { - shpc_free_devices_in_slot(shpc, slot); - shpc_set_status(shpc, slot, 1, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_EMPTY, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_PRESENCE; - } + } + + if (trace_event_get_state_backends(TRACE_SHPC_SLOT_COMMAND)) { + DeviceState *parent = DEVICE(d); + int pci_slot = SHPC_IDX_TO_PCI(slot); + DeviceState *child = + DEVICE(shpc->sec_bus->devices[PCI_DEVFN(pci_slot, 0)]); + + trace_shpc_slot_command( + parent->canonical_path, pci_slot, + child ? child->canonical_path : "no-child", + shpc_led_state_to_str(old_power), + shpc_led_state_to_str(power), + shpc_led_state_to_str(old_attn), + shpc_led_state_to_str(attn), + shpc_slot_state_to_str(old_state), + shpc_slot_state_to_str(state)); + } + + if (!shpc_slot_is_off(old_state, old_power, old_attn) && + shpc_slot_is_off(state, power, attn)) + { + shpc_free_devices_in_slot(shpc, slot); + shpc_set_status(shpc, slot, 1, SHPC_SLOT_STATUS_MRL_OPEN); + shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_EMPTY, + SHPC_SLOT_STATUS_PRSNT_MASK); + shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= + SHPC_SLOT_EVENT_MRL | + SHPC_SLOT_EVENT_PRESENCE; } } -static void shpc_command(SHPCDevice *shpc) +static void shpc_command(PCIDevice *d) { + SHPCDevice *shpc = d->shpc; uint8_t code = pci_get_byte(shpc->config + SHPC_CMD_CODE); uint8_t speed; uint8_t target; @@ -328,7 +384,7 @@ static void shpc_command(SHPCDevice *shpc) state = (code & SHPC_SLOT_STATE_MASK) >> SHPC_SLOT_STATE_SHIFT; power = (code & SHPC_SLOT_PWR_LED_MASK) >> SHPC_SLOT_PWR_LED_SHIFT; attn = (code & SHPC_SLOT_ATTN_LED_MASK) >> SHPC_SLOT_ATTN_LED_SHIFT; - shpc_slot_command(shpc, target, state, power, attn); + shpc_slot_command(d, target, state, power, attn); break; case 0x40 ... 0x47: speed = code & SHPC_SEC_BUS_MASK; @@ -346,10 +402,10 @@ static void shpc_command(SHPCDevice *shpc) } for (i = 0; i < shpc->nslots; ++i) { if (!(shpc_get_status(shpc, i, SHPC_SLOT_STATUS_MRL_OPEN))) { - shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, + shpc_slot_command(d, i + SHPC_CMD_TRGT_MIN, SHPC_STATE_PWRONLY, SHPC_LED_ON, SHPC_LED_NO); } else { - shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, + shpc_slot_command(d, i + SHPC_CMD_TRGT_MIN, SHPC_STATE_NO, SHPC_LED_OFF, SHPC_LED_NO); } } @@ -367,10 +423,10 @@ static void shpc_command(SHPCDevice *shpc) } for (i = 0; i < shpc->nslots; ++i) { if (!(shpc_get_status(shpc, i, SHPC_SLOT_STATUS_MRL_OPEN))) { - shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, + shpc_slot_command(d, i + SHPC_CMD_TRGT_MIN, SHPC_STATE_ENABLED, SHPC_LED_ON, SHPC_LED_NO); } else { - shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, + shpc_slot_command(d, i + SHPC_CMD_TRGT_MIN, SHPC_STATE_NO, SHPC_LED_OFF, SHPC_LED_NO); } } @@ -402,7 +458,7 @@ static void shpc_write(PCIDevice *d, unsigned addr, uint64_t val, int l) shpc->config[a] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */ } if (ranges_overlap(addr, l, SHPC_CMD_CODE, 2)) { - shpc_command(shpc); + shpc_command(d); } shpc_interrupt_update(d); } @@ -480,13 +536,15 @@ static const MemoryRegionOps shpc_mmio_ops = { .endianness = DEVICE_LITTLE_ENDIAN, .valid = { /* SHPC ECN requires dword accesses, but the original 1.0 spec doesn't. - * It's easier to suppport all sizes than worry about it. */ + * It's easier to support all sizes than worry about it. + */ .min_access_size = 1, .max_access_size = 4, }, }; -static void shpc_device_plug_common(PCIDevice *affected_dev, int *slot, - SHPCDevice *shpc, Error **errp) + +static bool shpc_device_get_slot(PCIDevice *affected_dev, int *slot, + SHPCDevice *shpc, Error **errp) { int pci_slot = PCI_SLOT(affected_dev->devfn); *slot = SHPC_PCI_TO_IDX(pci_slot); @@ -496,21 +554,20 @@ static void shpc_device_plug_common(PCIDevice *affected_dev, int *slot, "controller. Valid slots are between %d and %d.", pci_slot, SHPC_IDX_TO_PCI(0), SHPC_IDX_TO_PCI(shpc->nslots) - 1); - return; + return false; } + + return true; } void shpc_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - Error *local_err = NULL; PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); SHPCDevice *shpc = pci_hotplug_dev->shpc; int slot; - shpc_device_plug_common(PCI_DEVICE(dev), &slot, shpc, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!shpc_device_get_slot(PCI_DEVICE(dev), &slot, shpc, errp)) { return; } @@ -552,21 +609,25 @@ void shpc_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, void shpc_device_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - Error *local_err = NULL; PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); SHPCDevice *shpc = pci_hotplug_dev->shpc; uint8_t state; uint8_t led; int slot; - shpc_device_plug_common(PCI_DEVICE(dev), &slot, shpc, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!shpc_device_get_slot(PCI_DEVICE(dev), &slot, shpc, errp)) { return; } state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK); led = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK); + + if (led == SHPC_LED_BLINK) { + error_setg(errp, "Hot-unplug failed: " + "guest is busy (power indicator blinking)"); + return; + } + if (state == SHPC_STATE_DISABLED && led == SHPC_LED_OFF) { shpc_free_devices_in_slot(shpc, slot); shpc_set_status(shpc, slot, 1, SHPC_SLOT_STATUS_MRL_OPEN); @@ -600,7 +661,7 @@ int shpc_init(PCIDevice *d, PCIBus *sec_bus, MemoryRegion *bar, } if (nslots > SHPC_MAX_SLOTS || SHPC_IDX_TO_PCI(nslots) > PCI_SLOT_MAX) { - /* TODO: report an error mesage that makes sense. */ + /* TODO: report an error message that makes sense. */ return -EINVAL; } shpc->nslots = nslots; @@ -721,7 +782,7 @@ static int shpc_load(QEMUFile *f, void *pv, size_t size, return 0; } -VMStateInfo shpc_vmstate_info = { +const VMStateInfo shpc_vmstate_info = { .name = "shpc", .get = shpc_load, .put = shpc_save, diff --git a/hw/pci/slotid_cap.c b/hw/pci/slotid_cap.c index 36d021b4a6..8372d05d9e 100644 --- a/hw/pci/slotid_cap.c +++ b/hw/pci/slotid_cap.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" #include "hw/pci/slotid_cap.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "qemu/error-report.h" #include "qapi/error.h" diff --git a/hw/pci/trace-events b/hw/pci/trace-events index aaf46bc92d..19643aa8c6 100644 --- a/hw/pci/trace-events +++ b/hw/pci/trace-events @@ -3,6 +3,7 @@ # pci.c pci_update_mappings_del(const char *dev, uint32_t bus, uint32_t slot, uint32_t func, int bar, uint64_t addr, uint64_t size) "%s %02x:%02x.%x %d,0x%"PRIx64"+0x%"PRIx64 pci_update_mappings_add(const char *dev, uint32_t bus, uint32_t slot, uint32_t func, int bar, uint64_t addr, uint64_t size) "%s %02x:%02x.%x %d,0x%"PRIx64"+0x%"PRIx64 +pci_route_irq(int dev_irq, const char *dev_path, int parent_irq, const char *parent_path) "IRQ %d @%s -> IRQ %d @%s" # pci_host.c pci_cfg_read(const char *dev, uint32_t bus, uint32_t slot, uint32_t func, unsigned offs, unsigned val) "%s %02x:%02x.%x @0x%x -> 0x%x" @@ -15,3 +16,9 @@ msix_write_config(char *name, bool enabled, bool masked) "dev %s enabled %d mask sriov_register_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: creating %d vf devs" sriov_unregister_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: Unregistering %d vf devs" sriov_config_write(const char *name, int slot, int fun, uint32_t offset, uint32_t val, uint32_t len) "%s %02x:%x: sriov offset 0x%x val 0x%x len %d" + +# pcie.c +pcie_cap_slot_write_config(const char *parent, const char *child, const char *pds, const char *old_pic, const char *new_pic, const char *old_aic, const char *new_aic, const char *old_power, const char *new_power) "%s > %s: pds: %s, pic: %s->%s, aic: %s->%s, power: %s->%s" + +# shpc.c +shpc_slot_command(const char *parent, int pci_slot, const char *child, const char *old_pic, const char *new_pic, const char *old_aic, const char *new_aic, const char *old_state, const char *new_state) "%s[%d] > %s: pic: %s->%s, aic: %s->%s, state: %s->%s" diff --git a/hw/pcmcia/meson.build b/hw/pcmcia/meson.build index 51f2512b8e..04e29c109c 100644 --- a/hw/pcmcia/meson.build +++ b/hw/pcmcia/meson.build @@ -1,2 +1,2 @@ -softmmu_ss.add(when: 'CONFIG_PCMCIA', if_true: files('pcmcia.c')) -softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx.c')) +system_ss.add(when: 'CONFIG_PCMCIA', if_true: files('pcmcia.c')) +system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx.c')) diff --git a/hw/pcmcia/pxa2xx.c b/hw/pcmcia/pxa2xx.c index fcca7e571b..e3111fdf1a 100644 --- a/hw/pcmcia/pxa2xx.c +++ b/hw/pcmcia/pxa2xx.c @@ -138,21 +138,6 @@ static void pxa2xx_pcmcia_set_irq(void *opaque, int line, int level) qemu_set_irq(s->irq, level); } -PXA2xxPCMCIAState *pxa2xx_pcmcia_init(MemoryRegion *sysmem, - hwaddr base) -{ - DeviceState *dev; - PXA2xxPCMCIAState *s; - - dev = qdev_new(TYPE_PXA2XX_PCMCIA); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - s = PXA2XX_PCMCIA(dev); - - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - - return s; -} - static void pxa2xx_pcmcia_initfn(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index 400511c6b7..37ccf9cdca 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -1,13 +1,14 @@ config PSERIES bool + imply USB_OHCI_PCI imply PCI_DEVICES imply TEST_DEVICES imply VIRTIO_VGA - imply NVDIMM + imply VFIO_PCI if LINUX # needed by spapr_pci_vfio.c + select NVDIMM select DIMM select PCI select SPAPR_VSCSI - select VFIO if LINUX # needed by spapr_pci_vfio.c select XICS select XIVE select MSI_NONBROKEN @@ -32,6 +33,8 @@ config POWERNV select XIVE select FDT_PPC select PCI_POWERNV + select PCA9552 + select PCA9554 config PPC405 bool @@ -46,6 +49,7 @@ config PPC440 imply TEST_DEVICES imply E1000_PCI select PCI_EXPRESS + select PPC440_PCIX select PPC4XX select SERIAL select FDT_PPC @@ -53,12 +57,11 @@ config PPC440 config PPC4XX bool select BITBANG_I2C - select PCI + select PPC4XX_PCI select PPC_UIC config SAM460EX bool - select PPC405 select PFLASH_CFI01 select IDE_SII3112 select M41T80 @@ -67,19 +70,23 @@ config SAM460EX select SM501 select SMBUS_EEPROM select USB_EHCI_SYSBUS - select USB_OHCI + select USB_OHCI_SYSBUS select FDT_PPC +config AMIGAONE + bool + imply ATI_VGA + select ARTICIA + select VT82C686 + select SMBUS_EEPROM + config PEGASOS2 bool + imply ATI_VGA select MV64361 select VT82C686 - select IDE_VIA select SMBUS_EEPROM select VOF -# This should come with VT82C686 - select ACPI_X86 - imply ATI_VGA config PREP bool @@ -113,6 +120,7 @@ config MAC_NEWWORLD imply PCI_DEVICES imply SUNGEM imply TEST_DEVICES + imply USB_OHCI_PCI select ADB select MACIO select MACIO_GPIO @@ -125,13 +133,25 @@ config E500 imply AT24C imply VIRTIO_PCI select ETSEC + select GPIO_MPC8XXX select OPENPIC + select PFLASH_CFI01 select PLATFORM_BUS select PPCE500_PCI + select SDHCI select SERIAL select MPC_I2C select FDT_PPC select DS1338 + select UNIMP + +config E500PLAT + bool + select E500 + +config MPC8544DS + bool + select E500 config VIRTEX bool diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c new file mode 100644 index 0000000000..ddfa09457a --- /dev/null +++ b/hw/ppc/amigaone.c @@ -0,0 +1,177 @@ +/* + * QEMU Eyetech AmigaOne/Mai Logic Teron emulation + * + * Copyright (c) 2023 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/datadir.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/ppc/ppc.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/pci-host/articia.h" +#include "hw/isa/vt82c686.h" +#include "hw/ide/pci.h" +#include "hw/i2c/smbus_eeprom.h" +#include "hw/ppc/ppc.h" +#include "sysemu/qtest.h" +#include "sysemu/reset.h" +#include "kvm_ppc.h" + +#define BUS_FREQ_HZ 100000000 + +/* + * Firmware binary available at + * https://www.hyperion-entertainment.com/index.php/downloads?view=files&parent=28 + * then "tail -c 524288 updater.image >u-boot-amigaone.bin" + * + * BIOS emulator in firmware cannot run QEMU vgabios and hangs on it, use + * -device VGA,romfile=VGABIOS-lgpl-latest.bin + * from http://www.nongnu.org/vgabios/ instead. + */ +#define PROM_ADDR 0xfff00000 +#define PROM_SIZE (512 * KiB) + +/* AmigaOS calls this routine from ROM, use this if no firmware loaded */ +static const char dummy_fw[] = { + 0x38, 0x00, 0x00, 0x08, /* li r0,8 */ + 0x7c, 0x09, 0x03, 0xa6, /* mtctr r0 */ + 0x54, 0x63, 0xf8, 0x7e, /* srwi r3,r3,1 */ + 0x42, 0x00, 0xff, 0xfc, /* bdnz 0x8 */ + 0x7c, 0x63, 0x18, 0xf8, /* not r3,r3 */ + 0x4e, 0x80, 0x00, 0x20, /* blr */ +}; + +static void amigaone_cpu_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + + cpu_reset(CPU(cpu)); + cpu_ppc_tb_reset(&cpu->env); +} + +static void fix_spd_data(uint8_t *spd) +{ + uint32_t bank_size = 4 * MiB * spd[31]; + uint32_t rows = bank_size / spd[13] / spd[17]; + spd[3] = ctz32(rows) - spd[4]; +} + +static void amigaone_init(MachineState *machine) +{ + PowerPCCPU *cpu; + CPUPPCState *env; + MemoryRegion *rom, *pci_mem, *mr; + ssize_t sz; + PCIBus *pci_bus; + Object *via; + DeviceState *dev; + I2CBus *i2c_bus; + uint8_t *spd_data; + int i; + + /* init CPU */ + cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); + env = &cpu->env; + if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { + error_report("Incompatible CPU, only 6xx bus supported"); + exit(1); + } + cpu_ppc_tb_init(env, BUS_FREQ_HZ / 4); + qemu_register_reset(amigaone_cpu_reset, cpu); + + /* RAM */ + if (machine->ram_size > 2 * GiB) { + error_report("RAM size more than 2 GiB is not supported"); + exit(1); + } + memory_region_add_subregion(get_system_memory(), 0, machine->ram); + if (machine->ram_size < 1 * GiB + 32 * KiB) { + /* Firmware uses this area for startup */ + mr = g_new(MemoryRegion, 1); + memory_region_init_ram(mr, NULL, "init-cache", 32 * KiB, &error_fatal); + memory_region_add_subregion(get_system_memory(), 0x40000000, mr); + } + + /* allocate and load firmware */ + rom = g_new(MemoryRegion, 1); + memory_region_init_rom(rom, NULL, "rom", PROM_SIZE, &error_fatal); + memory_region_add_subregion(get_system_memory(), PROM_ADDR, rom); + if (!machine->firmware) { + rom_add_blob_fixed("dummy-fw", dummy_fw, sizeof(dummy_fw), + PROM_ADDR + PROM_SIZE - 0x80); + } else { + g_autofree char *filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, + machine->firmware); + if (!filename) { + error_report("Could not find firmware '%s'", machine->firmware); + exit(1); + } + sz = load_image_targphys(filename, PROM_ADDR, PROM_SIZE); + if (sz <= 0 || sz > PROM_SIZE) { + error_report("Could not load firmware '%s'", filename); + exit(1); + } + } + + /* Articia S */ + dev = sysbus_create_simple(TYPE_ARTICIA, 0xfe000000, NULL); + + i2c_bus = I2C_BUS(qdev_get_child_bus(dev, "smbus")); + if (machine->ram_size > 512 * MiB) { + spd_data = spd_data_generate(SDR, machine->ram_size / 2); + } else { + spd_data = spd_data_generate(SDR, machine->ram_size); + } + fix_spd_data(spd_data); + smbus_eeprom_init_one(i2c_bus, 0x51, spd_data); + if (machine->ram_size > 512 * MiB) { + smbus_eeprom_init_one(i2c_bus, 0x52, spd_data); + } + + pci_mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); + mr = g_new(MemoryRegion, 1); + memory_region_init_alias(mr, OBJECT(dev), "pci-mem-low", pci_mem, + 0, 0x1000000); + memory_region_add_subregion(get_system_memory(), 0xfd000000, mr); + mr = g_new(MemoryRegion, 1); + memory_region_init_alias(mr, OBJECT(dev), "pci-mem-high", pci_mem, + 0x80000000, 0x7d000000); + memory_region_add_subregion(get_system_memory(), 0x80000000, mr); + pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0")); + + /* VIA VT82c686B South Bridge (multifunction PCI device) */ + via = OBJECT(pci_create_simple_multifunction(pci_bus, PCI_DEVFN(7, 0), + TYPE_VT82C686B_ISA)); + object_property_add_alias(OBJECT(machine), "rtc-time", + object_resolve_path_component(via, "rtc"), + "date"); + qdev_connect_gpio_out(DEVICE(via), 0, + qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_INT)); + for (i = 0; i < PCI_NUM_PINS; i++) { + qdev_connect_gpio_out(dev, i, qdev_get_gpio_in_named(DEVICE(via), + "pirq", i)); + } + pci_ide_create_devs(PCI_DEVICE(object_resolve_path_component(via, "ide"))); + pci_vga_init(pci_bus); +} + +static void amigaone_machine_init(MachineClass *mc) +{ + mc->desc = "Eyetech AmigaOne/Mai Logic Teron"; + mc->init = amigaone_init; + mc->block_default_type = IF_IDE; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7457_v1.2"); + mc->default_display = "std"; + mc->default_ram_id = "ram"; + mc->default_ram_size = 512 * MiB; +} + +DEFINE_MACHINE("amigaone", amigaone_machine_init) diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 7f7f5b3452..3bd12b54ab 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -17,13 +17,16 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" #include "qemu/units.h" +#include "qemu/guest-random.h" #include "qapi/error.h" #include "e500.h" #include "e500-ccsr.h" #include "net/net.h" #include "qemu/config-file.h" +#include "hw/block/flash.h" #include "hw/char/serial.h" #include "hw/pci/pci.h" +#include "sysemu/block-backend-io.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "sysemu/reset.h" @@ -45,6 +48,8 @@ #include "hw/net/fsl_etsec/etsec.h" #include "hw/i2c/i2c.h" #include "hw/irq.h" +#include "hw/sd/sdhci.h" +#include "hw/misc/unimp.h" #define EPAPR_MAGIC (0x45504150) #define DTC_LOAD_PAD 0x1800000 @@ -63,11 +68,14 @@ #define MPC8544_SERIAL1_REGS_OFFSET 0x4600ULL #define MPC8544_PCI_REGS_OFFSET 0x8000ULL #define MPC8544_PCI_REGS_SIZE 0x1000ULL +#define MPC85XX_ESDHC_REGS_OFFSET 0x2e000ULL +#define MPC85XX_ESDHC_REGS_SIZE 0x1000ULL #define MPC8544_UTIL_OFFSET 0xe0000ULL #define MPC8XXX_GPIO_OFFSET 0x000FF000ULL #define MPC8544_I2C_REGS_OFFSET 0x3000ULL #define MPC8XXX_GPIO_IRQ 47 #define MPC8544_I2C_IRQ 43 +#define MPC85XX_ESDHC_IRQ 72 #define RTC_REGS_OFFSET 0x68 #define PLATFORM_CLK_FREQ_HZ (400 * 1000 * 1000) @@ -200,6 +208,22 @@ static void dt_i2c_create(void *fdt, const char *soc, const char *mpic, g_free(i2c); } +static void dt_sdhc_create(void *fdt, const char *parent, const char *mpic) +{ + hwaddr mmio = MPC85XX_ESDHC_REGS_OFFSET; + hwaddr size = MPC85XX_ESDHC_REGS_SIZE; + int irq = MPC85XX_ESDHC_IRQ; + g_autofree char *name = NULL; + + name = g_strdup_printf("%s/sdhc@%" PRIx64, parent, mmio); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop(fdt, name, "sdhci,auto-cmd12", NULL, 0); + qemu_fdt_setprop_phandle(fdt, name, "interrupt-parent", mpic); + qemu_fdt_setprop_cells(fdt, name, "bus-width", 4); + qemu_fdt_setprop_cells(fdt, name, "interrupts", irq, 0x2); + qemu_fdt_setprop_cells(fdt, name, "reg", mmio, size); + qemu_fdt_setprop_string(fdt, name, "compatible", "fsl,esdhc"); +} typedef struct PlatformDevtreeData { void *fdt; @@ -217,7 +241,7 @@ static int create_devtree_etsec(SysBusDevice *sbdev, PlatformDevtreeData *data) int irq0 = platform_bus_get_irqn(pbus, sbdev, 0); int irq1 = platform_bus_get_irqn(pbus, sbdev, 1); int irq2 = platform_bus_get_irqn(pbus, sbdev, 2); - gchar *node = g_strdup_printf("/platform/ethernet@%"PRIx64, mmio0); + gchar *node = g_strdup_printf("%s/ethernet@%"PRIx64, data->node, mmio0); gchar *group = g_strdup_printf("%s/queue-group", node); void *fdt = data->fdt; @@ -266,6 +290,31 @@ static void sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque) } } +static void create_devtree_flash(SysBusDevice *sbdev, + PlatformDevtreeData *data) +{ + g_autofree char *name = NULL; + uint64_t num_blocks = object_property_get_uint(OBJECT(sbdev), + "num-blocks", + &error_fatal); + uint64_t sector_length = object_property_get_uint(OBJECT(sbdev), + "sector-length", + &error_fatal); + uint64_t bank_width = object_property_get_uint(OBJECT(sbdev), + "width", + &error_fatal); + hwaddr flashbase = 0; + hwaddr flashsize = num_blocks * sector_length; + void *fdt = data->fdt; + + name = g_strdup_printf("%s/nor@%" PRIx64, data->node, flashbase); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(fdt, name, "reg", + 1, flashbase, 1, flashsize); + qemu_fdt_setprop_cell(fdt, name, "bank-width", bank_width); +} + static void platform_bus_create_devtree(PPCE500MachineState *pms, void *fdt, const char *mpic) { @@ -275,6 +324,8 @@ static void platform_bus_create_devtree(PPCE500MachineState *pms, uint64_t addr = pmc->platform_bus_base; uint64_t size = pmc->platform_bus_size; int irq_start = pmc->platform_bus_first_irq; + SysBusDevice *sbdev; + bool ambiguous; /* Create a /platform node that we can put all devices into */ @@ -301,6 +352,13 @@ static void platform_bus_create_devtree(PPCE500MachineState *pms, /* Loop through all dynamic sysbus devices and create nodes for them */ foreach_dynamic_sysbus_device(sysbus_device_create_devtree, &data); + sbdev = SYS_BUS_DEVICE(object_resolve_path_type("", TYPE_PFLASH_CFI01, + &ambiguous)); + if (sbdev) { + assert(!ambiguous); + create_devtree_flash(sbdev, &data); + } + g_free(node); } @@ -315,7 +373,7 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, MachineState *machine = MACHINE(pms); unsigned int smp_cpus = machine->smp.cpus; const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(pms); - CPUPPCState *env = first_cpu->env_ptr; + CPUPPCState *env = cpu_env(first_cpu); int ret = -1; uint64_t mem_reg_property[] = { 0, cpu_to_be64(machine->ram_size) }; int fdt_size; @@ -346,6 +404,7 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, }; const char *dtb_file = machine->dtb; const char *toplevel_compat = machine->dt_compatible; + uint8_t rng_seed[32]; if (dtb_file) { char *filename; @@ -403,6 +462,9 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, if (ret < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); + if (kvm_enabled()) { /* Read out host's frequencies */ clock_freq = kvmppc_get_clockfreq(); @@ -437,7 +499,7 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, if (cpu == NULL) { continue; } - env = cpu->env_ptr; + env = cpu_env(cpu); cpu_name = g_strdup_printf("/cpus/PowerPC,8544@%x", i); qemu_fdt_add_subnode(fdt, cpu_name); @@ -512,6 +574,10 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, dt_rtc_create(fdt, "i2c", "rtc"); + /* sdhc */ + if (pmc->has_esdhc) { + dt_sdhc_create(fdt, soc, mpic); + } gutil = g_strdup_printf("%s/global-utilities@%llx", soc, MPC8544_UTIL_OFFSET); @@ -577,9 +643,8 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, } g_free(soc); - if (pms->pbus_dev) { - platform_bus_create_devtree(pms, fdt, mpic); - } + platform_bus_create_devtree(pms, fdt, mpic); + g_free(mpic); pmc->fixup_devtree(fdt); @@ -593,9 +658,14 @@ done: if (!dry_run) { qemu_fdt_dumpdtb(fdt, fdt_size); cpu_physical_memory_write(addr, fdt, fdt_size); + + /* Set machine->fdt for 'dumpdtb' QMP/HMP command */ + g_free(machine->fdt); + machine->fdt = fdt; + } else { + g_free(fdt); } ret = fdt_size; - g_free(fdt); out: g_free(pci_map); @@ -642,7 +712,7 @@ static int ppce500_prep_device_tree(PPCE500MachineState *machine, p->kernel_base = kernel_base; p->kernel_size = kernel_size; - qemu_register_reset(ppce500_reset_device_tree, p); + qemu_register_reset_nosnapshotload(ppce500_reset_device_tree, p); p->notifier.notify = ppce500_init_notify; qemu_add_machine_init_done_notifier(&p->notifier); @@ -651,7 +721,6 @@ static int ppce500_prep_device_tree(PPCE500MachineState *machine, kernel_base, kernel_size, true); } -/* Create -kernel TLB entries for BookE. */ hwaddr booke206_page_size_to_tlb(uint64_t size) { return 63 - clz64(size / KiB); @@ -682,6 +751,7 @@ static uint64_t mmubooke_initial_mapsize(CPUPPCState *env) return (1ULL << 10 << tsize); } +/* Create -kernel TLB entries for BookE. */ static void mmubooke_create_initial_mapping(CPUPPCState *env) { ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 0); @@ -695,7 +765,9 @@ static void mmubooke_create_initial_mapping(CPUPPCState *env) tlb->mas7_3 = 0; tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; +#ifdef CONFIG_KVM env->tlb_dirty = true; +#endif } static void ppce500_cpu_reset_sec(void *opaque) @@ -762,6 +834,7 @@ static DeviceState *ppce500_init_mpic_qemu(PPCE500MachineState *pms, static DeviceState *ppce500_init_mpic_kvm(const PPCE500MachineClass *pmc, IrqLines *irqs, Error **errp) { +#ifdef CONFIG_KVM DeviceState *dev; CPUState *cs; @@ -782,6 +855,9 @@ static DeviceState *ppce500_init_mpic_kvm(const PPCE500MachineClass *pmc, } return dev; +#else + g_assert_not_reached(); +#endif } static DeviceState *ppce500_init_mpic(PPCE500MachineState *pms, @@ -828,6 +904,7 @@ void ppce500_init(MachineState *machine) MemoryRegion *address_space_mem = get_system_memory(); PPCE500MachineState *pms = PPCE500_MACHINE(machine); const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(machine); + MachineClass *mc = MACHINE_CLASS(pmc); PCIBus *pci_bus; CPUPPCState *env = NULL; uint64_t loadaddr; @@ -842,7 +919,7 @@ void ppce500_init(MachineState *machine) bool kernel_as_payload; hwaddr bios_entry = 0; target_long payload_size; - struct boot_info *boot_info; + struct boot_info *boot_info = NULL; int dt_size; int i; unsigned int smp_cpus = machine->smp.cpus; @@ -851,6 +928,7 @@ void ppce500_init(MachineState *machine) unsigned int pci_irq_nrs[PCI_NUM_PINS] = {1, 2, 3, 4}; IrqLines *irqs; DeviceState *dev, *mpicdev; + DriveInfo *dinfo; CPUPPCState *firstenv = NULL; MemoryRegion *ccsr_addr_space; SysBusDevice *s; @@ -861,7 +939,6 @@ void ppce500_init(MachineState *machine) for (i = 0; i < smp_cpus; i++) { PowerPCCPU *cpu; CPUState *cs; - qemu_irq *input; cpu = POWERPC_CPU(object_new(machine->cpu_type)); env = &cpu->env; @@ -878,16 +955,17 @@ void ppce500_init(MachineState *machine) * when implementing non-kernel boot. */ object_property_set_bool(OBJECT(cs), "start-powered-off", i != 0, - &error_fatal); + &error_abort); qdev_realize_and_unref(DEVICE(cs), NULL, &error_fatal); if (!firstenv) { firstenv = env; } - input = (qemu_irq *)env->irq_inputs; - irqs[i].irq[OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; - irqs[i].irq[OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; + irqs[i].irq[OPENPIC_OUTPUT_INT] = + qdev_get_gpio_in(DEVICE(cpu), PPCE500_INPUT_INT); + irqs[i].irq[OPENPIC_OUTPUT_CINT] = + qdev_get_gpio_in(DEVICE(cpu), PPCE500_INPUT_CINT); env->spr_cb[SPR_BOOKE_PIR].default_value = cs->cpu_index = i; env->mpic_iack = pmc->ccsrbar_base + MPC8544_MPIC_REGS_OFFSET + 0xa0; @@ -896,7 +974,6 @@ void ppce500_init(MachineState *machine) /* Register reset handler */ if (!i) { /* Primary CPU */ - struct boot_info *boot_info; boot_info = g_new0(struct boot_info, 1); qemu_register_reset(ppce500_cpu_reset, cpu); env->load_info = boot_info; @@ -917,8 +994,7 @@ void ppce500_init(MachineState *machine) memory_region_add_subregion(address_space_mem, 0, machine->ram); dev = qdev_new("e500-ccsr"); - object_property_add_child(qdev_get_machine(), "e500-ccsr", - OBJECT(dev)); + object_property_add_child(OBJECT(machine), "e500-ccsr", OBJECT(dev)); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); ccsr = CCSR(dev); ccsr_addr_space = &ccsr->ccsr_space; @@ -940,7 +1016,8 @@ void ppce500_init(MachineState *machine) 0, qdev_get_gpio_in(mpicdev, 42), 399193, serial_hd(1), DEVICE_BIG_ENDIAN); } - /* I2C */ + + /* I2C */ dev = qdev_new("mpc-i2c"); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); @@ -950,6 +1027,30 @@ void ppce500_init(MachineState *machine) i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); i2c_slave_create_simple(i2c, "ds1338", RTC_REGS_OFFSET); + /* eSDHC */ + if (pmc->has_esdhc) { + dev = qdev_new(TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(dev, "name", "esdhc"); + qdev_prop_set_uint64(dev, "size", MPC85XX_ESDHC_REGS_SIZE); + s = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(s, &error_fatal); + memory_region_add_subregion(ccsr_addr_space, MPC85XX_ESDHC_REGS_OFFSET, + sysbus_mmio_get_region(s, 0)); + + /* + * Compatible with: + * - SD Host Controller Specification Version 2.0 Part A2 + * (See MPC8569E Reference Manual) + */ + dev = qdev_new(TYPE_SYSBUS_SDHCI); + qdev_prop_set_uint8(dev, "sd-spec-version", 2); + qdev_prop_set_uint8(dev, "endianness", DEVICE_BIG_ENDIAN); + s = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(s, &error_fatal); + sysbus_connect_irq(s, 0, qdev_get_gpio_in(mpicdev, MPC85XX_ESDHC_IRQ)); + memory_region_add_subregion(ccsr_addr_space, MPC85XX_ESDHC_REGS_OFFSET, + sysbus_mmio_get_region(s, 0)); + } /* General Utility device */ dev = qdev_new("mpc8544-guts"); @@ -960,7 +1061,7 @@ void ppce500_init(MachineState *machine) /* PCI */ dev = qdev_new("e500-pcihost"); - object_property_add_child(qdev_get_machine(), "pci-host", OBJECT(dev)); + object_property_add_child(OBJECT(machine), "pci-host", OBJECT(dev)); qdev_prop_set_uint32(dev, "first_slot", pmc->pci_first_slot); qdev_prop_set_uint32(dev, "first_pin_irq", pci_irq_nrs[0]); s = SYS_BUS_DEVICE(dev); @@ -978,9 +1079,7 @@ void ppce500_init(MachineState *machine) if (pci_bus) { /* Register network interfaces. */ - for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci_bus, "virtio-net-pci", NULL); - } + pci_init_nic_devices(pci_bus, mc->default_nic); } /* Register spinning region */ @@ -1002,23 +1101,63 @@ void ppce500_init(MachineState *machine) } /* Platform Bus Device */ - if (pmc->has_platform_bus) { - dev = qdev_new(TYPE_PLATFORM_BUS_DEVICE); - dev->id = g_strdup(TYPE_PLATFORM_BUS_DEVICE); - qdev_prop_set_uint32(dev, "num_irqs", pmc->platform_bus_num_irqs); - qdev_prop_set_uint32(dev, "mmio_size", pmc->platform_bus_size); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - pms->pbus_dev = PLATFORM_BUS_DEVICE(dev); + dev = qdev_new(TYPE_PLATFORM_BUS_DEVICE); + dev->id = g_strdup(TYPE_PLATFORM_BUS_DEVICE); + qdev_prop_set_uint32(dev, "num_irqs", pmc->platform_bus_num_irqs); + qdev_prop_set_uint32(dev, "mmio_size", pmc->platform_bus_size); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + pms->pbus_dev = PLATFORM_BUS_DEVICE(dev); - s = SYS_BUS_DEVICE(pms->pbus_dev); - for (i = 0; i < pmc->platform_bus_num_irqs; i++) { - int irqn = pmc->platform_bus_first_irq + i; - sysbus_connect_irq(s, i, qdev_get_gpio_in(mpicdev, irqn)); + s = SYS_BUS_DEVICE(pms->pbus_dev); + for (i = 0; i < pmc->platform_bus_num_irqs; i++) { + int irqn = pmc->platform_bus_first_irq + i; + sysbus_connect_irq(s, i, qdev_get_gpio_in(mpicdev, irqn)); + } + + memory_region_add_subregion(address_space_mem, + pmc->platform_bus_base, + &pms->pbus_dev->mmio); + + dinfo = drive_get(IF_PFLASH, 0, 0); + if (dinfo) { + BlockBackend *blk = blk_by_legacy_dinfo(dinfo); + BlockDriverState *bs = blk_bs(blk); + uint64_t mmio_size = memory_region_size(&pms->pbus_dev->mmio); + uint64_t size = bdrv_getlength(bs); + uint32_t sector_len = 64 * KiB; + + if (!is_power_of_2(size)) { + error_report("Size of pflash file must be a power of two."); + exit(1); } - memory_region_add_subregion(address_space_mem, - pmc->platform_bus_base, - sysbus_mmio_get_region(s, 0)); + if (size > mmio_size) { + error_report("Size of pflash file must not be bigger than %" PRIu64 + " bytes.", mmio_size); + exit(1); + } + + if (!QEMU_IS_ALIGNED(size, sector_len)) { + error_report("Size of pflash file must be a multiple of %" PRIu32 + ".", sector_len); + exit(1); + } + + dev = qdev_new(TYPE_PFLASH_CFI01); + qdev_prop_set_drive(dev, "drive", blk); + qdev_prop_set_uint32(dev, "num-blocks", size / sector_len); + qdev_prop_set_uint64(dev, "sector-length", sector_len); + qdev_prop_set_uint8(dev, "width", 2); + qdev_prop_set_bit(dev, "big-endian", true); + qdev_prop_set_uint16(dev, "id0", 0x89); + qdev_prop_set_uint16(dev, "id1", 0x18); + qdev_prop_set_uint16(dev, "id2", 0x0000); + qdev_prop_set_uint16(dev, "id3", 0x0); + qdev_prop_set_string(dev, "name", "e500.flash"); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + memory_region_add_subregion(&pms->pbus_dev->mmio, 0, + pflash_cfi01_get_memory(PFLASH_CFI01(dev))); } /* @@ -1135,7 +1274,6 @@ void ppce500_init(MachineState *machine) } assert(dt_size < DTB_MAX_SIZE); - boot_info = env->load_info; boot_info->entry = bios_entry; boot_info->dt_base = dt_base; boot_info->dt_size = dt_size; diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h index 1e5853b032..8c09ef92e4 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -27,7 +27,7 @@ struct PPCE500MachineClass { int mpic_version; bool has_mpc8xxx_gpio; - bool has_platform_bus; + bool has_esdhc; hwaddr platform_bus_base; hwaddr platform_bus_size; int platform_bus_first_irq; diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index fc911bbb7b..7aa2f2107a 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -46,13 +46,10 @@ static void e500plat_machine_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PPCE500MachineState *pms = PPCE500_MACHINE(hotplug_dev); + MachineClass *mc = MACHINE_GET_CLASS(pms); - if (pms->pbus_dev) { - MachineClass *mc = MACHINE_GET_CLASS(pms); - - if (device_is_dynamic_sysbus(mc, dev)) { - platform_bus_link_device(pms->pbus_dev, SYS_BUS_DEVICE(dev)); - } + if (device_is_dynamic_sysbus(mc, dev)) { + platform_bus_link_device(pms->pbus_dev, SYS_BUS_DEVICE(dev)); } } @@ -86,7 +83,7 @@ static void e500plat_machine_class_init(ObjectClass *oc, void *data) pmc->fixup_devtree = e500plat_fixup_devtree; pmc->mpic_version = OPENPIC_MODEL_FSL_MPIC_42; pmc->has_mpc8xxx_gpio = true; - pmc->has_platform_bus = true; + pmc->has_esdhc = true; pmc->platform_bus_base = 0xf00000000ULL; pmc->platform_bus_size = 128 * MiB; pmc->platform_bus_first_irq = 5; @@ -102,6 +99,7 @@ static void e500plat_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = 32; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30"); mc->default_ram_id = "mpc8544ds.ram"; + mc->default_nic = "virtio-net-pci"; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ETSEC_COMMON); } diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h deleted file mode 100644 index a1fa8f8e41..0000000000 --- a/hw/ppc/mac.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * QEMU PowerMac emulation shared definitions and prototypes - * - * Copyright (c) 2004-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * 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 PPC_MAC_H -#define PPC_MAC_H - -#include "qemu/units.h" -#include "exec/memory.h" -#include "hw/boards.h" -#include "hw/sysbus.h" -#include "hw/input/adb.h" -#include "hw/misc/mos6522.h" -#include "hw/pci/pci_host.h" -#include "hw/pci-host/uninorth.h" -#include "qom/object.h" - -#define NVRAM_SIZE 0x2000 -#define PROM_FILENAME "openbios-ppc" - -#define KERNEL_LOAD_ADDR 0x01000000 -#define KERNEL_GAP 0x00100000 - -#define ESCC_CLOCK 3686400 - -/* Old World IRQs */ -#define OLDWORLD_CUDA_IRQ 0x12 -#define OLDWORLD_ESCCB_IRQ 0x10 -#define OLDWORLD_ESCCA_IRQ 0xf -#define OLDWORLD_IDE0_IRQ 0xd -#define OLDWORLD_IDE0_DMA_IRQ 0x2 -#define OLDWORLD_IDE1_IRQ 0xe -#define OLDWORLD_IDE1_DMA_IRQ 0x3 - -/* New World IRQs */ -#define NEWWORLD_CUDA_IRQ 0x19 -#define NEWWORLD_PMU_IRQ 0x19 -#define NEWWORLD_ESCCB_IRQ 0x24 -#define NEWWORLD_ESCCA_IRQ 0x25 -#define NEWWORLD_IDE0_IRQ 0xd -#define NEWWORLD_IDE0_DMA_IRQ 0x2 -#define NEWWORLD_IDE1_IRQ 0xe -#define NEWWORLD_IDE1_DMA_IRQ 0x3 -#define NEWWORLD_EXTING_GPIO1 0x2f -#define NEWWORLD_EXTING_GPIO9 0x37 - -/* Core99 machine */ -#define TYPE_CORE99_MACHINE MACHINE_TYPE_NAME("mac99") -typedef struct Core99MachineState Core99MachineState; -DECLARE_INSTANCE_CHECKER(Core99MachineState, CORE99_MACHINE, - TYPE_CORE99_MACHINE) - -#define CORE99_VIA_CONFIG_CUDA 0x0 -#define CORE99_VIA_CONFIG_PMU 0x1 -#define CORE99_VIA_CONFIG_PMU_ADB 0x2 - -struct Core99MachineState { - /*< private >*/ - MachineState parent; - - uint8_t via_config; -}; - -/* Grackle PCI */ -#define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" - -/* Mac NVRAM */ -#define TYPE_MACIO_NVRAM "macio-nvram" -OBJECT_DECLARE_SIMPLE_TYPE(MacIONVRAMState, MACIO_NVRAM) - -struct MacIONVRAMState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - uint32_t size; - uint32_t it_shift; - - MemoryRegion mem; - uint8_t *data; -}; - -void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len); -#endif /* PPC_MAC_H */ diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index c865921bdc..ff9e490c4e 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -48,10 +48,13 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/ppc/ppc.h" #include "hw/qdev-properties.h" -#include "hw/ppc/mac.h" +#include "hw/nvram/mac_nvram.h" +#include "hw/boards.h" +#include "hw/pci-host/uninorth.h" #include "hw/input/adb.h" #include "hw/ppc/mac_dbdma.h" #include "hw/pci/pci.h" @@ -74,15 +77,37 @@ #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 -#define TBFREQ (100UL * 1000UL * 1000UL) +#define TBFREQ (25UL * 1000UL * 1000UL) #define CLOCKFREQ (900UL * 1000UL * 1000UL) #define BUSFREQ (100UL * 1000UL * 1000UL) #define NDRV_VGA_FILENAME "qemu_vga.ndrv" +#define PROM_FILENAME "openbios-ppc" #define PROM_BASE 0xfff00000 #define PROM_SIZE (1 * MiB) +#define KERNEL_LOAD_ADDR 0x01000000 +#define KERNEL_GAP 0x00100000 + +#define TYPE_CORE99_MACHINE MACHINE_TYPE_NAME("mac99") +typedef struct Core99MachineState Core99MachineState; +DECLARE_INSTANCE_CHECKER(Core99MachineState, CORE99_MACHINE, + TYPE_CORE99_MACHINE) + +typedef enum { + CORE99_VIA_CONFIG_CUDA = 0, + CORE99_VIA_CONFIG_PMU, + CORE99_VIA_CONFIG_PMU_ADB +} Core99ViaConfig; + +struct Core99MachineState { + /*< private >*/ + MachineState parent; + + Core99ViaConfig via_config; +}; + static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) { @@ -106,45 +131,33 @@ static void ppc_core99_reset(void *opaque) /* PowerPC Mac99 hardware initialisation */ static void ppc_core99_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; - const char *bios_name = machine->firmware ?: PROM_FILENAME; - 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_config.order; Core99MachineState *core99_machine = CORE99_MACHINE(machine); + MachineClass *mc = MACHINE_GET_CLASS(machine); PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; char *filename; IrqLines *openpic_irqs; - int linux_boot, i, j, k; + int i, j, k, ppc_boot_device, machine_arch, bios_size = -1; + const char *bios_name = machine->firmware ?: PROM_FILENAME; MemoryRegion *bios = g_new(MemoryRegion, 1); - hwaddr kernel_base, initrd_base, cmdline_base = 0; - long kernel_size, initrd_size; - UNINHostState *uninorth_pci; + hwaddr kernel_base = 0, initrd_base = 0, cmdline_base = 0; + long kernel_size = 0, initrd_size = 0; PCIBus *pci_bus; - PCIDevice *macio; - ESCCState *escc; bool has_pmu, has_adb; + Object *macio; MACIOIDEState *macio_ide; BusState *adb_bus; MacIONVRAMState *nvr; - int bios_size; - int ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; - int machine_arch; SysBusDevice *s; - DeviceState *dev, *pic_dev; + DeviceState *dev, *pic_dev, *uninorth_pci_dev; DeviceState *uninorth_internal_dev = NULL, *uninorth_agp_dev = NULL; hwaddr nvram_addr = 0xFFF04000; - uint64_t tbfreq; - unsigned int smp_cpus = machine->smp.cpus; - - linux_boot = (kernel_filename != NULL); + uint64_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TBFREQ; /* init CPUs */ - for (i = 0; i < smp_cpus; i++) { + for (i = 0; i < machine->smp.cpus; i++) { cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); env = &cpu->env; @@ -176,68 +189,62 @@ static void ppc_core99_init(MachineState *machine) bios_size = load_image_targphys(filename, PROM_BASE, PROM_SIZE); } g_free(filename); - } else { - bios_size = -1; } if (bios_size < 0 || bios_size > PROM_SIZE) { error_report("could not load PowerPC bios '%s'", bios_name); exit(1); } - if (linux_boot) { - int bswap_needed; + if (machine->kernel_filename) { + int bswap_needed = 0; #ifdef BSWAP_NEEDED bswap_needed = 1; -#else - bswap_needed = 0; #endif kernel_base = KERNEL_LOAD_ADDR; - - kernel_size = load_elf(kernel_filename, NULL, + kernel_size = load_elf(machine->kernel_filename, NULL, translate_kernel_address, NULL, NULL, NULL, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); - if (kernel_size < 0) - kernel_size = load_aout(kernel_filename, kernel_base, - ram_size - kernel_base, bswap_needed, - TARGET_PAGE_SIZE); - if (kernel_size < 0) - 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); + kernel_size = load_aout(machine->kernel_filename, kernel_base, + machine->ram_size - kernel_base, + bswap_needed, TARGET_PAGE_SIZE); + } + if (kernel_size < 0) { + kernel_size = load_image_targphys(machine->kernel_filename, + kernel_base, + machine->ram_size - kernel_base); + } + if (kernel_size < 0) { + error_report("could not load kernel '%s'", + machine->kernel_filename); exit(1); } /* load initrd */ - if (initrd_filename) { + if (machine->initrd_filename) { initrd_base = TARGET_PAGE_ALIGN(kernel_base + kernel_size + KERNEL_GAP); - initrd_size = load_image_targphys(initrd_filename, initrd_base, - ram_size - initrd_base); + initrd_size = load_image_targphys(machine->initrd_filename, + initrd_base, + machine->ram_size - initrd_base); if (initrd_size < 0) { error_report("could not load initial ram disk '%s'", - initrd_filename); + machine->initrd_filename); exit(1); } cmdline_base = TARGET_PAGE_ALIGN(initrd_base + initrd_size); } else { - initrd_base = 0; - initrd_size = 0; cmdline_base = TARGET_PAGE_ALIGN(kernel_base + kernel_size + KERNEL_GAP); } ppc_boot_device = 'm'; } else { - kernel_base = 0; - kernel_size = 0; - initrd_base = 0; - initrd_size = 0; ppc_boot_device = '\0'; /* We consider that NewWorld PowerMac never have any floppy drive * For now, OHW cannot boot from the network. */ - for (i = 0; boot_device[i] != '\0'; i++) { - if (boot_device[i] >= 'c' && boot_device[i] <= 'f') { - ppc_boot_device = boot_device[i]; + for (i = 0; machine->boot_config.order[i] != '\0'; i++) { + if (machine->boot_config.order[i] >= 'c' && + machine->boot_config.order[i] <= 'f') { + ppc_boot_device = machine->boot_config.order[i]; break; } } @@ -247,45 +254,39 @@ static void ppc_core99_init(MachineState *machine) } } - /* UniN init */ - dev = qdev_new(TYPE_UNI_NORTH); - s = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(s, &error_fatal); - memory_region_add_subregion(get_system_memory(), 0xf8000000, - sysbus_mmio_get_region(s, 0)); - - openpic_irqs = g_new0(IrqLines, smp_cpus); - for (i = 0; i < smp_cpus; i++) { + openpic_irqs = g_new0(IrqLines, machine->smp.cpus); + dev = DEVICE(cpu); + for (i = 0; i < machine->smp.cpus; i++) { /* Mac99 IRQ connection between OpenPIC outputs pins * and PowerPC input pins */ switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_6xx: openpic_irqs[i].irq[OPENPIC_OUTPUT_INT] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; + qdev_get_gpio_in(dev, PPC6xx_INPUT_INT); openpic_irqs[i].irq[OPENPIC_OUTPUT_CINT] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; + qdev_get_gpio_in(dev, PPC6xx_INPUT_INT); openpic_irqs[i].irq[OPENPIC_OUTPUT_MCK] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_MCP]; + qdev_get_gpio_in(dev, PPC6xx_INPUT_MCP); /* Not connected ? */ openpic_irqs[i].irq[OPENPIC_OUTPUT_DEBUG] = NULL; /* Check this */ openpic_irqs[i].irq[OPENPIC_OUTPUT_RESET] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_HRESET]; + qdev_get_gpio_in(dev, PPC6xx_INPUT_HRESET); break; #if defined(TARGET_PPC64) case PPC_FLAGS_INPUT_970: openpic_irqs[i].irq[OPENPIC_OUTPUT_INT] = - ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_INT]; + qdev_get_gpio_in(dev, PPC970_INPUT_INT); openpic_irqs[i].irq[OPENPIC_OUTPUT_CINT] = - ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_INT]; + qdev_get_gpio_in(dev, PPC970_INPUT_INT); openpic_irqs[i].irq[OPENPIC_OUTPUT_MCK] = - ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_MCP]; + qdev_get_gpio_in(dev, PPC970_INPUT_MCP); /* Not connected ? */ openpic_irqs[i].irq[OPENPIC_OUTPUT_DEBUG] = NULL; /* Check this */ openpic_irqs[i].irq[OPENPIC_OUTPUT_RESET] = - ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_HRESET]; + qdev_get_gpio_in(dev, PPC970_INPUT_HRESET); break; #endif /* defined(TARGET_PPC64) */ default: @@ -294,24 +295,29 @@ static void ppc_core99_init(MachineState *machine) } } + /* UniN init */ + s = SYS_BUS_DEVICE(qdev_new(TYPE_UNI_NORTH)); + sysbus_realize_and_unref(s, &error_fatal); + memory_region_add_subregion(get_system_memory(), 0xf8000000, + sysbus_mmio_get_region(s, 0)); + if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) { + machine_arch = ARCH_MAC99_U3; /* 970 gets a U3 bus */ /* Uninorth AGP bus */ - dev = qdev_new(TYPE_U3_AGP_HOST_BRIDGE); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - uninorth_pci = U3_AGP_HOST_BRIDGE(dev); - s = SYS_BUS_DEVICE(dev); + uninorth_pci_dev = qdev_new(TYPE_U3_AGP_HOST_BRIDGE); + s = SYS_BUS_DEVICE(uninorth_pci_dev); + sysbus_realize_and_unref(s, &error_fatal); + sysbus_mmio_map(s, 0, 0xf0800000); + sysbus_mmio_map(s, 1, 0xf0c00000); /* PCI hole */ - memory_region_add_subregion(get_system_memory(), 0x80000000ULL, + memory_region_add_subregion(get_system_memory(), 0x80000000, sysbus_mmio_get_region(s, 2)); /* Register 8 MB of ISA IO space */ memory_region_add_subregion(get_system_memory(), 0xf2000000, sysbus_mmio_get_region(s, 3)); - sysbus_mmio_map(s, 0, 0xf0800000); - sysbus_mmio_map(s, 1, 0xf0c00000); - - machine_arch = ARCH_MAC99_U3; } else { + machine_arch = ARCH_MAC99; /* Use values found on a real PowerMac */ /* Uninorth AGP bus */ uninorth_agp_dev = qdev_new(TYPE_UNI_NORTH_AGP_HOST_BRIDGE); @@ -328,22 +334,19 @@ static void ppc_core99_init(MachineState *machine) sysbus_mmio_map(s, 0, 0xf4800000); sysbus_mmio_map(s, 1, 0xf4c00000); - /* Uninorth main bus */ - dev = qdev_new(TYPE_UNI_NORTH_PCI_HOST_BRIDGE); - qdev_prop_set_uint32(dev, "ofw-addr", 0xf2000000); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - uninorth_pci = UNI_NORTH_PCI_HOST_BRIDGE(dev); - s = SYS_BUS_DEVICE(dev); + /* Uninorth main bus - this must be last to make it the default */ + uninorth_pci_dev = qdev_new(TYPE_UNI_NORTH_PCI_HOST_BRIDGE); + qdev_prop_set_uint32(uninorth_pci_dev, "ofw-addr", 0xf2000000); + s = SYS_BUS_DEVICE(uninorth_pci_dev); + sysbus_realize_and_unref(s, &error_fatal); + sysbus_mmio_map(s, 0, 0xf2800000); + sysbus_mmio_map(s, 1, 0xf2c00000); /* PCI hole */ - memory_region_add_subregion(get_system_memory(), 0x80000000ULL, + memory_region_add_subregion(get_system_memory(), 0x80000000, sysbus_mmio_get_region(s, 2)); /* Register 8 MB of ISA IO space */ memory_region_add_subregion(get_system_memory(), 0xf2000000, sysbus_mmio_get_region(s, 3)); - sysbus_mmio_map(s, 0, 0xf2800000); - sysbus_mmio_map(s, 1, 0xf2c00000); - - machine_arch = ARCH_MAC99; } machine->usb |= defaults_enabled() && !machine->usb_disabled; @@ -351,32 +354,25 @@ static void ppc_core99_init(MachineState *machine) has_adb = (core99_machine->via_config == CORE99_VIA_CONFIG_CUDA || core99_machine->via_config == CORE99_VIA_CONFIG_PMU_ADB); - /* Timebase Frequency */ - if (kvm_enabled()) { - tbfreq = kvmppc_get_tbfreq(); - } else { - tbfreq = TBFREQ; - } - /* init basic PC hardware */ - pci_bus = PCI_HOST_BRIDGE(uninorth_pci)->bus; + pci_bus = PCI_HOST_BRIDGE(uninorth_pci_dev)->bus; /* MacIO */ - macio = pci_new(-1, TYPE_NEWWORLD_MACIO); + macio = OBJECT(pci_new(-1, TYPE_NEWWORLD_MACIO)); dev = DEVICE(macio); qdev_prop_set_uint64(dev, "frequency", tbfreq); qdev_prop_set_bit(dev, "has-pmu", has_pmu); qdev_prop_set_bit(dev, "has-adb", has_adb); - escc = ESCC(object_resolve_path_component(OBJECT(macio), "escc")); - qdev_prop_set_chr(DEVICE(escc), "chrA", serial_hd(0)); - qdev_prop_set_chr(DEVICE(escc), "chrB", serial_hd(1)); + dev = DEVICE(object_resolve_path_component(macio, "escc")); + qdev_prop_set_chr(dev, "chrA", serial_hd(0)); + qdev_prop_set_chr(dev, "chrB", serial_hd(1)); - pci_realize_and_unref(macio, pci_bus, &error_fatal); + pci_realize_and_unref(PCI_DEVICE(macio), pci_bus, &error_fatal); - pic_dev = DEVICE(object_resolve_path_component(OBJECT(macio), "pic")); + pic_dev = DEVICE(object_resolve_path_component(macio, "pic")); for (i = 0; i < 4; i++) { - qdev_connect_gpio_out(DEVICE(uninorth_pci), i, + qdev_connect_gpio_out(uninorth_pci_dev, i, qdev_get_gpio_in(pic_dev, 0x1b + i)); } @@ -398,7 +394,7 @@ static void ppc_core99_init(MachineState *machine) /* OpenPIC */ s = SYS_BUS_DEVICE(pic_dev); k = 0; - for (i = 0; i < smp_cpus; i++) { + for (i = 0; i < machine->smp.cpus; i++) { for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { sysbus_connect_irq(s, k++, openpic_irqs[i].irq[j]); } @@ -408,19 +404,17 @@ static void ppc_core99_init(MachineState *machine) /* We only emulate 2 out of 3 IDE controllers for now */ ide_drive_get(hd, ARRAY_SIZE(hd)); - macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), - "ide[0]")); + macio_ide = MACIO_IDE(object_resolve_path_component(macio, "ide[0]")); macio_ide_init_drives(macio_ide, hd); - macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), - "ide[1]")); + macio_ide = MACIO_IDE(object_resolve_path_component(macio, "ide[1]")); macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); if (has_adb) { if (has_pmu) { - dev = DEVICE(object_resolve_path_component(OBJECT(macio), "pmu")); + dev = DEVICE(object_resolve_path_component(macio, "pmu")); } else { - dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + dev = DEVICE(object_resolve_path_component(macio, "cuda")); } adb_bus = qdev_get_child_bus(dev, "adb.0"); @@ -437,8 +431,10 @@ static void ppc_core99_init(MachineState *machine) /* U3 needs to use USB for input because Linux doesn't support via-cuda on PPC64 */ if (!has_adb || machine_arch == ARCH_MAC99_U3) { - USBBus *usb_bus = usb_bus_find(-1); + USBBus *usb_bus; + usb_bus = USB_BUS(object_resolve_type_unambiguous(TYPE_USB_BUS, + &error_abort)); usb_create_simple(usb_bus, "usb-kbd"); usb_create_simple(usb_bus, "usb-mouse"); } @@ -450,9 +446,7 @@ static void ppc_core99_init(MachineState *machine) graphic_depth = 15; } - for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci_bus, "sungem", NULL); - } + pci_init_nic_devices(pci_bus, mc->default_nic); /* The NewWorld NVRAM is not located in the MacIO device */ if (kvm_enabled() && qemu_real_host_page_size() > 4096) { @@ -461,34 +455,34 @@ static void ppc_core99_init(MachineState *machine) nvram_addr = 0xFFE00000; } dev = qdev_new(TYPE_MACIO_NVRAM); - qdev_prop_set_uint32(dev, "size", 0x2000); + qdev_prop_set_uint32(dev, "size", MACIO_NVRAM_SIZE); qdev_prop_set_uint32(dev, "it_shift", 1); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, nvram_addr); nvr = MACIO_NVRAM(dev); - pmac_format_nvram_partition(nvr, 0x2000); + pmac_format_nvram_partition(nvr, MACIO_NVRAM_SIZE); /* No PCI init: the BIOS will do it */ dev = qdev_new(TYPE_FW_CFG_MEM); fw_cfg = FW_CFG(dev); qdev_prop_set_uint32(dev, "data_width", 1); qdev_prop_set_bit(dev, "dma_enabled", false); - object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG, - OBJECT(fw_cfg)); + object_property_add_child(OBJECT(machine), TYPE_FW_CFG, OBJECT(fw_cfg)); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); sysbus_mmio_map(s, 0, CFG_ADDR); sysbus_mmio_map(s, 1, CFG_ADDR + 2); - fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); + fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)machine->smp.cpus); fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)machine->smp.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, machine_arch); fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base); fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); - if (kernel_cmdline) { + if (machine->kernel_cmdline) { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, cmdline_base); - pstrcpy_targphys("cmdline", cmdline_base, TARGET_PAGE_SIZE, kernel_cmdline); + pstrcpy_targphys("cmdline", cmdline_base, TARGET_PAGE_SIZE, + machine->kernel_cmdline); } else { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); } @@ -584,6 +578,7 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = 1; mc->default_boot_order = "cd"; mc->default_display = "std"; + mc->default_nic = "sungem"; mc->kvm_type = core99_kvm_type; #ifdef TARGET_PPC64 mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("970fx_v3.1"); diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index d62fdf0db3..1981d3d8f6 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -30,13 +30,14 @@ #include "qapi/error.h" #include "hw/ppc/ppc.h" #include "hw/qdev-properties.h" -#include "mac.h" +#include "hw/boards.h" #include "hw/input/adb.h" #include "sysemu/sysemu.h" #include "net/net.h" #include "hw/isa/isa.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" +#include "hw/pci-host/grackle.h" #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" #include "hw/misc/macio/macio.h" @@ -56,10 +57,15 @@ #define NDRV_VGA_FILENAME "qemu_vga.ndrv" -#define GRACKLE_BASE 0xfec00000 +#define PROM_FILENAME "openbios-ppc" #define PROM_BASE 0xffc00000 #define PROM_SIZE (4 * MiB) +#define KERNEL_LOAD_ADDR 0x01000000 +#define KERNEL_GAP 0x00100000 + +#define GRACKLE_BASE 0xfec00000 + static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) { @@ -75,38 +81,35 @@ static void ppc_heathrow_reset(void *opaque) { PowerPCCPU *cpu = opaque; + cpu_ppc_tb_reset(&cpu->env); cpu_reset(CPU(cpu)); } static void ppc_heathrow_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; const char *bios_name = machine->firmware ?: PROM_FILENAME; - const char *boot_device = machine->boot_config.order; + MachineClass *mc = MACHINE_GET_CLASS(machine); PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; char *filename; - int i; + int i, bios_size = -1; MemoryRegion *bios = g_new(MemoryRegion, 1); - uint32_t kernel_base, initrd_base, cmdline_base = 0; - int32_t kernel_size, initrd_size; + uint64_t bios_addr; + uint32_t kernel_base = 0, initrd_base = 0, cmdline_base = 0; + int32_t kernel_size = 0, initrd_size = 0; PCIBus *pci_bus; - PCIDevice *macio; + Object *macio; MACIOIDEState *macio_ide; - ESCCState *escc; SysBusDevice *s; DeviceState *dev, *pic_dev, *grackle_dev; BusState *adb_bus; - uint64_t bios_addr; - int bios_size; - unsigned int smp_cpus = machine->smp.cpus; uint16_t ppc_boot_device; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + DriveInfo *dinfo, *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; - uint64_t tbfreq; + uint64_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TBFREQ; /* init CPUs */ - for (i = 0; i < smp_cpus; i++) { + for (i = 0; i < machine->smp.cpus; i++) { cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); env = &cpu->env; @@ -116,9 +119,9 @@ static void ppc_heathrow_init(MachineState *machine) } /* allocate RAM */ - if (ram_size > 2047 * MiB) { + if (machine->ram_size > 2047 * MiB) { error_report("Too much memory for this machine: %" PRId64 " MB, " - "maximum 2047 MB", ram_size / MiB); + "maximum 2047 MB", machine->ram_size / MiB); exit(1); } @@ -143,8 +146,6 @@ static void ppc_heathrow_init(MachineState *machine) bios_addr = PROM_BASE; } g_free(filename); - } else { - bios_size = -1; } if (bios_size < 0 || bios_addr - PROM_BASE + bios_size > PROM_SIZE) { error_report("could not load PowerPC bios '%s'", bios_name); @@ -152,25 +153,25 @@ static void ppc_heathrow_init(MachineState *machine) } if (machine->kernel_filename) { - int bswap_needed; + int bswap_needed = 0; #ifdef BSWAP_NEEDED bswap_needed = 1; -#else - bswap_needed = 0; #endif kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(machine->kernel_filename, NULL, translate_kernel_address, NULL, NULL, NULL, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); - if (kernel_size < 0) + if (kernel_size < 0) { kernel_size = load_aout(machine->kernel_filename, kernel_base, - ram_size - kernel_base, bswap_needed, - TARGET_PAGE_SIZE); - if (kernel_size < 0) + machine->ram_size - kernel_base, + bswap_needed, TARGET_PAGE_SIZE); + } + if (kernel_size < 0) { kernel_size = load_image_targphys(machine->kernel_filename, kernel_base, - ram_size - kernel_base); + machine->ram_size - kernel_base); + } if (kernel_size < 0) { error_report("could not load kernel '%s'", machine->kernel_filename); @@ -182,7 +183,7 @@ static void ppc_heathrow_init(MachineState *machine) KERNEL_GAP); 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'", machine->initrd_filename); @@ -190,30 +191,27 @@ static void ppc_heathrow_init(MachineState *machine) } cmdline_base = TARGET_PAGE_ALIGN(initrd_base + initrd_size); } else { - initrd_base = 0; - initrd_size = 0; cmdline_base = TARGET_PAGE_ALIGN(kernel_base + kernel_size + KERNEL_GAP); } ppc_boot_device = 'm'; } else { - kernel_base = 0; - kernel_size = 0; - initrd_base = 0; - initrd_size = 0; ppc_boot_device = '\0'; - for (i = 0; boot_device[i] != '\0'; i++) { - /* TOFIX: for now, the second IDE channel is not properly + for (i = 0; machine->boot_config.order[i] != '\0'; i++) { + /* + * TOFIX: for now, the second IDE channel is not properly * used by OHW. The Mac floppy disk are not emulated. * For now, OHW cannot boot from the network. */ #if 0 - if (boot_device[i] >= 'a' && boot_device[i] <= 'f') { - ppc_boot_device = boot_device[i]; + if (machine->boot_config.order[i] >= 'a' && + machine->boot_config.order[i] <= 'f') { + ppc_boot_device = machine->boot_config.order[i]; break; } #else - if (boot_device[i] >= 'c' && boot_device[i] <= 'd') { - ppc_boot_device = boot_device[i]; + if (machine->boot_config.order[i] >= 'c' && + machine->boot_config.order[i] <= 'd') { + ppc_boot_device = machine->boot_config.order[i]; break; } #endif @@ -224,13 +222,6 @@ static void ppc_heathrow_init(MachineState *machine) } } - /* Timebase Frequency */ - if (kvm_enabled()) { - tbfreq = kvmppc_get_tbfreq(); - } else { - tbfreq = TBFREQ; - } - /* Grackle PCI host bridge */ grackle_dev = qdev_new(TYPE_GRACKLE_PCI_HOST_BRIDGE); qdev_prop_set_uint32(grackle_dev, "ofw-addr", 0x80000000); @@ -249,29 +240,34 @@ static void ppc_heathrow_init(MachineState *machine) pci_bus = PCI_HOST_BRIDGE(grackle_dev)->bus; /* MacIO */ - macio = pci_new(PCI_DEVFN(16, 0), TYPE_OLDWORLD_MACIO); - dev = DEVICE(macio); - qdev_prop_set_uint64(dev, "frequency", tbfreq); + macio = OBJECT(pci_new(PCI_DEVFN(16, 0), TYPE_OLDWORLD_MACIO)); + qdev_prop_set_uint64(DEVICE(macio), "frequency", tbfreq); - escc = ESCC(object_resolve_path_component(OBJECT(macio), "escc")); - qdev_prop_set_chr(DEVICE(escc), "chrA", serial_hd(0)); - qdev_prop_set_chr(DEVICE(escc), "chrB", serial_hd(1)); + dev = DEVICE(object_resolve_path_component(macio, "escc")); + qdev_prop_set_chr(dev, "chrA", serial_hd(0)); + qdev_prop_set_chr(dev, "chrB", serial_hd(1)); - pci_realize_and_unref(macio, pci_bus, &error_fatal); + dinfo = drive_get(IF_MTD, 0, 0); + if (dinfo) { + dev = DEVICE(object_resolve_path_component(macio, "nvram")); + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo)); + } - pic_dev = DEVICE(object_resolve_path_component(OBJECT(macio), "pic")); + pci_realize_and_unref(PCI_DEVICE(macio), pci_bus, &error_fatal); + + pic_dev = DEVICE(object_resolve_path_component(macio, "pic")); for (i = 0; i < 4; i++) { qdev_connect_gpio_out(grackle_dev, i, qdev_get_gpio_in(pic_dev, 0x15 + i)); } /* Connect the heathrow PIC outputs to the 6xx bus */ - for (i = 0; i < smp_cpus; i++) { + for (i = 0; i < machine->smp.cpus; i++) { switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_6xx: /* XXX: we register only 1 output pin for heathrow PIC */ qdev_connect_gpio_out(pic_dev, 0, - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]); + qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_INT)); break; default: error_report("Bus model not supported on OldWorld Mac machine"); @@ -281,22 +277,18 @@ static void ppc_heathrow_init(MachineState *machine) pci_vga_init(pci_bus); - for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL); - } + pci_init_nic_devices(pci_bus, mc->default_nic); /* MacIO IDE */ ide_drive_get(hd, ARRAY_SIZE(hd)); - macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), - "ide[0]")); + macio_ide = MACIO_IDE(object_resolve_path_component(macio, "ide[0]")); macio_ide_init_drives(macio_ide, hd); - macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), - "ide[1]")); + macio_ide = MACIO_IDE(object_resolve_path_component(macio, "ide[1]")); macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); /* MacIO CUDA/ADB */ - dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + dev = DEVICE(object_resolve_path_component(macio, "cuda")); adb_bus = qdev_get_child_bus(dev, "adb.0"); dev = qdev_new(TYPE_ADB_KEYBOARD); qdev_realize_and_unref(dev, adb_bus, &error_fatal); @@ -307,8 +299,9 @@ static void ppc_heathrow_init(MachineState *machine) pci_create_simple(pci_bus, -1, "pci-ohci"); } - if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) + if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) { graphic_depth = 15; + } /* No PCI init: the BIOS will do it */ @@ -316,16 +309,15 @@ static void ppc_heathrow_init(MachineState *machine) fw_cfg = FW_CFG(dev); qdev_prop_set_uint32(dev, "data_width", 1); qdev_prop_set_bit(dev, "dma_enabled", false); - object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG, - OBJECT(fw_cfg)); + object_property_add_child(OBJECT(machine), TYPE_FW_CFG, OBJECT(fw_cfg)); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); sysbus_mmio_map(s, 0, CFG_ADDR); sysbus_mmio_map(s, 1, CFG_ADDR + 2); - fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); + fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)machine->smp.cpus); fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)machine->smp.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, ARCH_HEATHROW); fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base); fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); @@ -432,6 +424,7 @@ static void heathrow_class_init(ObjectClass *oc, void *data) mc->kvm_type = heathrow_kvm_type; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("750_v3.1"); mc->default_display = "std"; + mc->default_nic = "ne2k_pci"; 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/meson.build b/hw/ppc/meson.build index aa4c8e6a2e..d096636ee7 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -15,6 +15,7 @@ ppc_ss.add(when: 'CONFIG_PSERIES', if_true: files( 'spapr_vio.c', 'spapr_events.c', 'spapr_hcall.c', + 'spapr_nested.c', 'spapr_iommu.c', 'spapr_rtas.c', 'spapr_pci.c', @@ -30,25 +31,31 @@ ppc_ss.add(when: 'CONFIG_PSERIES', if_true: files( 'pef.c', )) ppc_ss.add(when: ['CONFIG_PSERIES', 'CONFIG_TCG'], if_true: files( - 'spapr_softmmu.c', + 'spapr_vhyp_mmu.c', )) ppc_ss.add(when: 'CONFIG_SPAPR_RNG', if_true: files('spapr_rng.c')) -ppc_ss.add(when: ['CONFIG_PSERIES', 'CONFIG_LINUX'], if_true: files( - 'spapr_pci_vfio.c', - 'spapr_pci_nvlink2.c' -)) +if host_os == 'linux' + ppc_ss.add(when: 'CONFIG_PSERIES', if_true: files( + 'spapr_pci_vfio.c', + )) +endif # IBM PowerNV ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files( 'pnv.c', 'pnv_xscom.c', 'pnv_core.c', + 'pnv_i2c.c', 'pnv_lpc.c', 'pnv_psi.c', + 'pnv_chiptod.c', 'pnv_occ.c', + 'pnv_sbe.c', 'pnv_bmc.c', 'pnv_homer.c', 'pnv_pnor.c', + 'pnv_nest_pervasive.c', + 'pnv_n1_chiplet.c', )) # PowerPC 4xx boards ppc_ss.add(when: 'CONFIG_PPC405', if_true: files( @@ -56,10 +63,10 @@ ppc_ss.add(when: 'CONFIG_PPC405', if_true: files( 'ppc405_uc.c')) ppc_ss.add(when: 'CONFIG_PPC440', if_true: files( 'ppc440_bamboo.c', - 'ppc440_pcix.c', 'ppc440_uc.c')) + 'ppc440_uc.c')) ppc_ss.add(when: 'CONFIG_PPC4XX', if_true: files( - 'ppc4xx_pci.c', - 'ppc4xx_devs.c')) + 'ppc4xx_devs.c', + 'ppc4xx_sdram.c')) ppc_ss.add(when: 'CONFIG_SAM460EX', if_true: files('sam460ex.c')) # PReP ppc_ss.add(when: 'CONFIG_PREP', if_true: files('prep.c')) @@ -70,17 +77,17 @@ ppc_ss.add(when: 'CONFIG_MAC_OLDWORLD', if_true: files('mac_oldworld.c')) # NewWorld PowerMac ppc_ss.add(when: 'CONFIG_MAC_NEWWORLD', if_true: files('mac_newworld.c')) # e500 +ppc_ss.add(when: 'CONFIG_E500PLAT', if_true: files('e500plat.c')) +ppc_ss.add(when: 'CONFIG_MPC8544DS', if_true: files('mpc8544ds.c')) ppc_ss.add(when: 'CONFIG_E500', if_true: files( 'e500.c', - 'mpc8544ds.c', - 'e500plat.c' -)) -ppc_ss.add(when: 'CONFIG_E500', if_true: files( 'mpc8544_guts.c', 'ppce500_spin.c' )) # PowerPC 440 Xilinx ML507 reference board. ppc_ss.add(when: 'CONFIG_VIRTEX', if_true: files('virtex_ml507.c')) +# AmigaOne +ppc_ss.add(when: 'CONFIG_AMIGAONE', if_true: files('amigaone.c')) # Pegasos2 ppc_ss.add(when: 'CONFIG_PEGASOS2', if_true: files('pegasos2.c')) diff --git a/hw/ppc/mpc8544_guts.c b/hw/ppc/mpc8544_guts.c index a26e83d048..e3540b0281 100644 --- a/hw/ppc/mpc8544_guts.c +++ b/hw/ppc/mpc8544_guts.c @@ -71,8 +71,7 @@ static uint64_t mpc8544_guts_read(void *opaque, hwaddr addr, unsigned size) { uint32_t value = 0; - PowerPCCPU *cpu = POWERPC_CPU(current_cpu); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(current_cpu); addr &= MPC8544_GUTS_MMIO_SIZE - 1; switch (addr) { diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index 81177505f0..b7130903d6 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -14,6 +14,7 @@ #include "sysemu/device_tree.h" #include "hw/ppc/openpic.h" #include "qemu/error-report.h" +#include "qemu/units.h" #include "cpu.h" static void mpc8544ds_fixup_devtree(void *fdt) @@ -36,7 +37,7 @@ static void mpc8544ds_init(MachineState *machine) ppce500_init(machine); } -static void e500plat_machine_class_init(ObjectClass *oc, void *data) +static void mpc8544ds_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); PPCE500MachineClass *pmc = PPCE500_MACHINE_CLASS(oc); @@ -45,6 +46,10 @@ static void e500plat_machine_class_init(ObjectClass *oc, void *data) pmc->pci_nr_slots = 2; pmc->fixup_devtree = mpc8544ds_fixup_devtree; pmc->mpic_version = OPENPIC_MODEL_FSL_MPIC_20; + pmc->platform_bus_base = 0xFF800000ULL; + pmc->platform_bus_size = 8 * MiB; + pmc->platform_bus_first_irq = 5; + pmc->platform_bus_num_irqs = 10; pmc->ccsrbar_base = 0xE0000000ULL; pmc->pci_mmio_base = 0xC0000000ULL; pmc->pci_mmio_bus_base = 0xC0000000ULL; @@ -56,6 +61,7 @@ static void e500plat_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = 15; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30"); mc->default_ram_id = "mpc8544ds.ram"; + mc->default_nic = "virtio-net-pci"; } #define TYPE_MPC8544DS_MACHINE MACHINE_TYPE_NAME("mpc8544ds") @@ -63,7 +69,7 @@ static void e500plat_machine_class_init(ObjectClass *oc, void *data) static const TypeInfo mpc8544ds_info = { .name = TYPE_MPC8544DS_MACHINE, .parent = TYPE_PPCE500_MACHINE, - .class_init = e500plat_machine_class_init, + .class_init = mpc8544ds_machine_class_init, }; static void mpc8544ds_register_types(void) diff --git a/hw/ppc/pef.c b/hw/ppc/pef.c index cc44d5e339..d28ed3ba73 100644 --- a/hw/ppc/pef.c +++ b/hw/ppc/pef.c @@ -63,7 +63,7 @@ static int kvmppc_svm_init(ConfidentialGuestSupport *cgs, Error **errp) /* add migration blocker */ error_setg(&pef_mig_blocker, "PEF: Migration is not implemented"); /* NB: This can fail if --only-migratable is used */ - migrate_add_blocker(pef_mig_blocker, &error_fatal); + migrate_add_blocker(&pef_mig_blocker, &error_fatal); cgs->ready = true; diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index 9411ca6b16..04d6decb2b 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -10,7 +10,6 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "hw/hw.h" #include "hw/ppc/ppc.h" #include "hw/sysbus.h" #include "hw/pci/pci_host.h" @@ -45,6 +44,8 @@ #define PROM_ADDR 0xfff00000 #define PROM_SIZE 0x80000 +#define INITRD_MIN_ADDR 0x600000 + #define KVMPPC_HCALL_BASE 0xf000 #define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0) #define KVMPPC_H_VOF_CLIENT (KVMPPC_HCALL_BASE + 0x5) @@ -74,11 +75,15 @@ struct Pegasos2MachineState { MachineState parent_obj; PowerPCCPU *cpu; DeviceState *mv; + qemu_irq mv_pirq[PCI_NUM_PINS]; + qemu_irq via_pirq[PCI_NUM_PINS]; Vof *vof; void *fdt_blob; uint64_t kernel_addr; uint64_t kernel_entry; uint64_t kernel_size; + uint64_t initrd_addr; + uint64_t initrd_size; }; static void *build_fdt(MachineState *machine, int *fdt_size); @@ -94,6 +99,16 @@ static void pegasos2_cpu_reset(void *opaque) cpu->env.gpr[1] = 2 * VOF_STACK_SIZE - 0x20; cpu->env.nip = 0x100; } + cpu_ppc_tb_reset(&cpu->env); +} + +static void pegasos2_pci_irq(void *opaque, int n, int level) +{ + Pegasos2MachineState *pm = opaque; + + /* PCI interrupt lines are connected to both MV64361 and VT8231 */ + qemu_set_irq(pm->mv_pirq[n], level); + qemu_set_irq(pm->via_pirq[n], level); } static void pegasos2_init(MachineState *machine) @@ -102,11 +117,13 @@ static void pegasos2_init(MachineState *machine) CPUPPCState *env; MemoryRegion *rom = g_new(MemoryRegion, 1); PCIBus *pci_bus; + Object *via; PCIDevice *dev; I2CBus *i2c_bus; const char *fwname = machine->firmware ?: PROM_FILENAME; char *filename; - int sz; + int i; + ssize_t sz; uint8_t *spd_data; /* init CPU */ @@ -155,34 +172,40 @@ static void pegasos2_init(MachineState *machine) /* Marvell Discovery II system controller */ pm->mv = DEVICE(sysbus_create_simple(TYPE_MV64361, -1, - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT])); + qdev_get_gpio_in(DEVICE(pm->cpu), PPC6xx_INPUT_INT))); + for (i = 0; i < PCI_NUM_PINS; i++) { + pm->mv_pirq[i] = qdev_get_gpio_in_named(pm->mv, "gpp", 12 + i); + } pci_bus = mv64361_get_pci_bus(pm->mv, 1); + pci_bus_irqs(pci_bus, pegasos2_pci_irq, pm, PCI_NUM_PINS); /* VIA VT8231 South Bridge (multifunction PCI device) */ - /* VT8231 function 0: PCI-to-ISA Bridge */ - dev = pci_create_simple_multifunction(pci_bus, PCI_DEVFN(12, 0), true, - TYPE_VT8231_ISA); - qdev_connect_gpio_out(DEVICE(dev), 0, + via = OBJECT(pci_new_multifunction(PCI_DEVFN(12, 0), TYPE_VT8231_ISA)); + + /* Set properties on individual devices before realizing the south bridge */ + if (machine->audiodev) { + dev = PCI_DEVICE(object_resolve_path_component(via, "ac97")); + qdev_prop_set_string(DEVICE(dev), "audiodev", machine->audiodev); + } + + pci_realize_and_unref(PCI_DEVICE(via), pci_bus, &error_abort); + for (i = 0; i < PCI_NUM_PINS; i++) { + pm->via_pirq[i] = qdev_get_gpio_in_named(DEVICE(via), "pirq", i); + } + object_property_add_alias(OBJECT(machine), "rtc-time", + object_resolve_path_component(via, "rtc"), + "date"); + qdev_connect_gpio_out(DEVICE(via), 0, qdev_get_gpio_in_named(pm->mv, "gpp", 31)); - /* VT8231 function 1: IDE Controller */ - dev = pci_create_simple(pci_bus, PCI_DEVFN(12, 1), "via-ide"); + dev = PCI_DEVICE(object_resolve_path_component(via, "ide")); pci_ide_create_devs(dev); - /* VT8231 function 2-3: USB Ports */ - pci_create_simple(pci_bus, PCI_DEVFN(12, 2), "vt82c686b-usb-uhci"); - pci_create_simple(pci_bus, PCI_DEVFN(12, 3), "vt82c686b-usb-uhci"); - - /* VT8231 function 4: Power Management Controller */ - dev = pci_create_simple(pci_bus, PCI_DEVFN(12, 4), TYPE_VT8231_PM); + dev = PCI_DEVICE(object_resolve_path_component(via, "pm")); i2c_bus = I2C_BUS(qdev_get_child_bus(DEVICE(dev), "i2c")); spd_data = spd_data_generate(DDR, machine->ram_size); smbus_eeprom_init_one(i2c_bus, 0x57, spd_data); - /* VT8231 function 5-6: AC97 Audio & Modem */ - pci_create_simple(pci_bus, PCI_DEVFN(12, 5), TYPE_VIA_AC97); - pci_create_simple(pci_bus, PCI_DEVFN(12, 6), TYPE_VIA_MC97); - /* other PC hardware */ pci_vga_init(pci_bus); @@ -203,6 +226,20 @@ static void pegasos2_init(MachineState *machine) warn_report("Using Virtual OpenFirmware but no -kernel option."); } + if (machine->initrd_filename) { + pm->initrd_addr = pm->kernel_addr + pm->kernel_size + 64 * KiB; + pm->initrd_addr = ROUND_UP(pm->initrd_addr, 4); + pm->initrd_addr = MAX(pm->initrd_addr, INITRD_MIN_ADDR); + sz = load_image_targphys(machine->initrd_filename, pm->initrd_addr, + machine->ram_size - pm->initrd_addr); + if (sz <= 0) { + error_report("Could not load initrd '%s'", + machine->initrd_filename); + exit(1); + } + pm->initrd_size = sz; + } + if (!pm->vof && machine->kernel_cmdline && machine->kernel_cmdline[0]) { warn_report("Option -append may be ineffective with -bios."); } @@ -248,14 +285,20 @@ static void pegasos2_pci_config_write(Pegasos2MachineState *pm, int bus, pegasos2_mv_reg_write(pm, pcicfg + 4, len, val); } -static void pegasos2_machine_reset(MachineState *machine) +static void pegasos2_superio_write(uint8_t addr, uint8_t val) +{ + cpu_physical_memory_write(PCI1_IO_BASE + 0x3f0, &addr, 1); + cpu_physical_memory_write(PCI1_IO_BASE + 0x3f1, &val, 1); +} + +static void pegasos2_machine_reset(MachineState *machine, ShutdownCause reason) { Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine); void *fdt; uint64_t d[2]; int sz; - qemu_devices_reset(); + qemu_devices_reset(reason); if (!pm->vof) { return; /* Firmware should set up machine so nothing to do */ } @@ -273,8 +316,20 @@ static void pegasos2_machine_reset(MachineState *machine) pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | PCI_INTERRUPT_LINE, 2, 0x9); + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | + 0x50, 1, 0x6); + pegasos2_superio_write(0xf4, 0xbe); + pegasos2_superio_write(0xf6, 0xef); + pegasos2_superio_write(0xf7, 0xfc); + pegasos2_superio_write(0xf2, 0x14); pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | 0x50, 1, 0x2); + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | + 0x55, 1, 0x90); + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | + 0x56, 1, 0x99); + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | + 0x57, 1, 0x90); pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | PCI_INTERRUPT_LINE, 2, 0x109); @@ -289,9 +344,13 @@ static void pegasos2_machine_reset(MachineState *machine) pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 2) << 8) | PCI_INTERRUPT_LINE, 2, 0x409); + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 2) << 8) | + PCI_COMMAND, 2, 0x7); pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 3) << 8) | PCI_INTERRUPT_LINE, 2, 0x409); + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 3) << 8) | + PCI_COMMAND, 2, 0x7); pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | PCI_INTERRUPT_LINE, 2, 0x9); @@ -319,6 +378,11 @@ static void pegasos2_machine_reset(MachineState *machine) error_report("Memory for kernel is in use"); exit(1); } + if (pm->initrd_size && + vof_claim(pm->vof, pm->initrd_addr, pm->initrd_size, 0) == -1) { + error_report("Memory for initrd is in use"); + exit(1); + } fdt = build_fdt(machine, &sz); /* FIXME: VOF assumes entry is same as load address */ d[0] = cpu_to_be64(pm->kernel_entry); @@ -331,6 +395,10 @@ static void pegasos2_machine_reset(MachineState *machine) vof_build_dt(fdt, pm->vof); vof_client_open_store(fdt, pm->vof, "/chosen", "stdout", "/failsafe"); + + /* Set machine->fdt for 'dumpdtb' QMP/HMP command */ + machine->fdt = fdt; + pm->cpu->vhyp = PPC_VIRTUAL_HYPERVISOR(machine); } @@ -459,7 +527,7 @@ static void pegasos2_hypercall(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu) CPUPPCState *env = &cpu->env; /* The TCG path should also be holding the BQL at this point */ - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); if (FIELD_EX64(env->msr, MSR, PR)) { qemu_log_mask(LOG_GUEST_ERROR, "Hypercall made with MSR[PR]=1\n"); @@ -504,9 +572,10 @@ static void pegasos2_machine_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_IDE; mc->default_boot_order = "cd"; mc->default_display = "std"; - mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7400_v2.9"); + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7457_v1.2"); mc->default_ram_id = "pegasos2.ram"; mc->default_ram_size = 512 * MiB; + machine_add_audiodev_property(mc); vhc->cpu_in_nested = pegasos2_cpu_in_nested; vhc->hypercall = pegasos2_hypercall; @@ -567,7 +636,7 @@ static void dt_isa(PCIBus *bus, PCIDevice *d, FDTInfo *fi) qemu_fdt_setprop_string(fi->fdt, fi->path, "device_type", "isa"); qemu_fdt_setprop_string(fi->fdt, fi->path, "name", "isa"); - /* addional devices */ + /* additional devices */ g_string_printf(name, "%s/lpt@i3bc", fi->path); qemu_fdt_add_subnode(fi->fdt, name->str); qemu_fdt_setprop_cell(fi->fdt, name->str, "clock-frequency", 0); @@ -691,6 +760,13 @@ static void add_pci_device(PCIBus *bus, PCIDevice *d, void *opaque) pci_get_word(&d->config[PCI_VENDOR_ID]), pci_get_word(&d->config[PCI_DEVICE_ID])); + if (pci_get_word(&d->config[PCI_CLASS_DEVICE]) == + PCI_CLASS_NETWORK_ETHERNET) { + name = "ethernet"; + } else if (pci_get_word(&d->config[PCI_CLASS_DEVICE]) >> 8 == + PCI_BASE_CLASS_DISPLAY) { + name = "display"; + } for (i = 0; device_map[i].id; i++) { if (!strcmp(pn, device_map[i].id)) { name = device_map[i].name; @@ -718,11 +794,19 @@ static void add_pci_device(PCIBus *bus, PCIDevice *d, void *opaque) if (!d->io_regions[i].size) { continue; } - cells[j] = cpu_to_be32(d->devfn << 8 | (PCI_BASE_ADDRESS_0 + i * 4)); + cells[j] = PCI_BASE_ADDRESS_0 + i * 4; + if (cells[j] == 0x28) { + cells[j] = 0x30; + } + cells[j] = cpu_to_be32(d->devfn << 8 | cells[j]); if (d->io_regions[i].type & PCI_BASE_ADDRESS_SPACE_IO) { cells[j] |= cpu_to_be32(1 << 24); } else { - cells[j] |= cpu_to_be32(2 << 24); + if (d->io_regions[i].type & PCI_BASE_ADDRESS_MEM_TYPE_64) { + cells[j] |= cpu_to_be32(3 << 24); + } else { + cells[j] |= cpu_to_be32(2 << 24); + } if (d->io_regions[i].type & PCI_BASE_ADDRESS_MEM_PREFETCH) { cells[j] |= cpu_to_be32(4 << 28); } @@ -877,6 +961,7 @@ static void *build_fdt(MachineState *machine, int *fdt_size) qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-display-device", 0); qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-size", 20); qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-version", 1); + qemu_fdt_setprop_string(fdt, "/rtas", "name", "rtas"); /* cpus */ qemu_fdt_add_subnode(fdt, "/cpus"); @@ -946,6 +1031,12 @@ static void *build_fdt(MachineState *machine, int *fdt_size) qemu_fdt_setprop_string(fdt, "/memory@0", "name", "memory"); qemu_fdt_add_subnode(fdt, "/chosen"); + if (pm->initrd_addr && pm->initrd_size) { + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + pm->initrd_addr + pm->initrd_size); + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", + pm->initrd_addr); + } qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", machine->kernel_cmdline ?: ""); qemu_fdt_setprop_string(fdt, "/chosen", "name", "chosen"); diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 7c08a78d6c..6e3a5ccdec 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -43,9 +43,13 @@ #include "hw/ipmi/ipmi.h" #include "target/ppc/mmu-hash64.h" #include "hw/pci/msi.h" +#include "hw/pci-host/pnv_phb.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/pci-host/pnv_phb4.h" #include "hw/ppc/xics.h" #include "hw/qdev-properties.h" +#include "hw/ppc/pnv_chip.h" #include "hw/ppc/pnv_xscom.h" #include "hw/ppc/pnv_pnor.h" @@ -129,7 +133,7 @@ static int get_cpus_node(void *fdt) * device tree, used in XSCOM to address cores and in interrupt * servers. */ -static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) +static int pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) { PowerPCCPU *cpu = pc->threads[0]; CPUState *cs = CPU(cpu); @@ -137,32 +141,31 @@ static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) int smt_threads = CPU_CORE(pc)->nr_threads; CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); - uint32_t servers_prop[smt_threads]; + PnvChipClass *pnv_cc = PNV_CHIP_GET_CLASS(chip); + g_autofree uint32_t *servers_prop = g_new(uint32_t, smt_threads); int i; + uint32_t pir; uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), 0xffffffff, 0xffffffff}; uint32_t tbfreq = PNV_TIMEBASE_FREQ; uint32_t cpufreq = 1000000000; uint32_t page_sizes_prop[64]; size_t page_sizes_prop_size; - const uint8_t pa_features[] = { 24, 0, - 0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xf0, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, - 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; int offset; char *nodename; int cpus_offset = get_cpus_node(fdt); - nodename = g_strdup_printf("%s@%x", dc->fw_name, pc->pir); + pir = pnv_cc->chip_pir(chip, pc->hwid, 0); + + nodename = g_strdup_printf("%s@%x", dc->fw_name, pir); offset = fdt_add_subnode(fdt, cpus_offset, nodename); _FDT(offset); g_free(nodename); _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", chip->chip_id))); - _FDT((fdt_setprop_cell(fdt, offset, "reg", pc->pir))); - _FDT((fdt_setprop_cell(fdt, offset, "ibm,pir", pc->pir))); + _FDT((fdt_setprop_cell(fdt, offset, "reg", pir))); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,pir", pir))); _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu"))); _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR]))); @@ -232,20 +235,21 @@ static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) page_sizes_prop, page_sizes_prop_size))); } - _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", - pa_features, sizeof(pa_features)))); - /* Build interrupt servers properties */ for (i = 0; i < smt_threads; i++) { - servers_prop[i] = cpu_to_be32(pc->pir + i); + servers_prop[i] = cpu_to_be32(pnv_cc->chip_pir(chip, pc->hwid, i)); } _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", - servers_prop, sizeof(servers_prop)))); + servers_prop, sizeof(*servers_prop) * smt_threads))); + + return offset; } -static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t pir, +static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t hwid, uint32_t nr_threads) { + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + uint32_t pir = pcc->chip_pir(chip, hwid, 0); uint64_t addr = PNV_ICP_BASE(chip) | (pir << 12); char *name; const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp"; @@ -259,6 +263,7 @@ static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t pir, rsize = sizeof(uint64_t) * 2 * nr_threads; reg = g_malloc(rsize); for (i = 0; i < nr_threads; i++) { + /* We know P8 PIR is linear with thread id */ reg[i * 2] = cpu_to_be64(addr | ((pir + i) * 0x1000)); reg[i * 2 + 1] = cpu_to_be64(0x1000); } @@ -280,6 +285,32 @@ static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t pir, g_free(reg); } +/* + * Adds a PnvPHB to the chip on P8. + * Implemented here, like for defaults PHBs + */ +PnvChip *pnv_chip_add_phb(PnvChip *chip, PnvPHB *phb) +{ + Pnv8Chip *chip8 = PNV8_CHIP(chip); + + phb->chip = chip; + + chip8->phbs[chip8->num_phbs] = phb; + chip8->num_phbs++; + return chip; +} + +/* + * Same as spapr pa_features_207 except pnv always enables CI largepages bit. + * HTM is always enabled because TCG does implement HTM, it's just a + * degenerate implementation. + */ +static const uint8_t pa_features_207[] = { 24, 0, + 0xf6, 0x3f, 0xc7, 0xc0, 0x00, 0xf0, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; + static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt) { static const char compat[] = "ibm,power8-xscom\0ibm,xscom"; @@ -292,11 +323,15 @@ static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt) for (i = 0; i < chip->nr_cores; i++) { PnvCore *pnv_core = chip->cores[i]; + int offset; - pnv_dt_core(chip, pnv_core, fdt); + offset = pnv_dt_core(chip, pnv_core, fdt); + + _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", + pa_features_207, sizeof(pa_features_207)))); /* Interrupt Control Presenters (ICP). One per core. */ - pnv_dt_icp(chip, fdt, pnv_core->pir, CPU_CORE(pnv_core)->nr_threads); + pnv_dt_icp(chip, fdt, pnv_core->hwid, CPU_CORE(pnv_core)->nr_threads); } if (chip->ram_size) { @@ -304,6 +339,35 @@ static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt) } } +/* + * Same as spapr pa_features_300 except pnv always enables CI largepages bit. + */ +static const uint8_t pa_features_300[] = { 66, 0, + /* 0: MMU|FPU|SLB|RUN|DABR|NX, 1: CILRG|fri[nzpm]|DABRX|SPRG3|SLB0|PP110 */ + /* 2: VPM|DS205|PPR|DS202|DS206, 3: LSD|URG, 5: LE|CFAR|EB|LSQ */ + 0xf6, 0x3f, 0xc7, 0xc0, 0x00, 0xf0, /* 0 - 5 */ + /* 6: DS207 */ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */ + /* 16: Vector */ + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */ + /* 18: Vec. Scalar, 20: Vec. XOR, 22: HTM */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 18 - 23 */ + /* 24: Ext. Dec, 26: 64 bit ftrs, 28: PM ftrs */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 24 - 29 */ + /* 32: LE atomic, 34: EBB + ext EBB */ + 0x00, 0x00, 0x80, 0x00, 0xC0, 0x00, /* 30 - 35 */ + /* 40: Radix MMU */ + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 36 - 41 */ + /* 42: PM, 44: PC RA, 46: SC vec'd */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 42 - 47 */ + /* 48: SIMD, 50: QP BFP, 52: String */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 48 - 53 */ + /* 54: DecFP, 56: DecI, 58: SHA */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 54 - 59 */ + /* 60: NM atomic, 62: RNG */ + 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 60 - 65 */ +}; + static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt) { static const char compat[] = "ibm,power9-xscom\0ibm,xscom"; @@ -316,8 +380,12 @@ static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt) for (i = 0; i < chip->nr_cores; i++) { PnvCore *pnv_core = chip->cores[i]; + int offset; - pnv_dt_core(chip, pnv_core, fdt); + offset = pnv_dt_core(chip, pnv_core, fdt); + + _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", + pa_features_300, sizeof(pa_features_300)))); } if (chip->ram_size) { @@ -327,6 +395,40 @@ static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt) pnv_dt_lpc(chip, fdt, 0, PNV9_LPCM_BASE(chip), PNV9_LPCM_SIZE); } +/* + * Same as spapr pa_features_31 except pnv always enables CI largepages bit, + * always disables copy/paste. + */ +static const uint8_t pa_features_31[] = { 74, 0, + /* 0: MMU|FPU|SLB|RUN|DABR|NX, 1: CILRG|fri[nzpm]|DABRX|SPRG3|SLB0|PP110 */ + /* 2: VPM|DS205|PPR|DS202|DS206, 3: LSD|URG, 5: LE|CFAR|EB|LSQ */ + 0xf6, 0x3f, 0xc7, 0xc0, 0x00, 0xf0, /* 0 - 5 */ + /* 6: DS207 */ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */ + /* 16: Vector */ + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */ + /* 18: Vec. Scalar, 20: Vec. XOR */ + 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 18 - 23 */ + /* 24: Ext. Dec, 26: 64 bit ftrs, 28: PM ftrs */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 24 - 29 */ + /* 32: LE atomic, 34: EBB + ext EBB */ + 0x00, 0x00, 0x80, 0x00, 0xC0, 0x00, /* 30 - 35 */ + /* 40: Radix MMU */ + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 36 - 41 */ + /* 42: PM, 44: PC RA, 46: SC vec'd */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 42 - 47 */ + /* 48: SIMD, 50: QP BFP, 52: String */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 48 - 53 */ + /* 54: DecFP, 56: DecI, 58: SHA */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 54 - 59 */ + /* 60: NM atomic, 62: RNG */ + 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 60 - 65 */ + /* 68: DEXCR[SBHE|IBRTPDUS|SRAPD|NPHIE|PHIE] */ + 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, /* 66 - 71 */ + /* 72: [P]HASHST/[P]HASHCHK */ + 0x80, 0x00, /* 72 - 73 */ +}; + static void pnv_chip_power10_dt_populate(PnvChip *chip, void *fdt) { static const char compat[] = "ibm,power10-xscom\0ibm,xscom"; @@ -339,8 +441,12 @@ static void pnv_chip_power10_dt_populate(PnvChip *chip, void *fdt) for (i = 0; i < chip->nr_cores; i++) { PnvCore *pnv_core = chip->cores[i]; + int offset; - pnv_dt_core(chip, pnv_core, fdt); + offset = pnv_dt_core(chip, pnv_core, fdt); + + _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", + pa_features_31, sizeof(pa_features_31)))); } if (chip->ram_size) { @@ -573,13 +679,13 @@ static void pnv_powerdown_notify(Notifier *n, void *opaque) } } -static void pnv_reset(MachineState *machine) +static void pnv_reset(MachineState *machine, ShutdownCause reason) { PnvMachineState *pnv = PNV_MACHINE(machine); IPMIBmc *bmc; void *fdt; - qemu_devices_reset(); + qemu_devices_reset(reason); /* * The machine should provide by default an internal BMC simulator. @@ -608,7 +714,13 @@ 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); + /* + * Set machine->fdt for 'dumpdtb' QMP/HMP command. Free + * the existing machine->fdt to avoid leaking it during + * a reset. + */ + g_free(machine->fdt); + machine->fdt = fdt; } static ISABus *pnv_chip_power8_isa_create(PnvChip *chip, Error **errp) @@ -652,35 +764,33 @@ static ISABus *pnv_isa_create(PnvChip *chip, Error **errp) return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp); } -static int pnv_chip_power8_pic_print_info_child(Object *child, void *opaque) -{ - Monitor *mon = opaque; - PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3); - - if (phb3) { - pnv_phb3_msi_pic_print_info(&phb3->msis, mon); - ics_pic_print_info(&phb3->lsis, mon); - } - return 0; -} - 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); - object_child_foreach(OBJECT(chip), - pnv_chip_power8_pic_print_info_child, mon); + + for (i = 0; i < chip8->num_phbs; i++) { + PnvPHB *phb = chip8->phbs[i]; + PnvPHB3 *phb3 = PNV_PHB3(phb->backend); + + pnv_phb3_msi_pic_print_info(&phb3->msis, mon); + ics_pic_print_info(&phb3->lsis, mon); + } } static int pnv_chip_power9_pic_print_info_child(Object *child, void *opaque) { Monitor *mon = opaque; - PnvPHB4 *phb4 = (PnvPHB4 *) object_dynamic_cast(child, TYPE_PNV_PHB4); + PnvPHB *phb = (PnvPHB *) object_dynamic_cast(child, TYPE_PNV_PHB); - if (phb4) { - pnv_phb4_pic_print_info(phb4, mon); + if (!phb) { + return 0; } + + pnv_phb4_pic_print_info(PNV_PHB4(phb->backend), mon); + return 0; } @@ -720,7 +830,7 @@ static bool pnv_match_cpu(const char *default_type, const char *cpu_type) PowerPCCPUClass *ppc = POWERPC_CPU_CLASS(object_class_by_name(cpu_type)); - return ppc_default->pvr_match(ppc_default, ppc->pvr); + return ppc_default->pvr_match(ppc_default, ppc->pvr, false); } static void pnv_ipmi_bt_init(ISABus *bus, IPMIBmc *bmc, uint32_t irq) @@ -767,6 +877,7 @@ static void pnv_init(MachineState *machine) const char *bios_name = machine->firmware ?: FW_FILE_NAME; PnvMachineState *pnv = PNV_MACHINE(machine); MachineClass *mc = MACHINE_GET_CLASS(machine); + PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(machine); char *fw_filename; long fw_size; uint64_t chip_ram_start = 0; @@ -776,7 +887,8 @@ static void pnv_init(MachineState *machine) DeviceState *dev; if (kvm_enabled()) { - error_report("The powernv machine does not work with KVM acceleration"); + error_report("machine %s does not support the KVM accelerator", + mc->name); exit(EXIT_FAILURE); } @@ -863,6 +975,18 @@ static void pnv_init(MachineState *machine) pnv->num_chips = machine->smp.max_cpus / (machine->smp.cores * machine->smp.threads); + + if (machine->smp.threads > 8) { + error_report("Cannot support more than 8 threads/core " + "on a powernv machine"); + exit(1); + } + if (!is_power_of_2(machine->smp.threads)) { + error_report("Cannot support %d threads/core on a powernv" + "machine because it must be a power of 2", + machine->smp.threads); + exit(1); + } /* * TODO: should we decide on how many chips we can create based * on #cores and Venice vs. Murano vs. Naples chip type etc..., @@ -943,6 +1067,13 @@ static void pnv_init(MachineState *machine) */ pnv->powerdown_notifier.notify = pnv_powerdown_notify; qemu_register_powerdown_notifier(&pnv->powerdown_notifier); + + /* + * Create/Connect any machine-specific I2C devices + */ + if (pmc->i2c_init) { + pmc->i2c_init(pnv); + } } /* @@ -951,9 +1082,10 @@ static void pnv_init(MachineState *machine) * 25:28 Core number * 29:31 Thread ID */ -static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id) +static uint32_t pnv_chip_pir_p8(PnvChip *chip, uint32_t core_id, + uint32_t thread_id) { - return (chip->chip_id << 7) | (core_id << 3); + return (chip->chip_id << 7) | (core_id << 3) | thread_id; } static void pnv_chip_power8_intc_create(PnvChip *chip, PowerPCCPU *cpu, @@ -1005,14 +1137,37 @@ static void pnv_chip_power8_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, * * We only care about the lower bits. uint32_t is fine for the moment. */ -static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id) +static uint32_t pnv_chip_pir_p9(PnvChip *chip, uint32_t core_id, + uint32_t thread_id) { - return (chip->chip_id << 8) | (core_id << 2); + if (chip->nr_threads == 8) { + return (chip->chip_id << 8) | ((thread_id & 1) << 2) | (core_id << 3) | + (thread_id >> 1); + } else { + return (chip->chip_id << 8) | (core_id << 2) | thread_id; + } } -static uint32_t pnv_chip_core_pir_p10(PnvChip *chip, uint32_t core_id) +/* + * 0:48 Reserved - Read as zeroes + * 49:52 Node ID + * 53:55 Chip ID + * 56 Reserved - Read as zero + * 57:59 Quad ID + * 60 Core Chiplet Pair ID + * 61:63 Thread/Core Chiplet ID t0-t2 + * + * We only care about the lower bits. uint32_t is fine for the moment. + */ +static uint32_t pnv_chip_pir_p10(PnvChip *chip, uint32_t core_id, + uint32_t thread_id) { - return (chip->chip_id << 8) | (core_id << 2); + if (chip->nr_threads == 8) { + return (chip->chip_id << 8) | ((core_id / 4) << 4) | + ((core_id % 2) << 3) | thread_id; + } else { + return (chip->chip_id << 8) | (core_id << 2) | thread_id; + } } static void pnv_chip_power9_intc_create(PnvChip *chip, PowerPCCPU *cpu, @@ -1152,10 +1307,22 @@ static void pnv_chip_power8_instance_init(Object *obj) object_initialize_child(obj, "homer", &chip8->homer, TYPE_PNV8_HOMER); - chip8->num_phbs = pcc->num_phbs; + if (defaults_enabled()) { + chip8->num_phbs = pcc->num_phbs; - for (i = 0; i < chip8->num_phbs; i++) { - object_initialize_child(obj, "phb[*]", &chip8->phbs[i], TYPE_PNV_PHB3); + for (i = 0; i < chip8->num_phbs; i++) { + Object *phb = object_new(TYPE_PNV_PHB); + + /* + * We need the chip to parent the PHB to allow the DT + * to build correctly (via pnv_xscom_dt()). + * + * TODO: the PHB should be parented by a PEC device that, at + * this moment, is not modelled powernv8/phb3. + */ + object_property_add_child(obj, "phb[*]", phb); + chip8->phbs[i] = PNV_PHB(phb); + } } } @@ -1169,10 +1336,9 @@ static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp) name = g_strdup_printf("icp-%x", chip->chip_id); memory_region_init(&chip8->icp_mmio, OBJECT(chip), name, PNV_ICP_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(chip), &chip8->icp_mmio); g_free(name); - - sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip)); + memory_region_add_subregion(get_system_memory(), PNV_ICP_BASE(chip), + &chip8->icp_mmio); /* Map the ICP registers for each thread */ for (i = 0; i < chip->nr_cores; i++) { @@ -1180,7 +1346,7 @@ static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp) 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; + uint32_t pir = pcc->chip_pir(chip, core_hwid, j); PnvICPState *icp = PNV_ICP(xics_icp_get(chip8->xics, pir)); memory_region_add_subregion(&chip8->icp_mmio, pir << 12, @@ -1189,14 +1355,6 @@ static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp) } } -/* Attach a root port device */ -void pnv_phb_attach_root_port(PCIHostState *pci, const char *name) -{ - PCIDevice *root = pci_new(PCI_DEVFN(0, 0), name); - - pci_realize_and_unref(root, pci->bus, &error_fatal); -} - static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); @@ -1209,12 +1367,7 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) assert(chip8->xics); /* XSCOM bridge is first */ - pnv_xscom_realize(chip, PNV_XSCOM_SIZE, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip)); + pnv_xscom_init(chip, PNV_XSCOM_SIZE, PNV_XSCOM_BASE(chip)); pcc->parent_realize(dev, &local_err); if (local_err) { @@ -1223,11 +1376,11 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) } /* Processor Service Interface (PSI) Host Bridge */ - object_property_set_int(OBJECT(&chip8->psi), "bar", PNV_PSIHB_BASE(chip), + object_property_set_int(OBJECT(psi8), "bar", PNV_PSIHB_BASE(chip), &error_fatal); - object_property_set_link(OBJECT(&chip8->psi), ICS_PROP_XICS, + object_property_set_link(OBJECT(psi8), ICS_PROP_XICS, OBJECT(chip8->xics), &error_abort); - if (!qdev_realize(DEVICE(&chip8->psi), NULL, errp)) { + if (!qdev_realize(DEVICE(psi8), NULL, errp)) { return; } pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, @@ -1258,7 +1411,7 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) } pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip8->occ.xscom_regs); qdev_connect_gpio_out(DEVICE(&chip8->occ), 0, - qdev_get_gpio_in(DEVICE(&chip8->psi), PSIHB_IRQ_OCC)); + qdev_get_gpio_in(DEVICE(psi8), PSIHB_IRQ_OCC)); /* OCC SRAM model */ memory_region_add_subregion(get_system_memory(), PNV_OCC_SENSOR_BASE(chip), @@ -1277,9 +1430,9 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(get_system_memory(), PNV_HOMER_BASE(chip), &chip8->homer.regs); - /* PHB3 controllers */ + /* PHB controllers */ for (i = 0; i < chip8->num_phbs; i++) { - PnvPHB3 *phb = &chip8->phbs[i]; + PnvPHB *phb = chip8->phbs[i]; object_property_set_int(OBJECT(phb), "index", i, &error_fatal); object_property_set_int(OBJECT(phb), "chip-id", chip->chip_id, @@ -1306,7 +1459,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) 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->chip_pir = pnv_chip_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; @@ -1330,7 +1483,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) 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->chip_pir = pnv_chip_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; @@ -1354,7 +1507,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x120d304980000000ull; /* P8 Naples DD1.0 */ k->cores_mask = POWER8_CORE_MASK; k->num_phbs = 4; - k->core_pir = pnv_chip_core_pir_p8; + k->chip_pir = pnv_chip_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; @@ -1385,8 +1538,12 @@ static void pnv_chip_power9_instance_init(Object *obj) object_initialize_child(obj, "lpc", &chip9->lpc, TYPE_PNV9_LPC); + object_initialize_child(obj, "chiptod", &chip9->chiptod, TYPE_PNV9_CHIPTOD); + object_initialize_child(obj, "occ", &chip9->occ, TYPE_PNV9_OCC); + object_initialize_child(obj, "sbe", &chip9->sbe, TYPE_PNV9_SBE); + object_initialize_child(obj, "homer", &chip9->homer, TYPE_PNV9_HOMER); /* Number of PECs is the chip default */ @@ -1396,17 +1553,22 @@ static void pnv_chip_power9_instance_init(Object *obj) object_initialize_child(obj, "pec[*]", &chip9->pecs[i], TYPE_PNV_PHB4_PEC); } + + for (i = 0; i < pcc->i2c_num_engines; i++) { + object_initialize_child(obj, "i2c[*]", &chip9->i2c[i], TYPE_PNV_I2C); + } } static void pnv_chip_quad_realize_one(PnvChip *chip, PnvQuad *eq, - PnvCore *pnv_core) + PnvCore *pnv_core, + const char *type) { char eq_name[32]; int core_id = CPU_CORE(pnv_core)->core_id; snprintf(eq_name, sizeof(eq_name), "eq[%d]", core_id); object_initialize_child_with_props(OBJECT(chip), eq_name, eq, - sizeof(*eq), TYPE_PNV_QUAD, + sizeof(*eq), type, &error_fatal, NULL); object_property_set_int(OBJECT(eq), "quad-id", core_id, &error_fatal); @@ -1424,7 +1586,8 @@ static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp) for (i = 0; i < chip9->nr_quads; i++) { PnvQuad *eq = &chip9->quads[i]; - pnv_chip_quad_realize_one(chip, eq, chip->cores[i * 4]); + pnv_chip_quad_realize_one(chip, eq, chip->cores[i * 4], + PNV_QUAD_TYPE_NAME("power9")); pnv_xscom_add_subregion(chip, PNV9_XSCOM_EQ_BASE(eq->quad_id), &eq->xscom_regs); @@ -1466,14 +1629,10 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) PnvChip *chip = PNV_CHIP(dev); Pnv9Psi *psi9 = &chip9->psi; Error *local_err = NULL; + int i; /* XSCOM bridge is first */ - pnv_xscom_realize(chip, PNV9_XSCOM_SIZE, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV9_XSCOM_BASE(chip)); + pnv_xscom_init(chip, PNV9_XSCOM_SIZE, PNV9_XSCOM_BASE(chip)); pcc->parent_realize(dev, &local_err); if (local_err) { @@ -1505,12 +1664,12 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) &chip9->xive.xscom_regs); /* Processor Service Interface (PSI) Host Bridge */ - object_property_set_int(OBJECT(&chip9->psi), "bar", PNV9_PSIHB_BASE(chip), + object_property_set_int(OBJECT(psi9), "bar", PNV9_PSIHB_BASE(chip), &error_fatal); /* This is the only device with 4k ESB pages */ - object_property_set_int(OBJECT(&chip9->psi), "shift", XIVE_ESB_4K, + object_property_set_int(OBJECT(psi9), "shift", XIVE_ESB_4K, &error_fatal); - if (!qdev_realize(DEVICE(&chip9->psi), NULL, errp)) { + if (!qdev_realize(DEVICE(psi9), NULL, errp)) { return; } pnv_xscom_add_subregion(chip, PNV9_XSCOM_PSIHB_BASE, @@ -1527,18 +1686,42 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0", (uint64_t) PNV9_LPCM_BASE(chip)); + /* ChipTOD */ + object_property_set_bool(OBJECT(&chip9->chiptod), "primary", + chip->chip_id == 0, &error_abort); + object_property_set_bool(OBJECT(&chip9->chiptod), "secondary", + chip->chip_id == 1, &error_abort); + object_property_set_link(OBJECT(&chip9->chiptod), "chip", OBJECT(chip), + &error_abort); + if (!qdev_realize(DEVICE(&chip9->chiptod), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV9_XSCOM_CHIPTOD_BASE, + &chip9->chiptod.xscom_regs); + /* Create the simplified OCC model */ if (!qdev_realize(DEVICE(&chip9->occ), NULL, errp)) { return; } pnv_xscom_add_subregion(chip, PNV9_XSCOM_OCC_BASE, &chip9->occ.xscom_regs); qdev_connect_gpio_out(DEVICE(&chip9->occ), 0, qdev_get_gpio_in( - DEVICE(&chip9->psi), PSIHB9_IRQ_OCC)); + DEVICE(psi9), PSIHB9_IRQ_OCC)); /* OCC SRAM model */ memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip), &chip9->occ.sram_regs); + /* SBE */ + if (!qdev_realize(DEVICE(&chip9->sbe), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV9_XSCOM_SBE_CTRL_BASE, + &chip9->sbe.xscom_ctrl_regs); + pnv_xscom_add_subregion(chip, PNV9_XSCOM_SBE_MBOX_BASE, + &chip9->sbe.xscom_mbox_regs); + qdev_connect_gpio_out(DEVICE(&chip9->sbe), 0, qdev_get_gpio_in( + DEVICE(psi9), PSIHB9_IRQ_PSU)); + /* HOMER */ object_property_set_link(OBJECT(&chip9->homer), "chip", OBJECT(chip), &error_abort); @@ -1558,6 +1741,29 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) error_propagate(errp, local_err); return; } + + /* + * I2C + */ + for (i = 0; i < pcc->i2c_num_engines; i++) { + Object *obj = OBJECT(&chip9->i2c[i]); + + object_property_set_int(obj, "engine", i + 1, &error_fatal); + object_property_set_int(obj, "num-busses", + pcc->i2c_ports_per_engine[i], + &error_fatal); + object_property_set_link(obj, "chip", OBJECT(chip), &error_abort); + if (!qdev_realize(DEVICE(obj), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV9_XSCOM_I2CM_BASE + + (chip9->i2c[i].engine - 1) * + PNV9_XSCOM_I2CM_SIZE, + &chip9->i2c[i].xscom_regs); + qdev_connect_gpio_out(DEVICE(&chip9->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(psi9), + PSIHB9_IRQ_SBE_I2C)); + } } static uint32_t pnv_chip_power9_xscom_pcba(PnvChip *chip, uint64_t addr) @@ -1570,10 +1776,11 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); + static const int i2c_ports_per_engine[PNV9_CHIP_MAX_I2C] = {2, 13, 2, 2}; k->chip_cfam_id = 0x220d104900008000ull; /* P9 Nimbus DD2.0 */ k->cores_mask = POWER9_CORE_MASK; - k->core_pir = pnv_chip_core_pir_p9; + k->chip_pir = pnv_chip_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; @@ -1585,6 +1792,8 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) k->xscom_pcba = pnv_chip_power9_xscom_pcba; dc->desc = "PowerNV Chip POWER9"; k->num_pecs = PNV9_CHIP_MAX_PEC; + k->i2c_num_engines = PNV9_CHIP_MAX_I2C; + k->i2c_ports_per_engine = i2c_ports_per_engine; device_class_set_parent_realize(dc, pnv_chip_power9_realize, &k->parent_realize); @@ -1602,8 +1811,13 @@ static void pnv_chip_power10_instance_init(Object *obj) "xive-fabric"); object_initialize_child(obj, "psi", &chip10->psi, TYPE_PNV10_PSI); object_initialize_child(obj, "lpc", &chip10->lpc, TYPE_PNV10_LPC); + object_initialize_child(obj, "chiptod", &chip10->chiptod, + TYPE_PNV10_CHIPTOD); object_initialize_child(obj, "occ", &chip10->occ, TYPE_PNV10_OCC); + object_initialize_child(obj, "sbe", &chip10->sbe, TYPE_PNV10_SBE); object_initialize_child(obj, "homer", &chip10->homer, TYPE_PNV10_HOMER); + object_initialize_child(obj, "n1-chiplet", &chip10->n1_chiplet, + TYPE_PNV_N1_CHIPLET); chip->num_pecs = pcc->num_pecs; @@ -1611,6 +1825,10 @@ static void pnv_chip_power10_instance_init(Object *obj) object_initialize_child(obj, "pec[*]", &chip10->pecs[i], TYPE_PNV_PHB5_PEC); } + + for (i = 0; i < pcc->i2c_num_engines; i++) { + object_initialize_child(obj, "i2c[*]", &chip10->i2c[i], TYPE_PNV_I2C); + } } static void pnv_chip_power10_quad_realize(Pnv10Chip *chip10, Error **errp) @@ -1624,10 +1842,14 @@ static void pnv_chip_power10_quad_realize(Pnv10Chip *chip10, Error **errp) for (i = 0; i < chip10->nr_quads; i++) { PnvQuad *eq = &chip10->quads[i]; - pnv_chip_quad_realize_one(chip, eq, chip->cores[i * 4]); + pnv_chip_quad_realize_one(chip, eq, chip->cores[i * 4], + PNV_QUAD_TYPE_NAME("power10")); pnv_xscom_add_subregion(chip, PNV10_XSCOM_EQ_BASE(eq->quad_id), &eq->xscom_regs); + + pnv_xscom_add_subregion(chip, PNV10_XSCOM_QME_BASE(eq->quad_id), + &eq->xscom_qme_regs); } } @@ -1665,14 +1887,10 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) PnvChip *chip = PNV_CHIP(dev); Pnv10Chip *chip10 = PNV10_CHIP(dev); Error *local_err = NULL; + int i; /* 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)); + pnv_xscom_init(chip, PNV10_XSCOM_SIZE, PNV10_XSCOM_BASE(chip)); pcc->parent_realize(dev, &local_err); if (local_err) { @@ -1730,6 +1948,19 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0", (uint64_t) PNV10_LPCM_BASE(chip)); + /* ChipTOD */ + object_property_set_bool(OBJECT(&chip10->chiptod), "primary", + chip->chip_id == 0, &error_abort); + object_property_set_bool(OBJECT(&chip10->chiptod), "secondary", + chip->chip_id == 1, &error_abort); + object_property_set_link(OBJECT(&chip10->chiptod), "chip", OBJECT(chip), + &error_abort); + if (!qdev_realize(DEVICE(&chip10->chiptod), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_CHIPTOD_BASE, + &chip10->chiptod.xscom_regs); + /* Create the simplified OCC model */ if (!qdev_realize(DEVICE(&chip10->occ), NULL, errp)) { return; @@ -1744,6 +1975,17 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) PNV10_OCC_SENSOR_BASE(chip), &chip10->occ.sram_regs); + /* SBE */ + if (!qdev_realize(DEVICE(&chip10->sbe), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_SBE_CTRL_BASE, + &chip10->sbe.xscom_ctrl_regs); + pnv_xscom_add_subregion(chip, PNV10_XSCOM_SBE_MBOX_BASE, + &chip10->sbe.xscom_mbox_regs); + qdev_connect_gpio_out(DEVICE(&chip10->sbe), 0, qdev_get_gpio_in( + DEVICE(&chip10->psi), PSIHB9_IRQ_PSU)); + /* HOMER */ object_property_set_link(OBJECT(&chip10->homer), "chip", OBJECT(chip), &error_abort); @@ -1758,12 +2000,82 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(get_system_memory(), PNV10_HOMER_BASE(chip), &chip10->homer.regs); + /* N1 chiplet */ + if (!qdev_realize(DEVICE(&chip10->n1_chiplet), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_N1_CHIPLET_CTRL_REGS_BASE, + &chip10->n1_chiplet.nest_pervasive.xscom_ctrl_regs_mr); + + pnv_xscom_add_subregion(chip, PNV10_XSCOM_N1_PB_SCOM_EQ_BASE, + &chip10->n1_chiplet.xscom_pb_eq_mr); + + pnv_xscom_add_subregion(chip, PNV10_XSCOM_N1_PB_SCOM_ES_BASE, + &chip10->n1_chiplet.xscom_pb_es_mr); + /* PHBs */ pnv_chip_power10_phb_realize(chip, &local_err); if (local_err) { error_propagate(errp, local_err); return; } + + + /* + * I2C + */ + for (i = 0; i < pcc->i2c_num_engines; i++) { + Object *obj = OBJECT(&chip10->i2c[i]); + + object_property_set_int(obj, "engine", i + 1, &error_fatal); + object_property_set_int(obj, "num-busses", + pcc->i2c_ports_per_engine[i], + &error_fatal); + object_property_set_link(obj, "chip", OBJECT(chip), &error_abort); + if (!qdev_realize(DEVICE(obj), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_I2CM_BASE + + (chip10->i2c[i].engine - 1) * + PNV10_XSCOM_I2CM_SIZE, + &chip10->i2c[i].xscom_regs); + qdev_connect_gpio_out(DEVICE(&chip10->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(&chip10->psi), + PSIHB9_IRQ_SBE_I2C)); + } + +} + +static void pnv_rainier_i2c_init(PnvMachineState *pnv) +{ + int i; + for (i = 0; i < pnv->num_chips; i++) { + Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]); + + /* + * Add a PCA9552 I2C device for PCIe hotplug control + * to engine 2, bus 1, address 0x63 + */ + I2CSlave *dev = i2c_slave_create_simple(chip10->i2c[2].busses[1], + "pca9552", 0x63); + + /* + * Connect PCA9552 GPIO pins 0-4 (SLOTx_EN) outputs to GPIO pins 5-9 + * (SLOTx_PG) inputs in order to fake the pgood state of PCIe slots + * after hypervisor code sets a SLOTx_EN pin high. + */ + qdev_connect_gpio_out(DEVICE(dev), 0, qdev_get_gpio_in(DEVICE(dev), 5)); + qdev_connect_gpio_out(DEVICE(dev), 1, qdev_get_gpio_in(DEVICE(dev), 6)); + qdev_connect_gpio_out(DEVICE(dev), 2, qdev_get_gpio_in(DEVICE(dev), 7)); + qdev_connect_gpio_out(DEVICE(dev), 3, qdev_get_gpio_in(DEVICE(dev), 8)); + qdev_connect_gpio_out(DEVICE(dev), 4, qdev_get_gpio_in(DEVICE(dev), 9)); + + /* + * Add a PCA9554 I2C device for cable card presence detection + * to engine 2, bus 1, address 0x25 + */ + i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9554", 0x25); + } } static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr) @@ -1776,10 +2088,11 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); + static const int i2c_ports_per_engine[PNV10_CHIP_MAX_I2C] = {14, 14, 2, 16}; 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->chip_pir = pnv_chip_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; @@ -1791,6 +2104,8 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) k->xscom_pcba = pnv_chip_power10_xscom_pcba; dc->desc = "PowerNV Chip POWER10"; k->num_pecs = PNV10_CHIP_MAX_PEC; + k->i2c_num_engines = PNV10_CHIP_MAX_I2C; + k->i2c_ports_per_engine = i2c_ports_per_engine; device_class_set_parent_realize(dc, pnv_chip_power10_realize, &k->parent_realize); @@ -1867,8 +2182,8 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp) chip->nr_threads, &error_fatal); object_property_set_int(OBJECT(pnv_core), CPU_CORE_PROP_CORE_ID, core_hwid, &error_fatal); - object_property_set_int(OBJECT(pnv_core), "pir", - pcc->core_pir(chip, core_hwid), &error_fatal); + object_property_set_int(OBJECT(pnv_core), "hwid", core_hwid, + &error_fatal); object_property_set_int(OBJECT(pnv_core), "hrmor", pnv->fw_load_addr, &error_fatal); object_property_set_link(OBJECT(pnv_core), "chip", OBJECT(chip), @@ -1917,6 +2232,21 @@ static void pnv_chip_class_init(ObjectClass *klass, void *data) dc->desc = "PowerNV Chip"; } +PnvCore *pnv_chip_find_core(PnvChip *chip, uint32_t core_id) +{ + int i; + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pc = chip->cores[i]; + CPUCore *cc = CPU_CORE(pc); + + if (cc->core_id == core_id) { + return pc; + } + } + return NULL; +} + PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir) { int i, j; @@ -1934,44 +2264,29 @@ PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir) return NULL; } -typedef struct ForeachPhb3Args { - int irq; - ICSState *ics; -} ForeachPhb3Args; - -static int pnv_ics_get_child(Object *child, void *opaque) -{ - ForeachPhb3Args *args = opaque; - PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3); - - if (phb3) { - if (ics_valid_irq(&phb3->lsis, args->irq)) { - args->ics = &phb3->lsis; - } - if (ics_valid_irq(ICS(&phb3->msis), args->irq)) { - args->ics = ICS(&phb3->msis); - } - } - return args->ics ? 1 : 0; -} - static ICSState *pnv_ics_get(XICSFabric *xi, int irq) { PnvMachineState *pnv = PNV_MACHINE(xi); - ForeachPhb3Args args = { irq, NULL }; - 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; } - object_child_foreach(OBJECT(chip), pnv_ics_get_child, &args); - if (args.ics) { - return args.ics; + for (j = 0; j < chip8->num_phbs; j++) { + PnvPHB *phb = chip8->phbs[j]; + PnvPHB3 *phb3 = PNV_PHB3(phb->backend); + + if (ics_valid_irq(&phb3->lsis, irq)) { + return &phb3->lsis; + } + + if (ics_valid_irq(ICS(&phb3->msis), irq)) { + return ICS(&phb3->msis); + } } } return NULL; @@ -1990,28 +2305,23 @@ PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id) return NULL; } -static int pnv_ics_resend_child(Object *child, void *opaque) -{ - PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3); - - if (phb3) { - ics_resend(&phb3->lsis); - ics_resend(ICS(&phb3->msis)); - } - return 0; -} - 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); - object_child_foreach(OBJECT(chip), pnv_ics_resend_child, NULL); + + for (j = 0; j < chip8->num_phbs; j++) { + PnvPHB *phb = chip8->phbs[j]; + PnvPHB3 *phb3 = PNV_PHB3(phb->backend); + + ics_resend(&phb3->lsis); + ics_resend(ICS(&phb3->msis)); + } } } @@ -2107,8 +2417,14 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); static const char compat[] = "qemu,powernv8\0qemu,powernv\0ibm,powernv"; + static GlobalProperty phb_compat[] = { + { TYPE_PNV_PHB, "version", "3" }, + { TYPE_PNV_PHB_ROOT_PORT, "version", "3" }, + }; + mc->desc = "IBM PowerNV (Non-Virtualized) POWER8"; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0"); + compat_props_add(mc->compat_props, phb_compat, G_N_ELEMENTS(phb_compat)); xic->icp_get = pnv_icp_get; xic->ics_get = pnv_ics_get; @@ -2116,6 +2432,8 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); + + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); } static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) @@ -2125,32 +2443,66 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) 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; + static GlobalProperty phb_compat[] = { + { TYPE_PNV_PHB, "version", "4" }, + { TYPE_PNV_PHB_ROOT_PORT, "version", "4" }, + }; - mc->alias = "powernv"; + mc->desc = "IBM PowerNV (Non-Virtualized) POWER9"; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.2"); + compat_props_add(mc->compat_props, phb_compat, G_N_ELEMENTS(phb_compat)); + + xfc->match_nvt = pnv_match_nvt; pmc->compat = compat; pmc->compat_size = sizeof(compat); pmc->dt_power_mgt = pnv_dt_power_mgt; + + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); } -static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) +static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc); static const char compat[] = "qemu,powernv10\0ibm,powernv"; - mc->desc = "IBM PowerNV (Non-Virtualized) POWER10"; + static GlobalProperty phb_compat[] = { + { TYPE_PNV_PHB, "version", "5" }, + { TYPE_PNV_PHB_ROOT_PORT, "version", "5" }, + }; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power10_v2.0"); + compat_props_add(mc->compat_props, phb_compat, G_N_ELEMENTS(phb_compat)); + + mc->alias = "powernv"; pmc->compat = compat; pmc->compat_size = sizeof(compat); pmc->dt_power_mgt = pnv_dt_power_mgt; xfc->match_nvt = pnv10_xive_match_nvt; + + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); +} + +static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + pnv_machine_p10_common_class_init(oc, data); + mc->desc = "IBM PowerNV (Non-Virtualized) POWER10"; +} + +static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); + + pnv_machine_p10_common_class_init(oc, data); + mc->desc = "IBM PowerNV (Non-Virtualized) POWER10 Rainier"; + pmc->i2c_init = pnv_rainier_i2c_init; } static bool pnv_machine_get_hb(Object *obj, Error **errp) @@ -2171,8 +2523,7 @@ static void pnv_machine_set_hb(Object *obj, bool value, Error **errp) static void pnv_cpu_do_nmi_on_cpu(CPUState *cs, run_on_cpu_data arg) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); cpu_synchronize_state(cs); ppc_cpu_do_system_reset(cs); @@ -2258,6 +2609,11 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data) } static const TypeInfo types[] = { + { + .name = MACHINE_TYPE_NAME("powernv10-rainier"), + .parent = MACHINE_TYPE_NAME("powernv10"), + .class_init = pnv_machine_p10_rainier_class_init, + }, { .name = MACHINE_TYPE_NAME("powernv10"), .parent = TYPE_PNV_MACHINE, diff --git a/hw/ppc/pnv_bmc.c b/hw/ppc/pnv_bmc.c index 99f1e8d7f9..0c1274df21 100644 --- a/hw/ppc/pnv_bmc.c +++ b/hw/ppc/pnv_bmc.c @@ -269,13 +269,13 @@ void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor) */ IPMIBmc *pnv_bmc_create(PnvPnor *pnor) { - Object *obj; + DeviceState *dev; - obj = object_new(TYPE_IPMI_BMC_SIMULATOR); - qdev_realize(DEVICE(obj), NULL, &error_fatal); - pnv_bmc_set_pnor(IPMI_BMC(obj), pnor); + dev = qdev_new(TYPE_IPMI_BMC_SIMULATOR); + qdev_realize(dev, NULL, &error_fatal); + pnv_bmc_set_pnor(IPMI_BMC(dev), pnor); - return IPMI_BMC(obj); + return IPMI_BMC(dev); } typedef struct ForeachArgs { diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c new file mode 100644 index 0000000000..3831a72101 --- /dev/null +++ b/hw/ppc/pnv_chiptod.c @@ -0,0 +1,586 @@ +/* + * QEMU PowerPC PowerNV Emulation of some ChipTOD behaviour + * + * Copyright (c) 2022-2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * ChipTOD (aka TOD) is a facility implemented in the nest / pervasive. The + * purpose is to keep time-of-day across chips and cores. + * + * There is a master chip TOD, which sends signals to slave chip TODs to + * keep them synchronized. There are two sets of configuration registers + * called primary and secondary, which can be used fail over. + * + * The chip TOD also distributes synchronisation signals to the timebase + * facility in each of the cores on the chip. In particular there is a + * feature that can move the TOD value in the ChipTOD to and from the TB. + * + * Initialisation typically brings all ChipTOD into sync (see tod_state), + * and then brings each core TB into sync with the ChipTODs (see timebase + * state and TFMR). This model is a very basic simulation of the init sequence + * performed by skiboot. + */ + +#include "qemu/osdep.h" +#include "sysemu/reset.h" +#include "target/ppc/cpu.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/ppc/fdt.h" +#include "hw/ppc/ppc.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_chip.h" +#include "hw/ppc/pnv_core.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_chiptod.h" +#include "trace.h" + +#include + +/* TOD chip XSCOM addresses */ +#define TOD_M_PATH_CTRL_REG 0x00000000 /* Master Path ctrl reg */ +#define TOD_PRI_PORT_0_CTRL_REG 0x00000001 /* Primary port0 ctrl reg */ +#define TOD_PRI_PORT_1_CTRL_REG 0x00000002 /* Primary port1 ctrl reg */ +#define TOD_SEC_PORT_0_CTRL_REG 0x00000003 /* Secondary p0 ctrl reg */ +#define TOD_SEC_PORT_1_CTRL_REG 0x00000004 /* Secondary p1 ctrl reg */ +#define TOD_S_PATH_CTRL_REG 0x00000005 /* Slave Path ctrl reg */ +#define TOD_I_PATH_CTRL_REG 0x00000006 /* Internal Path ctrl reg */ + +/* -- TOD primary/secondary master/slave control register -- */ +#define TOD_PSS_MSS_CTRL_REG 0x00000007 + +/* -- TOD primary/secondary master/slave status register -- */ +#define TOD_PSS_MSS_STATUS_REG 0x00000008 + +/* TOD chip XSCOM addresses */ +#define TOD_CHIP_CTRL_REG 0x00000010 /* Chip control reg */ + +#define TOD_TX_TTYPE_0_REG 0x00000011 +#define TOD_TX_TTYPE_1_REG 0x00000012 /* PSS switch reg */ +#define TOD_TX_TTYPE_2_REG 0x00000013 /* Enable step checkers */ +#define TOD_TX_TTYPE_3_REG 0x00000014 /* Request TOD reg */ +#define TOD_TX_TTYPE_4_REG 0x00000015 /* Send TOD reg */ +#define TOD_TX_TTYPE_5_REG 0x00000016 /* Invalidate TOD reg */ + +#define TOD_MOVE_TOD_TO_TB_REG 0x00000017 +#define TOD_LOAD_TOD_MOD_REG 0x00000018 +#define TOD_LOAD_TOD_REG 0x00000021 +#define TOD_START_TOD_REG 0x00000022 +#define TOD_FSM_REG 0x00000024 + +#define TOD_TX_TTYPE_CTRL_REG 0x00000027 /* TX TTYPE Control reg */ +#define TOD_TX_TTYPE_PIB_SLAVE_ADDR PPC_BITMASK(26, 31) + +/* -- TOD Error interrupt register -- */ +#define TOD_ERROR_REG 0x00000030 + +/* PC unit PIB address which recieves the timebase transfer from TOD */ +#define PC_TOD 0x4A3 + +/* + * The TOD FSM: + * - The reset state is 0 error. + * - A hardware error detected will transition to state 0 from any state. + * - LOAD_TOD_MOD and TTYPE5 will transition to state 7 from any state. + * + * | state | action | new | + * |------------+------------------------------+-----| + * | 0 error | LOAD_TOD_MOD | 7 | + * | 0 error | Recv TTYPE5 (invalidate TOD) | 7 | + * | 7 not_set | LOAD_TOD (bit-63 = 0) | 2 | + * | 7 not_set | LOAD_TOD (bit-63 = 1) | 1 | + * | 7 not_set | Recv TTYPE4 (send TOD) | 2 | + * | 2 running | | | + * | 1 stopped | START_TOD | 2 | + * + * Note the hardware has additional states but they relate to the sending + * and receiving and waiting on synchronisation signals between chips and + * are not described or modeled here. + */ + +static uint64_t pnv_chiptod_xscom_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvChipTOD *chiptod = PNV_CHIPTOD(opaque); + uint32_t offset = addr >> 3; + uint64_t val = 0; + + switch (offset) { + case TOD_PSS_MSS_STATUS_REG: + /* + * ChipTOD does not support configurations other than primary + * master, does not support errors, etc. + */ + val |= PPC_BITMASK(6, 10); /* STEP checker validity */ + val |= PPC_BIT(12); /* Primary config master path select */ + if (chiptod->tod_state == tod_running) { + val |= PPC_BIT(20); /* Is running */ + } + val |= PPC_BIT(21); /* Is using primary config */ + val |= PPC_BIT(26); /* Is using master path select */ + + if (chiptod->primary) { + val |= PPC_BIT(23); /* Is active master */ + } else if (chiptod->secondary) { + val |= PPC_BIT(24); /* Is backup master */ + } else { + val |= PPC_BIT(25); /* Is slave (should backup master set this?) */ + } + break; + case TOD_PSS_MSS_CTRL_REG: + val = chiptod->pss_mss_ctrl_reg; + break; + case TOD_TX_TTYPE_CTRL_REG: + val = 0; + break; + case TOD_ERROR_REG: + val = chiptod->tod_error; + break; + case TOD_FSM_REG: + if (chiptod->tod_state == tod_running) { + val |= PPC_BIT(4); + } + break; + default: + qemu_log_mask(LOG_UNIMP, "pnv_chiptod: unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } + + trace_pnv_chiptod_xscom_read(addr >> 3, val); + + return val; +} + +static void chiptod_receive_ttype(PnvChipTOD *chiptod, uint32_t trigger) +{ + switch (trigger) { + case TOD_TX_TTYPE_4_REG: + if (chiptod->tod_state != tod_not_set) { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: received TTYPE4 in " + " state %d, should be in 7 (TOD_NOT_SET)\n", + chiptod->tod_state); + } else { + chiptod->tod_state = tod_running; + } + break; + case TOD_TX_TTYPE_5_REG: + /* Works from any state */ + chiptod->tod_state = tod_not_set; + break; + default: + qemu_log_mask(LOG_UNIMP, "pnv_chiptod: received unimplemented " + " TTYPE %u\n", trigger); + break; + } +} + +static void chiptod_power9_broadcast_ttype(PnvChipTOD *sender, + uint32_t trigger) +{ + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + Pnv9Chip *chip9 = PNV9_CHIP(pnv->chips[i]); + PnvChipTOD *chiptod = &chip9->chiptod; + + if (chiptod != sender) { + chiptod_receive_ttype(chiptod, trigger); + } + } +} + +static void chiptod_power10_broadcast_ttype(PnvChipTOD *sender, + uint32_t trigger) +{ + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]); + PnvChipTOD *chiptod = &chip10->chiptod; + + if (chiptod != sender) { + chiptod_receive_ttype(chiptod, trigger); + } + } +} + +static PnvCore *pnv_chip_get_core_by_xscom_base(PnvChip *chip, + uint32_t xscom_base) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + int i; + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pc = chip->cores[i]; + CPUCore *cc = CPU_CORE(pc); + int core_hwid = cc->core_id; + + if (pcc->xscom_core_base(chip, core_hwid) == xscom_base) { + return pc; + } + } + return NULL; +} + +static PnvCore *chiptod_power9_tx_ttype_target(PnvChipTOD *chiptod, + uint64_t val) +{ + /* + * skiboot uses Core ID for P9, though SCOM should work too. + */ + if (val & PPC_BIT(35)) { /* SCOM addressing */ + uint32_t addr = val >> 32; + uint32_t reg = addr & 0xfff; + + if (reg != PC_TOD) { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: SCOM addressing: " + "unimplemented slave register 0x%" PRIx32 "\n", reg); + return NULL; + } + + return pnv_chip_get_core_by_xscom_base(chiptod->chip, addr & ~0xfff); + + } else { /* Core ID addressing */ + uint32_t core_id = GETFIELD(TOD_TX_TTYPE_PIB_SLAVE_ADDR, val) & 0x1f; + return pnv_chip_find_core(chiptod->chip, core_id); + } +} + +static PnvCore *chiptod_power10_tx_ttype_target(PnvChipTOD *chiptod, + uint64_t val) +{ + /* + * skiboot uses SCOM for P10 because Core ID was unable to be made to + * work correctly. For this reason only SCOM addressing is implemented. + */ + if (val & PPC_BIT(35)) { /* SCOM addressing */ + uint32_t addr = val >> 32; + uint32_t reg = addr & 0xfff; + + if (reg != PC_TOD) { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: SCOM addressing: " + "unimplemented slave register 0x%" PRIx32 "\n", reg); + return NULL; + } + + /* + * This may not deal with P10 big-core addressing at the moment. + * The big-core code in skiboot syncs small cores, but it targets + * the even PIR (first small-core) when syncing second small-core. + */ + return pnv_chip_get_core_by_xscom_base(chiptod->chip, addr & ~0xfff); + + } else { /* Core ID addressing */ + qemu_log_mask(LOG_UNIMP, "pnv_chiptod: TX TTYPE Core ID " + "addressing is not implemented for POWER10\n"); + return NULL; + } +} + +static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvChipTOD *chiptod = PNV_CHIPTOD(opaque); + PnvChipTODClass *pctc = PNV_CHIPTOD_GET_CLASS(chiptod); + uint32_t offset = addr >> 3; + + trace_pnv_chiptod_xscom_write(addr >> 3, val); + + switch (offset) { + case TOD_PSS_MSS_CTRL_REG: + /* Is this correct? */ + if (chiptod->primary) { + val |= PPC_BIT(1); /* TOD is master */ + } else { + val &= ~PPC_BIT(1); + } + val |= PPC_BIT(2); /* Drawer is master (don't simulate multi-drawer) */ + chiptod->pss_mss_ctrl_reg = val & PPC_BITMASK(0, 31); + break; + + case TOD_TX_TTYPE_CTRL_REG: + /* + * This register sets the target of the TOD value transfer initiated + * by TOD_MOVE_TOD_TO_TB. The TOD is able to send the address to + * any target register, though in practice only the PC TOD register + * should be used. ChipTOD has a "SCOM addressing" mode which fully + * specifies the SCOM address, and a core-ID mode which uses the + * core ID to target the PC TOD for a given core. + */ + chiptod->slave_pc_target = pctc->tx_ttype_target(chiptod, val); + if (!chiptod->slave_pc_target) { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg" + " TOD_TX_TTYPE_CTRL_REG val 0x%" PRIx64 + " invalid slave address\n", val); + } + break; + case TOD_ERROR_REG: + chiptod->tod_error &= ~val; + break; + case TOD_LOAD_TOD_MOD_REG: + if (!(val & PPC_BIT(0))) { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg" + " TOD_LOAD_TOD_MOD_REG with bad val 0x%" PRIx64"\n", + val); + } else { + chiptod->tod_state = tod_not_set; + } + break; + case TOD_LOAD_TOD_REG: + if (chiptod->tod_state != tod_not_set) { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: LOAD_TOG_REG in " + " state %d, should be in 7 (TOD_NOT_SET)\n", + chiptod->tod_state); + } else { + if (val & PPC_BIT(63)) { + chiptod->tod_state = tod_stopped; + } else { + chiptod->tod_state = tod_running; + } + } + break; + + case TOD_MOVE_TOD_TO_TB_REG: + /* + * XXX: it should be a cleaner model to have this drive a SCOM + * transaction to the target address, and implement the state machine + * in the PnvCore. For now, this hack makes things work. + */ + if (chiptod->tod_state != tod_running) { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg" + " TOD_MOVE_TOD_TO_TB_REG in bad state %d\n", + chiptod->tod_state); + } else if (!(val & PPC_BIT(0))) { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg" + " TOD_MOVE_TOD_TO_TB_REG with bad val 0x%" PRIx64"\n", + val); + } else if (chiptod->slave_pc_target == NULL) { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg" + " TOD_MOVE_TOD_TO_TB_REG with no slave target\n"); + } else { + PowerPCCPU *cpu = chiptod->slave_pc_target->threads[0]; + CPUPPCState *env = &cpu->env; + + /* + * Moving TOD to TB will set the TB of all threads in a + * core, so skiboot only does this once per thread0, so + * that is where we keep the timebase state machine. + * + * It is likely possible for TBST to be driven from other + * threads in the core, but for now we only implement it for + * thread 0. + */ + + if (env->pnv_tod_tbst.tb_ready_for_tod) { + env->pnv_tod_tbst.tod_sent_to_tb = 1; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg" + " TOD_MOVE_TOD_TO_TB_REG with TB not ready to" + " receive TOD\n"); + } + } + break; + case TOD_START_TOD_REG: + if (chiptod->tod_state != tod_stopped) { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: LOAD_TOG_REG in " + " state %d, should be in 1 (TOD_STOPPED)\n", + chiptod->tod_state); + } else { + chiptod->tod_state = tod_running; + } + break; + case TOD_TX_TTYPE_4_REG: + case TOD_TX_TTYPE_5_REG: + pctc->broadcast_ttype(chiptod, offset); + break; + default: + qemu_log_mask(LOG_UNIMP, "pnv_chiptod: unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } +} + +static const MemoryRegionOps pnv_chiptod_xscom_ops = { + .read = pnv_chiptod_xscom_read, + .write = pnv_chiptod_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_chiptod_dt_xscom(PnvXScomInterface *dev, void *fdt, + int xscom_offset, + const char compat[], size_t compat_size) +{ + PnvChipTOD *chiptod = PNV_CHIPTOD(dev); + g_autofree char *name = NULL; + int offset; + uint32_t chiptod_pcba = PNV9_XSCOM_CHIPTOD_BASE; + uint32_t reg[] = { + cpu_to_be32(chiptod_pcba), + cpu_to_be32(PNV9_XSCOM_CHIPTOD_SIZE) + }; + + name = g_strdup_printf("chiptod@%x", chiptod_pcba); + offset = fdt_add_subnode(fdt, xscom_offset, name); + _FDT(offset); + + if (chiptod->primary) { + _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0))); + } else if (chiptod->secondary) { + _FDT((fdt_setprop(fdt, offset, "secondary", NULL, 0))); + } + + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); + _FDT((fdt_setprop(fdt, offset, "compatible", compat, compat_size))); + return 0; +} + +static int pnv_chiptod_power9_dt_xscom(PnvXScomInterface *dev, void *fdt, + int xscom_offset) +{ + const char compat[] = "ibm,power-chiptod\0ibm,power9-chiptod"; + + return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat)); +} + +static Property pnv_chiptod_properties[] = { + DEFINE_PROP_BOOL("primary", PnvChipTOD, primary, false), + DEFINE_PROP_BOOL("secondary", PnvChipTOD, secondary, false), + DEFINE_PROP_LINK("chip", PnvChipTOD , chip, TYPE_PNV_CHIP, PnvChip *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_chiptod_power9_class_init(ObjectClass *klass, void *data) +{ + PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); + + dc->desc = "PowerNV ChipTOD Controller (POWER9)"; + device_class_set_props(dc, pnv_chiptod_properties); + + xdc->dt_xscom = pnv_chiptod_power9_dt_xscom; + + pctc->broadcast_ttype = chiptod_power9_broadcast_ttype; + pctc->tx_ttype_target = chiptod_power9_tx_ttype_target; + + pctc->xscom_size = PNV_XSCOM_CHIPTOD_SIZE; +} + +static const TypeInfo pnv_chiptod_power9_type_info = { + .name = TYPE_PNV9_CHIPTOD, + .parent = TYPE_PNV_CHIPTOD, + .instance_size = sizeof(PnvChipTOD), + .class_init = pnv_chiptod_power9_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static int pnv_chiptod_power10_dt_xscom(PnvXScomInterface *dev, void *fdt, + int xscom_offset) +{ + const char compat[] = "ibm,power-chiptod\0ibm,power10-chiptod"; + + return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat)); +} + +static void pnv_chiptod_power10_class_init(ObjectClass *klass, void *data) +{ + PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); + + dc->desc = "PowerNV ChipTOD Controller (POWER10)"; + device_class_set_props(dc, pnv_chiptod_properties); + + xdc->dt_xscom = pnv_chiptod_power10_dt_xscom; + + pctc->broadcast_ttype = chiptod_power10_broadcast_ttype; + pctc->tx_ttype_target = chiptod_power10_tx_ttype_target; + + pctc->xscom_size = PNV_XSCOM_CHIPTOD_SIZE; +} + +static const TypeInfo pnv_chiptod_power10_type_info = { + .name = TYPE_PNV10_CHIPTOD, + .parent = TYPE_PNV_CHIPTOD, + .instance_size = sizeof(PnvChipTOD), + .class_init = pnv_chiptod_power10_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_chiptod_reset(void *dev) +{ + PnvChipTOD *chiptod = PNV_CHIPTOD(dev); + + chiptod->pss_mss_ctrl_reg = 0; + if (chiptod->primary) { + chiptod->pss_mss_ctrl_reg |= PPC_BIT(1); /* TOD is master */ + } + /* Drawer is master (we do not simulate multi-drawer) */ + chiptod->pss_mss_ctrl_reg |= PPC_BIT(2); + + chiptod->tod_error = 0; + chiptod->tod_state = tod_error; +} + +static void pnv_chiptod_realize(DeviceState *dev, Error **errp) +{ + PnvChipTOD *chiptod = PNV_CHIPTOD(dev); + PnvChipTODClass *pctc = PNV_CHIPTOD_GET_CLASS(chiptod); + + /* XScom regions for ChipTOD registers */ + pnv_xscom_region_init(&chiptod->xscom_regs, OBJECT(dev), + &pnv_chiptod_xscom_ops, chiptod, "xscom-chiptod", + pctc->xscom_size); + + qemu_register_reset(pnv_chiptod_reset, chiptod); +} + +static void pnv_chiptod_unrealize(DeviceState *dev) +{ + PnvChipTOD *chiptod = PNV_CHIPTOD(dev); + + qemu_unregister_reset(pnv_chiptod_reset, chiptod); +} + +static void pnv_chiptod_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pnv_chiptod_realize; + dc->unrealize = pnv_chiptod_unrealize; + dc->desc = "PowerNV ChipTOD Controller"; + dc->user_creatable = false; +} + +static const TypeInfo pnv_chiptod_type_info = { + .name = TYPE_PNV_CHIPTOD, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvChipTOD), + .class_init = pnv_chiptod_class_init, + .class_size = sizeof(PnvChipTODClass), + .abstract = true, +}; + +static void pnv_chiptod_register_types(void) +{ + type_register_static(&pnv_chiptod_type_info); + type_register_static(&pnv_chiptod_power9_type_info); + type_register_static(&pnv_chiptod_power10_type_info); +} + +type_init(pnv_chiptod_register_types); diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 19e8eb885f..f40ab721d6 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -25,6 +25,7 @@ #include "target/ppc/cpu.h" #include "hw/ppc/ppc.h" #include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_chip.h" #include "hw/ppc/pnv_core.h" #include "hw/ppc/pnv_xscom.h" #include "hw/ppc/xics.h" @@ -58,6 +59,9 @@ static void pnv_core_cpu_reset(PnvCore *pc, PowerPCCPU *cpu) env->msr |= MSR_HVB; /* Hypervisor mode */ env->spr[SPR_HRMOR] = pc->hrmor; hreg_compute_hflags(env); + ppc_maybe_interrupt(env); + + cpu_ppc_tb_reset(env); pcc->intc_reset(pc->chip, cpu); } @@ -83,8 +87,8 @@ static uint64_t pnv_core_power8_xscom_read(void *opaque, hwaddr addr, val = 0x24f000000000000ull; break; default: - qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx "\n", - addr); + qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__, + offset); } return val; @@ -93,8 +97,10 @@ static uint64_t pnv_core_power8_xscom_read(void *opaque, hwaddr addr, static void pnv_core_power8_xscom_write(void *opaque, hwaddr addr, uint64_t val, unsigned int width) { - qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx "\n", - addr); + uint32_t offset = addr >> 3; + + qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__, + offset); } static const MemoryRegionOps pnv_core_power8_xscom_ops = { @@ -114,6 +120,8 @@ static const MemoryRegionOps pnv_core_power8_xscom_ops = { #define PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_HYP 0xf010d #define PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_OTR 0xf010a +#define PNV9_XSCOM_EC_CORE_THREAD_STATE 0x10ab3 + static uint64_t pnv_core_power9_xscom_read(void *opaque, hwaddr addr, unsigned int width) { @@ -132,9 +140,12 @@ static uint64_t pnv_core_power9_xscom_read(void *opaque, hwaddr addr, case PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_OTR: val = 0x0; break; + case PNV9_XSCOM_EC_CORE_THREAD_STATE: + val = 0; + break; default: - qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx "\n", - addr); + qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__, + offset); } return val; @@ -150,8 +161,8 @@ static void pnv_core_power9_xscom_write(void *opaque, hwaddr addr, uint64_t val, case PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_OTR: break; default: - qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx "\n", - addr); + qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__, + offset); } } @@ -165,12 +176,59 @@ static const MemoryRegionOps pnv_core_power9_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp) +/* + * POWER10 core controls + */ + +#define PNV10_XSCOM_EC_CORE_THREAD_STATE 0x412 + +static uint64_t pnv_core_power10_xscom_read(void *opaque, hwaddr addr, + unsigned int width) +{ + uint32_t offset = addr >> 3; + uint64_t val = 0; + + switch (offset) { + case PNV10_XSCOM_EC_CORE_THREAD_STATE: + val = 0; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__, + offset); + } + + return val; +} + +static void pnv_core_power10_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned int width) +{ + uint32_t offset = addr >> 3; + + switch (offset) { + default: + qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__, + offset); + } +} + +static const MemoryRegionOps pnv_core_power10_xscom_ops = { + .read = pnv_core_power10_xscom_read, + .write = pnv_core_power10_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_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp, + int thread_index) { CPUPPCState *env = &cpu->env; - int core_pir; - int thread_index = 0; /* TODO: TCG supports only one thread */ + int core_hwid; ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; + ppc_spr_t *tir = &env->spr_cb[SPR_TIR]; Error *local_err = NULL; PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pc->chip); @@ -184,14 +242,10 @@ static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp) return; } - core_pir = object_property_get_uint(OBJECT(pc), "pir", &error_abort); + core_hwid = object_property_get_uint(OBJECT(pc), "hwid", &error_abort); - /* - * The PIR of a thread is the core PIR + the thread index. We will - * need to find a way to get the thread index when TCG supports - * more than 1. We could use the object name ? - */ - pir->default_value = core_pir + thread_index; + tir->default_value = thread_index; + pir->default_value = pcc->chip_pir(pc->chip, core_hwid, thread_index); /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); @@ -239,16 +293,15 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) } for (j = 0; j < cc->nr_threads; j++) { - pnv_core_cpu_realize(pc, pc->threads[j], &local_err); + pnv_core_cpu_realize(pc, pc->threads[j], &local_err, j); 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); + pc, name, pcc->xscom_size); qemu_register_reset(pnv_core_reset, pc); return; @@ -289,7 +342,7 @@ static void pnv_core_unrealize(DeviceState *dev) } static Property pnv_core_properties[] = { - DEFINE_PROP_UINT32("pir", PnvCore, pir, 0), + DEFINE_PROP_UINT32("hwid", PnvCore, hwid, 0), DEFINE_PROP_UINT64("hrmor", PnvCore, hrmor, 0), DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *), DEFINE_PROP_END_OF_LIST(), @@ -300,6 +353,7 @@ static void pnv_core_power8_class_init(ObjectClass *oc, void *data) PnvCoreClass *pcc = PNV_CORE_CLASS(oc); pcc->xscom_ops = &pnv_core_power8_xscom_ops; + pcc->xscom_size = PNV_XSCOM_EX_SIZE; } static void pnv_core_power9_class_init(ObjectClass *oc, void *data) @@ -307,14 +361,15 @@ static void pnv_core_power9_class_init(ObjectClass *oc, void *data) PnvCoreClass *pcc = PNV_CORE_CLASS(oc); pcc->xscom_ops = &pnv_core_power9_xscom_ops; + pcc->xscom_size = PNV_XSCOM_EX_SIZE; } 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; + pcc->xscom_ops = &pnv_core_power10_xscom_ops; + pcc->xscom_size = PNV10_XSCOM_EC_SIZE; } static void pnv_core_class_init(ObjectClass *oc, void *data) @@ -346,7 +401,7 @@ static const TypeInfo pnv_core_infos[] = { DEFINE_PNV_CORE_TYPE(power8, "power8e_v2.1"), 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(power9, "power9_v2.2"), DEFINE_PNV_CORE_TYPE(power10, "power10_v2.0"), }; @@ -358,8 +413,8 @@ DEFINE_TYPES(pnv_core_infos) #define P9X_EX_NCU_SPEC_BAR 0x11010 -static uint64_t pnv_quad_xscom_read(void *opaque, hwaddr addr, - unsigned int width) +static uint64_t pnv_quad_power9_xscom_read(void *opaque, hwaddr addr, + unsigned int width) { uint32_t offset = addr >> 3; uint64_t val = -1; @@ -370,15 +425,15 @@ static uint64_t pnv_quad_xscom_read(void *opaque, hwaddr addr, val = 0; break; default: - qemu_log_mask(LOG_UNIMP, "%s: writing @0x%08x\n", __func__, + qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__, offset); } return val; } -static void pnv_quad_xscom_write(void *opaque, hwaddr addr, uint64_t val, - unsigned int width) +static void pnv_quad_power9_xscom_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int width) { uint32_t offset = addr >> 3; @@ -387,14 +442,14 @@ static void pnv_quad_xscom_write(void *opaque, hwaddr addr, uint64_t val, case P9X_EX_NCU_SPEC_BAR + 0x400: /* Second EX */ break; default: - qemu_log_mask(LOG_UNIMP, "%s: writing @0x%08x\n", __func__, + qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__, offset); } } -static const MemoryRegionOps pnv_quad_xscom_ops = { - .read = pnv_quad_xscom_read, - .write = pnv_quad_xscom_write, +static const MemoryRegionOps pnv_quad_power9_xscom_ops = { + .read = pnv_quad_power9_xscom_read, + .write = pnv_quad_power9_xscom_write, .valid.min_access_size = 8, .valid.max_access_size = 8, .impl.min_access_size = 8, @@ -402,14 +457,124 @@ static const MemoryRegionOps pnv_quad_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_quad_realize(DeviceState *dev, Error **errp) +/* + * POWER10 Quads + */ + +static uint64_t pnv_quad_power10_xscom_read(void *opaque, hwaddr addr, + unsigned int width) +{ + uint32_t offset = addr >> 3; + uint64_t val = -1; + + switch (offset) { + default: + qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__, + offset); + } + + return val; +} + +static void pnv_quad_power10_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned int width) +{ + uint32_t offset = addr >> 3; + + switch (offset) { + default: + qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__, + offset); + } +} + +static const MemoryRegionOps pnv_quad_power10_xscom_ops = { + .read = pnv_quad_power10_xscom_read, + .write = pnv_quad_power10_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, +}; + +#define P10_QME_SPWU_HYP 0x83c +#define P10_QME_SSH_HYP 0x82c + +static uint64_t pnv_qme_power10_xscom_read(void *opaque, hwaddr addr, + unsigned int width) +{ + uint32_t offset = addr >> 3; + uint64_t val = -1; + + /* + * Forth nibble selects the core within a quad, mask it to process read + * for any core. + */ + switch (offset & ~0xf000) { + case P10_QME_SPWU_HYP: + case P10_QME_SSH_HYP: + return 0; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__, + offset); + } + + return val; +} + +static void pnv_qme_power10_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned int width) +{ + uint32_t offset = addr >> 3; + + switch (offset) { + default: + qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__, + offset); + } +} + +static const MemoryRegionOps pnv_qme_power10_xscom_ops = { + .read = pnv_qme_power10_xscom_read, + .write = pnv_qme_power10_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_quad_power9_realize(DeviceState *dev, Error **errp) { PnvQuad *eq = PNV_QUAD(dev); + PnvQuadClass *pqc = PNV_QUAD_GET_CLASS(eq); char name[32]; snprintf(name, sizeof(name), "xscom-quad.%d", eq->quad_id); - pnv_xscom_region_init(&eq->xscom_regs, OBJECT(dev), &pnv_quad_xscom_ops, - eq, name, PNV9_XSCOM_EQ_SIZE); + pnv_xscom_region_init(&eq->xscom_regs, OBJECT(dev), + pqc->xscom_ops, + eq, name, + pqc->xscom_size); +} + +static void pnv_quad_power10_realize(DeviceState *dev, Error **errp) +{ + PnvQuad *eq = PNV_QUAD(dev); + PnvQuadClass *pqc = PNV_QUAD_GET_CLASS(eq); + char name[32]; + + snprintf(name, sizeof(name), "xscom-quad.%d", eq->quad_id); + pnv_xscom_region_init(&eq->xscom_regs, OBJECT(dev), + pqc->xscom_ops, + eq, name, + pqc->xscom_size); + + snprintf(name, sizeof(name), "xscom-qme.%d", eq->quad_id); + pnv_xscom_region_init(&eq->xscom_qme_regs, OBJECT(dev), + pqc->xscom_qme_ops, + eq, name, + pqc->xscom_qme_size); } static Property pnv_quad_properties[] = { @@ -417,25 +582,58 @@ static Property pnv_quad_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void pnv_quad_power9_class_init(ObjectClass *oc, void *data) +{ + PnvQuadClass *pqc = PNV_QUAD_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = pnv_quad_power9_realize; + + pqc->xscom_ops = &pnv_quad_power9_xscom_ops; + pqc->xscom_size = PNV9_XSCOM_EQ_SIZE; +} + +static void pnv_quad_power10_class_init(ObjectClass *oc, void *data) +{ + PnvQuadClass *pqc = PNV_QUAD_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = pnv_quad_power10_realize; + + pqc->xscom_ops = &pnv_quad_power10_xscom_ops; + pqc->xscom_size = PNV10_XSCOM_EQ_SIZE; + + pqc->xscom_qme_ops = &pnv_qme_power10_xscom_ops; + pqc->xscom_qme_size = PNV10_XSCOM_QME_SIZE; +} + static void pnv_quad_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - dc->realize = pnv_quad_realize; device_class_set_props(dc, pnv_quad_properties); dc->user_creatable = false; } -static const TypeInfo pnv_quad_info = { - .name = TYPE_PNV_QUAD, - .parent = TYPE_DEVICE, - .instance_size = sizeof(PnvQuad), - .class_init = pnv_quad_class_init, +static const TypeInfo pnv_quad_infos[] = { + { + .name = TYPE_PNV_QUAD, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvQuad), + .class_size = sizeof(PnvQuadClass), + .class_init = pnv_quad_class_init, + .abstract = true, + }, + { + .parent = TYPE_PNV_QUAD, + .name = PNV_QUAD_TYPE_NAME("power9"), + .class_init = pnv_quad_power9_class_init, + }, + { + .parent = TYPE_PNV_QUAD, + .name = PNV_QUAD_TYPE_NAME("power10"), + .class_init = pnv_quad_power10_class_init, + }, }; -static void pnv_core_register_types(void) -{ - type_register_static(&pnv_quad_info); -} - -type_init(pnv_core_register_types) +DEFINE_TYPES(pnv_quad_infos); diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index ea73919e54..f9a203d11d 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -25,6 +25,7 @@ #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_chip.h" #include "hw/ppc/pnv_homer.h" #include "hw/ppc/pnv_xscom.h" diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c new file mode 100644 index 0000000000..eec5047ce8 --- /dev/null +++ b/hw/ppc/pnv_i2c.c @@ -0,0 +1,584 @@ +/* + * QEMU PowerPC PowerNV Processor I2C model + * + * Copyright (c) 2019-2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/log.h" +#include "sysemu/reset.h" + +#include "hw/irq.h" +#include "hw/qdev-properties.h" + +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_chip.h" +#include "hw/ppc/pnv_i2c.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/fdt.h" + +#include + +#include "hw/i2c/pnv_i2c_regs.h" + +static I2CBus *pnv_i2c_get_bus(PnvI2C *i2c) +{ + uint8_t port = GETFIELD(I2C_MODE_PORT_NUM, i2c->regs[I2C_MODE_REG]); + + if (port >= i2c->num_busses) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid bus number %d/%d\n", port, + i2c->num_busses); + return NULL; + } + return i2c->busses[port]; +} + +static void pnv_i2c_update_irq(PnvI2C *i2c) +{ + I2CBus *bus = pnv_i2c_get_bus(i2c); + bool recv = !!(i2c->regs[I2C_CMD_REG] & I2C_CMD_READ_NOT_WRITE); + uint16_t front_end = GETFIELD(I2C_RESIDUAL_FRONT_END, + i2c->regs[I2C_RESIDUAL_LEN_REG]); + uint16_t back_end = GETFIELD(I2C_RESIDUAL_BACK_END, + i2c->regs[I2C_RESIDUAL_LEN_REG]); + uint8_t fifo_count = GETFIELD(I2C_STAT_FIFO_ENTRY_COUNT, + i2c->regs[I2C_STAT_REG]); + uint8_t fifo_free = PNV_I2C_FIFO_SIZE - fifo_count; + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + + if (i2c_bus_busy(bus)) { + i2c->regs[I2C_STAT_REG] &= ~I2C_STAT_DATA_REQ; + + if (recv) { + if (fifo_count >= + GETFIELD(I2C_WATERMARK_HIGH, i2c->regs[I2C_WATERMARK_REG])) { + i2c->regs[I2C_EXTD_STAT_REG] |= I2C_EXTD_STAT_HIGH_WATER; + } else { + i2c->regs[I2C_EXTD_STAT_REG] &= ~I2C_EXTD_STAT_HIGH_WATER; + } + + if (((i2c->regs[I2C_EXTD_STAT_REG] & I2C_EXTD_STAT_HIGH_WATER) && + fifo_count != 0) || front_end == 0) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_DATA_REQ; + } + } else { + if (fifo_count <= + GETFIELD(I2C_WATERMARK_LOW, i2c->regs[I2C_WATERMARK_REG])) { + i2c->regs[I2C_EXTD_STAT_REG] |= I2C_EXTD_STAT_LOW_WATER; + } else { + i2c->regs[I2C_EXTD_STAT_REG] &= ~I2C_EXTD_STAT_LOW_WATER; + } + + if (back_end > 0 && + (fifo_free >= back_end || + (i2c->regs[I2C_EXTD_STAT_REG] & I2C_EXTD_STAT_LOW_WATER))) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_DATA_REQ; + } + } + + if (back_end == 0 && front_end == 0) { + i2c->regs[I2C_STAT_REG] &= ~I2C_STAT_DATA_REQ; + i2c->regs[I2C_STAT_REG] |= I2C_STAT_CMD_COMP; + + if (i2c->regs[I2C_CMD_REG] & I2C_CMD_WITH_STOP) { + i2c_end_transfer(bus); + i2c->regs[I2C_EXTD_STAT_REG] &= + ~(I2C_EXTD_STAT_I2C_BUSY | I2C_EXTD_STAT_SELF_BUSY); + } + } else { + i2c->regs[I2C_STAT_REG] &= ~I2C_STAT_CMD_COMP; + } + } + + /* + * Status and interrupt registers have nearly the same layout. + */ + i2c->regs[I2C_INTR_RAW_COND_REG] = i2c->regs[I2C_STAT_REG] >> 16; + i2c->regs[I2C_INTR_COND_REG] = + i2c->regs[I2C_INTR_RAW_COND_REG] & i2c->regs[I2C_INTR_MASK_REG]; + + qemu_set_irq(i2c->psi_irq, i2c->regs[I2C_INTR_COND_REG] != 0); +} + +static void pnv_i2c_fifo_update_count(PnvI2C *i2c) +{ + uint64_t stat = i2c->regs[I2C_STAT_REG]; + + i2c->regs[I2C_STAT_REG] = SETFIELD(I2C_STAT_FIFO_ENTRY_COUNT, stat, + fifo8_num_used(&i2c->fifo)); +} + +static void pnv_i2c_frontend_update(PnvI2C *i2c) +{ + uint64_t residual_end = i2c->regs[I2C_RESIDUAL_LEN_REG]; + uint16_t front_end = GETFIELD(I2C_RESIDUAL_FRONT_END, residual_end); + + i2c->regs[I2C_RESIDUAL_LEN_REG] = + SETFIELD(I2C_RESIDUAL_FRONT_END, residual_end, front_end - 1); +} + +static void pnv_i2c_fifo_flush(PnvI2C *i2c) +{ + I2CBus *bus = pnv_i2c_get_bus(i2c); + uint8_t data; + int ret; + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + if (!i2c_bus_busy(bus)) { + return; + } + + if (i2c->regs[I2C_CMD_REG] & I2C_CMD_READ_NOT_WRITE) { + if (fifo8_is_full(&i2c->fifo)) { + return; + } + + data = i2c_recv(bus); + fifo8_push(&i2c->fifo, data); + } else { + if (fifo8_is_empty(&i2c->fifo)) { + return; + } + + data = fifo8_pop(&i2c->fifo); + ret = i2c_send(bus, data); + if (ret) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_NACK_RCVD_ERR; + i2c_end_transfer(bus); + } + } + + pnv_i2c_fifo_update_count(i2c); + pnv_i2c_frontend_update(i2c); +} + +static void pnv_i2c_handle_cmd(PnvI2C *i2c, uint64_t val) +{ + I2CBus *bus = pnv_i2c_get_bus(i2c); + uint8_t addr = GETFIELD(I2C_CMD_DEV_ADDR, val); + int recv = !!(val & I2C_CMD_READ_NOT_WRITE); + uint32_t len_bytes = GETFIELD(I2C_CMD_LEN_BYTES, val); + + if (!(val & I2C_CMD_WITH_START) && !(val & I2C_CMD_WITH_ADDR) && + !(val & I2C_CMD_WITH_STOP) && !len_bytes) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid command 0x%"PRIx64"\n", + val); + return; + } + + if (!(i2c->regs[I2C_STAT_REG] & I2C_STAT_CMD_COMP)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: command in progress\n"); + return; + } + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + + i2c->regs[I2C_RESIDUAL_LEN_REG] = + SETFIELD(I2C_RESIDUAL_FRONT_END, 0ull, len_bytes) | + SETFIELD(I2C_RESIDUAL_BACK_END, 0ull, len_bytes); + + if (val & I2C_CMD_WITH_START) { + if (i2c_start_transfer(bus, addr, recv)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_NACK_RCVD_ERR; + } else { + i2c->regs[I2C_EXTD_STAT_REG] |= + (I2C_EXTD_STAT_I2C_BUSY | I2C_EXTD_STAT_SELF_BUSY); + pnv_i2c_fifo_flush(i2c); + } + } +} + +static void pnv_i2c_backend_update(PnvI2C *i2c) +{ + uint64_t residual_end = i2c->regs[I2C_RESIDUAL_LEN_REG]; + uint16_t back_end = GETFIELD(I2C_RESIDUAL_BACK_END, residual_end); + + if (!back_end) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_BKEND_ACCESS_ERR; + return; + } + + i2c->regs[I2C_RESIDUAL_LEN_REG] = + SETFIELD(I2C_RESIDUAL_BACK_END, residual_end, back_end - 1); +} + +static void pnv_i2c_fifo_in(PnvI2C *i2c) +{ + uint8_t data = GETFIELD(I2C_FIFO, i2c->regs[I2C_FIFO_REG]); + I2CBus *bus = pnv_i2c_get_bus(i2c); + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + + if (!i2c_bus_busy(bus)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: no command in progress\n"); + return; + } + + if (i2c->regs[I2C_CMD_REG] & I2C_CMD_READ_NOT_WRITE) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: read command in progress\n"); + return; + } + + if (fifo8_is_full(&i2c->fifo)) { + if (!(i2c->regs[I2C_MODE_REG] & I2C_MODE_PACING_ALLOW)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_BKEND_OVERRUN_ERR; + } + return; + } + + fifo8_push(&i2c->fifo, data); + pnv_i2c_fifo_update_count(i2c); + pnv_i2c_backend_update(i2c); + pnv_i2c_fifo_flush(i2c); +} + +static void pnv_i2c_fifo_out(PnvI2C *i2c) +{ + uint8_t data; + I2CBus *bus = pnv_i2c_get_bus(i2c); + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + + if (!i2c_bus_busy(bus)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: no command in progress\n"); + return; + } + + if (!(i2c->regs[I2C_CMD_REG] & I2C_CMD_READ_NOT_WRITE)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: write command in progress\n"); + return; + } + + if (fifo8_is_empty(&i2c->fifo)) { + if (!(i2c->regs[I2C_MODE_REG] & I2C_MODE_PACING_ALLOW)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_BKEND_OVERRUN_ERR; + } + return; + } + + data = fifo8_pop(&i2c->fifo); + + i2c->regs[I2C_FIFO_REG] = SETFIELD(I2C_FIFO, 0ull, data); + pnv_i2c_fifo_update_count(i2c); + pnv_i2c_backend_update(i2c); +} + +static uint64_t pnv_i2c_xscom_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvI2C *i2c = PNV_I2C(opaque); + uint32_t offset = addr >> 3; + uint64_t val = -1; + int i; + + switch (offset) { + case I2C_STAT_REG: + val = i2c->regs[offset]; + break; + + case I2C_FIFO_REG: + pnv_i2c_fifo_out(i2c); + val = i2c->regs[offset]; + break; + + case I2C_PORT_BUSY_REG: /* compute busy bit for each port */ + val = 0; + for (i = 0; i < i2c->num_busses; i++) { + val |= (uint64_t)i2c_bus_busy(i2c->busses[i]) << i; + } + break; + + case I2C_CMD_REG: + case I2C_MODE_REG: + case I2C_WATERMARK_REG: + case I2C_INTR_MASK_REG: + case I2C_INTR_RAW_COND_REG: + case I2C_INTR_COND_REG: + case I2C_EXTD_STAT_REG: + case I2C_RESIDUAL_LEN_REG: + val = i2c->regs[offset]; + break; + default: + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: read at register: 0x%" + HWADDR_PRIx "\n", addr >> 3); + } + + pnv_i2c_update_irq(i2c); + + return val; +} + +static void pnv_i2c_reset(void *dev) +{ + PnvI2C *i2c = PNV_I2C(dev); + + memset(i2c->regs, 0, sizeof(i2c->regs)); + + i2c->regs[I2C_STAT_REG] = + SETFIELD(I2C_STAT_UPPER_THRS, 0ull, i2c->num_busses - 1) | + I2C_STAT_CMD_COMP | I2C_STAT_SCL_INPUT_LEVEL | + I2C_STAT_SDA_INPUT_LEVEL; + i2c->regs[I2C_EXTD_STAT_REG] = + SETFIELD(I2C_EXTD_STAT_FIFO_SIZE, 0ull, PNV_I2C_FIFO_SIZE) | + SETFIELD(I2C_EXTD_STAT_I2C_VERSION, 0ull, 23); /* last version */ + + fifo8_reset(&i2c->fifo); +} + +static void pnv_i2c_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvI2C *i2c = PNV_I2C(opaque); + uint32_t offset = addr >> 3; + + switch (offset) { + case I2C_MODE_REG: + { + i2c->regs[offset] = val; + I2CBus *bus = pnv_i2c_get_bus(i2c); + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + if (i2c_bus_busy(bus)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: command in progress\n"); + } + } + break; + + case I2C_CMD_REG: + i2c->regs[offset] = val; + pnv_i2c_handle_cmd(i2c, val); + break; + + case I2C_FIFO_REG: + i2c->regs[offset] = val; + pnv_i2c_fifo_in(i2c); + break; + + case I2C_WATERMARK_REG: + i2c->regs[offset] = val; + break; + + case I2C_RESET_I2C_REG: + pnv_i2c_reset(i2c); + break; + + case I2C_RESET_ERRORS: + i2c->regs[I2C_STAT_REG] &= ~I2C_STAT_ANY_ERR; + i2c->regs[I2C_RESIDUAL_LEN_REG] = 0; + i2c->regs[I2C_EXTD_STAT_REG] &= + (I2C_EXTD_STAT_FIFO_SIZE | I2C_EXTD_STAT_I2C_VERSION); + fifo8_reset(&i2c->fifo); + break; + + case I2C_INTR_MASK_REG: + i2c->regs[offset] = val; + break; + + case I2C_INTR_MASK_OR_REG: + i2c->regs[I2C_INTR_MASK_REG] |= val; + break; + + case I2C_INTR_MASK_AND_REG: + i2c->regs[I2C_INTR_MASK_REG] &= val; + break; + + case I2C_PORT_BUSY_REG: + case I2C_SET_S_SCL_REG: + case I2C_RESET_S_SCL_REG: + case I2C_SET_S_SDA_REG: + case I2C_RESET_S_SDA_REG: + i2c->regs[offset] = val; + break; + default: + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: write at register: 0x%" + HWADDR_PRIx " val=0x%"PRIx64"\n", addr >> 3, val); + } + + pnv_i2c_update_irq(i2c); +} + +static const MemoryRegionOps pnv_i2c_xscom_ops = { + .read = pnv_i2c_xscom_read, + .write = pnv_i2c_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_i2c_bus_dt_xscom(PnvI2C *i2c, void *fdt, + int offset, int index) +{ + int i2c_bus_offset; + const char i2c_compat[] = + "ibm,opal-i2c\0ibm,power8-i2c-port\0ibm,power9-i2c-port"; + g_autofree char *i2c_port_name = NULL; + g_autofree char *name = g_strdup_printf("i2c-bus@%x", index); + + i2c_bus_offset = fdt_add_subnode(fdt, offset, name); + _FDT(i2c_bus_offset); + + _FDT((fdt_setprop_cell(fdt, i2c_bus_offset, "reg", index))); + _FDT((fdt_setprop_cell(fdt, i2c_bus_offset, "#address-cells", 1))); + _FDT((fdt_setprop_cell(fdt, i2c_bus_offset, "#size-cells", 0))); + _FDT(fdt_setprop(fdt, i2c_bus_offset, "compatible", i2c_compat, + sizeof(i2c_compat))); + _FDT((fdt_setprop_cell(fdt, i2c_bus_offset, "bus-frequency", 400000))); + + i2c_port_name = g_strdup_printf("p8_%08x_e%dp%d", i2c->chip->chip_id, + i2c->engine, index); + _FDT(fdt_setprop_string(fdt, i2c_bus_offset, "ibm,port-name", + i2c_port_name)); + return 0; +} + +#define XSCOM_BUS_FREQUENCY 466500000 +#define I2C_CLOCK_FREQUENCY (XSCOM_BUS_FREQUENCY / 4) + +static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void *fdt, + int offset) +{ + PnvI2C *i2c = PNV_I2C(dev); + int i2c_offset; + const char i2c_compat[] = "ibm,power8-i2cm\0ibm,power9-i2cm"; + uint32_t i2c_pcba = PNV9_XSCOM_I2CM_BASE + + (i2c->engine - 1) * PNV9_XSCOM_I2CM_SIZE; + uint32_t reg[2] = { + cpu_to_be32(i2c_pcba), + cpu_to_be32(PNV9_XSCOM_I2CM_SIZE) + }; + int i; + g_autofree char *name = g_strdup_printf("i2cm@%x", i2c_pcba); + + i2c_offset = fdt_add_subnode(fdt, offset, name); + _FDT(i2c_offset); + + _FDT(fdt_setprop(fdt, i2c_offset, "reg", reg, sizeof(reg))); + + _FDT((fdt_setprop_cell(fdt, i2c_offset, "#address-cells", 1))); + _FDT((fdt_setprop_cell(fdt, i2c_offset, "#size-cells", 0))); + _FDT(fdt_setprop(fdt, i2c_offset, "compatible", i2c_compat, + sizeof(i2c_compat))); + _FDT((fdt_setprop_cell(fdt, i2c_offset, "chip-engine#", i2c->engine))); + _FDT((fdt_setprop_cell(fdt, i2c_offset, "clock-frequency", + I2C_CLOCK_FREQUENCY))); + + for (i = 0; i < i2c->num_busses; i++) { + pnv_i2c_bus_dt_xscom(i2c, fdt, i2c_offset, i); + } + return 0; +} + +static void pnv_i2c_sys_reset(void *dev) +{ + int port; + PnvI2C *i2c = PNV_I2C(dev); + + pnv_i2c_reset(dev); + + /* reset all buses connected to this i2c controller */ + for (port = 0; port < i2c->num_busses; port++) { + bus_cold_reset(BUS(i2c->busses[port])); + } +} + +static void pnv_i2c_realize(DeviceState *dev, Error **errp) +{ + PnvI2C *i2c = PNV_I2C(dev); + int i; + + assert(i2c->chip); + + if (i2c->num_busses > PNV_I2C_MAX_BUSSES) { + error_setg(errp, "Invalid number of busses: %u", i2c->num_busses); + return; + } + + pnv_xscom_region_init(&i2c->xscom_regs, OBJECT(i2c), &pnv_i2c_xscom_ops, + i2c, "xscom-i2c", PNV9_XSCOM_I2CM_SIZE); + + i2c->busses = g_new(I2CBus *, i2c->num_busses); + for (i = 0; i < i2c->num_busses; i++) { + char name[32]; + + snprintf(name, sizeof(name), TYPE_PNV_I2C ".%d", i); + i2c->busses[i] = i2c_init_bus(dev, name); + } + + fifo8_create(&i2c->fifo, PNV_I2C_FIFO_SIZE); + + qemu_register_reset(pnv_i2c_sys_reset, dev); + + qdev_init_gpio_out(DEVICE(dev), &i2c->psi_irq, 1); +} + +static Property pnv_i2c_properties[] = { + DEFINE_PROP_LINK("chip", PnvI2C, chip, TYPE_PNV_CHIP, PnvChip *), + DEFINE_PROP_UINT32("engine", PnvI2C, engine, 1), + DEFINE_PROP_UINT32("num-busses", PnvI2C, num_busses, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xscomc = PNV_XSCOM_INTERFACE_CLASS(klass); + + xscomc->dt_xscom = pnv_i2c_dt_xscom; + + /* Reason: This device is part of the CPU and cannot be used separately */ + dc->user_creatable = false; + + dc->desc = "PowerNV I2C"; + dc->realize = pnv_i2c_realize; + device_class_set_props(dc, pnv_i2c_properties); +} + +static const TypeInfo pnv_i2c_info = { + .name = TYPE_PNV_I2C, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvI2C), + .class_init = pnv_i2c_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_i2c_register_types(void) +{ + type_register_static(&pnv_i2c_info); +} + +type_init(pnv_i2c_register_types); diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index ee890e7ab4..d692858bee 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -26,6 +26,7 @@ #include "hw/isa/isa.h" #include "hw/qdev-properties.h" #include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_chip.h" #include "hw/ppc/pnv_lpc.h" #include "hw/ppc/pnv_xscom.h" #include "hw/ppc/fdt.h" @@ -733,14 +734,17 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) /* Create MMIO regions for LPC HC and OPB registers */ memory_region_init_io(&lpc->opb_master_regs, OBJECT(dev), &opb_master_ops, lpc, "lpc-opb-master", LPC_OPB_REGS_OPB_SIZE); + lpc->opb_master_regs.disable_reentrancy_guard = true; memory_region_add_subregion(&lpc->opb_mr, LPC_OPB_REGS_OPB_ADDR, &lpc->opb_master_regs); memory_region_init_io(&lpc->lpc_hc_regs, OBJECT(dev), &lpc_hc_ops, lpc, "lpc-hc", LPC_HC_REGS_OPB_SIZE); + /* xscom writes to lpc-hc. As such mark lpc-hc re-entrancy safe */ + lpc->lpc_hc_regs.disable_reentrancy_guard = true; memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR, &lpc->lpc_hc_regs); - qdev_init_gpio_out(DEVICE(dev), &lpc->psi_irq, 1); + qdev_init_gpio_out(dev, &lpc->psi_irq, 1); } static void pnv_lpc_class_init(ObjectClass *klass, void *data) @@ -836,7 +840,7 @@ 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); + isa_bus_register_input_irqs(isa_bus, irqs); return isa_bus; } diff --git a/hw/ppc/pnv_n1_chiplet.c b/hw/ppc/pnv_n1_chiplet.c new file mode 100644 index 0000000000..03ff9fbad0 --- /dev/null +++ b/hw/ppc/pnv_n1_chiplet.c @@ -0,0 +1,173 @@ +/* + * QEMU PowerPC N1 chiplet model + * + * Copyright (c) 2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/qdev-properties.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_n1_chiplet.h" +#include "hw/ppc/pnv_nest_pervasive.h" + +/* + * The n1 chiplet contains chiplet control unit, + * PowerBus/RaceTrack/Bridge logic, nest Memory Management Unit(nMMU) + * and more. + * + * In this model Nest1 chiplet control registers are modelled via common + * nest pervasive model and few PowerBus racetrack registers are modelled. + */ + +#define PB_SCOM_EQ0_HP_MODE2_CURR 0xe +#define PB_SCOM_ES3_MODE 0x8a + +static uint64_t pnv_n1_chiplet_pb_scom_eq_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvN1Chiplet *n1_chiplet = PNV_N1_CHIPLET(opaque); + uint32_t reg = addr >> 3; + uint64_t val = ~0ull; + + switch (reg) { + case PB_SCOM_EQ0_HP_MODE2_CURR: + val = n1_chiplet->eq[0].hp_mode2_curr; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Invalid xscom read at 0x%" PRIx32 "\n", + __func__, reg); + } + return val; +} + +static void pnv_n1_chiplet_pb_scom_eq_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvN1Chiplet *n1_chiplet = PNV_N1_CHIPLET(opaque); + uint32_t reg = addr >> 3; + + switch (reg) { + case PB_SCOM_EQ0_HP_MODE2_CURR: + n1_chiplet->eq[0].hp_mode2_curr = val; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Invalid xscom write at 0x%" PRIx32 "\n", + __func__, reg); + } +} + +static const MemoryRegionOps pnv_n1_chiplet_pb_scom_eq_ops = { + .read = pnv_n1_chiplet_pb_scom_eq_read, + .write = pnv_n1_chiplet_pb_scom_eq_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_n1_chiplet_pb_scom_es_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvN1Chiplet *n1_chiplet = PNV_N1_CHIPLET(opaque); + uint32_t reg = addr >> 3; + uint64_t val = ~0ull; + + switch (reg) { + case PB_SCOM_ES3_MODE: + val = n1_chiplet->es[3].mode; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Invalid xscom read at 0x%" PRIx32 "\n", + __func__, reg); + } + return val; +} + +static void pnv_n1_chiplet_pb_scom_es_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvN1Chiplet *n1_chiplet = PNV_N1_CHIPLET(opaque); + uint32_t reg = addr >> 3; + + switch (reg) { + case PB_SCOM_ES3_MODE: + n1_chiplet->es[3].mode = val; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Invalid xscom write at 0x%" PRIx32 "\n", + __func__, reg); + } +} + +static const MemoryRegionOps pnv_n1_chiplet_pb_scom_es_ops = { + .read = pnv_n1_chiplet_pb_scom_es_read, + .write = pnv_n1_chiplet_pb_scom_es_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_n1_chiplet_realize(DeviceState *dev, Error **errp) +{ + PnvN1Chiplet *n1_chiplet = PNV_N1_CHIPLET(dev); + + /* Realize nest pervasive common chiplet model */ + if (!qdev_realize(DEVICE(&n1_chiplet->nest_pervasive), NULL, errp)) { + return; + } + + /* Nest1 chiplet power bus EQ xscom region */ + pnv_xscom_region_init(&n1_chiplet->xscom_pb_eq_mr, OBJECT(n1_chiplet), + &pnv_n1_chiplet_pb_scom_eq_ops, n1_chiplet, + "xscom-n1-chiplet-pb-scom-eq", + PNV10_XSCOM_N1_PB_SCOM_EQ_SIZE); + + /* Nest1 chiplet power bus ES xscom region */ + pnv_xscom_region_init(&n1_chiplet->xscom_pb_es_mr, OBJECT(n1_chiplet), + &pnv_n1_chiplet_pb_scom_es_ops, n1_chiplet, + "xscom-n1-chiplet-pb-scom-es", + PNV10_XSCOM_N1_PB_SCOM_ES_SIZE); +} + +static void pnv_n1_chiplet_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "PowerNV n1 chiplet"; + dc->realize = pnv_n1_chiplet_realize; +} + +static void pnv_n1_chiplet_instance_init(Object *obj) +{ + PnvN1Chiplet *n1_chiplet = PNV_N1_CHIPLET(obj); + + object_initialize_child(OBJECT(n1_chiplet), "nest-pervasive-common", + &n1_chiplet->nest_pervasive, + TYPE_PNV_NEST_CHIPLET_PERVASIVE); +} + +static const TypeInfo pnv_n1_chiplet_info = { + .name = TYPE_PNV_N1_CHIPLET, + .parent = TYPE_DEVICE, + .instance_init = pnv_n1_chiplet_instance_init, + .instance_size = sizeof(PnvN1Chiplet), + .class_init = pnv_n1_chiplet_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_n1_chiplet_register_types(void) +{ + type_register_static(&pnv_n1_chiplet_info); +} + +type_init(pnv_n1_chiplet_register_types); diff --git a/hw/ppc/pnv_nest_pervasive.c b/hw/ppc/pnv_nest_pervasive.c new file mode 100644 index 0000000000..77476753a4 --- /dev/null +++ b/hw/ppc/pnv_nest_pervasive.c @@ -0,0 +1,208 @@ +/* + * QEMU PowerPC nest pervasive common chiplet model + * + * Copyright (c) 2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/qdev-properties.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_nest_pervasive.h" + +/* + * Status, configuration, and control units in POWER chips is provided + * by the pervasive subsystem, which connects registers to the SCOM bus, + * which can be programmed by processor cores, other units on the chip, + * BMCs, or other POWER chips. + * + * A POWER10 chip is divided into logical units called chiplets. Chiplets + * are broadly divided into "core chiplets" (with the processor cores) and + * "nest chiplets" (with everything else). Each chiplet has an attachment + * to the pervasive bus (PIB) and with chiplet-specific registers. + * All nest chiplets have a common basic set of registers. + * + * This model will provide the registers functionality for common registers of + * nest unit (PB Chiplet, PCI Chiplets, MC Chiplet, PAU Chiplets) + * + * Currently this model provide the read/write functionality of chiplet control + * scom registers. + */ + +#define CPLT_CONF0 0x08 +#define CPLT_CONF0_OR 0x18 +#define CPLT_CONF0_CLEAR 0x28 +#define CPLT_CONF1 0x09 +#define CPLT_CONF1_OR 0x19 +#define CPLT_CONF1_CLEAR 0x29 +#define CPLT_STAT0 0x100 +#define CPLT_MASK0 0x101 +#define CPLT_PROTECT_MODE 0x3FE +#define CPLT_ATOMIC_CLOCK 0x3FF + +static uint64_t pnv_chiplet_ctrl_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvNestChipletPervasive *nest_pervasive = PNV_NEST_CHIPLET_PERVASIVE( + opaque); + uint32_t reg = addr >> 3; + uint64_t val = ~0ull; + + /* CPLT_CTRL0 to CPLT_CTRL5 */ + for (int i = 0; i < PNV_CPLT_CTRL_SIZE; i++) { + if (reg == i) { + return nest_pervasive->control_regs.cplt_ctrl[i]; + } else if ((reg == (i + 0x10)) || (reg == (i + 0x20))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Write only register, ignoring " + "xscom read at 0x%" PRIx32 "\n", + __func__, reg); + return val; + } + } + + switch (reg) { + case CPLT_CONF0: + val = nest_pervasive->control_regs.cplt_cfg0; + break; + case CPLT_CONF0_OR: + case CPLT_CONF0_CLEAR: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Write only register, ignoring " + "xscom read at 0x%" PRIx32 "\n", + __func__, reg); + break; + case CPLT_CONF1: + val = nest_pervasive->control_regs.cplt_cfg1; + break; + case CPLT_CONF1_OR: + case CPLT_CONF1_CLEAR: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Write only register, ignoring " + "xscom read at 0x%" PRIx32 "\n", + __func__, reg); + break; + case CPLT_STAT0: + val = nest_pervasive->control_regs.cplt_stat0; + break; + case CPLT_MASK0: + val = nest_pervasive->control_regs.cplt_mask0; + break; + case CPLT_PROTECT_MODE: + val = nest_pervasive->control_regs.ctrl_protect_mode; + break; + case CPLT_ATOMIC_CLOCK: + val = nest_pervasive->control_regs.ctrl_atomic_lock; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Chiplet_control_regs: Invalid xscom " + "read at 0x%" PRIx32 "\n", __func__, reg); + } + return val; +} + +static void pnv_chiplet_ctrl_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvNestChipletPervasive *nest_pervasive = PNV_NEST_CHIPLET_PERVASIVE( + opaque); + uint32_t reg = addr >> 3; + + /* CPLT_CTRL0 to CPLT_CTRL5 */ + for (int i = 0; i < PNV_CPLT_CTRL_SIZE; i++) { + if (reg == i) { + nest_pervasive->control_regs.cplt_ctrl[i] = val; + return; + } else if (reg == (i + 0x10)) { + nest_pervasive->control_regs.cplt_ctrl[i] |= val; + return; + } else if (reg == (i + 0x20)) { + nest_pervasive->control_regs.cplt_ctrl[i] &= ~val; + return; + } + } + + switch (reg) { + case CPLT_CONF0: + nest_pervasive->control_regs.cplt_cfg0 = val; + break; + case CPLT_CONF0_OR: + nest_pervasive->control_regs.cplt_cfg0 |= val; + break; + case CPLT_CONF0_CLEAR: + nest_pervasive->control_regs.cplt_cfg0 &= ~val; + break; + case CPLT_CONF1: + nest_pervasive->control_regs.cplt_cfg1 = val; + break; + case CPLT_CONF1_OR: + nest_pervasive->control_regs.cplt_cfg1 |= val; + break; + case CPLT_CONF1_CLEAR: + nest_pervasive->control_regs.cplt_cfg1 &= ~val; + break; + case CPLT_STAT0: + nest_pervasive->control_regs.cplt_stat0 = val; + break; + case CPLT_MASK0: + nest_pervasive->control_regs.cplt_mask0 = val; + break; + case CPLT_PROTECT_MODE: + nest_pervasive->control_regs.ctrl_protect_mode = val; + break; + case CPLT_ATOMIC_CLOCK: + nest_pervasive->control_regs.ctrl_atomic_lock = val; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Chiplet_control_regs: Invalid xscom " + "write at 0x%" PRIx32 "\n", + __func__, reg); + } +} + +static const MemoryRegionOps pnv_nest_pervasive_control_xscom_ops = { + .read = pnv_chiplet_ctrl_read, + .write = pnv_chiplet_ctrl_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_nest_pervasive_realize(DeviceState *dev, Error **errp) +{ + PnvNestChipletPervasive *nest_pervasive = PNV_NEST_CHIPLET_PERVASIVE(dev); + + /* Chiplet control scoms */ + pnv_xscom_region_init(&nest_pervasive->xscom_ctrl_regs_mr, + OBJECT(nest_pervasive), + &pnv_nest_pervasive_control_xscom_ops, + nest_pervasive, "pervasive-control", + PNV10_XSCOM_CHIPLET_CTRL_REGS_SIZE); +} + +static void pnv_nest_pervasive_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "PowerNV nest pervasive chiplet"; + dc->realize = pnv_nest_pervasive_realize; +} + +static const TypeInfo pnv_nest_pervasive_info = { + .name = TYPE_PNV_NEST_CHIPLET_PERVASIVE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvNestChipletPervasive), + .class_init = pnv_nest_pervasive_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_nest_pervasive_register_types(void) +{ + type_register_static(&pnv_nest_pervasive_info); +} + +type_init(pnv_nest_pervasive_register_types); diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index 9fa6d91d31..48123ceae1 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -278,7 +278,7 @@ static void pnv_occ_realize(DeviceState *dev, Error **errp) occ, "occ-common-area", PNV_OCC_SENSOR_DATA_BLOCK_SIZE); - qdev_init_gpio_out(DEVICE(dev), &occ->psi_irq, 1); + qdev_init_gpio_out(dev, &occ->psi_irq, 1); } static void pnv_occ_class_init(ObjectClass *klass, void *data) diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c index 83ecccca28..6280408299 100644 --- a/hw/ppc/pnv_pnor.c +++ b/hw/ppc/pnv_pnor.c @@ -44,8 +44,8 @@ static void pnv_pnor_update(PnvPnor *s, int offset, int 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); + ret = blk_pwrite(s->blk, offset, offset_end - offset, s->storage + offset, + 0); if (ret < 0) { error_report("Could not update PNOR offset=0x%" PRIx32" : %s", offset, strerror(-ret)); @@ -99,7 +99,7 @@ static void pnv_pnor_realize(DeviceState *dev, Error **errp) s->storage = blk_blockalign(s->blk, s->size); - if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { + if (blk_pread(s->blk, 0, s->size, s->storage, 0) < 0) { error_setg(errp, "failed to read the initial flash content"); return; } diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index 98045ed3d2..26460d210d 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "exec/address-spaces.h" #include "hw/irq.h" #include "target/ppc/cpu.h" #include "qemu/log.h" @@ -120,8 +121,12 @@ #define PSIHB9_BAR_MASK 0x00fffffffff00000ull #define PSIHB9_FSPBAR_MASK 0x00ffffff00000000ull +/* mmio address to xscom address */ #define PSIHB_REG(addr) (((addr) >> 3) + PSIHB_XSCOM_BAR) +/* xscom address to mmio address */ +#define PSIHB_MMIO(reg) ((reg - PSIHB_XSCOM_BAR) << 3) + static void pnv_psi_set_bar(PnvPsi *psi, uint64_t bar) { PnvPsiClass *ppc = PNV_PSI_GET_CLASS(psi); @@ -733,8 +738,9 @@ static void pnv_psi_p9_mmio_write(void *opaque, hwaddr addr, } } else { if (!(psi->regs[reg] & PSIHB9_ESB_CI_VALID)) { - hwaddr addr = val & ~(PSIHB9_ESB_CI_VALID | PSIHB10_ESB_CI_64K); - memory_region_add_subregion(sysmem, addr, + hwaddr esb_addr = + val & ~(PSIHB9_ESB_CI_VALID | PSIHB10_ESB_CI_64K); + memory_region_add_subregion(sysmem, esb_addr, &psi9->source.esb_mmio); } } @@ -768,24 +774,31 @@ static const MemoryRegionOps pnv_psi_p9_mmio_ops = { static uint64_t pnv_psi_p9_xscom_read(void *opaque, hwaddr addr, unsigned size) { - /* No read are expected */ - qemu_log_mask(LOG_GUEST_ERROR, "PSI: xscom read at 0x%" PRIx64 "\n", addr); - return -1; + uint32_t reg = addr >> 3; + uint64_t val = -1; + + if (reg < PSIHB_XSCOM_BAR) { + /* FIR, not modeled */ + qemu_log_mask(LOG_UNIMP, "PSI: xscom read at 0x%08x\n", reg); + } else { + val = pnv_psi_p9_mmio_read(opaque, PSIHB_MMIO(reg), size); + } + return val; } static void pnv_psi_p9_xscom_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { PnvPsi *psi = PNV_PSI(opaque); + uint32_t reg = addr >> 3; - /* XSCOM is only used to set the PSIHB MMIO region */ - switch (addr >> 3) { - case PSIHB_XSCOM_BAR: + if (reg < PSIHB_XSCOM_BAR) { + /* FIR, not modeled */ + qemu_log_mask(LOG_UNIMP, "PSI: xscom write at 0x%08x\n", reg); + } else if (reg == PSIHB_XSCOM_BAR) { pnv_psi_set_bar(psi, val); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "PSI: xscom write at 0x%" PRIx64 "\n", - addr); + } else { + pnv_psi_p9_mmio_write(opaque, PSIHB_MMIO(reg), val, size); } } @@ -851,6 +864,8 @@ static void pnv_psi_power9_realize(DeviceState *dev, Error **errp) object_property_set_int(OBJECT(xsrc), "nr-irqs", PSIHB9_NUM_IRQS, &error_fatal); object_property_set_link(OBJECT(xsrc), "xive", OBJECT(psi), &error_abort); + object_property_set_int(OBJECT(xsrc), "reset-pq", XIVE_ESB_RESET, + &error_abort); if (!qdev_realize(DEVICE(xsrc), NULL, errp)) { return; } diff --git a/hw/ppc/pnv_sbe.c b/hw/ppc/pnv_sbe.c new file mode 100644 index 0000000000..74cee4eea7 --- /dev/null +++ b/hw/ppc/pnv_sbe.c @@ -0,0 +1,414 @@ +/* + * QEMU PowerPC PowerNV Emulation of some SBE behaviour + * + * Copyright (c) 2022, IBM Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * 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 "target/ppc/cpu.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_sbe.h" +#include "trace.h" + +/* + * Most register and command definitions come from skiboot. + * + * xscom addresses are adjusted to be relative to xscom subregion bases + */ + +/* + * SBE MBOX register address + * Reg 0 - 3 : Host to send command packets to SBE + * Reg 4 - 7 : SBE to send response packets to Host + */ +#define PSU_HOST_SBE_MBOX_REG0 0x00000000 +#define PSU_HOST_SBE_MBOX_REG1 0x00000001 +#define PSU_HOST_SBE_MBOX_REG2 0x00000002 +#define PSU_HOST_SBE_MBOX_REG3 0x00000003 +#define PSU_HOST_SBE_MBOX_REG4 0x00000004 +#define PSU_HOST_SBE_MBOX_REG5 0x00000005 +#define PSU_HOST_SBE_MBOX_REG6 0x00000006 +#define PSU_HOST_SBE_MBOX_REG7 0x00000007 +#define PSU_SBE_DOORBELL_REG_RW 0x00000010 +#define PSU_SBE_DOORBELL_REG_AND 0x00000011 +#define PSU_SBE_DOORBELL_REG_OR 0x00000012 +#define PSU_HOST_DOORBELL_REG_RW 0x00000013 +#define PSU_HOST_DOORBELL_REG_AND 0x00000014 +#define PSU_HOST_DOORBELL_REG_OR 0x00000015 + +/* + * Doorbell register to trigger SBE interrupt. Set by OPAL to inform + * the SBE about a waiting message in the Host/SBE mailbox registers + */ +#define HOST_SBE_MSG_WAITING PPC_BIT(0) + +/* + * Doorbell register for host bridge interrupt. Set by the SBE to inform + * host about a response message in the Host/SBE mailbox registers + */ +#define SBE_HOST_RESPONSE_WAITING PPC_BIT(0) +#define SBE_HOST_MSG_READ PPC_BIT(1) +#define SBE_HOST_STOP15_EXIT PPC_BIT(2) +#define SBE_HOST_RESET PPC_BIT(3) +#define SBE_HOST_PASSTHROUGH PPC_BIT(4) +#define SBE_HOST_TIMER_EXPIRY PPC_BIT(14) +#define SBE_HOST_RESPONSE_MASK (PPC_BITMASK(0, 4) | \ + SBE_HOST_TIMER_EXPIRY) + +/* SBE Control Register */ +#define SBE_CONTROL_REG_RW 0x00000000 + +/* SBE interrupt s0/s1 bits */ +#define SBE_CONTROL_REG_S0 PPC_BIT(14) +#define SBE_CONTROL_REG_S1 PPC_BIT(15) + +struct sbe_msg { + uint64_t reg[4]; +}; + +static uint64_t pnv_sbe_power9_xscom_ctrl_read(void *opaque, hwaddr addr, + unsigned size) +{ + uint32_t offset = addr >> 3; + uint64_t val = 0; + + switch (offset) { + default: + qemu_log_mask(LOG_UNIMP, "SBE Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } + + trace_pnv_sbe_xscom_ctrl_read(addr, val); + + return val; +} + +static void pnv_sbe_power9_xscom_ctrl_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + uint32_t offset = addr >> 3; + + trace_pnv_sbe_xscom_ctrl_write(addr, val); + + switch (offset) { + default: + qemu_log_mask(LOG_UNIMP, "SBE Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } +} + +static const MemoryRegionOps pnv_sbe_power9_xscom_ctrl_ops = { + .read = pnv_sbe_power9_xscom_ctrl_read, + .write = pnv_sbe_power9_xscom_ctrl_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_sbe_set_host_doorbell(PnvSBE *sbe, uint64_t val) +{ + val &= SBE_HOST_RESPONSE_MASK; /* Is this right? What does HW do? */ + sbe->host_doorbell = val; + + trace_pnv_sbe_reg_set_host_doorbell(val); + qemu_set_irq(sbe->psi_irq, !!val); +} + +/* SBE Target Type */ +#define SBE_TARGET_TYPE_PROC 0x00 +#define SBE_TARGET_TYPE_EX 0x01 +#define SBE_TARGET_TYPE_PERV 0x02 +#define SBE_TARGET_TYPE_MCS 0x03 +#define SBE_TARGET_TYPE_EQ 0x04 +#define SBE_TARGET_TYPE_CORE 0x05 + +/* SBE MBOX command class */ +#define SBE_MCLASS_FIRST 0xD1 +#define SBE_MCLASS_CORE_STATE 0xD1 +#define SBE_MCLASS_SCOM 0xD2 +#define SBE_MCLASS_RING 0xD3 +#define SBE_MCLASS_TIMER 0xD4 +#define SBE_MCLASS_MPIPL 0xD5 +#define SBE_MCLASS_SECURITY 0xD6 +#define SBE_MCLASS_GENERIC 0xD7 +#define SBE_MCLASS_LAST 0xD7 + +/* + * Commands are provided in xxyy form where: + * - xx : command class + * - yy : command + * + * Both request and response message uses same seq ID, + * command class and command. + */ +#define SBE_CMD_CTRL_DEADMAN_LOOP 0xD101 +#define SBE_CMD_MULTI_SCOM 0xD201 +#define SBE_CMD_PUT_RING_FORM_IMAGE 0xD301 +#define SBE_CMD_CONTROL_TIMER 0xD401 +#define SBE_CMD_GET_ARCHITECTED_REG 0xD501 +#define SBE_CMD_CLR_ARCHITECTED_REG 0xD502 +#define SBE_CMD_SET_UNSEC_MEM_WINDOW 0xD601 +#define SBE_CMD_GET_SBE_FFDC 0xD701 +#define SBE_CMD_GET_CAPABILITY 0xD702 +#define SBE_CMD_READ_SBE_SEEPROM 0xD703 +#define SBE_CMD_SET_FFDC_ADDR 0xD704 +#define SBE_CMD_QUIESCE_SBE 0xD705 +#define SBE_CMD_SET_FABRIC_ID_MAP 0xD706 +#define SBE_CMD_STASH_MPIPL_CONFIG 0xD707 + +/* SBE MBOX control flags */ + +/* Generic flags */ +#define SBE_CMD_CTRL_RESP_REQ 0x0100 +#define SBE_CMD_CTRL_ACK_REQ 0x0200 + +/* Deadman loop */ +#define CTRL_DEADMAN_LOOP_START 0x0001 +#define CTRL_DEADMAN_LOOP_STOP 0x0002 + +/* Control timer */ +#define CONTROL_TIMER_START 0x0001 +#define CONTROL_TIMER_STOP 0x0002 + +/* Stash MPIPL config */ +#define SBE_STASH_KEY_SKIBOOT_BASE 0x03 + +static void sbe_timer(void *opaque) +{ + PnvSBE *sbe = opaque; + + trace_pnv_sbe_cmd_timer_expired(); + + pnv_sbe_set_host_doorbell(sbe, sbe->host_doorbell | SBE_HOST_TIMER_EXPIRY); +} + +static void do_sbe_msg(PnvSBE *sbe) +{ + struct sbe_msg msg; + uint16_t cmd, ctrl_flags, seq_id; + int i; + + memset(&msg, 0, sizeof(msg)); + + for (i = 0; i < 4; i++) { + msg.reg[i] = sbe->mbox[i]; + } + + cmd = msg.reg[0]; + seq_id = msg.reg[0] >> 16; + ctrl_flags = msg.reg[0] >> 32; + + trace_pnv_sbe_msg_recv(cmd, seq_id, ctrl_flags); + + if (ctrl_flags & SBE_CMD_CTRL_ACK_REQ) { + pnv_sbe_set_host_doorbell(sbe, sbe->host_doorbell | SBE_HOST_MSG_READ); + } + + switch (cmd) { + case SBE_CMD_CONTROL_TIMER: + if (ctrl_flags & CONTROL_TIMER_START) { + uint64_t us = msg.reg[1]; + trace_pnv_sbe_cmd_timer_start(us); + timer_mod(sbe->timer, qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + us); + } + if (ctrl_flags & CONTROL_TIMER_STOP) { + trace_pnv_sbe_cmd_timer_stop(); + timer_del(sbe->timer); + } + break; + default: + qemu_log_mask(LOG_UNIMP, "SBE Unimplemented command: 0x%x\n", cmd); + } +} + +static void pnv_sbe_set_sbe_doorbell(PnvSBE *sbe, uint64_t val) +{ + val &= HOST_SBE_MSG_WAITING; + sbe->sbe_doorbell = val; + + if (val & HOST_SBE_MSG_WAITING) { + sbe->sbe_doorbell &= ~HOST_SBE_MSG_WAITING; + do_sbe_msg(sbe); + } +} + +static uint64_t pnv_sbe_power9_xscom_mbox_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvSBE *sbe = PNV_SBE(opaque); + uint32_t offset = addr >> 3; + uint64_t val = 0; + + if (offset <= PSU_HOST_SBE_MBOX_REG7) { + uint32_t idx = offset - PSU_HOST_SBE_MBOX_REG0; + val = sbe->mbox[idx]; + } else { + switch (offset) { + case PSU_SBE_DOORBELL_REG_RW: + val = sbe->sbe_doorbell; + break; + case PSU_HOST_DOORBELL_REG_RW: + val = sbe->host_doorbell; + break; + default: + qemu_log_mask(LOG_UNIMP, "SBE Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } + } + + trace_pnv_sbe_xscom_mbox_read(addr, val); + + return val; +} + +static void pnv_sbe_power9_xscom_mbox_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvSBE *sbe = PNV_SBE(opaque); + uint32_t offset = addr >> 3; + + trace_pnv_sbe_xscom_mbox_write(addr, val); + + if (offset <= PSU_HOST_SBE_MBOX_REG7) { + uint32_t idx = offset - PSU_HOST_SBE_MBOX_REG0; + sbe->mbox[idx] = val; + } else { + switch (offset) { + case PSU_SBE_DOORBELL_REG_RW: + pnv_sbe_set_sbe_doorbell(sbe, val); + break; + case PSU_SBE_DOORBELL_REG_AND: + pnv_sbe_set_sbe_doorbell(sbe, sbe->sbe_doorbell & val); + break; + case PSU_SBE_DOORBELL_REG_OR: + pnv_sbe_set_sbe_doorbell(sbe, sbe->sbe_doorbell | val); + break; + + case PSU_HOST_DOORBELL_REG_RW: + pnv_sbe_set_host_doorbell(sbe, val); + break; + case PSU_HOST_DOORBELL_REG_AND: + pnv_sbe_set_host_doorbell(sbe, sbe->host_doorbell & val); + break; + case PSU_HOST_DOORBELL_REG_OR: + pnv_sbe_set_host_doorbell(sbe, sbe->host_doorbell | val); + break; + + default: + qemu_log_mask(LOG_UNIMP, "SBE Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } + } +} + +static const MemoryRegionOps pnv_sbe_power9_xscom_mbox_ops = { + .read = pnv_sbe_power9_xscom_mbox_read, + .write = pnv_sbe_power9_xscom_mbox_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_sbe_power9_class_init(ObjectClass *klass, void *data) +{ + PnvSBEClass *psc = PNV_SBE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "PowerNV SBE Controller (POWER9)"; + psc->xscom_ctrl_size = PNV9_XSCOM_SBE_CTRL_SIZE; + psc->xscom_ctrl_ops = &pnv_sbe_power9_xscom_ctrl_ops; + psc->xscom_mbox_size = PNV9_XSCOM_SBE_MBOX_SIZE; + psc->xscom_mbox_ops = &pnv_sbe_power9_xscom_mbox_ops; +} + +static const TypeInfo pnv_sbe_power9_type_info = { + .name = TYPE_PNV9_SBE, + .parent = TYPE_PNV_SBE, + .instance_size = sizeof(PnvSBE), + .class_init = pnv_sbe_power9_class_init, +}; + +static void pnv_sbe_power10_class_init(ObjectClass *klass, void *data) +{ + PnvSBEClass *psc = PNV_SBE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "PowerNV SBE Controller (POWER10)"; + psc->xscom_ctrl_size = PNV10_XSCOM_SBE_CTRL_SIZE; + psc->xscom_ctrl_ops = &pnv_sbe_power9_xscom_ctrl_ops; + psc->xscom_mbox_size = PNV10_XSCOM_SBE_MBOX_SIZE; + psc->xscom_mbox_ops = &pnv_sbe_power9_xscom_mbox_ops; +} + +static const TypeInfo pnv_sbe_power10_type_info = { + .name = TYPE_PNV10_SBE, + .parent = TYPE_PNV9_SBE, + .class_init = pnv_sbe_power10_class_init, +}; + +static void pnv_sbe_realize(DeviceState *dev, Error **errp) +{ + PnvSBE *sbe = PNV_SBE(dev); + PnvSBEClass *psc = PNV_SBE_GET_CLASS(sbe); + + /* XScom regions for SBE registers */ + pnv_xscom_region_init(&sbe->xscom_ctrl_regs, OBJECT(dev), + psc->xscom_ctrl_ops, sbe, "xscom-sbe-ctrl", + psc->xscom_ctrl_size); + pnv_xscom_region_init(&sbe->xscom_mbox_regs, OBJECT(dev), + psc->xscom_mbox_ops, sbe, "xscom-sbe-mbox", + psc->xscom_mbox_size); + + qdev_init_gpio_out(dev, &sbe->psi_irq, 1); + + sbe->timer = timer_new_us(QEMU_CLOCK_VIRTUAL, sbe_timer, sbe); +} + +static void pnv_sbe_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pnv_sbe_realize; + dc->desc = "PowerNV SBE Controller"; + dc->user_creatable = false; +} + +static const TypeInfo pnv_sbe_type_info = { + .name = TYPE_PNV_SBE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvSBE), + .class_init = pnv_sbe_class_init, + .class_size = sizeof(PnvSBEClass), + .abstract = true, +}; + +static void pnv_sbe_register_types(void) +{ + type_register_static(&pnv_sbe_type_info); + type_register_static(&pnv_sbe_power9_type_info); + type_register_static(&pnv_sbe_power10_type_info); +} + +type_init(pnv_sbe_register_types); diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index 9ce018dbc2..a17816d072 100644 --- a/hw/ppc/pnv_xscom.c +++ b/hw/ppc/pnv_xscom.c @@ -26,6 +26,7 @@ #include "hw/ppc/fdt.h" #include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_chip.h" #include "hw/ppc/pnv_xscom.h" #include @@ -43,15 +44,12 @@ static void xscom_complete(CPUState *cs, uint64_t hmer_bits) * passed for the cpu, and no CPU completion is generated. */ if (cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - /* * TODO: Need a CPU helper to set HMER, also handle generation * of HMIs */ cpu_synchronize_state(cs); - env->spr[SPR_HMER] |= hmer_bits; + cpu_env(cs)->spr[SPR_HMER] |= hmer_bits; } } @@ -220,15 +218,14 @@ const MemoryRegionOps pnv_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -void pnv_xscom_realize(PnvChip *chip, uint64_t size, Error **errp) +void pnv_xscom_init(PnvChip *chip, uint64_t size, hwaddr addr) { - SysBusDevice *sbd = SYS_BUS_DEVICE(chip); char *name; name = g_strdup_printf("xscom-%x", chip->chip_id); memory_region_init_io(&chip->xscom_mmio, OBJECT(chip), &pnv_xscom_ops, chip, name, size); - sysbus_init_mmio(sbd, &chip->xscom_mmio); + memory_region_add_subregion(get_system_memory(), addr, &chip->xscom_mmio); memory_region_init(&chip->xscom, OBJECT(chip), name, size); address_space_init(&chip->xscom_as, &chip->xscom, name); @@ -295,6 +292,9 @@ int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset, _FDT((fdt_setprop(fdt, xscom_offset, "reg", reg, sizeof(reg)))); _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat, compat_size))); _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0))); + if (chip->chip_id == 0) { + _FDT((fdt_setprop(fdt, xscom_offset, "primary", NULL, 0))); + } args.fdt = fdt; args.xscom_offset = xscom_offset; diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index fea70df45e..e6fa5580c0 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -32,6 +32,7 @@ #include "qemu/main-loop.h" #include "qemu/error-report.h" #include "sysemu/kvm.h" +#include "sysemu/replay.h" #include "sysemu/runstate.h" #include "kvm_ppc.h" #include "migration/vmstate.h" @@ -40,42 +41,31 @@ static void cpu_ppc_tb_stop (CPUPPCState *env); static void cpu_ppc_tb_start (CPUPPCState *env); -void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level) +void ppc_set_irq(PowerPCCPU *cpu, int irq, int level) { - CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; unsigned int old_pending; - bool locked = false; /* We may already have the BQL if coming from the reset path */ - if (!qemu_mutex_iothread_locked()) { - locked = true; - qemu_mutex_lock_iothread(); - } + BQL_LOCK_GUARD(); old_pending = env->pending_interrupts; if (level) { - env->pending_interrupts |= 1 << n_IRQ; - cpu_interrupt(cs, CPU_INTERRUPT_HARD); + env->pending_interrupts |= irq; } else { - env->pending_interrupts &= ~(1 << n_IRQ); - if (env->pending_interrupts == 0) { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } + env->pending_interrupts &= ~irq; } if (old_pending != env->pending_interrupts) { - kvmppc_set_interrupt(cpu, n_IRQ, level); + ppc_maybe_interrupt(env); + if (kvm_enabled()) { + kvmppc_set_interrupt(cpu, irq, level); + } } - - trace_ppc_irq_set_exit(env, n_IRQ, level, env->pending_interrupts, + trace_ppc_irq_set_exit(env, irq, level, env->pending_interrupts, CPU(cpu)->interrupt_request); - - if (locked) { - qemu_mutex_unlock_iothread(); - } } /* PowerPC 6xx / 7xx internal IRQ controller */ @@ -154,10 +144,7 @@ static void ppc6xx_set_irq(void *opaque, int pin, int level) void ppc6xx_irq_init(PowerPCCPU *cpu) { - CPUPPCState *env = &cpu->env; - - env->irq_inputs = (void **)qemu_allocate_irqs(&ppc6xx_set_irq, cpu, - PPC6xx_INPUT_NB); + qdev_init_gpio_in(DEVICE(cpu), ppc6xx_set_irq, PPC6xx_INPUT_NB); } #if defined(TARGET_PPC64) @@ -234,10 +221,7 @@ static void ppc970_set_irq(void *opaque, int pin, int level) void ppc970_irq_init(PowerPCCPU *cpu) { - CPUPPCState *env = &cpu->env; - - env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, cpu, - PPC970_INPUT_NB); + qdev_init_gpio_in(DEVICE(cpu), ppc970_set_irq, PPC970_INPUT_NB); } /* POWER7 internal IRQ controller */ @@ -260,10 +244,7 @@ static void power7_set_irq(void *opaque, int pin, int level) void ppcPOWER7_irq_init(PowerPCCPU *cpu) { - CPUPPCState *env = &cpu->env; - - env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, cpu, - POWER7_INPUT_NB); + qdev_init_gpio_in(DEVICE(cpu), power7_set_irq, POWER7_INPUT_NB); } /* POWER9 internal IRQ controller */ @@ -292,10 +273,7 @@ static void power9_set_irq(void *opaque, int pin, int level) void ppcPOWER9_irq_init(PowerPCCPU *cpu) { - CPUPPCState *env = &cpu->env; - - env->irq_inputs = (void **)qemu_allocate_irqs(&power9_set_irq, cpu, - POWER9_INPUT_NB); + qdev_init_gpio_in(DEVICE(cpu), power9_set_irq, POWER9_INPUT_NB); } #endif /* defined(TARGET_PPC64) */ @@ -336,7 +314,7 @@ void store_40x_dbcr0(CPUPPCState *env, uint32_t val) { PowerPCCPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); switch ((val >> 28) & 0x3) { case 0x0: @@ -356,7 +334,7 @@ void store_40x_dbcr0(CPUPPCState *env, uint32_t val) break; } - qemu_mutex_unlock_iothread(); + bql_unlock(); } /* PowerPC 40x internal IRQ controller */ @@ -431,10 +409,7 @@ static void ppc40x_set_irq(void *opaque, int pin, int level) void ppc40x_irq_init(PowerPCCPU *cpu) { - CPUPPCState *env = &cpu->env; - - env->irq_inputs = (void **)qemu_allocate_irqs(&ppc40x_set_irq, - cpu, PPC40x_INPUT_NB); + qdev_init_gpio_in(DEVICE(cpu), ppc40x_set_irq, PPC40x_INPUT_NB); } /* PowerPC E500 internal IRQ controller */ @@ -489,10 +464,7 @@ static void ppce500_set_irq(void *opaque, int pin, int level) void ppce500_irq_init(PowerPCCPU *cpu) { - CPUPPCState *env = &cpu->env; - - env->irq_inputs = (void **)qemu_allocate_irqs(&ppce500_set_irq, - cpu, PPCE500_INPUT_NB); + qdev_init_gpio_in(DEVICE(cpu), ppce500_set_irq, PPCE500_INPUT_NB); } /* Enable or Disable the E500 EPR capability */ @@ -513,10 +485,32 @@ void ppce500_set_mpic_proxy(bool enabled) /*****************************************************************************/ /* PowerPC time base and decrementer emulation */ +/* + * Conversion between QEMU_CLOCK_VIRTUAL ns and timebase (TB) ticks: + * TB ticks are arrived at by multiplying tb_freq then dividing by + * ns per second, and rounding down. TB ticks drive all clocks and + * timers in the target machine. + * + * Converting TB intervals to ns for the purpose of setting a + * QEMU_CLOCK_VIRTUAL timer should go the other way, but rounding + * up. Rounding down could cause the timer to fire before the TB + * value has been reached. + */ +static uint64_t ns_to_tb(uint32_t freq, int64_t clock) +{ + return muldiv64(clock, freq, NANOSECONDS_PER_SECOND); +} + +/* virtual clock in TB ticks, not adjusted by TB offset */ +static int64_t tb_to_ns_round_up(uint32_t freq, uint64_t tb) +{ + return muldiv64_round_up(tb, NANOSECONDS_PER_SECOND, freq); +} + uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset) { /* TB time in tb periods */ - return muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND) + tb_offset; + return ns_to_tb(tb_env->tb_freq, vmclk) + tb_offset; } uint64_t cpu_ppc_load_tbl (CPUPPCState *env) @@ -528,7 +522,8 @@ uint64_t cpu_ppc_load_tbl (CPUPPCState *env) return env->spr[SPR_TBL]; } - tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + tb_env->tb_offset); trace_ppc_tb_load(tb); return tb; @@ -539,7 +534,8 @@ static inline uint32_t _cpu_ppc_load_tbu(CPUPPCState *env) 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 = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + tb_env->tb_offset); trace_ppc_tb_load(tb); return tb >> 32; @@ -557,8 +553,7 @@ uint32_t cpu_ppc_load_tbu (CPUPPCState *env) static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t *tb_offsetp, uint64_t value) { - *tb_offsetp = value - - muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND); + *tb_offsetp = value - ns_to_tb(tb_env->tb_freq, vmclk); trace_ppc_tb_store(value, *tb_offsetp); } @@ -566,23 +561,24 @@ static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk, void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value) { ppc_tb_t *tb_env = env->tb_env; + int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); + tb = cpu_ppc_get_tb(tb_env, clock, tb_env->tb_offset); tb &= 0xFFFFFFFF00000000ULL; - cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - &tb_env->tb_offset, tb | (uint64_t)value); + cpu_ppc_store_tb(tb_env, clock, &tb_env->tb_offset, tb | (uint64_t)value); } static inline void _cpu_ppc_store_tbu(CPUPPCState *env, uint32_t value) { ppc_tb_t *tb_env = env->tb_env; + int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); + tb = cpu_ppc_get_tb(tb_env, clock, tb_env->tb_offset); tb &= 0x00000000FFFFFFFFULL; - cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - &tb_env->tb_offset, ((uint64_t)value << 32) | tb); + cpu_ppc_store_tb(tb_env, clock, &tb_env->tb_offset, + ((uint64_t)value << 32) | tb); } void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value) @@ -595,7 +591,8 @@ uint64_t cpu_ppc_load_atbl (CPUPPCState *env) 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->atb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + tb_env->atb_offset); trace_ppc_tb_load(tb); return tb; @@ -606,7 +603,8 @@ uint32_t cpu_ppc_load_atbu (CPUPPCState *env) 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->atb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + tb_env->atb_offset); trace_ppc_tb_load(tb); return tb >> 32; @@ -615,23 +613,34 @@ uint32_t cpu_ppc_load_atbu (CPUPPCState *env) void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value) { ppc_tb_t *tb_env = env->tb_env; + int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); + tb = cpu_ppc_get_tb(tb_env, clock, tb_env->atb_offset); tb &= 0xFFFFFFFF00000000ULL; - cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - &tb_env->atb_offset, tb | (uint64_t)value); + cpu_ppc_store_tb(tb_env, clock, &tb_env->atb_offset, tb | (uint64_t)value); } void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value) { ppc_tb_t *tb_env = env->tb_env; + int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); + tb = cpu_ppc_get_tb(tb_env, clock, tb_env->atb_offset); tb &= 0x00000000FFFFFFFFULL; - cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - &tb_env->atb_offset, ((uint64_t)value << 32) | tb); + cpu_ppc_store_tb(tb_env, clock, &tb_env->atb_offset, + ((uint64_t)value << 32) | tb); +} + +void cpu_ppc_increase_tb_by_offset(CPUPPCState *env, int64_t offset) +{ + env->tb_env->tb_offset += offset; +} + +void cpu_ppc_decrease_tb_by_offset(CPUPPCState *env, int64_t offset) +{ + env->tb_env->tb_offset -= offset; } uint64_t cpu_ppc_load_vtb(CPUPPCState *env) @@ -653,14 +662,13 @@ void cpu_ppc_store_vtb(CPUPPCState *env, uint64_t value) void cpu_ppc_store_tbu40(CPUPPCState *env, uint64_t value) { ppc_tb_t *tb_env = env->tb_env; + int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - tb_env->tb_offset); + tb = cpu_ppc_get_tb(tb_env, clock, 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); + cpu_ppc_store_tb(tb_env, clock, &tb_env->tb_offset, tb); } static void cpu_ppc_tb_stop (CPUPPCState *env) @@ -713,64 +721,77 @@ bool ppc_decr_clear_on_delivery(CPUPPCState *env) return ((tb_env->flags & flags) == PPC_DECR_UNDERFLOW_TRIGGERED); } -static inline int64_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next) +static inline int64_t __cpu_ppc_load_decr(CPUPPCState *env, int64_t now, + uint64_t next) { ppc_tb_t *tb_env = env->tb_env; - int64_t decr, diff; + uint64_t n; + int64_t decr; - diff = next - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - if (diff >= 0) { - decr = muldiv64(diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND); - } else if (tb_env->flags & PPC_TIMER_BOOKE) { + n = ns_to_tb(tb_env->decr_freq, now); + if (next > n && tb_env->flags & PPC_TIMER_BOOKE) { decr = 0; - } else { - decr = -muldiv64(-diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND); + } else { + decr = next - n; } + trace_ppc_decr_load(decr); return decr; } -target_ulong cpu_ppc_load_decr(CPUPPCState *env) +static target_ulong _cpu_ppc_load_decr(CPUPPCState *env, int64_t now) { ppc_tb_t *tb_env = env->tb_env; uint64_t decr; - if (kvm_enabled()) { - return env->spr[SPR_DECR]; - } - - decr = _cpu_ppc_load_decr(env, tb_env->decr_next); + decr = __cpu_ppc_load_decr(env, now, tb_env->decr_next); /* - * If large decrementer is enabled then the decrementer is signed extened + * If large decrementer is enabled then the decrementer is signed extended * to 64 bits, otherwise it is a 32 bit value. */ if (env->spr[SPR_LPCR] & LPCR_LD) { - return decr; + PowerPCCPU *cpu = env_archcpu(env); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + return sextract64(decr, 0, pcc->lrg_decr_bits); } return (uint32_t) decr; } -target_ulong cpu_ppc_load_hdecr(CPUPPCState *env) +target_ulong cpu_ppc_load_decr(CPUPPCState *env) +{ + if (kvm_enabled()) { + return env->spr[SPR_DECR]; + } else { + return _cpu_ppc_load_decr(env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + } +} + +static target_ulong _cpu_ppc_load_hdecr(CPUPPCState *env, int64_t now) { PowerPCCPU *cpu = env_archcpu(env); PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); ppc_tb_t *tb_env = env->tb_env; uint64_t hdecr; - hdecr = _cpu_ppc_load_decr(env, tb_env->hdecr_next); + hdecr = __cpu_ppc_load_decr(env, now, tb_env->hdecr_next); /* * If we have a large decrementer (POWER9 or later) then hdecr is sign * extended to 64 bits, otherwise it is 32 bits. */ if (pcc->lrg_decr_bits > 32) { - return hdecr; + return sextract64(hdecr, 0, pcc->lrg_decr_bits); } return (uint32_t) hdecr; } +target_ulong cpu_ppc_load_hdecr(CPUPPCState *env) +{ + return _cpu_ppc_load_hdecr(env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); +} + uint64_t cpu_ppc_load_purr (CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; @@ -815,36 +836,38 @@ static inline void cpu_ppc_hdecr_lower(PowerPCCPU *cpu) ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 0); } -static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, +static void __cpu_ppc_store_decr(PowerPCCPU *cpu, int64_t now, uint64_t *nextp, QEMUTimer *timer, void (*raise_excp)(void *), void (*lower_excp)(PowerPCCPU *), - target_ulong decr, target_ulong value, - int nr_bits) + uint32_t flags, target_ulong decr, + target_ulong value, int nr_bits) { CPUPPCState *env = &cpu->env; ppc_tb_t *tb_env = env->tb_env; - uint64_t now, next; + uint64_t next; int64_t signed_value; int64_t signed_decr; /* Truncate value to decr_width and sign extend for simplicity */ + value = extract64(value, 0, nr_bits); + decr = extract64(decr, 0, nr_bits); signed_value = sextract64(value, 0, nr_bits); signed_decr = sextract64(decr, 0, nr_bits); trace_ppc_decr_store(nr_bits, decr, value); - if (kvm_enabled()) { - /* KVM handles decrementer exceptions, we don't need our own timer */ - return; - } + /* + * Calculate the next decrementer event and set a timer. + * decr_next is in timebase units to keep rounding simple. Note it is + * not adjusted by tb_offset because if TB changes via tb_offset changing, + * decrementer does not change, so not directly comparable with TB. + */ + next = ns_to_tb(tb_env->decr_freq, now) + value; + *nextp = next; /* nextp is in timebase units */ /* - * Going from 2 -> 1, 1 -> 0 or 0 -> -1 is the event to generate a DEC - * interrupt. - * - * If we get a really small DEC value, we can assume that by the time we - * handled it we should inject an interrupt already. + * Going from 1 -> 0 or 0 -> -1 is the event to generate a DEC interrupt. * * On MSB level based DEC implementations the MSB always means the interrupt * is pending, so raise it on those. @@ -852,49 +875,53 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, * On MSB edge based DEC implementations the MSB going from 0 -> 1 triggers * an edge interrupt, so raise it here too. */ - if ((value < 3) || - ((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && signed_value < 0) || - ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && signed_value < 0 + if (((flags & PPC_DECR_UNDERFLOW_LEVEL) && signed_value < 0) || + ((flags & PPC_DECR_UNDERFLOW_TRIGGERED) && signed_value < 0 && signed_decr >= 0)) { (*raise_excp)(cpu); return; } /* On MSB level based systems a 0 for the MSB stops interrupt delivery */ - if (signed_value >= 0 && (tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL)) { + if (signed_value >= 0 && (flags & PPC_DECR_UNDERFLOW_LEVEL)) { (*lower_excp)(cpu); } - /* Calculate the next timer event */ - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - next = now + muldiv64(value, NANOSECONDS_PER_SECOND, tb_env->decr_freq); - *nextp = next; - /* Adjust timer */ - timer_mod(timer, next); + timer_mod(timer, tb_to_ns_round_up(tb_env->decr_freq, next)); } -static inline void _cpu_ppc_store_decr(PowerPCCPU *cpu, target_ulong decr, - target_ulong value, int nr_bits) +static inline void _cpu_ppc_store_decr(PowerPCCPU *cpu, int64_t now, + target_ulong decr, target_ulong value, + int nr_bits) { ppc_tb_t *tb_env = cpu->env.tb_env; - __cpu_ppc_store_decr(cpu, &tb_env->decr_next, tb_env->decr_timer, - tb_env->decr_timer->cb, &cpu_ppc_decr_lower, decr, - value, nr_bits); + __cpu_ppc_store_decr(cpu, now, &tb_env->decr_next, tb_env->decr_timer, + tb_env->decr_timer->cb, &cpu_ppc_decr_lower, + tb_env->flags, decr, value, nr_bits); } void cpu_ppc_store_decr(CPUPPCState *env, target_ulong value) { PowerPCCPU *cpu = env_archcpu(env); PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + int64_t now; + target_ulong decr; int nr_bits = 32; + if (kvm_enabled()) { + /* KVM handles decrementer exceptions, we don't need our own timer */ + return; + } + if (env->spr[SPR_LPCR] & LPCR_LD) { nr_bits = pcc->lrg_decr_bits; } - _cpu_ppc_store_decr(cpu, cpu_ppc_load_decr(env), value, nr_bits); + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + decr = _cpu_ppc_load_decr(env, now); + _cpu_ppc_store_decr(cpu, now, decr, value, nr_bits); } static void cpu_ppc_decr_cb(void *opaque) @@ -904,14 +931,17 @@ static void cpu_ppc_decr_cb(void *opaque) cpu_ppc_decr_excp(cpu); } -static inline void _cpu_ppc_store_hdecr(PowerPCCPU *cpu, target_ulong hdecr, - target_ulong value, int nr_bits) +static inline void _cpu_ppc_store_hdecr(PowerPCCPU *cpu, int64_t now, + target_ulong hdecr, target_ulong value, + int nr_bits) { ppc_tb_t *tb_env = cpu->env.tb_env; if (tb_env->hdecr_timer != NULL) { - __cpu_ppc_store_decr(cpu, &tb_env->hdecr_next, tb_env->hdecr_timer, + /* HDECR (Book3S 64bit) is edge-based, not level like DECR */ + __cpu_ppc_store_decr(cpu, now, &tb_env->hdecr_next, tb_env->hdecr_timer, tb_env->hdecr_timer->cb, &cpu_ppc_hdecr_lower, + PPC_DECR_UNDERFLOW_TRIGGERED, hdecr, value, nr_bits); } } @@ -920,9 +950,12 @@ void cpu_ppc_store_hdecr(CPUPPCState *env, target_ulong value) { PowerPCCPU *cpu = env_archcpu(env); PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + int64_t now; + target_ulong hdecr; - _cpu_ppc_store_hdecr(cpu, cpu_ppc_load_hdecr(env), value, - pcc->lrg_decr_bits); + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + hdecr = _cpu_ppc_load_hdecr(env, now); + _cpu_ppc_store_hdecr(cpu, now, hdecr, value, pcc->lrg_decr_bits); } static void cpu_ppc_hdecr_cb(void *opaque) @@ -932,29 +965,16 @@ static void cpu_ppc_hdecr_cb(void *opaque) cpu_ppc_hdecr_excp(cpu); } -void cpu_ppc_store_purr(CPUPPCState *env, uint64_t value) +static void _cpu_ppc_store_purr(CPUPPCState *env, int64_t now, 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->purr_offset, value); + cpu_ppc_store_tb(tb_env, now, &tb_env->purr_offset, value); } -static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) +void cpu_ppc_store_purr(CPUPPCState *env, uint64_t value) { - CPUPPCState *env = opaque; - PowerPCCPU *cpu = env_archcpu(env); - ppc_tb_t *tb_env = env->tb_env; - - tb_env->tb_freq = freq; - tb_env->decr_freq = freq; - /* There is a bug in Linux 2.4 kernels: - * if a decrementer exception is pending when it enables msr_ee at startup, - * it's not ready to handle it... - */ - _cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32); - _cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32); - cpu_ppc_store_purr(env, 0x0000000000000000ULL); + _cpu_ppc_store_purr(env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), value); } static void timebase_save(PPCTimebase *tb) @@ -967,8 +987,14 @@ static void timebase_save(PPCTimebase *tb) return; } - /* not used anymore, we keep it for compatibility */ - tb->time_of_the_day_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST); + if (replay_mode == REPLAY_MODE_NONE) { + /* not used anymore, we keep it for compatibility */ + tb->time_of_the_day_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST); + } else { + /* simpler for record-replay to avoid this event, compat not needed */ + tb->time_of_the_day_ns = 0; + } + /* * tb_offset is only expected to be changed by QEMU so * there is no need to update it from KVM here @@ -1050,7 +1076,7 @@ const VMStateDescription vmstate_ppc_timebase = { .version_id = 1, .minimum_version_id = 1, .pre_save = timebase_pre_save, - .fields = (VMStateField []) { + .fields = (const VMStateField []) { VMSTATE_UINT64(guest_timebase, PPCTimebase), VMSTATE_INT64(time_of_the_day_ns, PPCTimebase), VMSTATE_END_OF_LIST() @@ -1058,7 +1084,7 @@ const VMStateDescription vmstate_ppc_timebase = { }; /* Set up (once) timebase frequency (in Hz) */ -clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) +void cpu_ppc_tb_init(CPUPPCState *env, uint32_t freq) { PowerPCCPU *cpu = env_archcpu(env); ppc_tb_t *tb_env; @@ -1071,16 +1097,41 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) tb_env->flags |= PPC_DECR_UNDERFLOW_LEVEL; } /* Create new timer */ - tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_decr_cb, cpu); + tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + &cpu_ppc_decr_cb, cpu); if (env->has_hv_mode && !cpu->vhyp) { - tb_env->hdecr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_hdecr_cb, - cpu); + tb_env->hdecr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + &cpu_ppc_hdecr_cb, cpu); } else { tb_env->hdecr_timer = NULL; } - cpu_ppc_set_tb_clk(env, freq); - return &cpu_ppc_set_tb_clk; + tb_env->tb_freq = freq; + tb_env->decr_freq = freq; +} + +void cpu_ppc_tb_reset(CPUPPCState *env) +{ + PowerPCCPU *cpu = env_archcpu(env); + ppc_tb_t *tb_env = env->tb_env; + + timer_del(tb_env->decr_timer); + ppc_set_irq(cpu, PPC_INTERRUPT_DECR, 0); + tb_env->decr_next = 0; + if (tb_env->hdecr_timer != NULL) { + timer_del(tb_env->hdecr_timer); + ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 0); + tb_env->hdecr_next = 0; + } + + /* + * There is a bug in Linux 2.4 kernels: + * if a decrementer exception is pending when it enables msr_ee at startup, + * it's not ready to handle it... + */ + cpu_ppc_store_decr(env, -1); + cpu_ppc_store_hdecr(env, -1); + cpu_ppc_store_purr(env, 0x0000000000000000ULL); } void cpu_ppc_tb_free(CPUPPCState *env) @@ -1156,9 +1207,7 @@ static void cpu_4xx_fit_cb (void *opaque) /* Cannot occur, but makes gcc happy */ return; } - next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->tb_freq); - if (next == now) - next++; + next = now + tb_to_ns_round_up(tb_env->tb_freq, next); timer_mod(ppc40x_timer->fit_timer, next); env->spr[SPR_40x_TSR] |= 1 << 26; if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) { @@ -1184,14 +1233,15 @@ static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp) } else { trace_ppc4xx_pit_start(ppc40x_timer->pit_reload); now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - next = now + muldiv64(ppc40x_timer->pit_reload, - NANOSECONDS_PER_SECOND, tb_env->decr_freq); - if (is_excp) - next += tb_env->decr_next - now; - if (next == now) - next++; + + if (is_excp) { + tb_env->decr_next += ppc40x_timer->pit_reload; + } else { + tb_env->decr_next = ns_to_tb(tb_env->decr_freq, now) + + ppc40x_timer->pit_reload; + } + next = tb_to_ns_round_up(tb_env->decr_freq, tb_env->decr_next); timer_mod(tb_env->decr_timer, next); - tb_env->decr_next = next; } } @@ -1244,9 +1294,7 @@ static void cpu_4xx_wdt_cb (void *opaque) /* Cannot occur, but makes gcc happy */ return; } - next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->decr_freq); - if (next == now) - next++; + next = now + tb_to_ns_round_up(tb_env->decr_freq, next); trace_ppc4xx_wdt(env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) { case 0x0: @@ -1470,6 +1518,12 @@ int ppc_cpu_pir(PowerPCCPU *cpu) return env->spr_cb[SPR_PIR].default_value; } +int ppc_cpu_tir(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + return env->spr_cb[SPR_TIR].default_value; +} + PowerPCCPU *ppc_get_vcpu_by_pir(int pir) { CPUState *cs; @@ -1490,5 +1544,7 @@ void ppc_irq_reset(PowerPCCPU *cpu) CPUPPCState *env = &cpu->env; env->irq_input_state = 0; - kvmppc_set_interrupt(cpu, PPC_INTERRUPT_EXT, 0); + if (kvm_enabled()) { + kvmppc_set_interrupt(cpu, PPC_INTERRUPT_EXT, 0); + } } diff --git a/hw/ppc/ppc405.h b/hw/ppc/ppc405.h index 83f156f585..9a4312691e 100644 --- a/hw/ppc/ppc405.h +++ b/hw/ppc/ppc405.h @@ -25,54 +25,162 @@ #ifndef PPC405_H #define PPC405_H +#include "qom/object.h" #include "hw/ppc/ppc4xx.h" +#include "hw/intc/ppc-uic.h" +#include "hw/i2c/ppc4xx_i2c.h" -#define PPC405EP_SDRAM_BASE 0x00000000 -#define PPC405EP_NVRAM_BASE 0xF0000000 -#define PPC405EP_FPGA_BASE 0xF0300000 -#define PPC405EP_SRAM_BASE 0xFFF00000 -#define PPC405EP_SRAM_SIZE (512 * KiB) -#define PPC405EP_FLASH_BASE 0xFFF80000 +/* PLB to OPB bridge */ +#define TYPE_PPC405_POB "ppc405-pob" +OBJECT_DECLARE_SIMPLE_TYPE(Ppc405PobState, PPC405_POB); +struct Ppc405PobState { + Ppc4xxDcrDeviceState parent_obj; -/* Bootinfo as set-up by u-boot */ -typedef struct ppc4xx_bd_info_t ppc4xx_bd_info_t; -struct ppc4xx_bd_info_t { - uint32_t bi_memstart; - uint32_t bi_memsize; - uint32_t bi_flashstart; - uint32_t bi_flashsize; - uint32_t bi_flashoffset; /* 0x10 */ - uint32_t bi_sramstart; - uint32_t bi_sramsize; - uint32_t bi_bootflags; - uint32_t bi_ipaddr; /* 0x20 */ - uint8_t bi_enetaddr[6]; - uint16_t bi_ethspeed; - uint32_t bi_intfreq; - uint32_t bi_busfreq; /* 0x30 */ - uint32_t bi_baudrate; - uint8_t bi_s_version[4]; - uint8_t bi_r_version[32]; - uint32_t bi_procfreq; - uint32_t bi_plb_busfreq; - uint32_t bi_pci_busfreq; - uint8_t bi_pci_enetaddr[6]; - uint8_t bi_pci_enetaddr2[6]; /* PPC405EP specific */ - uint32_t bi_opbfreq; - uint32_t bi_iic_fast[2]; + uint32_t bear; + uint32_t besr0; + uint32_t besr1; }; -/* PowerPC 405 core */ -ram_addr_t ppc405_set_bootinfo(CPUPPCState *env, ram_addr_t ram_size); +/* OPB arbitrer */ +#define TYPE_PPC405_OPBA "ppc405-opba" +OBJECT_DECLARE_SIMPLE_TYPE(Ppc405OpbaState, PPC405_OPBA); +struct Ppc405OpbaState { + SysBusDevice parent_obj; -void ppc4xx_plb_init(CPUPPCState *env); -void ppc405_ebc_init(CPUPPCState *env); + MemoryRegion io; + uint8_t cr; + uint8_t pr; +}; -PowerPCCPU *ppc405ep_init(MemoryRegion *address_space_mem, - MemoryRegion ram_memories[2], - hwaddr ram_bases[2], - hwaddr ram_sizes[2], - uint32_t sysclk, DeviceState **uicdev, - int do_init); +/* DMA controller */ +#define TYPE_PPC405_DMA "ppc405-dma" +OBJECT_DECLARE_SIMPLE_TYPE(Ppc405DmaState, PPC405_DMA); +struct Ppc405DmaState { + Ppc4xxDcrDeviceState parent_obj; + + qemu_irq irqs[4]; + uint32_t cr[4]; + uint32_t ct[4]; + uint32_t da[4]; + uint32_t sa[4]; + uint32_t sg[4]; + uint32_t sr; + uint32_t sgc; + uint32_t slp; + uint32_t pol; +}; + +/* GPIO */ +#define TYPE_PPC405_GPIO "ppc405-gpio" +OBJECT_DECLARE_SIMPLE_TYPE(Ppc405GpioState, PPC405_GPIO); +struct Ppc405GpioState { + SysBusDevice parent_obj; + + MemoryRegion io; + uint32_t or; + uint32_t tcr; + uint32_t osrh; + uint32_t osrl; + uint32_t tsrh; + uint32_t tsrl; + uint32_t odr; + uint32_t ir; + uint32_t rr1; + uint32_t isr1h; + uint32_t isr1l; +}; + +/* On Chip Memory */ +#define TYPE_PPC405_OCM "ppc405-ocm" +OBJECT_DECLARE_SIMPLE_TYPE(Ppc405OcmState, PPC405_OCM); +struct Ppc405OcmState { + Ppc4xxDcrDeviceState parent_obj; + + MemoryRegion ram; + MemoryRegion isarc_ram; + MemoryRegion dsarc_ram; + uint32_t isarc; + uint32_t isacntl; + uint32_t dsarc; + uint32_t dsacntl; +}; + +/* General purpose timers */ +#define TYPE_PPC405_GPT "ppc405-gpt" +OBJECT_DECLARE_SIMPLE_TYPE(Ppc405GptState, PPC405_GPT); +struct Ppc405GptState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + int64_t tb_offset; + uint32_t tb_freq; + QEMUTimer *timer; + qemu_irq irqs[5]; + uint32_t oe; + uint32_t ol; + uint32_t im; + uint32_t is; + uint32_t ie; + uint32_t comp[5]; + uint32_t mask[5]; +}; + +#define TYPE_PPC405_CPC "ppc405-cpc" +OBJECT_DECLARE_SIMPLE_TYPE(Ppc405CpcState, PPC405_CPC); + +enum { + PPC405EP_CPU_CLK = 0, + PPC405EP_PLB_CLK = 1, + PPC405EP_OPB_CLK = 2, + PPC405EP_EBC_CLK = 3, + PPC405EP_MAL_CLK = 4, + PPC405EP_PCI_CLK = 5, + PPC405EP_UART0_CLK = 6, + PPC405EP_UART1_CLK = 7, + PPC405EP_CLK_NB = 8, +}; + +struct Ppc405CpcState { + Ppc4xxDcrDeviceState parent_obj; + + uint32_t sysclk; + clk_setup_t clk_setup[PPC405EP_CLK_NB]; + uint32_t boot; + uint32_t epctl; + uint32_t pllmr[2]; + uint32_t ucr; + uint32_t srr; + uint32_t jtagid; + uint32_t pci; + /* Clock and power management */ + uint32_t er; + uint32_t fr; + uint32_t sr; +}; + +#define TYPE_PPC405_SOC "ppc405-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Ppc405SoCState, PPC405_SOC); + +struct Ppc405SoCState { + /* Private */ + DeviceState parent_obj; + + /* Public */ + PowerPCCPU cpu; + PPCUIC uic; + Ppc405CpcState cpc; + Ppc405GptState gpt; + Ppc405OcmState ocm; + Ppc405GpioState gpio; + Ppc405DmaState dma; + PPC4xxI2CState i2c; + Ppc4xxEbcState ebc; + Ppc405OpbaState opba; + Ppc405PobState pob; + Ppc4xxPlbState plb; + Ppc4xxMalState mal; + Ppc4xxSdramDdrState sdram; +}; #endif /* PPC405_H */ diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index a66ad05e3a..4092ebc1ab 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -48,97 +48,24 @@ #define KERNEL_LOAD_ADDR 0x01000000 #define INITRD_LOAD_ADDR 0x01800000 +#define PPC405EP_SDRAM_BASE 0x00000000 +#define PPC405EP_SRAM_BASE 0xFFF00000 +#define PPC405EP_SRAM_SIZE (512 * KiB) + #define USE_FLASH_BIOS -/*****************************************************************************/ -/* PPC405EP reference board (IBM) */ -/* Standalone board with: - * - PowerPC 405EP CPU - * - SDRAM (0x00000000) - * - Flash (0xFFF80000) - * - SRAM (0xFFF00000) - * - NVRAM (0xF0000000) - * - FPGA (0xF0300000) - */ -typedef struct ref405ep_fpga_t ref405ep_fpga_t; -struct ref405ep_fpga_t { - uint8_t reg0; - uint8_t reg1; +#define TYPE_PPC405_MACHINE MACHINE_TYPE_NAME("ppc405") +OBJECT_DECLARE_SIMPLE_TYPE(Ppc405MachineState, PPC405_MACHINE); + +struct Ppc405MachineState { + /* Private */ + MachineState parent_obj; + /* Public */ + + Ppc405SoCState soc; }; -static uint64_t ref405ep_fpga_readb(void *opaque, hwaddr addr, unsigned size) -{ - ref405ep_fpga_t *fpga; - uint32_t ret; - - fpga = opaque; - switch (addr) { - case 0x0: - ret = fpga->reg0; - break; - case 0x1: - ret = fpga->reg1; - break; - default: - ret = 0; - break; - } - - return ret; -} - -static void ref405ep_fpga_writeb(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - ref405ep_fpga_t *fpga; - - fpga = opaque; - switch (addr) { - case 0x0: - /* Read only */ - break; - case 0x1: - fpga->reg1 = value; - break; - default: - break; - } -} - -static const MemoryRegionOps ref405ep_fpga_ops = { - .read = ref405ep_fpga_readb, - .write = ref405ep_fpga_writeb, - .impl.min_access_size = 1, - .impl.max_access_size = 1, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_BIG_ENDIAN, -}; - -static void ref405ep_fpga_reset (void *opaque) -{ - ref405ep_fpga_t *fpga; - - fpga = opaque; - fpga->reg0 = 0x00; - fpga->reg1 = 0x0F; -} - -static void ref405ep_fpga_init(MemoryRegion *sysmem, uint32_t base) -{ - ref405ep_fpga_t *fpga; - MemoryRegion *fpga_memory = g_new(MemoryRegion, 1); - - fpga = g_new0(ref405ep_fpga_t, 1); - memory_region_init_io(fpga_memory, NULL, &ref405ep_fpga_ops, fpga, - "fpga", 0x00000100); - memory_region_add_subregion(sysmem, base, fpga_memory); - qemu_register_reset(&ref405ep_fpga_reset, fpga); -} - -/* - * CPU reset handler when booting directly from a loaded kernel - */ +/* CPU reset handler when booting directly from a loaded kernel */ static struct boot_info { uint32_t entry; uint32_t bdloc; @@ -169,6 +96,126 @@ static void main_cpu_reset(void *opaque) env->nip = bi->entry; } +/* Bootinfo as set-up by u-boot */ +typedef struct { + uint32_t bi_memstart; + uint32_t bi_memsize; + uint32_t bi_flashstart; + uint32_t bi_flashsize; + uint32_t bi_flashoffset; /* 0x10 */ + uint32_t bi_sramstart; + uint32_t bi_sramsize; + uint32_t bi_bootflags; + uint32_t bi_ipaddr; /* 0x20 */ + uint8_t bi_enetaddr[6]; + uint16_t bi_ethspeed; + uint32_t bi_intfreq; + uint32_t bi_busfreq; /* 0x30 */ + uint32_t bi_baudrate; + uint8_t bi_s_version[4]; + uint8_t bi_r_version[32]; + uint32_t bi_procfreq; + uint32_t bi_plb_busfreq; + uint32_t bi_pci_busfreq; + uint8_t bi_pci_enetaddr[6]; + uint8_t bi_pci_enetaddr2[6]; /* PPC405EP specific */ + uint32_t bi_opbfreq; + uint32_t bi_iic_fast[2]; +} ppc4xx_bd_info_t; + +static void ppc405_set_default_bootinfo(ppc4xx_bd_info_t *bd, + ram_addr_t ram_size) +{ + memset(bd, 0, sizeof(*bd)); + + bd->bi_memstart = PPC405EP_SDRAM_BASE; + bd->bi_memsize = ram_size; + bd->bi_sramstart = PPC405EP_SRAM_BASE; + bd->bi_sramsize = PPC405EP_SRAM_SIZE; + bd->bi_bootflags = 0; + bd->bi_intfreq = 133333333; + bd->bi_busfreq = 33333333; + bd->bi_baudrate = 115200; + bd->bi_s_version[0] = 'Q'; + bd->bi_s_version[1] = 'M'; + bd->bi_s_version[2] = 'U'; + bd->bi_s_version[3] = '\0'; + bd->bi_r_version[0] = 'Q'; + bd->bi_r_version[1] = 'E'; + bd->bi_r_version[2] = 'M'; + bd->bi_r_version[3] = 'U'; + bd->bi_r_version[4] = '\0'; + bd->bi_procfreq = 133333333; + bd->bi_plb_busfreq = 33333333; + bd->bi_pci_busfreq = 33333333; + bd->bi_opbfreq = 33333333; +} + +static ram_addr_t __ppc405_set_bootinfo(CPUPPCState *env, ppc4xx_bd_info_t *bd) +{ + CPUState *cs = env_cpu(env); + ram_addr_t bdloc; + int i, n; + + /* We put the bd structure at the top of memory */ + if (bd->bi_memsize >= 0x01000000UL) { + bdloc = 0x01000000UL - sizeof(ppc4xx_bd_info_t); + } else { + bdloc = bd->bi_memsize - sizeof(ppc4xx_bd_info_t); + } + stl_be_phys(cs->as, bdloc + 0x00, bd->bi_memstart); + stl_be_phys(cs->as, bdloc + 0x04, bd->bi_memsize); + stl_be_phys(cs->as, bdloc + 0x08, bd->bi_flashstart); + stl_be_phys(cs->as, bdloc + 0x0C, bd->bi_flashsize); + stl_be_phys(cs->as, bdloc + 0x10, bd->bi_flashoffset); + stl_be_phys(cs->as, bdloc + 0x14, bd->bi_sramstart); + stl_be_phys(cs->as, bdloc + 0x18, bd->bi_sramsize); + stl_be_phys(cs->as, bdloc + 0x1C, bd->bi_bootflags); + stl_be_phys(cs->as, bdloc + 0x20, bd->bi_ipaddr); + for (i = 0; i < 6; i++) { + stb_phys(cs->as, bdloc + 0x24 + i, bd->bi_enetaddr[i]); + } + stw_be_phys(cs->as, bdloc + 0x2A, bd->bi_ethspeed); + stl_be_phys(cs->as, bdloc + 0x2C, bd->bi_intfreq); + stl_be_phys(cs->as, bdloc + 0x30, bd->bi_busfreq); + stl_be_phys(cs->as, bdloc + 0x34, bd->bi_baudrate); + for (i = 0; i < 4; i++) { + stb_phys(cs->as, bdloc + 0x38 + i, bd->bi_s_version[i]); + } + for (i = 0; i < 32; i++) { + stb_phys(cs->as, bdloc + 0x3C + i, bd->bi_r_version[i]); + } + stl_be_phys(cs->as, bdloc + 0x5C, bd->bi_procfreq); + stl_be_phys(cs->as, bdloc + 0x60, bd->bi_plb_busfreq); + stl_be_phys(cs->as, bdloc + 0x64, bd->bi_pci_busfreq); + for (i = 0; i < 6; i++) { + stb_phys(cs->as, bdloc + 0x68 + i, bd->bi_pci_enetaddr[i]); + } + n = 0x70; /* includes 2 bytes hole */ + for (i = 0; i < 6; i++) { + stb_phys(cs->as, bdloc + n++, bd->bi_pci_enetaddr2[i]); + } + stl_be_phys(cs->as, bdloc + n, bd->bi_opbfreq); + n += 4; + for (i = 0; i < 2; i++) { + stl_be_phys(cs->as, bdloc + n, bd->bi_iic_fast[i]); + n += 4; + } + + return bdloc; +} + +static ram_addr_t ppc405_set_bootinfo(CPUPPCState *env, ram_addr_t ram_size) +{ + ppc4xx_bd_info_t bd; + + memset(&bd, 0, sizeof(bd)); + + ppc405_set_default_bootinfo(&bd, ram_size); + + return __ppc405_set_bootinfo(env, &bd); +} + static void boot_from_kernel(MachineState *machine, PowerPCCPU *cpu) { CPUPPCState *env = &cpu->env; @@ -221,42 +268,19 @@ static void boot_from_kernel(MachineState *machine, PowerPCCPU *cpu) env->load_info = &boot_info; } -static void ref405ep_init(MachineState *machine) +static void ppc405_init(MachineState *machine) { - MachineClass *mc = MACHINE_GET_CLASS(machine); + Ppc405MachineState *ppc405 = PPC405_MACHINE(machine); const char *kernel_filename = machine->kernel_filename; - PowerPCCPU *cpu; - DeviceState *dev; - SysBusDevice *s; - MemoryRegion *sram = g_new(MemoryRegion, 1); - MemoryRegion *ram_memories = g_new(MemoryRegion, 2); - hwaddr ram_bases[2], ram_sizes[2]; MemoryRegion *sysmem = get_system_memory(); - DeviceState *uicdev; - 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_init_alias(&ram_memories[0], NULL, "ef405ep.ram.alias", - machine->ram, 0, machine->ram_size); - ram_bases[0] = 0; - ram_sizes[0] = machine->ram_size; - memory_region_init(&ram_memories[1], NULL, "ef405ep.ram1", 0); - ram_bases[1] = 0x00000000; - ram_sizes[1] = 0x00000000; - - cpu = ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes, - 33333333, &uicdev, kernel_filename == NULL ? 0 : 1); - - /* allocate SRAM */ - memory_region_init_ram(sram, NULL, "ef405ep.sram", PPC405EP_SRAM_SIZE, - &error_fatal); - memory_region_add_subregion(sysmem, PPC405EP_SRAM_BASE, sram); + object_initialize_child(OBJECT(machine), "soc", &ppc405->soc, + TYPE_PPC405_SOC); + object_property_set_link(OBJECT(&ppc405->soc), "dram", + OBJECT(machine->ram), &error_abort); + object_property_set_uint(OBJECT(&ppc405->soc), "sys-clk", 33333333, + &error_abort); + qdev_realize(DEVICE(&ppc405->soc), NULL, &error_fatal); /* allocate and load BIOS */ if (machine->firmware) { @@ -285,15 +309,6 @@ static void ref405ep_init(MachineState *machine) memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios); } - /* Register FPGA */ - ref405ep_fpga_init(sysmem, PPC405EP_FPGA_BASE); - /* Register NVRAM */ - dev = qdev_new("sysbus-m48t08"); - qdev_prop_set_int32(dev, "base-year", 1968); - s = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(s, &error_fatal); - sysbus_mmio_map(s, 0, PPC405EP_NVRAM_BASE); - /* Load kernel and initrd using U-Boot images */ if (kernel_filename && machine->firmware) { target_ulong kernel_base, initrd_base; @@ -322,63 +337,67 @@ static void ref405ep_init(MachineState *machine) /* Load ELF kernel and rootfs.cpio */ } else if (kernel_filename && !machine->firmware) { - boot_from_kernel(machine, cpu); + ppc4xx_sdram_ddr_enable(&ppc405->soc.sdram); + boot_from_kernel(machine, &ppc405->soc.cpu); } } -static void ref405ep_class_init(ObjectClass *oc, void *data) +static void ppc405_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->desc = "ref405ep"; - mc->init = ref405ep_init; - mc->default_ram_size = 0x08000000; - mc->default_ram_id = "ef405ep.ram"; + mc->desc = "PPC405 generic machine"; + mc->init = ppc405_init; + mc->default_ram_size = 128 * MiB; + mc->default_ram_id = "ppc405.ram"; } -static const TypeInfo ref405ep_type = { - .name = MACHINE_TYPE_NAME("ref405ep"), +static const TypeInfo ppc405_machine_type = { + .name = TYPE_PPC405_MACHINE, .parent = TYPE_MACHINE, - .class_init = ref405ep_class_init, + .instance_size = sizeof(Ppc405MachineState), + .class_init = ppc405_machine_class_init, + .abstract = true, }; /*****************************************************************************/ -/* AMCC Taihu evaluation board */ -/* - PowerPC 405EP processor - * - SDRAM 128 MB at 0x00000000 - * - Boot flash 2 MB at 0xFFE00000 - * - Application flash 32 MB at 0xFC000000 - * - 2 serial ports - * - 2 ethernet PHY - * - 1 USB 1.1 device 0x50000000 - * - 1 LCD display 0x50100000 - * - 1 CPLD 0x50100000 - * - 1 I2C EEPROM - * - 1 I2C thermal sensor - * - a set of LEDs - * - bit-bang SPI port using GPIOs - * - 1 EBC interface connector 0 0x50200000 - * - 1 cardbus controller + expansion slot. - * - 1 PCI expansion slot. +/* PPC405EP reference board (IBM) */ +/* + * Standalone board with: + * - PowerPC 405EP CPU + * - SDRAM (0x00000000) + * - Flash (0xFFF80000) + * - SRAM (0xFFF00000) + * - NVRAM (0xF0000000) + * - FPGA (0xF0300000) */ -typedef struct taihu_cpld_t taihu_cpld_t; -struct taihu_cpld_t { + +#define PPC405EP_NVRAM_BASE 0xF0000000 +#define PPC405EP_FPGA_BASE 0xF0300000 +#define PPC405EP_FLASH_BASE 0xFFF80000 + +#define TYPE_REF405EP_FPGA "ref405ep-fpga" +OBJECT_DECLARE_SIMPLE_TYPE(Ref405epFpgaState, REF405EP_FPGA); +struct Ref405epFpgaState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + uint8_t reg0; uint8_t reg1; }; -static uint64_t taihu_cpld_read(void *opaque, hwaddr addr, unsigned size) +static uint64_t ref405ep_fpga_readb(void *opaque, hwaddr addr, unsigned size) { - taihu_cpld_t *cpld; + Ref405epFpgaState *fpga = opaque; uint32_t ret; - cpld = opaque; switch (addr) { case 0x0: - ret = cpld->reg0; + ret = fpga->reg0; break; case 0x1: - ret = cpld->reg1; + ret = fpga->reg1; break; default: ret = 0; @@ -388,195 +407,113 @@ static uint64_t taihu_cpld_read(void *opaque, hwaddr addr, unsigned size) return ret; } -static void taihu_cpld_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) +static void ref405ep_fpga_writeb(void *opaque, hwaddr addr, uint64_t value, + unsigned size) { - taihu_cpld_t *cpld; + Ref405epFpgaState *fpga = opaque; - cpld = opaque; switch (addr) { case 0x0: /* Read only */ break; case 0x1: - cpld->reg1 = value; + fpga->reg1 = value; break; default: break; } } -static const MemoryRegionOps taihu_cpld_ops = { - .read = taihu_cpld_read, - .write = taihu_cpld_write, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - .endianness = DEVICE_NATIVE_ENDIAN, +static const MemoryRegionOps ref405ep_fpga_ops = { + .read = ref405ep_fpga_readb, + .write = ref405ep_fpga_writeb, + .impl.min_access_size = 1, + .impl.max_access_size = 1, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, }; -static void taihu_cpld_reset (void *opaque) +static void ref405ep_fpga_reset(DeviceState *dev) { - taihu_cpld_t *cpld; + Ref405epFpgaState *fpga = REF405EP_FPGA(dev); - cpld = opaque; - cpld->reg0 = 0x01; - cpld->reg1 = 0x80; + fpga->reg0 = 0x00; + fpga->reg1 = 0x0F; } -static void taihu_cpld_init(MemoryRegion *sysmem, uint32_t base) +static void ref405ep_fpga_realize(DeviceState *dev, Error **errp) { - taihu_cpld_t *cpld; - MemoryRegion *cpld_memory = g_new(MemoryRegion, 1); + Ref405epFpgaState *s = REF405EP_FPGA(dev); - cpld = g_new0(taihu_cpld_t, 1); - memory_region_init_io(cpld_memory, NULL, &taihu_cpld_ops, cpld, "cpld", 0x100); - memory_region_add_subregion(sysmem, base, cpld_memory); - qemu_register_reset(&taihu_cpld_reset, cpld); + memory_region_init_io(&s->iomem, OBJECT(s), &ref405ep_fpga_ops, s, + "fpga", 0x00000100); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); } -static void taihu_405ep_init(MachineState *machine) +static void ref405ep_fpga_class_init(ObjectClass *oc, void *data) { - MachineClass *mc = MACHINE_GET_CLASS(machine); - const char *bios_name = machine->firmware ?: BIOS_FILENAME; - const char *kernel_filename = machine->kernel_filename; - const char *initrd_filename = machine->initrd_filename; - char *filename; - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *bios; - MemoryRegion *ram_memories = g_new(MemoryRegion, 2); - hwaddr ram_bases[2], ram_sizes[2]; - long bios_size; - target_ulong kernel_base, initrd_base; - long kernel_size, initrd_size; - int linux_boot; - int fl_idx; - DriveInfo *dinfo; - DeviceState *uicdev; + DeviceClass *dc = DEVICE_CLASS(oc); - 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", 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", machine->ram, ram_bases[1], - ram_sizes[1]); - ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes, - 33333333, &uicdev, kernel_filename == NULL ? 0 : 1); - /* allocate and load BIOS */ - fl_idx = 0; -#if defined(USE_FLASH_BIOS) - dinfo = drive_get(IF_PFLASH, 0, fl_idx); - if (dinfo) { - bios_size = 2 * MiB; - pflash_cfi02_register(0xFFE00000, - "taihu_405ep.bios", bios_size, - blk_by_legacy_dinfo(dinfo), - 64 * KiB, 1, - 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, - 1); - fl_idx++; - } else -#endif - { - bios = g_new(MemoryRegion, 1); - 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) { - bios_size = load_image_size(filename, - memory_region_get_ram_ptr(bios), - BIOS_SIZE); - g_free(filename); - if (bios_size < 0) { - error_report("Could not load PowerPC BIOS '%s'", bios_name); - exit(1); - } - bios_size = (bios_size + 0xfff) & ~0xfff; - memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios); - } else if (!qtest_enabled()) { - error_report("Could not load PowerPC BIOS '%s'", bios_name); - exit(1); - } - } - /* 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, - blk_by_legacy_dinfo(dinfo), - 64 * KiB, 1, - 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, - 1); - fl_idx++; - } - /* Register CLPD & LCD display */ - taihu_cpld_init(sysmem, 0x50100000); - /* Load kernel */ - linux_boot = (kernel_filename != NULL); - if (linux_boot) { - kernel_base = KERNEL_LOAD_ADDR; - /* now we can load the kernel */ - kernel_size = load_image_targphys(kernel_filename, kernel_base, - machine->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, - machine->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; - } - } else { - kernel_base = 0; - kernel_size = 0; - initrd_base = 0; - initrd_size = 0; - } + dc->realize = ref405ep_fpga_realize; + dc->reset = ref405ep_fpga_reset; + /* Reason: only works as part of a ppc405 board */ + dc->user_creatable = false; } -static void taihu_class_init(ObjectClass *oc, void *data) +static const TypeInfo ref405ep_fpga_type = { + .name = TYPE_REF405EP_FPGA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Ref405epFpgaState), + .class_init = ref405ep_fpga_class_init, +}; + +static void ref405ep_init(MachineState *machine) +{ + DeviceState *dev; + SysBusDevice *s; + MemoryRegion *sram = g_new(MemoryRegion, 1); + + ppc405_init(machine); + + /* allocate SRAM */ + memory_region_init_ram(sram, NULL, "ref405ep.sram", PPC405EP_SRAM_SIZE, + &error_fatal); + memory_region_add_subregion(get_system_memory(), PPC405EP_SRAM_BASE, sram); + + /* Register FPGA */ + dev = qdev_new(TYPE_REF405EP_FPGA); + object_property_add_child(OBJECT(machine), "fpga", OBJECT(dev)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, PPC405EP_FPGA_BASE); + + /* Register NVRAM */ + dev = qdev_new("sysbus-m48t08"); + qdev_prop_set_int32(dev, "base-year", 1968); + s = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(s, &error_fatal); + sysbus_mmio_map(s, 0, PPC405EP_NVRAM_BASE); +} + +static void ref405ep_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->desc = "taihu"; - mc->init = taihu_405ep_init; - mc->default_ram_size = 0x08000000; - mc->default_ram_id = "taihu_405ep.ram"; - mc->deprecation_reason = "incomplete, use 'ref405ep' instead"; + mc->desc = "ref405ep"; + mc->init = ref405ep_init; } -static const TypeInfo taihu_type = { - .name = MACHINE_TYPE_NAME("taihu"), - .parent = TYPE_MACHINE, - .class_init = taihu_class_init, +static const TypeInfo ref405ep_type = { + .name = MACHINE_TYPE_NAME("ref405ep"), + .parent = TYPE_PPC405_MACHINE, + .class_init = ref405ep_class_init, }; static void ppc405_machine_init(void) { + type_register_static(&ppc405_machine_type); type_register_static(&ref405ep_type); - type_register_static(&taihu_type); + type_register_static(&ref405ep_fpga_type); } type_init(ppc405_machine_init) diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index 36c8ba6f3c..0cc68178ad 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -30,6 +30,7 @@ #include "hw/ppc/ppc.h" #include "hw/i2c/ppc4xx_i2c.h" #include "hw/irq.h" +#include "hw/qdev-properties.h" #include "ppc405.h" #include "hw/char/serial.h" #include "qemu/timer.h" @@ -37,194 +38,11 @@ #include "sysemu/sysemu.h" #include "exec/address-spaces.h" #include "hw/intc/ppc-uic.h" -#include "hw/qdev-properties.h" -#include "qapi/error.h" #include "trace.h" -static void ppc405_set_default_bootinfo(ppc4xx_bd_info_t *bd, - ram_addr_t ram_size) -{ - memset(bd, 0, sizeof(*bd)); - - bd->bi_memstart = PPC405EP_SDRAM_BASE; - bd->bi_memsize = ram_size; - bd->bi_sramstart = PPC405EP_SRAM_BASE; - bd->bi_sramsize = PPC405EP_SRAM_SIZE; - bd->bi_bootflags = 0; - bd->bi_intfreq = 133333333; - bd->bi_busfreq = 33333333; - bd->bi_baudrate = 115200; - bd->bi_s_version[0] = 'Q'; - bd->bi_s_version[1] = 'M'; - bd->bi_s_version[2] = 'U'; - bd->bi_s_version[3] = '\0'; - bd->bi_r_version[0] = 'Q'; - bd->bi_r_version[1] = 'E'; - bd->bi_r_version[2] = 'M'; - bd->bi_r_version[3] = 'U'; - bd->bi_r_version[4] = '\0'; - bd->bi_procfreq = 133333333; - bd->bi_plb_busfreq = 33333333; - bd->bi_pci_busfreq = 33333333; - bd->bi_opbfreq = 33333333; -} - -static ram_addr_t __ppc405_set_bootinfo(CPUPPCState *env, ppc4xx_bd_info_t *bd) -{ - CPUState *cs = env_cpu(env); - ram_addr_t bdloc; - int i, n; - - /* We put the bd structure at the top of memory */ - if (bd->bi_memsize >= 0x01000000UL) - bdloc = 0x01000000UL - sizeof(struct ppc4xx_bd_info_t); - else - bdloc = bd->bi_memsize - sizeof(struct ppc4xx_bd_info_t); - stl_be_phys(cs->as, bdloc + 0x00, bd->bi_memstart); - stl_be_phys(cs->as, bdloc + 0x04, bd->bi_memsize); - stl_be_phys(cs->as, bdloc + 0x08, bd->bi_flashstart); - stl_be_phys(cs->as, bdloc + 0x0C, bd->bi_flashsize); - stl_be_phys(cs->as, bdloc + 0x10, bd->bi_flashoffset); - stl_be_phys(cs->as, bdloc + 0x14, bd->bi_sramstart); - stl_be_phys(cs->as, bdloc + 0x18, bd->bi_sramsize); - stl_be_phys(cs->as, bdloc + 0x1C, bd->bi_bootflags); - stl_be_phys(cs->as, bdloc + 0x20, bd->bi_ipaddr); - for (i = 0; i < 6; i++) { - stb_phys(cs->as, bdloc + 0x24 + i, bd->bi_enetaddr[i]); - } - stw_be_phys(cs->as, bdloc + 0x2A, bd->bi_ethspeed); - stl_be_phys(cs->as, bdloc + 0x2C, bd->bi_intfreq); - stl_be_phys(cs->as, bdloc + 0x30, bd->bi_busfreq); - stl_be_phys(cs->as, bdloc + 0x34, bd->bi_baudrate); - for (i = 0; i < 4; i++) { - stb_phys(cs->as, bdloc + 0x38 + i, bd->bi_s_version[i]); - } - for (i = 0; i < 32; i++) { - stb_phys(cs->as, bdloc + 0x3C + i, bd->bi_r_version[i]); - } - stl_be_phys(cs->as, bdloc + 0x5C, bd->bi_procfreq); - stl_be_phys(cs->as, bdloc + 0x60, bd->bi_plb_busfreq); - stl_be_phys(cs->as, bdloc + 0x64, bd->bi_pci_busfreq); - for (i = 0; i < 6; i++) { - stb_phys(cs->as, bdloc + 0x68 + i, bd->bi_pci_enetaddr[i]); - } - n = 0x70; /* includes 2 bytes hole */ - for (i = 0; i < 6; i++) { - stb_phys(cs->as, bdloc + n++, bd->bi_pci_enetaddr2[i]); - } - stl_be_phys(cs->as, bdloc + n, bd->bi_opbfreq); - n += 4; - for (i = 0; i < 2; i++) { - stl_be_phys(cs->as, bdloc + n, bd->bi_iic_fast[i]); - n += 4; - } - - return bdloc; -} - -ram_addr_t ppc405_set_bootinfo(CPUPPCState *env, ram_addr_t ram_size) -{ - ppc4xx_bd_info_t bd; - - memset(&bd, 0, sizeof(bd)); - - ppc405_set_default_bootinfo(&bd, ram_size); - - return __ppc405_set_bootinfo(env, &bd); -} - /*****************************************************************************/ /* Shared peripherals */ -/*****************************************************************************/ -/* Peripheral local bus arbitrer */ -enum { - PLB3A0_ACR = 0x077, - PLB4A0_ACR = 0x081, - PLB0_BESR = 0x084, - PLB0_BEAR = 0x086, - PLB0_ACR = 0x087, - PLB4A1_ACR = 0x089, -}; - -typedef struct ppc4xx_plb_t ppc4xx_plb_t; -struct ppc4xx_plb_t { - uint32_t acr; - uint32_t bear; - uint32_t besr; -}; - -static uint32_t dcr_read_plb (void *opaque, int dcrn) -{ - ppc4xx_plb_t *plb; - uint32_t ret; - - plb = opaque; - switch (dcrn) { - case PLB0_ACR: - ret = plb->acr; - break; - case PLB0_BEAR: - ret = plb->bear; - break; - case PLB0_BESR: - ret = plb->besr; - break; - default: - /* Avoid gcc warning */ - ret = 0; - break; - } - - return ret; -} - -static void dcr_write_plb (void *opaque, int dcrn, uint32_t val) -{ - ppc4xx_plb_t *plb; - - plb = opaque; - switch (dcrn) { - case PLB0_ACR: - /* We don't care about the actual parameters written as - * we don't manage any priorities on the bus - */ - plb->acr = val & 0xF8000000; - break; - case PLB0_BEAR: - /* Read only */ - break; - case PLB0_BESR: - /* Write-clear */ - plb->besr &= ~val; - break; - } -} - -static void ppc4xx_plb_reset (void *opaque) -{ - ppc4xx_plb_t *plb; - - plb = opaque; - plb->acr = 0x00000000; - plb->bear = 0x00000000; - plb->besr = 0x00000000; -} - -void ppc4xx_plb_init(CPUPPCState *env) -{ - ppc4xx_plb_t *plb; - - plb = g_new0(ppc4xx_plb_t, 1); - ppc_dcr_register(env, PLB3A0_ACR, plb, &dcr_read_plb, &dcr_write_plb); - ppc_dcr_register(env, PLB4A0_ACR, plb, &dcr_read_plb, &dcr_write_plb); - ppc_dcr_register(env, PLB0_ACR, plb, &dcr_read_plb, &dcr_write_plb); - ppc_dcr_register(env, PLB0_BEAR, plb, &dcr_read_plb, &dcr_write_plb); - ppc_dcr_register(env, PLB0_BESR, plb, &dcr_read_plb, &dcr_write_plb); - ppc_dcr_register(env, PLB4A1_ACR, plb, &dcr_read_plb, &dcr_write_plb); - qemu_register_reset(ppc4xx_plb_reset, plb); -} - /*****************************************************************************/ /* PLB to OPB bridge */ enum { @@ -233,19 +51,11 @@ enum { POB0_BEAR = 0x0A4, }; -typedef struct ppc4xx_pob_t ppc4xx_pob_t; -struct ppc4xx_pob_t { - uint32_t bear; - uint32_t besr0; - uint32_t besr1; -}; - -static uint32_t dcr_read_pob (void *opaque, int dcrn) +static uint32_t dcr_read_pob(void *opaque, int dcrn) { - ppc4xx_pob_t *pob; + Ppc405PobState *pob = opaque; uint32_t ret; - pob = opaque; switch (dcrn) { case POB0_BEAR: ret = pob->bear; @@ -265,11 +75,10 @@ static uint32_t dcr_read_pob (void *opaque, int dcrn) return ret; } -static void dcr_write_pob (void *opaque, int dcrn, uint32_t val) +static void dcr_write_pob(void *opaque, int dcrn, uint32_t val) { - ppc4xx_pob_t *pob; + Ppc405PobState *pob = opaque; - pob = opaque; switch (dcrn) { case POB0_BEAR: /* Read only */ @@ -285,40 +94,41 @@ static void dcr_write_pob (void *opaque, int dcrn, uint32_t val) } } -static void ppc4xx_pob_reset (void *opaque) +static void ppc405_pob_reset(DeviceState *dev) { - ppc4xx_pob_t *pob; + Ppc405PobState *pob = PPC405_POB(dev); - pob = opaque; /* No error */ pob->bear = 0x00000000; pob->besr0 = 0x0000000; pob->besr1 = 0x0000000; } -static void ppc4xx_pob_init(CPUPPCState *env) +static void ppc405_pob_realize(DeviceState *dev, Error **errp) { - ppc4xx_pob_t *pob; + Ppc405PobState *pob = PPC405_POB(dev); + Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); - pob = g_new0(ppc4xx_pob_t, 1); - ppc_dcr_register(env, POB0_BEAR, pob, &dcr_read_pob, &dcr_write_pob); - ppc_dcr_register(env, POB0_BESR0, pob, &dcr_read_pob, &dcr_write_pob); - ppc_dcr_register(env, POB0_BESR1, pob, &dcr_read_pob, &dcr_write_pob); - qemu_register_reset(ppc4xx_pob_reset, pob); + ppc4xx_dcr_register(dcr, POB0_BEAR, pob, &dcr_read_pob, &dcr_write_pob); + ppc4xx_dcr_register(dcr, POB0_BESR0, pob, &dcr_read_pob, &dcr_write_pob); + ppc4xx_dcr_register(dcr, POB0_BESR1, pob, &dcr_read_pob, &dcr_write_pob); +} + +static void ppc405_pob_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ppc405_pob_realize; + dc->reset = ppc405_pob_reset; + /* Reason: only works as function of a ppc4xx SoC */ + dc->user_creatable = false; } /*****************************************************************************/ /* OPB arbitrer */ -typedef struct ppc4xx_opba_t ppc4xx_opba_t; -struct ppc4xx_opba_t { - MemoryRegion io; - uint8_t cr; - uint8_t pr; -}; - static uint64_t opba_readb(void *opaque, hwaddr addr, unsigned size) { - ppc4xx_opba_t *opba = opaque; + Ppc405OpbaState *opba = opaque; uint32_t ret; switch (addr) { @@ -340,7 +150,7 @@ static uint64_t opba_readb(void *opaque, hwaddr addr, unsigned size) static void opba_writeb(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - ppc4xx_opba_t *opba = opaque; + Ppc405OpbaState *opba = opaque; trace_opba_writeb(addr, value); @@ -365,224 +175,36 @@ static const MemoryRegionOps opba_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void ppc4xx_opba_reset (void *opaque) +static void ppc405_opba_reset(DeviceState *dev) { - ppc4xx_opba_t *opba; + Ppc405OpbaState *opba = PPC405_OPBA(dev); - opba = opaque; opba->cr = 0x00; /* No dynamic priorities - park disabled */ opba->pr = 0x11; } -static void ppc4xx_opba_init(hwaddr base) +static void ppc405_opba_realize(DeviceState *dev, Error **errp) { - ppc4xx_opba_t *opba; + Ppc405OpbaState *s = PPC405_OPBA(dev); - trace_opba_init(base); + memory_region_init_io(&s->io, OBJECT(s), &opba_ops, s, "opba", 2); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->io); +} - opba = g_new0(ppc4xx_opba_t, 1); - memory_region_init_io(&opba->io, NULL, &opba_ops, opba, "opba", 0x002); - memory_region_add_subregion(get_system_memory(), base, &opba->io); - qemu_register_reset(ppc4xx_opba_reset, opba); +static void ppc405_opba_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ppc405_opba_realize; + dc->reset = ppc405_opba_reset; + /* Reason: only works as function of a ppc4xx SoC */ + dc->user_creatable = false; } /*****************************************************************************/ /* Code decompression controller */ /* XXX: TODO */ -/*****************************************************************************/ -/* Peripheral controller */ -typedef struct ppc4xx_ebc_t ppc4xx_ebc_t; -struct ppc4xx_ebc_t { - uint32_t addr; - uint32_t bcr[8]; - uint32_t bap[8]; - uint32_t bear; - uint32_t besr0; - uint32_t besr1; - uint32_t cfg; -}; - -enum { - EBC0_CFGADDR = 0x012, - EBC0_CFGDATA = 0x013, -}; - -static uint32_t dcr_read_ebc (void *opaque, int dcrn) -{ - ppc4xx_ebc_t *ebc; - uint32_t ret; - - ebc = opaque; - switch (dcrn) { - case EBC0_CFGADDR: - ret = ebc->addr; - break; - case EBC0_CFGDATA: - switch (ebc->addr) { - case 0x00: /* B0CR */ - ret = ebc->bcr[0]; - break; - case 0x01: /* B1CR */ - ret = ebc->bcr[1]; - break; - case 0x02: /* B2CR */ - ret = ebc->bcr[2]; - break; - case 0x03: /* B3CR */ - ret = ebc->bcr[3]; - break; - case 0x04: /* B4CR */ - ret = ebc->bcr[4]; - break; - case 0x05: /* B5CR */ - ret = ebc->bcr[5]; - break; - case 0x06: /* B6CR */ - ret = ebc->bcr[6]; - break; - case 0x07: /* B7CR */ - ret = ebc->bcr[7]; - break; - case 0x10: /* B0AP */ - ret = ebc->bap[0]; - break; - case 0x11: /* B1AP */ - ret = ebc->bap[1]; - break; - case 0x12: /* B2AP */ - ret = ebc->bap[2]; - break; - case 0x13: /* B3AP */ - ret = ebc->bap[3]; - break; - case 0x14: /* B4AP */ - ret = ebc->bap[4]; - break; - case 0x15: /* B5AP */ - ret = ebc->bap[5]; - break; - case 0x16: /* B6AP */ - ret = ebc->bap[6]; - break; - case 0x17: /* B7AP */ - ret = ebc->bap[7]; - break; - case 0x20: /* BEAR */ - ret = ebc->bear; - break; - case 0x21: /* BESR0 */ - ret = ebc->besr0; - break; - case 0x22: /* BESR1 */ - ret = ebc->besr1; - break; - case 0x23: /* CFG */ - ret = ebc->cfg; - break; - default: - ret = 0x00000000; - break; - } - break; - default: - ret = 0x00000000; - break; - } - - return ret; -} - -static void dcr_write_ebc (void *opaque, int dcrn, uint32_t val) -{ - ppc4xx_ebc_t *ebc; - - ebc = opaque; - switch (dcrn) { - case EBC0_CFGADDR: - ebc->addr = val; - break; - case EBC0_CFGDATA: - switch (ebc->addr) { - case 0x00: /* B0CR */ - break; - case 0x01: /* B1CR */ - break; - case 0x02: /* B2CR */ - break; - case 0x03: /* B3CR */ - break; - case 0x04: /* B4CR */ - break; - case 0x05: /* B5CR */ - break; - case 0x06: /* B6CR */ - break; - case 0x07: /* B7CR */ - break; - case 0x10: /* B0AP */ - break; - case 0x11: /* B1AP */ - break; - case 0x12: /* B2AP */ - break; - case 0x13: /* B3AP */ - break; - case 0x14: /* B4AP */ - break; - case 0x15: /* B5AP */ - break; - case 0x16: /* B6AP */ - break; - case 0x17: /* B7AP */ - break; - case 0x20: /* BEAR */ - break; - case 0x21: /* BESR0 */ - break; - case 0x22: /* BESR1 */ - break; - case 0x23: /* CFG */ - break; - default: - break; - } - break; - default: - break; - } -} - -static void ebc_reset (void *opaque) -{ - ppc4xx_ebc_t *ebc; - int i; - - ebc = opaque; - ebc->addr = 0x00000000; - ebc->bap[0] = 0x7F8FFE80; - ebc->bcr[0] = 0xFFE28000; - for (i = 0; i < 8; i++) { - ebc->bap[i] = 0x00000000; - ebc->bcr[i] = 0x00000000; - } - ebc->besr0 = 0x00000000; - ebc->besr1 = 0x00000000; - ebc->cfg = 0x80400000; -} - -void ppc405_ebc_init(CPUPPCState *env) -{ - ppc4xx_ebc_t *ebc; - - ebc = g_new0(ppc4xx_ebc_t, 1); - qemu_register_reset(&ebc_reset, ebc); - ppc_dcr_register(env, EBC0_CFGADDR, - ebc, &dcr_read_ebc, &dcr_write_ebc); - ppc_dcr_register(env, EBC0_CFGDATA, - ebc, &dcr_read_ebc, &dcr_write_ebc); -} - /*****************************************************************************/ /* DMA controller */ enum { @@ -612,35 +234,20 @@ enum { DMA0_POL = 0x126, }; -typedef struct ppc405_dma_t ppc405_dma_t; -struct ppc405_dma_t { - qemu_irq irqs[4]; - uint32_t cr[4]; - uint32_t ct[4]; - uint32_t da[4]; - uint32_t sa[4]; - uint32_t sg[4]; - uint32_t sr; - uint32_t sgc; - uint32_t slp; - uint32_t pol; -}; - -static uint32_t dcr_read_dma (void *opaque, int dcrn) +static uint32_t dcr_read_dma(void *opaque, int dcrn) { return 0; } -static void dcr_write_dma (void *opaque, int dcrn, uint32_t val) +static void dcr_write_dma(void *opaque, int dcrn, uint32_t val) { } -static void ppc405_dma_reset (void *opaque) +static void ppc405_dma_reset(DeviceState *dev) { - ppc405_dma_t *dma; + Ppc405DmaState *dma = PPC405_DMA(dev); int i; - dma = opaque; for (i = 0; i < 4; i++) { dma->cr[i] = 0x00000000; dma->ct[i] = 0x00000000; @@ -654,81 +261,54 @@ static void ppc405_dma_reset (void *opaque) dma->pol = 0x00000000; } -static void ppc405_dma_init(CPUPPCState *env, qemu_irq irqs[4]) +static void ppc405_dma_realize(DeviceState *dev, Error **errp) { - ppc405_dma_t *dma; + Ppc405DmaState *dma = PPC405_DMA(dev); + Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); + int i; - dma = g_new0(ppc405_dma_t, 1); - memcpy(dma->irqs, irqs, 4 * sizeof(qemu_irq)); - qemu_register_reset(&ppc405_dma_reset, dma); - ppc_dcr_register(env, DMA0_CR0, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_CT0, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_DA0, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SA0, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SG0, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_CR1, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_CT1, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_DA1, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SA1, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SG1, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_CR2, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_CT2, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_DA2, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SA2, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SG2, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_CR3, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_CT3, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_DA3, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SA3, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SG3, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SR, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SGC, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SLP, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_POL, - dma, &dcr_read_dma, &dcr_write_dma); + for (i = 0; i < ARRAY_SIZE(dma->irqs); i++) { + sysbus_init_irq(SYS_BUS_DEVICE(dma), &dma->irqs[i]); + } + + ppc4xx_dcr_register(dcr, DMA0_CR0, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_CT0, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_DA0, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_SA0, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_SG0, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_CR1, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_CT1, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_DA1, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_SA1, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_SG1, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_CR2, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_CT2, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_DA2, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_SA2, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_SG2, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_CR3, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_CT3, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_DA3, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_SA3, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_SG3, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_SR, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_SGC, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_SLP, dma, &dcr_read_dma, &dcr_write_dma); + ppc4xx_dcr_register(dcr, DMA0_POL, dma, &dcr_read_dma, &dcr_write_dma); +} + +static void ppc405_dma_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ppc405_dma_realize; + dc->reset = ppc405_dma_reset; + /* Reason: only works as function of a ppc4xx SoC */ + dc->user_creatable = false; } /*****************************************************************************/ /* GPIO */ -typedef struct ppc405_gpio_t ppc405_gpio_t; -struct ppc405_gpio_t { - MemoryRegion io; - uint32_t or; - uint32_t tcr; - uint32_t osrh; - uint32_t osrl; - uint32_t tsrh; - uint32_t tsrl; - uint32_t odr; - uint32_t ir; - uint32_t rr1; - uint32_t isr1h; - uint32_t isr1l; -}; - static uint64_t ppc405_gpio_read(void *opaque, hwaddr addr, unsigned size) { trace_ppc405_gpio_read(addr, size); @@ -747,20 +327,22 @@ static const MemoryRegionOps ppc405_gpio_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void ppc405_gpio_reset (void *opaque) +static void ppc405_gpio_realize(DeviceState *dev, Error **errp) { + Ppc405GpioState *s = PPC405_GPIO(dev); + + memory_region_init_io(&s->io, OBJECT(s), &ppc405_gpio_ops, s, "gpio", + 0x38); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->io); } -static void ppc405_gpio_init(hwaddr base) +static void ppc405_gpio_class_init(ObjectClass *oc, void *data) { - ppc405_gpio_t *gpio; + DeviceClass *dc = DEVICE_CLASS(oc); - trace_ppc405_gpio_init(base); - - gpio = g_new0(ppc405_gpio_t, 1); - memory_region_init_io(&gpio->io, NULL, &ppc405_gpio_ops, gpio, "pgio", 0x038); - memory_region_add_subregion(get_system_memory(), base, &gpio->io); - qemu_register_reset(&ppc405_gpio_reset, gpio); + dc->realize = ppc405_gpio_realize; + /* Reason: only works as function of a ppc4xx SoC */ + dc->user_creatable = false; } /*****************************************************************************/ @@ -772,20 +354,9 @@ enum { OCM0_DSACNTL = 0x01B, }; -typedef struct ppc405_ocm_t ppc405_ocm_t; -struct ppc405_ocm_t { - MemoryRegion ram; - MemoryRegion isarc_ram; - MemoryRegion dsarc_ram; - uint32_t isarc; - uint32_t isacntl; - uint32_t dsarc; - uint32_t dsacntl; -}; - -static void ocm_update_mappings (ppc405_ocm_t *ocm, - uint32_t isarc, uint32_t isacntl, - uint32_t dsarc, uint32_t dsacntl) +static void ocm_update_mappings(Ppc405OcmState *ocm, + uint32_t isarc, uint32_t isacntl, + uint32_t dsarc, uint32_t dsacntl) { trace_ocm_update_mappings(isarc, isacntl, dsarc, dsacntl, ocm->isarc, ocm->isacntl, ocm->dsarc, ocm->dsacntl); @@ -827,12 +398,11 @@ static void ocm_update_mappings (ppc405_ocm_t *ocm, } } -static uint32_t dcr_read_ocm (void *opaque, int dcrn) +static uint32_t dcr_read_ocm(void *opaque, int dcrn) { - ppc405_ocm_t *ocm; + Ppc405OcmState *ocm = opaque; uint32_t ret; - ocm = opaque; switch (dcrn) { case OCM0_ISARC: ret = ocm->isarc; @@ -854,12 +424,11 @@ static uint32_t dcr_read_ocm (void *opaque, int dcrn) return ret; } -static void dcr_write_ocm (void *opaque, int dcrn, uint32_t val) +static void dcr_write_ocm(void *opaque, int dcrn, uint32_t val) { - ppc405_ocm_t *ocm; + Ppc405OcmState *ocm = opaque; uint32_t isarc, dsarc, isacntl, dsacntl; - ocm = opaque; isarc = ocm->isarc; dsarc = ocm->dsarc; isacntl = ocm->isacntl; @@ -885,12 +454,11 @@ static void dcr_write_ocm (void *opaque, int dcrn, uint32_t val) ocm->dsacntl = dsacntl; } -static void ocm_reset (void *opaque) +static void ppc405_ocm_reset(DeviceState *dev) { - ppc405_ocm_t *ocm; + Ppc405OcmState *ocm = PPC405_OCM(dev); uint32_t isarc, dsarc, isacntl, dsacntl; - ocm = opaque; isarc = 0x00000000; isacntl = 0x00000000; dsarc = 0x00000000; @@ -902,57 +470,47 @@ static void ocm_reset (void *opaque) ocm->dsacntl = dsacntl; } -static void ppc405_ocm_init(CPUPPCState *env) +static void ppc405_ocm_realize(DeviceState *dev, Error **errp) { - ppc405_ocm_t *ocm; + Ppc405OcmState *ocm = PPC405_OCM(dev); + Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); - ocm = g_new0(ppc405_ocm_t, 1); /* XXX: Size is 4096 or 0x04000000 */ - memory_region_init_ram(&ocm->isarc_ram, NULL, "ppc405.ocm", 4 * KiB, + memory_region_init_ram(&ocm->isarc_ram, OBJECT(ocm), "ppc405.ocm", 4 * KiB, &error_fatal); - memory_region_init_alias(&ocm->dsarc_ram, NULL, "ppc405.dsarc", + memory_region_init_alias(&ocm->dsarc_ram, OBJECT(ocm), "ppc405.dsarc", &ocm->isarc_ram, 0, 4 * KiB); - qemu_register_reset(&ocm_reset, ocm); - ppc_dcr_register(env, OCM0_ISARC, - ocm, &dcr_read_ocm, &dcr_write_ocm); - ppc_dcr_register(env, OCM0_ISACNTL, - ocm, &dcr_read_ocm, &dcr_write_ocm); - ppc_dcr_register(env, OCM0_DSARC, - ocm, &dcr_read_ocm, &dcr_write_ocm); - ppc_dcr_register(env, OCM0_DSACNTL, - ocm, &dcr_read_ocm, &dcr_write_ocm); + + ppc4xx_dcr_register(dcr, OCM0_ISARC, ocm, &dcr_read_ocm, &dcr_write_ocm); + ppc4xx_dcr_register(dcr, OCM0_ISACNTL, ocm, &dcr_read_ocm, &dcr_write_ocm); + ppc4xx_dcr_register(dcr, OCM0_DSARC, ocm, &dcr_read_ocm, &dcr_write_ocm); + ppc4xx_dcr_register(dcr, OCM0_DSACNTL, ocm, &dcr_read_ocm, &dcr_write_ocm); +} + +static void ppc405_ocm_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ppc405_ocm_realize; + dc->reset = ppc405_ocm_reset; + /* Reason: only works as function of a ppc4xx SoC */ + dc->user_creatable = false; } /*****************************************************************************/ /* General purpose timers */ -typedef struct ppc4xx_gpt_t ppc4xx_gpt_t; -struct ppc4xx_gpt_t { - MemoryRegion iomem; - int64_t tb_offset; - uint32_t tb_freq; - QEMUTimer *timer; - qemu_irq irqs[5]; - uint32_t oe; - uint32_t ol; - uint32_t im; - uint32_t is; - uint32_t ie; - uint32_t comp[5]; - uint32_t mask[5]; -}; - -static int ppc4xx_gpt_compare (ppc4xx_gpt_t *gpt, int n) +static int ppc4xx_gpt_compare(Ppc405GptState *gpt, int n) { /* XXX: TODO */ return 0; } -static void ppc4xx_gpt_set_output (ppc4xx_gpt_t *gpt, int n, int level) +static void ppc4xx_gpt_set_output(Ppc405GptState *gpt, int n, int level) { /* XXX: TODO */ } -static void ppc4xx_gpt_set_outputs (ppc4xx_gpt_t *gpt) +static void ppc4xx_gpt_set_outputs(Ppc405GptState *gpt) { uint32_t mask; int i; @@ -973,29 +531,30 @@ static void ppc4xx_gpt_set_outputs (ppc4xx_gpt_t *gpt) } } -static void ppc4xx_gpt_set_irqs (ppc4xx_gpt_t *gpt) +static void ppc4xx_gpt_set_irqs(Ppc405GptState *gpt) { uint32_t mask; int i; mask = 0x00008000; for (i = 0; i < 5; i++) { - if (gpt->is & gpt->im & mask) + if (gpt->is & gpt->im & mask) { qemu_irq_raise(gpt->irqs[i]); - else + } else { qemu_irq_lower(gpt->irqs[i]); + } mask = mask >> 1; } } -static void ppc4xx_gpt_compute_timer (ppc4xx_gpt_t *gpt) +static void ppc4xx_gpt_compute_timer(Ppc405GptState *gpt) { /* XXX: TODO */ } static uint64_t ppc4xx_gpt_read(void *opaque, hwaddr addr, unsigned size) { - ppc4xx_gpt_t *gpt = opaque; + Ppc405GptState *gpt = opaque; uint32_t ret; int idx; @@ -1049,7 +608,7 @@ static uint64_t ppc4xx_gpt_read(void *opaque, hwaddr addr, unsigned size) static void ppc4xx_gpt_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - ppc4xx_gpt_t *gpt = opaque; + Ppc405GptState *gpt = opaque; int idx; trace_ppc4xx_gpt_write(addr, size, value); @@ -1113,22 +672,20 @@ static const MemoryRegionOps gpt_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void ppc4xx_gpt_cb (void *opaque) +static void ppc4xx_gpt_cb(void *opaque) { - ppc4xx_gpt_t *gpt; + Ppc405GptState *gpt = opaque; - gpt = opaque; ppc4xx_gpt_set_irqs(gpt); ppc4xx_gpt_set_outputs(gpt); ppc4xx_gpt_compute_timer(gpt); } -static void ppc4xx_gpt_reset (void *opaque) +static void ppc405_gpt_reset(DeviceState *dev) { - ppc4xx_gpt_t *gpt; + Ppc405GptState *gpt = PPC405_GPT(dev); int i; - gpt = opaque; timer_del(gpt->timer); gpt->oe = 0x00000000; gpt->ol = 0x00000000; @@ -1141,21 +698,37 @@ static void ppc4xx_gpt_reset (void *opaque) } } -static void ppc4xx_gpt_init(hwaddr base, qemu_irq irqs[5]) +static void ppc405_gpt_realize(DeviceState *dev, Error **errp) { - ppc4xx_gpt_t *gpt; + Ppc405GptState *s = PPC405_GPT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); int i; - trace_ppc4xx_gpt_init(base); + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &ppc4xx_gpt_cb, s); + memory_region_init_io(&s->iomem, OBJECT(s), &gpt_ops, s, "gpt", 0xd4); + sysbus_init_mmio(sbd, &s->iomem); - gpt = g_new0(ppc4xx_gpt_t, 1); - for (i = 0; i < 5; i++) { - gpt->irqs[i] = irqs[i]; + for (i = 0; i < ARRAY_SIZE(s->irqs); i++) { + sysbus_init_irq(sbd, &s->irqs[i]); } - gpt->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &ppc4xx_gpt_cb, gpt); - memory_region_init_io(&gpt->iomem, NULL, &gpt_ops, gpt, "gpt", 0x0d4); - memory_region_add_subregion(get_system_memory(), base, &gpt->iomem); - qemu_register_reset(ppc4xx_gpt_reset, gpt); +} + +static void ppc405_gpt_finalize(Object *obj) +{ + /* timer will be NULL if the GPT wasn't realized */ + if (PPC405_GPT(obj)->timer) { + timer_del(PPC405_GPT(obj)->timer); + } +} + +static void ppc405_gpt_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ppc405_gpt_realize; + dc->reset = ppc405_gpt_reset; + /* Reason: only works as function of a ppc4xx SoC */ + dc->user_creatable = false; } /*****************************************************************************/ @@ -1177,36 +750,7 @@ enum { #endif }; -enum { - PPC405EP_CPU_CLK = 0, - PPC405EP_PLB_CLK = 1, - PPC405EP_OPB_CLK = 2, - PPC405EP_EBC_CLK = 3, - PPC405EP_MAL_CLK = 4, - PPC405EP_PCI_CLK = 5, - PPC405EP_UART0_CLK = 6, - PPC405EP_UART1_CLK = 7, - PPC405EP_CLK_NB = 8, -}; - -typedef struct ppc405ep_cpc_t ppc405ep_cpc_t; -struct ppc405ep_cpc_t { - uint32_t sysclk; - clk_setup_t clk_setup[PPC405EP_CLK_NB]; - uint32_t boot; - uint32_t epctl; - uint32_t pllmr[2]; - uint32_t ucr; - uint32_t srr; - uint32_t jtagid; - uint32_t pci; - /* Clock and power management */ - uint32_t er; - uint32_t fr; - uint32_t sr; -}; - -static void ppc405ep_compute_clocks (ppc405ep_cpc_t *cpc) +static void ppc405ep_compute_clocks(Ppc405CpcState *cpc) { uint32_t CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk; uint32_t UART0_clk, UART1_clk; @@ -1299,12 +843,11 @@ static void ppc405ep_compute_clocks (ppc405ep_cpc_t *cpc) clk_setup(&cpc->clk_setup[PPC405EP_UART1_CLK], UART1_clk); } -static uint32_t dcr_read_epcpc (void *opaque, int dcrn) +static uint32_t dcr_read_epcpc(void *opaque, int dcrn) { - ppc405ep_cpc_t *cpc; + Ppc405CpcState *cpc = opaque; uint32_t ret; - cpc = opaque; switch (dcrn) { case PPC405EP_CPC0_BOOT: ret = cpc->boot; @@ -1339,11 +882,10 @@ static uint32_t dcr_read_epcpc (void *opaque, int dcrn) return ret; } -static void dcr_write_epcpc (void *opaque, int dcrn, uint32_t val) +static void dcr_write_epcpc(void *opaque, int dcrn, uint32_t val) { - ppc405ep_cpc_t *cpc; + Ppc405CpcState *cpc = opaque; - cpc = opaque; switch (dcrn) { case PPC405EP_CPC0_BOOT: /* Read-only register */ @@ -1376,9 +918,9 @@ static void dcr_write_epcpc (void *opaque, int dcrn, uint32_t val) } } -static void ppc405ep_cpc_reset (void *opaque) +static void ppc405_cpc_reset(DeviceState *dev) { - ppc405ep_cpc_t *cpc = opaque; + Ppc405CpcState *cpc = PPC405_CPC(dev); cpc->boot = 0x00000010; /* Boot from PCI - IIC EEPROM disabled */ cpc->epctl = 0x00000000; @@ -1390,143 +932,286 @@ static void ppc405ep_cpc_reset (void *opaque) cpc->er = 0x00000000; cpc->fr = 0x00000000; cpc->sr = 0x00000000; + cpc->jtagid = 0x20267049; ppc405ep_compute_clocks(cpc); } /* XXX: sysclk should be between 25 and 100 MHz */ -static void ppc405ep_cpc_init (CPUPPCState *env, clk_setup_t clk_setup[8], - uint32_t sysclk) +static void ppc405_cpc_realize(DeviceState *dev, Error **errp) { - ppc405ep_cpc_t *cpc; + Ppc405CpcState *cpc = PPC405_CPC(dev); + Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); - cpc = g_new0(ppc405ep_cpc_t, 1); - memcpy(cpc->clk_setup, clk_setup, - PPC405EP_CLK_NB * sizeof(clk_setup_t)); - cpc->jtagid = 0x20267049; - cpc->sysclk = sysclk; - qemu_register_reset(&ppc405ep_cpc_reset, cpc); - ppc_dcr_register(env, PPC405EP_CPC0_BOOT, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_EPCTL, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_PLLMR0, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_PLLMR1, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_UCR, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_SRR, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_JTAGID, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_PCI, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); -#if 0 - ppc_dcr_register(env, PPC405EP_CPC0_ER, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_FR, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_SR, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); -#endif + assert(dcr->cpu); + cpc->clk_setup[PPC405EP_CPU_CLK].cb = + ppc_40x_timers_init(&dcr->cpu->env, cpc->sysclk, PPC_INTERRUPT_PIT); + cpc->clk_setup[PPC405EP_CPU_CLK].opaque = &dcr->cpu->env; + + ppc4xx_dcr_register(dcr, PPC405EP_CPC0_BOOT, cpc, + &dcr_read_epcpc, &dcr_write_epcpc); + ppc4xx_dcr_register(dcr, PPC405EP_CPC0_EPCTL, cpc, + &dcr_read_epcpc, &dcr_write_epcpc); + ppc4xx_dcr_register(dcr, PPC405EP_CPC0_PLLMR0, cpc, + &dcr_read_epcpc, &dcr_write_epcpc); + ppc4xx_dcr_register(dcr, PPC405EP_CPC0_PLLMR1, cpc, + &dcr_read_epcpc, &dcr_write_epcpc); + ppc4xx_dcr_register(dcr, PPC405EP_CPC0_UCR, cpc, + &dcr_read_epcpc, &dcr_write_epcpc); + ppc4xx_dcr_register(dcr, PPC405EP_CPC0_SRR, cpc, + &dcr_read_epcpc, &dcr_write_epcpc); + ppc4xx_dcr_register(dcr, PPC405EP_CPC0_JTAGID, cpc, + &dcr_read_epcpc, &dcr_write_epcpc); + ppc4xx_dcr_register(dcr, PPC405EP_CPC0_PCI, cpc, + &dcr_read_epcpc, &dcr_write_epcpc); } -PowerPCCPU *ppc405ep_init(MemoryRegion *address_space_mem, - MemoryRegion ram_memories[2], - hwaddr ram_bases[2], - hwaddr ram_sizes[2], - uint32_t sysclk, DeviceState **uicdevp, - int do_init) +static Property ppc405_cpc_properties[] = { + DEFINE_PROP_UINT32("sys-clk", Ppc405CpcState, sysclk, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ppc405_cpc_class_init(ObjectClass *oc, void *data) { - clk_setup_t clk_setup[PPC405EP_CLK_NB], tlb_clk_setup; - qemu_irq dma_irqs[4], gpt_irqs[5], mal_irqs[4]; - PowerPCCPU *cpu; + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ppc405_cpc_realize; + dc->reset = ppc405_cpc_reset; + /* Reason: only works as function of a ppc4xx SoC */ + dc->user_creatable = false; + device_class_set_props(dc, ppc405_cpc_properties); +} + +/* PPC405_SOC */ + +static void ppc405_soc_instance_init(Object *obj) +{ + Ppc405SoCState *s = PPC405_SOC(obj); + + object_initialize_child(obj, "cpu", &s->cpu, + POWERPC_CPU_TYPE_NAME("405ep")); + + object_initialize_child(obj, "uic", &s->uic, TYPE_PPC_UIC); + + object_initialize_child(obj, "cpc", &s->cpc, TYPE_PPC405_CPC); + object_property_add_alias(obj, "sys-clk", OBJECT(&s->cpc), "sys-clk"); + + object_initialize_child(obj, "gpt", &s->gpt, TYPE_PPC405_GPT); + + object_initialize_child(obj, "ocm", &s->ocm, TYPE_PPC405_OCM); + + object_initialize_child(obj, "gpio", &s->gpio, TYPE_PPC405_GPIO); + + object_initialize_child(obj, "dma", &s->dma, TYPE_PPC405_DMA); + + object_initialize_child(obj, "i2c", &s->i2c, TYPE_PPC4xx_I2C); + + object_initialize_child(obj, "ebc", &s->ebc, TYPE_PPC4xx_EBC); + + object_initialize_child(obj, "opba", &s->opba, TYPE_PPC405_OPBA); + + object_initialize_child(obj, "pob", &s->pob, TYPE_PPC405_POB); + + object_initialize_child(obj, "plb", &s->plb, TYPE_PPC4xx_PLB); + + object_initialize_child(obj, "mal", &s->mal, TYPE_PPC4xx_MAL); + + object_initialize_child(obj, "sdram", &s->sdram, TYPE_PPC4xx_SDRAM_DDR); + object_property_add_alias(obj, "dram", OBJECT(&s->sdram), "dram"); +} + +static void ppc405_reset(void *opaque) +{ + cpu_reset(CPU(opaque)); +} + +static void ppc405_soc_realize(DeviceState *dev, Error **errp) +{ + Ppc405SoCState *s = PPC405_SOC(dev); CPUPPCState *env; - DeviceState *uicdev; - SysBusDevice *uicsbd; + SysBusDevice *sbd; + int i; - memset(clk_setup, 0, sizeof(clk_setup)); /* init CPUs */ - cpu = ppc4xx_init(POWERPC_CPU_TYPE_NAME("405ep"), - &clk_setup[PPC405EP_CPU_CLK], - &tlb_clk_setup, sysclk); - env = &cpu->env; - clk_setup[PPC405EP_CPU_CLK].cb = tlb_clk_setup.cb; - clk_setup[PPC405EP_CPU_CLK].opaque = tlb_clk_setup.opaque; - /* Internal devices init */ - /* Memory mapped devices registers */ + if (!qdev_realize(DEVICE(&s->cpu), NULL, errp)) { + return; + } + qemu_register_reset(ppc405_reset, &s->cpu); + + env = &s->cpu.env; + + ppc_dcr_init(env, NULL, NULL); + + /* CPU control */ + if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->cpc), &s->cpu, errp)) { + return; + } + /* PLB arbitrer */ - ppc4xx_plb_init(env); + if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->plb), &s->cpu, errp)) { + return; + } + /* PLB to OPB bridge */ - ppc4xx_pob_init(env); + if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->pob), &s->cpu, errp)) { + return; + } + /* OBP arbitrer */ - ppc4xx_opba_init(0xef600600); + sbd = SYS_BUS_DEVICE(&s->opba); + if (!sysbus_realize(sbd, errp)) { + return; + } + sysbus_mmio_map(sbd, 0, 0xef600600); + /* Universal interrupt controller */ - uicdev = qdev_new(TYPE_PPC_UIC); - uicsbd = SYS_BUS_DEVICE(uicdev); - - object_property_set_link(OBJECT(uicdev), "cpu", OBJECT(cpu), - &error_fatal); - sysbus_realize_and_unref(uicsbd, &error_fatal); - - sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]); - sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]); - - *uicdevp = uicdev; + if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->uic), &s->cpu, errp)) { + return; + } + sbd = SYS_BUS_DEVICE(&s->uic); + sysbus_connect_irq(sbd, PPCUIC_OUTPUT_INT, + qdev_get_gpio_in(DEVICE(&s->cpu), PPC40x_INPUT_INT)); + sysbus_connect_irq(sbd, PPCUIC_OUTPUT_CINT, + qdev_get_gpio_in(DEVICE(&s->cpu), PPC40x_INPUT_CINT)); /* SDRAM controller */ - /* XXX 405EP has no ECC interrupt */ - ppc4xx_sdram_init(env, qdev_get_gpio_in(uicdev, 17), 2, ram_memories, - ram_bases, ram_sizes, do_init); + /* + * We use the 440 DDR SDRAM controller which has more regs and features + * but it's compatible enough for now + */ + object_property_set_int(OBJECT(&s->sdram), "nbanks", 2, &error_abort); + if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->sdram), &s->cpu, errp)) { + return; + } + /* XXX 405EP has no ECC interrupt */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdram), 0, + qdev_get_gpio_in(DEVICE(&s->uic), 17)); + /* External bus controller */ - ppc405_ebc_init(env); + if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->ebc), &s->cpu, errp)) { + return; + } + /* DMA controller */ - dma_irqs[0] = qdev_get_gpio_in(uicdev, 5); - dma_irqs[1] = qdev_get_gpio_in(uicdev, 6); - dma_irqs[2] = qdev_get_gpio_in(uicdev, 7); - dma_irqs[3] = qdev_get_gpio_in(uicdev, 8); - ppc405_dma_init(env, dma_irqs); - /* IIC controller */ - sysbus_create_simple(TYPE_PPC4xx_I2C, 0xef600500, - qdev_get_gpio_in(uicdev, 2)); + if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->dma), &s->cpu, errp)) { + return; + } + sbd = SYS_BUS_DEVICE(&s->dma); + for (i = 0; i < ARRAY_SIZE(s->dma.irqs); i++) { + sysbus_connect_irq(sbd, i, qdev_get_gpio_in(DEVICE(&s->uic), 5 + i)); + } + + /* I2C controller */ + sbd = SYS_BUS_DEVICE(&s->i2c); + if (!sysbus_realize(sbd, errp)) { + return; + } + sysbus_mmio_map(sbd, 0, 0xef600500); + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(DEVICE(&s->uic), 2)); + /* GPIO */ - ppc405_gpio_init(0xef600700); + sbd = SYS_BUS_DEVICE(&s->gpio); + if (!sysbus_realize(sbd, errp)) { + return; + } + sysbus_mmio_map(sbd, 0, 0xef600700); + /* Serial ports */ if (serial_hd(0) != NULL) { - serial_mm_init(address_space_mem, 0xef600300, 0, - qdev_get_gpio_in(uicdev, 0), + serial_mm_init(get_system_memory(), 0xef600300, 0, + qdev_get_gpio_in(DEVICE(&s->uic), 0), PPC_SERIAL_MM_BAUDBASE, serial_hd(0), DEVICE_BIG_ENDIAN); } if (serial_hd(1) != NULL) { - serial_mm_init(address_space_mem, 0xef600400, 0, - qdev_get_gpio_in(uicdev, 1), + serial_mm_init(get_system_memory(), 0xef600400, 0, + qdev_get_gpio_in(DEVICE(&s->uic), 1), PPC_SERIAL_MM_BAUDBASE, serial_hd(1), DEVICE_BIG_ENDIAN); } + /* OCM */ - ppc405_ocm_init(env); + if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->ocm), &s->cpu, errp)) { + return; + } + /* GPT */ - gpt_irqs[0] = qdev_get_gpio_in(uicdev, 19); - gpt_irqs[1] = qdev_get_gpio_in(uicdev, 20); - gpt_irqs[2] = qdev_get_gpio_in(uicdev, 21); - gpt_irqs[3] = qdev_get_gpio_in(uicdev, 22); - gpt_irqs[4] = qdev_get_gpio_in(uicdev, 23); - ppc4xx_gpt_init(0xef600000, gpt_irqs); - /* PCI */ - /* Uses UIC IRQs 3, 16, 18 */ + sbd = SYS_BUS_DEVICE(&s->gpt); + if (!sysbus_realize(sbd, errp)) { + return; + } + sysbus_mmio_map(sbd, 0, 0xef600000); + for (i = 0; i < ARRAY_SIZE(s->gpt.irqs); i++) { + sysbus_connect_irq(sbd, i, qdev_get_gpio_in(DEVICE(&s->uic), 19 + i)); + } + /* MAL */ - mal_irqs[0] = qdev_get_gpio_in(uicdev, 11); - mal_irqs[1] = qdev_get_gpio_in(uicdev, 12); - mal_irqs[2] = qdev_get_gpio_in(uicdev, 13); - mal_irqs[3] = qdev_get_gpio_in(uicdev, 14); - ppc4xx_mal_init(env, 4, 2, mal_irqs); + object_property_set_int(OBJECT(&s->mal), "txc-num", 4, &error_abort); + object_property_set_int(OBJECT(&s->mal), "rxc-num", 2, &error_abort); + if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->mal), &s->cpu, errp)) { + return; + } + sbd = SYS_BUS_DEVICE(&s->mal); + for (i = 0; i < ARRAY_SIZE(s->mal.irqs); i++) { + sysbus_connect_irq(sbd, i, qdev_get_gpio_in(DEVICE(&s->uic), 11 + i)); + } + /* Ethernet */ /* Uses UIC IRQs 9, 15, 17 */ - /* CPU control */ - ppc405ep_cpc_init(env, clk_setup, sysclk); - - return cpu; } + +static void ppc405_soc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ppc405_soc_realize; + /* Reason: only works as part of a ppc405 board/machine */ + dc->user_creatable = false; +} + +static const TypeInfo ppc405_types[] = { + { + .name = TYPE_PPC405_POB, + .parent = TYPE_PPC4xx_DCR_DEVICE, + .instance_size = sizeof(Ppc405PobState), + .class_init = ppc405_pob_class_init, + }, { + .name = TYPE_PPC405_OPBA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Ppc405OpbaState), + .class_init = ppc405_opba_class_init, + }, { + .name = TYPE_PPC405_DMA, + .parent = TYPE_PPC4xx_DCR_DEVICE, + .instance_size = sizeof(Ppc405DmaState), + .class_init = ppc405_dma_class_init, + }, { + .name = TYPE_PPC405_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Ppc405GpioState), + .class_init = ppc405_gpio_class_init, + }, { + .name = TYPE_PPC405_OCM, + .parent = TYPE_PPC4xx_DCR_DEVICE, + .instance_size = sizeof(Ppc405OcmState), + .class_init = ppc405_ocm_class_init, + }, { + .name = TYPE_PPC405_GPT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Ppc405GptState), + .instance_finalize = ppc405_gpt_finalize, + .class_init = ppc405_gpt_class_init, + }, { + .name = TYPE_PPC405_CPC, + .parent = TYPE_PPC4xx_DCR_DEVICE, + .instance_size = sizeof(Ppc405CpcState), + .class_init = ppc405_cpc_class_init, + }, { + .name = TYPE_PPC405_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(Ppc405SoCState), + .instance_init = ppc405_soc_instance_init, + .class_init = ppc405_soc_class_init, + } +}; + +DEFINE_TYPES(ppc405_types) diff --git a/hw/ppc/ppc440.h b/hw/ppc/ppc440.h index 7cef936125..909373fb38 100644 --- a/hw/ppc/ppc440.h +++ b/hw/ppc/ppc440.h @@ -16,12 +16,7 @@ void ppc4xx_l2sram_init(CPUPPCState *env); void ppc4xx_cpr_init(CPUPPCState *env); void ppc4xx_sdr_init(CPUPPCState *env); -void ppc440_sdram_init(CPUPPCState *env, int nbanks, - MemoryRegion *ram_memories, - hwaddr *ram_bases, hwaddr *ram_sizes, - int do_init); void ppc4xx_ahb_init(CPUPPCState *env); void ppc4xx_dma_init(CPUPPCState *env, int dcr_base); -void ppc460ex_pcie_init(CPUPPCState *env); #endif /* PPC440_H */ diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index d5973f2484..e18f57efce 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -13,20 +13,18 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "qemu/error-report.h" #include "qemu/datadir.h" #include "qemu/error-report.h" #include "net/net.h" #include "hw/pci/pci.h" #include "hw/boards.h" #include "sysemu/kvm.h" -#include "kvm_ppc.h" #include "sysemu/device_tree.h" #include "hw/loader.h" #include "elf.h" #include "hw/char/serial.h" #include "hw/ppc/ppc.h" -#include "ppc405.h" +#include "hw/pci-host/ppc4xx.h" #include "sysemu/sysemu.h" #include "sysemu/reset.h" #include "hw/sysbus.h" @@ -34,6 +32,8 @@ #include "hw/qdev-properties.h" #include "qapi/error.h" +#include + #define BINARY_DEVICE_TREE_FILE "bamboo.dtb" /* from u-boot */ @@ -48,22 +48,15 @@ #define PPC440EP_PCI_IO 0xe8000000 #define PPC440EP_PCI_IOLEN 0x00010000 -#define PPC440EP_SDRAM_NR_BANKS 4 - -static const ram_addr_t ppc440ep_sdram_bank_sizes[] = { - 256 * MiB, 128 * MiB, 64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 0 -}; - static hwaddr entry; -static int bamboo_load_device_tree(hwaddr addr, - uint32_t ramsize, - hwaddr initrd_base, - hwaddr initrd_size, - const char *kernel_cmdline) +static int bamboo_load_device_tree(MachineState *machine, + hwaddr addr, + hwaddr initrd_base, + hwaddr initrd_size) { int ret = -1; - uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(ramsize) }; + uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(machine->ram_size) }; char *filename; int fdt_size; void *fdt; @@ -84,30 +77,23 @@ static int bamboo_load_device_tree(hwaddr addr, ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, sizeof(mem_reg_property)); - if (ret < 0) + if (ret < 0) { fprintf(stderr, "couldn't set /memory/reg\n"); - + } ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", initrd_base); - if (ret < 0) + if (ret < 0) { fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); - + } ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", (initrd_base + initrd_size)); - if (ret < 0) + if (ret < 0) { fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); - + } ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", - kernel_cmdline); - if (ret < 0) + machine->kernel_cmdline); + if (ret < 0) { fprintf(stderr, "couldn't set /chosen/bootargs\n"); - - /* Copy data from the host device tree into the guest. Since the guest can - * directly access the timebase without host involvement, we must expose - * the correct frequencies. */ - if (kvm_enabled()) { - tb_freq = kvmppc_get_tbfreq(); - clock_freq = kvmppc_get_clockfreq(); } qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency", @@ -116,7 +102,10 @@ static int bamboo_load_device_tree(hwaddr addr, tb_freq); rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); - g_free(fdt); + + /* Set ms->fdt for 'dumpdtb' QMP/HMP command */ + machine->fdt = fdt; + return 0; } @@ -160,14 +149,11 @@ static void main_cpu_reset(void *opaque) static void bamboo_init(MachineState *machine) { const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; + MachineClass *mc = MACHINE_GET_CLASS(machine); unsigned int pci_irq_nrs[4] = { 28, 27, 26, 25 }; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *isa = g_new(MemoryRegion, 1); - MemoryRegion *ram_memories = g_new(MemoryRegion, PPC440EP_SDRAM_NR_BANKS); - hwaddr ram_bases[PPC440EP_SDRAM_NR_BANKS]; - hwaddr ram_sizes[PPC440EP_SDRAM_NR_BANKS]; PCIBus *pcibus; PowerPCCPU *cpu; CPUPPCState *env; @@ -176,7 +162,12 @@ static void bamboo_init(MachineState *machine) DeviceState *uicdev; SysBusDevice *uicsbd; int success; - int i; + + if (kvm_enabled()) { + error_report("machine %s does not support the KVM accelerator", + MACHINE_GET_CLASS(machine)->name); + exit(EXIT_FAILURE); + } cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); env = &cpu->env; @@ -193,31 +184,27 @@ static void bamboo_init(MachineState *machine) /* interrupt controller */ uicdev = qdev_new(TYPE_PPC_UIC); + ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(uicdev), cpu, &error_fatal); + object_unref(OBJECT(uicdev)); uicsbd = SYS_BUS_DEVICE(uicdev); - - object_property_set_link(OBJECT(uicdev), "cpu", OBJECT(cpu), - &error_fatal); - sysbus_realize_and_unref(uicsbd, &error_fatal); - sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]); + qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT)); sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]); + qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_CINT)); /* SDRAM controller */ - memset(ram_bases, 0, sizeof(ram_bases)); - memset(ram_sizes, 0, sizeof(ram_sizes)); - ppc4xx_sdram_banks(machine->ram, PPC440EP_SDRAM_NR_BANKS, ram_memories, - ram_bases, ram_sizes, ppc440ep_sdram_bank_sizes); + dev = qdev_new(TYPE_PPC4xx_SDRAM_DDR); + object_property_set_link(OBJECT(dev), "dram", OBJECT(machine->ram), + &error_abort); + ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(dev), cpu, &error_fatal); + object_unref(OBJECT(dev)); /* XXX 440EP's ECC interrupts are on UIC1, but we've only created UIC0. */ - ppc4xx_sdram_init(env, - qdev_get_gpio_in(uicdev, 14), - PPC440EP_SDRAM_NR_BANKS, ram_memories, - ram_bases, ram_sizes, 1); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(uicdev, 14)); + /* Enable SDRAM memory regions, this should be done by the firmware */ + ppc4xx_sdram_ddr_enable(PPC4xx_SDRAM_DDR(dev)); /* PCI */ - dev = sysbus_create_varargs(TYPE_PPC4xx_PCI_HOST_BRIDGE, - PPC440EP_PCI_CONFIG, + dev = sysbus_create_varargs(TYPE_PPC4xx_PCI_HOST, PPC440EP_PCI_CONFIG, qdev_get_gpio_in(uicdev, pci_irq_nrs[0]), qdev_get_gpio_in(uicdev, pci_irq_nrs[1]), qdev_get_gpio_in(uicdev, pci_irq_nrs[2]), @@ -247,12 +234,11 @@ static void bamboo_init(MachineState *machine) } if (pcibus) { - /* Register network interfaces. */ - for (i = 0; i < nb_nics; i++) { - /* There are no PCI NICs on the Bamboo board, but there are - * PCI slots, so we can pick whatever default model we want. */ - pci_nic_init_nofail(&nd_table[i], pcibus, "e1000", NULL); - } + /* + * There are no PCI NICs on the Bamboo board, but there are + * PCI slots, so we can pick whatever default model we want. + */ + pci_init_nic_devices(pcibus, mc->default_nic); } /* Load kernel. */ @@ -287,8 +273,8 @@ 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, machine->ram_size, RAMDISK_ADDR, - initrd_size, kernel_cmdline) < 0) { + if (bamboo_load_device_tree(machine, FDT_ADDR, + RAMDISK_ADDR, initrd_size) < 0) { error_report("couldn't load device tree"); exit(1); } @@ -301,6 +287,7 @@ static void bamboo_machine_init(MachineClass *mc) mc->init = bamboo_init; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("440epb"); mc->default_ram_id = "ppc4xx.sdram"; + mc->default_nic = "e1000"; } DEFINE_MACHINE("bamboo", bamboo_machine_init) diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 993e3ba955..1312aa2080 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -10,19 +10,16 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "qemu/error-report.h" #include "qapi/error.h" #include "qemu/log.h" -#include "qemu/module.h" #include "hw/irq.h" -#include "exec/memory.h" -#include "hw/ppc/ppc.h" +#include "hw/ppc/ppc4xx.h" +#include "hw/pci-host/ppc4xx.h" #include "hw/qdev-properties.h" #include "hw/pci/pci.h" -#include "sysemu/block-backend.h" #include "sysemu/reset.h" +#include "cpu.h" #include "ppc440.h" -#include "qom/object.h" /*****************************************************************************/ /* L2 Cache as SRAM */ @@ -77,46 +74,6 @@ typedef struct ppc4xx_l2sram_t { uint32_t isram0[11]; } ppc4xx_l2sram_t; -#ifdef MAP_L2SRAM -static void l2sram_update_mappings(ppc4xx_l2sram_t *l2sram, - uint32_t isarc, uint32_t isacntl, - uint32_t dsarc, uint32_t dsacntl) -{ - if (l2sram->isarc != isarc || - (l2sram->isacntl & 0x80000000) != (isacntl & 0x80000000)) { - if (l2sram->isacntl & 0x80000000) { - /* Unmap previously assigned memory region */ - memory_region_del_subregion(get_system_memory(), - &l2sram->isarc_ram); - } - if (isacntl & 0x80000000) { - /* Map new instruction memory region */ - memory_region_add_subregion(get_system_memory(), isarc, - &l2sram->isarc_ram); - } - } - if (l2sram->dsarc != dsarc || - (l2sram->dsacntl & 0x80000000) != (dsacntl & 0x80000000)) { - if (l2sram->dsacntl & 0x80000000) { - /* Beware not to unmap the region we just mapped */ - if (!(isacntl & 0x80000000) || l2sram->dsarc != isarc) { - /* Unmap previously assigned memory region */ - memory_region_del_subregion(get_system_memory(), - &l2sram->dsarc_ram); - } - } - if (dsacntl & 0x80000000) { - /* Beware not to remap the region we just mapped */ - if (!(isacntl & 0x80000000) || dsarc != isarc) { - /* Map new data memory region */ - memory_region_add_subregion(get_system_memory(), dsarc, - &l2sram->dsarc_ram); - } - } - } -} -#endif - static uint32_t dcr_read_l2sram(void *opaque, int dcrn) { ppc4xx_l2sram_t *l2sram = opaque; @@ -197,7 +154,6 @@ static void dcr_write_l2sram(void *opaque, int dcrn, uint32_t val) /*l2sram->isram1[dcrn - DCR_L2CACHE_BASE] = val;*/ break; } - /*l2sram_update_mappings(l2sram, isarc, isacntl, dsarc, dsacntl);*/ } static void l2sram_reset(void *opaque) @@ -207,7 +163,6 @@ static void l2sram_reset(void *opaque) memset(l2sram->l2cache, 0, sizeof(l2sram->l2cache)); l2sram->l2cache[DCR_L2CACHE_STAT - DCR_L2CACHE_BASE] = 0x80000000; memset(l2sram->isram0, 0, sizeof(l2sram->isram0)); - /*l2sram_update_mappings(l2sram, isarc, isacntl, dsarc, dsacntl);*/ } void ppc4xx_l2sram_init(CPUPPCState *env) @@ -378,10 +333,6 @@ enum { PESDR1_RSTSTA = 0x365, }; -#define SDR0_DDR0_DDRM_ENCODE(n) ((((unsigned long)(n)) & 0x03) << 29) -#define SDR0_DDR0_DDRM_DDR1 0x20000000 -#define SDR0_DDR0_DDRM_DDR2 0x40000000 - static uint32_t dcr_read_sdr(void *opaque, int dcrn) { ppc4xx_sdr_t *sdr = opaque; @@ -480,262 +431,6 @@ void ppc4xx_sdr_init(CPUPPCState *env) sdr, &dcr_read_sdr, &dcr_write_sdr); } -/*****************************************************************************/ -/* SDRAM controller */ -typedef struct ppc440_sdram_t { - uint32_t addr; - int nbanks; - MemoryRegion containers[4]; /* used for clipping */ - MemoryRegion *ram_memories; - hwaddr ram_bases[4]; - hwaddr ram_sizes[4]; - uint32_t bcr[4]; -} ppc440_sdram_t; - -enum { - SDRAM0_CFGADDR = 0x10, - SDRAM0_CFGDATA, - SDRAM_R0BAS = 0x40, - SDRAM_R1BAS, - SDRAM_R2BAS, - SDRAM_R3BAS, - SDRAM_CONF1HB = 0x45, - SDRAM_PLBADDULL = 0x4a, - SDRAM_CONF1LL = 0x4b, - SDRAM_CONFPATHB = 0x4f, - SDRAM_PLBADDUHB = 0x50, -}; - -static uint32_t sdram_bcr(hwaddr ram_base, hwaddr ram_size) -{ - uint32_t bcr; - - switch (ram_size) { - case (8 * MiB): - bcr = 0xffc0; - break; - case (16 * MiB): - bcr = 0xff80; - break; - case (32 * MiB): - bcr = 0xff00; - break; - case (64 * MiB): - bcr = 0xfe00; - break; - case (128 * MiB): - bcr = 0xfc00; - break; - case (256 * MiB): - bcr = 0xf800; - break; - case (512 * MiB): - bcr = 0xf000; - break; - case (1 * GiB): - bcr = 0xe000; - break; - case (2 * GiB): - bcr = 0xc000; - break; - case (4 * GiB): - bcr = 0x8000; - break; - default: - error_report("invalid RAM size " TARGET_FMT_plx, ram_size); - return 0; - } - bcr |= ram_base >> 2 & 0xffe00000; - bcr |= 1; - - return bcr; -} - -static inline hwaddr sdram_base(uint32_t bcr) -{ - return (bcr & 0xffe00000) << 2; -} - -static uint64_t sdram_size(uint32_t bcr) -{ - uint64_t size; - int sh; - - sh = 1024 - ((bcr >> 6) & 0x3ff); - size = 8 * MiB * sh; - - return size; -} - -static void sdram_set_bcr(ppc440_sdram_t *sdram, int i, - uint32_t bcr, int enabled) -{ - if (sdram->bcr[i] & 1) { - /* First unmap RAM if enabled */ - memory_region_del_subregion(get_system_memory(), - &sdram->containers[i]); - memory_region_del_subregion(&sdram->containers[i], - &sdram->ram_memories[i]); - object_unparent(OBJECT(&sdram->containers[i])); - } - sdram->bcr[i] = bcr & 0xffe0ffc1; - if (enabled && (bcr & 1)) { - memory_region_init(&sdram->containers[i], NULL, "sdram-containers", - sdram_size(bcr)); - memory_region_add_subregion(&sdram->containers[i], 0, - &sdram->ram_memories[i]); - memory_region_add_subregion(get_system_memory(), - sdram_base(bcr), - &sdram->containers[i]); - } -} - -static void sdram_map_bcr(ppc440_sdram_t *sdram) -{ - int i; - - for (i = 0; i < sdram->nbanks; i++) { - if (sdram->ram_sizes[i] != 0) { - sdram_set_bcr(sdram, i, sdram_bcr(sdram->ram_bases[i], - sdram->ram_sizes[i]), 1); - } else { - sdram_set_bcr(sdram, i, 0, 0); - } - } -} - -static uint32_t dcr_read_sdram(void *opaque, int dcrn) -{ - ppc440_sdram_t *sdram = opaque; - uint32_t ret = 0; - - switch (dcrn) { - case SDRAM_R0BAS: - case SDRAM_R1BAS: - case SDRAM_R2BAS: - case SDRAM_R3BAS: - if (sdram->ram_sizes[dcrn - SDRAM_R0BAS]) { - ret = sdram_bcr(sdram->ram_bases[dcrn - SDRAM_R0BAS], - sdram->ram_sizes[dcrn - SDRAM_R0BAS]); - } - break; - case SDRAM_CONF1HB: - case SDRAM_CONF1LL: - case SDRAM_CONFPATHB: - case SDRAM_PLBADDULL: - case SDRAM_PLBADDUHB: - break; - case SDRAM0_CFGADDR: - ret = sdram->addr; - break; - case SDRAM0_CFGDATA: - switch (sdram->addr) { - case 0x14: /* SDRAM_MCSTAT (405EX) */ - case 0x1F: - ret = 0x80000000; - break; - case 0x21: /* SDRAM_MCOPT2 */ - ret = 0x08000000; - break; - case 0x40: /* SDRAM_MB0CF */ - ret = 0x00008001; - break; - case 0x7A: /* SDRAM_DLCR */ - ret = 0x02000000; - break; - case 0xE1: /* SDR0_DDR0 */ - ret = SDR0_DDR0_DDRM_ENCODE(1) | SDR0_DDR0_DDRM_DDR1; - break; - default: - break; - } - break; - default: - break; - } - - return ret; -} - -static void dcr_write_sdram(void *opaque, int dcrn, uint32_t val) -{ - ppc440_sdram_t *sdram = opaque; - - switch (dcrn) { - case SDRAM_R0BAS: - case SDRAM_R1BAS: - case SDRAM_R2BAS: - case SDRAM_R3BAS: - case SDRAM_CONF1HB: - case SDRAM_CONF1LL: - case SDRAM_CONFPATHB: - case SDRAM_PLBADDULL: - case SDRAM_PLBADDUHB: - break; - case SDRAM0_CFGADDR: - sdram->addr = val; - break; - case SDRAM0_CFGDATA: - switch (sdram->addr) { - case 0x00: /* B0CR */ - break; - default: - break; - } - break; - default: - break; - } -} - -static void sdram_reset(void *opaque) -{ - ppc440_sdram_t *sdram = opaque; - - sdram->addr = 0; -} - -void ppc440_sdram_init(CPUPPCState *env, int nbanks, - MemoryRegion *ram_memories, - hwaddr *ram_bases, hwaddr *ram_sizes, - int do_init) -{ - ppc440_sdram_t *sdram; - - sdram = g_malloc0(sizeof(*sdram)); - sdram->nbanks = nbanks; - sdram->ram_memories = ram_memories; - memcpy(sdram->ram_bases, ram_bases, nbanks * sizeof(hwaddr)); - memcpy(sdram->ram_sizes, ram_sizes, nbanks * sizeof(hwaddr)); - qemu_register_reset(&sdram_reset, sdram); - ppc_dcr_register(env, SDRAM0_CFGADDR, - sdram, &dcr_read_sdram, &dcr_write_sdram); - ppc_dcr_register(env, SDRAM0_CFGDATA, - sdram, &dcr_read_sdram, &dcr_write_sdram); - if (do_init) { - sdram_map_bcr(sdram); - } - - ppc_dcr_register(env, SDRAM_R0BAS, - sdram, &dcr_read_sdram, &dcr_write_sdram); - ppc_dcr_register(env, SDRAM_R1BAS, - sdram, &dcr_read_sdram, &dcr_write_sdram); - ppc_dcr_register(env, SDRAM_R2BAS, - sdram, &dcr_read_sdram, &dcr_write_sdram); - ppc_dcr_register(env, SDRAM_R3BAS, - sdram, &dcr_read_sdram, &dcr_write_sdram); - ppc_dcr_register(env, SDRAM_CONF1HB, - sdram, &dcr_read_sdram, &dcr_write_sdram); - ppc_dcr_register(env, SDRAM_PLBADDULL, - sdram, &dcr_read_sdram, &dcr_write_sdram); - ppc_dcr_register(env, SDRAM_CONF1LL, - sdram, &dcr_read_sdram, &dcr_write_sdram); - ppc_dcr_register(env, SDRAM_CONFPATHB, - sdram, &dcr_read_sdram, &dcr_write_sdram); - ppc_dcr_register(env, SDRAM_PLBADDUHB, - sdram, &dcr_read_sdram, &dcr_write_sdram); -} - /*****************************************************************************/ /* PLB to AHB bridge */ enum { @@ -904,14 +599,17 @@ static void dcr_write_dma(void *opaque, int dcrn, uint32_t val) int width, i, sidx, didx; uint8_t *rptr, *wptr; hwaddr rlen, wlen; + hwaddr xferlen; sidx = didx = 0; width = 1 << ((val & DMA0_CR_PW) >> 25); + xferlen = count * width; + wlen = rlen = xferlen; 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 (rptr && rlen == xferlen && wptr && wlen == xferlen) { if (!(val & DMA0_CR_DEC) && val & DMA0_CR_SAI && val & DMA0_CR_DAI) { /* optimise common case */ @@ -1025,20 +723,23 @@ void ppc4xx_dma_init(CPUPPCState *env, int dcr_base) /*****************************************************************************/ /* PCI Express controller */ -/* FIXME: This is not complete and does not work, only implemented partially +/* + * FIXME: This is not complete and does not work, only implemented partially * to allow firmware and guests to find an empty bus. Cards should use PCI. */ #include "hw/pci/pcie_host.h" -#define TYPE_PPC460EX_PCIE_HOST "ppc460ex-pcie-host" OBJECT_DECLARE_SIMPLE_TYPE(PPC460EXPCIEState, PPC460EX_PCIE_HOST) struct PPC460EXPCIEState { - PCIExpressHost host; + PCIExpressHost parent_obj; + MemoryRegion busmem; MemoryRegion iomem; qemu_irq irq[4]; + int32_t num; int32_t dcrn_base; + PowerPCCPU *cpu; uint64_t cfg_base; uint32_t cfg_mask; @@ -1056,9 +757,6 @@ struct PPC460EXPCIEState { uint32_t cfg; }; -#define DCRN_PCIE0_BASE 0x100 -#define DCRN_PCIE1_BASE 0x120 - enum { PEGPL_CFGBAH = 0x0, PEGPL_CFGBAL, @@ -1087,78 +785,78 @@ enum { static uint32_t dcr_read_pcie(void *opaque, int dcrn) { - PPC460EXPCIEState *state = opaque; + PPC460EXPCIEState *s = opaque; uint32_t ret = 0; - switch (dcrn - state->dcrn_base) { + switch (dcrn - s->dcrn_base) { case PEGPL_CFGBAH: - ret = state->cfg_base >> 32; + ret = s->cfg_base >> 32; break; case PEGPL_CFGBAL: - ret = state->cfg_base; + ret = s->cfg_base; break; case PEGPL_CFGMSK: - ret = state->cfg_mask; + ret = s->cfg_mask; break; case PEGPL_MSGBAH: - ret = state->msg_base >> 32; + ret = s->msg_base >> 32; break; case PEGPL_MSGBAL: - ret = state->msg_base; + ret = s->msg_base; break; case PEGPL_MSGMSK: - ret = state->msg_mask; + ret = s->msg_mask; break; case PEGPL_OMR1BAH: - ret = state->omr1_base >> 32; + ret = s->omr1_base >> 32; break; case PEGPL_OMR1BAL: - ret = state->omr1_base; + ret = s->omr1_base; break; case PEGPL_OMR1MSKH: - ret = state->omr1_mask >> 32; + ret = s->omr1_mask >> 32; break; case PEGPL_OMR1MSKL: - ret = state->omr1_mask; + ret = s->omr1_mask; break; case PEGPL_OMR2BAH: - ret = state->omr2_base >> 32; + ret = s->omr2_base >> 32; break; case PEGPL_OMR2BAL: - ret = state->omr2_base; + ret = s->omr2_base; break; case PEGPL_OMR2MSKH: - ret = state->omr2_mask >> 32; + ret = s->omr2_mask >> 32; break; case PEGPL_OMR2MSKL: - ret = state->omr3_mask; + ret = s->omr3_mask; break; case PEGPL_OMR3BAH: - ret = state->omr3_base >> 32; + ret = s->omr3_base >> 32; break; case PEGPL_OMR3BAL: - ret = state->omr3_base; + ret = s->omr3_base; break; case PEGPL_OMR3MSKH: - ret = state->omr3_mask >> 32; + ret = s->omr3_mask >> 32; break; case PEGPL_OMR3MSKL: - ret = state->omr3_mask; + ret = s->omr3_mask; break; case PEGPL_REGBAH: - ret = state->reg_base >> 32; + ret = s->reg_base >> 32; break; case PEGPL_REGBAL: - ret = state->reg_base; + ret = s->reg_base; break; case PEGPL_REGMSK: - ret = state->reg_mask; + ret = s->reg_mask; break; case PEGPL_SPECIAL: - ret = state->special; + ret = s->special; break; case PEGPL_CFG: - ret = state->cfg; + ret = s->cfg; break; } @@ -1180,6 +878,14 @@ static void dcr_write_pcie(void *opaque, int dcrn, uint32_t val) case PEGPL_CFGMSK: s->cfg_mask = val; size = ~(val & 0xfffffffe) + 1; + /* + * Firmware sets this register to E0000001. Why we are not sure, + * but the current guess is anything above PCIE_MMCFG_SIZE_MAX is + * ignored. + */ + if (size > PCIE_MMCFG_SIZE_MAX) { + size = PCIE_MMCFG_SIZE_MAX; + } pcie_host_mmcfg_update(PCIE_HOST_BRIDGE(s), val & 1, s->cfg_base, size); break; case PEGPL_MSGBAH: @@ -1253,37 +959,72 @@ static void ppc460ex_set_irq(void *opaque, int irq_num, int level) qemu_set_irq(s->irq[irq_num], level); } +#define PPC440_PCIE_DCR(s, dcrn) \ + ppc_dcr_register(&(s)->cpu->env, (s)->dcrn_base + (dcrn), (s), \ + &dcr_read_pcie, &dcr_write_pcie) + + +static void ppc460ex_pcie_register_dcrs(PPC460EXPCIEState *s) +{ + PPC440_PCIE_DCR(s, PEGPL_CFGBAH); + PPC440_PCIE_DCR(s, PEGPL_CFGBAL); + PPC440_PCIE_DCR(s, PEGPL_CFGMSK); + PPC440_PCIE_DCR(s, PEGPL_MSGBAH); + PPC440_PCIE_DCR(s, PEGPL_MSGBAL); + PPC440_PCIE_DCR(s, PEGPL_MSGMSK); + PPC440_PCIE_DCR(s, PEGPL_OMR1BAH); + PPC440_PCIE_DCR(s, PEGPL_OMR1BAL); + PPC440_PCIE_DCR(s, PEGPL_OMR1MSKH); + PPC440_PCIE_DCR(s, PEGPL_OMR1MSKL); + PPC440_PCIE_DCR(s, PEGPL_OMR2BAH); + PPC440_PCIE_DCR(s, PEGPL_OMR2BAL); + PPC440_PCIE_DCR(s, PEGPL_OMR2MSKH); + PPC440_PCIE_DCR(s, PEGPL_OMR2MSKL); + PPC440_PCIE_DCR(s, PEGPL_OMR3BAH); + PPC440_PCIE_DCR(s, PEGPL_OMR3BAL); + PPC440_PCIE_DCR(s, PEGPL_OMR3MSKH); + PPC440_PCIE_DCR(s, PEGPL_OMR3MSKL); + PPC440_PCIE_DCR(s, PEGPL_REGBAH); + PPC440_PCIE_DCR(s, PEGPL_REGBAL); + PPC440_PCIE_DCR(s, PEGPL_REGMSK); + PPC440_PCIE_DCR(s, PEGPL_SPECIAL); + PPC440_PCIE_DCR(s, PEGPL_CFG); +} + static void ppc460ex_pcie_realize(DeviceState *dev, Error **errp) { PPC460EXPCIEState *s = PPC460EX_PCIE_HOST(dev); PCIHostState *pci = PCI_HOST_BRIDGE(dev); - int i, id; - char buf[16]; + int i; + char buf[20]; - switch (s->dcrn_base) { - case DCRN_PCIE0_BASE: - id = 0; - break; - case DCRN_PCIE1_BASE: - id = 1; - break; - default: - error_setg(errp, "invalid PCIe DCRN base"); + if (!s->cpu) { + error_setg(errp, "cpu link property must be set"); return; } - snprintf(buf, sizeof(buf), "pcie%d-io", id); - memory_region_init(&s->iomem, OBJECT(s), buf, UINT64_MAX); + if (s->num < 0 || s->dcrn_base < 0) { + error_setg(errp, "busnum and dcrn-base properties must be set"); + return; + } + snprintf(buf, sizeof(buf), "pcie%d-mem", s->num); + memory_region_init(&s->busmem, OBJECT(s), buf, UINT64_MAX); + snprintf(buf, sizeof(buf), "pcie%d-io", s->num); + memory_region_init(&s->iomem, OBJECT(s), buf, 64 * KiB); for (i = 0; i < 4; i++) { sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]); } - snprintf(buf, sizeof(buf), "pcie.%d", id); + snprintf(buf, sizeof(buf), "pcie.%d", s->num); pci->bus = pci_register_root_bus(DEVICE(s), buf, ppc460ex_set_irq, - pci_swizzle_map_irq_fn, s, &s->iomem, - get_system_io(), 0, 4, TYPE_PCIE_BUS); + pci_swizzle_map_irq_fn, s, &s->busmem, + &s->iomem, 0, 4, TYPE_PCIE_BUS); + ppc460ex_pcie_register_dcrs(s); } static Property ppc460ex_pcie_props[] = { + DEFINE_PROP_INT32("busnum", PPC460EXPCIEState, num, -1), DEFINE_PROP_INT32("dcrn-base", PPC460EXPCIEState, dcrn_base, -1), + DEFINE_PROP_LINK("cpu", PPC460EXPCIEState, cpu, TYPE_POWERPC_CPU, + PowerPCCPU *), DEFINE_PROP_END_OF_LIST(), }; @@ -1310,68 +1051,3 @@ static void ppc460ex_pcie_register(void) } type_init(ppc460ex_pcie_register) - -static void ppc460ex_pcie_register_dcrs(PPC460EXPCIEState *s, CPUPPCState *env) -{ - ppc_dcr_register(env, s->dcrn_base + PEGPL_CFGBAH, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_CFGBAL, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_CFGMSK, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_MSGBAH, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_MSGBAL, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_MSGMSK, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR1BAH, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR1BAL, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR1MSKH, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR1MSKL, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR2BAH, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR2BAL, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR2MSKH, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR2MSKL, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR3BAH, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR3BAL, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR3MSKH, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR3MSKL, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_REGBAH, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_REGBAL, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_REGMSK, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_SPECIAL, s, - &dcr_read_pcie, &dcr_write_pcie); - ppc_dcr_register(env, s->dcrn_base + PEGPL_CFG, s, - &dcr_read_pcie, &dcr_write_pcie); -} - -void ppc460ex_pcie_init(CPUPPCState *env) -{ - DeviceState *dev; - - dev = qdev_new(TYPE_PPC460EX_PCIE_HOST); - qdev_prop_set_int32(dev, "dcrn-base", DCRN_PCIE0_BASE); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - ppc460ex_pcie_register_dcrs(PPC460EX_PCIE_HOST(dev), env); - - dev = qdev_new(TYPE_PPC460EX_PCIE_HOST); - qdev_prop_set_int32(dev, "dcrn-base", DCRN_PCIE1_BASE); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - ppc460ex_pcie_register_dcrs(PPC460EX_PCIE_HOST(dev), env); -} diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index 737c0896b4..c1d111465d 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -23,452 +23,10 @@ */ #include "qemu/osdep.h" -#include "qemu/units.h" -#include "sysemu/reset.h" #include "cpu.h" -#include "hw/irq.h" -#include "hw/ppc/ppc.h" #include "hw/ppc/ppc4xx.h" -#include "hw/intc/ppc-uic.h" #include "hw/qdev-properties.h" -#include "qemu/log.h" -#include "exec/address-spaces.h" -#include "qemu/error-report.h" #include "qapi/error.h" -#include "trace.h" - -static void ppc4xx_reset(void *opaque) -{ - PowerPCCPU *cpu = opaque; - - cpu_reset(CPU(cpu)); -} - -/*****************************************************************************/ -/* Generic PowerPC 4xx processor instantiation */ -PowerPCCPU *ppc4xx_init(const char *cpu_type, - clk_setup_t *cpu_clk, clk_setup_t *tb_clk, - uint32_t sysclk) -{ - PowerPCCPU *cpu; - CPUPPCState *env; - - /* init CPUs */ - cpu = POWERPC_CPU(cpu_create(cpu_type)); - env = &cpu->env; - - cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */ - cpu_clk->opaque = env; - /* Set time-base frequency to sysclk */ - tb_clk->cb = ppc_40x_timers_init(env, sysclk, PPC_INTERRUPT_PIT); - tb_clk->opaque = env; - ppc_dcr_init(env, NULL, NULL); - /* Register qemu callbacks */ - qemu_register_reset(ppc4xx_reset, cpu); - - return cpu; -} - -/*****************************************************************************/ -/* SDRAM controller */ -typedef struct ppc4xx_sdram_t ppc4xx_sdram_t; -struct ppc4xx_sdram_t { - uint32_t addr; - int nbanks; - MemoryRegion containers[4]; /* used for clipping */ - MemoryRegion *ram_memories; - hwaddr ram_bases[4]; - hwaddr ram_sizes[4]; - uint32_t besr0; - uint32_t besr1; - uint32_t bear; - uint32_t cfg; - uint32_t status; - uint32_t rtr; - uint32_t pmit; - uint32_t bcr[4]; - uint32_t tr; - uint32_t ecccfg; - uint32_t eccesr; - qemu_irq irq; -}; - -enum { - SDRAM0_CFGADDR = 0x010, - SDRAM0_CFGDATA = 0x011, -}; - -/* XXX: TOFIX: some patches have made this code become inconsistent: - * there are type inconsistencies, mixing hwaddr, target_ulong - * and uint32_t - */ -static uint32_t sdram_bcr (hwaddr ram_base, - hwaddr ram_size) -{ - uint32_t bcr; - - switch (ram_size) { - case 4 * MiB: - bcr = 0x00000000; - break; - case 8 * MiB: - bcr = 0x00020000; - break; - case 16 * MiB: - bcr = 0x00040000; - break; - case 32 * MiB: - bcr = 0x00060000; - break; - case 64 * MiB: - bcr = 0x00080000; - break; - case 128 * MiB: - bcr = 0x000A0000; - break; - case 256 * MiB: - bcr = 0x000C0000; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid RAM size 0x%" HWADDR_PRIx "\n", __func__, - ram_size); - return 0x00000000; - } - bcr |= ram_base & 0xFF800000; - bcr |= 1; - - return bcr; -} - -static inline hwaddr sdram_base(uint32_t bcr) -{ - return bcr & 0xFF800000; -} - -static target_ulong sdram_size (uint32_t bcr) -{ - target_ulong size; - int sh; - - sh = (bcr >> 17) & 0x7; - if (sh == 7) - size = -1; - else - size = (4 * MiB) << sh; - - return size; -} - -static void sdram_set_bcr(ppc4xx_sdram_t *sdram, int i, - uint32_t bcr, int enabled) -{ - if (sdram->bcr[i] & 0x00000001) { - /* Unmap RAM */ - trace_ppc4xx_sdram_unmap(sdram_base(sdram->bcr[i]), - sdram_size(sdram->bcr[i])); - memory_region_del_subregion(get_system_memory(), - &sdram->containers[i]); - memory_region_del_subregion(&sdram->containers[i], - &sdram->ram_memories[i]); - object_unparent(OBJECT(&sdram->containers[i])); - } - sdram->bcr[i] = bcr & 0xFFDEE001; - if (enabled && (bcr & 0x00000001)) { - trace_ppc4xx_sdram_unmap(sdram_base(bcr), sdram_size(bcr)); - memory_region_init(&sdram->containers[i], NULL, "sdram-containers", - sdram_size(bcr)); - memory_region_add_subregion(&sdram->containers[i], 0, - &sdram->ram_memories[i]); - memory_region_add_subregion(get_system_memory(), - sdram_base(bcr), - &sdram->containers[i]); - } -} - -static void sdram_map_bcr (ppc4xx_sdram_t *sdram) -{ - int i; - - for (i = 0; i < sdram->nbanks; i++) { - if (sdram->ram_sizes[i] != 0) { - sdram_set_bcr(sdram, i, sdram_bcr(sdram->ram_bases[i], - sdram->ram_sizes[i]), 1); - } else { - sdram_set_bcr(sdram, i, 0x00000000, 0); - } - } -} - -static void sdram_unmap_bcr (ppc4xx_sdram_t *sdram) -{ - int i; - - for (i = 0; i < sdram->nbanks; i++) { - trace_ppc4xx_sdram_unmap(sdram_base(sdram->bcr[i]), - sdram_size(sdram->bcr[i])); - memory_region_del_subregion(get_system_memory(), - &sdram->ram_memories[i]); - } -} - -static uint32_t dcr_read_sdram (void *opaque, int dcrn) -{ - ppc4xx_sdram_t *sdram; - uint32_t ret; - - sdram = opaque; - switch (dcrn) { - case SDRAM0_CFGADDR: - ret = sdram->addr; - break; - case SDRAM0_CFGDATA: - switch (sdram->addr) { - case 0x00: /* SDRAM_BESR0 */ - ret = sdram->besr0; - break; - case 0x08: /* SDRAM_BESR1 */ - ret = sdram->besr1; - break; - case 0x10: /* SDRAM_BEAR */ - ret = sdram->bear; - break; - case 0x20: /* SDRAM_CFG */ - ret = sdram->cfg; - break; - case 0x24: /* SDRAM_STATUS */ - ret = sdram->status; - break; - case 0x30: /* SDRAM_RTR */ - ret = sdram->rtr; - break; - case 0x34: /* SDRAM_PMIT */ - ret = sdram->pmit; - break; - case 0x40: /* SDRAM_B0CR */ - ret = sdram->bcr[0]; - break; - case 0x44: /* SDRAM_B1CR */ - ret = sdram->bcr[1]; - break; - case 0x48: /* SDRAM_B2CR */ - ret = sdram->bcr[2]; - break; - case 0x4C: /* SDRAM_B3CR */ - ret = sdram->bcr[3]; - break; - case 0x80: /* SDRAM_TR */ - ret = -1; /* ? */ - break; - case 0x94: /* SDRAM_ECCCFG */ - ret = sdram->ecccfg; - break; - case 0x98: /* SDRAM_ECCESR */ - ret = sdram->eccesr; - break; - default: /* Error */ - ret = -1; - break; - } - break; - default: - /* Avoid gcc warning */ - ret = 0x00000000; - break; - } - - return ret; -} - -static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val) -{ - ppc4xx_sdram_t *sdram; - - sdram = opaque; - switch (dcrn) { - case SDRAM0_CFGADDR: - sdram->addr = val; - break; - case SDRAM0_CFGDATA: - switch (sdram->addr) { - case 0x00: /* SDRAM_BESR0 */ - sdram->besr0 &= ~val; - break; - case 0x08: /* SDRAM_BESR1 */ - sdram->besr1 &= ~val; - break; - case 0x10: /* SDRAM_BEAR */ - sdram->bear = val; - break; - case 0x20: /* SDRAM_CFG */ - val &= 0xFFE00000; - if (!(sdram->cfg & 0x80000000) && (val & 0x80000000)) { - trace_ppc4xx_sdram_enable("enable"); - /* validate all RAM mappings */ - sdram_map_bcr(sdram); - sdram->status &= ~0x80000000; - } else if ((sdram->cfg & 0x80000000) && !(val & 0x80000000)) { - trace_ppc4xx_sdram_enable("disable"); - /* invalidate all RAM mappings */ - sdram_unmap_bcr(sdram); - sdram->status |= 0x80000000; - } - if (!(sdram->cfg & 0x40000000) && (val & 0x40000000)) - sdram->status |= 0x40000000; - else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000)) - sdram->status &= ~0x40000000; - sdram->cfg = val; - break; - case 0x24: /* SDRAM_STATUS */ - /* Read-only register */ - break; - case 0x30: /* SDRAM_RTR */ - sdram->rtr = val & 0x3FF80000; - break; - case 0x34: /* SDRAM_PMIT */ - sdram->pmit = (val & 0xF8000000) | 0x07C00000; - break; - case 0x40: /* SDRAM_B0CR */ - sdram_set_bcr(sdram, 0, val, sdram->cfg & 0x80000000); - break; - case 0x44: /* SDRAM_B1CR */ - sdram_set_bcr(sdram, 1, val, sdram->cfg & 0x80000000); - break; - case 0x48: /* SDRAM_B2CR */ - sdram_set_bcr(sdram, 2, val, sdram->cfg & 0x80000000); - break; - case 0x4C: /* SDRAM_B3CR */ - sdram_set_bcr(sdram, 3, val, sdram->cfg & 0x80000000); - break; - case 0x80: /* SDRAM_TR */ - sdram->tr = val & 0x018FC01F; - break; - case 0x94: /* SDRAM_ECCCFG */ - sdram->ecccfg = val & 0x00F00000; - break; - case 0x98: /* SDRAM_ECCESR */ - val &= 0xFFF0F000; - if (sdram->eccesr == 0 && val != 0) - qemu_irq_raise(sdram->irq); - else if (sdram->eccesr != 0 && val == 0) - qemu_irq_lower(sdram->irq); - sdram->eccesr = val; - break; - default: /* Error */ - break; - } - break; - } -} - -static void sdram_reset (void *opaque) -{ - ppc4xx_sdram_t *sdram; - - sdram = opaque; - sdram->addr = 0x00000000; - sdram->bear = 0x00000000; - sdram->besr0 = 0x00000000; /* No error */ - sdram->besr1 = 0x00000000; /* No error */ - sdram->cfg = 0x00000000; - sdram->ecccfg = 0x00000000; /* No ECC */ - sdram->eccesr = 0x00000000; /* No error */ - sdram->pmit = 0x07C00000; - sdram->rtr = 0x05F00000; - sdram->tr = 0x00854009; - /* We pre-initialize RAM banks */ - sdram->status = 0x00000000; - sdram->cfg = 0x00800000; -} - -void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks, - MemoryRegion *ram_memories, - hwaddr *ram_bases, - hwaddr *ram_sizes, - int do_init) -{ - ppc4xx_sdram_t *sdram; - - sdram = g_new0(ppc4xx_sdram_t, 1); - sdram->irq = irq; - sdram->nbanks = nbanks; - sdram->ram_memories = ram_memories; - memset(sdram->ram_bases, 0, 4 * sizeof(hwaddr)); - memcpy(sdram->ram_bases, ram_bases, - nbanks * sizeof(hwaddr)); - memset(sdram->ram_sizes, 0, 4 * sizeof(hwaddr)); - memcpy(sdram->ram_sizes, ram_sizes, - nbanks * sizeof(hwaddr)); - qemu_register_reset(&sdram_reset, sdram); - ppc_dcr_register(env, SDRAM0_CFGADDR, - sdram, &dcr_read_sdram, &dcr_write_sdram); - ppc_dcr_register(env, SDRAM0_CFGDATA, - sdram, &dcr_read_sdram, &dcr_write_sdram); - if (do_init) - sdram_map_bcr(sdram); -} - -/* - * Split RAM between SDRAM banks. - * - * 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. - */ -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[]) -{ - ram_addr_t size_left = memory_region_size(ram); - ram_addr_t base = 0; - ram_addr_t bank_size; - int i; - int j; - - for (i = 0; i < nr_banks; i++) { - 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) { - /* No need to use the remaining banks. */ - break; - } - } - - if (size_left) { - ram_addr_t used_size = memory_region_size(ram) - size_left; - GString *s = g_string_new(NULL); - - 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("at most %d bank%s of %s MiB each supported", - nr_banks, nr_banks == 1 ? "" : "s", s->str); - error_printf("Possible valid RAM size: %" PRIi64 " MiB \n", - used_size ? used_size / MiB : sdram_bank_sizes[i - 1] / MiB); - - g_string_free(s, true); - exit(EXIT_FAILURE); - } -} /*****************************************************************************/ /* MAL */ @@ -491,32 +49,10 @@ enum { MAL0_RCBS1 = 0x1E1, }; -typedef struct ppc4xx_mal_t ppc4xx_mal_t; -struct ppc4xx_mal_t { - qemu_irq irqs[4]; - uint32_t cfg; - uint32_t esr; - uint32_t ier; - uint32_t txcasr; - uint32_t txcarr; - uint32_t txeobisr; - uint32_t txdeir; - uint32_t rxcasr; - uint32_t rxcarr; - uint32_t rxeobisr; - uint32_t rxdeir; - uint32_t *txctpr; - uint32_t *rxctpr; - uint32_t *rcbs; - uint8_t txcnum; - uint8_t rxcnum; -}; - -static void ppc4xx_mal_reset(void *opaque) +static void ppc4xx_mal_reset(DeviceState *dev) { - ppc4xx_mal_t *mal; + Ppc4xxMalState *mal = PPC4xx_MAL(dev); - mal = opaque; mal->cfg = 0x0007C000; mal->esr = 0x00000000; mal->ier = 0x00000000; @@ -530,10 +66,9 @@ static void ppc4xx_mal_reset(void *opaque) static uint32_t dcr_read_mal(void *opaque, int dcrn) { - ppc4xx_mal_t *mal; + Ppc4xxMalState *mal = opaque; uint32_t ret; - mal = opaque; switch (dcrn) { case MAL0_CFG: ret = mal->cfg; @@ -587,13 +122,12 @@ static uint32_t dcr_read_mal(void *opaque, int dcrn) static void dcr_write_mal(void *opaque, int dcrn, uint32_t val) { - ppc4xx_mal_t *mal; + Ppc4xxMalState *mal = opaque; - mal = opaque; switch (dcrn) { case MAL0_CFG: if (val & 0x80000000) { - ppc4xx_mal_reset(mal); + ppc4xx_mal_reset(DEVICE(mal)); } mal->cfg = val & 0x00FFC087; break; @@ -644,55 +178,404 @@ static void dcr_write_mal(void *opaque, int dcrn, uint32_t val) } } -void ppc4xx_mal_init(CPUPPCState *env, uint8_t txcnum, uint8_t rxcnum, - qemu_irq irqs[4]) +static void ppc4xx_mal_realize(DeviceState *dev, Error **errp) { - ppc4xx_mal_t *mal; + Ppc4xxMalState *mal = PPC4xx_MAL(dev); + Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); int i; - assert(txcnum <= 32 && rxcnum <= 32); - mal = g_malloc0(sizeof(*mal)); - mal->txcnum = txcnum; - mal->rxcnum = rxcnum; - mal->txctpr = g_new0(uint32_t, txcnum); - mal->rxctpr = g_new0(uint32_t, rxcnum); - mal->rcbs = g_new0(uint32_t, rxcnum); - for (i = 0; i < 4; i++) { - mal->irqs[i] = irqs[i]; + if (mal->txcnum > 32 || mal->rxcnum > 32) { + error_setg(errp, "invalid TXC/RXC number"); + return; } - qemu_register_reset(&ppc4xx_mal_reset, mal); - ppc_dcr_register(env, MAL0_CFG, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_ESR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_IER, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_TXCASR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_TXCARR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_TXEOBISR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_TXDEIR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_RXCASR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_RXCARR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_RXEOBISR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_RXDEIR, - mal, &dcr_read_mal, &dcr_write_mal); - for (i = 0; i < txcnum; i++) { - ppc_dcr_register(env, MAL0_TXCTP0R + i, - mal, &dcr_read_mal, &dcr_write_mal); + + mal->txctpr = g_new0(uint32_t, mal->txcnum); + mal->rxctpr = g_new0(uint32_t, mal->rxcnum); + mal->rcbs = g_new0(uint32_t, mal->rxcnum); + + for (i = 0; i < ARRAY_SIZE(mal->irqs); i++) { + sysbus_init_irq(SYS_BUS_DEVICE(dev), &mal->irqs[i]); } - for (i = 0; i < rxcnum; i++) { - ppc_dcr_register(env, MAL0_RXCTP0R + i, - mal, &dcr_read_mal, &dcr_write_mal); + + ppc4xx_dcr_register(dcr, MAL0_CFG, mal, &dcr_read_mal, &dcr_write_mal); + ppc4xx_dcr_register(dcr, MAL0_ESR, mal, &dcr_read_mal, &dcr_write_mal); + ppc4xx_dcr_register(dcr, MAL0_IER, mal, &dcr_read_mal, &dcr_write_mal); + ppc4xx_dcr_register(dcr, MAL0_TXCASR, mal, &dcr_read_mal, &dcr_write_mal); + ppc4xx_dcr_register(dcr, MAL0_TXCARR, mal, &dcr_read_mal, &dcr_write_mal); + ppc4xx_dcr_register(dcr, MAL0_TXEOBISR, mal, &dcr_read_mal, &dcr_write_mal); + ppc4xx_dcr_register(dcr, MAL0_TXDEIR, mal, &dcr_read_mal, &dcr_write_mal); + ppc4xx_dcr_register(dcr, MAL0_RXCASR, mal, &dcr_read_mal, &dcr_write_mal); + ppc4xx_dcr_register(dcr, MAL0_RXCARR, mal, &dcr_read_mal, &dcr_write_mal); + ppc4xx_dcr_register(dcr, MAL0_RXEOBISR, mal, &dcr_read_mal, &dcr_write_mal); + ppc4xx_dcr_register(dcr, MAL0_RXDEIR, mal, &dcr_read_mal, &dcr_write_mal); + for (i = 0; i < mal->txcnum; i++) { + ppc4xx_dcr_register(dcr, MAL0_TXCTP0R + i, + mal, &dcr_read_mal, &dcr_write_mal); } - for (i = 0; i < rxcnum; i++) { - ppc_dcr_register(env, MAL0_RCBS0 + i, - mal, &dcr_read_mal, &dcr_write_mal); + for (i = 0; i < mal->rxcnum; i++) { + ppc4xx_dcr_register(dcr, MAL0_RXCTP0R + i, + mal, &dcr_read_mal, &dcr_write_mal); + } + for (i = 0; i < mal->rxcnum; i++) { + ppc4xx_dcr_register(dcr, MAL0_RCBS0 + i, + mal, &dcr_read_mal, &dcr_write_mal); } } + +static void ppc4xx_mal_finalize(Object *obj) +{ + Ppc4xxMalState *mal = PPC4xx_MAL(obj); + + g_free(mal->rcbs); + g_free(mal->rxctpr); + g_free(mal->txctpr); +} + +static Property ppc4xx_mal_properties[] = { + DEFINE_PROP_UINT8("txc-num", Ppc4xxMalState, txcnum, 0), + DEFINE_PROP_UINT8("rxc-num", Ppc4xxMalState, rxcnum, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ppc4xx_mal_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ppc4xx_mal_realize; + dc->reset = ppc4xx_mal_reset; + /* Reason: only works as function of a ppc4xx SoC */ + dc->user_creatable = false; + device_class_set_props(dc, ppc4xx_mal_properties); +} + +/*****************************************************************************/ +/* Peripheral local bus arbitrer */ +enum { + PLB3A0_ACR = 0x077, + PLB4A0_ACR = 0x081, + PLB0_BESR = 0x084, + PLB0_BEAR = 0x086, + PLB0_ACR = 0x087, + PLB4A1_ACR = 0x089, +}; + +static uint32_t dcr_read_plb(void *opaque, int dcrn) +{ + Ppc4xxPlbState *plb = opaque; + uint32_t ret; + + switch (dcrn) { + case PLB0_ACR: + ret = plb->acr; + break; + case PLB0_BEAR: + ret = plb->bear; + break; + case PLB0_BESR: + ret = plb->besr; + break; + default: + /* Avoid gcc warning */ + ret = 0; + break; + } + + return ret; +} + +static void dcr_write_plb(void *opaque, int dcrn, uint32_t val) +{ + Ppc4xxPlbState *plb = opaque; + + switch (dcrn) { + case PLB0_ACR: + /* + * We don't care about the actual parameters written as + * we don't manage any priorities on the bus + */ + plb->acr = val & 0xF8000000; + break; + case PLB0_BEAR: + /* Read only */ + break; + case PLB0_BESR: + /* Write-clear */ + plb->besr &= ~val; + break; + } +} + +static void ppc405_plb_reset(DeviceState *dev) +{ + Ppc4xxPlbState *plb = PPC4xx_PLB(dev); + + plb->acr = 0x00000000; + plb->bear = 0x00000000; + plb->besr = 0x00000000; +} + +static void ppc405_plb_realize(DeviceState *dev, Error **errp) +{ + Ppc4xxPlbState *plb = PPC4xx_PLB(dev); + Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); + + ppc4xx_dcr_register(dcr, PLB3A0_ACR, plb, &dcr_read_plb, &dcr_write_plb); + ppc4xx_dcr_register(dcr, PLB4A0_ACR, plb, &dcr_read_plb, &dcr_write_plb); + ppc4xx_dcr_register(dcr, PLB0_ACR, plb, &dcr_read_plb, &dcr_write_plb); + ppc4xx_dcr_register(dcr, PLB0_BEAR, plb, &dcr_read_plb, &dcr_write_plb); + ppc4xx_dcr_register(dcr, PLB0_BESR, plb, &dcr_read_plb, &dcr_write_plb); + ppc4xx_dcr_register(dcr, PLB4A1_ACR, plb, &dcr_read_plb, &dcr_write_plb); +} + +static void ppc405_plb_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ppc405_plb_realize; + dc->reset = ppc405_plb_reset; + /* Reason: only works as function of a ppc4xx SoC */ + dc->user_creatable = false; +} + +/*****************************************************************************/ +/* Peripheral controller */ +enum { + EBC0_CFGADDR = 0x012, + EBC0_CFGDATA = 0x013, +}; + +static uint32_t dcr_read_ebc(void *opaque, int dcrn) +{ + Ppc4xxEbcState *ebc = opaque; + uint32_t ret; + + switch (dcrn) { + case EBC0_CFGADDR: + ret = ebc->addr; + break; + case EBC0_CFGDATA: + switch (ebc->addr) { + case 0x00: /* B0CR */ + ret = ebc->bcr[0]; + break; + case 0x01: /* B1CR */ + ret = ebc->bcr[1]; + break; + case 0x02: /* B2CR */ + ret = ebc->bcr[2]; + break; + case 0x03: /* B3CR */ + ret = ebc->bcr[3]; + break; + case 0x04: /* B4CR */ + ret = ebc->bcr[4]; + break; + case 0x05: /* B5CR */ + ret = ebc->bcr[5]; + break; + case 0x06: /* B6CR */ + ret = ebc->bcr[6]; + break; + case 0x07: /* B7CR */ + ret = ebc->bcr[7]; + break; + case 0x10: /* B0AP */ + ret = ebc->bap[0]; + break; + case 0x11: /* B1AP */ + ret = ebc->bap[1]; + break; + case 0x12: /* B2AP */ + ret = ebc->bap[2]; + break; + case 0x13: /* B3AP */ + ret = ebc->bap[3]; + break; + case 0x14: /* B4AP */ + ret = ebc->bap[4]; + break; + case 0x15: /* B5AP */ + ret = ebc->bap[5]; + break; + case 0x16: /* B6AP */ + ret = ebc->bap[6]; + break; + case 0x17: /* B7AP */ + ret = ebc->bap[7]; + break; + case 0x20: /* BEAR */ + ret = ebc->bear; + break; + case 0x21: /* BESR0 */ + ret = ebc->besr0; + break; + case 0x22: /* BESR1 */ + ret = ebc->besr1; + break; + case 0x23: /* CFG */ + ret = ebc->cfg; + break; + default: + ret = 0x00000000; + break; + } + break; + default: + ret = 0x00000000; + break; + } + + return ret; +} + +static void dcr_write_ebc(void *opaque, int dcrn, uint32_t val) +{ + Ppc4xxEbcState *ebc = opaque; + + switch (dcrn) { + case EBC0_CFGADDR: + ebc->addr = val; + break; + case EBC0_CFGDATA: + switch (ebc->addr) { + case 0x00: /* B0CR */ + break; + case 0x01: /* B1CR */ + break; + case 0x02: /* B2CR */ + break; + case 0x03: /* B3CR */ + break; + case 0x04: /* B4CR */ + break; + case 0x05: /* B5CR */ + break; + case 0x06: /* B6CR */ + break; + case 0x07: /* B7CR */ + break; + case 0x10: /* B0AP */ + break; + case 0x11: /* B1AP */ + break; + case 0x12: /* B2AP */ + break; + case 0x13: /* B3AP */ + break; + case 0x14: /* B4AP */ + break; + case 0x15: /* B5AP */ + break; + case 0x16: /* B6AP */ + break; + case 0x17: /* B7AP */ + break; + case 0x20: /* BEAR */ + break; + case 0x21: /* BESR0 */ + break; + case 0x22: /* BESR1 */ + break; + case 0x23: /* CFG */ + break; + default: + break; + } + break; + default: + break; + } +} + +static void ppc405_ebc_reset(DeviceState *dev) +{ + Ppc4xxEbcState *ebc = PPC4xx_EBC(dev); + int i; + + ebc->addr = 0x00000000; + ebc->bap[0] = 0x7F8FFE80; + ebc->bcr[0] = 0xFFE28000; + for (i = 0; i < 8; i++) { + ebc->bap[i] = 0x00000000; + ebc->bcr[i] = 0x00000000; + } + ebc->besr0 = 0x00000000; + ebc->besr1 = 0x00000000; + ebc->cfg = 0x80400000; +} + +static void ppc405_ebc_realize(DeviceState *dev, Error **errp) +{ + Ppc4xxEbcState *ebc = PPC4xx_EBC(dev); + Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); + + ppc4xx_dcr_register(dcr, EBC0_CFGADDR, ebc, &dcr_read_ebc, &dcr_write_ebc); + ppc4xx_dcr_register(dcr, EBC0_CFGDATA, ebc, &dcr_read_ebc, &dcr_write_ebc); +} + +static void ppc405_ebc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ppc405_ebc_realize; + dc->reset = ppc405_ebc_reset; + /* Reason: only works as function of a ppc4xx SoC */ + dc->user_creatable = false; +} + +/* PPC4xx_DCR_DEVICE */ + +void ppc4xx_dcr_register(Ppc4xxDcrDeviceState *dev, int dcrn, void *opaque, + dcr_read_cb dcr_read, dcr_write_cb dcr_write) +{ + assert(dev->cpu); + ppc_dcr_register(&dev->cpu->env, dcrn, opaque, dcr_read, dcr_write); +} + +bool ppc4xx_dcr_realize(Ppc4xxDcrDeviceState *dev, PowerPCCPU *cpu, + Error **errp) +{ + object_property_set_link(OBJECT(dev), "cpu", OBJECT(cpu), &error_abort); + return sysbus_realize(SYS_BUS_DEVICE(dev), errp); +} + +static Property ppc4xx_dcr_properties[] = { + DEFINE_PROP_LINK("cpu", Ppc4xxDcrDeviceState, cpu, TYPE_POWERPC_CPU, + PowerPCCPU *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ppc4xx_dcr_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + device_class_set_props(dc, ppc4xx_dcr_properties); +} + +static const TypeInfo ppc4xx_types[] = { + { + .name = TYPE_PPC4xx_MAL, + .parent = TYPE_PPC4xx_DCR_DEVICE, + .instance_size = sizeof(Ppc4xxMalState), + .instance_finalize = ppc4xx_mal_finalize, + .class_init = ppc4xx_mal_class_init, + }, { + .name = TYPE_PPC4xx_PLB, + .parent = TYPE_PPC4xx_DCR_DEVICE, + .instance_size = sizeof(Ppc4xxPlbState), + .class_init = ppc405_plb_class_init, + }, { + .name = TYPE_PPC4xx_EBC, + .parent = TYPE_PPC4xx_DCR_DEVICE, + .instance_size = sizeof(Ppc4xxEbcState), + .class_init = ppc405_ebc_class_init, + }, { + .name = TYPE_PPC4xx_DCR_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Ppc4xxDcrDeviceState), + .class_init = ppc4xx_dcr_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(ppc4xx_types) diff --git a/hw/ppc/ppc4xx_sdram.c b/hw/ppc/ppc4xx_sdram.c new file mode 100644 index 0000000000..c0c87ff636 --- /dev/null +++ b/hw/ppc/ppc4xx_sdram.c @@ -0,0 +1,751 @@ +/* + * QEMU PowerPC 4xx embedded processors SDRAM controller emulation + * + * DDR SDRAM controller: + * Copyright (c) 2007 Jocelyn Mayer + * + * 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. + * + * DDR2 SDRAM controller: + * Copyright (c) 2012 François Revol + * Copyright (c) 2016-2019 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "exec/address-spaces.h" /* get_system_memory() */ +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/ppc/ppc4xx.h" +#include "trace.h" + +/*****************************************************************************/ +/* Shared functions */ + +/* + * Split RAM between SDRAM banks. + * + * 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. + */ +static bool ppc4xx_sdram_banks(MemoryRegion *ram, int nr_banks, + Ppc4xxSdramBank ram_banks[], + const ram_addr_t sdram_bank_sizes[], + Error **errp) +{ + ERRP_GUARD(); + ram_addr_t size_left = memory_region_size(ram); + ram_addr_t base = 0; + ram_addr_t bank_size; + int i; + int j; + + for (i = 0; i < nr_banks; i++) { + for (j = 0; sdram_bank_sizes[j] != 0; j++) { + bank_size = sdram_bank_sizes[j]; + if (bank_size <= size_left) { + char name[32]; + + ram_banks[i].base = base; + ram_banks[i].size = bank_size; + base += bank_size; + size_left -= bank_size; + snprintf(name, sizeof(name), "ppc4xx.sdram%d", i); + memory_region_init_alias(&ram_banks[i].ram, NULL, name, ram, + ram_banks[i].base, ram_banks[i].size); + break; + } + } + if (!size_left) { + /* No need to use the remaining banks. */ + break; + } + } + + if (size_left) { + ram_addr_t used_size = memory_region_size(ram) - size_left; + GString *s = g_string_new(NULL); + + 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_setg(errp, "Invalid SDRAM banks"); + error_append_hint(errp, "at most %d bank%s of %s MiB each supported\n", + nr_banks, nr_banks == 1 ? "" : "s", s->str); + error_append_hint(errp, "Possible valid RAM size: %" PRIi64 " MiB\n", + used_size ? used_size / MiB : sdram_bank_sizes[i - 1] / MiB); + + g_string_free(s, true); + return false; + } + return true; +} + +static void sdram_bank_map(Ppc4xxSdramBank *bank) +{ + trace_ppc4xx_sdram_map(bank->base, bank->size); + memory_region_init(&bank->container, NULL, "sdram-container", bank->size); + memory_region_add_subregion(&bank->container, 0, &bank->ram); + memory_region_add_subregion(get_system_memory(), bank->base, + &bank->container); +} + +static void sdram_bank_unmap(Ppc4xxSdramBank *bank) +{ + trace_ppc4xx_sdram_unmap(bank->base, bank->size); + memory_region_del_subregion(get_system_memory(), &bank->container); + memory_region_del_subregion(&bank->container, &bank->ram); + object_unparent(OBJECT(&bank->container)); +} + +static void sdram_bank_set_bcr(Ppc4xxSdramBank *bank, uint32_t bcr, + hwaddr base, hwaddr size, int enabled) +{ + if (memory_region_is_mapped(&bank->container)) { + sdram_bank_unmap(bank); + } + bank->bcr = bcr; + bank->base = base; + bank->size = size; + if (enabled && (bcr & 1)) { + sdram_bank_map(bank); + } +} + +enum { + SDRAM0_CFGADDR = 0x010, + SDRAM0_CFGDATA = 0x011, +}; + +/*****************************************************************************/ +/* DDR SDRAM controller */ +#define SDRAM_DDR_BCR_MASK 0xFFDEE001 + +static uint32_t sdram_ddr_bcr(hwaddr ram_base, hwaddr ram_size) +{ + uint32_t bcr; + + switch (ram_size) { + case 4 * MiB: + bcr = 0; + break; + case 8 * MiB: + bcr = 0x20000; + break; + case 16 * MiB: + bcr = 0x40000; + break; + case 32 * MiB: + bcr = 0x60000; + break; + case 64 * MiB: + bcr = 0x80000; + break; + case 128 * MiB: + bcr = 0xA0000; + break; + case 256 * MiB: + bcr = 0xC0000; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid RAM size 0x%" HWADDR_PRIx "\n", __func__, + ram_size); + return 0; + } + bcr |= ram_base & 0xFF800000; + bcr |= 1; + + return bcr; +} + +static inline hwaddr sdram_ddr_base(uint32_t bcr) +{ + return bcr & 0xFF800000; +} + +static hwaddr sdram_ddr_size(uint32_t bcr) +{ + int sh = (bcr >> 17) & 0x7; + + if (sh == 7) { + return -1; + } + + return (4 * MiB) << sh; +} + +static uint32_t sdram_ddr_dcr_read(void *opaque, int dcrn) +{ + Ppc4xxSdramDdrState *s = opaque; + uint32_t ret; + + switch (dcrn) { + case SDRAM0_CFGADDR: + ret = s->addr; + break; + case SDRAM0_CFGDATA: + switch (s->addr) { + case 0x00: /* SDRAM_BESR0 */ + ret = s->besr0; + break; + case 0x08: /* SDRAM_BESR1 */ + ret = s->besr1; + break; + case 0x10: /* SDRAM_BEAR */ + ret = s->bear; + break; + case 0x20: /* SDRAM_CFG */ + ret = s->cfg; + break; + case 0x24: /* SDRAM_STATUS */ + ret = s->status; + break; + case 0x30: /* SDRAM_RTR */ + ret = s->rtr; + break; + case 0x34: /* SDRAM_PMIT */ + ret = s->pmit; + break; + case 0x40: /* SDRAM_B0CR */ + ret = s->bank[0].bcr; + break; + case 0x44: /* SDRAM_B1CR */ + ret = s->bank[1].bcr; + break; + case 0x48: /* SDRAM_B2CR */ + ret = s->bank[2].bcr; + break; + case 0x4C: /* SDRAM_B3CR */ + ret = s->bank[3].bcr; + break; + case 0x80: /* SDRAM_TR */ + ret = -1; /* ? */ + break; + case 0x94: /* SDRAM_ECCCFG */ + ret = s->ecccfg; + break; + case 0x98: /* SDRAM_ECCESR */ + ret = s->eccesr; + break; + default: /* Error */ + ret = -1; + break; + } + break; + default: + /* Avoid gcc warning */ + ret = 0; + break; + } + + return ret; +} + +static void sdram_ddr_dcr_write(void *opaque, int dcrn, uint32_t val) +{ + Ppc4xxSdramDdrState *s = opaque; + int i; + + switch (dcrn) { + case SDRAM0_CFGADDR: + s->addr = val; + break; + case SDRAM0_CFGDATA: + switch (s->addr) { + case 0x00: /* SDRAM_BESR0 */ + s->besr0 &= ~val; + break; + case 0x08: /* SDRAM_BESR1 */ + s->besr1 &= ~val; + break; + case 0x10: /* SDRAM_BEAR */ + s->bear = val; + break; + case 0x20: /* SDRAM_CFG */ + val &= 0xFFE00000; + if (!(s->cfg & 0x80000000) && (val & 0x80000000)) { + trace_ppc4xx_sdram_enable("enable"); + /* validate all RAM mappings */ + for (i = 0; i < s->nbanks; i++) { + if (s->bank[i].size) { + sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr, + s->bank[i].base, s->bank[i].size, + 1); + } + } + s->status &= ~0x80000000; + } else if ((s->cfg & 0x80000000) && !(val & 0x80000000)) { + trace_ppc4xx_sdram_enable("disable"); + /* invalidate all RAM mappings */ + for (i = 0; i < s->nbanks; i++) { + if (s->bank[i].size) { + sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr, + s->bank[i].base, s->bank[i].size, + 0); + } + } + s->status |= 0x80000000; + } + if (!(s->cfg & 0x40000000) && (val & 0x40000000)) { + s->status |= 0x40000000; + } else if ((s->cfg & 0x40000000) && !(val & 0x40000000)) { + s->status &= ~0x40000000; + } + s->cfg = val; + break; + case 0x24: /* SDRAM_STATUS */ + /* Read-only register */ + break; + case 0x30: /* SDRAM_RTR */ + s->rtr = val & 0x3FF80000; + break; + case 0x34: /* SDRAM_PMIT */ + s->pmit = (val & 0xF8000000) | 0x07C00000; + break; + case 0x40: /* SDRAM_B0CR */ + case 0x44: /* SDRAM_B1CR */ + case 0x48: /* SDRAM_B2CR */ + case 0x4C: /* SDRAM_B3CR */ + i = (s->addr - 0x40) / 4; + val &= SDRAM_DDR_BCR_MASK; + if (s->bank[i].size) { + sdram_bank_set_bcr(&s->bank[i], val, + sdram_ddr_base(val), sdram_ddr_size(val), + s->cfg & 0x80000000); + } + break; + case 0x80: /* SDRAM_TR */ + s->tr = val & 0x018FC01F; + break; + case 0x94: /* SDRAM_ECCCFG */ + s->ecccfg = val & 0x00F00000; + break; + case 0x98: /* SDRAM_ECCESR */ + val &= 0xFFF0F000; + if (s->eccesr == 0 && val != 0) { + qemu_irq_raise(s->irq); + } else if (s->eccesr != 0 && val == 0) { + qemu_irq_lower(s->irq); + } + s->eccesr = val; + break; + default: /* Error */ + break; + } + break; + } +} + +static void ppc4xx_sdram_ddr_reset(DeviceState *dev) +{ + Ppc4xxSdramDdrState *s = PPC4xx_SDRAM_DDR(dev); + + s->addr = 0; + s->bear = 0; + s->besr0 = 0; /* No error */ + s->besr1 = 0; /* No error */ + s->cfg = 0; + s->ecccfg = 0; /* No ECC */ + s->eccesr = 0; /* No error */ + s->pmit = 0x07C00000; + s->rtr = 0x05F00000; + s->tr = 0x00854009; + /* We pre-initialize RAM banks */ + s->status = 0; + s->cfg = 0x00800000; +} + +static void ppc4xx_sdram_ddr_realize(DeviceState *dev, Error **errp) +{ + Ppc4xxSdramDdrState *s = PPC4xx_SDRAM_DDR(dev); + Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); + const ram_addr_t valid_bank_sizes[] = { + 256 * MiB, 128 * MiB, 64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 4 * MiB, 0 + }; + int i; + + if (s->nbanks < 1 || s->nbanks > 4) { + error_setg(errp, "Invalid number of RAM banks"); + return; + } + if (!s->dram_mr) { + error_setg(errp, "Missing dram memory region"); + return; + } + if (!ppc4xx_sdram_banks(s->dram_mr, s->nbanks, s->bank, + valid_bank_sizes, errp)) { + return; + } + for (i = 0; i < s->nbanks; i++) { + if (s->bank[i].size) { + s->bank[i].bcr = sdram_ddr_bcr(s->bank[i].base, s->bank[i].size); + sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr, + s->bank[i].base, s->bank[i].size, 0); + } else { + sdram_bank_set_bcr(&s->bank[i], 0, 0, 0, 0); + } + trace_ppc4xx_sdram_init(sdram_ddr_base(s->bank[i].bcr), + sdram_ddr_size(s->bank[i].bcr), + s->bank[i].bcr); + } + + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + + ppc4xx_dcr_register(dcr, SDRAM0_CFGADDR, + s, &sdram_ddr_dcr_read, &sdram_ddr_dcr_write); + ppc4xx_dcr_register(dcr, SDRAM0_CFGDATA, + s, &sdram_ddr_dcr_read, &sdram_ddr_dcr_write); +} + +static Property ppc4xx_sdram_ddr_props[] = { + DEFINE_PROP_LINK("dram", Ppc4xxSdramDdrState, dram_mr, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdrState, nbanks, 4), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ppc4xx_sdram_ddr_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ppc4xx_sdram_ddr_realize; + dc->reset = ppc4xx_sdram_ddr_reset; + /* Reason: only works as function of a ppc4xx SoC */ + dc->user_creatable = false; + device_class_set_props(dc, ppc4xx_sdram_ddr_props); +} + +void ppc4xx_sdram_ddr_enable(Ppc4xxSdramDdrState *s) +{ + sdram_ddr_dcr_write(s, SDRAM0_CFGADDR, 0x20); + sdram_ddr_dcr_write(s, SDRAM0_CFGDATA, 0x80000000); +} + +/*****************************************************************************/ +/* DDR2 SDRAM controller */ +#define SDRAM_DDR2_BCR_MASK 0xffe0ffc1 + +enum { + SDRAM_R0BAS = 0x40, + SDRAM_R1BAS, + SDRAM_R2BAS, + SDRAM_R3BAS, + SDRAM_CONF1HB = 0x45, + SDRAM_PLBADDULL = 0x4a, + SDRAM_CONF1LL = 0x4b, + SDRAM_CONFPATHB = 0x4f, + SDRAM_PLBADDUHB = 0x50, +}; + +static uint32_t sdram_ddr2_bcr(hwaddr ram_base, hwaddr ram_size) +{ + uint32_t bcr; + + switch (ram_size) { + case 8 * MiB: + bcr = 0xffc0; + break; + case 16 * MiB: + bcr = 0xff80; + break; + case 32 * MiB: + bcr = 0xff00; + break; + case 64 * MiB: + bcr = 0xfe00; + break; + case 128 * MiB: + bcr = 0xfc00; + break; + case 256 * MiB: + bcr = 0xf800; + break; + case 512 * MiB: + bcr = 0xf000; + break; + case 1 * GiB: + bcr = 0xe000; + break; + case 2 * GiB: + bcr = 0xc000; + break; + case 4 * GiB: + bcr = 0x8000; + break; + default: + error_report("invalid RAM size " HWADDR_FMT_plx, ram_size); + return 0; + } + bcr |= ram_base >> 2 & 0xffe00000; + bcr |= 1; + + return bcr; +} + +static inline hwaddr sdram_ddr2_base(uint32_t bcr) +{ + return (bcr & 0xffe00000) << 2; +} + +static hwaddr sdram_ddr2_size(uint32_t bcr) +{ + int sh; + + sh = 1024 - ((bcr >> 6) & 0x3ff); + return 8 * MiB * sh; +} + +static uint32_t sdram_ddr2_dcr_read(void *opaque, int dcrn) +{ + Ppc4xxSdramDdr2State *s = opaque; + uint32_t ret = 0; + + switch (dcrn) { + case SDRAM_R0BAS: + case SDRAM_R1BAS: + case SDRAM_R2BAS: + case SDRAM_R3BAS: + if (s->bank[dcrn - SDRAM_R0BAS].size) { + ret = sdram_ddr2_bcr(s->bank[dcrn - SDRAM_R0BAS].base, + s->bank[dcrn - SDRAM_R0BAS].size); + } + break; + case SDRAM_CONF1HB: + case SDRAM_CONF1LL: + case SDRAM_CONFPATHB: + case SDRAM_PLBADDULL: + case SDRAM_PLBADDUHB: + break; + case SDRAM0_CFGADDR: + ret = s->addr; + break; + case SDRAM0_CFGDATA: + switch (s->addr) { + case 0x14: /* SDRAM_MCSTAT (405EX) */ + case 0x1F: + ret = 0x80000000; + break; + case 0x21: /* SDRAM_MCOPT2 */ + ret = s->mcopt2; + break; + case 0x40: /* SDRAM_MB0CF */ + ret = 0x00008001; + break; + case 0x7A: /* SDRAM_DLCR */ + ret = 0x02000000; + break; + case 0xE1: /* SDR0_DDR0 */ + ret = SDR0_DDR0_DDRM_ENCODE(1) | SDR0_DDR0_DDRM_DDR1; + break; + default: + break; + } + break; + default: + break; + } + + return ret; +} + +#define SDRAM_DDR2_MCOPT2_DCEN BIT(27) + +static void sdram_ddr2_dcr_write(void *opaque, int dcrn, uint32_t val) +{ + Ppc4xxSdramDdr2State *s = opaque; + int i; + + switch (dcrn) { + case SDRAM_R0BAS: + case SDRAM_R1BAS: + case SDRAM_R2BAS: + case SDRAM_R3BAS: + case SDRAM_CONF1HB: + case SDRAM_CONF1LL: + case SDRAM_CONFPATHB: + case SDRAM_PLBADDULL: + case SDRAM_PLBADDUHB: + break; + case SDRAM0_CFGADDR: + s->addr = val; + break; + case SDRAM0_CFGDATA: + switch (s->addr) { + case 0x00: /* B0CR */ + break; + case 0x21: /* SDRAM_MCOPT2 */ + if (!(s->mcopt2 & SDRAM_DDR2_MCOPT2_DCEN) && + (val & SDRAM_DDR2_MCOPT2_DCEN)) { + trace_ppc4xx_sdram_enable("enable"); + /* validate all RAM mappings */ + for (i = 0; i < s->nbanks; i++) { + if (s->bank[i].size) { + sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr, + s->bank[i].base, s->bank[i].size, + 1); + } + } + s->mcopt2 |= SDRAM_DDR2_MCOPT2_DCEN; + } else if ((s->mcopt2 & SDRAM_DDR2_MCOPT2_DCEN) && + !(val & SDRAM_DDR2_MCOPT2_DCEN)) { + trace_ppc4xx_sdram_enable("disable"); + /* invalidate all RAM mappings */ + for (i = 0; i < s->nbanks; i++) { + if (s->bank[i].size) { + sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr, + s->bank[i].base, s->bank[i].size, + 0); + } + } + s->mcopt2 &= ~SDRAM_DDR2_MCOPT2_DCEN; + } + break; + default: + break; + } + break; + default: + break; + } +} + +static void ppc4xx_sdram_ddr2_reset(DeviceState *dev) +{ + Ppc4xxSdramDdr2State *s = PPC4xx_SDRAM_DDR2(dev); + + s->addr = 0; + s->mcopt2 = 0; +} + +static void ppc4xx_sdram_ddr2_realize(DeviceState *dev, Error **errp) +{ + Ppc4xxSdramDdr2State *s = PPC4xx_SDRAM_DDR2(dev); + Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); + /* + * SoC also has 4 GiB but that causes problem with 32 bit + * builds (4*GiB overflows the 32 bit ram_addr_t). + */ + const ram_addr_t valid_bank_sizes[] = { + 2 * GiB, 1 * GiB, 512 * MiB, 256 * MiB, 128 * MiB, + 64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 0 + }; + int i; + + if (s->nbanks < 1 || s->nbanks > 4) { + error_setg(errp, "Invalid number of RAM banks"); + return; + } + if (!s->dram_mr) { + error_setg(errp, "Missing dram memory region"); + return; + } + if (!ppc4xx_sdram_banks(s->dram_mr, s->nbanks, s->bank, + valid_bank_sizes, errp)) { + return; + } + for (i = 0; i < s->nbanks; i++) { + if (s->bank[i].size) { + s->bank[i].bcr = sdram_ddr2_bcr(s->bank[i].base, s->bank[i].size); + s->bank[i].bcr &= SDRAM_DDR2_BCR_MASK; + sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr, + s->bank[i].base, s->bank[i].size, 0); + } else { + sdram_bank_set_bcr(&s->bank[i], 0, 0, 0, 0); + } + trace_ppc4xx_sdram_init(sdram_ddr2_base(s->bank[i].bcr), + sdram_ddr2_size(s->bank[i].bcr), + s->bank[i].bcr); + } + + ppc4xx_dcr_register(dcr, SDRAM0_CFGADDR, + s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); + ppc4xx_dcr_register(dcr, SDRAM0_CFGDATA, + s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); + + ppc4xx_dcr_register(dcr, SDRAM_R0BAS, + s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); + ppc4xx_dcr_register(dcr, SDRAM_R1BAS, + s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); + ppc4xx_dcr_register(dcr, SDRAM_R2BAS, + s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); + ppc4xx_dcr_register(dcr, SDRAM_R3BAS, + s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); + ppc4xx_dcr_register(dcr, SDRAM_CONF1HB, + s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); + ppc4xx_dcr_register(dcr, SDRAM_PLBADDULL, + s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); + ppc4xx_dcr_register(dcr, SDRAM_CONF1LL, + s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); + ppc4xx_dcr_register(dcr, SDRAM_CONFPATHB, + s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); + ppc4xx_dcr_register(dcr, SDRAM_PLBADDUHB, + s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); +} + +static Property ppc4xx_sdram_ddr2_props[] = { + DEFINE_PROP_LINK("dram", Ppc4xxSdramDdr2State, dram_mr, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdr2State, nbanks, 4), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ppc4xx_sdram_ddr2_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ppc4xx_sdram_ddr2_realize; + dc->reset = ppc4xx_sdram_ddr2_reset; + /* Reason: only works as function of a ppc4xx SoC */ + dc->user_creatable = false; + device_class_set_props(dc, ppc4xx_sdram_ddr2_props); +} + +void ppc4xx_sdram_ddr2_enable(Ppc4xxSdramDdr2State *s) +{ + sdram_ddr2_dcr_write(s, SDRAM0_CFGADDR, 0x21); + sdram_ddr2_dcr_write(s, SDRAM0_CFGDATA, 0x08000000); +} + +static const TypeInfo ppc4xx_sdram_types[] = { + { + .name = TYPE_PPC4xx_SDRAM_DDR, + .parent = TYPE_PPC4xx_DCR_DEVICE, + .instance_size = sizeof(Ppc4xxSdramDdrState), + .class_init = ppc4xx_sdram_ddr_class_init, + }, { + .name = TYPE_PPC4xx_SDRAM_DDR2, + .parent = TYPE_PPC4xx_DCR_DEVICE, + .instance_size = sizeof(Ppc4xxSdramDdr2State), + .class_init = ppc4xx_sdram_ddr2_class_init, + } +}; + +DEFINE_TYPES(ppc4xx_sdram_types) diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c index d57b199797..dfbe759481 100644 --- a/hw/ppc/ppce500_spin.c +++ b/hw/ppc/ppce500_spin.c @@ -83,13 +83,14 @@ static void mmubooke_create_initial_mapping(CPUPPCState *env, tlb->mas2 = (va & TARGET_PAGE_MASK) | MAS2_M; tlb->mas7_3 = pa & TARGET_PAGE_MASK; tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; +#ifdef CONFIG_KVM env->tlb_dirty = true; +#endif } static void spin_kick(CPUState *cs, run_on_cpu_data data) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); SpinInfo *curspin = data.host_ptr; hwaddr map_size = 64 * MiB; hwaddr map_start; diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index a1cd4505cc..4eb5477069 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -45,13 +45,11 @@ #include "trace.h" #include "elf.h" #include "qemu/units.h" -#include "kvm_ppc.h" +#include "audio/audio.h" /* SMP is not enabled, for now */ #define MAX_CPUS 1 -#define MAX_IDE_BUS 2 - #define CFG_ADDR 0xf0000510 #define KERNEL_LOAD_ADDR 0x01000000 @@ -70,6 +68,7 @@ static void ppc_prep_reset(void *opaque) PowerPCCPU *cpu = opaque; cpu_reset(CPU(cpu)); + cpu_ppc_tb_reset(&cpu->env); } @@ -214,14 +213,13 @@ static int PPC_NVRAM_set_params (Nvram *nvram, uint16_t NVRAM_size, static int prep_set_cmos_checksum(DeviceState *dev, void *opaque) { uint16_t checksum = *(uint16_t *)opaque; - ISADevice *rtc; if (object_dynamic_cast(OBJECT(dev), TYPE_MC146818_RTC)) { - rtc = ISA_DEVICE(dev); - rtc_set_memory(rtc, 0x2e, checksum & 0xff); - rtc_set_memory(rtc, 0x3e, checksum & 0xff); - rtc_set_memory(rtc, 0x2f, checksum >> 8); - rtc_set_memory(rtc, 0x3f, checksum >> 8); + MC146818RtcState *rtc = MC146818_RTC(dev); + mc146818rtc_set_cmos_data(rtc, 0x2e, checksum & 0xff); + mc146818rtc_set_cmos_data(rtc, 0x3e, checksum & 0xff); + mc146818rtc_set_cmos_data(rtc, 0x2f, checksum >> 8); + mc146818rtc_set_cmos_data(rtc, 0x3f, checksum >> 8); object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(rtc), "date"); @@ -232,6 +230,7 @@ static int prep_set_cmos_checksum(DeviceState *dev, void *opaque) static void ibm_40p_init(MachineState *machine) { const char *bios_name = machine->firmware ?: "openbios-ppc"; + MachineClass *mc = MACHINE_GET_CLASS(machine); CPUPPCState *env = NULL; uint16_t cmos_checksum; PowerPCCPU *cpu; @@ -242,11 +241,16 @@ static void ibm_40p_init(MachineState *machine) ISADevice *isa_dev; ISABus *isa_bus; void *fw_cfg; - int i; uint32_t kernel_base = 0, initrd_base = 0; long kernel_size = 0, initrd_size = 0; char boot_device; + if (kvm_enabled()) { + error_report("machine %s does not support the KVM accelerator", + MACHINE_GET_CLASS(machine)->name); + exit(EXIT_FAILURE); + } + /* init CPU */ cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); env = &cpu->env; @@ -273,9 +277,11 @@ static void ibm_40p_init(MachineState *machine) } /* PCI -> ISA bridge */ - i82378_dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(11, 0), "i82378")); + i82378_dev = DEVICE(pci_new(PCI_DEVFN(11, 0), "i82378")); + qdev_realize_and_unref(i82378_dev, BUS(pci_bus), &error_fatal); qdev_connect_gpio_out(i82378_dev, 0, - cpu->env.irq_inputs[PPC6xx_INPUT_INT]); + qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_INT)); + sysbus_connect_irq(pcihost, 0, qdev_get_gpio_in(i82378_dev, 15)); isa_bus = ISA_BUS(qdev_get_child_bus(i82378_dev, "isa.0")); @@ -304,6 +310,10 @@ static void ibm_40p_init(MachineState *machine) dev = DEVICE(isa_dev); qdev_prop_set_uint32(dev, "iobase", 0x830); qdev_prop_set_uint32(dev, "irq", 10); + + if (machine->audiodev) { + qdev_prop_set_string(dev, "audiodev", machine->audiodev); + } isa_realize_and_unref(isa_dev, isa_bus, &error_fatal); isa_dev = isa_new("pc87312"); @@ -325,10 +335,9 @@ static void ibm_40p_init(MachineState *machine) /* XXX: s3-trio at PCI_DEVFN(2, 0) */ pci_vga_init(pci_bus); - for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci_bus, "pcnet", - i == 0 ? "3" : NULL); - } + /* First PCNET device at PCI_DEVFN(3, 0) */ + pci_init_nic_in_slot(pci_bus, mc->default_nic, NULL, "3"); + pci_init_nic_devices(pci_bus, mc->default_nic); } /* Prepare firmware configuration for OpenBIOS */ @@ -392,18 +401,7 @@ static void ibm_40p_init(MachineState *machine) fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height); fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth); - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled()); - if (kvm_enabled()) { - uint8_t *hypercall; - - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq()); - hypercall = g_malloc(16); - kvmppc_get_hypercall(env, hypercall, 16); - fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16); - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid()); - } else { - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, NANOSECONDS_PER_SECOND); - } + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, NANOSECONDS_PER_SECOND); fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, boot_device); qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); @@ -430,6 +428,9 @@ static void ibm_40p_machine_init(MachineClass *mc) mc->default_boot_order = "c"; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("604"); mc->default_display = "std"; + mc->default_nic = "pcnet"; + + machine_add_audiodev_property(mc); } DEFINE_MACHINE("40p", ibm_40p_machine_init) diff --git a/hw/ppc/prep_systemio.c b/hw/ppc/prep_systemio.c index 8c9b8dd67b..4d3a251ed8 100644 --- a/hw/ppc/prep_systemio.c +++ b/hw/ppc/prep_systemio.c @@ -39,7 +39,7 @@ #define TYPE_PREP_SYSTEMIO "prep-systemio" OBJECT_DECLARE_SIMPLE_TYPE(PrepSystemIoState, PREP_SYSTEMIO) -/* Bit as defined in PowerPC Reference Plaform v1.1, sect. 6.1.5, p. 132 */ +/* Bit as defined in PowerPC Reference Platform v1.1, sect. 6.1.5, p. 132 */ #define PREP_BIT(n) (1 << (7 - (n))) struct PrepSystemIoState { @@ -262,7 +262,7 @@ static void prep_systemio_realize(DeviceState *dev, Error **errp) qemu_set_irq(s->non_contiguous_io_map_irq, s->iomap_type & PORT0850_IOMAP_NONCONTIGUOUS); cpu = POWERPC_CPU(first_cpu); - s->softreset_irq = cpu->env.irq_inputs[PPC6xx_INPUT_HRESET]; + s->softreset_irq = qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_HRESET); isa_register_portio_list(isa, &s->portio, 0x0, ppc_io800_port_list, s, "systemio800"); @@ -277,7 +277,7 @@ static const VMStateDescription vmstate_prep_systemio = { .name = "prep_systemio", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(sreset, PrepSystemIoState), VMSTATE_UINT8(system_control, PrepSystemIoState), VMSTATE_UINT8(iomap_type, PrepSystemIoState), diff --git a/hw/ppc/rs6000_mc.c b/hw/ppc/rs6000_mc.c index c0bc212e92..e6ec4b4c40 100644 --- a/hw/ppc/rs6000_mc.c +++ b/hw/ppc/rs6000_mc.c @@ -143,7 +143,6 @@ static void rs6000mc_realize(DeviceState *dev, Error **errp) RS6000MCState *s = RS6000MC(dev); int socket = 0; unsigned int ram_size = s->ram_size / MiB; - Error *local_err = NULL; while (socket < 6) { if (ram_size >= 64) { @@ -165,10 +164,8 @@ static void rs6000mc_realize(DeviceState *dev, Error **errp) if (s->simm_size[socket]) { char name[] = "simm.?"; name[5] = socket + '0'; - memory_region_init_ram(&s->simm[socket], OBJECT(dev), name, - s->simm_size[socket] * MiB, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!memory_region_init_ram(&s->simm[socket], OBJECT(dev), name, + s->simm_size[socket] * MiB, errp)) { return; } memory_region_add_subregion_overlap(get_system_memory(), 0, @@ -202,7 +199,7 @@ static const VMStateDescription vmstate_rs6000mc = { .name = "rs6000-mc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(port0820_index, RS6000MCState), VMSTATE_END_OF_LIST() }, diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 2f24598f55..d42b677898 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -25,7 +25,7 @@ #include "elf.h" #include "exec/memory.h" #include "ppc440.h" -#include "ppc405.h" +#include "hw/pci-host/ppc4xx.h" #include "hw/block/flash.h" #include "sysemu/sysemu.h" #include "sysemu/reset.h" @@ -33,6 +33,7 @@ #include "hw/char/serial.h" #include "hw/i2c/ppc4xx_i2c.h" #include "hw/i2c/smbus_eeprom.h" +#include "hw/ide/pci.h" #include "hw/usb/hcd-ehci.h" #include "hw/ppc/fdt.h" #include "hw/qdev-properties.h" @@ -46,6 +47,9 @@ /* dd bs=1 skip=$(($(stat -c '%s' updater/updater-460) - 0x80000)) \ if=updater/updater-460 of=u-boot-sam460-20100605.bin */ +#define PCIE0_DCRN_BASE 0x100 +#define PCIE1_DCRN_BASE 0x120 + /* from Sam460 U-Boot include/configs/Sam460ex.h */ #define FLASH_BASE 0xfff00000 #define FLASH_BASE_H 0x4 @@ -74,14 +78,6 @@ #define OPB_FREQ 115000000 #define EBC_FREQ 115000000 #define UART_FREQ 11059200 -#define SDRAM_NR_BANKS 4 - -/* The SoC could also handle 4 GiB but firmware does not work with that. */ -/* Maybe it overflows a signed 32 bit number somewhere? */ -static const ram_addr_t ppc460ex_sdram_bank_sizes[] = { - 2 * GiB, 1 * GiB, 512 * MiB, 256 * MiB, 128 * MiB, 64 * MiB, - 32 * MiB, 0 -}; struct boot_info { uint32_t dt_base; @@ -132,13 +128,12 @@ static int sam460ex_load_uboot(void) return 0; } -static int sam460ex_load_device_tree(hwaddr addr, - uint32_t ramsize, +static int sam460ex_load_device_tree(MachineState *machine, + hwaddr addr, hwaddr initrd_base, - hwaddr initrd_size, - const char *kernel_cmdline) + hwaddr initrd_size) { - uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(ramsize) }; + uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(machine->ram_size) }; char *filename; int fdt_size; void *fdt; @@ -172,7 +167,8 @@ static int sam460ex_load_device_tree(hwaddr addr, qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", (initrd_base + initrd_size)); - qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", + machine->kernel_cmdline); /* Copy data from the host device tree into the guest. Since the guest can * directly access the timebase without host involvement, we must expose @@ -209,7 +205,9 @@ static int sam460ex_load_device_tree(hwaddr addr, EBC_FREQ); rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); - g_free(fdt); + + /* Set machine->fdt for 'dumpdtb' QMP/HMP command */ + machine->fdt = fdt; return fdt_size; } @@ -273,16 +271,11 @@ static void main_cpu_reset(void *opaque) static void sam460ex_init(MachineState *machine) { - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *isa = g_new(MemoryRegion, 1); - MemoryRegion *ram_memories = g_new(MemoryRegion, SDRAM_NR_BANKS); - hwaddr ram_bases[SDRAM_NR_BANKS] = {0}; - hwaddr ram_sizes[SDRAM_NR_BANKS] = {0}; MemoryRegion *l2cache_ram = g_new(MemoryRegion, 1); DeviceState *uic[4]; - qemu_irq mal_irqs[4]; int i; PCIBus *pci_bus; + USBBus *usb_bus; PowerPCCPU *cpu; CPUPPCState *env; I2CBus *i2c; @@ -309,11 +302,12 @@ static void sam460ex_init(MachineState *machine) ppc_dcr_init(env, NULL, NULL); /* PLB arbitrer */ - ppc4xx_plb_init(env); + dev = qdev_new(TYPE_PPC4xx_PLB); + ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(dev), cpu, &error_fatal); + object_unref(OBJECT(dev)); /* interrupt controllers */ for (i = 0; i < ARRAY_SIZE(uic); i++) { - SysBusDevice *sbd; /* * UICs 1, 2 and 3 are cascaded through UIC 0. * input_ints[n] is the interrupt number on UIC 0 which @@ -325,43 +319,56 @@ static void sam460ex_init(MachineState *machine) const int input_ints[] = { -1, 30, 10, 16 }; uic[i] = qdev_new(TYPE_PPC_UIC); - sbd = SYS_BUS_DEVICE(uic[i]); - qdev_prop_set_uint32(uic[i], "dcr-base", 0xc0 + i * 0x10); - object_property_set_link(OBJECT(uic[i]), "cpu", OBJECT(cpu), - &error_fatal); - sysbus_realize_and_unref(sbd, &error_fatal); + ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(uic[i]), cpu, &error_fatal); + object_unref(OBJECT(uic[i])); + sbdev = SYS_BUS_DEVICE(uic[i]); if (i == 0) { - sysbus_connect_irq(sbd, PPCUIC_OUTPUT_INT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]); - sysbus_connect_irq(sbd, PPCUIC_OUTPUT_CINT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]); + sysbus_connect_irq(sbdev, PPCUIC_OUTPUT_INT, + qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT)); + sysbus_connect_irq(sbdev, PPCUIC_OUTPUT_CINT, + qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_CINT)); } else { - sysbus_connect_irq(sbd, PPCUIC_OUTPUT_INT, + sysbus_connect_irq(sbdev, PPCUIC_OUTPUT_INT, qdev_get_gpio_in(uic[0], input_ints[i])); - sysbus_connect_irq(sbd, PPCUIC_OUTPUT_CINT, + sysbus_connect_irq(sbdev, PPCUIC_OUTPUT_CINT, qdev_get_gpio_in(uic[0], input_ints[i] + 1)); } } /* SDRAM controller */ - /* put all RAM on first bank because board has one slot - * and firmware only checks that */ - ppc4xx_sdram_banks(machine->ram, 1, ram_memories, ram_bases, ram_sizes, - ppc460ex_sdram_bank_sizes); - + /* The SoC could also handle 4 GiB but firmware does not work with that. */ + if (machine->ram_size > 2 * GiB) { + error_report("Memory over 2 GiB is not supported"); + exit(1); + } + /* Firmware needs at least 64 MiB */ + if (machine->ram_size < 64 * MiB) { + error_report("Memory below 64 MiB is not supported"); + exit(1); + } + dev = qdev_new(TYPE_PPC4xx_SDRAM_DDR2); + object_property_set_link(OBJECT(dev), "dram", OBJECT(machine->ram), + &error_abort); + /* + * Put all RAM on first bank because board has one slot + * and firmware only checks that + */ + object_property_set_int(OBJECT(dev), "nbanks", 1, &error_abort); + ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(dev), cpu, &error_fatal); + object_unref(OBJECT(dev)); /* FIXME: does 460EX have ECC interrupts? */ - ppc440_sdram_init(env, SDRAM_NR_BANKS, ram_memories, - ram_bases, ram_sizes, 1); + /* Enable SDRAM memory regions as we may boot without firmware */ + ppc4xx_sdram_ddr2_enable(PPC4xx_SDRAM_DDR2(dev)); /* IIC controllers and devices */ dev = sysbus_create_simple(TYPE_PPC4xx_I2C, 0x4ef600700, qdev_get_gpio_in(uic[0], 2)); i2c = PPC4xx_I2C(dev)->bus; /* SPD EEPROM on RAM module */ - spd_data = spd_data_generate(ram_sizes[0] < 128 * MiB ? DDR : DDR2, - ram_sizes[0]); + spd_data = spd_data_generate(machine->ram_size < 128 * MiB ? DDR : DDR2, + machine->ram_size); spd_data[20] = 4; /* SO-DIMM module */ smbus_eeprom_init_one(i2c, 0x50, spd_data); /* RTC */ @@ -371,7 +378,9 @@ static void sam460ex_init(MachineState *machine) qdev_get_gpio_in(uic[0], 3)); /* External bus controller */ - ppc405_ebc_init(env); + dev = qdev_new(TYPE_PPC4xx_EBC); + ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(dev), cpu, &error_fatal); + object_unref(OBJECT(dev)); /* CPR */ ppc4xx_cpr_init(env); @@ -383,10 +392,15 @@ static void sam460ex_init(MachineState *machine) ppc4xx_sdr_init(env); /* MAL */ - for (i = 0; i < ARRAY_SIZE(mal_irqs); i++) { - mal_irqs[0] = qdev_get_gpio_in(uic[2], 3 + i); + dev = qdev_new(TYPE_PPC4xx_MAL); + qdev_prop_set_uint8(dev, "txc-num", 4); + qdev_prop_set_uint8(dev, "rxc-num", 16); + ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(dev), cpu, &error_fatal); + object_unref(OBJECT(dev)); + sbdev = SYS_BUS_DEVICE(dev); + for (i = 0; i < ARRAY_SIZE(PPC4xx_MAL(dev)->irqs); i++) { + sysbus_connect_irq(sbdev, i, qdev_get_gpio_in(uic[2], 3 + i)); } - ppc4xx_mal_init(env, 4, 16, mal_irqs); /* DMA */ ppc4xx_dma_init(env, 0x200); @@ -396,7 +410,8 @@ static void sam460ex_init(MachineState *machine) /* FIXME: remove this after fixing l2sram mapping in ppc440_uc.c? */ memory_region_init_ram(l2cache_ram, NULL, "ppc440.l2cache_ram", 256 * KiB, &error_abort); - memory_region_add_subregion(address_space_mem, 0x400000000LL, l2cache_ram); + memory_region_add_subregion(get_system_memory(), 0x400000000LL, + l2cache_ram); /* USB */ sysbus_create_simple(TYPE_PPC4xx_EHCI, 0x4bffd0400, @@ -408,39 +423,62 @@ static void sam460ex_init(MachineState *machine) sysbus_realize_and_unref(sbdev, &error_fatal); sysbus_mmio_map(sbdev, 0, 0x4bffd0000); sysbus_connect_irq(sbdev, 0, qdev_get_gpio_in(uic[2], 30)); - usb_create_simple(usb_bus_find(-1), "usb-kbd"); - usb_create_simple(usb_bus_find(-1), "usb-mouse"); + usb_bus = USB_BUS(object_resolve_type_unambiguous(TYPE_USB_BUS, + &error_abort)); + usb_create_simple(usb_bus, "usb-kbd"); + usb_create_simple(usb_bus, "usb-mouse"); + + /* PCIe buses */ + dev = qdev_new(TYPE_PPC460EX_PCIE_HOST); + qdev_prop_set_int32(dev, "busnum", 0); + qdev_prop_set_int32(dev, "dcrn-base", PCIE0_DCRN_BASE); + object_property_set_link(OBJECT(dev), "cpu", OBJECT(cpu), &error_abort); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + dev = qdev_new(TYPE_PPC460EX_PCIE_HOST); + qdev_prop_set_int32(dev, "busnum", 1); + qdev_prop_set_int32(dev, "dcrn-base", PCIE1_DCRN_BASE); + object_property_set_link(OBJECT(dev), "cpu", OBJECT(cpu), &error_abort); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); /* PCI bus */ - ppc460ex_pcie_init(env); /* All PCI irqs are connected to the same UIC pin (cf. UBoot source) */ - dev = sysbus_create_simple("ppc440-pcix-host", 0xc0ec00000, + dev = sysbus_create_simple(TYPE_PPC440_PCIX_HOST, 0xc0ec00000, qdev_get_gpio_in(uic[1], 0)); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, 0xc08000000); pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0")); - memory_region_init_alias(isa, NULL, "isa_mmio", get_system_io(), - 0, 0x10000); - memory_region_add_subregion(get_system_memory(), 0xc08000000, isa); - /* PCI devices */ pci_create_simple(pci_bus, PCI_DEVFN(6, 0), "sm501"); - /* SoC has a single SATA port but we don't emulate that yet + /* + * SoC has a single SATA port but we don't emulate that * However, firmware and usual clients have driver for SiI311x - * so add one for convenience by default */ + * PCI SATA card so add one for convenience by default + */ if (defaults_enabled()) { - pci_create_simple(pci_bus, -1, "sii3112"); + PCIIDEState *s = PCI_IDE(pci_create_simple(pci_bus, -1, "sii3112")); + DriveInfo *di; + + di = drive_get_by_index(IF_IDE, 0); + if (di) { + ide_bus_create_drive(&s->bus[0], 0, di); + } + /* Use index 2 only if 1 does not exist, this allows -cdrom */ + di = drive_get_by_index(IF_IDE, 1) ?: drive_get_by_index(IF_IDE, 2); + if (di) { + ide_bus_create_drive(&s->bus[1], 0, di); + } } - /* SoC has 4 UARTs - * but board has only one wired and two are present in fdt */ + /* SoC has 4 UARTs but board has only one wired and two described in fdt */ if (serial_hd(0) != NULL) { - serial_mm_init(address_space_mem, 0x4ef600300, 0, + serial_mm_init(get_system_memory(), 0x4ef600300, 0, qdev_get_gpio_in(uic[1], 1), PPC_SERIAL_MM_BAUDBASE, serial_hd(0), DEVICE_BIG_ENDIAN); } if (serial_hd(1) != NULL) { - serial_mm_init(address_space_mem, 0x4ef600400, 0, + serial_mm_init(get_system_memory(), 0x4ef600400, 0, qdev_get_gpio_in(uic[0], 1), PPC_SERIAL_MM_BAUDBASE, serial_hd(1), DEVICE_BIG_ENDIAN); @@ -492,9 +530,8 @@ static void sam460ex_init(MachineState *machine) if (machine->kernel_filename) { int dt_size; - dt_size = sam460ex_load_device_tree(FDT_ADDR, machine->ram_size, - RAMDISK_ADDR, initrd_size, - machine->kernel_cmdline); + dt_size = sam460ex_load_device_tree(machine, FDT_ADDR, + RAMDISK_ADDR, initrd_size); boot_info->dt_base = FDT_ADDR; boot_info->dt_size = dt_size; @@ -507,6 +544,7 @@ static void sam460ex_machine_init(MachineClass *mc) { mc->desc = "aCube Sam460ex"; mc->init = sam460ex_init; + mc->block_default_type = IF_IDE; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("460exb"); mc->default_ram_size = 512 * MiB; mc->default_ram_id = "ppc4xx.sdram"; diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index fd4942e881..e9bc97fee0 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -27,6 +27,7 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" #include "qemu/memalign.h" +#include "qemu/guest-random.h" #include "qapi/error.h" #include "qapi/qapi-events-machine.h" #include "qapi/qapi-events-qdev.h" @@ -34,6 +35,7 @@ #include "sysemu/sysemu.h" #include "sysemu/hostmem.h" #include "sysemu/numa.h" +#include "sysemu/tcg.h" #include "sysemu/qtest.h" #include "sysemu/reset.h" #include "sysemu/runstate.h" @@ -60,7 +62,9 @@ #include "hw/ppc/fdt.h" #include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_nested.h" #include "hw/ppc/spapr_vio.h" +#include "hw/ppc/vof.h" #include "hw/qdev-properties.h" #include "hw/pci-host/spapr.h" #include "hw/pci/msi.h" @@ -140,11 +144,16 @@ static bool pre_2_10_vmstate_dummy_icp_needed(void *opaque) } static const VMStateDescription pre_2_10_vmstate_dummy_icp = { + /* + * Hack ahead. We can't have two devices with the same name and + * instance id. So I rename this to pass make check. + * Real help from people who knows the hardware is needed. + */ .name = "icp/server", .version_id = 1, .minimum_version_id = 1, .needed = pre_2_10_vmstate_dummy_icp_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UNUSED(4), /* uint32_t xirr */ VMSTATE_UNUSED(1), /* uint8_t pending_priority */ VMSTATE_UNUSED(1), /* uint8_t mfrr */ @@ -152,16 +161,32 @@ static const VMStateDescription pre_2_10_vmstate_dummy_icp = { }, }; +/* + * See comment in hw/intc/xics.c:icp_realize() + * + * You have to remove vmstate_replace_hack_for_ppc() when you remove + * the machine types that need the following function. + */ static void pre_2_10_vmstate_register_dummy_icp(int i) { vmstate_register(NULL, i, &pre_2_10_vmstate_dummy_icp, (void *)(uintptr_t) i); } +/* + * See comment in hw/intc/xics.c:icp_realize() + * + * You have to remove vmstate_replace_hack_for_ppc() when you remove + * the machine types that need the following function. + */ static void pre_2_10_vmstate_unregister_dummy_icp(int i) { - vmstate_unregister(NULL, &pre_2_10_vmstate_dummy_icp, - (void *)(uintptr_t) i); + /* + * This used to be: + * + * vmstate_unregister(NULL, &pre_2_10_vmstate_dummy_icp, + * (void *)(uintptr_t) i); + */ } int spapr_max_server_number(SpaprMachineState *spapr) @@ -176,8 +201,8 @@ static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, int smt_threads) { int i, ret = 0; - uint32_t servers_prop[smt_threads]; - uint32_t gservers_prop[smt_threads * 2]; + g_autofree uint32_t *servers_prop = g_new(uint32_t, smt_threads); + g_autofree uint32_t *gservers_prop = g_new(uint32_t, smt_threads * 2); int index = spapr_get_vcpu_id(cpu); if (cpu->compat_pvr) { @@ -195,12 +220,12 @@ static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, gservers_prop[i*2 + 1] = 0; } ret = fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", - servers_prop, sizeof(servers_prop)); + servers_prop, sizeof(*servers_prop) * smt_threads); if (ret < 0) { return ret; } ret = fdt_setprop(fdt, offset, "ibm,ppc-interrupt-gserver#s", - gservers_prop, sizeof(gservers_prop)); + gservers_prop, sizeof(*gservers_prop) * smt_threads * 2); return ret; } @@ -209,29 +234,40 @@ static void spapr_dt_pa_features(SpaprMachineState *spapr, PowerPCCPU *cpu, void *fdt, int offset) { + /* + * SSO (SAO) ordering is supported on KVM and thread=single hosts, + * but not MTTCG, so disable it. To advertise it, a cap would have + * to be added, or support implemented for MTTCG. + * + * Copy/paste is not supported by TCG, so it is not advertised. KVM + * can execute them but it has no accelerator drivers which are usable, + * so there isn't much need for it anyway. + */ + + /* These should be kept in sync with pnv */ uint8_t pa_features_206[] = { 6, 0, - 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 }; + 0xf6, 0x1f, 0xc7, 0x00, 0x00, 0xc0 }; uint8_t pa_features_207[] = { 24, 0, - 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, + 0xf6, 0x1f, 0xc7, 0xc0, 0x00, 0xf0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00 }; uint8_t pa_features_300[] = { 66, 0, /* 0: MMU|FPU|SLB|RUN|DABR|NX, 1: fri[nzpm]|DABRX|SPRG3|SLB0|PP110 */ - /* 2: VPM|DS205|PPR|DS202|DS206, 3: LSD|URG, SSO, 5: LE|CFAR|EB|LSQ */ - 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, /* 0 - 5 */ + /* 2: VPM|DS205|PPR|DS202|DS206, 3: LSD|URG, 5: LE|CFAR|EB|LSQ */ + 0xf6, 0x1f, 0xc7, 0xc0, 0x00, 0xf0, /* 0 - 5 */ /* 6: DS207 */ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */ /* 16: Vector */ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */ - /* 18: Vec. Scalar, 20: Vec. XOR, 22: HTM */ + /* 18: Vec. Scalar, 20: Vec. XOR */ 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 18 - 23 */ /* 24: Ext. Dec, 26: 64 bit ftrs, 28: PM ftrs */ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 24 - 29 */ - /* 30: MMR, 32: LE atomic, 34: EBB + ext EBB */ - 0x80, 0x00, 0x80, 0x00, 0xC0, 0x00, /* 30 - 35 */ - /* 36: SPR SO, 38: Copy/Paste, 40: Radix MMU */ - 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 36 - 41 */ + /* 32: LE atomic, 34: EBB + ext EBB */ + 0x00, 0x00, 0x80, 0x00, 0xC0, 0x00, /* 30 - 35 */ + /* 40: Radix MMU */ + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 36 - 41 */ /* 42: PM, 44: PC RA, 46: SC vec'd */ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 42 - 47 */ /* 48: SIMD, 50: QP BFP, 52: String */ @@ -241,6 +277,36 @@ static void spapr_dt_pa_features(SpaprMachineState *spapr, /* 60: NM atomic, 62: RNG */ 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 60 - 65 */ }; + /* 3.1 removes SAO, HTM support */ + uint8_t pa_features_31[] = { 74, 0, + /* 0: MMU|FPU|SLB|RUN|DABR|NX, 1: fri[nzpm]|DABRX|SPRG3|SLB0|PP110 */ + /* 2: VPM|DS205|PPR|DS202|DS206, 3: LSD|URG, 5: LE|CFAR|EB|LSQ */ + 0xf6, 0x1f, 0xc7, 0xc0, 0x00, 0xf0, /* 0 - 5 */ + /* 6: DS207 */ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */ + /* 16: Vector */ + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */ + /* 18: Vec. Scalar, 20: Vec. XOR */ + 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 18 - 23 */ + /* 24: Ext. Dec, 26: 64 bit ftrs, 28: PM ftrs */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 24 - 29 */ + /* 32: LE atomic, 34: EBB + ext EBB */ + 0x00, 0x00, 0x80, 0x00, 0xC0, 0x00, /* 30 - 35 */ + /* 40: Radix MMU */ + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 36 - 41 */ + /* 42: PM, 44: PC RA, 46: SC vec'd */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 42 - 47 */ + /* 48: SIMD, 50: QP BFP, 52: String */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 48 - 53 */ + /* 54: DecFP, 56: DecI, 58: SHA */ + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 54 - 59 */ + /* 60: NM atomic, 62: RNG */ + 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 60 - 65 */ + /* 68: DEXCR[SBHE|IBRTPDUS|SRAPD|NPHIE|PHIE] */ + 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, /* 66 - 71 */ + /* 72: [P]HASHST/[P]HASHCHK */ + 0x80, 0x00, /* 72 - 73 */ + }; uint8_t *pa_features = NULL; size_t pa_size; @@ -256,6 +322,10 @@ static void spapr_dt_pa_features(SpaprMachineState *spapr, pa_features = pa_features_300; pa_size = sizeof(pa_features_300); } + if (ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_10, 0, cpu->compat_pvr)) { + pa_features = pa_features_31; + pa_size = sizeof(pa_features_31); + } if (!pa_features) { return; } @@ -544,10 +614,8 @@ static int spapr_dt_dynamic_reconfiguration_memory(SpaprMachineState *spapr, cpu_to_be32(lmb_size & 0xffffffff)}; MemoryDeviceInfoList *dimms = NULL; - /* - * Don't create the node if there is no device memory - */ - if (machine->ram_size == machine->maxram_size) { + /* Don't create the node if there is no device memory. */ + if (!machine->device_memory) { return 0; } @@ -779,6 +847,26 @@ static void spapr_dt_cpu(CPUState *cs, void *fdt, int offset, pcc->lrg_decr_bits))); } +static void spapr_dt_one_cpu(void *fdt, SpaprMachineState *spapr, CPUState *cs, + int cpus_offset) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + int index = spapr_get_vcpu_id(cpu); + DeviceClass *dc = DEVICE_GET_CLASS(cs); + g_autofree char *nodename = NULL; + int offset; + + if (!spapr_is_thread0_in_vcore(spapr, cpu)) { + return; + } + + nodename = g_strdup_printf("%s@%x", dc->fw_name, index); + offset = fdt_add_subnode(fdt, cpus_offset, nodename); + _FDT(offset); + spapr_dt_cpu(cs, fdt, offset, spapr); +} + + static void spapr_dt_cpus(void *fdt, SpaprMachineState *spapr) { CPUState **rev; @@ -808,21 +896,7 @@ static void spapr_dt_cpus(void *fdt, SpaprMachineState *spapr) } for (i = n_cpus - 1; i >= 0; i--) { - CPUState *cs = rev[i]; - PowerPCCPU *cpu = POWERPC_CPU(cs); - int index = spapr_get_vcpu_id(cpu); - DeviceClass *dc = DEVICE_GET_CLASS(cs); - g_autofree char *nodename = NULL; - int offset; - - if (!spapr_is_thread0_in_vcore(spapr, cpu)) { - continue; - } - - nodename = g_strdup_printf("%s@%x", dc->fw_name, index); - offset = fdt_add_subnode(fdt, cpus_offset, nodename); - _FDT(offset); - spapr_dt_cpu(cs, fdt, offset, spapr); + spapr_dt_one_cpu(fdt, spapr, rev[i], cpus_offset); } g_free(rev); @@ -857,16 +931,23 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) int rtas; GString *hypertas = g_string_sized_new(256); GString *qemu_hypertas = g_string_sized_new(256); - uint64_t max_device_addr = MACHINE(spapr)->device_memory->base + - memory_region_size(&MACHINE(spapr)->device_memory->mr); uint32_t lrdr_capacity[] = { - cpu_to_be32(max_device_addr >> 32), - cpu_to_be32(max_device_addr & 0xffffffff), + 0, + 0, cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE >> 32), cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE & 0xffffffff), cpu_to_be32(ms->smp.max_cpus / ms->smp.threads), }; + /* Do we have device memory? */ + if (MACHINE(spapr)->device_memory) { + uint64_t max_device_addr = MACHINE(spapr)->device_memory->base + + memory_region_size(&MACHINE(spapr)->device_memory->mr); + + lrdr_capacity[0] = cpu_to_be32(max_device_addr >> 32); + lrdr_capacity[1] = cpu_to_be32(max_device_addr & 0xffffffff); + } + _FDT(rtas = fdt_add_subnode(fdt, 0, "rtas")); /* hypertas */ @@ -898,6 +979,8 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) add_str(hypertas, "hcall-hpt-resize"); } + add_str(hypertas, "hcall-watchdog"); + _FDT(fdt_setprop(fdt, rtas, "ibm,hypertas-functions", hypertas->str, hypertas->len)); g_string_free(hypertas, TRUE); @@ -1089,6 +1172,8 @@ static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt, bool reset) spapr_dt_ov5_platform_support(spapr, fdt, chosen); } + _FDT(fdt_setprop(fdt, chosen, "rng-seed", spapr->fdt_rng_seed, 32)); + _FDT(spapr_dt_ovec(fdt, chosen, spapr->ov5_cas, "ibm,architecture-vec-5")); } @@ -1107,7 +1192,7 @@ static void spapr_dt_hypervisor(SpaprMachineState *spapr, void *fdt) * Older KVM versions with older guest kernels were broken * with the magic page, don't allow the guest to map it. */ - if (!kvmppc_get_hypercall(first_cpu->env_ptr, hypercall, + if (!kvmppc_get_hypercall(cpu_env(first_cpu), hypercall, sizeof(hypercall))) { _FDT(fdt_setprop(fdt, hypervisor, "hcall-instructions", hypercall, sizeof(hypercall))); @@ -1265,7 +1350,7 @@ static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp, CPUPPCState *env = &cpu->env; /* The TCG path should also be holding the BQL at this point */ - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); g_assert(!vhyp_cpu_in_nested(cpu)); @@ -1308,6 +1393,21 @@ void spapr_set_all_lpcrs(target_ulong value, target_ulong mask) } } +/* May be used when the machine is not running */ +void spapr_init_all_lpcrs(target_ulong value, target_ulong mask) +{ + CPUState *cs; + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + target_ulong lpcr; + + lpcr = env->spr[SPR_LPCR]; + lpcr &= ~(LPCR_HR | LPCR_UPRT); + ppc_store_lpcr(cpu, lpcr); + } +} + static bool spapr_get_pate(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu, target_ulong lpid, ppc_v3_pate_t *entry) { @@ -1320,28 +1420,16 @@ static bool spapr_get_pate(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu, /* Copy PATE1:GR into PATE0:HR */ entry->dw0 = spapr->patb_entry & PATE0_HR; entry->dw1 = spapr->patb_entry; - + return true; } else { - uint64_t patb, pats; - - assert(lpid != 0); - - patb = spapr->nested_ptcr & PTCR_PATB; - pats = spapr->nested_ptcr & PTCR_PATS; - - /* Calculate number of entries */ - pats = 1ull << (pats + 12 - 4); - if (pats <= lpid) { - return false; + if (spapr_nested_api(spapr) == NESTED_API_KVM_HV) { + return spapr_get_pate_nested_hv(spapr, cpu, lpid, entry); + } else if (spapr_nested_api(spapr) == NESTED_API_PAPR) { + return spapr_get_pate_nested_papr(spapr, cpu, lpid, entry); + } else { + g_assert_not_reached(); } - - /* Grab entry */ - patb += 16 * lpid; - entry->dw0 = ldq_phys(CPU(cpu)->as, patb); - entry->dw1 = ldq_phys(CPU(cpu)->as, patb + 8); } - - return true; } #define HPTE(_table, _i) (void *)(((uint64_t *)(_table)) + ((_i) * 2)) @@ -1510,7 +1598,7 @@ int spapr_hpt_shift_for_ramsize(uint64_t ramsize) void spapr_free_hpt(SpaprMachineState *spapr) { - g_free(spapr->htab); + qemu_vfree(spapr->htab); spapr->htab = NULL; spapr->htab_shift = 0; close_htab_fd(spapr); @@ -1564,7 +1652,7 @@ int spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp) } /* We're setting up a hash table, so that means we're not radix */ spapr->patb_entry = 0; - spapr_set_all_lpcrs(0, LPCR_HR | LPCR_UPRT); + spapr_init_all_lpcrs(0, LPCR_HR | LPCR_UPRT); return 0; } @@ -1611,7 +1699,7 @@ void spapr_check_mmu_mode(bool guest_radix) } } -static void spapr_machine_reset(MachineState *machine) +static void spapr_machine_reset(MachineState *machine, ShutdownCause reason) { SpaprMachineState *spapr = SPAPR_MACHINE(machine); PowerPCCPU *first_ppc_cpu; @@ -1619,8 +1707,17 @@ static void spapr_machine_reset(MachineState *machine) void *fdt; int rc; + if (reason != SHUTDOWN_CAUSE_SNAPSHOT_LOAD) { + /* + * Record-replay snapshot load must not consume random, this was + * already replayed from initial machine reset. + */ + qemu_guest_getrandom_nofail(spapr->fdt_rng_seed, 32); + } + pef_kvm_reset(machine->cgs, &error_fatal); spapr_caps_apply(spapr); + spapr_nested_reset(spapr); first_ppc_cpu = POWERPC_CPU(first_cpu); if (kvm_enabled() && kvmppc_has_cap_mmu_radix() && @@ -1637,12 +1734,12 @@ static void spapr_machine_reset(MachineState *machine) spapr_setup_hpt(spapr); } - qemu_devices_reset(); + qemu_devices_reset(reason); spapr_ovec_cleanup(spapr->ov5_cas); spapr->ov5_cas = spapr_ovec_new(); - ppc_set_compat_all(spapr->max_compat_pvr, &error_fatal); + ppc_init_compat_all(spapr->max_compat_pvr, &error_fatal); /* * This is fixing some of the default configuration of the XIVE @@ -1701,6 +1798,9 @@ static void spapr_machine_reset(MachineState *machine) spapr->fdt_initial_size = spapr->fdt_size; spapr->fdt_blob = fdt; + /* Set machine->fdt for 'dumpdtb' QMP/HMP command */ + machine->fdt = fdt; + /* Set up the entry state */ first_ppc_cpu->env.gpr[5] = 0; @@ -1711,7 +1811,7 @@ static void spapr_machine_reset(MachineState *machine) /* Signal all vCPUs waiting on this condition */ qemu_cond_broadcast(&spapr->fwnmi_machine_check_interlock_cond); - migrate_del_blocker(spapr->fwnmi_migration_blocker); + migrate_del_blocker(&spapr->fwnmi_migration_blocker); } static void spapr_create_nvram(SpaprMachineState *spapr) @@ -1848,7 +1948,7 @@ static const VMStateDescription vmstate_spapr_event_entry = { .name = "spapr_event_log_entry", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(summary, SpaprEventLogEntry), VMSTATE_UINT32(extended_length, SpaprEventLogEntry), VMSTATE_VBUFFER_ALLOC_UINT32(extended_log, SpaprEventLogEntry, 0, @@ -1862,7 +1962,7 @@ static const VMStateDescription vmstate_spapr_pending_events = { .version_id = 1, .minimum_version_id = 1, .needed = spapr_pending_events_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_QTAILQ_V(pending_events, SpaprMachineState, 1, vmstate_spapr_event_entry, SpaprEventLogEntry, next), VMSTATE_END_OF_LIST() @@ -1918,7 +2018,7 @@ static const VMStateDescription vmstate_spapr_ov5_cas = { .version_id = 1, .minimum_version_id = 1, .needed = spapr_ov5_cas_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_POINTER_V(ov5_cas, SpaprMachineState, 1, vmstate_spapr_ovec, SpaprOptionVector), VMSTATE_END_OF_LIST() @@ -1937,7 +2037,7 @@ static const VMStateDescription vmstate_spapr_patb_entry = { .version_id = 1, .minimum_version_id = 1, .needed = spapr_patb_entry_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(patb_entry, SpaprMachineState), VMSTATE_END_OF_LIST() }, @@ -1955,7 +2055,7 @@ static const VMStateDescription vmstate_spapr_irq_map = { .version_id = 1, .minimum_version_id = 1, .needed = spapr_irq_map_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BITMAP(irq_map, SpaprMachineState, 0, irq_map_nr), VMSTATE_END_OF_LIST() }, @@ -1985,7 +2085,7 @@ static const VMStateDescription vmstate_spapr_dtb = { .minimum_version_id = 1, .needed = spapr_dtb_needed, .pre_load = spapr_dtb_pre_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(fdt_initial_size, SpaprMachineState), VMSTATE_UINT32(fdt_size, SpaprMachineState), VMSTATE_VBUFFER_ALLOC_UINT32(fdt_blob, SpaprMachineState, 0, NULL, @@ -2023,7 +2123,7 @@ static const VMStateDescription vmstate_spapr_fwnmi = { .minimum_version_id = 1, .needed = spapr_fwnmi_needed, .pre_save = spapr_fwnmi_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(fwnmi_system_reset_addr, SpaprMachineState), VMSTATE_UINT64(fwnmi_machine_check_addr, SpaprMachineState), VMSTATE_INT32(fwnmi_machine_check_interlock, SpaprMachineState), @@ -2038,7 +2138,7 @@ static const VMStateDescription vmstate_spapr = { .pre_load = spapr_pre_load, .post_load = spapr_post_load, .pre_save = spapr_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { /* used to be @next_irq */ VMSTATE_UNUSED_BUFFER(version_before_3, 0, 4), @@ -2048,7 +2148,7 @@ static const VMStateDescription vmstate_spapr = { VMSTATE_PPC_TIMEBASE_V(tb, SpaprMachineState, 2), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_spapr_ov5_cas, &vmstate_spapr_patb_entry, &vmstate_spapr_pending_events, @@ -2067,6 +2167,7 @@ static const VMStateDescription vmstate_spapr = { &vmstate_spapr_cap_fwnmi, &vmstate_spapr_fwnmi, &vmstate_spapr_cap_rpt_invalidate, + &vmstate_spapr_cap_nested_papr, NULL } }; @@ -2150,7 +2251,7 @@ static void htab_save_first_pass(QEMUFile *f, SpaprMachineState *spapr, break; } } - } while ((index < htabslots) && !qemu_file_rate_limit(f)); + } while ((index < htabslots) && !migration_rate_exceeded(f)); if (index >= htabslots) { assert(index == htabslots); @@ -2221,7 +2322,7 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, assert(index == htabslots); index = 0; } - } while ((examined < htabslots) && (!qemu_file_rate_limit(f) || final)); + } while ((examined < htabslots) && (!migration_rate_exceeded(f) || final)); if (index >= htabslots) { assert(index == htabslots); @@ -2438,6 +2539,7 @@ static void spapr_create_lmb_dr_connectors(SpaprMachineState *spapr) uint32_t nr_lmbs = (machine->maxram_size - machine->ram_size)/lmb_size; int i; + g_assert(!nr_lmbs || machine->device_memory); for (i = 0; i < nr_lmbs; i++) { uint64_t addr; @@ -2508,10 +2610,19 @@ static void spapr_set_vsmt_mode(SpaprMachineState *spapr, Error **errp) int ret; unsigned int smp_threads = ms->smp.threads; - if (!kvm_enabled() && (smp_threads > 1)) { - error_setg(errp, "TCG cannot support more than 1 thread/core " - "on a pseries machine"); - return; + if (tcg_enabled()) { + if (smp_threads > 1 && + !ppc_type_check_compat(ms->cpu_type, CPU_POWERPC_LOGICAL_2_07, 0, + spapr->max_compat_pvr)) { + error_setg(errp, "TCG only supports SMT on POWER8 or newer CPUs"); + return; + } + + if (smp_threads > 8) { + error_setg(errp, "TCG cannot support more than 8 threads/core " + "on a pseries machine"); + return; + } } if (!is_power_of_2(smp_threads)) { error_setg(errp, "Cannot support %d threads/core on a pseries " @@ -2519,7 +2630,7 @@ static void spapr_set_vsmt_mode(SpaprMachineState *spapr, Error **errp) return; } - /* Detemine the VSMT mode to use: */ + /* Determine the VSMT mode to use: */ if (vsmt_user) { if (spapr->vsmt < smp_threads) { error_setg(errp, "Cannot support VSMT mode %d" @@ -2605,8 +2716,6 @@ static void spapr_init_cpus(SpaprMachineState *spapr) } if (smc->pre_2_10_has_unused_icps) { - int i; - for (i = 0; i < spapr_max_server_number(spapr); i++) { /* Dummy entries get deregistered when real ICPState objects * are registered during CPU core hotplug. @@ -2717,6 +2826,7 @@ static void spapr_machine_init(MachineState *machine) MemoryRegion *sysmem = get_system_memory(); long load_limit, fw_size; Error *resize_hpt_err = NULL; + NICInfo *nd; if (!filename) { error_report("Could not find LPAR firmware '%s'", bios_name); @@ -2821,8 +2931,6 @@ static void spapr_machine_init(MachineState *machine) /* init CPUs */ spapr_init_cpus(spapr); - spapr->gpu_numa_id = spapr_numa_initial_nvgpu_numa_id(machine); - /* Init numa_assoc_array */ spapr_numa_associativity_init(spapr, machine); @@ -2850,12 +2958,11 @@ static void spapr_machine_init(MachineState *machine) /* 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)); - /* initialize hotplug memory address space */ if (machine->ram_size < machine->maxram_size) { ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size; + hwaddr device_mem_base; + /* * Limit the number of hotpluggable memory slots to half the number * slots that KVM supports, leaving the other half for PCI and other @@ -2874,25 +2981,14 @@ static void spapr_machine_init(MachineState *machine) exit(1); } - machine->device_memory->base = ROUND_UP(machine->ram_size, - SPAPR_DEVICE_MEM_ALIGN); - memory_region_init(&machine->device_memory->mr, OBJECT(spapr), - "device-memory", device_mem_size); - memory_region_add_subregion(sysmem, machine->device_memory->base, - &machine->device_memory->mr); + device_mem_base = ROUND_UP(machine->ram_size, SPAPR_DEVICE_MEM_ALIGN); + machine_memory_devices_init(machine, device_mem_base, device_mem_size); } if (smc->dr_lmb_enabled) { 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); } @@ -2931,21 +3027,12 @@ static void spapr_machine_init(MachineState *machine) phb = spapr_create_default_phb(); - for (i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - - if (!nd->model) { - nd->model = g_strdup("spapr-vlan"); - } - - if (g_str_equal(nd->model, "spapr-vlan") || - g_str_equal(nd->model, "ibmveth")) { - spapr_vlan_create(spapr->vio_bus, nd); - } else { - pci_nic_init_nofail(&nd_table[i], phb->bus, nd->model, NULL); - } + while ((nd = qemu_find_nic_info("spapr-vlan", true, "ibmveth"))) { + spapr_vlan_create(spapr->vio_bus, nd); } + pci_init_nic_devices(phb->bus, NULL); + for (i = 0; i <= drive_get_max_bus(IF_SCSI); i++) { spapr_vscsi_create(spapr->vio_bus); } @@ -2967,8 +3054,10 @@ static void spapr_machine_init(MachineState *machine) } if (has_vga) { - USBBus *usb_bus = usb_bus_find(-1); + USBBus *usb_bus; + usb_bus = USB_BUS(object_resolve_type_unambiguous(TYPE_USB_BUS, + &error_abort)); usb_create_simple(usb_bus, "usb-kbd"); usb_create_simple(usb_bus, "usb-mouse"); } @@ -3051,6 +3140,8 @@ static void spapr_machine_init(MachineState *machine) spapr->vof->fw_size = fw_size; /* for claim() on itself */ spapr_register_hypercall(KVMPPC_H_VOF_CLIENT, spapr_h_vof_client); } + + spapr_watchdog_init(spapr); } #define DEFAULT_KVM_TYPE "auto" @@ -3058,7 +3149,7 @@ static int spapr_kvm_type(MachineState *machine, const char *vm_type) { /* * The use of g_ascii_strcasecmp() for 'hv' and 'pr' is to - * accomodate the 'HV' and 'PV' formats that exists in the + * accommodate the 'HV' and 'PV' formats that exists in the * wild. The 'auto' mode is being introduced already as * lower-case, thus we don't need to bother checking for * "AUTO". @@ -3076,7 +3167,7 @@ static int spapr_kvm_type(MachineState *machine, const char *vm_type) } error_report("Unknown kvm-type specified '%s'", vm_type); - exit(1); + return -1; } /* @@ -3161,8 +3252,8 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus, if (g_str_equal("pci-bridge", qdev_fw_name(dev))) { /* SLOF uses "pci" instead of "pci-bridge" for PCI bridges */ - PCIDevice *pcidev = CAST(PCIDevice, dev, TYPE_PCI_DEVICE); - return g_strdup_printf("pci@%x", PCI_SLOT(pcidev->devfn)); + PCIDevice *pdev = CAST(PCIDevice, dev, TYPE_PCI_DEVICE); + return g_strdup_printf("pci@%x", PCI_SLOT(pdev->devfn)); } if (pcidev) { @@ -3420,8 +3511,7 @@ 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()); - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); cpu_synchronize_state(cs); /* If FWNMI is inactive, addr will be -1, which will deliver to 0x100 */ @@ -3711,7 +3801,7 @@ void spapr_memory_unplug_rollback(SpaprMachineState *spapr, DeviceState *dev) qapi_event_send_mem_unplug_error(dev->id ? : "", qapi_error); - qapi_event_send_device_unplug_guest_error(!!dev->id, dev->id, + qapi_event_send_device_unplug_guest_error(dev->id, dev->canonical_path); } @@ -3918,7 +4008,6 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev) SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); SpaprCpuCore *core = SPAPR_CPU_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(dev); - CPUState *cs; SpaprDrc *drc; CPUArchId *core_slot; int index; @@ -3952,7 +4041,7 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev) } } - core_slot->cpu = OBJECT(dev); + core_slot->cpu = CPU(dev); /* * Set compatibility mode to match the boot CPU, which was either set @@ -3968,7 +4057,7 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev) if (smc->pre_2_10_has_unused_icps) { for (i = 0; i < cc->nr_threads; i++) { - cs = CPU(core->threads[i]); + CPUState *cs = CPU(core->threads[i]); pre_2_10_vmstate_unregister_dummy_icp(cs->cpu_index); } } @@ -4083,7 +4172,6 @@ static bool spapr_phb_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, &sphb->buid, &sphb->io_win_addr, &sphb->mem_win_addr, &sphb->mem64_win_addr, windows_supported, sphb->dma_liobn, - &sphb->nv2_gpa_win_addr, &sphb->nv2_atsd_win_addr, errp); } @@ -4292,7 +4380,7 @@ spapr_cpu_index_to_props(MachineState *machine, unsigned cpu_index) CPUArchId *core_slot; MachineClass *mc = MACHINE_GET_CLASS(machine); - /* make sure possible_cpu are intialized */ + /* make sure possible_cpu are initialized */ mc->possible_cpu_arch_ids(machine); /* get CPU core slot containing thread that matches cpu_index */ core_slot = spapr_find_cpu_slot(machine, cpu_index, NULL); @@ -4346,8 +4434,7 @@ static const CPUArchIdList *spapr_possible_cpu_arch_ids(MachineState *machine) static bool spapr_phb_placement(SpaprMachineState *spapr, uint32_t index, uint64_t *buid, hwaddr *pio, hwaddr *mmio32, hwaddr *mmio64, - unsigned n_dma, uint32_t *liobns, - hwaddr *nv2gpa, hwaddr *nv2atsd, Error **errp) + unsigned n_dma, uint32_t *liobns, Error **errp) { /* * New-style PHB window placement. @@ -4392,9 +4479,6 @@ static bool spapr_phb_placement(SpaprMachineState *spapr, uint32_t index, *pio = SPAPR_PCI_BASE + index * SPAPR_PCI_IO_WIN_SIZE; *mmio32 = SPAPR_PCI_BASE + (index + 1) * SPAPR_PCI_MEM32_WIN_SIZE; *mmio64 = SPAPR_PCI_BASE + (index + 1) * SPAPR_PCI_MEM64_WIN_SIZE; - - *nv2gpa = SPAPR_PCI_NV2RAM64_WIN_BASE + index * SPAPR_PCI_NV2RAM64_WIN_SIZE; - *nv2atsd = SPAPR_PCI_NV2ATSD_WIN_BASE + index * SPAPR_PCI_NV2ATSD_WIN_SIZE; return true; } @@ -4585,13 +4669,10 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_SCSI; /* - * Setting max_cpus to INT32_MAX. Both KVM and TCG max_cpus values - * should be limited by the host capability instead of hardcoded. - * max_cpus for KVM guests will be checked in kvm_init(), and TCG - * guests are welcome to have as many CPUs as the host are capable - * of emulate. + * While KVM determines max cpus in kvm_init() using kvm_max_vcpus(), + * In TCG the limit is restricted by the range of CPU IPIs available. */ - mc->max_cpus = INT32_MAX; + mc->max_cpus = SPAPR_IRQ_NR_IPIS; mc->no_parallel = 1; mc->default_boot_order = ""; @@ -4613,7 +4694,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->dr_lmb_enabled = true; smc->update_dt_enabled = true; - mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.0"); + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power10_v2.0"); mc->has_hotpluggable_cpus = true; mc->nvdimm_supported = true; smc->resize_hpt_default = SPAPR_RESIZE_HPT_ENABLED; @@ -4651,10 +4732,18 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_WORKAROUND; 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_NESTED_PAPR] = SPAPR_CAP_OFF; smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_ON; smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_ON; smc->default_caps.caps[SPAPR_CAP_FWNMI] = SPAPR_CAP_ON; smc->default_caps.caps[SPAPR_CAP_RPT_INVALIDATE] = SPAPR_CAP_OFF; + + /* + * This cap specifies whether the AIL 3 mode for + * H_SET_RESOURCE is supported. The default is modified + * by default_caps_with_cpu(). + */ + smc->default_caps.caps[SPAPR_CAP_AIL_MODE_3] = SPAPR_CAP_ON; spapr_caps_add_properties(smc); smc->irq = &spapr_irq_dual; smc->dr_phb_enabled = true; @@ -4717,14 +4806,69 @@ static void spapr_machine_latest_class_options(MachineClass *mc) type_init(spapr_machine_register_##suffix) /* - * pseries-7.1 + * pseries-9.0 */ -static void spapr_machine_7_1_class_options(MachineClass *mc) +static void spapr_machine_9_0_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE(7_1, "7.1", true); +DEFINE_SPAPR_MACHINE(9_0, "9.0", true); + +/* + * pseries-8.2 + */ +static void spapr_machine_8_2_class_options(MachineClass *mc) +{ + spapr_machine_9_0_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_8_2, hw_compat_8_2_len); +} + +DEFINE_SPAPR_MACHINE(8_2, "8.2", false); + +/* + * pseries-8.1 + */ +static void spapr_machine_8_1_class_options(MachineClass *mc) +{ + spapr_machine_8_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_8_1, hw_compat_8_1_len); +} + +DEFINE_SPAPR_MACHINE(8_1, "8.1", false); + +/* + * pseries-8.0 + */ +static void spapr_machine_8_0_class_options(MachineClass *mc) +{ + spapr_machine_8_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_8_0, hw_compat_8_0_len); +} + +DEFINE_SPAPR_MACHINE(8_0, "8.0", false); + +/* + * pseries-7.2 + */ +static void spapr_machine_7_2_class_options(MachineClass *mc) +{ + spapr_machine_8_0_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_7_2, hw_compat_7_2_len); +} + +DEFINE_SPAPR_MACHINE(7_2, "7.2", false); + +/* + * pseries-7.1 + */ +static void spapr_machine_7_1_class_options(MachineClass *mc) +{ + spapr_machine_7_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_7_1, hw_compat_7_1_len); +} + +DEFINE_SPAPR_MACHINE(7_1, "7.1", false); /* * pseries-7.0 @@ -4861,16 +5005,12 @@ DEFINE_SPAPR_MACHINE(4_1, "4.1", false); static bool phb_placement_4_0(SpaprMachineState *spapr, uint32_t index, uint64_t *buid, hwaddr *pio, hwaddr *mmio32, hwaddr *mmio64, - unsigned n_dma, uint32_t *liobns, - hwaddr *nv2gpa, hwaddr *nv2atsd, Error **errp) + unsigned n_dma, uint32_t *liobns, Error **errp) { if (!spapr_phb_placement(spapr, index, buid, pio, mmio32, mmio64, n_dma, - liobns, nv2gpa, nv2atsd, errp)) { + liobns, errp)) { return false; } - - *nv2gpa = 0; - *nv2atsd = 0; return true; } static void spapr_machine_4_0_class_options(MachineClass *mc) @@ -4943,7 +5083,7 @@ static void spapr_machine_2_12_class_options(MachineClass *mc) /* We depend on kvm_enabled() to choose a default value for the * hpt-max-page-size capability. Of course we can't do it here - * because this is too early and the HW accelerator isn't initialzed + * because this is too early and the HW accelerator isn't initialized * yet. Postpone this to machine init (see default_caps_with_cpu()). */ smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 0; @@ -4974,6 +5114,7 @@ static void spapr_machine_2_11_class_options(MachineClass *mc) spapr_machine_2_12_class_options(mc); smc->default_caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_ON; compat_props_add(mc->compat_props, hw_compat_2_11, hw_compat_2_11_len); + mc->deprecation_reason = "old and not maintained - use a 2.12+ version"; } DEFINE_SPAPR_MACHINE(2_11, "2.11", false); @@ -5035,8 +5176,7 @@ DEFINE_SPAPR_MACHINE(2_8, "2.8", false); static bool phb_placement_2_7(SpaprMachineState *spapr, uint32_t index, uint64_t *buid, hwaddr *pio, hwaddr *mmio32, hwaddr *mmio64, - unsigned n_dma, uint32_t *liobns, - hwaddr *nv2gpa, hwaddr *nv2atsd, Error **errp) + unsigned n_dma, uint32_t *liobns, Error **errp) { /* Legacy PHB placement for pseries-2.7 and earlier machine types */ const uint64_t base_buid = 0x800000020000000ULL; @@ -5051,7 +5191,7 @@ static bool phb_placement_2_7(SpaprMachineState *spapr, uint32_t index, int i; /* Do we have device memory? */ - if (MACHINE(spapr)->maxram_size > ram_top) { + if (MACHINE(spapr)->device_memory) { /* Can't just use maxram_size, because there may be an * alignment gap between normal and device memory regions */ @@ -5081,8 +5221,6 @@ static bool phb_placement_2_7(SpaprMachineState *spapr, uint32_t index, * window into contiguous 32-bit and 64-bit windows */ - *nv2gpa = 0; - *nv2atsd = 0; return true; } diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 655ab856a0..0a15415a1d 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -194,8 +194,7 @@ static void cap_htm_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) static void cap_vsx_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) { ERRP_GUARD(); - PowerPCCPU *cpu = POWERPC_CPU(first_cpu); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(first_cpu); if (!val) { /* TODO: We don't support disabling vsx yet */ @@ -213,14 +212,12 @@ static void cap_vsx_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) static void cap_dfp_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) { ERRP_GUARD(); - PowerPCCPU *cpu = POWERPC_CPU(first_cpu); - CPUPPCState *env = &cpu->env; if (!val) { /* TODO: We don't support disabling dfp yet */ return; } - if (!(env->insns_flags2 & PPC2_DFP)) { + if (!(cpu_env(first_cpu)->insns_flags2 & PPC2_DFP)) { error_setg(errp, "DFP support not available"); error_append_hint(errp, "Try appending -machine cap-dfp=off\n"); } @@ -473,6 +470,64 @@ static void cap_nested_kvm_hv_apply(SpaprMachineState *spapr, error_append_hint(errp, "Try appending -machine cap-nested-hv=off\n"); } + } else if (tcg_enabled()) { + MachineState *ms = MACHINE(spapr); + unsigned int smp_threads = ms->smp.threads; + + /* + * Nested-HV vCPU env state to L2, so SMT-shared SPR updates, for + * example, do not necessarily update the correct SPR value on sibling + * threads that are in a different guest/host context. + */ + if (smp_threads > 1) { + error_setg(errp, "TCG does not support nested-HV with SMT"); + error_append_hint(errp, "Try appending -machine cap-nested-hv=off " + "or use threads=1 with -smp\n"); + } + if (spapr_nested_api(spapr) && + spapr_nested_api(spapr) != NESTED_API_KVM_HV) { + error_setg(errp, "Nested-HV APIs are mutually exclusive"); + error_append_hint(errp, "Please use either cap-nested-hv or " + "cap-nested-papr to proceed.\n"); + return; + } else { + spapr->nested.api = NESTED_API_KVM_HV; + } + } +} + +static void cap_nested_papr_apply(SpaprMachineState *spapr, + uint8_t val, Error **errp) +{ + ERRP_GUARD(); + PowerPCCPU *cpu = POWERPC_CPU(first_cpu); + CPUPPCState *env = &cpu->env; + + if (!val) { + /* capability disabled by default */ + return; + } + + if (tcg_enabled()) { + if (!(env->insns_flags2 & PPC2_ISA300)) { + error_setg(errp, "Nested-PAPR only supported on POWER9 and later"); + error_append_hint(errp, + "Try appending -machine cap-nested-papr=off\n"); + return; + } + if (spapr_nested_api(spapr) && + spapr_nested_api(spapr) != NESTED_API_PAPR) { + error_setg(errp, "Nested-HV APIs are mutually exclusive"); + error_append_hint(errp, "Please use either cap-nested-hv or " + "cap-nested-papr to proceed.\n"); + return; + } else { + spapr->nested.api = NESTED_API_PAPR; + } + } else if (kvm_enabled()) { + error_setg(errp, "KVM implementation does not support Nested-PAPR"); + error_append_hint(errp, + "Try appending -machine cap-nested-papr=off\n"); } } @@ -553,7 +608,7 @@ static void cap_ccf_assist_apply(SpaprMachineState *spapr, uint8_t val, * 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 + * unnecessary. 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. @@ -614,6 +669,33 @@ static void cap_rpt_invalidate_apply(SpaprMachineState *spapr, } } +static void cap_ail_mode_3_apply(SpaprMachineState *spapr, + uint8_t val, Error **errp) +{ + ERRP_GUARD(); + PowerPCCPU *cpu = POWERPC_CPU(first_cpu); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + + if (!val) { + return; + } + + if (tcg_enabled()) { + /* AIL-3 is only supported on POWER8 and above CPUs. */ + if (!(pcc->insns_flags2 & PPC2_ISA207S)) { + error_setg(errp, "TCG only supports cap-ail-mode-3 on POWER8 and later CPUs"); + error_append_hint(errp, "Try appending -machine cap-ail-mode-3=off\n"); + return; + } + } else if (kvm_enabled()) { + if (!kvmppc_supports_ail_3()) { + error_setg(errp, "KVM implementation does not support cap-ail-mode-3"); + error_append_hint(errp, "Try appending -machine cap-ail-mode-3=off\n"); + return; + } + } +} + SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { [SPAPR_CAP_HTM] = { .name = "htm", @@ -694,6 +776,15 @@ SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .type = "bool", .apply = cap_nested_kvm_hv_apply, }, + [SPAPR_CAP_NESTED_PAPR] = { + .name = "nested-papr", + .description = "Allow Nested HV (PAPR API)", + .index = SPAPR_CAP_NESTED_PAPR, + .get = spapr_cap_get_bool, + .set = spapr_cap_set_bool, + .type = "bool", + .apply = cap_nested_papr_apply, + }, [SPAPR_CAP_LARGE_DECREMENTER] = { .name = "large-decr", .description = "Allow Large Decrementer", @@ -731,6 +822,15 @@ SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .type = "bool", .apply = cap_rpt_invalidate_apply, }, + [SPAPR_CAP_AIL_MODE_3] = { + .name = "ail-mode-3", + .description = "Alternate Interrupt Location (AIL) mode 3 support", + .index = SPAPR_CAP_AIL_MODE_3, + .get = spapr_cap_get_bool, + .set = spapr_cap_set_bool, + .type = "bool", + .apply = cap_ail_mode_3_apply, + }, }; static SpaprCapabilities default_caps_with_cpu(SpaprMachineState *spapr, @@ -750,6 +850,7 @@ static SpaprCapabilities default_caps_with_cpu(SpaprMachineState *spapr, 0, spapr->max_compat_pvr)) { caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF; caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; + caps.caps[SPAPR_CAP_AIL_MODE_3] = SPAPR_CAP_OFF; } if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_2_06_PLUS, @@ -853,7 +954,7 @@ const VMStateDescription vmstate_spapr_cap_##sname = { \ .version_id = 1, \ .minimum_version_id = 1, \ .needed = spapr_cap_##sname##_needed, \ - .fields = (VMStateField[]) { \ + .fields = (const VMStateField[]) { \ VMSTATE_UINT8(mig.caps[cap], \ SpaprMachineState), \ VMSTATE_END_OF_LIST() \ @@ -868,6 +969,7 @@ SPAPR_CAP_MIG_STATE(sbbc, SPAPR_CAP_SBBC); SPAPR_CAP_MIG_STATE(ibs, SPAPR_CAP_IBS); 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(nested_papr, SPAPR_CAP_NESTED_PAPR); 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); diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 8a4861f45a..e7c9edd033 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -39,9 +39,13 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu) /* * "PowerPC Processor binding to IEEE 1275" defines the initial MSR state - * as 32bit (MSR_SF=0) in "8.2.1. Initial Register Values". + * as 32bit (MSR_SF=0) with MSR_ME=1 and MSR_FP=1 in "8.2.1. Initial + * Register Values". This can also be found in "LoPAPR 1.1" "C.9.2.1 + * Initial Register Values". */ env->msr &= ~(1ULL << MSR_SF); + env->msr |= (1ULL << MSR_ME) | (1ULL << MSR_FP); + env->spr[SPR_HIOR] = 0; lpcr = env->spr[SPR_LPCR]; @@ -74,6 +78,8 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu) kvm_check_mmu(cpu, &error_fatal); + cpu_ppc_tb_reset(env); + spapr_irq_cpu_intc_reset(spapr, cpu); } @@ -125,7 +131,7 @@ static const VMStateDescription vmstate_spapr_cpu_slb_shadow = { .version_id = 1, .minimum_version_id = 1, .needed = slb_shadow_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(slb_shadow_addr, SpaprCpuState), VMSTATE_UINT64(slb_shadow_size, SpaprCpuState), VMSTATE_END_OF_LIST() @@ -144,7 +150,7 @@ static const VMStateDescription vmstate_spapr_cpu_dtl = { .version_id = 1, .minimum_version_id = 1, .needed = dtl_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(dtl_addr, SpaprCpuState), VMSTATE_UINT64(dtl_size, SpaprCpuState), VMSTATE_END_OF_LIST() @@ -163,11 +169,11 @@ static const VMStateDescription vmstate_spapr_cpu_vpa = { .version_id = 1, .minimum_version_id = 1, .needed = vpa_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(vpa_addr, SpaprCpuState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_spapr_cpu_slb_shadow, &vmstate_spapr_cpu_dtl, NULL @@ -178,10 +184,10 @@ static const VMStateDescription vmstate_spapr_cpu_state = { .name = "spapr_cpu", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_spapr_cpu_vpa, NULL } @@ -243,8 +249,7 @@ static void spapr_cpu_core_unrealize(DeviceState *dev) * spapr_cpu_core_realize(), make sure we only unrealize * vCPUs that have already been realized. */ - if (object_property_get_bool(OBJECT(sc->threads[i]), "realized", - &error_abort)) { + if (qdev_is_realized(DEVICE(sc->threads[i]))) { spapr_unrealize_vcpu(sc->threads[i], sc); } spapr_delete_vcpu(sc->threads[i]); @@ -255,7 +260,7 @@ static void spapr_cpu_core_unrealize(DeviceState *dev) } static bool spapr_realize_vcpu(PowerPCCPU *cpu, SpaprMachineState *spapr, - SpaprCpuCore *sc, Error **errp) + SpaprCpuCore *sc, int thread_index, Error **errp) { CPUPPCState *env = &cpu->env; CPUState *cs = CPU(cpu); @@ -267,6 +272,11 @@ static bool spapr_realize_vcpu(PowerPCCPU *cpu, SpaprMachineState *spapr, cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr)); kvmppc_set_papr(cpu); + env->spr_cb[SPR_PIR].default_value = cs->cpu_index; + env->spr_cb[SPR_TIR].default_value = thread_index; + + cpu_ppc_set_1lpar(cpu); + /* Set time-base frequency to 512 MHz. vhyp must be set first. */ cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); @@ -299,7 +309,7 @@ static PowerPCCPU *spapr_create_vcpu(SpaprCpuCore *sc, int i, Error **errp) * All CPUs start halted. CPU0 is unhalted from the machine level reset code * and the rest are explicitly started up by the guest using an RTAS call. */ - cs->start_powered_off = true; + qdev_prop_set_bit(DEVICE(obj), "start-powered-off", true); cs->cpu_index = cc->core_id + i; if (!spapr_set_vcpu_id(cpu, cs->cpu_index, errp)) { return NULL; @@ -337,7 +347,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) for (i = 0; i < cc->nr_threads; i++) { sc->threads[i] = spapr_create_vcpu(sc, i, errp); if (!sc->threads[i] || - !spapr_realize_vcpu(sc->threads[i], spapr, sc, errp)) { + !spapr_realize_vcpu(sc->threads[i], spapr, sc, i, errp)) { spapr_cpu_core_unrealize(dev); return; } @@ -382,15 +392,14 @@ static const TypeInfo spapr_cpu_core_type_infos[] = { DEFINE_SPAPR_CPU_CORE_TYPE("970_v2.2"), DEFINE_SPAPR_CPU_CORE_TYPE("970mp_v1.0"), DEFINE_SPAPR_CPU_CORE_TYPE("970mp_v1.1"), - DEFINE_SPAPR_CPU_CORE_TYPE("power5+_v2.1"), + DEFINE_SPAPR_CPU_CORE_TYPE("power5p_v2.1"), DEFINE_SPAPR_CPU_CORE_TYPE("power7_v2.3"), - DEFINE_SPAPR_CPU_CORE_TYPE("power7+_v2.1"), + DEFINE_SPAPR_CPU_CORE_TYPE("power7p_v2.1"), DEFINE_SPAPR_CPU_CORE_TYPE("power8_v2.0"), DEFINE_SPAPR_CPU_CORE_TYPE("power8e_v2.1"), DEFINE_SPAPR_CPU_CORE_TYPE("power8nvl_v1.0"), - DEFINE_SPAPR_CPU_CORE_TYPE("power9_v1.0"), DEFINE_SPAPR_CPU_CORE_TYPE("power9_v2.0"), - DEFINE_SPAPR_CPU_CORE_TYPE("power10_v1.0"), + DEFINE_SPAPR_CPU_CORE_TYPE("power9_v2.2"), DEFINE_SPAPR_CPU_CORE_TYPE("power10_v2.0"), #ifdef CONFIG_KVM DEFINE_SPAPR_CPU_CORE_TYPE("host"), diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 76bc5d42a0..1484e3209d 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -17,7 +17,6 @@ #include "hw/ppc/spapr_drc.h" #include "qom/object.h" #include "migration/vmstate.h" -#include "qapi/error.h" #include "qapi/qapi-events-qdev.h" #include "qapi/visitor.h" #include "qemu/error-report.h" @@ -175,8 +174,7 @@ static uint32_t drc_unisolate_logical(SpaprDrc *drc) "for device %s", drc->dev->id); } - qapi_event_send_device_unplug_guest_error(!!drc->dev->id, - drc->dev->id, + qapi_event_send_device_unplug_guest_error(drc->dev->id, drc->dev->canonical_path); } @@ -343,7 +341,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, fdt_depth = 0; do { - const char *name = NULL; + const char *dt_name = NULL; const struct fdt_property *prop = NULL; int prop_len = 0, name_len = 0; uint32_t tag; @@ -353,8 +351,8 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, switch (tag) { case FDT_BEGIN_NODE: fdt_depth++; - name = fdt_get_name(fdt, fdt_offset, &name_len); - if (!visit_start_struct(v, name, NULL, 0, errp)) { + dt_name = fdt_get_name(fdt, fdt_offset, &name_len); + if (!visit_start_struct(v, dt_name, NULL, 0, errp)) { return; } break; @@ -371,8 +369,8 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, case FDT_PROP: { int i; prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len); - name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); - if (!visit_start_list(v, name, NULL, 0, errp)) { + dt_name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + if (!visit_start_list(v, dt_name, NULL, 0, errp)) { return; } for (i = 0; i < prop_len; i++) { @@ -473,7 +471,7 @@ static const VMStateDescription vmstate_spapr_drc_unplug_requested = { .version_id = 1, .minimum_version_id = 1, .needed = spapr_drc_unplug_requested_needed, - .fields = (VMStateField []) { + .fields = (const VMStateField []) { VMSTATE_BOOL(unplug_requested, SpaprDrc), VMSTATE_END_OF_LIST() } @@ -506,11 +504,11 @@ static const VMStateDescription vmstate_spapr_drc = { .version_id = 1, .minimum_version_id = 1, .needed = spapr_drc_needed, - .fields = (VMStateField []) { + .fields = (const VMStateField []) { VMSTATE_UINT32(state, SpaprDrc), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_spapr_drc_unplug_requested, NULL } @@ -613,7 +611,7 @@ static const VMStateDescription vmstate_spapr_drc_physical = { .version_id = 1, .minimum_version_id = 1, .needed = drc_physical_needed, - .fields = (VMStateField []) { + .fields = (const VMStateField []) { VMSTATE_UINT32(dr_indicator, SpaprDrcPhysical), VMSTATE_END_OF_LIST() } @@ -1239,8 +1237,6 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu, case FDT_END_NODE: drc->ccs_depth--; if (drc->ccs_depth == 0) { - uint32_t drc_index = spapr_drc_index(drc); - /* done sending the device tree, move to configured state */ trace_spapr_drc_set_configured(drc_index); drc->state = drck->ready_state; diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 4508e40814..cb0eeee587 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -899,7 +899,7 @@ void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered) } return; } - qemu_cond_wait_iothread(&spapr->fwnmi_machine_check_interlock_cond); + qemu_cond_wait_bql(&spapr->fwnmi_machine_check_interlock_cond); if (spapr->fwnmi_machine_check_addr == -1) { /* * If the machine was reset while waiting for the interlock, @@ -920,7 +920,11 @@ void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered) * fails when running with -only-migrate. A proper interface to * delay migration completion for a bit could avoid that. */ - ret = migrate_add_blocker(spapr->fwnmi_migration_blocker, NULL); + 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"); + + ret = migrate_add_blocker(&spapr->fwnmi_migration_blocker, NULL); if (ret == -EBUSY) { warn_report("Received a fwnmi while migration was in progress"); } diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index d761a7d0c3..5e1d020e3d 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -3,15 +3,17 @@ #include "qapi/error.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" +#include "sysemu/tcg.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/error-report.h" -#include "exec/exec-all.h" +#include "exec/tb-flush.h" #include "helper_regs.h" #include "hw/ppc/ppc.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_cpu_core.h" +#include "hw/ppc/spapr_nested.h" #include "mmu-hash64.h" #include "cpu-models.h" #include "trace.h" @@ -30,7 +32,7 @@ bool is_ram_address(SpaprMachineState *spapr, hwaddr addr) if (addr < machine->ram_size) { return true; } - if ((addr >= dms->base) + if (dms && (addr >= dms->base) && ((addr - dms->base) < memory_region_size(&dms->mr))) { return true; } @@ -121,9 +123,11 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu, if (kvm_enabled()) { return H_HARDWARE; + } else if (tcg_enabled()) { + return vhyp_mmu_resize_hpt_prepare(cpu, spapr, shift); + } else { + g_assert_not_reached(); } - - return softmmu_resize_hpt_prepare(cpu, spapr, shift); } static void do_push_sregs_to_kvm_pr(CPUState *cs, run_on_cpu_data data) @@ -189,9 +193,11 @@ static target_ulong h_resize_hpt_commit(PowerPCCPU *cpu, if (kvm_enabled()) { return H_HARDWARE; + } else if (tcg_enabled()) { + return vhyp_mmu_resize_hpt_commit(cpu, spapr, flags, shift); + } else { + g_assert_not_reached(); } - - return softmmu_resize_hpt_commit(cpu, spapr, flags, shift); } @@ -490,6 +496,7 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr, env->msr |= (1ULL << MSR_EE); hreg_compute_hflags(env); + ppc_maybe_interrupt(env); if (spapr_cpu->prod) { spapr_cpu->prod = false; @@ -500,6 +507,7 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr, cs->halted = 1; cs->exception_index = EXCP_HLT; cs->exit_request = 1; + ppc_maybe_interrupt(env); } return H_SUCCESS; @@ -521,6 +529,7 @@ static target_ulong h_confer_self(PowerPCCPU *cpu) cs->halted = 1; cs->exception_index = EXCP_HALTED; cs->exit_request = 1; + ppc_maybe_interrupt(&cpu->env); return H_SUCCESS; } @@ -633,6 +642,7 @@ static target_ulong h_prod(PowerPCCPU *cpu, SpaprMachineState *spapr, spapr_cpu = spapr_cpu_state(tcpu); spapr_cpu->prod = true; cs->halted = 0; + ppc_maybe_interrupt(&cpu->env); qemu_cpu_kick(cs); return H_SUCCESS; @@ -783,6 +793,54 @@ static target_ulong h_logical_dcbf(PowerPCCPU *cpu, SpaprMachineState *spapr, return H_SUCCESS; } +static target_ulong h_set_mode_resource_set_ciabr(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong mflags, + target_ulong value1, + target_ulong value2) +{ + CPUPPCState *env = &cpu->env; + + assert(tcg_enabled()); /* KVM will have handled this */ + + if (mflags) { + return H_UNSUPPORTED_FLAG; + } + if (value2) { + return H_P4; + } + if ((value1 & PPC_BITMASK(62, 63)) == 0x3) { + return H_P3; + } + + ppc_store_ciabr(env, value1); + + return H_SUCCESS; +} + +static target_ulong h_set_mode_resource_set_dawr0(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong mflags, + target_ulong value1, + target_ulong value2) +{ + CPUPPCState *env = &cpu->env; + + assert(tcg_enabled()); /* KVM will have handled this */ + + if (mflags) { + return H_UNSUPPORTED_FLAG; + } + if (value2 & PPC_BIT(61)) { + return H_P4; + } + + ppc_store_dawr0(env, value1); + ppc_store_dawrx0(env, value2); + + return H_SUCCESS; +} + static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong mflags, @@ -812,30 +870,32 @@ static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu, } static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu, + SpaprMachineState *spapr, target_ulong mflags, target_ulong value1, target_ulong value2) { - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - - if (!(pcc->insns_flags2 & PPC2_ISA207S)) { - return H_P2; - } if (value1) { return H_P3; } + if (value2) { return H_P4; } - if (mflags == 1) { - /* AIL=1 is reserved in POWER8/POWER9/POWER10 */ + /* + * AIL-1 is not architected, and AIL-2 is not supported by QEMU spapr. + * It is supported for faithful emulation of bare metal systems, but for + * compatibility concerns we leave it out of the pseries machine. + */ + if (mflags != 0 && mflags != 3) { return H_UNSUPPORTED_FLAG; } - if (mflags == 2 && (pcc->insns_flags2 & PPC2_ISA310)) { - /* AIL=2 is reserved in POWER10 (ISA v3.1) */ - return H_UNSUPPORTED_FLAG; + if (mflags == 3) { + if (!spapr_get_cap(spapr, SPAPR_CAP_AIL_MODE_3)) { + return H_UNSUPPORTED_FLAG; + } } spapr_set_all_lpcrs(mflags << LPCR_AIL_SHIFT, LPCR_AIL); @@ -850,11 +910,19 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong ret = H_P2; switch (resource) { + case H_SET_MODE_RESOURCE_SET_CIABR: + ret = h_set_mode_resource_set_ciabr(cpu, spapr, args[0], args[2], + args[3]); + break; + case H_SET_MODE_RESOURCE_SET_DAWR0: + ret = h_set_mode_resource_set_dawr0(cpu, spapr, args[0], args[2], + args[3]); + break; case H_SET_MODE_RESOURCE_LE: ret = h_set_mode_resource_le(cpu, spapr, args[0], args[2], args[3]); break; case H_SET_MODE_RESOURCE_ADDR_TRANS_MODE: - ret = h_set_mode_resource_addr_trans_mode(cpu, args[0], + ret = h_set_mode_resource_addr_trans_mode(cpu, spapr, args[0], args[2], args[3]); break; } @@ -920,6 +988,7 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, target_ulong page_size = args[2]; target_ulong table_size = args[3]; target_ulong update_lpcr = 0; + target_ulong table_byte_size; uint64_t cproc; if (flags & ~FLAGS_MASK) { /* Check no reserved bits are set */ @@ -927,6 +996,14 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, } if (flags & FLAG_MODIFY) { if (flags & FLAG_REGISTER) { + /* Check process table alignment */ + table_byte_size = 1ULL << (table_size + 12); + if (proc_tbl & (table_byte_size - 1)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: process table not properly aligned: proc_tbl 0x" + TARGET_FMT_lx" proc_tbl_size 0x"TARGET_FMT_lx"\n", + __func__, proc_tbl, table_byte_size); + } if (flags & FLAG_RADIX) { /* Register new RADIX process table */ if (proc_tbl & 0xfff || proc_tbl >> 60) { return H_P2; @@ -1247,6 +1324,14 @@ target_ulong do_client_architecture_support(PowerPCCPU *cpu, spapr->fdt_initial_size = spapr->fdt_size; spapr->fdt_blob = fdt; + /* + * Set the machine->fdt pointer again since we just freed + * it above (by freeing spapr->fdt_blob). We set this + * pointer to enable support for the 'dumpdtb' QMP/HMP + * command. + */ + MACHINE(spapr)->fdt = fdt; + return H_SUCCESS; } @@ -1440,6 +1525,28 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn) *slot = fn; } +void spapr_unregister_hypercall(target_ulong opcode) +{ + spapr_hcall_fn *slot; + + if (opcode <= MAX_HCALL_OPCODE) { + assert((opcode & 0x3) == 0); + + slot = &papr_hypercall_table[opcode / 4]; + } else if (opcode >= SVM_HCALL_BASE && opcode <= SVM_HCALL_MAX) { + /* we only have SVM-related hcall numbers assigned in multiples of 4 */ + assert((opcode & 0x3) == 0); + + slot = &svm_hypercall_table[(opcode - SVM_HCALL_BASE) / 4]; + } else { + assert((opcode >= KVMPPC_HCALL_BASE) && (opcode <= KVMPPC_HCALL_MAX)); + + slot = &kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE]; + } + + *slot = NULL; +} + target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, target_ulong *args) { @@ -1474,361 +1581,17 @@ target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, } #ifdef CONFIG_TCG -#define PRTS_MASK 0x1f - -static target_ulong h_set_ptbl(PowerPCCPU *cpu, - SpaprMachineState *spapr, - target_ulong opcode, - target_ulong *args) -{ - target_ulong ptcr = args[0]; - - if (!spapr_get_cap(spapr, SPAPR_CAP_NESTED_KVM_HV)) { - return H_FUNCTION; - } - - if ((ptcr & PRTS_MASK) + 12 - 4 > 12) { - return H_PARAMETER; - } - - spapr->nested_ptcr = ptcr; /* Save new partition table */ - - return H_SUCCESS; -} - -static target_ulong h_tlb_invalidate(PowerPCCPU *cpu, - SpaprMachineState *spapr, - target_ulong opcode, - target_ulong *args) -{ - /* - * The spapr virtual hypervisor nested HV implementation retains no L2 - * translation state except for TLB. And the TLB is always invalidated - * across L1<->L2 transitions, so nothing is required here. - */ - - return H_SUCCESS; -} - -static target_ulong h_copy_tofrom_guest(PowerPCCPU *cpu, - SpaprMachineState *spapr, - target_ulong opcode, - target_ulong *args) -{ - /* - * This HCALL is not required, L1 KVM will take a slow path and walk the - * page tables manually to do the data copy. - */ - return H_FUNCTION; -} - -/* - * When this handler returns, the environment is switched to the L2 guest - * and TCG begins running that. spapr_exit_nested() performs the switch from - * L2 back to L1 and returns from the H_ENTER_NESTED hcall. - */ -static target_ulong h_enter_nested(PowerPCCPU *cpu, - SpaprMachineState *spapr, - target_ulong opcode, - target_ulong *args) -{ - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); - target_ulong hv_ptr = args[0]; - target_ulong regs_ptr = args[1]; - target_ulong hdec, now = cpu_ppc_load_tbl(env); - target_ulong lpcr, lpcr_mask; - struct kvmppc_hv_guest_state *hvstate; - struct kvmppc_hv_guest_state hv_state; - struct kvmppc_pt_regs *regs; - hwaddr len; - uint64_t cr; - int i; - - if (spapr->nested_ptcr == 0) { - return H_NOT_AVAILABLE; - } - - len = sizeof(*hvstate); - hvstate = address_space_map(CPU(cpu)->as, hv_ptr, &len, false, - MEMTXATTRS_UNSPECIFIED); - if (len != sizeof(*hvstate)) { - address_space_unmap(CPU(cpu)->as, hvstate, len, 0, false); - return H_PARAMETER; - } - - memcpy(&hv_state, hvstate, len); - - address_space_unmap(CPU(cpu)->as, hvstate, len, len, false); - - /* - * We accept versions 1 and 2. Version 2 fields are unused because TCG - * does not implement DAWR*. - */ - if (hv_state.version > HV_GUEST_STATE_VERSION) { - return H_PARAMETER; - } - - spapr_cpu->nested_host_state = g_try_new(CPUPPCState, 1); - if (!spapr_cpu->nested_host_state) { - return H_NO_MEM; - } - - memcpy(spapr_cpu->nested_host_state, env, sizeof(CPUPPCState)); - - len = sizeof(*regs); - regs = address_space_map(CPU(cpu)->as, regs_ptr, &len, false, - MEMTXATTRS_UNSPECIFIED); - if (!regs || len != sizeof(*regs)) { - address_space_unmap(CPU(cpu)->as, regs, len, 0, false); - g_free(spapr_cpu->nested_host_state); - return H_P2; - } - - len = sizeof(env->gpr); - assert(len == sizeof(regs->gpr)); - memcpy(env->gpr, regs->gpr, len); - - env->lr = regs->link; - env->ctr = regs->ctr; - cpu_write_xer(env, regs->xer); - - cr = regs->ccr; - for (i = 7; i >= 0; i--) { - env->crf[i] = cr & 15; - cr >>= 4; - } - - env->msr = regs->msr; - env->nip = regs->nip; - - address_space_unmap(CPU(cpu)->as, regs, len, len, false); - - env->cfar = hv_state.cfar; - - assert(env->spr[SPR_LPIDR] == 0); - env->spr[SPR_LPIDR] = hv_state.lpid; - - lpcr_mask = LPCR_DPFD | LPCR_ILE | LPCR_AIL | LPCR_LD | LPCR_MER; - lpcr = (env->spr[SPR_LPCR] & ~lpcr_mask) | (hv_state.lpcr & lpcr_mask); - lpcr |= LPCR_HR | LPCR_UPRT | LPCR_GTSE | LPCR_HVICE | LPCR_HDICE; - lpcr &= ~LPCR_LPES0; - env->spr[SPR_LPCR] = lpcr & pcc->lpcr_mask; - - env->spr[SPR_PCR] = hv_state.pcr; - /* hv_state.amor is not used */ - env->spr[SPR_DPDES] = hv_state.dpdes; - env->spr[SPR_HFSCR] = hv_state.hfscr; - hdec = hv_state.hdec_expiry - now; - spapr_cpu->nested_tb_offset = hv_state.tb_offset; - /* TCG does not implement DAWR*, CIABR, PURR, SPURR, IC, VTB, HEIR SPRs*/ - env->spr[SPR_SRR0] = hv_state.srr0; - env->spr[SPR_SRR1] = hv_state.srr1; - env->spr[SPR_SPRG0] = hv_state.sprg[0]; - env->spr[SPR_SPRG1] = hv_state.sprg[1]; - env->spr[SPR_SPRG2] = hv_state.sprg[2]; - env->spr[SPR_SPRG3] = hv_state.sprg[3]; - env->spr[SPR_BOOKS_PID] = hv_state.pidr; - env->spr[SPR_PPR] = hv_state.ppr; - - cpu_ppc_hdecr_init(env); - cpu_ppc_store_hdecr(env, hdec); - - /* - * The hv_state.vcpu_token is not needed. It is used by the KVM - * implementation to remember which L2 vCPU last ran on which physical - * CPU so as to invalidate process scope translations if it is moved - * between physical CPUs. For now TLBs are always flushed on L1<->L2 - * transitions so this is not a problem. - * - * Could validate that the same vcpu_token does not attempt to run on - * different L1 vCPUs at the same time, but that would be a L1 KVM bug - * and it's not obviously worth a new data structure to do it. - */ - - env->tb_env->tb_offset += spapr_cpu->nested_tb_offset; - spapr_cpu->in_nested = true; - - hreg_compute_hflags(env); - tlb_flush(cs); - env->reserve_addr = -1; /* Reset the reservation */ - - /* - * The spapr hcall helper sets env->gpr[3] to the return value, but at - * this point the L1 is not returning from the hcall but rather we - * start running the L2, so r3 must not be clobbered, so return env->gpr[3] - * to leave it unchanged. - */ - return env->gpr[3]; -} - -void spapr_exit_nested(PowerPCCPU *cpu, int excp) -{ - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); - target_ulong r3_return = env->excp_vectors[excp]; /* hcall return value */ - target_ulong hv_ptr = spapr_cpu->nested_host_state->gpr[4]; - target_ulong regs_ptr = spapr_cpu->nested_host_state->gpr[5]; - struct kvmppc_hv_guest_state *hvstate; - struct kvmppc_pt_regs *regs; - hwaddr len; - uint64_t cr; - int i; - - assert(spapr_cpu->in_nested); - - cpu_ppc_hdecr_exit(env); - - len = sizeof(*hvstate); - hvstate = address_space_map(CPU(cpu)->as, hv_ptr, &len, true, - MEMTXATTRS_UNSPECIFIED); - if (len != sizeof(*hvstate)) { - address_space_unmap(CPU(cpu)->as, hvstate, len, 0, true); - r3_return = H_PARAMETER; - goto out_restore_l1; - } - - hvstate->cfar = env->cfar; - hvstate->lpcr = env->spr[SPR_LPCR]; - hvstate->pcr = env->spr[SPR_PCR]; - hvstate->dpdes = env->spr[SPR_DPDES]; - hvstate->hfscr = env->spr[SPR_HFSCR]; - - if (excp == POWERPC_EXCP_HDSI) { - hvstate->hdar = env->spr[SPR_HDAR]; - hvstate->hdsisr = env->spr[SPR_HDSISR]; - hvstate->asdr = env->spr[SPR_ASDR]; - } else if (excp == POWERPC_EXCP_HISI) { - hvstate->asdr = env->spr[SPR_ASDR]; - } - - /* HEIR should be implemented for HV mode and saved here. */ - hvstate->srr0 = env->spr[SPR_SRR0]; - hvstate->srr1 = env->spr[SPR_SRR1]; - hvstate->sprg[0] = env->spr[SPR_SPRG0]; - hvstate->sprg[1] = env->spr[SPR_SPRG1]; - hvstate->sprg[2] = env->spr[SPR_SPRG2]; - hvstate->sprg[3] = env->spr[SPR_SPRG3]; - hvstate->pidr = env->spr[SPR_BOOKS_PID]; - hvstate->ppr = env->spr[SPR_PPR]; - - /* Is it okay to specify write length larger than actual data written? */ - address_space_unmap(CPU(cpu)->as, hvstate, len, len, true); - - len = sizeof(*regs); - regs = address_space_map(CPU(cpu)->as, regs_ptr, &len, true, - MEMTXATTRS_UNSPECIFIED); - if (!regs || len != sizeof(*regs)) { - address_space_unmap(CPU(cpu)->as, regs, len, 0, true); - r3_return = H_P2; - goto out_restore_l1; - } - - len = sizeof(env->gpr); - assert(len == sizeof(regs->gpr)); - memcpy(regs->gpr, env->gpr, len); - - regs->link = env->lr; - regs->ctr = env->ctr; - regs->xer = cpu_read_xer(env); - - cr = 0; - for (i = 0; i < 8; i++) { - cr |= (env->crf[i] & 15) << (4 * (7 - i)); - } - regs->ccr = cr; - - if (excp == POWERPC_EXCP_MCHECK || - excp == POWERPC_EXCP_RESET || - excp == POWERPC_EXCP_SYSCALL) { - regs->nip = env->spr[SPR_SRR0]; - regs->msr = env->spr[SPR_SRR1] & env->msr_mask; - } else { - regs->nip = env->spr[SPR_HSRR0]; - regs->msr = env->spr[SPR_HSRR1] & env->msr_mask; - } - - /* Is it okay to specify write length larger than actual data written? */ - address_space_unmap(CPU(cpu)->as, regs, len, len, true); - -out_restore_l1: - memcpy(env->gpr, spapr_cpu->nested_host_state->gpr, sizeof(env->gpr)); - env->lr = spapr_cpu->nested_host_state->lr; - env->ctr = spapr_cpu->nested_host_state->ctr; - memcpy(env->crf, spapr_cpu->nested_host_state->crf, sizeof(env->crf)); - env->cfar = spapr_cpu->nested_host_state->cfar; - env->xer = spapr_cpu->nested_host_state->xer; - env->so = spapr_cpu->nested_host_state->so; - env->ov = spapr_cpu->nested_host_state->ov; - env->ov32 = spapr_cpu->nested_host_state->ov32; - env->ca32 = spapr_cpu->nested_host_state->ca32; - env->msr = spapr_cpu->nested_host_state->msr; - env->nip = spapr_cpu->nested_host_state->nip; - - assert(env->spr[SPR_LPIDR] != 0); - env->spr[SPR_LPCR] = spapr_cpu->nested_host_state->spr[SPR_LPCR]; - env->spr[SPR_LPIDR] = spapr_cpu->nested_host_state->spr[SPR_LPIDR]; - env->spr[SPR_PCR] = spapr_cpu->nested_host_state->spr[SPR_PCR]; - env->spr[SPR_DPDES] = 0; - env->spr[SPR_HFSCR] = spapr_cpu->nested_host_state->spr[SPR_HFSCR]; - env->spr[SPR_SRR0] = spapr_cpu->nested_host_state->spr[SPR_SRR0]; - env->spr[SPR_SRR1] = spapr_cpu->nested_host_state->spr[SPR_SRR1]; - env->spr[SPR_SPRG0] = spapr_cpu->nested_host_state->spr[SPR_SPRG0]; - env->spr[SPR_SPRG1] = spapr_cpu->nested_host_state->spr[SPR_SPRG1]; - env->spr[SPR_SPRG2] = spapr_cpu->nested_host_state->spr[SPR_SPRG2]; - env->spr[SPR_SPRG3] = spapr_cpu->nested_host_state->spr[SPR_SPRG3]; - env->spr[SPR_BOOKS_PID] = spapr_cpu->nested_host_state->spr[SPR_BOOKS_PID]; - env->spr[SPR_PPR] = spapr_cpu->nested_host_state->spr[SPR_PPR]; - - /* - * Return the interrupt vector address from H_ENTER_NESTED to the L1 - * (or error code). - */ - env->gpr[3] = r3_return; - - env->tb_env->tb_offset -= spapr_cpu->nested_tb_offset; - spapr_cpu->in_nested = false; - - hreg_compute_hflags(env); - tlb_flush(cs); - env->reserve_addr = -1; /* Reset the reservation */ - - g_free(spapr_cpu->nested_host_state); - spapr_cpu->nested_host_state = NULL; -} - -static void hypercall_register_nested(void) -{ - spapr_register_hypercall(KVMPPC_H_SET_PARTITION_TABLE, h_set_ptbl); - spapr_register_hypercall(KVMPPC_H_ENTER_NESTED, h_enter_nested); - spapr_register_hypercall(KVMPPC_H_TLB_INVALIDATE, h_tlb_invalidate); - spapr_register_hypercall(KVMPPC_H_COPY_TOFROM_GUEST, h_copy_tofrom_guest); -} - static void hypercall_register_softmmu(void) { /* DO NOTHING */ } #else -void spapr_exit_nested(PowerPCCPU *cpu, int excp) -{ - g_assert_not_reached(); -} - static target_ulong h_softmmu(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { g_assert_not_reached(); } -static void hypercall_register_nested(void) -{ - /* DO NOTHING */ -} - static void hypercall_register_softmmu(void) { /* hcall-pft */ @@ -1877,7 +1640,7 @@ static void hypercall_register_types(void) spapr_register_hypercall(H_GET_CPU_CHARACTERISTICS, h_get_cpu_characteristics); - /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate + /* "debugger" hcalls (also used by SLOF). Note: We do -not- differentiate * here between the "CI" and the "CACHE" variants, they will use whatever * mapping attributes qemu is using. When using KVM, the kernel will * enforce the attributes more strongly @@ -1897,8 +1660,6 @@ static void hypercall_register_types(void) spapr_register_hypercall(KVMPPC_H_CAS, h_client_architecture_support); spapr_register_hypercall(KVMPPC_H_UPDATE_DT, h_update_dt); - - hypercall_register_nested(); } type_init(hypercall_register_types) diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 81e5a1aea3..e3c01ef44f 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -248,7 +248,7 @@ static int spapr_tce_table_post_load(void *opaque, int version_id) memcpy(tcet->table, tcet->mig_table, tcet->nb_table * sizeof(tcet->table[0])); - free(tcet->mig_table); + g_free(tcet->mig_table); tcet->mig_table = NULL; } @@ -270,7 +270,7 @@ static const VMStateDescription vmstate_spapr_tce_table_ex = { .version_id = 1, .minimum_version_id = 1, .needed = spapr_tce_table_ex_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(bus_offset, SpaprTceTable), VMSTATE_UINT32(page_shift, SpaprTceTable), VMSTATE_END_OF_LIST() @@ -279,11 +279,11 @@ static const VMStateDescription vmstate_spapr_tce_table_ex = { static const VMStateDescription vmstate_spapr_tce_table = { .name = "spapr_iommu", - .version_id = 2, + .version_id = 3, .minimum_version_id = 2, .pre_save = spapr_tce_table_pre_save, .post_load = spapr_tce_table_post_load, - .fields = (VMStateField []) { + .fields = (const VMStateField []) { /* Sanity check */ VMSTATE_UINT32_EQUAL(liobn, SpaprTceTable, NULL), @@ -292,10 +292,11 @@ static const VMStateDescription vmstate_spapr_tce_table = { VMSTATE_BOOL(bypass, SpaprTceTable), VMSTATE_VARRAY_UINT32_ALLOC(mig_table, SpaprTceTable, mig_nb_table, 0, vmstate_info_uint64, uint64_t), + VMSTATE_BOOL_V(def_win, SpaprTceTable, 3), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_spapr_tce_table_ex, NULL } diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c index a0d1e1298e..97b2fc42ab 100644 --- a/hw/ppc/spapr_irq.c +++ b/hw/ppc/spapr_irq.c @@ -23,6 +23,8 @@ #include "trace.h" +QEMU_BUILD_BUG_ON(SPAPR_IRQ_NR_IPIS > SPAPR_XIRQ_BASE); + static const TypeInfo spapr_intc_info = { .name = TYPE_SPAPR_INTC, .parent = TYPE_INTERFACE, @@ -329,7 +331,7 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp) int i; dev = qdev_new(TYPE_SPAPR_XIVE); - qdev_prop_set_uint32(dev, "nr-irqs", smc->nr_xirqs + SPAPR_XIRQ_BASE); + qdev_prop_set_uint32(dev, "nr-irqs", smc->nr_xirqs + SPAPR_IRQ_NR_IPIS); /* * 8 XIVE END structures per CPU. One for each available * priority @@ -356,7 +358,7 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp) } spapr->qirqs = qemu_allocate_irqs(spapr_set_irq, spapr, - smc->nr_xirqs + SPAPR_XIRQ_BASE); + smc->nr_xirqs + SPAPR_IRQ_NR_IPIS); /* * Mostly we don't actually need this until reset, except that not diff --git a/hw/ppc/spapr_nested.c b/hw/ppc/spapr_nested.c new file mode 100644 index 0000000000..c02785756c --- /dev/null +++ b/hw/ppc/spapr_nested.c @@ -0,0 +1,1905 @@ +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "exec/exec-all.h" +#include "helper_regs.h" +#include "hw/ppc/ppc.h" +#include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_cpu_core.h" +#include "hw/ppc/spapr_nested.h" +#include "mmu-book3s-v3.h" +#include "cpu-models.h" +#include "qemu/log.h" + +void spapr_nested_reset(SpaprMachineState *spapr) +{ + if (spapr_get_cap(spapr, SPAPR_CAP_NESTED_KVM_HV)) { + spapr_unregister_nested_hv(); + spapr_register_nested_hv(); + } else if (spapr_get_cap(spapr, SPAPR_CAP_NESTED_PAPR)) { + spapr->nested.capabilities_set = false; + spapr_unregister_nested_papr(); + spapr_register_nested_papr(); + spapr_nested_gsb_init(); + } else { + spapr->nested.api = 0; + } +} + +uint8_t spapr_nested_api(SpaprMachineState *spapr) +{ + return spapr->nested.api; +} + +#ifdef CONFIG_TCG + +bool spapr_get_pate_nested_hv(SpaprMachineState *spapr, PowerPCCPU *cpu, + target_ulong lpid, ppc_v3_pate_t *entry) +{ + uint64_t patb, pats; + + assert(lpid != 0); + + patb = spapr->nested.ptcr & PTCR_PATB; + pats = spapr->nested.ptcr & PTCR_PATS; + + /* Check if partition table is properly aligned */ + if (patb & MAKE_64BIT_MASK(0, pats + 12)) { + return false; + } + + /* Calculate number of entries */ + pats = 1ull << (pats + 12 - 4); + if (pats <= lpid) { + return false; + } + + /* Grab entry */ + patb += 16 * lpid; + entry->dw0 = ldq_phys(CPU(cpu)->as, patb); + entry->dw1 = ldq_phys(CPU(cpu)->as, patb + 8); + return true; +} + +static +SpaprMachineStateNestedGuest *spapr_get_nested_guest(SpaprMachineState *spapr, + target_ulong guestid) +{ + SpaprMachineStateNestedGuest *guest; + + guest = g_hash_table_lookup(spapr->nested.guests, GINT_TO_POINTER(guestid)); + return guest; +} + +bool spapr_get_pate_nested_papr(SpaprMachineState *spapr, PowerPCCPU *cpu, + target_ulong lpid, ppc_v3_pate_t *entry) +{ + SpaprMachineStateNestedGuest *guest; + assert(lpid != 0); + guest = spapr_get_nested_guest(spapr, lpid); + if (!guest) { + return false; + } + + entry->dw0 = guest->parttbl[0]; + entry->dw1 = guest->parttbl[1]; + return true; +} + +#define PRTS_MASK 0x1f + +static target_ulong h_set_ptbl(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + target_ulong ptcr = args[0]; + + if (!spapr_get_cap(spapr, SPAPR_CAP_NESTED_KVM_HV)) { + return H_FUNCTION; + } + + if ((ptcr & PRTS_MASK) + 12 - 4 > 12) { + return H_PARAMETER; + } + + spapr->nested.ptcr = ptcr; /* Save new partition table */ + + return H_SUCCESS; +} + +static target_ulong h_tlb_invalidate(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + /* + * The spapr virtual hypervisor nested HV implementation retains no L2 + * translation state except for TLB. And the TLB is always invalidated + * across L1<->L2 transitions, so nothing is required here. + */ + + return H_SUCCESS; +} + +static target_ulong h_copy_tofrom_guest(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + /* + * This HCALL is not required, L1 KVM will take a slow path and walk the + * page tables manually to do the data copy. + */ + return H_FUNCTION; +} + +static void nested_save_state(struct nested_ppc_state *save, PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + + memcpy(save->gpr, env->gpr, sizeof(save->gpr)); + + save->lr = env->lr; + save->ctr = env->ctr; + save->cfar = env->cfar; + save->msr = env->msr; + save->nip = env->nip; + + save->cr = ppc_get_cr(env); + save->xer = cpu_read_xer(env); + + save->lpcr = env->spr[SPR_LPCR]; + save->lpidr = env->spr[SPR_LPIDR]; + save->pcr = env->spr[SPR_PCR]; + save->dpdes = env->spr[SPR_DPDES]; + save->hfscr = env->spr[SPR_HFSCR]; + save->srr0 = env->spr[SPR_SRR0]; + save->srr1 = env->spr[SPR_SRR1]; + save->sprg0 = env->spr[SPR_SPRG0]; + save->sprg1 = env->spr[SPR_SPRG1]; + save->sprg2 = env->spr[SPR_SPRG2]; + save->sprg3 = env->spr[SPR_SPRG3]; + save->pidr = env->spr[SPR_BOOKS_PID]; + save->ppr = env->spr[SPR_PPR]; + + if (spapr_nested_api(spapr) == NESTED_API_PAPR) { + save->amor = env->spr[SPR_AMOR]; + save->dawr0 = env->spr[SPR_DAWR0]; + save->dawrx0 = env->spr[SPR_DAWRX0]; + save->ciabr = env->spr[SPR_CIABR]; + save->purr = env->spr[SPR_PURR]; + save->spurr = env->spr[SPR_SPURR]; + save->ic = env->spr[SPR_IC]; + save->vtb = env->spr[SPR_VTB]; + save->hdar = env->spr[SPR_HDAR]; + save->hdsisr = env->spr[SPR_HDSISR]; + save->heir = env->spr[SPR_HEIR]; + save->asdr = env->spr[SPR_ASDR]; + save->dawr1 = env->spr[SPR_DAWR1]; + save->dawrx1 = env->spr[SPR_DAWRX1]; + save->dexcr = env->spr[SPR_DEXCR]; + save->hdexcr = env->spr[SPR_HDEXCR]; + save->hashkeyr = env->spr[SPR_HASHKEYR]; + save->hashpkeyr = env->spr[SPR_HASHPKEYR]; + memcpy(save->vsr, env->vsr, sizeof(save->vsr)); + save->ebbhr = env->spr[SPR_EBBHR]; + save->tar = env->spr[SPR_TAR]; + save->ebbrr = env->spr[SPR_EBBRR]; + save->bescr = env->spr[SPR_BESCR]; + save->iamr = env->spr[SPR_IAMR]; + save->amr = env->spr[SPR_AMR]; + save->uamor = env->spr[SPR_UAMOR]; + save->dscr = env->spr[SPR_DSCR]; + save->fscr = env->spr[SPR_FSCR]; + save->pspb = env->spr[SPR_PSPB]; + save->ctrl = env->spr[SPR_CTRL]; + save->vrsave = env->spr[SPR_VRSAVE]; + save->dar = env->spr[SPR_DAR]; + save->dsisr = env->spr[SPR_DSISR]; + save->pmc1 = env->spr[SPR_POWER_PMC1]; + save->pmc2 = env->spr[SPR_POWER_PMC2]; + save->pmc3 = env->spr[SPR_POWER_PMC3]; + save->pmc4 = env->spr[SPR_POWER_PMC4]; + save->pmc5 = env->spr[SPR_POWER_PMC5]; + save->pmc6 = env->spr[SPR_POWER_PMC6]; + save->mmcr0 = env->spr[SPR_POWER_MMCR0]; + save->mmcr1 = env->spr[SPR_POWER_MMCR1]; + save->mmcr2 = env->spr[SPR_POWER_MMCR2]; + save->mmcra = env->spr[SPR_POWER_MMCRA]; + save->sdar = env->spr[SPR_POWER_SDAR]; + save->siar = env->spr[SPR_POWER_SIAR]; + save->sier = env->spr[SPR_POWER_SIER]; + save->vscr = ppc_get_vscr(env); + save->fpscr = env->fpscr; + } else if (spapr_nested_api(spapr) == NESTED_API_KVM_HV) { + save->tb_offset = env->tb_env->tb_offset; + } +} + +static void nested_post_load_state(CPUPPCState *env, CPUState *cs) +{ + /* + * compute hflags and possible interrupts. + */ + hreg_compute_hflags(env); + ppc_maybe_interrupt(env); + /* + * Nested HV does not tag TLB entries between L1 and L2, so must + * flush on transition. + */ + tlb_flush(cs); + env->reserve_addr = -1; /* Reset the reservation */ +} + +static void nested_load_state(PowerPCCPU *cpu, struct nested_ppc_state *load) +{ + CPUPPCState *env = &cpu->env; + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + + memcpy(env->gpr, load->gpr, sizeof(env->gpr)); + + env->lr = load->lr; + env->ctr = load->ctr; + env->cfar = load->cfar; + env->msr = load->msr; + env->nip = load->nip; + + ppc_set_cr(env, load->cr); + cpu_write_xer(env, load->xer); + + env->spr[SPR_LPCR] = load->lpcr; + env->spr[SPR_LPIDR] = load->lpidr; + env->spr[SPR_PCR] = load->pcr; + env->spr[SPR_DPDES] = load->dpdes; + env->spr[SPR_HFSCR] = load->hfscr; + env->spr[SPR_SRR0] = load->srr0; + env->spr[SPR_SRR1] = load->srr1; + env->spr[SPR_SPRG0] = load->sprg0; + env->spr[SPR_SPRG1] = load->sprg1; + env->spr[SPR_SPRG2] = load->sprg2; + env->spr[SPR_SPRG3] = load->sprg3; + env->spr[SPR_BOOKS_PID] = load->pidr; + env->spr[SPR_PPR] = load->ppr; + + if (spapr_nested_api(spapr) == NESTED_API_PAPR) { + env->spr[SPR_AMOR] = load->amor; + env->spr[SPR_DAWR0] = load->dawr0; + env->spr[SPR_DAWRX0] = load->dawrx0; + env->spr[SPR_CIABR] = load->ciabr; + env->spr[SPR_PURR] = load->purr; + env->spr[SPR_SPURR] = load->purr; + env->spr[SPR_IC] = load->ic; + env->spr[SPR_VTB] = load->vtb; + env->spr[SPR_HDAR] = load->hdar; + env->spr[SPR_HDSISR] = load->hdsisr; + env->spr[SPR_HEIR] = load->heir; + env->spr[SPR_ASDR] = load->asdr; + env->spr[SPR_DAWR1] = load->dawr1; + env->spr[SPR_DAWRX1] = load->dawrx1; + env->spr[SPR_DEXCR] = load->dexcr; + env->spr[SPR_HDEXCR] = load->hdexcr; + env->spr[SPR_HASHKEYR] = load->hashkeyr; + env->spr[SPR_HASHPKEYR] = load->hashpkeyr; + memcpy(env->vsr, load->vsr, sizeof(env->vsr)); + env->spr[SPR_EBBHR] = load->ebbhr; + env->spr[SPR_TAR] = load->tar; + env->spr[SPR_EBBRR] = load->ebbrr; + env->spr[SPR_BESCR] = load->bescr; + env->spr[SPR_IAMR] = load->iamr; + env->spr[SPR_AMR] = load->amr; + env->spr[SPR_UAMOR] = load->uamor; + env->spr[SPR_DSCR] = load->dscr; + env->spr[SPR_FSCR] = load->fscr; + env->spr[SPR_PSPB] = load->pspb; + env->spr[SPR_CTRL] = load->ctrl; + env->spr[SPR_VRSAVE] = load->vrsave; + env->spr[SPR_DAR] = load->dar; + env->spr[SPR_DSISR] = load->dsisr; + env->spr[SPR_POWER_PMC1] = load->pmc1; + env->spr[SPR_POWER_PMC2] = load->pmc2; + env->spr[SPR_POWER_PMC3] = load->pmc3; + env->spr[SPR_POWER_PMC4] = load->pmc4; + env->spr[SPR_POWER_PMC5] = load->pmc5; + env->spr[SPR_POWER_PMC6] = load->pmc6; + env->spr[SPR_POWER_MMCR0] = load->mmcr0; + env->spr[SPR_POWER_MMCR1] = load->mmcr1; + env->spr[SPR_POWER_MMCR2] = load->mmcr2; + env->spr[SPR_POWER_MMCRA] = load->mmcra; + env->spr[SPR_POWER_SDAR] = load->sdar; + env->spr[SPR_POWER_SIAR] = load->siar; + env->spr[SPR_POWER_SIER] = load->sier; + ppc_store_vscr(env, load->vscr); + ppc_store_fpscr(env, load->fpscr); + } else if (spapr_nested_api(spapr) == NESTED_API_KVM_HV) { + env->tb_env->tb_offset = load->tb_offset; + } +} + +/* + * When this handler returns, the environment is switched to the L2 guest + * and TCG begins running that. spapr_exit_nested() performs the switch from + * L2 back to L1 and returns from the H_ENTER_NESTED hcall. + */ +static target_ulong h_enter_nested(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); + struct nested_ppc_state l2_state; + target_ulong hv_ptr = args[0]; + target_ulong regs_ptr = args[1]; + target_ulong hdec, now = cpu_ppc_load_tbl(env); + target_ulong lpcr, lpcr_mask; + struct kvmppc_hv_guest_state *hvstate; + struct kvmppc_hv_guest_state hv_state; + struct kvmppc_pt_regs *regs; + hwaddr len; + + if (spapr->nested.ptcr == 0) { + return H_NOT_AVAILABLE; + } + + len = sizeof(*hvstate); + hvstate = address_space_map(CPU(cpu)->as, hv_ptr, &len, false, + MEMTXATTRS_UNSPECIFIED); + if (len != sizeof(*hvstate)) { + address_space_unmap(CPU(cpu)->as, hvstate, len, 0, false); + return H_PARAMETER; + } + + memcpy(&hv_state, hvstate, len); + + address_space_unmap(CPU(cpu)->as, hvstate, len, len, false); + + /* + * We accept versions 1 and 2. Version 2 fields are unused because TCG + * does not implement DAWR*. + */ + if (hv_state.version > HV_GUEST_STATE_VERSION) { + return H_PARAMETER; + } + + if (hv_state.lpid == 0) { + return H_PARAMETER; + } + + spapr_cpu->nested_host_state = g_try_new(struct nested_ppc_state, 1); + if (!spapr_cpu->nested_host_state) { + return H_NO_MEM; + } + + assert(env->spr[SPR_LPIDR] == 0); + assert(env->spr[SPR_DPDES] == 0); + nested_save_state(spapr_cpu->nested_host_state, cpu); + + len = sizeof(*regs); + regs = address_space_map(CPU(cpu)->as, regs_ptr, &len, false, + MEMTXATTRS_UNSPECIFIED); + if (!regs || len != sizeof(*regs)) { + address_space_unmap(CPU(cpu)->as, regs, len, 0, false); + g_free(spapr_cpu->nested_host_state); + return H_P2; + } + + len = sizeof(l2_state.gpr); + assert(len == sizeof(regs->gpr)); + memcpy(l2_state.gpr, regs->gpr, len); + + l2_state.lr = regs->link; + l2_state.ctr = regs->ctr; + l2_state.xer = regs->xer; + l2_state.cr = regs->ccr; + l2_state.msr = regs->msr; + l2_state.nip = regs->nip; + + address_space_unmap(CPU(cpu)->as, regs, len, len, false); + + l2_state.cfar = hv_state.cfar; + l2_state.lpidr = hv_state.lpid; + + lpcr_mask = LPCR_DPFD | LPCR_ILE | LPCR_AIL | LPCR_LD | LPCR_MER; + lpcr = (env->spr[SPR_LPCR] & ~lpcr_mask) | (hv_state.lpcr & lpcr_mask); + lpcr |= LPCR_HR | LPCR_UPRT | LPCR_GTSE | LPCR_HVICE | LPCR_HDICE; + lpcr &= ~LPCR_LPES0; + l2_state.lpcr = lpcr & pcc->lpcr_mask; + + l2_state.pcr = hv_state.pcr; + /* hv_state.amor is not used */ + l2_state.dpdes = hv_state.dpdes; + l2_state.hfscr = hv_state.hfscr; + /* TCG does not implement DAWR*, CIABR, PURR, SPURR, IC, VTB, HEIR SPRs*/ + l2_state.srr0 = hv_state.srr0; + l2_state.srr1 = hv_state.srr1; + l2_state.sprg0 = hv_state.sprg[0]; + l2_state.sprg1 = hv_state.sprg[1]; + l2_state.sprg2 = hv_state.sprg[2]; + l2_state.sprg3 = hv_state.sprg[3]; + l2_state.pidr = hv_state.pidr; + l2_state.ppr = hv_state.ppr; + l2_state.tb_offset = env->tb_env->tb_offset + hv_state.tb_offset; + + /* + * Switch to the nested guest environment and start the "hdec" timer. + */ + nested_load_state(cpu, &l2_state); + nested_post_load_state(env, cs); + + hdec = hv_state.hdec_expiry - now; + cpu_ppc_hdecr_init(env); + cpu_ppc_store_hdecr(env, hdec); + + /* + * The hv_state.vcpu_token is not needed. It is used by the KVM + * implementation to remember which L2 vCPU last ran on which physical + * CPU so as to invalidate process scope translations if it is moved + * between physical CPUs. For now TLBs are always flushed on L1<->L2 + * transitions so this is not a problem. + * + * Could validate that the same vcpu_token does not attempt to run on + * different L1 vCPUs at the same time, but that would be a L1 KVM bug + * and it's not obviously worth a new data structure to do it. + */ + + spapr_cpu->in_nested = true; + + /* + * The spapr hcall helper sets env->gpr[3] to the return value, but at + * this point the L1 is not returning from the hcall but rather we + * start running the L2, so r3 must not be clobbered, so return env->gpr[3] + * to leave it unchanged. + */ + return env->gpr[3]; +} + +static void spapr_exit_nested_hv(PowerPCCPU *cpu, int excp) +{ + CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); + struct nested_ppc_state l2_state; + target_ulong hv_ptr = spapr_cpu->nested_host_state->gpr[4]; + target_ulong regs_ptr = spapr_cpu->nested_host_state->gpr[5]; + target_ulong hsrr0, hsrr1, hdar, asdr, hdsisr; + struct kvmppc_hv_guest_state *hvstate; + struct kvmppc_pt_regs *regs; + hwaddr len; + + nested_save_state(&l2_state, cpu); + hsrr0 = env->spr[SPR_HSRR0]; + hsrr1 = env->spr[SPR_HSRR1]; + hdar = env->spr[SPR_HDAR]; + hdsisr = env->spr[SPR_HDSISR]; + asdr = env->spr[SPR_ASDR]; + + /* + * Switch back to the host environment (including for any error). + */ + assert(env->spr[SPR_LPIDR] != 0); + nested_load_state(cpu, spapr_cpu->nested_host_state); + nested_post_load_state(env, cs); + env->gpr[3] = env->excp_vectors[excp]; /* hcall return value */ + + cpu_ppc_hdecr_exit(env); + + spapr_cpu->in_nested = false; + + g_free(spapr_cpu->nested_host_state); + spapr_cpu->nested_host_state = NULL; + + len = sizeof(*hvstate); + hvstate = address_space_map(CPU(cpu)->as, hv_ptr, &len, true, + MEMTXATTRS_UNSPECIFIED); + if (len != sizeof(*hvstate)) { + address_space_unmap(CPU(cpu)->as, hvstate, len, 0, true); + env->gpr[3] = H_PARAMETER; + return; + } + + hvstate->cfar = l2_state.cfar; + hvstate->lpcr = l2_state.lpcr; + hvstate->pcr = l2_state.pcr; + hvstate->dpdes = l2_state.dpdes; + hvstate->hfscr = l2_state.hfscr; + + if (excp == POWERPC_EXCP_HDSI) { + hvstate->hdar = hdar; + hvstate->hdsisr = hdsisr; + hvstate->asdr = asdr; + } else if (excp == POWERPC_EXCP_HISI) { + hvstate->asdr = asdr; + } + + /* HEIR should be implemented for HV mode and saved here. */ + hvstate->srr0 = l2_state.srr0; + hvstate->srr1 = l2_state.srr1; + hvstate->sprg[0] = l2_state.sprg0; + hvstate->sprg[1] = l2_state.sprg1; + hvstate->sprg[2] = l2_state.sprg2; + hvstate->sprg[3] = l2_state.sprg3; + hvstate->pidr = l2_state.pidr; + hvstate->ppr = l2_state.ppr; + + /* Is it okay to specify write length larger than actual data written? */ + address_space_unmap(CPU(cpu)->as, hvstate, len, len, true); + + len = sizeof(*regs); + regs = address_space_map(CPU(cpu)->as, regs_ptr, &len, true, + MEMTXATTRS_UNSPECIFIED); + if (!regs || len != sizeof(*regs)) { + address_space_unmap(CPU(cpu)->as, regs, len, 0, true); + env->gpr[3] = H_P2; + return; + } + + len = sizeof(env->gpr); + assert(len == sizeof(regs->gpr)); + memcpy(regs->gpr, l2_state.gpr, len); + + regs->link = l2_state.lr; + regs->ctr = l2_state.ctr; + regs->xer = l2_state.xer; + regs->ccr = l2_state.cr; + + if (excp == POWERPC_EXCP_MCHECK || + excp == POWERPC_EXCP_RESET || + excp == POWERPC_EXCP_SYSCALL) { + regs->nip = l2_state.srr0; + regs->msr = l2_state.srr1 & env->msr_mask; + } else { + regs->nip = hsrr0; + regs->msr = hsrr1 & env->msr_mask; + } + + /* Is it okay to specify write length larger than actual data written? */ + address_space_unmap(CPU(cpu)->as, regs, len, len, true); +} + +static bool spapr_nested_vcpu_check(SpaprMachineStateNestedGuest *guest, + target_ulong vcpuid, bool inoutbuf) +{ + struct SpaprMachineStateNestedGuestVcpu *vcpu; + /* + * Perform sanity checks for the provided vcpuid of a guest. + * For now, ensure its valid, allocated and enabled for use. + */ + + if (vcpuid >= PAPR_NESTED_GUEST_VCPU_MAX) { + return false; + } + + if (!(vcpuid < guest->nr_vcpus)) { + return false; + } + + vcpu = &guest->vcpus[vcpuid]; + if (!vcpu->enabled) { + return false; + } + + if (!inoutbuf) { + return true; + } + + /* Check to see if the in/out buffers are registered */ + if (vcpu->runbufin.addr && vcpu->runbufout.addr) { + return true; + } + + return false; +} + +static void *get_vcpu_state_ptr(SpaprMachineStateNestedGuest *guest, + target_ulong vcpuid) +{ + assert(spapr_nested_vcpu_check(guest, vcpuid, false)); + return &guest->vcpus[vcpuid].state; +} + +static void *get_vcpu_ptr(SpaprMachineStateNestedGuest *guest, + target_ulong vcpuid) +{ + assert(spapr_nested_vcpu_check(guest, vcpuid, false)); + return &guest->vcpus[vcpuid]; +} + +static void *get_guest_ptr(SpaprMachineStateNestedGuest *guest, + target_ulong vcpuid) +{ + return guest; /* for GSBE_NESTED */ +} + +/* + * set=1 means the L1 is trying to set some state + * set=0 means the L1 is trying to get some state + */ +static void copy_state_8to8(void *a, void *b, bool set) +{ + /* set takes from the Big endian element_buf and sets internal buffer */ + + if (set) { + *(uint64_t *)a = be64_to_cpu(*(uint64_t *)b); + } else { + *(uint64_t *)b = cpu_to_be64(*(uint64_t *)a); + } +} + +static void copy_state_4to4(void *a, void *b, bool set) +{ + if (set) { + *(uint32_t *)a = be32_to_cpu(*(uint32_t *)b); + } else { + *(uint32_t *)b = cpu_to_be32(*((uint32_t *)a)); + } +} + +static void copy_state_16to16(void *a, void *b, bool set) +{ + uint64_t *src, *dst; + + if (set) { + src = b; + dst = a; + + dst[1] = be64_to_cpu(src[0]); + dst[0] = be64_to_cpu(src[1]); + } else { + src = a; + dst = b; + + dst[1] = cpu_to_be64(src[0]); + dst[0] = cpu_to_be64(src[1]); + } +} + +static void copy_state_4to8(void *a, void *b, bool set) +{ + if (set) { + *(uint64_t *)a = (uint64_t) be32_to_cpu(*(uint32_t *)b); + } else { + *(uint32_t *)b = cpu_to_be32((uint32_t) (*((uint64_t *)a))); + } +} + +static void copy_state_pagetbl(void *a, void *b, bool set) +{ + uint64_t *pagetbl; + uint64_t *buf; /* 3 double words */ + uint64_t rts; + + assert(set); + + pagetbl = a; + buf = b; + + *pagetbl = be64_to_cpu(buf[0]); + /* as per ISA section 6.7.6.1 */ + *pagetbl |= PATE0_HR; /* Host Radix bit is 1 */ + + /* RTS */ + rts = be64_to_cpu(buf[1]); + assert(rts == 52); + rts = rts - 31; /* since radix tree size = 2^(RTS+31) */ + *pagetbl |= ((rts & 0x7) << 5); /* RTS2 is bit 56:58 */ + *pagetbl |= (((rts >> 3) & 0x3) << 61); /* RTS1 is bit 1:2 */ + + /* RPDS {Size = 2^(RPDS+3) , RPDS >=5} */ + *pagetbl |= 63 - clz64(be64_to_cpu(buf[2])) - 3; +} + +static void copy_state_proctbl(void *a, void *b, bool set) +{ + uint64_t *proctbl; + uint64_t *buf; /* 2 double words */ + + assert(set); + + proctbl = a; + buf = b; + /* PRTB: Process Table Base */ + *proctbl = be64_to_cpu(buf[0]); + /* PRTS: Process Table Size = 2^(12+PRTS) */ + if (be64_to_cpu(buf[1]) == (1ULL << 12)) { + *proctbl |= 0; + } else if (be64_to_cpu(buf[1]) == (1ULL << 24)) { + *proctbl |= 12; + } else { + g_assert_not_reached(); + } +} + +static void copy_state_runbuf(void *a, void *b, bool set) +{ + uint64_t *buf; /* 2 double words */ + struct SpaprMachineStateNestedGuestVcpuRunBuf *runbuf; + + assert(set); + + runbuf = a; + buf = b; + + runbuf->addr = be64_to_cpu(buf[0]); + assert(runbuf->addr); + + /* per spec */ + assert(be64_to_cpu(buf[1]) <= 16384); + + /* + * This will also hit in the input buffer but should be fine for + * now. If not we can split this function. + */ + assert(be64_to_cpu(buf[1]) >= VCPU_OUT_BUF_MIN_SZ); + + runbuf->size = be64_to_cpu(buf[1]); +} + +/* tell the L1 how big we want the output vcpu run buffer */ +static void out_buf_min_size(void *a, void *b, bool set) +{ + uint64_t *buf; /* 1 double word */ + + assert(!set); + + buf = b; + + buf[0] = cpu_to_be64(VCPU_OUT_BUF_MIN_SZ); +} + +static void copy_logical_pvr(void *a, void *b, bool set) +{ + SpaprMachineStateNestedGuest *guest; + uint32_t *buf; /* 1 word */ + uint32_t *pvr_logical_ptr; + uint32_t pvr_logical; + target_ulong pcr = 0; + + pvr_logical_ptr = a; + buf = b; + + if (!set) { + buf[0] = cpu_to_be32(*pvr_logical_ptr); + return; + } + + pvr_logical = be32_to_cpu(buf[0]); + + *pvr_logical_ptr = pvr_logical; + + if (*pvr_logical_ptr) { + switch (*pvr_logical_ptr) { + case CPU_POWERPC_LOGICAL_3_10: + pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00; + break; + case CPU_POWERPC_LOGICAL_3_00: + pcr = PCR_COMPAT_3_00; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "Could not set PCR for LPVR=0x%08x\n", + *pvr_logical_ptr); + return; + } + } + + guest = container_of(pvr_logical_ptr, + struct SpaprMachineStateNestedGuest, + pvr_logical); + for (int i = 0; i < guest->nr_vcpus; i++) { + guest->vcpus[i].state.pcr = ~pcr | HVMASK_PCR; + } +} + +static void copy_tb_offset(void *a, void *b, bool set) +{ + SpaprMachineStateNestedGuest *guest; + uint64_t *buf; /* 1 double word */ + uint64_t *tb_offset_ptr; + uint64_t tb_offset; + + tb_offset_ptr = a; + buf = b; + + if (!set) { + buf[0] = cpu_to_be64(*tb_offset_ptr); + return; + } + + tb_offset = be64_to_cpu(buf[0]); + /* need to copy this to the individual tb_offset for each vcpu */ + guest = container_of(tb_offset_ptr, + struct SpaprMachineStateNestedGuest, + tb_offset); + for (int i = 0; i < guest->nr_vcpus; i++) { + guest->vcpus[i].tb_offset = tb_offset; + } +} + +static void copy_state_hdecr(void *a, void *b, bool set) +{ + uint64_t *buf; /* 1 double word */ + uint64_t *hdecr_expiry_tb; + + hdecr_expiry_tb = a; + buf = b; + + if (!set) { + buf[0] = cpu_to_be64(*hdecr_expiry_tb); + return; + } + + *hdecr_expiry_tb = be64_to_cpu(buf[0]); +} + +struct guest_state_element_type guest_state_element_types[] = { + GUEST_STATE_ELEMENT_NOP(GSB_HV_VCPU_IGNORED_ID, 0), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR0, gpr[0]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR1, gpr[1]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR2, gpr[2]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR3, gpr[3]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR4, gpr[4]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR5, gpr[5]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR6, gpr[6]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR7, gpr[7]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR8, gpr[8]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR9, gpr[9]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR10, gpr[10]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR11, gpr[11]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR12, gpr[12]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR13, gpr[13]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR14, gpr[14]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR15, gpr[15]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR16, gpr[16]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR17, gpr[17]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR18, gpr[18]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR19, gpr[19]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR20, gpr[20]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR21, gpr[21]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR22, gpr[22]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR23, gpr[23]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR24, gpr[24]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR25, gpr[25]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR26, gpr[26]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR27, gpr[27]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR28, gpr[28]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR29, gpr[29]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR30, gpr[30]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_GPR31, gpr[31]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_NIA, nip), + GSE_ENV_DWM(GSB_VCPU_SPR_MSR, msr, HVMASK_MSR), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_CTR, ctr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_LR, lr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_XER, xer), + GUEST_STATE_ELEMENT_ENV_WW(GSB_VCPU_SPR_CR, cr), + GUEST_STATE_ELEMENT_NOP_DW(GSB_VCPU_SPR_MMCR3), + GUEST_STATE_ELEMENT_NOP_DW(GSB_VCPU_SPR_SIER2), + GUEST_STATE_ELEMENT_NOP_DW(GSB_VCPU_SPR_SIER3), + GUEST_STATE_ELEMENT_NOP_W(GSB_VCPU_SPR_WORT), + GSE_ENV_DWM(GSB_VCPU_SPR_LPCR, lpcr, HVMASK_LPCR), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_AMOR, amor), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_HFSCR, hfscr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_DAWR0, dawr0), + GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_DAWRX0, dawrx0), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_CIABR, ciabr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_PURR, purr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SPURR, spurr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_IC, ic), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_VTB, vtb), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_HDAR, hdar), + GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_HDSISR, hdsisr), + GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_HEIR, heir), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_ASDR, asdr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SRR0, srr0), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SRR1, srr1), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SPRG0, sprg0), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SPRG1, sprg1), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SPRG2, sprg2), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SPRG3, sprg3), + GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PIDR, pidr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_CFAR, cfar), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_PPR, ppr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_DAWR1, dawr1), + GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_DAWRX1, dawrx1), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_DEXCR, dexcr), + GSE_ENV_DWM(GSB_VCPU_SPR_HDEXCR, hdexcr, HVMASK_HDEXCR), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_HASHKEYR, hashkeyr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_HASHPKEYR, hashpkeyr), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR0, vsr[0]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR1, vsr[1]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR2, vsr[2]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR3, vsr[3]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR4, vsr[4]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR5, vsr[5]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR6, vsr[6]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR7, vsr[7]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR8, vsr[8]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR9, vsr[9]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR10, vsr[10]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR11, vsr[11]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR12, vsr[12]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR13, vsr[13]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR14, vsr[14]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR15, vsr[15]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR16, vsr[16]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR17, vsr[17]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR18, vsr[18]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR19, vsr[19]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR20, vsr[20]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR21, vsr[21]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR22, vsr[22]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR23, vsr[23]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR24, vsr[24]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR25, vsr[25]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR26, vsr[26]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR27, vsr[27]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR28, vsr[28]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR29, vsr[29]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR30, vsr[30]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR31, vsr[31]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR32, vsr[32]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR33, vsr[33]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR34, vsr[34]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR35, vsr[35]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR36, vsr[36]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR37, vsr[37]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR38, vsr[38]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR39, vsr[39]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR40, vsr[40]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR41, vsr[41]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR42, vsr[42]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR43, vsr[43]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR44, vsr[44]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR45, vsr[45]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR46, vsr[46]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR47, vsr[47]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR48, vsr[48]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR49, vsr[49]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR50, vsr[50]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR51, vsr[51]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR52, vsr[52]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR53, vsr[53]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR54, vsr[54]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR55, vsr[55]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR56, vsr[56]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR57, vsr[57]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR58, vsr[58]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR59, vsr[59]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR60, vsr[60]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR61, vsr[61]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR62, vsr[62]), + GUEST_STATE_ELEMENT_ENV_QW(GSB_VCPU_SPR_VSR63, vsr[63]), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_EBBHR, ebbhr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_TAR, tar), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_EBBRR, ebbrr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_BESCR, bescr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_IAMR, iamr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_AMR, amr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_UAMOR, uamor), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_DSCR, dscr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_FSCR, fscr), + GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PSPB, pspb), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_CTRL, ctrl), + GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_VRSAVE, vrsave), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_DAR, dar), + GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_DSISR, dsisr), + GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PMC1, pmc1), + GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PMC2, pmc2), + GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PMC3, pmc3), + GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PMC4, pmc4), + GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PMC5, pmc5), + GUEST_STATE_ELEMENT_ENV_W(GSB_VCPU_SPR_PMC6, pmc6), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_MMCR0, mmcr0), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_MMCR1, mmcr1), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_MMCR2, mmcr2), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_MMCRA, mmcra), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SDAR , sdar), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SIAR , siar), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_SIER , sier), + GUEST_STATE_ELEMENT_ENV_WW(GSB_VCPU_SPR_VSCR, vscr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_SPR_FPSCR, fpscr), + GUEST_STATE_ELEMENT_ENV_DW(GSB_VCPU_DEC_EXPIRE_TB, dec_expiry_tb), + GSBE_NESTED(GSB_PART_SCOPED_PAGETBL, 0x18, parttbl[0], copy_state_pagetbl), + GSBE_NESTED(GSB_PROCESS_TBL, 0x10, parttbl[1], copy_state_proctbl), + GSBE_NESTED(GSB_VCPU_LPVR, 0x4, pvr_logical, copy_logical_pvr), + GSBE_NESTED_MSK(GSB_TB_OFFSET, 0x8, tb_offset, copy_tb_offset, + HVMASK_TB_OFFSET), + GSBE_NESTED_VCPU(GSB_VCPU_IN_BUFFER, 0x10, runbufin, copy_state_runbuf), + GSBE_NESTED_VCPU(GSB_VCPU_OUT_BUFFER, 0x10, runbufout, copy_state_runbuf), + GSBE_NESTED_VCPU(GSB_VCPU_OUT_BUF_MIN_SZ, 0x8, runbufout, out_buf_min_size), + GSBE_NESTED_VCPU(GSB_VCPU_HDEC_EXPIRY_TB, 0x8, hdecr_expiry_tb, + copy_state_hdecr) +}; + +void spapr_nested_gsb_init(void) +{ + struct guest_state_element_type *type; + + /* Init the guest state elements lookup table, flags for now */ + for (int i = 0; i < ARRAY_SIZE(guest_state_element_types); i++) { + type = &guest_state_element_types[i]; + + assert(type->id <= GSB_LAST); + if (type->id >= GSB_VCPU_SPR_HDAR) + /* 0xf000 - 0xf005 Thread + RO */ + type->flags = GUEST_STATE_ELEMENT_TYPE_FLAG_READ_ONLY; + else if (type->id >= GSB_VCPU_IN_BUFFER) + /* 0x0c00 - 0xf000 Thread + RW */ + type->flags = 0; + else if (type->id >= GSB_VCPU_LPVR) + /* 0x0003 - 0x0bff Guest + RW */ + type->flags = GUEST_STATE_ELEMENT_TYPE_FLAG_GUEST_WIDE; + else if (type->id >= GSB_HV_VCPU_STATE_SIZE) + /* 0x0001 - 0x0002 Guest + RO */ + type->flags = GUEST_STATE_ELEMENT_TYPE_FLAG_READ_ONLY | + GUEST_STATE_ELEMENT_TYPE_FLAG_GUEST_WIDE; + } +} + +static struct guest_state_element *guest_state_element_next( + struct guest_state_element *element, + int64_t *len, + int64_t *num_elements) +{ + uint16_t size; + + /* size is of element->value[] only. Not whole guest_state_element */ + size = be16_to_cpu(element->size); + + if (len) { + *len -= size + offsetof(struct guest_state_element, value); + } + + if (num_elements) { + *num_elements -= 1; + } + + return (struct guest_state_element *)(element->value + size); +} + +static +struct guest_state_element_type *guest_state_element_type_find(uint16_t id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(guest_state_element_types); i++) + if (id == guest_state_element_types[i].id) { + return &guest_state_element_types[i]; + } + + return NULL; +} + +static void log_element(struct guest_state_element *element, + struct guest_state_request *gsr) +{ + qemu_log_mask(LOG_GUEST_ERROR, "h_guest_%s_state id:0x%04x size:0x%04x", + gsr->flags & GUEST_STATE_REQUEST_SET ? "set" : "get", + be16_to_cpu(element->id), be16_to_cpu(element->size)); + qemu_log_mask(LOG_GUEST_ERROR, "buf:0x%016"PRIx64" ...\n", + be64_to_cpu(*(uint64_t *)element->value)); +} + +static bool guest_state_request_check(struct guest_state_request *gsr) +{ + int64_t num_elements, len = gsr->len; + struct guest_state_buffer *gsb = gsr->gsb; + struct guest_state_element *element; + struct guest_state_element_type *type; + uint16_t id, size; + + /* gsb->num_elements = 0 == 32 bits long */ + assert(len >= 4); + + num_elements = be32_to_cpu(gsb->num_elements); + element = gsb->elements; + len -= sizeof(gsb->num_elements); + + /* Walk the buffer to validate the length */ + while (num_elements) { + + id = be16_to_cpu(element->id); + size = be16_to_cpu(element->size); + + if (false) { + log_element(element, gsr); + } + /* buffer size too small */ + if (len < 0) { + return false; + } + + type = guest_state_element_type_find(id); + if (!type) { + qemu_log_mask(LOG_GUEST_ERROR, "Element ID %04x unknown\n", id); + log_element(element, gsr); + return false; + } + + if (id == GSB_HV_VCPU_IGNORED_ID) { + goto next_element; + } + + if (size != type->size) { + qemu_log_mask(LOG_GUEST_ERROR, "Size mismatch. Element ID:%04x." + "Size Exp:%i Got:%i\n", id, type->size, size); + log_element(element, gsr); + return false; + } + + if ((type->flags & GUEST_STATE_ELEMENT_TYPE_FLAG_READ_ONLY) && + (gsr->flags & GUEST_STATE_REQUEST_SET)) { + qemu_log_mask(LOG_GUEST_ERROR, "Trying to set a read-only Element " + "ID:%04x.\n", id); + return false; + } + + if (type->flags & GUEST_STATE_ELEMENT_TYPE_FLAG_GUEST_WIDE) { + /* guest wide element type */ + if (!(gsr->flags & GUEST_STATE_REQUEST_GUEST_WIDE)) { + qemu_log_mask(LOG_GUEST_ERROR, "trying to set a guest wide " + "Element ID:%04x.\n", id); + return false; + } + } else { + /* thread wide element type */ + if (gsr->flags & GUEST_STATE_REQUEST_GUEST_WIDE) { + qemu_log_mask(LOG_GUEST_ERROR, "trying to set a thread wide " + "Element ID:%04x.\n", id); + return false; + } + } +next_element: + element = guest_state_element_next(element, &len, &num_elements); + + } + return true; +} + +static bool is_gsr_invalid(struct guest_state_request *gsr, + struct guest_state_element *element, + struct guest_state_element_type *type) +{ + if ((gsr->flags & GUEST_STATE_REQUEST_SET) && + (*(uint64_t *)(element->value) & ~(type->mask))) { + log_element(element, gsr); + qemu_log_mask(LOG_GUEST_ERROR, "L1 can't set reserved bits " + "(allowed mask: 0x%08"PRIx64")\n", type->mask); + return true; + } + return false; +} + +static target_ulong h_guest_get_capabilities(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + CPUPPCState *env = &cpu->env; + target_ulong flags = args[0]; + + if (flags) { /* don't handle any flags capabilities for now */ + return H_PARAMETER; + } + + /* P10 capabilities */ + if (ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_10, 0, + spapr->max_compat_pvr)) { + env->gpr[4] |= H_GUEST_CAPABILITIES_P10_MODE; + } + + /* P9 capabilities */ + if (ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, 0, + spapr->max_compat_pvr)) { + env->gpr[4] |= H_GUEST_CAPABILITIES_P9_MODE; + } + + return H_SUCCESS; +} + +static target_ulong h_guest_set_capabilities(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + CPUPPCState *env = &cpu->env; + target_ulong flags = args[0]; + target_ulong capabilities = args[1]; + env->gpr[4] = 0; + + if (flags) { /* don't handle any flags capabilities for now */ + return H_PARAMETER; + } + + if (capabilities & H_GUEST_CAPABILITIES_COPY_MEM) { + env->gpr[4] = 1; + return H_P2; /* isn't supported */ + } + + /* + * If there are no capabilities configured, set the R5 to the index of + * the first supported Power Processor Mode + */ + if (!capabilities) { + env->gpr[4] = 1; + + /* set R5 to the first supported Power Processor Mode */ + if (ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_10, 0, + spapr->max_compat_pvr)) { + env->gpr[5] = H_GUEST_CAP_P10_MODE_BMAP; + } else if (ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, 0, + spapr->max_compat_pvr)) { + env->gpr[5] = H_GUEST_CAP_P9_MODE_BMAP; + } + + return H_P2; + } + + /* + * If an invalid capability is set, R5 should contain the index of the + * invalid capability bit + */ + if (capabilities & ~H_GUEST_CAP_VALID_MASK) { + env->gpr[4] = 1; + + /* Set R5 to the index of the invalid capability */ + env->gpr[5] = 63 - ctz64(capabilities); + + return H_P2; + } + + if (!spapr->nested.capabilities_set) { + spapr->nested.capabilities_set = true; + spapr->nested.pvr_base = env->spr[SPR_PVR]; + return H_SUCCESS; + } else { + return H_STATE; + } +} + +static void +destroy_guest_helper(gpointer value) +{ + struct SpaprMachineStateNestedGuest *guest = value; + g_free(guest->vcpus); + g_free(guest); +} + +static target_ulong h_guest_create(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + CPUPPCState *env = &cpu->env; + target_ulong flags = args[0]; + target_ulong continue_token = args[1]; + uint64_t guestid; + int nguests = 0; + struct SpaprMachineStateNestedGuest *guest; + + if (flags) { /* don't handle any flags for now */ + return H_UNSUPPORTED_FLAG; + } + + if (continue_token != -1) { + return H_P2; + } + + if (!spapr->nested.capabilities_set) { + return H_STATE; + } + + if (!spapr->nested.guests) { + spapr->nested.guests = g_hash_table_new_full(NULL, + NULL, + NULL, + destroy_guest_helper); + } + + nguests = g_hash_table_size(spapr->nested.guests); + + if (nguests == PAPR_NESTED_GUEST_MAX) { + return H_NO_MEM; + } + + /* Lookup for available guestid */ + for (guestid = 1; guestid < PAPR_NESTED_GUEST_MAX; guestid++) { + if (!(g_hash_table_lookup(spapr->nested.guests, + GINT_TO_POINTER(guestid)))) { + break; + } + } + + if (guestid == PAPR_NESTED_GUEST_MAX) { + return H_NO_MEM; + } + + guest = g_try_new0(struct SpaprMachineStateNestedGuest, 1); + if (!guest) { + return H_NO_MEM; + } + + guest->pvr_logical = spapr->nested.pvr_base; + g_hash_table_insert(spapr->nested.guests, GINT_TO_POINTER(guestid), guest); + env->gpr[4] = guestid; + + return H_SUCCESS; +} + +static target_ulong h_guest_delete(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + target_ulong flags = args[0]; + target_ulong guestid = args[1]; + struct SpaprMachineStateNestedGuest *guest; + + /* + * handle flag deleteAllGuests, if set: + * guestid is ignored and all guests are deleted + * + */ + if (flags & ~H_GUEST_DELETE_ALL_FLAG) { + return H_UNSUPPORTED_FLAG; /* other flag bits reserved */ + } else if (flags & H_GUEST_DELETE_ALL_FLAG) { + g_hash_table_destroy(spapr->nested.guests); + return H_SUCCESS; + } + + guest = g_hash_table_lookup(spapr->nested.guests, GINT_TO_POINTER(guestid)); + if (!guest) { + return H_P2; + } + + g_hash_table_remove(spapr->nested.guests, GINT_TO_POINTER(guestid)); + + return H_SUCCESS; +} + +static target_ulong h_guest_create_vcpu(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + target_ulong flags = args[0]; + target_ulong guestid = args[1]; + target_ulong vcpuid = args[2]; + SpaprMachineStateNestedGuest *guest; + + if (flags) { /* don't handle any flags for now */ + return H_UNSUPPORTED_FLAG; + } + + guest = spapr_get_nested_guest(spapr, guestid); + if (!guest) { + return H_P2; + } + + if (vcpuid < guest->nr_vcpus) { + qemu_log_mask(LOG_UNIMP, "vcpuid " TARGET_FMT_ld " already in use.", + vcpuid); + return H_IN_USE; + } + /* linear vcpuid allocation only */ + assert(vcpuid == guest->nr_vcpus); + + if (guest->nr_vcpus >= PAPR_NESTED_GUEST_VCPU_MAX) { + return H_P3; + } + + SpaprMachineStateNestedGuestVcpu *vcpus, *curr_vcpu; + vcpus = g_try_renew(struct SpaprMachineStateNestedGuestVcpu, + guest->vcpus, + guest->nr_vcpus + 1); + if (!vcpus) { + return H_NO_MEM; + } + guest->vcpus = vcpus; + curr_vcpu = &vcpus[guest->nr_vcpus]; + memset(curr_vcpu, 0, sizeof(SpaprMachineStateNestedGuestVcpu)); + + curr_vcpu->enabled = true; + guest->nr_vcpus++; + + return H_SUCCESS; +} + +static target_ulong getset_state(SpaprMachineStateNestedGuest *guest, + uint64_t vcpuid, + struct guest_state_request *gsr) +{ + void *ptr; + uint16_t id; + struct guest_state_element *element; + struct guest_state_element_type *type; + int64_t lenleft, num_elements; + + lenleft = gsr->len; + + if (!guest_state_request_check(gsr)) { + return H_P3; + } + + num_elements = be32_to_cpu(gsr->gsb->num_elements); + element = gsr->gsb->elements; + /* Process the elements */ + while (num_elements) { + type = NULL; + /* log_element(element, gsr); */ + + id = be16_to_cpu(element->id); + if (id == GSB_HV_VCPU_IGNORED_ID) { + goto next_element; + } + + type = guest_state_element_type_find(id); + assert(type); + + /* Get pointer to guest data to get/set */ + if (type->location && type->copy) { + ptr = type->location(guest, vcpuid); + assert(ptr); + if (!~(type->mask) && is_gsr_invalid(gsr, element, type)) { + return H_INVALID_ELEMENT_VALUE; + } + type->copy(ptr + type->offset, element->value, + gsr->flags & GUEST_STATE_REQUEST_SET ? true : false); + } + +next_element: + element = guest_state_element_next(element, &lenleft, &num_elements); + } + + return H_SUCCESS; +} + +static target_ulong map_and_getset_state(PowerPCCPU *cpu, + SpaprMachineStateNestedGuest *guest, + uint64_t vcpuid, + struct guest_state_request *gsr) +{ + target_ulong rc; + int64_t len; + bool is_write; + + len = gsr->len; + /* only get_state would require write access to the provided buffer */ + is_write = (gsr->flags & GUEST_STATE_REQUEST_SET) ? false : true; + gsr->gsb = address_space_map(CPU(cpu)->as, gsr->buf, (uint64_t *)&len, + is_write, MEMTXATTRS_UNSPECIFIED); + if (!gsr->gsb) { + rc = H_P3; + goto out1; + } + + if (len != gsr->len) { + rc = H_P3; + goto out1; + } + + rc = getset_state(guest, vcpuid, gsr); + +out1: + address_space_unmap(CPU(cpu)->as, gsr->gsb, len, is_write, len); + return rc; +} + +static target_ulong h_guest_getset_state(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong *args, + bool set) +{ + target_ulong flags = args[0]; + target_ulong lpid = args[1]; + target_ulong vcpuid = args[2]; + target_ulong buf = args[3]; + target_ulong buflen = args[4]; + struct guest_state_request gsr; + SpaprMachineStateNestedGuest *guest; + + guest = spapr_get_nested_guest(spapr, lpid); + if (!guest) { + return H_P2; + } + gsr.buf = buf; + assert(buflen <= GSB_MAX_BUF_SIZE); + gsr.len = buflen; + gsr.flags = 0; + if (flags & H_GUEST_GETSET_STATE_FLAG_GUEST_WIDE) { + gsr.flags |= GUEST_STATE_REQUEST_GUEST_WIDE; + } + if (flags & ~H_GUEST_GETSET_STATE_FLAG_GUEST_WIDE) { + return H_PARAMETER; /* flag not supported yet */ + } + + if (set) { + gsr.flags |= GUEST_STATE_REQUEST_SET; + } + return map_and_getset_state(cpu, guest, vcpuid, &gsr); +} + +static target_ulong h_guest_set_state(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + return h_guest_getset_state(cpu, spapr, args, true); +} + +static target_ulong h_guest_get_state(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + return h_guest_getset_state(cpu, spapr, args, false); +} + +static void exit_nested_store_l2(PowerPCCPU *cpu, int excp, + SpaprMachineStateNestedGuestVcpu *vcpu) +{ + CPUPPCState *env = &cpu->env; + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); + target_ulong now, hdar, hdsisr, asdr; + + assert(sizeof(env->gpr) == sizeof(vcpu->state.gpr)); /* sanity check */ + + now = cpu_ppc_load_tbl(env); /* L2 timebase */ + now -= vcpu->tb_offset; /* L1 timebase */ + vcpu->state.dec_expiry_tb = now - cpu_ppc_load_decr(env); + cpu_ppc_store_decr(env, spapr_cpu->nested_host_state->dec_expiry_tb - now); + /* backup hdar, hdsisr, asdr if reqd later below */ + hdar = vcpu->state.hdar; + hdsisr = vcpu->state.hdsisr; + asdr = vcpu->state.asdr; + + nested_save_state(&vcpu->state, cpu); + + if (excp == POWERPC_EXCP_MCHECK || + excp == POWERPC_EXCP_RESET || + excp == POWERPC_EXCP_SYSCALL) { + vcpu->state.nip = env->spr[SPR_SRR0]; + vcpu->state.msr = env->spr[SPR_SRR1] & env->msr_mask; + } else { + vcpu->state.nip = env->spr[SPR_HSRR0]; + vcpu->state.msr = env->spr[SPR_HSRR1] & env->msr_mask; + } + + /* hdar, hdsisr, asdr should be retained unless certain exceptions */ + if ((excp != POWERPC_EXCP_HDSI) && (excp != POWERPC_EXCP_HISI)) { + vcpu->state.asdr = asdr; + } else if (excp != POWERPC_EXCP_HDSI) { + vcpu->state.hdar = hdar; + vcpu->state.hdsisr = hdsisr; + } +} + +static int get_exit_ids(uint64_t srr0, uint16_t ids[16]) +{ + int nr; + + switch (srr0) { + case 0xc00: + nr = 10; + ids[0] = GSB_VCPU_GPR3; + ids[1] = GSB_VCPU_GPR4; + ids[2] = GSB_VCPU_GPR5; + ids[3] = GSB_VCPU_GPR6; + ids[4] = GSB_VCPU_GPR7; + ids[5] = GSB_VCPU_GPR8; + ids[6] = GSB_VCPU_GPR9; + ids[7] = GSB_VCPU_GPR10; + ids[8] = GSB_VCPU_GPR11; + ids[9] = GSB_VCPU_GPR12; + break; + case 0xe00: + nr = 5; + ids[0] = GSB_VCPU_SPR_HDAR; + ids[1] = GSB_VCPU_SPR_HDSISR; + ids[2] = GSB_VCPU_SPR_ASDR; + ids[3] = GSB_VCPU_SPR_NIA; + ids[4] = GSB_VCPU_SPR_MSR; + break; + case 0xe20: + nr = 4; + ids[0] = GSB_VCPU_SPR_HDAR; + ids[1] = GSB_VCPU_SPR_ASDR; + ids[2] = GSB_VCPU_SPR_NIA; + ids[3] = GSB_VCPU_SPR_MSR; + break; + case 0xe40: + nr = 3; + ids[0] = GSB_VCPU_SPR_HEIR; + ids[1] = GSB_VCPU_SPR_NIA; + ids[2] = GSB_VCPU_SPR_MSR; + break; + case 0xf80: + nr = 3; + ids[0] = GSB_VCPU_SPR_HFSCR; + ids[1] = GSB_VCPU_SPR_NIA; + ids[2] = GSB_VCPU_SPR_MSR; + break; + default: + nr = 0; + break; + } + + return nr; +} + +static void exit_process_output_buffer(PowerPCCPU *cpu, + SpaprMachineStateNestedGuest *guest, + target_ulong vcpuid, + target_ulong *r3) +{ + SpaprMachineStateNestedGuestVcpu *vcpu = &guest->vcpus[vcpuid]; + struct guest_state_request gsr; + struct guest_state_buffer *gsb; + struct guest_state_element *element; + struct guest_state_element_type *type; + int exit_id_count = 0; + uint16_t exit_cause_ids[16]; + hwaddr len; + + len = vcpu->runbufout.size; + gsb = address_space_map(CPU(cpu)->as, vcpu->runbufout.addr, &len, true, + MEMTXATTRS_UNSPECIFIED); + if (!gsb || len != vcpu->runbufout.size) { + address_space_unmap(CPU(cpu)->as, gsb, len, true, len); + *r3 = H_P2; + return; + } + + exit_id_count = get_exit_ids(*r3, exit_cause_ids); + + /* Create a buffer of elements to send back */ + gsb->num_elements = cpu_to_be32(exit_id_count); + element = gsb->elements; + for (int i = 0; i < exit_id_count; i++) { + type = guest_state_element_type_find(exit_cause_ids[i]); + assert(type); + element->id = cpu_to_be16(exit_cause_ids[i]); + element->size = cpu_to_be16(type->size); + element = guest_state_element_next(element, NULL, NULL); + } + gsr.gsb = gsb; + gsr.len = VCPU_OUT_BUF_MIN_SZ; + gsr.flags = 0; /* get + never guest wide */ + getset_state(guest, vcpuid, &gsr); + + address_space_unmap(CPU(cpu)->as, gsb, len, true, len); + return; +} + +static +void spapr_exit_nested_papr(SpaprMachineState *spapr, PowerPCCPU *cpu, int excp) +{ + CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); + target_ulong r3_return = env->excp_vectors[excp]; /* hcall return value */ + target_ulong lpid = 0, vcpuid = 0; + struct SpaprMachineStateNestedGuestVcpu *vcpu = NULL; + struct SpaprMachineStateNestedGuest *guest = NULL; + + lpid = spapr_cpu->nested_host_state->gpr[5]; + vcpuid = spapr_cpu->nested_host_state->gpr[6]; + guest = spapr_get_nested_guest(spapr, lpid); + assert(guest); + spapr_nested_vcpu_check(guest, vcpuid, false); + vcpu = &guest->vcpus[vcpuid]; + + exit_nested_store_l2(cpu, excp, vcpu); + /* do the output buffer for run_vcpu*/ + exit_process_output_buffer(cpu, guest, vcpuid, &r3_return); + + assert(env->spr[SPR_LPIDR] != 0); + nested_load_state(cpu, spapr_cpu->nested_host_state); + cpu_ppc_decrease_tb_by_offset(env, vcpu->tb_offset); + env->gpr[3] = H_SUCCESS; + env->gpr[4] = r3_return; + nested_post_load_state(env, cs); + cpu_ppc_hdecr_exit(env); + + spapr_cpu->in_nested = false; + g_free(spapr_cpu->nested_host_state); + spapr_cpu->nested_host_state = NULL; +} + +void spapr_exit_nested(PowerPCCPU *cpu, int excp) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); + + assert(spapr_cpu->in_nested); + if (spapr_nested_api(spapr) == NESTED_API_KVM_HV) { + spapr_exit_nested_hv(cpu, excp); + } else if (spapr_nested_api(spapr) == NESTED_API_PAPR) { + spapr_exit_nested_papr(spapr, cpu, excp); + } else { + g_assert_not_reached(); + } +} + +static void nested_papr_load_l2(PowerPCCPU *cpu, + CPUPPCState *env, + SpaprMachineStateNestedGuestVcpu *vcpu, + target_ulong now) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + target_ulong lpcr, lpcr_mask, hdec; + lpcr_mask = LPCR_DPFD | LPCR_ILE | LPCR_AIL | LPCR_LD | LPCR_MER; + + assert(vcpu); + assert(sizeof(env->gpr) == sizeof(vcpu->state.gpr)); + nested_load_state(cpu, &vcpu->state); + lpcr = (env->spr[SPR_LPCR] & ~lpcr_mask) | + (vcpu->state.lpcr & lpcr_mask); + lpcr |= LPCR_HR | LPCR_UPRT | LPCR_GTSE | LPCR_HVICE | LPCR_HDICE; + lpcr &= ~LPCR_LPES0; + env->spr[SPR_LPCR] = lpcr & pcc->lpcr_mask; + + hdec = vcpu->hdecr_expiry_tb - now; + cpu_ppc_store_decr(env, vcpu->state.dec_expiry_tb - now); + cpu_ppc_hdecr_init(env); + cpu_ppc_store_hdecr(env, hdec); + + cpu_ppc_increase_tb_by_offset(env, vcpu->tb_offset); +} + +static void nested_papr_run_vcpu(PowerPCCPU *cpu, + uint64_t lpid, + SpaprMachineStateNestedGuestVcpu *vcpu) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); + target_ulong now = cpu_ppc_load_tbl(env); + + assert(env->spr[SPR_LPIDR] == 0); + assert(spapr->nested.api); /* ensure API version is initialized */ + spapr_cpu->nested_host_state = g_try_new(struct nested_ppc_state, 1); + assert(spapr_cpu->nested_host_state); + nested_save_state(spapr_cpu->nested_host_state, cpu); + spapr_cpu->nested_host_state->dec_expiry_tb = now - cpu_ppc_load_decr(env); + nested_papr_load_l2(cpu, env, vcpu, now); + env->spr[SPR_LPIDR] = lpid; /* post load l2 */ + + spapr_cpu->in_nested = true; + nested_post_load_state(env, cs); +} + +static target_ulong h_guest_run_vcpu(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + CPUPPCState *env = &cpu->env; + target_ulong flags = args[0]; + target_ulong lpid = args[1]; + target_ulong vcpuid = args[2]; + struct SpaprMachineStateNestedGuestVcpu *vcpu; + struct guest_state_request gsr; + SpaprMachineStateNestedGuest *guest; + target_ulong rc; + + if (flags) /* don't handle any flags for now */ + return H_PARAMETER; + + guest = spapr_get_nested_guest(spapr, lpid); + if (!guest) { + return H_P2; + } + if (!spapr_nested_vcpu_check(guest, vcpuid, true)) { + return H_P3; + } + + if (guest->parttbl[0] == 0) { + /* At least need a partition scoped radix tree */ + return H_NOT_AVAILABLE; + } + + vcpu = &guest->vcpus[vcpuid]; + + /* Read run_vcpu input buffer to update state */ + gsr.buf = vcpu->runbufin.addr; + gsr.len = vcpu->runbufin.size; + gsr.flags = GUEST_STATE_REQUEST_SET; /* Thread wide + writing */ + rc = map_and_getset_state(cpu, guest, vcpuid, &gsr); + if (rc == H_SUCCESS) { + nested_papr_run_vcpu(cpu, lpid, vcpu); + } else { + env->gpr[3] = rc; + } + return env->gpr[3]; +} + +void spapr_register_nested_hv(void) +{ + spapr_register_hypercall(KVMPPC_H_SET_PARTITION_TABLE, h_set_ptbl); + spapr_register_hypercall(KVMPPC_H_ENTER_NESTED, h_enter_nested); + spapr_register_hypercall(KVMPPC_H_TLB_INVALIDATE, h_tlb_invalidate); + spapr_register_hypercall(KVMPPC_H_COPY_TOFROM_GUEST, h_copy_tofrom_guest); +} + +void spapr_unregister_nested_hv(void) +{ + spapr_unregister_hypercall(KVMPPC_H_SET_PARTITION_TABLE); + spapr_unregister_hypercall(KVMPPC_H_ENTER_NESTED); + spapr_unregister_hypercall(KVMPPC_H_TLB_INVALIDATE); + spapr_unregister_hypercall(KVMPPC_H_COPY_TOFROM_GUEST); +} + +void spapr_register_nested_papr(void) +{ + spapr_register_hypercall(H_GUEST_GET_CAPABILITIES, + h_guest_get_capabilities); + spapr_register_hypercall(H_GUEST_SET_CAPABILITIES, + h_guest_set_capabilities); + spapr_register_hypercall(H_GUEST_CREATE, h_guest_create); + spapr_register_hypercall(H_GUEST_DELETE, h_guest_delete); + spapr_register_hypercall(H_GUEST_CREATE_VCPU, h_guest_create_vcpu); + spapr_register_hypercall(H_GUEST_SET_STATE, h_guest_set_state); + spapr_register_hypercall(H_GUEST_GET_STATE, h_guest_get_state); + spapr_register_hypercall(H_GUEST_RUN_VCPU, h_guest_run_vcpu); +} + +void spapr_unregister_nested_papr(void) +{ + spapr_unregister_hypercall(H_GUEST_GET_CAPABILITIES); + spapr_unregister_hypercall(H_GUEST_SET_CAPABILITIES); + spapr_unregister_hypercall(H_GUEST_CREATE); + spapr_unregister_hypercall(H_GUEST_DELETE); + spapr_unregister_hypercall(H_GUEST_CREATE_VCPU); + spapr_unregister_hypercall(H_GUEST_SET_STATE); + spapr_unregister_hypercall(H_GUEST_GET_STATE); + spapr_unregister_hypercall(H_GUEST_RUN_VCPU); +} + +#else +void spapr_exit_nested(PowerPCCPU *cpu, int excp) +{ + g_assert_not_reached(); +} + +void spapr_register_nested_hv(void) +{ + /* DO NOTHING */ +} + +void spapr_unregister_nested_hv(void) +{ + /* DO NOTHING */ +} + +bool spapr_get_pate_nested_hv(SpaprMachineState *spapr, PowerPCCPU *cpu, + target_ulong lpid, ppc_v3_pate_t *entry) +{ + return false; +} + +bool spapr_get_pate_nested_papr(SpaprMachineState *spapr, PowerPCCPU *cpu, + target_ulong lpid, ppc_v3_pate_t *entry) +{ + return false; +} + +void spapr_register_nested_papr(void) +{ + /* DO NOTHING */ +} + +void spapr_unregister_nested_papr(void) +{ + /* DO NOTHING */ +} + +void spapr_nested_gsb_init(void) +{ + /* DO NOTHING */ +} + +#endif diff --git a/hw/ppc/spapr_numa.c b/hw/ppc/spapr_numa.c index a64098c375..ea6762d3d2 100644 --- a/hw/ppc/spapr_numa.c +++ b/hw/ppc/spapr_numa.c @@ -108,20 +108,6 @@ static bool spapr_numa_is_symmetrical(MachineState *ms) return true; } -/* - * NVLink2-connected GPU RAM needs to be placed on a separate NUMA node. - * We assign a new numa ID per GPU in spapr_pci_collect_nvgpu() which is - * called from vPHB reset handler so we initialize the counter here. - * If no NUMA is configured from the QEMU side, we start from 1 as GPU RAM - * must be equally distant from any other node. - * The final value of spapr->gpu_numa_id is going to be written to - * max-associativity-domains in spapr_build_fdt(). - */ -unsigned int spapr_numa_initial_nvgpu_numa_id(MachineState *machine) -{ - return MAX(1, machine->numa_state->num_nodes); -} - /* * This function will translate the user distances into * what the kernel understand as possible values: 10 @@ -277,7 +263,7 @@ static void spapr_numa_FORM1_affinity_init(SpaprMachineState *spapr, { SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); int nb_numa_nodes = machine->numa_state->num_nodes; - int i, j, max_nodes_with_gpus; + int i, j; /* * For all associativity arrays: first position is the size, @@ -293,17 +279,7 @@ static void spapr_numa_FORM1_affinity_init(SpaprMachineState *spapr, spapr->FORM1_assoc_array[i][FORM1_DIST_REF_POINTS] = cpu_to_be32(i); } - /* - * Initialize NVLink GPU associativity arrays. We know that - * the first GPU will take the first available NUMA id, and - * we'll have a maximum of NVGPU_MAX_NUM GPUs in the machine. - * At this point we're not sure if there are GPUs or not, but - * let's initialize the associativity arrays and allow NVLink - * GPUs to be handled like regular NUMA nodes later on. - */ - max_nodes_with_gpus = nb_numa_nodes + NVGPU_MAX_NUM; - - for (i = nb_numa_nodes; i < max_nodes_with_gpus; i++) { + for (i = nb_numa_nodes; i < nb_numa_nodes; i++) { spapr->FORM1_assoc_array[i][0] = cpu_to_be32(FORM1_DIST_REF_POINTS); for (j = 1; j < FORM1_DIST_REF_POINTS; j++) { @@ -345,10 +321,6 @@ static void spapr_numa_FORM2_affinity_init(SpaprMachineState *spapr) * CPUs will write an additional 'vcpu_id' on top of the arrays * being initialized here. 'numa_id' is represented by the * index 'i' of the loop. - * - * Given that this initialization is also valid for GPU associativity - * arrays, handle everything in one single step by populating the - * arrays up to NUMA_NODES_MAX_NUM. */ for (i = 0; i < NUMA_NODES_MAX_NUM; i++) { spapr->FORM2_assoc_array[i][0] = cpu_to_be32(1); @@ -461,8 +433,6 @@ static void spapr_numa_FORM1_write_rtas_dt(SpaprMachineState *spapr, { MachineState *ms = MACHINE(spapr); SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); - uint32_t number_nvgpus_nodes = spapr->gpu_numa_id - - spapr_numa_initial_nvgpu_numa_id(ms); uint32_t refpoints[] = { cpu_to_be32(0x4), cpu_to_be32(0x3), @@ -470,7 +440,7 @@ static void spapr_numa_FORM1_write_rtas_dt(SpaprMachineState *spapr, cpu_to_be32(0x1), }; uint32_t nr_refpoints = ARRAY_SIZE(refpoints); - uint32_t maxdomain = ms->numa_state->num_nodes + number_nvgpus_nodes; + uint32_t maxdomain = ms->numa_state->num_nodes; uint32_t maxdomains[] = { cpu_to_be32(4), cpu_to_be32(maxdomain), @@ -486,13 +456,12 @@ static void spapr_numa_FORM1_write_rtas_dt(SpaprMachineState *spapr, cpu_to_be32(0x4), cpu_to_be32(0x2), }; - uint32_t legacy_maxdomain = spapr->gpu_numa_id > 1 ? 1 : 0; uint32_t legacy_maxdomains[] = { cpu_to_be32(4), - cpu_to_be32(legacy_maxdomain), - cpu_to_be32(legacy_maxdomain), - cpu_to_be32(legacy_maxdomain), - cpu_to_be32(spapr->gpu_numa_id), + cpu_to_be32(0), + cpu_to_be32(0), + cpu_to_be32(0), + cpu_to_be32(maxdomain ? maxdomain : 1), }; G_STATIC_ASSERT(sizeof(legacy_refpoints) <= sizeof(refpoints)); @@ -581,8 +550,6 @@ static void spapr_numa_FORM2_write_rtas_dt(SpaprMachineState *spapr, void *fdt, int rtas) { MachineState *ms = MACHINE(spapr); - uint32_t number_nvgpus_nodes = spapr->gpu_numa_id - - spapr_numa_initial_nvgpu_numa_id(ms); /* * In FORM2, ibm,associativity-reference-points will point to @@ -596,7 +563,7 @@ static void spapr_numa_FORM2_write_rtas_dt(SpaprMachineState *spapr, */ uint32_t refpoints[] = { cpu_to_be32(1) }; - uint32_t maxdomain = ms->numa_state->num_nodes + number_nvgpus_nodes; + uint32_t maxdomain = ms->numa_state->num_nodes; uint32_t maxdomains[] = { cpu_to_be32(1), cpu_to_be32(maxdomain) }; _FDT(fdt_setprop(fdt, rtas, "ibm,associativity-reference-points", diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c index c4c97da5de..7d2dfe5e3d 100644 --- a/hw/ppc/spapr_nvdimm.c +++ b/hw/ppc/spapr_nvdimm.c @@ -320,7 +320,8 @@ static target_ulong h_scm_write_metadata(PowerPCCPU *cpu, nvdimm = NVDIMM(drc->dev); if ((offset + len < offset) || - (nvdimm->label_size < len + offset)) { + (nvdimm->label_size < len + offset) || + nvdimm->readonly) { return H_P2; } @@ -377,7 +378,7 @@ static target_ulong h_scm_bind_mem(PowerPCCPU *cpu, SpaprMachineState *spapr, /* * Currently continue token should be zero qemu has already bound - * everything and this hcall doesnt return H_BUSY. + * everything and this hcall doesn't return H_BUSY. */ if (continue_token > 0) { return H_P5; @@ -447,9 +448,15 @@ static int flush_worker_cb(void *opaque) { SpaprNVDIMMDeviceFlushState *state = opaque; SpaprDrc *drc = spapr_drc_by_index(state->drcidx); - PCDIMMDevice *dimm = PC_DIMM(drc->dev); - HostMemoryBackend *backend = MEMORY_BACKEND(dimm->hostmem); - int backend_fd = memory_region_get_fd(&backend->mr); + PCDIMMDevice *dimm; + HostMemoryBackend *backend; + int backend_fd; + + g_assert(drc != NULL); + + dimm = PC_DIMM(drc->dev); + backend = MEMORY_BACKEND(dimm->hostmem); + backend_fd = memory_region_get_fd(&backend->mr); if (object_property_get_bool(OBJECT(backend), "pmem", NULL)) { MemoryRegion *mr = host_memory_backend_get_memory(dimm->hostmem); @@ -475,7 +482,11 @@ static void spapr_nvdimm_flush_completion_cb(void *opaque, int hcall_ret) { SpaprNVDIMMDeviceFlushState *state = opaque; SpaprDrc *drc = spapr_drc_by_index(state->drcidx); - SpaprNVDIMMDevice *s_nvdimm = SPAPR_NVDIMM(drc->dev); + SpaprNVDIMMDevice *s_nvdimm; + + g_assert(drc != NULL); + + s_nvdimm = SPAPR_NVDIMM(drc->dev); state->hcall_ret = hcall_ret; QLIST_REMOVE(state, node); @@ -486,7 +497,6 @@ static int spapr_nvdimm_flush_post_load(void *opaque, int version_id) { SpaprNVDIMMDevice *s_nvdimm = (SpaprNVDIMMDevice *)opaque; SpaprNVDIMMDeviceFlushState *state; - ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context()); HostMemoryBackend *backend = MEMORY_BACKEND(PC_DIMM(s_nvdimm)->hostmem); bool is_pmem = object_property_get_bool(OBJECT(backend), "pmem", NULL); bool pmem_override = object_property_get_bool(OBJECT(s_nvdimm), @@ -507,7 +517,7 @@ static int spapr_nvdimm_flush_post_load(void *opaque, int version_id) } QLIST_FOREACH(state, &s_nvdimm->pending_nvdimm_flush_states, node) { - thread_pool_submit_aio(pool, flush_worker_cb, state, + thread_pool_submit_aio(flush_worker_cb, state, spapr_nvdimm_flush_completion_cb, state); } @@ -518,7 +528,7 @@ static const VMStateDescription vmstate_spapr_nvdimm_flush_state = { .name = "spapr_nvdimm_flush_state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(continue_token, SpaprNVDIMMDeviceFlushState), VMSTATE_INT64(hcall_ret, SpaprNVDIMMDeviceFlushState), VMSTATE_UINT32(drcidx, SpaprNVDIMMDeviceFlushState), @@ -531,7 +541,7 @@ const VMStateDescription vmstate_spapr_nvdimm_states = { .version_id = 1, .minimum_version_id = 1, .post_load = spapr_nvdimm_flush_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(hcall_flush_required, SpaprNVDIMMDevice), VMSTATE_UINT64(nvdimm_flush_token, SpaprNVDIMMDevice), VMSTATE_QLIST_V(completed_nvdimm_flush_states, SpaprNVDIMMDevice, 1, @@ -579,7 +589,7 @@ void spapr_nvdimm_finish_flushes(void) * Called on reset path, the main loop thread which calls * the pending BHs has gotten out running in the reset path, * finally reaching here. Other code path being guest - * h_client_architecture_support, thats early boot up. + * h_client_architecture_support, that's early boot up. */ nvdimms = nvdimm_get_device_list(); for (list = nvdimms; list; list = list->next) { @@ -654,7 +664,6 @@ static target_ulong h_scm_flush(PowerPCCPU *cpu, SpaprMachineState *spapr, PCDIMMDevice *dimm; HostMemoryBackend *backend = NULL; SpaprNVDIMMDeviceFlushState *state; - ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context()); int fd; if (!drc || !drc->dev || @@ -689,7 +698,7 @@ static target_ulong h_scm_flush(PowerPCCPU *cpu, SpaprMachineState *spapr, state->drcidx = drc_index; - thread_pool_submit_aio(pool, flush_worker_cb, state, + thread_pool_submit_aio(flush_worker_cb, state, spapr_nvdimm_flush_completion_cb, state); continue_token = state->continue_token; @@ -867,8 +876,7 @@ static void spapr_nvdimm_realize(NVDIMMDevice *dimm, Error **errp) s_nvdimm->hcall_flush_required = true; } - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, - &vmstate_spapr_nvdimm_states, dimm); + vmstate_register_any(NULL, &vmstate_spapr_nvdimm_states, dimm); } static void spapr_nvdimm_unrealize(NVDIMMDevice *dimm) diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c index b2567caa5c..88e29536aa 100644 --- a/hw/ppc/spapr_ovec.c +++ b/hw/ppc/spapr_ovec.c @@ -36,7 +36,7 @@ const VMStateDescription vmstate_spapr_ovec = { .name = "spapr_option_vector", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BITMAP(bitmap, SpaprOptionVector, 1, bitmap_size), VMSTATE_END_OF_LIST() } diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index b2f5fbef0c..25e0295d6f 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -780,6 +780,10 @@ static AddressSpace *spapr_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &phb->iommu_as; } +static const PCIIOMMUOps spapr_iommu_ops = { + .get_address_space = spapr_pci_dma_iommu, +}; + static char *spapr_phb_vfio_get_loc_code(SpaprPhbState *sphb, PCIDevice *pdev) { g_autofree char *path = NULL; @@ -800,6 +804,7 @@ static char *spapr_phb_vfio_get_loc_code(SpaprPhbState *sphb, PCIDevice *pdev) } /* Construct and read from host device tree the loc-code */ + g_free(path); path = g_strdup_printf("/proc/device-tree%s/ibm,loc-code", devspec); if (!g_file_get_contents(path, &buf, NULL, NULL)) { return NULL; @@ -1360,7 +1365,6 @@ static int spapr_dt_pci_device(SpaprPhbState *sphb, PCIDevice *dev, { int offset; g_autofree gchar *nodename = spapr_pci_fw_dev_name(dev); - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); ResourceProps rp; SpaprDrc *drc = drc_from_dev(sphb, dev); uint32_t vendor_id = pci_default_read_config(dev, PCI_VENDOR_ID, 2); @@ -1443,9 +1447,7 @@ static int spapr_dt_pci_device(SpaprPhbState *sphb, PCIDevice *dev, _FDT(fdt_setprop_cell(fdt, offset, "ibm,pci-config-space-type", 0x1)); } - spapr_phb_nvgpu_populate_pcidev_dt(dev, fdt, offset, sphb); - - if (!pc->is_bridge) { + if (!IS_PCI_BRIDGE(dev)) { /* Properties only for non-bridges */ uint32_t min_grant = pci_default_read_config(dev, PCI_MIN_GNT, 1); uint32_t max_latency = pci_default_read_config(dev, PCI_MAX_LAT, 1); @@ -1543,7 +1545,6 @@ static void spapr_pci_pre_plug(HotplugHandler *plug_handler, { SpaprPhbState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); PCIDevice *pdev = PCI_DEVICE(plugged_dev); - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(plugged_dev); SpaprDrc *drc = drc_from_dev(phb, pdev); PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))); uint32_t slotnr = PCI_SLOT(pdev->devfn); @@ -1554,12 +1555,12 @@ static void spapr_pci_pre_plug(HotplugHandler *plug_handler, */ if (plugged_dev->hotplugged) { error_setg(errp, QERR_BUS_NO_HOTPLUG, - object_get_typename(OBJECT(phb))); + phb->parent_obj.bus->qbus.name); return; } } - if (pc->is_bridge) { + if (IS_PCI_BRIDGE(plugged_dev)) { if (!bridge_has_valid_chassis_nr(OBJECT(plugged_dev), errp)) { return; } @@ -1588,7 +1589,6 @@ static void spapr_pci_plug(HotplugHandler *plug_handler, { SpaprPhbState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); PCIDevice *pdev = PCI_DEVICE(plugged_dev); - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(plugged_dev); SpaprDrc *drc = drc_from_dev(phb, pdev); uint32_t slotnr = PCI_SLOT(pdev->devfn); @@ -1602,7 +1602,7 @@ static void spapr_pci_plug(HotplugHandler *plug_handler, g_assert(drc); - if (pc->is_bridge) { + if (IS_PCI_BRIDGE(plugged_dev)) { spapr_pci_bridge_plug(phb, PCI_BRIDGE(plugged_dev)); } @@ -1645,7 +1645,6 @@ static void spapr_pci_bridge_unplug(SpaprPhbState *phb, static void spapr_pci_unplug(HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) { - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(plugged_dev); SpaprPhbState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); /* some version guests do not wait for completion of a device @@ -1660,7 +1659,7 @@ static void spapr_pci_unplug(HotplugHandler *plug_handler, */ pci_device_reset(PCI_DEVICE(plugged_dev)); - if (pc->is_bridge) { + if (IS_PCI_BRIDGE(plugged_dev)) { spapr_pci_bridge_unplug(phb, PCI_BRIDGE(plugged_dev)); return; } @@ -1677,7 +1676,7 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler, if (!phb->dr_enabled) { error_setg(errp, QERR_BUS_NO_HOTPLUG, - object_get_typename(OBJECT(phb))); + phb->parent_obj.bus->qbus.name); return; } @@ -1685,7 +1684,6 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler, g_assert(drc->dev == plugged_dev); if (!spapr_drc_unplug_requested(drc)) { - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(plugged_dev); uint32_t slotnr = PCI_SLOT(pdev->devfn); SpaprDrc *func_drc; SpaprDrcClass *func_drck; @@ -1693,7 +1691,7 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler, int i; uint8_t chassis = chassis_from_bus(pci_get_bus(pdev)); - if (pc->is_bridge) { + if (IS_PCI_BRIDGE(plugged_dev)) { error_setg(errp, "PCI: Hot unplug of PCI bridges not supported"); return; } @@ -1761,8 +1759,6 @@ static void spapr_phb_unrealize(DeviceState *dev) int i; const unsigned windows_supported = spapr_phb_windows_supported(sphb); - spapr_phb_nvgpu_free(sphb); - if (sphb->msi) { g_hash_table_unref(sphb->msi); sphb->msi = NULL; @@ -1834,9 +1830,9 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) (SpaprMachineState *) object_dynamic_cast(qdev_get_machine(), TYPE_SPAPR_MACHINE); SpaprMachineClass *smc = spapr ? SPAPR_MACHINE_GET_CLASS(spapr) : NULL; - SysBusDevice *s = SYS_BUS_DEVICE(dev); - SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(sbd); + PCIHostState *phb = PCI_HOST_BRIDGE(sbd); MachineState *ms = MACHINE(spapr); char *namebuf; int i; @@ -1986,7 +1982,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&sphb->iommu_root, SPAPR_PCI_MSI_WINDOW, &sphb->msiwindow); - pci_setup_iommu(bus, spapr_pci_dma_iommu, sphb); + pci_setup_iommu(bus, &spapr_iommu_ops, sphb); pci_bus_set_route_irq_fn(bus, spapr_route_intx_pin_to_irq); @@ -2044,7 +2040,7 @@ static int spapr_phb_children_reset(Object *child, void *opaque) DeviceState *dev = (DeviceState *) object_dynamic_cast(child, TYPE_DEVICE); if (dev) { - device_legacy_reset(dev); + device_cold_reset(dev); } return 0; @@ -2067,19 +2063,14 @@ void spapr_phb_dma_reset(SpaprPhbState *sphb) tcet = spapr_tce_find_by_liobn(sphb->dma_liobn[0]); spapr_tce_table_enable(tcet, SPAPR_TCE_PAGE_SHIFT, sphb->dma_win_addr, sphb->dma_win_size >> SPAPR_TCE_PAGE_SHIFT); + tcet->def_win = true; } static void spapr_phb_reset(DeviceState *qdev) { SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(qdev); - Error *err = NULL; spapr_phb_dma_reset(sphb); - spapr_phb_nvgpu_free(sphb); - spapr_phb_nvgpu_setup(sphb, &err); - if (err) { - error_report_err(err); - } /* Reset the IOMMU state */ object_child_foreach(OBJECT(qdev), spapr_phb_children_reset, NULL); @@ -2115,8 +2106,6 @@ static Property spapr_phb_properties[] = { pre_2_8_migration, false), DEFINE_PROP_BOOL("pcie-extended-configuration-space", SpaprPhbState, pcie_ecs, true), - DEFINE_PROP_UINT64("gpa", SpaprPhbState, nv2_gpa_win_addr, 0), - DEFINE_PROP_UINT64("atsd", SpaprPhbState, nv2_atsd_win_addr, 0), DEFINE_PROP_BOOL("pre-5.1-associativity", SpaprPhbState, pre_5_1_assoc, false), DEFINE_PROP_END_OF_LIST(), @@ -2126,7 +2115,7 @@ static const VMStateDescription vmstate_spapr_pci_lsi = { .name = "spapr_pci/lsi", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_EQUAL(irq, SpaprPciLsi, NULL), VMSTATE_END_OF_LIST() @@ -2137,7 +2126,7 @@ static const VMStateDescription vmstate_spapr_pci_msi = { .name = "spapr_pci/msi", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField []) { + .fields = (const VMStateField []) { VMSTATE_UINT32(key, SpaprPciMsiMig), VMSTATE_UINT32(value.first_irq, SpaprPciMsiMig), VMSTATE_UINT32(value.num, SpaprPciMsiMig), @@ -2227,7 +2216,7 @@ static const VMStateDescription vmstate_spapr_pci = { .pre_save = spapr_pci_pre_save, .post_save = spapr_pci_post_save, .post_load = spapr_pci_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_EQUAL(buid, SpaprPhbState, NULL), VMSTATE_UINT32_TEST(mig_liobn, SpaprPhbState, pre_2_8_migration), VMSTATE_UINT64_TEST(mig_mem_win_addr, SpaprPhbState, pre_2_8_migration), @@ -2359,12 +2348,12 @@ int spapr_dt_phb(SpaprMachineState *spapr, SpaprPhbState *phb, cpu_to_be32(RTAS_IBM_REMOVE_PE_DMA_WINDOW) }; uint32_t ddw_extensions[] = { - cpu_to_be32(1), - cpu_to_be32(RTAS_IBM_RESET_PE_DMA_WINDOW) + cpu_to_be32(2), + cpu_to_be32(RTAS_IBM_RESET_PE_DMA_WINDOW), + cpu_to_be32(1), /* 1: ibm,query-pe-dma-window 6 outputs, PAPR 2.8 */ }; SpaprTceTable *tcet; SpaprDrc *drc; - Error *err = NULL; /* Start populating the FDT */ _FDT(bus_off = fdt_add_subnode(fdt, 0, phb->dtbusname)); @@ -2445,12 +2434,6 @@ int spapr_dt_phb(SpaprMachineState *spapr, SpaprPhbState *phb, return ret; } - spapr_phb_nvgpu_populate_dt(phb, fdt, bus_off, &err); - if (err) { - error_report_err(err); - } - spapr_phb_nvgpu_ram_populate_dt(phb, fdt); - return 0; } diff --git a/hw/ppc/spapr_pci_nvlink2.c b/hw/ppc/spapr_pci_nvlink2.c deleted file mode 100644 index 63b476c8f7..0000000000 --- a/hw/ppc/spapr_pci_nvlink2.c +++ /dev/null @@ -1,442 +0,0 @@ -/* - * QEMU sPAPR PCI for NVLink2 pass through - * - * Copyright (c) 2019 Alexey Kardashevskiy, 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/pci/pci.h" -#include "hw/pci-host/spapr.h" -#include "hw/ppc/spapr_numa.h" -#include "qemu/error-report.h" -#include "hw/ppc/fdt.h" -#include "hw/pci/pci_bridge.h" - -#define PHANDLE_PCIDEV(phb, pdev) (0x12000000 | \ - (((phb)->index) << 16) | ((pdev)->devfn)) -#define PHANDLE_GPURAM(phb, n) (0x110000FF | ((n) << 8) | \ - (((phb)->index) << 16)) -#define PHANDLE_NVLINK(phb, gn, nn) (0x00130000 | (((phb)->index) << 8) | \ - ((gn) << 4) | (nn)) - -typedef struct SpaprPhbPciNvGpuSlot { - uint64_t tgt; - uint64_t gpa; - unsigned numa_id; - PCIDevice *gpdev; - int linknum; - struct { - uint64_t atsd_gpa; - PCIDevice *npdev; - uint32_t link_speed; - } links[NVGPU_MAX_LINKS]; -} SpaprPhbPciNvGpuSlot; - -struct SpaprPhbPciNvGpuConfig { - uint64_t nv2_ram_current; - uint64_t nv2_atsd_current; - int num; /* number of non empty (i.e. tgt!=0) entries in slots[] */ - SpaprPhbPciNvGpuSlot slots[NVGPU_MAX_NUM]; - Error *err; -}; - -static SpaprPhbPciNvGpuSlot * -spapr_nvgpu_get_slot(SpaprPhbPciNvGpuConfig *nvgpus, uint64_t tgt) -{ - int i; - - /* Search for partially collected "slot" */ - for (i = 0; i < nvgpus->num; ++i) { - if (nvgpus->slots[i].tgt == tgt) { - return &nvgpus->slots[i]; - } - } - - if (nvgpus->num == ARRAY_SIZE(nvgpus->slots)) { - return NULL; - } - - i = nvgpus->num; - nvgpus->slots[i].tgt = tgt; - ++nvgpus->num; - - return &nvgpus->slots[i]; -} - -static void spapr_pci_collect_nvgpu(SpaprPhbPciNvGpuConfig *nvgpus, - PCIDevice *pdev, uint64_t tgt, - MemoryRegion *mr, Error **errp) -{ - MachineState *machine = MACHINE(qdev_get_machine()); - SpaprMachineState *spapr = SPAPR_MACHINE(machine); - SpaprPhbPciNvGpuSlot *nvslot = spapr_nvgpu_get_slot(nvgpus, tgt); - - if (!nvslot) { - error_setg(errp, "Found too many GPUs per vPHB"); - return; - } - g_assert(!nvslot->gpdev); - nvslot->gpdev = pdev; - - nvslot->gpa = nvgpus->nv2_ram_current; - nvgpus->nv2_ram_current += memory_region_size(mr); - nvslot->numa_id = spapr->gpu_numa_id; - ++spapr->gpu_numa_id; -} - -static void spapr_pci_collect_nvnpu(SpaprPhbPciNvGpuConfig *nvgpus, - PCIDevice *pdev, uint64_t tgt, - MemoryRegion *mr, Error **errp) -{ - SpaprPhbPciNvGpuSlot *nvslot = spapr_nvgpu_get_slot(nvgpus, tgt); - int j; - - if (!nvslot) { - error_setg(errp, "Found too many NVLink bridges per vPHB"); - return; - } - - j = nvslot->linknum; - if (j == ARRAY_SIZE(nvslot->links)) { - error_setg(errp, "Found too many NVLink bridges per GPU"); - return; - } - ++nvslot->linknum; - - g_assert(!nvslot->links[j].npdev); - nvslot->links[j].npdev = pdev; - nvslot->links[j].atsd_gpa = nvgpus->nv2_atsd_current; - nvgpus->nv2_atsd_current += memory_region_size(mr); - nvslot->links[j].link_speed = - object_property_get_uint(OBJECT(pdev), "nvlink2-link-speed", NULL); -} - -static void spapr_phb_pci_collect_nvgpu(PCIBus *bus, PCIDevice *pdev, - void *opaque) -{ - PCIBus *sec_bus; - Object *po = OBJECT(pdev); - uint64_t tgt = object_property_get_uint(po, "nvlink2-tgt", NULL); - - if (tgt) { - Error *local_err = NULL; - SpaprPhbPciNvGpuConfig *nvgpus = opaque; - Object *mr_gpu = object_property_get_link(po, "nvlink2-mr[0]", NULL); - Object *mr_npu = object_property_get_link(po, "nvlink2-atsd-mr[0]", - NULL); - - g_assert(mr_gpu || mr_npu); - if (mr_gpu) { - spapr_pci_collect_nvgpu(nvgpus, pdev, tgt, MEMORY_REGION(mr_gpu), - &local_err); - } else { - spapr_pci_collect_nvnpu(nvgpus, pdev, tgt, MEMORY_REGION(mr_npu), - &local_err); - } - error_propagate(&nvgpus->err, local_err); - } - if ((pci_default_read_config(pdev, PCI_HEADER_TYPE, 1) != - PCI_HEADER_TYPE_BRIDGE)) { - return; - } - - sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev)); - if (!sec_bus) { - return; - } - - pci_for_each_device_under_bus(sec_bus, spapr_phb_pci_collect_nvgpu, opaque); -} - -void spapr_phb_nvgpu_setup(SpaprPhbState *sphb, Error **errp) -{ - int i, j, valid_gpu_num; - PCIBus *bus; - - /* Search for GPUs and NPUs */ - if (!sphb->nv2_gpa_win_addr || !sphb->nv2_atsd_win_addr) { - return; - } - - sphb->nvgpus = g_new0(SpaprPhbPciNvGpuConfig, 1); - sphb->nvgpus->nv2_ram_current = sphb->nv2_gpa_win_addr; - sphb->nvgpus->nv2_atsd_current = sphb->nv2_atsd_win_addr; - - bus = PCI_HOST_BRIDGE(sphb)->bus; - pci_for_each_device_under_bus(bus, spapr_phb_pci_collect_nvgpu, - sphb->nvgpus); - - if (sphb->nvgpus->err) { - error_propagate(errp, sphb->nvgpus->err); - sphb->nvgpus->err = NULL; - goto cleanup_exit; - } - - /* Add found GPU RAM and ATSD MRs if found */ - for (i = 0, valid_gpu_num = 0; i < sphb->nvgpus->num; ++i) { - Object *nvmrobj; - SpaprPhbPciNvGpuSlot *nvslot = &sphb->nvgpus->slots[i]; - - if (!nvslot->gpdev) { - continue; - } - nvmrobj = object_property_get_link(OBJECT(nvslot->gpdev), - "nvlink2-mr[0]", NULL); - /* ATSD is pointless without GPU RAM MR so skip those */ - if (!nvmrobj) { - continue; - } - - ++valid_gpu_num; - memory_region_add_subregion(get_system_memory(), nvslot->gpa, - MEMORY_REGION(nvmrobj)); - - for (j = 0; j < nvslot->linknum; ++j) { - Object *atsdmrobj; - - atsdmrobj = object_property_get_link(OBJECT(nvslot->links[j].npdev), - "nvlink2-atsd-mr[0]", NULL); - if (!atsdmrobj) { - continue; - } - memory_region_add_subregion(get_system_memory(), - nvslot->links[j].atsd_gpa, - MEMORY_REGION(atsdmrobj)); - } - } - - if (valid_gpu_num) { - return; - } - /* We did not find any interesting GPU */ -cleanup_exit: - g_free(sphb->nvgpus); - sphb->nvgpus = NULL; -} - -void spapr_phb_nvgpu_free(SpaprPhbState *sphb) -{ - int i, j; - - if (!sphb->nvgpus) { - return; - } - - for (i = 0; i < sphb->nvgpus->num; ++i) { - SpaprPhbPciNvGpuSlot *nvslot = &sphb->nvgpus->slots[i]; - Object *nv_mrobj = object_property_get_link(OBJECT(nvslot->gpdev), - "nvlink2-mr[0]", NULL); - - if (nv_mrobj) { - memory_region_del_subregion(get_system_memory(), - MEMORY_REGION(nv_mrobj)); - } - for (j = 0; j < nvslot->linknum; ++j) { - PCIDevice *npdev = nvslot->links[j].npdev; - Object *atsd_mrobj; - atsd_mrobj = object_property_get_link(OBJECT(npdev), - "nvlink2-atsd-mr[0]", NULL); - if (atsd_mrobj) { - memory_region_del_subregion(get_system_memory(), - MEMORY_REGION(atsd_mrobj)); - } - } - } - g_free(sphb->nvgpus); - sphb->nvgpus = NULL; -} - -void spapr_phb_nvgpu_populate_dt(SpaprPhbState *sphb, void *fdt, int bus_off, - Error **errp) -{ - int i, j, atsdnum = 0; - uint64_t atsd[8]; /* The existing limitation of known guests */ - - if (!sphb->nvgpus) { - return; - } - - for (i = 0; (i < sphb->nvgpus->num) && (atsdnum < ARRAY_SIZE(atsd)); ++i) { - SpaprPhbPciNvGpuSlot *nvslot = &sphb->nvgpus->slots[i]; - - if (!nvslot->gpdev) { - continue; - } - for (j = 0; j < nvslot->linknum; ++j) { - if (!nvslot->links[j].atsd_gpa) { - continue; - } - - if (atsdnum == ARRAY_SIZE(atsd)) { - error_report("Only %"PRIuPTR" ATSD registers supported", - ARRAY_SIZE(atsd)); - break; - } - atsd[atsdnum] = cpu_to_be64(nvslot->links[j].atsd_gpa); - ++atsdnum; - } - } - - if (!atsdnum) { - error_setg(errp, "No ATSD registers found"); - return; - } - - if (!spapr_phb_eeh_available(sphb)) { - /* - * ibm,mmio-atsd contains ATSD registers; these belong to an NPU PHB - * which we do not emulate as a separate device. Instead we put - * ibm,mmio-atsd to the vPHB with GPU and make sure that we do not - * put GPUs from different IOMMU groups to the same vPHB to ensure - * that the guest will use ATSDs from the corresponding NPU. - */ - error_setg(errp, "ATSD requires separate vPHB per GPU IOMMU group"); - return; - } - - _FDT((fdt_setprop(fdt, bus_off, "ibm,mmio-atsd", atsd, - atsdnum * sizeof(atsd[0])))); -} - -void spapr_phb_nvgpu_ram_populate_dt(SpaprPhbState *sphb, void *fdt) -{ - int i, j, linkidx, npuoff; - g_autofree char *npuname = NULL; - - if (!sphb->nvgpus) { - return; - } - - npuname = g_strdup_printf("npuphb%d", sphb->index); - npuoff = fdt_add_subnode(fdt, 0, npuname); - _FDT(npuoff); - _FDT(fdt_setprop_cell(fdt, npuoff, "#address-cells", 1)); - _FDT(fdt_setprop_cell(fdt, npuoff, "#size-cells", 0)); - /* Advertise NPU as POWER9 so the guest can enable NPU2 contexts */ - _FDT((fdt_setprop_string(fdt, npuoff, "compatible", "ibm,power9-npu"))); - - for (i = 0, linkidx = 0; i < sphb->nvgpus->num; ++i) { - for (j = 0; j < sphb->nvgpus->slots[i].linknum; ++j) { - g_autofree char *linkname = g_strdup_printf("link@%d", linkidx); - int off = fdt_add_subnode(fdt, npuoff, linkname); - - _FDT(off); - /* _FDT((fdt_setprop_cell(fdt, off, "reg", linkidx))); */ - _FDT((fdt_setprop_string(fdt, off, "compatible", - "ibm,npu-link"))); - _FDT((fdt_setprop_cell(fdt, off, "phandle", - PHANDLE_NVLINK(sphb, i, j)))); - _FDT((fdt_setprop_cell(fdt, off, "ibm,npu-link-index", linkidx))); - ++linkidx; - } - } - - /* Add memory nodes for GPU RAM and mark them unusable */ - for (i = 0; i < sphb->nvgpus->num; ++i) { - SpaprPhbPciNvGpuSlot *nvslot = &sphb->nvgpus->slots[i]; - Object *nv_mrobj = object_property_get_link(OBJECT(nvslot->gpdev), - "nvlink2-mr[0]", - &error_abort); - uint64_t size = object_property_get_uint(nv_mrobj, "size", NULL); - uint64_t mem_reg[2] = { cpu_to_be64(nvslot->gpa), cpu_to_be64(size) }; - g_autofree char *mem_name = g_strdup_printf("memory@%"PRIx64, - nvslot->gpa); - int off = fdt_add_subnode(fdt, 0, mem_name); - - _FDT(off); - _FDT((fdt_setprop_string(fdt, off, "device_type", "memory"))); - _FDT((fdt_setprop(fdt, off, "reg", mem_reg, sizeof(mem_reg)))); - - spapr_numa_write_associativity_dt(SPAPR_MACHINE(qdev_get_machine()), - fdt, off, nvslot->numa_id); - - _FDT((fdt_setprop_string(fdt, off, "compatible", - "ibm,coherent-device-memory"))); - - mem_reg[1] = cpu_to_be64(0); - _FDT((fdt_setprop(fdt, off, "linux,usable-memory", mem_reg, - sizeof(mem_reg)))); - _FDT((fdt_setprop_cell(fdt, off, "phandle", - PHANDLE_GPURAM(sphb, i)))); - } - -} - -void spapr_phb_nvgpu_populate_pcidev_dt(PCIDevice *dev, void *fdt, int offset, - SpaprPhbState *sphb) -{ - int i, j; - - if (!sphb->nvgpus) { - return; - } - - for (i = 0; i < sphb->nvgpus->num; ++i) { - SpaprPhbPciNvGpuSlot *nvslot = &sphb->nvgpus->slots[i]; - - /* Skip "slot" without attached GPU */ - if (!nvslot->gpdev) { - continue; - } - if (dev == nvslot->gpdev) { - uint32_t npus[nvslot->linknum]; - - for (j = 0; j < nvslot->linknum; ++j) { - PCIDevice *npdev = nvslot->links[j].npdev; - - npus[j] = cpu_to_be32(PHANDLE_PCIDEV(sphb, npdev)); - } - _FDT(fdt_setprop(fdt, offset, "ibm,npu", npus, - j * sizeof(npus[0]))); - _FDT((fdt_setprop_cell(fdt, offset, "phandle", - PHANDLE_PCIDEV(sphb, dev)))); - continue; - } - - for (j = 0; j < nvslot->linknum; ++j) { - if (dev != nvslot->links[j].npdev) { - continue; - } - - _FDT((fdt_setprop_cell(fdt, offset, "phandle", - PHANDLE_PCIDEV(sphb, dev)))); - _FDT(fdt_setprop_cell(fdt, offset, "ibm,gpu", - PHANDLE_PCIDEV(sphb, nvslot->gpdev))); - _FDT((fdt_setprop_cell(fdt, offset, "ibm,nvlink", - PHANDLE_NVLINK(sphb, i, j)))); - /* - * If we ever want to emulate GPU RAM at the same location as on - * the host - here is the encoding GPA->TGT: - * - * gta = ((sphb->nv2_gpa >> 42) & 0x1) << 42; - * gta |= ((sphb->nv2_gpa >> 45) & 0x3) << 43; - * gta |= ((sphb->nv2_gpa >> 49) & 0x3) << 45; - * gta |= sphb->nv2_gpa & ((1UL << 43) - 1); - */ - _FDT(fdt_setprop_cell(fdt, offset, "memory-region", - PHANDLE_GPURAM(sphb, i))); - _FDT(fdt_setprop_u64(fdt, offset, "ibm,device-tgt-addr", - nvslot->tgt)); - _FDT(fdt_setprop_cell(fdt, offset, "ibm,nvlink-speed", - nvslot->links[j].link_speed)); - } - } -} diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index 2a76b4e0b5..76b2a3487b 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -18,12 +18,113 @@ */ #include "qemu/osdep.h" +#include #include #include "hw/ppc/spapr.h" #include "hw/pci-host/spapr.h" #include "hw/pci/msix.h" -#include "hw/vfio/vfio.h" +#include "hw/pci/pci_device.h" +#include "hw/vfio/vfio-common.h" #include "qemu/error-report.h" +#include CONFIG_DEVICES /* CONFIG_VFIO_PCI */ + +/* + * Interfaces for IBM EEH (Enhanced Error Handling) + */ +#ifdef CONFIG_VFIO_PCI +static bool vfio_eeh_container_ok(VFIOContainer *container) +{ + /* + * As of 2016-03-04 (linux-4.5) the host kernel EEH/VFIO + * implementation is broken if there are multiple groups in a + * container. The hardware works in units of Partitionable + * Endpoints (== IOMMU groups) and the EEH operations naively + * iterate across all groups in the container, without any logic + * to make sure the groups have their state synchronized. For + * certain operations (ENABLE) that might be ok, until an error + * occurs, but for others (GET_STATE) it's clearly broken. + */ + + /* + * XXX Once fixed kernels exist, test for them here + */ + + if (QLIST_EMPTY(&container->group_list)) { + return false; + } + + if (QLIST_NEXT(QLIST_FIRST(&container->group_list), container_next)) { + return false; + } + + return true; +} + +static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op) +{ + struct vfio_eeh_pe_op pe_op = { + .argsz = sizeof(pe_op), + .op = op, + }; + int ret; + + if (!vfio_eeh_container_ok(container)) { + error_report("vfio/eeh: EEH_PE_OP 0x%x: " + "kernel requires a container with exactly one group", op); + return -EPERM; + } + + ret = ioctl(container->fd, VFIO_EEH_PE_OP, &pe_op); + if (ret < 0) { + error_report("vfio/eeh: EEH_PE_OP 0x%x failed: %m", op); + return -errno; + } + + return ret; +} + +static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) +{ + VFIOAddressSpace *space = vfio_get_address_space(as); + VFIOContainerBase *bcontainer = NULL; + + if (QLIST_EMPTY(&space->containers)) { + /* No containers to act on */ + goto out; + } + + bcontainer = QLIST_FIRST(&space->containers); + + if (QLIST_NEXT(bcontainer, next)) { + /* + * We don't yet have logic to synchronize EEH state across + * multiple containers + */ + bcontainer = NULL; + goto out; + } + +out: + vfio_put_address_space(space); + return container_of(bcontainer, VFIOContainer, bcontainer); +} + +static bool vfio_eeh_as_ok(AddressSpace *as) +{ + VFIOContainer *container = vfio_eeh_as_container(as); + + return (container != NULL) && vfio_eeh_container_ok(container); +} + +static int vfio_eeh_as_op(AddressSpace *as, uint32_t op) +{ + VFIOContainer *container = vfio_eeh_as_container(as); + + if (!container) { + return -ENODEV; + } + return vfio_eeh_container_op(container, op); +} bool spapr_phb_eeh_available(SpaprPhbState *sphb) { @@ -77,7 +178,7 @@ int spapr_phb_vfio_eeh_set_option(SpaprPhbState *sphb, * call. Now we just need to check the validity of the PCI * pass-through devices (vfio-pci) under this sphb bus. * We have already validated that all the devices under this sphb - * are from same iommu group (within same PE) before comming here. + * are from same iommu group (within same PE) before coming here. * * Prior to linux commit 98ba956f6a389 ("powerpc/pseries/eeh: * Rework device EEH PE determination") kernel would call @@ -215,3 +316,37 @@ int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb) return RTAS_OUT_SUCCESS; } + +#else + +bool spapr_phb_eeh_available(SpaprPhbState *sphb) +{ + return false; +} + +void spapr_phb_vfio_reset(DeviceState *qdev) +{ +} + +int spapr_phb_vfio_eeh_set_option(SpaprPhbState *sphb, + unsigned int addr, int option) +{ + return RTAS_OUT_NOT_SUPPORTED; +} + +int spapr_phb_vfio_eeh_get_state(SpaprPhbState *sphb, int *state) +{ + return RTAS_OUT_NOT_SUPPORTED; +} + +int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option) +{ + return RTAS_OUT_NOT_SUPPORTED; +} + +int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb) +{ + return RTAS_OUT_NOT_SUPPORTED; +} + +#endif /* CONFIG_VFIO_PCI */ diff --git a/hw/ppc/spapr_rng.c b/hw/ppc/spapr_rng.c index df5c4b9687..c2fda7ad20 100644 --- a/hw/ppc/spapr_rng.c +++ b/hw/ppc/spapr_rng.c @@ -82,9 +82,9 @@ static target_ulong h_random(PowerPCCPU *cpu, SpaprMachineState *spapr, while (hrdata.received < 8) { rng_backend_request_entropy(rngstate->backend, 8 - hrdata.received, random_recv, &hrdata); - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_sem_wait(&hrdata.sem); - qemu_mutex_lock_iothread(); + bql_lock(); } qemu_sem_destroy(&hrdata.sem); diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index d58b65e88f..f329693c55 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -33,11 +33,11 @@ #include "sysemu/cpus.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" +#include "sysemu/qtest.h" #include "kvm_ppc.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" -#include "hw/ppc/spapr_rtas.h" #include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/ppc.h" @@ -214,9 +214,9 @@ static void rtas_stop_self(PowerPCCPU *cpu, SpaprMachineState *spapr, * guest. * For the same reason, set PSSCR_EC. */ - ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm); env->spr[SPR_PSSCR] |= PSSCR_EC; cs->halted = 1; + ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm); kvmppc_set_reg_ppc_online(cpu, 0); qemu_cpu_kick(cs); } @@ -495,7 +495,7 @@ static void rtas_ibm_nmi_interlock(PowerPCCPU *cpu, 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); + migrate_del_blocker(&spapr->fwnmi_migration_blocker); } static struct rtas_call { @@ -530,8 +530,8 @@ target_ulong spapr_rtas_call(PowerPCCPU *cpu, SpaprMachineState *spapr, return H_PARAMETER; } -uint64_t qtest_rtas_call(char *cmd, uint32_t nargs, uint64_t args, - uint32_t nret, uint64_t rets) +static uint64_t qtest_rtas_call(char *cmd, uint32_t nargs, uint64_t args, + uint32_t nret, uint64_t rets) { int token; @@ -548,6 +548,32 @@ uint64_t qtest_rtas_call(char *cmd, uint32_t nargs, uint64_t args, return H_PARAMETER; } +static bool spapr_qtest_callback(CharBackend *chr, gchar **words) +{ + if (strcmp(words[0], "rtas") == 0) { + uint64_t res, args, ret; + unsigned long nargs, nret; + int rc; + + rc = qemu_strtoul(words[2], NULL, 0, &nargs); + g_assert(rc == 0); + rc = qemu_strtou64(words[3], NULL, 0, &args); + g_assert(rc == 0); + rc = qemu_strtoul(words[4], NULL, 0, &nret); + g_assert(rc == 0); + rc = qemu_strtou64(words[5], NULL, 0, &ret); + g_assert(rc == 0); + res = qtest_rtas_call(words[1], nargs, args, nret, ret); + + qtest_send_prefix(chr); + qtest_sendf(chr, "OK %"PRIu64"\n", res); + + return true; + } + + return false; +} + void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn) { assert((token >= RTAS_TOKEN_BASE) && (token < RTAS_TOKEN_MAX)); @@ -630,6 +656,8 @@ static void core_rtas_register_types(void) rtas_ibm_nmi_register); spapr_rtas_register(RTAS_IBM_NMI_INTERLOCK, "ibm,nmi-interlock", rtas_ibm_nmi_interlock); + + qtest_set_command_cb(spapr_qtest_callback); } type_init(core_rtas_register_types) diff --git a/hw/ppc/spapr_rtas_ddw.c b/hw/ppc/spapr_rtas_ddw.c index 13d339c807..7ba11382bc 100644 --- a/hw/ppc/spapr_rtas_ddw.c +++ b/hw/ppc/spapr_rtas_ddw.c @@ -100,7 +100,7 @@ static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu, uint64_t buid; uint32_t avail, addr, pgmask = 0; - if ((nargs != 3) || (nret != 5)) { + if ((nargs != 3) || ((nret != 5) && (nret != 6))) { goto param_error_exit; } @@ -118,9 +118,20 @@ static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu, rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, avail); - rtas_st(rets, 2, 0x80000000); /* The largest window we can possibly have */ - rtas_st(rets, 3, pgmask); - rtas_st(rets, 4, 0); /* DMA migration mask, not supported */ + if (nret == 6) { + /* + * Set the Max TCE number as 1<<(58-21) = 0x20.0000.0000 + * 1<<59 is the huge window start and 21 is 2M page shift. + */ + rtas_st(rets, 2, 0x00000020); + rtas_st(rets, 3, 0x00000000); + rtas_st(rets, 4, pgmask); + rtas_st(rets, 5, 0); /* DMA migration mask, not supported */ + } else { + rtas_st(rets, 2, 0x80000000); + rtas_st(rets, 3, pgmask); + rtas_st(rets, 4, 0); /* DMA migration mask, not supported */ + } trace_spapr_iommu_ddw_query(buid, addr, avail, 0x80000000, pgmask); return; @@ -215,6 +226,7 @@ static void rtas_ibm_remove_pe_dma_window(PowerPCCPU *cpu, SpaprPhbState *sphb; SpaprTceTable *tcet; uint32_t liobn; + bool def_win_removed; if ((nargs != 1) || (nret != 1)) { goto param_error_exit; @@ -231,9 +243,23 @@ static void rtas_ibm_remove_pe_dma_window(PowerPCCPU *cpu, goto param_error_exit; } + def_win_removed = tcet->def_win; spapr_tce_table_disable(tcet); trace_spapr_iommu_ddw_remove(liobn); + /* + * PAPR+/LoPAPR says: + * The platform must restore the default DMA window for the PE on a call + * to the ibm,remove-pe-dma-window RTAS call when all of the following + * are true: + * a. The call removes the last DMA window remaining for the PE. + * b. The DMA window being removed is not the default window + */ + if (spapr_phb_get_active_win_num(sphb) == 0 && !def_win_removed) { + spapr_phb_dma_reset(sphb); + trace_spapr_iommu_ddw_reset(sphb->buid, 0); + } + rtas_st(rets, 0, RTAS_OUT_SUCCESS); return; diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c index d55b4b0c50..deb3ea4e49 100644 --- a/hw/ppc/spapr_rtc.c +++ b/hw/ppc/spapr_rtc.c @@ -157,7 +157,7 @@ static const VMStateDescription vmstate_spapr_rtc = { .name = "spapr/rtc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64(ns_offset, SpaprRtcState), VMSTATE_END_OF_LIST() }, diff --git a/hw/ppc/spapr_softmmu.c b/hw/ppc/spapr_vhyp_mmu.c similarity index 97% rename from hw/ppc/spapr_softmmu.c rename to hw/ppc/spapr_vhyp_mmu.c index 5170a33369..b3dd8b3a59 100644 --- a/hw/ppc/spapr_softmmu.c +++ b/hw/ppc/spapr_vhyp_mmu.c @@ -1,12 +1,23 @@ +/* + * MMU hypercalls for the sPAPR (pseries) vHyp hypervisor that is used by TCG + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * Copyright (c) 2010 David Gibson, IBM Corporation. + * + * SPDX-License-Identifier: MIT + */ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/memalign.h" +#include "qemu/error-report.h" #include "cpu.h" #include "helper_regs.h" #include "hw/ppc/spapr.h" #include "mmu-hash64.h" #include "mmu-book3s-v3.h" + static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex) { /* @@ -332,7 +343,7 @@ static void *hpt_prepare_thread(void *opaque) pending->ret = H_NO_MEM; } - qemu_mutex_lock_iothread(); + bql_lock(); if (SPAPR_MACHINE(qdev_get_machine())->pending_hpt == pending) { /* Ready to go */ @@ -342,7 +353,7 @@ static void *hpt_prepare_thread(void *opaque) free_pending_hpt(pending); } - qemu_mutex_unlock_iothread(); + bql_unlock(); return NULL; } @@ -367,7 +378,7 @@ static void cancel_hpt_prepare(SpaprMachineState *spapr) free_pending_hpt(pending); } -target_ulong softmmu_resize_hpt_prepare(PowerPCCPU *cpu, +target_ulong vhyp_mmu_resize_hpt_prepare(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong shift) { @@ -551,7 +562,7 @@ static int rehash_hpt(PowerPCCPU *cpu, return H_SUCCESS; } -target_ulong softmmu_resize_hpt_commit(PowerPCCPU *cpu, +target_ulong vhyp_mmu_resize_hpt_commit(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong flags, target_ulong shift) diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 9d4fec2c04..3221874848 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -574,13 +574,14 @@ SpaprVioBus *spapr_vio_bus_init(void) /* Create bridge device */ dev = qdev_new(TYPE_SPAPR_VIO_BRIDGE); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); /* Create bus on bridge device */ qbus = qbus_new(TYPE_SPAPR_VIO_BUS, dev, "spapr-vio"); bus = SPAPR_VIO_BUS(qbus); bus->next_reg = SPAPR_VIO_REG_BASE; + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + /* hcall-vio */ spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal); @@ -615,7 +616,7 @@ const VMStateDescription vmstate_spapr_vio = { .name = "spapr_vio", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { /* Sanity check */ VMSTATE_UINT32_EQUAL(reg, SpaprVioDevice, NULL), VMSTATE_UINT32_EQUAL(irq, SpaprVioDevice, NULL), diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index 5c0a215cad..bf29bbfd4b 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -95,6 +95,21 @@ vof_write(uint32_t ih, unsigned cb, const char *msg) "ih=0x%x [%u] \"%s\"" vof_avail(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 vof_claimed(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 +# pnv_chiptod.c +pnv_chiptod_xscom_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_chiptod_xscom_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 + +# pnv_sbe.c +pnv_sbe_xscom_ctrl_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_sbe_xscom_ctrl_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_sbe_xscom_mbox_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_sbe_xscom_mbox_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_sbe_reg_set_host_doorbell(uint64_t val) "val 0x%" PRIx64 +pnv_sbe_cmd_timer_start(uint64_t ns) "ns 0x%" PRIu64 +pnv_sbe_cmd_timer_stop(void) "" +pnv_sbe_cmd_timer_expired(void) "" +pnv_sbe_msg_recv(uint16_t cmd, uint16_t seq, uint16_t ctrl_flags) "cmd 0x%" PRIx16 " seq %"PRIu16 " ctrl_flags 0x%" PRIx16 + # ppc.c ppc_tb_adjust(uint64_t offs1, uint64_t offs2, int64_t diff, int64_t seconds) "adjusted from 0x%"PRIx64" to 0x%"PRIx64", diff %"PRId64" (%"PRId64"s)" ppc_tb_load(uint64_t tb) "tb 0x%016" PRIx64 @@ -116,7 +131,7 @@ ppc40x_set_tb_clk(uint32_t value) "new frequency %" PRIu32 ppc40x_timers_init(uint32_t value) "frequency %" PRIu32 ppc_irq_set(void *env, uint32_t pin, uint32_t level) "env [%p] pin %d level %d" -ppc_irq_set_exit(void *env, uint32_t n_IRQ, uint32_t level, uint32_t pending, uint32_t request) "env [%p] n_IRQ %d level %d => pending 0x%08" PRIx32 " req 0x%08" PRIx32 +ppc_irq_set_exit(void *env, uint32_t irq, uint32_t level, uint32_t pending, uint32_t request) "env [%p] irq 0x%05" PRIx32 " level %d => pending 0x%08" PRIx32 " req 0x%08" PRIx32 ppc_irq_set_state(const char *name, uint32_t level) "\"%s\" level %d" ppc_irq_reset(const char *name) "%s" ppc_irq_cpu(const char *action) "%s" @@ -135,26 +150,12 @@ rs6000mc_size_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x" rs6000mc_size_write(uint32_t addr, uint32_t val) "write addr=0x%x val=0x%x" rs6000mc_parity_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x" -# ppc4xx_pci.c -ppc4xx_pci_map_irq(int32_t devfn, int irq_num, int slot) "devfn 0x%x irq %d -> %d" -ppc4xx_pci_set_irq(int irq_num) "PCI irq %d" - -# ppc440_pcix.c -ppc440_pcix_map_irq(int32_t devfn, int irq_num, int slot) "devfn 0x%x irq %d -> %d" -ppc440_pcix_set_irq(int irq_num) "PCI irq %d" -ppc440_pcix_update_pim(int idx, uint64_t size, uint64_t la) "Added window %d of size=0x%" PRIx64 " to CPU=0x%" PRIx64 -ppc440_pcix_update_pom(int idx, uint32_t size, uint64_t la, uint64_t pcia) "Added window %d of size=0x%x from CPU=0x%" PRIx64 " to PCI=0x%" PRIx64 -ppc440_pcix_reg_read(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " = 0x%" PRIx32 -ppc440_pcix_reg_write(uint64_t addr, uint32_t val, uint32_t size) "addr 0x%" PRIx64 " = 0x%" PRIx32 " size 0x%" PRIx32 - # ppc405_boards.c opba_readb(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " = 0x%" PRIx32 opba_writeb(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " = 0x%" PRIx64 -opba_init(uint64_t addr) "offet 0x%" PRIx64 ppc405_gpio_read(uint64_t addr, uint32_t size) "addr 0x%" PRIx64 " size %d" ppc405_gpio_write(uint64_t addr, uint32_t size, uint64_t val) "addr 0x%" PRIx64 " size %d = 0x%" PRIx64 -ppc405_gpio_init(uint64_t addr) "offet 0x%" PRIx64 ocm_update_mappings(uint32_t isarc, uint32_t isacntl, uint32_t dsarc, uint32_t dsacntl, uint32_t ocm_isarc, uint32_t ocm_isacntl, uint32_t ocm_dsarc, uint32_t ocm_dsacntl) "OCM update ISA 0x%08" PRIx32 " 0x%08" PRIx32 " (0x%08" PRIx32" 0x%08" PRIx32 ") DSA 0x%08" PRIx32 " 0x%08" PRIx32" (0x%08" PRIx32 " 0x%08" PRIx32 ")" ocm_map(const char* prefix, uint32_t isarc) "OCM map %s 0x%08" PRIx32 @@ -162,7 +163,6 @@ ocm_unmap(const char* prefix, uint32_t isarc) "OCM unmap %s 0x%08" PRIx32 ppc4xx_gpt_read(uint64_t addr, uint32_t size) "addr 0x%" PRIx64 " size %d" ppc4xx_gpt_write(uint64_t addr, uint32_t size, uint64_t val) "addr 0x%" PRIx64 " size %d = 0x%" PRIx64 -ppc4xx_gpt_init(uint64_t addr) "offet 0x%" PRIx64 ppc405ep_clocks_compute(const char *param, uint32_t param2, uint32_t val) "%s 0x%1" PRIx32 " %d" ppc405ep_clocks_setup(const char *trace) "%s" @@ -171,3 +171,4 @@ ppc405ep_clocks_setup(const char *trace) "%s" ppc4xx_sdram_enable(const char *trace) "%s SDRAM controller" ppc4xx_sdram_unmap(uint64_t addr, uint64_t size) "Unmap RAM area 0x%" PRIx64 " size 0x%" PRIx64 ppc4xx_sdram_map(uint64_t addr, uint64_t size) "Map RAM area 0x%" PRIx64 " size 0x%" PRIx64 +ppc4xx_sdram_init(uint64_t base, uint64_t size, uint32_t bcr) "Init RAM area 0x%" PRIx64 " size 0x%" PRIx64 " bcr 0x%x" diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index b67a709ddc..d02f330650 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -43,7 +43,8 @@ #include "hw/ppc/ppc.h" #include "hw/ppc/ppc4xx.h" #include "hw/qdev-properties.h" -#include "ppc405.h" + +#include #define EPAPR_MAGIC (0x45504150) #define FLASH_SIZE (16 * MiB) @@ -104,16 +105,13 @@ static PowerPCCPU *ppc440_init_xilinx(const char *cpu_type, uint32_t sysclk) /* interrupt controller */ uicdev = qdev_new(TYPE_PPC_UIC); + ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(uicdev), cpu, &error_fatal); + object_unref(OBJECT(uicdev)); uicsbd = SYS_BUS_DEVICE(uicdev); - - object_property_set_link(OBJECT(uicdev), "cpu", OBJECT(cpu), - &error_fatal); - sysbus_realize_and_unref(uicsbd, &error_fatal); - sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]); + qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT)); sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT, - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]); + qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_CINT)); /* This board doesn't wire anything up to the inputs of the UIC. */ return cpu; @@ -147,11 +145,10 @@ static void main_cpu_reset(void *opaque) } #define BINARY_DEVICE_TREE_FILE "virtex-ml507.dtb" -static int xilinx_load_device_tree(hwaddr addr, - uint32_t ramsize, - hwaddr initrd_base, - hwaddr initrd_size, - const char *kernel_cmdline) +static int xilinx_load_device_tree(MachineState *machine, + hwaddr addr, + hwaddr initrd_base, + hwaddr initrd_size) { char *path; int fdt_size; @@ -159,7 +156,7 @@ static int xilinx_load_device_tree(hwaddr addr, int r; const char *dtb_filename; - dtb_filename = current_machine->dtb; + dtb_filename = machine->dtb; if (dtb_filename) { fdt = load_device_tree(dtb_filename, &fdt_size); if (!fdt) { @@ -193,18 +190,21 @@ static int xilinx_load_device_tree(hwaddr addr, error_report("couldn't set /chosen/linux,initrd-end"); } - r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); + r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", + machine->kernel_cmdline); if (r < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); cpu_physical_memory_write(addr, fdt, fdt_size); - g_free(fdt); + + /* Set machine->fdt for 'dumpdtb' QMP/HMP command */ + machine->fdt = fdt; + return fdt_size; } static void virtex_init(MachineState *machine) { const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; hwaddr initrd_base = 0; int initrd_size = 0; MemoryRegion *address_space_mem = get_system_memory(); @@ -213,7 +213,7 @@ static void virtex_init(MachineState *machine) CPUPPCState *env; hwaddr ram_base = 0; DriveInfo *dinfo; - qemu_irq irq[32], *cpu_irq; + qemu_irq irq[32], cpu_irq; int kernel_size; int i; @@ -236,12 +236,12 @@ static void virtex_init(MachineState *machine) dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, 64 * KiB, 1, 0x89, 0x18, 0x0000, 0x0, 1); - cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT]; + cpu_irq = qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT); dev = qdev_new("xlnx.xps-intc"); qdev_prop_set_uint32(dev, "kind-of-intr", 0); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irq[0]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irq); for (i = 0; i < 32; i++) { irq[i] = qdev_get_gpio_in(dev, i); } @@ -297,9 +297,8 @@ static void virtex_init(MachineState *machine) boot_info.fdt = high + (8192 * 2); boot_info.fdt &= ~8191; - xilinx_load_device_tree(boot_info.fdt, machine->ram_size, - initrd_base, initrd_size, - kernel_cmdline); + xilinx_load_device_tree(machine, boot_info.fdt, + initrd_base, initrd_size); } env->load_info = &boot_info; } diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c index 18c3f92317..e3b430a81f 100644 --- a/hw/ppc/vof.c +++ b/hw/ppc/vof.c @@ -1024,6 +1024,8 @@ void vof_cleanup(Vof *vof) } vof->claimed = NULL; vof->of_instances = NULL; + vof->of_instance_last = 0; + vof->claimed_base = 0; } void vof_build_dt(void *fdt, Vof *vof) diff --git a/hw/rdma/Kconfig b/hw/rdma/Kconfig index 8e2211288f..840320bdc0 100644 --- a/hw/rdma/Kconfig +++ b/hw/rdma/Kconfig @@ -1,3 +1,3 @@ config VMW_PVRDMA default y if PCI_DEVICES - depends on PVRDMA && PCI && MSI_NONBROKEN + depends on PVRDMA && MSI_NONBROKEN && VMXNET3_PCI diff --git a/hw/rdma/meson.build b/hw/rdma/meson.build index 7325f40c32..363c9b8c83 100644 --- a/hw/rdma/meson.build +++ b/hw/rdma/meson.build @@ -1,10 +1,12 @@ -specific_ss.add(when: 'CONFIG_VMW_PVRDMA', if_true: files( +system_ss.add(when: 'CONFIG_VMW_PVRDMA', if_true: files( 'rdma.c', 'rdma_backend.c', - 'rdma_rm.c', 'rdma_utils.c', + 'vmw/pvrdma_qp_ops.c', +)) +specific_ss.add(when: 'CONFIG_VMW_PVRDMA', if_true: files( + 'rdma_rm.c', 'vmw/pvrdma_cmd.c', 'vmw/pvrdma_dev_ring.c', 'vmw/pvrdma_main.c', - 'vmw/pvrdma_qp_ops.c', )) diff --git a/hw/rdma/rdma_rm.c b/hw/rdma/rdma_rm.c index cfd85de3e6..038d564433 100644 --- a/hw/rdma/rdma_rm.c +++ b/hw/rdma/rdma_rm.c @@ -23,10 +23,6 @@ #include "rdma_backend.h" #include "rdma_rm.h" -/* Page directory and page tables */ -#define PG_DIR_SZ { TARGET_PAGE_SIZE / sizeof(__u64) } -#define PG_TBL_SZ { TARGET_PAGE_SIZE / sizeof(__u64) } - void rdma_format_device_counters(RdmaDeviceResources *dev_res, GString *buf) { g_string_append_printf(buf, "\ttx : %" PRId64 "\n", diff --git a/hw/rdma/rdma_utils.c b/hw/rdma/rdma_utils.c index 5a7ef63ad2..c948baf052 100644 --- a/hw/rdma/rdma_utils.c +++ b/hw/rdma/rdma_utils.c @@ -14,6 +14,7 @@ */ #include "qemu/osdep.h" +#include "hw/pci/pci_device.h" #include "trace.h" #include "rdma_utils.h" diff --git a/hw/rdma/rdma_utils.h b/hw/rdma/rdma_utils.h index 0c6414e7e0..54e4f56edd 100644 --- a/hw/rdma/rdma_utils.h +++ b/hw/rdma/rdma_utils.h @@ -18,7 +18,6 @@ #define RDMA_UTILS_H #include "qemu/error-report.h" -#include "hw/pci/pci.h" #include "sysemu/dma.h" #define rdma_error_report(fmt, ...) \ diff --git a/hw/rdma/vmw/pvrdma.h b/hw/rdma/vmw/pvrdma.h index d08965d3e2..4cbc10c980 100644 --- a/hw/rdma/vmw/pvrdma.h +++ b/hw/rdma/vmw/pvrdma.h @@ -18,8 +18,8 @@ #include "qemu/units.h" #include "qemu/notify.h" -#include "hw/pci/pci.h" #include "hw/pci/msix.h" +#include "hw/pci/pci_device.h" #include "chardev/char-fe.h" #include "hw/net/vmxnet3_defs.h" diff --git a/hw/rdma/vmw/pvrdma_cmd.c b/hw/rdma/vmw/pvrdma_cmd.c index da7ddfa548..d385d18d9c 100644 --- a/hw/rdma/vmw/pvrdma_cmd.c +++ b/hw/rdma/vmw/pvrdma_cmd.c @@ -129,23 +129,27 @@ static int query_port(PVRDMADev *dev, union pvrdma_cmd_req *req, { struct pvrdma_cmd_query_port *cmd = &req->query_port; struct pvrdma_cmd_query_port_resp *resp = &rsp->query_port_resp; - struct pvrdma_port_attr attrs = {}; + struct ibv_port_attr attrs = {}; if (cmd->port_num > MAX_PORTS) { return -EINVAL; } - if (rdma_backend_query_port(&dev->backend_dev, - (struct ibv_port_attr *)&attrs)) { + if (rdma_backend_query_port(&dev->backend_dev, &attrs)) { return -ENOMEM; } memset(resp, 0, sizeof(*resp)); - resp->attrs.state = dev->func0->device_active ? attrs.state : - PVRDMA_PORT_DOWN; - resp->attrs.max_mtu = attrs.max_mtu; - resp->attrs.active_mtu = attrs.active_mtu; + /* + * The state, max_mtu and active_mtu fields are enums; the values + * for pvrdma_port_state and pvrdma_mtu match those for + * ibv_port_state and ibv_mtu, so we can cast them safely. + */ + resp->attrs.state = dev->func0->device_active ? + (enum pvrdma_port_state)attrs.state : PVRDMA_PORT_DOWN; + resp->attrs.max_mtu = (enum pvrdma_mtu)attrs.max_mtu; + resp->attrs.active_mtu = (enum pvrdma_mtu)attrs.active_mtu; resp->attrs.phys_state = attrs.phys_state; resp->attrs.gid_tbl_len = MIN(MAX_PORT_GIDS, attrs.gid_tbl_len); resp->attrs.max_msg_sz = 1024; @@ -182,13 +186,10 @@ static int create_pd(PVRDMADev *dev, union pvrdma_cmd_req *req, { struct pvrdma_cmd_create_pd *cmd = &req->create_pd; struct pvrdma_cmd_create_pd_resp *resp = &rsp->create_pd_resp; - int rc; memset(resp, 0, sizeof(*resp)); - rc = rdma_rm_alloc_pd(&dev->rdma_dev_res, &dev->backend_dev, - &resp->pd_handle, cmd->ctx_handle); - - return rc; + return rdma_rm_alloc_pd(&dev->rdma_dev_res, &dev->backend_dev, + &resp->pd_handle, cmd->ctx_handle); } static int destroy_pd(PVRDMADev *dev, union pvrdma_cmd_req *req, @@ -269,8 +270,7 @@ static int create_cq_ring(PCIDevice *pci_dev , PvrdmaRing **ring, r = g_malloc(sizeof(*r)); *ring = r; - r->ring_state = (PvrdmaRingState *) - rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); + r->ring_state = rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); if (!r->ring_state) { rdma_error_report("Failed to map to CQ ring state"); @@ -405,8 +405,7 @@ static int create_qp_rings(PCIDevice *pci_dev, uint64_t pdir_dma, *rings = sr; /* Create send ring */ - sr->ring_state = (PvrdmaRingState *) - rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); + sr->ring_state = rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); if (!sr->ring_state) { rdma_error_report("Failed to map to QP ring state"); goto out_free_sr_mem; @@ -506,20 +505,17 @@ static int modify_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, union pvrdma_cmd_resp *rsp) { struct pvrdma_cmd_modify_qp *cmd = &req->modify_qp; - int rc; /* No need to verify sgid_index since it is u8 */ - rc = rdma_rm_modify_qp(&dev->rdma_dev_res, &dev->backend_dev, - cmd->qp_handle, cmd->attr_mask, - cmd->attrs.ah_attr.grh.sgid_index, - (union ibv_gid *)&cmd->attrs.ah_attr.grh.dgid, - cmd->attrs.dest_qp_num, - (enum ibv_qp_state)cmd->attrs.qp_state, - cmd->attrs.qkey, cmd->attrs.rq_psn, - cmd->attrs.sq_psn); - - return rc; + return rdma_rm_modify_qp(&dev->rdma_dev_res, &dev->backend_dev, + cmd->qp_handle, cmd->attr_mask, + cmd->attrs.ah_attr.grh.sgid_index, + (union ibv_gid *)&cmd->attrs.ah_attr.grh.dgid, + cmd->attrs.dest_qp_num, + (enum ibv_qp_state)cmd->attrs.qp_state, + cmd->attrs.qkey, cmd->attrs.rq_psn, + cmd->attrs.sq_psn); } static int query_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, @@ -528,15 +524,14 @@ static int query_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, struct pvrdma_cmd_query_qp *cmd = &req->query_qp; struct pvrdma_cmd_query_qp_resp *resp = &rsp->query_qp_resp; struct ibv_qp_init_attr init_attr; - int rc; memset(resp, 0, sizeof(*resp)); - rc = rdma_rm_query_qp(&dev->rdma_dev_res, &dev->backend_dev, cmd->qp_handle, - (struct ibv_qp_attr *)&resp->attrs, cmd->attr_mask, - &init_attr); - - return rc; + return rdma_rm_query_qp(&dev->rdma_dev_res, &dev->backend_dev, + cmd->qp_handle, + (struct ibv_qp_attr *)&resp->attrs, + cmd->attr_mask, + &init_attr); } static int destroy_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, @@ -562,34 +557,27 @@ static int create_bind(PVRDMADev *dev, union pvrdma_cmd_req *req, union pvrdma_cmd_resp *rsp) { struct pvrdma_cmd_create_bind *cmd = &req->create_bind; - int rc; union ibv_gid *gid = (union ibv_gid *)&cmd->new_gid; if (cmd->index >= MAX_PORT_GIDS) { return -EINVAL; } - rc = rdma_rm_add_gid(&dev->rdma_dev_res, &dev->backend_dev, - dev->backend_eth_device_name, gid, cmd->index); - - return rc; + return rdma_rm_add_gid(&dev->rdma_dev_res, &dev->backend_dev, + dev->backend_eth_device_name, gid, cmd->index); } static int destroy_bind(PVRDMADev *dev, union pvrdma_cmd_req *req, union pvrdma_cmd_resp *rsp) { - int rc; - struct pvrdma_cmd_destroy_bind *cmd = &req->destroy_bind; if (cmd->index >= MAX_PORT_GIDS) { return -EINVAL; } - rc = rdma_rm_del_gid(&dev->rdma_dev_res, &dev->backend_dev, - dev->backend_eth_device_name, cmd->index); - - return rc; + return rdma_rm_del_gid(&dev->rdma_dev_res, &dev->backend_dev, + dev->backend_eth_device_name, cmd->index); } static int create_uc(PVRDMADev *dev, union pvrdma_cmd_req *req, @@ -597,12 +585,9 @@ static int create_uc(PVRDMADev *dev, union pvrdma_cmd_req *req, { struct pvrdma_cmd_create_uc *cmd = &req->create_uc; struct pvrdma_cmd_create_uc_resp *resp = &rsp->create_uc_resp; - int rc; memset(resp, 0, sizeof(*resp)); - rc = rdma_rm_alloc_uc(&dev->rdma_dev_res, cmd->pfn, &resp->ctx_handle); - - return rc; + return rdma_rm_alloc_uc(&dev->rdma_dev_res, cmd->pfn, &resp->ctx_handle); } static int destroy_uc(PVRDMADev *dev, union pvrdma_cmd_req *req, @@ -646,8 +631,7 @@ static int create_srq_ring(PCIDevice *pci_dev, PvrdmaRing **ring, r = g_malloc(sizeof(*r)); *ring = r; - r->ring_state = (PvrdmaRingState *) - rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); + r->ring_state = rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); if (!r->ring_state) { rdma_error_report("Failed to map tp SRQ ring state"); goto out_free_ring_mem; @@ -796,6 +780,12 @@ int pvrdma_exec_cmd(PVRDMADev *dev) dsr_info = &dev->dsr_info; + if (!dsr_info->dsr) { + /* Buggy or malicious guest driver */ + rdma_error_report("Exec command without dsr, req or rsp buffers"); + goto out; + } + if (dsr_info->req->hdr.cmd >= sizeof(cmd_handlers) / sizeof(struct cmd_handler)) { rdma_error_report("Unsupported command"); diff --git a/hw/rdma/vmw/pvrdma_dev_ring.c b/hw/rdma/vmw/pvrdma_dev_ring.c index 598e6adc5e..30ce22a5be 100644 --- a/hw/rdma/vmw/pvrdma_dev_ring.c +++ b/hw/rdma/vmw/pvrdma_dev_ring.c @@ -14,7 +14,6 @@ */ #include "qemu/osdep.h" -#include "qemu/cutils.h" #include "hw/pci/pci.h" #include "cpu.h" #include "qemu/cutils.h" diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c index 58db0b8e3b..e735ff97eb 100644 --- a/hw/rdma/vmw/pvrdma_main.c +++ b/hw/rdma/vmw/pvrdma_main.c @@ -91,19 +91,33 @@ static int init_dev_ring(PvrdmaRing *ring, PvrdmaRingState **ring_state, dma_addr_t dir_addr, uint32_t num_pages) { uint64_t *dir, *tbl; - int rc = 0; + int max_pages, rc = 0; if (!num_pages) { rdma_error_report("Ring pages count must be strictly positive"); return -EINVAL; } + /* + * Make sure we can satisfy the requested number of pages in a single + * TARGET_PAGE_SIZE sized page table (taking into account that first entry + * is reserved for ring-state) + */ + max_pages = TARGET_PAGE_SIZE / sizeof(dma_addr_t) - 1; + if (num_pages > max_pages) { + rdma_error_report("Maximum pages on a single directory must not exceed %d\n", + max_pages); + return -EINVAL; + } + dir = rdma_pci_dma_map(pci_dev, dir_addr, TARGET_PAGE_SIZE); if (!dir) { rdma_error_report("Failed to map to page directory (ring %s)", name); rc = -ENOMEM; goto out; } + + /* We support only one page table for a ring */ tbl = rdma_pci_dma_map(pci_dev, dir[0], TARGET_PAGE_SIZE); if (!tbl) { rdma_error_report("Failed to map to page table (ring %s)", name); @@ -307,12 +321,7 @@ static int init_msix(PCIDevice *pdev) } for (i = 0; i < RDMA_MAX_INTRS; i++) { - rc = msix_vector_use(PCI_DEVICE(dev), i); - if (rc < 0) { - rdma_error_report("Fail mark MSI-X vector %d", i); - uninit_msix(pdev, i); - return rc; - } + msix_vector_use(PCI_DEVICE(dev), i); } return 0; @@ -606,6 +615,8 @@ static void pvrdma_realize(PCIDevice *pdev, Error **errp) bool ram_shared = false; PCIDevice *func0; + warn_report_once("pvrdma is deprecated and will be removed in a future release"); + rdma_info_report("Initializing device %s %x.%x", pdev->name, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); diff --git a/hw/rdma/vmw/pvrdma_qp_ops.c b/hw/rdma/vmw/pvrdma_qp_ops.c index bd7cbf2bdf..c30c8344f6 100644 --- a/hw/rdma/vmw/pvrdma_qp_ops.c +++ b/hw/rdma/vmw/pvrdma_qp_ops.c @@ -149,7 +149,7 @@ void pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle) ring = (PvrdmaRing *)qp->opaque; - wqe = (struct PvrdmaSqWqe *)pvrdma_ring_next_elem_read(ring); + wqe = pvrdma_ring_next_elem_read(ring); while (wqe) { CompHandlerCtx *comp_ctx; @@ -212,7 +212,7 @@ void pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle) ring = &((PvrdmaRing *)qp->opaque)[1]; - wqe = (struct PvrdmaRqWqe *)pvrdma_ring_next_elem_read(ring); + wqe = pvrdma_ring_next_elem_read(ring); while (wqe) { CompHandlerCtx *comp_ctx; @@ -254,7 +254,7 @@ void pvrdma_srq_recv(PVRDMADev *dev, uint32_t srq_handle) ring = (PvrdmaRing *)srq->opaque; - wqe = (struct PvrdmaRqWqe *)pvrdma_ring_next_elem_read(ring); + wqe = pvrdma_ring_next_elem_read(ring); while (wqe) { CompHandlerCtx *comp_ctx; diff --git a/hw/remote/Kconfig b/hw/remote/Kconfig index 08c16e235f..2d6b4f4cf4 100644 --- a/hw/remote/Kconfig +++ b/hw/remote/Kconfig @@ -2,3 +2,7 @@ config MULTIPROCESS bool depends on PCI && PCI_EXPRESS && KVM select REMOTE_PCIHOST + +config VFIO_USER_SERVER + bool + depends on MULTIPROCESS diff --git a/hw/remote/iommu.c b/hw/remote/iommu.c new file mode 100644 index 0000000000..7c56aad0fc --- /dev/null +++ b/hw/remote/iommu.c @@ -0,0 +1,135 @@ +/** + * IOMMU for remote device + * + * Copyright © 2022 Oracle and/or its affiliates. + * + * 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/remote/iommu.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/pci.h" +#include "exec/memory.h" +#include "exec/address-spaces.h" +#include "trace.h" + +/** + * IOMMU for TYPE_REMOTE_MACHINE - manages DMA address space isolation + * for remote machine. It is used by TYPE_VFIO_USER_SERVER. + * + * - Each TYPE_VFIO_USER_SERVER instance handles one PCIDevice on a PCIBus. + * There is one RemoteIommu per PCIBus, so the RemoteIommu tracks multiple + * PCIDevices by maintaining a ->elem_by_devfn mapping. + * + * - memory_region_init_iommu() is not used because vfio-user MemoryRegions + * will be added to the elem->mr container instead. This is more natural + * than implementing the IOMMUMemoryRegionClass APIs since vfio-user + * provides something that is close to a full-fledged MemoryRegion and + * not like an IOMMU mapping. + * + * - When a device is hot unplugged, the elem->mr reference is dropped so + * all vfio-user MemoryRegions associated with this vfio-user server are + * destroyed. + */ + +static AddressSpace *remote_iommu_find_add_as(PCIBus *pci_bus, + void *opaque, int devfn) +{ + RemoteIommu *iommu = opaque; + RemoteIommuElem *elem = NULL; + + qemu_mutex_lock(&iommu->lock); + + elem = g_hash_table_lookup(iommu->elem_by_devfn, INT2VOIDP(devfn)); + + if (!elem) { + elem = g_new0(RemoteIommuElem, 1); + g_hash_table_insert(iommu->elem_by_devfn, INT2VOIDP(devfn), elem); + } + + if (!elem->mr) { + elem->mr = MEMORY_REGION(object_new(TYPE_MEMORY_REGION)); + memory_region_set_size(elem->mr, UINT64_MAX); + address_space_init(&elem->as, elem->mr, NULL); + } + + qemu_mutex_unlock(&iommu->lock); + + return &elem->as; +} + +void remote_iommu_unplug_dev(PCIDevice *pci_dev) +{ + AddressSpace *as = pci_device_iommu_address_space(pci_dev); + RemoteIommuElem *elem = NULL; + + if (as == &address_space_memory) { + return; + } + + elem = container_of(as, RemoteIommuElem, as); + + address_space_destroy(&elem->as); + + object_unref(elem->mr); + + elem->mr = NULL; +} + +static void remote_iommu_init(Object *obj) +{ + RemoteIommu *iommu = REMOTE_IOMMU(obj); + + iommu->elem_by_devfn = g_hash_table_new_full(NULL, NULL, NULL, g_free); + + qemu_mutex_init(&iommu->lock); +} + +static void remote_iommu_finalize(Object *obj) +{ + RemoteIommu *iommu = REMOTE_IOMMU(obj); + + qemu_mutex_destroy(&iommu->lock); + + g_hash_table_destroy(iommu->elem_by_devfn); + + iommu->elem_by_devfn = NULL; +} + +static const PCIIOMMUOps remote_iommu_ops = { + .get_address_space = remote_iommu_find_add_as, +}; + +void remote_iommu_setup(PCIBus *pci_bus) +{ + RemoteIommu *iommu = NULL; + + g_assert(pci_bus); + + iommu = REMOTE_IOMMU(object_new(TYPE_REMOTE_IOMMU)); + + pci_setup_iommu(pci_bus, &remote_iommu_ops, iommu); + + object_property_add_child(OBJECT(pci_bus), "remote-iommu", OBJECT(iommu)); + + object_unref(OBJECT(iommu)); +} + +static const TypeInfo remote_iommu_info = { + .name = TYPE_REMOTE_IOMMU, + .parent = TYPE_OBJECT, + .instance_size = sizeof(RemoteIommu), + .instance_init = remote_iommu_init, + .instance_finalize = remote_iommu_finalize, +}; + +static void remote_iommu_register_types(void) +{ + type_register_static(&remote_iommu_info); +} + +type_init(remote_iommu_register_types) diff --git a/hw/remote/machine.c b/hw/remote/machine.c index 92d71d47bb..fdc6c441bb 100644 --- a/hw/remote/machine.c +++ b/hw/remote/machine.c @@ -20,6 +20,10 @@ #include "qapi/error.h" #include "hw/pci/pci_host.h" #include "hw/remote/iohub.h" +#include "hw/remote/iommu.h" +#include "hw/qdev-core.h" +#include "hw/remote/vfio-user-obj.h" +#include "hw/pci/msi.h" static void remote_machine_init(MachineState *machine) { @@ -49,25 +53,103 @@ static void remote_machine_init(MachineState *machine) pci_host = PCI_HOST_BRIDGE(rem_host); - remote_iohub_init(&s->iohub); + if (s->vfio_user) { + remote_iommu_setup(pci_host->bus); - pci_bus_irqs(pci_host->bus, remote_iohub_set_irq, remote_iohub_map_irq, - &s->iohub, REMOTE_IOHUB_NB_PIRQS); + msi_nonbroken = true; + + vfu_object_set_bus_irq(pci_host->bus); + } else { + remote_iohub_init(&s->iohub); + + pci_bus_irqs(pci_host->bus, remote_iohub_set_irq, + &s->iohub, REMOTE_IOHUB_NB_PIRQS); + pci_bus_map_irqs(pci_host->bus, remote_iohub_map_irq); + } + + qbus_set_hotplug_handler(BUS(pci_host->bus), OBJECT(s)); +} + +static bool remote_machine_get_vfio_user(Object *obj, Error **errp) +{ + RemoteMachineState *s = REMOTE_MACHINE(obj); + + return s->vfio_user; +} + +static void remote_machine_set_vfio_user(Object *obj, bool value, Error **errp) +{ + RemoteMachineState *s = REMOTE_MACHINE(obj); + + if (phase_check(PHASE_MACHINE_CREATED)) { + error_setg(errp, "Error enabling vfio-user - machine already created"); + return; + } + + s->vfio_user = value; +} + +static bool remote_machine_get_auto_shutdown(Object *obj, Error **errp) +{ + RemoteMachineState *s = REMOTE_MACHINE(obj); + + return s->auto_shutdown; +} + +static void remote_machine_set_auto_shutdown(Object *obj, bool value, + Error **errp) +{ + RemoteMachineState *s = REMOTE_MACHINE(obj); + + s->auto_shutdown = value; +} + +static void remote_machine_instance_init(Object *obj) +{ + RemoteMachineState *s = REMOTE_MACHINE(obj); + + s->auto_shutdown = true; +} + +static void remote_machine_dev_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + qdev_unrealize(dev); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + remote_iommu_unplug_dev(PCI_DEVICE(dev)); + } } static void remote_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); mc->init = remote_machine_init; mc->desc = "Experimental remote machine"; + + hc->unplug = remote_machine_dev_unplug_cb; + + object_class_property_add_bool(oc, "vfio-user", + remote_machine_get_vfio_user, + remote_machine_set_vfio_user); + + object_class_property_add_bool(oc, "auto-shutdown", + remote_machine_get_auto_shutdown, + remote_machine_set_auto_shutdown); } static const TypeInfo remote_machine = { .name = TYPE_REMOTE_MACHINE, .parent = TYPE_MACHINE, .instance_size = sizeof(RemoteMachineState), + .instance_init = remote_machine_instance_init, .class_init = remote_machine_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; static void remote_machine_register_types(void) diff --git a/hw/remote/meson.build b/hw/remote/meson.build index e6a5574242..41eb4971d9 100644 --- a/hw/remote/meson.build +++ b/hw/remote/meson.build @@ -6,8 +6,13 @@ remote_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('message.c')) remote_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('remote-obj.c')) remote_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('proxy.c')) remote_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('iohub.c')) +remote_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('iommu.c')) + +remote_ss.add(when: 'CONFIG_VFIO_USER_SERVER', if_true: libvfio_user_dep) +remote_ss.add(when: 'CONFIG_VFIO_USER_SERVER', if_true: files('vfio-user-obj.c'), + if_false: files('vfio-user-obj-stub.c')) specific_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('memory.c')) specific_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('proxy-memory-listener.c')) -softmmu_ss.add_all(when: 'CONFIG_MULTIPROCESS', if_true: remote_ss) +system_ss.add_all(when: 'CONFIG_MULTIPROCESS', if_true: remote_ss) diff --git a/hw/remote/mpqemu-link.c b/hw/remote/mpqemu-link.c index 9bd98e8219..4394dc4d82 100644 --- a/hw/remote/mpqemu-link.c +++ b/hw/remote/mpqemu-link.c @@ -33,7 +33,7 @@ */ bool mpqemu_msg_send(MPQemuMsg *msg, QIOChannel *ioc, Error **errp) { - bool iolock = qemu_mutex_iothread_locked(); + bool drop_bql = bql_locked(); bool iothread = qemu_in_iothread(); struct iovec send[2] = {}; int *fds = NULL; @@ -58,13 +58,13 @@ bool mpqemu_msg_send(MPQemuMsg *msg, QIOChannel *ioc, Error **errp) assert(qemu_in_coroutine() || !iothread); /* - * Skip unlocking/locking iothread lock when the IOThread is running + * Skip unlocking/locking BQL when the IOThread is running * in co-routine context. Co-routine context is asserted above * for IOThread case. * Also skip lock handling while in a co-routine in the main context. */ - if (iolock && !iothread && !qemu_in_coroutine()) { - qemu_mutex_unlock_iothread(); + if (drop_bql && !iothread && !qemu_in_coroutine()) { + bql_unlock(); } if (!qio_channel_writev_full_all(ioc, send, G_N_ELEMENTS(send), @@ -74,9 +74,9 @@ bool mpqemu_msg_send(MPQemuMsg *msg, QIOChannel *ioc, Error **errp) trace_mpqemu_send_io_error(msg->cmd, msg->size, nfds); } - if (iolock && !iothread && !qemu_in_coroutine()) { + if (drop_bql && !iothread && !qemu_in_coroutine()) { /* See above comment why skip locking here. */ - qemu_mutex_lock_iothread(); + bql_lock(); } return ret; @@ -96,7 +96,7 @@ static ssize_t mpqemu_read(QIOChannel *ioc, void *buf, size_t len, int **fds, size_t *nfds, Error **errp) { struct iovec iov = { .iov_base = buf, .iov_len = len }; - bool iolock = qemu_mutex_iothread_locked(); + bool drop_bql = bql_locked(); bool iothread = qemu_in_iothread(); int ret = -1; @@ -106,14 +106,14 @@ static ssize_t mpqemu_read(QIOChannel *ioc, void *buf, size_t len, int **fds, */ assert(qemu_in_coroutine() || !iothread); - if (iolock && !iothread && !qemu_in_coroutine()) { - qemu_mutex_unlock_iothread(); + if (drop_bql && !iothread && !qemu_in_coroutine()) { + bql_unlock(); } ret = qio_channel_readv_full_all_eof(ioc, &iov, 1, fds, nfds, errp); - if (iolock && !iothread && !qemu_in_coroutine()) { - qemu_mutex_lock_iothread(); + if (drop_bql && !iothread && !qemu_in_coroutine()) { + bql_lock(); } return (ret <= 0) ? ret : iov.iov_len; diff --git a/hw/remote/proxy-memory-listener.c b/hw/remote/proxy-memory-listener.c index eb9918fe72..a926f61ebe 100644 --- a/hw/remote/proxy-memory-listener.c +++ b/hw/remote/proxy-memory-listener.c @@ -8,7 +8,6 @@ #include "qemu/osdep.h" -#include "qemu/compiler.h" #include "qemu/int128.h" #include "qemu/range.h" #include "exec/memory.h" @@ -218,7 +217,7 @@ void proxy_memory_listener_configure(ProxyMemoryListener *proxy_listener, proxy_listener->listener.commit = proxy_memory_listener_commit; proxy_listener->listener.region_add = proxy_memory_listener_region_addnop; proxy_listener->listener.region_nop = proxy_memory_listener_region_addnop; - proxy_listener->listener.priority = 10; + proxy_listener->listener.priority = MEMORY_LISTENER_PRIORITY_DEV_BACKEND; proxy_listener->listener.name = "proxy"; memory_listener_register(&proxy_listener->listener, diff --git a/hw/remote/proxy.c b/hw/remote/proxy.c index 1c7786b52c..fbc85a8d36 100644 --- a/hw/remote/proxy.c +++ b/hw/remote/proxy.c @@ -22,7 +22,6 @@ #include "qom/object.h" #include "qemu/event_notifier.h" #include "sysemu/kvm.h" -#include "util/event_notifier-posix.c" static void probe_pci_info(PCIDevice *dev, Error **errp); static void proxy_device_reset(DeviceState *dev); @@ -108,8 +107,7 @@ static void pci_proxy_dev_realize(PCIDevice *device, Error **errp) error_setg(&dev->migration_blocker, "%s does not support migration", TYPE_PCI_PROXY_DEV); - if (migrate_add_blocker(dev->migration_blocker, errp) < 0) { - error_free(dev->migration_blocker); + if (migrate_add_blocker(&dev->migration_blocker, errp) < 0) { object_unref(dev->ioc); return; } @@ -135,9 +133,7 @@ static void pci_proxy_dev_exit(PCIDevice *pdev) qio_channel_close(dev->ioc, NULL); } - migrate_del_blocker(dev->migration_blocker); - - error_free(dev->migration_blocker); + migrate_del_blocker(&dev->migration_blocker); proxy_memory_listener_deconfigure(&dev->proxy_listener); diff --git a/hw/remote/remote-obj.c b/hw/remote/remote-obj.c index 333e5ac443..dc27cc8da1 100644 --- a/hw/remote/remote-obj.c +++ b/hw/remote/remote-obj.c @@ -12,7 +12,6 @@ #include "qemu/error-report.h" #include "qemu/notify.h" #include "qom/object_interfaces.h" -#include "hw/qdev-core.h" #include "io/channel.h" #include "hw/qdev-core.h" #include "hw/remote/machine.h" @@ -50,6 +49,7 @@ struct RemoteObject { static void remote_object_set_fd(Object *obj, const char *str, Error **errp) { + ERRP_GUARD(); RemoteObject *o = REMOTE_OBJECT(obj); int fd = -1; diff --git a/hw/remote/trace-events b/hw/remote/trace-events index 0b23974f90..0d1b7d56a5 100644 --- a/hw/remote/trace-events +++ b/hw/remote/trace-events @@ -2,3 +2,14 @@ mpqemu_send_io_error(int cmd, int size, int nfds) "send command %d size %d, %d file descriptors to remote process" mpqemu_recv_io_error(int cmd, int size, int nfds) "failed to receive %d size %d, %d file descriptors to remote process" + +# vfio-user-obj.c +vfu_prop(const char *prop, const char *val) "vfu: setting %s as %s" +vfu_cfg_read(uint32_t offset, uint32_t val) "vfu: cfg: 0x%x -> 0x%x" +vfu_cfg_write(uint32_t offset, uint32_t val) "vfu: cfg: 0x%x <- 0x%x" +vfu_dma_register(uint64_t gpa, size_t len) "vfu: registering GPA 0x%"PRIx64", %zu bytes" +vfu_dma_unregister(uint64_t gpa) "vfu: unregistering GPA 0x%"PRIx64"" +vfu_bar_register(int i, uint64_t addr, uint64_t size) "vfu: BAR %d: addr 0x%"PRIx64" size 0x%"PRIx64"" +vfu_bar_rw_enter(const char *op, uint64_t addr) "vfu: %s request for BAR address 0x%"PRIx64"" +vfu_bar_rw_exit(const char *op, uint64_t addr) "vfu: Finished %s of BAR address 0x%"PRIx64"" +vfu_interrupt(int pirq) "vfu: sending interrupt to device - PIRQ %d" diff --git a/hw/remote/vfio-user-obj-stub.c b/hw/remote/vfio-user-obj-stub.c new file mode 100644 index 0000000000..79100d768e --- /dev/null +++ b/hw/remote/vfio-user-obj-stub.c @@ -0,0 +1,6 @@ +#include "qemu/osdep.h" +#include "hw/remote/vfio-user-obj.h" + +void vfu_object_set_bus_irq(PCIBus *pci_bus) +{ +} diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c new file mode 100644 index 0000000000..d9b879e056 --- /dev/null +++ b/hw/remote/vfio-user-obj.c @@ -0,0 +1,958 @@ +/** + * QEMU vfio-user-server server object + * + * Copyright © 2022 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL-v2, version 2 or later. + * + * See the COPYING file in the top-level directory. + * + */ + +/** + * Usage: add options: + * -machine x-remote,vfio-user=on,auto-shutdown=on + * -device ,id= + * -object x-vfio-user-server,id=,type=unix,path=, + * device= + * + * Note that x-vfio-user-server object must be used with x-remote machine only. + * This server could only support PCI devices for now. + * + * type - SocketAddress type - presently "unix" alone is supported. Required + * option + * + * path - named unix socket, it will be created by the server. It is + * a required option + * + * device - id of a device on the server, a required option. PCI devices + * alone are supported presently. + * + * notes - x-vfio-user-server could block IO and monitor during the + * initialization phase. + * + * When x-remote machine has the auto-shutdown property + * enabled (default), x-vfio-user-server terminates after the last + * client disconnects. Otherwise, it will continue running until + * explicitly killed. + */ + +#include "qemu/osdep.h" + +#include "qom/object.h" +#include "qom/object_interfaces.h" +#include "qemu/error-report.h" +#include "trace.h" +#include "sysemu/runstate.h" +#include "hw/boards.h" +#include "hw/remote/machine.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-sockets.h" +#include "qapi/qapi-events-misc.h" +#include "qemu/notify.h" +#include "qemu/thread.h" +#include "qemu/main-loop.h" +#include "sysemu/sysemu.h" +#include "libvfio-user.h" +#include "hw/qdev-core.h" +#include "hw/pci/pci.h" +#include "qemu/timer.h" +#include "exec/memory.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" +#include "hw/remote/vfio-user-obj.h" + +#define TYPE_VFU_OBJECT "x-vfio-user-server" +OBJECT_DECLARE_TYPE(VfuObject, VfuObjectClass, VFU_OBJECT) + +/** + * VFU_OBJECT_ERROR - reports an error message. + * + * If auto_shutdown is set, it aborts the machine on error. Otherwise, + * it logs an error message without aborting. auto_shutdown is disabled + * when the server serves clients from multiple VMs; as such, an error + * from one VM shouldn't be able to disrupt other VM's services. + */ +#define VFU_OBJECT_ERROR(o, fmt, ...) \ + { \ + if (vfu_object_auto_shutdown()) { \ + error_setg(&error_abort, (fmt), ## __VA_ARGS__); \ + } else { \ + error_report((fmt), ## __VA_ARGS__); \ + } \ + } \ + +struct VfuObjectClass { + ObjectClass parent_class; + + unsigned int nr_devs; +}; + +struct VfuObject { + /* private */ + Object parent; + + SocketAddress *socket; + + char *device; + + Error *err; + + Notifier machine_done; + + vfu_ctx_t *vfu_ctx; + + PCIDevice *pci_dev; + + Error *unplug_blocker; + + int vfu_poll_fd; + + MSITriggerFunc *default_msi_trigger; + MSIPrepareMessageFunc *default_msi_prepare_message; + MSIxPrepareMessageFunc *default_msix_prepare_message; +}; + +static void vfu_object_init_ctx(VfuObject *o, Error **errp); + +static bool vfu_object_auto_shutdown(void) +{ + bool auto_shutdown = true; + Error *local_err = NULL; + + if (!current_machine) { + return auto_shutdown; + } + + auto_shutdown = object_property_get_bool(OBJECT(current_machine), + "auto-shutdown", + &local_err); + + /* + * local_err would be set if no such property exists - safe to ignore. + * Unlikely scenario as auto-shutdown is always defined for + * TYPE_REMOTE_MACHINE, and TYPE_VFU_OBJECT only works with + * TYPE_REMOTE_MACHINE + */ + if (local_err) { + auto_shutdown = true; + error_free(local_err); + } + + return auto_shutdown; +} + +static void vfu_object_set_socket(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + VfuObject *o = VFU_OBJECT(obj); + + if (o->vfu_ctx) { + error_setg(errp, "vfu: Unable to set socket property - server busy"); + return; + } + + qapi_free_SocketAddress(o->socket); + + o->socket = NULL; + + visit_type_SocketAddress(v, name, &o->socket, errp); + + if (o->socket->type != SOCKET_ADDRESS_TYPE_UNIX) { + error_setg(errp, "vfu: Unsupported socket type - %s", + SocketAddressType_str(o->socket->type)); + qapi_free_SocketAddress(o->socket); + o->socket = NULL; + return; + } + + trace_vfu_prop("socket", o->socket->u.q_unix.path); + + vfu_object_init_ctx(o, errp); +} + +static void vfu_object_set_device(Object *obj, const char *str, Error **errp) +{ + VfuObject *o = VFU_OBJECT(obj); + + if (o->vfu_ctx) { + error_setg(errp, "vfu: Unable to set device property - server busy"); + return; + } + + g_free(o->device); + + o->device = g_strdup(str); + + trace_vfu_prop("device", str); + + vfu_object_init_ctx(o, errp); +} + +static void vfu_object_ctx_run(void *opaque) +{ + VfuObject *o = opaque; + const char *vfu_id; + char *vfu_path, *pci_dev_path; + int ret = -1; + + while (ret != 0) { + ret = vfu_run_ctx(o->vfu_ctx); + if (ret < 0) { + if (errno == EINTR) { + continue; + } else if (errno == ENOTCONN) { + vfu_id = object_get_canonical_path_component(OBJECT(o)); + vfu_path = object_get_canonical_path(OBJECT(o)); + g_assert(o->pci_dev); + pci_dev_path = object_get_canonical_path(OBJECT(o->pci_dev)); + /* o->device is a required property and is non-NULL here */ + g_assert(o->device); + qapi_event_send_vfu_client_hangup(vfu_id, vfu_path, + o->device, pci_dev_path); + qemu_set_fd_handler(o->vfu_poll_fd, NULL, NULL, NULL); + o->vfu_poll_fd = -1; + object_unparent(OBJECT(o)); + g_free(vfu_path); + g_free(pci_dev_path); + break; + } else { + VFU_OBJECT_ERROR(o, "vfu: Failed to run device %s - %s", + o->device, strerror(errno)); + break; + } + } + } +} + +static void vfu_object_attach_ctx(void *opaque) +{ + VfuObject *o = opaque; + GPollFD pfds[1]; + int ret; + + qemu_set_fd_handler(o->vfu_poll_fd, NULL, NULL, NULL); + + pfds[0].fd = o->vfu_poll_fd; + pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR; + +retry_attach: + ret = vfu_attach_ctx(o->vfu_ctx); + if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + /** + * vfu_object_attach_ctx can block QEMU's main loop + * during attach - the monitor and other IO + * could be unresponsive during this time. + */ + (void)qemu_poll_ns(pfds, 1, 500 * (int64_t)SCALE_MS); + goto retry_attach; + } else if (ret < 0) { + VFU_OBJECT_ERROR(o, "vfu: Failed to attach device %s to context - %s", + o->device, strerror(errno)); + return; + } + + o->vfu_poll_fd = vfu_get_poll_fd(o->vfu_ctx); + if (o->vfu_poll_fd < 0) { + VFU_OBJECT_ERROR(o, "vfu: Failed to get poll fd %s", o->device); + return; + } + + qemu_set_fd_handler(o->vfu_poll_fd, vfu_object_ctx_run, NULL, o); +} + +static ssize_t vfu_object_cfg_access(vfu_ctx_t *vfu_ctx, char * const buf, + size_t count, loff_t offset, + const bool is_write) +{ + VfuObject *o = vfu_get_private(vfu_ctx); + uint32_t pci_access_width = sizeof(uint32_t); + size_t bytes = count; + uint32_t val = 0; + char *ptr = buf; + int len; + + /* + * Writes to the BAR registers would trigger an update to the + * global Memory and IO AddressSpaces. But the remote device + * never uses the global AddressSpaces, therefore overlapping + * memory regions are not a problem + */ + while (bytes > 0) { + len = (bytes > pci_access_width) ? pci_access_width : bytes; + if (is_write) { + memcpy(&val, ptr, len); + pci_host_config_write_common(o->pci_dev, offset, + pci_config_size(o->pci_dev), + val, len); + trace_vfu_cfg_write(offset, val); + } else { + val = pci_host_config_read_common(o->pci_dev, offset, + pci_config_size(o->pci_dev), len); + memcpy(ptr, &val, len); + trace_vfu_cfg_read(offset, val); + } + offset += len; + ptr += len; + bytes -= len; + } + + return count; +} + +static void dma_register(vfu_ctx_t *vfu_ctx, vfu_dma_info_t *info) +{ + VfuObject *o = vfu_get_private(vfu_ctx); + AddressSpace *dma_as = NULL; + MemoryRegion *subregion = NULL; + g_autofree char *name = NULL; + struct iovec *iov = &info->iova; + + if (!info->vaddr) { + return; + } + + name = g_strdup_printf("mem-%s-%"PRIx64"", o->device, + (uint64_t)info->vaddr); + + subregion = g_new0(MemoryRegion, 1); + + memory_region_init_ram_ptr(subregion, NULL, name, + iov->iov_len, info->vaddr); + + dma_as = pci_device_iommu_address_space(o->pci_dev); + + memory_region_add_subregion(dma_as->root, (hwaddr)iov->iov_base, subregion); + + trace_vfu_dma_register((uint64_t)iov->iov_base, iov->iov_len); +} + +static void dma_unregister(vfu_ctx_t *vfu_ctx, vfu_dma_info_t *info) +{ + VfuObject *o = vfu_get_private(vfu_ctx); + AddressSpace *dma_as = NULL; + MemoryRegion *mr = NULL; + ram_addr_t offset; + + mr = memory_region_from_host(info->vaddr, &offset); + if (!mr) { + return; + } + + dma_as = pci_device_iommu_address_space(o->pci_dev); + + memory_region_del_subregion(dma_as->root, mr); + + object_unparent((OBJECT(mr))); + + trace_vfu_dma_unregister((uint64_t)info->iova.iov_base); +} + +static int vfu_object_mr_rw(MemoryRegion *mr, uint8_t *buf, hwaddr offset, + hwaddr size, const bool is_write) +{ + uint8_t *ptr = buf; + bool release_lock = false; + uint8_t *ram_ptr = NULL; + MemTxResult result; + int access_size; + uint64_t val; + + if (memory_access_is_direct(mr, is_write)) { + /** + * Some devices expose a PCI expansion ROM, which could be buffer + * based as compared to other regions which are primarily based on + * MemoryRegionOps. memory_region_find() would already check + * for buffer overflow, we don't need to repeat it here. + */ + ram_ptr = memory_region_get_ram_ptr(mr); + + if (is_write) { + memcpy((ram_ptr + offset), buf, size); + } else { + memcpy(buf, (ram_ptr + offset), size); + } + + return 0; + } + + while (size) { + /** + * The read/write logic used below is similar to the ones in + * flatview_read/write_continue() + */ + release_lock = prepare_mmio_access(mr); + + access_size = memory_access_size(mr, size, offset); + + if (is_write) { + val = ldn_he_p(ptr, access_size); + + result = memory_region_dispatch_write(mr, offset, val, + size_memop(access_size), + MEMTXATTRS_UNSPECIFIED); + } else { + result = memory_region_dispatch_read(mr, offset, &val, + size_memop(access_size), + MEMTXATTRS_UNSPECIFIED); + + stn_he_p(ptr, access_size, val); + } + + if (release_lock) { + bql_unlock(); + release_lock = false; + } + + if (result != MEMTX_OK) { + return -1; + } + + size -= access_size; + ptr += access_size; + offset += access_size; + } + + return 0; +} + +static size_t vfu_object_bar_rw(PCIDevice *pci_dev, int pci_bar, + hwaddr bar_offset, char * const buf, + hwaddr len, const bool is_write) +{ + MemoryRegionSection section = { 0 }; + uint8_t *ptr = (uint8_t *)buf; + MemoryRegion *section_mr = NULL; + uint64_t section_size; + hwaddr section_offset; + hwaddr size = 0; + + while (len) { + section = memory_region_find(pci_dev->io_regions[pci_bar].memory, + bar_offset, len); + + if (!section.mr) { + warn_report("vfu: invalid address 0x%"PRIx64"", bar_offset); + return size; + } + + section_mr = section.mr; + section_offset = section.offset_within_region; + section_size = int128_get64(section.size); + + if (is_write && section_mr->readonly) { + warn_report("vfu: attempting to write to readonly region in " + "bar %d - [0x%"PRIx64" - 0x%"PRIx64"]", + pci_bar, bar_offset, + (bar_offset + section_size)); + memory_region_unref(section_mr); + return size; + } + + if (vfu_object_mr_rw(section_mr, ptr, section_offset, + section_size, is_write)) { + warn_report("vfu: failed to %s " + "[0x%"PRIx64" - 0x%"PRIx64"] in bar %d", + is_write ? "write to" : "read from", bar_offset, + (bar_offset + section_size), pci_bar); + memory_region_unref(section_mr); + return size; + } + + size += section_size; + bar_offset += section_size; + ptr += section_size; + len -= section_size; + + memory_region_unref(section_mr); + } + + return size; +} + +/** + * VFU_OBJECT_BAR_HANDLER - macro for defining handlers for PCI BARs. + * + * To create handler for BAR number 2, VFU_OBJECT_BAR_HANDLER(2) would + * define vfu_object_bar2_handler + */ +#define VFU_OBJECT_BAR_HANDLER(BAR_NO) \ + static ssize_t vfu_object_bar##BAR_NO##_handler(vfu_ctx_t *vfu_ctx, \ + char * const buf, size_t count, \ + loff_t offset, const bool is_write) \ + { \ + VfuObject *o = vfu_get_private(vfu_ctx); \ + PCIDevice *pci_dev = o->pci_dev; \ + \ + return vfu_object_bar_rw(pci_dev, BAR_NO, offset, \ + buf, count, is_write); \ + } \ + +VFU_OBJECT_BAR_HANDLER(0) +VFU_OBJECT_BAR_HANDLER(1) +VFU_OBJECT_BAR_HANDLER(2) +VFU_OBJECT_BAR_HANDLER(3) +VFU_OBJECT_BAR_HANDLER(4) +VFU_OBJECT_BAR_HANDLER(5) +VFU_OBJECT_BAR_HANDLER(6) + +static vfu_region_access_cb_t *vfu_object_bar_handlers[PCI_NUM_REGIONS] = { + &vfu_object_bar0_handler, + &vfu_object_bar1_handler, + &vfu_object_bar2_handler, + &vfu_object_bar3_handler, + &vfu_object_bar4_handler, + &vfu_object_bar5_handler, + &vfu_object_bar6_handler, +}; + +/** + * vfu_object_register_bars - Identify active BAR regions of pdev and setup + * callbacks to handle read/write accesses + */ +static void vfu_object_register_bars(vfu_ctx_t *vfu_ctx, PCIDevice *pdev) +{ + int flags = VFU_REGION_FLAG_RW; + int i; + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + if (!pdev->io_regions[i].size) { + continue; + } + + if ((i == VFU_PCI_DEV_ROM_REGION_IDX) || + pdev->io_regions[i].memory->readonly) { + flags &= ~VFU_REGION_FLAG_WRITE; + } + + vfu_setup_region(vfu_ctx, VFU_PCI_DEV_BAR0_REGION_IDX + i, + (size_t)pdev->io_regions[i].size, + vfu_object_bar_handlers[i], + flags, NULL, 0, -1, 0); + + trace_vfu_bar_register(i, pdev->io_regions[i].addr, + pdev->io_regions[i].size); + } +} + +static int vfu_object_map_irq(PCIDevice *pci_dev, int intx) +{ + int pci_bdf = PCI_BUILD_BDF(pci_bus_num(pci_get_bus(pci_dev)), + pci_dev->devfn); + + return pci_bdf; +} + +static void vfu_object_set_irq(void *opaque, int pirq, int level) +{ + PCIBus *pci_bus = opaque; + PCIDevice *pci_dev = NULL; + vfu_ctx_t *vfu_ctx = NULL; + int pci_bus_num, devfn; + + if (level) { + pci_bus_num = PCI_BUS_NUM(pirq); + devfn = PCI_BDF_TO_DEVFN(pirq); + + /* + * pci_find_device() performs at O(1) if the device is attached + * to the root PCI bus. Whereas, if the device is attached to a + * secondary PCI bus (such as when a root port is involved), + * finding the parent PCI bus could take O(n) + */ + pci_dev = pci_find_device(pci_bus, pci_bus_num, devfn); + + vfu_ctx = pci_dev->irq_opaque; + + g_assert(vfu_ctx); + + vfu_irq_trigger(vfu_ctx, 0); + } +} + +static MSIMessage vfu_object_msi_prepare_msg(PCIDevice *pci_dev, + unsigned int vector) +{ + MSIMessage msg; + + msg.address = 0; + msg.data = vector; + + return msg; +} + +static void vfu_object_msi_trigger(PCIDevice *pci_dev, MSIMessage msg) +{ + vfu_ctx_t *vfu_ctx = pci_dev->irq_opaque; + + vfu_irq_trigger(vfu_ctx, msg.data); +} + +static void vfu_object_setup_msi_cbs(VfuObject *o) +{ + o->default_msi_trigger = o->pci_dev->msi_trigger; + o->default_msi_prepare_message = o->pci_dev->msi_prepare_message; + o->default_msix_prepare_message = o->pci_dev->msix_prepare_message; + + o->pci_dev->msi_trigger = vfu_object_msi_trigger; + o->pci_dev->msi_prepare_message = vfu_object_msi_prepare_msg; + o->pci_dev->msix_prepare_message = vfu_object_msi_prepare_msg; +} + +static void vfu_object_restore_msi_cbs(VfuObject *o) +{ + o->pci_dev->msi_trigger = o->default_msi_trigger; + o->pci_dev->msi_prepare_message = o->default_msi_prepare_message; + o->pci_dev->msix_prepare_message = o->default_msix_prepare_message; +} + +static void vfu_msix_irq_state(vfu_ctx_t *vfu_ctx, uint32_t start, + uint32_t count, bool mask) +{ + VfuObject *o = vfu_get_private(vfu_ctx); + uint32_t vector; + + for (vector = start; vector < count; vector++) { + msix_set_mask(o->pci_dev, vector, mask); + } +} + +static void vfu_msi_irq_state(vfu_ctx_t *vfu_ctx, uint32_t start, + uint32_t count, bool mask) +{ + VfuObject *o = vfu_get_private(vfu_ctx); + Error *err = NULL; + uint32_t vector; + + for (vector = start; vector < count; vector++) { + msi_set_mask(o->pci_dev, vector, mask, &err); + if (err) { + VFU_OBJECT_ERROR(o, "vfu: %s: %s", o->device, + error_get_pretty(err)); + error_free(err); + err = NULL; + } + } +} + +static int vfu_object_setup_irqs(VfuObject *o, PCIDevice *pci_dev) +{ + vfu_ctx_t *vfu_ctx = o->vfu_ctx; + int ret; + + ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_INTX_IRQ, 1); + if (ret < 0) { + return ret; + } + + if (msix_nr_vectors_allocated(pci_dev)) { + ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_MSIX_IRQ, + msix_nr_vectors_allocated(pci_dev)); + vfu_setup_irq_state_callback(vfu_ctx, VFU_DEV_MSIX_IRQ, + &vfu_msix_irq_state); + } else if (msi_nr_vectors_allocated(pci_dev)) { + ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_MSI_IRQ, + msi_nr_vectors_allocated(pci_dev)); + vfu_setup_irq_state_callback(vfu_ctx, VFU_DEV_MSI_IRQ, + &vfu_msi_irq_state); + } + + if (ret < 0) { + return ret; + } + + vfu_object_setup_msi_cbs(o); + + pci_dev->irq_opaque = vfu_ctx; + + return 0; +} + +void vfu_object_set_bus_irq(PCIBus *pci_bus) +{ + int bus_num = pci_bus_num(pci_bus); + int max_bdf = PCI_BUILD_BDF(bus_num, PCI_DEVFN_MAX - 1); + + pci_bus_irqs(pci_bus, vfu_object_set_irq, pci_bus, max_bdf); + pci_bus_map_irqs(pci_bus, vfu_object_map_irq); +} + +static int vfu_object_device_reset(vfu_ctx_t *vfu_ctx, vfu_reset_type_t type) +{ + VfuObject *o = vfu_get_private(vfu_ctx); + + /* vfu_object_ctx_run() handles lost connection */ + if (type == VFU_RESET_LOST_CONN) { + return 0; + } + + device_cold_reset(DEVICE(o->pci_dev)); + + return 0; +} + +/* + * TYPE_VFU_OBJECT depends on the availability of the 'socket' and 'device' + * properties. It also depends on devices instantiated in QEMU. These + * dependencies are not available during the instance_init phase of this + * object's life-cycle. As such, the server is initialized after the + * machine is setup. machine_init_done_notifier notifies TYPE_VFU_OBJECT + * when the machine is setup, and the dependencies are available. + */ +static void vfu_object_machine_done(Notifier *notifier, void *data) +{ + VfuObject *o = container_of(notifier, VfuObject, machine_done); + Error *err = NULL; + + vfu_object_init_ctx(o, &err); + + if (err) { + error_propagate(&error_abort, err); + } +} + +/** + * vfu_object_init_ctx: Create and initialize libvfio-user context. Add + * an unplug blocker for the associated PCI device. Setup a FD handler + * to process incoming messages in the context's socket. + * + * The socket and device properties are mandatory, and this function + * will not create the context without them - the setters for these + * properties should call this function when the property is set. The + * machine should also be ready when this function is invoked - it is + * because QEMU objects are initialized before devices, and the + * associated PCI device wouldn't be available at the object + * initialization time. Until these conditions are satisfied, this + * function would return early without performing any task. + */ +static void vfu_object_init_ctx(VfuObject *o, Error **errp) +{ + DeviceState *dev = NULL; + vfu_pci_type_t pci_type = VFU_PCI_TYPE_CONVENTIONAL; + int ret; + + if (o->vfu_ctx || !o->socket || !o->device || + !phase_check(PHASE_MACHINE_READY)) { + return; + } + + if (o->err) { + error_propagate(errp, o->err); + o->err = NULL; + return; + } + + o->vfu_ctx = vfu_create_ctx(VFU_TRANS_SOCK, o->socket->u.q_unix.path, + LIBVFIO_USER_FLAG_ATTACH_NB, + o, VFU_DEV_TYPE_PCI); + if (o->vfu_ctx == NULL) { + error_setg(errp, "vfu: Failed to create context - %s", strerror(errno)); + return; + } + + dev = qdev_find_recursive(sysbus_get_default(), o->device); + if (dev == NULL) { + error_setg(errp, "vfu: Device %s not found", o->device); + goto fail; + } + + if (!object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + error_setg(errp, "vfu: %s not a PCI device", o->device); + goto fail; + } + + o->pci_dev = PCI_DEVICE(dev); + + object_ref(OBJECT(o->pci_dev)); + + if (pci_is_express(o->pci_dev)) { + pci_type = VFU_PCI_TYPE_EXPRESS; + } + + ret = vfu_pci_init(o->vfu_ctx, pci_type, PCI_HEADER_TYPE_NORMAL, 0); + if (ret < 0) { + error_setg(errp, + "vfu: Failed to attach PCI device %s to context - %s", + o->device, strerror(errno)); + goto fail; + } + + error_setg(&o->unplug_blocker, + "vfu: %s for %s must be deleted before unplugging", + TYPE_VFU_OBJECT, o->device); + qdev_add_unplug_blocker(DEVICE(o->pci_dev), o->unplug_blocker); + + ret = vfu_setup_region(o->vfu_ctx, VFU_PCI_DEV_CFG_REGION_IDX, + pci_config_size(o->pci_dev), &vfu_object_cfg_access, + VFU_REGION_FLAG_RW | VFU_REGION_FLAG_ALWAYS_CB, + NULL, 0, -1, 0); + if (ret < 0) { + error_setg(errp, + "vfu: Failed to setup config space handlers for %s- %s", + o->device, strerror(errno)); + goto fail; + } + + ret = vfu_setup_device_dma(o->vfu_ctx, &dma_register, &dma_unregister); + if (ret < 0) { + error_setg(errp, "vfu: Failed to setup DMA handlers for %s", + o->device); + goto fail; + } + + vfu_object_register_bars(o->vfu_ctx, o->pci_dev); + + ret = vfu_object_setup_irqs(o, o->pci_dev); + if (ret < 0) { + error_setg(errp, "vfu: Failed to setup interrupts for %s", + o->device); + goto fail; + } + + ret = vfu_setup_device_reset_cb(o->vfu_ctx, &vfu_object_device_reset); + if (ret < 0) { + error_setg(errp, "vfu: Failed to setup reset callback"); + goto fail; + } + + ret = vfu_realize_ctx(o->vfu_ctx); + if (ret < 0) { + error_setg(errp, "vfu: Failed to realize device %s- %s", + o->device, strerror(errno)); + goto fail; + } + + o->vfu_poll_fd = vfu_get_poll_fd(o->vfu_ctx); + if (o->vfu_poll_fd < 0) { + error_setg(errp, "vfu: Failed to get poll fd %s", o->device); + goto fail; + } + + qemu_set_fd_handler(o->vfu_poll_fd, vfu_object_attach_ctx, NULL, o); + + return; + +fail: + vfu_destroy_ctx(o->vfu_ctx); + if (o->unplug_blocker && o->pci_dev) { + qdev_del_unplug_blocker(DEVICE(o->pci_dev), o->unplug_blocker); + error_free(o->unplug_blocker); + o->unplug_blocker = NULL; + } + if (o->pci_dev) { + vfu_object_restore_msi_cbs(o); + o->pci_dev->irq_opaque = NULL; + object_unref(OBJECT(o->pci_dev)); + o->pci_dev = NULL; + } + o->vfu_ctx = NULL; +} + +static void vfu_object_init(Object *obj) +{ + VfuObjectClass *k = VFU_OBJECT_GET_CLASS(obj); + VfuObject *o = VFU_OBJECT(obj); + + k->nr_devs++; + + if (!object_dynamic_cast(OBJECT(current_machine), TYPE_REMOTE_MACHINE)) { + error_setg(&o->err, "vfu: %s only compatible with %s machine", + TYPE_VFU_OBJECT, TYPE_REMOTE_MACHINE); + return; + } + + if (!phase_check(PHASE_MACHINE_READY)) { + o->machine_done.notify = vfu_object_machine_done; + qemu_add_machine_init_done_notifier(&o->machine_done); + } + + o->vfu_poll_fd = -1; +} + +static void vfu_object_finalize(Object *obj) +{ + VfuObjectClass *k = VFU_OBJECT_GET_CLASS(obj); + VfuObject *o = VFU_OBJECT(obj); + + k->nr_devs--; + + qapi_free_SocketAddress(o->socket); + + o->socket = NULL; + + if (o->vfu_poll_fd != -1) { + qemu_set_fd_handler(o->vfu_poll_fd, NULL, NULL, NULL); + o->vfu_poll_fd = -1; + } + + if (o->vfu_ctx) { + vfu_destroy_ctx(o->vfu_ctx); + o->vfu_ctx = NULL; + } + + g_free(o->device); + + o->device = NULL; + + if (o->unplug_blocker && o->pci_dev) { + qdev_del_unplug_blocker(DEVICE(o->pci_dev), o->unplug_blocker); + error_free(o->unplug_blocker); + o->unplug_blocker = NULL; + } + + if (o->pci_dev) { + vfu_object_restore_msi_cbs(o); + o->pci_dev->irq_opaque = NULL; + object_unref(OBJECT(o->pci_dev)); + o->pci_dev = NULL; + } + + if (!k->nr_devs && vfu_object_auto_shutdown()) { + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + } + + if (o->machine_done.notify) { + qemu_remove_machine_init_done_notifier(&o->machine_done); + o->machine_done.notify = NULL; + } +} + +static void vfu_object_class_init(ObjectClass *klass, void *data) +{ + VfuObjectClass *k = VFU_OBJECT_CLASS(klass); + + k->nr_devs = 0; + + object_class_property_add(klass, "socket", "SocketAddress", NULL, + vfu_object_set_socket, NULL, NULL); + object_class_property_set_description(klass, "socket", + "SocketAddress " + "(ex: type=unix,path=/tmp/sock). " + "Only UNIX is presently supported"); + object_class_property_add_str(klass, "device", NULL, + vfu_object_set_device); + object_class_property_set_description(klass, "device", + "device ID - only PCI devices " + "are presently supported"); +} + +static const TypeInfo vfu_object_info = { + .name = TYPE_VFU_OBJECT, + .parent = TYPE_OBJECT, + .instance_size = sizeof(VfuObject), + .instance_init = vfu_object_init, + .instance_finalize = vfu_object_finalize, + .class_size = sizeof(VfuObjectClass), + .class_init = vfu_object_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void vfu_register_types(void) +{ + type_register_static(&vfu_object_info); +} + +type_init(vfu_register_types); diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 79ff61c464..5d644eb7b1 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -4,6 +4,8 @@ config RISCV_NUMA config IBEX bool +# RISC-V machines in alphabetical order + config MICROCHIP_PFSOC bool select CADENCE_SDHCI @@ -11,7 +13,6 @@ config MICROCHIP_PFSOC select MCHP_PFSOC_IOSCB select MCHP_PFSOC_MMUART select MCHP_PFSOC_SYSREG - select MSI_NONBROKEN select RISCV_ACLINT select SIFIVE_PDMA select SIFIVE_PLIC @@ -20,14 +21,8 @@ config MICROCHIP_PFSOC config OPENTITAN bool select IBEX - select UNIMP - -config SHAKTI_C - bool - select UNIMP - select SHAKTI_UART - select RISCV_ACLINT select SIFIVE_PLIC + select UNIMP config RISCV_VIRT bool @@ -37,7 +32,6 @@ config RISCV_VIRT imply TPM_TIS_SYSBUS select RISCV_NUMA select GOLDFISH_RTC - select MSI_NONBROKEN select PCI select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 @@ -47,24 +41,33 @@ config RISCV_VIRT select RISCV_IMSIC select SIFIVE_PLIC select SIFIVE_TEST + select SMBIOS select VIRTIO_MMIO select FW_CFG_DMA select PLATFORM_BUS + select ACPI + select ACPI_PCI + +config SHAKTI_C + bool + select RISCV_ACLINT + select SHAKTI_UART + select SIFIVE_PLIC + select UNIMP config SIFIVE_E bool - select MSI_NONBROKEN select RISCV_ACLINT select SIFIVE_GPIO select SIFIVE_PLIC select SIFIVE_UART select SIFIVE_E_PRCI + select SIFIVE_E_AON select UNIMP config SIFIVE_U bool select CADENCE - select MSI_NONBROKEN select RISCV_ACLINT select SIFIVE_GPIO select SIFIVE_PDMA @@ -82,6 +85,5 @@ config SPIKE bool select RISCV_NUMA select HTIF - select MSI_NONBROKEN select RISCV_ACLINT select SIFIVE_PLIC diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 57a41df8e9..09878e722c 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -30,12 +30,14 @@ #include "sysemu/device_tree.h" #include "sysemu/qtest.h" #include "sysemu/kvm.h" +#include "sysemu/reset.h" #include bool riscv_is_32bit(RISCVHartArrayState *harts) { - return harts->harts[0].env.misa_mxl_max == MXL_RV32; + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(&harts->harts[0]); + return mcc->misa_mxl_max == MXL_RV32; } /* @@ -74,25 +76,67 @@ target_ulong riscv_calc_kernel_start_addr(RISCVHartArrayState *harts, } } -target_ulong riscv_find_and_load_firmware(MachineState *machine, - const char *default_machine_firmware, - hwaddr firmware_load_addr, - symbol_fn_t sym_cb) +const char *riscv_default_firmware_name(RISCVHartArrayState *harts) { - char *firmware_filename = NULL; - target_ulong firmware_end_addr = firmware_load_addr; + if (riscv_is_32bit(harts)) { + return RISCV32_BIOS_BIN; + } - if ((!machine->firmware) || (!strcmp(machine->firmware, "default"))) { + return RISCV64_BIOS_BIN; +} + +static char *riscv_find_bios(const char *bios_filename) +{ + char *filename; + + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_filename); + if (filename == NULL) { + if (!qtest_enabled()) { + /* + * We only ship OpenSBI binary bios images in the QEMU source. + * For machines that use images other than the default bios, + * running QEMU test will complain hence let's suppress the error + * report for QEMU testing. + */ + error_report("Unable to find the RISC-V BIOS \"%s\"", + bios_filename); + exit(1); + } + } + + return filename; +} + +char *riscv_find_firmware(const char *firmware_filename, + const char *default_machine_firmware) +{ + char *filename = NULL; + + if ((!firmware_filename) || (!strcmp(firmware_filename, "default"))) { /* * The user didn't specify -bios, or has specified "-bios default". * That means we are going to load the OpenSBI binary included in * the QEMU source. */ - firmware_filename = riscv_find_firmware(default_machine_firmware); - } else if (strcmp(machine->firmware, "none")) { - firmware_filename = riscv_find_firmware(machine->firmware); + filename = riscv_find_bios(default_machine_firmware); + } else if (strcmp(firmware_filename, "none")) { + filename = riscv_find_bios(firmware_filename); } + return filename; +} + +target_ulong riscv_find_and_load_firmware(MachineState *machine, + const char *default_machine_firmware, + hwaddr firmware_load_addr, + symbol_fn_t sym_cb) +{ + char *firmware_filename; + target_ulong firmware_end_addr = firmware_load_addr; + + firmware_filename = riscv_find_firmware(machine->firmware, + default_machine_firmware); + if (firmware_filename) { /* If not "none" load the firmware */ firmware_end_addr = riscv_load_firmware(firmware_filename, @@ -103,33 +147,14 @@ target_ulong riscv_find_and_load_firmware(MachineState *machine, return firmware_end_addr; } -char *riscv_find_firmware(const char *firmware_filename) -{ - char *filename; - - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, firmware_filename); - if (filename == NULL) { - if (!qtest_enabled()) { - /* - * We only ship plain binary bios images in the QEMU source. - * With Spike machine that uses ELF images as the default bios, - * running QEMU test will complain hence let's suppress the error - * report for QEMU testing. - */ - error_report("Unable to load the RISC-V firmware \"%s\"", - firmware_filename); - exit(1); - } - } - - return filename; -} - target_ulong riscv_load_firmware(const char *firmware_filename, hwaddr firmware_load_addr, symbol_fn_t sym_cb) { - uint64_t firmware_entry, firmware_size, firmware_end; + uint64_t firmware_entry, firmware_end; + ssize_t firmware_size; + + g_assert(firmware_filename != NULL); if (load_elf_ram_sym(firmware_filename, NULL, NULL, NULL, &firmware_entry, NULL, &firmware_end, NULL, @@ -149,11 +174,57 @@ target_ulong riscv_load_firmware(const char *firmware_filename, exit(1); } -target_ulong riscv_load_kernel(const char *kernel_filename, +static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) +{ + const char *filename = machine->initrd_filename; + uint64_t mem_size = machine->ram_size; + void *fdt = machine->fdt; + hwaddr start, end; + ssize_t size; + + g_assert(filename != NULL); + + /* + * We want to put the initrd far enough into RAM that when the + * kernel is uncompressed it will not clobber the initrd. However + * on boards without much RAM we must ensure that we still leave + * enough room for a decent sized initrd, and on boards with large + * amounts of RAM, we put the initrd at 512MB to allow large kernels + * to boot. + * So for boards with less than 1GB of RAM we put the initrd + * halfway into RAM, and for boards with 1GB of RAM or more we put + * the initrd at 512MB. + */ + start = kernel_entry + MIN(mem_size / 2, 512 * MiB); + + size = load_ramdisk(filename, start, mem_size - start); + if (size == -1) { + size = load_image_targphys(filename, start, mem_size - start); + if (size == -1) { + error_report("could not load ramdisk '%s'", filename); + exit(1); + } + } + + /* Some RISC-V machines (e.g. opentitan) don't have a fdt. */ + if (fdt) { + end = start + size; + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", start); + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", end); + } +} + +target_ulong riscv_load_kernel(MachineState *machine, + RISCVHartArrayState *harts, target_ulong kernel_start_addr, + bool load_initrd, symbol_fn_t sym_cb) { + const char *kernel_filename = machine->kernel_filename; uint64_t kernel_load_base, kernel_entry; + void *fdt = machine->fdt; + + g_assert(kernel_filename != NULL); /* * NB: Use low address not ELF entry point to ensure that the fw_dynamic @@ -165,83 +236,110 @@ target_ulong riscv_load_kernel(const char *kernel_filename, if (load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, NULL, &kernel_load_base, NULL, NULL, 0, EM_RISCV, 1, 0, NULL, true, sym_cb) > 0) { - return kernel_load_base; + kernel_entry = kernel_load_base; + goto out; } if (load_uimage_as(kernel_filename, &kernel_entry, NULL, NULL, NULL, NULL, NULL) > 0) { - return kernel_entry; + goto out; } if (load_image_targphys_as(kernel_filename, kernel_start_addr, current_machine->ram_size, NULL) > 0) { - return kernel_start_addr; + kernel_entry = kernel_start_addr; + goto out; } error_report("could not load kernel '%s'", kernel_filename); exit(1); -} - -hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size, - uint64_t kernel_entry, hwaddr *start) -{ - int size; +out: /* - * We want to put the initrd far enough into RAM that when the - * kernel is uncompressed it will not clobber the initrd. However - * on boards without much RAM we must ensure that we still leave - * enough room for a decent sized initrd, and on boards with large - * amounts of RAM we must avoid the initrd being so far up in RAM - * that it is outside lowmem and inaccessible to the kernel. - * So for boards with less than 256MB of RAM we put the initrd - * halfway into RAM, and for boards with 256MB of RAM or more we put - * the initrd at 128MB. + * For 32 bit CPUs 'kernel_entry' can be sign-extended by + * load_elf_ram_sym(). */ - *start = kernel_entry + MIN(mem_size / 2, 128 * MiB); - - size = load_ramdisk(filename, *start, mem_size - *start); - if (size == -1) { - size = load_image_targphys(filename, *start, mem_size - *start); - if (size == -1) { - error_report("could not load ramdisk '%s'", filename); - exit(1); - } + if (riscv_is_32bit(harts)) { + kernel_entry = extract64(kernel_entry, 0, 32); } - return *start + size; + if (load_initrd && machine->initrd_filename) { + riscv_load_initrd(machine, kernel_entry); + } + + if (fdt && machine->kernel_cmdline && *machine->kernel_cmdline) { + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", + machine->kernel_cmdline); + } + + return kernel_entry; } -uint64_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt) +/* + * This function makes an assumption that the DRAM interval + * 'dram_base' + 'dram_size' is contiguous. + * + * Considering that 'dram_end' is the lowest value between + * the end of the DRAM block and MachineState->ram_size, the + * FDT location will vary according to 'dram_base': + * + * - if 'dram_base' is less that 3072 MiB, the FDT will be + * put at the lowest value between 3072 MiB and 'dram_end'; + * + * - if 'dram_base' is higher than 3072 MiB, the FDT will be + * put at 'dram_end'. + * + * The FDT is fdt_packed() during the calculation. + */ +uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, + MachineState *ms) { - uint64_t temp, fdt_addr; - hwaddr dram_end = dram_base + mem_size; - int ret, fdtsize = fdt_totalsize(fdt); + int ret = fdt_pack(ms->fdt); + hwaddr dram_end, temp; + int fdtsize; + /* Should only fail if we've built a corrupted tree */ + g_assert(ret == 0); + + fdtsize = fdt_totalsize(ms->fdt); if (fdtsize <= 0) { error_report("invalid device-tree"); exit(1); } + /* + * A dram_size == 0, usually from a MemMapEntry[].size element, + * means that the DRAM block goes all the way to ms->ram_size. + */ + dram_end = dram_base; + dram_end += dram_size ? MIN(ms->ram_size, dram_size) : ms->ram_size; + /* * We should put fdt as far as possible to avoid kernel/initrd overwriting * its content. But it should be addressable by 32 bit system as well. - * Thus, put it at an 16MB aligned address that less than fdt size from the + * Thus, put it at an 2MB aligned address that less than fdt size from the * end of dram or 3GB whichever is lesser. */ temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end; - fdt_addr = QEMU_ALIGN_DOWN(temp - fdtsize, 16 * MiB); - ret = fdt_pack(fdt); - /* Should only fail if we've built a corrupted tree */ - g_assert(ret == 0); + return QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB); +} + +/* + * 'fdt_addr' is received as hwaddr because boards might put + * the FDT beyond 32-bit addressing boundary. + */ +void riscv_load_fdt(hwaddr fdt_addr, void *fdt) +{ + uint32_t fdtsize = fdt_totalsize(fdt); + /* copy in the device tree */ qemu_fdt_dumpdtb(fdt, fdtsize); rom_add_blob_fixed_as("fdt", fdt, fdtsize, fdt_addr, &address_space_memory); - - return fdt_addr; + qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, + rom_ptr_for_as(&address_space_memory, fdt_addr, fdtsize)); } void riscv_rom_copy_firmware_info(MachineState *machine, hwaddr rom_base, @@ -285,7 +383,7 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts hwaddr start_addr, hwaddr rom_base, hwaddr rom_size, uint64_t kernel_entry, - uint64_t fdt_load_addr, void *fdt) + uint64_t fdt_load_addr) { int i; uint32_t start_addr_hi32 = 0x00000000; @@ -317,6 +415,15 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts reset_vec[4] = 0x0182b283; /* ld t0, 24(t0) */ } + if (!harts->harts[0].cfg.ext_zicsr) { + /* + * The Zicsr extension has been disabled, so let's ensure we don't + * run the CSR instruction. Let's fill the address with a non + * compressed nop. + */ + reset_vec[2] = 0x00000013; /* addi x0, x0, 0 */ + } + /* copy in the reset vector in little_endian byte order */ for (i = 0; i < ARRAY_SIZE(reset_vec); i++) { reset_vec[i] = cpu_to_le32(reset_vec[i]); @@ -325,8 +432,6 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts rom_base, &address_space_memory); riscv_rom_copy_firmware_info(machine, rom_base, rom_size, sizeof(reset_vec), kernel_entry); - - return; } void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr) @@ -339,3 +444,32 @@ void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr) riscv_cpu->env.fdt_addr = fdt_addr; } } + +void riscv_setup_firmware_boot(MachineState *machine) +{ + if (machine->kernel_filename) { + FWCfgState *fw_cfg; + fw_cfg = fw_cfg_find(); + + assert(fw_cfg); + /* + * Expose the kernel, the command line, and the initrd in fw_cfg. + * We don't process them here at all, it's all left to the + * firmware. + */ + load_image_to_fw_cfg(fw_cfg, + FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA, + machine->kernel_filename, + true); + load_image_to_fw_cfg(fw_cfg, + FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, + machine->initrd_filename, false); + + if (machine->kernel_cmdline) { + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(machine->kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, + machine->kernel_cmdline); + } + } +} diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index ab6cae57ea..2f7ee81be3 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -9,5 +9,6 @@ riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c')) riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u.c')) riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c')) riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.c')) +riscv_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) hw_arch += {'riscv': riscv_ss} diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index 10a5d0e501..7725dfbde5 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -86,46 +86,61 @@ * describes the complete IOSCB modules memory maps */ static const MemMapEntry microchip_pfsoc_memmap[] = { - [MICROCHIP_PFSOC_RSVD0] = { 0x0, 0x100 }, - [MICROCHIP_PFSOC_DEBUG] = { 0x100, 0xf00 }, - [MICROCHIP_PFSOC_E51_DTIM] = { 0x1000000, 0x2000 }, - [MICROCHIP_PFSOC_BUSERR_UNIT0] = { 0x1700000, 0x1000 }, - [MICROCHIP_PFSOC_BUSERR_UNIT1] = { 0x1701000, 0x1000 }, - [MICROCHIP_PFSOC_BUSERR_UNIT2] = { 0x1702000, 0x1000 }, - [MICROCHIP_PFSOC_BUSERR_UNIT3] = { 0x1703000, 0x1000 }, - [MICROCHIP_PFSOC_BUSERR_UNIT4] = { 0x1704000, 0x1000 }, - [MICROCHIP_PFSOC_CLINT] = { 0x2000000, 0x10000 }, - [MICROCHIP_PFSOC_L2CC] = { 0x2010000, 0x1000 }, - [MICROCHIP_PFSOC_DMA] = { 0x3000000, 0x100000 }, - [MICROCHIP_PFSOC_L2LIM] = { 0x8000000, 0x2000000 }, - [MICROCHIP_PFSOC_PLIC] = { 0xc000000, 0x4000000 }, - [MICROCHIP_PFSOC_MMUART0] = { 0x20000000, 0x1000 }, - [MICROCHIP_PFSOC_SYSREG] = { 0x20002000, 0x2000 }, - [MICROCHIP_PFSOC_MPUCFG] = { 0x20005000, 0x1000 }, - [MICROCHIP_PFSOC_DDR_SGMII_PHY] = { 0x20007000, 0x1000 }, - [MICROCHIP_PFSOC_EMMC_SD] = { 0x20008000, 0x1000 }, - [MICROCHIP_PFSOC_DDR_CFG] = { 0x20080000, 0x40000 }, - [MICROCHIP_PFSOC_MMUART1] = { 0x20100000, 0x1000 }, - [MICROCHIP_PFSOC_MMUART2] = { 0x20102000, 0x1000 }, - [MICROCHIP_PFSOC_MMUART3] = { 0x20104000, 0x1000 }, - [MICROCHIP_PFSOC_MMUART4] = { 0x20106000, 0x1000 }, - [MICROCHIP_PFSOC_SPI0] = { 0x20108000, 0x1000 }, - [MICROCHIP_PFSOC_SPI1] = { 0x20109000, 0x1000 }, - [MICROCHIP_PFSOC_I2C1] = { 0x2010b000, 0x1000 }, - [MICROCHIP_PFSOC_GEM0] = { 0x20110000, 0x2000 }, - [MICROCHIP_PFSOC_GEM1] = { 0x20112000, 0x2000 }, - [MICROCHIP_PFSOC_GPIO0] = { 0x20120000, 0x1000 }, - [MICROCHIP_PFSOC_GPIO1] = { 0x20121000, 0x1000 }, - [MICROCHIP_PFSOC_GPIO2] = { 0x20122000, 0x1000 }, - [MICROCHIP_PFSOC_ENVM_CFG] = { 0x20200000, 0x1000 }, - [MICROCHIP_PFSOC_ENVM_DATA] = { 0x20220000, 0x20000 }, - [MICROCHIP_PFSOC_QSPI_XIP] = { 0x21000000, 0x1000000 }, - [MICROCHIP_PFSOC_IOSCB] = { 0x30000000, 0x10000000 }, - [MICROCHIP_PFSOC_EMMC_SD_MUX] = { 0x4f000000, 0x4 }, - [MICROCHIP_PFSOC_DRAM_LO] = { 0x80000000, 0x40000000 }, - [MICROCHIP_PFSOC_DRAM_LO_ALIAS] = { 0xc0000000, 0x40000000 }, - [MICROCHIP_PFSOC_DRAM_HI] = { 0x1000000000, 0x0 }, - [MICROCHIP_PFSOC_DRAM_HI_ALIAS] = { 0x1400000000, 0x0 }, + [MICROCHIP_PFSOC_RSVD0] = { 0x0, 0x100 }, + [MICROCHIP_PFSOC_DEBUG] = { 0x100, 0xf00 }, + [MICROCHIP_PFSOC_E51_DTIM] = { 0x1000000, 0x2000 }, + [MICROCHIP_PFSOC_BUSERR_UNIT0] = { 0x1700000, 0x1000 }, + [MICROCHIP_PFSOC_BUSERR_UNIT1] = { 0x1701000, 0x1000 }, + [MICROCHIP_PFSOC_BUSERR_UNIT2] = { 0x1702000, 0x1000 }, + [MICROCHIP_PFSOC_BUSERR_UNIT3] = { 0x1703000, 0x1000 }, + [MICROCHIP_PFSOC_BUSERR_UNIT4] = { 0x1704000, 0x1000 }, + [MICROCHIP_PFSOC_CLINT] = { 0x2000000, 0x10000 }, + [MICROCHIP_PFSOC_L2CC] = { 0x2010000, 0x1000 }, + [MICROCHIP_PFSOC_DMA] = { 0x3000000, 0x100000 }, + [MICROCHIP_PFSOC_L2LIM] = { 0x8000000, 0x2000000 }, + [MICROCHIP_PFSOC_PLIC] = { 0xc000000, 0x4000000 }, + [MICROCHIP_PFSOC_MMUART0] = { 0x20000000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG0] = { 0x20001000, 0x1000 }, + [MICROCHIP_PFSOC_SYSREG] = { 0x20002000, 0x2000 }, + [MICROCHIP_PFSOC_AXISW] = { 0x20004000, 0x1000 }, + [MICROCHIP_PFSOC_MPUCFG] = { 0x20005000, 0x1000 }, + [MICROCHIP_PFSOC_FMETER] = { 0x20006000, 0x1000 }, + [MICROCHIP_PFSOC_DDR_SGMII_PHY] = { 0x20007000, 0x1000 }, + [MICROCHIP_PFSOC_EMMC_SD] = { 0x20008000, 0x1000 }, + [MICROCHIP_PFSOC_DDR_CFG] = { 0x20080000, 0x40000 }, + [MICROCHIP_PFSOC_MMUART1] = { 0x20100000, 0x1000 }, + [MICROCHIP_PFSOC_MMUART2] = { 0x20102000, 0x1000 }, + [MICROCHIP_PFSOC_MMUART3] = { 0x20104000, 0x1000 }, + [MICROCHIP_PFSOC_MMUART4] = { 0x20106000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG1] = { 0x20101000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG2] = { 0x20103000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG3] = { 0x20105000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG4] = { 0x20106000, 0x1000 }, + [MICROCHIP_PFSOC_SPI0] = { 0x20108000, 0x1000 }, + [MICROCHIP_PFSOC_SPI1] = { 0x20109000, 0x1000 }, + [MICROCHIP_PFSOC_I2C0] = { 0x2010a000, 0x1000 }, + [MICROCHIP_PFSOC_I2C1] = { 0x2010b000, 0x1000 }, + [MICROCHIP_PFSOC_CAN0] = { 0x2010c000, 0x1000 }, + [MICROCHIP_PFSOC_CAN1] = { 0x2010d000, 0x1000 }, + [MICROCHIP_PFSOC_GEM0] = { 0x20110000, 0x2000 }, + [MICROCHIP_PFSOC_GEM1] = { 0x20112000, 0x2000 }, + [MICROCHIP_PFSOC_GPIO0] = { 0x20120000, 0x1000 }, + [MICROCHIP_PFSOC_GPIO1] = { 0x20121000, 0x1000 }, + [MICROCHIP_PFSOC_GPIO2] = { 0x20122000, 0x1000 }, + [MICROCHIP_PFSOC_RTC] = { 0x20124000, 0x1000 }, + [MICROCHIP_PFSOC_ENVM_CFG] = { 0x20200000, 0x1000 }, + [MICROCHIP_PFSOC_ENVM_DATA] = { 0x20220000, 0x20000 }, + [MICROCHIP_PFSOC_USB] = { 0x20201000, 0x1000 }, + [MICROCHIP_PFSOC_QSPI_XIP] = { 0x21000000, 0x1000000 }, + [MICROCHIP_PFSOC_IOSCB] = { 0x30000000, 0x10000000 }, + [MICROCHIP_PFSOC_FABRIC_FIC0] = { 0x2000000000, 0x1000000000 }, + [MICROCHIP_PFSOC_FABRIC_FIC1] = { 0x3000000000, 0x1000000000 }, + [MICROCHIP_PFSOC_FABRIC_FIC3] = { 0x40000000, 0x20000000 }, + [MICROCHIP_PFSOC_DRAM_LO] = { 0x80000000, 0x40000000 }, + [MICROCHIP_PFSOC_DRAM_LO_ALIAS] = { 0xc0000000, 0x40000000 }, + [MICROCHIP_PFSOC_DRAM_HI] = { 0x1000000000, 0x0 }, + [MICROCHIP_PFSOC_DRAM_HI_ALIAS] = { 0x1400000000, 0x0 }, + }; static void microchip_pfsoc_soc_instance_init(Object *obj) @@ -187,7 +202,6 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) MemoryRegion *envm_data = g_new(MemoryRegion, 1); MemoryRegion *qspi_xip_mem = g_new(MemoryRegion, 1); char *plic_hart_config; - NICInfo *nd; int i; sysbus_realize(SYS_BUS_DEVICE(&s->e_cpus), &error_abort); @@ -291,12 +305,25 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(&s->sysreg), errp); sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysreg), 0, memmap[MICROCHIP_PFSOC_SYSREG].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sysreg), 0, + qdev_get_gpio_in(DEVICE(s->plic), + MICROCHIP_PFSOC_MAILBOX_IRQ)); + + /* AXISW */ + create_unimplemented_device("microchip.pfsoc.axisw", + memmap[MICROCHIP_PFSOC_AXISW].base, + memmap[MICROCHIP_PFSOC_AXISW].size); /* MPUCFG */ create_unimplemented_device("microchip.pfsoc.mpucfg", memmap[MICROCHIP_PFSOC_MPUCFG].base, memmap[MICROCHIP_PFSOC_MPUCFG].size); + /* FMETER */ + create_unimplemented_device("microchip.pfsoc.fmeter", + memmap[MICROCHIP_PFSOC_FMETER].base, + memmap[MICROCHIP_PFSOC_FMETER].size); + /* DDR SGMII PHY */ sysbus_realize(SYS_BUS_DEVICE(&s->ddr_sgmii_phy), errp); sysbus_mmio_map(SYS_BUS_DEVICE(&s->ddr_sgmii_phy), 0, @@ -336,6 +363,23 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(s->plic), MICROCHIP_PFSOC_MMUART4_IRQ), serial_hd(4)); + /* Watchdogs */ + create_unimplemented_device("microchip.pfsoc.watchdog0", + memmap[MICROCHIP_PFSOC_WDOG0].base, + memmap[MICROCHIP_PFSOC_WDOG0].size); + create_unimplemented_device("microchip.pfsoc.watchdog1", + memmap[MICROCHIP_PFSOC_WDOG1].base, + memmap[MICROCHIP_PFSOC_WDOG1].size); + create_unimplemented_device("microchip.pfsoc.watchdog2", + memmap[MICROCHIP_PFSOC_WDOG2].base, + memmap[MICROCHIP_PFSOC_WDOG2].size); + create_unimplemented_device("microchip.pfsoc.watchdog3", + memmap[MICROCHIP_PFSOC_WDOG3].base, + memmap[MICROCHIP_PFSOC_WDOG3].size); + create_unimplemented_device("microchip.pfsoc.watchdog4", + memmap[MICROCHIP_PFSOC_WDOG4].base, + memmap[MICROCHIP_PFSOC_WDOG4].size); + /* SPI */ create_unimplemented_device("microchip.pfsoc.spi0", memmap[MICROCHIP_PFSOC_SPI0].base, @@ -344,23 +388,30 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) memmap[MICROCHIP_PFSOC_SPI1].base, memmap[MICROCHIP_PFSOC_SPI1].size); - /* I2C1 */ + /* I2C */ + create_unimplemented_device("microchip.pfsoc.i2c0", + memmap[MICROCHIP_PFSOC_I2C0].base, + memmap[MICROCHIP_PFSOC_I2C0].size); create_unimplemented_device("microchip.pfsoc.i2c1", memmap[MICROCHIP_PFSOC_I2C1].base, memmap[MICROCHIP_PFSOC_I2C1].size); - /* GEMs */ + /* CAN */ + create_unimplemented_device("microchip.pfsoc.can0", + memmap[MICROCHIP_PFSOC_CAN0].base, + memmap[MICROCHIP_PFSOC_CAN0].size); + create_unimplemented_device("microchip.pfsoc.can1", + memmap[MICROCHIP_PFSOC_CAN1].base, + memmap[MICROCHIP_PFSOC_CAN1].size); - nd = &nd_table[0]; - if (nd->used) { - qemu_check_nic_model(nd, TYPE_CADENCE_GEM); - qdev_set_nic_properties(DEVICE(&s->gem0), nd); - } - nd = &nd_table[1]; - if (nd->used) { - qemu_check_nic_model(nd, TYPE_CADENCE_GEM); - qdev_set_nic_properties(DEVICE(&s->gem1), nd); - } + /* USB */ + create_unimplemented_device("microchip.pfsoc.usb", + memmap[MICROCHIP_PFSOC_USB].base, + memmap[MICROCHIP_PFSOC_USB].size); + + /* GEMs */ + qemu_configure_nic_device(DEVICE(&s->gem0), true, NULL); + qemu_configure_nic_device(DEVICE(&s->gem1), true, NULL); object_property_set_int(OBJECT(&s->gem0), "revision", GEM_REVISION, errp); object_property_set_int(OBJECT(&s->gem0), "phy-addr", 8, errp); @@ -401,11 +452,22 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(&s->ioscb), errp); sysbus_mmio_map(SYS_BUS_DEVICE(&s->ioscb), 0, memmap[MICROCHIP_PFSOC_IOSCB].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ioscb), 0, + qdev_get_gpio_in(DEVICE(s->plic), + MICROCHIP_PFSOC_MAILBOX_IRQ)); - /* eMMC/SD mux */ - create_unimplemented_device("microchip.pfsoc.emmc_sd_mux", - memmap[MICROCHIP_PFSOC_EMMC_SD_MUX].base, - memmap[MICROCHIP_PFSOC_EMMC_SD_MUX].size); + /* FPGA Fabric */ + create_unimplemented_device("microchip.pfsoc.fabricfic3", + memmap[MICROCHIP_PFSOC_FABRIC_FIC3].base, + memmap[MICROCHIP_PFSOC_FABRIC_FIC3].size); + /* FPGA Fabric */ + create_unimplemented_device("microchip.pfsoc.fabricfic0", + memmap[MICROCHIP_PFSOC_FABRIC_FIC0].base, + memmap[MICROCHIP_PFSOC_FABRIC_FIC0].size); + /* FPGA Fabric */ + create_unimplemented_device("microchip.pfsoc.fabricfic1", + memmap[MICROCHIP_PFSOC_FABRIC_FIC1].base, + memmap[MICROCHIP_PFSOC_FABRIC_FIC1].size); /* QSPI Flash */ memory_region_init_rom(qspi_xip_mem, OBJECT(dev), @@ -557,33 +619,20 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc.u_cpus, firmware_end_addr); - kernel_entry = riscv_load_kernel(machine->kernel_filename, - kernel_start_addr, NULL); - - if (machine->initrd_filename) { - hwaddr start; - hwaddr end = riscv_load_initrd(machine->initrd_filename, - machine->ram_size, kernel_entry, - &start); - qemu_fdt_setprop_cell(machine->fdt, "/chosen", - "linux,initrd-start", start); - qemu_fdt_setprop_cell(machine->fdt, "/chosen", - "linux,initrd-end", end); - } - - if (machine->kernel_cmdline && *machine->kernel_cmdline) { - qemu_fdt_setprop_string(machine->fdt, "/chosen", - "bootargs", machine->kernel_cmdline); - } + kernel_entry = riscv_load_kernel(machine, &s->soc.u_cpus, + kernel_start_addr, true, NULL); /* Compute the fdt load address in dram */ - fdt_load_addr = riscv_load_fdt(memmap[MICROCHIP_PFSOC_DRAM_LO].base, - machine->ram_size, machine->fdt); + fdt_load_addr = riscv_compute_fdt_addr(memmap[MICROCHIP_PFSOC_DRAM_LO].base, + memmap[MICROCHIP_PFSOC_DRAM_LO].size, + machine); + riscv_load_fdt(fdt_load_addr, machine->fdt); + /* Load the reset vector */ riscv_setup_rom_reset_vec(machine, &s->soc.u_cpus, firmware_load_addr, memmap[MICROCHIP_PFSOC_ENVM_DATA].base, memmap[MICROCHIP_PFSOC_ENVM_DATA].size, - kernel_entry, fdt_load_addr, machine->fdt); + kernel_entry, fdt_load_addr); } } @@ -600,7 +649,7 @@ static void microchip_icicle_kit_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_id = "microchip.icicle.kit.ram"; /* - * Map 513 MiB high memory, the mimimum required high memory size, because + * Map 513 MiB high memory, the minimum required high memory size, because * HSS will do memory test against the high memory address range regardless * of physical memory installed. * diff --git a/hw/riscv/numa.c b/hw/riscv/numa.c index 7fe92d402f..cf686f4ff1 100644 --- a/hw/riscv/numa.c +++ b/hw/riscv/numa.c @@ -156,18 +156,19 @@ uint64_t riscv_socket_mem_size(const MachineState *ms, int socket_id) ms->numa_state->nodes[socket_id].node_mem : 0; } -void riscv_socket_fdt_write_id(const MachineState *ms, void *fdt, - const char *node_name, int socket_id) +void riscv_socket_fdt_write_id(const MachineState *ms, const char *node_name, + int socket_id) { if (numa_enabled(ms)) { - qemu_fdt_setprop_cell(fdt, node_name, "numa-node-id", socket_id); + qemu_fdt_setprop_cell(ms->fdt, node_name, "numa-node-id", socket_id); } } -void riscv_socket_fdt_write_distance_matrix(const MachineState *ms, void *fdt) +void riscv_socket_fdt_write_distance_matrix(const MachineState *ms) { int i, j, idx; - uint32_t *dist_matrix, dist_matrix_size; + g_autofree uint32_t *dist_matrix = NULL; + uint32_t dist_matrix_size; if (numa_enabled(ms) && ms->numa_state->have_numa_distance) { dist_matrix_size = riscv_socket_count(ms) * riscv_socket_count(ms); @@ -184,12 +185,11 @@ void riscv_socket_fdt_write_distance_matrix(const MachineState *ms, void *fdt) } } - qemu_fdt_add_subnode(fdt, "/distance-map"); - qemu_fdt_setprop_string(fdt, "/distance-map", "compatible", + qemu_fdt_add_subnode(ms->fdt, "/distance-map"); + qemu_fdt_setprop_string(ms->fdt, "/distance-map", "compatible", "numa-distance-map-v1"); - qemu_fdt_setprop(fdt, "/distance-map", "distance-matrix", + qemu_fdt_setprop(ms->fdt, "/distance-map", "distance-matrix", dist_matrix, dist_matrix_size); - g_free(dist_matrix); } } @@ -207,6 +207,12 @@ int64_t riscv_numa_get_default_cpu_node_id(const MachineState *ms, int idx) { int64_t nidx = 0; + if (ms->numa_state->num_nodes > ms->smp.cpus) { + error_report("Number of NUMA nodes (%d)" + " cannot exceed the number of available CPUs (%u).", + ms->numa_state->num_nodes, ms->smp.cpus); + exit(EXIT_FAILURE); + } if (ms->numa_state->num_nodes) { nidx = idx / (ms->smp.cpus / ms->numa_state->num_nodes); if (ms->numa_state->num_nodes <= nidx) { diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 4495a2c039..436503f1ba 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -22,54 +22,64 @@ #include "qemu/cutils.h" #include "hw/riscv/opentitan.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "hw/boards.h" #include "hw/misc/unimp.h" #include "hw/riscv/boot.h" #include "qemu/units.h" #include "sysemu/sysemu.h" +/* + * This version of the OpenTitan machine currently supports + * OpenTitan RTL version: + * + * + * MMIO mapping as per (specified commit): + * lowRISC/opentitan: hw/top_earlgrey/sw/autogen/top_earlgrey_memory.h + */ static const MemMapEntry ibex_memmap[] = { - [IBEX_DEV_ROM] = { 0x00008000, 16 * KiB }, - [IBEX_DEV_RAM] = { 0x10000000, 0x10000 }, - [IBEX_DEV_FLASH] = { 0x20000000, 0x80000 }, - [IBEX_DEV_UART] = { 0x40000000, 0x1000 }, - [IBEX_DEV_GPIO] = { 0x40040000, 0x1000 }, - [IBEX_DEV_SPI_DEVICE] = { 0x40050000, 0x1000 }, - [IBEX_DEV_I2C] = { 0x40080000, 0x1000 }, - [IBEX_DEV_PATTGEN] = { 0x400e0000, 0x1000 }, - [IBEX_DEV_TIMER] = { 0x40100000, 0x1000 }, - [IBEX_DEV_SENSOR_CTRL] = { 0x40110000, 0x1000 }, - [IBEX_DEV_OTP_CTRL] = { 0x40130000, 0x4000 }, - [IBEX_DEV_USBDEV] = { 0x40150000, 0x1000 }, - [IBEX_DEV_SPI_HOST0] = { 0x40300000, 0x1000 }, - [IBEX_DEV_SPI_HOST1] = { 0x40310000, 0x1000 }, - [IBEX_DEV_PWRMGR] = { 0x40400000, 0x1000 }, - [IBEX_DEV_RSTMGR] = { 0x40410000, 0x1000 }, - [IBEX_DEV_CLKMGR] = { 0x40420000, 0x1000 }, - [IBEX_DEV_PINMUX] = { 0x40460000, 0x1000 }, - [IBEX_DEV_PADCTRL] = { 0x40470000, 0x1000 }, - [IBEX_DEV_FLASH_CTRL] = { 0x41000000, 0x1000 }, - [IBEX_DEV_AES] = { 0x41100000, 0x1000 }, - [IBEX_DEV_HMAC] = { 0x41110000, 0x1000 }, - [IBEX_DEV_KMAC] = { 0x41120000, 0x1000 }, - [IBEX_DEV_OTBN] = { 0x41130000, 0x10000 }, - [IBEX_DEV_KEYMGR] = { 0x41140000, 0x1000 }, - [IBEX_DEV_CSRNG] = { 0x41150000, 0x1000 }, - [IBEX_DEV_ENTROPY] = { 0x41160000, 0x1000 }, - [IBEX_DEV_EDNO] = { 0x41170000, 0x1000 }, - [IBEX_DEV_EDN1] = { 0x41180000, 0x1000 }, - [IBEX_DEV_ALERT_HANDLER] = { 0x411b0000, 0x1000 }, - [IBEX_DEV_NMI_GEN] = { 0x411c0000, 0x1000 }, - [IBEX_DEV_PERI] = { 0x411f0000, 0x10000 }, - [IBEX_DEV_PLIC] = { 0x48000000, 0x4005000 }, - [IBEX_DEV_FLASH_VIRTUAL] = { 0x80000000, 0x80000 }, + [IBEX_DEV_ROM] = { 0x00008000, 0x8000 }, + [IBEX_DEV_RAM] = { 0x10000000, 0x20000 }, + [IBEX_DEV_FLASH] = { 0x20000000, 0x100000 }, + [IBEX_DEV_UART] = { 0x40000000, 0x40 }, + [IBEX_DEV_GPIO] = { 0x40040000, 0x40 }, + [IBEX_DEV_SPI_DEVICE] = { 0x40050000, 0x2000 }, + [IBEX_DEV_I2C] = { 0x40080000, 0x80 }, + [IBEX_DEV_PATTGEN] = { 0x400e0000, 0x40 }, + [IBEX_DEV_TIMER] = { 0x40100000, 0x200 }, + [IBEX_DEV_OTP_CTRL] = { 0x40130000, 0x2000 }, + [IBEX_DEV_LC_CTRL] = { 0x40140000, 0x100 }, + [IBEX_DEV_ALERT_HANDLER] = { 0x40150000, 0x800 }, + [IBEX_DEV_SPI_HOST0] = { 0x40300000, 0x40 }, + [IBEX_DEV_SPI_HOST1] = { 0x40310000, 0x40 }, + [IBEX_DEV_USBDEV] = { 0x40320000, 0x1000 }, + [IBEX_DEV_PWRMGR] = { 0x40400000, 0x80 }, + [IBEX_DEV_RSTMGR] = { 0x40410000, 0x80 }, + [IBEX_DEV_CLKMGR] = { 0x40420000, 0x80 }, + [IBEX_DEV_PINMUX] = { 0x40460000, 0x1000 }, + [IBEX_DEV_AON_TIMER] = { 0x40470000, 0x40 }, + [IBEX_DEV_SENSOR_CTRL] = { 0x40490000, 0x40 }, + [IBEX_DEV_FLASH_CTRL] = { 0x41000000, 0x200 }, + [IBEX_DEV_AES] = { 0x41100000, 0x100 }, + [IBEX_DEV_HMAC] = { 0x41110000, 0x1000 }, + [IBEX_DEV_KMAC] = { 0x41120000, 0x1000 }, + [IBEX_DEV_OTBN] = { 0x41130000, 0x10000 }, + [IBEX_DEV_KEYMGR] = { 0x41140000, 0x100 }, + [IBEX_DEV_CSRNG] = { 0x41150000, 0x80 }, + [IBEX_DEV_ENTROPY] = { 0x41160000, 0x100 }, + [IBEX_DEV_EDNO] = { 0x41170000, 0x80 }, + [IBEX_DEV_EDN1] = { 0x41180000, 0x80 }, + [IBEX_DEV_SRAM_CTRL] = { 0x411c0000, 0x20 }, + [IBEX_DEV_IBEX_CFG] = { 0x411f0000, 0x100 }, + [IBEX_DEV_PLIC] = { 0x48000000, 0x8000000 }, + [IBEX_DEV_FLASH_VIRTUAL] = { 0x80000000, 0x80000 }, }; -static void opentitan_board_init(MachineState *machine) +static void opentitan_machine_init(MachineState *machine) { MachineClass *mc = MACHINE_GET_CLASS(machine); + OpenTitanState *s = OPENTITAN_MACHINE(machine); const MemMapEntry *memmap = ibex_memmap; - OpenTitanState *s = g_new0(OpenTitanState, 1); MemoryRegion *sys_mem = get_system_memory(); if (machine->ram_size != mc->default_ram_size) { @@ -92,23 +102,24 @@ static void opentitan_board_init(MachineState *machine) } if (machine->kernel_filename) { - riscv_load_kernel(machine->kernel_filename, - memmap[IBEX_DEV_RAM].base, NULL); + riscv_load_kernel(machine, &s->soc.cpus, + memmap[IBEX_DEV_RAM].base, + false, NULL); } } -static void opentitan_machine_init(MachineClass *mc) +static void opentitan_machine_class_init(ObjectClass *oc, void *data) { + MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "RISC-V Board compatible with OpenTitan"; - mc->init = opentitan_board_init; + mc->init = opentitan_machine_init; mc->max_cpus = 1; mc->default_cpu_type = TYPE_RISCV_CPU_IBEX; mc->default_ram_id = "riscv.lowrisc.ibex.ram"; mc->default_ram_size = ibex_memmap[IBEX_DEV_RAM].size; } -DEFINE_MACHINE("opentitan", opentitan_machine_init) - static void lowrisc_ibex_soc_init(Object *obj) { LowRISCIbexSoCState *s = RISCV_IBEX_SOC(obj); @@ -141,7 +152,8 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) &error_abort); object_property_set_int(OBJECT(&s->cpus), "num-harts", ms->smp.cpus, &error_abort); - object_property_set_int(OBJECT(&s->cpus), "resetvec", 0x8080, &error_abort); + object_property_set_int(OBJECT(&s->cpus), "resetvec", s->resetvec, + &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_fatal); /* Boot ROM */ @@ -163,10 +175,8 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) /* PLIC */ qdev_prop_set_string(DEVICE(&s->plic), "hart-config", "M"); - qdev_prop_set_uint32(DEVICE(&s->plic), "hartid-base", 0); qdev_prop_set_uint32(DEVICE(&s->plic), "num-sources", 180); qdev_prop_set_uint32(DEVICE(&s->plic), "num-priorities", 3); - qdev_prop_set_uint32(DEVICE(&s->plic), "priority-base", 0x00); qdev_prop_set_uint32(DEVICE(&s->plic), "pending-base", 0x1000); qdev_prop_set_uint32(DEVICE(&s->plic), "enable-base", 0x2000); qdev_prop_set_uint32(DEVICE(&s->plic), "enable-stride", 32); @@ -217,7 +227,7 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) IRQ_M_TIMER)); /* SPI-Hosts */ - for (int i = 0; i < OPENTITAN_NUM_SPI_HOSTS; ++i) { + for (i = 0; i < OPENTITAN_NUM_SPI_HOSTS; ++i) { dev = DEVICE(&(s->spi_host[i])); if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi_host[i]), errp)) { return; @@ -253,6 +263,8 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) memmap[IBEX_DEV_SENSOR_CTRL].base, memmap[IBEX_DEV_SENSOR_CTRL].size); create_unimplemented_device("riscv.lowrisc.ibex.otp_ctrl", memmap[IBEX_DEV_OTP_CTRL].base, memmap[IBEX_DEV_OTP_CTRL].size); + create_unimplemented_device("riscv.lowrisc.ibex.lc_ctrl", + memmap[IBEX_DEV_LC_CTRL].base, memmap[IBEX_DEV_LC_CTRL].size); create_unimplemented_device("riscv.lowrisc.ibex.pwrmgr", memmap[IBEX_DEV_PWRMGR].base, memmap[IBEX_DEV_PWRMGR].size); create_unimplemented_device("riscv.lowrisc.ibex.rstmgr", @@ -261,8 +273,8 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) memmap[IBEX_DEV_CLKMGR].base, memmap[IBEX_DEV_CLKMGR].size); create_unimplemented_device("riscv.lowrisc.ibex.pinmux", memmap[IBEX_DEV_PINMUX].base, memmap[IBEX_DEV_PINMUX].size); - create_unimplemented_device("riscv.lowrisc.ibex.padctrl", - memmap[IBEX_DEV_PADCTRL].base, memmap[IBEX_DEV_PADCTRL].size); + create_unimplemented_device("riscv.lowrisc.ibex.aon_timer", + memmap[IBEX_DEV_AON_TIMER].base, memmap[IBEX_DEV_AON_TIMER].size); create_unimplemented_device("riscv.lowrisc.ibex.usbdev", memmap[IBEX_DEV_USBDEV].base, memmap[IBEX_DEV_USBDEV].size); create_unimplemented_device("riscv.lowrisc.ibex.flash_ctrl", @@ -285,34 +297,42 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) memmap[IBEX_DEV_EDN1].base, memmap[IBEX_DEV_EDN1].size); create_unimplemented_device("riscv.lowrisc.ibex.alert_handler", memmap[IBEX_DEV_ALERT_HANDLER].base, memmap[IBEX_DEV_ALERT_HANDLER].size); - create_unimplemented_device("riscv.lowrisc.ibex.nmi_gen", - memmap[IBEX_DEV_NMI_GEN].base, memmap[IBEX_DEV_NMI_GEN].size); + create_unimplemented_device("riscv.lowrisc.ibex.sram_ctrl", + memmap[IBEX_DEV_SRAM_CTRL].base, memmap[IBEX_DEV_SRAM_CTRL].size); create_unimplemented_device("riscv.lowrisc.ibex.otbn", memmap[IBEX_DEV_OTBN].base, memmap[IBEX_DEV_OTBN].size); - create_unimplemented_device("riscv.lowrisc.ibex.peri", - memmap[IBEX_DEV_PERI].base, memmap[IBEX_DEV_PERI].size); + create_unimplemented_device("riscv.lowrisc.ibex.ibex_cfg", + memmap[IBEX_DEV_IBEX_CFG].base, memmap[IBEX_DEV_IBEX_CFG].size); } +static Property lowrisc_ibex_soc_props[] = { + DEFINE_PROP_UINT32("resetvec", LowRISCIbexSoCState, resetvec, 0x20000400), + DEFINE_PROP_END_OF_LIST() +}; + static void lowrisc_ibex_soc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + device_class_set_props(dc, lowrisc_ibex_soc_props); dc->realize = lowrisc_ibex_soc_realize; /* Reason: Uses serial_hds in realize function, thus can't be used twice */ dc->user_creatable = false; } -static const TypeInfo lowrisc_ibex_soc_type_info = { - .name = TYPE_RISCV_IBEX_SOC, - .parent = TYPE_DEVICE, - .instance_size = sizeof(LowRISCIbexSoCState), - .instance_init = lowrisc_ibex_soc_init, - .class_init = lowrisc_ibex_soc_class_init, +static const TypeInfo open_titan_types[] = { + { + .name = TYPE_RISCV_IBEX_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(LowRISCIbexSoCState), + .instance_init = lowrisc_ibex_soc_init, + .class_init = lowrisc_ibex_soc_class_init, + }, { + .name = TYPE_OPENTITAN_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(OpenTitanState), + .class_init = opentitan_machine_class_init, + } }; -static void lowrisc_ibex_soc_register_types(void) -{ - type_register_static(&lowrisc_ibex_soc_type_info); -} - -type_init(lowrisc_ibex_soc_register_types) +DEFINE_TYPES(open_titan_types) diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c index 90e2cf609f..3888034c2b 100644 --- a/hw/riscv/shakti_c.c +++ b/hw/riscv/shakti_c.c @@ -20,6 +20,7 @@ #include "hw/boards.h" #include "hw/riscv/shakti_c.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "hw/intc/sifive_plic.h" #include "hw/intc/riscv_aclint.h" #include "sysemu/sysemu.h" @@ -27,7 +28,6 @@ #include "exec/address-spaces.h" #include "hw/riscv/boot.h" - static const struct MemmapEntry { hwaddr base; hwaddr size; @@ -46,12 +46,6 @@ static void shakti_c_machine_state_init(MachineState *mstate) ShaktiCMachineState *sms = RISCV_SHAKTI_MACHINE(mstate); MemoryRegion *system_memory = get_system_memory(); - /* Allow only Shakti C CPU for this platform */ - if (strcmp(mstate->cpu_type, TYPE_RISCV_CPU_SHAKTI_C) != 0) { - error_report("This board can only be used with Shakti C CPU"); - exit(1); - } - /* Initialize SoC */ object_initialize_child(OBJECT(mstate), "soc", &sms->soc, TYPE_RISCV_SHAKTI_SOC); @@ -66,8 +60,7 @@ static void shakti_c_machine_state_init(MachineState *mstate) riscv_setup_rom_reset_vec(mstate, &sms->soc.cpus, shakti_c_memmap[SHAKTI_C_RAM].base, shakti_c_memmap[SHAKTI_C_ROM].base, - shakti_c_memmap[SHAKTI_C_ROM].size, 0, 0, - NULL); + shakti_c_memmap[SHAKTI_C_ROM].size, 0, 0); if (mstate->firmware) { riscv_load_firmware(mstate->firmware, shakti_c_memmap[SHAKTI_C_RAM].base, @@ -82,9 +75,15 @@ static void shakti_c_machine_instance_init(Object *obj) static void shakti_c_machine_class_init(ObjectClass *klass, void *data) { MachineClass *mc = MACHINE_CLASS(klass); + static const char * const valid_cpu_types[] = { + RISCV_CPU_TYPE_NAME("shakti-c"), + NULL + }; + mc->desc = "RISC-V Board compatible with Shakti SDK"; mc->init = shakti_c_machine_state_init; mc->default_cpu_type = TYPE_RISCV_CPU_SHAKTI_C; + mc->valid_cpu_types = valid_cpu_types; mc->default_ram_id = "riscv.shakti.c.ram"; } diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index d65d2fd869..87d9602383 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -45,6 +45,7 @@ #include "hw/intc/riscv_aclint.h" #include "hw/intc/sifive_plic.h" #include "hw/misc/sifive_e_prci.h" +#include "hw/misc/sifive_e_aon.h" #include "chardev/char.h" #include "sysemu/sysemu.h" @@ -114,8 +115,9 @@ static void sifive_e_machine_init(MachineState *machine) memmap[SIFIVE_E_DEV_MROM].base, &address_space_memory); if (machine->kernel_filename) { - riscv_load_kernel(machine->kernel_filename, - memmap[SIFIVE_E_DEV_DTIM].base, NULL); + riscv_load_kernel(machine, &s->soc.cpus, + memmap[SIFIVE_E_DEV_DTIM].base, + false, NULL); } } @@ -184,6 +186,8 @@ static void sifive_e_soc_init(Object *obj) object_property_set_int(OBJECT(&s->cpus), "resetvec", 0x1004, &error_abort); object_initialize_child(obj, "riscv.sifive.e.gpio0", &s->gpio, TYPE_SIFIVE_GPIO); + object_initialize_child(obj, "riscv.sifive.e.aon", &s->aon, + TYPE_SIFIVE_E_AON); } static void sifive_e_soc_realize(DeviceState *dev, Error **errp) @@ -221,11 +225,18 @@ static void sifive_e_soc_realize(DeviceState *dev, Error **errp) RISCV_ACLINT_SWI_SIZE, RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus, RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, - RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false); - create_unimplemented_device("riscv.sifive.e.aon", - memmap[SIFIVE_E_DEV_AON].base, memmap[SIFIVE_E_DEV_AON].size); + SIFIVE_E_LFCLK_DEFAULT_FREQ, false); sifive_e_prci_create(memmap[SIFIVE_E_DEV_PRCI].base); + /* AON */ + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->aon), errp)) { + return; + } + + /* Map AON registers */ + sysbus_mmio_map(SYS_BUS_DEVICE(&s->aon), 0, memmap[SIFIVE_E_DEV_AON].base); + /* GPIO */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { @@ -244,6 +255,9 @@ static void sifive_e_soc_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_GPIO0_IRQ0 + i)); } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->aon), 0, + qdev_get_gpio_in(DEVICE(s->plic), + SIFIVE_E_AON_WDT_IRQ)); sifive_uart_create(sys_mem, memmap[SIFIVE_E_DEV_UART0].base, serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_UART0_IRQ)); diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index e4c814a3ea..af5f923f54 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -94,9 +94,10 @@ static const MemMapEntry sifive_u_memmap[] = { #define GEM_REVISION 0x10070109 static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, - uint64_t mem_size, const char *cmdline, bool is_32_bit) + bool is_32_bit) { - MachineState *ms = MACHINE(qdev_get_machine()); + MachineState *ms = MACHINE(s); + uint64_t mem_size = ms->ram_size; void *fdt; int cpu; uint32_t *cells; @@ -111,19 +112,10 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, "sifive,plic-1.0.0", "riscv,plic0" }; - if (ms->dtb) { - fdt = s->fdt = load_device_tree(ms->dtb, &s->fdt_size); - if (!fdt) { - error_report("load_device_tree() failed"); - exit(1); - } - goto update_bootargs; - } else { - fdt = s->fdt = create_device_tree(&s->fdt_size); - if (!fdt) { - error_report("create_device_tree() failed"); - exit(1); - } + fdt = ms->fdt = create_device_tree(&s->fdt_size); + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); } qemu_fdt_setprop_string(fdt, "/", "model", "SiFive HiFive Unleashed A00"); @@ -179,7 +171,6 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, int cpu_phandle = phandle++; nodename = g_strdup_printf("/cpus/cpu@%d", cpu); char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); - char *isa; qemu_fdt_add_subnode(fdt, nodename); /* cpu 0 is the management hart that does not have mmu */ if (cpu != 0) { @@ -188,11 +179,10 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, } else { qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48"); } - isa = riscv_isa_string(&s->soc.u_cpus.harts[cpu - 1]); + riscv_isa_write_fdt(&s->soc.u_cpus.harts[cpu - 1], fdt, nodename); } else { - isa = riscv_isa_string(&s->soc.e_cpus.harts[0]); + riscv_isa_write_fdt(&s->soc.e_cpus.harts[0], fdt, nodename); } - qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa); qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv"); qemu_fdt_setprop_string(fdt, nodename, "status", "okay"); qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu); @@ -202,7 +192,6 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc"); qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0); qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1); - g_free(isa); g_free(intc); g_free(nodename); } @@ -287,7 +276,8 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0, memmap[SIFIVE_U_DEV_PLIC].base, 0x0, memmap[SIFIVE_U_DEV_PLIC].size); - qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 0x35); + qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", + SIFIVE_U_PLIC_NUM_SOURCES - 1); qemu_fdt_setprop_cell(fdt, nodename, "phandle", plic_phandle); plic_phandle = qemu_fdt_get_phandle(fdt, nodename); g_free(cells); @@ -509,11 +499,6 @@ static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap, qemu_fdt_setprop_string(fdt, "/aliases", "serial0", nodename); g_free(nodename); - -update_bootargs: - if (cmdline && *cmdline) { - qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); - } } static void sifive_u_machine_reset(void *opaque, int n, int level) @@ -532,6 +517,7 @@ static void sifive_u_machine_init(MachineState *machine) MemoryRegion *flash0 = g_new(MemoryRegion, 1); target_ulong start_addr = memmap[SIFIVE_U_DEV_DRAM].base; target_ulong firmware_end_addr, kernel_start_addr; + const char *firmware_name; uint32_t start_addr_hi32 = 0x00000000; int i; uint32_t fdt_load_addr; @@ -563,9 +549,16 @@ static void sifive_u_machine_init(MachineState *machine) qdev_connect_gpio_out(DEVICE(&(s->soc.gpio)), 10, qemu_allocate_irq(sifive_u_machine_reset, NULL, 0)); - /* create device tree */ - create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline, - riscv_is_32bit(&s->soc.u_cpus)); + /* load/create device tree */ + if (machine->dtb) { + machine->fdt = load_device_tree(machine->dtb, &s->fdt_size); + if (!machine->fdt) { + error_report("load_device_tree() failed"); + exit(1); + } + } else { + create_fdt(s, memmap, riscv_is_32bit(&s->soc.u_cpus)); + } if (s->start_in_flash) { /* @@ -594,31 +587,16 @@ static void sifive_u_machine_init(MachineState *machine) break; } - if (riscv_is_32bit(&s->soc.u_cpus)) { - firmware_end_addr = riscv_find_and_load_firmware(machine, - RISCV32_BIOS_BIN, start_addr, NULL); - } else { - firmware_end_addr = riscv_find_and_load_firmware(machine, - RISCV64_BIOS_BIN, start_addr, NULL); - } + firmware_name = riscv_default_firmware_name(&s->soc.u_cpus); + firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, + start_addr, NULL); if (machine->kernel_filename) { kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc.u_cpus, firmware_end_addr); - kernel_entry = riscv_load_kernel(machine->kernel_filename, - kernel_start_addr, NULL); - - if (machine->initrd_filename) { - hwaddr start; - hwaddr end = riscv_load_initrd(machine->initrd_filename, - machine->ram_size, kernel_entry, - &start); - qemu_fdt_setprop_cell(s->fdt, "/chosen", - "linux,initrd-start", start); - qemu_fdt_setprop_cell(s->fdt, "/chosen", "linux,initrd-end", - end); - } + kernel_entry = riscv_load_kernel(machine, &s->soc.u_cpus, + kernel_start_addr, true, NULL); } else { /* * If dynamic firmware is used, it doesn't know where is the next mode @@ -627,9 +605,11 @@ static void sifive_u_machine_init(MachineState *machine) kernel_entry = 0; } - /* Compute the fdt load address in dram */ - fdt_load_addr = riscv_load_fdt(memmap[SIFIVE_U_DEV_DRAM].base, - machine->ram_size, s->fdt); + fdt_load_addr = riscv_compute_fdt_addr(memmap[SIFIVE_U_DEV_DRAM].base, + memmap[SIFIVE_U_DEV_DRAM].size, + machine); + riscv_load_fdt(fdt_load_addr, machine->fdt); + if (!riscv_is_32bit(&s->soc.u_cpus)) { start_addr_hi32 = (uint64_t)start_addr >> 32; } @@ -691,9 +671,8 @@ static void sifive_u_machine_init(MachineState *machine) dinfo = drive_get(IF_SD, 0, 0); blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; - card_dev = qdev_new(TYPE_SD_CARD); + card_dev = qdev_new(TYPE_SD_CARD_SPI); qdev_prop_set_drive_err(card_dev, "drive", blk, &error_fatal); - qdev_prop_set_bit(card_dev, "spi", true); qdev_realize_and_unref(card_dev, qdev_get_child_bus(sd_dev, "sd-bus"), &error_fatal); @@ -807,7 +786,6 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp) MemoryRegion *l2lim_mem = g_new(MemoryRegion, 1); char *plic_hart_config; int i, j; - NICInfo *nd = &nd_table[0]; qdev_prop_set_uint32(DEVICE(&s->u_cpus), "num-harts", ms->smp.cpus - 1); qdev_prop_set_uint32(DEVICE(&s->u_cpus), "hartid-base", 1); @@ -911,11 +889,7 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->otp), 0, memmap[SIFIVE_U_DEV_OTP].base); - /* FIXME use qdev NIC properties instead of nd_table[] */ - if (nd->used) { - qemu_check_nic_model(nd, TYPE_CADENCE_GEM); - qdev_set_nic_properties(DEVICE(&s->gem), nd); - } + qemu_configure_nic_device(DEVICE(&s->gem), true, NULL); object_property_set_int(OBJECT(&s->gem), "revision", GEM_REVISION, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->gem), errp)) { diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index e41b6aa9f0..64074395bc 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -8,7 +8,6 @@ * * 0) HTIF Console and Poweroff * 1) CLINT (Timer and IPI) - * 2) PLIC (Platform Level Interrupt Controller) * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -40,6 +39,8 @@ #include "sysemu/device_tree.h" #include "sysemu/sysemu.h" +#include + static const MemMapEntry spike_memmap[] = { [SPIKE_MROM] = { 0x1000, 0xf000 }, [SPIKE_HTIF] = { 0x1000000, 0x1000 }, @@ -48,22 +49,23 @@ static const MemMapEntry spike_memmap[] = { }; static void create_fdt(SpikeState *s, const MemMapEntry *memmap, - uint64_t mem_size, const char *cmdline, bool is_32_bit) + bool is_32_bit, bool htif_custom_base) { void *fdt; + int fdt_size; uint64_t addr, size; unsigned long clint_addr; int cpu, socket; - MachineState *mc = MACHINE(s); + MachineState *ms = MACHINE(s); uint32_t *clint_cells; uint32_t cpu_phandle, intc_phandle, phandle = 1; - char *name, *mem_name, *clint_name, *clust_name; + char *mem_name, *clint_name, *clust_name; char *core_name, *cpu_name, *intc_name; static const char * const clint_compat[2] = { "sifive,clint0", "riscv,clint0" }; - fdt = s->fdt = create_device_tree(&s->fdt_size); + fdt = ms->fdt = create_device_tree(&fdt_size); if (!fdt) { error_report("create_device_tree() failed"); exit(1); @@ -76,7 +78,7 @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, qemu_fdt_add_subnode(fdt, "/htif"); qemu_fdt_setprop_string(fdt, "/htif", "compatible", "ucb,htif0"); - if (!htif_uses_elf_symbols()) { + if (htif_custom_base) { qemu_fdt_setprop_cells(fdt, "/htif", "reg", 0x0, memmap[SPIKE_HTIF].base, 0x0, memmap[SPIKE_HTIF].size); } @@ -94,7 +96,7 @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); qemu_fdt_add_subnode(fdt, "/cpus/cpu-map"); - for (socket = (riscv_socket_count(mc) - 1); socket >= 0; socket--) { + for (socket = (riscv_socket_count(ms) - 1); socket >= 0; socket--) { clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket); qemu_fdt_add_subnode(fdt, clust_name); @@ -111,15 +113,13 @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, } else { qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv48"); } - name = riscv_isa_string(&s->soc[socket].harts[cpu]); - qemu_fdt_setprop_string(fdt, cpu_name, "riscv,isa", name); - g_free(name); + riscv_isa_write_fdt(&s->soc[socket].harts[cpu], fdt, cpu_name); qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv"); qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay"); qemu_fdt_setprop_cell(fdt, cpu_name, "reg", s->soc[socket].hartid_base + cpu); qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu"); - riscv_socket_fdt_write_id(mc, fdt, cpu_name, socket); + riscv_socket_fdt_write_id(ms, cpu_name, socket); qemu_fdt_setprop_cell(fdt, cpu_name, "phandle", cpu_phandle); intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name); @@ -145,14 +145,14 @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, g_free(cpu_name); } - addr = memmap[SPIKE_DRAM].base + riscv_socket_mem_offset(mc, socket); - size = riscv_socket_mem_size(mc, socket); + addr = memmap[SPIKE_DRAM].base + riscv_socket_mem_offset(ms, socket); + size = riscv_socket_mem_size(ms, socket); mem_name = g_strdup_printf("/memory@%lx", (long)addr); qemu_fdt_add_subnode(fdt, mem_name); qemu_fdt_setprop_cells(fdt, mem_name, "reg", addr >> 32, addr, size >> 32, size); qemu_fdt_setprop_string(fdt, mem_name, "device_type", "memory"); - riscv_socket_fdt_write_id(mc, fdt, mem_name, socket); + riscv_socket_fdt_write_id(ms, mem_name, socket); g_free(mem_name); clint_addr = memmap[SPIKE_CLINT].base + @@ -165,20 +165,29 @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, 0x0, clint_addr, 0x0, memmap[SPIKE_CLINT].size); qemu_fdt_setprop(fdt, clint_name, "interrupts-extended", clint_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4); - riscv_socket_fdt_write_id(mc, fdt, clint_name, socket); + riscv_socket_fdt_write_id(ms, clint_name, socket); g_free(clint_name); g_free(clint_cells); g_free(clust_name); } - riscv_socket_fdt_write_distance_matrix(mc, fdt); + riscv_socket_fdt_write_distance_matrix(ms); qemu_fdt_add_subnode(fdt, "/chosen"); qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", "/htif"); +} - if (cmdline && *cmdline) { - qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); +static bool spike_test_elf_image(char *filename) +{ + Error *err = NULL; + + load_elf_hdr(filename, NULL, NULL, &err); + if (err) { + error_free(err); + return false; + } else { + return true; } } @@ -188,11 +197,14 @@ static void spike_board_init(MachineState *machine) SpikeState *s = SPIKE_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *mask_rom = g_new(MemoryRegion, 1); - target_ulong firmware_end_addr, kernel_start_addr; + target_ulong firmware_end_addr = memmap[SPIKE_DRAM].base; + target_ulong kernel_start_addr; + char *firmware_name; uint32_t fdt_load_addr; uint64_t kernel_entry; char *soc_name; int i, base_hartid, hart_count; + bool htif_custom_base = false; /* Check socket count limit */ if (SPIKE_SOCKETS_MAX < riscv_socket_count(machine)) { @@ -254,29 +266,46 @@ static void spike_board_init(MachineState *machine) memory_region_add_subregion(system_memory, memmap[SPIKE_MROM].base, mask_rom); + /* Find firmware */ + firmware_name = riscv_find_firmware(machine->firmware, + riscv_default_firmware_name(&s->soc[0])); + /* - * Not like other RISC-V machines that use plain binary bios images, - * keeping ELF files here was intentional because BIN files don't work - * for the Spike machine as HTIF emulation depends on ELF parsing. + * Test the given firmware or kernel file to see if it is an ELF image. + * If it is an ELF, we assume it contains the symbols required for + * the HTIF console, otherwise we fall back to use the custom base + * passed from device tree for the HTIF console. */ - if (riscv_is_32bit(&s->soc[0])) { - firmware_end_addr = riscv_find_and_load_firmware(machine, - RISCV32_BIOS_BIN, memmap[SPIKE_DRAM].base, - htif_symbol_callback); + if (!firmware_name && !machine->kernel_filename) { + htif_custom_base = true; } else { - firmware_end_addr = riscv_find_and_load_firmware(machine, - RISCV64_BIOS_BIN, memmap[SPIKE_DRAM].base, - htif_symbol_callback); + if (firmware_name) { + htif_custom_base = !spike_test_elf_image(firmware_name); + } + if (!htif_custom_base && machine->kernel_filename) { + htif_custom_base = !spike_test_elf_image(machine->kernel_filename); + } } + /* Load firmware */ + if (firmware_name) { + firmware_end_addr = riscv_load_firmware(firmware_name, + memmap[SPIKE_DRAM].base, + htif_symbol_callback); + g_free(firmware_name); + } + + /* Create device tree */ + create_fdt(s, memmap, riscv_is_32bit(&s->soc[0]), htif_custom_base); + /* Load kernel */ if (machine->kernel_filename) { kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0], firmware_end_addr); - kernel_entry = riscv_load_kernel(machine->kernel_filename, + kernel_entry = riscv_load_kernel(machine, &s->soc[0], kernel_start_addr, - htif_symbol_callback); + true, htif_symbol_callback); } else { /* * If dynamic firmware is used, it doesn't know where is the next mode @@ -285,35 +314,25 @@ static void spike_board_init(MachineState *machine) kernel_entry = 0; } - /* Create device tree */ - create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline, - riscv_is_32bit(&s->soc[0])); + fdt_load_addr = riscv_compute_fdt_addr(memmap[SPIKE_DRAM].base, + memmap[SPIKE_DRAM].size, + machine); + riscv_load_fdt(fdt_load_addr, machine->fdt); - /* Load initrd */ - if (machine->kernel_filename && machine->initrd_filename) { - hwaddr start; - hwaddr end = riscv_load_initrd(machine->initrd_filename, - machine->ram_size, kernel_entry, - &start); - qemu_fdt_setprop_cell(s->fdt, "/chosen", - "linux,initrd-start", start); - qemu_fdt_setprop_cell(s->fdt, "/chosen", "linux,initrd-end", - end); - } - - /* Compute the fdt load address in dram */ - fdt_load_addr = riscv_load_fdt(memmap[SPIKE_DRAM].base, - machine->ram_size, s->fdt); /* load the reset vector */ riscv_setup_rom_reset_vec(machine, &s->soc[0], memmap[SPIKE_DRAM].base, memmap[SPIKE_MROM].base, memmap[SPIKE_MROM].size, kernel_entry, - fdt_load_addr, s->fdt); + fdt_load_addr); /* initialize HTIF using symbols found in load_kernel */ - htif_mm_init(system_memory, mask_rom, - &s->soc[0].harts[0].env, serial_hd(0), - memmap[SPIKE_HTIF].base); + htif_mm_init(system_memory, serial_hd(0), memmap[SPIKE_HTIF].base, + htif_custom_base); +} + +static void spike_set_signature(Object *obj, const char *val, Error **errp) +{ + sig_file = g_strdup(val); } static void spike_machine_instance_init(Object *obj) @@ -333,7 +352,17 @@ static void spike_machine_class_init(ObjectClass *oc, void *data) mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props; mc->get_default_cpu_node_id = riscv_numa_get_default_cpu_node_id; mc->numa_mem_supported = true; + /* platform instead of architectural choice */ + mc->cpu_cluster_has_numa_boundary = true; mc->default_ram_id = "riscv.spike.ram"; + object_class_property_add_str(oc, "signature", NULL, spike_set_signature); + object_class_property_set_description(oc, "signature", + "File to write ACT test signature"); + object_class_property_add_uint8_ptr(oc, "signature-granularity", + &line_size, OBJ_PROP_FLAG_WRITE); + object_class_property_set_description(oc, "signature-granularity", + "Size of each line in ACT signature " + "file"); } static const TypeInfo spike_machine_typeinfo = { diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c new file mode 100644 index 0000000000..0925528160 --- /dev/null +++ b/hw/riscv/virt-acpi-build.c @@ -0,0 +1,787 @@ +/* + * Support for generating ACPI tables and passing them to Guests + * + * RISC-V virt ACPI generation + * + * Copyright (C) 2008-2010 Kevin O'Connor + * Copyright (C) 2006 Fabrice Bellard + * Copyright (C) 2013 Red Hat Inc + * Copyright (c) 2015 HUAWEI TECHNOLOGIES CO.,LTD. + * Copyright (C) 2021-2023 Ventana Micro Systems 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 . + */ + +#include "qemu/osdep.h" +#include "hw/acpi/acpi-defs.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/aml-build.h" +#include "hw/acpi/pci.h" +#include "hw/acpi/utils.h" +#include "hw/intc/riscv_aclint.h" +#include "hw/nvram/fw_cfg_acpi.h" +#include "hw/pci-host/gpex.h" +#include "hw/riscv/virt.h" +#include "hw/riscv/numa.h" +#include "hw/virtio/virtio-acpi.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "sysemu/reset.h" + +#define ACPI_BUILD_TABLE_SIZE 0x20000 +#define ACPI_BUILD_INTC_ID(socket, index) ((socket << 24) | (index)) + +typedef struct AcpiBuildState { + /* Copy of table in RAM (for patching) */ + MemoryRegion *table_mr; + MemoryRegion *rsdp_mr; + MemoryRegion *linker_mr; + /* Is table patched? */ + bool patched; +} AcpiBuildState; + +static void acpi_align_size(GArray *blob, unsigned align) +{ + /* + * Align size to multiple of given size. This reduces the chance + * we need to change size in the future (breaking cross version migration). + */ + g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align)); +} + +static void riscv_acpi_madt_add_rintc(uint32_t uid, + const CPUArchIdList *arch_ids, + GArray *entry, + RISCVVirtState *s) +{ + uint8_t guest_index_bits = imsic_num_bits(s->aia_guests + 1); + uint64_t hart_id = arch_ids->cpus[uid].arch_id; + uint32_t imsic_size, local_cpu_id, socket_id; + uint64_t imsic_socket_addr, imsic_addr; + MachineState *ms = MACHINE(s); + + socket_id = arch_ids->cpus[uid].props.node_id; + local_cpu_id = (arch_ids->cpus[uid].arch_id - + riscv_socket_first_hartid(ms, socket_id)) % + riscv_socket_hart_count(ms, socket_id); + imsic_socket_addr = s->memmap[VIRT_IMSIC_S].base + + (socket_id * VIRT_IMSIC_GROUP_MAX_SIZE); + imsic_size = IMSIC_HART_SIZE(guest_index_bits); + imsic_addr = imsic_socket_addr + local_cpu_id * imsic_size; + build_append_int_noprefix(entry, 0x18, 1); /* Type */ + build_append_int_noprefix(entry, 36, 1); /* Length */ + build_append_int_noprefix(entry, 1, 1); /* Version */ + build_append_int_noprefix(entry, 0, 1); /* Reserved */ + build_append_int_noprefix(entry, 0x1, 4); /* Flags */ + build_append_int_noprefix(entry, hart_id, 8); /* Hart ID */ + build_append_int_noprefix(entry, uid, 4); /* ACPI Processor UID */ + /* External Interrupt Controller ID */ + if (s->aia_type == VIRT_AIA_TYPE_APLIC) { + build_append_int_noprefix(entry, + ACPI_BUILD_INTC_ID( + arch_ids->cpus[uid].props.node_id, + local_cpu_id), + 4); + } else if (s->aia_type == VIRT_AIA_TYPE_NONE) { + build_append_int_noprefix(entry, + ACPI_BUILD_INTC_ID( + arch_ids->cpus[uid].props.node_id, + 2 * local_cpu_id + 1), + 4); + } else { + build_append_int_noprefix(entry, 0, 4); + } + + if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { + /* IMSIC Base address */ + build_append_int_noprefix(entry, imsic_addr, 8); + /* IMSIC Size */ + build_append_int_noprefix(entry, imsic_size, 4); + } else { + build_append_int_noprefix(entry, 0, 8); + build_append_int_noprefix(entry, 0, 4); + } +} + +static void acpi_dsdt_add_cpus(Aml *scope, RISCVVirtState *s) +{ + MachineClass *mc = MACHINE_GET_CLASS(s); + MachineState *ms = MACHINE(s); + const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); + + for (int i = 0; i < arch_ids->len; i++) { + Aml *dev; + GArray *madt_buf = g_array_new(0, 1, 1); + + dev = aml_device("C%.03X", i); + aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0007"))); + aml_append(dev, aml_name_decl("_UID", + aml_int(arch_ids->cpus[i].arch_id))); + + /* build _MAT object */ + riscv_acpi_madt_add_rintc(i, arch_ids, madt_buf, s); + aml_append(dev, aml_name_decl("_MAT", + aml_buffer(madt_buf->len, + (uint8_t *)madt_buf->data))); + g_array_free(madt_buf, true); + + aml_append(scope, dev); + } +} + +static void +acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, + uint32_t uart_irq) +{ + Aml *dev = aml_device("COM0"); + aml_append(dev, aml_name_decl("_HID", aml_string("PNP0501"))); + aml_append(dev, aml_name_decl("_UID", aml_int(0))); + + Aml *crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(uart_memmap->base, + uart_memmap->size, AML_READ_WRITE)); + aml_append(crs, + aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, + AML_EXCLUSIVE, &uart_irq, 1)); + aml_append(dev, aml_name_decl("_CRS", crs)); + + Aml *pkg = aml_package(2); + aml_append(pkg, aml_string("clock-frequency")); + aml_append(pkg, aml_int(3686400)); + + Aml *UUID = aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"); + + Aml *pkg1 = aml_package(1); + aml_append(pkg1, pkg); + + Aml *package = aml_package(2); + aml_append(package, UUID); + aml_append(package, pkg1); + + aml_append(dev, aml_name_decl("_DSD", package)); + aml_append(scope, dev); +} + +/* + * Serial Port Console Redirection Table (SPCR) + * Rev: 1.07 + */ + +static void +spcr_setup(GArray *table_data, BIOSLinker *linker, RISCVVirtState *s) +{ + AcpiSpcrData serial = { + .interface_type = 0, /* 16550 compatible */ + .base_addr.id = AML_AS_SYSTEM_MEMORY, + .base_addr.width = 32, + .base_addr.offset = 0, + .base_addr.size = 1, + .base_addr.addr = s->memmap[VIRT_UART0].base, + .interrupt_type = (1 << 4),/* Bit[4] RISC-V PLIC/APLIC */ + .pc_interrupt = 0, + .interrupt = UART0_IRQ, + .baud_rate = 7, /* 15200 */ + .parity = 0, + .stop_bits = 1, + .flow_control = 0, + .terminal_type = 3, /* ANSI */ + .language = 0, /* Language */ + .pci_device_id = 0xffff, /* not a PCI device*/ + .pci_vendor_id = 0xffff, /* not a PCI device*/ + .pci_bus = 0, + .pci_device = 0, + .pci_function = 0, + .pci_flags = 0, + .pci_segment = 0, + }; + + build_spcr(table_data, linker, &serial, 2, s->oem_id, s->oem_table_id); +} + +/* RHCT Node[N] starts at offset 56 */ +#define RHCT_NODE_ARRAY_OFFSET 56 + +/* + * ACPI spec, Revision 6.5+ + * 5.2.36 RISC-V Hart Capabilities Table (RHCT) + * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/16 + * https://drive.google.com/file/d/1nP3nFiH4jkPMp6COOxP6123DCZKR-tia/view + * https://drive.google.com/file/d/1sKbOa8m1UZw1JkquZYe3F1zQBN1xXsaf/view + */ +static void build_rhct(GArray *table_data, + BIOSLinker *linker, + RISCVVirtState *s) +{ + MachineClass *mc = MACHINE_GET_CLASS(s); + MachineState *ms = MACHINE(s); + const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); + size_t len, aligned_len; + uint32_t isa_offset, num_rhct_nodes, cmo_offset = 0; + RISCVCPU *cpu = &s->soc[0].harts[0]; + uint32_t mmu_offset = 0; + uint8_t satp_mode_max; + g_autofree char *isa = NULL; + + AcpiTable table = { .sig = "RHCT", .rev = 1, .oem_id = s->oem_id, + .oem_table_id = s->oem_table_id }; + + acpi_table_begin(&table, table_data); + + build_append_int_noprefix(table_data, 0x0, 4); /* Reserved */ + + /* Time Base Frequency */ + build_append_int_noprefix(table_data, + RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, 8); + + /* ISA + N hart info */ + num_rhct_nodes = 1 + ms->smp.cpus; + if (cpu->cfg.ext_zicbom || cpu->cfg.ext_zicboz) { + num_rhct_nodes++; + } + + if (cpu->cfg.satp_mode.supported != 0) { + num_rhct_nodes++; + } + + /* Number of RHCT nodes*/ + build_append_int_noprefix(table_data, num_rhct_nodes, 4); + + /* Offset to the RHCT node array */ + build_append_int_noprefix(table_data, RHCT_NODE_ARRAY_OFFSET, 4); + + /* ISA String Node */ + isa_offset = table_data->len - table.table_offset; + build_append_int_noprefix(table_data, 0, 2); /* Type 0 */ + + isa = riscv_isa_string(cpu); + len = 8 + strlen(isa) + 1; + aligned_len = (len % 2) ? (len + 1) : len; + + build_append_int_noprefix(table_data, aligned_len, 2); /* Length */ + build_append_int_noprefix(table_data, 0x1, 2); /* Revision */ + + /* ISA string length including NUL */ + build_append_int_noprefix(table_data, strlen(isa) + 1, 2); + g_array_append_vals(table_data, isa, strlen(isa) + 1); /* ISA string */ + + if (aligned_len != len) { + build_append_int_noprefix(table_data, 0x0, 1); /* Optional Padding */ + } + + /* CMO node */ + if (cpu->cfg.ext_zicbom || cpu->cfg.ext_zicboz) { + cmo_offset = table_data->len - table.table_offset; + build_append_int_noprefix(table_data, 1, 2); /* Type */ + build_append_int_noprefix(table_data, 10, 2); /* Length */ + build_append_int_noprefix(table_data, 0x1, 2); /* Revision */ + build_append_int_noprefix(table_data, 0, 1); /* Reserved */ + + /* CBOM block size */ + if (cpu->cfg.cbom_blocksize) { + build_append_int_noprefix(table_data, + __builtin_ctz(cpu->cfg.cbom_blocksize), + 1); + } else { + build_append_int_noprefix(table_data, 0, 1); + } + + /* CBOP block size */ + build_append_int_noprefix(table_data, 0, 1); + + /* CBOZ block size */ + if (cpu->cfg.cboz_blocksize) { + build_append_int_noprefix(table_data, + __builtin_ctz(cpu->cfg.cboz_blocksize), + 1); + } else { + build_append_int_noprefix(table_data, 0, 1); + } + } + + /* MMU node structure */ + if (cpu->cfg.satp_mode.supported != 0) { + satp_mode_max = satp_mode_max_from_map(cpu->cfg.satp_mode.map); + mmu_offset = table_data->len - table.table_offset; + build_append_int_noprefix(table_data, 2, 2); /* Type */ + build_append_int_noprefix(table_data, 8, 2); /* Length */ + build_append_int_noprefix(table_data, 0x1, 2); /* Revision */ + build_append_int_noprefix(table_data, 0, 1); /* Reserved */ + /* MMU Type */ + if (satp_mode_max == VM_1_10_SV57) { + build_append_int_noprefix(table_data, 2, 1); /* Sv57 */ + } else if (satp_mode_max == VM_1_10_SV48) { + build_append_int_noprefix(table_data, 1, 1); /* Sv48 */ + } else if (satp_mode_max == VM_1_10_SV39) { + build_append_int_noprefix(table_data, 0, 1); /* Sv39 */ + } else { + assert(1); + } + } + + /* Hart Info Node */ + for (int i = 0; i < arch_ids->len; i++) { + len = 16; + int num_offsets = 1; + build_append_int_noprefix(table_data, 0xFFFF, 2); /* Type */ + + /* Length */ + if (cmo_offset) { + len += 4; + num_offsets++; + } + + if (mmu_offset) { + len += 4; + num_offsets++; + } + + build_append_int_noprefix(table_data, len, 2); + build_append_int_noprefix(table_data, 0x1, 2); /* Revision */ + /* Number of offsets */ + build_append_int_noprefix(table_data, num_offsets, 2); + build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */ + /* Offsets */ + build_append_int_noprefix(table_data, isa_offset, 4); + if (cmo_offset) { + build_append_int_noprefix(table_data, cmo_offset, 4); + } + + if (mmu_offset) { + build_append_int_noprefix(table_data, mmu_offset, 4); + } + } + + acpi_table_end(linker, &table); +} + +/* FADT */ +static void build_fadt_rev6(GArray *table_data, + BIOSLinker *linker, + RISCVVirtState *s, + unsigned dsdt_tbl_offset) +{ + AcpiFadtData fadt = { + .rev = 6, + .minor_ver = 5, + .flags = 1 << ACPI_FADT_F_HW_REDUCED_ACPI, + .xdsdt_tbl_offset = &dsdt_tbl_offset, + }; + + build_fadt(table_data, linker, &fadt, s->oem_id, s->oem_table_id); +} + +/* DSDT */ +static void build_dsdt(GArray *table_data, + BIOSLinker *linker, + RISCVVirtState *s) +{ + Aml *scope, *dsdt; + MachineState *ms = MACHINE(s); + uint8_t socket_count; + const MemMapEntry *memmap = s->memmap; + AcpiTable table = { .sig = "DSDT", .rev = 2, .oem_id = s->oem_id, + .oem_table_id = s->oem_table_id }; + + + acpi_table_begin(&table, table_data); + dsdt = init_aml_allocator(); + + /* + * When booting the VM with UEFI, UEFI takes ownership of the RTC hardware. + * While UEFI can use libfdt to disable the RTC device node in the DTB that + * it passes to the OS, it cannot modify AML. Therefore, we won't generate + * the RTC ACPI device at all when using UEFI. + */ + scope = aml_scope("\\_SB"); + acpi_dsdt_add_cpus(scope, s); + + fw_cfg_acpi_dsdt_add(scope, &memmap[VIRT_FW_CFG]); + + socket_count = riscv_socket_count(ms); + + acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ); + + if (socket_count == 1) { + virtio_acpi_dsdt_add(scope, memmap[VIRT_VIRTIO].base, + memmap[VIRT_VIRTIO].size, + VIRTIO_IRQ, 0, VIRTIO_COUNT); + acpi_dsdt_add_gpex_host(scope, PCIE_IRQ); + } else if (socket_count == 2) { + virtio_acpi_dsdt_add(scope, memmap[VIRT_VIRTIO].base, + memmap[VIRT_VIRTIO].size, + VIRTIO_IRQ + VIRT_IRQCHIP_NUM_SOURCES, 0, + VIRTIO_COUNT); + acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + VIRT_IRQCHIP_NUM_SOURCES); + } else { + virtio_acpi_dsdt_add(scope, memmap[VIRT_VIRTIO].base, + memmap[VIRT_VIRTIO].size, + VIRTIO_IRQ + VIRT_IRQCHIP_NUM_SOURCES, 0, + VIRTIO_COUNT); + acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + VIRT_IRQCHIP_NUM_SOURCES * 2); + } + + aml_append(dsdt, scope); + + /* copy AML table into ACPI tables blob and patch header there */ + g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); + + acpi_table_end(linker, &table); + free_aml_allocator(); +} + +/* + * ACPI spec, Revision 6.5+ + * 5.2.12 Multiple APIC Description Table (MADT) + * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/15 + * https://drive.google.com/file/d/1R6k4MshhN3WTT-hwqAquu5nX6xSEqK2l/view + * https://drive.google.com/file/d/1oMGPyOD58JaPgMl1pKasT-VKsIKia7zR/view + */ +static void build_madt(GArray *table_data, + BIOSLinker *linker, + RISCVVirtState *s) +{ + MachineClass *mc = MACHINE_GET_CLASS(s); + MachineState *ms = MACHINE(s); + const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); + uint8_t group_index_bits = imsic_num_bits(riscv_socket_count(ms)); + uint8_t guest_index_bits = imsic_num_bits(s->aia_guests + 1); + uint16_t imsic_max_hart_per_socket = 0; + uint8_t hart_index_bits; + uint64_t aplic_addr; + uint32_t gsi_base; + uint8_t socket; + + for (socket = 0; socket < riscv_socket_count(ms); socket++) { + if (imsic_max_hart_per_socket < s->soc[socket].num_harts) { + imsic_max_hart_per_socket = s->soc[socket].num_harts; + } + } + + hart_index_bits = imsic_num_bits(imsic_max_hart_per_socket); + + AcpiTable table = { .sig = "APIC", .rev = 6, .oem_id = s->oem_id, + .oem_table_id = s->oem_table_id }; + + acpi_table_begin(&table, table_data); + /* Local Interrupt Controller Address */ + build_append_int_noprefix(table_data, 0, 4); + build_append_int_noprefix(table_data, 0, 4); /* MADT Flags */ + + /* RISC-V Local INTC structures per HART */ + for (int i = 0; i < arch_ids->len; i++) { + riscv_acpi_madt_add_rintc(i, arch_ids, table_data, s); + } + + /* IMSIC */ + if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { + /* IMSIC */ + build_append_int_noprefix(table_data, 0x19, 1); /* Type */ + build_append_int_noprefix(table_data, 16, 1); /* Length */ + build_append_int_noprefix(table_data, 1, 1); /* Version */ + build_append_int_noprefix(table_data, 0, 1); /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); /* Flags */ + /* Number of supervisor mode Interrupt Identities */ + build_append_int_noprefix(table_data, VIRT_IRQCHIP_NUM_MSIS, 2); + /* Number of guest mode Interrupt Identities */ + build_append_int_noprefix(table_data, VIRT_IRQCHIP_NUM_MSIS, 2); + /* Guest Index Bits */ + build_append_int_noprefix(table_data, guest_index_bits, 1); + /* Hart Index Bits */ + build_append_int_noprefix(table_data, hart_index_bits, 1); + /* Group Index Bits */ + build_append_int_noprefix(table_data, group_index_bits, 1); + /* Group Index Shift */ + build_append_int_noprefix(table_data, IMSIC_MMIO_GROUP_MIN_SHIFT, 1); + } + + if (s->aia_type != VIRT_AIA_TYPE_NONE) { + /* APLICs */ + for (socket = 0; socket < riscv_socket_count(ms); socket++) { + aplic_addr = s->memmap[VIRT_APLIC_S].base + + s->memmap[VIRT_APLIC_S].size * socket; + gsi_base = VIRT_IRQCHIP_NUM_SOURCES * socket; + build_append_int_noprefix(table_data, 0x1A, 1); /* Type */ + build_append_int_noprefix(table_data, 36, 1); /* Length */ + build_append_int_noprefix(table_data, 1, 1); /* Version */ + build_append_int_noprefix(table_data, socket, 1); /* APLIC ID */ + build_append_int_noprefix(table_data, 0, 4); /* Flags */ + build_append_int_noprefix(table_data, 0, 8); /* Hardware ID */ + /* Number of IDCs */ + if (s->aia_type == VIRT_AIA_TYPE_APLIC) { + build_append_int_noprefix(table_data, + s->soc[socket].num_harts, + 2); + } else { + build_append_int_noprefix(table_data, 0, 2); + } + /* Total External Interrupt Sources Supported */ + build_append_int_noprefix(table_data, VIRT_IRQCHIP_NUM_SOURCES, 2); + /* Global System Interrupt Base */ + build_append_int_noprefix(table_data, gsi_base, 4); + /* APLIC Address */ + build_append_int_noprefix(table_data, aplic_addr, 8); + /* APLIC size */ + build_append_int_noprefix(table_data, + s->memmap[VIRT_APLIC_S].size, 4); + } + } else { + /* PLICs */ + for (socket = 0; socket < riscv_socket_count(ms); socket++) { + aplic_addr = s->memmap[VIRT_PLIC].base + + s->memmap[VIRT_PLIC].size * socket; + gsi_base = VIRT_IRQCHIP_NUM_SOURCES * socket; + build_append_int_noprefix(table_data, 0x1B, 1); /* Type */ + build_append_int_noprefix(table_data, 36, 1); /* Length */ + build_append_int_noprefix(table_data, 1, 1); /* Version */ + build_append_int_noprefix(table_data, socket, 1); /* PLIC ID */ + build_append_int_noprefix(table_data, 0, 8); /* Hardware ID */ + /* Total External Interrupt Sources Supported */ + build_append_int_noprefix(table_data, + VIRT_IRQCHIP_NUM_SOURCES - 1, 2); + build_append_int_noprefix(table_data, 0, 2); /* Max Priority */ + build_append_int_noprefix(table_data, 0, 4); /* Flags */ + /* PLIC Size */ + build_append_int_noprefix(table_data, s->memmap[VIRT_PLIC].size, 4); + /* PLIC Address */ + build_append_int_noprefix(table_data, aplic_addr, 8); + /* Global System Interrupt Vector Base */ + build_append_int_noprefix(table_data, gsi_base, 4); + } + } + + acpi_table_end(linker, &table); +} + +/* + * ACPI spec, Revision 6.5+ + * 5.2.16 System Resource Affinity Table (SRAT) + * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/25 + * https://drive.google.com/file/d/1YTdDx2IPm5IeZjAW932EYU-tUtgS08tX/view + */ +static void +build_srat(GArray *table_data, BIOSLinker *linker, RISCVVirtState *vms) +{ + int i; + uint64_t mem_base; + MachineClass *mc = MACHINE_GET_CLASS(vms); + MachineState *ms = MACHINE(vms); + const CPUArchIdList *cpu_list = mc->possible_cpu_arch_ids(ms); + AcpiTable table = { .sig = "SRAT", .rev = 3, .oem_id = vms->oem_id, + .oem_table_id = vms->oem_table_id }; + + acpi_table_begin(&table, table_data); + build_append_int_noprefix(table_data, 1, 4); /* Reserved */ + build_append_int_noprefix(table_data, 0, 8); /* Reserved */ + + for (i = 0; i < cpu_list->len; ++i) { + uint32_t nodeid = cpu_list->cpus[i].props.node_id; + /* + * 5.2.16.8 RINTC Affinity Structure + */ + build_append_int_noprefix(table_data, 7, 1); /* Type */ + build_append_int_noprefix(table_data, 20, 1); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + build_append_int_noprefix(table_data, nodeid, 4); /* Proximity Domain */ + build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */ + /* Flags, Table 5-70 */ + build_append_int_noprefix(table_data, 1 /* Flags: Enabled */, 4); + build_append_int_noprefix(table_data, 0, 4); /* Clock Domain */ + } + + mem_base = vms->memmap[VIRT_DRAM].base; + for (i = 0; i < ms->numa_state->num_nodes; ++i) { + if (ms->numa_state->nodes[i].node_mem > 0) { + build_srat_memory(table_data, mem_base, + ms->numa_state->nodes[i].node_mem, i, + MEM_AFFINITY_ENABLED); + mem_base += ms->numa_state->nodes[i].node_mem; + } + } + + acpi_table_end(linker, &table); +} + +static void virt_acpi_build(RISCVVirtState *s, AcpiBuildTables *tables) +{ + GArray *table_offsets; + unsigned dsdt, xsdt; + GArray *tables_blob = tables->table_data; + MachineState *ms = MACHINE(s); + + table_offsets = g_array_new(false, true, + sizeof(uint32_t)); + + bios_linker_loader_alloc(tables->linker, + ACPI_BUILD_TABLE_FILE, tables_blob, + 64, false); + + /* DSDT is pointed to by FADT */ + dsdt = tables_blob->len; + build_dsdt(tables_blob, tables->linker, s); + + /* FADT and others pointed to by XSDT */ + acpi_add_table(table_offsets, tables_blob); + build_fadt_rev6(tables_blob, tables->linker, s, dsdt); + + acpi_add_table(table_offsets, tables_blob); + build_madt(tables_blob, tables->linker, s); + + acpi_add_table(table_offsets, tables_blob); + build_rhct(tables_blob, tables->linker, s); + + acpi_add_table(table_offsets, tables_blob); + spcr_setup(tables_blob, tables->linker, s); + + acpi_add_table(table_offsets, tables_blob); + { + AcpiMcfgInfo mcfg = { + .base = s->memmap[VIRT_PCIE_ECAM].base, + .size = s->memmap[VIRT_PCIE_ECAM].size, + }; + build_mcfg(tables_blob, tables->linker, &mcfg, s->oem_id, + s->oem_table_id); + } + + if (ms->numa_state->num_nodes > 0) { + acpi_add_table(table_offsets, tables_blob); + build_srat(tables_blob, tables->linker, s); + if (ms->numa_state->have_numa_distance) { + acpi_add_table(table_offsets, tables_blob); + build_slit(tables_blob, tables->linker, ms, s->oem_id, + s->oem_table_id); + } + } + + /* XSDT is pointed to by RSDP */ + xsdt = tables_blob->len; + build_xsdt(tables_blob, tables->linker, table_offsets, s->oem_id, + s->oem_table_id); + + /* RSDP is in FSEG memory, so allocate it separately */ + { + AcpiRsdpData rsdp_data = { + .revision = 2, + .oem_id = s->oem_id, + .xsdt_tbl_offset = &xsdt, + .rsdt_tbl_offset = NULL, + }; + build_rsdp(tables->rsdp, tables->linker, &rsdp_data); + } + + /* + * The align size is 128, warn if 64k is not enough therefore + * the align size could be resized. + */ + if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) { + warn_report("ACPI table size %u exceeds %d bytes," + " migration may not work", + tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2); + error_printf("Try removing some objects."); + } + + acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE); + + /* Clean up memory that's no longer used */ + g_array_free(table_offsets, true); +} + +static void acpi_ram_update(MemoryRegion *mr, GArray *data) +{ + uint32_t size = acpi_data_len(data); + + /* + * Make sure RAM size is correct - in case it got changed + * e.g. by migration + */ + memory_region_ram_resize(mr, size, &error_abort); + + memcpy(memory_region_get_ram_ptr(mr), data->data, size); + memory_region_set_dirty(mr, 0, size); +} + +static void virt_acpi_build_update(void *build_opaque) +{ + AcpiBuildState *build_state = build_opaque; + AcpiBuildTables tables; + + /* No state to update or already patched? Nothing to do. */ + if (!build_state || build_state->patched) { + return; + } + + build_state->patched = true; + + acpi_build_tables_init(&tables); + + virt_acpi_build(RISCV_VIRT_MACHINE(qdev_get_machine()), &tables); + + acpi_ram_update(build_state->table_mr, tables.table_data); + acpi_ram_update(build_state->rsdp_mr, tables.rsdp); + acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob); + + acpi_build_tables_cleanup(&tables, true); +} + +static void virt_acpi_build_reset(void *build_opaque) +{ + AcpiBuildState *build_state = build_opaque; + build_state->patched = false; +} + +static const VMStateDescription vmstate_virt_acpi_build = { + .name = "virt_acpi_build", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(patched, AcpiBuildState), + VMSTATE_END_OF_LIST() + }, +}; + +void virt_acpi_setup(RISCVVirtState *s) +{ + AcpiBuildTables tables; + AcpiBuildState *build_state; + + build_state = g_malloc0(sizeof *build_state); + + acpi_build_tables_init(&tables); + virt_acpi_build(s, &tables); + + /* Now expose it all to Guest */ + build_state->table_mr = acpi_add_rom_blob(virt_acpi_build_update, + build_state, tables.table_data, + ACPI_BUILD_TABLE_FILE); + assert(build_state->table_mr != NULL); + + build_state->linker_mr = acpi_add_rom_blob(virt_acpi_build_update, + build_state, + tables.linker->cmd_blob, + ACPI_BUILD_LOADER_FILE); + + build_state->rsdp_mr = acpi_add_rom_blob(virt_acpi_build_update, + build_state, tables.rsdp, + ACPI_BUILD_RSDP_FILE); + + qemu_register_reset(virt_acpi_build_reset, build_state); + virt_acpi_build_reset(build_state); + vmstate_register(NULL, 0, &vmstate_virt_acpi_build, build_state); + + /* + * Clean up tables but don't free the memory: we track it + * in build_state. + */ + acpi_build_tables_cleanup(&tables, false); +} diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 293e9c95b7..d171e74f7b 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/error-report.h" +#include "qemu/guest-random.h" #include "qapi/error.h" #include "hw/boards.h" #include "hw/loader.h" @@ -29,46 +30,42 @@ #include "hw/char/serial.h" #include "target/riscv/cpu.h" #include "hw/core/sysbus-fdt.h" +#include "target/riscv/pmu.h" #include "hw/riscv/riscv_hart.h" #include "hw/riscv/virt.h" #include "hw/riscv/boot.h" #include "hw/riscv/numa.h" +#include "kvm/kvm_riscv.h" +#include "hw/firmware/smbios.h" #include "hw/intc/riscv_aclint.h" #include "hw/intc/riscv_aplic.h" -#include "hw/intc/riscv_imsic.h" #include "hw/intc/sifive_plic.h" #include "hw/misc/sifive_test.h" #include "hw/platform-bus.h" #include "chardev/char.h" #include "sysemu/device_tree.h" #include "sysemu/sysemu.h" +#include "sysemu/tcg.h" #include "sysemu/kvm.h" #include "sysemu/tpm.h" +#include "sysemu/qtest.h" #include "hw/pci/pci.h" #include "hw/pci-host/gpex.h" #include "hw/display/ramfb.h" +#include "hw/acpi/aml-build.h" +#include "qapi/qapi-visit-common.h" +#include "hw/virtio/virtio-iommu.h" -/* - * The virt machine physical address space used by some of the devices - * namely ACLINT, PLIC, APLIC, and IMSIC depend on number of Sockets, - * number of CPUs, and number of IMSIC guest files. - * - * Various limits defined by VIRT_SOCKETS_MAX_BITS, VIRT_CPUS_MAX_BITS, - * and VIRT_IRQCHIP_MAX_GUESTS_BITS are tuned for maximum utilization - * of virt machine physical address space. - */ +/* KVM AIA only supports APLIC MSI. APLIC Wired is always emulated by QEMU. */ +static bool virt_use_kvm_aia(RISCVVirtState *s) +{ + return kvm_irqchip_in_kernel() && s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC; +} -#define VIRT_IMSIC_GROUP_MAX_SIZE (1U << IMSIC_MMIO_GROUP_MIN_SHIFT) -#if VIRT_IMSIC_GROUP_MAX_SIZE < \ - IMSIC_GROUP_SIZE(VIRT_CPUS_MAX_BITS, VIRT_IRQCHIP_MAX_GUESTS_BITS) -#error "Can't accomodate single IMSIC group in address space" -#endif - -#define VIRT_IMSIC_MAX_SIZE (VIRT_SOCKETS_MAX * \ - VIRT_IMSIC_GROUP_MAX_SIZE) -#if 0x4000000 < VIRT_IMSIC_MAX_SIZE -#error "Can't accomodate all IMSIC groups in address space" -#endif +static bool virt_aclint_allowed(void) +{ + return tcg_enabled() || qtest_enabled(); +} static const MemMapEntry virt_memmap[] = { [VIRT_DEBUG] = { 0x0, 0x100 }, @@ -221,84 +218,91 @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename, static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, char *clust_name, uint32_t *phandle, - bool is_32_bit, uint32_t *intc_phandles) + uint32_t *intc_phandles) { int cpu; uint32_t cpu_phandle; - MachineState *mc = MACHINE(s); - char *name, *cpu_name, *core_name, *intc_name; + MachineState *ms = MACHINE(s); + bool is_32_bit = riscv_is_32bit(&s->soc[0]); + uint8_t satp_mode_max; for (cpu = s->soc[socket].num_harts - 1; cpu >= 0; cpu--) { + RISCVCPU *cpu_ptr = &s->soc[socket].harts[cpu]; + g_autofree char *cpu_name = NULL; + g_autofree char *core_name = NULL; + g_autofree char *intc_name = NULL; + g_autofree char *sv_name = NULL; + cpu_phandle = (*phandle)++; cpu_name = g_strdup_printf("/cpus/cpu@%d", s->soc[socket].hartid_base + cpu); - qemu_fdt_add_subnode(mc->fdt, cpu_name); - if (riscv_feature(&s->soc[socket].harts[cpu].env, - RISCV_FEATURE_MMU)) { - qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type", - (is_32_bit) ? "riscv,sv32" : "riscv,sv48"); - } else { - qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type", - "riscv,none"); + qemu_fdt_add_subnode(ms->fdt, cpu_name); + + if (cpu_ptr->cfg.satp_mode.supported != 0) { + satp_mode_max = satp_mode_max_from_map(cpu_ptr->cfg.satp_mode.map); + sv_name = g_strdup_printf("riscv,%s", + satp_mode_str(satp_mode_max, is_32_bit)); + qemu_fdt_setprop_string(ms->fdt, cpu_name, "mmu-type", sv_name); } - name = riscv_isa_string(&s->soc[socket].harts[cpu]); - qemu_fdt_setprop_string(mc->fdt, cpu_name, "riscv,isa", name); - g_free(name); - qemu_fdt_setprop_string(mc->fdt, cpu_name, "compatible", "riscv"); - qemu_fdt_setprop_string(mc->fdt, cpu_name, "status", "okay"); - qemu_fdt_setprop_cell(mc->fdt, cpu_name, "reg", + + riscv_isa_write_fdt(cpu_ptr, ms->fdt, cpu_name); + + if (cpu_ptr->cfg.ext_zicbom) { + qemu_fdt_setprop_cell(ms->fdt, cpu_name, "riscv,cbom-block-size", + cpu_ptr->cfg.cbom_blocksize); + } + + if (cpu_ptr->cfg.ext_zicboz) { + qemu_fdt_setprop_cell(ms->fdt, cpu_name, "riscv,cboz-block-size", + cpu_ptr->cfg.cboz_blocksize); + } + + if (cpu_ptr->cfg.ext_zicbop) { + qemu_fdt_setprop_cell(ms->fdt, cpu_name, "riscv,cbop-block-size", + cpu_ptr->cfg.cbop_blocksize); + } + + qemu_fdt_setprop_string(ms->fdt, cpu_name, "compatible", "riscv"); + qemu_fdt_setprop_string(ms->fdt, cpu_name, "status", "okay"); + qemu_fdt_setprop_cell(ms->fdt, cpu_name, "reg", s->soc[socket].hartid_base + cpu); - qemu_fdt_setprop_string(mc->fdt, cpu_name, "device_type", "cpu"); - riscv_socket_fdt_write_id(mc, mc->fdt, cpu_name, socket); - qemu_fdt_setprop_cell(mc->fdt, cpu_name, "phandle", cpu_phandle); + qemu_fdt_setprop_string(ms->fdt, cpu_name, "device_type", "cpu"); + riscv_socket_fdt_write_id(ms, cpu_name, socket); + qemu_fdt_setprop_cell(ms->fdt, cpu_name, "phandle", cpu_phandle); intc_phandles[cpu] = (*phandle)++; intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name); - qemu_fdt_add_subnode(mc->fdt, intc_name); - qemu_fdt_setprop_cell(mc->fdt, intc_name, "phandle", + qemu_fdt_add_subnode(ms->fdt, intc_name); + qemu_fdt_setprop_cell(ms->fdt, intc_name, "phandle", intc_phandles[cpu]); - if (riscv_feature(&s->soc[socket].harts[cpu].env, - RISCV_FEATURE_AIA)) { - static const char * const compat[2] = { - "riscv,cpu-intc-aia", "riscv,cpu-intc" - }; - qemu_fdt_setprop_string_array(mc->fdt, intc_name, "compatible", - (char **)&compat, ARRAY_SIZE(compat)); - } else { - qemu_fdt_setprop_string(mc->fdt, intc_name, "compatible", - "riscv,cpu-intc"); - } - qemu_fdt_setprop(mc->fdt, intc_name, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(mc->fdt, intc_name, "#interrupt-cells", 1); + qemu_fdt_setprop_string(ms->fdt, intc_name, "compatible", + "riscv,cpu-intc"); + qemu_fdt_setprop(ms->fdt, intc_name, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, intc_name, "#interrupt-cells", 1); core_name = g_strdup_printf("%s/core%d", clust_name, cpu); - qemu_fdt_add_subnode(mc->fdt, core_name); - qemu_fdt_setprop_cell(mc->fdt, core_name, "cpu", cpu_phandle); - - g_free(core_name); - g_free(intc_name); - g_free(cpu_name); + qemu_fdt_add_subnode(ms->fdt, core_name); + qemu_fdt_setprop_cell(ms->fdt, core_name, "cpu", cpu_phandle); } } static void create_fdt_socket_memory(RISCVVirtState *s, const MemMapEntry *memmap, int socket) { - char *mem_name; + g_autofree char *mem_name = NULL; uint64_t addr, size; - MachineState *mc = MACHINE(s); + MachineState *ms = MACHINE(s); - addr = memmap[VIRT_DRAM].base + riscv_socket_mem_offset(mc, socket); - size = riscv_socket_mem_size(mc, socket); + addr = memmap[VIRT_DRAM].base + riscv_socket_mem_offset(ms, socket); + size = riscv_socket_mem_size(ms, socket); mem_name = g_strdup_printf("/memory@%lx", (long)addr); - qemu_fdt_add_subnode(mc->fdt, mem_name); - qemu_fdt_setprop_cells(mc->fdt, mem_name, "reg", + qemu_fdt_add_subnode(ms->fdt, mem_name); + qemu_fdt_setprop_cells(ms->fdt, mem_name, "reg", addr >> 32, addr, size >> 32, size); - qemu_fdt_setprop_string(mc->fdt, mem_name, "device_type", "memory"); - riscv_socket_fdt_write_id(mc, mc->fdt, mem_name, socket); - g_free(mem_name); + qemu_fdt_setprop_string(ms->fdt, mem_name, "device_type", "memory"); + riscv_socket_fdt_write_id(ms, mem_name, socket); } static void create_fdt_socket_clint(RISCVVirtState *s, @@ -306,10 +310,10 @@ static void create_fdt_socket_clint(RISCVVirtState *s, uint32_t *intc_phandles) { int cpu; - char *clint_name; - uint32_t *clint_cells; + g_autofree char *clint_name = NULL; + g_autofree uint32_t *clint_cells = NULL; unsigned long clint_addr; - MachineState *mc = MACHINE(s); + MachineState *ms = MACHINE(s); static const char * const clint_compat[2] = { "sifive,clint0", "riscv,clint0" }; @@ -325,18 +329,15 @@ static void create_fdt_socket_clint(RISCVVirtState *s, clint_addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket); clint_name = g_strdup_printf("/soc/clint@%lx", clint_addr); - qemu_fdt_add_subnode(mc->fdt, clint_name); - qemu_fdt_setprop_string_array(mc->fdt, clint_name, "compatible", + qemu_fdt_add_subnode(ms->fdt, clint_name); + qemu_fdt_setprop_string_array(ms->fdt, clint_name, "compatible", (char **)&clint_compat, ARRAY_SIZE(clint_compat)); - qemu_fdt_setprop_cells(mc->fdt, clint_name, "reg", + qemu_fdt_setprop_cells(ms->fdt, clint_name, "reg", 0x0, clint_addr, 0x0, memmap[VIRT_CLINT].size); - qemu_fdt_setprop(mc->fdt, clint_name, "interrupts-extended", + qemu_fdt_setprop(ms->fdt, clint_name, "interrupts-extended", clint_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4); - riscv_socket_fdt_write_id(mc, mc->fdt, clint_name, socket); - g_free(clint_name); - - g_free(clint_cells); + riscv_socket_fdt_write_id(ms, clint_name, socket); } static void create_fdt_socket_aclint(RISCVVirtState *s, @@ -347,10 +348,10 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, char *name; unsigned long addr, size; uint32_t aclint_cells_size; - uint32_t *aclint_mswi_cells; - uint32_t *aclint_sswi_cells; - uint32_t *aclint_mtimer_cells; - MachineState *mc = MACHINE(s); + g_autofree uint32_t *aclint_mswi_cells = NULL; + g_autofree uint32_t *aclint_sswi_cells = NULL; + g_autofree uint32_t *aclint_mtimer_cells = NULL; + MachineState *ms = MACHINE(s); aclint_mswi_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2); aclint_mtimer_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2); @@ -369,16 +370,16 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, if (s->aia_type != VIRT_AIA_TYPE_APLIC_IMSIC) { addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket); name = g_strdup_printf("/soc/mswi@%lx", addr); - qemu_fdt_add_subnode(mc->fdt, name); - qemu_fdt_setprop_string(mc->fdt, name, "compatible", + qemu_fdt_add_subnode(ms->fdt, name); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", "riscv,aclint-mswi"); - qemu_fdt_setprop_cells(mc->fdt, name, "reg", + qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, addr, 0x0, RISCV_ACLINT_SWI_SIZE); - qemu_fdt_setprop(mc->fdt, name, "interrupts-extended", + qemu_fdt_setprop(ms->fdt, name, "interrupts-extended", aclint_mswi_cells, aclint_cells_size); - qemu_fdt_setprop(mc->fdt, name, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", 0); - riscv_socket_fdt_write_id(mc, mc->fdt, name, socket); + qemu_fdt_setprop(ms->fdt, name, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, name, "#interrupt-cells", 0); + riscv_socket_fdt_write_id(ms, name, socket); g_free(name); } @@ -392,39 +393,35 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, size = memmap[VIRT_CLINT].size - RISCV_ACLINT_SWI_SIZE; } name = g_strdup_printf("/soc/mtimer@%lx", addr); - qemu_fdt_add_subnode(mc->fdt, name); - qemu_fdt_setprop_string(mc->fdt, name, "compatible", + qemu_fdt_add_subnode(ms->fdt, name); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", "riscv,aclint-mtimer"); - qemu_fdt_setprop_cells(mc->fdt, name, "reg", + qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, addr + RISCV_ACLINT_DEFAULT_MTIME, 0x0, size - RISCV_ACLINT_DEFAULT_MTIME, 0x0, addr + RISCV_ACLINT_DEFAULT_MTIMECMP, 0x0, RISCV_ACLINT_DEFAULT_MTIME); - qemu_fdt_setprop(mc->fdt, name, "interrupts-extended", + qemu_fdt_setprop(ms->fdt, name, "interrupts-extended", aclint_mtimer_cells, aclint_cells_size); - riscv_socket_fdt_write_id(mc, mc->fdt, name, socket); + riscv_socket_fdt_write_id(ms, name, socket); g_free(name); if (s->aia_type != VIRT_AIA_TYPE_APLIC_IMSIC) { addr = memmap[VIRT_ACLINT_SSWI].base + (memmap[VIRT_ACLINT_SSWI].size * socket); name = g_strdup_printf("/soc/sswi@%lx", addr); - qemu_fdt_add_subnode(mc->fdt, name); - qemu_fdt_setprop_string(mc->fdt, name, "compatible", + qemu_fdt_add_subnode(ms->fdt, name); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", "riscv,aclint-sswi"); - qemu_fdt_setprop_cells(mc->fdt, name, "reg", + qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, addr, 0x0, memmap[VIRT_ACLINT_SSWI].size); - qemu_fdt_setprop(mc->fdt, name, "interrupts-extended", + qemu_fdt_setprop(ms->fdt, name, "interrupts-extended", aclint_sswi_cells, aclint_cells_size); - qemu_fdt_setprop(mc->fdt, name, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", 0); - riscv_socket_fdt_write_id(mc, mc->fdt, name, socket); + qemu_fdt_setprop(ms->fdt, name, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, name, "#interrupt-cells", 0); + riscv_socket_fdt_write_id(ms, name, socket); g_free(name); } - - g_free(aclint_mswi_cells); - g_free(aclint_mtimer_cells); - g_free(aclint_sswi_cells); } static void create_fdt_socket_plic(RISCVVirtState *s, @@ -433,64 +430,70 @@ static void create_fdt_socket_plic(RISCVVirtState *s, uint32_t *plic_phandles) { int cpu; - char *plic_name; - uint32_t *plic_cells; + g_autofree char *plic_name = NULL; + g_autofree uint32_t *plic_cells; unsigned long plic_addr; - MachineState *mc = MACHINE(s); + MachineState *ms = MACHINE(s); static const char * const plic_compat[2] = { "sifive,plic-1.0.0", "riscv,plic0" }; + plic_phandles[socket] = (*phandle)++; + plic_addr = memmap[VIRT_PLIC].base + (memmap[VIRT_PLIC].size * socket); + plic_name = g_strdup_printf("/soc/plic@%lx", plic_addr); + qemu_fdt_add_subnode(ms->fdt, plic_name); + qemu_fdt_setprop_cell(ms->fdt, plic_name, + "#interrupt-cells", FDT_PLIC_INT_CELLS); + qemu_fdt_setprop_cell(ms->fdt, plic_name, + "#address-cells", FDT_PLIC_ADDR_CELLS); + qemu_fdt_setprop_string_array(ms->fdt, plic_name, "compatible", + (char **)&plic_compat, + ARRAY_SIZE(plic_compat)); + qemu_fdt_setprop(ms->fdt, plic_name, "interrupt-controller", NULL, 0); + if (kvm_enabled()) { plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2); - } else { - plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4); - } - for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) { - if (kvm_enabled()) { + for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) { plic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); plic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_EXT); - } else { + } + + qemu_fdt_setprop(ms->fdt, plic_name, "interrupts-extended", + plic_cells, + s->soc[socket].num_harts * sizeof(uint32_t) * 2); + } else { + plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4); + + for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) { plic_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]); plic_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT); plic_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]); plic_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT); } + + qemu_fdt_setprop(ms->fdt, plic_name, "interrupts-extended", + plic_cells, + s->soc[socket].num_harts * sizeof(uint32_t) * 4); } - plic_phandles[socket] = (*phandle)++; - plic_addr = memmap[VIRT_PLIC].base + (memmap[VIRT_PLIC].size * socket); - plic_name = g_strdup_printf("/soc/plic@%lx", plic_addr); - qemu_fdt_add_subnode(mc->fdt, plic_name); - qemu_fdt_setprop_cell(mc->fdt, plic_name, - "#interrupt-cells", FDT_PLIC_INT_CELLS); - qemu_fdt_setprop_string_array(mc->fdt, plic_name, "compatible", - (char **)&plic_compat, - ARRAY_SIZE(plic_compat)); - qemu_fdt_setprop(mc->fdt, plic_name, "interrupt-controller", NULL, 0); - qemu_fdt_setprop(mc->fdt, plic_name, "interrupts-extended", - plic_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4); - qemu_fdt_setprop_cells(mc->fdt, plic_name, "reg", + qemu_fdt_setprop_cells(ms->fdt, plic_name, "reg", 0x0, plic_addr, 0x0, memmap[VIRT_PLIC].size); - qemu_fdt_setprop_cell(mc->fdt, plic_name, "riscv,ndev", VIRTIO_NDEV); - riscv_socket_fdt_write_id(mc, mc->fdt, plic_name, socket); - qemu_fdt_setprop_cell(mc->fdt, plic_name, "phandle", + qemu_fdt_setprop_cell(ms->fdt, plic_name, "riscv,ndev", + VIRT_IRQCHIP_NUM_SOURCES - 1); + riscv_socket_fdt_write_id(ms, plic_name, socket); + qemu_fdt_setprop_cell(ms->fdt, plic_name, "phandle", plic_phandles[socket]); if (!socket) { - platform_bus_add_all_fdt_nodes(mc->fdt, plic_name, + platform_bus_add_all_fdt_nodes(ms->fdt, plic_name, memmap[VIRT_PLATFORM_BUS].base, memmap[VIRT_PLATFORM_BUS].size, VIRT_PLATFORM_BUS_IRQ); } - - g_free(plic_name); - - g_free(plic_cells); } -static uint32_t imsic_num_bits(uint32_t count) +uint32_t imsic_num_bits(uint32_t count) { uint32_t ret = 0; @@ -501,80 +504,29 @@ static uint32_t imsic_num_bits(uint32_t count) return ret; } -static void create_fdt_imsic(RISCVVirtState *s, const MemMapEntry *memmap, - uint32_t *phandle, uint32_t *intc_phandles, - uint32_t *msi_m_phandle, uint32_t *msi_s_phandle) +static void create_fdt_one_imsic(RISCVVirtState *s, hwaddr base_addr, + uint32_t *intc_phandles, uint32_t msi_phandle, + bool m_mode, uint32_t imsic_guest_bits) { int cpu, socket; - char *imsic_name; - MachineState *mc = MACHINE(s); - uint32_t imsic_max_hart_per_socket, imsic_guest_bits; - uint32_t *imsic_cells, *imsic_regs, imsic_addr, imsic_size; + g_autofree char *imsic_name = NULL; + MachineState *ms = MACHINE(s); + int socket_count = riscv_socket_count(ms); + uint32_t imsic_max_hart_per_socket, imsic_addr, imsic_size; + g_autofree uint32_t *imsic_cells = NULL; + g_autofree uint32_t *imsic_regs = NULL; - *msi_m_phandle = (*phandle)++; - *msi_s_phandle = (*phandle)++; - imsic_cells = g_new0(uint32_t, mc->smp.cpus * 2); - imsic_regs = g_new0(uint32_t, riscv_socket_count(mc) * 4); + imsic_cells = g_new0(uint32_t, ms->smp.cpus * 2); + imsic_regs = g_new0(uint32_t, socket_count * 4); - /* M-level IMSIC node */ - for (cpu = 0; cpu < mc->smp.cpus; cpu++) { + for (cpu = 0; cpu < ms->smp.cpus; cpu++) { imsic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); - imsic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_EXT); + imsic_cells[cpu * 2 + 1] = cpu_to_be32(m_mode ? IRQ_M_EXT : IRQ_S_EXT); } - imsic_max_hart_per_socket = 0; - for (socket = 0; socket < riscv_socket_count(mc); socket++) { - imsic_addr = memmap[VIRT_IMSIC_M].base + - socket * VIRT_IMSIC_GROUP_MAX_SIZE; - imsic_size = IMSIC_HART_SIZE(0) * s->soc[socket].num_harts; - imsic_regs[socket * 4 + 0] = 0; - imsic_regs[socket * 4 + 1] = cpu_to_be32(imsic_addr); - imsic_regs[socket * 4 + 2] = 0; - imsic_regs[socket * 4 + 3] = cpu_to_be32(imsic_size); - if (imsic_max_hart_per_socket < s->soc[socket].num_harts) { - imsic_max_hart_per_socket = s->soc[socket].num_harts; - } - } - imsic_name = g_strdup_printf("/soc/imsics@%lx", - (unsigned long)memmap[VIRT_IMSIC_M].base); - qemu_fdt_add_subnode(mc->fdt, imsic_name); - qemu_fdt_setprop_string(mc->fdt, imsic_name, "compatible", - "riscv,imsics"); - qemu_fdt_setprop_cell(mc->fdt, imsic_name, "#interrupt-cells", - FDT_IMSIC_INT_CELLS); - qemu_fdt_setprop(mc->fdt, imsic_name, "interrupt-controller", - NULL, 0); - qemu_fdt_setprop(mc->fdt, imsic_name, "msi-controller", - NULL, 0); - qemu_fdt_setprop(mc->fdt, imsic_name, "interrupts-extended", - imsic_cells, mc->smp.cpus * sizeof(uint32_t) * 2); - qemu_fdt_setprop(mc->fdt, imsic_name, "reg", imsic_regs, - riscv_socket_count(mc) * sizeof(uint32_t) * 4); - qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,num-ids", - VIRT_IRQCHIP_NUM_MSIS); - qemu_fdt_setprop_cells(mc->fdt, imsic_name, "riscv,ipi-id", - VIRT_IRQCHIP_IPI_MSI); - if (riscv_socket_count(mc) > 1) { - qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,hart-index-bits", - imsic_num_bits(imsic_max_hart_per_socket)); - qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,group-index-bits", - imsic_num_bits(riscv_socket_count(mc))); - qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,group-index-shift", - IMSIC_MMIO_GROUP_MIN_SHIFT); - } - qemu_fdt_setprop_cell(mc->fdt, imsic_name, "phandle", *msi_m_phandle); - g_free(imsic_name); - - /* S-level IMSIC node */ - for (cpu = 0; cpu < mc->smp.cpus; cpu++) { - imsic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); - imsic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_EXT); - } - imsic_guest_bits = imsic_num_bits(s->aia_guests + 1); imsic_max_hart_per_socket = 0; - for (socket = 0; socket < riscv_socket_count(mc); socket++) { - imsic_addr = memmap[VIRT_IMSIC_S].base + - socket * VIRT_IMSIC_GROUP_MAX_SIZE; + for (socket = 0; socket < socket_count; socket++) { + imsic_addr = base_addr + socket * VIRT_IMSIC_GROUP_MAX_SIZE; imsic_size = IMSIC_HART_SIZE(imsic_guest_bits) * s->soc[socket].num_harts; imsic_regs[socket * 4 + 0] = 0; @@ -585,42 +537,104 @@ static void create_fdt_imsic(RISCVVirtState *s, const MemMapEntry *memmap, imsic_max_hart_per_socket = s->soc[socket].num_harts; } } - imsic_name = g_strdup_printf("/soc/imsics@%lx", - (unsigned long)memmap[VIRT_IMSIC_S].base); - qemu_fdt_add_subnode(mc->fdt, imsic_name); - qemu_fdt_setprop_string(mc->fdt, imsic_name, "compatible", - "riscv,imsics"); - qemu_fdt_setprop_cell(mc->fdt, imsic_name, "#interrupt-cells", - FDT_IMSIC_INT_CELLS); - qemu_fdt_setprop(mc->fdt, imsic_name, "interrupt-controller", - NULL, 0); - qemu_fdt_setprop(mc->fdt, imsic_name, "msi-controller", - NULL, 0); - qemu_fdt_setprop(mc->fdt, imsic_name, "interrupts-extended", - imsic_cells, mc->smp.cpus * sizeof(uint32_t) * 2); - qemu_fdt_setprop(mc->fdt, imsic_name, "reg", imsic_regs, - riscv_socket_count(mc) * sizeof(uint32_t) * 4); - qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,num-ids", - VIRT_IRQCHIP_NUM_MSIS); - qemu_fdt_setprop_cells(mc->fdt, imsic_name, "riscv,ipi-id", - VIRT_IRQCHIP_IPI_MSI); - if (imsic_guest_bits) { - qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,guest-index-bits", - imsic_guest_bits); - } - if (riscv_socket_count(mc) > 1) { - qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,hart-index-bits", - imsic_num_bits(imsic_max_hart_per_socket)); - qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,group-index-bits", - imsic_num_bits(riscv_socket_count(mc))); - qemu_fdt_setprop_cell(mc->fdt, imsic_name, "riscv,group-index-shift", - IMSIC_MMIO_GROUP_MIN_SHIFT); - } - qemu_fdt_setprop_cell(mc->fdt, imsic_name, "phandle", *msi_s_phandle); - g_free(imsic_name); - g_free(imsic_regs); - g_free(imsic_cells); + imsic_name = g_strdup_printf("/soc/imsics@%lx", (unsigned long)base_addr); + qemu_fdt_add_subnode(ms->fdt, imsic_name); + qemu_fdt_setprop_string(ms->fdt, imsic_name, "compatible", "riscv,imsics"); + qemu_fdt_setprop_cell(ms->fdt, imsic_name, "#interrupt-cells", + FDT_IMSIC_INT_CELLS); + qemu_fdt_setprop(ms->fdt, imsic_name, "interrupt-controller", NULL, 0); + qemu_fdt_setprop(ms->fdt, imsic_name, "msi-controller", NULL, 0); + qemu_fdt_setprop(ms->fdt, imsic_name, "interrupts-extended", + imsic_cells, ms->smp.cpus * sizeof(uint32_t) * 2); + qemu_fdt_setprop(ms->fdt, imsic_name, "reg", imsic_regs, + socket_count * sizeof(uint32_t) * 4); + qemu_fdt_setprop_cell(ms->fdt, imsic_name, "riscv,num-ids", + VIRT_IRQCHIP_NUM_MSIS); + + if (imsic_guest_bits) { + qemu_fdt_setprop_cell(ms->fdt, imsic_name, "riscv,guest-index-bits", + imsic_guest_bits); + } + + if (socket_count > 1) { + qemu_fdt_setprop_cell(ms->fdt, imsic_name, "riscv,hart-index-bits", + imsic_num_bits(imsic_max_hart_per_socket)); + qemu_fdt_setprop_cell(ms->fdt, imsic_name, "riscv,group-index-bits", + imsic_num_bits(socket_count)); + qemu_fdt_setprop_cell(ms->fdt, imsic_name, "riscv,group-index-shift", + IMSIC_MMIO_GROUP_MIN_SHIFT); + } + qemu_fdt_setprop_cell(ms->fdt, imsic_name, "phandle", msi_phandle); +} + +static void create_fdt_imsic(RISCVVirtState *s, const MemMapEntry *memmap, + uint32_t *phandle, uint32_t *intc_phandles, + uint32_t *msi_m_phandle, uint32_t *msi_s_phandle) +{ + *msi_m_phandle = (*phandle)++; + *msi_s_phandle = (*phandle)++; + + if (!kvm_enabled()) { + /* M-level IMSIC node */ + create_fdt_one_imsic(s, memmap[VIRT_IMSIC_M].base, intc_phandles, + *msi_m_phandle, true, 0); + } + + /* S-level IMSIC node */ + create_fdt_one_imsic(s, memmap[VIRT_IMSIC_S].base, intc_phandles, + *msi_s_phandle, false, + imsic_num_bits(s->aia_guests + 1)); + +} + +static void create_fdt_one_aplic(RISCVVirtState *s, int socket, + unsigned long aplic_addr, uint32_t aplic_size, + uint32_t msi_phandle, + uint32_t *intc_phandles, + uint32_t aplic_phandle, + uint32_t aplic_child_phandle, + bool m_mode, int num_harts) +{ + int cpu; + g_autofree char *aplic_name = NULL; + g_autofree uint32_t *aplic_cells = g_new0(uint32_t, num_harts * 2); + MachineState *ms = MACHINE(s); + + for (cpu = 0; cpu < num_harts; cpu++) { + aplic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); + aplic_cells[cpu * 2 + 1] = cpu_to_be32(m_mode ? IRQ_M_EXT : IRQ_S_EXT); + } + + aplic_name = g_strdup_printf("/soc/aplic@%lx", aplic_addr); + qemu_fdt_add_subnode(ms->fdt, aplic_name); + qemu_fdt_setprop_string(ms->fdt, aplic_name, "compatible", "riscv,aplic"); + qemu_fdt_setprop_cell(ms->fdt, aplic_name, + "#interrupt-cells", FDT_APLIC_INT_CELLS); + qemu_fdt_setprop(ms->fdt, aplic_name, "interrupt-controller", NULL, 0); + + if (s->aia_type == VIRT_AIA_TYPE_APLIC) { + qemu_fdt_setprop(ms->fdt, aplic_name, "interrupts-extended", + aplic_cells, num_harts * sizeof(uint32_t) * 2); + } else { + qemu_fdt_setprop_cell(ms->fdt, aplic_name, "msi-parent", msi_phandle); + } + + qemu_fdt_setprop_cells(ms->fdt, aplic_name, "reg", + 0x0, aplic_addr, 0x0, aplic_size); + qemu_fdt_setprop_cell(ms->fdt, aplic_name, "riscv,num-sources", + VIRT_IRQCHIP_NUM_SOURCES); + + if (aplic_child_phandle) { + qemu_fdt_setprop_cell(ms->fdt, aplic_name, "riscv,children", + aplic_child_phandle); + qemu_fdt_setprop_cells(ms->fdt, aplic_name, "riscv,delegate", + aplic_child_phandle, 0x1, + VIRT_IRQCHIP_NUM_SOURCES); + } + + riscv_socket_fdt_write_id(ms, aplic_name, socket); + qemu_fdt_setprop_cell(ms->fdt, aplic_name, "phandle", aplic_phandle); } static void create_fdt_socket_aplic(RISCVVirtState *s, @@ -629,135 +643,102 @@ static void create_fdt_socket_aplic(RISCVVirtState *s, uint32_t msi_s_phandle, uint32_t *phandle, uint32_t *intc_phandles, - uint32_t *aplic_phandles) + uint32_t *aplic_phandles, + int num_harts) { - int cpu; - char *aplic_name; - uint32_t *aplic_cells; + g_autofree char *aplic_name = NULL; unsigned long aplic_addr; - MachineState *mc = MACHINE(s); + MachineState *ms = MACHINE(s); uint32_t aplic_m_phandle, aplic_s_phandle; aplic_m_phandle = (*phandle)++; aplic_s_phandle = (*phandle)++; - aplic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2); - /* M-level APLIC node */ - for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) { - aplic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); - aplic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_EXT); + if (!kvm_enabled()) { + /* M-level APLIC node */ + aplic_addr = memmap[VIRT_APLIC_M].base + + (memmap[VIRT_APLIC_M].size * socket); + create_fdt_one_aplic(s, socket, aplic_addr, memmap[VIRT_APLIC_M].size, + msi_m_phandle, intc_phandles, + aplic_m_phandle, aplic_s_phandle, + true, num_harts); } - aplic_addr = memmap[VIRT_APLIC_M].base + - (memmap[VIRT_APLIC_M].size * socket); - aplic_name = g_strdup_printf("/soc/aplic@%lx", aplic_addr); - qemu_fdt_add_subnode(mc->fdt, aplic_name); - qemu_fdt_setprop_string(mc->fdt, aplic_name, "compatible", "riscv,aplic"); - qemu_fdt_setprop_cell(mc->fdt, aplic_name, - "#interrupt-cells", FDT_APLIC_INT_CELLS); - qemu_fdt_setprop(mc->fdt, aplic_name, "interrupt-controller", NULL, 0); - if (s->aia_type == VIRT_AIA_TYPE_APLIC) { - qemu_fdt_setprop(mc->fdt, aplic_name, "interrupts-extended", - aplic_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 2); - } else { - qemu_fdt_setprop_cell(mc->fdt, aplic_name, "msi-parent", - msi_m_phandle); - } - qemu_fdt_setprop_cells(mc->fdt, aplic_name, "reg", - 0x0, aplic_addr, 0x0, memmap[VIRT_APLIC_M].size); - qemu_fdt_setprop_cell(mc->fdt, aplic_name, "riscv,num-sources", - VIRT_IRQCHIP_NUM_SOURCES); - qemu_fdt_setprop_cell(mc->fdt, aplic_name, "riscv,children", - aplic_s_phandle); - qemu_fdt_setprop_cells(mc->fdt, aplic_name, "riscv,delegate", - aplic_s_phandle, 0x1, VIRT_IRQCHIP_NUM_SOURCES); - riscv_socket_fdt_write_id(mc, mc->fdt, aplic_name, socket); - qemu_fdt_setprop_cell(mc->fdt, aplic_name, "phandle", aplic_m_phandle); - g_free(aplic_name); /* S-level APLIC node */ - for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) { - aplic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]); - aplic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_EXT); - } aplic_addr = memmap[VIRT_APLIC_S].base + (memmap[VIRT_APLIC_S].size * socket); + create_fdt_one_aplic(s, socket, aplic_addr, memmap[VIRT_APLIC_S].size, + msi_s_phandle, intc_phandles, + aplic_s_phandle, 0, + false, num_harts); + aplic_name = g_strdup_printf("/soc/aplic@%lx", aplic_addr); - qemu_fdt_add_subnode(mc->fdt, aplic_name); - qemu_fdt_setprop_string(mc->fdt, aplic_name, "compatible", "riscv,aplic"); - qemu_fdt_setprop_cell(mc->fdt, aplic_name, - "#interrupt-cells", FDT_APLIC_INT_CELLS); - qemu_fdt_setprop(mc->fdt, aplic_name, "interrupt-controller", NULL, 0); - if (s->aia_type == VIRT_AIA_TYPE_APLIC) { - qemu_fdt_setprop(mc->fdt, aplic_name, "interrupts-extended", - aplic_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 2); - } else { - qemu_fdt_setprop_cell(mc->fdt, aplic_name, "msi-parent", - msi_s_phandle); - } - qemu_fdt_setprop_cells(mc->fdt, aplic_name, "reg", - 0x0, aplic_addr, 0x0, memmap[VIRT_APLIC_S].size); - qemu_fdt_setprop_cell(mc->fdt, aplic_name, "riscv,num-sources", - VIRT_IRQCHIP_NUM_SOURCES); - riscv_socket_fdt_write_id(mc, mc->fdt, aplic_name, socket); - qemu_fdt_setprop_cell(mc->fdt, aplic_name, "phandle", aplic_s_phandle); if (!socket) { - platform_bus_add_all_fdt_nodes(mc->fdt, aplic_name, + platform_bus_add_all_fdt_nodes(ms->fdt, aplic_name, memmap[VIRT_PLATFORM_BUS].base, memmap[VIRT_PLATFORM_BUS].size, VIRT_PLATFORM_BUS_IRQ); } - g_free(aplic_name); - - g_free(aplic_cells); aplic_phandles[socket] = aplic_s_phandle; } +static void create_fdt_pmu(RISCVVirtState *s) +{ + g_autofree char *pmu_name = g_strdup_printf("/pmu"); + MachineState *ms = MACHINE(s); + RISCVCPU hart = s->soc[0].harts[0]; + + qemu_fdt_add_subnode(ms->fdt, pmu_name); + qemu_fdt_setprop_string(ms->fdt, pmu_name, "compatible", "riscv,pmu"); + riscv_pmu_generate_fdt_node(ms->fdt, hart.pmu_avail_ctrs, pmu_name); +} + static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, - bool is_32_bit, uint32_t *phandle, + uint32_t *phandle, uint32_t *irq_mmio_phandle, uint32_t *irq_pcie_phandle, uint32_t *irq_virtio_phandle, uint32_t *msi_pcie_phandle) { - char *clust_name; int socket, phandle_pos; - MachineState *mc = MACHINE(s); + MachineState *ms = MACHINE(s); uint32_t msi_m_phandle = 0, msi_s_phandle = 0; - uint32_t *intc_phandles, xplic_phandles[MAX_NODES]; + uint32_t xplic_phandles[MAX_NODES]; + g_autofree uint32_t *intc_phandles = NULL; + int socket_count = riscv_socket_count(ms); - qemu_fdt_add_subnode(mc->fdt, "/cpus"); - qemu_fdt_setprop_cell(mc->fdt, "/cpus", "timebase-frequency", + qemu_fdt_add_subnode(ms->fdt, "/cpus"); + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "timebase-frequency", + kvm_enabled() ? + kvm_riscv_get_timebase_frequency(first_cpu) : RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ); - qemu_fdt_setprop_cell(mc->fdt, "/cpus", "#size-cells", 0x0); - qemu_fdt_setprop_cell(mc->fdt, "/cpus", "#address-cells", 0x1); - qemu_fdt_add_subnode(mc->fdt, "/cpus/cpu-map"); + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0); + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1); + qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map"); - intc_phandles = g_new0(uint32_t, mc->smp.cpus); + intc_phandles = g_new0(uint32_t, ms->smp.cpus); - phandle_pos = mc->smp.cpus; - for (socket = (riscv_socket_count(mc) - 1); socket >= 0; socket--) { + phandle_pos = ms->smp.cpus; + for (socket = (socket_count - 1); socket >= 0; socket--) { + g_autofree char *clust_name = NULL; phandle_pos -= s->soc[socket].num_harts; clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket); - qemu_fdt_add_subnode(mc->fdt, clust_name); + qemu_fdt_add_subnode(ms->fdt, clust_name); create_fdt_socket_cpus(s, socket, clust_name, phandle, - is_32_bit, &intc_phandles[phandle_pos]); + &intc_phandles[phandle_pos]); create_fdt_socket_memory(s, memmap, socket); - g_free(clust_name); - - if (!kvm_enabled()) { - if (s->have_aclint) { - create_fdt_socket_aclint(s, memmap, socket, - &intc_phandles[phandle_pos]); - } else { - create_fdt_socket_clint(s, memmap, socket, - &intc_phandles[phandle_pos]); - } + if (virt_aclint_allowed() && s->have_aclint) { + create_fdt_socket_aclint(s, memmap, socket, + &intc_phandles[phandle_pos]); + } else if (tcg_enabled()) { + create_fdt_socket_clint(s, memmap, socket, + &intc_phandles[phandle_pos]); } } @@ -767,65 +748,79 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, *msi_pcie_phandle = msi_s_phandle; } - phandle_pos = mc->smp.cpus; - for (socket = (riscv_socket_count(mc) - 1); socket >= 0; socket--) { - phandle_pos -= s->soc[socket].num_harts; + /* KVM AIA only has one APLIC instance */ + if (kvm_enabled() && virt_use_kvm_aia(s)) { + create_fdt_socket_aplic(s, memmap, 0, + msi_m_phandle, msi_s_phandle, phandle, + &intc_phandles[0], xplic_phandles, + ms->smp.cpus); + } else { + phandle_pos = ms->smp.cpus; + for (socket = (socket_count - 1); socket >= 0; socket--) { + phandle_pos -= s->soc[socket].num_harts; - if (s->aia_type == VIRT_AIA_TYPE_NONE) { - create_fdt_socket_plic(s, memmap, socket, phandle, - &intc_phandles[phandle_pos], xplic_phandles); - } else { - create_fdt_socket_aplic(s, memmap, socket, - msi_m_phandle, msi_s_phandle, phandle, - &intc_phandles[phandle_pos], xplic_phandles); + if (s->aia_type == VIRT_AIA_TYPE_NONE) { + create_fdt_socket_plic(s, memmap, socket, phandle, + &intc_phandles[phandle_pos], + xplic_phandles); + } else { + create_fdt_socket_aplic(s, memmap, socket, + msi_m_phandle, msi_s_phandle, phandle, + &intc_phandles[phandle_pos], + xplic_phandles, + s->soc[socket].num_harts); + } } } - g_free(intc_phandles); - - for (socket = 0; socket < riscv_socket_count(mc); socket++) { - if (socket == 0) { - *irq_mmio_phandle = xplic_phandles[socket]; - *irq_virtio_phandle = xplic_phandles[socket]; - *irq_pcie_phandle = xplic_phandles[socket]; - } - if (socket == 1) { - *irq_virtio_phandle = xplic_phandles[socket]; - *irq_pcie_phandle = xplic_phandles[socket]; - } - if (socket == 2) { - *irq_pcie_phandle = xplic_phandles[socket]; + if (kvm_enabled() && virt_use_kvm_aia(s)) { + *irq_mmio_phandle = xplic_phandles[0]; + *irq_virtio_phandle = xplic_phandles[0]; + *irq_pcie_phandle = xplic_phandles[0]; + } else { + for (socket = 0; socket < socket_count; socket++) { + if (socket == 0) { + *irq_mmio_phandle = xplic_phandles[socket]; + *irq_virtio_phandle = xplic_phandles[socket]; + *irq_pcie_phandle = xplic_phandles[socket]; + } + if (socket == 1) { + *irq_virtio_phandle = xplic_phandles[socket]; + *irq_pcie_phandle = xplic_phandles[socket]; + } + if (socket == 2) { + *irq_pcie_phandle = xplic_phandles[socket]; + } } } - riscv_socket_fdt_write_distance_matrix(mc, mc->fdt); + riscv_socket_fdt_write_distance_matrix(ms); } static void create_fdt_virtio(RISCVVirtState *s, const MemMapEntry *memmap, uint32_t irq_virtio_phandle) { int i; - char *name; - MachineState *mc = MACHINE(s); + MachineState *ms = MACHINE(s); for (i = 0; i < VIRTIO_COUNT; i++) { - name = g_strdup_printf("/soc/virtio_mmio@%lx", + g_autofree char *name = g_strdup_printf("/soc/virtio_mmio@%lx", (long)(memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size)); - qemu_fdt_add_subnode(mc->fdt, name); - qemu_fdt_setprop_string(mc->fdt, name, "compatible", "virtio,mmio"); - qemu_fdt_setprop_cells(mc->fdt, name, "reg", + + qemu_fdt_add_subnode(ms->fdt, name); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", "virtio,mmio"); + qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size, 0x0, memmap[VIRT_VIRTIO].size); - qemu_fdt_setprop_cell(mc->fdt, name, "interrupt-parent", + qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_virtio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { - qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", + qemu_fdt_setprop_cell(ms->fdt, name, "interrupts", VIRTIO_IRQ + i); } else { - qemu_fdt_setprop_cells(mc->fdt, name, "interrupts", + qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", VIRTIO_IRQ + i, 0x4); } - g_free(name); } } @@ -833,30 +828,29 @@ static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, uint32_t irq_pcie_phandle, uint32_t msi_pcie_phandle) { - char *name; - MachineState *mc = MACHINE(s); + g_autofree char *name = NULL; + MachineState *ms = MACHINE(s); name = g_strdup_printf("/soc/pci@%lx", (long) memmap[VIRT_PCIE_ECAM].base); - qemu_fdt_add_subnode(mc->fdt, name); - qemu_fdt_setprop_cell(mc->fdt, name, "#address-cells", + qemu_fdt_setprop_cell(ms->fdt, name, "#address-cells", FDT_PCI_ADDR_CELLS); - qemu_fdt_setprop_cell(mc->fdt, name, "#interrupt-cells", + qemu_fdt_setprop_cell(ms->fdt, name, "#interrupt-cells", FDT_PCI_INT_CELLS); - qemu_fdt_setprop_cell(mc->fdt, name, "#size-cells", 0x2); - qemu_fdt_setprop_string(mc->fdt, name, "compatible", + qemu_fdt_setprop_cell(ms->fdt, name, "#size-cells", 0x2); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", "pci-host-ecam-generic"); - qemu_fdt_setprop_string(mc->fdt, name, "device_type", "pci"); - qemu_fdt_setprop_cell(mc->fdt, name, "linux,pci-domain", 0); - qemu_fdt_setprop_cells(mc->fdt, name, "bus-range", 0, + qemu_fdt_setprop_string(ms->fdt, name, "device_type", "pci"); + qemu_fdt_setprop_cell(ms->fdt, name, "linux,pci-domain", 0); + qemu_fdt_setprop_cells(ms->fdt, name, "bus-range", 0, memmap[VIRT_PCIE_ECAM].size / PCIE_MMCFG_SIZE_MIN - 1); - qemu_fdt_setprop(mc->fdt, name, "dma-coherent", NULL, 0); + qemu_fdt_setprop(ms->fdt, name, "dma-coherent", NULL, 0); if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { - qemu_fdt_setprop_cell(mc->fdt, name, "msi-parent", msi_pcie_phandle); + qemu_fdt_setprop_cell(ms->fdt, name, "msi-parent", msi_pcie_phandle); } - qemu_fdt_setprop_cells(mc->fdt, name, "reg", 0, + qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0, memmap[VIRT_PCIE_ECAM].base, 0, memmap[VIRT_PCIE_ECAM].size); - qemu_fdt_setprop_sized_cells(mc->fdt, name, "ranges", + qemu_fdt_setprop_sized_cells(ms->fdt, name, "ranges", 1, FDT_PCI_RANGE_IOPORT, 2, 0, 2, memmap[VIRT_PCIE_PIO].base, 2, memmap[VIRT_PCIE_PIO].size, 1, FDT_PCI_RANGE_MMIO, @@ -866,8 +860,7 @@ static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, 2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.size); - create_pcie_irq_map(s, mc->fdt, name, irq_pcie_phandle); - g_free(name); + create_pcie_irq_map(s, ms->fdt, name, irq_pcie_phandle); } static void create_fdt_reset(RISCVVirtState *s, const MemMapEntry *memmap, @@ -875,177 +868,246 @@ static void create_fdt_reset(RISCVVirtState *s, const MemMapEntry *memmap, { char *name; uint32_t test_phandle; - MachineState *mc = MACHINE(s); + MachineState *ms = MACHINE(s); test_phandle = (*phandle)++; name = g_strdup_printf("/soc/test@%lx", (long)memmap[VIRT_TEST].base); - qemu_fdt_add_subnode(mc->fdt, name); + qemu_fdt_add_subnode(ms->fdt, name); { static const char * const compat[3] = { "sifive,test1", "sifive,test0", "syscon" }; - qemu_fdt_setprop_string_array(mc->fdt, name, "compatible", + qemu_fdt_setprop_string_array(ms->fdt, name, "compatible", (char **)&compat, ARRAY_SIZE(compat)); } - qemu_fdt_setprop_cells(mc->fdt, name, "reg", + qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, memmap[VIRT_TEST].base, 0x0, memmap[VIRT_TEST].size); - qemu_fdt_setprop_cell(mc->fdt, name, "phandle", test_phandle); - test_phandle = qemu_fdt_get_phandle(mc->fdt, name); + qemu_fdt_setprop_cell(ms->fdt, name, "phandle", test_phandle); + test_phandle = qemu_fdt_get_phandle(ms->fdt, name); g_free(name); - name = g_strdup_printf("/soc/reboot"); - qemu_fdt_add_subnode(mc->fdt, name); - qemu_fdt_setprop_string(mc->fdt, name, "compatible", "syscon-reboot"); - qemu_fdt_setprop_cell(mc->fdt, name, "regmap", test_phandle); - qemu_fdt_setprop_cell(mc->fdt, name, "offset", 0x0); - qemu_fdt_setprop_cell(mc->fdt, name, "value", FINISHER_RESET); + name = g_strdup_printf("/reboot"); + qemu_fdt_add_subnode(ms->fdt, name); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-reboot"); + qemu_fdt_setprop_cell(ms->fdt, name, "regmap", test_phandle); + qemu_fdt_setprop_cell(ms->fdt, name, "offset", 0x0); + qemu_fdt_setprop_cell(ms->fdt, name, "value", FINISHER_RESET); g_free(name); - name = g_strdup_printf("/soc/poweroff"); - qemu_fdt_add_subnode(mc->fdt, name); - qemu_fdt_setprop_string(mc->fdt, name, "compatible", "syscon-poweroff"); - qemu_fdt_setprop_cell(mc->fdt, name, "regmap", test_phandle); - qemu_fdt_setprop_cell(mc->fdt, name, "offset", 0x0); - qemu_fdt_setprop_cell(mc->fdt, name, "value", FINISHER_PASS); + name = g_strdup_printf("/poweroff"); + qemu_fdt_add_subnode(ms->fdt, name); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-poweroff"); + qemu_fdt_setprop_cell(ms->fdt, name, "regmap", test_phandle); + qemu_fdt_setprop_cell(ms->fdt, name, "offset", 0x0); + qemu_fdt_setprop_cell(ms->fdt, name, "value", FINISHER_PASS); g_free(name); } static void create_fdt_uart(RISCVVirtState *s, const MemMapEntry *memmap, uint32_t irq_mmio_phandle) { - char *name; - MachineState *mc = MACHINE(s); + g_autofree char *name = NULL; + MachineState *ms = MACHINE(s); - name = g_strdup_printf("/soc/uart@%lx", (long)memmap[VIRT_UART0].base); - qemu_fdt_add_subnode(mc->fdt, name); - qemu_fdt_setprop_string(mc->fdt, name, "compatible", "ns16550a"); - qemu_fdt_setprop_cells(mc->fdt, name, "reg", + name = g_strdup_printf("/soc/serial@%lx", (long)memmap[VIRT_UART0].base); + qemu_fdt_add_subnode(ms->fdt, name); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", "ns16550a"); + qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, memmap[VIRT_UART0].base, 0x0, memmap[VIRT_UART0].size); - qemu_fdt_setprop_cell(mc->fdt, name, "clock-frequency", 3686400); - qemu_fdt_setprop_cell(mc->fdt, name, "interrupt-parent", irq_mmio_phandle); + qemu_fdt_setprop_cell(ms->fdt, name, "clock-frequency", 3686400); + qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_mmio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { - qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", UART0_IRQ); + qemu_fdt_setprop_cell(ms->fdt, name, "interrupts", UART0_IRQ); } else { - qemu_fdt_setprop_cells(mc->fdt, name, "interrupts", UART0_IRQ, 0x4); + qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", UART0_IRQ, 0x4); } - qemu_fdt_add_subnode(mc->fdt, "/chosen"); - qemu_fdt_setprop_string(mc->fdt, "/chosen", "stdout-path", name); - g_free(name); + qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", name); } static void create_fdt_rtc(RISCVVirtState *s, const MemMapEntry *memmap, uint32_t irq_mmio_phandle) { - char *name; - MachineState *mc = MACHINE(s); + g_autofree char *name = NULL; + MachineState *ms = MACHINE(s); name = g_strdup_printf("/soc/rtc@%lx", (long)memmap[VIRT_RTC].base); - qemu_fdt_add_subnode(mc->fdt, name); - qemu_fdt_setprop_string(mc->fdt, name, "compatible", + qemu_fdt_add_subnode(ms->fdt, name); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", "google,goldfish-rtc"); - qemu_fdt_setprop_cells(mc->fdt, name, "reg", + qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, memmap[VIRT_RTC].base, 0x0, memmap[VIRT_RTC].size); - qemu_fdt_setprop_cell(mc->fdt, name, "interrupt-parent", + qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_mmio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { - qemu_fdt_setprop_cell(mc->fdt, name, "interrupts", RTC_IRQ); + qemu_fdt_setprop_cell(ms->fdt, name, "interrupts", RTC_IRQ); } else { - qemu_fdt_setprop_cells(mc->fdt, name, "interrupts", RTC_IRQ, 0x4); + qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", RTC_IRQ, 0x4); } - g_free(name); } static void create_fdt_flash(RISCVVirtState *s, const MemMapEntry *memmap) { - char *name; - MachineState *mc = MACHINE(s); + MachineState *ms = MACHINE(s); hwaddr flashsize = virt_memmap[VIRT_FLASH].size / 2; hwaddr flashbase = virt_memmap[VIRT_FLASH].base; + g_autofree char *name = g_strdup_printf("/flash@%" PRIx64, flashbase); - name = g_strdup_printf("/flash@%" PRIx64, flashbase); - qemu_fdt_add_subnode(mc->fdt, name); - qemu_fdt_setprop_string(mc->fdt, name, "compatible", "cfi-flash"); - qemu_fdt_setprop_sized_cells(mc->fdt, name, "reg", + qemu_fdt_add_subnode(ms->fdt, name); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", 2, flashbase, 2, flashsize, 2, flashbase + flashsize, 2, flashsize); - qemu_fdt_setprop_cell(mc->fdt, name, "bank-width", 4); - g_free(name); + qemu_fdt_setprop_cell(ms->fdt, name, "bank-width", 4); } -static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap, - uint64_t mem_size, const char *cmdline, bool is_32_bit) +static void create_fdt_fw_cfg(RISCVVirtState *s, const MemMapEntry *memmap) +{ + MachineState *ms = MACHINE(s); + hwaddr base = memmap[VIRT_FW_CFG].base; + hwaddr size = memmap[VIRT_FW_CFG].size; + g_autofree char *nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base); + + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, + "compatible", "qemu,fw-cfg-mmio"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", + 2, base, 2, size); + qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); +} + +static void create_fdt_virtio_iommu(RISCVVirtState *s, uint16_t bdf) +{ + const char compat[] = "virtio,pci-iommu\0pci1af4,1057"; + void *fdt = MACHINE(s)->fdt; + uint32_t iommu_phandle; + g_autofree char *iommu_node = NULL; + g_autofree char *pci_node = NULL; + + pci_node = g_strdup_printf("/soc/pci@%lx", + (long) virt_memmap[VIRT_PCIE_ECAM].base); + iommu_node = g_strdup_printf("%s/virtio_iommu@%x,%x", pci_node, + PCI_SLOT(bdf), PCI_FUNC(bdf)); + iommu_phandle = qemu_fdt_alloc_phandle(fdt); + + qemu_fdt_add_subnode(fdt, iommu_node); + + qemu_fdt_setprop(fdt, iommu_node, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(fdt, iommu_node, "reg", + 1, bdf << 8, 1, 0, 1, 0, + 1, 0, 1, 0); + qemu_fdt_setprop_cell(fdt, iommu_node, "#iommu-cells", 1); + qemu_fdt_setprop_cell(fdt, iommu_node, "phandle", iommu_phandle); + + qemu_fdt_setprop_cells(fdt, pci_node, "iommu-map", + 0, iommu_phandle, 0, bdf, + bdf + 1, iommu_phandle, bdf + 1, 0xffff - bdf); +} + +static void finalize_fdt(RISCVVirtState *s) { - MachineState *mc = MACHINE(s); uint32_t phandle = 1, irq_mmio_phandle = 1, msi_pcie_phandle = 1; uint32_t irq_pcie_phandle = 1, irq_virtio_phandle = 1; - if (mc->dtb) { - mc->fdt = load_device_tree(mc->dtb, &s->fdt_size); - if (!mc->fdt) { - error_report("load_device_tree() failed"); - exit(1); - } - goto update_bootargs; - } else { - mc->fdt = create_device_tree(&s->fdt_size); - if (!mc->fdt) { - error_report("create_device_tree() failed"); - exit(1); - } + create_fdt_sockets(s, virt_memmap, &phandle, &irq_mmio_phandle, + &irq_pcie_phandle, &irq_virtio_phandle, + &msi_pcie_phandle); + + create_fdt_virtio(s, virt_memmap, irq_virtio_phandle); + + create_fdt_pcie(s, virt_memmap, irq_pcie_phandle, msi_pcie_phandle); + + create_fdt_reset(s, virt_memmap, &phandle); + + create_fdt_uart(s, virt_memmap, irq_mmio_phandle); + + create_fdt_rtc(s, virt_memmap, irq_mmio_phandle); +} + +static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap) +{ + MachineState *ms = MACHINE(s); + uint8_t rng_seed[32]; + g_autofree char *name = NULL; + + ms->fdt = create_device_tree(&s->fdt_size); + if (!ms->fdt) { + error_report("create_device_tree() failed"); + exit(1); } - qemu_fdt_setprop_string(mc->fdt, "/", "model", "riscv-virtio,qemu"); - qemu_fdt_setprop_string(mc->fdt, "/", "compatible", "riscv-virtio"); - qemu_fdt_setprop_cell(mc->fdt, "/", "#size-cells", 0x2); - qemu_fdt_setprop_cell(mc->fdt, "/", "#address-cells", 0x2); + qemu_fdt_setprop_string(ms->fdt, "/", "model", "riscv-virtio,qemu"); + qemu_fdt_setprop_string(ms->fdt, "/", "compatible", "riscv-virtio"); + qemu_fdt_setprop_cell(ms->fdt, "/", "#size-cells", 0x2); + qemu_fdt_setprop_cell(ms->fdt, "/", "#address-cells", 0x2); - qemu_fdt_add_subnode(mc->fdt, "/soc"); - qemu_fdt_setprop(mc->fdt, "/soc", "ranges", NULL, 0); - qemu_fdt_setprop_string(mc->fdt, "/soc", "compatible", "simple-bus"); - qemu_fdt_setprop_cell(mc->fdt, "/soc", "#size-cells", 0x2); - qemu_fdt_setprop_cell(mc->fdt, "/soc", "#address-cells", 0x2); + qemu_fdt_add_subnode(ms->fdt, "/soc"); + qemu_fdt_setprop(ms->fdt, "/soc", "ranges", NULL, 0); + qemu_fdt_setprop_string(ms->fdt, "/soc", "compatible", "simple-bus"); + qemu_fdt_setprop_cell(ms->fdt, "/soc", "#size-cells", 0x2); + qemu_fdt_setprop_cell(ms->fdt, "/soc", "#address-cells", 0x2); - create_fdt_sockets(s, memmap, is_32_bit, &phandle, - &irq_mmio_phandle, &irq_pcie_phandle, &irq_virtio_phandle, - &msi_pcie_phandle); + /* + * The "/soc/pci@..." node is needed for PCIE hotplugs + * that might happen before finalize_fdt(). + */ + name = g_strdup_printf("/soc/pci@%lx", (long) memmap[VIRT_PCIE_ECAM].base); + qemu_fdt_add_subnode(ms->fdt, name); - create_fdt_virtio(s, memmap, irq_virtio_phandle); + qemu_fdt_add_subnode(ms->fdt, "/chosen"); - create_fdt_pcie(s, memmap, irq_pcie_phandle, msi_pcie_phandle); - - create_fdt_reset(s, memmap, &phandle); - - create_fdt_uart(s, memmap, irq_mmio_phandle); - - create_fdt_rtc(s, memmap, irq_mmio_phandle); + /* Pass seed to RNG */ + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + qemu_fdt_setprop(ms->fdt, "/chosen", "rng-seed", + rng_seed, sizeof(rng_seed)); create_fdt_flash(s, memmap); - -update_bootargs: - if (cmdline && *cmdline) { - qemu_fdt_setprop_string(mc->fdt, "/chosen", "bootargs", cmdline); - } + create_fdt_fw_cfg(s, memmap); + create_fdt_pmu(s); } static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, - hwaddr ecam_base, hwaddr ecam_size, - hwaddr mmio_base, hwaddr mmio_size, - hwaddr high_mmio_base, - hwaddr high_mmio_size, - hwaddr pio_base, - DeviceState *irqchip) + DeviceState *irqchip, + RISCVVirtState *s) { DeviceState *dev; MemoryRegion *ecam_alias, *ecam_reg; MemoryRegion *mmio_alias, *high_mmio_alias, *mmio_reg; + hwaddr ecam_base = s->memmap[VIRT_PCIE_ECAM].base; + hwaddr ecam_size = s->memmap[VIRT_PCIE_ECAM].size; + hwaddr mmio_base = s->memmap[VIRT_PCIE_MMIO].base; + hwaddr mmio_size = s->memmap[VIRT_PCIE_MMIO].size; + hwaddr high_mmio_base = virt_high_pcie_memmap.base; + hwaddr high_mmio_size = virt_high_pcie_memmap.size; + hwaddr pio_base = s->memmap[VIRT_PCIE_PIO].base; + hwaddr pio_size = s->memmap[VIRT_PCIE_PIO].size; qemu_irq irq; int i; dev = qdev_new(TYPE_GPEX_HOST); + /* Set GPEX object properties for the virt machine */ + object_property_set_uint(OBJECT(GPEX_HOST(dev)), PCI_HOST_ECAM_BASE, + ecam_base, NULL); + object_property_set_int(OBJECT(GPEX_HOST(dev)), PCI_HOST_ECAM_SIZE, + ecam_size, NULL); + object_property_set_uint(OBJECT(GPEX_HOST(dev)), + PCI_HOST_BELOW_4G_MMIO_BASE, + mmio_base, NULL); + object_property_set_int(OBJECT(GPEX_HOST(dev)), PCI_HOST_BELOW_4G_MMIO_SIZE, + mmio_size, NULL); + object_property_set_uint(OBJECT(GPEX_HOST(dev)), + PCI_HOST_ABOVE_4G_MMIO_BASE, + high_mmio_base, NULL); + object_property_set_int(OBJECT(GPEX_HOST(dev)), PCI_HOST_ABOVE_4G_MMIO_SIZE, + high_mmio_size, NULL); + object_property_set_uint(OBJECT(GPEX_HOST(dev)), PCI_HOST_PIO_BASE, + pio_base, NULL); + object_property_set_int(OBJECT(GPEX_HOST(dev)), PCI_HOST_PIO_SIZE, + pio_size, NULL); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); ecam_alias = g_new0(MemoryRegion, 1); @@ -1076,28 +1138,19 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ + i); } + GPEX_HOST(dev)->gpex_cfg.bus = PCI_HOST_BRIDGE(GPEX_HOST(dev))->bus; return dev; } -static FWCfgState *create_fw_cfg(const MachineState *mc) +static FWCfgState *create_fw_cfg(const MachineState *ms) { hwaddr base = virt_memmap[VIRT_FW_CFG].base; - hwaddr size = virt_memmap[VIRT_FW_CFG].size; FWCfgState *fw_cfg; - char *nodename; fw_cfg = fw_cfg_init_mem_wide(base + 8, base, 8, base + 16, &address_space_memory); - fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)mc->smp.cpus); + fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)ms->smp.cpus); - nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base); - qemu_fdt_add_subnode(mc->fdt, nodename); - qemu_fdt_setprop_string(mc->fdt, nodename, - "compatible", "qemu,fw-cfg-mmio"); - qemu_fdt_setprop_sized_cells(mc->fdt, nodename, "reg", - 2, base, 2, size); - qemu_fdt_setprop(mc->fdt, nodename, "dma-coherent", NULL, 0); - g_free(nodename); return fw_cfg; } @@ -1105,7 +1158,7 @@ static DeviceState *virt_create_plic(const MemMapEntry *memmap, int socket, int base_hartid, int hart_count) { DeviceState *ret; - char *plic_hart_config; + g_autofree char *plic_hart_config = NULL; /* Per-socket PLIC hart topology configuration string */ plic_hart_config = riscv_plic_hart_config_string(hart_count); @@ -1124,8 +1177,6 @@ static DeviceState *virt_create_plic(const MemMapEntry *memmap, int socket, VIRT_PLIC_CONTEXT_STRIDE, memmap[VIRT_PLIC].size); - g_free(plic_hart_config); - return ret; } @@ -1136,16 +1187,20 @@ static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests, int i; hwaddr addr; uint32_t guest_bits; - DeviceState *aplic_m; - bool msimode = (aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) ? true : false; + DeviceState *aplic_s = NULL; + DeviceState *aplic_m = NULL; + bool msimode = aia_type == VIRT_AIA_TYPE_APLIC_IMSIC; if (msimode) { - /* Per-socket M-level IMSICs */ - addr = memmap[VIRT_IMSIC_M].base + socket * VIRT_IMSIC_GROUP_MAX_SIZE; - for (i = 0; i < hart_count; i++) { - riscv_imsic_create(addr + i * IMSIC_HART_SIZE(0), - base_hartid + i, true, 1, - VIRT_IRQCHIP_NUM_MSIS); + if (!kvm_enabled()) { + /* Per-socket M-level IMSICs */ + addr = memmap[VIRT_IMSIC_M].base + + socket * VIRT_IMSIC_GROUP_MAX_SIZE; + for (i = 0; i < hart_count; i++) { + riscv_imsic_create(addr + i * IMSIC_HART_SIZE(0), + base_hartid + i, true, 1, + VIRT_IRQCHIP_NUM_MSIS); + } } /* Per-socket S-level IMSICs */ @@ -1158,29 +1213,29 @@ static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests, } } - /* Per-socket M-level APLIC */ - aplic_m = riscv_aplic_create( - memmap[VIRT_APLIC_M].base + socket * memmap[VIRT_APLIC_M].size, - memmap[VIRT_APLIC_M].size, - (msimode) ? 0 : base_hartid, - (msimode) ? 0 : hart_count, - VIRT_IRQCHIP_NUM_SOURCES, - VIRT_IRQCHIP_NUM_PRIO_BITS, - msimode, true, NULL); - - if (aplic_m) { - /* Per-socket S-level APLIC */ - riscv_aplic_create( - memmap[VIRT_APLIC_S].base + socket * memmap[VIRT_APLIC_S].size, - memmap[VIRT_APLIC_S].size, - (msimode) ? 0 : base_hartid, - (msimode) ? 0 : hart_count, - VIRT_IRQCHIP_NUM_SOURCES, - VIRT_IRQCHIP_NUM_PRIO_BITS, - msimode, false, aplic_m); + if (!kvm_enabled()) { + /* Per-socket M-level APLIC */ + aplic_m = riscv_aplic_create(memmap[VIRT_APLIC_M].base + + socket * memmap[VIRT_APLIC_M].size, + memmap[VIRT_APLIC_M].size, + (msimode) ? 0 : base_hartid, + (msimode) ? 0 : hart_count, + VIRT_IRQCHIP_NUM_SOURCES, + VIRT_IRQCHIP_NUM_PRIO_BITS, + msimode, true, NULL); } - return aplic_m; + /* Per-socket S-level APLIC */ + aplic_s = riscv_aplic_create(memmap[VIRT_APLIC_S].base + + socket * memmap[VIRT_APLIC_S].size, + memmap[VIRT_APLIC_S].size, + (msimode) ? 0 : base_hartid, + (msimode) ? 0 : hart_count, + VIRT_IRQCHIP_NUM_SOURCES, + VIRT_IRQCHIP_NUM_PRIO_BITS, + msimode, false, aplic_m); + + return kvm_enabled() ? aplic_s : aplic_m; } static void create_platform_bus(RISCVVirtState *s, DeviceState *irqchip) @@ -1209,6 +1264,45 @@ static void create_platform_bus(RISCVVirtState *s, DeviceState *irqchip) sysbus_mmio_get_region(sysbus, 0)); } +static void virt_build_smbios(RISCVVirtState *s) +{ + MachineClass *mc = MACHINE_GET_CLASS(s); + MachineState *ms = MACHINE(s); + uint8_t *smbios_tables, *smbios_anchor; + size_t smbios_tables_len, smbios_anchor_len; + struct smbios_phys_mem_area mem_array; + const char *product = "QEMU Virtual Machine"; + + if (kvm_enabled()) { + product = "KVM Virtual Machine"; + } + + smbios_set_defaults("QEMU", product, mc->name, true); + + if (riscv_is_32bit(&s->soc[0])) { + smbios_set_default_processor_family(0x200); + } else { + smbios_set_default_processor_family(0x201); + } + + /* build the array of physical mem area from base_memmap */ + mem_array.address = s->memmap[VIRT_DRAM].base; + mem_array.length = ms->ram_size; + + smbios_get_tables(ms, SMBIOS_ENTRY_POINT_TYPE_64, + &mem_array, 1, + &smbios_tables, &smbios_tables_len, + &smbios_anchor, &smbios_anchor_len, + &error_fatal); + + if (smbios_anchor) { + fw_cfg_add_file(s->fw_cfg, "etc/smbios/smbios-tables", + smbios_tables, smbios_tables_len); + fw_cfg_add_file(s->fw_cfg, "etc/smbios/smbios-anchor", + smbios_anchor, smbios_anchor_len); + } +} + static void virt_machine_done(Notifier *notifier, void *data) { RISCVVirtState *s = container_of(notifier, RISCVVirtState, @@ -1217,8 +1311,18 @@ static void virt_machine_done(Notifier *notifier, void *data) MachineState *machine = MACHINE(s); target_ulong start_addr = memmap[VIRT_DRAM].base; target_ulong firmware_end_addr, kernel_start_addr; - uint32_t fdt_load_addr; - uint64_t kernel_entry; + const char *firmware_name = riscv_default_firmware_name(&s->soc[0]); + uint64_t fdt_load_addr; + uint64_t kernel_entry = 0; + BlockBackend *pflash_blk0; + + /* + * An user provided dtb must include everything, including + * dynamic sysbus devices. Our FDT needs to be finalized. + */ + if (machine->dtb == NULL) { + finalize_fdt(s); + } /* * Only direct boot kernel is currently supported for KVM VM, @@ -1236,62 +1340,47 @@ static void virt_machine_done(Notifier *notifier, void *data) } } - if (riscv_is_32bit(&s->soc[0])) { - firmware_end_addr = riscv_find_and_load_firmware(machine, - RISCV32_BIOS_BIN, start_addr, NULL); - } else { - firmware_end_addr = riscv_find_and_load_firmware(machine, - RISCV64_BIOS_BIN, start_addr, NULL); + firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, + start_addr, NULL); + + pflash_blk0 = pflash_cfi01_get_blk(s->flash[0]); + if (pflash_blk0) { + if (machine->firmware && !strcmp(machine->firmware, "none") && + !kvm_enabled()) { + /* + * Pflash was supplied but bios is none and not KVM guest, + * let's overwrite the address we jump to after reset to + * the base of the flash. + */ + start_addr = virt_memmap[VIRT_FLASH].base; + } else { + /* + * Pflash was supplied but either KVM guest or bios is not none. + * In this case, base of the flash would contain S-mode payload. + */ + riscv_setup_firmware_boot(machine); + kernel_entry = virt_memmap[VIRT_FLASH].base; + } } - if (machine->kernel_filename) { + if (machine->kernel_filename && !kernel_entry) { kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0], firmware_end_addr); - kernel_entry = riscv_load_kernel(machine->kernel_filename, - kernel_start_addr, NULL); - - if (machine->initrd_filename) { - hwaddr start; - hwaddr end = riscv_load_initrd(machine->initrd_filename, - machine->ram_size, kernel_entry, - &start); - qemu_fdt_setprop_cell(machine->fdt, "/chosen", - "linux,initrd-start", start); - qemu_fdt_setprop_cell(machine->fdt, "/chosen", "linux,initrd-end", - end); - } - } else { - /* - * If dynamic firmware is used, it doesn't know where is the next mode - * if kernel argument is not set. - */ - kernel_entry = 0; + kernel_entry = riscv_load_kernel(machine, &s->soc[0], + kernel_start_addr, true, NULL); } - if (drive_get(IF_PFLASH, 0, 0)) { - /* - * Pflash was supplied, let's overwrite the address we jump to after - * reset to the base of the flash. - */ - start_addr = virt_memmap[VIRT_FLASH].base; - } + fdt_load_addr = riscv_compute_fdt_addr(memmap[VIRT_DRAM].base, + memmap[VIRT_DRAM].size, + machine); + riscv_load_fdt(fdt_load_addr, machine->fdt); - /* - * Init fw_cfg. Must be done before riscv_load_fdt, otherwise the device - * tree cannot be altered and we get FDT_ERR_NOSPACE. - */ - s->fw_cfg = create_fw_cfg(machine); - rom_set_fw(s->fw_cfg); - - /* Compute the fdt load address in dram */ - fdt_load_addr = riscv_load_fdt(memmap[VIRT_DRAM].base, - machine->ram_size, machine->fdt); /* load the reset vector */ riscv_setup_rom_reset_vec(machine, &s->soc[0], start_addr, virt_memmap[VIRT_MROM].base, virt_memmap[VIRT_MROM].size, kernel_entry, - fdt_load_addr, machine->fdt); + fdt_load_addr); /* * Only direct boot kernel is currently supported for KVM VM, @@ -1301,6 +1390,12 @@ static void virt_machine_done(Notifier *notifier, void *data) if (kvm_enabled()) { riscv_setup_direct_kernel(kernel_entry, fdt_load_addr); } + + virt_build_smbios(s); + + if (virt_is_acpi_enabled(s)) { + virt_acpi_setup(s); + } } static void virt_machine_init(MachineState *machine) @@ -1309,20 +1404,27 @@ static void virt_machine_init(MachineState *machine) RISCVVirtState *s = RISCV_VIRT_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *mask_rom = g_new(MemoryRegion, 1); - char *soc_name; DeviceState *mmio_irqchip, *virtio_irqchip, *pcie_irqchip; int i, base_hartid, hart_count; + int socket_count = riscv_socket_count(machine); /* Check socket count limit */ - if (VIRT_SOCKETS_MAX < riscv_socket_count(machine)) { + if (VIRT_SOCKETS_MAX < socket_count) { error_report("number of sockets/nodes should be less than %d", VIRT_SOCKETS_MAX); exit(1); } + if (!virt_aclint_allowed() && s->have_aclint) { + error_report("'aclint' is only available with TCG acceleration"); + exit(1); + } + /* Initialize sockets */ mmio_irqchip = virtio_irqchip = pcie_irqchip = NULL; - for (i = 0; i < riscv_socket_count(machine); i++) { + for (i = 0; i < socket_count; i++) { + g_autofree char *soc_name = g_strdup_printf("soc%d", i); + if (!riscv_socket_check_hartids(machine, i)) { error_report("discontinuous hartids in socket%d", i); exit(1); @@ -1340,10 +1442,8 @@ static void virt_machine_init(MachineState *machine) exit(1); } - soc_name = g_strdup_printf("soc%d", i); object_initialize_child(OBJECT(machine), soc_name, &s->soc[i], TYPE_RISCV_HART_ARRAY); - g_free(soc_name); object_property_set_str(OBJECT(&s->soc[i]), "cpu-type", machine->cpu_type, &error_abort); object_property_set_int(OBJECT(&s->soc[i]), "hartid-base", @@ -1352,23 +1452,22 @@ static void virt_machine_init(MachineState *machine) hart_count, &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_fatal); - if (!kvm_enabled()) { - if (s->have_aclint) { - if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { - /* Per-socket ACLINT MTIMER */ - riscv_aclint_mtimer_create(memmap[VIRT_CLINT].base + + if (virt_aclint_allowed() && s->have_aclint) { + if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { + /* Per-socket ACLINT MTIMER */ + riscv_aclint_mtimer_create(memmap[VIRT_CLINT].base + i * RISCV_ACLINT_DEFAULT_MTIMER_SIZE, RISCV_ACLINT_DEFAULT_MTIMER_SIZE, base_hartid, hart_count, RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true); - } else { - /* Per-socket ACLINT MSWI, MTIMER, and SSWI */ - riscv_aclint_swi_create(memmap[VIRT_CLINT].base + + } else { + /* Per-socket ACLINT MSWI, MTIMER, and SSWI */ + riscv_aclint_swi_create(memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size, base_hartid, hart_count, false); - riscv_aclint_mtimer_create(memmap[VIRT_CLINT].base + + riscv_aclint_mtimer_create(memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size + RISCV_ACLINT_SWI_SIZE, RISCV_ACLINT_DEFAULT_MTIMER_SIZE, @@ -1376,21 +1475,20 @@ static void virt_machine_init(MachineState *machine) RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true); - riscv_aclint_swi_create(memmap[VIRT_ACLINT_SSWI].base + + riscv_aclint_swi_create(memmap[VIRT_ACLINT_SSWI].base + i * memmap[VIRT_ACLINT_SSWI].size, base_hartid, hart_count, true); - } - } else { - /* Per-socket SiFive CLINT */ - riscv_aclint_swi_create( + } + } else if (tcg_enabled()) { + /* Per-socket SiFive CLINT */ + riscv_aclint_swi_create( memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size, base_hartid, hart_count, false); - riscv_aclint_mtimer_create(memmap[VIRT_CLINT].base + + riscv_aclint_mtimer_create(memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size + RISCV_ACLINT_SWI_SIZE, RISCV_ACLINT_DEFAULT_MTIMER_SIZE, base_hartid, hart_count, RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true); - } } /* Per-socket interrupt controller */ @@ -1418,6 +1516,14 @@ static void virt_machine_init(MachineState *machine) } } + if (kvm_enabled() && virt_use_kvm_aia(s)) { + kvm_riscv_aia_create(machine, IMSIC_MMIO_GROUP_MIN_SHIFT, + VIRT_IRQCHIP_NUM_SOURCES, VIRT_IRQCHIP_NUM_MSIS, + memmap[VIRT_APLIC_S].base, + memmap[VIRT_IMSIC_S].base, + s->aia_guests); + } + if (riscv_is_32bit(&s->soc[0])) { #if HOST_LONG_BITS == 64 /* limit RAM size in a 32-bit system */ @@ -1435,6 +1541,8 @@ static void virt_machine_init(MachineState *machine) ROUND_UP(virt_high_pcie_memmap.base, virt_high_pcie_memmap.size); } + s->memmap = virt_memmap; + /* register system main memory (actual RAM) */ memory_region_add_subregion(system_memory, memmap[VIRT_DRAM].base, machine->ram); @@ -1445,6 +1553,13 @@ static void virt_machine_init(MachineState *machine) memory_region_add_subregion(system_memory, memmap[VIRT_MROM].base, mask_rom); + /* + * Init fw_cfg. Must be done before riscv_load_fdt, otherwise the + * device tree cannot be altered and we get FDT_ERR_NOSPACE. + */ + s->fw_cfg = create_fw_cfg(machine); + rom_set_fw(s->fw_cfg); + /* SiFive Test MMIO device */ sifive_test_create(memmap[VIRT_TEST].base); @@ -1452,29 +1567,19 @@ static void virt_machine_init(MachineState *machine) for (i = 0; i < VIRTIO_COUNT; i++) { sysbus_create_simple("virtio-mmio", memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size, - qdev_get_gpio_in(DEVICE(virtio_irqchip), VIRTIO_IRQ + i)); + qdev_get_gpio_in(virtio_irqchip, VIRTIO_IRQ + i)); } - gpex_pcie_init(system_memory, - memmap[VIRT_PCIE_ECAM].base, - memmap[VIRT_PCIE_ECAM].size, - memmap[VIRT_PCIE_MMIO].base, - memmap[VIRT_PCIE_MMIO].size, - virt_high_pcie_memmap.base, - virt_high_pcie_memmap.size, - memmap[VIRT_PCIE_PIO].base, - DEVICE(pcie_irqchip)); + gpex_pcie_init(system_memory, pcie_irqchip, s); - create_platform_bus(s, DEVICE(mmio_irqchip)); + create_platform_bus(s, mmio_irqchip); serial_mm_init(system_memory, memmap[VIRT_UART0].base, - 0, qdev_get_gpio_in(DEVICE(mmio_irqchip), UART0_IRQ), 399193, + 0, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 399193, serial_hd(0), DEVICE_LITTLE_ENDIAN); sysbus_create_simple("goldfish_rtc", memmap[VIRT_RTC].base, - qdev_get_gpio_in(DEVICE(mmio_irqchip), RTC_IRQ)); - - virt_flash_create(s); + qdev_get_gpio_in(mmio_irqchip, RTC_IRQ)); for (i = 0; i < ARRAY_SIZE(s->flash); i++) { /* Map legacy -drive if=pflash to machine properties */ @@ -1483,9 +1588,16 @@ static void virt_machine_init(MachineState *machine) } virt_flash_map(s, system_memory); - /* create device tree */ - create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline, - riscv_is_32bit(&s->soc[0])); + /* load/create device tree */ + if (machine->dtb) { + machine->fdt = load_device_tree(machine->dtb, &s->fdt_size); + if (!machine->fdt) { + error_report("load_device_tree() failed"); + exit(1); + } + } else { + create_fdt(s, memmap); + } s->machine_done.notify = virt_machine_done; qemu_add_machine_init_done_notifier(&s->machine_done); @@ -1493,6 +1605,13 @@ static void virt_machine_init(MachineState *machine) static void virt_machine_instance_init(Object *obj) { + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + + virt_flash_create(s); + + s->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); + s->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); + s->acpi = ON_OFF_AUTO_AUTO; } static char *virt_get_aia_guests(Object *obj, Error **errp) @@ -1555,26 +1674,47 @@ static void virt_set_aia(Object *obj, const char *val, Error **errp) static bool virt_get_aclint(Object *obj, Error **errp) { - MachineState *ms = MACHINE(obj); - RISCVVirtState *s = RISCV_VIRT_MACHINE(ms); + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); return s->have_aclint; } static void virt_set_aclint(Object *obj, bool value, Error **errp) { - MachineState *ms = MACHINE(obj); - RISCVVirtState *s = RISCV_VIRT_MACHINE(ms); + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); s->have_aclint = value; } +bool virt_is_acpi_enabled(RISCVVirtState *s) +{ + return s->acpi != ON_OFF_AUTO_OFF; +} + +static void virt_get_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + OnOffAuto acpi = s->acpi; + + visit_type_OnOffAuto(v, name, &acpi, errp); +} + +static void virt_set_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &s->acpi, errp); +} + static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, DeviceState *dev) { MachineClass *mc = MACHINE_GET_CLASS(machine); - if (device_is_dynamic_sysbus(mc, dev)) { + if (device_is_dynamic_sysbus(mc, dev) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { return HOTPLUG_HANDLER(machine); } return NULL; @@ -1593,6 +1733,10 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, SYS_BUS_DEVICE(dev)); } } + + if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { + create_fdt_virtio_iommu(s, pci_get_bdf(PCI_DEVICE(dev))); + } } static void virt_machine_class_init(ObjectClass *oc, void *data) @@ -1610,6 +1754,8 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props; mc->get_default_cpu_node_id = riscv_numa_get_default_cpu_node_id; mc->numa_mem_supported = true; + /* platform instead of architectural choice */ + mc->cpu_cluster_has_numa_boundary = true; mc->default_ram_id = "riscv_virt_board.ram"; assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = virt_machine_get_hotplug_handler; @@ -1621,17 +1767,19 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif + object_class_property_add_bool(oc, "aclint", virt_get_aclint, virt_set_aclint); object_class_property_set_description(oc, "aclint", - "Set on/off to enable/disable " - "emulating ACLINT devices"); + "(TCG only) Set on/off to " + "enable/disable emulating " + "ACLINT devices"); object_class_property_add_str(oc, "aia", virt_get_aia, virt_set_aia); object_class_property_set_description(oc, "aia", "Set type of AIA interrupt " - "conttoller. Valid values are " + "controller. Valid values are " "none, aplic, and aplic-imsic."); object_class_property_add_str(oc, "aia-guests", @@ -1640,6 +1788,11 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) sprintf(str, "Set number of guest MMIO pages for AIA IMSIC. Valid value " "should be between 0 and %d.", VIRT_IRQCHIP_MAX_GUESTS); object_class_property_set_description(oc, "aia-guests", str); + object_class_property_add(oc, "acpi", "OnOffAuto", + virt_get_acpi, virt_set_acpi, + NULL, NULL); + object_class_property_set_description(oc, "acpi", + "Enable ACPI"); } static const TypeInfo virt_machine_typeinfo = { diff --git a/hw/rtc/allwinner-rtc.c b/hw/rtc/allwinner-rtc.c index 7e493f0e79..2ac50b30cb 100644 --- a/hw/rtc/allwinner-rtc.c +++ b/hw/rtc/allwinner-rtc.c @@ -305,7 +305,7 @@ static const VMStateDescription allwinner_rtc_vmstate = { .name = "allwinner-rtc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AwRtcState, AW_RTC_REGS_NUM), VMSTATE_END_OF_LIST() } diff --git a/hw/rtc/aspeed_rtc.c b/hw/rtc/aspeed_rtc.c index f6da7b666d..589d9a5a7a 100644 --- a/hw/rtc/aspeed_rtc.c +++ b/hw/rtc/aspeed_rtc.c @@ -136,11 +136,10 @@ static const MemoryRegionOps aspeed_rtc_ops = { static const VMStateDescription vmstate_aspeed_rtc = { .name = TYPE_ASPEED_RTC, - .version_id = 1, - .fields = (VMStateField[]) { + .version_id = 2, + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(reg, AspeedRtcState, 0x18), - VMSTATE_INT32(offset, AspeedRtcState), - VMSTATE_INT32(offset, AspeedRtcState), + VMSTATE_INT64(offset, AspeedRtcState), VMSTATE_END_OF_LIST() } }; diff --git a/hw/rtc/ds1338.c b/hw/rtc/ds1338.c index 36d8121ddd..e479661c39 100644 --- a/hw/rtc/ds1338.c +++ b/hw/rtc/ds1338.c @@ -46,7 +46,7 @@ static const VMStateDescription vmstate_ds1338 = { .name = "ds1338", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_I2C_SLAVE(parent_obj, DS1338State), VMSTATE_INT64(offset, DS1338State), VMSTATE_UINT8_V(wday_offset, DS1338State, 2), diff --git a/hw/rtc/exynos4210_rtc.c b/hw/rtc/exynos4210_rtc.c index d1620c7a2a..319371f97d 100644 --- a/hw/rtc/exynos4210_rtc.c +++ b/hw/rtc/exynos4210_rtc.c @@ -122,7 +122,7 @@ static const VMStateDescription vmstate_exynos4210_rtc_state = { .name = "exynos4210.rtc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(reg_intp, Exynos4210RTCState), VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState), VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState), @@ -202,7 +202,7 @@ static void exynos4210_rtc_update_freq(Exynos4210RTCState *s, uint32_t freq; freq = s->freq; - /* set frequncy for time generator */ + /* set frequency for time generator */ s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value)); if (freq != s->freq) { @@ -374,7 +374,7 @@ static uint64_t exynos4210_rtc_read(void *opaque, hwaddr offset, default: qemu_log_mask(LOG_GUEST_ERROR, - "exynos4210.rtc: bad read offset " TARGET_FMT_plx, + "exynos4210.rtc: bad read offset " HWADDR_FMT_plx, offset); break; } @@ -508,7 +508,7 @@ static void exynos4210_rtc_write(void *opaque, hwaddr offset, default: qemu_log_mask(LOG_GUEST_ERROR, - "exynos4210.rtc: bad write offset " TARGET_FMT_plx, + "exynos4210.rtc: bad write offset " HWADDR_FMT_plx, offset); break; diff --git a/hw/rtc/goldfish_rtc.c b/hw/rtc/goldfish_rtc.c index 35e493be31..01acf30b27 100644 --- a/hw/rtc/goldfish_rtc.c +++ b/hw/rtc/goldfish_rtc.c @@ -216,14 +216,25 @@ static int goldfish_rtc_post_load(void *opaque, int version_id) return 0; } -static const MemoryRegionOps goldfish_rtc_ops = { - .read = goldfish_rtc_read, - .write = goldfish_rtc_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } +static const MemoryRegionOps goldfish_rtc_ops[2] = { + [false] = { + .read = goldfish_rtc_read, + .write = goldfish_rtc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } + }, + [true] = { + .read = goldfish_rtc_read, + .write = goldfish_rtc_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } + }, }; static const VMStateDescription goldfish_rtc_vmstate = { @@ -231,7 +242,7 @@ static const VMStateDescription goldfish_rtc_vmstate = { .version_id = 2, .pre_save = goldfish_rtc_pre_save, .post_load = goldfish_rtc_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(tick_offset_vmstate, GoldfishRTCState), VMSTATE_UINT64(alarm_next, GoldfishRTCState), VMSTATE_UINT32(alarm_running, GoldfishRTCState), @@ -265,7 +276,8 @@ 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, + memory_region_init_io(&s->iomem, OBJECT(s), + &goldfish_rtc_ops[s->big_endian], s, "goldfish_rtc", 0x24); sysbus_init_mmio(dev, &s->iomem); @@ -274,10 +286,17 @@ static void goldfish_rtc_realize(DeviceState *d, Error **errp) s->timer = timer_new_ns(rtc_clock, goldfish_rtc_interrupt, s); } +static Property goldfish_rtc_properties[] = { + DEFINE_PROP_BOOL("big-endian", GoldfishRTCState, big_endian, + false), + DEFINE_PROP_END_OF_LIST(), +}; + static void goldfish_rtc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + device_class_set_props(dc, goldfish_rtc_properties); dc->realize = goldfish_rtc_realize; dc->reset = goldfish_rtc_reset; dc->vmsd = &goldfish_rtc_vmstate; diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c index fe6710310f..ac28c1165b 100644 --- a/hw/rtc/ls7a_rtc.c +++ b/hw/rtc/ls7a_rtc.c @@ -72,9 +72,6 @@ struct LS7ARtcState { */ int64_t offset_toy; int64_t offset_rtc; - uint64_t save_toy_mon; - uint64_t save_toy_year; - uint64_t save_rtc; int64_t data; int tidx; uint32_t toymatch[3]; @@ -89,67 +86,45 @@ struct LS7ARtcState { }; /* switch nanoseconds time to rtc ticks */ -static inline uint64_t ls7a_rtc_ticks(void) +static uint64_t ls7a_rtc_ticks(void) { return qemu_clock_get_ns(rtc_clock) * LS7A_RTC_FREQ / NANOSECONDS_PER_SECOND; } /* switch rtc ticks to nanoseconds */ -static inline uint64_t ticks_to_ns(uint64_t ticks) +static uint64_t ticks_to_ns(uint64_t ticks) { return ticks * NANOSECONDS_PER_SECOND / LS7A_RTC_FREQ; } -static inline bool toy_enabled(LS7ARtcState *s) +static bool toy_enabled(LS7ARtcState *s) { return FIELD_EX32(s->cntrctl, RTC_CTRL, TOYEN) && FIELD_EX32(s->cntrctl, RTC_CTRL, EO); } -static inline bool rtc_enabled(LS7ARtcState *s) +static bool rtc_enabled(LS7ARtcState *s) { return FIELD_EX32(s->cntrctl, RTC_CTRL, RTCEN) && FIELD_EX32(s->cntrctl, RTC_CTRL, EO); } -/* parse toy value to struct tm */ -static inline void toy_val_to_time_mon(uint64_t toy_val, struct tm *tm) -{ - tm->tm_sec = FIELD_EX32(toy_val, TOY, SEC); - tm->tm_min = FIELD_EX32(toy_val, TOY, MIN); - tm->tm_hour = FIELD_EX32(toy_val, TOY, HOUR); - tm->tm_mday = FIELD_EX32(toy_val, TOY, DAY); - tm->tm_mon = FIELD_EX32(toy_val, TOY, MON) - 1; -} - -static inline void toy_val_to_time_year(uint64_t toy_year, struct tm *tm) -{ - tm->tm_year = toy_year; -} - /* parse struct tm to toy value */ -static inline uint64_t toy_time_to_val_mon(struct tm tm) +static uint64_t toy_time_to_val_mon(const struct tm *tm) { uint64_t val = 0; - val = FIELD_DP32(val, TOY, MON, tm.tm_mon + 1); - val = FIELD_DP32(val, TOY, DAY, tm.tm_mday); - val = FIELD_DP32(val, TOY, HOUR, tm.tm_hour); - val = FIELD_DP32(val, TOY, MIN, tm.tm_min); - val = FIELD_DP32(val, TOY, SEC, tm.tm_sec); + val = FIELD_DP32(val, TOY, MON, tm->tm_mon + 1); + val = FIELD_DP32(val, TOY, DAY, tm->tm_mday); + val = FIELD_DP32(val, TOY, HOUR, tm->tm_hour); + val = FIELD_DP32(val, TOY, MIN, tm->tm_min); + val = FIELD_DP32(val, TOY, SEC, tm->tm_sec); return val; } -static inline uint64_t toy_time_to_val_year(struct tm tm) -{ - uint64_t year; - - year = tm.tm_year; - return year; -} - -static inline void toymatch_val_to_time(uint64_t val, struct tm *tm) +static void toymatch_val_to_time(LS7ARtcState *s, uint64_t val, struct tm *tm) { + qemu_get_timedate(tm, s->offset_toy); tm->tm_sec = FIELD_EX32(val, TOY_MATCH, SEC); tm->tm_min = FIELD_EX32(val, TOY_MATCH, MIN); tm->tm_hour = FIELD_EX32(val, TOY_MATCH, HOUR); @@ -158,17 +133,18 @@ static inline void toymatch_val_to_time(uint64_t val, struct tm *tm) tm->tm_year += (FIELD_EX32(val, TOY_MATCH, YEAR) - (tm->tm_year & 0x3f)); } -static void toymatch_write(LS7ARtcState *s, struct tm *tm, uint64_t val, int num) +static void toymatch_write(LS7ARtcState *s, uint64_t val, int num) { int64_t now, expire_time; + struct tm tm = {}; /* it do not support write when toy disabled */ if (toy_enabled(s)) { s->toymatch[num] = val; - /* caculate expire time */ + /* calculate expire time */ now = qemu_clock_get_ms(rtc_clock); - toymatch_val_to_time(val, tm); - expire_time = now + (qemu_timedate_diff(tm) - s->offset_toy) * 1000; + toymatch_val_to_time(s, val, &tm); + expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000; timer_mod(s->toy_timer[num], expire_time); } } @@ -180,7 +156,7 @@ static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num) /* it do not support write when toy disabled */ if (rtc_enabled(s)) { s->rtcmatch[num] = val; - /* caculate expire time */ + /* calculate expire time */ expire_ns = ticks_to_ns(val) - ticks_to_ns(s->offset_rtc); timer_mod_ns(s->rtc_timer[num], expire_ns); } @@ -189,16 +165,8 @@ static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num) static void ls7a_toy_stop(LS7ARtcState *s) { int i; - struct tm tm; - /* - * save time when disabled toy, - * because toy time not add counters. - */ - qemu_get_timedate(&tm, s->offset_toy); - s->save_toy_mon = toy_time_to_val_mon(tm); - s->save_toy_year = toy_time_to_val_year(tm); - /* delete timers, and when re-enabled, recaculate expire time */ + /* delete timers, and when re-enabled, recalculate expire time */ for (i = 0; i < TIMER_NUMS; i++) { timer_del(s->toy_timer[i]); } @@ -207,13 +175,8 @@ static void ls7a_toy_stop(LS7ARtcState *s) static void ls7a_rtc_stop(LS7ARtcState *s) { int i; - uint64_t time; - /* save rtc time */ - time = ls7a_rtc_ticks() + s->offset_rtc; - s->save_rtc = time; - - /* delete timers, and when re-enabled, recaculate expire time */ + /* delete timers, and when re-enabled, recalculate expire time */ for (i = 0; i < TIMER_NUMS; i++) { timer_del(s->rtc_timer[i]); } @@ -223,20 +186,13 @@ static void ls7a_toy_start(LS7ARtcState *s) { int i; uint64_t expire_time, now; - struct tm tm; - /* - * need to recaculate toy offset - * and expire time when enable it. - */ - toy_val_to_time_mon(s->save_toy_mon, &tm); - toy_val_to_time_year(s->save_toy_year, &tm); + struct tm tm = {}; - s->offset_toy = qemu_timedate_diff(&tm); now = qemu_clock_get_ms(rtc_clock); - /* recaculate expire time and enable timer */ + /* recalculate expire time and enable timer */ for (i = 0; i < TIMER_NUMS; i++) { - toymatch_val_to_time(s->toymatch[i], &tm); + toymatch_val_to_time(s, s->toymatch[i], &tm); expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000; timer_mod(s->toy_timer[i], expire_time); } @@ -245,16 +201,9 @@ static void ls7a_toy_start(LS7ARtcState *s) static void ls7a_rtc_start(LS7ARtcState *s) { int i; - uint64_t expire_time, now; + uint64_t expire_time; - /* - * need to recaculate rtc offset - * and expire time when enable it. - */ - now = ls7a_rtc_ticks(); - s->offset_rtc = s->save_rtc - now; - - /* recaculate expire time and enable timer */ + /* recalculate expire time and enable timer */ for (i = 0; i < TIMER_NUMS; i++) { expire_time = ticks_to_ns(s->rtcmatch[i]) - ticks_to_ns(s->offset_rtc); timer_mod_ns(s->rtc_timer[i], expire_time); @@ -269,23 +218,21 @@ static uint64_t ls7a_rtc_read(void *opaque, hwaddr addr, unsigned size) switch (addr) { case SYS_TOYREAD0: - /* if toy disabled, read save toy time */ if (toy_enabled(s)) { qemu_get_timedate(&tm, s->offset_toy); - val = toy_time_to_val_mon(tm); + val = toy_time_to_val_mon(&tm); } else { - /* read save mon val */ - val = s->save_toy_mon; + /* return 0 when toy disabled */ + val = 0; } break; case SYS_TOYREAD1: - /* if toy disabled, read save toy time */ if (toy_enabled(s)) { qemu_get_timedate(&tm, s->offset_toy); val = tm.tm_year; } else { - /* read save year val */ - val = s->save_toy_year; + /* return 0 when toy disabled */ + val = 0; } break; case SYS_TOYMATCH0: @@ -301,11 +248,11 @@ static uint64_t ls7a_rtc_read(void *opaque, hwaddr addr, unsigned size) val = s->cntrctl; break; case SYS_RTCREAD0: - /* if rtc disabled, read save rtc time */ if (rtc_enabled(s)) { val = ls7a_rtc_ticks() + s->offset_rtc; } else { - val = s->save_rtc; + /* return 0 when rtc disabled */ + val = 0; } break; case SYS_RTCMATCH0: @@ -352,13 +299,13 @@ static void ls7a_rtc_write(void *opaque, hwaddr addr, } break; case SYS_TOYMATCH0: - toymatch_write(s, &tm, val, 0); + toymatch_write(s, val, 0); break; case SYS_TOYMATCH1: - toymatch_write(s, &tm, val, 1); + toymatch_write(s, val, 1); break; case SYS_TOYMATCH2: - toymatch_write(s, &tm, val, 2); + toymatch_write(s, val, 2); break; case SYS_RTCCTRL: /* get old ctrl */ @@ -423,7 +370,7 @@ static void toy_timer_cb(void *opaque) LS7ARtcState *s = opaque; if (toy_enabled(s)) { - qemu_irq_pulse(s->irq); + qemu_irq_raise(s->irq); } } @@ -432,7 +379,7 @@ static void rtc_timer_cb(void *opaque) LS7ARtcState *s = opaque; if (rtc_enabled(s)) { - qemu_irq_pulse(s->irq); + qemu_irq_raise(s->irq); } } @@ -455,11 +402,26 @@ static void ls7a_rtc_realize(DeviceState *dev, Error **errp) } d->offset_toy = 0; d->offset_rtc = 0; - d->save_toy_mon = 0; - d->save_toy_year = 0; - d->save_rtc = 0; - create_unimplemented_device("mmio fallback 1", 0x10013ffc, 0x4); +} + +/* delete timer and clear reg when reset */ +static void ls7a_rtc_reset(DeviceState *dev) +{ + int i; + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + LS7ARtcState *d = LS7A_RTC(sbd); + for (i = 0; i < TIMER_NUMS; i++) { + if (toy_enabled(d)) { + timer_del(d->toy_timer[i]); + } + if (rtc_enabled(d)) { + timer_del(d->rtc_timer[i]); + } + d->toymatch[i] = 0; + d->rtcmatch[i] = 0; + } + d->cntrctl = 0; } static int ls7a_rtc_pre_save(void *opaque) @@ -492,12 +454,9 @@ static const VMStateDescription vmstate_ls7a_rtc = { .minimum_version_id = 1, .pre_save = ls7a_rtc_pre_save, .post_load = ls7a_rtc_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64(offset_toy, LS7ARtcState), VMSTATE_INT64(offset_rtc, LS7ARtcState), - VMSTATE_UINT64(save_toy_mon, LS7ARtcState), - VMSTATE_UINT64(save_toy_year, LS7ARtcState), - VMSTATE_UINT64(save_rtc, LS7ARtcState), VMSTATE_UINT32_ARRAY(toymatch, LS7ARtcState, TIMER_NUMS), VMSTATE_UINT32_ARRAY(rtcmatch, LS7ARtcState, TIMER_NUMS), VMSTATE_UINT32(cntrctl, LS7ARtcState), @@ -510,6 +469,7 @@ static void ls7a_rtc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_ls7a_rtc; dc->realize = ls7a_rtc_realize; + dc->reset = ls7a_rtc_reset; dc->desc = "ls7a rtc"; } diff --git a/hw/rtc/m48t59-isa.c b/hw/rtc/m48t59-isa.c index e61f7ec370..5bb46f2383 100644 --- a/hw/rtc/m48t59-isa.c +++ b/hw/rtc/m48t59-isa.c @@ -47,7 +47,7 @@ struct M48txxISAState { }; struct M48txxISADeviceClass { - ISADeviceClass parent_class; + DeviceClass parent_class; M48txxInfo info; }; diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c index 74345d9d90..1585a2d399 100644 --- a/hw/rtc/m48t59.c +++ b/hw/rtc/m48t59.c @@ -36,6 +36,7 @@ #include "qemu/bcd.h" #include "qemu/module.h" #include "trace.h" +#include "sysemu/watchdog.h" #include "m48t59-internal.h" #include "migration/vmstate.h" @@ -93,9 +94,9 @@ static void alarm_cb (void *opaque) qemu_set_irq(NVRAM->IRQ, 1); if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 && - (NVRAM->buffer[0x1FF4] & 0x80) == 0 && - (NVRAM->buffer[0x1FF3] & 0x80) == 0 && - (NVRAM->buffer[0x1FF2] & 0x80) == 0) { + (NVRAM->buffer[0x1FF4] & 0x80) == 0 && + (NVRAM->buffer[0x1FF3] & 0x80) == 0 && + (NVRAM->buffer[0x1FF2] & 0x80) == 0) { /* Repeat once a month */ qemu_get_timedate(&tm, NVRAM->time_offset); tm.tm_mon++; @@ -105,21 +106,21 @@ static void alarm_cb (void *opaque) } next_time = qemu_timedate_diff(&tm) - NVRAM->time_offset; } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && - (NVRAM->buffer[0x1FF4] & 0x80) == 0 && - (NVRAM->buffer[0x1FF3] & 0x80) == 0 && - (NVRAM->buffer[0x1FF2] & 0x80) == 0) { + (NVRAM->buffer[0x1FF4] & 0x80) == 0 && + (NVRAM->buffer[0x1FF3] & 0x80) == 0 && + (NVRAM->buffer[0x1FF2] & 0x80) == 0) { /* Repeat once a day */ next_time = 24 * 60 * 60; } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && - (NVRAM->buffer[0x1FF4] & 0x80) != 0 && - (NVRAM->buffer[0x1FF3] & 0x80) == 0 && - (NVRAM->buffer[0x1FF2] & 0x80) == 0) { + (NVRAM->buffer[0x1FF4] & 0x80) != 0 && + (NVRAM->buffer[0x1FF3] & 0x80) == 0 && + (NVRAM->buffer[0x1FF2] & 0x80) == 0) { /* Repeat once an hour */ next_time = 60 * 60; } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && - (NVRAM->buffer[0x1FF4] & 0x80) != 0 && - (NVRAM->buffer[0x1FF3] & 0x80) != 0 && - (NVRAM->buffer[0x1FF2] & 0x80) == 0) { + (NVRAM->buffer[0x1FF4] & 0x80) != 0 && + (NVRAM->buffer[0x1FF3] & 0x80) != 0 && + (NVRAM->buffer[0x1FF2] & 0x80) == 0) { /* Repeat once a minute */ next_time = 60; } else { @@ -133,7 +134,7 @@ static void alarm_cb (void *opaque) static void set_alarm(M48t59State *NVRAM) { - int diff; + int64_t diff; if (NVRAM->alrm_timer != NULL) { timer_del(NVRAM->alrm_timer); diff = qemu_timedate_diff(&NVRAM->alarm) - NVRAM->time_offset; @@ -161,13 +162,12 @@ static void watchdog_cb (void *opaque) NVRAM->buffer[0x1FF0] |= 0x80; if (NVRAM->buffer[0x1FF7] & 0x80) { - NVRAM->buffer[0x1FF7] = 0x00; - NVRAM->buffer[0x1FFC] &= ~0x40; - /* May it be a hw CPU Reset instead ? */ - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + NVRAM->buffer[0x1FF7] = 0x00; + NVRAM->buffer[0x1FFC] &= ~0x40; + watchdog_perform_action(); } else { - qemu_set_irq(NVRAM->IRQ, 1); - qemu_set_irq(NVRAM->IRQ, 0); + qemu_set_irq(NVRAM->IRQ, 1); + qemu_set_irq(NVRAM->IRQ, 0); } } @@ -262,80 +262,80 @@ void m48t59_write(M48t59State *NVRAM, uint32_t addr, uint32_t val) case 0x1FF9: case 0x07F9: /* seconds (BCD) */ - tmp = from_bcd(val & 0x7F); - if (tmp >= 0 && tmp <= 59) { - get_time(NVRAM, &tm); - tm.tm_sec = tmp; - set_time(NVRAM, &tm); - } + tmp = from_bcd(val & 0x7F); + if (tmp >= 0 && tmp <= 59) { + get_time(NVRAM, &tm); + tm.tm_sec = tmp; + set_time(NVRAM, &tm); + } if ((val & 0x80) ^ (NVRAM->buffer[addr] & 0x80)) { - if (val & 0x80) { - NVRAM->stop_time = time(NULL); - } else { - NVRAM->time_offset += NVRAM->stop_time - time(NULL); - NVRAM->stop_time = 0; - } - } + if (val & 0x80) { + NVRAM->stop_time = time(NULL); + } else { + NVRAM->time_offset += NVRAM->stop_time - time(NULL); + NVRAM->stop_time = 0; + } + } NVRAM->buffer[addr] = val & 0x80; break; case 0x1FFA: case 0x07FA: /* minutes (BCD) */ - tmp = from_bcd(val & 0x7F); - if (tmp >= 0 && tmp <= 59) { - get_time(NVRAM, &tm); - tm.tm_min = tmp; - set_time(NVRAM, &tm); - } + tmp = from_bcd(val & 0x7F); + if (tmp >= 0 && tmp <= 59) { + get_time(NVRAM, &tm); + tm.tm_min = tmp; + set_time(NVRAM, &tm); + } break; case 0x1FFB: case 0x07FB: /* hours (BCD) */ - tmp = from_bcd(val & 0x3F); - if (tmp >= 0 && tmp <= 23) { - get_time(NVRAM, &tm); - tm.tm_hour = tmp; - set_time(NVRAM, &tm); - } + tmp = from_bcd(val & 0x3F); + if (tmp >= 0 && tmp <= 23) { + get_time(NVRAM, &tm); + tm.tm_hour = tmp; + set_time(NVRAM, &tm); + } break; case 0x1FFC: case 0x07FC: /* day of the week / century */ - tmp = from_bcd(val & 0x07); - get_time(NVRAM, &tm); - tm.tm_wday = tmp; - set_time(NVRAM, &tm); + tmp = from_bcd(val & 0x07); + get_time(NVRAM, &tm); + tm.tm_wday = tmp; + set_time(NVRAM, &tm); NVRAM->buffer[addr] = val & 0x40; break; case 0x1FFD: case 0x07FD: /* date (BCD) */ - tmp = from_bcd(val & 0x3F); - if (tmp != 0) { - get_time(NVRAM, &tm); - tm.tm_mday = tmp; - set_time(NVRAM, &tm); - } + tmp = from_bcd(val & 0x3F); + if (tmp != 0) { + get_time(NVRAM, &tm); + tm.tm_mday = tmp; + set_time(NVRAM, &tm); + } break; case 0x1FFE: case 0x07FE: /* month */ - tmp = from_bcd(val & 0x1F); - if (tmp >= 1 && tmp <= 12) { - get_time(NVRAM, &tm); - tm.tm_mon = tmp - 1; - set_time(NVRAM, &tm); - } + tmp = from_bcd(val & 0x1F); + if (tmp >= 1 && tmp <= 12) { + get_time(NVRAM, &tm); + tm.tm_mon = tmp - 1; + set_time(NVRAM, &tm); + } break; case 0x1FFF: case 0x07FF: /* year */ - tmp = from_bcd(val); - if (tmp >= 0 && tmp <= 99) { - get_time(NVRAM, &tm); + tmp = from_bcd(val); + if (tmp >= 0 && tmp <= 99) { + get_time(NVRAM, &tm); tm.tm_year = from_bcd(val) + NVRAM->base_year - 1900; - set_time(NVRAM, &tm); - } + set_time(NVRAM, &tm); + } break; default: /* Check lock registers state */ @@ -346,7 +346,7 @@ void m48t59_write(M48t59State *NVRAM, uint32_t addr, uint32_t val) do_write: if (addr < NVRAM->size) { NVRAM->buffer[addr] = val & 0xFF; - } + } break; } } @@ -367,34 +367,34 @@ uint32_t m48t59_read(M48t59State *NVRAM, uint32_t addr) switch (addr) { case 0x1FF0: /* flags register */ - goto do_read; + goto do_read; case 0x1FF1: /* unused */ - retval = 0; + retval = 0; break; case 0x1FF2: /* alarm seconds */ - goto do_read; + goto do_read; case 0x1FF3: /* alarm minutes */ - goto do_read; + goto do_read; case 0x1FF4: /* alarm hours */ - goto do_read; + goto do_read; case 0x1FF5: /* alarm date */ - goto do_read; + goto do_read; case 0x1FF6: /* interrupts */ - goto do_read; + goto do_read; case 0x1FF7: - /* A read resets the watchdog */ - set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]); - goto do_read; + /* A read resets the watchdog */ + set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]); + goto do_read; case 0x1FF8: case 0x07F8: /* control */ - goto do_read; + goto do_read; case 0x1FF9: case 0x07F9: /* seconds (BCD) */ @@ -446,7 +446,7 @@ uint32_t m48t59_read(M48t59State *NVRAM, uint32_t addr) do_read: if (addr < NVRAM->size) { retval = NVRAM->buffer[addr]; - } + } break; } trace_m48txx_nvram_mem_read(addr, retval); @@ -526,7 +526,7 @@ static const VMStateDescription vmstate_m48t59 = { .name = "m48t59", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(lock, M48t59State), VMSTATE_UINT16(addr, M48t59State), VMSTATE_VBUFFER_UINT32(buffer, M48t59State, 0, NULL, size), diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index f235c2ddbe..f4c1869232 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -26,7 +26,8 @@ #include "qemu/cutils.h" #include "qemu/module.h" #include "qemu/bcd.h" -#include "hw/acpi/aml-build.h" +#include "hw/acpi/acpi_aml_interface.h" +#include "hw/intc/kvm_irqcount.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" @@ -42,12 +43,6 @@ #include "qapi/error.h" #include "qapi/qapi-events-misc.h" #include "qapi/visitor.h" -#include "hw/rtc/mc146818rtc_regs.h" - -#ifdef TARGET_I386 -#include "qapi/qapi-commands-misc-target.h" -#include "hw/i386/apic.h" -#endif //#define DEBUG_CMOS //#define DEBUG_COALESCED @@ -74,19 +69,21 @@ #define RTC_CLOCK_RATE 32768 #define UIP_HOLD_LENGTH (8 * NANOSECONDS_PER_SECOND / 32768) -static void rtc_set_time(RTCState *s); -static void rtc_update_time(RTCState *s); -static void rtc_set_cmos(RTCState *s, const struct tm *tm); -static inline int rtc_from_bcd(RTCState *s, int a); -static uint64_t get_next_alarm(RTCState *s); +#define RTC_ISA_BASE 0x70 -static inline bool rtc_running(RTCState *s) +static void rtc_set_time(MC146818RtcState *s); +static void rtc_update_time(MC146818RtcState *s); +static void rtc_set_cmos(MC146818RtcState *s, const struct tm *tm); +static inline int rtc_from_bcd(MC146818RtcState *s, int a); +static uint64_t get_next_alarm(MC146818RtcState *s); + +static inline bool rtc_running(MC146818RtcState *s) { return (!(s->cmos_data[RTC_REG_B] & REG_B_SET) && (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20); } -static uint64_t get_guest_rtc_ns(RTCState *s) +static uint64_t get_guest_rtc_ns(MC146818RtcState *s) { uint64_t guest_clock = qemu_clock_get_ns(rtc_clock); @@ -94,7 +91,7 @@ static uint64_t get_guest_rtc_ns(RTCState *s) guest_clock - s->last_update + s->offset; } -static void rtc_coalesced_timer_update(RTCState *s) +static void rtc_coalesced_timer_update(MC146818RtcState *s) { if (s->irq_coalesced == 0) { timer_del(s->coalesced_timer); @@ -107,29 +104,28 @@ static void rtc_coalesced_timer_update(RTCState *s) } } -static QLIST_HEAD(, RTCState) rtc_devices = +static QLIST_HEAD(, MC146818RtcState) rtc_devices = QLIST_HEAD_INITIALIZER(rtc_devices); -#ifdef TARGET_I386 void qmp_rtc_reset_reinjection(Error **errp) { - RTCState *s; + MC146818RtcState *s; QLIST_FOREACH(s, &rtc_devices, link) { s->irq_coalesced = 0; } } -static bool rtc_policy_slew_deliver_irq(RTCState *s) +static bool rtc_policy_slew_deliver_irq(MC146818RtcState *s) { - apic_reset_irq_delivered(); + kvm_reset_irq_delivered(); qemu_irq_raise(s->irq); - return apic_get_irq_delivered(); + return kvm_get_irq_delivered(); } static void rtc_coalesced_timer(void *opaque) { - RTCState *s = opaque; + MC146818RtcState *s = opaque; if (s->irq_coalesced != 0) { s->cmos_data[RTC_REG_C] |= 0xc0; @@ -143,15 +139,8 @@ static void rtc_coalesced_timer(void *opaque) rtc_coalesced_timer_update(s); } -#else -static bool rtc_policy_slew_deliver_irq(RTCState *s) -{ - assert(0); - return false; -} -#endif -static uint32_t rtc_periodic_clock_ticks(RTCState *s) +static uint32_t rtc_periodic_clock_ticks(MC146818RtcState *s) { int period_code; @@ -168,8 +157,8 @@ static uint32_t rtc_periodic_clock_ticks(RTCState *s) * handle periodic timer. @old_period indicates the periodic timer update * is just due to period adjustment. */ -static void -periodic_timer_update(RTCState *s, int64_t current_time, uint32_t old_period, bool period_change) +static void periodic_timer_update(MC146818RtcState *s, int64_t current_time, + uint32_t old_period, bool period_change) { uint32_t period; int64_t cur_clock, next_irq_clock, lost_clock = 0; @@ -245,7 +234,7 @@ periodic_timer_update(RTCState *s, int64_t current_time, uint32_t old_period, bo static void rtc_periodic_timer(void *opaque) { - RTCState *s = opaque; + MC146818RtcState *s = opaque; periodic_timer_update(s, s->next_periodic_time, s->period, false); s->cmos_data[RTC_REG_C] |= REG_C_PF; @@ -266,7 +255,7 @@ static void rtc_periodic_timer(void *opaque) } /* handle update-ended timer */ -static void check_update_timer(RTCState *s) +static void check_update_timer(MC146818RtcState *s) { uint64_t next_update_time; uint64_t guest_nsec; @@ -317,7 +306,7 @@ static void check_update_timer(RTCState *s) } } -static inline uint8_t convert_hour(RTCState *s, uint8_t hour) +static inline uint8_t convert_hour(MC146818RtcState *s, uint8_t hour) { if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) { hour %= 12; @@ -328,7 +317,7 @@ static inline uint8_t convert_hour(RTCState *s, uint8_t hour) return hour; } -static uint64_t get_next_alarm(RTCState *s) +static uint64_t get_next_alarm(MC146818RtcState *s) { int32_t alarm_sec, alarm_min, alarm_hour, cur_hour, cur_min, cur_sec; int32_t hour, min, sec; @@ -421,7 +410,7 @@ static uint64_t get_next_alarm(RTCState *s) static void rtc_update_timer(void *opaque) { - RTCState *s = opaque; + MC146818RtcState *s = opaque; int32_t irqs = REG_C_UF; int32_t new_irqs; @@ -450,7 +439,7 @@ static void rtc_update_timer(void *opaque) static void cmos_ioport_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { - RTCState *s = opaque; + MC146818RtcState *s = opaque; uint32_t old_period; bool update_periodic_timer; @@ -568,7 +557,7 @@ static void cmos_ioport_write(void *opaque, hwaddr addr, } } -static inline int rtc_to_bcd(RTCState *s, int a) +static inline int rtc_to_bcd(MC146818RtcState *s, int a) { if (s->cmos_data[RTC_REG_B] & REG_B_DM) { return a; @@ -577,7 +566,7 @@ static inline int rtc_to_bcd(RTCState *s, int a) } } -static inline int rtc_from_bcd(RTCState *s, int a) +static inline int rtc_from_bcd(MC146818RtcState *s, int a) { if ((a & 0xc0) == 0xc0) { return -1; @@ -589,7 +578,7 @@ static inline int rtc_from_bcd(RTCState *s, int a) } } -static void rtc_get_time(RTCState *s, struct tm *tm) +static void rtc_get_time(MC146818RtcState *s, struct tm *tm) { tm->tm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]); tm->tm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]); @@ -608,9 +597,9 @@ static void rtc_get_time(RTCState *s, struct tm *tm) rtc_from_bcd(s, s->cmos_data[RTC_CENTURY]) * 100 - 1900; } -static void rtc_set_time(RTCState *s) +static void rtc_set_time(MC146818RtcState *s) { - struct tm tm; + struct tm tm = {}; g_autofree const char *qom_path = object_get_canonical_path(OBJECT(s)); rtc_get_time(s, &tm); @@ -620,7 +609,7 @@ static void rtc_set_time(RTCState *s) qapi_event_send_rtc_change(qemu_timedate_diff(&tm), qom_path); } -static void rtc_set_cmos(RTCState *s, const struct tm *tm) +static void rtc_set_cmos(MC146818RtcState *s, const struct tm *tm) { int year; @@ -644,7 +633,7 @@ static void rtc_set_cmos(RTCState *s, const struct tm *tm) s->cmos_data[RTC_CENTURY] = rtc_to_bcd(s, year / 100); } -static void rtc_update_time(RTCState *s) +static void rtc_update_time(MC146818RtcState *s) { struct tm ret; time_t guest_sec; @@ -660,7 +649,7 @@ static void rtc_update_time(RTCState *s) } } -static int update_in_progress(RTCState *s) +static int update_in_progress(MC146818RtcState *s) { int64_t guest_nsec; @@ -689,7 +678,7 @@ static int update_in_progress(RTCState *s) static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, unsigned size) { - RTCState *s = opaque; + MC146818RtcState *s = opaque; int ret; if ((addr & 1) == 0) { return 0xff; @@ -750,23 +739,21 @@ static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, } } -void rtc_set_memory(ISADevice *dev, int addr, int val) +void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val) { - RTCState *s = MC146818_RTC(dev); if (addr >= 0 && addr <= 127) s->cmos_data[addr] = val; } -int rtc_get_memory(ISADevice *dev, int addr) +int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr) { - RTCState *s = MC146818_RTC(dev); assert(addr >= 0 && addr <= 127); return s->cmos_data[addr]; } static void rtc_set_date_from_host(ISADevice *dev) { - RTCState *s = MC146818_RTC(dev); + MC146818RtcState *s = MC146818_RTC(dev); struct tm tm; qemu_get_timedate(&tm, 0); @@ -781,7 +768,7 @@ static void rtc_set_date_from_host(ISADevice *dev) static int rtc_pre_save(void *opaque) { - RTCState *s = opaque; + MC146818RtcState *s = opaque; rtc_update_time(s); @@ -790,7 +777,7 @@ static int rtc_pre_save(void *opaque) static int rtc_post_load(void *opaque, int version_id) { - RTCState *s = opaque; + MC146818RtcState *s = opaque; if (version_id <= 2 || rtc_clock == QEMU_CLOCK_REALTIME) { rtc_set_time(s); @@ -821,7 +808,7 @@ static int rtc_post_load(void *opaque, int version_id) static bool rtc_irq_reinject_on_ack_count_needed(void *opaque) { - RTCState *s = (RTCState *)opaque; + MC146818RtcState *s = (MC146818RtcState *)opaque; return s->irq_reinject_on_ack_count != 0; } @@ -830,8 +817,8 @@ static const VMStateDescription vmstate_rtc_irq_reinject_on_ack_count = { .version_id = 1, .minimum_version_id = 1, .needed = rtc_irq_reinject_on_ack_count_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT16(irq_reinject_on_ack_count, RTCState), + .fields = (const VMStateField[]) { + VMSTATE_UINT16(irq_reinject_on_ack_count, MC146818RtcState), VMSTATE_END_OF_LIST() } }; @@ -842,23 +829,23 @@ static const VMStateDescription vmstate_rtc = { .minimum_version_id = 1, .pre_save = rtc_pre_save, .post_load = rtc_post_load, - .fields = (VMStateField[]) { - VMSTATE_BUFFER(cmos_data, RTCState), - VMSTATE_UINT8(cmos_index, RTCState), + .fields = (const VMStateField[]) { + VMSTATE_BUFFER(cmos_data, MC146818RtcState), + VMSTATE_UINT8(cmos_index, MC146818RtcState), VMSTATE_UNUSED(7*4), - VMSTATE_TIMER_PTR(periodic_timer, RTCState), - VMSTATE_INT64(next_periodic_time, RTCState), + VMSTATE_TIMER_PTR(periodic_timer, MC146818RtcState), + VMSTATE_INT64(next_periodic_time, MC146818RtcState), VMSTATE_UNUSED(3*8), - VMSTATE_UINT32_V(irq_coalesced, RTCState, 2), - VMSTATE_UINT32_V(period, RTCState, 2), - VMSTATE_UINT64_V(base_rtc, RTCState, 3), - VMSTATE_UINT64_V(last_update, RTCState, 3), - VMSTATE_INT64_V(offset, RTCState, 3), - VMSTATE_TIMER_PTR_V(update_timer, RTCState, 3), - VMSTATE_UINT64_V(next_alarm_time, RTCState, 3), + VMSTATE_UINT32_V(irq_coalesced, MC146818RtcState, 2), + VMSTATE_UINT32_V(period, MC146818RtcState, 2), + VMSTATE_UINT64_V(base_rtc, MC146818RtcState, 3), + VMSTATE_UINT64_V(last_update, MC146818RtcState, 3), + VMSTATE_INT64_V(offset, MC146818RtcState, 3), + VMSTATE_TIMER_PTR_V(update_timer, MC146818RtcState, 3), + VMSTATE_UINT64_V(next_alarm_time, MC146818RtcState, 3), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_rtc_irq_reinject_on_ack_count, NULL } @@ -868,8 +855,9 @@ static const VMStateDescription vmstate_rtc = { BIOS will read it and start S3 resume at POST Entry */ static void rtc_notify_suspend(Notifier *notifier, void *data) { - RTCState *s = container_of(notifier, RTCState, suspend_notifier); - rtc_set_memory(ISA_DEVICE(s), 0xF, 0xFE); + MC146818RtcState *s = container_of(notifier, MC146818RtcState, + suspend_notifier); + mc146818rtc_set_cmos_data(s, 0xF, 0xFE); } static const MemoryRegionOps cmos_ops = { @@ -884,7 +872,7 @@ static const MemoryRegionOps cmos_ops = { static void rtc_get_date(Object *obj, struct tm *current_tm, Error **errp) { - RTCState *s = MC146818_RTC(obj); + MC146818RtcState *s = MC146818_RTC(obj); rtc_update_time(s); rtc_get_time(s, current_tm); @@ -893,7 +881,7 @@ static void rtc_get_date(Object *obj, struct tm *current_tm, Error **errp) static void rtc_realizefn(DeviceState *dev, Error **errp) { ISADevice *isadev = ISA_DEVICE(dev); - RTCState *s = MC146818_RTC(dev); + MC146818RtcState *s = MC146818_RTC(dev); s->cmos_data[RTC_REG_A] = 0x26; s->cmos_data[RTC_REG_B] = 0x02; @@ -920,12 +908,10 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) rtc_set_date_from_host(isadev); switch (s->lost_tick_policy) { -#ifdef TARGET_I386 case LOST_TICK_POLICY_SLEW: s->coalesced_timer = timer_new_ns(rtc_clock, rtc_coalesced_timer, s); break; -#endif case LOST_TICK_POLICY_DISCARD: break; default: @@ -941,7 +927,7 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) qemu_register_suspend_notifier(&s->suspend_notifier); memory_region_init_io(&s->io, OBJECT(s), &cmos_ops, s, "rtc", 2); - isa_register_ioport(isadev, &s->io, RTC_ISA_BASE); + isa_register_ioport(isadev, &s->io, s->io_base); /* register rtc 0x70 port for coalesced_pio */ memory_region_set_flush_coalesced(&s->io); @@ -950,7 +936,7 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->io, 0, &s->coalesced_io); memory_region_add_coalescing(&s->coalesced_io, 0, 1); - qdev_set_legacy_instance_id(dev, RTC_ISA_BASE, 3); + qdev_set_legacy_instance_id(dev, s->io_base, 3); object_property_add_tm(OBJECT(s), "date", rtc_get_date); @@ -958,11 +944,12 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) QLIST_INSERT_HEAD(&rtc_devices, s, link); } -ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) +MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year, + qemu_irq intercept_irq) { DeviceState *dev; ISADevice *isadev; - RTCState *s; + MC146818RtcState *s; isadev = isa_new(TYPE_MC146818_RTC); dev = DEVICE(isadev); @@ -978,20 +965,21 @@ ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(isadev), "date"); - return isadev; + return s; } static Property mc146818rtc_properties[] = { - DEFINE_PROP_INT32("base_year", RTCState, base_year, 1980), - DEFINE_PROP_UINT8("irq", RTCState, isairq, RTC_ISA_IRQ), - DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", RTCState, + DEFINE_PROP_INT32("base_year", MC146818RtcState, base_year, 1980), + DEFINE_PROP_UINT16("iobase", MC146818RtcState, io_base, RTC_ISA_BASE), + DEFINE_PROP_UINT8("irq", MC146818RtcState, isairq, RTC_ISA_IRQ), + DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", MC146818RtcState, lost_tick_policy, LOST_TICK_POLICY_DISCARD), DEFINE_PROP_END_OF_LIST(), }; static void rtc_reset_enter(Object *obj, ResetType type) { - RTCState *s = MC146818_RTC(obj); + MC146818RtcState *s = MC146818_RTC(obj); /* Reason: VM do suspend self will set 0xfe * Reset any values other than 0xfe(Guest suspend case) */ @@ -1012,14 +1000,14 @@ static void rtc_reset_enter(Object *obj, ResetType type) static void rtc_reset_hold(Object *obj) { - RTCState *s = MC146818_RTC(obj); + MC146818RtcState *s = MC146818_RTC(obj); qemu_irq_lower(s->irq); } -static void rtc_build_aml(ISADevice *isadev, Aml *scope) +static void rtc_build_aml(AcpiDevAmlIf *adev, Aml *scope) { - RTCState *s = MC146818_RTC(isadev); + MC146818RtcState *s = MC146818_RTC(adev); Aml *dev; Aml *crs; @@ -1028,7 +1016,7 @@ static void rtc_build_aml(ISADevice *isadev, Aml *scope) * does, even though qemu only responds to the first two ports. */ crs = aml_resource_template(); - aml_append(crs, aml_io(AML_DECODE16, RTC_ISA_BASE, RTC_ISA_BASE, + aml_append(crs, aml_io(AML_DECODE16, s->io_base, s->io_base, 0x01, 0x08)); aml_append(crs, aml_irq_no_flags(s->isairq)); @@ -1043,13 +1031,13 @@ static void rtc_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); - ISADeviceClass *isa = ISA_DEVICE_CLASS(klass); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); dc->realize = rtc_realizefn; dc->vmsd = &vmstate_rtc; rc->phases.enter = rtc_reset_enter; rc->phases.hold = rtc_reset_hold; - isa->build_aml = rtc_build_aml; + adevc->build_dev_aml = rtc_build_aml; device_class_set_props(dc, mc146818rtc_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } @@ -1057,8 +1045,12 @@ static void rtc_class_initfn(ObjectClass *klass, void *data) static const TypeInfo mc146818rtc_info = { .name = TYPE_MC146818_RTC, .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(RTCState), + .instance_size = sizeof(MC146818RtcState), .class_init = rtc_class_initfn, + .interfaces = (InterfaceInfo[]) { + { TYPE_ACPI_DEV_AML_IF }, + { }, + }, }; static void mc146818rtc_register_types(void) diff --git a/hw/rtc/meson.build b/hw/rtc/meson.build index dc33973384..3ea2affe0b 100644 --- a/hw/rtc/meson.build +++ b/hw/rtc/meson.build @@ -1,17 +1,16 @@ -softmmu_ss.add(when: 'CONFIG_DS1338', if_true: files('ds1338.c')) -softmmu_ss.add(when: 'CONFIG_M41T80', if_true: files('m41t80.c')) -softmmu_ss.add(when: 'CONFIG_M48T59', if_true: files('m48t59.c')) -softmmu_ss.add(when: 'CONFIG_PL031', if_true: files('pl031.c')) -softmmu_ss.add(when: 'CONFIG_TWL92230', if_true: files('twl92230.c')) -softmmu_ss.add(when: ['CONFIG_ISA_BUS', 'CONFIG_M48T59'], if_true: files('m48t59-isa.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-rtc.c')) +system_ss.add(when: 'CONFIG_DS1338', if_true: files('ds1338.c')) +system_ss.add(when: 'CONFIG_M41T80', if_true: files('m41t80.c')) +system_ss.add(when: 'CONFIG_M48T59', if_true: files('m48t59.c')) +system_ss.add(when: 'CONFIG_PL031', if_true: files('pl031.c')) +system_ss.add(when: 'CONFIG_TWL92230', if_true: files('twl92230.c')) +system_ss.add(when: ['CONFIG_ISA_BUS', 'CONFIG_M48T59'], if_true: files('m48t59-isa.c')) +system_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-rtc.c')) -softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_rtc.c')) -softmmu_ss.add(when: 'CONFIG_SUN4V_RTC', if_true: files('sun4v-rtc.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_rtc.c')) -softmmu_ss.add(when: 'CONFIG_GOLDFISH_RTC', if_true: files('goldfish_rtc.c')) -softmmu_ss.add(when: 'CONFIG_LS7A_RTC', if_true: files('ls7a_rtc.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-rtc.c')) - -specific_ss.add(when: 'CONFIG_MC146818RTC', if_true: files('mc146818rtc.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_rtc.c')) +system_ss.add(when: 'CONFIG_SUN4V_RTC', if_true: files('sun4v-rtc.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_rtc.c')) +system_ss.add(when: 'CONFIG_GOLDFISH_RTC', if_true: files('goldfish_rtc.c')) +system_ss.add(when: 'CONFIG_LS7A_RTC', if_true: files('ls7a_rtc.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-rtc.c')) +system_ss.add(when: 'CONFIG_MC146818RTC', if_true: files('mc146818rtc.c')) diff --git a/hw/rtc/pl031.c b/hw/rtc/pl031.c index b01d0e75d1..563bb4b446 100644 --- a/hw/rtc/pl031.c +++ b/hw/rtc/pl031.c @@ -141,6 +141,7 @@ static void pl031_write(void * opaque, hwaddr offset, g_autofree const char *qom_path = object_get_canonical_path(opaque); struct tm tm; + s->lr = value; s->tick_offset += value - pl031_get_count(s); qemu_get_timedate(&tm, s->tick_offset); @@ -290,7 +291,7 @@ static const VMStateDescription vmstate_pl031_tick_offset = { .minimum_version_id = 1, .needed = pl031_tick_offset_needed, .post_load = pl031_tick_offset_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(tick_offset, PL031State), VMSTATE_END_OF_LIST() } @@ -303,7 +304,7 @@ static const VMStateDescription vmstate_pl031 = { .pre_save = pl031_pre_save, .pre_load = pl031_pre_load, .post_load = pl031_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(tick_offset_vmstate, PL031State), VMSTATE_UINT32(mr, PL031State), VMSTATE_UINT32(lr, PL031State), @@ -312,7 +313,7 @@ static const VMStateDescription vmstate_pl031 = { VMSTATE_UINT32(is, PL031State), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_pl031_tick_offset, NULL } diff --git a/hw/rtc/sun4v-rtc.c b/hw/rtc/sun4v-rtc.c index e037acd1b5..ffcc0aa25d 100644 --- a/hw/rtc/sun4v-rtc.c +++ b/hw/rtc/sun4v-rtc.c @@ -5,7 +5,7 @@ * * Copyright (c) 2016 Artyom Tarasenko * - * This code is licensed under the GNU GPL v3 or (at your option) any later + * This code is licensed under the GNU GPL v2 or (at your option) any later * version. */ diff --git a/hw/rtc/twl92230.c b/hw/rtc/twl92230.c index e8d5eda3fc..efd19a76e6 100644 --- a/hw/rtc/twl92230.c +++ b/hw/rtc/twl92230.c @@ -65,8 +65,8 @@ struct MenelausState { struct tm tm; struct tm new; struct tm alm; - int sec_offset; - int alm_sec; + int64_t sec_offset; + int64_t alm_sec; int next_comp; } rtc; uint16_t rtc_next_vmstate; @@ -112,19 +112,19 @@ static void menelaus_rtc_hz(void *opaque) s->rtc.alm_sec --; s->rtc.next += 1000; timer_mod(s->rtc.hz_tm, s->rtc.next); - if ((s->rtc.ctrl >> 3) & 3) { /* EVERY */ + if ((s->rtc.ctrl >> 3) & 3) { /* EVERY */ menelaus_rtc_update(s); if (((s->rtc.ctrl >> 3) & 3) == 1 && !s->rtc.tm.tm_sec) - s->status |= 1 << 8; /* RTCTMR */ + s->status |= 1 << 8; /* RTCTMR */ else if (((s->rtc.ctrl >> 3) & 3) == 2 && !s->rtc.tm.tm_min) - s->status |= 1 << 8; /* RTCTMR */ + s->status |= 1 << 8; /* RTCTMR */ else if (!s->rtc.tm.tm_hour) - s->status |= 1 << 8; /* RTCTMR */ + s->status |= 1 << 8; /* RTCTMR */ } else - s->status |= 1 << 8; /* RTCTMR */ - if ((s->rtc.ctrl >> 1) & 1) { /* RTC_AL_EN */ + s->status |= 1 << 8; /* RTCTMR */ + if ((s->rtc.ctrl >> 1) & 1) { /* RTC_AL_EN */ if (s->rtc.alm_sec == 0) - s->status |= 1 << 9; /* RTCALM */ + s->status |= 1 << 9; /* RTCALM */ /* TODO: wake-up */ } if (s->rtc.next_comp <= 0) { @@ -140,19 +140,19 @@ static void menelaus_reset(I2CSlave *i2c) s->reg = 0x00; - s->vcore[0] = 0x0c; /* XXX: X-loader needs 0x8c? check! */ + s->vcore[0] = 0x0c; /* XXX: X-loader needs 0x8c? check! */ s->vcore[1] = 0x05; s->vcore[2] = 0x02; s->vcore[3] = 0x0c; s->vcore[4] = 0x03; - s->dcdc[0] = 0x33; /* Depends on wiring */ + s->dcdc[0] = 0x33; /* Depends on wiring */ s->dcdc[1] = 0x03; s->dcdc[2] = 0x00; s->ldo[0] = 0x95; s->ldo[1] = 0x7e; s->ldo[2] = 0x00; - s->ldo[3] = 0x00; /* Depends on wiring */ - s->ldo[4] = 0x03; /* Depends on wiring */ + s->ldo[3] = 0x00; /* Depends on wiring */ + s->ldo[4] = 0x03; /* Depends on wiring */ s->ldo[5] = 0x00; s->ldo[6] = 0x00; s->ldo[7] = 0x00; @@ -203,70 +203,70 @@ static void menelaus_gpio_set(void *opaque, int line, int level) } if (!s->pwrbtn_state && level) { - s->status |= 1 << 11; /* PSHBTN */ + s->status |= 1 << 11; /* PSHBTN */ menelaus_update(s); } s->pwrbtn_state = level; } -#define MENELAUS_REV 0x01 -#define MENELAUS_VCORE_CTRL1 0x02 -#define MENELAUS_VCORE_CTRL2 0x03 -#define MENELAUS_VCORE_CTRL3 0x04 -#define MENELAUS_VCORE_CTRL4 0x05 -#define MENELAUS_VCORE_CTRL5 0x06 -#define MENELAUS_DCDC_CTRL1 0x07 -#define MENELAUS_DCDC_CTRL2 0x08 -#define MENELAUS_DCDC_CTRL3 0x09 -#define MENELAUS_LDO_CTRL1 0x0a -#define MENELAUS_LDO_CTRL2 0x0b -#define MENELAUS_LDO_CTRL3 0x0c -#define MENELAUS_LDO_CTRL4 0x0d -#define MENELAUS_LDO_CTRL5 0x0e -#define MENELAUS_LDO_CTRL6 0x0f -#define MENELAUS_LDO_CTRL7 0x10 -#define MENELAUS_LDO_CTRL8 0x11 -#define MENELAUS_SLEEP_CTRL1 0x12 -#define MENELAUS_SLEEP_CTRL2 0x13 -#define MENELAUS_DEVICE_OFF 0x14 -#define MENELAUS_OSC_CTRL 0x15 -#define MENELAUS_DETECT_CTRL 0x16 -#define MENELAUS_INT_MASK1 0x17 -#define MENELAUS_INT_MASK2 0x18 -#define MENELAUS_INT_STATUS1 0x19 -#define MENELAUS_INT_STATUS2 0x1a -#define MENELAUS_INT_ACK1 0x1b -#define MENELAUS_INT_ACK2 0x1c -#define MENELAUS_GPIO_CTRL 0x1d -#define MENELAUS_GPIO_IN 0x1e -#define MENELAUS_GPIO_OUT 0x1f -#define MENELAUS_BBSMS 0x20 -#define MENELAUS_RTC_CTRL 0x21 -#define MENELAUS_RTC_UPDATE 0x22 -#define MENELAUS_RTC_SEC 0x23 -#define MENELAUS_RTC_MIN 0x24 -#define MENELAUS_RTC_HR 0x25 -#define MENELAUS_RTC_DAY 0x26 -#define MENELAUS_RTC_MON 0x27 -#define MENELAUS_RTC_YR 0x28 -#define MENELAUS_RTC_WKDAY 0x29 -#define MENELAUS_RTC_AL_SEC 0x2a -#define MENELAUS_RTC_AL_MIN 0x2b -#define MENELAUS_RTC_AL_HR 0x2c -#define MENELAUS_RTC_AL_DAY 0x2d -#define MENELAUS_RTC_AL_MON 0x2e -#define MENELAUS_RTC_AL_YR 0x2f -#define MENELAUS_RTC_COMP_MSB 0x30 -#define MENELAUS_RTC_COMP_LSB 0x31 -#define MENELAUS_S1_PULL_EN 0x32 -#define MENELAUS_S1_PULL_DIR 0x33 -#define MENELAUS_S2_PULL_EN 0x34 -#define MENELAUS_S2_PULL_DIR 0x35 -#define MENELAUS_MCT_CTRL1 0x36 -#define MENELAUS_MCT_CTRL2 0x37 -#define MENELAUS_MCT_CTRL3 0x38 -#define MENELAUS_MCT_PIN_ST 0x39 -#define MENELAUS_DEBOUNCE1 0x3a +#define MENELAUS_REV 0x01 +#define MENELAUS_VCORE_CTRL1 0x02 +#define MENELAUS_VCORE_CTRL2 0x03 +#define MENELAUS_VCORE_CTRL3 0x04 +#define MENELAUS_VCORE_CTRL4 0x05 +#define MENELAUS_VCORE_CTRL5 0x06 +#define MENELAUS_DCDC_CTRL1 0x07 +#define MENELAUS_DCDC_CTRL2 0x08 +#define MENELAUS_DCDC_CTRL3 0x09 +#define MENELAUS_LDO_CTRL1 0x0a +#define MENELAUS_LDO_CTRL2 0x0b +#define MENELAUS_LDO_CTRL3 0x0c +#define MENELAUS_LDO_CTRL4 0x0d +#define MENELAUS_LDO_CTRL5 0x0e +#define MENELAUS_LDO_CTRL6 0x0f +#define MENELAUS_LDO_CTRL7 0x10 +#define MENELAUS_LDO_CTRL8 0x11 +#define MENELAUS_SLEEP_CTRL1 0x12 +#define MENELAUS_SLEEP_CTRL2 0x13 +#define MENELAUS_DEVICE_OFF 0x14 +#define MENELAUS_OSC_CTRL 0x15 +#define MENELAUS_DETECT_CTRL 0x16 +#define MENELAUS_INT_MASK1 0x17 +#define MENELAUS_INT_MASK2 0x18 +#define MENELAUS_INT_STATUS1 0x19 +#define MENELAUS_INT_STATUS2 0x1a +#define MENELAUS_INT_ACK1 0x1b +#define MENELAUS_INT_ACK2 0x1c +#define MENELAUS_GPIO_CTRL 0x1d +#define MENELAUS_GPIO_IN 0x1e +#define MENELAUS_GPIO_OUT 0x1f +#define MENELAUS_BBSMS 0x20 +#define MENELAUS_RTC_CTRL 0x21 +#define MENELAUS_RTC_UPDATE 0x22 +#define MENELAUS_RTC_SEC 0x23 +#define MENELAUS_RTC_MIN 0x24 +#define MENELAUS_RTC_HR 0x25 +#define MENELAUS_RTC_DAY 0x26 +#define MENELAUS_RTC_MON 0x27 +#define MENELAUS_RTC_YR 0x28 +#define MENELAUS_RTC_WKDAY 0x29 +#define MENELAUS_RTC_AL_SEC 0x2a +#define MENELAUS_RTC_AL_MIN 0x2b +#define MENELAUS_RTC_AL_HR 0x2c +#define MENELAUS_RTC_AL_DAY 0x2d +#define MENELAUS_RTC_AL_MON 0x2e +#define MENELAUS_RTC_AL_YR 0x2f +#define MENELAUS_RTC_COMP_MSB 0x30 +#define MENELAUS_RTC_COMP_LSB 0x31 +#define MENELAUS_S1_PULL_EN 0x32 +#define MENELAUS_S1_PULL_DIR 0x33 +#define MENELAUS_S2_PULL_EN 0x34 +#define MENELAUS_S2_PULL_DIR 0x35 +#define MENELAUS_MCT_CTRL1 0x36 +#define MENELAUS_MCT_CTRL2 0x37 +#define MENELAUS_MCT_CTRL3 0x38 +#define MENELAUS_MCT_PIN_ST 0x39 +#define MENELAUS_DEBOUNCE1 0x3a static uint8_t menelaus_read(void *opaque, uint8_t addr) { @@ -293,7 +293,7 @@ static uint8_t menelaus_read(void *opaque, uint8_t addr) return 0; case MENELAUS_OSC_CTRL: - return s->osc | (1 << 7); /* CLK32K_GOOD */ + return s->osc | (1 << 7); /* CLK32K_GOOD */ case MENELAUS_DETECT_CTRL: return s->detect; @@ -334,9 +334,9 @@ static uint8_t menelaus_read(void *opaque, uint8_t addr) return to_bcd(s->rtc.tm.tm_min); case MENELAUS_RTC_HR: menelaus_rtc_update(s); - if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */ + if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */ return to_bcd((s->rtc.tm.tm_hour % 12) + 1) | - (!!(s->rtc.tm.tm_hour >= 12) << 7); /* PM_nAM */ + (!!(s->rtc.tm.tm_hour >= 12) << 7); /* PM_nAM */ else return to_bcd(s->rtc.tm.tm_hour); case MENELAUS_RTC_DAY: @@ -356,7 +356,7 @@ static uint8_t menelaus_read(void *opaque, uint8_t addr) case MENELAUS_RTC_AL_MIN: return to_bcd(s->rtc.alm.tm_min); case MENELAUS_RTC_AL_HR: - if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */ + if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */ return to_bcd((s->rtc.alm.tm_hour % 12) + 1) | (!!(s->rtc.alm.tm_hour >= 12) << 7);/* AL_PM_nAM */ else @@ -541,7 +541,7 @@ static void menelaus_write(void *opaque, uint8_t addr, uint8_t value) break; case MENELAUS_RTC_CTRL: - if ((s->rtc.ctrl ^ value) & 1) { /* RTC_EN */ + if ((s->rtc.ctrl ^ value) & 1) { /* RTC_EN */ if (value & 1) menelaus_rtc_start(s); else @@ -603,7 +603,7 @@ static void menelaus_write(void *opaque, uint8_t addr, uint8_t value) default: fprintf(stderr, "%s: bad RTC_UPDATE value %02x\n", __func__, value); - s->status |= 1 << 10; /* RTCERR */ + s->status |= 1 << 10; /* RTCERR */ menelaus_update(s); } s->rtc.sec_offset = qemu_timedate_diff(&tm); @@ -615,7 +615,7 @@ static void menelaus_write(void *opaque, uint8_t addr, uint8_t value) s->rtc.tm.tm_min = from_bcd(value & 0x7f); break; case MENELAUS_RTC_HR: - s->rtc.tm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */ + s->rtc.tm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */ MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) : from_bcd(value & 0x3f); break; @@ -640,7 +640,7 @@ static void menelaus_write(void *opaque, uint8_t addr, uint8_t value) menelaus_alm_update(s); break; case MENELAUS_RTC_AL_HR: - s->rtc.alm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */ + s->rtc.alm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */ MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) : from_bcd(value & 0x3f); menelaus_alm_update(s); @@ -768,7 +768,7 @@ static const VMStateDescription vmstate_menelaus_tm = { .name = "menelaus_tm", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16_HACK(tm_sec, struct tm), VMSTATE_UINT16_HACK(tm_min, struct tm), VMSTATE_UINT16_HACK(tm_hour, struct tm), @@ -792,14 +792,14 @@ static int menelaus_post_load(void *opaque, int version_id) { MenelausState *s = opaque; - if (s->rtc.ctrl & 1) /* RTC_EN */ + if (s->rtc.ctrl & 1) /* RTC_EN */ menelaus_rtc_stop(s); s->rtc.next = s->rtc_next_vmstate; menelaus_alm_update(s); menelaus_update(s); - if (s->rtc.ctrl & 1) /* RTC_EN */ + if (s->rtc.ctrl & 1) /* RTC_EN */ menelaus_rtc_start(s); return 0; } @@ -810,7 +810,7 @@ static const VMStateDescription vmstate_menelaus = { .minimum_version_id = 0, .pre_save = menelaus_pre_save, .post_load = menelaus_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(firstbyte, MenelausState), VMSTATE_UINT8(reg, MenelausState), VMSTATE_UINT8_ARRAY(vcore, MenelausState, 5), diff --git a/hw/rtc/xlnx-zynqmp-rtc.c b/hw/rtc/xlnx-zynqmp-rtc.c index 3e7d61a41c..613c6407a6 100644 --- a/hw/rtc/xlnx-zynqmp-rtc.c +++ b/hw/rtc/xlnx-zynqmp-rtc.c @@ -244,7 +244,7 @@ static const VMStateDescription vmstate_rtc = { .minimum_version_id = 1, .pre_save = rtc_pre_save, .post_load = rtc_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPRTC, XLNX_ZYNQMP_RTC_R_MAX), VMSTATE_UINT32(tick_offset, XlnxZynqMPRTC), VMSTATE_END_OF_LIST(), diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c index be147b4bd9..bb4746c556 100644 --- a/hw/rx/rx-gdbsim.c +++ b/hw/rx/rx-gdbsim.c @@ -19,11 +19,14 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/loader.h" #include "hw/rx/rx62n.h" #include "sysemu/qtest.h" #include "sysemu/device_tree.h" +#include "sysemu/reset.h" #include "hw/boards.h" #include "qom/object.h" @@ -83,6 +86,7 @@ static void rx_gdbsim_init(MachineState *machine) MemoryRegion *sysmem = get_system_memory(); const char *kernel_filename = machine->kernel_filename; const char *dtb_filename = machine->dtb; + uint8_t rng_seed[32]; if (machine->ram_size < mc->default_ram_size) { char *sz = size_to_str(mc->default_ram_size); @@ -140,10 +144,14 @@ static void rx_gdbsim_init(MachineState *machine) error_report("Couldn't set /chosen/bootargs"); exit(1); } + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + qemu_fdt_setprop(dtb, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); /* DTB is located at the end of SDRAM space. */ dtb_offset = ROUND_DOWN(machine->ram_size - dtb_size, 16); rom_add_blob_fixed("dtb", dtb, dtb_size, SDRAM_BASE + dtb_offset); + qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, + rom_ptr(SDRAM_BASE + dtb_offset, dtb_size)); /* Set dtb address to R1 */ RX_CPU(first_cpu)->env.regs[1] = SDRAM_BASE + dtb_offset; } diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c index fa5add9f9d..560f53a58a 100644 --- a/hw/rx/rx62n.c +++ b/hw/rx/rx62n.c @@ -23,11 +23,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/error-report.h" +#include "qemu/units.h" #include "hw/rx/rx62n.h" #include "hw/loader.h" #include "hw/sysbus.h" #include "hw/qdev-properties.h" #include "sysemu/sysemu.h" +#include "qapi/qmp/qlist.h" #include "qom/object.h" /* @@ -114,7 +116,7 @@ static const uint8_t ipr_table[NR_IRQS] = { }; /* - * Level triggerd IRQ list + * Level triggered IRQ list * Not listed IRQ is Edge trigger. * See "11.3.1 Interrupt Vector Table" in hardware manual. */ @@ -130,31 +132,28 @@ static void register_icu(RX62NState *s) { int i; SysBusDevice *icu; + QList *ipr_map, *trigger_level; object_initialize_child(OBJECT(s), "icu", &s->icu, TYPE_RX_ICU); icu = SYS_BUS_DEVICE(&s->icu); - qdev_prop_set_uint32(DEVICE(icu), "len-ipr-map", NR_IRQS); - for (i = 0; i < NR_IRQS; i++) { - char propname[32]; - snprintf(propname, sizeof(propname), "ipr-map[%d]", i); - qdev_prop_set_uint32(DEVICE(icu), propname, ipr_table[i]); - } - qdev_prop_set_uint32(DEVICE(icu), "len-trigger-level", - ARRAY_SIZE(levelirq)); - for (i = 0; i < ARRAY_SIZE(levelirq); i++) { - char propname[32]; - snprintf(propname, sizeof(propname), "trigger-level[%d]", i); - qdev_prop_set_uint32(DEVICE(icu), propname, levelirq[i]); - } + ipr_map = qlist_new(); for (i = 0; i < NR_IRQS; i++) { - s->irq[i] = qdev_get_gpio_in(DEVICE(icu), i); + qlist_append_int(ipr_map, ipr_table[i]); } + qdev_prop_set_array(DEVICE(icu), "ipr-map", ipr_map); + + trigger_level = qlist_new(); + for (i = 0; i < ARRAY_SIZE(levelirq); i++) { + qlist_append_int(trigger_level, levelirq[i]); + } + qdev_prop_set_array(DEVICE(icu), "trigger-level", trigger_level); sysbus_realize(icu, &error_abort); + sysbus_connect_irq(icu, 0, qdev_get_gpio_in(DEVICE(&s->cpu), RX_CPU_IRQ)); sysbus_connect_irq(icu, 1, qdev_get_gpio_in(DEVICE(&s->cpu), RX_CPU_FIR)); - sysbus_connect_irq(icu, 2, s->irq[SWI]); - sysbus_mmio_map(SYS_BUS_DEVICE(icu), 0, RX62N_ICU_BASE); + sysbus_connect_irq(icu, 2, qdev_get_gpio_in(DEVICE(&s->icu), SWI)); + sysbus_mmio_map(icu, 0, RX62N_ICU_BASE); } static void register_tmr(RX62NState *s, int unit) @@ -170,7 +169,8 @@ static void register_tmr(RX62NState *s, int unit) irqbase = RX62N_TMR_IRQ + TMR_NR_IRQ * unit; for (i = 0; i < TMR_NR_IRQ; i++) { - sysbus_connect_irq(tmr, i, s->irq[irqbase + i]); + sysbus_connect_irq(tmr, i, + qdev_get_gpio_in(DEVICE(&s->icu), irqbase + i)); } sysbus_mmio_map(tmr, 0, RX62N_TMR_BASE + unit * 0x10); } @@ -188,7 +188,8 @@ static void register_cmt(RX62NState *s, int unit) irqbase = RX62N_CMT_IRQ + CMT_NR_IRQ * unit; for (i = 0; i < CMT_NR_IRQ; i++) { - sysbus_connect_irq(cmt, i, s->irq[irqbase + i]); + sysbus_connect_irq(cmt, i, + qdev_get_gpio_in(DEVICE(&s->icu), irqbase + i)); } sysbus_mmio_map(cmt, 0, RX62N_CMT_BASE + unit * 0x10); } @@ -207,7 +208,8 @@ static void register_sci(RX62NState *s, int unit) irqbase = RX62N_SCI_IRQ + SCI_NR_IRQ * unit; for (i = 0; i < SCI_NR_IRQ; i++) { - sysbus_connect_irq(sci, i, s->irq[irqbase + i]); + sysbus_connect_irq(sci, i, + qdev_get_gpio_in(DEVICE(&s->icu), irqbase + i)); } sysbus_mmio_map(sci, 0, RX62N_SCI_BASE + unit * 0x08); } diff --git a/hw/s390x/Kconfig b/hw/s390x/Kconfig index 5e7d8a2bae..26ad104485 100644 --- a/hw/s390x/Kconfig +++ b/hw/s390x/Kconfig @@ -5,8 +5,11 @@ config S390_CCW_VIRTIO imply VFIO_AP imply VFIO_CCW imply WDT_DIAG288 - select PCI + imply PCIE_DEVICES + imply IOMMUFD + select PCI_EXPRESS select S390_FLIC + select S390_FLIC_KVM if KVM select SCLPCONSOLE select VIRTIO_CCW select MSI_NONBROKEN diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index 95f269ab44..fb8c1acc64 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -66,7 +66,7 @@ const VMStateDescription vmstate_ccw_dev = { .name = "s390_ccw_dev", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_POINTER(sch, CcwDevice, vmstate_subch_dev, SubchDev), VMSTATE_END_OF_LIST() } diff --git a/hw/s390x/cpu-topology.c b/hw/s390x/cpu-topology.c new file mode 100644 index 0000000000..f16bdf65fa --- /dev/null +++ b/hw/s390x/cpu-topology.c @@ -0,0 +1,469 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * CPU Topology + * + * Copyright IBM Corp. 2022, 2023 + * Author(s): Pierre Morel + * + * S390 topology handling can be divided in two parts: + * + * - The first part in this file is taking care of all common functions + * used by KVM and TCG to create and modify the topology. + * + * - The second part, building the topology information data for the + * guest with CPU and KVM specificity will be implemented inside + * the target/s390/kvm sub tree. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "hw/qdev-properties.h" +#include "hw/boards.h" +#include "target/s390x/cpu.h" +#include "hw/s390x/s390-virtio-ccw.h" +#include "hw/s390x/cpu-topology.h" +#include "qapi/qapi-commands-machine-target.h" +#include "qapi/qapi-events-machine-target.h" + +/* + * s390_topology is used to keep the topology information. + * .cores_per_socket: tracks information on the count of cores + * per socket. + * .polarization: tracks machine polarization. + */ +S390Topology s390_topology = { + /* will be initialized after the CPU model is realized */ + .cores_per_socket = NULL, + .polarization = S390_CPU_POLARIZATION_HORIZONTAL, +}; + +/** + * s390_socket_nb: + * @cpu: s390x CPU + * + * Returns the socket number used inside the cores_per_socket array + * for a topology tree entry + */ +static int s390_socket_nb_from_ids(int drawer_id, int book_id, int socket_id) +{ + return (drawer_id * current_machine->smp.books + book_id) * + current_machine->smp.sockets + socket_id; +} + +/** + * s390_socket_nb: + * @cpu: s390x CPU + * + * Returns the socket number used inside the cores_per_socket array + * for a cpu. + */ +static int s390_socket_nb(S390CPU *cpu) +{ + return s390_socket_nb_from_ids(cpu->env.drawer_id, cpu->env.book_id, + cpu->env.socket_id); +} + +/** + * s390_has_topology: + * + * Return: true if the topology is supported by the machine. + */ +bool s390_has_topology(void) +{ + return s390_has_feat(S390_FEAT_CONFIGURATION_TOPOLOGY); +} + +/** + * s390_topology_init: + * @ms: the machine state where the machine topology is defined + * + * Keep track of the machine topology. + * + * Allocate an array to keep the count of cores per socket. + * The index of the array starts at socket 0 from book 0 and + * drawer 0 up to the maximum allowed by the machine topology. + */ +static void s390_topology_init(MachineState *ms) +{ + CpuTopology *smp = &ms->smp; + + s390_topology.cores_per_socket = g_new0(uint8_t, smp->sockets * + smp->books * smp->drawers); +} + +/* + * s390_handle_ptf: + * + * @register 1: contains the function code + * + * Function codes 0 (horizontal) and 1 (vertical) define the CPU + * polarization requested by the guest. + * + * Function code 2 is handling topology changes and is interpreted + * by the SIE. + */ +void s390_handle_ptf(S390CPU *cpu, uint8_t r1, uintptr_t ra) +{ + CpuS390Polarization polarization; + CPUS390XState *env = &cpu->env; + uint64_t reg = env->regs[r1]; + int fc = reg & S390_TOPO_FC_MASK; + + if (!s390_has_feat(S390_FEAT_CONFIGURATION_TOPOLOGY)) { + s390_program_interrupt(env, PGM_OPERATION, ra); + return; + } + + if (env->psw.mask & PSW_MASK_PSTATE) { + s390_program_interrupt(env, PGM_PRIVILEGED, ra); + return; + } + + if (reg & ~S390_TOPO_FC_MASK) { + s390_program_interrupt(env, PGM_SPECIFICATION, ra); + return; + } + + polarization = S390_CPU_POLARIZATION_VERTICAL; + switch (fc) { + case 0: + polarization = S390_CPU_POLARIZATION_HORIZONTAL; + /* fallthrough */ + case 1: + if (s390_topology.polarization == polarization) { + env->regs[r1] |= S390_PTF_REASON_DONE; + setcc(cpu, 2); + } else { + s390_topology.polarization = polarization; + s390_cpu_topology_set_changed(true); + qapi_event_send_cpu_polarization_change(polarization); + setcc(cpu, 0); + } + break; + default: + /* Note that fc == 2 is interpreted by the SIE */ + s390_program_interrupt(env, PGM_SPECIFICATION, ra); + } +} + +/** + * s390_topology_reset: + * + * Generic reset for CPU topology, calls s390_topology_reset() + * to reset the kernel Modified Topology Change Record. + */ +void s390_topology_reset(void) +{ + s390_cpu_topology_set_changed(false); + s390_topology.polarization = S390_CPU_POLARIZATION_HORIZONTAL; +} + +/** + * s390_topology_cpu_default: + * @cpu: pointer to a S390CPU + * @errp: Error pointer + * + * Setup the default topology if no attributes are already set. + * Passing a CPU with some, but not all, attributes set is considered + * an error. + * + * The function calculates the (drawer_id, book_id, socket_id) + * topology by filling the cores starting from the first socket + * (0, 0, 0) up to the last (smp->drawers, smp->books, smp->sockets). + * + * CPU type and dedication have defaults values set in the + * s390x_cpu_properties, entitlement must be adjust depending on the + * dedication. + * + * Returns false if it is impossible to setup a default topology + * true otherwise. + */ +static bool s390_topology_cpu_default(S390CPU *cpu, Error **errp) +{ + CpuTopology *smp = ¤t_machine->smp; + CPUS390XState *env = &cpu->env; + + /* All geometry topology attributes must be set or all unset */ + if ((env->socket_id < 0 || env->book_id < 0 || env->drawer_id < 0) && + (env->socket_id >= 0 || env->book_id >= 0 || env->drawer_id >= 0)) { + error_setg(errp, + "Please define all or none of the topology geometry attributes"); + return false; + } + + /* If one value is unset all are unset -> calculate defaults */ + if (env->socket_id < 0) { + env->socket_id = s390_std_socket(env->core_id, smp); + env->book_id = s390_std_book(env->core_id, smp); + env->drawer_id = s390_std_drawer(env->core_id, smp); + } + + /* + * When the user specifies the entitlement as 'auto' on the command line, + * QEMU will set the entitlement as: + * Medium when the CPU is not dedicated. + * High when dedicated is true. + */ + if (env->entitlement == S390_CPU_ENTITLEMENT_AUTO) { + if (env->dedicated) { + env->entitlement = S390_CPU_ENTITLEMENT_HIGH; + } else { + env->entitlement = S390_CPU_ENTITLEMENT_MEDIUM; + } + } + return true; +} + +/** + * s390_topology_check: + * @socket_id: socket to check + * @book_id: book to check + * @drawer_id: drawer to check + * @entitlement: entitlement to check + * @dedicated: dedication to check + * @errp: Error pointer + * + * The function checks if the topology + * attributes fits inside the system topology. + * + * Returns false if the specified topology does not match with + * the machine topology. + */ +static bool s390_topology_check(uint16_t socket_id, uint16_t book_id, + uint16_t drawer_id, uint16_t entitlement, + bool dedicated, Error **errp) +{ + CpuTopology *smp = ¤t_machine->smp; + + if (socket_id >= smp->sockets) { + error_setg(errp, "Unavailable socket: %d", socket_id); + return false; + } + if (book_id >= smp->books) { + error_setg(errp, "Unavailable book: %d", book_id); + return false; + } + if (drawer_id >= smp->drawers) { + error_setg(errp, "Unavailable drawer: %d", drawer_id); + return false; + } + if (entitlement >= S390_CPU_ENTITLEMENT__MAX) { + error_setg(errp, "Unknown entitlement: %d", entitlement); + return false; + } + if (dedicated && (entitlement == S390_CPU_ENTITLEMENT_LOW || + entitlement == S390_CPU_ENTITLEMENT_MEDIUM)) { + error_setg(errp, "A dedicated CPU implies high entitlement"); + return false; + } + return true; +} + +/** + * s390_topology_need_report + * @cpu: Current cpu + * @drawer_id: future drawer ID + * @book_id: future book ID + * @socket_id: future socket ID + * @entitlement: future entitlement + * @dedicated: future dedicated + * + * A modified topology change report is needed if the topology + * tree or the topology attributes change. + */ +static bool s390_topology_need_report(S390CPU *cpu, int drawer_id, + int book_id, int socket_id, + uint16_t entitlement, bool dedicated) +{ + return cpu->env.drawer_id != drawer_id || + cpu->env.book_id != book_id || + cpu->env.socket_id != socket_id || + cpu->env.entitlement != entitlement || + cpu->env.dedicated != dedicated; +} + +/** + * s390_update_cpu_props: + * @ms: the machine state + * @cpu: the CPU for which to update the properties from the environment. + * + */ +static void s390_update_cpu_props(MachineState *ms, S390CPU *cpu) +{ + CpuInstanceProperties *props; + + props = &ms->possible_cpus->cpus[cpu->env.core_id].props; + + props->socket_id = cpu->env.socket_id; + props->book_id = cpu->env.book_id; + props->drawer_id = cpu->env.drawer_id; +} + +/** + * s390_topology_setup_cpu: + * @ms: MachineState used to initialize the topology structure on + * first call. + * @cpu: the new S390CPU to insert in the topology structure + * @errp: the error pointer + * + * Called from CPU hotplug to check and setup the CPU attributes + * before the CPU is inserted in the topology. + * There is no need to update the MTCR explicitly here because it + * will be updated by KVM on creation of the new CPU. + */ +void s390_topology_setup_cpu(MachineState *ms, S390CPU *cpu, Error **errp) +{ + int entry; + + /* + * We do not want to initialize the topology if the CPU model + * does not support topology, consequently, we have to wait for + * the first CPU to be realized, which realizes the CPU model + * to initialize the topology structures. + * + * s390_topology_setup_cpu() is called from the CPU hotplug. + */ + if (!s390_topology.cores_per_socket) { + s390_topology_init(ms); + } + + if (!s390_topology_cpu_default(cpu, errp)) { + return; + } + + if (!s390_topology_check(cpu->env.socket_id, cpu->env.book_id, + cpu->env.drawer_id, cpu->env.entitlement, + cpu->env.dedicated, errp)) { + return; + } + + /* Do we still have space in the socket */ + entry = s390_socket_nb(cpu); + if (s390_topology.cores_per_socket[entry] >= ms->smp.cores) { + error_setg(errp, "No more space on this socket"); + return; + } + + /* Update the count of cores in sockets */ + s390_topology.cores_per_socket[entry] += 1; + + /* topology tree is reflected in props */ + s390_update_cpu_props(ms, cpu); +} + +static void s390_change_topology(uint16_t core_id, + bool has_socket_id, uint16_t socket_id, + bool has_book_id, uint16_t book_id, + bool has_drawer_id, uint16_t drawer_id, + bool has_entitlement, + CpuS390Entitlement entitlement, + bool has_dedicated, bool dedicated, + Error **errp) +{ + MachineState *ms = current_machine; + int old_socket_entry; + int new_socket_entry; + bool report_needed; + S390CPU *cpu; + + cpu = s390_cpu_addr2state(core_id); + if (!cpu) { + error_setg(errp, "Core-id %d does not exist!", core_id); + return; + } + + /* Get attributes not provided from cpu and verify the new topology */ + if (!has_socket_id) { + socket_id = cpu->env.socket_id; + } + if (!has_book_id) { + book_id = cpu->env.book_id; + } + if (!has_drawer_id) { + drawer_id = cpu->env.drawer_id; + } + if (!has_dedicated) { + dedicated = cpu->env.dedicated; + } + + /* + * When the user specifies the entitlement as 'auto' on the command line, + * QEMU will set the entitlement as: + * Medium when the CPU is not dedicated. + * High when dedicated is true. + */ + if (!has_entitlement || entitlement == S390_CPU_ENTITLEMENT_AUTO) { + if (dedicated) { + entitlement = S390_CPU_ENTITLEMENT_HIGH; + } else { + entitlement = S390_CPU_ENTITLEMENT_MEDIUM; + } + } + + if (!s390_topology_check(socket_id, book_id, drawer_id, + entitlement, dedicated, errp)) { + return; + } + + /* Check for space on new socket */ + old_socket_entry = s390_socket_nb(cpu); + new_socket_entry = s390_socket_nb_from_ids(drawer_id, book_id, socket_id); + + if (new_socket_entry != old_socket_entry) { + if (s390_topology.cores_per_socket[new_socket_entry] >= + ms->smp.cores) { + error_setg(errp, "No more space on this socket"); + return; + } + /* Update the count of cores in sockets */ + s390_topology.cores_per_socket[new_socket_entry] += 1; + s390_topology.cores_per_socket[old_socket_entry] -= 1; + } + + /* Check if we will need to report the modified topology */ + report_needed = s390_topology_need_report(cpu, drawer_id, book_id, + socket_id, entitlement, + dedicated); + + /* All checks done, report new topology into the vCPU */ + cpu->env.drawer_id = drawer_id; + cpu->env.book_id = book_id; + cpu->env.socket_id = socket_id; + cpu->env.dedicated = dedicated; + cpu->env.entitlement = entitlement; + + /* topology tree is reflected in props */ + s390_update_cpu_props(ms, cpu); + + /* Advertise the topology change */ + if (report_needed) { + s390_cpu_topology_set_changed(true); + } +} + +void qmp_set_cpu_topology(uint16_t core, + bool has_socket, uint16_t socket, + bool has_book, uint16_t book, + bool has_drawer, uint16_t drawer, + bool has_entitlement, CpuS390Entitlement entitlement, + bool has_dedicated, bool dedicated, + Error **errp) +{ + if (!s390_has_topology()) { + error_setg(errp, "This machine doesn't support topology"); + return; + } + + s390_change_topology(core, has_socket, socket, has_book, book, + has_drawer, drawer, has_entitlement, entitlement, + has_dedicated, dedicated, errp); +} + +CpuPolarizationInfo *qmp_query_s390x_cpu_polarization(Error **errp) +{ + CpuPolarizationInfo *info = g_new0(CpuPolarizationInfo, 1); + + info->polarization = s390_topology.polarization; + return info; +} diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index 4017081d49..34639f2143 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -56,7 +56,7 @@ static void ccw_device_unplug(HotplugHandler *hotplug_dev, qdev_unrealize(dev); } -static void virtual_css_bus_reset(BusState *qbus) +static void virtual_css_bus_reset_hold(Object *obj) { /* This should actually be modelled via the generic css */ css_reset(); @@ -81,8 +81,9 @@ static char *virtual_css_bus_get_dev_path(DeviceState *dev) static void virtual_css_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); - k->reset = virtual_css_bus_reset; + rc->phases.hold = virtual_css_bus_reset_hold; k->get_dev_path = virtual_css_bus_get_dev_path; } @@ -95,7 +96,6 @@ static const TypeInfo virtual_css_bus_info = { VirtualCssBus *virtual_css_bus_init(void) { - VirtualCssBus *cbus; BusState *bus; DeviceState *dev; @@ -103,19 +103,19 @@ VirtualCssBus *virtual_css_bus_init(void) dev = qdev_new(TYPE_VIRTUAL_CSS_BRIDGE); object_property_add_child(qdev_get_machine(), TYPE_VIRTUAL_CSS_BRIDGE, OBJECT(dev)); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); /* Create bus on bridge device */ bus = qbus_new(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css"); - cbus = VIRTUAL_CSS_BUS(bus); /* Enable hotplugging */ qbus_set_hotplug_handler(bus, OBJECT(dev)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + css_register_io_adapters(CSS_IO_ADAPTER_VIRTIO, true, false, 0, &error_abort); - return cbus; + return VIRTUAL_CSS_BUS(bus); } /***************** Virtual-css Bus Bridge Device ********************/ diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 7d9523f811..295530963a 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -32,7 +32,7 @@ static const VMStateDescription vmstate_crw = { .name = "s390_crw", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(flags, CRW), VMSTATE_UINT16(rsid, CRW), VMSTATE_END_OF_LIST() @@ -43,7 +43,7 @@ static const VMStateDescription vmstate_crw_container = { .name = "s390_crw_container", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(crw, CrwContainer, 0, vmstate_crw, CRW), VMSTATE_END_OF_LIST() }, @@ -59,7 +59,7 @@ static const VMStateDescription vmstate_chp_info = { .name = "s390_chp_info", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(in_use, ChpInfo), VMSTATE_UINT8(type, ChpInfo), VMSTATE_UINT8(is_virtual, ChpInfo), @@ -77,7 +77,7 @@ static const VMStateDescription vmstate_scsw = { .name = "s390_scsw", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(flags, SCSW), VMSTATE_UINT16(ctrl, SCSW), VMSTATE_UINT32(cpa, SCSW), @@ -92,7 +92,7 @@ static const VMStateDescription vmstate_pmcw = { .name = "s390_pmcw", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(intparm, PMCW), VMSTATE_UINT16(flags, PMCW), VMSTATE_UINT16(devno, PMCW), @@ -113,7 +113,7 @@ static const VMStateDescription vmstate_schib = { .name = "s390_schib", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(pmcw, SCHIB, 0, vmstate_pmcw, PMCW), VMSTATE_STRUCT(scsw, SCHIB, 0, vmstate_scsw, SCSW), VMSTATE_UINT64(mba, SCHIB), @@ -127,7 +127,7 @@ static const VMStateDescription vmstate_ccw1 = { .name = "s390_ccw1", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(cmd_code, CCW1), VMSTATE_UINT8(flags, CCW1), VMSTATE_UINT16(count, CCW1), @@ -140,7 +140,7 @@ static const VMStateDescription vmstate_ciw = { .name = "s390_ciw", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(type, CIW), VMSTATE_UINT8(command, CIW), VMSTATE_UINT16(count, CIW), @@ -152,7 +152,7 @@ static const VMStateDescription vmstate_sense_id = { .name = "s390_sense_id", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(reserved, SenseId), VMSTATE_UINT16(cu_type, SenseId), VMSTATE_UINT8(cu_model, SenseId), @@ -168,7 +168,7 @@ static const VMStateDescription vmstate_orb = { .name = "s390_orb", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(intparm, ORB), VMSTATE_UINT16(ctrl0, ORB), VMSTATE_UINT8(lpm, ORB), @@ -188,7 +188,7 @@ static const VMStateDescription vmstate_schdev_orb = { .version_id = 1, .minimum_version_id = 1, .needed = vmstate_schdev_orb_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(orb, SubchDev, 1, vmstate_orb, ORB), VMSTATE_END_OF_LIST() } @@ -207,7 +207,7 @@ const VMStateDescription vmstate_subch_dev = { .minimum_version_id = 1, .post_load = subch_dev_post_load, .pre_save = subch_dev_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_EQUAL(cssid, SubchDev, "Bug!"), VMSTATE_UINT8_EQUAL(ssid, SubchDev, "Bug!"), VMSTATE_UINT16(migrated_schid, SubchDev), @@ -223,7 +223,7 @@ const VMStateDescription vmstate_subch_dev = { VMSTATE_UINT8(ccw_no_data_cnt, SubchDev), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_schdev_orb, NULL } @@ -264,12 +264,12 @@ static int pre_save_ind_addr(void *opaque) return 0; } -const VMStateDescription vmstate_ind_addr_tmp = { +static const VMStateDescription vmstate_ind_addr_tmp = { .name = "s390_ind_addr_tmp", .pre_save = pre_save_ind_addr, .post_load = post_load_ind_addr, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(len, IndAddrPtrTmp), VMSTATE_UINT64(addr, IndAddrPtrTmp), VMSTATE_END_OF_LIST() @@ -278,7 +278,7 @@ const VMStateDescription vmstate_ind_addr_tmp = { const VMStateDescription vmstate_ind_addr = { .name = "s390_ind_addr_tmp", - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_WITH_TMP(IndAddr*, IndAddrPtrTmp, vmstate_ind_addr_tmp), VMSTATE_END_OF_LIST() } @@ -293,7 +293,7 @@ static const VMStateDescription vmstate_css_img = { .name = "s390_css_img", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { /* Subchannel sets have no relevant state. */ VMSTATE_STRUCT_ARRAY(chpids, CssImage, MAX_CHPID + 1, 0, vmstate_chp_info, ChpInfo), @@ -330,7 +330,7 @@ static const VMStateDescription vmstate_css = { .name = "s390_css", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_QTAILQ_V(pending_crws, ChannelSubSys, 1, vmstate_crw_container, CrwContainer, sibling), VMSTATE_BOOL(sei_pending, ChannelSubSys), @@ -644,8 +644,9 @@ void css_conditional_io_interrupt(SubchDev *sch) } } -int css_do_sic(CPUS390XState *env, uint8_t isc, uint16_t mode) +int css_do_sic(S390CPU *cpu, uint8_t isc, uint16_t mode) { + CPUS390XState *env = &cpu->env; S390FLICState *fs = s390_get_flic(); S390FLICStateClass *fsc = s390_get_flic_class(fs); int r; @@ -1522,21 +1523,37 @@ IOInstEnding css_do_xsch(SubchDev *sch) IOInstEnding css_do_csch(SubchDev *sch) { SCHIB *schib = &sch->curr_status; + uint16_t old_scsw_ctrl; + IOInstEnding ccode; if (~(schib->pmcw.flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { return IOINST_CC_NOT_OPERATIONAL; } + /* + * Save the current scsw.ctrl in case CSCH fails and we need + * to revert the scsw to the status quo ante. + */ + old_scsw_ctrl = schib->scsw.ctrl; + /* Trigger the clear function. */ schib->scsw.ctrl &= ~(SCSW_CTRL_MASK_FCTL | SCSW_CTRL_MASK_ACTL); schib->scsw.ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_ACTL_CLEAR_PEND; - return do_subchannel_work(sch); + ccode = do_subchannel_work(sch); + + if (ccode != IOINST_CC_EXPECTED) { + schib->scsw.ctrl = old_scsw_ctrl; + } + + return ccode; } IOInstEnding css_do_hsch(SubchDev *sch) { SCHIB *schib = &sch->curr_status; + uint16_t old_scsw_ctrl; + IOInstEnding ccode; if (~(schib->pmcw.flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { return IOINST_CC_NOT_OPERATIONAL; @@ -1553,6 +1570,12 @@ IOInstEnding css_do_hsch(SubchDev *sch) return IOINST_CC_BUSY; } + /* + * Save the current scsw.ctrl in case HSCH fails and we need + * to revert the scsw to the status quo ante. + */ + old_scsw_ctrl = schib->scsw.ctrl; + /* Trigger the halt function. */ schib->scsw.ctrl |= SCSW_FCTL_HALT_FUNC; schib->scsw.ctrl &= ~SCSW_FCTL_START_FUNC; @@ -1564,7 +1587,13 @@ IOInstEnding css_do_hsch(SubchDev *sch) } schib->scsw.ctrl |= SCSW_ACTL_HALT_PEND; - return do_subchannel_work(sch); + ccode = do_subchannel_work(sch); + + if (ccode != IOINST_CC_EXPECTED) { + schib->scsw.ctrl = old_scsw_ctrl; + } + + return ccode; } static void css_update_chnmon(SubchDev *sch) @@ -1605,6 +1634,8 @@ static void css_update_chnmon(SubchDev *sch) IOInstEnding css_do_ssch(SubchDev *sch, ORB *orb) { SCHIB *schib = &sch->curr_status; + uint16_t old_scsw_ctrl, old_scsw_flags; + IOInstEnding ccode; if (~(schib->pmcw.flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { return IOINST_CC_NOT_OPERATIONAL; @@ -1626,11 +1657,26 @@ IOInstEnding css_do_ssch(SubchDev *sch, ORB *orb) } sch->orb = *orb; sch->channel_prog = orb->cpa; + + /* + * Save the current scsw.ctrl and scsw.flags in case SSCH fails and we need + * to revert the scsw to the status quo ante. + */ + old_scsw_ctrl = schib->scsw.ctrl; + old_scsw_flags = schib->scsw.flags; + /* Trigger the start function. */ schib->scsw.ctrl |= (SCSW_FCTL_START_FUNC | SCSW_ACTL_START_PEND); schib->scsw.flags &= ~SCSW_FLAGS_MASK_PNO; - return do_subchannel_work(sch); + ccode = do_subchannel_work(sch); + + if (ccode != IOINST_CC_EXPECTED) { + schib->scsw.ctrl = old_scsw_ctrl; + schib->scsw.flags = old_scsw_flags; + } + + return ccode; } static void copy_irb_to_guest(IRB *dest, const IRB *src, const PMCW *pmcw, diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index faa51aa4c7..f9829de953 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -64,8 +64,7 @@ static bool event_pending(SCLPEventFacility *ef) SCLPEventClass *event_class; QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { - DeviceState *qdev = kid->child; - event = DO_UPCAST(SCLPEvent, qdev, qdev); + event = SCLP_EVENT(kid->child); event_class = SCLP_EVENT_GET_CLASS(event); if (event->event_pending && event_class->get_send_mask() & ef->receive_mask) { @@ -368,7 +367,7 @@ static const VMStateDescription vmstate_event_facility_mask64 = { .version_id = 0, .minimum_version_id = 0, .needed = vmstate_event_facility_mask64_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(receive_mask_pieces[RECV_MASK_LOWER], SCLPEventFacility), VMSTATE_END_OF_LIST() } @@ -379,7 +378,7 @@ static const VMStateDescription vmstate_event_facility_mask_length = { .version_id = 0, .minimum_version_id = 0, .needed = vmstate_event_facility_mask_length_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(mask_length, SCLPEventFacility), VMSTATE_END_OF_LIST() } @@ -389,11 +388,11 @@ static const VMStateDescription vmstate_event_facility = { .name = "vmstate-event-facility", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(receive_mask_pieces[RECV_MASK_UPPER], SCLPEventFacility), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_event_facility_mask64, &vmstate_event_facility_mask_length, NULL diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 8612684d48..e934bf89d1 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -26,7 +26,7 @@ #include "hw/s390x/vfio-ccw.h" #include "hw/s390x/css.h" #include "hw/s390x/ebcdic.h" -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" #include "hw/scsi/scsi.h" #include "hw/virtio/virtio-net.h" #include "ipl.h" @@ -35,7 +35,6 @@ #include "qemu/cutils.h" #include "qemu/option.h" #include "standard-headers/linux/virtio_ids.h" -#include "exec/exec-all.h" #define KERN_IMAGE_START 0x010000UL #define LINUX_MAGIC_ADDR 0x010008UL @@ -60,7 +59,7 @@ static const VMStateDescription vmstate_iplb_extended = { .version_id = 0, .minimum_version_id = 0, .needed = iplb_extended_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(reserved_ext, IplParameterBlock, 4096 - 200), VMSTATE_END_OF_LIST() } @@ -70,13 +69,13 @@ static const VMStateDescription vmstate_iplb = { .name = "ipl/iplb", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(reserved1, IplParameterBlock, 110), VMSTATE_UINT16(devno, IplParameterBlock), VMSTATE_UINT8_ARRAY(reserved2, IplParameterBlock, 88), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_iplb_extended, NULL } @@ -86,7 +85,7 @@ static const VMStateDescription vmstate_ipl = { .name = "ipl", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(compat_start_addr, S390IPLState), VMSTATE_UINT64(compat_bios_start_addr, S390IPLState), VMSTATE_STRUCT(iplb, S390IPLState, 0, vmstate_iplb, IplParameterBlock), @@ -703,7 +702,7 @@ static void s390_ipl_prepare_qipl(S390CPU *cpu) cpu_physical_memory_unmap(addr, len, 1, len); } -int s390_ipl_prepare_pv_header(void) +int s390_ipl_prepare_pv_header(Error **errp) { IplParameterBlock *ipib = s390_ipl_get_iplb_pv(); IPLBlockPV *ipib_pv = &ipib->pv; @@ -712,8 +711,7 @@ int s390_ipl_prepare_pv_header(void) cpu_physical_memory_read(ipib_pv->pv_header_addr, hdr, ipib_pv->pv_header_len); - rc = s390_pv_set_sec_parms((uintptr_t)hdr, - ipib_pv->pv_header_len); + rc = s390_pv_set_sec_parms((uintptr_t)hdr, ipib_pv->pv_header_len, errp); g_free(hdr); return rc; } diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index dfc6dfd89c..57cd125769 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -107,7 +107,7 @@ typedef union IplParameterBlock IplParameterBlock; int s390_ipl_set_loadparm(uint8_t *loadparm); void s390_ipl_update_diag308(IplParameterBlock *iplb); -int s390_ipl_prepare_pv_header(void); +int s390_ipl_prepare_pv_header(Error **errp); int s390_ipl_pv_unpack(void); void s390_ipl_prepare_cpu(S390CPU *cpu); IplParameterBlock *s390_ipl_get_iplb(void); @@ -140,7 +140,7 @@ void s390_ipl_clear_reset_request(void); * have an offset of 4 + n * 8 bytes within the struct in order * to keep it double-word aligned. * The total size of the struct must never exceed 28 bytes. - * This definition must be kept in sync with the defininition + * This definition must be kept in sync with the definition * in pc-bios/s390-ccw/iplb.h. */ struct QemuIplParameters { diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build index feefe0717e..482fd13420 100644 --- a/hw/s390x/meson.build +++ b/hw/s390x/meson.build @@ -22,7 +22,8 @@ s390x_ss.add(when: 'CONFIG_KVM', if_true: files( 'tod-kvm.c', 's390-skeys-kvm.c', 's390-stattrib-kvm.c', - 'pv.c', + 's390-pci-kvm.c', + 'cpu-topology.c', )) s390x_ss.add(when: 'CONFIG_TCG', if_true: files( 'tod-tcg.c', diff --git a/hw/s390x/pv.c b/hw/s390x/pv.c deleted file mode 100644 index 401b63d6cb..0000000000 --- a/hw/s390x/pv.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Protected Virtualization functions - * - * Copyright IBM Corp. 2020 - * Author(s): - * Janosch Frank - * - * This work is licensed under the terms of the GNU GPL, version 2 or (at - * your option) any later version. See the COPYING file in the top-level - * directory. - */ -#include "qemu/osdep.h" - -#include - -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "sysemu/kvm.h" -#include "qom/object_interfaces.h" -#include "exec/confidential-guest-support.h" -#include "hw/s390x/ipl.h" -#include "hw/s390x/pv.h" - -static int __s390_pv_cmd(uint32_t cmd, const char *cmdname, void *data) -{ - struct kvm_pv_cmd pv_cmd = { - .cmd = cmd, - .data = (uint64_t)data, - }; - int rc; - - do { - rc = kvm_vm_ioctl(kvm_state, KVM_S390_PV_COMMAND, &pv_cmd); - } while (rc == -EINTR); - - if (rc) { - error_report("KVM PV command %d (%s) failed: header rc %x rrc %x " - "IOCTL rc: %d", cmd, cmdname, pv_cmd.rc, pv_cmd.rrc, - rc); - } - return rc; -} - -/* - * This macro lets us pass the command as a string to the function so - * we can print it on an error. - */ -#define s390_pv_cmd(cmd, data) __s390_pv_cmd(cmd, #cmd, data); -#define s390_pv_cmd_exit(cmd, data) \ -{ \ - int rc; \ - \ - rc = __s390_pv_cmd(cmd, #cmd, data);\ - if (rc) { \ - exit(1); \ - } \ -} - -int s390_pv_vm_enable(void) -{ - return s390_pv_cmd(KVM_PV_ENABLE, NULL); -} - -void s390_pv_vm_disable(void) -{ - s390_pv_cmd_exit(KVM_PV_DISABLE, NULL); -} - -int s390_pv_set_sec_parms(uint64_t origin, uint64_t length) -{ - struct kvm_s390_pv_sec_parm args = { - .origin = origin, - .length = length, - }; - - return s390_pv_cmd(KVM_PV_SET_SEC_PARMS, &args); -} - -/* - * Called for each component in the SE type IPL parameter block 0. - */ -int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) -{ - struct kvm_s390_pv_unp args = { - .addr = addr, - .size = size, - .tweak = tweak, - }; - - return s390_pv_cmd(KVM_PV_UNPACK, &args); -} - -void s390_pv_prep_reset(void) -{ - s390_pv_cmd_exit(KVM_PV_PREP_RESET, NULL); -} - -int s390_pv_verify(void) -{ - return s390_pv_cmd(KVM_PV_VERIFY, NULL); -} - -void s390_pv_unshare(void) -{ - s390_pv_cmd_exit(KVM_PV_UNSHARE_ALL, NULL); -} - -void s390_pv_inject_reset_error(CPUState *cs) -{ - int r1 = (cs->kvm_run->s390_sieic.ipa & 0x00f0) >> 4; - CPUS390XState *env = &S390_CPU(cs)->env; - - /* Report that we are unable to enter protected mode */ - env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV; -} - -#define TYPE_S390_PV_GUEST "s390-pv-guest" -OBJECT_DECLARE_SIMPLE_TYPE(S390PVGuest, S390_PV_GUEST) - -/** - * S390PVGuest: - * - * The S390PVGuest object is basically a dummy used to tell the - * confidential guest support system to use s390's PV mechanism. - * - * # $QEMU \ - * -object s390-pv-guest,id=pv0 \ - * -machine ...,confidential-guest-support=pv0 - */ -struct S390PVGuest { - ConfidentialGuestSupport parent_obj; -}; - -typedef struct S390PVGuestClass S390PVGuestClass; - -struct S390PVGuestClass { - ConfidentialGuestSupportClass parent_class; -}; - -int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) -{ - if (!object_dynamic_cast(OBJECT(cgs), TYPE_S390_PV_GUEST)) { - return 0; - } - - if (!s390_has_feat(S390_FEAT_UNPACK)) { - error_setg(errp, - "CPU model does not support Protected Virtualization"); - return -1; - } - - cgs->ready = true; - - return 0; -} - -OBJECT_DEFINE_TYPE_WITH_INTERFACES(S390PVGuest, - s390_pv_guest, - S390_PV_GUEST, - CONFIDENTIAL_GUEST_SUPPORT, - { TYPE_USER_CREATABLE }, - { NULL }) - -static void s390_pv_guest_class_init(ObjectClass *oc, void *data) -{ -} - -static void s390_pv_guest_init(Object *obj) -{ -} - -static void s390_pv_guest_finalize(Object *obj) -{ -} diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c index 2fc8bb9c23..5261e66724 100644 --- a/hw/s390x/s390-ccw.c +++ b/hw/s390x/s390-ccw.c @@ -57,7 +57,7 @@ IOInstEnding s390_ccw_store(SubchDev *sch) /* * This code is called for both virtual and passthrough devices, - * but only applies to to the latter. This ugly check makes that + * but only applies to the latter. This ugly check makes that * distinction for us. */ if (object_dynamic_cast(OBJECT(sch->driver_data), TYPE_S390_CCW)) { @@ -76,7 +76,9 @@ static void s390_ccw_get_dev_info(S390CCWDevice *cdev, Error **errp) { unsigned int cssid, ssid, devid; - char dev_path[PATH_MAX] = {0}, *tmp; + char dev_path[PATH_MAX] = {0}; + g_autofree char *tmp_dir = NULL; + g_autofree char *tmp = NULL; if (!sysfsdev) { error_setg(errp, "No host device provided"); @@ -92,7 +94,8 @@ static void s390_ccw_get_dev_info(S390CCWDevice *cdev, cdev->mdevid = g_path_get_basename(dev_path); - tmp = basename(dirname(dev_path)); + tmp_dir = g_path_get_dirname(dev_path); + tmp = g_path_get_basename(tmp_dir); if (sscanf(tmp, "%2x.%1x.%4x", &cssid, &ssid, &devid) != 3) { error_setg_errno(errp, errno, "Failed to read %s", tmp); return; diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 4b2bdd94b3..3e57d5faca 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -16,6 +16,7 @@ #include "qapi/visitor.h" #include "hw/s390x/s390-pci-bus.h" #include "hw/s390x/s390-pci-inst.h" +#include "hw/s390x/s390-pci-kvm.h" #include "hw/s390x/s390-pci-vfio.h" #include "hw/pci/pci_bus.h" #include "hw/qdev-properties.h" @@ -23,17 +24,10 @@ #include "hw/pci/msi.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "sysemu/reset.h" +#include "sysemu/runstate.h" -#ifndef DEBUG_S390PCI_BUS -#define DEBUG_S390PCI_BUS 0 -#endif - -#define DPRINTF(fmt, ...) \ - do { \ - if (DEBUG_S390PCI_BUS) { \ - fprintf(stderr, "S390pci-bus: " fmt, ## __VA_ARGS__); \ - } \ - } while (0) +#include "trace.h" S390pciState *s390_get_phb(void) { @@ -129,7 +123,7 @@ void s390_pci_sclp_configure(SCCB *sccb) uint16_t rc; if (!pbdev) { - DPRINTF("sclp config no dev found\n"); + trace_s390_pci_sclp_nodev("configure", be32_to_cpu(psccb->aid)); rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED; goto out; } @@ -149,10 +143,22 @@ out: psccb->header.response_code = cpu_to_be16(rc); } +static void s390_pci_shutdown_notifier(Notifier *n, void *opaque) +{ + S390PCIBusDevice *pbdev = container_of(n, S390PCIBusDevice, + shutdown_notifier); + + pci_device_reset(pbdev->pdev); +} + static void s390_pci_perform_unplug(S390PCIBusDevice *pbdev) { HotplugHandler *hotplug_ctrl; + if (pbdev->pft == ZPCI_PFT_ISM) { + notifier_remove(&pbdev->shutdown_notifier); + } + /* Unplug the PCI device */ if (pbdev->pdev) { DeviceState *pdev = DEVICE(pbdev->pdev); @@ -176,7 +182,7 @@ void s390_pci_sclp_deconfigure(SCCB *sccb) uint16_t rc; if (!pbdev) { - DPRINTF("sclp deconfig no dev found\n"); + trace_s390_pci_sclp_nodev("deconfigure", be32_to_cpu(psccb->aid)); rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED; goto out; } @@ -189,7 +195,10 @@ void s390_pci_sclp_deconfigure(SCCB *sccb) rc = SCLP_RC_NO_ACTION_REQUIRED; break; default: - if (pbdev->summary_ind) { + if (pbdev->interp && (pbdev->fh & FH_MASK_ENABLE)) { + /* Interpreted devices were using interrupt forwarding */ + s390_pci_kvm_aif_disable(pbdev); + } else if (pbdev->summary_ind) { pci_dereg_irqs(pbdev); } if (pbdev->iommu->enabled) { @@ -547,7 +556,7 @@ static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr, return ret; } - DPRINTF("iommu trans addr 0x%" PRIx64 "\n", addr); + trace_s390_pci_iommu_xlate(addr); if (addr < iommu->pba || addr > iommu->pal) { error = ERR_EVENT_OORANGE; @@ -635,6 +644,10 @@ static AddressSpace *s390_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &iommu->as; } +static const PCIIOMMUOps s390_iommu_ops = { + .get_address_space = s390_pci_dma_iommu, +}; + static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set) { uint8_t expected, actual; @@ -666,8 +679,8 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, uint32_t sum_bit; assert(pbdev); - DPRINTF("write_msix data 0x%" PRIx64 " idx %d vec 0x%x\n", data, - pbdev->idx, vec); + + trace_s390_pci_msi_ctrl_write(data, pbdev->idx, vec); if (pbdev->state != ZPCI_FS_ENABLED) { return; @@ -744,13 +757,14 @@ static void s390_pci_iommu_free(S390pciState *s, PCIBus *bus, int32_t devfn) object_unref(OBJECT(iommu)); } -S390PCIGroup *s390_group_create(int id) +S390PCIGroup *s390_group_create(int id, int host_id) { S390PCIGroup *group; S390pciState *s = s390_get_phb(); group = g_new0(S390PCIGroup, 1); group->id = id; + group->host_id = host_id; QTAILQ_INSERT_TAIL(&s->zpci_groups, group, link); return group; } @@ -768,12 +782,25 @@ S390PCIGroup *s390_group_find(int id) return NULL; } +S390PCIGroup *s390_group_find_host_sim(int host_id) +{ + S390PCIGroup *group; + S390pciState *s = s390_get_phb(); + + QTAILQ_FOREACH(group, &s->zpci_groups, link) { + if (group->id >= ZPCI_SIM_GRP_START && group->host_id == host_id) { + return group; + } + } + return NULL; +} + static void s390_pci_init_default_group(void) { S390PCIGroup *group; ClpRspQueryPciGrp *resgrp; - group = s390_group_create(ZPCI_DEFAULT_FN_GRP); + group = s390_group_create(ZPCI_DEFAULT_FN_GRP, ZPCI_DEFAULT_FN_GRP); resgrp = &group->zpci_group; resgrp->fr = 1; resgrp->dasm = 0; @@ -803,12 +830,12 @@ static void s390_pcihost_realize(DeviceState *dev, Error **errp) PCIHostState *phb = PCI_HOST_BRIDGE(dev); S390pciState *s = S390_PCI_HOST_BRIDGE(dev); - DPRINTF("host_init\n"); + trace_s390_pcihost("realize"); b = pci_register_root_bus(dev, NULL, s390_pci_set_irq, s390_pci_map_irq, NULL, get_system_memory(), get_system_io(), 0, 64, TYPE_PCI_BUS); - pci_setup_iommu(b, s390_pci_dma_iommu, s); + pci_setup_iommu(b, &s390_iommu_ops, s); bus = BUS(b); qbus_set_hotplug_handler(bus, OBJECT(dev)); @@ -821,6 +848,7 @@ static void s390_pcihost_realize(DeviceState *dev, Error **errp) NULL, g_free); s->zpci_table = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, NULL); s->bus_no = 0; + s->next_sim_grp = ZPCI_SIM_GRP_START; QTAILQ_INIT(&s->pending_sei); QTAILQ_INIT(&s->zpci_devs); QTAILQ_INIT(&s->zpci_dma_limit); @@ -880,6 +908,10 @@ static int s390_pci_msix_init(S390PCIBusDevice *pbdev) static void s390_pci_msix_free(S390PCIBusDevice *pbdev) { + if (pbdev->msix.entries == 0) { + return; + } + memory_region_del_subregion(&pbdev->iommu->mr, &pbdev->msix_notify_mr); object_unparent(OBJECT(&pbdev->msix_notify_mr)); } @@ -971,19 +1003,58 @@ static void s390_pci_update_subordinate(PCIDevice *dev, uint32_t nr) } } +static int s390_pci_interp_plug(S390pciState *s, S390PCIBusDevice *pbdev) +{ + uint32_t idx, fh; + + if (!s390_pci_get_host_fh(pbdev, &fh)) { + return -EPERM; + } + + /* + * The host device is already in an enabled state, but we always present + * the initial device state to the guest as disabled (ZPCI_FS_DISABLED). + * Therefore, mask off the enable bit from the passthrough handle until + * the guest issues a CLP SET PCI FN later to enable the device. + */ + pbdev->fh = fh & ~FH_MASK_ENABLE; + + /* Next, see if the idx is already in-use */ + idx = pbdev->fh & FH_MASK_INDEX; + if (pbdev->idx != idx) { + if (s390_pci_find_dev_by_idx(s, idx)) { + return -EINVAL; + } + /* + * Update the idx entry with the passed through idx + * If the relinquished idx is lower than next_idx, use it + * to replace next_idx + */ + g_hash_table_remove(s->zpci_table, &pbdev->idx); + if (idx < s->next_idx) { + s->next_idx = idx; + } + pbdev->idx = idx; + g_hash_table_insert(s->zpci_table, &pbdev->idx, pbdev); + } + + return 0; +} + static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev); PCIDevice *pdev = NULL; S390PCIBusDevice *pbdev = NULL; + int rc; if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) { PCIBridge *pb = PCI_BRIDGE(dev); pdev = PCI_DEVICE(dev); pci_bridge_map_irq(pb, dev->id, s390_pci_map_irq); - pci_setup_iommu(&pb->sec_bus, s390_pci_dma_iommu, s); + pci_setup_iommu(&pb->sec_bus, &s390_iommu_ops, s); qbus_set_hotplug_handler(BUS(&pb->sec_bus), OBJECT(s)); @@ -1022,15 +1093,46 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, set_pbdev_info(pbdev); if (object_dynamic_cast(OBJECT(dev), "vfio-pci")) { - pbdev->fh |= FH_SHM_VFIO; + /* + * By default, interpretation is always requested; if the available + * facilities indicate it is not available, fallback to the + * interception model. + */ + if (pbdev->interp) { + if (s390_pci_kvm_interp_allowed()) { + rc = s390_pci_interp_plug(s, pbdev); + if (rc) { + error_setg(errp, "Plug failed for zPCI device in " + "interpretation mode: %d", rc); + return; + } + } else { + trace_s390_pcihost("zPCI interpretation missing"); + pbdev->interp = false; + pbdev->forwarding_assist = false; + } + } pbdev->iommu->dma_limit = s390_pci_start_dma_count(s, pbdev); /* Fill in CLP information passed via the vfio region */ s390_pci_get_clp_info(pbdev); + if (!pbdev->interp) { + /* Do vfio passthrough but intercept for I/O */ + pbdev->fh |= FH_SHM_VFIO; + pbdev->forwarding_assist = false; + } + /* Register shutdown notifier and reset callback for ISM devices */ + if (pbdev->pft == ZPCI_PFT_ISM) { + pbdev->shutdown_notifier.notify = s390_pci_shutdown_notifier; + qemu_register_shutdown_notifier(&pbdev->shutdown_notifier); + } } else { pbdev->fh |= FH_SHM_EMUL; + /* Always intercept emulated devices */ + pbdev->interp = false; + pbdev->forwarding_assist = false; } - if (s390_pci_msix_init(pbdev)) { + if (s390_pci_msix_init(pbdev) && !pbdev->interp) { error_setg(errp, "MSI-X support is mandatory " "in the S390 architecture"); return; @@ -1168,6 +1270,23 @@ static void s390_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev, pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, s->bus_no, 1); } +void s390_pci_ism_reset(void) +{ + S390pciState *s = s390_get_phb(); + + S390PCIBusDevice *pbdev, *next; + + /* Trigger reset event for each passthrough ISM device currently in-use */ + QTAILQ_FOREACH_SAFE(pbdev, &s->zpci_devs, link, next) { + if (pbdev->interp && pbdev->pft == ZPCI_PFT_ISM && + pbdev->fh & FH_MASK_ENABLE) { + s390_pci_kvm_aif_disable(pbdev); + + pci_device_reset(pbdev->pdev); + } + } +} + static void s390_pcihost_reset(DeviceState *dev) { S390pciState *s = S390_PCI_HOST_BRIDGE(dev); @@ -1177,7 +1296,10 @@ static void s390_pcihost_reset(DeviceState *dev) /* Process all pending unplug requests */ QTAILQ_FOREACH_SAFE(pbdev, &s->zpci_devs, link, next) { if (pbdev->unplug_requested) { - if (pbdev->summary_ind) { + if (pbdev->interp && (pbdev->fh & FH_MASK_ENABLE)) { + /* Interpreted devices were using interrupt forwarding */ + s390_pci_kvm_aif_disable(pbdev); + } else if (pbdev->summary_ind) { pci_dereg_irqs(pbdev); } if (pbdev->iommu->enabled) { @@ -1315,7 +1437,10 @@ static void s390_pci_device_reset(DeviceState *dev) break; } - if (pbdev->summary_ind) { + if (pbdev->interp && (pbdev->fh & FH_MASK_ENABLE)) { + /* Interpreted devices were using interrupt forwarding */ + s390_pci_kvm_aif_disable(pbdev); + } else if (pbdev->summary_ind) { pci_dereg_irqs(pbdev); } if (pbdev->iommu->enabled) { @@ -1360,6 +1485,9 @@ static Property s390_pci_device_properties[] = { DEFINE_PROP_UINT16("uid", S390PCIBusDevice, uid, UID_UNDEFINED), DEFINE_PROP_S390_PCI_FID("fid", S390PCIBusDevice, fid), DEFINE_PROP_STRING("target", S390PCIBusDevice, target), + DEFINE_PROP_BOOL("interpret", S390PCIBusDevice, interp, true), + DEFINE_PROP_BOOL("forwarding-assist", S390PCIBusDevice, forwarding_assist, + true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 6d400d4147..30149546c0 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -13,23 +13,17 @@ #include "qemu/osdep.h" #include "exec/memop.h" -#include "exec/memory-internal.h" +#include "exec/memory.h" #include "qemu/error-report.h" #include "sysemu/hw_accel.h" +#include "hw/pci/pci_device.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/s390x/s390-pci-bus.h" +#include "hw/s390x/s390-pci-kvm.h" +#include "hw/s390x/s390-pci-vfio.h" #include "hw/s390x/tod.h" -#ifndef DEBUG_S390PCI_INST -#define DEBUG_S390PCI_INST 0 -#endif - -#define DPRINTF(fmt, ...) \ - do { \ - if (DEBUG_S390PCI_INST) { \ - fprintf(stderr, "s390pci-inst: " fmt, ## __VA_ARGS__); \ - } \ - } while (0) +#include "trace.h" static inline void inc_dma_avail(S390PCIIOMMU *iommu) { @@ -130,8 +124,7 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) g_l2 += sizeof(ClpFhListEntry); /* Add endian check for DPRINTF? */ - DPRINTF("g_l2 %d vendor id 0x%x device id 0x%x fid 0x%x fh 0x%x\n", - g_l2, + trace_s390_pci_list_entry(g_l2, lduw_p(&rrb->response.fh_list[i].vendor_id), lduw_p(&rrb->response.fh_list[i].device_id), ldl_p(&rrb->response.fh_list[i].fid), @@ -150,7 +143,7 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) stw_p(&rrb->response.hdr.rsp, CLP_RC_OK); out: if (rc) { - DPRINTF("list pci failed rc 0x%x\n", rc); + trace_s390_pci_list(rc); stw_p(&rrb->response.hdr.rsp, res_code); } return rc; @@ -246,6 +239,20 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) goto out; } + /* + * Take this opportunity to make sure we still have an accurate + * host fh. It's possible part of the handle changed while the + * device was disabled to the guest (e.g. vfio hot reset for + * ISM during plug) + */ + if (pbdev->interp) { + /* Take this opportunity to make sure we are sync'd with host */ + if (!s390_pci_get_host_fh(pbdev, &pbdev->fh) || + !(pbdev->fh & FH_MASK_ENABLE)) { + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FH); + goto out; + } + } pbdev->fh |= FH_MASK_ENABLE; pbdev->state = ZPCI_FS_ENABLED; stl_p(&ressetpci->fh, pbdev->fh); @@ -256,14 +263,14 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); goto out; } - device_legacy_reset(DEVICE(pbdev)); + device_cold_reset(DEVICE(pbdev)); pbdev->fh &= ~FH_MASK_ENABLE; pbdev->state = ZPCI_FS_DISABLED; stl_p(&ressetpci->fh, pbdev->fh); stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); break; default: - DPRINTF("unknown set pci command\n"); + trace_s390_pci_unknown("set-pci", reqsetpci->oc); stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); break; } @@ -275,7 +282,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) pbdev = s390_pci_find_dev_by_fh(s, ldl_p(&reqquery->fh)); if (!pbdev) { - DPRINTF("query pci no pci dev\n"); + trace_s390_pci_nodev("query", ldl_p(&reqquery->fh)); stw_p(&resquery->hdr.rsp, CLP_RC_SETPCIFN_FH); goto out; } @@ -300,7 +307,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) stl_p(&resquery->bar[i], data); resquery->bar_size[i] = pbdev->pdev->io_regions[i].size ? ctz64(pbdev->pdev->io_regions[i].size) : 0; - DPRINTF("bar %d addr 0x%x size 0x%" PRIx64 "barsize 0x%x\n", i, + trace_s390_pci_bar(i, ldl_p(&resquery->bar[i]), pbdev->pdev->io_regions[i].size, resquery->bar_size[i]); @@ -334,7 +341,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) break; } default: - DPRINTF("unknown clp command\n"); + trace_s390_pci_unknown("clp", lduw_p(&reqh->cmd)); stw_p(&resh->rsp, CLP_RC_CMD); break; } @@ -442,7 +449,7 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) pbdev = s390_pci_find_dev_by_fh(s390_get_phb(), fh); if (!pbdev) { - DPRINTF("pcilg no pci dev\n"); + trace_s390_pci_nodev("pcilg", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } @@ -483,7 +490,7 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) } break; default: - DPRINTF("pcilg invalid space\n"); + trace_s390_pci_invalid("pcilg", fh); setcc(cpu, ZPCI_PCI_LS_ERR); s390_set_status_code(env, r2, ZPCI_PCI_ST_INVAL_AS); return 0; @@ -542,7 +549,7 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) pbdev = s390_pci_find_dev_by_fh(s390_get_phb(), fh); if (!pbdev) { - DPRINTF("pcistg no pci dev\n"); + trace_s390_pci_nodev("pcistg", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } @@ -591,7 +598,7 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) data, len); break; default: - DPRINTF("pcistg invalid space\n"); + trace_s390_pci_invalid("pcistg", fh); setcc(cpu, ZPCI_PCI_LS_ERR); s390_set_status_code(env, r2, ZPCI_PCI_ST_INVAL_AS); return 0; @@ -624,6 +631,8 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu, } g_hash_table_remove(iommu->iotlb, &entry->iova); inc_dma_avail(iommu); + /* Don't notify the iommu yet, maybe we can bundle contiguous unmaps */ + goto out; } else { if (cache) { if (cache->perm == entry->perm && @@ -647,22 +656,52 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu, dec_dma_avail(iommu); } + /* + * All associated iotlb entries have already been cleared, trigger the + * unmaps. + */ memory_region_notify_iommu(&iommu->iommu_mr, 0, event); out: return iommu->dma_limit ? iommu->dma_limit->avail : 1; } +static void s390_pci_batch_unmap(S390PCIIOMMU *iommu, uint64_t iova, + uint64_t len) +{ + uint64_t remain = len, start = iova, end = start + len - 1, mask, size; + IOMMUTLBEvent event = { + .type = IOMMU_NOTIFIER_UNMAP, + .entry = { + .target_as = &address_space_memory, + .translated_addr = 0, + .perm = IOMMU_NONE, + }, + }; + + while (remain >= TARGET_PAGE_SIZE) { + mask = dma_aligned_pow2_mask(start, end, 64); + size = mask + 1; + event.entry.iova = start; + event.entry.addr_mask = mask; + memory_region_notify_iommu(&iommu->iommu_mr, 0, event); + start += size; + remain -= size; + } +} + int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) { CPUS390XState *env = &cpu->env; + uint64_t iova, coalesce = 0; uint32_t fh; uint16_t error = 0; S390PCIBusDevice *pbdev; S390PCIIOMMU *iommu; S390IOTLBEntry entry; - hwaddr start, end; + hwaddr start, end, sstart; uint32_t dma_avail; + bool again; if (env->psw.mask & PSW_MASK_PSTATE) { s390_program_interrupt(env, PGM_PRIVILEGED, ra); @@ -675,12 +714,12 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) } fh = env->regs[r1] >> 32; - start = env->regs[r2]; + sstart = start = env->regs[r2]; end = start + env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(s390_get_phb(), fh); if (!pbdev) { - DPRINTF("rpcit no pci dev\n"); + trace_s390_pci_nodev("rpcit", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } @@ -716,20 +755,54 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) goto err; } + retry: + start = sstart; + again = false; while (start < end) { error = s390_guest_io_table_walk(iommu->g_iota, start, &entry); if (error) { break; } + /* + * If this is an unmap of a PTE, let's try to coalesce multiple unmaps + * into as few notifier events as possible. + */ + if (entry.perm == IOMMU_NONE && entry.len == TARGET_PAGE_SIZE) { + if (coalesce == 0) { + iova = entry.iova; + } + coalesce += entry.len; + } else if (coalesce > 0) { + /* Unleash the coalesced unmap before processing a new map */ + s390_pci_batch_unmap(iommu, iova, coalesce); + coalesce = 0; + } + start += entry.len; - while (entry.iova < start && entry.iova < end && - (dma_avail > 0 || entry.perm == IOMMU_NONE)) { - dma_avail = s390_pci_update_iotlb(iommu, &entry); - entry.iova += TARGET_PAGE_SIZE; - entry.translated_addr += TARGET_PAGE_SIZE; + while (entry.iova < start && entry.iova < end) { + if (dma_avail > 0 || entry.perm == IOMMU_NONE) { + dma_avail = s390_pci_update_iotlb(iommu, &entry); + entry.iova += TARGET_PAGE_SIZE; + entry.translated_addr += TARGET_PAGE_SIZE; + } else { + /* + * We are unable to make a new mapping at this time, continue + * on and hopefully free up more space. Then attempt another + * pass. + */ + again = true; + break; + } } } + if (coalesce) { + /* Unleash the coalesced unmap before finishing rpcit */ + s390_pci_batch_unmap(iommu, iova, coalesce); + coalesce = 0; + } + if (again && dma_avail > 0) + goto retry; err: if (error) { pbdev->state = ZPCI_FS_ERROR; @@ -780,7 +853,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, pbdev = s390_pci_find_dev_by_fh(s390_get_phb(), fh); if (!pbdev) { - DPRINTF("pcistb no pci dev fh 0x%x\n", fh); + trace_s390_pci_nodev("pcistb", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } @@ -796,7 +869,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, } if (pcias > ZPCI_IO_BAR_MAX) { - DPRINTF("pcistb invalid space\n"); + trace_s390_pci_invalid("pcistb", fh); setcc(cpu, ZPCI_PCI_LS_ERR); s390_set_status_code(env, r1, ZPCI_PCI_ST_INVAL_AS); return 0; @@ -888,7 +961,7 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) pbdev->noi = FIB_DATA_NOI(ldl_p(&fib.data)); pbdev->sum = FIB_DATA_SUM(ldl_p(&fib.data)); - DPRINTF("reg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id); + trace_s390_pci_irqs("register", pbdev->routes.adapter.adapter_id); return 0; out: release_indicator(&pbdev->routes.adapter, pbdev->summary_ind); @@ -913,7 +986,7 @@ int pci_dereg_irqs(S390PCIBusDevice *pbdev) pbdev->noi = 0; pbdev->sum = 0; - DPRINTF("dereg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id); + trace_s390_pci_irqs("unregister", pbdev->routes.adapter.adapter_id); return 0; } @@ -1050,6 +1123,32 @@ static void fmb_update(void *opaque) timer_mod(pbdev->fmb_timer, t + pbdev->pci_group->zpci_group.mui); } +static int mpcifc_reg_int_interp(S390PCIBusDevice *pbdev, ZpciFib *fib) +{ + int rc; + + rc = s390_pci_kvm_aif_enable(pbdev, fib, pbdev->forwarding_assist); + if (rc) { + trace_s390_pci_kvm_aif("enable"); + return rc; + } + + return 0; +} + +static int mpcifc_dereg_int_interp(S390PCIBusDevice *pbdev, ZpciFib *fib) +{ + int rc; + + rc = s390_pci_kvm_aif_disable(pbdev); + if (rc) { + trace_s390_pci_kvm_aif("disable"); + return rc; + } + + return 0; +} + int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, uintptr_t ra) { @@ -1076,7 +1175,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, pbdev = s390_pci_find_dev_by_fh(s390_get_phb(), fh); if (!pbdev) { - DPRINTF("mpcifc no pci dev fh 0x%x\n", fh); + trace_s390_pci_nodev("mpcifc", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } @@ -1104,7 +1203,12 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, switch (oc) { case ZPCI_MOD_FC_REG_INT: - if (pbdev->summary_ind) { + if (pbdev->interp) { + if (mpcifc_reg_int_interp(pbdev, &fib)) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } + } else if (pbdev->summary_ind) { cc = ZPCI_PCI_LS_ERR; s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); } else if (reg_irqs(env, pbdev, fib)) { @@ -1113,7 +1217,12 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, } break; case ZPCI_MOD_FC_DEREG_INT: - if (!pbdev->summary_ind) { + if (pbdev->interp) { + if (mpcifc_dereg_int_interp(pbdev, &fib)) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } + } else if (!pbdev->summary_ind) { cc = ZPCI_PCI_LS_ERR; s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); } else { diff --git a/hw/s390x/s390-pci-kvm.c b/hw/s390x/s390-pci-kvm.c new file mode 100644 index 0000000000..9eef4fc3ec --- /dev/null +++ b/hw/s390x/s390-pci-kvm.c @@ -0,0 +1,82 @@ +/* + * s390 zPCI KVM interfaces + * + * Copyright 2022 IBM Corp. + * Author(s): Matthew Rosato + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "qemu/osdep.h" + +#include + +#include "kvm/kvm_s390x.h" +#include "target/s390x/kvm/pv.h" +#include "hw/s390x/s390-pci-bus.h" +#include "hw/s390x/s390-pci-kvm.h" +#include "hw/s390x/s390-pci-inst.h" +#include "hw/s390x/s390-pci-vfio.h" +#include "cpu_models.h" + +bool s390_pci_kvm_interp_allowed(void) +{ + return kvm_s390_get_zpci_op() && !s390_is_pv(); +} + +int s390_pci_kvm_aif_enable(S390PCIBusDevice *pbdev, ZpciFib *fib, bool assist) +{ + int rc; + struct kvm_s390_zpci_op args = { + .fh = pbdev->fh, + .op = KVM_S390_ZPCIOP_REG_AEN, + .u.reg_aen.ibv = fib->aibv, + .u.reg_aen.sb = fib->aisb, + .u.reg_aen.noi = FIB_DATA_NOI(fib->data), + .u.reg_aen.isc = FIB_DATA_ISC(fib->data), + .u.reg_aen.sbo = FIB_DATA_AISBO(fib->data), + .u.reg_aen.flags = (assist) ? 0 : KVM_S390_ZPCIOP_REGAEN_HOST + }; + + if (pbdev->aif) { + return -EINVAL; + } + + rc = kvm_vm_ioctl(kvm_state, KVM_S390_ZPCI_OP, &args); + if (rc == 0) { + pbdev->aif = true; + } + + return rc; +} + +int s390_pci_kvm_aif_disable(S390PCIBusDevice *pbdev) +{ + int rc; + + struct kvm_s390_zpci_op args = { + .fh = pbdev->fh, + .op = KVM_S390_ZPCIOP_DEREG_AEN + }; + + if (!pbdev->aif) { + return -EINVAL; + } + + /* + * The device may have already been reset but we still want to relinquish + * the guest ISC, so always be sure to use an up-to-date host fh. + */ + if (!s390_pci_get_host_fh(pbdev, &args.fh)) { + return -EPERM; + } + + rc = kvm_vm_ioctl(kvm_state, KVM_S390_ZPCI_OP, &args); + if (rc == 0) { + pbdev->aif = false; + } + + return rc; +} diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c index 6f80a47e29..7dbbc76823 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -66,6 +66,10 @@ S390PCIDMACount *s390_pci_start_dma_count(S390pciState *s, assert(vpdev); + if (!vpdev->vbasedev.group) { + return NULL; + } + id = vpdev->vbasedev.group->container->fd; if (!s390_pci_update_dma_avail(id, &avail)) { @@ -84,6 +88,7 @@ S390PCIDMACount *s390_pci_start_dma_count(S390pciState *s, cnt->users = 1; cnt->avail = avail; QTAILQ_INSERT_TAIL(&s->zpci_dma_limit, cnt, link); + pbdev->iommu->max_dma_limit = avail; return cnt; } @@ -103,6 +108,7 @@ static void s390_pci_read_base(S390PCIBusDevice *pbdev, struct vfio_info_cap_header *hdr; struct vfio_device_info_cap_zpci_base *cap; VFIOPCIDevice *vpci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + uint64_t vfio_size; hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_BASE); @@ -122,6 +128,38 @@ static void s390_pci_read_base(S390PCIBusDevice *pbdev, /* The following values remain 0 until we support other FMB formats */ pbdev->zpci_fn.fmbl = 0; pbdev->zpci_fn.pft = 0; + /* Store function type separately for type-specific behavior */ + pbdev->pft = cap->pft; + + /* + * If appropriate, reduce the size of the supported DMA aperture reported + * to the guest based upon the vfio DMA limit. + */ + vfio_size = pbdev->iommu->max_dma_limit << TARGET_PAGE_BITS; + if (vfio_size > 0 && vfio_size < cap->end_dma - cap->start_dma + 1) { + pbdev->zpci_fn.edma = cap->start_dma + vfio_size - 1; + } +} + +static bool get_host_fh(S390PCIBusDevice *pbdev, struct vfio_device_info *info, + uint32_t *fh) +{ + struct vfio_info_cap_header *hdr; + struct vfio_device_info_cap_zpci_base *cap; + VFIOPCIDevice *vpci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + + hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_BASE); + + /* Can only get the host fh with version 2 or greater */ + if (hdr == NULL || hdr->version < 2) { + trace_s390_pci_clp_cap(vpci->vbasedev.name, + VFIO_DEVICE_INFO_CAP_ZPCI_BASE); + return false; + } + cap = (void *) hdr; + + *fh = cap->fh; + return true; } static void s390_pci_read_group(S390PCIBusDevice *pbdev, @@ -129,13 +167,18 @@ static void s390_pci_read_group(S390PCIBusDevice *pbdev, { struct vfio_info_cap_header *hdr; struct vfio_device_info_cap_zpci_group *cap; + S390pciState *s = s390_get_phb(); ClpRspQueryPciGrp *resgrp; VFIOPCIDevice *vpci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + uint8_t start_gid = pbdev->zpci_fn.pfgid; hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_GROUP); - /* If capability not provided, just use the default group */ - if (hdr == NULL) { + /* + * If capability not provided or the underlying hostdev is simulated, just + * use the default group. + */ + if (hdr == NULL || pbdev->zpci_fn.pfgid >= ZPCI_SIM_GRP_START) { trace_s390_pci_clp_cap(vpci->vbasedev.name, VFIO_DEVICE_INFO_CAP_ZPCI_GROUP); pbdev->zpci_fn.pfgid = ZPCI_DEFAULT_FN_GRP; @@ -144,11 +187,40 @@ static void s390_pci_read_group(S390PCIBusDevice *pbdev, } cap = (void *) hdr; + /* + * For an intercept device, let's use an existing simulated group if one + * one was already created for other intercept devices in this group. + * If not, create a new simulated group if any are still available. + * If all else fails, just fall back on the default group. + */ + if (!pbdev->interp) { + pbdev->pci_group = s390_group_find_host_sim(pbdev->zpci_fn.pfgid); + if (pbdev->pci_group) { + /* Use existing simulated group */ + pbdev->zpci_fn.pfgid = pbdev->pci_group->id; + return; + } else { + if (s->next_sim_grp == ZPCI_DEFAULT_FN_GRP) { + /* All out of simulated groups, use default */ + trace_s390_pci_clp_cap(vpci->vbasedev.name, + VFIO_DEVICE_INFO_CAP_ZPCI_GROUP); + pbdev->zpci_fn.pfgid = ZPCI_DEFAULT_FN_GRP; + pbdev->pci_group = s390_group_find(ZPCI_DEFAULT_FN_GRP); + return; + } else { + /* We can assign a new simulated group */ + pbdev->zpci_fn.pfgid = s->next_sim_grp; + s->next_sim_grp++; + /* Fall through to create the new sim group using CLP info */ + } + } + } + /* See if the PCI group is already defined, create if not */ pbdev->pci_group = s390_group_find(pbdev->zpci_fn.pfgid); if (!pbdev->pci_group) { - pbdev->pci_group = s390_group_create(pbdev->zpci_fn.pfgid); + pbdev->pci_group = s390_group_create(pbdev->zpci_fn.pfgid, start_gid); resgrp = &pbdev->pci_group->zpci_group; if (cap->flags & VFIO_DEVICE_INFO_ZPCI_FLAG_REFRESH) { @@ -158,7 +230,11 @@ static void s390_pci_read_group(S390PCIBusDevice *pbdev, resgrp->msia = cap->msi_addr; resgrp->mui = cap->mui; resgrp->i = cap->noi; - resgrp->maxstbl = cap->maxstbl; + if (pbdev->interp && hdr->version >= 2) { + resgrp->maxstbl = cap->imaxstbl; + } else { + resgrp->maxstbl = cap->maxstbl; + } resgrp->version = cap->version; resgrp->dtsm = ZPCI_DTSM; } @@ -217,6 +293,33 @@ static void s390_pci_read_pfip(S390PCIBusDevice *pbdev, memcpy(pbdev->zpci_fn.pfip, cap->pfip, CLP_PFIP_NR_SEGMENTS); } +static struct vfio_device_info *get_device_info(S390PCIBusDevice *pbdev) +{ + VFIOPCIDevice *vfio_pci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + + return vfio_get_device_info(vfio_pci->vbasedev.fd); +} + +/* + * Get the host function handle from the vfio CLP capabilities chain. Returns + * true if a fh value was placed into the provided buffer. Returns false + * if a fh could not be obtained (ioctl failed or capability version does + * not include the fh) + */ +bool s390_pci_get_host_fh(S390PCIBusDevice *pbdev, uint32_t *fh) +{ + g_autofree struct vfio_device_info *info = NULL; + + assert(fh); + + info = get_device_info(pbdev); + if (!info) { + return false; + } + + return get_host_fh(pbdev, info, fh); +} + /* * This function will issue the VFIO_DEVICE_GET_INFO ioctl and look for * capabilities that contain information about CLP features provided by the @@ -229,36 +332,12 @@ static void s390_pci_read_pfip(S390PCIBusDevice *pbdev, void s390_pci_get_clp_info(S390PCIBusDevice *pbdev) { g_autofree struct vfio_device_info *info = NULL; - VFIOPCIDevice *vfio_pci; - uint32_t argsz; - int fd; - argsz = sizeof(*info); - info = g_malloc0(argsz); - - vfio_pci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); - fd = vfio_pci->vbasedev.fd; - - /* - * If the specified argsz is not large enough to contain all capabilities - * it will be updated upon return from the ioctl. Retry until we have - * a big enough buffer to hold the entire capability chain. On error, - * just exit and rely on CLP defaults. - */ -retry: - info->argsz = argsz; - - if (ioctl(fd, VFIO_DEVICE_GET_INFO, info)) { - trace_s390_pci_clp_dev_info(vfio_pci->vbasedev.name); + info = get_device_info(pbdev); + if (!info) { return; } - if (info->argsz > argsz) { - argsz = info->argsz; - info = g_realloc(info, argsz); - goto retry; - } - /* * Find the CLP features provided and fill in the guest CLP responses. * Always call s390_pci_read_base first as information from this could diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 5024faf411..5c535d483e 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "hw/boards.h" +#include "hw/qdev-properties.h" #include "hw/s390x/storage-keys.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc-target.h" @@ -152,7 +153,7 @@ void qmp_dump_skeys(const char *filename, Error **errp) goto out; } - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); guest_phys_blocks_init(&guest_phys_blocks); guest_phys_blocks_append(&guest_phys_blocks); @@ -432,58 +433,39 @@ static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id) return ret; } -static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp) -{ - S390SKeysState *ss = S390_SKEYS(obj); - - return ss->migration_enabled; -} - static SaveVMHandlers savevm_s390_storage_keys = { .save_state = s390_storage_keys_save, .load_state = s390_storage_keys_load, }; -static inline void s390_skeys_set_migration_enabled(Object *obj, bool value, - Error **errp) +static void s390_skeys_realize(DeviceState *dev, Error **errp) { - S390SKeysState *ss = S390_SKEYS(obj); - - /* Prevent double registration of savevm handler */ - if (ss->migration_enabled == value) { - return; - } - - ss->migration_enabled = value; + S390SKeysState *ss = S390_SKEYS(dev); if (ss->migration_enabled) { register_savevm_live(TYPE_S390_SKEYS, 0, 1, &savevm_s390_storage_keys, ss); - } else { - unregister_savevm(VMSTATE_IF(ss), TYPE_S390_SKEYS, ss); } } -static void s390_skeys_instance_init(Object *obj) -{ - object_property_add_bool(obj, "migration-enabled", - s390_skeys_get_migration_enabled, - s390_skeys_set_migration_enabled); - object_property_set_bool(obj, "migration-enabled", true, NULL); -} +static Property s390_skeys_props[] = { + DEFINE_PROP_BOOL("migration-enabled", S390SKeysState, migration_enabled, true), + DEFINE_PROP_END_OF_LIST(), +}; static void s390_skeys_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->hotpluggable = false; + dc->realize = s390_skeys_realize; + device_class_set_props(dc, s390_skeys_props); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo s390_skeys_info = { .name = TYPE_S390_SKEYS, .parent = TYPE_DEVICE, - .instance_init = s390_skeys_instance_init, .instance_size = sizeof(S390SKeysState), .class_init = s390_skeys_class_init, .class_size = sizeof(S390SKeysClass), diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index 9eda1c3b2a..c483b62a9b 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -13,6 +13,7 @@ #include "qemu/units.h" #include "migration/qemu-file.h" #include "migration/register.h" +#include "hw/qdev-properties.h" #include "hw/s390x/storage-attributes.h" #include "qemu/error-report.h" #include "exec/ram_addr.h" @@ -182,17 +183,15 @@ static int cmma_save_setup(QEMUFile *f, void *opaque) return 0; } -static void cmma_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, - uint64_t *res_precopy_only, - uint64_t *res_compatible, - uint64_t *res_postcopy_only) +static void cmma_state_pending(void *opaque, uint64_t *must_precopy, + uint64_t *can_postcopy) { S390StAttribState *sas = S390_STATTRIB(opaque); S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); long long res = sac->get_dirtycount(sas); if (res >= 0) { - *res_precopy_only += res; + *must_precopy += res; } } @@ -211,7 +210,7 @@ static int cmma_save(QEMUFile *f, void *opaque, int final) return -ENOMEM; } - while (final ? 1 : qemu_file_rate_limit(f) == 0) { + while (final ? 1 : migration_rate_exceeded(f) == 0) { reallen = sac->get_stattr(sas, &start_gfn, buflen, buf); if (reallen < 0) { g_free(buf); @@ -332,6 +331,17 @@ static const TypeInfo qemu_s390_stattrib_info = { /* Generic abstract object: */ +static SaveVMHandlers savevm_s390_stattrib_handlers = { + .save_setup = cmma_save_setup, + .save_live_iterate = cmma_save_iterate, + .save_live_complete_precopy = cmma_save_complete, + .state_pending_exact = cmma_state_pending, + .state_pending_estimate = cmma_state_pending, + .save_cleanup = cmma_save_cleanup, + .load_state = cmma_load, + .is_active = cmma_active, +}; + static void s390_stattrib_realize(DeviceState *dev, Error **errp) { bool ambiguous = false; @@ -339,9 +349,18 @@ static void s390_stattrib_realize(DeviceState *dev, Error **errp) object_resolve_path_type("", TYPE_S390_STATTRIB, &ambiguous); if (ambiguous) { error_setg(errp, "storage_attributes device already exists"); + return; } + + register_savevm_live(TYPE_S390_STATTRIB, 0, 0, + &savevm_s390_stattrib_handlers, dev); } +static Property s390_stattrib_props[] = { + DEFINE_PROP_BOOL("migration-enabled", S390StAttribState, migration_enabled, true), + DEFINE_PROP_END_OF_LIST(), +}; + static void s390_stattrib_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -349,45 +368,13 @@ static void s390_stattrib_class_init(ObjectClass *oc, void *data) dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->realize = s390_stattrib_realize; + device_class_set_props(dc, s390_stattrib_props); } -static inline bool s390_stattrib_get_migration_enabled(Object *obj, - Error **errp) -{ - S390StAttribState *s = S390_STATTRIB(obj); - - return s->migration_enabled; -} - -static inline void s390_stattrib_set_migration_enabled(Object *obj, bool value, - Error **errp) -{ - S390StAttribState *s = S390_STATTRIB(obj); - - s->migration_enabled = value; -} - -static SaveVMHandlers savevm_s390_stattrib_handlers = { - .save_setup = cmma_save_setup, - .save_live_iterate = cmma_save_iterate, - .save_live_complete_precopy = cmma_save_complete, - .save_live_pending = cmma_save_pending, - .save_cleanup = cmma_save_cleanup, - .load_state = cmma_load, - .is_active = cmma_active, -}; - static void s390_stattrib_instance_init(Object *obj) { S390StAttribState *sas = S390_STATTRIB(obj); - register_savevm_live(TYPE_S390_STATTRIB, 0, 0, - &savevm_s390_stattrib_handlers, sas); - - object_property_add_bool(obj, "migration-enabled", - s390_stattrib_get_migration_enabled, - s390_stattrib_set_migration_enabled); - object_property_set_bool(obj, "migration-enabled", true, NULL); sas->migration_cur_gfn = 0; } diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index cc3097bfee..b1dcb3857f 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -41,8 +41,11 @@ #include "hw/qdev-properties.h" #include "hw/s390x/tod.h" #include "sysemu/sysemu.h" -#include "hw/s390x/pv.h" +#include "sysemu/cpus.h" +#include "target/s390x/kvm/pv.h" #include "migration/blocker.h" +#include "qapi/visitor.h" +#include "hw/s390x/cpu-topology.h" static Error *pv_mig_blocker; @@ -84,8 +87,15 @@ out: static void s390_init_cpus(MachineState *machine) { MachineClass *mc = MACHINE_GET_CLASS(machine); + S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); int i; + if (machine->smp.threads > s390mc->max_threads) { + error_report("S390 does not support more than %d threads.", + s390mc->max_threads); + exit(1); + } + /* initialize possible_cpus */ mc->possible_cpu_arch_ids(machine); @@ -100,6 +110,7 @@ static const char *const reset_dev_types[] = { "s390-flic", "diag288", TYPE_S390_PCI_HOST_BRIDGE, + TYPE_AP_BRIDGE, }; static void subsystem_reset(void) @@ -107,12 +118,23 @@ static void subsystem_reset(void) DeviceState *dev; int i; + /* + * ISM firmware is sensitive to unexpected changes to the IOMMU, which can + * occur during reset of the vfio-pci device (unmap of entire aperture). + * Ensure any passthrough ISM devices are reset now, while CPUs are paused + * but before vfio-pci cleanup occurs. + */ + s390_pci_ism_reset(); + for (i = 0; i < ARRAY_SIZE(reset_dev_types); i++) { dev = DEVICE(object_resolve_path_type("", reset_dev_types[i], NULL)); if (dev) { - qdev_reset_all(dev); + device_cold_reset(dev); } } + if (s390_has_topology()) { + s390_topology_reset(); + } } static int virtio_ccw_hcall_notify(const uint64_t *args) @@ -207,20 +229,9 @@ static void s390_init_ipl_dev(const char *kernel_filename, static void s390_create_virtio_net(BusState *bus, const char *name) { - int i; + DeviceState *dev; - for (i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - DeviceState *dev; - - if (!nd->model) { - nd->model = g_strdup("virtio"); - } - - qemu_check_nic_model(nd, "virtio"); - - dev = qdev_new(name); - qdev_set_nic_properties(dev, nd); + while ((dev = qemu_create_nic_device(name, true, "virtio"))) { qdev_realize_and_unref(dev, bus, &error_fatal); } } @@ -236,6 +247,7 @@ static void s390_create_sclpconsole(const char *type, Chardev *chardev) static void ccw_init(MachineState *machine) { + MachineClass *mc = MACHINE_GET_CLASS(machine); int ret; VirtualCssBus *css_bus; DeviceState *dev; @@ -283,7 +295,7 @@ static void ccw_init(MachineState *machine) } /* Create VirtIO network adapters */ - s390_create_virtio_net(BUS(css_bus), "virtio-net-ccw"); + s390_create_virtio_net(BUS(css_bus), mc->default_nic); /* init consoles */ if (serial_hd(0)) { @@ -300,11 +312,19 @@ static void ccw_init(MachineState *machine) static void s390_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + ERRP_GUARD(); MachineState *ms = MACHINE(hotplug_dev); S390CPU *cpu = S390_CPU(dev); g_assert(!ms->possible_cpus->cpus[cpu->env.core_id].cpu); - ms->possible_cpus->cpus[cpu->env.core_id].cpu = OBJECT(dev); + ms->possible_cpus->cpus[cpu->env.core_id].cpu = CPU(dev); + + if (s390_has_topology()) { + s390_topology_setup_cpu(ms, cpu, errp); + if (*errp) { + return; + } + } if (dev->hotplugged) { raise_irq_cpu_hotplug(); @@ -321,10 +341,11 @@ static inline void s390_do_cpu_ipl(CPUState *cs, run_on_cpu_data arg) static void s390_machine_unprotect(S390CcwMachineState *ms) { - s390_pv_vm_disable(); + if (!s390_pv_vm_try_disable_async(ms)) { + s390_pv_vm_disable(); + } ms->pv = false; - migrate_del_blocker(pv_mig_blocker); - error_free_or_abort(&pv_mig_blocker); + migrate_del_blocker(&pv_mig_blocker); ram_block_discard_disable(false); } @@ -346,12 +367,11 @@ static int s390_machine_protect(S390CcwMachineState *ms) } error_setg(&pv_mig_blocker, - "protected VMs are currently not migrateable."); - rc = migrate_add_blocker(pv_mig_blocker, &local_err); + "protected VMs are currently not migratable."); + rc = migrate_add_blocker(&pv_mig_blocker, &local_err); if (rc) { ram_block_discard_disable(false); error_report_err(local_err); - error_free_or_abort(&pv_mig_blocker); return rc; } @@ -359,15 +379,20 @@ static int s390_machine_protect(S390CcwMachineState *ms) rc = s390_pv_vm_enable(); if (rc) { ram_block_discard_disable(false); - migrate_del_blocker(pv_mig_blocker); - error_free_or_abort(&pv_mig_blocker); + migrate_del_blocker(&pv_mig_blocker); return rc; } ms->pv = true; + /* Will return 0 if API is not available since it's not vital */ + rc = s390_pv_query_info(); + if (rc) { + goto out_err; + } + /* Set SE header and unpack */ - rc = s390_ipl_prepare_pv_header(); + rc = s390_ipl_prepare_pv_header(&local_err); if (rc) { goto out_err; } @@ -386,6 +411,9 @@ static int s390_machine_protect(S390CcwMachineState *ms) return rc; out_err: + if (local_err) { + error_report_err(local_err); + } s390_machine_unprotect(ms); return rc; } @@ -405,7 +433,7 @@ static void s390_pv_prepare_reset(S390CcwMachineState *ms) s390_pv_prep_reset(); } -static void s390_machine_reset(MachineState *machine) +static void s390_machine_reset(MachineState *machine, ShutdownCause reason) { S390CcwMachineState *ms = S390_CCW_MACHINE(machine); enum s390_reset reset_type; @@ -423,11 +451,21 @@ static void s390_machine_reset(MachineState *machine) switch (reset_type) { case S390_RESET_EXTERNAL: case S390_RESET_REIPL: + /* + * Reset the subsystem which includes a AP reset. If a PV + * guest had APQNs attached the AP reset is a prerequisite to + * unprotecting since the UV checks if all APQNs are reset. + */ + subsystem_reset(); if (s390_is_pv()) { s390_machine_unprotect(ms); } - qemu_devices_reset(); + /* + * Device reset includes CPU clear resets so this has to be + * done AFTER the unprotect call above. + */ + qemu_devices_reset(reason); s390_crypto_reset(); /* configure and start the ipl CPU only */ @@ -435,7 +473,7 @@ static void s390_machine_reset(MachineState *machine) break; case S390_RESET_MODIFIED_CLEAR: /* - * Susbsystem reset needs to be done before we unshare memory + * Subsystem reset needs to be done before we unshare memory * and lose access to VIRTIO structures in guest memory. */ subsystem_reset(); @@ -448,7 +486,7 @@ static void s390_machine_reset(MachineState *machine) break; case S390_RESET_LOAD_NORMAL: /* - * Susbsystem reset needs to be done before we unshare memory + * Subsystem reset needs to be done before we unshare memory * and lose access to VIRTIO structures in guest memory. */ subsystem_reset(); @@ -537,11 +575,20 @@ static const CPUArchIdList *s390_possible_cpu_arch_ids(MachineState *ms) sizeof(CPUArchId) * max_cpus); ms->possible_cpus->len = max_cpus; for (i = 0; i < ms->possible_cpus->len; i++) { + CpuInstanceProperties *props = &ms->possible_cpus->cpus[i].props; + ms->possible_cpus->cpus[i].type = ms->cpu_type; ms->possible_cpus->cpus[i].vcpus_count = 1; ms->possible_cpus->cpus[i].arch_id = i; - ms->possible_cpus->cpus[i].props.has_core_id = true; - ms->possible_cpus->cpus[i].props.core_id = i; + + props->has_core_id = true; + props->core_id = i; + props->has_socket_id = true; + props->socket_id = s390_std_socket(i, &ms->smp); + props->has_book_id = true; + props->book_id = s390_std_book(i, &ms->smp); + props->has_drawer_id = true; + props->drawer_id = s390_std_drawer(i, &ms->smp); } return ms->possible_cpus; @@ -583,38 +630,6 @@ static ram_addr_t s390_fixup_ram_size(ram_addr_t sz) return newsz; } -static void ccw_machine_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - NMIClass *nc = NMI_CLASS(oc); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); - S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); - - s390mc->ri_allowed = true; - s390mc->cpu_model_allowed = true; - s390mc->css_migration_enabled = true; - s390mc->hpage_1m_allowed = true; - mc->init = ccw_init; - mc->reset = s390_machine_reset; - mc->block_default_type = IF_VIRTIO; - mc->no_cdrom = 1; - mc->no_floppy = 1; - mc->no_parallel = 1; - mc->no_sdcard = 1; - mc->max_cpus = S390_MAX_CPUS; - mc->has_hotpluggable_cpus = true; - assert(!mc->get_hotplug_handler); - mc->get_hotplug_handler = s390_get_hotplug_handler; - mc->cpu_index_to_instance_props = s390_cpu_index_to_props; - mc->possible_cpu_arch_ids = s390_possible_cpu_arch_ids; - /* it is overridden with 'host' cpu *in kvm_arch_init* */ - mc->default_cpu_type = S390_CPU_TYPE_NAME("qemu"); - 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) { S390CcwMachineState *ms = S390_CCW_MACHINE(obj); @@ -689,19 +704,29 @@ bool hpage_1m_allowed(void) return get_machine_class()->hpage_1m_allowed; } -static char *machine_get_loadparm(Object *obj, Error **errp) +static void machine_get_loadparm(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { S390CcwMachineState *ms = S390_CCW_MACHINE(obj); + char *str = g_strndup((char *) ms->loadparm, sizeof(ms->loadparm)); - /* make a NUL-terminated string */ - return g_strndup((char *) ms->loadparm, sizeof(ms->loadparm)); + visit_type_str(v, name, &str, errp); + g_free(str); } -static void machine_set_loadparm(Object *obj, const char *val, Error **errp) +static void machine_set_loadparm(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { S390CcwMachineState *ms = S390_CCW_MACHINE(obj); + char *val; int i; + if (!visit_type_str(v, name, &val, errp)) { + return; + } + for (i = 0; i < sizeof(ms->loadparm) && val[i]; i++) { uint8_t c = qemu_toupper(val[i]); /* mimic HMC */ @@ -719,29 +744,71 @@ static void machine_set_loadparm(Object *obj, const char *val, Error **errp) ms->loadparm[i] = ' '; /* pad right with spaces */ } } -static inline void s390_machine_initfn(Object *obj) -{ - object_property_add_bool(obj, "aes-key-wrap", - machine_get_aes_key_wrap, - machine_set_aes_key_wrap); - object_property_set_description(obj, "aes-key-wrap", - "enable/disable AES key wrapping using the CPACF wrapping key"); - object_property_set_bool(obj, "aes-key-wrap", true, NULL); - object_property_add_bool(obj, "dea-key-wrap", - machine_get_dea_key_wrap, - machine_set_dea_key_wrap); - object_property_set_description(obj, "dea-key-wrap", +static void ccw_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + NMIClass *nc = NMI_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); + + s390mc->ri_allowed = true; + s390mc->cpu_model_allowed = true; + s390mc->css_migration_enabled = true; + s390mc->hpage_1m_allowed = true; + s390mc->max_threads = 1; + mc->init = ccw_init; + mc->reset = s390_machine_reset; + mc->block_default_type = IF_VIRTIO; + mc->no_cdrom = 1; + mc->no_floppy = 1; + mc->no_parallel = 1; + mc->no_sdcard = 1; + mc->max_cpus = S390_MAX_CPUS; + mc->has_hotpluggable_cpus = true; + mc->smp_props.books_supported = true; + mc->smp_props.drawers_supported = true; + assert(!mc->get_hotplug_handler); + mc->get_hotplug_handler = s390_get_hotplug_handler; + mc->cpu_index_to_instance_props = s390_cpu_index_to_props; + mc->possible_cpu_arch_ids = s390_possible_cpu_arch_ids; + /* it is overridden with 'host' cpu *in kvm_arch_init* */ + mc->default_cpu_type = S390_CPU_TYPE_NAME("qemu"); + 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"; + mc->default_nic = "virtio-net-ccw"; + + object_class_property_add_bool(oc, "aes-key-wrap", + machine_get_aes_key_wrap, + machine_set_aes_key_wrap); + object_class_property_set_description(oc, "aes-key-wrap", + "enable/disable AES key wrapping using the CPACF wrapping key"); + + object_class_property_add_bool(oc, "dea-key-wrap", + machine_get_dea_key_wrap, + machine_set_dea_key_wrap); + object_class_property_set_description(oc, "dea-key-wrap", "enable/disable DEA key wrapping using the CPACF wrapping key"); - object_property_set_bool(obj, "dea-key-wrap", true, NULL); - object_property_add_str(obj, "loadparm", - machine_get_loadparm, machine_set_loadparm); - object_property_set_description(obj, "loadparm", + + object_class_property_add(oc, "loadparm", "loadparm", + machine_get_loadparm, machine_set_loadparm, + NULL, NULL); + object_class_property_set_description(oc, "loadparm", "Up to 8 chars in set of [A-Za-z0-9. ] (lower case chars converted" " to upper case) to pass to machine loader, boot manager," " and guest kernel"); } +static inline void s390_machine_initfn(Object *obj) +{ + S390CcwMachineState *ms = S390_CCW_MACHINE(obj); + + ms->aes_key_wrap = true; + ms->dea_key_wrap = true; +} + static const TypeInfo ccw_machine_info = { .name = TYPE_S390_CCW_MACHINE, .parent = TYPE_MACHINE, @@ -792,14 +859,88 @@ bool css_migration_enabled(void) } \ type_init(ccw_machine_register_##suffix) +static void ccw_machine_9_0_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_9_0_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE(9_0, "9.0", true); + +static void ccw_machine_8_2_instance_options(MachineState *machine) +{ + ccw_machine_9_0_instance_options(machine); +} + +static void ccw_machine_8_2_class_options(MachineClass *mc) +{ + ccw_machine_9_0_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_8_2, hw_compat_8_2_len); +} +DEFINE_CCW_MACHINE(8_2, "8.2", false); + +static void ccw_machine_8_1_instance_options(MachineState *machine) +{ + ccw_machine_8_2_instance_options(machine); +} + +static void ccw_machine_8_1_class_options(MachineClass *mc) +{ + ccw_machine_8_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_8_1, hw_compat_8_1_len); + mc->smp_props.drawers_supported = false; + mc->smp_props.books_supported = false; +} +DEFINE_CCW_MACHINE(8_1, "8.1", false); + +static void ccw_machine_8_0_instance_options(MachineState *machine) +{ + ccw_machine_8_1_instance_options(machine); +} + +static void ccw_machine_8_0_class_options(MachineClass *mc) +{ + ccw_machine_8_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_8_0, hw_compat_8_0_len); +} +DEFINE_CCW_MACHINE(8_0, "8.0", false); + +static void ccw_machine_7_2_instance_options(MachineState *machine) +{ + ccw_machine_8_0_instance_options(machine); +} + +static void ccw_machine_7_2_class_options(MachineClass *mc) +{ + ccw_machine_8_0_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_7_2, hw_compat_7_2_len); +} +DEFINE_CCW_MACHINE(7_2, "7.2", false); + static void ccw_machine_7_1_instance_options(MachineState *machine) { + static const S390FeatInit qemu_cpu_feat = { S390_FEAT_LIST_QEMU_V7_1 }; + + ccw_machine_7_2_instance_options(machine); + s390_cpudef_featoff_greater(16, 1, S390_FEAT_PAIE); + s390_set_qemu_cpu_model(0x8561, 15, 1, qemu_cpu_feat); } static void ccw_machine_7_1_class_options(MachineClass *mc) { + S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); + static GlobalProperty compat[] = { + { TYPE_S390_PCI_DEVICE, "interpret", "off", }, + { TYPE_S390_PCI_DEVICE, "forwarding-assist", "off", }, + }; + + ccw_machine_7_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_7_1, hw_compat_7_1_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); + s390mc->max_threads = S390_MAX_CPUS; } -DEFINE_CCW_MACHINE(7_1, "7.1", true); +DEFINE_CCW_MACHINE(7_1, "7.1", false); static void ccw_machine_7_0_instance_options(MachineState *machine) { diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index eff74479f4..893e71a41b 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -20,6 +20,7 @@ #include "hw/s390x/event-facility.h" #include "hw/s390x/s390-pci-bus.h" #include "hw/s390x/ipl.h" +#include "hw/s390x/cpu-topology.h" static inline SCLPDevice *get_sclp_device(void) { @@ -123,6 +124,10 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) return; } + if (s390_has_topology()) { + read_info->stsi_parm = SCLP_READ_SCP_INFO_MNEST; + } + /* CPU information */ prepare_cpu_entries(machine, entries_start, &cpu_count); read_info->entries_cpu = cpu_to_be16(cpu_count); @@ -264,9 +269,9 @@ static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code) * service_interrupt call. */ #define SCLP_PV_DUMMY_ADDR 0x4000 -int sclp_service_call_protected(CPUS390XState *env, uint64_t sccb, - uint32_t code) +int sclp_service_call_protected(S390CPU *cpu, uint64_t sccb, uint32_t code) { + CPUS390XState *env = &cpu->env; SCLPDevice *sclp = get_sclp_device(); SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); SCCBHeader header; @@ -291,8 +296,9 @@ out_write: return 0; } -int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code) +int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code) { + CPUS390XState *env = &cpu->env; SCLPDevice *sclp = get_sclp_device(); SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); SCCBHeader header; diff --git a/hw/s390x/sclpcpu.c b/hw/s390x/sclpcpu.c index f2b1a4b037..fa79891f5a 100644 --- a/hw/s390x/sclpcpu.c +++ b/hw/s390x/sclpcpu.c @@ -73,7 +73,7 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, return 1; } -static void cpu_class_init(ObjectClass *oc, void *data) +static void sclp_cpu_class_init(ObjectClass *oc, void *data) { SCLPEventClass *k = SCLP_EVENT_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -94,7 +94,7 @@ static const TypeInfo sclp_cpu_info = { .name = TYPE_SCLP_CPU_HOTPLUG, .parent = TYPE_SCLP_EVENT, .instance_size = sizeof(SCLPEvent), - .class_init = cpu_class_init, + .class_init = sclp_cpu_class_init, .class_size = sizeof(SCLPEventClass), }; diff --git a/hw/s390x/sclpquiesce.c b/hw/s390x/sclpquiesce.c index ce07b16884..14936aa94b 100644 --- a/hw/s390x/sclpquiesce.c +++ b/hw/s390x/sclpquiesce.c @@ -72,18 +72,16 @@ static const VMStateDescription vmstate_sclpquiesce = { .name = TYPE_SCLP_QUIESCE, .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(event_pending, SCLPEvent), VMSTATE_END_OF_LIST() } }; -typedef struct QuiesceNotifier QuiesceNotifier; - -static struct QuiesceNotifier { +typedef struct QuiesceNotifier { Notifier notifier; SCLPEvent *event; -} qn; +} QuiesceNotifier; static void quiesce_powerdown_req(Notifier *n, void *opaque) { @@ -97,6 +95,8 @@ static void quiesce_powerdown_req(Notifier *n, void *opaque) static int quiesce_init(SCLPEvent *event) { + static QuiesceNotifier qn; + qn.notifier.notify = quiesce_powerdown_req; qn.event = event; diff --git a/hw/s390x/tod-kvm.c b/hw/s390x/tod-kvm.c index 9d0cbfbce2..9588b90f2b 100644 --- a/hw/s390x/tod-kvm.c +++ b/hw/s390x/tod-kvm.c @@ -13,6 +13,7 @@ #include "qemu/module.h" #include "sysemu/runstate.h" #include "hw/s390x/tod.h" +#include "target/s390x/kvm/pv.h" #include "kvm/kvm_s390x.h" static void kvm_s390_get_tod_raw(S390TOD *tod, Error **errp) @@ -84,6 +85,14 @@ static void kvm_s390_tod_vm_state_change(void *opaque, bool running, S390TODState *td = opaque; Error *local_err = NULL; + /* + * Under PV, the clock is under ultravisor control, hence we cannot restore + * it on resume. + */ + if (s390_is_pv()) { + return; + } + if (running && td->stopped) { /* Set the old TOD when running the VM - start the TOD clock. */ kvm_s390_set_tod_raw(&td->base, &local_err); diff --git a/hw/s390x/trace-events b/hw/s390x/trace-events index 8b9213eab9..34da5ea323 100644 --- a/hw/s390x/trace-events +++ b/hw/s390x/trace-events @@ -19,3 +19,20 @@ virtio_ccw_set_ind(uint64_t ind_loc, uint8_t ind_old, uint8_t ind_new) "VIRTIO-C s390_pci_clp_cap(const char *id, uint32_t cap) "PCI: %s: missing expected CLP capability %u" s390_pci_clp_cap_size(const char *id, uint32_t size, uint32_t cap) "PCI: %s: bad size (%u) for CLP capability %u" s390_pci_clp_dev_info(const char *id) "PCI: %s: cannot read vfio device info" + +# s390-pci-bus.c +s390_pci_sclp_nodev(const char *str, uint32_t aid) "%s no dev found aid 0x%x" +s390_pci_iommu_xlate(uint64_t addr) "iommu trans addr 0x%" PRIx64 +s390_pci_msi_ctrl_write(uint64_t data, uint32_t idx, uint32_t vec) "write_msix data 0x%" PRIx64 " idx %d vec 0x%x" +s390_pcihost(const char *msg) "%s" + +# s390-pci-inst.c +s390_pci_irqs(const char *str, uint32_t id) "%s irqs for adapter id %d" +s390_pci_kvm_aif(const char *str) "Failed to %s interrupt forwarding" + +s390_pci_list_entry(uint32_t g_l2, uint32_t vid, uint32_t did, uint32_t fid, uint32_t fh) "g_l2 %d vendor id 0x%x device id 0x%x fid 0x%x fh 0x%x" +s390_pci_list(uint32_t rc) "failed rc 0x%x" +s390_pci_unknown(const char *msg, uint32_t cmd) "%s unknown command 0x%x" +s390_pci_bar(uint32_t bar, uint32_t addr, uint64_t size, uint32_t barsize) "bar %d addr 0x%x size 0x%" PRIx64 "barsize 0x%x" +s390_pci_nodev(const char *cmd, uint32_t fh) "%s no pci dev fh 0x%x" +s390_pci_invalid(const char *cmd, uint32_t fh) "%s invalid space fh 0x%x" diff --git a/hw/s390x/virtio-ccw-serial.c b/hw/s390x/virtio-ccw-serial.c index bf8057880f..8f8d2302f8 100644 --- a/hw/s390x/virtio-ccw-serial.c +++ b/hw/s390x/virtio-ccw-serial.c @@ -15,7 +15,6 @@ #include "hw/qdev-properties.h" #include "hw/virtio/virtio-serial.h" #include "virtio-ccw.h" -#include "hw/virtio/virtio-serial.h" #define TYPE_VIRTIO_SERIAL_CCW "virtio-serial-ccw" OBJECT_DECLARE_SIMPLE_TYPE(VirtioSerialCcw, VIRTIO_SERIAL_CCW) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 15b458527e..b4676909dd 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -22,7 +22,6 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/module.h" -#include "hw/virtio/virtio-access.h" #include "hw/virtio/virtio-bus.h" #include "hw/s390x/adapter.h" #include "hw/s390x/s390_flic.h" @@ -88,7 +87,7 @@ const VMStateDescription vmstate_virtio_ccw_dev_tmp = { .name = "s390_virtio_ccw_dev_tmp", .pre_save = virtio_ccw_dev_tmp_pre_save, .post_load = virtio_ccw_dev_tmp_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(config_vector, VirtioCcwDeviceTmp), VMSTATE_END_OF_LIST() } @@ -99,7 +98,7 @@ const VMStateDescription vmstate_virtio_ccw_dev = { .version_id = 1, .minimum_version_id = 1, .post_load = virtio_ccw_dev_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CCW_DEVICE(parent_obj, VirtioCcwDevice), VMSTATE_PTR_TO_IND_ADDR(indicators, VirtioCcwDevice), VMSTATE_PTR_TO_IND_ADDR(indicators2, VirtioCcwDevice), @@ -237,6 +236,7 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, return -EINVAL; } virtio_queue_set_num(vdev, index, num); + virtio_init_region_cache(vdev, index); } else if (virtio_queue_get_num(vdev, index) > num) { /* Fail if we don't have a big enough queue. */ return -EINVAL; @@ -249,12 +249,11 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, return 0; } -static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev) +static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev) { CcwDevice *ccw_dev = CCW_DEVICE(dev); - virtio_ccw_stop_ioeventfd(dev); - virtio_reset(vdev); + virtio_bus_reset(&dev->bus); if (dev->indicators) { release_indicator(&dev->routes.adapter, dev->indicators); dev->indicators = NULL; @@ -359,7 +358,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = virtio_ccw_handle_set_vq(sch, ccw, check_len, dev->revision < 1); break; case CCW_CMD_VDEV_RESET: - virtio_ccw_reset_virtio(dev, vdev); + virtio_ccw_reset_virtio(dev); ret = 0; break; case CCW_CMD_READ_FEAT: @@ -536,7 +535,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) } if (virtio_set_status(vdev, status) == 0) { if (vdev->status == 0) { - virtio_ccw_reset_virtio(dev, vdev); + virtio_ccw_reset_virtio(dev); } if (status & VIRTIO_CONFIG_S_DRIVER_OK) { virtio_ccw_start_ioeventfd(dev); @@ -769,10 +768,6 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) sch->cssid, sch->ssid, sch->schid, sch->devno, ccw_dev->devno.valid ? "user-configured" : "auto-configured"); - if (kvm_enabled() && !kvm_eventfds_enabled()) { - dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; - } - /* fd-based ioevents can't be synchronized in record/replay */ if (replay_mode != REPLAY_MODE_NONE) { dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; @@ -921,10 +916,9 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) static void virtio_ccw_reset(DeviceState *d) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev); - virtio_ccw_reset_virtio(dev, vdev); + virtio_ccw_reset_virtio(dev); if (vdc->parent_reset) { vdc->parent_reset(d); } diff --git a/hw/scsi/Kconfig b/hw/scsi/Kconfig index e7b34dc8e2..1feab84c4c 100644 --- a/hw/scsi/Kconfig +++ b/hw/scsi/Kconfig @@ -48,13 +48,19 @@ config VIRTIO_SCSI depends on VIRTIO select SCSI +config VHOST_SCSI_COMMON + bool + depends on VIRTIO + config VHOST_SCSI bool default y + select VHOST_SCSI_COMMON depends on VIRTIO && VHOST_KERNEL config VHOST_USER_SCSI bool # Only PCI devices are provided for now default y if VIRTIO_PCI + select VHOST_SCSI_COMMON depends on VIRTIO && VHOST_USER && LINUX diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c index 1792f84cea..42d9d2e483 100644 --- a/hw/scsi/esp-pci.c +++ b/hw/scsi/esp-pci.c @@ -24,7 +24,7 @@ */ #include "qemu/osdep.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/irq.h" #include "hw/nvram/eeprom93xx.h" #include "hw/scsi/esp.h" @@ -77,9 +77,44 @@ struct PCIESPState { ESPState esp; }; +static void esp_pci_update_irq(PCIESPState *pci) +{ + int scsi_level = !!(pci->dma_regs[DMA_STAT] & DMA_STAT_SCSIINT); + int dma_level = (pci->dma_regs[DMA_CMD] & DMA_CMD_INTE_D) ? + !!(pci->dma_regs[DMA_STAT] & DMA_STAT_DONE) : 0; + int level = scsi_level || dma_level; + + pci_set_irq(PCI_DEVICE(pci), level); +} + +static void esp_irq_handler(void *opaque, int irq_num, int level) +{ + PCIESPState *pci = PCI_ESP(opaque); + + if (level) { + pci->dma_regs[DMA_STAT] |= DMA_STAT_SCSIINT; + + /* + * If raising the ESP IRQ to indicate end of DMA transfer, set + * DMA_STAT_DONE at the same time. In theory this should be done in + * esp_pci_dma_memory_rw(), however there is a delay between setting + * DMA_STAT_DONE and the ESP IRQ arriving which is visible to the + * guest that can cause confusion e.g. Linux + */ + if ((pci->dma_regs[DMA_CMD] & DMA_CMD_MASK) == 0x3 && + pci->dma_regs[DMA_WBC] == 0) { + pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE; + } + } else { + pci->dma_regs[DMA_STAT] &= ~DMA_STAT_SCSIINT; + } + + esp_pci_update_irq(pci); +} + static void esp_pci_handle_idle(PCIESPState *pci, uint32_t val) { - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; trace_esp_pci_dma_idle(val); esp_dma_enable(s, 0, 0); @@ -89,11 +124,12 @@ static void esp_pci_handle_blast(PCIESPState *pci, uint32_t val) { trace_esp_pci_dma_blast(val); qemu_log_mask(LOG_UNIMP, "am53c974: cmd BLAST not implemented\n"); + pci->dma_regs[DMA_STAT] |= DMA_STAT_BCMBLT; } static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val) { - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; trace_esp_pci_dma_abort(val); if (s->current_req) { @@ -103,7 +139,7 @@ static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val) static void esp_pci_handle_start(PCIESPState *pci, uint32_t val) { - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; trace_esp_pci_dma_start(val); @@ -151,6 +187,7 @@ static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val) /* clear some bits on write */ uint32_t mask = DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE; pci->dma_regs[DMA_STAT] &= ~(val & mask); + esp_pci_update_irq(pci); } break; default: @@ -161,17 +198,14 @@ static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val) static uint32_t esp_pci_dma_read(PCIESPState *pci, uint32_t saddr) { - ESPState *s = ESP(&pci->esp); uint32_t val; val = pci->dma_regs[saddr]; if (saddr == DMA_STAT) { - if (s->rregs[ESP_RSTAT] & STAT_INT) { - val |= DMA_STAT_SCSIINT; - } if (!(pci->sbac & SBAC_STATUS)) { pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE); + esp_pci_update_irq(pci); } } @@ -183,7 +217,7 @@ static void esp_pci_io_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size) { PCIESPState *pci = opaque; - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; if (size < 4 || addr & 3) { /* need to upgrade request: we only support 4-bytes accesses */ @@ -228,7 +262,7 @@ static uint64_t esp_pci_io_read(void *opaque, hwaddr addr, unsigned int size) { PCIESPState *pci = opaque; - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; uint32_t ret; if (addr < 0x40) { @@ -275,7 +309,7 @@ static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len, qemu_log_mask(LOG_UNIMP, "am53c974: MDL transfer not implemented\n"); } - addr = pci->dma_regs[DMA_SPA]; + addr = pci->dma_regs[DMA_WAC]; if (pci->dma_regs[DMA_WBC] < len) { len = pci->dma_regs[DMA_WBC]; } @@ -285,9 +319,6 @@ static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len, /* update status registers */ pci->dma_regs[DMA_WBC] -= len; pci->dma_regs[DMA_WAC] += len; - if (pci->dma_regs[DMA_WBC] == 0) { - pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE; - } } static void esp_pci_dma_memory_read(void *opaque, uint8_t *buf, int len) @@ -315,7 +346,7 @@ static const MemoryRegionOps esp_pci_io_ops = { static void esp_pci_hard_reset(DeviceState *dev) { PCIESPState *pci = PCI_ESP(dev); - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; esp_hard_reset(s); pci->dma_regs[DMA_CMD] &= ~(DMA_CMD_DIR | DMA_CMD_INTE_D | DMA_CMD_INTE_P @@ -333,7 +364,7 @@ static const VMStateDescription vmstate_esp_pci_scsi = { .version_id = 2, .minimum_version_id = 1, .pre_save = esp_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PCIESPState), VMSTATE_BUFFER_UNSAFE(dma_regs, PCIESPState, 0, 8 * sizeof(uint32_t)), VMSTATE_UINT8_V(esp.mig_version_id, PCIESPState, 2), @@ -342,23 +373,13 @@ static const VMStateDescription vmstate_esp_pci_scsi = { } }; -static void esp_pci_command_complete(SCSIRequest *req, size_t resid) -{ - ESPState *s = req->hba_private; - PCIESPState *pci = container_of(s, PCIESPState, esp); - - esp_command_complete(req, resid); - pci->dma_regs[DMA_WBC] = 0; - pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE; -} - static const struct SCSIBusInfo esp_pci_scsi_info = { .tcq = false, .max_target = ESP_MAX_DEVS, .max_lun = 7, .transfer_data = esp_transfer_data, - .complete = esp_pci_command_complete, + .complete = esp_command_complete, .cancel = esp_request_cancelled, }; @@ -366,7 +387,7 @@ static void esp_pci_scsi_realize(PCIDevice *dev, Error **errp) { PCIESPState *pci = PCI_ESP(dev); DeviceState *d = DEVICE(dev); - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; uint8_t *pci_conf; if (!qdev_realize(DEVICE(s), NULL, errp)) { @@ -386,7 +407,7 @@ static void esp_pci_scsi_realize(PCIDevice *dev, Error **errp) "esp-io", 0x80); pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->io); - s->irq = pci_allocate_irq(dev); + s->irq = qemu_allocate_irq(esp_irq_handler, pci, 0); scsi_bus_init(&s->bus, sizeof(s->bus), d, &esp_pci_scsi_info); } @@ -394,7 +415,7 @@ static void esp_pci_scsi_realize(PCIDevice *dev, Error **errp) static void esp_pci_scsi_exit(PCIDevice *d) { PCIESPState *pci = PCI_ESP(d); - ESPState *s = ESP(&pci->esp); + ESPState *s = &pci->esp; qemu_free_irq(s->irq); } diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 2d3c649567..5d9b52632e 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -3,6 +3,7 @@ * * Copyright (c) 2005-2006 Fabrice Bellard * Copyright (c) 2012 Herve Poussineau + * Copyright (c) 2023 Mark Cave-Ayland * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -62,14 +63,38 @@ static void esp_lower_irq(ESPState *s) static void esp_raise_drq(ESPState *s) { - qemu_irq_raise(s->irq_data); - trace_esp_raise_drq(); + if (!(s->drq_state)) { + qemu_irq_raise(s->drq_irq); + trace_esp_raise_drq(); + s->drq_state = true; + } } static void esp_lower_drq(ESPState *s) { - qemu_irq_lower(s->irq_data); - trace_esp_lower_drq(); + if (s->drq_state) { + qemu_irq_lower(s->drq_irq); + trace_esp_lower_drq(); + s->drq_state = false; + } +} + +static const char *esp_phase_names[8] = { + "DATA OUT", "DATA IN", "COMMAND", "STATUS", + "(reserved)", "(reserved)", "MESSAGE OUT", "MESSAGE IN" +}; + +static void esp_set_phase(ESPState *s, uint8_t phase) +{ + s->rregs[ESP_RSTAT] &= ~7; + s->rregs[ESP_RSTAT] |= phase; + + trace_esp_set_phase(esp_phase_names[phase]); +} + +static uint8_t esp_get_phase(ESPState *s) +{ + return s->rregs[ESP_RSTAT] & 7; } void esp_dma_enable(ESPState *s, int irq, int level) @@ -99,42 +124,117 @@ void esp_request_cancelled(SCSIRequest *req) } } -static void esp_fifo_push(Fifo8 *fifo, uint8_t val) +static void esp_update_drq(ESPState *s) { - if (fifo8_num_used(fifo) == fifo->capacity) { - trace_esp_error_fifo_overrun(); + bool to_device; + + switch (esp_get_phase(s)) { + case STAT_MO: + case STAT_CD: + case STAT_DO: + to_device = true; + break; + + case STAT_DI: + case STAT_ST: + case STAT_MI: + to_device = false; + break; + + default: return; } - fifo8_push(fifo, val); + if (s->dma) { + /* DMA request so update DRQ according to transfer direction */ + if (to_device) { + if (fifo8_num_free(&s->fifo) < 2) { + esp_lower_drq(s); + } else { + esp_raise_drq(s); + } + } else { + if (fifo8_num_used(&s->fifo) < 2) { + esp_lower_drq(s); + } else { + esp_raise_drq(s); + } + } + } else { + /* Not a DMA request */ + esp_lower_drq(s); + } } -static uint8_t esp_fifo_pop(Fifo8 *fifo) +static void esp_fifo_push(ESPState *s, uint8_t val) { - if (fifo8_is_empty(fifo)) { - return 0; + if (fifo8_num_used(&s->fifo) == s->fifo.capacity) { + trace_esp_error_fifo_overrun(); + } else { + fifo8_push(&s->fifo, val); } - return fifo8_pop(fifo); + esp_update_drq(s); } -static uint32_t esp_fifo_pop_buf(Fifo8 *fifo, uint8_t *dest, int maxlen) +static void esp_fifo_push_buf(ESPState *s, uint8_t *buf, int len) +{ + fifo8_push_all(&s->fifo, buf, len); + esp_update_drq(s); +} + +static uint8_t esp_fifo_pop(ESPState *s) +{ + uint8_t val; + + if (fifo8_is_empty(&s->fifo)) { + val = 0; + } else { + val = fifo8_pop(&s->fifo); + } + + esp_update_drq(s); + return val; +} + +static uint32_t esp_fifo8_pop_buf(Fifo8 *fifo, uint8_t *dest, int maxlen) { const uint8_t *buf; - uint32_t n; + uint32_t n, n2; + int len; if (maxlen == 0) { return 0; } - buf = fifo8_pop_buf(fifo, maxlen, &n); + len = maxlen; + buf = fifo8_pop_buf(fifo, len, &n); if (dest) { memcpy(dest, buf, n); } + /* Add FIFO wraparound if needed */ + len -= n; + len = MIN(len, fifo8_num_used(fifo)); + if (len) { + buf = fifo8_pop_buf(fifo, len, &n2); + if (dest) { + memcpy(&dest[n], buf, n2); + } + n += n2; + } + return n; } +static uint32_t esp_fifo_pop_buf(ESPState *s, uint8_t *dest, int maxlen) +{ + uint32_t len = esp_fifo8_pop_buf(&s->fifo, dest, maxlen); + + esp_update_drq(s); + return len; +} + static uint32_t esp_get_tc(ESPState *s) { uint32_t dmalen; @@ -148,9 +248,15 @@ static uint32_t esp_get_tc(ESPState *s) static void esp_set_tc(ESPState *s, uint32_t dmalen) { + uint32_t old_tc = esp_get_tc(s); + s->rregs[ESP_TCLO] = dmalen; s->rregs[ESP_TCMID] = dmalen >> 8; s->rregs[ESP_TCHI] = dmalen >> 16; + + if (old_tc && dmalen == 0) { + s->rregs[ESP_RSTAT] |= STAT_TC; + } } static uint32_t esp_get_stc(ESPState *s) @@ -168,12 +274,7 @@ static uint8_t esp_pdma_read(ESPState *s) { uint8_t val; - if (s->do_cmd) { - val = esp_fifo_pop(&s->cmdfifo); - } else { - val = esp_fifo_pop(&s->fifo); - } - + val = esp_fifo_pop(s); return val; } @@ -181,23 +282,12 @@ static void esp_pdma_write(ESPState *s, uint8_t val) { uint32_t dmalen = esp_get_tc(s); - if (dmalen == 0) { - return; + esp_fifo_push(s, val); + + if (dmalen && s->drq_state) { + dmalen--; + esp_set_tc(s, dmalen); } - - if (s->do_cmd) { - esp_fifo_push(&s->cmdfifo, val); - } else { - esp_fifo_push(&s->fifo, val); - } - - dmalen--; - esp_set_tc(s, dmalen); -} - -static void esp_set_pdma_cb(ESPState *s, enum pdma_cb cb) -{ - s->pdma_cb = cb; } static int esp_select(ESPState *s) @@ -207,75 +297,31 @@ static int esp_select(ESPState *s) target = s->wregs[ESP_WBUSID] & BUSID_DID; s->ti_size = 0; - fifo8_reset(&s->fifo); + s->rregs[ESP_RSEQ] = SEQ_0; + + if (s->current_req) { + /* Started a new command before the old one finished. Cancel it. */ + scsi_req_cancel(s->current_req); + } s->current_dev = scsi_device_find(&s->bus, 0, target, 0); if (!s->current_dev) { /* No such drive */ s->rregs[ESP_RSTAT] = 0; s->rregs[ESP_RINTR] = INTR_DC; - s->rregs[ESP_RSEQ] = SEQ_0; esp_raise_irq(s); return -1; } /* * Note that we deliberately don't raise the IRQ here: this will be done - * either in do_command_phase() for DATA OUT transfers or by the deferred - * IRQ mechanism in esp_transfer_data() for DATA IN transfers + * either in esp_transfer_data() or esp_command_complete() */ - s->rregs[ESP_RINTR] |= INTR_FC; - s->rregs[ESP_RSEQ] = SEQ_CD; return 0; } -static uint32_t get_cmd(ESPState *s, uint32_t maxlen) -{ - uint8_t buf[ESP_CMDFIFO_SZ]; - uint32_t dmalen, n; - int target; - - if (s->current_req) { - /* Started a new command before the old one finished. Cancel it. */ - scsi_req_cancel(s->current_req); - } - - target = s->wregs[ESP_WBUSID] & BUSID_DID; - if (s->dma) { - dmalen = MIN(esp_get_tc(s), maxlen); - if (dmalen == 0) { - return 0; - } - if (s->dma_memory_read) { - s->dma_memory_read(s->dma_opaque, buf, dmalen); - dmalen = MIN(fifo8_num_free(&s->cmdfifo), dmalen); - fifo8_push_all(&s->cmdfifo, buf, dmalen); - } else { - if (esp_select(s) < 0) { - fifo8_reset(&s->cmdfifo); - return -1; - } - esp_raise_drq(s); - fifo8_reset(&s->cmdfifo); - return 0; - } - } else { - dmalen = MIN(fifo8_num_used(&s->fifo), maxlen); - if (dmalen == 0) { - return 0; - } - n = esp_fifo_pop_buf(&s->fifo, buf, dmalen); - n = MIN(fifo8_num_free(&s->cmdfifo), n); - fifo8_push_all(&s->cmdfifo, buf, n); - } - trace_esp_get_cmd(dmalen, target); - - if (esp_select(s) < 0) { - fifo8_reset(&s->cmdfifo); - return -1; - } - return dmalen; -} +static void esp_do_dma(ESPState *s); +static void esp_do_nodma(ESPState *s); static void do_command_phase(ESPState *s) { @@ -289,30 +335,32 @@ static void do_command_phase(ESPState *s) if (!cmdlen || !s->current_dev) { return; } - esp_fifo_pop_buf(&s->cmdfifo, buf, cmdlen); + esp_fifo8_pop_buf(&s->cmdfifo, buf, cmdlen); current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, s->lun); - s->current_req = scsi_req_new(current_lun, 0, s->lun, buf, s); + if (!current_lun) { + /* No such drive */ + s->rregs[ESP_RSTAT] = 0; + s->rregs[ESP_RINTR] = INTR_DC; + s->rregs[ESP_RSEQ] = SEQ_0; + esp_raise_irq(s); + return; + } + + s->current_req = scsi_req_new(current_lun, 0, s->lun, buf, cmdlen, s); datalen = scsi_req_enqueue(s->current_req); s->ti_size = datalen; fifo8_reset(&s->cmdfifo); + s->data_ready = false; if (datalen != 0) { - s->rregs[ESP_RSTAT] = STAT_TC; - s->rregs[ESP_RSEQ] = SEQ_CD; - s->ti_cmd = 0; - esp_set_tc(s, 0); + /* + * Switch to DATA phase but wait until initial data xfer is + * complete before raising the command completion interrupt + */ if (datalen > 0) { - /* - * Switch to DATA IN phase but wait until initial data xfer is - * complete before raising the command completion interrupt - */ - s->data_in_ready = false; - s->rregs[ESP_RSTAT] |= STAT_DI; + esp_set_phase(s, STAT_DI); } else { - s->rregs[ESP_RSTAT] |= STAT_DO; - s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC; - esp_raise_irq(s); - esp_lower_drq(s); + esp_set_phase(s, STAT_DO); } scsi_req_continue(s->current_req); return; @@ -322,7 +370,8 @@ static void do_command_phase(ESPState *s) static void do_message_phase(ESPState *s) { if (s->cmdfifo_cdb_offset) { - uint8_t message = esp_fifo_pop(&s->cmdfifo); + uint8_t message = fifo8_is_empty(&s->cmdfifo) ? 0 : + fifo8_pop(&s->cmdfifo); trace_esp_do_identify(message); s->lun = message & 7; @@ -332,7 +381,7 @@ static void do_message_phase(ESPState *s) /* Ignore extended messages for now */ if (s->cmdfifo_cdb_offset) { int len = MIN(s->cmdfifo_cdb_offset, fifo8_num_used(&s->cmdfifo)); - esp_fifo_pop_buf(&s->cmdfifo, NULL, len); + esp_fifo8_pop_buf(&s->cmdfifo, NULL, len); s->cmdfifo_cdb_offset = 0; } } @@ -344,434 +393,389 @@ static void do_cmd(ESPState *s) do_command_phase(s); } -static void satn_pdma_cb(ESPState *s) -{ - if (!esp_get_tc(s) && !fifo8_is_empty(&s->cmdfifo)) { - s->cmdfifo_cdb_offset = 1; - s->do_cmd = 0; - do_cmd(s); - } -} - static void handle_satn(ESPState *s) { - int32_t cmdlen; - if (s->dma && !s->dma_enabled) { s->dma_cb = handle_satn; return; } - esp_set_pdma_cb(s, SATN_PDMA_CB); - cmdlen = get_cmd(s, ESP_CMDFIFO_SZ); - if (cmdlen > 0) { - s->cmdfifo_cdb_offset = 1; - s->do_cmd = 0; - do_cmd(s); - } else if (cmdlen == 0) { - s->do_cmd = 1; - /* Target present, but no cmd yet - switch to command phase */ - s->rregs[ESP_RSEQ] = SEQ_CD; - s->rregs[ESP_RSTAT] = STAT_CD; - } -} -static void s_without_satn_pdma_cb(ESPState *s) -{ - if (!esp_get_tc(s) && !fifo8_is_empty(&s->cmdfifo)) { - s->cmdfifo_cdb_offset = 0; - s->do_cmd = 0; - do_cmd(s); + if (esp_select(s) < 0) { + return; + } + + esp_set_phase(s, STAT_MO); + + if (s->dma) { + esp_do_dma(s); + } else { + esp_do_nodma(s); } } static void handle_s_without_atn(ESPState *s) { - int32_t cmdlen; - if (s->dma && !s->dma_enabled) { s->dma_cb = handle_s_without_atn; return; } - esp_set_pdma_cb(s, S_WITHOUT_SATN_PDMA_CB); - cmdlen = get_cmd(s, ESP_CMDFIFO_SZ); - if (cmdlen > 0) { - s->cmdfifo_cdb_offset = 0; - s->do_cmd = 0; - do_cmd(s); - } else if (cmdlen == 0) { - s->do_cmd = 1; - /* Target present, but no cmd yet - switch to command phase */ - s->rregs[ESP_RSEQ] = SEQ_CD; - s->rregs[ESP_RSTAT] = STAT_CD; - } -} -static void satn_stop_pdma_cb(ESPState *s) -{ - if (!esp_get_tc(s) && !fifo8_is_empty(&s->cmdfifo)) { - trace_esp_handle_satn_stop(fifo8_num_used(&s->cmdfifo)); - s->do_cmd = 1; - s->cmdfifo_cdb_offset = 1; - s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD; - s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC; - s->rregs[ESP_RSEQ] = SEQ_CD; - esp_raise_irq(s); + if (esp_select(s) < 0) { + return; + } + + esp_set_phase(s, STAT_CD); + s->cmdfifo_cdb_offset = 0; + + if (s->dma) { + esp_do_dma(s); + } else { + esp_do_nodma(s); } } static void handle_satn_stop(ESPState *s) { - int32_t cmdlen; - if (s->dma && !s->dma_enabled) { s->dma_cb = handle_satn_stop; return; } - esp_set_pdma_cb(s, SATN_STOP_PDMA_CB); - cmdlen = get_cmd(s, 1); - if (cmdlen > 0) { - trace_esp_handle_satn_stop(fifo8_num_used(&s->cmdfifo)); - s->do_cmd = 1; - s->cmdfifo_cdb_offset = 1; - s->rregs[ESP_RSTAT] = STAT_MO; - s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC; - s->rregs[ESP_RSEQ] = SEQ_MO; - esp_raise_irq(s); - } else if (cmdlen == 0) { - s->do_cmd = 1; - /* Target present, switch to message out phase */ - s->rregs[ESP_RSEQ] = SEQ_MO; - s->rregs[ESP_RSTAT] = STAT_MO; + + if (esp_select(s) < 0) { + return; + } + + esp_set_phase(s, STAT_MO); + s->cmdfifo_cdb_offset = 0; + + if (s->dma) { + esp_do_dma(s); + } else { + esp_do_nodma(s); } } -static void write_response_pdma_cb(ESPState *s) +static void handle_pad(ESPState *s) { - s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST; - s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC; - s->rregs[ESP_RSEQ] = SEQ_CD; - esp_raise_irq(s); + if (s->dma) { + esp_do_dma(s); + } else { + esp_do_nodma(s); + } } static void write_response(ESPState *s) { - uint8_t buf[2]; - trace_esp_write_response(s->status); - buf[0] = s->status; - buf[1] = 0; - if (s->dma) { - if (s->dma_memory_write) { - s->dma_memory_write(s->dma_opaque, buf, 2); - s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST; - s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC; - s->rregs[ESP_RSEQ] = SEQ_CD; - } else { - esp_set_pdma_cb(s, WRITE_RESPONSE_PDMA_CB); - esp_raise_drq(s); - return; - } + esp_do_dma(s); } else { - fifo8_reset(&s->fifo); - fifo8_push_all(&s->fifo, buf, 2); - s->rregs[ESP_RFLAGS] = 2; + esp_do_nodma(s); } - esp_raise_irq(s); } -static void esp_dma_done(ESPState *s) +static bool esp_cdb_ready(ESPState *s) { - s->rregs[ESP_RSTAT] |= STAT_TC; - s->rregs[ESP_RINTR] |= INTR_BS; - s->rregs[ESP_RFLAGS] = 0; - esp_set_tc(s, 0); - esp_raise_irq(s); -} - -static void do_dma_pdma_cb(ESPState *s) -{ - int to_device = ((s->rregs[ESP_RSTAT] & 7) == STAT_DO); - int len; + int len = fifo8_num_used(&s->cmdfifo) - s->cmdfifo_cdb_offset; + const uint8_t *pbuf; uint32_t n; + int cdblen; - if (s->do_cmd) { - /* Ensure we have received complete command after SATN and stop */ - if (esp_get_tc(s) || fifo8_is_empty(&s->cmdfifo)) { - return; - } - - s->ti_size = 0; - if ((s->rregs[ESP_RSTAT] & 7) == STAT_CD) { - /* No command received */ - if (s->cmdfifo_cdb_offset == fifo8_num_used(&s->cmdfifo)) { - return; - } - - /* Command has been received */ - s->do_cmd = 0; - do_cmd(s); - } else { - /* - * Extra message out bytes received: update cmdfifo_cdb_offset - * and then switch to commmand phase - */ - s->cmdfifo_cdb_offset = fifo8_num_used(&s->cmdfifo); - s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD; - s->rregs[ESP_RSEQ] = SEQ_CD; - s->rregs[ESP_RINTR] |= INTR_BS; - esp_raise_irq(s); - } - return; + if (len <= 0) { + return false; } - if (!s->current_req) { - return; + pbuf = fifo8_peek_buf(&s->cmdfifo, len, &n); + if (n < len) { + /* + * In normal use the cmdfifo should never wrap, but include this check + * to prevent a malicious guest from reading past the end of the + * cmdfifo data buffer below + */ + return false; } - if (to_device) { - /* Copy FIFO data to device */ - len = MIN(s->async_len, ESP_FIFO_SZ); - len = MIN(len, fifo8_num_used(&s->fifo)); - n = esp_fifo_pop_buf(&s->fifo, s->async_buf, len); - s->async_buf += n; - s->async_len -= n; - s->ti_size += n; + cdblen = scsi_cdb_length((uint8_t *)&pbuf[s->cmdfifo_cdb_offset]); - if (n < len) { - /* Unaligned accesses can cause FIFO wraparound */ - len = len - n; - n = esp_fifo_pop_buf(&s->fifo, s->async_buf, len); - s->async_buf += n; - s->async_len -= n; - s->ti_size += n; - } + return cdblen < 0 ? false : (len >= cdblen); +} - if (s->async_len == 0) { - scsi_req_continue(s->current_req); - return; - } - - if (esp_get_tc(s) == 0) { - esp_lower_drq(s); - esp_dma_done(s); - } - - return; - } else { - if (s->async_len == 0) { - /* Defer until the scsi layer has completed */ - scsi_req_continue(s->current_req); - s->data_in_ready = false; - return; - } - - if (esp_get_tc(s) != 0) { - /* Copy device data to FIFO */ - len = MIN(s->async_len, esp_get_tc(s)); - len = MIN(len, fifo8_num_free(&s->fifo)); - fifo8_push_all(&s->fifo, s->async_buf, len); - s->async_buf += len; - s->async_len -= len; - s->ti_size -= len; - esp_set_tc(s, esp_get_tc(s) - len); - - if (esp_get_tc(s) == 0) { - /* Indicate transfer to FIFO is complete */ - s->rregs[ESP_RSTAT] |= STAT_TC; - } - return; - } - - /* Partially filled a scsi buffer. Complete immediately. */ - esp_lower_drq(s); - esp_dma_done(s); +static void esp_dma_ti_check(ESPState *s) +{ + if (esp_get_tc(s) == 0 && fifo8_num_used(&s->fifo) < 2) { + s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); } } static void esp_do_dma(ESPState *s) { uint32_t len, cmdlen; - int to_device = ((s->rregs[ESP_RSTAT] & 7) == STAT_DO); uint8_t buf[ESP_CMDFIFO_SZ]; len = esp_get_tc(s); - if (s->do_cmd) { - /* - * handle_ti_cmd() case: esp_do_dma() is called only from - * handle_ti_cmd() with do_cmd != NULL (see the assert()) - */ + + switch (esp_get_phase(s)) { + case STAT_MO: + if (s->dma_memory_read) { + len = MIN(len, fifo8_num_free(&s->cmdfifo)); + s->dma_memory_read(s->dma_opaque, buf, len); + esp_set_tc(s, esp_get_tc(s) - len); + } else { + len = esp_fifo_pop_buf(s, buf, fifo8_num_used(&s->fifo)); + len = MIN(fifo8_num_free(&s->cmdfifo), len); + } + + fifo8_push_all(&s->cmdfifo, buf, len); + s->cmdfifo_cdb_offset += len; + + switch (s->rregs[ESP_CMD]) { + case CMD_SELATN | CMD_DMA: + if (fifo8_num_used(&s->cmdfifo) >= 1) { + /* First byte received, switch to command phase */ + esp_set_phase(s, STAT_CD); + s->rregs[ESP_RSEQ] = SEQ_CD; + s->cmdfifo_cdb_offset = 1; + + if (fifo8_num_used(&s->cmdfifo) > 1) { + /* Process any additional command phase data */ + esp_do_dma(s); + } + } + break; + + case CMD_SELATNS | CMD_DMA: + if (fifo8_num_used(&s->cmdfifo) == 1) { + /* First byte received, stop in message out phase */ + s->rregs[ESP_RSEQ] = SEQ_MO; + s->cmdfifo_cdb_offset = 1; + + /* Raise command completion interrupt */ + s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC; + esp_raise_irq(s); + } + break; + + case CMD_TI | CMD_DMA: + /* ATN remains asserted until TC == 0 */ + if (esp_get_tc(s) == 0) { + esp_set_phase(s, STAT_CD); + s->rregs[ESP_CMD] = 0; + s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); + } + break; + } + break; + + case STAT_CD: cmdlen = fifo8_num_used(&s->cmdfifo); trace_esp_do_dma(cmdlen, len); if (s->dma_memory_read) { len = MIN(len, fifo8_num_free(&s->cmdfifo)); s->dma_memory_read(s->dma_opaque, buf, len); fifo8_push_all(&s->cmdfifo, buf, len); + esp_set_tc(s, esp_get_tc(s) - len); } else { - esp_set_pdma_cb(s, DO_DMA_PDMA_CB); - esp_raise_drq(s); - return; + len = esp_fifo_pop_buf(s, buf, fifo8_num_used(&s->fifo)); + len = MIN(fifo8_num_free(&s->cmdfifo), len); + fifo8_push_all(&s->cmdfifo, buf, len); } trace_esp_handle_ti_cmd(cmdlen); s->ti_size = 0; - if ((s->rregs[ESP_RSTAT] & 7) == STAT_CD) { - /* No command received */ - if (s->cmdfifo_cdb_offset == fifo8_num_used(&s->cmdfifo)) { - return; - } - + if (esp_get_tc(s) == 0) { /* Command has been received */ - s->do_cmd = 0; do_cmd(s); - } else { - /* - * Extra message out bytes received: update cmdfifo_cdb_offset - * and then switch to commmand phase - */ - s->cmdfifo_cdb_offset = fifo8_num_used(&s->cmdfifo); - s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD; - s->rregs[ESP_RSEQ] = SEQ_CD; - s->rregs[ESP_RINTR] |= INTR_BS; - esp_raise_irq(s); } - return; - } - if (!s->current_req) { - return; - } - if (s->async_len == 0) { - /* Defer until data is available. */ - return; - } - if (len > s->async_len) { - len = s->async_len; - } - if (to_device) { - if (s->dma_memory_read) { - s->dma_memory_read(s->dma_opaque, s->async_buf, len); - } else { - esp_set_pdma_cb(s, DO_DMA_PDMA_CB); - esp_raise_drq(s); + break; + + case STAT_DO: + if (!s->current_req) { return; } - } else { - if (s->dma_memory_write) { - s->dma_memory_write(s->dma_opaque, s->async_buf, len); - } else { - /* Adjust TC for any leftover data in the FIFO */ - if (!fifo8_is_empty(&s->fifo)) { - esp_set_tc(s, esp_get_tc(s) - fifo8_num_used(&s->fifo)); + if (s->async_len == 0 && esp_get_tc(s) && s->ti_size) { + /* Defer until data is available. */ + return; + } + if (len > s->async_len) { + len = s->async_len; + } + + switch (s->rregs[ESP_CMD]) { + case CMD_TI | CMD_DMA: + if (s->dma_memory_read) { + s->dma_memory_read(s->dma_opaque, s->async_buf, len); + esp_set_tc(s, esp_get_tc(s) - len); + } else { + /* Copy FIFO data to device */ + len = MIN(s->async_len, ESP_FIFO_SZ); + len = MIN(len, fifo8_num_used(&s->fifo)); + len = esp_fifo_pop_buf(s, s->async_buf, len); + } + + s->async_buf += len; + s->async_len -= len; + s->ti_size += len; + break; + + case CMD_PAD | CMD_DMA: + /* Copy TC zero bytes into the incoming stream */ + if (!s->dma_memory_read) { + len = MIN(s->async_len, ESP_FIFO_SZ); + len = MIN(len, fifo8_num_free(&s->fifo)); + } + + memset(s->async_buf, 0, len); + + s->async_buf += len; + s->async_len -= len; + s->ti_size += len; + break; + } + + if (s->async_len == 0 && fifo8_num_used(&s->fifo) < 2) { + /* Defer until the scsi layer has completed */ + scsi_req_continue(s->current_req); + return; + } + + esp_dma_ti_check(s); + break; + + case STAT_DI: + if (!s->current_req) { + return; + } + if (s->async_len == 0 && esp_get_tc(s) && s->ti_size) { + /* Defer until data is available. */ + return; + } + if (len > s->async_len) { + len = s->async_len; + } + + switch (s->rregs[ESP_CMD]) { + case CMD_TI | CMD_DMA: + if (s->dma_memory_write) { + s->dma_memory_write(s->dma_opaque, s->async_buf, len); + } else { + /* Copy device data to FIFO */ + len = MIN(len, fifo8_num_free(&s->fifo)); + esp_fifo_push_buf(s, s->async_buf, len); } - /* Copy device data to FIFO */ - len = MIN(len, fifo8_num_free(&s->fifo)); - fifo8_push_all(&s->fifo, s->async_buf, len); s->async_buf += len; s->async_len -= len; s->ti_size -= len; + esp_set_tc(s, esp_get_tc(s) - len); + break; - /* - * MacOS toolbox uses a TI length of 16 bytes for all commands, so - * commands shorter than this must be padded accordingly - */ - if (len < esp_get_tc(s) && esp_get_tc(s) <= ESP_FIFO_SZ) { - while (fifo8_num_used(&s->fifo) < ESP_FIFO_SZ) { - esp_fifo_push(&s->fifo, 0); - len++; + case CMD_PAD | CMD_DMA: + /* Drop TC bytes from the incoming stream */ + if (!s->dma_memory_write) { + len = MIN(len, fifo8_num_free(&s->fifo)); + } + + s->async_buf += len; + s->async_len -= len; + s->ti_size -= len; + esp_set_tc(s, esp_get_tc(s) - len); + break; + } + + if (s->async_len == 0 && s->ti_size == 0 && esp_get_tc(s)) { + /* If the guest underflows TC then terminate SCSI request */ + scsi_req_continue(s->current_req); + return; + } + + if (s->async_len == 0 && fifo8_num_used(&s->fifo) < 2) { + /* Defer until the scsi layer has completed */ + scsi_req_continue(s->current_req); + return; + } + + esp_dma_ti_check(s); + break; + + case STAT_ST: + switch (s->rregs[ESP_CMD]) { + case CMD_ICCS | CMD_DMA: + len = MIN(len, 1); + + if (len) { + buf[0] = s->status; + + if (s->dma_memory_write) { + s->dma_memory_write(s->dma_opaque, buf, len); + } else { + esp_fifo_push_buf(s, buf, len); + } + + esp_set_tc(s, esp_get_tc(s) - len); + esp_set_phase(s, STAT_MI); + + if (esp_get_tc(s) > 0) { + /* Process any message in phase data */ + esp_do_dma(s); } } + break; - esp_set_tc(s, esp_get_tc(s) - len); - esp_set_pdma_cb(s, DO_DMA_PDMA_CB); - esp_raise_drq(s); - - /* Indicate transfer to FIFO is complete */ - s->rregs[ESP_RSTAT] |= STAT_TC; - return; + default: + /* Consume remaining data if the guest underflows TC */ + if (fifo8_num_used(&s->fifo) < 2) { + s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); + } + break; } - } - esp_set_tc(s, esp_get_tc(s) - len); - s->async_buf += len; - s->async_len -= len; - if (to_device) { - s->ti_size += len; - } else { - s->ti_size -= len; - } - if (s->async_len == 0) { - scsi_req_continue(s->current_req); - /* - * If there is still data to be read from the device then - * complete the DMA operation immediately. Otherwise defer - * until the scsi layer has completed. - */ - if (to_device || esp_get_tc(s) != 0 || s->ti_size == 0) { - return; - } - } + break; - /* Partially filled a scsi buffer. Complete immediately. */ - esp_dma_done(s); - esp_lower_drq(s); + case STAT_MI: + switch (s->rregs[ESP_CMD]) { + case CMD_ICCS | CMD_DMA: + len = MIN(len, 1); + + if (len) { + buf[0] = 0; + + if (s->dma_memory_write) { + s->dma_memory_write(s->dma_opaque, buf, len); + } else { + esp_fifo_push_buf(s, buf, len); + } + + esp_set_tc(s, esp_get_tc(s) - len); + + /* Raise end of command interrupt */ + s->rregs[ESP_RINTR] |= INTR_FC; + esp_raise_irq(s); + } + break; + } + break; + } } -static void esp_do_nodma(ESPState *s) +static void esp_nodma_ti_dataout(ESPState *s) { - int to_device = ((s->rregs[ESP_RSTAT] & 7) == STAT_DO); - uint32_t cmdlen; int len; - if (s->do_cmd) { - cmdlen = fifo8_num_used(&s->cmdfifo); - trace_esp_handle_ti_cmd(cmdlen); - s->ti_size = 0; - if ((s->rregs[ESP_RSTAT] & 7) == STAT_CD) { - /* No command received */ - if (s->cmdfifo_cdb_offset == fifo8_num_used(&s->cmdfifo)) { - return; - } - - /* Command has been received */ - s->do_cmd = 0; - do_cmd(s); - } else { - /* - * Extra message out bytes received: update cmdfifo_cdb_offset - * and then switch to commmand phase - */ - s->cmdfifo_cdb_offset = fifo8_num_used(&s->cmdfifo); - s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD; - s->rregs[ESP_RSEQ] = SEQ_CD; - s->rregs[ESP_RINTR] |= INTR_BS; - esp_raise_irq(s); - } - return; - } - if (!s->current_req) { return; } - if (s->async_len == 0) { /* Defer until data is available. */ return; } - - if (to_device) { - len = MIN(fifo8_num_used(&s->fifo), ESP_FIFO_SZ); - esp_fifo_pop_buf(&s->fifo, s->async_buf, len); - s->async_buf += len; - s->async_len -= len; - s->ti_size += len; - } else { - if (fifo8_is_empty(&s->fifo)) { - fifo8_push(&s->fifo, s->async_buf[0]); - s->async_buf++; - s->async_len--; - s->ti_size--; - } - } + len = MIN(s->async_len, ESP_FIFO_SZ); + len = MIN(len, fifo8_num_used(&s->fifo)); + esp_fifo_pop_buf(s, s->async_buf, len); + s->async_buf += len; + s->async_len -= len; + s->ti_size += len; if (s->async_len == 0) { scsi_req_continue(s->current_req); @@ -782,33 +786,186 @@ static void esp_do_nodma(ESPState *s) esp_raise_irq(s); } -static void esp_pdma_cb(ESPState *s) +static void esp_do_nodma(ESPState *s) { - switch (s->pdma_cb) { - case SATN_PDMA_CB: - satn_pdma_cb(s); + uint8_t buf[ESP_FIFO_SZ]; + uint32_t cmdlen; + int len; + + switch (esp_get_phase(s)) { + case STAT_MO: + switch (s->rregs[ESP_CMD]) { + case CMD_SELATN: + /* Copy FIFO into cmdfifo */ + len = esp_fifo_pop_buf(s, buf, fifo8_num_used(&s->fifo)); + len = MIN(fifo8_num_free(&s->cmdfifo), len); + fifo8_push_all(&s->cmdfifo, buf, len); + + if (fifo8_num_used(&s->cmdfifo) >= 1) { + /* First byte received, switch to command phase */ + esp_set_phase(s, STAT_CD); + s->rregs[ESP_RSEQ] = SEQ_CD; + s->cmdfifo_cdb_offset = 1; + + if (fifo8_num_used(&s->cmdfifo) > 1) { + /* Process any additional command phase data */ + esp_do_nodma(s); + } + } + break; + + case CMD_SELATNS: + /* Copy one byte from FIFO into cmdfifo */ + len = esp_fifo_pop_buf(s, buf, + MIN(fifo8_num_used(&s->fifo), 1)); + len = MIN(fifo8_num_free(&s->cmdfifo), len); + fifo8_push_all(&s->cmdfifo, buf, len); + + if (fifo8_num_used(&s->cmdfifo) >= 1) { + /* First byte received, stop in message out phase */ + s->rregs[ESP_RSEQ] = SEQ_MO; + s->cmdfifo_cdb_offset = 1; + + /* Raise command completion interrupt */ + s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC; + esp_raise_irq(s); + } + break; + + case CMD_TI: + /* Copy FIFO into cmdfifo */ + len = esp_fifo_pop_buf(s, buf, fifo8_num_used(&s->fifo)); + len = MIN(fifo8_num_free(&s->cmdfifo), len); + fifo8_push_all(&s->cmdfifo, buf, len); + + /* ATN remains asserted until FIFO empty */ + s->cmdfifo_cdb_offset = fifo8_num_used(&s->cmdfifo); + esp_set_phase(s, STAT_CD); + s->rregs[ESP_CMD] = 0; + s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); + break; + } break; - case S_WITHOUT_SATN_PDMA_CB: - s_without_satn_pdma_cb(s); + + case STAT_CD: + switch (s->rregs[ESP_CMD]) { + case CMD_TI: + /* Copy FIFO into cmdfifo */ + len = esp_fifo_pop_buf(s, buf, fifo8_num_used(&s->fifo)); + len = MIN(fifo8_num_free(&s->cmdfifo), len); + fifo8_push_all(&s->cmdfifo, buf, len); + + cmdlen = fifo8_num_used(&s->cmdfifo); + trace_esp_handle_ti_cmd(cmdlen); + + /* CDB may be transferred in one or more TI commands */ + if (esp_cdb_ready(s)) { + /* Command has been received */ + do_cmd(s); + } else { + /* + * If data was transferred from the FIFO then raise bus + * service interrupt to indicate transfer complete. Otherwise + * defer until the next FIFO write. + */ + if (len) { + /* Raise interrupt to indicate transfer complete */ + s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); + } + } + break; + + case CMD_SEL | CMD_DMA: + case CMD_SELATN | CMD_DMA: + /* Copy FIFO into cmdfifo */ + len = esp_fifo_pop_buf(s, buf, fifo8_num_used(&s->fifo)); + len = MIN(fifo8_num_free(&s->cmdfifo), len); + fifo8_push_all(&s->cmdfifo, buf, len); + + /* Handle when DMA transfer is terminated by non-DMA FIFO write */ + if (esp_cdb_ready(s)) { + /* Command has been received */ + do_cmd(s); + } + break; + + case CMD_SEL: + case CMD_SELATN: + /* FIFO already contain entire CDB: copy to cmdfifo and execute */ + len = esp_fifo_pop_buf(s, buf, fifo8_num_used(&s->fifo)); + len = MIN(fifo8_num_free(&s->cmdfifo), len); + fifo8_push_all(&s->cmdfifo, buf, len); + + do_cmd(s); + break; + } break; - case SATN_STOP_PDMA_CB: - satn_stop_pdma_cb(s); + + case STAT_DO: + /* Accumulate data in FIFO until non-DMA TI is executed */ break; - case WRITE_RESPONSE_PDMA_CB: - write_response_pdma_cb(s); + + case STAT_DI: + if (!s->current_req) { + return; + } + if (s->async_len == 0) { + /* Defer until data is available. */ + return; + } + if (fifo8_is_empty(&s->fifo)) { + esp_fifo_push(s, s->async_buf[0]); + s->async_buf++; + s->async_len--; + s->ti_size--; + } + + if (s->async_len == 0) { + scsi_req_continue(s->current_req); + return; + } + + /* If preloading the FIFO, defer until TI command issued */ + if (s->rregs[ESP_CMD] != CMD_TI) { + return; + } + + s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); break; - case DO_DMA_PDMA_CB: - do_dma_pdma_cb(s); + + case STAT_ST: + switch (s->rregs[ESP_CMD]) { + case CMD_ICCS: + esp_fifo_push(s, s->status); + esp_set_phase(s, STAT_MI); + + /* Process any message in phase data */ + esp_do_nodma(s); + break; + } + break; + + case STAT_MI: + switch (s->rregs[ESP_CMD]) { + case CMD_ICCS: + esp_fifo_push(s, 0); + + /* Raise end of command interrupt */ + s->rregs[ESP_RINTR] |= INTR_FC; + esp_raise_irq(s); + break; + } break; - default: - g_assert_not_reached(); } } void esp_command_complete(SCSIRequest *req, size_t resid) { ESPState *s = req->hba_private; - int to_device = ((s->rregs[ESP_RSTAT] & 7) == STAT_DO); + int to_device = (esp_get_phase(s) == STAT_DO); trace_esp_command_complete(); @@ -820,7 +977,6 @@ void esp_command_complete(SCSIRequest *req, size_t resid) if (s->ti_size != 0) { trace_esp_command_complete_unexpected(); } - s->ti_size = 0; } s->async_len = 0; @@ -830,15 +986,35 @@ void esp_command_complete(SCSIRequest *req, size_t resid) s->status = req->status; /* - * If the transfer is finished, switch to status phase. For non-DMA - * transfers from the target the last byte is still in the FIFO + * Switch to status phase. For non-DMA transfers from the target the last + * byte is still in the FIFO */ - if (s->ti_size == 0) { - s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST; - esp_dma_done(s); - esp_lower_drq(s); + s->ti_size = 0; + + switch (s->rregs[ESP_CMD]) { + case CMD_SEL | CMD_DMA: + case CMD_SEL: + case CMD_SELATN | CMD_DMA: + case CMD_SELATN: + /* + * No data phase for sequencer command so raise deferred bus service + * and function complete interrupt + */ + s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC; + s->rregs[ESP_RSEQ] = SEQ_CD; + break; + + case CMD_TI | CMD_DMA: + case CMD_TI: + s->rregs[ESP_CMD] = 0; + break; } + /* Raise bus service interrupt to indicate change to STATUS phase */ + esp_set_phase(s, STAT_ST); + s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); + if (s->current_req) { scsi_req_unref(s->current_req); s->current_req = NULL; @@ -849,48 +1025,66 @@ void esp_command_complete(SCSIRequest *req, size_t resid) void esp_transfer_data(SCSIRequest *req, uint32_t len) { ESPState *s = req->hba_private; - int to_device = ((s->rregs[ESP_RSTAT] & 7) == STAT_DO); uint32_t dmalen = esp_get_tc(s); - assert(!s->do_cmd); trace_esp_transfer_data(dmalen, s->ti_size); s->async_len = len; s->async_buf = scsi_req_get_buf(req); - if (!to_device && !s->data_in_ready) { - /* - * Initial incoming data xfer is complete so raise command - * completion interrupt - */ - s->data_in_ready = true; - s->rregs[ESP_RSTAT] |= STAT_TC; - s->rregs[ESP_RINTR] |= INTR_BS; + if (!s->data_ready) { + s->data_ready = true; + + switch (s->rregs[ESP_CMD]) { + case CMD_SEL | CMD_DMA: + case CMD_SEL: + case CMD_SELATN | CMD_DMA: + case CMD_SELATN: + /* + * Initial incoming data xfer is complete for sequencer command + * so raise deferred bus service and function complete interrupt + */ + s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC; + s->rregs[ESP_RSEQ] = SEQ_CD; + break; + + case CMD_SELATNS | CMD_DMA: + case CMD_SELATNS: + /* + * Initial incoming data xfer is complete so raise command + * completion interrupt + */ + s->rregs[ESP_RINTR] |= INTR_BS; + s->rregs[ESP_RSEQ] = SEQ_MO; + break; + + case CMD_TI | CMD_DMA: + case CMD_TI: + /* + * Bus service interrupt raised because of initial change to + * DATA phase + */ + s->rregs[ESP_CMD] = 0; + s->rregs[ESP_RINTR] |= INTR_BS; + break; + } + esp_raise_irq(s); } - if (s->ti_cmd == 0) { - /* - * Always perform the initial transfer upon reception of the next TI - * command to ensure the DMA/non-DMA status of the command is correct. - * It is not possible to use s->dma directly in the section below as - * some OSs send non-DMA NOP commands after a DMA transfer. Hence if the - * async data transfer is delayed then s->dma is set incorrectly. - */ - return; - } + /* + * Always perform the initial transfer upon reception of the next TI + * command to ensure the DMA/non-DMA status of the command is correct. + * It is not possible to use s->dma directly in the section below as + * some OSs send non-DMA NOP commands after a DMA transfer. Hence if the + * async data transfer is delayed then s->dma is set incorrectly. + */ - if (s->ti_cmd == (CMD_TI | CMD_DMA)) { - if (dmalen) { - esp_do_dma(s); - } else if (s->ti_size <= 0) { - /* - * If this was the last part of a DMA transfer then the - * completion interrupt is deferred to here. - */ - esp_dma_done(s); - esp_lower_drq(s); - } - } else if (s->ti_cmd == CMD_TI) { + if (s->rregs[ESP_CMD] == (CMD_TI | CMD_DMA)) { + /* When the SCSI layer returns more data, raise deferred INTR_BS */ + esp_dma_ti_check(s); + + esp_do_dma(s); + } else if (s->rregs[ESP_CMD] == CMD_TI) { esp_do_nodma(s); } } @@ -904,15 +1098,17 @@ static void handle_ti(ESPState *s) return; } - s->ti_cmd = s->rregs[ESP_CMD]; if (s->dma) { dmalen = esp_get_tc(s); trace_esp_handle_ti(dmalen); - s->rregs[ESP_RSTAT] &= ~STAT_TC; esp_do_dma(s); } else { trace_esp_handle_ti(s->ti_size); esp_do_nodma(s); + + if (esp_get_phase(s) == STAT_DO) { + esp_nodma_ti_dataout(s); + } } } @@ -926,7 +1122,6 @@ void esp_hard_reset(ESPState *s) fifo8_reset(&s->fifo); fifo8_reset(&s->cmdfifo); s->dma = 0; - s->do_cmd = 0; s->dma_cb = NULL; s->rregs[ESP_CFG1] = 7; @@ -935,10 +1130,15 @@ void esp_hard_reset(ESPState *s) static void esp_soft_reset(ESPState *s) { qemu_irq_lower(s->irq); - qemu_irq_lower(s->irq_data); + qemu_irq_lower(s->drq_irq); esp_hard_reset(s); } +static void esp_bus_reset(ESPState *s) +{ + bus_cold_reset(BUS(&s->bus)); +} + static void parent_esp_reset(ESPState *s, int irq, int level) { if (level) { @@ -946,31 +1146,100 @@ static void parent_esp_reset(ESPState *s, int irq, int level) } } +static void esp_run_cmd(ESPState *s) +{ + uint8_t cmd = s->rregs[ESP_CMD]; + + if (cmd & CMD_DMA) { + s->dma = 1; + /* Reload DMA counter. */ + if (esp_get_stc(s) == 0) { + esp_set_tc(s, 0x10000); + } else { + esp_set_tc(s, esp_get_stc(s)); + } + } else { + s->dma = 0; + } + switch (cmd & CMD_CMD) { + case CMD_NOP: + trace_esp_mem_writeb_cmd_nop(cmd); + break; + case CMD_FLUSH: + trace_esp_mem_writeb_cmd_flush(cmd); + fifo8_reset(&s->fifo); + break; + case CMD_RESET: + trace_esp_mem_writeb_cmd_reset(cmd); + esp_soft_reset(s); + break; + case CMD_BUSRESET: + trace_esp_mem_writeb_cmd_bus_reset(cmd); + esp_bus_reset(s); + if (!(s->wregs[ESP_CFG1] & CFG1_RESREPT)) { + s->rregs[ESP_RINTR] |= INTR_RST; + esp_raise_irq(s); + } + break; + case CMD_TI: + trace_esp_mem_writeb_cmd_ti(cmd); + handle_ti(s); + break; + case CMD_ICCS: + trace_esp_mem_writeb_cmd_iccs(cmd); + write_response(s); + break; + case CMD_MSGACC: + trace_esp_mem_writeb_cmd_msgacc(cmd); + s->rregs[ESP_RINTR] |= INTR_DC; + s->rregs[ESP_RSEQ] = 0; + s->rregs[ESP_RFLAGS] = 0; + esp_raise_irq(s); + break; + case CMD_PAD: + trace_esp_mem_writeb_cmd_pad(cmd); + handle_pad(s); + break; + case CMD_SATN: + trace_esp_mem_writeb_cmd_satn(cmd); + break; + case CMD_RSTATN: + trace_esp_mem_writeb_cmd_rstatn(cmd); + break; + case CMD_SEL: + trace_esp_mem_writeb_cmd_sel(cmd); + handle_s_without_atn(s); + break; + case CMD_SELATN: + trace_esp_mem_writeb_cmd_selatn(cmd); + handle_satn(s); + break; + case CMD_SELATNS: + trace_esp_mem_writeb_cmd_selatns(cmd); + handle_satn_stop(s); + break; + case CMD_ENSEL: + trace_esp_mem_writeb_cmd_ensel(cmd); + s->rregs[ESP_RINTR] = 0; + break; + case CMD_DISSEL: + trace_esp_mem_writeb_cmd_dissel(cmd); + s->rregs[ESP_RINTR] = 0; + esp_raise_irq(s); + break; + default: + trace_esp_error_unhandled_command(cmd); + break; + } +} + uint64_t esp_reg_read(ESPState *s, uint32_t saddr) { uint32_t val; switch (saddr) { case ESP_FIFO: - if (s->dma_memory_read && s->dma_memory_write && - (s->rregs[ESP_RSTAT] & STAT_PIO_MASK) == 0) { - /* Data out. */ - qemu_log_mask(LOG_UNIMP, "esp: PIO data read not implemented\n"); - s->rregs[ESP_FIFO] = 0; - } else { - if ((s->rregs[ESP_RSTAT] & 0x7) == STAT_DI) { - if (s->ti_size) { - esp_do_nodma(s); - } else { - /* - * The last byte of a non-DMA transfer has been read out - * of the FIFO so switch to status phase - */ - s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST; - } - } - s->rregs[ESP_FIFO] = esp_fifo_pop(&s->fifo); - } + s->rregs[ESP_FIFO] = esp_fifo_pop(s); val = s->rregs[ESP_FIFO]; break; case ESP_RINTR: @@ -980,7 +1249,8 @@ uint64_t esp_reg_read(ESPState *s, uint32_t saddr) */ val = s->rregs[ESP_RINTR]; s->rregs[ESP_RINTR] = 0; - s->rregs[ESP_RSTAT] &= ~STAT_TC; + esp_lower_irq(s); + s->rregs[ESP_RSTAT] &= STAT_TC | 7; /* * According to the datasheet ESP_RSEQ should be cleared, but as the * emulation currently defers information transfers to the next TI @@ -990,7 +1260,6 @@ uint64_t esp_reg_read(ESPState *s, uint32_t saddr) * * s->rregs[ESP_RSEQ] = SEQ_0; */ - esp_lower_irq(s); break; case ESP_TCHI: /* Return the unique id if the value has never been written */ @@ -1025,107 +1294,14 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) s->rregs[ESP_RSTAT] &= ~STAT_TC; break; case ESP_FIFO: - if (s->do_cmd) { - esp_fifo_push(&s->cmdfifo, val); - - /* - * If any unexpected message out/command phase data is - * transferred using non-DMA, raise the interrupt - */ - if (s->rregs[ESP_CMD] == CMD_TI) { - s->rregs[ESP_RINTR] |= INTR_BS; - esp_raise_irq(s); - } - } else { - esp_fifo_push(&s->fifo, val); + if (!fifo8_is_full(&s->fifo)) { + esp_fifo_push(s, val); } + esp_do_nodma(s); break; case ESP_CMD: s->rregs[saddr] = val; - if (val & CMD_DMA) { - s->dma = 1; - /* Reload DMA counter. */ - if (esp_get_stc(s) == 0) { - esp_set_tc(s, 0x10000); - } else { - esp_set_tc(s, esp_get_stc(s)); - } - } else { - s->dma = 0; - } - switch (val & CMD_CMD) { - case CMD_NOP: - trace_esp_mem_writeb_cmd_nop(val); - break; - case CMD_FLUSH: - trace_esp_mem_writeb_cmd_flush(val); - fifo8_reset(&s->fifo); - break; - case CMD_RESET: - trace_esp_mem_writeb_cmd_reset(val); - esp_soft_reset(s); - break; - case CMD_BUSRESET: - trace_esp_mem_writeb_cmd_bus_reset(val); - if (!(s->wregs[ESP_CFG1] & CFG1_RESREPT)) { - s->rregs[ESP_RINTR] |= INTR_RST; - esp_raise_irq(s); - } - break; - case CMD_TI: - trace_esp_mem_writeb_cmd_ti(val); - handle_ti(s); - break; - case CMD_ICCS: - trace_esp_mem_writeb_cmd_iccs(val); - write_response(s); - s->rregs[ESP_RINTR] |= INTR_FC; - s->rregs[ESP_RSTAT] |= STAT_MI; - break; - case CMD_MSGACC: - trace_esp_mem_writeb_cmd_msgacc(val); - s->rregs[ESP_RINTR] |= INTR_DC; - s->rregs[ESP_RSEQ] = 0; - s->rregs[ESP_RFLAGS] = 0; - esp_raise_irq(s); - break; - case CMD_PAD: - trace_esp_mem_writeb_cmd_pad(val); - s->rregs[ESP_RSTAT] = STAT_TC; - s->rregs[ESP_RINTR] |= INTR_FC; - s->rregs[ESP_RSEQ] = 0; - break; - case CMD_SATN: - trace_esp_mem_writeb_cmd_satn(val); - break; - case CMD_RSTATN: - trace_esp_mem_writeb_cmd_rstatn(val); - break; - case CMD_SEL: - trace_esp_mem_writeb_cmd_sel(val); - handle_s_without_atn(s); - break; - case CMD_SELATN: - trace_esp_mem_writeb_cmd_selatn(val); - handle_satn(s); - break; - case CMD_SELATNS: - trace_esp_mem_writeb_cmd_selatns(val); - handle_satn_stop(s); - break; - case CMD_ENSEL: - trace_esp_mem_writeb_cmd_ensel(val); - s->rregs[ESP_RINTR] = 0; - break; - case CMD_DISSEL: - trace_esp_mem_writeb_cmd_dissel(val); - s->rregs[ESP_RINTR] = 0; - esp_raise_irq(s); - break; - default: - trace_esp_error_unhandled_command(val); - break; - } + esp_run_cmd(s); break; case ESP_WBUSID ... ESP_WSYNO: break; @@ -1174,6 +1350,14 @@ static bool esp_is_version_6(void *opaque, int version_id) return version_id >= 6; } +static bool esp_is_between_version_5_and_6(void *opaque, int version_id) +{ + ESPState *s = ESP(opaque); + + version_id = MIN(version_id, s->mig_version_id); + return version_id >= 5 && version_id <= 6; +} + int esp_pre_save(void *opaque) { ESPState *s = ESP(object_resolve_path_component( @@ -1209,39 +1393,12 @@ static int esp_post_load(void *opaque, int version_id) return 0; } -/* - * PDMA (or pseudo-DMA) is only used on the Macintosh and requires the - * guest CPU to perform the transfers between the SCSI bus and memory - * itself. This is indicated by the dma_memory_read and dma_memory_write - * functions being NULL (in contrast to the ESP PCI device) whilst - * dma_enabled is still set. - */ - -static bool esp_pdma_needed(void *opaque) -{ - ESPState *s = ESP(opaque); - - return s->dma_memory_read == NULL && s->dma_memory_write == NULL && - s->dma_enabled; -} - -static const VMStateDescription vmstate_esp_pdma = { - .name = "esp/pdma", - .version_id = 0, - .minimum_version_id = 0, - .needed = esp_pdma_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(pdma_cb, ESPState), - VMSTATE_END_OF_LIST() - } -}; - const VMStateDescription vmstate_esp = { .name = "esp", - .version_id = 6, + .version_id = 7, .minimum_version_id = 3, .post_load = esp_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BUFFER(rregs, ESPState), VMSTATE_BUFFER(wregs, ESPState), VMSTATE_INT32(ti_size, ESPState), @@ -1262,18 +1419,16 @@ const VMStateDescription vmstate_esp = { VMSTATE_UINT32_TEST(mig_cmdlen, ESPState, esp_is_before_version_5), VMSTATE_UINT32(do_cmd, ESPState), VMSTATE_UINT32_TEST(mig_dma_left, ESPState, esp_is_before_version_5), - VMSTATE_BOOL_TEST(data_in_ready, ESPState, esp_is_version_5), + VMSTATE_BOOL_TEST(data_ready, ESPState, esp_is_version_5), VMSTATE_UINT8_TEST(cmdfifo_cdb_offset, ESPState, esp_is_version_5), VMSTATE_FIFO8_TEST(fifo, ESPState, esp_is_version_5), VMSTATE_FIFO8_TEST(cmdfifo, ESPState, esp_is_version_5), - VMSTATE_UINT8_TEST(ti_cmd, ESPState, esp_is_version_5), + VMSTATE_UINT8_TEST(mig_ti_cmd, ESPState, + esp_is_between_version_5_and_6), VMSTATE_UINT8_TEST(lun, ESPState, esp_is_version_6), + VMSTATE_BOOL(drq_state, ESPState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { - &vmstate_esp_pdma, - NULL - } }; static void sysbus_esp_mem_write(void *opaque, hwaddr addr, @@ -1322,7 +1477,7 @@ static void sysbus_esp_pdma_write(void *opaque, hwaddr addr, esp_pdma_write(s, val); break; } - esp_pdma_cb(s); + esp_do_dma(s); } static uint64_t sysbus_esp_pdma_read(void *opaque, hwaddr addr, @@ -1343,9 +1498,7 @@ static uint64_t sysbus_esp_pdma_read(void *opaque, hwaddr addr, val = (val << 8) | esp_pdma_read(s); break; } - if (fifo8_num_used(&s->fifo) < 2) { - esp_pdma_cb(s); - } + esp_do_dma(s); return val; } @@ -1389,7 +1542,7 @@ static void sysbus_esp_gpio_demux(void *opaque, int irq, int level) parent_esp_reset(s, irq, level); break; case 1: - esp_dma_enable(opaque, irq, level); + esp_dma_enable(s, irq, level); break; } } @@ -1405,7 +1558,7 @@ static void sysbus_esp_realize(DeviceState *dev, Error **errp) } sysbus_init_irq(sbd, &s->irq); - sysbus_init_irq(sbd, &s->irq_data); + sysbus_init_irq(sbd, &s->drq_irq); assert(sysbus->it_shift != -1); s->chip_id = TCHI_FAS100A; @@ -1441,7 +1594,7 @@ static const VMStateDescription vmstate_sysbus_esp_scsi = { .version_id = 2, .minimum_version_id = 1, .pre_save = esp_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_V(esp.mig_version_id, SysBusESPState, 2), VMSTATE_STRUCT(esp, SysBusESPState, 0, vmstate_esp, ESPState), VMSTATE_END_OF_LIST() @@ -1458,14 +1611,6 @@ static void sysbus_esp_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } -static const TypeInfo sysbus_esp_info = { - .name = TYPE_SYSBUS_ESP, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_init = sysbus_esp_init, - .instance_size = sizeof(SysBusESPState), - .class_init = sysbus_esp_class_init, -}; - static void esp_finalize(Object *obj) { ESPState *s = ESP(obj); @@ -1491,19 +1636,22 @@ static void esp_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } -static const TypeInfo esp_info = { - .name = TYPE_ESP, - .parent = TYPE_DEVICE, - .instance_init = esp_init, - .instance_finalize = esp_finalize, - .instance_size = sizeof(ESPState), - .class_init = esp_class_init, +static const TypeInfo esp_info_types[] = { + { + .name = TYPE_SYSBUS_ESP, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = sysbus_esp_init, + .instance_size = sizeof(SysBusESPState), + .class_init = sysbus_esp_class_init, + }, + { + .name = TYPE_ESP, + .parent = TYPE_DEVICE, + .instance_init = esp_init, + .instance_finalize = esp_finalize, + .instance_size = sizeof(ESPState), + .class_init = esp_class_init, + }, }; -static void esp_register_types(void) -{ - type_register_static(&sysbus_esp_info); - type_register_static(&esp_info); -} - -type_init(esp_register_types) +DEFINE_TYPES(esp_info_types) diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index c8773f73f7..eb9828dd5e 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/scsi/scsi.h" #include "migration/vmstate.h" #include "sysemu/dma.h" @@ -188,7 +188,7 @@ static const char *names[] = { #define LSI_TAG_VALID (1 << 16) /* Maximum instructions to process. */ -#define LSI_MAX_INSN 10000 +#define LSI_MAX_INSN 100 typedef struct lsi_request { SCSIRequest *req; @@ -205,6 +205,7 @@ enum { LSI_WAIT_RESELECT, /* Wait Reselect instruction has been issued */ LSI_DMA_SCRIPTS, /* processing DMA from lsi_execute_script */ LSI_DMA_IN_PROGRESS, /* DMA operation is in progress */ + LSI_WAIT_SCRIPTS, /* SCRIPTS stopped because of instruction count limit */ }; enum { @@ -224,8 +225,9 @@ struct LSIState { MemoryRegion ram_io; MemoryRegion io_io; AddressSpace pci_io_as; + QEMUTimer *scripts_timer; - int carry; /* ??? Should this be an a visible register somewhere? */ + int carry; /* ??? Should this be in a visible register somewhere? */ int status; int msg_action; int msg_len; @@ -415,6 +417,7 @@ static void lsi_soft_reset(LSIState *s) s->sbr = 0; assert(QTAILQ_EMPTY(&s->queue)); assert(!s->current); + timer_del(s->scripts_timer); } static int lsi_dma_40bit(LSIState *s) @@ -570,8 +573,9 @@ static inline void lsi_set_phase(LSIState *s, int phase) s->sstat1 = (s->sstat1 & ~PHASE_MASK) | phase; } -static void lsi_bad_phase(LSIState *s, int out, int new_phase) +static int lsi_bad_phase(LSIState *s, int out, int new_phase) { + int ret = 0; /* Trigger a phase mismatch. */ if (s->ccntl0 & LSI_CCNTL0_ENPMJ) { if ((s->ccntl0 & LSI_CCNTL0_PMJCTL)) { @@ -584,8 +588,10 @@ static void lsi_bad_phase(LSIState *s, int out, int new_phase) trace_lsi_bad_phase_interrupt(); lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0); lsi_stop_script(s); + ret = 1; } lsi_set_phase(s, new_phase); + return ret; } @@ -789,7 +795,7 @@ static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len) static void lsi_command_complete(SCSIRequest *req, size_t resid) { LSIState *s = LSI53C895A(req->bus->qbus.parent); - int out; + int out, stop = 0; out = (s->sstat1 & PHASE_MASK) == PHASE_DO; trace_lsi_command_complete(req->status); @@ -797,7 +803,10 @@ static void lsi_command_complete(SCSIRequest *req, size_t resid) s->command_complete = 2; if (s->waiting && s->dbc != 0) { /* Raise phase mismatch for short transfers. */ - lsi_bad_phase(s, out, PHASE_ST); + stop = lsi_bad_phase(s, out, PHASE_ST); + if (stop) { + s->waiting = 0; + } } else { lsi_set_phase(s, PHASE_ST); } @@ -807,7 +816,9 @@ static void lsi_command_complete(SCSIRequest *req, size_t resid) lsi_request_free(s, s->current); scsi_req_unref(req); } - lsi_resume_script(s); + if (!stop) { + lsi_resume_script(s); + } } /* Callback to indicate that the SCSI layer has completed a transfer. */ @@ -864,7 +875,7 @@ static void lsi_do_command(LSIState *s) s->current = g_new0(lsi_request, 1); s->current->tag = s->select_tag; s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun, buf, - s->current); + s->dbc, s->current); n = scsi_req_enqueue(s->current->req); if (n) { @@ -916,13 +927,18 @@ static void lsi_do_msgin(LSIState *s) assert(len > 0 && len <= LSI_MAX_MSGIN_LEN); if (len > s->dbc) len = s->dbc; - pci_dma_write(PCI_DEVICE(s), s->dnad, s->msg, len); - /* Linux drivers rely on the last byte being in the SIDL. */ - s->sidl = s->msg[len - 1]; - s->msg_len -= len; - if (s->msg_len) { - memmove(s->msg, s->msg + len, s->msg_len); - } else { + + if (len) { + pci_dma_write(PCI_DEVICE(s), s->dnad, s->msg, len); + /* Linux drivers rely on the last byte being in the SIDL. */ + s->sidl = s->msg[len - 1]; + s->msg_len -= len; + if (s->msg_len) { + memmove(s->msg, s->msg + len, s->msg_len); + } + } + + if (!s->msg_len) { /* ??? Check if ATN (not yet implemented) is asserted and maybe switch to PHASE_MO. */ switch (s->msg_action) { @@ -1028,8 +1044,9 @@ static void lsi_do_msgout(LSIState *s) case 0x0d: /* The ABORT TAG message clears the current I/O process only. */ trace_lsi_do_msgout_abort(current_tag); - if (current_req) { + if (current_req && current_req->req) { scsi_req_cancel(current_req->req); + current_req = NULL; } lsi_disconnect(s); break; @@ -1055,6 +1072,7 @@ static void lsi_do_msgout(LSIState *s) /* clear the current I/O process */ if (s->current) { scsi_req_cancel(s->current->req); + current_req = NULL; } /* As the current implemented devices scsi_disk and scsi_generic @@ -1125,6 +1143,12 @@ static void lsi_wait_reselect(LSIState *s) } } +static void lsi_scripts_timer_start(LSIState *s) +{ + trace_lsi_scripts_timer_start(); + timer_mod(s->scripts_timer, qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + 500); +} + static void lsi_execute_script(LSIState *s) { PCIDevice *pci_dev = PCI_DEVICE(s); @@ -1132,22 +1156,32 @@ static void lsi_execute_script(LSIState *s) uint32_t addr, addr_high; int opcode; int insn_processed = 0; + static int reentrancy_level; + + if (s->waiting == LSI_WAIT_SCRIPTS) { + timer_del(s->scripts_timer); + s->waiting = LSI_NOWAIT; + } + + reentrancy_level++; s->istat1 |= LSI_ISTAT1_SRUN; again: - if (++insn_processed > LSI_MAX_INSN) { - /* Some windows drivers make the device spin waiting for a memory - location to change. If we have been executed a lot of code then - assume this is the case and force an unexpected device disconnect. - This is apparently sufficient to beat the drivers into submission. - */ - if (!(s->sien0 & LSI_SIST0_UDC)) { - qemu_log_mask(LOG_GUEST_ERROR, - "lsi_scsi: inf. loop with UDC masked"); - } - lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0); - lsi_disconnect(s); - trace_lsi_execute_script_stop(); + /* + * Some windows drivers make the device spin waiting for a memory location + * to change. If we have executed more than LSI_MAX_INSN instructions then + * assume this is the case and start a timer. Until the timer fires, the + * host CPU has a chance to run and change the memory location. + * + * Another issue (CVE-2023-0330) can occur if the script is programmed to + * trigger itself again and again. Avoid this problem by stopping after + * being called multiple times in a reentrant way (8 is an arbitrary value + * which should be enough for all valid use cases). + */ + if (++insn_processed > LSI_MAX_INSN || reentrancy_level > 8) { + s->waiting = LSI_WAIT_SCRIPTS; + lsi_scripts_timer_start(s); + reentrancy_level--; return; } insn = read_dword(s, s->dsp); @@ -1310,7 +1344,7 @@ again: } trace_lsi_execute_script_io_selected(id, insn & (1 << 3) ? " ATN" : ""); - /* ??? Linux drivers compain when this is set. Maybe + /* ??? Linux drivers complain when this is set. Maybe it only applies in low-level mode (unimplemented). lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */ s->select_tag = id << 8; @@ -1594,6 +1628,8 @@ again: } } trace_lsi_execute_script_stop(); + + reentrancy_level--; } static uint8_t lsi_reg_readb(LSIState *s, int offset) @@ -1866,7 +1902,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) } if (val & LSI_SCNTL1_RST) { if (!(s->sstat0 & LSI_SSTAT0_RST)) { - qbus_reset_all(BUS(&s->bus)); + bus_cold_reset(BUS(&s->bus)); s->sstat0 |= LSI_SSTAT0_RST; lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0); } @@ -1924,7 +1960,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) lsi_execute_script(s); } if (val & LSI_ISTAT0_SRST) { - qdev_reset_all(DEVICE(s)); + device_cold_reset(DEVICE(s)); } break; case 0x16: /* MBOX0 */ @@ -2183,6 +2219,9 @@ static int lsi_post_load(void *opaque, int version_id) return -EINVAL; } + if (s->waiting == LSI_WAIT_SCRIPTS) { + lsi_scripts_timer_start(s); + } return 0; } @@ -2192,7 +2231,7 @@ static const VMStateDescription vmstate_lsi_scsi = { .minimum_version_id = 0, .pre_save = lsi_pre_save, .post_load = lsi_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, LSIState), VMSTATE_INT32(carry, LSIState), @@ -2280,6 +2319,15 @@ static const struct SCSIBusInfo lsi_scsi_info = { .cancel = lsi_request_cancelled }; +static void scripts_timer_cb(void *opaque) +{ + LSIState *s = opaque; + + trace_lsi_scripts_timer_triggered(); + s->waiting = LSI_NOWAIT; + lsi_execute_script(s); +} + static void lsi_scsi_realize(PCIDevice *dev, Error **errp) { LSIState *s = LSI53C895A(dev); @@ -2299,6 +2347,14 @@ static void lsi_scsi_realize(PCIDevice *dev, Error **errp) "lsi-ram", 0x2000); memory_region_init_io(&s->io_io, OBJECT(s), &lsi_io_ops, s, "lsi-io", 256); + s->scripts_timer = timer_new_us(QEMU_CLOCK_VIRTUAL, scripts_timer_cb, s); + + /* + * Since we use the address-space API to interact with ram_io, disable the + * re-entrancy guard. + */ + s->ram_io.disable_reentrancy_guard = true; + s->mmio_io.disable_reentrancy_guard = true; address_space_init(&s->pci_io_as, pci_address_space_io(dev), "lsi-pci-io"); qdev_init_gpio_out(d, &s->ext_irq, 1); @@ -2316,6 +2372,7 @@ static void lsi_scsi_exit(PCIDevice *dev) LSIState *s = LSI53C895A(dev); address_space_destroy(&s->pci_io_as); + timer_del(s->scripts_timer); } static void lsi_class_init(ObjectClass *klass, void *data) diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index d5dfb412ba..2d0c607177 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -42,6 +42,7 @@ #define MEGASAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */ #define MEGASAS_DEFAULT_FRAMES 1000 /* Windows requires this */ #define MEGASAS_GEN2_DEFAULT_FRAMES 1008 /* Windows requires this */ +#define MEGASAS_MIN_SGE 64 #define MEGASAS_MAX_SGE 128 /* Firmware limit */ #define MEGASAS_DEFAULT_SGE 80 #define MEGASAS_MAX_SECTORS 0xFFFF /* No real limit */ @@ -1062,7 +1063,7 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, info->inquiry_data[0] = 0x7f; /* Force PQual 0x3, PType 0x1f */ info->vpd_page83[0] = 0x7f; megasas_setup_inquiry(cmdbuf, 0, sizeof(info->inquiry_data)); - cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); + cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, sizeof(cmdbuf), cmd); if (!cmd->req) { trace_megasas_dcmd_req_alloc_failed(cmd->index, "PD get info std inquiry"); @@ -1080,7 +1081,7 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, return MFI_STAT_INVALID_STATUS; } else if (info->inquiry_data[0] != 0x7f && info->vpd_page83[0] == 0x7f) { megasas_setup_inquiry(cmdbuf, 0x83, sizeof(info->vpd_page83)); - cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); + cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, sizeof(cmdbuf), cmd); if (!cmd->req) { trace_megasas_dcmd_req_alloc_failed(cmd->index, "PD get info vpd inquiry"); @@ -1268,7 +1269,7 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, cmd->iov_buf = g_malloc0(dcmd_size); info = cmd->iov_buf; megasas_setup_inquiry(cdb, 0x83, sizeof(info->vpd_page83)); - cmd->req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd); + cmd->req = scsi_req_new(sdev, cmd->index, lun, cdb, sizeof(cdb), cmd); if (!cmd->req) { trace_megasas_dcmd_req_alloc_failed(cmd->index, "LD get info vpd inquiry"); @@ -1484,7 +1485,7 @@ static int megasas_cluster_reset_ld(MegasasState *s, MegasasCmd *cmd) MegasasCmd *tmp_cmd = &s->frames[i]; if (tmp_cmd->req && tmp_cmd->req->dev->id == target_id) { SCSIDevice *d = tmp_cmd->req->dev; - qdev_reset_all(&d->qdev); + device_cold_reset(&d->qdev); } } return MFI_STAT_OK; @@ -1748,7 +1749,7 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, return MFI_STAT_SCSI_DONE_WITH_ERROR; } - cmd->req = scsi_req_new(sdev, cmd->index, lun_id, cdb, cmd); + cmd->req = scsi_req_new(sdev, cmd->index, lun_id, cdb, cdb_len, cmd); if (!cmd->req) { trace_megasas_scsi_req_alloc_failed( mfi_frame_desc(frame_cmd), target_id, lun_id); @@ -1823,7 +1824,7 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd, int frame_cmd) megasas_encode_lba(cdb, lba_start, lba_count, is_write); cmd->req = scsi_req_new(sdev, cmd->index, - lun_id, cdb, cmd); + lun_id, cdb, cdb_len, cmd); if (!cmd->req) { trace_megasas_scsi_req_alloc_failed( mfi_frame_desc(frame_cmd), target_id, lun_id); @@ -2298,7 +2299,7 @@ static const VMStateDescription vmstate_megasas_gen1 = { .name = "megasas", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, MegasasState), VMSTATE_MSIX(parent_obj, MegasasState), @@ -2316,7 +2317,7 @@ static const VMStateDescription vmstate_megasas_gen2 = { .name = "megasas-gen2", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, MegasasState), VMSTATE_MSIX(parent_obj, MegasasState), @@ -2356,6 +2357,7 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp) MegasasState *s = MEGASAS(dev); MegasasBaseClass *b = MEGASAS_GET_CLASS(s); uint8_t *pci_conf; + uint32_t sge; int i, bar_type; Error *err = NULL; int ret; @@ -2424,13 +2426,15 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp) if (!s->hba_serial) { s->hba_serial = g_strdup(MEGASAS_HBA_SERIAL); } - if (s->fw_sge >= MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE) { - s->fw_sge = MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE; - } else if (s->fw_sge >= 128 - MFI_PASS_FRAME_SIZE) { - s->fw_sge = 128 - MFI_PASS_FRAME_SIZE; - } else { - s->fw_sge = 64 - MFI_PASS_FRAME_SIZE; + + sge = s->fw_sge + MFI_PASS_FRAME_SIZE; + if (sge < MEGASAS_MIN_SGE) { + sge = MEGASAS_MIN_SGE; + } else if (sge >= MEGASAS_MAX_SGE) { + sge = MEGASAS_MAX_SGE; } + s->fw_sge = sge - MFI_PASS_FRAME_SIZE; + if (s->fw_cmds > MEGASAS_MAX_FRAMES) { s->fw_cmds = MEGASAS_MAX_FRAMES; } diff --git a/hw/scsi/meson.build b/hw/scsi/meson.build index 923a34f344..bb7d289aa0 100644 --- a/hw/scsi/meson.build +++ b/hw/scsi/meson.build @@ -1,4 +1,8 @@ scsi_ss = ss.source_set() +specific_scsi_ss = ss.source_set() +virtio_scsi_ss = ss.source_set() +specific_virtio_scsi_ss = ss.source_set() + scsi_ss.add(files( 'emulation.c', 'scsi-bus.c', @@ -11,16 +15,18 @@ scsi_ss.add(when: 'CONFIG_LSI_SCSI_PCI', if_true: files('lsi53c895a.c')) scsi_ss.add(when: 'CONFIG_MEGASAS_SCSI_PCI', if_true: files('megasas.c')) scsi_ss.add(when: 'CONFIG_MPTSAS_SCSI_PCI', if_true: files('mptsas.c', 'mptconfig.c', 'mptendian.c')) scsi_ss.add(when: 'CONFIG_VMW_PVSCSI_SCSI_PCI', if_true: files('vmw_pvscsi.c')) -softmmu_ss.add_all(when: 'CONFIG_SCSI', if_true: scsi_ss) -specific_scsi_ss = ss.source_set() +virtio_scsi_ss.add(files('virtio-scsi-dataplane.c')) +virtio_scsi_ss.add(when: 'CONFIG_VHOST_SCSI', if_true: files('vhost-scsi.c')) +virtio_scsi_ss.add(when: 'CONFIG_VHOST_USER_SCSI', if_true: files('vhost-user-scsi.c')) -virtio_scsi_ss = ss.source_set() -virtio_scsi_ss.add(files('virtio-scsi.c', 'virtio-scsi-dataplane.c')) -virtio_scsi_ss.add(when: 'CONFIG_VHOST_SCSI', if_true: files('vhost-scsi-common.c', 'vhost-scsi.c')) -virtio_scsi_ss.add(when: 'CONFIG_VHOST_USER_SCSI', if_true: files('vhost-scsi-common.c', 'vhost-user-scsi.c')) -specific_scsi_ss.add_all(when: 'CONFIG_VIRTIO_SCSI', if_true: virtio_scsi_ss) +specific_virtio_scsi_ss.add(files('virtio-scsi.c')) +specific_virtio_scsi_ss.add(when: 'CONFIG_VHOST_SCSI_COMMON', if_true: files('vhost-scsi-common.c')) + +specific_scsi_ss.add_all(when: 'CONFIG_VIRTIO_SCSI', if_true: specific_virtio_scsi_ss) +scsi_ss.add_all(when: 'CONFIG_VIRTIO_SCSI', if_true: virtio_scsi_ss) specific_scsi_ss.add(when: 'CONFIG_SPAPR_VSCSI', if_true: files('spapr_vscsi.c')) +system_ss.add_all(when: 'CONFIG_SCSI', if_true: scsi_ss) specific_ss.add_all(when: 'CONFIG_SCSI', if_true: specific_scsi_ss) diff --git a/hw/scsi/mfi.h b/hw/scsi/mfi.h index 0b4ee53dfc..cf7a2d775b 100644 --- a/hw/scsi/mfi.h +++ b/hw/scsi/mfi.h @@ -65,7 +65,7 @@ #define MFI_IQPH 0xc4 /* Inbound queue port (high bytes) */ #define MFI_DIAG 0xf8 /* Host diag */ #define MFI_SEQ 0xfc /* Sequencer offset */ -#define MFI_1078_EIM 0x80000004 /* 1078 enable intrrupt mask */ +#define MFI_1078_EIM 0x80000004 /* 1078 enable interrupt mask */ #define MFI_RMI 0x2 /* reply message interrupt */ #define MFI_1078_RM 0x80000000 /* reply 1078 message interrupt */ #define MFI_ODC 0x4 /* outbound doorbell change interrupt */ diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index 706cf0df3a..c5d3138c93 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -192,7 +192,7 @@ static dma_addr_t mptsas_ld_sg_base(MPTSASState *s, uint32_t flags_and_length, return addr; } -static int mptsas_build_sgl(MPTSASState *s, MPTSASRequest *req, hwaddr addr) +static int mptsas_build_sgl(MPTSASState *s, MPTSASRequest *req, hwaddr req_addr) { PCIDevice *pci = (PCIDevice *) s; hwaddr next_chain_addr; @@ -201,8 +201,8 @@ static int mptsas_build_sgl(MPTSASState *s, MPTSASRequest *req, hwaddr addr) uint32_t chain_offset; chain_offset = req->scsi_io.ChainOffset; - next_chain_addr = addr + chain_offset * sizeof(uint32_t); - sgaddr = addr + sizeof(MPIMsgSCSIIORequest); + next_chain_addr = req_addr + chain_offset * sizeof(uint32_t); + sgaddr = req_addr + sizeof(MPIMsgSCSIIORequest); pci_dma_sglist_init(&req->qsg, pci, 4); left = req->scsi_io.DataLength; @@ -324,7 +324,8 @@ static int mptsas_process_scsi_io_request(MPTSASState *s, } req->sreq = scsi_req_new(sdev, scsi_io->MsgContext, - scsi_io->LUN[1], scsi_io->CDB, req); + scsi_io->LUN[1], scsi_io->CDB, + scsi_io->CDBLength, req); if (req->sreq->cmd.xfer > scsi_io->DataLength) { goto overrun; @@ -521,7 +522,7 @@ reply_maybe_async: reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN; goto out; } - qdev_reset_all(&sdev->qdev); + device_cold_reset(&sdev->qdev); break; case MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET: @@ -537,13 +538,13 @@ reply_maybe_async: QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { sdev = SCSI_DEVICE(kid->child); if (sdev->channel == 0 && sdev->id == req->TargetID) { - qdev_reset_all(kid->child); + device_cold_reset(kid->child); } } break; case MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS: - qbus_reset_all(BUS(&s->bus)); + bus_cold_reset(BUS(&s->bus)); break; default: @@ -806,7 +807,7 @@ static void mptsas_soft_reset(MPTSASState *s) s->intr_mask = MPI_HIM_DIM | MPI_HIM_RIM; mptsas_update_interrupt(s); - qbus_reset_all(BUS(&s->bus)); + bus_cold_reset(BUS(&s->bus)); s->intr_status = 0; s->intr_mask = save_mask; @@ -1321,7 +1322,8 @@ static void mptsas_scsi_realize(PCIDevice *dev, Error **errp) } s->max_devices = MPTSAS_NUM_PORTS; - s->request_bh = qemu_bh_new(mptsas_fetch_requests, s); + s->request_bh = qemu_bh_new_guarded(mptsas_fetch_requests, s, + &DEVICE(dev)->mem_reentrancy_guard); scsi_bus_init(&s->bus, sizeof(s->bus), &dev->qdev, &mptsas_scsi_info); } @@ -1364,7 +1366,7 @@ static const VMStateDescription vmstate_mptsas = { .version_id = 0, .minimum_version_id = 0, .post_load = mptsas_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, MPTSASState), VMSTATE_BOOL(msi_in_use, MPTSASState), VMSTATE_UINT32(state, MPTSASState), diff --git a/hw/scsi/mptsas.h b/hw/scsi/mptsas.h index c046497db7..04e97ce3af 100644 --- a/hw/scsi/mptsas.h +++ b/hw/scsi/mptsas.h @@ -2,7 +2,7 @@ #define MPTSAS_H #include "mpi.h" -#include "qom/object.h" +#include "hw/pci/pci_device.h" #define MPTSAS_NUM_PORTS 8 #define MPTSAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */ diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index b2e2bc3c96..9e40b0c920 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -22,6 +22,7 @@ static char *scsibus_get_fw_dev_path(DeviceState *dev); static void scsi_req_dequeue(SCSIRequest *req); static uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len); static void scsi_target_free_buf(SCSIRequest *req); +static void scsi_clear_reported_luns_changed(SCSIRequest *req); static int next_scsi_bus; @@ -60,8 +61,7 @@ static SCSIDevice *do_scsi_device_find(SCSIBus *bus, * the user access the device. */ - if (retval && !include_unrealized && - !qatomic_load_acquire(&retval->qdev.realized)) { + if (retval && !include_unrealized && !qdev_is_realized(&retval->qdev)) { retval = NULL; } @@ -85,6 +85,92 @@ SCSIDevice *scsi_device_get(SCSIBus *bus, int channel, int id, int lun) return d; } +/* + * Invoke @fn() for each enqueued request in device @s. Must be called from the + * main loop thread while the guest is stopped. This is only suitable for + * vmstate ->put(), use scsi_device_for_each_req_async() for other cases. + */ +static void scsi_device_for_each_req_sync(SCSIDevice *s, + void (*fn)(SCSIRequest *, void *), + void *opaque) +{ + SCSIRequest *req; + SCSIRequest *next_req; + + assert(!runstate_is_running()); + assert(qemu_in_main_thread()); + + QTAILQ_FOREACH_SAFE(req, &s->requests, next, next_req) { + fn(req, opaque); + } +} + +typedef struct { + SCSIDevice *s; + void (*fn)(SCSIRequest *, void *); + void *fn_opaque; +} SCSIDeviceForEachReqAsyncData; + +static void scsi_device_for_each_req_async_bh(void *opaque) +{ + g_autofree SCSIDeviceForEachReqAsyncData *data = opaque; + SCSIDevice *s = data->s; + AioContext *ctx; + SCSIRequest *req; + SCSIRequest *next; + + /* + * The BB cannot have changed contexts between this BH being scheduled and + * now: BBs' AioContexts, when they have a node attached, can only be + * changed via bdrv_try_change_aio_context(), in a drained section. While + * we have the in-flight counter incremented, that drain must block. + */ + ctx = blk_get_aio_context(s->conf.blk); + assert(ctx == qemu_get_current_aio_context()); + + QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) { + data->fn(req, data->fn_opaque); + } + + /* Drop the reference taken by scsi_device_for_each_req_async() */ + object_unref(OBJECT(s)); + + /* Paired with blk_inc_in_flight() in scsi_device_for_each_req_async() */ + blk_dec_in_flight(s->conf.blk); +} + +/* + * Schedule @fn() to be invoked for each enqueued request in device @s. @fn() + * runs in the AioContext that is executing the request. + * Keeps the BlockBackend's in-flight counter incremented until everything is + * done, so draining it will settle all scheduled @fn() calls. + */ +static void scsi_device_for_each_req_async(SCSIDevice *s, + void (*fn)(SCSIRequest *, void *), + void *opaque) +{ + assert(qemu_in_main_thread()); + + SCSIDeviceForEachReqAsyncData *data = + g_new(SCSIDeviceForEachReqAsyncData, 1); + + data->s = s; + data->fn = fn; + data->fn_opaque = opaque; + + /* + * Hold a reference to the SCSIDevice until + * scsi_device_for_each_req_async_bh() finishes. + */ + object_ref(OBJECT(s)); + + /* Paired with blk_dec_in_flight() in scsi_device_for_each_req_async_bh() */ + blk_inc_in_flight(s->conf.blk); + aio_bh_schedule_oneshot(blk_get_aio_context(s->conf.blk), + scsi_device_for_each_req_async_bh, + data); +} + static void scsi_device_realize(SCSIDevice *s, Error **errp) { SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s); @@ -102,15 +188,15 @@ static void scsi_device_unrealize(SCSIDevice *s) } int scsi_bus_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf, - void *hba_private) + size_t buf_len, void *hba_private) { SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus); int rc; assert(cmd->len == 0); - rc = scsi_req_parse_cdb(dev, cmd, buf); + rc = scsi_req_parse_cdb(dev, cmd, buf, buf_len); if (bus->info->parse_cdb) { - rc = bus->info->parse_cdb(dev, cmd, buf, hba_private); + rc = bus->info->parse_cdb(dev, cmd, buf, buf_len, hba_private); } return rc; } @@ -144,20 +230,18 @@ void scsi_bus_init_named(SCSIBus *bus, size_t bus_size, DeviceState *host, qbus_set_bus_hotplug_handler(BUS(bus)); } -static void scsi_dma_restart_bh(void *opaque) +void scsi_req_retry(SCSIRequest *req) { - SCSIDevice *s = opaque; - SCSIRequest *req, *next; + req->retry = true; +} - qemu_bh_delete(s->bh); - s->bh = NULL; - - aio_context_acquire(blk_get_aio_context(s->conf.blk)); - QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) { - scsi_req_ref(req); - if (req->retry) { - req->retry = false; - switch (req->cmd.mode) { +/* Called in the AioContext that is executing the request */ +static void scsi_dma_restart_req(SCSIRequest *req, void *opaque) +{ + scsi_req_ref(req); + if (req->retry) { + req->retry = false; + switch (req->cmd.mode) { case SCSI_XFER_FROM_DEV: case SCSI_XFER_TO_DEV: scsi_req_continue(req); @@ -166,41 +250,27 @@ static void scsi_dma_restart_bh(void *opaque) scsi_req_dequeue(req); scsi_req_enqueue(req); break; - } } - scsi_req_unref(req); } - aio_context_release(blk_get_aio_context(s->conf.blk)); - /* Drop the reference that was acquired in scsi_dma_restart_cb */ - object_unref(OBJECT(s)); -} - -void scsi_req_retry(SCSIRequest *req) -{ - /* No need to save a reference, because scsi_dma_restart_bh just - * looks at the request list. */ - req->retry = true; + scsi_req_unref(req); } static void scsi_dma_restart_cb(void *opaque, bool running, RunState state) { SCSIDevice *s = opaque; + assert(qemu_in_main_thread()); + if (!running) { return; } - if (!s->bh) { - AioContext *ctx = blk_get_aio_context(s->conf.blk); - /* The reference is dropped in scsi_dma_restart_bh.*/ - object_ref(OBJECT(s)); - s->bh = aio_bh_new(ctx, scsi_dma_restart_bh, s); - qemu_bh_schedule(s->bh); - } + + scsi_device_for_each_req_async(s, scsi_dma_restart_req, NULL); } static bool scsi_bus_is_address_free(SCSIBus *bus, - int channel, int target, int lun, - SCSIDevice **p_dev) + int channel, int target, int lun, + SCSIDevice **p_dev) { SCSIDevice *d; @@ -306,15 +376,13 @@ static void scsi_qdev_unrealize(DeviceState *qdev) /* handle legacy '-drive if=scsi,...' cmd line args */ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, - int unit, bool removable, int bootindex, - bool share_rw, - BlockdevOnError rerror, - BlockdevOnError werror, + int unit, bool removable, BlockConf *conf, const char *serial, Error **errp) { const char *driver; char *name; DeviceState *dev; + SCSIDevice *s; DriveInfo *dinfo; if (blk_is_sg(blk)) { @@ -332,11 +400,10 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, object_property_add_child(OBJECT(bus), name, OBJECT(dev)); g_free(name); + s = SCSI_DEVICE(dev); + s->conf = *conf; + qdev_prop_set_uint32(dev, "scsi-id", unit); - if (bootindex >= 0) { - object_property_set_int(OBJECT(dev), "bootindex", bootindex, - &error_abort); - } if (object_property_find(OBJECT(dev), "removable")) { qdev_prop_set_bit(dev, "removable", removable); } @@ -347,19 +414,12 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, object_unparent(OBJECT(dev)); return NULL; } - if (!object_property_set_bool(OBJECT(dev), "share-rw", share_rw, errp)) { - object_unparent(OBJECT(dev)); - return NULL; - } - - qdev_prop_set_enum(dev, "rerror", rerror); - qdev_prop_set_enum(dev, "werror", werror); if (!qdev_realize_and_unref(dev, &bus->qbus, errp)) { object_unparent(OBJECT(dev)); return NULL; } - return SCSI_DEVICE(dev); + return s; } void scsi_bus_legacy_handle_cmdline(SCSIBus *bus) @@ -367,6 +427,12 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus) Location loc; DriveInfo *dinfo; int unit; + BlockConf conf = { + .bootindex = -1, + .share_rw = false, + .rerror = BLOCKDEV_ON_ERROR_AUTO, + .werror = BLOCKDEV_ON_ERROR_AUTO, + }; loc_push_none(&loc); for (unit = 0; unit <= bus->info->max_target; unit++) { @@ -376,10 +442,7 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus) } qemu_opts_loc_restore(dinfo->opts); scsi_bus_legacy_add_drive(bus, blk_by_legacy_dinfo(dinfo), - unit, false, -1, false, - BLOCKDEV_ON_ERROR_AUTO, - BLOCKDEV_ON_ERROR_AUTO, - NULL, &error_fatal); + unit, false, &conf, NULL, &error_fatal); } loc_pop(&loc); } @@ -412,19 +475,35 @@ static const struct SCSIReqOps reqops_invalid_opcode = { /* SCSIReqOps implementation for unit attention conditions. */ +static void scsi_fetch_unit_attention_sense(SCSIRequest *req) +{ + SCSISense *ua = NULL; + + if (req->dev->unit_attention.key == UNIT_ATTENTION) { + ua = &req->dev->unit_attention; + } else if (req->bus->unit_attention.key == UNIT_ATTENTION) { + ua = &req->bus->unit_attention; + } + + /* + * Fetch the unit attention sense immediately so that another + * scsi_req_new does not use reqops_unit_attention. + */ + if (ua) { + scsi_req_build_sense(req, *ua); + *ua = SENSE_CODE(NO_SENSE); + } +} + static int32_t scsi_unit_attention(SCSIRequest *req, uint8_t *buf) { - if (req->dev->unit_attention.key == UNIT_ATTENTION) { - scsi_req_build_sense(req, req->dev->unit_attention); - } else if (req->bus->unit_attention.key == UNIT_ATTENTION) { - scsi_req_build_sense(req, req->bus->unit_attention); - } scsi_req_complete(req, CHECK_CONDITION); return 0; } static const struct SCSIReqOps reqops_unit_attention = { .size = sizeof(SCSIRequest), + .init_req = scsi_fetch_unit_attention_sense, .send_command = scsi_unit_attention }; @@ -487,7 +566,8 @@ static bool scsi_target_emulate_report_luns(SCSITargetReq *r) DeviceState *qdev = kid->child; SCSIDevice *dev = SCSI_DEVICE(qdev); - if (dev->channel == channel && dev->id == id && dev->lun != 0) { + if (dev->channel == channel && dev->id == id && dev->lun != 0 && + qdev_is_realized(&dev->qdev)) { store_lun(tmp, dev->lun); g_byte_array_append(buf, tmp, 8); len += 8; @@ -501,6 +581,14 @@ static bool scsi_target_emulate_report_luns(SCSITargetReq *r) /* store the LUN list length */ stl_be_p(&r->buf[0], len - 8); + + /* + * If a REPORT LUNS command enters the enabled command state, [...] + * the device server shall clear any pending unit attention condition + * with an additional sense code of REPORTED LUNS DATA HAS CHANGED. + */ + scsi_clear_reported_luns_changed(&r->req); + return true; } @@ -698,12 +786,17 @@ SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d, object_ref(OBJECT(d)); object_ref(OBJECT(qbus->parent)); notifier_list_init(&req->cancel_notifiers); + + if (reqops->init_req) { + reqops->init_req(req); + } + trace_scsi_req_alloc(req->dev->id, req->lun, req->tag); return req; } SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, - uint8_t *buf, void *hba_private) + uint8_t *buf, size_t buf_len, void *hba_private) { SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus); const SCSIReqOps *ops; @@ -712,6 +805,11 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, SCSICommand cmd = { .len = 0 }; int ret; + if (buf_len == 0) { + trace_scsi_req_parse_bad(d->id, lun, tag, 0); + goto invalid_opcode; + } + if ((d->unit_attention.key == UNIT_ATTENTION || bus->unit_attention.key == UNIT_ATTENTION) && (buf[0] != INQUIRY && @@ -734,13 +832,14 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, } if (ops != NULL || !sc->parse_cdb) { - ret = scsi_req_parse_cdb(d, &cmd, buf); + ret = scsi_req_parse_cdb(d, &cmd, buf, buf_len); } else { - ret = sc->parse_cdb(d, &cmd, buf, hba_private); + ret = sc->parse_cdb(d, &cmd, buf, buf_len, hba_private); } if (ret != 0) { trace_scsi_req_parse_bad(d->id, lun, tag, buf[0]); +invalid_opcode: req = scsi_req_alloc(&reqops_invalid_opcode, d, tag, lun, hba_private); } else { assert(cmd.len != 0); @@ -788,43 +887,22 @@ uint8_t *scsi_req_get_buf(SCSIRequest *req) return req->ops->get_buf(req); } -static void scsi_clear_unit_attention(SCSIRequest *req) +static void scsi_clear_reported_luns_changed(SCSIRequest *req) { SCSISense *ua; - if (req->dev->unit_attention.key != UNIT_ATTENTION && - req->bus->unit_attention.key != UNIT_ATTENTION) { - return; - } - - /* - * If an INQUIRY command enters the enabled command state, - * the device server shall [not] clear any unit attention condition; - * See also MMC-6, paragraphs 6.5 and 6.6.2. - */ - if (req->cmd.buf[0] == INQUIRY || - req->cmd.buf[0] == GET_CONFIGURATION || - req->cmd.buf[0] == GET_EVENT_STATUS_NOTIFICATION) { - return; - } if (req->dev->unit_attention.key == UNIT_ATTENTION) { ua = &req->dev->unit_attention; - } else { + } else if (req->bus->unit_attention.key == UNIT_ATTENTION) { ua = &req->bus->unit_attention; - } - - /* - * If a REPORT LUNS command enters the enabled command state, [...] - * the device server shall clear any pending unit attention condition - * with an additional sense code of REPORTED LUNS DATA HAS CHANGED. - */ - if (req->cmd.buf[0] == REPORT_LUNS && - !(ua->asc == SENSE_CODE(REPORTED_LUNS_CHANGED).asc && - ua->ascq == SENSE_CODE(REPORTED_LUNS_CHANGED).ascq)) { + } else { return; } - *ua = SENSE_CODE(NO_SENSE); + if (ua->asc == SENSE_CODE(REPORTED_LUNS_CHANGED).asc && + ua->ascq == SENSE_CODE(REPORTED_LUNS_CHANGED).ascq) { + *ua = SENSE_CODE(NO_SENSE); + } } int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len) @@ -1308,14 +1386,15 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd) } } -int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf) +int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf, + size_t buf_len) { int rc; int len; cmd->lba = -1; len = scsi_cdb_length(buf); - if (len < 0) { + if (len < 0 || len > buf_len) { return -1; } @@ -1506,13 +1585,6 @@ void scsi_req_complete(SCSIRequest *req, int status) req->dev->sense_is_ua = false; } - /* - * Unit attention state is now stored in the device's sense buffer - * if the HBA didn't do autosense. Clear the pending unit attention - * flags. - */ - scsi_clear_unit_attention(req); - scsi_req_ref(req); scsi_req_dequeue(req); req->bus->info->complete(req, req->residual); @@ -1609,6 +1681,24 @@ static int scsi_ua_precedence(SCSISense sense) return (sense.asc << 8) | sense.ascq; } +void scsi_bus_set_ua(SCSIBus *bus, SCSISense sense) +{ + int prec1, prec2; + if (sense.key != UNIT_ATTENTION) { + return; + } + + /* + * Override a pre-existing unit attention condition, except for a more + * important reset condition. + */ + prec1 = scsi_ua_precedence(bus->unit_attention); + prec2 = scsi_ua_precedence(sense); + if (prec2 < prec1) { + bus->unit_attention = sense; + } +} + void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense) { int prec1, prec2; @@ -1629,20 +1719,68 @@ void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense) } } +static void scsi_device_purge_one_req(SCSIRequest *req, void *opaque) +{ + scsi_req_cancel_async(req, NULL); +} + +/** + * Cancel all requests, and block until they are deleted. + */ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense) { - SCSIRequest *req; + scsi_device_for_each_req_async(sdev, scsi_device_purge_one_req, NULL); - aio_context_acquire(blk_get_aio_context(sdev->conf.blk)); - while (!QTAILQ_EMPTY(&sdev->requests)) { - req = QTAILQ_FIRST(&sdev->requests); - scsi_req_cancel_async(req, NULL); - } + /* + * Await all the scsi_device_purge_one_req() calls scheduled by + * scsi_device_for_each_req_async(), and all I/O requests that were + * cancelled this way, but may still take a bit of time to settle. + */ blk_drain(sdev->conf.blk); - aio_context_release(blk_get_aio_context(sdev->conf.blk)); + scsi_device_set_ua(sdev, sense); } +void scsi_device_drained_begin(SCSIDevice *sdev) +{ + SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, sdev->qdev.parent_bus); + if (!bus) { + return; + } + + assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + assert(bus->drain_count < INT_MAX); + + /* + * Multiple BlockBackends can be on a SCSIBus and each may begin/end + * draining at any time. Keep a counter so HBAs only see begin/end once. + */ + if (bus->drain_count++ == 0) { + trace_scsi_bus_drained_begin(bus, sdev); + if (bus->info->drained_begin) { + bus->info->drained_begin(bus); + } + } +} + +void scsi_device_drained_end(SCSIDevice *sdev) +{ + SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, sdev->qdev.parent_bus); + if (!bus) { + return; + } + + assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + assert(bus->drain_count > 0); + + if (bus->drain_count-- == 1) { + trace_scsi_bus_drained_end(bus, sdev); + if (bus->info->drained_end) { + bus->info->drained_end(bus); + } + } +} + static char *scsibus_get_dev_path(DeviceState *dev) { SCSIDevice *d = SCSI_DEVICE(dev); @@ -1669,31 +1807,33 @@ static char *scsibus_get_fw_dev_path(DeviceState *dev) /* SCSI request list. For simplicity, pv points to the whole device */ +static void put_scsi_req(SCSIRequest *req, void *opaque) +{ + QEMUFile *f = opaque; + + assert(!req->io_canceled); + assert(req->status == -1 && req->host_status == -1); + assert(req->enqueued); + + qemu_put_sbyte(f, req->retry ? 1 : 2); + qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf)); + qemu_put_be32s(f, &req->tag); + qemu_put_be32s(f, &req->lun); + if (req->bus->info->save_request) { + req->bus->info->save_request(f, req); + } + if (req->ops->save_request) { + req->ops->save_request(f, req); + } +} + static int put_scsi_requests(QEMUFile *f, void *pv, size_t size, const VMStateField *field, JSONWriter *vmdesc) { SCSIDevice *s = pv; - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus); - SCSIRequest *req; - QTAILQ_FOREACH(req, &s->requests, next) { - assert(!req->io_canceled); - assert(req->status == -1 && req->host_status == -1); - assert(req->enqueued); - - qemu_put_sbyte(f, req->retry ? 1 : 2); - qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf)); - qemu_put_be32s(f, &req->tag); - qemu_put_be32s(f, &req->lun); - if (bus->info->save_request) { - bus->info->save_request(f, req); - } - if (req->ops->save_request) { - req->ops->save_request(f, req); - } - } + scsi_device_for_each_req_sync(s, put_scsi_req, f); qemu_put_sbyte(f, 0); - return 0; } @@ -1713,7 +1853,11 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size, qemu_get_buffer(f, buf, sizeof(buf)); qemu_get_be32s(f, &tag); qemu_get_be32s(f, &lun); - req = scsi_req_new(s, tag, lun, buf, NULL); + /* + * A too-short CDB would have been rejected by scsi_req_new, so just use + * SCSI_CMD_BUF_SIZE as the CDB length. + */ + req = scsi_req_new(s, tag, lun, buf, sizeof(buf), NULL); req->retry = (sbyte == 1); if (bus->info->load_request) { req->hba_private = bus->info->load_request(f, req); @@ -1754,7 +1898,7 @@ static const VMStateDescription vmstate_scsi_sense_state = { .version_id = 1, .minimum_version_id = 1, .needed = scsi_sense_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_SUB_ARRAY(sense, SCSIDevice, SCSI_SENSE_BUF_SIZE_OLD, SCSI_SENSE_BUF_SIZE - SCSI_SENSE_BUF_SIZE_OLD), @@ -1766,7 +1910,7 @@ const VMStateDescription vmstate_scsi_device = { .name = "SCSIDevice", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(unit_attention.key, SCSIDevice), VMSTATE_UINT8(unit_attention.asc, SCSIDevice), VMSTATE_UINT8(unit_attention.ascq, SCSIDevice), @@ -1784,7 +1928,7 @@ const VMStateDescription vmstate_scsi_device = { }, VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_scsi_sense_state, NULL } diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 072686ed58..4bd7af9d0c 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -94,6 +94,7 @@ struct SCSIDiskState { uint16_t port_index; uint64_t max_unmap_size; uint64_t max_io_size; + uint32_t quirks; QEMUBH *bh; char *version; char *serial; @@ -272,9 +273,13 @@ static void scsi_aio_complete(void *opaque, int ret) SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + /* The request must only run in the BlockBackend's AioContext */ + assert(blk_get_aio_context(s->qdev.conf.blk) == + qemu_get_current_aio_context()); + assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); + if (scsi_disk_req_check_error(r, ret, true)) { goto done; } @@ -283,7 +288,6 @@ static void scsi_aio_complete(void *opaque, int ret) scsi_req_complete(&r->req, GOOD); done: - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); scsi_req_unref(&r->req); } @@ -359,20 +363,23 @@ static void scsi_dma_complete(void *opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); if (ret < 0) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); } else { block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); } scsi_dma_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); } static void scsi_read_complete_noio(SCSIDiskReq *r, int ret) { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; + /* The request must only run in the BlockBackend's AioContext */ + assert(blk_get_aio_context(s->qdev.conf.blk) == + qemu_get_current_aio_context()); + assert(r->req.aiocb == NULL); if (scsi_disk_req_check_error(r, ret, false)) { goto done; @@ -395,7 +402,6 @@ static void scsi_read_complete(void *opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); if (ret < 0) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); } else { @@ -403,7 +409,6 @@ static void scsi_read_complete(void *opaque, int ret) trace_scsi_disk_read_complete(r->req.tag, r->qiov.size); } scsi_read_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); } /* Actually issue a read to the block device. */ @@ -448,14 +453,12 @@ static void scsi_do_read_cb(void *opaque, int ret) assert (r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); if (ret < 0) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); } else { block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); } scsi_do_read(opaque, ret); - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); } /* Read more data from scsi device into buffer. */ @@ -501,8 +504,13 @@ static void scsi_read_data(SCSIRequest *req) static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; + /* The request must only run in the BlockBackend's AioContext */ + assert(blk_get_aio_context(s->qdev.conf.blk) == + qemu_get_current_aio_context()); + assert (r->req.aiocb == NULL); if (scsi_disk_req_check_error(r, ret, false)) { goto done; @@ -532,14 +540,12 @@ static void scsi_write_complete(void * opaque, int ret) assert (r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); if (ret < 0) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); } else { block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); } scsi_write_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); } static void scsi_write_data(SCSIRequest *req) @@ -1078,12 +1084,14 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, int page_control) { static const int mode_sense_valid[0x3f] = { + [MODE_PAGE_VENDOR_SPECIFIC] = (1 << TYPE_DISK) | (1 << TYPE_ROM), [MODE_PAGE_HD_GEOMETRY] = (1 << TYPE_DISK), [MODE_PAGE_FLEXIBLE_DISK_GEOMETRY] = (1 << TYPE_DISK), [MODE_PAGE_CACHING] = (1 << TYPE_DISK) | (1 << TYPE_ROM), [MODE_PAGE_R_W_ERROR] = (1 << TYPE_DISK) | (1 << TYPE_ROM), [MODE_PAGE_AUDIO_CTL] = (1 << TYPE_ROM), [MODE_PAGE_CAPABILITIES] = (1 << TYPE_ROM), + [MODE_PAGE_APPLE_VENDOR] = (1 << TYPE_ROM), }; uint8_t *p = *p_outbuf + 2; @@ -1185,6 +1193,10 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, case MODE_PAGE_R_W_ERROR: length = 10; if (page_control == 1) { /* Changeable Values */ + if (s->qdev.type == TYPE_ROM) { + /* Automatic Write Reallocation Enabled */ + p[0] = 0x80; + } break; } p[0] = 0x80; /* Automatic Write Reallocation Enabled */ @@ -1228,6 +1240,36 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, p[19] = (16 * 176) & 0xff; break; + case MODE_PAGE_APPLE_VENDOR: + if (s->quirks & (1 << SCSI_DISK_QUIRK_MODE_PAGE_APPLE_VENDOR)) { + length = 0x1e; + if (page_control == 1) { /* Changeable Values */ + break; + } + + memset(p, 0, length); + strcpy((char *)p + 8, "APPLE COMPUTER, INC "); + break; + } else { + return -1; + } + + case MODE_PAGE_VENDOR_SPECIFIC: + if (s->qdev.type == TYPE_DISK && (s->quirks & + (1 << SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE))) { + length = 0x2; + if (page_control == 1) { /* Changeable Values */ + p[0] = 0xff; + p[1] = 0xff; + break; + } + p[0] = 0; + p[1] = 0; + break; + } else { + return -1; + } + default: return -1; } @@ -1263,10 +1305,27 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) dev_specific_param |= 0x80; /* Readonly. */ } } else { - /* MMC prescribes that CD/DVD drives have no block descriptors, - * and defines no device-specific parameter. */ - dev_specific_param = 0x00; - dbd = true; + if (s->quirks & (1 << SCSI_DISK_QUIRK_MODE_SENSE_ROM_USE_DBD)) { + /* Use DBD from the request... */ + dev_specific_param = 0x00; + + /* + * ... unless we receive a request for MODE_PAGE_APPLE_VENDOR + * which should never return a block descriptor even though DBD is + * not set, otherwise CDROM detection fails in MacOS + */ + if (s->quirks & (1 << SCSI_DISK_QUIRK_MODE_PAGE_APPLE_VENDOR) && + page == MODE_PAGE_APPLE_VENDOR) { + dbd = true; + } + } else { + /* + * MMC prescribes that CD/DVD drives have no block descriptors, + * and defines no device-specific parameter. + */ + dev_specific_param = 0x00; + dbd = true; + } } if (r->req.cmd.buf[0] == MODE_SENSE) { @@ -1502,7 +1561,10 @@ static int mode_select_pages(SCSIDiskReq *r, uint8_t *p, int len, bool change) goto invalid_param; } if (page_len > len) { - goto invalid_param_len; + if (!(s->quirks & SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED)) { + goto invalid_param_len; + } + trace_scsi_disk_mode_select_page_truncated(page, page_len, len); } if (!change) { @@ -1534,12 +1596,15 @@ static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf) int cmd = r->req.cmd.buf[0]; int len = r->req.cmd.xfer; int hdr_len = (cmd == MODE_SELECT ? 4 : 8); - int bd_len; + int bd_len, bs; int pass; - /* We only support PF=1, SP=0. */ if ((r->req.cmd.buf[1] & 0x11) != 0x10) { - goto invalid_field; + if (!(s->quirks & + (1 << SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE))) { + /* We only support PF=1, SP=0. */ + goto invalid_field; + } } if (len < hdr_len) { @@ -1556,6 +1621,23 @@ static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf) goto invalid_param; } + /* Allow changing the block size */ + if (bd_len) { + bs = p[5] << 16 | p[6] << 8 | p[7]; + + /* + * Since the existing code only checks/updates bits 8-15 of the block + * size, restrict ourselves to the same requirement for now to ensure + * that a block size set by a block descriptor and then read back by + * a subsequent SCSI command will be the same. Also disallow a block + * size of 256 since we cannot handle anything below BDRV_SECTOR_SIZE. + */ + if (bs && !(bs & ~0xfe00) && bs != s->qdev.blocksize) { + s->qdev.blocksize = bs; + trace_scsi_disk_mode_select_set_blocksize(s->qdev.blocksize); + } + } + len -= bd_len; p += bd_len; @@ -1664,7 +1746,6 @@ static void scsi_unmap_complete(void *opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); if (scsi_disk_req_check_error(r, ret, true)) { scsi_req_unref(&r->req); g_free(data); @@ -1672,7 +1753,6 @@ static void scsi_unmap_complete(void *opaque, int ret) block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); scsi_unmap_complete_noio(data, ret); } - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); } static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) @@ -1742,7 +1822,7 @@ static void scsi_write_same_complete(void *opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); + if (scsi_disk_req_check_error(r, ret, true)) { goto done; } @@ -1763,7 +1843,6 @@ static void scsi_write_same_complete(void *opaque, int ret) data->sector << BDRV_SECTOR_BITS, &data->qiov, 0, scsi_write_same_complete, data); - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); return; } @@ -1773,7 +1852,6 @@ done: scsi_req_unref(&r->req); qemu_vfree(data->iov.iov_base); g_free(data); - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); } static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf) @@ -1783,7 +1861,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf) uint32_t nb_sectors = scsi_data_cdb_xfer(r->req.cmd.buf); WriteSameCBData *data; uint8_t *buf; - int i; + int i, l; /* Fail if PBDATA=1 or LBDATA=1 or ANCHOR=1. */ if (nb_sectors == 0 || (req->cmd.buf[1] & 0x16)) { @@ -1825,8 +1903,9 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf) data->iov.iov_len); qemu_iovec_init_external(&data->qiov, &data->iov, 1); - for (i = 0; i < data->iov.iov_len; i += s->qdev.blocksize) { - memcpy(&buf[i], inbuf, s->qdev.blocksize); + for (i = 0; i < data->iov.iov_len; i += l) { + l = MIN(s->qdev.blocksize, data->iov.iov_len - i); + memcpy(&buf[i], inbuf, l); } scsi_req_ref(&r->req); @@ -1874,6 +1953,10 @@ static void scsi_disk_emulate_write_data(SCSIRequest *req) scsi_disk_emulate_write_same(r, r->iov.iov_base); break; + case FORMAT_UNIT: + scsi_req_complete(&r->req, GOOD); + break; + default: abort(); } @@ -2127,6 +2210,9 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) trace_scsi_disk_emulate_command_WRITE_SAME( req->cmd.buf[0] == WRITE_SAME_10 ? 10 : 16, r->req.cmd.xfer); break; + case FORMAT_UNIT: + trace_scsi_disk_emulate_command_FORMAT_UNIT(r->req.cmd.xfer); + break; default: trace_scsi_disk_emulate_command_UNKNOWN(buf[0], scsi_command_name(buf[0])); @@ -2256,6 +2342,7 @@ static void scsi_disk_reset(DeviceState *dev) scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET)); blk_get_geometry(s->qdev.conf.blk, &nb_sectors); + nb_sectors /= s->qdev.blocksize / BDRV_SECTOR_SIZE; if (nb_sectors) { nb_sectors--; @@ -2268,6 +2355,20 @@ static void scsi_disk_reset(DeviceState *dev) s->qdev.scsi_version = s->qdev.default_scsi_version; } +static void scsi_disk_drained_begin(void *opaque) +{ + SCSIDiskState *s = opaque; + + scsi_device_drained_begin(&s->qdev); +} + +static void scsi_disk_drained_end(void *opaque) +{ + SCSIDiskState *s = opaque; + + scsi_device_drained_end(&s->qdev); +} + static void scsi_disk_resize_cb(void *opaque) { SCSIDiskState *s = opaque; @@ -2322,16 +2423,19 @@ static bool scsi_cd_is_medium_locked(void *opaque) } static const BlockDevOps scsi_disk_removable_block_ops = { - .change_media_cb = scsi_cd_change_media_cb, + .change_media_cb = scsi_cd_change_media_cb, + .drained_begin = scsi_disk_drained_begin, + .drained_end = scsi_disk_drained_end, .eject_request_cb = scsi_cd_eject_request_cb, - .is_tray_open = scsi_cd_is_tray_open, .is_medium_locked = scsi_cd_is_medium_locked, - - .resize_cb = scsi_disk_resize_cb, + .is_tray_open = scsi_cd_is_tray_open, + .resize_cb = scsi_disk_resize_cb, }; static const BlockDevOps scsi_disk_block_ops = { - .resize_cb = scsi_disk_resize_cb, + .drained_begin = scsi_disk_drained_begin, + .drained_end = scsi_disk_drained_end, + .resize_cb = scsi_disk_resize_cb, }; static void scsi_disk_unit_attention_reported(SCSIDevice *dev) @@ -2419,7 +2523,6 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) } else { blk_set_dev_ops(s->qdev.conf.blk, &scsi_disk_block_ops, s); } - blk_set_guest_block_size(s->qdev.conf.blk, s->qdev.blocksize); blk_iostatus_enable(s->qdev.conf.blk); @@ -2437,15 +2540,13 @@ static void scsi_unrealize(SCSIDevice *dev) static void scsi_hd_realize(SCSIDevice *dev, Error **errp) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - AioContext *ctx = NULL; + /* can happen for devices without drive. The error message for missing * backend will be issued in scsi_realize */ if (s->qdev.conf.blk) { - ctx = blk_get_aio_context(s->qdev.conf.blk); - aio_context_acquire(ctx); if (!blkconf_blocksizes(&s->qdev.conf, errp)) { - goto out; + return; } } s->qdev.blocksize = s->qdev.conf.logical_block_size; @@ -2454,17 +2555,13 @@ static void scsi_hd_realize(SCSIDevice *dev, Error **errp) s->product = g_strdup("QEMU HARDDISK"); } scsi_realize(&s->qdev, errp); -out: - if (ctx) { - aio_context_release(ctx); - } } static void scsi_cd_realize(SCSIDevice *dev, Error **errp) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - AioContext *ctx; int ret; + uint32_t blocksize = 2048; if (!dev->conf.blk) { /* Anonymous BlockBackend for an empty drive. As we put it into @@ -2474,16 +2571,17 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp) assert(ret == 0); } - ctx = blk_get_aio_context(dev->conf.blk); - aio_context_acquire(ctx); - s->qdev.blocksize = 2048; + if (dev->conf.physical_block_size != 0) { + blocksize = dev->conf.physical_block_size; + } + + s->qdev.blocksize = blocksize; s->qdev.type = TYPE_ROM; s->features |= 1 << SCSI_DISK_F_REMOVABLE; if (!s->product) { s->product = g_strdup("QEMU CD-ROM"); } scsi_realize(&s->qdev, errp); - aio_context_release(ctx); } @@ -2533,6 +2631,7 @@ static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = { [VERIFY_10] = &scsi_disk_emulate_reqops, [VERIFY_12] = &scsi_disk_emulate_reqops, [VERIFY_16] = &scsi_disk_emulate_reqops, + [FORMAT_UNIT] = &scsi_disk_emulate_reqops, [READ_6] = &scsi_disk_dma_reqops, [READ_10] = &scsi_disk_dma_reqops, @@ -2613,7 +2712,6 @@ static int get_device_type(SCSIDiskState *s) static void scsi_block_realize(SCSIDevice *dev, Error **errp) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - AioContext *ctx; int sg_version; int rc; @@ -2628,9 +2726,6 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) "be removed in a future version"); } - ctx = blk_get_aio_context(s->qdev.conf.blk); - aio_context_acquire(ctx); - /* check we are using a driver managing SG_IO (version 3 and after) */ rc = blk_ioctl(s->qdev.conf.blk, SG_GET_VERSION_NUM, &sg_version); if (rc < 0) { @@ -2638,18 +2733,18 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) if (rc != -EPERM) { error_append_hint(errp, "Is this a SCSI device?\n"); } - goto out; + return; } if (sg_version < 30000) { error_setg(errp, "scsi generic interface too old"); - goto out; + return; } /* get device type from INQUIRY data */ rc = get_device_type(s); if (rc < 0) { error_setg(errp, "INQUIRY failed"); - goto out; + return; } /* Make a guess for the block size, we'll fix it when the guest sends. @@ -2669,9 +2764,6 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) scsi_realize(&s->qdev, errp); scsi_generic_read_device_inquiry(&s->qdev); - -out: - aio_context_release(ctx); } typedef struct SCSIBlockReq { @@ -2691,7 +2783,6 @@ static void scsi_block_sgio_complete(void *opaque, int ret) { SCSIBlockReq *req = (SCSIBlockReq *)opaque; SCSIDiskReq *r = &req->req; - SCSIDevice *s = r->req.dev; sg_io_hdr_t *io_hdr = &req->io_header; if (ret == 0) { @@ -2708,13 +2799,10 @@ static void scsi_block_sgio_complete(void *opaque, int ret) } if (ret > 0) { - aio_context_acquire(blk_get_aio_context(s->conf.blk)); if (scsi_handle_rw_error(r, ret, true)) { - aio_context_release(blk_get_aio_context(s->conf.blk)); scsi_req_unref(&r->req); return; } - aio_context_release(blk_get_aio_context(s->conf.blk)); /* Ignore error. */ ret = 0; @@ -2950,14 +3038,15 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag, } static int scsi_block_parse_cdb(SCSIDevice *d, SCSICommand *cmd, - uint8_t *buf, void *hba_private) + uint8_t *buf, size_t buf_len, + void *hba_private) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); if (scsi_block_is_passthrough(s, buf)) { - return scsi_bus_parse_cdb(&s->qdev, cmd, buf, hba_private); + return scsi_bus_parse_cdb(&s->qdev, cmd, buf, buf_len, hba_private); } else { - return scsi_req_parse_cdb(&s->qdev, cmd, buf); + return scsi_req_parse_cdb(&s->qdev, cmd, buf, buf_len); } } @@ -3037,6 +3126,9 @@ static Property scsi_hd_properties[] = { DEFINE_PROP_UINT16("rotation_rate", SCSIDiskState, rotation_rate, 0), DEFINE_PROP_INT32("scsi_version", SCSIDiskState, qdev.default_scsi_version, 5), + DEFINE_PROP_BIT("quirk_mode_page_vendor_specific_apple", SCSIDiskState, + quirks, SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE, + 0), DEFINE_BLOCK_CHS_PROPERTIES(SCSIDiskState, qdev.conf), DEFINE_PROP_END_OF_LIST(), }; @@ -3045,7 +3137,7 @@ static const VMStateDescription vmstate_scsi_disk_state = { .name = "scsi-disk", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_SCSI_DEVICE(qdev, SCSIDiskState), VMSTATE_BOOL(media_changed, SCSIDiskState), VMSTATE_BOOL(media_event, SCSIDiskState), @@ -3085,6 +3177,15 @@ static Property scsi_cd_properties[] = { DEFAULT_MAX_IO_SIZE), DEFINE_PROP_INT32("scsi_version", SCSIDiskState, qdev.default_scsi_version, 5), + DEFINE_PROP_BIT("quirk_mode_page_apple_vendor", SCSIDiskState, quirks, + SCSI_DISK_QUIRK_MODE_PAGE_APPLE_VENDOR, 0), + DEFINE_PROP_BIT("quirk_mode_sense_rom_use_dbd", SCSIDiskState, quirks, + SCSI_DISK_QUIRK_MODE_SENSE_ROM_USE_DBD, 0), + DEFINE_PROP_BIT("quirk_mode_page_vendor_specific_apple", SCSIDiskState, + quirks, SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE, + 0), + DEFINE_PROP_BIT("quirk_mode_page_truncated", SCSIDiskState, quirks, + SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 0ab00ef85c..ee945f87e3 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -109,14 +109,11 @@ done: static void scsi_command_complete(void *opaque, int ret) { SCSIGenericReq *r = (SCSIGenericReq *)opaque; - SCSIDevice *s = r->req.dev; assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->conf.blk)); scsi_command_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(s->conf.blk)); } static int execute_command(BlockBackend *blk, @@ -147,6 +144,18 @@ static int execute_command(BlockBackend *blk, return 0; } +static uint64_t calculate_max_transfer(SCSIDevice *s) +{ + uint64_t max_transfer = blk_get_max_hw_transfer(s->conf.blk); + uint32_t max_iov = blk_get_max_hw_iov(s->conf.blk); + + assert(max_transfer); + max_transfer = MIN_NON_ZERO(max_transfer, + max_iov * qemu_real_host_page_size()); + + return max_transfer / s->blocksize; +} + static int scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s, int len) { uint8_t page, page_idx; @@ -178,17 +187,16 @@ static int scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s, int len) if ((s->type == TYPE_DISK || s->type == TYPE_ZBC) && (r->req.cmd.buf[1] & 0x01)) { page = r->req.cmd.buf[2]; - if (page == 0xb0) { - uint64_t max_transfer = blk_get_max_hw_transfer(s->conf.blk); - uint32_t max_iov = blk_get_max_hw_iov(s->conf.blk); + if (page == 0xb0 && r->buflen >= 8) { + uint8_t buf[16] = {}; + uint8_t buf_used = MIN(r->buflen, 16); + uint64_t max_transfer = calculate_max_transfer(s); + + memcpy(buf, r->buf, buf_used); + stl_be_p(&buf[8], max_transfer); + stl_be_p(&buf[12], MIN_NON_ZERO(max_transfer, ldl_be_p(&buf[12]))); + memcpy(r->buf + 8, buf + 8, buf_used - 8); - assert(max_transfer); - max_transfer = MIN_NON_ZERO(max_transfer, max_iov * qemu_real_host_page_size()) - / s->blocksize; - stl_be_p(&r->buf[8], max_transfer); - /* Also take care of the opt xfer len. */ - stl_be_p(&r->buf[12], - MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); } else if (s->needs_vpd_bl_emulation && page == 0x00 && r->buflen >= 4) { /* * Now we're capable of supplying the VPD Block Limits @@ -230,7 +238,7 @@ static int scsi_generic_emulate_block_limits(SCSIGenericReq *r, SCSIDevice *s) uint8_t buf[64]; SCSIBlockLimits bl = { - .max_io_sectors = blk_get_max_transfer(s->conf.blk) / s->blocksize + .max_io_sectors = calculate_max_transfer(s), }; memset(r->buf, 0, r->buflen); @@ -265,11 +273,9 @@ static void scsi_read_complete(void * opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->conf.blk)); - if (ret || r->req.io_canceled) { scsi_command_complete_noio(r, ret); - goto done; + return; } len = r->io_header.dxfer_len - r->io_header.resid; @@ -308,7 +314,7 @@ static void scsi_read_complete(void * opaque, int ret) r->io_header.status != GOOD || len == 0) { scsi_command_complete_noio(r, 0); - goto done; + return; } /* Snoop READ CAPACITY output to set the blocksize. */ @@ -321,7 +327,6 @@ static void scsi_read_complete(void * opaque, int ret) s->blocksize = ldl_be_p(&r->buf[8]); s->max_lba = ldq_be_p(&r->buf[0]); } - blk_set_guest_block_size(s->conf.blk, s->blocksize); /* * Patch MODE SENSE device specific parameters if the BDS is opened @@ -345,9 +350,6 @@ static void scsi_read_complete(void * opaque, int ret) req_complete: scsi_req_data(&r->req, len); scsi_req_unref(&r->req); - -done: - aio_context_release(blk_get_aio_context(s->conf.blk)); } /* Read more data from scsi device into buffer. */ @@ -383,11 +385,9 @@ static void scsi_write_complete(void * opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->conf.blk)); - if (ret || r->req.io_canceled) { scsi_command_complete_noio(r, ret); - goto done; + return; } if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 && @@ -397,9 +397,6 @@ static void scsi_write_complete(void * opaque, int ret) } scsi_command_complete_noio(r, ret); - -done: - aio_context_release(blk_get_aio_context(s->conf.blk)); } /* Write data to a scsi device. Returns nonzero on failure. @@ -755,7 +752,6 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp) /* Only used by scsi-block, but initialize it nevertheless to be clean. */ s->default_scsi_version = -1; - s->io_timeout = DEFAULT_IO_TIMEOUT; scsi_generic_read_device_inquiry(s); } @@ -785,9 +781,10 @@ static Property scsi_generic_properties[] = { }; static int scsi_generic_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, - uint8_t *buf, void *hba_private) + uint8_t *buf, size_t buf_len, + void *hba_private) { - return scsi_bus_parse_cdb(dev, cmd, buf, hba_private); + return scsi_bus_parse_cdb(dev, cmd, buf, buf_len, hba_private); } static void scsi_generic_class_initfn(ObjectClass *klass, void *data) diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index a07a8e1523..c75a6c8807 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -605,7 +605,7 @@ static const VMStateDescription vmstate_spapr_vscsi_req = { .name = "spapr_vscsi_req", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BUFFER(crq.raw, vscsi_req), VMSTATE_BUFFER(viosrp_iu_buf, vscsi_req), VMSTATE_UINT32(qtag, vscsi_req), @@ -783,6 +783,7 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) union srp_iu *srp = &req_iu(req)->srp; SCSIDevice *sdev; int n, lun; + size_t cdb_len = sizeof (srp->cmd.cdb) + (srp->cmd.add_cdb_len & ~3); if ((srp->cmd.lun == 0 || be64_to_cpu(srp->cmd.lun) == SRP_REPORT_LUNS_WLUN) && srp->cmd.cdb[0] == REPORT_LUNS) { @@ -801,7 +802,7 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) } return 1; } - req->sreq = scsi_req_new(sdev, req->qtag, lun, srp->cmd.cdb, req); + req->sreq = scsi_req_new(sdev, req->qtag, lun, srp->cmd.cdb, cdb_len, req); n = scsi_req_enqueue(req->sreq); trace_spapr_vscsi_queue_cmd(req->qtag, srp->cmd.cdb[0], @@ -864,7 +865,7 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) break; } - qdev_reset_all(&d->qdev); + device_cold_reset(&d->qdev); break; case SRP_TSK_ABORT_TASK_SET: @@ -1013,7 +1014,7 @@ static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req) } /* - * Current implementation does not suppport any migration or + * Current implementation does not support any migration or * reservation capabilities. Construct the response telling the * guest not to use them. */ @@ -1258,7 +1259,7 @@ static const VMStateDescription vmstate_spapr_vscsi = { .name = "spapr_vscsi", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_SPAPR_VIO(vdev, VSCSIState), /* VSCSI state */ /* ???? */ diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events index 20fb0dc162..f0f2a98c2e 100644 --- a/hw/scsi/trace-events +++ b/hw/scsi/trace-events @@ -6,6 +6,8 @@ scsi_req_cancel(int target, int lun, int tag) "target %d lun %d tag %d" scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d" scsi_req_data_canceled(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d" scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d" +scsi_bus_drained_begin(void *bus, void *sdev) "bus %p sdev %p" +scsi_bus_drained_end(void *bus, void *sdev) "bus %p sdev %p" scsi_req_continue(int target, int lun, int tag) "target %d lun %d tag %d" scsi_req_continue_canceled(int target, int lun, int tag) "target %d lun %d tag %d" scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer) "target %d lun %d tag %d command %d dir %d length %d" @@ -195,6 +197,7 @@ esp_mem_writeb_cmd_selatns(uint32_t val) "Select with ATN & stop (0x%2.2x)" esp_mem_writeb_cmd_ensel(uint32_t val) "Enable selection (0x%2.2x)" esp_mem_writeb_cmd_dissel(uint32_t val) "Disable selection (0x%2.2x)" esp_mem_writeb_cmd_ti(uint32_t val) "Transfer Information (0x%2.2x)" +esp_set_phase(const char *phase) "setting bus phase to %s" # esp-pci.c esp_pci_error_invalid_dma_direction(void) "invalid DMA transfer direction" @@ -299,6 +302,8 @@ lsi_execute_script_stop(void) "SCRIPTS execution stopped" lsi_awoken(void) "Woken by SIGP" lsi_reg_read(const char *name, int offset, uint8_t ret) "Read reg %s 0x%x = 0x%02x" lsi_reg_write(const char *name, int offset, uint8_t val) "Write reg %s 0x%x = 0x%02x" +lsi_scripts_timer_triggered(void) "SCRIPTS timer triggered" +lsi_scripts_timer_start(void) "SCRIPTS timer started" # virtio-scsi.c virtio_scsi_cmd_req(int lun, uint32_t tag, uint8_t cmd) "virtio_scsi_cmd_req lun=%u tag=0x%x cmd=0x%x" @@ -334,10 +339,13 @@ scsi_disk_emulate_command_UNMAP(size_t xfer) "Unmap (len %zd)" scsi_disk_emulate_command_VERIFY(int bytchk) "Verify (bytchk %d)" scsi_disk_emulate_command_WRITE_SAME(int cmd, size_t xfer) "WRITE SAME %d (len %zd)" scsi_disk_emulate_command_UNKNOWN(int cmd, const char *name) "Unknown SCSI command (0x%2.2x=%s)" +scsi_disk_emulate_command_FORMAT_UNIT(size_t xfer) "Format Unit (len %zu)" scsi_disk_dma_command_READ(uint64_t lba, uint32_t len) "Read (sector %" PRId64 ", count %u)" scsi_disk_dma_command_WRITE(const char *cmd, uint64_t lba, int len) "Write %s(sector %" PRId64 ", count %u)" scsi_disk_new_request(uint32_t lun, uint32_t tag, const char *line) "Command: lun=%d tag=0x%x data=%s" scsi_disk_aio_sgio_command(uint32_t tag, uint8_t cmd, uint64_t lba, int len, uint32_t timeout) "disk aio sgio: tag=0x%x cmd=0x%x (sector %" PRId64 ", count %d) timeout=%u" +scsi_disk_mode_select_page_truncated(int page, int len, int page_len) "page %d expected length %d but received length %d" +scsi_disk_mode_select_set_blocksize(int blocksize) "set block size to %d" # scsi-generic.c scsi_generic_command_complete_noio(void *req, uint32_t tag, int statuc) "Command complete %p tag=0x%x status=%d" diff --git a/hw/scsi/vhost-scsi-common.c b/hw/scsi/vhost-scsi-common.c index 767f827e55..4c8637045d 100644 --- a/hw/scsi/vhost-scsi-common.c +++ b/hw/scsi/vhost-scsi-common.c @@ -16,6 +16,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "hw/virtio/vhost.h" @@ -25,7 +26,7 @@ #include "hw/virtio/virtio-access.h" #include "hw/fw-path-provider.h" -int vhost_scsi_common_start(VHostSCSICommon *vsc) +int vhost_scsi_common_start(VHostSCSICommon *vsc, Error **errp) { int ret, i; VirtIODevice *vdev = VIRTIO_DEVICE(vsc); @@ -35,42 +36,51 @@ int vhost_scsi_common_start(VHostSCSICommon *vsc) VirtIOSCSICommon *vs = (VirtIOSCSICommon *)vsc; if (!k->set_guest_notifiers) { - error_report("binding does not support guest notifiers"); + error_setg(errp, "binding does not support guest notifiers"); return -ENOSYS; } ret = vhost_dev_enable_notifiers(&vsc->dev, vdev); if (ret < 0) { + error_setg_errno(errp, -ret, "Error enabling host notifiers"); return ret; } ret = k->set_guest_notifiers(qbus->parent, vsc->dev.nvqs, true); if (ret < 0) { - error_report("Error binding guest notifier"); + error_setg_errno(errp, -ret, "Error binding guest notifier"); goto err_host_notifiers; } vsc->dev.acked_features = vdev->guest_features; - assert(vsc->inflight == NULL); - vsc->inflight = g_new0(struct vhost_inflight, 1); - ret = vhost_dev_get_inflight(&vsc->dev, - vs->conf.virtqueue_size, - vsc->inflight); + ret = vhost_dev_prepare_inflight(&vsc->dev, vdev); if (ret < 0) { - error_report("Error get inflight: %d", -ret); + error_setg_errno(errp, -ret, "Error setting inflight format"); goto err_guest_notifiers; } - ret = vhost_dev_set_inflight(&vsc->dev, vsc->inflight); - if (ret < 0) { - error_report("Error set inflight: %d", -ret); - goto err_guest_notifiers; + if (vsc->inflight) { + if (!vsc->inflight->addr) { + ret = vhost_dev_get_inflight(&vsc->dev, + vs->conf.virtqueue_size, + vsc->inflight); + if (ret < 0) { + error_setg_errno(errp, -ret, "Error getting inflight"); + goto err_guest_notifiers; + } + } + + ret = vhost_dev_set_inflight(&vsc->dev, vsc->inflight); + if (ret < 0) { + error_setg_errno(errp, -ret, "Error setting inflight"); + goto err_guest_notifiers; + } } - ret = vhost_dev_start(&vsc->dev, vdev); + ret = vhost_dev_start(&vsc->dev, vdev, true); if (ret < 0) { - error_report("Error start vhost dev"); + error_setg_errno(errp, -ret, "Error starting vhost dev"); goto err_guest_notifiers; } @@ -85,9 +95,6 @@ int vhost_scsi_common_start(VHostSCSICommon *vsc) return ret; err_guest_notifiers: - g_free(vsc->inflight); - vsc->inflight = NULL; - k->set_guest_notifiers(qbus->parent, vsc->dev.nvqs, false); err_host_notifiers: vhost_dev_disable_notifiers(&vsc->dev, vdev); @@ -101,7 +108,7 @@ void vhost_scsi_common_stop(VHostSCSICommon *vsc) VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); int ret = 0; - vhost_dev_stop(&vsc->dev, vdev); + vhost_dev_stop(&vsc->dev, vdev, true); if (k->set_guest_notifiers) { ret = k->set_guest_notifiers(qbus->parent, vsc->dev.nvqs, false); @@ -111,11 +118,6 @@ void vhost_scsi_common_stop(VHostSCSICommon *vsc) } assert(ret >= 0); - if (vsc->inflight) { - vhost_dev_free_inflight(vsc->inflight); - vsc->inflight = NULL; - } - vhost_dev_disable_notifiers(&vsc->dev, vdev); } diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 3059068175..ae26bc19a4 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -26,7 +26,6 @@ #include "hw/virtio/vhost.h" #include "hw/virtio/virtio-scsi.h" #include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-access.h" #include "hw/fw-path-provider.h" #include "hw/qdev-properties.h" #include "qemu/cutils.h" @@ -38,6 +37,7 @@ static const int kernel_feature_bits[] = { VIRTIO_RING_F_INDIRECT_DESC, VIRTIO_RING_F_EVENT_IDX, VIRTIO_SCSI_F_HOTPLUG, + VIRTIO_F_RING_RESET, VHOST_INVALID_FEATURE_BIT }; @@ -75,6 +75,7 @@ static int vhost_scsi_start(VHostSCSI *s) int ret, abi_version; VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); const VhostOps *vhost_ops = vsc->dev.vhost_ops; + Error *local_err = NULL; ret = vhost_ops->vhost_scsi_get_abi_version(&vsc->dev, &abi_version); if (ret < 0) { @@ -88,8 +89,9 @@ static int vhost_scsi_start(VHostSCSI *s) return -ENOSYS; } - ret = vhost_scsi_common_start(vsc); + ret = vhost_scsi_common_start(vsc, &local_err); if (ret < 0) { + error_reportf_err(local_err, "Error starting vhost-scsi: "); return ret; } @@ -120,7 +122,7 @@ static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) start = false; } - if (vsc->dev.started == start) { + if (vhost_dev_is_started(&vsc->dev) == start) { return; } @@ -147,7 +149,7 @@ static int vhost_scsi_pre_save(void *opaque) /* At this point, backend must be stopped, otherwise * it might keep writing to memory. */ - assert(!vsc->dev.started); + assert(!vhost_dev_is_started(&vsc->dev)); return 0; } @@ -156,15 +158,69 @@ static const VMStateDescription vmstate_virtio_vhost_scsi = { .name = "virtio-vhost_scsi", .minimum_version_id = 1, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, .pre_save = vhost_scsi_pre_save, }; +static int vhost_scsi_set_workers(VHostSCSICommon *vsc, bool per_virtqueue) +{ + struct vhost_dev *dev = &vsc->dev; + struct vhost_vring_worker vq_worker; + struct vhost_worker_state worker; + int i, ret; + + /* Use default worker */ + if (!per_virtqueue || dev->nvqs == VHOST_SCSI_VQ_NUM_FIXED + 1) { + return 0; + } + + /* + * ctl/evt share the first worker since it will be rare for them + * to send cmds while IO is running. + */ + for (i = VHOST_SCSI_VQ_NUM_FIXED + 1; i < dev->nvqs; i++) { + memset(&worker, 0, sizeof(worker)); + + ret = dev->vhost_ops->vhost_new_worker(dev, &worker); + if (ret == -ENOTTY) { + /* + * worker ioctls are not implemented so just ignore and + * and continue device setup. + */ + warn_report("vhost-scsi: Backend supports a single worker. " + "Ignoring worker_per_virtqueue=true setting."); + ret = 0; + break; + } else if (ret) { + break; + } + + memset(&vq_worker, 0, sizeof(vq_worker)); + vq_worker.worker_id = worker.worker_id; + vq_worker.index = i; + + ret = dev->vhost_ops->vhost_attach_vring_worker(dev, &vq_worker); + if (ret == -ENOTTY) { + /* + * It's a bug for the kernel to have supported the worker creation + * ioctl but not attach. + */ + dev->vhost_ops->vhost_free_worker(dev, &worker); + break; + } else if (ret) { + break; + } + } + + return ret; +} + static void vhost_scsi_realize(DeviceState *dev, Error **errp) { + ERRP_GUARD(); VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev); Error *err = NULL; @@ -208,7 +264,7 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) "When external environment supports it (Orchestrator migrates " "target SCSI device state or use shared storage over network), " "set 'migratable' property to true to enable migration."); - if (migrate_add_blocker(vsc->migration_blocker, errp) < 0) { + if (migrate_add_blocker_normal(&vsc->migration_blocker, errp) < 0) { goto free_virtio; } } @@ -230,6 +286,13 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) goto free_vqs; } + ret = vhost_scsi_set_workers(vsc, vs->conf.worker_per_virtqueue); + if (ret < 0) { + error_setg(errp, "vhost-scsi: vhost worker setup failed: %s", + strerror(-ret)); + goto free_vqs; + } + /* At present, channel and lun both are 0 for bootable vhost-scsi disk */ vsc->channel = 0; vsc->lun = 0; @@ -241,10 +304,9 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) free_vqs: g_free(vqs); if (!vsc->migratable) { - migrate_del_blocker(vsc->migration_blocker); + migrate_del_blocker(&vsc->migration_blocker); } free_virtio: - error_free(vsc->migration_blocker); virtio_scsi_common_unrealize(dev); close_fd: if (vhostfd >= 0) { @@ -260,8 +322,7 @@ static void vhost_scsi_unrealize(DeviceState *dev) struct vhost_virtqueue *vqs = vsc->dev.vqs; if (!vsc->migratable) { - migrate_del_blocker(vsc->migration_blocker); - error_free(vsc->migration_blocker); + migrate_del_blocker(&vsc->migration_blocker); } /* This will stop vhost backend. */ @@ -297,6 +358,8 @@ static Property vhost_scsi_properties[] = { VIRTIO_SCSI_F_T10_PI, false), DEFINE_PROP_BOOL("migratable", VHostSCSICommon, migratable, false), + DEFINE_PROP_BOOL("worker_per_virtqueue", VirtIOSCSICommon, + conf.worker_per_virtqueue, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index 9be21d07ee..a63b1f4948 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -26,7 +26,6 @@ #include "hw/virtio/vhost-backend.h" #include "hw/virtio/vhost-user-scsi.h" #include "hw/virtio/virtio.h" -#include "hw/virtio/virtio-access.h" #include "chardev/char-fe.h" #include "sysemu/sysemu.h" @@ -36,76 +35,237 @@ static const int user_feature_bits[] = { VIRTIO_RING_F_INDIRECT_DESC, VIRTIO_RING_F_EVENT_IDX, VIRTIO_SCSI_F_HOTPLUG, + VIRTIO_F_RING_RESET, VHOST_INVALID_FEATURE_BIT }; -enum VhostUserProtocolFeature { - VHOST_USER_PROTOCOL_F_RESET_DEVICE = 13, -}; +static int vhost_user_scsi_start(VHostUserSCSI *s, Error **errp) +{ + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + int ret; + + ret = vhost_scsi_common_start(vsc, errp); + s->started_vu = !(ret < 0); + + return ret; +} + +static void vhost_user_scsi_stop(VHostUserSCSI *s) +{ + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + + if (!s->started_vu) { + return; + } + s->started_vu = false; + + vhost_scsi_common_stop(vsc); +} static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status) { VHostUserSCSI *s = (VHostUserSCSI *)vdev; + DeviceState *dev = DEVICE(vdev); VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); - bool start = (status & VIRTIO_CONFIG_S_DRIVER_OK) && vdev->vm_running; + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); + bool should_start = virtio_device_should_start(vdev, status); + Error *local_err = NULL; + int ret; - if (vsc->dev.started == start) { + if (!s->connected) { return; } - if (start) { - int ret; + if (vhost_dev_is_started(&vsc->dev) == should_start) { + return; + } - ret = vhost_scsi_common_start(vsc); + if (should_start) { + ret = vhost_user_scsi_start(s, &local_err); if (ret < 0) { - error_report("unable to start vhost-user-scsi: %s", strerror(-ret)); - exit(1); + error_reportf_err(local_err, + "unable to start vhost-user-scsi: %s: ", + strerror(-ret)); + qemu_chr_fe_disconnect(&vs->conf.chardev); } } else { - vhost_scsi_common_stop(vsc); + vhost_user_scsi_stop(s); } } -static void vhost_user_scsi_reset(VirtIODevice *vdev) +static void vhost_user_scsi_handle_output(VirtIODevice *vdev, VirtQueue *vq) { - VHostSCSICommon *vsc = VHOST_SCSI_COMMON(vdev); - struct vhost_dev *dev = &vsc->dev; + VHostUserSCSI *s = (VHostUserSCSI *)vdev; + DeviceState *dev = DEVICE(vdev); + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(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)) { + Error *local_err = NULL; + int i, ret; + + if (!vdev->start_on_kick) { return; } - if (dev->vhost_ops->vhost_reset_device) { - dev->vhost_ops->vhost_reset_device(dev); + if (!s->connected) { + return; + } + + if (vhost_dev_is_started(&vsc->dev)) { + return; + } + + /* + * Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start + * vhost here instead of waiting for .set_status(). + */ + ret = vhost_user_scsi_start(s, &local_err); + if (ret < 0) { + error_reportf_err(local_err, "vhost-user-scsi: vhost start failed: "); + qemu_chr_fe_disconnect(&vs->conf.chardev); + return; + } + + /* Kick right away to begin processing requests already in vring */ + for (i = 0; i < vsc->dev.nvqs; i++) { + VirtQueue *kick_vq = virtio_get_queue(vdev, i); + + if (!virtio_queue_get_desc_addr(vdev, i)) { + continue; + } + event_notifier_set(virtio_queue_get_host_notifier(kick_vq)); } } -static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) +static int vhost_user_scsi_connect(DeviceState *dev, Error **errp) { + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserSCSI *s = VHOST_USER_SCSI(vdev); + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); + int ret = 0; + + if (s->connected) { + return 0; + } + + vsc->dev.num_queues = vs->conf.num_queues; + vsc->dev.nvqs = VIRTIO_SCSI_VQ_NUM_FIXED + vs->conf.num_queues; + vsc->dev.vqs = s->vhost_vqs; + vsc->dev.vq_index = 0; + vsc->dev.backend_features = 0; + + ret = vhost_dev_init(&vsc->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0, + errp); + if (ret < 0) { + return ret; + } + + s->connected = true; + + /* restore vhost state */ + if (virtio_device_started(vdev, vdev->status)) { + ret = vhost_user_scsi_start(s, errp); + } + + return ret; +} + +static void vhost_user_scsi_event(void *opaque, QEMUChrEvent event); + +static void vhost_user_scsi_disconnect(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserSCSI *s = VHOST_USER_SCSI(vdev); + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); + + if (!s->connected) { + return; + } + s->connected = false; + + vhost_user_scsi_stop(s); + + vhost_dev_cleanup(&vsc->dev); + + /* Re-instate the event handler for new connections */ + qemu_chr_fe_set_handlers(&vs->conf.chardev, NULL, NULL, + vhost_user_scsi_event, NULL, dev, NULL, true); +} + +static void vhost_user_scsi_event(void *opaque, QEMUChrEvent event) +{ + DeviceState *dev = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserSCSI *s = VHOST_USER_SCSI(vdev); + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); + Error *local_err = NULL; + + switch (event) { + case CHR_EVENT_OPENED: + if (vhost_user_scsi_connect(dev, &local_err) < 0) { + error_report_err(local_err); + qemu_chr_fe_disconnect(&vs->conf.chardev); + return; + } + break; + case CHR_EVENT_CLOSED: + /* defer close until later to avoid circular close */ + vhost_user_async_close(dev, &vs->conf.chardev, &vsc->dev, + vhost_user_scsi_disconnect, + vhost_user_scsi_event); + break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; + } +} + +static int vhost_user_scsi_realize_connect(VHostUserSCSI *s, Error **errp) +{ + DeviceState *dev = DEVICE(s); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); + int ret; + + s->connected = false; + + ret = qemu_chr_fe_wait_connected(&vs->conf.chardev, errp); + if (ret < 0) { + return ret; + } + + ret = vhost_user_scsi_connect(dev, errp); + if (ret < 0) { + qemu_chr_fe_disconnect(&vs->conf.chardev); + return ret; + } + assert(s->connected); + + return 0; } static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) { + ERRP_GUARD(); VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); VHostUserSCSI *s = VHOST_USER_SCSI(dev); VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); - struct vhost_virtqueue *vqs = NULL; Error *err = NULL; int ret; + int retries = VU_REALIZE_CONN_RETRIES; if (!vs->conf.chardev.chr) { error_setg(errp, "vhost-user-scsi: missing chardev"); return; } - virtio_scsi_common_realize(dev, vhost_dummy_handle_output, - vhost_dummy_handle_output, - vhost_dummy_handle_output, &err); + virtio_scsi_common_realize(dev, vhost_user_scsi_handle_output, + vhost_user_scsi_handle_output, + vhost_user_scsi_handle_output, &err); if (err != NULL) { error_propagate(errp, err); return; @@ -115,19 +275,28 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) goto free_virtio; } - vsc->dev.nvqs = VIRTIO_SCSI_VQ_NUM_FIXED + vs->conf.num_queues; - vsc->dev.vqs = g_new0(struct vhost_virtqueue, vsc->dev.nvqs); - vsc->dev.vq_index = 0; - vsc->dev.backend_features = 0; - vqs = vsc->dev.vqs; + vsc->inflight = g_new0(struct vhost_inflight, 1); + s->vhost_vqs = g_new0(struct vhost_virtqueue, + VIRTIO_SCSI_VQ_NUM_FIXED + vs->conf.num_queues); + + assert(!*errp); + do { + if (*errp) { + error_prepend(errp, "Reconnecting after error: "); + error_report_err(*errp); + *errp = NULL; + } + ret = vhost_user_scsi_realize_connect(s, errp); + } while (ret < 0 && retries--); - s->vhost_user.supports_config = true; - ret = vhost_dev_init(&vsc->dev, &s->vhost_user, - VHOST_BACKEND_TYPE_USER, 0, errp); if (ret < 0) { goto free_vhost; } + /* we're fully initialized, now we can operate, so add the handler */ + qemu_chr_fe_set_handlers(&vs->conf.chardev, NULL, NULL, + vhost_user_scsi_event, NULL, (void *)dev, + NULL, true); /* Channel and lun both are 0 for bootable vhost-user-scsi disk */ vsc->channel = 0; vsc->lun = 0; @@ -136,8 +305,12 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) return; free_vhost: + g_free(s->vhost_vqs); + s->vhost_vqs = NULL; + g_free(vsc->inflight); + vsc->inflight = NULL; vhost_user_cleanup(&s->vhost_user); - g_free(vqs); + free_virtio: virtio_scsi_common_unrealize(dev); } @@ -147,16 +320,23 @@ static void vhost_user_scsi_unrealize(DeviceState *dev) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostUserSCSI *s = VHOST_USER_SCSI(dev); VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); - struct vhost_virtqueue *vqs = vsc->dev.vqs; + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); /* This will stop the vhost backend. */ vhost_user_scsi_set_status(vdev, 0); + qemu_chr_fe_set_handlers(&vs->conf.chardev, NULL, NULL, NULL, NULL, NULL, + NULL, false); vhost_dev_cleanup(&vsc->dev); - g_free(vqs); + g_free(s->vhost_vqs); + s->vhost_vqs = NULL; + + vhost_dev_free_inflight(vsc->inflight); + g_free(vsc->inflight); + vsc->inflight = NULL; - virtio_scsi_common_unrealize(dev); vhost_user_cleanup(&s->vhost_user); + virtio_scsi_common_unrealize(dev); } static Property vhost_user_scsi_properties[] = { @@ -181,11 +361,25 @@ static Property vhost_user_scsi_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void vhost_user_scsi_reset(VirtIODevice *vdev) +{ + VHostUserSCSI *s = VHOST_USER_SCSI(vdev); + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + + vhost_dev_free_inflight(vsc->inflight); +} + +static struct vhost_dev *vhost_user_scsi_get_vhost(VirtIODevice *vdev) +{ + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(vdev); + return &vsc->dev; +} + static const VMStateDescription vmstate_vhost_scsi = { .name = "virtio-scsi", .minimum_version_id = 1, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, @@ -205,8 +399,9 @@ 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; + vdc->reset = vhost_user_scsi_reset; + vdc->get_vhost = vhost_user_scsi_get_vhost; } static void vhost_user_scsi_instance_init(Object *obj) diff --git a/hw/scsi/viosrp.h b/hw/scsi/viosrp.h index e5f9768e8f..58c29aa925 100644 --- a/hw/scsi/viosrp.h +++ b/hw/scsi/viosrp.h @@ -16,8 +16,7 @@ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* along with this program. If not, see . */ /* */ /* */ /* This file contains structures and definitions for IBM RPA (RS/6000 */ diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 8bb6e6acfc..2806a121b2 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -19,9 +19,8 @@ #include "hw/scsi/scsi.h" #include "scsi/constants.h" #include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-access.h" -/* Context: QEMU global mutex held */ +/* Context: BQL held */ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp) { VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); @@ -71,16 +70,30 @@ static void virtio_scsi_dataplane_stop_bh(void *opaque) { VirtIOSCSI *s = opaque; VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + EventNotifier *host_notifier; int i; virtio_queue_aio_detach_host_notifier(vs->ctrl_vq, s->ctx); + host_notifier = virtio_queue_get_host_notifier(vs->ctrl_vq); + + /* + * Test and clear notifier after disabling event, in case poll callback + * didn't have time to run. + */ + virtio_queue_host_notifier_read(host_notifier); + virtio_queue_aio_detach_host_notifier(vs->event_vq, s->ctx); + host_notifier = virtio_queue_get_host_notifier(vs->event_vq); + virtio_queue_host_notifier_read(host_notifier); + for (i = 0; i < vs->conf.num_queues; i++) { virtio_queue_aio_detach_host_notifier(vs->cmd_vqs[i], s->ctx); + host_notifier = virtio_queue_get_host_notifier(vs->cmd_vqs[i]); + virtio_queue_host_notifier_read(host_notifier); } } -/* Context: QEMU global mutex held */ +/* Context: BQL held */ int virtio_scsi_dataplane_start(VirtIODevice *vdev) { int i; @@ -136,17 +149,18 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev) memory_region_transaction_commit(); - aio_context_acquire(s->ctx); - virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, s->ctx); - virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq, s->ctx); - - for (i = 0; i < vs->conf.num_queues; i++) { - virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx); - } - s->dataplane_starting = false; s->dataplane_started = true; - aio_context_release(s->ctx); + smp_wmb(); /* paired with aio_notify_accept() */ + + if (s->bus.drain_count == 0) { + virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, s->ctx); + virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq, s->ctx); + + for (i = 0; i < vs->conf.num_queues; i++) { + virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx); + } + } return 0; fail_host_notifiers: @@ -171,7 +185,7 @@ fail_guest_notifiers: return -ENOSYS; } -/* Context: QEMU global mutex held */ +/* Context: BQL held */ void virtio_scsi_dataplane_stop(VirtIODevice *vdev) { BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); @@ -192,9 +206,9 @@ void virtio_scsi_dataplane_stop(VirtIODevice *vdev) } s->dataplane_stopping = true; - aio_context_acquire(s->ctx); - aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s); - aio_context_release(s->ctx); + if (s->bus.drain_count == 0) { + aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s); + } blk_drain_all(); /* ensure there are no in-flight requests */ diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 4141dddd51..9f02ceea09 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -18,10 +18,12 @@ #include "standard-headers/linux/virtio_ids.h" #include "hw/virtio/virtio-scsi.h" #include "migration/qemu-file-types.h" +#include "qemu/defer-call.h" #include "qemu/error-report.h" #include "qemu/iov.h" #include "qemu/module.h" #include "sysemu/block-backend.h" +#include "sysemu/dma.h" #include "hw/qdev-properties.h" #include "hw/scsi/scsi.h" #include "scsi/constants.h" @@ -42,13 +44,11 @@ typedef struct VirtIOSCSIReq { QEMUSGList qsgl; QEMUIOVector resp_iov; - union { - /* Used for two-stage request submission */ - QTAILQ_ENTRY(VirtIOSCSIReq) next; + /* Used for two-stage request submission and TMFs deferred to BH */ + QTAILQ_ENTRY(VirtIOSCSIReq) next; - /* Used for cancellation of request during TMFs */ - int remaining; - }; + /* Used for cancellation of request during TMFs */ + int remaining; SCSIRequest *sreq; size_t resp_size; @@ -123,6 +123,30 @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req) virtio_scsi_free_req(req); } +static void virtio_scsi_complete_req_bh(void *opaque) +{ + VirtIOSCSIReq *req = opaque; + + virtio_scsi_complete_req(req); +} + +/* + * Called from virtio_scsi_do_one_tmf_bh() in main loop thread. The main loop + * thread cannot touch the virtqueue since that could race with an IOThread. + */ +static void virtio_scsi_complete_req_from_main_loop(VirtIOSCSIReq *req) +{ + VirtIOSCSI *s = req->dev; + + if (!s->ctx || s->ctx == qemu_get_aio_context()) { + /* No need to schedule a BH when there is no IOThread */ + virtio_scsi_complete_req(req); + } else { + /* Run request completion in the IOThread */ + aio_wait_bh_oneshot(s->ctx, virtio_scsi_complete_req_bh, req); + } +} + static void virtio_scsi_bad_req(VirtIOSCSIReq *req) { virtio_error(VIRTIO_DEVICE(req->dev), "wrong size for virtio-scsi headers"); @@ -293,6 +317,116 @@ static inline void virtio_scsi_ctx_check(VirtIOSCSI *s, SCSIDevice *d) } } +static void virtio_scsi_do_one_tmf_bh(VirtIOSCSIReq *req) +{ + VirtIOSCSI *s = req->dev; + SCSIDevice *d = virtio_scsi_device_get(s, req->req.tmf.lun); + BusChild *kid; + int target; + + switch (req->req.tmf.subtype) { + case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: + if (!d) { + req->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET; + goto out; + } + if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { + req->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN; + goto out; + } + qatomic_inc(&s->resetting); + device_cold_reset(&d->qdev); + qatomic_dec(&s->resetting); + break; + + case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: + target = req->req.tmf.lun[1]; + qatomic_inc(&s->resetting); + + rcu_read_lock(); + QTAILQ_FOREACH_RCU(kid, &s->bus.qbus.children, sibling) { + SCSIDevice *d1 = SCSI_DEVICE(kid->child); + if (d1->channel == 0 && d1->id == target) { + device_cold_reset(&d1->qdev); + } + } + rcu_read_unlock(); + + qatomic_dec(&s->resetting); + break; + + default: + g_assert_not_reached(); + break; + } + +out: + object_unref(OBJECT(d)); + virtio_scsi_complete_req_from_main_loop(req); +} + +/* Some TMFs must be processed from the main loop thread */ +static void virtio_scsi_do_tmf_bh(void *opaque) +{ + VirtIOSCSI *s = opaque; + QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); + VirtIOSCSIReq *req; + VirtIOSCSIReq *tmp; + + GLOBAL_STATE_CODE(); + + WITH_QEMU_LOCK_GUARD(&s->tmf_bh_lock) { + QTAILQ_FOREACH_SAFE(req, &s->tmf_bh_list, next, tmp) { + QTAILQ_REMOVE(&s->tmf_bh_list, req, next); + QTAILQ_INSERT_TAIL(&reqs, req, next); + } + + qemu_bh_delete(s->tmf_bh); + s->tmf_bh = NULL; + } + + QTAILQ_FOREACH_SAFE(req, &reqs, next, tmp) { + QTAILQ_REMOVE(&reqs, req, next); + virtio_scsi_do_one_tmf_bh(req); + } +} + +static void virtio_scsi_reset_tmf_bh(VirtIOSCSI *s) +{ + VirtIOSCSIReq *req; + VirtIOSCSIReq *tmp; + + GLOBAL_STATE_CODE(); + + /* Called after ioeventfd has been stopped, so tmf_bh_lock is not needed */ + if (s->tmf_bh) { + qemu_bh_delete(s->tmf_bh); + s->tmf_bh = NULL; + } + + QTAILQ_FOREACH_SAFE(req, &s->tmf_bh_list, next, tmp) { + QTAILQ_REMOVE(&s->tmf_bh_list, req, next); + + /* SAM-6 6.3.2 Hard reset */ + req->resp.tmf.response = VIRTIO_SCSI_S_TARGET_FAILURE; + virtio_scsi_complete_req(req); + } +} + +static void virtio_scsi_defer_tmf_to_bh(VirtIOSCSIReq *req) +{ + VirtIOSCSI *s = req->dev; + + WITH_QEMU_LOCK_GUARD(&s->tmf_bh_lock) { + QTAILQ_INSERT_TAIL(&s->tmf_bh_list, req, next); + + if (!s->tmf_bh) { + s->tmf_bh = qemu_bh_new(virtio_scsi_do_tmf_bh, s); + qemu_bh_schedule(s->tmf_bh); + } + } +} + /* Return 0 if the request is ready to be completed and return to guest; * -EINPROGRESS if the request is submitted and will be completed later, in the * case of async cancellation. */ @@ -300,8 +434,6 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) { SCSIDevice *d = virtio_scsi_device_get(s, req->req.tmf.lun); SCSIRequest *r, *next; - BusChild *kid; - int target; int ret = 0; virtio_scsi_ctx_check(s, d); @@ -358,15 +490,9 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) break; case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: - if (!d) { - goto fail; - } - if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { - goto incorrect_lun; - } - s->resetting++; - qdev_reset_all(&d->qdev); - s->resetting--; + case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: + virtio_scsi_defer_tmf_to_bh(req); + ret = -EINPROGRESS; break; case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET: @@ -409,22 +535,6 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) } break; - case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: - target = req->req.tmf.lun[1]; - s->resetting++; - - rcu_read_lock(); - QTAILQ_FOREACH_RCU(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *d1 = SCSI_DEVICE(kid->child); - if (d1->channel == 0 && d1->id == target) { - qdev_reset_all(&d1->qdev); - } - } - rcu_read_unlock(); - - s->resetting--; - break; - case VIRTIO_SCSI_T_TMF_CLEAR_ACA: default: req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_REJECTED; @@ -532,9 +642,7 @@ static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) return; } - virtio_scsi_acquire(s); virtio_scsi_handle_ctrl_vq(s, vq); - virtio_scsi_release(s); } static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req) @@ -622,7 +730,8 @@ static void virtio_scsi_command_complete(SCSIRequest *r, size_t resid) } static int virtio_scsi_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, - uint8_t *buf, void *hba_private) + uint8_t *buf, size_t buf_len, + void *hba_private) { VirtIOSCSIReq *req = hba_private; @@ -653,7 +762,7 @@ static void virtio_scsi_request_cancelled(SCSIRequest *r) if (!req) { return; } - if (req->dev->resetting) { + if (qatomic_read(&req->dev->resetting)) { req->resp.cmd.response = VIRTIO_SCSI_S_RESET; } else { req->resp.cmd.response = VIRTIO_SCSI_S_ABORTED; @@ -669,7 +778,7 @@ static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req) static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) { - VirtIOSCSICommon *vs = &s->parent_obj; + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); SCSIDevice *d; int rc; @@ -696,7 +805,7 @@ static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) virtio_scsi_ctx_check(s, d); req->sreq = scsi_req_new(d, req->req.cmd.tag, virtio_scsi_get_lun(req->req.cmd.lun), - req->req.cmd.cdb, req); + req->req.cmd.cdb, vs->cdb_size, req); if (req->sreq->cmd.mode != SCSI_XFER_NONE && (req->sreq->cmd.mode != req->mode || @@ -707,7 +816,7 @@ static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) return -ENOBUFS; } scsi_req_ref(req->sreq); - blk_io_plug(d->conf.blk); + defer_call_begin(); object_unref(OBJECT(d)); return 0; } @@ -718,7 +827,7 @@ static void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req) if (scsi_req_enqueue(sreq)) { scsi_req_continue(sreq); } - blk_io_unplug(sreq->dev->conf.blk); + defer_call_end(); scsi_req_unref(sreq); } @@ -744,7 +853,7 @@ static void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq) while (!QTAILQ_EMPTY(&reqs)) { req = QTAILQ_FIRST(&reqs); QTAILQ_REMOVE(&reqs, req, next); - blk_io_unplug(req->sreq->dev->conf.blk); + defer_call_end(); scsi_req_unref(req->sreq); virtqueue_detach_element(req->vq, &req->elem, 0); virtio_scsi_free_req(req); @@ -771,9 +880,7 @@ static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) return; } - virtio_scsi_acquire(s); virtio_scsi_handle_cmd_vq(s, vq); - virtio_scsi_release(s); } static void virtio_scsi_get_config(VirtIODevice *vdev, @@ -829,22 +936,39 @@ static void virtio_scsi_reset(VirtIODevice *vdev) VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); assert(!s->dataplane_started); - s->resetting++; - qbus_reset_all(BUS(&s->bus)); - s->resetting--; + + virtio_scsi_reset_tmf_bh(s); + + qatomic_inc(&s->resetting); + bus_cold_reset(BUS(&s->bus)); + qatomic_dec(&s->resetting); vs->sense_size = VIRTIO_SCSI_SENSE_DEFAULT_SIZE; vs->cdb_size = VIRTIO_SCSI_CDB_DEFAULT_SIZE; s->events_dropped = false; } -static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, - uint32_t event, uint32_t reason) +typedef struct { + uint32_t event; + uint32_t reason; + union { + /* Used by messages specific to a device */ + struct { + uint32_t id; + uint32_t lun; + } address; + }; +} VirtIOSCSIEventInfo; + +static void virtio_scsi_push_event(VirtIOSCSI *s, + const VirtIOSCSIEventInfo *info) { VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); VirtIOSCSIReq *req; VirtIOSCSIEvent *evt; VirtIODevice *vdev = VIRTIO_DEVICE(s); + uint32_t event = info->event; + uint32_t reason = info->reason; if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { return; @@ -870,27 +994,28 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, memset(evt, 0, sizeof(VirtIOSCSIEvent)); evt->event = virtio_tswap32(vdev, event); evt->reason = virtio_tswap32(vdev, reason); - if (!dev) { - assert(event == VIRTIO_SCSI_T_EVENTS_MISSED); - } else { + if (event != VIRTIO_SCSI_T_EVENTS_MISSED) { evt->lun[0] = 1; - evt->lun[1] = dev->id; + evt->lun[1] = info->address.id; /* Linux wants us to keep the same encoding we use for REPORT LUNS. */ - if (dev->lun >= 256) { - evt->lun[2] = (dev->lun >> 8) | 0x40; + if (info->address.lun >= 256) { + evt->lun[2] = (info->address.lun >> 8) | 0x40; } - evt->lun[3] = dev->lun & 0xFF; + evt->lun[3] = info->address.lun & 0xFF; } trace_virtio_scsi_event(virtio_scsi_get_lun(evt->lun), event, reason); - + virtio_scsi_complete_req(req); } static void virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq) { if (s->events_dropped) { - virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); + VirtIOSCSIEventInfo info = { + .event = VIRTIO_SCSI_T_NO_EVENT, + }; + virtio_scsi_push_event(s, &info); } } @@ -902,9 +1027,7 @@ static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq) return; } - virtio_scsi_acquire(s); virtio_scsi_handle_event_vq(s, vq); - virtio_scsi_release(s); } static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) @@ -914,10 +1037,16 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_CHANGE) && dev->type != TYPE_ROM) { - virtio_scsi_acquire(s); - virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE, - sense.asc | (sense.ascq << 8)); - virtio_scsi_release(s); + VirtIOSCSIEventInfo info = { + .event = VIRTIO_SCSI_T_PARAM_CHANGE, + .reason = sense.asc | (sense.ascq << 8), + .address = { + .id = dev->id, + .lun = dev->lun, + }, + }; + + virtio_scsi_push_event(s, &info); } } @@ -934,28 +1063,30 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); VirtIOSCSI *s = VIRTIO_SCSI(vdev); SCSIDevice *sd = SCSI_DEVICE(dev); - AioContext *old_context; int ret; if (s->ctx && !s->dataplane_fenced) { if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { return; } - old_context = blk_get_aio_context(sd->conf.blk); - aio_context_acquire(old_context); ret = blk_set_aio_context(sd->conf.blk, s->ctx, errp); - aio_context_release(old_context); if (ret < 0) { return; } } if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { - virtio_scsi_acquire(s); - virtio_scsi_push_event(s, sd, - VIRTIO_SCSI_T_TRANSPORT_RESET, - VIRTIO_SCSI_EVT_RESET_RESCAN); - virtio_scsi_release(s); + VirtIOSCSIEventInfo info = { + .event = VIRTIO_SCSI_T_TRANSPORT_RESET, + .reason = VIRTIO_SCSI_EVT_RESET_RESCAN, + .address = { + .id = sd->id, + .lun = sd->lun, + }, + }; + + virtio_scsi_push_event(s, &info); + scsi_bus_set_ua(&s->bus, SENSE_CODE(REPORTED_LUNS_CHANGED)); } } @@ -965,25 +1096,82 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); VirtIOSCSI *s = VIRTIO_SCSI(vdev); SCSIDevice *sd = SCSI_DEVICE(dev); - AioContext *ctx = s->ctx ?: qemu_get_aio_context(); + VirtIOSCSIEventInfo info = { + .event = VIRTIO_SCSI_T_TRANSPORT_RESET, + .reason = VIRTIO_SCSI_EVT_RESET_REMOVED, + .address = { + .id = sd->id, + .lun = sd->lun, + }, + }; - if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { - virtio_scsi_acquire(s); - virtio_scsi_push_event(s, sd, - VIRTIO_SCSI_T_TRANSPORT_RESET, - VIRTIO_SCSI_EVT_RESET_REMOVED); - virtio_scsi_release(s); - } - - aio_disable_external(ctx); qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); - aio_enable_external(ctx); if (s->ctx) { - virtio_scsi_acquire(s); /* If other users keep the BlockBackend in the iothread, that's ok */ blk_set_aio_context(sd->conf.blk, qemu_get_aio_context(), NULL); - virtio_scsi_release(s); + } + + if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { + virtio_scsi_push_event(s, &info); + scsi_bus_set_ua(&s->bus, SENSE_CODE(REPORTED_LUNS_CHANGED)); + } +} + +/* Suspend virtqueue ioeventfd processing during drain */ +static void virtio_scsi_drained_begin(SCSIBus *bus) +{ + VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); + VirtIODevice *vdev = VIRTIO_DEVICE(s); + uint32_t total_queues = VIRTIO_SCSI_VQ_NUM_FIXED + + s->parent_obj.conf.num_queues; + + /* + * Drain is called when stopping dataplane but the host notifier has + * already been detached. Detaching multiple times is a no-op if nothing + * else is using the monitoring same file descriptor, but avoid it just in + * case. + * + * Also, don't detach if dataplane has not even been started yet because + * the host notifier isn't attached. + */ + if (s->dataplane_stopping || !s->dataplane_started) { + return; + } + + for (uint32_t i = 0; i < total_queues; i++) { + VirtQueue *vq = virtio_get_queue(vdev, i); + virtio_queue_aio_detach_host_notifier(vq, s->ctx); + } +} + +/* Resume virtqueue ioeventfd processing after drain */ +static void virtio_scsi_drained_end(SCSIBus *bus) +{ + VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + VirtIODevice *vdev = VIRTIO_DEVICE(s); + uint32_t total_queues = VIRTIO_SCSI_VQ_NUM_FIXED + + s->parent_obj.conf.num_queues; + + /* + * Drain is called when stopping dataplane. Keep the host notifier detached + * so it's not left dangling after dataplane is stopped. + * + * Also, don't attach if dataplane has not even been started yet. We're not + * ready. + */ + if (s->dataplane_stopping || !s->dataplane_started) { + return; + } + + for (uint32_t i = 0; i < total_queues; i++) { + VirtQueue *vq = virtio_get_queue(vdev, i); + if (vq == vs->event_vq) { + virtio_queue_aio_attach_host_notifier_no_poll(vq, s->ctx); + } else { + virtio_queue_aio_attach_host_notifier(vq, s->ctx); + } } } @@ -1001,6 +1189,8 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = { .get_sg_list = virtio_scsi_get_sg_list, .save_request = virtio_scsi_save_request, .load_request = virtio_scsi_load_request, + .drained_begin = virtio_scsi_drained_begin, + .drained_end = virtio_scsi_drained_end, }; void virtio_scsi_common_realize(DeviceState *dev, @@ -1049,6 +1239,9 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) VirtIOSCSI *s = VIRTIO_SCSI(dev); Error *err = NULL; + QTAILQ_INIT(&s->tmf_bh_list); + qemu_mutex_init(&s->tmf_bh_lock); + virtio_scsi_common_realize(dev, virtio_scsi_handle_ctrl, virtio_scsi_handle_event, @@ -1086,8 +1279,11 @@ static void virtio_scsi_device_unrealize(DeviceState *dev) { VirtIOSCSI *s = VIRTIO_SCSI(dev); + virtio_scsi_reset_tmf_bh(s); + qbus_set_hotplug_handler(BUS(&s->bus), NULL); virtio_scsi_common_unrealize(dev); + qemu_mutex_destroy(&s->tmf_bh_lock); } static Property virtio_scsi_properties[] = { @@ -1114,7 +1310,7 @@ static const VMStateDescription vmstate_virtio_scsi = { .name = "virtio-scsi", .minimum_version_id = 1, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 4d9969f3b1..cd7bf6aa01 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -445,7 +445,7 @@ static void pvscsi_reset_adapter(PVSCSIState *s) { s->resetting++; - qbus_reset_all(BUS(&s->bus)); + bus_cold_reset(BUS(&s->bus)); s->resetting--; pvscsi_process_completion_queue(s); assert(QTAILQ_EMPTY(&s->pending_queue)); @@ -730,7 +730,7 @@ pvscsi_process_request_descriptor(PVSCSIState *s, r->sg.elemAddr = descr->dataAddr; } - r->sreq = scsi_req_new(d, descr->context, r->lun, descr->cdb, r); + r->sreq = scsi_req_new(d, descr->context, r->lun, descr->cdb, descr->cdbLen, r); if (r->sreq->cmd.mode == SCSI_XFER_FROM_DEV && (descr->flags & PVSCSI_FLAG_CMD_DIR_TODEVICE)) { r->cmp.hostStatus = BTSTAT_BADMSG; @@ -880,7 +880,7 @@ pvscsi_on_cmd_reset_device(PVSCSIState *s) if (sdev != NULL) { s->resetting++; - device_legacy_reset(&sdev->qdev); + device_cold_reset(&sdev->qdev); s->resetting--; return PVSCSI_COMMAND_PROCESSING_SUCCEEDED; } @@ -894,7 +894,7 @@ pvscsi_on_cmd_reset_bus(PVSCSIState *s) trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_RESET_BUS"); s->resetting++; - qbus_reset_all(BUS(&s->bus)); + bus_cold_reset(BUS(&s->bus)); s->resetting--; return PVSCSI_COMMAND_PROCESSING_SUCCEEDED; } @@ -1184,7 +1184,8 @@ pvscsi_realizefn(PCIDevice *pci_dev, Error **errp) pcie_endpoint_cap_init(pci_dev, PVSCSI_EXP_EP_OFFSET); } - s->completion_worker = qemu_bh_new(pvscsi_process_completion_queue, s); + s->completion_worker = qemu_bh_new_guarded(pvscsi_process_completion_queue, s, + &DEVICE(pci_dev)->mem_reentrancy_guard); scsi_bus_init(&s->bus, sizeof(s->bus), DEVICE(pci_dev), &pvscsi_scsi_info); /* override default SCSI bus hotplug-handler, with pvscsi's one */ @@ -1248,7 +1249,7 @@ static bool pvscsi_vmstate_test_pci_device(void *opaque, int version_id) static const VMStateDescription vmstate_pvscsi_pcie_device = { .name = "pvscsi/pcie", .needed = pvscsi_vmstate_need_pcie_device, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PVSCSIState), VMSTATE_END_OF_LIST() } @@ -1260,7 +1261,7 @@ static const VMStateDescription vmstate_pvscsi = { .minimum_version_id = 0, .pre_save = pvscsi_pre_save, .post_load = pvscsi_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_TEST(parent_obj, PVSCSIState, pvscsi_vmstate_test_pci_device, 0, vmstate_pci_device, PCIDevice), @@ -1289,7 +1290,7 @@ static const VMStateDescription vmstate_pvscsi = { VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_pvscsi_pcie_device, NULL } diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c index 455d6eabf6..a1b7230633 100644 --- a/hw/sd/allwinner-sdhost.c +++ b/hw/sd/allwinner-sdhost.c @@ -65,7 +65,7 @@ enum { 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_THLDC = 0x100, /* Card Threshold Control / FIFO (sun4i only)*/ 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 */ @@ -77,6 +77,7 @@ enum { 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_SAMP_DL = 0x144, /* Sample Delay Control (sun50i-a64) */ REG_SD_FIFO = 0x200, /* Read/Write FIFO */ }; @@ -158,6 +159,7 @@ enum { REG_SD_RES_CRC_RST = 0x0, REG_SD_DATA_CRC_RST = 0x0, REG_SD_CRC_STA_RST = 0x0, + REG_SD_SAMPLE_DL_RST = 0x00002000, REG_SD_FIFO_RST = 0x0, }; @@ -191,7 +193,7 @@ static void allwinner_sdhost_update_irq(AwSdHostState *s) } trace_allwinner_sdhost_update_irq(irq); - qemu_set_irq(s->irq, irq); + qemu_set_irq(s->irq, !!irq); } static void allwinner_sdhost_update_transfer_cnt(AwSdHostState *s, @@ -302,6 +304,30 @@ static void allwinner_sdhost_auto_stop(AwSdHostState *s) } } +static void read_descriptor(AwSdHostState *s, hwaddr desc_addr, + TransferDescriptor *desc) +{ + uint32_t desc_words[4]; + dma_memory_read(&s->dma_as, desc_addr, &desc_words, sizeof(desc_words), + MEMTXATTRS_UNSPECIFIED); + desc->status = le32_to_cpu(desc_words[0]); + desc->size = le32_to_cpu(desc_words[1]); + desc->addr = le32_to_cpu(desc_words[2]); + desc->next = le32_to_cpu(desc_words[3]); +} + +static void write_descriptor(AwSdHostState *s, hwaddr desc_addr, + const TransferDescriptor *desc) +{ + uint32_t desc_words[4]; + desc_words[0] = cpu_to_le32(desc->status); + desc_words[1] = cpu_to_le32(desc->size); + desc_words[2] = cpu_to_le32(desc->addr); + desc_words[3] = cpu_to_le32(desc->next); + dma_memory_write(&s->dma_as, desc_addr, &desc_words, sizeof(desc_words), + MEMTXATTRS_UNSPECIFIED); +} + static uint32_t allwinner_sdhost_process_desc(AwSdHostState *s, hwaddr desc_addr, TransferDescriptor *desc, @@ -312,9 +338,7 @@ static uint32_t allwinner_sdhost_process_desc(AwSdHostState *s, uint32_t num_bytes = max_bytes; uint8_t buf[1024]; - /* Read descriptor */ - dma_memory_read(&s->dma_as, desc_addr, desc, sizeof(*desc), - MEMTXATTRS_UNSPECIFIED); + read_descriptor(s, desc_addr, desc); if (desc->size == 0) { desc->size = klass->max_desc_size; } else if (desc->size > klass->max_desc_size) { @@ -356,8 +380,7 @@ static uint32_t allwinner_sdhost_process_desc(AwSdHostState *s, /* Clear hold flag and flush descriptor */ desc->status &= ~DESC_STATUS_HOLD; - dma_memory_write(&s->dma_as, desc_addr, desc, sizeof(*desc), - MEMTXATTRS_UNSPECIFIED); + write_descriptor(s, desc_addr, desc); return num_done; } @@ -415,10 +438,30 @@ static void allwinner_sdhost_dma(AwSdHostState *s) } } +static uint32_t allwinner_sdhost_fifo_read(AwSdHostState *s) +{ + uint32_t res = 0; + + if (sdbus_data_ready(&s->sdbus)) { + sdbus_read_data(&s->sdbus, &res, sizeof(uint32_t)); + le32_to_cpus(&res); + 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__); + } + + return res; +} + static uint64_t allwinner_sdhost_read(void *opaque, hwaddr offset, unsigned size) { AwSdHostState *s = AW_SDHOST(opaque); + AwSdHostClass *sc = AW_SDHOST_GET_CLASS(s); + bool out_of_bounds = false; uint32_t res = 0; switch (offset) { @@ -508,8 +551,12 @@ static uint64_t allwinner_sdhost_read(void *opaque, hwaddr offset, 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; + case REG_SD_THLDC: /* Card Threshold Control or FIFO register (sun4i) */ + if (sc->is_sun4i) { + res = allwinner_sdhost_fifo_read(s); + } else { + res = s->card_threshold; + } break; case REG_SD_DSBD: /* eMMC DDR Start Bit Detection Control */ res = s->startbit_detect; @@ -531,33 +578,45 @@ static uint64_t allwinner_sdhost_read(void *opaque, hwaddr offset, res = s->status_crc; break; case REG_SD_FIFO: /* Read/Write FIFO */ - if (sdbus_data_ready(&s->sdbus)) { - sdbus_read_data(&s->sdbus, &res, sizeof(uint32_t)); - le32_to_cpus(&res); - allwinner_sdhost_update_transfer_cnt(s, sizeof(uint32_t)); - allwinner_sdhost_auto_stop(s); - allwinner_sdhost_update_irq(s); + res = allwinner_sdhost_fifo_read(s); + break; + case REG_SD_SAMP_DL: /* Sample Delay */ + if (sc->can_calibrate) { + res = s->sample_delay; } else { - qemu_log_mask(LOG_GUEST_ERROR, "%s: no data ready on SD bus\n", - __func__); + out_of_bounds = true; } break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %" - HWADDR_PRIx"\n", __func__, offset); + out_of_bounds = true; res = 0; break; } + if (out_of_bounds) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %" + HWADDR_PRIx"\n", __func__, offset); + } + trace_allwinner_sdhost_read(offset, res, size); return res; } +static void allwinner_sdhost_fifo_write(AwSdHostState *s, uint64_t value) +{ + uint32_t u32 = cpu_to_le32(value); + sdbus_write_data(&s->sdbus, &u32, sizeof(u32)); + allwinner_sdhost_update_transfer_cnt(s, sizeof(u32)); + allwinner_sdhost_auto_stop(s); + allwinner_sdhost_update_irq(s); +} + static void allwinner_sdhost_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { AwSdHostState *s = AW_SDHOST(opaque); - uint32_t u32; + AwSdHostClass *sc = AW_SDHOST_GET_CLASS(s); + bool out_of_bounds = false; trace_allwinner_sdhost_write(offset, value, size); @@ -657,18 +716,18 @@ static void allwinner_sdhost_write(void *opaque, hwaddr offset, s->dmac_irq = value; allwinner_sdhost_update_irq(s); break; - case REG_SD_THLDC: /* Card Threshold Control */ - s->card_threshold = value; + case REG_SD_THLDC: /* Card Threshold Control or FIFO (sun4i) */ + if (sc->is_sun4i) { + allwinner_sdhost_fifo_write(s, value); + } else { + 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 */ - u32 = cpu_to_le32(value); - sdbus_write_data(&s->sdbus, &u32, sizeof(u32)); - allwinner_sdhost_update_transfer_cnt(s, sizeof(u32)); - allwinner_sdhost_auto_stop(s); - allwinner_sdhost_update_irq(s); + allwinner_sdhost_fifo_write(s, value); break; case REG_SD_RES_CRC: /* Response CRC from card/eMMC */ case REG_SD_DATA7_CRC: /* CRC Data 7 from card/eMMC */ @@ -681,10 +740,21 @@ static void allwinner_sdhost_write(void *opaque, hwaddr offset, 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; + case REG_SD_SAMP_DL: /* Sample delay control */ + if (sc->can_calibrate) { + s->sample_delay = value; + } else { + out_of_bounds = true; + } + break; default: + out_of_bounds = true; + break; + } + + if (out_of_bounds) { qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %" HWADDR_PRIx"\n", __func__, offset); - break; } } @@ -703,7 +773,7 @@ static const VMStateDescription vmstate_allwinner_sdhost = { .name = "allwinner-sdhost", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(global_ctl, AwSdHostState), VMSTATE_UINT32(clock_ctl, AwSdHostState), VMSTATE_UINT32(timeout, AwSdHostState), @@ -733,6 +803,7 @@ static const VMStateDescription vmstate_allwinner_sdhost = { VMSTATE_UINT32(response_crc, AwSdHostState), VMSTATE_UINT32_ARRAY(data_crc, AwSdHostState, 8), VMSTATE_UINT32(status_crc, AwSdHostState), + VMSTATE_UINT32(sample_delay, AwSdHostState), VMSTATE_END_OF_LIST() } }; @@ -771,6 +842,7 @@ static void allwinner_sdhost_realize(DeviceState *dev, Error **errp) static void allwinner_sdhost_reset(DeviceState *dev) { AwSdHostState *s = AW_SDHOST(dev); + AwSdHostClass *sc = AW_SDHOST_GET_CLASS(s); s->global_ctl = REG_SD_GCTL_RST; s->clock_ctl = REG_SD_CKCR_RST; @@ -811,6 +883,10 @@ static void allwinner_sdhost_reset(DeviceState *dev) } s->status_crc = REG_SD_CRC_STA_RST; + + if (sc->can_calibrate) { + s->sample_delay = REG_SD_SAMPLE_DL_RST; + } } static void allwinner_sdhost_bus_class_init(ObjectClass *klass, void *data) @@ -834,12 +910,34 @@ static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, void *data) { AwSdHostClass *sc = AW_SDHOST_CLASS(klass); sc->max_desc_size = 8 * KiB; + sc->is_sun4i = true; + sc->can_calibrate = false; } static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data) { AwSdHostClass *sc = AW_SDHOST_CLASS(klass); sc->max_desc_size = 64 * KiB; + sc->is_sun4i = false; + sc->can_calibrate = false; +} + +static void allwinner_sdhost_sun50i_a64_class_init(ObjectClass *klass, + void *data) +{ + AwSdHostClass *sc = AW_SDHOST_CLASS(klass); + sc->max_desc_size = 64 * KiB; + sc->is_sun4i = false; + sc->can_calibrate = true; +} + +static void allwinner_sdhost_sun50i_a64_emmc_class_init(ObjectClass *klass, + void *data) +{ + AwSdHostClass *sc = AW_SDHOST_CLASS(klass); + sc->max_desc_size = 8 * KiB; + sc->is_sun4i = false; + sc->can_calibrate = true; } static const TypeInfo allwinner_sdhost_info = { @@ -864,6 +962,18 @@ static const TypeInfo allwinner_sdhost_sun5i_info = { .class_init = allwinner_sdhost_sun5i_class_init, }; +static const TypeInfo allwinner_sdhost_sun50i_a64_info = { + .name = TYPE_AW_SDHOST_SUN50I_A64, + .parent = TYPE_AW_SDHOST, + .class_init = allwinner_sdhost_sun50i_a64_class_init, +}; + +static const TypeInfo allwinner_sdhost_sun50i_a64_emmc_info = { + .name = TYPE_AW_SDHOST_SUN50I_A64_EMMC, + .parent = TYPE_AW_SDHOST, + .class_init = allwinner_sdhost_sun50i_a64_emmc_class_init, +}; + static const TypeInfo allwinner_sdhost_bus_info = { .name = TYPE_AW_SDHOST_BUS, .parent = TYPE_SD_BUS, @@ -876,6 +986,8 @@ 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_sun50i_a64_info); + type_register_static(&allwinner_sdhost_sun50i_a64_emmc_info); type_register_static(&allwinner_sdhost_bus_info); } diff --git a/hw/sd/aspeed_sdhci.c b/hw/sd/aspeed_sdhci.c index be8cafd65f..3b63926c3a 100644 --- a/hw/sd/aspeed_sdhci.c +++ b/hw/sd/aspeed_sdhci.c @@ -177,7 +177,7 @@ static void aspeed_sdhci_reset(DeviceState *dev) static const VMStateDescription vmstate_aspeed_sdhci = { .name = TYPE_ASPEED_SDHCI, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedSDHCIState, ASPEED_SDHCI_NUM_REGS), VMSTATE_END_OF_LIST(), }, @@ -198,16 +198,13 @@ static void aspeed_sdhci_class_init(ObjectClass *classp, void *data) device_class_set_props(dc, aspeed_sdhci_properties); } -static const TypeInfo aspeed_sdhci_info = { - .name = TYPE_ASPEED_SDHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(AspeedSDHCIState), - .class_init = aspeed_sdhci_class_init, +static const TypeInfo aspeed_sdhci_types[] = { + { + .name = TYPE_ASPEED_SDHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedSDHCIState), + .class_init = aspeed_sdhci_class_init, + }, }; -static void aspeed_sdhci_register_types(void) -{ - type_register_static(&aspeed_sdhci_info); -} - -type_init(aspeed_sdhci_register_types) +DEFINE_TYPES(aspeed_sdhci_types) diff --git a/hw/sd/bcm2835_sdhost.c b/hw/sd/bcm2835_sdhost.c index 9431c35914..11c54dd4a7 100644 --- a/hw/sd/bcm2835_sdhost.c +++ b/hw/sd/bcm2835_sdhost.c @@ -381,7 +381,7 @@ static const VMStateDescription vmstate_bcm2835_sdhost = { .name = TYPE_BCM2835_SDHOST, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(cmd, BCM2835SDHostState), VMSTATE_UINT32(cmdarg, BCM2835SDHostState), VMSTATE_UINT32(status, BCM2835SDHostState), @@ -436,24 +436,19 @@ static void bcm2835_sdhost_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_bcm2835_sdhost; } -static const TypeInfo bcm2835_sdhost_info = { - .name = TYPE_BCM2835_SDHOST, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(BCM2835SDHostState), - .class_init = bcm2835_sdhost_class_init, - .instance_init = bcm2835_sdhost_init, +static const TypeInfo bcm2835_sdhost_types[] = { + { + .name = TYPE_BCM2835_SDHOST, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835SDHostState), + .class_init = bcm2835_sdhost_class_init, + .instance_init = bcm2835_sdhost_init, + }, + { + .name = TYPE_BCM2835_SDHOST_BUS, + .parent = TYPE_SD_BUS, + .instance_size = sizeof(SDBus), + }, }; -static const TypeInfo bcm2835_sdhost_bus_info = { - .name = TYPE_BCM2835_SDHOST_BUS, - .parent = TYPE_SD_BUS, - .instance_size = sizeof(SDBus), -}; - -static void bcm2835_sdhost_register_types(void) -{ - type_register_static(&bcm2835_sdhost_info); - type_register_static(&bcm2835_sdhost_bus_info); -} - -type_init(bcm2835_sdhost_register_types) +DEFINE_TYPES(bcm2835_sdhost_types) diff --git a/hw/sd/cadence_sdhci.c b/hw/sd/cadence_sdhci.c index 75db34befe..7c8bc5464b 100644 --- a/hw/sd/cadence_sdhci.c +++ b/hw/sd/cadence_sdhci.c @@ -159,7 +159,7 @@ static void cadence_sdhci_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_cadence_sdhci = { .name = TYPE_CADENCE_SDHCI, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, CadenceSDHCIState, CADENCE_SDHCI_NUM_REGS), VMSTATE_END_OF_LIST(), }, @@ -175,17 +175,14 @@ static void cadence_sdhci_class_init(ObjectClass *classp, void *data) dc->vmsd = &vmstate_cadence_sdhci; } -static const TypeInfo cadence_sdhci_info = { - .name = TYPE_CADENCE_SDHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(CadenceSDHCIState), - .instance_init = cadence_sdhci_instance_init, - .class_init = cadence_sdhci_class_init, +static const TypeInfo cadence_sdhci_types[] = { + { + .name = TYPE_CADENCE_SDHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CadenceSDHCIState), + .instance_init = cadence_sdhci_instance_init, + .class_init = cadence_sdhci_class_init, + }, }; -static void cadence_sdhci_register_types(void) -{ - type_register_static(&cadence_sdhci_info); -} - -type_init(cadence_sdhci_register_types) +DEFINE_TYPES(cadence_sdhci_types) diff --git a/hw/sd/core.c b/hw/sd/core.c index 30ee62c510..52d5d90045 100644 --- a/hw/sd/core.c +++ b/hw/sd/core.c @@ -259,16 +259,13 @@ void sdbus_reparent_card(SDBus *from, SDBus *to) sdbus_set_readonly(to, readonly); } -static const TypeInfo sd_bus_info = { - .name = TYPE_SD_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(SDBus), - .class_size = sizeof(SDBusClass), +static const TypeInfo sd_bus_types[] = { + { + .name = TYPE_SD_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(SDBus), + .class_size = sizeof(SDBusClass), + }, }; -static void sd_bus_register_types(void) -{ - type_register_static(&sd_bus_info); -} - -type_init(sd_bus_register_types) +DEFINE_TYPES(sd_bus_types) diff --git a/hw/sd/meson.build b/hw/sd/meson.build index 807ca07b7c..abfac9e461 100644 --- a/hw/sd/meson.build +++ b/hw/sd/meson.build @@ -1,13 +1,13 @@ -softmmu_ss.add(when: 'CONFIG_PL181', if_true: files('pl181.c')) -softmmu_ss.add(when: 'CONFIG_SD', if_true: files('sd.c', 'core.c', 'sdmmc-internal.c')) -softmmu_ss.add(when: 'CONFIG_SDHCI', if_true: files('sdhci.c')) -softmmu_ss.add(when: 'CONFIG_SDHCI_PCI', if_true: files('sdhci-pci.c')) -softmmu_ss.add(when: 'CONFIG_SSI_SD', if_true: files('ssi-sd.c')) +system_ss.add(when: 'CONFIG_PL181', if_true: files('pl181.c')) +system_ss.add(when: 'CONFIG_SD', if_true: files('sd.c', 'core.c', 'sdmmc-internal.c')) +system_ss.add(when: 'CONFIG_SDHCI', if_true: files('sdhci.c')) +system_ss.add(when: 'CONFIG_SDHCI_PCI', if_true: files('sdhci-pci.c')) +system_ss.add(when: 'CONFIG_SSI_SD', if_true: files('ssi-sd.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_mmc.c')) -softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_mmci.c')) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_sdhost.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_sdhci.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sdhost.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_sdhci.c')) -softmmu_ss.add(when: 'CONFIG_CADENCE_SDHCI', if_true: files('cadence_sdhci.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_mmc.c')) +system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_mmci.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_sdhost.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_sdhci.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sdhost.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_sdhci.c')) +system_ss.add(when: 'CONFIG_CADENCE_SDHCI', if_true: files('cadence_sdhci.c')) diff --git a/hw/sd/npcm7xx_sdhci.c b/hw/sd/npcm7xx_sdhci.c index b2f5b4a542..e93dab8dbd 100644 --- a/hw/sd/npcm7xx_sdhci.c +++ b/hw/sd/npcm7xx_sdhci.c @@ -142,7 +142,7 @@ static void npcm7xx_sdhci_reset(DeviceState *dev) static const VMStateDescription vmstate_npcm7xx_sdhci = { .name = TYPE_NPCM7XX_SDHCI, .version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(regs.boottoctrl, NPCM7xxSDHCIState), VMSTATE_END_OF_LIST(), }, @@ -166,17 +166,14 @@ static void npcm7xx_sdhci_instance_init(Object *obj) TYPE_SYSBUS_SDHCI); } -static const TypeInfo npcm7xx_sdhci_info = { - .name = TYPE_NPCM7XX_SDHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(NPCM7xxSDHCIState), - .instance_init = npcm7xx_sdhci_instance_init, - .class_init = npcm7xx_sdhci_class_init, +static const TypeInfo npcm7xx_sdhci_types[] = { + { + .name = TYPE_NPCM7XX_SDHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NPCM7xxSDHCIState), + .instance_init = npcm7xx_sdhci_instance_init, + .class_init = npcm7xx_sdhci_class_init, + }, }; -static void npcm7xx_sdhci_register_types(void) -{ - type_register_static(&npcm7xx_sdhci_info); -} - -type_init(npcm7xx_sdhci_register_types) +DEFINE_TYPES(npcm7xx_sdhci_types) diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index b67def6381..edd3cf2a1e 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -321,11 +321,10 @@ void omap_mmc_reset(struct omap_mmc_s *host) device_cold_reset(DEVICE(host->card)); } -static uint64_t omap_mmc_read(void *opaque, hwaddr offset, - unsigned size) +static uint64_t omap_mmc_read(void *opaque, hwaddr offset, unsigned size) { uint16_t i; - struct omap_mmc_s *s = (struct omap_mmc_s *) opaque; + struct omap_mmc_s *s = opaque; if (size != 2) { return omap_badwidth_read16(opaque, offset); @@ -418,7 +417,7 @@ static void omap_mmc_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { int i; - struct omap_mmc_s *s = (struct omap_mmc_s *) opaque; + struct omap_mmc_s *s = opaque; if (size != 2) { omap_badwidth_write16(opaque, offset, value); @@ -576,7 +575,7 @@ static const MemoryRegionOps omap_mmc_ops = { static void omap_mmc_cover_cb(void *opaque, int line, int level) { - struct omap_mmc_s *host = (struct omap_mmc_s *) opaque; + struct omap_mmc_s *host = opaque; if (!host->cdet_state && level) { host->status |= 0x0002; diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index 5e554bd467..e3633c2e6f 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -63,7 +63,7 @@ static const VMStateDescription vmstate_pl181 = { .name = "pl181", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(clock, PL181State), VMSTATE_UINT32(power, PL181State), VMSTATE_UINT32(cmdarg, PL181State), @@ -519,14 +519,6 @@ static void pl181_class_init(ObjectClass *klass, void *data) k->user_creatable = false; } -static const TypeInfo pl181_info = { - .name = TYPE_PL181, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PL181State), - .instance_init = pl181_init, - .class_init = pl181_class_init, -}; - static void pl181_bus_class_init(ObjectClass *klass, void *data) { SDBusClass *sbc = SD_BUS_CLASS(klass); @@ -535,17 +527,20 @@ static void pl181_bus_class_init(ObjectClass *klass, void *data) sbc->set_readonly = pl181_set_readonly; } -static const TypeInfo pl181_bus_info = { - .name = TYPE_PL181_BUS, - .parent = TYPE_SD_BUS, - .instance_size = sizeof(SDBus), - .class_init = pl181_bus_class_init, +static const TypeInfo pl181_info[] = { + { + .name = TYPE_PL181, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PL181State), + .instance_init = pl181_init, + .class_init = pl181_class_init, + }, + { + .name = TYPE_PL181_BUS, + .parent = TYPE_SD_BUS, + .instance_size = sizeof(SDBus), + .class_init = pl181_bus_class_init, + }, }; -static void pl181_register_types(void) -{ - type_register_static(&pl181_info); - type_register_static(&pl181_bus_info); -} - -type_init(pl181_register_types) +DEFINE_TYPES(pl181_info) diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c index 124fbf8bbd..82529708c8 100644 --- a/hw/sd/pxa2xx_mmci.c +++ b/hw/sd/pxa2xx_mmci.c @@ -84,7 +84,7 @@ static const VMStateDescription vmstate_pxa2xx_mmci = { .name = "pxa2xx-mmci", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(status, PXA2xxMMCIState), VMSTATE_UINT32(clkrt, PXA2xxMMCIState), VMSTATE_UINT32(spi, PXA2xxMMCIState), @@ -479,15 +479,10 @@ PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem, qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma) { DeviceState *dev; - SysBusDevice *sbd; - dev = qdev_new(TYPE_PXA2XX_MMCI); - sbd = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(sbd, 0, base); - sysbus_connect_irq(sbd, 0, irq); + dev = sysbus_create_simple(TYPE_PXA2XX_MMCI, base, irq); qdev_connect_gpio_out_named(dev, "rx-dma", 0, rx_dma); qdev_connect_gpio_out_named(dev, "tx-dma", 0, tx_dma); - sysbus_realize_and_unref(sbd, &error_fatal); return PXA2XX_MMCI(dev); } @@ -580,25 +575,20 @@ static void pxa2xx_mmci_bus_class_init(ObjectClass *klass, void *data) sbc->set_readonly = pxa2xx_mmci_set_readonly; } -static const TypeInfo pxa2xx_mmci_info = { - .name = TYPE_PXA2XX_MMCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxMMCIState), - .instance_init = pxa2xx_mmci_instance_init, - .class_init = pxa2xx_mmci_class_init, +static const TypeInfo pxa2xx_mmci_types[] = { + { + .name = TYPE_PXA2XX_MMCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PXA2xxMMCIState), + .instance_init = pxa2xx_mmci_instance_init, + .class_init = pxa2xx_mmci_class_init, + }, + { + .name = TYPE_PXA2XX_MMCI_BUS, + .parent = TYPE_SD_BUS, + .instance_size = sizeof(SDBus), + .class_init = pxa2xx_mmci_bus_class_init, + }, }; -static const TypeInfo pxa2xx_mmci_bus_info = { - .name = TYPE_PXA2XX_MMCI_BUS, - .parent = TYPE_SD_BUS, - .instance_size = sizeof(SDBus), - .class_init = pxa2xx_mmci_bus_class_init, -}; - -static void pxa2xx_mmci_register_types(void) -{ - type_register_static(&pxa2xx_mmci_info); - type_register_static(&pxa2xx_mmci_bus_info); -} - -type_init(pxa2xx_mmci_register_types) +DEFINE_TYPES(pxa2xx_mmci_types) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 8e6fa09151..807b5d3de3 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -87,6 +87,14 @@ enum SDCardStates { sd_disconnect_state, }; +typedef sd_rsp_type_t (*sd_cmd_handler)(SDState *sd, SDRequest req); + +typedef struct SDProto { + const char *name; + sd_cmd_handler cmd[SDMMC_CMD_MAX]; + sd_cmd_handler acmd[SDMMC_CMD_MAX]; +} SDProto; + struct SDState { DeviceState parent_obj; @@ -107,7 +115,6 @@ struct SDState { uint8_t spec_version; BlockBackend *blk; - bool spi; /* Runtime changeables */ @@ -137,7 +144,6 @@ struct SDState { qemu_irq readonly_cb; qemu_irq inserted_cb; QEMUTimer *ocr_power_timer; - const char *proto_name; bool enable; uint8_t dat_lines; bool cmd_line; @@ -145,6 +151,33 @@ struct SDState { static void sd_realize(DeviceState *dev, Error **errp); +static const struct SDProto *sd_proto(SDState *sd) +{ + SDCardClass *sc = SD_CARD_GET_CLASS(sd); + + return sc->proto; +} + +static const SDProto sd_proto_spi; + +static bool sd_is_spi(SDState *sd) +{ + return sd_proto(sd) == &sd_proto_spi; +} + +static const char *sd_version_str(enum SDPhySpecificationVersion version) +{ + static const char *sdphy_version[] = { + [SD_PHY_SPECv1_10_VERS] = "v1.10", + [SD_PHY_SPECv2_00_VERS] = "v2.00", + [SD_PHY_SPECv3_01_VERS] = "v3.01", + }; + if (version >= ARRAY_SIZE(sdphy_version)) { + return "unsupported version"; + } + return sdphy_version[version]; +} + static const char *sd_state_name(enum SDCardStates state) { static const char *state_name[] = { @@ -309,7 +342,7 @@ static void sd_set_ocr(SDState *sd) /* All voltages OK */ sd->ocr = R_OCR_VDD_VOLTAGE_WIN_HI_MASK; - if (sd->spi) { + if (sd_is_spi(sd)) { /* * We don't need to emulate power up sequence in SPI-mode. * Thus, the card's power up status bit should be set to 1 when reset. @@ -342,39 +375,39 @@ static void sd_set_scr(SDState *sd) sd->scr[7] = 0x00; } -#define MID 0xaa -#define OID "XY" -#define PNM "QEMU!" -#define PRV 0x01 -#define MDT_YR 2006 -#define MDT_MON 2 +#define MID 0xaa +#define OID "XY" +#define PNM "QEMU!" +#define PRV 0x01 +#define MDT_YR 2006 +#define MDT_MON 2 static void sd_set_cid(SDState *sd) { - sd->cid[0] = MID; /* Fake card manufacturer ID (MID) */ - sd->cid[1] = OID[0]; /* OEM/Application ID (OID) */ + sd->cid[0] = MID; /* Fake card manufacturer ID (MID) */ + sd->cid[1] = OID[0]; /* OEM/Application ID (OID) */ sd->cid[2] = OID[1]; - sd->cid[3] = PNM[0]; /* Fake product name (PNM) */ + sd->cid[3] = PNM[0]; /* Fake product name (PNM) */ sd->cid[4] = PNM[1]; sd->cid[5] = PNM[2]; sd->cid[6] = PNM[3]; sd->cid[7] = PNM[4]; - sd->cid[8] = PRV; /* Fake product revision (PRV) */ - sd->cid[9] = 0xde; /* Fake serial number (PSN) */ + sd->cid[8] = PRV; /* Fake product revision (PRV) */ + sd->cid[9] = 0xde; /* Fake serial number (PSN) */ sd->cid[10] = 0xad; sd->cid[11] = 0xbe; sd->cid[12] = 0xef; - sd->cid[13] = 0x00 | /* Manufacture date (MDT) */ + sd->cid[13] = 0x00 | /* Manufacture date (MDT) */ ((MDT_YR - 2000) / 10); sd->cid[14] = ((MDT_YR % 10) << 4) | MDT_MON; sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1; } -#define HWBLOCK_SHIFT 9 /* 512 bytes */ -#define SECTOR_SHIFT 5 /* 16 kilobytes */ -#define WPGROUP_SHIFT 7 /* 2 megs */ -#define CMULT_SHIFT 9 /* 512 times HWBLOCK_SIZE */ -#define WPGROUP_SIZE (1 << (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) +#define HWBLOCK_SHIFT 9 /* 512 bytes */ +#define SECTOR_SHIFT 5 /* 16 kilobytes */ +#define WPGROUP_SHIFT 7 /* 2 megs */ +#define CMULT_SHIFT 9 /* 512 times HWBLOCK_SIZE */ +#define WPGROUP_SIZE (1 << (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) static const uint8_t sd_csd_rw_mask[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -395,31 +428,31 @@ static void sd_set_csd(SDState *sd, uint64_t size) csize = (size >> (CMULT_SHIFT + hwblock_shift)) - 1; if (size <= SDSC_MAX_CAPACITY) { /* Standard Capacity SD */ - sd->csd[0] = 0x00; /* CSD structure */ - sd->csd[1] = 0x26; /* Data read access-time-1 */ - sd->csd[2] = 0x00; /* Data read access-time-2 */ + sd->csd[0] = 0x00; /* CSD structure */ + sd->csd[1] = 0x26; /* Data read access-time-1 */ + sd->csd[2] = 0x00; /* Data read access-time-2 */ sd->csd[3] = 0x32; /* Max. data transfer rate: 25 MHz */ - sd->csd[4] = 0x5f; /* Card Command Classes */ - sd->csd[5] = 0x50 | /* Max. read data block length */ + sd->csd[4] = 0x5f; /* Card Command Classes */ + sd->csd[5] = 0x50 | /* Max. read data block length */ hwblock_shift; - sd->csd[6] = 0xe0 | /* Partial block for read allowed */ + sd->csd[6] = 0xe0 | /* Partial block for read allowed */ ((csize >> 10) & 0x03); - sd->csd[7] = 0x00 | /* Device size */ + sd->csd[7] = 0x00 | /* Device size */ ((csize >> 2) & 0xff); - sd->csd[8] = 0x3f | /* Max. read current */ + sd->csd[8] = 0x3f | /* Max. read current */ ((csize << 6) & 0xc0); - sd->csd[9] = 0xfc | /* Max. write current */ + sd->csd[9] = 0xfc | /* Max. write current */ ((CMULT_SHIFT - 2) >> 1); - sd->csd[10] = 0x40 | /* Erase sector size */ + sd->csd[10] = 0x40 | /* Erase sector size */ (((CMULT_SHIFT - 2) << 7) & 0x80) | (sectsize >> 1); - sd->csd[11] = 0x00 | /* Write protect group size */ + sd->csd[11] = 0x00 | /* Write protect group size */ ((sectsize << 7) & 0x80) | wpsize; - sd->csd[12] = 0x90 | /* Write speed factor */ + sd->csd[12] = 0x90 | /* Write speed factor */ (hwblock_shift >> 2); - sd->csd[13] = 0x20 | /* Max. write data block length */ + sd->csd[13] = 0x20 | /* Max. write data block length */ ((hwblock_shift << 6) & 0xc0); - sd->csd[14] = 0x00; /* File format group */ - } else { /* SDHC */ + sd->csd[14] = 0x00; /* File format group */ + } else { /* SDHC */ size /= 512 * KiB; size -= 1; sd->csd[0] = 0x40; @@ -513,7 +546,7 @@ static int sd_req_crc_validate(SDRequest *req) buffer[0] = 0x40 | req->cmd; stl_be_p(&buffer[1], req->arg); return 0; - return sd_crc7(buffer, 5) != req->crc; /* TODO */ + return sd_crc7(buffer, 5) != req->crc; /* TODO */ } static void sd_response_r1_make(SDState *sd, uint8_t *response) @@ -648,7 +681,7 @@ static const VMStateDescription sd_ocr_vmstate = { .version_id = 1, .minimum_version_id = 1, .needed = sd_ocr_vmstate_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ocr, SDState), VMSTATE_TIMER_PTR(ocr_power_timer, SDState), VMSTATE_END_OF_LIST() @@ -673,7 +706,7 @@ static const VMStateDescription sd_vmstate = { .version_id = 2, .minimum_version_id = 2, .pre_load = sd_vmstate_pre_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(mode, SDState), VMSTATE_INT32(state, SDState), VMSTATE_UINT8_ARRAY(cid, SDState, 16), @@ -700,7 +733,7 @@ static const VMStateDescription sd_vmstate = { VMSTATE_BOOL(enable, SDState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &sd_ocr_vmstate, NULL }, @@ -714,13 +747,12 @@ SDState *sd_init(BlockBackend *blk, bool is_spi) SDState *sd; Error *err = NULL; - obj = object_new(TYPE_SD_CARD); + obj = object_new(is_spi ? TYPE_SD_CARD_SPI : TYPE_SD_CARD); dev = DEVICE(obj); if (!qdev_prop_set_drive_err(dev, "drive", blk, &err)) { error_reportf_err(err, "sd_init failed: "); return NULL; } - qdev_prop_set_bit(dev, "spi", is_spi); /* * Realizing the device properly would put it into the QOM @@ -752,7 +784,7 @@ void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert) static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) { trace_sdcard_read_block(addr, len); - if (!sd->blk || blk_pread(sd->blk, addr, sd->data, len) < 0) { + if (!sd->blk || blk_pread(sd->blk, addr, len, sd->data, 0) < 0) { fprintf(stderr, "sd_blk_read: read error on host side\n"); } } @@ -760,7 +792,7 @@ static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) { trace_sdcard_write_block(addr, len); - if (!sd->blk || blk_pwrite(sd->blk, addr, sd->data, len, 0) < 0) { + if (!sd->blk || blk_pwrite(sd->blk, addr, len, sd->data, 0) < 0) { fprintf(stderr, "sd_blk_write: write error on host side\n"); } } @@ -851,19 +883,19 @@ static void sd_function_switch(SDState *sd, uint32_t arg) int i, mode, new_func; mode = !!(arg & 0x80000000); - sd->data[0] = 0x00; /* Maximum current consumption */ + sd->data[0] = 0x00; /* Maximum current consumption */ sd->data[1] = 0x01; - sd->data[2] = 0x80; /* Supported group 6 functions */ + sd->data[2] = 0x80; /* Supported group 6 functions */ sd->data[3] = 0x01; - sd->data[4] = 0x80; /* Supported group 5 functions */ + sd->data[4] = 0x80; /* Supported group 5 functions */ sd->data[5] = 0x01; - sd->data[6] = 0x80; /* Supported group 4 functions */ + sd->data[6] = 0x80; /* Supported group 4 functions */ sd->data[7] = 0x01; - sd->data[8] = 0x80; /* Supported group 3 functions */ + sd->data[8] = 0x80; /* Supported group 3 functions */ sd->data[9] = 0x01; - sd->data[10] = 0x80; /* Supported group 2 functions */ + sd->data[10] = 0x80; /* Supported group 2 functions */ sd->data[11] = 0x43; - sd->data[12] = 0x80; /* Supported group 1 functions */ + sd->data[12] = 0x80; /* Supported group 1 functions */ sd->data[13] = 0x03; memset(&sd->data[14], 0, 3); @@ -966,6 +998,106 @@ static bool address_in_range(SDState *sd, const char *desc, return true; } +static sd_rsp_type_t sd_invalid_state_for_cmd(SDState *sd, SDRequest req) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: CMD%i in a wrong state: %s (spec %s)\n", + sd_proto(sd)->name, req.cmd, sd_state_name(sd->state), + sd_version_str(sd->spec_version)); + + return sd_illegal; +} + +static sd_rsp_type_t sd_cmd_illegal(SDState *sd, SDRequest req) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown CMD%i for spec %s\n", + sd_proto(sd)->name, req.cmd, + sd_version_str(sd->spec_version)); + + return sd_illegal; +} + +/* Commands that are recognised but not yet implemented. */ +static sd_rsp_type_t sd_cmd_unimplemented(SDState *sd, SDRequest req) +{ + qemu_log_mask(LOG_UNIMP, "%s: CMD%i not implemented\n", + sd_proto(sd)->name, req.cmd); + + return sd_illegal; +} + +static sd_rsp_type_t sd_cmd_GO_IDLE_STATE(SDState *sd, SDRequest req) +{ + if (sd->state != sd_inactive_state) { + sd->state = sd_idle_state; + sd_reset(DEVICE(sd)); + } + + return sd_is_spi(sd) ? sd_r1 : sd_r0; +} + +static sd_rsp_type_t sd_cmd_SEND_OP_CMD(SDState *sd, SDRequest req) +{ + sd->state = sd_transfer_state; + + return sd_r1; +} + +static sd_rsp_type_t sd_cmd_ALL_SEND_CID(SDState *sd, SDRequest req) +{ + if (sd->state != sd_ready_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + sd->state = sd_identification_state; + + return sd_r2_i; +} + +static sd_rsp_type_t sd_cmd_SEND_RELATIVE_ADDR(SDState *sd, SDRequest req) +{ + switch (sd->state) { + case sd_identification_state: + case sd_standby_state: + sd->state = sd_standby_state; + sd_set_rca(sd); + return sd_r6; + + default: + return sd_invalid_state_for_cmd(sd, req); + } +} + +static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) +{ + if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { + return sd_cmd_illegal(sd, req); + } + + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + sd->state = sd_sendingdata_state; + sd->data_offset = 0; + + return sd_r1; +} + +static sd_rsp_type_t sd_cmd_SET_BLOCK_COUNT(SDState *sd, SDRequest req) +{ + if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { + return sd_cmd_illegal(sd, req); + } + + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + sd->multi_blk_cnt = req.arg; + + return sd_r1; +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint32_t rca = 0x0000; @@ -975,7 +1107,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) * However there is no ACMD55, so we want to trace this particular case. */ if (req.cmd != 55 || sd->expecting_acmd) { - trace_sdcard_normal_command(sd->proto_name, + trace_sdcard_normal_command(sd_proto(sd)->name, sd_cmd_name(req.cmd), req.cmd, req.arg, sd_state_name(sd->state)); } @@ -999,58 +1131,13 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) return sd_illegal; } + if (sd_proto(sd)->cmd[req.cmd]) { + return sd_proto(sd)->cmd[req.cmd](sd, req); + } + switch (req.cmd) { /* Basic commands (Class 0 and Class 1) */ - case 0: /* CMD0: GO_IDLE_STATE */ - switch (sd->state) { - case sd_inactive_state: - return sd->spi ? sd_r1 : sd_r0; - - default: - sd->state = sd_idle_state; - sd_reset(DEVICE(sd)); - return sd->spi ? sd_r1 : sd_r0; - } - break; - - case 1: /* CMD1: SEND_OP_CMD */ - if (!sd->spi) - goto bad_cmd; - - sd->state = sd_transfer_state; - return sd_r1; - - case 2: /* CMD2: ALL_SEND_CID */ - if (sd->spi) - goto bad_cmd; - switch (sd->state) { - case sd_ready_state: - sd->state = sd_identification_state; - return sd_r2_i; - - default: - break; - } - break; - - case 3: /* CMD3: SEND_RELATIVE_ADDR */ - if (sd->spi) - goto bad_cmd; - switch (sd->state) { - case sd_identification_state: - case sd_standby_state: - sd->state = sd_standby_state; - sd_set_rca(sd); - return sd_r6; - - default: - break; - } - break; - - case 4: /* CMD4: SEND_DSR */ - if (sd->spi) - goto bad_cmd; + case 4: /* CMD4: SEND_DSR */ switch (sd->state) { case sd_standby_state: break; @@ -1060,10 +1147,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 5: /* CMD5: reserved for SDIO cards */ - return sd_illegal; - - case 6: /* CMD6: SWITCH_FUNCTION */ + case 6: /* CMD6: SWITCH_FUNCTION */ switch (sd->mode) { case sd_data_transfer_mode: sd_function_switch(sd, req.arg); @@ -1077,9 +1161,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 7: /* CMD7: SELECT/DESELECT_CARD */ - if (sd->spi) - goto bad_cmd; + case 7: /* CMD7: SELECT/DESELECT_CARD */ switch (sd->state) { case sd_standby_state: if (sd->rca != rca) @@ -1115,7 +1197,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 8: /* CMD8: SEND_IF_COND */ + case 8: /* CMD8: SEND_IF_COND */ if (sd->spec_version < SD_PHY_SPECv2_00_VERS) { break; } @@ -1126,14 +1208,14 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) /* No response if not exactly one VHS bit is set. */ if (!(req.arg >> 8) || (req.arg >> (ctz32(req.arg & ~0xff) + 1))) { - return sd->spi ? sd_r7 : sd_r0; + return sd_is_spi(sd) ? sd_r7 : sd_r0; } /* Accept. */ sd->vhs = req.arg; return sd_r7; - case 9: /* CMD9: SEND_CSD */ + case 9: /* CMD9: SEND_CSD */ switch (sd->state) { case sd_standby_state: if (sd->rca != rca) @@ -1142,8 +1224,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) return sd_r2_s; case sd_transfer_state: - if (!sd->spi) + if (!sd_is_spi(sd)) { break; + } sd->state = sd_sendingdata_state; memcpy(sd->data, sd->csd, 16); sd->data_start = addr; @@ -1155,7 +1238,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 10: /* CMD10: SEND_CID */ + case 10: /* CMD10: SEND_CID */ switch (sd->state) { case sd_standby_state: if (sd->rca != rca) @@ -1164,8 +1247,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) return sd_r2_i; case sd_transfer_state: - if (!sd->spi) + if (!sd_is_spi(sd)) { break; + } sd->state = sd_sendingdata_state; memcpy(sd->data, sd->cid, 16); sd->data_start = addr; @@ -1177,7 +1261,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 12: /* CMD12: STOP_TRANSMISSION */ + case 12: /* CMD12: STOP_TRANSMISSION */ switch (sd->state) { case sd_sendingdata_state: sd->state = sd_transfer_state; @@ -1194,10 +1278,10 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 13: /* CMD13: SEND_STATUS */ + case 13: /* CMD13: SEND_STATUS */ switch (sd->mode) { case sd_data_transfer_mode: - if (!sd->spi && sd->rca != rca) { + if (!sd_is_spi(sd) && sd->rca != rca) { return sd_r0; } @@ -1208,9 +1292,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 15: /* CMD15: GO_INACTIVE_STATE */ - if (sd->spi) - goto bad_cmd; + case 15: /* CMD15: GO_INACTIVE_STATE */ switch (sd->mode) { case sd_data_transfer_mode: if (sd->rca != rca) @@ -1224,8 +1306,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - /* Block read commands (Classs 2) */ - case 16: /* CMD16: SET_BLOCKLEN */ + /* Block read commands (Class 2) */ + case 16: /* CMD16: SET_BLOCKLEN */ switch (sd->state) { case sd_transfer_state: if (req.arg > (1 << HWBLOCK_SHIFT)) { @@ -1242,8 +1324,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 17: /* CMD17: READ_SINGLE_BLOCK */ - case 18: /* CMD18: READ_MULTIPLE_BLOCK */ + case 17: /* CMD17: READ_SINGLE_BLOCK */ + case 18: /* CMD18: READ_MULTIPLE_BLOCK */ switch (sd->state) { case sd_transfer_state: @@ -1261,34 +1343,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */ - if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { - break; - } - if (sd->state == sd_transfer_state) { - sd->state = sd_sendingdata_state; - sd->data_offset = 0; - return sd_r1; - } - break; - - case 23: /* CMD23: SET_BLOCK_COUNT */ - if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { - break; - } - switch (sd->state) { - case sd_transfer_state: - sd->multi_blk_cnt = req.arg; - return sd_r1; - - default: - break; - } - break; - /* Block write commands (Class 4) */ - case 24: /* CMD24: WRITE_SINGLE_BLOCK */ - case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ + case 24: /* CMD24: WRITE_SINGLE_BLOCK */ + case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ switch (sd->state) { case sd_transfer_state: @@ -1316,9 +1373,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 26: /* CMD26: PROGRAM_CID */ - if (sd->spi) - goto bad_cmd; + case 26: /* CMD26: PROGRAM_CID */ switch (sd->state) { case sd_transfer_state: sd->state = sd_receivingdata_state; @@ -1331,7 +1386,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 27: /* CMD27: PROGRAM_CSD */ + case 27: /* CMD27: PROGRAM_CSD */ switch (sd->state) { case sd_transfer_state: sd->state = sd_receivingdata_state; @@ -1345,7 +1400,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; /* Write protection (Class 6) */ - case 28: /* CMD28: SET_WRITE_PROT */ + case 28: /* CMD28: SET_WRITE_PROT */ if (sd->size > SDSC_MAX_CAPACITY) { return sd_illegal; } @@ -1367,7 +1422,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 29: /* CMD29: CLR_WRITE_PROT */ + case 29: /* CMD29: CLR_WRITE_PROT */ if (sd->size > SDSC_MAX_CAPACITY) { return sd_illegal; } @@ -1389,7 +1444,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 30: /* CMD30: SEND_WRITE_PROT */ + case 30: /* CMD30: SEND_WRITE_PROT */ if (sd->size > SDSC_MAX_CAPACITY) { return sd_illegal; } @@ -1413,7 +1468,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; /* Erase commands (Class 5) */ - case 32: /* CMD32: ERASE_WR_BLK_START */ + case 32: /* CMD32: ERASE_WR_BLK_START */ switch (sd->state) { case sd_transfer_state: sd->erase_start = req.arg; @@ -1424,7 +1479,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 33: /* CMD33: ERASE_WR_BLK_END */ + case 33: /* CMD33: ERASE_WR_BLK_END */ switch (sd->state) { case sd_transfer_state: sd->erase_end = req.arg; @@ -1435,7 +1490,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 38: /* CMD38: ERASE */ + case 38: /* CMD38: ERASE */ switch (sd->state) { case sd_transfer_state: if (sd->csd[14] & 0x30) { @@ -1455,7 +1510,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; /* Lock card commands (Class 7) */ - case 42: /* CMD42: LOCK_UNLOCK */ + case 42: /* CMD42: LOCK_UNLOCK */ switch (sd->state) { case sd_transfer_state: sd->state = sd_receivingdata_state; @@ -1468,17 +1523,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 52 ... 54: - /* CMD52, CMD53, CMD54: reserved for SDIO cards - * (see the SDIO Simplified Specification V2.0) - * Handle as illegal command but do not complain - * on stderr, as some OSes may use these in their - * probing for presence of an SDIO card. - */ - return sd_illegal; - /* Application specific commands (Class 8) */ - case 55: /* CMD55: APP_CMD */ + case 55: /* CMD55: APP_CMD */ switch (sd->state) { case sd_ready_state: case sd_identification_state: @@ -1492,7 +1538,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) default: break; } - if (!sd->spi) { + if (!sd_is_spi(sd)) { if (sd->rca != rca) { return sd_r0; } @@ -1501,7 +1547,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) sd->card_status |= APP_CMD; return sd_r1; - case 56: /* CMD56: GEN_CMD */ + case 56: /* CMD56: GEN_CMD */ switch (sd->state) { case sd_transfer_state: sd->data_offset = 0; @@ -1517,39 +1563,32 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 58: /* CMD58: READ_OCR (SPI) */ - if (!sd->spi) { - goto bad_cmd; - } return sd_r3; case 59: /* CMD59: CRC_ON_OFF (SPI) */ - if (!sd->spi) { - goto bad_cmd; - } return sd_r1; default: - bad_cmd: qemu_log_mask(LOG_GUEST_ERROR, "SD: Unknown CMD%i\n", req.cmd); return sd_illegal; } - qemu_log_mask(LOG_GUEST_ERROR, "SD: CMD%i in a wrong state: %s\n", - req.cmd, sd_state_name(sd->state)); - return sd_illegal; + return sd_invalid_state_for_cmd(sd, req); } static sd_rsp_type_t sd_app_command(SDState *sd, SDRequest req) { - trace_sdcard_app_command(sd->proto_name, sd_acmd_name(req.cmd), + trace_sdcard_app_command(sd_proto(sd)->name, sd_acmd_name(req.cmd), req.cmd, req.arg, sd_state_name(sd->state)); sd->card_status |= APP_CMD; + + if (sd_proto(sd)->acmd[req.cmd]) { + return sd_proto(sd)->acmd[req.cmd](sd, req); + } + switch (req.cmd) { - case 6: /* ACMD6: SET_BUS_WIDTH */ - if (sd->spi) { - goto unimplemented_spi_cmd; - } + case 6: /* ACMD6: SET_BUS_WIDTH */ switch (sd->state) { case sd_transfer_state: sd->sd_status[0] &= 0x3f; @@ -1561,7 +1600,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } break; - case 13: /* ACMD13: SD_STATUS */ + case 13: /* ACMD13: SD_STATUS */ switch (sd->state) { case sd_transfer_state: sd->state = sd_sendingdata_state; @@ -1574,7 +1613,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } break; - case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ + case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ switch (sd->state) { case sd_transfer_state: *(uint32_t *) sd->data = sd->blk_written; @@ -1589,7 +1628,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } break; - case 23: /* ACMD23: SET_WR_BLK_ERASE_COUNT */ + case 23: /* ACMD23: SET_WR_BLK_ERASE_COUNT */ switch (sd->state) { case sd_transfer_state: return sd_r1; @@ -1599,12 +1638,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } break; - case 41: /* ACMD41: SD_APP_OP_COND */ - if (sd->spi) { - /* SEND_OP_CMD */ - sd->state = sd_transfer_state; - return sd_r1; - } + case 41: /* ACMD41: SD_APP_OP_COND */ if (sd->state != sd_idle_state) { break; } @@ -1641,7 +1675,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd, return sd_r3; - case 42: /* ACMD42: SET_CLR_CARD_DETECT */ + case 42: /* ACMD42: SET_CLR_CARD_DETECT */ switch (sd->state) { case sd_transfer_state: /* Bringing in the 50KOhm pull-up resistor... Done. */ @@ -1652,7 +1686,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } break; - case 51: /* ACMD51: SEND_SCR */ + case 51: /* ACMD51: SEND_SCR */ switch (sd->state) { case sd_transfer_state: sd->state = sd_sendingdata_state; @@ -1680,12 +1714,6 @@ static sd_rsp_type_t sd_app_command(SDState *sd, default: /* Fall back to standard commands. */ return sd_normal_command(sd, req); - - unimplemented_spi_cmd: - /* Commands that are recognised but not yet implemented in SPI mode. */ - qemu_log_mask(LOG_UNIMP, "SD: CMD%i not implemented in SPI mode\n", - req.cmd); - return sd_illegal; } qemu_log_mask(LOG_GUEST_ERROR, "SD: ACMD%i in a wrong state\n", req.cmd); @@ -1836,11 +1864,11 @@ void sd_write_byte(SDState *sd, uint8_t value) if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) return; - trace_sdcard_write_data(sd->proto_name, + trace_sdcard_write_data(sd_proto(sd)->name, sd_acmd_name(sd->current_cmd), sd->current_cmd, value); switch (sd->current_cmd) { - case 24: /* CMD24: WRITE_SINGLE_BLOCK */ + case 24: /* CMD24: WRITE_SINGLE_BLOCK */ sd->data[sd->data_offset ++] = value; if (sd->data_offset >= sd->blk_len) { /* TODO: Check CRC before committing */ @@ -1853,7 +1881,7 @@ void sd_write_byte(SDState *sd, uint8_t value) } break; - case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ + case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ if (sd->data_offset == 0) { /* Start of the block - let's check the address is valid */ if (!address_in_range(sd, "WRITE_MULTIPLE_BLOCK", @@ -1890,7 +1918,7 @@ void sd_write_byte(SDState *sd, uint8_t value) } break; - case 26: /* CMD26: PROGRAM_CID */ + case 26: /* CMD26: PROGRAM_CID */ sd->data[sd->data_offset ++] = value; if (sd->data_offset >= sizeof(sd->cid)) { /* TODO: Check CRC before committing */ @@ -1909,7 +1937,7 @@ void sd_write_byte(SDState *sd, uint8_t value) } break; - case 27: /* CMD27: PROGRAM_CSD */ + case 27: /* CMD27: PROGRAM_CSD */ sd->data[sd->data_offset ++] = value; if (sd->data_offset >= sizeof(sd->csd)) { /* TODO: Check CRC before committing */ @@ -1933,7 +1961,7 @@ void sd_write_byte(SDState *sd, uint8_t value) } break; - case 42: /* CMD42: LOCK_UNLOCK */ + case 42: /* CMD42: LOCK_UNLOCK */ sd->data[sd->data_offset ++] = value; if (sd->data_offset >= sd->blk_len) { /* TODO: Check CRC before committing */ @@ -1944,7 +1972,7 @@ void sd_write_byte(SDState *sd, uint8_t value) } break; - case 56: /* CMD56: GEN_CMD */ + case 56: /* CMD56: GEN_CMD */ sd->data[sd->data_offset ++] = value; if (sd->data_offset >= sd->blk_len) { APP_WRITE_BLOCK(sd->data_start, sd->data_offset); @@ -1992,33 +2020,33 @@ uint8_t sd_read_byte(SDState *sd) io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len; - trace_sdcard_read_data(sd->proto_name, + trace_sdcard_read_data(sd_proto(sd)->name, sd_acmd_name(sd->current_cmd), sd->current_cmd, io_len); switch (sd->current_cmd) { - case 6: /* CMD6: SWITCH_FUNCTION */ + case 6: /* CMD6: SWITCH_FUNCTION */ ret = sd->data[sd->data_offset ++]; if (sd->data_offset >= 64) sd->state = sd_transfer_state; break; - case 9: /* CMD9: SEND_CSD */ - case 10: /* CMD10: SEND_CID */ + case 9: /* CMD9: SEND_CSD */ + case 10: /* CMD10: SEND_CID */ ret = sd->data[sd->data_offset ++]; if (sd->data_offset >= 16) sd->state = sd_transfer_state; break; - case 13: /* ACMD13: SD_STATUS */ + case 13: /* ACMD13: SD_STATUS */ ret = sd->sd_status[sd->data_offset ++]; if (sd->data_offset >= sizeof(sd->sd_status)) sd->state = sd_transfer_state; break; - case 17: /* CMD17: READ_SINGLE_BLOCK */ + case 17: /* CMD17: READ_SINGLE_BLOCK */ if (sd->data_offset == 0) BLK_READ_BLOCK(sd->data_start, io_len); ret = sd->data[sd->data_offset ++]; @@ -2027,7 +2055,7 @@ uint8_t sd_read_byte(SDState *sd) sd->state = sd_transfer_state; break; - case 18: /* CMD18: READ_MULTIPLE_BLOCK */ + case 18: /* CMD18: READ_MULTIPLE_BLOCK */ if (sd->data_offset == 0) { if (!address_in_range(sd, "READ_MULTIPLE_BLOCK", sd->data_start, io_len)) { @@ -2058,28 +2086,28 @@ uint8_t sd_read_byte(SDState *sd) ret = sd_tuning_block_pattern[sd->data_offset++]; break; - case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ + case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ ret = sd->data[sd->data_offset ++]; if (sd->data_offset >= 4) sd->state = sd_transfer_state; break; - case 30: /* CMD30: SEND_WRITE_PROT */ + case 30: /* CMD30: SEND_WRITE_PROT */ ret = sd->data[sd->data_offset ++]; if (sd->data_offset >= 4) sd->state = sd_transfer_state; break; - case 51: /* ACMD51: SEND_SCR */ + case 51: /* ACMD51: SEND_SCR */ ret = sd->scr[sd->data_offset ++]; if (sd->data_offset >= sizeof(sd->scr)) sd->state = sd_transfer_state; break; - case 56: /* CMD56: GEN_CMD */ + case 56: /* CMD56: GEN_CMD */ if (sd->data_offset == 0) APP_READ_BLOCK(sd->data_start, sd->blk_len); ret = sd->data[sd->data_offset ++]; @@ -2111,6 +2139,40 @@ void sd_enable(SDState *sd, bool enable) sd->enable = enable; } +static const SDProto sd_proto_spi = { + .name = "SPI", + .cmd = { + [0] = sd_cmd_GO_IDLE_STATE, + [1] = sd_cmd_SEND_OP_CMD, + [2 ... 4] = sd_cmd_illegal, + [5] = sd_cmd_illegal, + [7] = sd_cmd_illegal, + [15] = sd_cmd_illegal, + [26] = sd_cmd_illegal, + [52 ... 54] = sd_cmd_illegal, + }, + .acmd = { + [6] = sd_cmd_unimplemented, + [41] = sd_cmd_SEND_OP_CMD, + }, +}; + +static const SDProto sd_proto_sd = { + .name = "SD", + .cmd = { + [0] = sd_cmd_GO_IDLE_STATE, + [1] = sd_cmd_illegal, + [2] = sd_cmd_ALL_SEND_CID, + [3] = sd_cmd_SEND_RELATIVE_ADDR, + [5] = sd_cmd_illegal, + [19] = sd_cmd_SEND_TUNING_BLOCK, + [23] = sd_cmd_SET_BLOCK_COUNT, + [52 ... 54] = sd_cmd_illegal, + [58] = sd_cmd_illegal, + [59] = sd_cmd_illegal, + }, +}; + static void sd_instance_init(Object *obj) { SDState *sd = SD_CARD(obj); @@ -2131,8 +2193,6 @@ static void sd_realize(DeviceState *dev, Error **errp) SDState *sd = SD_CARD(dev); int ret; - sd->proto_name = sd->spi ? "SPI" : "SD"; - switch (sd->spec_version) { case SD_PHY_SPECv1_10_VERS ... SD_PHY_SPECv3_01_VERS: @@ -2189,7 +2249,6 @@ static Property sd_properties[] = { * whether card should be in SSI or MMC/SD mode. It is also up to the * board to ensure that ssi transfers only occur when the chip select * is asserted. */ - DEFINE_PROP_BOOL("spi", SDState, spi, false), DEFINE_PROP_END_OF_LIST() }; @@ -2216,21 +2275,39 @@ static void sd_class_init(ObjectClass *klass, void *data) sc->enable = sd_enable; sc->get_inserted = sd_get_inserted; sc->get_readonly = sd_get_readonly; + sc->proto = &sd_proto_sd; } -static const TypeInfo sd_info = { - .name = TYPE_SD_CARD, - .parent = TYPE_DEVICE, - .instance_size = sizeof(SDState), - .class_size = sizeof(SDCardClass), - .class_init = sd_class_init, - .instance_init = sd_instance_init, - .instance_finalize = sd_instance_finalize, +/* + * We do not model the chip select pin, so allow the board to select + * whether card should be in SSI or MMC/SD mode. It is also up to the + * board to ensure that ssi transfers only occur when the chip select + * is asserted. + */ +static void sd_spi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SDCardClass *sc = SD_CARD_CLASS(klass); + + dc->desc = "SD SPI"; + sc->proto = &sd_proto_spi; +} + +static const TypeInfo sd_types[] = { + { + .name = TYPE_SD_CARD, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SDState), + .class_size = sizeof(SDCardClass), + .class_init = sd_class_init, + .instance_init = sd_instance_init, + .instance_finalize = sd_instance_finalize, + }, + { + .name = TYPE_SD_CARD_SPI, + .parent = TYPE_SD_CARD, + .class_init = sd_spi_class_init, + }, }; -static void sd_register_types(void) -{ - type_register_static(&sd_info); -} - -type_init(sd_register_types) +DEFINE_TYPES(sd_types) diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h index e8c753d6d1..5f3765f12d 100644 --- a/hw/sd/sdhci-internal.h +++ b/hw/sd/sdhci-internal.h @@ -288,26 +288,6 @@ enum { extern const VMStateDescription sdhci_vmstate; - -#define ESDHC_MIX_CTRL 0x48 - -#define ESDHC_VENDOR_SPEC 0xc0 -#define ESDHC_IMX_FRC_SDCLK_ON (1 << 8) - -#define ESDHC_DLL_CTRL 0x60 - -#define ESDHC_TUNING_CTRL 0xcc -#define ESDHC_TUNE_CTRL_STATUS 0x68 -#define ESDHC_WTMK_LVL 0x44 - -/* Undocumented register used by guests working around erratum ERR004536 */ -#define ESDHC_UNDOCUMENTED_REG27 0x6c - -#define ESDHC_CTRL_4BITBUS (0x1 << 1) -#define ESDHC_CTRL_8BITBUS (0x2 << 1) - -#define ESDHC_PRNSTS_SDSTB (1 << 3) - /* * Default SD/MMC host controller features information, which will be * presented in CAPABILITIES register of generic SD host controller at reset. @@ -328,6 +308,7 @@ extern const VMStateDescription sdhci_vmstate; #define SDHC_CAPAB_REG_DEFAULT 0x057834b4 #define DEFINE_SDHCI_COMMON_PROPERTIES(_state) \ + DEFINE_PROP_UINT8("endianness", _state, endianness, DEVICE_LITTLE_ENDIAN), \ DEFINE_PROP_UINT8("sd-spec-version", _state, sd_spec_version, 2), \ DEFINE_PROP_UINT8("uhs", _state, uhs_mode, UHS_NOT_SUPPORTED), \ DEFINE_PROP_UINT8("vendor", _state, vendor, SDHCI_VENDOR_NONE), \ diff --git a/hw/sd/sdhci-pci.c b/hw/sd/sdhci-pci.c index c737c8b930..9b7bee8b3f 100644 --- a/hw/sd/sdhci-pci.c +++ b/hw/sd/sdhci-pci.c @@ -68,20 +68,17 @@ static void sdhci_pci_class_init(ObjectClass *klass, void *data) sdhci_common_class_init(klass, data); } -static const TypeInfo sdhci_pci_info = { - .name = TYPE_PCI_SDHCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(SDHCIState), - .class_init = sdhci_pci_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, +static const TypeInfo sdhci_pci_types[] = { + { + .name = TYPE_PCI_SDHCI, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(SDHCIState), + .class_init = sdhci_pci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }, }; -static void sdhci_pci_register_type(void) -{ - type_register_static(&sdhci_pci_info); -} - -type_init(sdhci_pci_register_type) +DEFINE_TYPES(sdhci_pci_types) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 0e5e988927..27673e1c70 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -321,6 +321,8 @@ static void sdhci_poweron_reset(DeviceState *dev) static void sdhci_data_transfer(void *opaque); +#define BLOCK_SIZE_MASK (4 * KiB - 1) + static void sdhci_send_command(SDHCIState *s) { SDRequest request; @@ -371,7 +373,8 @@ static void sdhci_send_command(SDHCIState *s) sdhci_update_irq(s); - if (!timeout && s->blksize && (s->cmdreg & SDHC_CMD_DATA_PRESENT)) { + if (!timeout && (s->blksize & BLOCK_SIZE_MASK) && + (s->cmdreg & SDHC_CMD_DATA_PRESENT)) { s->data_count = 0; sdhci_data_transfer(s); } @@ -406,7 +409,6 @@ static void sdhci_end_transfer(SDHCIState *s) /* * Programmed i/o data transfer */ -#define BLOCK_SIZE_MASK (4 * KiB - 1) /* Fill host controller's read buffer with BLKSIZE bytes of data from card */ static void sdhci_read_block_from_card(SDHCIState *s) @@ -471,6 +473,7 @@ static uint32_t sdhci_read_dataport(SDHCIState *s, unsigned size) } for (i = 0; i < size; i++) { + assert(s->data_count < s->buf_maxsz); value |= s->fifo_buffer[s->data_count] << i * 8; s->data_count++; /* check if we've read all valid data (blksize bytes) from buffer */ @@ -559,6 +562,7 @@ static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size) } for (i = 0; i < size; i++) { + assert(s->data_count < s->buf_maxsz); s->fifo_buffer[s->data_count] = value & 0xFF; s->data_count++; value >>= 8; @@ -1154,7 +1158,8 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) s->sdmasysad = (s->sdmasysad & mask) | value; MASKED_WRITE(s->sdmasysad, mask, value); /* Writing to last byte of sdmasysad might trigger transfer */ - if (!(mask & 0xFF000000) && s->blkcnt && s->blksize && + if (!(mask & 0xFF000000) && s->blkcnt && + (s->blksize & BLOCK_SIZE_MASK) && SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA) { if (s->trnmod & SDHC_TRNS_MULTI) { sdhci_sdma_transfer_multi_blocks(s); @@ -1168,7 +1173,11 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) if (!TRANSFERRING_DATA(s->prnsts)) { uint16_t blksize = s->blksize; - MASKED_WRITE(s->blksize, mask, extract32(value, 0, 12)); + /* + * [14:12] SDMA Buffer Boundary + * [11:00] Transfer Block Size + */ + MASKED_WRITE(s->blksize, mask, extract32(value, 0, 15)); MASKED_WRITE(s->blkcnt, mask >> 16, value >> 16); /* Limit block size to the maximum buffer size */ @@ -1201,6 +1210,12 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) if (!(s->capareg & R_SDHC_CAPAB_SDMA_MASK)) { value &= ~SDHC_TRNS_DMA; } + + /* TRNMOD writes are inhibited while Command Inhibit (DAT) is true */ + if (s->prnsts & SDHC_DATA_INHIBIT) { + mask |= 0xffff; + } + MASKED_WRITE(s->trnmod, mask, value & SDHC_TRNMOD_MASK); MASKED_WRITE(s->cmdreg, mask >> 16, value >> 16); @@ -1329,7 +1344,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) value >> shift, value >> shift); } -static const MemoryRegionOps sdhci_mmio_ops = { +static const MemoryRegionOps sdhci_mmio_le_ops = { .read = sdhci_read, .write = sdhci_write, .valid = { @@ -1340,6 +1355,21 @@ static const MemoryRegionOps sdhci_mmio_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static const MemoryRegionOps sdhci_mmio_be_ops = { + .read = sdhci_read, + .write = sdhci_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + .unaligned = false + }, + .endianness = DEVICE_BIG_ENDIAN, +}; + static void sdhci_init_readonly_registers(SDHCIState *s, Error **errp) { ERRP_GUARD(); @@ -1368,7 +1398,7 @@ void sdhci_initfn(SDHCIState *s) s->insert_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_raise_insertion_irq, s); s->transfer_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_data_transfer, s); - s->io_ops = &sdhci_mmio_ops; + s->io_ops = &sdhci_mmio_le_ops; } void sdhci_uninitfn(SDHCIState *s) @@ -1384,10 +1414,27 @@ void sdhci_common_realize(SDHCIState *s, Error **errp) { ERRP_GUARD(); + switch (s->endianness) { + case DEVICE_LITTLE_ENDIAN: + /* s->io_ops is little endian by default */ + break; + case DEVICE_BIG_ENDIAN: + if (s->io_ops != &sdhci_mmio_le_ops) { + error_setg(errp, "SD controller doesn't support big endianness"); + return; + } + s->io_ops = &sdhci_mmio_be_ops; + break; + default: + error_setg(errp, "Incorrect endianness"); + return; + } + sdhci_init_readonly_registers(s, errp); if (*errp) { return; } + s->buf_maxsz = sdhci_get_fifolen(s); s->fifo_buffer = g_malloc0(s->buf_maxsz); @@ -1418,7 +1465,7 @@ static const VMStateDescription sdhci_pending_insert_vmstate = { .version_id = 1, .minimum_version_id = 1, .needed = sdhci_pending_insert_vmstate_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(pending_insert_state, SDHCIState), VMSTATE_END_OF_LIST() }, @@ -1428,7 +1475,7 @@ const VMStateDescription sdhci_vmstate = { .name = "sdhci", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(sdmasysad, SDHCIState), VMSTATE_UINT16(blksize, SDHCIState), VMSTATE_UINT16(blkcnt, SDHCIState), @@ -1459,7 +1506,7 @@ const VMStateDescription sdhci_vmstate = { VMSTATE_TIMER_PTR(transfer_timer, SDHCIState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &sdhci_pending_insert_vmstate, NULL }, @@ -1577,6 +1624,25 @@ static const TypeInfo sdhci_bus_info = { /* --- qdev i.MX eSDHC --- */ +#define USDHC_MIX_CTRL 0x48 + +#define USDHC_VENDOR_SPEC 0xc0 +#define USDHC_IMX_FRC_SDCLK_ON (1 << 8) + +#define USDHC_DLL_CTRL 0x60 + +#define USDHC_TUNING_CTRL 0xcc +#define USDHC_TUNE_CTRL_STATUS 0x68 +#define USDHC_WTMK_LVL 0x44 + +/* Undocumented register used by guests working around erratum ERR004536 */ +#define USDHC_UNDOCUMENTED_REG27 0x6c + +#define USDHC_CTRL_4BITBUS (0x1 << 1) +#define USDHC_CTRL_8BITBUS (0x2 << 1) + +#define USDHC_PRNSTS_SDSTB (1 << 3) + static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size) { SDHCIState *s = SYSBUS_SDHCI(opaque); @@ -1596,11 +1662,11 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size) hostctl1 = SDHC_DMA_TYPE(s->hostctl1) << (8 - 3); if (s->hostctl1 & SDHC_CTRL_8BITBUS) { - hostctl1 |= ESDHC_CTRL_8BITBUS; + hostctl1 |= USDHC_CTRL_8BITBUS; } if (s->hostctl1 & SDHC_CTRL_4BITBUS) { - hostctl1 |= ESDHC_CTRL_4BITBUS; + hostctl1 |= USDHC_CTRL_4BITBUS; } ret = hostctl1; @@ -1611,21 +1677,21 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size) case SDHC_PRNSTS: /* Add SDSTB (SD Clock Stable) bit to PRNSTS */ - ret = sdhci_read(opaque, offset, size) & ~ESDHC_PRNSTS_SDSTB; + ret = sdhci_read(opaque, offset, size) & ~USDHC_PRNSTS_SDSTB; if (s->clkcon & SDHC_CLOCK_INT_STABLE) { - ret |= ESDHC_PRNSTS_SDSTB; + ret |= USDHC_PRNSTS_SDSTB; } break; - case ESDHC_VENDOR_SPEC: + case USDHC_VENDOR_SPEC: ret = s->vendor_spec; break; - case ESDHC_DLL_CTRL: - case ESDHC_TUNE_CTRL_STATUS: - case ESDHC_UNDOCUMENTED_REG27: - case ESDHC_TUNING_CTRL: - case ESDHC_MIX_CTRL: - case ESDHC_WTMK_LVL: + case USDHC_DLL_CTRL: + case USDHC_TUNE_CTRL_STATUS: + case USDHC_UNDOCUMENTED_REG27: + case USDHC_TUNING_CTRL: + case USDHC_MIX_CTRL: + case USDHC_WTMK_LVL: ret = 0; break; } @@ -1641,18 +1707,18 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) uint32_t value = (uint32_t)val; switch (offset) { - case ESDHC_DLL_CTRL: - case ESDHC_TUNE_CTRL_STATUS: - case ESDHC_UNDOCUMENTED_REG27: - case ESDHC_TUNING_CTRL: - case ESDHC_WTMK_LVL: + case USDHC_DLL_CTRL: + case USDHC_TUNE_CTRL_STATUS: + case USDHC_UNDOCUMENTED_REG27: + case USDHC_TUNING_CTRL: + case USDHC_WTMK_LVL: break; - case ESDHC_VENDOR_SPEC: + case USDHC_VENDOR_SPEC: s->vendor_spec = value; switch (s->vendor) { case SDHCI_VENDOR_IMX: - if (value & ESDHC_IMX_FRC_SDCLK_ON) { + if (value & USDHC_IMX_FRC_SDCLK_ON) { s->prnsts &= ~SDHC_IMX_CLOCK_GATE_OFF; } else { s->prnsts |= SDHC_IMX_CLOCK_GATE_OFF; @@ -1721,12 +1787,12 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) * Second, split "Data Transfer Width" from bits 2 and 1 in to * bits 5 and 1 */ - if (value & ESDHC_CTRL_8BITBUS) { + if (value & USDHC_CTRL_8BITBUS) { hostctl1 |= SDHC_CTRL_8BITBUS; } - if (value & ESDHC_CTRL_4BITBUS) { - hostctl1 |= ESDHC_CTRL_4BITBUS; + if (value & USDHC_CTRL_4BITBUS) { + hostctl1 |= USDHC_CTRL_4BITBUS; } /* @@ -1749,7 +1815,7 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) sdhci_write(opaque, offset, value, size); break; - case ESDHC_MIX_CTRL: + case USDHC_MIX_CTRL: /* * So, when SD/MMC stack in Linux tries to write to "Transfer * Mode Register", ESDHC i.MX quirk code will translate it @@ -1760,7 +1826,7 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) * on i.MX, but since it is not used by QEMU we do not care. * * We don't want to call sdhci_write(.., SDHC_TRNMOD, ...) - * here becuase it will result in a call to + * here because it will result in a call to * sdhci_send_command(s) which we don't want. * */ diff --git a/hw/sd/sdmmc-internal.c b/hw/sd/sdmmc-internal.c index 2053def3f1..8648a7808d 100644 --- a/hw/sd/sdmmc-internal.c +++ b/hw/sd/sdmmc-internal.c @@ -14,7 +14,7 @@ const char *sd_cmd_name(uint8_t cmd) { static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [0] = "GO_IDLE_STATE", + [0] = "GO_IDLE_STATE", [1] = "SEND_OP_CMD", [2] = "ALL_SEND_CID", [3] = "SEND_RELATIVE_ADDR", [4] = "SET_DSR", [5] = "IO_SEND_OP_COND", [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD", diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 167c03b780..2dd070f978 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -350,7 +350,7 @@ static const VMStateDescription vmstate_ssi_sd = { .version_id = 7, .minimum_version_id = 7, .post_load = ssi_sd_post_load, - .fields = (VMStateField []) { + .fields = (const VMStateField []) { VMSTATE_UINT32(mode, ssi_sd_state), VMSTATE_INT32(cmd, ssi_sd_state), VMSTATE_UINT8_ARRAY(cmdarg, ssi_sd_state, 4), @@ -403,16 +403,13 @@ static void ssi_sd_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; } -static const TypeInfo ssi_sd_info = { - .name = TYPE_SSI_SD, - .parent = TYPE_SSI_PERIPHERAL, - .instance_size = sizeof(ssi_sd_state), - .class_init = ssi_sd_class_init, +static const TypeInfo ssi_sd_types[] = { + { + .name = TYPE_SSI_SD, + .parent = TYPE_SSI_PERIPHERAL, + .instance_size = sizeof(ssi_sd_state), + .class_init = ssi_sd_class_init, + }, }; -static void ssi_sd_register_types(void) -{ - type_register_static(&ssi_sd_info); -} - -type_init(ssi_sd_register_types) +DEFINE_TYPES(ssi_sd_types) diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig index df392e7869..bc6331b4ab 100644 --- a/hw/sensor/Kconfig +++ b/hw/sensor/Kconfig @@ -22,6 +22,11 @@ config ADM1272 bool depends on I2C +config ADM1266 + bool + depends on PMBUS + default y if PMBUS + config MAX34451 bool depends on I2C @@ -34,3 +39,7 @@ config LSM303DLHC_MAG config ISL_PMBUS_VR bool depends on PMBUS + +config MAX31785 + bool + depends on PMBUS diff --git a/hw/sensor/adm1266.c b/hw/sensor/adm1266.c new file mode 100644 index 0000000000..5454b73a63 --- /dev/null +++ b/hw/sensor/adm1266.c @@ -0,0 +1,254 @@ +/* + * Analog Devices ADM1266 Cascadable Super Sequencer with Margin Control and + * Fault Recording with PMBus + * + * https://www.analog.com/media/en/technical-documentation/data-sheets/adm1266.pdf + * + * Copyright 2023 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/i2c/pmbus_device.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qemu/log.h" +#include "qemu/module.h" + +#define TYPE_ADM1266 "adm1266" +OBJECT_DECLARE_SIMPLE_TYPE(ADM1266State, ADM1266) + +#define ADM1266_BLACKBOX_CONFIG 0xD3 +#define ADM1266_PDIO_CONFIG 0xD4 +#define ADM1266_READ_STATE 0xD9 +#define ADM1266_READ_BLACKBOX 0xDE +#define ADM1266_SET_RTC 0xDF +#define ADM1266_GPIO_SYNC_CONFIGURATION 0xE1 +#define ADM1266_BLACKBOX_INFORMATION 0xE6 +#define ADM1266_PDIO_STATUS 0xE9 +#define ADM1266_GPIO_STATUS 0xEA + +/* Defaults */ +#define ADM1266_OPERATION_DEFAULT 0x80 +#define ADM1266_CAPABILITY_DEFAULT 0xA0 +#define ADM1266_CAPABILITY_NO_PEC 0x20 +#define ADM1266_PMBUS_REVISION_DEFAULT 0x22 +#define ADM1266_MFR_ID_DEFAULT "ADI" +#define ADM1266_MFR_ID_DEFAULT_LEN 32 +#define ADM1266_MFR_MODEL_DEFAULT "ADM1266-A1" +#define ADM1266_MFR_MODEL_DEFAULT_LEN 32 +#define ADM1266_MFR_REVISION_DEFAULT "25" +#define ADM1266_MFR_REVISION_DEFAULT_LEN 8 + +#define ADM1266_NUM_PAGES 17 +/** + * PAGE Index + * Page 0 VH1. + * Page 1 VH2. + * Page 2 VH3. + * Page 3 VH4. + * Page 4 VP1. + * Page 5 VP2. + * Page 6 VP3. + * Page 7 VP4. + * Page 8 VP5. + * Page 9 VP6. + * Page 10 VP7. + * Page 11 VP8. + * Page 12 VP9. + * Page 13 VP10. + * Page 14 VP11. + * Page 15 VP12. + * Page 16 VP13. + */ +typedef struct ADM1266State { + PMBusDevice parent; + + char mfr_id[32]; + char mfr_model[32]; + char mfr_rev[8]; +} ADM1266State; + +static const uint8_t adm1266_ic_device_id[] = {0x03, 0x41, 0x12, 0x66}; +static const uint8_t adm1266_ic_device_rev[] = {0x08, 0x01, 0x08, 0x07, 0x0, + 0x0, 0x07, 0x41, 0x30}; + +static void adm1266_exit_reset(Object *obj) +{ + ADM1266State *s = ADM1266(obj); + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + + pmdev->page = 0; + pmdev->capability = ADM1266_CAPABILITY_NO_PEC; + + for (int i = 0; i < ADM1266_NUM_PAGES; i++) { + pmdev->pages[i].operation = ADM1266_OPERATION_DEFAULT; + pmdev->pages[i].revision = ADM1266_PMBUS_REVISION_DEFAULT; + pmdev->pages[i].vout_mode = 0; + pmdev->pages[i].read_vout = pmbus_data2linear_mode(12, 0); + pmdev->pages[i].vout_margin_high = pmbus_data2linear_mode(15, 0); + pmdev->pages[i].vout_margin_low = pmbus_data2linear_mode(3, 0); + pmdev->pages[i].vout_ov_fault_limit = pmbus_data2linear_mode(16, 0); + pmdev->pages[i].revision = ADM1266_PMBUS_REVISION_DEFAULT; + } + + strncpy(s->mfr_id, ADM1266_MFR_ID_DEFAULT, 4); + strncpy(s->mfr_model, ADM1266_MFR_MODEL_DEFAULT, 11); + strncpy(s->mfr_rev, ADM1266_MFR_REVISION_DEFAULT, 3); +} + +static uint8_t adm1266_read_byte(PMBusDevice *pmdev) +{ + ADM1266State *s = ADM1266(pmdev); + + switch (pmdev->code) { + case PMBUS_MFR_ID: /* R/W block */ + pmbus_send_string(pmdev, s->mfr_id); + break; + + case PMBUS_MFR_MODEL: /* R/W block */ + pmbus_send_string(pmdev, s->mfr_model); + break; + + case PMBUS_MFR_REVISION: /* R/W block */ + pmbus_send_string(pmdev, s->mfr_rev); + break; + + case PMBUS_IC_DEVICE_ID: + pmbus_send(pmdev, adm1266_ic_device_id, sizeof(adm1266_ic_device_id)); + break; + + case PMBUS_IC_DEVICE_REV: + pmbus_send(pmdev, adm1266_ic_device_rev, sizeof(adm1266_ic_device_rev)); + break; + + default: + qemu_log_mask(LOG_UNIMP, + "%s: reading from unimplemented register: 0x%02x\n", + __func__, pmdev->code); + return 0xFF; + } + + return 0; +} + +static int adm1266_write_data(PMBusDevice *pmdev, const uint8_t *buf, + uint8_t len) +{ + ADM1266State *s = ADM1266(pmdev); + + switch (pmdev->code) { + case PMBUS_MFR_ID: /* R/W block */ + pmbus_receive_block(pmdev, (uint8_t *)s->mfr_id, sizeof(s->mfr_id)); + break; + + case PMBUS_MFR_MODEL: /* R/W block */ + pmbus_receive_block(pmdev, (uint8_t *)s->mfr_model, + sizeof(s->mfr_model)); + break; + + case PMBUS_MFR_REVISION: /* R/W block*/ + pmbus_receive_block(pmdev, (uint8_t *)s->mfr_rev, sizeof(s->mfr_rev)); + break; + + case ADM1266_SET_RTC: /* do nothing */ + break; + + default: + qemu_log_mask(LOG_UNIMP, + "%s: writing to unimplemented register: 0x%02x\n", + __func__, pmdev->code); + break; + } + return 0; +} + +static void adm1266_get(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + uint16_t value; + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + PMBusVoutMode *mode = (PMBusVoutMode *)&pmdev->pages[0].vout_mode; + + if (strcmp(name, "vout") == 0) { + value = pmbus_linear_mode2data(*(uint16_t *)opaque, mode->exp); + } else { + value = *(uint16_t *)opaque; + } + + visit_type_uint16(v, name, &value, errp); +} + +static void adm1266_set(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + uint16_t *internal = opaque; + uint16_t value; + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + PMBusVoutMode *mode = (PMBusVoutMode *)&pmdev->pages[0].vout_mode; + + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } + + *internal = pmbus_data2linear_mode(value, mode->exp); + pmbus_check_limits(pmdev); +} + +static const VMStateDescription vmstate_adm1266 = { + .name = "ADM1266", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]){ + VMSTATE_PMBUS_DEVICE(parent, ADM1266State), + VMSTATE_END_OF_LIST() + } +}; + +static void adm1266_init(Object *obj) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + uint64_t flags = PB_HAS_VOUT_MODE | PB_HAS_VOUT | PB_HAS_VOUT_MARGIN | + PB_HAS_VOUT_RATING | PB_HAS_STATUS_MFR_SPECIFIC; + + for (int i = 0; i < ADM1266_NUM_PAGES; i++) { + pmbus_page_config(pmdev, i, flags); + + object_property_add(obj, "vout[*]", "uint16", + adm1266_get, + adm1266_set, NULL, &pmdev->pages[i].read_vout); + } +} + +static void adm1266_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass); + + dc->desc = "Analog Devices ADM1266 Hot Swap controller"; + dc->vmsd = &vmstate_adm1266; + k->write_data = adm1266_write_data; + k->receive_byte = adm1266_read_byte; + k->device_num_pages = 17; + + rc->phases.exit = adm1266_exit_reset; +} + +static const TypeInfo adm1266_info = { + .name = TYPE_ADM1266, + .parent = TYPE_PMBUS_DEVICE, + .instance_size = sizeof(ADM1266State), + .instance_init = adm1266_init, + .class_init = adm1266_class_init, +}; + +static void adm1266_register_types(void) +{ + type_register_static(&adm1266_info); +} + +type_init(adm1266_register_types) diff --git a/hw/sensor/adm1272.c b/hw/sensor/adm1272.c index 7310c769be..1f7c8abb83 100644 --- a/hw/sensor/adm1272.c +++ b/hw/sensor/adm1272.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include #include "hw/i2c/pmbus_device.h" #include "hw/irq.h" #include "migration/vmstate.h" @@ -458,7 +457,7 @@ static const VMStateDescription vmstate_adm1272 = { .name = "ADM1272", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]){ + .fields = (const VMStateField[]){ VMSTATE_PMBUS_DEVICE(parent, ADM1272State), VMSTATE_UINT64(ein_ext, ADM1272State), VMSTATE_UINT32(pin_ext, ADM1272State), diff --git a/hw/sensor/dps310.c b/hw/sensor/dps310.c index d60a18ac41..01c776dd7a 100644 --- a/hw/sensor/dps310.c +++ b/hw/sensor/dps310.c @@ -9,7 +9,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "hw/hw.h" #include "hw/i2c/i2c.h" #include "qapi/error.h" #include "qapi/visitor.h" @@ -189,7 +188,7 @@ static const VMStateDescription vmstate_dps310 = { .name = "DPS310", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(len, DPS310State), VMSTATE_UINT8_ARRAY(regs, DPS310State, NUM_REGISTERS), VMSTATE_UINT8(pointer, DPS310State), diff --git a/hw/sensor/emc141x.c b/hw/sensor/emc141x.c index 7ce8f4e979..95079558e8 100644 --- a/hw/sensor/emc141x.c +++ b/hw/sensor/emc141x.c @@ -228,7 +228,7 @@ static const VMStateDescription vmstate_emc141x = { .name = "EMC141X", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(len, EMC141XState), VMSTATE_UINT8(data, EMC141XState), VMSTATE_UINT8(pointer, EMC141XState), diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c index e11e028884..e51269f6b8 100644 --- a/hw/sensor/isl_pmbus_vr.c +++ b/hw/sensor/isl_pmbus_vr.c @@ -15,6 +15,18 @@ static uint8_t isl_pmbus_vr_read_byte(PMBusDevice *pmdev) { + ISLState *s = ISL69260(pmdev); + + switch (pmdev->code) { + case PMBUS_IC_DEVICE_ID: + if (!s->ic_device_id_len) { + break; + } + pmbus_send(pmdev, s->ic_device_id, s->ic_device_id_len); + pmbus_idle(pmdev); + return 0; + } + qemu_log_mask(LOG_GUEST_ERROR, "%s: reading from unsupported register: 0x%02x\n", __func__, pmdev->code); @@ -89,7 +101,7 @@ static void isl_pmbus_vr_exit_reset(Object *obj) } } -/* The raa228000 uses different direct mode coefficents from most isl devices */ +/* The raa228000 uses different direct mode coefficients from most isl devices */ static void raa228000_exit_reset(Object *obj) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); @@ -107,6 +119,18 @@ static void raa228000_exit_reset(Object *obj) pmdev->pages[0].read_temperature_3 = 0; } +static void isl69259_exit_reset(Object *obj) +{ + ISLState *s = ISL69260(obj); + static const uint8_t ic_device_id[] = {0x04, 0x00, 0x81, 0xD2, 0x49, 0x3c}; + g_assert(sizeof(ic_device_id) <= sizeof(s->ic_device_id)); + + isl_pmbus_vr_exit_reset(obj); + + s->ic_device_id_len = sizeof(ic_device_id); + memcpy(s->ic_device_id, ic_device_id, sizeof(ic_device_id)); +} + static void isl_pmbus_vr_add_props(Object *obj, uint64_t *flags, uint8_t pages) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); @@ -245,6 +269,21 @@ static void raa229004_class_init(ObjectClass *klass, void *data) isl_pmbus_vr_class_init(klass, data, 2); } +static void isl69259_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + dc->desc = "Renesas ISL69259 Digital Multiphase Voltage Regulator"; + rc->phases.exit = isl69259_exit_reset; + isl_pmbus_vr_class_init(klass, data, 2); +} + +static const TypeInfo isl69259_info = { + .name = TYPE_ISL69259, + .parent = TYPE_ISL69260, + .class_init = isl69259_class_init, +}; + static const TypeInfo isl69260_info = { .name = TYPE_ISL69260, .parent = TYPE_PMBUS_DEVICE, @@ -271,6 +310,7 @@ static const TypeInfo raa228000_info = { static void isl_pmbus_vr_register_types(void) { + type_register_static(&isl69259_info); type_register_static(&isl69260_info); type_register_static(&raa228000_info); type_register_static(&raa229004_info); diff --git a/hw/sensor/lsm303dlhc_mag.c b/hw/sensor/lsm303dlhc_mag.c index 4c98ddbf20..343ff98990 100644 --- a/hw/sensor/lsm303dlhc_mag.c +++ b/hw/sensor/lsm303dlhc_mag.c @@ -427,6 +427,8 @@ static int lsm303dlhc_mag_event(I2CSlave *i2c, enum i2c_event event) break; case I2C_NACK: break; + default: + return -1; } s->len = 0; @@ -440,7 +442,7 @@ static const VMStateDescription vmstate_lsm303dlhc_mag = { .name = "LSM303DLHC_MAG", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_I2C_SLAVE(parent_obj, LSM303DLHCMagState), VMSTATE_UINT8(len, LSM303DLHCMagState), diff --git a/hw/sensor/max31785.c b/hw/sensor/max31785.c new file mode 100644 index 0000000000..916ed4d457 --- /dev/null +++ b/hw/sensor/max31785.c @@ -0,0 +1,573 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Maxim MAX31785 PMBus 6-Channel Fan Controller + * + * Datasheet: + * https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf + * + * Copyright(c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "qemu/osdep.h" +#include "hw/i2c/pmbus_device.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qemu/log.h" +#include "qemu/module.h" + +#define TYPE_MAX31785 "max31785" +#define MAX31785(obj) OBJECT_CHECK(MAX31785State, (obj), TYPE_MAX31785) + +/* MAX31785 mfr specific PMBus commands */ +#define MAX31785_MFR_MODE 0xD1 +#define MAX31785_MFR_PSEN_CONFIG 0xD2 +#define MAX31785_MFR_VOUT_PEAK 0xD4 +#define MAX31785_MFR_TEMPERATURE_PEAK 0xD6 +#define MAX31785_MFR_VOUT_MIN 0xD7 +#define MAX31785_MFR_FAULT_RESPONSE 0xD9 +#define MAX31785_MFR_NV_FAULT_LOG 0xDC +#define MAX31785_MFR_TIME_COUNT 0xDD +#define MAX31785_MFR_TEMP_SENSOR_CONFIG 0xF0 +#define MAX31785_MFR_FAN_CONFIG 0xF1 +#define MAX31785_MFR_FAN_LUT 0xF2 +#define MAX31785_MFR_READ_FAN_PWM 0xF3 +#define MAX31785_MFR_FAN_FAULT_LIMIT 0xF5 +#define MAX31785_MFR_FAN_WARN_LIMIT 0xF6 +#define MAX31785_MFR_FAN_RUN_TIME 0xF7 +#define MAX31785_MFR_FAN_PWM_AVG 0xF8 +#define MAX31785_MFR_FAN_PWM2RPM 0xF9 + +/* defaults as per the data sheet */ +#define MAX31785_DEFAULT_CAPABILITY 0x10 +#define MAX31785_DEFAULT_VOUT_MODE 0x40 +#define MAX31785_DEFAULT_VOUT_SCALE_MONITOR 0x7FFF +#define MAX31785_DEFAULT_FAN_COMMAND_1 0x7FFF +#define MAX31785_DEFAULT_OV_FAULT_LIMIT 0x7FFF +#define MAX31785_DEFAULT_OV_WARN_LIMIT 0x7FFF +#define MAX31785_DEFAULT_OT_FAULT_LIMIT 0x7FFF +#define MAX31785_DEFAULT_OT_WARN_LIMIT 0x7FFF +#define MAX31785_DEFAULT_PMBUS_REVISION 0x11 +#define MAX31785_DEFAULT_MFR_ID 0x4D +#define MAX31785_DEFAULT_MFR_MODEL 0x53 +#define MAX31785_DEFAULT_MFR_REVISION 0x3030 +#define MAX31785A_DEFAULT_MFR_REVISION 0x3040 +#define MAX31785B_DEFAULT_MFR_REVISION 0x3061 +#define MAX31785B_DEFAULT_MFR_TEMPERATURE_PEAK 0x8000 +#define MAX31785B_DEFAULT_MFR_VOUT_MIN 0x7FFF +#define MAX31785_DEFAULT_TEXT 0x3130313031303130 + +/* MAX31785 pages */ +#define MAX31785_TOTAL_NUM_PAGES 23 +#define MAX31785_FAN_PAGES 6 +#define MAX31785_MIN_FAN_PAGE 0 +#define MAX31785_MAX_FAN_PAGE 5 +#define MAX31785_MIN_TEMP_PAGE 6 +#define MAX31785_MAX_TEMP_PAGE 16 +#define MAX31785_MIN_ADC_VOLTAGE_PAGE 17 +#define MAX31785_MAX_ADC_VOLTAGE_PAGE 22 + +/* FAN_CONFIG_1_2 */ +#define MAX31785_MFR_FAN_CONFIG 0xF1 +#define MAX31785_FAN_CONFIG_ENABLE BIT(7) +#define MAX31785_FAN_CONFIG_RPM_PWM BIT(6) +#define MAX31785_FAN_CONFIG_PULSE(pulse) (pulse << 4) +#define MAX31785_DEFAULT_FAN_CONFIG_1_2(pulse) \ + (MAX31785_FAN_CONFIG_ENABLE | MAX31785_FAN_CONFIG_PULSE(pulse)) +#define MAX31785_DEFAULT_MFR_FAN_CONFIG 0x0000 + +/* fan speed in RPM */ +#define MAX31785_DEFAULT_FAN_SPEED 0x7fff +#define MAX31785_DEFAULT_FAN_STATUS 0x00 + +#define MAX31785_DEFAULT_FAN_MAX_PWM 0x2710 + +/* + * MAX31785State: + * @code: The command code received + * @page: Each page corresponds to a device monitored by the Max 31785 + * The page register determines the available commands depending on device + * _____________________________________________________________________________ + * | 0 | Fan Connected to PWM0 | + * |_______|___________________________________________________________________| + * | 1 | Fan Connected to PWM1 | + * |_______|___________________________________________________________________| + * | 2 | Fan Connected to PWM2 | + * |_______|___________________________________________________________________| + * | 3 | Fan Connected to PWM3 | + * |_______|___________________________________________________________________| + * | 4 | Fan Connected to PWM4 | + * |_______|___________________________________________________________________| + * | 5 | Fan Connected to PWM5 | + * |_______|___________________________________________________________________| + * | 6 | Remote Thermal Diode Connected to ADC 0 | + * |_______|___________________________________________________________________| + * | 7 | Remote Thermal Diode Connected to ADC 1 | + * |_______|___________________________________________________________________| + * | 8 | Remote Thermal Diode Connected to ADC 2 | + * |_______|___________________________________________________________________| + * | 9 | Remote Thermal Diode Connected to ADC 3 | + * |_______|___________________________________________________________________| + * | 10 | Remote Thermal Diode Connected to ADC 4 | + * |_______|___________________________________________________________________| + * | 11 | Remote Thermal Diode Connected to ADC 5 | + * |_______|___________________________________________________________________| + * | 12 | Internal Temperature Sensor | + * |_______|___________________________________________________________________| + * | 13 | Remote I2C Temperature Sensor with Address 0 | + * |_______|___________________________________________________________________| + * | 14 | Remote I2C Temperature Sensor with Address 1 | + * |_______|___________________________________________________________________| + * | 15 | Remote I2C Temperature Sensor with Address 2 | + * |_______|___________________________________________________________________| + * | 16 | Remote I2C Temperature Sensor with Address 3 | + * |_______|___________________________________________________________________| + * | 17 | Remote I2C Temperature Sensor with Address 4 | + * |_______|___________________________________________________________________| + * | 17 | Remote Voltage Connected to ADC0 | + * |_______|___________________________________________________________________| + * | 18 | Remote Voltage Connected to ADC1 | + * |_______|___________________________________________________________________| + * | 19 | Remote Voltage Connected to ADC2 | + * |_______|___________________________________________________________________| + * | 20 | Remote Voltage Connected to ADC3 | + * |_______|___________________________________________________________________| + * | 21 | Remote Voltage Connected to ADC4 | + * |_______|___________________________________________________________________| + * | 22 | Remote Voltage Connected to ADC5 | + * |_______|___________________________________________________________________| + * |23-254 | Reserved | + * |_______|___________________________________________________________________| + * | 255 | Applies to all pages | + * |_______|___________________________________________________________________| + */ + +/* Place holder to save the max31785 mfr specific registers */ +typedef struct MAX31785State { + PMBusDevice parent; + uint16_t mfr_mode[MAX31785_TOTAL_NUM_PAGES]; + uint16_t vout_peak[MAX31785_TOTAL_NUM_PAGES]; + uint16_t temperature_peak[MAX31785_TOTAL_NUM_PAGES]; + uint16_t vout_min[MAX31785_TOTAL_NUM_PAGES]; + uint8_t fault_response[MAX31785_TOTAL_NUM_PAGES]; + uint32_t time_count[MAX31785_TOTAL_NUM_PAGES]; + uint16_t temp_sensor_config[MAX31785_TOTAL_NUM_PAGES]; + uint16_t fan_config[MAX31785_TOTAL_NUM_PAGES]; + uint16_t read_fan_pwm[MAX31785_TOTAL_NUM_PAGES]; + uint16_t fan_fault_limit[MAX31785_TOTAL_NUM_PAGES]; + uint16_t fan_warn_limit[MAX31785_TOTAL_NUM_PAGES]; + uint16_t fan_run_time[MAX31785_TOTAL_NUM_PAGES]; + uint16_t fan_pwm_avg[MAX31785_TOTAL_NUM_PAGES]; + uint64_t fan_pwm2rpm[MAX31785_TOTAL_NUM_PAGES]; + uint64_t mfr_location; + uint64_t mfr_date; + uint64_t mfr_serial; + uint16_t mfr_revision; +} MAX31785State; + +static uint8_t max31785_read_byte(PMBusDevice *pmdev) +{ + MAX31785State *s = MAX31785(pmdev); + switch (pmdev->code) { + + case PMBUS_FAN_CONFIG_1_2: + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send8(pmdev, pmdev->pages[pmdev->page].fan_config_1_2); + } + break; + + case PMBUS_FAN_COMMAND_1: + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, pmdev->pages[pmdev->page].fan_command_1); + } + break; + + case PMBUS_READ_FAN_SPEED_1: + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, pmdev->pages[pmdev->page].read_fan_speed_1); + } + break; + + case PMBUS_STATUS_FANS_1_2: + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, pmdev->pages[pmdev->page].status_fans_1_2); + } + break; + + case PMBUS_MFR_REVISION: + pmbus_send16(pmdev, MAX31785_DEFAULT_MFR_REVISION); + break; + + case PMBUS_MFR_ID: + pmbus_send8(pmdev, 0x4d); /* Maxim */ + break; + + case PMBUS_MFR_MODEL: + pmbus_send8(pmdev, 0x53); + break; + + case PMBUS_MFR_LOCATION: + pmbus_send64(pmdev, s->mfr_location); + break; + + case PMBUS_MFR_DATE: + pmbus_send64(pmdev, s->mfr_date); + break; + + case PMBUS_MFR_SERIAL: + pmbus_send64(pmdev, s->mfr_serial); + break; + + case MAX31785_MFR_MODE: + pmbus_send16(pmdev, s->mfr_mode[pmdev->page]); + break; + + case MAX31785_MFR_VOUT_PEAK: + if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) && + (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) { + pmbus_send16(pmdev, s->vout_peak[pmdev->page]); + } + break; + + case MAX31785_MFR_TEMPERATURE_PEAK: + if ((pmdev->page >= MAX31785_MIN_TEMP_PAGE) && + (pmdev->page <= MAX31785_MAX_TEMP_PAGE)) { + pmbus_send16(pmdev, s->temperature_peak[pmdev->page]); + } + break; + + case MAX31785_MFR_VOUT_MIN: + if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) && + (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) { + pmbus_send16(pmdev, s->vout_min[pmdev->page]); + } + break; + + case MAX31785_MFR_FAULT_RESPONSE: + pmbus_send8(pmdev, s->fault_response[pmdev->page]); + break; + + case MAX31785_MFR_TIME_COUNT: /* R/W 32 */ + pmbus_send32(pmdev, s->time_count[pmdev->page]); + break; + + case MAX31785_MFR_TEMP_SENSOR_CONFIG: /* R/W 16 */ + if ((pmdev->page >= MAX31785_MIN_TEMP_PAGE) && + (pmdev->page <= MAX31785_MAX_TEMP_PAGE)) { + pmbus_send16(pmdev, s->temp_sensor_config[pmdev->page]); + } + break; + + case MAX31785_MFR_FAN_CONFIG: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, s->fan_config[pmdev->page]); + } + break; + + case MAX31785_MFR_READ_FAN_PWM: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, s->read_fan_pwm[pmdev->page]); + } + break; + + case MAX31785_MFR_FAN_FAULT_LIMIT: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, s->fan_fault_limit[pmdev->page]); + } + break; + + case MAX31785_MFR_FAN_WARN_LIMIT: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, s->fan_warn_limit[pmdev->page]); + } + break; + + case MAX31785_MFR_FAN_RUN_TIME: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, s->fan_run_time[pmdev->page]); + } + break; + + case MAX31785_MFR_FAN_PWM_AVG: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send16(pmdev, s->fan_pwm_avg[pmdev->page]); + } + break; + + case MAX31785_MFR_FAN_PWM2RPM: /* R/W 64 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmbus_send64(pmdev, s->fan_pwm2rpm[pmdev->page]); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: reading from unsupported register: 0x%02x\n", + __func__, pmdev->code); + break; + } + + return 0xFF; +} + +static int max31785_write_data(PMBusDevice *pmdev, const uint8_t *buf, + uint8_t len) +{ + MAX31785State *s = MAX31785(pmdev); + if (len == 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__); + return -1; + } + + pmdev->code = buf[0]; /* PMBus command code */ + + if (len == 1) { + return 0; + } + + /* Exclude command code from buffer */ + buf++; + len--; + + switch (pmdev->code) { + + case PMBUS_FAN_CONFIG_1_2: + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmdev->pages[pmdev->page].fan_config_1_2 = pmbus_receive8(pmdev); + } + break; + + case PMBUS_FAN_COMMAND_1: + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + pmdev->pages[pmdev->page].fan_command_1 = pmbus_receive16(pmdev); + pmdev->pages[pmdev->page].read_fan_speed_1 = + ((MAX31785_DEFAULT_FAN_SPEED / MAX31785_DEFAULT_FAN_MAX_PWM) * + pmdev->pages[pmdev->page].fan_command_1); + } + break; + + case PMBUS_MFR_LOCATION: /* R/W 64 */ + s->mfr_location = pmbus_receive64(pmdev); + break; + + case PMBUS_MFR_DATE: /* R/W 64 */ + s->mfr_date = pmbus_receive64(pmdev); + break; + + case PMBUS_MFR_SERIAL: /* R/W 64 */ + s->mfr_serial = pmbus_receive64(pmdev); + break; + + case MAX31785_MFR_MODE: /* R/W word */ + s->mfr_mode[pmdev->page] = pmbus_receive16(pmdev); + break; + + case MAX31785_MFR_VOUT_PEAK: /* R/W word */ + if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) && + (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) { + s->vout_peak[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_TEMPERATURE_PEAK: /* R/W word */ + if ((pmdev->page >= 6) && (pmdev->page <= 16)) { + s->temperature_peak[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_VOUT_MIN: /* R/W word */ + if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) && + (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) { + s->vout_min[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_FAULT_RESPONSE: /* R/W 8 */ + s->fault_response[pmdev->page] = pmbus_receive8(pmdev); + break; + + case MAX31785_MFR_TIME_COUNT: /* R/W 32 */ + s->time_count[pmdev->page] = pmbus_receive32(pmdev); + break; + + case MAX31785_MFR_TEMP_SENSOR_CONFIG: /* R/W 16 */ + if ((pmdev->page >= MAX31785_MIN_TEMP_PAGE) && + (pmdev->page <= MAX31785_MAX_TEMP_PAGE)) { + s->temp_sensor_config[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_FAN_CONFIG: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + s->fan_config[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_FAN_FAULT_LIMIT: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + s->fan_fault_limit[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_FAN_WARN_LIMIT: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + s->fan_warn_limit[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_FAN_RUN_TIME: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + s->fan_run_time[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_FAN_PWM_AVG: /* R/W 16 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + s->fan_pwm_avg[pmdev->page] = pmbus_receive16(pmdev); + } + break; + + case MAX31785_MFR_FAN_PWM2RPM: /* R/W 64 */ + if (pmdev->page <= MAX31785_MAX_FAN_PAGE) { + s->fan_pwm2rpm[pmdev->page] = pmbus_receive64(pmdev); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: writing to unsupported register: 0x%02x\n", + __func__, pmdev->code); + break; + } + + return 0; +} + +static void max31785_exit_reset(Object *obj) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + MAX31785State *s = MAX31785(obj); + + pmdev->capability = MAX31785_DEFAULT_CAPABILITY; + + for (int i = MAX31785_MIN_FAN_PAGE; i <= MAX31785_MAX_FAN_PAGE; i++) { + pmdev->pages[i].vout_mode = MAX31785_DEFAULT_VOUT_MODE; + pmdev->pages[i].fan_command_1 = MAX31785_DEFAULT_FAN_COMMAND_1; + pmdev->pages[i].revision = MAX31785_DEFAULT_PMBUS_REVISION; + pmdev->pages[i].fan_config_1_2 = MAX31785_DEFAULT_FAN_CONFIG_1_2(0); + pmdev->pages[i].read_fan_speed_1 = MAX31785_DEFAULT_FAN_SPEED; + pmdev->pages[i].status_fans_1_2 = MAX31785_DEFAULT_FAN_STATUS; + } + + for (int i = MAX31785_MIN_TEMP_PAGE; i <= MAX31785_MAX_TEMP_PAGE; i++) { + pmdev->pages[i].vout_mode = MAX31785_DEFAULT_VOUT_MODE; + pmdev->pages[i].revision = MAX31785_DEFAULT_PMBUS_REVISION; + pmdev->pages[i].ot_fault_limit = MAX31785_DEFAULT_OT_FAULT_LIMIT; + pmdev->pages[i].ot_warn_limit = MAX31785_DEFAULT_OT_WARN_LIMIT; + } + + for (int i = MAX31785_MIN_ADC_VOLTAGE_PAGE; + i <= MAX31785_MAX_ADC_VOLTAGE_PAGE; + i++) { + pmdev->pages[i].vout_mode = MAX31785_DEFAULT_VOUT_MODE; + pmdev->pages[i].revision = MAX31785_DEFAULT_PMBUS_REVISION; + pmdev->pages[i].vout_scale_monitor = + MAX31785_DEFAULT_VOUT_SCALE_MONITOR; + pmdev->pages[i].vout_ov_fault_limit = MAX31785_DEFAULT_OV_FAULT_LIMIT; + pmdev->pages[i].vout_ov_warn_limit = MAX31785_DEFAULT_OV_WARN_LIMIT; + } + + s->mfr_location = MAX31785_DEFAULT_TEXT; + s->mfr_date = MAX31785_DEFAULT_TEXT; + s->mfr_serial = MAX31785_DEFAULT_TEXT; +} + +static const VMStateDescription vmstate_max31785 = { + .name = TYPE_MAX31785, + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]){ + VMSTATE_PMBUS_DEVICE(parent, MAX31785State), + VMSTATE_UINT16_ARRAY(mfr_mode, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(vout_peak, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(temperature_peak, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(vout_min, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT8_ARRAY(fault_response, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT32_ARRAY(time_count, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(temp_sensor_config, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(fan_config, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(read_fan_pwm, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(fan_fault_limit, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(fan_warn_limit, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(fan_run_time, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT16_ARRAY(fan_pwm_avg, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT64_ARRAY(fan_pwm2rpm, MAX31785State, + MAX31785_TOTAL_NUM_PAGES), + VMSTATE_UINT64(mfr_location, MAX31785State), + VMSTATE_UINT64(mfr_date, MAX31785State), + VMSTATE_UINT64(mfr_serial, MAX31785State), + VMSTATE_END_OF_LIST() + } +}; + +static void max31785_init(Object *obj) +{ + PMBusDevice *pmdev = PMBUS_DEVICE(obj); + + for (int i = MAX31785_MIN_FAN_PAGE; i <= MAX31785_MAX_FAN_PAGE; i++) { + pmbus_page_config(pmdev, i, PB_HAS_VOUT_MODE); + } + + for (int i = MAX31785_MIN_TEMP_PAGE; i <= MAX31785_MAX_TEMP_PAGE; i++) { + pmbus_page_config(pmdev, i, PB_HAS_VOUT_MODE | PB_HAS_TEMPERATURE); + } + + for (int i = MAX31785_MIN_ADC_VOLTAGE_PAGE; + i <= MAX31785_MAX_ADC_VOLTAGE_PAGE; + i++) { + pmbus_page_config(pmdev, i, PB_HAS_VOUT_MODE | PB_HAS_VOUT | + PB_HAS_VOUT_RATING); + } +} + +static void max31785_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass); + dc->desc = "Maxim MAX31785 6-Channel Fan Controller"; + dc->vmsd = &vmstate_max31785; + k->write_data = max31785_write_data; + k->receive_byte = max31785_read_byte; + k->device_num_pages = MAX31785_TOTAL_NUM_PAGES; + rc->phases.exit = max31785_exit_reset; +} + +static const TypeInfo max31785_info = { + .name = TYPE_MAX31785, + .parent = TYPE_PMBUS_DEVICE, + .instance_size = sizeof(MAX31785State), + .instance_init = max31785_init, + .class_init = max31785_class_init, +}; + +static void max31785_register_types(void) +{ + type_register_static(&max31785_info); +} + +type_init(max31785_register_types) diff --git a/hw/sensor/max34451.c b/hw/sensor/max34451.c index a91d8bd487..031ae53f59 100644 --- a/hw/sensor/max34451.c +++ b/hw/sensor/max34451.c @@ -654,7 +654,7 @@ static const VMStateDescription vmstate_max34451 = { .name = TYPE_MAX34451, .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]){ + .fields = (const VMStateField[]){ VMSTATE_PMBUS_DEVICE(parent, MAX34451State), VMSTATE_UINT16_ARRAY(power_good_on, MAX34451State, MAX34451_NUM_PWR_DEVICES), @@ -734,7 +734,7 @@ static void max34451_init(Object *obj) /* * get and set the temperature of the internal temperature sensor in - * centidegrees Celcius i.e.: 2500 -> 25.00 C, max is 327.67 C + * centidegrees Celsius i.e.: 2500 -> 25.00 C, max is 327.67 C */ for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) { object_property_add(obj, "temperature[*]", "uint16", diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build index 12b6992bc8..420fdc3359 100644 --- a/hw/sensor/meson.build +++ b/hw/sensor/meson.build @@ -1,8 +1,10 @@ -softmmu_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c')) -softmmu_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c')) -softmmu_ss.add(when: 'CONFIG_DPS310', if_true: files('dps310.c')) -softmmu_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c')) -softmmu_ss.add(when: 'CONFIG_ADM1272', if_true: files('adm1272.c')) -softmmu_ss.add(when: 'CONFIG_MAX34451', if_true: files('max34451.c')) -softmmu_ss.add(when: 'CONFIG_LSM303DLHC_MAG', if_true: files('lsm303dlhc_mag.c')) -softmmu_ss.add(when: 'CONFIG_ISL_PMBUS_VR', if_true: files('isl_pmbus_vr.c')) +system_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c')) +system_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c')) +system_ss.add(when: 'CONFIG_DPS310', if_true: files('dps310.c')) +system_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c')) +system_ss.add(when: 'CONFIG_ADM1266', if_true: files('adm1266.c')) +system_ss.add(when: 'CONFIG_ADM1272', if_true: files('adm1272.c')) +system_ss.add(when: 'CONFIG_MAX34451', if_true: files('max34451.c')) +system_ss.add(when: 'CONFIG_LSM303DLHC_MAG', if_true: files('lsm303dlhc_mag.c')) +system_ss.add(when: 'CONFIG_ISL_PMBUS_VR', if_true: files('isl_pmbus_vr.c')) +system_ss.add(when: 'CONFIG_MAX31785', if_true: files('max31785.c')) diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c index 2056449489..a8730d0b7f 100644 --- a/hw/sensor/tmp105.c +++ b/hw/sensor/tmp105.c @@ -238,7 +238,7 @@ static const VMStateDescription vmstate_tmp105_detect_falling = { .version_id = 1, .minimum_version_id = 1, .needed = detect_falling_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(detect_falling, TMP105State), VMSTATE_END_OF_LIST() } @@ -249,7 +249,7 @@ static const VMStateDescription vmstate_tmp105 = { .version_id = 0, .minimum_version_id = 0, .post_load = tmp105_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(len, TMP105State), VMSTATE_UINT8_ARRAY(buf, TMP105State, 2), VMSTATE_UINT8(pointer, TMP105State), @@ -260,7 +260,7 @@ static const VMStateDescription vmstate_tmp105 = { VMSTATE_I2C_SLAVE(i2c, TMP105State), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_tmp105_detect_falling, NULL } diff --git a/hw/sensor/tmp421.c b/hw/sensor/tmp421.c index a3db57dcb5..b6f0b62ab1 100644 --- a/hw/sensor/tmp421.c +++ b/hw/sensor/tmp421.c @@ -290,7 +290,7 @@ static const VMStateDescription vmstate_tmp421 = { .name = "TMP421", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(len, TMP421State), VMSTATE_UINT8_ARRAY(buf, TMP421State, 2), VMSTATE_UINT8(pointer, TMP421State), diff --git a/hw/sh4/Kconfig b/hw/sh4/Kconfig index ab733a3f76..e0c4ecd1a5 100644 --- a/hw/sh4/Kconfig +++ b/hw/sh4/Kconfig @@ -6,7 +6,6 @@ config R2D select I82378 if TEST_DEVICES select IDE_MMIO select PFLASH_CFI02 - select USB_OHCI_PCI select PCI select SM501 select SH7750 diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 39fc4f19d9..e5ac6751bd 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -38,7 +38,7 @@ #include "hw/qdev-properties.h" #include "net/net.h" #include "sh7750_regs.h" -#include "hw/ide.h" +#include "hw/ide/mmio.h" #include "hw/irq.h" #include "hw/loader.h" #include "hw/usb.h" @@ -232,6 +232,7 @@ static void r2d_init(MachineState *machine) const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; + MachineClass *mc = MACHINE_GET_CLASS(machine); SuperHCPU *cpu; CPUSH4State *env; ResetData *reset_info; @@ -239,11 +240,11 @@ static void r2d_init(MachineState *machine) MemoryRegion *sdram = g_new(MemoryRegion, 1); qemu_irq *irq; DriveInfo *dinfo; - int i; DeviceState *dev; SysBusDevice *busdev; MemoryRegion *address_space_mem = get_system_memory(); PCIBus *pci_bus; + USBBus *usb_bus; cpu = SUPERH_CPU(cpu_create(machine->cpu_type)); env = &cpu->env; @@ -274,7 +275,7 @@ static void r2d_init(MachineState *machine) dev = qdev_new("sysbus-sm501"); 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_uint64(dev, "dma-offset", 0x10000000); qdev_prop_set_chr(dev, "chardev", serial_hd(2)); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, 0x10000000); @@ -285,9 +286,9 @@ static void r2d_init(MachineState *machine) dinfo = drive_get(IF_IDE, 0, 0); dev = qdev_new("mmio-ide"); busdev = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(busdev, 0, irq[CF_IDE]); qdev_prop_set_uint32(dev, "shift", 1); sysbus_realize_and_unref(busdev, &error_fatal); + sysbus_connect_irq(busdev, 0, irq[CF_IDE]); sysbus_mmio_map(busdev, 0, 0x14001000); sysbus_mmio_map(busdev, 1, 0x1400080c); mmio_ide_init_drives(dev, dinfo, NULL); @@ -308,12 +309,13 @@ static void r2d_init(MachineState *machine) 0x555, 0x2aa, 0); /* NIC: rtl8139 on-board, and 2 slots. */ - for (i = 0; i < nb_nics; i++) - pci_nic_init_nofail(&nd_table[i], pci_bus, - "rtl8139", i == 0 ? "2" : NULL); + pci_init_nic_in_slot(pci_bus, mc->default_nic, NULL, "2"); + pci_init_nic_devices(pci_bus, mc->default_nic); /* USB keyboard */ - usb_create_simple(usb_bus_find(-1), "usb-kbd"); + usb_bus = USB_BUS(object_resolve_type_unambiguous(TYPE_USB_BUS, + &error_abort)); + usb_create_simple(usb_bus, "usb-kbd"); /* Todo: register on board registers */ memset(&boot_params, 0, sizeof(boot_params)); @@ -375,6 +377,7 @@ static void r2d_machine_init(MachineClass *mc) mc->init = r2d_init; mc->block_default_type = IF_IDE; mc->default_cpu_type = TYPE_SH7751R_CPU; + mc->default_nic = "rtl8139"; } DEFINE_MACHINE("r2d", r2d_machine_init) diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index c77792d150..ebe0fd96d9 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -207,13 +207,13 @@ static void portb_changed(SH7750State *s, uint16_t prev) static void error_access(const char *kind, hwaddr addr) { - fprintf(stderr, "%s to %s (0x" TARGET_FMT_plx ") not supported\n", + fprintf(stderr, "%s to %s (0x" HWADDR_FMT_plx ") not supported\n", kind, regname(addr), addr); } static void ignore_access(const char *kind, hwaddr addr) { - fprintf(stderr, "%s to %s (0x" TARGET_FMT_plx ") ignored\n", + fprintf(stderr, "%s to %s (0x" HWADDR_FMT_plx ") ignored\n", kind, regname(addr), addr); } diff --git a/hw/sh4/sh7750_regs.h b/hw/sh4/sh7750_regs.h index beb571d5e9..946ad7b3aa 100644 --- a/hw/sh4/sh7750_regs.h +++ b/hw/sh4/sh7750_regs.h @@ -22,8 +22,7 @@ * 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 RTEMS; see - * file COPYING. If not, write to the Free Software Foundation, 675 - * Mass Ave, Cambridge, MA 02139, USA. + * file COPYING. If not, see . * * As a special exception, including RTEMS header files in a file, * instantiating RTEMS generics or templates, or linking other files @@ -114,7 +113,7 @@ #define SH7750_TTB SH7750_P4_REG32(SH7750_TTB_REGOFS) #define SH7750_TTB_A7 SH7750_A7_REG32(SH7750_TTB_REGOFS) -/* TLB exeption address register - TEA */ +/* TLB exception address register - TEA */ #define SH7750_TEA_REGOFS 0x00000c /* offset */ #define SH7750_TEA SH7750_P4_REG32(SH7750_TEA_REGOFS) #define SH7750_TEA_A7 SH7750_A7_REG32(SH7750_TEA_REGOFS) @@ -173,7 +172,7 @@ /* - * Exeption-related registers + * Exception-related registers */ /* Immediate data for TRAPA instruction - TRA */ @@ -184,19 +183,19 @@ #define SH7750_TRA_IMM 0x000003fd /* Immediate data operand */ #define SH7750_TRA_IMM_S 2 -/* Exeption event register - EXPEVT */ +/* Exception event register - EXPEVT */ #define SH7750_EXPEVT_REGOFS 0x000024 #define SH7750_EXPEVT SH7750_P4_REG32(SH7750_EXPEVT_REGOFS) #define SH7750_EXPEVT_A7 SH7750_A7_REG32(SH7750_EXPEVT_REGOFS) -#define SH7750_EXPEVT_EX 0x00000fff /* Exeption code */ +#define SH7750_EXPEVT_EX 0x00000fff /* Exception code */ #define SH7750_EXPEVT_EX_S 0 /* Interrupt event register */ #define SH7750_INTEVT_REGOFS 0x000028 #define SH7750_INTEVT SH7750_P4_REG32(SH7750_INTEVT_REGOFS) #define SH7750_INTEVT_A7 SH7750_A7_REG32(SH7750_INTEVT_REGOFS) -#define SH7750_INTEVT_EX 0x00000fff /* Exeption code */ +#define SH7750_INTEVT_EX 0x00000fff /* Exception code */ #define SH7750_INTEVT_EX_S 0 /* @@ -1275,15 +1274,15 @@ /* * User Break Controller registers */ -#define SH7750_BARA 0x200000 /* Break address regiser A */ -#define SH7750_BAMRA 0x200004 /* Break address mask regiser A */ -#define SH7750_BBRA 0x200008 /* Break bus cycle regiser A */ -#define SH7750_BARB 0x20000c /* Break address regiser B */ -#define SH7750_BAMRB 0x200010 /* Break address mask regiser B */ -#define SH7750_BBRB 0x200014 /* Break bus cycle regiser B */ -#define SH7750_BASRB 0x000018 /* Break ASID regiser B */ -#define SH7750_BDRB 0x200018 /* Break data regiser B */ -#define SH7750_BDMRB 0x20001c /* Break data mask regiser B */ +#define SH7750_BARA 0x200000 /* Break address register A */ +#define SH7750_BAMRA 0x200004 /* Break address mask register A */ +#define SH7750_BBRA 0x200008 /* Break bus cycle register A */ +#define SH7750_BARB 0x20000c /* Break address register B */ +#define SH7750_BAMRB 0x200010 /* Break address mask register B */ +#define SH7750_BBRB 0x200014 /* Break bus cycle register B */ +#define SH7750_BASRB 0x000018 /* Break ASID register B */ +#define SH7750_BDRB 0x200018 /* Break data register B */ +#define SH7750_BDMRB 0x20001c /* Break data mask register B */ #define SH7750_BRCR 0x200020 /* Break control register */ #define SH7750_BRCR_UDBE 0x0001 /* User break debug enable bit */ diff --git a/hw/sh4/shix.c b/hw/sh4/shix.c index aa812512f0..eb3150b5bc 100644 --- a/hw/sh4/shix.c +++ b/hw/sh4/shix.c @@ -80,6 +80,7 @@ static void shix_machine_init(MachineClass *mc) mc->init = shix_init; mc->is_default = true; mc->default_cpu_type = TYPE_SH7750R_CPU; + mc->deprecation_reason = "old and unmaintained"; } DEFINE_MACHINE("shix", shix_machine_init) diff --git a/hw/smbios/Kconfig b/hw/smbios/Kconfig index 553adf4bfc..8d989a2f1b 100644 --- a/hw/smbios/Kconfig +++ b/hw/smbios/Kconfig @@ -1,2 +1,4 @@ config SMBIOS bool +config SMBIOS_LEGACY + bool diff --git a/hw/smbios/meson.build b/hw/smbios/meson.build index 9e762c7108..a59039f669 100644 --- a/hw/smbios/meson.build +++ b/hw/smbios/meson.build @@ -4,10 +4,9 @@ smbios_ss.add(when: 'CONFIG_IPMI', if_true: files('smbios_type_38.c'), if_false: files('smbios_type_38-stub.c')) -softmmu_ss.add_all(when: 'CONFIG_SMBIOS', if_true: smbios_ss) -softmmu_ss.add(when: 'CONFIG_SMBIOS', if_false: files('smbios-stub.c')) +smbios_ss.add(when: 'CONFIG_SMBIOS_LEGACY', + if_true: files('smbios_legacy.c'), + if_false: files('smbios_legacy_stub.c')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files( - 'smbios-stub.c', - 'smbios_type_38-stub.c', -)) +system_ss.add_all(when: 'CONFIG_SMBIOS', if_true: smbios_ss) +system_ss.add(when: 'CONFIG_SMBIOS', if_false: files('smbios-stub.c')) diff --git a/hw/smbios/smbios-stub.c b/hw/smbios/smbios-stub.c index 64e5ba93ec..e8808adfda 100644 --- a/hw/smbios/smbios-stub.c +++ b/hw/smbios/smbios-stub.c @@ -21,11 +21,9 @@ */ #include "qemu/osdep.h" -#include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "hw/firmware/smbios.h" void smbios_entry_add(QemuOpts *opts, Error **errp) { - error_setg(errp, QERR_UNSUPPORTED); + g_assert_not_reached(); } diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 60349ee402..eed5787b15 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -19,7 +19,6 @@ #include "qemu/units.h" #include "qapi/error.h" #include "qemu/config-file.h" -#include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/option.h" #include "sysemu/sysemu.h" @@ -28,62 +27,34 @@ #include "hw/loader.h" #include "hw/boards.h" #include "hw/pci/pci_bus.h" +#include "hw/pci/pci_device.h" #include "smbios_build.h" -/* legacy structures and constants for <= 2.0 machines */ -struct smbios_header { - uint16_t length; - uint8_t type; -} QEMU_PACKED; - -struct smbios_field { - struct smbios_header header; - uint8_t type; - uint16_t offset; - uint8_t data[]; -} QEMU_PACKED; - -struct smbios_table { - struct smbios_header header; - uint8_t data[]; -} QEMU_PACKED; - -#define SMBIOS_FIELD_ENTRY 0 -#define SMBIOS_TABLE_ENTRY 1 - -static uint8_t *smbios_entries; -static size_t smbios_entries_len; -static bool smbios_legacy = true; static bool smbios_uuid_encoded = true; -/* end: legacy structures & constants for <= 2.0 machines */ - +/* + * SMBIOS tables provided by user with '-smbios file=' option + */ +uint8_t *usr_blobs; +size_t usr_blobs_len; +static unsigned usr_table_max; +static unsigned usr_table_cnt; uint8_t *smbios_tables; size_t smbios_tables_len; unsigned smbios_table_max; unsigned smbios_table_cnt; -static SmbiosEntryPointType smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_32; static SmbiosEntryPoint ep; static int smbios_type4_count = 0; -static bool smbios_immutable; static bool smbios_have_defaults; -static uint32_t smbios_cpuid_version, smbios_cpuid_features, smbios_smp_sockets; +static uint32_t smbios_cpuid_version, smbios_cpuid_features; -static DECLARE_BITMAP(have_binfile_bitmap, SMBIOS_MAX_TYPE+1); -static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1); +DECLARE_BITMAP(smbios_have_binfile_bitmap, SMBIOS_MAX_TYPE + 1); +DECLARE_BITMAP(smbios_have_fields_bitmap, SMBIOS_MAX_TYPE + 1); -static struct { - const char *vendor, *version, *date; - bool have_major_minor, uefi; - uint8_t major, minor; -} type0; - -static struct { - const char *manufacturer, *product, *version, *serial, *sku, *family; - /* uuid is in qemu_uuid */ -} type1; +smbios_type0_t smbios_type0; +smbios_type1_t smbios_type1; static struct { const char *manufacturer, *product, *version, *serial, *asset, *location; @@ -101,6 +72,7 @@ static struct { #define DEFAULT_CPU_SPEED 2000 static struct { + uint16_t processor_family; const char *sock_pfx, *manufacturer, *version, *serial, *asset, *part; uint64_t max_speed; uint64_t current_speed; @@ -109,8 +81,26 @@ static struct { .max_speed = DEFAULT_CPU_SPEED, .current_speed = DEFAULT_CPU_SPEED, .processor_id = 0, + .processor_family = 0x01, /* Other */ }; +struct type8_instance { + const char *internal_reference, *external_reference; + uint8_t connector_type, port_type; + QTAILQ_ENTRY(type8_instance) next; +}; +static QTAILQ_HEAD(, type8_instance) type8 = QTAILQ_HEAD_INITIALIZER(type8); + +/* type 9 instance for parsing */ +struct type9_instance { + const char *slot_designation, *pcidev; + uint8_t slot_type, slot_data_bus_width, current_usage, slot_length, + slot_characteristics1, slot_characteristics2; + uint16_t slot_id; + QTAILQ_ENTRY(type9_instance) next; +}; +static QTAILQ_HEAD(, type9_instance) type9 = QTAILQ_HEAD_INITIALIZER(type9); + static struct { size_t nvalues; char **values; @@ -329,6 +319,10 @@ static const QemuOptDesc qemu_smbios_type4_opts[] = { .name = "part", .type = QEMU_OPT_STRING, .help = "part number", + }, { + .name = "processor-family", + .type = QEMU_OPT_NUMBER, + .help = "processor family", }, { .name = "processor-id", .type = QEMU_OPT_NUMBER, @@ -337,7 +331,94 @@ static const QemuOptDesc qemu_smbios_type4_opts[] = { { /* end of list */ } }; +static const QemuOptDesc qemu_smbios_type8_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + }, + { + .name = "internal_reference", + .type = QEMU_OPT_STRING, + .help = "internal reference designator", + }, + { + .name = "external_reference", + .type = QEMU_OPT_STRING, + .help = "external reference designator", + }, + { + .name = "connector_type", + .type = QEMU_OPT_NUMBER, + .help = "connector type", + }, + { + .name = "port_type", + .type = QEMU_OPT_NUMBER, + .help = "port type", + }, + { /* end of list */ } +}; + +static const QemuOptDesc qemu_smbios_type9_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + }, + { + .name = "slot_designation", + .type = QEMU_OPT_STRING, + .help = "string number for reference designation", + }, + { + .name = "slot_type", + .type = QEMU_OPT_NUMBER, + .help = "connector type", + }, + { + .name = "slot_data_bus_width", + .type = QEMU_OPT_NUMBER, + .help = "port type", + }, + { + .name = "current_usage", + .type = QEMU_OPT_NUMBER, + .help = "current usage", + }, + { + .name = "slot_length", + .type = QEMU_OPT_NUMBER, + .help = "system slot length", + }, + { + .name = "slot_id", + .type = QEMU_OPT_NUMBER, + .help = "system slot id", + }, + { + .name = "slot_characteristics1", + .type = QEMU_OPT_NUMBER, + .help = "slot characteristics1, see the spec", + }, + { + .name = "slot_characteristics2", + .type = QEMU_OPT_NUMBER, + .help = "slot characteristics2, see the spec", + }, + { + .name = "pci_device", + .type = QEMU_OPT_STRING, + .help = "PCI device, if provided." + } +}; + static const QemuOptDesc qemu_smbios_type11_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + }, { .name = "value", .type = QEMU_OPT_STRING, @@ -348,6 +429,7 @@ static const QemuOptDesc qemu_smbios_type11_opts[] = { .type = QEMU_OPT_STRING, .help = "OEM string data from file", }, + { /* end of list */ } }; static const QemuOptDesc qemu_smbios_type17_opts[] = { @@ -427,126 +509,33 @@ opts_init(smbios_register_config); */ #define SMBIOS_21_MAX_TABLES_LEN 0xffff -static void smbios_validate_table(MachineState *ms) +static bool smbios_check_type4_count(uint32_t expected_t4_count, Error **errp) { - uint32_t expect_t4_count = smbios_legacy ? - ms->smp.cpus : smbios_smp_sockets; - - if (smbios_type4_count && smbios_type4_count != expect_t4_count) { - error_report("Expected %d SMBIOS Type 4 tables, got %d instead", - expect_t4_count, smbios_type4_count); - exit(1); + if (smbios_type4_count && smbios_type4_count != expected_t4_count) { + error_setg(errp, "Expected %d SMBIOS Type 4 tables, got %d instead", + expected_t4_count, smbios_type4_count); + return false; } + return true; +} - if (smbios_ep_type == SMBIOS_ENTRY_POINT_TYPE_32 && +bool smbios_validate_table(SmbiosEntryPointType ep_type, Error **errp) +{ + if (ep_type == SMBIOS_ENTRY_POINT_TYPE_32 && smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) { - error_report("SMBIOS 2.1 table length %zu exceeds %d", - smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN); - exit(1); + error_setg(errp, "SMBIOS 2.1 table length %zu exceeds %d", + smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN); + return false; } + return true; } - -/* legacy setup functions for <= 2.0 machines */ -static void smbios_add_field(int type, int offset, const void *data, size_t len) -{ - struct smbios_field *field; - - if (!smbios_entries) { - smbios_entries_len = sizeof(uint16_t); - smbios_entries = g_malloc0(smbios_entries_len); - } - smbios_entries = g_realloc(smbios_entries, smbios_entries_len + - sizeof(*field) + len); - field = (struct smbios_field *)(smbios_entries + smbios_entries_len); - field->header.type = SMBIOS_FIELD_ENTRY; - field->header.length = cpu_to_le16(sizeof(*field) + len); - - field->type = type; - field->offset = cpu_to_le16(offset); - memcpy(field->data, data, len); - - smbios_entries_len += sizeof(*field) + len; - (*(uint16_t *)smbios_entries) = - cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); -} - -static void smbios_maybe_add_str(int type, int offset, const char *data) -{ - if (data) { - smbios_add_field(type, offset, data, strlen(data) + 1); - } -} - -static void smbios_build_type_0_fields(void) -{ - smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str), - type0.vendor); - smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str), - type0.version); - smbios_maybe_add_str(0, offsetof(struct smbios_type_0, - bios_release_date_str), - type0.date); - if (type0.have_major_minor) { - smbios_add_field(0, offsetof(struct smbios_type_0, - system_bios_major_release), - &type0.major, 1); - smbios_add_field(0, offsetof(struct smbios_type_0, - system_bios_minor_release), - &type0.minor, 1); - } -} - -static void smbios_build_type_1_fields(void) -{ - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, manufacturer_str), - type1.manufacturer); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str), - type1.product); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str), - type1.version); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str), - type1.serial); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str), - type1.sku); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str), - type1.family); - if (qemu_uuid_set) { - /* We don't encode the UUID in the "wire format" here because this - * function is for legacy mode and needs to keep the guest ABI, and - * because we don't know what's the SMBIOS version advertised by the - * BIOS. - */ - smbios_add_field(1, offsetof(struct smbios_type_1, uuid), - &qemu_uuid, 16); - } -} - -uint8_t *smbios_get_table_legacy(MachineState *ms, size_t *length) -{ - if (!smbios_legacy) { - *length = 0; - return NULL; - } - - if (!smbios_immutable) { - smbios_build_type_0_fields(); - smbios_build_type_1_fields(); - smbios_validate_table(ms); - smbios_immutable = true; - } - *length = smbios_entries_len; - return smbios_entries; -} -/* end: legacy setup functions for <= 2.0 machines */ - - bool smbios_skip_table(uint8_t type, bool required_table) { - if (test_bit(type, have_binfile_bitmap)) { + if (test_bit(type, smbios_have_binfile_bitmap)) { return true; /* user provided their own binary blob(s) */ } - if (test_bit(type, have_fields_bitmap)) { + if (test_bit(type, smbios_have_fields_bitmap)) { return false; /* user provided fields via command line */ } if (smbios_have_defaults && required_table) { @@ -560,6 +549,7 @@ bool smbios_skip_table(uint8_t type, bool required_table) #define T2_BASE 0x200 #define T3_BASE 0x300 #define T4_BASE 0x400 +#define T9_BASE 0x900 #define T11_BASE 0xe00 #define T16_BASE 0x1000 @@ -573,25 +563,25 @@ static void smbios_build_type_0_table(void) { SMBIOS_BUILD_TABLE_PRE(0, T0_BASE, false); /* optional, leave up to BIOS */ - SMBIOS_TABLE_SET_STR(0, vendor_str, type0.vendor); - SMBIOS_TABLE_SET_STR(0, bios_version_str, type0.version); + SMBIOS_TABLE_SET_STR(0, vendor_str, smbios_type0.vendor); + SMBIOS_TABLE_SET_STR(0, bios_version_str, smbios_type0.version); t->bios_starting_address_segment = cpu_to_le16(0xE800); /* from SeaBIOS */ - SMBIOS_TABLE_SET_STR(0, bios_release_date_str, type0.date); + SMBIOS_TABLE_SET_STR(0, bios_release_date_str, smbios_type0.date); t->bios_rom_size = 0; /* hardcoded in SeaBIOS with FIXME comment */ t->bios_characteristics = cpu_to_le64(0x08); /* Not supported */ t->bios_characteristics_extension_bytes[0] = 0; t->bios_characteristics_extension_bytes[1] = 0x14; /* TCD/SVVP | VM */ - if (type0.uefi) { + if (smbios_type0.uefi) { t->bios_characteristics_extension_bytes[1] |= 0x08; /* |= UEFI */ } - if (type0.have_major_minor) { - t->system_bios_major_release = type0.major; - t->system_bios_minor_release = type0.minor; + if (smbios_type0.have_major_minor) { + t->system_bios_major_release = smbios_type0.major; + t->system_bios_minor_release = smbios_type0.minor; } else { t->system_bios_major_release = 0; t->system_bios_minor_release = 0; @@ -621,18 +611,18 @@ static void smbios_build_type_1_table(void) { SMBIOS_BUILD_TABLE_PRE(1, T1_BASE, true); /* required */ - SMBIOS_TABLE_SET_STR(1, manufacturer_str, type1.manufacturer); - SMBIOS_TABLE_SET_STR(1, product_name_str, type1.product); - SMBIOS_TABLE_SET_STR(1, version_str, type1.version); - SMBIOS_TABLE_SET_STR(1, serial_number_str, type1.serial); + SMBIOS_TABLE_SET_STR(1, manufacturer_str, smbios_type1.manufacturer); + SMBIOS_TABLE_SET_STR(1, product_name_str, smbios_type1.product); + SMBIOS_TABLE_SET_STR(1, version_str, smbios_type1.version); + SMBIOS_TABLE_SET_STR(1, serial_number_str, smbios_type1.serial); if (qemu_uuid_set) { smbios_encode_uuid(&t->uuid, &qemu_uuid); } else { memset(&t->uuid, 0, 16); } t->wake_up_type = 0x06; /* power switch */ - SMBIOS_TABLE_SET_STR(1, sku_number_str, type1.sku); - SMBIOS_TABLE_SET_STR(1, family_str, type1.family); + SMBIOS_TABLE_SET_STR(1, sku_number_str, smbios_type1.sku); + SMBIOS_TABLE_SET_STR(1, family_str, smbios_type1.family); SMBIOS_BUILD_TABLE_POST; } @@ -678,16 +668,26 @@ static void smbios_build_type_3_table(void) SMBIOS_BUILD_TABLE_POST; } -static void smbios_build_type_4_table(MachineState *ms, unsigned instance) +static void smbios_build_type_4_table(MachineState *ms, unsigned instance, + SmbiosEntryPointType ep_type, + Error **errp) { char sock_str[128]; + size_t tbl_len = SMBIOS_TYPE_4_LEN_V28; + unsigned threads_per_socket; + unsigned cores_per_socket; - SMBIOS_BUILD_TABLE_PRE(4, T4_BASE + instance, true); /* required */ + if (ep_type == SMBIOS_ENTRY_POINT_TYPE_64) { + tbl_len = SMBIOS_TYPE_4_LEN_V30; + } + + SMBIOS_BUILD_TABLE_PRE_SIZE(4, T4_BASE + instance, + true, tbl_len); /* required */ snprintf(sock_str, sizeof(sock_str), "%s%2x", type4.sock_pfx, instance); SMBIOS_TABLE_SET_STR(4, socket_designation_str, sock_str); t->processor_type = 0x03; /* CPU */ - t->processor_family = 0x01; /* Other */ + t->processor_family = 0xfe; /* use Processor Family 2 field */ SMBIOS_TABLE_SET_STR(4, processor_manufacturer_str, type4.manufacturer); if (type4.processor_id == 0) { t->processor_id[0] = cpu_to_le32(smbios_cpuid_version); @@ -709,15 +709,112 @@ static void smbios_build_type_4_table(MachineState *ms, unsigned instance) SMBIOS_TABLE_SET_STR(4, serial_number_str, type4.serial); SMBIOS_TABLE_SET_STR(4, asset_tag_number_str, type4.asset); SMBIOS_TABLE_SET_STR(4, part_number_str, type4.part); - t->core_count = t->core_enabled = ms->smp.cores; - t->thread_count = ms->smp.threads; + + threads_per_socket = machine_topo_get_threads_per_socket(ms); + cores_per_socket = machine_topo_get_cores_per_socket(ms); + + t->core_count = (cores_per_socket > 255) ? 0xFF : cores_per_socket; + t->core_enabled = t->core_count; + + t->thread_count = (threads_per_socket > 255) ? 0xFF : threads_per_socket; + t->processor_characteristics = cpu_to_le16(0x02); /* Unknown */ - t->processor_family2 = cpu_to_le16(0x01); /* Other */ + t->processor_family2 = cpu_to_le16(type4.processor_family); + + if (tbl_len == SMBIOS_TYPE_4_LEN_V30) { + t->core_count2 = t->core_enabled2 = cpu_to_le16(cores_per_socket); + t->thread_count2 = cpu_to_le16(threads_per_socket); + } else if (t->core_count == 0xFF || t->thread_count == 0xFF) { + error_setg(errp, "SMBIOS 2.0 doesn't support number of processor " + "cores/threads more than 255, use " + "-machine smbios-entry-point-type=64 option to enable " + "SMBIOS 3.0 support"); + return; + } SMBIOS_BUILD_TABLE_POST; smbios_type4_count++; } +static void smbios_build_type_8_table(void) +{ + unsigned instance = 0; + struct type8_instance *t8; + + QTAILQ_FOREACH(t8, &type8, next) { + SMBIOS_BUILD_TABLE_PRE(8, T0_BASE + instance, true); + + SMBIOS_TABLE_SET_STR(8, internal_reference_str, t8->internal_reference); + SMBIOS_TABLE_SET_STR(8, external_reference_str, t8->external_reference); + /* most vendors seem to set this to None */ + t->internal_connector_type = 0x0; + t->external_connector_type = t8->connector_type; + t->port_type = t8->port_type; + + SMBIOS_BUILD_TABLE_POST; + instance++; + } +} + +static void smbios_build_type_9_table(Error **errp) +{ + unsigned instance = 0; + struct type9_instance *t9; + + QTAILQ_FOREACH(t9, &type9, next) { + SMBIOS_BUILD_TABLE_PRE(9, T9_BASE + instance, true); + + SMBIOS_TABLE_SET_STR(9, slot_designation, t9->slot_designation); + t->slot_type = t9->slot_type; + t->slot_data_bus_width = t9->slot_data_bus_width; + t->current_usage = t9->current_usage; + t->slot_length = t9->slot_length; + t->slot_id = t9->slot_id; + t->slot_characteristics1 = t9->slot_characteristics1; + t->slot_characteristics2 = t9->slot_characteristics2; + + if (t9->pcidev) { + PCIDevice *pdev = NULL; + int rc = pci_qdev_find_device(t9->pcidev, &pdev); + if (rc != 0) { + error_setg(errp, + "No PCI device %s for SMBIOS type 9 entry %s", + t9->pcidev, t9->slot_designation); + return; + } + /* + * We only handle the case were the device is attached to + * the PCI root bus. The general case is more complex as + * bridges are enumerated later and the table would need + * to be updated at this moment. + */ + if (!pci_bus_is_root(pci_get_bus(pdev))) { + error_setg(errp, + "Cannot create type 9 entry for PCI device %s: " + "not attached to the root bus", + t9->pcidev); + return; + } + t->segment_group_number = cpu_to_le16(0); + t->bus_number = pci_dev_bus_num(pdev); + t->device_number = pdev->devfn; + } else { + /* + * Per SMBIOS spec, For slots that are not of the PCI, AGP, PCI-X, + * or PCI-Express type that do not have bus/device/function + * information, 0FFh should be populated in the fields of Segment + * Group Number, Bus Number, Device/Function Number. + */ + t->segment_group_number = 0xff; + t->bus_number = 0xff; + t->device_number = 0xff; + } + + SMBIOS_BUILD_TABLE_POST; + instance++; + } +} + static void smbios_build_type_11_table(void) { char count_str[128]; @@ -912,32 +1009,23 @@ void smbios_set_cpuid(uint32_t version, uint32_t features) field = value; \ } +void smbios_set_default_processor_family(uint16_t processor_family) +{ + if (type4.processor_family <= 0x01) { + type4.processor_family = processor_family; + } +} + void smbios_set_defaults(const char *manufacturer, const char *product, - const char *version, bool legacy_mode, - bool uuid_encoded, SmbiosEntryPointType ep_type) + const char *version, + bool uuid_encoded) { smbios_have_defaults = true; - smbios_legacy = legacy_mode; smbios_uuid_encoded = uuid_encoded; - smbios_ep_type = ep_type; - /* drop unwanted version of command-line file blob(s) */ - if (smbios_legacy) { - g_free(smbios_tables); - /* in legacy mode, also complain if fields were given for types > 1 */ - if (find_next_bit(have_fields_bitmap, - SMBIOS_MAX_TYPE+1, 2) < SMBIOS_MAX_TYPE+1) { - error_report("can't process fields for smbios " - "types > 1 on machine versions < 2.1!"); - exit(1); - } - } else { - g_free(smbios_entries); - } - - SMBIOS_SET_DEFAULT(type1.manufacturer, manufacturer); - SMBIOS_SET_DEFAULT(type1.product, product); - SMBIOS_SET_DEFAULT(type1.version, version); + SMBIOS_SET_DEFAULT(smbios_type1.manufacturer, manufacturer); + SMBIOS_SET_DEFAULT(smbios_type1.product, product); + SMBIOS_SET_DEFAULT(smbios_type1.version, version); SMBIOS_SET_DEFAULT(type2.manufacturer, manufacturer); SMBIOS_SET_DEFAULT(type2.product, product); SMBIOS_SET_DEFAULT(type2.version, version); @@ -950,9 +1038,9 @@ void smbios_set_defaults(const char *manufacturer, const char *product, SMBIOS_SET_DEFAULT(type17.manufacturer, manufacturer); } -static void smbios_entry_point_setup(void) +static void smbios_entry_point_setup(SmbiosEntryPointType ep_type) { - switch (smbios_ep_type) { + switch (ep_type) { case SMBIOS_ENTRY_POINT_TYPE_32: memcpy(ep.ep21.anchor_string, "_SM_", 4); memcpy(ep.ep21.intermediate_anchor_string, "_DMI_", 5); @@ -1001,7 +1089,8 @@ static void smbios_entry_point_setup(void) } } -void smbios_get_tables(MachineState *ms, +static bool smbios_get_tables_ep(MachineState *ms, + SmbiosEntryPointType ep_type, const struct smbios_phys_mem_area *mem_array, const unsigned int mem_array_size, uint8_t **tables, size_t *tables_len, @@ -1009,77 +1098,87 @@ void smbios_get_tables(MachineState *ms, Error **errp) { unsigned i, dimm_cnt, offset; + ERRP_GUARD(); - if (smbios_legacy) { - *tables = *anchor = NULL; - *tables_len = *anchor_len = 0; - return; + assert(ep_type == SMBIOS_ENTRY_POINT_TYPE_32 || + ep_type == SMBIOS_ENTRY_POINT_TYPE_64); + + g_free(smbios_tables); + smbios_type4_count = 0; + smbios_tables = g_memdup2(usr_blobs, usr_blobs_len); + smbios_tables_len = usr_blobs_len; + smbios_table_max = usr_table_max; + smbios_table_cnt = usr_table_cnt; + + smbios_build_type_0_table(); + smbios_build_type_1_table(); + smbios_build_type_2_table(); + smbios_build_type_3_table(); + + assert(ms->smp.sockets >= 1); + + for (i = 0; i < ms->smp.sockets; i++) { + smbios_build_type_4_table(ms, i, ep_type, errp); + if (*errp) { + goto err_exit; + } } - if (!smbios_immutable) { - smbios_build_type_0_table(); - smbios_build_type_1_table(); - smbios_build_type_2_table(); - smbios_build_type_3_table(); - - smbios_smp_sockets = DIV_ROUND_UP(ms->smp.cpus, - ms->smp.cores * ms->smp.threads); - assert(smbios_smp_sockets >= 1); - - for (i = 0; i < smbios_smp_sockets; i++) { - smbios_build_type_4_table(ms, i); - } - - smbios_build_type_11_table(); + smbios_build_type_8_table(); + smbios_build_type_9_table(errp); + smbios_build_type_11_table(); #define MAX_DIMM_SZ (16 * GiB) #define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \ : ((current_machine->ram_size - 1) % MAX_DIMM_SZ) + 1) - dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, MAX_DIMM_SZ) / MAX_DIMM_SZ; + dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, MAX_DIMM_SZ) / + MAX_DIMM_SZ; - /* - * The offset determines if we need to keep additional space betweeen - * table 17 and table 19 header handle numbers so that they do - * not overlap. For example, for a VM with larger than 8 TB guest - * memory and DIMM like chunks of 16 GiB, the default space between - * the two tables (T19_BASE - T17_BASE = 512) is not enough. - */ - offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \ - dimm_cnt - (T19_BASE - T17_BASE) : 0; + /* + * The offset determines if we need to keep additional space between + * table 17 and table 19 header handle numbers so that they do + * not overlap. For example, for a VM with larger than 8 TB guest + * memory and DIMM like chunks of 16 GiB, the default space between + * the two tables (T19_BASE - T17_BASE = 512) is not enough. + */ + offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \ + dimm_cnt - (T19_BASE - T17_BASE) : 0; - smbios_build_type_16_table(dimm_cnt); + smbios_build_type_16_table(dimm_cnt); - for (i = 0; i < dimm_cnt; i++) { - smbios_build_type_17_table(i, GET_DIMM_SZ); - } - - for (i = 0; i < mem_array_size; i++) { - smbios_build_type_19_table(i, offset, mem_array[i].address, - mem_array[i].length); - } - - /* - * make sure 16 bit handle numbers in the headers of tables 19 - * and 32 do not overlap. - */ - assert((mem_array_size + offset) < (T32_BASE - T19_BASE)); - - smbios_build_type_32_table(); - smbios_build_type_38_table(); - smbios_build_type_41_table(errp); - smbios_build_type_127_table(); - - smbios_validate_table(ms); - smbios_entry_point_setup(); - smbios_immutable = true; + for (i = 0; i < dimm_cnt; i++) { + smbios_build_type_17_table(i, GET_DIMM_SZ); } + for (i = 0; i < mem_array_size; i++) { + smbios_build_type_19_table(i, offset, mem_array[i].address, + mem_array[i].length); + } + + /* + * make sure 16 bit handle numbers in the headers of tables 19 + * and 32 do not overlap. + */ + assert((mem_array_size + offset) < (T32_BASE - T19_BASE)); + + smbios_build_type_32_table(); + smbios_build_type_38_table(); + smbios_build_type_41_table(errp); + smbios_build_type_127_table(); + + if (!smbios_check_type4_count(ms->smp.sockets, errp)) { + goto err_exit; + } + if (!smbios_validate_table(ep_type, errp)) { + goto err_exit; + } + smbios_entry_point_setup(ep_type); + /* return tables blob and entry point (anchor), and their sizes */ *tables = smbios_tables; *tables_len = smbios_tables_len; *anchor = (uint8_t *)&ep; - /* calculate length based on anchor string */ if (!strncmp((char *)&ep, "_SM_", 4)) { *anchor_len = sizeof(struct smbios_21_entry_point); @@ -1088,6 +1187,57 @@ void smbios_get_tables(MachineState *ms, } else { abort(); } + + return true; +err_exit: + g_free(smbios_tables); + smbios_tables = NULL; + return false; +} + +void smbios_get_tables(MachineState *ms, + SmbiosEntryPointType ep_type, + const struct smbios_phys_mem_area *mem_array, + const unsigned int mem_array_size, + uint8_t **tables, size_t *tables_len, + uint8_t **anchor, size_t *anchor_len, + Error **errp) +{ + Error *local_err = NULL; + bool is_valid; + ERRP_GUARD(); + + switch (ep_type) { + case SMBIOS_ENTRY_POINT_TYPE_AUTO: + case SMBIOS_ENTRY_POINT_TYPE_32: + is_valid = smbios_get_tables_ep(ms, SMBIOS_ENTRY_POINT_TYPE_32, + mem_array, mem_array_size, + tables, tables_len, + anchor, anchor_len, + &local_err); + if (is_valid || ep_type != SMBIOS_ENTRY_POINT_TYPE_AUTO) { + break; + } + /* + * fall through in case AUTO endpoint is selected and + * SMBIOS 2.x tables can't be generated, to try if SMBIOS 3.x + * tables would work + */ + case SMBIOS_ENTRY_POINT_TYPE_64: + error_free(local_err); + local_err = NULL; + is_valid = smbios_get_tables_ep(ms, SMBIOS_ENTRY_POINT_TYPE_64, + mem_array, mem_array_size, + tables, tables_len, + anchor, anchor_len, + &local_err); + break; + default: + abort(); + } + if (!is_valid) { + error_propagate(errp, local_err); + } } static void save_opt(const char **dest, QemuOpts *opts, const char *name) @@ -1173,13 +1323,10 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) { const char *val; - assert(!smbios_immutable); - val = qemu_opt_get(opts, "file"); if (val) { struct smbios_structure_header *header; - int size; - struct smbios_table *table; /* legacy mode only */ + size_t size; if (!qemu_opts_validate(opts, qemu_smbios_file_opts, errp)) { return; @@ -1196,55 +1343,40 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) * (except in legacy mode, where the second '\0' is implicit and * will be inserted by the BIOS). */ - smbios_tables = g_realloc(smbios_tables, smbios_tables_len + size); - header = (struct smbios_structure_header *)(smbios_tables + - smbios_tables_len); + usr_blobs = g_realloc(usr_blobs, usr_blobs_len + size); + header = (struct smbios_structure_header *)(usr_blobs + + usr_blobs_len); if (load_image_size(val, (uint8_t *)header, size) != size) { error_setg(errp, "Failed to load SMBIOS file %s", val); return; } - if (test_bit(header->type, have_fields_bitmap)) { - error_setg(errp, - "can't load type %d struct, fields already specified!", - header->type); - return; + if (header->type <= SMBIOS_MAX_TYPE) { + if (test_bit(header->type, smbios_have_fields_bitmap)) { + error_setg(errp, + "can't load type %d struct, fields already specified!", + header->type); + return; + } + set_bit(header->type, smbios_have_binfile_bitmap); } - set_bit(header->type, have_binfile_bitmap); if (header->type == 4) { smbios_type4_count++; } - smbios_tables_len += size; - if (size > smbios_table_max) { - smbios_table_max = size; - } - smbios_table_cnt++; - - /* add a copy of the newly loaded blob to legacy smbios_entries */ - /* NOTE: This code runs before smbios_set_defaults(), so we don't - * yet know which mode (legacy vs. aggregate-table) will be - * required. We therefore add the binary blob to both legacy - * (smbios_entries) and aggregate (smbios_tables) tables, and - * delete the one we don't need from smbios_set_defaults(), - * once we know which machine version has been requested. + /* + * preserve blob size for legacy mode so it could build its + * blobs flavor from 'usr_blobs' */ - if (!smbios_entries) { - smbios_entries_len = sizeof(uint16_t); - smbios_entries = g_malloc0(smbios_entries_len); + smbios_add_usr_blob_size(size); + + usr_blobs_len += size; + if (size > usr_table_max) { + usr_table_max = size; } - smbios_entries = g_realloc(smbios_entries, smbios_entries_len + - size + sizeof(*table)); - table = (struct smbios_table *)(smbios_entries + smbios_entries_len); - table->header.type = SMBIOS_TABLE_ENTRY; - table->header.length = cpu_to_le16(sizeof(*table) + size); - memcpy(table->data, header, size); - smbios_entries_len += sizeof(*table) + size; - (*(uint16_t *)smbios_entries) = - cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); - /* end: add a copy of the newly loaded blob to legacy smbios_entries */ + usr_table_cnt++; return; } @@ -1258,41 +1390,42 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) return; } - if (test_bit(type, have_binfile_bitmap)) { + if (test_bit(type, smbios_have_binfile_bitmap)) { error_setg(errp, "can't add fields, binary file already loaded!"); return; } - set_bit(type, have_fields_bitmap); + set_bit(type, smbios_have_fields_bitmap); switch (type) { case 0: if (!qemu_opts_validate(opts, qemu_smbios_type0_opts, errp)) { return; } - save_opt(&type0.vendor, opts, "vendor"); - save_opt(&type0.version, opts, "version"); - save_opt(&type0.date, opts, "date"); - type0.uefi = qemu_opt_get_bool(opts, "uefi", false); + save_opt(&smbios_type0.vendor, opts, "vendor"); + save_opt(&smbios_type0.version, opts, "version"); + save_opt(&smbios_type0.date, opts, "date"); + smbios_type0.uefi = qemu_opt_get_bool(opts, "uefi", false); val = qemu_opt_get(opts, "release"); if (val) { - if (sscanf(val, "%hhu.%hhu", &type0.major, &type0.minor) != 2) { + if (sscanf(val, "%hhu.%hhu", &smbios_type0.major, + &smbios_type0.minor) != 2) { error_setg(errp, "Invalid release"); return; } - type0.have_major_minor = true; + smbios_type0.have_major_minor = true; } return; case 1: if (!qemu_opts_validate(opts, qemu_smbios_type1_opts, errp)) { return; } - save_opt(&type1.manufacturer, opts, "manufacturer"); - save_opt(&type1.product, opts, "product"); - save_opt(&type1.version, opts, "version"); - save_opt(&type1.serial, opts, "serial"); - save_opt(&type1.sku, opts, "sku"); - save_opt(&type1.family, opts, "family"); + save_opt(&smbios_type1.manufacturer, opts, "manufacturer"); + save_opt(&smbios_type1.product, opts, "product"); + save_opt(&smbios_type1.version, opts, "version"); + save_opt(&smbios_type1.serial, opts, "serial"); + save_opt(&smbios_type1.sku, opts, "sku"); + save_opt(&smbios_type1.family, opts, "family"); val = qemu_opt_get(opts, "uuid"); if (val) { @@ -1329,6 +1462,9 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) return; } save_opt(&type4.sock_pfx, opts, "sock_pfx"); + type4.processor_family = qemu_opt_get_number(opts, + "processor-family", + 0x01 /* Other */); save_opt(&type4.manufacturer, opts, "manufacturer"); save_opt(&type4.version, opts, "version"); save_opt(&type4.serial, opts, "serial"); @@ -1346,6 +1482,40 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) UINT16_MAX); } return; + case 8: + if (!qemu_opts_validate(opts, qemu_smbios_type8_opts, errp)) { + return; + } + struct type8_instance *t8_i; + t8_i = g_new0(struct type8_instance, 1); + save_opt(&t8_i->internal_reference, opts, "internal_reference"); + save_opt(&t8_i->external_reference, opts, "external_reference"); + t8_i->connector_type = qemu_opt_get_number(opts, + "connector_type", 0); + t8_i->port_type = qemu_opt_get_number(opts, "port_type", 0); + QTAILQ_INSERT_TAIL(&type8, t8_i, next); + return; + case 9: { + if (!qemu_opts_validate(opts, qemu_smbios_type9_opts, errp)) { + return; + } + struct type9_instance *t; + t = g_new0(struct type9_instance, 1); + save_opt(&t->slot_designation, opts, "slot_designation"); + t->slot_type = qemu_opt_get_number(opts, "slot_type", 0); + t->slot_data_bus_width = + qemu_opt_get_number(opts, "slot_data_bus_width", 0); + t->current_usage = qemu_opt_get_number(opts, "current_usage", 0); + t->slot_length = qemu_opt_get_number(opts, "slot_length", 0); + t->slot_id = qemu_opt_get_number(opts, "slot_id", 0); + t->slot_characteristics1 = + qemu_opt_get_number(opts, "slot_characteristics1", 0); + t->slot_characteristics2 = + qemu_opt_get_number(opts, "slot_characteristics2", 0); + save_opt(&t->pcidev, opts, "pcidev"); + QTAILQ_INSERT_TAIL(&type9, t, next); + return; + } case 11: if (!qemu_opts_validate(opts, qemu_smbios_type11_opts, errp)) { return; @@ -1367,27 +1537,27 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) type17.speed = qemu_opt_get_number(opts, "speed", 0); return; case 41: { - struct type41_instance *t; + struct type41_instance *t41_i; Error *local_err = NULL; if (!qemu_opts_validate(opts, qemu_smbios_type41_opts, errp)) { return; } - t = g_new0(struct type41_instance, 1); - save_opt(&t->designation, opts, "designation"); - t->kind = qapi_enum_parse(&type41_kind_lookup, - qemu_opt_get(opts, "kind"), - 0, &local_err) + 1; - t->kind |= 0x80; /* enabled */ + t41_i = g_new0(struct type41_instance, 1); + save_opt(&t41_i->designation, opts, "designation"); + t41_i->kind = qapi_enum_parse(&type41_kind_lookup, + qemu_opt_get(opts, "kind"), + 0, &local_err) + 1; + t41_i->kind |= 0x80; /* enabled */ if (local_err != NULL) { error_propagate(errp, local_err); - g_free(t); + g_free(t41_i); return; } - t->instance = qemu_opt_get_number(opts, "instance", 1); - save_opt(&t->pcidev, opts, "pcidev"); + t41_i->instance = qemu_opt_get_number(opts, "instance", 1); + save_opt(&t41_i->pcidev, opts, "pcidev"); - QTAILQ_INSERT_TAIL(&type41, t, next); + QTAILQ_INSERT_TAIL(&type41, t41_i, next); return; } default: diff --git a/hw/smbios/smbios_build.h b/hw/smbios/smbios_build.h index 56b5a1e3f3..351660024e 100644 --- a/hw/smbios/smbios_build.h +++ b/hw/smbios/smbios_build.h @@ -27,6 +27,11 @@ extern unsigned smbios_table_max; extern unsigned smbios_table_cnt; #define SMBIOS_BUILD_TABLE_PRE(tbl_type, tbl_handle, tbl_required) \ + SMBIOS_BUILD_TABLE_PRE_SIZE(tbl_type, tbl_handle, tbl_required, \ + sizeof(struct smbios_type_##tbl_type))\ + +#define SMBIOS_BUILD_TABLE_PRE_SIZE(tbl_type, tbl_handle, \ + tbl_required, tbl_len) \ struct smbios_type_##tbl_type *t; \ size_t t_off; /* table offset into smbios_tables */ \ int str_index = 0; \ @@ -39,12 +44,12 @@ extern unsigned smbios_table_cnt; /* use offset of table t within smbios_tables */ \ /* (pointer must be updated after each realloc) */ \ t_off = smbios_tables_len; \ - smbios_tables_len += sizeof(*t); \ + smbios_tables_len += tbl_len; \ smbios_tables = g_realloc(smbios_tables, smbios_tables_len); \ t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \ \ t->header.type = tbl_type; \ - t->header.length = sizeof(*t); \ + t->header.length = tbl_len; \ t->header.handle = cpu_to_le16(tbl_handle); \ } while (0) diff --git a/hw/smbios/smbios_legacy.c b/hw/smbios/smbios_legacy.c new file mode 100644 index 0000000000..c37a8ee821 --- /dev/null +++ b/hw/smbios/smbios_legacy.c @@ -0,0 +1,192 @@ +/* + * SMBIOS legacy support + * + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * Copyright (C) 2013 Red Hat, Inc. + * + * Authors: + * Alex Williamson + * Markus Armbruster + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "hw/firmware/smbios.h" +#include "sysemu/sysemu.h" +#include "qapi/error.h" + +struct smbios_header { + uint16_t length; + uint8_t type; +} QEMU_PACKED; + +struct smbios_field { + struct smbios_header header; + uint8_t type; + uint16_t offset; + uint8_t data[]; +} QEMU_PACKED; + +struct smbios_table { + struct smbios_header header; + uint8_t data[]; +} QEMU_PACKED; + +#define SMBIOS_FIELD_ENTRY 0 +#define SMBIOS_TABLE_ENTRY 1 + +static uint8_t *smbios_entries; +static size_t smbios_entries_len; +GArray *usr_blobs_sizes; + +void smbios_add_usr_blob_size(size_t size) +{ + if (!usr_blobs_sizes) { + usr_blobs_sizes = g_array_new(false, false, sizeof(size_t)); + } + g_array_append_val(usr_blobs_sizes, size); +} + +static void smbios_add_field(int type, int offset, const void *data, size_t len) +{ + struct smbios_field *field; + + if (!smbios_entries) { + smbios_entries_len = sizeof(uint16_t); + smbios_entries = g_malloc0(smbios_entries_len); + } + smbios_entries = g_realloc(smbios_entries, smbios_entries_len + + sizeof(*field) + len); + field = (struct smbios_field *)(smbios_entries + smbios_entries_len); + field->header.type = SMBIOS_FIELD_ENTRY; + field->header.length = cpu_to_le16(sizeof(*field) + len); + + field->type = type; + field->offset = cpu_to_le16(offset); + memcpy(field->data, data, len); + + smbios_entries_len += sizeof(*field) + len; + (*(uint16_t *)smbios_entries) = + cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); +} + +static void smbios_maybe_add_str(int type, int offset, const char *data) +{ + if (data) { + smbios_add_field(type, offset, data, strlen(data) + 1); + } +} + +static void smbios_build_type_0_fields(void) +{ + smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str), + smbios_type0.vendor); + smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str), + smbios_type0.version); + smbios_maybe_add_str(0, offsetof(struct smbios_type_0, + bios_release_date_str), + smbios_type0.date); + if (smbios_type0.have_major_minor) { + smbios_add_field(0, offsetof(struct smbios_type_0, + system_bios_major_release), + &smbios_type0.major, 1); + smbios_add_field(0, offsetof(struct smbios_type_0, + system_bios_minor_release), + &smbios_type0.minor, 1); + } +} + +static void smbios_build_type_1_fields(void) +{ + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, manufacturer_str), + smbios_type1.manufacturer); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str), + smbios_type1.product); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str), + smbios_type1.version); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str), + smbios_type1.serial); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str), + smbios_type1.sku); + smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str), + smbios_type1.family); + if (qemu_uuid_set) { + /* + * We don't encode the UUID in the "wire format" here because this + * function is for legacy mode and needs to keep the guest ABI, and + * because we don't know what's the SMBIOS version advertised by the + * BIOS. + */ + smbios_add_field(1, offsetof(struct smbios_type_1, uuid), + &qemu_uuid, 16); + } +} + +uint8_t *smbios_get_table_legacy(size_t *length, Error **errp) +{ + int i; + size_t usr_offset; + + /* complain if fields were given for types > 1 */ + if (find_next_bit(smbios_have_fields_bitmap, + SMBIOS_MAX_TYPE + 1, 2) < SMBIOS_MAX_TYPE + 1) { + error_setg(errp, "can't process fields for smbios " + "types > 1 on machine versions < 2.1!"); + goto err_exit; + } + + if (test_bit(4, smbios_have_binfile_bitmap)) { + error_setg(errp, "can't process table for smbios " + "type 4 on machine versions < 2.1!"); + goto err_exit; + } + + g_free(smbios_entries); + smbios_entries_len = sizeof(uint16_t); + smbios_entries = g_malloc0(smbios_entries_len); + + /* + * build a set of legacy smbios_table entries using user provided blobs + */ + for (i = 0, usr_offset = 0; usr_blobs_sizes && i < usr_blobs_sizes->len; + i++) + { + struct smbios_table *table; + struct smbios_structure_header *header; + size_t size = g_array_index(usr_blobs_sizes, size_t, i); + + header = (struct smbios_structure_header *)(usr_blobs + usr_offset); + smbios_entries = g_realloc(smbios_entries, smbios_entries_len + + size + sizeof(*table)); + table = (struct smbios_table *)(smbios_entries + smbios_entries_len); + table->header.type = SMBIOS_TABLE_ENTRY; + table->header.length = cpu_to_le16(sizeof(*table) + size); + memcpy(table->data, header, size); + smbios_entries_len += sizeof(*table) + size; + /* + * update number of entries in the blob, + * see SeaBIOS: qemu_cfg_legacy():QEMU_CFG_SMBIOS_ENTRIES + */ + (*(uint16_t *)smbios_entries) = + cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); + usr_offset += size; + } + + smbios_build_type_0_fields(); + smbios_build_type_1_fields(); + if (!smbios_validate_table(SMBIOS_ENTRY_POINT_TYPE_32, errp)) { + goto err_exit; + } + + *length = smbios_entries_len; + return smbios_entries; +err_exit: + g_free(smbios_entries); + return NULL; +} diff --git a/hw/smbios/smbios_legacy_stub.c b/hw/smbios/smbios_legacy_stub.c new file mode 100644 index 0000000000..7d593dca98 --- /dev/null +++ b/hw/smbios/smbios_legacy_stub.c @@ -0,0 +1,20 @@ +/* + * IPMI SMBIOS firmware handling + * + * Copyright (c) 2024 Igor Mammedov, 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/firmware/smbios.h" + +void smbios_add_usr_blob_size(size_t size) +{ +} + +uint8_t *smbios_get_table_legacy(size_t *length, Error **errp) +{ + g_assert_not_reached(); +} diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 1e39d2e2d0..6aaa04cb19 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -1,7 +1,9 @@ /* * QEMU Leon3 System Emulator * - * Copyright (c) 2010-2019 AdaCore + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2010-2024 AdaCore * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -40,7 +42,9 @@ #include "elf.h" #include "trace.h" -#include "hw/sparc/grlib.h" +#include "hw/timer/grlib_gptimer.h" +#include "hw/char/grlib_uart.h" +#include "hw/intc/grlib_irqmp.h" #include "hw/misc/grlib_ahb_apb_pnp.h" /* Default system clock. */ @@ -50,6 +54,8 @@ #define LEON3_PROM_OFFSET (0x00000000) #define LEON3_RAM_OFFSET (0x40000000) +#define MAX_CPUS 4 + #define LEON3_UART_OFFSET (0x80000100) #define LEON3_UART_IRQ (3) @@ -63,9 +69,11 @@ #define LEON3_AHB_PNP_OFFSET (0xFFFFF000) typedef struct ResetData { - SPARCCPU *cpu; - uint32_t entry; /* save kernel entry in case of reset */ - target_ulong sp; /* initial stack pointer */ + struct CPUResetData { + int id; + SPARCCPU *cpu; + } info[MAX_CPUS]; + uint32_t entry; /* save kernel entry in case of reset */ } ResetData; static uint32_t *gen_store_u32(uint32_t *code, hwaddr addr, uint32_t val) @@ -91,13 +99,26 @@ static uint32_t *gen_store_u32(uint32_t *code, hwaddr addr, uint32_t val) /* * When loading a kernel in RAM the machine is expected to be in a different - * state (eg: initialized by the bootloader). This little code reproduces - * this behavior. + * state (eg: initialized by the bootloader). This little code reproduces + * this behavior. Also this code can be executed by the secondary cpus as + * well since it looks at the %asr17 register before doing any + * initialization, it allows to use the same reset address for all the + * cpus. */ -static void write_bootloader(CPUSPARCState *env, uint8_t *base, - hwaddr kernel_addr) +static void write_bootloader(void *ptr, hwaddr kernel_addr) { - uint32_t *p = (uint32_t *) base; + uint32_t *p = ptr; + uint32_t *sec_cpu_branch_p = NULL; + + /* If we are running on a secondary CPU, jump directly to the kernel. */ + + stl_p(p++, 0x85444000); /* rd %asr17, %g2 */ + stl_p(p++, 0x8530a01c); /* srl %g2, 0x1c, %g2 */ + stl_p(p++, 0x80908000); /* tst %g2 */ + /* Filled below. */ + sec_cpu_branch_p = p; + stl_p(p++, 0x0BADC0DE); /* bne xxx */ + stl_p(p++, 0x01000000); /* nop */ /* Initialize the UARTs */ /* *UART_CONTROL = UART_RECEIVE_ENABLE | UART_TRANSMIT_ENABLE; */ @@ -111,6 +132,10 @@ static void write_bootloader(CPUSPARCState *env, uint8_t *base, /* *GPTIMER0_CONFIG = GPTIMER_ENABLE | GPTIMER_RESTART; */ p = gen_store_u32(p, 0x80000318, 3); + /* Now, the relative branch above can be computed. */ + stl_p(sec_cpu_branch_p, 0x12800000 + + (p - sec_cpu_branch_p)); + /* JUMP to the entry point */ stl_p(p++, 0x82100000); /* mov %g0, %g1 */ stl_p(p++, 0x03000000 + extract32(kernel_addr, 10, 22)); @@ -121,18 +146,19 @@ static void write_bootloader(CPUSPARCState *env, uint8_t *base, stl_p(p++, 0x01000000); /* nop */ } -static void main_cpu_reset(void *opaque) +static void leon3_cpu_reset(void *opaque) { - ResetData *s = (ResetData *)opaque; - CPUState *cpu = CPU(s->cpu); - CPUSPARCState *env = &s->cpu->env; + struct CPUResetData *info = (struct CPUResetData *) opaque; + int id = info->id; + ResetData *s = container_of(info, ResetData, info[id]); + CPUState *cpu = CPU(s->info[id].cpu); + CPUSPARCState *env = cpu_env(cpu); cpu_reset(cpu); - cpu->halted = 0; - env->pc = s->entry; - env->npc = s->entry + 4; - env->regbase[6] = s->sp; + cpu->halted = cpu->cpu_index != 0; + env->pc = s->entry; + env->npc = s->entry + 4; } static void leon3_cache_control_int(CPUSPARCState *env) @@ -164,9 +190,10 @@ static void leon3_cache_control_int(CPUSPARCState *env) } } -static void leon3_irq_ack(void *irq_manager, int intno) +static void leon3_irq_ack(CPUSPARCState *env, int intno) { - grlib_irqmp_ack((DeviceState *)irq_manager, intno); + CPUState *cpu = CPU(env_cpu(env)); + grlib_irqmp_ack(env->irq_manager, cpu->cpu_index, intno); } /* @@ -175,9 +202,10 @@ static void leon3_irq_ack(void *irq_manager, int intno) */ static void leon3_set_pil_in(void *opaque, int n, int level) { - CPUSPARCState *env = opaque; + DeviceState *cpu = opaque; + CPUState *cs = CPU(cpu); + CPUSPARCState *env = cpu_env(cs); uint32_t pil_in = level; - CPUState *cs; assert(env != NULL); @@ -193,7 +221,6 @@ static void leon3_set_pil_in(void *opaque, int n, int level) env->interrupt_index = TT_EXTINT | i; if (old_interrupt != env->interrupt_index) { - cs = env_cpu(env); trace_leon3_set_irq(i); cpu_interrupt(cs, CPU_INTERRUPT_HARD); } @@ -201,16 +228,29 @@ static void leon3_set_pil_in(void *opaque, int n, int level) } } } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) { - cs = env_cpu(env); trace_leon3_reset_irq(env->interrupt_index & 15); env->interrupt_index = 0; cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } -static void leon3_irq_manager(CPUSPARCState *env, void *irq_manager, int intno) +static void leon3_start_cpu_async_work(CPUState *cpu, run_on_cpu_data data) { - leon3_irq_ack(irq_manager, intno); + cpu->halted = 0; +} + +static void leon3_start_cpu(void *opaque, int n, int level) +{ + DeviceState *cpu = opaque; + CPUState *cs = CPU(cpu); + + assert(level == 1); + async_run_on_cpu(cs, leon3_start_cpu_async_work, RUN_ON_CPU_NULL); +} + +static void leon3_irq_manager(CPUSPARCState *env, int intno) +{ + leon3_irq_ack(env, intno); leon3_cache_control_int(env); } @@ -233,17 +273,23 @@ static void leon3_generic_hw_init(MachineState *machine) AHBPnp *ahb_pnp; APBPnp *apb_pnp; - /* Init CPU */ - cpu = SPARC_CPU(cpu_create(machine->cpu_type)); - env = &cpu->env; + reset_info = g_malloc0(sizeof(ResetData)); - cpu_sparc_set_id(env, 0); + for (i = 0; i < machine->smp.cpus; i++) { + /* Init CPU */ + cpu = SPARC_CPU(object_new(machine->cpu_type)); + qdev_init_gpio_in_named(DEVICE(cpu), leon3_start_cpu, "start_cpu", 1); + qdev_init_gpio_in_named(DEVICE(cpu), leon3_set_pil_in, "pil", 1); + qdev_realize(DEVICE(cpu), NULL, &error_fatal); + env = &cpu->env; - /* Reset data */ - reset_info = g_new0(ResetData, 1); - reset_info->cpu = cpu; - reset_info->sp = LEON3_RAM_OFFSET + ram_size; - qemu_register_reset(main_cpu_reset, reset_info); + cpu_sparc_set_id(env, i); + + /* Reset data */ + reset_info->info[i].id = i; + reset_info->info[i].cpu = cpu; + qemu_register_reset(leon3_cpu_reset, &reset_info->info[i]); + } ahb_pnp = GRLIB_AHB_PNP(qdev_new(TYPE_GRLIB_AHB_PNP)); sysbus_realize_and_unref(SYS_BUS_DEVICE(ahb_pnp), &error_fatal); @@ -261,14 +307,24 @@ static void leon3_generic_hw_init(MachineState *machine) /* Allocate IRQ manager */ irqmpdev = qdev_new(TYPE_GRLIB_IRQMP); - qdev_init_gpio_in_named_with_opaque(DEVICE(cpu), leon3_set_pil_in, - env, "pil", 1); - qdev_connect_gpio_out_named(irqmpdev, "grlib-irq", 0, - qdev_get_gpio_in_named(DEVICE(cpu), "pil", 0)); + object_property_set_int(OBJECT(irqmpdev), "ncpus", machine->smp.cpus, + &error_fatal); sysbus_realize_and_unref(SYS_BUS_DEVICE(irqmpdev), &error_fatal); + + for (i = 0; i < machine->smp.cpus; i++) { + cpu = reset_info->info[i].cpu; + env = &cpu->env; + qdev_connect_gpio_out_named(irqmpdev, "grlib-start-cpu", i, + qdev_get_gpio_in_named(DEVICE(cpu), + "start_cpu", 0)); + qdev_connect_gpio_out_named(irqmpdev, "grlib-irq", i, + qdev_get_gpio_in_named(DEVICE(cpu), + "pil", 0)); + env->irq_manager = irqmpdev; + env->qemu_irq_ack = leon3_irq_manager; + } + sysbus_mmio_map(SYS_BUS_DEVICE(irqmpdev), 0, LEON3_IRQMP_OFFSET); - env->irq_manager = irqmpdev; - env->qemu_irq_ack = leon3_irq_manager; grlib_apb_pnp_add_entry(apb_pnp, LEON3_IRQMP_OFFSET, 0xFFF, GRLIB_VENDOR_GAISLER, GRLIB_IRQMP_DEV, 2, 0, GRLIB_APBIO_AREA); @@ -339,13 +395,12 @@ static void leon3_generic_hw_init(MachineState *machine) * the machine in an initialized state through a little * bootloader. */ - uint8_t *bootloader_entry; - - bootloader_entry = memory_region_get_ram_ptr(prom); - write_bootloader(env, bootloader_entry, entry); - env->pc = LEON3_PROM_OFFSET; - env->npc = LEON3_PROM_OFFSET + 4; + write_bootloader(memory_region_get_ram_ptr(prom), entry); reset_info->entry = LEON3_PROM_OFFSET; + for (i = 0; i < machine->smp.cpus; i++) { + reset_info->info[i].cpu->env.pc = LEON3_PROM_OFFSET; + reset_info->info[i].cpu->env.npc = LEON3_PROM_OFFSET + 4; + } } } @@ -384,6 +439,7 @@ static void leon3_generic_machine_init(MachineClass *mc) mc->init = leon3_generic_hw_init; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("LEON3"); mc->default_ram_id = "leon3.ram"; + mc->max_cpus = MAX_CPUS; } DEFINE_MACHINE("leon3_generic", leon3_generic_machine_init) diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index d9288326d6..d52e6a7213 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -299,30 +299,42 @@ static void *iommu_init(hwaddr addr, uint32_t version, qemu_irq irq) static void *sparc32_dma_init(hwaddr dma_base, hwaddr esp_base, qemu_irq espdma_irq, - hwaddr le_base, qemu_irq ledma_irq, NICInfo *nd) + hwaddr le_base, qemu_irq ledma_irq, + MACAddr *mac) { DeviceState *dma; ESPDMADeviceState *espdma; LEDMADeviceState *ledma; SysBusESPState *esp; SysBusPCNetState *lance; + NICInfo *nd = qemu_find_nic_info("lance", true, NULL); dma = qdev_new(TYPE_SPARC32_DMA); espdma = SPARC32_ESPDMA_DEVICE(object_resolve_path_component( OBJECT(dma), "espdma")); - sysbus_connect_irq(SYS_BUS_DEVICE(espdma), 0, espdma_irq); esp = SYSBUS_ESP(object_resolve_path_component(OBJECT(espdma), "esp")); ledma = SPARC32_LEDMA_DEVICE(object_resolve_path_component( OBJECT(dma), "ledma")); - sysbus_connect_irq(SYS_BUS_DEVICE(ledma), 0, ledma_irq); lance = SYSBUS_PCNET(object_resolve_path_component( OBJECT(ledma), "lance")); - qdev_set_nic_properties(DEVICE(lance), nd); + + if (nd) { + qdev_set_nic_properties(DEVICE(lance), nd); + memcpy(mac->a, nd->macaddr.a, sizeof(mac->a)); + } else { + qemu_macaddr_default_if_unset(mac); + qdev_prop_set_macaddr(DEVICE(lance), "mac", mac->a); + } sysbus_realize_and_unref(SYS_BUS_DEVICE(dma), &error_fatal); + + sysbus_connect_irq(SYS_BUS_DEVICE(espdma), 0, espdma_irq); + + sysbus_connect_irq(SYS_BUS_DEVICE(ledma), 0, ledma_irq); + sysbus_mmio_map(SYS_BUS_DEVICE(dma), 0, dma_base); sysbus_mmio_map(SYS_BUS_DEVICE(esp), 0, esp_base); @@ -577,12 +589,9 @@ static void idreg_realize(DeviceState *ds, Error **errp) { IDRegState *s = MACIO_ID_REGISTER(ds); SysBusDevice *dev = SYS_BUS_DEVICE(ds); - Error *local_err = NULL; - memory_region_init_ram_nomigrate(&s->mem, OBJECT(ds), "sun4m.idreg", - sizeof(idreg_data), &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!memory_region_init_ram_nomigrate(&s->mem, OBJECT(ds), "sun4m.idreg", + sizeof(idreg_data), errp)) { return; } @@ -631,12 +640,9 @@ static void afx_realize(DeviceState *ds, Error **errp) { AFXState *s = TCX_AFX(ds); SysBusDevice *dev = SYS_BUS_DEVICE(ds); - Error *local_err = NULL; - memory_region_init_ram_nomigrate(&s->mem, OBJECT(ds), "sun4m.afx", 4, - &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!memory_region_init_ram_nomigrate(&s->mem, OBJECT(ds), "sun4m.afx", + 4, errp)) { return; } @@ -715,12 +721,9 @@ static void prom_realize(DeviceState *ds, Error **errp) { PROMState *s = OPENPROM(ds); SysBusDevice *dev = SYS_BUS_DEVICE(ds); - Error *local_err = NULL; - memory_region_init_ram_nomigrate(&s->prom, OBJECT(ds), "sun4m.prom", - PROM_SIZE_MAX, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!memory_region_init_ram_nomigrate(&s->prom, OBJECT(ds), "sun4m.prom", + PROM_SIZE_MAX, errp)) { return; } @@ -804,7 +807,7 @@ static void cpu_devinit(const char *cpu_type, unsigned int id, qemu_register_reset(sun4m_cpu_reset, cpu); object_property_set_bool(OBJECT(cpu), "start-powered-off", id != 0, - &error_fatal); + &error_abort); qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); cpu_sparc_set_id(env, id); *cpu_irqs = qemu_allocate_irqs(cpu_set_irq, cpu, MAX_PILS); @@ -832,7 +835,7 @@ static void sun4m_hw_init(MachineState *machine) unsigned int smp_cpus = machine->smp.cpus; unsigned int max_cpus = machine->smp.max_cpus; HostMemoryBackend *ram_memdev = machine->memdev; - NICInfo *nd = &nd_table[0]; + MACAddr hostid; if (machine->ram_size > hwdef->max_mem) { error_report("Too much memory for this machine: %" PRId64 "," @@ -893,10 +896,9 @@ static void sun4m_hw_init(MachineState *machine) hwdef->iommu_pad_base, hwdef->iommu_pad_len); } - qemu_check_nic_model(nd, TYPE_LANCE); sparc32_dma_init(hwdef->dma_base, hwdef->esp_base, slavio_irq[18], - hwdef->le_base, slavio_irq[16], nd); + hwdef->le_base, slavio_irq[16], &hostid); if (graphic_depth != 8 && graphic_depth != 24) { error_report("Unsupported depth: %d", graphic_depth); @@ -982,7 +984,7 @@ static void sun4m_hw_init(MachineState *machine) qdev_realize_and_unref(ms_kb_orgate, NULL, &error_fatal); sysbus_connect_irq(s, 0, qdev_get_gpio_in(ms_kb_orgate, 0)); sysbus_connect_irq(s, 1, qdev_get_gpio_in(ms_kb_orgate, 1)); - qdev_connect_gpio_out(DEVICE(ms_kb_orgate), 0, slavio_irq[14]); + qdev_connect_gpio_out(ms_kb_orgate, 0, slavio_irq[14]); dev = qdev_new(TYPE_ESCC); qdev_prop_set_uint32(dev, "disabled", 0); @@ -1004,7 +1006,7 @@ static void sun4m_hw_init(MachineState *machine) qdev_realize_and_unref(serial_orgate, NULL, &error_fatal); sysbus_connect_irq(s, 0, qdev_get_gpio_in(serial_orgate, 0)); sysbus_connect_irq(s, 1, qdev_get_gpio_in(serial_orgate, 1)); - qdev_connect_gpio_out(DEVICE(serial_orgate), 0, slavio_irq[15]); + qdev_connect_gpio_out(serial_orgate, 0, slavio_irq[15]); if (hwdef->apc_base) { apc_init(hwdef->apc_base, qemu_allocate_irq(cpu_halt_signal, NULL, 0)); @@ -1048,7 +1050,7 @@ static void sun4m_hw_init(MachineState *machine) machine->initrd_filename, machine->ram_size, &initrd_size); - nvram_init(nvram, (uint8_t *)&nd->macaddr, machine->kernel_cmdline, + nvram_init(nvram, hostid.a, machine->kernel_cmdline, machine->boot_config.order, machine->ram_size, kernel_size, graphic_width, graphic_height, graphic_depth, hwdef->nvram_machine_id, "Sun4m"); diff --git a/hw/sparc/sun4m_iommu.c b/hw/sparc/sun4m_iommu.c index 71f5465249..06703b1d96 100644 --- a/hw/sparc/sun4m_iommu.c +++ b/hw/sparc/sun4m_iommu.c @@ -96,10 +96,10 @@ #define IOMMU_AER_SBW 0x80000000 /* S-to-M asynchronous writes */ #define IOMMU_AER_MASK 0x801f000f -#define IOMMU_SBCFG0 (0x1010 >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG1 (0x1014 >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG2 (0x1018 >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG3 (0x101c >> 2) /* SBUS configration per-slot */ +#define IOMMU_SBCFG0 (0x1010 >> 2) /* SBUS configuration per-slot */ +#define IOMMU_SBCFG1 (0x1014 >> 2) /* SBUS configuration per-slot */ +#define IOMMU_SBCFG2 (0x1018 >> 2) /* SBUS configuration per-slot */ +#define IOMMU_SBCFG3 (0x101c >> 2) /* SBUS configuration per-slot */ #define IOMMU_SBCFG_SAB30 0x00010000 /* Phys-address bit 30 when bypass enabled */ #define IOMMU_SBCFG_BA16 0x00000004 /* Slave supports 16 byte bursts */ @@ -331,7 +331,7 @@ static const VMStateDescription vmstate_iommu = { .name = "iommu", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, IOMMUState, IOMMU_NREGS), VMSTATE_UINT64(iostart, IOMMUState), VMSTATE_END_OF_LIST() diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index ccad2c43a3..ab3c4ec346 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "block/block_int-common.h" #include "qemu/units.h" #include "cpu.h" #include "hw/boards.h" diff --git a/hw/sparc64/sparc64.c b/hw/sparc64/sparc64.c index 72f0849f50..3091cde586 100644 --- a/hw/sparc64/sparc64.c +++ b/hw/sparc64/sparc64.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "hw/boards.h" #include "hw/sparc/sparc64.h" @@ -271,9 +272,10 @@ SPARCCPU *sparc64_cpu_devinit(const char *cpu_type, uint64_t prom_addr) uint32_t stick_frequency = 100 * 1000000; uint32_t hstick_frequency = 100 * 1000000; - cpu = SPARC_CPU(cpu_create(cpu_type)); + cpu = SPARC_CPU(object_new(cpu_type)); qdev_init_gpio_in_named(DEVICE(cpu), sparc64_cpu_set_ivec_irq, "ivec-irq", IVEC_MAX); + qdev_realize(DEVICE(cpu), NULL, &error_fatal); env = &cpu->env; env->tick = cpu_timer_create("tick", cpu, tick_irq, diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index d1bc77d27e..cff6d5abaf 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -28,14 +28,14 @@ #include "qapi/error.h" #include "qemu/datadir.h" #include "cpu.h" +#include "hw/irq.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bridge.h" -#include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" #include "hw/qdev-properties.h" #include "hw/pci-host/sabre.h" #include "hw/char/serial.h" -#include "hw/char/parallel.h" +#include "hw/char/parallel-isa.h" #include "hw/rtc/m48t59.h" #include "migration/vmstate.h" #include "hw/input/i8042.h" @@ -66,7 +66,6 @@ #define PBM_PCI_IO_BASE (PBM_SPECIAL_BASE + 0x02000000ULL) #define PROM_FILENAME "openbios-sparc64" #define NVRAM_SIZE 0x2000 -#define MAX_IDE_BUS 2 #define BIOS_CFG_IOPORT 0x510 #define FW_CFG_SPARC64_WIDTH (FW_CFG_ARCH_LOCAL + 0x00) #define FW_CFG_SPARC64_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01) @@ -85,7 +84,8 @@ struct EbusState { PCIDevice parent_obj; ISABus *isa_bus; - qemu_irq isa_bus_irqs[ISA_NUM_IRQS]; + qemu_irq *isa_irqs_in; + qemu_irq isa_irqs_out[ISA_NUM_IRQS]; uint64_t console_serial_base; MemoryRegion bar0; MemoryRegion bar1; @@ -288,7 +288,7 @@ static const TypeInfo power_info = { static void ebus_isa_irq_handler(void *opaque, int n, int level) { EbusState *s = EBUS(opaque); - qemu_irq irq = s->isa_bus_irqs[n]; + qemu_irq irq = s->isa_irqs_out[n]; /* Pass ISA bus IRQs onto their gpio equivalent */ trace_ebus_isa_irq_handler(n, level); @@ -304,7 +304,6 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) ISADevice *isa_dev; SysBusDevice *sbd; DeviceState *dev; - qemu_irq *isa_irq; DriveInfo *fd[MAX_FD]; int i; @@ -316,9 +315,9 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) } /* ISA bus */ - isa_irq = qemu_allocate_irqs(ebus_isa_irq_handler, s, ISA_NUM_IRQS); - isa_bus_irqs(s->isa_bus, isa_irq); - qdev_init_gpio_out_named(DEVICE(s), s->isa_bus_irqs, "isa-irq", + s->isa_irqs_in = qemu_allocate_irqs(ebus_isa_irq_handler, s, ISA_NUM_IRQS); + isa_bus_register_input_irqs(s->isa_bus, s->isa_irqs_in); + qdev_init_gpio_out_named(DEVICE(s), s->isa_irqs_out, "isa-irq", ISA_NUM_IRQS); /* Serial ports */ @@ -334,7 +333,7 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) parallel_hds_isa_init(s->isa_bus, MAX_PARALLEL_PORTS); /* Keyboard */ - isa_create_simple(s->isa_bus, "i8042"); + isa_create_simple(s->isa_bus, TYPE_I8042); /* Floppy */ for (i = 0; i < MAX_FD; i++) { @@ -361,11 +360,16 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) pci_dev->config[0x09] = 0x00; // programming i/f pci_dev->config[0x0D] = 0x0a; // latency_timer - memory_region_init_alias(&s->bar0, OBJECT(s), "bar0", get_system_io(), - 0, 0x1000000); + /* + * BAR0 is accessed by OpenBSD but not for ebus device access: allow any + * memory access to this region to succeed which allows the OpenBSD kernel + * to boot. + */ + memory_region_init_io(&s->bar0, OBJECT(s), &unassigned_io_ops, s, + "bar0", 0x1000000); pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0); - memory_region_init_alias(&s->bar1, OBJECT(s), "bar1", get_system_io(), - 0, 0x8000); + memory_region_init_alias(&s->bar1, OBJECT(s), "bar1", + pci_address_space_io(pci_dev), 0, 0x8000); pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->bar1); } @@ -455,12 +459,9 @@ static void prom_realize(DeviceState *ds, Error **errp) { PROMState *s = OPENPROM(ds); SysBusDevice *dev = SYS_BUS_DEVICE(ds); - Error *local_err = NULL; - memory_region_init_ram_nomigrate(&s->prom, OBJECT(ds), "sun4u.prom", - PROM_SIZE_MAX, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!memory_region_init_ram_nomigrate(&s->prom, OBJECT(ds), "sun4u.prom", + PROM_SIZE_MAX, errp)) { return; } @@ -554,6 +555,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem, MachineState *machine, const struct hwdef *hwdef) { + MachineClass *mc = MACHINE_GET_CLASS(machine); SPARCCPU *cpu; Nvram *nvram; unsigned int i; @@ -608,11 +610,11 @@ static void sun4uv_init(MemoryRegion *address_space_mem, /* Only in-built Simba APBs can exist on the root bus, slot 0 on busA is reserved (leaving no slots free after on-board devices) however slots 0-3 are free on busB */ - pci_bus->slot_reserved_mask = 0xfffffffc; - pci_busA->slot_reserved_mask = 0xfffffff1; - pci_busB->slot_reserved_mask = 0xfffffff0; + pci_bus_set_slot_reserved_mask(pci_bus, 0xfffffffc); + pci_bus_set_slot_reserved_mask(pci_busA, 0xfffffff1); + pci_bus_set_slot_reserved_mask(pci_busB, 0xfffffff0); - ebus = pci_new_multifunction(PCI_DEVFN(1, 0), true, TYPE_EBUS); + ebus = pci_new_multifunction(PCI_DEVFN(1, 0), TYPE_EBUS); qdev_prop_set_uint64(DEVICE(ebus), "console-serial-base", hwdef->console_serial_base); pci_realize_and_unref(ebus, pci_busA, &error_fatal); @@ -642,30 +644,18 @@ static void sun4uv_init(MemoryRegion *address_space_mem, memset(&macaddr, 0, sizeof(MACAddr)); onboard_nic = false; - for (i = 0; i < nb_nics; i++) { - PCIBus *bus; - nd = &nd_table[i]; - - if (!nd->model || strcmp(nd->model, "sunhme") == 0) { - if (!onboard_nic) { - pci_dev = pci_new_multifunction(PCI_DEVFN(1, 1), - true, "sunhme"); - bus = pci_busA; - memcpy(&macaddr, &nd->macaddr.a, sizeof(MACAddr)); - onboard_nic = true; - } else { - pci_dev = pci_new(-1, "sunhme"); - bus = pci_busB; - } - } else { - pci_dev = pci_new(-1, nd->model); - bus = pci_busB; - } + nd = qemu_find_nic_info(mc->default_nic, true, NULL); + if (nd) { + pci_dev = pci_new_multifunction(PCI_DEVFN(1, 1), mc->default_nic); dev = &pci_dev->qdev; qdev_set_nic_properties(dev, nd); - pci_realize_and_unref(pci_dev, bus, &error_fatal); + pci_realize_and_unref(pci_dev, pci_busA, &error_fatal); + + memcpy(&macaddr, &nd->macaddr.a, sizeof(MACAddr)); + onboard_nic = true; } + pci_init_nic_devices(pci_busB, mc->default_nic); /* If we don't have an onboard NIC, grab a default MAC address so that * we have a valid machine id */ @@ -817,6 +807,8 @@ static void sun4u_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-UltraSparc-IIi"); mc->ignore_boot_device_suffixes = true; mc->default_display = "std"; + mc->default_nic = "sunhme"; + mc->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); fwc->get_dev_path = sun4u_fw_dev_path; } @@ -841,6 +833,8 @@ static void sun4v_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("Sun-UltraSparc-T1"); mc->default_display = "std"; + mc->default_nic = "sunhme"; + mc->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); } static const TypeInfo sun4v_type = { diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig index 7d90a02181..83ee53c1d0 100644 --- a/hw/ssi/Kconfig +++ b/hw/ssi/Kconfig @@ -20,3 +20,7 @@ config XILINX_SPIPS config STM32F2XX_SPI bool select SSI + +config BCM2835_SPI + bool + select SSI diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 68aa697164..6e1a84c197 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "hw/block/flash.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/log.h" @@ -388,7 +389,7 @@ static inline int aspeed_smc_flash_cmd(const AspeedSMCFlash *fl) static inline int aspeed_smc_flash_addr_width(const AspeedSMCFlash *fl) { const AspeedSMCState *s = fl->controller; - AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); + AspeedSMCClass *asc = fl->asc; if (asc->addr_width) { return asc->addr_width(s); @@ -420,7 +421,7 @@ static uint32_t aspeed_smc_check_segment_addr(const AspeedSMCFlash *fl, uint32_t addr) { const AspeedSMCState *s = fl->controller; - AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); + AspeedSMCClass *asc = fl->asc; AspeedSegments seg; asc->reg_to_segment(s, s->regs[R_SEG_ADDR0 + fl->cs], &seg); @@ -488,7 +489,7 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) switch (aspeed_smc_flash_mode(fl)) { case CTRL_USERMODE: for (i = 0; i < size; i++) { - ret |= ssi_transfer(s->spi, 0x0) << (8 * i); + ret |= (uint64_t) ssi_transfer(s->spi, 0x0) << (8 * i); } break; case CTRL_READMODE: @@ -497,7 +498,7 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) aspeed_smc_flash_setup(fl, addr); for (i = 0; i < size; i++) { - ret |= ssi_transfer(s->spi, 0x0) << (8 * i); + ret |= (uint64_t) ssi_transfer(s->spi, 0x0) << (8 * i); } aspeed_smc_flash_unselect(fl); @@ -692,6 +693,22 @@ static void aspeed_smc_reset(DeviceState *d) memset(s->regs, 0, sizeof s->regs); } + for (i = 0; i < asc->cs_num_max; i++) { + DeviceState *dev = ssi_get_cs(s->spi, i); + if (dev) { + Object *o = OBJECT(dev); + + if (!object_dynamic_cast(o, TYPE_M25P80)) { + warn_report("Aspeed SMC %s.%d : Invalid %s device type", + BUS(s->spi)->name, i, object_get_typename(o)); + continue; + } + + qemu_irq cs_line = qdev_get_gpio_in_named(dev, SSI_GPIO_CS, 0); + qdev_connect_gpio_out_named(DEVICE(s), "cs", i, cs_line); + } + } + /* Unselect all peripherals */ for (i = 0; i < asc->cs_num_max; ++i) { s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE; @@ -1134,10 +1151,7 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) /* Setup cs_lines for peripherals */ s->cs_lines = g_new0(qemu_irq, asc->cs_num_max); - - for (i = 0; i < asc->cs_num_max; ++i) { - sysbus_init_irq(sbd, &s->cs_lines[i]); - } + qdev_init_gpio_out_named(DEVICE(s), s->cs_lines, "cs", asc->cs_num_max); /* The memory region for the controller registers */ memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_ops, s, @@ -1196,7 +1210,7 @@ static const VMStateDescription vmstate_aspeed_smc = { .name = "aspeed.smc", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX), VMSTATE_UINT8(snoop_index, AspeedSMCState), VMSTATE_UINT8(snoop_dummies, AspeedSMCState), @@ -1234,7 +1248,6 @@ static const TypeInfo aspeed_smc_info = { static void aspeed_smc_flash_realize(DeviceState *dev, Error **errp) { AspeedSMCFlash *s = ASPEED_SMC_FLASH(dev); - AspeedSMCClass *asc; g_autofree char *name = g_strdup_printf(TYPE_ASPEED_SMC_FLASH ".%d", s->cs); if (!s->controller) { @@ -1242,14 +1255,14 @@ static void aspeed_smc_flash_realize(DeviceState *dev, Error **errp) return; } - asc = ASPEED_SMC_GET_CLASS(s->controller); + s->asc = ASPEED_SMC_GET_CLASS(s->controller); /* * Use the default segment value to size the memory region. This * can be changed by FW at runtime. */ memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_flash_ops, - s, name, asc->segments[s->cs].size); + s, name, s->asc->segments[s->cs].size); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); } diff --git a/hw/ssi/bcm2835_spi.c b/hw/ssi/bcm2835_spi.c new file mode 100644 index 0000000000..6ecb42d4e3 --- /dev/null +++ b/hw/ssi/bcm2835_spi.c @@ -0,0 +1,288 @@ +/* + * BCM2835 SPI Master Controller + * + * Copyright (c) 2024 Rayhan Faizel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/fifo8.h" +#include "hw/ssi/bcm2835_spi.h" +#include "hw/irq.h" +#include "migration/vmstate.h" + +static void bcm2835_spi_update_int(BCM2835SPIState *s) +{ + int do_interrupt = 0; + + /* Interrupt on DONE */ + if (s->cs & BCM2835_SPI_CS_INTD && s->cs & BCM2835_SPI_CS_DONE) { + do_interrupt = 1; + } + /* Interrupt on RXR */ + if (s->cs & BCM2835_SPI_CS_INTR && s->cs & BCM2835_SPI_CS_RXR) { + do_interrupt = 1; + } + qemu_set_irq(s->irq, do_interrupt); +} + +static void bcm2835_spi_update_rx_flags(BCM2835SPIState *s) +{ + /* Set RXD if RX FIFO is non empty */ + if (!fifo8_is_empty(&s->rx_fifo)) { + s->cs |= BCM2835_SPI_CS_RXD; + } else { + s->cs &= ~BCM2835_SPI_CS_RXD; + } + + /* Set RXF if RX FIFO is full */ + if (fifo8_is_full(&s->rx_fifo)) { + s->cs |= BCM2835_SPI_CS_RXF; + } else { + s->cs &= ~BCM2835_SPI_CS_RXF; + } + + /* Set RXR if RX FIFO is 3/4th used or above */ + if (fifo8_num_used(&s->rx_fifo) >= FIFO_SIZE_3_4) { + s->cs |= BCM2835_SPI_CS_RXR; + } else { + s->cs &= ~BCM2835_SPI_CS_RXR; + } +} + +static void bcm2835_spi_update_tx_flags(BCM2835SPIState *s) +{ + /* Set TXD if TX FIFO is not full */ + if (fifo8_is_full(&s->tx_fifo)) { + s->cs &= ~BCM2835_SPI_CS_TXD; + } else { + s->cs |= BCM2835_SPI_CS_TXD; + } + + /* Set DONE if in TA mode and TX FIFO is empty */ + if (fifo8_is_empty(&s->tx_fifo) && s->cs & BCM2835_SPI_CS_TA) { + s->cs |= BCM2835_SPI_CS_DONE; + } else { + s->cs &= ~BCM2835_SPI_CS_DONE; + } +} + +static void bcm2835_spi_flush_tx_fifo(BCM2835SPIState *s) +{ + uint8_t tx_byte, rx_byte; + + while (!fifo8_is_empty(&s->tx_fifo) && !fifo8_is_full(&s->rx_fifo)) { + tx_byte = fifo8_pop(&s->tx_fifo); + rx_byte = ssi_transfer(s->bus, tx_byte); + fifo8_push(&s->rx_fifo, rx_byte); + } + + bcm2835_spi_update_tx_flags(s); + bcm2835_spi_update_rx_flags(s); +} + +static uint64_t bcm2835_spi_read(void *opaque, hwaddr addr, unsigned size) +{ + BCM2835SPIState *s = opaque; + uint32_t readval = 0; + + switch (addr) { + case BCM2835_SPI_CS: + readval = s->cs & 0xffffffff; + break; + case BCM2835_SPI_FIFO: + bcm2835_spi_flush_tx_fifo(s); + if (s->cs & BCM2835_SPI_CS_RXD) { + readval = fifo8_pop(&s->rx_fifo); + bcm2835_spi_update_rx_flags(s); + } + + bcm2835_spi_update_int(s); + break; + case BCM2835_SPI_CLK: + readval = s->clk & 0xffff; + break; + case BCM2835_SPI_DLEN: + readval = s->dlen & 0xffff; + break; + case BCM2835_SPI_LTOH: + readval = s->ltoh & 0xf; + break; + case BCM2835_SPI_DC: + readval = s->dc & 0xffffffff; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); + } + return readval; +} + +static void bcm2835_spi_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + BCM2835SPIState *s = opaque; + + switch (addr) { + case BCM2835_SPI_CS: + s->cs = (value & ~RO_MASK) | (s->cs & RO_MASK); + if (!(s->cs & BCM2835_SPI_CS_TA)) { + /* Clear DONE and RXR if TA is off */ + s->cs &= ~(BCM2835_SPI_CS_DONE); + s->cs &= ~(BCM2835_SPI_CS_RXR); + } + + /* Clear RX FIFO */ + if (s->cs & BCM2835_SPI_CLEAR_RX) { + fifo8_reset(&s->rx_fifo); + bcm2835_spi_update_rx_flags(s); + } + + /* Clear TX FIFO*/ + if (s->cs & BCM2835_SPI_CLEAR_TX) { + fifo8_reset(&s->tx_fifo); + bcm2835_spi_update_tx_flags(s); + } + + /* Set Transfer Active */ + if (s->cs & BCM2835_SPI_CS_TA) { + bcm2835_spi_update_tx_flags(s); + } + + if (s->cs & BCM2835_SPI_CS_DMAEN) { + qemu_log_mask(LOG_UNIMP, "%s: " \ + "DMA not supported\n", __func__); + } + + if (s->cs & BCM2835_SPI_CS_LEN) { + qemu_log_mask(LOG_UNIMP, "%s: " \ + "LoSSI not supported\n", __func__); + } + + bcm2835_spi_update_int(s); + break; + case BCM2835_SPI_FIFO: + /* + * According to documentation, writes to FIFO without TA controls + * CS and DLEN registers. This is supposed to be used in DMA mode + * which is currently unimplemented. Moreover, Linux does not make + * use of this and directly modifies the CS and DLEN registers. + */ + if (s->cs & BCM2835_SPI_CS_TA) { + if (s->cs & BCM2835_SPI_CS_TXD) { + fifo8_push(&s->tx_fifo, value & 0xff); + bcm2835_spi_update_tx_flags(s); + } + + bcm2835_spi_flush_tx_fifo(s); + bcm2835_spi_update_int(s); + } + break; + case BCM2835_SPI_CLK: + s->clk = value & 0xffff; + break; + case BCM2835_SPI_DLEN: + s->dlen = value & 0xffff; + break; + case BCM2835_SPI_LTOH: + s->ltoh = value & 0xf; + break; + case BCM2835_SPI_DC: + s->dc = value & 0xffffffff; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); + } +} + +static const MemoryRegionOps bcm2835_spi_ops = { + .read = bcm2835_spi_read, + .write = bcm2835_spi_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void bcm2835_spi_realize(DeviceState *dev, Error **errp) +{ + BCM2835SPIState *s = BCM2835_SPI(dev); + s->bus = ssi_create_bus(dev, "spi"); + + memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_spi_ops, s, + TYPE_BCM2835_SPI, 0x18); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + + fifo8_create(&s->tx_fifo, FIFO_SIZE); + fifo8_create(&s->rx_fifo, FIFO_SIZE); +} +static void bcm2835_spi_reset(DeviceState *dev) +{ + BCM2835SPIState *s = BCM2835_SPI(dev); + + fifo8_reset(&s->tx_fifo); + fifo8_reset(&s->rx_fifo); + + /* Reset values according to BCM2835 Peripheral Documentation */ + s->cs = BCM2835_SPI_CS_TXD | BCM2835_SPI_CS_REN; + s->clk = 0; + s->dlen = 0; + s->ltoh = 0x1; + s->dc = 0x30201020; +} + +static const VMStateDescription vmstate_bcm2835_spi = { + .name = TYPE_BCM2835_SPI, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_FIFO8(tx_fifo, BCM2835SPIState), + VMSTATE_FIFO8(rx_fifo, BCM2835SPIState), + VMSTATE_UINT32(cs, BCM2835SPIState), + VMSTATE_UINT32(clk, BCM2835SPIState), + VMSTATE_UINT32(dlen, BCM2835SPIState), + VMSTATE_UINT32(ltoh, BCM2835SPIState), + VMSTATE_UINT32(dc, BCM2835SPIState), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_spi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = bcm2835_spi_reset; + dc->realize = bcm2835_spi_realize; + dc->vmsd = &vmstate_bcm2835_spi; +} + +static const TypeInfo bcm2835_spi_info = { + .name = TYPE_BCM2835_SPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835SPIState), + .class_init = bcm2835_spi_class_init, +}; + +static void bcm2835_spi_register_types(void) +{ + type_register_static(&bcm2835_spi_info); +} + +type_init(bcm2835_spi_register_types) diff --git a/hw/ssi/ibex_spi_host.c b/hw/ssi/ibex_spi_host.c index d14580b409..863b5fd60e 100644 --- a/hw/ssi/ibex_spi_host.c +++ b/hw/ssi/ibex_spi_host.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/module.h" +#include "hw/registerfields.h" #include "hw/ssi/ibex_spi_host.h" #include "hw/irq.h" #include "hw/qdev-properties.h" @@ -93,7 +94,7 @@ REG32(ERROR_STATUS, 0x30) FIELD(ERROR_STATUS, CMDINVAL, 3, 1) FIELD(ERROR_STATUS, CSIDINVAL, 4, 1) FIELD(ERROR_STATUS, ACCESSINVAL, 5, 1) -REG32(EVENT_ENABLE, 0x30) +REG32(EVENT_ENABLE, 0x34) FIELD(EVENT_ENABLE, RXFULL, 0, 1) FIELD(EVENT_ENABLE, TXEMPTY, 1, 1) FIELD(EVENT_ENABLE, RXWM, 2, 1) @@ -108,18 +109,22 @@ static inline uint8_t div4_round_up(uint8_t dividend) static void ibex_spi_rxfifo_reset(IbexSPIHostState *s) { + uint32_t data = s->regs[IBEX_SPI_HOST_STATUS]; /* Empty the RX FIFO and assert RXEMPTY */ fifo8_reset(&s->rx_fifo); - s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXFULL_MASK; - s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXEMPTY_MASK; + data = FIELD_DP32(data, STATUS, RXFULL, 0); + data = FIELD_DP32(data, STATUS, RXEMPTY, 1); + s->regs[IBEX_SPI_HOST_STATUS] = data; } static void ibex_spi_txfifo_reset(IbexSPIHostState *s) { + uint32_t data = s->regs[IBEX_SPI_HOST_STATUS]; /* Empty the TX FIFO and assert TXEMPTY */ fifo8_reset(&s->tx_fifo); - s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXFULL_MASK; - s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXEMPTY_MASK; + data = FIELD_DP32(data, STATUS, TXFULL, 0); + data = FIELD_DP32(data, STATUS, TXEMPTY, 1); + s->regs[IBEX_SPI_HOST_STATUS] = data; } static void ibex_spi_host_reset(DeviceState *dev) @@ -162,80 +167,81 @@ static void ibex_spi_host_reset(DeviceState *dev) */ static void ibex_spi_host_irq(IbexSPIHostState *s) { - bool error_en = s->regs[IBEX_SPI_HOST_INTR_ENABLE] - & R_INTR_ENABLE_ERROR_MASK; - bool event_en = s->regs[IBEX_SPI_HOST_INTR_ENABLE] - & R_INTR_ENABLE_SPI_EVENT_MASK; - bool err_pending = s->regs[IBEX_SPI_HOST_INTR_STATE] - & R_INTR_STATE_ERROR_MASK; - bool status_pending = s->regs[IBEX_SPI_HOST_INTR_STATE] - & R_INTR_STATE_SPI_EVENT_MASK; + uint32_t intr_test_reg = s->regs[IBEX_SPI_HOST_INTR_TEST]; + uint32_t intr_en_reg = s->regs[IBEX_SPI_HOST_INTR_ENABLE]; + uint32_t intr_state_reg = s->regs[IBEX_SPI_HOST_INTR_STATE]; + + uint32_t err_en_reg = s->regs[IBEX_SPI_HOST_ERROR_ENABLE]; + uint32_t event_en_reg = s->regs[IBEX_SPI_HOST_EVENT_ENABLE]; + uint32_t err_status_reg = s->regs[IBEX_SPI_HOST_ERROR_STATUS]; + uint32_t status_reg = s->regs[IBEX_SPI_HOST_STATUS]; + + + bool error_en = FIELD_EX32(intr_en_reg, INTR_ENABLE, ERROR); + bool event_en = FIELD_EX32(intr_en_reg, INTR_ENABLE, SPI_EVENT); + bool err_pending = FIELD_EX32(intr_state_reg, INTR_STATE, ERROR); + bool status_pending = FIELD_EX32(intr_state_reg, INTR_STATE, SPI_EVENT); + int err_irq = 0, event_irq = 0; - /* Error IRQ enabled and Error IRQ Cleared*/ + /* Error IRQ enabled and Error IRQ Cleared */ if (error_en && !err_pending) { /* Event enabled, Interrupt Test Error */ - if (s->regs[IBEX_SPI_HOST_INTR_TEST] & R_INTR_TEST_ERROR_MASK) { + if (FIELD_EX32(intr_test_reg, INTR_TEST, ERROR)) { err_irq = 1; - } else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE] - & R_ERROR_ENABLE_CMDBUSY_MASK) && - s->regs[IBEX_SPI_HOST_ERROR_STATUS] - & R_ERROR_STATUS_CMDBUSY_MASK) { + } else if (FIELD_EX32(err_en_reg, ERROR_ENABLE, CMDBUSY) && + FIELD_EX32(err_status_reg, ERROR_STATUS, CMDBUSY)) { /* Wrote to COMMAND when not READY */ err_irq = 1; - } else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE] - & R_ERROR_ENABLE_CMDINVAL_MASK) && - s->regs[IBEX_SPI_HOST_ERROR_STATUS] - & R_ERROR_STATUS_CMDINVAL_MASK) { + } else if (FIELD_EX32(err_en_reg, ERROR_ENABLE, CMDINVAL) && + FIELD_EX32(err_status_reg, ERROR_STATUS, CMDINVAL)) { /* Invalid command segment */ err_irq = 1; - } else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE] - & R_ERROR_ENABLE_CSIDINVAL_MASK) && - s->regs[IBEX_SPI_HOST_ERROR_STATUS] - & R_ERROR_STATUS_CSIDINVAL_MASK) { + } else if (FIELD_EX32(err_en_reg, ERROR_ENABLE, CSIDINVAL) && + FIELD_EX32(err_status_reg, ERROR_STATUS, CSIDINVAL)) { /* Invalid value for CSID */ err_irq = 1; } if (err_irq) { s->regs[IBEX_SPI_HOST_INTR_STATE] |= R_INTR_STATE_ERROR_MASK; } - qemu_set_irq(s->host_err, err_irq); } + qemu_set_irq(s->host_err, err_irq); + /* Event IRQ Enabled and Event IRQ Cleared */ if (event_en && !status_pending) { - if (s->regs[IBEX_SPI_HOST_INTR_TEST] & R_INTR_TEST_SPI_EVENT_MASK) { + if (FIELD_EX32(intr_test_reg, INTR_STATE, SPI_EVENT)) { /* Event enabled, Interrupt Test Event */ event_irq = 1; - } else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE] - & R_EVENT_ENABLE_READY_MASK) && - (s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_READY_MASK)) { + } else if (FIELD_EX32(event_en_reg, EVENT_ENABLE, READY) && + FIELD_EX32(status_reg, STATUS, READY)) { /* SPI Host ready for next command */ event_irq = 1; - } else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE] - & R_EVENT_ENABLE_TXEMPTY_MASK) && - (s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_TXEMPTY_MASK)) { + } else if (FIELD_EX32(event_en_reg, EVENT_ENABLE, TXEMPTY) && + FIELD_EX32(status_reg, STATUS, TXEMPTY)) { /* SPI TXEMPTY, TXFIFO drained */ event_irq = 1; - } else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE] - & R_EVENT_ENABLE_RXFULL_MASK) && - (s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_RXFULL_MASK)) { + } else if (FIELD_EX32(event_en_reg, EVENT_ENABLE, RXFULL) && + FIELD_EX32(status_reg, STATUS, RXFULL)) { /* SPI RXFULL, RXFIFO full */ event_irq = 1; } if (event_irq) { s->regs[IBEX_SPI_HOST_INTR_STATE] |= R_INTR_STATE_SPI_EVENT_MASK; } - qemu_set_irq(s->event, event_irq); } + + qemu_set_irq(s->event, event_irq); } static void ibex_spi_host_transfer(IbexSPIHostState *s) { - uint32_t rx, tx; + uint32_t rx, tx, data; /* Get num of one byte transfers */ - uint8_t segment_len = ((s->regs[IBEX_SPI_HOST_COMMAND] & R_COMMAND_LEN_MASK) - >> R_COMMAND_LEN_SHIFT); + uint8_t segment_len = FIELD_EX32(s->regs[IBEX_SPI_HOST_COMMAND], + COMMAND, LEN); + while (segment_len > 0) { if (fifo8_is_empty(&s->tx_fifo)) { /* Assert Stall */ @@ -262,22 +268,21 @@ static void ibex_spi_host_transfer(IbexSPIHostState *s) --segment_len; } + data = s->regs[IBEX_SPI_HOST_STATUS]; /* Assert Ready */ - s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_READY_MASK; + data = FIELD_DP32(data, STATUS, READY, 1); /* Set RXQD */ - s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXQD_MASK; - s->regs[IBEX_SPI_HOST_STATUS] |= (R_STATUS_RXQD_MASK - & div4_round_up(segment_len)); + data = FIELD_DP32(data, STATUS, RXQD, div4_round_up(segment_len)); /* Set TXQD */ - s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXQD_MASK; - s->regs[IBEX_SPI_HOST_STATUS] |= (fifo8_num_used(&s->tx_fifo) / 4) - & R_STATUS_TXQD_MASK; + data = FIELD_DP32(data, STATUS, TXQD, fifo8_num_used(&s->tx_fifo) / 4); /* Clear TXFULL */ - s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXFULL_MASK; - /* Assert TXEMPTY and drop remaining bytes that exceed segment_len */ - ibex_spi_txfifo_reset(s); + data = FIELD_DP32(data, STATUS, TXFULL, 0); /* Reset RXEMPTY */ - s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXEMPTY_MASK; + data = FIELD_DP32(data, STATUS, RXEMPTY, 0); + /* Update register status */ + s->regs[IBEX_SPI_HOST_STATUS] = data; + /* Drop remaining bytes that exceed segment_len */ + ibex_spi_txfifo_reset(s); ibex_spi_host_irq(s); } @@ -340,7 +345,7 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr, { IbexSPIHostState *s = opaque; uint32_t val32 = val64; - uint32_t shift_mask = 0xff; + uint32_t shift_mask = 0xff, status = 0, data = 0; uint8_t txqd_len; trace_ibex_spi_host_write(addr, size, val64); @@ -350,7 +355,17 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr, switch (addr) { /* Skipping any R/O registers */ - case IBEX_SPI_HOST_INTR_STATE...IBEX_SPI_HOST_INTR_ENABLE: + case IBEX_SPI_HOST_INTR_STATE: + /* rw1c status register */ + if (FIELD_EX32(val32, INTR_STATE, ERROR)) { + data = FIELD_DP32(data, INTR_STATE, ERROR, 0); + } + if (FIELD_EX32(val32, INTR_STATE, SPI_EVENT)) { + data = FIELD_DP32(data, INTR_STATE, SPI_EVENT, 0); + } + s->regs[addr] = data; + break; + case IBEX_SPI_HOST_INTR_ENABLE: s->regs[addr] = val32; break; case IBEX_SPI_HOST_INTR_TEST: @@ -397,22 +412,24 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr, s->regs[addr] = val32; /* STALL, IP not enabled */ - if (!(s->regs[IBEX_SPI_HOST_CONTROL] & R_CONTROL_SPIEN_MASK)) { + if (!(FIELD_EX32(s->regs[IBEX_SPI_HOST_CONTROL], + CONTROL, SPIEN))) { return; } /* SPI not ready, IRQ Error */ - if (!(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_READY_MASK)) { + if (!(FIELD_EX32(s->regs[IBEX_SPI_HOST_STATUS], + STATUS, READY))) { s->regs[IBEX_SPI_HOST_ERROR_STATUS] |= R_ERROR_STATUS_CMDBUSY_MASK; ibex_spi_host_irq(s); return; } + /* Assert Not Ready */ s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_READY_MASK; - if (((val32 & R_COMMAND_DIRECTION_MASK) >> R_COMMAND_DIRECTION_SHIFT) - != BIDIRECTIONAL_TRANSFER) { - qemu_log_mask(LOG_UNIMP, + if (FIELD_EX32(val32, COMMAND, DIRECTION) != BIDIRECTIONAL_TRANSFER) { + qemu_log_mask(LOG_UNIMP, "%s: Rx Only/Tx Only are not supported\n", __func__); } @@ -434,7 +451,7 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr, case IBEX_SPI_HOST_TXDATA: /* * This is a hardware `feature` where - * the first word written TXDATA after init is omitted entirely + * the first word written to TXDATA after init is omitted entirely */ if (s->init_status) { s->init_status = false; @@ -452,8 +469,8 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr, return; } /* Byte ordering is set by the IP */ - if ((s->regs[IBEX_SPI_HOST_STATUS] & - R_STATUS_BYTEORDER_MASK) == 0) { + status = s->regs[IBEX_SPI_HOST_STATUS]; + if (FIELD_EX32(status, STATUS, BYTEORDER) == 0) { /* LE: LSB transmitted first (default for ibex processor) */ shift_mask = 0xff << (i * 8); } else { @@ -464,18 +481,18 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr, fifo8_push(&s->tx_fifo, (val32 & shift_mask) >> (i * 8)); } - + status = s->regs[IBEX_SPI_HOST_STATUS]; /* Reset TXEMPTY */ - s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXEMPTY_MASK; + status = FIELD_DP32(status, STATUS, TXEMPTY, 0); /* Update TXQD */ - txqd_len = (s->regs[IBEX_SPI_HOST_STATUS] & - R_STATUS_TXQD_MASK) >> R_STATUS_TXQD_SHIFT; + txqd_len = FIELD_EX32(status, STATUS, TXQD); /* Partial bytes (size < 4) are padded, in words. */ txqd_len += 1; - s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXQD_MASK; - s->regs[IBEX_SPI_HOST_STATUS] |= txqd_len; + status = FIELD_DP32(status, STATUS, TXQD, txqd_len); /* Assert Ready */ - s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_READY_MASK; + status = FIELD_DP32(status, STATUS, READY, 1); + /* Update register status */ + s->regs[IBEX_SPI_HOST_STATUS] = status; break; case IBEX_SPI_HOST_ERROR_ENABLE: s->regs[addr] = val32; @@ -487,11 +504,31 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr, break; case IBEX_SPI_HOST_ERROR_STATUS: /* - * Indicates that any errors that have occurred. + * Indicates any errors that have occurred. * When an error occurs, the corresponding bit must be cleared * here before issuing any further commands */ - s->regs[addr] = val32; + status = s->regs[addr]; + /* rw1c status register */ + if (FIELD_EX32(val32, ERROR_STATUS, CMDBUSY)) { + status = FIELD_DP32(status, ERROR_STATUS, CMDBUSY, 0); + } + if (FIELD_EX32(val32, ERROR_STATUS, OVERFLOW)) { + status = FIELD_DP32(status, ERROR_STATUS, OVERFLOW, 0); + } + if (FIELD_EX32(val32, ERROR_STATUS, UNDERFLOW)) { + status = FIELD_DP32(status, ERROR_STATUS, UNDERFLOW, 0); + } + if (FIELD_EX32(val32, ERROR_STATUS, CMDINVAL)) { + status = FIELD_DP32(status, ERROR_STATUS, CMDINVAL, 0); + } + if (FIELD_EX32(val32, ERROR_STATUS, CSIDINVAL)) { + status = FIELD_DP32(status, ERROR_STATUS, CSIDINVAL, 0); + } + if (FIELD_EX32(val32, ERROR_STATUS, ACCESSINVAL)) { + status = FIELD_DP32(status, ERROR_STATUS, ACCESSINVAL, 0); + } + s->regs[addr] = status; break; case IBEX_SPI_HOST_EVENT_ENABLE: /* Controls which classes of SPI events raise an interrupt. */ @@ -533,7 +570,7 @@ static const VMStateDescription vmstate_ibex = { .name = TYPE_IBEX_SPI_HOST, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, IbexSPIHostState, IBEX_SPI_HOST_MAX_REGS), VMSTATE_VARRAY_UINT32(config_opts, IbexSPIHostState, num_cs, 0, vmstate_info_uint32, uint32_t), diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c index 189423bb3a..d8a7583ff3 100644 --- a/hw/ssi/imx_spi.c +++ b/hw/ssi/imx_spi.c @@ -62,7 +62,7 @@ static const VMStateDescription vmstate_imx_spi = { .name = TYPE_IMX_SPI, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_FIFO32(tx_fifo, IMXSPIState), VMSTATE_FIFO32(rx_fifo, IMXSPIState), VMSTATE_INT16(burst_length, IMXSPIState), diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build index 702aa5e4df..b999aeb027 100644 --- a/hw/ssi/meson.build +++ b/hw/ssi/meson.build @@ -1,13 +1,14 @@ -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c')) -softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-spi.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c')) -softmmu_ss.add(when: 'CONFIG_PL022', if_true: files('pl022.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_SPI', if_true: files('sifive_spi.c')) -softmmu_ss.add(when: 'CONFIG_SSI', if_true: files('ssi.c')) -softmmu_ss.add(when: 'CONFIG_STM32F2XX_SPI', if_true: files('stm32f2xx_spi.c')) -softmmu_ss.add(when: 'CONFIG_XILINX_SPI', if_true: files('xilinx_spi.c')) -softmmu_ss.add(when: 'CONFIG_XILINX_SPIPS', if_true: files('xilinx_spips.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-ospi.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_spi.c')) -softmmu_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_spi_host.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c')) +system_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-spi.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c', 'npcm_pspi.c')) +system_ss.add(when: 'CONFIG_PL022', if_true: files('pl022.c')) +system_ss.add(when: 'CONFIG_SIFIVE_SPI', if_true: files('sifive_spi.c')) +system_ss.add(when: 'CONFIG_SSI', if_true: files('ssi.c')) +system_ss.add(when: 'CONFIG_STM32F2XX_SPI', if_true: files('stm32f2xx_spi.c')) +system_ss.add(when: 'CONFIG_XILINX_SPI', if_true: files('xilinx_spi.c')) +system_ss.add(when: 'CONFIG_XILINX_SPIPS', if_true: files('xilinx_spips.c')) +system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-ospi.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_spi.c')) +system_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_spi_host.c')) +system_ss.add(when: 'CONFIG_BCM2835_SPI', if_true: files('bcm2835_spi.c')) diff --git a/hw/ssi/mss-spi.c b/hw/ssi/mss-spi.c index b2432c5a13..1d25ba23aa 100644 --- a/hw/ssi/mss-spi.c +++ b/hw/ssi/mss-spi.c @@ -390,7 +390,7 @@ static const VMStateDescription vmstate_mss_spi = { .name = TYPE_MSS_SPI, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_FIFO32(tx_fifo, MSSSpiState), VMSTATE_FIFO32(rx_fifo, MSSSpiState), VMSTATE_UINT32_ARRAY(regs, MSSSpiState, R_SPI_MAX), diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c index 4eedb2927e..81dd972ee8 100644 --- a/hw/ssi/npcm7xx_fiu.c +++ b/hw/ssi/npcm7xx_fiu.c @@ -534,7 +534,7 @@ static const VMStateDescription vmstate_npcm7xx_fiu = { .name = "npcm7xx-fiu", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(active_cs, NPCM7xxFIUState), VMSTATE_UINT32_ARRAY(regs, NPCM7xxFIUState, NPCM7XX_FIU_NR_REGS), VMSTATE_END_OF_LIST(), diff --git a/hw/ssi/npcm_pspi.c b/hw/ssi/npcm_pspi.c new file mode 100644 index 0000000000..41a5323530 --- /dev/null +++ b/hw/ssi/npcm_pspi.c @@ -0,0 +1,221 @@ +/* + * Nuvoton NPCM Peripheral SPI Module (PSPI) + * + * Copyright 2023 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" + +#include "hw/irq.h" +#include "hw/registerfields.h" +#include "hw/ssi/npcm_pspi.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/units.h" + +#include "trace.h" + +REG16(PSPI_DATA, 0x0) +REG16(PSPI_CTL1, 0x2) + FIELD(PSPI_CTL1, SPIEN, 0, 1) + FIELD(PSPI_CTL1, MOD, 2, 1) + FIELD(PSPI_CTL1, EIR, 5, 1) + FIELD(PSPI_CTL1, EIW, 6, 1) + FIELD(PSPI_CTL1, SCM, 7, 1) + FIELD(PSPI_CTL1, SCIDL, 8, 1) + FIELD(PSPI_CTL1, SCDV, 9, 7) +REG16(PSPI_STAT, 0x4) + FIELD(PSPI_STAT, BSY, 0, 1) + FIELD(PSPI_STAT, RBF, 1, 1) + +static void npcm_pspi_update_irq(NPCMPSPIState *s) +{ + int level = 0; + + /* Only fire IRQ when the module is enabled. */ + if (FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, SPIEN)) { + /* Update interrupt as BSY is cleared. */ + if ((!FIELD_EX16(s->regs[R_PSPI_STAT], PSPI_STAT, BSY)) && + FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, EIW)) { + level = 1; + } + + /* Update interrupt as RBF is set. */ + if (FIELD_EX16(s->regs[R_PSPI_STAT], PSPI_STAT, RBF) && + FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, EIR)) { + level = 1; + } + } + qemu_set_irq(s->irq, level); +} + +static uint16_t npcm_pspi_read_data(NPCMPSPIState *s) +{ + uint16_t value = s->regs[R_PSPI_DATA]; + + /* Clear stat bits as the value are read out. */ + s->regs[R_PSPI_STAT] = 0; + + return value; +} + +static void npcm_pspi_write_data(NPCMPSPIState *s, uint16_t data) +{ + uint16_t value = 0; + + if (FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, MOD)) { + value = ssi_transfer(s->spi, extract16(data, 8, 8)) << 8; + } + value |= ssi_transfer(s->spi, extract16(data, 0, 8)); + s->regs[R_PSPI_DATA] = value; + + /* Mark data as available */ + s->regs[R_PSPI_STAT] = R_PSPI_STAT_BSY_MASK | R_PSPI_STAT_RBF_MASK; +} + +/* Control register read handler. */ +static uint64_t npcm_pspi_ctrl_read(void *opaque, hwaddr addr, + unsigned int size) +{ + NPCMPSPIState *s = opaque; + uint16_t value; + + switch (addr) { + case A_PSPI_DATA: + value = npcm_pspi_read_data(s); + break; + + case A_PSPI_CTL1: + value = s->regs[R_PSPI_CTL1]; + break; + + case A_PSPI_STAT: + value = s->regs[R_PSPI_STAT]; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to invalid offset 0x%" PRIx64 "\n", + DEVICE(s)->canonical_path, addr); + return 0; + } + trace_npcm_pspi_ctrl_read(DEVICE(s)->canonical_path, addr, value); + npcm_pspi_update_irq(s); + + return value; +} + +/* Control register write handler. */ +static void npcm_pspi_ctrl_write(void *opaque, hwaddr addr, uint64_t v, + unsigned int size) +{ + NPCMPSPIState *s = opaque; + uint16_t value = v; + + trace_npcm_pspi_ctrl_write(DEVICE(s)->canonical_path, addr, value); + + switch (addr) { + case A_PSPI_DATA: + npcm_pspi_write_data(s, value); + break; + + case A_PSPI_CTL1: + s->regs[R_PSPI_CTL1] = value; + break; + + case A_PSPI_STAT: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to read-only register PSPI_STAT: 0x%08" + PRIx64 "\n", DEVICE(s)->canonical_path, v); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to invalid offset 0x%" PRIx64 "\n", + DEVICE(s)->canonical_path, addr); + return; + } + npcm_pspi_update_irq(s); +} + +static const MemoryRegionOps npcm_pspi_ctrl_ops = { + .read = npcm_pspi_ctrl_read, + .write = npcm_pspi_ctrl_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 2, + .unaligned = false, + }, + .impl = { + .min_access_size = 2, + .max_access_size = 2, + .unaligned = false, + }, +}; + +static void npcm_pspi_enter_reset(Object *obj, ResetType type) +{ + NPCMPSPIState *s = NPCM_PSPI(obj); + + trace_npcm_pspi_enter_reset(DEVICE(obj)->canonical_path, type); + memset(s->regs, 0, sizeof(s->regs)); +} + +static void npcm_pspi_realize(DeviceState *dev, Error **errp) +{ + NPCMPSPIState *s = NPCM_PSPI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + + s->spi = ssi_create_bus(dev, "pspi"); + memory_region_init_io(&s->mmio, obj, &npcm_pspi_ctrl_ops, s, + "mmio", 4 * KiB); + sysbus_init_mmio(sbd, &s->mmio); + sysbus_init_irq(sbd, &s->irq); +} + +static const VMStateDescription vmstate_npcm_pspi = { + .name = "npcm-pspi", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT16_ARRAY(regs, NPCMPSPIState, NPCM_PSPI_NR_REGS), + VMSTATE_END_OF_LIST(), + }, +}; + + +static void npcm_pspi_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "NPCM Peripheral SPI Module"; + dc->realize = npcm_pspi_realize; + dc->vmsd = &vmstate_npcm_pspi; + rc->phases.enter = npcm_pspi_enter_reset; +} + +static const TypeInfo npcm_pspi_types[] = { + { + .name = TYPE_NPCM_PSPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NPCMPSPIState), + .class_init = npcm_pspi_class_init, + }, +}; +DEFINE_TYPES(npcm_pspi_types); diff --git a/hw/ssi/omap_spi.c b/hw/ssi/omap_spi.c index 7c7e689707..8f85c3e391 100644 --- a/hw/ssi/omap_spi.c +++ b/hw/ssi/omap_spi.c @@ -134,10 +134,9 @@ void omap_mcspi_reset(struct omap_mcspi_s *s) omap_mcspi_interrupt_update(s); } -static uint64_t omap_mcspi_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t omap_mcspi_read(void *opaque, hwaddr addr, unsigned size) { - struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; + struct omap_mcspi_s *s = opaque; int ch = 0; uint32_t ret; @@ -226,7 +225,7 @@ static uint64_t omap_mcspi_read(void *opaque, hwaddr addr, static void omap_mcspi_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; + struct omap_mcspi_s *s = opaque; int ch = 0; if (size != 4) { diff --git a/hw/ssi/pl022.c b/hw/ssi/pl022.c index 8954ffebb1..b8be8ddf0e 100644 --- a/hw/ssi/pl022.c +++ b/hw/ssi/pl022.c @@ -249,7 +249,7 @@ static const VMStateDescription vmstate_pl022 = { .version_id = 1, .minimum_version_id = 1, .post_load = pl022_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(cr0, PL022State), VMSTATE_UINT32(cr1, PL022State), VMSTATE_UINT32(bitmask, PL022State), diff --git a/hw/ssi/sifive_spi.c b/hw/ssi/sifive_spi.c index 03540cf5ca..1b4a401ca1 100644 --- a/hw/ssi/sifive_spi.c +++ b/hw/ssi/sifive_spi.c @@ -267,7 +267,7 @@ static void sifive_spi_write(void *opaque, hwaddr addr, case R_RXDATA: case R_IP: qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid write to read-only reigster 0x%" + "%s: invalid write to read-only register 0x%" HWADDR_PRIx " with 0x%x\n", __func__, addr << 2, value); break; diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c index 003931fb50..3f357e8f16 100644 --- a/hw/ssi/ssi.c +++ b/hw/ssi/ssi.c @@ -13,6 +13,7 @@ */ #include "qemu/osdep.h" +#include "hw/qdev-properties.h" #include "hw/ssi/ssi.h" #include "migration/vmstate.h" #include "qemu/module.h" @@ -26,10 +27,46 @@ struct SSIBus { #define TYPE_SSI_BUS "SSI" OBJECT_DECLARE_SIMPLE_TYPE(SSIBus, SSI_BUS) +DeviceState *ssi_get_cs(SSIBus *bus, uint8_t cs_index) +{ + BusState *b = BUS(bus); + BusChild *kid; + + QTAILQ_FOREACH(kid, &b->children, sibling) { + SSIPeripheral *kid_ssi = SSI_PERIPHERAL(kid->child); + if (kid_ssi->cs_index == cs_index) { + return kid->child; + } + } + + return NULL; +} + +static bool ssi_bus_check_address(BusState *b, DeviceState *dev, Error **errp) +{ + SSIPeripheral *s = SSI_PERIPHERAL(dev); + + if (ssi_get_cs(SSI_BUS(b), s->cs_index)) { + error_setg(errp, "CS index '0x%x' in use by a %s device", s->cs_index, + object_get_typename(OBJECT(dev))); + return false; + } + + return true; +} + +static void ssi_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + k->check_address = ssi_bus_check_address; +} + static const TypeInfo ssi_bus_info = { .name = TYPE_SSI_BUS, .parent = TYPE_BUS, .instance_size = sizeof(SSIBus), + .class_init = ssi_bus_class_init, }; static void ssi_cs_default(void *opaque, int n, int level) @@ -38,9 +75,8 @@ static void ssi_cs_default(void *opaque, int n, int level) bool cs = !!level; assert(n == 0); if (s->cs != cs) { - SSIPeripheralClass *ssc = SSI_PERIPHERAL_GET_CLASS(s); - if (ssc->set_cs) { - ssc->set_cs(s, cs); + if (s->spc->set_cs) { + s->spc->set_cs(s, cs); } } s->cs = cs; @@ -48,11 +84,11 @@ static void ssi_cs_default(void *opaque, int n, int level) static uint32_t ssi_transfer_raw_default(SSIPeripheral *dev, uint32_t val) { - SSIPeripheralClass *ssc = SSI_PERIPHERAL_GET_CLASS(dev); + SSIPeripheralClass *ssc = dev->spc; if ((dev->cs && ssc->cs_polarity == SSI_CS_HIGH) || - (!dev->cs && ssc->cs_polarity == SSI_CS_LOW) || - ssc->cs_polarity == SSI_CS_NONE) { + (!dev->cs && ssc->cs_polarity == SSI_CS_LOW) || + ssc->cs_polarity == SSI_CS_NONE) { return ssc->transfer(dev, val); } return 0; @@ -67,10 +103,16 @@ static void ssi_peripheral_realize(DeviceState *dev, Error **errp) ssc->cs_polarity != SSI_CS_NONE) { qdev_init_gpio_in_named(dev, ssi_cs_default, SSI_GPIO_CS, 1); } + s->spc = ssc; ssc->realize(s, errp); } +static Property ssi_peripheral_properties[] = { + DEFINE_PROP_UINT8("cs", SSIPeripheral, cs_index, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static void ssi_peripheral_class_init(ObjectClass *klass, void *data) { SSIPeripheralClass *ssc = SSI_PERIPHERAL_CLASS(klass); @@ -81,6 +123,7 @@ static void ssi_peripheral_class_init(ObjectClass *klass, void *data) if (!ssc->transfer_raw) { ssc->transfer_raw = ssi_transfer_raw_default; } + device_class_set_props(dc, ssi_peripheral_properties); } static const TypeInfo ssi_peripheral_info = { @@ -115,13 +158,11 @@ uint32_t ssi_transfer(SSIBus *bus, uint32_t val) { BusState *b = BUS(bus); BusChild *kid; - SSIPeripheralClass *ssc; uint32_t r = 0; QTAILQ_FOREACH(kid, &b->children, sibling) { - SSIPeripheral *peripheral = SSI_PERIPHERAL(kid->child); - ssc = SSI_PERIPHERAL_GET_CLASS(peripheral); - r |= ssc->transfer_raw(peripheral, val); + SSIPeripheral *p = SSI_PERIPHERAL(kid->child); + r |= p->spc->transfer_raw(p, val); } return r; @@ -131,7 +172,7 @@ const VMStateDescription vmstate_ssi_peripheral = { .name = "SSISlave", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(cs, SSIPeripheral), VMSTATE_END_OF_LIST() } diff --git a/hw/ssi/stm32f2xx_spi.c b/hw/ssi/stm32f2xx_spi.c index cd6e8443db..a37139fe5a 100644 --- a/hw/ssi/stm32f2xx_spi.c +++ b/hw/ssi/stm32f2xx_spi.c @@ -174,7 +174,7 @@ static const VMStateDescription vmstate_stm32f2xx_spi = { .name = TYPE_STM32F2XX_SPI, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(spi_cr1, STM32F2XXSPIState), VMSTATE_UINT32(spi_cr2, STM32F2XXSPIState), VMSTATE_UINT32(spi_sr, STM32F2XXSPIState), diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events index c707d4aaba..2d5bd2b83d 100644 --- a/hw/ssi/trace-events +++ b/hw/ssi/trace-events @@ -21,6 +21,11 @@ npcm7xx_fiu_ctrl_write(const char *id, uint64_t addr, uint32_t data) "%s offset: npcm7xx_fiu_flash_read(const char *id, int cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64 npcm7xx_fiu_flash_write(const char *id, unsigned cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64 +# npcm_pspi.c +npcm_pspi_enter_reset(const char *id, int reset_type) "%s reset type: %d" +npcm_pspi_ctrl_read(const char *id, uint64_t addr, uint16_t data) "%s offset: 0x%03" PRIx64 " value: 0x%04" PRIx16 +npcm_pspi_ctrl_write(const char *id, uint64_t addr, uint16_t data) "%s offset: 0x%03" PRIx64 " value: 0x%04" PRIx16 + # ibex_spi_host.c ibex_spi_host_reset(const char *msg) "%s" diff --git a/hw/ssi/xilinx_spi.c b/hw/ssi/xilinx_spi.c index b2819a7ff0..2e0687ac90 100644 --- a/hw/ssi/xilinx_spi.c +++ b/hw/ssi/xilinx_spi.c @@ -156,6 +156,7 @@ static void xlx_spi_do_reset(XilinxSPI *s) txfifo_reset(s); s->regs[R_SPISSR] = ~0; + s->regs[R_SPICR] = R_SPICR_MTI; xlx_spi_update_irq(s); xlx_spi_update_cs(s); } @@ -232,7 +233,7 @@ spi_read(void *opaque, hwaddr addr, unsigned int size) break; } - DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr * 4, r); + DB_PRINT("addr=" HWADDR_FMT_plx " = %x\n", addr * 4, r); xlx_spi_update_irq(s); return r; } @@ -244,7 +245,7 @@ spi_write(void *opaque, hwaddr addr, XilinxSPI *s = opaque; uint32_t value = val64; - DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr, value); + DB_PRINT("addr=" HWADDR_FMT_plx " = %x\n", addr, value); addr >>= 2; switch (addr) { case R_SRR: @@ -352,7 +353,7 @@ static const VMStateDescription vmstate_xilinx_spi = { .name = "xilinx_spi", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_FIFO8(tx_fifo, XilinxSPI), VMSTATE_FIFO8(rx_fifo, XilinxSPI), VMSTATE_UINT32_ARRAY(regs, XilinxSPI, R_MAX), diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index 1e9dba2039..71952a410d 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -163,7 +163,7 @@ FIELD(GQSPI_CNFG, ENDIAN, 26, 1) /* Poll timeout not implemented */ FIELD(GQSPI_CNFG, EN_POLL_TIMEOUT, 20, 1) - /* QEMU doesnt care about any of these last three */ + /* QEMU doesn't care about any of these last three */ FIELD(GQSPI_CNFG, BR, 3, 3) FIELD(GQSPI_CNFG, CPH, 2, 1) FIELD(GQSPI_CNFG, CPL, 1, 1) @@ -469,7 +469,7 @@ static void xlnx_zynqmp_qspips_flush_fifo_g(XlnxZynqMPQSPIPS *s) imm = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, IMMEDIATE_DATA); if (!ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, DATA_XFER)) { - /* immedate transfer */ + /* immediate transfer */ if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT) || ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE)) { s->regs[R_GQSPI_DATA_STS] = 1; @@ -768,7 +768,7 @@ static void xilinx_spips_check_zero_pump(XilinxSPIPS *s) */ while (s->regs[R_TRANSFER_SIZE] && s->rx_fifo.num + s->tx_fifo.num < RXFF_A_Q - 3) { - /* endianess just doesn't matter when zero pumping */ + /* endianness just doesn't matter when zero pumping */ tx_data_bytes(&s->tx_fifo, 0, 4, false); s->regs[R_TRANSFER_SIZE] &= ~0x03ull; s->regs[R_TRANSFER_SIZE] -= 4; @@ -887,7 +887,7 @@ static uint64_t xilinx_spips_read(void *opaque, hwaddr addr, case R_INTR_STATUS: ret = s->regs[addr] & IXR_ALL; s->regs[addr] = 0; - DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr * 4, ret); + DB_PRINT_L(0, "addr=" HWADDR_FMT_plx " = %x\n", addr * 4, ret); xilinx_spips_update_ixr(s); return ret; case R_INTR_MASK: @@ -916,12 +916,12 @@ static uint64_t xilinx_spips_read(void *opaque, hwaddr addr, if (!(s->regs[R_CONFIG] & R_CONFIG_ENDIAN)) { ret <<= 8 * shortfall; } - DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr * 4, ret); + DB_PRINT_L(0, "addr=" HWADDR_FMT_plx " = %x\n", addr * 4, ret); xilinx_spips_check_flush(s); xilinx_spips_update_ixr(s); return ret; } - DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr * 4, + DB_PRINT_L(0, "addr=" HWADDR_FMT_plx " = %x\n", addr * 4, s->regs[addr] & mask); return s->regs[addr] & mask; @@ -971,8 +971,10 @@ static void xilinx_spips_write(void *opaque, hwaddr addr, XilinxSPIPS *s = opaque; bool try_flush = true; - DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr, (unsigned)value); + DB_PRINT_L(0, "addr=" HWADDR_FMT_plx " = %x\n", addr, (unsigned)value); addr >>= 2; + assert(addr < XLNX_SPIPS_R_MAX); + switch (addr) { case R_CONFIG: mask = ~(R_CONFIG_RSVD | MAN_START_COM); @@ -1299,7 +1301,7 @@ static void xilinx_spips_realize(DeviceState *dev, Error **errp) } memory_region_init_io(&s->iomem, OBJECT(s), xsc->reg_ops, s, - "spi", XLNX_ZYNQMP_SPIPS_R_MAX * 4); + "spi", xsc->reg_size); sysbus_init_mmio(sbd, &s->iomem); s->irqline = -1; @@ -1367,7 +1369,7 @@ static const VMStateDescription vmstate_xilinx_spips = { .version_id = 2, .minimum_version_id = 2, .post_load = xilinx_spips_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_FIFO8(tx_fifo, XilinxSPIPS), VMSTATE_FIFO8(rx_fifo, XilinxSPIPS), VMSTATE_UINT32_ARRAY(regs, XilinxSPIPS, XLNX_SPIPS_R_MAX), @@ -1393,7 +1395,7 @@ static const VMStateDescription vmstate_xilinx_qspips = { .name = "xilinx_qspips", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(parent_obj, XilinxQSPIPS, 0, vmstate_xilinx_spips, XilinxSPIPS), VMSTATE_END_OF_LIST() @@ -1405,7 +1407,7 @@ static const VMStateDescription vmstate_xlnx_zynqmp_qspips = { .version_id = 1, .minimum_version_id = 1, .post_load = xlnx_zynqmp_qspips_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(parent_obj, XlnxZynqMPQSPIPS, 0, vmstate_xilinx_qspips, XilinxQSPIPS), VMSTATE_FIFO8(tx_fifo_g, XlnxZynqMPQSPIPS), @@ -1435,6 +1437,7 @@ static void xilinx_qspips_class_init(ObjectClass *klass, void * data) dc->realize = xilinx_qspips_realize; xsc->reg_ops = &qspips_ops; + xsc->reg_size = XLNX_SPIPS_R_MAX * 4; xsc->rx_fifo_size = RXFF_A_Q; xsc->tx_fifo_size = TXFF_A_Q; } @@ -1450,6 +1453,7 @@ static void xilinx_spips_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_xilinx_spips; xsc->reg_ops = &spips_ops; + xsc->reg_size = XLNX_SPIPS_R_MAX * 4; xsc->rx_fifo_size = RXFF_A; xsc->tx_fifo_size = TXFF_A; } @@ -1464,6 +1468,7 @@ static void xlnx_zynqmp_qspips_class_init(ObjectClass *klass, void * data) dc->vmsd = &vmstate_xlnx_zynqmp_qspips; device_class_set_props(dc, xilinx_zynqmp_qspips_properties); xsc->reg_ops = &xlnx_zynqmp_qspips_ops; + xsc->reg_size = XLNX_ZYNQMP_SPIPS_R_MAX * 4; xsc->rx_fifo_size = RXFF_A_Q; xsc->tx_fifo_size = TXFF_A_Q; } diff --git a/hw/ssi/xlnx-versal-ospi.c b/hw/ssi/xlnx-versal-ospi.c index c762e0b367..c479138ec1 100644 --- a/hw/ssi/xlnx-versal-ospi.c +++ b/hw/ssi/xlnx-versal-ospi.c @@ -837,7 +837,7 @@ static void ospi_do_ind_read(XlnxVersalOspi *s) /* Continue to read flash until we run out of space in sram */ while (!ospi_ind_op_completed(op) && !fifo8_is_full(&s->rx_sram)) { - /* Read reqested number of bytes, max bytes limited to size of sram */ + /* Read requested number of bytes, max bytes limited to size of sram */ next_b = ind_op_next_byte(op); end_b = next_b + fifo8_num_free(&s->rx_sram); end_b = MIN(end_b, ind_op_end_byte(op)); @@ -1772,6 +1772,12 @@ static void xlnx_versal_ospi_init(Object *obj) memory_region_init_io(&s->iomem_dac, obj, &ospi_dac_ops, s, TYPE_XILINX_VERSAL_OSPI "-dac", 0x20000000); sysbus_init_mmio(sbd, &s->iomem_dac); + /* + * The OSPI DMA reads flash data through the OSPI linear address space (the + * iomem_dac region), because of this the reentrancy guard needs to be + * disabled. + */ + s->iomem_dac.disable_reentrancy_guard = true; sysbus_init_irq(sbd, &s->irq); @@ -1787,7 +1793,7 @@ static const VMStateDescription vmstate_ind_op = { .name = "OSPIIndOp", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(flash_addr, IndOp), VMSTATE_UINT32(num_bytes, IndOp), VMSTATE_UINT32(done_bytes, IndOp), @@ -1800,7 +1806,7 @@ static const VMStateDescription vmstate_xlnx_versal_ospi = { .name = TYPE_XILINX_VERSAL_OSPI, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_FIFO8(rx_fifo, XlnxVersalOspi), VMSTATE_FIFO8(tx_fifo, XlnxVersalOspi), VMSTATE_FIFO8(rx_sram, XlnxVersalOspi), diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index 5e959b6d09..a2ac5bdfb9 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -328,7 +328,7 @@ static const VMStateDescription vmstate_a9_gtimer_per_cpu = { .name = "arm.cortex-a9-global-timer.percpu", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(control, A9GTimerPerCPU), VMSTATE_UINT64(compare, A9GTimerPerCPU), VMSTATE_UINT32(status, A9GTimerPerCPU), @@ -342,7 +342,7 @@ static const VMStateDescription vmstate_a9_gtimer_control = { .version_id = 1, .minimum_version_id = 1, .needed = vmstate_a9_gtimer_control_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(control, A9GTimerState), VMSTATE_END_OF_LIST() } @@ -352,7 +352,7 @@ static const VMStateDescription vmstate_a9_gtimer = { .name = "arm.cortex-a9-global-timer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(timer, A9GTimerState), VMSTATE_UINT64(counter, A9GTimerState), VMSTATE_UINT64(ref_counter, A9GTimerState), @@ -362,7 +362,7 @@ static const VMStateDescription vmstate_a9_gtimer = { A9GTimerPerCPU), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_a9_gtimer_control, NULL } diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c index 971f78462a..a524de1381 100644 --- a/hw/timer/allwinner-a10-pit.c +++ b/hw/timer/allwinner-a10-pit.c @@ -200,7 +200,7 @@ static const VMStateDescription vmstate_a10_pit = { .name = "a10.pit", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(irq_enable, AwA10PITState), VMSTATE_UINT32(irq_status, AwA10PITState), VMSTATE_UINT32_ARRAY(control, AwA10PITState, AW_A10_PIT_TIMER_NR), diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index cdfca3000b..bca4cee0e4 100644 --- a/hw/timer/arm_mptimer.c +++ b/hw/timer/arm_mptimer.c @@ -281,7 +281,7 @@ static const VMStateDescription vmstate_timerblock = { .name = "arm_mptimer_timerblock", .version_id = 3, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(control, TimerBlock), VMSTATE_UINT32(status, TimerBlock), VMSTATE_PTIMER(timer, TimerBlock), @@ -293,7 +293,7 @@ static const VMStateDescription vmstate_arm_mptimer = { .name = "arm_mptimer", .version_id = 3, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu, 3, vmstate_timerblock, TimerBlock), VMSTATE_END_OF_LIST() diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c index 69c8863472..0940e03f1d 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -163,7 +163,7 @@ static const VMStateDescription vmstate_arm_timer = { .name = "arm_timer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(control, arm_timer_state), VMSTATE_UINT32(limit, arm_timer_state), VMSTATE_INT32(int_level, arm_timer_state), @@ -181,7 +181,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_LEGACY); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_arm_timer, s); + vmstate_register_any(NULL, &vmstate_arm_timer, s); return s; } @@ -282,7 +282,7 @@ static const VMStateDescription vmstate_sp804 = { .name = "sp804", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32_ARRAY(level, SP804State, 2), VMSTATE_END_OF_LIST() } diff --git a/hw/timer/armv7m_systick.c b/hw/timer/armv7m_systick.c index 5dfe39afe3..f6b1acef27 100644 --- a/hw/timer/armv7m_systick.c +++ b/hw/timer/armv7m_systick.c @@ -275,7 +275,7 @@ static const VMStateDescription vmstate_systick = { .name = "armv7m_systick", .version_id = 3, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CLOCK(refclk, SysTickState), VMSTATE_CLOCK(cpuclk, SysTickState), VMSTATE_UINT32(control, SysTickState), diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 9c20b3d6ad..fc5c94bdf3 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -167,7 +167,7 @@ static uint64_t calculate_next(struct AspeedTimer *t) qemu_set_irq(t->irq, t->level); } - next = MAX(MAX(calculate_match(t, 0), calculate_match(t, 1)), 0); + next = MAX(calculate_match(t, 0), calculate_match(t, 1)); t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); return calculate_time(t, next); @@ -645,7 +645,7 @@ static const VMStateDescription vmstate_aspeed_timer = { .name = "aspeed.timer", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(id, AspeedTimer), VMSTATE_INT32(level, AspeedTimer), VMSTATE_TIMER(timer, AspeedTimer), @@ -659,7 +659,7 @@ static const VMStateDescription vmstate_aspeed_timer_state = { .name = "aspeed.timerctrl", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ctrl, AspeedTimerCtrlState), VMSTATE_UINT32(ctrl2, AspeedTimerCtrlState), VMSTATE_UINT32(ctrl3, AspeedTimerCtrlState), diff --git a/hw/timer/bcm2835_systmr.c b/hw/timer/bcm2835_systmr.c index 67669a57ff..3ec64604ee 100644 --- a/hw/timer/bcm2835_systmr.c +++ b/hw/timer/bcm2835_systmr.c @@ -146,7 +146,7 @@ static const VMStateDescription bcm2835_systmr_vmstate = { .name = "bcm2835_sys_timer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(reg.ctrl_status, BCM2835SystemTimerState), VMSTATE_UINT32_ARRAY(reg.compare, BCM2835SystemTimerState, BCM2835_SYSTIMER_COUNT), diff --git a/hw/timer/cadence_ttc.c b/hw/timer/cadence_ttc.c index e57a0f5f09..54dbd4c564 100644 --- a/hw/timer/cadence_ttc.c +++ b/hw/timer/cadence_ttc.c @@ -425,7 +425,7 @@ static const VMStateDescription vmstate_cadence_timer = { .minimum_version_id = 1, .pre_save = cadence_timer_pre_save, .post_load = cadence_timer_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(reg_clock, CadenceTimerState), VMSTATE_UINT32(reg_count, CadenceTimerState), VMSTATE_UINT32(reg_value, CadenceTimerState), @@ -443,7 +443,7 @@ static const VMStateDescription vmstate_cadence_ttc = { .name = "cadence_TTC", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(timer, CadenceTTCState, 3, 0, vmstate_cadence_timer, CadenceTimerState), diff --git a/hw/timer/cmsdk-apb-dualtimer.c b/hw/timer/cmsdk-apb-dualtimer.c index d4a509c798..ddf9070c3c 100644 --- a/hw/timer/cmsdk-apb-dualtimer.c +++ b/hw/timer/cmsdk-apb-dualtimer.c @@ -508,7 +508,7 @@ static const VMStateDescription cmsdk_dualtimermod_vmstate = { .name = "cmsdk-apb-dualtimer-module", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PTIMER(timer, CMSDKAPBDualTimerModule), VMSTATE_UINT32(load, CMSDKAPBDualTimerModule), VMSTATE_UINT32(value, CMSDKAPBDualTimerModule), @@ -522,7 +522,7 @@ static const VMStateDescription cmsdk_apb_dualtimer_vmstate = { .name = "cmsdk-apb-dualtimer", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CLOCK(timclk, CMSDKAPBDualTimer), VMSTATE_STRUCT_ARRAY(timermod, CMSDKAPBDualTimer, CMSDK_APB_DUALTIMER_NUM_MODULES, diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c index 68aa1a7636..814545c783 100644 --- a/hw/timer/cmsdk-apb-timer.c +++ b/hw/timer/cmsdk-apb-timer.c @@ -250,7 +250,7 @@ static const VMStateDescription cmsdk_apb_timer_vmstate = { .name = "cmsdk-apb-timer", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PTIMER(timer, CMSDKAPBTimer), VMSTATE_CLOCK(pclk, CMSDKAPBTimer), VMSTATE_UINT32(ctrl, CMSDKAPBTimer), diff --git a/hw/timer/digic-timer.c b/hw/timer/digic-timer.c index d5186f4454..9fc5c1d8a4 100644 --- a/hw/timer/digic-timer.c +++ b/hw/timer/digic-timer.c @@ -39,7 +39,7 @@ static const VMStateDescription vmstate_digic_timer = { .name = "digic.timer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PTIMER(ptimer, DigicTimerState), VMSTATE_UINT32(control, DigicTimerState), VMSTATE_UINT32(relvalue, DigicTimerState), @@ -76,7 +76,7 @@ static uint64_t digic_timer_read(void *opaque, hwaddr offset, unsigned size) default: qemu_log_mask(LOG_UNIMP, "digic-timer: read access to unknown register 0x" - TARGET_FMT_plx "\n", offset); + HWADDR_FMT_plx "\n", offset); } return ret; @@ -116,7 +116,7 @@ static void digic_timer_write(void *opaque, hwaddr offset, default: qemu_log_mask(LOG_UNIMP, "digic-timer: read access to unknown register 0x" - TARGET_FMT_plx "\n", offset); + HWADDR_FMT_plx "\n", offset); } } diff --git a/hw/timer/etraxfs_timer.c b/hw/timer/etraxfs_timer.c index ecc2831baf..da7c946af5 100644 --- a/hw/timer/etraxfs_timer.c +++ b/hw/timer/etraxfs_timer.c @@ -88,7 +88,7 @@ static const VMStateDescription vmstate_etraxfs = { .name = "etraxfs", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PTIMER(ptimer_t0, ETRAXTimerState), VMSTATE_PTIMER(ptimer_t1, ETRAXTimerState), VMSTATE_PTIMER(ptimer_wd, ETRAXTimerState), @@ -236,7 +236,7 @@ static void watchdog_hit(void *opaque) { ETRAXTimerState *t = opaque; if (t->wd_hits == 0) { - /* real hw gives a single tick before reseting but we are + /* real hw gives a single tick before resetting but we are a bit friendlier to compensate for our slower execution. */ ptimer_set_count(t->ptimer_wd, 10); ptimer_run(t->ptimer_wd, 1); @@ -324,8 +324,7 @@ timer_write(void *opaque, hwaddr addr, t->rw_ack_intr = 0; break; default: - printf ("%s " TARGET_FMT_plx " %x\n", - __func__, addr, value); + printf("%s " HWADDR_FMT_plx " %x\n", __func__, addr, value); break; } } diff --git a/hw/timer/exynos4210_mct.c b/hw/timer/exynos4210_mct.c index e175a9f5b9..75098cdb55 100644 --- a/hw/timer/exynos4210_mct.c +++ b/hw/timer/exynos4210_mct.c @@ -264,7 +264,7 @@ static const VMStateDescription vmstate_tick_timer = { .name = "exynos4210.mct.tick_timer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(cnt_run, struct tick_timer), VMSTATE_UINT32(int_run, struct tick_timer), VMSTATE_UINT32(last_icnto, struct tick_timer), @@ -283,7 +283,7 @@ static const VMStateDescription vmstate_lregs = { .name = "exynos4210.mct.lregs", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(cnt, struct lregs, L_REG_CNT_AMOUNT), VMSTATE_UINT32(tcon, struct lregs), VMSTATE_UINT32(int_cstat, struct lregs), @@ -297,7 +297,7 @@ static const VMStateDescription vmstate_exynos4210_mct_lt = { .name = "exynos4210.mct.lt", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(id, Exynos4210MCTLT), VMSTATE_STRUCT(tick_timer, Exynos4210MCTLT, 0, vmstate_tick_timer, @@ -314,7 +314,7 @@ static const VMStateDescription vmstate_gregs = { .name = "exynos4210.mct.lregs", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(cnt, struct gregs), VMSTATE_UINT32(cnt_wstat, struct gregs), VMSTATE_UINT32(tcon, struct gregs), @@ -332,7 +332,7 @@ static const VMStateDescription vmstate_exynos4210_mct_gt = { .name = "exynos4210.mct.lt", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(reg, Exynos4210MCTGT, 0, vmstate_gregs, struct gregs), VMSTATE_UINT64(count, Exynos4210MCTGT), @@ -346,7 +346,7 @@ static const VMStateDescription vmstate_exynos4210_mct_state = { .name = "exynos4210.mct", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(reg_mct_cfg, Exynos4210MCTState), VMSTATE_STRUCT_ARRAY(l_timer, Exynos4210MCTState, 2, 0, vmstate_exynos4210_mct_lt, Exynos4210MCTLT), @@ -480,11 +480,14 @@ static int32_t exynos4210_gcomp_find(Exynos4210MCTState *s) res = min_comp_i; } - DPRINTF("found comparator %d: comp 0x%llx distance 0x%llx, gfrc 0x%llx\n", - res, - s->g_timer.reg.comp[res], - distance_min, - gfrc); + if (res >= 0) { + DPRINTF("found comparator %d: " + "comp 0x%llx distance 0x%llx, gfrc 0x%llx\n", + res, + s->g_timer.reg.comp[res], + distance_min, + gfrc); + } return res; } @@ -1445,7 +1448,7 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset, case L0_ICNTO: case L1_ICNTO: case L0_FRCNTO: case L1_FRCNTO: qemu_log_mask(LOG_GUEST_ERROR, - "exynos4210.mct: write to RO register " TARGET_FMT_plx, + "exynos4210.mct: write to RO register " HWADDR_FMT_plx, offset); break; diff --git a/hw/timer/exynos4210_pwm.c b/hw/timer/exynos4210_pwm.c index 02924a9e5b..ca330e9446 100644 --- a/hw/timer/exynos4210_pwm.c +++ b/hw/timer/exynos4210_pwm.c @@ -123,7 +123,7 @@ static const VMStateDescription vmstate_exynos4210_pwm = { .name = "exynos4210.pwm.pwm", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(id, Exynos4210PWM), VMSTATE_UINT32(freq, Exynos4210PWM), VMSTATE_PTIMER(ptimer, Exynos4210PWM), @@ -137,7 +137,7 @@ static const VMStateDescription vmstate_exynos4210_pwm_state = { .name = "exynos4210.pwm", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(reg_tcfg, Exynos4210PWMState, 2), VMSTATE_UINT32(reg_tcon, Exynos4210PWMState), VMSTATE_UINT32(reg_tint_cstat, Exynos4210PWMState), @@ -257,7 +257,7 @@ static uint64_t exynos4210_pwm_read(void *opaque, hwaddr offset, default: qemu_log_mask(LOG_GUEST_ERROR, - "exynos4210.pwm: bad read offset " TARGET_FMT_plx, + "exynos4210.pwm: bad read offset " HWADDR_FMT_plx, offset); break; } @@ -352,7 +352,7 @@ static void exynos4210_pwm_write(void *opaque, hwaddr offset, default: qemu_log_mask(LOG_GUEST_ERROR, - "exynos4210.pwm: bad write offset " TARGET_FMT_plx, + "exynos4210.pwm: bad write offset " HWADDR_FMT_plx, offset); break; diff --git a/hw/timer/grlib_gptimer.c b/hw/timer/grlib_gptimer.c index 5c4923c1e0..4990885451 100644 --- a/hw/timer/grlib_gptimer.c +++ b/hw/timer/grlib_gptimer.c @@ -1,7 +1,9 @@ /* * QEMU GRLIB GPTimer Emulator * - * Copyright (c) 2010-2019 AdaCore + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2010-2024 AdaCore * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,7 +25,7 @@ */ #include "qemu/osdep.h" -#include "hw/sparc/grlib.h" +#include "hw/timer/grlib_gptimer.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "hw/irq.h" diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 9520471be2..01efe4885d 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -25,11 +25,11 @@ */ #include "qemu/osdep.h" -#include "hw/i386/pc.h" #include "hw/irq.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/timer.h" +#include "hw/qdev-properties.h" #include "hw/timer/hpet.h" #include "hw/sysbus.h" #include "hw/rtc/mc146818rtc.h" @@ -38,13 +38,7 @@ #include "hw/timer/i8254.h" #include "exec/address-spaces.h" #include "qom/object.h" - -//#define HPET_DEBUG -#ifdef HPET_DEBUG -#define DPRINTF printf -#else -#define DPRINTF(...) -#endif +#include "trace.h" #define HPET_MSI_SUPPORT 0 @@ -295,7 +289,7 @@ static const VMStateDescription vmstate_hpet_rtc_irq_level = { .version_id = 1, .minimum_version_id = 1, .needed = hpet_rtc_irq_level_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(rtc_irq_level, HPETState), VMSTATE_END_OF_LIST() } @@ -306,7 +300,7 @@ static const VMStateDescription vmstate_hpet_offset = { .version_id = 1, .minimum_version_id = 1, .needed = hpet_offset_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(hpet_offset, HPETState), VMSTATE_END_OF_LIST() } @@ -316,7 +310,7 @@ static const VMStateDescription vmstate_hpet_timer = { .name = "hpet_timer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(tn, HPETTimer), VMSTATE_UINT64(config, HPETTimer), VMSTATE_UINT64(cmp, HPETTimer), @@ -335,7 +329,7 @@ static const VMStateDescription vmstate_hpet = { .pre_save = hpet_pre_save, .pre_load = hpet_pre_load, .post_load = hpet_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(config, HPETState), VMSTATE_UINT64(isr, HPETState), VMSTATE_UINT64(hpet_counter, HPETState), @@ -345,13 +339,23 @@ static const VMStateDescription vmstate_hpet = { vmstate_hpet_timer, HPETTimer), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_hpet_rtc_irq_level, &vmstate_hpet_offset, NULL } }; +static void hpet_arm(HPETTimer *t, uint64_t ticks) +{ + if (ticks < ns_to_ticks(INT64_MAX / 2)) { + timer_mod(t->qemu_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ticks_to_ns(ticks)); + } else { + timer_del(t->qemu_timer); + } +} + /* * timer expiration callback */ @@ -374,13 +378,11 @@ static void hpet_timer(void *opaque) } } diff = hpet_calculate_diff(t, cur_tick); - timer_mod(t->qemu_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (int64_t)ticks_to_ns(diff)); + hpet_arm(t, diff); } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) { if (t->wrap_flag) { diff = hpet_calculate_diff(t, cur_tick); - timer_mod(t->qemu_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (int64_t)ticks_to_ns(diff)); + hpet_arm(t, diff); t->wrap_flag = 0; } } @@ -407,8 +409,7 @@ static void hpet_set_timer(HPETTimer *t) t->wrap_flag = 1; } } - timer_mod(t->qemu_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (int64_t)ticks_to_ns(diff)); + hpet_arm(t, diff); } static void hpet_del_timer(HPETTimer *t) @@ -423,7 +424,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, HPETState *s = opaque; uint64_t cur_tick, index; - DPRINTF("qemu: Enter hpet_ram_readl at %" PRIx64 "\n", addr); + trace_hpet_ram_read(addr); index = addr; /*address range of all TN regs*/ if (index >= 0x100 && index <= 0x3ff) { @@ -431,7 +432,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, HPETTimer *timer = &s->timer[timer_id]; if (timer_id > s->num_timers) { - DPRINTF("qemu: timer id out of range\n"); + trace_hpet_timer_id_out_of_range(timer_id); return 0; } @@ -449,7 +450,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, case HPET_TN_ROUTE + 4: return timer->fsb >> 32; default: - DPRINTF("qemu: invalid hpet_ram_readl\n"); + trace_hpet_ram_read_invalid(); break; } } else { @@ -461,7 +462,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, case HPET_CFG: return s->config; case HPET_CFG + 4: - DPRINTF("qemu: invalid HPET_CFG + 4 hpet_ram_readl\n"); + trace_hpet_invalid_hpet_cfg(4); return 0; case HPET_COUNTER: if (hpet_enabled(s)) { @@ -469,7 +470,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, } else { cur_tick = s->hpet_counter; } - DPRINTF("qemu: reading counter = %" PRIx64 "\n", cur_tick); + trace_hpet_ram_read_reading_counter(0, cur_tick); return cur_tick; case HPET_COUNTER + 4: if (hpet_enabled(s)) { @@ -477,12 +478,12 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, } else { cur_tick = s->hpet_counter; } - DPRINTF("qemu: reading counter + 4 = %" PRIx64 "\n", cur_tick); + trace_hpet_ram_read_reading_counter(4, cur_tick); return cur_tick >> 32; case HPET_STATUS: return s->isr; default: - DPRINTF("qemu: invalid hpet_ram_readl\n"); + trace_hpet_ram_read_invalid(); break; } } @@ -496,8 +497,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, HPETState *s = opaque; uint64_t old_val, new_val, val, index; - DPRINTF("qemu: Enter hpet_ram_writel at %" PRIx64 " = 0x%" PRIx64 "\n", - addr, value); + trace_hpet_ram_write(addr, value); index = addr; old_val = hpet_ram_read(opaque, addr, 4); new_val = value; @@ -507,14 +507,14 @@ static void hpet_ram_write(void *opaque, hwaddr addr, uint8_t timer_id = (addr - 0x100) / 0x20; HPETTimer *timer = &s->timer[timer_id]; - DPRINTF("qemu: hpet_ram_writel timer_id = 0x%x\n", timer_id); + trace_hpet_ram_write_timer_id(timer_id); if (timer_id > s->num_timers) { - DPRINTF("qemu: timer id out of range\n"); + trace_hpet_timer_id_out_of_range(timer_id); return; } switch ((addr - 0x100) % 0x20) { case HPET_TN_CFG: - DPRINTF("qemu: hpet_ram_writel HPET_TN_CFG\n"); + trace_hpet_ram_write_tn_cfg(); if (activating_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) { update_irq(timer, 0); } @@ -532,10 +532,10 @@ static void hpet_ram_write(void *opaque, hwaddr addr, } break; case HPET_TN_CFG + 4: // Interrupt capabilities - DPRINTF("qemu: invalid HPET_TN_CFG+4 write\n"); + trace_hpet_ram_write_invalid_tn_cfg(4); break; case HPET_TN_CMP: // comparator register - DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP\n"); + trace_hpet_ram_write_tn_cmp(0); if (timer->config & HPET_TN_32BIT) { new_val = (uint32_t)new_val; } @@ -558,7 +558,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, } break; case HPET_TN_CMP + 4: // comparator register high order - DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP + 4\n"); + trace_hpet_ram_write_tn_cmp(4); if (!timer_is_periodic(timer) || (timer->config & HPET_TN_SETVAL)) { timer->cmp = (timer->cmp & 0xffffffffULL) | new_val << 32; @@ -583,7 +583,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff); break; default: - DPRINTF("qemu: invalid hpet_ram_writel\n"); + trace_hpet_ram_write_invalid(); break; } return; @@ -623,7 +623,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, } break; case HPET_CFG + 4: - DPRINTF("qemu: invalid HPET_CFG+4 write\n"); + trace_hpet_invalid_hpet_cfg(4); break; case HPET_STATUS: val = new_val & s->isr; @@ -635,24 +635,20 @@ static void hpet_ram_write(void *opaque, hwaddr addr, break; case HPET_COUNTER: if (hpet_enabled(s)) { - DPRINTF("qemu: Writing counter while HPET enabled!\n"); + trace_hpet_ram_write_counter_write_while_enabled(); } s->hpet_counter = (s->hpet_counter & 0xffffffff00000000ULL) | value; - DPRINTF("qemu: HPET counter written. ctr = 0x%" PRIx64 " -> " - "%" PRIx64 "\n", value, s->hpet_counter); + trace_hpet_ram_write_counter_written(0, value, s->hpet_counter); break; case HPET_COUNTER + 4: - if (hpet_enabled(s)) { - DPRINTF("qemu: Writing counter while HPET enabled!\n"); - } + trace_hpet_ram_write_counter_write_while_enabled(); s->hpet_counter = (s->hpet_counter & 0xffffffffULL) | (((uint64_t)value) << 32); - DPRINTF("qemu: HPET counter + 4 written. ctr = 0x%" PRIx64 " -> " - "%" PRIx64 "\n", value, s->hpet_counter); + trace_hpet_ram_write_counter_written(4, value, s->hpet_counter); break; default: - DPRINTF("qemu: invalid hpet_ram_writel\n"); + trace_hpet_ram_write_invalid(); break; } } diff --git a/hw/timer/i8254.c b/hw/timer/i8254.c index c8388ea432..c235496fc9 100644 --- a/hw/timer/i8254.c +++ b/hw/timer/i8254.c @@ -350,11 +350,6 @@ static void pit_realizefn(DeviceState *dev, Error **errp) pc->parent_realize(dev, errp); } -static Property pit_properties[] = { - DEFINE_PROP_UINT32("iobase", PITCommonState, iobase, -1), - DEFINE_PROP_END_OF_LIST(), -}; - static void pit_class_initfn(ObjectClass *klass, void *data) { PITClass *pc = PIT_CLASS(klass); @@ -366,7 +361,6 @@ 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; - device_class_set_props(dc, pit_properties); } static const TypeInfo pit_info = { diff --git a/hw/timer/i8254_common.c b/hw/timer/i8254_common.c index 050875b497..28fdabc321 100644 --- a/hw/timer/i8254_common.c +++ b/hw/timer/i8254_common.c @@ -52,10 +52,8 @@ int pit_get_out(PITChannelState *s, int64_t current_time) switch (s->mode) { default: case 0: - out = (d >= s->count); - break; case 1: - out = (d < s->count); + out = (d >= s->count); break; case 2: if ((d % s->count) == 0 && d != 0) { @@ -182,7 +180,7 @@ static const VMStateDescription vmstate_pit_channel = { .name = "pit channel", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(count, PITChannelState), VMSTATE_UINT16(latched_count, PITChannelState), VMSTATE_UINT8(count_latched, PITChannelState), @@ -230,7 +228,7 @@ static const VMStateDescription vmstate_pit_common = { .minimum_version_id = 2, .pre_save = pit_dispatch_pre_save, .post_load = pit_dispatch_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_V(channels[0].irq_disabled, PITCommonState, 3), VMSTATE_STRUCT_ARRAY(channels, PITCommonState, 3, 2, vmstate_pit_channel, PITChannelState), @@ -240,6 +238,11 @@ static const VMStateDescription vmstate_pit_common = { } }; +static Property pit_common_properties[] = { + DEFINE_PROP_UINT32("iobase", PITCommonState, iobase, -1), + DEFINE_PROP_END_OF_LIST(), +}; + static void pit_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -252,6 +255,7 @@ static void pit_common_class_init(ObjectClass *klass, void *data) * done by board code. */ dc->user_creatable = false; + device_class_set_props(dc, pit_common_properties); } static const TypeInfo pit_common_type = { diff --git a/hw/timer/ibex_timer.c b/hw/timer/ibex_timer.c index 8c2ca364da..4917388d45 100644 --- a/hw/timer/ibex_timer.c +++ b/hw/timer/ibex_timer.c @@ -60,8 +60,6 @@ static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq) static void ibex_timer_update_irqs(IbexTimerState *s) { - CPUState *cs = qemu_get_cpu(0); - RISCVCPU *cpu = RISCV_CPU(cs); uint64_t value = s->timer_compare_lower0 | ((uint64_t)s->timer_compare_upper0 << 32); uint64_t next, diff; @@ -73,9 +71,9 @@ static void ibex_timer_update_irqs(IbexTimerState *s) } /* Update the CPUs mtimecmp */ - cpu->env.timecmp = value; + s->mtimecmp = value; - if (cpu->env.timecmp <= now) { + if (s->mtimecmp <= now) { /* * If the mtimecmp was in the past raise the interrupt now. */ @@ -91,7 +89,7 @@ static void ibex_timer_update_irqs(IbexTimerState *s) qemu_irq_lower(s->m_timer_irq); qemu_set_irq(s->irq, false); - diff = cpu->env.timecmp - now; + diff = s->mtimecmp - now; next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + muldiv64(diff, NANOSECONDS_PER_SECOND, @@ -99,9 +97,9 @@ static void ibex_timer_update_irqs(IbexTimerState *s) if (next < qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) { /* We overflowed the timer, just set it as large as we can */ - timer_mod(cpu->env.timer, 0x7FFFFFFFFFFFFFFF); + timer_mod(s->mtimer, 0x7FFFFFFFFFFFFFFF); } else { - timer_mod(cpu->env.timer, next); + timer_mod(s->mtimer, next); } } @@ -120,11 +118,9 @@ static void ibex_timer_reset(DeviceState *dev) { IbexTimerState *s = IBEX_TIMER(dev); - CPUState *cpu = qemu_get_cpu(0); - CPURISCVState *env = cpu->env_ptr; - env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + s->mtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &ibex_timer_cb, s); - env->timecmp = 0; + s->mtimecmp = 0; s->timer_ctrl = 0x00000000; s->timer_cfg0 = 0x00010000; @@ -256,7 +252,7 @@ static const VMStateDescription vmstate_ibex_timer = { .version_id = 2, .minimum_version_id = 2, .post_load = ibex_timer_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(timer_ctrl, IbexTimerState), VMSTATE_UINT32(timer_cfg0, IbexTimerState), VMSTATE_UINT32(timer_compare_lower0, IbexTimerState), diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c index 2bf8c754b2..bd625203aa 100644 --- a/hw/timer/imx_epit.c +++ b/hw/timer/imx_epit.c @@ -6,6 +6,7 @@ * Originally written by Hans Jiang * Updated by Peter Chubb * Updated by Jean-Christophe Dubois + * Updated by Axel Heider * * This code is licensed under GPL version 2 or later. See * the COPYING file in the top-level directory. @@ -66,73 +67,54 @@ static const IMXClk imx_epit_clocks[] = { */ static void imx_epit_update_int(IMXEPITState *s) { - if (s->sr && (s->cr & CR_OCIEN) && (s->cr & CR_EN)) { + if ((s->sr & SR_OCIF) && (s->cr & CR_OCIEN) && (s->cr & CR_EN)) { qemu_irq_raise(s->irq); } else { qemu_irq_lower(s->irq); } } -/* - * Must be called from within a ptimer_transaction_begin/commit block - * for both s->timer_cmp and s->timer_reload. - */ -static void imx_epit_set_freq(IMXEPITState *s) +static uint32_t imx_epit_get_freq(IMXEPITState *s) { - uint32_t clksrc; - uint32_t prescaler; - - clksrc = extract32(s->cr, CR_CLKSRC_SHIFT, 2); - prescaler = 1 + extract32(s->cr, CR_PRESCALE_SHIFT, 12); - - s->freq = imx_ccm_get_clock_frequency(s->ccm, - imx_epit_clocks[clksrc]) / prescaler; - - DPRINTF("Setting ptimer frequency to %u\n", s->freq); - - if (s->freq) { - ptimer_set_freq(s->timer_reload, s->freq); - ptimer_set_freq(s->timer_cmp, s->freq); - } + uint32_t clksrc = extract32(s->cr, CR_CLKSRC_SHIFT, CR_CLKSRC_BITS); + uint32_t prescaler = 1 + extract32(s->cr, CR_PRESCALE_SHIFT, CR_PRESCALE_BITS); + uint32_t f_in = imx_ccm_get_clock_frequency(s->ccm, imx_epit_clocks[clksrc]); + uint32_t freq = f_in / prescaler; + DPRINTF("ptimer frequency is %u\n", freq); + return freq; } -static void imx_epit_reset(DeviceState *dev) +/* + * This is called both on hardware (device) reset and software reset. + */ +static void imx_epit_reset(IMXEPITState *s, bool is_hard_reset) { - IMXEPITState *s = IMX_EPIT(dev); - - /* - * Soft reset doesn't touch some bits; hard reset clears them - */ - s->cr &= (CR_EN|CR_ENMOD|CR_STOPEN|CR_DOZEN|CR_WAITEN|CR_DBGEN); + /* Soft reset doesn't touch some bits; hard reset clears them */ + if (is_hard_reset) { + s->cr = 0; + } else { + s->cr &= (CR_EN|CR_ENMOD|CR_STOPEN|CR_DOZEN|CR_WAITEN|CR_DBGEN); + } s->sr = 0; s->lr = EPIT_TIMER_MAX; s->cmp = 0; - s->cnt = 0; ptimer_transaction_begin(s->timer_cmp); ptimer_transaction_begin(s->timer_reload); - /* stop both timers */ + + /* + * The reset switches off the input clock, so even if the CR.EN is still + * set, the timers are no longer running. + */ + assert(imx_epit_get_freq(s) == 0); ptimer_stop(s->timer_cmp); ptimer_stop(s->timer_reload); - /* compute new frequency */ - imx_epit_set_freq(s); /* init both timers to EPIT_TIMER_MAX */ ptimer_set_limit(s->timer_cmp, EPIT_TIMER_MAX, 1); ptimer_set_limit(s->timer_reload, EPIT_TIMER_MAX, 1); - if (s->freq && (s->cr & CR_EN)) { - /* if the timer is still enabled, restart it */ - ptimer_run(s->timer_reload, 0); - } ptimer_transaction_commit(s->timer_cmp); ptimer_transaction_commit(s->timer_reload); } -static uint32_t imx_epit_update_count(IMXEPITState *s) -{ - s->cnt = ptimer_get_count(s->timer_reload); - - return s->cnt; -} - static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size) { IMXEPITState *s = IMX_EPIT(opaque); @@ -156,8 +138,7 @@ static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size) break; case 4: /* CNT */ - imx_epit_update_count(s); - reg_value = s->cnt; + reg_value = ptimer_get_count(s->timer_reload); break; default: @@ -171,139 +152,219 @@ static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size) return reg_value; } -/* Must be called from ptimer_transaction_begin/commit block for s->timer_cmp */ -static void imx_epit_reload_compare_timer(IMXEPITState *s) +/* + * Must be called from a ptimer_transaction_begin/commit block for + * s->timer_cmp, but outside of a transaction block of s->timer_reload, + * so the proper counter value is read. + */ +static void imx_epit_update_compare_timer(IMXEPITState *s) { - if ((s->cr & (CR_EN | CR_OCIEN)) == (CR_EN | CR_OCIEN)) { - /* if the compare feature is on and timers are running */ - uint32_t tmp = imx_epit_update_count(s); - uint64_t next; - if (tmp > s->cmp) { - /* It'll fire in this round of the timer */ - next = tmp - s->cmp; - } else { /* catch it next time around */ - next = tmp - s->cmp + ((s->cr & CR_RLD) ? EPIT_TIMER_MAX : s->lr); + uint64_t counter = 0; + bool is_oneshot = false; + /* + * The compare timer only has to run if the timer peripheral is active + * and there is an input clock, Otherwise it can be switched off. + */ + bool is_active = (s->cr & CR_EN) && imx_epit_get_freq(s); + if (is_active) { + /* + * Calculate next timeout for compare timer. Reading the reload + * counter returns proper results only if pending transactions + * on it are committed here. Otherwise stale values are be read. + */ + counter = ptimer_get_count(s->timer_reload); + uint64_t limit = ptimer_get_limit(s->timer_cmp); + /* + * The compare timer is a periodic timer if the limit is at least + * the compare value. Otherwise it may fire at most once in the + * current round. + */ + is_oneshot = (limit < s->cmp); + if (counter >= s->cmp) { + /* The compare timer fires in the current round. */ + counter -= s->cmp; + } else if (!is_oneshot) { + /* + * The compare timer fires after a reload, as it is below the + * compare value already in this round. Note that the counter + * value calculated below can be above the 32-bit limit, which + * is legal here because the compare timer is an internal + * helper ptimer only. + */ + counter += limit - s->cmp; + } else { + /* + * The compare timer won't fire in this round, and the limit is + * set to a value below the compare value. This practically means + * it will never fire, so it can be switched off. + */ + is_active = false; } - ptimer_set_count(s->timer_cmp, next); } + + /* + * Set the compare timer and let it run, or stop it. This is agnostic + * of CR.OCIEN bit, as this bit affects interrupt generation only. The + * compare timer needs to run even if no interrupts are to be generated, + * because the SR.OCIF bit must be updated also. + * Note that the timer might already be stopped or be running with + * counter values. However, finding out when an update is needed and + * when not is not trivial. It's much easier applying the setting again, + * as this does not harm either and the overhead is negligible. + */ + if (is_active) { + ptimer_set_count(s->timer_cmp, counter); + ptimer_run(s->timer_cmp, is_oneshot ? 1 : 0); + } else { + ptimer_stop(s->timer_cmp); + } + +} + +static void imx_epit_write_cr(IMXEPITState *s, uint32_t value) +{ + uint32_t oldcr = s->cr; + + s->cr = value & 0x03ffffff; + + if (s->cr & CR_SWR) { + /* + * Reset clears CR.SWR again. It does not touch CR.EN, but the timers + * are still stopped because the input clock is disabled. + */ + imx_epit_reset(s, false); + } else { + uint32_t freq; + uint32_t toggled_cr_bits = oldcr ^ s->cr; + /* re-initialize the limits if CR.RLD has changed */ + bool set_limit = toggled_cr_bits & CR_RLD; + /* set the counter if the timer got just enabled and CR.ENMOD is set */ + bool is_switched_on = (toggled_cr_bits & s->cr) & CR_EN; + bool set_counter = is_switched_on && (s->cr & CR_ENMOD); + + ptimer_transaction_begin(s->timer_cmp); + ptimer_transaction_begin(s->timer_reload); + freq = imx_epit_get_freq(s); + if (freq) { + ptimer_set_freq(s->timer_reload, freq); + ptimer_set_freq(s->timer_cmp, freq); + } + + if (set_limit || set_counter) { + uint64_t limit = (s->cr & CR_RLD) ? s->lr : EPIT_TIMER_MAX; + ptimer_set_limit(s->timer_reload, limit, set_counter ? 1 : 0); + if (set_limit) { + ptimer_set_limit(s->timer_cmp, limit, 0); + } + } + /* + * If there is an input clock and the peripheral is enabled, then + * ensure the wall clock timer is ticking. Otherwise stop the timers. + * The compare timer will be updated later. + */ + if (freq && (s->cr & CR_EN)) { + ptimer_run(s->timer_reload, 0); + } else { + ptimer_stop(s->timer_reload); + } + /* Commit changes to reload timer, so they can propagate. */ + ptimer_transaction_commit(s->timer_reload); + /* Update compare timer based on the committed reload timer value. */ + imx_epit_update_compare_timer(s); + ptimer_transaction_commit(s->timer_cmp); + } + + /* + * The interrupt state can change due to: + * - reset clears both SR.OCIF and CR.OCIE + * - write to CR.EN or CR.OCIE + */ + imx_epit_update_int(s); +} + +static void imx_epit_write_sr(IMXEPITState *s, uint32_t value) +{ + /* writing 1 to SR.OCIF clears this bit and turns the interrupt off */ + if (value & SR_OCIF) { + s->sr = 0; /* SR.OCIF is the only bit in this register anyway */ + imx_epit_update_int(s); + } +} + +static void imx_epit_write_lr(IMXEPITState *s, uint32_t value) +{ + s->lr = value; + + ptimer_transaction_begin(s->timer_cmp); + ptimer_transaction_begin(s->timer_reload); + if (s->cr & CR_RLD) { + /* Also set the limit if the LRD bit is set */ + /* If IOVW bit is set then set the timer value */ + ptimer_set_limit(s->timer_reload, s->lr, s->cr & CR_IOVW); + ptimer_set_limit(s->timer_cmp, s->lr, 0); + } else if (s->cr & CR_IOVW) { + /* If IOVW bit is set then set the timer value */ + ptimer_set_count(s->timer_reload, s->lr); + } + /* Commit the changes to s->timer_reload, so they can propagate. */ + ptimer_transaction_commit(s->timer_reload); + /* Update the compare timer based on the committed reload timer value. */ + imx_epit_update_compare_timer(s); + ptimer_transaction_commit(s->timer_cmp); +} + +static void imx_epit_write_cmp(IMXEPITState *s, uint32_t value) +{ + s->cmp = value; + + /* Update the compare timer based on the committed reload timer value. */ + ptimer_transaction_begin(s->timer_cmp); + imx_epit_update_compare_timer(s); + ptimer_transaction_commit(s->timer_cmp); } static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { IMXEPITState *s = IMX_EPIT(opaque); - uint64_t oldcr; DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(offset >> 2), (uint32_t)value); switch (offset >> 2) { case 0: /* CR */ - - oldcr = s->cr; - s->cr = value & 0x03ffffff; - if (s->cr & CR_SWR) { - /* handle the reset */ - imx_epit_reset(DEVICE(s)); - /* - * TODO: could we 'break' here? following operations appear - * to duplicate the work imx_epit_reset() already did. - */ - } - - ptimer_transaction_begin(s->timer_cmp); - ptimer_transaction_begin(s->timer_reload); - - if (!(s->cr & CR_SWR)) { - imx_epit_set_freq(s); - } - - if (s->freq && (s->cr & CR_EN) && !(oldcr & CR_EN)) { - if (s->cr & CR_ENMOD) { - if (s->cr & CR_RLD) { - ptimer_set_limit(s->timer_reload, s->lr, 1); - ptimer_set_limit(s->timer_cmp, s->lr, 1); - } else { - ptimer_set_limit(s->timer_reload, EPIT_TIMER_MAX, 1); - ptimer_set_limit(s->timer_cmp, EPIT_TIMER_MAX, 1); - } - } - - imx_epit_reload_compare_timer(s); - ptimer_run(s->timer_reload, 0); - if (s->cr & CR_OCIEN) { - ptimer_run(s->timer_cmp, 0); - } else { - ptimer_stop(s->timer_cmp); - } - } else if (!(s->cr & CR_EN)) { - /* stop both timers */ - ptimer_stop(s->timer_reload); - ptimer_stop(s->timer_cmp); - } else if (s->cr & CR_OCIEN) { - if (!(oldcr & CR_OCIEN)) { - imx_epit_reload_compare_timer(s); - ptimer_run(s->timer_cmp, 0); - } - } else { - ptimer_stop(s->timer_cmp); - } - - ptimer_transaction_commit(s->timer_cmp); - ptimer_transaction_commit(s->timer_reload); + imx_epit_write_cr(s, (uint32_t)value); break; - case 1: /* SR - ACK*/ - /* writing 1 to OCIF clear the OCIF bit */ - if (value & 0x01) { - s->sr = 0; - imx_epit_update_int(s); - } + case 1: /* SR */ + imx_epit_write_sr(s, (uint32_t)value); break; - case 2: /* LR - set ticks */ - s->lr = value; - - ptimer_transaction_begin(s->timer_cmp); - ptimer_transaction_begin(s->timer_reload); - if (s->cr & CR_RLD) { - /* Also set the limit if the LRD bit is set */ - /* If IOVW bit is set then set the timer value */ - ptimer_set_limit(s->timer_reload, s->lr, s->cr & CR_IOVW); - ptimer_set_limit(s->timer_cmp, s->lr, 0); - } else if (s->cr & CR_IOVW) { - /* If IOVW bit is set then set the timer value */ - ptimer_set_count(s->timer_reload, s->lr); - } - - imx_epit_reload_compare_timer(s); - ptimer_transaction_commit(s->timer_cmp); - ptimer_transaction_commit(s->timer_reload); + case 2: /* LR */ + imx_epit_write_lr(s, (uint32_t)value); break; case 3: /* CMP */ - s->cmp = value; - - ptimer_transaction_begin(s->timer_cmp); - imx_epit_reload_compare_timer(s); - ptimer_transaction_commit(s->timer_cmp); - + imx_epit_write_cmp(s, (uint32_t)value); break; default: qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset); - break; } } + static void imx_epit_cmp(void *opaque) { IMXEPITState *s = IMX_EPIT(opaque); - DPRINTF("sr was %d\n", s->sr); + /* The cmp ptimer can't be running when the peripheral is disabled */ + assert(s->cr & CR_EN); - s->sr = 1; + DPRINTF("sr was %d\n", s->sr); + /* Set interrupt status bit SR.OCIF and update the interrupt state */ + s->sr |= SR_OCIF; imx_epit_update_int(s); } @@ -320,15 +381,13 @@ static const MemoryRegionOps imx_epit_ops = { static const VMStateDescription vmstate_imx_timer_epit = { .name = TYPE_IMX_EPIT, - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { + .version_id = 3, + .minimum_version_id = 3, + .fields = (const VMStateField[]) { VMSTATE_UINT32(cr, IMXEPITState), VMSTATE_UINT32(sr, IMXEPITState), VMSTATE_UINT32(lr, IMXEPITState), VMSTATE_UINT32(cmp, IMXEPITState), - VMSTATE_UINT32(cnt, IMXEPITState), - VMSTATE_UINT32(freq, IMXEPITState), VMSTATE_PTIMER(timer_reload, IMXEPITState), VMSTATE_PTIMER(timer_cmp, IMXEPITState), VMSTATE_END_OF_LIST() @@ -347,17 +406,33 @@ static void imx_epit_realize(DeviceState *dev, Error **errp) 0x00001000); sysbus_init_mmio(sbd, &s->iomem); + /* + * The reload timer keeps running when the peripheral is enabled. It is a + * kind of wall clock that does not generate any interrupts. The callback + * needs to be provided, but it does nothing as the ptimer already supports + * all necessary reloading functionality. + */ s->timer_reload = ptimer_init(imx_epit_reload, s, PTIMER_POLICY_LEGACY); + /* + * The compare timer is running only when the peripheral configuration is + * in a state that will generate compare interrupts. + */ s->timer_cmp = ptimer_init(imx_epit_cmp, s, PTIMER_POLICY_LEGACY); } +static void imx_epit_dev_reset(DeviceState *dev) +{ + IMXEPITState *s = IMX_EPIT(dev); + imx_epit_reset(s, true); +} + static void imx_epit_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = imx_epit_realize; - dc->reset = imx_epit_reset; + dc->reset = imx_epit_dev_reset; dc->vmsd = &vmstate_imx_timer_epit; dc->desc = "i.MX periodic timer"; } diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index 80b8302639..a8edaec867 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -63,7 +63,7 @@ static const VMStateDescription vmstate_imx_timer_gpt = { .name = TYPE_IMX_GPT, .version_id = 3, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(cr, IMXGPTState), VMSTATE_UINT32(pr, IMXGPTState), VMSTATE_UINT32(sr, IMXGPTState), @@ -115,6 +115,17 @@ static const IMXClk imx6_gpt_clocks[] = { CLK_HIGH, /* 111 reference clock */ }; +static const IMXClk imx6ul_gpt_clocks[] = { + CLK_NONE, /* 000 No clock source */ + CLK_IPG, /* 001 ipg_clk, 532MHz*/ + CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ + CLK_EXT, /* 011 External clock */ + CLK_32k, /* 100 ipg_clk_32k */ + CLK_NONE, /* 101 not defined */ + CLK_NONE, /* 110 not defined */ + CLK_NONE, /* 111 not defined */ +}; + static const IMXClk imx7_gpt_clocks[] = { CLK_NONE, /* 000 No clock source */ CLK_IPG, /* 001 ipg_clk, 532MHz*/ @@ -539,6 +550,13 @@ static void imx6_gpt_init(Object *obj) s->clocks = imx6_gpt_clocks; } +static void imx6ul_gpt_init(Object *obj) +{ + IMXGPTState *s = IMX_GPT(obj); + + s->clocks = imx6ul_gpt_clocks; +} + static void imx7_gpt_init(Object *obj) { IMXGPTState *s = IMX_GPT(obj); @@ -566,6 +584,12 @@ static const TypeInfo imx6_gpt_info = { .instance_init = imx6_gpt_init, }; +static const TypeInfo imx6ul_gpt_info = { + .name = TYPE_IMX6UL_GPT, + .parent = TYPE_IMX25_GPT, + .instance_init = imx6ul_gpt_init, +}; + static const TypeInfo imx7_gpt_info = { .name = TYPE_IMX7_GPT, .parent = TYPE_IMX25_GPT, @@ -577,6 +601,7 @@ static void imx_gpt_register_types(void) type_register_static(&imx25_gpt_info); type_register_static(&imx31_gpt_info); type_register_static(&imx6_gpt_info); + type_register_static(&imx6ul_gpt_info); type_register_static(&imx7_gpt_info); } diff --git a/hw/timer/meson.build b/hw/timer/meson.build index 03092e2ceb..fc632ad445 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -1,40 +1,40 @@ -softmmu_ss.add(when: 'CONFIG_A9_GTIMER', if_true: files('a9gtimer.c')) -softmmu_ss.add(when: 'CONFIG_ALLWINNER_A10_PIT', if_true: files('allwinner-a10-pit.c')) -softmmu_ss.add(when: 'CONFIG_ALTERA_TIMER', if_true: files('altera_timer.c')) -softmmu_ss.add(when: 'CONFIG_ARM_MPTIMER', if_true: files('arm_mptimer.c')) -softmmu_ss.add(when: 'CONFIG_ARM_TIMER', if_true: files('arm_timer.c')) -softmmu_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_systick.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_timer.c')) -softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_ttc.c')) -softmmu_ss.add(when: 'CONFIG_CMSDK_APB_DUALTIMER', if_true: files('cmsdk-apb-dualtimer.c')) -softmmu_ss.add(when: 'CONFIG_CMSDK_APB_TIMER', if_true: files('cmsdk-apb-timer.c')) -softmmu_ss.add(when: 'CONFIG_RENESAS_TMR', if_true: files('renesas_tmr.c')) -softmmu_ss.add(when: 'CONFIG_RENESAS_CMT', if_true: files('renesas_cmt.c')) -softmmu_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-timer.c')) -softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_timer.c')) -softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_mct.c')) -softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pwm.c')) -softmmu_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_gptimer.c')) -softmmu_ss.add(when: 'CONFIG_HPET', if_true: files('hpet.c')) -softmmu_ss.add(when: 'CONFIG_I8254', if_true: files('i8254_common.c', 'i8254.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_epit.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpt.c')) -softmmu_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gictimer.c')) -softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-timer.c')) -softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_timer.c')) -softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_timer.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gptimer.c')) -softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_synctimer.c')) -softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_timer.c')) -softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_systmr.c')) -softmmu_ss.add(when: 'CONFIG_SH_TIMER', if_true: files('sh_timer.c')) -softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_timer.c')) -softmmu_ss.add(when: 'CONFIG_SSE_COUNTER', if_true: files('sse-counter.c')) -softmmu_ss.add(when: 'CONFIG_SSE_TIMER', if_true: files('sse-timer.c')) -softmmu_ss.add(when: 'CONFIG_STELLARIS_GPTM', if_true: files('stellaris-gptm.c')) -softmmu_ss.add(when: 'CONFIG_STM32F2XX_TIMER', if_true: files('stm32f2xx_timer.c')) -softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_timer.c')) +system_ss.add(when: 'CONFIG_A9_GTIMER', if_true: files('a9gtimer.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_A10_PIT', if_true: files('allwinner-a10-pit.c')) +system_ss.add(when: 'CONFIG_ALTERA_TIMER', if_true: files('altera_timer.c')) +system_ss.add(when: 'CONFIG_ARM_MPTIMER', if_true: files('arm_mptimer.c')) +system_ss.add(when: 'CONFIG_ARM_TIMER', if_true: files('arm_timer.c')) +system_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_systick.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_timer.c')) +system_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_ttc.c')) +system_ss.add(when: 'CONFIG_CMSDK_APB_DUALTIMER', if_true: files('cmsdk-apb-dualtimer.c')) +system_ss.add(when: 'CONFIG_CMSDK_APB_TIMER', if_true: files('cmsdk-apb-timer.c')) +system_ss.add(when: 'CONFIG_RENESAS_TMR', if_true: files('renesas_tmr.c')) +system_ss.add(when: 'CONFIG_RENESAS_CMT', if_true: files('renesas_cmt.c')) +system_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-timer.c')) +system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_timer.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_mct.c')) +system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pwm.c')) +system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_gptimer.c')) +system_ss.add(when: 'CONFIG_HPET', if_true: files('hpet.c')) +system_ss.add(when: 'CONFIG_I8254', if_true: files('i8254_common.c', 'i8254.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_epit.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpt.c')) +system_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gictimer.c')) +system_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-timer.c')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_timer.c')) +system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_timer.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gptimer.c')) +system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_synctimer.c')) +system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_timer.c')) +system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_systmr.c')) +system_ss.add(when: 'CONFIG_SH_TIMER', if_true: files('sh_timer.c')) +system_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_timer.c')) +system_ss.add(when: 'CONFIG_SSE_COUNTER', if_true: files('sse-counter.c')) +system_ss.add(when: 'CONFIG_SSE_TIMER', if_true: files('sse-timer.c')) +system_ss.add(when: 'CONFIG_STELLARIS_GPTM', if_true: files('stellaris-gptm.c')) +system_ss.add(when: 'CONFIG_STM32F2XX_TIMER', if_true: files('stm32f2xx_timer.c')) +system_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_timer.c')) specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_timer.c')) -softmmu_ss.add(when: 'CONFIG_SIFIVE_PWM', if_true: files('sifive_pwm.c')) +system_ss.add(when: 'CONFIG_SIFIVE_PWM', if_true: files('sifive_pwm.c')) specific_ss.add(when: 'CONFIG_AVR_TIMER16', if_true: files('avr_timer16.c')) diff --git a/hw/timer/mss-timer.c b/hw/timer/mss-timer.c index ee7438f168..b66aed56ea 100644 --- a/hw/timer/mss-timer.c +++ b/hw/timer/mss-timer.c @@ -260,7 +260,7 @@ static const VMStateDescription vmstate_timers = { .name = "mss-timer-block", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PTIMER(ptimer, struct Msf2Timer), VMSTATE_UINT32_ARRAY(regs, struct Msf2Timer, R_TIM1_MAX), VMSTATE_END_OF_LIST() @@ -271,7 +271,7 @@ static const VMStateDescription vmstate_mss_timer = { .name = TYPE_MSS_TIMER, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(freq_hz, MSSTimerState), VMSTATE_STRUCT_ARRAY(timers, MSSTimerState, NUM_TIMERS, 0, vmstate_timers, struct Msf2Timer), diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c index 32f5e021f8..779c6049fa 100644 --- a/hw/timer/npcm7xx_timer.c +++ b/hw/timer/npcm7xx_timer.c @@ -138,6 +138,9 @@ static int64_t npcm7xx_timer_count_to_ns(NPCM7xxTimer *t, uint32_t count) /* Convert a time interval in nanoseconds to a timer cycle count. */ static uint32_t npcm7xx_timer_ns_to_count(NPCM7xxTimer *t, int64_t ns) { + if (ns < 0) { + return 0; + } return clock_ns_to_ticks(t->ctrl->clock, ns) / npcm7xx_tcsr_prescaler(t->tcsr); } @@ -634,7 +637,7 @@ static const VMStateDescription vmstate_npcm7xx_base_timer = { .name = "npcm7xx-base-timer", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER(qtimer, NPCM7xxBaseTimer), VMSTATE_INT64(expires_ns, NPCM7xxBaseTimer), VMSTATE_INT64(remaining_ns, NPCM7xxBaseTimer), @@ -646,7 +649,7 @@ static const VMStateDescription vmstate_npcm7xx_timer = { .name = "npcm7xx-timer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(base_timer, NPCM7xxTimer, 0, vmstate_npcm7xx_base_timer, NPCM7xxBaseTimer), @@ -660,7 +663,7 @@ static const VMStateDescription vmstate_npcm7xx_watchdog_timer = { .name = "npcm7xx-watchdog-timer", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(base_timer, NPCM7xxWatchdogTimer, 0, vmstate_npcm7xx_base_timer, NPCM7xxBaseTimer), @@ -673,7 +676,7 @@ static const VMStateDescription vmstate_npcm7xx_timer_ctrl = { .name = "npcm7xx-timer-ctrl", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState), VMSTATE_CLOCK(clock, NPCM7xxTimerCtrlState), VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState, diff --git a/hw/timer/nrf51_timer.c b/hw/timer/nrf51_timer.c index 42be79c736..a33166a881 100644 --- a/hw/timer/nrf51_timer.c +++ b/hw/timer/nrf51_timer.c @@ -45,7 +45,12 @@ static uint32_t update_counter(NRF51TimerState *s, int64_t now) uint32_t ticks = ns_to_ticks(s, now - s->update_counter_ns); s->counter = (s->counter + ticks) % BIT(bitwidths[s->bitmode]); - s->update_counter_ns = now; + /* + * Only advance the sync time to the timestamp of the last tick, + * not all the way to 'now', so we don't lose time if we do + * multiple resyncs in a single tick. + */ + s->update_counter_ns += ticks_to_ns(s, ticks); return ticks; } @@ -356,7 +361,7 @@ static const VMStateDescription vmstate_nrf51_timer = { .name = TYPE_NRF51_TIMER, .version_id = 1, .post_load = nrf51_timer_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER(timer, NRF51TimerState), VMSTATE_INT64(timer_start_ns, NRF51TimerState), VMSTATE_INT64(update_counter_ns, NRF51TimerState), diff --git a/hw/timer/omap_gptimer.c b/hw/timer/omap_gptimer.c index c407190138..34e6af7aff 100644 --- a/hw/timer/omap_gptimer.c +++ b/hw/timer/omap_gptimer.c @@ -159,7 +159,7 @@ static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer) static void omap_gp_timer_tick(void *opaque) { - struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; + struct omap_gp_timer_s *timer = opaque; if (!timer->ar) { timer->st = 0; @@ -179,7 +179,7 @@ static void omap_gp_timer_tick(void *opaque) static void omap_gp_timer_match(void *opaque) { - struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; + struct omap_gp_timer_s *timer = opaque; if (timer->trigger == gpt_trigger_both) omap_gp_timer_trigger(timer); @@ -189,7 +189,7 @@ static void omap_gp_timer_match(void *opaque) static void omap_gp_timer_input(void *opaque, int line, int on) { - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + struct omap_gp_timer_s *s = opaque; int trigger; switch (s->capture) { @@ -219,7 +219,7 @@ static void omap_gp_timer_input(void *opaque, int line, int on) static void omap_gp_timer_clk_update(void *opaque, int line, int on) { - struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; + struct omap_gp_timer_s *timer = opaque; omap_gp_timer_sync(timer); timer->rate = on ? omap_clk_getrate(timer->clk) : 0; @@ -262,7 +262,7 @@ void omap_gp_timer_reset(struct omap_gp_timer_s *s) static uint32_t omap_gp_timer_readw(void *opaque, hwaddr addr) { - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + struct omap_gp_timer_s *s = opaque; switch (addr) { case 0x00: /* TIDR */ @@ -328,7 +328,7 @@ static uint32_t omap_gp_timer_readw(void *opaque, hwaddr addr) static uint32_t omap_gp_timer_readh(void *opaque, hwaddr addr) { - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + struct omap_gp_timer_s *s = opaque; uint32_t ret; if (addr & 2) @@ -340,10 +340,9 @@ static uint32_t omap_gp_timer_readh(void *opaque, hwaddr addr) } } -static void omap_gp_timer_write(void *opaque, hwaddr addr, - uint32_t value) +static void omap_gp_timer_write(void *opaque, hwaddr addr, uint32_t value) { - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + struct omap_gp_timer_s *s = opaque; switch (addr) { case 0x00: /* TIDR */ @@ -440,10 +439,9 @@ static void omap_gp_timer_write(void *opaque, hwaddr addr, } } -static void omap_gp_timer_writeh(void *opaque, hwaddr addr, - uint32_t value) +static void omap_gp_timer_writeh(void *opaque, hwaddr addr, uint32_t value) { - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + struct omap_gp_timer_s *s = opaque; if (addr & 2) omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh); diff --git a/hw/timer/omap_synctimer.c b/hw/timer/omap_synctimer.c index 72b997939b..d93a9344ed 100644 --- a/hw/timer/omap_synctimer.c +++ b/hw/timer/omap_synctimer.c @@ -39,7 +39,7 @@ void omap_synctimer_reset(struct omap_synctimer_s *s) static uint32_t omap_synctimer_readw(void *opaque, hwaddr addr) { - struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque; + struct omap_synctimer_s *s = opaque; switch (addr) { case 0x00: /* 32KSYNCNT_REV */ @@ -55,7 +55,7 @@ static uint32_t omap_synctimer_readw(void *opaque, hwaddr addr) static uint32_t omap_synctimer_readh(void *opaque, hwaddr addr) { - struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque; + struct omap_synctimer_s *s = opaque; uint32_t ret; if (addr & 2) diff --git a/hw/timer/pxa2xx_timer.c b/hw/timer/pxa2xx_timer.c index 2ae5ae3212..6479ab1a8b 100644 --- a/hw/timer/pxa2xx_timer.c +++ b/hw/timer/pxa2xx_timer.c @@ -18,6 +18,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" +#include "sysemu/watchdog.h" #define OSMR0 0x00 #define OSMR1 0x04 @@ -417,7 +418,7 @@ static void pxa2xx_timer_tick(void *opaque) if (t->num == 3) if (i->reset3 & 1) { i->reset3 = 0; - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + watchdog_perform_action(); } } @@ -501,7 +502,7 @@ static const VMStateDescription vmstate_pxa2xx_timer0_regs = { .name = "pxa2xx_timer0", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(value, PXA2xxTimer0), VMSTATE_END_OF_LIST(), }, @@ -511,7 +512,7 @@ static const VMStateDescription vmstate_pxa2xx_timer4_regs = { .name = "pxa2xx_timer4", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(tm, PXA2xxTimer4, 1, vmstate_pxa2xx_timer0_regs, PXA2xxTimer0), VMSTATE_INT32(oldclock, PXA2xxTimer4), @@ -533,7 +534,7 @@ static const VMStateDescription vmstate_pxa2xx_timer_regs = { .version_id = 1, .minimum_version_id = 1, .post_load = pxa25x_timer_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(clock, PXA2xxTimerInfo), VMSTATE_INT32(oldclock, PXA2xxTimerInfo), VMSTATE_UINT64(lastload, PXA2xxTimerInfo), diff --git a/hw/timer/renesas_cmt.c b/hw/timer/renesas_cmt.c index 2e0fd21a36..08832932d2 100644 --- a/hw/timer/renesas_cmt.c +++ b/hw/timer/renesas_cmt.c @@ -57,7 +57,7 @@ static void update_events(RCMTState *cmt, int ch) if ((cmt->cmstr & (1 << ch)) == 0) { /* count disable, so not happened next event. */ - return ; + return; } next_time = cmt->cmcor[ch] - cmt->cmcnt[ch]; next_time *= NANOSECONDS_PER_SECOND; @@ -242,7 +242,7 @@ static const VMStateDescription vmstate_rcmt = { .name = "rx-cmt", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(cmstr, RCMTState), VMSTATE_UINT16_ARRAY(cmcr, RCMTState, CMT_CH), VMSTATE_UINT16_ARRAY(cmcnt, RCMTState, CMT_CH), diff --git a/hw/timer/renesas_tmr.c b/hw/timer/renesas_tmr.c index d96002e1ee..1d47d0615a 100644 --- a/hw/timer/renesas_tmr.c +++ b/hw/timer/renesas_tmr.c @@ -67,18 +67,18 @@ static void update_events(RTMRState *tmr, int ch) int i, event; if (tmr->tccr[ch] == 0) { - return ; + return; } if (FIELD_EX8(tmr->tccr[ch], TCCR, CSS) == 0) { /* external clock mode */ /* event not happened */ - return ; + return; } if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) == CSS_CASCADING) { /* cascading mode */ if (ch == 1) { tmr->next[ch] = none; - return ; + return; } diff[cmia] = concat_reg(tmr->tcora) - concat_reg(tmr->tcnt); diff[cmib] = concat_reg(tmr->tcorb) - concat_reg(tmr->tcnt); @@ -115,7 +115,7 @@ static int elapsed_time(RTMRState *tmr, int ch, int64_t delta) et = tmr->div_round[ch] / divrate; tmr->div_round[ch] %= divrate; } else { - /* disble clock. so no update */ + /* disable clock. so no update */ et = 0; } return et; @@ -384,7 +384,7 @@ static void timer_events(RTMRState *tmr, int ch) tmr->tcorb[ch]) & 0xff; } else { if (ch == 1) { - return ; + return; } tcnt = issue_event(tmr, ch, 16, concat_reg(tmr->tcnt), @@ -447,7 +447,7 @@ static const VMStateDescription vmstate_rtmr = { .name = "rx-tmr", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64(tick, RTMRState), VMSTATE_UINT8_ARRAY(tcnt, RTMRState, TMR_CH), VMSTATE_UINT8_ARRAY(tcora, RTMRState, TMR_CH), diff --git a/hw/timer/sifive_pwm.c b/hw/timer/sifive_pwm.c index c664480ccf..e8610c37dd 100644 --- a/hw/timer/sifive_pwm.c +++ b/hw/timer/sifive_pwm.c @@ -395,7 +395,7 @@ static const VMStateDescription vmstate_sifive_pwm = { .name = TYPE_SIFIVE_PWM, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_ARRAY(timer, SiFivePwmState, 4), VMSTATE_UINT64(tick_offset, SiFivePwmState), VMSTATE_UINT32(pwmcfg, SiFivePwmState), diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index 8c4f6eb06b..5507b0145b 100644 --- a/hw/timer/slavio_timer.c +++ b/hw/timer/slavio_timer.c @@ -344,7 +344,7 @@ static const VMStateDescription vmstate_timer = { .name ="timer", .version_id = 3, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(limit, CPUTimerState), VMSTATE_UINT32(count, CPUTimerState), VMSTATE_UINT32(counthigh, CPUTimerState), @@ -359,7 +359,7 @@ static const VMStateDescription vmstate_slavio_timer = { .name ="slavio_timer", .version_id = 3, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(cputimer, SLAVIO_TIMERState, MAX_CPUS + 1, 3, vmstate_timer, CPUTimerState), VMSTATE_END_OF_LIST() diff --git a/hw/timer/sse-counter.c b/hw/timer/sse-counter.c index 16c0e8ad15..daceedf964 100644 --- a/hw/timer/sse-counter.c +++ b/hw/timer/sse-counter.c @@ -442,7 +442,7 @@ static const VMStateDescription sse_counter_vmstate = { .name = "sse-counter", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CLOCK(clk, SSECounter), VMSTATE_END_OF_LIST() } diff --git a/hw/timer/sse-timer.c b/hw/timer/sse-timer.c index e92e83747d..cb20a9eb79 100644 --- a/hw/timer/sse-timer.c +++ b/hw/timer/sse-timer.c @@ -428,7 +428,7 @@ static const VMStateDescription sse_timer_vmstate = { .name = "sse-timer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER(timer, SSETimer), VMSTATE_UINT32(cntfrq, SSETimer), VMSTATE_UINT32(cntp_ctl, SSETimer), diff --git a/hw/timer/stellaris-gptm.c b/hw/timer/stellaris-gptm.c index fd71c79be4..f28958cefc 100644 --- a/hw/timer/stellaris-gptm.c +++ b/hw/timer/stellaris-gptm.c @@ -250,7 +250,7 @@ static const VMStateDescription vmstate_stellaris_gptm = { .name = "stellaris_gptm", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(config, gptm_state), VMSTATE_UINT32_ARRAY(mode, gptm_state, 2), VMSTATE_UINT32(control, gptm_state), diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c index ba8694dcd3..de4208b1a6 100644 --- a/hw/timer/stm32f2xx_timer.c +++ b/hw/timer/stm32f2xx_timer.c @@ -274,7 +274,7 @@ static const VMStateDescription vmstate_stm32f2xx_timer = { .name = TYPE_STM32F2XX_TIMER, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64(tick_offset, STM32F2XXTimerState), VMSTATE_UINT32(tim_cr1, STM32F2XXTimerState), VMSTATE_UINT32(tim_cr2, STM32F2XXTimerState), diff --git a/hw/timer/trace-events b/hw/timer/trace-events index 3eccef8385..de769f4b71 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -35,7 +35,7 @@ aspeed_timer_read(uint64_t offset, unsigned size, uint64_t value) "From 0x%" PRI # armv7m_systick.c systick_reload(void) "systick reload" -systick_timer_tick(void) "systick reload" +systick_timer_tick(void) "systick tick" systick_read(uint64_t addr, uint32_t value, unsigned size) "systick read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" systick_write(uint64_t addr, uint32_t value, unsigned size) "systick write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" @@ -99,3 +99,18 @@ sifive_pwm_write(uint64_t data, uint64_t offset) "Write 0x%" PRIx64 " at address sh_timer_start_stop(int enable, int current) "%d (%d)" sh_timer_read(uint64_t offset) "tmu012_read 0x%" PRIx64 sh_timer_write(uint64_t offset, uint64_t value) "tmu012_write 0x%" PRIx64 " 0x%08" PRIx64 + +# hpet.c +hpet_timer_id_out_of_range(uint8_t timer_id) "timer id out of range: 0x%" PRIx8 +hpet_invalid_hpet_cfg(uint8_t reg_off) "invalid HPET_CFG + %u" PRIx8 +hpet_ram_read(uint64_t addr) "enter hpet_ram_readl at 0x%" PRIx64 +hpet_ram_read_reading_counter(uint8_t reg_off, uint64_t cur_tick) "reading counter + %" PRIu8 " = 0x%" PRIx64 +hpet_ram_read_invalid(void) "invalid hpet_ram_readl" +hpet_ram_write(uint64_t addr, uint64_t value) "enter hpet_ram_writel at 0x%" PRIx64 " = 0x%" PRIx64 +hpet_ram_write_timer_id(uint64_t timer_id) "hpet_ram_writel timer_id = 0x%" PRIx64 +hpet_ram_write_tn_cfg(void) "hpet_ram_writel HPET_TN_CFG" +hpet_ram_write_invalid_tn_cfg(uint8_t reg_off) "invalid HPET_TN_CFG + %" PRIu8 " write" +hpet_ram_write_tn_cmp(uint8_t reg_off) "hpet_ram_writel HPET_TN_CMP + %" PRIu8 +hpet_ram_write_invalid(void) "invalid hpet_ram_writel" +hpet_ram_write_counter_write_while_enabled(void) "Writing counter while HPET enabled!" +hpet_ram_write_counter_written(uint8_t reg_off, uint64_t value, uint64_t counter) "HPET counter + %" PRIu8 "written. crt = 0x%" PRIx64 " -> 0x%" PRIx64 diff --git a/hw/timer/xilinx_timer.c b/hw/timer/xilinx_timer.c index c7f17cd646..32a9df69e0 100644 --- a/hw/timer/xilinx_timer.c +++ b/hw/timer/xilinx_timer.c @@ -62,10 +62,10 @@ struct xlx_timer }; #define TYPE_XILINX_TIMER "xlnx.xps-timer" -DECLARE_INSTANCE_CHECKER(struct timerblock, XILINX_TIMER, - TYPE_XILINX_TIMER) +typedef struct XpsTimerState XpsTimerState; +DECLARE_INSTANCE_CHECKER(XpsTimerState, XILINX_TIMER, TYPE_XILINX_TIMER) -struct timerblock +struct XpsTimerState { SysBusDevice parent_obj; @@ -76,7 +76,7 @@ struct timerblock struct xlx_timer *timers; }; -static inline unsigned int num_timers(struct timerblock *t) +static inline unsigned int num_timers(XpsTimerState *t) { return 2 - t->one_timer_only; } @@ -87,7 +87,7 @@ static inline unsigned int timer_from_addr(hwaddr addr) return addr >> 2; } -static void timer_update_irq(struct timerblock *t) +static void timer_update_irq(XpsTimerState *t) { unsigned int i, irq = 0; uint32_t csr; @@ -104,7 +104,7 @@ static void timer_update_irq(struct timerblock *t) static uint64_t timer_read(void *opaque, hwaddr addr, unsigned int size) { - struct timerblock *t = opaque; + XpsTimerState *t = opaque; struct xlx_timer *xt; uint32_t r = 0; unsigned int timer; @@ -155,7 +155,7 @@ static void timer_write(void *opaque, hwaddr addr, uint64_t val64, unsigned int size) { - struct timerblock *t = opaque; + XpsTimerState *t = opaque; struct xlx_timer *xt; unsigned int timer; uint32_t value = val64; @@ -202,7 +202,7 @@ static const MemoryRegionOps timer_ops = { static void timer_hit(void *opaque) { struct xlx_timer *xt = opaque; - struct timerblock *t = xt->parent; + XpsTimerState *t = xt->parent; D(fprintf(stderr, "%s %d\n", __func__, xt->nr)); xt->regs[R_TCSR] |= TCSR_TINT; @@ -213,7 +213,7 @@ static void timer_hit(void *opaque) static void xilinx_timer_realize(DeviceState *dev, Error **errp) { - struct timerblock *t = XILINX_TIMER(dev); + XpsTimerState *t = XILINX_TIMER(dev); unsigned int i; /* Init all the ptimers. */ @@ -236,16 +236,15 @@ static void xilinx_timer_realize(DeviceState *dev, Error **errp) static void xilinx_timer_init(Object *obj) { - struct timerblock *t = XILINX_TIMER(obj); + XpsTimerState *t = XILINX_TIMER(obj); /* All timers share a single irq line. */ sysbus_init_irq(SYS_BUS_DEVICE(obj), &t->irq); } static Property xilinx_timer_properties[] = { - DEFINE_PROP_UINT32("clock-frequency", struct timerblock, freq_hz, - 62 * 1000000), - DEFINE_PROP_UINT8("one-timer-only", struct timerblock, one_timer_only, 0), + DEFINE_PROP_UINT32("clock-frequency", XpsTimerState, freq_hz, 62 * 1000000), + DEFINE_PROP_UINT8("one-timer-only", XpsTimerState, one_timer_only, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -260,7 +259,7 @@ static void xilinx_timer_class_init(ObjectClass *klass, void *data) static const TypeInfo xilinx_timer_info = { .name = TYPE_XILINX_TIMER, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct timerblock), + .instance_size = sizeof(XpsTimerState), .instance_init = xilinx_timer_init, .class_init = xilinx_timer_class_init, }; diff --git a/hw/tpm/Kconfig b/hw/tpm/Kconfig index 29e82f3c92..a46663288c 100644 --- a/hw/tpm/Kconfig +++ b/hw/tpm/Kconfig @@ -1,3 +1,10 @@ +config TPM_TIS_I2C + bool + depends on TPM + select TPM_BACKEND + select I2C + select TPM_TIS + config TPM_TIS_ISA bool depends on TPM && ISA_BUS diff --git a/hw/tpm/meson.build b/hw/tpm/meson.build index 1c68d81d6a..6968e60b3f 100644 --- a/hw/tpm/meson.build +++ b/hw/tpm/meson.build @@ -1,8 +1,9 @@ -softmmu_ss.add(when: 'CONFIG_TPM_TIS', if_true: files('tpm_tis_common.c')) -softmmu_ss.add(when: 'CONFIG_TPM_TIS_ISA', if_true: files('tpm_tis_isa.c')) -softmmu_ss.add(when: 'CONFIG_TPM_TIS_SYSBUS', if_true: files('tpm_tis_sysbus.c')) -softmmu_ss.add(when: 'CONFIG_TPM_CRB', if_true: files('tpm_crb.c')) +system_ss.add(when: 'CONFIG_TPM_TIS', if_true: files('tpm_tis_common.c')) +system_ss.add(when: 'CONFIG_TPM_TIS_ISA', if_true: files('tpm_tis_isa.c')) +system_ss.add(when: 'CONFIG_TPM_TIS_SYSBUS', if_true: files('tpm_tis_sysbus.c')) +system_ss.add(when: 'CONFIG_TPM_TIS_I2C', if_true: files('tpm_tis_i2c.c')) +system_ss.add(when: 'CONFIG_TPM_CRB', if_true: files('tpm_crb.c')) +system_ss.add(when: 'CONFIG_TPM_TIS', if_true: files('tpm_ppi.c')) +system_ss.add(when: 'CONFIG_TPM_CRB', if_true: files('tpm_ppi.c')) -specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TPM_TIS'], if_true: files('tpm_ppi.c')) -specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TPM_CRB'], if_true: files('tpm_ppi.c')) specific_ss.add(when: 'CONFIG_TPM_SPAPR', if_true: files('tpm_spapr.c')) diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c index 67db594c48..5cd5a2533b 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -26,6 +26,7 @@ #include "sysemu/tpm_backend.h" #include "sysemu/tpm_util.h" #include "sysemu/reset.h" +#include "sysemu/xen.h" #include "tpm_prop.h" #include "tpm_ppi.h" #include "trace.h" @@ -219,7 +220,7 @@ static int tpm_crb_pre_save(void *opaque) static const VMStateDescription vmstate_tpm_crb = { .name = "tpm-crb", .pre_save = tpm_crb_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, CRBState, TPM_CRB_R_MAX), VMSTATE_END_OF_LIST(), } @@ -308,7 +309,11 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp) TPM_PPI_ADDR_BASE, OBJECT(s)); } - qemu_register_reset(tpm_crb_reset, dev); + if (xen_enabled()) { + tpm_crb_reset(dev); + } else { + qemu_register_reset(tpm_crb_reset, dev); + } } static void tpm_crb_class_init(ObjectClass *klass, void *data) diff --git a/hw/tpm/tpm_ppi.c b/hw/tpm/tpm_ppi.c index 7f74e26ec6..f27ed6c35e 100644 --- a/hw/tpm/tpm_ppi.c +++ b/hw/tpm/tpm_ppi.c @@ -47,8 +47,10 @@ void tpm_ppi_reset(TPMPPI *tpmppi) void tpm_ppi_init(TPMPPI *tpmppi, MemoryRegion *m, hwaddr addr, Object *obj) { - tpmppi->buf = qemu_memalign(qemu_real_host_page_size(), - HOST_PAGE_ALIGN(TPM_PPI_ADDR_SIZE)); + size_t host_page_size = qemu_real_host_page_size(); + + tpmppi->buf = qemu_memalign(host_page_size, + ROUND_UP(TPM_PPI_ADDR_SIZE, host_page_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 index dea7b1333b..e084e987e6 100644 --- a/hw/tpm/tpm_spapr.c +++ b/hw/tpm/tpm_spapr.c @@ -353,7 +353,7 @@ static const VMStateDescription vmstate_spapr_vtpm = { .name = "tpm-spapr", .pre_save = tpm_spapr_pre_save, .post_load = tpm_spapr_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_SPAPR_VIO(vdev, SpaprTpmState), VMSTATE_UINT8(state, SpaprTpmState), diff --git a/hw/tpm/tpm_tis.h b/hw/tpm/tpm_tis.h index f6b5872ba6..6f14896b97 100644 --- a/hw/tpm/tpm_tis.h +++ b/hw/tpm/tpm_tis.h @@ -19,7 +19,7 @@ * specification. * * TPM TIS for TPM 2 implementation following TCG PC Client Platform - * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43 + * TPM Profile (PTP) Specification, Family 2.0, Revision 00.43 */ #ifndef TPM_TPM_TIS_H #define TPM_TPM_TIS_H @@ -86,5 +86,8 @@ 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); +uint32_t tpm_tis_read_data(TPMState *s, hwaddr addr, unsigned size); +void tpm_tis_write_data(TPMState *s, hwaddr addr, uint64_t val, uint32_t size); +uint16_t tpm_tis_get_checksum(TPMState *s); #endif /* TPM_TPM_TIS_H */ diff --git a/hw/tpm/tpm_tis_common.c b/hw/tpm/tpm_tis_common.c index 503be2a541..1bfa28bfd9 100644 --- a/hw/tpm/tpm_tis_common.c +++ b/hw/tpm/tpm_tis_common.c @@ -20,12 +20,14 @@ * specification. * * TPM TIS for TPM 2 implementation following TCG PC Client Platform - * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43 + * TPM Profile (PTP) Specification, Family 2.0, Revision 00.43 */ #include "qemu/osdep.h" #include "hw/irq.h" #include "hw/isa/isa.h" #include "qapi/error.h" +#include "qemu/bswap.h" +#include "qemu/crc-ccitt.h" #include "qemu/module.h" #include "hw/acpi/tpm.h" @@ -447,6 +449,23 @@ static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, return val; } +/* + * A wrapper read function so that it can be directly called without + * mmio. + */ +uint32_t tpm_tis_read_data(TPMState *s, hwaddr addr, unsigned size) +{ + return tpm_tis_mmio_read(s, addr, size); +} + +/* + * Calculate current data buffer checksum + */ +uint16_t tpm_tis_get_checksum(TPMState *s) +{ + return bswap16(crc_ccitt(0, s->buffer, s->rw_offset)); +} + /* * Write a value to a register of the TIS interface * See specs pages 33-63 for description of the registers @@ -588,10 +607,6 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr, break; case TPM_TIS_REG_INT_ENABLE: - if (s->active_locty != locty) { - break; - } - s->loc[locty].inte &= mask; s->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED | TPM_TIS_INT_POLARITY_MASK | @@ -601,10 +616,6 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr, /* hard wired -- ignore */ break; case TPM_TIS_REG_INT_STATUS: - if (s->active_locty != locty) { - break; - } - /* clearing of interrupt flags */ if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) && (s->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) { @@ -767,6 +778,15 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr, } } +/* + * A wrapper write function so that it can be directly called without + * mmio. + */ +void tpm_tis_write_data(TPMState *s, hwaddr addr, uint64_t val, uint32_t size) +{ + tpm_tis_mmio_write(s, addr, val, size); +} + const MemoryRegionOps tpm_tis_memory_ops = { .read = tpm_tis_mmio_read, .write = tpm_tis_mmio_write, @@ -859,7 +879,7 @@ int tpm_tis_pre_save(TPMState *s) const VMStateDescription vmstate_locty = { .name = "tpm-tis/locty", .version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(state, TPMLocality), VMSTATE_UINT32(inte, TPMLocality), VMSTATE_UINT32(ints, TPMLocality), diff --git a/hw/tpm/tpm_tis_i2c.c b/hw/tpm/tpm_tis_i2c.c new file mode 100644 index 0000000000..4bb09655b4 --- /dev/null +++ b/hw/tpm/tpm_tis_i2c.c @@ -0,0 +1,571 @@ +/* + * tpm_tis_i2c.c - QEMU's TPM TIS I2C Device + * + * Copyright (c) 2023 IBM Corporation + * + * Authors: + * Ninad Palsule + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * TPM I2C implementation follows TCG TPM I2c Interface specification, + * Family 2.0, Level 00, Revision 1.00 + * + * TPM TIS for TPM 2 implementation following TCG PC Client Platform + * TPM Profile (PTP) Specification, Family 2.0, Revision 00.43 + * + */ + +#include "qemu/osdep.h" +#include "hw/i2c/i2c.h" +#include "hw/sysbus.h" +#include "hw/acpi/tpm.h" +#include "migration/vmstate.h" +#include "tpm_prop.h" +#include "qemu/log.h" +#include "trace.h" +#include "tpm_tis.h" + +/* Operations */ +#define OP_SEND 1 +#define OP_RECV 2 + +/* Is locality valid */ +#define TPM_TIS_I2C_IS_VALID_LOCTY(x) TPM_TIS_IS_VALID_LOCTY(x) + +typedef struct TPMStateI2C { + /*< private >*/ + I2CSlave parent_obj; + + uint8_t offset; /* offset into data[] */ + uint8_t operation; /* OP_SEND & OP_RECV */ + uint8_t data[5]; /* Data */ + + /* i2c registers */ + uint8_t loc_sel; /* Current locality */ + uint8_t csum_enable; /* Is checksum enabled */ + + /* Derived from the above */ + const char *reg_name; /* Register name */ + uint32_t tis_addr; /* Converted tis address including locty */ + + /*< public >*/ + TPMState state; /* not a QOM object */ + +} TPMStateI2C; + +DECLARE_INSTANCE_CHECKER(TPMStateI2C, TPM_TIS_I2C, + TYPE_TPM_TIS_I2C) + +/* Prototype */ +static inline void tpm_tis_i2c_to_tis_reg(TPMStateI2C *i2cst, uint8_t i2c_reg); + +/* Register map */ +typedef struct regMap { + uint8_t i2c_reg; /* I2C register */ + uint16_t tis_reg; /* TIS register */ + const char *reg_name; /* Register name */ +} I2CRegMap; + +/* + * The register values in the common code is different than the latest + * register numbers as per the spec hence add the conversion map + */ +static const I2CRegMap tpm_tis_reg_map[] = { + /* + * These registers are sent to TIS layer. The register with UNKNOWN + * mapping are not sent to TIS layer and handled in I2c layer. + * NOTE: Adding frequently used registers at the start + */ + { TPM_I2C_REG_DATA_FIFO, TPM_TIS_REG_DATA_FIFO, "FIFO", }, + { TPM_I2C_REG_STS, TPM_TIS_REG_STS, "STS", }, + { TPM_I2C_REG_DATA_CSUM_GET, TPM_I2C_REG_UNKNOWN, "CSUM_GET", }, + { TPM_I2C_REG_LOC_SEL, TPM_I2C_REG_UNKNOWN, "LOC_SEL", }, + { TPM_I2C_REG_ACCESS, TPM_TIS_REG_ACCESS, "ACCESS", }, + { TPM_I2C_REG_INT_ENABLE, TPM_TIS_REG_INT_ENABLE, "INTR_ENABLE",}, + { TPM_I2C_REG_INT_CAPABILITY, TPM_I2C_REG_UNKNOWN, "INTR_CAP", }, + { TPM_I2C_REG_INTF_CAPABILITY, TPM_TIS_REG_INTF_CAPABILITY, "INTF_CAP", }, + { TPM_I2C_REG_DID_VID, TPM_TIS_REG_DID_VID, "DID_VID", }, + { TPM_I2C_REG_RID, TPM_TIS_REG_RID, "RID", }, + { TPM_I2C_REG_I2C_DEV_ADDRESS, TPM_I2C_REG_UNKNOWN, "DEV_ADDRESS",}, + { TPM_I2C_REG_DATA_CSUM_ENABLE, TPM_I2C_REG_UNKNOWN, "CSUM_ENABLE",}, +}; + +static int tpm_tis_i2c_pre_save(void *opaque) +{ + TPMStateI2C *i2cst = opaque; + + return tpm_tis_pre_save(&i2cst->state); +} + +static int tpm_tis_i2c_post_load(void *opaque, int version_id) +{ + TPMStateI2C *i2cst = opaque; + + if (i2cst->offset >= 1) { + tpm_tis_i2c_to_tis_reg(i2cst, i2cst->data[0]); + } + + return 0; +} + +static const VMStateDescription vmstate_tpm_tis_i2c = { + .name = "tpm-tis-i2c", + .version_id = 0, + .pre_save = tpm_tis_i2c_pre_save, + .post_load = tpm_tis_i2c_post_load, + .fields = (const VMStateField[]) { + VMSTATE_BUFFER(state.buffer, TPMStateI2C), + VMSTATE_UINT16(state.rw_offset, TPMStateI2C), + VMSTATE_UINT8(state.active_locty, TPMStateI2C), + VMSTATE_UINT8(state.aborting_locty, TPMStateI2C), + VMSTATE_UINT8(state.next_locty, TPMStateI2C), + + VMSTATE_STRUCT_ARRAY(state.loc, TPMStateI2C, TPM_TIS_NUM_LOCALITIES, 0, + vmstate_locty, TPMLocality), + + /* i2c specifics */ + VMSTATE_UINT8(offset, TPMStateI2C), + VMSTATE_UINT8(operation, TPMStateI2C), + VMSTATE_BUFFER(data, TPMStateI2C), + VMSTATE_UINT8(loc_sel, TPMStateI2C), + VMSTATE_UINT8(csum_enable, TPMStateI2C), + + VMSTATE_END_OF_LIST() + } +}; + +/* + * Set data value. The i2cst->offset is not updated as called in + * the read path. + */ +static void tpm_tis_i2c_set_data(TPMStateI2C *i2cst, uint32_t data) +{ + i2cst->data[1] = data; + i2cst->data[2] = data >> 8; + i2cst->data[3] = data >> 16; + i2cst->data[4] = data >> 24; +} +/* + * Generate interface capability based on what is returned by TIS and what is + * expected by I2C. Save the capability in the data array overwriting the TIS + * capability. + */ +static uint32_t tpm_tis_i2c_interface_capability(TPMStateI2C *i2cst, + uint32_t tis_cap) +{ + uint32_t i2c_cap; + + /* Now generate i2c capability */ + i2c_cap = (TPM_I2C_CAP_INTERFACE_TYPE | + TPM_I2C_CAP_INTERFACE_VER | + TPM_I2C_CAP_TPM2_FAMILY | + TPM_I2C_CAP_LOCALITY_CAP | + TPM_I2C_CAP_BUS_SPEED | + TPM_I2C_CAP_DEV_ADDR_CHANGE); + + /* Now check the TIS and set some capabilities */ + + /* Static burst count set */ + if (tis_cap & TPM_TIS_CAP_BURST_COUNT_STATIC) { + i2c_cap |= TPM_I2C_CAP_BURST_COUNT_STATIC; + } + + return i2c_cap; +} + +/* Convert I2C register to TIS address and returns the name of the register */ +static inline void tpm_tis_i2c_to_tis_reg(TPMStateI2C *i2cst, uint8_t i2c_reg) +{ + const I2CRegMap *reg_map; + int i; + + i2cst->tis_addr = 0xffffffff; + + /* Special case for the STS register. */ + if (i2c_reg >= TPM_I2C_REG_STS && i2c_reg <= TPM_I2C_REG_STS + 3) { + i2c_reg = TPM_I2C_REG_STS; + } + + for (i = 0; i < ARRAY_SIZE(tpm_tis_reg_map); i++) { + reg_map = &tpm_tis_reg_map[i]; + if (reg_map->i2c_reg == i2c_reg) { + i2cst->reg_name = reg_map->reg_name; + i2cst->tis_addr = reg_map->tis_reg; + + /* Include the locality in the address. */ + assert(TPM_TIS_I2C_IS_VALID_LOCTY(i2cst->loc_sel)); + i2cst->tis_addr += (i2cst->loc_sel << TPM_TIS_LOCALITY_SHIFT); + break; + } + } +} + +/* Clear some fields from the structure. */ +static inline void tpm_tis_i2c_clear_data(TPMStateI2C *i2cst) +{ + /* Clear operation and offset */ + i2cst->operation = 0; + i2cst->offset = 0; + i2cst->tis_addr = 0xffffffff; + i2cst->reg_name = NULL; + memset(i2cst->data, 0, sizeof(i2cst->data)); + + return; +} + +/* Send data to TPM */ +static inline void tpm_tis_i2c_tpm_send(TPMStateI2C *i2cst) +{ + uint32_t data; + size_t offset = 0; + uint32_t sz = 4; + + if ((i2cst->operation == OP_SEND) && (i2cst->offset > 1)) { + + switch (i2cst->data[0]) { + case TPM_I2C_REG_DATA_CSUM_ENABLE: + /* + * Checksum is not handled by TIS code hence we will consume the + * register here. + */ + i2cst->csum_enable = i2cst->data[1] & TPM_DATA_CSUM_ENABLED; + break; + case TPM_I2C_REG_DATA_FIFO: + /* Handled in the main i2c_send function */ + break; + case TPM_I2C_REG_LOC_SEL: + /* + * This register is not handled by TIS so save the locality + * locally + */ + if (TPM_TIS_I2C_IS_VALID_LOCTY(i2cst->data[1])) { + i2cst->loc_sel = i2cst->data[1]; + } + break; + default: + /* We handle non-FIFO here */ + + /* Index 0 is a register. Convert byte stream to uint32_t */ + data = i2cst->data[1]; + data |= i2cst->data[2] << 8; + data |= i2cst->data[3] << 16; + data |= i2cst->data[4] << 24; + + /* Add register specific masking */ + switch (i2cst->data[0]) { + case TPM_I2C_REG_INT_ENABLE: + data &= TPM_I2C_INT_ENABLE_MASK; + break; + case TPM_I2C_REG_STS ... TPM_I2C_REG_STS + 3: + /* + * STS register has 4 bytes data. + * As per the specs following writes must be allowed. + * - From base address 1 to 4 bytes are allowed. + * - Single byte write to first or last byte must + * be allowed. + */ + offset = i2cst->data[0] - TPM_I2C_REG_STS; + if (offset > 0) { + sz = 1; + } + data &= (TPM_I2C_STS_WRITE_MASK >> (offset * 8)); + break; + } + + tpm_tis_write_data(&i2cst->state, i2cst->tis_addr + offset, data, + sz); + break; + } + + tpm_tis_i2c_clear_data(i2cst); + } + + return; +} + +/* Callback from TPM to indicate that response is copied */ +static void tpm_tis_i2c_request_completed(TPMIf *ti, int ret) +{ + TPMStateI2C *i2cst = TPM_TIS_I2C(ti); + TPMState *s = &i2cst->state; + + /* Inform the common code. */ + tpm_tis_request_completed(s, ret); +} + +static enum TPMVersion tpm_tis_i2c_get_tpm_version(TPMIf *ti) +{ + TPMStateI2C *i2cst = TPM_TIS_I2C(ti); + TPMState *s = &i2cst->state; + + return tpm_tis_get_tpm_version(s); +} + +static int tpm_tis_i2c_event(I2CSlave *i2c, enum i2c_event event) +{ + TPMStateI2C *i2cst = TPM_TIS_I2C(i2c); + int ret = 0; + + switch (event) { + case I2C_START_RECV: + trace_tpm_tis_i2c_event("START_RECV"); + break; + case I2C_START_SEND: + trace_tpm_tis_i2c_event("START_SEND"); + tpm_tis_i2c_clear_data(i2cst); + break; + case I2C_FINISH: + trace_tpm_tis_i2c_event("FINISH"); + if (i2cst->operation == OP_SEND) { + tpm_tis_i2c_tpm_send(i2cst); + } else { + tpm_tis_i2c_clear_data(i2cst); + } + break; + default: + break; + } + + return ret; +} + +/* + * If data is for FIFO then it is received from tpm_tis_common buffer + * otherwise it will be handled using single call to common code and + * cached in the local buffer. + */ +static uint8_t tpm_tis_i2c_recv(I2CSlave *i2c) +{ + int ret = 0; + uint32_t data_read; + TPMStateI2C *i2cst = TPM_TIS_I2C(i2c); + TPMState *s = &i2cst->state; + uint16_t i2c_reg = i2cst->data[0]; + size_t offset; + + if (i2cst->operation == OP_RECV) { + + /* Do not cache FIFO data. */ + if (i2cst->data[0] == TPM_I2C_REG_DATA_FIFO) { + data_read = tpm_tis_read_data(s, i2cst->tis_addr, 1); + ret = (data_read & 0xff); + } else if (i2cst->offset < sizeof(i2cst->data)) { + ret = i2cst->data[i2cst->offset++]; + } + + } else if ((i2cst->operation == OP_SEND) && (i2cst->offset < 2)) { + /* First receive call after send */ + + i2cst->operation = OP_RECV; + + switch (i2c_reg) { + case TPM_I2C_REG_LOC_SEL: + /* Location selection register is managed by i2c */ + tpm_tis_i2c_set_data(i2cst, i2cst->loc_sel); + break; + case TPM_I2C_REG_DATA_FIFO: + /* FIFO data is directly read from TPM TIS */ + data_read = tpm_tis_read_data(s, i2cst->tis_addr, 1); + tpm_tis_i2c_set_data(i2cst, (data_read & 0xff)); + break; + case TPM_I2C_REG_DATA_CSUM_ENABLE: + tpm_tis_i2c_set_data(i2cst, i2cst->csum_enable); + break; + case TPM_I2C_REG_INT_CAPABILITY: + /* + * Interrupt is not supported in the linux kernel hence we cannot + * test this model with interrupts. + */ + tpm_tis_i2c_set_data(i2cst, TPM_I2C_INT_ENABLE_MASK); + break; + case TPM_I2C_REG_DATA_CSUM_GET: + /* + * Checksum registers are not supported by common code hence + * call a common code to get the checksum. + */ + data_read = tpm_tis_get_checksum(s); + + /* Save the byte stream in data field */ + tpm_tis_i2c_set_data(i2cst, data_read); + break; + default: + data_read = tpm_tis_read_data(s, i2cst->tis_addr, 4); + + switch (i2c_reg) { + case TPM_I2C_REG_INTF_CAPABILITY: + /* Prepare the capabilities as per I2C interface */ + data_read = tpm_tis_i2c_interface_capability(i2cst, + data_read); + break; + case TPM_I2C_REG_STS ... TPM_I2C_REG_STS + 3: + offset = i2c_reg - TPM_I2C_REG_STS; + /* + * As per specs, STS bit 31:26 are reserved and must + * be set to 0 + */ + data_read &= TPM_I2C_STS_READ_MASK; + /* + * STS register has 4 bytes data. + * As per the specs following reads must be allowed. + * - From base address 1 to 4 bytes are allowed. + * - Last byte must be allowed to read as a single byte + * - Second and third byte must be allowed to read as two + * two bytes. + */ + data_read >>= (offset * 8); + break; + } + + /* Save byte stream in data[] */ + tpm_tis_i2c_set_data(i2cst, data_read); + break; + } + + /* Return first byte with this call */ + i2cst->offset = 1; /* keep the register value intact for debug */ + ret = i2cst->data[i2cst->offset++]; + } else { + i2cst->operation = OP_RECV; + } + + trace_tpm_tis_i2c_recv(ret); + + return ret; +} + +/* + * Send function only remembers data in the buffer and then calls + * TPM TIS common code during FINISH event. + */ +static int tpm_tis_i2c_send(I2CSlave *i2c, uint8_t data) +{ + TPMStateI2C *i2cst = TPM_TIS_I2C(i2c); + + /* Reject non-supported registers. */ + if (i2cst->offset == 0) { + /* Convert I2C register to TIS register */ + tpm_tis_i2c_to_tis_reg(i2cst, data); + if (i2cst->tis_addr == 0xffffffff) { + return 0xffffffff; + } + + trace_tpm_tis_i2c_send_reg(i2cst->reg_name, data); + + /* We do not support device address change */ + if (data == TPM_I2C_REG_I2C_DEV_ADDRESS) { + qemu_log_mask(LOG_UNIMP, "%s: Device address change " + "is not supported.\n", __func__); + return 0xffffffff; + } + } else { + trace_tpm_tis_i2c_send(data); + } + + if (i2cst->offset < sizeof(i2cst->data)) { + i2cst->operation = OP_SEND; + + /* + * In two cases, we save values in the local buffer. + * 1) The first value is always a register. + * 2) In case of non-FIFO multibyte registers, TIS expects full + * register value hence I2C layer cache the register value and send + * to TIS during FINISH event. + */ + if ((i2cst->offset == 0) || + (i2cst->data[0] != TPM_I2C_REG_DATA_FIFO)) { + i2cst->data[i2cst->offset++] = data; + } else { + /* + * The TIS can process FIFO data one byte at a time hence the FIFO + * data is sent to TIS directly. + */ + tpm_tis_write_data(&i2cst->state, i2cst->tis_addr, data, 1); + } + + return 0; + } + + /* Return non-zero to indicate NAK */ + return 1; +} + +static Property tpm_tis_i2c_properties[] = { + DEFINE_PROP_TPMBE("tpmdev", TPMStateI2C, state.be_driver), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tpm_tis_i2c_realizefn(DeviceState *dev, Error **errp) +{ + TPMStateI2C *i2cst = TPM_TIS_I2C(dev); + TPMState *s = &i2cst->state; + + if (!tpm_find()) { + error_setg(errp, "at most one TPM device is permitted"); + return; + } + + /* + * Get the backend pointer. It is not initialized properly during + * device_class_set_props + */ + s->be_driver = qemu_find_tpm_be("tpm0"); + + if (!s->be_driver) { + error_setg(errp, "'tpmdev' property is required"); + return; + } +} + +static void tpm_tis_i2c_reset(DeviceState *dev) +{ + TPMStateI2C *i2cst = TPM_TIS_I2C(dev); + TPMState *s = &i2cst->state; + + tpm_tis_i2c_clear_data(i2cst); + + i2cst->csum_enable = 0; + i2cst->loc_sel = 0x00; + + return tpm_tis_reset(s); +} + +static void tpm_tis_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + TPMIfClass *tc = TPM_IF_CLASS(klass); + + dc->realize = tpm_tis_i2c_realizefn; + dc->reset = tpm_tis_i2c_reset; + dc->vmsd = &vmstate_tpm_tis_i2c; + device_class_set_props(dc, tpm_tis_i2c_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + k->event = tpm_tis_i2c_event; + k->recv = tpm_tis_i2c_recv; + k->send = tpm_tis_i2c_send; + + tc->model = TPM_MODEL_TPM_TIS; + tc->request_completed = tpm_tis_i2c_request_completed; + tc->get_version = tpm_tis_i2c_get_tpm_version; +} + +static const TypeInfo tpm_tis_i2c_info = { + .name = TYPE_TPM_TIS_I2C, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(TPMStateI2C), + .class_init = tpm_tis_i2c_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_TPM_IF }, + { } + } +}; + +static void tpm_tis_i2c_register_types(void) +{ + type_register_static(&tpm_tis_i2c_info); +} + +type_init(tpm_tis_i2c_register_types) diff --git a/hw/tpm/tpm_tis_isa.c b/hw/tpm/tpm_tis_isa.c index 3477afd735..8887b3c9c4 100644 --- a/hw/tpm/tpm_tis_isa.c +++ b/hw/tpm/tpm_tis_isa.c @@ -19,7 +19,7 @@ * specification. * * TPM TIS for TPM 2 implementation following TCG PC Client Platform - * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43 + * TPM Profile (PTP) Specification, Family 2.0, Revision 00.43 */ #include "qemu/osdep.h" @@ -30,6 +30,7 @@ #include "tpm_prop.h" #include "tpm_tis.h" #include "qom/object.h" +#include "hw/acpi/acpi_aml_interface.h" struct TPMStateISA { /*< private >*/ @@ -52,7 +53,7 @@ static const VMStateDescription vmstate_tpm_tis_isa = { .name = "tpm-tis", .version_id = 0, .pre_save = tpm_tis_pre_save_isa, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BUFFER(state.buffer, TPMStateISA), VMSTATE_UINT16(state.rw_offset, TPMStateISA), VMSTATE_UINT8(state.active_locty, TPMStateISA), @@ -138,10 +139,39 @@ static void tpm_tis_isa_realizefn(DeviceState *dev, Error **errp) } } +static void build_tpm_tis_isa_aml(AcpiDevAmlIf *adev, Aml *scope) +{ + Aml *dev, *crs; + TPMStateISA *isadev = TPM_TIS_ISA(adev); + TPMIf *ti = TPM_IF(isadev); + + dev = aml_device("TPM"); + if (tpm_tis_isa_get_tpm_version(ti) == TPM_VERSION_2_0) { + aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101"))); + aml_append(dev, aml_name_decl("_STR", aml_string("TPM 2.0 Device"))); + } else { + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C31"))); + } + aml_append(dev, aml_name_decl("_UID", aml_int(1))); + aml_append(dev, aml_name_decl("_STA", aml_int(0xF))); + crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE, TPM_TIS_ADDR_SIZE, + AML_READ_WRITE)); + /* + * FIXME: TPM_TIS_IRQ=5 conflicts with PNP0C0F irqs, + * fix default TPM_TIS_IRQ value there to use some unused IRQ + */ + /* aml_append(crs, aml_irq_no_flags(isadev->state.irq_num)); */ + aml_append(dev, aml_name_decl("_CRS", crs)); + tpm_build_ppi_acpi(ti, dev); + aml_append(scope, dev); +} + static void tpm_tis_isa_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); TPMIfClass *tc = TPM_IF_CLASS(klass); + AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); device_class_set_props(dc, tpm_tis_isa_properties); dc->vmsd = &vmstate_tpm_tis_isa; @@ -151,6 +181,7 @@ static void tpm_tis_isa_class_init(ObjectClass *klass, void *data) tc->request_completed = tpm_tis_isa_request_completed; tc->get_version = tpm_tis_isa_get_tpm_version; set_bit(DEVICE_CATEGORY_MISC, dc->categories); + adevc->build_dev_aml = build_tpm_tis_isa_aml; } static const TypeInfo tpm_tis_isa_info = { @@ -161,6 +192,7 @@ static const TypeInfo tpm_tis_isa_info = { .class_init = tpm_tis_isa_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_TPM_IF }, + { TYPE_ACPI_DEV_AML_IF }, { } } }; diff --git a/hw/tpm/tpm_tis_sysbus.c b/hw/tpm/tpm_tis_sysbus.c index 45e63efd63..941f7f7f62 100644 --- a/hw/tpm/tpm_tis_sysbus.c +++ b/hw/tpm/tpm_tis_sysbus.c @@ -19,7 +19,7 @@ * specification. * * TPM TIS for TPM 2 implementation following TCG PC Client Platform - * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43 + * TPM Profile (PTP) Specification, Family 2.0, Revision 00.43 */ #include "qemu/osdep.h" @@ -52,7 +52,7 @@ static const VMStateDescription vmstate_tpm_tis_sysbus = { .name = "tpm-tis", .version_id = 0, .pre_save = tpm_tis_pre_save_sysbus, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BUFFER(state.buffer, TPMStateSysBus), VMSTATE_UINT16(state.rw_offset, TPMStateSysBus), VMSTATE_UINT8(state.active_locty, TPMStateSysBus), @@ -93,7 +93,6 @@ static void tpm_tis_sysbus_reset(DeviceState *dev) 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, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events index f17110458e..fa882dfefe 100644 --- a/hw/tpm/trace-events +++ b/hw/tpm/trace-events @@ -36,3 +36,9 @@ 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" + +# tpm_tis_i2c.c +tpm_tis_i2c_recv(uint8_t data) "TPM I2C read: 0x%X" +tpm_tis_i2c_send(uint8_t data) "TPM I2C write: 0x%X" +tpm_tis_i2c_event(const char *event) "TPM I2C event: %s" +tpm_tis_i2c_send_reg(const char *name, int reg) "TPM I2C write register: %s(0x%X)" diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index b6810e3be0..c29db8b451 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -89,9 +89,7 @@ static void tricore_testboard_init(MachineState *machine, int board_id) memory_region_add_subregion(sysmem, 0xf0050000, pcp_data); memory_region_add_subregion(sysmem, 0xf0060000, pcp_text); - test_dev = g_new(TriCoreTestDeviceState, 1); - object_initialize(test_dev, sizeof(TriCoreTestDeviceState), - TYPE_TRICORE_TESTDEVICE); + test_dev = TRICORE_TESTDEVICE(qdev_new(TYPE_TRICORE_TESTDEVICE)); memory_region_add_subregion(sysmem, 0xf0000000, &test_dev->iomem); diff --git a/hw/tricore/tricore_testdevice.c b/hw/tricore/tricore_testdevice.c index a1563aa568..9028d970b0 100644 --- a/hw/tricore/tricore_testdevice.c +++ b/hw/tricore/tricore_testdevice.c @@ -16,6 +16,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/sysbus.h" #include "hw/qdev-properties.h" #include "hw/tricore/tricore_testdevice.h" @@ -23,6 +24,9 @@ static void tricore_testdevice_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { + if (value != 0) { + qemu_log_mask(LOG_GUEST_ERROR, "Test %" PRIu64 " failed!\n", value); + } exit(value); } diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig new file mode 100644 index 0000000000..b7b3392e85 --- /dev/null +++ b/hw/ufs/Kconfig @@ -0,0 +1,4 @@ +config UFS_PCI + bool + default y if PCI_DEVICES + depends on PCI diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c new file mode 100644 index 0000000000..81bfff9b4e --- /dev/null +++ b/hw/ufs/lu.c @@ -0,0 +1,446 @@ +/* + * QEMU UFS Logical Unit + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * This code is licensed under the GNU GPL v2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu/memalign.h" +#include "hw/scsi/scsi.h" +#include "scsi/constants.h" +#include "sysemu/block-backend.h" +#include "qemu/cutils.h" +#include "trace.h" +#include "ufs.h" + +#define SCSI_COMMAND_FAIL (-1) + +static void ufs_build_upiu_sense_data(UfsRequest *req, uint8_t *sense, + uint32_t sense_len) +{ + req->rsp_upiu.sr.sense_data_len = cpu_to_be16(sense_len); + assert(sense_len <= SCSI_SENSE_LEN); + memcpy(req->rsp_upiu.sr.sense_data, sense, sense_len); +} + +static void ufs_build_scsi_response_upiu(UfsRequest *req, uint8_t *sense, + uint32_t sense_len, + uint32_t transfered_len, + int16_t status) +{ + uint32_t expected_len = be32_to_cpu(req->req_upiu.sc.exp_data_transfer_len); + uint8_t flags = 0, response = UFS_COMMAND_RESULT_SUCCESS; + uint16_t data_segment_length; + + if (expected_len > transfered_len) { + req->rsp_upiu.sr.residual_transfer_count = + cpu_to_be32(expected_len - transfered_len); + flags |= UFS_UPIU_FLAG_UNDERFLOW; + } else if (expected_len < transfered_len) { + req->rsp_upiu.sr.residual_transfer_count = + cpu_to_be32(transfered_len - expected_len); + flags |= UFS_UPIU_FLAG_OVERFLOW; + } + + if (status != 0) { + ufs_build_upiu_sense_data(req, sense, sense_len); + response = UFS_COMMAND_RESULT_FAIL; + } + + data_segment_length = + cpu_to_be16(sense_len + sizeof(req->rsp_upiu.sr.sense_data_len)); + ufs_build_upiu_header(req, UFS_UPIU_TRANSACTION_RESPONSE, flags, response, + status, data_segment_length); +} + +static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid) +{ + UfsRequest *req = scsi_req->hba_private; + int16_t status = scsi_req->status; + + uint32_t transfered_len = scsi_req->cmd.xfer - resid; + + ufs_build_scsi_response_upiu(req, scsi_req->sense, scsi_req->sense_len, + transfered_len, status); + + ufs_complete_req(req, UFS_REQUEST_SUCCESS); + + scsi_req->hba_private = NULL; + scsi_req_unref(scsi_req); +} + +static QEMUSGList *ufs_get_sg_list(SCSIRequest *scsi_req) +{ + UfsRequest *req = scsi_req->hba_private; + return req->sg; +} + +static const struct SCSIBusInfo ufs_scsi_info = { + .tcq = true, + .max_target = 0, + .max_lun = UFS_MAX_LUS, + .max_channel = 0, + + .get_sg_list = ufs_get_sg_list, + .complete = ufs_scsi_command_complete, +}; + +static int ufs_emulate_report_luns(UfsRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ + UfsHc *u = req->hc; + int len = 0; + + /* TODO: Support for cases where SELECT REPORT is 1 and 2 */ + if (req->req_upiu.sc.cdb[2] != 0) { + return SCSI_COMMAND_FAIL; + } + + len += 8; + + for (uint8_t lun = 0; lun < UFS_MAX_LUS; ++lun) { + if (u->lus[lun]) { + if (len + 8 > outbuf_len) { + break; + } + + memset(outbuf + len, 0, 8); + outbuf[len] = 0; + outbuf[len + 1] = lun; + len += 8; + } + } + + /* store the LUN list length */ + stl_be_p(outbuf, len - 8); + + return len; +} + +static int ufs_scsi_emulate_vpd_page(UfsRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ + uint8_t page_code = req->req_upiu.sc.cdb[2]; + int start, buflen = 0; + + outbuf[buflen++] = TYPE_WLUN; + outbuf[buflen++] = page_code; + outbuf[buflen++] = 0x00; + outbuf[buflen++] = 0x00; + start = buflen; + + switch (page_code) { + case 0x00: /* Supported page codes, mandatory */ + { + outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ + outbuf[buflen++] = 0x87; /* mode page policy */ + break; + } + case 0x87: /* Mode Page Policy, mandatory */ + { + outbuf[buflen++] = 0x3f; /* apply to all mode pages and subpages */ + outbuf[buflen++] = 0xff; + outbuf[buflen++] = 0; /* shared */ + outbuf[buflen++] = 0; + break; + } + default: + return SCSI_COMMAND_FAIL; + } + /* done with EVPD */ + assert(buflen - start <= 255); + outbuf[start - 1] = buflen - start; + return buflen; +} + +static int ufs_emulate_wlun_inquiry(UfsRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ + if (outbuf_len < SCSI_INQUIRY_LEN) { + return 0; + } + + if (req->req_upiu.sc.cdb[1] & 0x1) { + /* Vital product data */ + return ufs_scsi_emulate_vpd_page(req, outbuf, outbuf_len); + } + + /* Standard INQUIRY data */ + if (req->req_upiu.sc.cdb[2] != 0) { + return SCSI_COMMAND_FAIL; + } + + outbuf[0] = TYPE_WLUN; + outbuf[1] = 0; + outbuf[2] = 0x6; /* SPC-4 */ + outbuf[3] = 0x2; + outbuf[4] = 31; + outbuf[5] = 0; + outbuf[6] = 0; + outbuf[7] = 0x2; + strpadcpy((char *)&outbuf[8], 8, "QEMU", ' '); + strpadcpy((char *)&outbuf[16], 16, "QEMU UFS", ' '); + memset(&outbuf[32], 0, 4); + + return SCSI_INQUIRY_LEN; +} + +static UfsReqResult ufs_emulate_scsi_cmd(UfsLu *lu, UfsRequest *req) +{ + uint8_t lun = lu->lun; + uint8_t outbuf[4096]; + uint8_t sense_buf[UFS_SENSE_SIZE]; + uint8_t scsi_status; + int len = 0; + + switch (req->req_upiu.sc.cdb[0]) { + case REPORT_LUNS: + len = ufs_emulate_report_luns(req, outbuf, sizeof(outbuf)); + if (len == SCSI_COMMAND_FAIL) { + scsi_build_sense(sense_buf, SENSE_CODE(INVALID_FIELD)); + scsi_status = CHECK_CONDITION; + } else { + scsi_status = GOOD; + } + break; + case INQUIRY: + len = ufs_emulate_wlun_inquiry(req, outbuf, sizeof(outbuf)); + if (len == SCSI_COMMAND_FAIL) { + scsi_build_sense(sense_buf, SENSE_CODE(INVALID_FIELD)); + scsi_status = CHECK_CONDITION; + } else { + scsi_status = GOOD; + } + break; + case REQUEST_SENSE: + /* Just return no sense data */ + len = scsi_build_sense_buf(outbuf, sizeof(outbuf), SENSE_CODE(NO_SENSE), + true); + scsi_status = GOOD; + break; + case START_STOP: + /* TODO: Revisit it when Power Management is implemented */ + if (lun == UFS_UPIU_UFS_DEVICE_WLUN) { + scsi_status = GOOD; + break; + } + /* fallthrough */ + default: + scsi_build_sense(sense_buf, SENSE_CODE(INVALID_OPCODE)); + scsi_status = CHECK_CONDITION; + } + + len = MIN(len, (int)req->data_len); + if (scsi_status == GOOD && len > 0 && + dma_buf_read(outbuf, len, NULL, req->sg, MEMTXATTRS_UNSPECIFIED) != + MEMTX_OK) { + return UFS_REQUEST_FAIL; + } + + ufs_build_scsi_response_upiu(req, sense_buf, sizeof(sense_buf), len, + scsi_status); + return UFS_REQUEST_SUCCESS; +} + +static UfsReqResult ufs_process_scsi_cmd(UfsLu *lu, UfsRequest *req) +{ + uint8_t task_tag = req->req_upiu.header.task_tag; + + /* + * Each ufs-lu has its own independent virtual SCSI bus. Therefore, we can't + * use scsi_target_emulate_report_luns() which gets all lu information over + * the SCSI bus. Therefore, we use ufs_emulate_scsi_cmd() like the + * well-known lu. + */ + if (req->req_upiu.sc.cdb[0] == REPORT_LUNS) { + return ufs_emulate_scsi_cmd(lu, req); + } + + SCSIRequest *scsi_req = + scsi_req_new(lu->scsi_dev, task_tag, lu->lun, req->req_upiu.sc.cdb, + UFS_CDB_SIZE, req); + + uint32_t len = scsi_req_enqueue(scsi_req); + if (len) { + scsi_req_continue(scsi_req); + } + + return UFS_REQUEST_NO_COMPLETE; +} + +static Property ufs_lu_props[] = { + DEFINE_PROP_DRIVE("drive", UfsLu, conf.blk), + DEFINE_PROP_UINT8("lun", UfsLu, lun, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static bool ufs_add_lu(UfsHc *u, UfsLu *lu, Error **errp) +{ + BlockBackend *blk = lu->conf.blk; + int64_t brdv_len = blk_getlength(blk); + uint64_t raw_dev_cap = + be64_to_cpu(u->geometry_desc.total_raw_device_capacity); + + if (u->device_desc.number_lu >= UFS_MAX_LUS) { + error_setg(errp, "ufs host controller has too many logical units."); + return false; + } + + if (u->lus[lu->lun] != NULL) { + error_setg(errp, "ufs logical unit %d already exists.", lu->lun); + return false; + } + + u->lus[lu->lun] = lu; + u->device_desc.number_lu++; + raw_dev_cap += (brdv_len >> UFS_GEOMETRY_CAPACITY_SHIFT); + u->geometry_desc.total_raw_device_capacity = cpu_to_be64(raw_dev_cap); + return true; +} + +void ufs_init_wlu(UfsLu *wlu, uint8_t wlun) +{ + wlu->lun = wlun; + wlu->scsi_op = &ufs_emulate_scsi_cmd; +} + +static void ufs_init_lu(UfsLu *lu) +{ + BlockBackend *blk = lu->conf.blk; + int64_t brdv_len = blk_getlength(blk); + + memset(&lu->unit_desc, 0, sizeof(lu->unit_desc)); + lu->unit_desc.length = sizeof(UnitDescriptor); + lu->unit_desc.descriptor_idn = UFS_QUERY_DESC_IDN_UNIT; + lu->unit_desc.lu_enable = 0x01; + lu->unit_desc.logical_block_size = UFS_BLOCK_SIZE_SHIFT; + lu->unit_desc.unit_index = lu->lun; + lu->unit_desc.logical_block_count = + cpu_to_be64(brdv_len / (1 << lu->unit_desc.logical_block_size)); + + lu->scsi_op = &ufs_process_scsi_cmd; +} + +static bool ufs_lu_check_constraints(UfsLu *lu, Error **errp) +{ + if (!lu->conf.blk) { + error_setg(errp, "drive property not set"); + return false; + } + + if (lu->lun >= UFS_MAX_LUS) { + error_setg(errp, "lun must be between 0 and %d", UFS_MAX_LUS - 1); + return false; + } + + return true; +} + +static void ufs_init_scsi_device(UfsLu *lu, BlockBackend *blk, Error **errp) +{ + DeviceState *scsi_dev; + + scsi_bus_init(&lu->bus, sizeof(lu->bus), DEVICE(lu), &ufs_scsi_info); + + blk_ref(blk); + blk_detach_dev(blk, DEVICE(lu)); + lu->conf.blk = NULL; + + /* + * The ufs-lu is the device that is wrapping the scsi-hd. It owns a virtual + * SCSI bus that serves the scsi-hd. + */ + scsi_dev = qdev_new("scsi-hd"); + object_property_add_child(OBJECT(&lu->bus), "ufs-scsi", OBJECT(scsi_dev)); + + qdev_prop_set_uint32(scsi_dev, "physical_block_size", UFS_BLOCK_SIZE); + qdev_prop_set_uint32(scsi_dev, "logical_block_size", UFS_BLOCK_SIZE); + qdev_prop_set_uint32(scsi_dev, "scsi-id", 0); + qdev_prop_set_uint32(scsi_dev, "lun", lu->lun); + if (!qdev_prop_set_drive_err(scsi_dev, "drive", blk, errp)) { + object_unparent(OBJECT(scsi_dev)); + return; + } + + if (!qdev_realize_and_unref(scsi_dev, &lu->bus.qbus, errp)) { + object_unparent(OBJECT(scsi_dev)); + return; + } + + blk_unref(blk); + lu->scsi_dev = SCSI_DEVICE(scsi_dev); +} + +static void ufs_lu_realize(DeviceState *dev, Error **errp) +{ + UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev); + BusState *s = qdev_get_parent_bus(dev); + UfsHc *u = UFS(s->parent); + BlockBackend *blk = lu->conf.blk; + + if (!ufs_lu_check_constraints(lu, errp)) { + return; + } + + if (!blk) { + error_setg(errp, "drive property not set"); + return; + } + + if (!blkconf_blocksizes(&lu->conf, errp)) { + return; + } + + if (!blkconf_apply_backend_options(&lu->conf, !blk_supports_write_perm(blk), + true, errp)) { + return; + } + + ufs_init_lu(lu); + if (!ufs_add_lu(u, lu, errp)) { + return; + } + + ufs_init_scsi_device(lu, blk, errp); +} + +static void ufs_lu_unrealize(DeviceState *dev) +{ + UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev); + + if (lu->scsi_dev) { + object_unref(OBJECT(lu->scsi_dev)); + lu->scsi_dev = NULL; + } +} + +static void ufs_lu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ufs_lu_realize; + dc->unrealize = ufs_lu_unrealize; + dc->bus_type = TYPE_UFS_BUS; + device_class_set_props(dc, ufs_lu_props); + dc->desc = "Virtual UFS logical unit"; +} + +static const TypeInfo ufs_lu_info = { + .name = TYPE_UFS_LU, + .parent = TYPE_DEVICE, + .class_init = ufs_lu_class_init, + .instance_size = sizeof(UfsLu), +}; + +static void ufs_lu_register_types(void) +{ + type_register_static(&ufs_lu_info); +} + +type_init(ufs_lu_register_types) diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build new file mode 100644 index 0000000000..6e68328b93 --- /dev/null +++ b/hw/ufs/meson.build @@ -0,0 +1 @@ +system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c', 'lu.c')) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events new file mode 100644 index 0000000000..665e1a942b --- /dev/null +++ b/hw/ufs/trace-events @@ -0,0 +1,33 @@ +# ufs.c +ufs_irq_raise(void) "INTx" +ufs_irq_lower(void) "INTx" +ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" +ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32"" +ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8"" +ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8"" +ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"PRIx32", ucmdarg3 0x%"PRIx32"" + +# error condition +ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" +ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" +ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64"" +ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" +ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64"" +ufs_err_utrl_slot_error(uint32_t slot) "UTRLDBR slot %"PRIu32" is in error" +ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy" +ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported" +ufs_err_invalid_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is invalid" +ufs_err_scsi_cmd_invalid_lun(uint8_t lun) "scsi command has invalid lun: 0x%"PRIx8"" +ufs_err_query_flag_not_readable(uint8_t idn) "query flag idn 0x%"PRIx8" is denied to read" +ufs_err_query_flag_not_writable(uint8_t idn) "query flag idn 0x%"PRIx8" is denied to write" +ufs_err_query_attr_not_readable(uint8_t idn) "query attribute idn 0x%"PRIx8" is denied to read" +ufs_err_query_attr_not_writable(uint8_t idn) "query attribute idn 0x%"PRIx8" is denied to write" +ufs_err_query_invalid_opcode(uint8_t opcode) "query request has invalid opcode. opcode: 0x%"PRIx8"" +ufs_err_query_invalid_idn(uint8_t opcode, uint8_t idn) "query request has invalid idn. opcode: 0x%"PRIx8", idn 0x%"PRIx8"" +ufs_err_query_invalid_index(uint8_t opcode, uint8_t index) "query request has invalid index. opcode: 0x%"PRIx8", index 0x%"PRIx8"" +ufs_err_invalid_trans_code(uint32_t slot, uint8_t trans_code) "request upiu has invalid transaction code. slot: %"PRIu32", trans_code: 0x%"PRIx8"" diff --git a/hw/ufs/trace.h b/hw/ufs/trace.h new file mode 100644 index 0000000000..2dbd6397c3 --- /dev/null +++ b/hw/ufs/trace.h @@ -0,0 +1 @@ +#include "trace/trace-hw_ufs.h" diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c new file mode 100644 index 0000000000..eccdb852a0 --- /dev/null +++ b/hw/ufs/ufs.c @@ -0,0 +1,1362 @@ +/* + * QEMU Universal Flash Storage (UFS) Controller + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/** + * Reference Specs: https://www.jedec.org/, 3.1 + * + * Usage + * ----- + * + * Add options: + * -drive file=,if=none,id= + * -device ufs,serial=,id=, \ + * nutrs=,nutmrs= + * -device ufs-lu,drive=,bus= + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "scsi/constants.h" +#include "trace.h" +#include "ufs.h" + +/* The QEMU-UFS device follows spec version 3.1 */ +#define UFS_SPEC_VER 0x0310 +#define UFS_MAX_NUTRS 32 +#define UFS_MAX_NUTMRS 8 + +static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) +{ + hwaddr hi = addr + size - 1; + + if (hi < addr) { + return MEMTX_DECODE_ERROR; + } + + if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) { + return MEMTX_DECODE_ERROR; + } + + return pci_dma_read(PCI_DEVICE(u), addr, buf, size); +} + +static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, + int size) +{ + hwaddr hi = addr + size - 1; + if (hi < addr) { + return MEMTX_DECODE_ERROR; + } + + if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) { + return MEMTX_DECODE_ERROR; + } + + return pci_dma_write(PCI_DEVICE(u), addr, buf, size); +} + +static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) +{ + hwaddr utrl_base_addr = (((hwaddr)u->reg.utrlbau) << 32) + u->reg.utrlba; + hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc); + + return utrd_addr; +} + +static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ + uint32_t cmd_desc_base_addr_lo = + le32_to_cpu(utrd->command_desc_base_addr_lo); + uint32_t cmd_desc_base_addr_hi = + le32_to_cpu(utrd->command_desc_base_addr_hi); + + return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo; +} + +static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc *utrd) +{ + hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd); + uint32_t rsp_upiu_byte_off = + le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t); + return req_upiu_base_addr + rsp_upiu_byte_off; +} + +static MemTxResult ufs_dma_read_utrd(UfsRequest *req) +{ + UfsHc *u = req->hc; + hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot); + MemTxResult ret; + + ret = ufs_addr_read(u, utrd_addr, &req->utrd, sizeof(req->utrd)); + if (ret) { + trace_ufs_err_dma_read_utrd(req->slot, utrd_addr); + } + return ret; +} + +static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) +{ + UfsHc *u = req->hc; + hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(&req->utrd); + UtpUpiuReq *req_upiu = &req->req_upiu; + uint32_t copy_size; + uint16_t data_segment_length; + MemTxResult ret; + + /* + * To know the size of the req_upiu, we need to read the + * data_segment_length in the header first. + */ + ret = ufs_addr_read(u, req_upiu_base_addr, &req_upiu->header, + sizeof(UtpUpiuHeader)); + if (ret) { + trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); + return ret; + } + data_segment_length = be16_to_cpu(req_upiu->header.data_segment_length); + + copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE + + data_segment_length; + + ret = ufs_addr_read(u, req_upiu_base_addr, &req->req_upiu, copy_size); + if (ret) { + trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr); + } + return ret; +} + +static MemTxResult ufs_dma_read_prdt(UfsRequest *req) +{ + UfsHc *u = req->hc; + uint16_t prdt_len = le16_to_cpu(req->utrd.prd_table_length); + uint16_t prdt_byte_off = + le16_to_cpu(req->utrd.prd_table_offset) * sizeof(uint32_t); + uint32_t prdt_size = prdt_len * sizeof(UfshcdSgEntry); + g_autofree UfshcdSgEntry *prd_entries = NULL; + hwaddr req_upiu_base_addr, prdt_base_addr; + int err; + + assert(!req->sg); + + if (prdt_size == 0) { + return MEMTX_OK; + } + prd_entries = g_new(UfshcdSgEntry, prdt_size); + + req_upiu_base_addr = ufs_get_req_upiu_base_addr(&req->utrd); + prdt_base_addr = req_upiu_base_addr + prdt_byte_off; + + err = ufs_addr_read(u, prdt_base_addr, prd_entries, prdt_size); + if (err) { + trace_ufs_err_dma_read_prdt(req->slot, prdt_base_addr); + return err; + } + + req->sg = g_malloc0(sizeof(QEMUSGList)); + pci_dma_sglist_init(req->sg, PCI_DEVICE(u), prdt_len); + req->data_len = 0; + + for (uint16_t i = 0; i < prdt_len; ++i) { + hwaddr data_dma_addr = le64_to_cpu(prd_entries[i].addr); + uint32_t data_byte_count = le32_to_cpu(prd_entries[i].size) + 1; + qemu_sglist_add(req->sg, data_dma_addr, data_byte_count); + req->data_len += data_byte_count; + } + return MEMTX_OK; +} + +static MemTxResult ufs_dma_read_upiu(UfsRequest *req) +{ + MemTxResult ret; + + ret = ufs_dma_read_utrd(req); + if (ret) { + return ret; + } + + ret = ufs_dma_read_req_upiu(req); + if (ret) { + return ret; + } + + ret = ufs_dma_read_prdt(req); + if (ret) { + return ret; + } + + return 0; +} + +static MemTxResult ufs_dma_write_utrd(UfsRequest *req) +{ + UfsHc *u = req->hc; + hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot); + MemTxResult ret; + + ret = ufs_addr_write(u, utrd_addr, &req->utrd, sizeof(req->utrd)); + if (ret) { + trace_ufs_err_dma_write_utrd(req->slot, utrd_addr); + } + return ret; +} + +static MemTxResult ufs_dma_write_rsp_upiu(UfsRequest *req) +{ + UfsHc *u = req->hc; + hwaddr rsp_upiu_base_addr = ufs_get_rsp_upiu_base_addr(&req->utrd); + uint32_t rsp_upiu_byte_len = + le16_to_cpu(req->utrd.response_upiu_length) * sizeof(uint32_t); + uint16_t data_segment_length = + be16_to_cpu(req->rsp_upiu.header.data_segment_length); + uint32_t copy_size = sizeof(UtpUpiuHeader) + + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE + + data_segment_length; + MemTxResult ret; + + if (copy_size > rsp_upiu_byte_len) { + copy_size = rsp_upiu_byte_len; + } + + ret = ufs_addr_write(u, rsp_upiu_base_addr, &req->rsp_upiu, copy_size); + if (ret) { + trace_ufs_err_dma_write_rsp_upiu(req->slot, rsp_upiu_base_addr); + } + return ret; +} + +static MemTxResult ufs_dma_write_upiu(UfsRequest *req) +{ + MemTxResult ret; + + ret = ufs_dma_write_rsp_upiu(req); + if (ret) { + return ret; + } + + return ufs_dma_write_utrd(req); +} + +static void ufs_irq_check(UfsHc *u) +{ + PCIDevice *pci = PCI_DEVICE(u); + + if ((u->reg.is & UFS_INTR_MASK) & u->reg.ie) { + trace_ufs_irq_raise(); + pci_irq_assert(pci); + } else { + trace_ufs_irq_lower(); + pci_irq_deassert(pci); + } +} + +static void ufs_process_db(UfsHc *u, uint32_t val) +{ + DECLARE_BITMAP(doorbell, UFS_MAX_NUTRS); + uint32_t slot; + uint32_t nutrs = u->params.nutrs; + UfsRequest *req; + + val &= ~u->reg.utrldbr; + if (!val) { + return; + } + + doorbell[0] = val; + slot = find_first_bit(doorbell, nutrs); + + while (slot < nutrs) { + req = &u->req_list[slot]; + if (req->state == UFS_REQUEST_ERROR) { + trace_ufs_err_utrl_slot_error(req->slot); + return; + } + + if (req->state != UFS_REQUEST_IDLE) { + trace_ufs_err_utrl_slot_busy(req->slot); + return; + } + + trace_ufs_process_db(slot); + req->state = UFS_REQUEST_READY; + slot = find_next_bit(doorbell, nutrs, slot + 1); + } + + qemu_bh_schedule(u->doorbell_bh); +} + +static void ufs_process_uiccmd(UfsHc *u, uint32_t val) +{ + trace_ufs_process_uiccmd(val, u->reg.ucmdarg1, u->reg.ucmdarg2, + u->reg.ucmdarg3); + /* + * Only the essential uic commands for running drivers on Linux and Windows + * are implemented. + */ + switch (val) { + case UFS_UIC_CMD_DME_LINK_STARTUP: + u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, DP, 1); + u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UTRLRDY, 1); + u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UTMRLRDY, 1); + u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS; + break; + /* TODO: Revisit it when Power Management is implemented */ + case UFS_UIC_CMD_DME_HIBER_ENTER: + u->reg.is = FIELD_DP32(u->reg.is, IS, UHES, 1); + u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UPMCRS, UFS_PWR_LOCAL); + u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS; + break; + case UFS_UIC_CMD_DME_HIBER_EXIT: + u->reg.is = FIELD_DP32(u->reg.is, IS, UHXS, 1); + u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UPMCRS, UFS_PWR_LOCAL); + u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS; + break; + default: + u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_FAILURE; + } + + u->reg.is = FIELD_DP32(u->reg.is, IS, UCCS, 1); + + ufs_irq_check(u); +} + +static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size) +{ + switch (offset) { + case A_IS: + u->reg.is &= ~data; + ufs_irq_check(u); + break; + case A_IE: + u->reg.ie = data; + ufs_irq_check(u); + break; + case A_HCE: + if (!FIELD_EX32(u->reg.hce, HCE, HCE) && FIELD_EX32(data, HCE, HCE)) { + u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UCRDY, 1); + u->reg.hce = FIELD_DP32(u->reg.hce, HCE, HCE, 1); + } else if (FIELD_EX32(u->reg.hce, HCE, HCE) && + !FIELD_EX32(data, HCE, HCE)) { + u->reg.hcs = 0; + u->reg.hce = FIELD_DP32(u->reg.hce, HCE, HCE, 0); + } + break; + case A_UTRLBA: + u->reg.utrlba = data & R_UTRLBA_UTRLBA_MASK; + break; + case A_UTRLBAU: + u->reg.utrlbau = data; + break; + case A_UTRLDBR: + ufs_process_db(u, data); + u->reg.utrldbr |= data; + break; + case A_UTRLRSR: + u->reg.utrlrsr = data; + break; + case A_UTRLCNR: + u->reg.utrlcnr &= ~data; + break; + case A_UTMRLBA: + u->reg.utmrlba = data & R_UTMRLBA_UTMRLBA_MASK; + break; + case A_UTMRLBAU: + u->reg.utmrlbau = data; + break; + case A_UICCMD: + ufs_process_uiccmd(u, data); + break; + case A_UCMDARG1: + u->reg.ucmdarg1 = data; + break; + case A_UCMDARG2: + u->reg.ucmdarg2 = data; + break; + case A_UCMDARG3: + u->reg.ucmdarg3 = data; + break; + case A_UTRLCLR: + case A_UTMRLDBR: + case A_UTMRLCLR: + case A_UTMRLRSR: + trace_ufs_err_unsupport_register_offset(offset); + break; + default: + trace_ufs_err_invalid_register_offset(offset); + break; + } +} + +static uint64_t ufs_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + UfsHc *u = (UfsHc *)opaque; + uint8_t *ptr = (uint8_t *)&u->reg; + uint64_t value; + + if (addr > sizeof(u->reg) - size) { + trace_ufs_err_invalid_register_offset(addr); + return 0; + } + + value = *(uint32_t *)(ptr + addr); + trace_ufs_mmio_read(addr, value, size); + return value; +} + +static void ufs_mmio_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + UfsHc *u = (UfsHc *)opaque; + + if (addr > sizeof(u->reg) - size) { + trace_ufs_err_invalid_register_offset(addr); + return; + } + + trace_ufs_mmio_write(addr, data, size); + ufs_write_reg(u, addr, data, size); +} + +static const MemoryRegionOps ufs_mmio_ops = { + .read = ufs_mmio_read, + .write = ufs_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + + +void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags, + uint8_t response, uint8_t scsi_status, + uint16_t data_segment_length) +{ + memcpy(&req->rsp_upiu.header, &req->req_upiu.header, sizeof(UtpUpiuHeader)); + req->rsp_upiu.header.trans_type = trans_type; + req->rsp_upiu.header.flags = flags; + req->rsp_upiu.header.response = response; + req->rsp_upiu.header.scsi_status = scsi_status; + req->rsp_upiu.header.data_segment_length = cpu_to_be16(data_segment_length); +} + +static UfsReqResult ufs_exec_scsi_cmd(UfsRequest *req) +{ + UfsHc *u = req->hc; + uint8_t lun = req->req_upiu.header.lun; + + UfsLu *lu = NULL; + + trace_ufs_exec_scsi_cmd(req->slot, lun, req->req_upiu.sc.cdb[0]); + + if (!is_wlun(lun) && (lun >= UFS_MAX_LUS || u->lus[lun] == NULL)) { + trace_ufs_err_scsi_cmd_invalid_lun(lun); + return UFS_REQUEST_FAIL; + } + + switch (lun) { + case UFS_UPIU_REPORT_LUNS_WLUN: + lu = &u->report_wlu; + break; + case UFS_UPIU_UFS_DEVICE_WLUN: + lu = &u->dev_wlu; + break; + case UFS_UPIU_BOOT_WLUN: + lu = &u->boot_wlu; + break; + case UFS_UPIU_RPMB_WLUN: + lu = &u->rpmb_wlu; + break; + default: + lu = u->lus[lun]; + } + + return lu->scsi_op(lu, req); +} + +static UfsReqResult ufs_exec_nop_cmd(UfsRequest *req) +{ + trace_ufs_exec_nop_cmd(req->slot); + ufs_build_upiu_header(req, UFS_UPIU_TRANSACTION_NOP_IN, 0, 0, 0, 0); + return UFS_REQUEST_SUCCESS; +} + +/* + * This defines the permission of flags based on their IDN. There are some + * things that are declared read-only, which is inconsistent with the ufs spec, + * because we want to return an error for features that are not yet supported. + */ +static const int flag_permission[UFS_QUERY_FLAG_IDN_COUNT] = { + [UFS_QUERY_FLAG_IDN_FDEVICEINIT] = UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET, + /* Write protection is not supported */ + [UFS_QUERY_FLAG_IDN_PERMANENT_WPE] = UFS_QUERY_FLAG_READ, + [UFS_QUERY_FLAG_IDN_PWR_ON_WPE] = UFS_QUERY_FLAG_READ, + [UFS_QUERY_FLAG_IDN_BKOPS_EN] = UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET | + UFS_QUERY_FLAG_CLEAR | + UFS_QUERY_FLAG_TOGGLE, + [UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE] = + UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET | UFS_QUERY_FLAG_CLEAR | + UFS_QUERY_FLAG_TOGGLE, + /* Purge Operation is not supported */ + [UFS_QUERY_FLAG_IDN_PURGE_ENABLE] = UFS_QUERY_FLAG_NONE, + /* Refresh Operation is not supported */ + [UFS_QUERY_FLAG_IDN_REFRESH_ENABLE] = UFS_QUERY_FLAG_NONE, + /* Physical Resource Removal is not supported */ + [UFS_QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL] = UFS_QUERY_FLAG_READ, + [UFS_QUERY_FLAG_IDN_BUSY_RTC] = UFS_QUERY_FLAG_READ, + [UFS_QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE] = UFS_QUERY_FLAG_READ, + /* Write Booster is not supported */ + [UFS_QUERY_FLAG_IDN_WB_EN] = UFS_QUERY_FLAG_READ, + [UFS_QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN] = UFS_QUERY_FLAG_READ, + [UFS_QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8] = UFS_QUERY_FLAG_READ, +}; + +static inline QueryRespCode ufs_flag_check_idn_valid(uint8_t idn, int op) +{ + if (idn >= UFS_QUERY_FLAG_IDN_COUNT) { + return UFS_QUERY_RESULT_INVALID_IDN; + } + + if (!(flag_permission[idn] & op)) { + if (op == UFS_QUERY_FLAG_READ) { + trace_ufs_err_query_flag_not_readable(idn); + return UFS_QUERY_RESULT_NOT_READABLE; + } + trace_ufs_err_query_flag_not_writable(idn); + return UFS_QUERY_RESULT_NOT_WRITEABLE; + } + + return UFS_QUERY_RESULT_SUCCESS; +} + +static const int attr_permission[UFS_QUERY_ATTR_IDN_COUNT] = { + /* booting is not supported */ + [UFS_QUERY_ATTR_IDN_BOOT_LU_EN] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_POWER_MODE] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL] = + UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE, + [UFS_QUERY_ATTR_IDN_OOO_DATA_EN] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_BKOPS_STATUS] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_PURGE_STATUS] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_MAX_DATA_IN] = + UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE, + [UFS_QUERY_ATTR_IDN_MAX_DATA_OUT] = + UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE, + [UFS_QUERY_ATTR_IDN_DYN_CAP_NEEDED] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_REF_CLK_FREQ] = + UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE, + [UFS_QUERY_ATTR_IDN_CONF_DESC_LOCK] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_MAX_NUM_OF_RTT] = + UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE, + [UFS_QUERY_ATTR_IDN_EE_CONTROL] = + UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE, + [UFS_QUERY_ATTR_IDN_EE_STATUS] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_SECONDS_PASSED] = UFS_QUERY_ATTR_WRITE, + [UFS_QUERY_ATTR_IDN_CNTX_CONF] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_FFU_STATUS] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_PSA_STATE] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE, + [UFS_QUERY_ATTR_IDN_PSA_DATA_SIZE] = + UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE, + [UFS_QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_THROTTLING_STATUS] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_WB_FLUSH_STATUS] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE] = UFS_QUERY_ATTR_READ, + /* refresh operation is not supported */ + [UFS_QUERY_ATTR_IDN_REFRESH_STATUS] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_REFRESH_FREQ] = UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_REFRESH_UNIT] = UFS_QUERY_ATTR_READ, +}; + +static inline QueryRespCode ufs_attr_check_idn_valid(uint8_t idn, int op) +{ + if (idn >= UFS_QUERY_ATTR_IDN_COUNT) { + return UFS_QUERY_RESULT_INVALID_IDN; + } + + if (!(attr_permission[idn] & op)) { + if (op == UFS_QUERY_ATTR_READ) { + trace_ufs_err_query_attr_not_readable(idn); + return UFS_QUERY_RESULT_NOT_READABLE; + } + trace_ufs_err_query_attr_not_writable(idn); + return UFS_QUERY_RESULT_NOT_WRITEABLE; + } + + return UFS_QUERY_RESULT_SUCCESS; +} + +static QueryRespCode ufs_exec_query_flag(UfsRequest *req, int op) +{ + UfsHc *u = req->hc; + uint8_t idn = req->req_upiu.qr.idn; + uint32_t value; + QueryRespCode ret; + + ret = ufs_flag_check_idn_valid(idn, op); + if (ret) { + return ret; + } + + if (idn == UFS_QUERY_FLAG_IDN_FDEVICEINIT) { + value = 0; + } else if (op == UFS_QUERY_FLAG_READ) { + value = *(((uint8_t *)&u->flags) + idn); + } else if (op == UFS_QUERY_FLAG_SET) { + value = 1; + } else if (op == UFS_QUERY_FLAG_CLEAR) { + value = 0; + } else if (op == UFS_QUERY_FLAG_TOGGLE) { + value = *(((uint8_t *)&u->flags) + idn); + value = !value; + } else { + trace_ufs_err_query_invalid_opcode(op); + return UFS_QUERY_RESULT_INVALID_OPCODE; + } + + *(((uint8_t *)&u->flags) + idn) = value; + req->rsp_upiu.qr.value = cpu_to_be32(value); + return UFS_QUERY_RESULT_SUCCESS; +} + +static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn) +{ + switch (idn) { + case UFS_QUERY_ATTR_IDN_BOOT_LU_EN: + return u->attributes.boot_lun_en; + case UFS_QUERY_ATTR_IDN_POWER_MODE: + return u->attributes.current_power_mode; + case UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL: + return u->attributes.active_icc_level; + case UFS_QUERY_ATTR_IDN_OOO_DATA_EN: + return u->attributes.out_of_order_data_en; + case UFS_QUERY_ATTR_IDN_BKOPS_STATUS: + return u->attributes.background_op_status; + case UFS_QUERY_ATTR_IDN_PURGE_STATUS: + return u->attributes.purge_status; + case UFS_QUERY_ATTR_IDN_MAX_DATA_IN: + return u->attributes.max_data_in_size; + case UFS_QUERY_ATTR_IDN_MAX_DATA_OUT: + return u->attributes.max_data_out_size; + case UFS_QUERY_ATTR_IDN_DYN_CAP_NEEDED: + return be32_to_cpu(u->attributes.dyn_cap_needed); + case UFS_QUERY_ATTR_IDN_REF_CLK_FREQ: + return u->attributes.ref_clk_freq; + case UFS_QUERY_ATTR_IDN_CONF_DESC_LOCK: + return u->attributes.config_descr_lock; + case UFS_QUERY_ATTR_IDN_MAX_NUM_OF_RTT: + return u->attributes.max_num_of_rtt; + case UFS_QUERY_ATTR_IDN_EE_CONTROL: + return be16_to_cpu(u->attributes.exception_event_control); + case UFS_QUERY_ATTR_IDN_EE_STATUS: + return be16_to_cpu(u->attributes.exception_event_status); + case UFS_QUERY_ATTR_IDN_SECONDS_PASSED: + return be32_to_cpu(u->attributes.seconds_passed); + case UFS_QUERY_ATTR_IDN_CNTX_CONF: + return be16_to_cpu(u->attributes.context_conf); + case UFS_QUERY_ATTR_IDN_FFU_STATUS: + return u->attributes.device_ffu_status; + case UFS_QUERY_ATTR_IDN_PSA_STATE: + return be32_to_cpu(u->attributes.psa_state); + case UFS_QUERY_ATTR_IDN_PSA_DATA_SIZE: + return be32_to_cpu(u->attributes.psa_data_size); + case UFS_QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME: + return u->attributes.ref_clk_gating_wait_time; + case UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP: + return u->attributes.device_case_rough_temperaure; + case UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND: + return u->attributes.device_too_high_temp_boundary; + case UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND: + return u->attributes.device_too_low_temp_boundary; + case UFS_QUERY_ATTR_IDN_THROTTLING_STATUS: + return u->attributes.throttling_status; + case UFS_QUERY_ATTR_IDN_WB_FLUSH_STATUS: + return u->attributes.wb_buffer_flush_status; + case UFS_QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE: + return u->attributes.available_wb_buffer_size; + case UFS_QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST: + return u->attributes.wb_buffer_life_time_est; + case UFS_QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE: + return be32_to_cpu(u->attributes.current_wb_buffer_size); + case UFS_QUERY_ATTR_IDN_REFRESH_STATUS: + return u->attributes.refresh_status; + case UFS_QUERY_ATTR_IDN_REFRESH_FREQ: + return u->attributes.refresh_freq; + case UFS_QUERY_ATTR_IDN_REFRESH_UNIT: + return u->attributes.refresh_unit; + } + return 0; +} + +static void ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value) +{ + switch (idn) { + case UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL: + u->attributes.active_icc_level = value; + break; + case UFS_QUERY_ATTR_IDN_MAX_DATA_IN: + u->attributes.max_data_in_size = value; + break; + case UFS_QUERY_ATTR_IDN_MAX_DATA_OUT: + u->attributes.max_data_out_size = value; + break; + case UFS_QUERY_ATTR_IDN_REF_CLK_FREQ: + u->attributes.ref_clk_freq = value; + break; + case UFS_QUERY_ATTR_IDN_MAX_NUM_OF_RTT: + u->attributes.max_num_of_rtt = value; + break; + case UFS_QUERY_ATTR_IDN_EE_CONTROL: + u->attributes.exception_event_control = cpu_to_be16(value); + break; + case UFS_QUERY_ATTR_IDN_SECONDS_PASSED: + u->attributes.seconds_passed = cpu_to_be32(value); + break; + case UFS_QUERY_ATTR_IDN_PSA_STATE: + u->attributes.psa_state = value; + break; + case UFS_QUERY_ATTR_IDN_PSA_DATA_SIZE: + u->attributes.psa_data_size = cpu_to_be32(value); + break; + } +} + +static QueryRespCode ufs_exec_query_attr(UfsRequest *req, int op) +{ + UfsHc *u = req->hc; + uint8_t idn = req->req_upiu.qr.idn; + uint32_t value; + QueryRespCode ret; + + ret = ufs_attr_check_idn_valid(idn, op); + if (ret) { + return ret; + } + + if (op == UFS_QUERY_ATTR_READ) { + value = ufs_read_attr_value(u, idn); + } else { + value = be32_to_cpu(req->req_upiu.qr.value); + ufs_write_attr_value(u, idn, value); + } + + req->rsp_upiu.qr.value = cpu_to_be32(value); + return UFS_QUERY_RESULT_SUCCESS; +} + +static const RpmbUnitDescriptor rpmb_unit_desc = { + .length = sizeof(RpmbUnitDescriptor), + .descriptor_idn = 2, + .unit_index = UFS_UPIU_RPMB_WLUN, + .lu_enable = 0, +}; + +static QueryRespCode ufs_read_unit_desc(UfsRequest *req) +{ + UfsHc *u = req->hc; + uint8_t lun = req->req_upiu.qr.index; + + if (lun != UFS_UPIU_RPMB_WLUN && + (lun >= UFS_MAX_LUS || u->lus[lun] == NULL)) { + trace_ufs_err_query_invalid_index(req->req_upiu.qr.opcode, lun); + return UFS_QUERY_RESULT_INVALID_INDEX; + } + + if (lun == UFS_UPIU_RPMB_WLUN) { + memcpy(&req->rsp_upiu.qr.data, &rpmb_unit_desc, rpmb_unit_desc.length); + } else { + memcpy(&req->rsp_upiu.qr.data, &u->lus[lun]->unit_desc, + sizeof(u->lus[lun]->unit_desc)); + } + + return UFS_QUERY_RESULT_SUCCESS; +} + +static inline StringDescriptor manufacturer_str_desc(void) +{ + StringDescriptor desc = { + .length = 0x12, + .descriptor_idn = UFS_QUERY_DESC_IDN_STRING, + }; + desc.UC[0] = cpu_to_be16('R'); + desc.UC[1] = cpu_to_be16('E'); + desc.UC[2] = cpu_to_be16('D'); + desc.UC[3] = cpu_to_be16('H'); + desc.UC[4] = cpu_to_be16('A'); + desc.UC[5] = cpu_to_be16('T'); + return desc; +} + +static inline StringDescriptor product_name_str_desc(void) +{ + StringDescriptor desc = { + .length = 0x22, + .descriptor_idn = UFS_QUERY_DESC_IDN_STRING, + }; + desc.UC[0] = cpu_to_be16('Q'); + desc.UC[1] = cpu_to_be16('E'); + desc.UC[2] = cpu_to_be16('M'); + desc.UC[3] = cpu_to_be16('U'); + desc.UC[4] = cpu_to_be16(' '); + desc.UC[5] = cpu_to_be16('U'); + desc.UC[6] = cpu_to_be16('F'); + desc.UC[7] = cpu_to_be16('S'); + return desc; +} + +static inline StringDescriptor product_rev_level_str_desc(void) +{ + StringDescriptor desc = { + .length = 0x0a, + .descriptor_idn = UFS_QUERY_DESC_IDN_STRING, + }; + desc.UC[0] = cpu_to_be16('0'); + desc.UC[1] = cpu_to_be16('0'); + desc.UC[2] = cpu_to_be16('0'); + desc.UC[3] = cpu_to_be16('1'); + return desc; +} + +static const StringDescriptor null_str_desc = { + .length = 0x02, + .descriptor_idn = UFS_QUERY_DESC_IDN_STRING, +}; + +static QueryRespCode ufs_read_string_desc(UfsRequest *req) +{ + UfsHc *u = req->hc; + uint8_t index = req->req_upiu.qr.index; + StringDescriptor desc; + + if (index == u->device_desc.manufacturer_name) { + desc = manufacturer_str_desc(); + memcpy(&req->rsp_upiu.qr.data, &desc, desc.length); + } else if (index == u->device_desc.product_name) { + desc = product_name_str_desc(); + memcpy(&req->rsp_upiu.qr.data, &desc, desc.length); + } else if (index == u->device_desc.serial_number) { + memcpy(&req->rsp_upiu.qr.data, &null_str_desc, null_str_desc.length); + } else if (index == u->device_desc.oem_id) { + memcpy(&req->rsp_upiu.qr.data, &null_str_desc, null_str_desc.length); + } else if (index == u->device_desc.product_revision_level) { + desc = product_rev_level_str_desc(); + memcpy(&req->rsp_upiu.qr.data, &desc, desc.length); + } else { + trace_ufs_err_query_invalid_index(req->req_upiu.qr.opcode, index); + return UFS_QUERY_RESULT_INVALID_INDEX; + } + return UFS_QUERY_RESULT_SUCCESS; +} + +static inline InterconnectDescriptor interconnect_desc(void) +{ + InterconnectDescriptor desc = { + .length = sizeof(InterconnectDescriptor), + .descriptor_idn = UFS_QUERY_DESC_IDN_INTERCONNECT, + }; + desc.bcd_unipro_version = cpu_to_be16(0x180); + desc.bcd_mphy_version = cpu_to_be16(0x410); + return desc; +} + +static QueryRespCode ufs_read_desc(UfsRequest *req) +{ + UfsHc *u = req->hc; + QueryRespCode status; + uint8_t idn = req->req_upiu.qr.idn; + uint16_t length = be16_to_cpu(req->req_upiu.qr.length); + InterconnectDescriptor desc; + + switch (idn) { + case UFS_QUERY_DESC_IDN_DEVICE: + memcpy(&req->rsp_upiu.qr.data, &u->device_desc, sizeof(u->device_desc)); + status = UFS_QUERY_RESULT_SUCCESS; + break; + case UFS_QUERY_DESC_IDN_UNIT: + status = ufs_read_unit_desc(req); + break; + case UFS_QUERY_DESC_IDN_GEOMETRY: + memcpy(&req->rsp_upiu.qr.data, &u->geometry_desc, + sizeof(u->geometry_desc)); + status = UFS_QUERY_RESULT_SUCCESS; + break; + case UFS_QUERY_DESC_IDN_INTERCONNECT: { + desc = interconnect_desc(); + memcpy(&req->rsp_upiu.qr.data, &desc, sizeof(InterconnectDescriptor)); + status = UFS_QUERY_RESULT_SUCCESS; + break; + } + case UFS_QUERY_DESC_IDN_STRING: + status = ufs_read_string_desc(req); + break; + case UFS_QUERY_DESC_IDN_POWER: + /* mocking of power descriptor is not supported */ + memset(&req->rsp_upiu.qr.data, 0, sizeof(PowerParametersDescriptor)); + req->rsp_upiu.qr.data[0] = sizeof(PowerParametersDescriptor); + req->rsp_upiu.qr.data[1] = UFS_QUERY_DESC_IDN_POWER; + status = UFS_QUERY_RESULT_SUCCESS; + break; + case UFS_QUERY_DESC_IDN_HEALTH: + /* mocking of health descriptor is not supported */ + memset(&req->rsp_upiu.qr.data, 0, sizeof(DeviceHealthDescriptor)); + req->rsp_upiu.qr.data[0] = sizeof(DeviceHealthDescriptor); + req->rsp_upiu.qr.data[1] = UFS_QUERY_DESC_IDN_HEALTH; + status = UFS_QUERY_RESULT_SUCCESS; + break; + default: + length = 0; + trace_ufs_err_query_invalid_idn(req->req_upiu.qr.opcode, idn); + status = UFS_QUERY_RESULT_INVALID_IDN; + } + + if (length > req->rsp_upiu.qr.data[0]) { + length = req->rsp_upiu.qr.data[0]; + } + req->rsp_upiu.qr.opcode = req->req_upiu.qr.opcode; + req->rsp_upiu.qr.idn = req->req_upiu.qr.idn; + req->rsp_upiu.qr.index = req->req_upiu.qr.index; + req->rsp_upiu.qr.selector = req->req_upiu.qr.selector; + req->rsp_upiu.qr.length = cpu_to_be16(length); + + return status; +} + +static QueryRespCode ufs_exec_query_read(UfsRequest *req) +{ + QueryRespCode status; + switch (req->req_upiu.qr.opcode) { + case UFS_UPIU_QUERY_OPCODE_NOP: + status = UFS_QUERY_RESULT_SUCCESS; + break; + case UFS_UPIU_QUERY_OPCODE_READ_DESC: + status = ufs_read_desc(req); + break; + case UFS_UPIU_QUERY_OPCODE_READ_ATTR: + status = ufs_exec_query_attr(req, UFS_QUERY_ATTR_READ); + break; + case UFS_UPIU_QUERY_OPCODE_READ_FLAG: + status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_READ); + break; + default: + trace_ufs_err_query_invalid_opcode(req->req_upiu.qr.opcode); + status = UFS_QUERY_RESULT_INVALID_OPCODE; + break; + } + + return status; +} + +static QueryRespCode ufs_exec_query_write(UfsRequest *req) +{ + QueryRespCode status; + switch (req->req_upiu.qr.opcode) { + case UFS_UPIU_QUERY_OPCODE_NOP: + status = UFS_QUERY_RESULT_SUCCESS; + break; + case UFS_UPIU_QUERY_OPCODE_WRITE_DESC: + /* write descriptor is not supported */ + status = UFS_QUERY_RESULT_NOT_WRITEABLE; + break; + case UFS_UPIU_QUERY_OPCODE_WRITE_ATTR: + status = ufs_exec_query_attr(req, UFS_QUERY_ATTR_WRITE); + break; + case UFS_UPIU_QUERY_OPCODE_SET_FLAG: + status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_SET); + break; + case UFS_UPIU_QUERY_OPCODE_CLEAR_FLAG: + status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_CLEAR); + break; + case UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG: + status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_TOGGLE); + break; + default: + trace_ufs_err_query_invalid_opcode(req->req_upiu.qr.opcode); + status = UFS_QUERY_RESULT_INVALID_OPCODE; + break; + } + + return status; +} + +static UfsReqResult ufs_exec_query_cmd(UfsRequest *req) +{ + uint8_t query_func = req->req_upiu.header.query_func; + uint16_t data_segment_length; + QueryRespCode status; + + trace_ufs_exec_query_cmd(req->slot, req->req_upiu.qr.opcode); + if (query_func == UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST) { + status = ufs_exec_query_read(req); + } else if (query_func == UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST) { + status = ufs_exec_query_write(req); + } else { + status = UFS_QUERY_RESULT_GENERAL_FAILURE; + } + + data_segment_length = be16_to_cpu(req->rsp_upiu.qr.length); + ufs_build_upiu_header(req, UFS_UPIU_TRANSACTION_QUERY_RSP, 0, status, 0, + data_segment_length); + + if (status != UFS_QUERY_RESULT_SUCCESS) { + return UFS_REQUEST_FAIL; + } + return UFS_REQUEST_SUCCESS; +} + +static void ufs_exec_req(UfsRequest *req) +{ + UfsReqResult req_result; + + if (ufs_dma_read_upiu(req)) { + return; + } + + switch (req->req_upiu.header.trans_type) { + case UFS_UPIU_TRANSACTION_NOP_OUT: + req_result = ufs_exec_nop_cmd(req); + break; + case UFS_UPIU_TRANSACTION_COMMAND: + req_result = ufs_exec_scsi_cmd(req); + break; + case UFS_UPIU_TRANSACTION_QUERY_REQ: + req_result = ufs_exec_query_cmd(req); + break; + default: + trace_ufs_err_invalid_trans_code(req->slot, + req->req_upiu.header.trans_type); + req_result = UFS_REQUEST_FAIL; + } + + /* + * The ufs_complete_req for scsi commands is handled by the + * ufs_scsi_command_complete() callback function. Therefore, to avoid + * duplicate processing, ufs_complete_req() is not called for scsi commands. + */ + if (req_result != UFS_REQUEST_NO_COMPLETE) { + ufs_complete_req(req, req_result); + } +} + +static void ufs_process_req(void *opaque) +{ + UfsHc *u = opaque; + UfsRequest *req; + int slot; + + for (slot = 0; slot < u->params.nutrs; slot++) { + req = &u->req_list[slot]; + + if (req->state != UFS_REQUEST_READY) { + continue; + } + trace_ufs_process_req(slot); + req->state = UFS_REQUEST_RUNNING; + + ufs_exec_req(req); + } +} + +void ufs_complete_req(UfsRequest *req, UfsReqResult req_result) +{ + UfsHc *u = req->hc; + assert(req->state == UFS_REQUEST_RUNNING); + + if (req_result == UFS_REQUEST_SUCCESS) { + req->utrd.header.dword_2 = cpu_to_le32(UFS_OCS_SUCCESS); + } else { + req->utrd.header.dword_2 = cpu_to_le32(UFS_OCS_INVALID_CMD_TABLE_ATTR); + } + + trace_ufs_complete_req(req->slot); + req->state = UFS_REQUEST_COMPLETE; + qemu_bh_schedule(u->complete_bh); +} + +static void ufs_clear_req(UfsRequest *req) +{ + if (req->sg != NULL) { + qemu_sglist_destroy(req->sg); + g_free(req->sg); + req->sg = NULL; + req->data_len = 0; + } + + memset(&req->utrd, 0, sizeof(req->utrd)); + memset(&req->req_upiu, 0, sizeof(req->req_upiu)); + memset(&req->rsp_upiu, 0, sizeof(req->rsp_upiu)); +} + +static void ufs_sendback_req(void *opaque) +{ + UfsHc *u = opaque; + UfsRequest *req; + int slot; + + for (slot = 0; slot < u->params.nutrs; slot++) { + req = &u->req_list[slot]; + + if (req->state != UFS_REQUEST_COMPLETE) { + continue; + } + + if (ufs_dma_write_upiu(req)) { + req->state = UFS_REQUEST_ERROR; + continue; + } + + /* + * TODO: UTP Transfer Request Interrupt Aggregation Control is not yet + * supported + */ + if (le32_to_cpu(req->utrd.header.dword_2) != UFS_OCS_SUCCESS || + le32_to_cpu(req->utrd.header.dword_0) & UFS_UTP_REQ_DESC_INT_CMD) { + u->reg.is = FIELD_DP32(u->reg.is, IS, UTRCS, 1); + } + + u->reg.utrldbr &= ~(1 << slot); + u->reg.utrlcnr |= (1 << slot); + + trace_ufs_sendback_req(req->slot); + + ufs_clear_req(req); + req->state = UFS_REQUEST_IDLE; + } + + ufs_irq_check(u); +} + +static bool ufs_check_constraints(UfsHc *u, Error **errp) +{ + if (u->params.nutrs > UFS_MAX_NUTRS) { + error_setg(errp, "nutrs must be less than or equal to %d", + UFS_MAX_NUTRS); + return false; + } + + if (u->params.nutmrs > UFS_MAX_NUTMRS) { + error_setg(errp, "nutmrs must be less than or equal to %d", + UFS_MAX_NUTMRS); + return false; + } + + return true; +} + +static void ufs_init_pci(UfsHc *u, PCIDevice *pci_dev) +{ + uint8_t *pci_conf = pci_dev->config; + + pci_conf[PCI_INTERRUPT_PIN] = 1; + pci_config_set_prog_interface(pci_conf, 0x1); + + memory_region_init_io(&u->iomem, OBJECT(u), &ufs_mmio_ops, u, "ufs", + u->reg_size); + pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &u->iomem); + u->irq = pci_allocate_irq(pci_dev); +} + +static void ufs_init_state(UfsHc *u) +{ + u->req_list = g_new0(UfsRequest, u->params.nutrs); + + for (int i = 0; i < u->params.nutrs; i++) { + u->req_list[i].hc = u; + u->req_list[i].slot = i; + u->req_list[i].sg = NULL; + u->req_list[i].state = UFS_REQUEST_IDLE; + } + + u->doorbell_bh = qemu_bh_new_guarded(ufs_process_req, u, + &DEVICE(u)->mem_reentrancy_guard); + u->complete_bh = qemu_bh_new_guarded(ufs_sendback_req, u, + &DEVICE(u)->mem_reentrancy_guard); +} + +static void ufs_init_hc(UfsHc *u) +{ + uint32_t cap = 0; + + u->reg_size = pow2ceil(sizeof(UfsReg)); + + memset(&u->reg, 0, sizeof(u->reg)); + cap = FIELD_DP32(cap, CAP, NUTRS, (u->params.nutrs - 1)); + cap = FIELD_DP32(cap, CAP, RTT, 2); + cap = FIELD_DP32(cap, CAP, NUTMRS, (u->params.nutmrs - 1)); + cap = FIELD_DP32(cap, CAP, AUTOH8, 0); + cap = FIELD_DP32(cap, CAP, 64AS, 1); + cap = FIELD_DP32(cap, CAP, OODDS, 0); + cap = FIELD_DP32(cap, CAP, UICDMETMS, 0); + cap = FIELD_DP32(cap, CAP, CS, 0); + u->reg.cap = cap; + u->reg.ver = UFS_SPEC_VER; + + memset(&u->device_desc, 0, sizeof(DeviceDescriptor)); + u->device_desc.length = sizeof(DeviceDescriptor); + u->device_desc.descriptor_idn = UFS_QUERY_DESC_IDN_DEVICE; + u->device_desc.device_sub_class = 0x01; + u->device_desc.number_lu = 0x00; + u->device_desc.number_wlu = 0x04; + /* TODO: Revisit it when Power Management is implemented */ + u->device_desc.init_power_mode = 0x01; /* Active Mode */ + u->device_desc.high_priority_lun = 0x7F; /* Same Priority */ + u->device_desc.spec_version = cpu_to_be16(UFS_SPEC_VER); + u->device_desc.manufacturer_name = 0x00; + u->device_desc.product_name = 0x01; + u->device_desc.serial_number = 0x02; + u->device_desc.oem_id = 0x03; + u->device_desc.ud_0_base_offset = 0x16; + u->device_desc.ud_config_p_length = 0x1A; + u->device_desc.device_rtt_cap = 0x02; + u->device_desc.queue_depth = u->params.nutrs; + u->device_desc.product_revision_level = 0x04; + + memset(&u->geometry_desc, 0, sizeof(GeometryDescriptor)); + u->geometry_desc.length = sizeof(GeometryDescriptor); + u->geometry_desc.descriptor_idn = UFS_QUERY_DESC_IDN_GEOMETRY; + u->geometry_desc.max_number_lu = (UFS_MAX_LUS == 32) ? 0x1 : 0x0; + u->geometry_desc.segment_size = cpu_to_be32(0x2000); /* 4KB */ + u->geometry_desc.allocation_unit_size = 0x1; /* 4KB */ + u->geometry_desc.min_addr_block_size = 0x8; /* 4KB */ + u->geometry_desc.max_in_buffer_size = 0x8; + u->geometry_desc.max_out_buffer_size = 0x8; + u->geometry_desc.rpmb_read_write_size = 0x40; + u->geometry_desc.data_ordering = + 0x0; /* out-of-order data transfer is not supported */ + u->geometry_desc.max_context_id_number = 0x5; + u->geometry_desc.supported_memory_types = cpu_to_be16(0x8001); + + memset(&u->attributes, 0, sizeof(u->attributes)); + u->attributes.max_data_in_size = 0x08; + u->attributes.max_data_out_size = 0x08; + u->attributes.ref_clk_freq = 0x01; /* 26 MHz */ + /* configure descriptor is not supported */ + u->attributes.config_descr_lock = 0x01; + u->attributes.max_num_of_rtt = 0x02; + + memset(&u->flags, 0, sizeof(u->flags)); + u->flags.permanently_disable_fw_update = 1; +} + +static void ufs_realize(PCIDevice *pci_dev, Error **errp) +{ + UfsHc *u = UFS(pci_dev); + + if (!ufs_check_constraints(u, errp)) { + return; + } + + qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, &pci_dev->qdev, + u->parent_obj.qdev.id); + + ufs_init_state(u); + ufs_init_hc(u); + ufs_init_pci(u, pci_dev); + + ufs_init_wlu(&u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN); + ufs_init_wlu(&u->dev_wlu, UFS_UPIU_UFS_DEVICE_WLUN); + ufs_init_wlu(&u->boot_wlu, UFS_UPIU_BOOT_WLUN); + ufs_init_wlu(&u->rpmb_wlu, UFS_UPIU_RPMB_WLUN); +} + +static void ufs_exit(PCIDevice *pci_dev) +{ + UfsHc *u = UFS(pci_dev); + + qemu_bh_delete(u->doorbell_bh); + qemu_bh_delete(u->complete_bh); + + for (int i = 0; i < u->params.nutrs; i++) { + ufs_clear_req(&u->req_list[i]); + } + g_free(u->req_list); +} + +static Property ufs_props[] = { + DEFINE_PROP_STRING("serial", UfsHc, params.serial), + DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32), + DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription ufs_vmstate = { + .name = "ufs", + .unmigratable = 1, +}; + +static void ufs_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); + + pc->realize = ufs_realize; + pc->exit = ufs_exit; + pc->vendor_id = PCI_VENDOR_ID_REDHAT; + pc->device_id = PCI_DEVICE_ID_REDHAT_UFS; + pc->class_id = PCI_CLASS_STORAGE_UFS; + + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->desc = "Universal Flash Storage"; + device_class_set_props(dc, ufs_props); + dc->vmsd = &ufs_vmstate; +} + +static bool ufs_bus_check_address(BusState *qbus, DeviceState *qdev, + Error **errp) +{ + if (strcmp(object_get_typename(OBJECT(qdev)), TYPE_UFS_LU) != 0) { + error_setg(errp, "%s cannot be connected to ufs-bus", + object_get_typename(OBJECT(qdev))); + return false; + } + + return true; +} + +static char *ufs_bus_get_dev_path(DeviceState *dev) +{ + BusState *bus = qdev_get_parent_bus(dev); + + return qdev_get_dev_path(bus->parent); +} + +static void ufs_bus_class_init(ObjectClass *class, void *data) +{ + BusClass *bc = BUS_CLASS(class); + bc->get_dev_path = ufs_bus_get_dev_path; + bc->check_address = ufs_bus_check_address; +} + +static const TypeInfo ufs_info = { + .name = TYPE_UFS, + .parent = TYPE_PCI_DEVICE, + .class_init = ufs_class_init, + .instance_size = sizeof(UfsHc), + .interfaces = (InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} }, +}; + +static const TypeInfo ufs_bus_info = { + .name = TYPE_UFS_BUS, + .parent = TYPE_BUS, + .class_init = ufs_bus_class_init, + .class_size = sizeof(UfsBusClass), + .instance_size = sizeof(UfsBus), +}; + +static void ufs_register_types(void) +{ + type_register_static(&ufs_info); + type_register_static(&ufs_bus_info); +} + +type_init(ufs_register_types) diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h new file mode 100644 index 0000000000..8fda94f4ef --- /dev/null +++ b/hw/ufs/ufs.h @@ -0,0 +1,137 @@ +/* + * QEMU UFS + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_UFS_UFS_H +#define HW_UFS_UFS_H + +#include "hw/pci/pci_device.h" +#include "hw/scsi/scsi.h" +#include "block/ufs.h" + +#define UFS_MAX_LUS 32 +#define UFS_BLOCK_SIZE_SHIFT 12 +#define UFS_BLOCK_SIZE (1 << UFS_BLOCK_SIZE_SHIFT) + +typedef struct UfsBusClass { + BusClass parent_class; + bool (*parent_check_address)(BusState *bus, DeviceState *dev, Error **errp); +} UfsBusClass; + +typedef struct UfsBus { + BusState parent_bus; +} UfsBus; + +#define TYPE_UFS_BUS "ufs-bus" +DECLARE_OBJ_CHECKERS(UfsBus, UfsBusClass, UFS_BUS, TYPE_UFS_BUS) + +typedef enum UfsRequestState { + UFS_REQUEST_IDLE = 0, + UFS_REQUEST_READY = 1, + UFS_REQUEST_RUNNING = 2, + UFS_REQUEST_COMPLETE = 3, + UFS_REQUEST_ERROR = 4, +} UfsRequestState; + +typedef enum UfsReqResult { + UFS_REQUEST_SUCCESS = 0, + UFS_REQUEST_FAIL = 1, + UFS_REQUEST_NO_COMPLETE = 2, +} UfsReqResult; + +typedef struct UfsRequest { + struct UfsHc *hc; + UfsRequestState state; + int slot; + + UtpTransferReqDesc utrd; + UtpUpiuReq req_upiu; + UtpUpiuRsp rsp_upiu; + + /* for scsi command */ + QEMUSGList *sg; + uint32_t data_len; +} UfsRequest; + +struct UfsLu; +typedef UfsReqResult (*UfsScsiOp)(struct UfsLu *, UfsRequest *); + +typedef struct UfsLu { + DeviceState qdev; + uint8_t lun; + UnitDescriptor unit_desc; + SCSIBus bus; + SCSIDevice *scsi_dev; + BlockConf conf; + UfsScsiOp scsi_op; +} UfsLu; + +typedef struct UfsParams { + char *serial; + uint8_t nutrs; /* Number of UTP Transfer Request Slots */ + uint8_t nutmrs; /* Number of UTP Task Management Request Slots */ +} UfsParams; + +typedef struct UfsHc { + PCIDevice parent_obj; + UfsBus bus; + MemoryRegion iomem; + UfsReg reg; + UfsParams params; + uint32_t reg_size; + UfsRequest *req_list; + + UfsLu *lus[UFS_MAX_LUS]; + UfsLu report_wlu; + UfsLu dev_wlu; + UfsLu boot_wlu; + UfsLu rpmb_wlu; + DeviceDescriptor device_desc; + GeometryDescriptor geometry_desc; + Attributes attributes; + Flags flags; + + qemu_irq irq; + QEMUBH *doorbell_bh; + QEMUBH *complete_bh; +} UfsHc; + +#define TYPE_UFS "ufs" +#define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS) + +#define TYPE_UFS_LU "ufs-lu" +#define UFSLU(obj) OBJECT_CHECK(UfsLu, (obj), TYPE_UFS_LU) + +typedef enum UfsQueryFlagPerm { + UFS_QUERY_FLAG_NONE = 0x0, + UFS_QUERY_FLAG_READ = 0x1, + UFS_QUERY_FLAG_SET = 0x2, + UFS_QUERY_FLAG_CLEAR = 0x4, + UFS_QUERY_FLAG_TOGGLE = 0x8, +} UfsQueryFlagPerm; + +typedef enum UfsQueryAttrPerm { + UFS_QUERY_ATTR_NONE = 0x0, + UFS_QUERY_ATTR_READ = 0x1, + UFS_QUERY_ATTR_WRITE = 0x2, +} UfsQueryAttrPerm; + +static inline bool is_wlun(uint8_t lun) +{ + return (lun == UFS_UPIU_REPORT_LUNS_WLUN || + lun == UFS_UPIU_UFS_DEVICE_WLUN || lun == UFS_UPIU_BOOT_WLUN || + lun == UFS_UPIU_RPMB_WLUN); +} + +void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags, + uint8_t response, uint8_t scsi_status, + uint16_t data_segment_length); +void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); +void ufs_init_wlu(UfsLu *wlu, uint8_t wlun); +#endif /* HW_UFS_UFS_H */ diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig index 53f8283ffd..f569ed7eea 100644 --- a/hw/usb/Kconfig +++ b/hw/usb/Kconfig @@ -11,6 +11,10 @@ config USB_OHCI bool select USB +config USB_OHCI_SYSBUS + bool + select USB_OHCI + config USB_OHCI_PCI bool default y if PCI_DEVICES @@ -36,7 +40,7 @@ config USB_XHCI config USB_XHCI_PCI bool - default y if PCI_DEVICES + default y if PCI_DEVICES || PCIE_DEVICES depends on PCI select USB_XHCI @@ -119,6 +123,11 @@ config USB_U2F default y depends on USB +config USB_CANOKEY + bool + default y + depends on USB + config IMX_USBPHY bool default y @@ -131,5 +140,4 @@ config USB_DWC3 config XLNX_USB_SUBSYS bool - default y if XLNX_VERSAL select USB_DWC3 diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 92d6ed5626..bfab2807d7 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -69,7 +69,7 @@ const VMStateDescription vmstate_usb_device = { .version_id = 1, .minimum_version_id = 1, .post_load = usb_device_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(addr, USBDevice), VMSTATE_INT32(state, USBDevice), VMSTATE_INT32(remote_wakeup, USBDevice), @@ -100,19 +100,6 @@ void usb_bus_release(USBBus *bus) QTAILQ_REMOVE(&busses, bus, next); } -USBBus *usb_bus_find(int busnr) -{ - USBBus *bus; - - if (-1 == busnr) - return QTAILQ_FIRST(&busses); - QTAILQ_FOREACH(bus, &busses, next) { - if (bus->busnr == busnr) - return bus; - } - return NULL; -} - static void usb_device_realize(USBDevice *dev, Error **errp) { USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); @@ -273,13 +260,14 @@ static void usb_qdev_realize(DeviceState *qdev, Error **errp) } if (dev->pcap_filename) { - int fd = qemu_open_old(dev->pcap_filename, O_CREAT | O_WRONLY | O_TRUNC, 0666); + int fd = qemu_open_old(dev->pcap_filename, + O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0666); if (fd < 0) { error_setg(errp, "open %s failed", dev->pcap_filename); usb_qdev_unrealize(qdev); return; } - dev->pcap = fdopen(fd, "w"); + dev->pcap = fdopen(fd, "wb"); usb_pcap_init(dev->pcap); } } @@ -329,29 +317,6 @@ void usb_legacy_register(const char *typename, const char *usbdevice_name, } } -USBDevice *usb_new(const char *name) -{ - return USB_DEVICE(qdev_new(name)); -} - -static USBDevice *usb_try_new(const char *name) -{ - return USB_DEVICE(qdev_try_new(name)); -} - -bool usb_realize_and_unref(USBDevice *dev, USBBus *bus, Error **errp) -{ - return qdev_realize_and_unref(&dev->qdev, &bus->qbus, errp); -} - -USBDevice *usb_create_simple(USBBus *bus, const char *name) -{ - USBDevice *dev = usb_new(name); - - usb_realize_and_unref(dev, bus, &error_abort); - return dev; -} - static void usb_fill_port(USBPort *port, void *opaque, int index, USBPortOps *ops, int speedmask) { @@ -666,7 +631,7 @@ HumanReadableText *qmp_x_query_usb(Error **errp) /* handle legacy -usbdevice cmd line option */ USBDevice *usbdevice_create(const char *driver) { - USBBus *bus = usb_bus_find(-1 /* any */); + USBBus *bus = QTAILQ_FIRST(&busses); LegacyUSBFactory *f = NULL; Error *err = NULL; GSList *i; diff --git a/hw/usb/canokey.c b/hw/usb/canokey.c new file mode 100644 index 0000000000..b306eeb20e --- /dev/null +++ b/hw/usb/canokey.c @@ -0,0 +1,334 @@ +/* + * CanoKey QEMU device implementation. + * + * Copyright (c) 2021-2022 Canokeys.org + * Written by Hongren (Zenithal) Zheng + * + * This code is licensed under the GPL v2 or later. + */ + +#include "qemu/osdep.h" +#include + +#include "qemu/module.h" +#include "qapi/error.h" +#include "hw/usb.h" +#include "hw/qdev-properties.h" +#include "trace.h" +#include "desc.h" +#include "canokey.h" + +#define CANOKEY_EP_IN(ep) ((ep) & 0x7F) + +#define CANOKEY_VENDOR_NUM 0x20a0 +#define CANOKEY_PRODUCT_NUM 0x42d2 + +/* + * placeholder, canokey-qemu implements its own usb desc + * Namely we do not use usb_desc_handle_contorl + */ +enum { + STR_MANUFACTURER = 1, + STR_PRODUCT, + STR_SERIALNUMBER +}; + +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "canokeys.org", + [STR_PRODUCT] = "CanoKey QEMU", + [STR_SERIALNUMBER] = "0" +}; + +static const USBDescDevice desc_device_canokey = { + .bcdUSB = 0x0, + .bMaxPacketSize0 = 16, + .bNumConfigurations = 0, + .confs = NULL, +}; + +static const USBDesc desc_canokey = { + .id = { + .idVendor = CANOKEY_VENDOR_NUM, + .idProduct = CANOKEY_PRODUCT_NUM, + .bcdDevice = 0x0100, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_canokey, + .str = desc_strings, +}; + + +/* + * libcanokey-qemu.so side functions + * All functions are called from canokey_emu_device_loop + */ +int canokey_emu_stall_ep(void *base, uint8_t ep) +{ + trace_canokey_emu_stall_ep(ep); + CanoKeyState *key = base; + uint8_t ep_in = CANOKEY_EP_IN(ep); /* INTR IN has ep 129 */ + key->ep_in_size[ep_in] = 0; + key->ep_in_state[ep_in] = CANOKEY_EP_IN_STALL; + return 0; +} + +int canokey_emu_set_address(void *base, uint8_t addr) +{ + trace_canokey_emu_set_address(addr); + CanoKeyState *key = base; + key->dev.addr = addr; + return 0; +} + +int canokey_emu_prepare_receive( + void *base, uint8_t ep, uint8_t *pbuf, uint16_t size) +{ + trace_canokey_emu_prepare_receive(ep, size); + CanoKeyState *key = base; + key->ep_out[ep] = pbuf; + key->ep_out_size[ep] = size; + return 0; +} + +int canokey_emu_transmit( + void *base, uint8_t ep, const uint8_t *pbuf, uint16_t size) +{ + trace_canokey_emu_transmit(ep, size); + CanoKeyState *key = base; + uint8_t ep_in = CANOKEY_EP_IN(ep); /* INTR IN has ep 129 */ + memcpy(key->ep_in[ep_in] + key->ep_in_size[ep_in], + pbuf, size); + key->ep_in_size[ep_in] += size; + key->ep_in_state[ep_in] = CANOKEY_EP_IN_READY; + /* + * wake up controller if we NAKed IN token before + * Note: this is a quirk for CanoKey CTAPHID + */ + if (ep_in == CANOKEY_EMU_EP_CTAPHID) { + usb_wakeup(usb_ep_get(&key->dev, USB_TOKEN_IN, ep_in), 0); + } + /* + * ready for more data in device loop + * + * Note: this is a quirk for CanoKey CTAPHID + * because it calls multiple emu_transmit in one device_loop + * but w/o data_in it would stuck in device_loop + * This has side effect for CCID since CCID can send ZLP + * This also has side effect for Control transfer + */ + if (ep_in == CANOKEY_EMU_EP_CTAPHID) { + canokey_emu_data_in(ep_in); + } + return 0; +} + +uint32_t canokey_emu_get_rx_data_size(void *base, uint8_t ep) +{ + CanoKeyState *key = base; + return key->ep_out_size[ep]; +} + +/* + * QEMU side functions + */ +static void canokey_handle_reset(USBDevice *dev) +{ + trace_canokey_handle_reset(); + CanoKeyState *key = CANOKEY(dev); + for (int i = 0; i != CANOKEY_EP_NUM; ++i) { + key->ep_in_state[i] = CANOKEY_EP_IN_WAIT; + key->ep_in_pos[i] = 0; + key->ep_in_size[i] = 0; + } + canokey_emu_reset(); +} + +static void canokey_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) +{ + trace_canokey_handle_control_setup(request, value, index, length); + CanoKeyState *key = CANOKEY(dev); + + canokey_emu_setup(request, value, index, length); + + uint32_t dir_in = request & DeviceRequest; + if (!dir_in) { + /* OUT */ + trace_canokey_handle_control_out(); + if (key->ep_out[0] != NULL) { + memcpy(key->ep_out[0], data, length); + } + canokey_emu_data_out(p->ep->nr, data); + } + + canokey_emu_device_loop(); + + /* IN */ + switch (key->ep_in_state[0]) { + case CANOKEY_EP_IN_WAIT: + p->status = USB_RET_NAK; + break; + case CANOKEY_EP_IN_STALL: + p->status = USB_RET_STALL; + break; + case CANOKEY_EP_IN_READY: + memcpy(data, key->ep_in[0], key->ep_in_size[0]); + p->actual_length = key->ep_in_size[0]; + trace_canokey_handle_control_in(p->actual_length); + /* reset state */ + key->ep_in_state[0] = CANOKEY_EP_IN_WAIT; + key->ep_in_size[0] = 0; + key->ep_in_pos[0] = 0; + break; + } +} + +static void canokey_handle_data(USBDevice *dev, USBPacket *p) +{ + CanoKeyState *key = CANOKEY(dev); + + uint8_t ep_in = CANOKEY_EP_IN(p->ep->nr); + uint8_t ep_out = p->ep->nr; + uint32_t in_len; + uint32_t out_pos; + uint32_t out_len; + switch (p->pid) { + case USB_TOKEN_OUT: + trace_canokey_handle_data_out(ep_out, p->iov.size); + usb_packet_copy(p, key->ep_out_buffer[ep_out], p->iov.size); + out_pos = 0; + while (out_pos != p->iov.size) { + /* + * key->ep_out[ep_out] set by prepare_receive + * to be a buffer inside libcanokey-qemu.so + * key->ep_out_size[ep_out] set by prepare_receive + * to be the buffer length + */ + out_len = MIN(p->iov.size - out_pos, key->ep_out_size[ep_out]); + memcpy(key->ep_out[ep_out], + key->ep_out_buffer[ep_out] + out_pos, out_len); + out_pos += out_len; + /* update ep_out_size to actual len */ + key->ep_out_size[ep_out] = out_len; + canokey_emu_data_out(ep_out, NULL); + } + /* + * Note: this is a quirk for CanoKey CTAPHID + * + * There is one code path that uses this device loop + * INTR IN -> useful data_in and useless device_loop -> NAKed + * INTR OUT -> useful device loop -> transmit -> wakeup + * (useful thanks to both data_in and data_out having been called) + * the next INTR IN -> actual data to guest + * + * if there is no such device loop, there would be no further + * INTR IN, no device loop, no transmit hence no usb_wakeup + * then qemu would hang + */ + if (ep_in == CANOKEY_EMU_EP_CTAPHID) { + canokey_emu_device_loop(); /* may call transmit multiple times */ + } + break; + case USB_TOKEN_IN: + if (key->ep_in_pos[ep_in] == 0) { /* first time IN */ + canokey_emu_data_in(ep_in); + canokey_emu_device_loop(); /* may call transmit multiple times */ + } + switch (key->ep_in_state[ep_in]) { + case CANOKEY_EP_IN_WAIT: + /* NAK for early INTR IN */ + p->status = USB_RET_NAK; + break; + case CANOKEY_EP_IN_STALL: + p->status = USB_RET_STALL; + break; + case CANOKEY_EP_IN_READY: + /* submit part of ep_in buffer to USBPacket */ + in_len = MIN(key->ep_in_size[ep_in] - key->ep_in_pos[ep_in], + p->iov.size); + usb_packet_copy(p, + key->ep_in[ep_in] + key->ep_in_pos[ep_in], in_len); + key->ep_in_pos[ep_in] += in_len; + /* reset state if all data submitted */ + if (key->ep_in_pos[ep_in] == key->ep_in_size[ep_in]) { + key->ep_in_state[ep_in] = CANOKEY_EP_IN_WAIT; + key->ep_in_size[ep_in] = 0; + key->ep_in_pos[ep_in] = 0; + } + trace_canokey_handle_data_in(ep_in, in_len); + break; + } + break; + default: + p->status = USB_RET_STALL; + break; + } +} + +static void canokey_realize(USBDevice *base, Error **errp) +{ + trace_canokey_realize(); + CanoKeyState *key = CANOKEY(base); + + if (key->file == NULL) { + error_setg(errp, "You must provide file=/path/to/canokey-file"); + return; + } + + usb_desc_init(base); + + for (int i = 0; i != CANOKEY_EP_NUM; ++i) { + key->ep_in_state[i] = CANOKEY_EP_IN_WAIT; + key->ep_in_size[i] = 0; + key->ep_in_pos[i] = 0; + } + + if (canokey_emu_init(key, key->file)) { + error_setg(errp, "canokey can not create or read %s", key->file); + return; + } +} + +static void canokey_unrealize(USBDevice *base) +{ + trace_canokey_unrealize(); +} + +static Property canokey_properties[] = { + DEFINE_PROP_STRING("file", CanoKeyState, file), + DEFINE_PROP_END_OF_LIST(), +}; + +static void canokey_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + + uc->product_desc = "CanoKey QEMU"; + uc->usb_desc = &desc_canokey; + uc->handle_reset = canokey_handle_reset; + uc->handle_control = canokey_handle_control; + uc->handle_data = canokey_handle_data; + uc->handle_attach = usb_desc_attach; + uc->realize = canokey_realize; + uc->unrealize = canokey_unrealize; + dc->desc = "CanoKey QEMU"; + device_class_set_props(dc, canokey_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo canokey_info = { + .name = TYPE_CANOKEY, + .parent = TYPE_USB_DEVICE, + .instance_size = sizeof(CanoKeyState), + .class_init = canokey_class_init +}; + +static void canokey_register_types(void) +{ + type_register_static(&canokey_info); +} + +type_init(canokey_register_types) diff --git a/hw/usb/canokey.h b/hw/usb/canokey.h new file mode 100644 index 0000000000..e528889d33 --- /dev/null +++ b/hw/usb/canokey.h @@ -0,0 +1,69 @@ +/* + * CanoKey QEMU device header. + * + * Copyright (c) 2021-2022 Canokeys.org + * Written by Hongren (Zenithal) Zheng + * + * This code is licensed under the GPL v2 or later. + */ + +#ifndef CANOKEY_H +#define CANOKEY_H + +#include "hw/qdev-core.h" + +#define TYPE_CANOKEY "canokey" +#define CANOKEY(obj) \ + OBJECT_CHECK(CanoKeyState, (obj), TYPE_CANOKEY) + +/* + * State of Canokey (i.e. hw/canokey.c) + */ + +/* CTRL INTR BULK */ +#define CANOKEY_EP_NUM 3 +/* BULK/INTR IN can be up to 1352 bytes, e.g. get key info */ +#define CANOKEY_EP_IN_BUFFER_SIZE 2048 +/* BULK OUT can be up to 270 bytes, e.g. PIV import cert */ +#define CANOKEY_EP_OUT_BUFFER_SIZE 512 + +typedef enum { + CANOKEY_EP_IN_WAIT, + CANOKEY_EP_IN_READY, + CANOKEY_EP_IN_STALL +} CanoKeyEPState; + +typedef struct CanoKeyState { + USBDevice dev; + + /* IN packets from canokey device loop */ + uint8_t ep_in[CANOKEY_EP_NUM][CANOKEY_EP_IN_BUFFER_SIZE]; + /* + * See canokey_emu_transmit + * + * For large INTR IN, receive multiple data from canokey device loop + * in this case ep_in_size would increase with every call + */ + uint32_t ep_in_size[CANOKEY_EP_NUM]; + /* + * Used in canokey_handle_data + * for IN larger than p->iov.size, we would do multiple handle_data() + * + * The difference between ep_in_pos and ep_in_size: + * We first increase ep_in_size to fill ep_in buffer in device_loop, + * then use ep_in_pos to submit data from ep_in buffer in handle_data + */ + uint32_t ep_in_pos[CANOKEY_EP_NUM]; + CanoKeyEPState ep_in_state[CANOKEY_EP_NUM]; + + /* OUT pointer to canokey recv buffer */ + uint8_t *ep_out[CANOKEY_EP_NUM]; + uint32_t ep_out_size[CANOKEY_EP_NUM]; + /* For large BULK OUT, multiple write to ep_out is needed */ + uint8_t ep_out_buffer[CANOKEY_EP_NUM][CANOKEY_EP_OUT_BUFFER_SIZE]; + + /* Properties */ + char *file; /* canokey-file */ +} CanoKeyState; + +#endif /* CANOKEY_H */ diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c index 1ddf7297f6..3ee9c73b87 100644 --- a/hw/usb/ccid-card-emulated.c +++ b/hw/usb/ccid-card-emulated.c @@ -140,7 +140,7 @@ static void emulated_apdu_from_guest(CCIDCardState *base, const uint8_t *apdu, uint32_t len) { EmulatedState *card = EMULATED_CCID_CARD(base); - EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len); + EmulEvent *event = g_malloc(sizeof(EmulEvent) + len); assert(event); event->p.data.type = EMUL_GUEST_APDU; @@ -248,7 +248,7 @@ static void *handle_apdu_thread(void* arg) WITH_QEMU_LOCK_GUARD(&card->vreader_mutex) { while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) { event = QSIMPLEQ_FIRST(&card->guest_apdu_list); - assert((unsigned long)event > 1000); + assert(event != NULL); QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry); if (event->p.data.type != EMUL_GUEST_APDU) { DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n"); @@ -518,7 +518,7 @@ static void emulated_realize(CCIDCardState *base, Error **errp) goto out2; } - /* TODO: a passthru backened that works on local machine. third card type?*/ + /* TODO: a passthru backend that works on local machine. third card type?*/ if (card->backend == BACKEND_CERTIFICATES) { if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) { ret = emulated_initialize_vcard_from_certificates(card); diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index 07ee42f304..a515703904 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -378,7 +378,7 @@ static const VMStateDescription passthru_vmstate = { .name = "ccid-card-passthru", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BUFFER(vscard_in_data, PassthruState), VMSTATE_UINT32(vscard_in_pos, PassthruState), VMSTATE_UINT32(vscard_in_hdr, PassthruState), diff --git a/hw/usb/desc.c b/hw/usb/desc.c index 7f6cc2f99b..f2bdc05a95 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -227,7 +227,7 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, int flags, } if (superlen) { - USBDescriptor *d = (void *)(dest + bLength); + d = (void *)(dest + bLength); d->bLength = 0x06; d->bDescriptorType = USB_DT_ENDPOINT_COMPANION; diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c index 8748c1ba04..1897fff9e6 100644 --- a/hw/usb/dev-audio.c +++ b/hw/usb/dev-audio.c @@ -124,7 +124,6 @@ static const USBDescIface desc_iface[] = { .bNumEndpoints = 0, .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, - .bInterfaceProtocol = 0x04, .iInterface = STRING_USBAUDIO_CONTROL, .ndesc = 4, .descs = (USBDescOther[]) { @@ -282,7 +281,6 @@ static const USBDescIface desc_iface_multi[] = { .bNumEndpoints = 0, .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, - .bInterfaceProtocol = 0x04, .iInterface = STRING_USBAUDIO_CONTROL, .ndesc = 4, .descs = (USBDescOther[]) { @@ -293,7 +291,7 @@ static const USBDescIface desc_iface_multi[] = { USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ DST_AC_HEADER, /* u8 bDescriptorSubtype */ U16(0x0100), /* u16 bcdADC */ - U16(0x38), /* u16 wTotalLength */ + U16(0x37), /* u16 wTotalLength */ 0x01, /* u8 bInCollection */ 0x01, /* u8 baInterfaceNr */ } @@ -944,12 +942,15 @@ static void usb_audio_realize(USBDevice *dev, Error **errp) USBAudioState *s = USB_AUDIO(dev); int i; + if (!AUD_register_card(TYPE_USB_AUDIO, &s->card, errp)) { + return; + } + dev->usb_desc = s->multi ? &desc_audio_multi : &desc_audio; usb_desc_create_serial(dev); usb_desc_init(dev); s->dev.opaque = s; - AUD_register_card(TYPE_USB_AUDIO, &s->card); s->out.altset = ALTSET_OFF; s->out.vol.mute = false; diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index bdd6d1ffaf..9e358c934e 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -756,7 +756,7 @@ static const VMStateDescription vmstate_usb_ptr = { .version_id = 1, .minimum_version_id = 1, .post_load = usb_ptr_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_USB_DEVICE(dev, USBHIDState), VMSTATE_HID_POINTER_DEVICE(hid, USBHIDState), VMSTATE_END_OF_LIST() @@ -767,7 +767,7 @@ static const VMStateDescription vmstate_usb_kbd = { .name = "usb-kbd", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_USB_DEVICE(dev, USBHIDState), VMSTATE_HID_KEYBOARD_DEVICE(hid, USBHIDState), VMSTATE_END_OF_LIST() diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index e35813d772..06e9537d03 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -54,44 +54,44 @@ struct USBHubState { #define TYPE_USB_HUB "usb-hub" OBJECT_DECLARE_SIMPLE_TYPE(USBHubState, USB_HUB) -#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) -#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE) -#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR) -#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS) -#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS) -#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE) -#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE) +#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) +#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE) +#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR) +#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS) +#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS) +#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE) +#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE) -#define PORT_STAT_CONNECTION 0x0001 -#define PORT_STAT_ENABLE 0x0002 -#define PORT_STAT_SUSPEND 0x0004 -#define PORT_STAT_OVERCURRENT 0x0008 -#define PORT_STAT_RESET 0x0010 -#define PORT_STAT_POWER 0x0100 -#define PORT_STAT_LOW_SPEED 0x0200 +#define PORT_STAT_CONNECTION 0x0001 +#define PORT_STAT_ENABLE 0x0002 +#define PORT_STAT_SUSPEND 0x0004 +#define PORT_STAT_OVERCURRENT 0x0008 +#define PORT_STAT_RESET 0x0010 +#define PORT_STAT_POWER 0x0100 +#define PORT_STAT_LOW_SPEED 0x0200 #define PORT_STAT_HIGH_SPEED 0x0400 #define PORT_STAT_TEST 0x0800 #define PORT_STAT_INDICATOR 0x1000 -#define PORT_STAT_C_CONNECTION 0x0001 -#define PORT_STAT_C_ENABLE 0x0002 -#define PORT_STAT_C_SUSPEND 0x0004 -#define PORT_STAT_C_OVERCURRENT 0x0008 -#define PORT_STAT_C_RESET 0x0010 +#define PORT_STAT_C_CONNECTION 0x0001 +#define PORT_STAT_C_ENABLE 0x0002 +#define PORT_STAT_C_SUSPEND 0x0004 +#define PORT_STAT_C_OVERCURRENT 0x0008 +#define PORT_STAT_C_RESET 0x0010 -#define PORT_CONNECTION 0 -#define PORT_ENABLE 1 -#define PORT_SUSPEND 2 -#define PORT_OVERCURRENT 3 -#define PORT_RESET 4 -#define PORT_POWER 8 -#define PORT_LOWSPEED 9 -#define PORT_HIGHSPEED 10 -#define PORT_C_CONNECTION 16 -#define PORT_C_ENABLE 17 -#define PORT_C_SUSPEND 18 -#define PORT_C_OVERCURRENT 19 -#define PORT_C_RESET 20 +#define PORT_CONNECTION 0 +#define PORT_ENABLE 1 +#define PORT_SUSPEND 2 +#define PORT_OVERCURRENT 3 +#define PORT_RESET 4 +#define PORT_POWER 8 +#define PORT_LOWSPEED 9 +#define PORT_HIGHSPEED 10 +#define PORT_C_CONNECTION 16 +#define PORT_C_ENABLE 17 +#define PORT_C_SUSPEND 18 +#define PORT_C_OVERCURRENT 19 +#define PORT_C_RESET 20 #define PORT_TEST 21 #define PORT_INDICATOR 22 @@ -155,13 +155,13 @@ static const USBDesc desc_hub = { static const uint8_t qemu_hub_hub_descriptor[] = { - 0x00, /* u8 bLength; patched in later */ - 0x29, /* u8 bDescriptorType; Hub-descriptor */ - 0x00, /* u8 bNbrPorts; (patched later) */ - 0x0a, /* u16 wHubCharacteristics; */ - 0x00, /* (per-port OC, no power switching) */ - 0x01, /* u8 bPwrOn2pwrGood; 2ms */ - 0x00 /* u8 bHubContrCurrent; 0 mA */ + 0x00, /* u8 bLength; patched in later */ + 0x29, /* u8 bDescriptorType; Hub-descriptor */ + 0x00, /* u8 bNbrPorts; (patched later) */ + 0x0a, /* u16 wHubCharacteristics; */ + 0x00, /* (per-port OC, no power switching) */ + 0x01, /* u8 bPwrOn2pwrGood; 2ms */ + 0x00 /* u8 bHubContrCurrent; 0 mA */ /* DeviceRemovable and PortPwrCtrlMask patched in later */ }; @@ -402,7 +402,7 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, { unsigned int n = index - 1; USBHubPort *port; - USBDevice *dev; + USBDevice *pdev; trace_usb_hub_set_port_feature(s->dev.addr, index, feature_name(value)); @@ -411,7 +411,7 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, goto fail; } port = &s->ports[n]; - dev = port->port.dev; + pdev = port->port.dev; switch(value) { case PORT_SUSPEND: port->wPortStatus |= PORT_STAT_SUSPEND; @@ -419,8 +419,8 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, case PORT_RESET: usb_hub_port_set(port, PORT_STAT_RESET); usb_hub_port_clear(port, PORT_STAT_RESET); - if (dev && dev->attached) { - usb_device_reset(dev); + if (pdev && pdev->attached) { + usb_device_reset(pdev); usb_hub_port_set(port, PORT_STAT_ENABLE); } usb_wakeup(s->intr, 0); @@ -623,7 +623,7 @@ static const VMStateDescription vmstate_usb_hub_port = { .name = "usb-hub-port", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(wPortStatus, USBHubPort), VMSTATE_UINT16(wPortChange, USBHubPort), VMSTATE_END_OF_LIST() @@ -642,7 +642,7 @@ static const VMStateDescription vmstate_usb_hub_port_timer = { .version_id = 1, .minimum_version_id = 1, .needed = usb_hub_port_timer_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(port_timer, USBHubState), VMSTATE_END_OF_LIST() }, @@ -652,13 +652,13 @@ static const VMStateDescription vmstate_usb_hub = { .name = "usb-hub", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_USB_DEVICE(dev, USBHubState), VMSTATE_STRUCT_ARRAY(ports, USBHubState, MAX_PORTS, 0, vmstate_usb_hub_port, USBHubPort), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_usb_hub_port_timer, NULL } diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 5831395cef..7e4a0765ae 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -14,7 +14,7 @@ #include "qemu/error-report.h" #include #include - +#include #include @@ -1622,7 +1622,7 @@ static void usb_mtp_write_data(MTPState *s, uint32_t handle) if (s->dataset.filename) { path = g_strdup_printf("%s/%s", parent->path, s->dataset.filename); if (s->dataset.format == FMT_ASSOCIATION) { - ret = mkdir(path, mask); + ret = g_mkdir(path, mask); if (!ret) { usb_mtp_queue_result(s, RES_OK, d->trans, 3, QEMU_STORAGE_ID, @@ -2072,7 +2072,7 @@ static const VMStateDescription vmstate_usb_mtp = { .unmigratable = 1, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_USB_DEVICE(dev, MTPState), VMSTATE_END_OF_LIST() } diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 6c49c16015..2c33e36cad 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -52,7 +52,7 @@ #define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ enum usbstring_idx { - STRING_MANUFACTURER = 1, + STRING_MANUFACTURER = 1, STRING_PRODUCT, STRING_ETHADDR, STRING_DATA, @@ -64,37 +64,39 @@ enum usbstring_idx { STRING_SERIALNUMBER, }; -#define DEV_CONFIG_VALUE 1 /* CDC or a subset */ -#define DEV_RNDIS_CONFIG_VALUE 2 /* RNDIS; optional */ +#define DEV_CONFIG_VALUE 1 /* CDC or a subset */ +#define DEV_RNDIS_CONFIG_VALUE 2 /* RNDIS; optional */ -#define USB_CDC_SUBCLASS_ACM 0x02 -#define USB_CDC_SUBCLASS_ETHERNET 0x06 +#define USB_CDC_SUBCLASS_ACM 0x02 +#define USB_CDC_SUBCLASS_ETHERNET 0x06 -#define USB_CDC_PROTO_NONE 0 -#define USB_CDC_ACM_PROTO_VENDOR 0xff +#define USB_CDC_PROTO_NONE 0 +#define USB_CDC_ACM_PROTO_VENDOR 0xff -#define USB_CDC_HEADER_TYPE 0x00 /* header_desc */ -#define USB_CDC_CALL_MANAGEMENT_TYPE 0x01 /* call_mgmt_descriptor */ -#define USB_CDC_ACM_TYPE 0x02 /* acm_descriptor */ -#define USB_CDC_UNION_TYPE 0x06 /* union_desc */ -#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */ +#define USB_CDC_HEADER_TYPE 0x00 /* header_desc */ +#define USB_CDC_CALL_MANAGEMENT_TYPE 0x01 /* call_mgmt_descriptor */ +#define USB_CDC_ACM_TYPE 0x02 /* acm_descriptor */ +#define USB_CDC_UNION_TYPE 0x06 /* union_desc */ +#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */ -#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00 -#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01 -#define USB_CDC_REQ_SET_LINE_CODING 0x20 -#define USB_CDC_REQ_GET_LINE_CODING 0x21 -#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 -#define USB_CDC_REQ_SEND_BREAK 0x23 -#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 -#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41 -#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42 -#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43 -#define USB_CDC_GET_ETHERNET_STATISTIC 0x44 +#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00 +#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01 +#define USB_CDC_REQ_SET_LINE_CODING 0x20 +#define USB_CDC_REQ_GET_LINE_CODING 0x21 +#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 +#define USB_CDC_REQ_SEND_BREAK 0x23 +#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 +#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41 +#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42 +#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43 +#define USB_CDC_GET_ETHERNET_STATISTIC 0x44 -#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ -#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ +#define USB_CDC_NETWORK_CONNECTION 0x00 -#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ +#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ +#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ + +#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ static const USBDescStrings usb_net_stringtable = { [STRING_MANUFACTURER] = "QEMU", @@ -304,57 +306,57 @@ static const USBDesc desc_net = { /* * RNDIS Definitions - in theory not specific to USB. */ -#define RNDIS_MAXIMUM_FRAME_SIZE 1518 -#define RNDIS_MAX_TOTAL_SIZE 1558 +#define RNDIS_MAXIMUM_FRAME_SIZE 1518 +#define RNDIS_MAX_TOTAL_SIZE 1558 /* Remote NDIS Versions */ -#define RNDIS_MAJOR_VERSION 1 -#define RNDIS_MINOR_VERSION 0 +#define RNDIS_MAJOR_VERSION 1 +#define RNDIS_MINOR_VERSION 0 /* Status Values */ -#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */ -#define RNDIS_STATUS_FAILURE 0xc0000001U /* Unspecified error */ -#define RNDIS_STATUS_INVALID_DATA 0xc0010015U /* Invalid data */ -#define RNDIS_STATUS_NOT_SUPPORTED 0xc00000bbU /* Unsupported request */ -#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000bU /* Device connected */ -#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000cU /* Device disconnected */ +#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */ +#define RNDIS_STATUS_FAILURE 0xc0000001U /* Unspecified error */ +#define RNDIS_STATUS_INVALID_DATA 0xc0010015U /* Invalid data */ +#define RNDIS_STATUS_NOT_SUPPORTED 0xc00000bbU /* Unsupported request */ +#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000bU /* Device connected */ +#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000cU /* Device disconnected */ /* Message Set for Connectionless (802.3) Devices */ enum { - RNDIS_PACKET_MSG = 1, - RNDIS_INITIALIZE_MSG = 2, /* Initialize device */ - RNDIS_HALT_MSG = 3, - RNDIS_QUERY_MSG = 4, - RNDIS_SET_MSG = 5, - RNDIS_RESET_MSG = 6, - RNDIS_INDICATE_STATUS_MSG = 7, - RNDIS_KEEPALIVE_MSG = 8, + RNDIS_PACKET_MSG = 1, + RNDIS_INITIALIZE_MSG = 2, /* Initialize device */ + RNDIS_HALT_MSG = 3, + RNDIS_QUERY_MSG = 4, + RNDIS_SET_MSG = 5, + RNDIS_RESET_MSG = 6, + RNDIS_INDICATE_STATUS_MSG = 7, + RNDIS_KEEPALIVE_MSG = 8, }; /* Message completion */ enum { - RNDIS_INITIALIZE_CMPLT = 0x80000002U, - RNDIS_QUERY_CMPLT = 0x80000004U, - RNDIS_SET_CMPLT = 0x80000005U, - RNDIS_RESET_CMPLT = 0x80000006U, - RNDIS_KEEPALIVE_CMPLT = 0x80000008U, + RNDIS_INITIALIZE_CMPLT = 0x80000002U, + RNDIS_QUERY_CMPLT = 0x80000004U, + RNDIS_SET_CMPLT = 0x80000005U, + RNDIS_RESET_CMPLT = 0x80000006U, + RNDIS_KEEPALIVE_CMPLT = 0x80000008U, }; /* Device Flags */ enum { - RNDIS_DF_CONNECTIONLESS = 1, - RNDIS_DF_CONNECTIONORIENTED = 2, + RNDIS_DF_CONNECTIONLESS = 1, + RNDIS_DF_CONNECTIONORIENTED = 2, }; -#define RNDIS_MEDIUM_802_3 0x00000000U +#define RNDIS_MEDIUM_802_3 0x00000000U /* from drivers/net/sk98lin/h/skgepnmi.h */ -#define OID_PNP_CAPABILITIES 0xfd010100 -#define OID_PNP_SET_POWER 0xfd010101 -#define OID_PNP_QUERY_POWER 0xfd010102 -#define OID_PNP_ADD_WAKE_UP_PATTERN 0xfd010103 -#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xfd010104 -#define OID_PNP_ENABLE_WAKE_UP 0xfd010106 +#define OID_PNP_CAPABILITIES 0xfd010100 +#define OID_PNP_SET_POWER 0xfd010101 +#define OID_PNP_QUERY_POWER 0xfd010102 +#define OID_PNP_ADD_WAKE_UP_PATTERN 0xfd010103 +#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xfd010104 +#define OID_PNP_ENABLE_WAKE_UP 0xfd010106 typedef uint32_t le32; @@ -492,88 +494,88 @@ enum rndis_state /* from ndis.h */ enum ndis_oid { /* Required Object IDs (OIDs) */ - OID_GEN_SUPPORTED_LIST = 0x00010101, - OID_GEN_HARDWARE_STATUS = 0x00010102, - OID_GEN_MEDIA_SUPPORTED = 0x00010103, - OID_GEN_MEDIA_IN_USE = 0x00010104, - OID_GEN_MAXIMUM_LOOKAHEAD = 0x00010105, - OID_GEN_MAXIMUM_FRAME_SIZE = 0x00010106, - OID_GEN_LINK_SPEED = 0x00010107, - OID_GEN_TRANSMIT_BUFFER_SPACE = 0x00010108, - OID_GEN_RECEIVE_BUFFER_SPACE = 0x00010109, - OID_GEN_TRANSMIT_BLOCK_SIZE = 0x0001010a, - OID_GEN_RECEIVE_BLOCK_SIZE = 0x0001010b, - OID_GEN_VENDOR_ID = 0x0001010c, - OID_GEN_VENDOR_DESCRIPTION = 0x0001010d, - OID_GEN_CURRENT_PACKET_FILTER = 0x0001010e, - OID_GEN_CURRENT_LOOKAHEAD = 0x0001010f, - OID_GEN_DRIVER_VERSION = 0x00010110, - OID_GEN_MAXIMUM_TOTAL_SIZE = 0x00010111, - OID_GEN_PROTOCOL_OPTIONS = 0x00010112, - OID_GEN_MAC_OPTIONS = 0x00010113, - OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114, - OID_GEN_MAXIMUM_SEND_PACKETS = 0x00010115, - OID_GEN_VENDOR_DRIVER_VERSION = 0x00010116, - OID_GEN_SUPPORTED_GUIDS = 0x00010117, - OID_GEN_NETWORK_LAYER_ADDRESSES = 0x00010118, - OID_GEN_TRANSPORT_HEADER_OFFSET = 0x00010119, - OID_GEN_MACHINE_NAME = 0x0001021a, - OID_GEN_RNDIS_CONFIG_PARAMETER = 0x0001021b, - OID_GEN_VLAN_ID = 0x0001021c, + OID_GEN_SUPPORTED_LIST = 0x00010101, + OID_GEN_HARDWARE_STATUS = 0x00010102, + OID_GEN_MEDIA_SUPPORTED = 0x00010103, + OID_GEN_MEDIA_IN_USE = 0x00010104, + OID_GEN_MAXIMUM_LOOKAHEAD = 0x00010105, + OID_GEN_MAXIMUM_FRAME_SIZE = 0x00010106, + OID_GEN_LINK_SPEED = 0x00010107, + OID_GEN_TRANSMIT_BUFFER_SPACE = 0x00010108, + OID_GEN_RECEIVE_BUFFER_SPACE = 0x00010109, + OID_GEN_TRANSMIT_BLOCK_SIZE = 0x0001010a, + OID_GEN_RECEIVE_BLOCK_SIZE = 0x0001010b, + OID_GEN_VENDOR_ID = 0x0001010c, + OID_GEN_VENDOR_DESCRIPTION = 0x0001010d, + OID_GEN_CURRENT_PACKET_FILTER = 0x0001010e, + OID_GEN_CURRENT_LOOKAHEAD = 0x0001010f, + OID_GEN_DRIVER_VERSION = 0x00010110, + OID_GEN_MAXIMUM_TOTAL_SIZE = 0x00010111, + OID_GEN_PROTOCOL_OPTIONS = 0x00010112, + OID_GEN_MAC_OPTIONS = 0x00010113, + OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114, + OID_GEN_MAXIMUM_SEND_PACKETS = 0x00010115, + OID_GEN_VENDOR_DRIVER_VERSION = 0x00010116, + OID_GEN_SUPPORTED_GUIDS = 0x00010117, + OID_GEN_NETWORK_LAYER_ADDRESSES = 0x00010118, + OID_GEN_TRANSPORT_HEADER_OFFSET = 0x00010119, + OID_GEN_MACHINE_NAME = 0x0001021a, + OID_GEN_RNDIS_CONFIG_PARAMETER = 0x0001021b, + OID_GEN_VLAN_ID = 0x0001021c, /* Optional OIDs */ - OID_GEN_MEDIA_CAPABILITIES = 0x00010201, - OID_GEN_PHYSICAL_MEDIUM = 0x00010202, + OID_GEN_MEDIA_CAPABILITIES = 0x00010201, + OID_GEN_PHYSICAL_MEDIUM = 0x00010202, /* Required statistics OIDs */ - OID_GEN_XMIT_OK = 0x00020101, - OID_GEN_RCV_OK = 0x00020102, - OID_GEN_XMIT_ERROR = 0x00020103, - OID_GEN_RCV_ERROR = 0x00020104, - OID_GEN_RCV_NO_BUFFER = 0x00020105, + OID_GEN_XMIT_OK = 0x00020101, + OID_GEN_RCV_OK = 0x00020102, + OID_GEN_XMIT_ERROR = 0x00020103, + OID_GEN_RCV_ERROR = 0x00020104, + OID_GEN_RCV_NO_BUFFER = 0x00020105, /* Optional statistics OIDs */ - OID_GEN_DIRECTED_BYTES_XMIT = 0x00020201, - OID_GEN_DIRECTED_FRAMES_XMIT = 0x00020202, - OID_GEN_MULTICAST_BYTES_XMIT = 0x00020203, - OID_GEN_MULTICAST_FRAMES_XMIT = 0x00020204, - OID_GEN_BROADCAST_BYTES_XMIT = 0x00020205, - OID_GEN_BROADCAST_FRAMES_XMIT = 0x00020206, - OID_GEN_DIRECTED_BYTES_RCV = 0x00020207, - OID_GEN_DIRECTED_FRAMES_RCV = 0x00020208, - OID_GEN_MULTICAST_BYTES_RCV = 0x00020209, - OID_GEN_MULTICAST_FRAMES_RCV = 0x0002020a, - OID_GEN_BROADCAST_BYTES_RCV = 0x0002020b, - OID_GEN_BROADCAST_FRAMES_RCV = 0x0002020c, - OID_GEN_RCV_CRC_ERROR = 0x0002020d, - OID_GEN_TRANSMIT_QUEUE_LENGTH = 0x0002020e, - OID_GEN_GET_TIME_CAPS = 0x0002020f, - OID_GEN_GET_NETCARD_TIME = 0x00020210, - OID_GEN_NETCARD_LOAD = 0x00020211, - OID_GEN_DEVICE_PROFILE = 0x00020212, - OID_GEN_INIT_TIME_MS = 0x00020213, - OID_GEN_RESET_COUNTS = 0x00020214, - OID_GEN_MEDIA_SENSE_COUNTS = 0x00020215, - OID_GEN_FRIENDLY_NAME = 0x00020216, - OID_GEN_MINIPORT_INFO = 0x00020217, - OID_GEN_RESET_VERIFY_PARAMETERS = 0x00020218, + OID_GEN_DIRECTED_BYTES_XMIT = 0x00020201, + OID_GEN_DIRECTED_FRAMES_XMIT = 0x00020202, + OID_GEN_MULTICAST_BYTES_XMIT = 0x00020203, + OID_GEN_MULTICAST_FRAMES_XMIT = 0x00020204, + OID_GEN_BROADCAST_BYTES_XMIT = 0x00020205, + OID_GEN_BROADCAST_FRAMES_XMIT = 0x00020206, + OID_GEN_DIRECTED_BYTES_RCV = 0x00020207, + OID_GEN_DIRECTED_FRAMES_RCV = 0x00020208, + OID_GEN_MULTICAST_BYTES_RCV = 0x00020209, + OID_GEN_MULTICAST_FRAMES_RCV = 0x0002020a, + OID_GEN_BROADCAST_BYTES_RCV = 0x0002020b, + OID_GEN_BROADCAST_FRAMES_RCV = 0x0002020c, + OID_GEN_RCV_CRC_ERROR = 0x0002020d, + OID_GEN_TRANSMIT_QUEUE_LENGTH = 0x0002020e, + OID_GEN_GET_TIME_CAPS = 0x0002020f, + OID_GEN_GET_NETCARD_TIME = 0x00020210, + OID_GEN_NETCARD_LOAD = 0x00020211, + OID_GEN_DEVICE_PROFILE = 0x00020212, + OID_GEN_INIT_TIME_MS = 0x00020213, + OID_GEN_RESET_COUNTS = 0x00020214, + OID_GEN_MEDIA_SENSE_COUNTS = 0x00020215, + OID_GEN_FRIENDLY_NAME = 0x00020216, + OID_GEN_MINIPORT_INFO = 0x00020217, + OID_GEN_RESET_VERIFY_PARAMETERS = 0x00020218, /* IEEE 802.3 (Ethernet) OIDs */ - OID_802_3_PERMANENT_ADDRESS = 0x01010101, - OID_802_3_CURRENT_ADDRESS = 0x01010102, - OID_802_3_MULTICAST_LIST = 0x01010103, - OID_802_3_MAXIMUM_LIST_SIZE = 0x01010104, - OID_802_3_MAC_OPTIONS = 0x01010105, - OID_802_3_RCV_ERROR_ALIGNMENT = 0x01020101, - OID_802_3_XMIT_ONE_COLLISION = 0x01020102, - OID_802_3_XMIT_MORE_COLLISIONS = 0x01020103, - OID_802_3_XMIT_DEFERRED = 0x01020201, - OID_802_3_XMIT_MAX_COLLISIONS = 0x01020202, - OID_802_3_RCV_OVERRUN = 0x01020203, - OID_802_3_XMIT_UNDERRUN = 0x01020204, - OID_802_3_XMIT_HEARTBEAT_FAILURE = 0x01020205, - OID_802_3_XMIT_TIMES_CRS_LOST = 0x01020206, - OID_802_3_XMIT_LATE_COLLISIONS = 0x01020207, + OID_802_3_PERMANENT_ADDRESS = 0x01010101, + OID_802_3_CURRENT_ADDRESS = 0x01010102, + OID_802_3_MULTICAST_LIST = 0x01010103, + OID_802_3_MAXIMUM_LIST_SIZE = 0x01010104, + OID_802_3_MAC_OPTIONS = 0x01010105, + OID_802_3_RCV_ERROR_ALIGNMENT = 0x01020101, + OID_802_3_XMIT_ONE_COLLISION = 0x01020102, + OID_802_3_XMIT_MORE_COLLISIONS = 0x01020103, + OID_802_3_XMIT_DEFERRED = 0x01020201, + OID_802_3_XMIT_MAX_COLLISIONS = 0x01020202, + OID_802_3_RCV_OVERRUN = 0x01020203, + OID_802_3_XMIT_UNDERRUN = 0x01020204, + OID_802_3_XMIT_HEARTBEAT_FAILURE = 0x01020205, + OID_802_3_XMIT_TIMES_CRS_LOST = 0x01020206, + OID_802_3_XMIT_LATE_COLLISIONS = 0x01020207, }; static const uint32_t oid_supported_list[] = @@ -616,13 +618,13 @@ static const uint32_t oid_supported_list[] = OID_802_3_XMIT_MORE_COLLISIONS, }; -#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA (1 << 0) -#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED (1 << 1) -#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND (1 << 2) -#define NDIS_MAC_OPTION_NO_LOOPBACK (1 << 3) -#define NDIS_MAC_OPTION_FULL_DUPLEX (1 << 4) -#define NDIS_MAC_OPTION_EOTX_INDICATION (1 << 5) -#define NDIS_MAC_OPTION_8021P_PRIORITY (1 << 6) +#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA (1 << 0) +#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED (1 << 1) +#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND (1 << 2) +#define NDIS_MAC_OPTION_NO_LOOPBACK (1 << 3) +#define NDIS_MAC_OPTION_FULL_DUPLEX (1 << 4) +#define NDIS_MAC_OPTION_EOTX_INDICATION (1 << 5) +#define NDIS_MAC_OPTION_8021P_PRIORITY (1 << 6) struct rndis_response { QTAILQ_ENTRY(rndis_response) entries; @@ -640,6 +642,8 @@ struct USBNetState { uint16_t filter; uint32_t vendorid; + uint16_t connection; + unsigned int out_ptr; uint8_t out_buf[2048]; @@ -647,6 +651,7 @@ struct USBNetState { uint8_t in_buf[2048]; USBEndpoint *intr; + USBEndpoint *bulk_in; char usbstring_mac[13]; NICState *nic; @@ -1121,6 +1126,12 @@ static void usb_net_handle_control(USBDevice *dev, USBPacket *p, #endif break; + case ClassInterfaceOutRequest | USB_CDC_SET_ETHERNET_PACKET_FILTER: + if (is_rndis(s)) { + goto fail; + } + break; + default: fail: fprintf(stderr, "usbnet: failed control transaction: " @@ -1133,18 +1144,28 @@ static void usb_net_handle_control(USBDevice *dev, USBPacket *p, static void usb_net_handle_statusin(USBNetState *s, USBPacket *p) { - le32 buf[2]; + le32 rbuf[2]; + uint16_t ebuf[4]; if (p->iov.size < 8) { p->status = USB_RET_STALL; return; } - buf[0] = cpu_to_le32(1); - buf[1] = cpu_to_le32(0); - usb_packet_copy(p, buf, 8); - if (!s->rndis_resp.tqh_first) { - p->status = USB_RET_NAK; + if (is_rndis(s)) { + rbuf[0] = cpu_to_le32(1); + rbuf[1] = cpu_to_le32(0); + usb_packet_copy(p, rbuf, 8); + if (!s->rndis_resp.tqh_first) { + p->status = USB_RET_NAK; + } + } else { + ebuf[0] = + cpu_to_be16(ClassInterfaceRequest | USB_CDC_NETWORK_CONNECTION); + ebuf[1] = cpu_to_le16(s->connection); + ebuf[2] = cpu_to_le16(1); + ebuf[3] = cpu_to_le16(0); + usb_packet_copy(p, ebuf, 8); } #ifdef TRAFFIC_DEBUG @@ -1204,7 +1225,7 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p) s->out_ptr += sz; if (!is_rndis(s)) { - if (p->iov.size < 64) { + if (p->iov.size % 64 || p->iov.size == 0) { qemu_send_packet(qemu_get_queue(s->nic), s->out_buf, s->out_ptr); s->out_ptr = 0; } @@ -1317,6 +1338,7 @@ static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t siz memcpy(in_buf, buf, size); s->in_len = total_size; s->in_ptr = 0; + usb_wakeup(s->bulk_in, 0); return size; } @@ -1353,16 +1375,19 @@ static void usb_net_realize(USBDevice *dev, Error **errp) s->rndis_state = RNDIS_UNINITIALIZED; QTAILQ_INIT(&s->rndis_resp); - s->medium = 0; /* NDIS_MEDIUM_802_3 */ + s->medium = 0; /* NDIS_MEDIUM_802_3 */ s->speed = 1000000; /* 100MBps, in 100Bps units */ - s->media_state = 0; /* NDIS_MEDIA_STATE_CONNECTED */; + s->media_state = 0; /* NDIS_MEDIA_STATE_CONNECTED */; s->filter = 0; s->vendorid = 0x1234; + s->connection = 1; /* Connected */ s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); + s->bulk_in = usb_ep_get(dev, USB_TOKEN_IN, 2); qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_usbnet_info, &s->conf, - object_get_typename(OBJECT(s)), s->dev.qdev.id, s); + object_get_typename(OBJECT(s)), s->dev.qdev.id, + &s->dev.qdev.mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); snprintf(s->usbstring_mac, sizeof(s->usbstring_mac), "%02x%02x%02x%02x%02x%02x", diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 28164d89be..c0d63e0425 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -278,7 +278,9 @@ typedef struct BulkIn { struct CCIDBus { BusState qbus; }; -typedef struct CCIDBus CCIDBus; + +#define TYPE_CCID_BUS "ccid-bus" +OBJECT_DECLARE_SIMPLE_TYPE(CCIDBus, CCID_BUS) /* * powered - defaults to true, changed by PowerOn/PowerOff messages @@ -1174,9 +1176,6 @@ static Property ccid_props[] = { DEFINE_PROP_END_OF_LIST(), }; -#define TYPE_CCID_BUS "ccid-bus" -OBJECT_DECLARE_SIMPLE_TYPE(CCIDBus, CCID_BUS) - static const TypeInfo ccid_bus_info = { .name = TYPE_CCID_BUS, .parent = TYPE_BUS, @@ -1368,7 +1367,7 @@ static const VMStateDescription bulk_in_vmstate = { .name = "CCID BulkIn state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BUFFER(data, BulkIn), VMSTATE_UINT32(len, BulkIn), VMSTATE_UINT32(pos, BulkIn), @@ -1380,7 +1379,7 @@ static const VMStateDescription answer_vmstate = { .name = "CCID Answer state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(slot, Answer), VMSTATE_UINT8(seq, Answer), VMSTATE_END_OF_LIST() @@ -1391,7 +1390,7 @@ static const VMStateDescription usb_device_vmstate = { .name = "usb_device", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(addr, USBDevice), VMSTATE_BUFFER(setup_buf, USBDevice), VMSTATE_BUFFER(data_buf, USBDevice), @@ -1405,7 +1404,7 @@ static const VMStateDescription ccid_vmstate = { .minimum_version_id = 1, .post_load = ccid_post_load, .pre_save = ccid_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(dev, USBCCIDState, 1, usb_device_vmstate, USBDevice), VMSTATE_UINT8(debug, USBCCIDState), VMSTATE_BUFFER(bulk_out_data, USBCCIDState), diff --git a/hw/usb/dev-storage-bot.c b/hw/usb/dev-storage-bot.c index b24b3148c2..1e5c5c711f 100644 --- a/hw/usb/dev-storage-bot.c +++ b/hw/usb/dev-storage-bot.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include "qemu/typedefs.h" #include "qapi/error.h" #include "hw/usb.h" #include "hw/usb/desc.h" diff --git a/hw/usb/dev-storage-classic.c b/hw/usb/dev-storage-classic.c index 00f25bade2..6147387dc6 100644 --- a/hw/usb/dev-storage-classic.c +++ b/hw/usb/dev-storage-classic.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include "qemu/typedefs.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "hw/usb.h" @@ -39,15 +38,6 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) return; } - if (!blkconf_blocksizes(&s->conf, errp)) { - return; - } - - if (!blkconf_apply_backend_options(&s->conf, !blk_supports_write_perm(blk), - true, errp)) { - return; - } - /* * Hack alert: this pretends to be a block device, but it's really * a SCSI bus that can serve only a single device, which it @@ -68,10 +58,7 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) scsi_bus_init(&s->bus, sizeof(s->bus), DEVICE(dev), &usb_msd_scsi_info_storage); scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable, - s->conf.bootindex, s->conf.share_rw, - s->conf.rerror, s->conf.werror, - dev->serial, - errp); + &s->conf, dev->serial, errp); blk_unref(blk); if (!scsi_dev) { return; diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index dca62d544f..341e505bd0 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -177,6 +177,37 @@ static const USBDesc desc = { .str = desc_strings, }; +static void usb_msd_packet_complete(MSDState *s) +{ + USBPacket *p = s->packet; + + /* + * Set s->packet to NULL before calling usb_packet_complete + * because another request may be issued before + * usb_packet_complete returns. + */ + trace_usb_msd_packet_complete(); + s->packet = NULL; + usb_packet_complete(&s->dev, p); +} + +static void usb_msd_fatal_error(MSDState *s) +{ + trace_usb_msd_fatal_error(); + + if (s->packet) { + s->packet->status = USB_RET_STALL; + usb_msd_packet_complete(s); + } + + /* + * Guest messed up up device state with illegal requests. Go + * ignore any requests until the guests resets the device (and + * brings it into a known state that way). + */ + s->needs_reset = true; +} + static void usb_msd_copy_data(MSDState *s, USBPacket *p) { uint32_t len; @@ -208,24 +239,16 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p) memset(&s->csw, 0, sizeof(s->csw)); } -static void usb_msd_packet_complete(MSDState *s) -{ - USBPacket *p = s->packet; - - /* Set s->packet to NULL before calling usb_packet_complete - because another request may be issued before - usb_packet_complete returns. */ - trace_usb_msd_packet_complete(); - s->packet = NULL; - usb_packet_complete(&s->dev, p); -} - void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) { MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); USBPacket *p = s->packet; - assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV)); + if ((s->mode == USB_MSDM_DATAOUT) != (req->cmd.mode == SCSI_XFER_TO_DEV)) { + usb_msd_fatal_error(s); + return; + } + s->scsi_len = len; s->scsi_off = 0; if (p) { @@ -315,6 +338,8 @@ void usb_msd_handle_reset(USBDevice *dev) memset(&s->csw, 0, sizeof(s->csw)); s->mode = USB_MSDM_CBW; + + s->needs_reset = false; } static void usb_msd_handle_control(USBDevice *dev, USBPacket *p, @@ -378,7 +403,12 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) struct usb_msd_cbw cbw; uint8_t devep = p->ep->nr; SCSIDevice *scsi_dev; - uint32_t len; + int len; + + if (s->needs_reset) { + p->status = USB_RET_STALL; + return; + } switch (p->pid) { case USB_TOKEN_OUT: @@ -415,7 +445,7 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) cbw.cmd_len, s->data_len); assert(le32_to_cpu(s->csw.residue) == 0); s->scsi_len = 0; - s->req = scsi_req_new(scsi_dev, tag, cbw.lun, cbw.cmd, NULL); + s->req = scsi_req_new(scsi_dev, tag, cbw.lun, cbw.cmd, cbw.cmd_len, NULL); if (s->commandlog) { scsi_req_print(s->req); } @@ -435,7 +465,7 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) usb_msd_copy_data(s, p); } if (le32_to_cpu(s->csw.residue)) { - int len = p->iov.size - p->actual_length; + len = p->iov.size - p->actual_length; if (len) { usb_packet_skip(p, len); if (len > s->data_len) { @@ -496,7 +526,7 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) usb_msd_copy_data(s, p); } if (le32_to_cpu(s->csw.residue)) { - int len = p->iov.size - p->actual_length; + len = p->iov.size - p->actual_length; if (len) { usb_packet_skip(p, len); if (len > s->data_len) { @@ -542,7 +572,7 @@ static const VMStateDescription vmstate_usb_msd = { .name = "usb-storage", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_USB_DEVICE(dev, MSDState), VMSTATE_UINT32(mode, MSDState), VMSTATE_UINT32(scsi_len, MSDState), diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index c9f295e7e4..1804cb6799 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -71,7 +71,7 @@ typedef struct { uint8_t reserved_2; uint64_t lun; uint8_t cdb[16]; - uint8_t add_cdb[1]; /* not supported by QEMU */ + uint8_t add_cdb[1]; } QEMU_PACKED uas_iu_command; typedef struct { @@ -699,6 +699,7 @@ static void usb_uas_command(UASDevice *uas, uas_iu *iu) UASRequest *req; uint32_t len; uint16_t tag = be16_to_cpu(iu->hdr.tag); + size_t cdb_len = sizeof(iu->command.cdb) + iu->command.add_cdb_length; if (iu->command.add_cdb_length > 0) { qemu_log_mask(LOG_UNIMP, "additional adb length not yet supported\n"); @@ -729,7 +730,7 @@ static void usb_uas_command(UASDevice *uas, uas_iu *iu) req->req = scsi_req_new(req->dev, req->tag, usb_uas_get_lun(req->lun), - iu->command.cdb, req); + iu->command.cdb, cdb_len, req); if (uas->requestlog) { scsi_req_print(req->req); } @@ -790,7 +791,7 @@ static void usb_uas_task(UASDevice *uas, uas_iu *iu) case UAS_TMF_LOGICAL_UNIT_RESET: trace_usb_uas_tmf_logical_unit_reset(uas->dev.addr, tag, lun); - qdev_reset_all(&dev->qdev); + device_cold_reset(&dev->qdev); usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE); break; @@ -936,7 +937,8 @@ static void usb_uas_realize(USBDevice *dev, Error **errp) QTAILQ_INIT(&uas->results); QTAILQ_INIT(&uas->requests); - uas->status_bh = qemu_bh_new(usb_uas_send_status_bh, uas); + uas->status_bh = qemu_bh_new_guarded(usb_uas_send_status_bh, uas, + &d->mem_reentrancy_guard); dev->flags |= (1 << USB_DEV_FLAG_IS_SCSI_STORAGE); scsi_bus_init(&uas->bus, sizeof(uas->bus), DEVICE(dev), &usb_uas_scsi_info); @@ -945,7 +947,7 @@ static void usb_uas_realize(USBDevice *dev, Error **errp) static const VMStateDescription vmstate_usb_uas = { .name = "usb-uas", .unmigratable = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_USB_DEVICE(dev, UASDevice), VMSTATE_END_OF_LIST() } diff --git a/hw/usb/dev-wacom.c b/hw/usb/dev-wacom.c index 8323650c6a..7177c17f03 100644 --- a/hw/usb/dev-wacom.c +++ b/hw/usb/dev-wacom.c @@ -36,8 +36,8 @@ #include "qom/object.h" /* Interface requests */ -#define WACOM_GET_REPORT 0x2101 -#define WACOM_SET_REPORT 0x2109 +#define WACOM_GET_REPORT 0x2101 +#define WACOM_SET_REPORT 0x2109 struct USBWacomState { USBDevice dev; diff --git a/hw/usb/hcd-dwc2.c b/hw/usb/hcd-dwc2.c index 8755e9cbb0..222eef82a5 100644 --- a/hw/usb/hcd-dwc2.c +++ b/hw/usb/hcd-dwc2.c @@ -1364,7 +1364,8 @@ static void dwc2_realize(DeviceState *dev, Error **errp) s->fi = USB_FRMINTVL - 1; s->eof_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, dwc2_frame_boundary, s); s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, dwc2_work_timer, s); - s->async_bh = qemu_bh_new(dwc2_work_bh, s); + s->async_bh = qemu_bh_new_guarded(dwc2_work_bh, s, + &dev->mem_reentrancy_guard); sysbus_init_irq(sbd, &s->irq); } @@ -1390,7 +1391,7 @@ static const VMStateDescription vmstate_dwc2_state_packet = { .name = "dwc2/packet", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(devadr, DWC2Packet), VMSTATE_UINT32(epnum, DWC2Packet), VMSTATE_UINT32(epdir, DWC2Packet), @@ -1410,7 +1411,7 @@ const VMStateDescription vmstate_dwc2_state = { .name = "dwc2", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(glbreg, DWC2State, DWC2_GLBREG_SIZE / sizeof(uint32_t)), VMSTATE_UINT32_ARRAY(fszreg, DWC2State, diff --git a/hw/usb/hcd-dwc3.c b/hw/usb/hcd-dwc3.c index 279263489e..09d8e25b97 100644 --- a/hw/usb/hcd-dwc3.c +++ b/hw/usb/hcd-dwc3.c @@ -648,7 +648,7 @@ static void usb_dwc3_init(Object *obj) static const VMStateDescription vmstate_usb_dwc3 = { .name = "usb-dwc3", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, USBDWC3, USB_DWC3_R_MAX), VMSTATE_UINT8(cfg.mode, USBDWC3), VMSTATE_UINT32(cfg.dwc_usb3_user, USBDWC3), diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 4c37c8e227..3ff54edf62 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -74,7 +74,7 @@ static void usb_ehci_pci_realize(PCIDevice *dev, Error **errp) static void usb_ehci_pci_init(Object *obj) { - DeviceClass *dc = OBJECT_GET_CLASS(DeviceClass, obj, TYPE_DEVICE); + DeviceClass *dc = DEVICE_GET_CLASS(obj); EHCIPCIState *i = PCI_EHCI(obj); EHCIState *s = &i->ehci; @@ -83,7 +83,7 @@ static void usb_ehci_pci_init(Object *obj) s->capsbase = 0x00; s->opregbase = 0x20; s->portscbase = 0x44; - s->portnr = NB_PORTS; + s->portnr = EHCI_PORTS; if (!dc->hotpluggable) { s->companion_enable = true; @@ -144,7 +144,7 @@ static const VMStateDescription vmstate_ehci_pci = { .name = "ehci", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(pcidev, EHCIPCIState), VMSTATE_STRUCT(ehci, EHCIPCIState, 2, vmstate_ehci, EHCIState), VMSTATE_END_OF_LIST() diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index a12e218848..fe1dabd0bb 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -25,7 +25,7 @@ static const VMStateDescription vmstate_ehci_sysbus = { .name = "ehci-sysbus", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(ehci, EHCISysBusState, 2, vmstate_ehci, EHCIState), VMSTATE_END_OF_LIST() } @@ -88,7 +88,7 @@ static void ehci_sysbus_class_init(ObjectClass *klass, void *data) SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(klass); sec->portscbase = 0x44; - sec->portnr = NB_PORTS; + sec->portnr = EHCI_PORTS; dc->realize = usb_ehci_sysbus_realize; dc->vmsd = &vmstate_ehci_sysbus; diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 33a8a377bd..01864d4649 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -783,9 +783,9 @@ static void ehci_register_companion(USBBus *bus, USBPort *ports[], EHCIState *s = container_of(bus, EHCIState, bus); uint32_t i; - if (firstport + portcount > NB_PORTS) { + if (firstport + portcount > EHCI_PORTS) { error_setg(errp, "firstport must be between 0 and %u", - NB_PORTS - portcount); + EHCI_PORTS - portcount); return; } @@ -831,7 +831,7 @@ static USBDevice *ehci_find_device(EHCIState *ehci, uint8_t addr) USBPort *port; int i; - for (i = 0; i < NB_PORTS; i++) { + for (i = 0; i < EHCI_PORTS; i++) { port = &ehci->ports[i]; if (!(ehci->portsc[i] & PORTSC_PED)) { DPRINTF("Port %d not enabled\n", i); @@ -850,7 +850,7 @@ void ehci_reset(void *opaque) { EHCIState *s = opaque; int i; - USBDevice *devs[NB_PORTS]; + USBDevice *devs[EHCI_PORTS]; trace_usb_ehci_reset(); @@ -858,7 +858,7 @@ void ehci_reset(void *opaque) * Do the detach before touching portsc, so that it correctly gets send to * us or to our companion based on PORTSC_POWNER before the reset. */ - for(i = 0; i < NB_PORTS; i++) { + for(i = 0; i < EHCI_PORTS; i++) { devs[i] = s->ports[i].dev; if (devs[i] && devs[i]->attached) { usb_detach(&s->ports[i]); @@ -877,7 +877,7 @@ void ehci_reset(void *opaque) s->astate = EST_INACTIVE; s->pstate = EST_INACTIVE; - for(i = 0; i < NB_PORTS; i++) { + for(i = 0; i < EHCI_PORTS; i++) { if (s->companion_ports[i]) { s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER; } else { @@ -1086,8 +1086,9 @@ static void ehci_opreg_write(void *ptr, hwaddr addr, case CONFIGFLAG: val &= 0x1; if (val) { - for(i = 0; i < NB_PORTS; i++) + for (i = 0; i < EHCI_PORTS; i++) { handle_port_owner_write(s, i, 0); + } } break; @@ -1464,7 +1465,7 @@ static int ehci_process_itd(EHCIState *ehci, usb_handle_packet(dev, &ehci->ipacket); usb_packet_unmap(&ehci->ipacket, &ehci->isgl); } else { - DPRINTF("ISOCH: attempt to addess non-iso endpoint\n"); + DPRINTF("ISOCH: attempt to address non-iso endpoint\n"); ehci->ipacket.status = USB_RET_NAK; ehci->ipacket.actual_length = 0; } @@ -1513,7 +1514,7 @@ static int ehci_process_itd(EHCIState *ehci, /* This state is the entry point for asynchronous schedule - * processing. Entry here consitutes a EHCI start event state (4.8.5) + * processing. Entry here constitutes a EHCI start event state (4.8.5) */ static int ehci_state_waitlisthead(EHCIState *ehci, int async) { @@ -2011,7 +2012,10 @@ static int ehci_state_writeback(EHCIQueue *q) ehci_trace_qtd(q, NLPTR_GET(p->qtdaddr), (EHCIqtd *) &q->qh.next_qtd); qtd = (uint32_t *) &q->qh.next_qtd; addr = NLPTR_GET(p->qtdaddr); - put_dwords(q->ehci, addr + 2 * sizeof(uint32_t), qtd + 2, 2); + /* First write back the offset */ + put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qtd + 3, 1); + /* Then write back the token, clearing the 'active' bit */ + put_dwords(q->ehci, addr + 2 * sizeof(uint32_t), qtd + 2, 1); ehci_free_packet(p); /* @@ -2423,7 +2427,7 @@ static int usb_ehci_post_load(void *opaque, int version_id) EHCIState *s = opaque; int i; - for (i = 0; i < NB_PORTS; i++) { + for (i = 0; i < EHCI_PORTS; i++) { USBPort *companion = s->companion_ports[i]; if (companion == NULL) { continue; @@ -2448,14 +2452,14 @@ static void usb_ehci_vm_state_change(void *opaque, bool running, RunState state) * USB-devices which have async handled packages have a packet in the * ep queue to match the completion with. */ - if (state == RUN_STATE_RUNNING) { + if (running) { ehci_advance_async_state(ehci); } /* * The schedule rebuilt from guest memory could cause the migration dest * to miss a QH unlink, and fail to cancel packets, since the unlinked QH - * will never have existed on the destination. Therefor we must flush the + * will never have existed on the destination. Therefore we must flush the * async schedule on savevm to catch any not yet noticed unlinks. */ if (state == RUN_STATE_SAVE_VM) { @@ -2470,7 +2474,7 @@ const VMStateDescription vmstate_ehci = { .minimum_version_id = 1, .pre_save = usb_ehci_pre_save, .post_load = usb_ehci_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { /* mmio registers */ VMSTATE_UINT32(usbcmd, EHCIState), VMSTATE_UINT32(usbsts, EHCIState), @@ -2505,9 +2509,9 @@ void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp) { int i; - if (s->portnr > NB_PORTS) { + if (s->portnr > EHCI_PORTS) { error_setg(errp, "Too many ports! Max. port number is %d.", - NB_PORTS); + EHCI_PORTS); return; } if (s->maxframes < 8 || s->maxframes > 512) { @@ -2530,7 +2534,8 @@ void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp) } s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ehci_work_timer, s); - s->async_bh = qemu_bh_new(ehci_work_bh, s); + s->async_bh = qemu_bh_new_guarded(ehci_work_bh, s, + &dev->mem_reentrancy_guard); s->device = dev; s->vmstate = qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s); diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index a173707d9b..56a1c09d1f 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -21,9 +21,8 @@ #include "qemu/timer.h" #include "hw/usb.h" #include "sysemu/dma.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/sysbus.h" -#include "qom/object.h" #ifndef EHCI_DEBUG #define EHCI_DEBUG 0 @@ -38,7 +37,7 @@ #define MMIO_SIZE 0x1000 #define CAPA_SIZE 0x10 -#define NB_PORTS 6 /* Max. Number of downstream ports */ +#define EHCI_PORTS 6 /* Max. Number of downstream ports */ typedef struct EHCIPacket EHCIPacket; typedef struct EHCIQueue EHCIQueue; @@ -289,7 +288,7 @@ struct EHCIState { uint32_t configflag; }; }; - uint32_t portsc[NB_PORTS]; + uint32_t portsc[EHCI_PORTS]; /* * Internal states, shadow registers, etc @@ -299,8 +298,8 @@ struct EHCIState { bool working; uint32_t astate; /* Current state in asynchronous schedule */ uint32_t pstate; /* Current state in periodic schedule */ - USBPort ports[NB_PORTS]; - USBPort *companion_ports[NB_PORTS]; + USBPort ports[EHCI_PORTS]; + USBPort *companion_ports[EHCI_PORTS]; uint32_t usbsts_pending; uint32_t usbsts_frindex; EHCIQueueHead aqueues; diff --git a/hw/usb/hcd-musb.c b/hw/usb/hcd-musb.c index 85f5ff5bd4..6dca373cb1 100644 --- a/hw/usb/hcd-musb.c +++ b/hw/usb/hcd-musb.c @@ -28,227 +28,227 @@ #include "hw/hw.h" /* Common USB registers */ -#define MUSB_HDRC_FADDR 0x00 /* 8-bit */ -#define MUSB_HDRC_POWER 0x01 /* 8-bit */ +#define MUSB_HDRC_FADDR 0x00 /* 8-bit */ +#define MUSB_HDRC_POWER 0x01 /* 8-bit */ -#define MUSB_HDRC_INTRTX 0x02 /* 16-bit */ -#define MUSB_HDRC_INTRRX 0x04 -#define MUSB_HDRC_INTRTXE 0x06 -#define MUSB_HDRC_INTRRXE 0x08 -#define MUSB_HDRC_INTRUSB 0x0a /* 8 bit */ -#define MUSB_HDRC_INTRUSBE 0x0b /* 8 bit */ -#define MUSB_HDRC_FRAME 0x0c /* 16-bit */ -#define MUSB_HDRC_INDEX 0x0e /* 8 bit */ -#define MUSB_HDRC_TESTMODE 0x0f /* 8 bit */ +#define MUSB_HDRC_INTRTX 0x02 /* 16-bit */ +#define MUSB_HDRC_INTRRX 0x04 +#define MUSB_HDRC_INTRTXE 0x06 +#define MUSB_HDRC_INTRRXE 0x08 +#define MUSB_HDRC_INTRUSB 0x0a /* 8 bit */ +#define MUSB_HDRC_INTRUSBE 0x0b /* 8 bit */ +#define MUSB_HDRC_FRAME 0x0c /* 16-bit */ +#define MUSB_HDRC_INDEX 0x0e /* 8 bit */ +#define MUSB_HDRC_TESTMODE 0x0f /* 8 bit */ /* Per-EP registers in indexed mode */ -#define MUSB_HDRC_EP_IDX 0x10 /* 8-bit */ +#define MUSB_HDRC_EP_IDX 0x10 /* 8-bit */ /* EP FIFOs */ -#define MUSB_HDRC_FIFO 0x20 +#define MUSB_HDRC_FIFO 0x20 /* Additional Control Registers */ -#define MUSB_HDRC_DEVCTL 0x60 /* 8 bit */ +#define MUSB_HDRC_DEVCTL 0x60 /* 8 bit */ /* These are indexed */ -#define MUSB_HDRC_TXFIFOSZ 0x62 /* 8 bit (see masks) */ -#define MUSB_HDRC_RXFIFOSZ 0x63 /* 8 bit (see masks) */ -#define MUSB_HDRC_TXFIFOADDR 0x64 /* 16 bit offset shifted right 3 */ -#define MUSB_HDRC_RXFIFOADDR 0x66 /* 16 bit offset shifted right 3 */ +#define MUSB_HDRC_TXFIFOSZ 0x62 /* 8 bit (see masks) */ +#define MUSB_HDRC_RXFIFOSZ 0x63 /* 8 bit (see masks) */ +#define MUSB_HDRC_TXFIFOADDR 0x64 /* 16 bit offset shifted right 3 */ +#define MUSB_HDRC_RXFIFOADDR 0x66 /* 16 bit offset shifted right 3 */ /* Some more registers */ -#define MUSB_HDRC_VCTRL 0x68 /* 8 bit */ -#define MUSB_HDRC_HWVERS 0x6c /* 8 bit */ +#define MUSB_HDRC_VCTRL 0x68 /* 8 bit */ +#define MUSB_HDRC_HWVERS 0x6c /* 8 bit */ /* Added in HDRC 1.9(?) & MHDRC 1.4 */ /* ULPI pass-through */ -#define MUSB_HDRC_ULPI_VBUSCTL 0x70 -#define MUSB_HDRC_ULPI_REGDATA 0x74 -#define MUSB_HDRC_ULPI_REGADDR 0x75 -#define MUSB_HDRC_ULPI_REGCTL 0x76 +#define MUSB_HDRC_ULPI_VBUSCTL 0x70 +#define MUSB_HDRC_ULPI_REGDATA 0x74 +#define MUSB_HDRC_ULPI_REGADDR 0x75 +#define MUSB_HDRC_ULPI_REGCTL 0x76 /* Extended config & PHY control */ -#define MUSB_HDRC_ENDCOUNT 0x78 /* 8 bit */ -#define MUSB_HDRC_DMARAMCFG 0x79 /* 8 bit */ -#define MUSB_HDRC_PHYWAIT 0x7a /* 8 bit */ -#define MUSB_HDRC_PHYVPLEN 0x7b /* 8 bit */ -#define MUSB_HDRC_HS_EOF1 0x7c /* 8 bit, units of 546.1 us */ -#define MUSB_HDRC_FS_EOF1 0x7d /* 8 bit, units of 533.3 ns */ -#define MUSB_HDRC_LS_EOF1 0x7e /* 8 bit, units of 1.067 us */ +#define MUSB_HDRC_ENDCOUNT 0x78 /* 8 bit */ +#define MUSB_HDRC_DMARAMCFG 0x79 /* 8 bit */ +#define MUSB_HDRC_PHYWAIT 0x7a /* 8 bit */ +#define MUSB_HDRC_PHYVPLEN 0x7b /* 8 bit */ +#define MUSB_HDRC_HS_EOF1 0x7c /* 8 bit, units of 546.1 us */ +#define MUSB_HDRC_FS_EOF1 0x7d /* 8 bit, units of 533.3 ns */ +#define MUSB_HDRC_LS_EOF1 0x7e /* 8 bit, units of 1.067 us */ /* Per-EP BUSCTL registers */ -#define MUSB_HDRC_BUSCTL 0x80 +#define MUSB_HDRC_BUSCTL 0x80 /* Per-EP registers in flat mode */ -#define MUSB_HDRC_EP 0x100 +#define MUSB_HDRC_EP 0x100 /* offsets to registers in flat model */ -#define MUSB_HDRC_TXMAXP 0x00 /* 16 bit apparently */ -#define MUSB_HDRC_TXCSR 0x02 /* 16 bit apparently */ -#define MUSB_HDRC_CSR0 MUSB_HDRC_TXCSR /* re-used for EP0 */ -#define MUSB_HDRC_RXMAXP 0x04 /* 16 bit apparently */ -#define MUSB_HDRC_RXCSR 0x06 /* 16 bit apparently */ -#define MUSB_HDRC_RXCOUNT 0x08 /* 16 bit apparently */ -#define MUSB_HDRC_COUNT0 MUSB_HDRC_RXCOUNT /* re-used for EP0 */ -#define MUSB_HDRC_TXTYPE 0x0a /* 8 bit apparently */ -#define MUSB_HDRC_TYPE0 MUSB_HDRC_TXTYPE /* re-used for EP0 */ -#define MUSB_HDRC_TXINTERVAL 0x0b /* 8 bit apparently */ -#define MUSB_HDRC_NAKLIMIT0 MUSB_HDRC_TXINTERVAL /* re-used for EP0 */ -#define MUSB_HDRC_RXTYPE 0x0c /* 8 bit apparently */ -#define MUSB_HDRC_RXINTERVAL 0x0d /* 8 bit apparently */ -#define MUSB_HDRC_FIFOSIZE 0x0f /* 8 bit apparently */ -#define MUSB_HDRC_CONFIGDATA MGC_O_HDRC_FIFOSIZE /* re-used for EP0 */ +#define MUSB_HDRC_TXMAXP 0x00 /* 16 bit apparently */ +#define MUSB_HDRC_TXCSR 0x02 /* 16 bit apparently */ +#define MUSB_HDRC_CSR0 MUSB_HDRC_TXCSR /* re-used for EP0 */ +#define MUSB_HDRC_RXMAXP 0x04 /* 16 bit apparently */ +#define MUSB_HDRC_RXCSR 0x06 /* 16 bit apparently */ +#define MUSB_HDRC_RXCOUNT 0x08 /* 16 bit apparently */ +#define MUSB_HDRC_COUNT0 MUSB_HDRC_RXCOUNT /* re-used for EP0 */ +#define MUSB_HDRC_TXTYPE 0x0a /* 8 bit apparently */ +#define MUSB_HDRC_TYPE0 MUSB_HDRC_TXTYPE /* re-used for EP0 */ +#define MUSB_HDRC_TXINTERVAL 0x0b /* 8 bit apparently */ +#define MUSB_HDRC_NAKLIMIT0 MUSB_HDRC_TXINTERVAL /* re-used for EP0 */ +#define MUSB_HDRC_RXTYPE 0x0c /* 8 bit apparently */ +#define MUSB_HDRC_RXINTERVAL 0x0d /* 8 bit apparently */ +#define MUSB_HDRC_FIFOSIZE 0x0f /* 8 bit apparently */ +#define MUSB_HDRC_CONFIGDATA MGC_O_HDRC_FIFOSIZE /* re-used for EP0 */ /* "Bus control" registers */ -#define MUSB_HDRC_TXFUNCADDR 0x00 -#define MUSB_HDRC_TXHUBADDR 0x02 -#define MUSB_HDRC_TXHUBPORT 0x03 +#define MUSB_HDRC_TXFUNCADDR 0x00 +#define MUSB_HDRC_TXHUBADDR 0x02 +#define MUSB_HDRC_TXHUBPORT 0x03 -#define MUSB_HDRC_RXFUNCADDR 0x04 -#define MUSB_HDRC_RXHUBADDR 0x06 -#define MUSB_HDRC_RXHUBPORT 0x07 +#define MUSB_HDRC_RXFUNCADDR 0x04 +#define MUSB_HDRC_RXHUBADDR 0x06 +#define MUSB_HDRC_RXHUBPORT 0x07 /* * MUSBHDRC Register bit masks */ /* POWER */ -#define MGC_M_POWER_ISOUPDATE 0x80 -#define MGC_M_POWER_SOFTCONN 0x40 -#define MGC_M_POWER_HSENAB 0x20 -#define MGC_M_POWER_HSMODE 0x10 -#define MGC_M_POWER_RESET 0x08 -#define MGC_M_POWER_RESUME 0x04 -#define MGC_M_POWER_SUSPENDM 0x02 -#define MGC_M_POWER_ENSUSPEND 0x01 +#define MGC_M_POWER_ISOUPDATE 0x80 +#define MGC_M_POWER_SOFTCONN 0x40 +#define MGC_M_POWER_HSENAB 0x20 +#define MGC_M_POWER_HSMODE 0x10 +#define MGC_M_POWER_RESET 0x08 +#define MGC_M_POWER_RESUME 0x04 +#define MGC_M_POWER_SUSPENDM 0x02 +#define MGC_M_POWER_ENSUSPEND 0x01 /* INTRUSB */ -#define MGC_M_INTR_SUSPEND 0x01 -#define MGC_M_INTR_RESUME 0x02 -#define MGC_M_INTR_RESET 0x04 -#define MGC_M_INTR_BABBLE 0x04 -#define MGC_M_INTR_SOF 0x08 -#define MGC_M_INTR_CONNECT 0x10 -#define MGC_M_INTR_DISCONNECT 0x20 -#define MGC_M_INTR_SESSREQ 0x40 -#define MGC_M_INTR_VBUSERROR 0x80 /* FOR SESSION END */ -#define MGC_M_INTR_EP0 0x01 /* FOR EP0 INTERRUPT */ +#define MGC_M_INTR_SUSPEND 0x01 +#define MGC_M_INTR_RESUME 0x02 +#define MGC_M_INTR_RESET 0x04 +#define MGC_M_INTR_BABBLE 0x04 +#define MGC_M_INTR_SOF 0x08 +#define MGC_M_INTR_CONNECT 0x10 +#define MGC_M_INTR_DISCONNECT 0x20 +#define MGC_M_INTR_SESSREQ 0x40 +#define MGC_M_INTR_VBUSERROR 0x80 /* FOR SESSION END */ +#define MGC_M_INTR_EP0 0x01 /* FOR EP0 INTERRUPT */ /* DEVCTL */ -#define MGC_M_DEVCTL_BDEVICE 0x80 -#define MGC_M_DEVCTL_FSDEV 0x40 -#define MGC_M_DEVCTL_LSDEV 0x20 -#define MGC_M_DEVCTL_VBUS 0x18 -#define MGC_S_DEVCTL_VBUS 3 -#define MGC_M_DEVCTL_HM 0x04 -#define MGC_M_DEVCTL_HR 0x02 -#define MGC_M_DEVCTL_SESSION 0x01 +#define MGC_M_DEVCTL_BDEVICE 0x80 +#define MGC_M_DEVCTL_FSDEV 0x40 +#define MGC_M_DEVCTL_LSDEV 0x20 +#define MGC_M_DEVCTL_VBUS 0x18 +#define MGC_S_DEVCTL_VBUS 3 +#define MGC_M_DEVCTL_HM 0x04 +#define MGC_M_DEVCTL_HR 0x02 +#define MGC_M_DEVCTL_SESSION 0x01 /* TESTMODE */ -#define MGC_M_TEST_FORCE_HOST 0x80 -#define MGC_M_TEST_FIFO_ACCESS 0x40 -#define MGC_M_TEST_FORCE_FS 0x20 -#define MGC_M_TEST_FORCE_HS 0x10 -#define MGC_M_TEST_PACKET 0x08 -#define MGC_M_TEST_K 0x04 -#define MGC_M_TEST_J 0x02 -#define MGC_M_TEST_SE0_NAK 0x01 +#define MGC_M_TEST_FORCE_HOST 0x80 +#define MGC_M_TEST_FIFO_ACCESS 0x40 +#define MGC_M_TEST_FORCE_FS 0x20 +#define MGC_M_TEST_FORCE_HS 0x10 +#define MGC_M_TEST_PACKET 0x08 +#define MGC_M_TEST_K 0x04 +#define MGC_M_TEST_J 0x02 +#define MGC_M_TEST_SE0_NAK 0x01 /* CSR0 */ -#define MGC_M_CSR0_FLUSHFIFO 0x0100 -#define MGC_M_CSR0_TXPKTRDY 0x0002 -#define MGC_M_CSR0_RXPKTRDY 0x0001 +#define MGC_M_CSR0_FLUSHFIFO 0x0100 +#define MGC_M_CSR0_TXPKTRDY 0x0002 +#define MGC_M_CSR0_RXPKTRDY 0x0001 /* CSR0 in Peripheral mode */ -#define MGC_M_CSR0_P_SVDSETUPEND 0x0080 -#define MGC_M_CSR0_P_SVDRXPKTRDY 0x0040 -#define MGC_M_CSR0_P_SENDSTALL 0x0020 -#define MGC_M_CSR0_P_SETUPEND 0x0010 -#define MGC_M_CSR0_P_DATAEND 0x0008 -#define MGC_M_CSR0_P_SENTSTALL 0x0004 +#define MGC_M_CSR0_P_SVDSETUPEND 0x0080 +#define MGC_M_CSR0_P_SVDRXPKTRDY 0x0040 +#define MGC_M_CSR0_P_SENDSTALL 0x0020 +#define MGC_M_CSR0_P_SETUPEND 0x0010 +#define MGC_M_CSR0_P_DATAEND 0x0008 +#define MGC_M_CSR0_P_SENTSTALL 0x0004 /* CSR0 in Host mode */ -#define MGC_M_CSR0_H_NO_PING 0x0800 -#define MGC_M_CSR0_H_WR_DATATOGGLE 0x0400 /* set to allow setting: */ -#define MGC_M_CSR0_H_DATATOGGLE 0x0200 /* data toggle control */ -#define MGC_M_CSR0_H_NAKTIMEOUT 0x0080 -#define MGC_M_CSR0_H_STATUSPKT 0x0040 -#define MGC_M_CSR0_H_REQPKT 0x0020 -#define MGC_M_CSR0_H_ERROR 0x0010 -#define MGC_M_CSR0_H_SETUPPKT 0x0008 -#define MGC_M_CSR0_H_RXSTALL 0x0004 +#define MGC_M_CSR0_H_NO_PING 0x0800 +#define MGC_M_CSR0_H_WR_DATATOGGLE 0x0400 /* set to allow setting: */ +#define MGC_M_CSR0_H_DATATOGGLE 0x0200 /* data toggle control */ +#define MGC_M_CSR0_H_NAKTIMEOUT 0x0080 +#define MGC_M_CSR0_H_STATUSPKT 0x0040 +#define MGC_M_CSR0_H_REQPKT 0x0020 +#define MGC_M_CSR0_H_ERROR 0x0010 +#define MGC_M_CSR0_H_SETUPPKT 0x0008 +#define MGC_M_CSR0_H_RXSTALL 0x0004 /* CONFIGDATA */ -#define MGC_M_CONFIGDATA_MPRXE 0x80 /* auto bulk pkt combining */ -#define MGC_M_CONFIGDATA_MPTXE 0x40 /* auto bulk pkt splitting */ -#define MGC_M_CONFIGDATA_BIGENDIAN 0x20 -#define MGC_M_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */ -#define MGC_M_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */ -#define MGC_M_CONFIGDATA_DYNFIFO 0x04 /* dynamic FIFO sizing */ -#define MGC_M_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */ -#define MGC_M_CONFIGDATA_UTMIDW 0x01 /* Width, 0 => 8b, 1 => 16b */ +#define MGC_M_CONFIGDATA_MPRXE 0x80 /* auto bulk pkt combining */ +#define MGC_M_CONFIGDATA_MPTXE 0x40 /* auto bulk pkt splitting */ +#define MGC_M_CONFIGDATA_BIGENDIAN 0x20 +#define MGC_M_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */ +#define MGC_M_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */ +#define MGC_M_CONFIGDATA_DYNFIFO 0x04 /* dynamic FIFO sizing */ +#define MGC_M_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */ +#define MGC_M_CONFIGDATA_UTMIDW 0x01 /* Width, 0 => 8b, 1 => 16b */ /* TXCSR in Peripheral and Host mode */ -#define MGC_M_TXCSR_AUTOSET 0x8000 -#define MGC_M_TXCSR_ISO 0x4000 -#define MGC_M_TXCSR_MODE 0x2000 -#define MGC_M_TXCSR_DMAENAB 0x1000 -#define MGC_M_TXCSR_FRCDATATOG 0x0800 -#define MGC_M_TXCSR_DMAMODE 0x0400 -#define MGC_M_TXCSR_CLRDATATOG 0x0040 -#define MGC_M_TXCSR_FLUSHFIFO 0x0008 -#define MGC_M_TXCSR_FIFONOTEMPTY 0x0002 -#define MGC_M_TXCSR_TXPKTRDY 0x0001 +#define MGC_M_TXCSR_AUTOSET 0x8000 +#define MGC_M_TXCSR_ISO 0x4000 +#define MGC_M_TXCSR_MODE 0x2000 +#define MGC_M_TXCSR_DMAENAB 0x1000 +#define MGC_M_TXCSR_FRCDATATOG 0x0800 +#define MGC_M_TXCSR_DMAMODE 0x0400 +#define MGC_M_TXCSR_CLRDATATOG 0x0040 +#define MGC_M_TXCSR_FLUSHFIFO 0x0008 +#define MGC_M_TXCSR_FIFONOTEMPTY 0x0002 +#define MGC_M_TXCSR_TXPKTRDY 0x0001 /* TXCSR in Peripheral mode */ -#define MGC_M_TXCSR_P_INCOMPTX 0x0080 -#define MGC_M_TXCSR_P_SENTSTALL 0x0020 -#define MGC_M_TXCSR_P_SENDSTALL 0x0010 -#define MGC_M_TXCSR_P_UNDERRUN 0x0004 +#define MGC_M_TXCSR_P_INCOMPTX 0x0080 +#define MGC_M_TXCSR_P_SENTSTALL 0x0020 +#define MGC_M_TXCSR_P_SENDSTALL 0x0010 +#define MGC_M_TXCSR_P_UNDERRUN 0x0004 /* TXCSR in Host mode */ -#define MGC_M_TXCSR_H_WR_DATATOGGLE 0x0200 -#define MGC_M_TXCSR_H_DATATOGGLE 0x0100 -#define MGC_M_TXCSR_H_NAKTIMEOUT 0x0080 -#define MGC_M_TXCSR_H_RXSTALL 0x0020 -#define MGC_M_TXCSR_H_ERROR 0x0004 +#define MGC_M_TXCSR_H_WR_DATATOGGLE 0x0200 +#define MGC_M_TXCSR_H_DATATOGGLE 0x0100 +#define MGC_M_TXCSR_H_NAKTIMEOUT 0x0080 +#define MGC_M_TXCSR_H_RXSTALL 0x0020 +#define MGC_M_TXCSR_H_ERROR 0x0004 /* RXCSR in Peripheral and Host mode */ -#define MGC_M_RXCSR_AUTOCLEAR 0x8000 -#define MGC_M_RXCSR_DMAENAB 0x2000 -#define MGC_M_RXCSR_DISNYET 0x1000 -#define MGC_M_RXCSR_DMAMODE 0x0800 -#define MGC_M_RXCSR_INCOMPRX 0x0100 -#define MGC_M_RXCSR_CLRDATATOG 0x0080 -#define MGC_M_RXCSR_FLUSHFIFO 0x0010 -#define MGC_M_RXCSR_DATAERROR 0x0008 -#define MGC_M_RXCSR_FIFOFULL 0x0002 -#define MGC_M_RXCSR_RXPKTRDY 0x0001 +#define MGC_M_RXCSR_AUTOCLEAR 0x8000 +#define MGC_M_RXCSR_DMAENAB 0x2000 +#define MGC_M_RXCSR_DISNYET 0x1000 +#define MGC_M_RXCSR_DMAMODE 0x0800 +#define MGC_M_RXCSR_INCOMPRX 0x0100 +#define MGC_M_RXCSR_CLRDATATOG 0x0080 +#define MGC_M_RXCSR_FLUSHFIFO 0x0010 +#define MGC_M_RXCSR_DATAERROR 0x0008 +#define MGC_M_RXCSR_FIFOFULL 0x0002 +#define MGC_M_RXCSR_RXPKTRDY 0x0001 /* RXCSR in Peripheral mode */ -#define MGC_M_RXCSR_P_ISO 0x4000 -#define MGC_M_RXCSR_P_SENTSTALL 0x0040 -#define MGC_M_RXCSR_P_SENDSTALL 0x0020 -#define MGC_M_RXCSR_P_OVERRUN 0x0004 +#define MGC_M_RXCSR_P_ISO 0x4000 +#define MGC_M_RXCSR_P_SENTSTALL 0x0040 +#define MGC_M_RXCSR_P_SENDSTALL 0x0020 +#define MGC_M_RXCSR_P_OVERRUN 0x0004 /* RXCSR in Host mode */ -#define MGC_M_RXCSR_H_AUTOREQ 0x4000 -#define MGC_M_RXCSR_H_WR_DATATOGGLE 0x0400 -#define MGC_M_RXCSR_H_DATATOGGLE 0x0200 -#define MGC_M_RXCSR_H_RXSTALL 0x0040 -#define MGC_M_RXCSR_H_REQPKT 0x0020 -#define MGC_M_RXCSR_H_ERROR 0x0004 +#define MGC_M_RXCSR_H_AUTOREQ 0x4000 +#define MGC_M_RXCSR_H_WR_DATATOGGLE 0x0400 +#define MGC_M_RXCSR_H_DATATOGGLE 0x0200 +#define MGC_M_RXCSR_H_RXSTALL 0x0040 +#define MGC_M_RXCSR_H_REQPKT 0x0020 +#define MGC_M_RXCSR_H_ERROR 0x0004 /* HUBADDR */ -#define MGC_M_HUBADDR_MULTI_TT 0x80 +#define MGC_M_HUBADDR_MULTI_TT 0x80 /* ULPI: Added in HDRC 1.9(?) & MHDRC 1.4 */ -#define MGC_M_ULPI_VBCTL_USEEXTVBUSIND 0x02 -#define MGC_M_ULPI_VBCTL_USEEXTVBUS 0x01 -#define MGC_M_ULPI_REGCTL_INT_ENABLE 0x08 -#define MGC_M_ULPI_REGCTL_READNOTWRITE 0x04 -#define MGC_M_ULPI_REGCTL_COMPLETE 0x02 -#define MGC_M_ULPI_REGCTL_REG 0x01 +#define MGC_M_ULPI_VBCTL_USEEXTVBUSIND 0x02 +#define MGC_M_ULPI_VBCTL_USEEXTVBUS 0x01 +#define MGC_M_ULPI_REGCTL_INT_ENABLE 0x08 +#define MGC_M_ULPI_REGCTL_READNOTWRITE 0x04 +#define MGC_M_ULPI_REGCTL_COMPLETE 0x02 +#define MGC_M_ULPI_REGCTL_REG 0x01 /* #define MUSB_DEBUG */ @@ -296,7 +296,7 @@ struct MUSBEndPoint { uint8_t interval[2]; uint8_t config; uint8_t fifosize; - int timeout[2]; /* Always in microframes */ + int timeout[2]; /* Always in microframes */ uint8_t *buf[2]; int fifolen[2]; @@ -542,7 +542,7 @@ static void musb_cb_tick1(void *opaque) ep->delayed_cb[1](&ep->packey[1].p, opaque); } -#define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0) +#define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0) static void musb_schedule_cb(USBPort *port, USBPacket *packey) { @@ -1323,7 +1323,7 @@ static void musb_writeb(void *opaque, hwaddr addr, uint32_t value) /* Negotiate high-speed operation if MGC_M_POWER_HSENAB is set. */ if ((value & MGC_M_POWER_HSENAB) && s->port.dev->speed == USB_SPEED_HIGH) - s->power |= MGC_M_POWER_HSMODE; /* Success */ + s->power |= MGC_M_POWER_HSMODE; /* Success */ /* Restart frame counting. */ } if (value & MGC_M_POWER_SUSPENDM) { diff --git a/hw/usb/hcd-ohci-pci.c b/hw/usb/hcd-ohci-pci.c index 8e1146b862..33ed9b6f5a 100644 --- a/hw/usb/hcd-ohci-pci.c +++ b/hw/usb/hcd-ohci-pci.c @@ -23,7 +23,7 @@ #include "qemu/timer.h" #include "hw/usb.h" #include "migration/vmstate.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/sysbus.h" #include "hw/qdev-dma.h" #include "hw/qdev-properties.h" @@ -120,7 +120,7 @@ static const VMStateDescription vmstate_ohci = { .name = "ohci", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, OHCIPCIState), VMSTATE_STRUCT(state, OHCIPCIState, 1, vmstate_ohci_state, OHCIState), VMSTATE_END_OF_LIST() diff --git a/hw/usb/hcd-ohci-sysbus.c b/hw/usb/hcd-ohci-sysbus.c new file mode 100644 index 0000000000..6fba7f50f8 --- /dev/null +++ b/hw/usb/hcd-ohci-sysbus.c @@ -0,0 +1,88 @@ +/* + * QEMU USB OHCI Emulation + * Copyright (c) 2006 Openedhand Ltd. + * Copyright (c) 2010 CodeSourcery + * Copyright (c) 2024 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "hw/irq.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "qemu/timer.h" +#include "hw/usb.h" +#include "migration/vmstate.h" +#include "hw/sysbus.h" +#include "hw/qdev-dma.h" +#include "hw/qdev-properties.h" +#include "trace.h" +#include "hcd-ohci.h" + + +static void ohci_sysbus_realize(DeviceState *dev, Error **errp) +{ + OHCISysBusState *s = SYSBUS_OHCI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Error *err = NULL; + + usb_ohci_init(&s->ohci, dev, s->num_ports, s->dma_offset, + s->masterbus, s->firstport, + &address_space_memory, ohci_sysbus_die, &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_init_irq(sbd, &s->ohci.irq); + sysbus_init_mmio(sbd, &s->ohci.mem); +} + +static void ohci_sysbus_reset(DeviceState *dev) +{ + OHCISysBusState *s = SYSBUS_OHCI(dev); + OHCIState *ohci = &s->ohci; + + ohci_hard_reset(ohci); +} + +static Property ohci_sysbus_properties[] = { + DEFINE_PROP_STRING("masterbus", OHCISysBusState, masterbus), + DEFINE_PROP_UINT32("num-ports", OHCISysBusState, num_ports, 3), + DEFINE_PROP_UINT32("firstport", OHCISysBusState, firstport, 0), + DEFINE_PROP_DMAADDR("dma-offset", OHCISysBusState, dma_offset, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ohci_sysbus_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = ohci_sysbus_realize; + set_bit(DEVICE_CATEGORY_USB, dc->categories); + dc->desc = "OHCI USB Controller"; + device_class_set_props(dc, ohci_sysbus_properties); + dc->reset = ohci_sysbus_reset; +} + +static const TypeInfo ohci_sysbus_types[] = { + { + .name = TYPE_SYSBUS_OHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OHCISysBusState), + .class_init = ohci_sysbus_class_init, + }, +}; + +DEFINE_TYPES(ohci_sysbus_types); diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 895b29fb86..fc8fc91a1d 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -23,7 +23,7 @@ * o Disable timers when nothing needs to be done, or remove timer usage * all together. * o BIOS work to boot from USB storage -*/ + */ #include "qemu/osdep.h" #include "hw/irq.h" @@ -39,7 +39,7 @@ #include "hcd-ohci.h" /* This causes frames to occur 1000x slower */ -//#define OHCI_TIME_WARP 1 +/*#define OHCI_TIME_WARP 1*/ #define ED_LINK_LIMIT 32 @@ -58,48 +58,48 @@ struct ohci_hcca { #define ED_WBACK_OFFSET offsetof(struct ohci_ed, head) #define ED_WBACK_SIZE 4 -/* Bitfields for the first word of an Endpoint Desciptor. */ +/* Bitfields for the first word of an Endpoint Descriptor. */ #define OHCI_ED_FA_SHIFT 0 -#define OHCI_ED_FA_MASK (0x7f<> 2 < ARRAY_SIZE(ohci_reg_names)) { + return ohci_reg_names[addr >> 2]; + } else { + return ""; + } +} + static void ohci_die(OHCIState *ohci) { ohci->ohci_die(ohci); @@ -335,8 +353,8 @@ static void ohci_soft_reset(OHCIState *ohci) ohci->per_cur = 0; ohci->done = 0; ohci->done_count = 7; - - /* FSMPS is marked TBD in OCHI 1.0, what gives ffs? + /* + * FSMPS is marked TBD in OCHI 1.0, what gives ffs? * I took the value linux sets ... */ ohci->fsmps = 0x2778; @@ -460,10 +478,10 @@ static inline int ohci_read_hcca(OHCIState *ohci, static inline int ohci_put_ed(OHCIState *ohci, dma_addr_t addr, struct ohci_ed *ed) { - /* ed->tail is under control of the HCD. + /* + * ed->tail is under control of the HCD. * Since just ed->head is changed by HC, just write back this */ - return put_dwords(ohci, addr + ED_WBACK_OFFSET, (uint32_t *)((char *)ed + ED_WBACK_OFFSET), ED_WBACK_SIZE >> 2); @@ -499,9 +517,9 @@ static int ohci_copy_td(OHCIState *ohci, struct ohci_td *td, ptr = td->cbp; n = 0x1000 - (ptr & 0xfff); - if (n > len) + if (n > len) { n = len; - + } if (dma_memory_rw(ohci->as, ptr + ohci->localmem_base, buf, n, dir, MEMTXATTRS_UNSPECIFIED)) { return -1; @@ -527,9 +545,9 @@ static int ohci_copy_iso_td(OHCIState *ohci, ptr = start_addr; n = 0x1000 - (ptr & 0xfff); - if (n > len) + if (n > len) { n = len; - + } if (dma_memory_rw(ohci->as, ptr + ohci->localmem_base, buf, n, dir, MEMTXATTRS_UNSPECIFIED)) { return -1; @@ -571,6 +589,11 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) addr = ed->head & OHCI_DPTR_MASK; + if (addr == 0) { + ohci_die(ohci); + return 1; + } + if (ohci_read_iso_td(ohci, addr, &iso_td)) { trace_usb_ohci_iso_td_read_failed(addr); ohci_die(ohci); @@ -579,7 +602,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) starting_frame = OHCI_BM(iso_td.flags, TD_SF); frame_count = OHCI_BM(iso_td.flags, TD_FC); - relative_frame_number = USUB(ohci->frame_number, starting_frame); + relative_frame_number = USUB(ohci->frame_number, starting_frame); trace_usb_ohci_iso_td_head( ed->head & OHCI_DPTR_MASK, ed->tail & OHCI_DPTR_MASK, @@ -596,8 +619,10 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) trace_usb_ohci_iso_td_relative_frame_number_neg(relative_frame_number); return 1; } else if (relative_frame_number > frame_count) { - /* ISO TD expired - retire the TD to the Done Queue and continue with - the next ISO TD of the same ED */ + /* + * ISO TD expired - retire the TD to the Done Queue and continue with + * the next ISO TD of the same ED + */ trace_usb_ohci_iso_td_relative_frame_number_big(relative_frame_number, frame_count); if (OHCI_CC_DATAOVERRUN == OHCI_BM(iso_td.flags, TD_CC)) { @@ -610,8 +635,9 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) iso_td.next = ohci->done; ohci->done = addr; i = OHCI_BM(iso_td.flags, TD_DI); - if (i < ohci->done_count) + if (i < ohci->done_count) { ohci->done_count = i; + } if (ohci_put_iso_td(ohci, addr, &iso_td)) { ohci_die(ohci); return 1; @@ -650,8 +676,8 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) next_offset = iso_td.be; } - if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || - ((relative_frame_number < frame_count) && + if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || + ((relative_frame_number < frame_count) && !(OHCI_BM(next_offset, TD_PSW_CC) & 0xe))) { trace_usb_ohci_iso_td_bad_cc_not_accessed(start_offset, next_offset); return 1; @@ -796,8 +822,9 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) iso_td.next = ohci->done; ohci->done = addr; i = OHCI_BM(iso_td.flags, TD_DI); - if (i < ohci->done_count) + if (i < ohci->done_count) { ohci->done_count = i; + } } if (ohci_put_iso_td(ohci, addr, &iso_td)) { ohci_die(ohci); @@ -805,13 +832,14 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) return 1; } +#define HEX_CHAR_PER_LINE 16 + static void ohci_td_pkt(const char *msg, const uint8_t *buf, size_t len) { bool print16; bool printall; - const int width = 16; int i; - char tmp[3 * width + 1]; + char tmp[3 * HEX_CHAR_PER_LINE + 1]; char *p = tmp; print16 = !!trace_event_get_state_backends(TRACE_USB_OHCI_TD_PKT_SHORT); @@ -822,7 +850,7 @@ static void ohci_td_pkt(const char *msg, const uint8_t *buf, size_t len) } for (i = 0; ; i++) { - if (i && (!(i % width) || (i == len))) { + if (i && (!(i % HEX_CHAR_PER_LINE) || (i == len))) { if (!printall) { trace_usb_ohci_td_pkt_short(msg, tmp); break; @@ -839,9 +867,10 @@ static void ohci_td_pkt(const char *msg, const uint8_t *buf, size_t len) } } -/* Service a transport descriptor. - Returns nonzero to terminate processing of this endpoint. */ - +/* + * Service a transport descriptor. + * Returns nonzero to terminate processing of this endpoint. + */ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) { int dir; @@ -858,7 +887,12 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) int completion; addr = ed->head & OHCI_DPTR_MASK; - /* See if this TD has already been submitted to the device. */ + if (addr == 0) { + ohci_die(ohci); + return 1; + } + + /* See if this TD has already been submitted to the device. */ completion = (addr == ohci->async_td); if (completion && !ohci->async_complete) { trace_usb_ohci_td_skip_async(); @@ -874,7 +908,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) switch (dir) { case OHCI_TD_DIR_OUT: case OHCI_TD_DIR_IN: - /* Same value. */ + /* Same value. */ break; default: dir = OHCI_BM(td.flags, TD_DP); @@ -945,11 +979,12 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) } ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); if (ohci->async_td) { - /* ??? The hardware should allow one active packet per - endpoint. We only allow one active packet per controller. - This should be sufficient as long as devices respond in a - timely manner. - */ + /* + * ??? The hardware should allow one active packet per + * endpoint. We only allow one active packet per controller. + * This should be sufficient as long as devices respond in a + * timely manner. + */ trace_usb_ohci_td_too_many_pending(ep->nr); return 1; } @@ -985,7 +1020,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) /* Writeback */ if (ret == pktlen || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) { - /* Transmission succeeded. */ + /* Transmission succeeded. */ if (ret == len) { td.cbp = 0; } else { @@ -1007,8 +1042,9 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) /* Setting ED_C is part of the TD retirement process */ ed->head &= ~OHCI_ED_C; - if (td.flags & OHCI_TD_T0) + if (td.flags & OHCI_TD_T0) { ed->head |= OHCI_ED_C; + } } else { if (ret >= 0) { trace_usb_ohci_td_underrun(); @@ -1037,8 +1073,10 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) OHCI_SET_BM(td.flags, TD_EC, 3); break; } - /* An error occurred so we have to clear the interrupt counter. See - * spec at 6.4.4 on page 104 */ + /* + * An error occurred so we have to clear the interrupt counter. + * See spec at 6.4.4 on page 104 + */ ohci->done_count = 0; } ed->head |= OHCI_ED_H; @@ -1050,8 +1088,9 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) td.next = ohci->done; ohci->done = addr; i = OHCI_BM(td.flags, TD_DI); - if (i < ohci->done_count) + if (i < ohci->done_count) { ohci->done_count = i; + } exit_no_retire: if (ohci_put_td(ohci, addr, &td)) { ohci_die(ohci); @@ -1060,7 +1099,7 @@ exit_no_retire: return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR; } -/* Service an endpoint list. Returns nonzero if active TD were found. */ +/* Service an endpoint list. Returns nonzero if active TD were found. */ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) { struct ohci_ed ed; @@ -1070,9 +1109,9 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) uint32_t link_cnt = 0; active = 0; - if (head == 0) + if (head == 0) { return 0; - + } for (cur = head; cur && link_cnt++ < ED_LINK_LIMIT; cur = next_ed) { if (ohci_read_ed(ohci, cur, &ed)) { trace_usb_ohci_ed_read_error(cur); @@ -1084,7 +1123,7 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) { uint32_t addr; - /* Cancel pending packets for ED that have been paused. */ + /* Cancel pending packets for ED that have been paused. */ addr = ed.head & OHCI_DPTR_MASK; if (ohci->async_td && addr == ohci->async_td) { usb_cancel_packet(&ohci->usb_packet); @@ -1101,15 +1140,16 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK); trace_usb_ohci_ed_pkt_flags( OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN), - OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0, + OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S) != 0, (ed.flags & OHCI_ED_K) != 0, (ed.flags & OHCI_ED_F) != 0, OHCI_BM(ed.flags, ED_MPS)); active = 1; if ((ed.flags & OHCI_ED_F) == 0) { - if (ohci_service_td(ohci, &ed)) + if (ohci_service_td(ohci, &ed)) { break; + } } else { /* Handle isochronous endpoints */ if (ohci_service_iso_td(ohci, &ed)) { @@ -1140,7 +1180,7 @@ static void ohci_sof(OHCIState *ohci) ohci_set_interrupt(ohci, OHCI_INTR_SF); } -/* Process Control and Bulk lists. */ +/* Process Control and Bulk lists. */ static void ohci_process_lists(OHCIState *ohci) { if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) { @@ -1181,7 +1221,7 @@ static void ohci_frame_boundary(void *opaque) ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n])); } - /* Cancel all pending packets if either of the lists has been disabled. */ + /* Cancel all pending packets if either of the lists has been disabled. */ if (ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) { ohci_stop_endpoints(ohci); } @@ -1199,21 +1239,25 @@ static void ohci_frame_boundary(void *opaque) /* Increment frame number and take care of endianness. */ ohci->frame_number = (ohci->frame_number + 1) & 0xffff; hcca.frame = cpu_to_le16(ohci->frame_number); + /* When the HC updates frame number, set pad to 0. Ref OHCI Spec 4.4.1*/ + hcca.pad = 0; if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) { - if (!ohci->done) + if (!ohci->done) { abort(); - if (ohci->intr & ohci->intr_status) + } + if (ohci->intr & ohci->intr_status) { ohci->done |= 1; + } hcca.done = cpu_to_le32(ohci->done); ohci->done = 0; ohci->done_count = 7; ohci_set_interrupt(ohci, OHCI_INTR_WD); } - if (ohci->done_count != 7 && ohci->done_count != 0) + if (ohci->done_count != 7 && ohci->done_count != 0) { ohci->done_count--; - + } /* Do SOF stuff here */ ohci_sof(ohci); @@ -1223,18 +1267,17 @@ static void ohci_frame_boundary(void *opaque) } } -/* Start sending SOF tokens across the USB bus, lists are processed in +/* + * Start sending SOF tokens across the USB bus, lists are processed in * next frame */ static int ohci_bus_start(OHCIState *ohci) { trace_usb_ohci_start(ohci->name); - - /* Delay the first SOF event by one frame time as - * linux driver is not ready to receive it and - * can meet some race conditions + /* + * Delay the first SOF event by one frame time as linux driver is + * not ready to receive it and can meet some race conditions */ - ohci->sof_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ohci_eof_timer(ohci); @@ -1248,39 +1291,7 @@ void ohci_bus_stop(OHCIState *ohci) timer_del(ohci->eof_timer); } -/* Sets a flag in a port status register but only set it if the port is - * connected, if not set ConnectStatusChange flag. If flag is enabled - * return 1. - */ -static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val) -{ - int ret = 1; - - /* writing a 0 has no effect */ - if (val == 0) - return 0; - - /* If CurrentConnectStatus is cleared we set - * ConnectStatusChange - */ - if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) { - ohci->rhport[i].ctrl |= OHCI_PORT_CSC; - if (ohci->rhstatus & OHCI_RHS_DRWE) { - /* TODO: CSC is a wakeup event */ - } - return 0; - } - - if (ohci->rhport[i].ctrl & val) - ret = 0; - - /* set the bit */ - ohci->rhport[i].ctrl |= val; - - return ret; -} - -/* Set the frame interval - frame interval toggle is manipulated by the hcd only */ +/* Frame interval toggle is manipulated by the hcd only */ static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val) { val &= OHCI_FMI_FI; @@ -1297,10 +1308,8 @@ static void ohci_port_power(OHCIState *ohci, int i, int p) if (p) { ohci->rhport[i].ctrl |= OHCI_PORT_PPS; } else { - ohci->rhport[i].ctrl &= ~(OHCI_PORT_PPS| - OHCI_PORT_CCS| - OHCI_PORT_PSS| - OHCI_PORT_PRS); + ohci->rhport[i].ctrl &= ~(OHCI_PORT_PPS | OHCI_PORT_CCS | + OHCI_PORT_PSS | OHCI_PORT_PRS); } } @@ -1315,9 +1324,9 @@ static void ohci_set_ctl(OHCIState *ohci, uint32_t val) new_state = ohci->ctl & OHCI_CTL_HCFS; /* no state change */ - if (old_state == new_state) + if (old_state == new_state) { return; - + } trace_usb_ohci_set_ctl(ohci->name, new_state); switch (new_state) { case OHCI_USB_OPERATIONAL: @@ -1343,21 +1352,19 @@ static uint32_t ohci_get_frame_remaining(OHCIState *ohci) uint16_t fr; int64_t tks; - if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL) - return (ohci->frt << 31); - - /* Being in USB operational state guarnatees sof_time was - * set already. - */ + if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL) { + return ohci->frt << 31; + } + /* Being in USB operational state guarantees sof_time was set already. */ tks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - ohci->sof_time; if (tks < 0) { tks = 0; } /* avoid muldiv if possible */ - if (tks >= usb_frame_time) - return (ohci->frt << 31); - + if (tks >= usb_frame_time) { + return ohci->frt << 31; + } tks = tks / usb_bit_time; fr = (uint16_t)(ohci->fi - tks); @@ -1373,33 +1380,81 @@ static void ohci_set_hub_status(OHCIState *ohci, uint32_t val) old_state = ohci->rhstatus; /* write 1 to clear OCIC */ - if (val & OHCI_RHS_OCIC) + if (val & OHCI_RHS_OCIC) { ohci->rhstatus &= ~OHCI_RHS_OCIC; - + } if (val & OHCI_RHS_LPS) { int i; - for (i = 0; i < ohci->num_ports; i++) + for (i = 0; i < ohci->num_ports; i++) { ohci_port_power(ohci, i, 0); + } trace_usb_ohci_hub_power_down(); } if (val & OHCI_RHS_LPSC) { int i; - for (i = 0; i < ohci->num_ports; i++) + for (i = 0; i < ohci->num_ports; i++) { ohci_port_power(ohci, i, 1); + } trace_usb_ohci_hub_power_up(); } - if (val & OHCI_RHS_DRWE) + if (val & OHCI_RHS_DRWE) { ohci->rhstatus |= OHCI_RHS_DRWE; - - if (val & OHCI_RHS_CRWE) + } + if (val & OHCI_RHS_CRWE) { ohci->rhstatus &= ~OHCI_RHS_DRWE; - - if (old_state != ohci->rhstatus) + } + if (old_state != ohci->rhstatus) { ohci_set_interrupt(ohci, OHCI_INTR_RHSC); + } +} + +/* This is the one state transition the controller can do by itself */ +static bool ohci_resume(OHCIState *s) +{ + if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { + trace_usb_ohci_remote_wakeup(s->name); + s->ctl &= ~OHCI_CTL_HCFS; + s->ctl |= OHCI_USB_RESUME; + return true; + } + return false; +} + +/* + * Sets a flag in a port status reg but only set it if the port is connected. + * If not set ConnectStatusChange flag. If flag is enabled return 1. + */ +static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val) +{ + int ret = 1; + + /* writing a 0 has no effect */ + if (val == 0) { + return 0; + } + /* If CurrentConnectStatus is cleared we set ConnectStatusChange */ + if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) { + ohci->rhport[i].ctrl |= OHCI_PORT_CSC; + if (ohci->rhstatus & OHCI_RHS_DRWE) { + /* CSC is a wakeup event */ + if (ohci_resume(ohci)) { + ohci_set_interrupt(ohci, OHCI_INTR_RD); + } + } + return 0; + } + + if (ohci->rhport[i].ctrl & val) { + ret = 0; + } + /* set the bit */ + ohci->rhport[i].ctrl |= val; + + return ret; } /* Set root hub port status */ @@ -1412,12 +1467,12 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) old_state = port->ctrl; /* Write to clear CSC, PESC, PSSC, OCIC, PRSC */ - if (val & OHCI_PORT_WTC) + if (val & OHCI_PORT_WTC) { port->ctrl &= ~(val & OHCI_PORT_WTC); - - if (val & OHCI_PORT_CCS) + } + if (val & OHCI_PORT_CCS) { port->ctrl &= ~OHCI_PORT_PES; - + } ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES); if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS)) { @@ -1428,20 +1483,20 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) trace_usb_ohci_port_reset(portnum); usb_device_reset(port->port.dev); port->ctrl &= ~OHCI_PORT_PRS; - /* ??? Should this also set OHCI_PORT_PESC. */ + /* ??? Should this also set OHCI_PORT_PESC. */ port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC; } - /* Invert order here to ensure in ambiguous case, device is - * powered up... - */ - if (val & OHCI_PORT_LSDA) + /* Invert order here to ensure in ambiguous case, device is powered up. */ + if (val & OHCI_PORT_LSDA) { ohci_port_power(ohci, portnum, 0); - if (val & OHCI_PORT_PPS) + } + if (val & OHCI_PORT_PPS) { ohci_port_power(ohci, portnum, 1); - - if (old_state != port->ctrl) + } + if (old_state != port->ctrl) { ohci_set_interrupt(ohci, OHCI_INTR_RHSC); + } } static uint64_t ohci_mem_read(void *opaque, @@ -1458,6 +1513,8 @@ static uint64_t ohci_mem_read(void *opaque, } else if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) { /* HcRhPortStatus */ retval = ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS; + trace_usb_ohci_mem_port_read(size, "HcRhPortStatus", (addr - 0x50) >> 2, + addr, addr >> 2, retval); } else { switch (addr >> 2) { case 0: /* HcRevision */ @@ -1562,6 +1619,10 @@ static uint64_t ohci_mem_read(void *opaque, trace_usb_ohci_mem_read_bad_offset(addr); retval = 0xffffffff; } + if (addr != 0xc || retval) { + trace_usb_ohci_mem_read(size, ohci_reg_name(addr), addr, addr >> 2, + retval); + } } return retval; @@ -1582,10 +1643,13 @@ static void ohci_mem_write(void *opaque, if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) { /* HcRhPortStatus */ + trace_usb_ohci_mem_port_write(size, "HcRhPortStatus", + (addr - 0x50) >> 2, addr, addr >> 2, val); ohci_port_set_status(ohci, (addr - 0x54) >> 2, val); return; } + trace_usb_ohci_mem_write(size, ohci_reg_name(addr), addr, addr >> 2, val); switch (addr >> 2) { case 1: /* HcControl */ ohci_set_ctl(ohci, val); @@ -1598,8 +1662,9 @@ static void ohci_mem_write(void *opaque, /* Bits written as '0' remain unchanged in the register */ ohci->status |= val; - if (ohci->status & OHCI_STATUS_HCR) + if (ohci->status & OHCI_STATUS_HCR) { ohci_soft_reset(ohci); + } break; case 3: /* HcInterruptStatus */ @@ -1677,8 +1742,9 @@ static void ohci_mem_write(void *opaque, case 25: /* HcHReset */ ohci->hreset = val & ~OHCI_HRESET_FSBIR; - if (val & OHCI_HRESET_FSBIR) + if (val & OHCI_HRESET_FSBIR) { ohci_hard_reset(ohci); + } break; case 26: /* HcHInterruptEnable */ @@ -1779,11 +1845,7 @@ static void ohci_wakeup(USBPort *port1) intr = OHCI_INTR_RHSC; } /* Note that the controller can be suspended even if this port is not */ - if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { - trace_usb_ohci_remote_wakeup(s->name); - /* This is the one state transition the controller can do by itself */ - s->ctl &= ~OHCI_CTL_HCFS; - s->ctl |= OHCI_USB_RESUME; + if (ohci_resume(s)) { /* * In suspend mode only ResumeDetected is possible, not RHSC: * see the OHCI spec 5.1.2.3. @@ -1816,7 +1878,7 @@ static USBBusOps ohci_bus_ops = { void usb_ohci_init(OHCIState *ohci, DeviceState *dev, uint32_t num_ports, dma_addr_t localmem_base, char *masterbus, uint32_t firstport, AddressSpace *as, - void (*ohci_die_fn)(struct OHCIState *), Error **errp) + void (*ohci_die_fn)(OHCIState *), Error **errp) { Error *err = NULL; int i; @@ -1848,7 +1910,7 @@ void usb_ohci_init(OHCIState *ohci, DeviceState *dev, uint32_t num_ports, ohci->num_ports = num_ports; if (masterbus) { USBPort *ports[OHCI_MAX_PORTS]; - for(i = 0; i < num_ports; i++) { + for (i = 0; i < num_ports; i++) { ports[i] = &ohci->rhport[i].port; } usb_register_companion(masterbus, ports, num_ports, @@ -1881,7 +1943,7 @@ void usb_ohci_init(OHCIState *ohci, DeviceState *dev, uint32_t num_ports, ohci_frame_boundary, ohci); } -/** +/* * A typical OHCI will stop operating and set itself into error state * (which can be queried by MMIO) to signal that it got an error. */ @@ -1893,36 +1955,11 @@ void ohci_sysbus_die(struct OHCIState *ohci) ohci_bus_stop(ohci); } -static void ohci_realize_pxa(DeviceState *dev, Error **errp) -{ - OHCISysBusState *s = SYSBUS_OHCI(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - Error *err = NULL; - - usb_ohci_init(&s->ohci, dev, s->num_ports, s->dma_offset, - s->masterbus, s->firstport, - &address_space_memory, ohci_sysbus_die, &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_init_irq(sbd, &s->ohci.irq); - sysbus_init_mmio(sbd, &s->ohci.mem); -} - -static void usb_ohci_reset_sysbus(DeviceState *dev) -{ - OHCISysBusState *s = SYSBUS_OHCI(dev); - OHCIState *ohci = &s->ohci; - - ohci_hard_reset(ohci); -} - static const VMStateDescription vmstate_ohci_state_port = { .name = "ohci-core/port", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(ctrl, OHCIPort), VMSTATE_END_OF_LIST() }, @@ -1940,7 +1977,7 @@ static const VMStateDescription vmstate_ohci_eof_timer = { .version_id = 1, .minimum_version_id = 1, .needed = ohci_eof_timer_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(eof_timer, OHCIState), VMSTATE_END_OF_LIST() }, @@ -1950,7 +1987,7 @@ const VMStateDescription vmstate_ohci_state = { .name = "ohci-core", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64(sof_time, OHCIState), VMSTATE_UINT32(ctl, OHCIState), VMSTATE_UINT32(status, OHCIState), @@ -1987,41 +2024,8 @@ const VMStateDescription vmstate_ohci_state = { VMSTATE_BOOL(async_complete, OHCIState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_ohci_eof_timer, NULL } }; - -static Property ohci_sysbus_properties[] = { - DEFINE_PROP_STRING("masterbus", OHCISysBusState, masterbus), - DEFINE_PROP_UINT32("num-ports", OHCISysBusState, num_ports, 3), - DEFINE_PROP_UINT32("firstport", OHCISysBusState, firstport, 0), - DEFINE_PROP_DMAADDR("dma-offset", OHCISysBusState, dma_offset, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ohci_sysbus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = ohci_realize_pxa; - set_bit(DEVICE_CATEGORY_USB, dc->categories); - dc->desc = "OHCI USB Controller"; - device_class_set_props(dc, ohci_sysbus_properties); - dc->reset = usb_ohci_reset_sysbus; -} - -static const TypeInfo ohci_sysbus_info = { - .name = TYPE_SYSBUS_OHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(OHCISysBusState), - .class_init = ohci_sysbus_class_init, -}; - -static void ohci_register_types(void) -{ - type_register_static(&ohci_sysbus_info); -} - -type_init(ohci_register_types) diff --git a/hw/usb/hcd-ohci.h b/hw/usb/hcd-ohci.h index 11ac57058d..e1827227ac 100644 --- a/hw/usb/hcd-ohci.h +++ b/hw/usb/hcd-ohci.h @@ -21,6 +21,7 @@ #ifndef HCD_OHCI_H #define HCD_OHCI_H +#include "hw/sysbus.h" #include "sysemu/dma.h" #include "hw/usb.h" #include "qom/object.h" @@ -33,7 +34,9 @@ typedef struct OHCIPort { uint32_t ctrl; } OHCIPort; -typedef struct OHCIState { +typedef struct OHCIState OHCIState; + +struct OHCIState { USBBus bus; qemu_irq irq; MemoryRegion mem; @@ -89,8 +92,8 @@ typedef struct OHCIState { uint32_t async_td; bool async_complete; - void (*ohci_die)(struct OHCIState *ohci); -} OHCIState; + void (*ohci_die)(OHCIState *ohci); +}; #define TYPE_SYSBUS_OHCI "sysbus-ohci" OBJECT_DECLARE_SIMPLE_TYPE(OHCISysBusState, SYSBUS_OHCI) @@ -112,7 +115,7 @@ extern const VMStateDescription vmstate_ohci_state; void usb_ohci_init(OHCIState *ohci, DeviceState *dev, uint32_t num_ports, dma_addr_t localmem_base, char *masterbus, uint32_t firstport, AddressSpace *as, - void (*ohci_die_fn)(struct OHCIState *), Error **errp); + void (*ohci_die_fn)(OHCIState *), Error **errp); void ohci_bus_stop(OHCIState *ohci); void ohci_stop_endpoints(OHCIState *ohci); void ohci_hard_reset(OHCIState *ohci); diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index d1b5657d72..a03cf22e69 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -60,9 +60,7 @@ enum { TD_RESULT_ASYNC_CONT, }; -typedef struct UHCIState UHCIState; typedef struct UHCIAsync UHCIAsync; -typedef struct UHCIPCIDeviceClass UHCIPCIDeviceClass; struct UHCIPCIDeviceClass { PCIDeviceClass parent_class; @@ -324,7 +322,7 @@ static void uhci_reset(DeviceState *dev) s->fl_base_addr = 0; s->sof_timing = 64; - for(i = 0; i < NB_PORTS; i++) { + for(i = 0; i < UHCI_PORTS; i++) { port = &s->ports[i]; port->ctrl = 0x0080; if (port->port.dev && port->port.dev->attached) { @@ -341,7 +339,7 @@ static const VMStateDescription vmstate_uhci_port = { .name = "uhci port", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(ctrl, UHCIPort), VMSTATE_END_OF_LIST() } @@ -363,10 +361,10 @@ static const VMStateDescription vmstate_uhci = { .version_id = 3, .minimum_version_id = 1, .post_load = uhci_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, UHCIState), VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState, NULL), - VMSTATE_STRUCT_ARRAY(ports, UHCIState, NB_PORTS, 1, + VMSTATE_STRUCT_ARRAY(ports, UHCIState, UHCI_PORTS, 1, vmstate_uhci_port, UHCIPort), VMSTATE_UINT16(cmd, UHCIState), VMSTATE_UINT16(status, UHCIState), @@ -406,7 +404,7 @@ static void uhci_port_write(void *opaque, hwaddr addr, int i; /* send reset on the USB bus */ - for(i = 0; i < NB_PORTS; i++) { + for(i = 0; i < UHCI_PORTS; i++) { port = &s->ports[i]; usb_device_reset(port->port.dev); } @@ -459,8 +457,9 @@ static void uhci_port_write(void *opaque, hwaddr addr, int n; n = (addr >> 1) & 7; - if (n >= NB_PORTS) + if (n >= UHCI_PORTS) { return; + } port = &s->ports[n]; dev = port->port.dev; if (dev && dev->attached) { @@ -515,8 +514,9 @@ static uint64_t uhci_port_read(void *opaque, hwaddr addr, unsigned size) UHCIPort *port; int n; n = (addr >> 1) & 7; - if (n >= NB_PORTS) + if (n >= UHCI_PORTS) { goto read_default; + } port = &s->ports[n]; val = port->ctrl; } @@ -609,7 +609,7 @@ static USBDevice *uhci_find_device(UHCIState *s, uint8_t addr) USBDevice *dev; int i; - for (i = 0; i < NB_PORTS; i++) { + for (i = 0; i < UHCI_PORTS; i++) { UHCIPort *port = &s->ports[i]; if (!(port->ctrl & UHCI_PORT_EN)) { continue; @@ -1161,8 +1161,7 @@ static USBBusOps uhci_bus_ops = { void usb_uhci_common_realize(PCIDevice *dev, Error **errp) { Error *err = NULL; - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - UHCIPCIDeviceClass *u = container_of(pc, UHCIPCIDeviceClass, parent_class); + UHCIPCIDeviceClass *u = UHCI_GET_CLASS(dev); UHCIState *s = UHCI(dev); uint8_t *pci_conf = s->dev.config; int i; @@ -1174,11 +1173,11 @@ void usb_uhci_common_realize(PCIDevice *dev, Error **errp) s->irq = pci_allocate_irq(dev); if (s->masterbus) { - USBPort *ports[NB_PORTS]; - for(i = 0; i < NB_PORTS; i++) { + USBPort *ports[UHCI_PORTS]; + for(i = 0; i < UHCI_PORTS; i++) { ports[i] = &s->ports[i].port; } - usb_register_companion(s->masterbus, ports, NB_PORTS, + usb_register_companion(s->masterbus, ports, UHCI_PORTS, s->firstport, s, &uhci_port_ops, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL, &err); @@ -1188,14 +1187,14 @@ void usb_uhci_common_realize(PCIDevice *dev, Error **errp) } } else { usb_bus_new(&s->bus, sizeof(s->bus), &uhci_bus_ops, DEVICE(dev)); - for (i = 0; i < NB_PORTS; i++) { + for (i = 0; i < UHCI_PORTS; i++) { usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); } } - s->bh = qemu_bh_new(uhci_bh, s); + s->bh = qemu_bh_new_guarded(uhci_bh, s, &DEVICE(dev)->mem_reentrancy_guard); s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, uhci_frame_timer, s); - s->num_ports_vmstate = NB_PORTS; + s->num_ports_vmstate = UHCI_PORTS; QTAILQ_INIT(&s->queues); memory_region_init_io(&s->io_bar, OBJECT(s), &uhci_ioport_ops, s, @@ -1269,7 +1268,7 @@ void uhci_data_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - UHCIPCIDeviceClass *u = container_of(k, UHCIPCIDeviceClass, parent_class); + UHCIPCIDeviceClass *u = UHCI_CLASS(klass); UHCIInfo *info = data; k->realize = info->realize ? info->realize : usb_uhci_common_realize; @@ -1292,56 +1291,56 @@ void uhci_data_class_init(ObjectClass *klass, void *data) static UHCIInfo uhci_info[] = { { - .name = "piix3-usb-uhci", + .name = TYPE_PIIX3_USB_UHCI, .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82371SB_2, .revision = 0x01, .irq_pin = 3, .unplug = true, },{ - .name = "piix4-usb-uhci", + .name = TYPE_PIIX4_USB_UHCI, .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82371AB_2, .revision = 0x01, .irq_pin = 3, .unplug = true, },{ - .name = "ich9-usb-uhci1", /* 00:1d.0 */ + .name = TYPE_ICH9_USB_UHCI(1), /* 00:1d.0 */ .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1, .revision = 0x03, .irq_pin = 0, .unplug = false, },{ - .name = "ich9-usb-uhci2", /* 00:1d.1 */ + .name = TYPE_ICH9_USB_UHCI(2), /* 00:1d.1 */ .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2, .revision = 0x03, .irq_pin = 1, .unplug = false, },{ - .name = "ich9-usb-uhci3", /* 00:1d.2 */ + .name = TYPE_ICH9_USB_UHCI(3), /* 00:1d.2 */ .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3, .revision = 0x03, .irq_pin = 2, .unplug = false, },{ - .name = "ich9-usb-uhci4", /* 00:1a.0 */ + .name = TYPE_ICH9_USB_UHCI(4), /* 00:1a.0 */ .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI4, .revision = 0x03, .irq_pin = 0, .unplug = false, },{ - .name = "ich9-usb-uhci5", /* 00:1a.1 */ + .name = TYPE_ICH9_USB_UHCI(5), /* 00:1a.1 */ .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI5, .revision = 0x03, .irq_pin = 1, .unplug = false, },{ - .name = "ich9-usb-uhci6", /* 00:1a.2 */ + .name = TYPE_ICH9_USB_UHCI(6), /* 00:1a.2 */ .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI6, .revision = 0x03, diff --git a/hw/usb/hcd-uhci.h b/hw/usb/hcd-uhci.h index c85ab7868e..6d26b94e92 100644 --- a/hw/usb/hcd-uhci.h +++ b/hw/usb/hcd-uhci.h @@ -30,12 +30,12 @@ #include "exec/memory.h" #include "qemu/timer.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/usb.h" typedef struct UHCIQueue UHCIQueue; -#define NB_PORTS 2 +#define UHCI_PORTS 2 typedef struct UHCIPort { USBPort port; @@ -59,7 +59,7 @@ typedef struct UHCIState { uint32_t frame_bytes; uint32_t frame_bandwidth; bool completions_only; - UHCIPort ports[NB_PORTS]; + UHCIPort ports[UHCI_PORTS]; qemu_irq irq; /* Interrupts that should be raised at the end of the current frame. */ uint32_t pending_int_mask; @@ -75,7 +75,7 @@ typedef struct UHCIState { } UHCIState; #define TYPE_UHCI "pci-uhci-usb" -DECLARE_INSTANCE_CHECKER(UHCIState, UHCI, TYPE_UHCI) +OBJECT_DECLARE_TYPE(UHCIState, UHCIPCIDeviceClass, UHCI) typedef struct UHCIInfo { const char *name; @@ -91,4 +91,8 @@ typedef struct UHCIInfo { void uhci_data_class_init(ObjectClass *klass, void *data); void usb_uhci_common_realize(PCIDevice *dev, Error **errp); +#define TYPE_PIIX3_USB_UHCI "piix3-usb-uhci" +#define TYPE_PIIX4_USB_UHCI "piix4-usb-uhci" +#define TYPE_ICH9_USB_UHCI(fn) "ich9-usb-uhci" #fn + #endif diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c index 13c9ac5dbd..328e5bfe7c 100644 --- a/hw/usb/hcd-xhci-nec.c +++ b/hw/usb/hcd-xhci-nec.c @@ -27,14 +27,16 @@ #include "hcd-xhci-pci.h" -typedef struct XHCINecState { +OBJECT_DECLARE_SIMPLE_TYPE(XHCINecState, NEC_XHCI) + +struct XHCINecState { /*< private >*/ XHCIPciState parent_obj; /*< public >*/ uint32_t flags; uint32_t intrs; uint32_t slots; -} XHCINecState; +}; static Property nec_xhci_properties[] = { DEFINE_PROP_ON_OFF_AUTO("msi", XHCIPciState, msi, ON_OFF_AUTO_AUTO), @@ -51,7 +53,7 @@ static Property nec_xhci_properties[] = { static void nec_xhci_instance_init(Object *obj) { XHCIPciState *pci = XHCI_PCI(obj); - XHCINecState *nec = container_of(pci, XHCINecState, parent_obj); + XHCINecState *nec = NEC_XHCI(obj); pci->xhci.flags = nec->flags; pci->xhci.numintrs = nec->intrs; diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index e934b1a5b1..4423983308 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -85,7 +85,7 @@ static void xhci_pci_reset(DeviceState *dev) { XHCIPciState *s = XHCI_PCI(dev); - device_legacy_reset(DEVICE(&s->xhci)); + device_cold_reset(DEVICE(&s->xhci)); } static int xhci_pci_vmstate_post_load(void *opaque, int version_id) @@ -178,7 +178,7 @@ static const VMStateDescription vmstate_xhci_pci = { .name = "xhci", .version_id = 1, .post_load = xhci_pci_vmstate_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, XHCIPciState), VMSTATE_MSIX(parent_obj, XHCIPciState), VMSTATE_STRUCT(xhci, XHCIPciState, 1, vmstate_xhci, XHCIState), diff --git a/hw/usb/hcd-xhci-pci.h b/hw/usb/hcd-xhci-pci.h index c193f79443..08f70ce97c 100644 --- a/hw/usb/hcd-xhci-pci.h +++ b/hw/usb/hcd-xhci-pci.h @@ -24,6 +24,7 @@ #ifndef HW_USB_HCD_XHCI_PCI_H #define HW_USB_HCD_XHCI_PCI_H +#include "hw/pci/pci_device.h" #include "hw/usb.h" #include "hcd-xhci.h" diff --git a/hw/usb/hcd-xhci-sysbus.c b/hw/usb/hcd-xhci-sysbus.c index a14e438196..d93bae31f9 100644 --- a/hw/usb/hcd-xhci-sysbus.c +++ b/hw/usb/hcd-xhci-sysbus.c @@ -29,7 +29,7 @@ void xhci_sysbus_reset(DeviceState *dev) { XHCISysbusState *s = XHCI_SYSBUS(dev); - device_legacy_reset(DEVICE(&s->xhci)); + device_cold_reset(DEVICE(&s->xhci)); } static void xhci_sysbus_realize(DeviceState *dev, Error **errp) @@ -91,7 +91,7 @@ static Property xhci_sysbus_props[] = { static const VMStateDescription vmstate_xhci_sysbus = { .name = "xhci-sysbus", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(xhci, XHCISysbusState, 1, vmstate_xhci, XHCIState), VMSTATE_END_OF_LIST() } diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 0cd0a5e540..ad40232eb6 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qemu/timer.h" +#include "qemu/log.h" #include "qemu/module.h" #include "qemu/queue.h" #include "migration/vmstate.h" @@ -216,10 +217,10 @@ enum { (((data) >> field##_SHIFT) & field##_MASK) #define set_field(data, newval, field) do { \ - uint32_t val = *data; \ - val &= ~(field##_MASK << field##_SHIFT); \ - val |= ((newval) & field##_MASK) << field##_SHIFT; \ - *data = val; \ + uint32_t val_ = *data; \ + val_ &= ~(field##_MASK << field##_SHIFT); \ + val_ |= ((newval) & field##_MASK) << field##_SHIFT; \ + *data = val_; \ } while (0) typedef enum EPType { @@ -462,6 +463,12 @@ static void xhci_mfwrap_timer(void *opaque) xhci_mfwrap_update(xhci); } +static void xhci_die(XHCIState *xhci) +{ + xhci->usbsts |= USBSTS_HCE; + DPRINTF("xhci: asserted controller error\n"); +} + static inline dma_addr_t xhci_addr64(uint32_t low, uint32_t high) { if (sizeof(dma_addr_t) == 4) { @@ -487,7 +494,14 @@ static inline void xhci_dma_read_u32s(XHCIState *xhci, dma_addr_t addr, assert((len % sizeof(uint32_t)) == 0); - dma_memory_read(xhci->as, addr, buf, len, MEMTXATTRS_UNSPECIFIED); + if (dma_memory_read(xhci->as, addr, buf, len, + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n", + __func__); + memset(buf, 0xff, len); + xhci_die(xhci); + return; + } for (i = 0; i < (len / sizeof(uint32_t)); i++) { buf[i] = le32_to_cpu(buf[i]); @@ -495,7 +509,7 @@ static inline void xhci_dma_read_u32s(XHCIState *xhci, dma_addr_t addr, } static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr, - uint32_t *buf, size_t len) + const uint32_t *buf, size_t len) { int i; uint32_t tmp[5]; @@ -507,7 +521,13 @@ static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr, for (i = 0; i < n; i++) { tmp[i] = cpu_to_le32(buf[i]); } - dma_memory_write(xhci->as, addr, tmp, len, MEMTXATTRS_UNSPECIFIED); + if (dma_memory_write(xhci->as, addr, tmp, len, + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n", + __func__); + xhci_die(xhci); + return; + } } static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport) @@ -592,12 +612,6 @@ static inline int xhci_running(XHCIState *xhci) return !(xhci->usbsts & USBSTS_HCH); } -static void xhci_die(XHCIState *xhci) -{ - xhci->usbsts |= USBSTS_HCE; - DPRINTF("xhci: asserted controller error\n"); -} - static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v) { XHCIInterrupter *intr = &xhci->intr[v]; @@ -618,7 +632,12 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v) ev_trb.status, ev_trb.control); addr = intr->er_start + TRB_SIZE*intr->er_ep_idx; - dma_memory_write(xhci->as, addr, &ev_trb, TRB_SIZE, MEMTXATTRS_UNSPECIFIED); + if (dma_memory_write(xhci->as, addr, &ev_trb, TRB_SIZE, + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n", + __func__); + xhci_die(xhci); + } intr->er_ep_idx++; if (intr->er_ep_idx >= intr->er_size) { @@ -679,8 +698,12 @@ static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb, while (1) { TRBType type; - dma_memory_read(xhci->as, ring->dequeue, trb, TRB_SIZE, - MEMTXATTRS_UNSPECIFIED); + if (dma_memory_read(xhci->as, ring->dequeue, trb, TRB_SIZE, + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n", + __func__); + return 0; + } trb->addr = ring->dequeue; trb->ccs = ring->ccs; le64_to_cpus(&trb->parameter); @@ -725,10 +748,14 @@ static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring) bool control_td_set = 0; uint32_t link_cnt = 0; - while (1) { + do { TRBType type; - dma_memory_read(xhci->as, dequeue, &trb, TRB_SIZE, - MEMTXATTRS_UNSPECIFIED); + if (dma_memory_read(xhci->as, dequeue, &trb, TRB_SIZE, + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n", + __func__); + return -1; + } le64_to_cpus(&trb.parameter); le32_to_cpus(&trb.status); le32_to_cpus(&trb.control); @@ -762,7 +789,17 @@ static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring) if (!control_td_set && !(trb.control & TRB_TR_CH)) { return length; } - } + + /* + * According to the xHCI spec, Transfer Ring segments should have + * a maximum size of 64 kB (see chapter "6 Data Structures") + */ + } while (length < TRB_LINK_LIMIT * 65536 / TRB_SIZE); + + qemu_log_mask(LOG_GUEST_ERROR, "%s: exceeded maximum transfer ring size!\n", + __func__); + + return -1; } static void xhci_er_reset(XHCIState *xhci, int v) @@ -783,8 +820,14 @@ static void xhci_er_reset(XHCIState *xhci, int v) xhci_die(xhci); return; } - dma_memory_read(xhci->as, erstba, &seg, sizeof(seg), - MEMTXATTRS_UNSPECIFIED); + if (dma_memory_read(xhci->as, erstba, &seg, sizeof(seg), + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n", + __func__); + xhci_die(xhci); + return; + } + le32_to_cpus(&seg.addr_low); le32_to_cpus(&seg.addr_high); le32_to_cpus(&seg.size); @@ -977,7 +1020,9 @@ static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, } sctx = epctx->pstreams + streamid; } else { - FIXME("secondary streams not implemented yet"); + fprintf(stderr, "xhci: FIXME: secondary streams not implemented yet"); + *cc_error = CC_INVALID_STREAM_TYPE_ERROR; + return NULL; } if (sctx->sct == -1) { @@ -1849,7 +1894,7 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) } if (epctx->retry) { - XHCITransfer *xfer = epctx->retry; + xfer = epctx->retry; trace_usb_xhci_xfer_retry(xfer); assert(xfer->running_retry); @@ -2389,7 +2434,6 @@ static void xhci_detach_slot(XHCIState *xhci, USBPort *uport) static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx) { dma_addr_t ctx; - uint8_t bw_ctx[xhci->numports+1]; DPRINTF("xhci_get_port_bandwidth()\n"); @@ -2397,11 +2441,14 @@ static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx) DPRINTF("xhci: bandwidth context at "DMA_ADDR_FMT"\n", ctx); - /* TODO: actually implement real values here */ - bw_ctx[0] = 0; - memset(&bw_ctx[1], 80, xhci->numports); /* 80% */ - dma_memory_write(xhci->as, ctx, bw_ctx, sizeof(bw_ctx), - MEMTXATTRS_UNSPECIFIED); + /* TODO: actually implement real values here. This is 80% for all ports. */ + if (stb_dma(xhci->as, ctx, 0, MEMTXATTRS_UNSPECIFIED) != MEMTX_OK || + dma_memory_set(xhci->as, ctx + 1, 80, xhci->numports, + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory write failed!\n", + __func__); + return CC_TRB_ERROR; + } return CC_SUCCESS; } @@ -3269,7 +3316,8 @@ static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep, DPRINTF("%s\n", __func__); slotid = ep->dev->addr; - if (slotid == 0 || !xhci->slots[slotid-1].enabled) { + if (slotid == 0 || slotid > xhci->numslots || + !xhci->slots[slotid - 1].enabled) { DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr); return; } @@ -3474,7 +3522,7 @@ static int usb_xhci_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_xhci_ring = { .name = "xhci-ring", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(dequeue, XHCIRing), VMSTATE_BOOL(ccs, XHCIRing), VMSTATE_END_OF_LIST() @@ -3484,7 +3532,7 @@ static const VMStateDescription vmstate_xhci_ring = { static const VMStateDescription vmstate_xhci_port = { .name = "xhci-port", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(portsc, XHCIPort), VMSTATE_END_OF_LIST() } @@ -3493,7 +3541,7 @@ static const VMStateDescription vmstate_xhci_port = { static const VMStateDescription vmstate_xhci_slot = { .name = "xhci-slot", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(enabled, XHCISlot), VMSTATE_BOOL(addressed, XHCISlot), VMSTATE_END_OF_LIST() @@ -3503,7 +3551,7 @@ static const VMStateDescription vmstate_xhci_slot = { static const VMStateDescription vmstate_xhci_event = { .name = "xhci-event", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(type, XHCIEvent), VMSTATE_UINT32(ccode, XHCIEvent), VMSTATE_UINT64(ptr, XHCIEvent), @@ -3523,7 +3571,7 @@ static bool xhci_er_full(void *opaque, int version_id) static const VMStateDescription vmstate_xhci_intr = { .name = "xhci-intr", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { /* registers */ VMSTATE_UINT32(iman, XHCIInterrupter), VMSTATE_UINT32(imod, XHCIInterrupter), @@ -3556,7 +3604,7 @@ const VMStateDescription vmstate_xhci = { .name = "xhci-core", .version_id = 1, .post_load = usb_xhci_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_VARRAY_UINT32(ports, XHCIState, numports, 1, vmstate_xhci_port, XHCIPort), VMSTATE_STRUCT_VARRAY_UINT32(slots, XHCIState, numslots, 1, diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 28f8af8941..80122b4125 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1010,7 +1010,7 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev, int hostfd) * Speeds are defined in linux/usb/ch9.h, file not included * due to name conflicts. */ - int rc = ioctl(hostfd, USBDEVFS_GET_SPEED, NULL); + rc = ioctl(hostfd, USBDEVFS_GET_SPEED, NULL); switch (rc) { case 1: /* low */ libusb_speed = LIBUSB_SPEED_LOW; @@ -1141,7 +1141,8 @@ static void usb_host_nodev_bh(void *opaque) static void usb_host_nodev(USBHostDevice *s) { if (!s->bh_nodev) { - s->bh_nodev = qemu_bh_new(usb_host_nodev_bh, s); + s->bh_nodev = qemu_bh_new_guarded(usb_host_nodev_bh, s, + &DEVICE(s)->mem_reentrancy_guard); } qemu_bh_schedule(s->bh_nodev); } @@ -1739,7 +1740,8 @@ static int usb_host_post_load(void *opaque, int version_id) USBHostDevice *dev = opaque; if (!dev->bh_postld) { - dev->bh_postld = qemu_bh_new(usb_host_post_load_bh, dev); + dev->bh_postld = qemu_bh_new_guarded(usb_host_post_load_bh, dev, + &DEVICE(dev)->mem_reentrancy_guard); } qemu_bh_schedule(dev->bh_postld); dev->bh_postld_pending = true; @@ -1751,7 +1753,7 @@ static const VMStateDescription vmstate_usb_host = { .version_id = 1, .minimum_version_id = 1, .post_load = usb_host_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_USB_DEVICE(parent_obj, USBHostDevice), VMSTATE_END_OF_LIST() } @@ -1837,7 +1839,6 @@ static void usb_host_auto_check(void *unused) struct USBAutoFilter *f; libusb_device **devs = NULL; struct libusb_device_descriptor ddesc; - int unconnected = 0; int i, n; if (usb_host_init() != 0) { @@ -1897,9 +1898,6 @@ static void usb_host_auto_check(void *unused) libusb_free_device_list(devs, 1); QTAILQ_FOREACH(s, &hostdevs, next) { - if (s->dh == NULL) { - unconnected++; - } if (s->seen == 0) { if (s->dh) { usb_host_close(s); @@ -1908,17 +1906,6 @@ static void usb_host_auto_check(void *unused) } s->seen = 0; } - -#if 0 - if (unconnected == 0) { - /* nothing to watch */ - if (usb_auto_timer) { - timer_del(usb_auto_timer); - trace_usb_host_auto_scan_disabled(); - } - return; - } -#endif } if (!usb_vmstate) { diff --git a/hw/usb/imx-usb-phy.c b/hw/usb/imx-usb-phy.c index 5d7a549e34..18917d7599 100644 --- a/hw/usb/imx-usb-phy.c +++ b/hw/usb/imx-usb-phy.c @@ -13,13 +13,14 @@ #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[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(usbphy, IMXUSBPHYState, USBPHY_MAX), VMSTATE_END_OF_LIST() }, @@ -90,7 +91,15 @@ static uint64_t imx_usbphy_read(void *opaque, hwaddr offset, unsigned size) value = s->usbphy[index - 3]; break; default: - value = s->usbphy[index]; + if (index < USBPHY_MAX) { + value = s->usbphy[index]; + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Read from non-existing USB PHY register 0x%" + HWADDR_PRIx "\n", + __func__, offset); + value = 0; + } break; } return (uint64_t)value; @@ -168,7 +177,13 @@ static void imx_usbphy_write(void *opaque, hwaddr offset, uint64_t value, s->usbphy[index - 3] ^= value; break; default: - /* Other registers are read-only */ + /* Other registers are read-only or do not exist */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Write to %s USB PHY register 0x%" + HWADDR_PRIx "\n", + __func__, + index >= USBPHY_MAX ? "non-existing" : "read-only", + offset); break; } } diff --git a/hw/usb/meson.build b/hw/usb/meson.build index de853d780d..aac3bb35f2 100644 --- a/hw/usb/meson.build +++ b/hw/usb/meson.build @@ -1,7 +1,7 @@ hw_usb_modules = {} # usb subsystem core -softmmu_ss.add(when: 'CONFIG_USB', if_true: files( +system_ss.add(when: 'CONFIG_USB', if_true: files( 'bus.c', 'combined-packet.c', 'core.c', @@ -12,42 +12,45 @@ softmmu_ss.add(when: 'CONFIG_USB', if_true: files( )) # usb host adapters -softmmu_ss.add(when: 'CONFIG_USB_UHCI', if_true: files('hcd-uhci.c')) -softmmu_ss.add(when: 'CONFIG_USB_OHCI', if_true: files('hcd-ohci.c')) -softmmu_ss.add(when: 'CONFIG_USB_OHCI_PCI', if_true: files('hcd-ohci-pci.c')) -softmmu_ss.add(when: 'CONFIG_USB_EHCI', if_true: files('hcd-ehci.c')) -softmmu_ss.add(when: 'CONFIG_USB_EHCI_PCI', if_true: files('hcd-ehci-pci.c')) -softmmu_ss.add(when: 'CONFIG_USB_EHCI_SYSBUS', if_true: files('hcd-ehci.c', 'hcd-ehci-sysbus.c')) -softmmu_ss.add(when: 'CONFIG_USB_XHCI', if_true: files('hcd-xhci.c')) -softmmu_ss.add(when: 'CONFIG_USB_XHCI_PCI', if_true: files('hcd-xhci-pci.c')) -softmmu_ss.add(when: 'CONFIG_USB_XHCI_SYSBUS', if_true: files('hcd-xhci-sysbus.c')) -softmmu_ss.add(when: 'CONFIG_USB_XHCI_NEC', if_true: files('hcd-xhci-nec.c')) -softmmu_ss.add(when: 'CONFIG_USB_MUSB', if_true: files('hcd-musb.c')) -softmmu_ss.add(when: 'CONFIG_USB_DWC2', if_true: files('hcd-dwc2.c')) -softmmu_ss.add(when: 'CONFIG_USB_DWC3', if_true: files('hcd-dwc3.c')) +system_ss.add(when: 'CONFIG_USB_UHCI', if_true: files('hcd-uhci.c')) +system_ss.add(when: 'CONFIG_USB_OHCI', if_true: files('hcd-ohci.c')) +system_ss.add(when: 'CONFIG_USB_OHCI_PCI', if_true: files('hcd-ohci-pci.c')) +system_ss.add(when: 'CONFIG_USB_OHCI_SYSBUS', if_true: files('hcd-ohci-sysbus.c')) +system_ss.add(when: 'CONFIG_USB_EHCI', if_true: files('hcd-ehci.c')) +system_ss.add(when: 'CONFIG_USB_EHCI_PCI', if_true: files('hcd-ehci-pci.c')) +system_ss.add(when: 'CONFIG_USB_EHCI_SYSBUS', if_true: files('hcd-ehci-sysbus.c')) +system_ss.add(when: 'CONFIG_USB_XHCI', if_true: files('hcd-xhci.c')) +system_ss.add(when: 'CONFIG_USB_XHCI_PCI', if_true: files('hcd-xhci-pci.c')) +system_ss.add(when: 'CONFIG_USB_XHCI_SYSBUS', if_true: files('hcd-xhci-sysbus.c')) +system_ss.add(when: 'CONFIG_USB_XHCI_NEC', if_true: files('hcd-xhci-nec.c')) +system_ss.add(when: 'CONFIG_USB_MUSB', if_true: files('hcd-musb.c')) +system_ss.add(when: 'CONFIG_USB_DWC2', if_true: files('hcd-dwc2.c')) +system_ss.add(when: 'CONFIG_USB_DWC3', if_true: files('hcd-dwc3.c')) -softmmu_ss.add(when: 'CONFIG_TUSB6010', if_true: files('tusb6010.c')) -softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('chipidea.c')) -softmmu_ss.add(when: 'CONFIG_IMX_USBPHY', if_true: files('imx-usb-phy.c')) -softmmu_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686-uhci-pci.c')) -specific_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-usb2-ctrl-regs.c')) -specific_ss.add(when: 'CONFIG_XLNX_USB_SUBSYS', if_true: files('xlnx-usb-subsystem.c')) +system_ss.add(when: 'CONFIG_TUSB6010', if_true: files('tusb6010.c')) +system_ss.add(when: 'CONFIG_IMX', if_true: files('chipidea.c')) +system_ss.add(when: 'CONFIG_IMX_USBPHY', if_true: files('imx-usb-phy.c')) +system_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686-uhci-pci.c')) +system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-usb2-ctrl-regs.c')) +system_ss.add(when: 'CONFIG_XLNX_USB_SUBSYS', if_true: files('xlnx-usb-subsystem.c')) # emulated usb devices -softmmu_ss.add(when: 'CONFIG_USB', if_true: files('dev-hub.c')) -softmmu_ss.add(when: 'CONFIG_USB', if_true: files('dev-hid.c')) -softmmu_ss.add(when: 'CONFIG_USB_TABLET_WACOM', if_true: files('dev-wacom.c')) -softmmu_ss.add(when: 'CONFIG_USB_STORAGE_CORE', if_true: files('dev-storage.c')) -softmmu_ss.add(when: 'CONFIG_USB_STORAGE_BOT', if_true: files('dev-storage-bot.c')) -softmmu_ss.add(when: 'CONFIG_USB_STORAGE_CLASSIC', if_true: files('dev-storage-classic.c')) -softmmu_ss.add(when: 'CONFIG_USB_STORAGE_UAS', if_true: files('dev-uas.c')) -softmmu_ss.add(when: 'CONFIG_USB_AUDIO', if_true: files('dev-audio.c')) -softmmu_ss.add(when: 'CONFIG_USB_SERIAL', if_true: files('dev-serial.c')) -softmmu_ss.add(when: 'CONFIG_USB_NETWORK', if_true: files('dev-network.c')) -softmmu_ss.add(when: ['CONFIG_POSIX', 'CONFIG_USB_STORAGE_MTP'], if_true: files('dev-mtp.c')) +system_ss.add(when: 'CONFIG_USB', if_true: files('dev-hub.c')) +system_ss.add(when: 'CONFIG_USB', if_true: files('dev-hid.c')) +system_ss.add(when: 'CONFIG_USB_TABLET_WACOM', if_true: files('dev-wacom.c')) +system_ss.add(when: 'CONFIG_USB_STORAGE_CORE', if_true: files('dev-storage.c')) +system_ss.add(when: 'CONFIG_USB_STORAGE_BOT', if_true: files('dev-storage-bot.c')) +system_ss.add(when: 'CONFIG_USB_STORAGE_CLASSIC', if_true: files('dev-storage-classic.c')) +system_ss.add(when: 'CONFIG_USB_STORAGE_UAS', if_true: files('dev-uas.c')) +system_ss.add(when: 'CONFIG_USB_AUDIO', if_true: files('dev-audio.c')) +system_ss.add(when: 'CONFIG_USB_SERIAL', if_true: files('dev-serial.c')) +system_ss.add(when: 'CONFIG_USB_NETWORK', if_true: files('dev-network.c')) +if host_os != 'windows' + system_ss.add(when: 'CONFIG_USB_STORAGE_MTP', if_true: files('dev-mtp.c')) +endif # smartcard -softmmu_ss.add(when: 'CONFIG_USB_SMARTCARD', if_true: files('dev-smartcard-reader.c')) +system_ss.add(when: 'CONFIG_USB_SMARTCARD', if_true: files('dev-smartcard-reader.c')) if cacard.found() usbsmartcard_ss = ss.source_set() @@ -57,10 +60,17 @@ if cacard.found() endif # U2F -softmmu_ss.add(when: 'CONFIG_USB_U2F', if_true: files('u2f.c')) -softmmu_ss.add(when: ['CONFIG_LINUX', 'CONFIG_USB_U2F'], if_true: [libudev, files('u2f-passthru.c')]) +system_ss.add(when: 'CONFIG_USB_U2F', if_true: files('u2f.c')) +if host_os == 'linux' + system_ss.add(when: 'CONFIG_USB_U2F', if_true: [libudev, files('u2f-passthru.c')]) +endif if u2f.found() - softmmu_ss.add(when: 'CONFIG_USB_U2F', if_true: [u2f, files('u2f-emulated.c')]) + system_ss.add(when: 'CONFIG_USB_U2F', if_true: [u2f, files('u2f-emulated.c')]) +endif + +# CanoKey +if canokey.found() + system_ss.add(when: 'CONFIG_USB_CANOKEY', if_true: [canokey, files('canokey.c')]) endif # usb redirect @@ -79,6 +89,6 @@ if libusb.found() hw_usb_modules += {'host': usbhost_ss} endif -softmmu_ss.add(when: ['CONFIG_USB', 'CONFIG_XEN', libusb], if_true: files('xen-usb.c')) +system_ss.add(when: ['CONFIG_USB', 'CONFIG_XEN_BUS', libusb], if_true: files('xen-usb.c')) modules += { 'hw-usb': hw_usb_modules } diff --git a/hw/usb/quirks-pl2303-ids.h b/hw/usb/quirks-pl2303-ids.h index 8dbdb46ffe..28dd8da61c 100644 --- a/hw/usb/quirks-pl2303-ids.h +++ b/hw/usb/quirks-pl2303-ids.h @@ -1,150 +1,150 @@ /* * Prolific PL2303 USB to serial adaptor driver header file * - * 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 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. * */ -#define BENQ_VENDOR_ID 0x04a5 -#define BENQ_PRODUCT_ID_S81 0x4027 +#define BENQ_VENDOR_ID 0x04a5 +#define BENQ_PRODUCT_ID_S81 0x4027 -#define PL2303_VENDOR_ID 0x067b -#define PL2303_PRODUCT_ID 0x2303 -#define PL2303_PRODUCT_ID_RSAQ2 0x04bb -#define PL2303_PRODUCT_ID_DCU11 0x1234 -#define PL2303_PRODUCT_ID_PHAROS 0xaaa0 -#define PL2303_PRODUCT_ID_RSAQ3 0xaaa2 -#define PL2303_PRODUCT_ID_ALDIGA 0x0611 -#define PL2303_PRODUCT_ID_MMX 0x0612 -#define PL2303_PRODUCT_ID_GPRS 0x0609 -#define PL2303_PRODUCT_ID_HCR331 0x331a -#define PL2303_PRODUCT_ID_MOTOROLA 0x0307 +#define PL2303_VENDOR_ID 0x067b +#define PL2303_PRODUCT_ID 0x2303 +#define PL2303_PRODUCT_ID_RSAQ2 0x04bb +#define PL2303_PRODUCT_ID_DCU11 0x1234 +#define PL2303_PRODUCT_ID_PHAROS 0xaaa0 +#define PL2303_PRODUCT_ID_RSAQ3 0xaaa2 +#define PL2303_PRODUCT_ID_ALDIGA 0x0611 +#define PL2303_PRODUCT_ID_MMX 0x0612 +#define PL2303_PRODUCT_ID_GPRS 0x0609 +#define PL2303_PRODUCT_ID_HCR331 0x331a +#define PL2303_PRODUCT_ID_MOTOROLA 0x0307 -#define ATEN_VENDOR_ID 0x0557 -#define ATEN_VENDOR_ID2 0x0547 -#define ATEN_PRODUCT_ID 0x2008 +#define ATEN_VENDOR_ID 0x0557 +#define ATEN_VENDOR_ID2 0x0547 +#define ATEN_PRODUCT_ID 0x2008 -#define IODATA_VENDOR_ID 0x04bb -#define IODATA_PRODUCT_ID 0x0a03 -#define IODATA_PRODUCT_ID_RSAQ5 0x0a0e +#define IODATA_VENDOR_ID 0x04bb +#define IODATA_PRODUCT_ID 0x0a03 +#define IODATA_PRODUCT_ID_RSAQ5 0x0a0e -#define ELCOM_VENDOR_ID 0x056e -#define ELCOM_PRODUCT_ID 0x5003 -#define ELCOM_PRODUCT_ID_UCSGT 0x5004 +#define ELCOM_VENDOR_ID 0x056e +#define ELCOM_PRODUCT_ID 0x5003 +#define ELCOM_PRODUCT_ID_UCSGT 0x5004 -#define ITEGNO_VENDOR_ID 0x0eba -#define ITEGNO_PRODUCT_ID 0x1080 -#define ITEGNO_PRODUCT_ID_2080 0x2080 +#define ITEGNO_VENDOR_ID 0x0eba +#define ITEGNO_PRODUCT_ID 0x1080 +#define ITEGNO_PRODUCT_ID_2080 0x2080 -#define MA620_VENDOR_ID 0x0df7 -#define MA620_PRODUCT_ID 0x0620 +#define MA620_VENDOR_ID 0x0df7 +#define MA620_PRODUCT_ID 0x0620 -#define RATOC_VENDOR_ID 0x0584 -#define RATOC_PRODUCT_ID 0xb000 +#define RATOC_VENDOR_ID 0x0584 +#define RATOC_PRODUCT_ID 0xb000 -#define TRIPP_VENDOR_ID 0x2478 -#define TRIPP_PRODUCT_ID 0x2008 +#define TRIPP_VENDOR_ID 0x2478 +#define TRIPP_PRODUCT_ID 0x2008 -#define RADIOSHACK_VENDOR_ID 0x1453 -#define RADIOSHACK_PRODUCT_ID 0x4026 +#define RADIOSHACK_VENDOR_ID 0x1453 +#define RADIOSHACK_PRODUCT_ID 0x4026 -#define DCU10_VENDOR_ID 0x0731 -#define DCU10_PRODUCT_ID 0x0528 +#define DCU10_VENDOR_ID 0x0731 +#define DCU10_PRODUCT_ID 0x0528 -#define SITECOM_VENDOR_ID 0x6189 -#define SITECOM_PRODUCT_ID 0x2068 +#define SITECOM_VENDOR_ID 0x6189 +#define SITECOM_PRODUCT_ID 0x2068 /* Alcatel OT535/735 USB cable */ -#define ALCATEL_VENDOR_ID 0x11f7 -#define ALCATEL_PRODUCT_ID 0x02df +#define ALCATEL_VENDOR_ID 0x11f7 +#define ALCATEL_PRODUCT_ID 0x02df /* Samsung I330 phone cradle */ -#define SAMSUNG_VENDOR_ID 0x04e8 -#define SAMSUNG_PRODUCT_ID 0x8001 +#define SAMSUNG_VENDOR_ID 0x04e8 +#define SAMSUNG_PRODUCT_ID 0x8001 -#define SIEMENS_VENDOR_ID 0x11f5 -#define SIEMENS_PRODUCT_ID_SX1 0x0001 -#define SIEMENS_PRODUCT_ID_X65 0x0003 -#define SIEMENS_PRODUCT_ID_X75 0x0004 -#define SIEMENS_PRODUCT_ID_EF81 0x0005 +#define SIEMENS_VENDOR_ID 0x11f5 +#define SIEMENS_PRODUCT_ID_SX1 0x0001 +#define SIEMENS_PRODUCT_ID_X65 0x0003 +#define SIEMENS_PRODUCT_ID_X75 0x0004 +#define SIEMENS_PRODUCT_ID_EF81 0x0005 -#define SYNTECH_VENDOR_ID 0x0745 -#define SYNTECH_PRODUCT_ID 0x0001 +#define SYNTECH_VENDOR_ID 0x0745 +#define SYNTECH_PRODUCT_ID 0x0001 /* Nokia CA-42 Cable */ -#define NOKIA_CA42_VENDOR_ID 0x078b -#define NOKIA_CA42_PRODUCT_ID 0x1234 +#define NOKIA_CA42_VENDOR_ID 0x078b +#define NOKIA_CA42_PRODUCT_ID 0x1234 /* CA-42 CLONE Cable www.ca-42.com chipset: Prolific Technology Inc */ -#define CA_42_CA42_VENDOR_ID 0x10b5 -#define CA_42_CA42_PRODUCT_ID 0xac70 +#define CA_42_CA42_VENDOR_ID 0x10b5 +#define CA_42_CA42_PRODUCT_ID 0xac70 -#define SAGEM_VENDOR_ID 0x079b -#define SAGEM_PRODUCT_ID 0x0027 +#define SAGEM_VENDOR_ID 0x079b +#define SAGEM_PRODUCT_ID 0x0027 /* Leadtek GPS 9531 (ID 0413:2101) */ -#define LEADTEK_VENDOR_ID 0x0413 -#define LEADTEK_9531_PRODUCT_ID 0x2101 +#define LEADTEK_VENDOR_ID 0x0413 +#define LEADTEK_9531_PRODUCT_ID 0x2101 /* USB GSM cable from Speed Dragon Multimedia, Ltd */ -#define SPEEDDRAGON_VENDOR_ID 0x0e55 -#define SPEEDDRAGON_PRODUCT_ID 0x110b +#define SPEEDDRAGON_VENDOR_ID 0x0e55 +#define SPEEDDRAGON_PRODUCT_ID 0x110b /* DATAPILOT Universal-2 Phone Cable */ -#define DATAPILOT_U2_VENDOR_ID 0x0731 -#define DATAPILOT_U2_PRODUCT_ID 0x2003 +#define DATAPILOT_U2_VENDOR_ID 0x0731 +#define DATAPILOT_U2_PRODUCT_ID 0x2003 /* Belkin "F5U257" Serial Adapter */ -#define BELKIN_VENDOR_ID 0x050d -#define BELKIN_PRODUCT_ID 0x0257 +#define BELKIN_VENDOR_ID 0x050d +#define BELKIN_PRODUCT_ID 0x0257 /* Alcor Micro Corp. USB 2.0 TO RS-232 */ -#define ALCOR_VENDOR_ID 0x058F -#define ALCOR_PRODUCT_ID 0x9720 +#define ALCOR_VENDOR_ID 0x058F +#define ALCOR_PRODUCT_ID 0x9720 /* Willcom WS002IN Data Driver (by NetIndex Inc.) */ -#define WS002IN_VENDOR_ID 0x11f6 -#define WS002IN_PRODUCT_ID 0x2001 +#define WS002IN_VENDOR_ID 0x11f6 +#define WS002IN_PRODUCT_ID 0x2001 /* Corega CG-USBRS232R Serial Adapter */ -#define COREGA_VENDOR_ID 0x07aa -#define COREGA_PRODUCT_ID 0x002a +#define COREGA_VENDOR_ID 0x07aa +#define COREGA_PRODUCT_ID 0x002a /* Y.C. Cable U.S.A., Inc - USB to RS-232 */ -#define YCCABLE_VENDOR_ID 0x05ad -#define YCCABLE_PRODUCT_ID 0x0fba +#define YCCABLE_VENDOR_ID 0x05ad +#define YCCABLE_PRODUCT_ID 0x0fba /* "Superial" USB - Serial */ -#define SUPERIAL_VENDOR_ID 0x5372 -#define SUPERIAL_PRODUCT_ID 0x2303 +#define SUPERIAL_VENDOR_ID 0x5372 +#define SUPERIAL_PRODUCT_ID 0x2303 /* Hewlett-Packard LD220-HP POS Pole Display */ -#define HP_VENDOR_ID 0x03f0 -#define HP_LD220_PRODUCT_ID 0x3524 +#define HP_VENDOR_ID 0x03f0 +#define HP_LD220_PRODUCT_ID 0x3524 /* Cressi Edy (diving computer) PC interface */ -#define CRESSI_VENDOR_ID 0x04b8 -#define CRESSI_EDY_PRODUCT_ID 0x0521 +#define CRESSI_VENDOR_ID 0x04b8 +#define CRESSI_EDY_PRODUCT_ID 0x0521 /* Zeagle dive computer interface */ -#define ZEAGLE_VENDOR_ID 0x04b8 -#define ZEAGLE_N2ITION3_PRODUCT_ID 0x0522 +#define ZEAGLE_VENDOR_ID 0x04b8 +#define ZEAGLE_N2ITION3_PRODUCT_ID 0x0522 /* Sony, USB data cable for CMD-Jxx mobile phones */ -#define SONY_VENDOR_ID 0x054c -#define SONY_QN3USB_PRODUCT_ID 0x0437 +#define SONY_VENDOR_ID 0x054c +#define SONY_QN3USB_PRODUCT_ID 0x0437 /* Sanwa KB-USB2 multimeter cable (ID: 11ad:0001) */ -#define SANWA_VENDOR_ID 0x11ad -#define SANWA_PRODUCT_ID 0x0001 +#define SANWA_VENDOR_ID 0x11ad +#define SANWA_PRODUCT_ID 0x0001 /* ADLINK ND-6530 RS232,RS485 and RS422 adapter */ -#define ADLINK_VENDOR_ID 0x0b63 -#define ADLINK_ND6530_PRODUCT_ID 0x6530 +#define ADLINK_VENDOR_ID 0x0b63 +#define ADLINK_ND6530_PRODUCT_ID 0x6530 /* SMART USB Serial Adapter */ -#define SMART_VENDOR_ID 0x0b8c -#define SMART_PRODUCT_ID 0x2303 +#define SMART_VENDOR_ID 0x0b8c +#define SMART_PRODUCT_ID 0x2303 diff --git a/hw/usb/quirks.h b/hw/usb/quirks.h index c3e595f40b..94b2c95341 100644 --- a/hw/usb/quirks.h +++ b/hw/usb/quirks.h @@ -67,7 +67,7 @@ static const struct usb_device_id usbredir_raw_serial_ids[] = { { USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */ { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */ { USB_DEVICE(0x10C4, 0x8044) }, /* Cygnal Debug Adapter */ - { USB_DEVICE(0x10C4, 0x804E) }, /* Software Bisque Paramount ME build-in converter */ + { USB_DEVICE(0x10C4, 0x804E) }, /* Software Bisque Paramount ME built-in converter */ { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */ { USB_DEVICE(0x10C4, 0x8054) }, /* Enfora GSM2228 */ { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */ diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index fd7df599bc..0f2dd2e504 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -278,7 +278,7 @@ static gboolean usbredir_write_unblocked(void *do_not_use, GIOCondition cond, dev->watch = 0; usbredirparser_do_write(dev->parser); - return FALSE; + return G_SOURCE_REMOVE; } static int usbredir_write(void *priv, uint8_t *data, int count) @@ -471,7 +471,7 @@ static int bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len, DPRINTF("bufpq overflow, dropping packets ep %02X\n", ep); dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 1; } - /* Since we're interupting the stream anyways, drop enough packets to get + /* Since we're interrupting the stream anyways, drop enough packets to get back to our target buffer size */ if (dev->endpoint[EP2I(ep)].bufpq_dropping_packets) { if (dev->endpoint[EP2I(ep)].bufpq_size > @@ -1403,7 +1403,7 @@ static void usbredir_vm_state_change(void *priv, bool running, RunState state) { USBRedirDevice *dev = priv; - if (state == RUN_STATE_RUNNING && dev->parser != NULL) { + if (running && dev->parser != NULL) { usbredirparser_do_write(dev->parser); /* Flush any pending writes */ } } @@ -1441,8 +1441,10 @@ static void usbredir_realize(USBDevice *udev, Error **errp) } } - dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev); - dev->device_reject_bh = qemu_bh_new(usbredir_device_reject_bh, dev); + dev->chardev_close_bh = qemu_bh_new_guarded(usbredir_chardev_close_bh, dev, + &DEVICE(dev)->mem_reentrancy_guard); + dev->device_reject_bh = qemu_bh_new_guarded(usbredir_device_reject_bh, dev, + &DEVICE(dev)->mem_reentrancy_guard); dev->attach_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, usbredir_do_attach, dev); packet_id_queue_init(&dev->cancelled, dev, "cancelled"); @@ -2371,7 +2373,7 @@ static const VMStateDescription usbredir_bulk_receiving_vmstate = { .version_id = 1, .minimum_version_id = 1, .needed = usbredir_bulk_receiving_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(bulk_receiving_started, struct endp_data), VMSTATE_END_OF_LIST() } @@ -2389,7 +2391,7 @@ static const VMStateDescription usbredir_stream_vmstate = { .version_id = 1, .minimum_version_id = 1, .needed = usbredir_stream_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(max_streams, struct endp_data), VMSTATE_END_OF_LIST() } @@ -2399,7 +2401,7 @@ static const VMStateDescription usbredir_ep_vmstate = { .name = "usb-redir-ep", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(type, struct endp_data), VMSTATE_UINT8(interval, struct endp_data), VMSTATE_UINT8(interface, struct endp_data), @@ -2422,7 +2424,7 @@ static const VMStateDescription usbredir_ep_vmstate = { VMSTATE_INT32(bufpq_target_size, struct endp_data), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &usbredir_bulk_receiving_vmstate, &usbredir_stream_vmstate, NULL @@ -2479,7 +2481,7 @@ static const VMStateDescription usbredir_ep_packet_id_queue_vmstate = { .name = "usb-redir-packet-id-queue", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { { .name = "queue", .version_id = 0, @@ -2499,7 +2501,7 @@ static const VMStateDescription usbredir_device_info_vmstate = { .name = "usb-redir-device-info", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(speed, struct usb_redir_device_connect_header), VMSTATE_UINT8(device_class, struct usb_redir_device_connect_header), VMSTATE_UINT8(device_subclass, struct usb_redir_device_connect_header), @@ -2518,7 +2520,7 @@ static const VMStateDescription usbredir_interface_info_vmstate = { .name = "usb-redir-interface-info", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(interface_count, struct usb_redir_interface_info_header), VMSTATE_UINT8_ARRAY(interface, @@ -2541,7 +2543,7 @@ static const VMStateDescription usbredir_vmstate = { .minimum_version_id = 1, .pre_save = usbredir_pre_save, .post_load = usbredir_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_USB_DEVICE(dev, USBRedirDevice), VMSTATE_TIMER_PTR(attach_timer, USBRedirDevice), { diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 9773cb5330..ed7dc210d3 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -57,8 +57,12 @@ usb_ohci_ed_read_error(uint32_t addr) "ED read error at 0x%x" usb_ohci_ed_pkt(uint32_t cur, int h, int c, uint32_t head, uint32_t tail, uint32_t next) "ED @ 0x%.8x h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x" usb_ohci_ed_pkt_flags(uint32_t fa, uint32_t en, uint32_t d, int s, int k, int f, uint32_t mps) "fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u" usb_ohci_hcca_read_error(uint32_t addr) "HCCA read error at 0x%x" +usb_ohci_mem_read(uint32_t size, const char *name, uint32_t addr, uint32_t offs, uint32_t val) "%d %s 0x%x %d -> 0x%x" +usb_ohci_mem_port_read(uint32_t size, const char *name, uint32_t port, uint32_t addr, uint32_t offs, uint32_t val) "%d %s[%d] 0x%x %d -> 0x%x" usb_ohci_mem_read_unaligned(uint32_t addr) "at 0x%x" usb_ohci_mem_read_bad_offset(uint32_t addr) "0x%x" +usb_ohci_mem_write(uint32_t size, const char *name, uint32_t addr, uint32_t offs, uint32_t val) "%d %s 0x%x %d <- 0x%x" +usb_ohci_mem_port_write(uint32_t size, const char *name, uint32_t port, uint32_t addr, uint32_t offs, uint32_t val) "%d %s[%d] 0x%x %d <- 0x%x" usb_ohci_mem_write_unaligned(uint32_t addr) "at 0x%x" usb_ohci_mem_write_bad_offset(uint32_t addr) "0x%x" usb_ohci_process_lists(uint32_t head, uint32_t cur) "head 0x%x, cur 0x%x" @@ -244,7 +248,7 @@ usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret # dev-hub.c usb_hub_reset(int addr) "dev %d" -usb_hub_control(int addr, int request, int value, int index, int length) "dev %d, req 0x%x, value %d, index %d, langth %d" +usb_hub_control(int addr, int request, int value, int index, int length) "dev %d, req 0x%x, value %d, index %d, length %d" usb_hub_get_port_status(int addr, int nr, int status, int changed) "dev %d, port %d, status 0x%x, changed 0x%x" usb_hub_set_port_feature(int addr, int nr, const char *f) "dev %d, port %d, feature %s" usb_hub_clear_port_feature(int addr, int nr, const char *f) "dev %d, port %d, feature %s" @@ -263,6 +267,7 @@ usb_msd_packet_complete(void) "" usb_msd_cmd_submit(unsigned lun, unsigned tag, unsigned flags, unsigned len, unsigned data_len) "lun %u, tag 0x%x, flags 0x%08x, len %d, data-len %d" usb_msd_cmd_complete(unsigned status, unsigned tag) "status %d, tag 0x%x" usb_msd_cmd_cancel(unsigned tag) "tag 0x%x" +usb_msd_fatal_error(void) "" # dev-uas.c usb_uas_reset(int addr) "dev %d" @@ -345,3 +350,19 @@ usb_serial_set_baud(int bus, int addr, int baud) "dev %d:%u baud rate %d" usb_serial_set_data(int bus, int addr, int parity, int data, int stop) "dev %d:%u parity %c, data bits %d, stop bits %d" usb_serial_set_flow_control(int bus, int addr, int index) "dev %d:%u flow control %d" usb_serial_set_xonxoff(int bus, int addr, uint8_t xon, uint8_t xoff) "dev %d:%u xon 0x%x xoff 0x%x" + +# canokey.c +canokey_emu_stall_ep(uint8_t ep) "ep %d" +canokey_emu_set_address(uint8_t addr) "addr %d" +canokey_emu_prepare_receive(uint8_t ep, uint16_t size) "ep %d size %d" +canokey_emu_transmit(uint8_t ep, uint16_t size) "ep %d size %d" +canokey_thread_start(void) +canokey_thread_stop(void) +canokey_handle_reset(void) +canokey_handle_control_setup(int request, int value, int index, int length) "request 0x%04X value 0x%04X index 0x%04X length 0x%04X" +canokey_handle_control_out(void) +canokey_handle_control_in(int actual_len) "len %d" +canokey_handle_data_out(uint8_t ep_out, uint32_t out_len) "ep %d len %d" +canokey_handle_data_in(uint8_t ep_in, uint32_t in_len) "ep %d len %d" +canokey_realize(void) +canokey_unrealize(void) diff --git a/hw/usb/u2f-passthru.c b/hw/usb/u2f-passthru.c index fc93429c9c..b7025d303d 100644 --- a/hw/usb/u2f-passthru.c +++ b/hw/usb/u2f-passthru.c @@ -512,7 +512,7 @@ static const VMStateDescription u2f_passthru_vmstate = { .version_id = 1, .minimum_version_id = 1, .post_load = u2f_passthru_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_U2F_KEY(base, U2FPassthruState), VMSTATE_END_OF_LIST() } diff --git a/hw/usb/u2f.c b/hw/usb/u2f.c index 56001249a4..1fb59cf404 100644 --- a/hw/usb/u2f.c +++ b/hw/usb/u2f.c @@ -305,7 +305,7 @@ const VMStateDescription vmstate_u2f_key = { .name = "u2f-key", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_USB_DEVICE(dev, U2FKeyState), VMSTATE_UINT8(idle, U2FKeyState), VMSTATE_UINT8_2DARRAY(pending_in, U2FKeyState, diff --git a/hw/usb/u2f.h b/hw/usb/u2f.h index db30f3586b..8bff13141a 100644 --- a/hw/usb/u2f.h +++ b/hw/usb/u2f.h @@ -31,22 +31,16 @@ #define U2FHID_PACKET_SIZE 64 #define U2FHID_PENDING_IN_NUM 32 -typedef struct U2FKeyState U2FKeyState; typedef struct U2FKeyInfo U2FKeyInfo; #define TYPE_U2F_KEY "u2f-key" -#define U2F_KEY(obj) \ - OBJECT_CHECK(U2FKeyState, (obj), TYPE_U2F_KEY) -#define U2F_KEY_CLASS(klass) \ - OBJECT_CLASS_CHECK(U2FKeyClass, (klass), TYPE_U2F_KEY) -#define U2F_KEY_GET_CLASS(obj) \ - OBJECT_GET_CLASS(U2FKeyClass, (obj), TYPE_U2F_KEY) +OBJECT_DECLARE_TYPE(U2FKeyState, U2FKeyClass, U2F_KEY) /* * Callbacks to be used by the U2F key base device (i.e. hw/u2f.c) * to interact with its variants (i.e. hw/u2f-*.c) */ -typedef struct U2FKeyClass { +struct U2FKeyClass { /*< private >*/ USBDeviceClass parent_class; @@ -55,12 +49,12 @@ typedef struct U2FKeyClass { const uint8_t packet[U2FHID_PACKET_SIZE]); void (*realize)(U2FKeyState *key, Error **errp); void (*unrealize)(U2FKeyState *key); -} U2FKeyClass; +}; /* * State of the U2F key base device (i.e. hw/u2f.c) */ -typedef struct U2FKeyState { +struct U2FKeyState { USBDevice dev; USBEndpoint *ep; uint8_t idle; @@ -70,11 +64,11 @@ typedef struct U2FKeyState { uint8_t pending_in_start; uint8_t pending_in_end; uint8_t pending_in_num; -} U2FKeyState; +}; /* * API to be used by the U2F key device variants (i.e. hw/u2f-*.c) - * to interact with the the U2F key base device (i.e. hw/u2f.c) + * to interact with the U2F key base device (i.e. hw/u2f.c) */ void u2f_send_to_guest(U2FKeyState *key, const uint8_t packet[U2FHID_PACKET_SIZE]); diff --git a/hw/usb/vt82c686-uhci-pci.c b/hw/usb/vt82c686-uhci-pci.c index 0bf2b72ff0..6162806172 100644 --- a/hw/usb/vt82c686-uhci-pci.c +++ b/hw/usb/vt82c686-uhci-pci.c @@ -6,10 +6,7 @@ static void uhci_isa_set_irq(void *opaque, int irq_num, int level) { UHCIState *s = opaque; - uint8_t irq = pci_get_byte(s->dev.config + PCI_INTERRUPT_LINE); - if (irq > 0 && irq < 15) { - via_isa_set_irq(pci_get_function_0(&s->dev), irq, level); - } + via_isa_set_irq(&s->dev, 0, level); } static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp) @@ -31,7 +28,7 @@ static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp) static UHCIInfo uhci_info[] = { { - .name = "vt82c686b-usb-uhci", + .name = TYPE_VT82C686B_USB_UHCI, .vendor_id = PCI_VENDOR_ID_VIA, .device_id = PCI_DEVICE_ID_VIA_UHCI, .revision = 0x01, @@ -45,7 +42,7 @@ static UHCIInfo uhci_info[] = { static const TypeInfo vt82c686b_usb_uhci_type_info = { .parent = TYPE_UHCI, - .name = "vt82c686b-usb-uhci", + .name = TYPE_VT82C686B_USB_UHCI, .class_init = uhci_data_class_init, .class_data = uhci_info, }; diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 0f7369e7ed..09ec326aea 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -101,6 +101,8 @@ struct usbback_hotplug { struct usbback_info { struct XenLegacyDevice xendev; /* must be first */ USBBus bus; + uint32_t urb_ring_ref; + uint32_t conn_ring_ref; void *urb_sring; void *conn_sring; struct usbif_urb_back_ring urb_ring; @@ -159,7 +161,7 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) for (i = 0; i < nr_segs; i++) { if ((unsigned)usbback_req->req.seg[i].offset + - (unsigned)usbback_req->req.seg[i].length > XC_PAGE_SIZE) { + (unsigned)usbback_req->req.seg[i].length > XEN_PAGE_SIZE) { xen_pv_printf(xendev, 0, "segment crosses page boundary\n"); return -EINVAL; } @@ -183,7 +185,7 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) for (i = 0; i < usbback_req->nr_buffer_segs; i++) { seg = usbback_req->req.seg + i; - addr = usbback_req->buffer + i * XC_PAGE_SIZE + seg->offset; + addr = usbback_req->buffer + i * XEN_PAGE_SIZE + seg->offset; qemu_iovec_add(&usbback_req->packet.iov, addr, seg->length); } } @@ -277,10 +279,11 @@ static int usbback_init_packet(struct usbback_req *usbback_req) static void usbback_do_response(struct usbback_req *usbback_req, int32_t status, int32_t actual_length, int32_t error_count) { + uint32_t ref[USBIF_MAX_SEGMENTS_PER_REQUEST]; struct usbback_info *usbif; struct usbif_urb_response *res; struct XenLegacyDevice *xendev; - unsigned int notify; + unsigned int notify, i; usbif = usbback_req->usbif; xendev = &usbif->xendev; @@ -293,13 +296,19 @@ static void usbback_do_response(struct usbback_req *usbback_req, int32_t status, } if (usbback_req->buffer) { - xen_be_unmap_grant_refs(xendev, usbback_req->buffer, + for (i = 0; i < usbback_req->nr_buffer_segs; i++) { + ref[i] = usbback_req->req.seg[i].gref; + } + xen_be_unmap_grant_refs(xendev, usbback_req->buffer, ref, usbback_req->nr_buffer_segs); usbback_req->buffer = NULL; } if (usbback_req->isoc_buffer) { - xen_be_unmap_grant_refs(xendev, usbback_req->isoc_buffer, + for (i = 0; i < usbback_req->nr_extra_segs; i++) { + ref[i] = usbback_req->req.seg[i + usbback_req->req.nr_buffer_segs].gref; + } + xen_be_unmap_grant_refs(xendev, usbback_req->isoc_buffer, ref, usbback_req->nr_extra_segs); usbback_req->isoc_buffer = NULL; } @@ -442,7 +451,7 @@ static int usbback_check_and_submit(struct usbback_req *usbback_req) wValue = le16_to_cpu(ctrl->wValue); /* - * When the device is first connected or resetted, USB device has no + * When the device is first connected or reset, USB device has no * address. In this initial state, following requests are sent to device * address (#0), * @@ -832,11 +841,11 @@ static void usbback_disconnect(struct XenLegacyDevice *xendev) xen_pv_unbind_evtchn(xendev); if (usbif->urb_sring) { - xen_be_unmap_grant_ref(xendev, usbif->urb_sring); + xen_be_unmap_grant_ref(xendev, usbif->urb_sring, usbif->urb_ring_ref); usbif->urb_sring = NULL; } if (usbif->conn_sring) { - xen_be_unmap_grant_ref(xendev, usbif->conn_sring); + xen_be_unmap_grant_ref(xendev, usbif->conn_sring, usbif->conn_ring_ref); usbif->conn_sring = NULL; } @@ -889,10 +898,12 @@ static int usbback_connect(struct XenLegacyDevice *xendev) return -1; } + usbif->urb_ring_ref = urb_ring_ref; + usbif->conn_ring_ref = conn_ring_ref; urb_sring = usbif->urb_sring; conn_sring = usbif->conn_sring; - BACK_RING_INIT(&usbif->urb_ring, urb_sring, XC_PAGE_SIZE); - BACK_RING_INIT(&usbif->conn_ring, conn_sring, XC_PAGE_SIZE); + BACK_RING_INIT(&usbif->urb_ring, urb_sring, XEN_PAGE_SIZE); + BACK_RING_INIT(&usbif->conn_ring, conn_sring, XEN_PAGE_SIZE); xen_be_bind_evtchn(xendev); @@ -1021,7 +1032,8 @@ static void usbback_alloc(struct XenLegacyDevice *xendev) QTAILQ_INIT(&usbif->req_free_q); QSIMPLEQ_INIT(&usbif->hotplug_q); - usbif->bh = qemu_bh_new(usbback_bh, usbif); + usbif->bh = qemu_bh_new_guarded(usbback_bh, usbif, + &DEVICE(xendev)->mem_reentrancy_guard); } static int usbback_free(struct XenLegacyDevice *xendev) diff --git a/hw/usb/xlnx-versal-usb2-ctrl-regs.c b/hw/usb/xlnx-versal-usb2-ctrl-regs.c index 1c094aa1a6..6fc453817e 100644 --- a/hw/usb/xlnx-versal-usb2-ctrl-regs.c +++ b/hw/usb/xlnx-versal-usb2-ctrl-regs.c @@ -196,7 +196,7 @@ static const VMStateDescription vmstate_usb2_ctrl_regs = { .name = TYPE_XILINX_VERSAL_USB2_CTRL_REGS, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, VersalUsb2CtrlRegs, USB2_REGS_R_MAX), VMSTATE_END_OF_LIST(), } diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index e0dd561e85..7c4caa5938 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -11,13 +11,16 @@ */ #include "qemu/osdep.h" +#include CONFIG_DEVICES /* CONFIG_IOMMUFD */ #include #include #include "qapi/error.h" -#include "hw/vfio/vfio.h" #include "hw/vfio/vfio-common.h" +#include "sysemu/iommufd.h" #include "hw/s390x/ap-device.h" #include "qemu/error-report.h" +#include "qemu/event_notifier.h" +#include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/config-file.h" @@ -33,6 +36,7 @@ struct VFIOAPDevice { APDevice apdev; VFIODevice vdev; + EventNotifier req_notifier; }; OBJECT_DECLARE_SIMPLE_TYPE(VFIOAPDevice, VFIO_AP_DEVICE) @@ -50,99 +54,161 @@ struct VFIODeviceOps vfio_ap_ops = { .vfio_compute_needs_reset = vfio_ap_compute_needs_reset, }; -static void vfio_ap_put_device(VFIOAPDevice *vapdev) +static void vfio_ap_req_notifier_handler(void *opaque) { - g_free(vapdev->vdev.name); - vfio_put_base_device(&vapdev->vdev); + VFIOAPDevice *vapdev = opaque; + Error *err = NULL; + + if (!event_notifier_test_and_clear(&vapdev->req_notifier)) { + return; + } + + qdev_unplug(DEVICE(vapdev), &err); + + if (err) { + warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name); + } } -static VFIOGroup *vfio_ap_get_group(VFIOAPDevice *vapdev, Error **errp) +static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, + unsigned int irq, Error **errp) { - GError *gerror = NULL; - char *symlink, *group_path; - int groupid; + int fd; + size_t argsz; + IOHandler *fd_read; + EventNotifier *notifier; + struct vfio_irq_info *irq_info; + VFIODevice *vdev = &vapdev->vdev; - symlink = g_strdup_printf("%s/iommu_group", vapdev->vdev.sysfsdev); - group_path = g_file_read_link(symlink, &gerror); - g_free(symlink); - - if (!group_path) { - error_setg(errp, "%s: no iommu_group found for %s: %s", - TYPE_VFIO_AP_DEVICE, vapdev->vdev.sysfsdev, gerror->message); - g_error_free(gerror); - return NULL; + switch (irq) { + case VFIO_AP_REQ_IRQ_INDEX: + notifier = &vapdev->req_notifier; + fd_read = vfio_ap_req_notifier_handler; + break; + default: + error_setg(errp, "vfio: Unsupported device irq(%d)", irq); + return; } - if (sscanf(basename(group_path), "%d", &groupid) != 1) { - error_setg(errp, "vfio: failed to read %s", group_path); - g_free(group_path); - return NULL; + if (vdev->num_irqs < irq + 1) { + error_setg(errp, "vfio: IRQ %u not available (number of irqs %u)", + irq, vdev->num_irqs); + return; } - g_free(group_path); + argsz = sizeof(*irq_info); + irq_info = g_malloc0(argsz); + irq_info->index = irq; + irq_info->argsz = argsz; - return vfio_get_group(groupid, &address_space_memory, errp); + if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, + irq_info) < 0 || irq_info->count < 1) { + error_setg_errno(errp, errno, "vfio: Error getting irq info"); + goto out_free_info; + } + + if (event_notifier_init(notifier, 0)) { + error_setg_errno(errp, errno, + "vfio: Unable to init event notifier for irq (%d)", + irq); + goto out_free_info; + } + + fd = event_notifier_get_fd(notifier); + qemu_set_fd_handler(fd, fd_read, NULL, vapdev); + + if (vfio_set_irq_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, + errp)) { + qemu_set_fd_handler(fd, NULL, NULL, vapdev); + event_notifier_cleanup(notifier); + } + +out_free_info: + g_free(irq_info); + +} + +static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev, + unsigned int irq) +{ + Error *err = NULL; + EventNotifier *notifier; + + switch (irq) { + case VFIO_AP_REQ_IRQ_INDEX: + notifier = &vapdev->req_notifier; + break; + default: + error_report("vfio: Unsupported device irq(%d)", irq); + return; + } + + if (vfio_set_irq_signaling(&vapdev->vdev, irq, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { + warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name); + } + + qemu_set_fd_handler(event_notifier_get_fd(notifier), + NULL, NULL, vapdev); + event_notifier_cleanup(notifier); } static void vfio_ap_realize(DeviceState *dev, Error **errp) { + ERRP_GUARD(); int ret; - char *mdevid; - VFIOGroup *vfio_group; - APDevice *apdev = AP_DEVICE(dev); - VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); + Error *err = NULL; + VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev); + VFIODevice *vbasedev = &vapdev->vdev; - vfio_group = vfio_ap_get_group(vapdev, errp); - if (!vfio_group) { + if (vfio_device_get_name(vbasedev, errp) < 0) { return; } - vapdev->vdev.ops = &vfio_ap_ops; - vapdev->vdev.type = VFIO_DEVICE_TYPE_AP; - mdevid = basename(vapdev->vdev.sysfsdev); - vapdev->vdev.name = g_strdup_printf("%s", mdevid); - vapdev->vdev.dev = dev; - - /* - * vfio-ap devices operate in a way compatible with discarding of - * memory in RAM blocks, as no pages are pinned in the host. - * This needs to be set before vfio_get_device() for vfio common to - * handle ram_block_discard_disable(). - */ - vapdev->vdev.ram_block_discard_allowed = true; - - ret = vfio_get_device(vfio_group, mdevid, &vapdev->vdev, errp); + ret = vfio_attach_device(vbasedev->name, vbasedev, + &address_space_memory, errp); if (ret) { - goto out_get_dev_err; + goto error; + } + + vfio_ap_register_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX, &err); + if (err) { + /* + * Report this error, but do not make it a failing condition. + * Lack of this IRQ in the host does not prevent normal operation. + */ + error_report_err(err); } return; -out_get_dev_err: - vfio_ap_put_device(vapdev); - vfio_put_group(vfio_group); +error: + error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->name); + g_free(vbasedev->name); } static void vfio_ap_unrealize(DeviceState *dev) { - APDevice *apdev = AP_DEVICE(dev); - VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); - VFIOGroup *group = vapdev->vdev.group; + VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev); - vfio_ap_put_device(vapdev); - vfio_put_group(group); + vfio_ap_unregister_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX); + vfio_detach_device(&vapdev->vdev); + g_free(vapdev->vdev.name); } static Property vfio_ap_properties[] = { DEFINE_PROP_STRING("sysfsdev", VFIOAPDevice, vdev.sysfsdev), +#ifdef CONFIG_IOMMUFD + DEFINE_PROP_LINK("iommufd", VFIOAPDevice, vdev.iommufd, + TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), +#endif DEFINE_PROP_END_OF_LIST(), }; static void vfio_ap_reset(DeviceState *dev) { int ret; - APDevice *apdev = AP_DEVICE(dev); - VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); + VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev); ret = ioctl(vapdev->vdev.fd, VFIO_DEVICE_RESET); if (ret) { @@ -156,11 +222,36 @@ static const VMStateDescription vfio_ap_vmstate = { .unmigratable = 1, }; +static void vfio_ap_instance_init(Object *obj) +{ + VFIOAPDevice *vapdev = VFIO_AP_DEVICE(obj); + VFIODevice *vbasedev = &vapdev->vdev; + + /* + * vfio-ap devices operate in a way compatible with discarding of + * memory in RAM blocks, as no pages are pinned in the host. + * This needs to be set before vfio_get_device() for vfio common to + * handle ram_block_discard_disable(). + */ + vfio_device_init(vbasedev, VFIO_DEVICE_TYPE_AP, &vfio_ap_ops, + DEVICE(vapdev), true); +} + +#ifdef CONFIG_IOMMUFD +static void vfio_ap_set_fd(Object *obj, const char *str, Error **errp) +{ + vfio_device_set_fd(&VFIO_AP_DEVICE(obj)->vdev, str, errp); +} +#endif + static void vfio_ap_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_props(dc, vfio_ap_properties); +#ifdef CONFIG_IOMMUFD + object_class_property_add_str(klass, "fd", NULL, vfio_ap_set_fd); +#endif dc->vmsd = &vfio_ap_vmstate; dc->desc = "VFIO-based AP device assignment"; set_bit(DEVICE_CATEGORY_MISC, dc->categories); @@ -175,6 +266,7 @@ static const TypeInfo vfio_ap_info = { .name = TYPE_VFIO_AP_DEVICE, .parent = TYPE_AP_DEVICE, .instance_size = sizeof(VFIOAPDevice), + .instance_init = vfio_ap_instance_init, .class_init = vfio_ap_class_init, }; diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 0354737666..90e4a53437 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -15,13 +15,14 @@ */ #include "qemu/osdep.h" +#include CONFIG_DEVICES /* CONFIG_IOMMUFD */ #include #include #include #include "qapi/error.h" -#include "hw/vfio/vfio.h" #include "hw/vfio/vfio-common.h" +#include "sysemu/iommufd.h" #include "hw/s390x/s390-ccw.h" #include "hw/s390x/vfio-ccw.h" #include "hw/qdev-properties.h" @@ -76,8 +77,7 @@ struct VFIODeviceOps vfio_ccw_ops = { static IOInstEnding vfio_ccw_handle_request(SubchDev *sch) { - S390CCWDevice *cdev = sch->driver_data; - VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); + VFIOCCWDevice *vcdev = VFIO_CCW(sch->driver_data); struct ccw_io_region *region = vcdev->io_region; int ret; @@ -125,8 +125,7 @@ again: static IOInstEnding vfio_ccw_handle_store(SubchDev *sch) { - S390CCWDevice *cdev = sch->driver_data; - VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); + VFIOCCWDevice *vcdev = VFIO_CCW(sch->driver_data); SCHIB *schib = &sch->curr_status; struct ccw_schib_region *region = vcdev->schib_region; SCHIB *s; @@ -170,8 +169,7 @@ static IOInstEnding vfio_ccw_handle_store(SubchDev *sch) static int vfio_ccw_handle_clear(SubchDev *sch) { - S390CCWDevice *cdev = sch->driver_data; - VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); + VFIOCCWDevice *vcdev = VFIO_CCW(sch->driver_data); struct ccw_cmd_region *region = vcdev->async_cmd_region; int ret; @@ -210,8 +208,7 @@ again: static int vfio_ccw_handle_halt(SubchDev *sch) { - S390CCWDevice *cdev = sch->driver_data; - VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); + VFIOCCWDevice *vcdev = VFIO_CCW(sch->driver_data); struct ccw_cmd_region *region = vcdev->async_cmd_region; int ret; @@ -251,9 +248,7 @@ again: static void vfio_ccw_reset(DeviceState *dev) { - CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev); - S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev); - VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); + VFIOCCWDevice *vcdev = VFIO_CCW(dev); ioctl(vcdev->vdev.fd, VFIO_DEVICE_RESET); } @@ -315,8 +310,7 @@ static void vfio_ccw_io_notifier_handler(void *opaque) { VFIOCCWDevice *vcdev = opaque; struct ccw_io_region *region = vcdev->io_region; - S390CCWDevice *cdev = S390_CCW_DEVICE(vcdev); - CcwDevice *ccw_dev = CCW_DEVICE(cdev); + CcwDevice *ccw_dev = CCW_DEVICE(vcdev); SubchDev *sch = ccw_dev->sch; SCHIB *schib = &sch->curr_status; SCSW s; @@ -579,88 +573,14 @@ static void vfio_ccw_put_region(VFIOCCWDevice *vcdev) g_free(vcdev->io_region); } -static void vfio_ccw_put_device(VFIOCCWDevice *vcdev) -{ - g_free(vcdev->vdev.name); - vfio_put_base_device(&vcdev->vdev); -} - -static void vfio_ccw_get_device(VFIOGroup *group, VFIOCCWDevice *vcdev, - Error **errp) -{ - char *name = g_strdup_printf("%x.%x.%04x", vcdev->cdev.hostid.cssid, - vcdev->cdev.hostid.ssid, - vcdev->cdev.hostid.devid); - VFIODevice *vbasedev; - - QLIST_FOREACH(vbasedev, &group->device_list, next) { - if (strcmp(vbasedev->name, name) == 0) { - error_setg(errp, "vfio: subchannel %s has already been attached", - name); - goto out_err; - } - } - - /* - * All vfio-ccw devices are believed to operate in a way compatible with - * discarding of memory in RAM blocks, ie. pages pinned in the host are - * in the current working set of the guest driver and therefore never - * overlap e.g., with pages available to the guest balloon driver. This - * needs to be set before vfio_get_device() for vfio common to handle - * ram_block_discard_disable(). - */ - vcdev->vdev.ram_block_discard_allowed = true; - - if (vfio_get_device(group, vcdev->cdev.mdevid, &vcdev->vdev, errp)) { - goto out_err; - } - - vcdev->vdev.ops = &vfio_ccw_ops; - vcdev->vdev.type = VFIO_DEVICE_TYPE_CCW; - vcdev->vdev.name = name; - vcdev->vdev.dev = &vcdev->cdev.parent_obj.parent_obj; - - return; - -out_err: - g_free(name); -} - -static VFIOGroup *vfio_ccw_get_group(S390CCWDevice *cdev, Error **errp) -{ - char *tmp, group_path[PATH_MAX]; - ssize_t len; - int groupid; - - tmp = g_strdup_printf("/sys/bus/css/devices/%x.%x.%04x/%s/iommu_group", - cdev->hostid.cssid, cdev->hostid.ssid, - cdev->hostid.devid, cdev->mdevid); - len = readlink(tmp, group_path, sizeof(group_path)); - g_free(tmp); - - if (len <= 0 || len >= sizeof(group_path)) { - error_setg(errp, "vfio: no iommu_group found"); - return NULL; - } - - group_path[len] = 0; - - if (sscanf(basename(group_path), "%d", &groupid) != 1) { - error_setg(errp, "vfio: failed to read %s", group_path); - return NULL; - } - - return vfio_get_group(groupid, &address_space_memory, errp); -} - static void vfio_ccw_realize(DeviceState *dev, Error **errp) { - VFIOGroup *group; - CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev); - S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev); - VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); + S390CCWDevice *cdev = S390_CCW_DEVICE(dev); + VFIOCCWDevice *vcdev = VFIO_CCW(cdev); S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev); + VFIODevice *vbasedev = &vcdev->vdev; Error *err = NULL; + int ret; /* Call the class init function for subchannel. */ if (cdc->realize) { @@ -670,14 +590,14 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) } } - group = vfio_ccw_get_group(cdev, &err); - if (!group) { - goto out_group_err; + if (vfio_device_get_name(vbasedev, errp) < 0) { + return; } - vfio_ccw_get_device(group, vcdev, &err); - if (err) { - goto out_device_err; + ret = vfio_attach_device(cdev->mdevid, vbasedev, + &address_space_memory, errp); + if (ret) { + goto out_attach_dev_err; } vfio_ccw_get_region(vcdev, &err); @@ -715,10 +635,9 @@ out_irq_notifier_err: out_io_notifier_err: vfio_ccw_put_region(vcdev); out_region_err: - vfio_ccw_put_device(vcdev); -out_device_err: - vfio_put_group(group); -out_group_err: + vfio_detach_device(vbasedev); +out_attach_dev_err: + g_free(vbasedev->name); if (cdc->unrealize) { cdc->unrealize(cdev); } @@ -728,18 +647,16 @@ out_err_propagate: static void vfio_ccw_unrealize(DeviceState *dev) { - CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev); - S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev); - VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); + S390CCWDevice *cdev = S390_CCW_DEVICE(dev); + VFIOCCWDevice *vcdev = VFIO_CCW(cdev); S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev); - VFIOGroup *group = vcdev->vdev.group; vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX); vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX); vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX); vfio_ccw_put_region(vcdev); - vfio_ccw_put_device(vcdev); - vfio_put_group(group); + vfio_detach_device(&vcdev->vdev); + g_free(vcdev->vdev.name); if (cdc->unrealize) { cdc->unrealize(cdev); @@ -749,6 +666,10 @@ static void vfio_ccw_unrealize(DeviceState *dev) static Property vfio_ccw_properties[] = { DEFINE_PROP_STRING("sysfsdev", VFIOCCWDevice, vdev.sysfsdev), DEFINE_PROP_BOOL("force-orb-pfch", VFIOCCWDevice, force_orb_pfch, false), +#ifdef CONFIG_IOMMUFD + DEFINE_PROP_LINK("iommufd", VFIOCCWDevice, vdev.iommufd, + TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), +#endif DEFINE_PROP_END_OF_LIST(), }; @@ -757,12 +678,39 @@ static const VMStateDescription vfio_ccw_vmstate = { .unmigratable = 1, }; +static void vfio_ccw_instance_init(Object *obj) +{ + VFIOCCWDevice *vcdev = VFIO_CCW(obj); + VFIODevice *vbasedev = &vcdev->vdev; + + /* + * All vfio-ccw devices are believed to operate in a way compatible with + * discarding of memory in RAM blocks, ie. pages pinned in the host are + * in the current working set of the guest driver and therefore never + * overlap e.g., with pages available to the guest balloon driver. This + * needs to be set before vfio_get_device() for vfio common to handle + * ram_block_discard_disable(). + */ + vfio_device_init(vbasedev, VFIO_DEVICE_TYPE_CCW, &vfio_ccw_ops, + DEVICE(vcdev), true); +} + +#ifdef CONFIG_IOMMUFD +static void vfio_ccw_set_fd(Object *obj, const char *str, Error **errp) +{ + vfio_device_set_fd(&VFIO_CCW(obj)->vdev, str, errp); +} +#endif + static void vfio_ccw_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); S390CCWDeviceClass *cdc = S390_CCW_DEVICE_CLASS(klass); device_class_set_props(dc, vfio_ccw_properties); +#ifdef CONFIG_IOMMUFD + object_class_property_add_str(klass, "fd", NULL, vfio_ccw_set_fd); +#endif dc->vmsd = &vfio_ccw_vmstate; dc->desc = "VFIO-based subchannel assignment"; set_bit(DEVICE_CATEGORY_MISC, dc->categories); @@ -780,6 +728,7 @@ static const TypeInfo vfio_ccw_info = { .name = TYPE_VFIO_CCW, .parent = TYPE_S390_CCW, .instance_size = sizeof(VFIOCCWDevice), + .instance_init = vfio_ccw_instance_init, .class_init = vfio_ccw_class_init, }; diff --git a/hw/vfio/common.c b/hw/vfio/common.c index ace9562a9b..011ceaab89 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -26,7 +26,7 @@ #include #include "hw/vfio/vfio-common.h" -#include "hw/vfio/vfio.h" +#include "hw/vfio/pci.h" #include "exec/address-spaces.h" #include "exec/memory.h" #include "exec/ram_addr.h" @@ -39,11 +39,13 @@ #include "sysemu/runstate.h" #include "trace.h" #include "qapi/error.h" -#include "migration/migration.h" +#include "migration/misc.h" +#include "migration/blocker.h" +#include "migration/qemu-file.h" #include "sysemu/tpm.h" -VFIOGroupList vfio_group_list = - QLIST_HEAD_INITIALIZER(vfio_group_list); +VFIODeviceList vfio_device_list = + QLIST_HEAD_INITIALIZER(vfio_device_list); static QLIST_HEAD(, VFIOAddressSpace) vfio_address_spaces = QLIST_HEAD_INITIALIZER(vfio_address_spaces); @@ -55,509 +57,184 @@ static QLIST_HEAD(, VFIOAddressSpace) vfio_address_spaces = * initialized, this file descriptor is only released on QEMU exit and * we'll re-use it should another vfio device be attached before then. */ -static int vfio_kvm_device_fd = -1; +int vfio_kvm_device_fd = -1; #endif -/* - * Common VFIO interrupt disable - */ -void vfio_disable_irqindex(VFIODevice *vbasedev, int index) -{ - struct vfio_irq_set irq_set = { - .argsz = sizeof(irq_set), - .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER, - .index = index, - .start = 0, - .count = 0, - }; - - ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); -} - -void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index) -{ - struct vfio_irq_set irq_set = { - .argsz = sizeof(irq_set), - .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK, - .index = index, - .start = 0, - .count = 1, - }; - - ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); -} - -void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index) -{ - struct vfio_irq_set irq_set = { - .argsz = sizeof(irq_set), - .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_MASK, - .index = index, - .start = 0, - .count = 1, - }; - - ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); -} - -static inline const char *action_to_str(int action) -{ - switch (action) { - case VFIO_IRQ_SET_ACTION_MASK: - return "MASK"; - case VFIO_IRQ_SET_ACTION_UNMASK: - return "UNMASK"; - case VFIO_IRQ_SET_ACTION_TRIGGER: - return "TRIGGER"; - default: - return "UNKNOWN ACTION"; - } -} - -static const char *index_to_str(VFIODevice *vbasedev, int index) -{ - if (vbasedev->type != VFIO_DEVICE_TYPE_PCI) { - return NULL; - } - - switch (index) { - case VFIO_PCI_INTX_IRQ_INDEX: - return "INTX"; - case VFIO_PCI_MSI_IRQ_INDEX: - return "MSI"; - case VFIO_PCI_MSIX_IRQ_INDEX: - return "MSIX"; - case VFIO_PCI_ERR_IRQ_INDEX: - return "ERR"; - case VFIO_PCI_REQ_IRQ_INDEX: - return "REQ"; - default: - return NULL; - } -} - -static int vfio_ram_block_discard_disable(VFIOContainer *container, bool state) -{ - switch (container->iommu_type) { - case VFIO_TYPE1v2_IOMMU: - case VFIO_TYPE1_IOMMU: - /* - * We support coordinated discarding of RAM via the RamDiscardManager. - */ - return ram_block_uncoordinated_discard_disable(state); - default: - /* - * VFIO_SPAPR_TCE_IOMMU most probably works just fine with - * RamDiscardManager, however, it is completely untested. - * - * VFIO_SPAPR_TCE_v2_IOMMU with "DMA memory preregistering" does - * completely the opposite of managing mapping/pinning dynamically as - * required by RamDiscardManager. We would have to special-case sections - * with a RamDiscardManager. - */ - return ram_block_discard_disable(state); - } -} - -int vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, - int action, int fd, Error **errp) -{ - struct vfio_irq_set *irq_set; - int argsz, ret = 0; - const char *name; - int32_t *pfd; - - argsz = sizeof(*irq_set) + sizeof(*pfd); - - irq_set = g_malloc0(argsz); - irq_set->argsz = argsz; - irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | action; - irq_set->index = index; - irq_set->start = subindex; - irq_set->count = 1; - pfd = (int32_t *)&irq_set->data; - *pfd = fd; - - if (ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) { - ret = -errno; - } - g_free(irq_set); - - if (!ret) { - return 0; - } - - error_setg_errno(errp, -ret, "VFIO_DEVICE_SET_IRQS failure"); - - name = index_to_str(vbasedev, index); - if (name) { - error_prepend(errp, "%s-%d: ", name, subindex); - } else { - error_prepend(errp, "index %d-%d: ", index, subindex); - } - error_prepend(errp, - "Failed to %s %s eventfd signaling for interrupt ", - fd < 0 ? "tear down" : "set up", action_to_str(action)); - return ret; -} - -/* - * IO Port/MMIO - Beware of the endians, VFIO is always little endian - */ -void vfio_region_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIORegion *region = opaque; - VFIODevice *vbasedev = region->vbasedev; - union { - uint8_t byte; - uint16_t word; - uint32_t dword; - uint64_t qword; - } buf; - - switch (size) { - case 1: - buf.byte = data; - break; - case 2: - buf.word = cpu_to_le16(data); - break; - case 4: - buf.dword = cpu_to_le32(data); - break; - case 8: - buf.qword = cpu_to_le64(data); - break; - default: - hw_error("vfio: unsupported write size, %u bytes", size); - break; - } - - if (pwrite(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) { - error_report("%s(%s:region%d+0x%"HWADDR_PRIx", 0x%"PRIx64 - ",%d) failed: %m", - __func__, vbasedev->name, region->nr, - addr, data, size); - } - - trace_vfio_region_write(vbasedev->name, region->nr, addr, data, size); - - /* - * A read or write to a BAR always signals an INTx EOI. This will - * do nothing if not pending (including not in INTx mode). We assume - * that a BAR access is in response to an interrupt and that BAR - * accesses will service the interrupt. Unfortunately, we don't know - * which access will service the interrupt, so we're potentially - * getting quite a few host interrupts per guest interrupt. - */ - vbasedev->ops->vfio_eoi(vbasedev); -} - -uint64_t vfio_region_read(void *opaque, - hwaddr addr, unsigned size) -{ - VFIORegion *region = opaque; - VFIODevice *vbasedev = region->vbasedev; - union { - uint8_t byte; - uint16_t word; - uint32_t dword; - uint64_t qword; - } buf; - uint64_t data = 0; - - if (pread(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) { - error_report("%s(%s:region%d+0x%"HWADDR_PRIx", %d) failed: %m", - __func__, vbasedev->name, region->nr, - addr, size); - return (uint64_t)-1; - } - switch (size) { - case 1: - data = buf.byte; - break; - case 2: - data = le16_to_cpu(buf.word); - break; - case 4: - data = le32_to_cpu(buf.dword); - break; - case 8: - data = le64_to_cpu(buf.qword); - break; - default: - hw_error("vfio: unsupported read size, %u bytes", size); - break; - } - - trace_vfio_region_read(vbasedev->name, region->nr, addr, size, data); - - /* Same as write above */ - vbasedev->ops->vfio_eoi(vbasedev); - - return data; -} - -const MemoryRegionOps vfio_region_ops = { - .read = vfio_region_read, - .write = vfio_region_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 1, - .max_access_size = 8, - }, -}; - /* * Device state interfaces */ bool vfio_mig_active(void) { - VFIOGroup *group; VFIODevice *vbasedev; - if (QLIST_EMPTY(&vfio_group_list)) { + if (QLIST_EMPTY(&vfio_device_list)) { return false; } - QLIST_FOREACH(group, &vfio_group_list, next) { - QLIST_FOREACH(vbasedev, &group->device_list, next) { - if (vbasedev->migration_blocker) { - return false; - } + QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { + if (vbasedev->migration_blocker) { + return false; } } return true; } -static bool vfio_devices_all_dirty_tracking(VFIOContainer *container) -{ - VFIOGroup *group; - VFIODevice *vbasedev; - MigrationState *ms = migrate_get_current(); - - if (!migration_is_setup_or_active(ms->state)) { - return false; - } - - QLIST_FOREACH(group, &container->group_list, container_next) { - QLIST_FOREACH(vbasedev, &group->device_list, next) { - VFIOMigration *migration = vbasedev->migration; - - if (!migration) { - return false; - } - - if ((vbasedev->pre_copy_dirty_page_tracking == ON_OFF_AUTO_OFF) - && (migration->device_state & VFIO_DEVICE_STATE_V1_RUNNING)) { - return false; - } - } - } - return true; -} - -static bool vfio_devices_all_running_and_saving(VFIOContainer *container) -{ - VFIOGroup *group; - VFIODevice *vbasedev; - MigrationState *ms = migrate_get_current(); - - if (!migration_is_setup_or_active(ms->state)) { - return false; - } - - QLIST_FOREACH(group, &container->group_list, container_next) { - QLIST_FOREACH(vbasedev, &group->device_list, next) { - VFIOMigration *migration = vbasedev->migration; - - if (!migration) { - return false; - } - - if ((migration->device_state & VFIO_DEVICE_STATE_V1_SAVING) && - (migration->device_state & VFIO_DEVICE_STATE_V1_RUNNING)) { - continue; - } else { - return false; - } - } - } - return true; -} - -static int vfio_dma_unmap_bitmap(VFIOContainer *container, - hwaddr iova, ram_addr_t size, - IOMMUTLBEntry *iotlb) -{ - struct vfio_iommu_type1_dma_unmap *unmap; - struct vfio_bitmap *bitmap; - uint64_t pages = REAL_HOST_PAGE_ALIGN(size) / qemu_real_host_page_size(); - int ret; - - unmap = g_malloc0(sizeof(*unmap) + sizeof(*bitmap)); - - unmap->argsz = sizeof(*unmap) + sizeof(*bitmap); - unmap->iova = iova; - unmap->size = size; - unmap->flags |= VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP; - bitmap = (struct vfio_bitmap *)&unmap->data; - - /* - * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of - * qemu_real_host_page_size to mark those dirty. Hence set bitmap_pgsize - * to qemu_real_host_page_size. - */ - - bitmap->pgsize = qemu_real_host_page_size(); - bitmap->size = ROUND_UP(pages, sizeof(__u64) * BITS_PER_BYTE) / - BITS_PER_BYTE; - - if (bitmap->size > container->max_dirty_bitmap_size) { - error_report("UNMAP: Size of bitmap too big 0x%"PRIx64, - (uint64_t)bitmap->size); - ret = -E2BIG; - goto unmap_exit; - } - - bitmap->data = g_try_malloc0(bitmap->size); - if (!bitmap->data) { - ret = -ENOMEM; - goto unmap_exit; - } - - ret = ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, unmap); - if (!ret) { - cpu_physical_memory_set_dirty_lebitmap((unsigned long *)bitmap->data, - iotlb->translated_addr, pages); - } else { - error_report("VFIO_UNMAP_DMA with DIRTY_BITMAP : %m"); - } - - g_free(bitmap->data); -unmap_exit: - g_free(unmap); - return ret; -} +static Error *multiple_devices_migration_blocker; /* - * DMA - Mapping and unmapping for the "type1" IOMMU interface used on x86 + * Multiple devices migration is allowed only if all devices support P2P + * migration. Single device migration is allowed regardless of P2P migration + * support. */ -static int vfio_dma_unmap(VFIOContainer *container, - hwaddr iova, ram_addr_t size, - IOMMUTLBEntry *iotlb) +static bool vfio_multiple_devices_migration_is_supported(void) { - struct vfio_iommu_type1_dma_unmap unmap = { - .argsz = sizeof(unmap), - .flags = 0, - .iova = iova, - .size = size, - }; + VFIODevice *vbasedev; + unsigned int device_num = 0; + bool all_support_p2p = true; - if (iotlb && container->dirty_pages_supported && - vfio_devices_all_running_and_saving(container)) { - return vfio_dma_unmap_bitmap(container, iova, size, iotlb); - } + QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { + if (vbasedev->migration) { + device_num++; - while (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) { - /* - * The type1 backend has an off-by-one bug in the kernel (71a7d3d78e3c - * v4.15) where an overflow in its wrap-around check prevents us from - * unmapping the last page of the address space. Test for the error - * condition and re-try the unmap excluding the last page. The - * expectation is that we've never mapped the last page anyway and this - * unmap request comes via vIOMMU support which also makes it unlikely - * that this page is used. This bug was introduced well after type1 v2 - * support was introduced, so we shouldn't need to test for v1. A fix - * is queued for kernel v5.0 so this workaround can be removed once - * affected kernels are sufficiently deprecated. - */ - if (errno == EINVAL && unmap.size && !(unmap.iova + unmap.size) && - container->iommu_type == VFIO_TYPE1v2_IOMMU) { - trace_vfio_dma_unmap_overflow_workaround(); - unmap.size -= 1ULL << ctz64(container->pgsizes); - continue; + if (!(vbasedev->migration->mig_flags & VFIO_MIGRATION_P2P)) { + all_support_p2p = false; + } } - error_report("VFIO_UNMAP_DMA failed: %s", strerror(errno)); - return -errno; } - return 0; + return all_support_p2p || device_num <= 1; } -static int vfio_dma_map(VFIOContainer *container, hwaddr iova, - ram_addr_t size, void *vaddr, bool readonly) +int vfio_block_multiple_devices_migration(VFIODevice *vbasedev, Error **errp) { - struct vfio_iommu_type1_dma_map map = { - .argsz = sizeof(map), - .flags = VFIO_DMA_MAP_FLAG_READ, - .vaddr = (__u64)(uintptr_t)vaddr, - .iova = iova, - .size = size, - }; + int ret; - if (!readonly) { - map.flags |= VFIO_DMA_MAP_FLAG_WRITE; - } - - /* - * Try the mapping, if it fails with EBUSY, unmap the region and try - * again. This shouldn't be necessary, but we sometimes see it in - * the VGA ROM space. - */ - if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0 || - (errno == EBUSY && vfio_dma_unmap(container, iova, size, NULL) == 0 && - ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0)) { + if (vfio_multiple_devices_migration_is_supported()) { return 0; } - error_report("VFIO_MAP_DMA failed: %s", strerror(errno)); - return -errno; + if (vbasedev->enable_migration == ON_OFF_AUTO_ON) { + error_setg(errp, "Multiple VFIO devices migration is supported only if " + "all of them support P2P migration"); + return -EINVAL; + } + + if (multiple_devices_migration_blocker) { + return 0; + } + + error_setg(&multiple_devices_migration_blocker, + "Multiple VFIO devices migration is supported only if all of " + "them support P2P migration"); + ret = migrate_add_blocker_normal(&multiple_devices_migration_blocker, errp); + + return ret; } -static void vfio_host_win_add(VFIOContainer *container, - hwaddr min_iova, hwaddr max_iova, - uint64_t iova_pgsizes) +void vfio_unblock_multiple_devices_migration(void) { - VFIOHostDMAWindow *hostwin; + if (!multiple_devices_migration_blocker || + !vfio_multiple_devices_migration_is_supported()) { + return; + } - QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { - if (ranges_overlap(hostwin->min_iova, - hostwin->max_iova - hostwin->min_iova + 1, - min_iova, - max_iova - min_iova + 1)) { - hw_error("%s: Overlapped IOMMU are not enabled", __func__); + migrate_del_blocker(&multiple_devices_migration_blocker); +} + +bool vfio_viommu_preset(VFIODevice *vbasedev) +{ + return vbasedev->bcontainer->space->as != &address_space_memory; +} + +static void vfio_set_migration_error(int err) +{ + if (migration_is_setup_or_active()) { + migration_file_set_error(err); + } +} + +bool vfio_device_state_is_running(VFIODevice *vbasedev) +{ + VFIOMigration *migration = vbasedev->migration; + + return migration->device_state == VFIO_DEVICE_STATE_RUNNING || + migration->device_state == VFIO_DEVICE_STATE_RUNNING_P2P; +} + +bool vfio_device_state_is_precopy(VFIODevice *vbasedev) +{ + VFIOMigration *migration = vbasedev->migration; + + return migration->device_state == VFIO_DEVICE_STATE_PRE_COPY || + migration->device_state == VFIO_DEVICE_STATE_PRE_COPY_P2P; +} + +static bool vfio_devices_all_dirty_tracking(VFIOContainerBase *bcontainer) +{ + VFIODevice *vbasedev; + + if (!migration_is_active() && !migration_is_device()) { + return false; + } + + QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { + VFIOMigration *migration = vbasedev->migration; + + if (!migration) { + return false; + } + + if (vbasedev->pre_copy_dirty_page_tracking == ON_OFF_AUTO_OFF && + (vfio_device_state_is_running(vbasedev) || + vfio_device_state_is_precopy(vbasedev))) { + return false; + } + } + return true; +} + +bool vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer) +{ + VFIODevice *vbasedev; + + QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { + if (!vbasedev->dirty_pages_supported) { + return false; } } - hostwin = g_malloc0(sizeof(*hostwin)); - - hostwin->min_iova = min_iova; - hostwin->max_iova = max_iova; - hostwin->iova_pgsizes = iova_pgsizes; - QLIST_INSERT_HEAD(&container->hostwin_list, hostwin, hostwin_next); + return true; } -static int vfio_host_win_del(VFIOContainer *container, hwaddr min_iova, - hwaddr max_iova) +/* + * Check if all VFIO devices are running and migration is active, which is + * essentially equivalent to the migration being in pre-copy phase. + */ +bool +vfio_devices_all_running_and_mig_active(const VFIOContainerBase *bcontainer) { - VFIOHostDMAWindow *hostwin; + VFIODevice *vbasedev; - QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { - if (hostwin->min_iova == min_iova && hostwin->max_iova == max_iova) { - QLIST_REMOVE(hostwin, hostwin_next); - g_free(hostwin); - return 0; - } + if (!migration_is_active()) { + return false; } - return -1; + QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { + VFIOMigration *migration = vbasedev->migration; + + if (!migration) { + return false; + } + + if (vfio_device_state_is_running(vbasedev) || + vfio_device_state_is_precopy(vbasedev)) { + continue; + } else { + return false; + } + } + return true; } static bool vfio_listener_skipped_section(MemoryRegionSection *section) @@ -578,45 +255,11 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section) static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, ram_addr_t *ram_addr, bool *read_only) { - MemoryRegion *mr; - hwaddr xlat; - hwaddr len = iotlb->addr_mask + 1; - bool writable = iotlb->perm & IOMMU_WO; - - /* - * The IOMMU TLB entry we have just covers translation through - * this IOMMU to its immediate target. We need to translate - * it the rest of the way through to memory. - */ - mr = address_space_translate(&address_space_memory, - iotlb->translated_addr, - &xlat, &len, writable, - MEMTXATTRS_UNSPECIFIED); - if (!memory_region_is_ram(mr)) { - error_report("iommu map to non memory area %"HWADDR_PRIx"", - xlat); - return false; - } else if (memory_region_has_ram_discard_manager(mr)) { - RamDiscardManager *rdm = memory_region_get_ram_discard_manager(mr); - MemoryRegionSection tmp = { - .mr = mr, - .offset_within_region = xlat, - .size = int128_make64(len), - }; - - /* - * Malicious VMs can map memory into the IOMMU, which is expected - * to remain discarded. vfio will pin all pages, populating memory. - * Disallow that. vmstate priorities make sure any RamDiscardManager - * were already restored before IOMMUs are restored. - */ - if (!ram_discard_manager_is_populated(rdm, &tmp)) { - error_report("iommu map to discarded memory (e.g., unplugged via" - " virtio-mem): %"HWADDR_PRIx"", - iotlb->translated_addr); - return false; - } + bool ret, mr_has_discard_manager; + ret = memory_get_xlat_addr(iotlb, vaddr, ram_addr, read_only, + &mr_has_discard_manager); + if (ret && mr_has_discard_manager) { /* * Malicious VMs might trigger discarding of IOMMU-mapped memory. The * pages will remain pinned inside vfio until unmapped, resulting in a @@ -635,35 +278,13 @@ static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, " intended via an IOMMU. It's possible to mitigate " " by setting/adjusting RLIMIT_MEMLOCK."); } - - /* - * Translation truncates length to the IOMMU page size, - * check that it did not truncate too much. - */ - if (len & iotlb->addr_mask) { - error_report("iommu has granularity incompatible with target AS"); - return false; - } - - if (vaddr) { - *vaddr = memory_region_get_ram_ptr(mr) + xlat; - } - - if (ram_addr) { - *ram_addr = memory_region_get_ram_addr(mr) + xlat; - } - - if (read_only) { - *read_only = !writable || mr->readonly; - } - - return true; + return ret; } static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) { VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n); - VFIOContainer *container = giommu->container; + VFIOContainerBase *bcontainer = giommu->bcontainer; hwaddr iova = iotlb->iova + giommu->iommu_offset; void *vaddr; int ret; @@ -674,6 +295,7 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) if (iotlb->target_as != &address_space_memory) { error_report("Wrong target AS \"%s\", only system memory is allowed", iotlb->target_as->name ? iotlb->target_as->name : "none"); + vfio_set_migration_error(-EINVAL); return; } @@ -692,22 +314,24 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) * of vaddr will always be there, even if the memory object is * destroyed and its backing memory munmap-ed. */ - ret = vfio_dma_map(container, iova, - iotlb->addr_mask + 1, vaddr, - read_only); + ret = vfio_container_dma_map(bcontainer, iova, + iotlb->addr_mask + 1, vaddr, + read_only); if (ret) { - error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx", %p) = %d (%m)", - container, iova, - iotlb->addr_mask + 1, vaddr, ret); + error_report("vfio_container_dma_map(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx", %p) = %d (%s)", + bcontainer, iova, + iotlb->addr_mask + 1, vaddr, ret, strerror(-ret)); } } else { - ret = vfio_dma_unmap(container, iova, iotlb->addr_mask + 1, iotlb); + ret = vfio_container_dma_unmap(bcontainer, iova, + iotlb->addr_mask + 1, iotlb); if (ret) { - error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%m)", - container, iova, - iotlb->addr_mask + 1, ret); + error_report("vfio_container_dma_unmap(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx") = %d (%s)", + bcontainer, iova, + iotlb->addr_mask + 1, ret, strerror(-ret)); + vfio_set_migration_error(ret); } } out: @@ -719,14 +343,15 @@ static void vfio_ram_discard_notify_discard(RamDiscardListener *rdl, { VFIORamDiscardListener *vrdl = container_of(rdl, VFIORamDiscardListener, listener); + VFIOContainerBase *bcontainer = vrdl->bcontainer; const hwaddr size = int128_get64(section->size); const hwaddr iova = section->offset_within_address_space; int ret; /* Unmap with a single call. */ - ret = vfio_dma_unmap(vrdl->container, iova, size , NULL); + ret = vfio_container_dma_unmap(bcontainer, iova, size , NULL); if (ret) { - error_report("%s: vfio_dma_unmap() failed: %s", __func__, + error_report("%s: vfio_container_dma_unmap() failed: %s", __func__, strerror(-ret)); } } @@ -736,6 +361,7 @@ static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl, { VFIORamDiscardListener *vrdl = container_of(rdl, VFIORamDiscardListener, listener); + VFIOContainerBase *bcontainer = vrdl->bcontainer; const hwaddr end = section->offset_within_region + int128_get64(section->size); hwaddr start, next, iova; @@ -754,8 +380,8 @@ static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl, section->offset_within_address_space; vaddr = memory_region_get_ram_ptr(section->mr) + start; - ret = vfio_dma_map(vrdl->container, iova, next - start, - vaddr, section->readonly); + ret = vfio_container_dma_map(bcontainer, iova, next - start, + vaddr, section->readonly); if (ret) { /* Rollback */ vfio_ram_discard_notify_discard(rdl, section); @@ -765,7 +391,7 @@ static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl, return 0; } -static void vfio_register_ram_discard_listener(VFIOContainer *container, +static void vfio_register_ram_discard_listener(VFIOContainerBase *bcontainer, MemoryRegionSection *section) { RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); @@ -778,7 +404,7 @@ static void vfio_register_ram_discard_listener(VFIOContainer *container, g_assert(QEMU_IS_ALIGNED(int128_get64(section->size), TARGET_PAGE_SIZE)); vrdl = g_new0(VFIORamDiscardListener, 1); - vrdl->container = container; + vrdl->bcontainer = bcontainer; vrdl->mr = section->mr; vrdl->offset_within_address_space = section->offset_within_address_space; vrdl->size = int128_get64(section->size); @@ -786,14 +412,14 @@ static void vfio_register_ram_discard_listener(VFIOContainer *container, section->mr); g_assert(vrdl->granularity && is_power_of_2(vrdl->granularity)); - g_assert(container->pgsizes && - vrdl->granularity >= 1ULL << ctz64(container->pgsizes)); + g_assert(bcontainer->pgsizes && + vrdl->granularity >= 1ULL << ctz64(bcontainer->pgsizes)); ram_discard_listener_init(&vrdl->listener, vfio_ram_discard_notify_populate, vfio_ram_discard_notify_discard, true); ram_discard_manager_register_listener(rdm, &vrdl->listener, section); - QLIST_INSERT_HEAD(&container->vrdl_list, vrdl, next); + QLIST_INSERT_HEAD(&bcontainer->vrdl_list, vrdl, next); /* * Sanity-check if we have a theoretically problematic setup where we could @@ -808,7 +434,7 @@ static void vfio_register_ram_discard_listener(VFIOContainer *container, * number of sections in the address space we could have over time, * also consuming DMA mappings. */ - if (container->dma_max_mappings) { + if (bcontainer->dma_max_mappings) { unsigned int vrdl_count = 0, vrdl_mappings = 0, max_memslots = 512; #ifdef CONFIG_KVM @@ -817,7 +443,7 @@ static void vfio_register_ram_discard_listener(VFIOContainer *container, } #endif - QLIST_FOREACH(vrdl, &container->vrdl_list, next) { + QLIST_FOREACH(vrdl, &bcontainer->vrdl_list, next) { hwaddr start, end; start = QEMU_ALIGN_DOWN(vrdl->offset_within_address_space, @@ -829,23 +455,23 @@ static void vfio_register_ram_discard_listener(VFIOContainer *container, } if (vrdl_mappings + max_memslots - vrdl_count > - container->dma_max_mappings) { + bcontainer->dma_max_mappings) { warn_report("%s: possibly running out of DMA mappings. E.g., try" " increasing the 'block-size' of virtio-mem devies." " Maximum possible DMA mappings: %d, Maximum possible" - " memslots: %d", __func__, container->dma_max_mappings, + " memslots: %d", __func__, bcontainer->dma_max_mappings, max_memslots); } } } -static void vfio_unregister_ram_discard_listener(VFIOContainer *container, +static void vfio_unregister_ram_discard_listener(VFIOContainerBase *bcontainer, MemoryRegionSection *section) { RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); VFIORamDiscardListener *vrdl = NULL; - QLIST_FOREACH(vrdl, &container->vrdl_list, next) { + QLIST_FOREACH(vrdl, &bcontainer->vrdl_list, next) { if (vrdl->mr == section->mr && vrdl->offset_within_address_space == section->offset_within_address_space) { @@ -878,24 +504,15 @@ static bool vfio_known_safe_misalignment(MemoryRegionSection *section) return true; } -static void vfio_listener_region_add(MemoryListener *listener, - MemoryRegionSection *section) +static bool vfio_listener_valid_section(MemoryRegionSection *section, + const char *name) { - VFIOContainer *container = container_of(listener, VFIOContainer, listener); - hwaddr iova, end; - Int128 llend, llsize; - void *vaddr; - int ret; - VFIOHostDMAWindow *hostwin; - bool hostwin_found; - Error *err = NULL; - if (vfio_listener_skipped_section(section)) { - trace_vfio_listener_region_add_skip( + trace_vfio_listener_region_skip(name, section->offset_within_address_space, section->offset_within_address_space + int128_get64(int128_sub(section->size, int128_one()))); - return; + return false; } if (unlikely((section->offset_within_address_space & @@ -910,15 +527,54 @@ static void vfio_listener_region_add(MemoryListener *listener, section->offset_within_region, qemu_real_host_page_size()); } - return; + return false; } + return true; +} + +static bool vfio_get_section_iova_range(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, + hwaddr *out_iova, hwaddr *out_end, + Int128 *out_llend) +{ + Int128 llend; + hwaddr iova; + iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space); llend = int128_make64(section->offset_within_address_space); llend = int128_add(llend, section->size); llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask())); if (int128_ge(int128_make64(iova), llend)) { + return false; + } + + *out_iova = iova; + *out_end = int128_get64(int128_sub(llend, int128_one())); + if (out_llend) { + *out_llend = llend; + } + return true; +} + +static void vfio_listener_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, + listener); + hwaddr iova, end; + Int128 llend, llsize; + void *vaddr; + int ret; + Error *err = NULL; + + if (!vfio_listener_valid_section(section, "region_add")) { + return; + } + + if (!vfio_get_section_iova_range(bcontainer, section, &iova, &end, + &llend)) { if (memory_region_is_ram_device(section->mr)) { trace_vfio_listener_region_add_no_dma_map( memory_region_name(section->mr), @@ -928,77 +584,8 @@ static void vfio_listener_region_add(MemoryListener *listener, } return; } - end = int128_get64(int128_sub(llend, int128_one())); - if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { - hwaddr pgsize = 0; - - /* For now intersections are not allowed, we may relax this later */ - QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { - if (ranges_overlap(hostwin->min_iova, - hostwin->max_iova - hostwin->min_iova + 1, - section->offset_within_address_space, - int128_get64(section->size))) { - error_setg(&err, - "region [0x%"PRIx64",0x%"PRIx64"] overlaps with existing" - "host DMA window [0x%"PRIx64",0x%"PRIx64"]", - section->offset_within_address_space, - section->offset_within_address_space + - int128_get64(section->size) - 1, - hostwin->min_iova, hostwin->max_iova); - goto fail; - } - } - - ret = vfio_spapr_create_window(container, section, &pgsize); - if (ret) { - error_setg_errno(&err, -ret, "Failed to create SPAPR window"); - goto fail; - } - - vfio_host_win_add(container, section->offset_within_address_space, - section->offset_within_address_space + - int128_get64(section->size) - 1, pgsize); -#ifdef CONFIG_KVM - if (kvm_enabled()) { - VFIOGroup *group; - IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); - struct kvm_vfio_spapr_tce param; - struct kvm_device_attr attr = { - .group = KVM_DEV_VFIO_GROUP, - .attr = KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE, - .addr = (uint64_t)(unsigned long)¶m, - }; - - if (!memory_region_iommu_get_attr(iommu_mr, IOMMU_ATTR_SPAPR_TCE_FD, - ¶m.tablefd)) { - QLIST_FOREACH(group, &container->group_list, container_next) { - param.groupfd = group->fd; - if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { - error_report("vfio: failed to setup fd %d " - "for a group with fd %d: %s", - param.tablefd, param.groupfd, - strerror(errno)); - return; - } - trace_vfio_spapr_group_attach(param.groupfd, param.tablefd); - } - } - } -#endif - } - - hostwin_found = false; - QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { - if (hostwin->min_iova <= iova && end <= hostwin->max_iova) { - hostwin_found = true; - break; - } - } - - if (!hostwin_found) { - error_setg(&err, "Container %p can't map guest IOVA region" - " 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx, container, iova, end); + if (vfio_container_add_section_window(bcontainer, section, &err)) { goto fail; } @@ -1020,7 +607,7 @@ static void vfio_listener_region_add(MemoryListener *listener, giommu->iommu_mr = iommu_mr; giommu->iommu_offset = section->offset_within_address_space - section->offset_within_region; - giommu->container = container; + giommu->bcontainer = bcontainer; llend = int128_add(int128_make64(section->offset_within_region), section->size); llend = int128_sub(llend, int128_one()); @@ -1033,20 +620,30 @@ static void vfio_listener_region_add(MemoryListener *listener, iommu_idx); ret = memory_region_iommu_set_page_size_mask(giommu->iommu_mr, - container->pgsizes, + bcontainer->pgsizes, &err); if (ret) { g_free(giommu); goto fail; } + if (bcontainer->iova_ranges) { + ret = memory_region_iommu_set_iova_ranges(giommu->iommu_mr, + bcontainer->iova_ranges, + &err); + if (ret) { + g_free(giommu); + goto fail; + } + } + ret = memory_region_register_iommu_notifier(section->mr, &giommu->n, &err); if (ret) { g_free(giommu); goto fail; } - QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); + QLIST_INSERT_HEAD(&bcontainer->giommu_list, giommu, giommu_next); memory_region_iommu_replay(giommu->iommu_mr, &giommu->n); return; @@ -1060,7 +657,7 @@ static void vfio_listener_region_add(MemoryListener *listener, * about changes. */ if (memory_region_has_ram_discard_manager(section->mr)) { - vfio_register_ram_discard_listener(container, section); + vfio_register_ram_discard_listener(bcontainer, section); return; } @@ -1073,7 +670,7 @@ static void vfio_listener_region_add(MemoryListener *listener, llsize = int128_sub(llend, int128_make64(iova)); if (memory_region_is_ram_device(section->mr)) { - hwaddr pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1; + hwaddr pgmask = (1ULL << ctz64(bcontainer->pgsizes)) - 1; if ((iova & pgmask) || (int128_get64(llsize) & pgmask)) { trace_vfio_listener_region_add_no_dma_map( @@ -1085,12 +682,13 @@ static void vfio_listener_region_add(MemoryListener *listener, } } - ret = vfio_dma_map(container, iova, int128_get64(llsize), - vaddr, section->readonly); + ret = vfio_container_dma_map(bcontainer, iova, int128_get64(llsize), + vaddr, section->readonly); if (ret) { - error_setg(&err, "vfio_dma_map(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx", %p) = %d (%m)", - container, iova, int128_get64(llsize), vaddr, ret); + error_setg(&err, "vfio_container_dma_map(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx", %p) = %d (%s)", + bcontainer, iova, int128_get64(llsize), vaddr, ret, + strerror(-ret)); if (memory_region_is_ram_device(section->mr)) { /* Allow unexpected mappings not to be fatal for RAM devices */ error_report_err(err); @@ -1103,7 +701,7 @@ static void vfio_listener_region_add(MemoryListener *listener, fail: if (memory_region_is_ram_device(section->mr)) { - error_report("failed to vfio_dma_map. pci p2p may not work"); + error_reportf_err(err, "PCI p2p may not work: "); return; } /* @@ -1111,9 +709,9 @@ fail: * can gracefully fail. Runtime, there's not much we can do other * than throw a hardware error. */ - if (!container->initialized) { - if (!container->error) { - error_propagate_prepend(&container->error, err, + if (!bcontainer->initialized) { + if (!bcontainer->error) { + error_propagate_prepend(&bcontainer->error, err, "Region %s: ", memory_region_name(section->mr)); } else { @@ -1128,39 +726,21 @@ fail: static void vfio_listener_region_del(MemoryListener *listener, MemoryRegionSection *section) { - VFIOContainer *container = container_of(listener, VFIOContainer, listener); + VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, + listener); hwaddr iova, end; Int128 llend, llsize; int ret; bool try_unmap = true; - if (vfio_listener_skipped_section(section)) { - trace_vfio_listener_region_del_skip( - section->offset_within_address_space, - section->offset_within_address_space + - int128_get64(int128_sub(section->size, int128_one()))); - return; - } - - if (unlikely((section->offset_within_address_space & - ~qemu_real_host_page_mask()) != - (section->offset_within_region & ~qemu_real_host_page_mask()))) { - if (!vfio_known_safe_misalignment(section)) { - error_report("%s received unaligned region %s iova=0x%"PRIx64 - " offset_within_region=0x%"PRIx64 - " qemu_real_host_page_size=0x%"PRIxPTR, - __func__, memory_region_name(section->mr), - section->offset_within_address_space, - section->offset_within_region, - qemu_real_host_page_size()); - } + if (!vfio_listener_valid_section(section, "region_del")) { return; } if (memory_region_is_iommu(section->mr)) { VFIOGuestIOMMU *giommu; - QLIST_FOREACH(giommu, &container->giommu_list, giommu_next) { + QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) { if (MEMORY_REGION(giommu->iommu_mr) == section->mr && giommu->n.start == section->offset_within_region) { memory_region_unregister_iommu_notifier(section->mr, @@ -1180,15 +760,10 @@ static void vfio_listener_region_del(MemoryListener *listener, */ } - iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space); - llend = int128_make64(section->offset_within_address_space); - llend = int128_add(llend, section->size); - llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask())); - - if (int128_ge(int128_make64(iova), llend)) { + if (!vfio_get_section_iova_range(bcontainer, section, &iova, &end, + &llend)) { return; } - end = int128_get64(int128_sub(llend, int128_one())); llsize = int128_sub(llend, int128_make64(iova)); @@ -1196,21 +771,11 @@ static void vfio_listener_region_del(MemoryListener *listener, if (memory_region_is_ram_device(section->mr)) { hwaddr pgmask; - VFIOHostDMAWindow *hostwin; - bool hostwin_found = false; - QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { - if (hostwin->min_iova <= iova && end <= hostwin->max_iova) { - hostwin_found = true; - break; - } - } - assert(hostwin_found); /* or region_add() would have failed */ - - pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1; + pgmask = (1ULL << ctz64(bcontainer->pgsizes)) - 1; try_unmap = !((iova & pgmask) || (int128_get64(llsize) & pgmask)); } else if (memory_region_has_ram_discard_manager(section->mr)) { - vfio_unregister_ram_discard_listener(container, section); + vfio_unregister_ram_discard_listener(bcontainer, section); /* Unregistering will trigger an unmap. */ try_unmap = false; } @@ -1219,119 +784,412 @@ static void vfio_listener_region_del(MemoryListener *listener, if (int128_eq(llsize, int128_2_64())) { /* The unmap ioctl doesn't accept a full 64-bit span. */ llsize = int128_rshift(llsize, 1); - ret = vfio_dma_unmap(container, iova, int128_get64(llsize), NULL); + ret = vfio_container_dma_unmap(bcontainer, iova, + int128_get64(llsize), NULL); if (ret) { - error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%m)", - container, iova, int128_get64(llsize), ret); + error_report("vfio_container_dma_unmap(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx") = %d (%s)", + bcontainer, iova, int128_get64(llsize), ret, + strerror(-ret)); } iova += int128_get64(llsize); } - ret = vfio_dma_unmap(container, iova, int128_get64(llsize), NULL); + ret = vfio_container_dma_unmap(bcontainer, iova, + int128_get64(llsize), NULL); if (ret) { - error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%m)", - container, iova, int128_get64(llsize), ret); + error_report("vfio_container_dma_unmap(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx") = %d (%s)", + bcontainer, iova, int128_get64(llsize), ret, + strerror(-ret)); } } memory_region_unref(section->mr); - if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { - vfio_spapr_remove_window(container, - section->offset_within_address_space); - if (vfio_host_win_del(container, - section->offset_within_address_space, - section->offset_within_address_space + - int128_get64(section->size) - 1) < 0) { - hw_error("%s: Cannot delete missing window at %"HWADDR_PRIx, - __func__, section->offset_within_address_space); + vfio_container_del_section_window(bcontainer, section); +} + +typedef struct VFIODirtyRanges { + hwaddr min32; + hwaddr max32; + hwaddr min64; + hwaddr max64; + hwaddr minpci64; + hwaddr maxpci64; +} VFIODirtyRanges; + +typedef struct VFIODirtyRangesListener { + VFIOContainerBase *bcontainer; + VFIODirtyRanges ranges; + MemoryListener listener; +} VFIODirtyRangesListener; + +static bool vfio_section_is_vfio_pci(MemoryRegionSection *section, + VFIOContainerBase *bcontainer) +{ + VFIOPCIDevice *pcidev; + VFIODevice *vbasedev; + Object *owner; + + owner = memory_region_owner(section->mr); + + QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { + if (vbasedev->type != VFIO_DEVICE_TYPE_PCI) { + continue; } + pcidev = container_of(vbasedev, VFIOPCIDevice, vbasedev); + if (OBJECT(pcidev) == owner) { + return true; + } + } + + return false; +} + +static void vfio_dirty_tracking_update(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIODirtyRangesListener *dirty = container_of(listener, + VFIODirtyRangesListener, + listener); + VFIODirtyRanges *range = &dirty->ranges; + hwaddr iova, end, *min, *max; + + if (!vfio_listener_valid_section(section, "tracking_update") || + !vfio_get_section_iova_range(dirty->bcontainer, section, + &iova, &end, NULL)) { + return; + } + + /* + * The address space passed to the dirty tracker is reduced to three ranges: + * one for 32-bit DMA ranges, one for 64-bit DMA ranges and one for the + * PCI 64-bit hole. + * + * The underlying reports of dirty will query a sub-interval of each of + * these ranges. + * + * The purpose of the three range handling is to handle known cases of big + * holes in the address space, like the x86 AMD 1T hole, and firmware (like + * OVMF) which may relocate the pci-hole64 to the end of the address space. + * The latter would otherwise generate large ranges for tracking, stressing + * the limits of supported hardware. The pci-hole32 will always be below 4G + * (overlapping or not) so it doesn't need special handling and is part of + * the 32-bit range. + * + * The alternative would be an IOVATree but that has a much bigger runtime + * overhead and unnecessary complexity. + */ + if (vfio_section_is_vfio_pci(section, dirty->bcontainer) && + iova >= UINT32_MAX) { + min = &range->minpci64; + max = &range->maxpci64; + } else { + min = (end <= UINT32_MAX) ? &range->min32 : &range->min64; + max = (end <= UINT32_MAX) ? &range->max32 : &range->max64; + } + if (*min > iova) { + *min = iova; + } + if (*max < end) { + *max = end; + } + + trace_vfio_device_dirty_tracking_update(iova, end, *min, *max); + return; +} + +static const MemoryListener vfio_dirty_tracking_listener = { + .name = "vfio-tracking", + .region_add = vfio_dirty_tracking_update, +}; + +static void vfio_dirty_tracking_init(VFIOContainerBase *bcontainer, + VFIODirtyRanges *ranges) +{ + VFIODirtyRangesListener dirty; + + memset(&dirty, 0, sizeof(dirty)); + dirty.ranges.min32 = UINT32_MAX; + dirty.ranges.min64 = UINT64_MAX; + dirty.ranges.minpci64 = UINT64_MAX; + dirty.listener = vfio_dirty_tracking_listener; + dirty.bcontainer = bcontainer; + + memory_listener_register(&dirty.listener, + bcontainer->space->as); + + *ranges = dirty.ranges; + + /* + * The memory listener is synchronous, and used to calculate the range + * to dirty tracking. Unregister it after we are done as we are not + * interested in any follow-up updates. + */ + memory_listener_unregister(&dirty.listener); +} + +static void vfio_devices_dma_logging_stop(VFIOContainerBase *bcontainer) +{ + uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature), + sizeof(uint64_t))] = {}; + struct vfio_device_feature *feature = (struct vfio_device_feature *)buf; + VFIODevice *vbasedev; + + feature->argsz = sizeof(buf); + feature->flags = VFIO_DEVICE_FEATURE_SET | + VFIO_DEVICE_FEATURE_DMA_LOGGING_STOP; + + QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { + if (!vbasedev->dirty_tracking) { + continue; + } + + if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) { + warn_report("%s: Failed to stop DMA logging, err %d (%s)", + vbasedev->name, -errno, strerror(errno)); + } + vbasedev->dirty_tracking = false; } } -static void vfio_set_dirty_page_tracking(VFIOContainer *container, bool start) +static struct vfio_device_feature * +vfio_device_feature_dma_logging_start_create(VFIOContainerBase *bcontainer, + VFIODirtyRanges *tracking) { - int ret; - struct vfio_iommu_type1_dirty_bitmap dirty = { - .argsz = sizeof(dirty), - }; + struct vfio_device_feature *feature; + size_t feature_size; + struct vfio_device_feature_dma_logging_control *control; + struct vfio_device_feature_dma_logging_range *ranges; - if (start) { - dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_START; - } else { - dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP; + feature_size = sizeof(struct vfio_device_feature) + + sizeof(struct vfio_device_feature_dma_logging_control); + feature = g_try_malloc0(feature_size); + if (!feature) { + errno = ENOMEM; + return NULL; + } + feature->argsz = feature_size; + feature->flags = VFIO_DEVICE_FEATURE_SET | + VFIO_DEVICE_FEATURE_DMA_LOGGING_START; + + control = (struct vfio_device_feature_dma_logging_control *)feature->data; + control->page_size = qemu_real_host_page_size(); + + /* + * DMA logging uAPI guarantees to support at least a number of ranges that + * fits into a single host kernel base page. + */ + control->num_ranges = !!tracking->max32 + !!tracking->max64 + + !!tracking->maxpci64; + ranges = g_try_new0(struct vfio_device_feature_dma_logging_range, + control->num_ranges); + if (!ranges) { + g_free(feature); + errno = ENOMEM; + + return NULL; } - ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, &dirty); + control->ranges = (uintptr_t)ranges; + if (tracking->max32) { + ranges->iova = tracking->min32; + ranges->length = (tracking->max32 - tracking->min32) + 1; + ranges++; + } + if (tracking->max64) { + ranges->iova = tracking->min64; + ranges->length = (tracking->max64 - tracking->min64) + 1; + ranges++; + } + if (tracking->maxpci64) { + ranges->iova = tracking->minpci64; + ranges->length = (tracking->maxpci64 - tracking->minpci64) + 1; + } + + trace_vfio_device_dirty_tracking_start(control->num_ranges, + tracking->min32, tracking->max32, + tracking->min64, tracking->max64, + tracking->minpci64, tracking->maxpci64); + + return feature; +} + +static void vfio_device_feature_dma_logging_start_destroy( + struct vfio_device_feature *feature) +{ + struct vfio_device_feature_dma_logging_control *control = + (struct vfio_device_feature_dma_logging_control *)feature->data; + struct vfio_device_feature_dma_logging_range *ranges = + (struct vfio_device_feature_dma_logging_range *)(uintptr_t)control->ranges; + + g_free(ranges); + g_free(feature); +} + +static int vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer) +{ + struct vfio_device_feature *feature; + VFIODirtyRanges ranges; + VFIODevice *vbasedev; + int ret = 0; + + vfio_dirty_tracking_init(bcontainer, &ranges); + feature = vfio_device_feature_dma_logging_start_create(bcontainer, + &ranges); + if (!feature) { + return -errno; + } + + QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { + if (vbasedev->dirty_tracking) { + continue; + } + + ret = ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature); + if (ret) { + ret = -errno; + error_report("%s: Failed to start DMA logging, err %d (%s)", + vbasedev->name, ret, strerror(errno)); + goto out; + } + vbasedev->dirty_tracking = true; + } + +out: if (ret) { - error_report("Failed to set dirty tracking flag 0x%x errno: %d", - dirty.flags, errno); + vfio_devices_dma_logging_stop(bcontainer); } + + vfio_device_feature_dma_logging_start_destroy(feature); + + return ret; } static void vfio_listener_log_global_start(MemoryListener *listener) { - VFIOContainer *container = container_of(listener, VFIOContainer, listener); + VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, + listener); + int ret; - vfio_set_dirty_page_tracking(container, true); + if (vfio_devices_all_device_dirty_tracking(bcontainer)) { + ret = vfio_devices_dma_logging_start(bcontainer); + } else { + ret = vfio_container_set_dirty_page_tracking(bcontainer, true); + } + + if (ret) { + error_report("vfio: Could not start dirty page tracking, err: %d (%s)", + ret, strerror(-ret)); + vfio_set_migration_error(ret); + } } static void vfio_listener_log_global_stop(MemoryListener *listener) { - VFIOContainer *container = container_of(listener, VFIOContainer, listener); + VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, + listener); + int ret = 0; - vfio_set_dirty_page_tracking(container, false); + if (vfio_devices_all_device_dirty_tracking(bcontainer)) { + vfio_devices_dma_logging_stop(bcontainer); + } else { + ret = vfio_container_set_dirty_page_tracking(bcontainer, false); + } + + if (ret) { + error_report("vfio: Could not stop dirty page tracking, err: %d (%s)", + ret, strerror(-ret)); + vfio_set_migration_error(ret); + } } -static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova, - uint64_t size, ram_addr_t ram_addr) +static int vfio_device_dma_logging_report(VFIODevice *vbasedev, hwaddr iova, + hwaddr size, void *bitmap) { - struct vfio_iommu_type1_dirty_bitmap *dbitmap; - struct vfio_iommu_type1_dirty_bitmap_get *range; - uint64_t pages; + uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature) + + sizeof(struct vfio_device_feature_dma_logging_report), + sizeof(uint64_t))] = {}; + struct vfio_device_feature *feature = (struct vfio_device_feature *)buf; + struct vfio_device_feature_dma_logging_report *report = + (struct vfio_device_feature_dma_logging_report *)feature->data; + + report->iova = iova; + report->length = size; + report->page_size = qemu_real_host_page_size(); + report->bitmap = (uintptr_t)bitmap; + + feature->argsz = sizeof(buf); + feature->flags = VFIO_DEVICE_FEATURE_GET | + VFIO_DEVICE_FEATURE_DMA_LOGGING_REPORT; + + if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) { + return -errno; + } + + return 0; +} + +int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, + hwaddr size) +{ + VFIODevice *vbasedev; int ret; - dbitmap = g_malloc0(sizeof(*dbitmap) + sizeof(*range)); + QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { + ret = vfio_device_dma_logging_report(vbasedev, iova, size, + vbmap->bitmap); + if (ret) { + error_report("%s: Failed to get DMA logging report, iova: " + "0x%" HWADDR_PRIx ", size: 0x%" HWADDR_PRIx + ", err: %d (%s)", + vbasedev->name, iova, size, ret, strerror(-ret)); - dbitmap->argsz = sizeof(*dbitmap) + sizeof(*range); - dbitmap->flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP; - range = (struct vfio_iommu_type1_dirty_bitmap_get *)&dbitmap->data; - range->iova = iova; - range->size = size; - - /* - * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of - * qemu_real_host_page_size to mark those dirty. Hence set bitmap's pgsize - * to qemu_real_host_page_size. - */ - range->bitmap.pgsize = qemu_real_host_page_size(); - - pages = REAL_HOST_PAGE_ALIGN(range->size) / qemu_real_host_page_size(); - range->bitmap.size = ROUND_UP(pages, sizeof(__u64) * BITS_PER_BYTE) / - BITS_PER_BYTE; - range->bitmap.data = g_try_malloc0(range->bitmap.size); - if (!range->bitmap.data) { - ret = -ENOMEM; - goto err_out; + return ret; + } } - ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, dbitmap); + return 0; +} + +int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, + uint64_t size, ram_addr_t ram_addr) +{ + bool all_device_dirty_tracking = + vfio_devices_all_device_dirty_tracking(bcontainer); + uint64_t dirty_pages; + VFIOBitmap vbmap; + int ret; + + if (!bcontainer->dirty_pages_supported && !all_device_dirty_tracking) { + cpu_physical_memory_set_dirty_range(ram_addr, size, + tcg_enabled() ? DIRTY_CLIENTS_ALL : + DIRTY_CLIENTS_NOCODE); + return 0; + } + + ret = vfio_bitmap_alloc(&vbmap, size); if (ret) { - error_report("Failed to get dirty bitmap for iova: 0x%"PRIx64 - " size: 0x%"PRIx64" err: %d", (uint64_t)range->iova, - (uint64_t)range->size, errno); - goto err_out; + return ret; } - cpu_physical_memory_set_dirty_lebitmap((unsigned long *)range->bitmap.data, - ram_addr, pages); + if (all_device_dirty_tracking) { + ret = vfio_devices_query_dirty_bitmap(bcontainer, &vbmap, iova, size); + } else { + ret = vfio_container_query_dirty_bitmap(bcontainer, &vbmap, iova, size); + } - trace_vfio_get_dirty_bitmap(container->fd, range->iova, range->size, - range->bitmap.size, ram_addr); -err_out: - g_free(range->bitmap.data); - g_free(dbitmap); + if (ret) { + goto out; + } + + dirty_pages = cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, ram_addr, + vbmap.pages); + + trace_vfio_get_dirty_bitmap(iova, size, vbmap.size, ram_addr, dirty_pages); +out: + g_free(vbmap.bitmap); return ret; } @@ -1346,32 +1204,36 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) vfio_giommu_dirty_notifier *gdn = container_of(n, vfio_giommu_dirty_notifier, n); VFIOGuestIOMMU *giommu = gdn->giommu; - VFIOContainer *container = giommu->container; + VFIOContainerBase *bcontainer = giommu->bcontainer; hwaddr iova = iotlb->iova + giommu->iommu_offset; ram_addr_t translated_addr; + int ret = -EINVAL; trace_vfio_iommu_map_dirty_notify(iova, iova + iotlb->addr_mask); if (iotlb->target_as != &address_space_memory) { error_report("Wrong target AS \"%s\", only system memory is allowed", iotlb->target_as->name ? iotlb->target_as->name : "none"); - return; + goto out; } rcu_read_lock(); if (vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL)) { - int ret; - - ret = vfio_get_dirty_bitmap(container, iova, iotlb->addr_mask + 1, + ret = vfio_get_dirty_bitmap(bcontainer, iova, iotlb->addr_mask + 1, translated_addr); if (ret) { error_report("vfio_iommu_map_dirty_notify(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%m)", - container, iova, - iotlb->addr_mask + 1, ret); + "0x%"HWADDR_PRIx") = %d (%s)", + bcontainer, iova, iotlb->addr_mask + 1, ret, + strerror(-ret)); } } rcu_read_unlock(); + +out: + if (ret) { + vfio_set_migration_error(ret); + } } static int vfio_ram_discard_get_dirty_bitmap(MemoryRegionSection *section, @@ -1387,16 +1249,17 @@ static int vfio_ram_discard_get_dirty_bitmap(MemoryRegionSection *section, * Sync the whole mapped region (spanning multiple individual mappings) * in one go. */ - return vfio_get_dirty_bitmap(vrdl->container, iova, size, ram_addr); + return vfio_get_dirty_bitmap(vrdl->bcontainer, iova, size, ram_addr); } -static int vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainer *container, - MemoryRegionSection *section) +static int +vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainerBase *bcontainer, + MemoryRegionSection *section) { RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); VFIORamDiscardListener *vrdl = NULL; - QLIST_FOREACH(vrdl, &container->vrdl_list, next) { + QLIST_FOREACH(vrdl, &bcontainer->vrdl_list, next) { if (vrdl->mr == section->mr && vrdl->offset_within_address_space == section->offset_within_address_space) { @@ -1417,7 +1280,7 @@ static int vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainer *container, &vrdl); } -static int vfio_sync_dirty_bitmap(VFIOContainer *container, +static int vfio_sync_dirty_bitmap(VFIOContainerBase *bcontainer, MemoryRegionSection *section) { ram_addr_t ram_addr; @@ -1425,7 +1288,7 @@ static int vfio_sync_dirty_bitmap(VFIOContainer *container, if (memory_region_is_iommu(section->mr)) { VFIOGuestIOMMU *giommu; - QLIST_FOREACH(giommu, &container->giommu_list, giommu_next) { + QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) { if (MEMORY_REGION(giommu->iommu_mr) == section->mr && giommu->n.start == section->offset_within_region) { Int128 llend; @@ -1449,13 +1312,13 @@ static int vfio_sync_dirty_bitmap(VFIOContainer *container, } return 0; } else if (memory_region_has_ram_discard_manager(section->mr)) { - return vfio_sync_ram_discard_listener_dirty_bitmap(container, section); + return vfio_sync_ram_discard_listener_dirty_bitmap(bcontainer, section); } ram_addr = memory_region_get_ram_addr(section->mr) + section->offset_within_region; - return vfio_get_dirty_bitmap(container, + return vfio_get_dirty_bitmap(bcontainer, REAL_HOST_PAGE_ALIGN(section->offset_within_address_space), int128_get64(section->size), ram_addr); } @@ -1463,19 +1326,25 @@ static int vfio_sync_dirty_bitmap(VFIOContainer *container, static void vfio_listener_log_sync(MemoryListener *listener, MemoryRegionSection *section) { - VFIOContainer *container = container_of(listener, VFIOContainer, listener); + VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, + listener); + int ret; - if (vfio_listener_skipped_section(section) || - !container->dirty_pages_supported) { + if (vfio_listener_skipped_section(section)) { return; } - if (vfio_devices_all_dirty_tracking(container)) { - vfio_sync_dirty_bitmap(container, section); + if (vfio_devices_all_dirty_tracking(bcontainer)) { + ret = vfio_sync_dirty_bitmap(bcontainer, section); + if (ret) { + error_report("vfio: Failed to sync dirty bitmap, err: %d (%s)", ret, + strerror(-ret)); + vfio_set_migration_error(ret); + } } } -static const MemoryListener vfio_memory_listener = { +const MemoryListener vfio_memory_listener = { .name = "vfio", .region_add = vfio_listener_region_add, .region_del = vfio_listener_region_del, @@ -1484,338 +1353,34 @@ static const MemoryListener vfio_memory_listener = { .log_sync = vfio_listener_log_sync, }; -static void vfio_listener_release(VFIOContainer *container) -{ - memory_listener_unregister(&container->listener); - if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { - memory_listener_unregister(&container->prereg_listener); - } -} - -static struct vfio_info_cap_header * -vfio_get_cap(void *ptr, uint32_t cap_offset, uint16_t id) -{ - struct vfio_info_cap_header *hdr; - - for (hdr = ptr + cap_offset; hdr != ptr; hdr = ptr + hdr->next) { - if (hdr->id == id) { - return hdr; - } - } - - return NULL; -} - -struct vfio_info_cap_header * -vfio_get_region_info_cap(struct vfio_region_info *info, uint16_t id) -{ - if (!(info->flags & VFIO_REGION_INFO_FLAG_CAPS)) { - return NULL; - } - - return vfio_get_cap((void *)info, info->cap_offset, id); -} - -static struct vfio_info_cap_header * -vfio_get_iommu_type1_info_cap(struct vfio_iommu_type1_info *info, uint16_t id) -{ - if (!(info->flags & VFIO_IOMMU_INFO_CAPS)) { - return NULL; - } - - return vfio_get_cap((void *)info, info->cap_offset, id); -} - -struct vfio_info_cap_header * -vfio_get_device_info_cap(struct vfio_device_info *info, uint16_t id) -{ - if (!(info->flags & VFIO_DEVICE_FLAGS_CAPS)) { - return NULL; - } - - return vfio_get_cap((void *)info, info->cap_offset, id); -} - -bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, - unsigned int *avail) -{ - struct vfio_info_cap_header *hdr; - struct vfio_iommu_type1_info_dma_avail *cap; - - /* If the capability cannot be found, assume no DMA limiting */ - hdr = vfio_get_iommu_type1_info_cap(info, - VFIO_IOMMU_TYPE1_INFO_DMA_AVAIL); - if (hdr == NULL) { - return false; - } - - if (avail != NULL) { - cap = (void *) hdr; - *avail = cap->avail; - } - - return true; -} - -static int vfio_setup_region_sparse_mmaps(VFIORegion *region, - struct vfio_region_info *info) -{ - struct vfio_info_cap_header *hdr; - struct vfio_region_info_cap_sparse_mmap *sparse; - int i, j; - - hdr = vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP); - if (!hdr) { - return -ENODEV; - } - - sparse = container_of(hdr, struct vfio_region_info_cap_sparse_mmap, header); - - trace_vfio_region_sparse_mmap_header(region->vbasedev->name, - region->nr, sparse->nr_areas); - - region->mmaps = g_new0(VFIOMmap, sparse->nr_areas); - - for (i = 0, j = 0; i < sparse->nr_areas; i++) { - if (sparse->areas[i].size) { - trace_vfio_region_sparse_mmap_entry(i, sparse->areas[i].offset, - sparse->areas[i].offset + - sparse->areas[i].size - 1); - region->mmaps[j].offset = sparse->areas[i].offset; - region->mmaps[j].size = sparse->areas[i].size; - j++; - } - } - - region->nr_mmaps = j; - region->mmaps = g_realloc(region->mmaps, j * sizeof(VFIOMmap)); - - return 0; -} - -int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, - int index, const char *name) -{ - struct vfio_region_info *info; - int ret; - - ret = vfio_get_region_info(vbasedev, index, &info); - if (ret) { - return ret; - } - - region->vbasedev = vbasedev; - region->flags = info->flags; - region->size = info->size; - region->fd_offset = info->offset; - region->nr = index; - - if (region->size) { - region->mem = g_new0(MemoryRegion, 1); - memory_region_init_io(region->mem, obj, &vfio_region_ops, - region, name, region->size); - - if (!vbasedev->no_mmap && - region->flags & VFIO_REGION_INFO_FLAG_MMAP) { - - ret = vfio_setup_region_sparse_mmaps(region, info); - - if (ret) { - region->nr_mmaps = 1; - region->mmaps = g_new0(VFIOMmap, region->nr_mmaps); - region->mmaps[0].offset = 0; - region->mmaps[0].size = region->size; - } - } - } - - g_free(info); - - trace_vfio_region_setup(vbasedev->name, index, name, - region->flags, region->fd_offset, region->size); - return 0; -} - -static void vfio_subregion_unmap(VFIORegion *region, int index) -{ - trace_vfio_region_unmap(memory_region_name(®ion->mmaps[index].mem), - region->mmaps[index].offset, - region->mmaps[index].offset + - region->mmaps[index].size - 1); - memory_region_del_subregion(region->mem, ®ion->mmaps[index].mem); - munmap(region->mmaps[index].mmap, region->mmaps[index].size); - object_unparent(OBJECT(®ion->mmaps[index].mem)); - region->mmaps[index].mmap = NULL; -} - -int vfio_region_mmap(VFIORegion *region) -{ - int i, prot = 0; - char *name; - - if (!region->mem) { - return 0; - } - - prot |= region->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0; - prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0; - - for (i = 0; i < region->nr_mmaps; i++) { - region->mmaps[i].mmap = mmap(NULL, region->mmaps[i].size, prot, - MAP_SHARED, region->vbasedev->fd, - region->fd_offset + - region->mmaps[i].offset); - if (region->mmaps[i].mmap == MAP_FAILED) { - int ret = -errno; - - trace_vfio_region_mmap_fault(memory_region_name(region->mem), i, - region->fd_offset + - region->mmaps[i].offset, - region->fd_offset + - region->mmaps[i].offset + - region->mmaps[i].size - 1, ret); - - region->mmaps[i].mmap = NULL; - - for (i--; i >= 0; i--) { - vfio_subregion_unmap(region, i); - } - - return ret; - } - - name = g_strdup_printf("%s mmaps[%d]", - memory_region_name(region->mem), i); - memory_region_init_ram_device_ptr(®ion->mmaps[i].mem, - memory_region_owner(region->mem), - name, region->mmaps[i].size, - region->mmaps[i].mmap); - g_free(name); - memory_region_add_subregion(region->mem, region->mmaps[i].offset, - ®ion->mmaps[i].mem); - - trace_vfio_region_mmap(memory_region_name(®ion->mmaps[i].mem), - region->mmaps[i].offset, - region->mmaps[i].offset + - region->mmaps[i].size - 1); - } - - return 0; -} - -void vfio_region_unmap(VFIORegion *region) -{ - int i; - - if (!region->mem) { - return; - } - - for (i = 0; i < region->nr_mmaps; i++) { - if (region->mmaps[i].mmap) { - vfio_subregion_unmap(region, i); - } - } -} - -void vfio_region_exit(VFIORegion *region) -{ - int i; - - if (!region->mem) { - return; - } - - for (i = 0; i < region->nr_mmaps; i++) { - if (region->mmaps[i].mmap) { - memory_region_del_subregion(region->mem, ®ion->mmaps[i].mem); - } - } - - trace_vfio_region_exit(region->vbasedev->name, region->nr); -} - -void vfio_region_finalize(VFIORegion *region) -{ - int i; - - if (!region->mem) { - return; - } - - for (i = 0; i < region->nr_mmaps; i++) { - if (region->mmaps[i].mmap) { - munmap(region->mmaps[i].mmap, region->mmaps[i].size); - object_unparent(OBJECT(®ion->mmaps[i].mem)); - } - } - - object_unparent(OBJECT(region->mem)); - - g_free(region->mem); - g_free(region->mmaps); - - trace_vfio_region_finalize(region->vbasedev->name, region->nr); - - region->mem = NULL; - region->mmaps = NULL; - region->nr_mmaps = 0; - region->size = 0; - region->flags = 0; - region->nr = 0; -} - -void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled) -{ - int i; - - if (!region->mem) { - return; - } - - for (i = 0; i < region->nr_mmaps; i++) { - if (region->mmaps[i].mmap) { - memory_region_set_enabled(®ion->mmaps[i].mem, enabled); - } - } - - trace_vfio_region_mmaps_set_enabled(memory_region_name(region->mem), - enabled); -} - void vfio_reset_handler(void *opaque) { - VFIOGroup *group; VFIODevice *vbasedev; - QLIST_FOREACH(group, &vfio_group_list, next) { - QLIST_FOREACH(vbasedev, &group->device_list, next) { - if (vbasedev->dev->realized) { - vbasedev->ops->vfio_compute_needs_reset(vbasedev); - } + QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { + if (vbasedev->dev->realized) { + vbasedev->ops->vfio_compute_needs_reset(vbasedev); } } - QLIST_FOREACH(group, &vfio_group_list, next) { - QLIST_FOREACH(vbasedev, &group->device_list, next) { - if (vbasedev->dev->realized && vbasedev->needs_reset) { - vbasedev->ops->vfio_hot_reset_multi(vbasedev); - } + QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { + if (vbasedev->dev->realized && vbasedev->needs_reset) { + vbasedev->ops->vfio_hot_reset_multi(vbasedev); } } } -static void vfio_kvm_device_add_group(VFIOGroup *group) +int vfio_kvm_device_add_fd(int fd, Error **errp) { #ifdef CONFIG_KVM struct kvm_device_attr attr = { - .group = KVM_DEV_VFIO_GROUP, - .attr = KVM_DEV_VFIO_GROUP_ADD, - .addr = (uint64_t)(unsigned long)&group->fd, + .group = KVM_DEV_VFIO_FILE, + .attr = KVM_DEV_VFIO_FILE_ADD, + .addr = (uint64_t)(unsigned long)&fd, }; if (!kvm_enabled()) { - return; + return 0; } if (vfio_kvm_device_fd < 0) { @@ -1824,41 +1389,46 @@ static void vfio_kvm_device_add_group(VFIOGroup *group) }; if (kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd)) { - error_report("Failed to create KVM VFIO device: %m"); - return; + error_setg_errno(errp, errno, "Failed to create KVM VFIO device"); + return -errno; } vfio_kvm_device_fd = cd.fd; } if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { - error_report("Failed to add group %d to KVM VFIO device: %m", - group->groupid); + error_setg_errno(errp, errno, "Failed to add fd %d to KVM VFIO device", + fd); + return -errno; } #endif + return 0; } -static void vfio_kvm_device_del_group(VFIOGroup *group) +int vfio_kvm_device_del_fd(int fd, Error **errp) { #ifdef CONFIG_KVM struct kvm_device_attr attr = { - .group = KVM_DEV_VFIO_GROUP, - .attr = KVM_DEV_VFIO_GROUP_DEL, - .addr = (uint64_t)(unsigned long)&group->fd, + .group = KVM_DEV_VFIO_FILE, + .attr = KVM_DEV_VFIO_FILE_DEL, + .addr = (uint64_t)(unsigned long)&fd, }; if (vfio_kvm_device_fd < 0) { - return; + error_setg(errp, "KVM VFIO device isn't created yet"); + return -EINVAL; } if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { - error_report("Failed to remove group %d from KVM VFIO device: %m", - group->groupid); + error_setg_errno(errp, errno, + "Failed to remove fd %d from KVM VFIO device", fd); + return -errno; } #endif + return 0; } -static VFIOAddressSpace *vfio_get_address_space(AddressSpace *as) +VFIOAddressSpace *vfio_get_address_space(AddressSpace *as) { VFIOAddressSpace *space; @@ -1873,756 +1443,72 @@ static VFIOAddressSpace *vfio_get_address_space(AddressSpace *as) space->as = as; QLIST_INIT(&space->containers); + if (QLIST_EMPTY(&vfio_address_spaces)) { + qemu_register_reset(vfio_reset_handler, NULL); + } + QLIST_INSERT_HEAD(&vfio_address_spaces, space, list); return space; } -static void vfio_put_address_space(VFIOAddressSpace *space) +void vfio_put_address_space(VFIOAddressSpace *space) { - if (QLIST_EMPTY(&space->containers)) { - QLIST_REMOVE(space, list); - g_free(space); - } -} - -/* - * vfio_get_iommu_type - selects the richest iommu_type (v2 first) - */ -static int vfio_get_iommu_type(VFIOContainer *container, - Error **errp) -{ - int iommu_types[] = { VFIO_TYPE1v2_IOMMU, VFIO_TYPE1_IOMMU, - VFIO_SPAPR_TCE_v2_IOMMU, VFIO_SPAPR_TCE_IOMMU }; - int i; - - for (i = 0; i < ARRAY_SIZE(iommu_types); i++) { - if (ioctl(container->fd, VFIO_CHECK_EXTENSION, iommu_types[i])) { - return iommu_types[i]; - } - } - error_setg(errp, "No available IOMMU models"); - return -EINVAL; -} - -static int vfio_init_container(VFIOContainer *container, int group_fd, - Error **errp) -{ - int iommu_type, ret; - - iommu_type = vfio_get_iommu_type(container, errp); - if (iommu_type < 0) { - return iommu_type; - } - - ret = ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container->fd); - if (ret) { - error_setg_errno(errp, errno, "Failed to set group container"); - return -errno; - } - - while (ioctl(container->fd, VFIO_SET_IOMMU, iommu_type)) { - if (iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { - /* - * On sPAPR, despite the IOMMU subdriver always advertises v1 and - * v2, the running platform may not support v2 and there is no - * way to guess it until an IOMMU group gets added to the container. - * So in case it fails with v2, try v1 as a fallback. - */ - iommu_type = VFIO_SPAPR_TCE_IOMMU; - continue; - } - error_setg_errno(errp, errno, "Failed to set iommu for container"); - return -errno; - } - - container->iommu_type = iommu_type; - return 0; -} - -static int vfio_get_iommu_info(VFIOContainer *container, - struct vfio_iommu_type1_info **info) -{ - - size_t argsz = sizeof(struct vfio_iommu_type1_info); - - *info = g_new0(struct vfio_iommu_type1_info, 1); -again: - (*info)->argsz = argsz; - - if (ioctl(container->fd, VFIO_IOMMU_GET_INFO, *info)) { - g_free(*info); - *info = NULL; - return -errno; - } - - if (((*info)->argsz > argsz)) { - argsz = (*info)->argsz; - *info = g_realloc(*info, argsz); - goto again; - } - - return 0; -} - -static struct vfio_info_cap_header * -vfio_get_iommu_info_cap(struct vfio_iommu_type1_info *info, uint16_t id) -{ - struct vfio_info_cap_header *hdr; - void *ptr = info; - - if (!(info->flags & VFIO_IOMMU_INFO_CAPS)) { - return NULL; - } - - for (hdr = ptr + info->cap_offset; hdr != ptr; hdr = ptr + hdr->next) { - if (hdr->id == id) { - return hdr; - } - } - - return NULL; -} - -static void vfio_get_iommu_info_migration(VFIOContainer *container, - struct vfio_iommu_type1_info *info) -{ - struct vfio_info_cap_header *hdr; - struct vfio_iommu_type1_info_cap_migration *cap_mig; - - hdr = vfio_get_iommu_info_cap(info, VFIO_IOMMU_TYPE1_INFO_CAP_MIGRATION); - if (!hdr) { + if (!QLIST_EMPTY(&space->containers)) { return; } - cap_mig = container_of(hdr, struct vfio_iommu_type1_info_cap_migration, - header); + QLIST_REMOVE(space, list); + g_free(space); - /* - * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of - * qemu_real_host_page_size to mark those dirty. - */ - if (cap_mig->pgsize_bitmap & qemu_real_host_page_size()) { - container->dirty_pages_supported = true; - container->max_dirty_bitmap_size = cap_mig->max_dirty_bitmap_size; - container->dirty_pgsizes = cap_mig->pgsize_bitmap; - } -} - -static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, - Error **errp) -{ - VFIOContainer *container; - int ret, fd; - VFIOAddressSpace *space; - - space = vfio_get_address_space(as); - - /* - * VFIO is currently incompatible with discarding of RAM insofar as the - * madvise to purge (zap) the page from QEMU's address space does not - * interact with the memory API and therefore leaves stale virtual to - * physical mappings in the IOMMU if the page was previously pinned. We - * therefore set discarding broken for each group added to a container, - * whether the container is used individually or shared. This provides - * us with options to allow devices within a group to opt-in and allow - * discarding, so long as it is done consistently for a group (for instance - * if the device is an mdev device where it is known that the host vendor - * driver will never pin pages outside of the working set of the guest - * driver, which would thus not be discarding candidates). - * - * The first opportunity to induce pinning occurs here where we attempt to - * attach the group to existing containers within the AddressSpace. If any - * pages are already zapped from the virtual address space, such as from - * previous discards, new pinning will cause valid mappings to be - * re-established. Likewise, when the overall MemoryListener for a new - * container is registered, a replay of mappings within the AddressSpace - * will occur, re-establishing any previously zapped pages as well. - * - * Especially virtio-balloon is currently only prevented from discarding - * new memory, it will not yet set ram_block_discard_set_required() and - * therefore, neither stops us here or deals with the sudden memory - * consumption of inflated memory. - * - * We do support discarding of memory coordinated via the RamDiscardManager - * with some IOMMU types. vfio_ram_block_discard_disable() handles the - * details once we know which type of IOMMU we are using. - */ - - QLIST_FOREACH(container, &space->containers, next) { - if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { - ret = vfio_ram_block_discard_disable(container, true); - if (ret) { - error_setg_errno(errp, -ret, - "Cannot set discarding of RAM broken"); - if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER, - &container->fd)) { - error_report("vfio: error disconnecting group %d from" - " container", group->groupid); - } - return ret; - } - group->container = container; - QLIST_INSERT_HEAD(&container->group_list, group, container_next); - vfio_kvm_device_add_group(group); - return 0; - } - } - - fd = qemu_open_old("/dev/vfio/vfio", O_RDWR); - if (fd < 0) { - error_setg_errno(errp, errno, "failed to open /dev/vfio/vfio"); - ret = -errno; - goto put_space_exit; - } - - ret = ioctl(fd, VFIO_GET_API_VERSION); - if (ret != VFIO_API_VERSION) { - error_setg(errp, "supported vfio version: %d, " - "reported version: %d", VFIO_API_VERSION, ret); - ret = -EINVAL; - goto close_fd_exit; - } - - container = g_malloc0(sizeof(*container)); - container->space = space; - container->fd = fd; - container->error = NULL; - container->dirty_pages_supported = false; - container->dma_max_mappings = 0; - QLIST_INIT(&container->giommu_list); - QLIST_INIT(&container->hostwin_list); - QLIST_INIT(&container->vrdl_list); - - ret = vfio_init_container(container, group->fd, errp); - if (ret) { - goto free_container_exit; - } - - ret = vfio_ram_block_discard_disable(container, true); - if (ret) { - error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken"); - goto free_container_exit; - } - - switch (container->iommu_type) { - case VFIO_TYPE1v2_IOMMU: - case VFIO_TYPE1_IOMMU: - { - struct vfio_iommu_type1_info *info; - - /* - * FIXME: This assumes that a Type1 IOMMU can map any 64-bit - * IOVA whatsoever. That's not actually true, but the current - * kernel interface doesn't tell us what it can map, and the - * existing Type1 IOMMUs generally support any IOVA we're - * going to actually try in practice. - */ - ret = vfio_get_iommu_info(container, &info); - - if (ret || !(info->flags & VFIO_IOMMU_INFO_PGSIZES)) { - /* Assume 4k IOVA page size */ - info->iova_pgsizes = 4096; - } - vfio_host_win_add(container, 0, (hwaddr)-1, info->iova_pgsizes); - container->pgsizes = info->iova_pgsizes; - - /* The default in the kernel ("dma_entry_limit") is 65535. */ - container->dma_max_mappings = 65535; - if (!ret) { - vfio_get_info_dma_avail(info, &container->dma_max_mappings); - vfio_get_iommu_info_migration(container, info); - } - g_free(info); - break; - } - case VFIO_SPAPR_TCE_v2_IOMMU: - case VFIO_SPAPR_TCE_IOMMU: - { - struct vfio_iommu_spapr_tce_info info; - bool v2 = container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU; - - /* - * The host kernel code implementing VFIO_IOMMU_DISABLE is called - * when container fd is closed so we do not call it explicitly - * in this file. - */ - if (!v2) { - ret = ioctl(fd, VFIO_IOMMU_ENABLE); - if (ret) { - error_setg_errno(errp, errno, "failed to enable container"); - ret = -errno; - goto enable_discards_exit; - } - } else { - container->prereg_listener = vfio_prereg_listener; - - memory_listener_register(&container->prereg_listener, - &address_space_memory); - if (container->error) { - memory_listener_unregister(&container->prereg_listener); - ret = -1; - error_propagate_prepend(errp, container->error, - "RAM memory listener initialization failed: "); - goto enable_discards_exit; - } - } - - info.argsz = sizeof(info); - ret = ioctl(fd, VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info); - if (ret) { - error_setg_errno(errp, errno, - "VFIO_IOMMU_SPAPR_TCE_GET_INFO failed"); - ret = -errno; - if (v2) { - memory_listener_unregister(&container->prereg_listener); - } - goto enable_discards_exit; - } - - if (v2) { - container->pgsizes = info.ddw.pgsizes; - /* - * There is a default window in just created container. - * To make region_add/del simpler, we better remove this - * window now and let those iommu_listener callbacks - * create/remove them when needed. - */ - ret = vfio_spapr_remove_window(container, info.dma32_window_start); - if (ret) { - error_setg_errno(errp, -ret, - "failed to remove existing window"); - goto enable_discards_exit; - } - } else { - /* The default table uses 4K pages */ - container->pgsizes = 0x1000; - vfio_host_win_add(container, info.dma32_window_start, - info.dma32_window_start + - info.dma32_window_size - 1, - 0x1000); - } - } - } - - vfio_kvm_device_add_group(group); - - QLIST_INIT(&container->group_list); - QLIST_INSERT_HEAD(&space->containers, container, next); - - group->container = container; - QLIST_INSERT_HEAD(&container->group_list, group, container_next); - - container->listener = vfio_memory_listener; - - memory_listener_register(&container->listener, container->space->as); - - if (container->error) { - ret = -1; - error_propagate_prepend(errp, container->error, - "memory listener initialization failed: "); - goto listener_release_exit; - } - - container->initialized = true; - - return 0; -listener_release_exit: - QLIST_REMOVE(group, container_next); - QLIST_REMOVE(container, next); - vfio_kvm_device_del_group(group); - vfio_listener_release(container); - -enable_discards_exit: - vfio_ram_block_discard_disable(container, false); - -free_container_exit: - g_free(container); - -close_fd_exit: - close(fd); - -put_space_exit: - vfio_put_address_space(space); - - return ret; -} - -static void vfio_disconnect_container(VFIOGroup *group) -{ - VFIOContainer *container = group->container; - - QLIST_REMOVE(group, container_next); - group->container = NULL; - - /* - * Explicitly release the listener first before unset container, - * since unset may destroy the backend container if it's the last - * group. - */ - if (QLIST_EMPTY(&container->group_list)) { - vfio_listener_release(container); - } - - if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER, &container->fd)) { - error_report("vfio: error disconnecting group %d from container", - group->groupid); - } - - if (QLIST_EMPTY(&container->group_list)) { - VFIOAddressSpace *space = container->space; - VFIOGuestIOMMU *giommu, *tmp; - VFIOHostDMAWindow *hostwin, *next; - - QLIST_REMOVE(container, next); - - QLIST_FOREACH_SAFE(giommu, &container->giommu_list, giommu_next, tmp) { - memory_region_unregister_iommu_notifier( - MEMORY_REGION(giommu->iommu_mr), &giommu->n); - QLIST_REMOVE(giommu, giommu_next); - g_free(giommu); - } - - QLIST_FOREACH_SAFE(hostwin, &container->hostwin_list, hostwin_next, - next) { - QLIST_REMOVE(hostwin, hostwin_next); - g_free(hostwin); - } - - trace_vfio_disconnect_container(container->fd); - close(container->fd); - g_free(container); - - vfio_put_address_space(space); - } -} - -VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp) -{ - VFIOGroup *group; - char path[32]; - struct vfio_group_status status = { .argsz = sizeof(status) }; - - QLIST_FOREACH(group, &vfio_group_list, next) { - if (group->groupid == groupid) { - /* Found it. Now is it already in the right context? */ - if (group->container->space->as == as) { - return group; - } else { - error_setg(errp, "group %d used in multiple address spaces", - group->groupid); - return NULL; - } - } - } - - group = g_malloc0(sizeof(*group)); - - snprintf(path, sizeof(path), "/dev/vfio/%d", groupid); - group->fd = qemu_open_old(path, O_RDWR); - if (group->fd < 0) { - error_setg_errno(errp, errno, "failed to open %s", path); - goto free_group_exit; - } - - if (ioctl(group->fd, VFIO_GROUP_GET_STATUS, &status)) { - error_setg_errno(errp, errno, "failed to get group %d status", groupid); - goto close_fd_exit; - } - - if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) { - error_setg(errp, "group %d is not viable", groupid); - error_append_hint(errp, - "Please ensure all devices within the iommu_group " - "are bound to their vfio bus driver.\n"); - goto close_fd_exit; - } - - group->groupid = groupid; - QLIST_INIT(&group->device_list); - - if (vfio_connect_container(group, as, errp)) { - error_prepend(errp, "failed to setup container for group %d: ", - groupid); - goto close_fd_exit; - } - - if (QLIST_EMPTY(&vfio_group_list)) { - qemu_register_reset(vfio_reset_handler, NULL); - } - - QLIST_INSERT_HEAD(&vfio_group_list, group, next); - - return group; - -close_fd_exit: - close(group->fd); - -free_group_exit: - g_free(group); - - return NULL; -} - -void vfio_put_group(VFIOGroup *group) -{ - if (!group || !QLIST_EMPTY(&group->device_list)) { - return; - } - - if (!group->ram_block_discard_allowed) { - vfio_ram_block_discard_disable(group->container, false); - } - vfio_kvm_device_del_group(group); - vfio_disconnect_container(group); - QLIST_REMOVE(group, next); - trace_vfio_put_group(group->fd); - close(group->fd); - g_free(group); - - if (QLIST_EMPTY(&vfio_group_list)) { + if (QLIST_EMPTY(&vfio_address_spaces)) { qemu_unregister_reset(vfio_reset_handler, NULL); } } -int vfio_get_device(VFIOGroup *group, const char *name, - VFIODevice *vbasedev, Error **errp) +struct vfio_device_info *vfio_get_device_info(int fd) { - struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) }; - int ret, fd; + struct vfio_device_info *info; + uint32_t argsz = sizeof(*info); - fd = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name); - if (fd < 0) { - error_setg_errno(errp, errno, "error getting device from group %d", - group->groupid); - error_append_hint(errp, - "Verify all devices in group %d are bound to vfio- " - "or pci-stub and not already in use\n", group->groupid); - return fd; - } + info = g_malloc0(argsz); - ret = ioctl(fd, VFIO_DEVICE_GET_INFO, &dev_info); - if (ret) { - error_setg_errno(errp, errno, "error getting device info"); - close(fd); - return ret; - } - - /* - * Set discarding of RAM as not broken for this group if the driver knows - * the device operates compatibly with discarding. Setting must be - * consistent per group, but since compatibility is really only possible - * with mdev currently, we expect singleton groups. - */ - if (vbasedev->ram_block_discard_allowed != - group->ram_block_discard_allowed) { - if (!QLIST_EMPTY(&group->device_list)) { - error_setg(errp, "Inconsistent setting of support for discarding " - "RAM (e.g., balloon) within group"); - close(fd); - return -1; - } - - if (!group->ram_block_discard_allowed) { - group->ram_block_discard_allowed = true; - vfio_ram_block_discard_disable(group->container, false); - } - } - - vbasedev->fd = fd; - vbasedev->group = group; - QLIST_INSERT_HEAD(&group->device_list, vbasedev, next); - - vbasedev->num_irqs = dev_info.num_irqs; - vbasedev->num_regions = dev_info.num_regions; - vbasedev->flags = dev_info.flags; - - trace_vfio_get_device(name, dev_info.flags, dev_info.num_regions, - dev_info.num_irqs); - - vbasedev->reset_works = !!(dev_info.flags & VFIO_DEVICE_FLAGS_RESET); - return 0; -} - -void vfio_put_base_device(VFIODevice *vbasedev) -{ - if (!vbasedev->group) { - return; - } - QLIST_REMOVE(vbasedev, next); - vbasedev->group = NULL; - trace_vfio_put_base_device(vbasedev->fd); - close(vbasedev->fd); -} - -int vfio_get_region_info(VFIODevice *vbasedev, int index, - struct vfio_region_info **info) -{ - size_t argsz = sizeof(struct vfio_region_info); - - *info = g_malloc0(argsz); - - (*info)->index = index; retry: - (*info)->argsz = argsz; + info->argsz = argsz; - if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) { - g_free(*info); - *info = NULL; - return -errno; + if (ioctl(fd, VFIO_DEVICE_GET_INFO, info)) { + g_free(info); + return NULL; } - if ((*info)->argsz > argsz) { - argsz = (*info)->argsz; - *info = g_realloc(*info, argsz); - + if (info->argsz > argsz) { + argsz = info->argsz; + info = g_realloc(info, argsz); goto retry; } - return 0; + return info; } -int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, - uint32_t subtype, struct vfio_region_info **info) +int vfio_attach_device(char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp) { - int i; + const VFIOIOMMUClass *ops = + VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_LEGACY)); - for (i = 0; i < vbasedev->num_regions; i++) { - struct vfio_info_cap_header *hdr; - struct vfio_region_info_cap_type *cap_type; - - if (vfio_get_region_info(vbasedev, i, info)) { - continue; - } - - hdr = vfio_get_region_info_cap(*info, VFIO_REGION_INFO_CAP_TYPE); - if (!hdr) { - g_free(*info); - continue; - } - - cap_type = container_of(hdr, struct vfio_region_info_cap_type, header); - - trace_vfio_get_dev_region(vbasedev->name, i, - cap_type->type, cap_type->subtype); - - if (cap_type->type == type && cap_type->subtype == subtype) { - return 0; - } - - g_free(*info); + if (vbasedev->iommufd) { + ops = VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_IOMMUFD)); } - *info = NULL; - return -ENODEV; + assert(ops); + + return ops->attach_device(name, vbasedev, as, errp); } -bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type) +void vfio_detach_device(VFIODevice *vbasedev) { - struct vfio_region_info *info = NULL; - bool ret = false; - - if (!vfio_get_region_info(vbasedev, region, &info)) { - if (vfio_get_region_info_cap(info, cap_type)) { - ret = true; - } - g_free(info); + if (!vbasedev->bcontainer) { + return; } - - return ret; -} - -/* - * Interfaces for IBM EEH (Enhanced Error Handling) - */ -static bool vfio_eeh_container_ok(VFIOContainer *container) -{ - /* - * As of 2016-03-04 (linux-4.5) the host kernel EEH/VFIO - * implementation is broken if there are multiple groups in a - * container. The hardware works in units of Partitionable - * Endpoints (== IOMMU groups) and the EEH operations naively - * iterate across all groups in the container, without any logic - * to make sure the groups have their state synchronized. For - * certain operations (ENABLE) that might be ok, until an error - * occurs, but for others (GET_STATE) it's clearly broken. - */ - - /* - * XXX Once fixed kernels exist, test for them here - */ - - if (QLIST_EMPTY(&container->group_list)) { - return false; - } - - if (QLIST_NEXT(QLIST_FIRST(&container->group_list), container_next)) { - return false; - } - - return true; -} - -static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op) -{ - struct vfio_eeh_pe_op pe_op = { - .argsz = sizeof(pe_op), - .op = op, - }; - int ret; - - if (!vfio_eeh_container_ok(container)) { - error_report("vfio/eeh: EEH_PE_OP 0x%x: " - "kernel requires a container with exactly one group", op); - return -EPERM; - } - - ret = ioctl(container->fd, VFIO_EEH_PE_OP, &pe_op); - if (ret < 0) { - error_report("vfio/eeh: EEH_PE_OP 0x%x failed: %m", op); - return -errno; - } - - return ret; -} - -static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) -{ - VFIOAddressSpace *space = vfio_get_address_space(as); - VFIOContainer *container = NULL; - - if (QLIST_EMPTY(&space->containers)) { - /* No containers to act on */ - goto out; - } - - container = QLIST_FIRST(&space->containers); - - if (QLIST_NEXT(container, next)) { - /* We don't yet have logic to synchronize EEH state across - * multiple containers */ - container = NULL; - goto out; - } - -out: - vfio_put_address_space(space); - return container; -} - -bool vfio_eeh_as_ok(AddressSpace *as) -{ - VFIOContainer *container = vfio_eeh_as_container(as); - - return (container != NULL) && vfio_eeh_container_ok(container); -} - -int vfio_eeh_as_op(AddressSpace *as, uint32_t op) -{ - VFIOContainer *container = vfio_eeh_as_container(as); - - if (!container) { - return -ENODEV; - } - return vfio_eeh_container_op(container, op); + vbasedev->bcontainer->ops->detach_device(vbasedev); } diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c new file mode 100644 index 0000000000..913ae49077 --- /dev/null +++ b/hw/vfio/container-base.c @@ -0,0 +1,111 @@ +/* + * VFIO BASE CONTAINER + * + * Copyright (C) 2023 Intel Corporation. + * Copyright Red Hat, Inc. 2023 + * + * Authors: Yi Liu + * Eric Auger + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "hw/vfio/vfio-container-base.h" + +int vfio_container_dma_map(VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + void *vaddr, bool readonly) +{ + g_assert(bcontainer->ops->dma_map); + return bcontainer->ops->dma_map(bcontainer, iova, size, vaddr, readonly); +} + +int vfio_container_dma_unmap(VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + IOMMUTLBEntry *iotlb) +{ + g_assert(bcontainer->ops->dma_unmap); + return bcontainer->ops->dma_unmap(bcontainer, iova, size, iotlb); +} + +int vfio_container_add_section_window(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, + Error **errp) +{ + if (!bcontainer->ops->add_window) { + return 0; + } + + return bcontainer->ops->add_window(bcontainer, section, errp); +} + +void vfio_container_del_section_window(VFIOContainerBase *bcontainer, + MemoryRegionSection *section) +{ + if (!bcontainer->ops->del_window) { + return; + } + + return bcontainer->ops->del_window(bcontainer, section); +} + +int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, + bool start) +{ + if (!bcontainer->dirty_pages_supported) { + return 0; + } + + g_assert(bcontainer->ops->set_dirty_page_tracking); + return bcontainer->ops->set_dirty_page_tracking(bcontainer, start); +} + +int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, + hwaddr iova, hwaddr size) +{ + g_assert(bcontainer->ops->query_dirty_bitmap); + return bcontainer->ops->query_dirty_bitmap(bcontainer, vbmap, iova, size); +} + +void vfio_container_init(VFIOContainerBase *bcontainer, VFIOAddressSpace *space, + const VFIOIOMMUClass *ops) +{ + bcontainer->ops = ops; + bcontainer->space = space; + bcontainer->error = NULL; + bcontainer->dirty_pages_supported = false; + bcontainer->dma_max_mappings = 0; + bcontainer->iova_ranges = NULL; + QLIST_INIT(&bcontainer->giommu_list); + QLIST_INIT(&bcontainer->vrdl_list); +} + +void vfio_container_destroy(VFIOContainerBase *bcontainer) +{ + VFIOGuestIOMMU *giommu, *tmp; + + QLIST_REMOVE(bcontainer, next); + + QLIST_FOREACH_SAFE(giommu, &bcontainer->giommu_list, giommu_next, tmp) { + memory_region_unregister_iommu_notifier( + MEMORY_REGION(giommu->iommu_mr), &giommu->n); + QLIST_REMOVE(giommu, giommu_next); + g_free(giommu); + } + + g_list_free_full(bcontainer->iova_ranges, g_free); +} + +static const TypeInfo types[] = { + { + .name = TYPE_VFIO_IOMMU, + .parent = TYPE_INTERFACE, + .class_size = sizeof(VFIOIOMMUClass), + }, +}; + +DEFINE_TYPES(types) diff --git a/hw/vfio/container.c b/hw/vfio/container.c new file mode 100644 index 0000000000..77bdec276e --- /dev/null +++ b/hw/vfio/container.c @@ -0,0 +1,1154 @@ +/* + * generic functions used by VFIO devices + * + * Copyright Red Hat, Inc. 2012 + * + * 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. + * + * Based on qemu-kvm device-assignment: + * Adapted for KVM by Qumranet. + * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com) + * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com) + * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com) + * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com) + * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) + */ + +#include "qemu/osdep.h" +#include +#include + +#include "hw/vfio/vfio-common.h" +#include "exec/address-spaces.h" +#include "exec/memory.h" +#include "exec/ram_addr.h" +#include "hw/hw.h" +#include "qemu/error-report.h" +#include "qemu/range.h" +#include "sysemu/reset.h" +#include "trace.h" +#include "qapi/error.h" +#include "pci.h" + +VFIOGroupList vfio_group_list = + QLIST_HEAD_INITIALIZER(vfio_group_list); + +static int vfio_ram_block_discard_disable(VFIOContainer *container, bool state) +{ + switch (container->iommu_type) { + case VFIO_TYPE1v2_IOMMU: + case VFIO_TYPE1_IOMMU: + /* + * We support coordinated discarding of RAM via the RamDiscardManager. + */ + return ram_block_uncoordinated_discard_disable(state); + default: + /* + * VFIO_SPAPR_TCE_IOMMU most probably works just fine with + * RamDiscardManager, however, it is completely untested. + * + * VFIO_SPAPR_TCE_v2_IOMMU with "DMA memory preregistering" does + * completely the opposite of managing mapping/pinning dynamically as + * required by RamDiscardManager. We would have to special-case sections + * with a RamDiscardManager. + */ + return ram_block_discard_disable(state); + } +} + +static int vfio_dma_unmap_bitmap(const VFIOContainer *container, + hwaddr iova, ram_addr_t size, + IOMMUTLBEntry *iotlb) +{ + const VFIOContainerBase *bcontainer = &container->bcontainer; + struct vfio_iommu_type1_dma_unmap *unmap; + struct vfio_bitmap *bitmap; + VFIOBitmap vbmap; + int ret; + + ret = vfio_bitmap_alloc(&vbmap, size); + if (ret) { + return ret; + } + + unmap = g_malloc0(sizeof(*unmap) + sizeof(*bitmap)); + + unmap->argsz = sizeof(*unmap) + sizeof(*bitmap); + unmap->iova = iova; + unmap->size = size; + unmap->flags |= VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP; + bitmap = (struct vfio_bitmap *)&unmap->data; + + /* + * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of + * qemu_real_host_page_size to mark those dirty. Hence set bitmap_pgsize + * to qemu_real_host_page_size. + */ + bitmap->pgsize = qemu_real_host_page_size(); + bitmap->size = vbmap.size; + bitmap->data = (__u64 *)vbmap.bitmap; + + if (vbmap.size > bcontainer->max_dirty_bitmap_size) { + error_report("UNMAP: Size of bitmap too big 0x%"PRIx64, vbmap.size); + ret = -E2BIG; + goto unmap_exit; + } + + ret = ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, unmap); + if (!ret) { + cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, + iotlb->translated_addr, vbmap.pages); + } else { + error_report("VFIO_UNMAP_DMA with DIRTY_BITMAP : %m"); + } + +unmap_exit: + g_free(unmap); + g_free(vbmap.bitmap); + + return ret; +} + +/* + * DMA - Mapping and unmapping for the "type1" IOMMU interface used on x86 + */ +static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + IOMMUTLBEntry *iotlb) +{ + const VFIOContainer *container = container_of(bcontainer, VFIOContainer, + bcontainer); + struct vfio_iommu_type1_dma_unmap unmap = { + .argsz = sizeof(unmap), + .flags = 0, + .iova = iova, + .size = size, + }; + bool need_dirty_sync = false; + int ret; + + if (iotlb && vfio_devices_all_running_and_mig_active(bcontainer)) { + if (!vfio_devices_all_device_dirty_tracking(bcontainer) && + bcontainer->dirty_pages_supported) { + return vfio_dma_unmap_bitmap(container, iova, size, iotlb); + } + + need_dirty_sync = true; + } + + while (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) { + /* + * The type1 backend has an off-by-one bug in the kernel (71a7d3d78e3c + * v4.15) where an overflow in its wrap-around check prevents us from + * unmapping the last page of the address space. Test for the error + * condition and re-try the unmap excluding the last page. The + * expectation is that we've never mapped the last page anyway and this + * unmap request comes via vIOMMU support which also makes it unlikely + * that this page is used. This bug was introduced well after type1 v2 + * support was introduced, so we shouldn't need to test for v1. A fix + * is queued for kernel v5.0 so this workaround can be removed once + * affected kernels are sufficiently deprecated. + */ + if (errno == EINVAL && unmap.size && !(unmap.iova + unmap.size) && + container->iommu_type == VFIO_TYPE1v2_IOMMU) { + trace_vfio_legacy_dma_unmap_overflow_workaround(); + unmap.size -= 1ULL << ctz64(bcontainer->pgsizes); + continue; + } + error_report("VFIO_UNMAP_DMA failed: %s", strerror(errno)); + return -errno; + } + + if (need_dirty_sync) { + ret = vfio_get_dirty_bitmap(bcontainer, iova, size, + iotlb->translated_addr); + if (ret) { + return ret; + } + } + + return 0; +} + +static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, + ram_addr_t size, void *vaddr, bool readonly) +{ + const VFIOContainer *container = container_of(bcontainer, VFIOContainer, + bcontainer); + struct vfio_iommu_type1_dma_map map = { + .argsz = sizeof(map), + .flags = VFIO_DMA_MAP_FLAG_READ, + .vaddr = (__u64)(uintptr_t)vaddr, + .iova = iova, + .size = size, + }; + + if (!readonly) { + map.flags |= VFIO_DMA_MAP_FLAG_WRITE; + } + + /* + * Try the mapping, if it fails with EBUSY, unmap the region and try + * again. This shouldn't be necessary, but we sometimes see it in + * the VGA ROM space. + */ + if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0 || + (errno == EBUSY && + vfio_legacy_dma_unmap(bcontainer, iova, size, NULL) == 0 && + ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0)) { + return 0; + } + + error_report("VFIO_MAP_DMA failed: %s", strerror(errno)); + return -errno; +} + +static int +vfio_legacy_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, + bool start) +{ + const VFIOContainer *container = container_of(bcontainer, VFIOContainer, + bcontainer); + int ret; + struct vfio_iommu_type1_dirty_bitmap dirty = { + .argsz = sizeof(dirty), + }; + + if (start) { + dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_START; + } else { + dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP; + } + + ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, &dirty); + if (ret) { + ret = -errno; + error_report("Failed to set dirty tracking flag 0x%x errno: %d", + dirty.flags, errno); + } + + return ret; +} + +static int vfio_legacy_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, + hwaddr iova, hwaddr size) +{ + const VFIOContainer *container = container_of(bcontainer, VFIOContainer, + bcontainer); + struct vfio_iommu_type1_dirty_bitmap *dbitmap; + struct vfio_iommu_type1_dirty_bitmap_get *range; + int ret; + + dbitmap = g_malloc0(sizeof(*dbitmap) + sizeof(*range)); + + dbitmap->argsz = sizeof(*dbitmap) + sizeof(*range); + dbitmap->flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP; + range = (struct vfio_iommu_type1_dirty_bitmap_get *)&dbitmap->data; + range->iova = iova; + range->size = size; + + /* + * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of + * qemu_real_host_page_size to mark those dirty. Hence set bitmap's pgsize + * to qemu_real_host_page_size. + */ + range->bitmap.pgsize = qemu_real_host_page_size(); + range->bitmap.size = vbmap->size; + range->bitmap.data = (__u64 *)vbmap->bitmap; + + ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, dbitmap); + if (ret) { + ret = -errno; + error_report("Failed to get dirty bitmap for iova: 0x%"PRIx64 + " size: 0x%"PRIx64" err: %d", (uint64_t)range->iova, + (uint64_t)range->size, errno); + } + + g_free(dbitmap); + + return ret; +} + +static struct vfio_info_cap_header * +vfio_get_iommu_type1_info_cap(struct vfio_iommu_type1_info *info, uint16_t id) +{ + if (!(info->flags & VFIO_IOMMU_INFO_CAPS)) { + return NULL; + } + + return vfio_get_cap((void *)info, info->cap_offset, id); +} + +bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, + unsigned int *avail) +{ + struct vfio_info_cap_header *hdr; + struct vfio_iommu_type1_info_dma_avail *cap; + + /* If the capability cannot be found, assume no DMA limiting */ + hdr = vfio_get_iommu_type1_info_cap(info, + VFIO_IOMMU_TYPE1_INFO_DMA_AVAIL); + if (!hdr) { + return false; + } + + if (avail != NULL) { + cap = (void *) hdr; + *avail = cap->avail; + } + + return true; +} + +static bool vfio_get_info_iova_range(struct vfio_iommu_type1_info *info, + VFIOContainerBase *bcontainer) +{ + struct vfio_info_cap_header *hdr; + struct vfio_iommu_type1_info_cap_iova_range *cap; + + hdr = vfio_get_iommu_type1_info_cap(info, + VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE); + if (!hdr) { + return false; + } + + cap = (void *)hdr; + + for (int i = 0; i < cap->nr_iovas; i++) { + Range *range = g_new(Range, 1); + + range_set_bounds(range, cap->iova_ranges[i].start, + cap->iova_ranges[i].end); + bcontainer->iova_ranges = + range_list_insert(bcontainer->iova_ranges, range); + } + + return true; +} + +static void vfio_kvm_device_add_group(VFIOGroup *group) +{ + Error *err = NULL; + + if (vfio_kvm_device_add_fd(group->fd, &err)) { + error_reportf_err(err, "group ID %d: ", group->groupid); + } +} + +static void vfio_kvm_device_del_group(VFIOGroup *group) +{ + Error *err = NULL; + + if (vfio_kvm_device_del_fd(group->fd, &err)) { + error_reportf_err(err, "group ID %d: ", group->groupid); + } +} + +/* + * vfio_get_iommu_type - selects the richest iommu_type (v2 first) + */ +static int vfio_get_iommu_type(VFIOContainer *container, + Error **errp) +{ + int iommu_types[] = { VFIO_TYPE1v2_IOMMU, VFIO_TYPE1_IOMMU, + VFIO_SPAPR_TCE_v2_IOMMU, VFIO_SPAPR_TCE_IOMMU }; + int i; + + for (i = 0; i < ARRAY_SIZE(iommu_types); i++) { + if (ioctl(container->fd, VFIO_CHECK_EXTENSION, iommu_types[i])) { + return iommu_types[i]; + } + } + error_setg(errp, "No available IOMMU models"); + return -EINVAL; +} + +/* + * vfio_get_iommu_ops - get a VFIOIOMMUClass associated with a type + */ +static const VFIOIOMMUClass *vfio_get_iommu_class(int iommu_type, Error **errp) +{ + ObjectClass *klass = NULL; + + switch (iommu_type) { + case VFIO_TYPE1v2_IOMMU: + case VFIO_TYPE1_IOMMU: + klass = object_class_by_name(TYPE_VFIO_IOMMU_LEGACY); + break; + case VFIO_SPAPR_TCE_v2_IOMMU: + case VFIO_SPAPR_TCE_IOMMU: + klass = object_class_by_name(TYPE_VFIO_IOMMU_SPAPR); + break; + default: + g_assert_not_reached(); + }; + + return VFIO_IOMMU_CLASS(klass); +} + +static int vfio_set_iommu(VFIOContainer *container, int group_fd, + VFIOAddressSpace *space, Error **errp) +{ + int iommu_type, ret; + const VFIOIOMMUClass *vioc; + + iommu_type = vfio_get_iommu_type(container, errp); + if (iommu_type < 0) { + return iommu_type; + } + + ret = ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container->fd); + if (ret) { + error_setg_errno(errp, errno, "Failed to set group container"); + return -errno; + } + + while (ioctl(container->fd, VFIO_SET_IOMMU, iommu_type)) { + if (iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { + /* + * On sPAPR, despite the IOMMU subdriver always advertises v1 and + * v2, the running platform may not support v2 and there is no + * way to guess it until an IOMMU group gets added to the container. + * So in case it fails with v2, try v1 as a fallback. + */ + iommu_type = VFIO_SPAPR_TCE_IOMMU; + continue; + } + error_setg_errno(errp, errno, "Failed to set iommu for container"); + return -errno; + } + + container->iommu_type = iommu_type; + + vioc = vfio_get_iommu_class(iommu_type, errp); + if (!vioc) { + error_setg(errp, "No available IOMMU models"); + return -EINVAL; + } + + vfio_container_init(&container->bcontainer, space, vioc); + return 0; +} + +static int vfio_get_iommu_info(VFIOContainer *container, + struct vfio_iommu_type1_info **info) +{ + + size_t argsz = sizeof(struct vfio_iommu_type1_info); + + *info = g_new0(struct vfio_iommu_type1_info, 1); +again: + (*info)->argsz = argsz; + + if (ioctl(container->fd, VFIO_IOMMU_GET_INFO, *info)) { + g_free(*info); + *info = NULL; + return -errno; + } + + if (((*info)->argsz > argsz)) { + argsz = (*info)->argsz; + *info = g_realloc(*info, argsz); + goto again; + } + + return 0; +} + +static struct vfio_info_cap_header * +vfio_get_iommu_info_cap(struct vfio_iommu_type1_info *info, uint16_t id) +{ + struct vfio_info_cap_header *hdr; + void *ptr = info; + + if (!(info->flags & VFIO_IOMMU_INFO_CAPS)) { + return NULL; + } + + for (hdr = ptr + info->cap_offset; hdr != ptr; hdr = ptr + hdr->next) { + if (hdr->id == id) { + return hdr; + } + } + + return NULL; +} + +static void vfio_get_iommu_info_migration(VFIOContainer *container, + struct vfio_iommu_type1_info *info) +{ + struct vfio_info_cap_header *hdr; + struct vfio_iommu_type1_info_cap_migration *cap_mig; + VFIOContainerBase *bcontainer = &container->bcontainer; + + hdr = vfio_get_iommu_info_cap(info, VFIO_IOMMU_TYPE1_INFO_CAP_MIGRATION); + if (!hdr) { + return; + } + + cap_mig = container_of(hdr, struct vfio_iommu_type1_info_cap_migration, + header); + + /* + * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of + * qemu_real_host_page_size to mark those dirty. + */ + if (cap_mig->pgsize_bitmap & qemu_real_host_page_size()) { + bcontainer->dirty_pages_supported = true; + bcontainer->max_dirty_bitmap_size = cap_mig->max_dirty_bitmap_size; + bcontainer->dirty_pgsizes = cap_mig->pgsize_bitmap; + } +} + +static int vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp) +{ + VFIOContainer *container = container_of(bcontainer, VFIOContainer, + bcontainer); + g_autofree struct vfio_iommu_type1_info *info = NULL; + int ret; + + ret = vfio_get_iommu_info(container, &info); + if (ret) { + error_setg_errno(errp, -ret, "Failed to get VFIO IOMMU info"); + return ret; + } + + if (info->flags & VFIO_IOMMU_INFO_PGSIZES) { + bcontainer->pgsizes = info->iova_pgsizes; + } else { + bcontainer->pgsizes = qemu_real_host_page_size(); + } + + if (!vfio_get_info_dma_avail(info, &bcontainer->dma_max_mappings)) { + bcontainer->dma_max_mappings = 65535; + } + + vfio_get_info_iova_range(info, bcontainer); + + vfio_get_iommu_info_migration(container, info); + return 0; +} + +static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, + Error **errp) +{ + VFIOContainer *container; + VFIOContainerBase *bcontainer; + int ret, fd; + VFIOAddressSpace *space; + + space = vfio_get_address_space(as); + + /* + * VFIO is currently incompatible with discarding of RAM insofar as the + * madvise to purge (zap) the page from QEMU's address space does not + * interact with the memory API and therefore leaves stale virtual to + * physical mappings in the IOMMU if the page was previously pinned. We + * therefore set discarding broken for each group added to a container, + * whether the container is used individually or shared. This provides + * us with options to allow devices within a group to opt-in and allow + * discarding, so long as it is done consistently for a group (for instance + * if the device is an mdev device where it is known that the host vendor + * driver will never pin pages outside of the working set of the guest + * driver, which would thus not be discarding candidates). + * + * The first opportunity to induce pinning occurs here where we attempt to + * attach the group to existing containers within the AddressSpace. If any + * pages are already zapped from the virtual address space, such as from + * previous discards, new pinning will cause valid mappings to be + * re-established. Likewise, when the overall MemoryListener for a new + * container is registered, a replay of mappings within the AddressSpace + * will occur, re-establishing any previously zapped pages as well. + * + * Especially virtio-balloon is currently only prevented from discarding + * new memory, it will not yet set ram_block_discard_set_required() and + * therefore, neither stops us here or deals with the sudden memory + * consumption of inflated memory. + * + * We do support discarding of memory coordinated via the RamDiscardManager + * with some IOMMU types. vfio_ram_block_discard_disable() handles the + * details once we know which type of IOMMU we are using. + */ + + QLIST_FOREACH(bcontainer, &space->containers, next) { + container = container_of(bcontainer, VFIOContainer, bcontainer); + if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { + ret = vfio_ram_block_discard_disable(container, true); + if (ret) { + error_setg_errno(errp, -ret, + "Cannot set discarding of RAM broken"); + if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER, + &container->fd)) { + error_report("vfio: error disconnecting group %d from" + " container", group->groupid); + } + return ret; + } + group->container = container; + QLIST_INSERT_HEAD(&container->group_list, group, container_next); + vfio_kvm_device_add_group(group); + return 0; + } + } + + fd = qemu_open_old("/dev/vfio/vfio", O_RDWR); + if (fd < 0) { + error_setg_errno(errp, errno, "failed to open /dev/vfio/vfio"); + ret = -errno; + goto put_space_exit; + } + + ret = ioctl(fd, VFIO_GET_API_VERSION); + if (ret != VFIO_API_VERSION) { + error_setg(errp, "supported vfio version: %d, " + "reported version: %d", VFIO_API_VERSION, ret); + ret = -EINVAL; + goto close_fd_exit; + } + + container = g_malloc0(sizeof(*container)); + container->fd = fd; + bcontainer = &container->bcontainer; + + ret = vfio_set_iommu(container, group->fd, space, errp); + if (ret) { + goto free_container_exit; + } + + ret = vfio_cpr_register_container(bcontainer, errp); + if (ret) { + goto free_container_exit; + } + + ret = vfio_ram_block_discard_disable(container, true); + if (ret) { + error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken"); + goto unregister_container_exit; + } + + assert(bcontainer->ops->setup); + + ret = bcontainer->ops->setup(bcontainer, errp); + if (ret) { + goto enable_discards_exit; + } + + vfio_kvm_device_add_group(group); + + QLIST_INIT(&container->group_list); + QLIST_INSERT_HEAD(&space->containers, bcontainer, next); + + group->container = container; + QLIST_INSERT_HEAD(&container->group_list, group, container_next); + + bcontainer->listener = vfio_memory_listener; + memory_listener_register(&bcontainer->listener, bcontainer->space->as); + + if (bcontainer->error) { + ret = -1; + error_propagate_prepend(errp, bcontainer->error, + "memory listener initialization failed: "); + goto listener_release_exit; + } + + bcontainer->initialized = true; + + return 0; +listener_release_exit: + QLIST_REMOVE(group, container_next); + QLIST_REMOVE(bcontainer, next); + vfio_kvm_device_del_group(group); + memory_listener_unregister(&bcontainer->listener); + if (bcontainer->ops->release) { + bcontainer->ops->release(bcontainer); + } + +enable_discards_exit: + vfio_ram_block_discard_disable(container, false); + +unregister_container_exit: + vfio_cpr_unregister_container(bcontainer); + +free_container_exit: + g_free(container); + +close_fd_exit: + close(fd); + +put_space_exit: + vfio_put_address_space(space); + + return ret; +} + +static void vfio_disconnect_container(VFIOGroup *group) +{ + VFIOContainer *container = group->container; + VFIOContainerBase *bcontainer = &container->bcontainer; + + QLIST_REMOVE(group, container_next); + group->container = NULL; + + /* + * Explicitly release the listener first before unset container, + * since unset may destroy the backend container if it's the last + * group. + */ + if (QLIST_EMPTY(&container->group_list)) { + memory_listener_unregister(&bcontainer->listener); + if (bcontainer->ops->release) { + bcontainer->ops->release(bcontainer); + } + } + + if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER, &container->fd)) { + error_report("vfio: error disconnecting group %d from container", + group->groupid); + } + + if (QLIST_EMPTY(&container->group_list)) { + VFIOAddressSpace *space = bcontainer->space; + + vfio_container_destroy(bcontainer); + + trace_vfio_disconnect_container(container->fd); + vfio_cpr_unregister_container(bcontainer); + close(container->fd); + g_free(container); + + vfio_put_address_space(space); + } +} + +static VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp) +{ + ERRP_GUARD(); + VFIOGroup *group; + char path[32]; + struct vfio_group_status status = { .argsz = sizeof(status) }; + + QLIST_FOREACH(group, &vfio_group_list, next) { + if (group->groupid == groupid) { + /* Found it. Now is it already in the right context? */ + if (group->container->bcontainer.space->as == as) { + return group; + } else { + error_setg(errp, "group %d used in multiple address spaces", + group->groupid); + return NULL; + } + } + } + + group = g_malloc0(sizeof(*group)); + + snprintf(path, sizeof(path), "/dev/vfio/%d", groupid); + group->fd = qemu_open_old(path, O_RDWR); + if (group->fd < 0) { + error_setg_errno(errp, errno, "failed to open %s", path); + goto free_group_exit; + } + + if (ioctl(group->fd, VFIO_GROUP_GET_STATUS, &status)) { + error_setg_errno(errp, errno, "failed to get group %d status", groupid); + goto close_fd_exit; + } + + if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) { + error_setg(errp, "group %d is not viable", groupid); + error_append_hint(errp, + "Please ensure all devices within the iommu_group " + "are bound to their vfio bus driver.\n"); + goto close_fd_exit; + } + + group->groupid = groupid; + QLIST_INIT(&group->device_list); + + if (vfio_connect_container(group, as, errp)) { + error_prepend(errp, "failed to setup container for group %d: ", + groupid); + goto close_fd_exit; + } + + QLIST_INSERT_HEAD(&vfio_group_list, group, next); + + return group; + +close_fd_exit: + close(group->fd); + +free_group_exit: + g_free(group); + + return NULL; +} + +static void vfio_put_group(VFIOGroup *group) +{ + if (!group || !QLIST_EMPTY(&group->device_list)) { + return; + } + + if (!group->ram_block_discard_allowed) { + vfio_ram_block_discard_disable(group->container, false); + } + vfio_kvm_device_del_group(group); + vfio_disconnect_container(group); + QLIST_REMOVE(group, next); + trace_vfio_put_group(group->fd); + close(group->fd); + g_free(group); +} + +static int vfio_get_device(VFIOGroup *group, const char *name, + VFIODevice *vbasedev, Error **errp) +{ + g_autofree struct vfio_device_info *info = NULL; + int fd; + + fd = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name); + if (fd < 0) { + error_setg_errno(errp, errno, "error getting device from group %d", + group->groupid); + error_append_hint(errp, + "Verify all devices in group %d are bound to vfio- " + "or pci-stub and not already in use\n", group->groupid); + return fd; + } + + info = vfio_get_device_info(fd); + if (!info) { + error_setg_errno(errp, errno, "error getting device info"); + close(fd); + return -1; + } + + /* + * Set discarding of RAM as not broken for this group if the driver knows + * the device operates compatibly with discarding. Setting must be + * consistent per group, but since compatibility is really only possible + * with mdev currently, we expect singleton groups. + */ + if (vbasedev->ram_block_discard_allowed != + group->ram_block_discard_allowed) { + if (!QLIST_EMPTY(&group->device_list)) { + error_setg(errp, "Inconsistent setting of support for discarding " + "RAM (e.g., balloon) within group"); + close(fd); + return -1; + } + + if (!group->ram_block_discard_allowed) { + group->ram_block_discard_allowed = true; + vfio_ram_block_discard_disable(group->container, false); + } + } + + vbasedev->fd = fd; + vbasedev->group = group; + QLIST_INSERT_HEAD(&group->device_list, vbasedev, next); + + vbasedev->num_irqs = info->num_irqs; + vbasedev->num_regions = info->num_regions; + vbasedev->flags = info->flags; + + trace_vfio_get_device(name, info->flags, info->num_regions, info->num_irqs); + + vbasedev->reset_works = !!(info->flags & VFIO_DEVICE_FLAGS_RESET); + + return 0; +} + +static void vfio_put_base_device(VFIODevice *vbasedev) +{ + if (!vbasedev->group) { + return; + } + QLIST_REMOVE(vbasedev, next); + vbasedev->group = NULL; + trace_vfio_put_base_device(vbasedev->fd); + close(vbasedev->fd); +} + +static int vfio_device_groupid(VFIODevice *vbasedev, Error **errp) +{ + char *tmp, group_path[PATH_MAX]; + g_autofree char *group_name = NULL; + int ret, groupid; + ssize_t len; + + tmp = g_strdup_printf("%s/iommu_group", vbasedev->sysfsdev); + len = readlink(tmp, group_path, sizeof(group_path)); + g_free(tmp); + + if (len <= 0 || len >= sizeof(group_path)) { + ret = len < 0 ? -errno : -ENAMETOOLONG; + error_setg_errno(errp, -ret, "no iommu_group found"); + return ret; + } + + group_path[len] = 0; + + group_name = g_path_get_basename(group_path); + if (sscanf(group_name, "%d", &groupid) != 1) { + error_setg_errno(errp, errno, "failed to read %s", group_path); + return -errno; + } + return groupid; +} + +/* + * vfio_attach_device: attach a device to a security context + * @name and @vbasedev->name are likely to be different depending + * on the type of the device, hence the need for passing @name + */ +static int vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp) +{ + int groupid = vfio_device_groupid(vbasedev, errp); + VFIODevice *vbasedev_iter; + VFIOGroup *group; + VFIOContainerBase *bcontainer; + int ret; + + if (groupid < 0) { + return groupid; + } + + trace_vfio_attach_device(vbasedev->name, groupid); + + group = vfio_get_group(groupid, as, errp); + if (!group) { + return -ENOENT; + } + + QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { + if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) { + error_setg(errp, "device is already attached"); + vfio_put_group(group); + return -EBUSY; + } + } + ret = vfio_get_device(group, name, vbasedev, errp); + if (ret) { + vfio_put_group(group); + return ret; + } + + bcontainer = &group->container->bcontainer; + vbasedev->bcontainer = bcontainer; + QLIST_INSERT_HEAD(&bcontainer->device_list, vbasedev, container_next); + QLIST_INSERT_HEAD(&vfio_device_list, vbasedev, global_next); + + return ret; +} + +static void vfio_legacy_detach_device(VFIODevice *vbasedev) +{ + VFIOGroup *group = vbasedev->group; + + QLIST_REMOVE(vbasedev, global_next); + QLIST_REMOVE(vbasedev, container_next); + vbasedev->bcontainer = NULL; + trace_vfio_detach_device(vbasedev->name, group->groupid); + vfio_put_base_device(vbasedev); + vfio_put_group(group); +} + +static int vfio_legacy_pci_hot_reset(VFIODevice *vbasedev, bool single) +{ + VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); + VFIOGroup *group; + struct vfio_pci_hot_reset_info *info = NULL; + struct vfio_pci_dependent_device *devices; + struct vfio_pci_hot_reset *reset; + int32_t *fds; + int ret, i, count; + bool multi = false; + + trace_vfio_pci_hot_reset(vdev->vbasedev.name, single ? "one" : "multi"); + + if (!single) { + vfio_pci_pre_reset(vdev); + } + vdev->vbasedev.needs_reset = false; + + ret = vfio_pci_get_pci_hot_reset_info(vdev, &info); + + if (ret) { + goto out_single; + } + devices = &info->devices[0]; + + trace_vfio_pci_hot_reset_has_dep_devices(vdev->vbasedev.name); + + /* Verify that we have all the groups required */ + for (i = 0; i < info->count; i++) { + PCIHostDeviceAddress host; + VFIOPCIDevice *tmp; + VFIODevice *vbasedev_iter; + + host.domain = devices[i].segment; + host.bus = devices[i].bus; + host.slot = PCI_SLOT(devices[i].devfn); + host.function = PCI_FUNC(devices[i].devfn); + + trace_vfio_pci_hot_reset_dep_devices(host.domain, + host.bus, host.slot, host.function, devices[i].group_id); + + if (vfio_pci_host_match(&host, vdev->vbasedev.name)) { + continue; + } + + QLIST_FOREACH(group, &vfio_group_list, next) { + if (group->groupid == devices[i].group_id) { + break; + } + } + + if (!group) { + if (!vdev->has_pm_reset) { + error_report("vfio: Cannot reset device %s, " + "depends on group %d which is not owned.", + vdev->vbasedev.name, devices[i].group_id); + } + ret = -EPERM; + goto out; + } + + /* Prep dependent devices for reset and clear our marker. */ + QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { + if (!vbasedev_iter->dev->realized || + vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { + continue; + } + tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); + if (vfio_pci_host_match(&host, tmp->vbasedev.name)) { + if (single) { + ret = -EINVAL; + goto out_single; + } + vfio_pci_pre_reset(tmp); + tmp->vbasedev.needs_reset = false; + multi = true; + break; + } + } + } + + if (!single && !multi) { + ret = -EINVAL; + goto out_single; + } + + /* Determine how many group fds need to be passed */ + count = 0; + QLIST_FOREACH(group, &vfio_group_list, next) { + for (i = 0; i < info->count; i++) { + if (group->groupid == devices[i].group_id) { + count++; + break; + } + } + } + + reset = g_malloc0(sizeof(*reset) + (count * sizeof(*fds))); + reset->argsz = sizeof(*reset) + (count * sizeof(*fds)); + fds = &reset->group_fds[0]; + + /* Fill in group fds */ + QLIST_FOREACH(group, &vfio_group_list, next) { + for (i = 0; i < info->count; i++) { + if (group->groupid == devices[i].group_id) { + fds[reset->count++] = group->fd; + break; + } + } + } + + /* Bus reset! */ + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_PCI_HOT_RESET, reset); + g_free(reset); + if (ret) { + ret = -errno; + } + + trace_vfio_pci_hot_reset_result(vdev->vbasedev.name, + ret ? strerror(errno) : "Success"); + +out: + /* Re-enable INTx on affected devices */ + for (i = 0; i < info->count; i++) { + PCIHostDeviceAddress host; + VFIOPCIDevice *tmp; + VFIODevice *vbasedev_iter; + + host.domain = devices[i].segment; + host.bus = devices[i].bus; + host.slot = PCI_SLOT(devices[i].devfn); + host.function = PCI_FUNC(devices[i].devfn); + + if (vfio_pci_host_match(&host, vdev->vbasedev.name)) { + continue; + } + + QLIST_FOREACH(group, &vfio_group_list, next) { + if (group->groupid == devices[i].group_id) { + break; + } + } + + if (!group) { + break; + } + + QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { + if (!vbasedev_iter->dev->realized || + vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { + continue; + } + tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); + if (vfio_pci_host_match(&host, tmp->vbasedev.name)) { + vfio_pci_post_reset(tmp); + break; + } + } + } +out_single: + if (!single) { + vfio_pci_post_reset(vdev); + } + g_free(info); + + return ret; +} + +static void vfio_iommu_legacy_class_init(ObjectClass *klass, void *data) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); + + vioc->setup = vfio_legacy_setup; + vioc->dma_map = vfio_legacy_dma_map; + vioc->dma_unmap = vfio_legacy_dma_unmap; + vioc->attach_device = vfio_legacy_attach_device; + vioc->detach_device = vfio_legacy_detach_device; + vioc->set_dirty_page_tracking = vfio_legacy_set_dirty_page_tracking; + vioc->query_dirty_bitmap = vfio_legacy_query_dirty_bitmap; + vioc->pci_hot_reset = vfio_legacy_pci_hot_reset; +}; + +static const TypeInfo types[] = { + { + .name = TYPE_VFIO_IOMMU_LEGACY, + .parent = TYPE_VFIO_IOMMU, + .class_init = vfio_iommu_legacy_class_init, + }, +}; + +DEFINE_TYPES(types) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c new file mode 100644 index 0000000000..392c2dd95d --- /dev/null +++ b/hw/vfio/cpr.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021-2024 Oracle and/or its affiliates. + * + * 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/vfio/vfio-common.h" +#include "migration/misc.h" +#include "qapi/error.h" +#include "sysemu/runstate.h" + +static int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, Error **errp) +{ + if (e->type == MIG_EVENT_PRECOPY_SETUP && + !runstate_check(RUN_STATE_SUSPENDED) && !vm_get_suspended()) { + + error_setg(errp, + "VFIO device only supports cpr-reboot for runstate suspended"); + + return -1; + } + return 0; +} + +int vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp) +{ + migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier, + vfio_cpr_reboot_notifier, + MIG_MODE_CPR_REBOOT); + return 0; +} + +void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer) +{ + migration_remove_notifier(&bcontainer->cpr_reboot_notifier); +} diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 89bc90508f..1aa440c663 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -14,6 +14,7 @@ #include #include +#include "qemu/error-report.h" #include "hw/display/edid.h" #include "ui/console.h" #include "qapi/error.h" @@ -106,14 +107,14 @@ err: return; } -static int vfio_display_edid_ui_info(void *opaque, uint32_t idx, - QemuUIInfo *info) +static void vfio_display_edid_ui_info(void *opaque, uint32_t idx, + QemuUIInfo *info) { VFIOPCIDevice *vdev = opaque; VFIODisplay *dpy = vdev->dpy; if (!dpy->edid_regs) { - return 0; + return; } if (info->width && info->height) { @@ -121,8 +122,6 @@ static int vfio_display_edid_ui_info(void *opaque, uint32_t idx, } else { vfio_display_edid_update(vdev, false, 0, 0); } - - return 0; } static void vfio_display_edid_init(VFIOPCIDevice *vdev) @@ -244,6 +243,8 @@ static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, dmabuf->dmabuf_id = plane.dmabuf_id; dmabuf->buf.width = plane.width; dmabuf->buf.height = plane.height; + dmabuf->buf.backing_width = plane.width; + dmabuf->buf.backing_height = plane.height; dmabuf->buf.stride = plane.stride; dmabuf->buf.fourcc = plane.drm_format; dmabuf->buf.modifier = plane.drm_format_mod; @@ -543,3 +544,24 @@ void vfio_display_finalize(VFIOPCIDevice *vdev) vfio_display_edid_exit(vdev->dpy); g_free(vdev->dpy); } + +static bool migrate_needed(void *opaque) +{ + VFIODisplay *dpy = opaque; + bool ramfb_exists = dpy->ramfb != NULL; + + /* see vfio_display_migration_needed() */ + assert(ramfb_exists); + return ramfb_exists; +} + +const VMStateDescription vfio_display_vmstate = { + .name = "VFIODisplay", + .version_id = 1, + .minimum_version_id = 1, + .needed = migrate_needed, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT_POINTER(ramfb, VFIODisplay, ramfb_vmstate, RAMFBState), + VMSTATE_END_OF_LIST(), + } +}; diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c new file mode 100644 index 0000000000..47b4096c05 --- /dev/null +++ b/hw/vfio/helpers.c @@ -0,0 +1,668 @@ +/* + * low level and IOMMU backend agnostic helpers used by VFIO devices, + * related to regions, interrupts, capabilities + * + * Copyright Red Hat, Inc. 2012 + * + * 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. + * + * Based on qemu-kvm device-assignment: + * Adapted for KVM by Qumranet. + * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com) + * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com) + * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com) + * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com) + * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) + */ + +#include "qemu/osdep.h" +#include + +#include "hw/vfio/vfio-common.h" +#include "hw/hw.h" +#include "trace.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "monitor/monitor.h" + +/* + * Common VFIO interrupt disable + */ +void vfio_disable_irqindex(VFIODevice *vbasedev, int index) +{ + struct vfio_irq_set irq_set = { + .argsz = sizeof(irq_set), + .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER, + .index = index, + .start = 0, + .count = 0, + }; + + ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); +} + +void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index) +{ + struct vfio_irq_set irq_set = { + .argsz = sizeof(irq_set), + .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK, + .index = index, + .start = 0, + .count = 1, + }; + + ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); +} + +void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index) +{ + struct vfio_irq_set irq_set = { + .argsz = sizeof(irq_set), + .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_MASK, + .index = index, + .start = 0, + .count = 1, + }; + + ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); +} + +static inline const char *action_to_str(int action) +{ + switch (action) { + case VFIO_IRQ_SET_ACTION_MASK: + return "MASK"; + case VFIO_IRQ_SET_ACTION_UNMASK: + return "UNMASK"; + case VFIO_IRQ_SET_ACTION_TRIGGER: + return "TRIGGER"; + default: + return "UNKNOWN ACTION"; + } +} + +static const char *index_to_str(VFIODevice *vbasedev, int index) +{ + if (vbasedev->type != VFIO_DEVICE_TYPE_PCI) { + return NULL; + } + + switch (index) { + case VFIO_PCI_INTX_IRQ_INDEX: + return "INTX"; + case VFIO_PCI_MSI_IRQ_INDEX: + return "MSI"; + case VFIO_PCI_MSIX_IRQ_INDEX: + return "MSIX"; + case VFIO_PCI_ERR_IRQ_INDEX: + return "ERR"; + case VFIO_PCI_REQ_IRQ_INDEX: + return "REQ"; + default: + return NULL; + } +} + +int vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, + int action, int fd, Error **errp) +{ + ERRP_GUARD(); + struct vfio_irq_set *irq_set; + int argsz, ret = 0; + const char *name; + int32_t *pfd; + + argsz = sizeof(*irq_set) + sizeof(*pfd); + + irq_set = g_malloc0(argsz); + irq_set->argsz = argsz; + irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | action; + irq_set->index = index; + irq_set->start = subindex; + irq_set->count = 1; + pfd = (int32_t *)&irq_set->data; + *pfd = fd; + + if (ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) { + ret = -errno; + } + g_free(irq_set); + + if (!ret) { + return 0; + } + + error_setg_errno(errp, -ret, "VFIO_DEVICE_SET_IRQS failure"); + + name = index_to_str(vbasedev, index); + if (name) { + error_prepend(errp, "%s-%d: ", name, subindex); + } else { + error_prepend(errp, "index %d-%d: ", index, subindex); + } + error_prepend(errp, + "Failed to %s %s eventfd signaling for interrupt ", + fd < 0 ? "tear down" : "set up", action_to_str(action)); + return ret; +} + +/* + * IO Port/MMIO - Beware of the endians, VFIO is always little endian + */ +void vfio_region_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIORegion *region = opaque; + VFIODevice *vbasedev = region->vbasedev; + union { + uint8_t byte; + uint16_t word; + uint32_t dword; + uint64_t qword; + } buf; + + switch (size) { + case 1: + buf.byte = data; + break; + case 2: + buf.word = cpu_to_le16(data); + break; + case 4: + buf.dword = cpu_to_le32(data); + break; + case 8: + buf.qword = cpu_to_le64(data); + break; + default: + hw_error("vfio: unsupported write size, %u bytes", size); + break; + } + + if (pwrite(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) { + error_report("%s(%s:region%d+0x%"HWADDR_PRIx", 0x%"PRIx64 + ",%d) failed: %m", + __func__, vbasedev->name, region->nr, + addr, data, size); + } + + trace_vfio_region_write(vbasedev->name, region->nr, addr, data, size); + + /* + * A read or write to a BAR always signals an INTx EOI. This will + * do nothing if not pending (including not in INTx mode). We assume + * that a BAR access is in response to an interrupt and that BAR + * accesses will service the interrupt. Unfortunately, we don't know + * which access will service the interrupt, so we're potentially + * getting quite a few host interrupts per guest interrupt. + */ + vbasedev->ops->vfio_eoi(vbasedev); +} + +uint64_t vfio_region_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIORegion *region = opaque; + VFIODevice *vbasedev = region->vbasedev; + union { + uint8_t byte; + uint16_t word; + uint32_t dword; + uint64_t qword; + } buf; + uint64_t data = 0; + + if (pread(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) { + error_report("%s(%s:region%d+0x%"HWADDR_PRIx", %d) failed: %m", + __func__, vbasedev->name, region->nr, + addr, size); + return (uint64_t)-1; + } + switch (size) { + case 1: + data = buf.byte; + break; + case 2: + data = le16_to_cpu(buf.word); + break; + case 4: + data = le32_to_cpu(buf.dword); + break; + case 8: + data = le64_to_cpu(buf.qword); + break; + default: + hw_error("vfio: unsupported read size, %u bytes", size); + break; + } + + trace_vfio_region_read(vbasedev->name, region->nr, addr, size, data); + + /* Same as write above */ + vbasedev->ops->vfio_eoi(vbasedev); + + return data; +} + +const MemoryRegionOps vfio_region_ops = { + .read = vfio_region_read, + .write = vfio_region_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size) +{ + vbmap->pages = REAL_HOST_PAGE_ALIGN(size) / qemu_real_host_page_size(); + vbmap->size = ROUND_UP(vbmap->pages, sizeof(__u64) * BITS_PER_BYTE) / + BITS_PER_BYTE; + vbmap->bitmap = g_try_malloc0(vbmap->size); + if (!vbmap->bitmap) { + return -ENOMEM; + } + + return 0; +} + +struct vfio_info_cap_header * +vfio_get_cap(void *ptr, uint32_t cap_offset, uint16_t id) +{ + struct vfio_info_cap_header *hdr; + + for (hdr = ptr + cap_offset; hdr != ptr; hdr = ptr + hdr->next) { + if (hdr->id == id) { + return hdr; + } + } + + return NULL; +} + +struct vfio_info_cap_header * +vfio_get_region_info_cap(struct vfio_region_info *info, uint16_t id) +{ + if (!(info->flags & VFIO_REGION_INFO_FLAG_CAPS)) { + return NULL; + } + + return vfio_get_cap((void *)info, info->cap_offset, id); +} + +struct vfio_info_cap_header * +vfio_get_device_info_cap(struct vfio_device_info *info, uint16_t id) +{ + if (!(info->flags & VFIO_DEVICE_FLAGS_CAPS)) { + return NULL; + } + + return vfio_get_cap((void *)info, info->cap_offset, id); +} + +static int vfio_setup_region_sparse_mmaps(VFIORegion *region, + struct vfio_region_info *info) +{ + struct vfio_info_cap_header *hdr; + struct vfio_region_info_cap_sparse_mmap *sparse; + int i, j; + + hdr = vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP); + if (!hdr) { + return -ENODEV; + } + + sparse = container_of(hdr, struct vfio_region_info_cap_sparse_mmap, header); + + trace_vfio_region_sparse_mmap_header(region->vbasedev->name, + region->nr, sparse->nr_areas); + + region->mmaps = g_new0(VFIOMmap, sparse->nr_areas); + + for (i = 0, j = 0; i < sparse->nr_areas; i++) { + if (sparse->areas[i].size) { + trace_vfio_region_sparse_mmap_entry(i, sparse->areas[i].offset, + sparse->areas[i].offset + + sparse->areas[i].size - 1); + region->mmaps[j].offset = sparse->areas[i].offset; + region->mmaps[j].size = sparse->areas[i].size; + j++; + } + } + + region->nr_mmaps = j; + region->mmaps = g_realloc(region->mmaps, j * sizeof(VFIOMmap)); + + return 0; +} + +int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, + int index, const char *name) +{ + struct vfio_region_info *info; + int ret; + + ret = vfio_get_region_info(vbasedev, index, &info); + if (ret) { + return ret; + } + + region->vbasedev = vbasedev; + region->flags = info->flags; + region->size = info->size; + region->fd_offset = info->offset; + region->nr = index; + + if (region->size) { + region->mem = g_new0(MemoryRegion, 1); + memory_region_init_io(region->mem, obj, &vfio_region_ops, + region, name, region->size); + + if (!vbasedev->no_mmap && + region->flags & VFIO_REGION_INFO_FLAG_MMAP) { + + ret = vfio_setup_region_sparse_mmaps(region, info); + + if (ret) { + region->nr_mmaps = 1; + region->mmaps = g_new0(VFIOMmap, region->nr_mmaps); + region->mmaps[0].offset = 0; + region->mmaps[0].size = region->size; + } + } + } + + g_free(info); + + trace_vfio_region_setup(vbasedev->name, index, name, + region->flags, region->fd_offset, region->size); + return 0; +} + +static void vfio_subregion_unmap(VFIORegion *region, int index) +{ + trace_vfio_region_unmap(memory_region_name(®ion->mmaps[index].mem), + region->mmaps[index].offset, + region->mmaps[index].offset + + region->mmaps[index].size - 1); + memory_region_del_subregion(region->mem, ®ion->mmaps[index].mem); + munmap(region->mmaps[index].mmap, region->mmaps[index].size); + object_unparent(OBJECT(®ion->mmaps[index].mem)); + region->mmaps[index].mmap = NULL; +} + +int vfio_region_mmap(VFIORegion *region) +{ + int i, prot = 0; + char *name; + + if (!region->mem) { + return 0; + } + + prot |= region->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0; + prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0; + + for (i = 0; i < region->nr_mmaps; i++) { + region->mmaps[i].mmap = mmap(NULL, region->mmaps[i].size, prot, + MAP_SHARED, region->vbasedev->fd, + region->fd_offset + + region->mmaps[i].offset); + if (region->mmaps[i].mmap == MAP_FAILED) { + int ret = -errno; + + trace_vfio_region_mmap_fault(memory_region_name(region->mem), i, + region->fd_offset + + region->mmaps[i].offset, + region->fd_offset + + region->mmaps[i].offset + + region->mmaps[i].size - 1, ret); + + region->mmaps[i].mmap = NULL; + + for (i--; i >= 0; i--) { + vfio_subregion_unmap(region, i); + } + + return ret; + } + + name = g_strdup_printf("%s mmaps[%d]", + memory_region_name(region->mem), i); + memory_region_init_ram_device_ptr(®ion->mmaps[i].mem, + memory_region_owner(region->mem), + name, region->mmaps[i].size, + region->mmaps[i].mmap); + g_free(name); + memory_region_add_subregion(region->mem, region->mmaps[i].offset, + ®ion->mmaps[i].mem); + + trace_vfio_region_mmap(memory_region_name(®ion->mmaps[i].mem), + region->mmaps[i].offset, + region->mmaps[i].offset + + region->mmaps[i].size - 1); + } + + return 0; +} + +void vfio_region_unmap(VFIORegion *region) +{ + int i; + + if (!region->mem) { + return; + } + + for (i = 0; i < region->nr_mmaps; i++) { + if (region->mmaps[i].mmap) { + vfio_subregion_unmap(region, i); + } + } +} + +void vfio_region_exit(VFIORegion *region) +{ + int i; + + if (!region->mem) { + return; + } + + for (i = 0; i < region->nr_mmaps; i++) { + if (region->mmaps[i].mmap) { + memory_region_del_subregion(region->mem, ®ion->mmaps[i].mem); + } + } + + trace_vfio_region_exit(region->vbasedev->name, region->nr); +} + +void vfio_region_finalize(VFIORegion *region) +{ + int i; + + if (!region->mem) { + return; + } + + for (i = 0; i < region->nr_mmaps; i++) { + if (region->mmaps[i].mmap) { + munmap(region->mmaps[i].mmap, region->mmaps[i].size); + object_unparent(OBJECT(®ion->mmaps[i].mem)); + } + } + + object_unparent(OBJECT(region->mem)); + + g_free(region->mem); + g_free(region->mmaps); + + trace_vfio_region_finalize(region->vbasedev->name, region->nr); + + region->mem = NULL; + region->mmaps = NULL; + region->nr_mmaps = 0; + region->size = 0; + region->flags = 0; + region->nr = 0; +} + +void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled) +{ + int i; + + if (!region->mem) { + return; + } + + for (i = 0; i < region->nr_mmaps; i++) { + if (region->mmaps[i].mmap) { + memory_region_set_enabled(®ion->mmaps[i].mem, enabled); + } + } + + trace_vfio_region_mmaps_set_enabled(memory_region_name(region->mem), + enabled); +} + +int vfio_get_region_info(VFIODevice *vbasedev, int index, + struct vfio_region_info **info) +{ + size_t argsz = sizeof(struct vfio_region_info); + + *info = g_malloc0(argsz); + + (*info)->index = index; +retry: + (*info)->argsz = argsz; + + if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) { + g_free(*info); + *info = NULL; + return -errno; + } + + if ((*info)->argsz > argsz) { + argsz = (*info)->argsz; + *info = g_realloc(*info, argsz); + + goto retry; + } + + return 0; +} + +int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, + uint32_t subtype, struct vfio_region_info **info) +{ + int i; + + for (i = 0; i < vbasedev->num_regions; i++) { + struct vfio_info_cap_header *hdr; + struct vfio_region_info_cap_type *cap_type; + + if (vfio_get_region_info(vbasedev, i, info)) { + continue; + } + + hdr = vfio_get_region_info_cap(*info, VFIO_REGION_INFO_CAP_TYPE); + if (!hdr) { + g_free(*info); + continue; + } + + cap_type = container_of(hdr, struct vfio_region_info_cap_type, header); + + trace_vfio_get_dev_region(vbasedev->name, i, + cap_type->type, cap_type->subtype); + + if (cap_type->type == type && cap_type->subtype == subtype) { + return 0; + } + + g_free(*info); + } + + *info = NULL; + return -ENODEV; +} + +bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type) +{ + struct vfio_region_info *info = NULL; + bool ret = false; + + if (!vfio_get_region_info(vbasedev, region, &info)) { + if (vfio_get_region_info_cap(info, cap_type)) { + ret = true; + } + g_free(info); + } + + return ret; +} + +int vfio_device_get_name(VFIODevice *vbasedev, Error **errp) +{ + ERRP_GUARD(); + struct stat st; + + if (vbasedev->fd < 0) { + if (stat(vbasedev->sysfsdev, &st) < 0) { + error_setg_errno(errp, errno, "no such host device"); + error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->sysfsdev); + return -errno; + } + /* User may specify a name, e.g: VFIO platform device */ + if (!vbasedev->name) { + vbasedev->name = g_path_get_basename(vbasedev->sysfsdev); + } + } else { + if (!vbasedev->iommufd) { + error_setg(errp, "Use FD passing only with iommufd backend"); + return -EINVAL; + } + /* + * Give a name with fd so any function printing out vbasedev->name + * will not break. + */ + if (!vbasedev->name) { + vbasedev->name = g_strdup_printf("VFIO_FD%d", vbasedev->fd); + } + } + + return 0; +} + +void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp) +{ + ERRP_GUARD(); + int fd = monitor_fd_param(monitor_cur(), str, errp); + + if (fd < 0) { + error_prepend(errp, "Could not parse remote object fd %s:", str); + return; + } + vbasedev->fd = fd; +} + +void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops, + DeviceState *dev, bool ram_discard) +{ + vbasedev->type = type; + vbasedev->ops = ops; + vbasedev->dev = dev; + vbasedev->fd = -1; + + vbasedev->ram_block_discard_allowed = ram_discard; +} diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index afe3fe7efc..b31ee79c60 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/nvram/fw_cfg.h" diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c new file mode 100644 index 0000000000..8827ffe636 --- /dev/null +++ b/hw/vfio/iommufd.c @@ -0,0 +1,645 @@ +/* + * iommufd container backend + * + * Copyright (C) 2023 Intel Corporation. + * Copyright Red Hat, Inc. 2023 + * + * Authors: Yi Liu + * Eric Auger + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include +#include +#include + +#include "hw/vfio/vfio-common.h" +#include "qemu/error-report.h" +#include "trace.h" +#include "qapi/error.h" +#include "sysemu/iommufd.h" +#include "hw/qdev-core.h" +#include "sysemu/reset.h" +#include "qemu/cutils.h" +#include "qemu/chardev_open.h" +#include "pci.h" + +static int iommufd_cdev_map(const VFIOContainerBase *bcontainer, hwaddr iova, + ram_addr_t size, void *vaddr, bool readonly) +{ + const VFIOIOMMUFDContainer *container = + container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); + + return iommufd_backend_map_dma(container->be, + container->ioas_id, + iova, size, vaddr, readonly); +} + +static int iommufd_cdev_unmap(const VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + IOMMUTLBEntry *iotlb) +{ + const VFIOIOMMUFDContainer *container = + container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); + + /* TODO: Handle dma_unmap_bitmap with iotlb args (migration) */ + return iommufd_backend_unmap_dma(container->be, + container->ioas_id, iova, size); +} + +static int iommufd_cdev_kvm_device_add(VFIODevice *vbasedev, Error **errp) +{ + return vfio_kvm_device_add_fd(vbasedev->fd, errp); +} + +static void iommufd_cdev_kvm_device_del(VFIODevice *vbasedev) +{ + Error *err = NULL; + + if (vfio_kvm_device_del_fd(vbasedev->fd, &err)) { + error_report_err(err); + } +} + +static int iommufd_cdev_connect_and_bind(VFIODevice *vbasedev, Error **errp) +{ + IOMMUFDBackend *iommufd = vbasedev->iommufd; + struct vfio_device_bind_iommufd bind = { + .argsz = sizeof(bind), + .flags = 0, + }; + int ret; + + ret = iommufd_backend_connect(iommufd, errp); + if (ret) { + return ret; + } + + /* + * Add device to kvm-vfio to be prepared for the tracking + * in KVM. Especially for some emulated devices, it requires + * to have kvm information in the device open. + */ + ret = iommufd_cdev_kvm_device_add(vbasedev, errp); + if (ret) { + goto err_kvm_device_add; + } + + /* Bind device to iommufd */ + bind.iommufd = iommufd->fd; + ret = ioctl(vbasedev->fd, VFIO_DEVICE_BIND_IOMMUFD, &bind); + if (ret) { + error_setg_errno(errp, errno, "error bind device fd=%d to iommufd=%d", + vbasedev->fd, bind.iommufd); + goto err_bind; + } + + vbasedev->devid = bind.out_devid; + trace_iommufd_cdev_connect_and_bind(bind.iommufd, vbasedev->name, + vbasedev->fd, vbasedev->devid); + return ret; +err_bind: + iommufd_cdev_kvm_device_del(vbasedev); +err_kvm_device_add: + iommufd_backend_disconnect(iommufd); + return ret; +} + +static void iommufd_cdev_unbind_and_disconnect(VFIODevice *vbasedev) +{ + /* Unbind is automatically conducted when device fd is closed */ + iommufd_cdev_kvm_device_del(vbasedev); + iommufd_backend_disconnect(vbasedev->iommufd); +} + +static int iommufd_cdev_getfd(const char *sysfs_path, Error **errp) +{ + ERRP_GUARD(); + long int ret = -ENOTTY; + g_autofree char *path = NULL; + g_autofree char *vfio_dev_path = NULL; + g_autofree char *vfio_path = NULL; + DIR *dir = NULL; + struct dirent *dent; + g_autofree gchar *contents = NULL; + gsize length; + int major, minor; + dev_t vfio_devt; + + path = g_strdup_printf("%s/vfio-dev", sysfs_path); + dir = opendir(path); + if (!dir) { + error_setg_errno(errp, errno, "couldn't open directory %s", path); + goto out; + } + + while ((dent = readdir(dir))) { + if (!strncmp(dent->d_name, "vfio", 4)) { + vfio_dev_path = g_strdup_printf("%s/%s/dev", path, dent->d_name); + break; + } + } + + if (!vfio_dev_path) { + error_setg(errp, "failed to find vfio-dev/vfioX/dev"); + goto out_close_dir; + } + + if (!g_file_get_contents(vfio_dev_path, &contents, &length, NULL)) { + error_setg(errp, "failed to load \"%s\"", vfio_dev_path); + goto out_close_dir; + } + + if (sscanf(contents, "%d:%d", &major, &minor) != 2) { + error_setg(errp, "failed to get major:minor for \"%s\"", vfio_dev_path); + goto out_close_dir; + } + vfio_devt = makedev(major, minor); + + vfio_path = g_strdup_printf("/dev/vfio/devices/%s", dent->d_name); + ret = open_cdev(vfio_path, vfio_devt); + if (ret < 0) { + error_setg(errp, "Failed to open %s", vfio_path); + } + + trace_iommufd_cdev_getfd(vfio_path, ret); + +out_close_dir: + closedir(dir); +out: + if (*errp) { + error_prepend(errp, VFIO_MSG_PREFIX, path); + } + + return ret; +} + +static int iommufd_cdev_attach_ioas_hwpt(VFIODevice *vbasedev, uint32_t id, + Error **errp) +{ + int ret, iommufd = vbasedev->iommufd->fd; + struct vfio_device_attach_iommufd_pt attach_data = { + .argsz = sizeof(attach_data), + .flags = 0, + .pt_id = id, + }; + + /* Attach device to an IOAS or hwpt within iommufd */ + ret = ioctl(vbasedev->fd, VFIO_DEVICE_ATTACH_IOMMUFD_PT, &attach_data); + if (ret) { + error_setg_errno(errp, errno, + "[iommufd=%d] error attach %s (%d) to id=%d", + iommufd, vbasedev->name, vbasedev->fd, id); + } else { + trace_iommufd_cdev_attach_ioas_hwpt(iommufd, vbasedev->name, + vbasedev->fd, id); + } + return ret; +} + +static int iommufd_cdev_detach_ioas_hwpt(VFIODevice *vbasedev, Error **errp) +{ + int ret, iommufd = vbasedev->iommufd->fd; + struct vfio_device_detach_iommufd_pt detach_data = { + .argsz = sizeof(detach_data), + .flags = 0, + }; + + ret = ioctl(vbasedev->fd, VFIO_DEVICE_DETACH_IOMMUFD_PT, &detach_data); + if (ret) { + error_setg_errno(errp, errno, "detach %s failed", vbasedev->name); + } else { + trace_iommufd_cdev_detach_ioas_hwpt(iommufd, vbasedev->name); + } + return ret; +} + +static int iommufd_cdev_attach_container(VFIODevice *vbasedev, + VFIOIOMMUFDContainer *container, + Error **errp) +{ + return iommufd_cdev_attach_ioas_hwpt(vbasedev, container->ioas_id, errp); +} + +static void iommufd_cdev_detach_container(VFIODevice *vbasedev, + VFIOIOMMUFDContainer *container) +{ + Error *err = NULL; + + if (iommufd_cdev_detach_ioas_hwpt(vbasedev, &err)) { + error_report_err(err); + } +} + +static void iommufd_cdev_container_destroy(VFIOIOMMUFDContainer *container) +{ + VFIOContainerBase *bcontainer = &container->bcontainer; + + if (!QLIST_EMPTY(&bcontainer->device_list)) { + return; + } + memory_listener_unregister(&bcontainer->listener); + vfio_container_destroy(bcontainer); + iommufd_backend_free_id(container->be, container->ioas_id); + g_free(container); +} + +static int iommufd_cdev_ram_block_discard_disable(bool state) +{ + /* + * We support coordinated discarding of RAM via the RamDiscardManager. + */ + return ram_block_uncoordinated_discard_disable(state); +} + +static int iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container, + uint32_t ioas_id, Error **errp) +{ + VFIOContainerBase *bcontainer = &container->bcontainer; + struct iommu_ioas_iova_ranges *info; + struct iommu_iova_range *iova_ranges; + int ret, sz, fd = container->be->fd; + + info = g_malloc0(sizeof(*info)); + info->size = sizeof(*info); + info->ioas_id = ioas_id; + + ret = ioctl(fd, IOMMU_IOAS_IOVA_RANGES, info); + if (ret && errno != EMSGSIZE) { + goto error; + } + + sz = info->num_iovas * sizeof(struct iommu_iova_range); + info = g_realloc(info, sizeof(*info) + sz); + info->allowed_iovas = (uintptr_t)(info + 1); + + ret = ioctl(fd, IOMMU_IOAS_IOVA_RANGES, info); + if (ret) { + goto error; + } + + iova_ranges = (struct iommu_iova_range *)(uintptr_t)info->allowed_iovas; + + for (int i = 0; i < info->num_iovas; i++) { + Range *range = g_new(Range, 1); + + range_set_bounds(range, iova_ranges[i].start, iova_ranges[i].last); + bcontainer->iova_ranges = + range_list_insert(bcontainer->iova_ranges, range); + } + bcontainer->pgsizes = info->out_iova_alignment; + + g_free(info); + return 0; + +error: + ret = -errno; + g_free(info); + error_setg_errno(errp, errno, "Cannot get IOVA ranges"); + return ret; +} + +static int iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp) +{ + VFIOContainerBase *bcontainer; + VFIOIOMMUFDContainer *container; + VFIOAddressSpace *space; + struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) }; + int ret, devfd; + uint32_t ioas_id; + Error *err = NULL; + const VFIOIOMMUClass *iommufd_vioc = + VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_IOMMUFD)); + + if (vbasedev->fd < 0) { + devfd = iommufd_cdev_getfd(vbasedev->sysfsdev, errp); + if (devfd < 0) { + return devfd; + } + vbasedev->fd = devfd; + } else { + devfd = vbasedev->fd; + } + + ret = iommufd_cdev_connect_and_bind(vbasedev, errp); + if (ret) { + goto err_connect_bind; + } + + space = vfio_get_address_space(as); + + /* try to attach to an existing container in this space */ + QLIST_FOREACH(bcontainer, &space->containers, next) { + container = container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); + if (bcontainer->ops != iommufd_vioc || + vbasedev->iommufd != container->be) { + continue; + } + if (iommufd_cdev_attach_container(vbasedev, container, &err)) { + const char *msg = error_get_pretty(err); + + trace_iommufd_cdev_fail_attach_existing_container(msg); + error_free(err); + err = NULL; + } else { + ret = iommufd_cdev_ram_block_discard_disable(true); + if (ret) { + error_setg(errp, + "Cannot set discarding of RAM broken (%d)", ret); + goto err_discard_disable; + } + goto found_container; + } + } + + /* Need to allocate a new dedicated container */ + ret = iommufd_backend_alloc_ioas(vbasedev->iommufd, &ioas_id, errp); + if (ret < 0) { + goto err_alloc_ioas; + } + + trace_iommufd_cdev_alloc_ioas(vbasedev->iommufd->fd, ioas_id); + + container = g_malloc0(sizeof(*container)); + container->be = vbasedev->iommufd; + container->ioas_id = ioas_id; + + bcontainer = &container->bcontainer; + vfio_container_init(bcontainer, space, iommufd_vioc); + QLIST_INSERT_HEAD(&space->containers, bcontainer, next); + + ret = iommufd_cdev_attach_container(vbasedev, container, errp); + if (ret) { + goto err_attach_container; + } + + ret = iommufd_cdev_ram_block_discard_disable(true); + if (ret) { + goto err_discard_disable; + } + + ret = iommufd_cdev_get_info_iova_range(container, ioas_id, &err); + if (ret) { + error_append_hint(&err, + "Fallback to default 64bit IOVA range and 4K page size\n"); + warn_report_err(err); + err = NULL; + bcontainer->pgsizes = qemu_real_host_page_size(); + } + + bcontainer->listener = vfio_memory_listener; + memory_listener_register(&bcontainer->listener, bcontainer->space->as); + + if (bcontainer->error) { + ret = -1; + error_propagate_prepend(errp, bcontainer->error, + "memory listener initialization failed: "); + goto err_listener_register; + } + + bcontainer->initialized = true; + +found_container: + ret = ioctl(devfd, VFIO_DEVICE_GET_INFO, &dev_info); + if (ret) { + error_setg_errno(errp, errno, "error getting device info"); + goto err_listener_register; + } + + ret = vfio_cpr_register_container(bcontainer, errp); + if (ret) { + goto err_listener_register; + } + + /* + * TODO: examine RAM_BLOCK_DISCARD stuff, should we do group level + * for discarding incompatibility check as well? + */ + if (vbasedev->ram_block_discard_allowed) { + iommufd_cdev_ram_block_discard_disable(false); + } + + vbasedev->group = 0; + vbasedev->num_irqs = dev_info.num_irqs; + vbasedev->num_regions = dev_info.num_regions; + vbasedev->flags = dev_info.flags; + vbasedev->reset_works = !!(dev_info.flags & VFIO_DEVICE_FLAGS_RESET); + vbasedev->bcontainer = bcontainer; + QLIST_INSERT_HEAD(&bcontainer->device_list, vbasedev, container_next); + QLIST_INSERT_HEAD(&vfio_device_list, vbasedev, global_next); + + trace_iommufd_cdev_device_info(vbasedev->name, devfd, vbasedev->num_irqs, + vbasedev->num_regions, vbasedev->flags); + return 0; + +err_listener_register: + iommufd_cdev_ram_block_discard_disable(false); +err_discard_disable: + iommufd_cdev_detach_container(vbasedev, container); +err_attach_container: + iommufd_cdev_container_destroy(container); +err_alloc_ioas: + vfio_put_address_space(space); + iommufd_cdev_unbind_and_disconnect(vbasedev); +err_connect_bind: + close(vbasedev->fd); + return ret; +} + +static void iommufd_cdev_detach(VFIODevice *vbasedev) +{ + VFIOContainerBase *bcontainer = vbasedev->bcontainer; + VFIOAddressSpace *space = bcontainer->space; + VFIOIOMMUFDContainer *container = container_of(bcontainer, + VFIOIOMMUFDContainer, + bcontainer); + QLIST_REMOVE(vbasedev, global_next); + QLIST_REMOVE(vbasedev, container_next); + vbasedev->bcontainer = NULL; + + if (!vbasedev->ram_block_discard_allowed) { + iommufd_cdev_ram_block_discard_disable(false); + } + + vfio_cpr_unregister_container(bcontainer); + iommufd_cdev_detach_container(vbasedev, container); + iommufd_cdev_container_destroy(container); + vfio_put_address_space(space); + + iommufd_cdev_unbind_and_disconnect(vbasedev); + close(vbasedev->fd); +} + +static VFIODevice *iommufd_cdev_pci_find_by_devid(__u32 devid) +{ + VFIODevice *vbasedev_iter; + const VFIOIOMMUClass *iommufd_vioc = + VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_IOMMUFD)); + + QLIST_FOREACH(vbasedev_iter, &vfio_device_list, global_next) { + if (vbasedev_iter->bcontainer->ops != iommufd_vioc) { + continue; + } + if (devid == vbasedev_iter->devid) { + return vbasedev_iter; + } + } + return NULL; +} + +static VFIOPCIDevice * +iommufd_cdev_dep_get_realized_vpdev(struct vfio_pci_dependent_device *dep_dev, + VFIODevice *reset_dev) +{ + VFIODevice *vbasedev_tmp; + + if (dep_dev->devid == reset_dev->devid || + dep_dev->devid == VFIO_PCI_DEVID_OWNED) { + return NULL; + } + + vbasedev_tmp = iommufd_cdev_pci_find_by_devid(dep_dev->devid); + if (!vbasedev_tmp || !vbasedev_tmp->dev->realized || + vbasedev_tmp->type != VFIO_DEVICE_TYPE_PCI) { + return NULL; + } + + return container_of(vbasedev_tmp, VFIOPCIDevice, vbasedev); +} + +static int iommufd_cdev_pci_hot_reset(VFIODevice *vbasedev, bool single) +{ + VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); + struct vfio_pci_hot_reset_info *info = NULL; + struct vfio_pci_dependent_device *devices; + struct vfio_pci_hot_reset *reset; + int ret, i; + bool multi = false; + + trace_vfio_pci_hot_reset(vdev->vbasedev.name, single ? "one" : "multi"); + + if (!single) { + vfio_pci_pre_reset(vdev); + } + vdev->vbasedev.needs_reset = false; + + ret = vfio_pci_get_pci_hot_reset_info(vdev, &info); + + if (ret) { + goto out_single; + } + + assert(info->flags & VFIO_PCI_HOT_RESET_FLAG_DEV_ID); + + devices = &info->devices[0]; + + if (!(info->flags & VFIO_PCI_HOT_RESET_FLAG_DEV_ID_OWNED)) { + if (!vdev->has_pm_reset) { + for (i = 0; i < info->count; i++) { + if (devices[i].devid == VFIO_PCI_DEVID_NOT_OWNED) { + error_report("vfio: Cannot reset device %s, " + "depends on device %04x:%02x:%02x.%x " + "which is not owned.", + vdev->vbasedev.name, devices[i].segment, + devices[i].bus, PCI_SLOT(devices[i].devfn), + PCI_FUNC(devices[i].devfn)); + } + } + } + ret = -EPERM; + goto out_single; + } + + trace_vfio_pci_hot_reset_has_dep_devices(vdev->vbasedev.name); + + for (i = 0; i < info->count; i++) { + VFIOPCIDevice *tmp; + + trace_iommufd_cdev_pci_hot_reset_dep_devices(devices[i].segment, + devices[i].bus, + PCI_SLOT(devices[i].devfn), + PCI_FUNC(devices[i].devfn), + devices[i].devid); + + /* + * If a VFIO cdev device is resettable, all the dependent devices + * are either bound to same iommufd or within same iommu_groups as + * one of the iommufd bound devices. + */ + assert(devices[i].devid != VFIO_PCI_DEVID_NOT_OWNED); + + tmp = iommufd_cdev_dep_get_realized_vpdev(&devices[i], &vdev->vbasedev); + if (!tmp) { + continue; + } + + if (single) { + ret = -EINVAL; + goto out_single; + } + vfio_pci_pre_reset(tmp); + tmp->vbasedev.needs_reset = false; + multi = true; + } + + if (!single && !multi) { + ret = -EINVAL; + goto out_single; + } + + /* Use zero length array for hot reset with iommufd backend */ + reset = g_malloc0(sizeof(*reset)); + reset->argsz = sizeof(*reset); + + /* Bus reset! */ + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_PCI_HOT_RESET, reset); + g_free(reset); + if (ret) { + ret = -errno; + } + + trace_vfio_pci_hot_reset_result(vdev->vbasedev.name, + ret ? strerror(errno) : "Success"); + + /* Re-enable INTx on affected devices */ + for (i = 0; i < info->count; i++) { + VFIOPCIDevice *tmp; + + tmp = iommufd_cdev_dep_get_realized_vpdev(&devices[i], &vdev->vbasedev); + if (!tmp) { + continue; + } + vfio_pci_post_reset(tmp); + } +out_single: + if (!single) { + vfio_pci_post_reset(vdev); + } + g_free(info); + + return ret; +} + +static void vfio_iommu_iommufd_class_init(ObjectClass *klass, void *data) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); + + vioc->dma_map = iommufd_cdev_map; + vioc->dma_unmap = iommufd_cdev_unmap; + vioc->attach_device = iommufd_cdev_attach; + vioc->detach_device = iommufd_cdev_detach; + vioc->pci_hot_reset = iommufd_cdev_pci_hot_reset; +}; + +static const TypeInfo types[] = { + { + .name = TYPE_VFIO_IOMMU_IOMMUFD, + .parent = TYPE_VFIO_IOMMU, + .class_init = vfio_iommu_iommufd_class_init, + }, +}; + +DEFINE_TYPES(types) diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index da9af297a0..bba776f75c 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -1,8 +1,15 @@ vfio_ss = ss.source_set() vfio_ss.add(files( + 'helpers.c', 'common.c', - 'spapr.c', + 'container-base.c', + 'container.c', 'migration.c', + 'cpr.c', +)) +vfio_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr.c')) +vfio_ss.add(when: 'CONFIG_IOMMUFD', if_true: files( + 'iommufd.c', )) vfio_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( 'display.c', diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index a6ad1f8945..1149c6b374 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -10,17 +10,19 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/cutils.h" +#include "qemu/units.h" +#include "qemu/error-report.h" #include #include #include "sysemu/runstate.h" #include "hw/vfio/vfio-common.h" -#include "migration/migration.h" +#include "migration/misc.h" +#include "migration/savevm.h" #include "migration/vmstate.h" #include "migration/qemu-file.h" #include "migration/register.h" #include "migration/blocker.h" -#include "migration/misc.h" #include "qapi/error.h" #include "exec/ramlist.h" #include "exec/ram_addr.h" @@ -43,311 +45,145 @@ #define VFIO_MIG_FLAG_DEV_CONFIG_STATE (0xffffffffef100002ULL) #define VFIO_MIG_FLAG_DEV_SETUP_STATE (0xffffffffef100003ULL) #define VFIO_MIG_FLAG_DEV_DATA_STATE (0xffffffffef100004ULL) +#define VFIO_MIG_FLAG_DEV_INIT_DATA_SENT (0xffffffffef100005ULL) + +/* + * This is an arbitrary size based on migration of mlx5 devices, where typically + * total device migration size is on the order of 100s of MB. Testing with + * larger values, e.g. 128MB and 1GB, did not show a performance improvement. + */ +#define VFIO_MIG_DEFAULT_DATA_BUFFER_SIZE (1 * MiB) static int64_t bytes_transferred; -static inline int vfio_mig_access(VFIODevice *vbasedev, void *val, int count, - off_t off, bool iswrite) +static const char *mig_state_to_str(enum vfio_device_mig_state state) { - int ret; - - ret = iswrite ? pwrite(vbasedev->fd, val, count, off) : - pread(vbasedev->fd, val, count, off); - if (ret < count) { - error_report("vfio_mig_%s %d byte %s: failed at offset 0x%" - HWADDR_PRIx", err: %s", iswrite ? "write" : "read", count, - vbasedev->name, off, strerror(errno)); - return (ret < 0) ? ret : -EINVAL; + switch (state) { + case VFIO_DEVICE_STATE_ERROR: + return "ERROR"; + case VFIO_DEVICE_STATE_STOP: + return "STOP"; + case VFIO_DEVICE_STATE_RUNNING: + return "RUNNING"; + case VFIO_DEVICE_STATE_STOP_COPY: + return "STOP_COPY"; + case VFIO_DEVICE_STATE_RESUMING: + return "RESUMING"; + case VFIO_DEVICE_STATE_RUNNING_P2P: + return "RUNNING_P2P"; + case VFIO_DEVICE_STATE_PRE_COPY: + return "PRE_COPY"; + case VFIO_DEVICE_STATE_PRE_COPY_P2P: + return "PRE_COPY_P2P"; + default: + return "UNKNOWN STATE"; } - return 0; } -static int vfio_mig_rw(VFIODevice *vbasedev, __u8 *buf, size_t count, - off_t off, bool iswrite) -{ - int ret, done = 0; - __u8 *tbuf = buf; - - while (count) { - int bytes = 0; - - if (count >= 8 && !(off % 8)) { - bytes = 8; - } else if (count >= 4 && !(off % 4)) { - bytes = 4; - } else if (count >= 2 && !(off % 2)) { - bytes = 2; - } else { - bytes = 1; - } - - ret = vfio_mig_access(vbasedev, tbuf, bytes, off, iswrite); - if (ret) { - return ret; - } - - count -= bytes; - done += bytes; - off += bytes; - tbuf += bytes; - } - return done; -} - -#define vfio_mig_read(f, v, c, o) vfio_mig_rw(f, (__u8 *)v, c, o, false) -#define vfio_mig_write(f, v, c, o) vfio_mig_rw(f, (__u8 *)v, c, o, true) - -#define VFIO_MIG_STRUCT_OFFSET(f) \ - offsetof(struct vfio_device_migration_info, f) -/* - * Change the device_state register for device @vbasedev. Bits set in @mask - * are preserved, bits set in @value are set, and bits not set in either @mask - * or @value are cleared in device_state. If the register cannot be accessed, - * the resulting state would be invalid, or the device enters an error state, - * an error is returned. - */ - -static int vfio_migration_set_state(VFIODevice *vbasedev, uint32_t mask, - uint32_t value) +static int vfio_migration_set_state(VFIODevice *vbasedev, + enum vfio_device_mig_state new_state, + enum vfio_device_mig_state recover_state) { VFIOMigration *migration = vbasedev->migration; - VFIORegion *region = &migration->region; - off_t dev_state_off = region->fd_offset + - VFIO_MIG_STRUCT_OFFSET(device_state); - uint32_t device_state; + uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature) + + sizeof(struct vfio_device_feature_mig_state), + sizeof(uint64_t))] = {}; + struct vfio_device_feature *feature = (struct vfio_device_feature *)buf; + struct vfio_device_feature_mig_state *mig_state = + (struct vfio_device_feature_mig_state *)feature->data; int ret; - ret = vfio_mig_read(vbasedev, &device_state, sizeof(device_state), - dev_state_off); - if (ret < 0) { + feature->argsz = sizeof(buf); + feature->flags = + VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_MIG_DEVICE_STATE; + mig_state->device_state = new_state; + if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) { + /* Try to set the device in some good state */ + ret = -errno; + + if (recover_state == VFIO_DEVICE_STATE_ERROR) { + error_report("%s: Failed setting device state to %s, err: %s. " + "Recover state is ERROR. Resetting device", + vbasedev->name, mig_state_to_str(new_state), + strerror(errno)); + + goto reset_device; + } + + error_report( + "%s: Failed setting device state to %s, err: %s. Setting device in recover state %s", + vbasedev->name, mig_state_to_str(new_state), + strerror(errno), mig_state_to_str(recover_state)); + + mig_state->device_state = recover_state; + if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) { + ret = -errno; + error_report( + "%s: Failed setting device in recover state, err: %s. Resetting device", + vbasedev->name, strerror(errno)); + + goto reset_device; + } + + migration->device_state = recover_state; + return ret; } - device_state = (device_state & mask) | value; - - if (!VFIO_DEVICE_STATE_VALID(device_state)) { - return -EINVAL; - } - - ret = vfio_mig_write(vbasedev, &device_state, sizeof(device_state), - dev_state_off); - if (ret < 0) { - int rret; - - rret = vfio_mig_read(vbasedev, &device_state, sizeof(device_state), - dev_state_off); - - if ((rret < 0) || (VFIO_DEVICE_STATE_IS_ERROR(device_state))) { - hw_error("%s: Device in error state 0x%x", vbasedev->name, - device_state); - return rret ? rret : -EIO; - } - return ret; - } - - migration->device_state = device_state; - trace_vfio_migration_set_state(vbasedev->name, device_state); - return 0; -} - -static void *get_data_section_size(VFIORegion *region, uint64_t data_offset, - uint64_t data_size, uint64_t *size) -{ - void *ptr = NULL; - uint64_t limit = 0; - int i; - - if (!region->mmaps) { - if (size) { - *size = MIN(data_size, region->size - data_offset); - } - return ptr; - } - - for (i = 0; i < region->nr_mmaps; i++) { - VFIOMmap *map = region->mmaps + i; - - if ((data_offset >= map->offset) && - (data_offset < map->offset + map->size)) { - - /* check if data_offset is within sparse mmap areas */ - ptr = map->mmap + data_offset - map->offset; - if (size) { - *size = MIN(data_size, map->offset + map->size - data_offset); - } - break; - } else if ((data_offset < map->offset) && - (!limit || limit > map->offset)) { + migration->device_state = new_state; + if (mig_state->data_fd != -1) { + if (migration->data_fd != -1) { /* - * data_offset is not within sparse mmap areas, find size of - * non-mapped area. Check through all list since region->mmaps list - * is not sorted. + * This can happen if the device is asynchronously reset and + * terminates a data transfer. */ - limit = map->offset; + error_report("%s: data_fd out of sync", vbasedev->name); + close(mig_state->data_fd); + + return -EBADF; } + + migration->data_fd = mig_state->data_fd; } - if (!ptr && size) { - *size = limit ? MIN(data_size, limit - data_offset) : data_size; + trace_vfio_migration_set_state(vbasedev->name, mig_state_to_str(new_state)); + + return 0; + +reset_device: + if (ioctl(vbasedev->fd, VFIO_DEVICE_RESET)) { + hw_error("%s: Failed resetting device, err: %s", vbasedev->name, + strerror(errno)); } - return ptr; + + migration->device_state = VFIO_DEVICE_STATE_RUNNING; + + return ret; } -static int vfio_save_buffer(QEMUFile *f, VFIODevice *vbasedev, uint64_t *size) +/* + * Some device state transitions require resetting the device if they fail. + * This function sets the device in new_state and resets the device if that + * fails. Reset is done by using ERROR as the recover state. + */ +static int +vfio_migration_set_state_or_reset(VFIODevice *vbasedev, + enum vfio_device_mig_state new_state) { - VFIOMigration *migration = vbasedev->migration; - VFIORegion *region = &migration->region; - uint64_t data_offset = 0, data_size = 0, sz; - int ret; - - ret = vfio_mig_read(vbasedev, &data_offset, sizeof(data_offset), - region->fd_offset + VFIO_MIG_STRUCT_OFFSET(data_offset)); - if (ret < 0) { - return ret; - } - - ret = vfio_mig_read(vbasedev, &data_size, sizeof(data_size), - region->fd_offset + VFIO_MIG_STRUCT_OFFSET(data_size)); - if (ret < 0) { - return ret; - } - - trace_vfio_save_buffer(vbasedev->name, data_offset, data_size, - migration->pending_bytes); - - qemu_put_be64(f, data_size); - sz = data_size; - - while (sz) { - void *buf; - uint64_t sec_size; - bool buf_allocated = false; - - buf = get_data_section_size(region, data_offset, sz, &sec_size); - - if (!buf) { - buf = g_try_malloc(sec_size); - if (!buf) { - error_report("%s: Error allocating buffer ", __func__); - return -ENOMEM; - } - buf_allocated = true; - - ret = vfio_mig_read(vbasedev, buf, sec_size, - region->fd_offset + data_offset); - if (ret < 0) { - g_free(buf); - return ret; - } - } - - qemu_put_buffer(f, buf, sec_size); - - if (buf_allocated) { - g_free(buf); - } - sz -= sec_size; - data_offset += sec_size; - } - - ret = qemu_file_get_error(f); - - if (!ret && size) { - *size = data_size; - } - - bytes_transferred += data_size; - return ret; + return vfio_migration_set_state(vbasedev, new_state, + VFIO_DEVICE_STATE_ERROR); } static int vfio_load_buffer(QEMUFile *f, VFIODevice *vbasedev, uint64_t data_size) -{ - VFIORegion *region = &vbasedev->migration->region; - uint64_t data_offset = 0, size, report_size; - int ret; - - do { - ret = vfio_mig_read(vbasedev, &data_offset, sizeof(data_offset), - region->fd_offset + VFIO_MIG_STRUCT_OFFSET(data_offset)); - if (ret < 0) { - return ret; - } - - if (data_offset + data_size > region->size) { - /* - * If data_size is greater than the data section of migration region - * then iterate the write buffer operation. This case can occur if - * size of migration region at destination is smaller than size of - * migration region at source. - */ - report_size = size = region->size - data_offset; - data_size -= size; - } else { - report_size = size = data_size; - data_size = 0; - } - - trace_vfio_load_state_device_data(vbasedev->name, data_offset, size); - - while (size) { - void *buf; - uint64_t sec_size; - bool buf_alloc = false; - - buf = get_data_section_size(region, data_offset, size, &sec_size); - - if (!buf) { - buf = g_try_malloc(sec_size); - if (!buf) { - error_report("%s: Error allocating buffer ", __func__); - return -ENOMEM; - } - buf_alloc = true; - } - - qemu_get_buffer(f, buf, sec_size); - - if (buf_alloc) { - ret = vfio_mig_write(vbasedev, buf, sec_size, - region->fd_offset + data_offset); - g_free(buf); - - if (ret < 0) { - return ret; - } - } - size -= sec_size; - data_offset += sec_size; - } - - ret = vfio_mig_write(vbasedev, &report_size, sizeof(report_size), - region->fd_offset + VFIO_MIG_STRUCT_OFFSET(data_size)); - if (ret < 0) { - return ret; - } - } while (data_size); - - return 0; -} - -static int vfio_update_pending(VFIODevice *vbasedev) { VFIOMigration *migration = vbasedev->migration; - VFIORegion *region = &migration->region; - uint64_t pending_bytes = 0; int ret; - ret = vfio_mig_read(vbasedev, &pending_bytes, sizeof(pending_bytes), - region->fd_offset + VFIO_MIG_STRUCT_OFFSET(pending_bytes)); - if (ret < 0) { - migration->pending_bytes = 0; - return ret; - } + ret = qemu_file_get_to_fd(f, migration->data_fd, data_size); + trace_vfio_load_state_device_data(vbasedev->name, data_size, ret); - migration->pending_bytes = pending_bytes; - trace_vfio_update_pending(vbasedev->name, pending_bytes); - return 0; + return ret; } static int vfio_save_device_config_state(QEMUFile *f, void *opaque) @@ -398,184 +234,336 @@ static void vfio_migration_cleanup(VFIODevice *vbasedev) { VFIOMigration *migration = vbasedev->migration; - if (migration->region.mmaps) { - vfio_region_unmap(&migration->region); + close(migration->data_fd); + migration->data_fd = -1; +} + +static int vfio_query_stop_copy_size(VFIODevice *vbasedev, + uint64_t *stop_copy_size) +{ + uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature) + + sizeof(struct vfio_device_feature_mig_data_size), + sizeof(uint64_t))] = {}; + struct vfio_device_feature *feature = (struct vfio_device_feature *)buf; + struct vfio_device_feature_mig_data_size *mig_data_size = + (struct vfio_device_feature_mig_data_size *)feature->data; + + feature->argsz = sizeof(buf); + feature->flags = + VFIO_DEVICE_FEATURE_GET | VFIO_DEVICE_FEATURE_MIG_DATA_SIZE; + + if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) { + return -errno; } + + *stop_copy_size = mig_data_size->stop_copy_length; + + return 0; +} + +static int vfio_query_precopy_size(VFIOMigration *migration) +{ + struct vfio_precopy_info precopy = { + .argsz = sizeof(precopy), + }; + + migration->precopy_init_size = 0; + migration->precopy_dirty_size = 0; + + if (ioctl(migration->data_fd, VFIO_MIG_GET_PRECOPY_INFO, &precopy)) { + return -errno; + } + + migration->precopy_init_size = precopy.initial_bytes; + migration->precopy_dirty_size = precopy.dirty_bytes; + + return 0; +} + +/* Returns the size of saved data on success and -errno on error */ +static ssize_t vfio_save_block(QEMUFile *f, VFIOMigration *migration) +{ + ssize_t data_size; + + data_size = read(migration->data_fd, migration->data_buffer, + migration->data_buffer_size); + if (data_size < 0) { + /* + * Pre-copy emptied all the device state for now. For more information, + * please refer to the Linux kernel VFIO uAPI. + */ + if (errno == ENOMSG) { + return 0; + } + + return -errno; + } + if (data_size == 0) { + return 0; + } + + qemu_put_be64(f, VFIO_MIG_FLAG_DEV_DATA_STATE); + qemu_put_be64(f, data_size); + qemu_put_buffer(f, migration->data_buffer, data_size); + bytes_transferred += data_size; + + trace_vfio_save_block(migration->vbasedev->name, data_size); + + return qemu_file_get_error(f) ?: data_size; +} + +static void vfio_update_estimated_pending_data(VFIOMigration *migration, + uint64_t data_size) +{ + if (!data_size) { + /* + * Pre-copy emptied all the device state for now, update estimated sizes + * accordingly. + */ + migration->precopy_init_size = 0; + migration->precopy_dirty_size = 0; + + return; + } + + if (migration->precopy_init_size) { + uint64_t init_size = MIN(migration->precopy_init_size, data_size); + + migration->precopy_init_size -= init_size; + data_size -= init_size; + } + + migration->precopy_dirty_size -= MIN(migration->precopy_dirty_size, + data_size); +} + +static bool vfio_precopy_supported(VFIODevice *vbasedev) +{ + VFIOMigration *migration = vbasedev->migration; + + return migration->mig_flags & VFIO_MIGRATION_PRE_COPY; } /* ---------------------------------------------------------------------- */ +static int vfio_save_prepare(void *opaque, Error **errp) +{ + VFIODevice *vbasedev = opaque; + + /* + * Snapshot doesn't use postcopy nor background snapshot, so allow snapshot + * even if they are on. + */ + if (runstate_check(RUN_STATE_SAVE_VM)) { + return 0; + } + + if (migrate_postcopy_ram()) { + error_setg( + errp, "%s: VFIO migration is not supported with postcopy migration", + vbasedev->name); + return -EOPNOTSUPP; + } + + if (migrate_background_snapshot()) { + error_setg( + errp, + "%s: VFIO migration is not supported with background snapshot", + vbasedev->name); + return -EOPNOTSUPP; + } + + return 0; +} + static int vfio_save_setup(QEMUFile *f, void *opaque) { VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; - int ret; - - trace_vfio_save_setup(vbasedev->name); + uint64_t stop_copy_size = VFIO_MIG_DEFAULT_DATA_BUFFER_SIZE; qemu_put_be64(f, VFIO_MIG_FLAG_DEV_SETUP_STATE); - if (migration->region.mmaps) { - /* - * Calling vfio_region_mmap() from migration thread. Memory API called - * from this function require locking the iothread when called from - * outside the main loop thread. - */ - qemu_mutex_lock_iothread(); - ret = vfio_region_mmap(&migration->region); - qemu_mutex_unlock_iothread(); - if (ret) { - error_report("%s: Failed to mmap VFIO migration region: %s", - vbasedev->name, strerror(-ret)); - error_report("%s: Falling back to slow path", vbasedev->name); + vfio_query_stop_copy_size(vbasedev, &stop_copy_size); + migration->data_buffer_size = MIN(VFIO_MIG_DEFAULT_DATA_BUFFER_SIZE, + stop_copy_size); + migration->data_buffer = g_try_malloc0(migration->data_buffer_size); + if (!migration->data_buffer) { + error_report("%s: Failed to allocate migration data buffer", + vbasedev->name); + return -ENOMEM; + } + + if (vfio_precopy_supported(vbasedev)) { + int ret; + + switch (migration->device_state) { + case VFIO_DEVICE_STATE_RUNNING: + ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_PRE_COPY, + VFIO_DEVICE_STATE_RUNNING); + if (ret) { + return ret; + } + + vfio_query_precopy_size(migration); + + break; + case VFIO_DEVICE_STATE_STOP: + /* vfio_save_complete_precopy() will go to STOP_COPY */ + break; + default: + return -EINVAL; } } - ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_MASK, - VFIO_DEVICE_STATE_V1_SAVING); - if (ret) { - error_report("%s: Failed to set state SAVING", vbasedev->name); - return ret; - } + trace_vfio_save_setup(vbasedev->name, migration->data_buffer_size); qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); - ret = qemu_file_get_error(f); - if (ret) { - return ret; - } - - return 0; + return qemu_file_get_error(f); } static void vfio_save_cleanup(void *opaque) { VFIODevice *vbasedev = opaque; + VFIOMigration *migration = vbasedev->migration; + /* + * Changing device state from STOP_COPY to STOP can take time. Do it here, + * after migration has completed, so it won't increase downtime. + */ + if (migration->device_state == VFIO_DEVICE_STATE_STOP_COPY) { + vfio_migration_set_state_or_reset(vbasedev, VFIO_DEVICE_STATE_STOP); + } + + g_free(migration->data_buffer); + migration->data_buffer = NULL; + migration->precopy_init_size = 0; + migration->precopy_dirty_size = 0; + migration->initial_data_sent = false; vfio_migration_cleanup(vbasedev); trace_vfio_save_cleanup(vbasedev->name); } -static void vfio_save_pending(QEMUFile *f, void *opaque, - uint64_t threshold_size, - uint64_t *res_precopy_only, - uint64_t *res_compatible, - uint64_t *res_postcopy_only) +static void vfio_state_pending_estimate(void *opaque, uint64_t *must_precopy, + uint64_t *can_postcopy) { VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; - int ret; - ret = vfio_update_pending(vbasedev); - if (ret) { + if (!vfio_device_state_is_precopy(vbasedev)) { return; } - *res_precopy_only += migration->pending_bytes; + *must_precopy += + migration->precopy_init_size + migration->precopy_dirty_size; - trace_vfio_save_pending(vbasedev->name, *res_precopy_only, - *res_postcopy_only, *res_compatible); + trace_vfio_state_pending_estimate(vbasedev->name, *must_precopy, + *can_postcopy, + migration->precopy_init_size, + migration->precopy_dirty_size); } +/* + * Migration size of VFIO devices can be as little as a few KBs or as big as + * many GBs. This value should be big enough to cover the worst case. + */ +#define VFIO_MIG_STOP_COPY_SIZE (100 * GiB) + +static void vfio_state_pending_exact(void *opaque, uint64_t *must_precopy, + uint64_t *can_postcopy) +{ + VFIODevice *vbasedev = opaque; + VFIOMigration *migration = vbasedev->migration; + uint64_t stop_copy_size = VFIO_MIG_STOP_COPY_SIZE; + + /* + * If getting pending migration size fails, VFIO_MIG_STOP_COPY_SIZE is + * reported so downtime limit won't be violated. + */ + vfio_query_stop_copy_size(vbasedev, &stop_copy_size); + *must_precopy += stop_copy_size; + + if (vfio_device_state_is_precopy(vbasedev)) { + vfio_query_precopy_size(migration); + + *must_precopy += + migration->precopy_init_size + migration->precopy_dirty_size; + } + + trace_vfio_state_pending_exact(vbasedev->name, *must_precopy, *can_postcopy, + stop_copy_size, migration->precopy_init_size, + migration->precopy_dirty_size); +} + +static bool vfio_is_active_iterate(void *opaque) +{ + VFIODevice *vbasedev = opaque; + + return vfio_device_state_is_precopy(vbasedev); +} + +/* + * Note about migration rate limiting: VFIO migration buffer size is currently + * limited to 1MB, so there is no need to check if migration rate exceeded (as + * in the worst case it will exceed by 1MB). However, if the buffer size is + * later changed to a bigger value, migration rate should be enforced here. + */ static int vfio_save_iterate(QEMUFile *f, void *opaque) { VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; - uint64_t data_size; - int ret; + ssize_t data_size; - qemu_put_be64(f, VFIO_MIG_FLAG_DEV_DATA_STATE); - - if (migration->pending_bytes == 0) { - ret = vfio_update_pending(vbasedev); - if (ret) { - return ret; - } - - if (migration->pending_bytes == 0) { - qemu_put_be64(f, 0); - qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); - /* indicates data finished, goto complete phase */ - return 1; - } + data_size = vfio_save_block(f, migration); + if (data_size < 0) { + return data_size; } - ret = vfio_save_buffer(f, vbasedev, &data_size); - if (ret) { - error_report("%s: vfio_save_buffer failed %s", vbasedev->name, - strerror(errno)); - return ret; + vfio_update_estimated_pending_data(migration, data_size); + + if (migrate_switchover_ack() && !migration->precopy_init_size && + !migration->initial_data_sent) { + qemu_put_be64(f, VFIO_MIG_FLAG_DEV_INIT_DATA_SENT); + migration->initial_data_sent = true; + } else { + qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); } - qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); + trace_vfio_save_iterate(vbasedev->name, migration->precopy_init_size, + migration->precopy_dirty_size); - ret = qemu_file_get_error(f); - if (ret) { - return ret; - } - - /* - * Reset pending_bytes as .save_live_pending is not called during savevm or - * snapshot case, in such case vfio_update_pending() at the start of this - * function updates pending_bytes. - */ - migration->pending_bytes = 0; - trace_vfio_save_iterate(vbasedev->name, data_size); - return 0; + return !migration->precopy_init_size && !migration->precopy_dirty_size; } static int vfio_save_complete_precopy(QEMUFile *f, void *opaque) { VFIODevice *vbasedev = opaque; - VFIOMigration *migration = vbasedev->migration; - uint64_t data_size; + ssize_t data_size; int ret; - ret = vfio_migration_set_state(vbasedev, ~VFIO_DEVICE_STATE_V1_RUNNING, - VFIO_DEVICE_STATE_V1_SAVING); - if (ret) { - error_report("%s: Failed to set state STOP and SAVING", - vbasedev->name); - return ret; - } - - ret = vfio_update_pending(vbasedev); + /* We reach here with device state STOP or STOP_COPY only */ + ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_STOP_COPY, + VFIO_DEVICE_STATE_STOP); if (ret) { return ret; } - while (migration->pending_bytes > 0) { - qemu_put_be64(f, VFIO_MIG_FLAG_DEV_DATA_STATE); - ret = vfio_save_buffer(f, vbasedev, &data_size); - if (ret < 0) { - error_report("%s: Failed to save buffer", vbasedev->name); - return ret; + do { + data_size = vfio_save_block(f, vbasedev->migration); + if (data_size < 0) { + return data_size; } - - if (data_size == 0) { - break; - } - - ret = vfio_update_pending(vbasedev); - if (ret) { - return ret; - } - } + } while (data_size); qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); - ret = qemu_file_get_error(f); if (ret) { return ret; } - ret = vfio_migration_set_state(vbasedev, ~VFIO_DEVICE_STATE_V1_SAVING, 0); - if (ret) { - error_report("%s: Failed to set state STOPPED", vbasedev->name); - return ret; - } + trace_vfio_save_complete_precopy(vbasedev->name, ret); - trace_vfio_save_complete_precopy(vbasedev->name); return ret; } @@ -595,28 +583,9 @@ static void vfio_save_state(QEMUFile *f, void *opaque) static int vfio_load_setup(QEMUFile *f, void *opaque) { VFIODevice *vbasedev = opaque; - VFIOMigration *migration = vbasedev->migration; - int ret = 0; - if (migration->region.mmaps) { - ret = vfio_region_mmap(&migration->region); - if (ret) { - error_report("%s: Failed to mmap VFIO migration region %d: %s", - vbasedev->name, migration->region.nr, - strerror(-ret)); - error_report("%s: Falling back to slow path", vbasedev->name); - } - } - - ret = vfio_migration_set_state(vbasedev, ~VFIO_DEVICE_STATE_MASK, - VFIO_DEVICE_STATE_V1_RESUMING); - if (ret) { - error_report("%s: Failed to set state RESUMING", vbasedev->name); - if (migration->region.mmaps) { - vfio_region_unmap(&migration->region); - } - } - return ret; + return vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_RESUMING, + vbasedev->migration->device_state); } static int vfio_load_cleanup(void *opaque) @@ -625,6 +594,7 @@ static int vfio_load_cleanup(void *opaque) vfio_migration_cleanup(vbasedev); trace_vfio_load_cleanup(vbasedev->name); + return 0; } @@ -668,6 +638,24 @@ static int vfio_load_state(QEMUFile *f, void *opaque, int version_id) } break; } + case VFIO_MIG_FLAG_DEV_INIT_DATA_SENT: + { + if (!vfio_precopy_supported(vbasedev) || + !migrate_switchover_ack()) { + error_report("%s: Received INIT_DATA_SENT but switchover ack " + "is not used", vbasedev->name); + return -EINVAL; + } + + ret = qemu_loadvm_approve_switchover(); + if (ret) { + error_report( + "%s: qemu_loadvm_approve_switchover failed, err=%d (%s)", + vbasedev->name, ret, strerror(-ret)); + } + + return ret; + } default: error_report("%s: Unknown tag 0x%"PRIx64, vbasedev->name, data); return -EINVAL; @@ -682,119 +670,149 @@ static int vfio_load_state(QEMUFile *f, void *opaque, int version_id) return ret; } -static SaveVMHandlers savevm_vfio_handlers = { +static bool vfio_switchover_ack_needed(void *opaque) +{ + VFIODevice *vbasedev = opaque; + + return vfio_precopy_supported(vbasedev); +} + +static const SaveVMHandlers savevm_vfio_handlers = { + .save_prepare = vfio_save_prepare, .save_setup = vfio_save_setup, .save_cleanup = vfio_save_cleanup, - .save_live_pending = vfio_save_pending, + .state_pending_estimate = vfio_state_pending_estimate, + .state_pending_exact = vfio_state_pending_exact, + .is_active_iterate = vfio_is_active_iterate, .save_live_iterate = vfio_save_iterate, .save_live_complete_precopy = vfio_save_complete_precopy, .save_state = vfio_save_state, .load_setup = vfio_load_setup, .load_cleanup = vfio_load_cleanup, .load_state = vfio_load_state, + .switchover_ack_needed = vfio_switchover_ack_needed, }; /* ---------------------------------------------------------------------- */ -static void vfio_vmstate_change(void *opaque, bool running, RunState state) +static void vfio_vmstate_change_prepare(void *opaque, bool running, + RunState state) { VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; - uint32_t value, mask; + enum vfio_device_mig_state new_state; int ret; - if (vbasedev->migration->vm_running == running) { - return; - } + new_state = migration->device_state == VFIO_DEVICE_STATE_PRE_COPY ? + VFIO_DEVICE_STATE_PRE_COPY_P2P : + VFIO_DEVICE_STATE_RUNNING_P2P; - if (running) { - /* - * Here device state can have one of _SAVING, _RESUMING or _STOP bit. - * Transition from _SAVING to _RUNNING can happen if there is migration - * failure, in that case clear _SAVING bit. - * Transition from _RESUMING to _RUNNING occurs during resuming - * phase, in that case clear _RESUMING bit. - * In both the above cases, set _RUNNING bit. - */ - mask = ~VFIO_DEVICE_STATE_MASK; - value = VFIO_DEVICE_STATE_V1_RUNNING; - } else { - /* - * Here device state could be either _RUNNING or _SAVING|_RUNNING. Reset - * _RUNNING bit - */ - mask = ~VFIO_DEVICE_STATE_V1_RUNNING; - - /* - * When VM state transition to stop for savevm command, device should - * start saving data. - */ - if (state == RUN_STATE_SAVE_VM) { - value = VFIO_DEVICE_STATE_V1_SAVING; - } else { - value = 0; - } - } - - ret = vfio_migration_set_state(vbasedev, mask, value); + ret = vfio_migration_set_state_or_reset(vbasedev, new_state); if (ret) { /* * Migration should be aborted in this case, but vm_state_notify() * currently does not support reporting failures. */ - error_report("%s: Failed to set device state 0x%x", vbasedev->name, - (migration->device_state & mask) | value); - qemu_file_set_error(migrate_get_current()->to_dst_file, ret); + migration_file_set_error(ret); } - vbasedev->migration->vm_running = running; - trace_vfio_vmstate_change(vbasedev->name, running, RunState_str(state), - (migration->device_state & mask) | value); + + trace_vfio_vmstate_change_prepare(vbasedev->name, running, + RunState_str(state), + mig_state_to_str(new_state)); } -static void vfio_migration_state_notifier(Notifier *notifier, void *data) +static void vfio_vmstate_change(void *opaque, bool running, RunState state) +{ + VFIODevice *vbasedev = opaque; + enum vfio_device_mig_state new_state; + int ret; + + if (running) { + new_state = VFIO_DEVICE_STATE_RUNNING; + } else { + new_state = + (vfio_device_state_is_precopy(vbasedev) && + (state == RUN_STATE_FINISH_MIGRATE || state == RUN_STATE_PAUSED)) ? + VFIO_DEVICE_STATE_STOP_COPY : + VFIO_DEVICE_STATE_STOP; + } + + ret = vfio_migration_set_state_or_reset(vbasedev, new_state); + if (ret) { + /* + * Migration should be aborted in this case, but vm_state_notify() + * currently does not support reporting failures. + */ + migration_file_set_error(ret); + } + + trace_vfio_vmstate_change(vbasedev->name, running, RunState_str(state), + mig_state_to_str(new_state)); +} + +static int vfio_migration_state_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, Error **errp) { - MigrationState *s = data; VFIOMigration *migration = container_of(notifier, VFIOMigration, migration_state); VFIODevice *vbasedev = migration->vbasedev; - int ret; - trace_vfio_migration_state_notifier(vbasedev->name, - MigrationStatus_str(s->state)); + trace_vfio_migration_state_notifier(vbasedev->name, e->type); - switch (s->state) { - case MIGRATION_STATUS_CANCELLING: - case MIGRATION_STATUS_CANCELLED: - case MIGRATION_STATUS_FAILED: - bytes_transferred = 0; - ret = vfio_migration_set_state(vbasedev, - ~(VFIO_DEVICE_STATE_V1_SAVING | - VFIO_DEVICE_STATE_V1_RESUMING), - VFIO_DEVICE_STATE_V1_RUNNING); - if (ret) { - error_report("%s: Failed to set state RUNNING", vbasedev->name); - } + if (e->type == MIG_EVENT_PRECOPY_FAILED) { + vfio_migration_set_state_or_reset(vbasedev, VFIO_DEVICE_STATE_RUNNING); } + return 0; } -static void vfio_migration_exit(VFIODevice *vbasedev) +static void vfio_migration_free(VFIODevice *vbasedev) { - VFIOMigration *migration = vbasedev->migration; - - vfio_region_exit(&migration->region); - vfio_region_finalize(&migration->region); g_free(vbasedev->migration); vbasedev->migration = NULL; } -static int vfio_migration_init(VFIODevice *vbasedev, - struct vfio_region_info *info) +static int vfio_migration_query_flags(VFIODevice *vbasedev, uint64_t *mig_flags) +{ + uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature) + + sizeof(struct vfio_device_feature_migration), + sizeof(uint64_t))] = {}; + struct vfio_device_feature *feature = (struct vfio_device_feature *)buf; + struct vfio_device_feature_migration *mig = + (struct vfio_device_feature_migration *)feature->data; + + feature->argsz = sizeof(buf); + feature->flags = VFIO_DEVICE_FEATURE_GET | VFIO_DEVICE_FEATURE_MIGRATION; + if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) { + return -errno; + } + + *mig_flags = mig->flags; + + return 0; +} + +static bool vfio_dma_logging_supported(VFIODevice *vbasedev) +{ + uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature), + sizeof(uint64_t))] = {}; + struct vfio_device_feature *feature = (struct vfio_device_feature *)buf; + + feature->argsz = sizeof(buf); + feature->flags = VFIO_DEVICE_FEATURE_PROBE | + VFIO_DEVICE_FEATURE_DMA_LOGGING_START; + + return !ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature); +} + +static int vfio_migration_init(VFIODevice *vbasedev) { int ret; Object *obj; VFIOMigration *migration; char id[256] = ""; g_autofree char *path = NULL, *oid = NULL; + uint64_t mig_flags = 0; + VMChangeStateHandler *prepare_cb; if (!vbasedev->ops->vfio_get_object) { return -EINVAL; @@ -805,25 +823,24 @@ static int vfio_migration_init(VFIODevice *vbasedev, return -EINVAL; } - vbasedev->migration = g_new0(VFIOMigration, 1); - - ret = vfio_region_setup(obj, vbasedev, &vbasedev->migration->region, - info->index, "migration"); + ret = vfio_migration_query_flags(vbasedev, &mig_flags); if (ret) { - error_report("%s: Failed to setup VFIO migration region %d: %s", - vbasedev->name, info->index, strerror(-ret)); - goto err; + return ret; } - if (!vbasedev->migration->region.size) { - error_report("%s: Invalid zero-sized VFIO migration region %d", - vbasedev->name, info->index); - ret = -EINVAL; - goto err; + /* Basic migration functionality must be supported */ + if (!(mig_flags & VFIO_MIGRATION_STOP_COPY)) { + return -EOPNOTSUPP; } + vbasedev->migration = g_new0(VFIOMigration, 1); migration = vbasedev->migration; migration->vbasedev = vbasedev; + migration->device_state = VFIO_DEVICE_STATE_RUNNING; + migration->data_fd = -1; + migration->mig_flags = mig_flags; + + vbasedev->dirty_pages_supported = vfio_dma_logging_supported(vbasedev); oid = vmstate_if_get_id(VMSTATE_IF(DEVICE(obj))); if (oid) { @@ -836,16 +853,39 @@ static int vfio_migration_init(VFIODevice *vbasedev, register_savevm_live(id, VMSTATE_INSTANCE_ID_ANY, 1, &savevm_vfio_handlers, vbasedev); - migration->vm_state = qdev_add_vm_change_state_handler(vbasedev->dev, - vfio_vmstate_change, - vbasedev); - migration->migration_state.notify = vfio_migration_state_notifier; - add_migration_state_change_notifier(&migration->migration_state); - return 0; + prepare_cb = migration->mig_flags & VFIO_MIGRATION_P2P ? + vfio_vmstate_change_prepare : + NULL; + migration->vm_state = qdev_add_vm_change_state_handler_full( + vbasedev->dev, vfio_vmstate_change, prepare_cb, vbasedev); + migration_add_notifier(&migration->migration_state, + vfio_migration_state_notifier); -err: - vfio_migration_exit(vbasedev); - return ret; + return 0; +} + +static void vfio_migration_deinit(VFIODevice *vbasedev) +{ + VFIOMigration *migration = vbasedev->migration; + + migration_remove_notifier(&migration->migration_state); + qemu_del_vm_change_state_handler(migration->vm_state); + unregister_savevm(VMSTATE_IF(vbasedev->dev), "vfio", vbasedev); + vfio_migration_free(vbasedev); + vfio_unblock_multiple_devices_migration(); +} + +static int vfio_block_migration(VFIODevice *vbasedev, Error *err, Error **errp) +{ + if (vbasedev->enable_migration == ON_OFF_AUTO_ON) { + error_propagate(errp, err); + return -EINVAL; + } + + vbasedev->migration_blocker = error_copy(err); + error_free(err); + + return migrate_add_blocker_normal(&vbasedev->migration_blocker, errp); } /* ---------------------------------------------------------------------- */ @@ -855,60 +895,82 @@ int64_t vfio_mig_bytes_transferred(void) return bytes_transferred; } -int vfio_migration_probe(VFIODevice *vbasedev, Error **errp) +void vfio_reset_bytes_transferred(void) { - VFIOContainer *container = vbasedev->group->container; - struct vfio_region_info *info = NULL; - int ret = -ENOTSUP; + bytes_transferred = 0; +} - if (!vbasedev->enable_migration || !container->dirty_pages_supported) { - goto add_blocker; +/* + * Return true when either migration initialized or blocker registered. + * Currently only return false when adding blocker fails which will + * de-register vfio device. + */ +bool vfio_migration_realize(VFIODevice *vbasedev, Error **errp) +{ + Error *err = NULL; + int ret; + + if (vbasedev->enable_migration == ON_OFF_AUTO_OFF) { + error_setg(&err, "%s: Migration is disabled for VFIO device", + vbasedev->name); + return !vfio_block_migration(vbasedev, err, errp); } - ret = vfio_get_dev_region_info(vbasedev, - VFIO_REGION_TYPE_MIGRATION_DEPRECATED, - VFIO_REGION_SUBTYPE_MIGRATION_DEPRECATED, - &info); + ret = vfio_migration_init(vbasedev); if (ret) { - goto add_blocker; + if (ret == -ENOTTY) { + error_setg(&err, "%s: VFIO migration is not supported in kernel", + vbasedev->name); + } else { + error_setg(&err, + "%s: Migration couldn't be initialized for VFIO device, " + "err: %d (%s)", + vbasedev->name, ret, strerror(-ret)); + } + + return !vfio_block_migration(vbasedev, err, errp); } - ret = vfio_migration_init(vbasedev, info); + if (!vbasedev->dirty_pages_supported) { + if (vbasedev->enable_migration == ON_OFF_AUTO_AUTO) { + error_setg(&err, + "%s: VFIO device doesn't support device dirty tracking", + vbasedev->name); + goto add_blocker; + } + + warn_report("%s: VFIO device doesn't support device dirty tracking", + vbasedev->name); + } + + ret = vfio_block_multiple_devices_migration(vbasedev, errp); if (ret) { + goto out_deinit; + } + + if (vfio_viommu_preset(vbasedev)) { + error_setg(&err, "%s: Migration is currently not supported " + "with vIOMMU enabled", vbasedev->name); goto add_blocker; } - trace_vfio_migration_probe(vbasedev->name, info->index); - g_free(info); - return 0; + trace_vfio_migration_realize(vbasedev->name); + return true; add_blocker: - error_setg(&vbasedev->migration_blocker, - "VFIO device doesn't support migration"); - g_free(info); - - ret = migrate_add_blocker(vbasedev->migration_blocker, errp); - if (ret < 0) { - error_free(vbasedev->migration_blocker); - vbasedev->migration_blocker = NULL; + ret = vfio_block_migration(vbasedev, err, errp); +out_deinit: + if (ret) { + vfio_migration_deinit(vbasedev); } - return ret; + return !ret; } -void vfio_migration_finalize(VFIODevice *vbasedev) +void vfio_migration_exit(VFIODevice *vbasedev) { if (vbasedev->migration) { - VFIOMigration *migration = vbasedev->migration; - - remove_migration_state_change_notifier(&migration->migration_state); - qemu_del_vm_change_state_handler(migration->vm_state); - unregister_savevm(VMSTATE_IF(vbasedev->dev), "vfio", vbasedev); - vfio_migration_exit(vbasedev); + vfio_migration_deinit(vbasedev); } - if (vbasedev->migration_blocker) { - migrate_del_blocker(vbasedev->migration_blocker); - error_free(vbasedev->migration_blocker); - vbasedev->migration_blocker = NULL; - } + migrate_del_blocker(&vbasedev->migration_blocker); } diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index f0147a050a..496fd1ee86 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1490,6 +1490,9 @@ void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev) * +---------------------------------+---------------------------------+ * * https://lists.gnu.org/archive/html/qemu-devel/2017-08/pdfUda5iEpgOS.pdf + * + * Specification for Turning and later GPU architectures: + * https://lists.gnu.org/archive/html/qemu-devel/2023-06/pdf142OR4O4c2.pdf */ static void get_nv_gpudirect_clique_id(Object *obj, Visitor *v, const char *name, void *opaque, @@ -1527,10 +1530,19 @@ const PropertyInfo qdev_prop_nv_gpudirect_clique = { .set = set_nv_gpudirect_clique_id, }; +static bool is_valid_std_cap_offset(uint8_t pos) +{ + return (pos >= PCI_STD_HEADER_SIZEOF && + pos <= (PCI_CFG_SPACE_SIZE - PCI_CAP_SIZEOF)); +} + static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) { + ERRP_GUARD(); PCIDevice *pdev = &vdev->pdev; - int ret, pos = 0xC8; + int ret, pos; + bool c8_conflict = false, d4_conflict = false; + uint8_t tmp; if (vdev->nv_gpudirect_clique == 0xFF) { return 0; @@ -1547,6 +1559,40 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) return -EINVAL; } + /* + * Per the updated specification above, it's recommended to use offset + * D4h for Turing and later GPU architectures due to a conflict of the + * MSI-X capability at C8h. We don't know how to determine the GPU + * architecture, instead we walk the capability chain to mark conflicts + * and choose one or error based on the result. + * + * NB. Cap list head in pdev->config is already cleared, read from device. + */ + ret = pread(vdev->vbasedev.fd, &tmp, 1, + vdev->config_offset + PCI_CAPABILITY_LIST); + if (ret != 1 || !is_valid_std_cap_offset(tmp)) { + error_setg(errp, "NVIDIA GPUDirect Clique ID: error getting cap list"); + return -EINVAL; + } + + do { + if (tmp == 0xC8) { + c8_conflict = true; + } else if (tmp == 0xD4) { + d4_conflict = true; + } + tmp = pdev->config[tmp + PCI_CAP_LIST_NEXT]; + } while (is_valid_std_cap_offset(tmp)); + + if (!c8_conflict) { + pos = 0xC8; + } else if (!d4_conflict) { + pos = 0xD4; + } else { + error_setg(errp, "NVIDIA GPUDirect Clique ID: invalid config space"); + return -EINVAL; + } + ret = pci_add_capability(pdev, PCI_CAP_ID_VNDR, pos, 8, errp); if (ret < 0) { error_prepend(errp, "Failed to add NVIDIA GPUDirect cap: "); @@ -1565,121 +1611,6 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) return 0; } -int vfio_pci_nvidia_v100_ram_init(VFIOPCIDevice *vdev, Error **errp) -{ - int ret; - void *p; - struct vfio_region_info *nv2reg = NULL; - struct vfio_info_cap_header *hdr; - struct vfio_region_info_cap_nvlink2_ssatgt *cap; - VFIOQuirk *quirk; - - ret = vfio_get_dev_region_info(&vdev->vbasedev, - VFIO_REGION_TYPE_PCI_VENDOR_TYPE | - PCI_VENDOR_ID_NVIDIA, - VFIO_REGION_SUBTYPE_NVIDIA_NVLINK2_RAM, - &nv2reg); - if (ret) { - return ret; - } - - hdr = vfio_get_region_info_cap(nv2reg, VFIO_REGION_INFO_CAP_NVLINK2_SSATGT); - if (!hdr) { - ret = -ENODEV; - goto free_exit; - } - cap = (void *) hdr; - - p = mmap(NULL, nv2reg->size, PROT_READ | PROT_WRITE, - MAP_SHARED, vdev->vbasedev.fd, nv2reg->offset); - if (p == MAP_FAILED) { - ret = -errno; - goto free_exit; - } - - quirk = vfio_quirk_alloc(1); - memory_region_init_ram_ptr(&quirk->mem[0], OBJECT(vdev), "nvlink2-mr", - nv2reg->size, p); - QLIST_INSERT_HEAD(&vdev->bars[0].quirks, quirk, next); - - object_property_add_uint64_ptr(OBJECT(vdev), "nvlink2-tgt", - (uint64_t *) &cap->tgt, - OBJ_PROP_FLAG_READ); - trace_vfio_pci_nvidia_gpu_setup_quirk(vdev->vbasedev.name, cap->tgt, - nv2reg->size); -free_exit: - g_free(nv2reg); - - return ret; -} - -int vfio_pci_nvlink2_init(VFIOPCIDevice *vdev, Error **errp) -{ - int ret; - void *p; - struct vfio_region_info *atsdreg = NULL; - struct vfio_info_cap_header *hdr; - struct vfio_region_info_cap_nvlink2_ssatgt *captgt; - struct vfio_region_info_cap_nvlink2_lnkspd *capspeed; - VFIOQuirk *quirk; - - ret = vfio_get_dev_region_info(&vdev->vbasedev, - VFIO_REGION_TYPE_PCI_VENDOR_TYPE | - PCI_VENDOR_ID_IBM, - VFIO_REGION_SUBTYPE_IBM_NVLINK2_ATSD, - &atsdreg); - if (ret) { - return ret; - } - - hdr = vfio_get_region_info_cap(atsdreg, - VFIO_REGION_INFO_CAP_NVLINK2_SSATGT); - if (!hdr) { - ret = -ENODEV; - goto free_exit; - } - captgt = (void *) hdr; - - hdr = vfio_get_region_info_cap(atsdreg, - VFIO_REGION_INFO_CAP_NVLINK2_LNKSPD); - if (!hdr) { - ret = -ENODEV; - goto free_exit; - } - capspeed = (void *) hdr; - - /* Some NVLink bridges may not have assigned ATSD */ - if (atsdreg->size) { - p = mmap(NULL, atsdreg->size, PROT_READ | PROT_WRITE, - MAP_SHARED, vdev->vbasedev.fd, atsdreg->offset); - if (p == MAP_FAILED) { - ret = -errno; - goto free_exit; - } - - quirk = vfio_quirk_alloc(1); - memory_region_init_ram_device_ptr(&quirk->mem[0], OBJECT(vdev), - "nvlink2-atsd-mr", atsdreg->size, p); - QLIST_INSERT_HEAD(&vdev->bars[0].quirks, quirk, next); - } - - object_property_add_uint64_ptr(OBJECT(vdev), "nvlink2-tgt", - (uint64_t *) &captgt->tgt, - OBJ_PROP_FLAG_READ); - trace_vfio_pci_nvlink2_setup_quirk_ssatgt(vdev->vbasedev.name, captgt->tgt, - atsdreg->size); - - object_property_add_uint32_ptr(OBJECT(vdev), "nvlink2-link-speed", - &capspeed->link_speed, - OBJ_PROP_FLAG_READ); - trace_vfio_pci_nvlink2_setup_quirk_lnkspd(vdev->vbasedev.name, - capspeed->link_speed); -free_exit: - g_free(atsdreg); - - return ret; -} - /* * The VMD endpoint provides a real PCIe domain to the guest and the guest * kernel performs enumeration of the VMD sub-device domain. Guest transactions @@ -1700,6 +1631,7 @@ free_exit: #define VMD_SHADOW_CAP_LEN 24 static int vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) { + ERRP_GUARD(); uint8_t membar_phys[16]; int ret, pos = 0xE8; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 939dcc3d4a..64780d1b79 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include CONFIG_DEVICES /* CONFIG_IOMMUFD */ #include #include @@ -42,6 +43,7 @@ #include "qapi/error.h" #include "migration/blocker.h" #include "migration/qemu-file.h" +#include "sysemu/iommufd.h" #define TYPE_VFIO_PCI_NOHOTPLUG "vfio-pci-nohotplug" @@ -369,12 +371,56 @@ static void vfio_msi_interrupt(void *opaque) notify(&vdev->pdev, nr); } +/* + * Get MSI-X enabled, but no vector enabled, by setting vector 0 with an invalid + * fd to kernel. + */ +static int vfio_enable_msix_no_vec(VFIOPCIDevice *vdev) +{ + g_autofree struct vfio_irq_set *irq_set = NULL; + int ret = 0, argsz; + int32_t *fd; + + argsz = sizeof(*irq_set) + sizeof(*fd); + + irq_set = g_malloc0(argsz); + irq_set->argsz = argsz; + irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | + VFIO_IRQ_SET_ACTION_TRIGGER; + irq_set->index = VFIO_PCI_MSIX_IRQ_INDEX; + irq_set->start = 0; + irq_set->count = 1; + fd = (int32_t *)&irq_set->data; + *fd = -1; + + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set); + + return ret; +} + static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) { struct vfio_irq_set *irq_set; int ret = 0, i, argsz; int32_t *fds; + /* + * If dynamic MSI-X allocation is supported, the vectors to be allocated + * and enabled can be scattered. Before kernel enabling MSI-X, setting + * nr_vectors causes all these vectors to be allocated on host. + * + * To keep allocation as needed, use vector 0 with an invalid fd to get + * MSI-X enabled first, then set vectors with a potentially sparse set of + * eventfds to enable interrupts only when enabled in guest. + */ + if (msix && !vdev->msix->noresize) { + ret = vfio_enable_msix_no_vec(vdev); + + if (ret) { + return ret; + } + } + argsz = sizeof(*irq_set) + (vdev->nr_vectors * sizeof(*fds)); irq_set = g_malloc0(argsz); @@ -470,6 +516,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, VFIOPCIDevice *vdev = VFIO_PCI(pdev); VFIOMSIVector *vector; int ret; + bool resizing = !!(vdev->nr_vectors < nr + 1); trace_vfio_msix_vector_do_use(vdev->vbasedev.name, nr); @@ -512,33 +559,42 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, } /* - * We don't want to have the host allocate all possible MSI vectors - * for a device if they're not in use, so we shutdown and incrementally - * increase them as needed. + * When dynamic allocation is not supported, we don't want to have the + * host allocate all possible MSI vectors for a device if they're not + * in use, so we shutdown and incrementally increase them as needed. + * nr_vectors represents the total number of vectors allocated. + * + * When dynamic allocation is supported, let the host only allocate + * and enable a vector when it is in use in guest. nr_vectors represents + * the upper bound of vectors being enabled (but not all of the ranges + * is allocated or enabled). */ - if (vdev->nr_vectors < nr + 1) { + if (resizing) { vdev->nr_vectors = nr + 1; - if (!vdev->defer_kvm_irq_routing) { + } + + if (!vdev->defer_kvm_irq_routing) { + if (vdev->msix->noresize && resizing) { vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); ret = vfio_enable_vectors(vdev, true); if (ret) { error_report("vfio: failed to enable vectors, %d", ret); } - } - } else { - Error *err = NULL; - int32_t fd; - - if (vector->virq >= 0) { - fd = event_notifier_get_fd(&vector->kvm_interrupt); } else { - fd = event_notifier_get_fd(&vector->interrupt); - } + Error *err = NULL; + int32_t fd; - if (vfio_set_irq_signaling(&vdev->vbasedev, - VFIO_PCI_MSIX_IRQ_INDEX, nr, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { - error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); + if (vector->virq >= 0) { + fd = event_notifier_get_fd(&vector->kvm_interrupt); + } else { + fd = event_notifier_get_fd(&vector->interrupt); + } + + if (vfio_set_irq_signaling(&vdev->vbasedev, + VFIO_PCI_MSIX_IRQ_INDEX, nr, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { + error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); + } } } @@ -608,6 +664,8 @@ static void vfio_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev) static void vfio_msix_enable(VFIOPCIDevice *vdev) { + int ret; + vfio_disable_interrupts(vdev); vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->msix->entries); @@ -630,8 +688,6 @@ static void vfio_msix_enable(VFIOPCIDevice *vdev) vfio_commit_kvm_msi_virq_batch(vdev); if (vdev->nr_vectors) { - int ret; - ret = vfio_enable_vectors(vdev, true); if (ret) { error_report("vfio: failed to enable vectors, %d", ret); @@ -645,13 +701,14 @@ static void vfio_msix_enable(VFIOPCIDevice *vdev) * MSI-X capability, but leaves the vector table masked. We therefore * can't rely on a vector_use callback (from request_irq() in the guest) * to switch the physical device into MSI-X mode because that may come a - * long time after pci_enable_msix(). This code enables vector 0 with - * triggering to userspace, then immediately release the vector, leaving - * the physical device with no vectors enabled, but MSI-X enabled, just - * like the guest view. + * long time after pci_enable_msix(). This code sets vector 0 with an + * invalid fd to make the physical device MSI-X enabled, but with no + * vectors enabled, just like the guest view. */ - vfio_msix_vector_do_use(&vdev->pdev, 0, NULL, NULL); - vfio_msix_vector_release(&vdev->pdev, 0); + ret = vfio_enable_msix_no_vec(vdev); + if (ret) { + error_report("vfio: failed to enable MSI-X, %d", ret); + } } trace_vfio_msix_enable(vdev->vbasedev.name); @@ -663,6 +720,8 @@ static void vfio_msi_enable(VFIOPCIDevice *vdev) vfio_disable_interrupts(vdev); + vdev->nr_vectors = msi_nr_vectors_allocated(&vdev->pdev); +retry: /* * Setting vector notifiers needs to enable route for each vector. * Deferring to commit the KVM routes once rather than per vector @@ -670,8 +729,6 @@ static void vfio_msi_enable(VFIOPCIDevice *vdev) */ vfio_prepare_kvm_msi_virq_batch(vdev); - vdev->nr_vectors = msi_nr_vectors_allocated(&vdev->pdev); -retry: vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->nr_vectors); for (i = 0; i < vdev->nr_vectors; i++) { @@ -769,9 +826,11 @@ static void vfio_msix_disable(VFIOPCIDevice *vdev) } } - if (vdev->nr_vectors) { - vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); - } + /* + * Always clear MSI-X IRQ index. A PF device could have enabled + * MSI-X with no vectors. See vfio_msix_enable(). + */ + vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); vfio_msi_disable_common(vdev); vfio_intx_enable(vdev, &err); @@ -1493,7 +1552,9 @@ static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) uint8_t pos; uint16_t ctrl; uint32_t table, pba; - int fd = vdev->vbasedev.fd; + int ret, fd = vdev->vbasedev.fd; + struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info), + .index = VFIO_PCI_MSIX_IRQ_INDEX }; VFIOMSIXInfo *msix; pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX); @@ -1530,6 +1591,15 @@ static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) msix->pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK; msix->entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1; + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info); + if (ret < 0) { + error_setg_errno(errp, -ret, "failed to get MSI-X irq info"); + g_free(msix); + return; + } + + msix->noresize = !!(irq_info.flags & VFIO_IRQ_INFO_NORESIZE); + /* * Test the size of the pba_offset variable and catch if it extends outside * of the specified BAR. If it is the case, we need to apply a hardware @@ -1562,7 +1632,8 @@ static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) } trace_vfio_msix_early_setup(vdev->vbasedev.name, pos, msix->table_bar, - msix->table_offset, msix->entries); + msix->table_offset, msix->entries, + msix->noresize); vdev->msix = msix; vfio_pci_fixup_msix_region(vdev); @@ -1752,9 +1823,11 @@ static void vfio_bars_finalize(VFIOPCIDevice *vdev) vfio_bar_quirk_finalize(vdev, i); vfio_region_finalize(&bar->region); - if (bar->size) { + if (bar->mr) { + assert(bar->size); object_unparent(OBJECT(bar->mr)); g_free(bar->mr); + bar->mr = NULL; } } @@ -1826,6 +1899,81 @@ static void vfio_add_emulated_long(VFIOPCIDevice *vdev, int pos, vfio_set_long_bits(vdev->emulated_config_bits + pos, mask, mask); } +static void vfio_pci_enable_rp_atomics(VFIOPCIDevice *vdev) +{ + struct vfio_device_info_cap_pci_atomic_comp *cap; + g_autofree struct vfio_device_info *info = NULL; + PCIBus *bus = pci_get_bus(&vdev->pdev); + PCIDevice *parent = bus->parent_dev; + struct vfio_info_cap_header *hdr; + uint32_t mask = 0; + uint8_t *pos; + + /* + * PCIe Atomic Ops completer support is only added automatically for single + * function devices downstream of a root port supporting DEVCAP2. Support + * is added during realize and, if added, removed during device exit. The + * single function requirement avoids conflicting requirements should a + * slot be composed of multiple devices with differing capabilities. + */ + if (pci_bus_is_root(bus) || !parent || !parent->exp.exp_cap || + pcie_cap_get_type(parent) != PCI_EXP_TYPE_ROOT_PORT || + pcie_cap_get_version(parent) != PCI_EXP_FLAGS_VER2 || + vdev->pdev.devfn || + vdev->pdev.cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + return; + } + + pos = parent->config + parent->exp.exp_cap + PCI_EXP_DEVCAP2; + + /* Abort if there'a already an Atomic Ops configuration on the root port */ + if (pci_get_long(pos) & (PCI_EXP_DEVCAP2_ATOMIC_COMP32 | + PCI_EXP_DEVCAP2_ATOMIC_COMP64 | + PCI_EXP_DEVCAP2_ATOMIC_COMP128)) { + return; + } + + info = vfio_get_device_info(vdev->vbasedev.fd); + if (!info) { + return; + } + + hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_PCI_ATOMIC_COMP); + if (!hdr) { + return; + } + + cap = (void *)hdr; + if (cap->flags & VFIO_PCI_ATOMIC_COMP32) { + mask |= PCI_EXP_DEVCAP2_ATOMIC_COMP32; + } + if (cap->flags & VFIO_PCI_ATOMIC_COMP64) { + mask |= PCI_EXP_DEVCAP2_ATOMIC_COMP64; + } + if (cap->flags & VFIO_PCI_ATOMIC_COMP128) { + mask |= PCI_EXP_DEVCAP2_ATOMIC_COMP128; + } + + if (!mask) { + return; + } + + pci_long_test_and_set_mask(pos, mask); + vdev->clear_parent_atomics_on_exit = true; +} + +static void vfio_pci_disable_rp_atomics(VFIOPCIDevice *vdev) +{ + if (vdev->clear_parent_atomics_on_exit) { + PCIDevice *parent = pci_get_bus(&vdev->pdev)->parent_dev; + uint8_t *pos = parent->config + parent->exp.exp_cap + PCI_EXP_DEVCAP2; + + pci_long_test_and_clear_mask(pos, PCI_EXP_DEVCAP2_ATOMIC_COMP32 | + PCI_EXP_DEVCAP2_ATOMIC_COMP64 | + PCI_EXP_DEVCAP2_ATOMIC_COMP128); + } +} + static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, Error **errp) { @@ -1929,6 +2077,8 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, QEMU_PCI_EXP_LNKCAP_MLS(QEMU_PCI_EXP_LNK_2_5GT), ~0); vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKCTL, 0, ~0); } + + vfio_pci_enable_rp_atomics(vdev); } /* @@ -1986,6 +2136,7 @@ static void vfio_check_af_flr(VFIOPCIDevice *vdev, uint8_t pos) static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) { + ERRP_GUARD(); PCIDevice *pdev = &vdev->pdev; uint8_t cap_id, next, size; int ret; @@ -2066,6 +2217,54 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) return 0; } +static int vfio_setup_rebar_ecap(VFIOPCIDevice *vdev, uint16_t pos) +{ + uint32_t ctrl; + int i, nbar; + + ctrl = pci_get_long(vdev->pdev.config + pos + PCI_REBAR_CTRL); + nbar = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >> PCI_REBAR_CTRL_NBAR_SHIFT; + + for (i = 0; i < nbar; i++) { + uint32_t cap; + int size; + + ctrl = pci_get_long(vdev->pdev.config + pos + PCI_REBAR_CTRL + (i * 8)); + size = (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> PCI_REBAR_CTRL_BAR_SHIFT; + + /* The cap register reports sizes 1MB to 128TB, with 4 reserved bits */ + cap = size <= 27 ? 1U << (size + 4) : 0; + + /* + * The PCIe spec (v6.0.1, 7.8.6) requires HW to support at least one + * size in the range 1MB to 512GB. We intend to mask all sizes except + * the one currently enabled in the size field, therefore if it's + * outside the range, hide the whole capability as this virtualization + * trick won't work. If >512GB resizable BARs start to appear, we + * might need an opt-in or reservation scheme in the kernel. + */ + if (!(cap & PCI_REBAR_CAP_SIZES)) { + return -EINVAL; + } + + /* Hide all sizes reported in the ctrl reg per above requirement. */ + ctrl &= (PCI_REBAR_CTRL_BAR_SIZE | + PCI_REBAR_CTRL_NBAR_MASK | + PCI_REBAR_CTRL_BAR_IDX); + + /* + * The BAR size field is RW, however we've mangled the capability + * register such that we only report a single size, ie. the current + * BAR size. A write of an unsupported value is undefined, therefore + * the register field is essentially RO. + */ + vfio_add_emulated_long(vdev, pos + PCI_REBAR_CAP + (i * 8), cap, ~0); + vfio_add_emulated_long(vdev, pos + PCI_REBAR_CTRL + (i * 8), ctrl, ~0); + } + + return 0; +} + static void vfio_add_ext_cap(VFIOPCIDevice *vdev) { PCIDevice *pdev = &vdev->pdev; @@ -2139,9 +2338,13 @@ static void vfio_add_ext_cap(VFIOPCIDevice *vdev) case 0: /* kernel masked capability */ case PCI_EXT_CAP_ID_SRIOV: /* Read-only VF BARs confuse OVMF */ case PCI_EXT_CAP_ID_ARI: /* XXX Needs next function virtualization */ - case PCI_EXT_CAP_ID_REBAR: /* Can't expose read-only */ trace_vfio_add_ext_cap_dropped(vdev->vbasedev.name, cap_id, next); break; + case PCI_EXT_CAP_ID_REBAR: + if (!vfio_setup_rebar_ecap(vdev, next)) { + pcie_add_capability(pdev, cap_id, cap_ver, next, size); + } + break; default: pcie_add_capability(pdev, cap_id, cap_ver, next, size); } @@ -2176,7 +2379,7 @@ static int vfio_add_capabilities(VFIOPCIDevice *vdev, Error **errp) return 0; } -static void vfio_pci_pre_reset(VFIOPCIDevice *vdev) +void vfio_pci_pre_reset(VFIOPCIDevice *vdev) { PCIDevice *pdev = &vdev->pdev; uint16_t cmd; @@ -2213,7 +2416,7 @@ static void vfio_pci_pre_reset(VFIOPCIDevice *vdev) vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2); } -static void vfio_pci_post_reset(VFIOPCIDevice *vdev) +void vfio_pci_post_reset(VFIOPCIDevice *vdev) { Error *err = NULL; int nr; @@ -2237,7 +2440,7 @@ static void vfio_pci_post_reset(VFIOPCIDevice *vdev) vfio_quirk_reset(vdev); } -static bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name) +bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name) { char tmp[13]; @@ -2247,22 +2450,13 @@ static bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name) return (strcmp(tmp, name) == 0); } -static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single) +int vfio_pci_get_pci_hot_reset_info(VFIOPCIDevice *vdev, + struct vfio_pci_hot_reset_info **info_p) { - VFIOGroup *group; struct vfio_pci_hot_reset_info *info; - struct vfio_pci_dependent_device *devices; - struct vfio_pci_hot_reset *reset; - int32_t *fds; - int ret, i, count; - bool multi = false; + int ret, count; - trace_vfio_pci_hot_reset(vdev->vbasedev.name, single ? "one" : "multi"); - - if (!single) { - vfio_pci_pre_reset(vdev); - } - vdev->vbasedev.needs_reset = false; + assert(info_p && !*info_p); info = g_malloc0(sizeof(*info)); info->argsz = sizeof(*info); @@ -2270,163 +2464,36 @@ static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single) ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, info); if (ret && errno != ENOSPC) { ret = -errno; + g_free(info); if (!vdev->has_pm_reset) { error_report("vfio: Cannot reset device %s, " "no available reset mechanism.", vdev->vbasedev.name); } - goto out_single; + return ret; } count = info->count; - info = g_realloc(info, sizeof(*info) + (count * sizeof(*devices))); - info->argsz = sizeof(*info) + (count * sizeof(*devices)); - devices = &info->devices[0]; + info = g_realloc(info, sizeof(*info) + (count * sizeof(info->devices[0]))); + info->argsz = sizeof(*info) + (count * sizeof(info->devices[0])); ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, info); if (ret) { ret = -errno; + g_free(info); error_report("vfio: hot reset info failed: %m"); - goto out_single; + return ret; } - trace_vfio_pci_hot_reset_has_dep_devices(vdev->vbasedev.name); + *info_p = info; + return 0; +} - /* Verify that we have all the groups required */ - for (i = 0; i < info->count; i++) { - PCIHostDeviceAddress host; - VFIOPCIDevice *tmp; - VFIODevice *vbasedev_iter; +static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single) +{ + VFIODevice *vbasedev = &vdev->vbasedev; + const VFIOIOMMUClass *ops = vbasedev->bcontainer->ops; - host.domain = devices[i].segment; - host.bus = devices[i].bus; - host.slot = PCI_SLOT(devices[i].devfn); - host.function = PCI_FUNC(devices[i].devfn); - - trace_vfio_pci_hot_reset_dep_devices(host.domain, - host.bus, host.slot, host.function, devices[i].group_id); - - if (vfio_pci_host_match(&host, vdev->vbasedev.name)) { - continue; - } - - QLIST_FOREACH(group, &vfio_group_list, next) { - if (group->groupid == devices[i].group_id) { - break; - } - } - - if (!group) { - if (!vdev->has_pm_reset) { - error_report("vfio: Cannot reset device %s, " - "depends on group %d which is not owned.", - vdev->vbasedev.name, devices[i].group_id); - } - ret = -EPERM; - goto out; - } - - /* Prep dependent devices for reset and clear our marker. */ - QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { - if (!vbasedev_iter->dev->realized || - vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { - continue; - } - tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); - if (vfio_pci_host_match(&host, tmp->vbasedev.name)) { - if (single) { - ret = -EINVAL; - goto out_single; - } - vfio_pci_pre_reset(tmp); - tmp->vbasedev.needs_reset = false; - multi = true; - break; - } - } - } - - if (!single && !multi) { - ret = -EINVAL; - goto out_single; - } - - /* Determine how many group fds need to be passed */ - count = 0; - QLIST_FOREACH(group, &vfio_group_list, next) { - for (i = 0; i < info->count; i++) { - if (group->groupid == devices[i].group_id) { - count++; - break; - } - } - } - - reset = g_malloc0(sizeof(*reset) + (count * sizeof(*fds))); - reset->argsz = sizeof(*reset) + (count * sizeof(*fds)); - fds = &reset->group_fds[0]; - - /* Fill in group fds */ - QLIST_FOREACH(group, &vfio_group_list, next) { - for (i = 0; i < info->count; i++) { - if (group->groupid == devices[i].group_id) { - fds[reset->count++] = group->fd; - break; - } - } - } - - /* Bus reset! */ - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_PCI_HOT_RESET, reset); - g_free(reset); - - trace_vfio_pci_hot_reset_result(vdev->vbasedev.name, - ret ? strerror(errno) : "Success"); - -out: - /* Re-enable INTx on affected devices */ - for (i = 0; i < info->count; i++) { - PCIHostDeviceAddress host; - VFIOPCIDevice *tmp; - VFIODevice *vbasedev_iter; - - host.domain = devices[i].segment; - host.bus = devices[i].bus; - host.slot = PCI_SLOT(devices[i].devfn); - host.function = PCI_FUNC(devices[i].devfn); - - if (vfio_pci_host_match(&host, vdev->vbasedev.name)) { - continue; - } - - QLIST_FOREACH(group, &vfio_group_list, next) { - if (group->groupid == devices[i].group_id) { - break; - } - } - - if (!group) { - break; - } - - QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { - if (!vbasedev_iter->dev->realized || - vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { - continue; - } - tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); - if (vfio_pci_host_match(&host, tmp->vbasedev.name)) { - vfio_pci_post_reset(tmp); - break; - } - } - } -out_single: - if (!single) { - vfio_pci_post_reset(vdev); - } - g_free(info); - - return ret; + return ops->pci_hot_reset(vbasedev, single); } /* @@ -2477,14 +2544,45 @@ static bool vfio_msix_present(void *opaque, int version_id) return msix_present(pdev); } -const VMStateDescription vmstate_vfio_pci_config = { +static bool vfio_display_migration_needed(void *opaque) +{ + VFIOPCIDevice *vdev = opaque; + + /* + * We need to migrate the VFIODisplay object if ramfb *migration* was + * explicitly requested (in which case we enforced both ramfb=on and + * display=on), or ramfb migration was left at the default "auto" + * setting, and *ramfb* was explicitly requested (in which case we + * enforced display=on). + */ + return vdev->ramfb_migrate == ON_OFF_AUTO_ON || + (vdev->ramfb_migrate == ON_OFF_AUTO_AUTO && vdev->enable_ramfb); +} + +static const VMStateDescription vmstate_vfio_display = { + .name = "VFIOPCIDevice/VFIODisplay", + .version_id = 1, + .minimum_version_id = 1, + .needed = vfio_display_migration_needed, + .fields = (const VMStateField[]){ + VMSTATE_STRUCT_POINTER(dpy, VFIOPCIDevice, vfio_display_vmstate, + VFIODisplay), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_vfio_pci_config = { .name = "VFIOPCIDevice", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(pdev, VFIOPCIDevice), VMSTATE_MSIX_TEST(pdev, VFIOPCIDevice, vfio_msix_present), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_vfio_display, + NULL } }; @@ -2695,12 +2793,12 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) } } -static void vfio_put_device(VFIOPCIDevice *vdev) +static void vfio_pci_put_device(VFIOPCIDevice *vdev) { + vfio_detach_device(&vdev->vbasedev); + g_free(vdev->vbasedev.name); g_free(vdev->msix); - - vfio_put_base_device(&vdev->vbasedev); } static void vfio_err_notifier_handler(void *opaque) @@ -2845,23 +2943,24 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) static void vfio_realize(PCIDevice *pdev, Error **errp) { + ERRP_GUARD(); VFIOPCIDevice *vdev = VFIO_PCI(pdev); VFIODevice *vbasedev = &vdev->vbasedev; - VFIODevice *vbasedev_iter; - VFIOGroup *group; - char *tmp, *subsys, group_path[PATH_MAX], *group_name; + char *tmp, *subsys; Error *err = NULL; - ssize_t len; - struct stat st; - int groupid; int i, ret; bool is_mdev; + char uuid[UUID_STR_LEN]; + char *name; - if (!vbasedev->sysfsdev) { + if (vbasedev->fd < 0 && !vbasedev->sysfsdev) { if (!(~vdev->host.domain || ~vdev->host.bus || ~vdev->host.slot || ~vdev->host.function)) { error_setg(errp, "No provided host device"); error_append_hint(errp, "Use -device vfio-pci,host=DDDD:BB:DD.F " +#ifdef CONFIG_IOMMUFD + "or -device vfio-pci,fd=DEVICE_FD " +#endif "or -device vfio-pci,sysfsdev=PATH_TO_DEVICE\n"); return; } @@ -2871,50 +2970,10 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vdev->host.slot, vdev->host.function); } - if (stat(vbasedev->sysfsdev, &st) < 0) { - error_setg_errno(errp, errno, "no such host device"); - error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->sysfsdev); + if (vfio_device_get_name(vbasedev, errp) < 0) { return; } - vbasedev->name = g_path_get_basename(vbasedev->sysfsdev); - vbasedev->ops = &vfio_pci_ops; - vbasedev->type = VFIO_DEVICE_TYPE_PCI; - vbasedev->dev = DEVICE(vdev); - - tmp = g_strdup_printf("%s/iommu_group", vbasedev->sysfsdev); - len = readlink(tmp, group_path, sizeof(group_path)); - g_free(tmp); - - if (len <= 0 || len >= sizeof(group_path)) { - error_setg_errno(errp, len < 0 ? errno : ENAMETOOLONG, - "no iommu_group found"); - goto error; - } - - group_path[len] = 0; - - group_name = basename(group_path); - if (sscanf(group_name, "%d", &groupid) != 1) { - error_setg_errno(errp, errno, "failed to read %s", group_path); - goto error; - } - - trace_vfio_realize(vbasedev->name, groupid); - - group = vfio_get_group(groupid, pci_device_iommu_address_space(pdev), errp); - if (!group) { - goto error; - } - - QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { - if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) { - error_setg(errp, "device is already attached"); - vfio_put_group(group); - goto error; - } - } - /* * Mediated devices *might* operate compatibly with discarding of RAM, but * we cannot know for certain, it depends on whether the mdev vendor driver @@ -2932,13 +2991,20 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) if (vbasedev->ram_block_discard_allowed && !is_mdev) { error_setg(errp, "x-balloon-allowed only potentially compatible " "with mdev devices"); - vfio_put_group(group); goto error; } - ret = vfio_get_device(group, vbasedev->name, vbasedev, errp); + if (!qemu_uuid_is_null(&vdev->vf_token)) { + qemu_uuid_unparse(&vdev->vf_token, uuid); + name = g_strdup_printf("%s vf_token=%s", vbasedev->name, uuid); + } else { + name = g_strdup(vbasedev->name); + } + + ret = vfio_attach_device(name, vbasedev, + pci_device_iommu_address_space(pdev), errp); + g_free(name); if (ret) { - vfio_put_group(group); goto error; } @@ -3130,24 +3196,23 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) } } - if (vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID)) { - ret = vfio_pci_nvidia_v100_ram_init(vdev, errp); - if (ret && ret != -ENODEV) { - error_report("Failed to setup NVIDIA V100 GPU RAM"); - } + if (vdev->ramfb_migrate == ON_OFF_AUTO_ON && !vdev->enable_ramfb) { + warn_report("x-ramfb-migrate=on but ramfb=off. " + "Forcing x-ramfb-migrate to off."); + vdev->ramfb_migrate = ON_OFF_AUTO_OFF; } - - if (vfio_pci_is(vdev, PCI_VENDOR_ID_IBM, PCI_ANY_ID)) { - ret = vfio_pci_nvlink2_init(vdev, errp); - if (ret && ret != -ENODEV) { - error_report("Failed to setup NVlink2 bridge"); + if (vbasedev->enable_migration == ON_OFF_AUTO_OFF) { + if (vdev->ramfb_migrate == ON_OFF_AUTO_AUTO) { + vdev->ramfb_migrate = ON_OFF_AUTO_OFF; + } else if (vdev->ramfb_migrate == ON_OFF_AUTO_ON) { + error_setg(errp, "x-ramfb-migrate requires enable-migration"); + goto out_deregister; } } if (!pdev->failover_pair_id) { - ret = vfio_migration_probe(vbasedev, errp); - if (ret) { - error_report("%s: Migration disabled", vbasedev->name); + if (!vfio_migration_realize(vbasedev, errp)) { + goto out_deregister; } } @@ -3158,8 +3223,16 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) return; out_deregister: + if (vdev->interrupt == VFIO_INT_INTx) { + vfio_intx_disable(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); + } + if (vdev->intx.mmap_timer) { + timer_free(vdev->intx.mmap_timer); + } out_teardown: vfio_teardown_msi(vdev); vfio_bars_exit(vdev); @@ -3170,7 +3243,6 @@ error: static void vfio_instance_finalize(Object *obj) { VFIOPCIDevice *vdev = VFIO_PCI(obj); - VFIOGroup *group = vdev->vbasedev.group; vfio_display_finalize(vdev); vfio_bars_finalize(vdev); @@ -3183,8 +3255,7 @@ static void vfio_instance_finalize(Object *obj) * * g_free(vdev->igd_opregion); */ - vfio_put_device(vdev); - vfio_put_group(group); + vfio_pci_put_device(vdev); } static void vfio_exitfn(PCIDevice *pdev) @@ -3202,8 +3273,9 @@ static void vfio_exitfn(PCIDevice *pdev) timer_free(vdev->intx.mmap_timer); } vfio_teardown_msi(vdev); + vfio_pci_disable_rp_atomics(vdev); vfio_bars_exit(vdev); - vfio_migration_finalize(&vdev->vbasedev); + vfio_migration_exit(&vdev->vbasedev); } static void vfio_pci_reset(DeviceState *dev) @@ -3249,6 +3321,7 @@ static void vfio_instance_init(Object *obj) { PCIDevice *pci_dev = PCI_DEVICE(obj); VFIOPCIDevice *vdev = VFIO_PCI(obj); + VFIODevice *vbasedev = &vdev->vbasedev; device_add_bootindex_property(obj, &vdev->bootindex, "bootindex", NULL, @@ -3258,6 +3331,9 @@ static void vfio_instance_init(Object *obj) vdev->host.slot = ~0U; vdev->host.function = ~0U; + vfio_device_init(vbasedev, VFIO_DEVICE_TYPE_PCI, &vfio_pci_ops, + DEVICE(vdev), false); + vdev->nv_gpudirect_clique = 0xFF; /* QEMU_PCI_CAP_EXPRESS initialization does not depend on QEMU command @@ -3267,6 +3343,7 @@ static void vfio_instance_init(Object *obj) static Property vfio_pci_dev_properties[] = { DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIOPCIDevice, host), + DEFINE_PROP_UUID_NODEFAULT("vf-token", VFIOPCIDevice, vf_token), DEFINE_PROP_STRING("sysfsdev", VFIOPCIDevice, vbasedev.sysfsdev), DEFINE_PROP_ON_OFF_AUTO("x-pre-copy-dirty-page-tracking", VFIOPCIDevice, vbasedev.pre_copy_dirty_page_tracking, @@ -3283,8 +3360,8 @@ static Property vfio_pci_dev_properties[] = { VFIO_FEATURE_ENABLE_REQ_BIT, true), DEFINE_PROP_BIT("x-igd-opregion", VFIOPCIDevice, features, VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false), - DEFINE_PROP_BOOL("x-enable-migration", VFIOPCIDevice, - vbasedev.enable_migration, false), + DEFINE_PROP_ON_OFF_AUTO("enable-migration", VFIOPCIDevice, + vbasedev.enable_migration, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false), DEFINE_PROP_BOOL("x-balloon-allowed", VFIOPCIDevice, vbasedev.ram_block_discard_allowed, false), @@ -3309,14 +3386,20 @@ static Property vfio_pci_dev_properties[] = { qdev_prop_nv_gpudirect_clique, uint8_t), DEFINE_PROP_OFF_AUTO_PCIBAR("x-msix-relocation", VFIOPCIDevice, msix_relo, OFF_AUTOPCIBAR_OFF), - /* - * TODO - support passed fds... is this necessary? - * DEFINE_PROP_STRING("vfiofd", VFIOPCIDevice, vfiofd_name), - * DEFINE_PROP_STRING("vfiogroupfd, VFIOPCIDevice, vfiogroupfd_name), - */ +#ifdef CONFIG_IOMMUFD + DEFINE_PROP_LINK("iommufd", VFIOPCIDevice, vbasedev.iommufd, + TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), +#endif DEFINE_PROP_END_OF_LIST(), }; +#ifdef CONFIG_IOMMUFD +static void vfio_pci_set_fd(Object *obj, const char *str, Error **errp) +{ + vfio_device_set_fd(&VFIO_PCI(obj)->vbasedev, str, errp); +} +#endif + static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -3324,6 +3407,9 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) dc->reset = vfio_pci_reset; device_class_set_props(dc, vfio_pci_dev_properties); +#ifdef CONFIG_IOMMUFD + object_class_property_add_str(klass, "fd", NULL, vfio_pci_set_fd); +#endif dc->desc = "VFIO-based PCI device assignment"; set_bit(DEVICE_CATEGORY_MISC, dc->categories); pdc->realize = vfio_realize; @@ -3348,6 +3434,8 @@ static const TypeInfo vfio_pci_dev_info = { static Property vfio_pci_dev_nohotplug_properties[] = { DEFINE_PROP_BOOL("ramfb", VFIOPCIDevice, enable_ramfb, false), + DEFINE_PROP_ON_OFF_AUTO("x-ramfb-migrate", VFIOPCIDevice, ramfb_migrate, + ON_OFF_AUTO_AUTO), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 7c236a52f4..6e64a2654e 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -13,7 +13,7 @@ #define HW_VFIO_VFIO_PCI_H #include "exec/memory.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/vfio/vfio-common.h" #include "qemu/event_notifier.h" #include "qemu/queue.h" @@ -113,6 +113,7 @@ typedef struct VFIOMSIXInfo { uint32_t table_offset; uint32_t pba_offset; unsigned long *pending; + bool noresize; } VFIOMSIXInfo; #define TYPE_VFIO_PCI "vfio-pci" @@ -137,6 +138,7 @@ struct VFIOPCIDevice { VFIOVGA *vga; /* 0xa0000, 0x3b0, 0x3c0 */ void *igd_opregion; PCIHostDeviceAddress host; + QemuUUID vf_token; EventNotifier err_notifier; EventNotifier req_notifier; int (*resetfn)(struct VFIOPCIDevice *); @@ -172,7 +174,9 @@ struct VFIOPCIDevice { bool no_kvm_ioeventfd; bool no_vfio_ioeventfd; bool enable_ramfb; + OnOffAuto ramfb_migrate; bool defer_kvm_irq_routing; + bool clear_parent_atomics_on_exit; VFIODisplay *dpy; Notifier irqchip_change_notifier; }; @@ -214,16 +218,22 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr); extern const PropertyInfo qdev_prop_nv_gpudirect_clique; +void vfio_pci_pre_reset(VFIOPCIDevice *vdev); +void vfio_pci_post_reset(VFIOPCIDevice *vdev); +bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name); +int vfio_pci_get_pci_hot_reset_info(VFIOPCIDevice *vdev, + struct vfio_pci_hot_reset_info **info_p); + int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp); int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, struct vfio_region_info *info, Error **errp); -int vfio_pci_nvidia_v100_ram_init(VFIOPCIDevice *vdev, Error **errp); -int vfio_pci_nvlink2_init(VFIOPCIDevice *vdev, Error **errp); void vfio_display_reset(VFIOPCIDevice *vdev); int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp); void vfio_display_finalize(VFIOPCIDevice *vdev); +extern const VMStateDescription vfio_display_vmstate; + #endif /* HW_VFIO_VFIO_PCI_H */ diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 5af73f9287..dcd2365fb3 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -15,11 +15,13 @@ */ #include "qemu/osdep.h" +#include CONFIG_DEVICES /* CONFIG_IOMMUFD */ #include "qapi/error.h" #include #include #include "hw/vfio/vfio-platform.h" +#include "sysemu/iommufd.h" #include "migration/vmstate.h" #include "qemu/error-report.h" #include "qemu/lockable.h" @@ -529,19 +531,13 @@ static VFIODeviceOps vfio_platform_ops = { */ static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp) { - VFIOGroup *group; - VFIODevice *vbasedev_iter; - char *tmp, group_path[PATH_MAX], *group_name; - ssize_t len; - struct stat st; - int groupid; int ret; - /* @sysfsdev takes precedence over @host */ - if (vbasedev->sysfsdev) { + /* @fd takes precedence over @sysfsdev which takes precedence over @host */ + if (vbasedev->fd < 0 && vbasedev->sysfsdev) { g_free(vbasedev->name); vbasedev->name = g_path_get_basename(vbasedev->sysfsdev); - } else { + } else if (vbasedev->fd < 0) { if (!vbasedev->name || strchr(vbasedev->name, '/')) { error_setg(errp, "wrong host device name"); return -EINVAL; @@ -551,53 +547,20 @@ static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp) vbasedev->name); } - if (stat(vbasedev->sysfsdev, &st) < 0) { - error_setg_errno(errp, errno, - "failed to get the sysfs host device file status"); - return -errno; - } - - tmp = g_strdup_printf("%s/iommu_group", vbasedev->sysfsdev); - len = readlink(tmp, group_path, sizeof(group_path)); - g_free(tmp); - - if (len < 0 || len >= sizeof(group_path)) { - ret = len < 0 ? -errno : -ENAMETOOLONG; - error_setg_errno(errp, -ret, "no iommu_group found"); + ret = vfio_device_get_name(vbasedev, errp); + if (ret) { return ret; } - group_path[len] = 0; - - group_name = basename(group_path); - if (sscanf(group_name, "%d", &groupid) != 1) { - error_setg_errno(errp, errno, "failed to read %s", group_path); - return -errno; - } - - trace_vfio_platform_base_device_init(vbasedev->name, groupid); - - group = vfio_get_group(groupid, &address_space_memory, errp); - if (!group) { - return -ENOENT; - } - - QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { - if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) { - error_setg(errp, "device is already attached"); - vfio_put_group(group); - return -EBUSY; - } - } - ret = vfio_get_device(group, vbasedev->name, vbasedev, errp); + ret = vfio_attach_device(vbasedev->name, vbasedev, + &address_space_memory, errp); if (ret) { - vfio_put_group(group); return ret; } ret = vfio_populate_device(vbasedev, errp); if (ret) { - vfio_put_group(group); + vfio_detach_device(vbasedev); } return ret; @@ -613,15 +576,12 @@ static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp) */ static void vfio_platform_realize(DeviceState *dev, Error **errp) { + ERRP_GUARD(); VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); SysBusDevice *sbdev = SYS_BUS_DEVICE(dev); VFIODevice *vbasedev = &vdev->vbasedev; int i, ret; - vbasedev->type = VFIO_DEVICE_TYPE_PLATFORM; - vbasedev->dev = dev; - vbasedev->ops = &vfio_platform_ops; - qemu_mutex_init(&vdev->intp_mutex); trace_vfio_platform_realize(vbasedev->sysfsdev ? @@ -686,9 +646,29 @@ static Property vfio_platform_dev_properties[] = { DEFINE_PROP_UINT32("mmap-timeout-ms", VFIOPlatformDevice, mmap_timeout, 1100), DEFINE_PROP_BOOL("x-irqfd", VFIOPlatformDevice, irqfd_allowed, true), +#ifdef CONFIG_IOMMUFD + DEFINE_PROP_LINK("iommufd", VFIOPlatformDevice, vbasedev.iommufd, + TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), +#endif DEFINE_PROP_END_OF_LIST(), }; +static void vfio_platform_instance_init(Object *obj) +{ + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(obj); + VFIODevice *vbasedev = &vdev->vbasedev; + + vfio_device_init(vbasedev, VFIO_DEVICE_TYPE_PLATFORM, &vfio_platform_ops, + DEVICE(vdev), false); +} + +#ifdef CONFIG_IOMMUFD +static void vfio_platform_set_fd(Object *obj, const char *str, Error **errp) +{ + vfio_device_set_fd(&VFIO_PLATFORM_DEVICE(obj)->vbasedev, str, errp); +} +#endif + static void vfio_platform_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -696,6 +676,9 @@ static void vfio_platform_class_init(ObjectClass *klass, void *data) dc->realize = vfio_platform_realize; device_class_set_props(dc, vfio_platform_dev_properties); +#ifdef CONFIG_IOMMUFD + object_class_property_add_str(klass, "fd", NULL, vfio_platform_set_fd); +#endif dc->vmsd = &vfio_platform_vmstate; dc->desc = "VFIO-based platform device assignment"; sbc->connect_irq_notifier = vfio_start_irqfd_injection; @@ -708,6 +691,7 @@ static const TypeInfo vfio_platform_dev_info = { .name = TYPE_VFIO_PLATFORM, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(VFIOPlatformDevice), + .instance_init = vfio_platform_instance_init, .class_init = vfio_platform_class_init, .class_size = sizeof(VFIOPlatformDeviceClass), }; diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 9ec1e95f6d..0d949bb728 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -11,6 +11,11 @@ #include "qemu/osdep.h" #include #include +#ifdef CONFIG_KVM +#include +#endif +#include "sysemu/kvm.h" +#include "exec/address-spaces.h" #include "hw/vfio/vfio-common.h" #include "hw/hw.h" @@ -19,6 +24,12 @@ #include "qapi/error.h" #include "trace.h" +typedef struct VFIOSpaprContainer { + VFIOContainer container; + MemoryListener prereg_listener; + QLIST_HEAD(, VFIOHostDMAWindow) hostwin_list; +} VFIOSpaprContainer; + static bool vfio_prereg_listener_skipped_section(MemoryRegionSection *section) { if (memory_region_is_iommu(section->mr)) { @@ -39,8 +50,10 @@ static void *vfio_prereg_gpa_to_vaddr(MemoryRegionSection *section, hwaddr gpa) static void vfio_prereg_listener_region_add(MemoryListener *listener, MemoryRegionSection *section) { - VFIOContainer *container = container_of(listener, VFIOContainer, - prereg_listener); + VFIOSpaprContainer *scontainer = container_of(listener, VFIOSpaprContainer, + prereg_listener); + VFIOContainer *container = &scontainer->container; + VFIOContainerBase *bcontainer = &container->bcontainer; const hwaddr gpa = section->offset_within_address_space; hwaddr end; int ret; @@ -83,9 +96,9 @@ static void vfio_prereg_listener_region_add(MemoryListener *listener, * can gracefully fail. Runtime, there's not much we can do other * than throw a hardware error. */ - if (!container->initialized) { - if (!container->error) { - error_setg_errno(&container->error, -ret, + if (!bcontainer->initialized) { + if (!bcontainer->error) { + error_setg_errno(&bcontainer->error, -ret, "Memory registering failed"); } } else { @@ -97,8 +110,9 @@ static void vfio_prereg_listener_region_add(MemoryListener *listener, static void vfio_prereg_listener_region_del(MemoryListener *listener, MemoryRegionSection *section) { - VFIOContainer *container = container_of(listener, VFIOContainer, - prereg_listener); + VFIOSpaprContainer *scontainer = container_of(listener, VFIOSpaprContainer, + prereg_listener); + VFIOContainer *container = &scontainer->container; const hwaddr gpa = section->offset_within_address_space; hwaddr end; int ret; @@ -135,17 +149,93 @@ static void vfio_prereg_listener_region_del(MemoryListener *listener, trace_vfio_prereg_unregister(reg.vaddr, reg.size, ret ? -errno : 0); } -const MemoryListener vfio_prereg_listener = { +static const MemoryListener vfio_prereg_listener = { .name = "vfio-pre-reg", .region_add = vfio_prereg_listener_region_add, .region_del = vfio_prereg_listener_region_del, }; -int vfio_spapr_create_window(VFIOContainer *container, - MemoryRegionSection *section, - hwaddr *pgsize) +static void vfio_host_win_add(VFIOSpaprContainer *scontainer, hwaddr min_iova, + hwaddr max_iova, uint64_t iova_pgsizes) +{ + VFIOHostDMAWindow *hostwin; + + QLIST_FOREACH(hostwin, &scontainer->hostwin_list, hostwin_next) { + if (ranges_overlap(hostwin->min_iova, + hostwin->max_iova - hostwin->min_iova + 1, + min_iova, + max_iova - min_iova + 1)) { + hw_error("%s: Overlapped IOMMU are not enabled", __func__); + } + } + + hostwin = g_malloc0(sizeof(*hostwin)); + + hostwin->min_iova = min_iova; + hostwin->max_iova = max_iova; + hostwin->iova_pgsizes = iova_pgsizes; + QLIST_INSERT_HEAD(&scontainer->hostwin_list, hostwin, hostwin_next); +} + +static int vfio_host_win_del(VFIOSpaprContainer *scontainer, + hwaddr min_iova, hwaddr max_iova) +{ + VFIOHostDMAWindow *hostwin; + + QLIST_FOREACH(hostwin, &scontainer->hostwin_list, hostwin_next) { + if (hostwin->min_iova == min_iova && hostwin->max_iova == max_iova) { + QLIST_REMOVE(hostwin, hostwin_next); + g_free(hostwin); + return 0; + } + } + + return -1; +} + +static VFIOHostDMAWindow *vfio_find_hostwin(VFIOSpaprContainer *container, + hwaddr iova, hwaddr end) +{ + VFIOHostDMAWindow *hostwin; + bool hostwin_found = false; + + QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { + if (hostwin->min_iova <= iova && end <= hostwin->max_iova) { + hostwin_found = true; + break; + } + } + + return hostwin_found ? hostwin : NULL; +} + +static int vfio_spapr_remove_window(VFIOContainer *container, + hwaddr offset_within_address_space) +{ + struct vfio_iommu_spapr_tce_remove remove = { + .argsz = sizeof(remove), + .start_addr = offset_within_address_space, + }; + int ret; + + ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_REMOVE, &remove); + if (ret) { + error_report("Failed to remove window at %"PRIx64, + (uint64_t)remove.start_addr); + return -errno; + } + + trace_vfio_spapr_remove_window(offset_within_address_space); + + return 0; +} + +static int vfio_spapr_create_window(VFIOContainer *container, + MemoryRegionSection *section, + hwaddr *pgsize) { int ret = 0; + VFIOContainerBase *bcontainer = &container->bcontainer; IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); uint64_t pagesize = memory_region_iommu_get_min_page_size(iommu_mr), pgmask; unsigned entries, bits_total, bits_per_level, max_levels; @@ -159,13 +249,13 @@ int vfio_spapr_create_window(VFIOContainer *container, if (pagesize > rampagesize) { pagesize = rampagesize; } - pgmask = container->pgsizes & (pagesize | (pagesize - 1)); + pgmask = bcontainer->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", memory_region_iommu_get_min_page_size(iommu_mr), - container->pgsizes); + bcontainer->pgsizes); return -EINVAL; } @@ -233,23 +323,235 @@ int vfio_spapr_create_window(VFIOContainer *container, return 0; } -int vfio_spapr_remove_window(VFIOContainer *container, - hwaddr offset_within_address_space) +static int +vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, + Error **errp) { - struct vfio_iommu_spapr_tce_remove remove = { - .argsz = sizeof(remove), - .start_addr = offset_within_address_space, - }; + VFIOContainer *container = container_of(bcontainer, VFIOContainer, + bcontainer); + VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, + container); + VFIOHostDMAWindow *hostwin; + hwaddr pgsize = 0; int ret; - ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_REMOVE, &remove); - if (ret) { - error_report("Failed to remove window at %"PRIx64, - (uint64_t)remove.start_addr); - return -errno; + /* + * VFIO_SPAPR_TCE_IOMMU supports a single host window between + * [dma32_window_start, dma32_window_size), we need to ensure + * the section fall in this range. + */ + if (container->iommu_type == VFIO_SPAPR_TCE_IOMMU) { + hwaddr iova, end; + + iova = section->offset_within_address_space; + end = iova + int128_get64(section->size) - 1; + + if (!vfio_find_hostwin(scontainer, iova, end)) { + error_setg(errp, "Container %p can't map guest IOVA region" + " 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx, container, + iova, end); + return -EINVAL; + } + return 0; } - trace_vfio_spapr_remove_window(offset_within_address_space); + if (container->iommu_type != VFIO_SPAPR_TCE_v2_IOMMU) { + return 0; + } + /* For now intersections are not allowed, we may relax this later */ + QLIST_FOREACH(hostwin, &scontainer->hostwin_list, hostwin_next) { + if (ranges_overlap(hostwin->min_iova, + hostwin->max_iova - hostwin->min_iova + 1, + section->offset_within_address_space, + int128_get64(section->size))) { + error_setg(errp, + "region [0x%"PRIx64",0x%"PRIx64"] overlaps with existing" + "host DMA window [0x%"PRIx64",0x%"PRIx64"]", + section->offset_within_address_space, + section->offset_within_address_space + + int128_get64(section->size) - 1, + hostwin->min_iova, hostwin->max_iova); + return -EINVAL; + } + } + + ret = vfio_spapr_create_window(container, section, &pgsize); + if (ret) { + error_setg_errno(errp, -ret, "Failed to create SPAPR window"); + return ret; + } + + vfio_host_win_add(scontainer, section->offset_within_address_space, + section->offset_within_address_space + + int128_get64(section->size) - 1, pgsize); +#ifdef CONFIG_KVM + if (kvm_enabled()) { + VFIOGroup *group; + IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); + struct kvm_vfio_spapr_tce param; + struct kvm_device_attr attr = { + .group = KVM_DEV_VFIO_GROUP, + .attr = KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE, + .addr = (uint64_t)(unsigned long)¶m, + }; + + if (!memory_region_iommu_get_attr(iommu_mr, IOMMU_ATTR_SPAPR_TCE_FD, + ¶m.tablefd)) { + QLIST_FOREACH(group, &container->group_list, container_next) { + param.groupfd = group->fd; + if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { + error_setg_errno(errp, errno, + "vfio: failed GROUP_SET_SPAPR_TCE for " + "KVM VFIO device %d and group fd %d", + param.tablefd, param.groupfd); + return -errno; + } + trace_vfio_spapr_group_attach(param.groupfd, param.tablefd); + } + } + } +#endif return 0; } + +static void +vfio_spapr_container_del_section_window(VFIOContainerBase *bcontainer, + MemoryRegionSection *section) +{ + VFIOContainer *container = container_of(bcontainer, VFIOContainer, + bcontainer); + VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, + container); + + if (container->iommu_type != VFIO_SPAPR_TCE_v2_IOMMU) { + return; + } + + vfio_spapr_remove_window(container, + section->offset_within_address_space); + if (vfio_host_win_del(scontainer, + section->offset_within_address_space, + section->offset_within_address_space + + int128_get64(section->size) - 1) < 0) { + hw_error("%s: Cannot delete missing window at %"HWADDR_PRIx, + __func__, section->offset_within_address_space); + } +} + +static void vfio_spapr_container_release(VFIOContainerBase *bcontainer) +{ + VFIOContainer *container = container_of(bcontainer, VFIOContainer, + bcontainer); + VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, + container); + VFIOHostDMAWindow *hostwin, *next; + + if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { + memory_listener_unregister(&scontainer->prereg_listener); + } + QLIST_FOREACH_SAFE(hostwin, &scontainer->hostwin_list, hostwin_next, + next) { + QLIST_REMOVE(hostwin, hostwin_next); + g_free(hostwin); + } +} + +static int vfio_spapr_container_setup(VFIOContainerBase *bcontainer, + Error **errp) +{ + VFIOContainer *container = container_of(bcontainer, VFIOContainer, + bcontainer); + VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, + container); + struct vfio_iommu_spapr_tce_info info; + bool v2 = container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU; + int ret, fd = container->fd; + + QLIST_INIT(&scontainer->hostwin_list); + + /* + * The host kernel code implementing VFIO_IOMMU_DISABLE is called + * when container fd is closed so we do not call it explicitly + * in this file. + */ + if (!v2) { + ret = ioctl(fd, VFIO_IOMMU_ENABLE); + if (ret) { + error_setg_errno(errp, errno, "failed to enable container"); + return -errno; + } + } else { + scontainer->prereg_listener = vfio_prereg_listener; + + memory_listener_register(&scontainer->prereg_listener, + &address_space_memory); + if (bcontainer->error) { + ret = -1; + error_propagate_prepend(errp, bcontainer->error, + "RAM memory listener initialization failed: "); + goto listener_unregister_exit; + } + } + + info.argsz = sizeof(info); + ret = ioctl(fd, VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info); + if (ret) { + error_setg_errno(errp, errno, + "VFIO_IOMMU_SPAPR_TCE_GET_INFO failed"); + ret = -errno; + goto listener_unregister_exit; + } + + if (v2) { + bcontainer->pgsizes = info.ddw.pgsizes; + /* + * There is a default window in just created container. + * To make region_add/del simpler, we better remove this + * window now and let those iommu_listener callbacks + * create/remove them when needed. + */ + ret = vfio_spapr_remove_window(container, info.dma32_window_start); + if (ret) { + error_setg_errno(errp, -ret, + "failed to remove existing window"); + goto listener_unregister_exit; + } + } else { + /* The default table uses 4K pages */ + bcontainer->pgsizes = 0x1000; + vfio_host_win_add(scontainer, info.dma32_window_start, + info.dma32_window_start + + info.dma32_window_size - 1, + 0x1000); + } + + return 0; + +listener_unregister_exit: + if (v2) { + memory_listener_unregister(&scontainer->prereg_listener); + } + return ret; +} + +static void vfio_iommu_spapr_class_init(ObjectClass *klass, void *data) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); + + vioc->add_window = vfio_spapr_container_add_section_window; + vioc->del_window = vfio_spapr_container_del_section_window; + vioc->release = vfio_spapr_container_release; + vioc->setup = vfio_spapr_container_setup; +}; + +static const TypeInfo types[] = { + { + .name = TYPE_VFIO_IOMMU_SPAPR, + .parent = TYPE_VFIO_IOMMU_LEGACY, + .class_init = vfio_iommu_spapr_class_init, + }, +}; + +DEFINE_TYPES(types) diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 73dffe9e00..f0474b244b 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -27,7 +27,7 @@ vfio_vga_read(uint64_t addr, int size, uint64_t data) " (0x%"PRIx64", %d) = 0x%" vfio_pci_read_config(const char *name, int addr, int len, int val) " (%s, @0x%x, len=0x%x) 0x%x" vfio_pci_write_config(const char *name, int addr, int val, int len) " (%s, @0x%x, 0x%x, len=0x%x)" vfio_msi_setup(const char *name, int pos) "%s PCI MSI CAP @0x%x" -vfio_msix_early_setup(const char *name, int pos, int table_bar, int offset, int entries) "%s PCI MSI-X CAP @0x%x, BAR %d, offset 0x%x, entries %d" +vfio_msix_early_setup(const char *name, int pos, int table_bar, int offset, int entries, bool noresize) "%s PCI MSI-X CAP @0x%x, BAR %d, offset 0x%x, entries %d, noresize %d" vfio_check_pcie_flr(const char *name) "%s Supports FLR via PCIe cap" vfio_check_pm_reset(const char *name) "%s Supports PM reset" vfio_check_af_flr(const char *name) "%s Supports FLR via AF cap" @@ -37,7 +37,8 @@ vfio_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: %s" vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s config:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx" vfio_populate_device_get_irq_info_failure(const char *errstr) "VFIO_DEVICE_GET_IRQ_INFO failure: %s" -vfio_realize(const char *name, int group_id) " (%s) group %d" +vfio_attach_device(const char *name, int group_id) " (%s) group %d" +vfio_detach_device(const char *name, int group_id) " (%s) group %d" vfio_mdev(const char *name, bool is_mdev) " (%s) is_mdev %d" vfio_add_ext_cap_dropped(const char *name, uint16_t cap, uint16_t offset) "%s 0x%x@0x%x" vfio_pci_reset(const char *name) " (%s)" @@ -82,10 +83,6 @@ vfio_ioeventfd_handler(const char *name, uint64_t addr, unsigned size, uint64_t vfio_ioeventfd_init(const char *name, uint64_t addr, unsigned size, uint64_t data, bool vfio) "%s+0x%"PRIx64"[%d]:0x%"PRIx64" vfio:%d" vfio_pci_igd_opregion_enabled(const char *name) "%s" -vfio_pci_nvidia_gpu_setup_quirk(const char *name, uint64_t tgt, uint64_t size) "%s tgt=0x%"PRIx64" size=0x%"PRIx64 -vfio_pci_nvlink2_setup_quirk_ssatgt(const char *name, uint64_t tgt, uint64_t size) "%s tgt=0x%"PRIx64" size=0x%"PRIx64 -vfio_pci_nvlink2_setup_quirk_lnkspd(const char *name, uint32_t link_speed) "%s link_speed=0x%x" - # igd.c vfio_pci_igd_bar4_write(const char *name, uint32_t index, uint32_t data, uint32_t base) "%s [0x%03x] 0x%08x -> 0x%08x" vfio_pci_igd_bdsm_enabled(const char *name, int size) "%s %dMB" @@ -96,14 +93,15 @@ vfio_pci_igd_lpc_bridge_enabled(const char *name) "%s" vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)" vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t data) " (%s:region%d+0x%"PRIx64", %d) = 0x%"PRIx64 vfio_iommu_map_notify(const char *op, uint64_t iova_start, uint64_t iova_end) "iommu %s @ 0x%"PRIx64" - 0x%"PRIx64 -vfio_listener_region_add_skip(uint64_t start, uint64_t end) "SKIPPING region_add 0x%"PRIx64" - 0x%"PRIx64 +vfio_listener_region_skip(const char *name, uint64_t start, uint64_t end) "SKIPPING %s 0x%"PRIx64" - 0x%"PRIx64 vfio_spapr_group_attach(int groupfd, int tablefd) "Attached groupfd %d to liobn fd %d" vfio_listener_region_add_iommu(uint64_t start, uint64_t end) "region_add [iommu] 0x%"PRIx64" - 0x%"PRIx64 vfio_listener_region_add_ram(uint64_t iova_start, uint64_t iova_end, void *vaddr) "region_add [ram] 0x%"PRIx64" - 0x%"PRIx64" [%p]" vfio_known_safe_misalignment(const char *name, uint64_t iova, uint64_t offset_within_region, uintptr_t page_size) "Region \"%s\" iova=0x%"PRIx64" offset_within_region=0x%"PRIx64" qemu_real_host_page_size=0x%"PRIxPTR vfio_listener_region_add_no_dma_map(const char *name, uint64_t iova, uint64_t size, uint64_t page_size) "Region \"%s\" 0x%"PRIx64" size=0x%"PRIx64" is not aligned to 0x%"PRIx64" and cannot be mapped for DMA" -vfio_listener_region_del_skip(uint64_t start, uint64_t end) "SKIPPING region_del 0x%"PRIx64" - 0x%"PRIx64 vfio_listener_region_del(uint64_t start, uint64_t end) "region_del 0x%"PRIx64" - 0x%"PRIx64 +vfio_device_dirty_tracking_update(uint64_t start, uint64_t end, uint64_t min, uint64_t max) "section 0x%"PRIx64" - 0x%"PRIx64" -> update [0x%"PRIx64" - 0x%"PRIx64"]" +vfio_device_dirty_tracking_start(int nr_ranges, uint64_t min32, uint64_t max32, uint64_t min64, uint64_t max64, uint64_t minpci, uint64_t maxpci) "nr_ranges %d 32:[0x%"PRIx64" - 0x%"PRIx64"], 64:[0x%"PRIx64" - 0x%"PRIx64"], pci64:[0x%"PRIx64" - 0x%"PRIx64"]" vfio_disconnect_container(int fd) "close container->fd=%d" vfio_put_group(int fd) "close group->fd=%d" vfio_get_device(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u" @@ -117,11 +115,12 @@ vfio_region_mmaps_set_enabled(const char *name, bool enabled) "Region %s mmaps e vfio_region_unmap(const char *name, unsigned long offset, unsigned long end) "Region %s unmap [0x%lx - 0x%lx]" vfio_region_sparse_mmap_header(const char *name, int index, int nr_areas) "Device %s region %d: %d sparse mmap entries" vfio_region_sparse_mmap_entry(int i, unsigned long start, unsigned long end) "sparse entry %d [0x%lx - 0x%lx]" -vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%0x8" -vfio_dma_unmap_overflow_workaround(void) "" +vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%08x" +vfio_legacy_dma_unmap_overflow_workaround(void) "" +vfio_get_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64 +vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64 # platform.c -vfio_platform_base_device_init(char *name, int groupid) "%s belongs to group #%d" vfio_platform_realize(char *name, char *compat) "vfio device %s, compat = %s" vfio_platform_eoi(int pin, int fd) "EOI IRQ pin %d (fd=%d)" vfio_platform_intp_mmap_enable(int pin) "IRQ #%d still active, stay in slow path" @@ -148,21 +147,31 @@ vfio_display_edid_update(uint32_t prefx, uint32_t prefy) "%ux%u" vfio_display_edid_write_error(void) "" # migration.c -vfio_migration_probe(const char *name, uint32_t index) " (%s) Region %d" -vfio_migration_set_state(const char *name, uint32_t state) " (%s) state %d" -vfio_vmstate_change(const char *name, int running, const char *reason, uint32_t dev_state) " (%s) running %d reason %s device state %d" -vfio_migration_state_notifier(const char *name, const char *state) " (%s) state %s" -vfio_save_setup(const char *name) " (%s)" -vfio_save_cleanup(const char *name) " (%s)" -vfio_save_buffer(const char *name, uint64_t data_offset, uint64_t data_size, uint64_t pending) " (%s) Offset 0x%"PRIx64" size 0x%"PRIx64" pending 0x%"PRIx64 -vfio_update_pending(const char *name, uint64_t pending) " (%s) pending 0x%"PRIx64 -vfio_save_device_config_state(const char *name) " (%s)" -vfio_save_pending(const char *name, uint64_t precopy, uint64_t postcopy, uint64_t compatible) " (%s) precopy 0x%"PRIx64" postcopy 0x%"PRIx64" compatible 0x%"PRIx64 -vfio_save_iterate(const char *name, int data_size) " (%s) data_size %d" -vfio_save_complete_precopy(const char *name) " (%s)" +vfio_load_cleanup(const char *name) " (%s)" vfio_load_device_config_state(const char *name) " (%s)" vfio_load_state(const char *name, uint64_t data) " (%s) data 0x%"PRIx64 -vfio_load_state_device_data(const char *name, uint64_t data_offset, uint64_t data_size) " (%s) Offset 0x%"PRIx64" size 0x%"PRIx64 -vfio_load_cleanup(const char *name) " (%s)" -vfio_get_dirty_bitmap(int fd, uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start) "container fd=%d, iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64 -vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64 +vfio_load_state_device_data(const char *name, uint64_t data_size, int ret) " (%s) size 0x%"PRIx64" ret %d" +vfio_migration_realize(const char *name) " (%s)" +vfio_migration_set_state(const char *name, const char *state) " (%s) state %s" +vfio_migration_state_notifier(const char *name, int state) " (%s) state %d" +vfio_save_block(const char *name, int data_size) " (%s) data_size %d" +vfio_save_cleanup(const char *name) " (%s)" +vfio_save_complete_precopy(const char *name, int ret) " (%s) ret %d" +vfio_save_device_config_state(const char *name) " (%s)" +vfio_save_iterate(const char *name, uint64_t precopy_init_size, uint64_t precopy_dirty_size) " (%s) precopy initial size 0x%"PRIx64" precopy dirty size 0x%"PRIx64 +vfio_save_setup(const char *name, uint64_t data_buffer_size) " (%s) data buffer size 0x%"PRIx64 +vfio_state_pending_estimate(const char *name, uint64_t precopy, uint64_t postcopy, uint64_t precopy_init_size, uint64_t precopy_dirty_size) " (%s) precopy 0x%"PRIx64" postcopy 0x%"PRIx64" precopy initial size 0x%"PRIx64" precopy dirty size 0x%"PRIx64 +vfio_state_pending_exact(const char *name, uint64_t precopy, uint64_t postcopy, uint64_t stopcopy_size, uint64_t precopy_init_size, uint64_t precopy_dirty_size) " (%s) precopy 0x%"PRIx64" postcopy 0x%"PRIx64" stopcopy size 0x%"PRIx64" precopy initial size 0x%"PRIx64" precopy dirty size 0x%"PRIx64 +vfio_vmstate_change(const char *name, int running, const char *reason, const char *dev_state) " (%s) running %d reason %s device state %s" +vfio_vmstate_change_prepare(const char *name, int running, const char *reason, const char *dev_state) " (%s) running %d reason %s device state %s" + +#iommufd.c + +iommufd_cdev_connect_and_bind(int iommufd, const char *name, int devfd, int devid) " [iommufd=%d] Successfully bound device %s (fd=%d): output devid=%d" +iommufd_cdev_getfd(const char *dev, int devfd) " %s (fd=%d)" +iommufd_cdev_attach_ioas_hwpt(int iommufd, const char *name, int devfd, int id) " [iommufd=%d] Successfully attached device %s (%d) to id=%d" +iommufd_cdev_detach_ioas_hwpt(int iommufd, const char *name) " [iommufd=%d] Successfully detached %s" +iommufd_cdev_fail_attach_existing_container(const char *msg) " %s" +iommufd_cdev_alloc_ioas(int iommufd, int ioas_id) " [iommufd=%d] new IOMMUFD container with ioasid=%d" +iommufd_cdev_device_info(char *name, int devfd, int num_irqs, int num_regions, int flags) " %s (%d) num_irqs=%d num_regions=%d flags=%d" +iommufd_cdev_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int dev_id) "\t%04x:%02x:%02x.%x devid %d" diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig index e9ecae1f50..aa63ff7fd4 100644 --- a/hw/virtio/Kconfig +++ b/hw/virtio/Kconfig @@ -35,6 +35,10 @@ config VIRTIO_CRYPTO default y depends on VIRTIO +config VIRTIO_MD + bool + select MEM_DEVICE + config VIRTIO_PMEM_SUPPORTED bool @@ -43,7 +47,7 @@ config VIRTIO_PMEM default y depends on VIRTIO depends on VIRTIO_PMEM_SUPPORTED - select MEM_DEVICE + select VIRTIO_MD config VIRTIO_MEM_SUPPORTED bool @@ -54,16 +58,22 @@ config VIRTIO_MEM depends on VIRTIO depends on LINUX depends on VIRTIO_MEM_SUPPORTED - select MEM_DEVICE + select VIRTIO_MD + +config VHOST_VSOCK_COMMON + bool + depends on VIRTIO config VHOST_VSOCK bool default y + select VHOST_VSOCK_COMMON depends on VIRTIO && VHOST_KERNEL config VHOST_USER_VSOCK bool default y + select VHOST_VSOCK_COMMON depends on VIRTIO && VHOST_USER config VHOST_USER_I2C @@ -80,3 +90,23 @@ config VHOST_USER_FS bool default y depends on VIRTIO && VHOST_USER + +config VHOST_USER_GPIO + bool + default y + depends on VIRTIO && VHOST_USER + +config VHOST_VDPA_DEV + bool + default y + depends on VIRTIO && VHOST_VDPA && LINUX + +config VHOST_USER_SND + bool + default y + depends on VIRTIO && VHOST_USER + +config VHOST_USER_SCMI + bool + default y + depends on VIRTIO && VHOST_USER diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index 7e8877fd64..d7f18c96e6 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -1,42 +1,67 @@ -softmmu_virtio_ss = ss.source_set() -softmmu_virtio_ss.add(files('virtio-bus.c')) -softmmu_virtio_ss.add(when: 'CONFIG_VIRTIO_PCI', if_true: files('virtio-pci.c')) -softmmu_virtio_ss.add(when: 'CONFIG_VIRTIO_MMIO', if_true: files('virtio-mmio.c')) +system_virtio_ss = ss.source_set() +system_virtio_ss.add(files('virtio-bus.c')) +system_virtio_ss.add(when: 'CONFIG_VIRTIO_PCI', if_true: files('virtio-pci.c')) +system_virtio_ss.add(when: 'CONFIG_VIRTIO_MMIO', if_true: files('virtio-mmio.c')) +system_virtio_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('virtio-crypto.c')) +system_virtio_ss.add(when: 'CONFIG_VHOST_VSOCK_COMMON', if_true: files('vhost-vsock-common.c')) +system_virtio_ss.add(when: 'CONFIG_VIRTIO_IOMMU', if_true: files('virtio-iommu.c')) +system_virtio_ss.add(when: 'CONFIG_VHOST_VDPA_DEV', if_true: files('vdpa-dev.c')) -virtio_ss = ss.source_set() -virtio_ss.add(files('virtio.c')) +specific_virtio_ss = ss.source_set() +specific_virtio_ss.add(files('virtio.c')) +specific_virtio_ss.add(files('virtio-config-io.c', 'virtio-qmp.c')) if have_vhost - virtio_ss.add(files('vhost.c', 'vhost-backend.c', 'vhost-iova-tree.c')) + system_virtio_ss.add(files('vhost.c')) + specific_virtio_ss.add(files('vhost-backend.c', 'vhost-iova-tree.c')) if have_vhost_user - virtio_ss.add(files('vhost-user.c')) + # fixme - this really should be generic + specific_virtio_ss.add(files('vhost-user.c')) + system_virtio_ss.add(files('vhost-user-base.c')) + + # MMIO Stubs + system_virtio_ss.add(files('vhost-user-device.c')) + system_virtio_ss.add(when: 'CONFIG_VHOST_USER_GPIO', if_true: files('vhost-user-gpio.c')) + system_virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c')) + system_virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c')) + system_virtio_ss.add(when: 'CONFIG_VHOST_USER_SND', if_true: files('vhost-user-snd.c')) + system_virtio_ss.add(when: 'CONFIG_VHOST_USER_INPUT', if_true: files('vhost-user-input.c')) + + # PCI Stubs + system_virtio_ss.add(when: 'CONFIG_VIRTIO_PCI', if_true: files('vhost-user-device-pci.c')) + system_virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_GPIO'], + if_true: files('vhost-user-gpio-pci.c')) + system_virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_I2C'], + if_true: files('vhost-user-i2c-pci.c')) + system_virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_RNG'], + if_true: files('vhost-user-rng-pci.c')) + system_virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_SND'], + if_true: files('vhost-user-snd-pci.c')) + system_virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_INPUT'], + if_true: files('vhost-user-input-pci.c')) endif if have_vhost_vdpa - virtio_ss.add(files('vhost-vdpa.c', 'vhost-shadow-virtqueue.c')) + system_virtio_ss.add(files('vhost-vdpa.c')) + specific_virtio_ss.add(files('vhost-shadow-virtqueue.c')) endif else - softmmu_virtio_ss.add(files('vhost-stub.c')) + system_virtio_ss.add(files('vhost-stub.c')) endif -virtio_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-balloon.c')) -virtio_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('virtio-crypto.c')) -virtio_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs.c')) -virtio_ss.add(when: 'CONFIG_VIRTIO_PMEM', if_true: files('virtio-pmem.c')) -virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock.c', 'vhost-vsock-common.c')) -virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.c', 'vhost-vsock-common.c')) -virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c')) -virtio_ss.add(when: 'CONFIG_VIRTIO_IOMMU', if_true: files('virtio-iommu.c')) -virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c')) -virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c')) -virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c')) +specific_virtio_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-balloon.c')) +specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs.c')) +specific_virtio_ss.add(when: 'CONFIG_VIRTIO_PMEM', if_true: files('virtio-pmem.c')) +specific_virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock.c')) +specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.c')) +specific_virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c')) +specific_virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c')) +specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_SCMI', if_true: files('vhost-user-scmi.c')) +specific_virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_SCMI'], if_true: files('vhost-user-scmi-pci.c')) virtio_pci_ss = ss.source_set() virtio_pci_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('vhost-user-blk-pci.c')) -virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c-pci.c')) -virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_INPUT', if_true: files('vhost-user-input-pci.c')) -virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_SCSI', if_true: files('vhost-user-scsi-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VHOST_SCSI', if_true: files('vhost-scsi-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs-pci.c')) @@ -54,10 +79,15 @@ virtio_pci_ss.add(when: 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-serial-pc virtio_pci_ss.add(when: 'CONFIG_VIRTIO_PMEM', if_true: files('virtio-pmem-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VIRTIO_IOMMU', if_true: files('virtio-iommu-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem-pci.c')) +virtio_pci_ss.add(when: 'CONFIG_VHOST_VDPA_DEV', if_true: files('vdpa-dev-pci.c')) +virtio_pci_ss.add(when: 'CONFIG_VIRTIO_MD', if_true: files('virtio-md-pci.c')) -virtio_ss.add_all(when: 'CONFIG_VIRTIO_PCI', if_true: virtio_pci_ss) +specific_virtio_ss.add_all(when: 'CONFIG_VIRTIO_PCI', if_true: virtio_pci_ss) -specific_ss.add_all(when: 'CONFIG_VIRTIO', if_true: virtio_ss) -softmmu_ss.add_all(when: 'CONFIG_VIRTIO', if_true: softmmu_virtio_ss) -softmmu_ss.add(when: 'CONFIG_VIRTIO', if_false: files('vhost-stub.c')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-stub.c')) +system_ss.add_all(when: 'CONFIG_VIRTIO', if_true: system_virtio_ss) +system_ss.add(when: 'CONFIG_VIRTIO', if_false: files('vhost-stub.c')) +system_ss.add(when: 'CONFIG_VIRTIO', if_false: files('virtio-stub.c')) +system_ss.add(files('virtio-hmp-cmds.c')) + +specific_ss.add_all(when: 'CONFIG_VIRTIO', if_true: specific_virtio_ss) +system_ss.add(when: 'CONFIG_ACPI', if_true: files('virtio-acpi.c')) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index ab8e095b73..96632fd026 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -8,6 +8,10 @@ vhost_region_add_section_aligned(const char *name, uint64_t gpa, uint64_t size, vhost_section(const char *name) "%s" vhost_reject_section(const char *name, int d) "%s:%d" vhost_iotlb_miss(void *dev, int step) "%p step %d" +vhost_dev_cleanup(void *dev) "%p" +vhost_dev_start(void *dev, const char *name, bool vrings) "%p:%s vrings:%d" +vhost_dev_stop(void *dev, const char *name, bool vrings) "%p:%s vrings:%d" + # vhost-user.c vhost_user_postcopy_end_entry(void) "" @@ -26,38 +30,43 @@ vhost_user_write(uint32_t req, uint32_t flags) "req:%d flags:0x%"PRIx32"" vhost_user_create_notifier(int idx, void *n) "idx:%d n:%p" # vhost-vdpa.c -vhost_vdpa_dma_map(void *vdpa, int fd, uint32_t msg_type, uint64_t iova, uint64_t size, uint64_t uaddr, uint8_t perm, uint8_t type) "vdpa:%p fd: %d msg_type: %"PRIu32" iova: 0x%"PRIx64" size: 0x%"PRIx64" uaddr: 0x%"PRIx64" perm: 0x%"PRIx8" type: %"PRIu8 -vhost_vdpa_dma_unmap(void *vdpa, int fd, uint32_t msg_type, uint64_t iova, uint64_t size, uint8_t type) "vdpa:%p fd: %d msg_type: %"PRIu32" iova: 0x%"PRIx64" size: 0x%"PRIx64" type: %"PRIu8 -vhost_vdpa_listener_begin_batch(void *v, int fd, uint32_t msg_type, uint8_t type) "vdpa:%p fd: %d msg_type: %"PRIu32" type: %"PRIu8 -vhost_vdpa_listener_commit(void *v, int fd, uint32_t msg_type, uint8_t type) "vdpa:%p fd: %d msg_type: %"PRIu32" type: %"PRIu8 +vhost_vdpa_skipped_memory_section(int is_ram, int is_iommu, int is_protected, int is_ram_device, uint64_t first, uint64_t last, int page_mask) "is_ram=%d, is_iommu=%d, is_protected=%d, is_ram_device=%d iova_min=0x%"PRIx64" iova_last=0x%"PRIx64" page_mask=0x%x" +vhost_vdpa_dma_map(void *vdpa, int fd, uint32_t msg_type, uint32_t asid, uint64_t iova, uint64_t size, uint64_t uaddr, uint8_t perm, uint8_t type) "vdpa_shared:%p fd: %d msg_type: %"PRIu32" asid: %"PRIu32" iova: 0x%"PRIx64" size: 0x%"PRIx64" uaddr: 0x%"PRIx64" perm: 0x%"PRIx8" type: %"PRIu8 +vhost_vdpa_dma_unmap(void *vdpa, int fd, uint32_t msg_type, uint32_t asid, uint64_t iova, uint64_t size, uint8_t type) "vdpa_shared:%p fd: %d msg_type: %"PRIu32" asid: %"PRIu32" iova: 0x%"PRIx64" size: 0x%"PRIx64" type: %"PRIu8 +vhost_vdpa_listener_begin_batch(void *v, int fd, uint32_t msg_type, uint8_t type) "vdpa_shared:%p fd: %d msg_type: %"PRIu32" type: %"PRIu8 +vhost_vdpa_listener_commit(void *v, int fd, uint32_t msg_type, uint8_t type) "vdpa_shared:%p fd: %d msg_type: %"PRIu32" type: %"PRIu8 +vhost_vdpa_listener_region_add_unaligned(void *v, const char *name, uint64_t offset_as, uint64_t offset_page) "vdpa_shared: %p region %s offset_within_address_space %"PRIu64" offset_within_region %"PRIu64 vhost_vdpa_listener_region_add(void *vdpa, uint64_t iova, uint64_t llend, void *vaddr, bool readonly) "vdpa: %p iova 0x%"PRIx64" llend 0x%"PRIx64" vaddr: %p read-only: %d" +vhost_vdpa_listener_region_del_unaligned(void *v, const char *name, uint64_t offset_as, uint64_t offset_page) "vdpa_shared: %p region %s offset_within_address_space %"PRIu64" offset_within_region %"PRIu64 vhost_vdpa_listener_region_del(void *vdpa, uint64_t iova, uint64_t llend) "vdpa: %p iova 0x%"PRIx64" llend 0x%"PRIx64 vhost_vdpa_add_status(void *dev, uint8_t status) "dev: %p status: 0x%"PRIx8 -vhost_vdpa_init(void *dev, void *vdpa) "dev: %p vdpa: %p" +vhost_vdpa_init(void *dev, void *s, void *vdpa) "dev: %p, common dev: %p vdpa: %p" vhost_vdpa_cleanup(void *dev, void *vdpa) "dev: %p vdpa: %p" vhost_vdpa_memslots_limit(void *dev, int ret) "dev: %p = 0x%x" vhost_vdpa_set_mem_table(void *dev, uint32_t nregions, uint32_t padding) "dev: %p nregions: %"PRIu32" padding: 0x%"PRIx32 vhost_vdpa_dump_regions(void *dev, int i, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, uint64_t flags_padding) "dev: %p %d: guest_phys_addr: 0x%"PRIx64" memory_size: 0x%"PRIx64" userspace_addr: 0x%"PRIx64" flags_padding: 0x%"PRIx64 vhost_vdpa_set_features(void *dev, uint64_t features) "dev: %p features: 0x%"PRIx64 vhost_vdpa_get_device_id(void *dev, uint32_t device_id) "dev: %p device_id %"PRIu32 -vhost_vdpa_reset_device(void *dev, uint8_t status) "dev: %p status: 0x%"PRIx8 +vhost_vdpa_reset_device(void *dev) "dev: %p" vhost_vdpa_get_vq_index(void *dev, int idx, int vq_idx) "dev: %p idx: %d vq idx: %d" -vhost_vdpa_set_vring_ready(void *dev) "dev: %p" +vhost_vdpa_set_vring_enable_one(void *dev, unsigned i, int enable, int r) "dev: %p, idx: %u, enable: %u, r: %d" vhost_vdpa_dump_config(void *dev, const char *line) "dev: %p %s" vhost_vdpa_set_config(void *dev, uint32_t offset, uint32_t size, uint32_t flags) "dev: %p offset: %"PRIu32" size: %"PRIu32" flags: 0x%"PRIx32 vhost_vdpa_get_config(void *dev, void *config, uint32_t config_len) "dev: %p config: %p config_len: %"PRIu32 +vhost_vdpa_suspend(void *dev) "dev: %p" vhost_vdpa_dev_start(void *dev, bool started) "dev: %p started: %d" vhost_vdpa_set_log_base(void *dev, uint64_t base, unsigned long long size, int refcnt, int fd, void *log) "dev: %p base: 0x%"PRIx64" size: %llu refcnt: %d fd: %d log: %p" vhost_vdpa_set_vring_addr(void *dev, unsigned int index, unsigned int flags, uint64_t desc_user_addr, uint64_t used_user_addr, uint64_t avail_user_addr, uint64_t log_guest_addr) "dev: %p index: %u flags: 0x%x desc_user_addr: 0x%"PRIx64" used_user_addr: 0x%"PRIx64" avail_user_addr: 0x%"PRIx64" log_guest_addr: 0x%"PRIx64 vhost_vdpa_set_vring_num(void *dev, unsigned int index, unsigned int num) "dev: %p index: %u num: %u" -vhost_vdpa_set_vring_base(void *dev, unsigned int index, unsigned int num) "dev: %p index: %u num: %u" -vhost_vdpa_get_vring_base(void *dev, unsigned int index, unsigned int num) "dev: %p index: %u num: %u" +vhost_vdpa_set_dev_vring_base(void *dev, unsigned int index, unsigned int num, bool svq) "dev: %p index: %u num: %u svq: %d" +vhost_vdpa_get_vring_base(void *dev, unsigned int index, unsigned int num, bool svq) "dev: %p index: %u num: %u svq: %d" vhost_vdpa_set_vring_kick(void *dev, unsigned int index, int fd) "dev: %p index: %u fd: %d" vhost_vdpa_set_vring_call(void *dev, unsigned int index, int fd) "dev: %p index: %u fd: %d" vhost_vdpa_get_features(void *dev, uint64_t features) "dev: %p features: 0x%"PRIx64 vhost_vdpa_set_owner(void *dev) "dev: %p" vhost_vdpa_vq_get_addr(void *dev, void *vq, uint64_t desc_user_addr, uint64_t avail_user_addr, uint64_t used_user_addr) "dev: %p vq: %p desc_user_addr: 0x%"PRIx64" avail_user_addr: 0x%"PRIx64" used_user_addr: 0x%"PRIx64 vhost_vdpa_get_iova_range(void *dev, uint64_t first, uint64_t last) "dev: %p first: 0x%"PRIx64" last: 0x%"PRIx64 +vhost_vdpa_set_config_call(void *dev, int fd)"dev: %p fd: %d" # virtio.c virtqueue_alloc_element(void *elem, size_t sz, unsigned in_num, unsigned out_num) "elem %p size %zd in_num %u out_num %u" @@ -65,6 +74,7 @@ virtqueue_fill(void *vq, const void *elem, unsigned int len, unsigned int idx) " virtqueue_flush(void *vq, unsigned int count) "vq %p count %u" virtqueue_pop(void *vq, void *elem, unsigned int in_num, unsigned int out_num) "vq %p elem %p in_num %u out_num %u" virtio_queue_notify(void *vdev, int n, void *vq) "vdev %p n %d vq %p" +virtio_notify_irqfd_deferred_fn(void *vdev, void *vq) "vdev %p vq %p" virtio_notify_irqfd(void *vdev, void *vq) "vdev %p vq %p" virtio_notify(void *vdev, void *vq) "vdev %p vq %p" virtio_set_status(void *vdev, uint8_t val) "vdev %p val %u" @@ -102,7 +112,7 @@ virtio_iommu_device_reset(void) "reset!" virtio_iommu_system_reset(void) "system reset!" virtio_iommu_get_features(uint64_t features) "device supports features=0x%"PRIx64 virtio_iommu_device_status(uint8_t status) "driver status = %d" -virtio_iommu_get_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_start, uint32_t domain_end, uint32_t probe_size, uint8_t bypass) "page_size_mask=0x%"PRIx64" input range start=0x%"PRIx64" input range end=0x%"PRIx64" domain range start=%d domain range end=%d probe_size=0x%x bypass=0x%x" +virtio_iommu_get_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_start, uint32_t domain_end, uint32_t probe_size, uint8_t bypass) "page_size_mask=0x%"PRIx64" input range start=0x%"PRIx64" input range end=0x%"PRIx64" domain range start=%u domain range end=%u probe_size=0x%x bypass=0x%x" virtio_iommu_set_config(uint8_t bypass) "bypass=0x%x" virtio_iommu_attach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d" virtio_iommu_detach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d" @@ -124,6 +134,9 @@ virtio_iommu_remap(const char *name, uint64_t virt_start, uint64_t virt_end, uin virtio_iommu_set_page_size_mask(const char *name, uint64_t old, uint64_t new) "mr=%s old_mask=0x%"PRIx64" new_mask=0x%"PRIx64 virtio_iommu_notify_flag_add(const char *name) "add notifier to mr %s" virtio_iommu_notify_flag_del(const char *name) "del notifier from mr %s" +virtio_iommu_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" +virtio_iommu_freeze_granule(uint64_t page_size_mask) "granule set to 0x%"PRIx64 +virtio_iommu_host_resv_regions(const char *name, uint32_t index, uint64_t lob, uint64_t upb) "mr=%s host-resv-reg[%d] = [0x%"PRIx64",0x%"PRIx64"]" # virtio-mem.c virtio_mem_send_response(uint16_t type) "type=%" PRIu16 @@ -139,3 +152,8 @@ virtio_mem_state_response(uint16_t state) "state=%" PRIu16 virtio_pmem_flush_request(void) "flush request" virtio_pmem_response(void) "flush response" virtio_pmem_flush_done(int type) "fsync return=%d" + +# virtio-gpio.c +virtio_gpio_start(void) "start" +virtio_gpio_stop(void) "stop" +virtio_gpio_set_status(uint8_t status) "0x%x" diff --git a/hw/virtio/vdpa-dev-pci.c b/hw/virtio/vdpa-dev-pci.c new file mode 100644 index 0000000000..5446e6b393 --- /dev/null +++ b/hw/virtio/vdpa-dev-pci.c @@ -0,0 +1,102 @@ +/* + * Vhost Vdpa Device PCI Bindings + * + * Copyright (c) Huawei Technologies Co., Ltd. 2022. All Rights Reserved. + * + * Authors: + * Longpeng + * + * Largely based on the "vhost-user-blk-pci.c" and "vhost-user-blk.c" + * implemented by: + * Changpeng Liu + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#include "qemu/osdep.h" +#include +#include +#include "hw/virtio/virtio.h" +#include "hw/virtio/vdpa-dev.h" +#include "hw/pci/pci.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "hw/virtio/virtio-pci.h" +#include "qom/object.h" + + +typedef struct VhostVdpaDevicePCI VhostVdpaDevicePCI; + +#define TYPE_VHOST_VDPA_DEVICE_PCI "vhost-vdpa-device-pci-base" +DECLARE_INSTANCE_CHECKER(VhostVdpaDevicePCI, VHOST_VDPA_DEVICE_PCI, + TYPE_VHOST_VDPA_DEVICE_PCI) + +struct VhostVdpaDevicePCI { + VirtIOPCIProxy parent_obj; + VhostVdpaDevice vdev; +}; + +static void vhost_vdpa_device_pci_instance_init(Object *obj) +{ + VhostVdpaDevicePCI *dev = VHOST_VDPA_DEVICE_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_VDPA_DEVICE); + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), + "bootindex"); +} + +static Property vhost_vdpa_device_pci_properties[] = { + DEFINE_PROP_END_OF_LIST(), +}; + +static int vhost_vdpa_device_pci_post_init(VhostVdpaDevice *v, Error **errp) +{ + VhostVdpaDevicePCI *dev = container_of(v, VhostVdpaDevicePCI, vdev); + VirtIOPCIProxy *vpci_dev = &dev->parent_obj; + + vpci_dev->class_code = virtio_pci_get_class_id(v->vdev_id); + vpci_dev->trans_devid = virtio_pci_get_trans_devid(v->vdev_id); + /* one for config vector */ + vpci_dev->nvectors = v->num_queues + 1; + + return 0; +} + +static void +vhost_vdpa_device_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VhostVdpaDevicePCI *dev = VHOST_VDPA_DEVICE_PCI(vpci_dev); + + dev->vdev.post_init = vhost_vdpa_device_pci_post_init; + qdev_realize(DEVICE(&dev->vdev), BUS(&vpci_dev->bus), errp); +} + +static void vhost_vdpa_device_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, vhost_vdpa_device_pci_properties); + k->realize = vhost_vdpa_device_pci_realize; +} + +static const VirtioPCIDeviceTypeInfo vhost_vdpa_device_pci_info = { + .base_name = TYPE_VHOST_VDPA_DEVICE_PCI, + .generic_name = "vhost-vdpa-device-pci", + .transitional_name = "vhost-vdpa-device-pci-transitional", + .non_transitional_name = "vhost-vdpa-device-pci-non-transitional", + .instance_size = sizeof(VhostVdpaDevicePCI), + .instance_init = vhost_vdpa_device_pci_instance_init, + .class_init = vhost_vdpa_device_pci_class_init, +}; + +static void vhost_vdpa_device_pci_register(void) +{ + virtio_pci_types_register(&vhost_vdpa_device_pci_info); +} + +type_init(vhost_vdpa_device_pci_register); diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c new file mode 100644 index 0000000000..64b96b226c --- /dev/null +++ b/hw/virtio/vdpa-dev.c @@ -0,0 +1,395 @@ +/* + * Vhost Vdpa Device + * + * Copyright (c) Huawei Technologies Co., Ltd. 2022. All Rights Reserved. + * + * Authors: + * Longpeng + * + * Largely based on the "vhost-user-blk-pci.c" and "vhost-user-blk.c" + * implemented by: + * Changpeng Liu + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#include "qemu/osdep.h" +#include +#include +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/cutils.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/vdpa-dev.h" +#include "sysemu/sysemu.h" +#include "sysemu/runstate.h" + +static void +vhost_vdpa_device_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + /* Nothing to do */ +} + +static uint32_t +vhost_vdpa_device_get_u32(int fd, unsigned long int cmd, Error **errp) +{ + uint32_t val = (uint32_t)-1; + + if (ioctl(fd, cmd, &val) < 0) { + error_setg(errp, "vhost-vdpa-device: cmd 0x%lx failed: %s", + cmd, strerror(errno)); + } + + return val; +} + +static void vhost_vdpa_device_realize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VhostVdpaDevice *v = VHOST_VDPA_DEVICE(vdev); + struct vhost_vdpa_iova_range iova_range; + uint16_t max_queue_size; + struct vhost_virtqueue *vqs; + int i, ret; + + if (!v->vhostdev) { + error_setg(errp, "vhost-vdpa-device: vhostdev are missing"); + return; + } + + v->vhostfd = qemu_open(v->vhostdev, O_RDWR, errp); + if (*errp) { + return; + } + + v->vdev_id = vhost_vdpa_device_get_u32(v->vhostfd, + VHOST_VDPA_GET_DEVICE_ID, errp); + if (*errp) { + goto out; + } + + max_queue_size = vhost_vdpa_device_get_u32(v->vhostfd, + VHOST_VDPA_GET_VRING_NUM, errp); + if (*errp) { + goto out; + } + + if (v->queue_size > max_queue_size) { + error_setg(errp, "vhost-vdpa-device: invalid queue_size: %u (max:%u)", + v->queue_size, max_queue_size); + goto out; + } else if (!v->queue_size) { + v->queue_size = max_queue_size; + } + + v->num_queues = vhost_vdpa_device_get_u32(v->vhostfd, + VHOST_VDPA_GET_VQS_COUNT, errp); + if (*errp) { + goto out; + } + + if (!v->num_queues || v->num_queues > VIRTIO_QUEUE_MAX) { + error_setg(errp, "invalid number of virtqueues: %u (max:%u)", + v->num_queues, VIRTIO_QUEUE_MAX); + goto out; + } + + v->dev.nvqs = v->num_queues; + vqs = g_new0(struct vhost_virtqueue, v->dev.nvqs); + v->dev.vqs = vqs; + v->dev.vq_index = 0; + v->dev.vq_index_end = v->dev.nvqs; + v->dev.backend_features = 0; + v->started = false; + + ret = vhost_vdpa_get_iova_range(v->vhostfd, &iova_range); + if (ret < 0) { + error_setg(errp, "vhost-vdpa-device: get iova range failed: %s", + strerror(-ret)); + goto free_vqs; + } + v->vdpa.shared = g_new0(VhostVDPAShared, 1); + v->vdpa.shared->device_fd = v->vhostfd; + v->vdpa.shared->iova_range = iova_range; + + ret = vhost_dev_init(&v->dev, &v->vdpa, VHOST_BACKEND_TYPE_VDPA, 0, NULL); + if (ret < 0) { + error_setg(errp, "vhost-vdpa-device: vhost initialization failed: %s", + strerror(-ret)); + goto free_vqs; + } + + v->config_size = vhost_vdpa_device_get_u32(v->vhostfd, + VHOST_VDPA_GET_CONFIG_SIZE, + errp); + if (*errp) { + goto vhost_cleanup; + } + + /* + * Invoke .post_init() to initialize the transport-specific fields + * before calling virtio_init(). + */ + if (v->post_init && v->post_init(v, errp) < 0) { + goto vhost_cleanup; + } + + v->config = g_malloc0(v->config_size); + + ret = vhost_dev_get_config(&v->dev, v->config, v->config_size, NULL); + if (ret < 0) { + error_setg(errp, "vhost-vdpa-device: get config failed"); + goto free_config; + } + + virtio_init(vdev, v->vdev_id, v->config_size); + + v->virtqs = g_new0(VirtQueue *, v->dev.nvqs); + for (i = 0; i < v->dev.nvqs; i++) { + v->virtqs[i] = virtio_add_queue(vdev, v->queue_size, + vhost_vdpa_device_dummy_handle_output); + } + + return; + +free_config: + g_free(v->config); +vhost_cleanup: + vhost_dev_cleanup(&v->dev); +free_vqs: + g_free(vqs); + g_free(v->vdpa.shared); +out: + qemu_close(v->vhostfd); + v->vhostfd = -1; +} + +static void vhost_vdpa_device_unrealize(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); + int i; + + virtio_set_status(vdev, 0); + + for (i = 0; i < s->num_queues; i++) { + virtio_delete_queue(s->virtqs[i]); + } + g_free(s->virtqs); + virtio_cleanup(vdev); + + g_free(s->config); + g_free(s->dev.vqs); + vhost_dev_cleanup(&s->dev); + g_free(s->vdpa.shared); + qemu_close(s->vhostfd); + s->vhostfd = -1; +} + +static void +vhost_vdpa_device_get_config(VirtIODevice *vdev, uint8_t *config) +{ + VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); + int ret; + + ret = vhost_dev_get_config(&s->dev, s->config, s->config_size, + NULL); + if (ret < 0) { + error_report("get device config space failed"); + return; + } + memcpy(config, s->config, s->config_size); +} + +static void +vhost_vdpa_device_set_config(VirtIODevice *vdev, const uint8_t *config) +{ + VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); + int ret; + + ret = vhost_dev_set_config(&s->dev, s->config, 0, s->config_size, + VHOST_SET_CONFIG_TYPE_FRONTEND); + if (ret) { + error_report("set device config space failed"); + return; + } +} + +static uint64_t vhost_vdpa_device_get_features(VirtIODevice *vdev, + uint64_t features, + Error **errp) +{ + VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); + uint64_t backend_features = s->dev.features; + + if (!virtio_has_feature(features, VIRTIO_F_IOMMU_PLATFORM)) { + virtio_clear_feature(&backend_features, VIRTIO_F_IOMMU_PLATFORM); + } + + return backend_features; +} + +static int vhost_vdpa_device_start(VirtIODevice *vdev, Error **errp) +{ + VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + int i, ret; + + if (!k->set_guest_notifiers) { + error_setg(errp, "binding does not support guest notifiers"); + return -ENOSYS; + } + + ret = vhost_dev_enable_notifiers(&s->dev, vdev); + if (ret < 0) { + error_setg_errno(errp, -ret, "Error enabling host notifiers"); + return ret; + } + + ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true); + if (ret < 0) { + error_setg_errno(errp, -ret, "Error binding guest notifier"); + goto err_host_notifiers; + } + + s->dev.acked_features = vdev->guest_features; + + ret = vhost_dev_start(&s->dev, vdev, true); + if (ret < 0) { + error_setg_errno(errp, -ret, "Error starting vhost"); + goto err_guest_notifiers; + } + s->started = true; + + /* + * guest_notifier_mask/pending not used yet, so just unmask + * everything here. virtio-pci will do the right thing by + * enabling/disabling irqfd. + */ + for (i = 0; i < s->dev.nvqs; i++) { + vhost_virtqueue_mask(&s->dev, vdev, i, false); + } + + return ret; + +err_guest_notifiers: + k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); +err_host_notifiers: + vhost_dev_disable_notifiers(&s->dev, vdev); + return ret; +} + +static void vhost_vdpa_device_stop(VirtIODevice *vdev) +{ + VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + int ret; + + if (!s->started) { + return; + } + s->started = false; + + if (!k->set_guest_notifiers) { + return; + } + + vhost_dev_stop(&s->dev, vdev, false); + + ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); + if (ret < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + return; + } + + vhost_dev_disable_notifiers(&s->dev, vdev); +} + +static void vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) +{ + VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); + bool should_start = virtio_device_started(vdev, status); + Error *local_err = NULL; + int ret; + + if (!vdev->vm_running) { + should_start = false; + } + + if (s->started == should_start) { + return; + } + + if (should_start) { + ret = vhost_vdpa_device_start(vdev, &local_err); + if (ret < 0) { + error_reportf_err(local_err, "vhost-vdpa-device: start failed: "); + } + } else { + vhost_vdpa_device_stop(vdev); + } +} + +static Property vhost_vdpa_device_properties[] = { + DEFINE_PROP_STRING("vhostdev", VhostVdpaDevice, vhostdev), + DEFINE_PROP_UINT16("queue-size", VhostVdpaDevice, queue_size, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_vhost_vdpa_device = { + .name = "vhost-vdpa-device", + .unmigratable = 1, + .minimum_version_id = 1, + .version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_VIRTIO_DEVICE, + VMSTATE_END_OF_LIST() + }, +}; + +static void vhost_vdpa_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + + device_class_set_props(dc, vhost_vdpa_device_properties); + dc->desc = "VDPA-based generic device assignment"; + dc->vmsd = &vmstate_vhost_vdpa_device; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + vdc->realize = vhost_vdpa_device_realize; + vdc->unrealize = vhost_vdpa_device_unrealize; + vdc->get_config = vhost_vdpa_device_get_config; + vdc->set_config = vhost_vdpa_device_set_config; + vdc->get_features = vhost_vdpa_device_get_features; + vdc->set_status = vhost_vdpa_device_set_status; +} + +static void vhost_vdpa_device_instance_init(Object *obj) +{ + VhostVdpaDevice *s = VHOST_VDPA_DEVICE(obj); + + device_add_bootindex_property(obj, &s->bootindex, "bootindex", + NULL, DEVICE(obj)); +} + +static const TypeInfo vhost_vdpa_device_info = { + .name = TYPE_VHOST_VDPA_DEVICE, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VhostVdpaDevice), + .class_init = vhost_vdpa_device_class_init, + .instance_init = vhost_vdpa_device_instance_init, +}; + +static void register_vhost_vdpa_device_type(void) +{ + type_register_static(&vhost_vdpa_device_info); +} + +type_init(register_vhost_vdpa_device_type); diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index 4de8b6b3b0..833804dd40 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -146,12 +146,42 @@ static int vhost_kernel_set_vring_call(struct vhost_dev *dev, return vhost_kernel_call(dev, VHOST_SET_VRING_CALL, file); } +static int vhost_kernel_set_vring_err(struct vhost_dev *dev, + struct vhost_vring_file *file) +{ + return vhost_kernel_call(dev, VHOST_SET_VRING_ERR, file); +} + static int vhost_kernel_set_vring_busyloop_timeout(struct vhost_dev *dev, struct vhost_vring_state *s) { return vhost_kernel_call(dev, VHOST_SET_VRING_BUSYLOOP_TIMEOUT, s); } +static int vhost_kernel_new_worker(struct vhost_dev *dev, + struct vhost_worker_state *worker) +{ + return vhost_kernel_call(dev, VHOST_NEW_WORKER, worker); +} + +static int vhost_kernel_free_worker(struct vhost_dev *dev, + struct vhost_worker_state *worker) +{ + return vhost_kernel_call(dev, VHOST_FREE_WORKER, worker); +} + +static int vhost_kernel_attach_vring_worker(struct vhost_dev *dev, + struct vhost_vring_worker *worker) +{ + return vhost_kernel_call(dev, VHOST_ATTACH_VRING_WORKER, worker); +} + +static int vhost_kernel_get_vring_worker(struct vhost_dev *dev, + struct vhost_vring_worker *worker) +{ + return vhost_kernel_call(dev, VHOST_GET_VRING_WORKER, worker); +} + static int vhost_kernel_set_features(struct vhost_dev *dev, uint64_t features) { @@ -191,11 +221,6 @@ static int vhost_kernel_set_owner(struct vhost_dev *dev) return vhost_kernel_call(dev, VHOST_SET_OWNER, NULL); } -static int vhost_kernel_reset_device(struct vhost_dev *dev) -{ - return vhost_kernel_call(dev, VHOST_RESET_OWNER, NULL); -} - static int vhost_kernel_get_vq_index(struct vhost_dev *dev, int idx) { assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); @@ -309,13 +334,17 @@ const VhostOps kernel_ops = { .vhost_get_vring_base = vhost_kernel_get_vring_base, .vhost_set_vring_kick = vhost_kernel_set_vring_kick, .vhost_set_vring_call = vhost_kernel_set_vring_call, + .vhost_set_vring_err = vhost_kernel_set_vring_err, .vhost_set_vring_busyloop_timeout = vhost_kernel_set_vring_busyloop_timeout, + .vhost_get_vring_worker = vhost_kernel_get_vring_worker, + .vhost_attach_vring_worker = vhost_kernel_attach_vring_worker, + .vhost_new_worker = vhost_kernel_new_worker, + .vhost_free_worker = vhost_kernel_free_worker, .vhost_set_features = vhost_kernel_set_features, .vhost_get_features = vhost_kernel_get_features, .vhost_set_backend_cap = vhost_kernel_set_backend_cap, .vhost_set_owner = vhost_kernel_set_owner, - .vhost_reset_device = vhost_kernel_reset_device, .vhost_get_vq_index = vhost_kernel_get_vq_index, .vhost_vsock_set_guest_cid = vhost_kernel_vsock_set_guest_cid, .vhost_vsock_set_running = vhost_kernel_vsock_set_running, diff --git a/hw/virtio/vhost-iova-tree.c b/hw/virtio/vhost-iova-tree.c index 67bf6d57ab..3d03395a77 100644 --- a/hw/virtio/vhost-iova-tree.c +++ b/hw/virtio/vhost-iova-tree.c @@ -104,7 +104,7 @@ int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map) * @iova_tree: The vhost iova tree * @map: The map to remove */ -void vhost_iova_tree_remove(VhostIOVATree *iova_tree, const DMAMap *map) +void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map) { iova_tree_remove(iova_tree->iova_taddr_map, map); } diff --git a/hw/virtio/vhost-iova-tree.h b/hw/virtio/vhost-iova-tree.h index 6a4f24e0f9..4adfd79ff0 100644 --- a/hw/virtio/vhost-iova-tree.h +++ b/hw/virtio/vhost-iova-tree.h @@ -22,6 +22,6 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostIOVATree, vhost_iova_tree_delete); const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *iova_tree, const DMAMap *map); int vhost_iova_tree_map_alloc(VhostIOVATree *iova_tree, DMAMap *map); -void vhost_iova_tree_remove(VhostIOVATree *iova_tree, const DMAMap *map); +void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map); #endif diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c index 56c96ebd13..fc5f408f77 100644 --- a/hw/virtio/vhost-shadow-virtqueue.c +++ b/hw/virtio/vhost-shadow-virtqueue.c @@ -33,6 +33,7 @@ bool vhost_svq_valid_features(uint64_t features, Error **errp) ++b) { switch (b) { case VIRTIO_F_ANY_LAYOUT: + case VIRTIO_RING_F_EVENT_IDX: continue; case VIRTIO_F_ACCESS_PLATFORM: @@ -65,9 +66,9 @@ bool vhost_svq_valid_features(uint64_t features, Error **errp) * * @svq: The svq */ -static uint16_t vhost_svq_available_slots(const VhostShadowVirtqueue *svq) +uint16_t vhost_svq_available_slots(const VhostShadowVirtqueue *svq) { - return svq->vring.num - (svq->shadow_avail_idx - svq->shadow_used_idx); + return svq->num_free; } /** @@ -110,7 +111,7 @@ static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq, addrs[i] = map->iova + off; needle_last = int128_add(int128_make64(needle.translated_addr), - int128_make64(iovec[i].iov_len)); + int128_makes64(iovec[i].iov_len - 1)); map_last = int128_make64(map->translated_addr + map->size); if (unlikely(int128_gt(needle_last, map_last))) { qemu_log_mask(LOG_GUEST_ERROR, @@ -122,17 +123,35 @@ static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq, return true; } -static void vhost_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg, - const struct iovec *iovec, size_t num, - bool more_descs, bool write) +/** + * Write descriptors to SVQ vring + * + * @svq: The shadow virtqueue + * @sg: Cache for hwaddr + * @iovec: The iovec from the guest + * @num: iovec length + * @more_descs: True if more descriptors come in the chain + * @write: True if they are writeable descriptors + * + * Return true if success, false otherwise and print error. + */ +static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg, + const struct iovec *iovec, size_t num, + bool more_descs, bool write) { uint16_t i = svq->free_head, last = svq->free_head; unsigned n; uint16_t flags = write ? cpu_to_le16(VRING_DESC_F_WRITE) : 0; vring_desc_t *descs = svq->vring.desc; + bool ok; if (num == 0) { - return; + return true; + } + + ok = vhost_svq_translate_addr(svq, sg, iovec, num); + if (unlikely(!ok)) { + return false; } for (n = 0; n < num; n++) { @@ -150,39 +169,38 @@ static void vhost_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg, } svq->free_head = le16_to_cpu(svq->desc_next[last]); + return true; } static bool vhost_svq_add_split(VhostShadowVirtqueue *svq, - VirtQueueElement *elem, unsigned *head) + const struct iovec *out_sg, size_t out_num, + const struct iovec *in_sg, size_t in_num, + unsigned *head) { unsigned avail_idx; vring_avail_t *avail = svq->vring.avail; bool ok; - g_autofree hwaddr *sgs = g_new(hwaddr, MAX(elem->out_num, elem->in_num)); + g_autofree hwaddr *sgs = g_new(hwaddr, MAX(out_num, in_num)); *head = svq->free_head; /* We need some descriptors here */ - if (unlikely(!elem->out_num && !elem->in_num)) { + if (unlikely(!out_num && !in_num)) { qemu_log_mask(LOG_GUEST_ERROR, "Guest provided element with no descriptors"); return false; } - ok = vhost_svq_translate_addr(svq, sgs, elem->out_sg, elem->out_num); - if (unlikely(!ok)) { - return false; - } - vhost_vring_write_descs(svq, sgs, elem->out_sg, elem->out_num, - elem->in_num > 0, false); - - - ok = vhost_svq_translate_addr(svq, sgs, elem->in_sg, elem->in_num); + ok = vhost_svq_vring_write_descs(svq, sgs, out_sg, out_num, in_num > 0, + false); if (unlikely(!ok)) { return false; } - vhost_vring_write_descs(svq, sgs, elem->in_sg, elem->in_num, false, true); + ok = vhost_svq_vring_write_descs(svq, sgs, in_sg, in_num, false, true); + if (unlikely(!ok)) { + return false; + } /* * Put the entry in the available array (but don't update avail->idx until @@ -199,40 +217,67 @@ static bool vhost_svq_add_split(VhostShadowVirtqueue *svq, return true; } -/** - * Add an element to a SVQ. - * - * The caller must check that there is enough slots for the new element. It - * takes ownership of the element: In case of failure, it is free and the SVQ - * is considered broken. - */ -static bool vhost_svq_add(VhostShadowVirtqueue *svq, VirtQueueElement *elem) -{ - unsigned qemu_head; - bool ok = vhost_svq_add_split(svq, elem, &qemu_head); - if (unlikely(!ok)) { - g_free(elem); - return false; - } - - svq->ring_id_maps[qemu_head] = elem; - return true; -} - static void vhost_svq_kick(VhostShadowVirtqueue *svq) { + bool needs_kick; + /* * We need to expose the available array entries before checking the used * flags */ smp_mb(); - if (svq->vring.used->flags & VRING_USED_F_NO_NOTIFY) { + + if (virtio_vdev_has_feature(svq->vdev, VIRTIO_RING_F_EVENT_IDX)) { + uint16_t avail_event = *(uint16_t *)(&svq->vring.used->ring[svq->vring.num]); + needs_kick = vring_need_event(avail_event, svq->shadow_avail_idx, svq->shadow_avail_idx - 1); + } else { + needs_kick = !(svq->vring.used->flags & VRING_USED_F_NO_NOTIFY); + } + + if (!needs_kick) { return; } event_notifier_set(&svq->hdev_kick); } +/** + * Add an element to a SVQ. + * + * Return -EINVAL if element is invalid, -ENOSPC if dev queue is full + */ +int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg, + size_t out_num, const struct iovec *in_sg, size_t in_num, + VirtQueueElement *elem) +{ + unsigned qemu_head; + unsigned ndescs = in_num + out_num; + bool ok; + + if (unlikely(ndescs > vhost_svq_available_slots(svq))) { + return -ENOSPC; + } + + ok = vhost_svq_add_split(svq, out_sg, out_num, in_sg, in_num, &qemu_head); + if (unlikely(!ok)) { + return -EINVAL; + } + + svq->num_free -= ndescs; + svq->desc_state[qemu_head].elem = elem; + svq->desc_state[qemu_head].ndescs = ndescs; + vhost_svq_kick(svq); + return 0; +} + +/* Convenience wrapper to add a guest's element to SVQ */ +static int vhost_svq_add_element(VhostShadowVirtqueue *svq, + VirtQueueElement *elem) +{ + return vhost_svq_add(svq, elem->out_sg, elem->out_num, elem->in_sg, + elem->in_num, elem); +} + /** * Forward available buffers. * @@ -256,8 +301,8 @@ static void vhost_handle_guest_kick(VhostShadowVirtqueue *svq) virtio_queue_set_notification(svq->vq, false); while (true) { - VirtQueueElement *elem; - bool ok; + g_autofree VirtQueueElement *elem = NULL; + int r; if (svq->next_guest_avail_elem) { elem = g_steal_pointer(&svq->next_guest_avail_elem); @@ -269,28 +314,32 @@ static void vhost_handle_guest_kick(VhostShadowVirtqueue *svq) break; } - if (elem->out_num + elem->in_num > vhost_svq_available_slots(svq)) { - /* - * This condition is possible since a contiguous buffer in GPA - * does not imply a contiguous buffer in qemu's VA - * scatter-gather segments. If that happens, the buffer exposed - * to the device needs to be a chain of descriptors at this - * moment. - * - * SVQ cannot hold more available buffers if we are here: - * queue the current guest descriptor and ignore further kicks - * until some elements are used. - */ - svq->next_guest_avail_elem = elem; - return; + if (svq->ops) { + r = svq->ops->avail_handler(svq, elem, svq->ops_opaque); + } else { + r = vhost_svq_add_element(svq, elem); } + if (unlikely(r != 0)) { + if (r == -ENOSPC) { + /* + * This condition is possible since a contiguous buffer in + * GPA does not imply a contiguous buffer in qemu's VA + * scatter-gather segments. If that happens, the buffer + * exposed to the device needs to be a chain of descriptors + * at this moment. + * + * SVQ cannot hold more available buffers if we are here: + * queue the current guest descriptor and ignore kicks + * until some elements are used. + */ + svq->next_guest_avail_elem = g_steal_pointer(&elem); + } - ok = vhost_svq_add(svq, elem); - if (unlikely(!ok)) { - /* VQ is broken, just return and ignore any other kicks */ + /* VQ is full or broken, just return and ignore kicks */ return; } - vhost_svq_kick(svq); + /* elem belongs to SVQ or external caller now */ + elem = NULL; } virtio_queue_set_notification(svq->vq, true); @@ -311,11 +360,12 @@ static void vhost_handle_guest_kick_notifier(EventNotifier *n) static bool vhost_svq_more_used(VhostShadowVirtqueue *svq) { + uint16_t *used_idx = &svq->vring.used->idx; if (svq->last_used_idx != svq->shadow_used_idx) { return true; } - svq->shadow_used_idx = cpu_to_le16(svq->vring.used->idx); + svq->shadow_used_idx = cpu_to_le16(*(volatile uint16_t *)used_idx); return svq->last_used_idx != svq->shadow_used_idx; } @@ -331,15 +381,27 @@ static bool vhost_svq_more_used(VhostShadowVirtqueue *svq) */ static bool vhost_svq_enable_notification(VhostShadowVirtqueue *svq) { - svq->vring.avail->flags &= ~cpu_to_le16(VRING_AVAIL_F_NO_INTERRUPT); - /* Make sure the flag is written before the read of used_idx */ + if (virtio_vdev_has_feature(svq->vdev, VIRTIO_RING_F_EVENT_IDX)) { + uint16_t *used_event = (uint16_t *)&svq->vring.avail->ring[svq->vring.num]; + *used_event = svq->shadow_used_idx; + } else { + svq->vring.avail->flags &= ~cpu_to_le16(VRING_AVAIL_F_NO_INTERRUPT); + } + + /* Make sure the event is enabled before the read of used_idx */ smp_mb(); return !vhost_svq_more_used(svq); } static void vhost_svq_disable_notification(VhostShadowVirtqueue *svq) { - svq->vring.avail->flags |= cpu_to_le16(VRING_AVAIL_F_NO_INTERRUPT); + /* + * No need to disable notification in the event idx case, since used event + * index is already an index too far away. + */ + if (!virtio_vdev_has_feature(svq->vdev, VIRTIO_RING_F_EVENT_IDX)) { + svq->vring.avail->flags |= cpu_to_le16(VRING_AVAIL_F_NO_INTERRUPT); + } } static uint16_t vhost_svq_last_desc_of_chain(const VhostShadowVirtqueue *svq, @@ -376,21 +438,38 @@ static VirtQueueElement *vhost_svq_get_buf(VhostShadowVirtqueue *svq, return NULL; } - if (unlikely(!svq->ring_id_maps[used_elem.id])) { + if (unlikely(!svq->desc_state[used_elem.id].ndescs)) { qemu_log_mask(LOG_GUEST_ERROR, "Device %s says index %u is used, but it was not available", svq->vdev->name, used_elem.id); return NULL; } - num = svq->ring_id_maps[used_elem.id]->in_num + - svq->ring_id_maps[used_elem.id]->out_num; + num = svq->desc_state[used_elem.id].ndescs; + svq->desc_state[used_elem.id].ndescs = 0; last_used_chain = vhost_svq_last_desc_of_chain(svq, num, used_elem.id); svq->desc_next[last_used_chain] = svq->free_head; svq->free_head = used_elem.id; + svq->num_free += num; *len = used_elem.len; - return g_steal_pointer(&svq->ring_id_maps[used_elem.id]); + return g_steal_pointer(&svq->desc_state[used_elem.id].elem); +} + +/** + * Push an element to SVQ, returning it to the guest. + */ +void vhost_svq_push_elem(VhostShadowVirtqueue *svq, + const VirtQueueElement *elem, uint32_t len) +{ + virtqueue_push(svq->vq, elem, len); + if (svq->next_guest_avail_elem) { + /* + * Avail ring was full when vhost_svq_flush was called, so it's a + * good moment to make more descriptors available if possible. + */ + vhost_handle_guest_kick(svq); + } } static void vhost_svq_flush(VhostShadowVirtqueue *svq, @@ -434,6 +513,41 @@ static void vhost_svq_flush(VhostShadowVirtqueue *svq, } while (!vhost_svq_enable_notification(svq)); } +/** + * Poll the SVQ to wait for the device to use the specified number + * of elements and return the total length written by the device. + * + * This function race with main event loop SVQ polling, so extra + * synchronization is needed. + * + * @svq: The svq + * @num: The number of elements that need to be used + */ +size_t vhost_svq_poll(VhostShadowVirtqueue *svq, size_t num) +{ + size_t len = 0; + uint32_t r; + + while (num--) { + int64_t start_us = g_get_monotonic_time(); + + do { + if (vhost_svq_more_used(svq)) { + break; + } + + if (unlikely(g_get_monotonic_time() - start_us > 10e6)) { + return len; + } + } while (true); + + vhost_svq_get_buf(svq, &r); + len += r; + } + + return len; +} + /** * Forward used buffers. * @@ -489,16 +603,16 @@ void vhost_svq_get_vring_addr(const VhostShadowVirtqueue *svq, size_t vhost_svq_driver_area_size(const VhostShadowVirtqueue *svq) { size_t desc_size = sizeof(vring_desc_t) * svq->vring.num; - size_t avail_size = offsetof(vring_avail_t, ring) + - sizeof(uint16_t) * svq->vring.num; + size_t avail_size = offsetof(vring_avail_t, ring[svq->vring.num]) + + sizeof(uint16_t); return ROUND_UP(desc_size + avail_size, qemu_real_host_page_size()); } size_t vhost_svq_device_area_size(const VhostShadowVirtqueue *svq) { - size_t used_size = offsetof(vring_used_t, ring) + - sizeof(vring_used_elem_t) * svq->vring.num; + size_t used_size = offsetof(vring_used_t, ring[svq->vring.num]) + + sizeof(uint16_t); return ROUND_UP(used_size, qemu_real_host_page_size()); } @@ -520,13 +634,13 @@ void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd) event_notifier_set_handler(svq_kick, NULL); } + event_notifier_init_fd(svq_kick, svq_kick_fd); /* * event_notifier_set_handler already checks for guest's notifications if * they arrive at the new file descriptor in the switch, so there is no * need to explicitly check for them. */ if (poll_start) { - event_notifier_init_fd(svq_kick, svq_kick_fd); event_notifier_set(svq_kick); event_notifier_set_handler(svq_kick, vhost_handle_guest_kick_notifier); } @@ -538,29 +652,33 @@ void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd) * @svq: Shadow Virtqueue * @vdev: VirtIO device * @vq: Virtqueue to shadow + * @iova_tree: Tree to perform descriptors translations */ void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev, - VirtQueue *vq) + VirtQueue *vq, VhostIOVATree *iova_tree) { - size_t desc_size, driver_size, device_size; + size_t desc_size; + event_notifier_set_handler(&svq->hdev_call, vhost_svq_handle_call); svq->next_guest_avail_elem = NULL; svq->shadow_avail_idx = 0; svq->shadow_used_idx = 0; svq->last_used_idx = 0; svq->vdev = vdev; svq->vq = vq; + svq->iova_tree = iova_tree; svq->vring.num = virtio_queue_get_num(vdev, virtio_get_queue_index(vq)); - driver_size = vhost_svq_driver_area_size(svq); - device_size = vhost_svq_device_area_size(svq); - svq->vring.desc = qemu_memalign(qemu_real_host_page_size(), driver_size); + svq->num_free = svq->vring.num; + svq->vring.desc = mmap(NULL, vhost_svq_driver_area_size(svq), + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, + -1, 0); desc_size = sizeof(vring_desc_t) * svq->vring.num; svq->vring.avail = (void *)((char *)svq->vring.desc + desc_size); - memset(svq->vring.desc, 0, driver_size); - svq->vring.used = qemu_memalign(qemu_real_host_page_size(), device_size); - memset(svq->vring.used, 0, device_size); - svq->ring_id_maps = g_new0(VirtQueueElement *, svq->vring.num); + svq->vring.used = mmap(NULL, vhost_svq_device_area_size(svq), + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, + -1, 0); + svq->desc_state = g_new0(SVQDescState, svq->vring.num); svq->desc_next = g_new0(uint16_t, svq->vring.num); for (unsigned i = 0; i < svq->vring.num - 1; i++) { svq->desc_next[i] = cpu_to_le16(i + 1); @@ -573,7 +691,7 @@ void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev, */ void vhost_svq_stop(VhostShadowVirtqueue *svq) { - event_notifier_set_handler(&svq->svq_kick, NULL); + vhost_svq_set_svq_kick_fd(svq, VHOST_FILE_UNBIND); g_autofree VirtQueueElement *next_avail_elem = NULL; if (!svq->vq) { @@ -585,62 +703,44 @@ void vhost_svq_stop(VhostShadowVirtqueue *svq) for (unsigned i = 0; i < svq->vring.num; ++i) { g_autofree VirtQueueElement *elem = NULL; - elem = g_steal_pointer(&svq->ring_id_maps[i]); + elem = g_steal_pointer(&svq->desc_state[i].elem); if (elem) { - virtqueue_detach_element(svq->vq, elem, 0); + /* + * TODO: This is ok for networking, but other kinds of devices + * might have problems with just unpop these. + */ + virtqueue_unpop(svq->vq, elem, 0); } } next_avail_elem = g_steal_pointer(&svq->next_guest_avail_elem); if (next_avail_elem) { - virtqueue_detach_element(svq->vq, next_avail_elem, 0); + virtqueue_unpop(svq->vq, next_avail_elem, 0); } svq->vq = NULL; g_free(svq->desc_next); - g_free(svq->ring_id_maps); - qemu_vfree(svq->vring.desc); - qemu_vfree(svq->vring.used); + g_free(svq->desc_state); + munmap(svq->vring.desc, vhost_svq_driver_area_size(svq)); + munmap(svq->vring.used, vhost_svq_device_area_size(svq)); + event_notifier_set_handler(&svq->hdev_call, NULL); } /** * Creates vhost shadow virtqueue, and instructs the vhost device to use the * shadow methods and file descriptors. * - * @iova_tree: Tree to perform descriptors translations - * - * Returns the new virtqueue or NULL. - * - * In case of error, reason is reported through error_report. + * @ops: SVQ owner callbacks + * @ops_opaque: ops opaque pointer */ -VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree) +VhostShadowVirtqueue *vhost_svq_new(const VhostShadowVirtqueueOps *ops, + void *ops_opaque) { - g_autofree VhostShadowVirtqueue *svq = g_new0(VhostShadowVirtqueue, 1); - int r; - - r = event_notifier_init(&svq->hdev_kick, 0); - if (r != 0) { - error_report("Couldn't create kick event notifier: %s (%d)", - g_strerror(errno), errno); - goto err_init_hdev_kick; - } - - r = event_notifier_init(&svq->hdev_call, 0); - if (r != 0) { - error_report("Couldn't create call event notifier: %s (%d)", - g_strerror(errno), errno); - goto err_init_hdev_call; - } + VhostShadowVirtqueue *svq = g_new0(VhostShadowVirtqueue, 1); event_notifier_init_fd(&svq->svq_kick, VHOST_FILE_UNBIND); - event_notifier_set_handler(&svq->hdev_call, vhost_svq_handle_call); - svq->iova_tree = iova_tree; - return g_steal_pointer(&svq); - -err_init_hdev_call: - event_notifier_cleanup(&svq->hdev_kick); - -err_init_hdev_kick: - return NULL; + svq->ops = ops; + svq->ops_opaque = ops_opaque; + return svq; } /** @@ -652,8 +752,5 @@ void vhost_svq_free(gpointer pvq) { VhostShadowVirtqueue *vq = pvq; vhost_svq_stop(vq); - event_notifier_cleanup(&vq->hdev_kick); - event_notifier_set_handler(&vq->hdev_call, NULL); - event_notifier_cleanup(&vq->hdev_call); g_free(vq); } diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h index c132c994e9..19c842a15b 100644 --- a/hw/virtio/vhost-shadow-virtqueue.h +++ b/hw/virtio/vhost-shadow-virtqueue.h @@ -15,6 +15,37 @@ #include "standard-headers/linux/vhost_types.h" #include "hw/virtio/vhost-iova-tree.h" +typedef struct SVQDescState { + VirtQueueElement *elem; + + /* + * Number of descriptors exposed to the device. May or may not match + * guest's + */ + unsigned int ndescs; +} SVQDescState; + +typedef struct VhostShadowVirtqueue VhostShadowVirtqueue; + +/** + * Callback to handle an avail buffer. + * + * @svq: Shadow virtqueue + * @elem: Element placed in the queue by the guest + * @vq_callback_opaque: Opaque + * + * Returns 0 if the vq is running as expected. + * + * Note that ownership of elem is transferred to the callback. + */ +typedef int (*VirtQueueAvailCallback)(VhostShadowVirtqueue *svq, + VirtQueueElement *elem, + void *vq_callback_opaque); + +typedef struct VhostShadowVirtqueueOps { + VirtQueueAvailCallback avail_handler; +} VhostShadowVirtqueueOps; + /* Shadow virtqueue to relay notifications */ typedef struct VhostShadowVirtqueue { /* Shadow vring */ @@ -47,8 +78,8 @@ typedef struct VhostShadowVirtqueue { /* IOVA mapping */ VhostIOVATree *iova_tree; - /* Map for use the guest's descriptors */ - VirtQueueElement **ring_id_maps; + /* SVQ vring descriptors state */ + SVQDescState *desc_state; /* Next VirtQueue element that guest made available */ VirtQueueElement *next_guest_avail_elem; @@ -59,6 +90,12 @@ typedef struct VhostShadowVirtqueue { */ uint16_t *desc_next; + /* Caller callbacks */ + const VhostShadowVirtqueueOps *ops; + + /* Caller callbacks opaque */ + void *ops_opaque; + /* Next head to expose to the device */ uint16_t shadow_avail_idx; @@ -70,10 +107,21 @@ typedef struct VhostShadowVirtqueue { /* Next head to consume from the device */ uint16_t last_used_idx; + + /* Size of SVQ vring free descriptors */ + uint16_t num_free; } VhostShadowVirtqueue; bool vhost_svq_valid_features(uint64_t features, Error **errp); +uint16_t vhost_svq_available_slots(const VhostShadowVirtqueue *svq); +void vhost_svq_push_elem(VhostShadowVirtqueue *svq, + const VirtQueueElement *elem, uint32_t len); +int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg, + size_t out_num, const struct iovec *in_sg, size_t in_num, + VirtQueueElement *elem); +size_t vhost_svq_poll(VhostShadowVirtqueue *svq, size_t num); + void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd); void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd); void vhost_svq_get_vring_addr(const VhostShadowVirtqueue *svq, @@ -82,10 +130,11 @@ size_t vhost_svq_driver_area_size(const VhostShadowVirtqueue *svq); size_t vhost_svq_device_area_size(const VhostShadowVirtqueue *svq); void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev, - VirtQueue *vq); + VirtQueue *vq, VhostIOVATree *iova_tree); void vhost_svq_stop(VhostShadowVirtqueue *svq); -VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree); +VhostShadowVirtqueue *vhost_svq_new(const VhostShadowVirtqueueOps *ops, + void *ops_opaque); void vhost_svq_free(gpointer vq); G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostShadowVirtqueue, vhost_svq_free); diff --git a/hw/virtio/vhost-stub.c b/hw/virtio/vhost-stub.c index c175148fce..52d42adab2 100644 --- a/hw/virtio/vhost-stub.c +++ b/hw/virtio/vhost-stub.c @@ -2,9 +2,14 @@ #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-user.h" -bool vhost_has_free_slot(void) +unsigned int vhost_get_max_memslots(void) { - return true; + return UINT_MAX; +} + +unsigned int vhost_get_free_memslots(void) +{ + return UINT_MAX; } bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp) @@ -15,3 +20,7 @@ bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp) void vhost_user_cleanup(VhostUserState *user) { } + +void vhost_toggle_device_iotlb(VirtIODevice *vdev) +{ +} diff --git a/hw/virtio/vhost-user-base.c b/hw/virtio/vhost-user-base.c new file mode 100644 index 0000000000..a83167191e --- /dev/null +++ b/hw/virtio/vhost-user-base.c @@ -0,0 +1,371 @@ +/* + * Base vhost-user-base implementation. This can be used to derive a + * more fully specified vhost-user backend either generically (see + * vhost-user-device) or via a specific stub for a device which + * encapsulates some fixed parameters. + * + * Copyright (c) 2023 Linaro Ltd + * Author: Alex Bennée + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/vhost-user-base.h" +#include "qemu/error-report.h" + +static void vub_start(VirtIODevice *vdev) +{ + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + VHostUserBase *vub = VHOST_USER_BASE(vdev); + int ret, i; + + if (!k->set_guest_notifiers) { + error_report("binding does not support guest notifiers"); + return; + } + + ret = vhost_dev_enable_notifiers(&vub->vhost_dev, vdev); + if (ret < 0) { + error_report("Error enabling host notifiers: %d", -ret); + return; + } + + ret = k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, true); + if (ret < 0) { + error_report("Error binding guest notifier: %d", -ret); + goto err_host_notifiers; + } + + vub->vhost_dev.acked_features = vdev->guest_features; + + ret = vhost_dev_start(&vub->vhost_dev, vdev, true); + if (ret < 0) { + error_report("Error starting vhost-user-base: %d", -ret); + goto err_guest_notifiers; + } + + /* + * guest_notifier_mask/pending not used yet, so just unmask + * everything here. virtio-pci will do the right thing by + * enabling/disabling irqfd. + */ + for (i = 0; i < vub->vhost_dev.nvqs; i++) { + vhost_virtqueue_mask(&vub->vhost_dev, vdev, i, false); + } + + return; + +err_guest_notifiers: + k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false); +err_host_notifiers: + vhost_dev_disable_notifiers(&vub->vhost_dev, vdev); +} + +static void vub_stop(VirtIODevice *vdev) +{ + VHostUserBase *vub = VHOST_USER_BASE(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + int ret; + + if (!k->set_guest_notifiers) { + return; + } + + vhost_dev_stop(&vub->vhost_dev, vdev, true); + + ret = k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false); + if (ret < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + return; + } + + vhost_dev_disable_notifiers(&vub->vhost_dev, vdev); +} + +static void vub_set_status(VirtIODevice *vdev, uint8_t status) +{ + VHostUserBase *vub = VHOST_USER_BASE(vdev); + bool should_start = virtio_device_should_start(vdev, status); + + if (vhost_dev_is_started(&vub->vhost_dev) == should_start) { + return; + } + + if (should_start) { + vub_start(vdev); + } else { + vub_stop(vdev); + } +} + +/* + * For an implementation where everything is delegated to the backend + * we don't do anything other than return the full feature set offered + * by the daemon (module the reserved feature bit). + */ +static uint64_t vub_get_features(VirtIODevice *vdev, + uint64_t requested_features, Error **errp) +{ + VHostUserBase *vub = VHOST_USER_BASE(vdev); + /* This should be set when the vhost connection initialises */ + g_assert(vub->vhost_dev.features); + return vub->vhost_dev.features & ~(1ULL << VHOST_USER_F_PROTOCOL_FEATURES); +} + +/* + * To handle VirtIO config we need to know the size of the config + * space. We don't cache the config but re-fetch it from the guest + * every time in case something has changed. + */ +static void vub_get_config(VirtIODevice *vdev, uint8_t *config) +{ + VHostUserBase *vub = VHOST_USER_BASE(vdev); + Error *local_err = NULL; + + /* + * There will have been a warning during vhost_dev_init, but lets + * assert here as nothing will go right now. + */ + g_assert(vub->config_size && vub->vhost_user.supports_config == true); + + if (vhost_dev_get_config(&vub->vhost_dev, config, + vub->config_size, &local_err)) { + error_report_err(local_err); + } +} + +static void vub_set_config(VirtIODevice *vdev, const uint8_t *config_data) +{ + VHostUserBase *vub = VHOST_USER_BASE(vdev); + int ret; + + g_assert(vub->config_size && vub->vhost_user.supports_config == true); + + ret = vhost_dev_set_config(&vub->vhost_dev, config_data, + 0, vub->config_size, + VHOST_SET_CONFIG_TYPE_FRONTEND); + if (ret) { + error_report("vhost guest set device config space failed: %d", ret); + return; + } +} + +/* + * When the daemon signals an update to the config we just need to + * signal the guest as we re-read the config on demand above. + */ +static int vub_config_notifier(struct vhost_dev *dev) +{ + virtio_notify_config(dev->vdev); + return 0; +} + +const VhostDevConfigOps vub_config_ops = { + .vhost_dev_config_notifier = vub_config_notifier, +}; + +static void vub_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + /* + * Not normally called; it's the daemon that handles the queue; + * however virtio's cleanup path can call this. + */ +} + +static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserBase *vub) +{ + vhost_user_cleanup(&vub->vhost_user); + + for (int i = 0; i < vub->num_vqs; i++) { + VirtQueue *vq = g_ptr_array_index(vub->vqs, i); + virtio_delete_queue(vq); + } + + virtio_cleanup(vdev); +} + +static int vub_connect(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserBase *vub = VHOST_USER_BASE(vdev); + struct vhost_dev *vhost_dev = &vub->vhost_dev; + + if (vub->connected) { + return 0; + } + vub->connected = true; + + /* + * If we support VHOST_USER_GET_CONFIG we must enable the notifier + * so we can ping the guest when it updates. + */ + if (vub->vhost_user.supports_config) { + vhost_dev_set_config_notifier(vhost_dev, &vub_config_ops); + } + + /* restore vhost state */ + if (virtio_device_started(vdev, vdev->status)) { + vub_start(vdev); + } + + return 0; +} + +static void vub_event(void *opaque, QEMUChrEvent event); + +static void vub_disconnect(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserBase *vub = VHOST_USER_BASE(vdev); + + if (!vub->connected) { + return; + } + vub->connected = false; + + vub_stop(vdev); + vhost_dev_cleanup(&vub->vhost_dev); + + /* Re-instate the event handler for new connections */ + qemu_chr_fe_set_handlers(&vub->chardev, + NULL, NULL, vub_event, + NULL, dev, NULL, true); +} + +static void vub_event(void *opaque, QEMUChrEvent event) +{ + DeviceState *dev = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserBase *vub = VHOST_USER_BASE(vdev); + + switch (event) { + case CHR_EVENT_OPENED: + if (vub_connect(dev) < 0) { + qemu_chr_fe_disconnect(&vub->chardev); + return; + } + break; + case CHR_EVENT_CLOSED: + /* defer close until later to avoid circular close */ + vhost_user_async_close(dev, &vub->chardev, &vub->vhost_dev, + vub_disconnect, vub_event); + break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; + } +} + +static void vub_device_realize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserBase *vub = VHOST_USER_BASE(dev); + int ret; + + if (!vub->chardev.chr) { + error_setg(errp, "vhost-user-base: missing chardev"); + return; + } + + if (!vub->virtio_id) { + error_setg(errp, "vhost-user-base: need to define device id"); + return; + } + + if (!vub->num_vqs) { + vub->num_vqs = 1; /* reasonable default? */ + } + + if (!vub->vq_size) { + vub->vq_size = 64; + } + + /* + * We can't handle config requests unless we know the size of the + * config region, specialisations of the vhost-user-base will be + * able to set this. + */ + if (vub->config_size) { + vub->vhost_user.supports_config = true; + } + + if (!vhost_user_init(&vub->vhost_user, &vub->chardev, errp)) { + return; + } + + virtio_init(vdev, vub->virtio_id, vub->config_size); + + /* + * Disable guest notifiers, by default all notifications will be via the + * asynchronous vhost-user socket. + */ + vdev->use_guest_notifier_mask = false; + + /* Allocate queues */ + vub->vqs = g_ptr_array_sized_new(vub->num_vqs); + for (int i = 0; i < vub->num_vqs; i++) { + g_ptr_array_add(vub->vqs, + virtio_add_queue(vdev, vub->vq_size, + vub_handle_output)); + } + + vub->vhost_dev.nvqs = vub->num_vqs; + vub->vhost_dev.vqs = g_new0(struct vhost_virtqueue, vub->vhost_dev.nvqs); + + /* connect to backend */ + ret = vhost_dev_init(&vub->vhost_dev, &vub->vhost_user, + VHOST_BACKEND_TYPE_USER, 0, errp); + + if (ret < 0) { + do_vhost_user_cleanup(vdev, vub); + } + + qemu_chr_fe_set_handlers(&vub->chardev, NULL, NULL, vub_event, NULL, + dev, NULL, true); +} + +static void vub_device_unrealize(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserBase *vub = VHOST_USER_BASE(dev); + struct vhost_virtqueue *vhost_vqs = vub->vhost_dev.vqs; + + /* This will stop vhost backend if appropriate. */ + vub_set_status(vdev, 0); + vhost_dev_cleanup(&vub->vhost_dev); + g_free(vhost_vqs); + do_vhost_user_cleanup(vdev, vub); +} + +static void vub_class_init(ObjectClass *klass, void *data) +{ + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + + vdc->realize = vub_device_realize; + vdc->unrealize = vub_device_unrealize; + vdc->get_features = vub_get_features; + vdc->get_config = vub_get_config; + vdc->set_config = vub_set_config; + vdc->set_status = vub_set_status; +} + +static const TypeInfo vub_types[] = { + { + .name = TYPE_VHOST_USER_BASE, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VHostUserBase), + .class_init = vub_class_init, + .class_size = sizeof(VHostUserBaseClass), + .abstract = true + } +}; + +DEFINE_TYPES(vub_types) diff --git a/hw/virtio/vhost-user-device-pci.c b/hw/virtio/vhost-user-device-pci.c new file mode 100644 index 0000000000..efaf55d3dd --- /dev/null +++ b/hw/virtio/vhost-user-device-pci.c @@ -0,0 +1,72 @@ +/* + * Vhost-user generic virtio device PCI glue + * + * Copyright (c) 2023 Linaro Ltd + * Author: Alex Bennée + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/vhost-user-base.h" +#include "hw/virtio/virtio-pci.h" + +struct VHostUserDevicePCI { + VirtIOPCIProxy parent_obj; + + VHostUserBase vub; +}; + +#define TYPE_VHOST_USER_DEVICE_PCI "vhost-user-device-pci-base" + +OBJECT_DECLARE_SIMPLE_TYPE(VHostUserDevicePCI, VHOST_USER_DEVICE_PCI) + +static void vhost_user_device_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostUserDevicePCI *dev = VHOST_USER_DEVICE_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vub); + + vpci_dev->nvectors = 1; + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); +} + +static void vhost_user_device_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); + + /* Reason: stop users confusing themselves */ + dc->user_creatable = false; + + k->realize = vhost_user_device_pci_realize; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + 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; + pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER; +} + +static void vhost_user_device_pci_instance_init(Object *obj) +{ + VHostUserDevicePCI *dev = VHOST_USER_DEVICE_PCI(obj); + + virtio_instance_init_common(obj, &dev->vub, sizeof(dev->vub), + TYPE_VHOST_USER_DEVICE); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_device_pci_info = { + .base_name = TYPE_VHOST_USER_DEVICE_PCI, + .non_transitional_name = "vhost-user-device-pci", + .instance_size = sizeof(VHostUserDevicePCI), + .instance_init = vhost_user_device_pci_instance_init, + .class_init = vhost_user_device_pci_class_init, +}; + +static void vhost_user_device_pci_register(void) +{ + virtio_pci_types_register(&vhost_user_device_pci_info); +} + +type_init(vhost_user_device_pci_register); diff --git a/hw/virtio/vhost-user-device.c b/hw/virtio/vhost-user-device.c new file mode 100644 index 0000000000..67aa934710 --- /dev/null +++ b/hw/virtio/vhost-user-device.c @@ -0,0 +1,64 @@ +/* + * Generic vhost-user-device implementation for any vhost-user-backend + * + * This is a concrete implementation of vhost-user-base which can be + * configured via properties. It is useful for development and + * prototyping. It expects configuration details (if any) to be + * handled by the vhost-user daemon itself. + * + * Copyright (c) 2023 Linaro Ltd + * Author: Alex Bennée + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/vhost-user-base.h" +#include "qemu/error-report.h" + +/* + * The following is a concrete implementation of the base class which + * allows the user to define the key parameters via the command line. + */ + +static const VMStateDescription vud_vmstate = { + .name = "vhost-user-device", + .unmigratable = 1, +}; + +static Property vud_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), + DEFINE_PROP_UINT16("virtio-id", VHostUserBase, virtio_id, 0), + DEFINE_PROP_UINT32("vq_size", VHostUserBase, vq_size, 64), + DEFINE_PROP_UINT32("num_vqs", VHostUserBase, num_vqs, 1), + DEFINE_PROP_UINT32("config_size", VHostUserBase, config_size, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vud_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + /* Reason: stop inexperienced users confusing themselves */ + dc->user_creatable = false; + + device_class_set_props(dc, vud_properties); + dc->vmsd = &vud_vmstate; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo vud_info = { + .name = TYPE_VHOST_USER_DEVICE, + .parent = TYPE_VHOST_USER_BASE, + .class_init = vud_class_init, +}; + +static void vu_register_types(void) +{ + type_register_static(&vud_info); +} + +type_init(vu_register_types) diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c index e513e4fdda..cca2cd41be 100644 --- a/hw/virtio/vhost-user-fs.c +++ b/hw/virtio/vhost-user-fs.c @@ -20,6 +20,7 @@ #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" #include "qemu/error-report.h" +#include "hw/virtio/vhost.h" #include "hw/virtio/vhost-user-fs.h" #include "monitor/monitor.h" #include "sysemu/sysemu.h" @@ -31,6 +32,7 @@ static const int user_feature_bits[] = { VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_RING_PACKED, VIRTIO_F_IOMMU_PLATFORM, + VIRTIO_F_RING_RESET, VHOST_INVALID_FEATURE_BIT }; @@ -74,7 +76,7 @@ static void vuf_start(VirtIODevice *vdev) } fs->vhost_dev.acked_features = vdev->guest_features; - ret = vhost_dev_start(&fs->vhost_dev, vdev); + ret = vhost_dev_start(&fs->vhost_dev, vdev, true); if (ret < 0) { error_report("Error starting vhost: %d", -ret); goto err_guest_notifiers; @@ -108,7 +110,7 @@ static void vuf_stop(VirtIODevice *vdev) return; } - vhost_dev_stop(&fs->vhost_dev, vdev); + vhost_dev_stop(&fs->vhost_dev, vdev, true); ret = k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false); if (ret < 0) { @@ -122,13 +124,9 @@ static void vuf_stop(VirtIODevice *vdev) static void vuf_set_status(VirtIODevice *vdev, uint8_t status) { VHostUserFS *fs = VHOST_USER_FS(vdev); - bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; + bool should_start = virtio_device_should_start(vdev, status); - if (!vdev->vm_running) { - should_start = false; - } - - if (fs->vhost_dev.started == should_start) { + if (vhost_dev_is_started(&fs->vhost_dev) == should_start) { return; } @@ -161,6 +159,15 @@ static void vuf_guest_notifier_mask(VirtIODevice *vdev, int idx, { VHostUserFS *fs = VHOST_USER_FS(vdev); + /* + * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1 + * as the macro of configure interrupt's IDX, If this driver does not + * support, the function will return + */ + + if (idx == VIRTIO_CONFIG_IRQ_IDX) { + return; + } vhost_virtqueue_mask(&fs->vhost_dev, vdev, idx, mask); } @@ -168,6 +175,15 @@ static bool vuf_guest_notifier_pending(VirtIODevice *vdev, int idx) { VHostUserFS *fs = VHOST_USER_FS(vdev); + /* + * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1 + * as the macro of configure interrupt's IDX, If this driver does not + * support, the function will return + */ + + if (idx == VIRTIO_CONFIG_IRQ_IDX) { + return false; + } return vhost_virtqueue_pending(&fs->vhost_dev, idx); } @@ -257,6 +273,7 @@ static void vuf_device_unrealize(DeviceState *dev) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostUserFS *fs = VHOST_USER_FS(dev); + struct vhost_virtqueue *vhost_vqs = fs->vhost_dev.vqs; int i; /* This will stop vhost backend if appropriate. */ @@ -272,8 +289,7 @@ static void vuf_device_unrealize(DeviceState *dev) } g_free(fs->req_vqs); virtio_cleanup(vdev); - g_free(fs->vhost_dev.vqs); - fs->vhost_dev.vqs = NULL; + g_free(vhost_vqs); } static struct vhost_dev *vuf_get_vhost(VirtIODevice *vdev) @@ -282,9 +298,108 @@ static struct vhost_dev *vuf_get_vhost(VirtIODevice *vdev) return &fs->vhost_dev; } +/** + * Fetch the internal state from virtiofsd and save it to `f`. + */ +static int vuf_save_state(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + VirtIODevice *vdev = pv; + VHostUserFS *fs = VHOST_USER_FS(vdev); + Error *local_error = NULL; + int ret; + + ret = vhost_save_backend_state(&fs->vhost_dev, f, &local_error); + if (ret < 0) { + error_reportf_err(local_error, + "Error saving back-end state of %s device %s " + "(tag: \"%s\"): ", + vdev->name, vdev->parent_obj.canonical_path, + fs->conf.tag ?: ""); + return ret; + } + + return 0; +} + +/** + * Load virtiofsd's internal state from `f` and send it over to virtiofsd. + */ +static int vuf_load_state(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + VirtIODevice *vdev = pv; + VHostUserFS *fs = VHOST_USER_FS(vdev); + Error *local_error = NULL; + int ret; + + ret = vhost_load_backend_state(&fs->vhost_dev, f, &local_error); + if (ret < 0) { + error_reportf_err(local_error, + "Error loading back-end state of %s device %s " + "(tag: \"%s\"): ", + vdev->name, vdev->parent_obj.canonical_path, + fs->conf.tag ?: ""); + return ret; + } + + return 0; +} + +static bool vuf_is_internal_migration(void *opaque) +{ + /* TODO: Return false when an external migration is requested */ + return true; +} + +static int vuf_check_migration_support(void *opaque) +{ + VirtIODevice *vdev = opaque; + VHostUserFS *fs = VHOST_USER_FS(vdev); + + if (!vhost_supports_device_state(&fs->vhost_dev)) { + error_report("Back-end of %s device %s (tag: \"%s\") does not support " + "migration through qemu", + vdev->name, vdev->parent_obj.canonical_path, + fs->conf.tag ?: ""); + return -ENOTSUP; + } + + return 0; +} + +static const VMStateDescription vuf_backend_vmstate; + static const VMStateDescription vuf_vmstate = { .name = "vhost-user-fs", - .unmigratable = 1, + .version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_VIRTIO_DEVICE, + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vuf_backend_vmstate, + NULL, + } +}; + +static const VMStateDescription vuf_backend_vmstate = { + .name = "vhost-user-fs-backend", + .version_id = 0, + .needed = vuf_is_internal_migration, + .pre_load = vuf_check_migration_support, + .pre_save = vuf_check_migration_support, + .fields = (const VMStateField[]) { + { + .name = "back-end", + .info = &(const VMStateInfo) { + .name = "virtio-fs back-end state", + .get = vuf_load_state, + .put = vuf_save_state, + }, + }, + VMSTATE_END_OF_LIST() + }, }; static Property vuf_properties[] = { diff --git a/hw/virtio/vhost-user-gpio-pci.c b/hw/virtio/vhost-user-gpio-pci.c new file mode 100644 index 0000000000..b3028a24a1 --- /dev/null +++ b/hw/virtio/vhost-user-gpio-pci.c @@ -0,0 +1,69 @@ +/* + * Vhost-user gpio virtio device PCI glue + * + * Copyright (c) 2022 Viresh Kumar + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/vhost-user-gpio.h" +#include "hw/virtio/virtio-pci.h" + +struct VHostUserGPIOPCI { + VirtIOPCIProxy parent_obj; + VHostUserGPIO vdev; +}; + +typedef struct VHostUserGPIOPCI VHostUserGPIOPCI; + +#define TYPE_VHOST_USER_GPIO_PCI "vhost-user-gpio-pci-base" + +DECLARE_INSTANCE_CHECKER(VHostUserGPIOPCI, VHOST_USER_GPIO_PCI, + TYPE_VHOST_USER_GPIO_PCI) + +static void vhost_user_gpio_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostUserGPIOPCI *dev = VHOST_USER_GPIO_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + vpci_dev->nvectors = 1; + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); +} + +static void vhost_user_gpio_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 = vhost_user_gpio_pci_realize; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + 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; + pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER; +} + +static void vhost_user_gpio_pci_instance_init(Object *obj) +{ + VHostUserGPIOPCI *dev = VHOST_USER_GPIO_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_GPIO); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_gpio_pci_info = { + .base_name = TYPE_VHOST_USER_GPIO_PCI, + .non_transitional_name = "vhost-user-gpio-pci", + .instance_size = sizeof(VHostUserGPIOPCI), + .instance_init = vhost_user_gpio_pci_instance_init, + .class_init = vhost_user_gpio_pci_class_init, +}; + +static void vhost_user_gpio_pci_register(void) +{ + virtio_pci_types_register(&vhost_user_gpio_pci_info); +} + +type_init(vhost_user_gpio_pci_register); diff --git a/hw/virtio/vhost-user-gpio.c b/hw/virtio/vhost-user-gpio.c new file mode 100644 index 0000000000..9f37c25415 --- /dev/null +++ b/hw/virtio/vhost-user-gpio.c @@ -0,0 +1,64 @@ +/* + * Vhost-user GPIO virtio device + * + * Copyright (c) 2022 Viresh Kumar + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/vhost-user-gpio.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_gpio.h" + +static Property vgpio_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vgpio_realize(DeviceState *dev, Error **errp) +{ + VHostUserBase *vub = VHOST_USER_BASE(dev); + VHostUserBaseClass *vubc = VHOST_USER_BASE_GET_CLASS(dev); + + /* Fixed for GPIO */ + vub->virtio_id = VIRTIO_ID_GPIO; + vub->num_vqs = 2; + vub->config_size = sizeof(struct virtio_gpio_config); + + vubc->parent_realize(dev, errp); +} + +static const VMStateDescription vu_gpio_vmstate = { + .name = "vhost-user-gpio", + .unmigratable = 1, +}; + +static void vu_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass); + + dc->vmsd = &vu_gpio_vmstate; + device_class_set_props(dc, vgpio_properties); + device_class_set_parent_realize(dc, vgpio_realize, + &vubc->parent_realize); + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo vu_gpio_info = { + .name = TYPE_VHOST_USER_GPIO, + .parent = TYPE_VHOST_USER_BASE, + .instance_size = sizeof(VHostUserGPIO), + .class_init = vu_gpio_class_init, +}; + +static void vu_gpio_register_types(void) +{ + type_register_static(&vu_gpio_info); +} + +type_init(vu_gpio_register_types) diff --git a/hw/virtio/vhost-user-i2c.c b/hw/virtio/vhost-user-i2c.c index 6020eee093..a464f5e039 100644 --- a/hw/virtio/vhost-user-i2c.c +++ b/hw/virtio/vhost-user-i2c.c @@ -14,239 +14,22 @@ #include "qemu/error-report.h" #include "standard-headers/linux/virtio_ids.h" -static const int feature_bits[] = { - VIRTIO_I2C_F_ZERO_LENGTH_REQUEST, - VHOST_INVALID_FEATURE_BIT +static Property vi2c_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), + DEFINE_PROP_END_OF_LIST(), }; -static void vu_i2c_start(VirtIODevice *vdev) +static void vi2c_realize(DeviceState *dev, Error **errp) { - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VHostUserI2C *i2c = VHOST_USER_I2C(vdev); - int ret, i; + VHostUserBase *vub = VHOST_USER_BASE(dev); + VHostUserBaseClass *vubc = VHOST_USER_BASE_GET_CLASS(dev); - if (!k->set_guest_notifiers) { - error_report("binding does not support guest notifiers"); - return; - } + /* Fixed for I2C */ + vub->virtio_id = VIRTIO_ID_I2C_ADAPTER; + vub->num_vqs = 1; + vub->vq_size = 4; - ret = vhost_dev_enable_notifiers(&i2c->vhost_dev, vdev); - if (ret < 0) { - error_report("Error enabling host notifiers: %d", -ret); - return; - } - - ret = k->set_guest_notifiers(qbus->parent, i2c->vhost_dev.nvqs, true); - if (ret < 0) { - error_report("Error binding guest notifier: %d", -ret); - goto err_host_notifiers; - } - - i2c->vhost_dev.acked_features = vdev->guest_features; - - ret = vhost_dev_start(&i2c->vhost_dev, vdev); - if (ret < 0) { - error_report("Error starting vhost-user-i2c: %d", -ret); - goto err_guest_notifiers; - } - - /* - * guest_notifier_mask/pending not used yet, so just unmask - * everything here. virtio-pci will do the right thing by - * enabling/disabling irqfd. - */ - for (i = 0; i < i2c->vhost_dev.nvqs; i++) { - vhost_virtqueue_mask(&i2c->vhost_dev, vdev, i, false); - } - - return; - -err_guest_notifiers: - k->set_guest_notifiers(qbus->parent, i2c->vhost_dev.nvqs, false); -err_host_notifiers: - vhost_dev_disable_notifiers(&i2c->vhost_dev, vdev); -} - -static void vu_i2c_stop(VirtIODevice *vdev) -{ - VHostUserI2C *i2c = VHOST_USER_I2C(vdev); - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - int ret; - - if (!k->set_guest_notifiers) { - return; - } - - vhost_dev_stop(&i2c->vhost_dev, vdev); - - ret = k->set_guest_notifiers(qbus->parent, i2c->vhost_dev.nvqs, false); - if (ret < 0) { - error_report("vhost guest notifier cleanup failed: %d", ret); - return; - } - - vhost_dev_disable_notifiers(&i2c->vhost_dev, vdev); -} - -static void vu_i2c_set_status(VirtIODevice *vdev, uint8_t status) -{ - VHostUserI2C *i2c = VHOST_USER_I2C(vdev); - bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; - - if (!vdev->vm_running) { - should_start = false; - } - - if (i2c->vhost_dev.started == should_start) { - return; - } - - if (should_start) { - vu_i2c_start(vdev); - } else { - vu_i2c_stop(vdev); - } -} - -static uint64_t vu_i2c_get_features(VirtIODevice *vdev, - uint64_t requested_features, Error **errp) -{ - VHostUserI2C *i2c = VHOST_USER_I2C(vdev); - - virtio_add_feature(&requested_features, VIRTIO_I2C_F_ZERO_LENGTH_REQUEST); - return vhost_get_features(&i2c->vhost_dev, feature_bits, requested_features); -} - -static void vu_i2c_handle_output(VirtIODevice *vdev, VirtQueue *vq) -{ - /* - * Not normally called; it's the daemon that handles the queue; - * however virtio's cleanup path can call this. - */ -} - -static void vu_i2c_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) -{ - VHostUserI2C *i2c = VHOST_USER_I2C(vdev); - - vhost_virtqueue_mask(&i2c->vhost_dev, vdev, idx, mask); -} - -static bool vu_i2c_guest_notifier_pending(VirtIODevice *vdev, int idx) -{ - VHostUserI2C *i2c = VHOST_USER_I2C(vdev); - - return vhost_virtqueue_pending(&i2c->vhost_dev, idx); -} - -static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserI2C *i2c) -{ - vhost_user_cleanup(&i2c->vhost_user); - virtio_delete_queue(i2c->vq); - virtio_cleanup(vdev); - g_free(i2c->vhost_dev.vqs); - i2c->vhost_dev.vqs = NULL; -} - -static int vu_i2c_connect(DeviceState *dev) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserI2C *i2c = VHOST_USER_I2C(vdev); - - if (i2c->connected) { - return 0; - } - i2c->connected = true; - - /* restore vhost state */ - if (virtio_device_started(vdev, vdev->status)) { - vu_i2c_start(vdev); - } - - return 0; -} - -static void vu_i2c_disconnect(DeviceState *dev) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserI2C *i2c = VHOST_USER_I2C(vdev); - - if (!i2c->connected) { - return; - } - i2c->connected = false; - - if (i2c->vhost_dev.started) { - vu_i2c_stop(vdev); - } -} - -static void vu_i2c_event(void *opaque, QEMUChrEvent event) -{ - DeviceState *dev = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserI2C *i2c = VHOST_USER_I2C(vdev); - - switch (event) { - case CHR_EVENT_OPENED: - if (vu_i2c_connect(dev) < 0) { - qemu_chr_fe_disconnect(&i2c->chardev); - return; - } - break; - case CHR_EVENT_CLOSED: - vu_i2c_disconnect(dev); - break; - case CHR_EVENT_BREAK: - case CHR_EVENT_MUX_IN: - case CHR_EVENT_MUX_OUT: - /* Ignore */ - break; - } -} - -static void vu_i2c_device_realize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserI2C *i2c = VHOST_USER_I2C(dev); - int ret; - - if (!i2c->chardev.chr) { - error_setg(errp, "vhost-user-i2c: missing chardev"); - return; - } - - if (!vhost_user_init(&i2c->vhost_user, &i2c->chardev, errp)) { - return; - } - - virtio_init(vdev, VIRTIO_ID_I2C_ADAPTER, 0); - - i2c->vhost_dev.nvqs = 1; - i2c->vq = virtio_add_queue(vdev, 4, vu_i2c_handle_output); - i2c->vhost_dev.vqs = g_new0(struct vhost_virtqueue, i2c->vhost_dev.nvqs); - - ret = vhost_dev_init(&i2c->vhost_dev, &i2c->vhost_user, - VHOST_BACKEND_TYPE_USER, 0, errp); - if (ret < 0) { - do_vhost_user_cleanup(vdev, i2c); - } - - qemu_chr_fe_set_handlers(&i2c->chardev, NULL, NULL, vu_i2c_event, NULL, - dev, NULL, true); -} - -static void vu_i2c_device_unrealize(DeviceState *dev) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserI2C *i2c = VHOST_USER_I2C(dev); - - /* This will stop vhost backend if appropriate. */ - vu_i2c_set_status(vdev, 0); - vhost_dev_cleanup(&i2c->vhost_dev); - do_vhost_user_cleanup(vdev, i2c); + vubc->parent_realize(dev, errp); } static const VMStateDescription vu_i2c_vmstate = { @@ -254,30 +37,21 @@ static const VMStateDescription vu_i2c_vmstate = { .unmigratable = 1, }; -static Property vu_i2c_properties[] = { - DEFINE_PROP_CHR("chardev", VHostUserI2C, chardev), - DEFINE_PROP_END_OF_LIST(), -}; - static void vu_i2c_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass); - device_class_set_props(dc, vu_i2c_properties); dc->vmsd = &vu_i2c_vmstate; + device_class_set_props(dc, vi2c_properties); + device_class_set_parent_realize(dc, vi2c_realize, + &vubc->parent_realize); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - vdc->realize = vu_i2c_device_realize; - vdc->unrealize = vu_i2c_device_unrealize; - vdc->get_features = vu_i2c_get_features; - vdc->set_status = vu_i2c_set_status; - vdc->guest_notifier_mask = vu_i2c_guest_notifier_mask; - vdc->guest_notifier_pending = vu_i2c_guest_notifier_pending; } static const TypeInfo vu_i2c_info = { .name = TYPE_VHOST_USER_I2C, - .parent = TYPE_VIRTIO_DEVICE, + .parent = TYPE_VHOST_USER_BASE, .instance_size = sizeof(VHostUserI2C), .class_init = vu_i2c_class_init, }; diff --git a/hw/virtio/vhost-user-input-pci.c b/hw/virtio/vhost-user-input-pci.c index b858898a36..3f4761ce88 100644 --- a/hw/virtio/vhost-user-input-pci.c +++ b/hw/virtio/vhost-user-input-pci.c @@ -30,9 +30,6 @@ static void vhost_user_input_pci_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vhi, sizeof(dev->vhi), TYPE_VHOST_USER_INPUT); - - object_property_add_alias(obj, "chardev", - OBJECT(&dev->vhi), "chardev"); } static const VirtioPCIDeviceTypeInfo vhost_user_input_pci_info = { diff --git a/hw/virtio/vhost-user-input.c b/hw/virtio/vhost-user-input.c new file mode 100644 index 0000000000..bedec0468c --- /dev/null +++ b/hw/virtio/vhost-user-input.c @@ -0,0 +1,58 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/virtio/virtio-input.h" + +static Property vinput_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vinput_realize(DeviceState *dev, Error **errp) +{ + VHostUserBase *vub = VHOST_USER_BASE(dev); + VHostUserBaseClass *vubc = VHOST_USER_BASE_GET_CLASS(dev); + + /* Fixed for input device */ + vub->virtio_id = VIRTIO_ID_INPUT; + vub->num_vqs = 2; + vub->vq_size = 4; + vub->config_size = sizeof(virtio_input_config); + + vubc->parent_realize(dev, errp); +} + +static const VMStateDescription vmstate_vhost_input = { + .name = "vhost-user-input", + .unmigratable = 1, +}; + +static void vhost_input_class_init(ObjectClass *klass, void *data) +{ + VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_vhost_input; + device_class_set_props(dc, vinput_properties); + device_class_set_parent_realize(dc, vinput_realize, + &vubc->parent_realize); + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo vhost_input_info = { + .name = TYPE_VHOST_USER_INPUT, + .parent = TYPE_VHOST_USER_BASE, + .instance_size = sizeof(VHostUserInput), + .class_init = vhost_input_class_init, +}; + +static void vhost_input_register_types(void) +{ + type_register_static(&vhost_input_info); +} + +type_init(vhost_input_register_types) diff --git a/hw/virtio/vhost-user-rng.c b/hw/virtio/vhost-user-rng.c index 3a7bf8e32d..01879c863d 100644 --- a/hw/virtio/vhost-user-rng.c +++ b/hw/virtio/vhost-user-rng.c @@ -3,7 +3,7 @@ * * Copyright (c) 2021 Mathieu Poirier * - * Implementation seriously tailored on vhost-user-i2c.c + * Simple wrapper of the generic vhost-user-device. * * SPDX-License-Identifier: GPL-2.0-or-later */ @@ -13,277 +13,47 @@ #include "hw/qdev-properties.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/vhost-user-rng.h" -#include "qemu/error-report.h" #include "standard-headers/linux/virtio_ids.h" -static void vu_rng_start(VirtIODevice *vdev) -{ - VHostUserRNG *rng = VHOST_USER_RNG(vdev); - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - int ret; - int i; - - if (!k->set_guest_notifiers) { - error_report("binding does not support guest notifiers"); - return; - } - - ret = vhost_dev_enable_notifiers(&rng->vhost_dev, vdev); - if (ret < 0) { - error_report("Error enabling host notifiers: %d", -ret); - return; - } - - ret = k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, true); - if (ret < 0) { - error_report("Error binding guest notifier: %d", -ret); - goto err_host_notifiers; - } - - rng->vhost_dev.acked_features = vdev->guest_features; - ret = vhost_dev_start(&rng->vhost_dev, vdev); - if (ret < 0) { - error_report("Error starting vhost-user-rng: %d", -ret); - goto err_guest_notifiers; - } - - /* - * guest_notifier_mask/pending not used yet, so just unmask - * everything here. virtio-pci will do the right thing by - * enabling/disabling irqfd. - */ - for (i = 0; i < rng->vhost_dev.nvqs; i++) { - vhost_virtqueue_mask(&rng->vhost_dev, vdev, i, false); - } - - return; - -err_guest_notifiers: - k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, false); -err_host_notifiers: - vhost_dev_disable_notifiers(&rng->vhost_dev, vdev); -} - -static void vu_rng_stop(VirtIODevice *vdev) -{ - VHostUserRNG *rng = VHOST_USER_RNG(vdev); - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - int ret; - - if (!k->set_guest_notifiers) { - return; - } - - vhost_dev_stop(&rng->vhost_dev, vdev); - - ret = k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, false); - if (ret < 0) { - error_report("vhost guest notifier cleanup failed: %d", ret); - return; - } - - vhost_dev_disable_notifiers(&rng->vhost_dev, vdev); -} - -static void vu_rng_set_status(VirtIODevice *vdev, uint8_t status) -{ - VHostUserRNG *rng = VHOST_USER_RNG(vdev); - bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; - - if (!vdev->vm_running) { - should_start = false; - } - - if (rng->vhost_dev.started == should_start) { - return; - } - - if (should_start) { - vu_rng_start(vdev); - } else { - vu_rng_stop(vdev); - } -} - -static uint64_t vu_rng_get_features(VirtIODevice *vdev, - uint64_t requested_features, Error **errp) -{ - /* No feature bits used yet */ - return requested_features; -} - -static void vu_rng_handle_output(VirtIODevice *vdev, VirtQueue *vq) -{ - /* - * Not normally called; it's the daemon that handles the queue; - * however virtio's cleanup path can call this. - */ -} - -static void vu_rng_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) -{ - VHostUserRNG *rng = VHOST_USER_RNG(vdev); - - vhost_virtqueue_mask(&rng->vhost_dev, vdev, idx, mask); -} - -static bool vu_rng_guest_notifier_pending(VirtIODevice *vdev, int idx) -{ - VHostUserRNG *rng = VHOST_USER_RNG(vdev); - - return vhost_virtqueue_pending(&rng->vhost_dev, idx); -} - -static void vu_rng_connect(DeviceState *dev) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserRNG *rng = VHOST_USER_RNG(vdev); - - if (rng->connected) { - return; - } - - rng->connected = true; - - /* restore vhost state */ - if (virtio_device_started(vdev, vdev->status)) { - vu_rng_start(vdev); - } -} - -static void vu_rng_disconnect(DeviceState *dev) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserRNG *rng = VHOST_USER_RNG(vdev); - - if (!rng->connected) { - return; - } - - rng->connected = false; - - if (rng->vhost_dev.started) { - vu_rng_stop(vdev); - } -} - -static void vu_rng_event(void *opaque, QEMUChrEvent event) -{ - DeviceState *dev = opaque; - - switch (event) { - case CHR_EVENT_OPENED: - vu_rng_connect(dev); - break; - case CHR_EVENT_CLOSED: - vu_rng_disconnect(dev); - break; - case CHR_EVENT_BREAK: - case CHR_EVENT_MUX_IN: - case CHR_EVENT_MUX_OUT: - /* Ignore */ - break; - } -} - -static void vu_rng_device_realize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserRNG *rng = VHOST_USER_RNG(dev); - int ret; - - if (!rng->chardev.chr) { - error_setg(errp, "missing chardev"); - return; - } - - if (!vhost_user_init(&rng->vhost_user, &rng->chardev, errp)) { - return; - } - - virtio_init(vdev, VIRTIO_ID_RNG, 0); - - rng->req_vq = virtio_add_queue(vdev, 4, vu_rng_handle_output); - if (!rng->req_vq) { - error_setg_errno(errp, -1, "virtio_add_queue() failed"); - goto virtio_add_queue_failed; - } - - rng->vhost_dev.nvqs = 1; - rng->vhost_dev.vqs = g_new0(struct vhost_virtqueue, rng->vhost_dev.nvqs); - ret = vhost_dev_init(&rng->vhost_dev, &rng->vhost_user, - VHOST_BACKEND_TYPE_USER, 0, errp); - if (ret < 0) { - error_setg_errno(errp, -ret, "vhost_dev_init() failed"); - goto vhost_dev_init_failed; - } - - qemu_chr_fe_set_handlers(&rng->chardev, NULL, NULL, vu_rng_event, NULL, - dev, NULL, true); - - return; - -vhost_dev_init_failed: - virtio_delete_queue(rng->req_vq); -virtio_add_queue_failed: - virtio_cleanup(vdev); - vhost_user_cleanup(&rng->vhost_user); -} - -static void vu_rng_device_unrealize(DeviceState *dev) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserRNG *rng = VHOST_USER_RNG(dev); - - vu_rng_set_status(vdev, 0); - - vhost_dev_cleanup(&rng->vhost_dev); - g_free(rng->vhost_dev.vqs); - rng->vhost_dev.vqs = NULL; - virtio_delete_queue(rng->req_vq); - virtio_cleanup(vdev); - vhost_user_cleanup(&rng->vhost_user); -} - -static struct vhost_dev *vu_rng_get_vhost(VirtIODevice *vdev) -{ - VHostUserRNG *rng = VHOST_USER_RNG(vdev); - return &rng->vhost_dev; -} - static const VMStateDescription vu_rng_vmstate = { .name = "vhost-user-rng", .unmigratable = 1, }; -static Property vu_rng_properties[] = { - DEFINE_PROP_CHR("chardev", VHostUserRNG, chardev), +static Property vrng_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), DEFINE_PROP_END_OF_LIST(), }; +static void vu_rng_base_realize(DeviceState *dev, Error **errp) +{ + VHostUserBase *vub = VHOST_USER_BASE(dev); + VHostUserBaseClass *vubs = VHOST_USER_BASE_GET_CLASS(dev); + + /* Fixed for RNG */ + vub->virtio_id = VIRTIO_ID_RNG; + vub->num_vqs = 1; + vub->vq_size = 4; + + vubs->parent_realize(dev, errp); +} + static void vu_rng_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass); - device_class_set_props(dc, vu_rng_properties); dc->vmsd = &vu_rng_vmstate; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + device_class_set_props(dc, vrng_properties); + device_class_set_parent_realize(dc, vu_rng_base_realize, + &vubc->parent_realize); - vdc->realize = vu_rng_device_realize; - vdc->unrealize = vu_rng_device_unrealize; - vdc->get_features = vu_rng_get_features; - vdc->set_status = vu_rng_set_status; - vdc->guest_notifier_mask = vu_rng_guest_notifier_mask; - vdc->guest_notifier_pending = vu_rng_guest_notifier_pending; - vdc->get_vhost = vu_rng_get_vhost; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } static const TypeInfo vu_rng_info = { .name = TYPE_VHOST_USER_RNG, - .parent = TYPE_VIRTIO_DEVICE, + .parent = TYPE_VHOST_USER_BASE, .instance_size = sizeof(VHostUserRNG), .class_init = vu_rng_class_init, }; diff --git a/hw/virtio/vhost-user-scmi-pci.c b/hw/virtio/vhost-user-scmi-pci.c new file mode 100644 index 0000000000..7f53af7fce --- /dev/null +++ b/hw/virtio/vhost-user-scmi-pci.c @@ -0,0 +1,68 @@ +/* + * Vhost-user SCMI virtio device PCI glue + * + * SPDX-FileCopyrightText: Red Hat, Inc. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/vhost-user-scmi.h" +#include "hw/virtio/virtio-pci.h" + +struct VHostUserSCMIPCI { + VirtIOPCIProxy parent_obj; + VHostUserSCMI vdev; +}; + +typedef struct VHostUserSCMIPCI VHostUserSCMIPCI; + +#define TYPE_VHOST_USER_SCMI_PCI "vhost-user-scmi-pci-base" + +DECLARE_INSTANCE_CHECKER(VHostUserSCMIPCI, VHOST_USER_SCMI_PCI, + TYPE_VHOST_USER_SCMI_PCI) + +static void vhost_user_scmi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostUserSCMIPCI *dev = VHOST_USER_SCMI_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + vpci_dev->nvectors = 1; + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); +} + +static void vhost_user_scmi_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 = vhost_user_scmi_pci_realize; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + 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; + pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER; +} + +static void vhost_user_scmi_pci_instance_init(Object *obj) +{ + VHostUserSCMIPCI *dev = VHOST_USER_SCMI_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_SCMI); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_scmi_pci_info = { + .base_name = TYPE_VHOST_USER_SCMI_PCI, + .non_transitional_name = "vhost-user-scmi-pci", + .instance_size = sizeof(VHostUserSCMIPCI), + .instance_init = vhost_user_scmi_pci_instance_init, + .class_init = vhost_user_scmi_pci_class_init, +}; + +static void vhost_user_scmi_pci_register(void) +{ + virtio_pci_types_register(&vhost_user_scmi_pci_info); +} + +type_init(vhost_user_scmi_pci_register); diff --git a/hw/virtio/vhost-user-scmi.c b/hw/virtio/vhost-user-scmi.c new file mode 100644 index 0000000000..300847e672 --- /dev/null +++ b/hw/virtio/vhost-user-scmi.c @@ -0,0 +1,313 @@ +/* + * Vhost-user SCMI virtio device + * + * SPDX-FileCopyrightText: Red Hat, Inc. + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Implementation based on other vhost-user devices in QEMU. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/vhost-user-scmi.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_scmi.h" +#include "trace.h" + +/* + * In this version, we don't support VIRTIO_SCMI_F_SHARED_MEMORY. + * Note that VIRTIO_SCMI_F_SHARED_MEMORY is currently not supported in + * Linux VirtIO SCMI guest driver. + */ +static const int feature_bits[] = { + VIRTIO_F_VERSION_1, + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + VIRTIO_F_RING_RESET, + VIRTIO_SCMI_F_P2A_CHANNELS, + VHOST_INVALID_FEATURE_BIT +}; + +static int vu_scmi_start(VirtIODevice *vdev) +{ + VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + struct vhost_dev *vhost_dev = &scmi->vhost_dev; + int ret, i; + + if (!k->set_guest_notifiers) { + error_report("binding does not support guest notifiers"); + return -ENOSYS; + } + + ret = vhost_dev_enable_notifiers(vhost_dev, vdev); + if (ret < 0) { + error_report("Error enabling host notifiers: %d", ret); + return ret; + } + + ret = k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, true); + if (ret < 0) { + error_report("Error binding guest notifier: %d", ret); + goto err_host_notifiers; + } + + vhost_ack_features(vhost_dev, feature_bits, vdev->guest_features); + + ret = vhost_dev_start(vhost_dev, vdev, true); + if (ret < 0) { + error_report("Error starting vhost-user-scmi: %d", ret); + goto err_guest_notifiers; + } + scmi->started_vu = true; + + /* + * guest_notifier_mask/pending not used yet, so just unmask + * everything here. virtio-pci will do the right thing by + * enabling/disabling irqfd. + */ + for (i = 0; i < scmi->vhost_dev.nvqs; i++) { + vhost_virtqueue_mask(vhost_dev, vdev, i, false); + } + return 0; + +err_guest_notifiers: + k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, false); +err_host_notifiers: + vhost_dev_disable_notifiers(vhost_dev, vdev); + + return ret; +} + +static void vu_scmi_stop(VirtIODevice *vdev) +{ + VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + struct vhost_dev *vhost_dev = &scmi->vhost_dev; + int ret; + + /* vhost_dev_is_started() check in the callers is not fully reliable. */ + if (!scmi->started_vu) { + return; + } + scmi->started_vu = false; + + if (!k->set_guest_notifiers) { + return; + } + + vhost_dev_stop(vhost_dev, vdev, true); + + ret = k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, false); + if (ret < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + return; + } + vhost_dev_disable_notifiers(vhost_dev, vdev); +} + +static void vu_scmi_set_status(VirtIODevice *vdev, uint8_t status) +{ + VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); + bool should_start = virtio_device_should_start(vdev, status); + + if (!scmi->connected) { + return; + } + if (vhost_dev_is_started(&scmi->vhost_dev) == should_start) { + return; + } + + if (should_start) { + vu_scmi_start(vdev); + } else { + vu_scmi_stop(vdev); + } +} + +static uint64_t vu_scmi_get_features(VirtIODevice *vdev, uint64_t features, + Error **errp) +{ + VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); + + return vhost_get_features(&scmi->vhost_dev, feature_bits, features); +} + +static void vu_scmi_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + /* + * Not normally called; it's the daemon that handles the queue; + * however virtio's cleanup path can call this. + */ +} + +static void vu_scmi_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) +{ + VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); + + if (idx == VIRTIO_CONFIG_IRQ_IDX) { + return; + } + + vhost_virtqueue_mask(&scmi->vhost_dev, vdev, idx, mask); +} + +static bool vu_scmi_guest_notifier_pending(VirtIODevice *vdev, int idx) +{ + VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); + + return vhost_virtqueue_pending(&scmi->vhost_dev, idx); +} + +static void vu_scmi_connect(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); + + if (scmi->connected) { + return; + } + scmi->connected = true; + + /* restore vhost state */ + if (virtio_device_started(vdev, vdev->status)) { + vu_scmi_start(vdev); + } +} + +static void vu_scmi_disconnect(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); + + if (!scmi->connected) { + return; + } + scmi->connected = false; + + if (vhost_dev_is_started(&scmi->vhost_dev)) { + vu_scmi_stop(vdev); + } +} + +static void vu_scmi_event(void *opaque, QEMUChrEvent event) +{ + DeviceState *dev = opaque; + + switch (event) { + case CHR_EVENT_OPENED: + vu_scmi_connect(dev); + break; + case CHR_EVENT_CLOSED: + vu_scmi_disconnect(dev); + break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; + } +} + +static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserSCMI *scmi) +{ + virtio_delete_queue(scmi->cmd_vq); + virtio_delete_queue(scmi->event_vq); + g_free(scmi->vhost_dev.vqs); + virtio_cleanup(vdev); + vhost_user_cleanup(&scmi->vhost_user); +} + +static void vu_scmi_device_realize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserSCMI *scmi = VHOST_USER_SCMI(dev); + int ret; + + if (!scmi->chardev.chr) { + error_setg(errp, "vhost-user-scmi: chardev is mandatory"); + return; + } + + vdev->host_features |= (1ULL << VIRTIO_SCMI_F_P2A_CHANNELS); + + if (!vhost_user_init(&scmi->vhost_user, &scmi->chardev, errp)) { + return; + } + + virtio_init(vdev, VIRTIO_ID_SCMI, 0); + + scmi->cmd_vq = virtio_add_queue(vdev, 256, vu_scmi_handle_output); + scmi->event_vq = virtio_add_queue(vdev, 256, vu_scmi_handle_output); + scmi->vhost_dev.nvqs = 2; + scmi->vhost_dev.vqs = g_new0(struct vhost_virtqueue, scmi->vhost_dev.nvqs); + + ret = vhost_dev_init(&scmi->vhost_dev, &scmi->vhost_user, + VHOST_BACKEND_TYPE_USER, 0, errp); + if (ret < 0) { + error_setg_errno(errp, -ret, + "vhost-user-scmi: vhost_dev_init() failed"); + do_vhost_user_cleanup(vdev, scmi); + return; + } + + qemu_chr_fe_set_handlers(&scmi->chardev, NULL, NULL, vu_scmi_event, NULL, + dev, NULL, true); + + return; +} + +static void vu_scmi_device_unrealize(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserSCMI *scmi = VHOST_USER_SCMI(dev); + + vu_scmi_set_status(vdev, 0); + vhost_dev_cleanup(&scmi->vhost_dev); + do_vhost_user_cleanup(vdev, scmi); +} + +static const VMStateDescription vu_scmi_vmstate = { + .name = "vhost-user-scmi", + .unmigratable = 1, +}; + +static Property vu_scmi_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserSCMI, chardev), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vu_scmi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + + device_class_set_props(dc, vu_scmi_properties); + dc->vmsd = &vu_scmi_vmstate; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + vdc->realize = vu_scmi_device_realize; + vdc->unrealize = vu_scmi_device_unrealize; + vdc->get_features = vu_scmi_get_features; + vdc->set_status = vu_scmi_set_status; + vdc->guest_notifier_mask = vu_scmi_guest_notifier_mask; + vdc->guest_notifier_pending = vu_scmi_guest_notifier_pending; +} + +static const TypeInfo vu_scmi_info = { + .name = TYPE_VHOST_USER_SCMI, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VHostUserSCMI), + .class_init = vu_scmi_class_init, +}; + +static void vu_scmi_register_types(void) +{ + type_register_static(&vu_scmi_info); +} + +type_init(vu_scmi_register_types) diff --git a/hw/virtio/vhost-user-snd-pci.c b/hw/virtio/vhost-user-snd-pci.c new file mode 100644 index 0000000000..d61cfdae63 --- /dev/null +++ b/hw/virtio/vhost-user-snd-pci.c @@ -0,0 +1,75 @@ +/* + * Vhost-user Sound virtio device PCI glue + * + * Copyright (c) 2023 Manos Pitsidianakis + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/vhost-user-snd.h" +#include "hw/virtio/virtio-pci.h" + +struct VHostUserSoundPCI { + VirtIOPCIProxy parent_obj; + VHostUserSound vdev; +}; + +typedef struct VHostUserSoundPCI VHostUserSoundPCI; + +#define TYPE_VHOST_USER_SND_PCI "vhost-user-snd-pci-base" + +DECLARE_INSTANCE_CHECKER(VHostUserSoundPCI, VHOST_USER_SND_PCI, + TYPE_VHOST_USER_SND_PCI) + +static Property vhost_user_snd_pci_properties[] = { + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_user_snd_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostUserSoundPCI *dev = VHOST_USER_SND_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + vpci_dev->nvectors = 1; + + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); +} + +static void vhost_user_snd_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 = vhost_user_snd_pci_realize; + set_bit(DEVICE_CATEGORY_SOUND, dc->categories); + device_class_set_props(dc, vhost_user_snd_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; + pcidev_k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO; +} + +static void vhost_user_snd_pci_instance_init(Object *obj) +{ + VHostUserSoundPCI *dev = VHOST_USER_SND_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_SND); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_snd_pci_info = { + .base_name = TYPE_VHOST_USER_SND_PCI, + .non_transitional_name = "vhost-user-snd-pci", + .instance_size = sizeof(VHostUserSoundPCI), + .instance_init = vhost_user_snd_pci_instance_init, + .class_init = vhost_user_snd_pci_class_init, +}; + +static void vhost_user_snd_pci_register(void) +{ + virtio_pci_types_register(&vhost_user_snd_pci_info); +} + +type_init(vhost_user_snd_pci_register); diff --git a/hw/virtio/vhost-user-snd.c b/hw/virtio/vhost-user-snd.c new file mode 100644 index 0000000000..9a217543f8 --- /dev/null +++ b/hw/virtio/vhost-user-snd.c @@ -0,0 +1,67 @@ +/* + * Vhost-user snd virtio device + * + * Copyright (c) 2023 Manos Pitsidianakis + * + * Simple wrapper of the generic vhost-user-device. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/vhost-user-snd.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_snd.h" + +static const VMStateDescription vu_snd_vmstate = { + .name = "vhost-user-snd", + .unmigratable = 1, +}; + +static Property vsnd_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vu_snd_base_realize(DeviceState *dev, Error **errp) +{ + VHostUserBase *vub = VHOST_USER_BASE(dev); + VHostUserBaseClass *vubs = VHOST_USER_BASE_GET_CLASS(dev); + + vub->virtio_id = VIRTIO_ID_SOUND; + vub->num_vqs = 4; + vub->config_size = sizeof(struct virtio_snd_config); + vub->vq_size = 64; + + vubs->parent_realize(dev, errp); +} + +static void vu_snd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass); + + dc->vmsd = &vu_snd_vmstate; + device_class_set_props(dc, vsnd_properties); + device_class_set_parent_realize(dc, vu_snd_base_realize, + &vubc->parent_realize); + + set_bit(DEVICE_CATEGORY_SOUND, dc->categories); +} + +static const TypeInfo vu_snd_info = { + .name = TYPE_VHOST_USER_SND, + .parent = TYPE_VHOST_USER_BASE, + .instance_size = sizeof(VHostUserSound), + .class_init = vu_snd_class_init, +}; + +static void vu_snd_register_types(void) +{ + type_register_static(&vu_snd_info); +} + +type_init(vu_snd_register_types) diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c index 0f8ff99f85..9431b9792c 100644 --- a/hw/virtio/vhost-user-vsock.c +++ b/hw/virtio/vhost-user-vsock.c @@ -55,13 +55,9 @@ const VhostDevConfigOps vsock_ops = { static void vuv_set_status(VirtIODevice *vdev, uint8_t status) { VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); - bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; + bool should_start = virtio_device_should_start(vdev, status); - if (!vdev->vm_running) { - should_start = false; - } - - if (vvc->vhost_dev.started == should_start) { + if (vhost_dev_is_started(&vvc->vhost_dev) == should_start) { return; } diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index b040c1ad2b..cdf9af4a4b 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -10,7 +10,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "hw/virtio/virtio-dmabuf.h" #include "hw/virtio/vhost.h" +#include "hw/virtio/virtio-crypto.h" #include "hw/virtio/vhost-user.h" #include "hw/virtio/vhost-backend.h" #include "hw/virtio/virtio.h" @@ -20,9 +22,10 @@ #include "sysemu/kvm.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +#include "qemu/uuid.h" #include "qemu/sockets.h" +#include "sysemu/runstate.h" #include "sysemu/cryptodev.h" -#include "migration/migration.h" #include "migration/postcopy-ram.h" #include "trace.h" #include "exec/ramblock.h" @@ -39,19 +42,9 @@ #define VHOST_MEMORY_BASELINE_NREGIONS 8 #define VHOST_USER_F_PROTOCOL_FEATURES 30 -#define VHOST_USER_SLAVE_MAX_FDS 8 +#define VHOST_USER_BACKEND_MAX_FDS 8 -/* - * Set maximum number of RAM slots supported to - * the maximum number supported by the target - * hardware plaform. - */ -#if defined(TARGET_X86) || defined(TARGET_X86_64) || \ - defined(TARGET_ARM) || defined(TARGET_ARM_64) -#include "hw/acpi/acpi.h" -#define VHOST_USER_MAX_RAM_SLOTS ACPI_MAX_RAM_SLOTS - -#elif defined(TARGET_PPC) || defined(TARGET_PPC64) +#if defined(TARGET_PPC) || defined(TARGET_PPC64) #include "hw/ppc/spapr.h" #define VHOST_USER_MAX_RAM_SLOTS SPAPR_MAX_RAM_SLOTS @@ -64,26 +57,6 @@ */ #define VHOST_USER_MAX_CONFIG_SIZE 256 -enum VhostUserProtocolFeature { - VHOST_USER_PROTOCOL_F_MQ = 0, - VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1, - VHOST_USER_PROTOCOL_F_RARP = 2, - VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, - VHOST_USER_PROTOCOL_F_NET_MTU = 4, - VHOST_USER_PROTOCOL_F_SLAVE_REQ = 5, - VHOST_USER_PROTOCOL_F_CROSS_ENDIAN = 6, - VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7, - VHOST_USER_PROTOCOL_F_PAGEFAULT = 8, - VHOST_USER_PROTOCOL_F_CONFIG = 9, - 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, - /* Feature 14 reserved for VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS. */ - VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS = 15, - VHOST_USER_PROTOCOL_F_MAX -}; - #define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << VHOST_USER_PROTOCOL_F_MAX) - 1) typedef enum VhostUserRequest { @@ -108,7 +81,7 @@ typedef enum VhostUserRequest { VHOST_USER_SET_VRING_ENABLE = 18, VHOST_USER_SEND_RARP = 19, VHOST_USER_NET_SET_MTU = 20, - VHOST_USER_SET_SLAVE_REQ_FD = 21, + VHOST_USER_SET_BACKEND_REQ_FD = 21, VHOST_USER_IOTLB_MSG = 22, VHOST_USER_SET_VRING_ENDIAN = 23, VHOST_USER_GET_CONFIG = 24, @@ -126,16 +99,24 @@ typedef enum VhostUserRequest { VHOST_USER_GET_MAX_MEM_SLOTS = 36, VHOST_USER_ADD_MEM_REG = 37, VHOST_USER_REM_MEM_REG = 38, + VHOST_USER_SET_STATUS = 39, + VHOST_USER_GET_STATUS = 40, + VHOST_USER_GET_SHARED_OBJECT = 41, + VHOST_USER_SET_DEVICE_STATE_FD = 42, + VHOST_USER_CHECK_DEVICE_STATE = 43, VHOST_USER_MAX } VhostUserRequest; -typedef enum VhostUserSlaveRequest { - VHOST_USER_SLAVE_NONE = 0, - VHOST_USER_SLAVE_IOTLB_MSG = 1, - VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2, - VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3, - VHOST_USER_SLAVE_MAX -} VhostUserSlaveRequest; +typedef enum VhostUserBackendRequest { + VHOST_USER_BACKEND_NONE = 0, + VHOST_USER_BACKEND_IOTLB_MSG = 1, + VHOST_USER_BACKEND_CONFIG_CHANGE_MSG = 2, + VHOST_USER_BACKEND_VRING_HOST_NOTIFIER_MSG = 3, + VHOST_USER_BACKEND_SHARED_OBJECT_ADD = 6, + VHOST_USER_BACKEND_SHARED_OBJECT_REMOVE = 7, + VHOST_USER_BACKEND_SHARED_OBJECT_LOOKUP = 8, + VHOST_USER_BACKEND_MAX +} VhostUserBackendRequest; typedef struct VhostUserMemoryRegion { uint64_t guest_phys_addr; @@ -169,13 +150,24 @@ typedef struct VhostUserConfig { #define VHOST_CRYPTO_SYM_HMAC_MAX_KEY_LEN 512 #define VHOST_CRYPTO_SYM_CIPHER_MAX_KEY_LEN 64 +#define VHOST_CRYPTO_ASYM_MAX_KEY_LEN 1024 typedef struct VhostUserCryptoSession { + uint64_t op_code; + union { + struct { + CryptoDevBackendSymSessionInfo session_setup_data; + uint8_t key[VHOST_CRYPTO_SYM_CIPHER_MAX_KEY_LEN]; + uint8_t auth_key[VHOST_CRYPTO_SYM_HMAC_MAX_KEY_LEN]; + } sym; + struct { + CryptoDevBackendAsymSessionInfo session_setup_data; + uint8_t key[VHOST_CRYPTO_ASYM_MAX_KEY_LEN]; + } asym; + } u; + /* session id for success, -1 on errors */ int64_t session_id; - CryptoDevBackendSymSessionInfo session_setup_data; - uint8_t key[VHOST_CRYPTO_SYM_CIPHER_MAX_KEY_LEN]; - uint8_t auth_key[VHOST_CRYPTO_SYM_HMAC_MAX_KEY_LEN]; } VhostUserCryptoSession; static VhostUserConfig c __attribute__ ((unused)); @@ -196,19 +188,29 @@ typedef struct VhostUserInflight { uint16_t queue_size; } VhostUserInflight; +typedef struct VhostUserShared { + unsigned char uuid[16]; +} VhostUserShared; + typedef struct { VhostUserRequest request; #define VHOST_USER_VERSION_MASK (0x3) -#define VHOST_USER_REPLY_MASK (0x1<<2) +#define VHOST_USER_REPLY_MASK (0x1 << 2) #define VHOST_USER_NEED_REPLY_MASK (0x1 << 3) uint32_t flags; uint32_t size; /* the following payload size */ } QEMU_PACKED VhostUserHeader; +/* Request payload of VHOST_USER_SET_DEVICE_STATE_FD */ +typedef struct VhostUserTransferDeviceState { + uint32_t direction; + uint32_t phase; +} VhostUserTransferDeviceState; + typedef union { #define VHOST_USER_VRING_IDX_MASK (0xff) -#define VHOST_USER_VRING_NOFD_MASK (0x1<<8) +#define VHOST_USER_VRING_NOFD_MASK (0x1 << 8) uint64_t u64; struct vhost_vring_state state; struct vhost_vring_addr addr; @@ -220,6 +222,8 @@ typedef union { VhostUserCryptoSession session; VhostUserVringArea area; VhostUserInflight inflight; + VhostUserShared object; + VhostUserTransferDeviceState transfer_state; } VhostUserPayload; typedef struct VhostUserMsg { @@ -239,8 +243,8 @@ struct vhost_user { struct vhost_dev *dev; /* Shared between vhost devs of the same virtio device */ VhostUserState *user; - QIOChannel *slave_ioc; - GSource *slave_src; + QIOChannel *backend_ioc; + GSource *backend_src; NotifierWithReturn postcopy_notifier; struct PostCopyFD postcopy_fd; uint64_t postcopy_client_bases[VHOST_USER_MAX_RAM_SLOTS]; @@ -248,7 +252,8 @@ struct vhost_user { size_t region_rb_len; /* RAMBlock associated with a given region */ RAMBlock **region_rb; - /* The offset from the start of the RAMBlock to the start of the + /* + * The offset from the start of the RAMBlock to the start of the * vhost region. */ ram_addr_t *region_rb_offset; @@ -267,11 +272,6 @@ struct scrub_regions { int fd_idx; }; -static bool ioeventfd_enabled(void) -{ - return !kvm_enabled() || kvm_eventfds_enabled(); -} - static int vhost_user_read_header(struct vhost_dev *dev, VhostUserMsg *msg) { struct vhost_user *u = dev->opaque; @@ -295,22 +295,13 @@ static int vhost_user_read_header(struct vhost_dev *dev, VhostUserMsg *msg) return -EPROTO; } + trace_vhost_user_read(msg->hdr.request, msg->hdr.flags); + return 0; } -struct vhost_user_read_cb_data { - struct vhost_dev *dev; - VhostUserMsg *msg; - GMainLoop *loop; - int ret; -}; - -static gboolean vhost_user_read_cb(void *do_not_use, GIOCondition condition, - gpointer opaque) +static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) { - struct vhost_user_read_cb_data *data = opaque; - struct vhost_dev *dev = data->dev; - VhostUserMsg *msg = data->msg; struct vhost_user *u = dev->opaque; CharBackend *chr = u->user->chr; uint8_t *p = (uint8_t *) msg; @@ -318,8 +309,7 @@ static gboolean vhost_user_read_cb(void *do_not_use, GIOCondition condition, r = vhost_user_read_header(dev, msg); if (r < 0) { - data->ret = r; - goto end; + return r; } /* validate message size is sane */ @@ -327,8 +317,7 @@ static gboolean vhost_user_read_cb(void *do_not_use, GIOCondition condition, error_report("Failed to read msg header." " Size %d exceeds the maximum %zu.", msg->hdr.size, VHOST_USER_PAYLOAD_SIZE); - data->ret = -EPROTO; - goto end; + return -EPROTO; } if (msg->hdr.size) { @@ -339,84 +328,11 @@ static gboolean vhost_user_read_cb(void *do_not_use, GIOCondition condition, int saved_errno = errno; error_report("Failed to read msg payload." " Read %d instead of %d.", r, msg->hdr.size); - data->ret = r < 0 ? -saved_errno : -EIO; - goto end; + return r < 0 ? -saved_errno : -EIO; } } -end: - g_main_loop_quit(data->loop); - return G_SOURCE_REMOVE; -} - -static gboolean slave_read(QIOChannel *ioc, GIOCondition condition, - gpointer opaque); - -/* - * This updates the read handler to use a new event loop context. - * Event sources are removed from the previous context : this ensures - * that events detected in the previous context are purged. They will - * be re-detected and processed in the new context. - */ -static void slave_update_read_handler(struct vhost_dev *dev, - GMainContext *ctxt) -{ - struct vhost_user *u = dev->opaque; - - if (!u->slave_ioc) { - return; - } - - if (u->slave_src) { - g_source_destroy(u->slave_src); - g_source_unref(u->slave_src); - } - - u->slave_src = qio_channel_add_watch_source(u->slave_ioc, - G_IO_IN | G_IO_HUP, - slave_read, dev, NULL, - ctxt); -} - -static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) -{ - struct vhost_user *u = dev->opaque; - CharBackend *chr = u->user->chr; - GMainContext *prev_ctxt = chr->chr->gcontext; - GMainContext *ctxt = g_main_context_new(); - GMainLoop *loop = g_main_loop_new(ctxt, FALSE); - struct vhost_user_read_cb_data data = { - .dev = dev, - .loop = loop, - .msg = msg, - .ret = 0 - }; - - /* - * We want to be able to monitor the slave channel fd while waiting - * for chr I/O. This requires an event loop, but we can't nest the - * one to which chr is currently attached : its fd handlers might not - * be prepared for re-entrancy. So we create a new one and switch chr - * to use it. - */ - slave_update_read_handler(dev, ctxt); - qemu_chr_be_update_read_handlers(chr->chr, ctxt); - qemu_chr_fe_add_watch(chr, G_IO_IN | G_IO_HUP, vhost_user_read_cb, &data); - - g_main_loop_run(loop); - - /* - * Restore the previous event loop context. This also destroys/recreates - * event sources : this guarantees that all pending events in the original - * context that have been processed by the nested loop are purged. - */ - qemu_chr_be_update_read_handlers(chr->chr, prev_ctxt); - slave_update_read_handler(dev, NULL); - - g_main_loop_unref(loop); - g_main_context_unref(ctxt); - - return data.ret; + return 0; } static int process_message_reply(struct vhost_dev *dev, @@ -444,7 +360,7 @@ static int process_message_reply(struct vhost_dev *dev, return msg_reply.payload.u64 ? -EIO : 0; } -static bool vhost_user_one_time_request(VhostUserRequest request) +static bool vhost_user_per_device_request(VhostUserRequest request) { switch (request) { case VHOST_USER_SET_OWNER: @@ -452,6 +368,9 @@ static bool vhost_user_one_time_request(VhostUserRequest request) case VHOST_USER_SET_MEM_TABLE: case VHOST_USER_GET_QUEUE_NUM: case VHOST_USER_NET_SET_MTU: + case VHOST_USER_RESET_DEVICE: + case VHOST_USER_ADD_MEM_REG: + case VHOST_USER_REM_MEM_REG: return true; default: return false; @@ -467,11 +386,17 @@ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, int ret, size = VHOST_USER_HDR_SIZE + msg->hdr.size; /* - * For non-vring specific requests, like VHOST_USER_SET_MEM_TABLE, - * we just need send it once in the first time. For later such - * request, we just ignore it. + * Some devices, like virtio-scsi, are implemented as a single vhost_dev, + * while others, like virtio-net, contain multiple vhost_devs. For + * operations such as configuring device memory mappings or issuing device + * resets, which affect the whole device instead of individual VQs, + * vhost-user messages should only be sent once. + * + * Devices with multiple vhost_devs are given an associated dev->vq_index + * so per_device requests are only sent if vq_index is 0. */ - if (vhost_user_one_time_request(msg->hdr.request) && dev->vq_index != 0) { + if (vhost_user_per_device_request(msg->hdr.request) + && dev->vq_index != 0) { msg->hdr.flags &= ~VHOST_USER_NEED_REPLY_MASK; return 0; } @@ -520,6 +445,11 @@ static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base, .hdr.size = sizeof(msg.payload.log), }; + /* Send only once with first queue pair */ + if (dev->vq_index != 0) { + return 0; + } + if (shmfd && log->fd != -1) { fds[fd_num++] = log->fd; } @@ -544,8 +474,6 @@ static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base, } } - trace_vhost_user_read(msg.hdr.request, msg.hdr.flags); - return 0; } @@ -557,6 +485,7 @@ static MemoryRegion *vhost_user_get_mr_data(uint64_t addr, ram_addr_t *offset, assert((uintptr_t)addr == addr); mr = memory_region_from_host((void *)(uintptr_t)addr, offset); *fd = memory_region_get_fd(mr); + *offset += mr->ram_block->fd_offset; return mr; } @@ -1147,9 +1076,95 @@ static int vhost_user_set_vring_endian(struct vhost_dev *dev, return vhost_user_write(dev, &msg, NULL, 0); } +static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64) +{ + int ret; + VhostUserMsg msg = { + .hdr.request = request, + .hdr.flags = VHOST_USER_VERSION, + }; + + if (vhost_user_per_device_request(request) && dev->vq_index != 0) { + return 0; + } + + ret = vhost_user_write(dev, &msg, NULL, 0); + if (ret < 0) { + return ret; + } + + ret = vhost_user_read(dev, &msg); + if (ret < 0) { + return ret; + } + + if (msg.hdr.request != request) { + error_report("Received unexpected msg type. Expected %d received %d", + request, msg.hdr.request); + return -EPROTO; + } + + if (msg.hdr.size != sizeof(msg.payload.u64)) { + error_report("Received bad msg size."); + return -EPROTO; + } + + *u64 = msg.payload.u64; + + return 0; +} + +static int vhost_user_get_features(struct vhost_dev *dev, uint64_t *features) +{ + if (vhost_user_get_u64(dev, VHOST_USER_GET_FEATURES, features) < 0) { + return -EPROTO; + } + + return 0; +} + +/* Note: "msg->hdr.flags" may be modified. */ +static int vhost_user_write_sync(struct vhost_dev *dev, VhostUserMsg *msg, + bool wait_for_reply) +{ + int ret; + + if (wait_for_reply) { + bool reply_supported = virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_REPLY_ACK); + if (reply_supported) { + msg->hdr.flags |= VHOST_USER_NEED_REPLY_MASK; + } + } + + ret = vhost_user_write(dev, msg, NULL, 0); + if (ret < 0) { + return ret; + } + + if (wait_for_reply) { + uint64_t dummy; + + if (msg->hdr.flags & VHOST_USER_NEED_REPLY_MASK) { + return process_message_reply(dev, msg); + } + + /* + * We need to wait for a reply but the backend does not + * support replies for the command we just sent. + * Send VHOST_USER_GET_FEATURES which makes all backends + * send a reply. + */ + return vhost_user_get_features(dev, &dummy); + } + + return 0; +} + static int vhost_set_vring(struct vhost_dev *dev, unsigned long int request, - struct vhost_vring_state *ring) + struct vhost_vring_state *ring, + bool wait_for_reply) { VhostUserMsg msg = { .hdr.request = request, @@ -1158,13 +1173,13 @@ static int vhost_set_vring(struct vhost_dev *dev, .hdr.size = sizeof(msg.payload.state), }; - return vhost_user_write(dev, &msg, NULL, 0); + return vhost_user_write_sync(dev, &msg, wait_for_reply); } static int vhost_user_set_vring_num(struct vhost_dev *dev, struct vhost_vring_state *ring) { - return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring); + return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring, false); } static void vhost_user_host_notifier_free(VhostUserHostNotifier *n) @@ -1195,7 +1210,7 @@ static void vhost_user_host_notifier_remove(VhostUserHostNotifier *n, static int vhost_user_set_vring_base(struct vhost_dev *dev, struct vhost_vring_state *ring) { - return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring); + return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring, false); } static int vhost_user_set_vring_enable(struct vhost_dev *dev, int enable) @@ -1213,7 +1228,21 @@ static int vhost_user_set_vring_enable(struct vhost_dev *dev, int enable) .num = enable, }; - ret = vhost_set_vring(dev, VHOST_USER_SET_VRING_ENABLE, &state); + /* + * SET_VRING_ENABLE travels from guest to QEMU to vhost-user backend / + * control plane thread via unix domain socket. Virtio requests travel + * from guest to vhost-user backend / data plane thread via eventfd. + * Even if the guest enables the ring first, and pushes its first virtio + * request second (conforming to the virtio spec), the data plane thread + * in the backend may see the virtio request before the control plane + * thread sees the queue enablement. This causes (in fact, requires) the + * data plane thread to discard the virtio request (it arrived on a + * seemingly disabled queue). To prevent this out-of-order delivery, + * don't let the guest proceed to pushing the virtio request until the + * backend control plane acknowledges enabling the queue -- IOW, pass + * wait_for_reply=true below. + */ + ret = vhost_set_vring(dev, VHOST_USER_SET_VRING_ENABLE, &state, true); if (ret < 0) { /* * Restoring the previous state is likely infeasible, as well as @@ -1292,7 +1321,7 @@ static int vhost_set_vring_file(struct vhost_dev *dev, .hdr.size = sizeof(msg.payload.u64), }; - if (ioeventfd_enabled() && file->fd > 0) { + if (file->fd > 0) { fds[fd_num++] = file->fd; } else { msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK; @@ -1313,76 +1342,15 @@ static int vhost_user_set_vring_call(struct vhost_dev *dev, return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_CALL, file); } - -static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64) +static int vhost_user_set_vring_err(struct vhost_dev *dev, + struct vhost_vring_file *file) { - int ret; - VhostUserMsg msg = { - .hdr.request = request, - .hdr.flags = VHOST_USER_VERSION, - }; - - if (vhost_user_one_time_request(request) && dev->vq_index != 0) { - return 0; - } - - ret = vhost_user_write(dev, &msg, NULL, 0); - if (ret < 0) { - return ret; - } - - ret = vhost_user_read(dev, &msg); - if (ret < 0) { - return ret; - } - - if (msg.hdr.request != request) { - error_report("Received unexpected msg type. Expected %d received %d", - request, msg.hdr.request); - return -EPROTO; - } - - if (msg.hdr.size != sizeof(msg.payload.u64)) { - error_report("Received bad msg size."); - return -EPROTO; - } - - *u64 = msg.payload.u64; - - return 0; -} - -static int vhost_user_get_features(struct vhost_dev *dev, uint64_t *features) -{ - if (vhost_user_get_u64(dev, VHOST_USER_GET_FEATURES, features) < 0) { - return -EPROTO; - } - - return 0; -} - -static int enforce_reply(struct vhost_dev *dev, - const VhostUserMsg *msg) -{ - uint64_t dummy; - - if (msg->hdr.flags & VHOST_USER_NEED_REPLY_MASK) { - return process_message_reply(dev, msg); - } - - /* - * We need to wait for a reply but the backend does not - * support replies for the command we just sent. - * Send VHOST_USER_GET_FEATURES which makes all backends - * send a reply. - */ - return vhost_user_get_features(dev, &dummy); + return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_ERR, file); } static int vhost_user_set_vring_addr(struct vhost_dev *dev, struct vhost_vring_addr *addr) { - int ret; VhostUserMsg msg = { .hdr.request = VHOST_USER_SET_VRING_ADDR, .hdr.flags = VHOST_USER_VERSION, @@ -1390,29 +1358,13 @@ static int vhost_user_set_vring_addr(struct vhost_dev *dev, .hdr.size = sizeof(msg.payload.addr), }; - bool reply_supported = virtio_has_feature(dev->protocol_features, - VHOST_USER_PROTOCOL_F_REPLY_ACK); - /* * wait for a reply if logging is enabled to make sure * backend is actually logging changes */ bool wait_for_reply = addr->flags & (1 << VHOST_VRING_F_LOG); - if (reply_supported && wait_for_reply) { - msg.hdr.flags |= VHOST_USER_NEED_REPLY_MASK; - } - - ret = vhost_user_write(dev, &msg, NULL, 0); - if (ret < 0) { - return ret; - } - - if (wait_for_reply) { - return enforce_reply(dev, &msg); - } - - return 0; + return vhost_user_write_sync(dev, &msg, wait_for_reply); } static int vhost_user_set_u64(struct vhost_dev *dev, int request, uint64_t u64, @@ -1424,26 +1376,45 @@ static int vhost_user_set_u64(struct vhost_dev *dev, int request, uint64_t u64, .payload.u64 = u64, .hdr.size = sizeof(msg.payload.u64), }; + + return vhost_user_write_sync(dev, &msg, wait_for_reply); +} + +static int vhost_user_set_status(struct vhost_dev *dev, uint8_t status) +{ + return vhost_user_set_u64(dev, VHOST_USER_SET_STATUS, status, false); +} + +static int vhost_user_get_status(struct vhost_dev *dev, uint8_t *status) +{ + uint64_t value; int ret; - if (wait_for_reply) { - bool reply_supported = virtio_has_feature(dev->protocol_features, - VHOST_USER_PROTOCOL_F_REPLY_ACK); - if (reply_supported) { - msg.hdr.flags |= VHOST_USER_NEED_REPLY_MASK; - } + ret = vhost_user_get_u64(dev, VHOST_USER_GET_STATUS, &value); + if (ret < 0) { + return ret; } + *status = value; - ret = vhost_user_write(dev, &msg, NULL, 0); + return 0; +} + +static int vhost_user_add_status(struct vhost_dev *dev, uint8_t status) +{ + uint8_t s; + int ret; + + ret = vhost_user_get_status(dev, &s); if (ret < 0) { return ret; } - if (wait_for_reply) { - return enforce_reply(dev, &msg); + if ((s & status) == status) { + return 0; } + s |= status; - return 0; + return vhost_user_set_status(dev, s); } static int vhost_user_set_features(struct vhost_dev *dev, @@ -1454,9 +1425,26 @@ static int vhost_user_set_features(struct vhost_dev *dev, * backend is actually logging changes */ bool log_enabled = features & (0x1ULL << VHOST_F_LOG_ALL); + int ret; - return vhost_user_set_u64(dev, VHOST_USER_SET_FEATURES, features, + /* + * We need to include any extra backend only feature bits that + * might be needed by our device. Currently this includes the + * VHOST_USER_F_PROTOCOL_FEATURES bit for enabling protocol + * features. + */ + ret = vhost_user_set_u64(dev, VHOST_USER_SET_FEATURES, + features | dev->backend_features, log_enabled); + + if (virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_STATUS)) { + if (!ret) { + return vhost_user_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK); + } + } + + return ret; } static int vhost_user_set_protocol_features(struct vhost_dev *dev, @@ -1497,17 +1485,22 @@ static int vhost_user_reset_device(struct vhost_dev *dev) { VhostUserMsg msg = { .hdr.flags = VHOST_USER_VERSION, + .hdr.request = VHOST_USER_RESET_DEVICE, }; - msg.hdr.request = virtio_has_feature(dev->protocol_features, - VHOST_USER_PROTOCOL_F_RESET_DEVICE) - ? VHOST_USER_RESET_DEVICE - : VHOST_USER_RESET_OWNER; + /* + * 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 -ENOSYS; + } return vhost_user_write(dev, &msg, NULL, 0); } -static int vhost_user_slave_handle_config_change(struct vhost_dev *dev) +static int vhost_user_backend_handle_config_change(struct vhost_dev *dev) { if (!dev->config_ops || !dev->config_ops->vhost_dev_config_notifier) { return -ENOSYS; @@ -1525,11 +1518,16 @@ static VhostUserHostNotifier *fetch_or_create_notifier(VhostUserState *u, { VhostUserHostNotifier *n = NULL; if (idx >= u->notifiers->len) { - g_ptr_array_set_size(u->notifiers, idx); + g_ptr_array_set_size(u->notifiers, idx + 1); } n = g_ptr_array_index(u->notifiers, idx); if (!n) { + /* + * In case notification arrive out-of-order, + * make room for current index. + */ + g_ptr_array_remove_index(u->notifiers, idx); n = g_new0(VhostUserHostNotifier, 1); n->idx = idx; g_ptr_array_insert(u->notifiers, idx, n); @@ -1539,7 +1537,7 @@ static VhostUserHostNotifier *fetch_or_create_notifier(VhostUserState *u, return n; } -static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev, +static int vhost_user_backend_handle_vring_host_notifier(struct vhost_dev *dev, VhostUserVringArea *area, int fd) { @@ -1601,16 +1599,165 @@ static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev, return 0; } -static void close_slave_channel(struct vhost_user *u) +static int +vhost_user_backend_handle_shared_object_add(struct vhost_dev *dev, + VhostUserShared *object) { - g_source_destroy(u->slave_src); - g_source_unref(u->slave_src); - u->slave_src = NULL; - object_unref(OBJECT(u->slave_ioc)); - u->slave_ioc = NULL; + QemuUUID uuid; + + memcpy(uuid.data, object->uuid, sizeof(object->uuid)); + return virtio_add_vhost_device(&uuid, dev); } -static gboolean slave_read(QIOChannel *ioc, GIOCondition condition, +static int +vhost_user_backend_handle_shared_object_remove(struct vhost_dev *dev, + VhostUserShared *object) +{ + QemuUUID uuid; + + memcpy(uuid.data, object->uuid, sizeof(object->uuid)); + switch (virtio_object_type(&uuid)) { + case TYPE_VHOST_DEV: + { + struct vhost_dev *owner = virtio_lookup_vhost_device(&uuid); + if (dev != owner) { + /* Not allowed to remove non-owned entries */ + return 0; + } + break; + } + default: + /* Not allowed to remove non-owned entries */ + return 0; + } + + return virtio_remove_resource(&uuid); +} + +static bool vhost_user_send_resp(QIOChannel *ioc, VhostUserHeader *hdr, + VhostUserPayload *payload, Error **errp) +{ + struct iovec iov[] = { + { .iov_base = hdr, .iov_len = VHOST_USER_HDR_SIZE }, + { .iov_base = payload, .iov_len = hdr->size }, + }; + + hdr->flags &= ~VHOST_USER_NEED_REPLY_MASK; + hdr->flags |= VHOST_USER_REPLY_MASK; + + return !qio_channel_writev_all(ioc, iov, ARRAY_SIZE(iov), errp); +} + +static bool +vhost_user_backend_send_dmabuf_fd(QIOChannel *ioc, VhostUserHeader *hdr, + VhostUserPayload *payload, Error **errp) +{ + hdr->size = sizeof(payload->u64); + return vhost_user_send_resp(ioc, hdr, payload, errp); +} + +int vhost_user_get_shared_object(struct vhost_dev *dev, unsigned char *uuid, + int *dmabuf_fd) +{ + struct vhost_user *u = dev->opaque; + CharBackend *chr = u->user->chr; + int ret; + VhostUserMsg msg = { + .hdr.request = VHOST_USER_GET_SHARED_OBJECT, + .hdr.flags = VHOST_USER_VERSION, + }; + memcpy(msg.payload.object.uuid, uuid, sizeof(msg.payload.object.uuid)); + + ret = vhost_user_write(dev, &msg, NULL, 0); + if (ret < 0) { + return ret; + } + + ret = vhost_user_read(dev, &msg); + if (ret < 0) { + return ret; + } + + if (msg.hdr.request != VHOST_USER_GET_SHARED_OBJECT) { + error_report("Received unexpected msg type. " + "Expected %d received %d", + VHOST_USER_GET_SHARED_OBJECT, msg.hdr.request); + return -EPROTO; + } + + *dmabuf_fd = qemu_chr_fe_get_msgfd(chr); + if (*dmabuf_fd < 0) { + error_report("Failed to get dmabuf fd"); + return -EIO; + } + + return 0; +} + +static int +vhost_user_backend_handle_shared_object_lookup(struct vhost_user *u, + QIOChannel *ioc, + VhostUserHeader *hdr, + VhostUserPayload *payload) +{ + QemuUUID uuid; + CharBackend *chr = u->user->chr; + Error *local_err = NULL; + int dmabuf_fd = -1; + int fd_num = 0; + + memcpy(uuid.data, payload->object.uuid, sizeof(payload->object.uuid)); + + payload->u64 = 0; + switch (virtio_object_type(&uuid)) { + case TYPE_DMABUF: + dmabuf_fd = virtio_lookup_dmabuf(&uuid); + break; + case TYPE_VHOST_DEV: + { + struct vhost_dev *dev = virtio_lookup_vhost_device(&uuid); + if (dev == NULL) { + payload->u64 = -EINVAL; + break; + } + int ret = vhost_user_get_shared_object(dev, uuid.data, &dmabuf_fd); + if (ret < 0) { + payload->u64 = ret; + } + break; + } + case TYPE_INVALID: + payload->u64 = -EINVAL; + break; + } + + if (dmabuf_fd != -1) { + fd_num++; + } + + if (qemu_chr_fe_set_msgfds(chr, &dmabuf_fd, fd_num) < 0) { + error_report("Failed to set msg fds."); + payload->u64 = -EINVAL; + } + + if (!vhost_user_backend_send_dmabuf_fd(ioc, hdr, payload, &local_err)) { + error_report_err(local_err); + return -EINVAL; + } + + return 0; +} + +static void close_backend_channel(struct vhost_user *u) +{ + g_source_destroy(u->backend_src); + g_source_unref(u->backend_src); + u->backend_src = NULL; + object_unref(OBJECT(u->backend_ioc)); + u->backend_ioc = NULL; +} + +static gboolean backend_read(QIOChannel *ioc, GIOCondition condition, gpointer opaque) { struct vhost_dev *dev = opaque; @@ -1648,16 +1795,27 @@ static gboolean slave_read(QIOChannel *ioc, GIOCondition condition, } switch (hdr.request) { - case VHOST_USER_SLAVE_IOTLB_MSG: + case VHOST_USER_BACKEND_IOTLB_MSG: ret = vhost_backend_handle_iotlb_msg(dev, &payload.iotlb); break; - case VHOST_USER_SLAVE_CONFIG_CHANGE_MSG : - ret = vhost_user_slave_handle_config_change(dev); + case VHOST_USER_BACKEND_CONFIG_CHANGE_MSG: + ret = vhost_user_backend_handle_config_change(dev); break; - case VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG: - ret = vhost_user_slave_handle_vring_host_notifier(dev, &payload.area, + case VHOST_USER_BACKEND_VRING_HOST_NOTIFIER_MSG: + ret = vhost_user_backend_handle_vring_host_notifier(dev, &payload.area, fd ? fd[0] : -1); break; + case VHOST_USER_BACKEND_SHARED_OBJECT_ADD: + ret = vhost_user_backend_handle_shared_object_add(dev, &payload.object); + break; + case VHOST_USER_BACKEND_SHARED_OBJECT_REMOVE: + ret = vhost_user_backend_handle_shared_object_remove(dev, + &payload.object); + break; + case VHOST_USER_BACKEND_SHARED_OBJECT_LOOKUP: + ret = vhost_user_backend_handle_shared_object_lookup(dev->opaque, ioc, + &hdr, &payload); + break; default: error_report("Received unexpected msg type: %d.", hdr.request); ret = -EINVAL; @@ -1668,21 +1826,10 @@ static gboolean slave_read(QIOChannel *ioc, GIOCondition condition, * directly in their request handlers. */ if (hdr.flags & VHOST_USER_NEED_REPLY_MASK) { - struct iovec iovec[2]; - - - hdr.flags &= ~VHOST_USER_NEED_REPLY_MASK; - hdr.flags |= VHOST_USER_REPLY_MASK; - payload.u64 = !!ret; hdr.size = sizeof(payload.u64); - iovec[0].iov_base = &hdr; - iovec[0].iov_len = VHOST_USER_HDR_SIZE; - iovec[1].iov_base = &payload; - iovec[1].iov_len = hdr.size; - - if (qio_channel_writev_all(ioc, iovec, ARRAY_SIZE(iovec), &local_err)) { + if (!vhost_user_send_resp(ioc, &hdr, &payload, &local_err)) { error_report_err(local_err); goto err; } @@ -1691,7 +1838,7 @@ static gboolean slave_read(QIOChannel *ioc, GIOCondition condition, goto fdcleanup; err: - close_slave_channel(u); + close_backend_channel(u); rc = G_SOURCE_REMOVE; fdcleanup: @@ -1703,10 +1850,10 @@ fdcleanup: return rc; } -static int vhost_setup_slave_channel(struct vhost_dev *dev) +static int vhost_setup_backend_channel(struct vhost_dev *dev) { VhostUserMsg msg = { - .hdr.request = VHOST_USER_SET_SLAVE_REQ_FD, + .hdr.request = VHOST_USER_SET_BACKEND_REQ_FD, .hdr.flags = VHOST_USER_VERSION, }; struct vhost_user *u = dev->opaque; @@ -1717,11 +1864,11 @@ static int vhost_setup_slave_channel(struct vhost_dev *dev) QIOChannel *ioc; if (!virtio_has_feature(dev->protocol_features, - VHOST_USER_PROTOCOL_F_SLAVE_REQ)) { + VHOST_USER_PROTOCOL_F_BACKEND_REQ)) { return 0; } - if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { + if (qemu_socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { int saved_errno = errno; error_report("socketpair() failed"); return -saved_errno; @@ -1732,8 +1879,10 @@ static int vhost_setup_slave_channel(struct vhost_dev *dev) error_report_err(local_err); return -ECONNREFUSED; } - u->slave_ioc = ioc; - slave_update_read_handler(dev, NULL); + u->backend_ioc = ioc; + u->backend_src = qio_channel_add_watch_source(u->backend_ioc, + G_IO_IN | G_IO_HUP, + backend_read, dev, NULL, NULL); if (reply_supported) { msg.hdr.flags |= VHOST_USER_NEED_REPLY_MASK; @@ -1751,7 +1900,7 @@ static int vhost_setup_slave_channel(struct vhost_dev *dev) out: close(sv[1]); if (ret) { - close_slave_channel(u); + close_backend_channel(u); } return ret; @@ -1951,7 +2100,7 @@ static int vhost_user_postcopy_end(struct vhost_dev *dev, Error **errp) } static int vhost_user_postcopy_notifier(NotifierWithReturn *notifier, - void *opaque) + void *opaque, Error **errp) { struct PostcopyNotifyData *pnd = opaque; struct vhost_user *u = container_of(notifier, struct vhost_user, @@ -1963,20 +2112,20 @@ static int vhost_user_postcopy_notifier(NotifierWithReturn *notifier, if (!virtio_has_feature(dev->protocol_features, VHOST_USER_PROTOCOL_F_PAGEFAULT)) { /* TODO: Get the device name into this error somehow */ - error_setg(pnd->errp, + error_setg(errp, "vhost-user backend not capable of postcopy"); return -ENOENT; } break; case POSTCOPY_NOTIFY_INBOUND_ADVISE: - return vhost_user_postcopy_advise(dev, pnd->errp); + return vhost_user_postcopy_advise(dev, errp); case POSTCOPY_NOTIFY_INBOUND_LISTEN: - return vhost_user_postcopy_listen(dev, pnd->errp); + return vhost_user_postcopy_listen(dev, errp); case POSTCOPY_NOTIFY_INBOUND_END: - return vhost_user_postcopy_end(dev, pnd->errp); + return vhost_user_postcopy_end(dev, errp); default: /* We ignore notifications we don't know */ @@ -2031,18 +2180,16 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, if (supports_f_config) { if (!virtio_has_feature(protocol_features, VHOST_USER_PROTOCOL_F_CONFIG)) { - error_setg(errp, "vhost-user device %s expecting " + error_setg(errp, "vhost-user device expecting " "VHOST_USER_PROTOCOL_F_CONFIG but the vhost-user backend does " - "not support it.", dev->vdev->name); + "not support it."); return -EPROTO; } } else { if (virtio_has_feature(protocol_features, VHOST_USER_PROTOCOL_F_CONFIG)) { - warn_reportf_err(*errp, "vhost-user backend supports " - "VHOST_USER_PROTOCOL_F_CONFIG for " - "device %s but QEMU does not.", - dev->vdev->name); + warn_report("vhost-user backend supports " + "VHOST_USER_PROTOCOL_F_CONFIG but QEMU does not."); protocol_features &= ~(1ULL << VHOST_USER_PROTOCOL_F_CONFIG); } } @@ -2075,11 +2222,11 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, if (virtio_has_feature(features, VIRTIO_F_IOMMU_PLATFORM) && !(virtio_has_feature(dev->protocol_features, - VHOST_USER_PROTOCOL_F_SLAVE_REQ) && + VHOST_USER_PROTOCOL_F_BACKEND_REQ) && virtio_has_feature(dev->protocol_features, VHOST_USER_PROTOCOL_F_REPLY_ACK))) { error_setg(errp, "IOMMU support requires reply-ack and " - "slave-req protocol features."); + "backend-req protocol features."); return -EINVAL; } @@ -2115,7 +2262,7 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, } if (dev->vq_index == 0) { - err = vhost_setup_slave_channel(dev); + err = vhost_setup_backend_channel(dev); if (err < 0) { error_setg_errno(errp, EPROTO, "vhost_backend_init failed"); return -EPROTO; @@ -2145,8 +2292,8 @@ static int vhost_user_backend_cleanup(struct vhost_dev *dev) close(u->postcopy_fd.fd); u->postcopy_fd.handler = NULL; } - if (u->slave_ioc) { - close_slave_channel(u); + if (u->backend_ioc) { + close_backend_channel(u); } g_free(u->region_rb); u->region_rb = NULL; @@ -2205,19 +2352,6 @@ static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr) return -ENOTSUP; } -static bool vhost_user_can_merge(struct vhost_dev *dev, - uint64_t start1, uint64_t size1, - uint64_t start2, uint64_t size2) -{ - ram_addr_t offset; - int mfd, rfd; - - (void)vhost_user_get_mr_data(start1, &offset, &mfd); - (void)vhost_user_get_mr_data(start2, &offset, &rfd); - - return mfd == rfd; -} - static int vhost_user_net_set_mtu(struct vhost_dev *dev, uint16_t mtu) { VhostUserMsg msg; @@ -2242,7 +2376,7 @@ static int vhost_user_net_set_mtu(struct vhost_dev *dev, uint16_t mtu) return ret; } - /* If reply_ack supported, slave has to ack specified MTU is valid */ + /* If reply_ack supported, backend has to ack specified MTU is valid */ if (reply_supported) { return process_message_reply(dev, &msg); } @@ -2376,7 +2510,7 @@ static int vhost_user_crypto_create_session(struct vhost_dev *dev, int ret; bool crypto_session = virtio_has_feature(dev->protocol_features, VHOST_USER_PROTOCOL_F_CRYPTO_SESSION); - CryptoDevBackendSymSessionInfo *sess_info = session_info; + CryptoDevBackendSessionInfo *backend_info = session_info; VhostUserMsg msg = { .hdr.request = VHOST_USER_CREATE_CRYPTO_SESSION, .hdr.flags = VHOST_USER_VERSION, @@ -2390,16 +2524,53 @@ static int vhost_user_crypto_create_session(struct vhost_dev *dev, return -ENOTSUP; } - memcpy(&msg.payload.session.session_setup_data, sess_info, - sizeof(CryptoDevBackendSymSessionInfo)); - if (sess_info->key_len) { - memcpy(&msg.payload.session.key, sess_info->cipher_key, - sess_info->key_len); - } - if (sess_info->auth_key_len > 0) { - memcpy(&msg.payload.session.auth_key, sess_info->auth_key, - sess_info->auth_key_len); + if (backend_info->op_code == VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION) { + CryptoDevBackendAsymSessionInfo *sess = &backend_info->u.asym_sess_info; + size_t keylen; + + memcpy(&msg.payload.session.u.asym.session_setup_data, sess, + sizeof(CryptoDevBackendAsymSessionInfo)); + if (sess->keylen) { + keylen = sizeof(msg.payload.session.u.asym.key); + if (sess->keylen > keylen) { + error_report("Unsupported asymmetric key size"); + return -ENOTSUP; + } + + memcpy(&msg.payload.session.u.asym.key, sess->key, + sess->keylen); + } + } else { + CryptoDevBackendSymSessionInfo *sess = &backend_info->u.sym_sess_info; + size_t keylen; + + memcpy(&msg.payload.session.u.sym.session_setup_data, sess, + sizeof(CryptoDevBackendSymSessionInfo)); + if (sess->key_len) { + keylen = sizeof(msg.payload.session.u.sym.key); + if (sess->key_len > keylen) { + error_report("Unsupported cipher key size"); + return -ENOTSUP; + } + + memcpy(&msg.payload.session.u.sym.key, sess->cipher_key, + sess->key_len); + } + + if (sess->auth_key_len > 0) { + keylen = sizeof(msg.payload.session.u.sym.auth_key); + if (sess->auth_key_len > keylen) { + error_report("Unsupported auth key size"); + return -ENOTSUP; + } + + memcpy(&msg.payload.session.u.sym.auth_key, sess->auth_key, + sess->auth_key_len); + } } + + msg.payload.session.op_code = backend_info->op_code; + msg.payload.session.session_id = backend_info->session_id; ret = vhost_user_write(dev, &msg, NULL, 0); if (ret < 0) { error_report("vhost_user_write() return %d, create session failed", @@ -2463,14 +2634,9 @@ vhost_user_crypto_close_session(struct vhost_dev *dev, uint64_t session_id) return 0; } -static bool vhost_user_mem_section_filter(struct vhost_dev *dev, - MemoryRegionSection *section) +static bool vhost_user_no_private_memslots(struct vhost_dev *dev) { - bool result; - - result = memory_region_get_fd(section->mr) >= 0; - - return result; + return true; } static int vhost_user_get_inflight_fd(struct vhost_dev *dev, @@ -2604,11 +2770,256 @@ void vhost_user_cleanup(VhostUserState *user) user->chr = NULL; } + +typedef struct { + vu_async_close_fn cb; + DeviceState *dev; + CharBackend *cd; + struct vhost_dev *vhost; + IOEventHandler *event_cb; +} VhostAsyncCallback; + +static void vhost_user_async_close_bh(void *opaque) +{ + VhostAsyncCallback *data = opaque; + struct vhost_dev *vhost = data->vhost; + + /* + * If the vhost_dev has been cleared in the meantime there is + * nothing left to do as some other path has completed the + * cleanup. + */ + if (vhost->vdev) { + data->cb(data->dev); + } else if (data->event_cb) { + qemu_chr_fe_set_handlers(data->cd, NULL, NULL, data->event_cb, + NULL, data->dev, NULL, true); + } + + g_free(data); +} + +/* + * We only schedule the work if the machine is running. If suspended + * we want to keep all the in-flight data as is for migration + * purposes. + */ +void vhost_user_async_close(DeviceState *d, + CharBackend *chardev, struct vhost_dev *vhost, + vu_async_close_fn cb, + IOEventHandler *event_cb) +{ + if (!runstate_check(RUN_STATE_SHUTDOWN)) { + /* + * A close event may happen during a read/write, but vhost + * code assumes the vhost_dev remains setup, so delay the + * stop & clear. + */ + AioContext *ctx = qemu_get_current_aio_context(); + VhostAsyncCallback *data = g_new0(VhostAsyncCallback, 1); + + /* Save data for the callback */ + data->cb = cb; + data->dev = d; + data->cd = chardev; + data->vhost = vhost; + data->event_cb = event_cb; + + /* Disable any further notifications on the chardev */ + qemu_chr_fe_set_handlers(chardev, + NULL, NULL, NULL, NULL, NULL, NULL, + false); + + aio_bh_schedule_oneshot(ctx, vhost_user_async_close_bh, data); + + /* + * Move vhost device to the stopped state. The vhost-user device + * will be clean up and disconnected in BH. This can be useful in + * the vhost migration code. If disconnect was caught there is an + * option for the general vhost code to get the dev state without + * knowing its type (in this case vhost-user). + * + * Note if the vhost device is fully cleared by the time we + * execute the bottom half we won't continue with the cleanup. + */ + vhost->started = false; + } +} + +static int vhost_user_dev_start(struct vhost_dev *dev, bool started) +{ + if (!virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_STATUS)) { + return 0; + } + + /* Set device status only for last queue pair */ + if (dev->vq_index + dev->nvqs != dev->vq_index_end) { + return 0; + } + + if (started) { + return vhost_user_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER | + VIRTIO_CONFIG_S_DRIVER_OK); + } else { + return 0; + } +} + +static void vhost_user_reset_status(struct vhost_dev *dev) +{ + /* Set device status only for last queue pair */ + if (dev->vq_index + dev->nvqs != dev->vq_index_end) { + return; + } + + if (virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_STATUS)) { + vhost_user_set_status(dev, 0); + } +} + +static bool vhost_user_supports_device_state(struct vhost_dev *dev) +{ + return virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_DEVICE_STATE); +} + +static int vhost_user_set_device_state_fd(struct vhost_dev *dev, + VhostDeviceStateDirection direction, + VhostDeviceStatePhase phase, + int fd, + int *reply_fd, + Error **errp) +{ + int ret; + struct vhost_user *vu = dev->opaque; + VhostUserMsg msg = { + .hdr = { + .request = VHOST_USER_SET_DEVICE_STATE_FD, + .flags = VHOST_USER_VERSION, + .size = sizeof(msg.payload.transfer_state), + }, + .payload.transfer_state = { + .direction = direction, + .phase = phase, + }, + }; + + *reply_fd = -1; + + if (!vhost_user_supports_device_state(dev)) { + close(fd); + error_setg(errp, "Back-end does not support migration state transfer"); + return -ENOTSUP; + } + + ret = vhost_user_write(dev, &msg, &fd, 1); + close(fd); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to send SET_DEVICE_STATE_FD message"); + return ret; + } + + ret = vhost_user_read(dev, &msg); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to receive SET_DEVICE_STATE_FD reply"); + return ret; + } + + if (msg.hdr.request != VHOST_USER_SET_DEVICE_STATE_FD) { + error_setg(errp, + "Received unexpected message type, expected %d, received %d", + VHOST_USER_SET_DEVICE_STATE_FD, msg.hdr.request); + return -EPROTO; + } + + if (msg.hdr.size != sizeof(msg.payload.u64)) { + error_setg(errp, + "Received bad message size, expected %zu, received %" PRIu32, + sizeof(msg.payload.u64), msg.hdr.size); + return -EPROTO; + } + + if ((msg.payload.u64 & 0xff) != 0) { + error_setg(errp, "Back-end did not accept migration state transfer"); + return -EIO; + } + + if (!(msg.payload.u64 & VHOST_USER_VRING_NOFD_MASK)) { + *reply_fd = qemu_chr_fe_get_msgfd(vu->user->chr); + if (*reply_fd < 0) { + error_setg(errp, + "Failed to get back-end-provided transfer pipe FD"); + *reply_fd = -1; + return -EIO; + } + } + + return 0; +} + +static int vhost_user_check_device_state(struct vhost_dev *dev, Error **errp) +{ + int ret; + VhostUserMsg msg = { + .hdr = { + .request = VHOST_USER_CHECK_DEVICE_STATE, + .flags = VHOST_USER_VERSION, + .size = 0, + }, + }; + + if (!vhost_user_supports_device_state(dev)) { + error_setg(errp, "Back-end does not support migration state transfer"); + return -ENOTSUP; + } + + ret = vhost_user_write(dev, &msg, NULL, 0); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to send CHECK_DEVICE_STATE message"); + return ret; + } + + ret = vhost_user_read(dev, &msg); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to receive CHECK_DEVICE_STATE reply"); + return ret; + } + + if (msg.hdr.request != VHOST_USER_CHECK_DEVICE_STATE) { + error_setg(errp, + "Received unexpected message type, expected %d, received %d", + VHOST_USER_CHECK_DEVICE_STATE, msg.hdr.request); + return -EPROTO; + } + + if (msg.hdr.size != sizeof(msg.payload.u64)) { + error_setg(errp, + "Received bad message size, expected %zu, received %" PRIu32, + sizeof(msg.payload.u64), msg.hdr.size); + return -EPROTO; + } + + if (msg.payload.u64 != 0) { + error_setg(errp, "Back-end failed to process its internal state"); + return -EIO; + } + + return 0; +} + const VhostOps user_ops = { .backend_type = VHOST_BACKEND_TYPE_USER, .vhost_backend_init = vhost_user_backend_init, .vhost_backend_cleanup = vhost_user_backend_cleanup, .vhost_backend_memslots_limit = vhost_user_memslots_limit, + .vhost_backend_no_private_memslots = vhost_user_no_private_memslots, .vhost_set_log_base = vhost_user_set_log_base, .vhost_set_mem_table = vhost_user_set_mem_table, .vhost_set_vring_addr = vhost_user_set_vring_addr, @@ -2618,6 +3029,7 @@ const VhostOps user_ops = { .vhost_get_vring_base = vhost_user_get_vring_base, .vhost_set_vring_kick = vhost_user_set_vring_kick, .vhost_set_vring_call = vhost_user_set_vring_call, + .vhost_set_vring_err = vhost_user_set_vring_err, .vhost_set_features = vhost_user_set_features, .vhost_get_features = vhost_user_get_features, .vhost_set_owner = vhost_user_set_owner, @@ -2626,7 +3038,6 @@ const VhostOps user_ops = { .vhost_set_vring_enable = vhost_user_set_vring_enable, .vhost_requires_shm_log = vhost_user_requires_shm_log, .vhost_migration_done = vhost_user_migration_done, - .vhost_backend_can_merge = vhost_user_can_merge, .vhost_net_set_mtu = vhost_user_net_set_mtu, .vhost_set_iotlb_callback = vhost_user_set_iotlb_callback, .vhost_send_device_iotlb_msg = vhost_user_send_device_iotlb_msg, @@ -2634,7 +3045,11 @@ const VhostOps user_ops = { .vhost_set_config = vhost_user_set_config, .vhost_crypto_create_session = vhost_user_crypto_create_session, .vhost_crypto_close_session = vhost_user_crypto_close_session, - .vhost_backend_mem_section_filter = vhost_user_mem_section_filter, .vhost_get_inflight_fd = vhost_user_get_inflight_fd, .vhost_set_inflight_fd = vhost_user_set_inflight_fd, + .vhost_dev_start = vhost_user_dev_start, + .vhost_reset_status = vhost_user_reset_status, + .vhost_supports_device_state = vhost_user_supports_device_state, + .vhost_set_device_state_fd = vhost_user_set_device_state_fd, + .vhost_check_device_state = vhost_user_check_device_state, }; diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 66f054a12c..e827b9175f 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -14,15 +14,16 @@ #include #include #include +#include "exec/target_page.h" #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-backend.h" #include "hw/virtio/virtio-net.h" #include "hw/virtio/vhost-shadow-virtqueue.h" #include "hw/virtio/vhost-vdpa.h" #include "exec/address-spaces.h" +#include "migration/blocker.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" -#include "cpu.h" #include "trace.h" #include "qapi/error.h" @@ -30,26 +31,33 @@ * Return one past the end of the end of section. Be careful with uint64_t * conversions! */ -static Int128 vhost_vdpa_section_end(const MemoryRegionSection *section) +static Int128 vhost_vdpa_section_end(const MemoryRegionSection *section, + int page_mask) { Int128 llend = int128_make64(section->offset_within_address_space); llend = int128_add(llend, section->size); - llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); + llend = int128_and(llend, int128_exts64(page_mask)); return llend; } static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section, uint64_t iova_min, - uint64_t iova_max) + uint64_t iova_max, + int page_mask) { Int128 llend; + bool is_ram = memory_region_is_ram(section->mr); + bool is_iommu = memory_region_is_iommu(section->mr); + bool is_protected = memory_region_is_protected(section->mr); - if ((!memory_region_is_ram(section->mr) && - !memory_region_is_iommu(section->mr)) || - memory_region_is_protected(section->mr) || - /* vhost-vDPA doesn't allow MMIO to be mapped */ - memory_region_is_ram_device(section->mr)) { + /* vhost-vDPA doesn't allow MMIO to be mapped */ + bool is_ram_device = memory_region_is_ram_device(section->mr); + + if ((!is_ram && !is_iommu) || is_protected || is_ram_device) { + trace_vhost_vdpa_skipped_memory_section(is_ram, is_iommu, is_protected, + is_ram_device, iova_min, + iova_max, page_mask); return true; } @@ -59,34 +67,48 @@ static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section, iova_min, section->offset_within_address_space); return true; } + /* + * While using vIOMMU, sometimes the section will be larger than iova_max, + * but the memory that actually maps is smaller, so move the check to + * function vhost_vdpa_iommu_map_notify(). That function will use the actual + * size that maps to the kernel + */ - llend = vhost_vdpa_section_end(section); - if (int128_gt(llend, int128_make64(iova_max))) { - error_report("RAM section out of device range (max=0x%" PRIx64 - ", end addr=0x%" PRIx64 ")", - iova_max, int128_get64(llend)); - return true; + if (!is_iommu) { + llend = vhost_vdpa_section_end(section, page_mask); + if (int128_gt(llend, int128_make64(iova_max))) { + error_report("RAM section out of device range (max=0x%" PRIx64 + ", end addr=0x%" PRIx64 ")", + iova_max, int128_get64(llend)); + return true; + } } return false; } -static int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size, - void *vaddr, bool readonly) +/* + * The caller must set asid = 0 if the device does not support asid. + * This is not an ABI break since it is set to 0 by the initializer anyway. + */ +int vhost_vdpa_dma_map(VhostVDPAShared *s, uint32_t asid, hwaddr iova, + hwaddr size, void *vaddr, bool readonly) { struct vhost_msg_v2 msg = {}; - int fd = v->device_fd; + int fd = s->device_fd; int ret = 0; - msg.type = v->msg_type; + msg.type = VHOST_IOTLB_MSG_V2; + msg.asid = asid; msg.iotlb.iova = iova; msg.iotlb.size = size; msg.iotlb.uaddr = (uint64_t)(uintptr_t)vaddr; msg.iotlb.perm = readonly ? VHOST_ACCESS_RO : VHOST_ACCESS_RW; msg.iotlb.type = VHOST_IOTLB_UPDATE; - trace_vhost_vdpa_dma_map(v, fd, msg.type, msg.iotlb.iova, msg.iotlb.size, - msg.iotlb.uaddr, msg.iotlb.perm, msg.iotlb.type); + trace_vhost_vdpa_dma_map(s, fd, msg.type, msg.asid, msg.iotlb.iova, + msg.iotlb.size, msg.iotlb.uaddr, msg.iotlb.perm, + msg.iotlb.type); if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { error_report("failed to write, fd=%d, errno=%d (%s)", @@ -97,19 +119,24 @@ static int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size, return ret; } -static int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, hwaddr iova, - hwaddr size) +/* + * The caller must set asid = 0 if the device does not support asid. + * This is not an ABI break since it is set to 0 by the initializer anyway. + */ +int vhost_vdpa_dma_unmap(VhostVDPAShared *s, uint32_t asid, hwaddr iova, + hwaddr size) { struct vhost_msg_v2 msg = {}; - int fd = v->device_fd; + int fd = s->device_fd; int ret = 0; - msg.type = v->msg_type; + msg.type = VHOST_IOTLB_MSG_V2; + msg.asid = asid; msg.iotlb.iova = iova; msg.iotlb.size = size; msg.iotlb.type = VHOST_IOTLB_INVALIDATE; - trace_vhost_vdpa_dma_unmap(v, fd, msg.type, msg.iotlb.iova, + trace_vhost_vdpa_dma_unmap(s, fd, msg.type, msg.asid, msg.iotlb.iova, msg.iotlb.size, msg.iotlb.type); if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { @@ -121,80 +148,197 @@ static int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, hwaddr iova, return ret; } -static void vhost_vdpa_listener_begin_batch(struct vhost_vdpa *v) +static void vhost_vdpa_listener_begin_batch(VhostVDPAShared *s) { - int fd = v->device_fd; + int fd = s->device_fd; struct vhost_msg_v2 msg = { - .type = v->msg_type, + .type = VHOST_IOTLB_MSG_V2, .iotlb.type = VHOST_IOTLB_BATCH_BEGIN, }; - trace_vhost_vdpa_listener_begin_batch(v, fd, msg.type, msg.iotlb.type); + trace_vhost_vdpa_listener_begin_batch(s, fd, msg.type, msg.iotlb.type); if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { error_report("failed to write, fd=%d, errno=%d (%s)", fd, errno, strerror(errno)); } } -static void vhost_vdpa_iotlb_batch_begin_once(struct vhost_vdpa *v) +static void vhost_vdpa_iotlb_batch_begin_once(VhostVDPAShared *s) { - if (v->dev->backend_cap & (0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH) && - !v->iotlb_batch_begin_sent) { - vhost_vdpa_listener_begin_batch(v); + if (s->backend_cap & (0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH) && + !s->iotlb_batch_begin_sent) { + vhost_vdpa_listener_begin_batch(s); } - v->iotlb_batch_begin_sent = true; + s->iotlb_batch_begin_sent = true; } static void vhost_vdpa_listener_commit(MemoryListener *listener) { - struct vhost_vdpa *v = container_of(listener, struct vhost_vdpa, listener); - struct vhost_dev *dev = v->dev; + VhostVDPAShared *s = container_of(listener, VhostVDPAShared, listener); struct vhost_msg_v2 msg = {}; - int fd = v->device_fd; + int fd = s->device_fd; - if (!(dev->backend_cap & (0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH))) { + if (!(s->backend_cap & (0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH))) { return; } - if (!v->iotlb_batch_begin_sent) { + if (!s->iotlb_batch_begin_sent) { return; } - msg.type = v->msg_type; + msg.type = VHOST_IOTLB_MSG_V2; msg.iotlb.type = VHOST_IOTLB_BATCH_END; - trace_vhost_vdpa_listener_commit(v, fd, msg.type, msg.iotlb.type); + trace_vhost_vdpa_listener_commit(s, fd, msg.type, msg.iotlb.type); if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { error_report("failed to write, fd=%d, errno=%d (%s)", fd, errno, strerror(errno)); } - v->iotlb_batch_begin_sent = false; + s->iotlb_batch_begin_sent = false; +} + +static void vhost_vdpa_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) +{ + struct vdpa_iommu *iommu = container_of(n, struct vdpa_iommu, n); + + hwaddr iova = iotlb->iova + iommu->iommu_offset; + VhostVDPAShared *s = iommu->dev_shared; + void *vaddr; + int ret; + Int128 llend; + + if (iotlb->target_as != &address_space_memory) { + error_report("Wrong target AS \"%s\", only system memory is allowed", + iotlb->target_as->name ? iotlb->target_as->name : "none"); + return; + } + RCU_READ_LOCK_GUARD(); + /* check if RAM section out of device range */ + llend = int128_add(int128_makes64(iotlb->addr_mask), int128_makes64(iova)); + if (int128_gt(llend, int128_make64(s->iova_range.last))) { + error_report("RAM section out of device range (max=0x%" PRIx64 + ", end addr=0x%" PRIx64 ")", + s->iova_range.last, int128_get64(llend)); + return; + } + + if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) { + bool read_only; + + if (!memory_get_xlat_addr(iotlb, &vaddr, NULL, &read_only, NULL)) { + return; + } + ret = vhost_vdpa_dma_map(s, VHOST_VDPA_GUEST_PA_ASID, iova, + iotlb->addr_mask + 1, vaddr, read_only); + if (ret) { + error_report("vhost_vdpa_dma_map(%p, 0x%" HWADDR_PRIx ", " + "0x%" HWADDR_PRIx ", %p) = %d (%m)", + s, iova, iotlb->addr_mask + 1, vaddr, ret); + } + } else { + ret = vhost_vdpa_dma_unmap(s, VHOST_VDPA_GUEST_PA_ASID, iova, + iotlb->addr_mask + 1); + if (ret) { + error_report("vhost_vdpa_dma_unmap(%p, 0x%" HWADDR_PRIx ", " + "0x%" HWADDR_PRIx ") = %d (%m)", + s, iova, iotlb->addr_mask + 1, ret); + } + } +} + +static void vhost_vdpa_iommu_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + VhostVDPAShared *s = container_of(listener, VhostVDPAShared, listener); + + struct vdpa_iommu *iommu; + Int128 end; + int iommu_idx; + IOMMUMemoryRegion *iommu_mr; + int ret; + + iommu_mr = IOMMU_MEMORY_REGION(section->mr); + + iommu = g_malloc0(sizeof(*iommu)); + end = int128_add(int128_make64(section->offset_within_region), + section->size); + end = int128_sub(end, int128_one()); + iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr, + MEMTXATTRS_UNSPECIFIED); + iommu->iommu_mr = iommu_mr; + iommu_notifier_init(&iommu->n, vhost_vdpa_iommu_map_notify, + IOMMU_NOTIFIER_IOTLB_EVENTS, + section->offset_within_region, + int128_get64(end), + iommu_idx); + iommu->iommu_offset = section->offset_within_address_space - + section->offset_within_region; + iommu->dev_shared = s; + + ret = memory_region_register_iommu_notifier(section->mr, &iommu->n, NULL); + if (ret) { + g_free(iommu); + return; + } + + QLIST_INSERT_HEAD(&s->iommu_list, iommu, iommu_next); + memory_region_iommu_replay(iommu->iommu_mr, &iommu->n); + + return; +} + +static void vhost_vdpa_iommu_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + VhostVDPAShared *s = container_of(listener, VhostVDPAShared, listener); + + struct vdpa_iommu *iommu; + + QLIST_FOREACH(iommu, &s->iommu_list, iommu_next) + { + if (MEMORY_REGION(iommu->iommu_mr) == section->mr && + iommu->n.start == section->offset_within_region) { + memory_region_unregister_iommu_notifier(section->mr, &iommu->n); + QLIST_REMOVE(iommu, iommu_next); + g_free(iommu); + break; + } + } } static void vhost_vdpa_listener_region_add(MemoryListener *listener, MemoryRegionSection *section) { - struct vhost_vdpa *v = container_of(listener, struct vhost_vdpa, listener); + DMAMap mem_region = {}; + VhostVDPAShared *s = container_of(listener, VhostVDPAShared, listener); hwaddr iova; Int128 llend, llsize; void *vaddr; int ret; + int page_size = qemu_target_page_size(); + int page_mask = -page_size; - if (vhost_vdpa_listener_skipped_section(section, v->iova_range.first, - v->iova_range.last)) { + if (vhost_vdpa_listener_skipped_section(section, s->iova_range.first, + s->iova_range.last, page_mask)) { + return; + } + if (memory_region_is_iommu(section->mr)) { + vhost_vdpa_iommu_region_add(listener, section); return; } - if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != - (section->offset_within_region & ~TARGET_PAGE_MASK))) { - error_report("%s received unaligned region", __func__); + if (unlikely((section->offset_within_address_space & ~page_mask) != + (section->offset_within_region & ~page_mask))) { + trace_vhost_vdpa_listener_region_add_unaligned(s, section->mr->name, + section->offset_within_address_space & ~page_mask, + section->offset_within_region & ~page_mask); return; } - iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); - llend = vhost_vdpa_section_end(section); + iova = ROUND_UP(section->offset_within_address_space, page_size); + llend = vhost_vdpa_section_end(section, page_mask); if (int128_ge(int128_make64(iova), llend)) { return; } @@ -207,18 +351,18 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, section->offset_within_region + (iova - section->offset_within_address_space); - trace_vhost_vdpa_listener_region_add(v, iova, int128_get64(llend), + trace_vhost_vdpa_listener_region_add(s, iova, int128_get64(llend), vaddr, section->readonly); llsize = int128_sub(llend, int128_make64(iova)); - if (v->shadow_vqs_enabled) { - DMAMap mem_region = { - .translated_addr = (hwaddr)(uintptr_t)vaddr, - .size = int128_get64(llsize) - 1, - .perm = IOMMU_ACCESS_FLAG(true, section->readonly), - }; + if (s->shadow_data) { + int r; - int r = vhost_iova_tree_map_alloc(v->iova_tree, &mem_region); + mem_region.translated_addr = (hwaddr)(uintptr_t)vaddr, + mem_region.size = int128_get64(llsize) - 1, + mem_region.perm = IOMMU_ACCESS_FLAG(true, section->readonly), + + r = vhost_iova_tree_map_alloc(s->iova_tree, &mem_region); if (unlikely(r != IOVA_OK)) { error_report("Can't allocate a mapping (%d)", r); goto fail; @@ -227,16 +371,21 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, iova = mem_region.iova; } - vhost_vdpa_iotlb_batch_begin_once(v); - ret = vhost_vdpa_dma_map(v, iova, int128_get64(llsize), - vaddr, section->readonly); + vhost_vdpa_iotlb_batch_begin_once(s); + ret = vhost_vdpa_dma_map(s, VHOST_VDPA_GUEST_PA_ASID, iova, + int128_get64(llsize), vaddr, section->readonly); if (ret) { error_report("vhost vdpa map fail!"); - goto fail; + goto fail_map; } return; +fail_map: + if (s->shadow_data) { + vhost_iova_tree_remove(s->iova_tree, mem_region); + } + fail: /* * On the initfn path, store the first error in the container so we @@ -251,26 +400,34 @@ fail: static void vhost_vdpa_listener_region_del(MemoryListener *listener, MemoryRegionSection *section) { - struct vhost_vdpa *v = container_of(listener, struct vhost_vdpa, listener); + VhostVDPAShared *s = container_of(listener, VhostVDPAShared, listener); hwaddr iova; Int128 llend, llsize; int ret; + int page_size = qemu_target_page_size(); + int page_mask = -page_size; - if (vhost_vdpa_listener_skipped_section(section, v->iova_range.first, - v->iova_range.last)) { + if (vhost_vdpa_listener_skipped_section(section, s->iova_range.first, + s->iova_range.last, page_mask)) { + return; + } + if (memory_region_is_iommu(section->mr)) { + vhost_vdpa_iommu_region_del(listener, section); + } + + if (unlikely((section->offset_within_address_space & ~page_mask) != + (section->offset_within_region & ~page_mask))) { + trace_vhost_vdpa_listener_region_del_unaligned(s, section->mr->name, + section->offset_within_address_space & ~page_mask, + section->offset_within_region & ~page_mask); return; } - if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != - (section->offset_within_region & ~TARGET_PAGE_MASK))) { - error_report("%s received unaligned region", __func__); - return; - } + iova = ROUND_UP(section->offset_within_address_space, page_size); + llend = vhost_vdpa_section_end(section, page_mask); - iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); - llend = vhost_vdpa_section_end(section); - - trace_vhost_vdpa_listener_region_del(v, iova, int128_get64(llend)); + trace_vhost_vdpa_listener_region_del(s, iova, + int128_get64(int128_sub(llend, int128_one()))); if (int128_ge(int128_make64(iova), llend)) { return; @@ -278,7 +435,7 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, llsize = int128_sub(llend, int128_make64(iova)); - if (v->shadow_vqs_enabled) { + if (s->shadow_data) { const DMAMap *result; const void *vaddr = memory_region_get_ram_ptr(section->mr) + section->offset_within_region + @@ -288,14 +445,37 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, .size = int128_get64(llsize) - 1, }; - result = vhost_iova_tree_find_iova(v->iova_tree, &mem_region); + result = vhost_iova_tree_find_iova(s->iova_tree, &mem_region); + if (!result) { + /* The memory listener map wasn't mapped */ + return; + } iova = result->iova; - vhost_iova_tree_remove(v->iova_tree, &mem_region); + vhost_iova_tree_remove(s->iova_tree, *result); } - vhost_vdpa_iotlb_batch_begin_once(v); - ret = vhost_vdpa_dma_unmap(v, iova, int128_get64(llsize)); + vhost_vdpa_iotlb_batch_begin_once(s); + /* + * The unmap ioctl doesn't accept a full 64-bit. need to check it + */ + if (int128_eq(llsize, int128_2_64())) { + llsize = int128_rshift(llsize, 1); + ret = vhost_vdpa_dma_unmap(s, VHOST_VDPA_GUEST_PA_ASID, iova, + int128_get64(llsize)); + + if (ret) { + error_report("vhost_vdpa_dma_unmap(%p, 0x%" HWADDR_PRIx ", " + "0x%" HWADDR_PRIx ") = %d (%m)", + s, iova, int128_get64(llsize), ret); + } + iova += int128_get64(llsize); + } + ret = vhost_vdpa_dma_unmap(s, VHOST_VDPA_GUEST_PA_ASID, iova, + int128_get64(llsize)); + if (ret) { - error_report("vhost_vdpa dma unmap error!"); + error_report("vhost_vdpa_dma_unmap(%p, 0x%" HWADDR_PRIx ", " + "0x%" HWADDR_PRIx ") = %d (%m)", + s, iova, int128_get64(llsize), ret); } memory_region_unref(section->mr); @@ -316,7 +496,7 @@ static int vhost_vdpa_call(struct vhost_dev *dev, unsigned long int request, void *arg) { struct vhost_vdpa *v = dev->opaque; - int fd = v->device_fd; + int fd = v->shared->device_fd; int ret; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA); @@ -335,6 +515,10 @@ static int vhost_vdpa_add_status(struct vhost_dev *dev, uint8_t status) if (ret < 0) { return ret; } + if ((s & status) == status) { + /* Don't set bits already set */ + return 0; + } s |= status; @@ -355,17 +539,11 @@ static int vhost_vdpa_add_status(struct vhost_dev *dev, uint8_t status) return 0; } -static void vhost_vdpa_get_iova_range(struct vhost_vdpa *v) +int vhost_vdpa_get_iova_range(int fd, struct vhost_vdpa_iova_range *iova_range) { - int ret = vhost_vdpa_call(v->dev, VHOST_VDPA_GET_IOVA_RANGE, - &v->iova_range); - if (ret != 0) { - v->iova_range.first = 0; - v->iova_range.last = UINT64_MAX; - } + int ret = ioctl(fd, VHOST_VDPA_GET_IOVA_RANGE, iova_range); - trace_vhost_vdpa_get_iova_range(v->dev, v->iova_range.first, - v->iova_range.last); + return ret < 0 ? -errno : 0; } /* @@ -382,6 +560,11 @@ static bool vhost_vdpa_first_dev(struct vhost_dev *dev) return v->index == 0; } +static bool vhost_vdpa_last_dev(struct vhost_dev *dev) +{ + return dev->vq_index + dev->nvqs == dev->vq_index_end; +} + static int vhost_vdpa_get_dev_features(struct vhost_dev *dev, uint64_t *features) { @@ -392,52 +575,53 @@ static int vhost_vdpa_get_dev_features(struct vhost_dev *dev, return ret; } -static int vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v, - Error **errp) +static void vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v) { g_autoptr(GPtrArray) shadow_vqs = NULL; - uint64_t dev_features, svq_features; - int r; - bool ok; - - if (!v->shadow_vqs_enabled) { - return 0; - } - - r = vhost_vdpa_get_dev_features(hdev, &dev_features); - if (r != 0) { - error_setg_errno(errp, -r, "Can't get vdpa device features"); - return r; - } - - svq_features = dev_features; - ok = vhost_svq_valid_features(svq_features, errp); - if (unlikely(!ok)) { - return -1; - } shadow_vqs = g_ptr_array_new_full(hdev->nvqs, vhost_svq_free); for (unsigned n = 0; n < hdev->nvqs; ++n) { - g_autoptr(VhostShadowVirtqueue) svq = vhost_svq_new(v->iova_tree); + VhostShadowVirtqueue *svq; - if (unlikely(!svq)) { - error_setg(errp, "Cannot create svq %u", n); - return -1; - } - g_ptr_array_add(shadow_vqs, g_steal_pointer(&svq)); + svq = vhost_svq_new(v->shadow_vq_ops, v->shadow_vq_ops_opaque); + g_ptr_array_add(shadow_vqs, svq); } v->shadow_vqs = g_steal_pointer(&shadow_vqs); - return 0; } static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) { - struct vhost_vdpa *v; + struct vhost_vdpa *v = opaque; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA); - trace_vhost_vdpa_init(dev, opaque); + trace_vhost_vdpa_init(dev, v->shared, opaque); int ret; + v->dev = dev; + dev->opaque = opaque ; + v->shared->listener = vhost_vdpa_memory_listener; + vhost_vdpa_init_svq(dev, v); + + error_propagate(&dev->migration_blocker, v->migration_blocker); + if (!vhost_vdpa_first_dev(dev)) { + return 0; + } + + /* + * If dev->shadow_vqs_enabled at initialization that means the device has + * been started with x-svq=on, so don't block migration + */ + if (dev->migration_blocker == NULL && !v->shadow_vqs_enabled) { + /* We don't have dev->features yet */ + uint64_t features; + ret = vhost_vdpa_get_dev_features(dev, &features); + if (unlikely(ret)) { + error_setg_errno(errp, -ret, "Could not get device features"); + return ret; + } + vhost_svq_valid_features(features, &dev->migration_blocker); + } + /* * Similar to VFIO, we end up pinning all guest memory and have to * disable discarding of RAM. @@ -448,30 +632,10 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) return ret; } - v = opaque; - v->dev = dev; - dev->opaque = opaque ; - v->listener = vhost_vdpa_memory_listener; - v->msg_type = VHOST_IOTLB_MSG_V2; - ret = vhost_vdpa_init_svq(dev, v, errp); - if (ret) { - goto err; - } - - vhost_vdpa_get_iova_range(v); - - if (!vhost_vdpa_first_dev(dev)) { - return 0; - } - vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER); return 0; - -err: - ram_block_discard_disable(false); - return ret; } static void vhost_vdpa_host_notifier_uninit(struct vhost_dev *dev, @@ -498,7 +662,7 @@ static int vhost_vdpa_host_notifier_init(struct vhost_dev *dev, int queue_index) struct vhost_vdpa *v = dev->opaque; VirtIODevice *vdev = dev->vdev; VhostVDPAHostNotifier *n; - int fd = v->device_fd; + int fd = v->shared->device_fd; void *addr; char *name; @@ -535,9 +699,18 @@ static void vhost_vdpa_host_notifiers_uninit(struct vhost_dev *dev, int n) { int i; + /* + * Pack all the changes to the memory regions in a single + * transaction to avoid a few updating of the address space + * topology. + */ + memory_region_transaction_begin(); + for (i = dev->vq_index; i < dev->vq_index + n; i++) { vhost_vdpa_host_notifier_uninit(dev, i); } + + memory_region_transaction_commit(); } static void vhost_vdpa_host_notifiers_init(struct vhost_dev *dev) @@ -550,17 +723,21 @@ static void vhost_vdpa_host_notifiers_init(struct vhost_dev *dev) return; } + /* + * Pack all the changes to the memory regions in a single + * transaction to avoid a few updating of the address space + * topology. + */ + memory_region_transaction_begin(); + for (i = dev->vq_index; i < dev->vq_index + dev->nvqs; i++) { if (vhost_vdpa_host_notifier_init(dev, i)) { - goto err; + vhost_vdpa_host_notifiers_uninit(dev, i - dev->vq_index); + break; } } - return; - -err: - vhost_vdpa_host_notifiers_uninit(dev, i - dev->vq_index); - return; + memory_region_transaction_commit(); } static void vhost_vdpa_svq_cleanup(struct vhost_dev *dev) @@ -568,10 +745,6 @@ static void vhost_vdpa_svq_cleanup(struct vhost_dev *dev) struct vhost_vdpa *v = dev->opaque; size_t idx; - if (!v->shadow_vqs) { - return; - } - for (idx = 0; idx < v->shadow_vqs->len; ++idx) { vhost_svq_stop(g_ptr_array_index(v->shadow_vqs, idx)); } @@ -584,12 +757,15 @@ static int vhost_vdpa_cleanup(struct vhost_dev *dev) assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA); v = dev->opaque; trace_vhost_vdpa_cleanup(dev, v); + if (vhost_vdpa_first_dev(dev)) { + ram_block_discard_disable(false); + memory_listener_unregister(&v->shared->listener); + } + vhost_vdpa_host_notifiers_uninit(dev, dev->nvqs); - memory_listener_unregister(&v->listener); vhost_vdpa_svq_cleanup(dev); dev->opaque = NULL; - ram_block_discard_disable(false); return 0; } @@ -663,9 +839,13 @@ static int vhost_vdpa_set_features(struct vhost_dev *dev, static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev) { + struct vhost_vdpa *v = dev->opaque; + uint64_t features; uint64_t f = 0x1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2 | - 0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH; + 0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH | + 0x1ULL << VHOST_BACKEND_F_IOTLB_ASID | + 0x1ULL << VHOST_BACKEND_F_SUSPEND; int r; if (vhost_vdpa_call(dev, VHOST_GET_BACKEND_FEATURES, &features)) { @@ -682,6 +862,7 @@ static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev) } dev->backend_cap = features; + v->shared->backend_cap = features; return 0; } @@ -695,28 +876,15 @@ static int vhost_vdpa_get_device_id(struct vhost_dev *dev, return ret; } -static void vhost_vdpa_reset_svq(struct vhost_vdpa *v) -{ - if (!v->shadow_vqs_enabled) { - return; - } - - for (unsigned i = 0; i < v->shadow_vqs->len; ++i) { - VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, i); - vhost_svq_stop(svq); - } -} - static int vhost_vdpa_reset_device(struct vhost_dev *dev) { struct vhost_vdpa *v = dev->opaque; int ret; uint8_t status = 0; - vhost_vdpa_reset_svq(v); - ret = vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &status); - trace_vhost_vdpa_reset_device(dev, status); + trace_vhost_vdpa_reset_device(dev); + v->suspended = false; return ret; } @@ -728,20 +896,48 @@ static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, int idx) return idx; } -static int vhost_vdpa_set_vring_ready(struct vhost_dev *dev) +static int vhost_vdpa_set_vring_enable_one(struct vhost_vdpa *v, unsigned idx, + int enable) { - int i; - trace_vhost_vdpa_set_vring_ready(dev); + struct vhost_dev *dev = v->dev; + struct vhost_vring_state state = { + .index = idx, + .num = enable, + }; + int r = vhost_vdpa_call(dev, VHOST_VDPA_SET_VRING_ENABLE, &state); + + trace_vhost_vdpa_set_vring_enable_one(dev, idx, enable, r); + return r; +} + +static int vhost_vdpa_set_vring_enable(struct vhost_dev *dev, int enable) +{ + struct vhost_vdpa *v = dev->opaque; + unsigned int i; + int ret; + for (i = 0; i < dev->nvqs; ++i) { - struct vhost_vring_state state = { - .index = dev->vq_index + i, - .num = 1, - }; - vhost_vdpa_call(dev, VHOST_VDPA_SET_VRING_ENABLE, &state); + ret = vhost_vdpa_set_vring_enable_one(v, i, enable); + if (ret < 0) { + return ret; + } } + return 0; } +int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, unsigned idx) +{ + return vhost_vdpa_set_vring_enable_one(v, idx, 1); +} + +static int vhost_vdpa_set_config_call(struct vhost_dev *dev, + int fd) +{ + trace_vhost_vdpa_set_config_call(dev, fd); + return vhost_vdpa_call(dev, VHOST_VDPA_SET_CONFIG_CALL, &fd); +} + static void vhost_vdpa_dump_config(struct vhost_dev *dev, const uint8_t *config, uint32_t config_len) { @@ -801,7 +997,10 @@ static int vhost_vdpa_get_config(struct vhost_dev *dev, uint8_t *config, static int vhost_vdpa_set_dev_vring_base(struct vhost_dev *dev, struct vhost_vring_state *ring) { - trace_vhost_vdpa_set_vring_base(dev, ring->index, ring->num); + struct vhost_vdpa *v = dev->opaque; + + trace_vhost_vdpa_set_dev_vring_base(dev, ring->index, ring->num, + v->shadow_vqs_enabled); return vhost_vdpa_call(dev, VHOST_SET_VRING_BASE, ring); } @@ -852,11 +1051,23 @@ static int vhost_vdpa_svq_set_fds(struct vhost_dev *dev, const EventNotifier *event_notifier = &svq->hdev_kick; int r; + r = event_notifier_init(&svq->hdev_kick, 0); + if (r != 0) { + error_setg_errno(errp, -r, "Couldn't create kick event notifier"); + goto err_init_hdev_kick; + } + + r = event_notifier_init(&svq->hdev_call, 0); + if (r != 0) { + error_setg_errno(errp, -r, "Couldn't create call event notifier"); + goto err_init_hdev_call; + } + file.fd = event_notifier_get_fd(event_notifier); r = vhost_vdpa_set_vring_dev_kick(dev, &file); if (unlikely(r != 0)) { error_setg_errno(errp, -r, "Can't set device kick fd"); - return r; + goto err_init_set_dev_fd; } event_notifier = &svq->hdev_call; @@ -864,49 +1075,61 @@ static int vhost_vdpa_svq_set_fds(struct vhost_dev *dev, r = vhost_vdpa_set_vring_dev_call(dev, &file); if (unlikely(r != 0)) { error_setg_errno(errp, -r, "Can't set device call fd"); + goto err_init_set_dev_fd; } + return 0; + +err_init_set_dev_fd: + event_notifier_set_handler(&svq->hdev_call, NULL); + +err_init_hdev_call: + event_notifier_cleanup(&svq->hdev_kick); + +err_init_hdev_kick: return r; } /** * Unmap a SVQ area in the device */ -static bool vhost_vdpa_svq_unmap_ring(struct vhost_vdpa *v, - const DMAMap *needle) +static void vhost_vdpa_svq_unmap_ring(struct vhost_vdpa *v, hwaddr addr) { - const DMAMap *result = vhost_iova_tree_find_iova(v->iova_tree, needle); + const DMAMap needle = { + .translated_addr = addr, + }; + const DMAMap *result = vhost_iova_tree_find_iova(v->shared->iova_tree, + &needle); hwaddr size; int r; if (unlikely(!result)) { error_report("Unable to find SVQ address to unmap"); - return false; + return; } size = ROUND_UP(result->size, qemu_real_host_page_size()); - r = vhost_vdpa_dma_unmap(v, result->iova, size); - return r == 0; + r = vhost_vdpa_dma_unmap(v->shared, v->address_space_id, result->iova, + size); + if (unlikely(r < 0)) { + error_report("Unable to unmap SVQ vring: %s (%d)", g_strerror(-r), -r); + return; + } + + vhost_iova_tree_remove(v->shared->iova_tree, *result); } -static bool vhost_vdpa_svq_unmap_rings(struct vhost_dev *dev, +static void vhost_vdpa_svq_unmap_rings(struct vhost_dev *dev, const VhostShadowVirtqueue *svq) { - DMAMap needle = {}; struct vhost_vdpa *v = dev->opaque; struct vhost_vring_addr svq_addr; - bool ok; vhost_svq_get_vring_addr(svq, &svq_addr); - needle.translated_addr = svq_addr.desc_user_addr; - ok = vhost_vdpa_svq_unmap_ring(v, &needle); - if (unlikely(!ok)) { - return false; - } + vhost_vdpa_svq_unmap_ring(v, svq_addr.desc_user_addr); - needle.translated_addr = svq_addr.used_user_addr; - return vhost_vdpa_svq_unmap_ring(v, &needle); + vhost_vdpa_svq_unmap_ring(v, svq_addr.used_user_addr); } /** @@ -921,18 +1144,19 @@ static bool vhost_vdpa_svq_map_ring(struct vhost_vdpa *v, DMAMap *needle, { int r; - r = vhost_iova_tree_map_alloc(v->iova_tree, needle); + r = vhost_iova_tree_map_alloc(v->shared->iova_tree, needle); if (unlikely(r != IOVA_OK)) { error_setg(errp, "Cannot allocate iova (%d)", r); return false; } - r = vhost_vdpa_dma_map(v, needle->iova, needle->size + 1, + r = vhost_vdpa_dma_map(v->shared, v->address_space_id, needle->iova, + needle->size + 1, (void *)(uintptr_t)needle->translated_addr, needle->perm == IOMMU_RO); if (unlikely(r != 0)) { error_setg_errno(errp, -r, "Cannot map region to device"); - vhost_iova_tree_remove(v->iova_tree, needle); + vhost_iova_tree_remove(v->shared->iova_tree, *needle); } return r == 0; @@ -951,6 +1175,7 @@ static bool vhost_vdpa_svq_map_rings(struct vhost_dev *dev, struct vhost_vring_addr *addr, Error **errp) { + ERRP_GUARD(); DMAMap device_region, driver_region; struct vhost_vring_addr svq_addr; struct vhost_vdpa *v = dev->opaque; @@ -959,7 +1184,6 @@ static bool vhost_vdpa_svq_map_rings(struct vhost_dev *dev, size_t avail_offset; bool ok; - ERRP_GUARD(); vhost_svq_get_vring_addr(svq, &svq_addr); driver_region = (DMAMap) { @@ -984,7 +1208,7 @@ static bool vhost_vdpa_svq_map_rings(struct vhost_dev *dev, ok = vhost_vdpa_svq_map_ring(v, &device_region, errp); if (unlikely(!ok)) { error_prepend(errp, "Cannot create vq device region: "); - vhost_vdpa_svq_unmap_ring(v, &driver_region); + vhost_vdpa_svq_unmap_ring(v, driver_region.translated_addr); } addr->used_user_addr = device_region.iova; @@ -1017,7 +1241,7 @@ static bool vhost_vdpa_svqs_start(struct vhost_dev *dev) Error *err = NULL; unsigned i; - if (!v->shadow_vqs) { + if (!v->shadow_vqs_enabled) { return true; } @@ -1033,7 +1257,7 @@ static bool vhost_vdpa_svqs_start(struct vhost_dev *dev) goto err; } - vhost_svq_start(svq, dev->vdev, vq); + vhost_svq_start(svq, dev->vdev, vq, v->shared->iova_tree); ok = vhost_vdpa_svq_map_rings(dev, svq, &addr, &err); if (unlikely(!ok)) { goto err_map; @@ -1066,23 +1290,46 @@ err: return false; } -static bool vhost_vdpa_svqs_stop(struct vhost_dev *dev) +static void vhost_vdpa_svqs_stop(struct vhost_dev *dev) { struct vhost_vdpa *v = dev->opaque; - if (!v->shadow_vqs) { - return true; + if (!v->shadow_vqs_enabled) { + return; } for (unsigned i = 0; i < v->shadow_vqs->len; ++i) { VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, i); - bool ok = vhost_vdpa_svq_unmap_rings(dev, svq); - if (unlikely(!ok)) { - return false; + + vhost_svq_stop(svq); + vhost_vdpa_svq_unmap_rings(dev, svq); + + event_notifier_cleanup(&svq->hdev_kick); + event_notifier_cleanup(&svq->hdev_call); + } +} + +static void vhost_vdpa_suspend(struct vhost_dev *dev) +{ + struct vhost_vdpa *v = dev->opaque; + int r; + + if (!vhost_vdpa_first_dev(dev)) { + return; + } + + if (dev->backend_cap & BIT_ULL(VHOST_BACKEND_F_SUSPEND)) { + trace_vhost_vdpa_suspend(dev); + r = ioctl(v->shared->device_fd, VHOST_VDPA_SUSPEND); + if (unlikely(r)) { + error_report("Cannot suspend: %s(%d)", g_strerror(errno), errno); + } else { + v->suspended = true; + return; } } - return true; + vhost_vdpa_reset_device(dev); } static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) @@ -1097,30 +1344,42 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) if (unlikely(!ok)) { return -1; } - vhost_vdpa_set_vring_ready(dev); } else { - ok = vhost_vdpa_svqs_stop(dev); - if (unlikely(!ok)) { - return -1; - } + vhost_vdpa_suspend(dev); + vhost_vdpa_svqs_stop(dev); vhost_vdpa_host_notifiers_uninit(dev, dev->nvqs); } - if (dev->vq_index + dev->nvqs != dev->vq_index_end) { + if (!vhost_vdpa_last_dev(dev)) { return 0; } if (started) { - memory_listener_register(&v->listener, &address_space_memory); - return vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); - } else { - vhost_vdpa_reset_device(dev); - vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | - VIRTIO_CONFIG_S_DRIVER); - memory_listener_unregister(&v->listener); + if (vhost_dev_has_iommu(dev) && (v->shadow_vqs_enabled)) { + error_report("SVQ can not work while IOMMU enable, please disable" + "IOMMU and try again"); + return -1; + } + memory_listener_register(&v->shared->listener, dev->vdev->dma_as); - return 0; + return vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); } + + return 0; +} + +static void vhost_vdpa_reset_status(struct vhost_dev *dev) +{ + struct vhost_vdpa *v = dev->opaque; + + if (!vhost_vdpa_last_dev(dev)) { + return; + } + + vhost_vdpa_reset_device(dev); + vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER); + memory_listener_unregister(&v->shared->listener); } static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base, @@ -1179,26 +1438,24 @@ static int vhost_vdpa_get_vring_base(struct vhost_dev *dev, struct vhost_vring_state *ring) { struct vhost_vdpa *v = dev->opaque; - int vdpa_idx = ring->index - dev->vq_index; int ret; if (v->shadow_vqs_enabled) { - VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, vdpa_idx); - - /* - * Setting base as last used idx, so destination will see as available - * all the entries that the device did not use, including the in-flight - * processing ones. - * - * TODO: This is ok for networking, but other kinds of devices might - * have problems with these retransmissions. - */ - ring->num = svq->last_used_idx; + ring->num = virtio_queue_get_last_avail_idx(dev->vdev, ring->index); + trace_vhost_vdpa_get_vring_base(dev, ring->index, ring->num, true); return 0; } + if (!v->suspended) { + /* + * Cannot trust in value returned by device, let vhost recover used + * idx from guest. + */ + return -1; + } + ret = vhost_vdpa_call(dev, VHOST_GET_VRING_BASE, ring); - trace_vhost_vdpa_get_vring_base(dev, ring->index, ring->num); + trace_vhost_vdpa_get_vring_base(dev, ring->index, ring->num, false); return ret; } @@ -1221,25 +1478,32 @@ static int vhost_vdpa_set_vring_call(struct vhost_dev *dev, struct vhost_vring_file *file) { struct vhost_vdpa *v = dev->opaque; + int vdpa_idx = file->index - dev->vq_index; + VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, vdpa_idx); - if (v->shadow_vqs_enabled) { - int vdpa_idx = file->index - dev->vq_index; - VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, vdpa_idx); - - vhost_svq_set_svq_call_fd(svq, file->fd); + /* Remember last call fd because we can switch to SVQ anytime. */ + vhost_svq_set_svq_call_fd(svq, file->fd); + /* + * When SVQ is transitioning to off, shadow_vqs_enabled has + * not been set back to false yet, but the underlying call fd + * will have to switch back to the guest notifier to signal the + * passthrough virtqueues. In other situations, SVQ's own call + * fd shall be used to signal the device model. + */ + if (v->shadow_vqs_enabled && + v->shared->svq_switching != SVQ_TSTATE_DISABLING) { return 0; - } else { - return vhost_vdpa_set_vring_dev_call(dev, file); } + + return vhost_vdpa_set_vring_dev_call(dev, file); } static int vhost_vdpa_get_features(struct vhost_dev *dev, uint64_t *features) { - struct vhost_vdpa *v = dev->opaque; int ret = vhost_vdpa_get_dev_features(dev, features); - if (ret == 0 && v->shadow_vqs_enabled) { + if (ret == 0) { /* Add SVQ logging capabilities */ *features |= BIT_ULL(VHOST_F_LOG_ALL); } @@ -1294,11 +1558,11 @@ const VhostOps vdpa_ops = { .vhost_set_features = vhost_vdpa_set_features, .vhost_reset_device = vhost_vdpa_reset_device, .vhost_get_vq_index = vhost_vdpa_get_vq_index, + .vhost_set_vring_enable = vhost_vdpa_set_vring_enable, .vhost_get_config = vhost_vdpa_get_config, .vhost_set_config = vhost_vdpa_set_config, .vhost_requires_shm_log = NULL, .vhost_migration_done = NULL, - .vhost_backend_can_merge = NULL, .vhost_net_set_mtu = NULL, .vhost_set_iotlb_callback = NULL, .vhost_send_device_iotlb_msg = NULL, @@ -1306,4 +1570,6 @@ const VhostOps vdpa_ops = { .vhost_get_device_id = vhost_vdpa_get_device_id, .vhost_vq_get_addr = vhost_vdpa_vq_get_addr, .vhost_force_iommu = vhost_vdpa_force_iommu, + .vhost_set_config_call = vhost_vdpa_set_config_call, + .vhost_reset_status = vhost_vdpa_reset_status, }; diff --git a/hw/virtio/vhost-vsock-common.c b/hw/virtio/vhost-vsock-common.c index 7394818e00..12ea87d7a7 100644 --- a/hw/virtio/vhost-vsock-common.c +++ b/hw/virtio/vhost-vsock-common.c @@ -11,15 +11,17 @@ #include "qemu/osdep.h" #include "standard-headers/linux/virtio_vsock.h" #include "qapi/error.h" -#include "hw/virtio/virtio-access.h" +#include "hw/virtio/virtio-bus.h" #include "qemu/error-report.h" #include "hw/qdev-properties.h" +#include "hw/virtio/vhost.h" #include "hw/virtio/vhost-vsock.h" #include "qemu/iov.h" #include "monitor/monitor.h" const int feature_bits[] = { VIRTIO_VSOCK_F_SEQPACKET, + VIRTIO_F_RING_RESET, VHOST_INVALID_FEATURE_BIT }; @@ -68,7 +70,7 @@ int vhost_vsock_common_start(VirtIODevice *vdev) } vvc->vhost_dev.acked_features = vdev->guest_features; - ret = vhost_dev_start(&vvc->vhost_dev, vdev); + ret = vhost_dev_start(&vvc->vhost_dev, vdev, true); if (ret < 0) { error_report("Error starting vhost: %d", -ret); goto err_guest_notifiers; @@ -103,7 +105,7 @@ void vhost_vsock_common_stop(VirtIODevice *vdev) return; } - vhost_dev_stop(&vvc->vhost_dev, vdev); + vhost_dev_stop(&vvc->vhost_dev, vdev, true); ret = k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false); if (ret < 0) { @@ -125,6 +127,15 @@ static void vhost_vsock_common_guest_notifier_mask(VirtIODevice *vdev, int idx, { VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); + /* + * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1 + * as the macro of configure interrupt's IDX, If this driver does not + * support, the function will return + */ + + if (idx == VIRTIO_CONFIG_IRQ_IDX) { + return; + } vhost_virtqueue_mask(&vvc->vhost_dev, vdev, idx, mask); } @@ -133,6 +144,15 @@ static bool vhost_vsock_common_guest_notifier_pending(VirtIODevice *vdev, { VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); + /* + * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1 + * as the macro of configure interrupt's IDX, If this driver does not + * support, the function will return + */ + + if (idx == VIRTIO_CONFIG_IRQ_IDX) { + return false; + } return vhost_virtqueue_pending(&vvc->vhost_dev, idx); } @@ -199,7 +219,7 @@ int vhost_vsock_common_pre_save(void *opaque) * At this point, backend must be stopped, otherwise * it might keep writing to memory. */ - assert(!vvc->vhost_dev.started); + assert(!vhost_dev_is_started(&vvc->vhost_dev)); return 0; } diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index 0338de892f..3d4a5a97f4 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -70,14 +70,10 @@ static int vhost_vsock_set_running(VirtIODevice *vdev, int start) static void vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status) { VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); - bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; + bool should_start = virtio_device_should_start(vdev, status); int ret; - if (!vdev->vm_running) { - should_start = false; - } - - if (vvc->vhost_dev.started == should_start) { + if (vhost_dev_is_started(&vvc->vhost_dev) == should_start) { return; } @@ -115,7 +111,7 @@ static const VMStateDescription vmstate_virtio_vhost_vsock = { .name = "virtio-vhost_vsock", .minimum_version_id = VHOST_VSOCK_SAVEVM_VERSION, .version_id = VHOST_VSOCK_SAVEVM_VERSION, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, @@ -125,6 +121,7 @@ static const VMStateDescription vmstate_virtio_vhost_vsock = { static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) { + ERRP_GUARD(); VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev); VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostVSock *vsock = VHOST_VSOCK(dev); diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index dd3263df56..f50180e60e 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -20,9 +20,10 @@ #include "qemu/range.h" #include "qemu/error-report.h" #include "qemu/memfd.h" +#include "qemu/log.h" #include "standard-headers/linux/vhost_types.h" #include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-access.h" +#include "hw/mem/memory-device.h" #include "migration/blocker.h" #include "migration/qemu-file-types.h" #include "sysemu/dma.h" @@ -45,20 +46,44 @@ static struct vhost_log *vhost_log; static struct vhost_log *vhost_log_shm; +/* Memslots used by backends that support private memslots (without an fd). */ static unsigned int used_memslots; + +/* Memslots used by backends that only support shared memslots (with an fd). */ +static unsigned int used_shared_memslots; + static QLIST_HEAD(, vhost_dev) vhost_devices = QLIST_HEAD_INITIALIZER(vhost_devices); -bool vhost_has_free_slot(void) +unsigned int vhost_get_max_memslots(void) { - unsigned int slots_limit = ~0U; + unsigned int max = UINT_MAX; + struct vhost_dev *hdev; + + QLIST_FOREACH(hdev, &vhost_devices, entry) { + max = MIN(max, hdev->vhost_ops->vhost_backend_memslots_limit(hdev)); + } + return max; +} + +unsigned int vhost_get_free_memslots(void) +{ + unsigned int free = UINT_MAX; struct vhost_dev *hdev; QLIST_FOREACH(hdev, &vhost_devices, entry) { unsigned int r = hdev->vhost_ops->vhost_backend_memslots_limit(hdev); - slots_limit = MIN(slots_limit, r); + unsigned int cur_free; + + if (hdev->vhost_ops->vhost_backend_no_private_memslots && + hdev->vhost_ops->vhost_backend_no_private_memslots(hdev)) { + cur_free = r - used_shared_memslots; + } else { + cur_free = r - used_memslots; + } + free = MIN(free, cur_free); } - return slots_limit > used_memslots; + return free; } static void vhost_dev_sync_region(struct vhost_dev *dev, @@ -66,12 +91,12 @@ static void vhost_dev_sync_region(struct vhost_dev *dev, uint64_t mfirst, uint64_t mlast, uint64_t rfirst, uint64_t rlast) { - vhost_log_chunk_t *log = dev->log->log; + vhost_log_chunk_t *dev_log = dev->log->log; uint64_t start = MAX(mfirst, rfirst); uint64_t end = MIN(mlast, rlast); - vhost_log_chunk_t *from = log + start / VHOST_LOG_CHUNK; - vhost_log_chunk_t *to = log + end / VHOST_LOG_CHUNK + 1; + vhost_log_chunk_t *from = dev_log + start / VHOST_LOG_CHUNK; + vhost_log_chunk_t *to = dev_log + end / VHOST_LOG_CHUNK + 1; uint64_t addr = QEMU_ALIGN_DOWN(start, VHOST_LOG_CHUNK); if (end < start) { @@ -106,6 +131,24 @@ static void vhost_dev_sync_region(struct vhost_dev *dev, } } +bool vhost_dev_has_iommu(struct vhost_dev *dev) +{ + VirtIODevice *vdev = dev->vdev; + + /* + * 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 transactions. + */ + if (vdev) { + return virtio_bus_device_iommu_enabled(vdev) && + virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); + } else { + return false; + } +} + static int vhost_sync_dirty_bitmap(struct vhost_dev *dev, MemoryRegionSection *section, hwaddr first, @@ -137,8 +180,51 @@ static int vhost_sync_dirty_bitmap(struct vhost_dev *dev, continue; } - vhost_dev_sync_region(dev, section, start_addr, end_addr, vq->used_phys, - range_get_last(vq->used_phys, vq->used_size)); + if (vhost_dev_has_iommu(dev)) { + IOMMUTLBEntry iotlb; + hwaddr used_phys = vq->used_phys, used_size = vq->used_size; + hwaddr phys, s, offset; + + while (used_size) { + rcu_read_lock(); + iotlb = address_space_get_iotlb_entry(dev->vdev->dma_as, + used_phys, + true, + MEMTXATTRS_UNSPECIFIED); + rcu_read_unlock(); + + if (!iotlb.target_as) { + qemu_log_mask(LOG_GUEST_ERROR, "translation " + "failure for used_iova %"PRIx64"\n", + used_phys); + return -EINVAL; + } + + offset = used_phys & iotlb.addr_mask; + phys = iotlb.translated_addr + offset; + + /* + * Distance from start of used ring until last byte of + * IOMMU page. + */ + s = iotlb.addr_mask - offset; + /* + * Size of used ring, or of the part of it until end + * of IOMMU page. To avoid zero result, do the adding + * outside of MIN(). + */ + s = MIN(s, used_size - 1) + 1; + + vhost_dev_sync_region(dev, section, start_addr, end_addr, phys, + range_get_last(phys, s)); + used_size -= s; + used_phys += s; + } + } else { + vhost_dev_sync_region(dev, section, start_addr, + end_addr, vq->used_phys, + range_get_last(vq->used_phys, vq->used_size)); + } } return 0; } @@ -306,20 +392,6 @@ static inline void vhost_dev_log_resize(struct vhost_dev *dev, uint64_t size) dev->log_size = size; } -static int vhost_dev_has_iommu(struct vhost_dev *dev) -{ - VirtIODevice *vdev = dev->vdev; - - /* - * 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 transactions. - */ - return virtio_bus_device_iommu_enabled(vdev) && - virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); -} - static void *vhost_memory_map(struct vhost_dev *dev, hwaddr addr, hwaddr *plen, bool is_write) { @@ -427,8 +499,7 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev, * vhost_section: identify sections needed for vhost access * * We only care about RAM sections here (where virtqueue and guest - * internals accessed by virtio might live). If we find one we still - * allow the backend to potentially filter it out of our list. + * internals accessed by virtio might live). */ static bool vhost_section(struct vhost_dev *dev, MemoryRegionSection *section) { @@ -455,8 +526,16 @@ static bool vhost_section(struct vhost_dev *dev, MemoryRegionSection *section) return false; } - if (dev->vhost_ops->vhost_backend_mem_section_filter && - !dev->vhost_ops->vhost_backend_mem_section_filter(dev, section)) { + /* + * Some backends (like vhost-user) can only handle memory regions + * that have an fd (can be mapped into a different process). Filter + * the ones without an fd out, if requested. + * + * TODO: we might have to limit to MAP_SHARED as well. + */ + if (memory_region_get_fd(section->mr) < 0 && + dev->vhost_ops->vhost_backend_no_private_memslots && + dev->vhost_ops->vhost_backend_no_private_memslots(dev)) { trace_vhost_reject_section(mr->name, 2); return false; } @@ -502,7 +581,7 @@ static void vhost_commit(MemoryListener *listener) changed = true; } else { /* Same size, lets check the contents */ - for (int i = 0; i < n_old_sections; i++) { + for (i = 0; i < n_old_sections; i++) { if (!MemoryRegionSection_eq(&old_sections[i], &dev->mem_sections[i])) { changed = true; @@ -521,7 +600,14 @@ static void vhost_commit(MemoryListener *listener) dev->n_mem_sections * sizeof dev->mem->regions[0]; dev->mem = g_realloc(dev->mem, regions_size); dev->mem->nregions = dev->n_mem_sections; - used_memslots = dev->mem->nregions; + + if (dev->vhost_ops->vhost_backend_no_private_memslots && + dev->vhost_ops->vhost_backend_no_private_memslots(dev)) { + used_shared_memslots = dev->mem->nregions; + } else { + used_memslots = dev->mem->nregions; + } + for (i = 0; i < dev->n_mem_sections; i++) { struct vhost_memory_region *cur_vmr = dev->mem->regions + i; struct MemoryRegionSection *mrs = dev->mem_sections + i; @@ -621,7 +707,7 @@ static void vhost_region_add_section(struct vhost_dev *dev, mrs_size, mrs_host); } - if (dev->n_tmp_sections) { + if (dev->n_tmp_sections && !section->unmergeable) { /* Since we already have at least one section, lets see if * this extends it; since we're scanning in order, we only * have to look at the last one, and the FlatView that calls @@ -654,11 +740,7 @@ static void vhost_region_add_section(struct vhost_dev *dev, size_t offset = mrs_gpa - prev_gpa_start; if (prev_host_start + offset == mrs_host && - section->mr == prev_sec->mr && - (!dev->vhost_ops->vhost_backend_can_merge || - dev->vhost_ops->vhost_backend_can_merge(dev, - mrs_host, mrs_size, - prev_host_start, prev_size))) { + section->mr == prev_sec->mr && !prev_sec->unmergeable) { uint64_t max_end = MAX(prev_host_end, mrs_host + mrs_size); need_add = false; prev_sec->offset_within_address_space = @@ -733,7 +815,6 @@ static void vhost_iommu_region_add(MemoryListener *listener, Int128 end; int iommu_idx; IOMMUMemoryRegion *iommu_mr; - int ret; if (!memory_region_is_iommu(section->mr)) { return; @@ -748,7 +829,9 @@ static void vhost_iommu_region_add(MemoryListener *listener, iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr, MEMTXATTRS_UNSPECIFIED); iommu_notifier_init(&iommu->n, vhost_iommu_unmap_notify, - IOMMU_NOTIFIER_DEVIOTLB_UNMAP, + dev->vdev->device_iotlb_enabled ? + IOMMU_NOTIFIER_DEVIOTLB_UNMAP : + IOMMU_NOTIFIER_UNMAP, section->offset_within_region, int128_get64(end), iommu_idx); @@ -756,16 +839,8 @@ static void vhost_iommu_region_add(MemoryListener *listener, iommu->iommu_offset = section->offset_within_address_space - section->offset_within_region; iommu->hdev = dev; - ret = memory_region_register_iommu_notifier(section->mr, &iommu->n, NULL); - if (ret) { - /* - * Some vIOMMUs do not support dev-iotlb yet. If so, try to use the - * UNMAP legacy message - */ - iommu->n.notifier_flags = IOMMU_NOTIFIER_UNMAP; - memory_region_register_iommu_notifier(section->mr, &iommu->n, - &error_fatal); - } + memory_region_register_iommu_notifier(section->mr, &iommu->n, + &error_fatal); QLIST_INSERT_HEAD(&dev->iommu_list, iommu, iommu_next); /* TODO: can replay help performance here? */ } @@ -793,6 +868,27 @@ static void vhost_iommu_region_del(MemoryListener *listener, } } +void vhost_toggle_device_iotlb(VirtIODevice *vdev) +{ + VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); + struct vhost_dev *dev; + struct vhost_iommu *iommu; + + if (vdev->vhost_started) { + dev = vdc->get_vhost(vdev); + } else { + return; + } + + QLIST_FOREACH(iommu, &dev->iommu_list, iommu_next) { + memory_region_unregister_iommu_notifier(iommu->mr, &iommu->n); + iommu->n.notifier_flags = vdev->device_iotlb_enabled ? + IOMMU_NOTIFIER_DEVIOTLB_UNMAP : IOMMU_NOTIFIER_UNMAP; + memory_region_register_iommu_notifier(iommu->mr, &iommu->n, + &error_fatal); + } +} + static int vhost_virtqueue_set_addr(struct vhost_dev *dev, struct vhost_virtqueue *vq, unsigned idx, bool enable_log) @@ -886,6 +982,10 @@ static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log) err_vq: for (; i >= 0; --i) { idx = dev->vhost_ops->vhost_get_vq_index(dev, dev->vq_index + i); + addr = virtio_queue_get_desc_addr(dev->vdev, idx); + if (!addr) { + continue; + } vhost_virtqueue_set_addr(dev, dev->vqs + i, idx, dev->log_enabled); } @@ -1073,10 +1173,10 @@ out: return ret; } -static int vhost_virtqueue_start(struct vhost_dev *dev, - struct VirtIODevice *vdev, - struct vhost_virtqueue *vq, - unsigned idx) +int vhost_virtqueue_start(struct vhost_dev *dev, + struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, + unsigned idx) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusState *vbus = VIRTIO_BUS(qbus); @@ -1193,10 +1293,10 @@ fail_alloc_desc: return r; } -static void vhost_virtqueue_stop(struct vhost_dev *dev, - struct VirtIODevice *vdev, - struct vhost_virtqueue *vq, - unsigned idx) +void vhost_virtqueue_stop(struct vhost_dev *dev, + struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, + unsigned idx) { int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx); struct vhost_vring_state state = { @@ -1239,18 +1339,6 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev, 0, virtio_queue_get_desc_size(vdev, idx)); } -static void vhost_eventfd_add(MemoryListener *listener, - MemoryRegionSection *section, - bool match_data, uint64_t data, EventNotifier *e) -{ -} - -static void vhost_eventfd_del(MemoryListener *listener, - MemoryRegionSection *section, - bool match_data, uint64_t data, EventNotifier *e) -{ -} - static int vhost_virtqueue_set_busyloop_timeout(struct vhost_dev *dev, int n, uint32_t timeout) { @@ -1274,6 +1362,19 @@ static int vhost_virtqueue_set_busyloop_timeout(struct vhost_dev *dev, return 0; } +static void vhost_virtqueue_error_notifier(EventNotifier *n) +{ + struct vhost_virtqueue *vq = container_of(n, struct vhost_virtqueue, + error_notifier); + struct vhost_dev *dev = vq->dev; + int index = vq - dev->vqs; + + if (event_notifier_test_and_clear(n) && dev->vdev) { + VHOST_OPS_DEBUG(-EINVAL, "vhost vring error in virtqueue %d", + dev->vq_index + index); + } +} + static int vhost_virtqueue_init(struct vhost_dev *dev, struct vhost_virtqueue *vq, int n) { @@ -1295,7 +1396,27 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, vq->dev = dev; + if (dev->vhost_ops->vhost_set_vring_err) { + r = event_notifier_init(&vq->error_notifier, 0); + if (r < 0) { + goto fail_call; + } + + file.fd = event_notifier_get_fd(&vq->error_notifier); + r = dev->vhost_ops->vhost_set_vring_err(dev, &file); + if (r) { + VHOST_OPS_DEBUG(r, "vhost_set_vring_err failed"); + goto fail_err; + } + + event_notifier_set_handler(&vq->error_notifier, + vhost_virtqueue_error_notifier); + } + return 0; + +fail_err: + event_notifier_cleanup(&vq->error_notifier); fail_call: event_notifier_cleanup(&vq->masked_notifier); return r; @@ -1304,12 +1425,17 @@ fail_call: static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq) { event_notifier_cleanup(&vq->masked_notifier); + if (vq->dev->vhost_ops->vhost_set_vring_err) { + event_notifier_set_handler(&vq->error_notifier, NULL); + event_notifier_cleanup(&vq->error_notifier); + } } int vhost_dev_init(struct vhost_dev *hdev, void *opaque, VhostBackendType backend_type, uint32_t busyloop_timeout, Error **errp) { + unsigned int used, reserved, limit; uint64_t features; int i, r, n_initialized_vqs = 0; @@ -1336,6 +1462,19 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, goto fail; } + limit = hdev->vhost_ops->vhost_backend_memslots_limit(hdev); + if (limit < MEMORY_DEVICES_SAFE_MAX_MEMSLOTS && + memory_devices_memslot_auto_decision_active()) { + error_setg(errp, "some memory device (like virtio-mem)" + " decided how many memory slots to use based on the overall" + " number of memory slots; this vhost backend would further" + " restricts the overall number of memory slots"); + error_append_hint(errp, "Try plugging this vhost backend before" + " plugging such memory devices.\n"); + r = -EINVAL; + goto fail; + } + for (i = 0; i < hdev->nvqs; ++i, ++n_initialized_vqs) { r = vhost_virtqueue_init(hdev, hdev->vqs + i, hdev->vq_index + i); if (r < 0) { @@ -1368,9 +1507,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, .log_sync = vhost_log_sync, .log_global_start = vhost_log_global_start, .log_global_stop = vhost_log_global_stop, - .eventfd_add = vhost_eventfd_add, - .eventfd_del = vhost_eventfd_del, - .priority = 10 + .priority = MEMORY_LISTENER_PRIORITY_DEV_BACKEND }; hdev->iommu_listener = (MemoryListener) { @@ -1390,9 +1527,8 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, } if (hdev->migration_blocker != NULL) { - r = migrate_add_blocker(hdev->migration_blocker, errp); + r = migrate_add_blocker_normal(&hdev->migration_blocker, errp); if (r < 0) { - error_free(hdev->migration_blocker); goto fail_busyloop; } } @@ -1407,9 +1543,27 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, memory_listener_register(&hdev->memory_listener, &address_space_memory); QLIST_INSERT_HEAD(&vhost_devices, hdev, entry); - if (used_memslots > hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) { - error_setg(errp, "vhost backend memory slots limit is less" - " than current number of present memory slots"); + /* + * The listener we registered properly updated the corresponding counter. + * So we can trust that these values are accurate. + */ + if (hdev->vhost_ops->vhost_backend_no_private_memslots && + hdev->vhost_ops->vhost_backend_no_private_memslots(hdev)) { + used = used_shared_memslots; + } else { + used = used_memslots; + } + /* + * We assume that all reserved memslots actually require a real memslot + * in our vhost backend. This might not be true, for example, if the + * memslot would be ROM. If ever relevant, we can optimize for that -- + * but we'll need additional information about the reservations. + */ + reserved = memory_devices_get_reserved_memslots(); + if (used + reserved > limit) { + error_setg(errp, "vhost backend memory slots limit (%d) is less" + " than current number of used (%d) and reserved (%d)" + " memory slots for memory devices.", limit, used, reserved); r = -EINVAL; goto fail_busyloop; } @@ -1432,6 +1586,8 @@ void vhost_dev_cleanup(struct vhost_dev *hdev) { int i; + trace_vhost_dev_cleanup(hdev); + for (i = 0; i < hdev->nvqs; ++i) { vhost_virtqueue_cleanup(hdev->vqs + i); } @@ -1440,10 +1596,7 @@ void vhost_dev_cleanup(struct vhost_dev *hdev) memory_listener_unregister(&hdev->memory_listener); QLIST_REMOVE(hdev, entry); } - if (hdev->migration_blocker) { - migrate_del_blocker(hdev->migration_blocker); - error_free(hdev->migration_blocker); - } + migrate_del_blocker(&hdev->migration_blocker); g_free(hdev->mem); g_free(hdev->mem_sections); if (hdev->vhost_ops) { @@ -1454,13 +1607,47 @@ void vhost_dev_cleanup(struct vhost_dev *hdev) memset(hdev, 0, sizeof(struct vhost_dev)); } +static void vhost_dev_disable_notifiers_nvqs(struct vhost_dev *hdev, + VirtIODevice *vdev, + unsigned int nvqs) +{ + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + int i, r; + + /* + * Batch all the host notifiers in a single transaction to avoid + * quadratic time complexity in address_space_update_ioeventfds(). + */ + memory_region_transaction_begin(); + + for (i = 0; i < nvqs; ++i) { + r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i, + false); + if (r < 0) { + error_report("vhost VQ %d notifier cleanup failed: %d", i, -r); + } + assert(r >= 0); + } + + /* + * The transaction expects the ioeventfds to be open when it + * commits. Do it now, before the cleanup loop. + */ + memory_region_transaction_commit(); + + for (i = 0; i < nvqs; ++i) { + virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i); + } + virtio_device_release_ioeventfd(vdev); +} + /* Stop processing guest IO notifications in qemu. * Start processing them in vhost in kernel. */ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - int i, r, e; + int i, r; /* We will pass the notifiers to the kernel, make sure that QEMU * doesn't interfere. @@ -1468,32 +1655,29 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) r = virtio_device_grab_ioeventfd(vdev); if (r < 0) { error_report("binding does not support host notifiers"); - goto fail; + return r; } + /* + * Batch all the host notifiers in a single transaction to avoid + * quadratic time complexity in address_space_update_ioeventfds(). + */ + memory_region_transaction_begin(); + for (i = 0; i < hdev->nvqs; ++i) { r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i, true); if (r < 0) { error_report("vhost VQ %d notifier binding failed: %d", i, -r); - goto fail_vq; + memory_region_transaction_commit(); + vhost_dev_disable_notifiers_nvqs(hdev, vdev, i); + return r; } } + memory_region_transaction_commit(); + return 0; -fail_vq: - while (--i >= 0) { - e = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i, - false); - if (e < 0) { - error_report("vhost VQ %d notifier cleanup error: %d", i, -r); - } - assert (e >= 0); - virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i); - } - virtio_device_release_ioeventfd(vdev); -fail: - return r; } /* Stop processing guest IO notifications in vhost. @@ -1503,19 +1687,7 @@ fail: */ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) { - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - int i, r; - - for (i = 0; i < hdev->nvqs; ++i) { - r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i, - false); - if (r < 0) { - error_report("vhost VQ %d notifier cleanup failed: %d", i, -r); - } - assert (r >= 0); - virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i); - } - virtio_device_release_ioeventfd(vdev); + vhost_dev_disable_notifiers_nvqs(hdev, vdev, hdev->nvqs); } /* Test and clear event pending status. @@ -1549,7 +1721,68 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, file.index = hdev->vhost_ops->vhost_get_vq_index(hdev, n); r = hdev->vhost_ops->vhost_set_vring_call(hdev, &file); if (r < 0) { - VHOST_OPS_DEBUG(r, "vhost_set_vring_call failed"); + error_report("vhost_set_vring_call failed %d", -r); + } +} + +bool vhost_config_pending(struct vhost_dev *hdev) +{ + assert(hdev->vhost_ops); + if ((hdev->started == false) || + (hdev->vhost_ops->vhost_set_config_call == NULL)) { + return false; + } + + EventNotifier *notifier = + &hdev->vqs[VHOST_QUEUE_NUM_CONFIG_INR].masked_config_notifier; + return event_notifier_test_and_clear(notifier); +} + +void vhost_config_mask(struct vhost_dev *hdev, VirtIODevice *vdev, bool mask) +{ + int fd; + int r; + EventNotifier *notifier = + &hdev->vqs[VHOST_QUEUE_NUM_CONFIG_INR].masked_config_notifier; + EventNotifier *config_notifier = &vdev->config_notifier; + assert(hdev->vhost_ops); + + if ((hdev->started == false) || + (hdev->vhost_ops->vhost_set_config_call == NULL)) { + return; + } + if (mask) { + assert(vdev->use_guest_notifier_mask); + fd = event_notifier_get_fd(notifier); + } else { + fd = event_notifier_get_fd(config_notifier); + } + r = hdev->vhost_ops->vhost_set_config_call(hdev, fd); + if (r < 0) { + error_report("vhost_set_config_call failed %d", -r); + } +} + +static void vhost_stop_config_intr(struct vhost_dev *dev) +{ + int fd = -1; + assert(dev->vhost_ops); + if (dev->vhost_ops->vhost_set_config_call) { + dev->vhost_ops->vhost_set_config_call(dev, fd); + } +} + +static void vhost_start_config_intr(struct vhost_dev *dev) +{ + int r; + + assert(dev->vhost_ops); + int fd = event_notifier_get_fd(&dev->vdev->config_notifier); + if (dev->vhost_ops->vhost_set_config_call) { + r = dev->vhost_ops->vhost_set_config_call(dev, fd); + if (!r) { + event_notifier_set(&dev->vdev->config_notifier); + } } } @@ -1730,14 +1963,43 @@ int vhost_dev_get_inflight(struct vhost_dev *dev, uint16_t queue_size, return 0; } -/* Host notifiers must be enabled at this point. */ -int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) +static int vhost_dev_set_vring_enable(struct vhost_dev *hdev, int enable) +{ + if (!hdev->vhost_ops->vhost_set_vring_enable) { + return 0; + } + + /* + * For vhost-user devices, if VHOST_USER_F_PROTOCOL_FEATURES has not + * been negotiated, the rings start directly in the enabled state, and + * .vhost_set_vring_enable callback will fail since + * VHOST_USER_SET_VRING_ENABLE is not supported. + */ + if (hdev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER && + !virtio_has_feature(hdev->backend_features, + VHOST_USER_F_PROTOCOL_FEATURES)) { + return 0; + } + + return hdev->vhost_ops->vhost_set_vring_enable(hdev, enable); +} + +/* + * Host notifiers must be enabled at this point. + * + * If @vrings is true, this function will enable all vrings before starting the + * device. If it is false, the vring initialization is left to be done by the + * caller. + */ +int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) { int i, r; /* should only be called after backend is connected */ assert(hdev->vhost_ops); + trace_vhost_dev_start(hdev, vdev->name, vrings); + vdev->vhost_started = true; hdev->started = true; hdev->vdev = vdev; @@ -1766,6 +2028,17 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) } } + r = event_notifier_init( + &hdev->vqs[VHOST_QUEUE_NUM_CONFIG_INR].masked_config_notifier, 0); + if (r < 0) { + VHOST_OPS_DEBUG(r, "event_notifier_init failed"); + goto fail_vq; + } + event_notifier_test_and_clear( + &hdev->vqs[VHOST_QUEUE_NUM_CONFIG_INR].masked_config_notifier); + if (!vdev->use_guest_notifier_mask) { + vhost_config_mask(hdev, vdev, true); + } if (hdev->log_enabled) { uint64_t log_base; @@ -1781,10 +2054,16 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) goto fail_log; } } + if (vrings) { + r = vhost_dev_set_vring_enable(hdev, true); + if (r) { + goto fail_log; + } + } if (hdev->vhost_ops->vhost_dev_start) { r = hdev->vhost_ops->vhost_dev_start(hdev, true); if (r) { - goto fail_log; + goto fail_start; } } if (vhost_dev_has_iommu(hdev) && @@ -1798,7 +2077,12 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) vhost_device_iotlb_miss(hdev, vq->used_phys, true); } } + vhost_start_config_intr(hdev); return 0; +fail_start: + if (vrings) { + vhost_dev_set_vring_enable(hdev, false); + } fail_log: vhost_log_put(hdev, false); fail_vq: @@ -1810,6 +2094,9 @@ fail_vq: } fail_mem: + if (vhost_dev_has_iommu(hdev)) { + memory_listener_unregister(&hdev->iommu_listener); + } fail_features: vdev->vhost_started = false; hdev->started = false; @@ -1817,22 +2104,35 @@ fail_features: } /* Host notifiers must be enabled at this point. */ -void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) +void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) { int i; /* should only be called after backend is connected */ assert(hdev->vhost_ops); + event_notifier_test_and_clear( + &hdev->vqs[VHOST_QUEUE_NUM_CONFIG_INR].masked_config_notifier); + event_notifier_test_and_clear(&vdev->config_notifier); + event_notifier_cleanup( + &hdev->vqs[VHOST_QUEUE_NUM_CONFIG_INR].masked_config_notifier); + + trace_vhost_dev_stop(hdev, vdev->name, vrings); if (hdev->vhost_ops->vhost_dev_start) { hdev->vhost_ops->vhost_dev_start(hdev, false); } + if (vrings) { + vhost_dev_set_vring_enable(hdev, false); + } for (i = 0; i < hdev->nvqs; ++i) { vhost_virtqueue_stop(hdev, vdev, hdev->vqs + i, hdev->vq_index + i); } + if (hdev->vhost_ops->vhost_reset_status) { + hdev->vhost_ops->vhost_reset_status(hdev); + } if (vhost_dev_has_iommu(hdev)) { if (hdev->vhost_ops->vhost_set_iotlb_callback) { @@ -1840,6 +2140,7 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) } memory_listener_unregister(&hdev->iommu_listener); } + vhost_stop_config_intr(hdev); vhost_log_put(hdev, true); hdev->started = false; vdev->vhost_started = false; @@ -1855,3 +2156,255 @@ int vhost_net_set_backend(struct vhost_dev *hdev, return -ENOSYS; } + +int vhost_reset_device(struct vhost_dev *hdev) +{ + if (hdev->vhost_ops->vhost_reset_device) { + return hdev->vhost_ops->vhost_reset_device(hdev); + } + + return -ENOSYS; +} + +bool vhost_supports_device_state(struct vhost_dev *dev) +{ + if (dev->vhost_ops->vhost_supports_device_state) { + return dev->vhost_ops->vhost_supports_device_state(dev); + } + + return false; +} + +int vhost_set_device_state_fd(struct vhost_dev *dev, + VhostDeviceStateDirection direction, + VhostDeviceStatePhase phase, + int fd, + int *reply_fd, + Error **errp) +{ + if (dev->vhost_ops->vhost_set_device_state_fd) { + return dev->vhost_ops->vhost_set_device_state_fd(dev, direction, phase, + fd, reply_fd, errp); + } + + error_setg(errp, + "vhost transport does not support migration state transfer"); + return -ENOSYS; +} + +int vhost_check_device_state(struct vhost_dev *dev, Error **errp) +{ + if (dev->vhost_ops->vhost_check_device_state) { + return dev->vhost_ops->vhost_check_device_state(dev, errp); + } + + error_setg(errp, + "vhost transport does not support migration state transfer"); + return -ENOSYS; +} + +int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp) +{ + ERRP_GUARD(); + /* Maximum chunk size in which to transfer the state */ + const size_t chunk_size = 1 * 1024 * 1024; + g_autofree void *transfer_buf = NULL; + g_autoptr(GError) g_err = NULL; + int pipe_fds[2], read_fd = -1, write_fd = -1, reply_fd = -1; + int ret; + + /* [0] for reading (our end), [1] for writing (back-end's end) */ + if (!g_unix_open_pipe(pipe_fds, FD_CLOEXEC, &g_err)) { + error_setg(errp, "Failed to set up state transfer pipe: %s", + g_err->message); + ret = -EINVAL; + goto fail; + } + + read_fd = pipe_fds[0]; + write_fd = pipe_fds[1]; + + /* + * VHOST_TRANSFER_STATE_PHASE_STOPPED means the device must be stopped. + * Ideally, it is suspended, but SUSPEND/RESUME currently do not exist for + * vhost-user, so just check that it is stopped at all. + */ + assert(!dev->started); + + /* Transfer ownership of write_fd to the back-end */ + ret = vhost_set_device_state_fd(dev, + VHOST_TRANSFER_STATE_DIRECTION_SAVE, + VHOST_TRANSFER_STATE_PHASE_STOPPED, + write_fd, + &reply_fd, + errp); + if (ret < 0) { + error_prepend(errp, "Failed to initiate state transfer: "); + goto fail; + } + + /* If the back-end wishes to use a different pipe, switch over */ + if (reply_fd >= 0) { + close(read_fd); + read_fd = reply_fd; + } + + transfer_buf = g_malloc(chunk_size); + + while (true) { + ssize_t read_ret; + + read_ret = RETRY_ON_EINTR(read(read_fd, transfer_buf, chunk_size)); + if (read_ret < 0) { + ret = -errno; + error_setg_errno(errp, -ret, "Failed to receive state"); + goto fail; + } + + assert(read_ret <= chunk_size); + qemu_put_be32(f, read_ret); + + if (read_ret == 0) { + /* EOF */ + break; + } + + qemu_put_buffer(f, transfer_buf, read_ret); + } + + /* + * Back-end will not really care, but be clean and close our end of the pipe + * before inquiring the back-end about whether transfer was successful + */ + close(read_fd); + read_fd = -1; + + /* Also, verify that the device is still stopped */ + assert(!dev->started); + + ret = vhost_check_device_state(dev, errp); + if (ret < 0) { + goto fail; + } + + ret = 0; +fail: + if (read_fd >= 0) { + close(read_fd); + } + + return ret; +} + +int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp) +{ + ERRP_GUARD(); + size_t transfer_buf_size = 0; + g_autofree void *transfer_buf = NULL; + g_autoptr(GError) g_err = NULL; + int pipe_fds[2], read_fd = -1, write_fd = -1, reply_fd = -1; + int ret; + + /* [0] for reading (back-end's end), [1] for writing (our end) */ + if (!g_unix_open_pipe(pipe_fds, FD_CLOEXEC, &g_err)) { + error_setg(errp, "Failed to set up state transfer pipe: %s", + g_err->message); + ret = -EINVAL; + goto fail; + } + + read_fd = pipe_fds[0]; + write_fd = pipe_fds[1]; + + /* + * VHOST_TRANSFER_STATE_PHASE_STOPPED means the device must be stopped. + * Ideally, it is suspended, but SUSPEND/RESUME currently do not exist for + * vhost-user, so just check that it is stopped at all. + */ + assert(!dev->started); + + /* Transfer ownership of read_fd to the back-end */ + ret = vhost_set_device_state_fd(dev, + VHOST_TRANSFER_STATE_DIRECTION_LOAD, + VHOST_TRANSFER_STATE_PHASE_STOPPED, + read_fd, + &reply_fd, + errp); + if (ret < 0) { + error_prepend(errp, "Failed to initiate state transfer: "); + goto fail; + } + + /* If the back-end wishes to use a different pipe, switch over */ + if (reply_fd >= 0) { + close(write_fd); + write_fd = reply_fd; + } + + while (true) { + size_t this_chunk_size = qemu_get_be32(f); + ssize_t write_ret; + const uint8_t *transfer_pointer; + + if (this_chunk_size == 0) { + /* End of state */ + break; + } + + if (transfer_buf_size < this_chunk_size) { + transfer_buf = g_realloc(transfer_buf, this_chunk_size); + transfer_buf_size = this_chunk_size; + } + + if (qemu_get_buffer(f, transfer_buf, this_chunk_size) < + this_chunk_size) + { + error_setg(errp, "Failed to read state"); + ret = -EINVAL; + goto fail; + } + + transfer_pointer = transfer_buf; + while (this_chunk_size > 0) { + write_ret = RETRY_ON_EINTR( + write(write_fd, transfer_pointer, this_chunk_size) + ); + if (write_ret < 0) { + ret = -errno; + error_setg_errno(errp, -ret, "Failed to send state"); + goto fail; + } else if (write_ret == 0) { + error_setg(errp, "Failed to send state: Connection is closed"); + ret = -ECONNRESET; + goto fail; + } + + assert(write_ret <= this_chunk_size); + this_chunk_size -= write_ret; + transfer_pointer += write_ret; + } + } + + /* + * Close our end, thus ending transfer, before inquiring the back-end about + * whether transfer was successful + */ + close(write_fd); + write_fd = -1; + + /* Also, verify that the device is still stopped */ + assert(!dev->started); + + ret = vhost_check_device_state(dev, errp); + if (ret < 0) { + goto fail; + } + + ret = 0; +fail: + if (write_fd >= 0) { + close(write_fd); + } + + return ret; +} diff --git a/hw/virtio/virtio-acpi.c b/hw/virtio/virtio-acpi.c new file mode 100644 index 0000000000..230a669500 --- /dev/null +++ b/hw/virtio/virtio-acpi.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * virtio ACPI Support + * + */ + +#include "qemu/osdep.h" +#include "hw/virtio/virtio-acpi.h" +#include "hw/acpi/aml-build.h" + +void virtio_acpi_dsdt_add(Aml *scope, const hwaddr base, const hwaddr size, + uint32_t mmio_irq, long int start_index, int num) +{ + hwaddr virtio_base = base; + uint32_t irq = mmio_irq; + long int i; + + for (i = start_index; i < start_index + num; i++) { + Aml *dev = aml_device("VR%02u", (unsigned)i); + aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005"))); + aml_append(dev, aml_name_decl("_UID", aml_int(i))); + aml_append(dev, aml_name_decl("_CCA", aml_int(1))); + + Aml *crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(virtio_base, size, AML_READ_WRITE)); + aml_append(crs, + aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, + AML_EXCLUSIVE, &irq, 1)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); + virtio_base += size; + irq++; + } +} diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 73ac5eb675..609e39a821 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -31,7 +31,6 @@ #include "trace.h" #include "qemu/error-report.h" #include "migration/misc.h" -#include "migration/migration.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" @@ -241,36 +240,34 @@ static void balloon_stats_poll_cb(void *opaque) static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Error *err = NULL; VirtIOBalloon *s = VIRTIO_BALLOON(obj); + bool ok = false; int i; - if (!visit_start_struct(v, name, NULL, 0, &err)) { - goto out; + if (!visit_start_struct(v, name, NULL, 0, errp)) { + return; } - if (!visit_type_int(v, "last-update", &s->stats_last_update, &err)) { + if (!visit_type_int(v, "last-update", &s->stats_last_update, errp)) { goto out_end; } - if (!visit_start_struct(v, "stats", NULL, 0, &err)) { + if (!visit_start_struct(v, "stats", NULL, 0, errp)) { goto out_end; } for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { - if (!visit_type_uint64(v, balloon_stat_names[i], &s->stats[i], &err)) { + if (!visit_type_uint64(v, balloon_stat_names[i], &s->stats[i], errp)) { goto out_nested; } } - visit_check_struct(v, &err); + ok = visit_check_struct(v, errp); out_nested: visit_end_struct(v, NULL); - if (!err) { - visit_check_struct(v, &err); + if (ok) { + visit_check_struct(v, errp); } out_end: visit_end_struct(v, NULL); -out: - error_propagate(errp, err); } static void balloon_stats_get_poll_interval(Object *obj, Visitor *v, @@ -634,7 +631,8 @@ static void virtio_balloon_free_page_done(VirtIOBalloon *s) } static int -virtio_balloon_free_page_hint_notify(NotifierWithReturn *n, void *data) +virtio_balloon_free_page_hint_notify(NotifierWithReturn *n, void *data, + Error **errp) { VirtIOBalloon *dev = container_of(n, VirtIOBalloon, free_page_hint_notify); VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -731,37 +729,14 @@ static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) memcpy(config_data, &config, virtio_balloon_config_size(dev)); } -static int build_dimm_list(Object *obj, void *opaque) -{ - GSList **list = opaque; - - if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { - DeviceState *dev = DEVICE(obj); - if (dev->realized) { /* only realized DIMMs matter */ - *list = g_slist_prepend(*list, dev); - } - } - - object_child_foreach(obj, build_dimm_list, opaque); - return 0; -} - static ram_addr_t get_current_ram_size(void) { - GSList *list = NULL, *item; - ram_addr_t size = current_machine->ram_size; - - build_dimm_list(qdev_get_machine(), &list); - for (item = list; item; item = g_slist_next(item)) { - Object *obj = OBJECT(item->data); - if (!strcmp(object_get_typename(obj), TYPE_PC_DIMM)) { - size += object_property_get_int(obj, PC_DIMM_SIZE_PROP, - &error_abort); - } + MachineState *machine = MACHINE(qdev_get_machine()); + if (machine->device_memory) { + return machine->ram_size + machine->device_memory->dimm_size; + } else { + return machine->ram_size; } - g_slist_free(list); - - return size; } static bool virtio_balloon_page_poison_support(void *opaque) @@ -841,7 +816,7 @@ static const VMStateDescription vmstate_virtio_balloon_free_page_hint = { .version_id = 1, .minimum_version_id = 1, .needed = virtio_balloon_free_page_support, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(free_page_hint_cmd_id, VirtIOBalloon), VMSTATE_UINT32(free_page_hint_status, VirtIOBalloon), VMSTATE_END_OF_LIST() @@ -853,7 +828,7 @@ static const VMStateDescription vmstate_virtio_balloon_page_poison = { .version_id = 1, .minimum_version_id = 1, .needed = virtio_balloon_page_poison_support, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(poison_val, VirtIOBalloon), VMSTATE_END_OF_LIST() } @@ -864,12 +839,12 @@ static const VMStateDescription vmstate_virtio_balloon_device = { .version_id = 1, .minimum_version_id = 1, .post_load = virtio_balloon_post_load_device, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(num_pages, VirtIOBalloon), VMSTATE_UINT32(actual, VirtIOBalloon), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_virtio_balloon_free_page_hint, &vmstate_virtio_balloon_page_poison, NULL @@ -910,8 +885,9 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp) precopy_add_notifier(&s->free_page_hint_notify); object_ref(OBJECT(s->iothread)); - s->free_page_bh = aio_bh_new(iothread_get_aio_context(s->iothread), - virtio_ballloon_get_free_page_hints, s); + s->free_page_bh = aio_bh_new_guarded(iothread_get_aio_context(s->iothread), + virtio_ballloon_get_free_page_hints, s, + &dev->mem_reentrancy_guard); } if (virtio_has_feature(s->host_features, VIRTIO_BALLOON_F_REPORTING)) { @@ -1019,7 +995,7 @@ static const VMStateDescription vmstate_virtio_balloon = { .name = "virtio-balloon", .minimum_version_id = 1, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index d7ec023adf..896feb37a1 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -104,6 +104,7 @@ void virtio_bus_reset(VirtioBusState *bus) VirtIODevice *vdev = virtio_bus_get_device(bus); DPRINTF("%s: reset device.\n", BUS(bus)->name); + virtio_bus_stop_ioeventfd(bus); if (vdev != NULL) { virtio_reset(vdev); } diff --git a/hw/virtio/virtio-config-io.c b/hw/virtio/virtio-config-io.c new file mode 100644 index 0000000000..ad78e0b9bc --- /dev/null +++ b/hw/virtio/virtio-config-io.c @@ -0,0 +1,204 @@ +/* + * Virtio Support + * + * Copyright IBM, Corp. 2007 + * + * Authors: + * Anthony Liguori + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/virtio/virtio.h" +#include "cpu.h" + +uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr) +{ + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + uint8_t val; + + if (addr + sizeof(val) > vdev->config_len) { + return (uint32_t)-1; + } + + k->get_config(vdev, vdev->config); + + val = ldub_p(vdev->config + addr); + return val; +} + +uint32_t virtio_config_readw(VirtIODevice *vdev, uint32_t addr) +{ + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + uint16_t val; + + if (addr + sizeof(val) > vdev->config_len) { + return (uint32_t)-1; + } + + k->get_config(vdev, vdev->config); + + val = lduw_p(vdev->config + addr); + return val; +} + +uint32_t virtio_config_readl(VirtIODevice *vdev, uint32_t addr) +{ + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + uint32_t val; + + if (addr + sizeof(val) > vdev->config_len) { + return (uint32_t)-1; + } + + k->get_config(vdev, vdev->config); + + val = ldl_p(vdev->config + addr); + return val; +} + +void virtio_config_writeb(VirtIODevice *vdev, uint32_t addr, uint32_t data) +{ + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + uint8_t val = data; + + if (addr + sizeof(val) > vdev->config_len) { + return; + } + + stb_p(vdev->config + addr, val); + + if (k->set_config) { + k->set_config(vdev, vdev->config); + } +} + +void virtio_config_writew(VirtIODevice *vdev, uint32_t addr, uint32_t data) +{ + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + uint16_t val = data; + + if (addr + sizeof(val) > vdev->config_len) { + return; + } + + stw_p(vdev->config + addr, val); + + if (k->set_config) { + k->set_config(vdev, vdev->config); + } +} + +void virtio_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data) +{ + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + uint32_t val = data; + + if (addr + sizeof(val) > vdev->config_len) { + return; + } + + stl_p(vdev->config + addr, val); + + if (k->set_config) { + k->set_config(vdev, vdev->config); + } +} + +uint32_t virtio_config_modern_readb(VirtIODevice *vdev, uint32_t addr) +{ + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + uint8_t val; + + if (addr + sizeof(val) > vdev->config_len) { + return (uint32_t)-1; + } + + k->get_config(vdev, vdev->config); + + val = ldub_p(vdev->config + addr); + return val; +} + +uint32_t virtio_config_modern_readw(VirtIODevice *vdev, uint32_t addr) +{ + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + uint16_t val; + + if (addr + sizeof(val) > vdev->config_len) { + return (uint32_t)-1; + } + + k->get_config(vdev, vdev->config); + + val = lduw_le_p(vdev->config + addr); + return val; +} + +uint32_t virtio_config_modern_readl(VirtIODevice *vdev, uint32_t addr) +{ + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + uint32_t val; + + if (addr + sizeof(val) > vdev->config_len) { + return (uint32_t)-1; + } + + k->get_config(vdev, vdev->config); + + val = ldl_le_p(vdev->config + addr); + return val; +} + +void virtio_config_modern_writeb(VirtIODevice *vdev, + uint32_t addr, uint32_t data) +{ + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + uint8_t val = data; + + if (addr + sizeof(val) > vdev->config_len) { + return; + } + + stb_p(vdev->config + addr, val); + + if (k->set_config) { + k->set_config(vdev, vdev->config); + } +} + +void virtio_config_modern_writew(VirtIODevice *vdev, + uint32_t addr, uint32_t data) +{ + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + uint16_t val = data; + + if (addr + sizeof(val) > vdev->config_len) { + return; + } + + stw_le_p(vdev->config + addr, val); + + if (k->set_config) { + k->set_config(vdev, vdev->config); + } +} + +void virtio_config_modern_writel(VirtIODevice *vdev, + uint32_t addr, uint32_t data) +{ + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + uint32_t val = data; + + if (addr + sizeof(val) > vdev->config_len) { + return; + } + + stl_le_p(vdev->config + addr, val); + + if (k->set_config) { + k->set_config(vdev, vdev->config); + } +} + diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index c3829e7498..bbe8aa4b99 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -21,12 +21,44 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-crypto.h" #include "hw/qdev-properties.h" -#include "hw/virtio/virtio-access.h" #include "standard-headers/linux/virtio_ids.h" #include "sysemu/cryptodev-vhost.h" #define VIRTIO_CRYPTO_VM_VERSION 1 +typedef struct VirtIOCryptoSessionReq { + VirtIODevice *vdev; + VirtQueue *vq; + VirtQueueElement *elem; + CryptoDevBackendSessionInfo info; + CryptoDevCompletionFunc cb; +} VirtIOCryptoSessionReq; + +static void virtio_crypto_free_create_session_req(VirtIOCryptoSessionReq *sreq) +{ + switch (sreq->info.op_code) { + case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: + g_free(sreq->info.u.sym_sess_info.cipher_key); + g_free(sreq->info.u.sym_sess_info.auth_key); + break; + + case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION: + g_free(sreq->info.u.asym_sess_info.key); + break; + + case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION: + case VIRTIO_CRYPTO_HASH_DESTROY_SESSION: + case VIRTIO_CRYPTO_MAC_DESTROY_SESSION: + case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION: + case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION: + break; + + default: + error_report("Unknown opcode: %u", sreq->info.op_code); + } + g_free(sreq); +} + /* * Transfer virtqueue index to crypto queue index. * The control virtqueue is after the data virtqueues @@ -75,140 +107,232 @@ virtio_crypto_cipher_session_helper(VirtIODevice *vdev, return 0; } -static int64_t +static int virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto, struct virtio_crypto_sym_create_session_req *sess_req, uint32_t queue_id, uint32_t opcode, - struct iovec *iov, unsigned int out_num) + struct iovec *iov, unsigned int out_num, + VirtIOCryptoSessionReq *sreq) { VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); - CryptoDevBackendSymSessionInfo info; - int64_t session_id; + CryptoDevBackendSymSessionInfo *sym_info = &sreq->info.u.sym_sess_info; int queue_index; uint32_t op_type; - Error *local_err = NULL; int ret; - memset(&info, 0, sizeof(info)); op_type = ldl_le_p(&sess_req->op_type); - info.op_type = op_type; - info.op_code = opcode; + sreq->info.op_code = opcode; + + sym_info = &sreq->info.u.sym_sess_info; + sym_info->op_type = op_type; if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) { - ret = virtio_crypto_cipher_session_helper(vdev, &info, + ret = virtio_crypto_cipher_session_helper(vdev, sym_info, &sess_req->u.cipher.para, &iov, &out_num); if (ret < 0) { - goto err; + return ret; } } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { size_t s; /* cipher part */ - ret = virtio_crypto_cipher_session_helper(vdev, &info, + ret = virtio_crypto_cipher_session_helper(vdev, sym_info, &sess_req->u.chain.para.cipher_param, &iov, &out_num); if (ret < 0) { - goto err; + return ret; } /* hash part */ - info.alg_chain_order = ldl_le_p( + sym_info->alg_chain_order = ldl_le_p( &sess_req->u.chain.para.alg_chain_order); - info.add_len = ldl_le_p(&sess_req->u.chain.para.aad_len); - info.hash_mode = ldl_le_p(&sess_req->u.chain.para.hash_mode); - if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) { - info.hash_alg = ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo); - info.auth_key_len = ldl_le_p( + sym_info->add_len = ldl_le_p(&sess_req->u.chain.para.aad_len); + sym_info->hash_mode = ldl_le_p(&sess_req->u.chain.para.hash_mode); + if (sym_info->hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_AUTH) { + sym_info->hash_alg = + ldl_le_p(&sess_req->u.chain.para.u.mac_param.algo); + sym_info->auth_key_len = ldl_le_p( &sess_req->u.chain.para.u.mac_param.auth_key_len); - info.hash_result_len = ldl_le_p( + sym_info->hash_result_len = ldl_le_p( &sess_req->u.chain.para.u.mac_param.hash_result_len); - if (info.auth_key_len > vcrypto->conf.max_auth_key_len) { + if (sym_info->auth_key_len > vcrypto->conf.max_auth_key_len) { error_report("virtio-crypto length of auth key is too big: %u", - info.auth_key_len); - ret = -VIRTIO_CRYPTO_ERR; - goto err; + sym_info->auth_key_len); + return -VIRTIO_CRYPTO_ERR; } /* get auth key */ - if (info.auth_key_len > 0) { - DPRINTF("auth_keylen=%" PRIu32 "\n", info.auth_key_len); - info.auth_key = g_malloc(info.auth_key_len); - s = iov_to_buf(iov, out_num, 0, info.auth_key, - info.auth_key_len); - if (unlikely(s != info.auth_key_len)) { + if (sym_info->auth_key_len > 0) { + sym_info->auth_key = g_malloc(sym_info->auth_key_len); + s = iov_to_buf(iov, out_num, 0, sym_info->auth_key, + sym_info->auth_key_len); + if (unlikely(s != sym_info->auth_key_len)) { virtio_error(vdev, "virtio-crypto authenticated key incorrect"); - ret = -EFAULT; - goto err; + return -EFAULT; } - iov_discard_front(&iov, &out_num, info.auth_key_len); + iov_discard_front(&iov, &out_num, sym_info->auth_key_len); } - } else if (info.hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) { - info.hash_alg = ldl_le_p( + } else if (sym_info->hash_mode == VIRTIO_CRYPTO_SYM_HASH_MODE_PLAIN) { + sym_info->hash_alg = ldl_le_p( &sess_req->u.chain.para.u.hash_param.algo); - info.hash_result_len = ldl_le_p( + sym_info->hash_result_len = ldl_le_p( &sess_req->u.chain.para.u.hash_param.hash_result_len); } else { /* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */ error_report("unsupported hash mode"); - ret = -VIRTIO_CRYPTO_NOTSUPP; - goto err; + return -VIRTIO_CRYPTO_NOTSUPP; } } else { /* VIRTIO_CRYPTO_SYM_OP_NONE */ error_report("unsupported cipher op_type: VIRTIO_CRYPTO_SYM_OP_NONE"); - ret = -VIRTIO_CRYPTO_NOTSUPP; - goto err; + return -VIRTIO_CRYPTO_NOTSUPP; } queue_index = virtio_crypto_vq2q(queue_id); - session_id = cryptodev_backend_sym_create_session( - vcrypto->cryptodev, - &info, queue_index, &local_err); - if (session_id >= 0) { - DPRINTF("create session_id=%" PRIu64 " successfully\n", - session_id); - - ret = session_id; - } else { - if (local_err) { - error_report_err(local_err); - } - ret = -VIRTIO_CRYPTO_ERR; - } - -err: - g_free(info.cipher_key); - g_free(info.auth_key); - return ret; + return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info, + queue_index, sreq->cb, sreq); } -static uint8_t +static int +virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto, + struct virtio_crypto_akcipher_create_session_req *sess_req, + uint32_t queue_id, uint32_t opcode, + struct iovec *iov, unsigned int out_num, + VirtIOCryptoSessionReq *sreq) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); + CryptoDevBackendAsymSessionInfo *asym_info = &sreq->info.u.asym_sess_info; + int queue_index; + uint32_t algo, keytype, keylen; + + algo = ldl_le_p(&sess_req->para.algo); + keytype = ldl_le_p(&sess_req->para.keytype); + keylen = ldl_le_p(&sess_req->para.keylen); + + if ((keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC) + && (keytype != VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE)) { + error_report("unsupported asym keytype: %d", keytype); + return -VIRTIO_CRYPTO_NOTSUPP; + } + + if (keylen) { + asym_info->key = g_malloc(keylen); + if (iov_to_buf(iov, out_num, 0, asym_info->key, keylen) != keylen) { + virtio_error(vdev, "virtio-crypto asym key incorrect"); + return -EFAULT; + } + iov_discard_front(&iov, &out_num, keylen); + } + + sreq->info.op_code = opcode; + asym_info = &sreq->info.u.asym_sess_info; + asym_info->algo = algo; + asym_info->keytype = keytype; + asym_info->keylen = keylen; + switch (asym_info->algo) { + case VIRTIO_CRYPTO_AKCIPHER_RSA: + asym_info->u.rsa.padding_algo = + ldl_le_p(&sess_req->para.u.rsa.padding_algo); + asym_info->u.rsa.hash_algo = + ldl_le_p(&sess_req->para.u.rsa.hash_algo); + break; + + /* TODO DSA&ECDSA handling */ + + default: + return -VIRTIO_CRYPTO_ERR; + } + + queue_index = virtio_crypto_vq2q(queue_id); + return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info, + queue_index, sreq->cb, sreq); +} + +static int virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto, struct virtio_crypto_destroy_session_req *close_sess_req, - uint32_t queue_id) + uint32_t queue_id, + VirtIOCryptoSessionReq *sreq) { - int ret; uint64_t session_id; - uint32_t status; - Error *local_err = NULL; session_id = ldq_le_p(&close_sess_req->session_id); DPRINTF("close session, id=%" PRIu64 "\n", session_id); - ret = cryptodev_backend_sym_close_session( - vcrypto->cryptodev, session_id, queue_id, &local_err); - if (ret == 0) { - status = VIRTIO_CRYPTO_OK; + return cryptodev_backend_close_session( + vcrypto->cryptodev, session_id, queue_id, sreq->cb, sreq); +} + +static void virtio_crypto_create_session_completion(void *opaque, int ret) +{ + VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque; + VirtQueue *vq = sreq->vq; + VirtQueueElement *elem = sreq->elem; + VirtIODevice *vdev = sreq->vdev; + struct virtio_crypto_session_input input; + struct iovec *in_iov = elem->in_sg; + unsigned in_num = elem->in_num; + size_t s; + + memset(&input, 0, sizeof(input)); + /* Serious errors, need to reset virtio crypto device */ + if (ret == -EFAULT) { + virtqueue_detach_element(vq, elem, 0); + goto out; + } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) { + stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP); + } else if (ret == -VIRTIO_CRYPTO_KEY_REJECTED) { + stl_le_p(&input.status, VIRTIO_CRYPTO_KEY_REJECTED); + } else if (ret != VIRTIO_CRYPTO_OK) { + stl_le_p(&input.status, VIRTIO_CRYPTO_ERR); } else { - if (local_err) { - error_report_err(local_err); - } else { - error_report("destroy session failed"); - } - status = VIRTIO_CRYPTO_ERR; + /* Set the session id */ + stq_le_p(&input.session_id, sreq->info.session_id); + stl_le_p(&input.status, VIRTIO_CRYPTO_OK); } - return status; + s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input)); + if (unlikely(s != sizeof(input))) { + virtio_error(vdev, "virtio-crypto input incorrect"); + virtqueue_detach_element(vq, elem, 0); + goto out; + } + virtqueue_push(vq, elem, sizeof(input)); + virtio_notify(vdev, vq); + +out: + g_free(elem); + virtio_crypto_free_create_session_req(sreq); +} + +static void virtio_crypto_destroy_session_completion(void *opaque, int ret) +{ + VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque; + VirtQueue *vq = sreq->vq; + VirtQueueElement *elem = sreq->elem; + VirtIODevice *vdev = sreq->vdev; + struct iovec *in_iov = elem->in_sg; + unsigned in_num = elem->in_num; + uint8_t status; + size_t s; + + if (ret < 0) { + status = VIRTIO_CRYPTO_ERR; + } else { + status = VIRTIO_CRYPTO_OK; + } + s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status)); + if (unlikely(s != sizeof(status))) { + virtio_error(vdev, "virtio-crypto status incorrect"); + virtqueue_detach_element(vq, elem, 0); + goto out; + } + virtqueue_push(vq, elem, sizeof(status)); + virtio_notify(vdev, vq); + +out: + g_free(elem); + g_free(sreq); } static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) @@ -216,16 +340,16 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); struct virtio_crypto_op_ctrl_req ctrl; VirtQueueElement *elem; - struct iovec *in_iov; - struct iovec *out_iov; - unsigned in_num; + VirtIOCryptoSessionReq *sreq; unsigned out_num; + unsigned in_num; uint32_t queue_id; uint32_t opcode; struct virtio_crypto_session_input input; - int64_t session_id; - uint8_t status; size_t s; + int ret; + struct iovec *out_iov; + struct iovec *in_iov; for (;;) { g_autofree struct iovec *out_iov_copy = NULL; @@ -260,72 +384,71 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) opcode = ldl_le_p(&ctrl.header.opcode); queue_id = ldl_le_p(&ctrl.header.queue_id); + sreq = g_new0(VirtIOCryptoSessionReq, 1); + sreq->vdev = vdev; + sreq->vq = vq; + sreq->elem = elem; + switch (opcode) { case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION: - memset(&input, 0, sizeof(input)); - session_id = virtio_crypto_create_sym_session(vcrypto, - &ctrl.u.sym_create_session, - queue_id, opcode, - out_iov, out_num); - /* Serious errors, need to reset virtio crypto device */ - if (session_id == -EFAULT) { - virtqueue_detach_element(vq, elem, 0); - break; - } else if (session_id == -VIRTIO_CRYPTO_NOTSUPP) { - stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP); - } else if (session_id == -VIRTIO_CRYPTO_ERR) { - stl_le_p(&input.status, VIRTIO_CRYPTO_ERR); - } else { - /* Set the session id */ - stq_le_p(&input.session_id, session_id); - stl_le_p(&input.status, VIRTIO_CRYPTO_OK); + sreq->cb = virtio_crypto_create_session_completion; + ret = virtio_crypto_create_sym_session(vcrypto, + &ctrl.u.sym_create_session, + queue_id, opcode, + out_iov, out_num, + sreq); + if (ret < 0) { + virtio_crypto_create_session_completion(sreq, ret); } - - s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input)); - if (unlikely(s != sizeof(input))) { - virtio_error(vdev, "virtio-crypto input incorrect"); - virtqueue_detach_element(vq, elem, 0); - break; - } - virtqueue_push(vq, elem, sizeof(input)); - virtio_notify(vdev, vq); break; + + case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION: + sreq->cb = virtio_crypto_create_session_completion; + ret = virtio_crypto_create_asym_session(vcrypto, + &ctrl.u.akcipher_create_session, + queue_id, opcode, + out_iov, out_num, + sreq); + if (ret < 0) { + virtio_crypto_create_session_completion(sreq, ret); + } + break; + case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION: case VIRTIO_CRYPTO_HASH_DESTROY_SESSION: case VIRTIO_CRYPTO_MAC_DESTROY_SESSION: case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION: - status = virtio_crypto_handle_close_session(vcrypto, - &ctrl.u.destroy_session, queue_id); - /* The status only occupy one byte, we can directly use it */ - s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status)); - if (unlikely(s != sizeof(status))) { - virtio_error(vdev, "virtio-crypto status incorrect"); - virtqueue_detach_element(vq, elem, 0); - break; + case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION: + sreq->cb = virtio_crypto_destroy_session_completion; + ret = virtio_crypto_handle_close_session(vcrypto, + &ctrl.u.destroy_session, queue_id, + sreq); + if (ret < 0) { + virtio_crypto_destroy_session_completion(sreq, ret); } - virtqueue_push(vq, elem, sizeof(status)); - virtio_notify(vdev, vq); break; + case VIRTIO_CRYPTO_HASH_CREATE_SESSION: case VIRTIO_CRYPTO_MAC_CREATE_SESSION: case VIRTIO_CRYPTO_AEAD_CREATE_SESSION: default: - error_report("virtio-crypto unsupported ctrl opcode: %d", opcode); memset(&input, 0, sizeof(input)); + error_report("virtio-crypto unsupported ctrl opcode: %d", opcode); stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP); s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input)); if (unlikely(s != sizeof(input))) { virtio_error(vdev, "virtio-crypto input incorrect"); virtqueue_detach_element(vq, elem, 0); - break; + } else { + virtqueue_push(vq, elem, sizeof(input)); + virtio_notify(vdev, vq); } - virtqueue_push(vq, elem, sizeof(input)); - virtio_notify(vdev, vq); + g_free(sreq); + g_free(elem); break; } /* end switch case */ - g_free(elem); } /* end for loop */ } @@ -338,17 +461,21 @@ static void virtio_crypto_init_request(VirtIOCrypto *vcrypto, VirtQueue *vq, req->in_iov = NULL; req->in_num = 0; req->in_len = 0; - req->flags = CRYPTODEV_BACKEND_ALG__MAX; - req->u.sym_op_info = NULL; + req->flags = QCRYPTODEV_BACKEND_ALG__MAX; + memset(&req->op_info, 0x00, sizeof(req->op_info)); } static void virtio_crypto_free_request(VirtIOCryptoReq *req) { - if (req) { - if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) { - size_t max_len; - CryptoDevBackendSymOpInfo *op_info = req->u.sym_op_info; + if (!req) { + return; + } + if (req->flags == QCRYPTODEV_BACKEND_ALG_SYM) { + size_t max_len; + CryptoDevBackendSymOpInfo *op_info = req->op_info.u.sym_op_info; + + if (op_info) { max_len = op_info->iv_len + op_info->aad_len + op_info->src_len + @@ -359,8 +486,18 @@ static void virtio_crypto_free_request(VirtIOCryptoReq *req) memset(op_info, 0, sizeof(*op_info) + max_len); g_free(op_info); } - g_free(req); + } else if (req->flags == QCRYPTODEV_BACKEND_ALG_ASYM) { + CryptoDevBackendAsymOpInfo *op_info = req->op_info.u.asym_op_info; + if (op_info) { + g_free(op_info->src); + g_free(op_info->dst); + memset(op_info, 0, sizeof(*op_info)); + g_free(op_info); + } } + + g_free(req->in_iov); + g_free(req); } static void @@ -370,6 +507,7 @@ virtio_crypto_sym_input_data_helper(VirtIODevice *vdev, CryptoDevBackendSymOpInfo *sym_op_info) { size_t s, len; + struct iovec *in_iov = req->in_iov; if (status != VIRTIO_CRYPTO_OK) { return; @@ -377,18 +515,18 @@ virtio_crypto_sym_input_data_helper(VirtIODevice *vdev, len = sym_op_info->src_len; /* Save the cipher result */ - s = iov_from_buf(req->in_iov, req->in_num, 0, sym_op_info->dst, len); + s = iov_from_buf(in_iov, req->in_num, 0, sym_op_info->dst, len); if (s != len) { virtio_error(vdev, "virtio-crypto dest data incorrect"); return; } - iov_discard_front(&req->in_iov, &req->in_num, len); + iov_discard_front(&in_iov, &req->in_num, len); if (sym_op_info->op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { /* Save the digest result */ - s = iov_from_buf(req->in_iov, req->in_num, 0, + s = iov_from_buf(in_iov, req->in_num, 0, sym_op_info->digest_result, sym_op_info->digest_result_len); if (s != sym_op_info->digest_result_len) { @@ -397,18 +535,53 @@ virtio_crypto_sym_input_data_helper(VirtIODevice *vdev, } } -static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status) +static void +virtio_crypto_akcipher_input_data_helper(VirtIODevice *vdev, + VirtIOCryptoReq *req, int32_t status, + CryptoDevBackendAsymOpInfo *asym_op_info) { + size_t s, len; + struct iovec *in_iov = req->in_iov; + + if (status != VIRTIO_CRYPTO_OK) { + return; + } + + len = asym_op_info->dst_len; + if (!len) { + return; + } + + s = iov_from_buf(in_iov, req->in_num, 0, asym_op_info->dst, len); + if (s != len) { + virtio_error(vdev, "virtio-crypto asym dest data incorrect"); + return; + } + + iov_discard_front(&in_iov, &req->in_num, len); + + /* For akcipher, dst_len may be changed after operation */ + req->in_len = sizeof(struct virtio_crypto_inhdr) + asym_op_info->dst_len; +} + +static void virtio_crypto_req_complete(void *opaque, int ret) +{ + VirtIOCryptoReq *req = (VirtIOCryptoReq *)opaque; VirtIOCrypto *vcrypto = req->vcrypto; VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); + uint8_t status = -ret; - if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) { + if (req->flags == QCRYPTODEV_BACKEND_ALG_SYM) { virtio_crypto_sym_input_data_helper(vdev, req, status, - req->u.sym_op_info); + req->op_info.u.sym_op_info); + } else if (req->flags == QCRYPTODEV_BACKEND_ALG_ASYM) { + virtio_crypto_akcipher_input_data_helper(vdev, req, status, + req->op_info.u.asym_op_info); } stb_p(&req->in->status, status); virtqueue_push(req->vq, &req->elem, req->in_len); virtio_notify(vdev, req->vq); + virtio_crypto_free_request(req); } static VirtIOCryptoReq * @@ -461,6 +634,11 @@ virtio_crypto_sym_op_helper(VirtIODevice *vdev, return NULL; } + if (unlikely(src_len != dst_len)) { + virtio_error(vdev, "sym request src len is different from dst len"); + return NULL; + } + max_len = (uint64_t)iv_len + aad_len + src_len + dst_len + hash_result_len; if (unlikely(max_len > vcrypto->conf.max_size)) { virtio_error(vdev, "virtio-crypto too big length"); @@ -477,7 +655,7 @@ virtio_crypto_sym_op_helper(VirtIODevice *vdev, op_info->len_to_hash = len_to_hash; op_info->cipher_start_src_offset = cipher_start_src_offset; op_info->len_to_cipher = len_to_cipher; - /* Handle the initilization vector */ + /* Handle the initialization vector */ if (op_info->iv_len > 0) { DPRINTF("iv_len=%" PRIu32 "\n", op_info->iv_len); op_info->iv = op_info->data + curr_size; @@ -543,41 +721,100 @@ err: static int virtio_crypto_handle_sym_req(VirtIOCrypto *vcrypto, struct virtio_crypto_sym_data_req *req, - CryptoDevBackendSymOpInfo **sym_op_info, + CryptoDevBackendOpInfo *op_info, struct iovec *iov, unsigned int out_num) { VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); + CryptoDevBackendSymOpInfo *sym_op_info; uint32_t op_type; - CryptoDevBackendSymOpInfo *op_info; op_type = ldl_le_p(&req->op_type); - if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) { - op_info = virtio_crypto_sym_op_helper(vdev, &req->u.cipher.para, + sym_op_info = virtio_crypto_sym_op_helper(vdev, &req->u.cipher.para, NULL, iov, out_num); - if (!op_info) { + if (!sym_op_info) { return -EFAULT; } - op_info->op_type = op_type; } else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) { - op_info = virtio_crypto_sym_op_helper(vdev, NULL, + sym_op_info = virtio_crypto_sym_op_helper(vdev, NULL, &req->u.chain.para, iov, out_num); - if (!op_info) { + if (!sym_op_info) { return -EFAULT; } - op_info->op_type = op_type; } else { /* VIRTIO_CRYPTO_SYM_OP_NONE */ error_report("virtio-crypto unsupported cipher type"); return -VIRTIO_CRYPTO_NOTSUPP; } - *sym_op_info = op_info; + sym_op_info->op_type = op_type; + op_info->u.sym_op_info = sym_op_info; return 0; } +static int +virtio_crypto_handle_asym_req(VirtIOCrypto *vcrypto, + struct virtio_crypto_akcipher_data_req *req, + CryptoDevBackendOpInfo *op_info, + struct iovec *iov, unsigned int out_num) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); + CryptoDevBackendAsymOpInfo *asym_op_info; + uint32_t src_len; + uint32_t dst_len; + uint32_t len; + uint8_t *src = NULL; + uint8_t *dst = NULL; + + asym_op_info = g_new0(CryptoDevBackendAsymOpInfo, 1); + src_len = ldl_le_p(&req->para.src_data_len); + dst_len = ldl_le_p(&req->para.dst_data_len); + + if (src_len > 0) { + src = g_malloc0(src_len); + len = iov_to_buf(iov, out_num, 0, src, src_len); + if (unlikely(len != src_len)) { + virtio_error(vdev, "virtio-crypto asym src data incorrect" + "expected %u, actual %u", src_len, len); + goto err; + } + + iov_discard_front(&iov, &out_num, src_len); + } + + if (dst_len > 0) { + dst = g_malloc0(dst_len); + + if (op_info->op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY) { + len = iov_to_buf(iov, out_num, 0, dst, dst_len); + if (unlikely(len != dst_len)) { + virtio_error(vdev, "virtio-crypto asym dst data incorrect" + "expected %u, actual %u", dst_len, len); + goto err; + } + + iov_discard_front(&iov, &out_num, dst_len); + } + } + + asym_op_info->src_len = src_len; + asym_op_info->dst_len = dst_len; + asym_op_info->src = src; + asym_op_info->dst = dst; + op_info->u.asym_op_info = asym_op_info; + + return 0; + + err: + g_free(asym_op_info); + g_free(src); + g_free(dst); + + return -EFAULT; +} + static int virtio_crypto_handle_request(VirtIOCryptoReq *request) { @@ -594,10 +831,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request) unsigned in_num; unsigned out_num; uint32_t opcode; - uint8_t status = VIRTIO_CRYPTO_ERR; - uint64_t session_id; - CryptoDevBackendSymOpInfo *sym_op_info = NULL; - Error *local_err = NULL; + CryptoDevBackendOpInfo *op_info = &request->op_info; if (elem->out_num < 1 || elem->in_num < 1) { virtio_error(vdev, "virtio-crypto dataq missing headers"); @@ -637,43 +871,49 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request) */ request->in_num = in_num; request->in_iov = in_iov; + /* now, we free the in_iov_copy inside virtio_crypto_free_request */ + in_iov_copy = NULL; opcode = ldl_le_p(&req.header.opcode); - session_id = ldq_le_p(&req.header.session_id); + op_info->session_id = ldq_le_p(&req.header.session_id); + op_info->op_code = opcode; + op_info->queue_index = queue_index; + op_info->cb = virtio_crypto_req_complete; + op_info->opaque = request; switch (opcode) { case VIRTIO_CRYPTO_CIPHER_ENCRYPT: case VIRTIO_CRYPTO_CIPHER_DECRYPT: + op_info->algtype = request->flags = QCRYPTODEV_BACKEND_ALG_SYM; ret = virtio_crypto_handle_sym_req(vcrypto, - &req.u.sym_req, - &sym_op_info, + &req.u.sym_req, op_info, out_iov, out_num); + goto check_result; + + case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT: + case VIRTIO_CRYPTO_AKCIPHER_DECRYPT: + case VIRTIO_CRYPTO_AKCIPHER_SIGN: + case VIRTIO_CRYPTO_AKCIPHER_VERIFY: + op_info->algtype = request->flags = QCRYPTODEV_BACKEND_ALG_ASYM; + ret = virtio_crypto_handle_asym_req(vcrypto, + &req.u.akcipher_req, op_info, + out_iov, out_num); + +check_result: /* Serious errors, need to reset virtio crypto device */ if (ret == -EFAULT) { return -1; } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) { - virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP); - virtio_crypto_free_request(request); + virtio_crypto_req_complete(request, -VIRTIO_CRYPTO_NOTSUPP); } else { - sym_op_info->session_id = session_id; - - /* Set request's parameter */ - request->flags = CRYPTODEV_BACKEND_ALG_SYM; - request->u.sym_op_info = sym_op_info; ret = cryptodev_backend_crypto_operation(vcrypto->cryptodev, - request, queue_index, &local_err); + op_info); if (ret < 0) { - status = -ret; - if (local_err) { - error_report_err(local_err); - } - } else { /* ret == VIRTIO_CRYPTO_OK */ - status = ret; + virtio_crypto_req_complete(request, ret); } - virtio_crypto_req_complete(request, status); - virtio_crypto_free_request(request); } break; + case VIRTIO_CRYPTO_HASH: case VIRTIO_CRYPTO_MAC: case VIRTIO_CRYPTO_AEAD_ENCRYPT: @@ -681,8 +921,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request) default: error_report("virtio-crypto unsupported dataq opcode: %u", opcode); - virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP); - virtio_crypto_free_request(request); + virtio_crypto_req_complete(request, -VIRTIO_CRYPTO_NOTSUPP); } return 0; @@ -765,12 +1004,35 @@ static void virtio_crypto_reset(VirtIODevice *vdev) } } +static uint32_t virtio_crypto_init_services(uint32_t qservices) +{ + uint32_t vservices = 0; + + if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_CIPHER)) { + vservices |= (1 << VIRTIO_CRYPTO_SERVICE_CIPHER); + } + if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_HASH)) { + vservices |= (1 << VIRTIO_CRYPTO_SERVICE_HASH); + } + if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_MAC)) { + vservices |= (1 << VIRTIO_CRYPTO_SERVICE_MAC); + } + if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_AEAD)) { + vservices |= (1 << VIRTIO_CRYPTO_SERVICE_AEAD); + } + if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_AKCIPHER)) { + vservices |= (1 << VIRTIO_CRYPTO_SERVICE_AKCIPHER); + } + + return vservices; +} + static void virtio_crypto_init_config(VirtIODevice *vdev) { VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); - vcrypto->conf.crypto_services = - vcrypto->conf.cryptodev->conf.crypto_services; + vcrypto->conf.crypto_services = virtio_crypto_init_services( + vcrypto->conf.cryptodev->conf.crypto_services); vcrypto->conf.cipher_algo_l = vcrypto->conf.cryptodev->conf.cipher_algo_l; vcrypto->conf.cipher_algo_h = @@ -779,6 +1041,7 @@ static void virtio_crypto_init_config(VirtIODevice *vdev) vcrypto->conf.mac_algo_l = vcrypto->conf.cryptodev->conf.mac_algo_l; vcrypto->conf.mac_algo_h = vcrypto->conf.cryptodev->conf.mac_algo_h; vcrypto->conf.aead_algo = vcrypto->conf.cryptodev->conf.aead_algo; + vcrypto->conf.akcipher_algo = vcrypto->conf.cryptodev->conf.akcipher_algo; vcrypto->conf.max_cipher_key_len = vcrypto->conf.cryptodev->conf.max_cipher_key_len; vcrypto->conf.max_auth_key_len = @@ -817,11 +1080,12 @@ static void virtio_crypto_device_realize(DeviceState *dev, Error **errp) vcrypto->vqs[i].dataq = virtio_add_queue(vdev, 1024, virtio_crypto_handle_dataq_bh); vcrypto->vqs[i].dataq_bh = - qemu_bh_new(virtio_crypto_dataq_bh, &vcrypto->vqs[i]); + virtio_bh_new_guarded(dev, virtio_crypto_dataq_bh, + &vcrypto->vqs[i]); vcrypto->vqs[i].vcrypto = vcrypto; } - vcrypto->ctrl_vq = virtio_add_queue(vdev, 64, virtio_crypto_handle_ctrl); + vcrypto->ctrl_vq = virtio_add_queue(vdev, 1024, virtio_crypto_handle_ctrl); if (!cryptodev_backend_is_ready(vcrypto->cryptodev)) { vcrypto->status &= ~VIRTIO_CRYPTO_S_HW_READY; } else { @@ -858,7 +1122,7 @@ static const VMStateDescription vmstate_virtio_crypto = { .unmigratable = 1, .minimum_version_id = VIRTIO_CRYPTO_VM_VERSION, .version_id = VIRTIO_CRYPTO_VM_VERSION, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, @@ -891,6 +1155,7 @@ static void virtio_crypto_get_config(VirtIODevice *vdev, uint8_t *config) stl_le_p(&crypto_cfg.max_cipher_key_len, c->conf.max_cipher_key_len); stl_le_p(&crypto_cfg.max_auth_key_len, c->conf.max_auth_key_len); stq_le_p(&crypto_cfg.max_size, c->conf.max_size); + stl_le_p(&crypto_cfg.akcipher_algo, c->conf.akcipher_algo); memcpy(config, &crypto_cfg, c->config_size); } @@ -948,6 +1213,15 @@ static void virtio_crypto_guest_notifier_mask(VirtIODevice *vdev, int idx, assert(vcrypto->vhost_started); + /* + * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1 + * as the macro of configure interrupt's IDX, If this driver does not + * support, the function will return + */ + + if (idx == VIRTIO_CONFIG_IRQ_IDX) { + return; + } cryptodev_vhost_virtqueue_mask(vdev, queue, idx, mask); } @@ -958,6 +1232,15 @@ static bool virtio_crypto_guest_notifier_pending(VirtIODevice *vdev, int idx) assert(vcrypto->vhost_started); + /* + * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1 + * as the macro of configure interrupt's IDX, If this driver does not + * support, the function will return + */ + + if (idx == VIRTIO_CONFIG_IRQ_IDX) { + return false; + } return cryptodev_vhost_virtqueue_pending(vdev, queue, idx); } @@ -995,7 +1278,7 @@ static void virtio_crypto_instance_init(Object *obj) /* * The default config_size is sizeof(struct virtio_crypto_config). - * Can be overriden with virtio_crypto_set_config_size. + * Can be overridden with virtio_crypto_set_config_size. */ vcrypto->config_size = sizeof(struct virtio_crypto_config); } diff --git a/hw/virtio/virtio-hmp-cmds.c b/hw/virtio/virtio-hmp-cmds.c new file mode 100644 index 0000000000..477c97dea2 --- /dev/null +++ b/hw/virtio/virtio-hmp-cmds.c @@ -0,0 +1,321 @@ +/* + * HMP commands related to virtio + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/qapi-commands-virtio.h" +#include "qapi/qmp/qdict.h" + + +static void hmp_virtio_dump_protocols(Monitor *mon, + VhostDeviceProtocols *pcol) +{ + strList *pcol_list = pcol->protocols; + while (pcol_list) { + monitor_printf(mon, "\t%s", pcol_list->value); + pcol_list = pcol_list->next; + if (pcol_list != NULL) { + monitor_printf(mon, ",\n"); + } + } + monitor_printf(mon, "\n"); + if (pcol->has_unknown_protocols) { + monitor_printf(mon, " unknown-protocols(0x%016"PRIx64")\n", + pcol->unknown_protocols); + } +} + +static void hmp_virtio_dump_status(Monitor *mon, + VirtioDeviceStatus *status) +{ + strList *status_list = status->statuses; + while (status_list) { + monitor_printf(mon, "\t%s", status_list->value); + status_list = status_list->next; + if (status_list != NULL) { + monitor_printf(mon, ",\n"); + } + } + monitor_printf(mon, "\n"); + if (status->has_unknown_statuses) { + monitor_printf(mon, " unknown-statuses(0x%016"PRIx32")\n", + status->unknown_statuses); + } +} + +static void hmp_virtio_dump_features(Monitor *mon, + VirtioDeviceFeatures *features) +{ + strList *transport_list = features->transports; + while (transport_list) { + monitor_printf(mon, "\t%s", transport_list->value); + transport_list = transport_list->next; + if (transport_list != NULL) { + monitor_printf(mon, ",\n"); + } + } + + monitor_printf(mon, "\n"); + strList *list = features->dev_features; + if (list) { + while (list) { + monitor_printf(mon, "\t%s", list->value); + list = list->next; + if (list != NULL) { + monitor_printf(mon, ",\n"); + } + } + monitor_printf(mon, "\n"); + } + + if (features->has_unknown_dev_features) { + monitor_printf(mon, " unknown-features(0x%016"PRIx64")\n", + features->unknown_dev_features); + } +} + +void hmp_virtio_query(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + VirtioInfoList *list = qmp_x_query_virtio(&err); + VirtioInfoList *node; + + if (err != NULL) { + hmp_handle_error(mon, err); + return; + } + + if (list == NULL) { + monitor_printf(mon, "No VirtIO devices\n"); + return; + } + + node = list; + while (node) { + monitor_printf(mon, "%s [%s]\n", node->value->path, + node->value->name); + node = node->next; + } + qapi_free_VirtioInfoList(list); +} + +void hmp_virtio_status(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *path = qdict_get_try_str(qdict, "path"); + VirtioStatus *s = qmp_x_query_virtio_status(path, &err); + + if (err != NULL) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "%s:\n", path); + monitor_printf(mon, " device_name: %s %s\n", + s->name, s->vhost_dev ? "(vhost)" : ""); + monitor_printf(mon, " device_id: %d\n", s->device_id); + monitor_printf(mon, " vhost_started: %s\n", + s->vhost_started ? "true" : "false"); + monitor_printf(mon, " bus_name: %s\n", s->bus_name); + monitor_printf(mon, " broken: %s\n", + s->broken ? "true" : "false"); + monitor_printf(mon, " disabled: %s\n", + s->disabled ? "true" : "false"); + monitor_printf(mon, " disable_legacy_check: %s\n", + s->disable_legacy_check ? "true" : "false"); + monitor_printf(mon, " started: %s\n", + s->started ? "true" : "false"); + monitor_printf(mon, " use_started: %s\n", + s->use_started ? "true" : "false"); + monitor_printf(mon, " start_on_kick: %s\n", + s->start_on_kick ? "true" : "false"); + monitor_printf(mon, " use_guest_notifier_mask: %s\n", + s->use_guest_notifier_mask ? "true" : "false"); + monitor_printf(mon, " vm_running: %s\n", + s->vm_running ? "true" : "false"); + monitor_printf(mon, " num_vqs: %"PRId64"\n", s->num_vqs); + monitor_printf(mon, " queue_sel: %d\n", + s->queue_sel); + monitor_printf(mon, " isr: %d\n", s->isr); + monitor_printf(mon, " endianness: %s\n", + s->device_endian); + monitor_printf(mon, " status:\n"); + hmp_virtio_dump_status(mon, s->status); + monitor_printf(mon, " Guest features:\n"); + hmp_virtio_dump_features(mon, s->guest_features); + monitor_printf(mon, " Host features:\n"); + hmp_virtio_dump_features(mon, s->host_features); + monitor_printf(mon, " Backend features:\n"); + hmp_virtio_dump_features(mon, s->backend_features); + + if (s->vhost_dev) { + monitor_printf(mon, " VHost:\n"); + monitor_printf(mon, " nvqs: %d\n", + s->vhost_dev->nvqs); + monitor_printf(mon, " vq_index: %"PRId64"\n", + s->vhost_dev->vq_index); + monitor_printf(mon, " max_queues: %"PRId64"\n", + s->vhost_dev->max_queues); + monitor_printf(mon, " n_mem_sections: %"PRId64"\n", + s->vhost_dev->n_mem_sections); + monitor_printf(mon, " n_tmp_sections: %"PRId64"\n", + s->vhost_dev->n_tmp_sections); + monitor_printf(mon, " backend_cap: %"PRId64"\n", + s->vhost_dev->backend_cap); + monitor_printf(mon, " log_enabled: %s\n", + s->vhost_dev->log_enabled ? "true" : "false"); + monitor_printf(mon, " log_size: %"PRId64"\n", + s->vhost_dev->log_size); + monitor_printf(mon, " Features:\n"); + hmp_virtio_dump_features(mon, s->vhost_dev->features); + monitor_printf(mon, " Acked features:\n"); + hmp_virtio_dump_features(mon, s->vhost_dev->acked_features); + monitor_printf(mon, " Backend features:\n"); + hmp_virtio_dump_features(mon, s->vhost_dev->backend_features); + monitor_printf(mon, " Protocol features:\n"); + hmp_virtio_dump_protocols(mon, s->vhost_dev->protocol_features); + } + + qapi_free_VirtioStatus(s); +} + +void hmp_vhost_queue_status(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *path = qdict_get_try_str(qdict, "path"); + int queue = qdict_get_int(qdict, "queue"); + VirtVhostQueueStatus *s = + qmp_x_query_virtio_vhost_queue_status(path, queue, &err); + + if (err != NULL) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "%s:\n", path); + monitor_printf(mon, " device_name: %s (vhost)\n", + s->name); + monitor_printf(mon, " kick: %"PRId64"\n", s->kick); + monitor_printf(mon, " call: %"PRId64"\n", s->call); + monitor_printf(mon, " VRing:\n"); + monitor_printf(mon, " num: %"PRId64"\n", s->num); + monitor_printf(mon, " desc: 0x%016"PRIx64"\n", s->desc); + monitor_printf(mon, " desc_phys: 0x%016"PRIx64"\n", + s->desc_phys); + monitor_printf(mon, " desc_size: %"PRId32"\n", s->desc_size); + monitor_printf(mon, " avail: 0x%016"PRIx64"\n", s->avail); + monitor_printf(mon, " avail_phys: 0x%016"PRIx64"\n", + s->avail_phys); + monitor_printf(mon, " avail_size: %"PRId32"\n", s->avail_size); + monitor_printf(mon, " used: 0x%016"PRIx64"\n", s->used); + monitor_printf(mon, " used_phys: 0x%016"PRIx64"\n", + s->used_phys); + monitor_printf(mon, " used_size: %"PRId32"\n", s->used_size); + + qapi_free_VirtVhostQueueStatus(s); +} + +void hmp_virtio_queue_status(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *path = qdict_get_try_str(qdict, "path"); + int queue = qdict_get_int(qdict, "queue"); + VirtQueueStatus *s = qmp_x_query_virtio_queue_status(path, queue, &err); + + if (err != NULL) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "%s:\n", path); + monitor_printf(mon, " device_name: %s\n", s->name); + monitor_printf(mon, " queue_index: %d\n", s->queue_index); + monitor_printf(mon, " inuse: %d\n", s->inuse); + monitor_printf(mon, " used_idx: %d\n", s->used_idx); + monitor_printf(mon, " signalled_used: %d\n", + s->signalled_used); + monitor_printf(mon, " signalled_used_valid: %s\n", + s->signalled_used_valid ? "true" : "false"); + if (s->has_last_avail_idx) { + monitor_printf(mon, " last_avail_idx: %d\n", + s->last_avail_idx); + } + if (s->has_shadow_avail_idx) { + monitor_printf(mon, " shadow_avail_idx: %d\n", + s->shadow_avail_idx); + } + monitor_printf(mon, " VRing:\n"); + monitor_printf(mon, " num: %"PRId32"\n", s->vring_num); + monitor_printf(mon, " num_default: %"PRId32"\n", + s->vring_num_default); + monitor_printf(mon, " align: %"PRId32"\n", + s->vring_align); + monitor_printf(mon, " desc: 0x%016"PRIx64"\n", + s->vring_desc); + monitor_printf(mon, " avail: 0x%016"PRIx64"\n", + s->vring_avail); + monitor_printf(mon, " used: 0x%016"PRIx64"\n", + s->vring_used); + + qapi_free_VirtQueueStatus(s); +} + +void hmp_virtio_queue_element(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *path = qdict_get_try_str(qdict, "path"); + int queue = qdict_get_int(qdict, "queue"); + int index = qdict_get_try_int(qdict, "index", -1); + VirtioQueueElement *e; + VirtioRingDescList *list; + + e = qmp_x_query_virtio_queue_element(path, queue, index != -1, + index, &err); + if (err != NULL) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "%s:\n", path); + monitor_printf(mon, " device_name: %s\n", e->name); + monitor_printf(mon, " index: %d\n", e->index); + monitor_printf(mon, " desc:\n"); + monitor_printf(mon, " descs:\n"); + + list = e->descs; + while (list) { + monitor_printf(mon, " addr 0x%"PRIx64" len %d", + list->value->addr, list->value->len); + if (list->value->flags) { + strList *flag = list->value->flags; + monitor_printf(mon, " ("); + while (flag) { + monitor_printf(mon, "%s", flag->value); + flag = flag->next; + if (flag) { + monitor_printf(mon, ", "); + } + } + monitor_printf(mon, ")"); + } + list = list->next; + if (list) { + monitor_printf(mon, ",\n"); + } + } + monitor_printf(mon, "\n"); + monitor_printf(mon, " avail:\n"); + monitor_printf(mon, " flags: %d\n", e->avail->flags); + monitor_printf(mon, " idx: %d\n", e->avail->idx); + monitor_printf(mon, " ring: %d\n", e->avail->ring); + monitor_printf(mon, " used:\n"); + monitor_printf(mon, " flags: %d\n", e->used->flags); + monitor_printf(mon, " idx: %d\n", e->used->idx); + + qapi_free_VirtioQueueElement(e); +} diff --git a/hw/virtio/virtio-input-pci.c b/hw/virtio/virtio-input-pci.c index a9d0992389..a53edf46c4 100644 --- a/hw/virtio/virtio-input-pci.c +++ b/hw/virtio/virtio-input-pci.c @@ -25,10 +25,11 @@ struct VirtIOInputPCI { VirtIOInput vdev; }; -#define TYPE_VIRTIO_INPUT_HID_PCI "virtio-input-hid-pci" -#define TYPE_VIRTIO_KEYBOARD_PCI "virtio-keyboard-pci" -#define TYPE_VIRTIO_MOUSE_PCI "virtio-mouse-pci" -#define TYPE_VIRTIO_TABLET_PCI "virtio-tablet-pci" +#define TYPE_VIRTIO_INPUT_HID_PCI "virtio-input-hid-pci" +#define TYPE_VIRTIO_KEYBOARD_PCI "virtio-keyboard-pci" +#define TYPE_VIRTIO_MOUSE_PCI "virtio-mouse-pci" +#define TYPE_VIRTIO_TABLET_PCI "virtio-tablet-pci" +#define TYPE_VIRTIO_MULTITOUCH_PCI "virtio-multitouch-pci" OBJECT_DECLARE_SIMPLE_TYPE(VirtIOInputHIDPCI, VIRTIO_INPUT_HID_PCI) struct VirtIOInputHIDPCI { @@ -102,6 +103,14 @@ static void virtio_tablet_initfn(Object *obj) TYPE_VIRTIO_TABLET); } +static void virtio_multitouch_initfn(Object *obj) +{ + VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_MULTITOUCH); +} + static const TypeInfo virtio_input_pci_info = { .name = TYPE_VIRTIO_INPUT_PCI, .parent = TYPE_VIRTIO_PCI, @@ -140,6 +149,13 @@ static const VirtioPCIDeviceTypeInfo virtio_tablet_pci_info = { .instance_init = virtio_tablet_initfn, }; +static const VirtioPCIDeviceTypeInfo virtio_multitouch_pci_info = { + .generic_name = TYPE_VIRTIO_MULTITOUCH_PCI, + .parent = TYPE_VIRTIO_INPUT_HID_PCI, + .instance_size = sizeof(VirtIOInputHIDPCI), + .instance_init = virtio_multitouch_initfn, +}; + static void virtio_pci_input_register(void) { /* Base types: */ @@ -150,6 +166,7 @@ static void virtio_pci_input_register(void) virtio_pci_types_register(&virtio_keyboard_pci_info); virtio_pci_types_register(&virtio_mouse_pci_info); virtio_pci_types_register(&virtio_tablet_pci_info); + virtio_pci_types_register(&virtio_multitouch_pci_info); } type_init(virtio_pci_input_register) diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c index 844d647704..cbdfe4c591 100644 --- a/hw/virtio/virtio-iommu-pci.c +++ b/hw/virtio/virtio-iommu-pci.c @@ -17,6 +17,7 @@ #include "hw/qdev-properties-system.h" #include "qapi/error.h" #include "hw/boards.h" +#include "hw/pci/pci_bus.h" #include "qom/object.h" typedef struct VirtIOIOMMUPCI VirtIOIOMMUPCI; @@ -36,7 +37,7 @@ struct VirtIOIOMMUPCI { static Property virtio_iommu_pci_properties[] = { DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), DEFINE_PROP_ARRAY("reserved-regions", VirtIOIOMMUPCI, - vdev.nb_reserved_regions, vdev.reserved_regions, + vdev.nr_prop_resv_regions, vdev.prop_resv_regions, qdev_prop_reserved_region, ReservedRegion), DEFINE_PROP_END_OF_LIST(), }; @@ -44,6 +45,7 @@ static Property virtio_iommu_pci_properties[] = { static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIOIOMMUPCI *dev = VIRTIO_IOMMU_PCI(vpci_dev); + PCIBus *pbus = pci_get_bus(&vpci_dev->pci_dev); DeviceState *vdev = DEVICE(&dev->vdev); VirtIOIOMMU *s = VIRTIO_IOMMU(vdev); @@ -52,16 +54,22 @@ static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) "for the virtio-iommu-pci device"); return; } - for (int i = 0; i < s->nb_reserved_regions; i++) { - if (s->reserved_regions[i].type != VIRTIO_IOMMU_RESV_MEM_T_RESERVED && - s->reserved_regions[i].type != VIRTIO_IOMMU_RESV_MEM_T_MSI) { + for (int i = 0; i < s->nr_prop_resv_regions; i++) { + if (s->prop_resv_regions[i].type != VIRTIO_IOMMU_RESV_MEM_T_RESERVED && + s->prop_resv_regions[i].type != VIRTIO_IOMMU_RESV_MEM_T_MSI) { error_setg(errp, "reserved region %d has an invalid type", i); error_append_hint(errp, "Valid values are 0 and 1\n"); + return; } } + if (!pci_bus_is_root(pbus)) { + error_setg(errp, "virtio-iommu-pci must be plugged on the root bus"); + return; + } + object_property_set_link(OBJECT(dev), "primary-bus", - OBJECT(pci_get_bus(&vpci_dev->pci_dev)), - &error_abort); + OBJECT(pbus), &error_abort); + virtio_pci_force_virtio_1(vpci_dev); qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } @@ -74,8 +82,6 @@ static void virtio_iommu_pci_class_init(ObjectClass *klass, void *data) 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; @@ -89,10 +95,18 @@ static void virtio_iommu_pci_instance_init(Object *obj) TYPE_VIRTIO_IOMMU); } +static void virtio_iommu_pci_instance_finalize(Object *obj) +{ + VirtIOIOMMUPCI *dev = VIRTIO_IOMMU_PCI(obj); + + g_free(dev->vdev.prop_resv_regions); +} + static const VirtioPCIDeviceTypeInfo virtio_iommu_pci_info = { - .generic_name = TYPE_VIRTIO_IOMMU_PCI, + .generic_name = TYPE_VIRTIO_IOMMU_PCI, .instance_size = sizeof(VirtIOIOMMUPCI), .instance_init = virtio_iommu_pci_instance_init, + .instance_finalize = virtio_iommu_pci_instance_finalize, .class_init = virtio_iommu_pci_class_init, }; diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 2597e166f9..1326c6ec41 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -20,10 +20,16 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/iov.h" +#include "qemu/range.h" +#include "qemu/reserved-region.h" +#include "exec/target_page.h" #include "hw/qdev-properties.h" #include "hw/virtio/virtio.h" #include "sysemu/kvm.h" #include "sysemu/reset.h" +#include "sysemu/sysemu.h" +#include "qemu/reserved-region.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "trace.h" @@ -31,7 +37,6 @@ #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" @@ -69,6 +74,77 @@ static inline uint16_t virtio_iommu_get_bdf(IOMMUDevice *dev) return PCI_BUILD_BDF(pci_bus_num(dev->bus), dev->devfn); } +static bool virtio_iommu_device_bypassed(IOMMUDevice *sdev) +{ + uint32_t sid; + bool bypassed; + VirtIOIOMMU *s = sdev->viommu; + VirtIOIOMMUEndpoint *ep; + + sid = virtio_iommu_get_bdf(sdev); + + qemu_rec_mutex_lock(&s->mutex); + /* need to check bypass before system reset */ + if (!s->endpoints) { + bypassed = s->config.bypass; + goto unlock; + } + + ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(sid)); + if (!ep || !ep->domain) { + bypassed = s->config.bypass; + } else { + bypassed = ep->domain->bypass; + } + +unlock: + qemu_rec_mutex_unlock(&s->mutex); + return bypassed; +} + +/* Return whether the device is using IOMMU translation. */ +static bool virtio_iommu_switch_address_space(IOMMUDevice *sdev) +{ + bool use_remapping; + + assert(sdev); + + use_remapping = !virtio_iommu_device_bypassed(sdev); + + trace_virtio_iommu_switch_address_space(pci_bus_num(sdev->bus), + PCI_SLOT(sdev->devfn), + PCI_FUNC(sdev->devfn), + use_remapping); + + /* Turn off first then on the other */ + if (use_remapping) { + memory_region_set_enabled(&sdev->bypass_mr, false); + memory_region_set_enabled(MEMORY_REGION(&sdev->iommu_mr), true); + } else { + memory_region_set_enabled(MEMORY_REGION(&sdev->iommu_mr), false); + memory_region_set_enabled(&sdev->bypass_mr, true); + } + + return use_remapping; +} + +static void virtio_iommu_switch_address_space_all(VirtIOIOMMU *s) +{ + GHashTableIter iter; + IOMMUPciBus *iommu_pci_bus; + int i; + + g_hash_table_iter_init(&iter, s->as_by_busptr); + while (g_hash_table_iter_next(&iter, NULL, (void **)&iommu_pci_bus)) { + for (i = 0; i < PCI_DEVFN_MAX; i++) { + if (!iommu_pci_bus->pbdev[i]) { + continue; + } + virtio_iommu_switch_address_space(iommu_pci_bus->pbdev[i]); + } + } +} + /** * 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 @@ -126,6 +202,32 @@ static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer user_data) } } +static void virtio_iommu_notify_map_unmap(IOMMUMemoryRegion *mr, + IOMMUTLBEvent *event, + hwaddr virt_start, hwaddr virt_end) +{ + uint64_t delta = virt_end - virt_start; + + event->entry.iova = virt_start; + event->entry.addr_mask = delta; + + if (delta == UINT64_MAX) { + memory_region_notify_iommu(mr, 0, *event); + } + + while (virt_start != virt_end + 1) { + uint64_t mask = dma_aligned_pow2_mask(virt_start, virt_end, 64); + + event->entry.addr_mask = mask; + event->entry.iova = virt_start; + memory_region_notify_iommu(mr, 0, *event); + virt_start += mask + 1; + if (event->entry.perm != IOMMU_NONE) { + event->entry.translated_addr += mask + 1; + } + } +} + static void virtio_iommu_notify_map(IOMMUMemoryRegion *mr, hwaddr virt_start, hwaddr virt_end, hwaddr paddr, uint32_t flags) @@ -144,19 +246,16 @@ static void virtio_iommu_notify_map(IOMMUMemoryRegion *mr, hwaddr virt_start, event.type = IOMMU_NOTIFIER_MAP; event.entry.target_as = &address_space_memory; - event.entry.addr_mask = virt_end - virt_start; - event.entry.iova = virt_start; event.entry.perm = perm; event.entry.translated_addr = paddr; - memory_region_notify_iommu(mr, 0, event); + virtio_iommu_notify_map_unmap(mr, &event, virt_start, virt_end); } static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr virt_start, hwaddr virt_end) { IOMMUTLBEvent event; - uint64_t delta = virt_end - virt_start; if (!(mr->iommu_notify_flags & IOMMU_NOTIFIER_UNMAP)) { return; @@ -168,22 +267,8 @@ static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr virt_start, event.entry.target_as = &address_space_memory; event.entry.perm = IOMMU_NONE; event.entry.translated_addr = 0; - event.entry.addr_mask = delta; - event.entry.iova = virt_start; - if (delta == UINT64_MAX) { - memory_region_notify_iommu(mr, 0, event); - } - - - while (virt_start != virt_end + 1) { - uint64_t mask = dma_aligned_pow2_mask(virt_start, virt_end, 64); - - event.entry.addr_mask = mask; - event.entry.iova = virt_start; - memory_region_notify_iommu(mr, 0, event); - virt_start += mask + 1; - } + virtio_iommu_notify_map_unmap(mr, &event, virt_start, virt_end); } static gboolean virtio_iommu_notify_unmap_cb(gpointer key, gpointer value, @@ -213,6 +298,7 @@ static gboolean virtio_iommu_notify_map_cb(gpointer key, gpointer value, static void virtio_iommu_detach_endpoint_from_domain(VirtIOIOMMUEndpoint *ep) { VirtIOIOMMUDomain *domain = ep->domain; + IOMMUDevice *sdev = container_of(ep->iommu_mr, IOMMUDevice, iommu_mr); if (!ep->domain) { return; @@ -221,6 +307,7 @@ static void virtio_iommu_detach_endpoint_from_domain(VirtIOIOMMUEndpoint *ep) ep->iommu_mr); QLIST_REMOVE(ep, next); ep->domain = NULL; + virtio_iommu_switch_address_space(sdev); } static VirtIOIOMMUEndpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s, @@ -295,6 +382,19 @@ static void virtio_iommu_put_domain(gpointer data) g_free(domain); } +static void add_prop_resv_regions(IOMMUDevice *sdev) +{ + VirtIOIOMMU *s = sdev->viommu; + int i; + + for (i = 0; i < s->nr_prop_resv_regions; i++) { + ReservedRegion *reg = g_new0(ReservedRegion, 1); + + *reg = s->prop_resv_regions[i]; + sdev->resv_regions = resv_region_list_insert(sdev->resv_regions, reg); + } +} + static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque, int devfn) { @@ -323,17 +423,49 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque, trace_virtio_iommu_init_iommu_mr(name); + memory_region_init(&sdev->root, OBJECT(s), name, UINT64_MAX); + address_space_init(&sdev->as, &sdev->root, TYPE_VIRTIO_IOMMU); + add_prop_resv_regions(sdev); + + /* + * Build the IOMMU disabled container with aliases to the + * shared MRs. Note that aliasing to a shared memory region + * could help the memory API to detect same FlatViews so we + * can have devices to share the same FlatView when in bypass + * mode. (either by not configuring virtio-iommu driver or with + * "iommu=pt"). It will greatly reduce the total number of + * FlatViews of the system hence VM runs faster. + */ + memory_region_init_alias(&sdev->bypass_mr, OBJECT(s), + "system", get_system_memory(), 0, + memory_region_size(get_system_memory())); + 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); + + /* + * Hook both the containers under the root container, we + * switch between iommu & bypass MRs by enable/disable + * corresponding sub-containers + */ + memory_region_add_subregion_overlap(&sdev->root, 0, + MEMORY_REGION(&sdev->iommu_mr), + 0); + memory_region_add_subregion_overlap(&sdev->root, 0, + &sdev->bypass_mr, 0); + + virtio_iommu_switch_address_space(sdev); g_free(name); } return &sdev->as; } +static const PCIIOMMUOps virtio_iommu_ops = { + .get_address_space = virtio_iommu_find_add_as, +}; + static int virtio_iommu_attach(VirtIOIOMMU *s, struct virtio_iommu_req_attach *req) { @@ -342,6 +474,7 @@ static int virtio_iommu_attach(VirtIOIOMMU *s, uint32_t flags = le32_to_cpu(req->flags); VirtIOIOMMUDomain *domain; VirtIOIOMMUEndpoint *ep; + IOMMUDevice *sdev; trace_virtio_iommu_attach(domain_id, ep_id); @@ -375,6 +508,8 @@ static int virtio_iommu_attach(VirtIOIOMMU *s, QLIST_INSERT_HEAD(&domain->endpoint_list, ep, next); ep->domain = domain; + sdev = container_of(ep->iommu_mr, IOMMUDevice, iommu_mr); + virtio_iommu_switch_address_space(sdev); /* Replay domain mappings on the associated memory region */ g_tree_foreach(domain->mappings, virtio_iommu_notify_map_cb, @@ -511,29 +646,30 @@ static int virtio_iommu_unmap(VirtIOIOMMU *s, return ret; } -static ssize_t virtio_iommu_fill_resv_mem_prop(VirtIOIOMMU *s, uint32_t ep, +static ssize_t virtio_iommu_fill_resv_mem_prop(IOMMUDevice *sdev, uint32_t ep, uint8_t *buf, size_t free) { struct virtio_iommu_probe_resv_mem prop = {}; size_t size = sizeof(prop), length = size - sizeof(prop.head), total; - int i; - - total = size * s->nb_reserved_regions; + GList *l; + total = size * g_list_length(sdev->resv_regions); if (total > free) { return -ENOSPC; } - for (i = 0; i < s->nb_reserved_regions; i++) { - unsigned subtype = s->reserved_regions[i].type; + for (l = sdev->resv_regions; l; l = l->next) { + ReservedRegion *reg = l->data; + unsigned subtype = reg->type; + Range *range = ®->range; assert(subtype == VIRTIO_IOMMU_RESV_MEM_T_RESERVED || subtype == VIRTIO_IOMMU_RESV_MEM_T_MSI); prop.head.type = cpu_to_le16(VIRTIO_IOMMU_PROBE_T_RESV_MEM); prop.head.length = cpu_to_le16(length); prop.subtype = subtype; - prop.start = cpu_to_le64(s->reserved_regions[i].low); - prop.end = cpu_to_le64(s->reserved_regions[i].high); + prop.start = cpu_to_le64(range_lob(range)); + prop.end = cpu_to_le64(range_upb(range)); memcpy(buf, &prop, size); @@ -553,30 +689,34 @@ static int virtio_iommu_probe(VirtIOIOMMU *s, uint8_t *buf) { uint32_t ep_id = le32_to_cpu(req->endpoint); + IOMMUMemoryRegion *iommu_mr = virtio_iommu_mr(s, ep_id); size_t free = VIOMMU_PROBE_SIZE; + IOMMUDevice *sdev; ssize_t count; - if (!virtio_iommu_mr(s, ep_id)) { + if (!iommu_mr) { return VIRTIO_IOMMU_S_NOENT; } - count = virtio_iommu_fill_resv_mem_prop(s, ep_id, buf, free); + sdev = container_of(iommu_mr, IOMMUDevice, iommu_mr); + + count = virtio_iommu_fill_resv_mem_prop(sdev, ep_id, buf, free); if (count < 0) { return VIRTIO_IOMMU_S_INVAL; } buf += count; free -= count; + sdev->probe_done = true; return VIRTIO_IOMMU_S_OK; } static int virtio_iommu_iov_to_req(struct iovec *iov, unsigned int iov_cnt, - void *req, size_t req_sz) + void *req, size_t payload_sz) { - size_t sz, payload_sz = req_sz - sizeof(struct virtio_iommu_req_tail); + size_t sz = iov_to_buf(iov, iov_cnt, 0, req, payload_sz); - sz = iov_to_buf(iov, iov_cnt, 0, req, payload_sz); if (unlikely(sz != payload_sz)) { return VIRTIO_IOMMU_S_INVAL; } @@ -589,7 +729,8 @@ static int virtio_iommu_handle_ ## __req(VirtIOIOMMU *s, \ unsigned int iov_cnt) \ { \ struct virtio_iommu_req_ ## __req req; \ - int ret = virtio_iommu_iov_to_req(iov, iov_cnt, &req, sizeof(req)); \ + int ret = virtio_iommu_iov_to_req(iov, iov_cnt, &req, \ + sizeof(req) - sizeof(struct virtio_iommu_req_tail));\ \ return ret ? ret : virtio_iommu_ ## __req(s, &req); \ } @@ -615,13 +756,15 @@ 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 = {}; - size_t output_size = sizeof(tail), sz; VirtQueueElement *elem; unsigned int iov_cnt; struct iovec *iov; void *buf = NULL; + size_t sz; for (;;) { + size_t output_size = sizeof(tail); + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { return; @@ -642,7 +785,7 @@ static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) tail.status = VIRTIO_IOMMU_S_DEVERR; goto out; } - qemu_mutex_lock(&s->mutex); + qemu_rec_mutex_lock(&s->mutex); switch (head.type) { case VIRTIO_IOMMU_T_ATTACH: tail.status = virtio_iommu_handle_attach(s, iov, iov_cnt); @@ -663,15 +806,14 @@ static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) output_size = s->config.probe_size + sizeof(tail); buf = g_malloc0(output_size); - ptail = (struct virtio_iommu_req_tail *) - (buf + s->config.probe_size); + ptail = buf + s->config.probe_size; ptail->status = virtio_iommu_handle_probe(s, iov, iov_cnt, buf); break; } default: tail.status = VIRTIO_IOMMU_S_UNSUPP; } - qemu_mutex_unlock(&s->mutex); + qemu_rec_mutex_unlock(&s->mutex); out: sz = iov_from_buf(elem->in_sg, elem->in_num, 0, @@ -740,17 +882,19 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, VirtIOIOMMUEndpoint *ep; uint32_t sid, flags; bool bypass_allowed; + int granule; bool found; - int i; + GList *l; interval.low = addr; interval.high = addr + 1; + granule = ctz64(s->config.page_size_mask); IOMMUTLBEntry entry = { .target_as = &address_space_memory, .iova = addr, .translated_addr = addr, - .addr_mask = (1 << ctz32(s->config.page_size_mask)) - 1, + .addr_mask = BIT_ULL(granule) - 1, .perm = IOMMU_NONE, }; @@ -759,9 +903,13 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, sid = virtio_iommu_get_bdf(sdev); trace_virtio_iommu_translate(mr->parent_obj.name, sid, addr, flag); - qemu_mutex_lock(&s->mutex); + qemu_rec_mutex_lock(&s->mutex); ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(sid)); + + if (bypass_allowed) + assert(ep && ep->domain && !ep->domain->bypass); + if (!ep) { if (!bypass_allowed) { error_report_once("%s sid=%d is not known!!", __func__, sid); @@ -774,10 +922,10 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, goto unlock; } - for (i = 0; i < s->nb_reserved_regions; i++) { - ReservedRegion *reg = &s->reserved_regions[i]; + for (l = sdev->resv_regions; l; l = l->next) { + ReservedRegion *reg = l->data; - if (addr >= reg->low && addr <= reg->high) { + if (range_contains(®->range, addr)) { switch (reg->type) { case VIRTIO_IOMMU_RESV_MEM_T_MSI: entry.perm = flag; @@ -843,7 +991,7 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, trace_virtio_iommu_translate_out(addr, entry.translated_addr, sid); unlock: - qemu_mutex_unlock(&s->mutex); + qemu_rec_mutex_unlock(&s->mutex); return entry; } @@ -887,6 +1035,7 @@ static void virtio_iommu_set_config(VirtIODevice *vdev, return; } dev_config->bypass = in_config->bypass; + virtio_iommu_switch_address_space_all(dev); } trace_virtio_iommu_set_config(in_config->bypass); @@ -931,7 +1080,7 @@ static void virtio_iommu_replay(IOMMUMemoryRegion *mr, IOMMUNotifier *n) sid = virtio_iommu_get_bdf(sdev); - qemu_mutex_lock(&s->mutex); + qemu_rec_mutex_lock(&s->mutex); if (!s->endpoints) { goto unlock; @@ -945,7 +1094,7 @@ static void virtio_iommu_replay(IOMMUMemoryRegion *mr, IOMMUNotifier *n) g_tree_foreach(ep->domain->mappings, virtio_iommu_remap, mr); unlock: - qemu_mutex_unlock(&s->mutex); + qemu_rec_mutex_unlock(&s->mutex); } static int virtio_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu_mr, @@ -967,8 +1116,8 @@ static int virtio_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu_mr, } /* - * The default mask (TARGET_PAGE_MASK) is the smallest supported guest granule, - * for example 0xfffffffffffff000. When an assigned device has page size + * The default mask depends on the "granule" property. For example, with + * 4k granule, it is -(4 * KiB). When an assigned device has page size * restrictions due to the hardware IOMMU configuration, apply this restriction * to the mask. */ @@ -984,29 +1133,24 @@ static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr, new_mask); if ((cur_mask & new_mask) == 0) { - error_setg(errp, "virtio-iommu page mask 0x%"PRIx64 - " is incompatible with mask 0x%"PRIx64, cur_mask, new_mask); + error_setg(errp, "virtio-iommu %s reports a page size mask 0x%"PRIx64 + " incompatible with currently supported mask 0x%"PRIx64, + mr->parent_obj.name, new_mask, cur_mask); return -1; } /* - * After the machine is finalized, we can't change the mask anymore. If by + * Once the granule is frozen we can't change the mask anymore. If by * chance the hotplugged device supports the same granule, we can still - * accept it. Having a different masks is possible but the guest will use - * sub-optimal block sizes, so warn about it. + * accept it. */ - if (phase_check(PHASE_MACHINE_READY)) { - int new_granule = ctz64(new_mask); + if (s->granule_frozen) { int cur_granule = ctz64(cur_mask); - if (new_granule != cur_granule) { - error_setg(errp, "virtio-iommu page mask 0x%"PRIx64 - " is incompatible with mask 0x%"PRIx64, cur_mask, - new_mask); + if (!(BIT_ULL(cur_granule) & new_mask)) { + error_setg(errp, "virtio-iommu %s does not support frozen granule 0x%llx", + mr->parent_obj.name, BIT_ULL(cur_granule)); return -1; - } else if (new_mask != cur_mask) { - warn_report("virtio-iommu page mask 0x%"PRIx64 - " does not match 0x%"PRIx64, cur_mask, new_mask); } return 0; } @@ -1015,17 +1159,143 @@ static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr, return 0; } +/** + * rebuild_resv_regions: rebuild resv regions with both the + * info of host resv ranges and property set resv ranges + */ +static int rebuild_resv_regions(IOMMUDevice *sdev) +{ + GList *l; + int i = 0; + + /* free the existing list and rebuild it from scratch */ + g_list_free_full(sdev->resv_regions, g_free); + sdev->resv_regions = NULL; + + /* First add host reserved regions if any, all tagged as RESERVED */ + for (l = sdev->host_resv_ranges; l; l = l->next) { + ReservedRegion *reg = g_new0(ReservedRegion, 1); + Range *r = (Range *)l->data; + + reg->type = VIRTIO_IOMMU_RESV_MEM_T_RESERVED; + range_set_bounds(®->range, range_lob(r), range_upb(r)); + sdev->resv_regions = resv_region_list_insert(sdev->resv_regions, reg); + trace_virtio_iommu_host_resv_regions(sdev->iommu_mr.parent_obj.name, i, + range_lob(®->range), + range_upb(®->range)); + i++; + } + /* + * then add higher priority reserved regions set by the machine + * through properties + */ + add_prop_resv_regions(sdev); + return 0; +} + +/** + * virtio_iommu_set_iova_ranges: Conveys the usable IOVA ranges + * + * The function turns those into reserved ranges. Once some + * reserved ranges have been set, new reserved regions cannot be + * added outside of the original ones. + * + * @mr: IOMMU MR + * @iova_ranges: list of usable IOVA ranges + * @errp: error handle + */ +static int virtio_iommu_set_iova_ranges(IOMMUMemoryRegion *mr, + GList *iova_ranges, + Error **errp) +{ + IOMMUDevice *sdev = container_of(mr, IOMMUDevice, iommu_mr); + GList *current_ranges = sdev->host_resv_ranges; + GList *l, *tmp, *new_ranges = NULL; + int ret = -EINVAL; + + /* check that each new resv region is included in an existing one */ + if (sdev->host_resv_ranges) { + range_inverse_array(iova_ranges, + &new_ranges, + 0, UINT64_MAX); + + for (tmp = new_ranges; tmp; tmp = tmp->next) { + Range *newr = (Range *)tmp->data; + bool included = false; + + for (l = current_ranges; l; l = l->next) { + Range * r = (Range *)l->data; + + if (range_contains_range(r, newr)) { + included = true; + break; + } + } + if (!included) { + goto error; + } + } + /* all new reserved ranges are included in existing ones */ + ret = 0; + goto out; + } + + if (sdev->probe_done) { + warn_report("%s: Notified about new host reserved regions after probe", + mr->parent_obj.name); + } + + range_inverse_array(iova_ranges, + &sdev->host_resv_ranges, + 0, UINT64_MAX); + rebuild_resv_regions(sdev); + + return 0; +error: + error_setg(errp, "IOMMU mr=%s Conflicting host reserved ranges set!", + mr->parent_obj.name); +out: + g_list_free_full(new_ranges, g_free); + return ret; +} + static void virtio_iommu_system_reset(void *opaque) { VirtIOIOMMU *s = opaque; trace_virtio_iommu_system_reset(); + memset(s->iommu_pcibus_by_bus_num, 0, sizeof(s->iommu_pcibus_by_bus_num)); + /* * config.bypass is sticky across device reset, but should be restored on * system reset */ s->config.bypass = s->boot_bypass; + virtio_iommu_switch_address_space_all(s); + +} + +static void virtio_iommu_freeze_granule(Notifier *notifier, void *data) +{ + VirtIOIOMMU *s = container_of(notifier, VirtIOIOMMU, machine_done); + int granule; + + if (likely(s->config.bypass)) { + /* + * Transient IOMMU MR enable to collect page_size_mask requirements + * through memory_region_iommu_set_page_size_mask() called by + * VFIO region_add() callback + */ + s->config.bypass = false; + virtio_iommu_switch_address_space_all(s); + /* restore default */ + s->config.bypass = true; + virtio_iommu_switch_address_space_all(s); + } + s->granule_frozen = true; + granule = ctz64(s->config.page_size_mask); + trace_virtio_iommu_freeze_granule(BIT_ULL(granule)); } static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) @@ -1035,14 +1305,41 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) virtio_init(vdev, 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 = UINT64_MAX; + /* + * config.bypass is needed to get initial address space early, such as + * in vfio realize + */ + s->config.bypass = s->boot_bypass; + if (s->aw_bits < 32 || s->aw_bits > 64) { + error_setg(errp, "aw-bits must be within [32,64]"); + return; + } + s->config.input_range.end = + s->aw_bits == 64 ? UINT64_MAX : BIT_ULL(s->aw_bits) - 1; + + switch (s->granule_mode) { + case GRANULE_MODE_4K: + s->config.page_size_mask = -(4 * KiB); + break; + case GRANULE_MODE_8K: + s->config.page_size_mask = -(8 * KiB); + break; + case GRANULE_MODE_16K: + s->config.page_size_mask = -(16 * KiB); + break; + case GRANULE_MODE_64K: + s->config.page_size_mask = -(64 * KiB); + break; + case GRANULE_MODE_HOST: + s->config.page_size_mask = qemu_real_host_page_mask(); + break; + default: + error_setg(errp, "Unsupported granule mode"); + } s->config.domain_range.end = UINT32_MAX; s->config.probe_size = VIOMMU_PROBE_SIZE; @@ -1056,16 +1353,19 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) virtio_add_feature(&s->features, VIRTIO_IOMMU_F_PROBE); virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS_CONFIG); - qemu_mutex_init(&s->mutex); + qemu_rec_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); + pci_setup_iommu(s->primary_bus, &virtio_iommu_ops, s); } else { error_setg(errp, "VIRTIO-IOMMU is not attached to any PCI bus!"); } + s->machine_done.notify = virtio_iommu_freeze_granule; + qemu_add_machine_init_done_notifier(&s->machine_done); + qemu_register_reset(virtio_iommu_system_reset, s); } @@ -1075,6 +1375,7 @@ static void virtio_iommu_device_unrealize(DeviceState *dev) VirtIOIOMMU *s = VIRTIO_IOMMU(dev); qemu_unregister_reset(virtio_iommu_system_reset, s); + qemu_remove_machine_init_done_notifier(&s->machine_done); g_hash_table_destroy(s->as_by_busptr); if (s->domains) { @@ -1084,6 +1385,8 @@ static void virtio_iommu_device_unrealize(DeviceState *dev) g_tree_destroy(s->endpoints); } + qemu_rec_mutex_destroy(&s->mutex); + virtio_delete_queue(s->req_vq); virtio_delete_queue(s->event_vq); virtio_cleanup(vdev); @@ -1121,7 +1424,7 @@ static void virtio_iommu_instance_init(Object *obj) .name = "interval", \ .version_id = 1, \ .minimum_version_id = 1, \ - .fields = (VMStateField[]) { \ + .fields = (const VMStateField[]) { \ VMSTATE_UINT64(low, VirtIOIOMMUInterval), \ VMSTATE_UINT64(high, VirtIOIOMMUInterval), \ VMSTATE_END_OF_LIST() \ @@ -1133,7 +1436,7 @@ static void virtio_iommu_instance_init(Object *obj) .name = "mapping", \ .version_id = 1, \ .minimum_version_id = 1, \ - .fields = (VMStateField[]) { \ + .fields = (const VMStateField[]) { \ VMSTATE_UINT64(phys_addr, VirtIOIOMMUMapping),\ VMSTATE_UINT32(flags, VirtIOIOMMUMapping), \ VMSTATE_END_OF_LIST() \ @@ -1158,7 +1461,7 @@ static const VMStateDescription vmstate_endpoint = { .name = "endpoint", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(id, VirtIOIOMMUEndpoint), VMSTATE_END_OF_LIST() } @@ -1169,7 +1472,7 @@ static const VMStateDescription vmstate_domain = { .version_id = 2, .minimum_version_id = 2, .pre_load = domain_preload, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(id, VirtIOIOMMUDomain), VMSTATE_GTREE_V(mappings, VirtIOIOMMUDomain, 1, vmstate_interval_mapping, @@ -1205,6 +1508,14 @@ static int iommu_post_load(void *opaque, int version_id) VirtIOIOMMU *s = opaque; g_tree_foreach(s->domains, reconstruct_endpoints, s); + + /* + * Memory regions are dynamically turned on/off depending on + * 'config.bypass' and attached domain type if there is. After + * migration, we need to make sure the memory regions are + * still correct. + */ + virtio_iommu_switch_address_space_all(s); return 0; } @@ -1213,7 +1524,7 @@ static const VMStateDescription vmstate_virtio_iommu_device = { .minimum_version_id = 2, .version_id = 2, .post_load = iommu_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_GTREE_DIRECT_KEY_V(domains, VirtIOIOMMU, 2, &vmstate_domain, VirtIOIOMMUDomain), VMSTATE_UINT8_V(config.bypass, VirtIOIOMMU, 2), @@ -1226,15 +1537,19 @@ static const VMStateDescription vmstate_virtio_iommu = { .minimum_version_id = 2, .priority = MIG_PRI_IOMMU, .version_id = 2, - .fields = (VMStateField[]) { + .fields = (const 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_LINK("primary-bus", VirtIOIOMMU, primary_bus, + TYPE_PCI_BUS, PCIBus *), DEFINE_PROP_BOOL("boot-bypass", VirtIOIOMMU, boot_bypass, true), + DEFINE_PROP_GRANULE_MODE("granule", VirtIOIOMMU, granule_mode, + GRANULE_MODE_HOST), + DEFINE_PROP_UINT8("aw-bits", VirtIOIOMMU, aw_bits, 64), DEFINE_PROP_END_OF_LIST(), }; @@ -1266,6 +1581,7 @@ static void virtio_iommu_memory_region_class_init(ObjectClass *klass, imrc->replay = virtio_iommu_replay; imrc->notify_flag_changed = virtio_iommu_notify_flag_changed; imrc->iommu_set_page_size_mask = virtio_iommu_set_page_size_mask; + imrc->iommu_set_iova_ranges = virtio_iommu_set_iova_ranges; } static const TypeInfo virtio_iommu_info = { diff --git a/hw/virtio/virtio-md-pci.c b/hw/virtio/virtio-md-pci.c new file mode 100644 index 0000000000..62bfb7920b --- /dev/null +++ b/hw/virtio/virtio-md-pci.c @@ -0,0 +1,151 @@ +/* + * Abstract virtio based memory device + * + * Copyright (C) 2023 Red Hat, Inc. + * + * Authors: + * David Hildenbrand + * + * 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 "hw/virtio/virtio-md-pci.h" +#include "hw/mem/memory-device.h" +#include "qapi/error.h" +#include "qemu/error-report.h" + +void virtio_md_pci_pre_plug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp) +{ + DeviceState *dev = DEVICE(vmd); + HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + MemoryDeviceState *md = MEMORY_DEVICE(vmd); + Error *local_err = NULL; + + if (!bus_handler && dev->hotplugged) { + /* + * Without a bus hotplug handler, we cannot control the plug/unplug + * order. We should never reach this point when hotplugging on x86, + * however, better add a safety net. + */ + error_setg(errp, "hotplug of virtio based memory devices not supported" + " on this bus."); + return; + } + /* + * First, see if we can plug this memory device at all. If that + * succeeds, branch of to the actual hotplug handler. + */ + memory_device_pre_plug(md, ms, NULL, &local_err); + if (!local_err && bus_handler) { + hotplug_handler_pre_plug(bus_handler, dev, &local_err); + } + error_propagate(errp, local_err); +} + +void virtio_md_pci_plug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp) +{ + DeviceState *dev = DEVICE(vmd); + HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + MemoryDeviceState *md = MEMORY_DEVICE(vmd); + Error *local_err = NULL; + + /* + * Plug the memory device first and then branch off to the actual + * hotplug handler. If that one fails, we can easily undo the memory + * device bits. + */ + memory_device_plug(md, ms); + if (bus_handler) { + hotplug_handler_plug(bus_handler, dev, &local_err); + if (local_err) { + memory_device_unplug(md, ms); + } + } + error_propagate(errp, local_err); +} + +void virtio_md_pci_unplug_request(VirtIOMDPCI *vmd, MachineState *ms, + Error **errp) +{ + VirtIOMDPCIClass *vmdc = VIRTIO_MD_PCI_GET_CLASS(vmd); + DeviceState *dev = DEVICE(vmd); + HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + HotplugHandlerClass *hdc; + Error *local_err = NULL; + + if (!vmdc->unplug_request_check) { + error_setg(errp, "this virtio based memory devices cannot be unplugged"); + return; + } + + if (!bus_handler) { + error_setg(errp, "hotunplug of virtio based memory devices not" + "supported on this bus"); + return; + } + + vmdc->unplug_request_check(vmd, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* + * Forward the async request or turn it into a sync request (handling it + * like qdev_unplug()). + */ + hdc = HOTPLUG_HANDLER_GET_CLASS(bus_handler); + if (hdc->unplug_request) { + hotplug_handler_unplug_request(bus_handler, dev, &local_err); + } else { + virtio_md_pci_unplug(vmd, ms, &local_err); + if (!local_err) { + object_unparent(OBJECT(dev)); + } + } +} + +void virtio_md_pci_unplug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp) +{ + DeviceState *dev = DEVICE(vmd); + HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); + MemoryDeviceState *md = MEMORY_DEVICE(vmd); + Error *local_err = NULL; + + /* Unplug the memory device while it is still realized. */ + memory_device_unplug(md, ms); + + if (bus_handler) { + hotplug_handler_unplug(bus_handler, dev, &local_err); + if (local_err) { + /* Not expected to fail ... but still try to recover. */ + memory_device_plug(md, ms); + error_propagate(errp, local_err); + return; + } + } else { + /* Very unexpected, but let's just try to do the right thing. */ + warn_report("Unexpected unplug of virtio based memory device"); + qdev_unrealize(dev); + } +} + +static const TypeInfo virtio_md_pci_info = { + .name = TYPE_VIRTIO_MD_PCI, + .parent = TYPE_VIRTIO_PCI, + .instance_size = sizeof(VirtIOMDPCI), + .class_size = sizeof(VirtIOMDPCIClass), + .abstract = true, + .interfaces = (InterfaceInfo[]) { + { TYPE_MEMORY_DEVICE }, + { } + }, +}; + +static void virtio_md_pci_register(void) +{ + type_register_static(&virtio_md_pci_info); +} +type_init(virtio_md_pci_register) diff --git a/hw/virtio/virtio-mem-pci.c b/hw/virtio/virtio-mem-pci.c index be2383b0c5..1b4e9a3284 100644 --- a/hw/virtio/virtio-mem-pci.c +++ b/hw/virtio/virtio-mem-pci.c @@ -42,12 +42,31 @@ static MemoryRegion *virtio_mem_pci_get_memory_region(MemoryDeviceState *md, Error **errp) { VirtIOMEMPCI *pci_mem = VIRTIO_MEM_PCI(md); - VirtIOMEM *vmem = VIRTIO_MEM(&pci_mem->vdev); + VirtIOMEM *vmem = &pci_mem->vdev; VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem); return vmc->get_memory_region(vmem, errp); } +static void virtio_mem_pci_decide_memslots(MemoryDeviceState *md, + unsigned int limit) +{ + VirtIOMEMPCI *pci_mem = VIRTIO_MEM_PCI(md); + VirtIOMEM *vmem = VIRTIO_MEM(&pci_mem->vdev); + VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem); + + vmc->decide_memslots(vmem, limit); +} + +static unsigned int virtio_mem_pci_get_memslots(MemoryDeviceState *md) +{ + VirtIOMEMPCI *pci_mem = VIRTIO_MEM_PCI(md); + VirtIOMEM *vmem = VIRTIO_MEM(&pci_mem->vdev); + VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem); + + return vmc->get_memslots(vmem); +} + static uint64_t virtio_mem_pci_get_plugged_size(const MemoryDeviceState *md, Error **errp) { @@ -60,12 +79,11 @@ static void virtio_mem_pci_fill_device_info(const MemoryDeviceState *md, { VirtioMEMDeviceInfo *vi = g_new0(VirtioMEMDeviceInfo, 1); VirtIOMEMPCI *pci_mem = VIRTIO_MEM_PCI(md); - VirtIOMEM *vmem = VIRTIO_MEM(&pci_mem->vdev); + VirtIOMEM *vmem = &pci_mem->vdev; VirtIOMEMClass *vpc = VIRTIO_MEM_GET_CLASS(vmem); DeviceState *dev = DEVICE(md); if (dev->id) { - vi->has_id = true; vi->id = g_strdup(dev->id); } @@ -90,22 +108,60 @@ static void virtio_mem_pci_size_change_notify(Notifier *notifier, void *data) char *qom_path = object_get_canonical_path(OBJECT(dev)); const uint64_t * const size_p = data; - qapi_event_send_memory_device_size_change(!!dev->id, dev->id, *size_p, - qom_path); + qapi_event_send_memory_device_size_change(dev->id, *size_p, qom_path); g_free(qom_path); } +static void virtio_mem_pci_unplug_request_check(VirtIOMDPCI *vmd, Error **errp) +{ + VirtIOMEMPCI *pci_mem = VIRTIO_MEM_PCI(vmd); + VirtIOMEM *vmem = &pci_mem->vdev; + VirtIOMEMClass *vpc = VIRTIO_MEM_GET_CLASS(vmem); + + vpc->unplug_request_check(vmem, errp); +} + +static void virtio_mem_pci_get_requested_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + VirtIOMEMPCI *pci_mem = VIRTIO_MEM_PCI(obj); + + object_property_get(OBJECT(&pci_mem->vdev), name, v, errp); +} + +static void virtio_mem_pci_set_requested_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + VirtIOMEMPCI *pci_mem = VIRTIO_MEM_PCI(obj); + DeviceState *dev = DEVICE(obj); + + /* + * If we passed virtio_mem_pci_unplug_request_check(), making sure that + * the requested size is 0, don't allow modifying the requested size + * anymore, otherwise the VM might end up hotplugging memory before + * handling the unplug request. + */ + if (dev->pending_deleted_event) { + error_setg(errp, "'%s' cannot be changed if the device is in the" + " process of unplug", name); + return; + } + + object_property_set(OBJECT(&pci_mem->vdev), name, v, errp); +} + static void virtio_mem_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); MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(klass); + VirtIOMDPCIClass *vmdc = VIRTIO_MD_PCI_CLASS(klass); k->realize = virtio_mem_pci_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_MEM; pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; pcidev_k->class_id = PCI_CLASS_OTHERS; @@ -113,8 +169,12 @@ static void virtio_mem_pci_class_init(ObjectClass *klass, void *data) mdc->set_addr = virtio_mem_pci_set_addr; mdc->get_plugged_size = virtio_mem_pci_get_plugged_size; mdc->get_memory_region = virtio_mem_pci_get_memory_region; + mdc->decide_memslots = virtio_mem_pci_decide_memslots; + mdc->get_memslots = virtio_mem_pci_get_memslots; mdc->fill_device_info = virtio_mem_pci_fill_device_info; mdc->get_min_alignment = virtio_mem_pci_get_min_alignment; + + vmdc->unplug_request_check = virtio_mem_pci_unplug_request_check; } static void virtio_mem_pci_instance_init(Object *obj) @@ -127,7 +187,7 @@ static void virtio_mem_pci_instance_init(Object *obj) TYPE_VIRTIO_MEM); dev->size_change_notifier.notify = virtio_mem_pci_size_change_notify; - vmem = VIRTIO_MEM(&dev->vdev); + vmem = &dev->vdev; vmc = VIRTIO_MEM_GET_CLASS(vmem); /* * We never remove the notifier again, as we expect both devices to @@ -139,21 +199,18 @@ static void virtio_mem_pci_instance_init(Object *obj) OBJECT(&dev->vdev), VIRTIO_MEM_BLOCK_SIZE_PROP); object_property_add_alias(obj, VIRTIO_MEM_SIZE_PROP, OBJECT(&dev->vdev), VIRTIO_MEM_SIZE_PROP); - object_property_add_alias(obj, VIRTIO_MEM_REQUESTED_SIZE_PROP, - OBJECT(&dev->vdev), - VIRTIO_MEM_REQUESTED_SIZE_PROP); + object_property_add(obj, VIRTIO_MEM_REQUESTED_SIZE_PROP, "size", + virtio_mem_pci_get_requested_size, + virtio_mem_pci_set_requested_size, NULL, NULL); } static const VirtioPCIDeviceTypeInfo virtio_mem_pci_info = { .base_name = TYPE_VIRTIO_MEM_PCI, + .parent = TYPE_VIRTIO_MD_PCI, .generic_name = "virtio-mem-pci", .instance_size = sizeof(VirtIOMEMPCI), .instance_init = virtio_mem_pci_instance_init, .class_init = virtio_mem_pci_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_MEMORY_DEVICE }, - { } - }, }; static void virtio_mem_pci_register_types(void) diff --git a/hw/virtio/virtio-mem-pci.h b/hw/virtio/virtio-mem-pci.h index e636e1a48d..c50b51d608 100644 --- a/hw/virtio/virtio-mem-pci.h +++ b/hw/virtio/virtio-mem-pci.h @@ -13,21 +13,21 @@ #ifndef QEMU_VIRTIO_MEM_PCI_H #define QEMU_VIRTIO_MEM_PCI_H -#include "hw/virtio/virtio-pci.h" +#include "hw/virtio/virtio-md-pci.h" #include "hw/virtio/virtio-mem.h" #include "qom/object.h" typedef struct VirtIOMEMPCI VirtIOMEMPCI; /* - * virtio-mem-pci: This extends VirtioPCIProxy. + * virtio-mem-pci: This extends VirtIOMDPCI. */ #define TYPE_VIRTIO_MEM_PCI "virtio-mem-pci-base" DECLARE_INSTANCE_CHECKER(VirtIOMEMPCI, VIRTIO_MEM_PCI, TYPE_VIRTIO_MEM_PCI) struct VirtIOMEMPCI { - VirtIOPCIProxy parent_obj; + VirtIOMDPCI parent_obj; VirtIOMEM vdev; Notifier size_change_notifier; }; diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 30d03e987a..ffd119ebac 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -18,9 +18,9 @@ #include "sysemu/numa.h" #include "sysemu/sysemu.h" #include "sysemu/reset.h" +#include "sysemu/runstate.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-access.h" #include "hw/virtio/virtio-mem.h" #include "qapi/error.h" #include "qapi/visitor.h" @@ -31,6 +31,8 @@ #include CONFIG_DEVICES #include "trace.h" +static const VMStateDescription vmstate_virtio_mem_device_early; + /* * We only had legacy x86 guests that did not support * VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE. Other targets don't have legacy guests. @@ -64,6 +66,13 @@ static uint32_t virtio_mem_default_thp_size(void) return default_thp_size; } +/* + * The minimum memslot size depends on this setting ("sane default"), the + * device block size, and the memory backend page size. The last (or single) + * memslot might be smaller than this constant. + */ +#define VIRTIO_MEM_MIN_MEMSLOT_SIZE (1 * GiB) + /* * We want to have a reasonable default block size such that * 1. We avoid splitting THPs when unplugging memory, which degrades @@ -133,7 +142,7 @@ static bool virtio_mem_has_shared_zeropage(RAMBlock *rb) * anonymous RAM. In any other case, reading unplugged *can* populate a * fresh page, consuming actual memory. */ - return !qemu_ram_is_shared(rb) && rb->fd < 0 && + return !qemu_ram_is_shared(rb) && qemu_ram_get_fd(rb) < 0 && qemu_ram_pagesize(rb) == qemu_real_host_page_size(); } #endif /* VIRTIO_MEM_HAS_LEGACY_GUESTS */ @@ -175,10 +184,10 @@ static bool virtio_mem_is_busy(void) return migration_in_incoming_postcopy() || !migration_is_idle(); } -typedef int (*virtio_mem_range_cb)(const VirtIOMEM *vmem, void *arg, +typedef int (*virtio_mem_range_cb)(VirtIOMEM *vmem, void *arg, uint64_t offset, uint64_t size); -static int virtio_mem_for_each_unplugged_range(const VirtIOMEM *vmem, void *arg, +static int virtio_mem_for_each_unplugged_range(VirtIOMEM *vmem, void *arg, virtio_mem_range_cb cb) { unsigned long first_zero_bit, last_zero_bit; @@ -202,12 +211,36 @@ static int virtio_mem_for_each_unplugged_range(const VirtIOMEM *vmem, void *arg, return ret; } +static int virtio_mem_for_each_plugged_range(VirtIOMEM *vmem, void *arg, + virtio_mem_range_cb cb) +{ + unsigned long first_bit, last_bit; + uint64_t offset, size; + int ret = 0; + + first_bit = find_first_bit(vmem->bitmap, vmem->bitmap_size); + while (first_bit < vmem->bitmap_size) { + offset = first_bit * vmem->block_size; + last_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size, + first_bit + 1) - 1; + size = (last_bit - first_bit + 1) * vmem->block_size; + + ret = cb(vmem, arg, offset, size); + if (ret) { + break; + } + first_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size, + last_bit + 2); + } + return ret; +} + /* * Adjust the memory section to cover the intersection with the given range. * * Returns false if the intersection is empty, otherwise returns true. */ -static bool virito_mem_intersect_memory_section(MemoryRegionSection *s, +static bool virtio_mem_intersect_memory_section(MemoryRegionSection *s, uint64_t offset, uint64_t size) { uint64_t start = MAX(s->offset_within_region, offset); @@ -235,7 +268,7 @@ static int virtio_mem_for_each_plugged_section(const VirtIOMEM *vmem, uint64_t offset, size; int ret = 0; - first_bit = s->offset_within_region / vmem->bitmap_size; + first_bit = s->offset_within_region / vmem->block_size; first_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size, first_bit); while (first_bit < vmem->bitmap_size) { MemoryRegionSection tmp = *s; @@ -245,7 +278,7 @@ static int virtio_mem_for_each_plugged_section(const VirtIOMEM *vmem, first_bit + 1) - 1; size = (last_bit - first_bit + 1) * vmem->block_size; - if (!virito_mem_intersect_memory_section(&tmp, offset, size)) { + if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { break; } ret = cb(&tmp, arg); @@ -267,7 +300,7 @@ static int virtio_mem_for_each_unplugged_section(const VirtIOMEM *vmem, uint64_t offset, size; int ret = 0; - first_bit = s->offset_within_region / vmem->bitmap_size; + first_bit = s->offset_within_region / vmem->block_size; first_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size, first_bit); while (first_bit < vmem->bitmap_size) { MemoryRegionSection tmp = *s; @@ -277,7 +310,7 @@ static int virtio_mem_for_each_unplugged_section(const VirtIOMEM *vmem, first_bit + 1) - 1; size = (last_bit - first_bit + 1) * vmem->block_size; - if (!virito_mem_intersect_memory_section(&tmp, offset, size)) { + if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { break; } ret = cb(&tmp, arg); @@ -313,7 +346,7 @@ static void virtio_mem_notify_unplug(VirtIOMEM *vmem, uint64_t offset, QLIST_FOREACH(rdl, &vmem->rdl_list, next) { MemoryRegionSection tmp = *rdl->section; - if (!virito_mem_intersect_memory_section(&tmp, offset, size)) { + if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { continue; } rdl->notify_discard(rdl, &tmp); @@ -329,7 +362,7 @@ static int virtio_mem_notify_plug(VirtIOMEM *vmem, uint64_t offset, QLIST_FOREACH(rdl, &vmem->rdl_list, next) { MemoryRegionSection tmp = *rdl->section; - if (!virito_mem_intersect_memory_section(&tmp, offset, size)) { + if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { continue; } ret = rdl->notify_populate(rdl, &tmp); @@ -341,12 +374,12 @@ static int virtio_mem_notify_plug(VirtIOMEM *vmem, uint64_t offset, if (ret) { /* Notify all already-notified listeners. */ QLIST_FOREACH(rdl2, &vmem->rdl_list, next) { - MemoryRegionSection tmp = *rdl->section; + MemoryRegionSection tmp = *rdl2->section; if (rdl2 == rdl) { break; } - if (!virito_mem_intersect_memory_section(&tmp, offset, size)) { + if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { continue; } rdl2->notify_discard(rdl2, &tmp); @@ -373,33 +406,46 @@ static void virtio_mem_notify_unplug_all(VirtIOMEM *vmem) } } -static bool virtio_mem_test_bitmap(const VirtIOMEM *vmem, uint64_t start_gpa, - uint64_t size, bool plugged) +static bool virtio_mem_is_range_plugged(const VirtIOMEM *vmem, + uint64_t start_gpa, uint64_t size) { const unsigned long first_bit = (start_gpa - vmem->addr) / vmem->block_size; const unsigned long last_bit = first_bit + (size / vmem->block_size) - 1; unsigned long found_bit; /* We fake a shorter bitmap to avoid searching too far. */ - if (plugged) { - found_bit = find_next_zero_bit(vmem->bitmap, last_bit + 1, first_bit); - } else { - found_bit = find_next_bit(vmem->bitmap, last_bit + 1, first_bit); - } + found_bit = find_next_zero_bit(vmem->bitmap, last_bit + 1, first_bit); return found_bit > last_bit; } -static void virtio_mem_set_bitmap(VirtIOMEM *vmem, uint64_t start_gpa, - uint64_t size, bool plugged) +static bool virtio_mem_is_range_unplugged(const VirtIOMEM *vmem, + uint64_t start_gpa, uint64_t size) +{ + const unsigned long first_bit = (start_gpa - vmem->addr) / vmem->block_size; + const unsigned long last_bit = first_bit + (size / vmem->block_size) - 1; + unsigned long found_bit; + + /* We fake a shorter bitmap to avoid searching too far. */ + found_bit = find_next_bit(vmem->bitmap, last_bit + 1, first_bit); + return found_bit > last_bit; +} + +static void virtio_mem_set_range_plugged(VirtIOMEM *vmem, uint64_t start_gpa, + uint64_t size) { const unsigned long bit = (start_gpa - vmem->addr) / vmem->block_size; const unsigned long nbits = size / vmem->block_size; - if (plugged) { - bitmap_set(vmem->bitmap, bit, nbits); - } else { - bitmap_clear(vmem->bitmap, bit, nbits); - } + bitmap_set(vmem->bitmap, bit, nbits); +} + +static void virtio_mem_set_range_unplugged(VirtIOMEM *vmem, uint64_t start_gpa, + uint64_t size) +{ + const unsigned long bit = (start_gpa - vmem->addr) / vmem->block_size; + const unsigned long nbits = size / vmem->block_size; + + bitmap_clear(vmem->bitmap, bit, nbits); } static void virtio_mem_send_response(VirtIOMEM *vmem, VirtQueueElement *elem, @@ -444,11 +490,98 @@ static bool virtio_mem_valid_range(const VirtIOMEM *vmem, uint64_t gpa, return true; } +static void virtio_mem_activate_memslot(VirtIOMEM *vmem, unsigned int idx) +{ + const uint64_t memslot_offset = idx * vmem->memslot_size; + + assert(vmem->memslots); + + /* + * Instead of enabling/disabling memslots, we add/remove them. This should + * make address space updates faster, because we don't have to loop over + * many disabled subregions. + */ + if (memory_region_is_mapped(&vmem->memslots[idx])) { + return; + } + memory_region_add_subregion(vmem->mr, memslot_offset, &vmem->memslots[idx]); +} + +static void virtio_mem_deactivate_memslot(VirtIOMEM *vmem, unsigned int idx) +{ + assert(vmem->memslots); + + if (!memory_region_is_mapped(&vmem->memslots[idx])) { + return; + } + memory_region_del_subregion(vmem->mr, &vmem->memslots[idx]); +} + +static void virtio_mem_activate_memslots_to_plug(VirtIOMEM *vmem, + uint64_t offset, uint64_t size) +{ + const unsigned int start_idx = offset / vmem->memslot_size; + const unsigned int end_idx = (offset + size + vmem->memslot_size - 1) / + vmem->memslot_size; + unsigned int idx; + + assert(vmem->dynamic_memslots); + + /* Activate all involved memslots in a single transaction. */ + memory_region_transaction_begin(); + for (idx = start_idx; idx < end_idx; idx++) { + virtio_mem_activate_memslot(vmem, idx); + } + memory_region_transaction_commit(); +} + +static void virtio_mem_deactivate_unplugged_memslots(VirtIOMEM *vmem, + uint64_t offset, + uint64_t size) +{ + const uint64_t region_size = memory_region_size(&vmem->memdev->mr); + const unsigned int start_idx = offset / vmem->memslot_size; + const unsigned int end_idx = (offset + size + vmem->memslot_size - 1) / + vmem->memslot_size; + unsigned int idx; + + assert(vmem->dynamic_memslots); + + /* Deactivate all memslots with unplugged blocks in a single transaction. */ + memory_region_transaction_begin(); + for (idx = start_idx; idx < end_idx; idx++) { + const uint64_t memslot_offset = idx * vmem->memslot_size; + uint64_t memslot_size = vmem->memslot_size; + + /* The size of the last memslot might be smaller. */ + if (idx == vmem->nb_memslots - 1) { + memslot_size = region_size - memslot_offset; + } + + /* + * Partially covered memslots might still have some blocks plugged and + * have to remain active if that's the case. + */ + if (offset > memslot_offset || + offset + size < memslot_offset + memslot_size) { + const uint64_t gpa = vmem->addr + memslot_offset; + + if (!virtio_mem_is_range_unplugged(vmem, gpa, memslot_size)) { + continue; + } + } + + virtio_mem_deactivate_memslot(vmem, idx); + } + memory_region_transaction_commit(); +} + static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa, uint64_t size, bool plug) { const uint64_t offset = start_gpa - vmem->addr; RAMBlock *rb = vmem->memdev->mr.ram_block; + int ret = 0; if (virtio_mem_is_busy()) { return -EBUSY; @@ -459,42 +592,61 @@ static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa, return -EBUSY; } virtio_mem_notify_unplug(vmem, offset, size); - } else { - int ret = 0; + virtio_mem_set_range_unplugged(vmem, start_gpa, size); + /* Deactivate completely unplugged memslots after updating the state. */ + if (vmem->dynamic_memslots) { + virtio_mem_deactivate_unplugged_memslots(vmem, offset, size); + } + return 0; + } - if (vmem->prealloc) { - void *area = memory_region_get_ram_ptr(&vmem->memdev->mr) + offset; - int fd = memory_region_get_fd(&vmem->memdev->mr); - Error *local_err = NULL; + if (vmem->prealloc) { + void *area = memory_region_get_ram_ptr(&vmem->memdev->mr) + offset; + int fd = memory_region_get_fd(&vmem->memdev->mr); + Error *local_err = NULL; - os_mem_prealloc(fd, area, size, 1, &local_err); - if (local_err) { - static bool warned; + if (!qemu_prealloc_mem(fd, area, size, 1, NULL, false, &local_err)) { + static bool warned; - /* - * Warn only once, we don't want to fill the log with these - * warnings. - */ - if (!warned) { - warn_report_err(local_err); - warned = true; - } else { - error_free(local_err); - } - ret = -EBUSY; + /* + * Warn only once, we don't want to fill the log with these + * warnings. + */ + if (!warned) { + warn_report_err(local_err); + warned = true; + } else { + error_free(local_err); } - } - if (!ret) { - ret = virtio_mem_notify_plug(vmem, offset, size); - } - - if (ret) { - /* Could be preallocation or a notifier populated memory. */ - ram_block_discard_range(vmem->memdev->mr.ram_block, offset, size); - return -EBUSY; + ret = -EBUSY; } } - virtio_mem_set_bitmap(vmem, start_gpa, size, plug); + + if (!ret) { + /* + * Activate before notifying and rollback in case of any errors. + * + * When activating a yet inactive memslot, memory notifiers will get + * notified about the added memory region and can register with the + * RamDiscardManager; this will traverse all plugged blocks and skip the + * blocks we are plugging here. The following notification will inform + * registered listeners about the blocks we're plugging. + */ + if (vmem->dynamic_memslots) { + virtio_mem_activate_memslots_to_plug(vmem, offset, size); + } + ret = virtio_mem_notify_plug(vmem, offset, size); + if (ret && vmem->dynamic_memslots) { + virtio_mem_deactivate_unplugged_memslots(vmem, offset, size); + } + } + if (ret) { + /* Could be preallocation or a notifier populated memory. */ + ram_block_discard_range(vmem->memdev->mr.ram_block, offset, size); + return -EBUSY; + } + + virtio_mem_set_range_plugged(vmem, start_gpa, size); return 0; } @@ -513,7 +665,8 @@ static int virtio_mem_state_change_request(VirtIOMEM *vmem, uint64_t gpa, } /* test if really all blocks are in the opposite state */ - if (!virtio_mem_test_bitmap(vmem, gpa, size, !plug)) { + if ((plug && !virtio_mem_is_range_unplugged(vmem, gpa, size)) || + (!plug && !virtio_mem_is_range_plugged(vmem, gpa, size))) { return VIRTIO_MEM_RESP_ERROR; } @@ -578,22 +731,28 @@ static void virtio_mem_resize_usable_region(VirtIOMEM *vmem, static int virtio_mem_unplug_all(VirtIOMEM *vmem) { + const uint64_t region_size = memory_region_size(&vmem->memdev->mr); RAMBlock *rb = vmem->memdev->mr.ram_block; - if (virtio_mem_is_busy()) { - return -EBUSY; - } - - if (ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb))) { - return -EBUSY; - } - virtio_mem_notify_unplug_all(vmem); - - bitmap_clear(vmem->bitmap, 0, vmem->bitmap_size); if (vmem->size) { + if (virtio_mem_is_busy()) { + return -EBUSY; + } + if (ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb))) { + return -EBUSY; + } + virtio_mem_notify_unplug_all(vmem); + + bitmap_clear(vmem->bitmap, 0, vmem->bitmap_size); vmem->size = 0; notifier_list_notify(&vmem->size_change_notifiers, &vmem->size); + + /* Deactivate all memslots after updating the state. */ + if (vmem->dynamic_memslots) { + virtio_mem_deactivate_unplugged_memslots(vmem, 0, region_size); + } } + trace_virtio_mem_unplugged_all(); virtio_mem_resize_usable_region(vmem, vmem->requested_size, true); return 0; @@ -626,9 +785,9 @@ static void virtio_mem_state_request(VirtIOMEM *vmem, VirtQueueElement *elem, return; } - if (virtio_mem_test_bitmap(vmem, gpa, size, true)) { + if (virtio_mem_is_range_plugged(vmem, gpa, size)) { resp.u.state.state = cpu_to_le16(VIRTIO_MEM_STATE_PLUGGED); - } else if (virtio_mem_test_bitmap(vmem, gpa, size, false)) { + } else if (virtio_mem_is_range_unplugged(vmem, gpa, size)) { resp.u.state.state = cpu_to_le16(VIRTIO_MEM_STATE_UNPLUGGED); } else { resp.u.state.state = cpu_to_le16(VIRTIO_MEM_STATE_MIXED); @@ -748,6 +907,49 @@ static void virtio_mem_system_reset(void *opaque) virtio_mem_unplug_all(vmem); } +static void virtio_mem_prepare_mr(VirtIOMEM *vmem) +{ + const uint64_t region_size = memory_region_size(&vmem->memdev->mr); + + assert(!vmem->mr && vmem->dynamic_memslots); + vmem->mr = g_new0(MemoryRegion, 1); + memory_region_init(vmem->mr, OBJECT(vmem), "virtio-mem", + region_size); + vmem->mr->align = memory_region_get_alignment(&vmem->memdev->mr); +} + +static void virtio_mem_prepare_memslots(VirtIOMEM *vmem) +{ + const uint64_t region_size = memory_region_size(&vmem->memdev->mr); + unsigned int idx; + + g_assert(!vmem->memslots && vmem->nb_memslots && vmem->dynamic_memslots); + vmem->memslots = g_new0(MemoryRegion, vmem->nb_memslots); + + /* Initialize our memslots, but don't map them yet. */ + for (idx = 0; idx < vmem->nb_memslots; idx++) { + const uint64_t memslot_offset = idx * vmem->memslot_size; + uint64_t memslot_size = vmem->memslot_size; + char name[20]; + + /* The size of the last memslot might be smaller. */ + if (idx == vmem->nb_memslots - 1) { + memslot_size = region_size - memslot_offset; + } + + snprintf(name, sizeof(name), "memslot-%u", idx); + memory_region_init_alias(&vmem->memslots[idx], OBJECT(vmem), name, + &vmem->memdev->mr, memslot_offset, + memslot_size); + /* + * We want to be able to atomically and efficiently activate/deactivate + * individual memslots without affecting adjacent memslots in memory + * notifiers. + */ + memory_region_set_unmergeable(&vmem->memslots[idx], true); + } +} + static void virtio_mem_device_realize(DeviceState *dev, Error **errp) { MachineState *ms = MACHINE(qdev_get_machine()); @@ -772,6 +974,12 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) error_setg(errp, "'%s' property specifies an unsupported memdev", VIRTIO_MEM_MEMDEV_PROP); return; + } else if (vmem->memdev->prealloc) { + error_setg(errp, "'%s' property specifies a memdev with preallocation" + " enabled: %s. Instead, specify 'prealloc=on' for the" + " virtio-mem device. ", VIRTIO_MEM_MEMDEV_PROP, + object_get_canonical_path_component(OBJECT(vmem->memdev))); + return; } if ((nb_numa_nodes && vmem->node >= nb_numa_nodes) || @@ -813,6 +1021,14 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) vmem->unplugged_inaccessible = ON_OFF_AUTO_ON; #endif /* VIRTIO_MEM_HAS_LEGACY_GUESTS */ + if (vmem->dynamic_memslots && + vmem->unplugged_inaccessible != ON_OFF_AUTO_ON) { + error_setg(errp, "'%s' property set to 'on' requires '%s' to be 'on'", + VIRTIO_MEM_DYNAMIC_MEMSLOTS_PROP, + VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP); + return; + } + /* * If the block size wasn't configured by the user, use a sane default. This * allows using hugetlbfs backends of any page size without manual @@ -854,11 +1070,23 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) return; } - ret = ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb)); - if (ret) { - error_setg_errno(errp, -ret, "Unexpected error discarding RAM"); - ram_block_coordinated_discard_require(false); - return; + /* + * We don't know at this point whether shared RAM is migrated using + * QEMU or migrated using the file content. "x-ignore-shared" will be + * configured after realizing the device. So in case we have an + * incoming migration, simply always skip the discard step. + * + * Otherwise, make sure that we start with a clean slate: either the + * memory backend might get reused or the shared file might still have + * memory allocated. + */ + if (!runstate_check(RUN_STATE_INMIGRATE)) { + ret = ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb)); + if (ret) { + error_setg_errno(errp, -ret, "Unexpected error discarding RAM"); + ram_block_coordinated_discard_require(false); + return; + } } virtio_mem_resize_usable_region(vmem, vmem->requested_size, true); @@ -870,8 +1098,31 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) virtio_init(vdev, VIRTIO_ID_MEM, sizeof(struct virtio_mem_config)); vmem->vq = virtio_add_queue(vdev, 128, virtio_mem_handle_request); + /* + * With "dynamic-memslots=off" (old behavior) we always map the whole + * RAM memory region directly. + */ + if (vmem->dynamic_memslots) { + if (!vmem->mr) { + virtio_mem_prepare_mr(vmem); + } + if (vmem->nb_memslots <= 1) { + vmem->nb_memslots = 1; + vmem->memslot_size = memory_region_size(&vmem->memdev->mr); + } + if (!vmem->memslots) { + virtio_mem_prepare_memslots(vmem); + } + } else { + assert(!vmem->mr && !vmem->nb_memslots && !vmem->memslots); + } + host_memory_backend_set_mapped(vmem->memdev, true); vmstate_register_ram(&vmem->memdev->mr, DEVICE(vmem)); + if (vmem->early_migration) { + vmstate_register_any(VMSTATE_IF(vmem), + &vmstate_virtio_mem_device_early, vmem); + } qemu_register_reset(virtio_mem_system_reset, vmem); /* @@ -893,6 +1144,10 @@ static void virtio_mem_device_unrealize(DeviceState *dev) */ memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL); qemu_unregister_reset(virtio_mem_system_reset, vmem); + if (vmem->early_migration) { + vmstate_unregister(VMSTATE_IF(vmem), &vmstate_virtio_mem_device_early, + vmem); + } vmstate_unregister_ram(&vmem->memdev->mr, DEVICE(vmem)); host_memory_backend_set_mapped(vmem->memdev, false); virtio_del_queue(vdev, 0); @@ -901,7 +1156,7 @@ static void virtio_mem_device_unrealize(DeviceState *dev) ram_block_coordinated_discard_require(false); } -static int virtio_mem_discard_range_cb(const VirtIOMEM *vmem, void *arg, +static int virtio_mem_discard_range_cb(VirtIOMEM *vmem, void *arg, uint64_t offset, uint64_t size) { RAMBlock *rb = vmem->memdev->mr.ram_block; @@ -916,12 +1171,31 @@ static int virtio_mem_restore_unplugged(VirtIOMEM *vmem) virtio_mem_discard_range_cb); } -static int virtio_mem_post_load(void *opaque, int version_id) +static int virtio_mem_activate_memslot_range_cb(VirtIOMEM *vmem, void *arg, + uint64_t offset, uint64_t size) +{ + virtio_mem_activate_memslots_to_plug(vmem, offset, size); + return 0; +} + +static int virtio_mem_post_load_bitmap(VirtIOMEM *vmem) { - VirtIOMEM *vmem = VIRTIO_MEM(opaque); RamDiscardListener *rdl; int ret; + /* + * We restored the bitmap and updated the requested size; activate all + * memslots (so listeners register) before notifying about plugged blocks. + */ + if (vmem->dynamic_memslots) { + /* + * We don't expect any active memslots at this point to deactivate: no + * memory was plugged on the migration destination. + */ + virtio_mem_for_each_plugged_range(vmem, NULL, + virtio_mem_activate_memslot_range_cb); + } + /* * We started out with all memory discarded and our memory region is mapped * into an address space. Replay, now that we updated the bitmap. @@ -933,6 +1207,32 @@ static int virtio_mem_post_load(void *opaque, int version_id) return ret; } } + return 0; +} + +static int virtio_mem_post_load(void *opaque, int version_id) +{ + VirtIOMEM *vmem = VIRTIO_MEM(opaque); + int ret; + + if (!vmem->early_migration) { + ret = virtio_mem_post_load_bitmap(vmem); + if (ret) { + return ret; + } + } + + /* + * If shared RAM is migrated using the file content and not using QEMU, + * don't mess with preallocation and postcopy. + */ + if (migrate_ram_is_ignored(vmem->memdev->mr.ram_block)) { + return 0; + } + + if (vmem->prealloc && !vmem->early_migration) { + warn_report("Proper preallocation with migration requires a newer QEMU machine"); + } if (migration_in_incoming_postcopy()) { return 0; @@ -941,6 +1241,74 @@ static int virtio_mem_post_load(void *opaque, int version_id) return virtio_mem_restore_unplugged(vmem); } +static int virtio_mem_prealloc_range_cb(VirtIOMEM *vmem, void *arg, + uint64_t offset, uint64_t size) +{ + void *area = memory_region_get_ram_ptr(&vmem->memdev->mr) + offset; + int fd = memory_region_get_fd(&vmem->memdev->mr); + Error *local_err = NULL; + + if (!qemu_prealloc_mem(fd, area, size, 1, NULL, false, &local_err)) { + error_report_err(local_err); + return -ENOMEM; + } + return 0; +} + +static int virtio_mem_post_load_early(void *opaque, int version_id) +{ + VirtIOMEM *vmem = VIRTIO_MEM(opaque); + RAMBlock *rb = vmem->memdev->mr.ram_block; + int ret; + + if (!vmem->prealloc) { + goto post_load_bitmap; + } + + /* + * If shared RAM is migrated using the file content and not using QEMU, + * don't mess with preallocation and postcopy. + */ + if (migrate_ram_is_ignored(rb)) { + goto post_load_bitmap; + } + + /* + * We restored the bitmap and verified that the basic properties + * match on source and destination, so we can go ahead and preallocate + * memory for all plugged memory blocks, before actual RAM migration starts + * touching this memory. + */ + ret = virtio_mem_for_each_plugged_range(vmem, NULL, + virtio_mem_prealloc_range_cb); + if (ret) { + return ret; + } + + /* + * This is tricky: postcopy wants to start with a clean slate. On + * POSTCOPY_INCOMING_ADVISE, postcopy code discards all (ordinarily + * preallocated) RAM such that postcopy will work as expected later. + * + * However, we run after POSTCOPY_INCOMING_ADVISE -- but before actual + * RAM migration. So let's discard all memory again. This looks like an + * expensive NOP, but actually serves a purpose: we made sure that we + * were able to allocate all required backend memory once. We cannot + * guarantee that the backend memory we will free will remain free + * until we need it during postcopy, but at least we can catch the + * obvious setup issues this way. + */ + if (migration_incoming_postcopy_advised()) { + if (ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb))) { + return -EBUSY; + } + } + +post_load_bitmap: + /* Finally, update any other state to be consistent with the new bitmap. */ + return virtio_mem_post_load_bitmap(vmem); +} + typedef struct VirtIOMEMMigSanityChecks { VirtIOMEM *parent; uint64_t addr; @@ -973,7 +1341,7 @@ static int virtio_mem_mig_sanity_checks_post_load(void *opaque, int version_id) return -EINVAL; } /* - * Note: Preparation for resizeable memory regions. The maximum size + * Note: Preparation for resizable memory regions. The maximum size * of the memory region must not change during migration. */ if (tmp->region_size != new_region_size) { @@ -1000,7 +1368,7 @@ static const VMStateDescription vmstate_virtio_mem_sanity_checks = { .name = "virtio-mem-device/sanity-checks", .pre_save = virtio_mem_mig_sanity_checks_pre_save, .post_load = virtio_mem_mig_sanity_checks_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(addr, VirtIOMEMMigSanityChecks), VMSTATE_UINT64(region_size, VirtIOMEMMigSanityChecks), VMSTATE_UINT64(block_size, VirtIOMEMMigSanityChecks), @@ -1009,18 +1377,54 @@ static const VMStateDescription vmstate_virtio_mem_sanity_checks = { }, }; +static bool virtio_mem_vmstate_field_exists(void *opaque, int version_id) +{ + const VirtIOMEM *vmem = VIRTIO_MEM(opaque); + + /* With early migration, these fields were already migrated. */ + return !vmem->early_migration; +} + static const VMStateDescription vmstate_virtio_mem_device = { .name = "virtio-mem-device", .minimum_version_id = 1, .version_id = 1, .priority = MIG_PRI_VIRTIO_MEM, .post_load = virtio_mem_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { + VMSTATE_WITH_TMP_TEST(VirtIOMEM, virtio_mem_vmstate_field_exists, + VirtIOMEMMigSanityChecks, + vmstate_virtio_mem_sanity_checks), + VMSTATE_UINT64(usable_region_size, VirtIOMEM), + VMSTATE_UINT64_TEST(size, VirtIOMEM, virtio_mem_vmstate_field_exists), + VMSTATE_UINT64(requested_size, VirtIOMEM), + VMSTATE_BITMAP_TEST(bitmap, VirtIOMEM, virtio_mem_vmstate_field_exists, + 0, bitmap_size), + VMSTATE_END_OF_LIST() + }, +}; + +/* + * Transfer properties that are immutable while migration is active early, + * such that we have have this information around before migrating any RAM + * content. + * + * Note that virtio_mem_is_busy() makes sure these properties can no longer + * change on the migration source until migration completed. + * + * With QEMU compat machines, we transmit these properties later, via + * vmstate_virtio_mem_device instead -- see virtio_mem_vmstate_field_exists(). + */ +static const VMStateDescription vmstate_virtio_mem_device_early = { + .name = "virtio-mem-device-early", + .minimum_version_id = 1, + .version_id = 1, + .early_setup = true, + .post_load = virtio_mem_post_load_early, + .fields = (const VMStateField[]) { VMSTATE_WITH_TMP(VirtIOMEM, VirtIOMEMMigSanityChecks, vmstate_virtio_mem_sanity_checks), - VMSTATE_UINT64(usable_region_size, VirtIOMEM), VMSTATE_UINT64(size, VirtIOMEM), - VMSTATE_UINT64(requested_size, VirtIOMEM), VMSTATE_BITMAP(bitmap, VirtIOMEM, 0, bitmap_size), VMSTATE_END_OF_LIST() }, @@ -1030,7 +1434,7 @@ static const VMStateDescription vmstate_virtio_mem = { .name = "virtio-mem", .minimum_version_id = 1, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, @@ -1053,11 +1457,79 @@ static MemoryRegion *virtio_mem_get_memory_region(VirtIOMEM *vmem, Error **errp) if (!vmem->memdev) { error_setg(errp, "'%s' property must be set", VIRTIO_MEM_MEMDEV_PROP); return NULL; + } else if (vmem->dynamic_memslots) { + if (!vmem->mr) { + virtio_mem_prepare_mr(vmem); + } + return vmem->mr; } return &vmem->memdev->mr; } +static void virtio_mem_decide_memslots(VirtIOMEM *vmem, unsigned int limit) +{ + uint64_t region_size, memslot_size, min_memslot_size; + unsigned int memslots; + RAMBlock *rb; + + if (!vmem->dynamic_memslots) { + return; + } + + /* We're called exactly once, before realizing the device. */ + assert(!vmem->nb_memslots); + + /* If realizing the device will fail, just assume a single memslot. */ + if (limit <= 1 || !vmem->memdev || !vmem->memdev->mr.ram_block) { + vmem->nb_memslots = 1; + return; + } + + rb = vmem->memdev->mr.ram_block; + region_size = memory_region_size(&vmem->memdev->mr); + + /* + * Determine the default block size now, to determine the minimum memslot + * size. We want the minimum slot size to be at least the device block size. + */ + if (!vmem->block_size) { + vmem->block_size = virtio_mem_default_block_size(rb); + } + /* If realizing the device will fail, just assume a single memslot. */ + if (vmem->block_size < qemu_ram_pagesize(rb) || + !QEMU_IS_ALIGNED(region_size, vmem->block_size)) { + vmem->nb_memslots = 1; + return; + } + + /* + * All memslots except the last one have a reasonable minimum size, and + * and all memslot sizes are aligned to the device block size. + */ + memslot_size = QEMU_ALIGN_UP(region_size / limit, vmem->block_size); + min_memslot_size = MAX(vmem->block_size, VIRTIO_MEM_MIN_MEMSLOT_SIZE); + memslot_size = MAX(memslot_size, min_memslot_size); + + memslots = QEMU_ALIGN_UP(region_size, memslot_size) / memslot_size; + if (memslots != 1) { + vmem->memslot_size = memslot_size; + } + vmem->nb_memslots = memslots; +} + +static unsigned int virtio_mem_get_memslots(VirtIOMEM *vmem) +{ + if (!vmem->dynamic_memslots) { + /* Exactly one static RAM memory region. */ + return 1; + } + + /* We're called after instructed to make a decision. */ + g_assert(vmem->nb_memslots); + return vmem->nb_memslots; +} + static void virtio_mem_add_size_change_notifier(VirtIOMEM *vmem, Notifier *notifier) { @@ -1094,12 +1566,9 @@ static void virtio_mem_set_requested_size(Object *obj, Visitor *v, Error **errp) { VirtIOMEM *vmem = VIRTIO_MEM(obj); - Error *err = NULL; uint64_t value; - visit_type_size(v, name, &value, &err); - if (err) { - error_propagate(errp, err); + if (!visit_type_size(v, name, &value, errp)) { return; } @@ -1159,7 +1628,6 @@ static void virtio_mem_set_block_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { VirtIOMEM *vmem = VIRTIO_MEM(obj); - Error *err = NULL; uint64_t value; if (DEVICE(obj)->realized) { @@ -1167,9 +1635,7 @@ static void virtio_mem_set_block_size(Object *obj, Visitor *v, const char *name, return; } - visit_type_size(v, name, &value, &err); - if (err) { - error_propagate(errp, err); + if (!visit_type_size(v, name, &value, errp)) { return; } @@ -1201,6 +1667,21 @@ static void virtio_mem_instance_init(Object *obj) NULL, NULL); } +static void virtio_mem_instance_finalize(Object *obj) +{ + VirtIOMEM *vmem = VIRTIO_MEM(obj); + + /* + * Note: the core already dropped the references on all memory regions + * (it's passed as the owner to memory_region_init_*()) and finalized + * these objects. We can simply free the memory. + */ + g_free(vmem->memslots); + vmem->memslots = NULL; + g_free(vmem->mr); + vmem->mr = NULL; +} + static Property virtio_mem_properties[] = { DEFINE_PROP_UINT64(VIRTIO_MEM_ADDR_PROP, VirtIOMEM, addr, 0), DEFINE_PROP_UINT32(VIRTIO_MEM_NODE_PROP, VirtIOMEM, node, 0), @@ -1209,8 +1690,12 @@ static Property virtio_mem_properties[] = { TYPE_MEMORY_BACKEND, HostMemoryBackend *), #if defined(VIRTIO_MEM_HAS_LEGACY_GUESTS) DEFINE_PROP_ON_OFF_AUTO(VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP, VirtIOMEM, - unplugged_inaccessible, ON_OFF_AUTO_AUTO), + unplugged_inaccessible, ON_OFF_AUTO_ON), #endif + DEFINE_PROP_BOOL(VIRTIO_MEM_EARLY_MIGRATION_PROP, VirtIOMEM, + early_migration, true), + DEFINE_PROP_BOOL(VIRTIO_MEM_DYNAMIC_MEMSLOTS_PROP, VirtIOMEM, + dynamic_memslots, false), DEFINE_PROP_END_OF_LIST(), }; @@ -1239,7 +1724,7 @@ static bool virtio_mem_rdm_is_populated(const RamDiscardManager *rdm, return false; } - return virtio_mem_test_bitmap(vmem, start_gpa, end_gpa - start_gpa, true); + return virtio_mem_is_range_plugged(vmem, start_gpa, end_gpa - start_gpa); } struct VirtIOMEMReplayData { @@ -1334,6 +1819,30 @@ static void virtio_mem_rdm_unregister_listener(RamDiscardManager *rdm, QLIST_REMOVE(rdl, next); } +static void virtio_mem_unplug_request_check(VirtIOMEM *vmem, Error **errp) +{ + if (vmem->unplugged_inaccessible == ON_OFF_AUTO_OFF) { + /* + * We could allow it with a usable region size of 0, but let's just + * not care about that legacy setting. + */ + error_setg(errp, "virtio-mem device cannot get unplugged while" + " '" VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP "' != 'on'"); + return; + } + + if (vmem->size) { + error_setg(errp, "virtio-mem device cannot get unplugged while" + " '" VIRTIO_MEM_SIZE_PROP "' != '0'"); + return; + } + if (vmem->requested_size) { + error_setg(errp, "virtio-mem device cannot get unplugged while" + " '" VIRTIO_MEM_REQUESTED_SIZE_PROP "' != '0'"); + return; + } +} + static void virtio_mem_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1354,8 +1863,11 @@ static void virtio_mem_class_init(ObjectClass *klass, void *data) vmc->fill_device_info = virtio_mem_fill_device_info; vmc->get_memory_region = virtio_mem_get_memory_region; + vmc->decide_memslots = virtio_mem_decide_memslots; + vmc->get_memslots = virtio_mem_get_memslots; vmc->add_size_change_notifier = virtio_mem_add_size_change_notifier; vmc->remove_size_change_notifier = virtio_mem_remove_size_change_notifier; + vmc->unplug_request_check = virtio_mem_unplug_request_check; rdmc->get_min_granularity = virtio_mem_rdm_get_min_granularity; rdmc->is_populated = virtio_mem_rdm_is_populated; @@ -1370,6 +1882,7 @@ static const TypeInfo virtio_mem_info = { .parent = TYPE_VIRTIO_DEVICE, .instance_size = sizeof(VirtIOMEM), .instance_init = virtio_mem_instance_init, + .instance_finalize = virtio_mem_instance_finalize, .class_init = virtio_mem_class_init, .class_size = sizeof(VirtIOMEMClass), .interfaces = (InterfaceInfo[]) { diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 688eccda94..22f9fbcf5a 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -72,12 +72,12 @@ static void virtio_mmio_soft_reset(VirtIOMMIOProxy *proxy) { int i; - if (proxy->legacy) { - return; - } + virtio_bus_reset(&proxy->bus); - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - proxy->vqs[i].enabled = 0; + if (!proxy->legacy) { + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { + proxy->vqs[i].enabled = 0; + } } } @@ -354,6 +354,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, if (proxy->legacy) { virtio_queue_update_rings(vdev, vdev->queue_sel); } else { + virtio_init_region_cache(vdev, vdev->queue_sel); proxy->vqs[vdev->queue_sel].num = value; } break; @@ -376,7 +377,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, return; } if (value == 0) { - virtio_reset(vdev); + virtio_mmio_soft_reset(proxy); } else { virtio_queue_set_addr(vdev, vdev->queue_sel, value << proxy->guest_page_shift); @@ -432,7 +433,6 @@ 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; @@ -565,7 +565,7 @@ static const VMStateDescription vmstate_virtio_mmio_queue_state = { .name = "virtio_mmio/queue_state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(num, VirtIOMMIOQueue), VMSTATE_BOOL(enabled, VirtIOMMIOQueue), VMSTATE_UINT32_ARRAY(desc, VirtIOMMIOQueue, 2), @@ -579,7 +579,7 @@ static const VMStateDescription vmstate_virtio_mmio_state_sub = { .name = "virtio_mmio/state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(guest_features, VirtIOMMIOProxy, 2), VMSTATE_STRUCT_ARRAY(vqs, VirtIOMMIOProxy, VIRTIO_QUEUE_MAX, 0, vmstate_virtio_mmio_queue_state, @@ -592,10 +592,10 @@ static const VMStateDescription vmstate_virtio_mmio = { .name = "virtio_mmio", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_virtio_mmio_state_sub, NULL } @@ -627,8 +627,8 @@ static void virtio_mmio_reset(DeviceState *d) VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); int i; - virtio_mmio_stop_ioeventfd(proxy); - virtio_bus_reset(&proxy->bus); + virtio_mmio_soft_reset(proxy); + proxy->host_features_sel = 0; proxy->guest_features_sel = 0; proxy->guest_page_shift = 0; @@ -637,7 +637,6 @@ static void virtio_mmio_reset(DeviceState *d) proxy->guest_features[0] = proxy->guest_features[1] = 0; for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - proxy->vqs[i].enabled = 0; proxy->vqs[i].num = 0; proxy->vqs[i].desc[0] = proxy->vqs[i].desc[1] = 0; proxy->vqs[i].avail[0] = proxy->vqs[i].avail[1] = 0; @@ -672,7 +671,30 @@ static int virtio_mmio_set_guest_notifier(DeviceState *d, int n, bool assign, return 0; } +static int virtio_mmio_set_config_guest_notifier(DeviceState *d, bool assign, + bool with_irqfd) +{ + VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); + EventNotifier *notifier = virtio_config_get_guest_notifier(vdev); + int r = 0; + if (assign) { + r = event_notifier_init(notifier, 0); + if (r < 0) { + return r; + } + virtio_config_set_guest_notifier_fd_handler(vdev, assign, with_irqfd); + } else { + virtio_config_set_guest_notifier_fd_handler(vdev, assign, with_irqfd); + event_notifier_cleanup(notifier); + } + if (vdc->guest_notifier_mask && vdev->use_guest_notifier_mask) { + vdc->guest_notifier_mask(vdev, VIRTIO_CONFIG_IRQ_IDX, !assign); + } + return r; +} static int virtio_mmio_set_guest_notifiers(DeviceState *d, int nvqs, bool assign) { @@ -694,6 +716,10 @@ static int virtio_mmio_set_guest_notifiers(DeviceState *d, int nvqs, goto assign_error; } } + r = virtio_mmio_set_config_guest_notifier(d, assign, with_irqfd); + if (r < 0) { + goto assign_error; + } return 0; @@ -735,10 +761,6 @@ static void virtio_mmio_realizefn(DeviceState *d, Error **errp) qbus_init(&proxy->bus, sizeof(proxy->bus), TYPE_VIRTIO_MMIO_BUS, d, NULL); sysbus_init_irq(sbd, &proxy->irq); - if (!kvm_eventfds_enabled()) { - proxy->flags &= ~VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD; - } - /* fd-based ioevents can't be synchronized in record/replay */ if (replay_mode != REPLAY_MODE_NONE) { proxy->flags &= ~VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD; @@ -804,10 +826,10 @@ static char *virtio_mmio_bus_get_dev_path(DeviceState *dev) assert(section.mr); if (proxy_path) { - path = g_strdup_printf("%s/virtio-mmio@" TARGET_FMT_plx, proxy_path, + path = g_strdup_printf("%s/virtio-mmio@" HWADDR_FMT_plx, proxy_path, section.offset_within_address_space); } else { - path = g_strdup_printf("virtio-mmio@" TARGET_FMT_plx, + path = g_strdup_printf("virtio-mmio@" HWADDR_FMT_plx, section.offset_within_address_space); } memory_region_unref(section.mr); diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 0566ad7d00..cb159fd078 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -19,6 +19,7 @@ #include "exec/memop.h" #include "standard-headers/linux/virtio_pci.h" +#include "standard-headers/linux/virtio_ids.h" #include "hw/boards.h" #include "hw/virtio/virtio.h" #include "migration/qemu-file-types.h" @@ -71,9 +72,11 @@ static void virtio_pci_notify(DeviceState *d, uint16_t vector) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy_fast(d); - if (msix_enabled(&proxy->pci_dev)) - msix_notify(&proxy->pci_dev, vector); - else { + if (msix_enabled(&proxy->pci_dev)) { + if (vector != VIRTIO_NO_VECTOR) { + msix_notify(&proxy->pci_dev, vector); + } + } else { VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); pci_set_irq(&proxy->pci_dev, qatomic_read(&vdev->isr) & 1); } @@ -94,7 +97,7 @@ static const VMStateDescription vmstate_virtio_pci_modern_queue_state = { .name = "virtio_pci/modern_queue_state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(num, VirtIOPCIQueue), VMSTATE_UNUSED(1), /* enabled was stored as be16 */ VMSTATE_BOOL(enabled, VirtIOPCIQueue), @@ -117,7 +120,7 @@ static const VMStateDescription vmstate_virtio_pci_modern_state_sub = { .version_id = 1, .minimum_version_id = 1, .needed = &virtio_pci_modern_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(dfselect, VirtIOPCIProxy), VMSTATE_UINT32(gfselect, VirtIOPCIProxy), VMSTATE_UINT32_ARRAY(guest_features, VirtIOPCIProxy, 2), @@ -132,10 +135,10 @@ static const VMStateDescription vmstate_virtio_pci = { .name = "virtio_pci", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_virtio_pci_modern_state_sub, NULL } @@ -175,6 +178,7 @@ static int virtio_pci_load_config(DeviceState *d, QEMUFile *f) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + uint16_t vector; int ret; ret = pci_device_load(&proxy->pci_dev, f); @@ -184,12 +188,17 @@ static int virtio_pci_load_config(DeviceState *d, QEMUFile *f) msix_unuse_all_vectors(&proxy->pci_dev); msix_load(&proxy->pci_dev, f); if (msix_present(&proxy->pci_dev)) { - qemu_get_be16s(f, &vdev->config_vector); + qemu_get_be16s(f, &vector); + + if (vector != VIRTIO_NO_VECTOR && vector >= proxy->nvectors) { + return -EINVAL; + } } else { - vdev->config_vector = VIRTIO_NO_VECTOR; + vector = VIRTIO_NO_VECTOR; } - if (vdev->config_vector != VIRTIO_NO_VECTOR) { - return msix_vector_use(&proxy->pci_dev, vdev->config_vector); + vdev->config_vector = vector; + if (vector != VIRTIO_NO_VECTOR) { + msix_vector_use(&proxy->pci_dev, vector); } return 0; } @@ -202,17 +211,104 @@ static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f) uint16_t vector; if (msix_present(&proxy->pci_dev)) { qemu_get_be16s(f, &vector); + if (vector != VIRTIO_NO_VECTOR && vector >= proxy->nvectors) { + return -EINVAL; + } } else { vector = VIRTIO_NO_VECTOR; } virtio_queue_set_vector(vdev, n, vector); if (vector != VIRTIO_NO_VECTOR) { - return msix_vector_use(&proxy->pci_dev, vector); + msix_vector_use(&proxy->pci_dev, vector); } return 0; } +typedef struct VirtIOPCIIDInfo { + /* virtio id */ + uint16_t vdev_id; + /* pci device id for the transitional device */ + uint16_t trans_devid; + uint16_t class_id; +} VirtIOPCIIDInfo; + +static const VirtIOPCIIDInfo virtio_pci_id_info[] = { + { + .vdev_id = VIRTIO_ID_CRYPTO, + .class_id = PCI_CLASS_OTHERS, + }, { + .vdev_id = VIRTIO_ID_FS, + .class_id = PCI_CLASS_STORAGE_OTHER, + }, { + .vdev_id = VIRTIO_ID_NET, + .trans_devid = PCI_DEVICE_ID_VIRTIO_NET, + .class_id = PCI_CLASS_NETWORK_ETHERNET, + }, { + .vdev_id = VIRTIO_ID_BLOCK, + .trans_devid = PCI_DEVICE_ID_VIRTIO_BLOCK, + .class_id = PCI_CLASS_STORAGE_SCSI, + }, { + .vdev_id = VIRTIO_ID_CONSOLE, + .trans_devid = PCI_DEVICE_ID_VIRTIO_CONSOLE, + .class_id = PCI_CLASS_COMMUNICATION_OTHER, + }, { + .vdev_id = VIRTIO_ID_SCSI, + .trans_devid = PCI_DEVICE_ID_VIRTIO_SCSI, + .class_id = PCI_CLASS_STORAGE_SCSI + }, { + .vdev_id = VIRTIO_ID_9P, + .trans_devid = PCI_DEVICE_ID_VIRTIO_9P, + .class_id = PCI_BASE_CLASS_NETWORK, + }, { + .vdev_id = VIRTIO_ID_BALLOON, + .trans_devid = PCI_DEVICE_ID_VIRTIO_BALLOON, + .class_id = PCI_CLASS_OTHERS, + }, { + .vdev_id = VIRTIO_ID_RNG, + .trans_devid = PCI_DEVICE_ID_VIRTIO_RNG, + .class_id = PCI_CLASS_OTHERS, + }, +}; + +static const VirtIOPCIIDInfo *virtio_pci_get_id_info(uint16_t vdev_id) +{ + const VirtIOPCIIDInfo *info = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(virtio_pci_id_info); i++) { + if (virtio_pci_id_info[i].vdev_id == vdev_id) { + info = &virtio_pci_id_info[i]; + break; + } + } + + if (!info) { + /* The device id is invalid or not added to the id_info yet. */ + error_report("Invalid virtio device(id %u)", vdev_id); + abort(); + } + + return info; +} + +/* + * Get the Transitional Device ID for the specific device, return + * zero if the device is non-transitional. + */ +uint16_t virtio_pci_get_trans_devid(uint16_t device_id) +{ + return virtio_pci_get_id_info(device_id)->trans_devid; +} + +/* + * Get the Class ID for the specific device. + */ +uint16_t virtio_pci_get_class_id(uint16_t device_id) +{ + return virtio_pci_get_id_info(device_id)->class_id; +} + static bool virtio_pci_ioeventfd_enabled(DeviceState *d) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); @@ -236,7 +332,6 @@ static int virtio_pci_ioeventfd_assign(DeviceState *d, EventNotifier *notifier, VirtQueue *vq = virtio_get_queue(vdev, n); bool legacy = virtio_pci_legacy(proxy); bool modern = virtio_pci_modern(proxy); - bool fast_mmio = kvm_ioeventfd_any_length_enabled(); bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY; MemoryRegion *modern_mr = &proxy->notify.mr; MemoryRegion *modern_notify_mr = &proxy->notify_pio.mr; @@ -247,13 +342,8 @@ static int virtio_pci_ioeventfd_assign(DeviceState *d, EventNotifier *notifier, if (assign) { if (modern) { - if (fast_mmio) { - memory_region_add_eventfd(modern_mr, modern_addr, 0, - false, n, notifier); - } else { - memory_region_add_eventfd(modern_mr, modern_addr, 2, - false, n, notifier); - } + memory_region_add_eventfd(modern_mr, modern_addr, 0, + false, n, notifier); if (modern_pio) { memory_region_add_eventfd(modern_notify_mr, 0, 2, true, n, notifier); @@ -265,13 +355,8 @@ static int virtio_pci_ioeventfd_assign(DeviceState *d, EventNotifier *notifier, } } else { if (modern) { - if (fast_mmio) { - memory_region_del_eventfd(modern_mr, modern_addr, 0, - false, n, notifier); - } else { - memory_region_del_eventfd(modern_mr, modern_addr, 2, - false, n, notifier); - } + memory_region_del_eventfd(modern_mr, modern_addr, 0, + false, n, notifier); if (modern_pio) { memory_region_del_eventfd(modern_notify_mr, 0, 2, true, n, notifier); @@ -299,6 +384,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) { VirtIOPCIProxy *proxy = opaque; VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + uint16_t vector; hwaddr pa; switch (addr) { @@ -352,18 +438,28 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) } break; case VIRTIO_MSI_CONFIG_VECTOR: - msix_vector_unuse(&proxy->pci_dev, vdev->config_vector); + if (vdev->config_vector != VIRTIO_NO_VECTOR) { + msix_vector_unuse(&proxy->pci_dev, vdev->config_vector); + } /* Make it possible for guest to discover an error took place. */ - if (msix_vector_use(&proxy->pci_dev, val) < 0) + if (val < proxy->nvectors) { + msix_vector_use(&proxy->pci_dev, val); + } else { val = VIRTIO_NO_VECTOR; + } vdev->config_vector = val; break; case VIRTIO_MSI_QUEUE_VECTOR: - msix_vector_unuse(&proxy->pci_dev, - virtio_queue_vector(vdev, vdev->queue_sel)); + vector = virtio_queue_vector(vdev, vdev->queue_sel); + if (vector != VIRTIO_NO_VECTOR) { + msix_vector_unuse(&proxy->pci_dev, vector); + } /* Make it possible for guest to discover an error took place. */ - if (msix_vector_use(&proxy->pci_dev, val) < 0) + if (val < proxy->nvectors) { + msix_vector_use(&proxy->pci_dev, val); + } else { val = VIRTIO_NO_VECTOR; + } virtio_queue_set_vector(vdev, vdev->queue_sel, val); break; default: @@ -609,6 +705,38 @@ virtio_address_space_read(VirtIOPCIProxy *proxy, hwaddr addr, } } +static void virtio_pci_ats_ctrl_trigger(PCIDevice *pci_dev, bool enable) +{ + VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + + vdev->device_iotlb_enabled = enable; + + if (k->toggle_device_iotlb) { + k->toggle_device_iotlb(vdev); + } +} + +static void pcie_ats_config_write(PCIDevice *dev, uint32_t address, + uint32_t val, int len) +{ + uint32_t off; + uint16_t ats_cap = dev->exp.ats_cap; + + if (!ats_cap || address < ats_cap) { + return; + } + off = address - ats_cap; + if (off >= PCI_EXT_CAP_ATS_SIZEOF) { + return; + } + + if (range_covers_byte(off, len, PCI_ATS_CTRL + 1)) { + virtio_pci_ats_ctrl_trigger(dev, !!(val & PCI_ATS_CTRL_ENABLE)); + } +} + static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, uint32_t val, int len) { @@ -622,6 +750,10 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, pcie_cap_flr_write_config(pci_dev, address, val, len); } + if (proxy->flags & VIRTIO_PCI_FLAG_ATS) { + pcie_ats_config_write(pci_dev, address, val, len); + } + if (range_covers_byte(address, len, PCI_COMMAND)) { if (!(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { virtio_set_disabled(vdev, true); @@ -637,15 +769,15 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, pci_cfg_data), sizeof cfg->pci_cfg_data)) { uint32_t off; - uint32_t len; + uint32_t caplen; cfg = (void *)(proxy->pci_dev.config + proxy->config_cap); off = le32_to_cpu(cfg->cap.offset); - len = le32_to_cpu(cfg->cap.length); + caplen = le32_to_cpu(cfg->cap.length); - if (len == 1 || len == 2 || len == 4) { - assert(len <= sizeof cfg->pci_cfg_data); - virtio_address_space_write(proxy, off, cfg->pci_cfg_data, len); + if (caplen == 1 || caplen == 2 || caplen == 4) { + assert(caplen <= sizeof cfg->pci_cfg_data); + virtio_address_space_write(proxy, off, cfg->pci_cfg_data, caplen); } } } @@ -661,15 +793,15 @@ static uint32_t virtio_read_config(PCIDevice *pci_dev, pci_cfg_data), sizeof cfg->pci_cfg_data)) { uint32_t off; - uint32_t len; + uint32_t caplen; cfg = (void *)(proxy->pci_dev.config + proxy->config_cap); off = le32_to_cpu(cfg->cap.offset); - len = le32_to_cpu(cfg->cap.length); + caplen = le32_to_cpu(cfg->cap.length); - if (len == 1 || len == 2 || len == 4) { - assert(len <= sizeof cfg->pci_cfg_data); - virtio_address_space_read(proxy, off, cfg->pci_cfg_data, len); + if (caplen == 1 || caplen == 2 || caplen == 4) { + assert(caplen <= sizeof cfg->pci_cfg_data); + virtio_address_space_read(proxy, off, cfg->pci_cfg_data, caplen); } } @@ -677,7 +809,6 @@ static uint32_t virtio_read_config(PCIDevice *pci_dev, } static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy, - unsigned int queue_no, unsigned int vector) { VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; @@ -706,112 +837,160 @@ static void kvm_virtio_pci_vq_vector_release(VirtIOPCIProxy *proxy, } static int kvm_virtio_pci_irqfd_use(VirtIOPCIProxy *proxy, - unsigned int queue_no, + EventNotifier *n, unsigned int vector) { VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtQueue *vq = virtio_get_queue(vdev, queue_no); - EventNotifier *n = virtio_queue_get_guest_notifier(vq); return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, irqfd->virq); } static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy, - unsigned int queue_no, + EventNotifier *n , unsigned int vector) { - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtQueue *vq = virtio_get_queue(vdev, queue_no); - EventNotifier *n = virtio_queue_get_guest_notifier(vq); VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; int ret; ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, irqfd->virq); assert(ret == 0); } - -static int kvm_virtio_pci_vector_use(VirtIOPCIProxy *proxy, int nvqs) +static int virtio_pci_get_notifier(VirtIOPCIProxy *proxy, int queue_no, + EventNotifier **n, unsigned int *vector) { + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + VirtQueue *vq; + + if (queue_no == VIRTIO_CONFIG_IRQ_IDX) { + *n = virtio_config_get_guest_notifier(vdev); + *vector = vdev->config_vector; + } else { + if (!virtio_queue_get_num(vdev, queue_no)) { + return -1; + } + *vector = virtio_queue_vector(vdev, queue_no); + vq = virtio_get_queue(vdev, queue_no); + *n = virtio_queue_get_guest_notifier(vq); + } + return 0; +} + +static int kvm_virtio_pci_vector_use_one(VirtIOPCIProxy *proxy, int queue_no) +{ + unsigned int vector; + int ret; + EventNotifier *n; PCIDevice *dev = &proxy->pci_dev; VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - unsigned int vector; - int ret, queue_no; + + ret = virtio_pci_get_notifier(proxy, queue_no, &n, &vector); + if (ret < 0) { + return ret; + } + if (vector >= msix_nr_vectors_allocated(dev)) { + return 0; + } + ret = kvm_virtio_pci_vq_vector_use(proxy, vector); + if (ret < 0) { + goto undo; + } + /* + * If guest supports masking, set up irqfd now. + * Otherwise, delay until unmasked in the frontend. + */ + if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) { + ret = kvm_virtio_pci_irqfd_use(proxy, n, vector); + if (ret < 0) { + kvm_virtio_pci_vq_vector_release(proxy, vector); + goto undo; + } + } + + return 0; +undo: + + vector = virtio_queue_vector(vdev, queue_no); + if (vector >= msix_nr_vectors_allocated(dev)) { + return ret; + } + if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) { + ret = virtio_pci_get_notifier(proxy, queue_no, &n, &vector); + if (ret < 0) { + return ret; + } + kvm_virtio_pci_irqfd_release(proxy, n, vector); + } + return ret; +} +static int kvm_virtio_pci_vector_vq_use(VirtIOPCIProxy *proxy, int nvqs) +{ + int queue_no; + int ret = 0; + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); for (queue_no = 0; queue_no < nvqs; queue_no++) { if (!virtio_queue_get_num(vdev, queue_no)) { - break; + return -1; } - vector = virtio_queue_vector(vdev, queue_no); - if (vector >= msix_nr_vectors_allocated(dev)) { - continue; - } - ret = kvm_virtio_pci_vq_vector_use(proxy, queue_no, vector); - if (ret < 0) { - goto undo; - } - /* If guest supports masking, set up irqfd now. - * Otherwise, delay until unmasked in the frontend. - */ - if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) { - ret = kvm_virtio_pci_irqfd_use(proxy, queue_no, vector); - if (ret < 0) { - kvm_virtio_pci_vq_vector_release(proxy, vector); - goto undo; - } - } - } - return 0; - -undo: - while (--queue_no >= 0) { - vector = virtio_queue_vector(vdev, queue_no); - if (vector >= msix_nr_vectors_allocated(dev)) { - continue; - } - if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) { - kvm_virtio_pci_irqfd_release(proxy, queue_no, vector); - } - kvm_virtio_pci_vq_vector_release(proxy, vector); + ret = kvm_virtio_pci_vector_use_one(proxy, queue_no); } return ret; } -static void kvm_virtio_pci_vector_release(VirtIOPCIProxy *proxy, int nvqs) +static int kvm_virtio_pci_vector_config_use(VirtIOPCIProxy *proxy) +{ + return kvm_virtio_pci_vector_use_one(proxy, VIRTIO_CONFIG_IRQ_IDX); +} + +static void kvm_virtio_pci_vector_release_one(VirtIOPCIProxy *proxy, + int queue_no) { - PCIDevice *dev = &proxy->pci_dev; VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); unsigned int vector; - int queue_no; + EventNotifier *n; + int ret; VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + PCIDevice *dev = &proxy->pci_dev; + + ret = virtio_pci_get_notifier(proxy, queue_no, &n, &vector); + if (ret < 0) { + return; + } + if (vector >= msix_nr_vectors_allocated(dev)) { + return; + } + if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) { + kvm_virtio_pci_irqfd_release(proxy, n, vector); + } + kvm_virtio_pci_vq_vector_release(proxy, vector); +} + +static void kvm_virtio_pci_vector_vq_release(VirtIOPCIProxy *proxy, int nvqs) +{ + int queue_no; + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); for (queue_no = 0; queue_no < nvqs; queue_no++) { if (!virtio_queue_get_num(vdev, queue_no)) { break; } - vector = virtio_queue_vector(vdev, queue_no); - if (vector >= msix_nr_vectors_allocated(dev)) { - continue; - } - /* If guest supports masking, clean up irqfd now. - * Otherwise, it was cleaned when masked in the frontend. - */ - if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) { - kvm_virtio_pci_irqfd_release(proxy, queue_no, vector); - } - kvm_virtio_pci_vq_vector_release(proxy, vector); + kvm_virtio_pci_vector_release_one(proxy, queue_no); } } -static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy, +static void kvm_virtio_pci_vector_config_release(VirtIOPCIProxy *proxy) +{ + kvm_virtio_pci_vector_release_one(proxy, VIRTIO_CONFIG_IRQ_IDX); +} + +static int virtio_pci_one_vector_unmask(VirtIOPCIProxy *proxy, unsigned int queue_no, unsigned int vector, - MSIMessage msg) + MSIMessage msg, + EventNotifier *n) { VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - VirtQueue *vq = virtio_get_queue(vdev, queue_no); - EventNotifier *n = virtio_queue_get_guest_notifier(vq); VirtIOIRQFD *irqfd; int ret = 0; @@ -838,14 +1017,15 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy, event_notifier_set(n); } } else { - ret = kvm_virtio_pci_irqfd_use(proxy, queue_no, vector); + ret = kvm_virtio_pci_irqfd_use(proxy, n, vector); } return ret; } -static void virtio_pci_vq_vector_mask(VirtIOPCIProxy *proxy, +static void virtio_pci_one_vector_mask(VirtIOPCIProxy *proxy, unsigned int queue_no, - unsigned int vector) + unsigned int vector, + EventNotifier *n) { VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); @@ -856,7 +1036,7 @@ static void virtio_pci_vq_vector_mask(VirtIOPCIProxy *proxy, if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) { k->guest_notifier_mask(vdev, queue_no, true); } else { - kvm_virtio_pci_irqfd_release(proxy, queue_no, vector); + kvm_virtio_pci_irqfd_release(proxy, n, vector); } } @@ -866,6 +1046,7 @@ static int virtio_pci_vector_unmask(PCIDevice *dev, unsigned vector, VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev); VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); VirtQueue *vq = virtio_vector_first_queue(vdev, vector); + EventNotifier *n; int ret, index, unmasked = 0; while (vq) { @@ -874,7 +1055,8 @@ static int virtio_pci_vector_unmask(PCIDevice *dev, unsigned vector, break; } if (index < proxy->nvqs_with_notifiers) { - ret = virtio_pci_vq_vector_unmask(proxy, index, vector, msg); + n = virtio_queue_get_guest_notifier(vq); + ret = virtio_pci_one_vector_unmask(proxy, index, vector, msg, n); if (ret < 0) { goto undo; } @@ -882,15 +1064,26 @@ static int virtio_pci_vector_unmask(PCIDevice *dev, unsigned vector, } vq = virtio_vector_next_queue(vq); } - + /* unmask config intr */ + if (vector == vdev->config_vector) { + n = virtio_config_get_guest_notifier(vdev); + ret = virtio_pci_one_vector_unmask(proxy, VIRTIO_CONFIG_IRQ_IDX, vector, + msg, n); + if (ret < 0) { + goto undo_config; + } + } return 0; - +undo_config: + n = virtio_config_get_guest_notifier(vdev); + virtio_pci_one_vector_mask(proxy, VIRTIO_CONFIG_IRQ_IDX, vector, n); undo: vq = virtio_vector_first_queue(vdev, vector); while (vq && unmasked >= 0) { index = virtio_get_queue_index(vq); if (index < proxy->nvqs_with_notifiers) { - virtio_pci_vq_vector_mask(proxy, index, vector); + n = virtio_queue_get_guest_notifier(vq); + virtio_pci_one_vector_mask(proxy, index, vector, n); --unmasked; } vq = virtio_vector_next_queue(vq); @@ -903,18 +1096,25 @@ static void virtio_pci_vector_mask(PCIDevice *dev, unsigned vector) VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev); VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); VirtQueue *vq = virtio_vector_first_queue(vdev, vector); + EventNotifier *n; int index; while (vq) { index = virtio_get_queue_index(vq); + n = virtio_queue_get_guest_notifier(vq); if (!virtio_queue_get_num(vdev, index)) { break; } if (index < proxy->nvqs_with_notifiers) { - virtio_pci_vq_vector_mask(proxy, index, vector); + virtio_pci_one_vector_mask(proxy, index, vector, n); } vq = virtio_vector_next_queue(vq); } + + if (vector == vdev->config_vector) { + n = virtio_config_get_guest_notifier(vdev); + virtio_pci_one_vector_mask(proxy, VIRTIO_CONFIG_IRQ_IDX, vector, n); + } } static void virtio_pci_vector_poll(PCIDevice *dev, @@ -927,19 +1127,17 @@ static void virtio_pci_vector_poll(PCIDevice *dev, int queue_no; unsigned int vector; EventNotifier *notifier; - VirtQueue *vq; + int ret; for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) { - if (!virtio_queue_get_num(vdev, queue_no)) { + ret = virtio_pci_get_notifier(proxy, queue_no, ¬ifier, &vector); + if (ret < 0) { break; } - vector = virtio_queue_vector(vdev, queue_no); if (vector < vector_start || vector >= vector_end || !msix_is_masked(dev, vector)) { continue; } - vq = virtio_get_queue(vdev, queue_no); - notifier = virtio_queue_get_guest_notifier(vq); if (k->guest_notifier_pending) { if (k->guest_notifier_pending(vdev, queue_no)) { msix_set_pending(dev, vector); @@ -948,6 +1146,34 @@ static void virtio_pci_vector_poll(PCIDevice *dev, msix_set_pending(dev, vector); } } + /* poll the config intr */ + ret = virtio_pci_get_notifier(proxy, VIRTIO_CONFIG_IRQ_IDX, ¬ifier, + &vector); + if (ret < 0) { + return; + } + if (vector < vector_start || vector >= vector_end || + !msix_is_masked(dev, vector)) { + return; + } + if (k->guest_notifier_pending) { + if (k->guest_notifier_pending(vdev, VIRTIO_CONFIG_IRQ_IDX)) { + msix_set_pending(dev, vector); + } + } else if (event_notifier_test_and_clear(notifier)) { + msix_set_pending(dev, vector); + } +} + +void virtio_pci_set_guest_notifier_fd_handler(VirtIODevice *vdev, VirtQueue *vq, + int n, bool assign, + bool with_irqfd) +{ + if (n == VIRTIO_CONFIG_IRQ_IDX) { + virtio_config_set_guest_notifier_fd_handler(vdev, assign, with_irqfd); + } else { + virtio_queue_set_guest_notifier_fd_handler(vq, assign, with_irqfd); + } } static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign, @@ -956,17 +1182,25 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign, VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); - VirtQueue *vq = virtio_get_queue(vdev, n); - EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); + VirtQueue *vq = NULL; + EventNotifier *notifier = NULL; + + if (n == VIRTIO_CONFIG_IRQ_IDX) { + notifier = virtio_config_get_guest_notifier(vdev); + } else { + vq = virtio_get_queue(vdev, n); + notifier = virtio_queue_get_guest_notifier(vq); + } if (assign) { int r = event_notifier_init(notifier, 0); if (r < 0) { return r; } - virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); + virtio_pci_set_guest_notifier_fd_handler(vdev, vq, n, true, with_irqfd); } else { - virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); + virtio_pci_set_guest_notifier_fd_handler(vdev, vq, n, false, + with_irqfd); event_notifier_cleanup(notifier); } @@ -996,18 +1230,26 @@ static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign) nvqs = MIN(nvqs, VIRTIO_QUEUE_MAX); - /* When deassigning, pass a consistent nvqs value - * to avoid leaking notifiers. + /* + * When deassigning, pass a consistent nvqs value to avoid leaking + * notifiers. But first check we've actually been configured, exit + * early if we haven't. */ + if (!assign && !proxy->nvqs_with_notifiers) { + return 0; + } assert(assign || nvqs == proxy->nvqs_with_notifiers); proxy->nvqs_with_notifiers = nvqs; /* Must unset vector notifier while guest notifier is still assigned */ - if ((proxy->vector_irqfd || k->guest_notifier_mask) && !assign) { + if ((proxy->vector_irqfd || + (vdev->use_guest_notifier_mask && k->guest_notifier_mask)) && + !assign) { msix_unset_vector_notifiers(&proxy->pci_dev); if (proxy->vector_irqfd) { - kvm_virtio_pci_vector_release(proxy, nvqs); + kvm_virtio_pci_vector_vq_release(proxy, nvqs); + kvm_virtio_pci_vector_config_release(proxy); g_free(proxy->vector_irqfd); proxy->vector_irqfd = NULL; } @@ -1023,20 +1265,30 @@ static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign) goto assign_error; } } - + r = virtio_pci_set_guest_notifier(d, VIRTIO_CONFIG_IRQ_IDX, assign, + with_irqfd); + if (r < 0) { + goto config_assign_error; + } /* Must set vector notifier after guest notifier has been assigned */ - if ((with_irqfd || k->guest_notifier_mask) && assign) { + if ((with_irqfd || + (vdev->use_guest_notifier_mask && k->guest_notifier_mask)) && + assign) { if (with_irqfd) { proxy->vector_irqfd = g_malloc0(sizeof(*proxy->vector_irqfd) * msix_nr_vectors_allocated(&proxy->pci_dev)); - r = kvm_virtio_pci_vector_use(proxy, nvqs); + r = kvm_virtio_pci_vector_vq_use(proxy, nvqs); if (r < 0) { - goto assign_error; + goto config_assign_error; + } + r = kvm_virtio_pci_vector_config_use(proxy); + if (r < 0) { + goto config_error; } } - r = msix_set_vector_notifiers(&proxy->pci_dev, - virtio_pci_vector_unmask, + + r = msix_set_vector_notifiers(&proxy->pci_dev, virtio_pci_vector_unmask, virtio_pci_vector_mask, virtio_pci_vector_poll); if (r < 0) { @@ -1049,15 +1301,23 @@ static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign) notifiers_error: if (with_irqfd) { assert(assign); - kvm_virtio_pci_vector_release(proxy, nvqs); + kvm_virtio_pci_vector_vq_release(proxy, nvqs); } - +config_error: + if (with_irqfd) { + kvm_virtio_pci_vector_config_release(proxy); + } +config_assign_error: + virtio_pci_set_guest_notifier(d, VIRTIO_CONFIG_IRQ_IDX, !assign, + with_irqfd); assign_error: /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */ assert(assign); while (--n >= 0) { virtio_pci_set_guest_notifier(d, n, !assign, with_irqfd); } + g_free(proxy->vector_irqfd); + proxy->vector_irqfd = NULL; return r; } @@ -1164,6 +1424,56 @@ static int virtio_pci_add_mem_cap(VirtIOPCIProxy *proxy, return offset; } +static void virtio_pci_set_vector(VirtIODevice *vdev, + VirtIOPCIProxy *proxy, + int queue_no, uint16_t old_vector, + uint16_t new_vector) +{ + bool kvm_irqfd = (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) && + msix_enabled(&proxy->pci_dev) && kvm_msi_via_irqfd_enabled(); + + if (new_vector == old_vector) { + return; + } + + /* + * If the device uses irqfd and the vector changes after DRIVER_OK is + * set, we need to release the old vector and set up the new one. + * Otherwise just need to set the new vector on the device. + */ + if (kvm_irqfd && old_vector != VIRTIO_NO_VECTOR) { + kvm_virtio_pci_vector_release_one(proxy, queue_no); + } + /* Set the new vector on the device. */ + if (queue_no == VIRTIO_CONFIG_IRQ_IDX) { + vdev->config_vector = new_vector; + } else { + virtio_queue_set_vector(vdev, queue_no, new_vector); + } + /* If the new vector changed need to set it up. */ + if (kvm_irqfd && new_vector != VIRTIO_NO_VECTOR) { + kvm_virtio_pci_vector_use_one(proxy, queue_no); + } +} + +int virtio_pci_add_shm_cap(VirtIOPCIProxy *proxy, + uint8_t bar, uint64_t offset, uint64_t length, + uint8_t id) +{ + struct virtio_pci_cap64 cap = { + .cap.cap_len = sizeof cap, + .cap.cfg_type = VIRTIO_PCI_CAP_SHARED_MEMORY_CFG, + }; + + cap.cap.bar = bar; + cap.cap.length = cpu_to_le32(length); + cap.length_hi = cpu_to_le32(length >> 32); + cap.cap.offset = cpu_to_le32(offset); + cap.offset_hi = cpu_to_le32(offset >> 32); + cap.cap.id = id; + return virtio_pci_add_mem_cap(proxy, &cap.cap); +} + static uint64_t virtio_pci_common_read(void *opaque, hwaddr addr, unsigned size) { @@ -1246,6 +1556,9 @@ static uint64_t virtio_pci_common_read(void *opaque, hwaddr addr, case VIRTIO_PCI_COMMON_Q_USEDHI: val = proxy->vqs[vdev->queue_sel].used[1]; break; + case VIRTIO_PCI_COMMON_Q_RESET: + val = proxy->vqs[vdev->queue_sel].reset; + break; default: val = 0; } @@ -1258,6 +1571,7 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, { VirtIOPCIProxy *proxy = opaque; VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + uint16_t vector; if (vdev == NULL) { return; @@ -1279,12 +1593,17 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, } break; case VIRTIO_PCI_COMMON_MSIX: - msix_vector_unuse(&proxy->pci_dev, vdev->config_vector); + if (vdev->config_vector != VIRTIO_NO_VECTOR) { + msix_vector_unuse(&proxy->pci_dev, vdev->config_vector); + } /* Make it possible for guest to discover an error took place. */ - if (msix_vector_use(&proxy->pci_dev, val) < 0) { + if (val < proxy->nvectors) { + msix_vector_use(&proxy->pci_dev, val); + } else { val = VIRTIO_NO_VECTOR; } - vdev->config_vector = val; + virtio_pci_set_vector(vdev, proxy, VIRTIO_CONFIG_IRQ_IDX, + vdev->config_vector, val); break; case VIRTIO_PCI_COMMON_STATUS: if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) { @@ -1311,15 +1630,20 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, proxy->vqs[vdev->queue_sel].num = val; virtio_queue_set_num(vdev, vdev->queue_sel, proxy->vqs[vdev->queue_sel].num); + virtio_init_region_cache(vdev, vdev->queue_sel); break; case VIRTIO_PCI_COMMON_Q_MSIX: - msix_vector_unuse(&proxy->pci_dev, - virtio_queue_vector(vdev, vdev->queue_sel)); + vector = virtio_queue_vector(vdev, vdev->queue_sel); + if (vector != VIRTIO_NO_VECTOR) { + msix_vector_unuse(&proxy->pci_dev, vector); + } /* Make it possible for guest to discover an error took place. */ - if (msix_vector_use(&proxy->pci_dev, val) < 0) { + if (val < proxy->nvectors) { + msix_vector_use(&proxy->pci_dev, val); + } else { val = VIRTIO_NO_VECTOR; } - virtio_queue_set_vector(vdev, vdev->queue_sel, val); + virtio_pci_set_vector(vdev, proxy, vdev->queue_sel, vector, val); break; case VIRTIO_PCI_COMMON_Q_ENABLE: if (val == 1) { @@ -1333,6 +1657,8 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, ((uint64_t)proxy->vqs[vdev->queue_sel].used[1]) << 32 | proxy->vqs[vdev->queue_sel].used[0]); proxy->vqs[vdev->queue_sel].enabled = 1; + proxy->vqs[vdev->queue_sel].reset = 0; + virtio_queue_enable(vdev, vdev->queue_sel); } else { virtio_error(vdev, "wrong value for queue_enable %"PRIx64, val); } @@ -1355,6 +1681,16 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, case VIRTIO_PCI_COMMON_Q_USEDHI: proxy->vqs[vdev->queue_sel].used[1] = val; break; + case VIRTIO_PCI_COMMON_Q_RESET: + if (val == 1) { + proxy->vqs[vdev->queue_sel].reset = 1; + + virtio_queue_reset(vdev, vdev->queue_sel); + + proxy->vqs[vdev->queue_sel].reset = 0; + proxy->vqs[vdev->queue_sel].enabled = 0; + } + break; default: break; } @@ -1626,7 +1962,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY; uint8_t *config; uint32_t size; - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + VirtIODevice *vdev = virtio_bus_get_device(bus); /* * Virtio capabilities present without @@ -1670,7 +2006,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) if (virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) { error_setg(errp, "VIRTIO_F_IOMMU_PLATFORM was supported by" " neither legacy nor transitional device"); - return ; + return; } /* * Legacy and transitional devices use specific subsystem IDs. @@ -1678,12 +2014,15 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) * is set to PCI_SUBVENDOR_ID_REDHAT_QUMRANET by default. */ pci_set_word(config + PCI_SUBSYSTEM_ID, virtio_bus_get_vdev_id(bus)); + if (proxy->trans_devid) { + pci_config_set_device_id(config, proxy->trans_devid); + } } else { /* pure virtio-1.0 */ pci_set_word(config + PCI_VENDOR_ID, PCI_VENDOR_ID_REDHAT_QUMRANET); pci_set_word(config + PCI_DEVICE_ID, - 0x1040 + virtio_bus_get_vdev_id(bus)); + PCI_DEVICE_ID_VIRTIO_10_BASE + virtio_bus_get_vdev_id(bus)); pci_config_set_revision(config, 1); } config[PCI_INTERRUPT_PIN] = 1; @@ -1797,10 +2136,6 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) bool pcie_port = pci_bus_is_express(pci_get_bus(pci_dev)) && !pci_bus_is_root(pci_get_bus(pci_dev)); - if (kvm_enabled() && !kvm_has_many_ioeventfds()) { - proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; - } - /* fd-based ioevents can't be synchronized in record/replay */ if (replay_mode != REPLAY_MODE_NONE) { proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; @@ -1942,20 +2277,27 @@ static void virtio_pci_reset(DeviceState *qdev) { VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev); VirtioBusState *bus = VIRTIO_BUS(&proxy->bus); - PCIDevice *dev = PCI_DEVICE(qdev); int i; - virtio_pci_stop_ioeventfd(proxy); virtio_bus_reset(bus); msix_unuse_all_vectors(&proxy->pci_dev); for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { proxy->vqs[i].enabled = 0; + proxy->vqs[i].reset = 0; proxy->vqs[i].num = 0; proxy->vqs[i].desc[0] = proxy->vqs[i].desc[1] = 0; proxy->vqs[i].avail[0] = proxy->vqs[i].avail[1] = 0; proxy->vqs[i].used[0] = proxy->vqs[i].used[1] = 0; } +} + +static void virtio_pci_bus_reset_hold(Object *obj) +{ + PCIDevice *dev = PCI_DEVICE(obj); + DeviceState *qdev = DEVICE(obj); + + virtio_pci_reset(qdev); if (pci_is_express(dev)) { pcie_cap_deverr_reset(dev); @@ -2014,6 +2356,7 @@ static void virtio_pci_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); device_class_set_props(dc, virtio_pci_properties); k->realize = virtio_pci_realize; @@ -2023,7 +2366,7 @@ static void virtio_pci_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_OTHERS; device_class_set_parent_realize(dc, virtio_pci_dc_realize, &vpciklass->parent_dc_realize); - dc->reset = virtio_pci_reset; + rc->phases.hold = virtio_pci_bus_reset_hold; } static const TypeInfo virtio_pci_info = { @@ -2081,6 +2424,7 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t) .parent = t->parent ? t->parent : TYPE_VIRTIO_PCI, .instance_size = t->instance_size, .instance_init = t->instance_init, + .instance_finalize = t->instance_finalize, .class_size = t->class_size, .abstract = true, .interfaces = t->interfaces, diff --git a/hw/virtio/virtio-pmem-pci.c b/hw/virtio/virtio-pmem-pci.c index 2b2a0b1eae..cfe7f3b67c 100644 --- a/hw/virtio/virtio-pmem-pci.c +++ b/hw/virtio/virtio-pmem-pci.c @@ -42,7 +42,7 @@ static MemoryRegion *virtio_pmem_pci_get_memory_region(MemoryDeviceState *md, Error **errp) { VirtIOPMEMPCI *pci_pmem = VIRTIO_PMEM_PCI(md); - VirtIOPMEM *pmem = VIRTIO_PMEM(&pci_pmem->vdev); + VirtIOPMEM *pmem = &pci_pmem->vdev; VirtIOPMEMClass *vpc = VIRTIO_PMEM_GET_CLASS(pmem); return vpc->get_memory_region(pmem, errp); @@ -52,7 +52,7 @@ static uint64_t virtio_pmem_pci_get_plugged_size(const MemoryDeviceState *md, Error **errp) { VirtIOPMEMPCI *pci_pmem = VIRTIO_PMEM_PCI(md); - VirtIOPMEM *pmem = VIRTIO_PMEM(&pci_pmem->vdev); + VirtIOPMEM *pmem = &pci_pmem->vdev; VirtIOPMEMClass *vpc = VIRTIO_PMEM_GET_CLASS(pmem); MemoryRegion *mr = vpc->get_memory_region(pmem, errp); @@ -65,12 +65,11 @@ static void virtio_pmem_pci_fill_device_info(const MemoryDeviceState *md, { VirtioPMEMDeviceInfo *vi = g_new0(VirtioPMEMDeviceInfo, 1); VirtIOPMEMPCI *pci_pmem = VIRTIO_PMEM_PCI(md); - VirtIOPMEM *pmem = VIRTIO_PMEM(&pci_pmem->vdev); + VirtIOPMEM *pmem = &pci_pmem->vdev; VirtIOPMEMClass *vpc = VIRTIO_PMEM_GET_CLASS(pmem); DeviceState *dev = DEVICE(md); if (dev->id) { - vi->has_id = true; vi->id = g_strdup(dev->id); } @@ -90,8 +89,6 @@ static void virtio_pmem_pci_class_init(ObjectClass *klass, void *data) k->realize = virtio_pmem_pci_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_PMEM; pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; pcidev_k->class_id = PCI_CLASS_OTHERS; @@ -113,13 +110,10 @@ static void virtio_pmem_pci_instance_init(Object *obj) static const VirtioPCIDeviceTypeInfo virtio_pmem_pci_info = { .base_name = TYPE_VIRTIO_PMEM_PCI, .generic_name = "virtio-pmem-pci", + .parent = TYPE_VIRTIO_MD_PCI, .instance_size = sizeof(VirtIOPMEMPCI), .instance_init = virtio_pmem_pci_instance_init, .class_init = virtio_pmem_pci_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_MEMORY_DEVICE }, - { } - }, }; static void virtio_pmem_pci_register_types(void) diff --git a/hw/virtio/virtio-pmem-pci.h b/hw/virtio/virtio-pmem-pci.h index 63cfe727f7..88b01ce2db 100644 --- a/hw/virtio/virtio-pmem-pci.h +++ b/hw/virtio/virtio-pmem-pci.h @@ -14,21 +14,21 @@ #ifndef QEMU_VIRTIO_PMEM_PCI_H #define QEMU_VIRTIO_PMEM_PCI_H -#include "hw/virtio/virtio-pci.h" +#include "hw/virtio/virtio-md-pci.h" #include "hw/virtio/virtio-pmem.h" #include "qom/object.h" typedef struct VirtIOPMEMPCI VirtIOPMEMPCI; /* - * virtio-pmem-pci: This extends VirtioPCIProxy. + * virtio-pmem-pci: This extends VirtIOMDPCI. */ #define TYPE_VIRTIO_PMEM_PCI "virtio-pmem-pci-base" DECLARE_INSTANCE_CHECKER(VirtIOPMEMPCI, VIRTIO_PMEM_PCI, TYPE_VIRTIO_PMEM_PCI) struct VirtIOPMEMPCI { - VirtIOPCIProxy parent_obj; + VirtIOMDPCI parent_obj; VirtIOPMEM vdev; }; diff --git a/hw/virtio/virtio-pmem.c b/hw/virtio/virtio-pmem.c index a1abfe0e1b..c3512c2dae 100644 --- a/hw/virtio/virtio-pmem.c +++ b/hw/virtio/virtio-pmem.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/error-report.h" +#include "qemu/iov.h" #include "qemu/main-loop.h" #include "hw/virtio/virtio-pmem.h" #include "hw/qdev-properties.h" @@ -69,7 +70,6 @@ static void virtio_pmem_flush(VirtIODevice *vdev, VirtQueue *vq) VirtIODeviceRequest *req_data; VirtIOPMEM *pmem = VIRTIO_PMEM(vdev); HostMemoryBackend *backend = MEMORY_BACKEND(pmem->memdev); - ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context()); trace_virtio_pmem_flush_request(); req_data = virtqueue_pop(vq, sizeof(VirtIODeviceRequest)); @@ -87,7 +87,7 @@ static void virtio_pmem_flush(VirtIODevice *vdev, VirtQueue *vq) req_data->fd = memory_region_get_fd(&backend->mr); req_data->pmem = pmem; req_data->vdev = vdev; - thread_pool_submit_aio(pool, worker_cb, req_data, done_cb, req_data); + thread_pool_submit_aio(worker_cb, req_data, done_cb, req_data); } static void virtio_pmem_get_config(VirtIODevice *vdev, uint8_t *config) diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c new file mode 100644 index 0000000000..1dd96ed20f --- /dev/null +++ b/hw/virtio/virtio-qmp.c @@ -0,0 +1,837 @@ +/* + * Virtio QMP helpers + * + * Copyright IBM, Corp. 2007 + * + * Authors: + * Anthony Liguori + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "virtio-qmp.h" + +#include "qapi/error.h" +#include "qapi/qapi-commands-virtio.h" +#include "qapi/qapi-commands-qom.h" +#include "qapi/qmp/qobject.h" +#include "qapi/qmp/qjson.h" +#include "hw/virtio/vhost-user.h" + +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/vhost_types.h" +#include "standard-headers/linux/virtio_blk.h" +#include "standard-headers/linux/virtio_console.h" +#include "standard-headers/linux/virtio_gpu.h" +#include "standard-headers/linux/virtio_net.h" +#include "standard-headers/linux/virtio_scsi.h" +#include "standard-headers/linux/virtio_i2c.h" +#include "standard-headers/linux/virtio_balloon.h" +#include "standard-headers/linux/virtio_iommu.h" +#include "standard-headers/linux/virtio_mem.h" +#include "standard-headers/linux/virtio_vsock.h" +#include "standard-headers/linux/virtio_gpio.h" + +#include CONFIG_DEVICES + +#define FEATURE_ENTRY(name, desc) (qmp_virtio_feature_map_t) \ + { .virtio_bit = name, .feature_desc = desc } + +/* Virtio transport features mapping */ +static const qmp_virtio_feature_map_t virtio_transport_map[] = { + /* Virtio device transport features */ +#ifndef VIRTIO_CONFIG_NO_LEGACY + FEATURE_ENTRY(VIRTIO_F_NOTIFY_ON_EMPTY, \ + "VIRTIO_F_NOTIFY_ON_EMPTY: Notify when device runs out of avail. " + "descs. on VQ"), + FEATURE_ENTRY(VIRTIO_F_ANY_LAYOUT, \ + "VIRTIO_F_ANY_LAYOUT: Device accepts arbitrary desc. layouts"), +#endif /* !VIRTIO_CONFIG_NO_LEGACY */ + FEATURE_ENTRY(VIRTIO_F_VERSION_1, \ + "VIRTIO_F_VERSION_1: Device compliant for v1 spec (legacy)"), + FEATURE_ENTRY(VIRTIO_F_IOMMU_PLATFORM, \ + "VIRTIO_F_IOMMU_PLATFORM: Device can be used on IOMMU platform"), + FEATURE_ENTRY(VIRTIO_F_RING_PACKED, \ + "VIRTIO_F_RING_PACKED: Device supports packed VQ layout"), + FEATURE_ENTRY(VIRTIO_F_IN_ORDER, \ + "VIRTIO_F_IN_ORDER: Device uses buffers in same order as made " + "available by driver"), + FEATURE_ENTRY(VIRTIO_F_ORDER_PLATFORM, \ + "VIRTIO_F_ORDER_PLATFORM: Memory accesses ordered by platform"), + FEATURE_ENTRY(VIRTIO_F_SR_IOV, \ + "VIRTIO_F_SR_IOV: Device supports single root I/O virtualization"), + FEATURE_ENTRY(VIRTIO_F_RING_RESET, \ + "VIRTIO_F_RING_RESET: Driver can reset a queue individually"), + /* Virtio ring transport features */ + FEATURE_ENTRY(VIRTIO_RING_F_INDIRECT_DESC, \ + "VIRTIO_RING_F_INDIRECT_DESC: Indirect descriptors supported"), + FEATURE_ENTRY(VIRTIO_RING_F_EVENT_IDX, \ + "VIRTIO_RING_F_EVENT_IDX: Used & avail. event fields enabled"), + { -1, "" } +}; + +/* Vhost-user protocol features mapping */ +static const qmp_virtio_feature_map_t vhost_user_protocol_map[] = { + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_MQ, \ + "VHOST_USER_PROTOCOL_F_MQ: Multiqueue protocol supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_LOG_SHMFD, \ + "VHOST_USER_PROTOCOL_F_LOG_SHMFD: Shared log memory fd supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_RARP, \ + "VHOST_USER_PROTOCOL_F_RARP: Vhost-user back-end RARP broadcasting " + "supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_REPLY_ACK, \ + "VHOST_USER_PROTOCOL_F_REPLY_ACK: Requested operation status ack. " + "supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_NET_MTU, \ + "VHOST_USER_PROTOCOL_F_NET_MTU: Expose host MTU to guest supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_BACKEND_REQ, \ + "VHOST_USER_PROTOCOL_F_BACKEND_REQ: Socket fd for back-end initiated " + "requests supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_CROSS_ENDIAN, \ + "VHOST_USER_PROTOCOL_F_CROSS_ENDIAN: Endianness of VQs for legacy " + "devices supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_CRYPTO_SESSION, \ + "VHOST_USER_PROTOCOL_F_CRYPTO_SESSION: Session creation for crypto " + "operations supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_PAGEFAULT, \ + "VHOST_USER_PROTOCOL_F_PAGEFAULT: Request servicing on userfaultfd " + "for accessed pages supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_CONFIG, \ + "VHOST_USER_PROTOCOL_F_CONFIG: Vhost-user messaging for virtio " + "device configuration space supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_BACKEND_SEND_FD, \ + "VHOST_USER_PROTOCOL_F_BACKEND_SEND_FD: Backend fd communication " + "channel supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_HOST_NOTIFIER, \ + "VHOST_USER_PROTOCOL_F_HOST_NOTIFIER: Host notifiers for specified " + "VQs supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD, \ + "VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD: Shared inflight I/O buffers " + "supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_RESET_DEVICE, \ + "VHOST_USER_PROTOCOL_F_RESET_DEVICE: Disabling all rings and " + "resetting internal device state supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS, \ + "VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS: In-band messaging " + "supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS, \ + "VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS: Configuration for " + "memory slots supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_STATUS, \ + "VHOST_USER_PROTOCOL_F_STATUS: Querying and notifying back-end " + "device status supported"), + { -1, "" } +}; + +/* virtio device configuration statuses */ +static const qmp_virtio_feature_map_t virtio_config_status_map[] = { + FEATURE_ENTRY(VIRTIO_CONFIG_S_DRIVER_OK, \ + "VIRTIO_CONFIG_S_DRIVER_OK: Driver setup and ready"), + FEATURE_ENTRY(VIRTIO_CONFIG_S_FEATURES_OK, \ + "VIRTIO_CONFIG_S_FEATURES_OK: Feature negotiation complete"), + FEATURE_ENTRY(VIRTIO_CONFIG_S_DRIVER, \ + "VIRTIO_CONFIG_S_DRIVER: Guest OS compatible with device"), + FEATURE_ENTRY(VIRTIO_CONFIG_S_NEEDS_RESET, \ + "VIRTIO_CONFIG_S_NEEDS_RESET: Irrecoverable error, device needs " + "reset"), + FEATURE_ENTRY(VIRTIO_CONFIG_S_FAILED, \ + "VIRTIO_CONFIG_S_FAILED: Error in guest, device failed"), + FEATURE_ENTRY(VIRTIO_CONFIG_S_ACKNOWLEDGE, \ + "VIRTIO_CONFIG_S_ACKNOWLEDGE: Valid virtio device found"), + { -1, "" } +}; + +/* virtio-blk features mapping */ +#ifdef CONFIG_VIRTIO_BLK +static const qmp_virtio_feature_map_t virtio_blk_feature_map[] = { + FEATURE_ENTRY(VIRTIO_BLK_F_SIZE_MAX, \ + "VIRTIO_BLK_F_SIZE_MAX: Max segment size is size_max"), + FEATURE_ENTRY(VIRTIO_BLK_F_SEG_MAX, \ + "VIRTIO_BLK_F_SEG_MAX: Max segments in a request is seg_max"), + FEATURE_ENTRY(VIRTIO_BLK_F_GEOMETRY, \ + "VIRTIO_BLK_F_GEOMETRY: Legacy geometry available"), + FEATURE_ENTRY(VIRTIO_BLK_F_RO, \ + "VIRTIO_BLK_F_RO: Device is read-only"), + FEATURE_ENTRY(VIRTIO_BLK_F_BLK_SIZE, \ + "VIRTIO_BLK_F_BLK_SIZE: Block size of disk available"), + FEATURE_ENTRY(VIRTIO_BLK_F_TOPOLOGY, \ + "VIRTIO_BLK_F_TOPOLOGY: Topology information available"), + FEATURE_ENTRY(VIRTIO_BLK_F_MQ, \ + "VIRTIO_BLK_F_MQ: Multiqueue supported"), + FEATURE_ENTRY(VIRTIO_BLK_F_DISCARD, \ + "VIRTIO_BLK_F_DISCARD: Discard command supported"), + FEATURE_ENTRY(VIRTIO_BLK_F_WRITE_ZEROES, \ + "VIRTIO_BLK_F_WRITE_ZEROES: Write zeroes command supported"), + FEATURE_ENTRY(VIRTIO_BLK_F_SECURE_ERASE, \ + "VIRTIO_BLK_F_SECURE_ERASE: Secure erase supported"), + FEATURE_ENTRY(VIRTIO_BLK_F_ZONED, \ + "VIRTIO_BLK_F_ZONED: Zoned block devices"), +#ifndef VIRTIO_BLK_NO_LEGACY + FEATURE_ENTRY(VIRTIO_BLK_F_BARRIER, \ + "VIRTIO_BLK_F_BARRIER: Request barriers supported"), + FEATURE_ENTRY(VIRTIO_BLK_F_SCSI, \ + "VIRTIO_BLK_F_SCSI: SCSI packet commands supported"), + FEATURE_ENTRY(VIRTIO_BLK_F_FLUSH, \ + "VIRTIO_BLK_F_FLUSH: Flush command supported"), + FEATURE_ENTRY(VIRTIO_BLK_F_CONFIG_WCE, \ + "VIRTIO_BLK_F_CONFIG_WCE: Cache writeback and writethrough modes " + "supported"), +#endif /* !VIRTIO_BLK_NO_LEGACY */ + FEATURE_ENTRY(VHOST_F_LOG_ALL, \ + "VHOST_F_LOG_ALL: Logging write descriptors supported"), + FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \ + "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features " + "negotiation supported"), + { -1, "" } +}; +#endif + +/* virtio-serial features mapping */ +#ifdef CONFIG_VIRTIO_SERIAL +static const qmp_virtio_feature_map_t virtio_serial_feature_map[] = { + FEATURE_ENTRY(VIRTIO_CONSOLE_F_SIZE, \ + "VIRTIO_CONSOLE_F_SIZE: Host providing console size"), + FEATURE_ENTRY(VIRTIO_CONSOLE_F_MULTIPORT, \ + "VIRTIO_CONSOLE_F_MULTIPORT: Multiple ports for device supported"), + FEATURE_ENTRY(VIRTIO_CONSOLE_F_EMERG_WRITE, \ + "VIRTIO_CONSOLE_F_EMERG_WRITE: Emergency write supported"), + { -1, "" } +}; +#endif + +/* virtio-gpu features mapping */ +#ifdef CONFIG_VIRTIO_GPU +static const qmp_virtio_feature_map_t virtio_gpu_feature_map[] = { + FEATURE_ENTRY(VIRTIO_GPU_F_VIRGL, \ + "VIRTIO_GPU_F_VIRGL: Virgl 3D mode supported"), + FEATURE_ENTRY(VIRTIO_GPU_F_EDID, \ + "VIRTIO_GPU_F_EDID: EDID metadata supported"), + FEATURE_ENTRY(VIRTIO_GPU_F_RESOURCE_UUID, \ + "VIRTIO_GPU_F_RESOURCE_UUID: Resource UUID assigning supported"), + FEATURE_ENTRY(VIRTIO_GPU_F_RESOURCE_BLOB, \ + "VIRTIO_GPU_F_RESOURCE_BLOB: Size-based blob resources supported"), + FEATURE_ENTRY(VIRTIO_GPU_F_CONTEXT_INIT, \ + "VIRTIO_GPU_F_CONTEXT_INIT: Context types and synchronization " + "timelines supported"), + FEATURE_ENTRY(VHOST_F_LOG_ALL, \ + "VHOST_F_LOG_ALL: Logging write descriptors supported"), + FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \ + "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features " + "negotiation supported"), + { -1, "" } +}; +#endif + +/* virtio-input features mapping */ +#ifdef CONFIG_VIRTIO_INPUT +static const qmp_virtio_feature_map_t virtio_input_feature_map[] = { + FEATURE_ENTRY(VHOST_F_LOG_ALL, \ + "VHOST_F_LOG_ALL: Logging write descriptors supported"), + FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \ + "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features " + "negotiation supported"), + { -1, "" } +}; +#endif + +/* virtio-net features mapping */ +#ifdef CONFIG_VIRTIO_NET +static const qmp_virtio_feature_map_t virtio_net_feature_map[] = { + FEATURE_ENTRY(VIRTIO_NET_F_CSUM, \ + "VIRTIO_NET_F_CSUM: Device handling packets with partial checksum " + "supported"), + FEATURE_ENTRY(VIRTIO_NET_F_GUEST_CSUM, \ + "VIRTIO_NET_F_GUEST_CSUM: Driver handling packets with partial " + "checksum supported"), + FEATURE_ENTRY(VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, \ + "VIRTIO_NET_F_CTRL_GUEST_OFFLOADS: Control channel offloading " + "reconfig. supported"), + FEATURE_ENTRY(VIRTIO_NET_F_MTU, \ + "VIRTIO_NET_F_MTU: Device max MTU reporting supported"), + FEATURE_ENTRY(VIRTIO_NET_F_MAC, \ + "VIRTIO_NET_F_MAC: Device has given MAC address"), + FEATURE_ENTRY(VIRTIO_NET_F_GUEST_TSO4, \ + "VIRTIO_NET_F_GUEST_TSO4: Driver can receive TSOv4"), + FEATURE_ENTRY(VIRTIO_NET_F_GUEST_TSO6, \ + "VIRTIO_NET_F_GUEST_TSO6: Driver can receive TSOv6"), + FEATURE_ENTRY(VIRTIO_NET_F_GUEST_ECN, \ + "VIRTIO_NET_F_GUEST_ECN: Driver can receive TSO with ECN"), + FEATURE_ENTRY(VIRTIO_NET_F_GUEST_UFO, \ + "VIRTIO_NET_F_GUEST_UFO: Driver can receive UFO"), + FEATURE_ENTRY(VIRTIO_NET_F_HOST_TSO4, \ + "VIRTIO_NET_F_HOST_TSO4: Device can receive TSOv4"), + FEATURE_ENTRY(VIRTIO_NET_F_HOST_TSO6, \ + "VIRTIO_NET_F_HOST_TSO6: Device can receive TSOv6"), + FEATURE_ENTRY(VIRTIO_NET_F_HOST_ECN, \ + "VIRTIO_NET_F_HOST_ECN: Device can receive TSO with ECN"), + FEATURE_ENTRY(VIRTIO_NET_F_HOST_UFO, \ + "VIRTIO_NET_F_HOST_UFO: Device can receive UFO"), + FEATURE_ENTRY(VIRTIO_NET_F_MRG_RXBUF, \ + "VIRTIO_NET_F_MRG_RXBUF: Driver can merge receive buffers"), + FEATURE_ENTRY(VIRTIO_NET_F_STATUS, \ + "VIRTIO_NET_F_STATUS: Configuration status field available"), + FEATURE_ENTRY(VIRTIO_NET_F_CTRL_VQ, \ + "VIRTIO_NET_F_CTRL_VQ: Control channel available"), + FEATURE_ENTRY(VIRTIO_NET_F_CTRL_RX, \ + "VIRTIO_NET_F_CTRL_RX: Control channel RX mode supported"), + FEATURE_ENTRY(VIRTIO_NET_F_CTRL_VLAN, \ + "VIRTIO_NET_F_CTRL_VLAN: Control channel VLAN filtering supported"), + FEATURE_ENTRY(VIRTIO_NET_F_CTRL_RX_EXTRA, \ + "VIRTIO_NET_F_CTRL_RX_EXTRA: Extra RX mode control supported"), + FEATURE_ENTRY(VIRTIO_NET_F_GUEST_ANNOUNCE, \ + "VIRTIO_NET_F_GUEST_ANNOUNCE: Driver sending gratuitous packets " + "supported"), + FEATURE_ENTRY(VIRTIO_NET_F_MQ, \ + "VIRTIO_NET_F_MQ: Multiqueue with automatic receive steering " + "supported"), + FEATURE_ENTRY(VIRTIO_NET_F_CTRL_MAC_ADDR, \ + "VIRTIO_NET_F_CTRL_MAC_ADDR: MAC address set through control " + "channel"), + FEATURE_ENTRY(VIRTIO_NET_F_NOTF_COAL, \ + "VIRTIO_NET_F_NOTF_COAL: Device supports coalescing notifications"), + FEATURE_ENTRY(VIRTIO_NET_F_GUEST_USO4, \ + "VIRTIO_NET_F_GUEST_USO4: Driver can receive USOv4"), + FEATURE_ENTRY(VIRTIO_NET_F_GUEST_USO6, \ + "VIRTIO_NET_F_GUEST_USO4: Driver can receive USOv6"), + FEATURE_ENTRY(VIRTIO_NET_F_HOST_USO, \ + "VIRTIO_NET_F_HOST_USO: Device can receive USO"), + FEATURE_ENTRY(VIRTIO_NET_F_HASH_REPORT, \ + "VIRTIO_NET_F_HASH_REPORT: Hash reporting supported"), + FEATURE_ENTRY(VIRTIO_NET_F_RSS, \ + "VIRTIO_NET_F_RSS: RSS RX steering supported"), + FEATURE_ENTRY(VIRTIO_NET_F_RSC_EXT, \ + "VIRTIO_NET_F_RSC_EXT: Extended coalescing info supported"), + FEATURE_ENTRY(VIRTIO_NET_F_STANDBY, \ + "VIRTIO_NET_F_STANDBY: Device acting as standby for primary " + "device with same MAC addr. supported"), + FEATURE_ENTRY(VIRTIO_NET_F_SPEED_DUPLEX, \ + "VIRTIO_NET_F_SPEED_DUPLEX: Device set linkspeed and duplex"), +#ifndef VIRTIO_NET_NO_LEGACY + FEATURE_ENTRY(VIRTIO_NET_F_GSO, \ + "VIRTIO_NET_F_GSO: Handling GSO-type packets supported"), +#endif /* !VIRTIO_NET_NO_LEGACY */ + FEATURE_ENTRY(VHOST_NET_F_VIRTIO_NET_HDR, \ + "VHOST_NET_F_VIRTIO_NET_HDR: Virtio-net headers for RX and TX " + "packets supported"), + FEATURE_ENTRY(VHOST_F_LOG_ALL, \ + "VHOST_F_LOG_ALL: Logging write descriptors supported"), + FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \ + "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features " + "negotiation supported"), + { -1, "" } +}; +#endif + +/* virtio-scsi features mapping */ +#ifdef CONFIG_VIRTIO_SCSI +static const qmp_virtio_feature_map_t virtio_scsi_feature_map[] = { + FEATURE_ENTRY(VIRTIO_SCSI_F_INOUT, \ + "VIRTIO_SCSI_F_INOUT: Requests including read and writable data " + "buffers supported"), + FEATURE_ENTRY(VIRTIO_SCSI_F_HOTPLUG, \ + "VIRTIO_SCSI_F_HOTPLUG: Reporting and handling hot-plug events " + "supported"), + FEATURE_ENTRY(VIRTIO_SCSI_F_CHANGE, \ + "VIRTIO_SCSI_F_CHANGE: Reporting and handling LUN changes " + "supported"), + FEATURE_ENTRY(VIRTIO_SCSI_F_T10_PI, \ + "VIRTIO_SCSI_F_T10_PI: T10 info included in request header"), + FEATURE_ENTRY(VHOST_F_LOG_ALL, \ + "VHOST_F_LOG_ALL: Logging write descriptors supported"), + FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \ + "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features " + "negotiation supported"), + { -1, "" } +}; +#endif + +/* virtio/vhost-user-fs features mapping */ +#ifdef CONFIG_VHOST_USER_FS +static const qmp_virtio_feature_map_t virtio_fs_feature_map[] = { + FEATURE_ENTRY(VHOST_F_LOG_ALL, \ + "VHOST_F_LOG_ALL: Logging write descriptors supported"), + FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \ + "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features " + "negotiation supported"), + { -1, "" } +}; +#endif + +/* virtio/vhost-user-i2c features mapping */ +#ifdef CONFIG_VIRTIO_I2C_ADAPTER +static const qmp_virtio_feature_map_t virtio_i2c_feature_map[] = { + FEATURE_ENTRY(VIRTIO_I2C_F_ZERO_LENGTH_REQUEST, \ + "VIRTIO_I2C_F_ZERO_LEGNTH_REQUEST: Zero length requests supported"), + FEATURE_ENTRY(VHOST_F_LOG_ALL, \ + "VHOST_F_LOG_ALL: Logging write descriptors supported"), + FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \ + "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features " + "negotiation supported"), + { -1, "" } +}; +#endif + +/* virtio/vhost-vsock features mapping */ +#ifdef CONFIG_VHOST_VSOCK +static const qmp_virtio_feature_map_t virtio_vsock_feature_map[] = { + FEATURE_ENTRY(VIRTIO_VSOCK_F_SEQPACKET, \ + "VIRTIO_VSOCK_F_SEQPACKET: SOCK_SEQPACKET supported"), + FEATURE_ENTRY(VHOST_F_LOG_ALL, \ + "VHOST_F_LOG_ALL: Logging write descriptors supported"), + FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \ + "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features " + "negotiation supported"), + { -1, "" } +}; +#endif + +/* virtio-balloon features mapping */ +#ifdef CONFIG_VIRTIO_BALLOON +static const qmp_virtio_feature_map_t virtio_balloon_feature_map[] = { + FEATURE_ENTRY(VIRTIO_BALLOON_F_MUST_TELL_HOST, \ + "VIRTIO_BALLOON_F_MUST_TELL_HOST: Tell host before reclaiming " + "pages"), + FEATURE_ENTRY(VIRTIO_BALLOON_F_STATS_VQ, \ + "VIRTIO_BALLOON_F_STATS_VQ: Guest memory stats VQ available"), + FEATURE_ENTRY(VIRTIO_BALLOON_F_DEFLATE_ON_OOM, \ + "VIRTIO_BALLOON_F_DEFLATE_ON_OOM: Deflate balloon when guest OOM"), + FEATURE_ENTRY(VIRTIO_BALLOON_F_FREE_PAGE_HINT, \ + "VIRTIO_BALLOON_F_FREE_PAGE_HINT: VQ reporting free pages enabled"), + FEATURE_ENTRY(VIRTIO_BALLOON_F_PAGE_POISON, \ + "VIRTIO_BALLOON_F_PAGE_POISON: Guest page poisoning enabled"), + FEATURE_ENTRY(VIRTIO_BALLOON_F_REPORTING, \ + "VIRTIO_BALLOON_F_REPORTING: Page reporting VQ enabled"), + { -1, "" } +}; +#endif + +/* virtio-crypto features mapping */ +#ifdef CONFIG_VIRTIO_CRYPTO +static const qmp_virtio_feature_map_t virtio_crypto_feature_map[] = { + FEATURE_ENTRY(VHOST_F_LOG_ALL, \ + "VHOST_F_LOG_ALL: Logging write descriptors supported"), + { -1, "" } +}; +#endif + +/* virtio-iommu features mapping */ +#ifdef CONFIG_VIRTIO_IOMMU +static const qmp_virtio_feature_map_t virtio_iommu_feature_map[] = { + FEATURE_ENTRY(VIRTIO_IOMMU_F_INPUT_RANGE, \ + "VIRTIO_IOMMU_F_INPUT_RANGE: Range of available virtual addrs. " + "available"), + FEATURE_ENTRY(VIRTIO_IOMMU_F_DOMAIN_RANGE, \ + "VIRTIO_IOMMU_F_DOMAIN_RANGE: Number of supported domains " + "available"), + FEATURE_ENTRY(VIRTIO_IOMMU_F_MAP_UNMAP, \ + "VIRTIO_IOMMU_F_MAP_UNMAP: Map and unmap requests available"), + FEATURE_ENTRY(VIRTIO_IOMMU_F_BYPASS, \ + "VIRTIO_IOMMU_F_BYPASS: Endpoints not attached to domains are in " + "bypass mode"), + FEATURE_ENTRY(VIRTIO_IOMMU_F_PROBE, \ + "VIRTIO_IOMMU_F_PROBE: Probe requests available"), + FEATURE_ENTRY(VIRTIO_IOMMU_F_MMIO, \ + "VIRTIO_IOMMU_F_MMIO: VIRTIO_IOMMU_MAP_F_MMIO flag available"), + FEATURE_ENTRY(VIRTIO_IOMMU_F_BYPASS_CONFIG, \ + "VIRTIO_IOMMU_F_BYPASS_CONFIG: Bypass field of IOMMU config " + "available"), + { -1, "" } +}; +#endif + +/* virtio-mem features mapping */ +#ifdef CONFIG_VIRTIO_MEM +static const qmp_virtio_feature_map_t virtio_mem_feature_map[] = { +#ifndef CONFIG_ACPI + FEATURE_ENTRY(VIRTIO_MEM_F_ACPI_PXM, \ + "VIRTIO_MEM_F_ACPI_PXM: node_id is an ACPI PXM and is valid"), +#endif /* !CONFIG_ACPI */ + FEATURE_ENTRY(VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE, \ + "VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE: Unplugged memory cannot be " + "accessed"), + { -1, "" } +}; +#endif + +/* virtio-rng features mapping */ +#ifdef CONFIG_VIRTIO_RNG +static const qmp_virtio_feature_map_t virtio_rng_feature_map[] = { + FEATURE_ENTRY(VHOST_F_LOG_ALL, \ + "VHOST_F_LOG_ALL: Logging write descriptors supported"), + FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \ + "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features " + "negotiation supported"), + { -1, "" } +}; +#endif + +/* virtio/vhost-gpio features mapping */ +#ifdef CONFIG_VHOST_USER_GPIO +static const qmp_virtio_feature_map_t virtio_gpio_feature_map[] = { + FEATURE_ENTRY(VIRTIO_GPIO_F_IRQ, \ + "VIRTIO_GPIO_F_IRQ: Device supports interrupts on GPIO lines"), + FEATURE_ENTRY(VHOST_USER_F_PROTOCOL_FEATURES, \ + "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features " + "negotiation supported"), + { -1, "" } +}; +#endif + +#define CONVERT_FEATURES(type, map, is_status, bitmap) \ + ({ \ + type *list = NULL; \ + type *node; \ + for (i = 0; map[i].virtio_bit != -1; i++) { \ + if (is_status) { \ + bit = map[i].virtio_bit; \ + } \ + else { \ + bit = 1ULL << map[i].virtio_bit; \ + } \ + if ((bitmap & bit) == 0) { \ + continue; \ + } \ + node = g_new0(type, 1); \ + node->value = g_strdup(map[i].feature_desc); \ + node->next = list; \ + list = node; \ + bitmap ^= bit; \ + } \ + list; \ + }) + +VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap) +{ + VirtioDeviceStatus *status; + uint8_t bit; + int i; + + status = g_new0(VirtioDeviceStatus, 1); + status->statuses = CONVERT_FEATURES(strList, virtio_config_status_map, + 1, bitmap); + status->has_unknown_statuses = bitmap != 0; + if (status->has_unknown_statuses) { + status->unknown_statuses = bitmap; + } + + return status; +} + +VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap) +{ + VhostDeviceProtocols *vhu_protocols; + uint64_t bit; + int i; + + vhu_protocols = g_new0(VhostDeviceProtocols, 1); + vhu_protocols->protocols = + CONVERT_FEATURES(strList, + vhost_user_protocol_map, 0, bitmap); + vhu_protocols->has_unknown_protocols = bitmap != 0; + if (vhu_protocols->has_unknown_protocols) { + vhu_protocols->unknown_protocols = bitmap; + } + + return vhu_protocols; +} + +VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap) +{ + VirtioDeviceFeatures *features; + uint64_t bit; + int i; + + features = g_new0(VirtioDeviceFeatures, 1); + features->has_dev_features = true; + + /* transport features */ + features->transports = CONVERT_FEATURES(strList, virtio_transport_map, 0, + bitmap); + + /* device features */ + switch (device_id) { +#ifdef CONFIG_VIRTIO_SERIAL + case VIRTIO_ID_CONSOLE: + features->dev_features = + CONVERT_FEATURES(strList, virtio_serial_feature_map, 0, bitmap); + break; +#endif +#ifdef CONFIG_VIRTIO_BLK + case VIRTIO_ID_BLOCK: + features->dev_features = + CONVERT_FEATURES(strList, virtio_blk_feature_map, 0, bitmap); + break; +#endif +#ifdef CONFIG_VIRTIO_GPU + case VIRTIO_ID_GPU: + features->dev_features = + CONVERT_FEATURES(strList, virtio_gpu_feature_map, 0, bitmap); + break; +#endif +#ifdef CONFIG_VIRTIO_NET + case VIRTIO_ID_NET: + features->dev_features = + CONVERT_FEATURES(strList, virtio_net_feature_map, 0, bitmap); + break; +#endif +#ifdef CONFIG_VIRTIO_SCSI + case VIRTIO_ID_SCSI: + features->dev_features = + CONVERT_FEATURES(strList, virtio_scsi_feature_map, 0, bitmap); + break; +#endif +#ifdef CONFIG_VIRTIO_BALLOON + case VIRTIO_ID_BALLOON: + features->dev_features = + CONVERT_FEATURES(strList, virtio_balloon_feature_map, 0, bitmap); + break; +#endif +#ifdef CONFIG_VIRTIO_IOMMU + case VIRTIO_ID_IOMMU: + features->dev_features = + CONVERT_FEATURES(strList, virtio_iommu_feature_map, 0, bitmap); + break; +#endif +#ifdef CONFIG_VIRTIO_INPUT + case VIRTIO_ID_INPUT: + features->dev_features = + CONVERT_FEATURES(strList, virtio_input_feature_map, 0, bitmap); + break; +#endif +#ifdef CONFIG_VHOST_USER_FS + case VIRTIO_ID_FS: + features->dev_features = + CONVERT_FEATURES(strList, virtio_fs_feature_map, 0, bitmap); + break; +#endif +#ifdef CONFIG_VHOST_VSOCK + case VIRTIO_ID_VSOCK: + features->dev_features = + CONVERT_FEATURES(strList, virtio_vsock_feature_map, 0, bitmap); + break; +#endif +#ifdef CONFIG_VIRTIO_CRYPTO + case VIRTIO_ID_CRYPTO: + features->dev_features = + CONVERT_FEATURES(strList, virtio_crypto_feature_map, 0, bitmap); + break; +#endif +#ifdef CONFIG_VIRTIO_MEM + case VIRTIO_ID_MEM: + features->dev_features = + CONVERT_FEATURES(strList, virtio_mem_feature_map, 0, bitmap); + break; +#endif +#ifdef CONFIG_VIRTIO_I2C_ADAPTER + case VIRTIO_ID_I2C_ADAPTER: + features->dev_features = + CONVERT_FEATURES(strList, virtio_i2c_feature_map, 0, bitmap); + break; +#endif +#ifdef CONFIG_VIRTIO_RNG + case VIRTIO_ID_RNG: + features->dev_features = + CONVERT_FEATURES(strList, virtio_rng_feature_map, 0, bitmap); + break; +#endif +#ifdef CONFIG_VHOST_USER_GPIO + case VIRTIO_ID_GPIO: + features->dev_features = + CONVERT_FEATURES(strList, virtio_gpio_feature_map, 0, bitmap); + break; +#endif + /* No features */ + case VIRTIO_ID_9P: + case VIRTIO_ID_PMEM: + case VIRTIO_ID_IOMEM: + case VIRTIO_ID_RPMSG: + case VIRTIO_ID_CLOCK: + case VIRTIO_ID_MAC80211_WLAN: + case VIRTIO_ID_MAC80211_HWSIM: + case VIRTIO_ID_RPROC_SERIAL: + case VIRTIO_ID_MEMORY_BALLOON: + case VIRTIO_ID_CAIF: + case VIRTIO_ID_SIGNAL_DIST: + case VIRTIO_ID_PSTORE: + case VIRTIO_ID_SOUND: + case VIRTIO_ID_BT: + case VIRTIO_ID_RPMB: + case VIRTIO_ID_VIDEO_ENCODER: + case VIRTIO_ID_VIDEO_DECODER: + case VIRTIO_ID_SCMI: + case VIRTIO_ID_NITRO_SEC_MOD: + case VIRTIO_ID_WATCHDOG: + case VIRTIO_ID_CAN: + case VIRTIO_ID_DMABUF: + case VIRTIO_ID_PARAM_SERV: + case VIRTIO_ID_AUDIO_POLICY: + break; + default: + g_assert_not_reached(); + } + + features->has_unknown_dev_features = bitmap != 0; + if (features->has_unknown_dev_features) { + features->unknown_dev_features = bitmap; + } + + return features; +} + +static int query_dev_child(Object *child, void *opaque) +{ + VirtioInfoList **vdevs = opaque; + Object *dev = object_dynamic_cast(child, TYPE_VIRTIO_DEVICE); + if (dev != NULL && DEVICE(dev)->realized) { + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtioInfo *info = g_new(VirtioInfo, 1); + + /* Get canonical path & name of device */ + info->path = object_get_canonical_path(dev); + info->name = g_strdup(vdev->name); + QAPI_LIST_PREPEND(*vdevs, info); + } + return 0; +} + +VirtioInfoList *qmp_x_query_virtio(Error **errp) +{ + VirtioInfoList *vdevs = NULL; + + /* Query the QOM composition tree recursively for virtio devices */ + object_child_foreach_recursive(object_get_root(), query_dev_child, &vdevs); + if (vdevs == NULL) { + error_setg(errp, "No virtio devices found"); + } + return vdevs; +} + +VirtIODevice *qmp_find_virtio_device(const char *path) +{ + /* Verify the canonical path is a realized virtio device */ + Object *dev = object_dynamic_cast(object_resolve_path(path, NULL), + TYPE_VIRTIO_DEVICE); + if (!dev || !DEVICE(dev)->realized) { + return NULL; + } + return VIRTIO_DEVICE(dev); +} + +VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp) +{ + VirtIODevice *vdev; + VirtioStatus *status; + + vdev = qmp_find_virtio_device(path); + if (vdev == NULL) { + error_setg(errp, "Path %s is not a realized VirtIODevice", path); + return NULL; + } + + status = g_new0(VirtioStatus, 1); + status->name = g_strdup(vdev->name); + status->device_id = vdev->device_id; + status->vhost_started = vdev->vhost_started; + status->guest_features = qmp_decode_features(vdev->device_id, + vdev->guest_features); + status->host_features = qmp_decode_features(vdev->device_id, + vdev->host_features); + status->backend_features = qmp_decode_features(vdev->device_id, + vdev->backend_features); + + switch (vdev->device_endian) { + case VIRTIO_DEVICE_ENDIAN_LITTLE: + status->device_endian = g_strdup("little"); + break; + case VIRTIO_DEVICE_ENDIAN_BIG: + status->device_endian = g_strdup("big"); + break; + default: + status->device_endian = g_strdup("unknown"); + break; + } + + status->num_vqs = virtio_get_num_queues(vdev); + status->status = qmp_decode_status(vdev->status); + status->isr = vdev->isr; + status->queue_sel = vdev->queue_sel; + status->vm_running = vdev->vm_running; + status->broken = vdev->broken; + status->disabled = vdev->disabled; + status->use_started = vdev->use_started; + status->started = vdev->started; + status->start_on_kick = vdev->start_on_kick; + status->disable_legacy_check = vdev->disable_legacy_check; + status->bus_name = g_strdup(vdev->bus_name); + status->use_guest_notifier_mask = vdev->use_guest_notifier_mask; + + if (vdev->vhost_started) { + VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); + struct vhost_dev *hdev = vdc->get_vhost(vdev); + + status->vhost_dev = g_new0(VhostStatus, 1); + status->vhost_dev->n_mem_sections = hdev->n_mem_sections; + status->vhost_dev->n_tmp_sections = hdev->n_tmp_sections; + status->vhost_dev->nvqs = hdev->nvqs; + status->vhost_dev->vq_index = hdev->vq_index; + status->vhost_dev->features = + qmp_decode_features(vdev->device_id, hdev->features); + status->vhost_dev->acked_features = + qmp_decode_features(vdev->device_id, hdev->acked_features); + status->vhost_dev->backend_features = + qmp_decode_features(vdev->device_id, hdev->backend_features); + status->vhost_dev->protocol_features = + qmp_decode_protocols(hdev->protocol_features); + status->vhost_dev->max_queues = hdev->max_queues; + status->vhost_dev->backend_cap = hdev->backend_cap; + status->vhost_dev->log_enabled = hdev->log_enabled; + status->vhost_dev->log_size = hdev->log_size; + } + + return status; +} + +VirtVhostQueueStatus *qmp_x_query_virtio_vhost_queue_status(const char *path, + uint16_t queue, + Error **errp) +{ + VirtIODevice *vdev; + VirtVhostQueueStatus *status; + + vdev = qmp_find_virtio_device(path); + if (vdev == NULL) { + error_setg(errp, "Path %s is not a VirtIODevice", path); + return NULL; + } + + if (!vdev->vhost_started) { + error_setg(errp, "Error: vhost device has not started yet"); + return NULL; + } + + VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); + struct vhost_dev *hdev = vdc->get_vhost(vdev); + + if (queue < hdev->vq_index || queue >= hdev->vq_index + hdev->nvqs) { + error_setg(errp, "Invalid vhost virtqueue number %d", queue); + return NULL; + } + + status = g_new0(VirtVhostQueueStatus, 1); + status->name = g_strdup(vdev->name); + status->kick = hdev->vqs[queue].kick; + status->call = hdev->vqs[queue].call; + status->desc = (uintptr_t)hdev->vqs[queue].desc; + status->avail = (uintptr_t)hdev->vqs[queue].avail; + status->used = (uintptr_t)hdev->vqs[queue].used; + status->num = hdev->vqs[queue].num; + status->desc_phys = hdev->vqs[queue].desc_phys; + status->desc_size = hdev->vqs[queue].desc_size; + status->avail_phys = hdev->vqs[queue].avail_phys; + status->avail_size = hdev->vqs[queue].avail_size; + status->used_phys = hdev->vqs[queue].used_phys; + status->used_size = hdev->vqs[queue].used_size; + + return status; +} diff --git a/hw/virtio/virtio-qmp.h b/hw/virtio/virtio-qmp.h new file mode 100644 index 0000000000..245a446a56 --- /dev/null +++ b/hw/virtio/virtio-qmp.h @@ -0,0 +1,23 @@ +/* + * Virtio QMP helpers + * + * Copyright IBM, Corp. 2007 + * + * Authors: + * Anthony Liguori + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_VIRTIO_QMP_H +#define HW_VIRTIO_QMP_H + +#include "qapi/qapi-types-virtio.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/vhost.h" + +VirtIODevice *qmp_find_virtio_device(const char *path); +VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap); +VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap); +VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap); + +#endif diff --git a/hw/virtio/virtio-rng-pci.c b/hw/virtio/virtio-rng-pci.c index 151ece6f94..6e76f8b57b 100644 --- a/hw/virtio/virtio-rng-pci.c +++ b/hw/virtio/virtio-rng-pci.c @@ -13,6 +13,7 @@ #include "hw/virtio/virtio-pci.h" #include "hw/virtio/virtio-rng.h" +#include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/module.h" #include "qom/object.h" @@ -31,11 +32,23 @@ struct VirtIORngPCI { VirtIORNG vdev; }; +static Property virtio_rng_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_END_OF_LIST(), +}; + static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIORngPCI *vrng = VIRTIO_RNG_PCI(vpci_dev); DeviceState *vdev = DEVICE(&vrng->vdev); + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = 2; + } + if (!qdev_realize(vdev, BUS(&vpci_dev->bus), errp)) { return; } @@ -54,6 +67,7 @@ static void virtio_rng_pci_class_init(ObjectClass *klass, void *data) pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_RNG; pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; pcidev_k->class_id = PCI_CLASS_OTHERS; + device_class_set_props(dc, virtio_rng_properties); } static void virtio_rng_initfn(Object *obj) diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 7e12fc03bf..f74efffef7 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -242,7 +242,7 @@ static const VMStateDescription vmstate_virtio_rng = { .name = "virtio-rng", .minimum_version_id = 1, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, diff --git a/hw/virtio/virtio-stub.c b/hw/virtio/virtio-stub.c new file mode 100644 index 0000000000..7ddb22cc5e --- /dev/null +++ b/hw/virtio/virtio-stub.c @@ -0,0 +1,42 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-virtio.h" + +static void *qmp_virtio_unsupported(Error **errp) +{ + error_setg(errp, "Virtio is disabled"); + return NULL; +} + +VirtioInfoList *qmp_x_query_virtio(Error **errp) +{ + return qmp_virtio_unsupported(errp); +} + +VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp) +{ + return qmp_virtio_unsupported(errp); +} + +VirtVhostQueueStatus *qmp_x_query_virtio_vhost_queue_status(const char *path, + uint16_t queue, + Error **errp) +{ + return qmp_virtio_unsupported(errp); +} + +VirtQueueStatus *qmp_x_query_virtio_queue_status(const char *path, + uint16_t queue, + Error **errp) +{ + return qmp_virtio_unsupported(errp); +} + +VirtioQueueElement *qmp_x_query_virtio_queue_element(const char *path, + uint16_t queue, + bool has_index, + uint16_t index, + Error **errp) +{ + return qmp_virtio_unsupported(errp); +} diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 5d607aeaa0..871674f9be 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -13,13 +13,17 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "cpu.h" +#include "qapi/qapi-commands-virtio.h" #include "trace.h" +#include "qemu/defer-call.h" #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/module.h" +#include "qom/object_interfaces.h" +#include "hw/core/cpu.h" #include "hw/virtio/virtio.h" +#include "hw/virtio/vhost.h" #include "migration/qemu-file-types.h" #include "qemu/atomic.h" #include "hw/virtio/virtio-bus.h" @@ -27,7 +31,25 @@ #include "hw/virtio/virtio-access.h" #include "sysemu/dma.h" #include "sysemu/runstate.h" +#include "virtio-qmp.h" + #include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/vhost_types.h" +#include "standard-headers/linux/virtio_blk.h" +#include "standard-headers/linux/virtio_console.h" +#include "standard-headers/linux/virtio_gpu.h" +#include "standard-headers/linux/virtio_net.h" +#include "standard-headers/linux/virtio_scsi.h" +#include "standard-headers/linux/virtio_i2c.h" +#include "standard-headers/linux/virtio_balloon.h" +#include "standard-headers/linux/virtio_iommu.h" +#include "standard-headers/linux/virtio_mem.h" +#include "standard-headers/linux/virtio_vsock.h" + +/* + * Maximum size of virtio device config space + */ +#define VHOST_USER_MAX_CONFIG_SIZE 256 /* * The alignment to use between consumer and producer parts of vring. @@ -203,7 +225,7 @@ static void virtio_virtqueue_reset_region_cache(struct VirtQueue *vq) } } -static void virtio_init_region_cache(VirtIODevice *vdev, int n) +void virtio_init_region_cache(VirtIODevice *vdev, int n) { VirtQueue *vq = &vdev->vq[n]; VRingMemoryRegionCaches *old = vq->vring.caches; @@ -391,6 +413,19 @@ static inline void vring_used_write(VirtQueue *vq, VRingUsedElem *uelem, address_space_cache_invalidate(&caches->used, pa, sizeof(VRingUsedElem)); } +/* Called within rcu_read_lock(). */ +static inline uint16_t vring_used_flags(VirtQueue *vq) +{ + VRingMemoryRegionCaches *caches = vring_get_region_caches(vq); + hwaddr pa = offsetof(VRingUsed, flags); + + if (!caches) { + return 0; + } + + return virtio_lduw_phys_cached(vq->vdev, &caches->used, pa); +} + /* Called within rcu_read_lock(). */ static uint16_t vring_used_idx(VirtQueue *vq) { @@ -922,12 +957,20 @@ static void virtqueue_packed_flush(VirtQueue *vq, unsigned int count) return; } + /* + * For indirect element's 'ndescs' is 1. + * For all other elemment's 'ndescs' is the + * number of descriptors chained by NEXT (as set in virtqueue_packed_pop). + * So When the 'elem' be filled into the descriptor ring, + * The 'idx' of this 'elem' shall be + * the value of 'vq->used_idx' plus the 'ndescs'. + */ + ndescs += vq->used_elems[0].ndescs; for (i = 1; i < count; i++) { - virtqueue_packed_fill_desc(vq, &vq->used_elems[i], i, false); + virtqueue_packed_fill_desc(vq, &vq->used_elems[i], ndescs, false); ndescs += vq->used_elems[i].ndescs; } virtqueue_packed_fill_desc(vq, &vq->used_elems[0], 0, true); - ndescs += vq->used_elems[0].ndescs; vq->inuse -= ndescs; vq->used_idx += ndescs; @@ -963,7 +1006,12 @@ void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, /* Called within rcu_read_lock(). */ static int virtqueue_num_heads(VirtQueue *vq, unsigned int idx) { - uint16_t num_heads = vring_avail_idx(vq) - idx; + uint16_t avail_idx, num_heads; + + /* Use shadow index whenever possible. */ + avail_idx = (vq->shadow_avail_idx != idx) ? vq->shadow_avail_idx + : vring_avail_idx(vq); + num_heads = avail_idx - idx; /* Check it isn't doing very strange things with descriptor numbers. */ if (num_heads > vq->vring.num) { @@ -971,8 +1019,15 @@ static int virtqueue_num_heads(VirtQueue *vq, unsigned int idx) idx, vq->shadow_avail_idx); return -EINVAL; } - /* On success, callers read a descriptor at vq->last_avail_idx. - * Make sure descriptor read does not bypass avail index read. */ + /* + * On success, callers read a descriptor at vq->last_avail_idx. + * Make sure descriptor read does not bypass avail index read. + * + * This is necessary even if we are using a shadow index, since + * the shadow index could have been initialized by calling + * vring_avail_idx() outside of this function, i.e., by a guest + * memory read not accompanied by a barrier. + */ if (num_heads) { smp_rmb(); } @@ -1003,9 +1058,10 @@ enum { VIRTQUEUE_READ_DESC_MORE = 1, /* more buffers in chain */ }; +/* Reads the 'desc->next' descriptor into '*desc'. */ static int virtqueue_split_read_next_desc(VirtIODevice *vdev, VRingDesc *desc, MemoryRegionCache *desc_cache, - unsigned int max, unsigned int *next) + unsigned int max) { /* If this descriptor says it doesn't chain, we're done. */ if (!(desc->flags & VRING_DESC_F_NEXT)) { @@ -1013,16 +1069,12 @@ static int virtqueue_split_read_next_desc(VirtIODevice *vdev, VRingDesc *desc, } /* Check they're not leading us off end of descriptors. */ - *next = desc->next; - /* Make sure compiler knows to grab that: we don't want it changing! */ - smp_wmb(); - - if (*next >= max) { - virtio_error(vdev, "Desc next is %u", *next); + if (desc->next >= max) { + virtio_error(vdev, "Desc next is %u", desc->next); return VIRTQUEUE_READ_DESC_ERROR; } - vring_split_desc_read(vdev, desc, desc_cache, *next); + vring_split_desc_read(vdev, desc, desc_cache, desc->next); return VIRTQUEUE_READ_DESC_MORE; } @@ -1033,22 +1085,23 @@ static void virtqueue_split_get_avail_bytes(VirtQueue *vq, VRingMemoryRegionCaches *caches) { VirtIODevice *vdev = vq->vdev; - unsigned int max, idx; + unsigned int idx; unsigned int total_bufs, in_total, out_total; - MemoryRegionCache indirect_desc_cache = MEMORY_REGION_CACHE_INVALID; + MemoryRegionCache indirect_desc_cache; int64_t len = 0; int rc; + address_space_cache_init_empty(&indirect_desc_cache); + idx = vq->last_avail_idx; total_bufs = in_total = out_total = 0; - max = vq->vring.num; - while ((rc = virtqueue_num_heads(vq, idx)) > 0) { MemoryRegionCache *desc_cache = &caches->desc; unsigned int num_bufs; VRingDesc desc; unsigned int i; + unsigned int max = vq->vring.num; num_bufs = total_bufs; @@ -1101,7 +1154,7 @@ static void virtqueue_split_get_avail_bytes(VirtQueue *vq, goto done; } - rc = virtqueue_split_read_next_desc(vdev, &desc, desc_cache, max, &i); + rc = virtqueue_split_read_next_desc(vdev, &desc, desc_cache, max); } while (rc == VIRTQUEUE_READ_DESC_MORE); if (rc == VIRTQUEUE_READ_DESC_ERROR) { @@ -1170,26 +1223,28 @@ static void virtqueue_packed_get_avail_bytes(VirtQueue *vq, VRingMemoryRegionCaches *caches) { VirtIODevice *vdev = vq->vdev; - unsigned int max, idx; + unsigned int idx; unsigned int total_bufs, in_total, out_total; + MemoryRegionCache indirect_desc_cache; MemoryRegionCache *desc_cache; - MemoryRegionCache indirect_desc_cache = MEMORY_REGION_CACHE_INVALID; int64_t len = 0; VRingPackedDesc desc; bool wrap_counter; + address_space_cache_init_empty(&indirect_desc_cache); + idx = vq->last_avail_idx; wrap_counter = vq->last_avail_wrap_counter; total_bufs = in_total = out_total = 0; - max = vq->vring.num; - for (;;) { unsigned int num_bufs = total_bufs; unsigned int i = idx; int rc; + unsigned int max = vq->vring.num; desc_cache = &caches->desc; + vring_packed_desc_read(vdev, &desc, desc_cache, idx, true); if (!is_desc_avail(desc.flags, wrap_counter)) { break; @@ -1452,7 +1507,7 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz) { unsigned int i, head, max; VRingMemoryRegionCaches *caches; - MemoryRegionCache indirect_desc_cache = MEMORY_REGION_CACHE_INVALID; + MemoryRegionCache indirect_desc_cache; MemoryRegionCache *desc_cache; int64_t len; VirtIODevice *vdev = vq->vdev; @@ -1463,6 +1518,8 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz) VRingDesc desc; int rc; + address_space_cache_init_empty(&indirect_desc_cache); + RCU_READ_LOCK_GUARD(); if (virtio_queue_empty_rcu(vq)) { goto done; @@ -1552,7 +1609,7 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz) goto err_undo_map; } - rc = virtqueue_split_read_next_desc(vdev, &desc, desc_cache, max, &i); + rc = virtqueue_split_read_next_desc(vdev, &desc, desc_cache, max); } while (rc == VIRTQUEUE_READ_DESC_MORE); if (rc == VIRTQUEUE_READ_DESC_ERROR) { @@ -1589,7 +1646,7 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz) { unsigned int i, max; VRingMemoryRegionCaches *caches; - MemoryRegionCache indirect_desc_cache = MEMORY_REGION_CACHE_INVALID; + MemoryRegionCache indirect_desc_cache; MemoryRegionCache *desc_cache; int64_t len; VirtIODevice *vdev = vq->vdev; @@ -1601,6 +1658,8 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz) uint16_t id; int rc; + address_space_cache_init_empty(&indirect_desc_cache); + RCU_READ_LOCK_GUARD(); if (virtio_queue_packed_empty_rcu(vq)) { goto done; @@ -2019,6 +2078,58 @@ static enum virtio_device_endian virtio_current_cpu_endian(void) } } +static void __virtio_queue_reset(VirtIODevice *vdev, uint32_t i) +{ + vdev->vq[i].vring.desc = 0; + vdev->vq[i].vring.avail = 0; + vdev->vq[i].vring.used = 0; + vdev->vq[i].last_avail_idx = 0; + vdev->vq[i].shadow_avail_idx = 0; + vdev->vq[i].used_idx = 0; + vdev->vq[i].last_avail_wrap_counter = true; + vdev->vq[i].shadow_avail_wrap_counter = true; + vdev->vq[i].used_wrap_counter = true; + virtio_queue_set_vector(vdev, i, VIRTIO_NO_VECTOR); + vdev->vq[i].signalled_used = 0; + vdev->vq[i].signalled_used_valid = false; + vdev->vq[i].notification = true; + vdev->vq[i].vring.num = vdev->vq[i].vring.num_default; + vdev->vq[i].inuse = 0; + virtio_virtqueue_reset_region_cache(&vdev->vq[i]); +} + +void virtio_queue_reset(VirtIODevice *vdev, uint32_t queue_index) +{ + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + + if (k->queue_reset) { + k->queue_reset(vdev, queue_index); + } + + __virtio_queue_reset(vdev, queue_index); +} + +void virtio_queue_enable(VirtIODevice *vdev, uint32_t queue_index) +{ + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + + /* + * TODO: Seabios is currently out of spec and triggering this error. + * So this needs to be fixed in Seabios, then this can + * be re-enabled for new machine types only, and also after + * being converted to LOG_GUEST_ERROR. + * + if (!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { + error_report("queue_enable is only supported in devices of virtio " + "1.0 or later."); + } + */ + + if (k->queue_enable) { + k->queue_enable(vdev, queue_index); + } +} + void virtio_reset(void *opaque) { VirtIODevice *vdev = opaque; @@ -2034,6 +2145,10 @@ void virtio_reset(void *opaque) vdev->device_endian = virtio_default_endian(); } + if (vdev->vhost_started && k->get_vhost) { + vhost_reset_device(k->get_vhost(vdev)); + } + if (k->reset) { k->reset(vdev); } @@ -2050,211 +2165,7 @@ void virtio_reset(void *opaque) virtio_notify_vector(vdev, vdev->config_vector); for(i = 0; i < VIRTIO_QUEUE_MAX; i++) { - vdev->vq[i].vring.desc = 0; - vdev->vq[i].vring.avail = 0; - vdev->vq[i].vring.used = 0; - vdev->vq[i].last_avail_idx = 0; - vdev->vq[i].shadow_avail_idx = 0; - vdev->vq[i].used_idx = 0; - vdev->vq[i].last_avail_wrap_counter = true; - vdev->vq[i].shadow_avail_wrap_counter = true; - vdev->vq[i].used_wrap_counter = true; - virtio_queue_set_vector(vdev, i, VIRTIO_NO_VECTOR); - vdev->vq[i].signalled_used = 0; - vdev->vq[i].signalled_used_valid = false; - vdev->vq[i].notification = true; - vdev->vq[i].vring.num = vdev->vq[i].vring.num_default; - vdev->vq[i].inuse = 0; - virtio_virtqueue_reset_region_cache(&vdev->vq[i]); - } -} - -uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint8_t val; - - if (addr + sizeof(val) > vdev->config_len) { - return (uint32_t)-1; - } - - k->get_config(vdev, vdev->config); - - val = ldub_p(vdev->config + addr); - return val; -} - -uint32_t virtio_config_readw(VirtIODevice *vdev, uint32_t addr) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint16_t val; - - if (addr + sizeof(val) > vdev->config_len) { - return (uint32_t)-1; - } - - k->get_config(vdev, vdev->config); - - val = lduw_p(vdev->config + addr); - return val; -} - -uint32_t virtio_config_readl(VirtIODevice *vdev, uint32_t addr) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint32_t val; - - if (addr + sizeof(val) > vdev->config_len) { - return (uint32_t)-1; - } - - k->get_config(vdev, vdev->config); - - val = ldl_p(vdev->config + addr); - return val; -} - -void virtio_config_writeb(VirtIODevice *vdev, uint32_t addr, uint32_t data) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint8_t val = data; - - if (addr + sizeof(val) > vdev->config_len) { - return; - } - - stb_p(vdev->config + addr, val); - - if (k->set_config) { - k->set_config(vdev, vdev->config); - } -} - -void virtio_config_writew(VirtIODevice *vdev, uint32_t addr, uint32_t data) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint16_t val = data; - - if (addr + sizeof(val) > vdev->config_len) { - return; - } - - stw_p(vdev->config + addr, val); - - if (k->set_config) { - k->set_config(vdev, vdev->config); - } -} - -void virtio_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint32_t val = data; - - if (addr + sizeof(val) > vdev->config_len) { - return; - } - - stl_p(vdev->config + addr, val); - - if (k->set_config) { - k->set_config(vdev, vdev->config); - } -} - -uint32_t virtio_config_modern_readb(VirtIODevice *vdev, uint32_t addr) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint8_t val; - - if (addr + sizeof(val) > vdev->config_len) { - return (uint32_t)-1; - } - - k->get_config(vdev, vdev->config); - - val = ldub_p(vdev->config + addr); - return val; -} - -uint32_t virtio_config_modern_readw(VirtIODevice *vdev, uint32_t addr) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint16_t val; - - if (addr + sizeof(val) > vdev->config_len) { - return (uint32_t)-1; - } - - k->get_config(vdev, vdev->config); - - val = lduw_le_p(vdev->config + addr); - return val; -} - -uint32_t virtio_config_modern_readl(VirtIODevice *vdev, uint32_t addr) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint32_t val; - - if (addr + sizeof(val) > vdev->config_len) { - return (uint32_t)-1; - } - - k->get_config(vdev, vdev->config); - - val = ldl_le_p(vdev->config + addr); - return val; -} - -void virtio_config_modern_writeb(VirtIODevice *vdev, - uint32_t addr, uint32_t data) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint8_t val = data; - - if (addr + sizeof(val) > vdev->config_len) { - return; - } - - stb_p(vdev->config + addr, val); - - if (k->set_config) { - k->set_config(vdev, vdev->config); - } -} - -void virtio_config_modern_writew(VirtIODevice *vdev, - uint32_t addr, uint32_t data) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint16_t val = data; - - if (addr + sizeof(val) > vdev->config_len) { - return; - } - - stw_le_p(vdev->config + addr, val); - - if (k->set_config) { - k->set_config(vdev, vdev->config); - } -} - -void virtio_config_modern_writel(VirtIODevice *vdev, - uint32_t addr, uint32_t data) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint32_t val = data; - - if (addr + sizeof(val) > vdev->config_len) { - return; - } - - stl_le_p(vdev->config + addr, val); - - if (k->set_config) { - k->set_config(vdev, vdev->config); + __virtio_queue_reset(vdev, i); } } @@ -2543,6 +2454,16 @@ static bool virtio_should_notify(VirtIODevice *vdev, VirtQueue *vq) } } +/* Batch irqs while inside a defer_call_begin()/defer_call_end() section */ +static void virtio_notify_irqfd_deferred_fn(void *opaque) +{ + EventNotifier *notifier = opaque; + VirtQueue *vq = container_of(notifier, VirtQueue, guest_notifier); + + trace_virtio_notify_irqfd_deferred_fn(vq->vdev, vq); + event_notifier_set(notifier); +} + void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq) { WITH_RCU_READ_LOCK_GUARD() { @@ -2569,7 +2490,7 @@ void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq) * to an atomic operation. */ virtio_set_isr(vq->vdev, 0x1); - event_notifier_set(&vq->guest_notifier); + defer_call(virtio_notify_irqfd_deferred_fn, &vq->guest_notifier); } static void virtio_irq(VirtQueue *vq) @@ -2681,7 +2602,7 @@ static const VMStateDescription vmstate_virtqueue = { .name = "virtqueue_state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(vring.avail, struct VirtQueue), VMSTATE_UINT64(vring.used, struct VirtQueue), VMSTATE_END_OF_LIST() @@ -2692,7 +2613,7 @@ static const VMStateDescription vmstate_packed_virtqueue = { .name = "packed_virtqueue_state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(last_avail_idx, struct VirtQueue), VMSTATE_BOOL(last_avail_wrap_counter, struct VirtQueue), VMSTATE_UINT16(used_idx, struct VirtQueue), @@ -2707,7 +2628,7 @@ static const VMStateDescription vmstate_virtio_virtqueues = { .version_id = 1, .minimum_version_id = 1, .needed = &virtio_virtqueue_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_VARRAY_POINTER_KNOWN(vq, struct VirtIODevice, VIRTIO_QUEUE_MAX, 0, vmstate_virtqueue, VirtQueue), VMSTATE_END_OF_LIST() @@ -2719,7 +2640,7 @@ static const VMStateDescription vmstate_virtio_packed_virtqueues = { .version_id = 1, .minimum_version_id = 1, .needed = &virtio_packed_virtqueue_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_VARRAY_POINTER_KNOWN(vq, struct VirtIODevice, VIRTIO_QUEUE_MAX, 0, vmstate_packed_virtqueue, VirtQueue), VMSTATE_END_OF_LIST() @@ -2730,7 +2651,7 @@ static const VMStateDescription vmstate_ringsize = { .name = "ringsize_state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(vring.num_default, struct VirtQueue), VMSTATE_END_OF_LIST() } @@ -2741,7 +2662,7 @@ static const VMStateDescription vmstate_virtio_ringsize = { .version_id = 1, .minimum_version_id = 1, .needed = &virtio_ringsize_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_VARRAY_POINTER_KNOWN(vq, struct VirtIODevice, VIRTIO_QUEUE_MAX, 0, vmstate_ringsize, VirtQueue), VMSTATE_END_OF_LIST() @@ -2784,7 +2705,7 @@ static const VMStateDescription vmstate_virtio_extra_state = { .version_id = 1, .minimum_version_id = 1, .needed = &virtio_extra_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { { .name = "extra_state", .version_id = 0, @@ -2803,7 +2724,7 @@ static const VMStateDescription vmstate_virtio_device_endian = { .version_id = 1, .minimum_version_id = 1, .needed = &virtio_device_endian_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(device_endian, VirtIODevice), VMSTATE_END_OF_LIST() } @@ -2814,7 +2735,7 @@ static const VMStateDescription vmstate_virtio_64bit_features = { .version_id = 1, .minimum_version_id = 1, .needed = &virtio_64bit_features_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(guest_features, VirtIODevice), VMSTATE_END_OF_LIST() } @@ -2825,7 +2746,7 @@ static const VMStateDescription vmstate_virtio_broken = { .version_id = 1, .minimum_version_id = 1, .needed = &virtio_broken_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(broken, VirtIODevice), VMSTATE_END_OF_LIST() } @@ -2836,7 +2757,7 @@ static const VMStateDescription vmstate_virtio_started = { .version_id = 1, .minimum_version_id = 1, .needed = &virtio_started_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(started, VirtIODevice), VMSTATE_END_OF_LIST() } @@ -2847,7 +2768,7 @@ static const VMStateDescription vmstate_virtio_disabled = { .version_id = 1, .minimum_version_id = 1, .needed = &virtio_disabled_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(disabled, VirtIODevice), VMSTATE_END_OF_LIST() } @@ -2857,10 +2778,10 @@ static const VMStateDescription vmstate_virtio = { .name = "virtio", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_virtio_device_endian, &vmstate_virtio_64bit_features, &vmstate_virtio_virtqueues, @@ -2942,8 +2863,9 @@ static int virtio_device_put(QEMUFile *f, void *opaque, size_t size, } /* A wrapper for use as a VMState .get function */ -static int virtio_device_get(QEMUFile *f, void *opaque, size_t size, - const VMStateField *field) +static int coroutine_mixed_fn +virtio_device_get(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) { VirtIODevice *vdev = VIRTIO_DEVICE(opaque); DeviceClass *dc = DEVICE_CLASS(VIRTIO_DEVICE_GET_CLASS(vdev)); @@ -2970,6 +2892,39 @@ static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val) return bad ? -1 : 0; } +typedef struct VirtioSetFeaturesNocheckData { + Coroutine *co; + VirtIODevice *vdev; + uint64_t val; + int ret; +} VirtioSetFeaturesNocheckData; + +static void virtio_set_features_nocheck_bh(void *opaque) +{ + VirtioSetFeaturesNocheckData *data = opaque; + + data->ret = virtio_set_features_nocheck(data->vdev, data->val); + aio_co_wake(data->co); +} + +static int coroutine_mixed_fn +virtio_set_features_nocheck_maybe_co(VirtIODevice *vdev, uint64_t val) +{ + if (qemu_in_coroutine()) { + VirtioSetFeaturesNocheckData data = { + .co = qemu_coroutine_self(), + .vdev = vdev, + .val = val, + }; + aio_bh_schedule_oneshot(qemu_get_current_aio_context(), + virtio_set_features_nocheck_bh, &data); + qemu_coroutine_yield(); + return data.ret; + } else { + return virtio_set_features_nocheck(vdev, val); + } +} + int virtio_set_features(VirtIODevice *vdev, uint64_t val) { int ret; @@ -2980,6 +2935,13 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val) if (vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) { return -EINVAL; } + + if (val & (1ull << VIRTIO_F_BAD_FEATURE)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: guest driver for %s has enabled UNUSED(30) feature bit!\n", + __func__, vdev->name); + } + ret = virtio_set_features_nocheck(vdev, val); if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { /* VIRTIO_RING_F_EVENT_IDX changes the size of the caches. */ @@ -2999,11 +2961,12 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val) return ret; } -size_t virtio_feature_get_config_size(const VirtIOFeature *feature_sizes, - uint64_t host_features) +size_t virtio_get_config_size(const VirtIOConfigSizeParams *params, + uint64_t host_features) { - size_t config_size = 0; - int i; + size_t config_size = params->min_size; + const VirtIOFeature *feature_sizes = params->feature_sizes; + size_t i; for (i = 0; feature_sizes[i].flags != 0; i++) { if (host_features & feature_sizes[i].flags) { @@ -3011,10 +2974,12 @@ size_t virtio_feature_get_config_size(const VirtIOFeature *feature_sizes, } } + assert(config_size <= params->max_size); return config_size; } -int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) +int coroutine_mixed_fn +virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) { int i, ret; int32_t config_len; @@ -3131,14 +3096,14 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) * host_features. */ uint64_t features64 = vdev->guest_features; - if (virtio_set_features_nocheck(vdev, features64) < 0) { + if (virtio_set_features_nocheck_maybe_co(vdev, features64) < 0) { error_report("Features 0x%" PRIx64 " unsupported. " "Allowed features: 0x%" PRIx64, features64, vdev->host_features); return -1; } } else { - if (virtio_set_features_nocheck(vdev, features) < 0) { + if (virtio_set_features_nocheck_maybe_co(vdev, features) < 0) { error_report("Features 0x%x unsupported. " "Allowed features: 0x%" PRIx64, features, vdev->host_features); @@ -3429,7 +3394,7 @@ static void virtio_queue_packed_set_last_avail_idx(VirtIODevice *vdev, vq->last_avail_wrap_counter = vq->shadow_avail_wrap_counter = !!(idx & 0x8000); idx >>= 16; - vq->used_idx = idx & 0x7ffff; + vq->used_idx = idx & 0x7fff; vq->used_wrap_counter = !!(idx & 0x8000); } @@ -3521,7 +3486,14 @@ static void virtio_queue_guest_notifier_read(EventNotifier *n) virtio_irq(vq); } } +static void virtio_config_guest_notifier_read(EventNotifier *n) +{ + VirtIODevice *vdev = container_of(n, VirtIODevice, config_notifier); + if (event_notifier_test_and_clear(n)) { + virtio_notify_config(vdev); + } +} void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign, bool with_irqfd) { @@ -3538,6 +3510,23 @@ void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign, } } +void virtio_config_set_guest_notifier_fd_handler(VirtIODevice *vdev, + bool assign, bool with_irqfd) +{ + EventNotifier *n; + n = &vdev->config_notifier; + if (assign && !with_irqfd) { + event_notifier_set_handler(n, virtio_config_guest_notifier_read); + } else { + event_notifier_set_handler(n, NULL); + } + if (!assign) { + /* Test and clear notifier before closing it,*/ + /* in case poll callback didn't have time to run. */ + virtio_config_guest_notifier_read(n); + } +} + EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq) { return &vq->guest_notifier; @@ -3575,13 +3564,31 @@ static void virtio_queue_host_notifier_aio_poll_end(EventNotifier *n) void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx) { - aio_set_event_notifier(ctx, &vq->host_notifier, true, + /* + * virtio_queue_aio_detach_host_notifier() can leave notifications disabled. + * Re-enable them. (And if detach has not been used before, notifications + * being enabled is still the default state while a notifier is attached; + * see virtio_queue_host_notifier_aio_poll_end(), which will always leave + * notifications enabled once the polling section is left.) + */ + if (!virtio_queue_get_notification(vq)) { + virtio_queue_set_notification(vq, 1); + } + + aio_set_event_notifier(ctx, &vq->host_notifier, virtio_queue_host_notifier_read, virtio_queue_host_notifier_aio_poll, virtio_queue_host_notifier_aio_poll_ready); aio_set_event_notifier_poll(ctx, &vq->host_notifier, virtio_queue_host_notifier_aio_poll_begin, virtio_queue_host_notifier_aio_poll_end); + + /* + * We will have ignored notifications about new requests from the guest + * while no notifiers were attached, so "kick" the virt queue to process + * those requests now. + */ + event_notifier_set(&vq->host_notifier); } /* @@ -3592,17 +3599,38 @@ void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx) */ void virtio_queue_aio_attach_host_notifier_no_poll(VirtQueue *vq, AioContext *ctx) { - aio_set_event_notifier(ctx, &vq->host_notifier, true, + /* See virtio_queue_aio_attach_host_notifier() */ + if (!virtio_queue_get_notification(vq)) { + virtio_queue_set_notification(vq, 1); + } + + aio_set_event_notifier(ctx, &vq->host_notifier, virtio_queue_host_notifier_read, NULL, NULL); + + /* + * See virtio_queue_aio_attach_host_notifier(). + * Note that this may be unnecessary for the type of virtqueues this + * function is used for. Still, it will not hurt to have a quick look into + * whether we can/should process any of the virtqueue elements. + */ + event_notifier_set(&vq->host_notifier); } void virtio_queue_aio_detach_host_notifier(VirtQueue *vq, AioContext *ctx) { - aio_set_event_notifier(ctx, &vq->host_notifier, true, NULL, NULL, NULL); - /* Test and clear notifier before after disabling event, - * in case poll callback didn't have time to run. */ - virtio_queue_host_notifier_read(&vq->host_notifier); + aio_set_event_notifier(ctx, &vq->host_notifier, NULL, NULL, NULL); + + /* + * aio_set_event_notifier_poll() does not guarantee whether io_poll_end() + * will run after io_poll_begin(), so by removing the notifier, we do not + * know whether virtio_queue_host_notifier_aio_poll_end() has run after a + * previous virtio_queue_host_notifier_aio_poll_begin(), i.e. whether + * notifications are enabled or disabled. It does not really matter anyway; + * we just removed the notifier, so we do not care about notifications until + * we potentially re-attach it. The attach_host_notifier functions will + * ensure that notifications are enabled again when they are needed. + */ } void virtio_queue_host_notifier_read(EventNotifier *n) @@ -3618,6 +3646,11 @@ EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq) return &vq->host_notifier; } +EventNotifier *virtio_config_get_guest_notifier(VirtIODevice *vdev) +{ + return &vdev->config_notifier; +} + void virtio_queue_set_host_notifier_enabled(VirtQueue *vq, bool enabled) { vq->host_notifier_enabled = enabled; @@ -3895,6 +3928,207 @@ bool virtio_device_ioeventfd_enabled(VirtIODevice *vdev) return virtio_bus_ioeventfd_enabled(vbus); } +VirtQueueStatus *qmp_x_query_virtio_queue_status(const char *path, + uint16_t queue, + Error **errp) +{ + VirtIODevice *vdev; + VirtQueueStatus *status; + + vdev = qmp_find_virtio_device(path); + if (vdev == NULL) { + error_setg(errp, "Path %s is not a VirtIODevice", path); + return NULL; + } + + if (queue >= VIRTIO_QUEUE_MAX || !virtio_queue_get_num(vdev, queue)) { + error_setg(errp, "Invalid virtqueue number %d", queue); + return NULL; + } + + status = g_new0(VirtQueueStatus, 1); + status->name = g_strdup(vdev->name); + status->queue_index = vdev->vq[queue].queue_index; + status->inuse = vdev->vq[queue].inuse; + status->vring_num = vdev->vq[queue].vring.num; + status->vring_num_default = vdev->vq[queue].vring.num_default; + status->vring_align = vdev->vq[queue].vring.align; + status->vring_desc = vdev->vq[queue].vring.desc; + status->vring_avail = vdev->vq[queue].vring.avail; + status->vring_used = vdev->vq[queue].vring.used; + status->used_idx = vdev->vq[queue].used_idx; + status->signalled_used = vdev->vq[queue].signalled_used; + status->signalled_used_valid = vdev->vq[queue].signalled_used_valid; + + if (vdev->vhost_started) { + VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); + struct vhost_dev *hdev = vdc->get_vhost(vdev); + + /* check if vq index exists for vhost as well */ + if (queue >= hdev->vq_index && queue < hdev->vq_index + hdev->nvqs) { + status->has_last_avail_idx = true; + + int vhost_vq_index = + hdev->vhost_ops->vhost_get_vq_index(hdev, queue); + struct vhost_vring_state state = { + .index = vhost_vq_index, + }; + + status->last_avail_idx = + hdev->vhost_ops->vhost_get_vring_base(hdev, &state); + } + } else { + status->has_shadow_avail_idx = true; + status->has_last_avail_idx = true; + status->last_avail_idx = vdev->vq[queue].last_avail_idx; + status->shadow_avail_idx = vdev->vq[queue].shadow_avail_idx; + } + + return status; +} + +static strList *qmp_decode_vring_desc_flags(uint16_t flags) +{ + strList *list = NULL; + strList *node; + int i; + + struct { + uint16_t flag; + const char *value; + } map[] = { + { VRING_DESC_F_NEXT, "next" }, + { VRING_DESC_F_WRITE, "write" }, + { VRING_DESC_F_INDIRECT, "indirect" }, + { 1 << VRING_PACKED_DESC_F_AVAIL, "avail" }, + { 1 << VRING_PACKED_DESC_F_USED, "used" }, + { 0, "" } + }; + + for (i = 0; map[i].flag; i++) { + if ((map[i].flag & flags) == 0) { + continue; + } + node = g_malloc0(sizeof(strList)); + node->value = g_strdup(map[i].value); + node->next = list; + list = node; + } + + return list; +} + +VirtioQueueElement *qmp_x_query_virtio_queue_element(const char *path, + uint16_t queue, + bool has_index, + uint16_t index, + Error **errp) +{ + VirtIODevice *vdev; + VirtQueue *vq; + VirtioQueueElement *element = NULL; + + vdev = qmp_find_virtio_device(path); + if (vdev == NULL) { + error_setg(errp, "Path %s is not a VirtIO device", path); + return NULL; + } + + if (queue >= VIRTIO_QUEUE_MAX || !virtio_queue_get_num(vdev, queue)) { + error_setg(errp, "Invalid virtqueue number %d", queue); + return NULL; + } + vq = &vdev->vq[queue]; + + if (virtio_vdev_has_feature(vdev, VIRTIO_F_RING_PACKED)) { + error_setg(errp, "Packed ring not supported"); + return NULL; + } else { + unsigned int head, i, max; + VRingMemoryRegionCaches *caches; + MemoryRegionCache indirect_desc_cache; + MemoryRegionCache *desc_cache; + VRingDesc desc; + VirtioRingDescList *list = NULL; + VirtioRingDescList *node; + int rc; int ndescs; + + address_space_cache_init_empty(&indirect_desc_cache); + + RCU_READ_LOCK_GUARD(); + + max = vq->vring.num; + + if (!has_index) { + head = vring_avail_ring(vq, vq->last_avail_idx % vq->vring.num); + } else { + head = vring_avail_ring(vq, index % vq->vring.num); + } + i = head; + + caches = vring_get_region_caches(vq); + if (!caches) { + error_setg(errp, "Region caches not initialized"); + return NULL; + } + if (caches->desc.len < max * sizeof(VRingDesc)) { + error_setg(errp, "Cannot map descriptor ring"); + return NULL; + } + + desc_cache = &caches->desc; + vring_split_desc_read(vdev, &desc, desc_cache, i); + if (desc.flags & VRING_DESC_F_INDIRECT) { + int64_t len; + len = address_space_cache_init(&indirect_desc_cache, vdev->dma_as, + desc.addr, desc.len, false); + desc_cache = &indirect_desc_cache; + if (len < desc.len) { + error_setg(errp, "Cannot map indirect buffer"); + goto done; + } + + max = desc.len / sizeof(VRingDesc); + i = 0; + vring_split_desc_read(vdev, &desc, desc_cache, i); + } + + element = g_new0(VirtioQueueElement, 1); + element->avail = g_new0(VirtioRingAvail, 1); + element->used = g_new0(VirtioRingUsed, 1); + element->name = g_strdup(vdev->name); + element->index = head; + element->avail->flags = vring_avail_flags(vq); + element->avail->idx = vring_avail_idx(vq); + element->avail->ring = head; + element->used->flags = vring_used_flags(vq); + element->used->idx = vring_used_idx(vq); + ndescs = 0; + + do { + /* A buggy driver may produce an infinite loop */ + if (ndescs >= max) { + break; + } + node = g_new0(VirtioRingDescList, 1); + node->value = g_new0(VirtioRingDesc, 1); + node->value->addr = desc.addr; + node->value->len = desc.len; + node->value->flags = qmp_decode_vring_desc_flags(desc.flags); + node->next = list; + list = node; + + ndescs++; + rc = virtqueue_split_read_next_desc(vdev, &desc, desc_cache, max); + } while (rc == VIRTQUEUE_READ_DESC_MORE); + element->descs = list; +done: + address_space_cache_destroy(&indirect_desc_cache); + } + + return element; +} + static const TypeInfo virtio_device_info = { .name = TYPE_VIRTIO_DEVICE, .parent = TYPE_DEVICE, @@ -3911,3 +4145,13 @@ static void virtio_register_types(void) } type_init(virtio_register_types) + +QEMUBH *virtio_bh_new_guarded_full(DeviceState *dev, + QEMUBHFunc *cb, void *opaque, + const char *name) +{ + DeviceState *transport = qdev_get_parent_bus(dev)->parent; + + return qemu_bh_new_full(cb, opaque, name, + &transport->mem_reentrancy_guard); +} diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig index 66e1d029e3..861fd00334 100644 --- a/hw/watchdog/Kconfig +++ b/hw/watchdog/Kconfig @@ -20,3 +20,7 @@ config WDT_IMX2 config WDT_SBSA bool + +config ALLWINNER_WDT + bool + select PTIMER diff --git a/hw/watchdog/allwinner-wdt.c b/hw/watchdog/allwinner-wdt.c new file mode 100644 index 0000000000..d35711c7c5 --- /dev/null +++ b/hw/watchdog/allwinner-wdt.c @@ -0,0 +1,416 @@ +/* + * Allwinner Watchdog emulation + * + * Copyright (C) 2023 Strahinja Jankovic + * + * This file is derived from Allwinner RTC, + * by 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/units.h" +#include "qemu/module.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/watchdog/allwinner-wdt.h" +#include "sysemu/watchdog.h" +#include "migration/vmstate.h" + +/* WDT registers */ +enum { + REG_IRQ_EN = 0, /* Watchdog interrupt enable */ + REG_IRQ_STA, /* Watchdog interrupt status */ + REG_CTRL, /* Watchdog control register */ + REG_CFG, /* Watchdog configuration register */ + REG_MODE, /* Watchdog mode register */ +}; + +/* Universal WDT register flags */ +#define WDT_RESTART_MASK (1 << 0) +#define WDT_EN_MASK (1 << 0) + +/* sun4i specific WDT register flags */ +#define RST_EN_SUN4I_MASK (1 << 1) +#define INTV_VALUE_SUN4I_SHIFT (3) +#define INTV_VALUE_SUN4I_MASK (0xfu << INTV_VALUE_SUN4I_SHIFT) + +/* sun6i specific WDT register flags */ +#define RST_EN_SUN6I_MASK (1 << 0) +#define KEY_FIELD_SUN6I_SHIFT (1) +#define KEY_FIELD_SUN6I_MASK (0xfffu << KEY_FIELD_SUN6I_SHIFT) +#define KEY_FIELD_SUN6I (0xA57u) +#define INTV_VALUE_SUN6I_SHIFT (4) +#define INTV_VALUE_SUN6I_MASK (0xfu << INTV_VALUE_SUN6I_SHIFT) + +/* Map of INTV_VALUE to 0.5s units. */ +static const uint8_t allwinner_wdt_count_map[] = { + 1, + 2, + 4, + 6, + 8, + 10, + 12, + 16, + 20, + 24, + 28, + 32 +}; + +/* WDT sun4i register map (offset to name) */ +const uint8_t allwinner_wdt_sun4i_regmap[] = { + [0x0000] = REG_CTRL, + [0x0004] = REG_MODE, +}; + +/* WDT sun6i register map (offset to name) */ +const uint8_t allwinner_wdt_sun6i_regmap[] = { + [0x0000] = REG_IRQ_EN, + [0x0004] = REG_IRQ_STA, + [0x0010] = REG_CTRL, + [0x0014] = REG_CFG, + [0x0018] = REG_MODE, +}; + +static bool allwinner_wdt_sun4i_read(AwWdtState *s, uint32_t offset) +{ + /* no sun4i specific registers currently implemented */ + return false; +} + +static bool allwinner_wdt_sun4i_write(AwWdtState *s, uint32_t offset, + uint32_t data) +{ + /* no sun4i specific registers currently implemented */ + return false; +} + +static bool allwinner_wdt_sun4i_can_reset_system(AwWdtState *s) +{ + if (s->regs[REG_MODE] & RST_EN_SUN4I_MASK) { + return true; + } else { + return false; + } +} + +static bool allwinner_wdt_sun4i_is_key_valid(AwWdtState *s, uint32_t val) +{ + /* sun4i has no key */ + return true; +} + +static uint8_t allwinner_wdt_sun4i_get_intv_value(AwWdtState *s) +{ + return ((s->regs[REG_MODE] & INTV_VALUE_SUN4I_MASK) >> + INTV_VALUE_SUN4I_SHIFT); +} + +static bool allwinner_wdt_sun6i_read(AwWdtState *s, uint32_t offset) +{ + const AwWdtClass *c = AW_WDT_GET_CLASS(s); + + switch (c->regmap[offset]) { + case REG_IRQ_EN: + case REG_IRQ_STA: + case REG_CFG: + return true; + default: + break; + } + return false; +} + +static bool allwinner_wdt_sun6i_write(AwWdtState *s, uint32_t offset, + uint32_t data) +{ + const AwWdtClass *c = AW_WDT_GET_CLASS(s); + + switch (c->regmap[offset]) { + case REG_IRQ_EN: + case REG_IRQ_STA: + case REG_CFG: + return true; + default: + break; + } + return false; +} + +static bool allwinner_wdt_sun6i_can_reset_system(AwWdtState *s) +{ + if (s->regs[REG_CFG] & RST_EN_SUN6I_MASK) { + return true; + } else { + return false; + } +} + +static bool allwinner_wdt_sun6i_is_key_valid(AwWdtState *s, uint32_t val) +{ + uint16_t key = (val & KEY_FIELD_SUN6I_MASK) >> KEY_FIELD_SUN6I_SHIFT; + return (key == KEY_FIELD_SUN6I); +} + +static uint8_t allwinner_wdt_sun6i_get_intv_value(AwWdtState *s) +{ + return ((s->regs[REG_MODE] & INTV_VALUE_SUN6I_MASK) >> + INTV_VALUE_SUN6I_SHIFT); +} + +static void allwinner_wdt_update_timer(AwWdtState *s) +{ + const AwWdtClass *c = AW_WDT_GET_CLASS(s); + uint8_t count = c->get_intv_value(s); + + ptimer_transaction_begin(s->timer); + ptimer_stop(s->timer); + + /* Use map to convert. */ + if (count < sizeof(allwinner_wdt_count_map)) { + ptimer_set_count(s->timer, allwinner_wdt_count_map[count]); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: incorrect INTV_VALUE 0x%02x\n", + __func__, count); + } + + ptimer_run(s->timer, 1); + ptimer_transaction_commit(s->timer); + + trace_allwinner_wdt_update_timer(count); +} + +static uint64_t allwinner_wdt_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwWdtState *s = AW_WDT(opaque); + const AwWdtClass *c = AW_WDT_GET_CLASS(s); + uint64_t r; + + 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; + } + + switch (c->regmap[offset]) { + case REG_CTRL: + case REG_MODE: + r = 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); + return 0; + } + r = s->regs[c->regmap[offset]]; + break; + } + + trace_allwinner_wdt_read(offset, r, size); + + return r; +} + +static void allwinner_wdt_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwWdtState *s = AW_WDT(opaque); + const AwWdtClass *c = AW_WDT_GET_CLASS(s); + uint32_t old_val; + + if (offset >= c->regmap_size) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + trace_allwinner_wdt_write(offset, val, size); + + switch (c->regmap[offset]) { + case REG_CTRL: + if (c->is_key_valid(s, val)) { + if (val & WDT_RESTART_MASK) { + /* Kick timer */ + allwinner_wdt_update_timer(s); + } + } + break; + case REG_MODE: + old_val = s->regs[REG_MODE]; + s->regs[REG_MODE] = (uint32_t)val; + + /* Check for rising edge on WDOG_MODE_EN */ + if ((s->regs[REG_MODE] & ~old_val) & WDT_EN_MASK) { + allwinner_wdt_update_timer(s); + } + break; + default: + if (!c->write(s, offset, val)) { + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register 0x%04x\n", + __func__, (uint32_t)offset); + } + s->regs[c->regmap[offset]] = (uint32_t)val; + break; + } +} + +static const MemoryRegionOps allwinner_wdt_ops = { + .read = allwinner_wdt_read, + .write = allwinner_wdt_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_wdt_expired(void *opaque) +{ + AwWdtState *s = AW_WDT(opaque); + const AwWdtClass *c = AW_WDT_GET_CLASS(s); + + bool enabled = s->regs[REG_MODE] & WDT_EN_MASK; + bool reset_enabled = c->can_reset_system(s); + + trace_allwinner_wdt_expired(enabled, reset_enabled); + + /* Perform watchdog action if watchdog is enabled and can trigger reset */ + if (enabled && reset_enabled) { + watchdog_perform_action(); + } +} + +static void allwinner_wdt_reset_enter(Object *obj, ResetType type) +{ + AwWdtState *s = AW_WDT(obj); + + trace_allwinner_wdt_reset_enter(); + + /* Clear registers */ + memset(s->regs, 0, sizeof(s->regs)); +} + +static const VMStateDescription allwinner_wdt_vmstate = { + .name = "allwinner-wdt", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_PTIMER(timer, AwWdtState), + VMSTATE_UINT32_ARRAY(regs, AwWdtState, AW_WDT_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_wdt_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwWdtState *s = AW_WDT(obj); + const AwWdtClass *c = AW_WDT_GET_CLASS(s); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_wdt_ops, s, + TYPE_AW_WDT, c->regmap_size * 4); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void allwinner_wdt_realize(DeviceState *dev, Error **errp) +{ + AwWdtState *s = AW_WDT(dev); + + s->timer = ptimer_init(allwinner_wdt_expired, s, + PTIMER_POLICY_NO_IMMEDIATE_TRIGGER | + PTIMER_POLICY_NO_IMMEDIATE_RELOAD | + PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + + ptimer_transaction_begin(s->timer); + /* Set to 2Hz (0.5s period); other periods are multiples of 0.5s. */ + ptimer_set_freq(s->timer, 2); + ptimer_set_limit(s->timer, 0xff, 1); + ptimer_transaction_commit(s->timer); +} + +static void allwinner_wdt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.enter = allwinner_wdt_reset_enter; + dc->realize = allwinner_wdt_realize; + dc->vmsd = &allwinner_wdt_vmstate; +} + +static void allwinner_wdt_sun4i_class_init(ObjectClass *klass, void *data) +{ + AwWdtClass *awc = AW_WDT_CLASS(klass); + + awc->regmap = allwinner_wdt_sun4i_regmap; + awc->regmap_size = sizeof(allwinner_wdt_sun4i_regmap); + awc->read = allwinner_wdt_sun4i_read; + awc->write = allwinner_wdt_sun4i_write; + awc->can_reset_system = allwinner_wdt_sun4i_can_reset_system; + awc->is_key_valid = allwinner_wdt_sun4i_is_key_valid; + awc->get_intv_value = allwinner_wdt_sun4i_get_intv_value; +} + +static void allwinner_wdt_sun6i_class_init(ObjectClass *klass, void *data) +{ + AwWdtClass *awc = AW_WDT_CLASS(klass); + + awc->regmap = allwinner_wdt_sun6i_regmap; + awc->regmap_size = sizeof(allwinner_wdt_sun6i_regmap); + awc->read = allwinner_wdt_sun6i_read; + awc->write = allwinner_wdt_sun6i_write; + awc->can_reset_system = allwinner_wdt_sun6i_can_reset_system; + awc->is_key_valid = allwinner_wdt_sun6i_is_key_valid; + awc->get_intv_value = allwinner_wdt_sun6i_get_intv_value; +} + +static const TypeInfo allwinner_wdt_info = { + .name = TYPE_AW_WDT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_wdt_init, + .instance_size = sizeof(AwWdtState), + .class_init = allwinner_wdt_class_init, + .class_size = sizeof(AwWdtClass), + .abstract = true, +}; + +static const TypeInfo allwinner_wdt_sun4i_info = { + .name = TYPE_AW_WDT_SUN4I, + .parent = TYPE_AW_WDT, + .class_init = allwinner_wdt_sun4i_class_init, +}; + +static const TypeInfo allwinner_wdt_sun6i_info = { + .name = TYPE_AW_WDT_SUN6I, + .parent = TYPE_AW_WDT, + .class_init = allwinner_wdt_sun6i_class_init, +}; + +static void allwinner_wdt_register(void) +{ + type_register_static(&allwinner_wdt_info); + type_register_static(&allwinner_wdt_sun4i_info); + type_register_static(&allwinner_wdt_sun6i_info); +} + +type_init(allwinner_wdt_register) diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c index 5a2cd46eb7..3091e5c3d5 100644 --- a/hw/watchdog/cmsdk-apb-watchdog.c +++ b/hw/watchdog/cmsdk-apb-watchdog.c @@ -361,7 +361,7 @@ static const VMStateDescription cmsdk_apb_watchdog_vmstate = { .name = "cmsdk-apb-watchdog", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CLOCK(wdogclk, CMSDKAPBWatchdog), VMSTATE_PTIMER(timer, CMSDKAPBWatchdog), VMSTATE_UINT32(control, CMSDKAPBWatchdog), diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build index 054c403dea..15370565bd 100644 --- a/hw/watchdog/meson.build +++ b/hw/watchdog/meson.build @@ -1,8 +1,10 @@ -softmmu_ss.add(files('watchdog.c')) -softmmu_ss.add(when: 'CONFIG_CMSDK_APB_WATCHDOG', if_true: files('cmsdk-apb-watchdog.c')) -softmmu_ss.add(when: 'CONFIG_WDT_IB6300ESB', if_true: files('wdt_i6300esb.c')) -softmmu_ss.add(when: 'CONFIG_WDT_IB700', if_true: files('wdt_ib700.c')) -softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c')) -softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c')) -softmmu_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c')) -softmmu_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c')) +system_ss.add(files('watchdog.c')) +system_ss.add(when: 'CONFIG_ALLWINNER_WDT', if_true: files('allwinner-wdt.c')) +system_ss.add(when: 'CONFIG_CMSDK_APB_WATCHDOG', if_true: files('cmsdk-apb-watchdog.c')) +system_ss.add(when: 'CONFIG_WDT_IB6300ESB', if_true: files('wdt_i6300esb.c')) +system_ss.add(when: 'CONFIG_WDT_IB700', if_true: files('wdt_ib700.c')) +system_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c')) +system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c')) +system_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c')) +system_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c')) +specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_watchdog.c')) diff --git a/hw/watchdog/sbsa_gwdt.c b/hw/watchdog/sbsa_gwdt.c index e49cacd0e2..96895d7636 100644 --- a/hw/watchdog/sbsa_gwdt.c +++ b/hw/watchdog/sbsa_gwdt.c @@ -24,16 +24,11 @@ #include "qemu/log.h" #include "qemu/module.h" -static WatchdogTimerModel model = { - .wdt_name = TYPE_WDT_SBSA, - .wdt_description = "SBSA-compliant generic watchdog device", -}; - static const VMStateDescription vmstate_sbsa_gwdt = { .name = "sbsa-gwdt", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(timer, SBSA_GWDTState), VMSTATE_UINT32(wcs, SBSA_GWDTState), VMSTATE_UINT32(worl, SBSA_GWDTState), @@ -287,7 +282,6 @@ static const TypeInfo wdt_sbsa_gwdt_info = { static void wdt_sbsa_gwdt_register_types(void) { - watchdog_add_model(&model); type_register_static(&wdt_sbsa_gwdt_info); } diff --git a/hw/watchdog/spapr_watchdog.c b/hw/watchdog/spapr_watchdog.c new file mode 100644 index 0000000000..2bb1d3c532 --- /dev/null +++ b/hw/watchdog/spapr_watchdog.c @@ -0,0 +1,274 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "qapi/error.h" +#include "target/ppc/cpu.h" +#include "migration/vmstate.h" +#include "trace.h" + +#include "hw/ppc/spapr.h" + +#define FIELD_BE(reg, field, start, len) \ + FIELD(reg, field, 64 - (start + len), len) + +/* + * Bits 47: "leaveOtherWatchdogsRunningOnTimeout", specified on + * the "Start watchdog" operation, + * 0 - stop out-standing watchdogs on timeout, + * 1 - leave outstanding watchdogs running on timeout + */ +FIELD_BE(PSERIES_WDTF, LEAVE_OTHER, 47, 1) + +/* Bits 48-55: "operation" */ +FIELD_BE(PSERIES_WDTF, OP, 48, 8) +#define PSERIES_WDTF_OP_START 0x1 +#define PSERIES_WDTF_OP_STOP 0x2 +#define PSERIES_WDTF_OP_QUERY 0x3 +#define PSERIES_WDTF_OP_QUERY_LPM 0x4 + +/* Bits 56-63: "timeoutAction" */ +FIELD_BE(PSERIES_WDTF, ACTION, 56, 8) +#define PSERIES_WDTF_ACTION_HARD_POWER_OFF 0x1 +#define PSERIES_WDTF_ACTION_HARD_RESTART 0x2 +#define PSERIES_WDTF_ACTION_DUMP_RESTART 0x3 + +FIELD_BE(PSERIES_WDTF, RESERVED, 0, 47) + +/* Special watchdogNumber for the "stop all watchdogs" operation */ +#define PSERIES_WDT_STOP_ALL ((uint64_t)~0) + +/* + * For the "Query watchdog capabilities" operation, a uint64 structure + * defined as: + * Bits 0-15: The minimum supported timeout in milliseconds + * Bits 16-31: The number of watchdogs supported + * Bits 32-63: Reserved + */ +FIELD_BE(PSERIES_WDTQ, MIN_TIMEOUT, 0, 16) +FIELD_BE(PSERIES_WDTQ, NUM, 16, 16) + +/* + * For the "Query watchdog LPM requirement" operation: + * 1 = The given "watchdogNumber" must be stopped prior to suspending + * 2 = The given "watchdogNumber" does not have to be stopped prior to + * suspending + */ +#define PSERIES_WDTQL_STOPPED 1 +#define PSERIES_WDTQL_QUERY_NOT_STOPPED 2 + +#define WDT_MIN_TIMEOUT 1 /* 1ms */ + +static target_ulong watchdog_stop(unsigned watchdogNumber, SpaprWatchdog *w) +{ + target_ulong ret = H_NOOP; + + if (timer_pending(&w->timer)) { + timer_del(&w->timer); + ret = H_SUCCESS; + } + trace_spapr_watchdog_stop(watchdogNumber, ret); + + return ret; +} + +static target_ulong watchdog_stop_all(SpaprMachineState *spapr) +{ + target_ulong ret = H_NOOP; + int i; + + for (i = 1; i <= ARRAY_SIZE(spapr->wds); ++i) { + target_ulong r = watchdog_stop(i, &spapr->wds[i - 1]); + + if (r != H_NOOP && r != H_SUCCESS) { + ret = r; + } + } + + return ret; +} + +static void watchdog_expired(void *pw) +{ + SpaprWatchdog *w = pw; + CPUState *cs; + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + unsigned num = w - spapr->wds; + + g_assert(num < ARRAY_SIZE(spapr->wds)); + trace_spapr_watchdog_expired(num, w->action); + switch (w->action) { + case PSERIES_WDTF_ACTION_HARD_POWER_OFF: + qemu_system_vmstop_request(RUN_STATE_SHUTDOWN); + break; + case PSERIES_WDTF_ACTION_HARD_RESTART: + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + break; + case PSERIES_WDTF_ACTION_DUMP_RESTART: + CPU_FOREACH(cs) { + async_run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL); + } + break; + } + if (!w->leave_others) { + watchdog_stop_all(spapr); + } +} + +static target_ulong h_watchdog(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong ret = H_SUCCESS; + target_ulong flags = args[0]; + target_ulong watchdogNumber = args[1]; /* 1-Based per PAPR */ + target_ulong timeoutInMs = args[2]; + unsigned operation = FIELD_EX64(flags, PSERIES_WDTF, OP); + unsigned timeoutAction = FIELD_EX64(flags, PSERIES_WDTF, ACTION); + SpaprWatchdog *w; + + if (FIELD_EX64(flags, PSERIES_WDTF, RESERVED)) { + return H_PARAMETER; + } + + switch (operation) { + case PSERIES_WDTF_OP_START: + if (watchdogNumber > ARRAY_SIZE(spapr->wds)) { + return H_P2; + } + if (timeoutInMs <= WDT_MIN_TIMEOUT) { + return H_P3; + } + + w = &spapr->wds[watchdogNumber - 1]; + switch (timeoutAction) { + case PSERIES_WDTF_ACTION_HARD_POWER_OFF: + case PSERIES_WDTF_ACTION_HARD_RESTART: + case PSERIES_WDTF_ACTION_DUMP_RESTART: + w->action = timeoutAction; + break; + default: + return H_PARAMETER; + } + w->leave_others = FIELD_EX64(flags, PSERIES_WDTF, LEAVE_OTHER); + timer_mod(&w->timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + timeoutInMs); + trace_spapr_watchdog_start(flags, watchdogNumber, timeoutInMs); + break; + case PSERIES_WDTF_OP_STOP: + if (watchdogNumber == PSERIES_WDT_STOP_ALL) { + ret = watchdog_stop_all(spapr); + } else if (watchdogNumber <= ARRAY_SIZE(spapr->wds)) { + ret = watchdog_stop(watchdogNumber, + &spapr->wds[watchdogNumber - 1]); + } else { + return H_P2; + } + break; + case PSERIES_WDTF_OP_QUERY: + args[0] = FIELD_DP64(0, PSERIES_WDTQ, MIN_TIMEOUT, WDT_MIN_TIMEOUT); + args[0] = FIELD_DP64(args[0], PSERIES_WDTQ, NUM, + ARRAY_SIZE(spapr->wds)); + trace_spapr_watchdog_query(args[0]); + break; + case PSERIES_WDTF_OP_QUERY_LPM: + if (watchdogNumber > ARRAY_SIZE(spapr->wds)) { + return H_P2; + } + args[0] = PSERIES_WDTQL_QUERY_NOT_STOPPED; + trace_spapr_watchdog_query_lpm(args[0]); + break; + default: + return H_PARAMETER; + } + + return ret; +} + +void spapr_watchdog_init(SpaprMachineState *spapr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(spapr->wds); ++i) { + char name[16]; + SpaprWatchdog *w = &spapr->wds[i]; + + snprintf(name, sizeof(name) - 1, "wdt%d", i + 1); + object_initialize_child_with_props(OBJECT(spapr), name, w, + sizeof(SpaprWatchdog), + TYPE_SPAPR_WDT, + &error_fatal, NULL); + qdev_realize(DEVICE(w), NULL, &error_fatal); + } +} + +static bool watchdog_needed(void *opaque) +{ + SpaprWatchdog *w = opaque; + + return timer_pending(&w->timer); +} + +static const VMStateDescription vmstate_wdt = { + .name = "spapr_watchdog", + .version_id = 1, + .minimum_version_id = 1, + .needed = watchdog_needed, + .fields = (const VMStateField[]) { + VMSTATE_TIMER(timer, SpaprWatchdog), + VMSTATE_UINT8(action, SpaprWatchdog), + VMSTATE_UINT8(leave_others, SpaprWatchdog), + VMSTATE_END_OF_LIST() + } +}; + +static void spapr_wdt_realize(DeviceState *dev, Error **errp) +{ + SpaprWatchdog *w = SPAPR_WDT(dev); + Object *o = OBJECT(dev); + + timer_init_ms(&w->timer, QEMU_CLOCK_VIRTUAL, watchdog_expired, w); + + object_property_add_uint64_ptr(o, "expire", + (uint64_t *)&w->timer.expire_time, + OBJ_PROP_FLAG_READ); + object_property_add_uint8_ptr(o, "action", &w->action, OBJ_PROP_FLAG_READ); + object_property_add_uint8_ptr(o, "leaveOtherWatchdogsRunningOnTimeout", + &w->leave_others, OBJ_PROP_FLAG_READ); +} + +static void spapr_wdt_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = spapr_wdt_realize; + dc->vmsd = &vmstate_wdt; + dc->user_creatable = false; +} + +static const TypeInfo spapr_wdt_info = { + .name = TYPE_SPAPR_WDT, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SpaprWatchdog), + .class_init = spapr_wdt_class_init, +}; + +static void spapr_watchdog_register_types(void) +{ + spapr_register_hypercall(H_WATCHDOG, h_watchdog); + type_register_static(&spapr_wdt_info); +} + +type_init(spapr_watchdog_register_types) diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events index e7523e22aa..ad3be1e9bd 100644 --- a/hw/watchdog/trace-events +++ b/hw/watchdog/trace-events @@ -1,5 +1,12 @@ # See docs/devel/tracing.rst for syntax documentation. +# allwinner-wdt.c +allwinner_wdt_read(uint64_t offset, uint64_t data, unsigned size) "Allwinner watchdog read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +allwinner_wdt_write(uint64_t offset, uint64_t data, unsigned size) "Allwinner watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +allwinner_wdt_reset_enter(void) "Allwinner watchdog: reset" +allwinner_wdt_update_timer(uint8_t count) "Allwinner watchdog: count %" PRIu8 +allwinner_wdt_expired(bool enabled, bool reset_enabled) "Allwinner watchdog: enabled %u reset_enabled %u" + # cmsdk-apb-watchdog.c cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" @@ -9,3 +16,20 @@ cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" PRIu32 # wdt-aspeed.c aspeed_wdt_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" aspeed_wdt_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 + +# wdt_imx2.c +imx2_wdt_read(uint32_t addr, uint16_t data) "[0x%" PRIx32 "] -> 0x%" PRIx16 +imx2_wdt_write(uint32_t addr, uint16_t data) "[0x%" PRIx32 "] <- 0x%" PRIx16 +imx2_wdt_interrupt(void) "" +imx2_wdt_expired(void) "" + +# spapr_watchdog.c +spapr_watchdog_start(uint64_t flags, uint64_t num, uint64_t timeout) "Flags 0x%" PRIx64 " num=%" PRId64 " %" PRIu64 "ms" +spapr_watchdog_stop(uint64_t num, uint64_t ret) "num=%" PRIu64 " ret=%" PRId64 +spapr_watchdog_query(uint64_t caps) "caps=0x%" PRIx64 +spapr_watchdog_query_lpm(uint64_t caps) "caps=0x%" PRIx64 +spapr_watchdog_expired(uint64_t num, unsigned action) "num=%" PRIu64 " action=%u" + +# watchdog.c +watchdog_perform_action(unsigned int action) "action=%u" +watchdog_set_action(unsigned int action) "action=%u" diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c index 1437e6c5b6..955046161b 100644 --- a/hw/watchdog/watchdog.c +++ b/hw/watchdog/watchdog.c @@ -30,51 +30,9 @@ #include "sysemu/watchdog.h" #include "hw/nmi.h" #include "qemu/help_option.h" +#include "trace.h" static WatchdogAction watchdog_action = WATCHDOG_ACTION_RESET; -static QLIST_HEAD(, WatchdogTimerModel) watchdog_list; - -void watchdog_add_model(WatchdogTimerModel *model) -{ - QLIST_INSERT_HEAD(&watchdog_list, model, entry); -} - -/* Returns: - * 0 = continue - * 1 = exit program with error - * 2 = exit program without error - */ -int select_watchdog(const char *p) -{ - WatchdogTimerModel *model; - QemuOpts *opts; - - /* -watchdog ? lists available devices and exits cleanly. */ - if (is_help_option(p)) { - QLIST_FOREACH(model, &watchdog_list, entry) { - fprintf(stderr, "\t%s\t%s\n", - model->wdt_name, model->wdt_description); - } - return 2; - } - - QLIST_FOREACH(model, &watchdog_list, entry) { - if (strcasecmp(model->wdt_name, p) == 0) { - /* add the device */ - opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, - &error_abort); - qemu_opt_set(opts, "driver", p, &error_abort); - return 0; - } - } - - fprintf(stderr, "Unknown -watchdog device. Supported devices are:\n"); - QLIST_FOREACH(model, &watchdog_list, entry) { - fprintf(stderr, "\t%s\t%s\n", - model->wdt_name, model->wdt_description); - } - return 1; -} WatchdogAction get_watchdog_action(void) { @@ -86,6 +44,8 @@ WatchdogAction get_watchdog_action(void) */ void watchdog_perform_action(void) { + trace_watchdog_perform_action(watchdog_action); + switch (watchdog_action) { case WATCHDOG_ACTION_RESET: /* same as 'system_reset' in monitor */ qapi_event_send_watchdog(WATCHDOG_ACTION_RESET); @@ -132,4 +92,5 @@ void watchdog_perform_action(void) void qmp_watchdog_set_action(WatchdogAction action, Error **errp) { watchdog_action = action; + trace_watchdog_set_action(watchdog_action); } diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index 31855afdf4..d70b656f8e 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -14,7 +14,6 @@ #include "qemu/module.h" #include "qemu/timer.h" #include "sysemu/watchdog.h" -#include "hw/misc/aspeed_scu.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "hw/watchdog/wdt_aspeed.h" @@ -42,6 +41,11 @@ #define WDT_PUSH_PULL_MAGIC (0xA8 << 24) #define WDT_OPEN_DRAIN_MAGIC (0x8A << 24) #define WDT_RESET_MASK1 (0x1c / 4) +#define WDT_RESET_MASK2 (0x20 / 4) + +#define WDT_SW_RESET_CTRL (0x24 / 4) +#define WDT_SW_RESET_MASK1 (0x28 / 4) +#define WDT_SW_RESET_MASK2 (0x2c / 4) #define WDT_TIMEOUT_STATUS (0x10 / 4) #define WDT_TIMEOUT_CLEAR (0x14 / 4) @@ -83,6 +87,10 @@ static uint64_t aspeed_wdt_read(void *opaque, hwaddr offset, unsigned size) return s->regs[WDT_RESET_MASK1]; case WDT_TIMEOUT_STATUS: case WDT_TIMEOUT_CLEAR: + case WDT_RESET_MASK2: + case WDT_SW_RESET_CTRL: + case WDT_SW_RESET_MASK1: + case WDT_SW_RESET_MASK2: qemu_log_mask(LOG_UNIMP, "%s: uninmplemented read at offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -190,6 +198,10 @@ static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, case WDT_TIMEOUT_STATUS: case WDT_TIMEOUT_CLEAR: + case WDT_RESET_MASK2: + case WDT_SW_RESET_CTRL: + case WDT_SW_RESET_MASK1: + case WDT_SW_RESET_MASK2: qemu_log_mask(LOG_UNIMP, "%s: uninmplemented write at offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -202,16 +214,11 @@ static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, return; } -static WatchdogTimerModel model = { - .wdt_name = TYPE_ASPEED_WDT, - .wdt_description = "Aspeed watchdog device", -}; - static const VMStateDescription vmstate_aspeed_wdt = { .name = "vmstate_aspeed_wdt", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(timer, AspeedWDTState), VMSTATE_UINT32_ARRAY(regs, AspeedWDTState, ASPEED_WDT_REGS_MAX), VMSTATE_END_OF_LIST() @@ -265,6 +272,7 @@ static void aspeed_wdt_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AspeedWDTState *s = ASPEED_WDT(dev); + AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(dev); assert(s->scu); @@ -276,7 +284,7 @@ static void aspeed_wdt_realize(DeviceState *dev, Error **errp) s->pclk_freq = PCLK_HZ; memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_wdt_ops, s, - TYPE_ASPEED_WDT, ASPEED_WDT_REGS_MAX * 4); + TYPE_ASPEED_WDT, awc->iosize); sysbus_init_mmio(sbd, &s->iomem); } @@ -314,7 +322,7 @@ static void aspeed_2400_wdt_class_init(ObjectClass *klass, void *data) AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); dc->desc = "ASPEED 2400 Watchdog Controller"; - awc->offset = 0x20; + awc->iosize = 0x20; awc->ext_pulse_width_mask = 0xff; awc->reset_ctrl_reg = SCU_RESET_CONTROL1; awc->wdt_reload = aspeed_wdt_reload; @@ -351,7 +359,7 @@ static void aspeed_2500_wdt_class_init(ObjectClass *klass, void *data) AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); dc->desc = "ASPEED 2500 Watchdog Controller"; - awc->offset = 0x20; + awc->iosize = 0x20; awc->ext_pulse_width_mask = 0xfffff; awc->reset_ctrl_reg = SCU_RESET_CONTROL1; awc->reset_pulse = aspeed_2500_wdt_reset_pulse; @@ -374,7 +382,7 @@ static void aspeed_2600_wdt_class_init(ObjectClass *klass, void *data) AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); dc->desc = "ASPEED 2600 Watchdog Controller"; - awc->offset = 0x40; + awc->iosize = 0x40; awc->ext_pulse_width_mask = 0xfffff; /* TODO */ awc->reset_ctrl_reg = AST2600_SCU_RESET_CONTROL1; awc->reset_pulse = aspeed_2500_wdt_reset_pulse; @@ -397,7 +405,7 @@ static void aspeed_1030_wdt_class_init(ObjectClass *klass, void *data) AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); dc->desc = "ASPEED 1030 Watchdog Controller"; - awc->offset = 0x80; + awc->iosize = 0x80; awc->ext_pulse_width_mask = 0xfffff; /* TODO */ awc->reset_ctrl_reg = AST2600_SCU_RESET_CONTROL1; awc->reset_pulse = aspeed_2500_wdt_reset_pulse; @@ -416,7 +424,6 @@ static const TypeInfo aspeed_1030_wdt_info = { static void wdt_aspeed_register_types(void) { - watchdog_add_model(&model); type_register_static(&aspeed_wdt_info); type_register_static(&aspeed_2400_wdt_info); type_register_static(&aspeed_2500_wdt_info); diff --git a/hw/watchdog/wdt_diag288.c b/hw/watchdog/wdt_diag288.c index 9e8882a11c..1b73b16fb3 100644 --- a/hw/watchdog/wdt_diag288.c +++ b/hw/watchdog/wdt_diag288.c @@ -19,16 +19,11 @@ #include "migration/vmstate.h" #include "qemu/log.h" -static WatchdogTimerModel model = { - .wdt_name = TYPE_WDT_DIAG288, - .wdt_description = "diag288 device for s390x platform", -}; - static const VMStateDescription vmstate_diag288 = { .name = "vmstate_diag288", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(timer, DIAG288State), VMSTATE_BOOL(enabled, DIAG288State), VMSTATE_END_OF_LIST() @@ -138,7 +133,6 @@ static const TypeInfo wdt_diag288_info = { static void wdt_diag288_register_types(void) { - watchdog_add_model(&model); type_register_static(&wdt_diag288_info); } diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c index f99a1c9d29..8bce0509cd 100644 --- a/hw/watchdog/wdt_i6300esb.c +++ b/hw/watchdog/wdt_i6300esb.c @@ -24,7 +24,7 @@ #include "qemu/module.h" #include "qemu/timer.h" #include "sysemu/watchdog.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "migration/vmstate.h" #include "qom/object.h" @@ -418,7 +418,7 @@ static const VMStateDescription vmstate_i6300esb = { */ .version_id = 10000, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, I6300State), VMSTATE_INT32(reboot_enabled, I6300State), VMSTATE_INT32(clock_scale, I6300State), @@ -457,11 +457,6 @@ static void i6300esb_exit(PCIDevice *dev) timer_free(d->timer); } -static WatchdogTimerModel model = { - .wdt_name = "i6300esb", - .wdt_description = "Intel 6300ESB", -}; - static void i6300esb_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -493,7 +488,6 @@ static const TypeInfo i6300esb_info = { static void i6300esb_register_types(void) { - watchdog_add_model(&model); type_register_static(&i6300esb_info); } diff --git a/hw/watchdog/wdt_ib700.c b/hw/watchdog/wdt_ib700.c index 91d1bdc0da..eea8da6059 100644 --- a/hw/watchdog/wdt_ib700.c +++ b/hw/watchdog/wdt_ib700.c @@ -30,7 +30,7 @@ /*#define IB700_DEBUG 1*/ #ifdef IB700_DEBUG -#define ib700_debug(fs,...) \ +#define ib700_debug(fs,...) \ fprintf(stderr,"ib700: %s: "fs,__func__,##__VA_ARGS__) #else #define ib700_debug(fs,...) @@ -95,7 +95,7 @@ static const VMStateDescription vmstate_ib700 = { .name = "ib700_wdt", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(timer, IB700State), VMSTATE_END_OF_LIST() } @@ -128,11 +128,6 @@ static void wdt_ib700_reset(DeviceState *dev) timer_del(s->timer); } -static WatchdogTimerModel model = { - .wdt_name = "ib700", - .wdt_description = "iBASE 700", -}; - static void wdt_ib700_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -153,7 +148,6 @@ static const TypeInfo wdt_ib700_info = { static void wdt_ib700_register_types(void) { - watchdog_add_model(&model); type_register_static(&wdt_ib700_info); } diff --git a/hw/watchdog/wdt_imx2.c b/hw/watchdog/wdt_imx2.c index c3128370b5..6452fc4721 100644 --- a/hw/watchdog/wdt_imx2.c +++ b/hw/watchdog/wdt_imx2.c @@ -17,11 +17,14 @@ #include "hw/qdev-properties.h" #include "hw/watchdog/wdt_imx2.h" +#include "trace.h" static void imx2_wdt_interrupt(void *opaque) { IMX2WdtState *s = IMX2_WDT(opaque); + trace_imx2_wdt_interrupt(); + s->wicr |= IMX2_WDT_WICR_WTIS; qemu_set_irq(s->irq, 1); } @@ -30,6 +33,8 @@ static void imx2_wdt_expired(void *opaque) { IMX2WdtState *s = IMX2_WDT(opaque); + trace_imx2_wdt_expired(); + s->wrsr = IMX2_WDT_WRSR_TOUT; /* Perform watchdog action if watchdog is enabled */ @@ -67,20 +72,29 @@ static void imx2_wdt_reset(DeviceState *dev) static uint64_t imx2_wdt_read(void *opaque, hwaddr addr, unsigned int size) { IMX2WdtState *s = IMX2_WDT(opaque); + uint16_t value = 0; switch (addr) { case IMX2_WDT_WCR: - return s->wcr; + value = s->wcr; + break; case IMX2_WDT_WSR: - return s->wsr; + value = s->wsr; + break; case IMX2_WDT_WRSR: - return s->wrsr; + value = s->wrsr; + break; case IMX2_WDT_WICR: - return s->wicr; + value = s->wicr; + break; case IMX2_WDT_WMCR: - return s->wmcr; + value = s->wmcr; + break; } - return 0; + + trace_imx2_wdt_read(addr, value); + + return value; } static void imx_wdt2_update_itimer(IMX2WdtState *s, bool start) @@ -137,6 +151,8 @@ static void imx2_wdt_write(void *opaque, hwaddr addr, { IMX2WdtState *s = IMX2_WDT(opaque); + trace_imx2_wdt_write(addr, value); + switch (addr) { case IMX2_WDT_WCR: if (s->wcr_locked) { @@ -218,7 +234,7 @@ static const MemoryRegionOps imx2_wdt_ops = { static const VMStateDescription vmstate_imx2_wdt = { .name = "imx2.wdt", - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PTIMER(timer, IMX2WdtState), VMSTATE_PTIMER(itimer, IMX2WdtState), VMSTATE_BOOL(wicr_locked, IMX2WdtState), @@ -291,14 +307,8 @@ static const TypeInfo imx2_wdt_info = { .class_init = imx2_wdt_class_init, }; -static WatchdogTimerModel model = { - .wdt_name = "imx2-watchdog", - .wdt_description = "i.MX2 Watchdog", -}; - static void imx2_wdt_register_type(void) { - watchdog_add_model(&model); type_register_static(&imx2_wdt_info); } type_init(imx2_wdt_register_type) diff --git a/hw/xen/Kconfig b/hw/xen/Kconfig new file mode 100644 index 0000000000..3467efb986 --- /dev/null +++ b/hw/xen/Kconfig @@ -0,0 +1,3 @@ +config XEN_BUS + bool + default y if (XEN || XEN_EMU) diff --git a/hw/xen/meson.build b/hw/xen/meson.build index 08dc1f6857..d887fa9ba4 100644 --- a/hw/xen/meson.build +++ b/hw/xen/meson.build @@ -1,4 +1,4 @@ -softmmu_ss.add(when: ['CONFIG_XEN', xen], if_true: files( +system_ss.add(when: ['CONFIG_XEN_BUS'], if_true: files( 'xen-backend.c', 'xen-bus-helper.c', 'xen-bus.c', @@ -7,7 +7,15 @@ softmmu_ss.add(when: ['CONFIG_XEN', xen], if_true: files( 'xen_pvdev.c', )) +system_ss.add(when: ['CONFIG_XEN', xen], if_true: files( + 'xen-operations.c', +)) + xen_specific_ss = ss.source_set() +xen_specific_ss.add(files( + 'xen-mapcache.c', + 'xen-hvm-common.c', +)) if have_xen_pci_passthrough xen_specific_ss.add(files( 'xen-host-pci-device.c', @@ -18,7 +26,7 @@ if have_xen_pci_passthrough 'xen_pt_msi.c', )) else - xen_specific_ss.add('xen_pt_stub.c') + xen_specific_ss.add(files('xen_pt_stub.c')) endif specific_ss.add_all(when: ['CONFIG_XEN', xen], if_true: xen_specific_ss) diff --git a/hw/xen/trace-events b/hw/xen/trace-events index 3da3fd8348..d1b27f6c11 100644 --- a/hw/xen/trace-events +++ b/hw/xen/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.rst for syntax documentation. -# ../../include/hw/xen/xen_common.h +# ../../include/hw/xen/xen_native.h xen_default_ioreq_server(void) "" xen_ioreq_server_create(uint32_t id) "id: %u" xen_ioreq_server_destroy(uint32_t id) "id: %u" @@ -41,3 +41,41 @@ xs_node_vprintf(char *path, char *value) "%s %s" xs_node_vscanf(char *path, char *value) "%s %s" xs_node_watch(char *path) "%s" xs_node_unwatch(char *path) "%s" + +# xen-hvm-common.c +xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: 0x%lx, size 0x%lx" +xen_client_set_memory(uint64_t start_addr, unsigned long size, bool log_dirty) "0x%"PRIx64" size 0x%lx, log_dirty %i" +handle_ioreq(void *req, uint32_t type, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p type=%d dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" +handle_ioreq_read(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p read type=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" +handle_ioreq_write(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p write type=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" +cpu_ioreq_pio(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p pio dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" +cpu_ioreq_pio_read_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio read reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" +cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" +cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" +xen_map_resource_ioreq(uint32_t id, void *addr) "id: %u addr: %p" +cpu_ioreq_config_read(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" +cpu_ioreq_config_write(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" +cpu_get_ioreq_from_shared_memory_req_not_ready(int state, int data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O request not ready: 0x%x, ptr: 0x%x, port: 0x%"PRIx64", data: 0x%"PRIx64", count: %u, size: %u" +xen_main_loop_prepare_init_cpu(int id, void *cpu) "cpu_by_vcpu_id[%d]=%p" +xen_map_ioreq_server_shared_page(long unsigned int ioreq_pfn) "shared page at pfn 0x%lx" +xen_map_ioreq_server_buffered_io_page(long unsigned int ioreq_pfn) "buffered io page at pfn 0x%lx" +xen_map_ioreq_server_buffered_io_evtchn(int bufioreq_evtchn) "buffered io evtchn is 0x%x" +destroy_hvm_domain_cannot_acquire_handle(void) "Cannot acquire xenctrl handle" +destroy_hvm_domain_failed_action(const char *action, int sts, char *errno_s) "xc_domain_shutdown failed to issue %s, sts %d, %s" +destroy_hvm_domain_action(int xen_domid, const char *action) "Issued domain %d %s" + +# xen-mapcache.c +xen_map_cache(uint64_t phys_addr) "want 0x%"PRIx64 +xen_remap_bucket(uint64_t index) "index 0x%"PRIx64 +xen_map_cache_return(void* ptr) "%p" +xen_map_cache_init(uint64_t nr_buckets, uint64_t size) "nr_buckets = 0x%"PRIx64" size 0x%"PRIx64 +xen_replace_cache_entry_dummy(uint64_t old_phys_addr, uint64_t new_phys_addr) "Replacing a dummy mapcache entry for 0x%"PRIx64" with 0x%"PRIx64 +xen_invalidate_map_cache_entry_unlocked_not_found(void *p) "could not find %p" +xen_invalidate_map_cache_entry_unlocked_found(uint64_t addr, void *p) " 0x%"PRIx64" -> %p is present" +xen_invalidate_map_cache_entry_unlocked_miss(void *buffer) "Trying to unmap address %p that is not in the mapcache" +xen_replace_cache_entry_unlocked_could_not_update_entry(uint64_t old_phys_addr) "Unable to update a mapcache entry for 0x%"PRIx64 +xen_ram_addr_from_mapcache_not_found(void *p) "could not find %p" +xen_ram_addr_from_mapcache_found(uint64_t addr, void *p) " 0x%"PRIx64" -> %p is present" +xen_ram_addr_from_mapcache_not_in_cache(void *p) "Trying to find address %p that is not in the mapcache" +xen_replace_cache_entry_unlocked(uint64_t old_phys_addr) "Trying to update an entry for 0x%"PRIx64" that is not in the mapcache" +xen_invalidate_map_cache(uint64_t paddr_index, void *vaddr_req) "Locked DMA mapping while invalidating mapcache 0x%"PRIx64" -> %p is present" diff --git a/hw/xen/xen-backend.c b/hw/xen/xen-backend.c index 5b0fb76eae..b9bf70a9f5 100644 --- a/hw/xen/xen-backend.c +++ b/hw/xen/xen-backend.c @@ -101,6 +101,24 @@ static XenBackendInstance *xen_backend_list_find(XenDevice *xendev) return NULL; } +bool xen_backend_exists(const char *type, const char *name) +{ + const XenBackendImpl *impl = xen_backend_table_lookup(type); + XenBackendInstance *backend; + + if (!impl) { + return false; + } + + QLIST_FOREACH(backend, &backend_list, entry) { + if (backend->impl == impl && !strcmp(backend->name, name)) { + return true; + } + } + + return false; +} + static void xen_backend_list_remove(XenBackendInstance *backend) { QLIST_REMOVE(backend, entry); @@ -122,11 +140,6 @@ void xen_backend_device_create(XenBus *xenbus, const char *type, backend->name = g_strdup(name); impl->create(backend, opts, errp); - if (*errp) { - g_free(backend->name); - g_free(backend); - return; - } backend->impl = impl; xen_backend_list_add(backend); @@ -165,7 +178,9 @@ bool xen_backend_try_device_destroy(XenDevice *xendev, Error **errp) } impl = backend->impl; - impl->destroy(backend, errp); + if (backend->xendev) { + impl->destroy(backend, errp); + } xen_backend_list_remove(backend); g_free(backend->name); diff --git a/hw/xen/xen-bus-helper.c b/hw/xen/xen-bus-helper.c index 5a1e12b374..b2b2cc9c5d 100644 --- a/hw/xen/xen-bus-helper.c +++ b/hw/xen/xen-bus-helper.c @@ -10,6 +10,7 @@ #include "hw/xen/xen-bus.h" #include "hw/xen/xen-bus-helper.h" #include "qapi/error.h" +#include "trace.h" #include @@ -46,34 +47,28 @@ const char *xs_strstate(enum xenbus_state state) return "INVALID"; } -void xs_node_create(struct xs_handle *xsh, xs_transaction_t tid, - const char *node, struct xs_permissions perms[], - unsigned int nr_perms, Error **errp) +void xs_node_create(struct qemu_xs_handle *h, xs_transaction_t tid, + const char *node, unsigned int owner, unsigned int domid, + unsigned int perms, Error **errp) { trace_xs_node_create(node); - if (!xs_write(xsh, tid, node, "", 0)) { + if (!qemu_xen_xs_create(h, tid, owner, domid, perms, node)) { error_setg_errno(errp, errno, "failed to create node '%s'", node); - return; - } - - if (!xs_set_permissions(xsh, tid, node, perms, nr_perms)) { - error_setg_errno(errp, errno, "failed to set node '%s' permissions", - node); } } -void xs_node_destroy(struct xs_handle *xsh, xs_transaction_t tid, +void xs_node_destroy(struct qemu_xs_handle *h, xs_transaction_t tid, const char *node, Error **errp) { trace_xs_node_destroy(node); - if (!xs_rm(xsh, tid, node)) { + if (!qemu_xen_xs_destroy(h, tid, node)) { error_setg_errno(errp, errno, "failed to destroy node '%s'", node); } } -void xs_node_vprintf(struct xs_handle *xsh, xs_transaction_t tid, +void xs_node_vprintf(struct qemu_xs_handle *h, xs_transaction_t tid, const char *node, const char *key, Error **errp, const char *fmt, va_list ap) { @@ -86,7 +81,7 @@ void xs_node_vprintf(struct xs_handle *xsh, xs_transaction_t tid, trace_xs_node_vprintf(path, value); - if (!xs_write(xsh, tid, path, value, len)) { + if (!qemu_xen_xs_write(h, tid, path, value, len)) { error_setg_errno(errp, errno, "failed to write '%s' to '%s'", value, path); } @@ -95,18 +90,18 @@ void xs_node_vprintf(struct xs_handle *xsh, xs_transaction_t tid, g_free(path); } -void xs_node_printf(struct xs_handle *xsh, xs_transaction_t tid, +void xs_node_printf(struct qemu_xs_handle *h, xs_transaction_t tid, const char *node, const char *key, Error **errp, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - xs_node_vprintf(xsh, tid, node, key, errp, fmt, ap); + xs_node_vprintf(h, tid, node, key, errp, fmt, ap); va_end(ap); } -int xs_node_vscanf(struct xs_handle *xsh, xs_transaction_t tid, +int xs_node_vscanf(struct qemu_xs_handle *h, xs_transaction_t tid, const char *node, const char *key, Error **errp, const char *fmt, va_list ap) { @@ -115,7 +110,7 @@ int xs_node_vscanf(struct xs_handle *xsh, xs_transaction_t tid, path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) : g_strdup(key); - value = xs_read(xsh, tid, path, NULL); + value = qemu_xen_xs_read(h, tid, path, NULL); trace_xs_node_vscanf(path, value); @@ -133,7 +128,7 @@ int xs_node_vscanf(struct xs_handle *xsh, xs_transaction_t tid, return rc; } -int xs_node_scanf(struct xs_handle *xsh, xs_transaction_t tid, +int xs_node_scanf(struct qemu_xs_handle *h, xs_transaction_t tid, const char *node, const char *key, Error **errp, const char *fmt, ...) { @@ -141,42 +136,35 @@ int xs_node_scanf(struct xs_handle *xsh, xs_transaction_t tid, int rc; va_start(ap, fmt); - rc = xs_node_vscanf(xsh, tid, node, key, errp, fmt, ap); + rc = xs_node_vscanf(h, tid, node, key, errp, fmt, ap); va_end(ap); return rc; } -void xs_node_watch(struct xs_handle *xsh, const char *node, const char *key, - char *token, Error **errp) +struct qemu_xs_watch *xs_node_watch(struct qemu_xs_handle *h, const char *node, + const char *key, xs_watch_fn fn, + void *opaque, Error **errp) { char *path; + struct qemu_xs_watch *w; path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) : g_strdup(key); trace_xs_node_watch(path); - if (!xs_watch(xsh, path, token)) { + w = qemu_xen_xs_watch(h, path, fn, opaque); + if (!w) { error_setg_errno(errp, errno, "failed to watch node '%s'", path); } g_free(path); + + return w; } -void xs_node_unwatch(struct xs_handle *xsh, const char *node, - const char *key, const char *token, Error **errp) +void xs_node_unwatch(struct qemu_xs_handle *h, struct qemu_xs_watch *w) { - char *path; - - path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) : - g_strdup(key); - - trace_xs_node_unwatch(path); - - if (!xs_unwatch(xsh, path, token)) { - error_setg_errno(errp, errno, "failed to unwatch node '%s'", path); - } - - g_free(path); + qemu_xen_xs_unwatch(h, w); } diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index 645a29a5a0..fb82cc33e4 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -19,6 +19,7 @@ #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "sysemu/sysemu.h" +#include "net/net.h" #include "trace.h" static char *xen_device_get_backend_path(XenDevice *xendev) @@ -62,7 +63,7 @@ static void xen_device_unplug(XenDevice *xendev, Error **errp) /* Mimic the way the Xen toolstack does an unplug */ again: - tid = xs_transaction_start(xenbus->xsh); + tid = qemu_xen_xs_transaction_start(xenbus->xsh); if (tid == XBT_NULL) { error_setg_errno(errp, errno, "failed xs_transaction_start"); return; @@ -80,7 +81,7 @@ again: goto abort; } - if (!xs_transaction_end(xenbus->xsh, tid, false)) { + if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, false)) { if (errno == EAGAIN) { goto again; } @@ -95,7 +96,7 @@ abort: * We only abort if there is already a failure so ignore any error * from ending the transaction. */ - xs_transaction_end(xenbus->xsh, tid, true); + qemu_xen_xs_transaction_end(xenbus->xsh, tid, true); } static void xen_bus_print_dev(Monitor *mon, DeviceState *dev, int indent) @@ -111,143 +112,6 @@ static char *xen_bus_get_dev_path(DeviceState *dev) return xen_device_get_backend_path(XEN_DEVICE(dev)); } -struct XenWatch { - char *node, *key; - char *token; - XenWatchHandler handler; - void *opaque; - Notifier notifier; -}; - -static void watch_notify(Notifier *n, void *data) -{ - XenWatch *watch = container_of(n, XenWatch, notifier); - const char *token = data; - - if (!strcmp(watch->token, token)) { - watch->handler(watch->opaque); - } -} - -static XenWatch *new_watch(const char *node, const char *key, - XenWatchHandler handler, void *opaque) -{ - XenWatch *watch = g_new0(XenWatch, 1); - QemuUUID uuid; - - qemu_uuid_generate(&uuid); - - watch->token = qemu_uuid_unparse_strdup(&uuid); - watch->node = g_strdup(node); - watch->key = g_strdup(key); - watch->handler = handler; - watch->opaque = opaque; - watch->notifier.notify = watch_notify; - - return watch; -} - -static void free_watch(XenWatch *watch) -{ - g_free(watch->token); - g_free(watch->key); - g_free(watch->node); - - g_free(watch); -} - -struct XenWatchList { - struct xs_handle *xsh; - NotifierList notifiers; -}; - -static void watch_list_event(void *opaque) -{ - XenWatchList *watch_list = opaque; - char **v; - const char *token; - - v = xs_check_watch(watch_list->xsh); - if (!v) { - return; - } - - token = v[XS_WATCH_TOKEN]; - - notifier_list_notify(&watch_list->notifiers, (void *)token); - - free(v); -} - -static XenWatchList *watch_list_create(struct xs_handle *xsh) -{ - XenWatchList *watch_list = g_new0(XenWatchList, 1); - - g_assert(xsh); - - watch_list->xsh = xsh; - notifier_list_init(&watch_list->notifiers); - qemu_set_fd_handler(xs_fileno(watch_list->xsh), watch_list_event, NULL, - watch_list); - - return watch_list; -} - -static void watch_list_destroy(XenWatchList *watch_list) -{ - g_assert(notifier_list_empty(&watch_list->notifiers)); - qemu_set_fd_handler(xs_fileno(watch_list->xsh), NULL, NULL, NULL); - g_free(watch_list); -} - -static XenWatch *watch_list_add(XenWatchList *watch_list, const char *node, - const char *key, XenWatchHandler handler, - void *opaque, Error **errp) -{ - ERRP_GUARD(); - XenWatch *watch = new_watch(node, key, handler, opaque); - - notifier_list_add(&watch_list->notifiers, &watch->notifier); - - xs_node_watch(watch_list->xsh, node, key, watch->token, errp); - if (*errp) { - notifier_remove(&watch->notifier); - free_watch(watch); - - return NULL; - } - - return watch; -} - -static void watch_list_remove(XenWatchList *watch_list, XenWatch *watch, - Error **errp) -{ - xs_node_unwatch(watch_list->xsh, watch->node, watch->key, watch->token, - errp); - - notifier_remove(&watch->notifier); - free_watch(watch); -} - -static XenWatch *xen_bus_add_watch(XenBus *xenbus, const char *node, - const char *key, XenWatchHandler handler, - Error **errp) -{ - trace_xen_bus_add_watch(node, key); - - return watch_list_add(xenbus->watch_list, node, key, handler, xenbus, - errp); -} - -static void xen_bus_remove_watch(XenBus *xenbus, XenWatch *watch, - Error **errp) -{ - trace_xen_bus_remove_watch(watch->node, watch->key); - - watch_list_remove(xenbus->watch_list, watch, errp); -} - static void xen_bus_backend_create(XenBus *xenbus, const char *type, const char *name, char *path, Error **errp) @@ -261,15 +125,15 @@ static void xen_bus_backend_create(XenBus *xenbus, const char *type, trace_xen_bus_backend_create(type, path); again: - tid = xs_transaction_start(xenbus->xsh); + tid = qemu_xen_xs_transaction_start(xenbus->xsh); if (tid == XBT_NULL) { error_setg(errp, "failed xs_transaction_start"); return; } - key = xs_directory(xenbus->xsh, tid, path, &n); + key = qemu_xen_xs_directory(xenbus->xsh, tid, path, &n); if (!key) { - if (!xs_transaction_end(xenbus->xsh, tid, true)) { + if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, true)) { error_setg_errno(errp, errno, "failed xs_transaction_end"); } return; @@ -300,7 +164,7 @@ again: free(key); - if (!xs_transaction_end(xenbus->xsh, tid, false)) { + if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, false)) { qobject_unref(opts); if (errno == EAGAIN) { @@ -327,7 +191,7 @@ static void xen_bus_type_enumerate(XenBus *xenbus, const char *type) trace_xen_bus_type_enumerate(type); - backend = xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n); + backend = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n); if (!backend) { goto out; } @@ -346,7 +210,8 @@ static void xen_bus_type_enumerate(XenBus *xenbus, const char *type) NULL, "%u", &online) != 1) online = 0; - if (online && state == XenbusStateInitialising) { + if (online && state == XenbusStateInitialising && + !xen_backend_exists(type, backend[i])) { Error *local_err = NULL; xen_bus_backend_create(xenbus, type, backend[i], backend_path, @@ -372,7 +237,7 @@ static void xen_bus_enumerate(XenBus *xenbus) trace_xen_bus_enumerate(); - type = xs_directory(xenbus->xsh, XBT_NULL, "backend", &n); + type = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, "backend", &n); if (!type) { return; } @@ -415,7 +280,7 @@ static void xen_bus_cleanup(XenBus *xenbus) } } -static void xen_bus_backend_changed(void *opaque) +static void xen_bus_backend_changed(void *opaque, const char *path) { XenBus *xenbus = opaque; @@ -434,7 +299,7 @@ static void xen_bus_unrealize(BusState *bus) for (i = 0; i < xenbus->backend_types; i++) { if (xenbus->backend_watch[i]) { - xen_bus_remove_watch(xenbus, xenbus->backend_watch[i], NULL); + xs_node_unwatch(xenbus->xsh, xenbus->backend_watch[i]); } } @@ -442,13 +307,8 @@ static void xen_bus_unrealize(BusState *bus) xenbus->backend_watch = NULL; } - if (xenbus->watch_list) { - watch_list_destroy(xenbus->watch_list); - xenbus->watch_list = NULL; - } - if (xenbus->xsh) { - xs_close(xenbus->xsh); + qemu_xen_xs_close(xenbus->xsh); } } @@ -463,7 +323,7 @@ static void xen_bus_realize(BusState *bus, Error **errp) trace_xen_bus_realize(); - xenbus->xsh = xs_open(0); + xenbus->xsh = qemu_xen_xs_open(); if (!xenbus->xsh) { error_setg_errno(errp, errno, "failed xs_open"); goto fail; @@ -476,19 +336,18 @@ static void xen_bus_realize(BusState *bus, Error **errp) xenbus->backend_id = 0; /* Assume lack of node means dom0 */ } - xenbus->watch_list = watch_list_create(xenbus->xsh); - module_call_init(MODULE_INIT_XEN_BACKEND); type = xen_backend_get_types(&xenbus->backend_types); - xenbus->backend_watch = g_new(XenWatch *, xenbus->backend_types); + xenbus->backend_watch = g_new(struct qemu_xs_watch *, + xenbus->backend_types); for (i = 0; i < xenbus->backend_types; i++) { char *node = g_strdup_printf("backend/%s", type[i]); xenbus->backend_watch[i] = - xen_bus_add_watch(xenbus, node, key, xen_bus_backend_changed, - &local_err); + xs_node_watch(xenbus->xsh, node, key, xen_bus_backend_changed, + xenbus, &local_err); if (local_err) { /* This need not be treated as a hard error so don't propagate */ error_reportf_err(local_err, @@ -561,6 +420,7 @@ void xen_device_backend_printf(XenDevice *xendev, const char *key, } } +G_GNUC_SCANF(3, 4) static int xen_device_backend_scanf(XenDevice *xendev, const char *key, const char *fmt, ...) { @@ -630,7 +490,7 @@ static bool xen_device_frontend_is_active(XenDevice *xendev) } } -static void xen_device_backend_changed(void *opaque) +static void xen_device_backend_changed(void *opaque, const char *path) { XenDevice *xendev = opaque; const char *type = object_get_typename(OBJECT(xendev)); @@ -684,66 +544,35 @@ static void xen_device_backend_changed(void *opaque) } } -static XenWatch *xen_device_add_watch(XenDevice *xendev, const char *node, - const char *key, - XenWatchHandler handler, - Error **errp) -{ - const char *type = object_get_typename(OBJECT(xendev)); - - trace_xen_device_add_watch(type, xendev->name, node, key); - - return watch_list_add(xendev->watch_list, node, key, handler, xendev, - errp); -} - -static void xen_device_remove_watch(XenDevice *xendev, XenWatch *watch, - Error **errp) -{ - const char *type = object_get_typename(OBJECT(xendev)); - - trace_xen_device_remove_watch(type, xendev->name, watch->node, - watch->key); - - watch_list_remove(xendev->watch_list, watch, errp); -} - - static void xen_device_backend_create(XenDevice *xendev, Error **errp) { ERRP_GUARD(); XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); - struct xs_permissions perms[2]; xendev->backend_path = xen_device_get_backend_path(xendev); - perms[0].id = xenbus->backend_id; - perms[0].perms = XS_PERM_NONE; - perms[1].id = xendev->frontend_id; - perms[1].perms = XS_PERM_READ; - g_assert(xenbus->xsh); - xs_node_create(xenbus->xsh, XBT_NULL, xendev->backend_path, perms, - ARRAY_SIZE(perms), errp); + xs_node_create(xenbus->xsh, XBT_NULL, xendev->backend_path, + xenbus->backend_id, xendev->frontend_id, XS_PERM_READ, errp); if (*errp) { error_prepend(errp, "failed to create backend: "); return; } xendev->backend_state_watch = - xen_device_add_watch(xendev, xendev->backend_path, - "state", xen_device_backend_changed, - errp); + xs_node_watch(xendev->xsh, xendev->backend_path, + "state", xen_device_backend_changed, xendev, + errp); if (*errp) { error_prepend(errp, "failed to watch backend state: "); return; } xendev->backend_online_watch = - xen_device_add_watch(xendev, xendev->backend_path, - "online", xen_device_backend_changed, - errp); + xs_node_watch(xendev->xsh, xendev->backend_path, + "online", xen_device_backend_changed, xendev, + errp); if (*errp) { error_prepend(errp, "failed to watch backend online: "); return; @@ -756,12 +585,12 @@ static void xen_device_backend_destroy(XenDevice *xendev) Error *local_err = NULL; if (xendev->backend_online_watch) { - xen_device_remove_watch(xendev, xendev->backend_online_watch, NULL); + xs_node_unwatch(xendev->xsh, xendev->backend_online_watch); xendev->backend_online_watch = NULL; } if (xendev->backend_state_watch) { - xen_device_remove_watch(xendev, xendev->backend_state_watch, NULL); + xs_node_unwatch(xendev->xsh, xendev->backend_state_watch); xendev->backend_state_watch = NULL; } @@ -836,7 +665,7 @@ static void xen_device_frontend_set_state(XenDevice *xendev, } } -static void xen_device_frontend_changed(void *opaque) +static void xen_device_frontend_changed(void *opaque, const char *path) { XenDevice *xendev = opaque; XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); @@ -884,24 +713,28 @@ static void xen_device_frontend_create(XenDevice *xendev, Error **errp) { ERRP_GUARD(); XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); - struct xs_permissions perms[2]; + XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); - xendev->frontend_path = xen_device_get_frontend_path(xendev); + if (xendev_class->get_frontend_path) { + xendev->frontend_path = xendev_class->get_frontend_path(xendev, errp); + if (!xendev->frontend_path) { + error_prepend(errp, "failed to create frontend: "); + return; + } + } else { + xendev->frontend_path = xen_device_get_frontend_path(xendev); + } /* * The frontend area may have already been created by a legacy * toolstack. */ if (!xen_device_frontend_exists(xendev)) { - perms[0].id = xendev->frontend_id; - perms[0].perms = XS_PERM_NONE; - perms[1].id = xenbus->backend_id; - perms[1].perms = XS_PERM_READ | XS_PERM_WRITE; - g_assert(xenbus->xsh); - xs_node_create(xenbus->xsh, XBT_NULL, xendev->frontend_path, perms, - ARRAY_SIZE(perms), errp); + xs_node_create(xenbus->xsh, XBT_NULL, xendev->frontend_path, + xendev->frontend_id, xenbus->backend_id, + XS_PERM_READ | XS_PERM_WRITE, errp); if (*errp) { error_prepend(errp, "failed to create frontend: "); return; @@ -909,8 +742,8 @@ static void xen_device_frontend_create(XenDevice *xendev, Error **errp) } xendev->frontend_state_watch = - xen_device_add_watch(xendev, xendev->frontend_path, "state", - xen_device_frontend_changed, errp); + xs_node_watch(xendev->xsh, xendev->frontend_path, "state", + xen_device_frontend_changed, xendev, errp); if (*errp) { error_prepend(errp, "failed to watch frontend state: "); } @@ -922,8 +755,7 @@ static void xen_device_frontend_destroy(XenDevice *xendev) Error *local_err = NULL; if (xendev->frontend_state_watch) { - xen_device_remove_watch(xendev, xendev->frontend_state_watch, - NULL); + xs_node_unwatch(xendev->xsh, xendev->frontend_state_watch); xendev->frontend_state_watch = NULL; } @@ -946,7 +778,7 @@ static void xen_device_frontend_destroy(XenDevice *xendev) void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs, Error **errp) { - if (xengnttab_set_max_grants(xendev->xgth, nr_refs)) { + if (qemu_xen_gnttab_set_max_grants(xendev->xgth, nr_refs)) { error_setg_errno(errp, errno, "xengnttab_set_max_grants failed"); } } @@ -955,9 +787,8 @@ void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs, unsigned int nr_refs, int prot, Error **errp) { - void *map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_refs, - xendev->frontend_id, refs, - prot); + void *map = qemu_xen_gnttab_map_refs(xendev->xgth, nr_refs, + xendev->frontend_id, refs, prot); if (!map) { error_setg_errno(errp, errno, @@ -967,112 +798,20 @@ void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs, return map; } -void xen_device_unmap_grant_refs(XenDevice *xendev, void *map, +void xen_device_unmap_grant_refs(XenDevice *xendev, void *map, uint32_t *refs, unsigned int nr_refs, Error **errp) { - if (xengnttab_unmap(xendev->xgth, map, nr_refs)) { + if (qemu_xen_gnttab_unmap(xendev->xgth, map, refs, nr_refs)) { error_setg_errno(errp, errno, "xengnttab_unmap failed"); } } -static void compat_copy_grant_refs(XenDevice *xendev, bool to_domain, - XenDeviceGrantCopySegment segs[], - unsigned int nr_segs, Error **errp) -{ - uint32_t *refs = g_new(uint32_t, nr_segs); - int prot = to_domain ? PROT_WRITE : PROT_READ; - void *map; - unsigned int i; - - for (i = 0; i < nr_segs; i++) { - XenDeviceGrantCopySegment *seg = &segs[i]; - - refs[i] = to_domain ? seg->dest.foreign.ref : - seg->source.foreign.ref; - } - - map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_segs, - xendev->frontend_id, refs, - prot); - if (!map) { - error_setg_errno(errp, errno, - "xengnttab_map_domain_grant_refs failed"); - goto done; - } - - for (i = 0; i < nr_segs; i++) { - XenDeviceGrantCopySegment *seg = &segs[i]; - void *page = map + (i * XC_PAGE_SIZE); - - if (to_domain) { - memcpy(page + seg->dest.foreign.offset, seg->source.virt, - seg->len); - } else { - memcpy(seg->dest.virt, page + seg->source.foreign.offset, - seg->len); - } - } - - if (xengnttab_unmap(xendev->xgth, map, nr_segs)) { - error_setg_errno(errp, errno, "xengnttab_unmap failed"); - } - -done: - g_free(refs); -} - void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain, XenDeviceGrantCopySegment segs[], unsigned int nr_segs, Error **errp) { - xengnttab_grant_copy_segment_t *xengnttab_segs; - unsigned int i; - - if (!xendev->feature_grant_copy) { - compat_copy_grant_refs(xendev, to_domain, segs, nr_segs, errp); - return; - } - - xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs); - - for (i = 0; i < nr_segs; i++) { - XenDeviceGrantCopySegment *seg = &segs[i]; - xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; - - if (to_domain) { - xengnttab_seg->flags = GNTCOPY_dest_gref; - xengnttab_seg->dest.foreign.domid = xendev->frontend_id; - xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref; - xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset; - xengnttab_seg->source.virt = seg->source.virt; - } else { - xengnttab_seg->flags = GNTCOPY_source_gref; - xengnttab_seg->source.foreign.domid = xendev->frontend_id; - xengnttab_seg->source.foreign.ref = seg->source.foreign.ref; - xengnttab_seg->source.foreign.offset = - seg->source.foreign.offset; - xengnttab_seg->dest.virt = seg->dest.virt; - } - - xengnttab_seg->len = seg->len; - } - - if (xengnttab_grant_copy(xendev->xgth, nr_segs, xengnttab_segs)) { - error_setg_errno(errp, errno, "xengnttab_grant_copy failed"); - goto done; - } - - for (i = 0; i < nr_segs; i++) { - xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; - - if (xengnttab_seg->status != GNTST_okay) { - error_setg(errp, "xengnttab_grant_copy seg[%u] failed", i); - break; - } - } - -done: - g_free(xengnttab_segs); + qemu_xen_gnttab_grant_copy(xendev->xgth, to_domain, xendev->frontend_id, + (XenGrantCopySegment *)segs, nr_segs, errp); } struct XenEventChannel { @@ -1094,12 +833,12 @@ static bool xen_device_poll(void *opaque) static void xen_device_event(void *opaque) { XenEventChannel *channel = opaque; - unsigned long port = xenevtchn_pending(channel->xeh); + unsigned long port = qemu_xen_evtchn_pending(channel->xeh); if (port == channel->local_port) { xen_device_poll(channel); - xenevtchn_unmask(channel->xeh, port); + qemu_xen_evtchn_unmask(channel->xeh, port); } } @@ -1114,12 +853,15 @@ void xen_device_set_event_channel_context(XenDevice *xendev, } if (channel->ctx) - aio_set_fd_handler(channel->ctx, xenevtchn_fd(channel->xeh), true, + aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), NULL, 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, NULL, channel); + if (ctx) { + aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), + xen_device_event, NULL, xen_device_poll, NULL, + channel); + } } XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev, @@ -1130,13 +872,13 @@ XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev, XenEventChannel *channel = g_new0(XenEventChannel, 1); xenevtchn_port_or_error_t local_port; - channel->xeh = xenevtchn_open(NULL, 0); + channel->xeh = qemu_xen_evtchn_open(); if (!channel->xeh) { error_setg_errno(errp, errno, "failed xenevtchn_open"); goto fail; } - local_port = xenevtchn_bind_interdomain(channel->xeh, + local_port = qemu_xen_evtchn_bind_interdomain(channel->xeh, xendev->frontend_id, port); if (local_port < 0) { @@ -1159,7 +901,7 @@ XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev, fail: if (channel->xeh) { - xenevtchn_close(channel->xeh); + qemu_xen_evtchn_close(channel->xeh); } g_free(channel); @@ -1176,11 +918,16 @@ void xen_device_notify_event_channel(XenDevice *xendev, return; } - if (xenevtchn_notify(channel->xeh, channel->local_port) < 0) { + if (qemu_xen_evtchn_notify(channel->xeh, channel->local_port) < 0) { error_setg_errno(errp, errno, "xenevtchn_notify failed"); } } +unsigned int xen_event_channel_get_local_port(XenEventChannel *channel) +{ + return channel->local_port; +} + void xen_device_unbind_event_channel(XenDevice *xendev, XenEventChannel *channel, Error **errp) @@ -1192,14 +939,16 @@ void xen_device_unbind_event_channel(XenDevice *xendev, QLIST_REMOVE(channel, list); - aio_set_fd_handler(channel->ctx, xenevtchn_fd(channel->xeh), true, - NULL, NULL, NULL, NULL, NULL); + if (channel->ctx) { + aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), + NULL, NULL, NULL, NULL, NULL); + } - if (xenevtchn_unbind(channel->xeh, channel->local_port) < 0) { + if (qemu_xen_evtchn_unbind(channel->xeh, channel->local_port) < 0) { error_setg_errno(errp, errno, "xenevtchn_unbind failed"); } - xenevtchn_close(channel->xeh); + qemu_xen_evtchn_close(channel->xeh); g_free(channel); } @@ -1234,17 +983,12 @@ static void xen_device_unrealize(DeviceState *dev) xen_device_backend_destroy(xendev); if (xendev->xgth) { - xengnttab_close(xendev->xgth); + qemu_xen_gnttab_close(xendev->xgth); xendev->xgth = NULL; } - if (xendev->watch_list) { - watch_list_destroy(xendev->watch_list); - xendev->watch_list = NULL; - } - if (xendev->xsh) { - xs_close(xendev->xsh); + qemu_xen_xs_close(xendev->xsh); xendev->xsh = NULL; } @@ -1289,23 +1033,18 @@ static void xen_device_realize(DeviceState *dev, Error **errp) trace_xen_device_realize(type, xendev->name); - xendev->xsh = xs_open(0); + xendev->xsh = qemu_xen_xs_open(); if (!xendev->xsh) { error_setg_errno(errp, errno, "failed xs_open"); goto unrealize; } - xendev->watch_list = watch_list_create(xendev->xsh); - - xendev->xgth = xengnttab_open(NULL, 0); + xendev->xgth = qemu_xen_gnttab_open(); if (!xendev->xgth) { error_setg_errno(errp, errno, "failed xengnttab_open"); goto unrealize; } - xendev->feature_grant_copy = - (xengnttab_grant_copy(xendev->xgth, 0, NULL) == 0); - xen_device_backend_create(xendev, errp); if (*errp) { goto unrealize; @@ -1316,13 +1055,6 @@ static void xen_device_realize(DeviceState *dev, Error **errp) goto unrealize; } - if (xendev_class->realize) { - xendev_class->realize(xendev, errp); - if (*errp) { - goto unrealize; - } - } - xen_device_backend_printf(xendev, "frontend", "%s", xendev->frontend_path); xen_device_backend_printf(xendev, "frontend-id", "%u", @@ -1341,6 +1073,13 @@ static void xen_device_realize(DeviceState *dev, Error **errp) xen_device_frontend_set_state(xendev, XenbusStateInitialising, true); } + if (xendev_class->realize) { + xendev_class->realize(xendev, errp); + if (*errp) { + goto unrealize; + } + } + xendev->exit.notify = xen_device_exit; qemu_add_exit_notifier(&xendev->exit); return; @@ -1402,4 +1141,7 @@ void xen_bus_init(void) sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); qbus_set_bus_hotplug_handler(bus); + + qemu_create_nic_bus_devices(bus, TYPE_XEN_DEVICE, "xen-net-device", + "xen", "xen-net-device"); } diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c new file mode 100644 index 0000000000..1627da7398 --- /dev/null +++ b/hw/xen/xen-hvm-common.c @@ -0,0 +1,882 @@ +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "exec/target_page.h" +#include "trace.h" + +#include "hw/pci/pci_host.h" +#include "hw/xen/xen-hvm-common.h" +#include "hw/xen/xen-bus.h" +#include "hw/boards.h" +#include "hw/xen/arch_hvm.h" + +MemoryRegion xen_memory; + +void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr, + Error **errp) +{ + unsigned target_page_bits = qemu_target_page_bits(); + unsigned long nr_pfn; + xen_pfn_t *pfn_list; + int i; + + if (runstate_check(RUN_STATE_INMIGRATE)) { + /* RAM already populated in Xen */ + warn_report("%s: do not alloc "RAM_ADDR_FMT + " bytes of ram at "RAM_ADDR_FMT" when runstate is INMIGRATE", + __func__, size, ram_addr); + return; + } + + if (mr == &xen_memory) { + return; + } + + trace_xen_ram_alloc(ram_addr, size); + + nr_pfn = size >> target_page_bits; + pfn_list = g_new(xen_pfn_t, nr_pfn); + + for (i = 0; i < nr_pfn; i++) { + pfn_list[i] = (ram_addr >> target_page_bits) + i; + } + + if (xc_domain_populate_physmap_exact(xen_xc, xen_domid, nr_pfn, 0, 0, pfn_list)) { + error_setg(errp, "xen: failed to populate ram at " RAM_ADDR_FMT, + ram_addr); + } + + g_free(pfn_list); +} + +static void xen_set_memory(struct MemoryListener *listener, + MemoryRegionSection *section, + bool add) +{ + XenIOState *state = container_of(listener, XenIOState, memory_listener); + + if (section->mr == &xen_memory) { + return; + } else { + if (add) { + xen_map_memory_section(xen_domid, state->ioservid, + section); + } else { + xen_unmap_memory_section(xen_domid, state->ioservid, + section); + } + } + + arch_xen_set_memory(state, section, add); +} + +void xen_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + memory_region_ref(section->mr); + xen_set_memory(listener, section, true); +} + +void xen_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + xen_set_memory(listener, section, false); + memory_region_unref(section->mr); +} + +void xen_io_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + XenIOState *state = container_of(listener, XenIOState, io_listener); + MemoryRegion *mr = section->mr; + + if (mr->ops == &unassigned_io_ops) { + return; + } + + memory_region_ref(mr); + + xen_map_io_section(xen_domid, state->ioservid, section); +} + +void xen_io_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + XenIOState *state = container_of(listener, XenIOState, io_listener); + MemoryRegion *mr = section->mr; + + if (mr->ops == &unassigned_io_ops) { + return; + } + + xen_unmap_io_section(xen_domid, state->ioservid, section); + + memory_region_unref(mr); +} + +void xen_device_realize(DeviceListener *listener, + DeviceState *dev) +{ + XenIOState *state = container_of(listener, XenIOState, device_listener); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + PCIDevice *pci_dev = PCI_DEVICE(dev); + XenPciDevice *xendev = g_new(XenPciDevice, 1); + + xendev->pci_dev = pci_dev; + xendev->sbdf = PCI_BUILD_BDF(pci_dev_bus_num(pci_dev), + pci_dev->devfn); + QLIST_INSERT_HEAD(&state->dev_list, xendev, entry); + + xen_map_pcidev(xen_domid, state->ioservid, pci_dev); + } +} + +void xen_device_unrealize(DeviceListener *listener, + DeviceState *dev) +{ + XenIOState *state = container_of(listener, XenIOState, device_listener); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + PCIDevice *pci_dev = PCI_DEVICE(dev); + XenPciDevice *xendev, *next; + + xen_unmap_pcidev(xen_domid, state->ioservid, pci_dev); + + QLIST_FOREACH_SAFE(xendev, &state->dev_list, entry, next) { + if (xendev->pci_dev == pci_dev) { + QLIST_REMOVE(xendev, entry); + g_free(xendev); + break; + } + } + } +} + +MemoryListener xen_io_listener = { + .name = "xen-io", + .region_add = xen_io_add, + .region_del = xen_io_del, + .priority = MEMORY_LISTENER_PRIORITY_ACCEL, +}; + +DeviceListener xen_device_listener = { + .realize = xen_device_realize, + .unrealize = xen_device_unrealize, +}; + +/* get the ioreq packets from share mem */ +static ioreq_t *cpu_get_ioreq_from_shared_memory(XenIOState *state, int vcpu) +{ + ioreq_t *req = xen_vcpu_ioreq(state->shared_page, vcpu); + + if (req->state != STATE_IOREQ_READY) { + trace_cpu_get_ioreq_from_shared_memory_req_not_ready(req->state, + req->data_is_ptr, + req->addr, + req->data, + req->count, + req->size); + return NULL; + } + + xen_rmb(); /* see IOREQ_READY /then/ read contents of ioreq */ + + req->state = STATE_IOREQ_INPROCESS; + return req; +} + +/* use poll to get the port notification */ +/* ioreq_vec--out,the */ +/* retval--the number of ioreq packet */ +static ioreq_t *cpu_get_ioreq(XenIOState *state) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + unsigned int max_cpus = ms->smp.max_cpus; + int i; + evtchn_port_t port; + + port = qemu_xen_evtchn_pending(state->xce_handle); + if (port == state->bufioreq_local_port) { + timer_mod(state->buffered_io_timer, + BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); + return NULL; + } + + if (port != -1) { + for (i = 0; i < max_cpus; i++) { + if (state->ioreq_local_port[i] == port) { + break; + } + } + + if (i == max_cpus) { + hw_error("Fatal error while trying to get io event!\n"); + } + + /* unmask the wanted port again */ + qemu_xen_evtchn_unmask(state->xce_handle, port); + + /* get the io packet from shared memory */ + state->send_vcpu = i; + return cpu_get_ioreq_from_shared_memory(state, i); + } + + /* read error or read nothing */ + return NULL; +} + +static uint32_t do_inp(uint32_t addr, unsigned long size) +{ + switch (size) { + case 1: + return cpu_inb(addr); + case 2: + return cpu_inw(addr); + case 4: + return cpu_inl(addr); + default: + hw_error("inp: bad size: %04x %lx", addr, size); + } +} + +static void do_outp(uint32_t addr, + unsigned long size, uint32_t val) +{ + switch (size) { + case 1: + return cpu_outb(addr, val); + case 2: + return cpu_outw(addr, val); + case 4: + return cpu_outl(addr, val); + default: + hw_error("outp: bad size: %04x %lx", addr, size); + } +} + +/* + * Helper functions which read/write an object from/to physical guest + * memory, as part of the implementation of an ioreq. + * + * Equivalent to + * cpu_physical_memory_rw(addr + (req->df ? -1 : +1) * req->size * i, + * val, req->size, 0/1) + * except without the integer overflow problems. + */ +static void rw_phys_req_item(hwaddr addr, + ioreq_t *req, uint32_t i, void *val, int rw) +{ + /* Do everything unsigned so overflow just results in a truncated result + * and accesses to undesired parts of guest memory, which is up + * to the guest */ + hwaddr offset = (hwaddr)req->size * i; + if (req->df) { + addr -= offset; + } else { + addr += offset; + } + cpu_physical_memory_rw(addr, val, req->size, rw); +} + +static inline void read_phys_req_item(hwaddr addr, + ioreq_t *req, uint32_t i, void *val) +{ + rw_phys_req_item(addr, req, i, val, 0); +} +static inline void write_phys_req_item(hwaddr addr, + ioreq_t *req, uint32_t i, void *val) +{ + rw_phys_req_item(addr, req, i, val, 1); +} + + +void cpu_ioreq_pio(ioreq_t *req) +{ + uint32_t i; + + trace_cpu_ioreq_pio(req, req->dir, req->df, req->data_is_ptr, req->addr, + req->data, req->count, req->size); + + if (req->size > sizeof(uint32_t)) { + hw_error("PIO: bad size (%u)", req->size); + } + + if (req->dir == IOREQ_READ) { + if (!req->data_is_ptr) { + req->data = do_inp(req->addr, req->size); + trace_cpu_ioreq_pio_read_reg(req, req->data, req->addr, + req->size); + } else { + uint32_t tmp; + + for (i = 0; i < req->count; i++) { + tmp = do_inp(req->addr, req->size); + write_phys_req_item(req->data, req, i, &tmp); + } + } + } else if (req->dir == IOREQ_WRITE) { + if (!req->data_is_ptr) { + trace_cpu_ioreq_pio_write_reg(req, req->data, req->addr, + req->size); + do_outp(req->addr, req->size, req->data); + } else { + for (i = 0; i < req->count; i++) { + uint32_t tmp = 0; + + read_phys_req_item(req->data, req, i, &tmp); + do_outp(req->addr, req->size, tmp); + } + } + } +} + +static void cpu_ioreq_move(ioreq_t *req) +{ + uint32_t i; + + trace_cpu_ioreq_move(req, req->dir, req->df, req->data_is_ptr, req->addr, + req->data, req->count, req->size); + + if (req->size > sizeof(req->data)) { + hw_error("MMIO: bad size (%u)", req->size); + } + + if (!req->data_is_ptr) { + if (req->dir == IOREQ_READ) { + for (i = 0; i < req->count; i++) { + read_phys_req_item(req->addr, req, i, &req->data); + } + } else if (req->dir == IOREQ_WRITE) { + for (i = 0; i < req->count; i++) { + write_phys_req_item(req->addr, req, i, &req->data); + } + } + } else { + uint64_t tmp; + + if (req->dir == IOREQ_READ) { + for (i = 0; i < req->count; i++) { + read_phys_req_item(req->addr, req, i, &tmp); + write_phys_req_item(req->data, req, i, &tmp); + } + } else if (req->dir == IOREQ_WRITE) { + for (i = 0; i < req->count; i++) { + read_phys_req_item(req->data, req, i, &tmp); + write_phys_req_item(req->addr, req, i, &tmp); + } + } + } +} + +static void cpu_ioreq_config(XenIOState *state, ioreq_t *req) +{ + uint32_t sbdf = req->addr >> 32; + uint32_t reg = req->addr; + XenPciDevice *xendev; + + if (req->size != sizeof(uint8_t) && req->size != sizeof(uint16_t) && + req->size != sizeof(uint32_t)) { + hw_error("PCI config access: bad size (%u)", req->size); + } + + if (req->count != 1) { + hw_error("PCI config access: bad count (%u)", req->count); + } + + QLIST_FOREACH(xendev, &state->dev_list, entry) { + if (xendev->sbdf != sbdf) { + continue; + } + + if (!req->data_is_ptr) { + if (req->dir == IOREQ_READ) { + req->data = pci_host_config_read_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->size); + trace_cpu_ioreq_config_read(req, xendev->sbdf, reg, + req->size, req->data); + } else if (req->dir == IOREQ_WRITE) { + trace_cpu_ioreq_config_write(req, xendev->sbdf, reg, + req->size, req->data); + pci_host_config_write_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->data, req->size); + } + } else { + uint32_t tmp; + + if (req->dir == IOREQ_READ) { + tmp = pci_host_config_read_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->size); + trace_cpu_ioreq_config_read(req, xendev->sbdf, reg, + req->size, tmp); + write_phys_req_item(req->data, req, 0, &tmp); + } else if (req->dir == IOREQ_WRITE) { + read_phys_req_item(req->data, req, 0, &tmp); + trace_cpu_ioreq_config_write(req, xendev->sbdf, reg, + req->size, tmp); + pci_host_config_write_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + tmp, req->size); + } + } + } +} + +static void handle_ioreq(XenIOState *state, ioreq_t *req) +{ + trace_handle_ioreq(req, req->type, req->dir, req->df, req->data_is_ptr, + req->addr, req->data, req->count, req->size); + + if (!req->data_is_ptr && (req->dir == IOREQ_WRITE) && + (req->size < sizeof (target_ulong))) { + req->data &= ((target_ulong) 1 << (8 * req->size)) - 1; + } + + if (req->dir == IOREQ_WRITE) + trace_handle_ioreq_write(req, req->type, req->df, req->data_is_ptr, + req->addr, req->data, req->count, req->size); + + switch (req->type) { + case IOREQ_TYPE_PIO: + cpu_ioreq_pio(req); + break; + case IOREQ_TYPE_COPY: + cpu_ioreq_move(req); + break; + case IOREQ_TYPE_TIMEOFFSET: + break; + case IOREQ_TYPE_INVALIDATE: + xen_invalidate_map_cache(); + break; + case IOREQ_TYPE_PCI_CONFIG: + cpu_ioreq_config(state, req); + break; + default: + arch_handle_ioreq(state, req); + } + if (req->dir == IOREQ_READ) { + trace_handle_ioreq_read(req, req->type, req->df, req->data_is_ptr, + req->addr, req->data, req->count, req->size); + } +} + +static bool handle_buffered_iopage(XenIOState *state) +{ + buffered_iopage_t *buf_page = state->buffered_io_page; + buf_ioreq_t *buf_req = NULL; + bool handled_ioreq = false; + ioreq_t req; + int qw; + + if (!buf_page) { + return 0; + } + + memset(&req, 0x00, sizeof(req)); + req.state = STATE_IOREQ_READY; + req.count = 1; + req.dir = IOREQ_WRITE; + + for (;;) { + uint32_t rdptr = buf_page->read_pointer, wrptr; + + xen_rmb(); + wrptr = buf_page->write_pointer; + xen_rmb(); + if (rdptr != buf_page->read_pointer) { + continue; + } + if (rdptr == wrptr) { + break; + } + buf_req = &buf_page->buf_ioreq[rdptr % IOREQ_BUFFER_SLOT_NUM]; + req.size = 1U << buf_req->size; + req.addr = buf_req->addr; + req.data = buf_req->data; + req.type = buf_req->type; + xen_rmb(); + qw = (req.size == 8); + if (qw) { + if (rdptr + 1 == wrptr) { + hw_error("Incomplete quad word buffered ioreq"); + } + buf_req = &buf_page->buf_ioreq[(rdptr + 1) % + IOREQ_BUFFER_SLOT_NUM]; + req.data |= ((uint64_t)buf_req->data) << 32; + xen_rmb(); + } + + handle_ioreq(state, &req); + + /* Only req.data may get updated by handle_ioreq(), albeit even that + * should not happen as such data would never make it to the guest (we + * can only usefully see writes here after all). + */ + assert(req.state == STATE_IOREQ_READY); + assert(req.count == 1); + assert(req.dir == IOREQ_WRITE); + assert(!req.data_is_ptr); + + qatomic_add(&buf_page->read_pointer, qw + 1); + handled_ioreq = true; + } + + return handled_ioreq; +} + +static void handle_buffered_io(void *opaque) +{ + XenIOState *state = opaque; + + if (handle_buffered_iopage(state)) { + timer_mod(state->buffered_io_timer, + BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); + } else { + timer_del(state->buffered_io_timer); + qemu_xen_evtchn_unmask(state->xce_handle, state->bufioreq_local_port); + } +} + +static void cpu_handle_ioreq(void *opaque) +{ + XenIOState *state = opaque; + ioreq_t *req = cpu_get_ioreq(state); + + handle_buffered_iopage(state); + if (req) { + ioreq_t copy = *req; + + xen_rmb(); + handle_ioreq(state, ©); + req->data = copy.data; + + if (req->state != STATE_IOREQ_INPROCESS) { + warn_report("Badness in I/O request ... not in service?!: " + "%x, ptr: %x, port: %"PRIx64", " + "data: %"PRIx64", count: %u, size: %u, type: %u", + req->state, req->data_is_ptr, req->addr, + req->data, req->count, req->size, req->type); + destroy_hvm_domain(false); + return; + } + + xen_wmb(); /* Update ioreq contents /then/ update state. */ + + /* + * We do this before we send the response so that the tools + * have the opportunity to pick up on the reset before the + * guest resumes and does a hlt with interrupts disabled which + * causes Xen to powerdown the domain. + */ + if (runstate_is_running()) { + ShutdownCause request; + + if (qemu_shutdown_requested_get()) { + destroy_hvm_domain(false); + } + request = qemu_reset_requested_get(); + if (request) { + qemu_system_reset(request); + destroy_hvm_domain(true); + } + } + + req->state = STATE_IORESP_READY; + qemu_xen_evtchn_notify(state->xce_handle, + state->ioreq_local_port[state->send_vcpu]); + } +} + +static void xen_main_loop_prepare(XenIOState *state) +{ + int evtchn_fd = -1; + + if (state->xce_handle != NULL) { + evtchn_fd = qemu_xen_evtchn_fd(state->xce_handle); + } + + state->buffered_io_timer = timer_new_ms(QEMU_CLOCK_REALTIME, handle_buffered_io, + state); + + if (evtchn_fd != -1) { + CPUState *cpu_state; + + CPU_FOREACH(cpu_state) { + trace_xen_main_loop_prepare_init_cpu(cpu_state->cpu_index, + cpu_state); + state->cpu_by_vcpu_id[cpu_state->cpu_index] = cpu_state; + } + qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, state); + } +} + + +void xen_hvm_change_state_handler(void *opaque, bool running, + RunState rstate) +{ + XenIOState *state = opaque; + + if (running) { + xen_main_loop_prepare(state); + } + + xen_set_ioreq_server_state(xen_domid, + state->ioservid, + running); +} + +void xen_exit_notifier(Notifier *n, void *data) +{ + XenIOState *state = container_of(n, XenIOState, exit); + + xen_destroy_ioreq_server(xen_domid, state->ioservid); + if (state->fres != NULL) { + xenforeignmemory_unmap_resource(xen_fmem, state->fres); + } + + qemu_xen_evtchn_close(state->xce_handle); + xs_daemon_close(state->xenstore); +} + +static int xen_map_ioreq_server(XenIOState *state) +{ + void *addr = NULL; + xen_pfn_t ioreq_pfn; + xen_pfn_t bufioreq_pfn; + evtchn_port_t bufioreq_evtchn; + int rc; + + /* + * Attempt to map using the resource API and fall back to normal + * foreign mapping if this is not supported. + */ + QEMU_BUILD_BUG_ON(XENMEM_resource_ioreq_server_frame_bufioreq != 0); + QEMU_BUILD_BUG_ON(XENMEM_resource_ioreq_server_frame_ioreq(0) != 1); + state->fres = xenforeignmemory_map_resource(xen_fmem, xen_domid, + XENMEM_resource_ioreq_server, + state->ioservid, 0, 2, + &addr, + PROT_READ | PROT_WRITE, 0); + if (state->fres != NULL) { + trace_xen_map_resource_ioreq(state->ioservid, addr); + state->buffered_io_page = addr; + state->shared_page = addr + XC_PAGE_SIZE; + } else if (errno != EOPNOTSUPP) { + error_report("failed to map ioreq server resources: error %d handle=%p", + errno, xen_xc); + return -1; + } + + rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, + (state->shared_page == NULL) ? + &ioreq_pfn : NULL, + (state->buffered_io_page == NULL) ? + &bufioreq_pfn : NULL, + &bufioreq_evtchn); + if (rc < 0) { + error_report("failed to get ioreq server info: error %d handle=%p", + errno, xen_xc); + return rc; + } + + if (state->shared_page == NULL) { + trace_xen_map_ioreq_server_shared_page(ioreq_pfn); + + state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &ioreq_pfn, NULL); + if (state->shared_page == NULL) { + error_report("map shared IO page returned error %d handle=%p", + errno, xen_xc); + } + } + + if (state->buffered_io_page == NULL) { + trace_xen_map_ioreq_server_buffered_io_page(bufioreq_pfn); + + state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &bufioreq_pfn, + NULL); + if (state->buffered_io_page == NULL) { + error_report("map buffered IO page returned error %d", errno); + return -1; + } + } + + if (state->shared_page == NULL || state->buffered_io_page == NULL) { + return -1; + } + + trace_xen_map_ioreq_server_buffered_io_evtchn(bufioreq_evtchn); + + state->bufioreq_remote_port = bufioreq_evtchn; + + return 0; +} + +void destroy_hvm_domain(bool reboot) +{ + xc_interface *xc_handle; + int sts; + int rc; + + unsigned int reason = reboot ? SHUTDOWN_reboot : SHUTDOWN_poweroff; + + if (xen_dmod) { + rc = xendevicemodel_shutdown(xen_dmod, xen_domid, reason); + if (!rc) { + return; + } + if (errno != ENOTTY /* old Xen */) { + error_report("xendevicemodel_shutdown failed with error %d", errno); + } + /* well, try the old thing then */ + } + + xc_handle = xc_interface_open(0, 0, 0); + if (xc_handle == NULL) { + trace_destroy_hvm_domain_cannot_acquire_handle(); + } else { + sts = xc_domain_shutdown(xc_handle, xen_domid, reason); + if (sts != 0) { + trace_destroy_hvm_domain_failed_action( + reboot ? "reboot" : "poweroff", sts, strerror(errno) + ); + } else { + trace_destroy_hvm_domain_action( + xen_domid, reboot ? "reboot" : "poweroff" + ); + } + xc_interface_close(xc_handle); + } +} + +void xen_shutdown_fatal_error(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + error_vreport(fmt, ap); + va_end(ap); + error_report("Will destroy the domain."); + /* destroy the domain */ + qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_ERROR); +} + +static void xen_do_ioreq_register(XenIOState *state, + unsigned int max_cpus, + const MemoryListener *xen_memory_listener) +{ + int i, rc; + + state->exit.notify = xen_exit_notifier; + qemu_add_exit_notifier(&state->exit); + + /* + * Register wake-up support in QMP query-current-machine API + */ + qemu_register_wakeup_support(); + + rc = xen_map_ioreq_server(state); + if (rc < 0) { + goto err; + } + + /* Note: cpus is empty at this point in init */ + state->cpu_by_vcpu_id = g_new0(CPUState *, max_cpus); + + rc = xen_set_ioreq_server_state(xen_domid, state->ioservid, true); + if (rc < 0) { + error_report("failed to enable ioreq server info: error %d handle=%p", + errno, xen_xc); + goto err; + } + + state->ioreq_local_port = g_new0(evtchn_port_t, max_cpus); + + /* FIXME: how about if we overflow the page here? */ + for (i = 0; i < max_cpus; i++) { + rc = qemu_xen_evtchn_bind_interdomain(state->xce_handle, xen_domid, + xen_vcpu_eport(state->shared_page, + i)); + if (rc == -1) { + error_report("shared evtchn %d bind error %d", i, errno); + goto err; + } + state->ioreq_local_port[i] = rc; + } + + rc = qemu_xen_evtchn_bind_interdomain(state->xce_handle, xen_domid, + state->bufioreq_remote_port); + if (rc == -1) { + error_report("buffered evtchn bind error %d", errno); + goto err; + } + state->bufioreq_local_port = rc; + + /* Init RAM management */ +#ifdef XEN_COMPAT_PHYSMAP + xen_map_cache_init(xen_phys_offset_to_gaddr, state); +#else + xen_map_cache_init(NULL, state); +#endif + + qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); + + state->memory_listener = *xen_memory_listener; + memory_listener_register(&state->memory_listener, &address_space_memory); + + state->io_listener = xen_io_listener; + memory_listener_register(&state->io_listener, &address_space_io); + + state->device_listener = xen_device_listener; + QLIST_INIT(&state->dev_list); + device_listener_register(&state->device_listener); + + return; + +err: + error_report("xen hardware virtual machine initialisation failed"); + exit(1); +} + +void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, + const MemoryListener *xen_memory_listener) +{ + int rc; + + setup_xen_backend_ops(); + + state->xce_handle = qemu_xen_evtchn_open(); + if (state->xce_handle == NULL) { + error_report("xen: event channel open failed with error %d", errno); + goto err; + } + + state->xenstore = xs_daemon_open(); + if (state->xenstore == NULL) { + error_report("xen: xenstore open failed with error %d", errno); + goto err; + } + + rc = xen_create_ioreq_server(xen_domid, &state->ioservid); + if (!rc) { + xen_do_ioreq_register(state, max_cpus, xen_memory_listener); + } else { + warn_report("xen: failed to create ioreq server"); + } + + xen_bus_init(); + + xen_be_init(); + + return; + +err: + error_report("xen hardware virtual machine backend registration failed"); + exit(1); +} diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index 085fd31ef7..124dd5f3d6 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -39,11 +39,10 @@ BusState *xen_sysbus; /* ------------------------------------------------------------- */ /* public */ -struct xs_handle *xenstore; +struct qemu_xs_handle *xenstore; const char *xen_protocol; /* private */ -static bool xen_feature_grant_copy; static int debug; int xenstore_write_be_str(struct XenLegacyDevice *xendev, const char *node, @@ -113,7 +112,7 @@ void xen_be_set_max_grant_refs(struct XenLegacyDevice *xendev, { assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); - if (xengnttab_set_max_grants(xendev->gnttabdev, nr_refs)) { + if (qemu_xen_gnttab_set_max_grants(xendev->gnttabdev, nr_refs)) { xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", strerror(errno)); } @@ -126,8 +125,8 @@ void *xen_be_map_grant_refs(struct XenLegacyDevice *xendev, uint32_t *refs, assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); - ptr = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_refs, - xen_domid, refs, prot); + ptr = qemu_xen_gnttab_map_refs(xendev->gnttabdev, nr_refs, xen_domid, refs, + prot); if (!ptr) { xen_pv_printf(xendev, 0, "xengnttab_map_domain_grant_refs failed: %s\n", @@ -138,123 +137,31 @@ void *xen_be_map_grant_refs(struct XenLegacyDevice *xendev, uint32_t *refs, } void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr, - unsigned int nr_refs) + uint32_t *refs, unsigned int nr_refs) { assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); - if (xengnttab_unmap(xendev->gnttabdev, ptr, nr_refs)) { + if (qemu_xen_gnttab_unmap(xendev->gnttabdev, ptr, refs, nr_refs)) { xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n", strerror(errno)); } } -static int compat_copy_grant_refs(struct XenLegacyDevice *xendev, - bool to_domain, - XenGrantCopySegment segs[], - unsigned int nr_segs) -{ - uint32_t *refs = g_new(uint32_t, nr_segs); - int prot = to_domain ? PROT_WRITE : PROT_READ; - void *pages; - unsigned int i; - - for (i = 0; i < nr_segs; i++) { - XenGrantCopySegment *seg = &segs[i]; - - refs[i] = to_domain ? - seg->dest.foreign.ref : seg->source.foreign.ref; - } - - pages = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_segs, - xen_domid, refs, prot); - if (!pages) { - xen_pv_printf(xendev, 0, - "xengnttab_map_domain_grant_refs failed: %s\n", - strerror(errno)); - g_free(refs); - return -1; - } - - for (i = 0; i < nr_segs; i++) { - XenGrantCopySegment *seg = &segs[i]; - void *page = pages + (i * XC_PAGE_SIZE); - - if (to_domain) { - memcpy(page + seg->dest.foreign.offset, seg->source.virt, - seg->len); - } else { - memcpy(seg->dest.virt, page + seg->source.foreign.offset, - seg->len); - } - } - - if (xengnttab_unmap(xendev->gnttabdev, pages, nr_segs)) { - xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n", - strerror(errno)); - } - - g_free(refs); - return 0; -} - int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev, bool to_domain, XenGrantCopySegment segs[], unsigned int nr_segs) { - xengnttab_grant_copy_segment_t *xengnttab_segs; - unsigned int i; int rc; assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); - if (!xen_feature_grant_copy) { - return compat_copy_grant_refs(xendev, to_domain, segs, nr_segs); - } - - xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs); - - for (i = 0; i < nr_segs; i++) { - XenGrantCopySegment *seg = &segs[i]; - xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; - - if (to_domain) { - xengnttab_seg->flags = GNTCOPY_dest_gref; - xengnttab_seg->dest.foreign.domid = xen_domid; - xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref; - xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset; - xengnttab_seg->source.virt = seg->source.virt; - } else { - xengnttab_seg->flags = GNTCOPY_source_gref; - xengnttab_seg->source.foreign.domid = xen_domid; - xengnttab_seg->source.foreign.ref = seg->source.foreign.ref; - xengnttab_seg->source.foreign.offset = - seg->source.foreign.offset; - xengnttab_seg->dest.virt = seg->dest.virt; - } - - xengnttab_seg->len = seg->len; - } - - rc = xengnttab_grant_copy(xendev->gnttabdev, nr_segs, xengnttab_segs); - + rc = qemu_xen_gnttab_grant_copy(xendev->gnttabdev, to_domain, xen_domid, + segs, nr_segs, NULL); if (rc) { - xen_pv_printf(xendev, 0, "xengnttab_copy failed: %s\n", - strerror(errno)); + xen_pv_printf(xendev, 0, "xengnttab_grant_copy failed: %s\n", + strerror(-rc)); } - - for (i = 0; i < nr_segs; i++) { - xengnttab_grant_copy_segment_t *xengnttab_seg = - &xengnttab_segs[i]; - - if (xengnttab_seg->status != GNTST_okay) { - xen_pv_printf(xendev, 0, "segment[%u] status: %d\n", i, - xengnttab_seg->status); - rc = -1; - } - } - - g_free(xengnttab_segs); return rc; } @@ -294,13 +201,13 @@ static struct XenLegacyDevice *xen_be_get_xendev(const char *type, int dom, xendev->debug = debug; xendev->local_port = -1; - xendev->evtchndev = xenevtchn_open(NULL, 0); + xendev->evtchndev = qemu_xen_evtchn_open(); if (xendev->evtchndev == NULL) { xen_pv_printf(NULL, 0, "can't open evtchn device\n"); qdev_unplug(DEVICE(xendev), NULL); return NULL; } - qemu_set_cloexec(xenevtchn_fd(xendev->evtchndev)); + qemu_set_cloexec(qemu_xen_evtchn_fd(xendev->evtchndev)); xen_pv_insert_xendev(xendev); @@ -367,6 +274,25 @@ static void xen_be_frontend_changed(struct XenLegacyDevice *xendev, } } +static void xenstore_update_fe(void *opaque, const char *watch) +{ + struct XenLegacyDevice *xendev = opaque; + const char *node; + unsigned int len; + + len = strlen(xendev->fe); + if (strncmp(xendev->fe, watch, len) != 0) { + return; + } + if (watch[len] != '/') { + return; + } + node = watch + len + 1; + + xen_be_frontend_changed(xendev, node); + xen_be_check_state(xendev); +} + /* ------------------------------------------------------------- */ /* Check for possible state transitions and perform them. */ @@ -380,7 +306,6 @@ static void xen_be_frontend_changed(struct XenLegacyDevice *xendev, */ static int xen_be_try_setup(struct XenLegacyDevice *xendev) { - char token[XEN_BUFSIZE]; int be_state; if (xenstore_read_be_int(xendev, "state", &be_state) == -1) { @@ -401,8 +326,9 @@ static int xen_be_try_setup(struct XenLegacyDevice *xendev) } /* setup frontend watch */ - snprintf(token, sizeof(token), "fe:%p", xendev); - if (!xs_watch(xenstore, xendev->fe, token)) { + xendev->watch = qemu_xen_xs_watch(xenstore, xendev->fe, xenstore_update_fe, + xendev); + if (!xendev->watch) { xen_pv_printf(xendev, 0, "watching frontend path (%s) failed\n", xendev->fe); return -1; @@ -466,7 +392,7 @@ static int xen_be_try_initialise(struct XenLegacyDevice *xendev) } if (xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { - xendev->gnttabdev = xengnttab_open(NULL, 0); + xendev->gnttabdev = qemu_xen_gnttab_open(); if (xendev->gnttabdev == NULL) { xen_pv_printf(NULL, 0, "can't open gnttab device\n"); return -1; @@ -524,7 +450,7 @@ static void xen_be_disconnect(struct XenLegacyDevice *xendev, xendev->ops->disconnect(xendev); } if (xendev->gnttabdev) { - xengnttab_close(xendev->gnttabdev); + qemu_xen_gnttab_close(xendev->gnttabdev); xendev->gnttabdev = NULL; } if (xendev->be_state != state) { @@ -591,24 +517,67 @@ void xen_be_check_state(struct XenLegacyDevice *xendev) /* ------------------------------------------------------------- */ +struct xenstore_be { + const char *type; + int dom; + struct XenDevOps *ops; +}; + +static void xenstore_update_be(void *opaque, const char *watch) +{ + struct xenstore_be *be = opaque; + struct XenLegacyDevice *xendev; + char path[XEN_BUFSIZE], *bepath; + unsigned int len, dev; + + len = snprintf(path, sizeof(path), "backend/%s/%d", be->type, be->dom); + if (strncmp(path, watch, len) != 0) { + return; + } + if (sscanf(watch + len, "/%u/%255s", &dev, path) != 2) { + strcpy(path, ""); + if (sscanf(watch + len, "/%u", &dev) != 1) { + dev = -1; + } + } + if (dev == -1) { + return; + } + + xendev = xen_be_get_xendev(be->type, be->dom, dev, be->ops); + if (xendev != NULL) { + bepath = qemu_xen_xs_read(xenstore, 0, xendev->be, &len); + if (bepath == NULL) { + xen_pv_del_xendev(xendev); + } else { + free(bepath); + xen_be_backend_changed(xendev, path); + xen_be_check_state(xendev); + } + } +} + static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) { struct XenLegacyDevice *xendev; - char path[XEN_BUFSIZE], token[XEN_BUFSIZE]; + char path[XEN_BUFSIZE]; + struct xenstore_be *be = g_new0(struct xenstore_be, 1); char **dev = NULL; unsigned int cdev, j; /* setup watch */ - snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops); + be->type = type; + be->dom = dom; + be->ops = ops; snprintf(path, sizeof(path), "backend/%s/%d", type, dom); - if (!xs_watch(xenstore, path, token)) { + if (!qemu_xen_xs_watch(xenstore, path, xenstore_update_be, be)) { xen_pv_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", path); return -1; } /* look for backends */ - dev = xs_directory(xenstore, 0, path, &cdev); + dev = qemu_xen_xs_directory(xenstore, 0, path, &cdev); if (!dev) { return 0; } @@ -623,99 +592,8 @@ static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) return 0; } -void xenstore_update_be(char *watch, char *type, int dom, - struct XenDevOps *ops) -{ - struct XenLegacyDevice *xendev; - char path[XEN_BUFSIZE], *bepath; - unsigned int len, dev; - - len = snprintf(path, sizeof(path), "backend/%s/%d", type, dom); - if (strncmp(path, watch, len) != 0) { - return; - } - if (sscanf(watch + len, "/%u/%255s", &dev, path) != 2) { - strcpy(path, ""); - if (sscanf(watch + len, "/%u", &dev) != 1) { - dev = -1; - } - } - if (dev == -1) { - return; - } - - xendev = xen_be_get_xendev(type, dom, dev, ops); - if (xendev != NULL) { - bepath = xs_read(xenstore, 0, xendev->be, &len); - if (bepath == NULL) { - xen_pv_del_xendev(xendev); - } else { - free(bepath); - xen_be_backend_changed(xendev, path); - xen_be_check_state(xendev); - } - } -} - -void xenstore_update_fe(char *watch, struct XenLegacyDevice *xendev) -{ - char *node; - unsigned int len; - - len = strlen(xendev->fe); - if (strncmp(xendev->fe, watch, len) != 0) { - return; - } - if (watch[len] != '/') { - return; - } - node = watch + len + 1; - - xen_be_frontend_changed(xendev, node); - xen_be_check_state(xendev); -} /* -------------------------------------------------------------------- */ -int xen_be_init(void) -{ - xengnttab_handle *gnttabdev; - - xenstore = xs_daemon_open(); - if (!xenstore) { - xen_pv_printf(NULL, 0, "can't connect to xenstored\n"); - return -1; - } - - qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL); - - if (xen_xc == NULL || xen_fmem == NULL) { - /* Check if xen_init() have been called */ - goto err; - } - - gnttabdev = xengnttab_open(NULL, 0); - if (gnttabdev != NULL) { - if (xengnttab_grant_copy(gnttabdev, 0, NULL) == 0) { - xen_feature_grant_copy = true; - } - xengnttab_close(gnttabdev); - } - - xen_sysdev = qdev_new(TYPE_XENSYSDEV); - sysbus_realize_and_unref(SYS_BUS_DEVICE(xen_sysdev), &error_fatal); - xen_sysbus = qbus_new(TYPE_XENSYSBUS, xen_sysdev, "xen-sysbus"); - qbus_set_bus_hotplug_handler(xen_sysbus); - - return 0; - -err: - qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL); - xs_daemon_close(xenstore); - xenstore = NULL; - - return -1; -} - static void xen_set_dynamic_sysbus(void) { Object *machine = qdev_get_machine(); @@ -725,6 +603,35 @@ static void xen_set_dynamic_sysbus(void) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_XENSYSDEV); } +void xen_be_init(void) +{ + xenstore = qemu_xen_xs_open(); + if (!xenstore) { + xen_pv_printf(NULL, 0, "can't connect to xenstored\n"); + exit(1); + } + + if (xen_evtchn_ops == NULL || xen_gnttab_ops == NULL) { + xen_pv_printf(NULL, 0, "Xen operations not set up\n"); + exit(1); + } + + xen_sysdev = qdev_new(TYPE_XENSYSDEV); + sysbus_realize_and_unref(SYS_BUS_DEVICE(xen_sysdev), &error_fatal); + xen_sysbus = qbus_new(TYPE_XENSYSBUS, xen_sysdev, "xen-sysbus"); + qbus_set_bus_hotplug_handler(xen_sysbus); + + xen_set_dynamic_sysbus(); + + xen_be_register("vkbd", &xen_kbdmouse_ops); +#ifdef CONFIG_VIRTFS + xen_be_register("9pfs", &xen_9pfs_ops); +#endif +#ifdef CONFIG_USB_LIBUSB + xen_be_register("qusb", &xen_usb_ops); +#endif +} + int xen_be_register(const char *type, struct XenDevOps *ops) { char path[50]; @@ -744,33 +651,19 @@ int xen_be_register(const char *type, struct XenDevOps *ops) return xenstore_scan(type, xen_domid, ops); } -void xen_be_register_common(void) -{ - xen_set_dynamic_sysbus(); - - xen_be_register("console", &xen_console_ops); - xen_be_register("vkbd", &xen_kbdmouse_ops); -#ifdef CONFIG_VIRTFS - xen_be_register("9pfs", &xen_9pfs_ops); -#endif -#ifdef CONFIG_USB_LIBUSB - xen_be_register("qusb", &xen_usb_ops); -#endif -} - int xen_be_bind_evtchn(struct XenLegacyDevice *xendev) { if (xendev->local_port != -1) { return 0; } - xendev->local_port = xenevtchn_bind_interdomain + xendev->local_port = qemu_xen_evtchn_bind_interdomain (xendev->evtchndev, xendev->dom, xendev->remote_port); if (xendev->local_port == -1) { xen_pv_printf(xendev, 0, "xenevtchn_bind_interdomain failed\n"); return -1; } xen_pv_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); - qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev), + qemu_set_fd_handler(qemu_xen_evtchn_fd(xendev->evtchndev), xen_pv_evtchn_event, NULL, xendev); return 0; } diff --git a/hw/i386/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c similarity index 88% rename from hw/i386/xen/xen-mapcache.c rename to hw/xen/xen-mapcache.c index a2f93096e7..7f59080ba7 100644 --- a/hw/i386/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -14,7 +14,7 @@ #include -#include "hw/xen/xen-legacy-backend.h" +#include "hw/xen/xen_native.h" #include "qemu/bitmap.h" #include "sysemu/runstate.h" @@ -22,16 +22,6 @@ #include "trace.h" -//#define MAPCACHE_DEBUG - -#ifdef MAPCACHE_DEBUG -# define DPRINTF(fmt, ...) do { \ - fprintf(stderr, "xen_mapcache: " fmt, ## __VA_ARGS__); \ -} while (0) -#else -# define DPRINTF(fmt, ...) do { } while (0) -#endif - #if HOST_LONG_BITS == 32 # define MCACHE_BUCKET_SHIFT 16 # define MCACHE_MAX_SIZE (1UL<<31) /* 2GB Cap */ @@ -145,8 +135,7 @@ void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) size = mapcache->nr_buckets * sizeof (MapCacheEntry); size = (size + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1); - DPRINTF("%s, nr_buckets = %lx size %lu\n", __func__, - mapcache->nr_buckets, size); + trace_xen_map_cache_init(mapcache->nr_buckets, size); mapcache->entry = g_malloc0(size); } @@ -286,7 +275,9 @@ tryagain: test_bits(address_offset >> XC_PAGE_SHIFT, test_bit_size >> XC_PAGE_SHIFT, mapcache->last_entry->valid_mapping)) { - trace_xen_map_cache_return(mapcache->last_entry->vaddr_base + address_offset); + trace_xen_map_cache_return( + mapcache->last_entry->vaddr_base + address_offset + ); return mapcache->last_entry->vaddr_base + address_offset; } @@ -356,9 +347,8 @@ tryagain: MapCacheRev *reventry = g_new0(MapCacheRev, 1); entry->lock++; if (entry->lock == 0) { - fprintf(stderr, - "mapcache entry lock overflow: "TARGET_FMT_plx" -> %p\n", - entry->paddr_index, entry->vaddr_base); + error_report("mapcache entry lock overflow: "HWADDR_FMT_plx" -> %p", + entry->paddr_index, entry->vaddr_base); abort(); } reventry->dma = dma; @@ -368,7 +358,9 @@ tryagain: QTAILQ_INSERT_HEAD(&mapcache->locked_entries, reventry, next); } - trace_xen_map_cache_return(mapcache->last_entry->vaddr_base + address_offset); + trace_xen_map_cache_return( + mapcache->last_entry->vaddr_base + address_offset + ); return mapcache->last_entry->vaddr_base + address_offset; } @@ -402,10 +394,10 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) } } if (!found) { - fprintf(stderr, "%s, could not find %p\n", __func__, ptr); + trace_xen_ram_addr_from_mapcache_not_found(ptr); QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { - DPRINTF(" "TARGET_FMT_plx" -> %p is present\n", reventry->paddr_index, - reventry->vaddr_req); + trace_xen_ram_addr_from_mapcache_found(reventry->paddr_index, + reventry->vaddr_req); } abort(); return 0; @@ -416,7 +408,7 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) entry = entry->next; } if (!entry) { - DPRINTF("Trying to find address %p that is not in the mapcache!\n", ptr); + trace_xen_ram_addr_from_mapcache_not_in_cache(ptr); raddr = 0; } else { raddr = (reventry->paddr_index << MCACHE_BUCKET_SHIFT) + @@ -443,9 +435,12 @@ static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer) } } if (!found) { - DPRINTF("%s, could not find %p\n", __func__, buffer); + trace_xen_invalidate_map_cache_entry_unlocked_not_found(buffer); QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { - DPRINTF(" "TARGET_FMT_plx" -> %p is present\n", reventry->paddr_index, reventry->vaddr_req); + trace_xen_invalidate_map_cache_entry_unlocked_found( + reventry->paddr_index, + reventry->vaddr_req + ); } return; } @@ -463,7 +458,7 @@ static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer) entry = entry->next; } if (!entry) { - DPRINTF("Trying to unmap address %p that is not in the mapcache!\n", buffer); + trace_xen_invalidate_map_cache_entry_unlocked_miss(buffer); return; } entry->lock--; @@ -481,11 +476,37 @@ static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer) g_free(entry); } -void xen_invalidate_map_cache_entry(uint8_t *buffer) +typedef struct XenMapCacheData { + Coroutine *co; + uint8_t *buffer; +} XenMapCacheData; + +static void xen_invalidate_map_cache_entry_bh(void *opaque) { + XenMapCacheData *data = opaque; + mapcache_lock(); - xen_invalidate_map_cache_entry_unlocked(buffer); + xen_invalidate_map_cache_entry_unlocked(data->buffer); mapcache_unlock(); + + aio_co_wake(data->co); +} + +void coroutine_mixed_fn xen_invalidate_map_cache_entry(uint8_t *buffer) +{ + if (qemu_in_coroutine()) { + XenMapCacheData data = { + .co = qemu_coroutine_self(), + .buffer = buffer, + }; + aio_bh_schedule_oneshot(qemu_get_current_aio_context(), + xen_invalidate_map_cache_entry_bh, &data); + qemu_coroutine_yield(); + } else { + mapcache_lock(); + xen_invalidate_map_cache_entry_unlocked(buffer); + mapcache_unlock(); + } } void xen_invalidate_map_cache(void) @@ -502,9 +523,8 @@ void xen_invalidate_map_cache(void) if (!reventry->dma) { continue; } - fprintf(stderr, "Locked DMA mapping while invalidating mapcache!" - " "TARGET_FMT_plx" -> %p is present\n", - reventry->paddr_index, reventry->vaddr_req); + trace_xen_invalidate_map_cache(reventry->paddr_index, + reventry->vaddr_req); } for (i = 0; i < mapcache->nr_buckets; i++) { @@ -562,24 +582,23 @@ static uint8_t *xen_replace_cache_entry_unlocked(hwaddr old_phys_addr, entry = entry->next; } if (!entry) { - DPRINTF("Trying to update an entry for "TARGET_FMT_plx \ - "that is not in the mapcache!\n", old_phys_addr); + trace_xen_replace_cache_entry_unlocked(old_phys_addr); return NULL; } address_index = new_phys_addr >> MCACHE_BUCKET_SHIFT; address_offset = new_phys_addr & (MCACHE_BUCKET_SIZE - 1); - fprintf(stderr, "Replacing a dummy mapcache entry for "TARGET_FMT_plx \ - " with "TARGET_FMT_plx"\n", old_phys_addr, new_phys_addr); + trace_xen_replace_cache_entry_dummy(old_phys_addr, new_phys_addr); xen_remap_bucket(entry, entry->vaddr_base, cache_size, address_index, false); if (!test_bits(address_offset >> XC_PAGE_SHIFT, test_bit_size >> XC_PAGE_SHIFT, entry->valid_mapping)) { - DPRINTF("Unable to update a mapcache entry for "TARGET_FMT_plx"!\n", - old_phys_addr); + trace_xen_replace_cache_entry_unlocked_could_not_update_entry( + old_phys_addr + ); return NULL; } diff --git a/hw/xen/xen-operations.c b/hw/xen/xen-operations.c new file mode 100644 index 0000000000..e00983ec44 --- /dev/null +++ b/hw/xen/xen-operations.c @@ -0,0 +1,423 @@ +/* + * QEMU Xen backend support: Operations for true Xen + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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/uuid.h" +#include "qapi/error.h" + +#include "hw/xen/xen_native.h" +#include "hw/xen/xen_backend_ops.h" + +/* + * If we have new enough libxenctrl then we do not want/need these compat + * interfaces, despite what the user supplied cflags might say. They + * must be undefined before including xenctrl.h + */ +#undef XC_WANT_COMPAT_EVTCHN_API +#undef XC_WANT_COMPAT_GNTTAB_API +#undef XC_WANT_COMPAT_MAP_FOREIGN_API + +#include + +/* + * We don't support Xen prior to 4.7.1. + */ + +#include +#include +#include + +/* Xen before 4.8 */ + +static int libxengnttab_fallback_grant_copy(xengnttab_handle *xgt, + bool to_domain, uint32_t domid, + XenGrantCopySegment segs[], + unsigned int nr_segs, Error **errp) +{ + uint32_t *refs = g_new(uint32_t, nr_segs); + int prot = to_domain ? PROT_WRITE : PROT_READ; + void *map; + unsigned int i; + int rc = 0; + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + + refs[i] = to_domain ? seg->dest.foreign.ref : + seg->source.foreign.ref; + } + map = xengnttab_map_domain_grant_refs(xgt, nr_segs, domid, refs, prot); + if (!map) { + if (errp) { + error_setg_errno(errp, errno, + "xengnttab_map_domain_grant_refs failed"); + } + rc = -errno; + goto done; + } + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + void *page = map + (i * XEN_PAGE_SIZE); + + if (to_domain) { + memcpy(page + seg->dest.foreign.offset, seg->source.virt, + seg->len); + } else { + memcpy(seg->dest.virt, page + seg->source.foreign.offset, + seg->len); + } + } + + if (xengnttab_unmap(xgt, map, nr_segs)) { + if (errp) { + error_setg_errno(errp, errno, "xengnttab_unmap failed"); + } + rc = -errno; + } + +done: + g_free(refs); + return rc; +} + +#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40800 + +static int libxengnttab_backend_grant_copy(xengnttab_handle *xgt, + bool to_domain, uint32_t domid, + XenGrantCopySegment *segs, + uint32_t nr_segs, Error **errp) +{ + xengnttab_grant_copy_segment_t *xengnttab_segs; + unsigned int i; + int rc; + + xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs); + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; + + if (to_domain) { + xengnttab_seg->flags = GNTCOPY_dest_gref; + xengnttab_seg->dest.foreign.domid = domid; + xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref; + xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset; + xengnttab_seg->source.virt = seg->source.virt; + } else { + xengnttab_seg->flags = GNTCOPY_source_gref; + xengnttab_seg->source.foreign.domid = domid; + xengnttab_seg->source.foreign.ref = seg->source.foreign.ref; + xengnttab_seg->source.foreign.offset = + seg->source.foreign.offset; + xengnttab_seg->dest.virt = seg->dest.virt; + } + + xengnttab_seg->len = seg->len; + } + + if (xengnttab_grant_copy(xgt, nr_segs, xengnttab_segs)) { + if (errp) { + error_setg_errno(errp, errno, "xengnttab_grant_copy failed"); + } + rc = -errno; + goto done; + } + + rc = 0; + for (i = 0; i < nr_segs; i++) { + xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; + + if (xengnttab_seg->status != GNTST_okay) { + if (errp) { + error_setg(errp, "xengnttab_grant_copy seg[%u] failed", i); + } + rc = -EIO; + break; + } + } + +done: + g_free(xengnttab_segs); + return rc; +} +#endif + +static xenevtchn_handle *libxenevtchn_backend_open(void) +{ + return xenevtchn_open(NULL, 0); +} + +struct evtchn_backend_ops libxenevtchn_backend_ops = { + .open = libxenevtchn_backend_open, + .close = xenevtchn_close, + .bind_interdomain = xenevtchn_bind_interdomain, + .unbind = xenevtchn_unbind, + .get_fd = xenevtchn_fd, + .notify = xenevtchn_notify, + .unmask = xenevtchn_unmask, + .pending = xenevtchn_pending, +}; + +static xengnttab_handle *libxengnttab_backend_open(void) +{ + return xengnttab_open(NULL, 0); +} + +static int libxengnttab_backend_unmap(xengnttab_handle *xgt, + void *start_address, uint32_t *refs, + uint32_t count) +{ + return xengnttab_unmap(xgt, start_address, count); +} + + +static struct gnttab_backend_ops libxengnttab_backend_ops = { + .features = XEN_GNTTAB_OP_FEATURE_MAP_MULTIPLE, + .open = libxengnttab_backend_open, + .close = xengnttab_close, + .grant_copy = libxengnttab_fallback_grant_copy, + .set_max_grants = xengnttab_set_max_grants, + .map_refs = xengnttab_map_domain_grant_refs, + .unmap = libxengnttab_backend_unmap, +}; + +static void *libxenforeignmem_backend_map(uint32_t dom, void *addr, int prot, + size_t pages, xen_pfn_t *pfns, + int *errs) +{ + return xenforeignmemory_map2(xen_fmem, dom, addr, prot, 0, pages, pfns, + errs); +} + +static int libxenforeignmem_backend_unmap(void *addr, size_t pages) +{ + return xenforeignmemory_unmap(xen_fmem, addr, pages); +} + +struct foreignmem_backend_ops libxenforeignmem_backend_ops = { + .map = libxenforeignmem_backend_map, + .unmap = libxenforeignmem_backend_unmap, +}; + +struct qemu_xs_handle { + struct xs_handle *xsh; + NotifierList notifiers; +}; + +static void watch_event(void *opaque) +{ + struct qemu_xs_handle *h = opaque; + + for (;;) { + char **v = xs_check_watch(h->xsh); + + if (!v) { + break; + } + + notifier_list_notify(&h->notifiers, v); + free(v); + } +} + +static struct qemu_xs_handle *libxenstore_open(void) +{ + struct xs_handle *xsh = xs_open(0); + struct qemu_xs_handle *h; + + if (!xsh) { + return NULL; + } + + h = g_new0(struct qemu_xs_handle, 1); + h->xsh = xsh; + + notifier_list_init(&h->notifiers); + qemu_set_fd_handler(xs_fileno(h->xsh), watch_event, NULL, h); + + return h; +} + +static void libxenstore_close(struct qemu_xs_handle *h) +{ + g_assert(notifier_list_empty(&h->notifiers)); + qemu_set_fd_handler(xs_fileno(h->xsh), NULL, NULL, NULL); + xs_close(h->xsh); + g_free(h); +} + +static char *libxenstore_get_domain_path(struct qemu_xs_handle *h, + unsigned int domid) +{ + return xs_get_domain_path(h->xsh, domid); +} + +static char **libxenstore_directory(struct qemu_xs_handle *h, + xs_transaction_t t, const char *path, + unsigned int *num) +{ + return xs_directory(h->xsh, t, path, num); +} + +static void *libxenstore_read(struct qemu_xs_handle *h, xs_transaction_t t, + const char *path, unsigned int *len) +{ + return xs_read(h->xsh, t, path, len); +} + +static bool libxenstore_write(struct qemu_xs_handle *h, xs_transaction_t t, + const char *path, const void *data, + unsigned int len) +{ + return xs_write(h->xsh, t, path, data, len); +} + +static bool libxenstore_create(struct qemu_xs_handle *h, xs_transaction_t t, + unsigned int owner, unsigned int domid, + unsigned int perms, const char *path) +{ + struct xs_permissions perms_list[] = { + { + .id = owner, + .perms = XS_PERM_NONE, + }, + { + .id = domid, + .perms = perms, + }, + }; + + if (!xs_mkdir(h->xsh, t, path)) { + return false; + } + + return xs_set_permissions(h->xsh, t, path, perms_list, + ARRAY_SIZE(perms_list)); +} + +static bool libxenstore_destroy(struct qemu_xs_handle *h, xs_transaction_t t, + const char *path) +{ + return xs_rm(h->xsh, t, path); +} + +struct qemu_xs_watch { + char *path; + char *token; + xs_watch_fn fn; + void *opaque; + Notifier notifier; +}; + +static void watch_notify(Notifier *n, void *data) +{ + struct qemu_xs_watch *w = container_of(n, struct qemu_xs_watch, notifier); + const char **v = data; + + if (!strcmp(w->token, v[XS_WATCH_TOKEN])) { + w->fn(w->opaque, v[XS_WATCH_PATH]); + } +} + +static struct qemu_xs_watch *new_watch(const char *path, xs_watch_fn fn, + void *opaque) +{ + struct qemu_xs_watch *w = g_new0(struct qemu_xs_watch, 1); + QemuUUID uuid; + + qemu_uuid_generate(&uuid); + + w->token = qemu_uuid_unparse_strdup(&uuid); + w->path = g_strdup(path); + w->fn = fn; + w->opaque = opaque; + w->notifier.notify = watch_notify; + + return w; +} + +static void free_watch(struct qemu_xs_watch *w) +{ + g_free(w->token); + g_free(w->path); + + g_free(w); +} + +static struct qemu_xs_watch *libxenstore_watch(struct qemu_xs_handle *h, + const char *path, xs_watch_fn fn, + void *opaque) +{ + struct qemu_xs_watch *w = new_watch(path, fn, opaque); + + notifier_list_add(&h->notifiers, &w->notifier); + + if (!xs_watch(h->xsh, path, w->token)) { + notifier_remove(&w->notifier); + free_watch(w); + return NULL; + } + + return w; +} + +static void libxenstore_unwatch(struct qemu_xs_handle *h, + struct qemu_xs_watch *w) +{ + xs_unwatch(h->xsh, w->path, w->token); + notifier_remove(&w->notifier); + free_watch(w); +} + +static xs_transaction_t libxenstore_transaction_start(struct qemu_xs_handle *h) +{ + return xs_transaction_start(h->xsh); +} + +static bool libxenstore_transaction_end(struct qemu_xs_handle *h, + xs_transaction_t t, bool abort) +{ + return xs_transaction_end(h->xsh, t, abort); +} + +struct xenstore_backend_ops libxenstore_backend_ops = { + .open = libxenstore_open, + .close = libxenstore_close, + .get_domain_path = libxenstore_get_domain_path, + .directory = libxenstore_directory, + .read = libxenstore_read, + .write = libxenstore_write, + .create = libxenstore_create, + .destroy = libxenstore_destroy, + .watch = libxenstore_watch, + .unwatch = libxenstore_unwatch, + .transaction_start = libxenstore_transaction_start, + .transaction_end = libxenstore_transaction_end, +}; + +void setup_xen_backend_ops(void) +{ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40800 + xengnttab_handle *xgt = xengnttab_open(NULL, 0); + + if (xgt) { + if (xengnttab_grant_copy(xgt, 0, NULL) == 0) { + libxengnttab_backend_ops.grant_copy = libxengnttab_backend_grant_copy; + } + xengnttab_close(xgt); + } +#endif + xen_evtchn_ops = &libxenevtchn_backend_ops; + xen_gnttab_ops = &libxengnttab_backend_ops; + xen_foreignmem_ops = &libxenforeignmem_backend_ops; + xen_xenstore_ops = &libxenstore_backend_ops; +} diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c index 46ee4a7f02..2150869f60 100644 --- a/hw/xen/xen_devconfig.c +++ b/hw/xen/xen_devconfig.c @@ -11,11 +11,11 @@ static int xen_config_dev_dirs(const char *ftype, const char *btype, int vdev, { char *dom; - dom = xs_get_domain_path(xenstore, xen_domid); + dom = qemu_xen_xs_get_domain_path(xenstore, xen_domid); snprintf(fe, len, "%s/device/%s/%d", dom, ftype, vdev); free(dom); - dom = xs_get_domain_path(xenstore, 0); + dom = qemu_xen_xs_get_domain_path(xenstore, 0); snprintf(be, len, "%s/backend/%s/%d/%d", dom, btype, xen_domid, vdev); free(dom); @@ -46,59 +46,6 @@ static int xen_config_dev_all(char *fe, char *be) /* ------------------------------------------------------------- */ -int xen_config_dev_blk(DriveInfo *disk) -{ - char fe[256], be[256], device_name[32]; - int vdev = 202 * 256 + 16 * disk->unit; - int cdrom = disk->media_cd; - const char *devtype = cdrom ? "cdrom" : "disk"; - const char *mode = cdrom ? "r" : "w"; - const char *filename = qemu_opt_get(disk->opts, "file"); - - snprintf(device_name, sizeof(device_name), "xvd%c", 'a' + disk->unit); - xen_pv_printf(NULL, 1, "config disk %d [%s]: %s\n", - disk->unit, device_name, filename); - xen_config_dev_dirs("vbd", "qdisk", vdev, fe, be, sizeof(fe)); - - /* frontend */ - xenstore_write_int(fe, "virtual-device", vdev); - xenstore_write_str(fe, "device-type", devtype); - - /* backend */ - xenstore_write_str(be, "dev", device_name); - xenstore_write_str(be, "type", "file"); - xenstore_write_str(be, "params", filename); - xenstore_write_str(be, "mode", mode); - - /* common stuff */ - return xen_config_dev_all(fe, be); -} - -int xen_config_dev_nic(NICInfo *nic) -{ - char fe[256], be[256]; - char mac[20]; - int vlan_id = -1; - - net_hub_id_for_client(nic->netdev, &vlan_id); - snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", - nic->macaddr.a[0], nic->macaddr.a[1], nic->macaddr.a[2], - nic->macaddr.a[3], nic->macaddr.a[4], nic->macaddr.a[5]); - xen_pv_printf(NULL, 1, "config nic %d: mac=\"%s\"\n", vlan_id, mac); - xen_config_dev_dirs("vif", "qnic", vlan_id, fe, be, sizeof(fe)); - - /* frontend */ - xenstore_write_int(fe, "handle", vlan_id); - xenstore_write_str(fe, "mac", mac); - - /* backend */ - xenstore_write_int(be, "handle", vlan_id); - xenstore_write_str(be, "mac", mac); - - /* common stuff */ - return xen_config_dev_all(fe, be); -} - int xen_config_dev_vfb(int vdev, const char *type) { char fe[256], be[256]; diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 0ec7e52183..3635d1b39f 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -59,9 +59,10 @@ #include "hw/pci/pci.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" +#include "hw/xen/xen_pt.h" +#include "hw/xen/xen_igd.h" #include "hw/xen/xen.h" #include "hw/xen/xen-legacy-backend.h" -#include "xen_pt.h" #include "qemu/range.h" static bool has_igd_gfx_passthru; @@ -434,7 +435,7 @@ static uint64_t xen_pt_bar_read(void *o, hwaddr addr, PCIDevice *d = o; /* if this function is called, that probably means that there is a * misconfiguration of the IOMMU. */ - XEN_PT_ERR(d, "Should not read BAR through QEMU. @0x"TARGET_FMT_plx"\n", + XEN_PT_ERR(d, "Should not read BAR through QEMU. @0x"HWADDR_FMT_plx"\n", addr); return 0; } @@ -443,7 +444,7 @@ static void xen_pt_bar_write(void *o, hwaddr addr, uint64_t val, { PCIDevice *d = o; /* Same comment as xen_pt_bar_read function */ - XEN_PT_ERR(d, "Should not write BAR through QEMU. @0x"TARGET_FMT_plx"\n", + XEN_PT_ERR(d, "Should not write BAR through QEMU. @0x"HWADDR_FMT_plx"\n", addr); } @@ -691,14 +692,14 @@ static const MemoryListener xen_pt_memory_listener = { .name = "xen-pt-mem", .region_add = xen_pt_region_add, .region_del = xen_pt_region_del, - .priority = 10, + .priority = MEMORY_LISTENER_PRIORITY_ACCEL, }; static const MemoryListener xen_pt_io_listener = { .name = "xen-pt-io", .region_add = xen_pt_io_region_add, .region_del = xen_pt_io_region_del, - .priority = 10, + .priority = MEMORY_LISTENER_PRIORITY_ACCEL, }; /* destroy. */ @@ -710,7 +711,7 @@ static void xen_pt_destroy(PCIDevice *d) { uint8_t intx; int rc; - if (machine_irq && !xen_host_pci_device_closed(&s->real_device)) { + if (machine_irq && !xen_host_pci_device_closed(host_dev)) { intx = xen_pt_pci_intx(s); rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq, PT_IRQ_TYPE_PCI, @@ -759,8 +760,8 @@ static void xen_pt_destroy(PCIDevice *d) { memory_listener_unregister(&s->io_listener); s->listener_set = false; } - if (!xen_host_pci_device_closed(&s->real_device)) { - xen_host_pci_device_put(&s->real_device); + if (!xen_host_pci_device_closed(host_dev)) { + xen_host_pci_device_put(host_dev); } } /* init */ @@ -780,15 +781,6 @@ static void xen_pt_realize(PCIDevice *d, Error **errp) s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function, s->dev.devfn); - xen_host_pci_device_get(&s->real_device, - s->hostaddr.domain, s->hostaddr.bus, - s->hostaddr.slot, s->hostaddr.function, - errp); - if (*errp) { - error_append_hint(errp, "Failed to \"open\" the real pci device"); - return; - } - s->is_virtfn = s->real_device.is_virtfn; if (s->is_virtfn) { XEN_PT_LOG(d, "%04x:%02x:%02x.%d is a SR-IOV Virtual Function\n", @@ -803,8 +795,10 @@ static void xen_pt_realize(PCIDevice *d, Error **errp) s->io_listener = xen_pt_io_listener; /* Setup VGA bios for passthrough GFX */ - if ((s->real_device.domain == 0) && (s->real_device.bus == 0) && - (s->real_device.dev == 2) && (s->real_device.func == 0)) { + if ((s->real_device.domain == XEN_PCI_IGD_DOMAIN) && + (s->real_device.bus == XEN_PCI_IGD_BUS) && + (s->real_device.dev == XEN_PCI_IGD_DEV) && + (s->real_device.func == XEN_PCI_IGD_FN)) { if (!is_igd_vga_passthrough(&s->real_device)) { error_setg(errp, "Need to enable igd-passthru if you're trying" " to passthrough IGD GFX"); @@ -950,11 +944,58 @@ static void xen_pci_passthrough_instance_init(Object *obj) PCI_DEVICE(obj)->cap_present |= QEMU_PCI_CAP_EXPRESS; } +void xen_igd_reserve_slot(PCIBus *pci_bus) +{ + if (!xen_igd_gfx_pt_enabled()) { + return; + } + + XEN_PT_LOG(0, "Reserving PCI slot 2 for IGD\n"); + pci_bus_set_slot_reserved_mask(pci_bus, XEN_PCI_IGD_SLOT_MASK); +} + +static void xen_igd_clear_slot(DeviceState *qdev, Error **errp) +{ + ERRP_GUARD(); + PCIDevice *pci_dev = (PCIDevice *)qdev; + XenPCIPassthroughState *s = XEN_PT_DEVICE(pci_dev); + XenPTDeviceClass *xpdc = XEN_PT_DEVICE_GET_CLASS(s); + PCIBus *pci_bus = pci_get_bus(pci_dev); + + xen_host_pci_device_get(&s->real_device, + s->hostaddr.domain, s->hostaddr.bus, + s->hostaddr.slot, s->hostaddr.function, + errp); + if (*errp) { + error_append_hint(errp, "Failed to \"open\" the real pci device"); + return; + } + + if (!(pci_bus_get_slot_reserved_mask(pci_bus) & XEN_PCI_IGD_SLOT_MASK)) { + xpdc->pci_qdev_realize(qdev, errp); + return; + } + + if (is_igd_vga_passthrough(&s->real_device) && + s->real_device.domain == XEN_PCI_IGD_DOMAIN && + s->real_device.bus == XEN_PCI_IGD_BUS && + s->real_device.dev == XEN_PCI_IGD_DEV && + s->real_device.func == XEN_PCI_IGD_FN && + s->real_device.vendor_id == PCI_VENDOR_ID_INTEL) { + pci_bus_clear_slot_reserved_mask(pci_bus, XEN_PCI_IGD_SLOT_MASK); + XEN_PT_LOG(pci_dev, "Intel IGD found, using slot 2\n"); + } + xpdc->pci_qdev_realize(qdev, errp); +} + static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + XenPTDeviceClass *xpdc = XEN_PT_DEVICE_CLASS(klass); + xpdc->pci_qdev_realize = dc->realize; + dc->realize = xen_igd_clear_slot; k->realize = xen_pt_realize; k->exit = xen_pt_unregister_device; k->config_read = xen_pt_pci_read_config; @@ -977,6 +1018,7 @@ static const TypeInfo xen_pci_passthrough_info = { .instance_size = sizeof(XenPCIPassthroughState), .instance_finalize = xen_pci_passthrough_finalize, .class_init = xen_pci_passthrough_class_init, + .class_size = sizeof(XenPTDeviceClass), .instance_init = xen_pci_passthrough_instance_init, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index e7c4316a7d..095a0f0365 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -1,14 +1,20 @@ +/* + * Copyright (c) 2007, Neocleus Corporation. + * Copyright (c) 2007, Intel Corporation. + * + * SPDX-License-Identifier: GPL-2.0-only + * + * Alex Novik + * Allen Kay + * Guy Zana + */ #ifndef XEN_PT_H #define XEN_PT_H -#include "hw/xen/xen_common.h" -#include "hw/pci/pci.h" +#include "hw/xen/xen_native.h" #include "xen-host-pci-device.h" #include "qom/object.h" -bool xen_igd_gfx_pt_enabled(void); -void xen_igd_gfx_pt_set(bool value, Error **errp); - void xen_pt_log(const PCIDevice *d, const char *f, ...) G_GNUC_PRINTF(2, 3); #define XEN_PT_ERR(d, _f, _a...) xen_pt_log(d, "%s: Error: "_f, __func__, ##_a) @@ -41,10 +47,17 @@ typedef struct XenPTReg XenPTReg; #define TYPE_XEN_PT_DEVICE "xen-pci-passthrough" OBJECT_DECLARE_SIMPLE_TYPE(XenPCIPassthroughState, XEN_PT_DEVICE) -uint32_t igd_read_opregion(XenPCIPassthroughState *s); -void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val); -void xen_igd_passthrough_isa_bridge_create(XenPCIPassthroughState *s, - XenHostPCIDevice *dev); +#define XEN_PT_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(XenPTDeviceClass, klass, TYPE_XEN_PT_DEVICE) +#define XEN_PT_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(XenPTDeviceClass, obj, TYPE_XEN_PT_DEVICE) + +typedef void (*XenPTQdevRealize)(DeviceState *qdev, Error **errp); + +typedef struct XenPTDeviceClass { + PCIDeviceClass parent_class; + XenPTQdevRealize pci_qdev_realize; +} XenPTDeviceClass; /* function type for config reg */ typedef int (*xen_pt_conf_reg_init) @@ -76,6 +89,13 @@ typedef int (*xen_pt_conf_byte_read) #define XEN_PCI_INTEL_OPREGION 0xfc +#define XEN_PCI_IGD_DOMAIN 0 +#define XEN_PCI_IGD_BUS 0 +#define XEN_PCI_IGD_DEV 2 +#define XEN_PCI_IGD_FN 0 +#define XEN_PCI_IGD_SLOT_MASK \ + (1UL << PCI_SLOT(PCI_DEVFN(XEN_PCI_IGD_DEV, XEN_PCI_IGD_FN))) + typedef enum { XEN_PT_GRP_TYPE_HARDWIRED = 0, /* 0 Hardwired reg group */ XEN_PT_GRP_TYPE_EMU, /* emul reg group */ @@ -321,16 +341,9 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar) return s->msix && s->msix->bar_index == bar; } -extern void *pci_assign_dev_load_option_rom(PCIDevice *dev, - int *size, - unsigned int domain, - unsigned int bus, unsigned int slot, - unsigned int function); -static inline bool is_igd_vga_passthrough(XenHostPCIDevice *dev) -{ - return (xen_igd_gfx_pt_enabled() - && ((dev->class_code >> 0x8) == PCI_CLASS_DISPLAY_VGA)); -} +void *pci_assign_dev_load_option_rom(PCIDevice *dev, int *size, + unsigned int domain, unsigned int bus, + unsigned int slot, unsigned int function); int xen_pt_register_vga_regions(XenHostPCIDevice *dev); int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev); void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev, diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c index c5c4e943a8..3edaeab1e3 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -15,8 +15,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/timer.h" +#include "hw/xen/xen_pt.h" +#include "hw/xen/xen_igd.h" #include "hw/xen/xen-legacy-backend.h" -#include "xen_pt.h" #define XEN_PT_MERGE_VALUE(value, data, val_mask) \ (((value) & (val_mask)) | ((data) & ~(val_mask))) @@ -291,7 +292,10 @@ static int xen_pt_header_type_reg_init(XenPCIPassthroughState *s, uint32_t *data) { /* read PCI_HEADER_TYPE */ - *data = reg->init_val | 0x80; + *data = reg->init_val; + if ((PCI_DEVICE(s)->cap_present & QEMU_PCI_CAP_MULTIFUNCTION)) { + *data |= PCI_HEADER_TYPE_MULTI_FUNCTION; + } return 0; } @@ -676,7 +680,7 @@ static XenPTRegInfo xen_pt_emu_reg_header0[] = { .size = 1, .init_val = 0x00, .ro_mask = 0xFF, - .emu_mask = 0x00, + .emu_mask = PCI_HEADER_TYPE_MULTI_FUNCTION, .init = xen_pt_header_type_reg_init, .u.b.read = xen_pt_byte_reg_read, .u.b.write = xen_pt_byte_reg_write, @@ -985,8 +989,8 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = { .offset = 0x28, .size = 2, .init_val = 0x0000, - .ro_mask = 0xFFE0, - .emu_mask = 0xFFFF, + .ro_mask = 0xFFA0, + .emu_mask = 0xFFBF, .init = xen_pt_devctrl2_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_word_reg_write, @@ -1924,7 +1928,7 @@ static void xen_pt_config_reg_init(XenPCIPassthroughState *s, if (reg->init) { uint32_t host_mask, size_mask; unsigned int offset; - uint32_t val; + uint32_t val = 0; /* initialize emulate register */ rc = reg->init(s, reg_entry->reg, @@ -1965,11 +1969,12 @@ static void xen_pt_config_reg_init(XenPCIPassthroughState *s, if ((data & host_mask) != (val & host_mask)) { uint32_t new_val; - - /* Mask out host (including past size). */ - new_val = val & host_mask; - /* Merge emulated ones (excluding the non-emulated ones). */ - new_val |= data & host_mask; + /* + * Merge the emulated bits (data) with the host bits (val) + * and mask out the bits past size to enable restoration + * of the proper value for logging below. + */ + new_val = XEN_PT_MERGE_VALUE(val, data, host_mask) & size_mask; /* Leave intact host and emulated values past the size - even though * we do not care as we write per reg->size granularity, but for the * logging below lets have the proper value. */ @@ -2031,12 +2036,16 @@ void xen_pt_config_init(XenPCIPassthroughState *s, Error **errp) } } - /* - * By default we will trap up to 0x40 in the cfg space. - * If an intel device is pass through we need to trap 0xfc, - * therefore the size should be 0xff. - */ if (xen_pt_emu_reg_grps[i].grp_id == XEN_PCI_INTEL_OPREGION) { + if (!is_igd_vga_passthrough(&s->real_device) || + s->real_device.vendor_id != PCI_VENDOR_ID_INTEL) { + continue; + } + /* + * By default we will trap up to 0x40 in the cfg space. + * If an intel device is pass through we need to trap 0xfc, + * therefore the size should be 0xff. + */ reg_grp_offset = XEN_PCI_INTEL_OPREGION; } diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c index f303f67c9c..6c2e3f4840 100644 --- a/hw/xen/xen_pt_graphics.c +++ b/hw/xen/xen_pt_graphics.c @@ -3,9 +3,9 @@ */ #include "qemu/osdep.h" #include "qapi/error.h" -#include "xen_pt.h" +#include "hw/xen/xen_pt.h" +#include "hw/xen/xen_igd.h" #include "xen-host-pci-device.h" -#include "hw/xen/xen-legacy-backend.h" static unsigned long igd_guest_opregion; static unsigned long igd_host_opregion; diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c index b71563f98a..09cca4eecb 100644 --- a/hw/xen/xen_pt_msi.c +++ b/hw/xen/xen_pt_msi.c @@ -11,9 +11,9 @@ #include "qemu/osdep.h" -#include "hw/xen/xen-legacy-backend.h" -#include "xen_pt.h" #include "hw/i386/apic-msidef.h" +#include "xen_pt.h" +#include "hw/xen/xen-legacy-backend.h" #define XEN_PT_AUTO_ASSIGN -1 diff --git a/hw/xen/xen_pt_stub.c b/hw/xen/xen_pt_stub.c index 2d8cac8d54..72feebeb20 100644 --- a/hw/xen/xen_pt_stub.c +++ b/hw/xen/xen_pt_stub.c @@ -6,7 +6,7 @@ */ #include "qemu/osdep.h" -#include "hw/xen/xen_pt.h" +#include "hw/xen/xen_igd.h" #include "qapi/error.h" bool xen_igd_gfx_pt_enabled(void) @@ -20,3 +20,7 @@ void xen_igd_gfx_pt_set(bool value, Error **errp) error_setg(errp, "Xen PCI passthrough support not built in"); } } + +void xen_igd_reserve_slot(PCIBus *pci_bus) +{ +} diff --git a/hw/xen/xen_pvdev.c b/hw/xen/xen_pvdev.c index 037152f063..c5ad71e8dc 100644 --- a/hw/xen/xen_pvdev.c +++ b/hw/xen/xen_pvdev.c @@ -54,31 +54,17 @@ void xen_config_cleanup(void) struct xs_dirs *d; QTAILQ_FOREACH(d, &xs_cleanup, list) { - xs_rm(xenstore, 0, d->xs_dir); + qemu_xen_xs_destroy(xenstore, 0, d->xs_dir); } } int xenstore_mkdir(char *path, int p) { - struct xs_permissions perms[2] = { - { - .id = 0, /* set owner: dom0 */ - }, { - .id = xen_domid, - .perms = p, - } - }; - - if (!xs_mkdir(xenstore, 0, path)) { + if (!qemu_xen_xs_create(xenstore, 0, 0, xen_domid, p, path)) { xen_pv_printf(NULL, 0, "xs_mkdir %s: failed\n", path); return -1; } xenstore_cleanup_dir(g_strdup(path)); - - if (!xs_set_permissions(xenstore, 0, path, perms, 2)) { - xen_pv_printf(NULL, 0, "xs_set_permissions %s: failed\n", path); - return -1; - } return 0; } @@ -87,7 +73,7 @@ int xenstore_write_str(const char *base, const char *node, const char *val) char abspath[XEN_BUFSIZE]; snprintf(abspath, sizeof(abspath), "%s/%s", base, node); - if (!xs_write(xenstore, 0, abspath, val, strlen(val))) { + if (!qemu_xen_xs_write(xenstore, 0, abspath, val, strlen(val))) { return -1; } return 0; @@ -100,10 +86,10 @@ char *xenstore_read_str(const char *base, const char *node) char *str, *ret = NULL; snprintf(abspath, sizeof(abspath), "%s/%s", base, node); - str = xs_read(xenstore, 0, abspath, &len); + str = qemu_xen_xs_read(xenstore, 0, abspath, &len); if (str != NULL) { /* move to qemu-allocated memory to make sure - * callers can savely g_free() stuff. */ + * callers can safely g_free() stuff. */ ret = g_strdup(str); free(str); } @@ -152,29 +138,6 @@ int xenstore_read_uint64(const char *base, const char *node, uint64_t *uval) return rc; } -void xenstore_update(void *unused) -{ - char **vec = NULL; - intptr_t type, ops, ptr; - unsigned int dom, count; - - vec = xs_read_watch(xenstore, &count); - if (vec == NULL) { - goto cleanup; - } - - if (sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, - &type, &dom, &ops) == 3) { - xenstore_update_be(vec[XS_WATCH_PATH], (void *)type, dom, (void*)ops); - } - if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1) { - xenstore_update_fe(vec[XS_WATCH_PATH], (void *)ptr); - } - -cleanup: - free(vec); -} - const char *xenbus_strstate(enum xenbus_state state) { static const char *const name[] = { @@ -196,6 +159,7 @@ const char *xenbus_strstate(enum xenbus_state state) * 2 == noisy debug messages (logfile only). * 3 == will flood your log (logfile only). */ +G_GNUC_PRINTF(3, 0) static void xen_pv_output_msg(struct XenLegacyDevice *xendev, FILE *f, const char *fmt, va_list args) { @@ -237,14 +201,14 @@ void xen_pv_evtchn_event(void *opaque) struct XenLegacyDevice *xendev = opaque; evtchn_port_t port; - port = xenevtchn_pending(xendev->evtchndev); + port = qemu_xen_evtchn_pending(xendev->evtchndev); if (port != xendev->local_port) { xen_pv_printf(xendev, 0, "xenevtchn_pending returned %d (expected %d)\n", port, xendev->local_port); return; } - xenevtchn_unmask(xendev->evtchndev, port); + qemu_xen_evtchn_unmask(xendev->evtchndev, port); if (xendev->ops->event) { xendev->ops->event(xendev); @@ -256,15 +220,15 @@ void xen_pv_unbind_evtchn(struct XenLegacyDevice *xendev) if (xendev->local_port == -1) { return; } - qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev), NULL, NULL, NULL); - xenevtchn_unbind(xendev->evtchndev, xendev->local_port); + qemu_set_fd_handler(qemu_xen_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL); + qemu_xen_evtchn_unbind(xendev->evtchndev, xendev->local_port); xen_pv_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port); xendev->local_port = -1; } int xen_pv_send_notify(struct XenLegacyDevice *xendev) { - return xenevtchn_notify(xendev->evtchndev, xendev->local_port); + return qemu_xen_evtchn_notify(xendev->evtchndev, xendev->local_port); } /* ------------------------------------------------------------- */ @@ -298,17 +262,15 @@ void xen_pv_del_xendev(struct XenLegacyDevice *xendev) } if (xendev->fe) { - char token[XEN_BUFSIZE]; - snprintf(token, sizeof(token), "fe:%p", xendev); - xs_unwatch(xenstore, xendev->fe, token); + qemu_xen_xs_unwatch(xenstore, xendev->watch); g_free(xendev->fe); } if (xendev->evtchndev != NULL) { - xenevtchn_close(xendev->evtchndev); + qemu_xen_evtchn_close(xendev->evtchndev); } if (xendev->gnttabdev != NULL) { - xengnttab_close(xendev->gnttabdev); + qemu_xen_gnttab_close(xendev->gnttabdev); } QTAILQ_REMOVE(&xendevs, xendev, next); diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index 20c9611d71..1130d1a147 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -32,14 +32,10 @@ static void xen_init_pv(MachineState *machine) { - DriveInfo *dinfo; - int i; + setup_xen_backend_ops(); /* Initialize backend core & drivers */ - if (xen_be_init() != 0) { - error_report("%s: xen backend core setup failed", __func__); - exit(1); - } + xen_be_init(); switch (xen_mode) { case XEN_ATTACH: @@ -55,9 +51,7 @@ static void xen_init_pv(MachineState *machine) break; } - xen_be_register_common(); xen_be_register("vfb", &xen_framebuffer_ops); - xen_be_register("qnic", &xen_netdev_ops); /* configure framebuffer */ if (vga_interface_type == VGA_XENFB) { @@ -66,21 +60,6 @@ static void xen_init_pv(MachineState *machine) vga_interface_created = true; } - /* configure disks */ - for (i = 0; i < 16; i++) { - dinfo = drive_get(IF_XEN, 0, i); - if (!dinfo) - continue; - xen_config_dev_blk(dinfo); - } - - /* configure nics */ - for (i = 0; i < nb_nics; i++) { - if (!nd_table[i].model || 0 != strcmp(nd_table[i].model, "xen")) - continue; - xen_config_dev_nic(nd_table + i); - } - xen_bus_init(); /* config cleanup hook */ diff --git a/hw/xtensa/pic_cpu.c b/hw/xtensa/pic_cpu.c index 6c9447565d..8cef88c61b 100644 --- a/hw/xtensa/pic_cpu.c +++ b/hw/xtensa/pic_cpu.c @@ -30,6 +30,7 @@ #include "hw/irq.h" #include "qemu/log.h" #include "qemu/timer.h" +#include "qemu/atomic.h" void check_interrupts(CPUXtensaState *env) { diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index 946c71cb5b..2160e61964 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -96,16 +96,11 @@ XtensaCPU *xtensa_sim_common_init(MachineState *machine) void xtensa_sim_load_kernel(XtensaCPU *cpu, MachineState *machine) { const char *kernel_filename = machine->kernel_filename; -#if TARGET_BIG_ENDIAN - int big_endian = true; -#else - int big_endian = false; -#endif if (kernel_filename) { uint64_t elf_entry; int success = load_elf(kernel_filename, NULL, translate_phys_addr, cpu, - &elf_entry, NULL, NULL, NULL, big_endian, + &elf_entry, NULL, NULL, NULL, TARGET_BIG_ENDIAN, EM_XTENSA, 0, 0); if (success > 0) { diff --git a/hw/xtensa/virt.c b/hw/xtensa/virt.c index a18e3fc910..5310a88861 100644 --- a/hw/xtensa/virt.c +++ b/hw/xtensa/virt.c @@ -38,7 +38,8 @@ #include "xtensa_memory.h" #include "xtensa_sim.h" -static void create_pcie(CPUXtensaState *env, int irq_base, hwaddr addr_base) +static void create_pcie(MachineState *ms, CPUXtensaState *env, int irq_base, + hwaddr addr_base) { hwaddr base_ecam = addr_base + 0x00100000; hwaddr size_ecam = 0x03f00000; @@ -54,6 +55,7 @@ static void create_pcie(CPUXtensaState *env, int irq_base, hwaddr addr_base) MemoryRegion *mmio_alias; MemoryRegion *mmio_reg; + MachineClass *mc = MACHINE_GET_CLASS(ms); DeviceState *dev; PCIHostState *pci; qemu_irq *extints; @@ -100,15 +102,7 @@ static void create_pcie(CPUXtensaState *env, int irq_base, hwaddr addr_base) pci = PCI_HOST_BRIDGE(dev); if (pci->bus) { - for (i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - - if (!nd->model) { - nd->model = g_strdup("virtio"); - } - - pci_nic_init_nofail(nd, pci->bus, nd->model, NULL); - } + pci_init_nic_devices(pci->bus, mc->default_nic); } } @@ -117,7 +111,7 @@ static void xtensa_virt_init(MachineState *machine) XtensaCPU *cpu = xtensa_sim_common_init(machine); CPUXtensaState *env = &cpu->env; - create_pcie(env, 0, 0xf0000000); + create_pcie(machine, env, 0, 0xf0000000); xtensa_sim_load_kernel(cpu, machine); } @@ -127,6 +121,7 @@ static void xtensa_virt_machine_init(MachineClass *mc) mc->init = xtensa_virt_init; mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; + mc->default_nic = "virtio-net-pci"; } DEFINE_MACHINE("virt", xtensa_virt_machine_init) diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 2a5556a35f..f49e6591dc 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -141,14 +141,16 @@ static void xtfpga_net_init(MemoryRegion *address_space, hwaddr base, hwaddr descriptors, hwaddr buffers, - qemu_irq irq, NICInfo *nd) + qemu_irq irq) { DeviceState *dev; SysBusDevice *s; MemoryRegion *ram; - dev = qdev_new("open_eth"); - qdev_set_nic_properties(dev, nd); + dev = qemu_create_nic_device("open_eth", true, NULL); + if (!dev) { + return; + } s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); @@ -219,11 +221,6 @@ static const MemoryRegionOps xtfpga_io_ops = { static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) { -#if TARGET_BIG_ENDIAN - int be = 1; -#else - int be = 0; -#endif MemoryRegion *system_memory = get_system_memory(); XtensaCPU *cpu = NULL; CPUXtensaState *env = NULL; @@ -306,17 +303,14 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) memory_region_add_subregion(system_memory, board->io[1], io); } xtfpga_fpga_init(system_io, 0x0d020000, freq); - if (nd_table[0].used) { - xtfpga_net_init(system_io, 0x0d030000, 0x0d030400, 0x0d800000, - extints[1], nd_table); - } + xtfpga_net_init(system_io, 0x0d030000, 0x0d030400, 0x0d800000, extints[1]); serial_mm_init(system_io, 0x0d050020, 2, extints[0], 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); dinfo = drive_get(IF_PFLASH, 0, 0); if (dinfo) { - flash = xtfpga_flash_init(system_io, board, dinfo, be); + flash = xtfpga_flash_init(system_io, board, dinfo, TARGET_BIG_ENDIAN); } /* Use presence of kernel file name as 'boot from SRAM' switch. */ @@ -412,7 +406,8 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) uint64_t elf_entry; int success = load_elf(kernel_filename, NULL, translate_phys_addr, cpu, - &elf_entry, NULL, NULL, NULL, be, EM_XTENSA, 0, 0); + &elf_entry, NULL, NULL, NULL, TARGET_BIG_ENDIAN, + EM_XTENSA, 0, 0); if (success > 0) { entry_point = elf_entry; } else { diff --git a/include/block/accounting.h b/include/block/accounting.h index 878b4c3581..a59e39f49d 100644 --- a/include/block/accounting.h +++ b/include/block/accounting.h @@ -27,7 +27,7 @@ #include "qemu/timed-average.h" #include "qemu/thread.h" -#include "qapi/qapi-builtin-types.h" +#include "qapi/qapi-types-common.h" typedef struct BlockAcctTimedStats BlockAcctTimedStats; typedef struct BlockAcctStats BlockAcctStats; @@ -37,6 +37,7 @@ enum BlockAcctType { BLOCK_ACCT_READ, BLOCK_ACCT_WRITE, BLOCK_ACCT_FLUSH, + BLOCK_ACCT_ZONE_APPEND, BLOCK_ACCT_UNMAP, BLOCK_MAX_IOTYPE, }; @@ -100,8 +101,8 @@ typedef struct BlockAcctCookie { } BlockAcctCookie; void block_acct_init(BlockAcctStats *stats); -void block_acct_setup(BlockAcctStats *stats, bool account_invalid, - bool account_failed); +void block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid, + enum OnOffAuto account_failed); void block_acct_cleanup(BlockAcctStats *stats); void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length); BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats, diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h index b39eefb38d..cf5e8bde1c 100644 --- a/include/block/aio-wait.h +++ b/include/block/aio-wait.h @@ -59,7 +59,7 @@ typedef struct { extern AioWait global_aio_wait; /** - * AIO_WAIT_WHILE: + * AIO_WAIT_WHILE_INTERNAL: * @ctx: the aio context, or NULL if multiple aio contexts (for which the * caller does not hold a lock) are involved in the polling condition. * @cond: wait while this conditional expression is true @@ -75,12 +75,14 @@ extern AioWait global_aio_wait; * wait on conditions between two IOThreads since that could lead to deadlock, * go via the main loop instead. */ -#define AIO_WAIT_WHILE(ctx, cond) ({ \ +#define AIO_WAIT_WHILE_INTERNAL(ctx, cond) ({ \ bool waited_ = false; \ AioWait *wait_ = &global_aio_wait; \ AioContext *ctx_ = (ctx); \ /* Increment wait_->num_waiters before evaluating cond. */ \ qatomic_inc(&wait_->num_waiters); \ + /* Paired with smp_mb in aio_wait_kick(). */ \ + smp_mb__after_rmw(); \ if (ctx_ && in_aio_context_home_thread(ctx_)) { \ while ((cond)) { \ aio_poll(ctx_, true); \ @@ -90,19 +92,20 @@ extern AioWait global_aio_wait; assert(qemu_get_current_aio_context() == \ qemu_get_aio_context()); \ while ((cond)) { \ - if (ctx_) { \ - aio_context_release(ctx_); \ - } \ aio_poll(qemu_get_aio_context(), true); \ - if (ctx_) { \ - aio_context_acquire(ctx_); \ - } \ waited_ = true; \ } \ } \ qatomic_dec(&wait_->num_waiters); \ waited_; }) +#define AIO_WAIT_WHILE(ctx, cond) \ + AIO_WAIT_WHILE_INTERNAL(ctx, cond) + +/* TODO replace this with AIO_WAIT_WHILE() in a future patch */ +#define AIO_WAIT_WHILE_UNLOCKED(ctx, cond) \ + AIO_WAIT_WHILE_INTERNAL(ctx, cond) + /** * aio_wait_kick: * Wake up the main thread if it is waiting on AIO_WAIT_WHILE(). During @@ -120,7 +123,7 @@ void aio_wait_kick(void); * * Run a BH in @ctx and wait for it to complete. * - * Must be called from the main loop thread with @ctx acquired exactly once. + * Must be called from the main loop thread without @ctx acquired. * Note that main loop event processing may occur. */ void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque); @@ -140,7 +143,7 @@ static inline bool in_aio_context_home_thread(AioContext *ctx) } if (ctx == qemu_get_aio_context()) { - return qemu_mutex_iothread_locked(); + return bql_locked(); } else { return false; } diff --git a/include/block/aio.h b/include/block/aio.h index d128558f1d..8378553eb9 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -17,18 +17,20 @@ #ifdef CONFIG_LINUX_IO_URING #include #endif -#include "qemu/coroutine.h" +#include "qemu/coroutine-core.h" #include "qemu/queue.h" #include "qemu/event_notifier.h" #include "qemu/thread.h" #include "qemu/timer.h" +#include "block/graph-lock.h" +#include "hw/qdev-core.h" + typedef struct BlockAIOCB BlockAIOCB; typedef void BlockCompletionFunc(void *opaque, int ret); typedef struct AIOCBInfo { void (*cancel_async)(BlockAIOCB *acb); - AioContext *(*get_aio_context)(BlockAIOCB *acb); size_t aiocb_size; } AIOCBInfo; @@ -51,10 +53,9 @@ typedef void QEMUBHFunc(void *opaque); typedef bool AioPollFn(void *opaque); typedef void IOHandler(void *opaque); -struct Coroutine; struct ThreadPool; struct LinuxAioState; -struct LuringState; +typedef struct LuringState LuringState; /* Is polling disabled? */ bool aio_poll_disabled(AioContext *ctx); @@ -127,6 +128,14 @@ struct AioContext { /* Used by AioContext users to protect from multi-threaded access. */ QemuRecMutex lock; + /* + * Keep track of readers and writers of the block layer graph. + * This is essential to avoid performing additions and removal + * of nodes and edges from block graph while some + * other thread is traversing it. + */ + BdrvGraphRWlock *bdrv_graph; + /* The list of registered AIO handlers. Protected by ctx->list_lock. */ AioHandlerList aio_handlers; @@ -200,18 +209,10 @@ struct AioContext { struct ThreadPool *thread_pool; #ifdef CONFIG_LINUX_AIO - /* - * 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; + LuringState *linux_io_uring; /* State for file descriptor monitoring using Linux io_uring */ struct io_uring fdmon_io_uring; @@ -223,8 +224,6 @@ struct AioContext { */ QEMUTimerListGroup tlg; - int external_disable_cnt; - /* Number of AioHandlers without .io_poll() */ int poll_disable_cnt; @@ -279,23 +278,6 @@ void aio_context_ref(AioContext *ctx); */ void aio_context_unref(AioContext *ctx); -/* Take ownership of the AioContext. If the AioContext will be shared between - * threads, and a thread does not want to be interrupted, it will have to - * take ownership around calls to aio_poll(). Otherwise, aio_poll() - * automatically takes care of calling aio_context_acquire and - * aio_context_release. - * - * Note that this is separate from bdrv_drained_begin/bdrv_drained_end. A - * thread still has to call those to avoid being interrupted by the guest. - * - * Bottom halves, timers and callbacks can be created or removed without - * acquiring the AioContext. - */ -void aio_context_acquire(AioContext *ctx); - -/* Relinquish ownership of the AioContext. */ -void aio_context_release(AioContext *ctx); - /** * aio_bh_schedule_oneshot_full: Allocate a new bottom half structure that will * run only once and as soon as possible. @@ -323,9 +305,11 @@ void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, * is opaque and must be allocated prior to its use. * * @name: A human-readable identifier for debugging purposes. + * @reentrancy_guard: A guard set when entering a cb to prevent + * device-reentrancy issues */ QEMUBH *aio_bh_new_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, - const char *name); + const char *name, MemReentrancyGuard *reentrancy_guard); /** * aio_bh_new: Allocate a new bottom half structure @@ -334,7 +318,17 @@ QEMUBH *aio_bh_new_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, * string. */ #define aio_bh_new(ctx, cb, opaque) \ - aio_bh_new_full((ctx), (cb), (opaque), (stringify(cb))) + aio_bh_new_full((ctx), (cb), (opaque), (stringify(cb)), NULL) + +/** + * aio_bh_new_guarded: Allocate a new bottom half structure with a + * reentrancy_guard + * + * A convenience wrapper for aio_bh_new_full() that uses the cb as the name + * string. + */ +#define aio_bh_new_guarded(ctx, cb, opaque, guard) \ + aio_bh_new_full((ctx), (cb), (opaque), (stringify(cb)), guard) /** * aio_notify: Force processing of pending events. @@ -456,7 +450,7 @@ void aio_dispatch(AioContext *ctx); * or more AIO events have completed, to ensure something has moved * before returning. */ -bool aio_poll(AioContext *ctx, bool blocking); +bool no_coroutine_fn aio_poll(AioContext *ctx, bool blocking); /* Register a file descriptor and associated callbacks. Behaves very similarly * to qemu_set_fd_handler. Unlike qemu_set_fd_handler, these callbacks will @@ -467,21 +461,12 @@ bool aio_poll(AioContext *ctx, bool blocking); */ void aio_set_fd_handler(AioContext *ctx, int fd, - bool is_external, IOHandler *io_read, IOHandler *io_write, AioPollFn *io_poll, IOHandler *io_poll_ready, void *opaque); -/* Set polling begin/end callbacks for a file descriptor that has already been - * registered with aio_set_fd_handler. Do nothing if the file descriptor is - * not registered. - */ -void aio_set_fd_poll(AioContext *ctx, int fd, - IOHandler *io_poll_begin, - IOHandler *io_poll_end); - /* Register an event notifier and associated callbacks. Behaves very similarly * to event_notifier_set_handler. Unlike event_notifier_set_handler, these callbacks * will be invoked when using aio_poll(). @@ -491,14 +476,18 @@ void aio_set_fd_poll(AioContext *ctx, int fd, */ void aio_set_event_notifier(AioContext *ctx, EventNotifier *notifier, - bool is_external, EventNotifierHandler *io_read, AioPollFn *io_poll, EventNotifierHandler *io_poll_ready); -/* Set polling begin/end callbacks for an event notifier that has already been +/* + * Set polling begin/end callbacks for an event notifier that has already been * registered with aio_set_event_notifier. Do nothing if the event notifier is * not registered. + * + * Note that if the io_poll_end() callback (or the entire notifier) is removed + * during polling, it will not be called, so an io_poll_begin() is not + * necessarily always followed by an io_poll_end(). */ void aio_set_event_notifier_poll(AioContext *ctx, EventNotifier *notifier, @@ -520,10 +509,10 @@ struct LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp); 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); +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); +LuringState *aio_get_linux_io_uring(AioContext *ctx); /** * aio_timer_new_with_attrs: * @ctx: the aio context @@ -620,59 +609,6 @@ static inline void aio_timer_init(AioContext *ctx, */ int64_t aio_compute_timeout(AioContext *ctx); -/** - * aio_disable_external: - * @ctx: the aio context - * - * Disable the further processing of external clients. - */ -static inline void aio_disable_external(AioContext *ctx) -{ - qatomic_inc(&ctx->external_disable_cnt); -} - -/** - * aio_enable_external: - * @ctx: the aio context - * - * Enable the processing of external clients. - */ -static inline void aio_enable_external(AioContext *ctx) -{ - int old; - - old = qatomic_fetch_dec(&ctx->external_disable_cnt); - assert(old > 0); - if (old == 1) { - /* Kick event loop so it re-arms file descriptors */ - aio_notify(ctx); - } -} - -/** - * aio_external_disabled: - * @ctx: the aio context - * - * Return true if the external clients are disabled. - */ -static inline bool aio_external_disabled(AioContext *ctx) -{ - return qatomic_read(&ctx->external_disable_cnt); -} - -/** - * aio_node_check: - * @ctx: the aio context - * @is_external: Whether or not the checked node is an external event source. - * - * Check if the node's is_external flag is okay to be polled by the ctx at this - * moment. True means green light. - */ -static inline bool aio_node_check(AioContext *ctx, bool is_external) -{ - return !is_external || !qatomic_read(&ctx->external_disable_cnt); -} - /** * aio_co_schedule: * @ctx: the aio context @@ -685,7 +621,7 @@ static inline bool aio_node_check(AioContext *ctx, bool is_external) * is the context in which the coroutine is running (i.e. the value of * qemu_get_current_aio_context() from the coroutine itself). */ -void aio_co_schedule(AioContext *ctx, struct Coroutine *co); +void aio_co_schedule(AioContext *ctx, Coroutine *co); /** * aio_co_reschedule_self: @@ -708,7 +644,7 @@ void coroutine_fn aio_co_reschedule_self(AioContext *new_ctx); * context. The coroutine must not be entered by anyone else while * aio_co_wake() is active. */ -void aio_co_wake(struct Coroutine *co); +void aio_co_wake(Coroutine *co); /** * aio_co_enter: @@ -717,7 +653,7 @@ void aio_co_wake(struct Coroutine *co); * * Enter a coroutine in the specified AioContext. */ -void aio_co_enter(AioContext *ctx, struct Coroutine *co); +void aio_co_enter(AioContext *ctx, Coroutine *co); /** * Return the AioContext whose event loop runs in the current thread. @@ -768,8 +704,7 @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, * @max_batch: maximum number of requests in a batch, 0 means that the * engine will use its default */ -void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch, - Error **errp); +void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch); /** * aio_context_set_thread_pool_params: diff --git a/include/block/aio_task.h b/include/block/aio_task.h index 50bc1e1817..18a9c41f4e 100644 --- a/include/block/aio_task.h +++ b/include/block/aio_task.h @@ -25,8 +25,6 @@ #ifndef BLOCK_AIO_TASK_H #define BLOCK_AIO_TASK_H -#include "qemu/coroutine.h" - typedef struct AioTaskPool AioTaskPool; typedef struct AioTask AioTask; typedef int coroutine_fn (*AioTaskFunc)(AioTask *task); diff --git a/include/block/block-common.h b/include/block/block-common.h index fdb7306e78..a846023a09 100644 --- a/include/block/block-common.h +++ b/include/block/block-common.h @@ -24,34 +24,123 @@ #ifndef BLOCK_COMMON_H #define BLOCK_COMMON_H -#include "block/aio.h" -#include "block/aio-wait.h" -#include "qemu/iov.h" -#include "qemu/coroutine.h" -#include "block/accounting.h" -#include "block/dirty-bitmap.h" -#include "block/blockjob.h" -#include "qemu/hbitmap.h" -#include "qemu/transactions.h" +#include "qapi/qapi-types-block-core.h" +#include "qemu/queue.h" /* - * generated_co_wrapper + * co_wrapper{*}: Function specifiers used by block-coroutine-wrapper.py * - * Function specifier, which does nothing but mark functions to be + * Function specifiers, which do nothing but mark functions to be * generated by scripts/block-coroutine-wrapper.py * - * Read more in docs/devel/block-coroutine-wrapper.rst + * Usage: read docs/devel/block-coroutine-wrapper.rst + * + * There are 4 kind of specifiers: + * - co_wrapper functions can be called by only non-coroutine context, because + * they always generate a new coroutine. + * - co_wrapper_mixed functions can be called by both coroutine and + * non-coroutine context. + * - co_wrapper_bdrv_rdlock are co_wrapper functions but automatically take and + * release the graph rdlock when creating a new coroutine + * - co_wrapper_mixed_bdrv_rdlock are co_wrapper_mixed functions but + * automatically take and release the graph rdlock when creating a new + * coroutine. + * + * These functions should not be called from a coroutine_fn; instead, + * call the wrapped function directly. */ -#define generated_co_wrapper +#define co_wrapper no_coroutine_fn +#define co_wrapper_mixed no_coroutine_fn coroutine_mixed_fn +#define co_wrapper_bdrv_rdlock no_coroutine_fn +#define co_wrapper_mixed_bdrv_rdlock no_coroutine_fn coroutine_mixed_fn + +/* + * no_co_wrapper: Function specifier used by block-coroutine-wrapper.py + * + * Function specifier which does nothing but mark functions to be generated by + * scripts/block-coroutine-wrapper.py. + * + * A no_co_wrapper function declaration creates a coroutine_fn wrapper around + * functions that must not be called in coroutine context. It achieves this by + * scheduling a BH in the bottom half that runs the respective non-coroutine + * function. The coroutine yields after scheduling the BH and is reentered when + * the wrapped function returns. + * + * A no_co_wrapper_bdrv_rdlock function is a no_co_wrapper function that + * automatically takes the graph rdlock when calling the wrapped function. In + * the same way, no_co_wrapper_bdrv_wrlock functions automatically take the + * graph wrlock. + */ +#define no_co_wrapper +#define no_co_wrapper_bdrv_rdlock +#define no_co_wrapper_bdrv_wrlock + +#include "block/blockjob.h" /* block.c */ typedef struct BlockDriver BlockDriver; typedef struct BdrvChild BdrvChild; typedef struct BdrvChildClass BdrvChildClass; +typedef enum BlockZoneOp { + BLK_ZO_OPEN, + BLK_ZO_CLOSE, + BLK_ZO_FINISH, + BLK_ZO_RESET, +} BlockZoneOp; + +typedef enum BlockZoneModel { + BLK_Z_NONE = 0x0, /* Regular block device */ + BLK_Z_HM = 0x1, /* Host-managed zoned block device */ + BLK_Z_HA = 0x2, /* Host-aware zoned block device */ +} BlockZoneModel; + +typedef enum BlockZoneState { + BLK_ZS_NOT_WP = 0x0, + BLK_ZS_EMPTY = 0x1, + BLK_ZS_IOPEN = 0x2, + BLK_ZS_EOPEN = 0x3, + BLK_ZS_CLOSED = 0x4, + BLK_ZS_RDONLY = 0xD, + BLK_ZS_FULL = 0xE, + BLK_ZS_OFFLINE = 0xF, +} BlockZoneState; + +typedef enum BlockZoneType { + BLK_ZT_CONV = 0x1, /* Conventional random writes supported */ + BLK_ZT_SWR = 0x2, /* Sequential writes required */ + BLK_ZT_SWP = 0x3, /* Sequential writes preferred */ +} BlockZoneType; + +/* + * Zone descriptor data structure. + * Provides information on a zone with all position and size values in bytes. + */ +typedef struct BlockZoneDescriptor { + uint64_t start; + uint64_t length; + uint64_t cap; + uint64_t wp; + BlockZoneType type; + BlockZoneState state; +} BlockZoneDescriptor; + +/* + * Track write pointers of a zone in bytes. + */ +typedef struct BlockZoneWps { + CoMutex colock; + uint64_t wp[]; +} BlockZoneWps; + typedef struct BlockDriverInfo { /* in bytes, 0 if irrelevant */ int cluster_size; + /* + * A fraction of cluster_size, if supported (currently QCOW2 only); if + * disabled or unsupported, set equal to cluster_size. + */ + int subcluster_size; /* offset at which the VM state can be saved (0 if not possible) */ int64_t vm_state_offset; bool is_dirty; @@ -80,6 +169,15 @@ typedef enum { */ BDRV_REQ_MAY_UNMAP = 0x4, + /* + * An optimization hint when all QEMUIOVector elements are within + * previously registered bdrv_register_buf() memory ranges. + * + * Code that replaces the user's QEMUIOVector elements with bounce buffers + * must take care to clear this flag. + */ + BDRV_REQ_REGISTERED_BUF = 0x8, + BDRV_REQ_FUA = 0x10, BDRV_REQ_WRITE_COMPRESSED = 0x20, @@ -162,6 +260,12 @@ typedef enum { #define BDRV_SECTOR_BITS 9 #define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS) +/* + * Get the first most significant bit of wp. If it is zero, then + * the zone type is SWR. + */ +#define BDRV_ZT_IS_CONV(wp) (wp & (1ULL << 63)) + #define BDRV_REQUEST_MAX_SECTORS MIN_CONST(SIZE_MAX >> BDRV_SECTOR_BITS, \ INT_MAX >> BDRV_SECTOR_BITS) #define BDRV_REQUEST_MAX_BYTES (BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS) @@ -187,6 +291,8 @@ typedef enum { * layer rather than any backing, set by block layer * BDRV_BLOCK_EOF: the returned pnum covers through end of file for this * layer, set by block layer + * BDRV_BLOCK_COMPRESSED: the underlying data is compressed; only valid for + * the formats supporting compression: qcow, qcow2 * * Internal flags: * BDRV_BLOCK_RAW: for use by passthrough drivers, such as raw, to request @@ -222,6 +328,7 @@ typedef enum { #define BDRV_BLOCK_ALLOCATED 0x10 #define BDRV_BLOCK_EOF 0x20 #define BDRV_BLOCK_RECURSE 0x40 +#define BDRV_BLOCK_COMPRESSED 0x80 typedef QTAILQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue; @@ -313,6 +420,45 @@ enum { * * At least one of DATA, METADATA, FILTERED, or COW must be set for * every child. + * + * + * = Connection with bs->children, bs->file and bs->backing fields = + * + * 1. Filters + * + * Filter drivers have drv->is_filter = true. + * + * Filter node has exactly one FILTERED|PRIMARY child, and may have other + * children which must not have these bits (one example is the + * copy-before-write filter, which also has its target DATA child). + * + * Filter nodes never have COW children. + * + * For most filters, the filtered child is linked in bs->file, bs->backing is + * NULL. For some filters (as an exception), it is the other way around; those + * drivers will have drv->filtered_child_is_backing set to true (see that + * field’s documentation for what drivers this concerns) + * + * 2. "raw" driver (block/raw-format.c) + * + * Formally it's not a filter (drv->is_filter = false) + * + * bs->backing is always NULL + * + * Only has one child, linked in bs->file. Its role is either FILTERED|PRIMARY + * (like filter) or DATA|PRIMARY depending on options. + * + * 3. Other drivers + * + * Don't have any FILTERED children. + * + * May have at most one COW child. In this case it's linked in bs->backing. + * Otherwise bs->backing is NULL. COW child is never PRIMARY. + * + * May have at most one PRIMARY child. In this case it's linked in bs->file. + * Otherwise bs->file is NULL. + * + * May also have some other children that don't have the PRIMARY or COW bit set. */ enum BdrvChildRoleBits { /* diff --git a/include/block/block-copy.h b/include/block/block-copy.h index 68bbd344b2..0700953ab8 100644 --- a/include/block/block-copy.h +++ b/include/block/block-copy.h @@ -15,8 +15,8 @@ #ifndef BLOCK_COPY_H #define BLOCK_COPY_H -#include "block/block.h" -#include "qemu/co-shared-resource.h" +#include "block/block-common.h" +#include "qemu/progress_meter.h" /* All APIs are thread-safe */ @@ -36,11 +36,14 @@ void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm); void block_copy_state_free(BlockCopyState *s); void block_copy_reset(BlockCopyState *s, int64_t offset, int64_t bytes); -int64_t block_copy_reset_unallocated(BlockCopyState *s, - int64_t offset, int64_t *count); + +int64_t coroutine_fn GRAPH_RDLOCK +block_copy_reset_unallocated(BlockCopyState *s, int64_t offset, int64_t *count); int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes, - bool ignore_ratelimit); + bool ignore_ratelimit, uint64_t timeout_ns, + BlockCopyAsyncCallbackFunc cb, + void *cb_opaque); /* * Run block-copy in a coroutine, create corresponding BlockCopyCallState diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index 21265e3966..bd7cecd1cf 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -24,16 +24,17 @@ #ifndef BLOCK_GLOBAL_STATE_H #define BLOCK_GLOBAL_STATE_H -#include "block-common.h" +#include "block/block-common.h" +#include "qemu/coroutine.h" +#include "qemu/transactions.h" /* * Global state (GS) API. These functions run under the BQL. * - * If a function modifies the graph, it also uses drain and/or - * aio_context_acquire/release to be sure it has unique access. - * aio_context locking is needed together with BQL because of - * the thread-safe I/O API that concurrently runs and accesses - * the graph without the BQL. + * If a function modifies the graph, it also uses the graph lock to be sure it + * has unique access. The graph lock is needed together with BQL because of the + * thread-safe I/O API that concurrently runs and accesses the graph without + * the BQL. * * It is important to note that not all of these functions are * necessarily limited to running under the BQL, but they would @@ -55,34 +56,67 @@ BlockDriver *bdrv_find_protocol(const char *filename, bool allow_protocol_prefix, Error **errp); BlockDriver *bdrv_find_format(const char *format_name); -int bdrv_create(BlockDriver *drv, const char* filename, - QemuOpts *opts, Error **errp); -int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp); + +int coroutine_fn GRAPH_UNLOCKED +bdrv_co_create(BlockDriver *drv, const char *filename, QemuOpts *opts, + Error **errp); + +int co_wrapper bdrv_create(BlockDriver *drv, const char *filename, + QemuOpts *opts, Error **errp); + +int coroutine_fn GRAPH_UNLOCKED +bdrv_co_create_file(const char *filename, QemuOpts *opts, Error **errp); BlockDriverState *bdrv_new(void); int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, Error **errp); -int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, - Error **errp); + +int GRAPH_WRLOCK +bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, Error **errp); + int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, Error **errp); BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, int flags, Error **errp); int bdrv_drop_filter(BlockDriverState *bs, Error **errp); -BdrvChild *bdrv_open_child(const char *filename, - QDict *options, const char *bdref_key, - BlockDriverState *parent, - const BdrvChildClass *child_class, - BdrvChildRole child_role, - bool allow_none, Error **errp); -BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp); +BdrvChild * no_coroutine_fn +bdrv_open_child(const char *filename, QDict *options, const char *bdref_key, + BlockDriverState *parent, const BdrvChildClass *child_class, + BdrvChildRole child_role, bool allow_none, Error **errp); + +BdrvChild * coroutine_fn no_co_wrapper +bdrv_co_open_child(const char *filename, QDict *options, const char *bdref_key, + BlockDriverState *parent, const BdrvChildClass *child_class, + BdrvChildRole child_role, bool allow_none, Error **errp); + +int bdrv_open_file_child(const char *filename, + QDict *options, const char *bdref_key, + BlockDriverState *parent, Error **errp); + +BlockDriverState * no_coroutine_fn +bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp); + +BlockDriverState * coroutine_fn no_co_wrapper +bdrv_co_open_blockdev_ref(BlockdevRef *ref, Error **errp); + int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, Error **errp); +int GRAPH_WRLOCK +bdrv_set_backing_hd_drained(BlockDriverState *bs, BlockDriverState *backing_hd, + Error **errp); + int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, const char *bdref_key, Error **errp); -BlockDriverState *bdrv_open(const char *filename, const char *reference, - QDict *options, int flags, Error **errp); + +BlockDriverState * no_coroutine_fn +bdrv_open(const char *filename, const char *reference, QDict *options, + int flags, Error **errp); + +BlockDriverState * coroutine_fn no_co_wrapper +bdrv_co_open(const char *filename, const char *reference, + QDict *options, int flags, Error **errp); + BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv, const char *node_name, QDict *options, int flags, @@ -100,23 +134,29 @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, const char *backing_file); -void bdrv_refresh_filename(BlockDriverState *bs); -void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp); +void GRAPH_RDLOCK bdrv_refresh_filename(BlockDriverState *bs); + +void GRAPH_RDLOCK +bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp); + int bdrv_commit(BlockDriverState *bs); -int bdrv_make_empty(BdrvChild *c, Error **errp); -int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, - const char *backing_fmt, bool warn); +int GRAPH_RDLOCK bdrv_make_empty(BdrvChild *c, Error **errp); + void bdrv_register(BlockDriver *bdrv); int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, - const char *backing_file_str); -BlockDriverState *bdrv_find_overlay(BlockDriverState *active, - BlockDriverState *bs); -BlockDriverState *bdrv_find_base(BlockDriverState *bs); -bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, - Error **errp); -int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, - Error **errp); -void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base); + const char *backing_file_str, + bool backing_mask_protocol); + +BlockDriverState * GRAPH_RDLOCK +bdrv_find_overlay(BlockDriverState *active, BlockDriverState *bs); + +BlockDriverState * GRAPH_RDLOCK bdrv_find_base(BlockDriverState *bs); + +int GRAPH_RDLOCK +bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, + Error **errp); +void GRAPH_RDLOCK +bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base); /* * The units of offset and total_work_size may be chosen arbitrarily by the @@ -125,34 +165,45 @@ void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base); */ typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset, int64_t total_work_size, void *opaque); -int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb, void *cb_opaque, - bool force, - Error **errp); +int GRAPH_RDLOCK +bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + bool force, Error **errp); /* check if a named node can be replaced when doing drive-mirror */ -BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, - const char *node_name, Error **errp); +BlockDriverState * GRAPH_RDLOCK +check_to_replace_node(BlockDriverState *parent_bs, const char *node_name, + Error **errp); + +int no_coroutine_fn GRAPH_RDLOCK +bdrv_activate(BlockDriverState *bs, Error **errp); + +int coroutine_fn no_co_wrapper_bdrv_rdlock +bdrv_co_activate(BlockDriverState *bs, Error **errp); -int bdrv_activate(BlockDriverState *bs, Error **errp); void bdrv_activate_all(Error **errp); int bdrv_inactivate_all(void); int bdrv_flush_all(void); void bdrv_close_all(void); void bdrv_drain_all_begin(void); +void bdrv_drain_all_begin_nopoll(void); void bdrv_drain_all_end(void); void bdrv_drain_all(void); +void bdrv_aio_cancel(BlockAIOCB *acb); + int bdrv_has_zero_init_1(BlockDriverState *bs); -int bdrv_has_zero_init(BlockDriverState *bs); +int coroutine_mixed_fn GRAPH_RDLOCK bdrv_has_zero_init(BlockDriverState *bs); BlockDriverState *bdrv_find_node(const char *node_name); BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, Error **errp); -XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp); +XDbgBlockGraph * GRAPH_RDLOCK bdrv_get_xdbg_block_graph(Error **errp); BlockDriverState *bdrv_lookup_bs(const char *device, const char *node_name, Error **errp); -bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base); +bool GRAPH_RDLOCK +bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base); + BlockDriverState *bdrv_next_node(BlockDriverState *bs); BlockDriverState *bdrv_next_all_states(BlockDriverState *bs); @@ -165,15 +216,18 @@ typedef struct BdrvNextIterator { BlockDriverState *bs; } BdrvNextIterator; -BlockDriverState *bdrv_first(BdrvNextIterator *it); -BlockDriverState *bdrv_next(BdrvNextIterator *it); +BlockDriverState * GRAPH_RDLOCK bdrv_first(BdrvNextIterator *it); +BlockDriverState * GRAPH_RDLOCK bdrv_next(BdrvNextIterator *it); void bdrv_next_cleanup(BdrvNextIterator *it); BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs); void bdrv_iterate_format(void (*it)(void *opaque, const char *name), void *opaque, bool read_only); -char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp); -char *bdrv_dirname(BlockDriverState *bs, Error **errp); + +char * GRAPH_RDLOCK +bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp); + +char * GRAPH_RDLOCK bdrv_dirname(BlockDriverState *bs, Error **errp); void bdrv_img_create(const char *filename, const char *fmt, const char *base_filename, const char *base_fmt, @@ -181,16 +235,27 @@ void bdrv_img_create(const char *filename, const char *fmt, bool quiet, Error **errp); void bdrv_ref(BlockDriverState *bs); -void bdrv_unref(BlockDriverState *bs); -void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child); -BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, - BlockDriverState *child_bs, - const char *child_name, - const BdrvChildClass *child_class, - BdrvChildRole child_role, - Error **errp); +void no_coroutine_fn bdrv_unref(BlockDriverState *bs); +void coroutine_fn no_co_wrapper bdrv_co_unref(BlockDriverState *bs); +void GRAPH_WRLOCK bdrv_schedule_unref(BlockDriverState *bs); + +void GRAPH_WRLOCK +bdrv_unref_child(BlockDriverState *parent, BdrvChild *child); + +void coroutine_fn no_co_wrapper_bdrv_wrlock +bdrv_co_unref_child(BlockDriverState *parent, BdrvChild *child); + +BdrvChild * GRAPH_WRLOCK +bdrv_attach_child(BlockDriverState *parent_bs, + BlockDriverState *child_bs, + const char *child_name, + const BdrvChildClass *child_class, + BdrvChildRole child_role, + Error **errp); + +bool GRAPH_RDLOCK +bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp); -bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp); void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason); void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, Error *reason); void bdrv_op_block_all(BlockDriverState *bs, Error *reason); @@ -203,38 +268,20 @@ int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag); int bdrv_debug_resume(BlockDriverState *bs, const char *tag); bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag); -/** - * Locks the AioContext of @bs if it's not the current AioContext. This avoids - * double locking which could lead to deadlocks: This is a coroutine_fn, so we - * know we already own the lock of the current AioContext. - * - * May only be called in the main thread. - */ -void coroutine_fn bdrv_co_lock(BlockDriverState *bs); +bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp); +int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, + BdrvChild *ignore_child, Error **errp); -/** - * Unlocks the AioContext of @bs if it's not the current AioContext. - */ -void coroutine_fn bdrv_co_unlock(BlockDriverState *bs); - -void bdrv_set_aio_context_ignore(BlockDriverState *bs, - AioContext *new_context, GSList **ignore); -int bdrv_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, - Error **errp); -int bdrv_child_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, - BdrvChild *ignore_child, Error **errp); -bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx, - GSList **ignore, Error **errp); -bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx, - GSList **ignore, Error **errp); -AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c); - -int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz); +int GRAPH_RDLOCK bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz); int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo); -void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child, - Error **errp); -void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp); +void GRAPH_WRLOCK +bdrv_add_child(BlockDriverState *parent, BlockDriverState *child, Error **errp); + +void GRAPH_WRLOCK +bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp); /** * @@ -243,9 +290,15 @@ void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp); * Register/unregister a buffer for I/O. For example, VFIO drivers are * interested to know the memory areas that would later be used for I/O, so * that they can prepare IOMMU mapping etc., to get better performance. + * + * Buffers must not overlap and they must be unregistered with the same values that they were registered with. + * + * Returns: true on success, false on failure */ -void bdrv_register_buf(BlockDriverState *bs, void *host, size_t size); -void bdrv_unregister_buf(BlockDriverState *bs, void *host); +bool bdrv_register_buf(BlockDriverState *bs, void *host, size_t size, + Error **errp); +void bdrv_unregister_buf(BlockDriverState *bs, void *host, size_t size); void bdrv_cancel_in_flight(BlockDriverState *bs); diff --git a/include/block/block-hmp-cmds.h b/include/block/block-hmp-cmds.h index 50ce0247c3..71113cd7ef 100644 --- a/include/block/block-hmp-cmds.h +++ b/include/block/block-hmp-cmds.h @@ -15,6 +15,8 @@ #ifndef BLOCK_BLOCK_HMP_CMDS_H #define BLOCK_BLOCK_HMP_CMDS_H +#include "qemu/coroutine.h" + void hmp_drive_add(Monitor *mon, const QDict *qdict); void hmp_commit(Monitor *mon, const QDict *qdict); @@ -38,7 +40,7 @@ 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 coroutine_fn 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); diff --git a/include/block/block-io.h b/include/block/block-io.h index 62c84f0519..b49e0537dd 100644 --- a/include/block/block-io.h +++ b/include/block/block-io.h @@ -24,12 +24,14 @@ #ifndef BLOCK_IO_H #define BLOCK_IO_H -#include "block-common.h" +#include "block/aio-wait.h" +#include "block/block-common.h" +#include "qemu/coroutine.h" +#include "qemu/iov.h" /* * I/O API functions. These functions are thread-safe, and therefore - * can run in any thread as long as the thread has called - * aio_context_acquire/release(). + * can run in any thread. * * These functions can only call functions from I/O and Common categories, * but can be invoked by GS, "I/O or GS" and I/O APIs. @@ -39,92 +41,182 @@ * to catch when they are accidentally called by the wrong API. */ -int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, - int64_t bytes, BdrvRequestFlags flags); +int co_wrapper_mixed_bdrv_rdlock +bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int64_t bytes, + BdrvRequestFlags flags); + int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags); -int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int64_t bytes); -int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, - int64_t bytes); -int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, - const void *buf, int64_t bytes); + +int co_wrapper_mixed_bdrv_rdlock +bdrv_pread(BdrvChild *child, int64_t offset, int64_t bytes, void *buf, + BdrvRequestFlags flags); + +int co_wrapper_mixed_bdrv_rdlock +bdrv_pwrite(BdrvChild *child, int64_t offset,int64_t bytes, + const void *buf, BdrvRequestFlags flags); + +int co_wrapper_mixed_bdrv_rdlock +bdrv_pwrite_sync(BdrvChild *child, int64_t offset, int64_t bytes, + const void *buf, BdrvRequestFlags flags); + +int coroutine_fn GRAPH_RDLOCK +bdrv_co_pwrite_sync(BdrvChild *child, int64_t offset, int64_t bytes, + const void *buf, BdrvRequestFlags flags); + /* * Efficiently zero a region of the disk image. Note that this is a regular * I/O request like read or write and should have a reasonable size. This * function is not suitable for zeroing the entire image in a single request * because it may allocate memory for the entire region. */ -int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, - int64_t bytes, BdrvRequestFlags flags); +int coroutine_fn GRAPH_RDLOCK +bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, int64_t bytes, + BdrvRequestFlags flags); -int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, - PreallocMode prealloc, BdrvRequestFlags flags, - Error **errp); +int coroutine_fn GRAPH_RDLOCK +bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, + PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); + +int64_t coroutine_fn GRAPH_RDLOCK bdrv_co_nb_sectors(BlockDriverState *bs); +int64_t coroutine_mixed_fn bdrv_nb_sectors(BlockDriverState *bs); + +int64_t coroutine_fn GRAPH_RDLOCK bdrv_co_getlength(BlockDriverState *bs); +int64_t co_wrapper_mixed_bdrv_rdlock bdrv_getlength(BlockDriverState *bs); + +int64_t coroutine_fn GRAPH_RDLOCK +bdrv_co_get_allocated_file_size(BlockDriverState *bs); + +int64_t co_wrapper_bdrv_rdlock +bdrv_get_allocated_file_size(BlockDriverState *bs); -int64_t bdrv_nb_sectors(BlockDriverState *bs); -int64_t bdrv_getlength(BlockDriverState *bs); -int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts, BlockDriverState *in_bs, Error **errp); -void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); -int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp); -void coroutine_fn bdrv_co_delete_file_noerr(BlockDriverState *bs); + +int coroutine_fn GRAPH_RDLOCK +bdrv_co_delete_file(BlockDriverState *bs, Error **errp); + +void coroutine_fn GRAPH_RDLOCK +bdrv_co_delete_file_noerr(BlockDriverState *bs); /* async block I/O */ -void bdrv_aio_cancel(BlockAIOCB *acb); void bdrv_aio_cancel_async(BlockAIOCB *acb); /* sg packet commands */ -int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf); +int coroutine_fn GRAPH_RDLOCK +bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf); /* Ensure contents are flushed to disk. */ -int coroutine_fn bdrv_co_flush(BlockDriverState *bs); +int coroutine_fn GRAPH_RDLOCK bdrv_co_flush(BlockDriverState *bs); + +int coroutine_fn GRAPH_RDLOCK bdrv_co_pdiscard(BdrvChild *child, int64_t offset, + int64_t bytes); + +/* Report zone information of zone block device. */ +int coroutine_fn GRAPH_RDLOCK bdrv_co_zone_report(BlockDriverState *bs, + int64_t offset, + unsigned int *nr_zones, + BlockZoneDescriptor *zones); +int coroutine_fn GRAPH_RDLOCK bdrv_co_zone_mgmt(BlockDriverState *bs, + BlockZoneOp op, + int64_t offset, int64_t len); +int coroutine_fn GRAPH_RDLOCK bdrv_co_zone_append(BlockDriverState *bs, + int64_t *offset, + QEMUIOVector *qiov, + BdrvRequestFlags flags); -int bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes); bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs); -int bdrv_block_status(BlockDriverState *bs, int64_t offset, - int64_t bytes, int64_t *pnum, int64_t *map, - BlockDriverState **file); -int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base, - int64_t offset, int64_t bytes, int64_t *pnum, - int64_t *map, BlockDriverState **file); -int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, - int64_t *pnum); -int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, - bool include_base, int64_t offset, int64_t bytes, - int64_t *pnum); -int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, - int64_t bytes); -int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, - bool ignore_allow_rdw, Error **errp); -int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, - Error **errp); +int coroutine_fn GRAPH_RDLOCK +bdrv_co_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, BlockDriverState **file); +int co_wrapper_mixed_bdrv_rdlock +bdrv_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, BlockDriverState **file); + +int coroutine_fn GRAPH_RDLOCK +bdrv_co_block_status_above(BlockDriverState *bs, BlockDriverState *base, + int64_t offset, int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file); +int co_wrapper_mixed_bdrv_rdlock +bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base, + int64_t offset, int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file); + +int coroutine_fn GRAPH_RDLOCK +bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, + int64_t *pnum); +int co_wrapper_mixed_bdrv_rdlock +bdrv_is_allocated(BlockDriverState *bs, int64_t offset, + int64_t bytes, int64_t *pnum); + +int coroutine_fn GRAPH_RDLOCK +bdrv_co_is_allocated_above(BlockDriverState *top, BlockDriverState *base, + bool include_base, int64_t offset, int64_t bytes, + int64_t *pnum); +int co_wrapper_mixed_bdrv_rdlock +bdrv_is_allocated_above(BlockDriverState *bs, BlockDriverState *base, + bool include_base, int64_t offset, + int64_t bytes, int64_t *pnum); + +int coroutine_fn GRAPH_RDLOCK +bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, int64_t bytes); + +int GRAPH_RDLOCK +bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, + Error **errp); + bool bdrv_is_read_only(BlockDriverState *bs); bool bdrv_is_writable(BlockDriverState *bs); bool bdrv_is_sg(BlockDriverState *bs); int bdrv_get_flags(BlockDriverState *bs); -bool bdrv_is_inserted(BlockDriverState *bs); -void bdrv_lock_medium(BlockDriverState *bs, bool locked); -void bdrv_eject(BlockDriverState *bs, bool eject_flag); + +bool coroutine_fn GRAPH_RDLOCK bdrv_co_is_inserted(BlockDriverState *bs); +bool co_wrapper_bdrv_rdlock bdrv_is_inserted(BlockDriverState *bs); + +void coroutine_fn GRAPH_RDLOCK +bdrv_co_lock_medium(BlockDriverState *bs, bool locked); + +void coroutine_fn GRAPH_RDLOCK +bdrv_co_eject(BlockDriverState *bs, bool eject_flag); + const char *bdrv_get_format_name(BlockDriverState *bs); -bool bdrv_supports_compressed_writes(BlockDriverState *bs); +bool GRAPH_RDLOCK bdrv_supports_compressed_writes(BlockDriverState *bs); const char *bdrv_get_node_name(const BlockDriverState *bs); -const char *bdrv_get_device_name(const BlockDriverState *bs); -const char *bdrv_get_device_or_node_name(const BlockDriverState *bs); -int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); -ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, - Error **errp); + +const char * GRAPH_RDLOCK +bdrv_get_device_name(const BlockDriverState *bs); + +const char * GRAPH_RDLOCK +bdrv_get_device_or_node_name(const BlockDriverState *bs); + +int coroutine_fn GRAPH_RDLOCK +bdrv_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); + +int co_wrapper_mixed_bdrv_rdlock +bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); + +ImageInfoSpecific * GRAPH_RDLOCK +bdrv_get_specific_info(BlockDriverState *bs, Error **errp); + BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs); -void bdrv_round_to_clusters(BlockDriverState *bs, - int64_t offset, int64_t bytes, - int64_t *cluster_offset, - int64_t *cluster_bytes); +void bdrv_round_to_subclusters(BlockDriverState *bs, + int64_t offset, int64_t bytes, + int64_t *cluster_offset, + int64_t *cluster_bytes); void bdrv_get_backing_filename(BlockDriverState *bs, char *filename, int filename_size); +int coroutine_fn GRAPH_RDLOCK +bdrv_co_change_backing_file(BlockDriverState *bs, const char *backing_file, + const char *backing_fmt, bool warn); + +int co_wrapper_bdrv_rdlock +bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, + const char *backing_fmt, bool warn); + int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, int64_t pos, int size); @@ -142,12 +234,22 @@ void *qemu_blockalign(BlockDriverState *bs, size_t size); void *qemu_blockalign0(BlockDriverState *bs, size_t size); void *qemu_try_blockalign(BlockDriverState *bs, size_t size); void *qemu_try_blockalign0(BlockDriverState *bs, size_t size); -bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov); void bdrv_enable_copy_on_read(BlockDriverState *bs); void bdrv_disable_copy_on_read(BlockDriverState *bs); -void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event); +void coroutine_fn GRAPH_RDLOCK +bdrv_co_debug_event(BlockDriverState *bs, BlkdebugEvent event); + +void co_wrapper_mixed_bdrv_rdlock +bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event); + +#define BLKDBG_CO_EVENT(child, evt) \ + do { \ + if (child) { \ + bdrv_co_debug_event(child->bs, evt); \ + } \ + } while (0) #define BLKDBG_EVENT(child, evt) \ do { \ @@ -163,6 +265,8 @@ void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event); */ AioContext *bdrv_get_aio_context(BlockDriverState *bs); +AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c); + /** * Move the current coroutine to the AioContext of @bs and return the old * AioContext of the coroutine. Increase bs->in_flight so that draining @bs @@ -180,18 +284,14 @@ AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs); */ void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx); -/** - * Transfer control to @co in the aio context of @bs - */ -void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co); - AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c); -void bdrv_io_plug(BlockDriverState *bs); -void bdrv_io_unplug(BlockDriverState *bs); - -bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, - uint32_t granularity, Error **errp); +bool coroutine_fn GRAPH_RDLOCK +bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, + uint32_t granularity, Error **errp); +bool co_wrapper_bdrv_rdlock +bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, + uint32_t granularity, Error **errp); /** * @@ -222,35 +322,20 @@ bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, * * Returns: 0 if succeeded; negative error code if failed. **/ -int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, - BdrvChild *dst, int64_t dst_offset, - int64_t bytes, BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags); - -/** - * bdrv_drained_end_no_poll: - * - * Same as bdrv_drained_end(), but do not poll for the subgraph to - * actually become unquiesced. Therefore, no graph changes will occur - * with this function. - * - * *drained_end_counter is incremented for every background operation - * that is scheduled, and will be decremented for every operation once - * it settles. The caller must poll until it reaches 0. The counter - * should be accessed using atomic operations only. - */ -void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter); - +int coroutine_fn GRAPH_RDLOCK +bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); /* * "I/O or GS" API functions. These functions can run without * the BQL, but only in one specific iothread/main loop. * - * More specifically, these functions use BDRV_POLL_WHILE(bs), which - * requires the caller to be either in the main thread and hold - * the BlockdriverState (bs) AioContext lock, or directly in the - * home thread that runs the bs AioContext. Calling them from - * another thread in another AioContext would cause deadlocks. + * More specifically, these functions use BDRV_POLL_WHILE(bs), which requires + * the caller to be either in the main thread or directly in the home thread + * that runs the bs AioContext. Calling them from another thread in another + * AioContext would cause deadlocks. * * Therefore, these functions are not proper I/O, because they * can't run in *any* iothreads, but only in a specific one. @@ -270,49 +355,55 @@ void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter); cond); }) void bdrv_drain(BlockDriverState *bs); -void coroutine_fn bdrv_co_drain(BlockDriverState *bs); -int generated_co_wrapper +int co_wrapper_mixed_bdrv_rdlock bdrv_truncate(BdrvChild *child, int64_t offset, bool exact, PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); -int generated_co_wrapper bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, - BdrvCheckMode fix); +int co_wrapper_mixed_bdrv_rdlock +bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); /* Invalidate any cached metadata used by image formats */ -int generated_co_wrapper bdrv_invalidate_cache(BlockDriverState *bs, - Error **errp); -int generated_co_wrapper bdrv_flush(BlockDriverState *bs); -int generated_co_wrapper bdrv_pdiscard(BdrvChild *child, int64_t offset, - int64_t bytes); -int generated_co_wrapper +int co_wrapper_mixed_bdrv_rdlock +bdrv_invalidate_cache(BlockDriverState *bs, Error **errp); + +int co_wrapper_mixed_bdrv_rdlock bdrv_flush(BlockDriverState *bs); + +int co_wrapper_mixed_bdrv_rdlock +bdrv_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes); + +int co_wrapper_mixed_bdrv_rdlock bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); -int generated_co_wrapper + +int co_wrapper_mixed_bdrv_rdlock bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); /** * bdrv_parent_drained_begin_single: * - * Begin a quiesced section for the parent of @c. If @poll is true, wait for - * any pending activity to cease. + * Begin a quiesced section for the parent of @c. */ -void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll); +void GRAPH_RDLOCK bdrv_parent_drained_begin_single(BdrvChild *c); + +/** + * bdrv_parent_drained_poll_single: + * + * Returns true if there is any pending activity to cease before @c can be + * called quiesced, false otherwise. + */ +bool GRAPH_RDLOCK bdrv_parent_drained_poll_single(BdrvChild *c); /** * bdrv_parent_drained_end_single: * * End a quiesced section for the parent of @c. - * - * This polls @bs's AioContext until all scheduled sub-drained_ends - * have settled, which may result in graph changes. */ -void bdrv_parent_drained_end_single(BdrvChild *c); +void GRAPH_RDLOCK bdrv_parent_drained_end_single(BdrvChild *c); /** * bdrv_drain_poll: * - * Poll for pending requests in @bs, its parents (except for @ignore_parent), - * and if @recursive is true its children as well (used for subtree drain). + * Poll for pending requests in @bs and its parents (except for @ignore_parent). * * If @ignore_bds_parents is true, parents that are BlockDriverStates must * ignore the drain request because they will be drained separately (used for @@ -320,8 +411,9 @@ void bdrv_parent_drained_end_single(BdrvChild *c); * * This is part of bdrv_drained_begin. */ -bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, - BdrvChild *ignore_parent, bool ignore_bds_parents); +bool GRAPH_RDLOCK +bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent, + bool ignore_bds_parents); /** * bdrv_drained_begin: @@ -329,6 +421,12 @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, * Begin a quiesced section for exclusive access to the BDS, by disabling * external request sources including NBD server, block jobs, and device model. * + * This function can only be invoked by the main loop or a coroutine + * (regardless of the AioContext where it is running). + * If the coroutine is running in an Iothread AioContext, this function will + * just schedule a BH to run in the main loop. + * However, it cannot be directly called by an Iothread. + * * This function can be recursive. */ void bdrv_drained_begin(BlockDriverState *bs); @@ -339,31 +437,19 @@ void bdrv_drained_begin(BlockDriverState *bs); * Quiesces a BDS like bdrv_drained_begin(), but does not wait for already * running requests to complete. */ -void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, - BdrvChild *parent, bool ignore_bds_parents); - -/** - * Like bdrv_drained_begin, but recursively begins a quiesced section for - * exclusive access to all child nodes as well. - */ -void bdrv_subtree_drained_begin(BlockDriverState *bs); +void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent); /** * bdrv_drained_end: * * End a quiescent section started by bdrv_drained_begin(). * - * This polls @bs's AioContext until all scheduled sub-drained_ends - * have settled. On one hand, that may result in graph changes. On - * the other, this requires that the caller either runs in the main - * loop; or that all involved nodes (@bs and all of its parents) are - * in the caller's AioContext. + * This function can only be invoked by the main loop or a coroutine + * (regardless of the AioContext where it is running). + * If the coroutine is running in an Iothread AioContext, this function will + * just schedule a BH to run in the main loop. + * However, it cannot be directly called by an Iothread. */ void bdrv_drained_end(BlockDriverState *bs); -/** - * End a quiescent section started by bdrv_subtree_drained_begin(). - */ -void bdrv_subtree_drained_end(BlockDriverState *bs); - #endif /* BLOCK_IO_H */ diff --git a/include/block/block.h b/include/block/block.h index 1e6b8fef1e..e2c647de27 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -24,8 +24,8 @@ #ifndef BLOCK_H #define BLOCK_H -#include "block-global-state.h" -#include "block-io.h" +#include "block/block-global-state.h" +#include "block/block-io.h" /* DO NOT ADD ANYTHING IN HERE. USE ONE OF THE HEADERS INCLUDED ABOVE */ diff --git a/include/block/block_backup.h b/include/block/block_backup.h index 157596c296..4d4d5ba153 100644 --- a/include/block/block_backup.h +++ b/include/block/block_backup.h @@ -18,7 +18,7 @@ #ifndef BLOCK_BACKUP_H #define BLOCK_BACKUP_H -#include "block/block_int.h" +#include "block/blockjob.h" void backup_do_checkpoint(BlockJob *job, Error **errp); diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 8947abab76..761276127e 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -24,17 +24,13 @@ #ifndef BLOCK_INT_COMMON_H #define BLOCK_INT_COMMON_H -#include "block/accounting.h" -#include "block/block.h" -#include "block/aio-wait.h" -#include "qemu/queue.h" -#include "qemu/coroutine.h" -#include "qemu/stats64.h" -#include "qemu/timer.h" -#include "qemu/hbitmap.h" +#include "block/aio.h" +#include "block/block-common.h" +#include "block/block-global-state.h" #include "block/snapshot.h" -#include "qemu/throttle.h" +#include "qemu/iov.h" #include "qemu/rcu.h" +#include "qemu/stats64.h" #define BLOCK_FLAG_LAZY_REFCOUNTS 8 @@ -119,6 +115,20 @@ struct BlockDriver { * (And this filtered child must then be bs->file or bs->backing.) */ bool is_filter; + /* + * Only make sense for filter drivers, for others must be false. + * If true, filtered child is bs->backing. Otherwise it's bs->file. + * Two internal filters use bs->backing as filtered child and has this + * field set to true: mirror_top and commit_top. There also two such test + * filters in tests/unit/test-bdrv-graph-mod.c. + * + * Never create any more such filters! + * + * TODO: imagine how to deprecate this behavior and make all filters work + * similarly using bs->file as filtered child. + */ + bool filtered_child_is_backing; + /* * Set to true if the BlockDriver is a format driver. Format nodes * generally do not expect their children to be other format nodes @@ -127,6 +137,11 @@ struct BlockDriver { */ bool is_format; + /* + * Set to true if the BlockDriver supports zoned children. + */ + bool supports_zoned_children; + /* * Drivers not implementing bdrv_parse_filename nor bdrv_open should have * this field set to true, except ones that are defined only by their @@ -148,8 +163,6 @@ struct BlockDriver { */ bool supports_backing; - bool has_variable_length; - /* * Drivers setting this field must be able to work with just a plain * filename with ':' as a prefix, and no other options. @@ -196,20 +209,21 @@ struct BlockDriver { * to allow driver-specific initialization code that requires * the BQL, like setting up specific permission flags. */ - int (*bdrv_amend_pre_run)(BlockDriverState *bs, Error **errp); + int GRAPH_RDLOCK_PTR (*bdrv_amend_pre_run)( + BlockDriverState *bs, Error **errp); /* * This function is invoked under BQL after .bdrv_co_amend() * to allow cleaning up what was done in .bdrv_amend_pre_run(). */ - void (*bdrv_amend_clean)(BlockDriverState *bs); + void GRAPH_RDLOCK_PTR (*bdrv_amend_clean)(BlockDriverState *bs); /* * Return true if @to_replace can be replaced by a BDS with the * same data as @bs without it affecting @bs's behavior (that is, * without it being visible to @bs's parents). */ - bool (*bdrv_recurse_can_replace)(BlockDriverState *bs, - BlockDriverState *to_replace); + bool GRAPH_RDLOCK_PTR (*bdrv_recurse_can_replace)( + BlockDriverState *bs, BlockDriverState *to_replace); int (*bdrv_probe_device)(const char *filename); @@ -221,42 +235,42 @@ struct BlockDriver { Error **errp); /* For handling image reopen for split or non-split files. */ - int (*bdrv_reopen_prepare)(BDRVReopenState *reopen_state, - BlockReopenQueue *queue, Error **errp); - void (*bdrv_reopen_commit)(BDRVReopenState *reopen_state); - void (*bdrv_reopen_commit_post)(BDRVReopenState *reopen_state); - void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state); + int GRAPH_UNLOCKED_PTR (*bdrv_reopen_prepare)( + BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp); + void GRAPH_UNLOCKED_PTR (*bdrv_reopen_commit)( + BDRVReopenState *reopen_state); + void GRAPH_UNLOCKED_PTR (*bdrv_reopen_commit_post)( + BDRVReopenState *reopen_state); + void GRAPH_UNLOCKED_PTR (*bdrv_reopen_abort)( + BDRVReopenState *reopen_state); void (*bdrv_join_options)(QDict *options, QDict *old_options); - int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags, - Error **errp); + int GRAPH_UNLOCKED_PTR (*bdrv_open)( + BlockDriverState *bs, QDict *options, int flags, Error **errp); /* Protocol drivers should implement this instead of bdrv_open */ - int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags, - Error **errp); + int GRAPH_UNLOCKED_PTR (*bdrv_file_open)( + BlockDriverState *bs, QDict *options, int flags, Error **errp); void (*bdrv_close)(BlockDriverState *bs); - int coroutine_fn (*bdrv_co_create)(BlockdevCreateOptions *opts, - Error **errp); - int coroutine_fn (*bdrv_co_create_opts)(BlockDriver *drv, - const char *filename, - QemuOpts *opts, - Error **errp); + int coroutine_fn GRAPH_UNLOCKED_PTR (*bdrv_co_create)( + BlockdevCreateOptions *opts, Error **errp); - int (*bdrv_amend_options)(BlockDriverState *bs, - QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb, - void *cb_opaque, - bool force, - Error **errp); + int coroutine_fn GRAPH_UNLOCKED_PTR (*bdrv_co_create_opts)( + BlockDriver *drv, const char *filename, QemuOpts *opts, Error **errp); - int (*bdrv_make_empty)(BlockDriverState *bs); + int GRAPH_RDLOCK_PTR (*bdrv_amend_options)( + BlockDriverState *bs, QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + bool force, Error **errp); + + int GRAPH_RDLOCK_PTR (*bdrv_make_empty)(BlockDriverState *bs); /* * Refreshes the bs->exact_filename field. If that is impossible, * bs->exact_filename has to be left empty. */ - void (*bdrv_refresh_filename)(BlockDriverState *bs); + void GRAPH_RDLOCK_PTR (*bdrv_refresh_filename)(BlockDriverState *bs); /* * Gathers the open options for all children into @target. @@ -279,15 +293,15 @@ struct BlockDriver { * block driver which implements it is probably doing something * shady regarding its runtime option structure. */ - void (*bdrv_gather_child_options)(BlockDriverState *bs, QDict *target, - bool backing_overridden); + void GRAPH_RDLOCK_PTR (*bdrv_gather_child_options)( + BlockDriverState *bs, QDict *target, bool backing_overridden); /* * Returns an allocated string which is the directory name of this BDS: It * will be used to make relative filenames absolute by prepending this * function's return value to them. */ - char *(*bdrv_dirname)(BlockDriverState *bs, Error **errp); + char * GRAPH_RDLOCK_PTR (*bdrv_dirname)(BlockDriverState *bs, Error **errp); /* * This informs the driver that we are no longer interested in the result @@ -296,27 +310,30 @@ struct BlockDriver { * One example usage is to avoid waiting for an nbd target node reconnect * timeout during job-cancel with force=true. */ - void (*bdrv_cancel_in_flight)(BlockDriverState *bs); + void GRAPH_RDLOCK_PTR (*bdrv_cancel_in_flight)(BlockDriverState *bs); - int (*bdrv_inactivate)(BlockDriverState *bs); + int GRAPH_RDLOCK_PTR (*bdrv_inactivate)(BlockDriverState *bs); - int (*bdrv_snapshot_create)(BlockDriverState *bs, - QEMUSnapshotInfo *sn_info); - int (*bdrv_snapshot_goto)(BlockDriverState *bs, - const char *snapshot_id); - int (*bdrv_snapshot_delete)(BlockDriverState *bs, - const char *snapshot_id, - const char *name, - Error **errp); - int (*bdrv_snapshot_list)(BlockDriverState *bs, - QEMUSnapshotInfo **psn_info); - int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs, - const char *snapshot_id, - const char *name, - Error **errp); + int GRAPH_RDLOCK_PTR (*bdrv_snapshot_create)( + BlockDriverState *bs, QEMUSnapshotInfo *sn_info); - int (*bdrv_change_backing_file)(BlockDriverState *bs, - const char *backing_file, const char *backing_fmt); + int GRAPH_UNLOCKED_PTR (*bdrv_snapshot_goto)( + BlockDriverState *bs, const char *snapshot_id); + + int GRAPH_RDLOCK_PTR (*bdrv_snapshot_delete)( + BlockDriverState *bs, const char *snapshot_id, const char *name, + Error **errp); + + int GRAPH_RDLOCK_PTR (*bdrv_snapshot_list)( + BlockDriverState *bs, QEMUSnapshotInfo **psn_info); + + int GRAPH_RDLOCK_PTR (*bdrv_snapshot_load_tmp)( + BlockDriverState *bs, const char *snapshot_id, const char *name, + Error **errp); + + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_change_backing_file)( + BlockDriverState *bs, const char *backing_file, + const char *backing_fmt); /* TODO Better pass a option string/QDict/QemuOpts to add any rule? */ int (*bdrv_debug_breakpoint)(BlockDriverState *bs, const char *event, @@ -326,13 +343,14 @@ struct BlockDriver { int (*bdrv_debug_resume)(BlockDriverState *bs, const char *tag); bool (*bdrv_debug_is_suspended)(BlockDriverState *bs, const char *tag); - void (*bdrv_refresh_limits)(BlockDriverState *bs, Error **errp); + void GRAPH_RDLOCK_PTR (*bdrv_refresh_limits)( + BlockDriverState *bs, Error **errp); /* * Returns 1 if newly created images are guaranteed to contain only * zeros, 0 otherwise. */ - int (*bdrv_has_zero_init)(BlockDriverState *bs); + int GRAPH_RDLOCK_PTR (*bdrv_has_zero_init)(BlockDriverState *bs); /* * Remove fd handlers, timers, and other event loop callbacks so the event @@ -349,12 +367,28 @@ struct BlockDriver { void (*bdrv_attach_aio_context)(BlockDriverState *bs, AioContext *new_context); + /** + * bdrv_drain_begin is called if implemented in the beginning of a + * drain operation to drain and stop any internal sources of requests in + * the driver. + * bdrv_drain_end is called if implemented at the end of the drain. + * + * They should be used by the driver to e.g. manage scheduled I/O + * requests, or toggle an internal state. After the end of the drain new + * requests will continue normally. + * + * Implementations of both functions must not call aio_poll(). + */ + void (*bdrv_drain_begin)(BlockDriverState *bs); + void (*bdrv_drain_end)(BlockDriverState *bs); + /** * Try to get @bs's logical and physical block size. * On success, store them in @bsz and return zero. * On failure, return negative errno. */ - int (*bdrv_probe_blocksizes)(BlockDriverState *bs, BlockSizes *bsz); + int GRAPH_RDLOCK_PTR (*bdrv_probe_blocksizes)( + BlockDriverState *bs, BlockSizes *bsz); /** * Try to get @bs's geometry (cyls, heads, sectors) * On success, store them in @geo and return 0. @@ -362,12 +396,14 @@ struct BlockDriver { * Only drivers that want to override guest geometry implement this * callback; see hd_geometry_guess(). */ - int (*bdrv_probe_geometry)(BlockDriverState *bs, HDGeometry *geo); + int GRAPH_RDLOCK_PTR (*bdrv_probe_geometry)( + BlockDriverState *bs, HDGeometry *geo); - void (*bdrv_add_child)(BlockDriverState *parent, BlockDriverState *child, - Error **errp); - void (*bdrv_del_child)(BlockDriverState *parent, BdrvChild *child, - Error **errp); + void GRAPH_WRLOCK_PTR (*bdrv_add_child)( + BlockDriverState *parent, BlockDriverState *child, Error **errp); + + void GRAPH_WRLOCK_PTR (*bdrv_del_child)( + BlockDriverState *parent, BdrvChild *child, Error **errp); /** * Informs the block driver that a permission change is intended. The @@ -384,12 +420,12 @@ struct BlockDriver { * If both conditions are met, 0 is returned. Otherwise, -errno is returned * and errp is set to an error describing the conflict. */ - int (*bdrv_check_perm)(BlockDriverState *bs, uint64_t perm, - uint64_t shared, Error **errp); + int GRAPH_RDLOCK_PTR (*bdrv_check_perm)(BlockDriverState *bs, uint64_t perm, + uint64_t shared, Error **errp); /** * Called to inform the driver that the set of cumulative set of used - * permissions for @bs has changed to @perm, and the set of sharable + * permissions for @bs has changed to @perm, and the set of shareable * permission to @shared. The driver can use this to propagate changes to * its children (i.e. request permissions only if a parent actually needs * them). @@ -397,7 +433,8 @@ struct BlockDriver { * This function is only invoked after bdrv_check_perm(), so block drivers * may rely on preparations made in their .bdrv_check_perm implementation. */ - void (*bdrv_set_perm)(BlockDriverState *bs, uint64_t perm, uint64_t shared); + void GRAPH_RDLOCK_PTR (*bdrv_set_perm)( + BlockDriverState *bs, uint64_t perm, uint64_t shared); /* * Called to inform the driver that after a previous bdrv_check_perm() @@ -407,7 +444,7 @@ struct BlockDriver { * This function can be called even for nodes that never saw a * bdrv_check_perm() call. It is a no-op then. */ - void (*bdrv_abort_perm_update)(BlockDriverState *bs); + void GRAPH_RDLOCK_PTR (*bdrv_abort_perm_update)(BlockDriverState *bs); /** * Returns in @nperm and @nshared the permissions that the driver for @bs @@ -421,11 +458,11 @@ struct BlockDriver { * permissions, but those that will be needed after applying the * @reopen_queue. */ - void (*bdrv_child_perm)(BlockDriverState *bs, BdrvChild *c, - BdrvChildRole role, - BlockReopenQueue *reopen_queue, - uint64_t parent_perm, uint64_t parent_shared, - uint64_t *nperm, uint64_t *nshared); + void GRAPH_RDLOCK_PTR (*bdrv_child_perm)( + BlockDriverState *bs, BdrvChild *c, BdrvChildRole role, + BlockReopenQueue *reopen_queue, + uint64_t parent_perm, uint64_t parent_shared, + uint64_t *nperm, uint64_t *nshared); /** * Register/unregister a buffer for I/O. For example, when the driver is @@ -433,9 +470,13 @@ struct BlockDriver { * that it can do IOMMU mapping with VFIO etc., in order to get better * performance. In the case of VFIO drivers, this callback is used to do * DMA mapping for hot buffers. + * + * Returns: true on success, false on failure */ - void (*bdrv_register_buf)(BlockDriverState *bs, void *host, size_t size); - void (*bdrv_unregister_buf)(BlockDriverState *bs, void *host); + bool GRAPH_RDLOCK_PTR (*bdrv_register_buf)( + BlockDriverState *bs, void *host, size_t size, Error **errp); + void GRAPH_RDLOCK_PTR (*bdrv_unregister_buf)( + BlockDriverState *bs, void *host, size_t size); /* * This field is modified only under the BQL, and is part of @@ -452,25 +493,27 @@ struct BlockDriver { int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename); - int coroutine_fn (*bdrv_co_amend)(BlockDriverState *bs, - BlockdevAmendOptions *opts, - bool force, - Error **errp); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_amend)( + BlockDriverState *bs, BlockdevAmendOptions *opts, bool force, + Error **errp); /* aio */ - BlockAIOCB *(*bdrv_aio_preadv)(BlockDriverState *bs, + BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_preadv)(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); - BlockAIOCB *(*bdrv_aio_pwritev)(BlockDriverState *bs, + + BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_pwritev)(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); - BlockAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs, - BlockCompletionFunc *cb, void *opaque); - BlockAIOCB *(*bdrv_aio_pdiscard)(BlockDriverState *bs, - int64_t offset, int bytes, + + BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_flush)( + BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque); + + BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_pdiscard)( + BlockDriverState *bs, int64_t offset, int bytes, BlockCompletionFunc *cb, void *opaque); - int coroutine_fn (*bdrv_co_readv)(BlockDriverState *bs, + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_readv)(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); /** @@ -488,16 +531,16 @@ struct BlockDriver { * * The buffer in @qiov may point directly to guest memory. */ - int coroutine_fn (*bdrv_co_preadv)(BlockDriverState *bs, + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_preadv)(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); - int coroutine_fn (*bdrv_co_preadv_part)(BlockDriverState *bs, - int64_t offset, int64_t bytes, + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_preadv_part)( + BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); - int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs, + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_writev)(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int flags); /** @@ -515,12 +558,12 @@ struct BlockDriver { * * The buffer in @qiov may point directly to guest memory. */ - int coroutine_fn (*bdrv_co_pwritev)(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags); - int coroutine_fn (*bdrv_co_pwritev_part)(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwritev)( + BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwritev_part)( + BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, + size_t qiov_offset, BdrvRequestFlags flags); /* * Efficiently zero a region of the disk image. Typically an image format @@ -528,10 +571,12 @@ struct BlockDriver { * function pointer may be NULL or return -ENOSUP and .bdrv_co_writev() * will be called instead. */ - int coroutine_fn (*bdrv_co_pwrite_zeroes)(BlockDriverState *bs, - int64_t offset, int64_t bytes, BdrvRequestFlags flags); - int coroutine_fn (*bdrv_co_pdiscard)(BlockDriverState *bs, - int64_t offset, int64_t bytes); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwrite_zeroes)( + BlockDriverState *bs, int64_t offset, int64_t bytes, + BdrvRequestFlags flags); + + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pdiscard)( + BlockDriverState *bs, int64_t offset, int64_t bytes); /* * Map [offset, offset + nbytes) range onto a child of @bs to copy from, @@ -541,14 +586,10 @@ struct BlockDriver { * See the comment of bdrv_co_copy_range for the parameter and return value * semantics. */ - int coroutine_fn (*bdrv_co_copy_range_from)(BlockDriverState *bs, - BdrvChild *src, - int64_t offset, - BdrvChild *dst, - int64_t dst_offset, - int64_t bytes, - BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_copy_range_from)( + BlockDriverState *bs, BdrvChild *src, int64_t offset, + BdrvChild *dst, int64_t dst_offset, int64_t bytes, + BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); /* * Map [offset, offset + nbytes) range onto a child of bs to copy data to, @@ -559,14 +600,10 @@ struct BlockDriver { * See the comment of bdrv_co_copy_range for the parameter and return value * semantics. */ - int coroutine_fn (*bdrv_co_copy_range_to)(BlockDriverState *bs, - BdrvChild *src, - int64_t src_offset, - BdrvChild *dst, - int64_t dst_offset, - int64_t bytes, - BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_copy_range_to)( + BlockDriverState *bs, BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, int64_t bytes, + BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); /* * Building block for bdrv_block_status[_above] and @@ -593,7 +630,8 @@ struct BlockDriver { * *pnum value for the block-status cache on protocol nodes, prior * to clamping *pnum for return to its caller. */ - int coroutine_fn (*bdrv_co_block_status)(BlockDriverState *bs, + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_block_status)( + BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file); @@ -613,43 +651,48 @@ struct BlockDriver { * - receive the snapshot's actual length (which may differ from bs's * length) */ - int coroutine_fn (*bdrv_co_preadv_snapshot)(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset); - int coroutine_fn (*bdrv_co_snapshot_block_status)(BlockDriverState *bs, - bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, - int64_t *map, BlockDriverState **file); - int coroutine_fn (*bdrv_co_pdiscard_snapshot)(BlockDriverState *bs, - int64_t offset, int64_t bytes); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_preadv_snapshot)( + BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset); + + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_snapshot_block_status)( + BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, BlockDriverState **file); + + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pdiscard_snapshot)( + BlockDriverState *bs, int64_t offset, int64_t bytes); /* * Invalidate any cached meta-data. */ - void coroutine_fn (*bdrv_co_invalidate_cache)(BlockDriverState *bs, - Error **errp); + void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_invalidate_cache)( + BlockDriverState *bs, Error **errp); /* * Flushes all data for all layers by calling bdrv_co_flush for underlying * layers, if needed. This function is needed for deterministic * synchronization of the flush finishing callback. */ - int coroutine_fn (*bdrv_co_flush)(BlockDriverState *bs); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_flush)(BlockDriverState *bs); /* Delete a created file. */ - int coroutine_fn (*bdrv_co_delete_file)(BlockDriverState *bs, - Error **errp); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_delete_file)( + BlockDriverState *bs, Error **errp); /* * Flushes all data that was already written to the OS all the way down to * the disk (for example file-posix.c calls fsync()). */ - int coroutine_fn (*bdrv_co_flush_to_disk)(BlockDriverState *bs); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_flush_to_disk)( + BlockDriverState *bs); /* * Flushes all internal caches to the OS. The data may still sit in a * writeback cache of the host OS, but it will survive a crash of the qemu * process. */ - int coroutine_fn (*bdrv_co_flush_to_os)(BlockDriverState *bs); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_flush_to_os)( + BlockDriverState *bs); /* * Truncate @bs to @offset bytes using the given @prealloc mode @@ -664,83 +707,86 @@ struct BlockDriver { * If @exact is true and this function fails but would succeed * with @exact = false, it should return -ENOTSUP. */ - int coroutine_fn (*bdrv_co_truncate)(BlockDriverState *bs, int64_t offset, - bool exact, PreallocMode prealloc, - BdrvRequestFlags flags, Error **errp); - int64_t (*bdrv_getlength)(BlockDriverState *bs); - int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_truncate)( + BlockDriverState *bs, int64_t offset, bool exact, + PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); + + int64_t coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_getlength)( + BlockDriverState *bs); + + int64_t coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_get_allocated_file_size)( + BlockDriverState *bs); + BlockMeasureInfo *(*bdrv_measure)(QemuOpts *opts, BlockDriverState *in_bs, Error **errp); - int coroutine_fn (*bdrv_co_pwritev_compressed)(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov); - int coroutine_fn (*bdrv_co_pwritev_compressed_part)(BlockDriverState *bs, - int64_t offset, int64_t bytes, QEMUIOVector *qiov, - size_t qiov_offset); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwritev_compressed)( + BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov); - int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwritev_compressed_part)( + BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset); - ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs, - Error **errp); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_get_info)( + BlockDriverState *bs, BlockDriverInfo *bdi); + + ImageInfoSpecific * GRAPH_RDLOCK_PTR (*bdrv_get_specific_info)( + BlockDriverState *bs, Error **errp); BlockStatsSpecific *(*bdrv_get_specific_stats)(BlockDriverState *bs); - int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs, - QEMUIOVector *qiov, - int64_t pos); - int coroutine_fn (*bdrv_load_vmstate)(BlockDriverState *bs, - QEMUIOVector *qiov, - int64_t pos); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_save_vmstate)( + BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); + + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_load_vmstate)( + BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); + + int coroutine_fn (*bdrv_co_zone_report)(BlockDriverState *bs, + int64_t offset, unsigned int *nr_zones, + BlockZoneDescriptor *zones); + int coroutine_fn (*bdrv_co_zone_mgmt)(BlockDriverState *bs, BlockZoneOp op, + int64_t offset, int64_t len); + int coroutine_fn (*bdrv_co_zone_append)(BlockDriverState *bs, + int64_t *offset, QEMUIOVector *qiov, + BdrvRequestFlags flags); /* removable device specific */ - bool (*bdrv_is_inserted)(BlockDriverState *bs); - void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag); - void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked); + bool coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_is_inserted)( + BlockDriverState *bs); + void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_eject)( + BlockDriverState *bs, bool eject_flag); + void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_lock_medium)( + BlockDriverState *bs, bool locked); /* to control generic scsi devices */ - BlockAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs, - unsigned long int req, void *buf, + BlockAIOCB *coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_aio_ioctl)( + BlockDriverState *bs, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque); - int coroutine_fn (*bdrv_co_ioctl)(BlockDriverState *bs, - unsigned long int req, void *buf); + + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_ioctl)( + BlockDriverState *bs, unsigned long int req, void *buf); /* * Returns 0 for completed check, -errno for internal errors. * The check results are stored in result. */ - int coroutine_fn (*bdrv_co_check)(BlockDriverState *bs, - BdrvCheckResult *result, - BdrvCheckMode fix); + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_check)( + BlockDriverState *bs, BdrvCheckResult *result, BdrvCheckMode fix); - void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event); - - /* io queue for linux-aio */ - void (*bdrv_io_plug)(BlockDriverState *bs); - void (*bdrv_io_unplug)(BlockDriverState *bs); - - /** - * bdrv_co_drain_begin is called if implemented in the beginning of a - * drain operation to drain and stop any internal sources of requests in - * the driver. - * bdrv_co_drain_end is called if implemented at the end of the drain. - * - * They should be used by the driver to e.g. manage scheduled I/O - * requests, or toggle an internal state. After the end of the drain new - * requests will continue normally. - */ - void coroutine_fn (*bdrv_co_drain_begin)(BlockDriverState *bs); - void coroutine_fn (*bdrv_co_drain_end)(BlockDriverState *bs); + void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_debug_event)( + BlockDriverState *bs, BlkdebugEvent event); bool (*bdrv_supports_persistent_dirty_bitmap)(BlockDriverState *bs); - bool (*bdrv_co_can_store_new_dirty_bitmap)(BlockDriverState *bs, - const char *name, - uint32_t granularity, - Error **errp); - int (*bdrv_co_remove_persistent_dirty_bitmap)(BlockDriverState *bs, - const char *name, - Error **errp); + + bool coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_can_store_new_dirty_bitmap)( + BlockDriverState *bs, const char *name, uint32_t granularity, + Error **errp); + + int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_remove_persistent_dirty_bitmap)( + BlockDriverState *bs, const char *name, Error **errp); }; -static inline bool block_driver_can_compress(BlockDriver *drv) +static inline bool TSA_NO_TSA block_driver_can_compress(BlockDriver *drv) { return drv->bdrv_co_pwritev_compressed || drv->bdrv_co_pwritev_compressed_part; @@ -825,6 +871,34 @@ typedef struct BlockLimits { /* maximum number of iovec elements */ int max_iov; + + /* + * true if the length of the underlying file can change, and QEMU + * is expected to adjust automatically. Mostly for CD-ROM drives, + * whose length is zero when the tray is empty (they don't need + * an explicit monitor command to load the disk inside the guest). + */ + bool has_variable_length; + + /* device zone model */ + BlockZoneModel zoned; + + /* zone size expressed in bytes */ + uint32_t zone_size; + + /* total number of zones */ + uint32_t nr_zones; + + /* maximum sectors of a zone append write operation */ + uint32_t max_append_sectors; + + /* maximum number of open zones */ + uint32_t max_open_zones; + + /* maximum number of active zones */ + uint32_t max_active_zones; + + uint32_t write_granularity; } BlockLimits; typedef struct BdrvOpBlocker BdrvOpBlocker; @@ -878,11 +952,32 @@ struct BdrvChildClass { * when migration is completing) and it can start/stop requesting * permissions and doing I/O on it. */ - void (*activate)(BdrvChild *child, Error **errp); - int (*inactivate)(BdrvChild *child); + void GRAPH_RDLOCK_PTR (*activate)(BdrvChild *child, Error **errp); + int GRAPH_RDLOCK_PTR (*inactivate)(BdrvChild *child); - void (*attach)(BdrvChild *child); - void (*detach)(BdrvChild *child); + void GRAPH_WRLOCK_PTR (*attach)(BdrvChild *child); + void GRAPH_WRLOCK_PTR (*detach)(BdrvChild *child); + + /* + * If this pair of functions is implemented, the parent doesn't issue new + * requests after returning from .drained_begin() until .drained_end() is + * called. + * + * These functions must not change the graph (and therefore also must not + * call aio_poll(), which could change the graph indirectly). + * + * Note that this can be nested. If drained_begin() was called twice, new + * I/O is allowed only after drained_end() was called twice, too. + */ + void GRAPH_RDLOCK_PTR (*drained_begin)(BdrvChild *child); + void GRAPH_RDLOCK_PTR (*drained_end)(BdrvChild *child); + + /* + * Returns whether the parent has pending requests for the child. This + * callback is polled after .drained_begin() has been called until all + * activity on the child has stopped. + */ + bool GRAPH_RDLOCK_PTR (*drained_poll)(BdrvChild *child); /* * Notifies the parent that the filename of its child has changed (e.g. @@ -890,13 +985,13 @@ struct BdrvChildClass { * can update its reference. */ int (*update_filename)(BdrvChild *child, BlockDriverState *new_base, - const char *filename, Error **errp); + const char *filename, + bool backing_mask_protocol, + Error **errp); - bool (*can_set_aio_ctx)(BdrvChild *child, AioContext *ctx, - GSList **ignore, Error **errp); - void (*set_aio_ctx)(BdrvChild *child, AioContext *ctx, GSList **ignore); - - AioContext *(*get_parent_aio_context)(BdrvChild *child); + bool (*change_aio_ctx)(BdrvChild *child, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp); /* * I/O API functions. These functions are thread-safe. @@ -914,30 +1009,7 @@ struct BdrvChildClass { */ const char *(*get_name)(BdrvChild *child); - /* - * If this pair of functions is implemented, the parent doesn't issue new - * requests after returning from .drained_begin() until .drained_end() is - * called. - * - * These functions must not change the graph (and therefore also must not - * call aio_poll(), which could change the graph indirectly). - * - * If drained_end() schedules background operations, it must atomically - * increment *drained_end_counter for each such operation and atomically - * decrement it once the operation has settled. - * - * Note that this can be nested. If drained_begin() was called twice, new - * I/O is allowed only after drained_end() was called twice, too. - */ - void (*drained_begin)(BdrvChild *child); - void (*drained_end)(BdrvChild *child, int *drained_end_counter); - - /* - * Returns whether the parent has pending requests for the child. This - * callback is polled after .drained_begin() has been called until all - * activity on the child has stopped. - */ - bool (*drained_poll)(BdrvChild *child); + AioContext *(*get_parent_aio_context)(BdrvChild *child); }; extern const BdrvChildClass child_of_bds; @@ -967,16 +1039,16 @@ struct BdrvChild { bool frozen; /* - * How many times the parent of this child has been drained + * True if the parent of this child has been drained by this BdrvChild * (through klass->drained_*). - * Usually, this is equal to bs->quiesce_counter (potentially - * reduced by bdrv_drain_all_count). It may differ while the + * + * It is generally true if bs->quiesce_counter > 0. It may differ while the * child is entering or leaving a drained section. */ - int parent_quiesce_counter; + bool quiesced_parent; - QLIST_ENTRY(BdrvChild) next; - QLIST_ENTRY(BdrvChild) next_parent; + QLIST_ENTRY(BdrvChild GRAPH_RDLOCK_PTR) next; + QLIST_ENTRY(BdrvChild GRAPH_RDLOCK_PTR) next_parent; }; /* @@ -1042,16 +1114,13 @@ struct BlockDriverState { QDict *full_open_options; char exact_filename[PATH_MAX]; - BdrvChild *backing; - BdrvChild *file; - /* I/O Limits */ BlockLimits bl; /* * Flags honored during pread */ - unsigned int supported_read_flags; + BdrvRequestFlags supported_read_flags; /* * Flags honored during pwrite (so far: BDRV_REQ_FUA, * BDRV_REQ_WRITE_UNCHANGED). @@ -1069,12 +1138,12 @@ struct BlockDriverState { * flag), or they have to explicitly take the WRITE permission for * their children. */ - unsigned int supported_write_flags; + BdrvRequestFlags supported_write_flags; /* * Flags honored during pwrite_zeroes (so far: BDRV_REQ_FUA, * BDRV_REQ_MAY_UNMAP, BDRV_REQ_WRITE_UNCHANGED) */ - unsigned int supported_zero_flags; + BdrvRequestFlags supported_zero_flags; /* * Flags honoured during truncate (so far: BDRV_REQ_ZERO_WRITE). * @@ -1082,7 +1151,7 @@ struct BlockDriverState { * that any added space reads as all zeros. If this can't be guaranteed, * the operation must fail. */ - unsigned int supported_truncate_flags; + BdrvRequestFlags supported_truncate_flags; /* the following member gives a name to every node on the bs graph. */ char node_name[32]; @@ -1103,8 +1172,20 @@ struct BlockDriverState { * parent node of this node. */ BlockDriverState *inherits_from; - QLIST_HEAD(, BdrvChild) children; - QLIST_HEAD(, BdrvChild) parents; + + /* + * @backing and @file are some of @children or NULL. All these three fields + * (@file, @backing and @children) are modified only in + * bdrv_child_cb_attach() and bdrv_child_cb_detach(). + * + * See also comment in include/block/block.h, to learn how backing and file + * are connected with BdrvChildRole. + */ + QLIST_HEAD(, BdrvChild GRAPH_RDLOCK_PTR) children; + BdrvChild * GRAPH_RDLOCK_PTR backing; + BdrvChild * GRAPH_RDLOCK_PTR file; + + QLIST_HEAD(, BdrvChild GRAPH_RDLOCK_PTR) parents; QDict *options; QDict *explicit_options; @@ -1113,8 +1194,6 @@ struct BlockDriverState { /* The error object in use for blocking operations on backing_hd */ Error *backing_blocker; - /* Protected by AioContext lock */ - /* * If we are reading a disk image, give its size in sectors. * Generally read-only; it is written to by load_snapshot and @@ -1151,23 +1230,16 @@ struct BlockDriverState { unsigned int in_flight; unsigned int serialising_in_flight; - /* - * counter for nested bdrv_io_plug. - * Accessed with atomic ops. - */ - unsigned io_plugged; - /* do we need to tell the quest if we have a volatile write cache? */ int enable_write_cache; /* Accessed with atomic ops. */ int quiesce_counter; - int recursive_quiesce_counter; unsigned int write_gen; /* Current data generation */ /* Protected by reqs_lock. */ - CoMutex reqs_lock; + QemuMutex reqs_lock; QLIST_HEAD(, BdrvTrackedRequest) tracked_requests; CoQueue flush_queue; /* Serializing flush queue */ bool active_flush_req; /* Flush request in flight? */ @@ -1182,6 +1254,9 @@ struct BlockDriverState { CoMutex bsc_modify_lock; /* Always non-NULL, but must only be dereferenced under an RCU read guard */ BdrvBlockStatusCache *block_status_cache; + + /* array of write pointers' location of each zone in the zoned device. */ + BlockZoneWps *wps; }; struct BlockBackendRootState { @@ -1220,7 +1295,7 @@ extern QemuOptsList bdrv_create_opts_simple; /* * Common functions that are neither I/O nor Global State. * - * See include/block/block-commmon.h for more information about + * See include/block/block-common.h for more information about * the Common API. */ @@ -1230,7 +1305,7 @@ static inline BlockDriverState *child_bs(BdrvChild *child) } int bdrv_check_request(int64_t offset, int64_t bytes, Error **errp); -int get_tmp_filename(char *filename, int size); +char *create_tmp_file(Error **errp); void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix, QDict *options); diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h index b49f4eb35b..d2201e27f4 100644 --- a/include/block/block_int-global-state.h +++ b/include/block/block_int-global-state.h @@ -25,7 +25,10 @@ #ifndef BLOCK_INT_GLOBAL_STATE_H #define BLOCK_INT_GLOBAL_STATE_H -#include "block_int-common.h" +#include "block/blockjob.h" +#include "block/block_int-common.h" +#include "qemu/hbitmap.h" +#include "qemu/main-loop.h" /* * Global state (GS) API. These functions run under the BQL. @@ -43,6 +46,8 @@ * flatten the whole backing file chain onto @bs. * @backing_file_str: The file name that will be written to @bs as the * the new backing file if the job completes. Ignored if @base is %NULL. + * @backing_mask_protocol: Replace potential protocol name with 'raw' in + * 'backing file format' header * @creation_flags: Flags that control the behavior of the Job lifetime. * See @BlockJobCreateFlags * @speed: The maximum speed, in bytes per second, or 0 for unlimited. @@ -61,6 +66,7 @@ */ void stream_start(const char *job_id, BlockDriverState *bs, BlockDriverState *base, const char *backing_file_str, + bool backing_mask_protocol, BlockDriverState *bottom, int creation_flags, int64_t speed, BlockdevOnError on_error, @@ -79,6 +85,8 @@ void stream_start(const char *job_id, BlockDriverState *bs, * @speed: The maximum speed, in bytes per second, or 0 for unlimited. * @on_error: The action to take upon error. * @backing_file_str: String to use as the backing file in @top's overlay + * @backing_mask_protocol: Replace potential protocol name with 'raw' in + * 'backing file format' header * @filter_node_name: The node name that should be assigned to the filter * driver that the commit job inserts into the graph above @top. NULL means * that a node name should be autogenerated. @@ -89,6 +97,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, BlockDriverState *base, BlockDriverState *top, int creation_flags, int64_t speed, BlockdevOnError on_error, const char *backing_file_str, + bool backing_mask_protocol, const char *filter_node_name, Error **errp); /** * commit_active_start: @@ -193,24 +202,26 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque, JobTxn *txn, Error **errp); -BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, - const char *child_name, - const BdrvChildClass *child_class, - BdrvChildRole child_role, - uint64_t perm, uint64_t shared_perm, - void *opaque, Error **errp); -void bdrv_root_unref_child(BdrvChild *child); +BdrvChild * GRAPH_WRLOCK +bdrv_root_attach_child(BlockDriverState *child_bs, const char *child_name, + const BdrvChildClass *child_class, + BdrvChildRole child_role, + uint64_t perm, uint64_t shared_perm, + void *opaque, Error **errp); -void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, - uint64_t *shared_perm); +void GRAPH_WRLOCK bdrv_root_unref_child(BdrvChild *child); + +void GRAPH_RDLOCK bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, + uint64_t *shared_perm); /** * Sets a BdrvChild's permissions. Avoid if the parent is a BDS; use * bdrv_child_refresh_perms() instead and make the parent's * .bdrv_child_perm() implementation return the correct values. */ -int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, - Error **errp); +int GRAPH_RDLOCK +bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, + Error **errp); /** * Calls bs->drv->bdrv_child_perm() and updates the child's permission @@ -220,10 +231,11 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, * values than before, but which will not result in the block layer * automatically refreshing the permissions. */ -int bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp); +int GRAPH_RDLOCK +bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp); -bool bdrv_recurse_can_replace(BlockDriverState *bs, - BlockDriverState *to_replace); +bool GRAPH_RDLOCK bdrv_recurse_can_replace(BlockDriverState *bs, + BlockDriverState *to_replace); /* * Default implementation for BlockDriver.bdrv_child_perm() that can @@ -271,7 +283,8 @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, Error **errp); -BlockDriverState *bdrv_skip_implicit_filters(BlockDriverState *bs); +BlockDriverState * GRAPH_RDLOCK +bdrv_skip_implicit_filters(BlockDriverState *bs); /** * bdrv_add_aio_context_notifier: @@ -310,21 +323,4 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs, */ void bdrv_drain_all_end_quiesce(BlockDriverState *bs); -/** - * Make sure that the function is running under both drain and BQL. - * The latter protects from concurrent writings - * from the GS API, while the former prevents concurrent reads - * from I/O. - */ -static inline void assert_bdrv_graph_writable(BlockDriverState *bs) -{ - /* - * TODO: this function is incomplete. Because the users of this - * assert lack the necessary drains, check only for BQL. - * Once the necessary drains are added, - * assert also for qatomic_read(&bs->quiesce_counter) > 0 - */ - assert(qemu_in_main_thread()); -} - #endif /* BLOCK_INT_GLOBAL_STATE_H */ diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h index bb454200e5..4a7cf2b4fd 100644 --- a/include/block/block_int-io.h +++ b/include/block/block_int-io.h @@ -24,7 +24,9 @@ #ifndef BLOCK_INT_IO_H #define BLOCK_INT_IO_H -#include "block_int-common.h" +#include "block/block_int-common.h" +#include "qemu/hbitmap.h" +#include "qemu/main-loop.h" /* * I/O API functions. These functions are thread-safe. @@ -33,47 +35,49 @@ * the I/O API. */ -int coroutine_fn bdrv_co_preadv_snapshot(BdrvChild *child, +int coroutine_fn GRAPH_RDLOCK bdrv_co_preadv_snapshot(BdrvChild *child, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset); -int coroutine_fn bdrv_co_snapshot_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, - int64_t *map, BlockDriverState **file); -int coroutine_fn bdrv_co_pdiscard_snapshot(BlockDriverState *bs, +int coroutine_fn GRAPH_RDLOCK bdrv_co_snapshot_block_status( + BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, BlockDriverState **file); +int coroutine_fn GRAPH_RDLOCK bdrv_co_pdiscard_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes); -int coroutine_fn bdrv_co_preadv(BdrvChild *child, +int coroutine_fn GRAPH_RDLOCK bdrv_co_preadv(BdrvChild *child, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); -int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, +int coroutine_fn GRAPH_RDLOCK bdrv_co_preadv_part(BdrvChild *child, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); -int coroutine_fn bdrv_co_pwritev(BdrvChild *child, +int coroutine_fn GRAPH_RDLOCK bdrv_co_pwritev(BdrvChild *child, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); -int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, +int coroutine_fn GRAPH_RDLOCK bdrv_co_pwritev_part(BdrvChild *child, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); -static inline int coroutine_fn bdrv_co_pread(BdrvChild *child, - int64_t offset, unsigned int bytes, void *buf, BdrvRequestFlags flags) +static inline int coroutine_fn GRAPH_RDLOCK bdrv_co_pread(BdrvChild *child, + int64_t offset, int64_t bytes, void *buf, BdrvRequestFlags flags) { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); IO_CODE(); + assert_bdrv_graph_readable(); return bdrv_co_preadv(child, offset, bytes, &qiov, flags); } -static inline int coroutine_fn bdrv_co_pwrite(BdrvChild *child, - int64_t offset, unsigned int bytes, void *buf, BdrvRequestFlags flags) +static inline int coroutine_fn GRAPH_RDLOCK bdrv_co_pwrite(BdrvChild *child, + int64_t offset, int64_t bytes, const void *buf, BdrvRequestFlags flags) { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); IO_CODE(); + assert_bdrv_graph_readable(); return bdrv_co_pwritev(child, offset, bytes, &qiov, flags); } -bool coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, +void coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, uint64_t align); BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs); @@ -95,59 +99,67 @@ BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, */ void bdrv_wakeup(BlockDriverState *bs); -const char *bdrv_get_parent_name(const BlockDriverState *bs); +const char * GRAPH_RDLOCK bdrv_get_parent_name(const BlockDriverState *bs); bool blk_dev_has_tray(BlockBackend *blk); bool blk_dev_is_tray_open(BlockBackend *blk); void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes); void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out); -bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, +void bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, HBitmap **backup, bool lock); void bdrv_inc_in_flight(BlockDriverState *bs); void bdrv_dec_in_flight(BlockDriverState *bs); -int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, - BdrvChild *dst, int64_t dst_offset, - int64_t bytes, - BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags); -int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, - BdrvChild *dst, int64_t dst_offset, - int64_t bytes, - BdrvRequestFlags read_flags, - BdrvRequestFlags write_flags); +int coroutine_fn GRAPH_RDLOCK +bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); +int coroutine_fn GRAPH_RDLOCK +bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); -int refresh_total_sectors(BlockDriverState *bs, int64_t hint); +int coroutine_fn GRAPH_RDLOCK +bdrv_co_refresh_total_sectors(BlockDriverState *bs, int64_t hint); -BdrvChild *bdrv_cow_child(BlockDriverState *bs); -BdrvChild *bdrv_filter_child(BlockDriverState *bs); -BdrvChild *bdrv_filter_or_cow_child(BlockDriverState *bs); -BdrvChild *bdrv_primary_child(BlockDriverState *bs); -BlockDriverState *bdrv_skip_filters(BlockDriverState *bs); -BlockDriverState *bdrv_backing_chain_next(BlockDriverState *bs); +int co_wrapper_mixed_bdrv_rdlock +bdrv_refresh_total_sectors(BlockDriverState *bs, int64_t hint); -static inline BlockDriverState *bdrv_cow_bs(BlockDriverState *bs) +BdrvChild * GRAPH_RDLOCK bdrv_cow_child(BlockDriverState *bs); +BdrvChild * GRAPH_RDLOCK bdrv_filter_child(BlockDriverState *bs); +BdrvChild * GRAPH_RDLOCK bdrv_filter_or_cow_child(BlockDriverState *bs); +BdrvChild * GRAPH_RDLOCK bdrv_primary_child(BlockDriverState *bs); +BlockDriverState * GRAPH_RDLOCK bdrv_skip_filters(BlockDriverState *bs); +BlockDriverState * GRAPH_RDLOCK bdrv_backing_chain_next(BlockDriverState *bs); + +static inline BlockDriverState * GRAPH_RDLOCK +bdrv_cow_bs(BlockDriverState *bs) { IO_CODE(); return child_bs(bdrv_cow_child(bs)); } -static inline BlockDriverState *bdrv_filter_bs(BlockDriverState *bs) +static inline BlockDriverState * GRAPH_RDLOCK +bdrv_filter_bs(BlockDriverState *bs) { IO_CODE(); return child_bs(bdrv_filter_child(bs)); } -static inline BlockDriverState *bdrv_filter_or_cow_bs(BlockDriverState *bs) +static inline BlockDriverState * GRAPH_RDLOCK +bdrv_filter_or_cow_bs(BlockDriverState *bs) { IO_CODE(); return child_bs(bdrv_filter_or_cow_child(bs)); } -static inline BlockDriverState *bdrv_primary_bs(BlockDriverState *bs) +static inline BlockDriverState * GRAPH_RDLOCK +bdrv_primary_bs(BlockDriverState *bs) { IO_CODE(); return child_bs(bdrv_primary_child(bs)); @@ -179,16 +191,4 @@ void bdrv_bsc_invalidate_range(BlockDriverState *bs, */ void bdrv_bsc_fill(BlockDriverState *bs, int64_t offset, int64_t bytes); - -/* - * "I/O or GS" API functions. These functions can run without - * the BQL, but only in one specific iothread/main loop. - * - * See include/block/block-io.h for more information about - * the "I/O or GS" API. - */ - -void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent); -void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent); - #endif /* BLOCK_INT_IO_H */ diff --git a/include/block/block_int.h b/include/block/block_int.h index 7d50b6bbd1..567a178e13 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -24,8 +24,9 @@ #ifndef BLOCK_INT_H #define BLOCK_INT_H -#include "block_int-global-state.h" -#include "block_int-io.h" +#include "block/block_int-global-state.h" +#include "block/block_int-io.h" +#include "block/graph-lock.h" /* DO NOT ADD ANYTHING IN HERE. USE ONE OF THE HEADERS INCLUDED ABOVE */ diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 6525e16fd5..7061ab7201 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -26,8 +26,8 @@ #ifndef BLOCKJOB_H #define BLOCKJOB_H +#include "qapi/qapi-types-block-core.h" #include "qemu/job.h" -#include "block/block.h" #include "qemu/ratelimit.h" #define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */ @@ -40,21 +40,38 @@ typedef struct BlockJobDriver BlockJobDriver; * Long-running operation on a BlockDriverState. */ typedef struct BlockJob { - /** Data belonging to the generic Job infrastructure */ + /** + * Data belonging to the generic Job infrastructure. + * Protected by job mutex. + */ Job job; - /** Status that is published by the query-block-jobs QMP API */ + /** + * Status that is published by the query-block-jobs QMP API. + * Protected by job mutex. + */ BlockDeviceIoStatus iostatus; - /** Speed that was set with @block_job_set_speed. */ + /** + * Speed that was set with @block_job_set_speed. + * Always modified and read under the BQL (GLOBAL_STATE_CODE). + */ int64_t speed; - /** Rate limiting data structure for implementing @speed. */ + /** + * Rate limiting data structure for implementing @speed. + * RateLimit API is thread-safe. + */ RateLimit limit; - /** Block other operations when block job is running */ + /** + * Block other operations when block job is running. + * Always modified and read under the BQL (GLOBAL_STATE_CODE). + */ Error *blocker; + /** All notifiers are set once in block_job_create() and never modified. */ + /** Called when a cancelled job is finalised. */ Notifier finalize_cancelled_notifier; @@ -70,7 +87,10 @@ typedef struct BlockJob { /** Called when the job coroutine yields or terminates */ Notifier idle_notifier; - /** BlockDriverStates that are involved in this block job */ + /** + * BlockDriverStates that are involved in this block job. + * Always modified and read under the BQL (GLOBAL_STATE_CODE). + */ GSList *nodes; } BlockJob; @@ -82,15 +102,16 @@ typedef struct BlockJob { */ /** - * block_job_next: + * block_job_next_locked: * @job: A block job, or %NULL. * * Get the next element from the list of block jobs after @job, or the * first one if @job is %NULL. * * Returns the requested job, or %NULL if there are no more jobs left. + * Called with job lock held. */ -BlockJob *block_job_next(BlockJob *job); +BlockJob *block_job_next_locked(BlockJob *job); /** * block_job_get: @@ -99,9 +120,13 @@ BlockJob *block_job_next(BlockJob *job); * Get the block job identified by @id (which must not be %NULL). * * Returns the requested job, or %NULL if it doesn't exist. + * Called with job lock *not* held. */ BlockJob *block_job_get(const char *id); +/* Same as block_job_get(), but called with job lock held. */ +BlockJob *block_job_get_locked(const char *id); + /** * block_job_add_bdrv: * @job: A block job @@ -113,8 +138,9 @@ BlockJob *block_job_get(const char *id); * @job. This means that all operations will be blocked on @bs while * @job exists. */ -int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, - uint64_t perm, uint64_t shared_perm, Error **errp); +int GRAPH_WRLOCK +block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, + uint64_t perm, uint64_t shared_perm, Error **errp); /** * block_job_remove_all_bdrv: @@ -135,32 +161,49 @@ void block_job_remove_all_bdrv(BlockJob *job); bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs); /** - * block_job_set_speed: + * block_job_set_speed_locked: * @job: The job to set the speed for. * @speed: The new value * @errp: Error object. * * Set a rate-limiting parameter for the job; the actual meaning may * vary depending on the job type. + * + * Called with job lock held, but might release it temporarily. */ -bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); +bool block_job_set_speed_locked(BlockJob *job, int64_t speed, Error **errp); /** - * block_job_query: + * block_job_change_locked: + * @job: The job to change. + * @opts: The new options. + * @errp: Error object. + * + * Change the job according to opts. + */ +void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, + Error **errp); + +/** + * block_job_query_locked: * @job: The job to get information about. * * Return information about a job. + * + * Called with job lock held. */ -BlockJobInfo *block_job_query(BlockJob *job, Error **errp); +BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp); /** - * block_job_iostatus_reset: + * block_job_iostatus_reset_locked: * @job: The job whose I/O status should be reset. * * Reset I/O status on @job and on BlockDriverState objects it uses, * other than job->blk. + * + * Called with job lock held. */ -void block_job_iostatus_reset(BlockJob *job); +void block_job_iostatus_reset_locked(BlockJob *job); /* * block_job_get_aio_context: diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 6bd9ae2b20..4c3d2e25a2 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -27,7 +27,6 @@ #define BLOCKJOB_INT_H #include "block/blockjob.h" -#include "block/block.h" /** * BlockJobDriver: @@ -68,6 +67,18 @@ struct BlockJobDriver { void (*attached_aio_context)(BlockJob *job, AioContext *new_context); void (*set_speed)(BlockJob *job, int64_t speed); + + /* + * Change the @job's options according to @opts. + * + * Note that this can already be called before the job coroutine is running. + */ + void (*change)(BlockJob *job, BlockJobChangeOptions *opts, Error **errp); + + /* + * Query information specific to this kind of block job. + */ + void (*query)(BlockJob *job, BlockJobInfo *info); }; /* @@ -100,10 +111,11 @@ struct BlockJobDriver { * This function is not part of the public job interface; it should be * called from a wrapper that is specific to the job type. */ -void *block_job_create(const char *job_id, const BlockJobDriver *driver, - JobTxn *txn, BlockDriverState *bs, uint64_t perm, - uint64_t shared_perm, int64_t speed, int flags, - BlockCompletionFunc *cb, void *opaque, Error **errp); +void * GRAPH_UNLOCKED +block_job_create(const char *job_id, const BlockJobDriver *driver, + JobTxn *txn, BlockDriverState *bs, uint64_t perm, + uint64_t shared_perm, int64_t speed, int flags, + BlockCompletionFunc *cb, void *opaque, Error **errp); /** * block_job_free: @@ -127,12 +139,18 @@ void block_job_user_resume(Job *job); */ /** - * block_job_ratelimit_get_delay: + * block_job_ratelimit_processed_bytes: * - * Calculate and return delay for the next request in ns. See the documentation - * of ratelimit_calculate_delay() for details. + * To be called after some work has been done. Adjusts the delay for the next + * request. See the documentation of ratelimit_calculate_delay() for details. */ -int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n); +void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n); + +/** + * Put the job to sleep (assuming that it wasn't canceled) to throttle it to the + * right speed according to its rate limiting. + */ +void block_job_ratelimit_sleep(BlockJob *job); /** * block_job_error_action: diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index 6528336c4c..fa956debfb 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -1,6 +1,7 @@ #ifndef BLOCK_DIRTY_BITMAP_H #define BLOCK_DIRTY_BITMAP_H +#include "block/block-common.h" #include "qapi/qapi-types-block-core.h" #include "qemu/hbitmap.h" @@ -34,8 +35,14 @@ int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags, Error **errp); void bdrv_release_dirty_bitmap(BdrvDirtyBitmap *bitmap); void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs); -int bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, - Error **errp); + +int coroutine_fn GRAPH_RDLOCK +bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, + Error **errp); +int co_wrapper_bdrv_rdlock +bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, + Error **errp); + void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap); void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap); void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap); diff --git a/include/block/export.h b/include/block/export.h index 7feb02e10d..f2fe0f8078 100644 --- a/include/block/export.h +++ b/include/block/export.h @@ -57,6 +57,8 @@ struct BlockExport { * Reference count for this block export. This includes strong references * both from the owner (qemu-nbd or the monitor) and clients connected to * the export. + * + * Use atomics to access this field. */ int refcount; diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h new file mode 100644 index 0000000000..d7545e82d0 --- /dev/null +++ b/include/block/graph-lock.h @@ -0,0 +1,278 @@ +/* + * Graph lock: rwlock to protect block layer graph manipulations (add/remove + * edges and nodes) + * + * Copyright (c) 2022 Red Hat + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 GRAPH_LOCK_H +#define GRAPH_LOCK_H + +#include "qemu/clang-tsa.h" + +/** + * Graph Lock API + * This API provides a rwlock used to protect block layer + * graph modifications like edge (BdrvChild) and node (BlockDriverState) + * addition and removal. + * Currently we have 1 writer only, the Main loop, and many + * readers, mostly coroutines running in other AioContext thus other threads. + * + * We distinguish between writer (main loop, under BQL) that modifies the + * graph, and readers (all other coroutines running in various AioContext), + * that go through the graph edges, reading + * BlockDriverState ->parents and->children. + * + * The writer (main loop) has an "exclusive" access, so it first waits for + * current read to finish, and then prevents incoming ones from + * entering while it has the exclusive access. + * + * The readers (coroutines in multiple AioContext) are free to + * access the graph as long the writer is not modifying the graph. + * In case it is, they go in a CoQueue and sleep until the writer + * is done. + * + * If a coroutine changes AioContext, the counter in the original and new + * AioContext are left intact, since the writer does not care where is the + * reader, but only if there is one. + * As a result, some AioContexts might have a negative reader count, to + * balance the positive count of the AioContext that took the lock. + * This also means that when an AioContext is deleted it may have a nonzero + * reader count. In that case we transfer the count to a global shared counter + * so that the writer is always aware of all readers. + */ +typedef struct BdrvGraphRWlock BdrvGraphRWlock; + +/* Dummy lock object to use for Thread Safety Analysis (TSA) */ +typedef struct TSA_CAPABILITY("mutex") BdrvGraphLock { +} BdrvGraphLock; + +extern BdrvGraphLock graph_lock; + +/* + * clang doesn't check consistency in locking annotations between forward + * declarations and the function definition. Having the annotation on the + * definition, but not the declaration in a header file, may give the reader + * a false sense of security because the condition actually remains unchecked + * for callers in other source files. + * + * Therefore, as a convention, for public functions, GRAPH_RDLOCK and + * GRAPH_WRLOCK annotations should be present only in the header file. + */ +#define GRAPH_WRLOCK TSA_REQUIRES(graph_lock) +#define GRAPH_RDLOCK TSA_REQUIRES_SHARED(graph_lock) +#define GRAPH_UNLOCKED TSA_EXCLUDES(graph_lock) + +/* + * TSA annotations are not part of function types, so checks are defeated when + * using a function pointer. As a workaround, annotate function pointers with + * this macro that will require that the lock is at least taken while reading + * the pointer. In most cases this is equivalent to actually protecting the + * function call. + */ +#define GRAPH_RDLOCK_PTR TSA_GUARDED_BY(graph_lock) +#define GRAPH_WRLOCK_PTR TSA_GUARDED_BY(graph_lock) +#define GRAPH_UNLOCKED_PTR + +/* + * register_aiocontext: + * Add AioContext @ctx to the list of AioContext. + * This list is used to obtain the total number of readers + * currently running the graph. + */ +void register_aiocontext(AioContext *ctx); + +/* + * unregister_aiocontext: + * Removes AioContext @ctx to the list of AioContext. + */ +void unregister_aiocontext(AioContext *ctx); + +/* + * bdrv_graph_wrlock: + * Start an exclusive write operation to modify the graph. This means we are + * adding or removing an edge or a node in the block layer graph. Nobody else + * is allowed to access the graph. + * + * Must only be called from outside bdrv_graph_co_rdlock. + * + * The wrlock can only be taken from the main loop, with BQL held, as only the + * main loop is allowed to modify the graph. + */ +void no_coroutine_fn TSA_ACQUIRE(graph_lock) TSA_NO_TSA +bdrv_graph_wrlock(void); + +/* + * bdrv_graph_wrunlock: + * Write finished, reset global has_writer to 0 and restart + * all readers that are waiting. + */ +void no_coroutine_fn TSA_RELEASE(graph_lock) TSA_NO_TSA +bdrv_graph_wrunlock(void); + +/* + * bdrv_graph_co_rdlock: + * Read the bs graph. This usually means traversing all nodes in + * the graph, therefore it can't happen while another thread is + * modifying it. + * Increases the reader counter of the current aiocontext, + * and if has_writer is set, it means that the writer is modifying + * the graph, therefore wait in a coroutine queue. + * The writer will then wake this coroutine once it is done. + * + * This lock should be taken from Iothreads (IO_CODE() class of functions) + * because it signals the writer that there are some + * readers currently running, or waits until the current + * write is finished before continuing. + * Calling this function from the Main Loop with BQL held + * is not necessary, since the Main Loop itself is the only + * writer, thus won't be able to read and write at the same time. + * The only exception to that is when we can't take the lock in the + * function/coroutine itself, and need to delegate the caller (usually main + * loop) to take it and wait that the coroutine ends, so that + * we always signal that a reader is running. + */ +void coroutine_fn TSA_ACQUIRE_SHARED(graph_lock) TSA_NO_TSA +bdrv_graph_co_rdlock(void); + +/* + * bdrv_graph_rdunlock: + * Read terminated, decrease the count of readers in the current aiocontext. + * If the writer is waiting for reads to finish (has_writer == 1), signal + * the writer that we are done via aio_wait_kick() to let it continue. + */ +void coroutine_fn TSA_RELEASE_SHARED(graph_lock) TSA_NO_TSA +bdrv_graph_co_rdunlock(void); + +/* + * bdrv_graph_rd{un}lock_main_loop: + * Just a placeholder to mark where the graph rdlock should be taken + * in the main loop. It is just asserting that we are not + * in a coroutine and in GLOBAL_STATE_CODE. + */ +void TSA_ACQUIRE_SHARED(graph_lock) TSA_NO_TSA +bdrv_graph_rdlock_main_loop(void); + +void TSA_RELEASE_SHARED(graph_lock) TSA_NO_TSA +bdrv_graph_rdunlock_main_loop(void); + +/* + * assert_bdrv_graph_readable: + * Make sure that the reader is either the main loop, + * or there is at least a reader helding the rdlock. + * In this way an incoming writer is aware of the read and waits. + */ +void GRAPH_RDLOCK assert_bdrv_graph_readable(void); + +/* + * assert_bdrv_graph_writable: + * Make sure that the writer is the main loop and has set @has_writer, + * so that incoming readers will pause. + */ +void GRAPH_WRLOCK assert_bdrv_graph_writable(void); + +/* + * Calling this function tells TSA that we know that the lock is effectively + * taken even though we cannot prove it (yet) with GRAPH_RDLOCK. This can be + * useful in intermediate stages of a conversion to using the GRAPH_RDLOCK + * macro. + */ +static inline void TSA_ASSERT_SHARED(graph_lock) TSA_NO_TSA +assume_graph_lock(void) +{ +} + +typedef struct GraphLockable { } GraphLockable; + +/* + * In C, compound literals have the lifetime of an automatic variable. + * In C++ it would be different, but then C++ wouldn't need QemuLockable + * either... + */ +#define GML_OBJ_() (&(GraphLockable) { }) + +/* + * This is not marked as TSA_ACQUIRE_SHARED() because TSA doesn't understand the + * cleanup attribute and would therefore complain that the graph is never + * unlocked. TSA_ASSERT_SHARED() makes sure that the following calls know that + * we hold the lock while unlocking is left unchecked. + */ +static inline GraphLockable * TSA_ASSERT_SHARED(graph_lock) TSA_NO_TSA coroutine_fn +graph_lockable_auto_lock(GraphLockable *x) +{ + bdrv_graph_co_rdlock(); + return x; +} + +static inline void TSA_NO_TSA coroutine_fn +graph_lockable_auto_unlock(GraphLockable *x) +{ + bdrv_graph_co_rdunlock(); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockable, graph_lockable_auto_unlock) + +#define WITH_GRAPH_RDLOCK_GUARD_(var) \ + for (g_autoptr(GraphLockable) var = graph_lockable_auto_lock(GML_OBJ_()); \ + var; \ + graph_lockable_auto_unlock(var), var = NULL) + +#define WITH_GRAPH_RDLOCK_GUARD() \ + WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__)) + +#define GRAPH_RDLOCK_GUARD(x) \ + g_autoptr(GraphLockable) \ + glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \ + graph_lockable_auto_lock(GML_OBJ_()) + + +typedef struct GraphLockableMainloop { } GraphLockableMainloop; + +/* + * In C, compound literals have the lifetime of an automatic variable. + * In C++ it would be different, but then C++ wouldn't need QemuLockable + * either... + */ +#define GMLML_OBJ_() (&(GraphLockableMainloop) { }) + +/* + * This is not marked as TSA_ACQUIRE_SHARED() because TSA doesn't understand the + * cleanup attribute and would therefore complain that the graph is never + * unlocked. TSA_ASSERT_SHARED() makes sure that the following calls know that + * we hold the lock while unlocking is left unchecked. + */ +static inline GraphLockableMainloop * TSA_ASSERT_SHARED(graph_lock) TSA_NO_TSA +graph_lockable_auto_lock_mainloop(GraphLockableMainloop *x) +{ + bdrv_graph_rdlock_main_loop(); + return x; +} + +static inline void TSA_NO_TSA +graph_lockable_auto_unlock_mainloop(GraphLockableMainloop *x) +{ + bdrv_graph_rdunlock_main_loop(); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockableMainloop, + graph_lockable_auto_unlock_mainloop) + +#define GRAPH_RDLOCK_GUARD_MAINLOOP(x) \ + g_autoptr(GraphLockableMainloop) \ + glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \ + graph_lockable_auto_lock_mainloop(GMLML_OBJ_()) + +#endif /* GRAPH_LOCK_H */ + diff --git a/include/block/nbd.h b/include/block/nbd.h index c74b7a9d2e..4e7bd6342f 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2022 Red Hat, Inc. + * Copyright Red Hat * Copyright (C) 2005 Anthony Liguori * * Network Block Device @@ -24,25 +24,29 @@ #include "io/channel-socket.h" #include "crypto/tlscreds.h" #include "qapi/error.h" +#include "qemu/bswap.h" + +typedef struct NBDExport NBDExport; +typedef struct NBDClient NBDClient; +typedef struct NBDClientConnection NBDClientConnection; +typedef struct NBDMetaContexts NBDMetaContexts; extern const BlockExportDriver blk_exp_nbd; /* Handshake phase structs - this struct is passed on the wire */ -struct NBDOption { +typedef struct NBDOption { uint64_t magic; /* NBD_OPTS_MAGIC */ uint32_t option; /* NBD_OPT_* */ uint32_t length; -} QEMU_PACKED; -typedef struct NBDOption NBDOption; +} QEMU_PACKED NBDOption; -struct NBDOptionReply { +typedef struct NBDOptionReply { uint64_t magic; /* NBD_REP_MAGIC */ uint32_t option; /* NBD_OPT_* */ uint32_t type; /* NBD_REP_* */ uint32_t length; -} QEMU_PACKED; -typedef struct NBDOptionReply NBDOptionReply; +} QEMU_PACKED NBDOptionReply; typedef struct NBDOptionReplyMetaContext { NBDOptionReply h; /* h.type = NBD_REP_META_CONTEXT, h.length > 4 */ @@ -50,24 +54,36 @@ typedef struct NBDOptionReplyMetaContext { /* metadata context name follows */ } QEMU_PACKED NBDOptionReplyMetaContext; -/* Transmission phase structs - * - * Note: these are _NOT_ the same as the network representation of an NBD - * request and reply! +/* Track results of negotiation */ +typedef enum NBDMode { + /* Keep this list in a continuum of increasing features. */ + NBD_MODE_OLDSTYLE, /* server lacks newstyle negotiation */ + NBD_MODE_EXPORT_NAME, /* newstyle but only OPT_EXPORT_NAME safe */ + NBD_MODE_SIMPLE, /* newstyle but only simple replies */ + NBD_MODE_STRUCTURED, /* newstyle, structured replies enabled */ + NBD_MODE_EXTENDED, /* newstyle, extended headers enabled */ +} NBDMode; + +/* Transmission phase structs */ + +/* + * Note: NBDRequest is _NOT_ the same as the network representation of an NBD + * request! */ -struct NBDRequest { - uint64_t handle; - uint64_t from; - uint32_t len; +typedef struct NBDRequest { + uint64_t cookie; + uint64_t from; /* Offset touched by the command */ + uint64_t len; /* Effect length; 32 bit limit without extended headers */ uint16_t flags; /* NBD_CMD_FLAG_* */ - uint16_t type; /* NBD_CMD_* */ -}; -typedef struct NBDRequest NBDRequest; + uint16_t type; /* NBD_CMD_* */ + NBDMode mode; /* Determines which network representation to use */ + NBDMetaContexts *contexts; /* Used by NBD_CMD_BLOCK_STATUS */ +} NBDRequest; typedef struct NBDSimpleReply { uint32_t magic; /* NBD_SIMPLE_REPLY_MAGIC */ uint32_t error; - uint64_t handle; + uint64_t cookie; } QEMU_PACKED NBDSimpleReply; /* Header of all structured replies */ @@ -75,57 +91,94 @@ typedef struct NBDStructuredReplyChunk { uint32_t magic; /* NBD_STRUCTURED_REPLY_MAGIC */ uint16_t flags; /* combination of NBD_REPLY_FLAG_* */ uint16_t type; /* NBD_REPLY_TYPE_* */ - uint64_t handle; /* request handle */ + uint64_t cookie; /* request handle */ uint32_t length; /* length of payload */ } QEMU_PACKED NBDStructuredReplyChunk; +typedef struct NBDExtendedReplyChunk { + uint32_t magic; /* NBD_EXTENDED_REPLY_MAGIC */ + uint16_t flags; /* combination of NBD_REPLY_FLAG_* */ + uint16_t type; /* NBD_REPLY_TYPE_* */ + uint64_t cookie; /* request handle */ + uint64_t offset; /* request offset */ + uint64_t length; /* length of payload */ +} QEMU_PACKED NBDExtendedReplyChunk; + typedef union NBDReply { NBDSimpleReply simple; NBDStructuredReplyChunk structured; + NBDExtendedReplyChunk extended; struct { - /* @magic and @handle fields have the same offset and size both in - * simple reply and structured reply chunk, so let them be accessible - * without ".simple." or ".structured." specification + /* + * @magic and @cookie fields have the same offset and size in all + * forms of replies, so let them be accessible without ".simple.", + * ".structured.", or ".extended." specifications. */ uint32_t magic; uint32_t _skip; - uint64_t handle; - } QEMU_PACKED; + uint64_t cookie; + }; } NBDReply; +QEMU_BUILD_BUG_ON(offsetof(NBDReply, simple.cookie) != + offsetof(NBDReply, cookie)); +QEMU_BUILD_BUG_ON(offsetof(NBDReply, structured.cookie) != + offsetof(NBDReply, cookie)); +QEMU_BUILD_BUG_ON(offsetof(NBDReply, extended.cookie) != + offsetof(NBDReply, cookie)); /* Header of chunk for NBD_REPLY_TYPE_OFFSET_DATA */ typedef struct NBDStructuredReadData { - NBDStructuredReplyChunk h; /* h.length >= 9 */ + /* header's .length >= 9 */ uint64_t offset; /* At least one byte of data payload follows, calculated from h.length */ } QEMU_PACKED NBDStructuredReadData; /* Complete chunk for NBD_REPLY_TYPE_OFFSET_HOLE */ typedef struct NBDStructuredReadHole { - NBDStructuredReplyChunk h; /* h.length == 12 */ + /* header's length == 12 */ uint64_t offset; uint32_t length; } QEMU_PACKED NBDStructuredReadHole; /* Header of all NBD_REPLY_TYPE_ERROR* errors */ typedef struct NBDStructuredError { - NBDStructuredReplyChunk h; /* h.length >= 6 */ + /* header's length >= 6 */ uint32_t error; uint16_t message_length; } QEMU_PACKED NBDStructuredError; /* Header of NBD_REPLY_TYPE_BLOCK_STATUS */ typedef struct NBDStructuredMeta { - NBDStructuredReplyChunk h; /* h.length >= 12 (at least one extent) */ + /* header's length >= 12 (at least one extent) */ uint32_t context_id; - /* extents follows */ + /* NBDExtent32 extents[] follows, array length implied by header */ } QEMU_PACKED NBDStructuredMeta; -/* Extent chunk for NBD_REPLY_TYPE_BLOCK_STATUS */ -typedef struct NBDExtent { +/* Extent array element for NBD_REPLY_TYPE_BLOCK_STATUS */ +typedef struct NBDExtent32 { uint32_t length; uint32_t flags; /* NBD_STATE_* */ -} QEMU_PACKED NBDExtent; +} QEMU_PACKED NBDExtent32; + +/* Header of NBD_REPLY_TYPE_BLOCK_STATUS_EXT */ +typedef struct NBDExtendedMeta { + /* header's length >= 24 (at least one extent) */ + uint32_t context_id; + uint32_t count; /* header length must be count * 16 + 8 */ + /* NBDExtent64 extents[count] follows */ +} QEMU_PACKED NBDExtendedMeta; + +/* Extent array element for NBD_REPLY_TYPE_BLOCK_STATUS_EXT */ +typedef struct NBDExtent64 { + uint64_t length; + uint64_t flags; /* NBD_STATE_* */ +} QEMU_PACKED NBDExtent64; + +/* Client payload for limiting NBD_CMD_BLOCK_STATUS reply */ +typedef struct NBDBlockStatusPayload { + uint64_t effect_length; + /* uint32_t ids[] follows, array length implied by header */ +} QEMU_PACKED NBDBlockStatusPayload; /* Transmission (export) flags: sent from server to client during handshake, but describe what will happen during transmission */ @@ -143,20 +196,22 @@ enum { NBD_FLAG_SEND_RESIZE_BIT = 9, /* Send resize */ NBD_FLAG_SEND_CACHE_BIT = 10, /* Send CACHE (prefetch) */ NBD_FLAG_SEND_FAST_ZERO_BIT = 11, /* FAST_ZERO flag for WRITE_ZEROES */ + NBD_FLAG_BLOCK_STAT_PAYLOAD_BIT = 12, /* PAYLOAD flag for BLOCK_STATUS */ }; -#define NBD_FLAG_HAS_FLAGS (1 << NBD_FLAG_HAS_FLAGS_BIT) -#define NBD_FLAG_READ_ONLY (1 << NBD_FLAG_READ_ONLY_BIT) -#define NBD_FLAG_SEND_FLUSH (1 << NBD_FLAG_SEND_FLUSH_BIT) -#define NBD_FLAG_SEND_FUA (1 << NBD_FLAG_SEND_FUA_BIT) -#define NBD_FLAG_ROTATIONAL (1 << NBD_FLAG_ROTATIONAL_BIT) -#define NBD_FLAG_SEND_TRIM (1 << NBD_FLAG_SEND_TRIM_BIT) -#define NBD_FLAG_SEND_WRITE_ZEROES (1 << NBD_FLAG_SEND_WRITE_ZEROES_BIT) -#define NBD_FLAG_SEND_DF (1 << NBD_FLAG_SEND_DF_BIT) -#define NBD_FLAG_CAN_MULTI_CONN (1 << NBD_FLAG_CAN_MULTI_CONN_BIT) -#define NBD_FLAG_SEND_RESIZE (1 << NBD_FLAG_SEND_RESIZE_BIT) -#define NBD_FLAG_SEND_CACHE (1 << NBD_FLAG_SEND_CACHE_BIT) -#define NBD_FLAG_SEND_FAST_ZERO (1 << NBD_FLAG_SEND_FAST_ZERO_BIT) +#define NBD_FLAG_HAS_FLAGS (1 << NBD_FLAG_HAS_FLAGS_BIT) +#define NBD_FLAG_READ_ONLY (1 << NBD_FLAG_READ_ONLY_BIT) +#define NBD_FLAG_SEND_FLUSH (1 << NBD_FLAG_SEND_FLUSH_BIT) +#define NBD_FLAG_SEND_FUA (1 << NBD_FLAG_SEND_FUA_BIT) +#define NBD_FLAG_ROTATIONAL (1 << NBD_FLAG_ROTATIONAL_BIT) +#define NBD_FLAG_SEND_TRIM (1 << NBD_FLAG_SEND_TRIM_BIT) +#define NBD_FLAG_SEND_WRITE_ZEROES (1 << NBD_FLAG_SEND_WRITE_ZEROES_BIT) +#define NBD_FLAG_SEND_DF (1 << NBD_FLAG_SEND_DF_BIT) +#define NBD_FLAG_CAN_MULTI_CONN (1 << NBD_FLAG_CAN_MULTI_CONN_BIT) +#define NBD_FLAG_SEND_RESIZE (1 << NBD_FLAG_SEND_RESIZE_BIT) +#define NBD_FLAG_SEND_CACHE (1 << NBD_FLAG_SEND_CACHE_BIT) +#define NBD_FLAG_SEND_FAST_ZERO (1 << NBD_FLAG_SEND_FAST_ZERO_BIT) +#define NBD_FLAG_BLOCK_STAT_PAYLOAD (1 << NBD_FLAG_BLOCK_STAT_PAYLOAD_BIT) /* New-style handshake (global) flags, sent from server to client, and control what will happen during handshake phase. */ @@ -179,6 +234,7 @@ enum { #define NBD_OPT_STRUCTURED_REPLY (8) #define NBD_OPT_LIST_META_CONTEXT (9) #define NBD_OPT_SET_META_CONTEXT (10) +#define NBD_OPT_EXTENDED_HEADERS (11) /* Option reply types. */ #define NBD_REP_ERR(value) ((UINT32_C(1) << 31) | (value)) @@ -196,6 +252,8 @@ enum { #define NBD_REP_ERR_UNKNOWN NBD_REP_ERR(6) /* Export unknown */ #define NBD_REP_ERR_SHUTDOWN NBD_REP_ERR(7) /* Server shutting down */ #define NBD_REP_ERR_BLOCK_SIZE_REQD NBD_REP_ERR(8) /* Need INFO_BLOCK_SIZE */ +#define NBD_REP_ERR_TOO_BIG NBD_REP_ERR(9) /* Payload size overflow */ +#define NBD_REP_ERR_EXT_HEADER_REQD NBD_REP_ERR(10) /* Need extended headers */ /* Info types, used during NBD_REP_INFO */ #define NBD_INFO_EXPORT 0 @@ -204,12 +262,14 @@ enum { #define NBD_INFO_BLOCK_SIZE 3 /* Request flags, sent from client to server during transmission phase */ -#define NBD_CMD_FLAG_FUA (1 << 0) /* 'force unit access' during write */ -#define NBD_CMD_FLAG_NO_HOLE (1 << 1) /* don't punch hole on zero run */ -#define NBD_CMD_FLAG_DF (1 << 2) /* don't fragment structured read */ -#define NBD_CMD_FLAG_REQ_ONE (1 << 3) /* only one extent in BLOCK_STATUS - * reply chunk */ -#define NBD_CMD_FLAG_FAST_ZERO (1 << 4) /* fail if WRITE_ZEROES is not fast */ +#define NBD_CMD_FLAG_FUA (1 << 0) /* 'force unit access' during write */ +#define NBD_CMD_FLAG_NO_HOLE (1 << 1) /* don't punch hole on zero run */ +#define NBD_CMD_FLAG_DF (1 << 2) /* don't fragment structured read */ +#define NBD_CMD_FLAG_REQ_ONE (1 << 3) \ + /* only one extent in BLOCK_STATUS reply chunk */ +#define NBD_CMD_FLAG_FAST_ZERO (1 << 4) /* fail if WRITE_ZEROES is not fast */ +#define NBD_CMD_FLAG_PAYLOAD_LEN (1 << 5) \ + /* length describes payload, not effect; only with ext header */ /* Supported request types */ enum { @@ -235,22 +295,31 @@ enum { */ #define NBD_MAX_STRING_SIZE 4096 -/* Two types of reply structures */ +/* Two types of request structures, a given client will only use 1 */ +#define NBD_REQUEST_MAGIC 0x25609513 +#define NBD_EXTENDED_REQUEST_MAGIC 0x21e41c71 + +/* + * Three types of reply structures, but what a client expects depends + * on NBD_OPT_STRUCTURED_REPLY and NBD_OPT_EXTENDED_HEADERS. + */ #define NBD_SIMPLE_REPLY_MAGIC 0x67446698 #define NBD_STRUCTURED_REPLY_MAGIC 0x668e33ef +#define NBD_EXTENDED_REPLY_MAGIC 0x6e8a278c -/* Structured reply flags */ +/* Chunk reply flags (for structured and extended replies) */ #define NBD_REPLY_FLAG_DONE (1 << 0) /* This reply-chunk is last */ -/* Structured reply types */ +/* Chunk reply types */ #define NBD_REPLY_ERR(value) ((1 << 15) | (value)) -#define NBD_REPLY_TYPE_NONE 0 -#define NBD_REPLY_TYPE_OFFSET_DATA 1 -#define NBD_REPLY_TYPE_OFFSET_HOLE 2 -#define NBD_REPLY_TYPE_BLOCK_STATUS 5 -#define NBD_REPLY_TYPE_ERROR NBD_REPLY_ERR(1) -#define NBD_REPLY_TYPE_ERROR_OFFSET NBD_REPLY_ERR(2) +#define NBD_REPLY_TYPE_NONE 0 +#define NBD_REPLY_TYPE_OFFSET_DATA 1 +#define NBD_REPLY_TYPE_OFFSET_HOLE 2 +#define NBD_REPLY_TYPE_BLOCK_STATUS 5 +#define NBD_REPLY_TYPE_BLOCK_STATUS_EXT 6 +#define NBD_REPLY_TYPE_ERROR NBD_REPLY_ERR(1) +#define NBD_REPLY_TYPE_ERROR_OFFSET NBD_REPLY_ERR(2) /* Extent flags for base:allocation in NBD_REPLY_TYPE_BLOCK_STATUS */ #define NBD_STATE_HOLE (1 << 0) @@ -281,7 +350,7 @@ static inline bool nbd_reply_type_is_error(int type) #define NBD_ESHUTDOWN 108 /* Details collected by NBD_OPT_EXPORT_NAME and NBD_OPT_GO */ -struct NBDExportInfo { +typedef struct NBDExportInfo { /* Set by client before nbd_receive_negotiate() */ bool request_sizes; char *x_dirty_bitmap; @@ -292,7 +361,7 @@ struct NBDExportInfo { /* In-out fields, set by client before nbd_receive_negotiate() and * updated by server results during nbd_receive_negotiate() */ - bool structured_reply; + NBDMode mode; /* input maximum mode tolerated; output actual mode chosen */ bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STATUS */ /* Set by server results during nbd_receive_negotiate() and @@ -309,11 +378,9 @@ struct NBDExportInfo { char *description; int n_contexts; char **contexts; -}; -typedef struct NBDExportInfo NBDExportInfo; +} NBDExportInfo; -int nbd_receive_negotiate(AioContext *aio_context, QIOChannel *ioc, - QCryptoTLSCreds *tlscreds, +int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, const char *hostname, QIOChannel **outioc, NBDExportInfo *info, Error **errp); void nbd_free_export_list(NBDExportInfo *info, int count); @@ -324,14 +391,12 @@ int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, Error **errp); int nbd_send_request(QIOChannel *ioc, NBDRequest *request); int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc, - NBDReply *reply, Error **errp); + NBDReply *reply, NBDMode mode, + Error **errp); int nbd_client(int fd); int nbd_disconnect(int fd); int nbd_errno_to_system_errno(int err); -typedef struct NBDExport NBDExport; -typedef struct NBDClient NBDClient; - void nbd_export_set_on_eject_blk(BlockExport *exp, BlockBackend *blk); AioContext *nbd_export_aio_context(NBDExport *exp); @@ -406,10 +471,9 @@ const char *nbd_rep_lookup(uint32_t rep); const char *nbd_info_lookup(uint16_t info); const char *nbd_cmd_lookup(uint16_t info); const char *nbd_err_lookup(int err); +const char *nbd_mode_lookup(NBDMode mode); /* nbd/client-connection.c */ -typedef struct NBDClientConnection NBDClientConnection; - void nbd_client_connection_enable_retry(NBDClientConnection *conn); NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr, @@ -424,6 +488,6 @@ QIOChannel *coroutine_fn nbd_co_establish_connection(NBDClientConnection *conn, NBDExportInfo *info, bool blocking, Error **errp); -void coroutine_fn nbd_co_establish_connection_cancel(NBDClientConnection *conn); +void nbd_co_establish_connection_cancel(NBDClientConnection *conn); #endif diff --git a/include/block/nvme.h b/include/block/nvme.h index 3737351cc8..bb231d0b9a 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -1,6 +1,8 @@ #ifndef BLOCK_NVME_H #define BLOCK_NVME_H +#include "hw/registerfields.h" + typedef struct QEMU_PACKED NvmeBar { uint64_t cap; uint32_t vs; @@ -58,6 +60,24 @@ enum NvmeBarRegs { NVME_REG_PMRMSCU = offsetof(NvmeBar, pmrmscu), }; +typedef struct QEMU_PACKED NvmeEndGrpLog { + uint8_t critical_warning; + uint8_t rsvd[2]; + uint8_t avail_spare; + uint8_t avail_spare_thres; + uint8_t percet_used; + uint8_t rsvd1[26]; + uint64_t end_estimate[2]; + uint64_t data_units_read[2]; + uint64_t data_units_written[2]; + uint64_t media_units_written[2]; + uint64_t host_read_commands[2]; + uint64_t host_write_commands[2]; + uint64_t media_integrity_errors[2]; + uint64_t no_err_info_log_entries[2]; + uint8_t rsvd2[352]; +} NvmeEndGrpLog; + enum NvmeCapShift { CAP_MQES_SHIFT = 0, CAP_CQR_SHIFT = 16, @@ -98,28 +118,28 @@ enum NvmeCapMask { #define NVME_CAP_PMRS(cap) (((cap) >> CAP_PMRS_SHIFT) & CAP_PMRS_MASK) #define NVME_CAP_CMBS(cap) (((cap) >> CAP_CMBS_SHIFT) & CAP_CMBS_MASK) -#define NVME_CAP_SET_MQES(cap, val) (cap |= (uint64_t)(val & CAP_MQES_MASK) \ - << CAP_MQES_SHIFT) -#define NVME_CAP_SET_CQR(cap, val) (cap |= (uint64_t)(val & CAP_CQR_MASK) \ - << CAP_CQR_SHIFT) -#define NVME_CAP_SET_AMS(cap, val) (cap |= (uint64_t)(val & CAP_AMS_MASK) \ - << CAP_AMS_SHIFT) -#define NVME_CAP_SET_TO(cap, val) (cap |= (uint64_t)(val & CAP_TO_MASK) \ - << CAP_TO_SHIFT) -#define NVME_CAP_SET_DSTRD(cap, val) (cap |= (uint64_t)(val & CAP_DSTRD_MASK) \ - << CAP_DSTRD_SHIFT) -#define NVME_CAP_SET_NSSRS(cap, val) (cap |= (uint64_t)(val & CAP_NSSRS_MASK) \ - << CAP_NSSRS_SHIFT) -#define NVME_CAP_SET_CSS(cap, val) (cap |= (uint64_t)(val & CAP_CSS_MASK) \ - << CAP_CSS_SHIFT) -#define NVME_CAP_SET_MPSMIN(cap, val) (cap |= (uint64_t)(val & CAP_MPSMIN_MASK)\ - << CAP_MPSMIN_SHIFT) -#define NVME_CAP_SET_MPSMAX(cap, val) (cap |= (uint64_t)(val & CAP_MPSMAX_MASK)\ - << CAP_MPSMAX_SHIFT) -#define NVME_CAP_SET_PMRS(cap, val) (cap |= (uint64_t)(val & CAP_PMRS_MASK) \ - << CAP_PMRS_SHIFT) -#define NVME_CAP_SET_CMBS(cap, val) (cap |= (uint64_t)(val & CAP_CMBS_MASK) \ - << CAP_CMBS_SHIFT) +#define NVME_CAP_SET_MQES(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_MQES_MASK) << CAP_MQES_SHIFT) +#define NVME_CAP_SET_CQR(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_CQR_MASK) << CAP_CQR_SHIFT) +#define NVME_CAP_SET_AMS(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_AMS_MASK) << CAP_AMS_SHIFT) +#define NVME_CAP_SET_TO(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_TO_MASK) << CAP_TO_SHIFT) +#define NVME_CAP_SET_DSTRD(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_DSTRD_MASK) << CAP_DSTRD_SHIFT) +#define NVME_CAP_SET_NSSRS(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_NSSRS_MASK) << CAP_NSSRS_SHIFT) +#define NVME_CAP_SET_CSS(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_CSS_MASK) << CAP_CSS_SHIFT) +#define NVME_CAP_SET_MPSMIN(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_MPSMIN_MASK) << CAP_MPSMIN_SHIFT) +#define NVME_CAP_SET_MPSMAX(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_MPSMAX_MASK) << CAP_MPSMAX_SHIFT) +#define NVME_CAP_SET_PMRS(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_PMRS_MASK) << CAP_PMRS_SHIFT) +#define NVME_CAP_SET_CMBS(cap, val) \ + ((cap) |= (uint64_t)((val) & CAP_CMBS_MASK) << CAP_CMBS_SHIFT) enum NvmeCapCss { NVME_CAP_CSS_NVM = 1 << 0, @@ -595,6 +615,10 @@ enum NvmeAdminCommands { NVME_ADM_CMD_ACTIVATE_FW = 0x10, NVME_ADM_CMD_DOWNLOAD_FW = 0x11, NVME_ADM_CMD_NS_ATTACHMENT = 0x15, + NVME_ADM_CMD_DIRECTIVE_SEND = 0x19, + NVME_ADM_CMD_VIRT_MNGMT = 0x1c, + NVME_ADM_CMD_DIRECTIVE_RECV = 0x1a, + NVME_ADM_CMD_DBBUF_CONFIG = 0x7c, NVME_ADM_CMD_FORMAT_NVM = 0x80, NVME_ADM_CMD_SECURITY_SEND = 0x81, NVME_ADM_CMD_SECURITY_RECV = 0x82, @@ -609,7 +633,9 @@ enum NvmeIoCommands { NVME_CMD_WRITE_ZEROES = 0x08, NVME_CMD_DSM = 0x09, NVME_CMD_VERIFY = 0x0c, + NVME_CMD_IO_MGMT_RECV = 0x12, NVME_CMD_COPY = 0x19, + NVME_CMD_IO_MGMT_SEND = 0x1d, NVME_CMD_ZONE_MGMT_SEND = 0x79, NVME_CMD_ZONE_MGMT_RECV = 0x7a, NVME_CMD_ZONE_APPEND = 0x7d, @@ -702,7 +728,9 @@ typedef struct QEMU_PACKED NvmeRwCmd { uint64_t slba; uint16_t nlb; uint16_t control; - uint32_t dsmgmt; + uint8_t dsmgmt; + uint8_t rsvd; + uint16_t dspec; uint32_t reftag; uint16_t apptag; uint16_t appmask; @@ -873,6 +901,8 @@ enum NvmeStatusCodes { NVME_INVALID_PRP_OFFSET = 0x0013, NVME_CMD_SET_CMB_REJECTED = 0x002b, NVME_INVALID_CMD_SET = 0x002c, + NVME_FDP_DISABLED = 0x0029, + NVME_INVALID_PHID_LIST = 0x002a, NVME_LBA_RANGE = 0x0080, NVME_CAP_EXCEEDED = 0x0081, NVME_NS_NOT_READY = 0x0082, @@ -899,6 +929,10 @@ enum NvmeStatusCodes { NVME_NS_PRIVATE = 0x0119, NVME_NS_NOT_ATTACHED = 0x011a, NVME_NS_CTRL_LIST_INVALID = 0x011c, + NVME_INVALID_CTRL_ID = 0x011f, + NVME_INVALID_SEC_CTRL_STATE = 0x0120, + NVME_INVALID_NUM_RESOURCES = 0x0121, + NVME_INVALID_RESOURCE_ID = 0x0122, NVME_CONFLICTING_ATTRS = 0x0180, NVME_INVALID_PROT_INFO = 0x0181, NVME_WRITE_TO_RO = 0x0182, @@ -999,11 +1033,16 @@ enum { }; enum NvmeLogIdentifier { - NVME_LOG_ERROR_INFO = 0x01, - NVME_LOG_SMART_INFO = 0x02, - NVME_LOG_FW_SLOT_INFO = 0x03, - NVME_LOG_CHANGED_NSLIST = 0x04, - NVME_LOG_CMD_EFFECTS = 0x05, + NVME_LOG_ERROR_INFO = 0x01, + NVME_LOG_SMART_INFO = 0x02, + NVME_LOG_FW_SLOT_INFO = 0x03, + NVME_LOG_CHANGED_NSLIST = 0x04, + NVME_LOG_CMD_EFFECTS = 0x05, + NVME_LOG_ENDGRP = 0x09, + NVME_LOG_FDP_CONFS = 0x20, + NVME_LOG_FDP_RUH_USAGE = 0x21, + NVME_LOG_FDP_STATS = 0x22, + NVME_LOG_FDP_EVENTS = 0x23, }; typedef struct QEMU_PACKED NvmePSD { @@ -1033,6 +1072,8 @@ enum NvmeIdCns { NVME_ID_CNS_NS_PRESENT = 0x11, NVME_ID_CNS_NS_ATTACHED_CTRL_LIST = 0x12, NVME_ID_CNS_CTRL_LIST = 0x13, + NVME_ID_CNS_PRIMARY_CTRL_CAP = 0x14, + NVME_ID_CNS_SECONDARY_CTRL_LIST = 0x15, NVME_ID_CNS_CS_NS_PRESENT_LIST = 0x1a, NVME_ID_CNS_CS_NS_PRESENT = 0x1b, NVME_ID_CNS_IO_COMMAND_SET = 0x1c, @@ -1083,7 +1124,10 @@ typedef struct QEMU_PACKED NvmeIdCtrl { uint16_t mntmt; uint16_t mxtmt; uint32_t sanicap; - uint8_t rsvd332[180]; + uint8_t rsvd332[6]; + uint16_t nsetidmax; + uint16_t endgidmax; + uint8_t rsvd342[170]; uint8_t sqes; uint8_t cqes; uint16_t maxcmd; @@ -1126,14 +1170,18 @@ enum NvmeIdCtrlOaes { }; enum NvmeIdCtrlCtratt { + NVME_CTRATT_ENDGRPS = 1 << 4, NVME_CTRATT_ELBAS = 1 << 15, + NVME_CTRATT_FDPS = 1 << 19, }; enum NvmeIdCtrlOacs { - NVME_OACS_SECURITY = 1 << 0, - NVME_OACS_FORMAT = 1 << 1, - NVME_OACS_FW = 1 << 2, - NVME_OACS_NS_MGMT = 1 << 3, + NVME_OACS_SECURITY = 1 << 0, + NVME_OACS_FORMAT = 1 << 1, + NVME_OACS_FW = 1 << 2, + NVME_OACS_NS_MGMT = 1 << 3, + NVME_OACS_DIRECTIVES = 1 << 5, + NVME_OACS_DBBUF = 1 << 8, }; enum NvmeIdCtrlOncs { @@ -1218,6 +1266,7 @@ enum NvmeNsAttachmentOperation { #define NVME_AEC_SMART(aec) (aec & 0xff) #define NVME_AEC_NS_ATTR(aec) ((aec >> 8) & 0x1) #define NVME_AEC_FW_ACTIVATION(aec) ((aec >> 9) & 0x1) +#define NVME_AEC_ENDGRP_NOTICE(aec) ((aec >> 14) & 0x1) #define NVME_ERR_REC_TLER(err_rec) (err_rec & 0xffff) #define NVME_ERR_REC_DULBE(err_rec) (err_rec & 0x10000) @@ -1237,6 +1286,8 @@ enum NvmeFeatureIds { NVME_TIMESTAMP = 0xe, NVME_HOST_BEHAVIOR_SUPPORT = 0x16, NVME_COMMAND_SET_PROFILE = 0x19, + NVME_FDP_MODE = 0x1d, + NVME_FDP_EVENTS = 0x1e, NVME_SOFTWARE_PROGRESS_MARKER = 0x80, NVME_FID_MAX = 0x100, }; @@ -1329,7 +1380,10 @@ typedef struct QEMU_PACKED NvmeIdNs { uint16_t mssrl; uint32_t mcl; uint8_t msrc; - uint8_t rsvd81[23]; + uint8_t rsvd81[18]; + uint8_t nsattr; + uint16_t nvmsetid; + uint16_t endgid; uint8_t nguid[16]; uint64_t eui64; NvmeLBAF lbaf[NVME_MAX_NLBAF]; @@ -1553,6 +1607,224 @@ typedef enum NvmeZoneState { NVME_ZONE_STATE_OFFLINE = 0x0f, } NvmeZoneState; +typedef struct QEMU_PACKED NvmePriCtrlCap { + uint16_t cntlid; + uint16_t portid; + uint8_t crt; + uint8_t rsvd5[27]; + uint32_t vqfrt; + uint32_t vqrfa; + uint16_t vqrfap; + uint16_t vqprt; + uint16_t vqfrsm; + uint16_t vqgran; + uint8_t rsvd48[16]; + uint32_t vifrt; + uint32_t virfa; + uint16_t virfap; + uint16_t viprt; + uint16_t vifrsm; + uint16_t vigran; + uint8_t rsvd80[4016]; +} NvmePriCtrlCap; + +typedef enum NvmePriCtrlCapCrt { + NVME_CRT_VQ = 1 << 0, + NVME_CRT_VI = 1 << 1, +} NvmePriCtrlCapCrt; + +typedef struct QEMU_PACKED NvmeSecCtrlEntry { + uint16_t scid; + uint16_t pcid; + uint8_t scs; + uint8_t rsvd5[3]; + uint16_t vfn; + uint16_t nvq; + uint16_t nvi; + uint8_t rsvd14[18]; +} NvmeSecCtrlEntry; + +typedef struct QEMU_PACKED NvmeSecCtrlList { + uint8_t numcntl; + uint8_t rsvd1[31]; + NvmeSecCtrlEntry sec[127]; +} NvmeSecCtrlList; + +typedef enum NvmeVirtMngmtAction { + NVME_VIRT_MNGMT_ACTION_PRM_ALLOC = 0x01, + NVME_VIRT_MNGMT_ACTION_SEC_OFFLINE = 0x07, + NVME_VIRT_MNGMT_ACTION_SEC_ASSIGN = 0x08, + NVME_VIRT_MNGMT_ACTION_SEC_ONLINE = 0x09, +} NvmeVirtMngmtAction; + +typedef enum NvmeVirtualResourceType { + NVME_VIRT_RES_QUEUE = 0x00, + NVME_VIRT_RES_INTERRUPT = 0x01, +} NvmeVirtualResourceType; + +typedef struct NvmeDirectiveIdentify { + uint8_t supported; + uint8_t unused1[31]; + uint8_t enabled; + uint8_t unused33[31]; + uint8_t persistent; + uint8_t unused65[31]; + uint8_t rsvd64[4000]; +} NvmeDirectiveIdentify; + +enum NvmeDirectiveTypes { + NVME_DIRECTIVE_IDENTIFY = 0x0, + NVME_DIRECTIVE_DATA_PLACEMENT = 0x2, +}; + +enum NvmeDirectiveOperations { + NVME_DIRECTIVE_RETURN_PARAMS = 0x1, +}; + +typedef struct QEMU_PACKED NvmeFdpConfsHdr { + uint16_t num_confs; + uint8_t version; + uint8_t rsvd3; + uint32_t size; + uint8_t rsvd8[8]; +} NvmeFdpConfsHdr; + +REG8(FDPA, 0x0) + FIELD(FDPA, RGIF, 0, 4) + FIELD(FDPA, VWC, 4, 1) + FIELD(FDPA, VALID, 7, 1); + +typedef struct QEMU_PACKED NvmeFdpDescrHdr { + uint16_t descr_size; + uint8_t fdpa; + uint8_t vss; + uint32_t nrg; + uint16_t nruh; + uint16_t maxpids; + uint32_t nnss; + uint64_t runs; + uint32_t erutl; + uint8_t rsvd28[36]; +} NvmeFdpDescrHdr; + +enum NvmeRuhType { + NVME_RUHT_INITIALLY_ISOLATED = 1, + NVME_RUHT_PERSISTENTLY_ISOLATED = 2, +}; + +typedef struct QEMU_PACKED NvmeRuhDescr { + uint8_t ruht; + uint8_t rsvd1[3]; +} NvmeRuhDescr; + +typedef struct QEMU_PACKED NvmeRuhuLog { + uint16_t nruh; + uint8_t rsvd2[6]; +} NvmeRuhuLog; + +enum NvmeRuhAttributes { + NVME_RUHA_UNUSED = 0, + NVME_RUHA_HOST = 1, + NVME_RUHA_CTRL = 2, +}; + +typedef struct QEMU_PACKED NvmeRuhuDescr { + uint8_t ruha; + uint8_t rsvd1[7]; +} NvmeRuhuDescr; + +typedef struct QEMU_PACKED NvmeFdpStatsLog { + uint64_t hbmw[2]; + uint64_t mbmw[2]; + uint64_t mbe[2]; + uint8_t rsvd48[16]; +} NvmeFdpStatsLog; + +typedef struct QEMU_PACKED NvmeFdpEventsLog { + uint32_t num_events; + uint8_t rsvd4[60]; +} NvmeFdpEventsLog; + +enum NvmeFdpEventType { + FDP_EVT_RU_NOT_FULLY_WRITTEN = 0x0, + FDP_EVT_RU_ATL_EXCEEDED = 0x1, + FDP_EVT_CTRL_RESET_RUH = 0x2, + FDP_EVT_INVALID_PID = 0x3, + FDP_EVT_MEDIA_REALLOC = 0x80, + FDP_EVT_RUH_IMPLICIT_RU_CHANGE = 0x81, +}; + +enum NvmeFdpEventFlags { + FDPEF_PIV = 1 << 0, + FDPEF_NSIDV = 1 << 1, + FDPEF_LV = 1 << 2, +}; + +typedef struct QEMU_PACKED NvmeFdpEvent { + uint8_t type; + uint8_t flags; + uint16_t pid; + uint64_t timestamp; + uint32_t nsid; + uint64_t type_specific[2]; + uint16_t rgid; + uint8_t ruhid; + uint8_t rsvd35[5]; + uint64_t vendor[3]; +} NvmeFdpEvent; + +typedef struct QEMU_PACKED NvmePhidList { + uint16_t nnruhd; + uint8_t rsvd2[6]; +} NvmePhidList; + +typedef struct QEMU_PACKED NvmePhidDescr { + uint8_t ruht; + uint8_t rsvd1; + uint16_t ruhid; +} NvmePhidDescr; + +REG32(FEAT_FDP, 0x0) + FIELD(FEAT_FDP, FDPE, 0, 1) + FIELD(FEAT_FDP, CONF_NDX, 8, 8); + +typedef struct QEMU_PACKED NvmeFdpEventDescr { + uint8_t evt; + uint8_t evta; +} NvmeFdpEventDescr; + +REG32(NVME_IOMR, 0x0) + FIELD(NVME_IOMR, MO, 0, 8) + FIELD(NVME_IOMR, MOS, 16, 16); + +enum NvmeIomr2Mo { + NVME_IOMR_MO_NOP = 0x0, + NVME_IOMR_MO_RUH_STATUS = 0x1, + NVME_IOMR_MO_VENDOR_SPECIFIC = 0x255, +}; + +typedef struct QEMU_PACKED NvmeRuhStatus { + uint8_t rsvd0[14]; + uint16_t nruhsd; +} NvmeRuhStatus; + +typedef struct QEMU_PACKED NvmeRuhStatusDescr { + uint16_t pid; + uint16_t ruhid; + uint32_t earutr; + uint64_t ruamw; + uint8_t rsvd16[16]; +} NvmeRuhStatusDescr; + +REG32(NVME_IOMS, 0x0) + FIELD(NVME_IOMS, MO, 0, 8) + FIELD(NVME_IOMS, MOS, 16, 16); + +enum NvmeIoms2Mo { + NVME_IOMS_MO_NOP = 0x0, + NVME_IOMS_MO_RUH_UPDATE = 0x1, +}; + static inline void _nvme_check_size(void) { QEMU_BUILD_BUG_ON(sizeof(NvmeBar) != 4096); @@ -1588,5 +1860,10 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeIdNsDescr) != 4); QEMU_BUILD_BUG_ON(sizeof(NvmeZoneDescr) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeDifTuple) != 16); + QEMU_BUILD_BUG_ON(sizeof(NvmePriCtrlCap) != 4096); + QEMU_BUILD_BUG_ON(sizeof(NvmeSecCtrlEntry) != 32); + QEMU_BUILD_BUG_ON(sizeof(NvmeSecCtrlList) != 4096); + QEMU_BUILD_BUG_ON(sizeof(NvmeEndGrpLog) != 512); + QEMU_BUILD_BUG_ON(sizeof(NvmeDirectiveIdentify) != 4096); } #endif diff --git a/include/block/qapi.h b/include/block/qapi.h index 22c7807c89..54c48de26a 100644 --- a/include/block/qapi.h +++ b/include/block/qapi.h @@ -25,21 +25,28 @@ #ifndef BLOCK_QAPI_H #define BLOCK_QAPI_H -#include "block/block.h" +#include "block/graph-lock.h" #include "block/snapshot.h" +#include "qapi/qapi-types-block-core.h" -BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, - BlockDriverState *bs, - bool flat, - Error **errp); -int bdrv_query_snapshot_info_list(BlockDriverState *bs, - SnapshotInfoList **p_list, - Error **errp); -void bdrv_query_image_info(BlockDriverState *bs, - ImageInfo **p_info, - Error **errp); +BlockDeviceInfo * GRAPH_RDLOCK +bdrv_block_device_info(BlockBackend *blk, BlockDriverState *bs, + bool flat, Error **errp); + +int GRAPH_RDLOCK +bdrv_query_snapshot_info_list(BlockDriverState *bs, + SnapshotInfoList **p_list, + Error **errp); +void GRAPH_RDLOCK +bdrv_query_image_info(BlockDriverState *bs, ImageInfo **p_info, bool flat, + bool skip_implicit_filters, Error **errp); +void GRAPH_RDLOCK +bdrv_query_block_graph_info(BlockDriverState *bs, BlockGraphInfo **p_info, + Error **errp); void bdrv_snapshot_dump(QEMUSnapshotInfo *sn); -void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec); -void bdrv_image_info_dump(ImageInfo *info); +void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec, + const char *prefix, + int indentation); +void bdrv_node_info_dump(BlockNodeInfo *info, int indentation, bool protocol); #endif diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h index 21fc10c4c9..20e000b8ef 100644 --- a/include/block/raw-aio.h +++ b/include/block/raw-aio.h @@ -17,7 +17,6 @@ #define QEMU_RAW_AIO_H #include "block/aio.h" -#include "qemu/coroutine.h" #include "qemu/iov.h" /* AIO request types */ @@ -29,6 +28,9 @@ #define QEMU_AIO_WRITE_ZEROES 0x0020 #define QEMU_AIO_COPY_RANGE 0x0040 #define QEMU_AIO_TRUNCATE 0x0080 +#define QEMU_AIO_ZONE_REPORT 0x0100 +#define QEMU_AIO_ZONE_MGMT 0x0200 +#define QEMU_AIO_ZONE_APPEND 0x0400 #define QEMU_AIO_TYPE_MASK \ (QEMU_AIO_READ | \ QEMU_AIO_WRITE | \ @@ -37,7 +39,10 @@ QEMU_AIO_DISCARD | \ QEMU_AIO_WRITE_ZEROES | \ QEMU_AIO_COPY_RANGE | \ - QEMU_AIO_TRUNCATE) + QEMU_AIO_TRUNCATE | \ + QEMU_AIO_ZONE_REPORT | \ + QEMU_AIO_ZONE_MGMT | \ + QEMU_AIO_ZONE_APPEND) /* AIO flags */ #define QEMU_AIO_MISALIGNED 0x1000 @@ -50,26 +55,24 @@ typedef struct LinuxAioState LinuxAioState; LinuxAioState *laio_init(Error **errp); void laio_cleanup(LinuxAioState *s); -int coroutine_fn laio_co_submit(BlockDriverState *bs, LinuxAioState *s, int fd, - uint64_t offset, QEMUIOVector *qiov, int type, - uint64_t dev_max_batch); + +/* laio_co_submit: submit I/O requests in the thread's current AioContext. */ +int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, + int type, uint64_t dev_max_batch); + void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context); 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, - uint64_t dev_max_batch); #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); + +/* luring_co_submit: submit I/O requests in the thread's current AioContext. */ +int coroutine_fn luring_co_submit(BlockDriverState *bs, 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 diff --git a/include/block/snapshot.h b/include/block/snapshot.h index 50ff924710..304cc6ea61 100644 --- a/include/block/snapshot.h +++ b/include/block/snapshot.h @@ -25,6 +25,7 @@ #ifndef SNAPSHOT_H #define SNAPSHOT_H +#include "block/graph-lock.h" #include "qapi/qapi-builtin-types.h" #define SNAPSHOT_OPT_BASE "snapshot." @@ -59,16 +60,19 @@ bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, const char *name, QEMUSnapshotInfo *sn_info, Error **errp); -int bdrv_can_snapshot(BlockDriverState *bs); -int bdrv_snapshot_create(BlockDriverState *bs, - QEMUSnapshotInfo *sn_info); -int bdrv_snapshot_goto(BlockDriverState *bs, - const char *snapshot_id, - Error **errp); -int bdrv_snapshot_delete(BlockDriverState *bs, - const char *snapshot_id, - const char *name, - Error **errp); + +int GRAPH_RDLOCK bdrv_can_snapshot(BlockDriverState *bs); + +int GRAPH_RDLOCK +bdrv_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); + +int GRAPH_UNLOCKED +bdrv_snapshot_goto(BlockDriverState *bs, const char *snapshot_id, Error **errp); + +int GRAPH_RDLOCK +bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id, + const char *name, Error **errp); + int bdrv_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_info); int bdrv_snapshot_load_tmp(BlockDriverState *bs, @@ -82,8 +86,6 @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, /* * Group operations. All block drivers are involved. - * These functions will properly handle dataplane (take aio_context_acquire - * when appropriate for appropriate block drivers */ bool bdrv_all_can_snapshot(bool has_devices, strList *devices, diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h index 2020bcc92d..948ff5f30c 100644 --- a/include/block/thread-pool.h +++ b/include/block/thread-pool.h @@ -18,7 +18,7 @@ #ifndef QEMU_THREAD_POOL_H #define QEMU_THREAD_POOL_H -#include "block/block.h" +#include "block/aio.h" #define THREAD_POOL_MAX_THREADS_DEFAULT 64 @@ -29,12 +29,15 @@ typedef struct ThreadPool ThreadPool; ThreadPool *thread_pool_new(struct AioContext *ctx); void thread_pool_free(ThreadPool *pool); -BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool, - ThreadPoolFunc *func, void *arg, - BlockCompletionFunc *cb, void *opaque); -int coroutine_fn thread_pool_submit_co(ThreadPool *pool, - ThreadPoolFunc *func, void *arg); -void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg); +/* + * thread_pool_submit* API: submit I/O requests in the thread's + * current AioContext. + */ +BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, + BlockCompletionFunc *cb, void *opaque); +int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg); +void thread_pool_submit(ThreadPoolFunc *func, void *arg); + void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx); #endif diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h index 9541b32432..2355e8d9de 100644 --- a/include/block/throttle-groups.h +++ b/include/block/throttle-groups.h @@ -25,8 +25,8 @@ #ifndef THROTTLE_GROUPS_H #define THROTTLE_GROUPS_H +#include "qemu/coroutine.h" #include "qemu/throttle.h" -#include "block/block_int.h" #include "qom/object.h" /* The ThrottleGroupMember structure indicates membership in a ThrottleGroup @@ -37,7 +37,7 @@ typedef struct ThrottleGroupMember { AioContext *aio_context; /* throttled_reqs_lock protects the CoQueues for throttled requests. */ CoMutex throttled_reqs_lock; - CoQueue throttled_reqs[2]; + CoQueue throttled_reqs[THROTTLE_MAX]; /* Nonzero if the I/O limits are currently being ignored; generally * it is zero. Accessed with atomic operations. @@ -54,7 +54,7 @@ typedef struct ThrottleGroupMember { * throttle_state tells us if I/O limits are configured. */ ThrottleState *throttle_state; ThrottleTimers throttle_timers; - unsigned pending_reqs[2]; + unsigned pending_reqs[THROTTLE_MAX]; QLIST_ENTRY(ThrottleGroupMember) round_robin; } ThrottleGroupMember; @@ -78,7 +78,7 @@ void throttle_group_restart_tgm(ThrottleGroupMember *tgm); void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm, int64_t bytes, - bool is_write); + ThrottleDirection direction); void throttle_group_attach_aio_context(ThrottleGroupMember *tgm, AioContext *new_context); void throttle_group_detach_aio_context(ThrottleGroupMember *tgm); diff --git a/include/block/ufs.h b/include/block/ufs.h new file mode 100644 index 0000000000..d61598b8f3 --- /dev/null +++ b/include/block/ufs.h @@ -0,0 +1,1090 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef BLOCK_UFS_H +#define BLOCK_UFS_H + +#include "hw/registerfields.h" + +typedef struct QEMU_PACKED UfsReg { + uint32_t cap; + uint32_t rsvd0; + uint32_t ver; + uint32_t rsvd1; + uint32_t hcpid; + uint32_t hcmid; + uint32_t ahit; + uint32_t rsvd2; + uint32_t is; + uint32_t ie; + uint32_t rsvd3[2]; + uint32_t hcs; + uint32_t hce; + uint32_t uecpa; + uint32_t uecdl; + uint32_t uecn; + uint32_t uect; + uint32_t uecdme; + uint32_t utriacr; + uint32_t utrlba; + uint32_t utrlbau; + uint32_t utrldbr; + uint32_t utrlclr; + uint32_t utrlrsr; + uint32_t utrlcnr; + uint32_t rsvd4[2]; + uint32_t utmrlba; + uint32_t utmrlbau; + uint32_t utmrldbr; + uint32_t utmrlclr; + uint32_t utmrlrsr; + uint32_t rsvd5[3]; + uint32_t uiccmd; + uint32_t ucmdarg1; + uint32_t ucmdarg2; + uint32_t ucmdarg3; + uint32_t rsvd6[4]; + uint32_t rsvd7[4]; + uint32_t rsvd8[16]; + uint32_t ccap; +} UfsReg; + +REG32(CAP, offsetof(UfsReg, cap)) + FIELD(CAP, NUTRS, 0, 5) + FIELD(CAP, RTT, 8, 8) + FIELD(CAP, NUTMRS, 16, 3) + FIELD(CAP, AUTOH8, 23, 1) + FIELD(CAP, 64AS, 24, 1) + FIELD(CAP, OODDS, 25, 1) + FIELD(CAP, UICDMETMS, 26, 1) + FIELD(CAP, CS, 28, 1) +REG32(VER, offsetof(UfsReg, ver)) +REG32(HCPID, offsetof(UfsReg, hcpid)) +REG32(HCMID, offsetof(UfsReg, hcmid)) +REG32(AHIT, offsetof(UfsReg, ahit)) +REG32(IS, offsetof(UfsReg, is)) + FIELD(IS, UTRCS, 0, 1) + FIELD(IS, UDEPRI, 1, 1) + FIELD(IS, UE, 2, 1) + FIELD(IS, UTMS, 3, 1) + FIELD(IS, UPMS, 4, 1) + FIELD(IS, UHXS, 5, 1) + FIELD(IS, UHES, 6, 1) + FIELD(IS, ULLS, 7, 1) + FIELD(IS, ULSS, 8, 1) + FIELD(IS, UTMRCS, 9, 1) + FIELD(IS, UCCS, 10, 1) + FIELD(IS, DFES, 11, 1) + FIELD(IS, UTPES, 12, 1) + FIELD(IS, HCFES, 16, 1) + FIELD(IS, SBFES, 17, 1) + FIELD(IS, CEFES, 18, 1) +REG32(IE, offsetof(UfsReg, ie)) + FIELD(IE, UTRCE, 0, 1) + FIELD(IE, UDEPRIE, 1, 1) + FIELD(IE, UEE, 2, 1) + FIELD(IE, UTMSE, 3, 1) + FIELD(IE, UPMSE, 4, 1) + FIELD(IE, UHXSE, 5, 1) + FIELD(IE, UHESE, 6, 1) + FIELD(IE, ULLSE, 7, 1) + FIELD(IE, ULSSE, 8, 1) + FIELD(IE, UTMRCE, 9, 1) + FIELD(IE, UCCE, 10, 1) + FIELD(IE, DFEE, 11, 1) + FIELD(IE, UTPEE, 12, 1) + FIELD(IE, HCFEE, 16, 1) + FIELD(IE, SBFEE, 17, 1) + FIELD(IE, CEFEE, 18, 1) +REG32(HCS, offsetof(UfsReg, hcs)) + FIELD(HCS, DP, 0, 1) + FIELD(HCS, UTRLRDY, 1, 1) + FIELD(HCS, UTMRLRDY, 2, 1) + FIELD(HCS, UCRDY, 3, 1) + FIELD(HCS, UPMCRS, 8, 3) +REG32(HCE, offsetof(UfsReg, hce)) + FIELD(HCE, HCE, 0, 1) + FIELD(HCE, CGE, 1, 1) +REG32(UECPA, offsetof(UfsReg, uecpa)) +REG32(UECDL, offsetof(UfsReg, uecdl)) +REG32(UECN, offsetof(UfsReg, uecn)) +REG32(UECT, offsetof(UfsReg, uect)) +REG32(UECDME, offsetof(UfsReg, uecdme)) +REG32(UTRIACR, offsetof(UfsReg, utriacr)) +REG32(UTRLBA, offsetof(UfsReg, utrlba)) + FIELD(UTRLBA, UTRLBA, 10, 22) +REG32(UTRLBAU, offsetof(UfsReg, utrlbau)) +REG32(UTRLDBR, offsetof(UfsReg, utrldbr)) +REG32(UTRLCLR, offsetof(UfsReg, utrlclr)) +REG32(UTRLRSR, offsetof(UfsReg, utrlrsr)) +REG32(UTRLCNR, offsetof(UfsReg, utrlcnr)) +REG32(UTMRLBA, offsetof(UfsReg, utmrlba)) + FIELD(UTMRLBA, UTMRLBA, 10, 22) +REG32(UTMRLBAU, offsetof(UfsReg, utmrlbau)) +REG32(UTMRLDBR, offsetof(UfsReg, utmrldbr)) +REG32(UTMRLCLR, offsetof(UfsReg, utmrlclr)) +REG32(UTMRLRSR, offsetof(UfsReg, utmrlrsr)) +REG32(UICCMD, offsetof(UfsReg, uiccmd)) +REG32(UCMDARG1, offsetof(UfsReg, ucmdarg1)) +REG32(UCMDARG2, offsetof(UfsReg, ucmdarg2)) +REG32(UCMDARG3, offsetof(UfsReg, ucmdarg3)) +REG32(CCAP, offsetof(UfsReg, ccap)) + +#define UFS_INTR_MASK \ + ((1 << R_IS_CEFES_SHIFT) | (1 << R_IS_SBFES_SHIFT) | \ + (1 << R_IS_HCFES_SHIFT) | (1 << R_IS_UTPES_SHIFT) | \ + (1 << R_IS_DFES_SHIFT) | (1 << R_IS_UCCS_SHIFT) | \ + (1 << R_IS_UTMRCS_SHIFT) | (1 << R_IS_ULSS_SHIFT) | \ + (1 << R_IS_ULLS_SHIFT) | (1 << R_IS_UHES_SHIFT) | \ + (1 << R_IS_UHXS_SHIFT) | (1 << R_IS_UPMS_SHIFT) | \ + (1 << R_IS_UTMS_SHIFT) | (1 << R_IS_UE_SHIFT) | \ + (1 << R_IS_UDEPRI_SHIFT) | (1 << R_IS_UTRCS_SHIFT)) + +#define UFS_UPIU_HEADER_TRANSACTION_TYPE_SHIFT 24 +#define UFS_UPIU_HEADER_TRANSACTION_TYPE_MASK 0xff +#define UFS_UPIU_HEADER_TRANSACTION_TYPE(dword0) \ + ((be32_to_cpu(dword0) >> UFS_UPIU_HEADER_TRANSACTION_TYPE_SHIFT) & \ + UFS_UPIU_HEADER_TRANSACTION_TYPE_MASK) + +#define UFS_UPIU_HEADER_QUERY_FUNC_SHIFT 16 +#define UFS_UPIU_HEADER_QUERY_FUNC_MASK 0xff +#define UFS_UPIU_HEADER_QUERY_FUNC(dword1) \ + ((be32_to_cpu(dword1) >> UFS_UPIU_HEADER_QUERY_FUNC_SHIFT) & \ + UFS_UPIU_HEADER_QUERY_FUNC_MASK) + +#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_SHIFT 0 +#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_MASK 0xffff +#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH(dword2) \ + ((be32_to_cpu(dword2) >> UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_SHIFT) & \ + UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_MASK) + +typedef struct QEMU_PACKED DeviceDescriptor { + uint8_t length; + uint8_t descriptor_idn; + uint8_t device; + uint8_t device_class; + uint8_t device_sub_class; + uint8_t protocol; + uint8_t number_lu; + uint8_t number_wlu; + uint8_t boot_enable; + uint8_t descr_access_en; + uint8_t init_power_mode; + uint8_t high_priority_lun; + uint8_t secure_removal_type; + uint8_t security_lu; + uint8_t background_ops_term_lat; + uint8_t init_active_icc_level; + uint16_t spec_version; + uint16_t manufacture_date; + uint8_t manufacturer_name; + uint8_t product_name; + uint8_t serial_number; + uint8_t oem_id; + uint16_t manufacturer_id; + uint8_t ud_0_base_offset; + uint8_t ud_config_p_length; + uint8_t device_rtt_cap; + uint16_t periodic_rtc_update; + uint8_t ufs_features_support; + uint8_t ffu_timeout; + uint8_t queue_depth; + uint16_t device_version; + uint8_t num_secure_wp_area; + uint32_t psa_max_data_size; + uint8_t psa_state_timeout; + uint8_t product_revision_level; + uint8_t reserved[36]; + uint32_t extended_ufs_features_support; + uint8_t write_booster_buffer_preserve_user_space_en; + uint8_t write_booster_buffer_type; + uint32_t num_shared_write_booster_buffer_alloc_units; +} DeviceDescriptor; + +typedef struct QEMU_PACKED GeometryDescriptor { + uint8_t length; + uint8_t descriptor_idn; + uint8_t media_technology; + uint8_t reserved; + uint64_t total_raw_device_capacity; + uint8_t max_number_lu; + uint32_t segment_size; + uint8_t allocation_unit_size; + uint8_t min_addr_block_size; + uint8_t optimal_read_block_size; + uint8_t optimal_write_block_size; + uint8_t max_in_buffer_size; + uint8_t max_out_buffer_size; + uint8_t rpmb_read_write_size; + uint8_t dynamic_capacity_resource_policy; + uint8_t data_ordering; + uint8_t max_context_id_number; + uint8_t sys_data_tag_unit_size; + uint8_t sys_data_tag_res_size; + uint8_t supported_sec_r_types; + uint16_t supported_memory_types; + uint32_t system_code_max_n_alloc_u; + uint16_t system_code_cap_adj_fac; + uint32_t non_persist_max_n_alloc_u; + uint16_t non_persist_cap_adj_fac; + uint32_t enhanced_1_max_n_alloc_u; + uint16_t enhanced_1_cap_adj_fac; + uint32_t enhanced_2_max_n_alloc_u; + uint16_t enhanced_2_cap_adj_fac; + uint32_t enhanced_3_max_n_alloc_u; + uint16_t enhanced_3_cap_adj_fac; + uint32_t enhanced_4_max_n_alloc_u; + uint16_t enhanced_4_cap_adj_fac; + uint32_t optimal_logical_block_size; + uint8_t reserved2[7]; + uint32_t write_booster_buffer_max_n_alloc_units; + uint8_t device_max_write_booster_l_us; + uint8_t write_booster_buffer_cap_adj_fac; + uint8_t supported_write_booster_buffer_user_space_reduction_types; + uint8_t supported_write_booster_buffer_types; +} GeometryDescriptor; + +#define UFS_GEOMETRY_CAPACITY_SHIFT 9 + +typedef struct QEMU_PACKED UnitDescriptor { + uint8_t length; + uint8_t descriptor_idn; + uint8_t unit_index; + uint8_t lu_enable; + uint8_t boot_lun_id; + uint8_t lu_write_protect; + uint8_t lu_queue_depth; + uint8_t psa_sensitive; + uint8_t memory_type; + uint8_t data_reliability; + uint8_t logical_block_size; + uint64_t logical_block_count; + uint32_t erase_block_size; + uint8_t provisioning_type; + uint64_t phy_mem_resource_count; + uint16_t context_capabilities; + uint8_t large_unit_granularity_m1; + uint8_t reserved[6]; + uint32_t lu_num_write_booster_buffer_alloc_units; +} UnitDescriptor; + +typedef struct QEMU_PACKED RpmbUnitDescriptor { + uint8_t length; + uint8_t descriptor_idn; + uint8_t unit_index; + uint8_t lu_enable; + uint8_t boot_lun_id; + uint8_t lu_write_protect; + uint8_t lu_queue_depth; + uint8_t psa_sensitive; + uint8_t memory_type; + uint8_t reserved; + uint8_t logical_block_size; + uint64_t logical_block_count; + uint32_t erase_block_size; + uint8_t provisioning_type; + uint64_t phy_mem_resource_count; + uint8_t reserved2[3]; +} RpmbUnitDescriptor; + +typedef struct QEMU_PACKED PowerParametersDescriptor { + uint8_t length; + uint8_t descriptor_idn; + uint16_t active_icc_levels_vcc[16]; + uint16_t active_icc_levels_vccq[16]; + uint16_t active_icc_levels_vccq_2[16]; +} PowerParametersDescriptor; + +typedef struct QEMU_PACKED InterconnectDescriptor { + uint8_t length; + uint8_t descriptor_idn; + uint16_t bcd_unipro_version; + uint16_t bcd_mphy_version; +} InterconnectDescriptor; + +typedef struct QEMU_PACKED StringDescriptor { + uint8_t length; + uint8_t descriptor_idn; + uint16_t UC[126]; +} StringDescriptor; + +typedef struct QEMU_PACKED DeviceHealthDescriptor { + uint8_t length; + uint8_t descriptor_idn; + uint8_t pre_eol_info; + uint8_t device_life_time_est_a; + uint8_t device_life_time_est_b; + uint8_t vendor_prop_info[32]; + uint32_t refresh_total_count; + uint32_t refresh_progress; +} DeviceHealthDescriptor; + +typedef struct QEMU_PACKED Flags { + uint8_t reserved; + uint8_t device_init; + uint8_t permanent_wp_en; + uint8_t power_on_wp_en; + uint8_t background_ops_en; + uint8_t device_life_span_mode_en; + uint8_t purge_enable; + uint8_t refresh_enable; + uint8_t phy_resource_removal; + uint8_t busy_rtc; + uint8_t reserved2; + uint8_t permanently_disable_fw_update; + uint8_t reserved3[2]; + uint8_t wb_en; + uint8_t wb_buffer_flush_en; + uint8_t wb_buffer_flush_during_hibernate; + uint8_t reserved4[2]; +} Flags; + +typedef struct Attributes { + uint8_t boot_lun_en; + uint8_t reserved; + uint8_t current_power_mode; + uint8_t active_icc_level; + uint8_t out_of_order_data_en; + uint8_t background_op_status; + uint8_t purge_status; + uint8_t max_data_in_size; + uint8_t max_data_out_size; + uint32_t dyn_cap_needed; + uint8_t ref_clk_freq; + uint8_t config_descr_lock; + uint8_t max_num_of_rtt; + uint16_t exception_event_control; + uint16_t exception_event_status; + uint32_t seconds_passed; + uint16_t context_conf; + uint8_t device_ffu_status; + uint8_t psa_state; + uint32_t psa_data_size; + uint8_t ref_clk_gating_wait_time; + uint8_t device_case_rough_temperaure; + uint8_t device_too_high_temp_boundary; + uint8_t device_too_low_temp_boundary; + uint8_t throttling_status; + uint8_t wb_buffer_flush_status; + uint8_t available_wb_buffer_size; + uint8_t wb_buffer_life_time_est; + uint32_t current_wb_buffer_size; + uint8_t refresh_status; + uint8_t refresh_freq; + uint8_t refresh_unit; + uint8_t refresh_method; +} Attributes; + +#define UFS_TRANSACTION_SPECIFIC_FIELD_SIZE 20 +#define UFS_MAX_QUERY_DATA_SIZE 256 + +/* Command response result code */ +typedef enum CommandRespCode { + UFS_COMMAND_RESULT_SUCCESS = 0x00, + UFS_COMMAND_RESULT_FAIL = 0x01, +} CommandRespCode; + +enum { + UFS_UPIU_FLAG_UNDERFLOW = 0x20, + UFS_UPIU_FLAG_OVERFLOW = 0x40, +}; + +typedef struct QEMU_PACKED UtpUpiuHeader { + uint8_t trans_type; + uint8_t flags; + uint8_t lun; + uint8_t task_tag; + uint8_t iid_cmd_set_type; + uint8_t query_func; + uint8_t response; + uint8_t scsi_status; + uint8_t ehs_len; + uint8_t device_inf; + uint16_t data_segment_length; +} UtpUpiuHeader; + +/* + * The code below is copied from the linux kernel + * ("include/uapi/scsi/scsi_bsg_ufs.h") and modified to fit the qemu style. + */ + +typedef struct QEMU_PACKED UtpUpiuQuery { + uint8_t opcode; + uint8_t idn; + uint8_t index; + uint8_t selector; + uint16_t reserved_osf; + uint16_t length; + uint32_t value; + uint32_t reserved[2]; + /* EHS length should be 0. We don't have to worry about EHS area. */ + uint8_t data[UFS_MAX_QUERY_DATA_SIZE]; +} UtpUpiuQuery; + +#define UFS_CDB_SIZE 16 + +/* + * struct UtpUpiuCmd - Command UPIU structure + * @data_transfer_len: Data Transfer Length DW-3 + * @cdb: Command Descriptor Block CDB DW-4 to DW-7 + */ +typedef struct QEMU_PACKED UtpUpiuCmd { + uint32_t exp_data_transfer_len; + uint8_t cdb[UFS_CDB_SIZE]; +} UtpUpiuCmd; + +/* + * struct UtpUpiuReq - general upiu request structure + * @header:UPIU header structure DW-0 to DW-2 + * @sc: fields structure for scsi command DW-3 to DW-7 + * @qr: fields structure for query request DW-3 to DW-7 + * @uc: use utp_upiu_query to host the 4 dwords of uic command + */ +typedef struct QEMU_PACKED UtpUpiuReq { + UtpUpiuHeader header; + union { + UtpUpiuCmd sc; + UtpUpiuQuery qr; + }; +} UtpUpiuReq; + +/* + * The code below is copied from the linux kernel ("include/ufs/ufshci.h") and + * modified to fit the qemu style. + */ + +enum { + UFS_PWR_OK = 0x0, + UFS_PWR_LOCAL = 0x01, + UFS_PWR_REMOTE = 0x02, + UFS_PWR_BUSY = 0x03, + UFS_PWR_ERROR_CAP = 0x04, + UFS_PWR_FATAL_ERROR = 0x05, +}; + +/* UIC Commands */ +enum uic_cmd_dme { + UFS_UIC_CMD_DME_GET = 0x01, + UFS_UIC_CMD_DME_SET = 0x02, + UFS_UIC_CMD_DME_PEER_GET = 0x03, + UFS_UIC_CMD_DME_PEER_SET = 0x04, + UFS_UIC_CMD_DME_POWERON = 0x10, + UFS_UIC_CMD_DME_POWEROFF = 0x11, + UFS_UIC_CMD_DME_ENABLE = 0x12, + UFS_UIC_CMD_DME_RESET = 0x14, + UFS_UIC_CMD_DME_END_PT_RST = 0x15, + UFS_UIC_CMD_DME_LINK_STARTUP = 0x16, + UFS_UIC_CMD_DME_HIBER_ENTER = 0x17, + UFS_UIC_CMD_DME_HIBER_EXIT = 0x18, + UFS_UIC_CMD_DME_TEST_MODE = 0x1A, +}; + +/* UIC Config result code / Generic error code */ +enum { + UFS_UIC_CMD_RESULT_SUCCESS = 0x00, + UFS_UIC_CMD_RESULT_INVALID_ATTR = 0x01, + UFS_UIC_CMD_RESULT_FAILURE = 0x01, + UFS_UIC_CMD_RESULT_INVALID_ATTR_VALUE = 0x02, + UFS_UIC_CMD_RESULT_READ_ONLY_ATTR = 0x03, + UFS_UIC_CMD_RESULT_WRITE_ONLY_ATTR = 0x04, + UFS_UIC_CMD_RESULT_BAD_INDEX = 0x05, + UFS_UIC_CMD_RESULT_LOCKED_ATTR = 0x06, + UFS_UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX = 0x07, + UFS_UIC_CMD_RESULT_PEER_COMM_FAILURE = 0x08, + UFS_UIC_CMD_RESULT_BUSY = 0x09, + UFS_UIC_CMD_RESULT_DME_FAILURE = 0x0A, +}; + +#define UFS_MASK_UIC_COMMAND_RESULT 0xFF + +/* + * Request Descriptor Definitions + */ + +/* Transfer request command type */ +enum { + UFS_UTP_CMD_TYPE_SCSI = 0x0, + UFS_UTP_CMD_TYPE_UFS = 0x1, + UFS_UTP_CMD_TYPE_DEV_MANAGE = 0x2, +}; + +/* To accommodate UFS2.0 required Command type */ +enum { + UFS_UTP_CMD_TYPE_UFS_STORAGE = 0x1, +}; + +enum { + UFS_UTP_SCSI_COMMAND = 0x00000000, + UFS_UTP_NATIVE_UFS_COMMAND = 0x10000000, + UFS_UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000, + UFS_UTP_REQ_DESC_INT_CMD = 0x01000000, + UFS_UTP_REQ_DESC_CRYPTO_ENABLE_CMD = 0x00800000, +}; + +/* UTP Transfer Request Data Direction (DD) */ +enum { + UFS_UTP_NO_DATA_TRANSFER = 0x00000000, + UFS_UTP_HOST_TO_DEVICE = 0x02000000, + UFS_UTP_DEVICE_TO_HOST = 0x04000000, +}; + +/* Overall command status values */ +enum UtpOcsCodes { + UFS_OCS_SUCCESS = 0x0, + UFS_OCS_INVALID_CMD_TABLE_ATTR = 0x1, + UFS_OCS_INVALID_PRDT_ATTR = 0x2, + UFS_OCS_MISMATCH_DATA_BUF_SIZE = 0x3, + UFS_OCS_MISMATCH_RESP_UPIU_SIZE = 0x4, + UFS_OCS_PEER_COMM_FAILURE = 0x5, + UFS_OCS_ABORTED = 0x6, + UFS_OCS_FATAL_ERROR = 0x7, + UFS_OCS_DEVICE_FATAL_ERROR = 0x8, + UFS_OCS_INVALID_CRYPTO_CONFIG = 0x9, + UFS_OCS_GENERAL_CRYPTO_ERROR = 0xa, + UFS_OCS_INVALID_COMMAND_STATUS = 0xf, +}; + +enum { + UFS_MASK_OCS = 0x0F, +}; + +/* + * struct UfshcdSgEntry - UFSHCI PRD Entry + * @addr: Physical address; DW-0 and DW-1. + * @reserved: Reserved for future use DW-2 + * @size: size of physical segment DW-3 + */ +typedef struct QEMU_PACKED UfshcdSgEntry { + uint64_t addr; + uint32_t reserved; + uint32_t size; + /* + * followed by variant-specific fields if + * CONFIG_SCSI_UFS_VARIABLE_SG_ENTRY_SIZE has been defined. + */ +} UfshcdSgEntry; + +/* + * struct RequestDescHeader - Descriptor Header common to both UTRD and UTMRD + * @dword0: Descriptor Header DW0 + * @dword1: Descriptor Header DW1 + * @dword2: Descriptor Header DW2 + * @dword3: Descriptor Header DW3 + */ +typedef struct QEMU_PACKED RequestDescHeader { + uint32_t dword_0; + uint32_t dword_1; + uint32_t dword_2; + uint32_t dword_3; +} RequestDescHeader; + +/* + * struct UtpTransferReqDesc - UTP Transfer Request Descriptor (UTRD) + * @header: UTRD header DW-0 to DW-3 + * @command_desc_base_addr_lo: UCD base address low DW-4 + * @command_desc_base_addr_hi: UCD base address high DW-5 + * @response_upiu_length: response UPIU length DW-6 + * @response_upiu_offset: response UPIU offset DW-6 + * @prd_table_length: Physical region descriptor length DW-7 + * @prd_table_offset: Physical region descriptor offset DW-7 + */ +typedef struct QEMU_PACKED UtpTransferReqDesc { + /* DW 0-3 */ + RequestDescHeader header; + + /* DW 4-5*/ + uint32_t command_desc_base_addr_lo; + uint32_t command_desc_base_addr_hi; + + /* DW 6 */ + uint16_t response_upiu_length; + uint16_t response_upiu_offset; + + /* DW 7 */ + uint16_t prd_table_length; + uint16_t prd_table_offset; +} UtpTransferReqDesc; + +/* + * UTMRD structure. + */ +typedef struct QEMU_PACKED UtpTaskReqDesc { + /* DW 0-3 */ + RequestDescHeader header; + + /* DW 4-11 - Task request UPIU structure */ + struct { + UtpUpiuHeader req_header; + uint32_t input_param1; + uint32_t input_param2; + uint32_t input_param3; + uint32_t reserved1[2]; + } upiu_req; + + /* DW 12-19 - Task Management Response UPIU structure */ + struct { + UtpUpiuHeader rsp_header; + uint32_t output_param1; + uint32_t output_param2; + uint32_t reserved2[3]; + } upiu_rsp; +} UtpTaskReqDesc; + +/* + * The code below is copied from the linux kernel ("include/ufs/ufs.h") and + * modified to fit the qemu style. + */ + +#define UFS_GENERAL_UPIU_REQUEST_SIZE (sizeof(UtpUpiuReq)) +#define UFS_QUERY_DESC_MAX_SIZE 255 +#define UFS_QUERY_DESC_MIN_SIZE 2 +#define UFS_QUERY_DESC_HDR_SIZE 2 +#define UFS_QUERY_OSF_SIZE (GENERAL_UPIU_REQUEST_SIZE - (sizeof(UtpUpiuHeader))) +#define UFS_SENSE_SIZE 18 + +/* + * UFS device may have standard LUs and LUN id could be from 0x00 to + * 0x7F. Standard LUs use "Peripheral Device Addressing Format". + * UFS device may also have the Well Known LUs (also referred as W-LU) + * which again could be from 0x00 to 0x7F. For W-LUs, device only use + * the "Extended Addressing Format" which means the W-LUNs would be + * from 0xc100 (SCSI_W_LUN_BASE) onwards. + * This means max. LUN number reported from UFS device could be 0xC17F. + */ +#define UFS_UPIU_MAX_UNIT_NUM_ID 0x7F +#define UFS_UPIU_WLUN_ID (1 << 7) + +/* WriteBooster buffer is available only for the logical unit from 0 to 7 */ +#define UFS_UPIU_MAX_WB_LUN_ID 8 + +/* + * WriteBooster buffer lifetime has a limit set by vendor. + * If it is over the limit, WriteBooster feature will be disabled. + */ +#define UFS_WB_EXCEED_LIFETIME 0x0B + +/* + * In UFS Spec, the Extra Header Segment (EHS) starts from byte 32 in UPIU + * request/response packet + */ +#define UFS_EHS_OFFSET_IN_RESPONSE 32 + +/* Well known logical unit id in LUN field of UPIU */ +enum { + UFS_UPIU_REPORT_LUNS_WLUN = 0x81, + UFS_UPIU_UFS_DEVICE_WLUN = 0xD0, + UFS_UPIU_BOOT_WLUN = 0xB0, + UFS_UPIU_RPMB_WLUN = 0xC4, +}; + +/* + * UFS Protocol Information Unit related definitions + */ + +/* Task management functions */ +enum { + UFS_ABORT_TASK = 0x01, + UFS_ABORT_TASK_SET = 0x02, + UFS_CLEAR_TASK_SET = 0x04, + UFS_LOGICAL_RESET = 0x08, + UFS_QUERY_TASK = 0x80, + UFS_QUERY_TASK_SET = 0x81, +}; + +/* UTP UPIU Transaction Codes Initiator to Target */ +enum { + UFS_UPIU_TRANSACTION_NOP_OUT = 0x00, + UFS_UPIU_TRANSACTION_COMMAND = 0x01, + UFS_UPIU_TRANSACTION_DATA_OUT = 0x02, + UFS_UPIU_TRANSACTION_TASK_REQ = 0x04, + UFS_UPIU_TRANSACTION_QUERY_REQ = 0x16, +}; + +/* UTP UPIU Transaction Codes Target to Initiator */ +enum { + UFS_UPIU_TRANSACTION_NOP_IN = 0x20, + UFS_UPIU_TRANSACTION_RESPONSE = 0x21, + UFS_UPIU_TRANSACTION_DATA_IN = 0x22, + UFS_UPIU_TRANSACTION_TASK_RSP = 0x24, + UFS_UPIU_TRANSACTION_READY_XFER = 0x31, + UFS_UPIU_TRANSACTION_QUERY_RSP = 0x36, + UFS_UPIU_TRANSACTION_REJECT_UPIU = 0x3F, +}; + +/* UPIU Read/Write flags */ +enum { + UFS_UPIU_CMD_FLAGS_NONE = 0x00, + UFS_UPIU_CMD_FLAGS_WRITE = 0x20, + UFS_UPIU_CMD_FLAGS_READ = 0x40, +}; + +/* UPIU Task Attributes */ +enum { + UFS_UPIU_TASK_ATTR_SIMPLE = 0x00, + UFS_UPIU_TASK_ATTR_ORDERED = 0x01, + UFS_UPIU_TASK_ATTR_HEADQ = 0x02, + UFS_UPIU_TASK_ATTR_ACA = 0x03, +}; + +/* UPIU Query request function */ +enum { + UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01, + UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81, +}; + +/* Flag idn for Query Requests*/ +enum flag_idn { + UFS_QUERY_FLAG_IDN_FDEVICEINIT = 0x01, + UFS_QUERY_FLAG_IDN_PERMANENT_WPE = 0x02, + UFS_QUERY_FLAG_IDN_PWR_ON_WPE = 0x03, + UFS_QUERY_FLAG_IDN_BKOPS_EN = 0x04, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE = 0x05, + UFS_QUERY_FLAG_IDN_PURGE_ENABLE = 0x06, + UFS_QUERY_FLAG_IDN_REFRESH_ENABLE = 0x07, + UFS_QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL = 0x08, + UFS_QUERY_FLAG_IDN_BUSY_RTC = 0x09, + UFS_QUERY_FLAG_IDN_RESERVED3 = 0x0A, + UFS_QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE = 0x0B, + UFS_QUERY_FLAG_IDN_WB_EN = 0x0E, + UFS_QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN = 0x0F, + UFS_QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8 = 0x10, + UFS_QUERY_FLAG_IDN_HPB_RESET = 0x11, + UFS_QUERY_FLAG_IDN_HPB_EN = 0x12, + UFS_QUERY_FLAG_IDN_COUNT, +}; + +/* Attribute idn for Query requests */ +enum attr_idn { + UFS_QUERY_ATTR_IDN_BOOT_LU_EN = 0x00, + UFS_QUERY_ATTR_IDN_MAX_HPB_SINGLE_CMD = 0x01, + UFS_QUERY_ATTR_IDN_POWER_MODE = 0x02, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03, + UFS_QUERY_ATTR_IDN_OOO_DATA_EN = 0x04, + UFS_QUERY_ATTR_IDN_BKOPS_STATUS = 0x05, + UFS_QUERY_ATTR_IDN_PURGE_STATUS = 0x06, + UFS_QUERY_ATTR_IDN_MAX_DATA_IN = 0x07, + UFS_QUERY_ATTR_IDN_MAX_DATA_OUT = 0x08, + UFS_QUERY_ATTR_IDN_DYN_CAP_NEEDED = 0x09, + UFS_QUERY_ATTR_IDN_REF_CLK_FREQ = 0x0A, + UFS_QUERY_ATTR_IDN_CONF_DESC_LOCK = 0x0B, + UFS_QUERY_ATTR_IDN_MAX_NUM_OF_RTT = 0x0C, + UFS_QUERY_ATTR_IDN_EE_CONTROL = 0x0D, + UFS_QUERY_ATTR_IDN_EE_STATUS = 0x0E, + UFS_QUERY_ATTR_IDN_SECONDS_PASSED = 0x0F, + UFS_QUERY_ATTR_IDN_CNTX_CONF = 0x10, + UFS_QUERY_ATTR_IDN_CORR_PRG_BLK_NUM = 0x11, + UFS_QUERY_ATTR_IDN_RESERVED2 = 0x12, + UFS_QUERY_ATTR_IDN_RESERVED3 = 0x13, + UFS_QUERY_ATTR_IDN_FFU_STATUS = 0x14, + UFS_QUERY_ATTR_IDN_PSA_STATE = 0x15, + UFS_QUERY_ATTR_IDN_PSA_DATA_SIZE = 0x16, + UFS_QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME = 0x17, + UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP = 0x18, + UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND = 0x19, + UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND = 0x1A, + UFS_QUERY_ATTR_IDN_THROTTLING_STATUS = 0x1B, + UFS_QUERY_ATTR_IDN_WB_FLUSH_STATUS = 0x1C, + UFS_QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE = 0x1D, + UFS_QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST = 0x1E, + UFS_QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE = 0x1F, + UFS_QUERY_ATTR_IDN_REFRESH_STATUS = 0x2C, + UFS_QUERY_ATTR_IDN_REFRESH_FREQ = 0x2D, + UFS_QUERY_ATTR_IDN_REFRESH_UNIT = 0x2E, + UFS_QUERY_ATTR_IDN_COUNT, +}; + +/* Descriptor idn for Query requests */ +enum desc_idn { + UFS_QUERY_DESC_IDN_DEVICE = 0x0, + UFS_QUERY_DESC_IDN_CONFIGURATION = 0x1, + UFS_QUERY_DESC_IDN_UNIT = 0x2, + UFS_QUERY_DESC_IDN_RFU_0 = 0x3, + UFS_QUERY_DESC_IDN_INTERCONNECT = 0x4, + UFS_QUERY_DESC_IDN_STRING = 0x5, + UFS_QUERY_DESC_IDN_RFU_1 = 0x6, + UFS_QUERY_DESC_IDN_GEOMETRY = 0x7, + UFS_QUERY_DESC_IDN_POWER = 0x8, + UFS_QUERY_DESC_IDN_HEALTH = 0x9, + UFS_QUERY_DESC_IDN_MAX, +}; + +enum desc_header_offset { + UFS_QUERY_DESC_LENGTH_OFFSET = 0x00, + UFS_QUERY_DESC_DESC_TYPE_OFFSET = 0x01, +}; + +/* Unit descriptor parameters offsets in bytes*/ +enum unit_desc_param { + UFS_UNIT_DESC_PARAM_LEN = 0x0, + UFS_UNIT_DESC_PARAM_TYPE = 0x1, + UFS_UNIT_DESC_PARAM_UNIT_INDEX = 0x2, + UFS_UNIT_DESC_PARAM_LU_ENABLE = 0x3, + UFS_UNIT_DESC_PARAM_BOOT_LUN_ID = 0x4, + UFS_UNIT_DESC_PARAM_LU_WR_PROTECT = 0x5, + UFS_UNIT_DESC_PARAM_LU_Q_DEPTH = 0x6, + UFS_UNIT_DESC_PARAM_PSA_SENSITIVE = 0x7, + UFS_UNIT_DESC_PARAM_MEM_TYPE = 0x8, + UFS_UNIT_DESC_PARAM_DATA_RELIABILITY = 0x9, + UFS_UNIT_DESC_PARAM_LOGICAL_BLK_SIZE = 0xA, + UFS_UNIT_DESC_PARAM_LOGICAL_BLK_COUNT = 0xB, + UFS_UNIT_DESC_PARAM_ERASE_BLK_SIZE = 0x13, + UFS_UNIT_DESC_PARAM_PROVISIONING_TYPE = 0x17, + UFS_UNIT_DESC_PARAM_PHY_MEM_RSRC_CNT = 0x18, + UFS_UNIT_DESC_PARAM_CTX_CAPABILITIES = 0x20, + UFS_UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22, + UFS_UNIT_DESC_PARAM_HPB_LU_MAX_ACTIVE_RGNS = 0x23, + UFS_UNIT_DESC_PARAM_HPB_PIN_RGN_START_OFF = 0x25, + UFS_UNIT_DESC_PARAM_HPB_NUM_PIN_RGNS = 0x27, + UFS_UNIT_DESC_PARAM_WB_BUF_ALLOC_UNITS = 0x29, +}; + +/* RPMB Unit descriptor parameters offsets in bytes*/ +enum rpmb_unit_desc_param { + UFS_RPMB_UNIT_DESC_PARAM_LEN = 0x0, + UFS_RPMB_UNIT_DESC_PARAM_TYPE = 0x1, + UFS_RPMB_UNIT_DESC_PARAM_UNIT_INDEX = 0x2, + UFS_RPMB_UNIT_DESC_PARAM_LU_ENABLE = 0x3, + UFS_RPMB_UNIT_DESC_PARAM_BOOT_LUN_ID = 0x4, + UFS_RPMB_UNIT_DESC_PARAM_LU_WR_PROTECT = 0x5, + UFS_RPMB_UNIT_DESC_PARAM_LU_Q_DEPTH = 0x6, + UFS_RPMB_UNIT_DESC_PARAM_PSA_SENSITIVE = 0x7, + UFS_RPMB_UNIT_DESC_PARAM_MEM_TYPE = 0x8, + UFS_RPMB_UNIT_DESC_PARAM_REGION_EN = 0x9, + UFS_RPMB_UNIT_DESC_PARAM_LOGICAL_BLK_SIZE = 0xA, + UFS_RPMB_UNIT_DESC_PARAM_LOGICAL_BLK_COUNT = 0xB, + UFS_RPMB_UNIT_DESC_PARAM_REGION0_SIZE = 0x13, + UFS_RPMB_UNIT_DESC_PARAM_REGION1_SIZE = 0x14, + UFS_RPMB_UNIT_DESC_PARAM_REGION2_SIZE = 0x15, + UFS_RPMB_UNIT_DESC_PARAM_REGION3_SIZE = 0x16, + UFS_RPMB_UNIT_DESC_PARAM_PROVISIONING_TYPE = 0x17, + UFS_RPMB_UNIT_DESC_PARAM_PHY_MEM_RSRC_CNT = 0x18, +}; + +/* Device descriptor parameters offsets in bytes*/ +enum device_desc_param { + UFS_DEVICE_DESC_PARAM_LEN = 0x0, + UFS_DEVICE_DESC_PARAM_TYPE = 0x1, + UFS_DEVICE_DESC_PARAM_DEVICE_TYPE = 0x2, + UFS_DEVICE_DESC_PARAM_DEVICE_CLASS = 0x3, + UFS_DEVICE_DESC_PARAM_DEVICE_SUB_CLASS = 0x4, + UFS_DEVICE_DESC_PARAM_PRTCL = 0x5, + UFS_DEVICE_DESC_PARAM_NUM_LU = 0x6, + UFS_DEVICE_DESC_PARAM_NUM_WLU = 0x7, + UFS_DEVICE_DESC_PARAM_BOOT_ENBL = 0x8, + UFS_DEVICE_DESC_PARAM_DESC_ACCSS_ENBL = 0x9, + UFS_DEVICE_DESC_PARAM_INIT_PWR_MODE = 0xA, + UFS_DEVICE_DESC_PARAM_HIGH_PR_LUN = 0xB, + UFS_DEVICE_DESC_PARAM_SEC_RMV_TYPE = 0xC, + UFS_DEVICE_DESC_PARAM_SEC_LU = 0xD, + UFS_DEVICE_DESC_PARAM_BKOP_TERM_LT = 0xE, + UFS_DEVICE_DESC_PARAM_ACTVE_ICC_LVL = 0xF, + UFS_DEVICE_DESC_PARAM_SPEC_VER = 0x10, + UFS_DEVICE_DESC_PARAM_MANF_DATE = 0x12, + UFS_DEVICE_DESC_PARAM_MANF_NAME = 0x14, + UFS_DEVICE_DESC_PARAM_PRDCT_NAME = 0x15, + UFS_DEVICE_DESC_PARAM_SN = 0x16, + UFS_DEVICE_DESC_PARAM_OEM_ID = 0x17, + UFS_DEVICE_DESC_PARAM_MANF_ID = 0x18, + UFS_DEVICE_DESC_PARAM_UD_OFFSET = 0x1A, + UFS_DEVICE_DESC_PARAM_UD_LEN = 0x1B, + UFS_DEVICE_DESC_PARAM_RTT_CAP = 0x1C, + UFS_DEVICE_DESC_PARAM_FRQ_RTC = 0x1D, + UFS_DEVICE_DESC_PARAM_UFS_FEAT = 0x1F, + UFS_DEVICE_DESC_PARAM_FFU_TMT = 0x20, + UFS_DEVICE_DESC_PARAM_Q_DPTH = 0x21, + UFS_DEVICE_DESC_PARAM_DEV_VER = 0x22, + UFS_DEVICE_DESC_PARAM_NUM_SEC_WPA = 0x24, + UFS_DEVICE_DESC_PARAM_PSA_MAX_DATA = 0x25, + UFS_DEVICE_DESC_PARAM_PSA_TMT = 0x29, + UFS_DEVICE_DESC_PARAM_PRDCT_REV = 0x2A, + UFS_DEVICE_DESC_PARAM_HPB_VER = 0x40, + UFS_DEVICE_DESC_PARAM_HPB_CONTROL = 0x42, + UFS_DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP = 0x4F, + UFS_DEVICE_DESC_PARAM_WB_PRESRV_USRSPC_EN = 0x53, + UFS_DEVICE_DESC_PARAM_WB_TYPE = 0x54, + UFS_DEVICE_DESC_PARAM_WB_SHARED_ALLOC_UNITS = 0x55, +}; + +/* Interconnect descriptor parameters offsets in bytes*/ +enum interconnect_desc_param { + UFS_INTERCONNECT_DESC_PARAM_LEN = 0x0, + UFS_INTERCONNECT_DESC_PARAM_TYPE = 0x1, + UFS_INTERCONNECT_DESC_PARAM_UNIPRO_VER = 0x2, + UFS_INTERCONNECT_DESC_PARAM_MPHY_VER = 0x4, +}; + +/* Geometry descriptor parameters offsets in bytes*/ +enum geometry_desc_param { + UFS_GEOMETRY_DESC_PARAM_LEN = 0x0, + UFS_GEOMETRY_DESC_PARAM_TYPE = 0x1, + UFS_GEOMETRY_DESC_PARAM_DEV_CAP = 0x4, + UFS_GEOMETRY_DESC_PARAM_MAX_NUM_LUN = 0xC, + UFS_GEOMETRY_DESC_PARAM_SEG_SIZE = 0xD, + UFS_GEOMETRY_DESC_PARAM_ALLOC_UNIT_SIZE = 0x11, + UFS_GEOMETRY_DESC_PARAM_MIN_BLK_SIZE = 0x12, + UFS_GEOMETRY_DESC_PARAM_OPT_RD_BLK_SIZE = 0x13, + UFS_GEOMETRY_DESC_PARAM_OPT_WR_BLK_SIZE = 0x14, + UFS_GEOMETRY_DESC_PARAM_MAX_IN_BUF_SIZE = 0x15, + UFS_GEOMETRY_DESC_PARAM_MAX_OUT_BUF_SIZE = 0x16, + UFS_GEOMETRY_DESC_PARAM_RPMB_RW_SIZE = 0x17, + UFS_GEOMETRY_DESC_PARAM_DYN_CAP_RSRC_PLC = 0x18, + UFS_GEOMETRY_DESC_PARAM_DATA_ORDER = 0x19, + UFS_GEOMETRY_DESC_PARAM_MAX_NUM_CTX = 0x1A, + UFS_GEOMETRY_DESC_PARAM_TAG_UNIT_SIZE = 0x1B, + UFS_GEOMETRY_DESC_PARAM_TAG_RSRC_SIZE = 0x1C, + UFS_GEOMETRY_DESC_PARAM_SEC_RM_TYPES = 0x1D, + UFS_GEOMETRY_DESC_PARAM_MEM_TYPES = 0x1E, + UFS_GEOMETRY_DESC_PARAM_SCM_MAX_NUM_UNITS = 0x20, + UFS_GEOMETRY_DESC_PARAM_SCM_CAP_ADJ_FCTR = 0x24, + UFS_GEOMETRY_DESC_PARAM_NPM_MAX_NUM_UNITS = 0x26, + UFS_GEOMETRY_DESC_PARAM_NPM_CAP_ADJ_FCTR = 0x2A, + UFS_GEOMETRY_DESC_PARAM_ENM1_MAX_NUM_UNITS = 0x2C, + UFS_GEOMETRY_DESC_PARAM_ENM1_CAP_ADJ_FCTR = 0x30, + UFS_GEOMETRY_DESC_PARAM_ENM2_MAX_NUM_UNITS = 0x32, + UFS_GEOMETRY_DESC_PARAM_ENM2_CAP_ADJ_FCTR = 0x36, + UFS_GEOMETRY_DESC_PARAM_ENM3_MAX_NUM_UNITS = 0x38, + UFS_GEOMETRY_DESC_PARAM_ENM3_CAP_ADJ_FCTR = 0x3C, + UFS_GEOMETRY_DESC_PARAM_ENM4_MAX_NUM_UNITS = 0x3E, + UFS_GEOMETRY_DESC_PARAM_ENM4_CAP_ADJ_FCTR = 0x42, + UFS_GEOMETRY_DESC_PARAM_OPT_LOG_BLK_SIZE = 0x44, + UFS_GEOMETRY_DESC_PARAM_HPB_REGION_SIZE = 0x48, + UFS_GEOMETRY_DESC_PARAM_HPB_NUMBER_LU = 0x49, + UFS_GEOMETRY_DESC_PARAM_HPB_SUBREGION_SIZE = 0x4A, + UFS_GEOMETRY_DESC_PARAM_HPB_MAX_ACTIVE_REGS = 0x4B, + UFS_GEOMETRY_DESC_PARAM_WB_MAX_ALLOC_UNITS = 0x4F, + UFS_GEOMETRY_DESC_PARAM_WB_MAX_WB_LUNS = 0x53, + UFS_GEOMETRY_DESC_PARAM_WB_BUFF_CAP_ADJ = 0x54, + UFS_GEOMETRY_DESC_PARAM_WB_SUP_RED_TYPE = 0x55, + UFS_GEOMETRY_DESC_PARAM_WB_SUP_WB_TYPE = 0x56, +}; + +/* Health descriptor parameters offsets in bytes*/ +enum health_desc_param { + UFS_HEALTH_DESC_PARAM_LEN = 0x0, + UFS_HEALTH_DESC_PARAM_TYPE = 0x1, + UFS_HEALTH_DESC_PARAM_EOL_INFO = 0x2, + UFS_HEALTH_DESC_PARAM_LIFE_TIME_EST_A = 0x3, + UFS_HEALTH_DESC_PARAM_LIFE_TIME_EST_B = 0x4, +}; + +/* WriteBooster buffer mode */ +enum { + UFS_WB_BUF_MODE_LU_DEDICATED = 0x0, + UFS_WB_BUF_MODE_SHARED = 0x1, +}; + +/* + * Logical Unit Write Protect + * 00h: LU not write protected + * 01h: LU write protected when fPowerOnWPEn =1 + * 02h: LU permanently write protected when fPermanentWPEn =1 + */ +enum ufs_lu_wp_type { + UFS_LU_NO_WP = 0x00, + UFS_LU_POWER_ON_WP = 0x01, + UFS_LU_PERM_WP = 0x02, +}; + +/* UTP QUERY Transaction Specific Fields OpCode */ +enum query_opcode { + UFS_UPIU_QUERY_OPCODE_NOP = 0x0, + UFS_UPIU_QUERY_OPCODE_READ_DESC = 0x1, + UFS_UPIU_QUERY_OPCODE_WRITE_DESC = 0x2, + UFS_UPIU_QUERY_OPCODE_READ_ATTR = 0x3, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR = 0x4, + UFS_UPIU_QUERY_OPCODE_READ_FLAG = 0x5, + UFS_UPIU_QUERY_OPCODE_SET_FLAG = 0x6, + UFS_UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7, + UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8, +}; + +/* Query response result code */ +typedef enum QueryRespCode { + UFS_QUERY_RESULT_SUCCESS = 0x00, + UFS_QUERY_RESULT_NOT_READABLE = 0xF6, + UFS_QUERY_RESULT_NOT_WRITEABLE = 0xF7, + UFS_QUERY_RESULT_ALREADY_WRITTEN = 0xF8, + UFS_QUERY_RESULT_INVALID_LENGTH = 0xF9, + UFS_QUERY_RESULT_INVALID_VALUE = 0xFA, + UFS_QUERY_RESULT_INVALID_SELECTOR = 0xFB, + UFS_QUERY_RESULT_INVALID_INDEX = 0xFC, + UFS_QUERY_RESULT_INVALID_IDN = 0xFD, + UFS_QUERY_RESULT_INVALID_OPCODE = 0xFE, + UFS_QUERY_RESULT_GENERAL_FAILURE = 0xFF, +} QueryRespCode; + +/* UTP Transfer Request Command Type (CT) */ +enum { + UFS_UPIU_COMMAND_SET_TYPE_SCSI = 0x0, + UFS_UPIU_COMMAND_SET_TYPE_UFS = 0x1, + UFS_UPIU_COMMAND_SET_TYPE_QUERY = 0x2, +}; + +/* Task management service response */ +enum { + UFS_UPIU_TASK_MANAGEMENT_FUNC_COMPL = 0x00, + UFS_UPIU_TASK_MANAGEMENT_FUNC_NOT_SUPPORTED = 0x04, + UFS_UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED = 0x08, + UFS_UPIU_TASK_MANAGEMENT_FUNC_FAILED = 0x05, + UFS_UPIU_INCORRECT_LOGICAL_UNIT_NO = 0x09, +}; + +/* UFS device power modes */ +enum ufs_dev_pwr_mode { + UFS_ACTIVE_PWR_MODE = 1, + UFS_SLEEP_PWR_MODE = 2, + UFS_POWERDOWN_PWR_MODE = 3, + UFS_DEEPSLEEP_PWR_MODE = 4, +}; + +/* + * struct UtpCmdRsp - Response UPIU structure + * @residual_transfer_count: Residual transfer count DW-3 + * @reserved: Reserved double words DW-4 to DW-7 + * @sense_data_len: Sense data length DW-8 U16 + * @sense_data: Sense data field DW-8 to DW-12 + */ +typedef struct QEMU_PACKED UtpCmdRsp { + uint32_t residual_transfer_count; + uint32_t reserved[4]; + uint16_t sense_data_len; + uint8_t sense_data[UFS_SENSE_SIZE]; +} UtpCmdRsp; + +/* + * struct UtpUpiuRsp - general upiu response structure + * @header: UPIU header structure DW-0 to DW-2 + * @sr: fields structure for scsi command DW-3 to DW-12 + * @qr: fields structure for query request DW-3 to DW-7 + */ +typedef struct QEMU_PACKED UtpUpiuRsp { + UtpUpiuHeader header; + union { + UtpCmdRsp sr; + UtpUpiuQuery qr; + }; +} UtpUpiuRsp; + +static inline void _ufs_check_size(void) +{ + QEMU_BUILD_BUG_ON(sizeof(UfsReg) != 0x104); + QEMU_BUILD_BUG_ON(sizeof(DeviceDescriptor) != 89); + QEMU_BUILD_BUG_ON(sizeof(GeometryDescriptor) != 87); + QEMU_BUILD_BUG_ON(sizeof(UnitDescriptor) != 45); + QEMU_BUILD_BUG_ON(sizeof(RpmbUnitDescriptor) != 35); + QEMU_BUILD_BUG_ON(sizeof(PowerParametersDescriptor) != 98); + QEMU_BUILD_BUG_ON(sizeof(InterconnectDescriptor) != 6); + QEMU_BUILD_BUG_ON(sizeof(StringDescriptor) != 254); + QEMU_BUILD_BUG_ON(sizeof(DeviceHealthDescriptor) != 45); + QEMU_BUILD_BUG_ON(sizeof(Flags) != 0x13); + QEMU_BUILD_BUG_ON(sizeof(UtpUpiuHeader) != 12); + QEMU_BUILD_BUG_ON(sizeof(UtpUpiuQuery) != 276); + QEMU_BUILD_BUG_ON(sizeof(UtpUpiuCmd) != 20); + QEMU_BUILD_BUG_ON(sizeof(UtpUpiuReq) != 288); + QEMU_BUILD_BUG_ON(sizeof(UfshcdSgEntry) != 16); + QEMU_BUILD_BUG_ON(sizeof(RequestDescHeader) != 16); + QEMU_BUILD_BUG_ON(sizeof(UtpTransferReqDesc) != 32); + QEMU_BUILD_BUG_ON(sizeof(UtpTaskReqDesc) != 80); + QEMU_BUILD_BUG_ON(sizeof(UtpCmdRsp) != 40); + QEMU_BUILD_BUG_ON(sizeof(UtpUpiuRsp) != 288); +} +#endif diff --git a/include/block/write-threshold.h b/include/block/write-threshold.h index f50f923e7e..63d1583887 100644 --- a/include/block/write-threshold.h +++ b/include/block/write-threshold.h @@ -13,8 +13,6 @@ #ifndef BLOCK_WRITE_THRESHOLD_H #define BLOCK_WRITE_THRESHOLD_H -#include "qemu/typedefs.h" - /* * bdrv_write_threshold_set: * diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h index 8c420fa36e..ecef182835 100644 --- a/include/chardev/char-fe.h +++ b/include/chardev/char-fe.h @@ -7,8 +7,12 @@ typedef void IOEventHandler(void *opaque, QEMUChrEvent event); typedef int BackendChangeHandler(void *opaque); -/* This is the backend as seen by frontend, the actual backend is - * Chardev */ +/** + * struct CharBackend - back end as seen by front end + * @fe_is_open: the front end is ready for IO + * + * The actual backend is Chardev + */ struct CharBackend { Chardev *chr; IOEventHandler *chr_event; @@ -17,7 +21,7 @@ struct CharBackend { BackendChangeHandler *chr_be_change; void *opaque; int tag; - int fe_open; + bool fe_is_open; }; /** @@ -78,7 +82,7 @@ bool qemu_chr_fe_backend_open(CharBackend *be); * is not supported and will not be attempted * @opaque: an opaque pointer for the callbacks * @context: a main loop context or NULL for the default - * @set_open: whether to call qemu_chr_fe_set_open() implicitely when + * @set_open: whether to call qemu_chr_fe_set_open() implicitly when * any of the handler is non-NULL * @sync_state: whether to issue event callback with updated state * @@ -138,7 +142,7 @@ void qemu_chr_fe_disconnect(CharBackend *be); /** * qemu_chr_fe_wait_connected: * - * Wait for characted backend to be connected, return < 0 on error or + * Wait for character backend to be connected, return < 0 on error or * if no associated Chardev. */ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp); @@ -156,12 +160,13 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo); /** * qemu_chr_fe_set_open: + * @be: a CharBackend + * @is_open: the front end open status * - * Set character frontend open status. This is an indication that the - * front end is ready (or not) to begin doing I/O. - * Without associated Chardev, do nothing. + * This is an indication that the front end is ready (or not) to begin + * doing I/O. Without associated Chardev, do nothing. */ -void qemu_chr_fe_set_open(CharBackend *be, int fe_open); +void qemu_chr_fe_set_open(CharBackend *be, bool is_open); /** * qemu_chr_fe_printf: @@ -175,6 +180,20 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...) G_GNUC_PRINTF(2, 3); +/** + * FEWatchFunc: a #GSourceFunc called when any conditions requested by + * qemu_chr_fe_add_watch() is satisfied. + * @do_not_use: depending on the underlying chardev, a GIOChannel or a + * QIOChannel. DO NOT USE! + * @cond: bitwise combination of conditions watched and satisfied + * before calling this callback. + * @data: user data passed at creation to qemu_chr_fe_add_watch(). Can + * be NULL. + * + * Returns: G_SOURCE_REMOVE if the GSource should be removed from the + * main loop, or G_SOURCE_CONTINUE to leave the GSource in + * the main loop. + */ typedef gboolean (*FEWatchFunc)(void *do_not_use, GIOCondition condition, void *data); /** diff --git a/include/chardev/char.h b/include/chardev/char.h index a319b5fdff..01df55f9e8 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -186,7 +186,7 @@ int qemu_chr_be_can_write(Chardev *s); * the caller should call @qemu_chr_be_can_write to determine how much data * the front end can currently accept. */ -void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len); +void qemu_chr_be_write(Chardev *s, const uint8_t *buf, int len); /** * qemu_chr_be_write_impl: @@ -195,7 +195,7 @@ void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len); * * Implementation of back end writing. Used by replay module. */ -void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len); +void qemu_chr_be_write_impl(Chardev *s, const uint8_t *buf, int len); /** * qemu_chr_be_update_read_handlers: @@ -320,7 +320,4 @@ GSource *qemu_chr_timeout_add_ms(Chardev *chr, guint ms, void suspend_mux_open(void); void resume_mux_open(void); -/* console.c */ -void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp); - #endif diff --git a/include/crypto/aes-round.h b/include/crypto/aes-round.h new file mode 100644 index 0000000000..854fb0966a --- /dev/null +++ b/include/crypto/aes-round.h @@ -0,0 +1,164 @@ +/* + * AES round fragments, generic version + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (C) 2023 Linaro, Ltd. + */ + +#ifndef CRYPTO_AES_ROUND_H +#define CRYPTO_AES_ROUND_H + +/* Hosts with acceleration will usually need a 16-byte vector type. */ +typedef uint8_t AESStateVec __attribute__((vector_size(16))); + +typedef union { + uint8_t b[16]; + uint32_t w[4]; + uint64_t d[2]; + AESStateVec v; +} AESState; + +#include "host/crypto/aes-round.h" + +/* + * Perform MixColumns. + */ + +void aesenc_MC_gen(AESState *ret, const AESState *st); +void aesenc_MC_genrev(AESState *ret, const AESState *st); + +static inline void aesenc_MC(AESState *r, const AESState *st, bool be) +{ + if (HAVE_AES_ACCEL) { + aesenc_MC_accel(r, st, be); + } else if (HOST_BIG_ENDIAN == be) { + aesenc_MC_gen(r, st); + } else { + aesenc_MC_genrev(r, st); + } +} + +/* + * Perform SubBytes + ShiftRows + AddRoundKey. + */ + +void aesenc_SB_SR_AK_gen(AESState *ret, const AESState *st, + const AESState *rk); +void aesenc_SB_SR_AK_genrev(AESState *ret, const AESState *st, + const AESState *rk); + +static inline void aesenc_SB_SR_AK(AESState *r, const AESState *st, + const AESState *rk, bool be) +{ + if (HAVE_AES_ACCEL) { + aesenc_SB_SR_AK_accel(r, st, rk, be); + } else if (HOST_BIG_ENDIAN == be) { + aesenc_SB_SR_AK_gen(r, st, rk); + } else { + aesenc_SB_SR_AK_genrev(r, st, rk); + } +} + +/* + * Perform SubBytes + ShiftRows + MixColumns + AddRoundKey. + */ + +void aesenc_SB_SR_MC_AK_gen(AESState *ret, const AESState *st, + const AESState *rk); +void aesenc_SB_SR_MC_AK_genrev(AESState *ret, const AESState *st, + const AESState *rk); + +static inline void aesenc_SB_SR_MC_AK(AESState *r, const AESState *st, + const AESState *rk, bool be) +{ + if (HAVE_AES_ACCEL) { + aesenc_SB_SR_MC_AK_accel(r, st, rk, be); + } else if (HOST_BIG_ENDIAN == be) { + aesenc_SB_SR_MC_AK_gen(r, st, rk); + } else { + aesenc_SB_SR_MC_AK_genrev(r, st, rk); + } +} + +/* + * Perform InvMixColumns. + */ + +void aesdec_IMC_gen(AESState *ret, const AESState *st); +void aesdec_IMC_genrev(AESState *ret, const AESState *st); + +static inline void aesdec_IMC(AESState *r, const AESState *st, bool be) +{ + if (HAVE_AES_ACCEL) { + aesdec_IMC_accel(r, st, be); + } else if (HOST_BIG_ENDIAN == be) { + aesdec_IMC_gen(r, st); + } else { + aesdec_IMC_genrev(r, st); + } +} + +/* + * Perform InvSubBytes + InvShiftRows + AddRoundKey. + */ + +void aesdec_ISB_ISR_AK_gen(AESState *ret, const AESState *st, + const AESState *rk); +void aesdec_ISB_ISR_AK_genrev(AESState *ret, const AESState *st, + const AESState *rk); + +static inline void aesdec_ISB_ISR_AK(AESState *r, const AESState *st, + const AESState *rk, bool be) +{ + if (HAVE_AES_ACCEL) { + aesdec_ISB_ISR_AK_accel(r, st, rk, be); + } else if (HOST_BIG_ENDIAN == be) { + aesdec_ISB_ISR_AK_gen(r, st, rk); + } else { + aesdec_ISB_ISR_AK_genrev(r, st, rk); + } +} + +/* + * Perform InvSubBytes + InvShiftRows + AddRoundKey + InvMixColumns. + */ + +void aesdec_ISB_ISR_AK_IMC_gen(AESState *ret, const AESState *st, + const AESState *rk); +void aesdec_ISB_ISR_AK_IMC_genrev(AESState *ret, const AESState *st, + const AESState *rk); + +static inline void aesdec_ISB_ISR_AK_IMC(AESState *r, const AESState *st, + const AESState *rk, bool be) +{ + if (HAVE_AES_ACCEL) { + aesdec_ISB_ISR_AK_IMC_accel(r, st, rk, be); + } else if (HOST_BIG_ENDIAN == be) { + aesdec_ISB_ISR_AK_IMC_gen(r, st, rk); + } else { + aesdec_ISB_ISR_AK_IMC_genrev(r, st, rk); + } +} + +/* + * Perform InvSubBytes + InvShiftRows + InvMixColumns + AddRoundKey. + */ + +void aesdec_ISB_ISR_IMC_AK_gen(AESState *ret, const AESState *st, + const AESState *rk); +void aesdec_ISB_ISR_IMC_AK_genrev(AESState *ret, const AESState *st, + const AESState *rk); + +static inline void aesdec_ISB_ISR_IMC_AK(AESState *r, const AESState *st, + const AESState *rk, bool be) +{ + if (HAVE_AES_ACCEL) { + aesdec_ISB_ISR_IMC_AK_accel(r, st, rk, be); + } else if (HOST_BIG_ENDIAN == be) { + aesdec_ISB_ISR_IMC_AK_gen(r, st, rk); + } else { + aesdec_ISB_ISR_IMC_AK_genrev(r, st, rk); + } +} + +#endif /* CRYPTO_AES_ROUND_H */ diff --git a/include/crypto/aes.h b/include/crypto/aes.h index ba297d6a73..381f24c902 100644 --- a/include/crypto/aes.h +++ b/include/crypto/aes.h @@ -18,46 +18,23 @@ typedef struct aes_key_st AES_KEY; #define AES_decrypt QEMU_AES_decrypt int AES_set_encrypt_key(const unsigned char *userKey, const int bits, - AES_KEY *key); + AES_KEY *key); int AES_set_decrypt_key(const unsigned char *userKey, const int bits, - AES_KEY *key); + AES_KEY *key); void AES_encrypt(const unsigned char *in, unsigned char *out, - const AES_KEY *key); + const AES_KEY *key); void AES_decrypt(const unsigned char *in, unsigned char *out, - const AES_KEY *key); + const AES_KEY *key); extern const uint8_t AES_sbox[256]; extern const uint8_t AES_isbox[256]; -/* AES ShiftRows and InvShiftRows */ -extern const uint8_t AES_shifts[16]; -extern const uint8_t AES_ishifts[16]; - -/* AES InvMixColumns */ -/* AES_imc[x][0] = [x].[0e, 09, 0d, 0b]; */ -/* AES_imc[x][1] = [x].[0b, 0e, 09, 0d]; */ -/* AES_imc[x][2] = [x].[0d, 0b, 0e, 09]; */ -/* AES_imc[x][3] = [x].[09, 0d, 0b, 0e]; */ -extern const uint32_t AES_imc[256][4]; - /* AES_Te0[x] = S [x].[02, 01, 01, 03]; -AES_Te1[x] = S [x].[03, 02, 01, 01]; -AES_Te2[x] = S [x].[01, 03, 02, 01]; -AES_Te3[x] = S [x].[01, 01, 03, 02]; -AES_Te4[x] = S [x].[01, 01, 01, 01]; - AES_Td0[x] = Si[x].[0e, 09, 0d, 0b]; -AES_Td1[x] = Si[x].[0b, 0e, 09, 0d]; -AES_Td2[x] = Si[x].[0d, 0b, 0e, 09]; -AES_Td3[x] = Si[x].[09, 0d, 0b, 0e]; -AES_Td4[x] = Si[x].[01, 01, 01, 01]; */ -extern const uint32_t AES_Te0[256], AES_Te1[256], AES_Te2[256], - AES_Te3[256], AES_Te4[256]; -extern const uint32_t AES_Td0[256], AES_Td1[256], AES_Td2[256], - AES_Td3[256], AES_Td4[256]; +extern const uint32_t AES_Te0[256], AES_Td0[256]; #endif diff --git a/include/crypto/akcipher.h b/include/crypto/akcipher.h index 51f5fa2774..8756105f22 100644 --- a/include/crypto/akcipher.h +++ b/include/crypto/akcipher.h @@ -30,7 +30,7 @@ typedef struct QCryptoAkCipher QCryptoAkCipher; * qcrypto_akcipher_supports: * @opts: the asymmetric key algorithm and related options * - * Determine if asymmetric key cipher decribed with @opts is + * Determine if asymmetric key cipher described with @opts is * supported by the current configured build * * Returns: true if it is supported, false otherwise. @@ -153,6 +153,27 @@ int qcrypto_akcipher_max_dgst_len(QCryptoAkCipher *akcipher); */ void qcrypto_akcipher_free(QCryptoAkCipher *akcipher); +/** + * qcrypto_akcipher_export_p8info: + * @opts: the options of the akcipher to be exported. + * @key: the original key of the akcipher to be exported. + * @keylen: length of the 'key' + * @dst: output parameter, if export succeed, *dst is set to the + * PKCS#8 encoded private key, caller MUST free this key with + * g_free after use. + * @dst_len: output parameter, indicates the length of PKCS#8 encoded + * key. + * + * Export the akcipher into DER encoded pkcs#8 private key info, expects + * |key| stores a valid asymmetric PRIVATE key. + * + * Returns: 0 for succeed, otherwise -1 is returned. + */ +int qcrypto_akcipher_export_p8info(const QCryptoAkCipherOptions *opts, + uint8_t *key, size_t keylen, + uint8_t **dst, size_t *dst_len, + Error **errp); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipher, qcrypto_akcipher_free) #endif /* QCRYPTO_AKCIPHER_H */ diff --git a/include/crypto/block.h b/include/crypto/block.h index 7a65e8e402..92e823c9f2 100644 --- a/include/crypto/block.h +++ b/include/crypto/block.h @@ -29,24 +29,24 @@ typedef struct QCryptoBlock QCryptoBlock; /* See also QCryptoBlockFormat, QCryptoBlockCreateOptions * and QCryptoBlockOpenOptions in qapi/crypto.json */ -typedef ssize_t (*QCryptoBlockReadFunc)(QCryptoBlock *block, - size_t offset, - uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp); +typedef int (*QCryptoBlockReadFunc)(QCryptoBlock *block, + size_t offset, + uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp); -typedef ssize_t (*QCryptoBlockInitFunc)(QCryptoBlock *block, - size_t headerlen, - void *opaque, - Error **errp); +typedef int (*QCryptoBlockInitFunc)(QCryptoBlock *block, + size_t headerlen, + void *opaque, + Error **errp); -typedef ssize_t (*QCryptoBlockWriteFunc)(QCryptoBlock *block, - size_t offset, - const uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp); +typedef int (*QCryptoBlockWriteFunc)(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp); /** * qcrypto_block_has_format: @@ -66,6 +66,7 @@ bool qcrypto_block_has_format(QCryptoBlockFormat format, typedef enum { QCRYPTO_BLOCK_OPEN_NO_IO = (1 << 0), + QCRYPTO_BLOCK_OPEN_DETACHED = (1 << 1), } QCryptoBlockOpenFlags; /** @@ -95,6 +96,10 @@ typedef enum { * metadata such as the payload offset. There will be * no cipher or ivgen objects available. * + * If @flags contains QCRYPTO_BLOCK_OPEN_DETACHED then + * the open process will be optimized to skip the LUKS + * payload overlap check. + * * If any part of initializing the encryption context * fails an error will be returned. This could be due * to the volume being in the wrong format, a cipher @@ -111,6 +116,10 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, size_t n_threads, Error **errp); +typedef enum { + QCRYPTO_BLOCK_CREATE_DETACHED = (1 << 0), +} QCryptoBlockCreateFlags; + /** * qcrypto_block_create: * @options: the encryption options @@ -118,6 +127,7 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, * @initfunc: callback for initializing volume header * @writefunc: callback for writing data to the volume header * @opaque: data to pass to @initfunc and @writefunc + * @flags: bitmask of QCryptoBlockCreateFlags values * @errp: pointer to a NULL-initialized error object * * Create a new block encryption object for initializing @@ -129,6 +139,11 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, * generating new master keys, etc as required. Any existing * data present on the volume will be irrevocably destroyed. * + * If @flags contains QCRYPTO_BLOCK_CREATE_DETACHED then + * the open process will set the payload_offset_sector to 0 + * to specify the starting point for the read/write of a + * detached LUKS header image. + * * If any part of initializing the encryption context * fails an error will be returned. This could be due * to the volume being in the wrong format, a cipher @@ -142,6 +157,7 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, QCryptoBlockInitFunc initfunc, QCryptoBlockWriteFunc writefunc, void *opaque, + unsigned int flags, Error **errp); /** diff --git a/include/crypto/clmul.h b/include/crypto/clmul.h new file mode 100644 index 0000000000..446931fe05 --- /dev/null +++ b/include/crypto/clmul.h @@ -0,0 +1,83 @@ +/* + * Carry-less multiply operations. + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (C) 2023 Linaro, Ltd. + */ + +#ifndef CRYPTO_CLMUL_H +#define CRYPTO_CLMUL_H + +#include "qemu/int128.h" +#include "host/crypto/clmul.h" + +/** + * clmul_8x8_low: + * + * Perform eight 8x8->8 carry-less multiplies. + */ +uint64_t clmul_8x8_low(uint64_t, uint64_t); + +/** + * clmul_8x4_even: + * + * Perform four 8x8->16 carry-less multiplies. + * The odd bytes of the inputs are ignored. + */ +uint64_t clmul_8x4_even(uint64_t, uint64_t); + +/** + * clmul_8x4_odd: + * + * Perform four 8x8->16 carry-less multiplies. + * The even bytes of the inputs are ignored. + */ +uint64_t clmul_8x4_odd(uint64_t, uint64_t); + +/** + * clmul_8x4_packed: + * + * Perform four 8x8->16 carry-less multiplies. + */ +uint64_t clmul_8x4_packed(uint32_t, uint32_t); + +/** + * clmul_16x2_even: + * + * Perform two 16x16->32 carry-less multiplies. + * The odd words of the inputs are ignored. + */ +uint64_t clmul_16x2_even(uint64_t, uint64_t); + +/** + * clmul_16x2_odd: + * + * Perform two 16x16->32 carry-less multiplies. + * The even words of the inputs are ignored. + */ +uint64_t clmul_16x2_odd(uint64_t, uint64_t); + +/** + * clmul_32: + * + * Perform a 32x32->64 carry-less multiply. + */ +uint64_t clmul_32(uint32_t, uint32_t); + +/** + * clmul_64: + * + * Perform a 64x64->128 carry-less multiply. + */ +Int128 clmul_64_gen(uint64_t, uint64_t); + +static inline Int128 clmul_64(uint64_t a, uint64_t b) +{ + if (HAVE_CLMUL_ACCEL) { + return clmul_64_accel(a, b); + } else { + return clmul_64_gen(a, b); + } +} + +#endif /* CRYPTO_CLMUL_H */ diff --git a/include/crypto/desrfb.h b/include/crypto/desrfb.h index 7ca596c387..af8d12234d 100644 --- a/include/crypto/desrfb.h +++ b/include/crypto/desrfb.h @@ -15,30 +15,30 @@ /* d3des.h - * - * Headers and defines for d3des.c - * Graven Imagery, 1992. + * Headers and defines for d3des.c + * Graven Imagery, 1992. * * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge - * (GEnie : OUTER; CIS : [71755,204]) + * (GEnie : OUTER; CIS : [71755,204]) */ -#define EN0 0 /* MODE == encrypt */ -#define DE1 1 /* MODE == decrypt */ +#define EN0 0 /* MODE == encrypt */ +#define DE1 1 /* MODE == decrypt */ void deskey(unsigned char *, int); -/* hexkey[8] MODE +/* hexkey[8] MODE * Sets the internal key register according to the hexadecimal * key contained in the 8 bytes of hexkey, according to the DES, * for encryption or decryption according to MODE. */ void usekey(unsigned long *); -/* cookedkey[32] +/* cookedkey[32] * Loads the internal key register with the data in cookedkey. */ void des(unsigned char *, unsigned char *); -/* from[8] to[8] +/* from[8] to[8] * Encrypts/Decrypts (according to the key currently loaded in the * internal key register) one block of eight bytes at address 'from' * into the block at address 'to'. They can be the same. diff --git a/include/crypto/ivgen.h b/include/crypto/ivgen.h index e41521519c..a09d5732da 100644 --- a/include/crypto/ivgen.h +++ b/include/crypto/ivgen.h @@ -32,7 +32,7 @@ * sector. * * - * Encrypting block data with initialiation vectors + * Encrypting block data with initialization vectors * * uint8_t *data = ....data to encrypt... * size_t ndata = XXX; @@ -147,7 +147,7 @@ QCryptoIVGen *qcrypto_ivgen_new(QCryptoIVGenAlgorithm alg, * @niv: the number of bytes in @iv * @errp: pointer to a NULL-initialized error object * - * Calculate a new initialiation vector for the data + * Calculate a new initialization vector for the data * to be stored in sector @sector. The IV will be * written into the buffer @iv of size @niv. * diff --git a/include/crypto/secret_common.h b/include/crypto/secret_common.h index 42c7ff7af6..a0a22e1abd 100644 --- a/include/crypto/secret_common.h +++ b/include/crypto/secret_common.h @@ -48,13 +48,11 @@ struct QCryptoSecretCommonClass { }; -extern int qcrypto_secret_lookup(const char *secretid, - uint8_t **data, - size_t *datalen, - Error **errp); -extern char *qcrypto_secret_lookup_as_utf8(const char *secretid, - Error **errp); -extern char *qcrypto_secret_lookup_as_base64(const char *secretid, - Error **errp); +int qcrypto_secret_lookup(const char *secretid, + uint8_t **data, + size_t *datalen, + Error **errp); +char *qcrypto_secret_lookup_as_utf8(const char *secretid, Error **errp); +char *qcrypto_secret_lookup_as_base64(const char *secretid, Error **errp); #endif /* QCRYPTO_SECRET_COMMON_H */ diff --git a/include/crypto/sm4.h b/include/crypto/sm4.h index 9bd3ebc62e..382b26d922 100644 --- a/include/crypto/sm4.h +++ b/include/crypto/sm4.h @@ -2,5 +2,14 @@ #define QEMU_SM4_H extern const uint8_t sm4_sbox[256]; +extern const uint32_t sm4_ck[32]; + +static inline uint32_t sm4_subword(uint32_t word) +{ + return sm4_sbox[word & 0xff] | + sm4_sbox[(word >> 8) & 0xff] << 8 | + sm4_sbox[(word >> 16) & 0xff] << 16 | + sm4_sbox[(word >> 24) & 0xff] << 24; +} #endif diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h index 15b9cef086..571049bd0e 100644 --- a/include/crypto/tlssession.h +++ b/include/crypto/tlssession.h @@ -248,6 +248,17 @@ ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess, char *buf, size_t len); +/** + * qcrypto_tls_session_check_pending: + * @sess: the TLS session object + * + * Check if there are unread data in the TLS buffers that have + * already been read from the underlying data source. + * + * Returns: the number of bytes available or zero + */ +size_t qcrypto_tls_session_check_pending(QCryptoTLSSession *sess); + /** * qcrypto_tls_session_handshake: * @sess: the TLS session object diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index 512cc3af92..7cc3c64850 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -11,10 +11,6 @@ #include "qemu/bswap.h" -#ifdef __cplusplus -extern "C" { -#endif - typedef void *PTR; typedef uint64_t bfd_vma; typedef int64_t bfd_signed_vma; @@ -192,20 +188,20 @@ enum bfd_architecture #define bfd_mach_alpha_ev5 0x20 #define bfd_mach_alpha_ev6 0x30 bfd_arch_arm, /* Advanced Risc Machines ARM */ -#define bfd_mach_arm_unknown 0 -#define bfd_mach_arm_2 1 -#define bfd_mach_arm_2a 2 -#define bfd_mach_arm_3 3 -#define bfd_mach_arm_3M 4 -#define bfd_mach_arm_4 5 -#define bfd_mach_arm_4T 6 -#define bfd_mach_arm_5 7 -#define bfd_mach_arm_5T 8 -#define bfd_mach_arm_5TE 9 -#define bfd_mach_arm_XScale 10 -#define bfd_mach_arm_ep9312 11 -#define bfd_mach_arm_iWMMXt 12 -#define bfd_mach_arm_iWMMXt2 13 +#define bfd_mach_arm_unknown 0 +#define bfd_mach_arm_2 1 +#define bfd_mach_arm_2a 2 +#define bfd_mach_arm_3 3 +#define bfd_mach_arm_3M 4 +#define bfd_mach_arm_4 5 +#define bfd_mach_arm_4T 6 +#define bfd_mach_arm_5 7 +#define bfd_mach_arm_5T 8 +#define bfd_mach_arm_5TE 9 +#define bfd_mach_arm_XScale 10 +#define bfd_mach_arm_ep9312 11 +#define bfd_mach_arm_iWMMXt 12 +#define bfd_mach_arm_iWMMXt2 13 bfd_arch_ns32k, /* National Semiconductors ns32000 */ bfd_arch_w65, /* WDC 65816 */ bfd_arch_tic30, /* Texas Instruments TMS320C30 */ @@ -262,7 +258,7 @@ enum bfd_architecture bfd_arch_ia64, /* HP/Intel ia64 */ #define bfd_mach_ia64_elf64 64 #define bfd_mach_ia64_elf32 32 - bfd_arch_nios2, /* Nios II */ + bfd_arch_nios2, /* Nios II */ #define bfd_mach_nios2 0 #define bfd_mach_nios2r1 1 #define bfd_mach_nios2r2 2 @@ -290,14 +286,14 @@ typedef int (*fprintf_function)(FILE *f, const char *fmt, ...) G_GNUC_PRINTF(2, 3); enum dis_insn_type { - dis_noninsn, /* Not a valid instruction */ - dis_nonbranch, /* Not a branch instruction */ - dis_branch, /* Unconditional branch */ - dis_condbranch, /* Conditional branch */ - dis_jsr, /* Jump to subroutine */ - dis_condjsr, /* Conditional jump to subroutine */ - dis_dref, /* Data reference instruction */ - dis_dref2 /* Two data references in instruction */ + dis_noninsn, /* Not a valid instruction */ + dis_nonbranch, /* Not a branch instruction */ + dis_branch, /* Unconditional branch */ + dis_condbranch, /* Conditional branch */ + dis_jsr, /* Jump to subroutine */ + dis_condjsr, /* Conditional jump to subroutine */ + dis_dref, /* Data reference instruction */ + dis_dref2 /* Two data references in instruction */ }; /* This struct is passed into the instruction decoding routine, @@ -340,8 +336,8 @@ typedef struct disassemble_info { The top 16 bits are reserved for public use (and are documented here). The bottom 16 bits are for the internal use of the disassembler. */ unsigned long flags; -#define INSN_HAS_RELOC 0x80000000 -#define INSN_ARM_BE32 0x00010000 +#define INSN_HAS_RELOC 0x80000000 +#define INSN_ARM_BE32 0x00010000 PTR private_data; /* Function used to get bytes to disassemble. MEMADDR is the @@ -351,7 +347,7 @@ typedef struct disassemble_info { Returns an errno value or 0 for success. */ int (*read_memory_func) (bfd_vma memaddr, bfd_byte *myaddr, int length, - struct disassemble_info *info); + struct disassemble_info *info); /* Function which should be called if we get an error that we can't recover from. STATUS is the errno value from read_memory_func and @@ -405,20 +401,28 @@ typedef struct disassemble_info { To determine whether this decoder supports this information, set insn_info_valid to 0, decode an instruction, then check it. */ - char insn_info_valid; /* Branch info has been set. */ - char branch_delay_insns; /* How many sequential insn's will run before - a branch takes effect. (0 = normal) */ - char data_size; /* Size of data reference in insn, in bytes */ - enum dis_insn_type insn_type; /* Type of instruction */ - bfd_vma target; /* Target address of branch or dref, if known; - zero if unknown. */ - bfd_vma target2; /* Second target address for dref2 */ + char insn_info_valid; /* Branch info has been set. */ + char branch_delay_insns; /* How many sequential insn's will run before + a branch takes effect. (0 = normal) */ + char data_size; /* Size of data reference in insn, in bytes */ + enum dis_insn_type insn_type; /* Type of instruction */ + bfd_vma target; /* Target address of branch or dref, if known; + zero if unknown. */ + bfd_vma target2; /* Second target address for dref2 */ /* Command line options specific to the target disassembler. */ char * disassembler_options; + /* + * When true instruct the disassembler it may preface the + * disassembly with the opcodes values if it wants to. This is + * mainly for the benefit of the plugin interface which doesn't want + * that. + */ + bool show_opcodes; + /* Field intended to be used by targets in any way they deem suitable. */ - int64_t target_info; + void *target_info; /* Options for Capstone disassembly. */ int cap_arch; @@ -532,8 +536,4 @@ static inline bfd_vma bfd_getb16(const bfd_byte *addr) typedef bool bfd_boolean; -#ifdef __cplusplus -} -#endif - #endif /* DISAS_DIS_ASM_H */ diff --git a/include/disas/disas.h b/include/disas/disas.h index d363e95ede..176775eff7 100644 --- a/include/disas/disas.h +++ b/include/disas/disas.h @@ -1,34 +1,23 @@ #ifndef QEMU_DISAS_H #define QEMU_DISAS_H -#include "exec/hwaddr.h" - -#ifdef NEED_CPU_H -#include "cpu.h" - /* Disassemble this for me please... (debugging). */ -void disas(FILE *out, const void *code, unsigned long size); -void target_disas(FILE *out, CPUState *cpu, target_ulong code, - target_ulong size); +void disas(FILE *out, const void *code, size_t size); +void target_disas(FILE *out, CPUState *cpu, uint64_t code, size_t size); -void monitor_disas(Monitor *mon, CPUState *cpu, - target_ulong pc, int nb_insn, int is_physical); +void monitor_disas(Monitor *mon, CPUState *cpu, uint64_t pc, + int nb_insn, bool is_physical); char *plugin_disas(CPUState *cpu, uint64_t addr, size_t size); /* Look up symbol for debugging purpose. Returns "" if unknown. */ -const char *lookup_symbol(target_ulong orig_addr); -#endif +const char *lookup_symbol(uint64_t orig_addr); struct syminfo; struct elf32_sym; struct elf64_sym; -#if defined(CONFIG_USER_ONLY) -typedef const char *(*lookup_symbol_t)(struct syminfo *s, target_ulong orig_addr); -#else -typedef const char *(*lookup_symbol_t)(struct syminfo *s, hwaddr orig_addr); -#endif +typedef const char *(*lookup_symbol_t)(struct syminfo *s, uint64_t orig_addr); struct syminfo { lookup_symbol_t lookup_symbol; diff --git a/include/elf.h b/include/elf.h index 0dd47cf908..0ae8eceff5 100644 --- a/include/elf.h +++ b/include/elf.h @@ -11,26 +11,27 @@ typedef uint32_t Elf32_Word; /* 64-bit ELF base types. */ typedef uint64_t Elf64_Addr; typedef uint16_t Elf64_Half; -typedef int16_t Elf64_SHalf; +typedef int16_t Elf64_SHalf; typedef uint64_t Elf64_Off; -typedef int32_t Elf64_Sword; +typedef int32_t Elf64_Sword; typedef uint32_t Elf64_Word; typedef uint64_t Elf64_Xword; typedef int64_t Elf64_Sxword; /* These constants are for the segment types stored in the image headers */ -#define PT_NULL 0 -#define PT_LOAD 1 -#define PT_DYNAMIC 2 -#define PT_INTERP 3 -#define PT_NOTE 4 -#define PT_SHLIB 5 -#define PT_PHDR 6 -#define PT_LOOS 0x60000000 -#define PT_HIOS 0x6fffffff -#define PT_LOPROC 0x70000000 -#define PT_HIPROC 0x7fffffff +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_LOOS 0x60000000 +#define PT_HIOS 0x6fffffff +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff +#define PT_GNU_STACK (PT_LOOS + 0x474e551) #define PT_GNU_PROPERTY (PT_LOOS + 0x474e553) #define PT_MIPS_REGINFO 0x70000000 @@ -40,34 +41,34 @@ typedef int64_t Elf64_Sxword; /* Flags in the e_flags field of the header */ /* MIPS architecture level. */ -#define EF_MIPS_ARCH 0xf0000000 +#define EF_MIPS_ARCH 0xf0000000 /* Legal values for MIPS architecture level. */ -#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ -#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ -#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ -#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ -#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ -#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */ -#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */ -#define EF_MIPS_ARCH_32R2 0x70000000 /* MIPS32r2 code. */ -#define EF_MIPS_ARCH_64R2 0x80000000 /* MIPS64r2 code. */ -#define EF_MIPS_ARCH_32R6 0x90000000 /* MIPS32r6 code. */ -#define EF_MIPS_ARCH_64R6 0xa0000000 /* MIPS64r6 code. */ +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */ +#define EF_MIPS_ARCH_32R2 0x70000000 /* MIPS32r2 code. */ +#define EF_MIPS_ARCH_64R2 0x80000000 /* MIPS64r2 code. */ +#define EF_MIPS_ARCH_32R6 0x90000000 /* MIPS32r6 code. */ +#define EF_MIPS_ARCH_64R6 0xa0000000 /* MIPS64r6 code. */ /* The ABI of a file. */ -#define EF_MIPS_ABI_O32 0x00001000 /* O32 ABI. */ -#define EF_MIPS_ABI_O64 0x00002000 /* O32 extended for 64 bit. */ +#define EF_MIPS_ABI_O32 0x00001000 /* O32 ABI. */ +#define EF_MIPS_ABI_O64 0x00002000 /* O32 extended for 64 bit. */ -#define EF_MIPS_NOREORDER 0x00000001 -#define EF_MIPS_PIC 0x00000002 -#define EF_MIPS_CPIC 0x00000004 -#define EF_MIPS_ABI2 0x00000020 -#define EF_MIPS_OPTIONS_FIRST 0x00000080 -#define EF_MIPS_32BITMODE 0x00000100 -#define EF_MIPS_ABI 0x0000f000 -#define EF_MIPS_FP64 0x00000200 -#define EF_MIPS_NAN2008 0x00000400 +#define EF_MIPS_NOREORDER 0x00000001 +#define EF_MIPS_PIC 0x00000002 +#define EF_MIPS_CPIC 0x00000004 +#define EF_MIPS_ABI2 0x00000020 +#define EF_MIPS_OPTIONS_FIRST 0x00000080 +#define EF_MIPS_32BITMODE 0x00000100 +#define EF_MIPS_ABI 0x0000f000 +#define EF_MIPS_FP64 0x00000200 +#define EF_MIPS_NAN2008 0x00000400 /* MIPS machine variant */ #define EF_MIPS_MACH_NONE 0x00000000 /* A standard MIPS implementation */ @@ -128,165 +129,165 @@ typedef struct mips_elf_abiflags_v0 { #define ET_HIPROC 0xffff /* These constants define the various ELF target machines */ -#define EM_NONE 0 -#define EM_M32 1 -#define EM_SPARC 2 -#define EM_386 3 -#define EM_68K 4 -#define EM_88K 5 -#define EM_486 6 /* Perhaps disused */ -#define EM_860 7 +#define EM_NONE 0 +#define EM_M32 1 +#define EM_SPARC 2 +#define EM_386 3 +#define EM_68K 4 +#define EM_88K 5 +#define EM_486 6 /* Perhaps disused */ +#define EM_860 7 -#define EM_MIPS 8 /* MIPS R3000 (officially, big-endian only) */ +#define EM_MIPS 8 /* MIPS R3000 (officially, big-endian only) */ -#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */ +#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */ -#define EM_PARISC 15 /* HPPA */ +#define EM_PARISC 15 /* HPPA */ -#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ -#define EM_PPC 20 /* PowerPC */ -#define EM_PPC64 21 /* PowerPC64 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC64 */ -#define EM_ARM 40 /* ARM */ +#define EM_ARM 40 /* ARM */ -#define EM_SH 42 /* SuperH */ +#define EM_SH 42 /* SuperH */ -#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ -#define EM_TRICORE 44 /* Infineon TriCore */ +#define EM_TRICORE 44 /* Infineon TriCore */ -#define EM_IA_64 50 /* HP/Intel IA-64 */ +#define EM_IA_64 50 /* HP/Intel IA-64 */ -#define EM_X86_64 62 /* AMD x86-64 */ +#define EM_X86_64 62 /* AMD x86-64 */ -#define EM_S390 22 /* IBM S/390 */ +#define EM_S390 22 /* IBM S/390 */ -#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ -#define EM_AVR 83 /* AVR 8-bit microcontroller */ +#define EM_AVR 83 /* AVR 8-bit microcontroller */ -#define EM_V850 87 /* NEC v850 */ +#define EM_V850 87 /* NEC v850 */ -#define EM_H8_300H 47 /* Hitachi H8/300H */ -#define EM_H8S 48 /* Hitachi H8S */ -#define EM_LATTICEMICO32 138 /* LatticeMico32 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_LATTICEMICO32 138 /* LatticeMico32 */ -#define EM_OPENRISC 92 /* OpenCores OpenRISC */ +#define EM_OPENRISC 92 /* OpenCores OpenRISC */ -#define EM_HEXAGON 164 /* Qualcomm Hexagon */ +#define EM_HEXAGON 164 /* Qualcomm Hexagon */ -#define EM_RX 173 /* Renesas RX family */ +#define EM_RX 173 /* Renesas RX family */ -#define EM_MCST_ELBRUS 175 /* MCST Elbrus */ -#define EM_E2K_OLD 49 /* `e_machine' used in old binaries for E2K */ +#define EM_MCST_ELBRUS 175 /* MCST Elbrus */ +#define EM_E2K_OLD 49 /* `e_machine' used in old binaries for E2K */ -#define EM_RISCV 243 /* RISC-V */ +#define EM_RISCV 243 /* RISC-V */ -#define EM_NANOMIPS 249 /* Wave Computing nanoMIPS */ +#define EM_NANOMIPS 249 /* Wave Computing nanoMIPS */ -#define EM_LOONGARCH 258 /* LoongArch */ +#define EM_LOONGARCH 258 /* LoongArch */ /* * This is an interim value that we will use until the committee comes * up with a final number. */ -#define EM_ALPHA 0x9026 +#define EM_ALPHA 0x9026 /* Bogus old v850 magic number, used by old tools. */ -#define EM_CYGNUS_V850 0x9080 +#define EM_CYGNUS_V850 0x9080 /* * This is the old interim value for S/390 architecture */ -#define EM_S390_OLD 0xA390 +#define EM_S390_OLD 0xA390 -#define EM_ALTERA_NIOS2 113 /* Altera Nios II soft-core processor */ +#define EM_ALTERA_NIOS2 113 /* Altera Nios II soft-core processor */ -#define EM_MICROBLAZE 189 -#define EM_MICROBLAZE_OLD 0xBAAB +#define EM_MICROBLAZE 189 +#define EM_MICROBLAZE_OLD 0xBAAB -#define EM_XTENSA 94 /* Tensilica Xtensa */ +#define EM_XTENSA 94 /* Tensilica Xtensa */ -#define EM_AARCH64 183 +#define EM_AARCH64 183 -#define EF_AVR_MACH 0x7F /* Mask for AVR e_flags to get core type */ +#define EF_AVR_MACH 0x7F /* Mask for AVR e_flags to get core type */ /* This is the info that is needed to parse the dynamic section of the file */ -#define DT_NULL 0 -#define DT_NEEDED 1 -#define DT_PLTRELSZ 2 -#define DT_PLTGOT 3 -#define DT_HASH 4 -#define DT_STRTAB 5 -#define DT_SYMTAB 6 -#define DT_RELA 7 -#define DT_RELASZ 8 -#define DT_RELAENT 9 -#define DT_STRSZ 10 -#define DT_SYMENT 11 -#define DT_INIT 12 -#define DT_FINI 13 -#define DT_SONAME 14 -#define DT_RPATH 15 -#define DT_SYMBOLIC 16 -#define DT_REL 17 -#define DT_RELSZ 18 -#define DT_RELENT 19 -#define DT_PLTREL 20 -#define DT_DEBUG 21 -#define DT_TEXTREL 22 -#define DT_JMPREL 23 -#define DT_BINDNOW 24 -#define DT_INIT_ARRAY 25 -#define DT_FINI_ARRAY 26 -#define DT_INIT_ARRAYSZ 27 -#define DT_FINI_ARRAYSZ 28 -#define DT_RUNPATH 29 -#define DT_FLAGS 30 -#define DT_LOOS 0x6000000d -#define DT_HIOS 0x6ffff000 -#define DT_LOPROC 0x70000000 -#define DT_HIPROC 0x7fffffff +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_SONAME 14 +#define DT_RPATH 15 +#define DT_SYMBOLIC 16 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_DEBUG 21 +#define DT_TEXTREL 22 +#define DT_JMPREL 23 +#define DT_BINDNOW 24 +#define DT_INIT_ARRAY 25 +#define DT_FINI_ARRAY 26 +#define DT_INIT_ARRAYSZ 27 +#define DT_FINI_ARRAYSZ 28 +#define DT_RUNPATH 29 +#define DT_FLAGS 30 +#define DT_LOOS 0x6000000d +#define DT_HIOS 0x6ffff000 +#define DT_LOPROC 0x70000000 +#define DT_HIPROC 0x7fffffff /* DT_ entries which fall between DT_VALRNGLO and DT_VALRNDHI use the d_val field of the Elf*_Dyn structure. I.e. they contain scalars. */ -#define DT_VALRNGLO 0x6ffffd00 -#define DT_VALRNGHI 0x6ffffdff +#define DT_VALRNGLO 0x6ffffd00 +#define DT_VALRNGHI 0x6ffffdff /* DT_ entries which fall between DT_ADDRRNGLO and DT_ADDRRNGHI use the d_ptr field of the Elf*_Dyn structure. I.e. they contain pointers. */ -#define DT_ADDRRNGLO 0x6ffffe00 -#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_ADDRRNGHI 0x6ffffeff -#define DT_VERSYM 0x6ffffff0 -#define DT_RELACOUNT 0x6ffffff9 -#define DT_RELCOUNT 0x6ffffffa -#define DT_FLAGS_1 0x6ffffffb -#define DT_VERDEF 0x6ffffffc -#define DT_VERDEFNUM 0x6ffffffd -#define DT_VERNEED 0x6ffffffe -#define DT_VERNEEDNUM 0x6fffffff +#define DT_VERSYM 0x6ffffff0 +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa +#define DT_FLAGS_1 0x6ffffffb +#define DT_VERDEF 0x6ffffffc +#define DT_VERDEFNUM 0x6ffffffd +#define DT_VERNEED 0x6ffffffe +#define DT_VERNEEDNUM 0x6fffffff -#define DT_MIPS_RLD_VERSION 0x70000001 -#define DT_MIPS_TIME_STAMP 0x70000002 -#define DT_MIPS_ICHECKSUM 0x70000003 -#define DT_MIPS_IVERSION 0x70000004 -#define DT_MIPS_FLAGS 0x70000005 - #define RHF_NONE 0 - #define RHF_HARDWAY 1 - #define RHF_NOTPOT 2 -#define DT_MIPS_BASE_ADDRESS 0x70000006 -#define DT_MIPS_CONFLICT 0x70000008 -#define DT_MIPS_LIBLIST 0x70000009 -#define DT_MIPS_LOCAL_GOTNO 0x7000000a -#define DT_MIPS_CONFLICTNO 0x7000000b -#define DT_MIPS_LIBLISTNO 0x70000010 -#define DT_MIPS_SYMTABNO 0x70000011 -#define DT_MIPS_UNREFEXTNO 0x70000012 -#define DT_MIPS_GOTSYM 0x70000013 -#define DT_MIPS_HIPAGENO 0x70000014 -#define DT_MIPS_RLD_MAP 0x70000016 +#define DT_MIPS_RLD_VERSION 0x70000001 +#define DT_MIPS_TIME_STAMP 0x70000002 +#define DT_MIPS_ICHECKSUM 0x70000003 +#define DT_MIPS_IVERSION 0x70000004 +#define DT_MIPS_FLAGS 0x70000005 + #define RHF_NONE 0 + #define RHF_HARDWAY 1 + #define RHF_NOTPOT 2 +#define DT_MIPS_BASE_ADDRESS 0x70000006 +#define DT_MIPS_CONFLICT 0x70000008 +#define DT_MIPS_LIBLIST 0x70000009 +#define DT_MIPS_LOCAL_GOTNO 0x7000000a +#define DT_MIPS_CONFLICTNO 0x7000000b +#define DT_MIPS_LIBLISTNO 0x70000010 +#define DT_MIPS_SYMTABNO 0x70000011 +#define DT_MIPS_UNREFEXTNO 0x70000012 +#define DT_MIPS_GOTSYM 0x70000013 +#define DT_MIPS_HIPAGENO 0x70000014 +#define DT_MIPS_RLD_MAP 0x70000016 /* This info is needed when parsing the symbol table */ #define STB_LOCAL 0 @@ -299,61 +300,61 @@ typedef struct mips_elf_abiflags_v0 { #define STT_SECTION 3 #define STT_FILE 4 -#define ELF_ST_BIND(x) ((x) >> 4) -#define ELF_ST_TYPE(x) (((unsigned int) x) & 0xf) +#define ELF_ST_BIND(x) ((x) >> 4) +#define ELF_ST_TYPE(x) (((unsigned int) x) & 0xf) #define ELF_ST_INFO(bind, type) (((bind) << 4) | ((type) & 0xf)) -#define ELF32_ST_BIND(x) ELF_ST_BIND(x) -#define ELF32_ST_TYPE(x) ELF_ST_TYPE(x) -#define ELF64_ST_BIND(x) ELF_ST_BIND(x) -#define ELF64_ST_TYPE(x) ELF_ST_TYPE(x) +#define ELF32_ST_BIND(x) ELF_ST_BIND(x) +#define ELF32_ST_TYPE(x) ELF_ST_TYPE(x) +#define ELF64_ST_BIND(x) ELF_ST_BIND(x) +#define ELF64_ST_TYPE(x) ELF_ST_TYPE(x) /* Symbolic values for the entries in the auxiliary table put on the initial stack */ -#define AT_NULL 0 /* end of vector */ -#define AT_IGNORE 1 /* entry should be ignored */ -#define AT_EXECFD 2 /* file descriptor of program */ -#define AT_PHDR 3 /* program headers for program */ -#define AT_PHENT 4 /* size of program header entry */ -#define AT_PHNUM 5 /* number of program headers */ -#define AT_PAGESZ 6 /* system page size */ -#define AT_BASE 7 /* base address of interpreter */ -#define AT_FLAGS 8 /* flags */ -#define AT_ENTRY 9 /* entry point of program */ -#define AT_NOTELF 10 /* program is not ELF */ -#define AT_UID 11 /* real uid */ -#define AT_EUID 12 /* effective uid */ -#define AT_GID 13 /* real gid */ -#define AT_EGID 14 /* effective gid */ -#define AT_PLATFORM 15 /* string identifying CPU for optimizations */ -#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */ -#define AT_CLKTCK 17 /* frequency at which times() increments */ -#define AT_FPUCW 18 /* info about fpu initialization by kernel */ -#define AT_DCACHEBSIZE 19 /* data cache block size */ -#define AT_ICACHEBSIZE 20 /* instruction cache block size */ -#define AT_UCACHEBSIZE 21 /* unified cache block size */ -#define AT_IGNOREPPC 22 /* ppc only; entry should be ignored */ -#define AT_SECURE 23 /* boolean, was exec suid-like? */ -#define AT_BASE_PLATFORM 24 /* string identifying real platforms */ -#define AT_RANDOM 25 /* address of 16 random bytes */ -#define AT_HWCAP2 26 /* extension of AT_HWCAP */ -#define AT_EXECFN 31 /* filename of the executable */ -#define AT_SYSINFO 32 /* address of kernel entry point */ -#define AT_SYSINFO_EHDR 33 /* address of kernel vdso */ -#define AT_L1I_CACHESHAPE 34 /* shapes of the caches: */ -#define AT_L1D_CACHESHAPE 35 /* bits 0-3: cache associativity. */ -#define AT_L2_CACHESHAPE 36 /* bits 4-7: log2 of line size. */ -#define AT_L3_CACHESHAPE 37 /* val&~255: cache size. */ +#define AT_NULL 0 /* end of vector */ +#define AT_IGNORE 1 /* entry should be ignored */ +#define AT_EXECFD 2 /* file descriptor of program */ +#define AT_PHDR 3 /* program headers for program */ +#define AT_PHENT 4 /* size of program header entry */ +#define AT_PHNUM 5 /* number of program headers */ +#define AT_PAGESZ 6 /* system page size */ +#define AT_BASE 7 /* base address of interpreter */ +#define AT_FLAGS 8 /* flags */ +#define AT_ENTRY 9 /* entry point of program */ +#define AT_NOTELF 10 /* program is not ELF */ +#define AT_UID 11 /* real uid */ +#define AT_EUID 12 /* effective uid */ +#define AT_GID 13 /* real gid */ +#define AT_EGID 14 /* effective gid */ +#define AT_PLATFORM 15 /* string identifying CPU for optimizations */ +#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */ +#define AT_CLKTCK 17 /* frequency at which times() increments */ +#define AT_FPUCW 18 /* info about fpu initialization by kernel */ +#define AT_DCACHEBSIZE 19 /* data cache block size */ +#define AT_ICACHEBSIZE 20 /* instruction cache block size */ +#define AT_UCACHEBSIZE 21 /* unified cache block size */ +#define AT_IGNOREPPC 22 /* ppc only; entry should be ignored */ +#define AT_SECURE 23 /* boolean, was exec suid-like? */ +#define AT_BASE_PLATFORM 24 /* string identifying real platforms */ +#define AT_RANDOM 25 /* address of 16 random bytes */ +#define AT_HWCAP2 26 /* extension of AT_HWCAP */ +#define AT_EXECFN 31 /* filename of the executable */ +#define AT_SYSINFO 32 /* address of kernel entry point */ +#define AT_SYSINFO_EHDR 33 /* address of kernel vdso */ +#define AT_L1I_CACHESHAPE 34 /* shapes of the caches: */ +#define AT_L1D_CACHESHAPE 35 /* bits 0-3: cache associativity. */ +#define AT_L2_CACHESHAPE 36 /* bits 4-7: log2 of line size. */ +#define AT_L3_CACHESHAPE 37 /* val&~255: cache size. */ typedef struct dynamic{ Elf32_Sword d_tag; union{ - Elf32_Sword d_val; - Elf32_Addr d_ptr; + Elf32_Sword d_val; + Elf32_Addr d_ptr; } d_un; } Elf32_Dyn; typedef struct { - Elf64_Sxword d_tag; /* entry tag value */ + Elf64_Sxword d_tag; /* entry tag value */ union { Elf64_Xword d_val; Elf64_Addr d_ptr; @@ -364,72 +365,72 @@ typedef struct { #define ELF32_R_SYM(x) ((x) >> 8) #define ELF32_R_TYPE(x) ((x) & 0xff) -#define ELF64_R_SYM(i) ((i) >> 32) -#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) #define ELF64_R_TYPE_DATA(i) (((ELF64_R_TYPE(i) >> 8) ^ 0x00800000) - 0x00800000) -#define R_386_NONE 0 -#define R_386_32 1 -#define R_386_PC32 2 -#define R_386_GOT32 3 -#define R_386_PLT32 4 -#define R_386_COPY 5 -#define R_386_GLOB_DAT 6 -#define R_386_JMP_SLOT 7 -#define R_386_RELATIVE 8 -#define R_386_GOTOFF 9 -#define R_386_GOTPC 10 -#define R_386_NUM 11 +#define R_386_NONE 0 +#define R_386_32 1 +#define R_386_PC32 2 +#define R_386_GOT32 3 +#define R_386_PLT32 4 +#define R_386_COPY 5 +#define R_386_GLOB_DAT 6 +#define R_386_JMP_SLOT 7 +#define R_386_RELATIVE 8 +#define R_386_GOTOFF 9 +#define R_386_GOTPC 10 +#define R_386_NUM 11 /* Not a dynamic reloc, so not included in R_386_NUM. Used in TCG. */ -#define R_386_PC8 23 +#define R_386_PC8 23 -#define R_MIPS_NONE 0 -#define R_MIPS_16 1 -#define R_MIPS_32 2 -#define R_MIPS_REL32 3 -#define R_MIPS_26 4 -#define R_MIPS_HI16 5 -#define R_MIPS_LO16 6 -#define R_MIPS_GPREL16 7 -#define R_MIPS_LITERAL 8 -#define R_MIPS_GOT16 9 -#define R_MIPS_PC16 10 -#define R_MIPS_CALL16 11 -#define R_MIPS_GPREL32 12 +#define R_MIPS_NONE 0 +#define R_MIPS_16 1 +#define R_MIPS_32 2 +#define R_MIPS_REL32 3 +#define R_MIPS_26 4 +#define R_MIPS_HI16 5 +#define R_MIPS_LO16 6 +#define R_MIPS_GPREL16 7 +#define R_MIPS_LITERAL 8 +#define R_MIPS_GOT16 9 +#define R_MIPS_PC16 10 +#define R_MIPS_CALL16 11 +#define R_MIPS_GPREL32 12 /* The remaining relocs are defined on Irix, although they are not in the MIPS ELF ABI. */ -#define R_MIPS_UNUSED1 13 -#define R_MIPS_UNUSED2 14 -#define R_MIPS_UNUSED3 15 -#define R_MIPS_SHIFT5 16 -#define R_MIPS_SHIFT6 17 -#define R_MIPS_64 18 -#define R_MIPS_GOT_DISP 19 -#define R_MIPS_GOT_PAGE 20 -#define R_MIPS_GOT_OFST 21 +#define R_MIPS_UNUSED1 13 +#define R_MIPS_UNUSED2 14 +#define R_MIPS_UNUSED3 15 +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 /* * The following two relocation types are specified in the MIPS ABI * conformance guide version 1.2 but not yet in the psABI. */ -#define R_MIPS_GOTHI16 22 -#define R_MIPS_GOTLO16 23 -#define R_MIPS_SUB 24 -#define R_MIPS_INSERT_A 25 -#define R_MIPS_INSERT_B 26 -#define R_MIPS_DELETE 27 -#define R_MIPS_HIGHER 28 -#define R_MIPS_HIGHEST 29 +#define R_MIPS_GOTHI16 22 +#define R_MIPS_GOTLO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 /* * The following two relocation types are specified in the MIPS ABI * conformance guide version 1.2 but not yet in the psABI. */ -#define R_MIPS_CALLHI16 30 -#define R_MIPS_CALLLO16 31 +#define R_MIPS_CALLHI16 30 +#define R_MIPS_CALLLO16 31 /* * This range is reserved for vendor specific relocations. */ -#define R_MIPS_LOVENDOR 100 -#define R_MIPS_HIVENDOR 127 +#define R_MIPS_LOVENDOR 100 +#define R_MIPS_HIVENDOR 127 /* SUN SPARC specific definitions. */ @@ -450,48 +451,48 @@ typedef struct { /* * Sparc ELF relocation types */ -#define R_SPARC_NONE 0 -#define R_SPARC_8 1 -#define R_SPARC_16 2 -#define R_SPARC_32 3 -#define R_SPARC_DISP8 4 -#define R_SPARC_DISP16 5 -#define R_SPARC_DISP32 6 -#define R_SPARC_WDISP30 7 -#define R_SPARC_WDISP22 8 -#define R_SPARC_HI22 9 -#define R_SPARC_22 10 -#define R_SPARC_13 11 -#define R_SPARC_LO10 12 -#define R_SPARC_GOT10 13 -#define R_SPARC_GOT13 14 -#define R_SPARC_GOT22 15 -#define R_SPARC_PC10 16 -#define R_SPARC_PC22 17 -#define R_SPARC_WPLT30 18 -#define R_SPARC_COPY 19 -#define R_SPARC_GLOB_DAT 20 -#define R_SPARC_JMP_SLOT 21 -#define R_SPARC_RELATIVE 22 -#define R_SPARC_UA32 23 -#define R_SPARC_PLT32 24 -#define R_SPARC_HIPLT22 25 -#define R_SPARC_LOPLT10 26 -#define R_SPARC_PCPLT32 27 -#define R_SPARC_PCPLT22 28 -#define R_SPARC_PCPLT10 29 -#define R_SPARC_10 30 -#define R_SPARC_11 31 -#define R_SPARC_64 32 -#define R_SPARC_OLO10 33 -#define R_SPARC_HH22 34 -#define R_SPARC_HM10 35 -#define R_SPARC_LM22 36 -#define R_SPARC_WDISP16 40 -#define R_SPARC_WDISP19 41 -#define R_SPARC_7 43 -#define R_SPARC_5 44 -#define R_SPARC_6 45 +#define R_SPARC_NONE 0 +#define R_SPARC_8 1 +#define R_SPARC_16 2 +#define R_SPARC_32 3 +#define R_SPARC_DISP8 4 +#define R_SPARC_DISP16 5 +#define R_SPARC_DISP32 6 +#define R_SPARC_WDISP30 7 +#define R_SPARC_WDISP22 8 +#define R_SPARC_HI22 9 +#define R_SPARC_22 10 +#define R_SPARC_13 11 +#define R_SPARC_LO10 12 +#define R_SPARC_GOT10 13 +#define R_SPARC_GOT13 14 +#define R_SPARC_GOT22 15 +#define R_SPARC_PC10 16 +#define R_SPARC_PC22 17 +#define R_SPARC_WPLT30 18 +#define R_SPARC_COPY 19 +#define R_SPARC_GLOB_DAT 20 +#define R_SPARC_JMP_SLOT 21 +#define R_SPARC_RELATIVE 22 +#define R_SPARC_UA32 23 +#define R_SPARC_PLT32 24 +#define R_SPARC_HIPLT22 25 +#define R_SPARC_LOPLT10 26 +#define R_SPARC_PCPLT32 27 +#define R_SPARC_PCPLT22 28 +#define R_SPARC_PCPLT10 29 +#define R_SPARC_10 30 +#define R_SPARC_11 31 +#define R_SPARC_64 32 +#define R_SPARC_OLO10 33 +#define R_SPARC_HH22 34 +#define R_SPARC_HM10 35 +#define R_SPARC_LM22 36 +#define R_SPARC_WDISP16 40 +#define R_SPARC_WDISP19 41 +#define R_SPARC_7 43 +#define R_SPARC_5 44 +#define R_SPARC_6 45 /* Bits present in AT_HWCAP for ARM. */ @@ -598,25 +599,53 @@ typedef struct { /* Bits present in AT_HWCAP for s390. */ -#define HWCAP_S390_ESAN3 1 -#define HWCAP_S390_ZARCH 2 -#define HWCAP_S390_STFLE 4 -#define HWCAP_S390_MSA 8 -#define HWCAP_S390_LDISP 16 -#define HWCAP_S390_EIMM 32 -#define HWCAP_S390_DFP 64 -#define HWCAP_S390_HPAGE 128 -#define HWCAP_S390_ETF3EH 256 -#define HWCAP_S390_HIGH_GPRS 512 -#define HWCAP_S390_TE 1024 -#define HWCAP_S390_VXRS 2048 -#define HWCAP_S390_VXRS_BCD 4096 -#define HWCAP_S390_VXRS_EXT 8192 -#define HWCAP_S390_GS 16384 -#define HWCAP_S390_VXRS_EXT2 32768 -#define HWCAP_S390_VXRS_PDE 65536 -#define HWCAP_S390_SORT 131072 -#define HWCAP_S390_DFLT 262144 +#define HWCAP_S390_NR_ESAN3 0 +#define HWCAP_S390_NR_ZARCH 1 +#define HWCAP_S390_NR_STFLE 2 +#define HWCAP_S390_NR_MSA 3 +#define HWCAP_S390_NR_LDISP 4 +#define HWCAP_S390_NR_EIMM 5 +#define HWCAP_S390_NR_DFP 6 +#define HWCAP_S390_NR_HPAGE 7 +#define HWCAP_S390_NR_ETF3EH 8 +#define HWCAP_S390_NR_HIGH_GPRS 9 +#define HWCAP_S390_NR_TE 10 +#define HWCAP_S390_NR_VXRS 11 +#define HWCAP_S390_NR_VXRS_BCD 12 +#define HWCAP_S390_NR_VXRS_EXT 13 +#define HWCAP_S390_NR_GS 14 +#define HWCAP_S390_NR_VXRS_EXT2 15 +#define HWCAP_S390_NR_VXRS_PDE 16 +#define HWCAP_S390_NR_SORT 17 +#define HWCAP_S390_NR_DFLT 18 +#define HWCAP_S390_NR_VXRS_PDE2 19 +#define HWCAP_S390_NR_NNPA 20 +#define HWCAP_S390_NR_PCI_MIO 21 +#define HWCAP_S390_NR_SIE 22 + +#define HWCAP_S390_ESAN3 (1 << HWCAP_S390_NR_ESAN3) +#define HWCAP_S390_ZARCH (1 << HWCAP_S390_NR_ZARCH) +#define HWCAP_S390_STFLE (1 << HWCAP_S390_NR_STFLE) +#define HWCAP_S390_MSA (1 << HWCAP_S390_NR_MSA) +#define HWCAP_S390_LDISP (1 << HWCAP_S390_NR_LDISP) +#define HWCAP_S390_EIMM (1 << HWCAP_S390_NR_EIMM) +#define HWCAP_S390_DFP (1 << HWCAP_S390_NR_DFP) +#define HWCAP_S390_HPAGE (1 << HWCAP_S390_NR_HPAGE) +#define HWCAP_S390_ETF3EH (1 << HWCAP_S390_NR_ETF3EH) +#define HWCAP_S390_HIGH_GPRS (1 << HWCAP_S390_NR_HIGH_GPRS) +#define HWCAP_S390_TE (1 << HWCAP_S390_NR_TE) +#define HWCAP_S390_VXRS (1 << HWCAP_S390_NR_VXRS) +#define HWCAP_S390_VXRS_BCD (1 << HWCAP_S390_NR_VXRS_BCD) +#define HWCAP_S390_VXRS_EXT (1 << HWCAP_S390_NR_VXRS_EXT) +#define HWCAP_S390_GS (1 << HWCAP_S390_NR_GS) +#define HWCAP_S390_VXRS_EXT2 (1 << HWCAP_S390_NR_VXRS_EXT2) +#define HWCAP_S390_VXRS_PDE (1 << HWCAP_S390_NR_VXRS_PDE) +#define HWCAP_S390_SORT (1 << HWCAP_S390_NR_SORT) +#define HWCAP_S390_DFLT (1 << HWCAP_S390_NR_DFLT) +#define HWCAP_S390_VXRS_PDE2 (1 << HWCAP_S390_NR_VXRS_PDE2) +#define HWCAP_S390_NNPA (1 << HWCAP_S390_NR_NNPA) +#define HWCAP_S390_PCI_MIO (1 << HWCAP_S390_NR_PCI_MIO) +#define HWCAP_S390_SIE (1 << HWCAP_S390_NR_SIE) /* M68K specific definitions. */ /* We use the top 24 bits to encode information about the @@ -649,29 +678,29 @@ typedef struct { /* * 68k ELF relocation types */ -#define R_68K_NONE 0 -#define R_68K_32 1 -#define R_68K_16 2 -#define R_68K_8 3 -#define R_68K_PC32 4 -#define R_68K_PC16 5 -#define R_68K_PC8 6 -#define R_68K_GOT32 7 -#define R_68K_GOT16 8 -#define R_68K_GOT8 9 -#define R_68K_GOT32O 10 -#define R_68K_GOT16O 11 -#define R_68K_GOT8O 12 -#define R_68K_PLT32 13 -#define R_68K_PLT16 14 -#define R_68K_PLT8 15 -#define R_68K_PLT32O 16 -#define R_68K_PLT16O 17 -#define R_68K_PLT8O 18 -#define R_68K_COPY 19 -#define R_68K_GLOB_DAT 20 -#define R_68K_JMP_SLOT 21 -#define R_68K_RELATIVE 22 +#define R_68K_NONE 0 +#define R_68K_32 1 +#define R_68K_16 2 +#define R_68K_8 3 +#define R_68K_PC32 4 +#define R_68K_PC16 5 +#define R_68K_PC8 6 +#define R_68K_GOT32 7 +#define R_68K_GOT16 8 +#define R_68K_GOT8 9 +#define R_68K_GOT32O 10 +#define R_68K_GOT16O 11 +#define R_68K_GOT8O 12 +#define R_68K_PLT32 13 +#define R_68K_PLT16 14 +#define R_68K_PLT8 15 +#define R_68K_PLT32O 16 +#define R_68K_PLT16O 17 +#define R_68K_PLT8O 18 +#define R_68K_COPY 19 +#define R_68K_GLOB_DAT 20 +#define R_68K_JMP_SLOT 21 +#define R_68K_RELATIVE 22 /* * Alpha ELF relocation types @@ -695,7 +724,7 @@ typedef struct { #define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ #define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ #define R_ALPHA_RELATIVE 27 /* Adjust by program base */ -#define R_ALPHA_BRSGP 28 +#define R_ALPHA_BRSGP 28 #define R_ALPHA_TLSGD 29 #define R_ALPHA_TLS_LDM 30 #define R_ALPHA_DTPMOD64 31 @@ -710,78 +739,78 @@ typedef struct { #define R_ALPHA_TPRELLO 40 #define R_ALPHA_TPREL16 41 -#define SHF_ALPHA_GPREL 0x10000000 +#define SHF_ALPHA_GPREL 0x10000000 /* PowerPC specific definitions. */ /* Processor specific flags for the ELF header e_flags field. */ -#define EF_PPC64_ABI 0x3 +#define EF_PPC64_ABI 0x3 /* PowerPC relocations defined by the ABIs */ -#define R_PPC_NONE 0 -#define R_PPC_ADDR32 1 /* 32bit absolute address */ -#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ -#define R_PPC_ADDR16 3 /* 16bit absolute address */ -#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ -#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ -#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ -#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ -#define R_PPC_ADDR14_BRTAKEN 8 -#define R_PPC_ADDR14_BRNTAKEN 9 -#define R_PPC_REL24 10 /* PC relative 26 bit */ -#define R_PPC_REL14 11 /* PC relative 16 bit */ -#define R_PPC_REL14_BRTAKEN 12 -#define R_PPC_REL14_BRNTAKEN 13 -#define R_PPC_GOT16 14 -#define R_PPC_GOT16_LO 15 -#define R_PPC_GOT16_HI 16 -#define R_PPC_GOT16_HA 17 -#define R_PPC_PLTREL24 18 -#define R_PPC_COPY 19 -#define R_PPC_GLOB_DAT 20 -#define R_PPC_JMP_SLOT 21 -#define R_PPC_RELATIVE 22 -#define R_PPC_LOCAL24PC 23 -#define R_PPC_UADDR32 24 -#define R_PPC_UADDR16 25 -#define R_PPC_REL32 26 -#define R_PPC_PLT32 27 -#define R_PPC_PLTREL32 28 -#define R_PPC_PLT16_LO 29 -#define R_PPC_PLT16_HI 30 -#define R_PPC_PLT16_HA 31 -#define R_PPC_SDAREL16 32 -#define R_PPC_SECTOFF 33 -#define R_PPC_SECTOFF_LO 34 -#define R_PPC_SECTOFF_HI 35 -#define R_PPC_SECTOFF_HA 36 +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 /* Keep this the last entry. */ #ifndef R_PPC_NUM -#define R_PPC_NUM 37 +#define R_PPC_NUM 37 #endif /* ARM specific declarations */ /* Processor specific flags for the ELF header e_flags field. */ -#define EF_ARM_RELEXEC 0x01 -#define EF_ARM_HASENTRY 0x02 -#define EF_ARM_INTERWORK 0x04 -#define EF_ARM_APCS_26 0x08 -#define EF_ARM_APCS_FLOAT 0x10 -#define EF_ARM_PIC 0x20 -#define EF_ALIGN8 0x40 /* 8-bit structure alignment is in use */ -#define EF_NEW_ABI 0x80 -#define EF_OLD_ABI 0x100 -#define EF_ARM_SOFT_FLOAT 0x200 -#define EF_ARM_VFP_FLOAT 0x400 +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_NEW_ABI 0x80 +#define EF_OLD_ABI 0x100 +#define EF_ARM_SOFT_FLOAT 0x200 +#define EF_ARM_VFP_FLOAT 0x400 #define EF_ARM_MAVERICK_FLOAT 0x800 /* Other constants defined in the ARM ELF spec. version B-01. */ -#define EF_ARM_SYMSARESORTED 0x04 /* NB conflicts with EF_INTERWORK */ -#define EF_ARM_DYNSYMSUSESEGIDX 0x08 /* NB conflicts with EF_APCS26 */ -#define EF_ARM_MAPSYMSFIRST 0x10 /* NB conflicts with EF_APCS_FLOAT */ -#define EF_ARM_EABIMASK 0xFF000000 +#define EF_ARM_SYMSARESORTED 0x04 /* NB conflicts with EF_INTERWORK */ +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 /* NB conflicts with EF_APCS26 */ +#define EF_ARM_MAPSYMSFIRST 0x10 /* NB conflicts with EF_APCS_FLOAT */ +#define EF_ARM_EABIMASK 0xFF000000 /* Constants defined in AAELF. */ #define EF_ARM_BE8 0x00800000 @@ -808,46 +837,46 @@ typedef struct { addressed by the static base */ /* ARM relocs. */ -#define R_ARM_NONE 0 /* No reloc */ -#define R_ARM_PC24 1 /* PC relative 26 bit branch */ -#define R_ARM_ABS32 2 /* Direct 32 bit */ -#define R_ARM_REL32 3 /* PC relative 32 bit */ -#define R_ARM_PC13 4 -#define R_ARM_ABS16 5 /* Direct 16 bit */ -#define R_ARM_ABS12 6 /* Direct 12 bit */ -#define R_ARM_THM_ABS5 7 -#define R_ARM_ABS8 8 /* Direct 8 bit */ -#define R_ARM_SBREL32 9 -#define R_ARM_THM_PC22 10 -#define R_ARM_THM_PC8 11 -#define R_ARM_AMP_VCALL9 12 -#define R_ARM_SWI24 13 -#define R_ARM_THM_SWI8 14 -#define R_ARM_XPC25 15 -#define R_ARM_THM_XPC22 16 -#define R_ARM_COPY 20 /* Copy symbol at runtime */ -#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ -#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ -#define R_ARM_RELATIVE 23 /* Adjust by program base */ -#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ -#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ -#define R_ARM_GOT32 26 /* 32 bit GOT entry */ -#define R_ARM_PLT32 27 /* 32 bit PLT address */ +#define R_ARM_NONE 0 /* No reloc */ +#define R_ARM_PC24 1 /* PC relative 26 bit branch */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_REL32 3 /* PC relative 32 bit */ +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 /* Direct 16 bit */ +#define R_ARM_ABS12 6 /* Direct 12 bit */ +#define R_ARM_THM_ABS5 7 +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 +#define R_ARM_THM_PC8 11 +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 +#define R_ARM_THM_SWI8 14 +#define R_ARM_XPC25 15 +#define R_ARM_THM_XPC22 16 +#define R_ARM_COPY 20 /* Copy symbol at runtime */ +#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ +#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ +#define R_ARM_RELATIVE 23 /* Adjust by program base */ +#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ +#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT32 26 /* 32 bit GOT entry */ +#define R_ARM_PLT32 27 /* 32 bit PLT address */ #define R_ARM_CALL 28 #define R_ARM_JUMP24 29 -#define R_ARM_GNU_VTENTRY 100 -#define R_ARM_GNU_VTINHERIT 101 -#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ -#define R_ARM_THM_PC9 103 /* thumb conditional branch */ -#define R_ARM_RXPC25 249 -#define R_ARM_RSBREL32 250 -#define R_ARM_THM_RPC22 251 -#define R_ARM_RREL32 252 -#define R_ARM_RABS22 253 -#define R_ARM_RPC24 254 -#define R_ARM_RBASE 255 +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ +#define R_ARM_THM_PC9 103 /* thumb conditional branch */ +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 /* Keep this the last entry. */ -#define R_ARM_NUM 256 +#define R_ARM_NUM 256 /* ARM Aarch64 relocation types */ #define R_AARCH64_NONE 256 /* also accepts R_ARM_NONE (0) */ @@ -977,385 +1006,385 @@ typedef struct { #define R_AARCH64_TLS_TPREL32 1033 /* s390 relocations defined by the ABIs */ -#define R_390_NONE 0 /* No reloc. */ -#define R_390_8 1 /* Direct 8 bit. */ -#define R_390_12 2 /* Direct 12 bit. */ -#define R_390_16 3 /* Direct 16 bit. */ -#define R_390_32 4 /* Direct 32 bit. */ -#define R_390_PC32 5 /* PC relative 32 bit. */ -#define R_390_GOT12 6 /* 12 bit GOT offset. */ -#define R_390_GOT32 7 /* 32 bit GOT offset. */ -#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ -#define R_390_COPY 9 /* Copy symbol at runtime. */ -#define R_390_GLOB_DAT 10 /* Create GOT entry. */ -#define R_390_JMP_SLOT 11 /* Create PLT entry. */ -#define R_390_RELATIVE 12 /* Adjust by program base. */ -#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ -#define R_390_GOTPC 14 /* 32 bit PC rel. offset to GOT. */ -#define R_390_GOT16 15 /* 16 bit GOT offset. */ -#define R_390_PC16 16 /* PC relative 16 bit. */ -#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ -#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ -#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ -#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ -#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ -#define R_390_64 22 /* Direct 64 bit. */ -#define R_390_PC64 23 /* PC relative 64 bit. */ -#define R_390_GOT64 24 /* 64 bit GOT offset. */ -#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ -#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ -#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ -#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ -#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ -#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ -#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ -#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ -#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ -#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ -#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ -#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ -#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ -#define R_390_TLS_GDCALL 38 /* Tag for function call in general - dynamic TLS code. */ -#define R_390_TLS_LDCALL 39 /* Tag for function call in local - dynamic TLS code. */ -#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic - thread local data. */ -#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic - thread local data. */ -#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS - block offset. */ -#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS - block offset. */ -#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS - block offset. */ -#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic - thread local data in LD code. */ -#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic - thread local data in LD code. */ -#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for - negated static TLS block offset. */ -#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for - negated static TLS block offset. */ -#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for - negated static TLS block offset. */ -#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to - static TLS block. */ -#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to - static TLS block. */ -#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS - block. */ -#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS - block. */ -#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ -#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ -#define R_390_TLS_TPOFF 56 /* Negate offset in static TLS +#define R_390_NONE 0 /* No reloc. */ +#define R_390_8 1 /* Direct 8 bit. */ +#define R_390_12 2 /* Direct 12 bit. */ +#define R_390_16 3 /* Direct 16 bit. */ +#define R_390_32 4 /* Direct 32 bit. */ +#define R_390_PC32 5 /* PC relative 32 bit. */ +#define R_390_GOT12 6 /* 12 bit GOT offset. */ +#define R_390_GOT32 7 /* 32 bit GOT offset. */ +#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ +#define R_390_COPY 9 /* Copy symbol at runtime. */ +#define R_390_GLOB_DAT 10 /* Create GOT entry. */ +#define R_390_JMP_SLOT 11 /* Create PLT entry. */ +#define R_390_RELATIVE 12 /* Adjust by program base. */ +#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ +#define R_390_GOTPC 14 /* 32 bit PC rel. offset to GOT. */ +#define R_390_GOT16 15 /* 16 bit GOT offset. */ +#define R_390_PC16 16 /* PC relative 16 bit. */ +#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ +#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ +#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ +#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ +#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ +#define R_390_64 22 /* Direct 64 bit. */ +#define R_390_PC64 23 /* PC relative 64 bit. */ +#define R_390_GOT64 24 /* 64 bit GOT offset. */ +#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ +#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ +#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ +#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ +#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ +#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ +#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ +#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ +#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ +#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ +#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ +#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ +#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ +#define R_390_TLS_GDCALL 38 /* Tag for function call in general + dynamic TLS code. */ +#define R_390_TLS_LDCALL 39 /* Tag for function call in local + dynamic TLS code. */ +#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic + thread local data. */ +#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic + thread local data. */ +#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic + thread local data in LD code. */ +#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic + thread local data in LD code. */ +#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS + block. */ +#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS + block. */ +#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ +#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ +#define R_390_TLS_TPOFF 56 /* Negate offset in static TLS block. */ #define R_390_20 57 /* Keep this the last entry. */ #define R_390_NUM 58 /* x86-64 relocation types */ -#define R_X86_64_NONE 0 /* No reloc */ -#define R_X86_64_64 1 /* Direct 64 bit */ -#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ -#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ -#define R_X86_64_PLT32 4 /* 32 bit PLT address */ -#define R_X86_64_COPY 5 /* Copy symbol at runtime */ -#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ -#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ -#define R_X86_64_RELATIVE 8 /* Adjust by program base */ -#define R_X86_64_GOTPCREL 9 /* 32 bit signed pc relative +#define R_X86_64_NONE 0 /* No reloc */ +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_COPY 5 /* Copy symbol at runtime */ +#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ +#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ +#define R_X86_64_RELATIVE 8 /* Adjust by program base */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed pc relative offset to GOT */ -#define R_X86_64_32 10 /* Direct 32 bit zero extended */ -#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ -#define R_X86_64_16 12 /* Direct 16 bit zero extended */ -#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ -#define R_X86_64_8 14 /* Direct 8 bit sign extended */ -#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_16 12 /* Direct 16 bit zero extended */ +#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ +#define R_X86_64_8 14 /* Direct 8 bit sign extended */ +#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ -#define R_X86_64_NUM 16 +#define R_X86_64_NUM 16 /* Legal values for e_flags field of Elf64_Ehdr. */ -#define EF_ALPHA_32BIT 1 /* All addresses are below 2GB */ +#define EF_ALPHA_32BIT 1 /* All addresses are below 2GB */ /* HPPA specific definitions. */ /* Legal values for e_flags field of Elf32_Ehdr. */ -#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ -#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ -#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ -#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ -#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch +#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ +#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ +#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ +#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch prediction. */ -#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ -#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ +#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ +#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ /* Defined values for `e_flags & EF_PARISC_ARCH' are: */ -#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ -#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ -#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ +#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ +#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ +#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ -/* Additional section indeces. */ +/* Additional section indices. */ -#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared +#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tentatively declared symbols in ANSI C. */ -#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ +#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ /* Legal values for sh_type field of Elf32_Shdr. */ -#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ -#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ -#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ +#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ +#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ +#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ /* Legal values for sh_flags field of Elf32_Shdr. */ -#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ -#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ -#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ +#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ +#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ /* Legal values for ST_TYPE subfield of st_info (symbol type). */ -#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ -#define STT_HP_OPAQUE (STT_LOOS + 0x1) -#define STT_HP_STUB (STT_LOOS + 0x2) +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) /* HPPA relocs. */ -#define R_PARISC_NONE 0 /* No reloc. */ -#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ -#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ -#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ -#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ -#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ -#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ -#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ -#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ -#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ -#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ -#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ -#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ -#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ -#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ -#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ -#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ -#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ -#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ -#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ -#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ -#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ -#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ -#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ -#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ -#define R_PARISC_FPTR64 64 /* 64 bits function address. */ -#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ -#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ -#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ -#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ -#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ -#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ -#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ -#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ -#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ -#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ -#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ -#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ -#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ -#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ -#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ -#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ -#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ -#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ -#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ -#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ -#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ -#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ -#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ -#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ -#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ -#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ -#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ -#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ -#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ -#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ -#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ -#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ -#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ -#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ -#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ -#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ -#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ -#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ -#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ -#define R_PARISC_LORESERVE 128 -#define R_PARISC_COPY 128 /* Copy relocation. */ -#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ -#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ -#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ -#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ -#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ -#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ -#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ -#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ -#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ -#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ -#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ -#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ -#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ -#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ -#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ -#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ -#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ -#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ -#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ -#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ -#define R_PARISC_HIRESERVE 255 +#define R_PARISC_NONE 0 /* No reloc. */ +#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ +#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ +#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ +#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ +#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ +#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ +#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ +#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ +#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ +#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ +#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ +#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ +#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ +#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ +#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ +#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ +#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ +#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ +#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ +#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ +#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ +#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ +#define R_PARISC_FPTR64 64 /* 64 bits function address. */ +#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ +#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ +#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ +#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ +#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ +#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ +#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ +#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ +#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ +#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ +#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ +#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ +#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ +#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ +#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ +#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ +#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ +#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ +#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ +#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 /* Copy relocation. */ +#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ +#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ +#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ +#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ +#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ +#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ +#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ +#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ +#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_HIRESERVE 255 /* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ -#define PT_HP_TLS (PT_LOOS + 0x0) -#define PT_HP_CORE_NONE (PT_LOOS + 0x1) -#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) -#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) -#define PT_HP_CORE_COMM (PT_LOOS + 0x4) -#define PT_HP_CORE_PROC (PT_LOOS + 0x5) -#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) -#define PT_HP_CORE_STACK (PT_LOOS + 0x7) -#define PT_HP_CORE_SHM (PT_LOOS + 0x8) -#define PT_HP_CORE_MMF (PT_LOOS + 0x9) -#define PT_HP_PARALLEL (PT_LOOS + 0x10) -#define PT_HP_FASTBIND (PT_LOOS + 0x11) -#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) -#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) -#define PT_HP_STACK (PT_LOOS + 0x14) +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) -#define PT_PARISC_ARCHEXT 0x70000000 -#define PT_PARISC_UNWIND 0x70000001 +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 /* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ -#define PF_PARISC_SBP 0x08000000 +#define PF_PARISC_SBP 0x08000000 -#define PF_HP_PAGE_SIZE 0x00100000 -#define PF_HP_FAR_SHARED 0x00200000 -#define PF_HP_NEAR_SHARED 0x00400000 -#define PF_HP_CODE 0x01000000 -#define PF_HP_MODIFY 0x02000000 -#define PF_HP_LAZYSWAP 0x04000000 -#define PF_HP_SBP 0x08000000 +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 /* IA-64 specific declarations. */ /* Processor specific flags for the Ehdr e_flags field. */ -#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ -#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ -#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ +#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ +#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ +#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ /* Processor specific values for the Phdr p_type field. */ -#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ -#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ +#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ /* Processor specific flags for the Phdr p_flags field. */ -#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ +#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ /* Processor specific values for the Shdr sh_type field. */ -#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ -#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ +#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ /* Processor specific flags for the Shdr sh_flags field. */ -#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ -#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ +#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ +#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ /* Processor specific values for the Dyn d_tag field. */ -#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) -#define DT_IA_64_NUM 1 +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 /* IA-64 relocations. */ -#define R_IA64_NONE 0x00 /* none */ -#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ -#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ -#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ -#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ -#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ -#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ -#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ -#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ -#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ -#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ -#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ -#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ -#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ -#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ -#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ -#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ -#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ -#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ -#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ -#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ -#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ -#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ -#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ -#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ -#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ -#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ -#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ -#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ -#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ -#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ -#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ -#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ -#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ -#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ -#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ -#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ -#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ -#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ -#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ -#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ -#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ -#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ -#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ -#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ -#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ -#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ -#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ -#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ -#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ -#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ -#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ -#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ -#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ -#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ -#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ -#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ -#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ -#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ -#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ -#define R_IA64_COPY 0x84 /* copy relocation */ -#define R_IA64_SUB 0x85 /* Addend and symbol difference */ -#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ -#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ -#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ -#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ -#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ -#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ -#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ -#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ -#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ -#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ -#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ -#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ -#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ -#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ -#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ -#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ -#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ -#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ -#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ +#define R_IA64_NONE 0x00 /* none */ +#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ +#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ +#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ +#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ +#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ +#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ +#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ +#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ +#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ +#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ +#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ +#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ +#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ +#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ +#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ +#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ +#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ +#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ +#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ +#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ +#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ +#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ +#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ +#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ +#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ +#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ +#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ +#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ +#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ +#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ +#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ +#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ +#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ +#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ +#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ +#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ +#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ +#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ +#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ +#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ +#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ +#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ +#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ +#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ +#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ +#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ +#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ +#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ +#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ +#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ +#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ +#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ +#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ +#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ +#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ +#define R_IA64_COPY 0x84 /* copy relocation */ +#define R_IA64_SUB 0x85 /* Addend and symbol difference */ +#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ +#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ +#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ +#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ +#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ +#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ +#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ +#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ +#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ +#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ +#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ +#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ +#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ +#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ +#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ +#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ /* RISC-V relocations. */ #define R_RISCV_NONE 0 @@ -1499,47 +1528,47 @@ typedef struct { #define DT_E2K_NUM 0x1020 typedef struct elf32_rel { - Elf32_Addr r_offset; - Elf32_Word r_info; + Elf32_Addr r_offset; + Elf32_Word r_info; } Elf32_Rel; typedef struct elf64_rel { - Elf64_Addr r_offset; /* Location at which to apply the action */ - Elf64_Xword r_info; /* index and type of relocation */ + Elf64_Addr r_offset; /* Location at which to apply the action */ + Elf64_Xword r_info; /* index and type of relocation */ } Elf64_Rel; typedef struct elf32_rela{ - Elf32_Addr r_offset; - Elf32_Word r_info; - Elf32_Sword r_addend; + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; } Elf32_Rela; typedef struct elf64_rela { - Elf64_Addr r_offset; /* Location at which to apply the action */ - Elf64_Xword r_info; /* index and type of relocation */ - Elf64_Sxword r_addend; /* Constant addend used to compute value */ + Elf64_Addr r_offset; /* Location at which to apply the action */ + Elf64_Xword r_info; /* index and type of relocation */ + Elf64_Sxword r_addend; /* Constant addend used to compute value */ } Elf64_Rela; typedef struct elf32_sym{ - Elf32_Word st_name; - Elf32_Addr st_value; - Elf32_Word st_size; - unsigned char st_info; - unsigned char st_other; - Elf32_Half st_shndx; + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; } Elf32_Sym; typedef struct elf64_sym { - Elf64_Word st_name; /* Symbol name, index in string tbl */ - unsigned char st_info; /* Type and binding attributes */ - unsigned char st_other; /* No defined meaning, 0 */ - Elf64_Half st_shndx; /* Associated section index */ - Elf64_Addr st_value; /* Value of the symbol */ - Elf64_Xword st_size; /* Associated symbol size */ + Elf64_Word st_name; /* Symbol name, index in string tbl */ + unsigned char st_info; /* Type and binding attributes */ + unsigned char st_other; /* No defined meaning, 0 */ + Elf64_Half st_shndx; /* Associated section index */ + Elf64_Addr st_value; /* Value of the symbol */ + Elf64_Xword st_size; /* Associated symbol size */ } Elf64_Sym; -#define EI_NIDENT 16 +#define EI_NIDENT 16 /* Special value for e_phnum. This indicates that the real number of program headers is too large to fit into e_phnum. Instead the real @@ -1547,30 +1576,30 @@ typedef struct elf64_sym { #define PN_XNUM 0xffff typedef struct elf32_hdr{ - unsigned char e_ident[EI_NIDENT]; - Elf32_Half e_type; - Elf32_Half e_machine; - Elf32_Word e_version; - Elf32_Addr e_entry; /* Entry point */ - Elf32_Off e_phoff; - Elf32_Off e_shoff; - Elf32_Word e_flags; - Elf32_Half e_ehsize; - Elf32_Half e_phentsize; - Elf32_Half e_phnum; - Elf32_Half e_shentsize; - Elf32_Half e_shnum; - Elf32_Half e_shstrndx; + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; /* Entry point */ + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; } Elf32_Ehdr; typedef struct elf64_hdr { - unsigned char e_ident[16]; /* ELF "magic number" */ + unsigned char e_ident[16]; /* ELF "magic number" */ Elf64_Half e_type; Elf64_Half e_machine; Elf64_Word e_version; - Elf64_Addr e_entry; /* Entry point virtual address */ - Elf64_Off e_phoff; /* Program header table file offset */ - Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ Elf64_Word e_flags; Elf64_Half e_ehsize; Elf64_Half e_phentsize; @@ -1582,107 +1611,107 @@ typedef struct elf64_hdr { /* These constants define the permissions on sections in the program header, p_flags. */ -#define PF_R 0x4 -#define PF_W 0x2 -#define PF_X 0x1 +#define PF_R 0x4 +#define PF_W 0x2 +#define PF_X 0x1 typedef struct elf32_phdr{ - Elf32_Word p_type; - Elf32_Off p_offset; - Elf32_Addr p_vaddr; - Elf32_Addr p_paddr; - Elf32_Word p_filesz; - Elf32_Word p_memsz; - Elf32_Word p_flags; - Elf32_Word p_align; + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; } Elf32_Phdr; typedef struct elf64_phdr { Elf64_Word p_type; Elf64_Word p_flags; - Elf64_Off p_offset; /* Segment file offset */ - Elf64_Addr p_vaddr; /* Segment virtual address */ - Elf64_Addr p_paddr; /* Segment physical address */ - Elf64_Xword p_filesz; /* Segment size in file */ - Elf64_Xword p_memsz; /* Segment size in memory */ - Elf64_Xword p_align; /* Segment alignment, file & memory */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment, file & memory */ } Elf64_Phdr; /* sh_type */ -#define SHT_NULL 0 -#define SHT_PROGBITS 1 -#define SHT_SYMTAB 2 -#define SHT_STRTAB 3 -#define SHT_RELA 4 -#define SHT_HASH 5 -#define SHT_DYNAMIC 6 -#define SHT_NOTE 7 -#define SHT_NOBITS 8 -#define SHT_REL 9 -#define SHT_SHLIB 10 -#define SHT_DYNSYM 11 -#define SHT_NUM 12 -#define SHT_LOPROC 0x70000000 -#define SHT_HIPROC 0x7fffffff -#define SHT_LOUSER 0x80000000 -#define SHT_HIUSER 0xffffffff -#define SHT_MIPS_LIST 0x70000000 -#define SHT_MIPS_CONFLICT 0x70000002 -#define SHT_MIPS_GPTAB 0x70000003 -#define SHT_MIPS_UCODE 0x70000004 +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_NUM 12 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff +#define SHT_MIPS_LIST 0x70000000 +#define SHT_MIPS_CONFLICT 0x70000002 +#define SHT_MIPS_GPTAB 0x70000003 +#define SHT_MIPS_UCODE 0x70000004 /* sh_flags */ -#define SHF_WRITE 0x1 -#define SHF_ALLOC 0x2 -#define SHF_EXECINSTR 0x4 -#define SHF_MASKPROC 0xf0000000 -#define SHF_MIPS_GPREL 0x10000000 +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 +#define SHF_MIPS_GPREL 0x10000000 /* special section indexes */ -#define SHN_UNDEF 0 -#define SHN_LORESERVE 0xff00 -#define SHN_LOPROC 0xff00 -#define SHN_HIPROC 0xff1f -#define SHN_ABS 0xfff1 -#define SHN_COMMON 0xfff2 -#define SHN_HIRESERVE 0xffff -#define SHN_MIPS_ACCOMON 0xff00 +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff +#define SHN_MIPS_ACCOMON 0xff00 typedef struct elf32_shdr { - Elf32_Word sh_name; - Elf32_Word sh_type; - Elf32_Word sh_flags; - Elf32_Addr sh_addr; - Elf32_Off sh_offset; - Elf32_Word sh_size; - Elf32_Word sh_link; - Elf32_Word sh_info; - Elf32_Word sh_addralign; - Elf32_Word sh_entsize; + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; } Elf32_Shdr; typedef struct elf64_shdr { - Elf64_Word sh_name; /* Section name, index in string tbl */ - Elf64_Word sh_type; /* Type of section */ - Elf64_Xword sh_flags; /* Miscellaneous section attributes */ - Elf64_Addr sh_addr; /* Section virtual addr at execution */ - Elf64_Off sh_offset; /* Section file offset */ - Elf64_Xword sh_size; /* Size of section in bytes */ - Elf64_Word sh_link; /* Index of another section */ - Elf64_Word sh_info; /* Additional section information */ - Elf64_Xword sh_addralign; /* Section alignment */ - Elf64_Xword sh_entsize; /* Entry size if section holds table */ + Elf64_Word sh_name; /* Section name, index in string tbl */ + Elf64_Word sh_type; /* Type of section */ + Elf64_Xword sh_flags; /* Miscellaneous section attributes */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Size of section in bytes */ + Elf64_Word sh_link; /* Index of another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ } Elf64_Shdr; -#define EI_MAG0 0 /* e_ident[] indexes */ -#define EI_MAG1 1 -#define EI_MAG2 2 -#define EI_MAG3 3 -#define EI_CLASS 4 -#define EI_DATA 5 -#define EI_VERSION 6 -#define EI_OSABI 7 -#define EI_PAD 8 +#define EI_MAG0 0 /* e_ident[] indexes */ +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_OSABI 7 +#define EI_PAD 8 #define ELFOSABI_NONE 0 /* UNIX System V ABI */ #define ELFOSABI_SYSV 0 /* Alias. */ @@ -1697,54 +1726,57 @@ typedef struct elf64_shdr { #define ELFOSABI_MODESTO 11 /* Novell Modesto. */ #define ELFOSABI_OPENBSD 12 /* OpenBSD. */ #define ELFOSABI_ARM_FDPIC 65 /* ARM FDPIC */ +#define ELFOSABI_XTENSA_FDPIC 65 /* Xtensa FDPIC */ #define ELFOSABI_ARM 97 /* ARM */ #define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ -#define ELFMAG0 0x7f /* EI_MAG */ -#define ELFMAG1 'E' -#define ELFMAG2 'L' -#define ELFMAG3 'F' -#define ELFMAG "\177ELF" -#define SELFMAG 4 +#define ELFMAG0 0x7f /* EI_MAG */ +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFMAG "\177ELF" +#define SELFMAG 4 -#define ELFCLASSNONE 0 /* EI_CLASS */ -#define ELFCLASS32 1 -#define ELFCLASS64 2 -#define ELFCLASSNUM 3 +#define ELFCLASSNONE 0 /* EI_CLASS */ +#define ELFCLASS32 1 +#define ELFCLASS64 2 +#define ELFCLASSNUM 3 -#define ELFDATANONE 0 /* e_ident[EI_DATA] */ -#define ELFDATA2LSB 1 -#define ELFDATA2MSB 2 +#define ELFDATANONE 0 /* e_ident[EI_DATA] */ +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 -#define EV_NONE 0 /* e_version, EI_VERSION */ -#define EV_CURRENT 1 -#define EV_NUM 2 +#define EV_NONE 0 /* e_version, EI_VERSION */ +#define EV_CURRENT 1 +#define EV_NUM 2 /* Notes used in ET_CORE */ -#define NT_PRSTATUS 1 -#define NT_FPREGSET 2 -#define NT_PRFPREG 2 -#define NT_PRPSINFO 3 -#define NT_TASKSTRUCT 4 -#define NT_AUXV 6 -#define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ -#define NT_S390_GS_CB 0x30b /* s390 guarded storage registers */ -#define NT_S390_VXRS_HIGH 0x30a /* s390 vector registers 16-31 */ -#define NT_S390_VXRS_LOW 0x309 /* s390 vector registers 0-15 (lower half) */ -#define NT_S390_PREFIX 0x305 /* s390 prefix register */ -#define NT_S390_CTRS 0x304 /* s390 control registers */ -#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */ -#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ -#define NT_S390_TIMER 0x301 /* s390 timer register */ -#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ -#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ -#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ -#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ -#define NT_ARM_TLS 0x401 /* ARM TLS register */ -#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 */ +#define NT_PRSTATUS 1 +#define NT_FPREGSET 2 +#define NT_PRFPREG 2 +#define NT_PRPSINFO 3 +#define NT_TASKSTRUCT 4 +#define NT_AUXV 6 +#define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ +#define NT_S390_PV_CPU_DATA 0x30e /* s390 protvirt cpu dump data */ +#define NT_S390_RI_CB 0x30d /* s390 runtime instrumentation */ +#define NT_S390_GS_CB 0x30b /* s390 guarded storage registers */ +#define NT_S390_VXRS_HIGH 0x30a /* s390 vector registers 16-31 */ +#define NT_S390_VXRS_LOW 0x309 /* s390 vector registers 0-15 (lower half) */ +#define NT_S390_PREFIX 0x305 /* s390 prefix register */ +#define NT_S390_CTRS 0x304 /* s390 control registers */ +#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */ +#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ +#define NT_S390_TIMER 0x301 /* s390 timer register */ +#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ +#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ +#define NT_ARM_TLS 0x401 /* ARM TLS register */ +#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 */ /* Defined note types for GNU systems. */ @@ -1777,16 +1809,16 @@ typedef struct elf64_shdr { /* Note header in a PT_NOTE section */ typedef struct elf32_note { - Elf32_Word n_namesz; /* Name size */ - Elf32_Word n_descsz; /* Content size */ - Elf32_Word n_type; /* Content type */ + Elf32_Word n_namesz; /* Name size */ + Elf32_Word n_descsz; /* Content size */ + Elf32_Word n_type; /* Content type */ } Elf32_Nhdr; /* Note header in a PT_NOTE section */ typedef struct elf64_note { - Elf64_Word n_namesz; /* Name size */ - Elf64_Word n_descsz; /* Content size */ - Elf64_Word n_type; /* Content type */ + Elf64_Word n_namesz; /* Name size */ + Elf64_Word n_descsz; /* Content size */ + Elf64_Word n_type; /* Content type */ } Elf64_Nhdr; @@ -1811,13 +1843,13 @@ struct elf32_fdpic_loadmap { #ifdef ELF_CLASS #if ELF_CLASS == ELFCLASS32 -#define elfhdr elf32_hdr -#define elf_phdr elf32_phdr -#define elf_note elf32_note -#define elf_shdr elf32_shdr -#define elf_sym elf32_sym -#define elf_addr_t Elf32_Off -#define elf_rela elf32_rela +#define elfhdr elf32_hdr +#define elf_phdr elf32_phdr +#define elf_note elf32_note +#define elf_shdr elf32_shdr +#define elf_sym elf32_sym +#define elf_addr_t Elf32_Off +#define elf_rela elf32_rela #ifdef ELF_USES_RELOCA # define ELF_RELOC Elf32_Rela @@ -1827,13 +1859,13 @@ struct elf32_fdpic_loadmap { #else -#define elfhdr elf64_hdr -#define elf_phdr elf64_phdr -#define elf_note elf64_note -#define elf_shdr elf64_shdr -#define elf_sym elf64_sym -#define elf_addr_t Elf64_Off -#define elf_rela elf64_rela +#define elfhdr elf64_hdr +#define elf_phdr elf64_phdr +#define elf_note elf64_note +#define elf_shdr elf64_shdr +#define elf_sym elf64_sym +#define elf_addr_t Elf64_Off +#define elf_rela elf64_rela #ifdef ELF_USES_RELOCA # define ELF_RELOC Elf64_Rela diff --git a/include/exec/address-spaces.h b/include/exec/address-spaces.h index db8bfa9a92..0d0aa61d68 100644 --- a/include/exec/address-spaces.h +++ b/include/exec/address-spaces.h @@ -19,8 +19,6 @@ * you're one of them. */ -#include "exec/memory.h" - #ifndef CONFIG_USER_ONLY /* Get the root memory region. This interface should only be used temporarily diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 5d5290deb5..1a6510fd3b 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -21,17 +21,11 @@ #include "exec/cpu-common.h" #include "exec/memory.h" +#include "exec/tswap.h" #include "qemu/thread.h" #include "hw/core/cpu.h" #include "qemu/rcu.h" -#define EXCP_INTERRUPT 0x10000 /* async interruption */ -#define EXCP_HLT 0x10001 /* hlt instruction reached */ -#define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */ -#define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */ -#define EXCP_YIELD 0x10004 /* cpu wants to yield timeslice to another */ -#define EXCP_ATOMIC 0x10005 /* stop-the-world and emulate atomic */ - /* some important defines: * * HOST_BIG_ENDIAN : whether the host cpu is big endian and @@ -44,69 +38,6 @@ #define BSWAP_NEEDED #endif -#ifdef BSWAP_NEEDED - -static inline uint16_t tswap16(uint16_t s) -{ - return bswap16(s); -} - -static inline uint32_t tswap32(uint32_t s) -{ - return bswap32(s); -} - -static inline uint64_t tswap64(uint64_t s) -{ - return bswap64(s); -} - -static inline void tswap16s(uint16_t *s) -{ - *s = bswap16(*s); -} - -static inline void tswap32s(uint32_t *s) -{ - *s = bswap32(*s); -} - -static inline void tswap64s(uint64_t *s) -{ - *s = bswap64(*s); -} - -#else - -static inline uint16_t tswap16(uint16_t s) -{ - return s; -} - -static inline uint32_t tswap32(uint32_t s) -{ - return s; -} - -static inline uint64_t tswap64(uint64_t s) -{ - return s; -} - -static inline void tswap16s(uint16_t *s) -{ -} - -static inline void tswap32s(uint32_t *s) -{ -} - -static inline void tswap64s(uint64_t *s) -{ -} - -#endif - #if TARGET_LONG_SIZE == 4 #define tswapl(s) tswap32(s) #define tswapls(s) tswap32s((uint32_t *)(s)) @@ -146,12 +77,18 @@ static inline void tswap64s(uint64_t *s) #if defined(CONFIG_USER_ONLY) #include "exec/user/abitypes.h" +#include "exec/user/guest-base.h" -/* On some host systems the guest address space is reserved on the host. - * This allows the guest address space to be offset to a convenient location. - */ -extern uintptr_t guest_base; extern bool have_guest_base; + +/* + * If non-zero, the guest virtual address space is a contiguous subset + * of the host virtual address space, i.e. '-R reserved_va' is in effect + * either from the command-line or by default. The value is the last + * byte of the guest address space e.g. UINT32_MAX. + * + * If zero, the host and guest virtual address spaces are intermingled. + */ extern unsigned long reserved_va; /* @@ -171,7 +108,7 @@ extern unsigned long reserved_va; #define GUEST_ADDR_MAX_ \ ((MIN_CONST(TARGET_VIRT_ADDR_SPACE_BITS, TARGET_ABI_BITS) <= 32) ? \ UINT32_MAX : ~0ul) -#define GUEST_ADDR_MAX (reserved_va ? reserved_va - 1 : GUEST_ADDR_MAX_) +#define GUEST_ADDR_MAX (reserved_va ? : GUEST_ADDR_MAX_) #else @@ -234,33 +171,15 @@ extern const TargetPageBits target_page; #define TARGET_PAGE_ALIGN(addr) ROUND_UP((addr), TARGET_PAGE_SIZE) -/* same as PROT_xxx */ -#define PAGE_READ 0x0001 -#define PAGE_WRITE 0x0002 -#define PAGE_EXEC 0x0004 -#define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC) -#define PAGE_VALID 0x0008 -/* - * Original state of the write flag (used when tracking self-modifying code) - */ -#define PAGE_WRITE_ORG 0x0010 -/* - * Invalidate the TLB entry immediately, helpful for s390x - * Low-Address-Protection. Used with PAGE_WRITE in tlb_set_page_with_attrs() - */ -#define PAGE_WRITE_INV 0x0020 -/* For use with page_set_flags: page is being replaced; target_data cleared. */ -#define PAGE_RESET 0x0040 -/* For linux-user, indicates that the page is MAP_ANON. */ -#define PAGE_ANON 0x0080 - #if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY) /* FIXME: Code that sets/uses this is broken and needs to go away. */ #define PAGE_RESERVED 0x0100 #endif -/* Target-specific bits that will be used via page_get_flags(). */ -#define PAGE_TARGET_1 0x0200 -#define PAGE_TARGET_2 0x0400 +/* + * For linux-user, indicates that the page is mapped with the same semantics + * in both guest and host. + */ +#define PAGE_PASSTHROUGH 0x0800 #if defined(CONFIG_USER_ONLY) void page_dump(FILE *f); @@ -270,32 +189,61 @@ typedef int (*walk_memory_regions_fn)(void *, target_ulong, int walk_memory_regions(void *, walk_memory_regions_fn); int page_get_flags(target_ulong address); -void page_set_flags(target_ulong start, target_ulong end, int flags); -int page_check_range(target_ulong start, target_ulong len, int flags); +void page_set_flags(target_ulong start, target_ulong last, int flags); +void page_reset_target_data(target_ulong start, target_ulong last); /** - * page_alloc_target_data(address, size) - * @address: guest virtual address - * @size: size of data to allocate + * page_check_range + * @start: first byte of range + * @len: length of range + * @flags: flags required for each page * - * Allocate @size bytes of out-of-band data to associate with the - * guest page at @address. If the page is not mapped, NULL will - * be returned. If there is existing data associated with @address, - * no new memory will be allocated. - * - * The memory will be freed when the guest page is deallocated, - * e.g. with the munmap system call. + * Return true if every page in [@start, @start+@len) has @flags set. + * Return false if any page is unmapped. Thus testing flags == 0 is + * equivalent to testing for flags == PAGE_VALID. */ -void *page_alloc_target_data(target_ulong address, size_t size); +bool page_check_range(target_ulong start, target_ulong last, int flags); + +/** + * page_check_range_empty: + * @start: first byte of range + * @last: last byte of range + * Context: holding mmap lock + * + * Return true if the entire range [@start, @last] is unmapped. + * The memory lock must be held so that the caller will can ensure + * the result stays true until a new mapping can be installed. + */ +bool page_check_range_empty(target_ulong start, target_ulong last); + +/** + * page_find_range_empty + * @min: first byte of search range + * @max: last byte of search range + * @len: size of the hole required + * @align: alignment of the hole required (power of 2) + * + * If there is a range [x, x+@len) within [@min, @max] such that + * x % @align == 0, then return x. Otherwise return -1. + * The memory lock must be held, as the caller will want to ensure + * the returned range stays empty until a new mapping can be installed. + */ +target_ulong page_find_range_empty(target_ulong min, target_ulong max, + target_ulong len, target_ulong align); /** * page_get_target_data(address) * @address: guest virtual address * - * Return any out-of-bound memory assocated with the guest page - * at @address, as per page_alloc_target_data. + * Return TARGET_PAGE_DATA_SIZE bytes of out-of-band data to associate + * with the guest page at @address, allocating it if necessary. The + * caller should already have verified that the address is valid. + * + * The memory will be freed when the guest page is deallocated, + * e.g. with the munmap system call. */ -void *page_get_target_data(target_ulong address); +void *page_get_target_data(target_ulong address) + __attribute__((returns_nonnull)); #endif CPUArchState *cpu_copy(CPUArchState *env); @@ -360,9 +308,13 @@ CPUArchState *cpu_copy(CPUArchState *env); * be signaled by probe_access_flags(). */ #define TLB_INVALID_MASK (1 << (TARGET_PAGE_BITS_MIN - 1)) -#define TLB_MMIO 0 +#define TLB_MMIO (1 << (TARGET_PAGE_BITS_MIN - 2)) #define TLB_WATCHPOINT 0 +static inline int cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return MMU_USER_IDX; +} #else /* @@ -373,6 +325,9 @@ CPUArchState *cpu_copy(CPUArchState *env); * * Use TARGET_PAGE_BITS_MIN so that these bits are constant * when TARGET_PAGE_BITS_VARY is in effect. + * + * The count, if not the placement of these bits is known + * to tcg/tcg-op-ldst.c, check_max_alignment(). */ /* Zero if TLB entry is valid. */ #define TLB_INVALID_MASK (1 << (TARGET_PAGE_BITS_MIN - 1)) @@ -381,19 +336,34 @@ CPUArchState *cpu_copy(CPUArchState *env); #define TLB_NOTDIRTY (1 << (TARGET_PAGE_BITS_MIN - 2)) /* Set if TLB entry is an IO callback. */ #define TLB_MMIO (1 << (TARGET_PAGE_BITS_MIN - 3)) -/* Set if TLB entry contains a watchpoint. */ -#define TLB_WATCHPOINT (1 << (TARGET_PAGE_BITS_MIN - 4)) -/* Set if TLB entry requires byte swap. */ -#define TLB_BSWAP (1 << (TARGET_PAGE_BITS_MIN - 5)) /* Set if TLB entry writes ignored. */ -#define TLB_DISCARD_WRITE (1 << (TARGET_PAGE_BITS_MIN - 6)) +#define TLB_DISCARD_WRITE (1 << (TARGET_PAGE_BITS_MIN - 4)) +/* Set if the slow path must be used; more flags in CPUTLBEntryFull. */ +#define TLB_FORCE_SLOW (1 << (TARGET_PAGE_BITS_MIN - 5)) -/* Use this mask to check interception with an alignment mask +/* + * Use this mask to check interception with an alignment mask * in a TCG backend. */ #define TLB_FLAGS_MASK \ (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_MMIO \ - | TLB_WATCHPOINT | TLB_BSWAP | TLB_DISCARD_WRITE) + | TLB_FORCE_SLOW | TLB_DISCARD_WRITE) + +/* + * Flags stored in CPUTLBEntryFull.slow_flags[x]. + * TLB_FORCE_SLOW must be set in CPUTLBEntry.addr_idx[x]. + */ +/* Set if TLB entry requires byte swap. */ +#define TLB_BSWAP (1 << 0) +/* Set if TLB entry contains a watchpoint. */ +#define TLB_WATCHPOINT (1 << 1) +/* Set if TLB entry requires aligned accesses. */ +#define TLB_CHECK_ALIGNED (1 << 2) + +#define TLB_SLOW_FLAGS_MASK (TLB_BSWAP | TLB_WATCHPOINT | TLB_CHECK_ALIGNED) + +/* The two sets of flags must not overlap. */ +QEMU_BUILD_BUG_ON(TLB_FLAGS_MASK & TLB_SLOW_FLAGS_MASK); /** * tlb_hit_page: return true if page aligned @addr is a hit against the @@ -402,7 +372,7 @@ CPUArchState *cpu_copy(CPUArchState *env); * @addr: virtual address to test (must be page aligned) * @tlb_addr: TLB entry address (a CPUTLBEntry addr_read/write/code value) */ -static inline bool tlb_hit_page(target_ulong tlb_addr, target_ulong addr) +static inline bool tlb_hit_page(uint64_t tlb_addr, vaddr addr) { return addr == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)); } @@ -413,93 +383,15 @@ static inline bool tlb_hit_page(target_ulong tlb_addr, target_ulong addr) * @addr: virtual address to test (need not be page aligned) * @tlb_addr: TLB entry address (a CPUTLBEntry addr_read/write/code value) */ -static inline bool tlb_hit(target_ulong tlb_addr, target_ulong addr) +static inline bool tlb_hit(uint64_t tlb_addr, vaddr addr) { return tlb_hit_page(tlb_addr, addr & TARGET_PAGE_MASK); } -#ifdef CONFIG_TCG -/* accel/tcg/cpu-exec.c */ -void dump_drift_info(GString *buf); -/* accel/tcg/translate-all.c */ -void dump_exec_info(GString *buf); -void dump_opcount_info(GString *buf); -#endif /* CONFIG_TCG */ - #endif /* !CONFIG_USER_ONLY */ -/* accel/tcg/cpu-exec.c */ -int cpu_exec(CPUState *cpu); -void tcg_exec_realizefn(CPUState *cpu, Error **errp); -void tcg_exec_unrealizefn(CPUState *cpu); - -/** - * cpu_set_cpustate_pointers(cpu) - * @cpu: The cpu object - * - * Set the generic pointers in CPUState into the outer object. - */ -static inline void cpu_set_cpustate_pointers(ArchCPU *cpu) -{ - cpu->parent_obj.env_ptr = &cpu->env; - cpu->parent_obj.icount_decr_ptr = &cpu->neg.icount_decr; -} - -/** - * env_archcpu(env) - * @env: The architecture environment - * - * Return the ArchCPU associated with the environment. - */ -static inline ArchCPU *env_archcpu(CPUArchState *env) -{ - return container_of(env, ArchCPU, env); -} - -/** - * env_cpu(env) - * @env: The architecture environment - * - * Return the CPUState associated with the environment. - */ -static inline CPUState *env_cpu(CPUArchState *env) -{ - return &env_archcpu(env)->parent_obj; -} - -/** - * env_neg(env) - * @env: The architecture environment - * - * Return the CPUNegativeOffsetState associated with the environment. - */ -static inline CPUNegativeOffsetState *env_neg(CPUArchState *env) -{ - ArchCPU *arch_cpu = container_of(env, ArchCPU, env); - return &arch_cpu->neg; -} - -/** - * cpu_neg(cpu) - * @cpu: The generic CPUState - * - * Return the CPUNegativeOffsetState associated with the cpu. - */ -static inline CPUNegativeOffsetState *cpu_neg(CPUState *cpu) -{ - ArchCPU *arch_cpu = container_of(cpu, ArchCPU, parent_obj); - return &arch_cpu->neg; -} - -/** - * env_tlb(env) - * @env: The architecture environment - * - * Return the CPUTLB state associated with the environment. - */ -static inline CPUTLB *env_tlb(CPUArchState *env) -{ - return &env_neg(env)->tlb; -} +/* Validate correct placement of CPUArchState. */ +QEMU_BUILD_BUG_ON(offsetof(ArchCPU, parent_obj) != 0); +QEMU_BUILD_BUG_ON(offsetof(ArchCPU, env) != sizeof(CPUState)); #endif /* CPU_ALL_H */ diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 5968551a05..6346df17ce 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -3,40 +3,31 @@ /* CPU interfaces that are target independent. */ +#include "exec/vaddr.h" #ifndef CONFIG_USER_ONLY #include "exec/hwaddr.h" #endif +#include "hw/core/cpu.h" +#include "tcg/debug-assert.h" -/** - * vaddr: - * Type wide enough to contain any #target_ulong virtual address. - */ -typedef uint64_t vaddr; -#define VADDR_PRId PRId64 -#define VADDR_PRIu PRIu64 -#define VADDR_PRIo PRIo64 -#define VADDR_PRIx PRIx64 -#define VADDR_PRIX PRIX64 -#define VADDR_MAX UINT64_MAX +#define EXCP_INTERRUPT 0x10000 /* async interruption */ +#define EXCP_HLT 0x10001 /* hlt instruction reached */ +#define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */ +#define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */ +#define EXCP_YIELD 0x10004 /* cpu wants to yield timeslice to another */ +#define EXCP_ATOMIC 0x10005 /* stop-the-world and emulate atomic */ void cpu_exec_init_all(void); void cpu_exec_step_atomic(CPUState *cpu); -/* Using intptr_t ensures that qemu_*_page_mask is sign-extended even - * when intptr_t is 32-bit and we are aligning a long long. - */ -extern uintptr_t qemu_host_page_size; -extern intptr_t qemu_host_page_mask; - -#define HOST_PAGE_ALIGN(addr) ROUND_UP((addr), qemu_host_page_size) #define REAL_HOST_PAGE_ALIGN(addr) ROUND_UP((addr), qemu_real_host_page_size()) /* The CPU list lock nests outside page_(un)lock or mmap_(un)lock */ +extern QemuMutex qemu_cpu_list_lock; void qemu_init_cpu_list(void); void cpu_list_lock(void); void cpu_list_unlock(void); - -void tcg_flush_softmmu_tlb(CPUState *cs); +unsigned int cpu_list_generation_id_get(void); void tcg_iommu_init_notifier_list(CPUState *cpu); void tcg_iommu_free_notifier_list(CPUState *cpu); @@ -71,7 +62,23 @@ typedef uintptr_t ram_addr_t; void qemu_ram_remap(ram_addr_t addr, ram_addr_t length); /* This should not be used by devices. */ ram_addr_t qemu_ram_addr_from_host(void *ptr); +ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr); RAMBlock *qemu_ram_block_by_name(const char *name); + +/* + * Translates a host ptr back to a RAMBlock and an offset in that RAMBlock. + * + * @ptr: The host pointer to translate. + * @round_offset: Whether to round the result offset down to a target page + * @offset: Will be set to the offset within the returned RAMBlock. + * + * Returns: RAMBlock (or NULL if not found) + * + * By the time this function returns, the returned pointer is not protected + * by RCU anymore. If the caller is not within an RCU critical section and + * does not hold the BQL, it must have other means of protecting the + * pointer, such as a reference to the memory region that owns the RAMBlock. + */ RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset, ram_addr_t *offset); ram_addr_t qemu_ram_block_host_offset(RAMBlock *rb, void *host); @@ -89,6 +96,8 @@ void qemu_ram_set_uf_zeroable(RAMBlock *rb); bool qemu_ram_is_migratable(RAMBlock *rb); void qemu_ram_set_migratable(RAMBlock *rb); void qemu_ram_unset_migratable(RAMBlock *rb); +bool qemu_ram_is_named_file(RAMBlock *rb); +int qemu_ram_get_fd(RAMBlock *rb); size_t qemu_ram_pagesize(RAMBlock *block); size_t qemu_ram_pagesize_largest(void); @@ -127,7 +136,6 @@ static inline void cpu_physical_memory_write(hwaddr addr, { cpu_physical_memory_rw(addr, (void *)buf, len, true); } -void cpu_reloading_memory_map(void); void *cpu_physical_memory_map(hwaddr addr, hwaddr *plen, bool is_write); @@ -159,8 +167,113 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, void *ptr, size_t len, bool is_write); /* vl.c */ -extern int singlestep; +void list_cpus(void); -void list_cpus(const char *optarg); +#ifdef CONFIG_TCG +/** + * cpu_unwind_state_data: + * @cpu: the cpu context + * @host_pc: the host pc within the translation + * @data: output data + * + * Attempt to load the the unwind state for a host pc occurring in + * translated code. If @host_pc is not in translated code, the + * function returns false; otherwise @data is loaded. + * This is the same unwind info as given to restore_state_to_opc. + */ +bool cpu_unwind_state_data(CPUState *cpu, uintptr_t host_pc, uint64_t *data); + +/** + * cpu_restore_state: + * @cpu: the cpu context + * @host_pc: the host pc within the translation + * @return: true if state was restored, false otherwise + * + * Attempt to restore the state for a fault occurring in translated + * code. If @host_pc is not in translated code no state is + * restored and the function returns false. + */ +bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc); + +G_NORETURN void cpu_loop_exit_noexc(CPUState *cpu); +G_NORETURN void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc); +#endif /* CONFIG_TCG */ +G_NORETURN void cpu_loop_exit(CPUState *cpu); +G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc); + +/* same as PROT_xxx */ +#define PAGE_READ 0x0001 +#define PAGE_WRITE 0x0002 +#define PAGE_EXEC 0x0004 +#define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC) +#define PAGE_VALID 0x0008 +/* + * Original state of the write flag (used when tracking self-modifying code) + */ +#define PAGE_WRITE_ORG 0x0010 +/* + * Invalidate the TLB entry immediately, helpful for s390x + * Low-Address-Protection. Used with PAGE_WRITE in tlb_set_page_with_attrs() + */ +#define PAGE_WRITE_INV 0x0020 +/* For use with page_set_flags: page is being replaced; target_data cleared. */ +#define PAGE_RESET 0x0040 +/* For linux-user, indicates that the page is MAP_ANON. */ +#define PAGE_ANON 0x0080 + +/* Target-specific bits that will be used via page_get_flags(). */ +#define PAGE_TARGET_1 0x0200 +#define PAGE_TARGET_2 0x0400 + +/* + * For linux-user, indicates that the page is mapped with the same semantics + * in both guest and host. + */ +#define PAGE_PASSTHROUGH 0x0800 + +/* accel/tcg/cpu-exec.c */ +int cpu_exec(CPUState *cpu); + +/** + * env_archcpu(env) + * @env: The architecture environment + * + * Return the ArchCPU associated with the environment. + */ +static inline ArchCPU *env_archcpu(CPUArchState *env) +{ + return (void *)env - sizeof(CPUState); +} + +/** + * env_cpu(env) + * @env: The architecture environment + * + * Return the CPUState associated with the environment. + */ +static inline CPUState *env_cpu(CPUArchState *env) +{ + return (void *)env - sizeof(CPUState); +} + +#ifndef CONFIG_USER_ONLY +/** + * 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. + * + * The user-only version of this function is inline in cpu-all.h, + * where it always returns MMU_USER_IDX. + */ +static inline int cpu_mmu_index(CPUState *cs, bool ifetch) +{ + int ret = cs->cc->mmu_index(cs, ifetch); + tcg_debug_assert(ret >= 0 && ret < NB_MMU_MODES); + return ret; +} +#endif /* !CONFIG_USER_ONLY */ #endif /* CPU_COMMON_H */ diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index ba3cd32a1e..3915438b83 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -36,9 +36,6 @@ #ifndef TARGET_LONG_BITS # error TARGET_LONG_BITS must be defined in cpu-param.h #endif -#ifndef NB_MMU_MODES -# error NB_MMU_MODES must be defined in cpu-param.h -#endif #ifndef TARGET_PHYS_ADDR_SPACE_BITS # error TARGET_PHYS_ADDR_SPACE_BITS must be defined in cpu-param.h #endif @@ -55,36 +52,9 @@ # endif #endif -#define TARGET_LONG_SIZE (TARGET_LONG_BITS / 8) - -/* target_ulong is the type of a virtual address */ -#if TARGET_LONG_SIZE == 4 -typedef int32_t target_long; -typedef uint32_t target_ulong; -#define TARGET_FMT_lx "%08x" -#define TARGET_FMT_ld "%d" -#define TARGET_FMT_lu "%u" -#elif TARGET_LONG_SIZE == 8 -typedef int64_t target_long; -typedef uint64_t target_ulong; -#define TARGET_FMT_lx "%016" PRIx64 -#define TARGET_FMT_ld "%" PRId64 -#define TARGET_FMT_lu "%" PRIu64 -#else -#error TARGET_LONG_SIZE undefined -#endif - -#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) - -/* use a fully associative victim tlb of 8 entries */ -#define CPU_VTLB_SIZE 8 - -#if HOST_LONG_BITS == 32 && TARGET_LONG_BITS == 32 -#define CPU_TLB_ENTRY_BITS 4 -#else -#define CPU_TLB_ENTRY_BITS 5 -#endif +#include "exec/target_long.h" +#if defined(CONFIG_SOFTMMU) && defined(CONFIG_TCG) #define CPU_TLB_DYN_MIN_BITS 6 #define CPU_TLB_DYN_DEFAULT_BITS 8 @@ -108,138 +78,6 @@ typedef uint64_t target_ulong; # endif # endif -typedef struct CPUTLBEntry { - /* bit TARGET_LONG_BITS to TARGET_PAGE_BITS : virtual address - bit TARGET_PAGE_BITS-1..4 : Nonzero for accesses that should not - go directly to ram. - bit 3 : indicates that the entry is invalid - bit 2..0 : zero - */ - union { - struct { - target_ulong addr_read; - target_ulong addr_write; - target_ulong addr_code; - /* Addend to virtual address to get host address. IO accesses - use the corresponding iotlb value. */ - uintptr_t addend; - }; - /* padding to get a power of two size */ - uint8_t dummy[1 << CPU_TLB_ENTRY_BITS]; - }; -} CPUTLBEntry; - -QEMU_BUILD_BUG_ON(sizeof(CPUTLBEntry) != (1 << CPU_TLB_ENTRY_BITS)); - -/* The IOTLB is not accessed directly inline by generated TCG code, - * so the CPUIOTLBEntry layout is not as critical as that of the - * CPUTLBEntry. (This is also why we don't want to combine the two - * structs into one.) - */ -typedef struct CPUIOTLBEntry { - /* - * @addr contains: - * - in the lower TARGET_PAGE_BITS, a physical section number - * - with the lower TARGET_PAGE_BITS masked off, an offset which - * must be added to the virtual address to obtain: - * + the ram_addr_t of the target RAM (if the physical section - * number is PHYS_SECTION_NOTDIRTY or PHYS_SECTION_ROM) - * + the offset within the target MemoryRegion (otherwise) - */ - hwaddr addr; - MemTxAttrs attrs; -} CPUIOTLBEntry; - -/* - * Data elements that are per MMU mode, minus the bits accessed by - * the TCG fast path. - */ -typedef struct CPUTLBDesc { - /* - * Describe a region covering all of the large pages allocated - * into the tlb. When any page within this region is flushed, - * we must flush the entire tlb. The region is matched if - * (addr & large_page_mask) == large_page_addr. - */ - target_ulong large_page_addr; - target_ulong large_page_mask; - /* host time (in ns) at the beginning of the time window */ - int64_t window_begin_ns; - /* maximum number of entries observed in the window */ - size_t window_max_entries; - size_t n_used_entries; - /* The next index to use in the tlb victim table. */ - size_t vindex; - /* The tlb victim table, in two parts. */ - CPUTLBEntry vtable[CPU_VTLB_SIZE]; - CPUIOTLBEntry viotlb[CPU_VTLB_SIZE]; - /* The iotlb. */ - CPUIOTLBEntry *iotlb; -} CPUTLBDesc; - -/* - * Data elements that are per MMU mode, accessed by the fast path. - * The structure is aligned to aid loading the pair with one insn. - */ -typedef struct CPUTLBDescFast { - /* Contains (n_entries - 1) << CPU_TLB_ENTRY_BITS */ - uintptr_t mask; - /* The array of tlb entries itself. */ - CPUTLBEntry *table; -} CPUTLBDescFast QEMU_ALIGNED(2 * sizeof(void *)); - -/* - * Data elements that are shared between all MMU modes. - */ -typedef struct CPUTLBCommon { - /* Serialize updates to f.table and d.vtable, and others as noted. */ - QemuSpin lock; - /* - * Within dirty, for each bit N, modifications have been made to - * mmu_idx N since the last time that mmu_idx was flushed. - * Protected by tlb_c.lock. - */ - uint16_t dirty; - /* - * Statistics. These are not lock protected, but are read and - * written atomically. This allows the monitor to print a snapshot - * of the stats without interfering with the cpu. - */ - size_t full_flush_count; - size_t part_flush_count; - size_t elide_flush_count; -} CPUTLBCommon; - -/* - * The entire softmmu tlb, for all MMU modes. - * The meaning of each of the MMU modes is defined in the target code. - * Since this is placed within CPUNegativeOffsetState, the smallest - * negative offsets are at the end of the struct. - */ - -typedef struct CPUTLB { - CPUTLBCommon c; - CPUTLBDesc d[NB_MMU_MODES]; - CPUTLBDescFast f[NB_MMU_MODES]; -} CPUTLB; - -/* This will be used by TCG backends to compute offsets. */ -#define TLB_MASK_TABLE_OFS(IDX) \ - ((int)offsetof(ArchCPU, neg.tlb.f[IDX]) - (int)offsetof(ArchCPU, env)) - -#else - -typedef struct CPUTLB { } CPUTLB; - -#endif /* !CONFIG_USER_ONLY && CONFIG_TCG */ - -/* - * This structure must be placed in ArchCPU immediately - * before CPUArchState, as a field named "neg". - */ -typedef struct CPUNegativeOffsetState { - CPUTLB tlb; - IcountDecr icount_decr; -} CPUNegativeOffsetState; +#endif /* CONFIG_SOFTMMU && CONFIG_TCG */ #endif diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index d0c7c0d5fe..eb8f3f0595 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -121,8 +121,8 @@ static inline bool guest_range_valid_untagged(abi_ulong start, abi_ulong len) h2g_nocheck(x); \ }) #else -typedef target_ulong abi_ptr; -#define TARGET_ABI_FMT_ptr TARGET_FMT_lx +typedef vaddr abi_ptr; +#define TARGET_ABI_FMT_ptr VADDR_PRIx #endif uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr); @@ -207,59 +207,47 @@ void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint64_t val, int mmu_idx, uintptr_t ra); uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr ptr, MemOpIdx oi, uintptr_t ra); -uint16_t cpu_ldw_be_mmu(CPUArchState *env, abi_ptr ptr, - MemOpIdx oi, uintptr_t ra); -uint32_t cpu_ldl_be_mmu(CPUArchState *env, abi_ptr ptr, - MemOpIdx oi, uintptr_t ra); -uint64_t cpu_ldq_be_mmu(CPUArchState *env, abi_ptr ptr, - MemOpIdx oi, uintptr_t ra); -uint16_t cpu_ldw_le_mmu(CPUArchState *env, abi_ptr ptr, - MemOpIdx oi, uintptr_t ra); -uint32_t cpu_ldl_le_mmu(CPUArchState *env, abi_ptr ptr, - MemOpIdx oi, uintptr_t ra); -uint64_t cpu_ldq_le_mmu(CPUArchState *env, abi_ptr ptr, - MemOpIdx oi, uintptr_t ra); +uint16_t cpu_ldw_mmu(CPUArchState *env, abi_ptr ptr, MemOpIdx oi, uintptr_t ra); +uint32_t cpu_ldl_mmu(CPUArchState *env, abi_ptr ptr, MemOpIdx oi, uintptr_t ra); +uint64_t cpu_ldq_mmu(CPUArchState *env, abi_ptr ptr, MemOpIdx oi, uintptr_t ra); +Int128 cpu_ld16_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra); void cpu_stb_mmu(CPUArchState *env, abi_ptr ptr, uint8_t val, MemOpIdx oi, uintptr_t ra); -void cpu_stw_be_mmu(CPUArchState *env, abi_ptr ptr, uint16_t val, - MemOpIdx oi, uintptr_t ra); -void cpu_stl_be_mmu(CPUArchState *env, abi_ptr ptr, uint32_t val, - MemOpIdx oi, uintptr_t ra); -void cpu_stq_be_mmu(CPUArchState *env, abi_ptr ptr, uint64_t val, - MemOpIdx oi, uintptr_t ra); -void cpu_stw_le_mmu(CPUArchState *env, abi_ptr ptr, uint16_t val, - MemOpIdx oi, uintptr_t ra); -void cpu_stl_le_mmu(CPUArchState *env, abi_ptr ptr, uint32_t val, - MemOpIdx oi, uintptr_t ra); -void cpu_stq_le_mmu(CPUArchState *env, abi_ptr ptr, uint64_t val, - MemOpIdx oi, uintptr_t ra); +void cpu_stw_mmu(CPUArchState *env, abi_ptr ptr, uint16_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_stl_mmu(CPUArchState *env, abi_ptr ptr, uint32_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_stq_mmu(CPUArchState *env, abi_ptr ptr, uint64_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_st16_mmu(CPUArchState *env, abi_ptr addr, Int128 val, + MemOpIdx oi, uintptr_t ra); -uint32_t cpu_atomic_cmpxchgb_mmu(CPUArchState *env, target_ulong addr, +uint32_t cpu_atomic_cmpxchgb_mmu(CPUArchState *env, abi_ptr addr, uint32_t cmpv, uint32_t newv, MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgw_le_mmu(CPUArchState *env, target_ulong addr, +uint32_t cpu_atomic_cmpxchgw_le_mmu(CPUArchState *env, abi_ptr addr, uint32_t cmpv, uint32_t newv, MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgl_le_mmu(CPUArchState *env, target_ulong addr, +uint32_t cpu_atomic_cmpxchgl_le_mmu(CPUArchState *env, abi_ptr addr, uint32_t cmpv, uint32_t newv, MemOpIdx oi, uintptr_t retaddr); -uint64_t cpu_atomic_cmpxchgq_le_mmu(CPUArchState *env, target_ulong addr, +uint64_t cpu_atomic_cmpxchgq_le_mmu(CPUArchState *env, abi_ptr addr, uint64_t cmpv, uint64_t newv, MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgw_be_mmu(CPUArchState *env, target_ulong addr, +uint32_t cpu_atomic_cmpxchgw_be_mmu(CPUArchState *env, abi_ptr addr, uint32_t cmpv, uint32_t newv, MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgl_be_mmu(CPUArchState *env, target_ulong addr, +uint32_t cpu_atomic_cmpxchgl_be_mmu(CPUArchState *env, abi_ptr addr, uint32_t cmpv, uint32_t newv, MemOpIdx oi, uintptr_t retaddr); -uint64_t cpu_atomic_cmpxchgq_be_mmu(CPUArchState *env, target_ulong addr, +uint64_t cpu_atomic_cmpxchgq_be_mmu(CPUArchState *env, abi_ptr addr, uint64_t cmpv, uint64_t newv, MemOpIdx oi, uintptr_t retaddr); -#define GEN_ATOMIC_HELPER(NAME, TYPE, SUFFIX) \ -TYPE cpu_atomic_ ## NAME ## SUFFIX ## _mmu \ - (CPUArchState *env, target_ulong addr, TYPE val, \ +#define GEN_ATOMIC_HELPER(NAME, TYPE, SUFFIX) \ +TYPE cpu_atomic_ ## NAME ## SUFFIX ## _mmu \ + (CPUArchState *env, abi_ptr addr, TYPE val, \ MemOpIdx oi, uintptr_t retaddr); #ifdef CONFIG_ATOMIC64 @@ -305,22 +293,13 @@ GEN_ATOMIC_HELPER_ALL(xchg) #undef GEN_ATOMIC_HELPER_ALL #undef GEN_ATOMIC_HELPER -Int128 cpu_atomic_cmpxchgo_le_mmu(CPUArchState *env, target_ulong addr, +Int128 cpu_atomic_cmpxchgo_le_mmu(CPUArchState *env, abi_ptr addr, Int128 cmpv, Int128 newv, MemOpIdx oi, uintptr_t retaddr); -Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, target_ulong addr, +Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, abi_ptr addr, Int128 cmpv, Int128 newv, MemOpIdx oi, uintptr_t retaddr); -Int128 cpu_atomic_ldo_le_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -Int128 cpu_atomic_ldo_be_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -void cpu_atomic_sto_le_mmu(CPUArchState *env, target_ulong addr, Int128 val, - MemOpIdx oi, uintptr_t retaddr); -void cpu_atomic_sto_be_mmu(CPUArchState *env, target_ulong addr, Int128 val, - MemOpIdx oi, uintptr_t retaddr); - #if defined(CONFIG_USER_ONLY) extern __thread uintptr_t helper_retaddr; @@ -347,32 +326,54 @@ static inline void clear_helper_retaddr(void) #else -/* Needed for TCG_OVERSIZED_GUEST */ -#include "tcg/tcg.h" +#include "tcg/oversized-guest.h" -static inline target_ulong tlb_addr_write(const CPUTLBEntry *entry) +static inline uint64_t tlb_read_idx(const CPUTLBEntry *entry, + MMUAccessType access_type) { -#if TCG_OVERSIZED_GUEST - return entry->addr_write; + /* Do not rearrange the CPUTLBEntry structure members. */ + QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_read) != + MMU_DATA_LOAD * sizeof(uint64_t)); + QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_write) != + MMU_DATA_STORE * sizeof(uint64_t)); + QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_code) != + MMU_INST_FETCH * sizeof(uint64_t)); + +#if TARGET_LONG_BITS == 32 + /* Use qatomic_read, in case of addr_write; only care about low bits. */ + const uint32_t *ptr = (uint32_t *)&entry->addr_idx[access_type]; + ptr += HOST_BIG_ENDIAN; + return qatomic_read(ptr); #else - return qatomic_read(&entry->addr_write); + const uint64_t *ptr = &entry->addr_idx[access_type]; +# if TCG_OVERSIZED_GUEST + return *ptr; +# else + /* ofs might correspond to .addr_write, so use qatomic_read */ + return qatomic_read(ptr); +# endif #endif } -/* Find the TLB index corresponding to the mmu_idx + address pair. */ -static inline uintptr_t tlb_index(CPUArchState *env, uintptr_t mmu_idx, - target_ulong addr) +static inline uint64_t tlb_addr_write(const CPUTLBEntry *entry) { - uintptr_t size_mask = env_tlb(env)->f[mmu_idx].mask >> CPU_TLB_ENTRY_BITS; + return tlb_read_idx(entry, MMU_DATA_STORE); +} + +/* Find the TLB index corresponding to the mmu_idx + address pair. */ +static inline uintptr_t tlb_index(CPUState *cpu, uintptr_t mmu_idx, + vaddr addr) +{ + uintptr_t size_mask = cpu->neg.tlb.f[mmu_idx].mask >> CPU_TLB_ENTRY_BITS; return (addr >> TARGET_PAGE_BITS) & size_mask; } /* 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) +static inline CPUTLBEntry *tlb_entry(CPUState *cpu, uintptr_t mmu_idx, + vaddr addr) { - return &env_tlb(env)->f[mmu_idx].table[tlb_index(env, mmu_idx, addr)]; + return &cpu->neg.tlb.f[mmu_idx].table[tlb_index(cpu, mmu_idx, addr)]; } #endif /* defined(CONFIG_USER_ONLY) */ @@ -390,9 +391,6 @@ static inline CPUTLBEntry *tlb_entry(CPUArchState *env, uintptr_t mmu_idx, # define cpu_ldsw_mmuidx_ra cpu_ldsw_be_mmuidx_ra # define cpu_ldl_mmuidx_ra cpu_ldl_be_mmuidx_ra # define cpu_ldq_mmuidx_ra cpu_ldq_be_mmuidx_ra -# define cpu_ldw_mmu cpu_ldw_be_mmu -# define cpu_ldl_mmu cpu_ldl_be_mmu -# define cpu_ldq_mmu cpu_ldq_be_mmu # define cpu_stw_data cpu_stw_be_data # define cpu_stl_data cpu_stl_be_data # define cpu_stq_data cpu_stq_be_data @@ -402,9 +400,6 @@ static inline CPUTLBEntry *tlb_entry(CPUArchState *env, uintptr_t mmu_idx, # define cpu_stw_mmuidx_ra cpu_stw_be_mmuidx_ra # define cpu_stl_mmuidx_ra cpu_stl_be_mmuidx_ra # define cpu_stq_mmuidx_ra cpu_stq_be_mmuidx_ra -# define cpu_stw_mmu cpu_stw_be_mmu -# define cpu_stl_mmu cpu_stl_be_mmu -# define cpu_stq_mmu cpu_stq_be_mmu #else # define cpu_lduw_data cpu_lduw_le_data # define cpu_ldsw_data cpu_ldsw_le_data @@ -418,9 +413,6 @@ static inline CPUTLBEntry *tlb_entry(CPUArchState *env, uintptr_t mmu_idx, # define cpu_ldsw_mmuidx_ra cpu_ldsw_le_mmuidx_ra # define cpu_ldl_mmuidx_ra cpu_ldl_le_mmuidx_ra # define cpu_ldq_mmuidx_ra cpu_ldq_le_mmuidx_ra -# define cpu_ldw_mmu cpu_ldw_le_mmu -# define cpu_ldl_mmu cpu_ldl_le_mmu -# define cpu_ldq_mmu cpu_ldq_le_mmu # define cpu_stw_data cpu_stw_le_data # define cpu_stl_data cpu_stl_le_data # define cpu_stq_data cpu_stq_le_data @@ -430,11 +422,17 @@ static inline CPUTLBEntry *tlb_entry(CPUArchState *env, uintptr_t mmu_idx, # define cpu_stw_mmuidx_ra cpu_stw_le_mmuidx_ra # define cpu_stl_mmuidx_ra cpu_stl_le_mmuidx_ra # define cpu_stq_mmuidx_ra cpu_stq_le_mmuidx_ra -# define cpu_stw_mmu cpu_stw_le_mmu -# define cpu_stl_mmu cpu_stl_le_mmu -# define cpu_stq_mmu cpu_stq_le_mmu #endif +uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra); +uint16_t cpu_ldw_code_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra); +uint32_t cpu_ldl_code_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra); +uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr, + MemOpIdx oi, uintptr_t ra); + 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); diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index 19b16e58f8..6da1462c4f 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -26,6 +26,5 @@ /* cputlb.c */ void tlb_protect_code(ram_addr_t ram_addr); void tlb_unprotect_code(ram_addr_t ram_addr); -void tlb_flush_counts(size_t *full, size_t *part, size_t *elide); #endif #endif diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 311e5fb422..3e53501691 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -21,47 +21,11 @@ #define EXEC_ALL_H #include "cpu.h" -#ifdef CONFIG_TCG +#if defined(CONFIG_USER_ONLY) #include "exec/cpu_ldst.h" #endif - -/* allow to see translation results - the slowdown should be negligible, so we leave it */ -#define DEBUG_DISAS - -/* Page tracking code uses ram addresses in system mode, and virtual - addresses in userspace mode. Define tb_page_addr_t to be an appropriate - type. */ -#if defined(CONFIG_USER_ONLY) -typedef abi_ulong tb_page_addr_t; -#define TB_PAGE_ADDR_FMT TARGET_ABI_FMT_lx -#else -typedef ram_addr_t tb_page_addr_t; -#define TB_PAGE_ADDR_FMT RAM_ADDR_FMT -#endif - -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns); -void restore_state_to_opc(CPUArchState *env, TranslationBlock *tb, - target_ulong *data); - -/** - * cpu_restore_state: - * @cpu: the vCPU state is to be restore to - * @searched_pc: the host PC the fault occurred at - * @will_exit: true if the TB executed will be interrupted after some - cpu adjustments. Required for maintaining the correct - icount valus - * @return: true if state was restored, false otherwise - * - * Attempt to restore the state for a fault occurring in translated - * code. If the searched_pc is not in translated code no state is - * restored and the function returns false. - */ -bool cpu_restore_state(CPUState *cpu, uintptr_t searched_pc, bool will_exit); - -G_NORETURN void cpu_loop_exit_noexc(CPUState *cpu); -G_NORETURN void cpu_loop_exit(CPUState *cpu); -G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc); -G_NORETURN void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc); +#include "exec/translation-block.h" +#include "qemu/clang-tsa.h" /** * cpu_loop_exit_requested: @@ -77,7 +41,7 @@ G_NORETURN void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc); */ static inline bool cpu_loop_exit_requested(CPUState *cpu) { - return (int32_t)qatomic_read(&cpu_neg(cpu)->icount_decr.u32) < 0; + return (int32_t)qatomic_read(&cpu->neg.icount_decr.u32) < 0; } #if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) @@ -100,7 +64,7 @@ void tlb_destroy(CPUState *cpu); * Flush one page from the TLB of the specified CPU, for all * MMU indexes. */ -void tlb_flush_page(CPUState *cpu, target_ulong addr); +void tlb_flush_page(CPUState *cpu, vaddr addr); /** * tlb_flush_page_all_cpus: * @cpu: src CPU of the flush @@ -109,7 +73,7 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr); * Flush one page from the TLB of the specified CPU, for all * MMU indexes. */ -void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr); +void tlb_flush_page_all_cpus(CPUState *src, vaddr addr); /** * tlb_flush_page_all_cpus_synced: * @cpu: src CPU of the flush @@ -121,7 +85,7 @@ void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr); * the source vCPUs safe work is complete. This will depend on when * the guests translation ends the TB. */ -void tlb_flush_page_all_cpus_synced(CPUState *src, target_ulong addr); +void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr); /** * tlb_flush: * @cpu: CPU whose TLB should be flushed @@ -156,7 +120,7 @@ void tlb_flush_all_cpus_synced(CPUState *src_cpu); * Flush one page from the TLB of the specified CPU, for the specified * MMU indexes. */ -void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, +void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap); /** * tlb_flush_page_by_mmuidx_all_cpus: @@ -167,7 +131,7 @@ void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, * Flush one page from the TLB of all CPUs, for the specified * MMU indexes. */ -void tlb_flush_page_by_mmuidx_all_cpus(CPUState *cpu, target_ulong addr, +void tlb_flush_page_by_mmuidx_all_cpus(CPUState *cpu, vaddr addr, uint16_t idxmap); /** * tlb_flush_page_by_mmuidx_all_cpus_synced: @@ -181,7 +145,7 @@ void tlb_flush_page_by_mmuidx_all_cpus(CPUState *cpu, target_ulong addr, * complete once the source vCPUs safe work is complete. This will * depend on when the guests translation ends the TB. */ -void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, target_ulong addr, +void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, uint16_t idxmap); /** * tlb_flush_by_mmuidx: @@ -224,14 +188,14 @@ void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap); * * Similar to tlb_flush_page_mask, but with a bitmap of indexes. */ -void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, target_ulong addr, +void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap, unsigned bits); /* Similarly, with broadcast and syncing. */ -void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *cpu, target_ulong addr, +void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *cpu, vaddr addr, uint16_t idxmap, unsigned bits); void tlb_flush_page_bits_by_mmuidx_all_cpus_synced - (CPUState *cpu, target_ulong addr, uint16_t idxmap, unsigned bits); + (CPUState *cpu, vaddr addr, uint16_t idxmap, unsigned bits); /** * tlb_flush_range_by_mmuidx @@ -244,24 +208,46 @@ void tlb_flush_page_bits_by_mmuidx_all_cpus_synced * For each mmuidx in @idxmap, flush all pages within [@addr,@addr+@len), * comparing only the low @bits worth of each virtual page. */ -void tlb_flush_range_by_mmuidx(CPUState *cpu, target_ulong addr, - target_ulong len, uint16_t idxmap, +void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, + vaddr len, uint16_t idxmap, unsigned bits); /* Similarly, with broadcast and syncing. */ -void tlb_flush_range_by_mmuidx_all_cpus(CPUState *cpu, target_ulong addr, - target_ulong len, uint16_t idxmap, +void tlb_flush_range_by_mmuidx_all_cpus(CPUState *cpu, vaddr addr, + vaddr len, uint16_t idxmap, unsigned bits); void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, - target_ulong addr, - target_ulong len, + vaddr addr, + vaddr len, uint16_t idxmap, unsigned bits); +/** + * tlb_set_page_full: + * @cpu: CPU context + * @mmu_idx: mmu index of the tlb to modify + * @addr: virtual address of the entry to add + * @full: the details of the tlb entry + * + * Add an entry to @cpu tlb index @mmu_idx. All of the fields of + * @full must be filled, except for xlat_section, and constitute + * the complete description of the translated page. + * + * This is generally called by the target tlb_fill function after + * having performed a successful page table walk to find the physical + * address and attributes for the translation. + * + * At most one entry for a given virtual address is permitted. Only a + * single TARGET_PAGE_SIZE region is mapped; @full->lg_page_size is only + * used by tlb_flush_page. + */ +void tlb_set_page_full(CPUState *cpu, int mmu_idx, vaddr addr, + CPUTLBEntryFull *full); + /** * tlb_set_page_with_attrs: * @cpu: CPU to add this TLB entry for - * @vaddr: virtual address of page to add entry for + * @addr: virtual address of page to add entry for * @paddr: physical address of the page * @attrs: memory transaction attributes * @prot: access permissions (PAGE_READ/PAGE_WRITE/PAGE_EXEC bits) @@ -269,7 +255,7 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, * @size: size of the page in bytes * * Add an entry to this CPU's TLB (a mapping from virtual address - * @vaddr to physical address @paddr) with the specified memory + * @addr to physical address @paddr) with the specified memory * transaction attributes. This is generally called by the target CPU * specific code after it has been called through the tlb_fill() * entry point and performed a successful page table walk to find @@ -280,18 +266,18 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, * single TARGET_PAGE_SIZE region is mapped; the supplied @size is only * used by tlb_flush_page. */ -void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, +void tlb_set_page_with_attrs(CPUState *cpu, vaddr addr, hwaddr paddr, MemTxAttrs attrs, - int prot, int mmu_idx, target_ulong size); + int prot, int mmu_idx, vaddr size); /* tlb_set_page: * * This function is equivalent to calling tlb_set_page_with_attrs() * with an @attrs argument of MEMTXATTRS_UNSPECIFIED. It's provided * as a convenience for CPUs which don't use memory transaction attributes. */ -void tlb_set_page(CPUState *cpu, target_ulong vaddr, +void tlb_set_page(CPUState *cpu, vaddr addr, hwaddr paddr, int prot, - int mmu_idx, target_ulong size); + int mmu_idx, vaddr size); #else static inline void tlb_init(CPUState *cpu) { @@ -299,14 +285,13 @@ static inline void tlb_init(CPUState *cpu) static inline void tlb_destroy(CPUState *cpu) { } -static inline void tlb_flush_page(CPUState *cpu, target_ulong addr) +static inline void tlb_flush_page(CPUState *cpu, vaddr addr) { } -static inline void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr) +static inline void tlb_flush_page_all_cpus(CPUState *src, vaddr addr) { } -static inline void tlb_flush_page_all_cpus_synced(CPUState *src, - target_ulong addr) +static inline void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr) { } static inline void tlb_flush(CPUState *cpu) @@ -319,7 +304,7 @@ static inline void tlb_flush_all_cpus_synced(CPUState *src_cpu) { } static inline void tlb_flush_page_by_mmuidx(CPUState *cpu, - target_ulong addr, uint16_t idxmap) + vaddr addr, uint16_t idxmap) { } @@ -327,12 +312,12 @@ static inline void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) { } static inline void tlb_flush_page_by_mmuidx_all_cpus(CPUState *cpu, - target_ulong addr, + vaddr addr, uint16_t idxmap) { } static inline void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, - target_ulong addr, + vaddr addr, uint16_t idxmap) { } @@ -345,37 +330,37 @@ static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, { } static inline void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, - target_ulong addr, + vaddr addr, uint16_t idxmap, unsigned bits) { } static inline void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *cpu, - target_ulong addr, + vaddr addr, uint16_t idxmap, unsigned bits) { } static inline void -tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *cpu, target_ulong addr, +tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, uint16_t idxmap, unsigned bits) { } -static inline void tlb_flush_range_by_mmuidx(CPUState *cpu, target_ulong addr, - target_ulong len, uint16_t idxmap, +static inline void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, + vaddr len, uint16_t idxmap, unsigned bits) { } static inline void tlb_flush_range_by_mmuidx_all_cpus(CPUState *cpu, - target_ulong addr, - target_ulong len, + vaddr addr, + vaddr len, uint16_t idxmap, unsigned bits) { } static inline void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, - target_ulong addr, - target_long len, + vaddr addr, + vaddr len, uint16_t idxmap, unsigned bits) { @@ -398,16 +383,16 @@ static inline void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, * Finally, return the host address for a page that is backed by RAM, * or NULL if the page requires I/O. */ -void *probe_access(CPUArchState *env, target_ulong addr, int size, +void *probe_access(CPUArchState *env, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); -static inline void *probe_write(CPUArchState *env, target_ulong addr, int size, +static inline void *probe_write(CPUArchState *env, vaddr addr, int size, int mmu_idx, uintptr_t retaddr) { return probe_access(env, addr, size, MMU_DATA_STORE, mmu_idx, retaddr); } -static inline void *probe_read(CPUArchState *env, target_ulong addr, int size, +static inline void *probe_read(CPUArchState *env, vaddr addr, int size, int mmu_idx, uintptr_t retaddr) { return probe_access(env, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr); @@ -417,6 +402,7 @@ static inline void *probe_read(CPUArchState *env, target_ulong addr, int size, * probe_access_flags: * @env: CPUArchState * @addr: guest virtual address to look up + * @size: size of the access * @access_type: read, write or execute permission * @mmu_idx: MMU index to use for lookup * @nonfault: suppress the fault @@ -431,130 +417,103 @@ static inline void *probe_read(CPUArchState *env, target_ulong addr, int size, * Do handle clean pages, so exclude TLB_NOTDIRY from the returned flags. * For simplicity, all "mmio-like" flags are folded to TLB_MMIO. */ -int probe_access_flags(CPUArchState *env, target_ulong addr, +int probe_access_flags(CPUArchState *env, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, bool nonfault, void **phost, uintptr_t retaddr); -#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ +#ifndef CONFIG_USER_ONLY +/** + * probe_access_full: + * Like probe_access_flags, except also return into @pfull. + * + * The CPUTLBEntryFull structure returned via @pfull is transient + * and must be consumed or copied immediately, before any further + * access or changes to TLB @mmu_idx. + */ +int probe_access_full(CPUArchState *env, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + bool nonfault, void **phost, + CPUTLBEntryFull **pfull, uintptr_t retaddr); + +/** + * probe_access_mmu() - Like probe_access_full except cannot fault and + * doesn't trigger instrumentation. + * + * @env: CPUArchState + * @vaddr: virtual address to probe + * @size: size of the probe + * @access_type: read, write or execute permission + * @mmu_idx: softmmu index + * @phost: ptr to return value host address or NULL + * @pfull: ptr to return value CPUTLBEntryFull structure or NULL + * + * The CPUTLBEntryFull structure returned via @pfull is transient + * and must be consumed or copied immediately, before any further + * access or changes to TLB @mmu_idx. + * + * Returns: TLB flags as per probe_access_flags() + */ +int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + void **phost, CPUTLBEntryFull **pfull); -/* Estimated block size for TB allocation. */ -/* ??? The following is based on a 2015 survey of x86_64 host output. - Better would seem to be some sort of dynamically sized TB array, - adapting to the block sizes actually being produced. */ -#if defined(CONFIG_SOFTMMU) -#define CODE_GEN_AVG_BLOCK_SIZE 400 -#else -#define CODE_GEN_AVG_BLOCK_SIZE 150 #endif -/* - * Translation Cache-related fields of a TB. - * This struct exists just for convenience; we keep track of TB's in a binary - * search tree, and the only fields needed to compare TB's in the tree are - * @ptr and @size. - * Note: the address of search data can be obtained by adding @size to @ptr. - */ -struct tb_tc { - const void *ptr; /* pointer to the translated code */ - size_t size; -}; - -struct TranslationBlock { - target_ulong pc; /* simulated PC corresponding to this block (EIP + CS base) */ - target_ulong cs_base; /* CS base for this block */ - uint32_t flags; /* flags defining in which context the code was generated */ - uint32_t cflags; /* compile flags */ - -/* Note that TCG_MAX_INSNS is 512; we validate this match elsewhere. */ -#define CF_COUNT_MASK 0x000001ff -#define CF_NO_GOTO_TB 0x00000200 /* Do not chain with goto_tb */ -#define CF_NO_GOTO_PTR 0x00000400 /* Do not chain with goto_ptr */ -#define CF_SINGLE_STEP 0x00000800 /* gdbstub single-step in effect */ -#define CF_LAST_IO 0x00008000 /* Last insn may be an IO access. */ -#define CF_MEMI_ONLY 0x00010000 /* Only instrument memory ops */ -#define CF_USE_ICOUNT 0x00020000 -#define CF_INVALID 0x00040000 /* TB is stale. Set with @jmp_lock held */ -#define CF_PARALLEL 0x00080000 /* Generate code for a parallel context */ -#define CF_NOIRQ 0x00100000 /* Generate an uninterruptible TB */ -#define CF_CLUSTER_MASK 0xff000000 /* Top 8 bits are cluster ID */ -#define CF_CLUSTER_SHIFT 24 - - /* Per-vCPU dynamic tracing state used to generate this TB */ - uint32_t trace_vcpu_dstate; - - /* - * Above fields used for comparing - */ - - /* size of target code for this block (1 <= size <= TARGET_PAGE_SIZE) */ - uint16_t size; - uint16_t icount; - - struct tb_tc tc; - - /* first and second physical page containing code. The lower bit - of the pointer tells the index in page_next[]. - The list is protected by the TB's page('s) lock(s) */ - uintptr_t page_next[2]; - tb_page_addr_t page_addr[2]; - - /* jmp_lock placed here to fill a 4-byte hole. Its documentation is below */ - QemuSpin jmp_lock; - - /* The following data are used to directly call another TB from - * the code of this one. This can be done either by emitting direct or - * indirect native jump instructions. These jumps are reset so that the TB - * just continues its execution. The TB can be linked to another one by - * setting one of the jump targets (or patching the jump instruction). Only - * two of such jumps are supported. - */ - uint16_t jmp_reset_offset[2]; /* offset of original jump target */ -#define TB_JMP_RESET_OFFSET_INVALID 0xffff /* indicates no jump generated */ - uintptr_t jmp_target_arg[2]; /* target address or offset */ - - /* - * Each TB has a NULL-terminated list (jmp_list_head) of incoming jumps. - * Each TB can have two outgoing jumps, and therefore can participate - * in two lists. The list entries are kept in jmp_list_next[2]. The least - * significant bit (LSB) of the pointers in these lists is used to encode - * which of the two list entries is to be used in the pointed TB. - * - * List traversals are protected by jmp_lock. The destination TB of each - * outgoing jump is kept in jmp_dest[] so that the appropriate jmp_lock - * can be acquired from any origin TB. - * - * jmp_dest[] are tagged pointers as well. The LSB is set when the TB is - * being invalidated, so that no further outgoing jumps from it can be set. - * - * jmp_lock also protects the CF_INVALID cflag; a jump must not be chained - * to a destination TB that has CF_INVALID set. - */ - uintptr_t jmp_list_head; - uintptr_t jmp_list_next[2]; - uintptr_t jmp_dest[2]; -}; - -/* Hide the qatomic_read to make code a little easier on the eyes */ -static inline uint32_t tb_cflags(const TranslationBlock *tb) +static inline tb_page_addr_t tb_page_addr0(const TranslationBlock *tb) { - return qatomic_read(&tb->cflags); +#ifdef CONFIG_USER_ONLY + return tb->itree.start; +#else + return tb->page_addr[0]; +#endif +} + +static inline tb_page_addr_t tb_page_addr1(const TranslationBlock *tb) +{ +#ifdef CONFIG_USER_ONLY + tb_page_addr_t next = tb->itree.last & TARGET_PAGE_MASK; + return next == (tb->itree.start & TARGET_PAGE_MASK) ? -1 : next; +#else + return tb->page_addr[1]; +#endif +} + +static inline void tb_set_page_addr0(TranslationBlock *tb, + tb_page_addr_t addr) +{ +#ifdef CONFIG_USER_ONLY + tb->itree.start = addr; + /* + * To begin, we record an interval of one byte. When the translation + * loop encounters a second page, the interval will be extended to + * include the first byte of the second page, which is sufficient to + * allow tb_page_addr1() above to work properly. The final corrected + * interval will be set by tb_page_add() from tb->size before the + * node is added to the interval tree. + */ + tb->itree.last = addr; +#else + tb->page_addr[0] = addr; +#endif +} + +static inline void tb_set_page_addr1(TranslationBlock *tb, + tb_page_addr_t addr) +{ +#ifdef CONFIG_USER_ONLY + /* Extend the interval to the first byte of the second page. See above. */ + tb->itree.last = addr; +#else + tb->page_addr[1] = addr; +#endif } /* current cflags for hashing/comparison */ uint32_t curr_cflags(CPUState *cpu); /* TranslationBlock invalidate API */ -#if defined(CONFIG_USER_ONLY) -void tb_invalidate_phys_addr(target_ulong addr); -void tb_invalidate_phys_range(target_ulong start, target_ulong end); -#else -void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs); -#endif -void tb_flush(CPUState *cpu); void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); -TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, - target_ulong cs_base, uint32_t flags, - uint32_t cflags); +void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last); void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr); /* GETPC is the true target of the return instruction that we'll execute. */ @@ -575,14 +534,6 @@ extern __thread uintptr_t tci_tb_ptr; smaller than 4 bytes, so we don't worry about special-casing this. */ #define GETPC_ADJ 2 -#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_DEBUG_TCG) -void assert_no_pages_locked(void); -#else -static inline void assert_no_pages_locked(void) -{ -} -#endif - #if !defined(CONFIG_USER_ONLY) /** @@ -598,44 +549,54 @@ struct MemoryRegionSection *iotlb_to_section(CPUState *cpu, hwaddr index, MemTxAttrs attrs); #endif -#if defined(CONFIG_USER_ONLY) -void mmap_lock(void); -void mmap_unlock(void); -bool have_mmap_lock(void); - /** - * get_page_addr_code() - user-mode version + * get_page_addr_code_hostp() * @env: CPUArchState * @addr: guest virtual address of guest code * - * Returns @addr. + * See get_page_addr_code() (full-system version) for documentation on the + * return value. + * + * Sets *@hostp (when @hostp is non-NULL) as follows. + * If the return value is -1, sets *@hostp to NULL. Otherwise, sets *@hostp + * to the host address where @addr's content is kept. + * + * Note: this function can trigger an exception. + */ +tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, + void **hostp); + +/** + * get_page_addr_code() + * @env: CPUArchState + * @addr: guest virtual address of guest code + * + * If we cannot translate and execute from the entire RAM page, or if + * the region is not backed by RAM, returns -1. Otherwise, returns the + * ram_addr_t corresponding to the guest code at @addr. + * + * Note: this function can trigger an exception. */ static inline tb_page_addr_t get_page_addr_code(CPUArchState *env, - target_ulong addr) + vaddr addr) { - return addr; + return get_page_addr_code_hostp(env, addr, NULL); } -/** - * get_page_addr_code_hostp() - user-mode version - * @env: CPUArchState - * @addr: guest virtual address of guest code - * - * Returns @addr. - * - * If @hostp is non-NULL, sets *@hostp to the host address where @addr's content - * is kept. - */ -static inline tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, - target_ulong addr, - void **hostp) +#if defined(CONFIG_USER_ONLY) +void TSA_NO_TSA mmap_lock(void); +void TSA_NO_TSA mmap_unlock(void); +bool have_mmap_lock(void); + +static inline void mmap_unlock_guard(void *unused) { - if (hostp) { - *hostp = g2h_untagged(addr); - } - return addr; + mmap_unlock(); } +#define WITH_MMAP_LOCK_GUARD() \ + for (int _mmap_lock_iter __attribute__((cleanup(mmap_unlock_guard))) \ + = (mmap_lock(), 0); _mmap_lock_iter == 0; _mmap_lock_iter = 1) + /** * adjust_signal_pc: * @pc: raw pc from the host signal ucontext_t. @@ -690,39 +651,11 @@ G_NORETURN void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, #else static inline void mmap_lock(void) {} static inline void mmap_unlock(void) {} - -/** - * get_page_addr_code() - full-system version - * @env: CPUArchState - * @addr: guest virtual address of guest code - * - * If we cannot translate and execute from the entire RAM page, or if - * the region is not backed by RAM, returns -1. Otherwise, returns the - * ram_addr_t corresponding to the guest code at @addr. - * - * Note: this function can trigger an exception. - */ -tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr); - -/** - * get_page_addr_code_hostp() - full-system version - * @env: CPUArchState - * @addr: guest virtual address of guest code - * - * See get_page_addr_code() (full-system version) for documentation on the - * return value. - * - * Sets *@hostp (when @hostp is non-NULL) as follows. - * If the return value is -1, sets *@hostp to NULL. Otherwise, sets *@hostp - * to the host address where @addr's content is kept. - * - * Note: this function can trigger an exception. - */ -tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong addr, - void **hostp); +#define WITH_MMAP_LOCK_GUARD() void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length); -void tlb_set_dirty(CPUState *cpu, target_ulong vaddr); +void tlb_set_dirty(CPUState *cpu, vaddr addr); +void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length); MemoryRegionSection * address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index c35d7334b4..eb14b91139 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -10,151 +10,44 @@ #define GDB_WATCHPOINT_READ 3 #define GDB_WATCHPOINT_ACCESS 4 -#ifdef NEED_CPU_H -#include "cpu.h" +typedef struct GDBFeature { + const char *xmlname; + const char *xml; + const char *name; + const char * const *regs; + int num_regs; +} GDBFeature; -typedef void (*gdb_syscall_complete_cb)(CPUState *cpu, - target_ulong ret, target_ulong err); +typedef struct GDBFeatureBuilder { + GDBFeature *feature; + GPtrArray *xml; + GPtrArray *regs; + int base_reg; +} GDBFeatureBuilder; -/** - * gdb_do_syscall: - * @cb: function to call when the system call has completed - * @fmt: gdb syscall format string - * ...: list of arguments to interpolate into @fmt - * - * Send a GDB syscall request. This function will return immediately; - * the callback function will be called later when the remote system - * call has completed. - * - * @fmt should be in the 'call-id,parameter,parameter...' format documented - * for the F request packet in the GDB remote protocol. A limited set of - * printf-style format specifiers is supported: - * %x - target_ulong argument printed in hex - * %lx - 64-bit argument printed in hex - * %s - string pointer (target_ulong) and length (int) pair - */ -void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...); -/** - * gdb_do_syscallv: - * @cb: function to call when the system call has completed - * @fmt: gdb syscall format string - * @va: arguments to interpolate into @fmt - * - * As gdb_do_syscall, but taking a va_list rather than a variable - * argument list. - */ -void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va); -int use_gdb_syscalls(void); -#ifdef CONFIG_USER_ONLY -/** - * gdb_handlesig: yield control to gdb - * @cpu: CPU - * @sig: if non-zero, the signal number which caused us to stop - * - * This function yields control to gdb, when a user-mode-only target - * needs to stop execution. If @sig is non-zero, then we will send a - * stop packet to tell gdb that we have stopped because of this signal. - * - * This function will block (handling protocol requests from gdb) - * until gdb tells us to continue target execution. When it does - * return, the return value is a signal to deliver to the target, - * or 0 if no signal should be delivered, ie the signal that caused - * us to stop should be ignored. - */ -int gdb_handlesig(CPUState *, int); -void gdb_signalled(CPUArchState *, int); -void gdbserver_fork(CPUState *); -#endif /* Get or set a register. Returns the size of the register. */ -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); +typedef int (*gdb_get_reg_cb)(CPUState *cpu, GByteArray *buf, int reg); +typedef int (*gdb_set_reg_cb)(CPUState *cpu, uint8_t *buf, int reg); + +/** + * gdb_init_cpu(): Initialize the CPU for gdbstub. + * @cpu: The CPU to be initialized. + */ +void gdb_init_cpu(CPUState *cpu); + +/** + * gdb_register_coprocessor() - register a supplemental set of registers + * @cpu - the CPU associated with registers + * @get_reg - get function (gdb reading) + * @set_reg - set function (gdb modifying) + * @num_regs - number of registers in set + * @xml - xml name of set + * @gpos - non-zero to append to "general" register set at @gpos + */ void gdb_register_coprocessor(CPUState *cpu, 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. As - * the gdbstub may be batching up several register values we always - * append to the array. - */ - -static inline int gdb_get_reg8(GByteArray *buf, uint8_t val) -{ - g_byte_array_append(buf, &val, 1); - return 1; -} - -static inline int gdb_get_reg16(GByteArray *buf, uint16_t 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(GByteArray *buf, uint32_t 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(GByteArray *buf, uint64_t 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; -#if TARGET_BIG_ENDIAN - 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_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) -#else -#define gdb_get_regl(buf, val) gdb_get_reg32(buf, val) -#define ldtul_p(addr) ldl_p(addr) -#endif - -#endif /* NEED_CPU_H */ + const GDBFeature *feature, int g_pos); /** * gdbserver_start: start the gdb server @@ -167,26 +60,88 @@ static inline uint8_t * gdb_get_reg_ptr(GByteArray *buf, int len) int gdbserver_start(const char *port_or_device); /** - * gdb_exit: exit gdb session, reporting inferior status - * @code: exit code reported - * - * This closes the session and sends a final packet to GDB reporting - * the exit status of the program. It also cleans up any connections - * detritus before returning. + * gdb_feature_builder_init() - Initialize GDBFeatureBuilder. + * @builder: The builder to be initialized. + * @feature: The feature to be filled. + * @name: The name of the feature. + * @xmlname: The name of the XML. + * @base_reg: The base number of the register ID. */ -void gdb_exit(int code); +void gdb_feature_builder_init(GDBFeatureBuilder *builder, GDBFeature *feature, + const char *name, const char *xmlname, + int base_reg); + +/** + * gdb_feature_builder_append_tag() - Append a tag. + * @builder: The builder. + * @format: The format of the tag. + * @...: The values to be formatted. + */ +void G_GNUC_PRINTF(2, 3) +gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder, + const char *format, ...); + +/** + * gdb_feature_builder_append_reg() - Append a register. + * @builder: The builder. + * @name: The register's name; it must be unique within a CPU. + * @bitsize: The register's size, in bits. + * @regnum: The offset of the register's number in the feature. + * @type: The type of the register. + * @group: The register group to which this register belongs; it can be NULL. + */ +void gdb_feature_builder_append_reg(const GDBFeatureBuilder *builder, + const char *name, + int bitsize, + int regnum, + const char *type, + const char *group); + +/** + * gdb_feature_builder_end() - End building GDBFeature. + * @builder: The builder. + */ +void gdb_feature_builder_end(const GDBFeatureBuilder *builder); + +/** + * gdb_find_static_feature() - Find a static feature. + * @xmlname: The name of the XML. + * + * Return: The static feature. + */ +const GDBFeature *gdb_find_static_feature(const char *xmlname); + +/** + * gdb_read_register() - Read a register associated with a CPU. + * @cpu: The CPU associated with the register. + * @buf: The buffer that the read register will be appended to. + * @reg: The register's number returned by gdb_find_feature_register(). + * + * Return: The number of read bytes. + */ +int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); + +/** + * typedef GDBRegDesc - a register description from gdbstub + */ +typedef struct { + int gdb_reg; + const char *name; + const char *feature_name; +} GDBRegDesc; + +/** + * gdb_get_register_list() - Return list of all registers for CPU + * @cpu: The CPU being searched + * + * Returns a GArray of GDBRegDesc, caller frees array but not the + * const strings. + */ +GArray *gdb_get_register_list(CPUState *cpu); void gdb_set_stop_cpu(CPUState *cpu); -/** - * gdb_has_xml: - * This is an ugly hack to cope with both new and old gdb. - * If gdb sends qXfer:features:read then assume we're talking to a newish - * gdb that understands target descriptions. - */ -extern bool gdb_has_xml; - -/* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */ -extern const char *const xml_builtin[][2]; +/* in gdbstub-xml.c, generated by scripts/feature_to_c.py */ +extern const GDBFeature gdb_static_features[]; #endif diff --git a/include/exec/gen-icount.h b/include/exec/gen-icount.h deleted file mode 100644 index c57204ddad..0000000000 --- a/include/exec/gen-icount.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef GEN_ICOUNT_H -#define GEN_ICOUNT_H - -#include "exec/exec-all.h" -#include "qemu/timer.h" - -/* Helpers for instruction counting code generation. */ - -static TCGOp *icount_start_insn; - -static inline void gen_io_start(void) -{ - TCGv_i32 tmp = tcg_const_i32(1); - tcg_gen_st_i32(tmp, cpu_env, - offsetof(ArchCPU, parent_obj.can_do_io) - - offsetof(ArchCPU, env)); - tcg_temp_free_i32(tmp); -} - -static inline void gen_tb_start(const TranslationBlock *tb) -{ - TCGv_i32 count; - - if (tb_cflags(tb) & CF_USE_ICOUNT) { - count = tcg_temp_local_new_i32(); - } else { - count = tcg_temp_new_i32(); - } - - tcg_gen_ld_i32(count, cpu_env, - offsetof(ArchCPU, neg.icount_decr.u32) - - offsetof(ArchCPU, env)); - - if (tb_cflags(tb) & CF_USE_ICOUNT) { - /* - * We emit a sub with a dummy immediate argument. Keep the insn index - * of the sub so that we later (when we know the actual insn count) - * can update the argument with the actual insn count. - */ - tcg_gen_sub_i32(count, count, tcg_constant_i32(0)); - icount_start_insn = tcg_last_op(); - } - - /* - * Emit the check against icount_decr.u32 to see if we should exit - * unless we suppress the check with CF_NOIRQ. If we are using - * icount and have suppressed interruption the higher level code - * should have ensured we don't run more instructions than the - * budget. - */ - if (tb_cflags(tb) & CF_NOIRQ) { - tcg_ctx->exitreq_label = NULL; - } else { - tcg_ctx->exitreq_label = gen_new_label(); - tcg_gen_brcondi_i32(TCG_COND_LT, count, 0, tcg_ctx->exitreq_label); - } - - if (tb_cflags(tb) & CF_USE_ICOUNT) { - tcg_gen_st16_i32(count, cpu_env, - offsetof(ArchCPU, neg.icount_decr.u16.low) - - offsetof(ArchCPU, env)); - /* - * cpu->can_do_io is cleared automatically here at the beginning of - * each translation block. The cost is minimal and only paid for - * -icount, plus it would be very easy to forget doing it in the - * translator. Doing it here means we don't need a gen_io_end() to - * go with gen_io_start(). - */ - tcg_gen_st_i32(tcg_constant_i32(0), cpu_env, - offsetof(ArchCPU, parent_obj.can_do_io) - - offsetof(ArchCPU, env)); - } - - tcg_temp_free_i32(count); -} - -static inline void gen_tb_end(const TranslationBlock *tb, int num_insns) -{ - if (tb_cflags(tb) & CF_USE_ICOUNT) { - /* - * Update the num_insn immediate parameter now that we know - * the actual insn count. - */ - tcg_set_insn_param(icount_start_insn, 2, - tcgv_i32_arg(tcg_constant_i32(num_insns))); - } - - if (tcg_ctx->exitreq_label) { - gen_set_label(tcg_ctx->exitreq_label); - tcg_gen_exit_tb(tb, TB_EXIT_REQUESTED); - } -} - -#endif diff --git a/include/exec/helper-gen-common.h b/include/exec/helper-gen-common.h new file mode 100644 index 0000000000..5d6d78a625 --- /dev/null +++ b/include/exec/helper-gen-common.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper file for declaring TCG helper functions. + * This one expands generation functions for tcg opcodes. + */ + +#ifndef HELPER_GEN_COMMON_H +#define HELPER_GEN_COMMON_H + +#define HELPER_H "accel/tcg/tcg-runtime.h" +#include "exec/helper-gen.h.inc" +#undef HELPER_H + +#define HELPER_H "accel/tcg/plugin-helpers.h" +#include "exec/helper-gen.h.inc" +#undef HELPER_H + +#endif /* HELPER_GEN_COMMON_H */ diff --git a/include/exec/helper-gen.h b/include/exec/helper-gen.h index 7b6ca975ef..f7ec155699 100644 --- a/include/exec/helper-gen.h +++ b/include/exec/helper-gen.h @@ -1,95 +1,16 @@ -/* Helper file for declaring TCG helper functions. - This one expands generation functions for tcg opcodes. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper file for declaring TCG helper functions. + * This one expands generation functions for tcg opcodes. + */ #ifndef HELPER_GEN_H #define HELPER_GEN_H -#include "exec/helper-head.h" +#include "exec/helper-gen-common.h" -#define DEF_HELPER_FLAGS_0(name, flags, ret) \ -static inline void glue(gen_helper_, name)(dh_retvar_decl0(ret)) \ -{ \ - tcg_gen_callN(HELPER(name), dh_retvar(ret), 0, NULL); \ -} - -#define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \ -static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ - dh_arg_decl(t1, 1)) \ -{ \ - TCGTemp *args[1] = { dh_arg(t1, 1) }; \ - tcg_gen_callN(HELPER(name), dh_retvar(ret), 1, args); \ -} - -#define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ -static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ - dh_arg_decl(t1, 1), dh_arg_decl(t2, 2)) \ -{ \ - TCGTemp *args[2] = { dh_arg(t1, 1), dh_arg(t2, 2) }; \ - tcg_gen_callN(HELPER(name), dh_retvar(ret), 2, args); \ -} - -#define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3) \ -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)) \ -{ \ - TCGTemp *args[3] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3) }; \ - tcg_gen_callN(HELPER(name), dh_retvar(ret), 3, args); \ -} - -#define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \ -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)) \ -{ \ - TCGTemp *args[4] = { dh_arg(t1, 1), dh_arg(t2, 2), \ - dh_arg(t3, 3), dh_arg(t4, 4) }; \ - tcg_gen_callN(HELPER(name), dh_retvar(ret), 4, args); \ -} - -#define DEF_HELPER_FLAGS_5(name, flags, ret, t1, t2, t3, t4, t5) \ -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)) \ -{ \ - TCGTemp *args[5] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ - dh_arg(t4, 4), dh_arg(t5, 5) }; \ - tcg_gen_callN(HELPER(name), dh_retvar(ret), 5, args); \ -} - -#define DEF_HELPER_FLAGS_6(name, flags, ret, t1, t2, t3, t4, t5, t6) \ -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)) \ -{ \ - TCGTemp *args[6] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ - dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6) }; \ - 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 "accel/tcg/tcg-runtime.h" -#include "accel/tcg/plugin-helpers.h" - -#undef DEF_HELPER_FLAGS_0 -#undef DEF_HELPER_FLAGS_1 -#undef DEF_HELPER_FLAGS_2 -#undef DEF_HELPER_FLAGS_3 -#undef DEF_HELPER_FLAGS_4 -#undef DEF_HELPER_FLAGS_5 -#undef DEF_HELPER_FLAGS_6 -#undef DEF_HELPER_FLAGS_7 -#undef GEN_HELPER +#define HELPER_H "helper.h" +#include "exec/helper-gen.h.inc" +#undef HELPER_H #endif /* HELPER_GEN_H */ diff --git a/include/exec/helper-gen.h.inc b/include/exec/helper-gen.h.inc new file mode 100644 index 0000000000..c009641517 --- /dev/null +++ b/include/exec/helper-gen.h.inc @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper file for declaring TCG helper functions. + * This one expands generation functions for tcg opcodes. + * Define HELPER_H for the header file to be expanded, + * and static inline to change from global file scope. + */ + +#include "tcg/tcg.h" +#include "tcg/helper-info.h" +#include "exec/helper-head.h" + +#define DEF_HELPER_FLAGS_0(name, flags, ret) \ +extern TCGHelperInfo glue(helper_info_, name); \ +static inline void glue(gen_helper_, name)(dh_retvar_decl0(ret)) \ +{ \ + tcg_gen_call0(&glue(helper_info_, name), dh_retvar(ret)); \ +} + +#define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \ +extern TCGHelperInfo glue(helper_info_, name); \ +static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ + dh_arg_decl(t1, 1)) \ +{ \ + tcg_gen_call1(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1)); \ +} + +#define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ +extern TCGHelperInfo glue(helper_info_, name); \ +static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ + dh_arg_decl(t1, 1), dh_arg_decl(t2, 2)) \ +{ \ + tcg_gen_call2(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2)); \ +} + +#define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3) \ +extern TCGHelperInfo glue(helper_info_, name); \ +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)) \ +{ \ + tcg_gen_call3(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3)); \ +} + +#define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \ +extern TCGHelperInfo glue(helper_info_, name); \ +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)) \ +{ \ + tcg_gen_call4(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2), \ + dh_arg(t3, 3), dh_arg(t4, 4)); \ +} + +#define DEF_HELPER_FLAGS_5(name, flags, ret, t1, t2, t3, t4, t5) \ +extern TCGHelperInfo glue(helper_info_, name); \ +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)) \ +{ \ + tcg_gen_call5(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ + dh_arg(t4, 4), dh_arg(t5, 5)); \ +} + +#define DEF_HELPER_FLAGS_6(name, flags, ret, t1, t2, t3, t4, t5, t6) \ +extern TCGHelperInfo glue(helper_info_, name); \ +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)) \ +{ \ + tcg_gen_call6(&glue(helper_info_, name), dh_retvar(ret), \ + dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ + dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6)); \ +} + +#define DEF_HELPER_FLAGS_7(name, flags, ret, t1, t2, t3, t4, t5, t6, t7)\ +extern TCGHelperInfo glue(helper_info_, name); \ +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)) \ +{ \ + tcg_gen_call7(&glue(helper_info_, name), dh_retvar(ret), \ + 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)); \ +} + +#include HELPER_H + +#undef DEF_HELPER_FLAGS_0 +#undef DEF_HELPER_FLAGS_1 +#undef DEF_HELPER_FLAGS_2 +#undef DEF_HELPER_FLAGS_3 +#undef DEF_HELPER_FLAGS_4 +#undef DEF_HELPER_FLAGS_5 +#undef DEF_HELPER_FLAGS_6 +#undef DEF_HELPER_FLAGS_7 diff --git a/include/exec/helper-head.h b/include/exec/helper-head.h index e242fed46e..28ceab0a46 100644 --- a/include/exec/helper-head.h +++ b/include/exec/helper-head.h @@ -1,23 +1,13 @@ -/* Helper file for declaring TCG helper functions. - Used by other helper files. - - Targets should use DEF_HELPER_N and DEF_HELPER_FLAGS_N to declare helper - functions. Names should be specified without the helper_ prefix, and - the return and argument types specified. 3 basic types are understood - (i32, i64 and ptr). Additional aliases are provided for convenience and - to match the types used by the C helper implementation. - - The target helper.h should be included in all files that use/define - helper functions. THis will ensure that function prototypes are - consistent. In addition it should be included an extra two times for - helper.c, defining: - GEN_HELPER 1 to produce op generation functions (gen_helper_*) - GEN_HELPER 2 to do runtime registration helper functions. +/* + * Helper file for declaring TCG helper functions. + * Used by other helper files. */ #ifndef EXEC_HELPER_HEAD_H #define EXEC_HELPER_HEAD_H +#include "fpu/softfloat-types.h" + #define HELPER(name) glue(helper_, name) /* Some types that make sense in C, but not for TCG. */ @@ -26,11 +16,13 @@ #define dh_alias_int i32 #define dh_alias_i64 i64 #define dh_alias_s64 i64 +#define dh_alias_i128 i128 #define dh_alias_f16 i32 #define dh_alias_f32 i32 #define dh_alias_f64 i64 #define dh_alias_ptr ptr #define dh_alias_cptr ptr +#define dh_alias_env ptr #define dh_alias_void void #define dh_alias_noreturn noreturn #define dh_alias(t) glue(dh_alias_, t) @@ -40,11 +32,13 @@ #define dh_ctype_int int #define dh_ctype_i64 uint64_t #define dh_ctype_s64 int64_t +#define dh_ctype_i128 Int128 #define dh_ctype_f16 uint32_t #define dh_ctype_f32 float32 #define dh_ctype_f64 float64 #define dh_ctype_ptr void * #define dh_ctype_cptr const void * +#define dh_ctype_env CPUArchState * #define dh_ctype_void void #define dh_ctype_noreturn G_NORETURN void #define dh_ctype(t) dh_ctype_##t @@ -60,9 +54,6 @@ # endif # endif # define dh_ctype_tl target_ulong -# define dh_alias_env ptr -# define dh_ctype_env CPUArchState * -# define dh_typecode_env dh_typecode_ptr #endif /* We can't use glue() here because it falls foul of C preprocessor @@ -71,6 +62,7 @@ #define dh_retvar_decl0_noreturn void #define dh_retvar_decl0_i32 TCGv_i32 retval #define dh_retvar_decl0_i64 TCGv_i64 retval +#define dh_retval_decl0_i128 TCGv_i128 retval #define dh_retvar_decl0_ptr TCGv_ptr retval #define dh_retvar_decl0(t) glue(dh_retvar_decl0_, dh_alias(t)) @@ -78,6 +70,7 @@ #define dh_retvar_decl_noreturn #define dh_retvar_decl_i32 TCGv_i32 retval, #define dh_retvar_decl_i64 TCGv_i64 retval, +#define dh_retvar_decl_i128 TCGv_i128 retval, #define dh_retvar_decl_ptr TCGv_ptr retval, #define dh_retvar_decl(t) glue(dh_retvar_decl_, dh_alias(t)) @@ -85,6 +78,7 @@ #define dh_retvar_noreturn NULL #define dh_retvar_i32 tcgv_i32_temp(retval) #define dh_retvar_i64 tcgv_i64_temp(retval) +#define dh_retvar_i128 tcgv_i128_temp(retval) #define dh_retvar_ptr tcgv_ptr_temp(retval) #define dh_retvar(t) glue(dh_retvar_, dh_alias(t)) @@ -95,15 +89,18 @@ #define dh_typecode_i64 4 #define dh_typecode_s64 5 #define dh_typecode_ptr 6 +#define dh_typecode_i128 7 #define dh_typecode_int dh_typecode_s32 #define dh_typecode_f16 dh_typecode_i32 #define dh_typecode_f32 dh_typecode_i32 #define dh_typecode_f64 dh_typecode_i64 #define dh_typecode_cptr dh_typecode_ptr +#define dh_typecode_env dh_typecode_ptr #define dh_typecode(t) dh_typecode_##t #define dh_callflag_i32 0 #define dh_callflag_i64 0 +#define dh_callflag_i128 0 #define dh_callflag_ptr 0 #define dh_callflag_void 0 #define dh_callflag_noreturn TCG_CALL_NO_RETURN @@ -133,6 +130,6 @@ #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. */ +/* MAX_CALL_IARGS must be set to n if last entry is DEF_HELPER_FLAGS_n. */ #endif /* EXEC_HELPER_HEAD_H */ diff --git a/include/exec/helper-info.c.inc b/include/exec/helper-info.c.inc new file mode 100644 index 0000000000..530d2e6d35 --- /dev/null +++ b/include/exec/helper-info.c.inc @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper file for declaring TCG helper functions. + * This one expands info structures for tcg helpers. + * Define HELPER_H for the header file to be expanded. + */ + +#include "tcg/tcg.h" +#include "tcg/helper-info.h" +#include "exec/helper-head.h" + +/* + * Need one more level of indirection before stringification + * to get all the macros expanded first. + */ +#define str(s) #s + +#define DEF_HELPER_FLAGS_0(NAME, FLAGS, RET) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) \ + }; + +#define DEF_HELPER_FLAGS_1(NAME, FLAGS, RET, T1) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) | dh_typemask(T1, 1) \ + }; + +#define DEF_HELPER_FLAGS_2(NAME, FLAGS, RET, T1, T2) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) | dh_typemask(T1, 1) \ + | dh_typemask(T2, 2) \ + }; + +#define DEF_HELPER_FLAGS_3(NAME, FLAGS, RET, T1, T2, T3) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) | dh_typemask(T1, 1) \ + | dh_typemask(T2, 2) | dh_typemask(T3, 3) \ + }; + +#define DEF_HELPER_FLAGS_4(NAME, FLAGS, RET, T1, T2, T3, T4) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) | dh_typemask(T1, 1) \ + | dh_typemask(T2, 2) | dh_typemask(T3, 3) \ + | dh_typemask(T4, 4) \ + }; + +#define DEF_HELPER_FLAGS_5(NAME, FLAGS, RET, T1, T2, T3, T4, T5) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) | dh_typemask(T1, 1) \ + | dh_typemask(T2, 2) | dh_typemask(T3, 3) \ + | dh_typemask(T4, 4) | dh_typemask(T5, 5) \ + }; + +#define DEF_HELPER_FLAGS_6(NAME, FLAGS, RET, T1, T2, T3, T4, T5, T6) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) | dh_typemask(T1, 1) \ + | dh_typemask(T2, 2) | dh_typemask(T3, 3) \ + | dh_typemask(T4, 4) | dh_typemask(T5, 5) \ + | dh_typemask(T6, 6) \ + }; + +#define DEF_HELPER_FLAGS_7(NAME, FLAGS, RET, T1, T2, T3, T4, T5, T6, T7) \ + TCGHelperInfo glue(helper_info_, NAME) = { \ + .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(RET), \ + .typemask = dh_typemask(RET, 0) | dh_typemask(T1, 1) \ + | dh_typemask(T2, 2) | dh_typemask(T3, 3) \ + | dh_typemask(T4, 4) | dh_typemask(T5, 5) \ + | dh_typemask(T6, 6) | dh_typemask(T7, 7) \ + }; + +#include HELPER_H + +#undef str +#undef DEF_HELPER_FLAGS_0 +#undef DEF_HELPER_FLAGS_1 +#undef DEF_HELPER_FLAGS_2 +#undef DEF_HELPER_FLAGS_3 +#undef DEF_HELPER_FLAGS_4 +#undef DEF_HELPER_FLAGS_5 +#undef DEF_HELPER_FLAGS_6 +#undef DEF_HELPER_FLAGS_7 diff --git a/include/exec/helper-proto-common.h b/include/exec/helper-proto-common.h new file mode 100644 index 0000000000..8b67170a22 --- /dev/null +++ b/include/exec/helper-proto-common.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper file for declaring TCG helper functions. + * This one expands prototypes for the helper functions. + */ + +#ifndef HELPER_PROTO_COMMON_H +#define HELPER_PROTO_COMMON_H + +#include "qemu/atomic128.h" /* for HAVE_CMPXCHG128 */ + +#define HELPER_H "accel/tcg/tcg-runtime.h" +#include "exec/helper-proto.h.inc" +#undef HELPER_H + +#define HELPER_H "accel/tcg/plugin-helpers.h" +#include "exec/helper-proto.h.inc" +#undef HELPER_H + +#endif /* HELPER_PROTO_COMMON_H */ diff --git a/include/exec/helper-proto.h b/include/exec/helper-proto.h index c4b1bda632..6935cb4f16 100644 --- a/include/exec/helper-proto.h +++ b/include/exec/helper-proto.h @@ -1,55 +1,16 @@ -/* Helper file for declaring TCG helper functions. - This one expands prototypes for the helper functions. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper file for declaring TCG helper functions. + * This one expands prototypes for the helper functions. + */ #ifndef HELPER_PROTO_H #define HELPER_PROTO_H -#include "exec/helper-head.h" +#include "exec/helper-proto-common.h" -#define DEF_HELPER_FLAGS_0(name, flags, ret) \ -dh_ctype(ret) HELPER(name) (void); - -#define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1)); - -#define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2)); - -#define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3)); - -#define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ - dh_ctype(t4)); - -#define DEF_HELPER_FLAGS_5(name, flags, ret, t1, t2, t3, t4, t5) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ - dh_ctype(t4), dh_ctype(t5)); - -#define DEF_HELPER_FLAGS_6(name, flags, ret, t1, t2, t3, t4, t5, t6) \ -dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ - dh_ctype(t4), dh_ctype(t5), dh_ctype(t6)); - -#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)); - -#define IN_HELPER_PROTO - -#include "helper.h" -#include "accel/tcg/tcg-runtime.h" -#include "accel/tcg/plugin-helpers.h" - -#undef IN_HELPER_PROTO - -#undef DEF_HELPER_FLAGS_0 -#undef DEF_HELPER_FLAGS_1 -#undef DEF_HELPER_FLAGS_2 -#undef DEF_HELPER_FLAGS_3 -#undef DEF_HELPER_FLAGS_4 -#undef DEF_HELPER_FLAGS_5 -#undef DEF_HELPER_FLAGS_6 -#undef DEF_HELPER_FLAGS_7 +#define HELPER_H "helper.h" +#include "exec/helper-proto.h.inc" +#undef HELPER_H #endif /* HELPER_PROTO_H */ diff --git a/include/exec/helper-proto.h.inc b/include/exec/helper-proto.h.inc new file mode 100644 index 0000000000..c3aa666929 --- /dev/null +++ b/include/exec/helper-proto.h.inc @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper file for declaring TCG helper functions. + * This one expands prototypes for the helper functions. + * Define HELPER_H for the header file to be expanded. + */ + +#include "exec/helper-head.h" + +/* + * Work around an issue with --enable-lto, in which GCC's ipa-split pass + * decides to split out the noreturn code paths that raise an exception, + * taking the __builtin_return_address() along into the new function, + * where it no longer computes a value that returns to TCG generated code. + * Despite the name, the noinline attribute affects splitter, so this + * prevents the optimization in question. Given that helpers should not + * otherwise be called directly, this should not have any other visible effect. + * + * See https://gitlab.com/qemu-project/qemu/-/issues/1454 + */ +#define DEF_HELPER_ATTR __attribute__((noinline)) + +#define DEF_HELPER_FLAGS_0(name, flags, ret) \ +dh_ctype(ret) HELPER(name) (void) DEF_HELPER_ATTR; + +#define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1)) DEF_HELPER_ATTR; + +#define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2)) DEF_HELPER_ATTR; + +#define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), \ + dh_ctype(t3)) DEF_HELPER_ATTR; + +#define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ + dh_ctype(t4)) DEF_HELPER_ATTR; + +#define DEF_HELPER_FLAGS_5(name, flags, ret, t1, t2, t3, t4, t5) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ + dh_ctype(t4), dh_ctype(t5)) DEF_HELPER_ATTR; + +#define DEF_HELPER_FLAGS_6(name, flags, ret, t1, t2, t3, t4, t5, t6) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ + dh_ctype(t4), dh_ctype(t5), \ + dh_ctype(t6)) DEF_HELPER_ATTR; + +#define DEF_HELPER_FLAGS_7(name, flags, ret, t1, t2, t3, t4, t5, t6, t7) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ + dh_ctype(t4), dh_ctype(t5), dh_ctype(t6), \ + dh_ctype(t7)) DEF_HELPER_ATTR; + +#define IN_HELPER_PROTO + +#include HELPER_H + +#undef IN_HELPER_PROTO + +#undef DEF_HELPER_FLAGS_0 +#undef DEF_HELPER_FLAGS_1 +#undef DEF_HELPER_FLAGS_2 +#undef DEF_HELPER_FLAGS_3 +#undef DEF_HELPER_FLAGS_4 +#undef DEF_HELPER_FLAGS_5 +#undef DEF_HELPER_FLAGS_6 +#undef DEF_HELPER_FLAGS_7 +#undef DEF_HELPER_ATTR diff --git a/include/exec/helper-tcg.h b/include/exec/helper-tcg.h deleted file mode 100644 index 3933258f1a..0000000000 --- a/include/exec/helper-tcg.h +++ /dev/null @@ -1,75 +0,0 @@ -/* Helper file for declaring TCG helper functions. - This one defines data structures private to tcg.c. */ - -#ifndef HELPER_TCG_H -#define HELPER_TCG_H - -#include "exec/helper-head.h" - -/* Need one more level of indirection before stringification - to get all the macros expanded first. */ -#define str(s) #s - -#define DEF_HELPER_FLAGS_0(NAME, FLAGS, ret) \ - { .func = HELPER(NAME), .name = str(NAME), \ - .flags = FLAGS | dh_callflag(ret), \ - .typemask = dh_typemask(ret, 0) }, - -#define DEF_HELPER_FLAGS_1(NAME, FLAGS, ret, t1) \ - { .func = HELPER(NAME), .name = str(NAME), \ - .flags = FLAGS | dh_callflag(ret), \ - .typemask = dh_typemask(ret, 0) | dh_typemask(t1, 1) }, - -#define DEF_HELPER_FLAGS_2(NAME, FLAGS, ret, t1, t2) \ - { .func = HELPER(NAME), .name = str(NAME), \ - .flags = FLAGS | dh_callflag(ret), \ - .typemask = dh_typemask(ret, 0) | dh_typemask(t1, 1) \ - | dh_typemask(t2, 2) }, - -#define DEF_HELPER_FLAGS_3(NAME, FLAGS, ret, t1, t2, t3) \ - { .func = HELPER(NAME), .name = str(NAME), \ - .flags = FLAGS | dh_callflag(ret), \ - .typemask = dh_typemask(ret, 0) | dh_typemask(t1, 1) \ - | dh_typemask(t2, 2) | dh_typemask(t3, 3) }, - -#define DEF_HELPER_FLAGS_4(NAME, FLAGS, ret, t1, t2, t3, t4) \ - { .func = HELPER(NAME), .name = str(NAME), \ - .flags = FLAGS | dh_callflag(ret), \ - .typemask = dh_typemask(ret, 0) | dh_typemask(t1, 1) \ - | dh_typemask(t2, 2) | dh_typemask(t3, 3) | dh_typemask(t4, 4) }, - -#define DEF_HELPER_FLAGS_5(NAME, FLAGS, ret, t1, t2, t3, t4, t5) \ - { .func = HELPER(NAME), .name = str(NAME), \ - .flags = FLAGS | dh_callflag(ret), \ - .typemask = dh_typemask(ret, 0) | dh_typemask(t1, 1) \ - | dh_typemask(t2, 2) | dh_typemask(t3, 3) | dh_typemask(t4, 4) \ - | dh_typemask(t5, 5) }, - -#define DEF_HELPER_FLAGS_6(NAME, FLAGS, ret, t1, t2, t3, t4, t5, t6) \ - { .func = HELPER(NAME), .name = str(NAME), \ - .flags = FLAGS | dh_callflag(ret), \ - .typemask = dh_typemask(ret, 0) | dh_typemask(t1, 1) \ - | dh_typemask(t2, 2) | dh_typemask(t3, 3) | dh_typemask(t4, 4) \ - | dh_typemask(t5, 5) | dh_typemask(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, \ - .typemask = dh_typemask(ret, 0) | dh_typemask(t1, 1) \ - | dh_typemask(t2, 2) | dh_typemask(t3, 3) | dh_typemask(t4, 4) \ - | dh_typemask(t5, 5) | dh_typemask(t6, 6) | dh_typemask(t7, 7) }, - -#include "helper.h" -#include "accel/tcg/tcg-runtime.h" -#include "accel/tcg/plugin-helpers.h" - -#undef str -#undef DEF_HELPER_FLAGS_0 -#undef DEF_HELPER_FLAGS_1 -#undef DEF_HELPER_FLAGS_2 -#undef DEF_HELPER_FLAGS_3 -#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/hwaddr.h b/include/exec/hwaddr.h index 8f16d179a8..50fbb2d96c 100644 --- a/include/exec/hwaddr.h +++ b/include/exec/hwaddr.h @@ -10,7 +10,7 @@ typedef uint64_t hwaddr; #define HWADDR_MAX UINT64_MAX -#define TARGET_FMT_plx "%016" PRIx64 +#define HWADDR_FMT_plx "%016" PRIx64 #define HWADDR_PRId PRId64 #define HWADDR_PRIi PRIi64 #define HWADDR_PRIo PRIo64 diff --git a/include/exec/ioport.h b/include/exec/ioport.h index e34f668998..4397f12f93 100644 --- a/include/exec/ioport.h +++ b/include/exec/ioport.h @@ -35,7 +35,6 @@ typedef struct MemoryRegionPortio { unsigned size; uint32_t (*read)(void *opaque, uint32_t address); void (*write)(void *opaque, uint32_t address, uint32_t data); - uint32_t base; /* private field */ } MemoryRegionPortio; #define PORTIO_END_OF_LIST() { } @@ -55,6 +54,7 @@ typedef struct PortioList { const struct MemoryRegionPortio *ports; Object *owner; struct MemoryRegion *address_space; + uint32_t addr; unsigned nr; struct MemoryRegion **regions; void *opaque; @@ -71,5 +71,7 @@ void portio_list_add(PortioList *piolist, struct MemoryRegion *address_space, uint32_t addr); void portio_list_del(PortioList *piolist); +void portio_list_set_enabled(PortioList *piolist, bool enabled); +void portio_list_set_address(PortioList *piolist, uint32_t addr); #endif /* IOPORT_H */ diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h index 9fb98bc1ef..14cdd8d582 100644 --- a/include/exec/memattrs.h +++ b/include/exec/memattrs.h @@ -29,10 +29,17 @@ typedef struct MemTxAttrs { * "didn't specify" if necessary. */ unsigned int unspecified:1; - /* ARM/AMBA: TrustZone Secure access + /* + * ARM/AMBA: TrustZone Secure access * x86: System Management Mode access */ unsigned int secure:1; + /* + * ARM: ArmSecuritySpace. This partially overlaps secure, but it is + * easier to have both fields to assist code that does not understand + * ARMv9 RME, or no specific knowledge of ARM at all (e.g. pflash). + */ + unsigned int space:2; /* Memory access is usermode (unprivileged) */ unsigned int user:1; /* @@ -45,18 +52,6 @@ typedef struct MemTxAttrs { unsigned int memory:1; /* Requester ID (for MSI for example) */ unsigned int requester_id:16; - /* Invert endianness for this page */ - unsigned int byte_swap:1; - /* - * The following are target-specific page-table bits. These are not - * related to actual memory transactions at all. However, this structure - * is part of the tlb_fill interface, cached in the cputlb structure, - * and has unused bits. These fields will be read by target-specific - * helpers using env->iotlb[mmu_idx][tlb_index()].attrs.target_tlb_bitN. - */ - unsigned int target_tlb_bit0 : 1; - unsigned int target_tlb_bit1 : 1; - unsigned int target_tlb_bit2 : 1; } MemTxAttrs; /* Bus masters which don't specify any attributes will get this, diff --git a/include/exec/memop.h b/include/exec/memop.h index 25d027434a..a86dc6743a 100644 --- a/include/exec/memop.h +++ b/include/exec/memop.h @@ -47,8 +47,6 @@ typedef enum MemOp { * MO_UNALN accesses are never checked for alignment. * MO_ALIGN accesses will result in a call to the CPU's * do_unaligned_access hook if the guest address is not aligned. - * The default depends on whether the target CPU defines - * TARGET_ALIGNED_ONLY. * * Some architectures (e.g. ARMv8) need the address which is aligned * to a size more than the size of the memory access. @@ -65,21 +63,51 @@ typedef enum MemOp { */ MO_ASHIFT = 5, MO_AMASK = 0x7 << MO_ASHIFT, -#ifdef NEED_CPU_H -#ifdef TARGET_ALIGNED_ONLY - MO_ALIGN = 0, - MO_UNALN = MO_AMASK, -#else - MO_ALIGN = MO_AMASK, - MO_UNALN = 0, -#endif -#endif + MO_UNALN = 0, MO_ALIGN_2 = 1 << MO_ASHIFT, MO_ALIGN_4 = 2 << MO_ASHIFT, MO_ALIGN_8 = 3 << MO_ASHIFT, MO_ALIGN_16 = 4 << MO_ASHIFT, MO_ALIGN_32 = 5 << MO_ASHIFT, MO_ALIGN_64 = 6 << MO_ASHIFT, + MO_ALIGN = MO_AMASK, + + /* + * MO_ATOM_* describes the atomicity requirements of the operation: + * MO_ATOM_IFALIGN: the operation must be single-copy atomic if it + * is aligned; if unaligned there is no atomicity. + * MO_ATOM_IFALIGN_PAIR: the entire operation may be considered to + * be a pair of half-sized operations which are packed together + * for convenience, with single-copy atomicity on each half if + * the half is aligned. + * This is the atomicity e.g. of Arm pre-FEAT_LSE2 LDP. + * MO_ATOM_WITHIN16: the operation is single-copy atomic, even if it + * is unaligned, so long as it does not cross a 16-byte boundary; + * if it crosses a 16-byte boundary there is no atomicity. + * This is the atomicity e.g. of Arm FEAT_LSE2 LDR. + * MO_ATOM_WITHIN16_PAIR: the entire operation is single-copy atomic, + * if it happens to be within a 16-byte boundary, otherwise it + * devolves to a pair of half-sized MO_ATOM_WITHIN16 operations. + * Depending on alignment, one or both will be single-copy atomic. + * This is the atomicity e.g. of Arm FEAT_LSE2 LDP. + * MO_ATOM_SUBALIGN: the operation is single-copy atomic by parts + * by the alignment. E.g. if the address is 0 mod 4, then each + * 4-byte subobject is single-copy atomic. + * This is the atomicity e.g. of IBM Power. + * MO_ATOM_NONE: the operation has no atomicity requirements. + * + * Note the default (i.e. 0) value is single-copy atomic to the + * size of the operation, if aligned. This retains the behaviour + * from before this field was introduced. + */ + MO_ATOM_SHIFT = 8, + MO_ATOM_IFALIGN = 0 << MO_ATOM_SHIFT, + MO_ATOM_IFALIGN_PAIR = 1 << MO_ATOM_SHIFT, + MO_ATOM_WITHIN16 = 2 << MO_ATOM_SHIFT, + MO_ATOM_WITHIN16_PAIR = 3 << MO_ATOM_SHIFT, + MO_ATOM_SUBALIGN = 4 << MO_ATOM_SHIFT, + MO_ATOM_NONE = 5 << MO_ATOM_SHIFT, + MO_ATOM_MASK = 7 << MO_ATOM_SHIFT, /* Combinations of the above, for ease of use. */ MO_UB = MO_8, diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 9fcc2af25c..100c1237ac 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -38,10 +38,6 @@ void flatview_unref(FlatView *view); extern const MemoryRegionOps unassigned_mem_ops; -bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr, - unsigned size, bool is_write, - MemTxAttrs attrs); - void flatview_add_to_dispatch(FlatView *fv, MemoryRegionSection *section); AddressSpaceDispatch *address_space_dispatch_new(FlatView *fv); void address_space_dispatch_compact(AddressSpaceDispatch *d); diff --git a/include/exec/memory.h b/include/exec/memory.h index f1c19451bc..8626a355b3 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -24,6 +24,7 @@ #include "qemu/bswap.h" #include "qemu/queue.h" #include "qemu/int128.h" +#include "qemu/range.h" #include "qemu/notify.h" #include "qom/object.h" #include "qemu/rcu.h" @@ -42,7 +43,7 @@ typedef struct IOMMUMemoryRegionClass IOMMUMemoryRegionClass; DECLARE_OBJ_CHECKERS(IOMMUMemoryRegion, IOMMUMemoryRegionClass, IOMMU_MEMORY_REGION, TYPE_IOMMU_MEMORY_REGION) -#define TYPE_RAM_DISCARD_MANAGER "qemu:ram-discard-manager" +#define TYPE_RAM_DISCARD_MANAGER "ram-discard-manager" typedef struct RamDiscardManagerClass RamDiscardManagerClass; typedef struct RamDiscardManager RamDiscardManager; DECLARE_OBJ_CHECKERS(RamDiscardManager, RamDiscardManagerClass, @@ -69,15 +70,17 @@ static inline void fuzz_dma_read_cb(size_t addr, /* Dirty tracking enabled because measuring dirty rate */ #define GLOBAL_DIRTY_DIRTY_RATE (1U << 1) -#define GLOBAL_DIRTY_MASK (0x3) +/* Dirty tracking enabled because dirty limit */ +#define GLOBAL_DIRTY_LIMIT (1U << 2) + +#define GLOBAL_DIRTY_MASK (0x7) extern unsigned int global_dirty_tracking; typedef struct MemoryRegionOps MemoryRegionOps; struct ReservedRegion { - hwaddr low; - hwaddr high; + Range range; unsigned type; }; @@ -92,6 +95,7 @@ struct ReservedRegion { * relative to the region's address space * @readonly: writes to this section are ignored * @nonvolatile: this section is non-volatile + * @unmergeable: this section should not get merged with adjacent sections */ struct MemoryRegionSection { Int128 size; @@ -101,6 +105,7 @@ struct MemoryRegionSection { hwaddr offset_within_address_space; bool readonly; bool nonvolatile; + bool unmergeable; }; typedef struct IOMMUTLBEntry IOMMUTLBEntry; @@ -126,6 +131,32 @@ struct IOMMUTLBEntry { /* * Bitmap for different IOMMUNotifier capabilities. Each notifier can * register with one or multiple IOMMU Notifier capability bit(s). + * + * Normally there're two use cases for the notifiers: + * + * (1) When the device needs accurate synchronizations of the vIOMMU page + * tables, it needs to register with both MAP|UNMAP notifies (which + * is defined as IOMMU_NOTIFIER_IOTLB_EVENTS below). + * + * Regarding to accurate synchronization, it's when the notified + * device maintains a shadow page table and must be notified on each + * guest MAP (page table entry creation) and UNMAP (invalidation) + * events (e.g. VFIO). Both notifications must be accurate so that + * the shadow page table is fully in sync with the guest view. + * + * (2) When the device doesn't need accurate synchronizations of the + * vIOMMU page tables, it needs to register only with UNMAP or + * DEVIOTLB_UNMAP notifies. + * + * It's when the device maintains a cache of IOMMU translations + * (IOTLB) and is able to fill that cache by requesting translations + * from the vIOMMU through a protocol similar to ATS (Address + * Translation Service). + * + * Note that in this mode the vIOMMU will not maintain a shadowed + * page table for the address space, and the UNMAP messages can cover + * more than the pages that used to get mapped. The IOMMU notifiee + * should be able to take care of over-sized invalidations. */ typedef enum { IOMMU_NOTIFIER_NONE = 0, @@ -203,6 +234,15 @@ typedef struct IOMMUTLBEvent { /* RAM that isn't accessible through normal means. */ #define RAM_PROTECTED (1 << 8) +/* RAM is an mmap-ed named file */ +#define RAM_NAMED_FILE (1 << 9) + +/* RAM is mmap-ed read-only */ +#define RAM_READONLY (1 << 10) + +/* RAM FD is opened read-only */ +#define RAM_READONLY_FD (1 << 11) + static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, IOMMUNotifierFlag flags, hwaddr start, hwaddr end, @@ -487,6 +527,26 @@ struct IOMMUMemoryRegionClass { int (*iommu_set_page_size_mask)(IOMMUMemoryRegion *iommu, uint64_t page_size_mask, Error **errp); + /** + * @iommu_set_iova_ranges: + * + * Propagate information about the usable IOVA ranges for a given IOMMU + * memory region. Used for example to propagate host physical device + * reserved memory region constraints to the virtual IOMMU. + * + * Optional method: if this method is not provided, then the default IOVA + * aperture is used. + * + * @iommu: the IOMMUMemoryRegion + * + * @iova_ranges: list of ordered IOVA ranges (at least one range) + * + * Returns 0 on success, or a negative error. In case of failure, the error + * object must be created. + */ + int (*iommu_set_iova_ranges)(IOMMUMemoryRegion *iommu, + GList *iova_ranges, + Error **errp); }; typedef struct RamDiscardListener RamDiscardListener; @@ -558,11 +618,12 @@ typedef void (*ReplayRamDiscard)(MemoryRegionSection *section, void *opaque); * A #RamDiscardManager coordinates which parts of specific RAM #MemoryRegion * regions are currently populated to be used/accessed by the VM, notifying * after parts were discarded (freeing up memory) and before parts will be - * populated (consuming memory), to be used/acessed by the VM. + * populated (consuming memory), to be used/accessed by the VM. * * A #RamDiscardManager can only be set for a RAM #MemoryRegion while the - * #MemoryRegion isn't mapped yet; it cannot change while the #MemoryRegion is - * mapped. + * #MemoryRegion isn't mapped into an address space yet (either directly + * or via an alias); it cannot change while the #MemoryRegion is + * mapped into an address space. * * The #RamDiscardManager is intended to be used by technologies that are * incompatible with discarding of RAM (e.g., VFIO, which may pin all @@ -582,7 +643,7 @@ typedef void (*ReplayRamDiscard)(MemoryRegionSection *section, void *opaque); * Listeners are called in multiples of the minimum granularity (unless it * would exceed the registered range) and changes are aligned to the minimum * granularity within the #MemoryRegion. Listeners have to prepare for memory - * becomming discarded in a different granularity than it was populated and the + * becoming discarded in a different granularity than it was populated and the * other way around. */ struct RamDiscardManagerClass { @@ -710,6 +771,10 @@ void ram_discard_manager_register_listener(RamDiscardManager *rdm, void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, RamDiscardListener *rdl); +bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, + ram_addr_t *ram_addr, bool *read_only, + bool *mr_has_discard_manager); + typedef struct CoalescedMemoryRange CoalescedMemoryRange; typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd; @@ -730,10 +795,13 @@ struct MemoryRegion { bool nonvolatile; bool rom_device; bool flush_coalesced_mmio; + bool unmergeable; uint8_t dirty_log_mask; bool is_iommu; RAMBlock *ram_block; Object *owner; + /* owner as TYPE_DEVICE. Used for re-entrancy checks in MR access hotpath */ + DeviceState *dev; const MemoryRegionOps *ops; void *opaque; @@ -758,6 +826,9 @@ struct MemoryRegion { unsigned ioeventfd_nb; MemoryRegionIoeventfd *ioeventfds; RamDiscardManager *rdm; /* Only for RAM */ + + /* For devices designed to perform re-entrant IO into their own IO MRs */ + bool disable_reentrancy_guard; }; struct IOMMUMemoryRegion { @@ -770,6 +841,10 @@ struct IOMMUMemoryRegion { #define IOMMU_NOTIFIER_FOREACH(n, mr) \ QLIST_FOREACH((n), &(mr)->iommu_notify, node) +#define MEMORY_LISTENER_PRIORITY_MIN 0 +#define MEMORY_LISTENER_PRIORITY_ACCEL 10 +#define MEMORY_LISTENER_PRIORITY_DEV_BACKEND 10 + /** * struct MemoryListener: callbacks structure for updates to the physical memory map * @@ -896,8 +971,11 @@ struct MemoryListener { * its @log_sync must be NULL. Vice versa. * * @listener: The #MemoryListener. + * @last_stage: The last stage to synchronize the log during migration. + * The caller should guarantee that the synchronization with true for + * @last_stage is triggered for once after all VCPUs have been stopped. */ - void (*log_sync_global)(MemoryListener *listener); + void (*log_sync_global)(MemoryListener *listener, bool last_stage); /** * @log_clear: @@ -1041,6 +1119,7 @@ struct AddressSpace { struct FlatView *current_map; int ioeventfd_nb; + int ioeventfd_notifiers; struct MemoryRegionIoeventfd *ioeventfds; QTAILQ_HEAD(, MemoryListener) listeners; QTAILQ_ENTRY(AddressSpace) address_spaces_link; @@ -1209,8 +1288,10 @@ void memory_region_init_io(MemoryRegion *mr, * * Note that this function does not do anything to cause the data in the * RAM memory region to be migrated; that is the responsibility of the caller. + * + * Return: true on success, else false setting @errp with error. */ -void memory_region_init_ram_nomigrate(MemoryRegion *mr, +bool memory_region_init_ram_nomigrate(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, @@ -1231,8 +1312,10 @@ void memory_region_init_ram_nomigrate(MemoryRegion *mr, * * Note that this function does not do anything to cause the data in the * RAM memory region to be migrated; that is the responsibility of the caller. + * + * Return: true on success, else false setting @errp with error. */ -void memory_region_init_ram_flags_nomigrate(MemoryRegion *mr, +bool memory_region_init_ram_flags_nomigrate(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, @@ -1240,7 +1323,7 @@ void memory_region_init_ram_flags_nomigrate(MemoryRegion *mr, Error **errp); /** - * memory_region_init_resizeable_ram: Initialize memory region with resizeable + * memory_region_init_resizeable_ram: Initialize memory region with resizable * RAM. Accesses into the region will * modify memory directly. Only an initial * portion of this RAM is actually used. @@ -1259,8 +1342,10 @@ void memory_region_init_ram_flags_nomigrate(MemoryRegion *mr, * * Note that this function does not do anything to cause the data in the * RAM memory region to be migrated; that is the responsibility of the caller. + * + * Return: true on success, else false setting @errp with error. */ -void memory_region_init_resizeable_ram(MemoryRegion *mr, +bool memory_region_init_resizeable_ram(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, @@ -1283,22 +1368,25 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, * @align: alignment of the region base address; if 0, the default alignment * (getpagesize()) will be used. * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, - * RAM_NORESERVE, + * RAM_NORESERVE, RAM_PROTECTED, RAM_NAMED_FILE, RAM_READONLY, + * RAM_READONLY_FD * @path: the path in which to allocate the RAM. - * @readonly: true to open @path for reading, false for read/write. + * @offset: offset within the file referenced by path * @errp: pointer to Error*, to store an error if it happens. * * Note that this function does not do anything to cause the data in the * RAM memory region to be migrated; that is the responsibility of the caller. + * + * Return: true on success, else false setting @errp with error. */ -void memory_region_init_ram_from_file(MemoryRegion *mr, +bool memory_region_init_ram_from_file(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, uint64_t align, uint32_t ram_flags, const char *path, - bool readonly, + ram_addr_t offset, Error **errp); /** @@ -1310,15 +1398,18 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, * @name: the name of the region. * @size: size of the region. * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, - * RAM_NORESERVE, RAM_PROTECTED. + * RAM_NORESERVE, RAM_PROTECTED, RAM_NAMED_FILE, RAM_READONLY, + * RAM_READONLY_FD * @fd: the fd to mmap. * @offset: offset within the file referenced by fd * @errp: pointer to Error*, to store an error if it happens. * * Note that this function does not do anything to cause the data in the * RAM memory region to be migrated; that is the responsibility of the caller. + * + * Return: true on success, else false setting @errp with error. */ -void memory_region_init_ram_from_fd(MemoryRegion *mr, +bool memory_region_init_ram_from_fd(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, @@ -1413,8 +1504,10 @@ void memory_region_init_alias(MemoryRegion *mr, * must be unique within any device * @size: size of the region. * @errp: pointer to Error*, to store an error if it happens. + * + * Return: true on success, else false setting @errp with error. */ -void memory_region_init_rom_nomigrate(MemoryRegion *mr, +bool memory_region_init_rom_nomigrate(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, @@ -1436,8 +1529,10 @@ void memory_region_init_rom_nomigrate(MemoryRegion *mr, * must be unique within any device * @size: size of the region. * @errp: pointer to Error*, to store an error if it happens. + * + * Return: true on success, else false setting @errp with error. */ -void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, +bool memory_region_init_rom_device_nomigrate(MemoryRegion *mr, Object *owner, const MemoryRegionOps *ops, void *opaque, @@ -1495,8 +1590,10 @@ void memory_region_init_iommu(void *_iommu_mr, * give the RAM block a unique name for migration purposes. * We should lift this restriction and allow arbitrary Objects. * If you pass a non-NULL non-device @owner then we will assert. + * + * Return: true on success, else false setting @errp with error. */ -void memory_region_init_ram(MemoryRegion *mr, +bool memory_region_init_ram(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, @@ -1522,8 +1619,10 @@ void memory_region_init_ram(MemoryRegion *mr, * must be unique within any device * @size: size of the region. * @errp: pointer to Error*, to store an error if it happens. + * + * Return: true on success, else false setting @errp with error. */ -void memory_region_init_rom(MemoryRegion *mr, +bool memory_region_init_rom(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, @@ -1553,8 +1652,10 @@ void memory_region_init_rom(MemoryRegion *mr, * must be unique within any device * @size: size of the region. * @errp: pointer to Error*, to store an error if it happens. + * + * Return: true on success, else false setting @errp with error. */ -void memory_region_init_rom_device(MemoryRegion *mr, +bool memory_region_init_rom_device(MemoryRegion *mr, Object *owner, const MemoryRegionOps *ops, void *opaque, @@ -1698,6 +1799,16 @@ void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, void memory_region_notify_iommu_one(IOMMUNotifier *notifier, IOMMUTLBEvent *event); +/** + * memory_region_unmap_iommu_notifier_range: notify a unmap for an IOMMU + * translation that covers the + * range of a notifier + * + * @notifier: the notifier to be notified + */ +void memory_region_unmap_iommu_notifier_range(IOMMUNotifier *notifier); + + /** * memory_region_register_iommu_notifier: register a notifier for changes to * IOMMU translation entries. @@ -1785,6 +1896,18 @@ int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, uint64_t page_size_mask, Error **errp); +/** + * memory_region_iommu_set_iova_ranges - Set the usable IOVA ranges + * for a given IOMMU MR region + * + * @iommu: IOMMU memory region + * @iova_ranges: list of ordered IOVA ranges (at least one range) + * @errp: pointer to Error*, to store an error if it happens. + */ +int memory_region_iommu_set_iova_ranges(IOMMUMemoryRegion *iommu, + GList *iova_ranges, + Error **errp); + /** * memory_region_name: get a memory region's name * @@ -1859,7 +1982,7 @@ int memory_region_get_fd(MemoryRegion *mr); * * Use with care; by the time this function returns, the returned pointer is * not protected by RCU anymore. If the caller is not within an RCU critical - * section and does not hold the iothread lock, it must have other means of + * section and does not hold the BQL, it must have other means of * protecting the pointer, such as a reference to the region that includes * the incoming ram_addr_t. * @@ -1876,7 +1999,7 @@ MemoryRegion *memory_region_from_host(void *ptr, ram_addr_t *offset); * * Use with care; by the time this function returns, the returned pointer is * not protected by RCU anymore. If the caller is not within an RCU critical - * section and does not hold the iothread lock, it must have other means of + * section and does not hold the BQL, it must have other means of * protecting the pointer, such as a reference to the region that includes * the incoming ram_addr_t. * @@ -1967,7 +2090,7 @@ void memory_region_clear_dirty_bitmap(MemoryRegion *mr, hwaddr start, * querying the same page multiple times, which is especially useful for * display updates where the scanlines often are not page aligned. * - * The dirty bitmap region which gets copyed into the snapshot (and + * The dirty bitmap region which gets copied into the snapshot (and * cleared afterwards) can be larger than requested. The boundaries * are rounded up/down so complete bitmap longs (covering 64 pages on * 64bit hosts) can be copied over into the bitmap snapshot. Which @@ -2283,6 +2406,25 @@ void memory_region_set_size(MemoryRegion *mr, uint64_t size); void memory_region_set_alias_offset(MemoryRegion *mr, hwaddr offset); +/* + * memory_region_set_unmergeable: Set a memory region unmergeable + * + * Mark a memory region unmergeable, resulting in the memory region (or + * everything contained in a memory region container) not getting merged when + * simplifying the address space and notifying memory listeners. Consequently, + * memory listeners will never get notified about ranges that are larger than + * the original memory regions. + * + * This is primarily useful when multiple aliases to a RAM memory region are + * mapped into a memory region container, and updates (e.g., enable/disable or + * map/unmap) of individual memory region aliases are not supposed to affect + * other memory regions in the same container. + * + * @mr: the #MemoryRegion to be updated + * @unmergeable: whether to mark the #MemoryRegion unmergeable + */ +void memory_region_set_unmergeable(MemoryRegion *mr, bool unmergeable); + /** * memory_region_present: checks if an address relative to a @container * translates into #MemoryRegion within @container @@ -2374,8 +2516,10 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr, * memory_global_dirty_log_sync: synchronize the dirty log for all memory * * Synchronizes the dirty page log for all address spaces. + * + * @last_stage: whether this is the last stage of live migration */ -void memory_global_dirty_log_sync(void); +void memory_global_dirty_log_sync(bool last_stage); /** * memory_global_dirty_log_sync: synchronize the dirty log for all memory @@ -2435,6 +2579,10 @@ void memory_global_dirty_log_stop(unsigned int flags); void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled); +bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr, + unsigned size, bool is_write, + MemTxAttrs attrs); + /** * memory_region_dispatch_read: perform a read directly to the specified * MemoryRegion. @@ -2598,9 +2746,6 @@ struct MemoryRegionCache { bool is_write; }; -#define MEMORY_REGION_CACHE_INVALID ((MemoryRegionCache) { .mrs.mr = NULL }) - - /* address_space_ld*_cached: load from a cached #MemoryRegion * address_space_st*_cached: store into a cached #MemoryRegion * @@ -2689,6 +2834,21 @@ int64_t address_space_cache_init(MemoryRegionCache *cache, hwaddr len, bool is_write); +/** + * address_space_cache_init_empty: Initialize empty #MemoryRegionCache + * + * @cache: The #MemoryRegionCache to operate on. + * + * Initializes #MemoryRegionCache structure without memory region attached. + * Cache initialized this way can only be safely destroyed, but not used. + */ +static inline void address_space_cache_init_empty(MemoryRegionCache *cache) +{ + cache->mrs.mr = NULL; + /* There is no real need to initialize fv, but it makes Coverity happy. */ + cache->fv = NULL; +} + /** * address_space_cache_invalidate: complete a write to a #MemoryRegionCache * @@ -2810,6 +2970,9 @@ MemTxResult address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr, const void *buf, hwaddr len); +int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr); +bool prepare_mmio_access(MemoryRegion *mr); + static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) { if (is_write) { @@ -2983,7 +3146,7 @@ int ram_block_discard_require(bool state); /* * See ram_block_discard_require(): only inhibit technologies that disable - * uncoordinated discarding of pages in RAM blocks, allowing co-existance with + * uncoordinated discarding of pages in RAM blocks, allowing co-existence with * technologies that only inhibit uncoordinated discards (via the * RamDiscardManager). */ diff --git a/include/exec/memory_ldst.h.inc b/include/exec/memory_ldst.h.inc index 7c3a641f7e..92ad74e956 100644 --- a/include/exec/memory_ldst.h.inc +++ b/include/exec/memory_ldst.h.inc @@ -20,48 +20,48 @@ */ #ifdef TARGET_ENDIANNESS -extern uint16_t glue(address_space_lduw, SUFFIX)(ARG1_DECL, +uint16_t glue(address_space_lduw, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result); -extern uint32_t glue(address_space_ldl, SUFFIX)(ARG1_DECL, +uint32_t glue(address_space_ldl, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result); -extern uint64_t glue(address_space_ldq, SUFFIX)(ARG1_DECL, +uint64_t glue(address_space_ldq, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result); -extern void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, +void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); -extern void glue(address_space_stw, SUFFIX)(ARG1_DECL, +void glue(address_space_stw, SUFFIX)(ARG1_DECL, hwaddr addr, uint16_t val, MemTxAttrs attrs, MemTxResult *result); -extern void glue(address_space_stl, SUFFIX)(ARG1_DECL, +void glue(address_space_stl, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); -extern void glue(address_space_stq, SUFFIX)(ARG1_DECL, +void glue(address_space_stq, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); #else -extern uint8_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, +uint8_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result); -extern uint16_t glue(address_space_lduw_le, SUFFIX)(ARG1_DECL, +uint16_t glue(address_space_lduw_le, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result); -extern uint16_t glue(address_space_lduw_be, SUFFIX)(ARG1_DECL, +uint16_t glue(address_space_lduw_be, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result); -extern uint32_t glue(address_space_ldl_le, SUFFIX)(ARG1_DECL, +uint32_t glue(address_space_ldl_le, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result); -extern uint32_t glue(address_space_ldl_be, SUFFIX)(ARG1_DECL, +uint32_t glue(address_space_ldl_be, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result); -extern uint64_t glue(address_space_ldq_le, SUFFIX)(ARG1_DECL, +uint64_t glue(address_space_ldq_le, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result); -extern uint64_t glue(address_space_ldq_be, SUFFIX)(ARG1_DECL, +uint64_t glue(address_space_ldq_be, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result); -extern void glue(address_space_stb, SUFFIX)(ARG1_DECL, +void glue(address_space_stb, SUFFIX)(ARG1_DECL, hwaddr addr, uint8_t val, MemTxAttrs attrs, MemTxResult *result); -extern void glue(address_space_stw_le, SUFFIX)(ARG1_DECL, +void glue(address_space_stw_le, SUFFIX)(ARG1_DECL, hwaddr addr, uint16_t val, MemTxAttrs attrs, MemTxResult *result); -extern void glue(address_space_stw_be, SUFFIX)(ARG1_DECL, +void glue(address_space_stw_be, SUFFIX)(ARG1_DECL, hwaddr addr, uint16_t val, MemTxAttrs attrs, MemTxResult *result); -extern void glue(address_space_stl_le, SUFFIX)(ARG1_DECL, +void glue(address_space_stl_le, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); -extern void glue(address_space_stl_be, SUFFIX)(ARG1_DECL, +void glue(address_space_stl_be, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); -extern void glue(address_space_stq_le, SUFFIX)(ARG1_DECL, +void glue(address_space_stq_le, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); -extern void glue(address_space_stq_be, SUFFIX)(ARG1_DECL, +void glue(address_space_stq_be, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); #endif diff --git a/include/exec/page-vary.h b/include/exec/page-vary.h index ebbe9b169b..54ddde308a 100644 --- a/include/exec/page-vary.h +++ b/include/exec/page-vary.h @@ -27,8 +27,8 @@ typedef struct { } TargetPageBits; #ifdef IN_PAGE_VARY -extern bool set_preferred_target_page_bits_common(int bits); -extern void finalize_target_page_bits_common(int min); +bool set_preferred_target_page_bits_common(int bits); +void finalize_target_page_bits_common(int min); #endif /** diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h index f92f169739..c4552b5061 100644 --- a/include/exec/plugin-gen.h +++ b/include/exec/plugin-gen.h @@ -12,44 +12,25 @@ #ifndef QEMU_PLUGIN_GEN_H #define QEMU_PLUGIN_GEN_H -#include "qemu/plugin.h" #include "tcg/tcg.h" struct DisasContextBase; #ifdef CONFIG_PLUGIN -bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb, bool supress); -void plugin_gen_tb_end(CPUState *cpu); +bool plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db, + bool supress); +void plugin_gen_tb_end(CPUState *cpu, size_t num_insns); void plugin_gen_insn_start(CPUState *cpu, const struct DisasContextBase *db); void plugin_gen_insn_end(void); void plugin_gen_disable_mem_helpers(void); -void plugin_gen_empty_mem_callback(TCGv addr, uint32_t info); - -static inline void plugin_insn_append(abi_ptr pc, const void *from, size_t size) -{ - struct qemu_plugin_insn *insn = tcg_ctx->plugin_insn; - abi_ptr off; - - if (insn == NULL) { - return; - } - off = pc - insn->vaddr; - if (off < insn->data->len) { - g_byte_array_set_size(insn->data, off); - } else if (off > insn->data->len) { - /* we have an unexpected gap */ - g_assert_not_reached(); - } - - insn->data = g_byte_array_append(insn->data, from, size); -} +void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info); #else /* !CONFIG_PLUGIN */ -static inline -bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb, bool supress) +static inline bool +plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db, bool sup) { return false; } @@ -61,16 +42,13 @@ void plugin_gen_insn_start(CPUState *cpu, const struct DisasContextBase *db) static inline void plugin_gen_insn_end(void) { } -static inline void plugin_gen_tb_end(CPUState *cpu) +static inline void plugin_gen_tb_end(CPUState *cpu, size_t num_insns) { } static inline void plugin_gen_disable_mem_helpers(void) { } -static inline void plugin_gen_empty_mem_callback(TCGv addr, uint32_t info) -{ } - -static inline void plugin_insn_append(abi_ptr pc, const void *from, size_t size) +static inline void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info) { } #endif /* CONFIG_PLUGIN */ diff --git a/include/exec/poison.h b/include/exec/poison.h index 08421ebe82..2c2075ad0c 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -37,7 +37,6 @@ #pragma GCC poison TARGET_TRICORE #pragma GCC poison TARGET_XTENSA -#pragma GCC poison TARGET_ALIGNED_ONLY #pragma GCC poison TARGET_HAS_BFLT #pragma GCC poison TARGET_NAME #pragma GCC poison TARGET_SUPPORTS_MTTCG @@ -68,8 +67,6 @@ #pragma GCC poison CPU_INTERRUPT_TGT_INT_2 #pragma GCC poison CONFIG_ALPHA_DIS -#pragma GCC poison CONFIG_ARM_A64_DIS -#pragma GCC poison CONFIG_ARM_DIS #pragma GCC poison CONFIG_CRIS_DIS #pragma GCC poison CONFIG_HPPA_DIS #pragma GCC poison CONFIG_I386_DIS @@ -78,7 +75,6 @@ #pragma GCC poison CONFIG_M68K_DIS #pragma GCC poison CONFIG_MICROBLAZE_DIS #pragma GCC poison CONFIG_MIPS_DIS -#pragma GCC poison CONFIG_NANOMIPS_DIS #pragma GCC poison CONFIG_NIOS2_DIS #pragma GCC poison CONFIG_PPC_DIS #pragma GCC poison CONFIG_RISCV_DIS @@ -87,11 +83,9 @@ #pragma GCC poison CONFIG_SPARC_DIS #pragma GCC poison CONFIG_XTENSA_DIS -#pragma GCC poison CONFIG_HAX #pragma GCC poison CONFIG_HVF #pragma GCC poison CONFIG_LINUX_USER #pragma GCC poison CONFIG_KVM -#pragma GCC poison CONFIG_SOFTMMU #pragma GCC poison CONFIG_WHPX #pragma GCC poison CONFIG_XEN diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index f3e0c78161..de45ba7bc9 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -25,6 +25,7 @@ #include "sysemu/tcg.h" #include "exec/ramlist.h" #include "exec/ramblock.h" +#include "exec/exec-all.h" extern uint64_t total_dirty_pages; @@ -42,7 +43,8 @@ static inline long clear_bmap_size(uint64_t pages, uint8_t shift) } /** - * clear_bmap_set: set clear bitmap for the page range + * clear_bmap_set: set clear bitmap for the page range. Must be with + * bitmap_mutex held. * * @rb: the ramblock to operate on * @start: the start page number @@ -55,12 +57,12 @@ static inline void clear_bmap_set(RAMBlock *rb, uint64_t start, { uint8_t shift = rb->clear_bmap_shift; - bitmap_set_atomic(rb->clear_bmap, start >> shift, - clear_bmap_size(npages, shift)); + bitmap_set(rb->clear_bmap, start >> shift, clear_bmap_size(npages, shift)); } /** - * clear_bmap_test_and_clear: test clear bitmap for the page, clear if set + * clear_bmap_test_and_clear: test clear bitmap for the page, clear if set. + * Must be with bitmap_mutex held. * * @rb: the ramblock to operate on * @page: the page number to check @@ -71,7 +73,7 @@ static inline bool clear_bmap_test_and_clear(RAMBlock *rb, uint64_t page) { uint8_t shift = rb->clear_bmap_shift; - return bitmap_test_and_clear_atomic(rb->clear_bmap, page >> shift, 1); + return bitmap_test_and_clear(rb->clear_bmap, page >> shift, 1); } static inline bool offset_in_ramblock(RAMBlock *b, ram_addr_t offset) @@ -107,9 +109,10 @@ long qemu_maxrampagesize(void); * @size: the size in bytes of the ram block * @mr: the memory region where the ram block is * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, - * RAM_NORESERVE. + * RAM_NORESERVE, RAM_PROTECTED, RAM_NAMED_FILE, RAM_READONLY, + * RAM_READONLY_FD * @mem_path or @fd: specify the backing file or device - * @readonly: true to open @path for reading, false for read/write. + * @offset: Offset into target file * @errp: pointer to Error*, to store an error if it happens * * Return: @@ -118,10 +121,10 @@ long qemu_maxrampagesize(void); */ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, uint32_t ram_flags, const char *mem_path, - bool readonly, Error **errp); + off_t offset, Error **errp); RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, uint32_t ram_flags, int fd, off_t offset, - bool readonly, Error **errp); + Error **errp); RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, MemoryRegion *mr, Error **errp); @@ -147,8 +150,6 @@ static inline void qemu_ram_block_writeback(RAMBlock *block) #define DIRTY_CLIENTS_ALL ((1 << DIRTY_MEMORY_NUM) - 1) #define DIRTY_CLIENTS_NOCODE (DIRTY_CLIENTS_ALL & ~(1 << DIRTY_MEMORY_CODE)) -void tb_invalidate_phys_range(ram_addr_t start, ram_addr_t end); - static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, ram_addr_t length, unsigned client) @@ -334,14 +335,23 @@ static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, } #if !defined(_WIN32) -static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, - ram_addr_t start, - ram_addr_t pages) + +/* + * Contrary to cpu_physical_memory_sync_dirty_bitmap() this function returns + * the number of dirty pages in @bitmap passed as argument. On the other hand, + * cpu_physical_memory_sync_dirty_bitmap() returns newly dirtied pages that + * weren't set in the global migration bitmap. + */ +static inline +uint64_t cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, + ram_addr_t start, + ram_addr_t pages) { unsigned long i, j; - unsigned long page_number, c; + unsigned long page_number, c, nbits; hwaddr addr; ram_addr_t ram_addr; + uint64_t num_dirty = 0; unsigned long len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS; unsigned long hpratio = qemu_real_host_page_size() / TARGET_PAGE_SIZE; unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); @@ -369,6 +379,7 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, if (bitmap[k]) { unsigned long temp = leul_to_cpu(bitmap[k]); + nbits = ctpopl(temp); qatomic_or(&blocks[DIRTY_MEMORY_VGA][idx][offset], temp); if (global_dirty_tracking) { @@ -377,10 +388,12 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, temp); if (unlikely( global_dirty_tracking & GLOBAL_DIRTY_DIRTY_RATE)) { - total_dirty_pages += ctpopl(temp); + total_dirty_pages += nbits; } } + num_dirty += nbits; + if (tcg_enabled()) { qatomic_or(&blocks[DIRTY_MEMORY_CODE][idx][offset], temp); @@ -409,9 +422,11 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, for (i = 0; i < len; i++) { if (bitmap[i] != 0) { c = leul_to_cpu(bitmap[i]); + nbits = ctpopl(c); if (unlikely(global_dirty_tracking & GLOBAL_DIRTY_DIRTY_RATE)) { - total_dirty_pages += ctpopl(c); + total_dirty_pages += nbits; } + num_dirty += nbits; do { j = ctzl(c); c &= ~(1ul << j); @@ -424,9 +439,19 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, } } } + + return num_dirty; } #endif /* not _WIN32 */ +static inline void cpu_physical_memory_dirty_bits_cleared(ram_addr_t start, + ram_addr_t length) +{ + if (tcg_enabled()) { + tlb_reset_dirty_range_all(start, length); + } + +} bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, ram_addr_t length, unsigned client); @@ -488,6 +513,9 @@ uint64_t cpu_physical_memory_sync_dirty_bitmap(RAMBlock *rb, idx++; } } + if (num_dirty) { + cpu_physical_memory_dirty_bits_cleared(start, length); + } if (rb->clear_bmap) { /* diff --git a/include/exec/ramblock.h b/include/exec/ramblock.h index 6cbedf9e0c..848915ea5b 100644 --- a/include/exec/ramblock.h +++ b/include/exec/ramblock.h @@ -34,15 +34,29 @@ struct RAMBlock { ram_addr_t max_length; void (*resized)(const char*, uint64_t length, void *host); uint32_t flags; - /* Protected by iothread lock. */ + /* Protected by the BQL. */ char idstr[256]; /* RCU-enabled, writes protected by the ramlist lock */ QLIST_ENTRY(RAMBlock) next; QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers; int fd; + uint64_t fd_offset; size_t page_size; /* dirty bitmap used during migration */ unsigned long *bmap; + + /* + * Below fields are only used by mapped-ram migration + */ + /* bitmap of pages present in the migration file */ + unsigned long *file_bmap; + /* + * offset in the file pages belonging to this ramblock are saved, + * used only during migration to a file. + */ + off_t bitmap_offset; + uint64_t pages_offset; + /* bitmap of already received pages in postcopy */ unsigned long *receivedmap; @@ -53,6 +67,9 @@ struct RAMBlock { * and split clearing of dirty bitmap on the remote node (e.g., * KVM). The bitmap will be set only when doing global sync. * + * It is only used during src side of ram migration, and it is + * protected by the global ram_state.bitmap_mutex. + * * 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 diff --git a/include/exec/replay-core.h b/include/exec/replay-core.h new file mode 100644 index 0000000000..244c77acce --- /dev/null +++ b/include/exec/replay-core.h @@ -0,0 +1,80 @@ +/* + * QEMU replay core API + * + * Copyright (c) 2010-2015 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. + */ + +#ifndef EXEC_REPLAY_H +#define EXEC_REPLAY_H + +#include "qapi/qapi-types-replay.h" + +extern ReplayMode replay_mode; + +/* Replay process control functions */ + +/* Enables recording or saving event log with specified parameters */ +void replay_configure(struct QemuOpts *opts); +/* Initializes timers used for snapshotting and enables events recording */ +void replay_start(void); +/* Closes replay log file and frees other resources. */ +void replay_finish(void); +/* Adds replay blocker with the specified error description */ +void replay_add_blocker(const char *feature); +/* Returns name of the replay log file */ +const char *replay_get_filename(void); + +/* + * Start making one step in backward direction. + * Used by gdbstub for backwards debugging. + * Returns true on success. + */ +bool replay_reverse_step(void); +/* + * Start searching the last breakpoint/watchpoint. + * Used by gdbstub for backwards debugging. + * Returns true if the process successfully started. + */ +bool replay_reverse_continue(void); +/* + * Returns true if replay module is processing + * reverse_continue or reverse_step request + */ +bool replay_running_debug(void); +/* Called in reverse debugging mode to collect breakpoint information */ +void replay_breakpoint(void); +/* Called when gdb is attached to gdbstub */ +void replay_gdb_attached(void); + +/* Interrupts and exceptions */ + +/* Called by exception handler to write or read exception processing events */ +bool replay_exception(void); +/* + * Used to determine that exception is pending. + * Does not proceed to the next event in the log. + */ +bool replay_has_exception(void); +/* + * Called by interrupt handlers to write or read interrupt processing events. + * Returns true if interrupt should be processed. + */ +bool replay_interrupt(void); +/* + * Tries to read interrupt event from the file. + * Returns true, when interrupt request is pending. + */ +bool replay_has_interrupt(void); + +/* 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); + +#endif diff --git a/include/exec/softmmu-semi.h b/include/exec/softmmu-semi.h deleted file mode 100644 index fbcae88f4b..0000000000 --- a/include/exec/softmmu-semi.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Helper routines to provide target memory access for semihosting - * syscalls in system emulation mode. - * - * Copyright (c) 2007 CodeSourcery. - * - * This code is licensed under the GPL - */ - -#ifndef SOFTMMU_SEMI_H -#define SOFTMMU_SEMI_H - -#include "cpu.h" - -static inline uint64_t softmmu_tget64(CPUArchState *env, target_ulong addr) -{ - uint64_t val; - - cpu_memory_rw_debug(env_cpu(env), addr, (uint8_t *)&val, 8, 0); - return tswap64(val); -} - -static inline uint32_t softmmu_tget32(CPUArchState *env, target_ulong addr) -{ - uint32_t val; - - cpu_memory_rw_debug(env_cpu(env), addr, (uint8_t *)&val, 4, 0); - return tswap32(val); -} - -static inline uint32_t softmmu_tget8(CPUArchState *env, target_ulong addr) -{ - uint8_t val; - - cpu_memory_rw_debug(env_cpu(env), addr, &val, 1, 0); - return val; -} - -#define get_user_u64(arg, p) ({ arg = softmmu_tget64(env, p); 0; }) -#define get_user_u32(arg, p) ({ arg = softmmu_tget32(env, p) ; 0; }) -#define get_user_u8(arg, p) ({ arg = softmmu_tget8(env, p) ; 0; }) -#define get_user_ual(arg, p) get_user_u32(arg, p) - -static inline void softmmu_tput64(CPUArchState *env, - target_ulong addr, uint64_t val) -{ - val = tswap64(val); - cpu_memory_rw_debug(env_cpu(env), addr, (uint8_t *)&val, 8, 1); -} - -static inline void softmmu_tput32(CPUArchState *env, - target_ulong addr, uint32_t val) -{ - val = tswap32(val); - cpu_memory_rw_debug(env_cpu(env), addr, (uint8_t *)&val, 4, 1); -} -#define put_user_u64(arg, p) ({ softmmu_tput64(env, p, arg) ; 0; }) -#define put_user_u32(arg, p) ({ softmmu_tput32(env, p, arg) ; 0; }) -#define put_user_ual(arg, p) put_user_u32(arg, p) - -static void *softmmu_lock_user(CPUArchState *env, - target_ulong addr, target_ulong len, int copy) -{ - uint8_t *p; - /* TODO: Make this something that isn't fixed size. */ - p = malloc(len); - if (p && copy) { - cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0); - } - return p; -} -#define lock_user(type, p, len, copy) softmmu_lock_user(env, p, len, copy) -static char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr) -{ - char *p; - char *s; - uint8_t c; - /* TODO: Make this something that isn't fixed size. */ - s = p = malloc(1024); - if (!s) { - return NULL; - } - do { - cpu_memory_rw_debug(env_cpu(env), addr, &c, 1, 0); - addr++; - *(p++) = c; - } while (c); - return s; -} -#define lock_user_string(p) softmmu_lock_user_string(env, p) -static void softmmu_unlock_user(CPUArchState *env, void *p, target_ulong addr, - target_ulong len) -{ - if (len) { - cpu_memory_rw_debug(env_cpu(env), addr, p, len, 1); - } - free(p); -} -#define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len) - -#endif diff --git a/include/exec/target_long.h b/include/exec/target_long.h new file mode 100644 index 0000000000..3cd8e26a23 --- /dev/null +++ b/include/exec/target_long.h @@ -0,0 +1,44 @@ +/* + * Target Long Definitions + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2023 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef _TARGET_LONG_H_ +#define _TARGET_LONG_H_ + +/* + * Usually this should only be included via cpu-defs.h however for + * certain cases where we want to build only two versions of a binary + * object we can include directly. However the build-system must + * ensure TARGET_LONG_BITS is defined directly. + */ +#ifndef TARGET_LONG_BITS +#error TARGET_LONG_BITS not defined +#endif + +#define TARGET_LONG_SIZE (TARGET_LONG_BITS / 8) + +/* target_ulong is the type of a virtual address */ +#if TARGET_LONG_SIZE == 4 +typedef int32_t target_long; +typedef uint32_t target_ulong; +#define TARGET_FMT_lx "%08x" +#define TARGET_FMT_ld "%d" +#define TARGET_FMT_lu "%u" +#define MO_TL MO_32 +#elif TARGET_LONG_SIZE == 8 +typedef int64_t target_long; +typedef uint64_t target_ulong; +#define TARGET_FMT_lx "%016" PRIx64 +#define TARGET_FMT_ld "%" PRId64 +#define TARGET_FMT_lu "%" PRIu64 +#define MO_TL MO_64 +#else +#error TARGET_LONG_SIZE undefined +#endif + +#endif /* _TARGET_LONG_H_ */ diff --git a/include/exec/target_page.h b/include/exec/target_page.h index 96726c36a4..98ffbb5c23 100644 --- a/include/exec/target_page.h +++ b/include/exec/target_page.h @@ -15,7 +15,9 @@ #define EXEC_TARGET_PAGE_H size_t qemu_target_page_size(void); +int qemu_target_page_mask(void); int qemu_target_page_bits(void); int qemu_target_page_bits_min(void); +size_t qemu_target_pages_to_MiB(size_t pages); #endif diff --git a/include/exec/tb-flush.h b/include/exec/tb-flush.h new file mode 100644 index 0000000000..142c240d94 --- /dev/null +++ b/include/exec/tb-flush.h @@ -0,0 +1,28 @@ +/* + * tb-flush prototype for use by the rest of the system. + * + * Copyright (c) 2022 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef _TB_FLUSH_H_ +#define _TB_FLUSH_H_ + +/** + * tb_flush() - flush all translation blocks + * @cs: CPUState (must be valid, but treated as anonymous pointer) + * + * Used to flush all the translation blocks in the system. Sometimes + * it is simpler to flush everything than work out which individual + * translations are now invalid and ensure they are not called + * anymore. + * + * tb_flush() takes care of running the flush in an exclusive context + * if it is not already running in one. This means no guest code will + * run until this complete. + */ +void tb_flush(CPUState *cs); + +void tcg_flush_jmp_cache(CPUState *cs); + +#endif /* _TB_FLUSH_H_ */ diff --git a/include/exec/tlb-common.h b/include/exec/tlb-common.h new file mode 100644 index 0000000000..dc5a5faa0b --- /dev/null +++ b/include/exec/tlb-common.h @@ -0,0 +1,56 @@ +/* + * Common definitions for the softmmu tlb + * + * 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.1 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 EXEC_TLB_COMMON_H +#define EXEC_TLB_COMMON_H 1 + +#define CPU_TLB_ENTRY_BITS 5 + +/* Minimalized TLB entry for use by TCG fast path. */ +typedef union CPUTLBEntry { + struct { + uint64_t addr_read; + uint64_t addr_write; + uint64_t addr_code; + /* + * Addend to virtual address to get host address. IO accesses + * use the corresponding iotlb value. + */ + uintptr_t addend; + }; + /* + * Padding to get a power of two size, as well as index + * access to addr_{read,write,code}. + */ + uint64_t addr_idx[(1 << CPU_TLB_ENTRY_BITS) / sizeof(uint64_t)]; +} CPUTLBEntry; + +QEMU_BUILD_BUG_ON(sizeof(CPUTLBEntry) != (1 << CPU_TLB_ENTRY_BITS)); + +/* + * Data elements that are per MMU mode, accessed by the fast path. + * The structure is aligned to aid loading the pair with one insn. + */ +typedef struct CPUTLBDescFast { + /* Contains (n_entries - 1) << CPU_TLB_ENTRY_BITS */ + uintptr_t mask; + /* The array of tlb entries itself. */ + CPUTLBEntry *table; +} CPUTLBDescFast QEMU_ALIGNED(2 * sizeof(void *)); + +#endif /* EXEC_TLB_COMMON_H */ diff --git a/include/exec/translate-all.h b/include/exec/translate-all.h index 9f646389af..85c9460c7c 100644 --- a/include/exec/translate-all.h +++ b/include/exec/translate-all.h @@ -23,13 +23,6 @@ /* translate-all.c */ -struct page_collection *page_collection_lock(tb_page_addr_t start, - tb_page_addr_t end); -void page_collection_unlock(struct page_collection *set); -void tb_invalidate_phys_page_fast(struct page_collection *pages, - tb_page_addr_t start, int len, - uintptr_t retaddr); -void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end); void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr); #ifdef CONFIG_USER_ONLY diff --git a/include/exec/translation-block.h b/include/exec/translation-block.h new file mode 100644 index 0000000000..48211c890a --- /dev/null +++ b/include/exec/translation-block.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Definition of TranslationBlock. + * Copyright (c) 2003 Fabrice Bellard + */ + +#ifndef EXEC_TRANSLATION_BLOCK_H +#define EXEC_TRANSLATION_BLOCK_H + +#include "qemu/thread.h" +#include "exec/cpu-common.h" +#ifdef CONFIG_USER_ONLY +#include "qemu/interval-tree.h" +#endif + +/* + * Page tracking code uses ram addresses in system mode, and virtual + * addresses in userspace mode. Define tb_page_addr_t to be an + * appropriate type. + */ +#if defined(CONFIG_USER_ONLY) +typedef vaddr tb_page_addr_t; +#define TB_PAGE_ADDR_FMT "%" VADDR_PRIx +#else +typedef ram_addr_t tb_page_addr_t; +#define TB_PAGE_ADDR_FMT RAM_ADDR_FMT +#endif + +/* + * Translation Cache-related fields of a TB. + * This struct exists just for convenience; we keep track of TB's in a binary + * search tree, and the only fields needed to compare TB's in the tree are + * @ptr and @size. + * Note: the address of search data can be obtained by adding @size to @ptr. + */ +struct tb_tc { + const void *ptr; /* pointer to the translated code */ + size_t size; +}; + +struct TranslationBlock { + /* + * Guest PC corresponding to this block. This must be the true + * virtual address. Therefore e.g. x86 stores EIP + CS_BASE, and + * targets like Arm, MIPS, HP-PA, which reuse low bits for ISA or + * privilege, must store those bits elsewhere. + * + * If CF_PCREL, the opcodes for the TranslationBlock are written + * such that the TB is associated only with the physical page and + * may be run in any virtual address context. In this case, PC + * must always be taken from ENV in a target-specific manner. + * Unwind information is taken as offsets from the page, to be + * deposited into the "current" PC. + */ + vaddr pc; + + /* + * Target-specific data associated with the TranslationBlock, e.g.: + * x86: the original user, the Code Segment virtual base, + * arm: an extension of tb->flags, + * s390x: instruction data for EXECUTE, + * sparc: the next pc of the instruction queue (for delay slots). + */ + uint64_t cs_base; + + uint32_t flags; /* flags defining in which context the code was generated */ + uint32_t cflags; /* compile flags */ + +/* Note that TCG_MAX_INSNS is 512; we validate this match elsewhere. */ +#define CF_COUNT_MASK 0x000001ff +#define CF_NO_GOTO_TB 0x00000200 /* Do not chain with goto_tb */ +#define CF_NO_GOTO_PTR 0x00000400 /* Do not chain with goto_ptr */ +#define CF_SINGLE_STEP 0x00000800 /* gdbstub single-step in effect */ +#define CF_MEMI_ONLY 0x00001000 /* Only instrument memory ops */ +#define CF_USE_ICOUNT 0x00002000 +#define CF_INVALID 0x00004000 /* TB is stale. Set with @jmp_lock held */ +#define CF_PARALLEL 0x00008000 /* Generate code for a parallel context */ +#define CF_NOIRQ 0x00010000 /* Generate an uninterruptible TB */ +#define CF_PCREL 0x00020000 /* Opcodes in TB are PC-relative */ +#define CF_CLUSTER_MASK 0xff000000 /* Top 8 bits are cluster ID */ +#define CF_CLUSTER_SHIFT 24 + + /* + * Above fields used for comparing + */ + + /* size of target code for this block (1 <= size <= TARGET_PAGE_SIZE) */ + uint16_t size; + uint16_t icount; + + struct tb_tc tc; + + /* + * Track tb_page_addr_t intervals that intersect this TB. + * For user-only, the virtual addresses are always contiguous, + * and we use a unified interval tree. For system, we use a + * linked list headed in each PageDesc. Within the list, the lsb + * of the previous pointer tells the index of page_next[], and the + * list is protected by the PageDesc lock(s). + */ +#ifdef CONFIG_USER_ONLY + IntervalTreeNode itree; +#else + uintptr_t page_next[2]; + tb_page_addr_t page_addr[2]; +#endif + + /* jmp_lock placed here to fill a 4-byte hole. Its documentation is below */ + QemuSpin jmp_lock; + + /* The following data are used to directly call another TB from + * the code of this one. This can be done either by emitting direct or + * indirect native jump instructions. These jumps are reset so that the TB + * just continues its execution. The TB can be linked to another one by + * setting one of the jump targets (or patching the jump instruction). Only + * two of such jumps are supported. + */ +#define TB_JMP_OFFSET_INVALID 0xffff /* indicates no jump generated */ + uint16_t jmp_reset_offset[2]; /* offset of original jump target */ + uint16_t jmp_insn_offset[2]; /* offset of direct jump insn */ + uintptr_t jmp_target_addr[2]; /* target address */ + + /* + * Each TB has a NULL-terminated list (jmp_list_head) of incoming jumps. + * Each TB can have two outgoing jumps, and therefore can participate + * in two lists. The list entries are kept in jmp_list_next[2]. The least + * significant bit (LSB) of the pointers in these lists is used to encode + * which of the two list entries is to be used in the pointed TB. + * + * List traversals are protected by jmp_lock. The destination TB of each + * outgoing jump is kept in jmp_dest[] so that the appropriate jmp_lock + * can be acquired from any origin TB. + * + * jmp_dest[] are tagged pointers as well. The LSB is set when the TB is + * being invalidated, so that no further outgoing jumps from it can be set. + * + * jmp_lock also protects the CF_INVALID cflag; a jump must not be chained + * to a destination TB that has CF_INVALID set. + */ + uintptr_t jmp_list_head; + uintptr_t jmp_list_next[2]; + uintptr_t jmp_dest[2]; +}; + +/* The alignment given to TranslationBlock during allocation. */ +#define CODE_GEN_ALIGN 16 + +/* Hide the qatomic_read to make code a little easier on the eyes */ +static inline uint32_t tb_cflags(const TranslationBlock *tb) +{ + return qatomic_read(&tb->cflags); +} + +#endif /* EXEC_TRANSLATION_BLOCK_H */ diff --git a/include/exec/translator.h b/include/exec/translator.h index 7db6845535..2c4fb818e7 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -18,14 +18,22 @@ * member in your target-specific DisasContext. */ - #include "qemu/bswap.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "exec/plugin-gen.h" -#include "exec/translate-all.h" -#include "tcg/tcg.h" +#include "exec/cpu_ldst.h" /* for abi_ptr */ +/** + * gen_intermediate_code + * @cpu: cpu context + * @tb: translation block + * @max_insns: max number of instructions to translate + * @pc: guest virtual program counter address + * @host_pc: host physical program counter address + * + * This function must be provided by the target, which should create + * the target-specific DisasContext, and then invoke translator_loop. + */ +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc); /** * DisasJumpType: @@ -64,28 +72,24 @@ typedef enum DisasJumpType { * @num_insns: Number of translated instructions (including current). * @max_insns: Maximum number of instructions to be translated in this TB. * @singlestep_enabled: "Hardware" single stepping enabled. + * @saved_can_do_io: Known value of cpu->neg.can_do_io, or -1 for unknown. + * @plugin_enabled: TCG plugin enabled in this TB. + * @insn_start: The last op emitted by the insn_start hook, + * which is expected to be INDEX_op_insn_start. * * Architecture-agnostic disassembly context. */ typedef struct DisasContextBase { - const TranslationBlock *tb; - target_ulong pc_first; - target_ulong pc_next; + TranslationBlock *tb; + vaddr pc_first; + vaddr pc_next; DisasJumpType is_jmp; int num_insns; int max_insns; bool singlestep_enabled; -#ifdef CONFIG_USER_ONLY - /* - * Guest address of the last byte of the last protected page. - * - * Pages containing the translated instructions are made non-writable in - * order to achieve consistency in case another thread is modifying the - * code while translate_insn() fetches the instruction bytes piecemeal. - * Such writer threads are blocked on mmap_lock() in page_unprotect(). - */ - target_ulong page_protect_end; -#endif + bool plugin_enabled; + struct TCGOp *insn_start; + void *host_addr[2]; } DisasContextBase; /** @@ -123,11 +127,13 @@ typedef struct TranslatorOps { /** * translator_loop: - * @ops: Target-specific operations. - * @db: Disassembly context. * @cpu: Target vCPU. * @tb: Translation block. * @max_insns: Maximum number of insns to translate. + * @pc: guest virtual program counter address + * @host_pc: host physical program counter address + * @ops: Target-specific operations. + * @db: Disassembly context. * * Generic translator loop. * @@ -141,10 +147,9 @@ typedef struct TranslatorOps { * - When single-stepping is enabled (system-wide or on the current vCPU). * - When too many instructions have been translated. */ -void translator_loop(const TranslatorOps *ops, DisasContextBase *db, - CPUState *cpu, TranslationBlock *tb, int max_insns); - -void translator_loop_temp_check(DisasContextBase *db); +void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc, const TranslatorOps *ops, + DisasContextBase *db); /** * translator_use_goto_tb @@ -154,7 +159,17 @@ void translator_loop_temp_check(DisasContextBase *db); * Return true if goto_tb is allowed between the current TB * and the destination PC. */ -bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest); +bool translator_use_goto_tb(DisasContextBase *db, vaddr dest); + +/** + * translator_io_start + * @db: Disassembly context + * + * If icount is enabled, set cpu->can_do_io, adjust db->is_jmp to + * DISAS_TOO_MANY if it is still DISAS_NEXT, and return true. + * Otherwise return false. + */ +bool translator_io_start(DisasContextBase *db); /* * Translator Load Functions @@ -167,24 +182,64 @@ bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest); * the relevant information at translation time. */ -#define GEN_TRANSLATOR_LD(fullname, type, load_fn, swap_fn) \ - type fullname ## _swap(CPUArchState *env, DisasContextBase *dcbase, \ - abi_ptr pc, bool do_swap); \ - static inline type fullname(CPUArchState *env, \ - DisasContextBase *dcbase, abi_ptr pc) \ - { \ - return fullname ## _swap(env, dcbase, pc, false); \ +uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc); +uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc); +uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc); +uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc); + +static inline uint16_t +translator_lduw_swap(CPUArchState *env, DisasContextBase *db, + abi_ptr pc, bool do_swap) +{ + uint16_t ret = translator_lduw(env, db, pc); + if (do_swap) { + ret = bswap16(ret); } + return ret; +} -#define FOR_EACH_TRANSLATOR_LD(F) \ - F(translator_ldub, uint8_t, cpu_ldub_code, /* no swap */) \ - F(translator_ldsw, int16_t, cpu_ldsw_code, bswap16) \ - F(translator_lduw, uint16_t, cpu_lduw_code, bswap16) \ - F(translator_ldl, uint32_t, cpu_ldl_code, bswap32) \ - F(translator_ldq, uint64_t, cpu_ldq_code, bswap64) +static inline uint32_t +translator_ldl_swap(CPUArchState *env, DisasContextBase *db, + abi_ptr pc, bool do_swap) +{ + uint32_t ret = translator_ldl(env, db, pc); + if (do_swap) { + ret = bswap32(ret); + } + return ret; +} -FOR_EACH_TRANSLATOR_LD(GEN_TRANSLATOR_LD) +static inline uint64_t +translator_ldq_swap(CPUArchState *env, DisasContextBase *db, + abi_ptr pc, bool do_swap) +{ + uint64_t ret = translator_ldq(env, db, pc); + if (do_swap) { + ret = bswap64(ret); + } + return ret; +} -#undef GEN_TRANSLATOR_LD +/** + * translator_fake_ldb - fake instruction load + * @insn8: byte of instruction + * @pc: program counter of instruction + * + * This is a special case helper used where the instruction we are + * about to translate comes from somewhere else (e.g. being + * re-synthesised for s390x "ex"). It ensures we update other areas of + * the translator with details of the executed instruction. + */ +void translator_fake_ldb(uint8_t insn8, abi_ptr pc); + +/* + * Return whether addr is on the same page as where disassembly started. + * Translators can use this to enforce the rule that only single-insn + * translation blocks are allowed to cross page boundaries. + */ +static inline bool is_same_page(const DisasContextBase *db, vaddr addr) +{ + return ((addr ^ db->pc_first) & TARGET_PAGE_MASK) == 0; +} #endif /* EXEC__TRANSLATOR_H */ diff --git a/include/exec/tswap.h b/include/exec/tswap.h new file mode 100644 index 0000000000..68944a880b --- /dev/null +++ b/include/exec/tswap.h @@ -0,0 +1,72 @@ +/* + * Macros for swapping a value if the endianness is different + * between the target and the host. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef TSWAP_H +#define TSWAP_H + +#include "hw/core/cpu.h" +#include "qemu/bswap.h" + +/* + * If we're in target-specific code, we can hard-code the swapping + * condition, otherwise we have to do (slower) run-time checks. + */ +#ifdef NEED_CPU_H +#define target_needs_bswap() (HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN) +#else +#define target_needs_bswap() (target_words_bigendian() != HOST_BIG_ENDIAN) +#endif + +static inline uint16_t tswap16(uint16_t s) +{ + if (target_needs_bswap()) { + return bswap16(s); + } else { + return s; + } +} + +static inline uint32_t tswap32(uint32_t s) +{ + if (target_needs_bswap()) { + return bswap32(s); + } else { + return s; + } +} + +static inline uint64_t tswap64(uint64_t s) +{ + if (target_needs_bswap()) { + return bswap64(s); + } else { + return s; + } +} + +static inline void tswap16s(uint16_t *s) +{ + if (target_needs_bswap()) { + *s = bswap16(*s); + } +} + +static inline void tswap32s(uint32_t *s) +{ + if (target_needs_bswap()) { + *s = bswap32(*s); + } +} + +static inline void tswap64s(uint64_t *s) +{ + if (target_needs_bswap()) { + *s = bswap64(*s); + } +} + +#endif /* TSWAP_H */ diff --git a/include/exec/user/abitypes.h b/include/exec/user/abitypes.h index 743b8bb9ea..6178453d94 100644 --- a/include/exec/user/abitypes.h +++ b/include/exec/user/abitypes.h @@ -15,7 +15,18 @@ #define ABI_LLONG_ALIGNMENT 2 #endif -#if (defined(TARGET_I386) && !defined(TARGET_X86_64)) || defined(TARGET_SH4) +#ifdef TARGET_CRIS +#define ABI_SHORT_ALIGNMENT 1 +#define ABI_INT_ALIGNMENT 1 +#define ABI_LONG_ALIGNMENT 1 +#define ABI_LLONG_ALIGNMENT 1 +#endif + +#if (defined(TARGET_I386) && !defined(TARGET_X86_64)) \ + || defined(TARGET_SH4) \ + || defined(TARGET_OPENRISC) \ + || defined(TARGET_MICROBLAZE) \ + || defined(TARGET_NIOS2) #define ABI_LLONG_ALIGNMENT 4 #endif diff --git a/include/exec/user/guest-base.h b/include/exec/user/guest-base.h new file mode 100644 index 0000000000..afe2ab7fbb --- /dev/null +++ b/include/exec/user/guest-base.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Declaration of guest_base. + * Copyright (c) 2003 Fabrice Bellard + */ + +#ifndef EXEC_USER_GUEST_BASE_H +#define EXEC_USER_GUEST_BASE_H + +extern uintptr_t guest_base; + +#endif diff --git a/include/exec/user/thunk.h b/include/exec/user/thunk.h index 300a840d58..2ebfecf58e 100644 --- a/include/exec/user/thunk.h +++ b/include/exec/user/thunk.h @@ -111,8 +111,7 @@ static inline int thunk_type_size(const argtype *type_ptr, int is_host) if (is_host) { #if defined(HOST_X86_64) return 8; -#elif defined(HOST_ALPHA) || defined(HOST_IA64) || defined(HOST_MIPS) || \ - defined(HOST_PARISC) || defined(HOST_SPARC64) +#elif defined(HOST_MIPS) || defined(HOST_SPARC64) return 4; #elif defined(HOST_PPC) return sizeof(void *); @@ -193,10 +192,17 @@ static inline int thunk_type_align(const argtype *type_ptr, int is_host) } } -unsigned int target_to_host_bitmask(unsigned int target_mask, - const bitmask_transtbl * trans_tbl); -unsigned int host_to_target_bitmask(unsigned int host_mask, - const bitmask_transtbl * trans_tbl); +unsigned int target_to_host_bitmask_len(unsigned int target_mask, + const bitmask_transtbl *trans_tbl, + size_t trans_len); +unsigned int host_to_target_bitmask_len(unsigned int host_mask, + const bitmask_transtbl * trans_tbl, + size_t trans_len); + +#define target_to_host_bitmask(M, T) \ + target_to_host_bitmask_len(M, T, ARRAY_SIZE(T)) +#define host_to_target_bitmask(M, T) \ + host_to_target_bitmask_len(M, T, ARRAY_SIZE(T)) void thunk_init(unsigned int max_structs); diff --git a/include/exec/vaddr.h b/include/exec/vaddr.h new file mode 100644 index 0000000000..b9844afc77 --- /dev/null +++ b/include/exec/vaddr.h @@ -0,0 +1,18 @@ +/* Define vaddr. */ + +#ifndef VADDR_H +#define VADDR_H + +/** + * vaddr: + * Type wide enough to contain any #target_ulong virtual address. + */ +typedef uint64_t vaddr; +#define VADDR_PRId PRId64 +#define VADDR_PRIu PRIu64 +#define VADDR_PRIo PRIo64 +#define VADDR_PRIx PRIx64 +#define VADDR_PRIX PRIX64 +#define VADDR_MAX UINT64_MAX + +#endif diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 7a6ea881d8..0884ec4ef7 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -195,6 +195,10 @@ typedef struct float_status { bool snan_bit_is_one; bool use_first_nan; bool no_signaling_nans; + /* should overflowed results subtract re_bias to its exponent? */ + bool rebias_overflow; + /* should underflowed results add re_bias to its exponent? */ + bool rebias_underflow; } float_status; #endif /* SOFTFLOAT_TYPES_H */ diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 3dcf20e3a2..eb64075b9c 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -366,6 +366,8 @@ float32 bfloat16_to_float32(bfloat16, float_status *status); bfloat16 float64_to_bfloat16(float64 a, float_status *status); float64 bfloat16_to_float64(bfloat16 a, float_status *status); +int8_t bfloat16_to_int8_scalbn(bfloat16, FloatRoundMode, + int, float_status *status); int16_t bfloat16_to_int16_scalbn(bfloat16, FloatRoundMode, int, float_status *status); int32_t bfloat16_to_int32_scalbn(bfloat16, FloatRoundMode, @@ -373,14 +375,18 @@ int32_t bfloat16_to_int32_scalbn(bfloat16, FloatRoundMode, int64_t bfloat16_to_int64_scalbn(bfloat16, FloatRoundMode, int, float_status *status); +int8_t bfloat16_to_int8(bfloat16, float_status *status); int16_t bfloat16_to_int16(bfloat16, float_status *status); int32_t bfloat16_to_int32(bfloat16, float_status *status); int64_t bfloat16_to_int64(bfloat16, float_status *status); +int8_t bfloat16_to_int8_round_to_zero(bfloat16, float_status *status); int16_t bfloat16_to_int16_round_to_zero(bfloat16, float_status *status); int32_t bfloat16_to_int32_round_to_zero(bfloat16, float_status *status); int64_t bfloat16_to_int64_round_to_zero(bfloat16, float_status *status); +uint8_t bfloat16_to_uint8_scalbn(bfloat16 a, FloatRoundMode, + int, float_status *status); uint16_t bfloat16_to_uint16_scalbn(bfloat16 a, FloatRoundMode, int, float_status *status); uint32_t bfloat16_to_uint32_scalbn(bfloat16 a, FloatRoundMode, @@ -388,24 +394,30 @@ uint32_t bfloat16_to_uint32_scalbn(bfloat16 a, FloatRoundMode, uint64_t bfloat16_to_uint64_scalbn(bfloat16 a, FloatRoundMode, int, float_status *status); +uint8_t bfloat16_to_uint8(bfloat16 a, float_status *status); uint16_t bfloat16_to_uint16(bfloat16 a, float_status *status); uint32_t bfloat16_to_uint32(bfloat16 a, float_status *status); uint64_t bfloat16_to_uint64(bfloat16 a, float_status *status); +uint8_t bfloat16_to_uint8_round_to_zero(bfloat16 a, float_status *status); uint16_t bfloat16_to_uint16_round_to_zero(bfloat16 a, float_status *status); uint32_t bfloat16_to_uint32_round_to_zero(bfloat16 a, float_status *status); uint64_t bfloat16_to_uint64_round_to_zero(bfloat16 a, float_status *status); +bfloat16 int8_to_bfloat16_scalbn(int8_t a, int, float_status *status); bfloat16 int16_to_bfloat16_scalbn(int16_t a, int, float_status *status); bfloat16 int32_to_bfloat16_scalbn(int32_t a, int, float_status *status); bfloat16 int64_to_bfloat16_scalbn(int64_t a, int, float_status *status); +bfloat16 uint8_to_bfloat16_scalbn(uint8_t a, int, float_status *status); bfloat16 uint16_to_bfloat16_scalbn(uint16_t a, int, float_status *status); bfloat16 uint32_to_bfloat16_scalbn(uint32_t a, int, float_status *status); bfloat16 uint64_to_bfloat16_scalbn(uint64_t a, int, float_status *status); +bfloat16 int8_to_bfloat16(int8_t a, float_status *status); bfloat16 int16_to_bfloat16(int16_t a, float_status *status); bfloat16 int32_to_bfloat16(int32_t a, float_status *status); bfloat16 int64_to_bfloat16(int64_t a, float_status *status); +bfloat16 uint8_to_bfloat16(uint8_t a, float_status *status); bfloat16 uint16_to_bfloat16(uint16_t a, float_status *status); bfloat16 uint32_to_bfloat16(uint32_t a, float_status *status); bfloat16 uint64_to_bfloat16(uint64_t a, float_status *status); @@ -751,6 +763,9 @@ int16_t float64_to_int16_round_to_zero(float64, float_status *status); int32_t float64_to_int32_round_to_zero(float64, float_status *status); int64_t float64_to_int64_round_to_zero(float64, float_status *status); +int32_t float64_to_int32_modulo(float64, FloatRoundMode, float_status *status); +int64_t float64_to_int64_modulo(float64, FloatRoundMode, float_status *status); + uint16_t float64_to_uint16_scalbn(float64, FloatRoundMode, int, float_status *); uint32_t float64_to_uint32_scalbn(float64, FloatRoundMode, int, float_status *); uint64_t float64_to_uint64_scalbn(float64, FloatRoundMode, int, float_status *); diff --git a/include/gdbstub/helpers.h b/include/gdbstub/helpers.h new file mode 100644 index 0000000000..c573aef2dc --- /dev/null +++ b/include/gdbstub/helpers.h @@ -0,0 +1,103 @@ +/* + * gdbstub helpers + * + * These are all used by the various frontends and have to be host + * aware to ensure things are store in target order. + * + * Copyright (c) 2022 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef _GDBSTUB_HELPERS_H_ +#define _GDBSTUB_HELPERS_H_ + +#ifdef NEED_CPU_H +#include "cpu.h" + +/* + * 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(GByteArray *buf, uint8_t val) +{ + g_byte_array_append(buf, &val, 1); + return 1; +} + +static inline int gdb_get_reg16(GByteArray *buf, uint16_t 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(GByteArray *buf, uint32_t 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(GByteArray *buf, uint64_t 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; +#if TARGET_BIG_ENDIAN + 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_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) +#else +#define gdb_get_regl(buf, val) gdb_get_reg32(buf, val) +#define ldtul_p(addr) ldl_p(addr) +#endif + +#else +#error "gdbstub helpers should only be included by target specific code" +#endif + +#endif /* _GDBSTUB_HELPERS_H_ */ diff --git a/include/gdbstub/syscalls.h b/include/gdbstub/syscalls.h new file mode 100644 index 0000000000..54ff7245a1 --- /dev/null +++ b/include/gdbstub/syscalls.h @@ -0,0 +1,122 @@ +/* + * GDB Syscall support + * + * Copyright (c) 2023 Linaro Ltd + * + * SPDX-License-Identifier: LGPL-2.0+ + */ + +#ifndef _SYSCALLS_H_ +#define _SYSCALLS_H_ + +/* For gdb file i/o remote protocol open flags. */ +#define GDB_O_RDONLY 0 +#define GDB_O_WRONLY 1 +#define GDB_O_RDWR 2 +#define GDB_O_APPEND 8 +#define GDB_O_CREAT 0x200 +#define GDB_O_TRUNC 0x400 +#define GDB_O_EXCL 0x800 + +/* For gdb file i/o remote protocol errno values */ +#define GDB_EPERM 1 +#define GDB_ENOENT 2 +#define GDB_EINTR 4 +#define GDB_EBADF 9 +#define GDB_EACCES 13 +#define GDB_EFAULT 14 +#define GDB_EBUSY 16 +#define GDB_EEXIST 17 +#define GDB_ENODEV 19 +#define GDB_ENOTDIR 20 +#define GDB_EISDIR 21 +#define GDB_EINVAL 22 +#define GDB_ENFILE 23 +#define GDB_EMFILE 24 +#define GDB_EFBIG 27 +#define GDB_ENOSPC 28 +#define GDB_ESPIPE 29 +#define GDB_EROFS 30 +#define GDB_ENAMETOOLONG 91 +#define GDB_EUNKNOWN 9999 + +/* For gdb file i/o remote protocol lseek whence. */ +#define GDB_SEEK_SET 0 +#define GDB_SEEK_CUR 1 +#define GDB_SEEK_END 2 + +/* For gdb file i/o stat/fstat. */ +typedef uint32_t gdb_mode_t; +typedef uint32_t gdb_time_t; + +struct gdb_stat { + uint32_t gdb_st_dev; /* device */ + uint32_t gdb_st_ino; /* inode */ + gdb_mode_t gdb_st_mode; /* protection */ + uint32_t gdb_st_nlink; /* number of hard links */ + uint32_t gdb_st_uid; /* user ID of owner */ + uint32_t gdb_st_gid; /* group ID of owner */ + uint32_t gdb_st_rdev; /* device type (if inode device) */ + uint64_t gdb_st_size; /* total size, in bytes */ + uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */ + uint64_t gdb_st_blocks; /* number of blocks allocated */ + gdb_time_t gdb_st_atime; /* time of last access */ + gdb_time_t gdb_st_mtime; /* time of last modification */ + gdb_time_t gdb_st_ctime; /* time of last change */ +} QEMU_PACKED; + +struct gdb_timeval { + gdb_time_t tv_sec; /* second */ + uint64_t tv_usec; /* microsecond */ +} QEMU_PACKED; + +typedef void (*gdb_syscall_complete_cb)(CPUState *cpu, uint64_t ret, int err); + +/** + * gdb_do_syscall: + * @cb: function to call when the system call has completed + * @fmt: gdb syscall format string + * ...: list of arguments to interpolate into @fmt + * + * Send a GDB syscall request. This function will return immediately; + * the callback function will be called later when the remote system + * call has completed. + * + * @fmt should be in the 'call-id,parameter,parameter...' format documented + * for the F request packet in the GDB remote protocol. A limited set of + * printf-style format specifiers is supported: + * %x - target_ulong argument printed in hex + * %lx - 64-bit argument printed in hex + * %s - string pointer (target_ulong) and length (int) pair + */ +void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...); + +/** + * use_gdb_syscalls() - report if GDB should be used for syscalls + * + * This is mostly driven by the semihosting mode the user configures + * but assuming GDB is allowed by that we report true if GDB is + * connected to the stub. + */ +int use_gdb_syscalls(void); + +/** + * gdb_exit: exit gdb session, reporting inferior status + * @code: exit code reported + * + * This closes the session and sends a final packet to GDB reporting + * the exit status of the program. It also cleans up any connections + * detritus before returning. + */ +void gdb_exit(int code); + +/** + * gdb_qemu_exit: ask qemu to exit + * @code: exit code reported + * + * This requests qemu to exit. This function is allowed to return as + * the exit request might be processed asynchronously by qemu backend. + */ +void gdb_qemu_exit(int code); + +#endif /* _SYSCALLS_H_ */ diff --git a/include/gdbstub/user.h b/include/gdbstub/user.h new file mode 100644 index 0000000000..3b8358e3da --- /dev/null +++ b/include/gdbstub/user.h @@ -0,0 +1,67 @@ +/* + * gdbstub user-mode only APIs + * + * Copyright (c) 2022 Linaro Ltd + * + * SPDX-License-Identifier: LGPL-2.0+ + */ + +#ifndef GDBSTUB_USER_H +#define GDBSTUB_USER_H + +#define MAX_SIGINFO_LENGTH 128 + +/** + * gdb_handlesig() - yield control to gdb + * @cpu: CPU + * @sig: if non-zero, the signal number which caused us to stop + * @reason: stop reason for stop reply packet or NULL + * @siginfo: target-specific siginfo struct + * @siginfo_len: target-specific siginfo struct length + * + * This function yields control to gdb, when a user-mode-only target + * needs to stop execution. If @sig is non-zero, then we will send a + * stop packet to tell gdb that we have stopped because of this signal. + * + * This function will block (handling protocol requests from gdb) + * until gdb tells us to continue target execution. When it does + * return, the return value is a signal to deliver to the target, + * or 0 if no signal should be delivered, ie the signal that caused + * us to stop should be ignored. + */ +int gdb_handlesig(CPUState *, int, const char *, void *, int); + +/** + * gdb_signalled() - inform remote gdb of sig exit + * @as: current CPUArchState + * @sig: signal number + */ +void gdb_signalled(CPUArchState *as, int sig); + +/** + * gdbserver_fork_start() - inform gdb of the upcoming fork() + */ +void gdbserver_fork_start(void); + +/** + * gdbserver_fork_end() - inform gdb of the completed fork() + * @cs: CPU + * @pid: 0 if in child process, -1 if fork failed, child process pid otherwise + */ +void gdbserver_fork_end(CPUState *cs, pid_t pid); + +/** + * gdb_syscall_entry() - inform gdb of syscall entry and yield control to it + * @cs: CPU + * @num: syscall number + */ +void gdb_syscall_entry(CPUState *cs, int num); + +/** + * gdb_syscall_entry() - inform gdb of syscall return and yield control to it + * @cs: CPU + * @num: syscall number + */ +void gdb_syscall_return(CPUState *cs, int num); + +#endif /* GDBSTUB_USER_H */ diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 2b42e4192b..0e6e82b339 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -90,6 +90,39 @@ typedef struct AcpiFadtData { unsigned *xdsdt_tbl_offset; } AcpiFadtData; +typedef struct AcpiGas { + uint8_t id; /* Address space ID */ + uint8_t width; /* Register bit width */ + uint8_t offset; /* Register bit offset */ + uint8_t size; /* Access size */ + uint64_t addr; /* Address */ +} AcpiGas; + +/* SPCR (Serial Port Console Redirection table) */ +typedef struct AcpiSpcrData { + uint8_t interface_type; + uint8_t reserved[3]; + struct AcpiGas base_addr; + uint8_t interrupt_type; + uint8_t pc_interrupt; + uint32_t interrupt; /* Global system interrupt */ + uint8_t baud_rate; + uint8_t parity; + uint8_t stop_bits; + uint8_t flow_control; + uint8_t terminal_type; + uint8_t language; + uint8_t reserved1; + uint16_t pci_device_id; /* Must be 0xffff if not PCI device */ + uint16_t pci_vendor_id; /* Must be 0xffff if not PCI device */ + uint8_t pci_bus; + uint8_t pci_device; + uint8_t pci_function; + uint32_t pci_flags; + uint8_t pci_segment; + uint32_t reserved2; +} AcpiSpcrData; + #define ACPI_FADT_ARM_PSCI_COMPLIANT (1 << 0) #define ACPI_FADT_ARM_PSCI_USE_HVC (1 << 1) diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h index cc0d370745..e0e51e85b4 100644 --- a/include/hw/acpi/acpi.h +++ b/include/hw/acpi/acpi.h @@ -66,7 +66,7 @@ #define ACPI_BITMASK_POWER_BUTTON_STATUS 0x0100 #define ACPI_BITMASK_SLEEP_BUTTON_STATUS 0x0200 #define ACPI_BITMASK_RT_CLOCK_STATUS 0x0400 -#define ACPI_BITMASK_PCIEXP_WAKE_STATUS 0x4000 /* ACPI 3.0 */ +#define ACPI_BITMASK_PCIEXP_WAKE_STATUS 0x4000 /* ACPI 3.0 */ #define ACPI_BITMASK_WAKE_STATUS 0x8000 #define ACPI_BITMASK_ALL_FIXED_STATUS (\ @@ -84,7 +84,7 @@ #define ACPI_BITMASK_POWER_BUTTON_ENABLE 0x0100 #define ACPI_BITMASK_SLEEP_BUTTON_ENABLE 0x0200 #define ACPI_BITMASK_RT_CLOCK_ENABLE 0x0400 -#define ACPI_BITMASK_PCIEXP_WAKE_DISABLE 0x4000 /* ACPI 3.0 */ +#define ACPI_BITMASK_PCIEXP_WAKE_DISABLE 0x4000 /* ACPI 3.0 */ #define ACPI_BITMASK_PM1_COMMON_ENABLED ( \ ACPI_BITMASK_RT_CLOCK_ENABLE | \ diff --git a/include/hw/acpi/acpi_aml_interface.h b/include/hw/acpi/acpi_aml_interface.h new file mode 100644 index 0000000000..11748a8866 --- /dev/null +++ b/include/hw/acpi/acpi_aml_interface.h @@ -0,0 +1,52 @@ +#ifndef ACPI_AML_INTERFACE_H +#define ACPI_AML_INTERFACE_H + +#include "qom/object.h" +#include "hw/acpi/aml-build.h" +#include "hw/qdev-core.h" + +#define TYPE_ACPI_DEV_AML_IF "acpi-dev-aml-interface" +typedef struct AcpiDevAmlIfClass AcpiDevAmlIfClass; +DECLARE_CLASS_CHECKERS(AcpiDevAmlIfClass, ACPI_DEV_AML_IF, TYPE_ACPI_DEV_AML_IF) +#define ACPI_DEV_AML_IF(obj) \ + INTERFACE_CHECK(AcpiDevAmlIf, (obj), TYPE_ACPI_DEV_AML_IF) + +typedef struct AcpiDevAmlIf AcpiDevAmlIf; +typedef void (*dev_aml_fn)(AcpiDevAmlIf *adev, Aml *scope); + +/** + * AcpiDevAmlIfClass: + * + * build_dev_aml: adds device specific AML blob to provided scope + * + * Interface is designed for providing generic callback that builds device + * specific AML blob. + */ +struct AcpiDevAmlIfClass { + /* */ + InterfaceClass parent_class; + + /* */ + dev_aml_fn build_dev_aml; +}; + +static inline dev_aml_fn get_dev_aml_func(DeviceState *dev) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_ACPI_DEV_AML_IF)) { + AcpiDevAmlIfClass *klass = ACPI_DEV_AML_IF_GET_CLASS(dev); + return klass->build_dev_aml; + } + return NULL; +} + +static inline void call_dev_aml_func(DeviceState *dev, Aml *scope) +{ + dev_aml_fn fn = get_dev_aml_func(dev); + if (fn) { + fn(ACPI_DEV_AML_IF(dev), scope); + } +} + +void qbus_build_aml(BusState *bus, Aml *scope); + +#endif diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h index ea6056ab92..68d9d15f50 100644 --- a/include/hw/acpi/acpi_dev_interface.h +++ b/include/hw/acpi/acpi_dev_interface.h @@ -3,7 +3,6 @@ #include "qapi/qapi-types-acpi.h" #include "qom/object.h" -#include "hw/boards.h" #include "hw/qdev-core.h" /* These values are part of guest ABI, and can not be changed */ @@ -52,8 +51,5 @@ struct AcpiDeviceIfClass { /* */ void (*ospm_status)(AcpiDeviceIf *adev, ACPIOSTInfoList ***list); void (*send_event)(AcpiDeviceIf *adev, AcpiEventStatusBits ev); - void (*madt_cpu)(AcpiDeviceIf *adev, int uid, - const CPUArchIdList *apic_ids, GArray *entry, - bool force_enabled); }; #endif diff --git a/include/hw/acpi/acpi_generic_initiator.h b/include/hw/acpi/acpi_generic_initiator.h new file mode 100644 index 0000000000..a304bad73e --- /dev/null +++ b/include/hw/acpi/acpi_generic_initiator.h @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved + */ + +#ifndef ACPI_GENERIC_INITIATOR_H +#define ACPI_GENERIC_INITIATOR_H + +#include "qom/object_interfaces.h" + +#define TYPE_ACPI_GENERIC_INITIATOR "acpi-generic-initiator" + +typedef struct AcpiGenericInitiator { + /* private */ + Object parent; + + /* public */ + char *pci_dev; + uint16_t node; +} AcpiGenericInitiator; + +/* + * ACPI 6.3: + * Table 5-81 Flags – Generic Initiator Affinity Structure + */ +typedef enum { + /* + * If clear, the OSPM ignores the contents of the Generic + * Initiator/Port Affinity Structure. This allows system firmware + * to populate the SRAT with a static number of structures, but only + * enable them as necessary. + */ + GEN_AFFINITY_ENABLED = (1 << 0), +} GenericAffinityFlags; + +/* + * ACPI 6.3: + * Table 5-80 Device Handle - PCI + */ +typedef struct PCIDeviceHandle { + uint16_t segment; + uint16_t bdf; +} PCIDeviceHandle; + +void build_srat_generic_pci_initiator(GArray *table_data); + +#endif diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index d1fb08514b..a3784155cb 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -277,7 +277,7 @@ void free_aml_allocator(void); * @child: element that is copied into @parent_ctx context * * Joins Aml elements together and helps to construct AML tables - * Examle of usage: + * Example of usage: * Aml *table = aml_def_block("SSDT", ...); * Aml *sb = aml_scope("\\_SB"); * Aml *dev = aml_device("PCI0"); @@ -497,4 +497,8 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, void build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog, const char *oem_id, const char *oem_table_id); + +void build_spcr(GArray *table_data, BIOSLinker *linker, + const AcpiSpcrData *f, const uint8_t rev, + const char *oem_id, const char *oem_table_id); #endif diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h index 999caaf510..e6e1a9ef59 100644 --- a/include/hw/acpi/cpu.h +++ b/include/hw/acpi/cpu.h @@ -12,13 +12,15 @@ #ifndef ACPI_CPU_H #define ACPI_CPU_H +#include "qapi/qapi-types-acpi.h" #include "hw/qdev-core.h" #include "hw/acpi/acpi.h" #include "hw/acpi/aml-build.h" +#include "hw/boards.h" #include "hw/hotplug.h" typedef struct AcpiCpuStatus { - struct CPUState *cpu; + CPUState *cpu; uint64_t arch_id; bool is_inserting; bool is_removing; @@ -55,8 +57,11 @@ typedef struct CPUHotplugFeatures { const char *smi_path; } CPUHotplugFeatures; +typedef void (*build_madt_cpu_fn)(int uid, const CPUArchIdList *apic_ids, + GArray *entry, bool force_enabled); + void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, - hwaddr io_base, + build_madt_cpu_fn build_madt_cpu, hwaddr io_base, const char *res_root, const char *event_handler_method); diff --git a/include/hw/acpi/cxl.h b/include/hw/acpi/cxl.h index 0c496538c0..8f22c71530 100644 --- a/include/hw/acpi/cxl.h +++ b/include/hw/acpi/cxl.h @@ -19,10 +19,12 @@ #define HW_ACPI_CXL_H #include "hw/acpi/bios-linker-loader.h" +#include "hw/cxl/cxl.h" -void cxl_build_cedt(MachineState *ms, GArray *table_offsets, GArray *table_data, +void cxl_build_cedt(GArray *table_offsets, GArray *table_data, BIOSLinker *linker, const char *oem_id, - const char *oem_table_id); + const char *oem_table_id, CXLState *cxl_state); void build_cxl_osc_method(Aml *dev); +void build_cxl_dsm_method(Aml *dev); #endif diff --git a/include/hw/acpi/erst.h b/include/hw/acpi/erst.h index b747fe7739..b2ff663ddc 100644 --- a/include/hw/acpi/erst.h +++ b/include/hw/acpi/erst.h @@ -11,6 +11,9 @@ #ifndef HW_ACPI_ERST_H #define HW_ACPI_ERST_H +#include "hw/acpi/bios-linker-loader.h" +#include "qom/object.h" + void build_erst(GArray *table_data, BIOSLinker *linker, Object *erst_dev, const char *oem_id, const char *oem_table_id); diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h index d831bbd889..ba84ce0214 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h @@ -69,8 +69,6 @@ #define TYPE_ACPI_GED "acpi-ged" OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED) -#define TYPE_ACPI_GED_X86 "acpi-ged-x86" - #define ACPI_GED_EVT_SEL_OFFSET 0x0 #define ACPI_GED_EVT_SEL_LEN 0x4 diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index 7ca92843c6..2faf7f0cae 100644 --- a/include/hw/acpi/ich9.h +++ b/include/hw/acpi/ich9.h @@ -27,7 +27,7 @@ #include "hw/acpi/pcihp.h" #include "hw/acpi/memory_hotplug.h" #include "hw/acpi/acpi_dev_interface.h" -#include "hw/acpi/tco.h" +#include "hw/acpi/ich9_tco.h" #define ACPI_PCIHP_ADDR_ICH9 0x0cc0 @@ -64,7 +64,7 @@ typedef struct ICH9LPCPMRegs { uint8_t disable_s3; uint8_t disable_s4; uint8_t s4_val; - uint8_t smm_enabled; + bool smm_enabled; bool smm_compat; bool enable_tco; TCOIORegs tco_regs; @@ -72,9 +72,7 @@ typedef struct ICH9LPCPMRegs { #define ACPI_PM_PROP_TCO_ENABLED "enable_tco" -void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, - bool smm_enabled, - qemu_irq sci_irq); +void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq); void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base); extern const VMStateDescription vmstate_ich9_pm; @@ -89,6 +87,7 @@ void ich9_pm_device_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); void ich9_pm_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); +bool ich9_pm_is_hotpluggable_bus(HotplugHandler *hotplug_dev, BusState *bus); void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list); #endif /* HW_ACPI_ICH9_H */ diff --git a/include/hw/acpi/tco.h b/include/hw/acpi/ich9_tco.h similarity index 95% rename from include/hw/acpi/tco.h rename to include/hw/acpi/ich9_tco.h index a1e0da8213..2562a7cf39 100644 --- a/include/hw/acpi/tco.h +++ b/include/hw/acpi/ich9_tco.h @@ -1,5 +1,5 @@ /* - * QEMU ICH9 TCO emulation + * QEMU ICH9 TCO emulation (total cost of ownership) * * Copyright (c) 2015 Paulo Alcantara * @@ -11,6 +11,7 @@ #define HW_ACPI_TCO_H #include "exec/memory.h" +#include "migration/vmstate.h" /* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */ #define TCO_TICK_NSEC 600000000LL diff --git a/include/hw/acpi/ipmi.h b/include/hw/acpi/ipmi.h index c14ad682ac..6c8079c97a 100644 --- a/include/hw/acpi/ipmi.h +++ b/include/hw/acpi/ipmi.h @@ -9,13 +9,8 @@ #ifndef HW_ACPI_IPMI_H #define HW_ACPI_IPMI_H -#include "hw/acpi/aml-build.h" +#include "hw/acpi/acpi_aml_interface.h" -/* - * Add ACPI IPMI entries for all registered IPMI devices whose parent - * bus matches the given bus. The resource is the ACPI resource that - * contains the IPMI device, this is required for the I2C CRS. - */ -void build_acpi_ipmi_devices(Aml *table, BusState *bus, const char *resource); +void build_ipmi_dev_aml(AcpiDevAmlIf *adev, Aml *scope); #endif /* HW_ACPI_IPMI_H */ diff --git a/include/hw/acpi/memory_hotplug.h b/include/hw/acpi/memory_hotplug.h index dfe9cf3fde..38841d7b06 100644 --- a/include/hw/acpi/memory_hotplug.h +++ b/include/hw/acpi/memory_hotplug.h @@ -1,6 +1,7 @@ #ifndef QEMU_HW_ACPI_MEMORY_HOTPLUG_H #define QEMU_HW_ACPI_MEMORY_HOTPLUG_H +#include "qapi/qapi-types-acpi.h" #include "hw/qdev-core.h" #include "hw/acpi/acpi.h" #include "hw/acpi/aml-build.h" diff --git a/include/hw/acpi/pc-hotplug.h b/include/hw/acpi/pc-hotplug.h index 31bc9191c3..8a654248e9 100644 --- a/include/hw/acpi/pc-hotplug.h +++ b/include/hw/acpi/pc-hotplug.h @@ -13,7 +13,7 @@ #define PC_HOTPLUG_H /* - * ONLY DEFINEs are permited in this file since it's shared + * ONLY DEFINEs are permitted in this file since it's shared * between C and ASL code. */ diff --git a/include/hw/acpi/pci.h b/include/hw/acpi/pci.h index b5deee0a9d..467a99461c 100644 --- a/include/hw/acpi/pci.h +++ b/include/hw/acpi/pci.h @@ -27,6 +27,7 @@ #define HW_ACPI_PCI_H #include "hw/acpi/bios-linker-loader.h" +#include "hw/acpi/acpi_aml_interface.h" typedef struct AcpiMcfgInfo { uint64_t base; @@ -36,4 +37,7 @@ typedef struct AcpiMcfgInfo { void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info, const char *oem_id, const char *oem_table_id); Aml *aml_pci_device_dsm(void); + +void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus); +void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope); #endif diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index 7e268c2c9c..ac21a95913 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -49,15 +49,16 @@ typedef struct AcpiPciHpState { uint32_t acpi_index; PCIBus *root; MemoryRegion io; - bool legacy_piix; uint16_t io_base; uint16_t io_len; + bool use_acpi_hotplug_bridge; + bool use_acpi_root_pci_hotplug; } AcpiPciHpState; void acpi_pcihp_init(Object *owner, AcpiPciHpState *, PCIBus *root, - MemoryRegion *address_space_io, bool bridges_enabled, - uint16_t io_base); + MemoryRegion *io, uint16_t io_base); +bool acpi_pcihp_is_hotpluggbale_bus(AcpiPciHpState *s, BusState *bus); void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, @@ -69,7 +70,9 @@ void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, Error **errp); /* Called on reset */ -void acpi_pcihp_reset(AcpiPciHpState *s, bool acpihp_root_off); +void acpi_pcihp_reset(AcpiPciHpState *s); + +void build_append_pcihp_slots(Aml *parent_scope, PCIBus *bus); extern const VMStateDescription vmstate_acpi_pcihp_pci_status; diff --git a/include/hw/acpi/piix4.h b/include/hw/acpi/piix4.h new file mode 100644 index 0000000000..eb1c122d80 --- /dev/null +++ b/include/hw/acpi/piix4.h @@ -0,0 +1,73 @@ +/* + * ACPI implementation + * + * Copyright (c) 2006 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 version 2.1 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 + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#ifndef HW_ACPI_PIIX4_H +#define HW_ACPI_PIIX4_H + +#include "hw/pci/pci_device.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/cpu_hotplug.h" +#include "hw/acpi/memory_hotplug.h" +#include "hw/acpi/pcihp.h" +#include "hw/i2c/pm_smbus.h" +#include "hw/isa/apm.h" + +#define TYPE_PIIX4_PM "PIIX4_PM" +OBJECT_DECLARE_SIMPLE_TYPE(PIIX4PMState, PIIX4_PM) + +struct PIIX4PMState { + /*< private >*/ + PCIDevice parent_obj; + /*< public >*/ + + MemoryRegion io; + uint32_t io_base; + + MemoryRegion io_gpe; + ACPIREGS ar; + + APMState apm; + + PMSMBus smb; + uint32_t smb_io_base; + + qemu_irq irq; + qemu_irq smi_irq; + bool smm_enabled; + bool smm_compat; + Notifier machine_ready; + Notifier powerdown_notifier; + + AcpiPciHpState acpi_pci_hotplug; + bool not_migrate_acpi_index; + + uint8_t disable_s3; + uint8_t disable_s4; + uint8_t s4_val; + + bool cpu_hotplug_legacy; + AcpiCpuHotplug gpe_cpu; + CPUHotplugState cpuhp_state; + + MemHotplugState acpi_memory_hotplug; +}; + +#endif diff --git a/include/hw/acpi/tpm.h b/include/hw/acpi/tpm.h index 559ba6906c..579c45f5ba 100644 --- a/include/hw/acpi/tpm.h +++ b/include/hw/acpi/tpm.h @@ -93,6 +93,7 @@ #define TPM_TIS_CAP_DATA_TRANSFER_64B (3 << 9) #define TPM_TIS_CAP_DATA_TRANSFER_LEGACY (0 << 9) #define TPM_TIS_CAP_BURST_COUNT_DYNAMIC (0 << 8) +#define TPM_TIS_CAP_BURST_COUNT_STATIC (1 << 8) #define TPM_TIS_CAP_INTERRUPT_LOW_LEVEL (1 << 4) /* support is mandatory */ #define TPM_TIS_CAPABILITIES_SUPPORTED1_3 \ (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \ @@ -209,6 +210,46 @@ REG32(CRB_DATA_BUFFER, 0x80) #define TPM_PPI_FUNC_ALLOWED_USR_NOT_REQ (4 << 0) #define TPM_PPI_FUNC_MASK (7 << 0) +/* TPM TIS I2C registers */ +#define TPM_I2C_REG_LOC_SEL 0x00 +#define TPM_I2C_REG_ACCESS 0x04 +#define TPM_I2C_REG_INT_ENABLE 0x08 +#define TPM_I2C_REG_INT_CAPABILITY 0x14 +#define TPM_I2C_REG_STS 0x18 +#define TPM_I2C_REG_DATA_FIFO 0x24 +#define TPM_I2C_REG_INTF_CAPABILITY 0x30 +#define TPM_I2C_REG_I2C_DEV_ADDRESS 0x38 +#define TPM_I2C_REG_DATA_CSUM_ENABLE 0x40 +#define TPM_I2C_REG_DATA_CSUM_GET 0x44 +#define TPM_I2C_REG_DID_VID 0x48 +#define TPM_I2C_REG_RID 0x4c +#define TPM_I2C_REG_UNKNOWN 0xff + +/* I2C specific interface capabilities */ +#define TPM_I2C_CAP_INTERFACE_TYPE (0x2 << 0) /* FIFO interface */ +#define TPM_I2C_CAP_INTERFACE_VER (0x0 << 4) /* TCG I2C intf 1.0 */ +#define TPM_I2C_CAP_TPM2_FAMILY (0x1 << 7) /* TPM 2.0 family. */ +#define TPM_I2C_CAP_DEV_ADDR_CHANGE (0x0 << 27) /* No dev addr chng */ +#define TPM_I2C_CAP_BURST_COUNT_STATIC (0x1 << 29) /* Burst count static */ +#define TPM_I2C_CAP_LOCALITY_CAP (0x1 << 25) /* 0-5 locality */ +#define TPM_I2C_CAP_BUS_SPEED (3 << 21) /* std and fast mode */ + +/* + * TPM_I2C_STS masks for read/writing bits from/to TIS + * TPM_STS mask for read bits 31:26 must be zero + */ +#define TPM_I2C_STS_READ_MASK 0x00ffffdd +#define TPM_I2C_STS_WRITE_MASK 0x03000062 + +/* Checksum enabled. */ +#define TPM_DATA_CSUM_ENABLED 0x1 + +/* + * TPM_I2C_INT_ENABLE mask. Linux kernel does not support + * interrupts hence setting it to 0. + */ +#define TPM_I2C_INT_ENABLE_MASK 0x0 + void tpm_build_ppi_acpi(TPMIf *tpm, Aml *dev); #endif /* CONFIG_TPM */ diff --git a/include/hw/acpi/vmgenid.h b/include/hw/acpi/vmgenid.h index dc8bb3433e..fb135d5bcb 100644 --- a/include/hw/acpi/vmgenid.h +++ b/include/hw/acpi/vmgenid.h @@ -13,7 +13,7 @@ #define VMGENID_FW_CFG_SIZE 4096 /* Occupy a page of memory */ #define VMGENID_GUID_OFFSET 40 /* allow space for - * OVMF SDT Header Probe Supressor + * OVMF SDT Header Probe Suppressor */ OBJECT_DECLARE_SIMPLE_TYPE(VmGenIdState, VMGENID) diff --git a/include/hw/adc/npcm7xx_adc.h b/include/hw/adc/npcm7xx_adc.h index 7d8442107a..93330a408d 100644 --- a/include/hw/adc/npcm7xx_adc.h +++ b/include/hw/adc/npcm7xx_adc.h @@ -42,7 +42,7 @@ * @iref: The internal reference voltage, initialized at launch time. * @rv: The calibrated output values of 0.5V and 1.5V for the ADC. */ -typedef struct { +struct NPCM7xxADCState { SysBusDevice parent; MemoryRegion iomem; @@ -60,10 +60,9 @@ typedef struct { uint32_t iref; uint16_t calibration_r_values[NPCM7XX_ADC_NUM_CALIB]; -} NPCM7xxADCState; +}; #define TYPE_NPCM7XX_ADC "npcm7xx-adc" -#define NPCM7XX_ADC(obj) \ - OBJECT_CHECK(NPCM7xxADCState, (obj), TYPE_NPCM7XX_ADC) +OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxADCState, NPCM7XX_ADC) #endif /* NPCM7XX_ADC_H */ diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h index a76dc7b84d..67a9a17b86 100644 --- a/include/hw/arm/allwinner-a10.h +++ b/include/hw/arm/allwinner-a10.h @@ -1,17 +1,19 @@ #ifndef HW_ARM_ALLWINNER_A10_H #define HW_ARM_ALLWINNER_A10_H -#include "qemu/error-report.h" -#include "hw/char/serial.h" -#include "hw/arm/boot.h" #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/ide/ahci-sysbus.h" #include "hw/usb/hcd-ohci.h" #include "hw/usb/hcd-ehci.h" #include "hw/rtc/allwinner-rtc.h" +#include "hw/misc/allwinner-a10-ccm.h" +#include "hw/misc/allwinner-a10-dramc.h" +#include "hw/i2c/allwinner-i2c.h" +#include "hw/watchdog/allwinner-wdt.h" +#include "sysemu/block-backend.h" #include "target/arm/cpu.h" #include "qom/object.h" @@ -30,15 +32,39 @@ struct AwA10State { /*< public >*/ ARMCPU cpu; + AwA10ClockCtlState ccm; + AwA10DramControllerState dramc; AwA10PITState timer; AwA10PICState intc; AwEmacState emac; AllwinnerAHCIState sata; AwSdHostState mmc0; + AWI2CState i2c0; AwRtcState rtc; + AwWdtState wdt; MemoryRegion sram_a; EHCISysBusState ehci[AW_A10_NUM_USB]; OHCISysBusState ohci[AW_A10_NUM_USB]; }; +/** + * Emulate Boot ROM firmware setup functionality. + * + * A real Allwinner A10 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 at offset 8 KiB from the given block device and writes it to + * the start of the first internal SRAM memory. + * + * @s: Allwinner A10 state object pointer + * @blk: Block backend device object pointer + */ +void allwinner_a10_bootrom_setup(AwA10State *s, BlockBackend *blk); + #endif diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index 63025fb27c..24ba4e1bf4 100644 --- a/include/hw/arm/allwinner-h3.h +++ b/include/hw/arm/allwinner-h3.h @@ -36,7 +36,6 @@ #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" @@ -47,6 +46,8 @@ #include "hw/sd/allwinner-sdhost.h" #include "hw/net/allwinner-sun8i-emac.h" #include "hw/rtc/allwinner-rtc.h" +#include "hw/i2c/allwinner-i2c.h" +#include "hw/watchdog/allwinner-wdt.h" #include "target/arm/cpu.h" #include "sysemu/block-backend.h" @@ -82,6 +83,9 @@ enum { AW_H3_DEV_UART2, AW_H3_DEV_UART3, AW_H3_DEV_EMAC, + AW_H3_DEV_TWI0, + AW_H3_DEV_TWI1, + AW_H3_DEV_TWI2, AW_H3_DEV_DRAMCOM, AW_H3_DEV_DRAMCTL, AW_H3_DEV_DRAMPHY, @@ -91,7 +95,9 @@ enum { AW_H3_DEV_GIC_VCPU, AW_H3_DEV_RTC, AW_H3_DEV_CPUCFG, - AW_H3_DEV_SDRAM + AW_H3_DEV_R_TWI, + AW_H3_DEV_SDRAM, + AW_H3_DEV_WDT }; /** Total number of CPU cores in the H3 SoC */ @@ -130,8 +136,13 @@ struct AwH3State { AwH3SysCtrlState sysctrl; AwSidState sid; AwSdHostState mmc0; + AWI2CState i2c0; + AWI2CState i2c1; + AWI2CState i2c2; + AWI2CState r_twi; AwSun8iEmacState emac; AwRtcState rtc; + AwWdtState wdt; GICState gic; MemoryRegion sram_a1; MemoryRegion sram_a2; diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h new file mode 100644 index 0000000000..614e74b7ed --- /dev/null +++ b/include/hw/arm/allwinner-r40.h @@ -0,0 +1,157 @@ +/* + * Allwinner R40/A40i/T3 System on Chip emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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_ARM_ALLWINNER_R40_H +#define HW_ARM_ALLWINNER_R40_H + +#include "qom/object.h" +#include "hw/timer/allwinner-a10-pit.h" +#include "hw/ide/ahci-sysbus.h" +#include "hw/intc/arm_gic.h" +#include "hw/sd/allwinner-sdhost.h" +#include "hw/misc/allwinner-r40-ccu.h" +#include "hw/misc/allwinner-r40-dramc.h" +#include "hw/misc/allwinner-sramc.h" +#include "hw/i2c/allwinner-i2c.h" +#include "hw/net/allwinner_emac.h" +#include "hw/net/allwinner-sun8i-emac.h" +#include "hw/usb/hcd-ohci.h" +#include "hw/usb/hcd-ehci.h" +#include "hw/watchdog/allwinner-wdt.h" +#include "target/arm/cpu.h" +#include "sysemu/block-backend.h" + +enum { + AW_R40_DEV_SRAM_A1, + AW_R40_DEV_SRAM_A2, + AW_R40_DEV_SRAM_A3, + AW_R40_DEV_SRAM_A4, + AW_R40_DEV_SRAMC, + AW_R40_DEV_EMAC, + AW_R40_DEV_MMC0, + AW_R40_DEV_MMC1, + AW_R40_DEV_MMC2, + AW_R40_DEV_MMC3, + AW_R40_DEV_AHCI, + AW_R40_DEV_EHCI1, + AW_R40_DEV_OHCI1, + AW_R40_DEV_EHCI2, + AW_R40_DEV_OHCI2, + AW_R40_DEV_CCU, + AW_R40_DEV_PIT, + AW_R40_DEV_WDT, + AW_R40_DEV_UART0, + AW_R40_DEV_UART1, + AW_R40_DEV_UART2, + AW_R40_DEV_UART3, + AW_R40_DEV_UART4, + AW_R40_DEV_UART5, + AW_R40_DEV_UART6, + AW_R40_DEV_UART7, + AW_R40_DEV_TWI0, + AW_R40_DEV_GMAC, + AW_R40_DEV_GIC_DIST, + AW_R40_DEV_GIC_CPU, + AW_R40_DEV_GIC_HYP, + AW_R40_DEV_GIC_VCPU, + AW_R40_DEV_SDRAM, + AW_R40_DEV_DRAMCOM, + AW_R40_DEV_DRAMCTL, + AW_R40_DEV_DRAMPHY, +}; + +#define AW_R40_NUM_CPUS (4) + +/** + * Allwinner R40 object model + * @{ + */ + +/** Object type for the Allwinner R40 SoC */ +#define TYPE_AW_R40 "allwinner-r40" + +/** Convert input object to Allwinner R40 state object */ +OBJECT_DECLARE_SIMPLE_TYPE(AwR40State, AW_R40) + +/** @} */ + +/** + * Allwinner R40 object + * + * This struct contains the state of all the devices + * which are currently emulated by the R40 SoC code. + */ +#define AW_R40_NUM_MMCS 4 +#define AW_R40_NUM_USB 2 +#define AW_R40_NUM_UARTS 8 + +struct AwR40State { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + /** Physical base address for start of RAM */ + hwaddr ram_addr; + + /** Total RAM size in megabytes */ + uint32_t ram_size; + + ARMCPU cpus[AW_R40_NUM_CPUS]; + const hwaddr *memmap; + AwSRAMCState sramc; + AwA10PITState timer; + AwWdtState wdt; + AllwinnerAHCIState sata; + AwSdHostState mmc[AW_R40_NUM_MMCS]; + EHCISysBusState ehci[AW_R40_NUM_USB]; + OHCISysBusState ohci[AW_R40_NUM_USB]; + AwR40ClockCtlState ccu; + AwR40DramCtlState dramc; + AWI2CState i2c0; + AwEmacState emac; + AwSun8iEmacState gmac; + GICState gic; + MemoryRegion sram_a1; + MemoryRegion sram_a2; + MemoryRegion sram_a3; + MemoryRegion sram_a4; +}; + +/** + * Emulate Boot ROM firmware setup functionality. + * + * A real Allwinner R40 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 R40 state object pointer + * @blk: Block backend device object pointer + * @unit: the mmc control's unit + */ +bool allwinner_r40_bootrom_setup(AwR40State *s, BlockBackend *blk, int unit); + +#endif /* HW_ARM_ALLWINNER_R40_H */ diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h index 9648e7a419..88b3b759c5 100644 --- a/include/hw/arm/armsse.h +++ b/include/hw/arm/armsse.h @@ -56,6 +56,9 @@ * (matching the hardware) is that for CPU0 in an IoTKit and CPU1 in an * SSE-200 both are present; CPU0 in an SSE-200 has neither. * Since the IoTKit has only one CPU, it does not have the CPU1_* properties. + * + QOM properties "CPU0_MPU_NS", "CPU0_MPU_S", "CPU1_MPU_NS" and "CPU1_MPU_S" + * which set the number of MPU regions on the CPUs. If there is only one + * CPU the CPU1 properties are not present. * + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts for CPU 0, * which are wired to its NVIC lines 32 .. n+32 * + Named GPIO inputs "EXP_CPU1_IRQ" 0..n are the expansion interrupts for @@ -155,12 +158,12 @@ struct ARMSSE { TZPPC apb_ppc[NUM_INTERNAL_PPCS]; TZMPC mpc[IOTS_NUM_MPC]; CMSDKAPBTimer timer[3]; - qemu_or_irq ppc_irq_orgate; + OrIRQState ppc_irq_orgate; SplitIRQ sec_resp_splitter; SplitIRQ ppc_irq_splitter[NUM_PPCS]; SplitIRQ mpc_irq_splitter[IOTS_NUM_EXP_MPC + IOTS_NUM_MPC]; - qemu_or_irq mpc_irq_orgate; - qemu_or_irq nmi_orgate; + OrIRQState mpc_irq_orgate; + OrIRQState nmi_orgate; SplitIRQ cpu_irq_splitter[NUM_SSE_IRQS]; @@ -221,6 +224,8 @@ struct ARMSSE { uint32_t exp_numirq; uint32_t sram_addr_width; uint32_t init_svtor; + uint32_t cpu_mpu_ns[SSE_MAX_CPUS]; + uint32_t cpu_mpu_s[SSE_MAX_CPUS]; bool cpu_fpu[SSE_MAX_CPUS]; bool cpu_dsp[SSE_MAX_CPUS]; }; diff --git a/include/hw/arm/armv7m.h b/include/hw/arm/armv7m.h index b7ba0ff409..5c057ab2ec 100644 --- a/include/hw/arm/armv7m.h +++ b/include/hw/arm/armv7m.h @@ -43,6 +43,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(ARMv7MState, ARMV7M) * a qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET). * + Property "cpu-type": CPU type to instantiate * + Property "num-irq": number of external IRQ lines + * + Property "num-prio-bits": number of priority bits in the NVIC * + Property "memory": MemoryRegion defining the physical address space * that CPU accesses see. (The NVIC, bitbanding and other CPU-internal * devices will be automatically layered on top of this view.) @@ -52,6 +53,12 @@ OBJECT_DECLARE_SIMPLE_TYPE(ARMv7MState, ARMV7M) * + Property "vfp": enable VFP (forwarded to CPU object) * + Property "dsp": enable DSP (forwarded to CPU object) * + Property "enable-bitband": expose bitbanded IO + * + Property "mpu-ns-regions": number of Non-Secure MPU regions (forwarded + * to CPU object pmsav7-dregion property; default is whatever the default + * for the CPU is) + * + Property "mpu-s-regions": number of Secure MPU regions (default is + * whatever the default for the CPU is; must currently be set to the same + * value as mpu-ns-regions if the CPU implements the Security Extension) * + Clock input "refclk" is the external reference clock for the systick timers * + Clock input "cpuclk" is the main CPU clock */ @@ -95,6 +102,8 @@ struct ARMv7MState { Object *idau; uint32_t init_svtor; uint32_t init_nsvtor; + uint32_t mpu_ns_regions; + uint32_t mpu_s_regions; bool enable_bitband; bool start_powered_off; bool vfp; diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 02a5a9ffcb..c60fac900a 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -34,24 +34,28 @@ #include "hw/usb/hcd-ehci.h" #include "qom/object.h" #include "hw/misc/aspeed_lpc.h" +#include "hw/misc/unimp.h" +#include "hw/misc/aspeed_peci.h" +#include "hw/fsi/aspeed_apb2opb.h" +#include "hw/char/serial.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 +#define ASPEED_UARTS_NUM 13 +#define ASPEED_JTAG_NUM 2 struct AspeedSoCState { - /*< private >*/ DeviceState parent; - /*< public >*/ - ARMCPU cpu[ASPEED_CPUS_NUM]; - A15MPPrivState a7mpcore; - ARMv7MState armv7m; + MemoryRegion *memory; MemoryRegion *dram_mr; + MemoryRegion dram_container; MemoryRegion sram; - AspeedVICState vic; + MemoryRegion spi_boot_container; + MemoryRegion spi_boot; AspeedRtcState rtc; AspeedTimerCtrlState timerctrl; AspeedI2CState i2c; @@ -64,6 +68,8 @@ struct AspeedSoCState { AspeedSMCState spi[ASPEED_SPIS_NUM]; EHCISysBusState ehci[ASPEED_EHCIS_NUM]; AspeedSBCState sbc; + MemoryRegion secsram; + UnimplementedDeviceState sbc_unimplemented; AspeedSDMCState sdmc; AspeedWDTState wdt[ASPEED_WDTS_NUM]; FTGMAC100State ftgmac100[ASPEED_MACS_NUM]; @@ -73,34 +79,80 @@ struct AspeedSoCState { AspeedSDHCIState sdhci; AspeedSDHCIState emmc; AspeedLPCState lpc; - uint32_t uart_default; + AspeedPECIState peci; + SerialMM uart[ASPEED_UARTS_NUM]; Clock *sysclk; + UnimplementedDeviceState iomem; + UnimplementedDeviceState video; + UnimplementedDeviceState emmc_boot_controller; + UnimplementedDeviceState dpmcu; + UnimplementedDeviceState pwm; + UnimplementedDeviceState espi; + UnimplementedDeviceState udc; + UnimplementedDeviceState sgpiom; + UnimplementedDeviceState jtag[ASPEED_JTAG_NUM]; + AspeedAPB2OPBState fsi[2]; }; #define TYPE_ASPEED_SOC "aspeed-soc" OBJECT_DECLARE_TYPE(AspeedSoCState, AspeedSoCClass, ASPEED_SOC) +struct Aspeed2400SoCState { + AspeedSoCState parent; + + ARMCPU cpu[ASPEED_CPUS_NUM]; + AspeedVICState vic; +}; + +#define TYPE_ASPEED2400_SOC "aspeed2400-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed2400SoCState, ASPEED2400_SOC) + +struct Aspeed2600SoCState { + AspeedSoCState parent; + + A15MPPrivState a7mpcore; + ARMCPU cpu[ASPEED_CPUS_NUM]; /* XXX belong to a7mpcore */ +}; + +#define TYPE_ASPEED2600_SOC "aspeed2600-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed2600SoCState, ASPEED2600_SOC) + +struct Aspeed10x0SoCState { + AspeedSoCState parent; + + ARMv7MState armv7m; +}; + +#define TYPE_ASPEED10X0_SOC "aspeed10x0-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed10x0SoCState, ASPEED10X0_SOC) + struct AspeedSoCClass { DeviceClass parent_class; const char *name; - const char *cpu_type; + /** valid_cpu_types: NULL terminated array of a single CPU type. */ + const char * const *valid_cpu_types; uint32_t silicon_rev; uint64_t sram_size; + uint64_t secsram_size; int spis_num; int ehcis_num; int wdts_num; int macs_num; int uarts_num; + int uarts_base; const int *irqmap; const hwaddr *memmap; uint32_t num_cpus; qemu_irq (*get_irq)(AspeedSoCState *s, int dev); }; +const char *aspeed_soc_cpu_type(AspeedSoCClass *sc); enum { + ASPEED_DEV_SPI_BOOT, ASPEED_DEV_IOMEM, + ASPEED_DEV_UART0, ASPEED_DEV_UART1, ASPEED_DEV_UART2, ASPEED_DEV_UART3, @@ -125,6 +177,7 @@ enum { ASPEED_DEV_SCU, ASPEED_DEV_ADC, ASPEED_DEV_SBC, + ASPEED_DEV_SECSRAM, ASPEED_DEV_EMMC_BC, ASPEED_DEV_VIDEO, ASPEED_DEV_SRAM, @@ -145,6 +198,7 @@ enum { ASPEED_DEV_LPC, ASPEED_DEV_IBT, ASPEED_DEV_I2C, + ASPEED_DEV_PECI, ASPEED_DEV_ETH1, ASPEED_DEV_ETH2, ASPEED_DEV_ETH3, @@ -161,9 +215,39 @@ enum { ASPEED_DEV_DPMCU, ASPEED_DEV_DP, ASPEED_DEV_I3C, + ASPEED_DEV_ESPI, + ASPEED_DEV_UDC, + ASPEED_DEV_SGPIOM, + ASPEED_DEV_JTAG0, + ASPEED_DEV_JTAG1, + ASPEED_DEV_FSI1, + ASPEED_DEV_FSI2, }; qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev); -void aspeed_soc_uart_init(AspeedSoCState *s); +bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp); +void aspeed_soc_uart_set_chr(AspeedSoCState *s, int dev, Chardev *chr); +bool aspeed_soc_dram_init(AspeedSoCState *s, Error **errp); +void aspeed_mmio_map(AspeedSoCState *s, SysBusDevice *dev, int n, hwaddr addr); +void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, + const char *name, hwaddr addr, + uint64_t size); +void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, + unsigned int count, int unit0); + +static inline int aspeed_uart_index(int uart_dev) +{ + return uart_dev - ASPEED_DEV_UART0; +} + +static inline int aspeed_uart_first(AspeedSoCClass *sc) +{ + return aspeed_uart_index(sc->uarts_base); +} + +static inline int aspeed_uart_last(AspeedSoCClass *sc) +{ + return aspeed_uart_first(sc) + sc->uarts_num - 1; +} #endif /* ASPEED_SOC_H */ diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h index d864879421..636203baa5 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -17,6 +17,7 @@ #include "hw/char/bcm2835_aux.h" #include "hw/display/bcm2835_fb.h" #include "hw/dma/bcm2835_dma.h" +#include "hw/or-irq.h" #include "hw/intc/bcm2835_ic.h" #include "hw/misc/bcm2835_property.h" #include "hw/misc/bcm2835_rng.h" @@ -30,13 +31,18 @@ #include "hw/gpio/bcm2835_gpio.h" #include "hw/timer/bcm2835_systmr.h" #include "hw/usb/hcd-dwc2.h" +#include "hw/ssi/bcm2835_spi.h" +#include "hw/i2c/bcm2835_i2c.h" #include "hw/misc/unimp.h" #include "qom/object.h" +#define TYPE_BCM_SOC_PERIPHERALS_BASE "bcm-soc-peripherals-base" +OBJECT_DECLARE_TYPE(BCMSocPeripheralBaseState, BCMSocPeripheralBaseClass, + BCM_SOC_PERIPHERALS_BASE) #define TYPE_BCM2835_PERIPHERALS "bcm2835-peripherals" OBJECT_DECLARE_SIMPLE_TYPE(BCM2835PeripheralState, BCM2835_PERIPHERALS) -struct BCM2835PeripheralState { +struct BCMSocPeripheralBaseState { /*< private >*/ SysBusDevice parent_obj; /*< public >*/ @@ -55,17 +61,16 @@ struct BCM2835PeripheralState { BCM2835AuxState aux; BCM2835FBState fb; BCM2835DMAState dma; + OrIRQState orgated_dma_irq; BCM2835ICState ic; BCM2835PropertyState property; - BCM2835RngState rng; BCM2835MboxState mboxes; SDHCIState sdhci; BCM2835SDHostState sdhost; - BCM2835GpioState gpio; - Bcm2835ThermalState thermal; UnimplementedDeviceState i2s; - UnimplementedDeviceState spi[1]; - UnimplementedDeviceState i2c[3]; + BCM2835SPIState spi[1]; + BCM2835I2CState i2c[3]; + OrIRQState orgated_i2c_irq; UnimplementedDeviceState otp; UnimplementedDeviceState dbus; UnimplementedDeviceState ave0; @@ -76,4 +81,25 @@ struct BCM2835PeripheralState { UnimplementedDeviceState sdramc; }; +struct BCMSocPeripheralBaseClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + uint64_t peri_size; /* Peripheral range size */ +}; + +struct BCM2835PeripheralState { + /*< private >*/ + BCMSocPeripheralBaseState parent_obj; + /*< public >*/ + BCM2835RngState rng; + Bcm2835ThermalState thermal; + BCM2835GpioState gpio; +}; + +void create_unimp(BCMSocPeripheralBaseState *ps, + UnimplementedDeviceState *uds, + const char *name, hwaddr ofs, hwaddr size); +void bcm_soc_peripherals_common_realize(DeviceState *dev, Error **errp); + #endif /* BCM2835_PERIPHERALS_H */ diff --git a/include/hw/arm/bcm2836.h b/include/hw/arm/bcm2836.h index 6f90cabfa3..918fb3bf14 100644 --- a/include/hw/arm/bcm2836.h +++ b/include/hw/arm/bcm2836.h @@ -17,8 +17,10 @@ #include "target/arm/cpu.h" #include "qom/object.h" +#define TYPE_BCM283X_BASE "bcm283x-base" +OBJECT_DECLARE_TYPE(BCM283XBaseState, BCM283XBaseClass, BCM283X_BASE) #define TYPE_BCM283X "bcm283x" -OBJECT_DECLARE_TYPE(BCM283XState, BCM283XClass, BCM283X) +OBJECT_DECLARE_SIMPLE_TYPE(BCM283XState, BCM283X) #define BCM283X_NCPUS 4 @@ -30,7 +32,7 @@ OBJECT_DECLARE_TYPE(BCM283XState, BCM283XClass, BCM283X) #define TYPE_BCM2836 "bcm2836" #define TYPE_BCM2837 "bcm2837" -struct BCM283XState { +struct BCM283XBaseState { /*< private >*/ DeviceState parent_obj; /*< public >*/ @@ -41,7 +43,28 @@ struct BCM283XState { ARMCPU core; } cpu[BCM283X_NCPUS]; BCM2836ControlState control; +}; + +struct BCM283XBaseClass { + /*< private >*/ + DeviceClass parent_class; + /*< public >*/ + const char *name; + const char *cpu_type; + unsigned core_count; + hwaddr peri_base; /* Peripheral base address seen by the CPU */ + hwaddr ctrl_base; /* Interrupt controller and mailboxes etc. */ + int clusterid; +}; + +struct BCM283XState { + /*< private >*/ + BCM283XBaseState parent_obj; + /*< public >*/ BCM2835PeripheralState peripherals; }; +bool bcm283x_common_realize(DeviceState *dev, BCMSocPeripheralBaseState *ps, + Error **errp); + #endif /* BCM2836_H */ diff --git a/include/hw/arm/bcm2838.h b/include/hw/arm/bcm2838.h new file mode 100644 index 0000000000..e53c7bedf9 --- /dev/null +++ b/include/hw/arm/bcm2838.h @@ -0,0 +1,31 @@ +/* + * BCM2838 SoC emulation + * + * Copyright (C) 2022 Ovchinnikov Vitalii + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BCM2838_H +#define BCM2838_H + +#include "hw/arm/bcm2836.h" +#include "hw/intc/arm_gic.h" +#include "hw/arm/bcm2838_peripherals.h" + +#define BCM2838_PERI_LOW_BASE 0xfc000000 +#define BCM2838_GIC_BASE 0x40000 + +#define TYPE_BCM2838 "bcm2838" + +OBJECT_DECLARE_TYPE(BCM2838State, BCM2838Class, BCM2838) + +struct BCM2838State { + /*< private >*/ + BCM283XBaseState parent_obj; + /*< public >*/ + BCM2838PeripheralState peripherals; + GICState gic; +}; + +#endif /* BCM2838_H */ diff --git a/include/hw/arm/bcm2838_peripherals.h b/include/hw/arm/bcm2838_peripherals.h new file mode 100644 index 0000000000..7ee1bd066f --- /dev/null +++ b/include/hw/arm/bcm2838_peripherals.h @@ -0,0 +1,84 @@ +/* + * BCM2838 peripherals emulation + * + * Copyright (C) 2022 Ovchinnikov Vitalii + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BCM2838_PERIPHERALS_H +#define BCM2838_PERIPHERALS_H + +#include "hw/arm/bcm2835_peripherals.h" +#include "hw/sd/sdhci.h" +#include "hw/gpio/bcm2838_gpio.h" + +/* SPI */ +#define GIC_SPI_INTERRUPT_MBOX 33 +#define GIC_SPI_INTERRUPT_MPHI 40 +#define GIC_SPI_INTERRUPT_DWC2 73 +#define GIC_SPI_INTERRUPT_DMA_0 80 +#define GIC_SPI_INTERRUPT_DMA_6 86 +#define GIC_SPI_INTERRUPT_DMA_7_8 87 +#define GIC_SPI_INTERRUPT_DMA_9_10 88 +#define GIC_SPI_INTERRUPT_AUX_UART1 93 +#define GIC_SPI_INTERRUPT_SDHOST 120 +#define GIC_SPI_INTERRUPT_UART0 121 +#define GIC_SPI_INTERRUPT_RNG200 125 +#define GIC_SPI_INTERRUPT_EMMC_EMMC2 126 +#define GIC_SPI_INTERRUPT_PCI_INT_A 143 +#define GIC_SPI_INTERRUPT_GENET_A 157 +#define GIC_SPI_INTERRUPT_GENET_B 158 + + +/* GPU (legacy) DMA interrupts */ +#define GPU_INTERRUPT_DMA0 16 +#define GPU_INTERRUPT_DMA1 17 +#define GPU_INTERRUPT_DMA2 18 +#define GPU_INTERRUPT_DMA3 19 +#define GPU_INTERRUPT_DMA4 20 +#define GPU_INTERRUPT_DMA5 21 +#define GPU_INTERRUPT_DMA6 22 +#define GPU_INTERRUPT_DMA7_8 23 +#define GPU_INTERRUPT_DMA9_10 24 +#define GPU_INTERRUPT_DMA11 25 +#define GPU_INTERRUPT_DMA12 26 +#define GPU_INTERRUPT_DMA13 27 +#define GPU_INTERRUPT_DMA14 28 +#define GPU_INTERRUPT_DMA15 31 + +#define BCM2838_MPHI_OFFSET 0xb200 +#define BCM2838_MPHI_SIZE 0x200 + +#define TYPE_BCM2838_PERIPHERALS "bcm2838-peripherals" +OBJECT_DECLARE_TYPE(BCM2838PeripheralState, BCM2838PeripheralClass, + BCM2838_PERIPHERALS) + +struct BCM2838PeripheralState { + /*< private >*/ + BCMSocPeripheralBaseState parent_obj; + + /*< public >*/ + MemoryRegion peri_low_mr; + MemoryRegion peri_low_mr_alias; + MemoryRegion mphi_mr_alias; + + SDHCIState emmc2; + BCM2838GpioState gpio; + + OrIRQState mmc_irq_orgate; + OrIRQState dma_7_8_irq_orgate; + OrIRQState dma_9_10_irq_orgate; + + UnimplementedDeviceState asb; + UnimplementedDeviceState clkisp; +}; + +struct BCM2838PeripheralClass { + /*< private >*/ + BCMSocPeripheralBaseClass parent_class; + /*< public >*/ + uint64_t peri_low_size; /* Peripheral lower range size */ +}; + +#endif /* BCM2838_PERIPHERALS_H */ diff --git a/include/hw/arm/boot.h b/include/hw/arm/boot.h index c7ebae156e..80c492d742 100644 --- a/include/hw/arm/boot.h +++ b/include/hw/arm/boot.h @@ -25,13 +25,16 @@ typedef enum { * armv7m_load_kernel: * @cpu: CPU * @kernel_filename: file to load + * @mem_base: base address to load image at (should be where the + * CPU expects to find its vector table on reset) * @mem_size: mem_size: maximum image size to load * * Load the guest image for an ARMv7M system. This must be called by * any ARMv7M board. (This is necessary to ensure that the CPU resets * correctly on system reset, as well as for kernel loading.) */ -void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size); +void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, + hwaddr mem_base, int mem_size); /* arm_boot.c */ struct arm_boot_info { @@ -180,4 +183,53 @@ void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu, const struct arm_boot_info *info, hwaddr mvbar_addr); +typedef enum { + FIXUP_NONE = 0, /* do nothing */ + FIXUP_TERMINATOR, /* end of insns */ + FIXUP_BOARDID, /* overwrite with board ID number */ + FIXUP_BOARD_SETUP, /* overwrite with board specific setup code address */ + FIXUP_ARGPTR_LO, /* overwrite with pointer to kernel args */ + FIXUP_ARGPTR_HI, /* overwrite with pointer to kernel args (high half) */ + FIXUP_ENTRYPOINT_LO, /* overwrite with kernel entry point */ + FIXUP_ENTRYPOINT_HI, /* overwrite with kernel entry point (high half) */ + FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */ + FIXUP_BOOTREG, /* overwrite with boot register address */ + FIXUP_DSB, /* overwrite with correct DSB insn for cpu */ + FIXUP_MAX, +} FixupType; + +typedef struct ARMInsnFixup { + uint32_t insn; + FixupType fixup; +} ARMInsnFixup; + +/** + * arm_write_bootloader - write a bootloader to guest memory + * @name: name of the bootloader blob + * @as: AddressSpace to write the bootloader + * @addr: guest address to write it + * @insns: the blob to be loaded + * @fixupcontext: context to be used for any fixups in @insns + * + * Write a bootloader to guest memory at address @addr in the address + * space @as. @name is the name to use for the resulting ROM blob, so + * it should be unique in the system and reasonably identifiable for debugging. + * + * @insns must be an array of ARMInsnFixup structs, each of which has + * one 32-bit value to be written to the guest memory, and a fixup to be + * applied to the value. FIXUP_NONE (do nothing) is value 0, so effectively + * the fixup is optional when writing a struct initializer. + * The final entry in the array must be { 0, FIXUP_TERMINATOR }. + * + * All other supported fixup types have the semantics "ignore insn + * and instead use the value from the array element @fixupcontext[fixup]". + * The caller should therefore provide @fixupcontext as an array of + * size FIXUP_MAX whose elements have been initialized for at least + * the entries that @insns refers to. + */ +void arm_write_bootloader(const char *name, + AddressSpace *as, hwaddr addr, + const ARMInsnFixup *insns, + const uint32_t *fixupcontext); + #endif /* HW_ARM_BOOT_H */ diff --git a/include/hw/arm/bsa.h b/include/hw/arm/bsa.h new file mode 100644 index 0000000000..8eaab603c0 --- /dev/null +++ b/include/hw/arm/bsa.h @@ -0,0 +1,35 @@ +/* + * Common definitions for Arm Base System Architecture (BSA) platforms. + * + * Copyright (c) 2015 Linaro Limited + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * + * 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_ARM_BSA_H +#define QEMU_ARM_BSA_H + +/* These are architectural INTID values */ +#define VIRTUAL_PMU_IRQ 23 +#define ARCH_GIC_MAINT_IRQ 25 +#define ARCH_TIMER_NS_EL2_IRQ 26 +#define ARCH_TIMER_VIRT_IRQ 27 +#define ARCH_TIMER_NS_EL2_VIRT_IRQ 28 +#define ARCH_TIMER_S_EL1_IRQ 29 +#define ARCH_TIMER_NS_EL1_IRQ 30 + +#define INTID_TO_PPI(irq) ((irq) - 16) + +#endif /* QEMU_ARM_BSA_H */ diff --git a/include/hw/arm/exynos4210.h b/include/hw/arm/exynos4210.h index 97353f1c02..d33fe38586 100644 --- a/include/hw/arm/exynos4210.h +++ b/include/hw/arm/exynos4210.h @@ -30,7 +30,7 @@ #include "hw/intc/exynos4210_gic.h" #include "hw/intc/exynos4210_combiner.h" #include "hw/core/split-irq.h" -#include "target/arm/cpu-qom.h" +#include "hw/arm/boot.h" #include "qom/object.h" #define EXYNOS4210_NCPUS 2 @@ -96,8 +96,8 @@ struct Exynos4210State { MemoryRegion boot_secondary; MemoryRegion bootreg_mem; I2CBus *i2c_if[EXYNOS4210_I2C_NUMBER]; - qemu_or_irq pl330_irq_orgate[EXYNOS4210_NUM_DMA]; - qemu_or_irq cpu_irq_orgate[EXYNOS4210_NCPUS]; + OrIRQState pl330_irq_orgate[EXYNOS4210_NUM_DMA]; + OrIRQState cpu_irq_orgate[EXYNOS4210_NCPUS]; A9MPPrivState a9mpcore; Exynos4210GicState ext_gic; Exynos4210CombinerState int_combiner; diff --git a/include/hw/arm/fsl-imx25.h b/include/hw/arm/fsl-imx25.h index 1b1086e945..df2f83980f 100644 --- a/include/hw/arm/fsl-imx25.h +++ b/include/hw/arm/fsl-imx25.h @@ -17,7 +17,6 @@ #ifndef FSL_IMX25_H #define FSL_IMX25_H -#include "hw/arm/boot.h" #include "hw/intc/imx_avic.h" #include "hw/misc/imx25_ccm.h" #include "hw/char/imx_serial.h" diff --git a/include/hw/arm/fsl-imx31.h b/include/hw/arm/fsl-imx31.h index c116a73e0b..40c593a5cf 100644 --- a/include/hw/arm/fsl-imx31.h +++ b/include/hw/arm/fsl-imx31.h @@ -17,7 +17,6 @@ #ifndef FSL_IMX31_H #define FSL_IMX31_H -#include "hw/arm/boot.h" #include "hw/intc/imx_avic.h" #include "hw/misc/imx31_ccm.h" #include "hw/char/imx_serial.h" diff --git a/include/hw/arm/fsl-imx6.h b/include/hw/arm/fsl-imx6.h index 83291457cf..61c593ffd2 100644 --- a/include/hw/arm/fsl-imx6.h +++ b/include/hw/arm/fsl-imx6.h @@ -17,10 +17,10 @@ #ifndef FSL_IMX6_H #define FSL_IMX6_H -#include "hw/arm/boot.h" #include "hw/cpu/a9mpcore.h" #include "hw/misc/imx6_ccm.h" #include "hw/misc/imx6_src.h" +#include "hw/misc/imx7_snvs.h" #include "hw/watchdog/wdt_imx2.h" #include "hw/char/imx_serial.h" #include "hw/timer/imx_gpt.h" @@ -32,6 +32,7 @@ #include "hw/net/imx_fec.h" #include "hw/usb/chipidea.h" #include "hw/usb/imx-usb-phy.h" +#include "hw/pci-host/designware.h" #include "exec/memory.h" #include "cpu.h" #include "qom/object.h" @@ -55,26 +56,28 @@ struct FslIMX6State { DeviceState parent_obj; /*< public >*/ - ARMCPU cpu[FSL_IMX6_NUM_CPUS]; - A9MPPrivState a9mpcore; - IMX6CCMState ccm; - IMX6SRCState src; - IMXSerialState uart[FSL_IMX6_NUM_UARTS]; - IMXGPTState gpt; - IMXEPITState epit[FSL_IMX6_NUM_EPITS]; - IMXI2CState i2c[FSL_IMX6_NUM_I2CS]; - 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; - MemoryRegion ocram; - MemoryRegion ocram_alias; - uint32_t phy_num; + ARMCPU cpu[FSL_IMX6_NUM_CPUS]; + A9MPPrivState a9mpcore; + IMX6CCMState ccm; + IMX6SRCState src; + IMX7SNVSState snvs; + IMXSerialState uart[FSL_IMX6_NUM_UARTS]; + IMXGPTState gpt; + IMXEPITState epit[FSL_IMX6_NUM_EPITS]; + IMXI2CState i2c[FSL_IMX6_NUM_I2CS]; + 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; + DesignwarePCIEHost pcie; + MemoryRegion rom; + MemoryRegion caam; + MemoryRegion ocram; + MemoryRegion ocram_alias; + uint32_t phy_num; }; diff --git a/include/hw/arm/fsl-imx6ul.h b/include/hw/arm/fsl-imx6ul.h index 7812e516a5..8277b0e8b2 100644 --- a/include/hw/arm/fsl-imx6ul.h +++ b/include/hw/arm/fsl-imx6ul.h @@ -17,12 +17,10 @@ #ifndef FSL_IMX6UL_H #define FSL_IMX6UL_H -#include "hw/arm/boot.h" #include "hw/cpu/a15mpcore.h" #include "hw/misc/imx6ul_ccm.h" #include "hw/misc/imx6_src.h" #include "hw/misc/imx7_snvs.h" -#include "hw/misc/imx7_gpr.h" #include "hw/intc/imx_gpcv2.h" #include "hw/watchdog/wdt_imx2.h" #include "hw/gpio/imx_gpio.h" @@ -30,7 +28,6 @@ #include "hw/timer/imx_gpt.h" #include "hw/timer/imx_epit.h" #include "hw/i2c/imx_i2c.h" -#include "hw/gpio/imx_gpio.h" #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" #include "hw/net/imx_fec.h" @@ -39,6 +36,7 @@ #include "exec/memory.h" #include "cpu.h" #include "qom/object.h" +#include "qemu/units.h" #define TYPE_FSL_IMX6UL "fsl-imx6ul" OBJECT_DECLARE_SIMPLE_TYPE(FslIMX6ULState, FSL_IMX6UL) @@ -59,6 +57,9 @@ enum FslIMX6ULConfiguration { FSL_IMX6UL_NUM_ADCS = 2, FSL_IMX6UL_NUM_USB_PHYS = 2, FSL_IMX6UL_NUM_USBS = 2, + FSL_IMX6UL_NUM_SAIS = 3, + FSL_IMX6UL_NUM_CANS = 2, + FSL_IMX6UL_NUM_PWMS = 8, }; struct FslIMX6ULState { @@ -75,7 +76,6 @@ struct FslIMX6ULState { IMX6SRCState src; IMX7SNVSState snvs; IMXGPCv2State gpcv2; - IMX7GPRState gpr; IMXSPIState spi[FSL_IMX6UL_NUM_ECSPIS]; IMXI2CState i2c[FSL_IMX6UL_NUM_I2CS]; IMXSerialState uart[FSL_IMX6UL_NUM_UARTS]; @@ -90,123 +90,234 @@ struct FslIMX6ULState { MemoryRegion ocram_alias; uint32_t phy_num[FSL_IMX6UL_NUM_ETHS]; + bool phy_connected[FSL_IMX6UL_NUM_ETHS]; }; enum FslIMX6ULMemoryMap { FSL_IMX6UL_MMDC_ADDR = 0x80000000, - FSL_IMX6UL_MMDC_SIZE = 2 * 1024 * 1024 * 1024UL, + FSL_IMX6UL_MMDC_SIZE = (2 * GiB), FSL_IMX6UL_QSPI1_MEM_ADDR = 0x60000000, - FSL_IMX6UL_EIM_ALIAS_ADDR = 0x58000000, - FSL_IMX6UL_EIM_CS_ADDR = 0x50000000, - FSL_IMX6UL_AES_ENCRYPT_ADDR = 0x10000000, - FSL_IMX6UL_QSPI1_RX_ADDR = 0x0C000000, + FSL_IMX6UL_QSPI1_MEM_SIZE = (256 * MiB), - /* AIPS-2 */ + FSL_IMX6UL_EIM_ALIAS_ADDR = 0x58000000, + FSL_IMX6UL_EIM_ALIAS_SIZE = (128 * MiB), + + FSL_IMX6UL_EIM_CS_ADDR = 0x50000000, + FSL_IMX6UL_EIM_CS_SIZE = (128 * MiB), + + FSL_IMX6UL_AES_ENCRYPT_ADDR = 0x10000000, + FSL_IMX6UL_AES_ENCRYPT_SIZE = (1 * MiB), + + FSL_IMX6UL_QSPI1_RX_ADDR = 0x0C000000, + FSL_IMX6UL_QSPI1_RX_SIZE = (32 * MiB), + + /* AIPS-2 Begin */ FSL_IMX6UL_UART6_ADDR = 0x021FC000, + FSL_IMX6UL_I2C4_ADDR = 0x021F8000, + FSL_IMX6UL_UART5_ADDR = 0x021F4000, FSL_IMX6UL_UART4_ADDR = 0x021F0000, FSL_IMX6UL_UART3_ADDR = 0x021EC000, FSL_IMX6UL_UART2_ADDR = 0x021E8000, + FSL_IMX6UL_WDOG3_ADDR = 0x021E4000, + FSL_IMX6UL_QSPI_ADDR = 0x021E0000, + FSL_IMX6UL_QSPI_SIZE = 0x500, + FSL_IMX6UL_SYS_CNT_CTRL_ADDR = 0x021DC000, + FSL_IMX6UL_SYS_CNT_CTRL_SIZE = (16 * KiB), + FSL_IMX6UL_SYS_CNT_CMP_ADDR = 0x021D8000, + FSL_IMX6UL_SYS_CNT_CMP_SIZE = (16 * KiB), + FSL_IMX6UL_SYS_CNT_RD_ADDR = 0x021D4000, + FSL_IMX6UL_SYS_CNT_RD_SIZE = (16 * KiB), + FSL_IMX6UL_TZASC_ADDR = 0x021D0000, + FSL_IMX6UL_TZASC_SIZE = (16 * KiB), + FSL_IMX6UL_PXP_ADDR = 0x021CC000, + FSL_IMX6UL_PXP_SIZE = (16 * KiB), + FSL_IMX6UL_LCDIF_ADDR = 0x021C8000, + FSL_IMX6UL_LCDIF_SIZE = 0x100, + FSL_IMX6UL_CSI_ADDR = 0x021C4000, + FSL_IMX6UL_CSI_SIZE = 0x100, + FSL_IMX6UL_CSU_ADDR = 0x021C0000, + FSL_IMX6UL_CSU_SIZE = (16 * KiB), + FSL_IMX6UL_OCOTP_CTRL_ADDR = 0x021BC000, + FSL_IMX6UL_OCOTP_CTRL_SIZE = (4 * KiB), + FSL_IMX6UL_EIM_ADDR = 0x021B8000, + FSL_IMX6UL_EIM_SIZE = 0x100, + FSL_IMX6UL_SIM2_ADDR = 0x021B4000, + FSL_IMX6UL_MMDC_CFG_ADDR = 0x021B0000, + FSL_IMX6UL_MMDC_CFG_SIZE = (4 * KiB), + FSL_IMX6UL_ROMCP_ADDR = 0x021AC000, + FSL_IMX6UL_ROMCP_SIZE = 0x300, + FSL_IMX6UL_I2C3_ADDR = 0x021A8000, FSL_IMX6UL_I2C2_ADDR = 0x021A4000, FSL_IMX6UL_I2C1_ADDR = 0x021A0000, + FSL_IMX6UL_ADC2_ADDR = 0x0219C000, FSL_IMX6UL_ADC1_ADDR = 0x02198000, + FSL_IMX6UL_ADCn_SIZE = 0x100, + FSL_IMX6UL_USDHC2_ADDR = 0x02194000, FSL_IMX6UL_USDHC1_ADDR = 0x02190000, - FSL_IMX6UL_SIM1_ADDR = 0x0218C000, - FSL_IMX6UL_ENET1_ADDR = 0x02188000, - FSL_IMX6UL_USBO2_USBMISC_ADDR = 0x02184800, - FSL_IMX6UL_USBO2_USB_ADDR = 0x02184000, - FSL_IMX6UL_USBO2_PL301_ADDR = 0x02180000, - FSL_IMX6UL_AIPS2_CFG_ADDR = 0x0217C000, - FSL_IMX6UL_CAAM_ADDR = 0x02140000, - FSL_IMX6UL_A7MPCORE_DAP_ADDR = 0x02100000, - /* AIPS-1 */ + FSL_IMX6UL_SIM1_ADDR = 0x0218C000, + FSL_IMX6UL_SIMn_SIZE = (16 * KiB), + + FSL_IMX6UL_ENET1_ADDR = 0x02188000, + + FSL_IMX6UL_USBO2_USBMISC_ADDR = 0x02184800, + FSL_IMX6UL_USBO2_USBMISC_SIZE = 0x200, + + FSL_IMX6UL_USBO2_USB1_ADDR = 0x02184000, + FSL_IMX6UL_USBO2_USB2_ADDR = 0x02184200, + + FSL_IMX6UL_USBO2_PL301_ADDR = 0x02180000, + FSL_IMX6UL_USBO2_PL301_SIZE = (16 * KiB), + + FSL_IMX6UL_AIPS2_CFG_ADDR = 0x0217C000, + FSL_IMX6UL_AIPS2_CFG_SIZE = 0x100, + + FSL_IMX6UL_CAAM_ADDR = 0x02140000, + FSL_IMX6UL_CAAM_SIZE = (16 * KiB), + + FSL_IMX6UL_A7MPCORE_DAP_ADDR = 0x02100000, + FSL_IMX6UL_A7MPCORE_DAP_SIZE = (4 * KiB), + /* AIPS-2 End */ + + /* AIPS-1 Begin */ FSL_IMX6UL_PWM8_ADDR = 0x020FC000, FSL_IMX6UL_PWM7_ADDR = 0x020F8000, FSL_IMX6UL_PWM6_ADDR = 0x020F4000, FSL_IMX6UL_PWM5_ADDR = 0x020F0000, + FSL_IMX6UL_SDMA_ADDR = 0x020EC000, + FSL_IMX6UL_SDMA_SIZE = 0x300, + FSL_IMX6UL_GPT2_ADDR = 0x020E8000, + FSL_IMX6UL_IOMUXC_GPR_ADDR = 0x020E4000, + FSL_IMX6UL_IOMUXC_GPR_SIZE = 0x40, + FSL_IMX6UL_IOMUXC_ADDR = 0x020E0000, + FSL_IMX6UL_IOMUXC_SIZE = 0x700, + FSL_IMX6UL_GPC_ADDR = 0x020DC000, + FSL_IMX6UL_SRC_ADDR = 0x020D8000, + 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_ANALOG_SIZE = 0x300, + FSL_IMX6UL_CCM_ADDR = 0x020C4000, + FSL_IMX6UL_WDOG2_ADDR = 0x020C0000, FSL_IMX6UL_WDOG1_ADDR = 0x020BC000, + FSL_IMX6UL_KPP_ADDR = 0x020B8000, + FSL_IMX6UL_KPP_SIZE = 0x10, + FSL_IMX6UL_ENET2_ADDR = 0x020B4000, + FSL_IMX6UL_SNVS_LP_ADDR = 0x020B0000, + FSL_IMX6UL_SNVS_LP_SIZE = (16 * KiB), + FSL_IMX6UL_GPIO5_ADDR = 0x020AC000, FSL_IMX6UL_GPIO4_ADDR = 0x020A8000, FSL_IMX6UL_GPIO3_ADDR = 0x020A4000, FSL_IMX6UL_GPIO2_ADDR = 0x020A0000, FSL_IMX6UL_GPIO1_ADDR = 0x0209C000, + FSL_IMX6UL_GPT1_ADDR = 0x02098000, + FSL_IMX6UL_CAN2_ADDR = 0x02094000, FSL_IMX6UL_CAN1_ADDR = 0x02090000, + FSL_IMX6UL_CANn_SIZE = (4 * KiB), + FSL_IMX6UL_PWM4_ADDR = 0x0208C000, FSL_IMX6UL_PWM3_ADDR = 0x02088000, FSL_IMX6UL_PWM2_ADDR = 0x02084000, FSL_IMX6UL_PWM1_ADDR = 0x02080000, + FSL_IMX6UL_PWMn_SIZE = 0x20, + FSL_IMX6UL_AIPS1_CFG_ADDR = 0x0207C000, + FSL_IMX6UL_AIPS1_CFG_SIZE = (16 * KiB), + FSL_IMX6UL_BEE_ADDR = 0x02044000, + FSL_IMX6UL_BEE_SIZE = (16 * KiB), + FSL_IMX6UL_TOUCH_CTRL_ADDR = 0x02040000, + FSL_IMX6UL_TOUCH_CTRL_SIZE = 0x100, + FSL_IMX6UL_SPBA_ADDR = 0x0203C000, + FSL_IMX6UL_SPBA_SIZE = 0x100, + FSL_IMX6UL_ASRC_ADDR = 0x02034000, + FSL_IMX6UL_ASRC_SIZE = 0x100, + FSL_IMX6UL_SAI3_ADDR = 0x02030000, FSL_IMX6UL_SAI2_ADDR = 0x0202C000, FSL_IMX6UL_SAI1_ADDR = 0x02028000, + FSL_IMX6UL_SAIn_SIZE = 0x200, + FSL_IMX6UL_UART8_ADDR = 0x02024000, FSL_IMX6UL_UART1_ADDR = 0x02020000, FSL_IMX6UL_UART7_ADDR = 0x02018000, + FSL_IMX6UL_ECSPI4_ADDR = 0x02014000, FSL_IMX6UL_ECSPI3_ADDR = 0x02010000, FSL_IMX6UL_ECSPI2_ADDR = 0x0200C000, FSL_IMX6UL_ECSPI1_ADDR = 0x02008000, + FSL_IMX6UL_SPDIF_ADDR = 0x02004000, + FSL_IMX6UL_SPDIF_SIZE = 0x100, + /* AIPS-1 End */ + + FSL_IMX6UL_BCH_ADDR = 0x01808000, + FSL_IMX6UL_BCH_SIZE = 0x200, + + FSL_IMX6UL_GPMI_ADDR = 0x01806000, + FSL_IMX6UL_GPMI_SIZE = 0x200, FSL_IMX6UL_APBH_DMA_ADDR = 0x01804000, - FSL_IMX6UL_APBH_DMA_SIZE = (32 * 1024), + FSL_IMX6UL_APBH_DMA_SIZE = (4 * KiB), FSL_IMX6UL_A7MPCORE_ADDR = 0x00A00000, FSL_IMX6UL_OCRAM_ALIAS_ADDR = 0x00920000, - FSL_IMX6UL_OCRAM_ALIAS_SIZE = 0x00060000, + FSL_IMX6UL_OCRAM_ALIAS_SIZE = (384 * KiB), + FSL_IMX6UL_OCRAM_MEM_ADDR = 0x00900000, - FSL_IMX6UL_OCRAM_MEM_SIZE = 0x00020000, + FSL_IMX6UL_OCRAM_MEM_SIZE = (128 * KiB), + FSL_IMX6UL_CAAM_MEM_ADDR = 0x00100000, - FSL_IMX6UL_CAAM_MEM_SIZE = 0x00008000, + FSL_IMX6UL_CAAM_MEM_SIZE = (32 * KiB), + FSL_IMX6UL_ROM_ADDR = 0x00000000, - FSL_IMX6UL_ROM_SIZE = 0x00018000, + FSL_IMX6UL_ROM_SIZE = (96 * KiB), }; enum FslIMX6ULIRQs { diff --git a/include/hw/arm/fsl-imx7.h b/include/hw/arm/fsl-imx7.h index 1c5fa6fd67..411fa1c2e3 100644 --- a/include/hw/arm/fsl-imx7.h +++ b/include/hw/arm/fsl-imx7.h @@ -19,20 +19,18 @@ #ifndef FSL_IMX7_H #define FSL_IMX7_H -#include "hw/arm/boot.h" #include "hw/cpu/a15mpcore.h" #include "hw/intc/imx_gpcv2.h" #include "hw/misc/imx7_ccm.h" #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx7_gpr.h" -#include "hw/misc/imx6_src.h" +#include "hw/misc/imx7_src.h" #include "hw/watchdog/wdt_imx2.h" #include "hw/gpio/imx_gpio.h" #include "hw/char/imx_serial.h" #include "hw/timer/imx_gpt.h" #include "hw/timer/imx_epit.h" #include "hw/i2c/imx_i2c.h" -#include "hw/gpio/imx_gpio.h" #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" #include "hw/net/imx_fec.h" @@ -40,6 +38,7 @@ #include "hw/usb/chipidea.h" #include "cpu.h" #include "qom/object.h" +#include "qemu/units.h" #define TYPE_FSL_IMX7 "fsl-imx7" OBJECT_DECLARE_SIMPLE_TYPE(FslIMX7State, FSL_IMX7) @@ -58,6 +57,9 @@ enum FslIMX7Configuration { FSL_IMX7_NUM_ECSPIS = 4, FSL_IMX7_NUM_USBS = 3, FSL_IMX7_NUM_ADCS = 2, + FSL_IMX7_NUM_SAIS = 3, + FSL_IMX7_NUM_CANS = 2, + FSL_IMX7_NUM_PWMS = 4, }; struct FslIMX7State { @@ -72,6 +74,7 @@ struct FslIMX7State { IMX7CCMState ccm; IMX7AnalogState analog; IMX7SNVSState snvs; + IMX7SRCState src; IMXGPCv2State gpcv2; IMXSPIState spi[FSL_IMX7_NUM_ECSPIS]; IMXI2CState i2c[FSL_IMX7_NUM_I2CS]; @@ -82,131 +85,293 @@ struct FslIMX7State { IMX7GPRState gpr; ChipideaState usb[FSL_IMX7_NUM_USBS]; DesignwarePCIEHost pcie; + MemoryRegion rom; + MemoryRegion caam; + MemoryRegion ocram; + MemoryRegion ocram_epdc; + MemoryRegion ocram_pxp; + MemoryRegion ocram_s; + uint32_t phy_num[FSL_IMX7_NUM_ETHS]; + bool phy_connected[FSL_IMX7_NUM_ETHS]; }; enum FslIMX7MemoryMap { FSL_IMX7_MMDC_ADDR = 0x80000000, - FSL_IMX7_MMDC_SIZE = 2 * 1024 * 1024 * 1024UL, + FSL_IMX7_MMDC_SIZE = (2 * GiB), - FSL_IMX7_GPIO1_ADDR = 0x30200000, - FSL_IMX7_GPIO2_ADDR = 0x30210000, - FSL_IMX7_GPIO3_ADDR = 0x30220000, - FSL_IMX7_GPIO4_ADDR = 0x30230000, - FSL_IMX7_GPIO5_ADDR = 0x30240000, - FSL_IMX7_GPIO6_ADDR = 0x30250000, - FSL_IMX7_GPIO7_ADDR = 0x30260000, + FSL_IMX7_QSPI1_MEM_ADDR = 0x60000000, + FSL_IMX7_QSPI1_MEM_SIZE = (256 * MiB), - FSL_IMX7_IOMUXC_LPSR_GPR_ADDR = 0x30270000, + FSL_IMX7_PCIE1_MEM_ADDR = 0x40000000, + FSL_IMX7_PCIE1_MEM_SIZE = (256 * MiB), - FSL_IMX7_WDOG1_ADDR = 0x30280000, - FSL_IMX7_WDOG2_ADDR = 0x30290000, - FSL_IMX7_WDOG3_ADDR = 0x302A0000, - FSL_IMX7_WDOG4_ADDR = 0x302B0000, + FSL_IMX7_QSPI1_RX_BUF_ADDR = 0x34000000, + FSL_IMX7_QSPI1_RX_BUF_SIZE = (32 * MiB), - FSL_IMX7_IOMUXC_LPSR_ADDR = 0x302C0000, + /* PCIe Peripherals */ + FSL_IMX7_PCIE_REG_ADDR = 0x33800000, - FSL_IMX7_GPT1_ADDR = 0x302D0000, - FSL_IMX7_GPT2_ADDR = 0x302E0000, - FSL_IMX7_GPT3_ADDR = 0x302F0000, - FSL_IMX7_GPT4_ADDR = 0x30300000, + /* MMAP Peripherals */ + FSL_IMX7_DMA_APBH_ADDR = 0x33000000, + FSL_IMX7_DMA_APBH_SIZE = 0x8000, - FSL_IMX7_IOMUXC_ADDR = 0x30330000, - FSL_IMX7_IOMUXC_GPR_ADDR = 0x30340000, - FSL_IMX7_IOMUXCn_SIZE = 0x1000, + /* GPV configuration */ + FSL_IMX7_GPV6_ADDR = 0x32600000, + FSL_IMX7_GPV5_ADDR = 0x32500000, + FSL_IMX7_GPV4_ADDR = 0x32400000, + FSL_IMX7_GPV3_ADDR = 0x32300000, + FSL_IMX7_GPV2_ADDR = 0x32200000, + FSL_IMX7_GPV1_ADDR = 0x32100000, + FSL_IMX7_GPV0_ADDR = 0x32000000, + FSL_IMX7_GPVn_SIZE = (1 * MiB), - FSL_IMX7_OCOTP_ADDR = 0x30350000, - FSL_IMX7_OCOTP_SIZE = 0x10000, + /* Arm Peripherals */ + FSL_IMX7_A7MPCORE_ADDR = 0x31000000, - FSL_IMX7_ANALOG_ADDR = 0x30360000, - FSL_IMX7_SNVS_ADDR = 0x30370000, - FSL_IMX7_CCM_ADDR = 0x30380000, + /* AIPS-3 Begin */ - FSL_IMX7_SRC_ADDR = 0x30390000, - FSL_IMX7_SRC_SIZE = 0x1000, + FSL_IMX7_ENET2_ADDR = 0x30BF0000, + FSL_IMX7_ENET1_ADDR = 0x30BE0000, - FSL_IMX7_ADC1_ADDR = 0x30610000, - FSL_IMX7_ADC2_ADDR = 0x30620000, - FSL_IMX7_ADCn_SIZE = 0x1000, + FSL_IMX7_SDMA_ADDR = 0x30BD0000, + FSL_IMX7_SDMA_SIZE = (4 * KiB), - FSL_IMX7_PWM1_ADDR = 0x30660000, - FSL_IMX7_PWM2_ADDR = 0x30670000, - FSL_IMX7_PWM3_ADDR = 0x30680000, - FSL_IMX7_PWM4_ADDR = 0x30690000, - FSL_IMX7_PWMn_SIZE = 0x10000, + FSL_IMX7_EIM_ADDR = 0x30BC0000, + FSL_IMX7_EIM_SIZE = (4 * KiB), - FSL_IMX7_PCIE_PHY_ADDR = 0x306D0000, - FSL_IMX7_PCIE_PHY_SIZE = 0x10000, + FSL_IMX7_QSPI_ADDR = 0x30BB0000, + FSL_IMX7_QSPI_SIZE = 0x8000, - FSL_IMX7_GPC_ADDR = 0x303A0000, + FSL_IMX7_SIM2_ADDR = 0x30BA0000, + FSL_IMX7_SIM1_ADDR = 0x30B90000, + FSL_IMX7_SIMn_SIZE = (4 * KiB), + + FSL_IMX7_USDHC3_ADDR = 0x30B60000, + FSL_IMX7_USDHC2_ADDR = 0x30B50000, + FSL_IMX7_USDHC1_ADDR = 0x30B40000, + + FSL_IMX7_USB3_ADDR = 0x30B30000, + FSL_IMX7_USBMISC3_ADDR = 0x30B30200, + FSL_IMX7_USB2_ADDR = 0x30B20000, + FSL_IMX7_USBMISC2_ADDR = 0x30B20200, + FSL_IMX7_USB1_ADDR = 0x30B10000, + FSL_IMX7_USBMISC1_ADDR = 0x30B10200, + FSL_IMX7_USBMISCn_SIZE = 0x200, + + FSL_IMX7_USB_PL301_ADDR = 0x30AD0000, + FSL_IMX7_USB_PL301_SIZE = (64 * KiB), + + FSL_IMX7_SEMAPHORE_HS_ADDR = 0x30AC0000, + FSL_IMX7_SEMAPHORE_HS_SIZE = (64 * KiB), + + FSL_IMX7_MUB_ADDR = 0x30AB0000, + FSL_IMX7_MUA_ADDR = 0x30AA0000, + FSL_IMX7_MUn_SIZE = (KiB), + + FSL_IMX7_UART7_ADDR = 0x30A90000, + FSL_IMX7_UART6_ADDR = 0x30A80000, + FSL_IMX7_UART5_ADDR = 0x30A70000, + FSL_IMX7_UART4_ADDR = 0x30A60000, + + FSL_IMX7_I2C4_ADDR = 0x30A50000, + FSL_IMX7_I2C3_ADDR = 0x30A40000, + FSL_IMX7_I2C2_ADDR = 0x30A30000, + FSL_IMX7_I2C1_ADDR = 0x30A20000, + + FSL_IMX7_CAN2_ADDR = 0x30A10000, + FSL_IMX7_CAN1_ADDR = 0x30A00000, + FSL_IMX7_CANn_SIZE = (4 * KiB), + + FSL_IMX7_AIPS3_CONF_ADDR = 0x309F0000, + FSL_IMX7_AIPS3_CONF_SIZE = (64 * KiB), FSL_IMX7_CAAM_ADDR = 0x30900000, - FSL_IMX7_CAAM_SIZE = 0x40000, + FSL_IMX7_CAAM_SIZE = (256 * KiB), - FSL_IMX7_CAN1_ADDR = 0x30A00000, - FSL_IMX7_CAN2_ADDR = 0x30A10000, - FSL_IMX7_CANn_SIZE = 0x10000, + FSL_IMX7_SPBA_ADDR = 0x308F0000, + FSL_IMX7_SPBA_SIZE = (4 * KiB), - FSL_IMX7_I2C1_ADDR = 0x30A20000, - FSL_IMX7_I2C2_ADDR = 0x30A30000, - FSL_IMX7_I2C3_ADDR = 0x30A40000, - FSL_IMX7_I2C4_ADDR = 0x30A50000, + FSL_IMX7_SAI3_ADDR = 0x308C0000, + FSL_IMX7_SAI2_ADDR = 0x308B0000, + FSL_IMX7_SAI1_ADDR = 0x308A0000, + FSL_IMX7_SAIn_SIZE = (4 * KiB), - FSL_IMX7_ECSPI1_ADDR = 0x30820000, - FSL_IMX7_ECSPI2_ADDR = 0x30830000, - FSL_IMX7_ECSPI3_ADDR = 0x30840000, - FSL_IMX7_ECSPI4_ADDR = 0x30630000, - - FSL_IMX7_LCDIF_ADDR = 0x30730000, - FSL_IMX7_LCDIF_SIZE = 0x1000, - - FSL_IMX7_UART1_ADDR = 0x30860000, + FSL_IMX7_UART3_ADDR = 0x30880000, /* * Some versions of the reference manual claim that UART2 is @ * 0x30870000, but experiments with HW + DT files in upstream * Linux kernel show that not to be true and that block is - * acutally located @ 0x30890000 + * actually located @ 0x30890000 */ FSL_IMX7_UART2_ADDR = 0x30890000, - FSL_IMX7_UART3_ADDR = 0x30880000, - FSL_IMX7_UART4_ADDR = 0x30A60000, - FSL_IMX7_UART5_ADDR = 0x30A70000, - FSL_IMX7_UART6_ADDR = 0x30A80000, - FSL_IMX7_UART7_ADDR = 0x30A90000, + FSL_IMX7_UART1_ADDR = 0x30860000, - FSL_IMX7_SAI1_ADDR = 0x308A0000, - FSL_IMX7_SAI2_ADDR = 0x308B0000, - FSL_IMX7_SAI3_ADDR = 0x308C0000, - FSL_IMX7_SAIn_SIZE = 0x10000, + FSL_IMX7_ECSPI3_ADDR = 0x30840000, + FSL_IMX7_ECSPI2_ADDR = 0x30830000, + FSL_IMX7_ECSPI1_ADDR = 0x30820000, + FSL_IMX7_ECSPIn_SIZE = (4 * KiB), - FSL_IMX7_ENET1_ADDR = 0x30BE0000, - FSL_IMX7_ENET2_ADDR = 0x30BF0000, + /* AIPS-3 End */ - FSL_IMX7_USB1_ADDR = 0x30B10000, - FSL_IMX7_USBMISC1_ADDR = 0x30B10200, - FSL_IMX7_USB2_ADDR = 0x30B20000, - FSL_IMX7_USBMISC2_ADDR = 0x30B20200, - FSL_IMX7_USB3_ADDR = 0x30B30000, - FSL_IMX7_USBMISC3_ADDR = 0x30B30200, - FSL_IMX7_USBMISCn_SIZE = 0x200, + /* AIPS-2 Begin */ - FSL_IMX7_USDHC1_ADDR = 0x30B40000, - FSL_IMX7_USDHC2_ADDR = 0x30B50000, - FSL_IMX7_USDHC3_ADDR = 0x30B60000, + FSL_IMX7_AXI_DEBUG_MON_ADDR = 0x307E0000, + FSL_IMX7_AXI_DEBUG_MON_SIZE = (64 * KiB), - FSL_IMX7_SDMA_ADDR = 0x30BD0000, - FSL_IMX7_SDMA_SIZE = 0x1000, + FSL_IMX7_PERFMON2_ADDR = 0x307D0000, + FSL_IMX7_PERFMON1_ADDR = 0x307C0000, + FSL_IMX7_PERFMONn_SIZE = (64 * KiB), + + FSL_IMX7_DDRC_ADDR = 0x307A0000, + FSL_IMX7_DDRC_SIZE = (4 * KiB), + + FSL_IMX7_DDRC_PHY_ADDR = 0x30790000, + FSL_IMX7_DDRC_PHY_SIZE = (4 * KiB), + + FSL_IMX7_TZASC_ADDR = 0x30780000, + FSL_IMX7_TZASC_SIZE = (64 * KiB), + + FSL_IMX7_MIPI_DSI_ADDR = 0x30760000, + FSL_IMX7_MIPI_DSI_SIZE = (4 * KiB), + + FSL_IMX7_MIPI_CSI_ADDR = 0x30750000, + FSL_IMX7_MIPI_CSI_SIZE = 0x4000, + + FSL_IMX7_LCDIF_ADDR = 0x30730000, + FSL_IMX7_LCDIF_SIZE = 0x8000, + + FSL_IMX7_CSI_ADDR = 0x30710000, + FSL_IMX7_CSI_SIZE = (4 * KiB), + + FSL_IMX7_PXP_ADDR = 0x30700000, + FSL_IMX7_PXP_SIZE = 0x4000, + + FSL_IMX7_EPDC_ADDR = 0x306F0000, + FSL_IMX7_EPDC_SIZE = (4 * KiB), + + FSL_IMX7_PCIE_PHY_ADDR = 0x306D0000, + FSL_IMX7_PCIE_PHY_SIZE = (4 * KiB), + + FSL_IMX7_SYSCNT_CTRL_ADDR = 0x306C0000, + FSL_IMX7_SYSCNT_CMP_ADDR = 0x306B0000, + FSL_IMX7_SYSCNT_RD_ADDR = 0x306A0000, + + FSL_IMX7_PWM4_ADDR = 0x30690000, + FSL_IMX7_PWM3_ADDR = 0x30680000, + FSL_IMX7_PWM2_ADDR = 0x30670000, + FSL_IMX7_PWM1_ADDR = 0x30660000, + FSL_IMX7_PWMn_SIZE = (4 * KiB), + + FSL_IMX7_FlEXTIMER2_ADDR = 0x30650000, + FSL_IMX7_FlEXTIMER1_ADDR = 0x30640000, + FSL_IMX7_FLEXTIMERn_SIZE = (4 * KiB), + + FSL_IMX7_ECSPI4_ADDR = 0x30630000, + + FSL_IMX7_ADC2_ADDR = 0x30620000, + FSL_IMX7_ADC1_ADDR = 0x30610000, + FSL_IMX7_ADCn_SIZE = (4 * KiB), + + FSL_IMX7_AIPS2_CONF_ADDR = 0x305F0000, + FSL_IMX7_AIPS2_CONF_SIZE = (64 * KiB), + + /* AIPS-2 End */ + + /* AIPS-1 Begin */ + + FSL_IMX7_CSU_ADDR = 0x303E0000, + FSL_IMX7_CSU_SIZE = (64 * KiB), + + FSL_IMX7_RDC_ADDR = 0x303D0000, + FSL_IMX7_RDC_SIZE = (4 * KiB), + + FSL_IMX7_SEMAPHORE2_ADDR = 0x303C0000, + FSL_IMX7_SEMAPHORE1_ADDR = 0x303B0000, + FSL_IMX7_SEMAPHOREn_SIZE = (4 * KiB), + + FSL_IMX7_GPC_ADDR = 0x303A0000, + + FSL_IMX7_SRC_ADDR = 0x30390000, + + FSL_IMX7_CCM_ADDR = 0x30380000, + + FSL_IMX7_SNVS_HP_ADDR = 0x30370000, + + FSL_IMX7_ANALOG_ADDR = 0x30360000, + + FSL_IMX7_OCOTP_ADDR = 0x30350000, + FSL_IMX7_OCOTP_SIZE = 0x10000, + + FSL_IMX7_IOMUXC_GPR_ADDR = 0x30340000, + FSL_IMX7_IOMUXC_GPR_SIZE = (4 * KiB), + + FSL_IMX7_IOMUXC_ADDR = 0x30330000, + FSL_IMX7_IOMUXC_SIZE = (4 * KiB), + + FSL_IMX7_KPP_ADDR = 0x30320000, + FSL_IMX7_KPP_SIZE = (4 * KiB), + + FSL_IMX7_ROMCP_ADDR = 0x30310000, + FSL_IMX7_ROMCP_SIZE = (4 * KiB), + + FSL_IMX7_GPT4_ADDR = 0x30300000, + FSL_IMX7_GPT3_ADDR = 0x302F0000, + FSL_IMX7_GPT2_ADDR = 0x302E0000, + FSL_IMX7_GPT1_ADDR = 0x302D0000, + + FSL_IMX7_IOMUXC_LPSR_ADDR = 0x302C0000, + FSL_IMX7_IOMUXC_LPSR_SIZE = (4 * KiB), + + FSL_IMX7_WDOG4_ADDR = 0x302B0000, + FSL_IMX7_WDOG3_ADDR = 0x302A0000, + FSL_IMX7_WDOG2_ADDR = 0x30290000, + FSL_IMX7_WDOG1_ADDR = 0x30280000, + + FSL_IMX7_IOMUXC_LPSR_GPR_ADDR = 0x30270000, + + FSL_IMX7_GPIO7_ADDR = 0x30260000, + FSL_IMX7_GPIO6_ADDR = 0x30250000, + FSL_IMX7_GPIO5_ADDR = 0x30240000, + FSL_IMX7_GPIO4_ADDR = 0x30230000, + FSL_IMX7_GPIO3_ADDR = 0x30220000, + FSL_IMX7_GPIO2_ADDR = 0x30210000, + FSL_IMX7_GPIO1_ADDR = 0x30200000, + + FSL_IMX7_AIPS1_CONF_ADDR = 0x301F0000, + FSL_IMX7_AIPS1_CONF_SIZE = (64 * KiB), - FSL_IMX7_A7MPCORE_ADDR = 0x31000000, FSL_IMX7_A7MPCORE_DAP_ADDR = 0x30000000, + FSL_IMX7_A7MPCORE_DAP_SIZE = (1 * MiB), - FSL_IMX7_PCIE_REG_ADDR = 0x33800000, - FSL_IMX7_PCIE_REG_SIZE = 16 * 1024, + /* AIPS-1 End */ - FSL_IMX7_GPR_ADDR = 0x30340000, + FSL_IMX7_EIM_CS0_ADDR = 0x28000000, + FSL_IMX7_EIM_CS0_SIZE = (128 * MiB), - FSL_IMX7_DMA_APBH_ADDR = 0x33000000, - FSL_IMX7_DMA_APBH_SIZE = 0x2000, + FSL_IMX7_OCRAM_PXP_ADDR = 0x00940000, + FSL_IMX7_OCRAM_PXP_SIZE = (32 * KiB), + + FSL_IMX7_OCRAM_EPDC_ADDR = 0x00920000, + FSL_IMX7_OCRAM_EPDC_SIZE = (128 * KiB), + + FSL_IMX7_OCRAM_MEM_ADDR = 0x00900000, + FSL_IMX7_OCRAM_MEM_SIZE = (128 * KiB), + + FSL_IMX7_TCMU_ADDR = 0x00800000, + FSL_IMX7_TCMU_SIZE = (32 * KiB), + + FSL_IMX7_TCML_ADDR = 0x007F8000, + FSL_IMX7_TCML_SIZE = (32 * KiB), + + FSL_IMX7_OCRAM_S_ADDR = 0x00180000, + FSL_IMX7_OCRAM_S_SIZE = (32 * KiB), + + FSL_IMX7_CAAM_MEM_ADDR = 0x00100000, + FSL_IMX7_CAAM_MEM_SIZE = (32 * KiB), + + FSL_IMX7_ROM_ADDR = 0x00000000, + FSL_IMX7_ROM_SIZE = (96 * KiB), }; enum FslIMX7IRQs { @@ -235,6 +400,26 @@ enum FslIMX7IRQs { FSL_IMX7_USB2_IRQ = 42, FSL_IMX7_USB3_IRQ = 40, + FSL_IMX7_GPT1_IRQ = 55, + FSL_IMX7_GPT2_IRQ = 54, + FSL_IMX7_GPT3_IRQ = 53, + FSL_IMX7_GPT4_IRQ = 52, + + FSL_IMX7_GPIO1_LOW_IRQ = 64, + FSL_IMX7_GPIO1_HIGH_IRQ = 65, + FSL_IMX7_GPIO2_LOW_IRQ = 66, + FSL_IMX7_GPIO2_HIGH_IRQ = 67, + FSL_IMX7_GPIO3_LOW_IRQ = 68, + FSL_IMX7_GPIO3_HIGH_IRQ = 69, + FSL_IMX7_GPIO4_LOW_IRQ = 70, + FSL_IMX7_GPIO4_HIGH_IRQ = 71, + FSL_IMX7_GPIO5_LOW_IRQ = 72, + FSL_IMX7_GPIO5_HIGH_IRQ = 73, + FSL_IMX7_GPIO6_LOW_IRQ = 74, + FSL_IMX7_GPIO6_HIGH_IRQ = 75, + FSL_IMX7_GPIO7_LOW_IRQ = 76, + FSL_IMX7_GPIO7_HIGH_IRQ = 77, + FSL_IMX7_WDOG1_IRQ = 78, FSL_IMX7_WDOG2_IRQ = 79, FSL_IMX7_WDOG3_IRQ = 10, diff --git a/include/hw/arm/msf2-soc.h b/include/hw/arm/msf2-soc.h index ce417a6266..9300664e8e 100644 --- a/include/hw/arm/msf2-soc.h +++ b/include/hw/arm/msf2-soc.h @@ -47,13 +47,10 @@ OBJECT_DECLARE_SIMPLE_TYPE(MSF2State, MSF2_SOC) #define MSF2_NUM_TIMERS 2 struct MSF2State { - /*< private >*/ SysBusDevice parent_obj; - /*< public >*/ ARMv7MState armv7m; - char *cpu_type; char *part_name; uint64_t envm_size; uint64_t esram_size; diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h index ce593235d9..4e0d210188 100644 --- a/include/hw/arm/npcm7xx.h +++ b/include/hw/arm/npcm7xx.h @@ -29,9 +29,11 @@ #include "hw/misc/npcm7xx_pwm.h" #include "hw/misc/npcm7xx_rng.h" #include "hw/net/npcm7xx_emc.h" +#include "hw/net/npcm_gmac.h" #include "hw/nvram/npcm7xx_otp.h" #include "hw/timer/npcm7xx_timer.h" #include "hw/ssi/npcm7xx_fiu.h" +#include "hw/ssi/npcm_pspi.h" #include "hw/usb/hcd-ehci.h" #include "hw/usb/hcd-ohci.h" #include "target/arm/cpu.h" @@ -52,7 +54,7 @@ #define NPCM7XX_NR_PWM_MODULES 2 -typedef struct NPCM7xxMachine { +struct NPCM7xxMachine { MachineState parent; /* * PWM fan splitter. each splitter connects to one PWM output and @@ -60,11 +62,10 @@ typedef struct NPCM7xxMachine { */ SplitIRQ fan_splitter[NPCM7XX_NR_PWM_MODULES * NPCM7XX_PWM_PER_MODULE]; -} NPCM7xxMachine; +}; #define TYPE_NPCM7XX_MACHINE MACHINE_TYPE_NAME("npcm7xx") -#define NPCM7XX_MACHINE(obj) \ - OBJECT_CHECK(NPCM7xxMachine, (obj), TYPE_NPCM7XX_MACHINE) +OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxMachine, NPCM7XX_MACHINE) typedef struct NPCM7xxMachineClass { MachineClass parent; @@ -77,7 +78,7 @@ typedef struct NPCM7xxMachineClass { #define NPCM7XX_MACHINE_GET_CLASS(obj) \ OBJECT_GET_CLASS(NPCM7xxMachineClass, (obj), TYPE_NPCM7XX_MACHINE) -typedef struct NPCM7xxState { +struct NPCM7xxState { DeviceState parent; ARMCPU cpu[NPCM7XX_MAX_NUM_CPUS]; @@ -104,11 +105,13 @@ typedef struct NPCM7xxState { OHCISysBusState ohci; NPCM7xxFIUState fiu[2]; NPCM7xxEMCState emc[2]; + NPCMGMACState gmac[2]; NPCM7xxSDHCIState mmc; -} NPCM7xxState; + NPCMPSPIState pspi[2]; +}; #define TYPE_NPCM7XX "npcm7xx" -#define NPCM7XX(obj) OBJECT_CHECK(NPCM7xxState, (obj), TYPE_NPCM7XX) +OBJECT_DECLARE_TYPE(NPCM7xxState, NPCM7xxClass, NPCM7XX) #define TYPE_NPCM730 "npcm730" #define TYPE_NPCM750 "npcm750" @@ -122,11 +125,6 @@ typedef struct NPCM7xxClass { uint32_t num_cpus; } NPCM7xxClass; -#define NPCM7XX_CLASS(klass) \ - OBJECT_CLASS_CHECK(NPCM7xxClass, (klass), TYPE_NPCM7XX) -#define NPCM7XX_GET_CLASS(obj) \ - OBJECT_GET_CLASS(NPCM7xxClass, (obj), TYPE_NPCM7XX) - /** * npcm7xx_load_kernel - Loads memory with everything needed to boot * @machine - The machine containing the SoC to be booted. diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h index ff6a173f8a..40ee8ea9e5 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -70,9 +70,8 @@ void omap_clk_reparent(omap_clk clk, omap_clk parent); /* omap_intc.c */ #define TYPE_OMAP_INTC "common-omap-intc" -typedef struct omap_intr_handler_s omap_intr_handler; -DECLARE_INSTANCE_CHECKER(omap_intr_handler, OMAP_INTC, - TYPE_OMAP_INTC) +typedef struct OMAPIntcState OMAPIntcState; +DECLARE_INSTANCE_CHECKER(OMAPIntcState, OMAP_INTC, TYPE_OMAP_INTC) /* @@ -89,8 +88,8 @@ DECLARE_INSTANCE_CHECKER(omap_intr_handler, OMAP_INTC, * (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); +void omap_intc_set_iclk(OMAPIntcState *intc, omap_clk clk); +void omap_intc_set_fclk(OMAPIntcState *intc, omap_clk clk); /* omap_i2c.c */ #define TYPE_OMAP_I2C "omap_i2c" @@ -103,21 +102,20 @@ void omap_i2c_set_fclk(OMAPI2CState *i2c, omap_clk clk); /* omap_gpio.c */ #define TYPE_OMAP1_GPIO "omap-gpio" -DECLARE_INSTANCE_CHECKER(struct omap_gpif_s, OMAP1_GPIO, +typedef struct Omap1GpioState Omap1GpioState; +DECLARE_INSTANCE_CHECKER(Omap1GpioState, OMAP1_GPIO, TYPE_OMAP1_GPIO) #define TYPE_OMAP2_GPIO "omap2-gpio" -DECLARE_INSTANCE_CHECKER(struct omap2_gpif_s, OMAP2_GPIO, +typedef struct Omap2GpioState Omap2GpioState; +DECLARE_INSTANCE_CHECKER(Omap2GpioState, OMAP2_GPIO, 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 omap_gpio_set_clk(Omap1GpioState *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); +void omap2_gpio_set_iclk(Omap2GpioState *gpio, omap_clk clk); +void omap2_gpio_set_fclk(Omap2GpioState *gpio, uint8_t i, omap_clk clk); /* OMAP2 l4 Interconnect */ struct omap_l4_s; @@ -726,7 +724,6 @@ struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem, qemu_irq txdma, qemu_irq rxdma, const char *label, Chardev *chr); void omap_uart_reset(struct omap_uart_s *s); -void omap_uart_attach(struct omap_uart_s *s, Chardev *chr); struct omap_mpuio_s; qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s); @@ -1011,7 +1008,8 @@ void omap_mpu_wakeup(void *opaque, int irq, int req); __func__, paddr) /* OMAP-specific Linux bootloader tags for the ATAG_BOARD area - (Board-specifc tags are not here) */ + * (Board-specific tags are not here) + */ #define OMAP_TAG_CLOCK 0x4f01 #define OMAP_TAG_MMC 0x4f02 #define OMAP_TAG_SERIAL_CONSOLE 0x4f03 diff --git a/include/hw/arm/pxa.h b/include/hw/arm/pxa.h index 1095504b86..4c6caee113 100644 --- a/include/hw/arm/pxa.h +++ b/include/hw/arm/pxa.h @@ -100,8 +100,6 @@ void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly, #define TYPE_PXA2XX_PCMCIA "pxa2xx-pcmcia" OBJECT_DECLARE_SIMPLE_TYPE(PXA2xxPCMCIAState, PXA2XX_PCMCIA) -PXA2xxPCMCIAState *pxa2xx_pcmcia_init(MemoryRegion *sysmem, - hwaddr base); int pxa2xx_pcmcia_attach(void *opaque, PCMCIACardState *card); int pxa2xx_pcmcia_detach(void *opaque); void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq); @@ -119,14 +117,14 @@ void pxa27x_register_keypad(PXA2xxKeyPadState *kp, const struct keymap *map, int size); /* pxa2xx.c */ -typedef struct PXA2xxI2CState PXA2xxI2CState; +#define TYPE_PXA2XX_I2C "pxa2xx_i2c" +OBJECT_DECLARE_SIMPLE_TYPE(PXA2xxI2CState, PXA2XX_I2C) + PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base, qemu_irq irq, uint32_t page_size); I2CBus *pxa2xx_i2c_bus(PXA2xxI2CState *s); -#define TYPE_PXA2XX_I2C "pxa2xx_i2c" typedef struct PXA2xxI2SState PXA2xxI2SState; -OBJECT_DECLARE_SIMPLE_TYPE(PXA2xxI2CState, PXA2XX_I2C) #define TYPE_PXA2XX_FIR "pxa2xx-fir" OBJECT_DECLARE_SIMPLE_TYPE(PXA2xxFIrState, PXA2XX_FIR) @@ -193,8 +191,7 @@ struct PXA2xxI2SState { # define PA_FMT "0x%08lx" -PXA2xxState *pxa270_init(MemoryRegion *address_space, unsigned int sdram_size, - const char *revision); -PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size); +PXA2xxState *pxa270_init(unsigned int sdram_size, const char *revision); +PXA2xxState *pxa255_init(unsigned int sdram_size); #endif /* PXA_H */ diff --git a/include/hw/arm/raspberrypi-fw-defs.h b/include/hw/arm/raspberrypi-fw-defs.h new file mode 100644 index 0000000000..8b404e0533 --- /dev/null +++ b/include/hw/arm/raspberrypi-fw-defs.h @@ -0,0 +1,173 @@ +/* + * Raspberry Pi firmware definitions + * + * Copyright (C) 2022 Auriga LLC, based on Linux kernel + * `include/soc/bcm2835/raspberrypi-firmware.h` (Copyright © 2015 Broadcom) + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef INCLUDE_HW_MISC_RASPBERRYPI_FW_DEFS_H_ +#define INCLUDE_HW_MISC_RASPBERRYPI_FW_DEFS_H_ + + +enum rpi_firmware_property_tag { + RPI_FWREQ_PROPERTY_END = 0, + RPI_FWREQ_GET_FIRMWARE_REVISION = 0x00000001, + RPI_FWREQ_GET_FIRMWARE_VARIANT = 0x00000002, + RPI_FWREQ_GET_FIRMWARE_HASH = 0x00000003, + + RPI_FWREQ_SET_CURSOR_INFO = 0x00008010, + RPI_FWREQ_SET_CURSOR_STATE = 0x00008011, + + RPI_FWREQ_GET_BOARD_MODEL = 0x00010001, + RPI_FWREQ_GET_BOARD_REVISION = 0x00010002, + RPI_FWREQ_GET_BOARD_MAC_ADDRESS = 0x00010003, + RPI_FWREQ_GET_BOARD_SERIAL = 0x00010004, + RPI_FWREQ_GET_ARM_MEMORY = 0x00010005, + RPI_FWREQ_GET_VC_MEMORY = 0x00010006, + RPI_FWREQ_GET_CLOCKS = 0x00010007, + RPI_FWREQ_GET_POWER_STATE = 0x00020001, + RPI_FWREQ_GET_TIMING = 0x00020002, + RPI_FWREQ_SET_POWER_STATE = 0x00028001, + RPI_FWREQ_GET_CLOCK_STATE = 0x00030001, + RPI_FWREQ_GET_CLOCK_RATE = 0x00030002, + RPI_FWREQ_GET_VOLTAGE = 0x00030003, + RPI_FWREQ_GET_MAX_CLOCK_RATE = 0x00030004, + RPI_FWREQ_GET_MAX_VOLTAGE = 0x00030005, + RPI_FWREQ_GET_TEMPERATURE = 0x00030006, + RPI_FWREQ_GET_MIN_CLOCK_RATE = 0x00030007, + RPI_FWREQ_GET_MIN_VOLTAGE = 0x00030008, + RPI_FWREQ_GET_TURBO = 0x00030009, + RPI_FWREQ_GET_MAX_TEMPERATURE = 0x0003000a, + RPI_FWREQ_GET_STC = 0x0003000b, + RPI_FWREQ_ALLOCATE_MEMORY = 0x0003000c, + RPI_FWREQ_LOCK_MEMORY = 0x0003000d, + RPI_FWREQ_UNLOCK_MEMORY = 0x0003000e, + RPI_FWREQ_RELEASE_MEMORY = 0x0003000f, + RPI_FWREQ_EXECUTE_CODE = 0x00030010, + RPI_FWREQ_EXECUTE_QPU = 0x00030011, + RPI_FWREQ_SET_ENABLE_QPU = 0x00030012, + RPI_FWREQ_GET_DISPMANX_RESOURCE_MEM_HANDLE = 0x00030014, + RPI_FWREQ_GET_EDID_BLOCK = 0x00030020, + RPI_FWREQ_GET_CUSTOMER_OTP = 0x00030021, + RPI_FWREQ_GET_EDID_BLOCK_DISPLAY = 0x00030023, + RPI_FWREQ_GET_DOMAIN_STATE = 0x00030030, + RPI_FWREQ_GET_THROTTLED = 0x00030046, + RPI_FWREQ_GET_CLOCK_MEASURED = 0x00030047, + RPI_FWREQ_NOTIFY_REBOOT = 0x00030048, + RPI_FWREQ_SET_CLOCK_STATE = 0x00038001, + RPI_FWREQ_SET_CLOCK_RATE = 0x00038002, + RPI_FWREQ_SET_VOLTAGE = 0x00038003, + RPI_FWREQ_SET_MAX_CLOCK_RATE = 0x00038004, + RPI_FWREQ_SET_MIN_CLOCK_RATE = 0x00038007, + RPI_FWREQ_SET_TURBO = 0x00038009, + RPI_FWREQ_SET_CUSTOMER_OTP = 0x00038021, + RPI_FWREQ_SET_DOMAIN_STATE = 0x00038030, + RPI_FWREQ_GET_GPIO_STATE = 0x00030041, + RPI_FWREQ_SET_GPIO_STATE = 0x00038041, + RPI_FWREQ_SET_SDHOST_CLOCK = 0x00038042, + RPI_FWREQ_GET_GPIO_CONFIG = 0x00030043, + RPI_FWREQ_SET_GPIO_CONFIG = 0x00038043, + RPI_FWREQ_GET_PERIPH_REG = 0x00030045, + RPI_FWREQ_SET_PERIPH_REG = 0x00038045, + RPI_FWREQ_GET_POE_HAT_VAL = 0x00030049, + RPI_FWREQ_SET_POE_HAT_VAL = 0x00038049, + RPI_FWREQ_SET_POE_HAT_VAL_OLD = 0x00030050, + RPI_FWREQ_NOTIFY_XHCI_RESET = 0x00030058, + RPI_FWREQ_GET_REBOOT_FLAGS = 0x00030064, + RPI_FWREQ_SET_REBOOT_FLAGS = 0x00038064, + RPI_FWREQ_NOTIFY_DISPLAY_DONE = 0x00030066, + + /* Dispmanx TAGS */ + RPI_FWREQ_FRAMEBUFFER_ALLOCATE = 0x00040001, + RPI_FWREQ_FRAMEBUFFER_BLANK = 0x00040002, + RPI_FWREQ_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT = 0x00040003, + RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_WIDTH_HEIGHT = 0x00040004, + RPI_FWREQ_FRAMEBUFFER_GET_DEPTH = 0x00040005, + RPI_FWREQ_FRAMEBUFFER_GET_PIXEL_ORDER = 0x00040006, + RPI_FWREQ_FRAMEBUFFER_GET_ALPHA_MODE = 0x00040007, + RPI_FWREQ_FRAMEBUFFER_GET_PITCH = 0x00040008, + RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_OFFSET = 0x00040009, + RPI_FWREQ_FRAMEBUFFER_GET_OVERSCAN = 0x0004000a, + RPI_FWREQ_FRAMEBUFFER_GET_PALETTE = 0x0004000b, + RPI_FWREQ_FRAMEBUFFER_GET_LAYER = 0x0004000c, + RPI_FWREQ_FRAMEBUFFER_GET_TRANSFORM = 0x0004000d, + RPI_FWREQ_FRAMEBUFFER_GET_VSYNC = 0x0004000e, + RPI_FWREQ_FRAMEBUFFER_GET_TOUCHBUF = 0x0004000f, + RPI_FWREQ_FRAMEBUFFER_GET_GPIOVIRTBUF = 0x00040010, + RPI_FWREQ_FRAMEBUFFER_RELEASE = 0x00048001, + RPI_FWREQ_FRAMEBUFFER_GET_DISPLAY_ID = 0x00040016, + RPI_FWREQ_FRAMEBUFFER_SET_DISPLAY_NUM = 0x00048013, + RPI_FWREQ_FRAMEBUFFER_GET_NUM_DISPLAYS = 0x00040013, + RPI_FWREQ_FRAMEBUFFER_GET_DISPLAY_SETTINGS = 0x00040014, + RPI_FWREQ_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, + RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT = 0x00044004, + RPI_FWREQ_FRAMEBUFFER_TEST_DEPTH = 0x00044005, + RPI_FWREQ_FRAMEBUFFER_TEST_PIXEL_ORDER = 0x00044006, + RPI_FWREQ_FRAMEBUFFER_TEST_ALPHA_MODE = 0x00044007, + RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_OFFSET = 0x00044009, + RPI_FWREQ_FRAMEBUFFER_TEST_OVERSCAN = 0x0004400a, + RPI_FWREQ_FRAMEBUFFER_TEST_PALETTE = 0x0004400b, + RPI_FWREQ_FRAMEBUFFER_TEST_LAYER = 0x0004400c, + RPI_FWREQ_FRAMEBUFFER_TEST_TRANSFORM = 0x0004400d, + RPI_FWREQ_FRAMEBUFFER_TEST_VSYNC = 0x0004400e, + RPI_FWREQ_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT = 0x00048003, + RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT = 0x00048004, + RPI_FWREQ_FRAMEBUFFER_SET_DEPTH = 0x00048005, + RPI_FWREQ_FRAMEBUFFER_SET_PIXEL_ORDER = 0x00048006, + RPI_FWREQ_FRAMEBUFFER_SET_ALPHA_MODE = 0x00048007, + RPI_FWREQ_FRAMEBUFFER_SET_PITCH = 0x00048008, + RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_OFFSET = 0x00048009, + RPI_FWREQ_FRAMEBUFFER_SET_OVERSCAN = 0x0004800a, + RPI_FWREQ_FRAMEBUFFER_SET_PALETTE = 0x0004800b, + + RPI_FWREQ_FRAMEBUFFER_SET_TOUCHBUF = 0x0004801f, + RPI_FWREQ_FRAMEBUFFER_SET_GPIOVIRTBUF = 0x00048020, + RPI_FWREQ_FRAMEBUFFER_SET_VSYNC = 0x0004800e, + RPI_FWREQ_FRAMEBUFFER_SET_LAYER = 0x0004800c, + RPI_FWREQ_FRAMEBUFFER_SET_TRANSFORM = 0x0004800d, + RPI_FWREQ_FRAMEBUFFER_SET_BACKLIGHT = 0x0004800f, + + RPI_FWREQ_VCHIQ_INIT = 0x00048010, + + RPI_FWREQ_SET_PLANE = 0x00048015, + RPI_FWREQ_GET_DISPLAY_TIMING = 0x00040017, + RPI_FWREQ_SET_TIMING = 0x00048017, + RPI_FWREQ_GET_DISPLAY_CFG = 0x00040018, + RPI_FWREQ_SET_DISPLAY_POWER = 0x00048019, + RPI_FWREQ_GET_COMMAND_LINE = 0x00050001, + RPI_FWREQ_GET_DMA_CHANNELS = 0x00060001, +}; + +enum rpi_firmware_clk_id { + RPI_FIRMWARE_EMMC_CLK_ID = 1, + RPI_FIRMWARE_UART_CLK_ID, + RPI_FIRMWARE_ARM_CLK_ID, + RPI_FIRMWARE_CORE_CLK_ID, + RPI_FIRMWARE_V3D_CLK_ID, + RPI_FIRMWARE_H264_CLK_ID, + RPI_FIRMWARE_ISP_CLK_ID, + RPI_FIRMWARE_SDRAM_CLK_ID, + RPI_FIRMWARE_PIXEL_CLK_ID, + RPI_FIRMWARE_PWM_CLK_ID, + RPI_FIRMWARE_HEVC_CLK_ID, + RPI_FIRMWARE_EMMC2_CLK_ID, + RPI_FIRMWARE_M2MC_CLK_ID, + RPI_FIRMWARE_PIXEL_BVB_CLK_ID, + RPI_FIRMWARE_VEC_CLK_ID, + RPI_FIRMWARE_NUM_CLK_ID, +}; + +struct rpi_firmware_property_tag_header { + uint32_t tag; + uint32_t buf_size; + uint32_t req_resp_size; +}; + +typedef struct rpi_firmware_prop_request { + struct rpi_firmware_property_tag_header hdr; + uint8_t payload[0]; +} rpi_firmware_prop_request_t; + +#endif /* INCLUDE_HW_MISC_RASPBERRYPI_FW_DEFS_H_ */ diff --git a/include/hw/arm/raspi_platform.h b/include/hw/arm/raspi_platform.h index e0e6c8ce94..7bc4807fa5 100644 --- a/include/hw/arm/raspi_platform.h +++ b/include/hw/arm/raspi_platform.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program. If not, see . * * Various undocumented addresses and names come from Herman Hermitage's VC4 * documentation: @@ -29,6 +28,42 @@ #ifndef HW_ARM_RASPI_PLATFORM_H #define HW_ARM_RASPI_PLATFORM_H +#include "hw/boards.h" +#include "hw/arm/boot.h" + +/* Registered machine type (matches RPi Foundation bootloader and U-Boot) */ +#define MACH_TYPE_BCM2708 3138 + +#define TYPE_RASPI_BASE_MACHINE MACHINE_TYPE_NAME("raspi-base") +OBJECT_DECLARE_TYPE(RaspiBaseMachineState, RaspiBaseMachineClass, + RASPI_BASE_MACHINE) + +struct RaspiBaseMachineState { + /*< private >*/ + MachineState parent_obj; + /*< public >*/ + struct arm_boot_info binfo; +}; + +struct RaspiBaseMachineClass { + /*< private >*/ + MachineClass parent_obj; + /*< public >*/ + uint32_t board_rev; +}; + +/* Common functions for raspberry pi machines */ +const char *board_soc_type(uint32_t board_rev); +void raspi_machine_init(MachineState *machine); + +typedef struct BCM283XBaseState BCM283XBaseState; +void raspi_base_machine_init(MachineState *machine, + BCM283XBaseState *soc); + +void raspi_machine_class_common_init(MachineClass *mc, + uint32_t board_rev); +uint64_t board_ram_size(uint32_t board_rev); + #define MSYNC_OFFSET 0x0000 /* Multicore Sync Block */ #define CCPT_OFFSET 0x1000 /* Compact Camera Port 2 TX */ #define INTE_OFFSET 0x2000 /* VC Interrupt controller */ @@ -38,7 +73,7 @@ #define MPHI_OFFSET 0x6000 /* Message-based Parallel Host Intf. */ #define DMA_OFFSET 0x7000 /* DMA controller, channels 0-14 */ #define ARBA_OFFSET 0x9000 -#define BRDG_OFFSET 0xa000 +#define BRDG_OFFSET 0xa000 /* RPiVid ASB for BCM2838 (BCM2711) */ #define ARM_OFFSET 0xB000 /* ARM control block */ #define ARMCTRL_OFFSET (ARM_OFFSET + 0x000) #define ARMCTRL_IC_OFFSET (ARM_OFFSET + 0x200) /* Interrupt controller */ @@ -171,4 +206,14 @@ #define INTERRUPT_ILLEGAL_TYPE0 6 #define INTERRUPT_ILLEGAL_TYPE1 7 +/* Clock rates */ +#define RPI_FIRMWARE_EMMC_CLK_RATE 50000000 +#define RPI_FIRMWARE_UART_CLK_RATE 3000000 +/* + * TODO: this is really SoC-specific; we might want to + * set it per-SoC if it turns out any guests care. + */ +#define RPI_FIRMWARE_CORE_CLK_RATE 350000000 +#define RPI_FIRMWARE_DEFAULT_CLK_RATE 700000000 + #endif diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 21e62342e9..5ec2e6c1a4 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -23,11 +23,19 @@ #include "hw/pci/pci.h" #include "qom/object.h" -#define SMMU_PCI_BUS_MAX 256 -#define SMMU_PCI_DEVFN_MAX 256 -#define SMMU_PCI_DEVFN(sid) (sid & 0xFF) +#define SMMU_PCI_BUS_MAX 256 +#define SMMU_PCI_DEVFN_MAX 256 +#define SMMU_PCI_DEVFN(sid) (sid & 0xFF) -#define SMMU_MAX_VA_BITS 48 +/* VMSAv8-64 Translation constants and functions */ +#define VMSA_LEVELS 4 +#define VMSA_MAX_S2_CONCAT 16 + +#define VMSA_STRIDE(gran) ((gran) - VMSA_LEVELS + 1) +#define VMSA_BIT_LVL(isz, strd, lvl) ((isz) - (strd) * \ + (VMSA_LEVELS - (lvl))) +#define VMSA_IDXMSK(isz, strd, lvl) ((1ULL << \ + VMSA_BIT_LVL(isz, strd, lvl)) - 1) /* * Page table walk error types @@ -42,6 +50,7 @@ typedef enum { } SMMUPTWEventType; typedef struct SMMUPTWEventInfo { + int stage; SMMUPTWEventType type; dma_addr_t addr; /* fetched address that induced an abort, if any */ } SMMUPTWEventInfo; @@ -60,25 +69,42 @@ typedef struct SMMUTLBEntry { uint8_t granule; } SMMUTLBEntry; +/* Stage-2 configuration. */ +typedef struct SMMUS2Cfg { + uint8_t tsz; /* Size of IPA input region (S2T0SZ) */ + uint8_t sl0; /* Start level of translation (S2SL0) */ + bool affd; /* AF Fault Disable (S2AFFD) */ + bool record_faults; /* Record fault events (S2R) */ + uint8_t granule_sz; /* Granule page shift (based on S2TG) */ + uint8_t eff_ps; /* Effective PA output range (based on S2PS) */ + uint16_t vmid; /* Virtual Machine ID (S2VMID) */ + uint64_t vttb; /* Address of translation table base (S2TTB) */ +} SMMUS2Cfg; + /* * Generic structure populated by derived SMMU devices * after decoding the configuration information and used as * input to the page table walk */ typedef struct SMMUTransCfg { + /* Shared fields between stage-1 and stage-2. */ int stage; /* translation stage */ - bool aa64; /* arch64 or aarch32 translation table */ bool disabled; /* smmu is disabled */ bool bypassed; /* translation is bypassed */ bool aborted; /* translation is aborted */ + bool affd; /* AF fault disable */ + uint32_t iotlb_hits; /* counts IOTLB hits */ + uint32_t iotlb_misses; /* counts IOTLB misses*/ + /* Used by stage-1 only. */ + bool aa64; /* arch64 or aarch32 translation table */ bool record_faults; /* record fault events */ uint64_t ttb; /* TT base address */ uint8_t oas; /* output address width */ uint8_t tbi; /* Top Byte Ignore */ uint16_t asid; SMMUTransTableInfo tt[2]; - uint32_t iotlb_hits; /* counts IOTLB hits for this asid */ - uint32_t iotlb_misses; /* counts IOTLB misses for this asid */ + /* Used by stage-2 only. */ + struct SMMUS2Cfg s2cfg; } SMMUTransCfg; typedef struct SMMUDevice { @@ -100,6 +126,7 @@ typedef struct SMMUPciBus { typedef struct SMMUIOTLBKey { uint64_t iova; uint16_t asid; + uint16_t vmid; uint8_t tg; uint8_t level; } SMMUIOTLBKey; @@ -163,17 +190,15 @@ IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid); SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, SMMUTransTableInfo *tt, hwaddr iova); void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *entry); -SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint64_t iova, +SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint16_t vmid, uint64_t iova, uint8_t tg, uint8_t level); void smmu_iotlb_inv_all(SMMUState *s); void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid); -void smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova, +void smmu_iotlb_inv_vmid(SMMUState *s, uint16_t vmid); +void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, uint8_t tg, uint64_t num_pages, uint8_t ttl); /* Unmap the range of all the notifiers registered to any IOMMU mr */ void smmu_inv_notifiers_all(SMMUState *s); -/* Unmap the range of all the notifiers registered to @mr */ -void smmu_inv_notifiers_mr(IOMMUMemoryRegion *mr); - #endif /* HW_ARM_SMMU_COMMON_H */ diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h index c641e60735..d183a62766 100644 --- a/include/hw/arm/smmuv3.h +++ b/include/hw/arm/smmuv3.h @@ -20,7 +20,6 @@ #define HW_ARM_SMMUV3_H #include "hw/arm/smmu-common.h" -#include "hw/registerfields.h" #include "qom/object.h" #define TYPE_SMMUV3_IOMMU_MEMORY_REGION "smmuv3-iommu-memory-region" @@ -46,6 +45,7 @@ struct SMMUv3State { uint32_t cr[3]; uint32_t cr0ack; uint32_t statusr; + uint32_t gbpa; uint32_t irq_ctrl; uint32_t gerror; uint32_t gerrorn; @@ -62,6 +62,7 @@ struct SMMUv3State { qemu_irq irq[4]; QemuMutex mutex; + char *stage; }; typedef enum { @@ -77,10 +78,13 @@ struct SMMUv3Class { /*< public >*/ DeviceRealize parent_realize; - DeviceReset parent_reset; + ResettablePhases parent_phases; }; #define TYPE_ARM_SMMUV3 "arm-smmuv3" OBJECT_DECLARE_TYPE(SMMUv3State, SMMUv3Class, ARM_SMMUV3) +#define STAGE1_SUPPORTED(s) FIELD_EX32(s->idr[0], IDR0, S1P) +#define STAGE2_SUPPORTED(s) FIELD_EX32(s->idr[0], IDR0, S2P) + #endif diff --git a/include/hw/arm/stm32f100_soc.h b/include/hw/arm/stm32f100_soc.h index 40cd415b28..a74d7b369c 100644 --- a/include/hw/arm/stm32f100_soc.h +++ b/include/hw/arm/stm32f100_soc.h @@ -43,12 +43,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(STM32F100State, STM32F100_SOC) #define SRAM_SIZE (8 * 1024) struct STM32F100State { - /*< private >*/ SysBusDevice parent_obj; - /*< public >*/ - char *cpu_type; - ARMv7MState armv7m; STM32F2XXUsartState usart[STM_NUM_USARTS]; diff --git a/include/hw/arm/stm32f205_soc.h b/include/hw/arm/stm32f205_soc.h index 849d3ed889..4f4c8bbebc 100644 --- a/include/hw/arm/stm32f205_soc.h +++ b/include/hw/arm/stm32f205_soc.h @@ -49,11 +49,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(STM32F205State, STM32F205_SOC) #define SRAM_SIZE (128 * 1024) struct STM32F205State { - /*< private >*/ SysBusDevice parent_obj; - /*< public >*/ - - char *cpu_type; ARMv7MState armv7m; @@ -63,7 +59,7 @@ struct STM32F205State { STM32F2XXADCState adc[STM_NUM_ADCS]; STM32F2XXSPIState spi[STM_NUM_SPIS]; - qemu_or_irq *adc_irqs; + OrIRQState *adc_irqs; MemoryRegion sram; MemoryRegion flash; diff --git a/include/hw/arm/stm32f405_soc.h b/include/hw/arm/stm32f405_soc.h index 5bb0c8d569..d15c03c4b5 100644 --- a/include/hw/arm/stm32f405_soc.h +++ b/include/hw/arm/stm32f405_soc.h @@ -46,14 +46,12 @@ OBJECT_DECLARE_SIMPLE_TYPE(STM32F405State, STM32F405_SOC) #define FLASH_BASE_ADDRESS 0x08000000 #define FLASH_SIZE (1024 * 1024) #define SRAM_BASE_ADDRESS 0x20000000 -#define SRAM_SIZE (192 * 1024) +#define SRAM_SIZE (128 * 1024) +#define CCM_BASE_ADDRESS 0x10000000 +#define CCM_SIZE (64 * 1024) struct STM32F405State { - /*< private >*/ SysBusDevice parent_obj; - /*< public >*/ - - char *cpu_type; ARMv7MState armv7m; @@ -61,10 +59,11 @@ struct STM32F405State { STM32F4xxExtiState exti; STM32F2XXUsartState usart[STM_NUM_USARTS]; STM32F2XXTimerState timer[STM_NUM_TIMERS]; - qemu_or_irq adc_irqs; + OrIRQState adc_irqs; STM32F2XXADCState adc[STM_NUM_ADCS]; STM32F2XXSPIState spi[STM_NUM_SPIS]; + MemoryRegion ccm; MemoryRegion sram; MemoryRegion flash; MemoryRegion flash_alias; diff --git a/include/hw/arm/stm32l4x5_soc.h b/include/hw/arm/stm32l4x5_soc.h new file mode 100644 index 0000000000..ee5f362405 --- /dev/null +++ b/include/hw/arm/stm32l4x5_soc.h @@ -0,0 +1,67 @@ +/* + * STM32L4x5 SoC family + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This work is heavily inspired by the stm32f405_soc by Alistair Francis. + * Original code is licensed under the MIT License: + * + * Copyright (c) 2014 Alistair Francis + */ + +/* + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html + */ + +#ifndef HW_ARM_STM32L4x5_SOC_H +#define HW_ARM_STM32L4x5_SOC_H + +#include "exec/memory.h" +#include "hw/arm/armv7m.h" +#include "hw/or-irq.h" +#include "hw/misc/stm32l4x5_syscfg.h" +#include "hw/misc/stm32l4x5_exti.h" +#include "hw/misc/stm32l4x5_rcc.h" +#include "hw/gpio/stm32l4x5_gpio.h" +#include "qom/object.h" + +#define TYPE_STM32L4X5_SOC "stm32l4x5-soc" +#define TYPE_STM32L4X5XC_SOC "stm32l4x5xc-soc" +#define TYPE_STM32L4X5XE_SOC "stm32l4x5xe-soc" +#define TYPE_STM32L4X5XG_SOC "stm32l4x5xg-soc" +OBJECT_DECLARE_TYPE(Stm32l4x5SocState, Stm32l4x5SocClass, STM32L4X5_SOC) + +#define NUM_EXTI_OR_GATES 4 + +struct Stm32l4x5SocState { + SysBusDevice parent_obj; + + ARMv7MState armv7m; + + Stm32l4x5ExtiState exti; + OrIRQState exti_or_gates[NUM_EXTI_OR_GATES]; + Stm32l4x5SyscfgState syscfg; + Stm32l4x5RccState rcc; + Stm32l4x5GpioState gpio[NUM_GPIOS]; + + MemoryRegion sram1; + MemoryRegion sram2; + MemoryRegion flash; + MemoryRegion flash_alias; +}; + +struct Stm32l4x5SocClass { + SysBusDeviceClass parent_class; + + size_t flash_size; +}; + +#endif diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 15feabac63..bb486d36b1 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -34,6 +34,7 @@ #include "qemu/notify.h" #include "hw/boards.h" #include "hw/arm/boot.h" +#include "hw/arm/bsa.h" #include "hw/block/flash.h" #include "sysemu/kvm.h" #include "hw/intc/arm_gicv3_common.h" @@ -43,17 +44,6 @@ #define NUM_VIRTIO_TRANSPORTS 32 #define NUM_SMMU_IRQS 4 -#define ARCH_GIC_MAINT_IRQ 9 - -#define ARCH_TIMER_VIRT_IRQ 11 -#define ARCH_TIMER_S_EL1_IRQ 13 -#define ARCH_TIMER_NS_EL1_IRQ 14 -#define ARCH_TIMER_NS_EL2_IRQ 10 - -#define VIRTUAL_PMU_IRQ 7 - -#define PPI(irq) ((irq) + 16) - /* See Linux kernel arch/arm64/include/asm/pvclock-abi.h */ #define PVTIME_SIZE_PER_CPU 64 @@ -109,14 +99,19 @@ typedef enum VirtMSIControllerType { } VirtMSIControllerType; typedef enum VirtGICType { - VIRT_GIC_VERSION_MAX, - VIRT_GIC_VERSION_HOST, - VIRT_GIC_VERSION_2, - VIRT_GIC_VERSION_3, - VIRT_GIC_VERSION_4, + VIRT_GIC_VERSION_MAX = 0, + VIRT_GIC_VERSION_HOST = 1, + /* The concrete GIC values have to match the GIC version number */ + VIRT_GIC_VERSION_2 = 2, + VIRT_GIC_VERSION_3 = 3, + VIRT_GIC_VERSION_4 = 4, VIRT_GIC_VERSION_NOSEL, } VirtGICType; +#define VIRT_GIC_VERSION_2_MASK BIT(VIRT_GIC_VERSION_2) +#define VIRT_GIC_VERSION_3_MASK BIT(VIRT_GIC_VERSION_3) +#define VIRT_GIC_VERSION_4_MASK BIT(VIRT_GIC_VERSION_4) + struct VirtMachineClass { MachineClass parent; bool disallow_affinity_adjustment; @@ -125,6 +120,7 @@ struct VirtMachineClass { bool no_pmu; bool claim_edge_triggered_timers; bool smbios_old_sys_ver; + bool no_highmem_compact; bool no_highmem_ecam; bool no_ged; /* Machines < 4.2 have no support for ACPI GED device */ bool kvm_no_adjvtime; @@ -134,6 +130,7 @@ struct VirtMachineClass { /* Machines < 6.2 have no support for describing cpu topology to guest */ bool no_cpu_topology; bool no_tcg_lpa2; + bool no_ns_el2_virt_timer_irq; }; struct VirtMachineState { @@ -144,6 +141,7 @@ struct VirtMachineState { PFlashCFI01 *flash[2]; bool secure; bool highmem; + bool highmem_compact; bool highmem_ecam; bool highmem_mmio; bool highmem_redists; @@ -152,7 +150,7 @@ struct VirtMachineState { bool virt; bool ras; bool mte; - bool dtb_kaslr_seed; + bool dtb_randomness; OnOffAuto acpi; VirtGICType gic_version; VirtIOMMUType iommu; @@ -176,6 +174,7 @@ struct VirtMachineState { PCIBus *bus; char *oem_id; char *oem_table_id; + bool ns_el2_virt_timer_irq; }; #define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM) diff --git a/include/hw/arm/xen_arch_hvm.h b/include/hw/arm/xen_arch_hvm.h new file mode 100644 index 0000000000..8fd645e723 --- /dev/null +++ b/include/hw/arm/xen_arch_hvm.h @@ -0,0 +1,9 @@ +#ifndef HW_XEN_ARCH_ARM_HVM_H +#define HW_XEN_ARCH_ARM_HVM_H + +#include +void arch_handle_ioreq(XenIOState *state, ioreq_t *req); +void arch_xen_set_memory(XenIOState *state, + MemoryRegionSection *section, + bool add); +#endif diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index cbe8a19c10..025beb5532 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -13,7 +13,6 @@ #define XLNX_VERSAL_H #include "hw/sysbus.h" -#include "hw/arm/boot.h" #include "hw/cpu/cluster.h" #include "hw/or-irq.h" #include "hw/sd/sdhci.h" @@ -31,6 +30,11 @@ #include "hw/dma/xlnx_csu_dma.h" #include "hw/misc/xlnx-versal-crl.h" #include "hw/misc/xlnx-versal-pmc-iou-slcr.h" +#include "hw/misc/xlnx-versal-trng.h" +#include "hw/net/xlnx-versal-canfd.h" +#include "hw/misc/xlnx-versal-cfu.h" +#include "hw/misc/xlnx-versal-cframe-reg.h" +#include "target/arm/cpu.h" #define TYPE_XLNX_VERSAL "xlnx-versal" OBJECT_DECLARE_SIMPLE_TYPE(Versal, XLNX_VERSAL) @@ -43,6 +47,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(Versal, XLNX_VERSAL) #define XLNX_VERSAL_NR_SDS 2 #define XLNX_VERSAL_NR_XRAM 4 #define XLNX_VERSAL_NR_IRQS 192 +#define XLNX_VERSAL_NR_CANFD 2 +#define XLNX_VERSAL_CANFD_REF_CLK (24 * 1000 * 1000) +#define XLNX_VERSAL_NR_CFRAME 15 struct Versal { /*< private >*/ @@ -73,6 +80,8 @@ struct Versal { CadenceGEMState gem[XLNX_VERSAL_NR_GEMS]; XlnxZDMA adma[XLNX_VERSAL_NR_ADMAS]; VersalUsb2 usb; + CanBusState *canbus[XLNX_VERSAL_NR_CANFD]; + XlnxVersalCANFDState canfd[XLNX_VERSAL_NR_CANFD]; } iou; /* Real-time Processing Unit. */ @@ -85,7 +94,7 @@ struct Versal { } rpu; struct { - qemu_or_irq irq_orgate; + OrIRQState irq_orgate; XlnxXramCtrl ctrl[XLNX_VERSAL_NR_XRAM]; } xram; @@ -103,17 +112,23 @@ struct Versal { XlnxCSUDMA dma_src; XlnxCSUDMA dma_dst; MemoryRegion linear_mr; - qemu_or_irq irq_orgate; + OrIRQState irq_orgate; } ospi; } iou; XlnxZynqMPRTC rtc; + XlnxVersalTRng trng; XlnxBBRam bbram; XlnxEFuse efuse; XlnxVersalEFuseCtrl efuse_ctrl; XlnxVersalEFuseCache efuse_cache; + XlnxVersalCFUAPB cfu_apb; + XlnxVersalCFUFDRO cfu_fdro; + XlnxVersalCFUSFR cfu_sfr; + XlnxVersalCFrameReg cframe[XLNX_VERSAL_NR_CFRAME]; + XlnxVersalCFrameBcastReg cframe_bcast; - qemu_or_irq apb_irq_orgate; + OrIRQState apb_irq_orgate; } pmc; struct { @@ -133,6 +148,8 @@ struct Versal { #define VERSAL_CRL_IRQ 10 #define VERSAL_UART0_IRQ_0 18 #define VERSAL_UART1_IRQ_0 19 +#define VERSAL_CANFD0_IRQ_0 20 +#define VERSAL_CANFD1_IRQ_0 21 #define VERSAL_USB0_IRQ_0 22 #define VERSAL_GEM0_IRQ_0 56 #define VERSAL_GEM0_WAKE_IRQ_0 57 @@ -140,10 +157,12 @@ struct Versal { #define VERSAL_GEM1_WAKE_IRQ_0 59 #define VERSAL_ADMA_IRQ_0 60 #define VERSAL_XRAM_IRQ_0 79 +#define VERSAL_CFU_IRQ_0 120 #define VERSAL_PMC_APB_IRQ 121 #define VERSAL_OSPI_IRQ 124 #define VERSAL_SD0_IRQ_0 126 #define VERSAL_EFUSE_IRQ 139 +#define VERSAL_TRNG_IRQ 141 #define VERSAL_RTC_ALARM_IRQ 142 #define VERSAL_RTC_SECONDS_IRQ 143 @@ -163,6 +182,11 @@ struct Versal { #define MM_UART1 0xff010000U #define MM_UART1_SIZE 0x10000 +#define MM_CANFD0 0xff060000U +#define MM_CANFD0_SIZE 0x10000 +#define MM_CANFD1 0xff070000U +#define MM_CANFD1_SIZE 0x10000 + #define MM_GEM0 0xff0c0000U #define MM_GEM0_SIZE 0x10000 #define MM_GEM1 0xff0d0000U @@ -228,8 +252,86 @@ struct Versal { #define MM_PMC_EFUSE_CACHE 0xf1250000 #define MM_PMC_EFUSE_CACHE_SIZE 0x00C00 +#define MM_PMC_CFU_APB 0xf12b0000 +#define MM_PMC_CFU_APB_SIZE 0x10000 +#define MM_PMC_CFU_STREAM 0xf12c0000 +#define MM_PMC_CFU_STREAM_SIZE 0x1000 +#define MM_PMC_CFU_SFR 0xf12c1000 +#define MM_PMC_CFU_SFR_SIZE 0x1000 +#define MM_PMC_CFU_FDRO 0xf12c2000 +#define MM_PMC_CFU_FDRO_SIZE 0x1000 +#define MM_PMC_CFU_STREAM_2 0xf1f80000 +#define MM_PMC_CFU_STREAM_2_SIZE 0x40000 + +#define MM_PMC_CFRAME0_REG 0xf12d0000 +#define MM_PMC_CFRAME0_REG_SIZE 0x1000 +#define MM_PMC_CFRAME0_FDRI 0xf12d1000 +#define MM_PMC_CFRAME0_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME1_REG 0xf12d2000 +#define MM_PMC_CFRAME1_REG_SIZE 0x1000 +#define MM_PMC_CFRAME1_FDRI 0xf12d3000 +#define MM_PMC_CFRAME1_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME2_REG 0xf12d4000 +#define MM_PMC_CFRAME2_REG_SIZE 0x1000 +#define MM_PMC_CFRAME2_FDRI 0xf12d5000 +#define MM_PMC_CFRAME2_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME3_REG 0xf12d6000 +#define MM_PMC_CFRAME3_REG_SIZE 0x1000 +#define MM_PMC_CFRAME3_FDRI 0xf12d7000 +#define MM_PMC_CFRAME3_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME4_REG 0xf12d8000 +#define MM_PMC_CFRAME4_REG_SIZE 0x1000 +#define MM_PMC_CFRAME4_FDRI 0xf12d9000 +#define MM_PMC_CFRAME4_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME5_REG 0xf12da000 +#define MM_PMC_CFRAME5_REG_SIZE 0x1000 +#define MM_PMC_CFRAME5_FDRI 0xf12db000 +#define MM_PMC_CFRAME5_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME6_REG 0xf12dc000 +#define MM_PMC_CFRAME6_REG_SIZE 0x1000 +#define MM_PMC_CFRAME6_FDRI 0xf12dd000 +#define MM_PMC_CFRAME6_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME7_REG 0xf12de000 +#define MM_PMC_CFRAME7_REG_SIZE 0x1000 +#define MM_PMC_CFRAME7_FDRI 0xf12df000 +#define MM_PMC_CFRAME7_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME8_REG 0xf12e0000 +#define MM_PMC_CFRAME8_REG_SIZE 0x1000 +#define MM_PMC_CFRAME8_FDRI 0xf12e1000 +#define MM_PMC_CFRAME8_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME9_REG 0xf12e2000 +#define MM_PMC_CFRAME9_REG_SIZE 0x1000 +#define MM_PMC_CFRAME9_FDRI 0xf12e3000 +#define MM_PMC_CFRAME9_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME10_REG 0xf12e4000 +#define MM_PMC_CFRAME10_REG_SIZE 0x1000 +#define MM_PMC_CFRAME10_FDRI 0xf12e5000 +#define MM_PMC_CFRAME10_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME11_REG 0xf12e6000 +#define MM_PMC_CFRAME11_REG_SIZE 0x1000 +#define MM_PMC_CFRAME11_FDRI 0xf12e7000 +#define MM_PMC_CFRAME11_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME12_REG 0xf12e8000 +#define MM_PMC_CFRAME12_REG_SIZE 0x1000 +#define MM_PMC_CFRAME12_FDRI 0xf12e9000 +#define MM_PMC_CFRAME12_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME13_REG 0xf12ea000 +#define MM_PMC_CFRAME13_REG_SIZE 0x1000 +#define MM_PMC_CFRAME13_FDRI 0xf12eb000 +#define MM_PMC_CFRAME13_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME14_REG 0xf12ec000 +#define MM_PMC_CFRAME14_REG_SIZE 0x1000 +#define MM_PMC_CFRAME14_FDRI 0xf12ed000 +#define MM_PMC_CFRAME14_FDRI_SIZE 0x1000 +#define MM_PMC_CFRAME_BCAST_REG 0xf12ee000 +#define MM_PMC_CFRAME_BCAST_REG_SIZE 0x1000 +#define MM_PMC_CFRAME_BCAST_FDRI 0xf12ef000 +#define MM_PMC_CFRAME_BCAST_FDRI_SIZE 0x1000 + #define MM_PMC_CRP 0xf1260000U #define MM_PMC_CRP_SIZE 0x10000 #define MM_PMC_RTC 0xf12a0000 #define MM_PMC_RTC_SIZE 0x10000 +#define MM_PMC_TRNG 0xf1230000 +#define MM_PMC_TRNG_SIZE 0x10000 #endif diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index 85fd9f53da..48f7948092 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -18,12 +18,11 @@ #ifndef XLNX_ZYNQMP_H #define XLNX_ZYNQMP_H -#include "hw/arm/boot.h" #include "hw/intc/arm_gic.h" #include "hw/net/cadence_gem.h" #include "hw/char/cadence_uart.h" #include "hw/net/xlnx-zynqmp-can.h" -#include "hw/ide/ahci.h" +#include "hw/ide/ahci-sysbus.h" #include "hw/sd/sdhci.h" #include "hw/ssi/xilinx_spips.h" #include "hw/dma/xlnx_dpdma.h" @@ -42,6 +41,7 @@ #include "hw/misc/xlnx-zynqmp-apu-ctrl.h" #include "hw/misc/xlnx-zynqmp-crf.h" #include "hw/timer/cadence_ttc.h" +#include "hw/usb/hcd-dwc3.h" #define TYPE_XLNX_ZYNQMP "xlnx-zynqmp" OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP) @@ -56,6 +56,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP) #define XLNX_ZYNQMP_NUM_SPIS 2 #define XLNX_ZYNQMP_NUM_GDMA_CH 8 #define XLNX_ZYNQMP_NUM_ADMA_CH 8 +#define XLNX_ZYNQMP_NUM_USB 2 #define XLNX_ZYNQMP_NUM_QSPI_BUS 2 #define XLNX_ZYNQMP_NUM_QSPI_BUS_CS 2 @@ -128,10 +129,11 @@ struct XlnxZynqMPState { XlnxZDMA gdma[XLNX_ZYNQMP_NUM_GDMA_CH]; XlnxZDMA adma[XLNX_ZYNQMP_NUM_ADMA_CH]; XlnxCSUDMA qspi_dma; - qemu_or_irq qspi_irq_orgate; + OrIRQState qspi_irq_orgate; XlnxZynqMPAPUCtrl apu_ctrl; XlnxZynqMPCRF crf; CadenceTTCState ttc[XLNX_ZYNQMP_NUM_TTC]; + USBDWC3 usb[XLNX_ZYNQMP_NUM_USB]; char *boot_cpu; ARMCPU *boot_cpu_ptr; diff --git a/include/hw/audio/asc.h b/include/hw/audio/asc.h new file mode 100644 index 0000000000..04fac270b6 --- /dev/null +++ b/include/hw/audio/asc.h @@ -0,0 +1,85 @@ +/* + * QEMU Apple Sound Chip emulation + * + * Apple Sound Chip (ASC) 344S0063 + * Enhanced Apple Sound Chip (EASC) 343S1063 + * + * Copyright (c) 2012-2018 Laurent Vivier + * Copyright (c) 2022 Mark Cave-Ayland + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_AUDIO_ASC_H +#define HW_AUDIO_ASC_H + +#include "hw/sysbus.h" +#include "audio/audio.h" + +#define ASC_FREQ 22257 + +enum { + ASC_TYPE_ASC = 0, /* original discrete Apple Sound Chip */ + ASC_TYPE_EASC = 1 /* discrete Enhanced Apple Sound Chip */ +}; + +#define ASC_FIFO_OFFSET 0x0 +#define ASC_FIFO_SIZE 0x400 + +#define ASC_REG_OFFSET 0x800 +#define ASC_REG_SIZE 0x60 + +#define ASC_EXTREG_OFFSET 0xf00 +#define ASC_EXTREG_SIZE 0x20 + +typedef struct ASCFIFOState { + int index; + + MemoryRegion mem_fifo; + uint8_t fifo[ASC_FIFO_SIZE]; + uint8_t int_status; + + int cnt; + int wptr; + int rptr; + + MemoryRegion mem_extregs; + uint8_t extregs[ASC_EXTREG_SIZE]; + + int xa_cnt; + uint8_t xa_val; + uint8_t xa_flags; + int16_t xa_last[2]; +} ASCFIFOState; + +struct ASCState { + SysBusDevice parent_obj; + + uint8_t type; + MemoryRegion asc; + MemoryRegion mem_fifo; + MemoryRegion mem_regs; + MemoryRegion mem_extregs; + + QEMUSoundCard card; + SWVoiceOut *voice; + uint8_t *mixbuf; + int samples; + int shift; + + uint8_t *silentbuf; + + /* Time when we were last able to generate samples */ + int64_t fifo_empty_ns; + + qemu_irq irq; + + ASCFIFOState fifos[2]; + + uint8_t regs[ASC_REG_SIZE]; +}; + +#define TYPE_ASC "apple-sound-chip" +OBJECT_DECLARE_SIMPLE_TYPE(ASCState, ASC) + +#endif diff --git a/include/hw/audio/pcspk.h b/include/hw/audio/pcspk.h index 9506179587..6be75a6b86 100644 --- a/include/hw/audio/pcspk.h +++ b/include/hw/audio/pcspk.h @@ -25,16 +25,6 @@ #ifndef HW_PCSPK_H #define HW_PCSPK_H -#include "hw/isa/isa.h" -#include "hw/qdev-properties.h" -#include "qapi/error.h" - #define TYPE_PC_SPEAKER "isa-pcspk" -static inline void pcspk_init(ISADevice *isadev, ISABus *bus, ISADevice *pit) -{ - object_property_set_link(OBJECT(isadev), "pit", OBJECT(pit), NULL); - isa_realize_and_unref(isadev, bus, &error_fatal); -} - #endif /* HW_PCSPK_H */ diff --git a/include/hw/audio/soundhw.h b/include/hw/audio/soundhw.h index 270717a06a..474c5ff94e 100644 --- a/include/hw/audio/soundhw.h +++ b/include/hw/audio/soundhw.h @@ -8,6 +8,6 @@ void deprecated_register_soundhw(const char *name, const char *descr, void soundhw_init(void); void show_valid_soundhw(void); -void select_soundhw(const char *optarg, const char *audiodev); +void select_soundhw(const char *name, const char *audiodev); #endif diff --git a/include/hw/audio/virtio-snd.h b/include/hw/audio/virtio-snd.h new file mode 100644 index 0000000000..8dafedb276 --- /dev/null +++ b/include/hw/audio/virtio-snd.h @@ -0,0 +1,250 @@ +/* + * VIRTIO Sound Device conforming to + * + * "Virtual I/O Device (VIRTIO) Version 1.2 + * Committee Specification Draft 01 + * 09 May 2022" + * + * Copyright (c) 2023 Emmanouil Pitsidianakis + * Copyright (C) 2019 OpenSynergy GmbH + * + * 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. + */ + +#ifndef QEMU_VIRTIO_SOUND_H +#define QEMU_VIRTIO_SOUND_H + +#include "hw/virtio/virtio.h" +#include "audio/audio.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_snd.h" + +#define TYPE_VIRTIO_SND "virtio-sound-device" +#define VIRTIO_SND(obj) \ + OBJECT_CHECK(VirtIOSound, (obj), TYPE_VIRTIO_SND) + +/* CONFIGURATION SPACE */ + +typedef struct virtio_snd_config virtio_snd_config; + +/* COMMON DEFINITIONS */ + +/* common header for request/response*/ +typedef struct virtio_snd_hdr virtio_snd_hdr; + +/* event notification */ +typedef struct virtio_snd_event virtio_snd_event; + +/* common control request to query an item information */ +typedef struct virtio_snd_query_info virtio_snd_query_info; + +/* JACK CONTROL MESSAGES */ + +typedef struct virtio_snd_jack_hdr virtio_snd_jack_hdr; + +/* jack information structure */ +typedef struct virtio_snd_jack_info virtio_snd_jack_info; + +/* jack remapping control request */ +typedef struct virtio_snd_jack_remap virtio_snd_jack_remap; + +/* + * PCM CONTROL MESSAGES + */ +typedef struct virtio_snd_pcm_hdr virtio_snd_pcm_hdr; + +/* PCM stream info structure */ +typedef struct virtio_snd_pcm_info virtio_snd_pcm_info; + +/* set PCM stream params */ +typedef struct virtio_snd_pcm_set_params virtio_snd_pcm_set_params; + +/* I/O request header */ +typedef struct virtio_snd_pcm_xfer virtio_snd_pcm_xfer; + +/* I/O request status */ +typedef struct virtio_snd_pcm_status virtio_snd_pcm_status; + +/* device structs */ + +typedef struct VirtIOSound VirtIOSound; + +typedef struct VirtIOSoundPCMStream VirtIOSoundPCMStream; + +typedef struct virtio_snd_ctrl_command virtio_snd_ctrl_command; + +typedef struct VirtIOSoundPCM VirtIOSoundPCM; + +typedef struct VirtIOSoundPCMBuffer VirtIOSoundPCMBuffer; + +/* + * The VirtIO sound spec reuses layouts and values from the High Definition + * Audio spec (virtio/v1.2: 5.14 Sound Device). This struct handles each I/O + * message's buffer (virtio/v1.2: 5.14.6.8 PCM I/O Messages). + * + * In the case of TX (i.e. playback) buffers, we defer reading the raw PCM data + * from the virtqueue until QEMU's sound backsystem calls the output callback. + * This is tracked by the `bool populated;` field, which is set to true when + * data has been read into our own buffer for consumption. + * + * VirtIOSoundPCMBuffer has a dynamic size since it includes the raw PCM data + * in its allocation. It must be initialized and destroyed as follows: + * + * size_t size = [[derived from owned VQ element descriptor sizes]]; + * buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); + * buffer->elem = [[owned VQ element]]; + * + * [..] + * + * g_free(buffer->elem); + * g_free(buffer); + */ +struct VirtIOSoundPCMBuffer { + QSIMPLEQ_ENTRY(VirtIOSoundPCMBuffer) entry; + VirtQueueElement *elem; + VirtQueue *vq; + size_t size; + /* + * In TX / Plaback, `offset` represents the first unused position inside + * `data`. If `offset == size` then there are no unused data left. + */ + uint64_t offset; + /* Used for the TX queue for lazy I/O copy from `elem` */ + bool populated; + /* + * VirtIOSoundPCMBuffer is an unsized type because it ends with an array of + * bytes. The size of `data` is determined from the I/O message's read-only + * or write-only size when allocating VirtIOSoundPCMBuffer. + */ + uint8_t data[]; +}; + +struct VirtIOSoundPCM { + VirtIOSound *snd; + /* + * PCM parameters are a separate field instead of a VirtIOSoundPCMStream + * field, because the operation of PCM control requests is first + * VIRTIO_SND_R_PCM_SET_PARAMS and then VIRTIO_SND_R_PCM_PREPARE; this + * means that some times we get parameters without having an allocated + * stream yet. + */ + virtio_snd_pcm_set_params *pcm_params; + VirtIOSoundPCMStream **streams; +}; + +struct VirtIOSoundPCMStream { + VirtIOSoundPCM *pcm; + virtio_snd_pcm_info info; + virtio_snd_pcm_set_params params; + uint32_t id; + /* channel position values (VIRTIO_SND_CHMAP_XXX) */ + uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE]; + VirtIOSound *s; + bool flushing; + audsettings as; + union { + SWVoiceIn *in; + SWVoiceOut *out; + } voice; + QemuMutex queue_mutex; + bool active; + QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) queue; +}; + +/* + * PCM stream state machine. + * ------------------------- + * + * 5.14.6.6.1 PCM Command Lifecycle + * ================================ + * + * A PCM stream has the following command lifecycle: + * - `SET PARAMETERS` + * The driver negotiates the stream parameters (format, transport, etc) with + * the device. + * Possible valid transitions: `SET PARAMETERS`, `PREPARE`. + * - `PREPARE` + * The device prepares the stream (allocates resources, etc). + * Possible valid transitions: `SET PARAMETERS`, `PREPARE`, `START`, + * `RELEASE`. Output only: the driver transfers data for pre-buffing. + * - `START` + * The device starts the stream (unmute, putting into running state, etc). + * Possible valid transitions: `STOP`. + * The driver transfers data to/from the stream. + * - `STOP` + * The device stops the stream (mute, putting into non-running state, etc). + * Possible valid transitions: `START`, `RELEASE`. + * - `RELEASE` + * The device releases the stream (frees resources, etc). + * Possible valid transitions: `SET PARAMETERS`, `PREPARE`. + * + * +---------------+ +---------+ +---------+ +-------+ +-------+ + * | SetParameters | | Prepare | | Release | | Start | | Stop | + * +---------------+ +---------+ +---------+ +-------+ +-------+ + * |- | | | | + * || | | | | + * |< | | | | + * |------------->| | | | + * |<-------------| | | | + * | |- | | | + * | || | | | + * | |< | | | + * | |--------------------->| | + * | |---------->| | | + * | | | |-------->| + * | | | |<--------| + * | | |<-------------------| + * |<-------------------------| | | + * | |<----------| | | + * + * CTRL in the VirtIOSound device + * ============================== + * + * The control messages that affect the state of a stream arrive in the + * `virtio_snd_handle_ctrl()` queue callback and are of type `struct + * virtio_snd_ctrl_command`. They are stored in a queue field in the device + * type, `VirtIOSound`. This allows deferring the CTRL request completion if + * it's not immediately possible due to locking/state reasons. + * + * The CTRL message is finally handled in `process_cmd()`. + */ +struct VirtIOSound { + VirtIODevice parent_obj; + + VirtQueue *queues[VIRTIO_SND_VQ_MAX]; + uint64_t features; + VirtIOSoundPCM *pcm; + QEMUSoundCard card; + VMChangeStateEntry *vmstate; + virtio_snd_config snd_conf; + QemuMutex cmdq_mutex; + QTAILQ_HEAD(, virtio_snd_ctrl_command) cmdq; + bool processing_cmdq; + /* + * Convenience queue to keep track of invalid tx/rx queue messages inside + * the tx/rx callbacks. + * + * In the callbacks as a first step we are emptying the virtqueue to handle + * each message and we cannot add an invalid message back to the queue: we + * would re-process it in subsequent loop iterations. + * + * Instead, we add them to this queue and after finishing examining every + * virtqueue element, we inform the guest for each invalid message. + * + * This queue must be empty at all times except for inside the tx/rx + * callbacks. + */ + QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) invalid; +}; + +struct virtio_snd_ctrl_command { + VirtQueueElement *elem; + VirtQueue *vq; + virtio_snd_hdr ctrl; + virtio_snd_hdr resp; + size_t payload_size; + QTAILQ_ENTRY(virtio_snd_ctrl_command) next; +}; +#endif diff --git a/include/hw/block/block.h b/include/hw/block/block.h index 5902c0440a..de3946a5f1 100644 --- a/include/hw/block/block.h +++ b/include/hw/block/block.h @@ -31,6 +31,7 @@ typedef struct BlockConf { uint32_t lcyls, lheads, lsecs; OnOffAuto wce; bool share_rw; + OnOffAuto account_invalid, account_failed; BlockdevOnError rerror; BlockdevOnError werror; } BlockConf; @@ -61,7 +62,11 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) _conf.discard_granularity, -1), \ DEFINE_PROP_ON_OFF_AUTO("write-cache", _state, _conf.wce, \ ON_OFF_AUTO_AUTO), \ - DEFINE_PROP_BOOL("share-rw", _state, _conf.share_rw, false) + DEFINE_PROP_BOOL("share-rw", _state, _conf.share_rw, false), \ + DEFINE_PROP_ON_OFF_AUTO("account-invalid", _state, \ + _conf.account_invalid, ON_OFF_AUTO_AUTO), \ + DEFINE_PROP_ON_OFF_AUTO("account-failed", _state, \ + _conf.account_failed, ON_OFF_AUTO_AUTO) #define DEFINE_BLOCK_PROPERTIES(_state, _conf) \ DEFINE_PROP_DRIVE("drive", _state, _conf.blk), \ @@ -83,8 +88,8 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) /* Backend access helpers */ -bool blk_check_size_and_read_all(BlockBackend *blk, void *buf, hwaddr size, - Error **errp); +bool blk_check_size_and_read_all(BlockBackend *blk, DeviceState *dev, + void *buf, hwaddr size, Error **errp); /* Configuration helpers */ diff --git a/include/hw/block/fdc.h b/include/hw/block/fdc.h index 1ecca7cac7..c367c5efea 100644 --- a/include/hw/block/fdc.h +++ b/include/hw/block/fdc.h @@ -10,11 +10,13 @@ #define TYPE_ISA_FDC "isa-fdc" void isa_fdc_init_drives(ISADevice *fdc, DriveInfo **fds); -void fdctrl_init_sysbus(qemu_irq irq, int dma_chann, - hwaddr mmio_base, DriveInfo **fds); +void fdctrl_init_sysbus(qemu_irq irq, hwaddr mmio_base, DriveInfo **fds); void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base, DriveInfo **fds, qemu_irq *fdc_tc); +void isa_fdc_set_iobase(ISADevice *fdc, hwaddr iobase); +void isa_fdc_set_enabled(ISADevice *fdc, bool enabled); + FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i); int cmos_get_fd_drive_type(FloppyDriveType fd0); diff --git a/include/hw/block/flash.h b/include/hw/block/flash.h index 86d8363bb0..2b5ccd92f4 100644 --- a/include/hw/block/flash.h +++ b/include/hw/block/flash.h @@ -53,22 +53,22 @@ void nand_setio(DeviceState *dev, uint32_t value); uint32_t nand_getio(DeviceState *dev); uint32_t nand_getbuswidth(DeviceState *dev); -#define NAND_MFR_TOSHIBA 0x98 -#define NAND_MFR_SAMSUNG 0xec -#define NAND_MFR_FUJITSU 0x04 -#define NAND_MFR_NATIONAL 0x8f -#define NAND_MFR_RENESAS 0x07 -#define NAND_MFR_STMICRO 0x20 -#define NAND_MFR_HYNIX 0xad -#define NAND_MFR_MICRON 0x2c +#define NAND_MFR_TOSHIBA 0x98 +#define NAND_MFR_SAMSUNG 0xec +#define NAND_MFR_FUJITSU 0x04 +#define NAND_MFR_NATIONAL 0x8f +#define NAND_MFR_RENESAS 0x07 +#define NAND_MFR_STMICRO 0x20 +#define NAND_MFR_HYNIX 0xad +#define NAND_MFR_MICRON 0x2c /* onenand.c */ void *onenand_raw_otp(DeviceState *onenand_device); /* ecc.c */ typedef struct { - uint8_t cp; /* Column parity */ - uint16_t lp[2]; /* Line parity */ + uint8_t cp; /* Column parity */ + uint16_t lp[2]; /* Line parity */ uint16_t count; } ECCState; @@ -76,4 +76,10 @@ uint8_t ecc_digest(ECCState *s, uint8_t sample); void ecc_reset(ECCState *s); extern const VMStateDescription vmstate_ecc_state; +/* m25p80.c */ + +#define TYPE_M25P80 "m25p80-generic" + +BlockBackend *m25p80_get_blk(DeviceState *dev); + #endif diff --git a/include/hw/block/swim.h b/include/hw/block/swim.h index c1bd5f6555..5f567e8d59 100644 --- a/include/hw/block/swim.h +++ b/include/hw/block/swim.h @@ -11,6 +11,7 @@ #ifndef SWIM_H #define SWIM_H +#include "hw/block/block.h" #include "hw/sysbus.h" #include "qom/object.h" @@ -42,25 +43,22 @@ typedef struct FDrive { } FDrive; struct SWIMCtrl { - MemoryRegion iomem; + MemoryRegion swim; + MemoryRegion iwm; + MemoryRegion ism; FDrive drives[SWIM_MAX_FD]; int mode; /* IWM mode */ int iwm_switch; - uint16_t regs[8]; -#define IWM_PH0 0 -#define IWM_PH1 1 -#define IWM_PH2 2 -#define IWM_PH3 3 -#define IWM_MTR 4 -#define IWM_DRIVE 5 -#define IWM_Q6 6 -#define IWM_Q7 7 - uint8_t iwm_data; - uint8_t iwm_mode; + uint8_t iwm_latches; + uint8_t iwmregs[8]; /* SWIM mode */ + uint8_t ismregs[16]; uint8_t swim_phase; uint8_t swim_mode; + uint8_t swim_status; + uint8_t pram[16]; + uint8_t pram_idx; SWIMBus bus; }; diff --git a/include/hw/boards.h b/include/hw/boards.h index fa57bac4fb..8b8f6d5c00 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -6,7 +6,6 @@ #include "exec/memory.h" #include "sysemu/hostmem.h" #include "sysemu/blockdev.h" -#include "qemu/accel.h" #include "qapi/qapi-types-machine.h" #include "qemu/module.h" #include "qom/object.h" @@ -25,6 +24,13 @@ OBJECT_DECLARE_TYPE(MachineState, MachineClass, MACHINE) extern MachineState *current_machine; +/** + * machine_class_default_cpu_type: Return the machine default CPU type. + * @mc: Machine class + */ +const char *machine_class_default_cpu_type(MachineClass *mc); + +void machine_add_audiodev_property(MachineClass *mc); void machine_run_board_init(MachineState *machine, const char *mem_path, Error **errp); bool machine_usb(MachineState *machine); int machine_phandle_start(MachineState *machine); @@ -36,6 +42,9 @@ void machine_set_cpu_numa_node(MachineState *machine, Error **errp); void machine_parse_smp_config(MachineState *ms, const SMPConfiguration *config, Error **errp); +unsigned int machine_topo_get_cores_per_socket(const MachineState *ms); +unsigned int machine_topo_get_threads_per_socket(const MachineState *ms); +void machine_memory_devices_init(MachineState *ms, hwaddr base, uint64_t size); /** * machine_class_allow_dynamic_sysbus_dev: Add type to list of valid devices @@ -111,7 +120,7 @@ typedef struct CPUArchId { uint64_t arch_id; int64_t vcpus_count; CpuInstanceProperties props; - Object *cpu; + CPUState *cpu; const char *type; } CPUArchId; @@ -130,11 +139,18 @@ typedef struct { * @prefer_sockets - whether sockets are preferred over cores in smp parsing * @dies_supported - whether dies are supported by the machine * @clusters_supported - whether clusters are supported by the machine + * @has_clusters - whether clusters are explicitly specified in the user + * provided SMP configuration + * @books_supported - whether books are supported by the machine + * @drawers_supported - whether drawers are supported by the machine */ typedef struct { bool prefer_sockets; bool dies_supported; bool clusters_supported; + bool has_clusters; + bool books_supported; + bool drawers_supported; } SMPCompatProps; /** @@ -153,7 +169,7 @@ typedef struct { * any actions to be performed by hotplug handler. * @cpu_index_to_instance_props: * used to provide @cpu_index to socket/core/thread number mapping, allowing - * legacy code to perform maping from cpu_index to topology properties + * legacy code to perform mapping from cpu_index to topology properties * Returns: tuple of socket/core/thread ids given cpu_index belongs to. * used to provide @cpu_index to socket number mapping, allowing * a machine to group CPU threads belonging to the same socket/package @@ -206,10 +222,10 @@ typedef struct { * 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 + * Specifies initial 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. + * It also will be used as a way to option 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: @@ -231,7 +247,7 @@ struct MachineClass { const char *deprecation_reason; void (*init)(MachineState *state); - void (*reset)(MachineState *state); + void (*reset)(MachineState *state, ShutdownCause reason); void (*wakeup)(MachineState *state); int (*kvm_type)(MachineState *machine, const char *arg); @@ -251,6 +267,7 @@ struct MachineClass { const char *default_machine_opts; const char *default_boot_order; const char *default_display; + const char *default_nic; GPtrArray *compat_props; const char *hw_version; ram_addr_t default_ram_size; @@ -262,16 +279,16 @@ struct MachineClass { bool has_hotpluggable_cpus; bool ignore_memory_transaction_failures; int numa_mem_align_shift; - const char **valid_cpu_types; + const char * const *valid_cpu_types; strList *allowed_dynamic_sysbus_devices; bool auto_enable_numa_with_memhp; bool auto_enable_numa_with_memdev; bool ignore_boot_device_suffixes; bool smbus_no_migration_support; bool nvdimm_supported; - bool cxl_supported; bool numa_mem_supported; bool auto_enable_numa; + bool cpu_cluster_has_numa_boundary; SMPCompatProps smp_props; const char *default_ram_id; @@ -290,17 +307,35 @@ struct MachineClass { * DeviceMemoryState: * @base: address in guest physical address space where the memory * address space for memory devices starts - * @mr: address space container for memory devices + * @mr: memory region container for memory devices + * @as: address space for memory devices + * @listener: memory listener used to track used memslots in the address space + * @dimm_size: the sum of plugged DIMMs' sizes + * @used_region_size: the part of @mr already used by memory devices + * @required_memslots: the number of memslots required by memory devices + * @used_memslots: the number of memslots currently used by memory devices + * @memslot_auto_decision_active: whether any plugged memory device + * automatically decided to use more than + * one memslot */ typedef struct DeviceMemoryState { hwaddr base; MemoryRegion mr; + AddressSpace as; + MemoryListener listener; + uint64_t dimm_size; + uint64_t used_region_size; + unsigned int required_memslots; + unsigned int used_memslots; + unsigned int memslot_auto_decision_active; } DeviceMemoryState; /** * CpuTopology: * @cpus: the number of present logical processors on the machine - * @sockets: the number of sockets on the machine + * @drawers: the number of drawers on the machine + * @books: the number of books in one drawer + * @sockets: the number of sockets in one book * @dies: the number of dies in one socket * @clusters: the number of clusters in one die * @cores: the number of cores in one cluster @@ -309,6 +344,8 @@ typedef struct DeviceMemoryState { */ typedef struct CpuTopology { unsigned int cpus; + unsigned int drawers; + unsigned int books; unsigned int sockets; unsigned int dies; unsigned int clusters; @@ -348,6 +385,14 @@ struct MachineState { MemoryRegion *ram; DeviceMemoryState *device_memory; + /* + * Included in MachineState for simplicity, but not supported + * unless machine_add_audiodev_property is called. Boards + * that have embedded audio devices can call it from the + * machine init function and forward the property to the device. + */ + char *audiodev; + ram_addr_t ram_size; ram_addr_t maxram_size; uint64_t ram_slots; @@ -360,7 +405,6 @@ struct MachineState { CPUArchIdList *possible_cpus; CpuTopology smp; struct NVDIMMState *nvdimms_state; - struct CXLState *cxl_devices_state; struct NumaState *numa_state; }; @@ -381,6 +425,21 @@ struct MachineState { } \ type_init(machine_initfn##_register_types) +extern GlobalProperty hw_compat_8_2[]; +extern const size_t hw_compat_8_2_len; + +extern GlobalProperty hw_compat_8_1[]; +extern const size_t hw_compat_8_1_len; + +extern GlobalProperty hw_compat_8_0[]; +extern const size_t hw_compat_8_0_len; + +extern GlobalProperty hw_compat_7_2[]; +extern const size_t hw_compat_7_2_len; + +extern GlobalProperty hw_compat_7_1[]; +extern const size_t hw_compat_7_1_len; + extern GlobalProperty hw_compat_7_0[]; extern const size_t hw_compat_7_0_len; diff --git a/include/hw/char/avr_usart.h b/include/hw/char/avr_usart.h index 62eaa1528e..0cc599e9b1 100644 --- a/include/hw/char/avr_usart.h +++ b/include/hw/char/avr_usart.h @@ -34,7 +34,7 @@ #define USART_BRRH 0x05 #define USART_BRRL 0x04 -/* Relevant bits in regiters. */ +/* Relevant bits in registers. */ #define USART_CSRA_RXC (1 << 7) #define USART_CSRA_TXC (1 << 6) #define USART_CSRA_DRE (1 << 5) diff --git a/include/hw/char/cmsdk-apb-uart.h b/include/hw/char/cmsdk-apb-uart.h index 9daff0eeee..7de8f8d1b9 100644 --- a/include/hw/char/cmsdk-apb-uart.h +++ b/include/hw/char/cmsdk-apb-uart.h @@ -12,7 +12,6 @@ #ifndef CMSDK_APB_UART_H #define CMSDK_APB_UART_H -#include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "chardev/char-fe.h" #include "qom/object.h" @@ -44,36 +43,4 @@ struct CMSDKAPBUART { uint8_t rxbuf; }; -/** - * cmsdk_apb_uart_create - convenience function to create TYPE_CMSDK_APB_UART - * @addr: location in system memory to map registers - * @chr: Chardev backend to connect UART to, or NULL if no backend - * @pclk_frq: frequency in Hz of the PCLK clock (used for calculating baud rate) - */ -static inline DeviceState *cmsdk_apb_uart_create(hwaddr addr, - qemu_irq txint, - qemu_irq rxint, - qemu_irq txovrint, - qemu_irq rxovrint, - qemu_irq uartint, - Chardev *chr, - uint32_t pclk_frq) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_new(TYPE_CMSDK_APB_UART); - s = SYS_BUS_DEVICE(dev); - qdev_prop_set_chr(dev, "chardev", chr); - qdev_prop_set_uint32(dev, "pclk-frq", pclk_frq); - sysbus_realize_and_unref(s, &error_fatal); - sysbus_mmio_map(s, 0, addr); - sysbus_connect_irq(s, 0, txint); - sysbus_connect_irq(s, 1, rxint); - sysbus_connect_irq(s, 2, txovrint); - sysbus_connect_irq(s, 3, rxovrint); - sysbus_connect_irq(s, 4, uartint); - return dev; -} - #endif diff --git a/include/hw/char/escc.h b/include/hw/char/escc.h index 7e9482dee2..5669a5b811 100644 --- a/include/hw/char/escc.h +++ b/include/hw/char/escc.h @@ -45,6 +45,7 @@ typedef struct ESCCChannelState { ESCCChnType type; uint8_t rx, tx; QemuInputHandlerState *hs; + char *sunkbd_layout; } ESCCChannelState; struct ESCCState { diff --git a/include/hw/char/goldfish_tty.h b/include/hw/char/goldfish_tty.h index 7503d2fa1e..d59733e5ae 100644 --- a/include/hw/char/goldfish_tty.h +++ b/include/hw/char/goldfish_tty.h @@ -12,6 +12,7 @@ #include "qemu/fifo8.h" #include "chardev/char-fe.h" +#include "hw/sysbus.h" #define TYPE_GOLDFISH_TTY "goldfish_tty" OBJECT_DECLARE_SIMPLE_TYPE(GoldfishTTYState, GOLDFISH_TTY) diff --git a/migration/qemu-file-channel.h b/include/hw/char/grlib_uart.h similarity index 80% rename from migration/qemu-file-channel.h rename to include/hw/char/grlib_uart.h index 0028a09eb6..7496f8fd5e 100644 --- a/migration/qemu-file-channel.h +++ b/include/hw/char/grlib_uart.h @@ -1,7 +1,9 @@ /* - * QEMUFile backend for QIOChannel objects + * QEMU GRLIB UART * - * Copyright (c) 2015-2016 Red Hat, Inc + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2024 AdaCore * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,11 +24,9 @@ * THE SOFTWARE. */ -#ifndef QEMU_FILE_CHANNEL_H -#define QEMU_FILE_CHANNEL_H +#ifndef GRLIB_UART_H +#define GRLIB_UART_H -#include "io/channel.h" +#define TYPE_GRLIB_APB_UART "grlib-apbuart" -QEMUFile *qemu_fopen_channel_input(QIOChannel *ioc); -QEMUFile *qemu_fopen_channel_output(QIOChannel *ioc); #endif diff --git a/include/hw/char/ibex_uart.h b/include/hw/char/ibex_uart.h index a39985516a..9deadf223b 100644 --- a/include/hw/char/ibex_uart.h +++ b/include/hw/char/ibex_uart.h @@ -26,7 +26,6 @@ #define HW_IBEX_UART_H #include "hw/sysbus.h" -#include "hw/registerfields.h" #include "chardev/char-fe.h" #include "qemu/timer.h" #include "qom/object.h" diff --git a/include/hw/char/imx_serial.h b/include/hw/char/imx_serial.h index 91c9894ad5..65f0e97c76 100644 --- a/include/hw/char/imx_serial.h +++ b/include/hw/char/imx_serial.h @@ -21,12 +21,16 @@ #include "hw/sysbus.h" #include "chardev/char-fe.h" #include "qom/object.h" +#include "qemu/fifo32.h" #define TYPE_IMX_SERIAL "imx.serial" OBJECT_DECLARE_SIMPLE_TYPE(IMXSerialState, IMX_SERIAL) +#define FIFO_SIZE 32 + #define URXD_CHARRDY (1<<15) /* character read is valid */ #define URXD_ERR (1<<14) /* Character has error */ +#define URXD_OVRRUN (1<<13) /* 32nd character in RX FIFO */ #define URXD_FRMERR (1<<12) /* Character has frame error */ #define URXD_BRK (1<<11) /* Break received */ @@ -65,25 +69,40 @@ OBJECT_DECLARE_SIMPLE_TYPE(IMXSerialState, IMX_SERIAL) #define UCR1_TXMPTYEN (1<<6) /* Tx Empty Interrupt Enable */ #define UCR1_UARTEN (1<<0) /* UART Enable */ +#define UCR2_ATEN (1<<3) /* Ageing Timer Enable */ #define UCR2_TXEN (1<<2) /* Transmitter enable */ #define UCR2_RXEN (1<<1) /* Receiver enable */ #define UCR2_SRST (1<<0) /* Reset complete */ #define UCR4_DREN BIT(0) /* Receive Data Ready interrupt enable */ +#define UCR4_OREN BIT(1) /* Overrun interrupt enable */ #define UCR4_TCEN BIT(3) /* TX complete interrupt enable */ +#define UCR4_WKEN BIT(7) /* WAKE interrupt enable */ #define UTS1_TXEMPTY (1<<6) #define UTS1_RXEMPTY (1<<5) #define UTS1_TXFULL (1<<4) #define UTS1_RXFULL (1<<3) +#define TL_MASK 0x3f + + /* Bit time in nanoseconds assuming maximum baud rate of 115200 */ +#define BIT_TIME_NS 8681 + +/* Assume 8 bits per character */ +#define NUM_BITS 8 + +/* Ageing timer triggers after 8 characters */ +#define AGE_DURATION_NS (8 * NUM_BITS * BIT_TIME_NS) + struct IMXSerialState { /*< private >*/ SysBusDevice parent_obj; /*< public >*/ MemoryRegion iomem; - int32_t readbuff; + QEMUTimer ageing_timer; + Fifo32 rx_fifo; uint32_t usr1; uint32_t usr2; diff --git a/include/hw/char/parallel-isa.h b/include/hw/char/parallel-isa.h new file mode 100644 index 0000000000..5284b2ffec --- /dev/null +++ b/include/hw/char/parallel-isa.h @@ -0,0 +1,35 @@ +/* + * QEMU ISA Parallel PORT emulation + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2007 Marko Kohtala + * + * SPDX-License-Identifier: MIT + */ + +#ifndef HW_PARALLEL_ISA_H +#define HW_PARALLEL_ISA_H + +#include "parallel.h" + +#include "exec/ioport.h" +#include "hw/isa/isa.h" +#include "qom/object.h" + +#define TYPE_ISA_PARALLEL "isa-parallel" +OBJECT_DECLARE_SIMPLE_TYPE(ISAParallelState, ISA_PARALLEL) + +struct ISAParallelState { + ISADevice parent_obj; + + uint32_t index; + uint32_t iobase; + uint32_t isairq; + ParallelState state; + PortioList portio_list; +}; + +void isa_parallel_set_iobase(ISADevice *parallel, hwaddr iobase); +void isa_parallel_set_enabled(ISADevice *parallel, bool enabled); + +#endif /* HW_PARALLEL_ISA_H */ diff --git a/include/hw/char/parallel.h b/include/hw/char/parallel.h index 0a23c0f57e..cfb97cc7cc 100644 --- a/include/hw/char/parallel.h +++ b/include/hw/char/parallel.h @@ -1,9 +1,28 @@ #ifndef HW_PARALLEL_H #define HW_PARALLEL_H +#include "exec/memory.h" #include "hw/isa/isa.h" +#include "hw/irq.h" +#include "chardev/char-fe.h" #include "chardev/char.h" +typedef struct ParallelState { + MemoryRegion iomem; + uint8_t dataw; + uint8_t datar; + uint8_t status; + uint8_t control; + qemu_irq irq; + int irq_pending; + CharBackend chr; + int hw_driver; + int epp_timeout; + uint32_t last_read_offset; /* For debugging */ + /* Memory-mapped interface */ + int it_shift; +} ParallelState; + void parallel_hds_isa_init(ISABus *bus, int n); bool parallel_mm_init(MemoryRegion *address_space, diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h index dc2c90eedc..d853802132 100644 --- a/include/hw/char/pl011.h +++ b/include/hw/char/pl011.h @@ -15,10 +15,8 @@ #ifndef HW_PL011_H #define HW_PL011_H -#include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "chardev/char-fe.h" -#include "qapi/error.h" #include "qom/object.h" #define TYPE_PL011 "pl011" @@ -27,6 +25,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(PL011State, PL011) /* This shares the same struct (and cast macro) as the base pl011 device */ #define TYPE_PL011_LUMINARY "pl011_luminary" +/* Depth of UART FIFO in bytes, when FIFO mode is enabled (else depth == 1) */ +#define PL011_FIFO_DEPTH 16 + struct PL011State { SysBusDevice parent_obj; @@ -39,7 +40,7 @@ struct PL011State { uint32_t dmacr; uint32_t int_enabled; uint32_t int_level; - uint32_t read_fifo[16]; + uint32_t read_fifo[PL011_FIFO_DEPTH]; uint32_t ilpr; uint32_t ibrd; uint32_t fbrd; @@ -54,38 +55,6 @@ struct PL011State { const unsigned char *id; }; -static inline DeviceState *pl011_create(hwaddr addr, - qemu_irq irq, - Chardev *chr) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_new("pl011"); - s = SYS_BUS_DEVICE(dev); - qdev_prop_set_chr(dev, "chardev", chr); - sysbus_realize_and_unref(s, &error_fatal); - sysbus_mmio_map(s, 0, addr); - sysbus_connect_irq(s, 0, irq); - - return dev; -} - -static inline DeviceState *pl011_luminary_create(hwaddr addr, - qemu_irq irq, - Chardev *chr) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_new("pl011_luminary"); - s = SYS_BUS_DEVICE(dev); - qdev_prop_set_chr(dev, "chardev", chr); - sysbus_realize_and_unref(s, &error_fatal); - sysbus_mmio_map(s, 0, addr); - sysbus_connect_irq(s, 0, irq); - - return dev; -} +DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr); #endif diff --git a/include/hw/char/riscv_htif.h b/include/hw/char/riscv_htif.h index f888ac1b30..df493fdf6b 100644 --- a/include/hw/char/riscv_htif.h +++ b/include/hw/char/riscv_htif.h @@ -23,7 +23,6 @@ #include "chardev/char.h" #include "chardev/char-fe.h" #include "exec/memory.h" -#include "target/riscv/cpu.h" #define TYPE_HTIF_UART "riscv.htif.uart" @@ -31,32 +30,25 @@ typedef struct HTIFState { int allow_tohost; int fromhost_inprogress; + uint64_t tohost; + uint64_t fromhost; hwaddr tohost_offset; hwaddr fromhost_offset; - uint64_t tohost_size; - uint64_t fromhost_size; MemoryRegion mmio; - MemoryRegion *address_space; - MemoryRegion *main_mem; - void *main_mem_ram_ptr; - CPURISCVState *env; CharBackend chr; uint64_t pending_read; } HTIFState; -extern const VMStateDescription vmstate_htif; -extern const MemoryRegionOps htif_io_ops; +extern const char *sig_file; +extern uint8_t line_size; /* HTIF symbol callback */ void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value, uint64_t st_size); -/* Check if HTIF uses ELF symbols */ -bool htif_uses_elf_symbols(void); - /* legacy pre qom */ -HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem, - CPURISCVState *env, Chardev *chr, uint64_t nonelf_base); +HTIFState *htif_mm_init(MemoryRegion *address_space, Chardev *chr, + uint64_t nonelf_base, bool custom_base); #endif diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h index 8ba7eca3d6..6e14099ee7 100644 --- a/include/hw/char/serial.h +++ b/include/hw/char/serial.h @@ -112,5 +112,7 @@ SerialMM *serial_mm_init(MemoryRegion *address_space, #define TYPE_ISA_SERIAL "isa-serial" void serial_hds_isa_init(ISABus *bus, int from, int to); +void isa_serial_set_iobase(ISADevice *serial, hwaddr iobase); +void isa_serial_set_enabled(ISADevice *serial, bool enabled); #endif diff --git a/include/hw/char/stm32f2xx_usart.h b/include/hw/char/stm32f2xx_usart.h index 65bcc85470..fdfa7424a7 100644 --- a/include/hw/char/stm32f2xx_usart.h +++ b/include/hw/char/stm32f2xx_usart.h @@ -48,10 +48,12 @@ #define USART_SR_TC (1 << 6) #define USART_SR_RXNE (1 << 5) -#define USART_CR1_UE (1 << 13) -#define USART_CR1_RXNEIE (1 << 5) -#define USART_CR1_TE (1 << 3) -#define USART_CR1_RE (1 << 2) +#define USART_CR1_UE (1 << 13) +#define USART_CR1_TXEIE (1 << 7) +#define USART_CR1_TCEIE (1 << 6) +#define USART_CR1_RXNEIE (1 << 5) +#define USART_CR1_TE (1 << 3) +#define USART_CR1_RE (1 << 2) #define TYPE_STM32F2XX_USART "stm32f2xx-usart" OBJECT_DECLARE_SIMPLE_TYPE(STM32F2XXUsartState, STM32F2XX_USART) diff --git a/include/hw/char/xilinx_uartlite.h b/include/hw/char/xilinx_uartlite.h index bb32d0fcb3..36d4e8444d 100644 --- a/include/hw/char/xilinx_uartlite.h +++ b/include/hw/char/xilinx_uartlite.h @@ -15,24 +15,9 @@ #ifndef XILINX_UARTLITE_H #define XILINX_UARTLITE_H -#include "hw/qdev-properties.h" -#include "hw/sysbus.h" +#include "qom/object.h" -static inline DeviceState *xilinx_uartlite_create(hwaddr addr, - qemu_irq irq, - Chardev *chr) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_new("xlnx.xps-uartlite"); - s = SYS_BUS_DEVICE(dev); - qdev_prop_set_chr(dev, "chardev", chr); - sysbus_realize_and_unref(s, &error_fatal); - sysbus_mmio_map(s, 0, addr); - sysbus_connect_irq(s, 0, irq); - - return dev; -} +#define TYPE_XILINX_UARTLITE "xlnx.xps-uartlite" +OBJECT_DECLARE_SIMPLE_TYPE(XilinxUARTLite, XILINX_UARTLITE) #endif diff --git a/include/hw/clock.h b/include/hw/clock.h index 5c927cee7f..eb58599131 100644 --- a/include/hw/clock.h +++ b/include/hw/clock.h @@ -204,7 +204,7 @@ static inline bool clock_set_ns(Clock *clk, unsigned ns) * Propagate the clock period that has been previously configured using * @clock_set(). This will update recursively all connected clocks. * It is an error to call this function on a clock which has a source. - * Note: this function must not be called during device inititialization + * Note: this function must not be called during device initialization * or migration. */ void clock_propagate(Clock *clk); @@ -357,6 +357,8 @@ char *clock_display_freq(Clock *clk); * @multiplier: multiplier value * @divider: divider value * + * @return: true if the clock is changed. + * * By default, a Clock's children will all run with the same period * as their parent. This function allows you to adjust the multiplier * and divider used to derive the child clock frequency. @@ -374,6 +376,6 @@ char *clock_display_freq(Clock *clk); * Note that this function does not call clock_propagate(); the * caller should do that if necessary. */ -void clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider); +bool clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider); #endif /* QEMU_HW_CLOCK_H */ diff --git a/include/hw/core/accel-cpu.h b/include/hw/core/accel-cpu.h index 5dbfd79955..24dad45ab9 100644 --- a/include/hw/core/accel-cpu.h +++ b/include/hw/core/accel-cpu.h @@ -32,7 +32,7 @@ typedef struct AccelCPUClass { void (*cpu_class_init)(CPUClass *cc); void (*cpu_instance_init)(CPUState *cpu); - bool (*cpu_realizefn)(CPUState *cpu, Error **errp); + bool (*cpu_target_realize)(CPUState *cpu, Error **errp); } AccelCPUClass; #endif /* ACCEL_CPU_H */ diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 996f94059f..ec14f74ce5 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -22,15 +22,15 @@ #include "hw/qdev-core.h" #include "disas/dis-asm.h" -#include "exec/cpu-common.h" #include "exec/hwaddr.h" +#include "exec/vaddr.h" #include "exec/memattrs.h" +#include "exec/tlb-common.h" #include "qapi/qapi-types-run-state.h" #include "qemu/bitmap.h" #include "qemu/rcu_queue.h" #include "qemu/queue.h" #include "qemu/thread.h" -#include "qemu/plugin.h" #include "qom/object.h" typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size, @@ -51,6 +51,13 @@ typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size, */ #define CPU(obj) ((CPUState *)(obj)) +/* + * The class checkers bring in CPU_GET_CLASS() which is potentially + * expensive given the eventual call to + * object_class_dynamic_cast_assert(). Because of this the CPUState + * has a cached value for the class in cs->cc which is set up in + * cpu_exec_realizefn() for use in hot code paths. + */ typedef struct CPUClass CPUClass; DECLARE_CLASS_CHECKERS(CPUClass, CPU, TYPE_CPU) @@ -77,13 +84,11 @@ typedef enum MMUAccessType { MMU_DATA_LOAD = 0, MMU_DATA_STORE = 1, MMU_INST_FETCH = 2 +#define MMU_ACCESS_COUNT 3 } MMUAccessType; typedef struct CPUWatchpoint CPUWatchpoint; -/* see tcg-cpu-ops.h */ -struct TCGCPUOps; - /* see accel-cpu.h */ struct AccelCPUClass; @@ -93,12 +98,17 @@ struct SysemuCPUOps; /** * CPUClass: * @class_by_name: Callback to map -cpu command line model name to an - * instantiatable CPU type. + * instantiatable CPU type. * @parse_features: Callback to parse command line arguments. * @reset_dump_flags: #CPUDumpFlags to use for reset logging. * @has_work: Callback for checking if there is work to do. + * @mmu_index: Callback for choosing softmmu mmu index; + * may be used internally by memory_rw_debug without TCG. * @memory_rw_debug: Callback for GDB memory access. * @dump_state: Callback for dumping state. + * @query_cpu_fast: + * Fill in target specific information for the "query-cpus-fast" + * QAPI call. * @get_arch_id: Callback for getting architecture-dependent CPU ID. * @set_pc: Callback for setting the Program Counter register. This * should have the semantics used by the target architecture when @@ -108,20 +118,20 @@ struct SysemuCPUOps; * If the target behaviour here is anything other than "set * the PC register to the value passed in" then the target must * also implement the synchronize_from_tb hook. + * @get_pc: Callback for getting the Program Counter register. + * As above, with the semantics of the target architecture. * @gdb_read_register: Callback for letting GDB read a register. * @gdb_write_register: Callback for letting GDB write a register. * @gdb_adjust_breakpoint: Callback for adjusting the address of a * breakpoint. Used by AVR to handle a gdb mis-feature with * its Harvard architecture split code and data. - * @gdb_num_core_regs: Number of core registers accessible to GDB. + * @gdb_num_core_regs: Number of core registers accessible to GDB or 0 to infer + * from @gdb_core_xml_file. * @gdb_core_xml_file: File name for core registers GDB XML description. * @gdb_stop_before_watchpoint: Indicates whether GDB expects the CPU to stop * before the insn which triggers a watchpoint rather than after it. * @gdb_arch_name: Optional callback that returns the architecture name known * to GDB. The caller must free the returned string with g_free. - * @gdb_get_dynamic_xml: Callback to return dynamically generated XML for the - * gdb stub. Returns a pointer to the XML contents for the specified XML file - * or NULL if the CPU doesn't have a dynamically generated content for it. * @disas_set_info: Setup architecture specific components of disassembly info * @adjust_watchpoint_address: Perform a target-specific adjustment to an * address before attempting to match it against watchpoints. @@ -139,18 +149,20 @@ struct CPUClass { void (*parse_features)(const char *typename, char *str, Error **errp); bool (*has_work)(CPUState *cpu); + int (*mmu_index)(CPUState *cpu, bool ifetch); int (*memory_rw_debug)(CPUState *cpu, vaddr addr, uint8_t *buf, int len, bool is_write); void (*dump_state)(CPUState *cpu, FILE *, int flags); + void (*query_cpu_fast)(CPUState *cpu, CpuInfoFast *value); int64_t (*get_arch_id)(CPUState *cpu); void (*set_pc)(CPUState *cpu, vaddr value); + vaddr (*get_pc)(CPUState *cpu); int (*gdb_read_register)(CPUState *cpu, GByteArray *buf, int reg); int (*gdb_write_register)(CPUState *cpu, uint8_t *buf, int reg); vaddr (*gdb_adjust_breakpoint)(CPUState *cpu, vaddr addr); const char *gdb_core_xml_file; - gchar * (*gdb_arch_name)(CPUState *cpu); - const char * (*gdb_get_dynamic_xml)(CPUState *cpu, const char *xmlname); + const gchar * (*gdb_arch_name)(CPUState *cpu); void (*disas_set_info)(CPUState *cpu, disassemble_info *info); @@ -161,7 +173,7 @@ struct CPUClass { const struct SysemuCPUOps *sysemu_ops; /* when TCG is not available, this pointer is NULL */ - const struct TCGCPUOps *tcg_ops; + const TCGCPUOps *tcg_ops; /* * if not NULL, this is called in order for the CPUClass to initialize @@ -177,6 +189,140 @@ struct CPUClass { bool gdb_stop_before_watchpoint; }; +/* + * Fix the number of mmu modes to 16, which is also the maximum + * supported by the softmmu tlb api. + */ +#define NB_MMU_MODES 16 + +/* Use a fully associative victim tlb of 8 entries. */ +#define CPU_VTLB_SIZE 8 + +/* + * The full TLB entry, which is not accessed by generated TCG code, + * so the layout is not as critical as that of CPUTLBEntry. This is + * also why we don't want to combine the two structs. + */ +typedef struct CPUTLBEntryFull { + /* + * @xlat_section contains: + * - in the lower TARGET_PAGE_BITS, a physical section number + * - with the lower TARGET_PAGE_BITS masked off, an offset which + * must be added to the virtual address to obtain: + * + the ram_addr_t of the target RAM (if the physical section + * number is PHYS_SECTION_NOTDIRTY or PHYS_SECTION_ROM) + * + the offset within the target MemoryRegion (otherwise) + */ + hwaddr xlat_section; + + /* + * @phys_addr contains the physical address in the address space + * given by cpu_asidx_from_attrs(cpu, @attrs). + */ + hwaddr phys_addr; + + /* @attrs contains the memory transaction attributes for the page. */ + MemTxAttrs attrs; + + /* @prot contains the complete protections for the page. */ + uint8_t prot; + + /* @lg_page_size contains the log2 of the page size. */ + uint8_t lg_page_size; + + /* Additional tlb flags requested by tlb_fill. */ + uint8_t tlb_fill_flags; + + /* + * Additional tlb flags for use by the slow path. If non-zero, + * the corresponding CPUTLBEntry comparator must have TLB_FORCE_SLOW. + */ + uint8_t slow_flags[MMU_ACCESS_COUNT]; + + /* + * Allow target-specific additions to this structure. + * This may be used to cache items from the guest cpu + * page tables for later use by the implementation. + */ + union { + /* + * Cache the attrs and shareability fields from the page table entry. + * + * For ARMMMUIdx_Stage2*, pte_attrs is the S2 descriptor bits [5:2]. + * Otherwise, pte_attrs is the same as the MAIR_EL1 8-bit format. + * For shareability and guarded, as in the SH and GP fields respectively + * of the VMSAv8-64 PTEs. + */ + struct { + uint8_t pte_attrs; + uint8_t shareability; + bool guarded; + } arm; + } extra; +} CPUTLBEntryFull; + +/* + * Data elements that are per MMU mode, minus the bits accessed by + * the TCG fast path. + */ +typedef struct CPUTLBDesc { + /* + * Describe a region covering all of the large pages allocated + * into the tlb. When any page within this region is flushed, + * we must flush the entire tlb. The region is matched if + * (addr & large_page_mask) == large_page_addr. + */ + vaddr large_page_addr; + vaddr large_page_mask; + /* host time (in ns) at the beginning of the time window */ + int64_t window_begin_ns; + /* maximum number of entries observed in the window */ + size_t window_max_entries; + size_t n_used_entries; + /* The next index to use in the tlb victim table. */ + size_t vindex; + /* The tlb victim table, in two parts. */ + CPUTLBEntry vtable[CPU_VTLB_SIZE]; + CPUTLBEntryFull vfulltlb[CPU_VTLB_SIZE]; + CPUTLBEntryFull *fulltlb; +} CPUTLBDesc; + +/* + * Data elements that are shared between all MMU modes. + */ +typedef struct CPUTLBCommon { + /* Serialize updates to f.table and d.vtable, and others as noted. */ + QemuSpin lock; + /* + * Within dirty, for each bit N, modifications have been made to + * mmu_idx N since the last time that mmu_idx was flushed. + * Protected by tlb_c.lock. + */ + uint16_t dirty; + /* + * Statistics. These are not lock protected, but are read and + * written atomically. This allows the monitor to print a snapshot + * of the stats without interfering with the cpu. + */ + size_t full_flush_count; + size_t part_flush_count; + size_t elide_flush_count; +} CPUTLBCommon; + +/* + * The entire softmmu tlb, for all MMU modes. + * The meaning of each of the MMU modes is defined in the target code. + * Since this is placed within CPUNegativeOffsetState, the smallest + * negative offsets are at the end of the struct. + */ +typedef struct CPUTLB { +#ifdef CONFIG_TCG + CPUTLBCommon c; + CPUTLBDesc d[NB_MMU_MODES]; + CPUTLBDescFast f[NB_MMU_MODES]; +#endif +} CPUTLB; + /* * Low 16 bits: number of cycles left, used only in icount mode. * High 16 bits: Set to -1 to force TCG to stop executing linked TBs @@ -197,6 +343,16 @@ typedef union IcountDecr { } u16; } IcountDecr; +/* + * Elements of CPUState most efficiently accessed from CPUArchState, + * via small negative offsets. + */ +typedef struct CPUNegativeOffsetState { + CPUTLB tlb; + IcountDecr icount_decr; + bool can_do_io; +} CPUNegativeOffsetState; + typedef struct CPUBreakpoint { vaddr pc; int flags; /* BP_* */ @@ -212,27 +368,9 @@ struct CPUWatchpoint { QTAILQ_ENTRY(CPUWatchpoint) entry; }; -#ifdef CONFIG_PLUGIN -/* - * For plugins we sometime need to save the resolved iotlb data before - * the memory regions get moved around by io_writex. - */ -typedef struct SavedIOTLB { - hwaddr addr; - MemoryRegionSection *section; - hwaddr mr_offset; -} SavedIOTLB; -#endif - struct KVMState; struct kvm_run; -struct hax_vcpu_state; -struct hvf_vcpu_state; - -#define TB_JMP_CACHE_BITS 12 -#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS) - /* work queue */ /* The union type allows passing of 64 bit target pointers on 32 bit @@ -256,7 +394,6 @@ typedef void (*run_on_cpu_func)(CPUState *cpu, run_on_cpu_data data); struct qemu_work_item; #define CPU_UNSET_NUMA_NODE_ID -1 -#define CPU_TRACE_DSTATE_MAX_EVENTS 32 /** * CPUState: @@ -266,9 +403,11 @@ struct qemu_work_item; * to a cluster this will be UNASSIGNED_CLUSTER_INDEX; otherwise it will * be the same as the cluster-id property of the CPU object's TYPE_CPU_CLUSTER * QOM parent. + * Under TCG this value is propagated to @tcg_cflags. + * See TranslationBlock::TCG CF_CLUSTER_MASK. * @tcg_cflags: Pre-computed cflags for this cpu. * @nr_cores: Number of cores within this CPU package. - * @nr_threads: Number of threads within this CPU. + * @nr_threads: Number of threads within this CPU core. * @running: #true if CPU is currently running (lockless). * @has_waiter: #true if a CPU is currently waiting for the cpu_exec_end; * valid under cpu_list_lock. @@ -281,29 +420,24 @@ struct qemu_work_item; * @crash_occurred: Indicates the OS reported a crash (panic) for this CPU * @singlestep_enabled: Flags for single-stepping. * @icount_extra: Instructions until next timer event. - * @can_do_io: Nonzero if memory-mapped IO is safe. Deterministic execution - * requires that IO only be performed on the last instruction of a TB - * so that interrupts take effect immediately. + * @neg.can_do_io: True if memory-mapped IO is allowed. * @cpu_ases: Pointer to array of CPUAddressSpaces (which define the * AddressSpaces this CPU has) * @num_ases: number of CPUAddressSpaces in @cpu_ases * @as: Pointer to the first AddressSpace, for the convenience of targets which * only have a single AddressSpace - * @env_ptr: Pointer to subclass-specific CPUArchState field. - * @icount_decr_ptr: Pointer to IcountDecr field within subclass. * @gdb_regs: Additional GDB registers. * @gdb_num_regs: Number of total registers accessible to GDB. * @gdb_num_g_regs: Number of registers in GDB 'g' packets. - * @next_cpu: Next CPU sharing TB cache. + * @node: QTAILQ of CPUs sharing TB cache. * @opaque: User data. * @mem_io_pc: Host Program Counter at which the memory was accessed. + * @accel: Pointer to accelerator specific state. * @kvm_fd: vCPU file descriptor for KVM. * @work_mutex: Lock to prevent multiple access to @work_list. * @work_list: List of pending asynchronous work. - * @trace_dstate_delayed: Delayed changes to trace_dstate (includes all changes - * to @trace_dstate). - * @trace_dstate: Dynamic tracing state of events for this vCPU (bitmask). - * @plugin_mask: Plugin event bitmap. Modified only via async work. + * @plugin_mem_cbs: active plugin memory callbacks + * @plugin_state: per-CPU plugin state * @ignore_memory_transaction_failures: Cached copy of the MachineState * flag of the same name: allows the board to suppress calling of the * CPU do_transaction_failed hook function. @@ -313,10 +447,15 @@ struct qemu_work_item; * dirty ring structure. * * State of one CPU core or thread. + * + * Align, in order to match possible alignment required by CPUArchState, + * and eliminate a hole between CPUState and CPUArchState within ArchCPU. */ struct CPUState { /*< private >*/ DeviceState parent_obj; + /* cache to avoid expensive CPU_GET_CLASS */ + CPUClass *cc; /*< public >*/ int nr_cores; @@ -324,7 +463,7 @@ struct CPUState { struct QemuThread *thread; #ifdef _WIN32 - HANDLE hThread; + QemuSemaphore sem; #endif int thread_id; bool running, has_waiter; @@ -340,7 +479,7 @@ struct CPUState { bool unplug; bool crash_occurred; bool exit_request; - bool in_exclusive_context; + int exclusive_context_count; uint32_t cflags_next_tb; /* updates protected by BQL */ uint32_t interrupt_request; @@ -358,13 +497,9 @@ struct CPUState { AddressSpace *as; MemoryRegion *memory; - CPUArchState *env_ptr; - IcountDecr *icount_decr_ptr; + CPUJumpCache *tb_jmp_cache; - /* Accessed in parallel; all accesses must be atomic */ - TranslationBlock *tb_jmp_cache[TB_JMP_CACHE_SIZE]; - - struct GDBRegisterState *gdb_regs; + GArray *gdb_regs; int gdb_num_regs; int gdb_num_g_regs; QTAILQ_ENTRY(CPUState) node; @@ -389,17 +524,18 @@ struct CPUState { struct kvm_dirty_gfn *kvm_dirty_gfns; uint32_t kvm_fetch_index; uint64_t dirty_pages; + int kvm_vcpu_stats_fd; - /* Used for events with 'vcpu' and *without* the 'disabled' properties */ - DECLARE_BITMAP(trace_dstate_delayed, CPU_TRACE_DSTATE_MAX_EVENTS); - DECLARE_BITMAP(trace_dstate, CPU_TRACE_DSTATE_MAX_EVENTS); - - DECLARE_BITMAP(plugin_mask, QEMU_PLUGIN_EV_MAX); + /* Use by accel-block: CPU is executing an ioctl() */ + QemuLockCnt in_ioctl_lock; #ifdef CONFIG_PLUGIN + /* + * The callback pointer stays in the main CPUState as it is + * accessed via TCG (see gen_empty_mem_helper). + */ GArray *plugin_mem_cbs; - /* saved iotlb data from io_writex */ - SavedIOTLB saved_iotlb; + CPUPluginState *plugin_state; #endif /* TODO Move common fields from CPUArchState here. */ @@ -407,10 +543,10 @@ struct CPUState { int cluster_index; uint32_t tcg_cflags; uint32_t halted; - uint32_t can_do_io; int32_t exception_index; - /* shared by kvm, hax and hvf */ + AccelCPUState *accel; + /* shared by kvm and hvf */ bool vcpu_dirty; /* Used to keep track of an outstanding cpu throttle thread for migration @@ -418,39 +554,48 @@ struct CPUState { */ bool throttle_thread_scheduled; + /* + * Sleep throttle_us_per_full microseconds once dirty ring is full + * if dirty page rate limit is enabled. + */ + int64_t throttle_us_per_full; + bool ignore_memory_transaction_failures; /* Used for user-only emulation of prctl(PR_SET_UNALIGN). */ bool prctl_unalign_sigbus; - struct hax_vcpu_state *hax_vcpu; - - struct hvf_vcpu_state *hvf; - /* track IOMMUs whose translations we've cached in the TCG TLB */ GArray *iommu_notifiers; + + /* + * MUST BE LAST in order to minimize the displacement to CPUArchState. + */ + char neg_align[-sizeof(CPUNegativeOffsetState) % 16] QEMU_ALIGNED(16); + CPUNegativeOffsetState neg; }; -typedef QTAILQ_HEAD(CPUTailQ, CPUState) CPUTailQ; -extern CPUTailQ cpus; +/* Validate placement of CPUNegativeOffsetState. */ +QEMU_BUILD_BUG_ON(offsetof(CPUState, neg) != + sizeof(CPUState) - sizeof(CPUNegativeOffsetState)); -#define first_cpu QTAILQ_FIRST_RCU(&cpus) +static inline CPUArchState *cpu_env(CPUState *cpu) +{ + /* We validate that CPUArchState follows CPUState in cpu-all.h. */ + return (CPUArchState *)(cpu + 1); +} + +typedef QTAILQ_HEAD(CPUTailQ, CPUState) CPUTailQ; +extern CPUTailQ cpus_queue; + +#define first_cpu QTAILQ_FIRST_RCU(&cpus_queue) #define CPU_NEXT(cpu) QTAILQ_NEXT_RCU(cpu, node) -#define CPU_FOREACH(cpu) QTAILQ_FOREACH_RCU(cpu, &cpus, node) +#define CPU_FOREACH(cpu) QTAILQ_FOREACH_RCU(cpu, &cpus_queue, node) #define CPU_FOREACH_SAFE(cpu, next_cpu) \ - QTAILQ_FOREACH_SAFE_RCU(cpu, &cpus, node, next_cpu) + QTAILQ_FOREACH_SAFE_RCU(cpu, &cpus_queue, node, next_cpu) extern __thread CPUState *current_cpu; -static inline void cpu_tb_jmp_cache_clear(CPUState *cpu) -{ - unsigned int i; - - for (i = 0; i < TB_JMP_CACHE_SIZE; i++) { - qatomic_set(&cpu->tb_jmp_cache[i], NULL); - } -} - /** * qemu_tcg_mttcg_enabled: * Check whether we are running MultiThread TCG or not. @@ -473,8 +618,10 @@ bool cpu_paging_enabled(const CPUState *cpu); * @cpu: The CPU whose memory mappings are to be obtained. * @list: Where to write the memory mappings to. * @errp: Pointer for reporting an #Error. + * + * Returns: %true on success, %false otherwise. */ -void cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, +bool cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, Error **errp); #if !defined(CONFIG_USER_ONLY) @@ -535,11 +682,13 @@ GuestPanicInformation *cpu_get_crash_info(CPUState *cpu); * @CPU_DUMP_CODE: * @CPU_DUMP_FPU: dump FPU register state, not just integer * @CPU_DUMP_CCOP: dump info about TCG QEMU's condition code optimization state + * @CPU_DUMP_VPU: dump VPU registers */ enum CPUDumpFlags { CPU_DUMP_CODE = 0x00010000, CPU_DUMP_FPU = 0x00020000, CPU_DUMP_CCOP = 0x00040000, + CPU_DUMP_VPU = 0x00080000, }; /** @@ -623,12 +772,26 @@ void cpu_reset(CPUState *cpu); * @typename: The CPU base type. * @cpu_model: The model string without any parameters. * - * Looks up a CPU #ObjectClass matching name @cpu_model. + * Looks up a concrete CPU #ObjectClass matching name @cpu_model. * - * Returns: A #CPUClass or %NULL if not matching class is found. + * Returns: A concrete #CPUClass or %NULL if no matching class is found + * or if the matching class is abstract. */ ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model); +/** + * cpu_model_from_type: + * @typename: The CPU type name + * + * Extract the CPU model name from the CPU type name. The + * CPU type name is either the combination of the CPU model + * name and suffix, or same to the CPU model name. + * + * Returns: CPU model name or NULL if the CPU class doesn't exist + * The user should g_free() the string once no longer needed. + */ +char *cpu_model_from_type(const char *typename); + /** * cpu_create: * @typename: The CPU type. @@ -750,7 +913,7 @@ void async_safe_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data */ static inline bool cpu_in_exclusive_context(const CPUState *cpu) { - return cpu->in_exclusive_context; + return cpu->exclusive_context_count; } /** @@ -913,9 +1076,10 @@ void cpu_single_step(CPUState *cpu, int enabled); #define BP_GDB 0x10 #define BP_CPU 0x20 #define BP_ANY (BP_GDB | BP_CPU) -#define BP_WATCHPOINT_HIT_READ 0x40 -#define BP_WATCHPOINT_HIT_WRITE 0x80 -#define BP_WATCHPOINT_HIT (BP_WATCHPOINT_HIT_READ | BP_WATCHPOINT_HIT_WRITE) +#define BP_HIT_SHIFT 6 +#define BP_WATCHPOINT_HIT_READ (BP_MEM_READ << BP_HIT_SHIFT) +#define BP_WATCHPOINT_HIT_WRITE (BP_MEM_WRITE << BP_HIT_SHIFT) +#define BP_WATCHPOINT_HIT (BP_MEM_ACCESS << BP_HIT_SHIFT) int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags, CPUBreakpoint **breakpoint); @@ -938,7 +1102,7 @@ static inline bool cpu_breakpoint_test(CPUState *cpu, vaddr pc, int mask) return false; } -#ifdef CONFIG_USER_ONLY +#if defined(CONFIG_USER_ONLY) static inline int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, int flags, CPUWatchpoint **watchpoint) { @@ -959,17 +1123,6 @@ static inline void cpu_watchpoint_remove_by_ref(CPUState *cpu, static inline void cpu_watchpoint_remove_all(CPUState *cpu, int mask) { } - -static inline void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, - MemTxAttrs atr, int fl, uintptr_t ra) -{ -} - -static inline int cpu_watchpoint_address_matches(CPUState *cpu, - vaddr addr, vaddr len) -{ - return 0; -} #else int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, int flags, CPUWatchpoint **watchpoint); @@ -977,34 +1130,25 @@ int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, vaddr len, int flags); void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint); void cpu_watchpoint_remove_all(CPUState *cpu, int mask); - -/** - * cpu_check_watchpoint: - * @cpu: cpu context - * @addr: guest virtual address - * @len: access length - * @attrs: memory access attributes - * @flags: watchpoint access type - * @ra: unwind return address - * - * Check for a watchpoint hit in [addr, addr+len) of the type - * specified by @flags. Exit via exception with a hit. - */ -void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, - MemTxAttrs attrs, int flags, uintptr_t ra); - -/** - * cpu_watchpoint_address_matches: - * @cpu: cpu context - * @addr: guest virtual address - * @len: access length - * - * Return the watchpoint flags that apply to [addr, addr+len). - * If no watchpoint is registered for the range, the result is 0. - */ -int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len); #endif +/** + * cpu_plugin_mem_cbs_enabled() - are plugin memory callbacks enabled? + * @cs: CPUState pointer + * + * The memory callbacks are installed if a plugin has instrumented an + * instruction for memory. This can be useful to know if you want to + * force a slow path for a series of memory accesses. + */ +static inline bool cpu_plugin_mem_cbs_enabled(const CPUState *cpu) +{ +#ifdef CONFIG_PLUGIN + return !!cpu->plugin_mem_cbs; +#else + return false; +#endif +} + /** * cpu_get_address_space: * @cpu: CPU to get address space from @@ -1021,8 +1165,9 @@ G_NORETURN void cpu_abort(CPUState *cpu, const char *fmt, ...) /* $(top_srcdir)/cpu.c */ void cpu_class_init_props(DeviceClass *dc); void cpu_exec_initfn(CPUState *cpu); -void cpu_exec_realizefn(CPUState *cpu, Error **errp); +bool cpu_exec_realizefn(CPUState *cpu, Error **errp); void cpu_exec_unrealizefn(CPUState *cpu); +void cpu_exec_reset_hold(CPUState *cpu); /** * target_words_bigendian: @@ -1035,11 +1180,11 @@ void cpu_exec_unrealizefn(CPUState *cpu); */ bool target_words_bigendian(void); -void page_size_init(void); +const char *target_name(void); #ifdef NEED_CPU_H -#ifdef CONFIG_SOFTMMU +#ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_cpu_common; @@ -1050,7 +1195,7 @@ extern const VMStateDescription vmstate_cpu_common; .flags = VMS_STRUCT, \ .offset = 0, \ } -#endif /* CONFIG_SOFTMMU */ +#endif /* !CONFIG_USER_ONLY */ #endif /* NEED_CPU_H */ diff --git a/include/hw/core/resetcontainer.h b/include/hw/core/resetcontainer.h new file mode 100644 index 0000000000..23db0c7a88 --- /dev/null +++ b/include/hw/core/resetcontainer.h @@ -0,0 +1,48 @@ +/* + * Reset container + * + * Copyright (c) 2024 Linaro, Ltd + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_RESETCONTAINER_H +#define HW_RESETCONTAINER_H + +/* + * The "reset container" is an object which implements the Resettable + * interface. It contains a list of arbitrary other objects which also + * implement Resettable. Resetting the reset container resets all the + * objects in it. + */ + +#include "qom/object.h" + +#define TYPE_RESETTABLE_CONTAINER "resettable-container" +OBJECT_DECLARE_TYPE(ResettableContainer, ResettableContainerClass, RESETTABLE_CONTAINER) + +/** + * resettable_container_add: Add a resettable object to the container + * @rc: container + * @obj: object to add to the container + * + * Add @obj to the ResettableContainer @rc. @obj must implement the + * Resettable interface. + * + * When @rc is reset, it will reset every object that has been added + * to it, in the order they were added. + */ +void resettable_container_add(ResettableContainer *rc, Object *obj); + +/** + * resettable_container_remove: Remove an object from the container + * @rc: container + * @obj: object to remove from the container + * + * Remove @obj from the ResettableContainer @rc. @obj must have been + * previously added to this container. + */ +void resettable_container_remove(ResettableContainer *rc, Object *obj); + +#endif diff --git a/include/hw/core/sysemu-cpu-ops.h b/include/hw/core/sysemu-cpu-ops.h index a9ba39e5f2..24d003fe04 100644 --- a/include/hw/core/sysemu-cpu-ops.h +++ b/include/hw/core/sysemu-cpu-ops.h @@ -19,7 +19,7 @@ typedef struct SysemuCPUOps { /** * @get_memory_mapping: Callback for obtaining the memory mappings. */ - void (*get_memory_mapping)(CPUState *cpu, MemoryMappingList *list, + bool (*get_memory_mapping)(CPUState *cpu, MemoryMappingList *list, Error **errp); /** * @get_paging_enabled: Callback for inquiring whether paging is enabled. @@ -53,25 +53,25 @@ typedef struct SysemuCPUOps { * 32-bit VM coredump. */ int (*write_elf32_note)(WriteCoreDumpFunction f, CPUState *cpu, - int cpuid, void *opaque); + int cpuid, DumpState *s); /** * @write_elf64_note: Callback for writing a CPU-specific ELF note to a * 64-bit VM coredump. */ int (*write_elf64_note)(WriteCoreDumpFunction f, CPUState *cpu, - int cpuid, void *opaque); + int cpuid, DumpState *s); /** * @write_elf32_qemunote: Callback for writing a CPU- and QEMU-specific ELF * note to a 32-bit VM coredump. */ int (*write_elf32_qemunote)(WriteCoreDumpFunction f, CPUState *cpu, - void *opaque); + DumpState *s); /** * @write_elf64_qemunote: Callback for writing a CPU- and QEMU-specific ELF * note to a 64-bit VM coredump. */ int (*write_elf64_qemunote)(WriteCoreDumpFunction f, CPUState *cpu, - void *opaque); + DumpState *s); /** * @virtio_is_big_endian: Callback to return %true if a CPU which supports * runtime configurable endianness is currently big-endian. diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h index 78c6c6635d..bf8ff8e3ee 100644 --- a/include/hw/core/tcg-cpu-ops.h +++ b/include/hw/core/tcg-cpu-ops.h @@ -14,7 +14,7 @@ struct TCGCPUOps { /** - * @initialize: Initalize TCG state + * @initialize: Initialize TCG state * * Called when the first CPU is realized. */ @@ -31,6 +31,17 @@ struct TCGCPUOps { * function to restore all the state, and register it here. */ void (*synchronize_from_tb)(CPUState *cpu, const TranslationBlock *tb); + /** + * @restore_state_to_opc: Synchronize state from INDEX_op_start_insn + * + * This is called when we unwind state in the middle of a TB, + * usually before raising an exception. Set all part of the CPU + * state which are tracked insn-by-insn in the target-specific + * arguments to start_insn, passed as @data. + */ + void (*restore_state_to_opc)(CPUState *cpu, const TranslationBlock *tb, + const uint64_t *data); + /** @cpu_exec_enter: Callback for cpu_exec preparation */ void (*cpu_exec_enter)(CPUState *cpu); /** @cpu_exec_exit: Callback for cpu_exec cleanup */ @@ -39,7 +50,7 @@ struct TCGCPUOps { void (*debug_excp_handler)(CPUState *cpu); #ifdef NEED_CPU_H -#if defined(CONFIG_USER_ONLY) && defined(TARGET_I386) +#ifdef CONFIG_USER_ONLY /** * @fake_user_interrupt: Callback for 'fake exception' handling. * @@ -47,15 +58,62 @@ struct TCGCPUOps { * cpu execution loop (hack for x86 user mode). */ void (*fake_user_interrupt)(CPUState *cpu); -#else + /** - * @do_interrupt: Callback for interrupt handling. + * record_sigsegv: + * @cpu: cpu context + * @addr: faulting guest address + * @access_type: access was read/write/execute + * @maperr: true for invalid page, false for permission fault + * @ra: host pc for unwinding + * + * We are about to raise SIGSEGV with si_code set for @maperr, + * and si_addr set for @addr. Record anything further needed + * for the signal ucontext_t. + * + * If the emulated kernel does not provide anything to the signal + * handler with anything besides the user context registers, and + * the siginfo_t, then this hook need do nothing and may be omitted. + * Otherwise, record the data and return; the caller will raise + * the signal, unwind the cpu state, and return to the main loop. + * + * If it is simpler to re-use the sysemu tlb_fill code, @ra is provided + * so that a "normal" cpu exception can be raised. In this case, + * the signal must be raised by the architecture cpu_loop. */ + void (*record_sigsegv)(CPUState *cpu, vaddr addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra); + /** + * record_sigbus: + * @cpu: cpu context + * @addr: misaligned guest address + * @access_type: access was read/write/execute + * @ra: host pc for unwinding + * + * We are about to raise SIGBUS with si_code BUS_ADRALN, + * and si_addr set for @addr. Record anything further needed + * for the signal ucontext_t. + * + * If the emulated kernel does not provide the signal handler with + * anything besides the user context registers, and the siginfo_t, + * then this hook need do nothing and may be omitted. + * Otherwise, record the data and return; the caller will raise + * the signal, unwind the cpu state, and return to the main loop. + * + * If it is simpler to re-use the sysemu do_unaligned_access code, + * @ra is provided so that a "normal" cpu exception can be raised. + * In this case, the signal must be raised by the architecture cpu_loop. + */ + void (*record_sigbus)(CPUState *cpu, vaddr addr, + MMUAccessType access_type, uintptr_t ra); +#else + /** @do_interrupt: Callback for interrupt handling. */ void (*do_interrupt)(CPUState *cpu); -#endif /* !CONFIG_USER_ONLY || !TARGET_I386 */ -#ifdef CONFIG_SOFTMMU /** @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec */ bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request); + /** @cpu_exec_halt: Callback for handling halt in cpu_exec */ + void (*cpu_exec_halt)(CPUState *cpu); /** * @tlb_fill: Handle a softmmu tlb miss * @@ -110,58 +168,57 @@ struct TCGCPUOps { */ bool (*io_recompile_replay_branch)(CPUState *cpu, const TranslationBlock *tb); -#else /** - * record_sigsegv: - * @cpu: cpu context - * @addr: faulting guest address - * @access_type: access was read/write/execute - * @maperr: true for invalid page, false for permission fault - * @ra: host pc for unwinding - * - * We are about to raise SIGSEGV with si_code set for @maperr, - * and si_addr set for @addr. Record anything further needed - * for the signal ucontext_t. - * - * If the emulated kernel does not provide anything to the signal - * handler with anything besides the user context registers, and - * the siginfo_t, then this hook need do nothing and may be omitted. - * Otherwise, record the data and return; the caller will raise - * the signal, unwind the cpu state, and return to the main loop. - * - * If it is simpler to re-use the sysemu tlb_fill code, @ra is provided - * so that a "normal" cpu exception can be raised. In this case, - * the signal must be raised by the architecture cpu_loop. + * @need_replay_interrupt: Return %true if @interrupt_request + * needs to be recorded for replay purposes. */ - void (*record_sigsegv)(CPUState *cpu, vaddr addr, - MMUAccessType access_type, - bool maperr, uintptr_t ra); - /** - * record_sigbus: - * @cpu: cpu context - * @addr: misaligned guest address - * @access_type: access was read/write/execute - * @ra: host pc for unwinding - * - * We are about to raise SIGBUS with si_code BUS_ADRALN, - * and si_addr set for @addr. Record anything further needed - * for the signal ucontext_t. - * - * If the emulated kernel does not provide the signal handler with - * anything besides the user context registers, and the siginfo_t, - * then this hook need do nothing and may be omitted. - * Otherwise, record the data and return; the caller will raise - * the signal, unwind the cpu state, and return to the main loop. - * - * If it is simpler to re-use the sysemu do_unaligned_access code, - * @ra is provided so that a "normal" cpu exception can be raised. - * In this case, the signal must be raised by the architecture cpu_loop. - */ - void (*record_sigbus)(CPUState *cpu, vaddr addr, - MMUAccessType access_type, uintptr_t ra); -#endif /* CONFIG_SOFTMMU */ + bool (*need_replay_interrupt)(int interrupt_request); +#endif /* !CONFIG_USER_ONLY */ #endif /* NEED_CPU_H */ }; +#if defined(CONFIG_USER_ONLY) + +static inline void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, + MemTxAttrs atr, int fl, uintptr_t ra) +{ +} + +static inline int cpu_watchpoint_address_matches(CPUState *cpu, + vaddr addr, vaddr len) +{ + return 0; +} + +#else + +/** + * cpu_check_watchpoint: + * @cpu: cpu context + * @addr: guest virtual address + * @len: access length + * @attrs: memory access attributes + * @flags: watchpoint access type + * @ra: unwind return address + * + * Check for a watchpoint hit in [addr, addr+len) of the type + * specified by @flags. Exit via exception with a hit. + */ +void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, + MemTxAttrs attrs, int flags, uintptr_t ra); + +/** + * cpu_watchpoint_address_matches: + * @cpu: cpu context + * @addr: guest virtual address + * @len: access length + * + * Return the watchpoint flags that apply to [addr, addr+len). + * If no watchpoint is registered for the range, the result is 0. + */ +int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len); + +#endif + #endif /* TCG_CPU_OPS_H */ diff --git a/include/hw/cris/etraxfs.h b/include/hw/cris/etraxfs.h index 8b01ed67d3..012c4e9974 100644 --- a/include/hw/cris/etraxfs.h +++ b/include/hw/cris/etraxfs.h @@ -29,8 +29,9 @@ #include "hw/cris/etraxfs_dma.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" +#include "qapi/error.h" -DeviceState *etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr, +DeviceState *etraxfs_eth_init(hwaddr base, int phyaddr, struct etraxfs_dma_client *dma_out, struct etraxfs_dma_client *dma_in); diff --git a/include/hw/cxl/cxl.h b/include/hw/cxl/cxl.h index 21d28ca110..75e47b6864 100644 --- a/include/hw/cxl/cxl.h +++ b/include/hw/cxl/cxl.h @@ -12,21 +12,24 @@ #include "qapi/qapi-types-machine.h" -#include "hw/pci/pci_bridge.h" +#include "qapi/qapi-visit-machine.h" #include "hw/pci/pci_host.h" #include "cxl_pci.h" #include "cxl_component.h" #include "cxl_device.h" +#define CXL_CACHE_LINE_SIZE 64 #define CXL_COMPONENT_REG_BAR_IDX 0 #define CXL_DEVICE_REG_BAR_IDX 2 #define CXL_WINDOW_MAX 10 +typedef struct PXBCXLDev PXBCXLDev; + typedef struct CXLFixedWindow { uint64_t size; char **targets; - struct PXBDev *target_hbs[8]; + PXBCXLDev *target_hbs[16]; uint8_t num_targets; uint8_t enc_int_ways; uint8_t enc_int_gran; @@ -40,22 +43,28 @@ typedef struct CXLState { MemoryRegion host_mr; unsigned int next_mr_idx; GList *fixed_windows; + CXLFixedMemoryWindowOptionsList *cfmw_list; } CXLState; struct CXLHost { PCIHostState parent_obj; CXLComponentState cxl_cstate; + bool passthrough; }; #define TYPE_PXB_CXL_HOST "pxb-cxl-host" OBJECT_DECLARE_SIMPLE_TYPE(CXLHost, PXB_CXL_HOST) -void cxl_fixed_memory_window_config(MachineState *ms, - CXLFixedMemoryWindowOptions *object, - Error **errp); -void cxl_fixed_memory_window_link_targets(Error **errp); +#define TYPE_CXL_USP "cxl-upstream" -extern const MemoryRegionOps cfmws_ops; +typedef struct CXLUpstreamPort CXLUpstreamPort; +DECLARE_INSTANCE_CHECKER(CXLUpstreamPort, CXL_USP, TYPE_CXL_USP) +CXLComponentState *cxl_usp_to_cstate(CXLUpstreamPort *usp); + +#define TYPE_CXL_DSP "cxl-downstream" + +typedef struct CXLDownstreamPort CXLDownstreamPort; +DECLARE_INSTANCE_CHECKER(CXLDownstreamPort, CXL_DSP, TYPE_CXL_DSP) #endif diff --git a/include/hw/cxl/cxl_cdat.h b/include/hw/cxl/cxl_cdat.h new file mode 100644 index 0000000000..17a09066dc --- /dev/null +++ b/include/hw/cxl/cxl_cdat.h @@ -0,0 +1,172 @@ +/* + * CXL CDAT Structure + * + * Copyright (C) 2021 Avery Design Systems, 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 CXL_CDAT_H +#define CXL_CDAT_H + +#include "hw/cxl/cxl_pci.h" +#include "hw/pci/pcie_doe.h" + +/* + * Reference: + * Coherent Device Attribute Table (CDAT) Specification, Rev. 1.03, July. 2022 + * Compute Express Link (CXL) Specification, Rev. 3.1, Aug. 2023 + */ +/* Table Access DOE - CXL r3.1 8.1.11 */ +#define CXL_DOE_TABLE_ACCESS 2 +#define CXL_DOE_PROTOCOL_CDAT ((CXL_DOE_TABLE_ACCESS << 16) | CXL_VENDOR_ID) + +/* Read Entry - CXL r3.1 8.1.11.1 */ +#define CXL_DOE_TAB_TYPE_CDAT 0 +#define CXL_DOE_TAB_ENT_MAX 0xFFFF + +/* Read Entry Request - CXL r3.1 8.1.11.1 Table 8-13 */ +#define CXL_DOE_TAB_REQ 0 +typedef struct CDATReq { + DOEHeader header; + uint8_t req_code; + uint8_t table_type; + uint16_t entry_handle; +} QEMU_PACKED CDATReq; + +/* Read Entry Response - CXL r3.1 8.1.11.1 Table 8-14 */ +#define CXL_DOE_TAB_RSP 0 +typedef struct CDATRsp { + DOEHeader header; + uint8_t rsp_code; + uint8_t table_type; + uint16_t entry_handle; +} QEMU_PACKED CDATRsp; + +/* CDAT Table Format - CDAT Table 1 */ +#define CXL_CDAT_REV 2 +typedef struct CDATTableHeader { + uint32_t length; + uint8_t revision; + uint8_t checksum; + uint8_t reserved[6]; + uint32_t sequence; +} QEMU_PACKED CDATTableHeader; + +/* CDAT Structure Types - CDAT Table 2 */ +typedef enum { + CDAT_TYPE_DSMAS = 0, + CDAT_TYPE_DSLBIS = 1, + CDAT_TYPE_DSMSCIS = 2, + CDAT_TYPE_DSIS = 3, + CDAT_TYPE_DSEMTS = 4, + CDAT_TYPE_SSLBIS = 5, +} CDATType; + +typedef struct CDATSubHeader { + uint8_t type; + uint8_t reserved; + uint16_t length; +} CDATSubHeader; + +/* Device Scoped Memory Affinity Structure - CDAT Table 3 */ +typedef struct CDATDsmas { + CDATSubHeader header; + uint8_t DSMADhandle; + uint8_t flags; +#define CDAT_DSMAS_FLAG_NV (1 << 2) +#define CDAT_DSMAS_FLAG_SHAREABLE (1 << 3) +#define CDAT_DSMAS_FLAG_HW_COHERENT (1 << 4) +#define CDAT_DSMAS_FLAG_DYNAMIC_CAP (1 << 5) + uint16_t reserved; + uint64_t DPA_base; + uint64_t DPA_length; +} CDATDsmas; +QEMU_BUILD_BUG_ON(sizeof(CDATDsmas) != 24); + +/* Device Scoped Latency and Bandwidth Information Structure - CDAT Table 5 */ +typedef struct CDATDslbis { + CDATSubHeader header; + uint8_t handle; + /* Definitions of these fields refer directly to HMAT fields */ + uint8_t flags; + uint8_t data_type; + uint8_t reserved; + uint64_t entry_base_unit; + uint16_t entry[3]; + uint16_t reserved2; +} CDATDslbis; +QEMU_BUILD_BUG_ON(sizeof(CDATDslbis) != 24); + +/* Device Scoped Memory Side Cache Information Structure - CDAT Table 6 */ +typedef struct CDATDsmscis { + CDATSubHeader header; + uint8_t DSMAS_handle; + uint8_t reserved[3]; + uint64_t memory_side_cache_size; + uint32_t cache_attributes; +} QEMU_PACKED CDATDsmscis; + +/* Device Scoped Initiator Structure - CDAT Table 7 */ +typedef struct CDATDsis { + CDATSubHeader header; + uint8_t flags; + uint8_t handle; + uint16_t reserved; +} QEMU_PACKED CDATDsis; + +/* Device Scoped EFI Memory Type Structure - CDAT Table 8 */ +typedef struct CDATDsemts { + CDATSubHeader header; + uint8_t DSMAS_handle; + uint8_t EFI_memory_type_attr; + uint16_t reserved; + uint64_t DPA_offset; + uint64_t DPA_length; +} CDATDsemts; +QEMU_BUILD_BUG_ON(sizeof(CDATDsemts) != 24); + +/* Switch Scoped Latency and Bandwidth Information Structure - CDAT Table 9 */ +typedef struct CDATSslbisHeader { + CDATSubHeader header; + uint8_t data_type; + uint8_t reserved[3]; + uint64_t entry_base_unit; +} CDATSslbisHeader; +QEMU_BUILD_BUG_ON(sizeof(CDATSslbisHeader) != 16); + +#define CDAT_PORT_ID_USP 0x100 +/* Switch Scoped Latency and Bandwidth Entry - CDAT Table 10 */ +typedef struct CDATSslbe { + uint16_t port_x_id; + uint16_t port_y_id; + uint16_t latency_bandwidth; + uint16_t reserved; +} CDATSslbe; +QEMU_BUILD_BUG_ON(sizeof(CDATSslbe) != 8); + +typedef struct CDATSslbis { + CDATSslbisHeader sslbis_header; + CDATSslbe sslbe[]; +} CDATSslbis; + +typedef struct CDATEntry { + void *base; + uint32_t length; +} CDATEntry; + +typedef struct CDATObject { + CDATEntry *entry; + int entry_len; + + int (*build_cdat_table)(CDATSubHeader ***cdat_table, void *priv); + void (*free_cdat_table)(CDATSubHeader **cdat_table, int num, void *priv); + bool to_update; + void *private; + char *filename; + uint8_t *buf; + struct CDATSubHeader **built_buf; + int built_buf_len; +} CDATObject; +#endif /* CXL_CDAT_H */ diff --git a/include/hw/cxl/cxl_component.h b/include/hw/cxl/cxl_component.h index 70b5018156..5012fab6f7 100644 --- a/include/hw/cxl/cxl_component.h +++ b/include/hw/cxl/cxl_component.h @@ -10,33 +10,36 @@ #ifndef CXL_COMPONENT_H #define CXL_COMPONENT_H -/* CXL 2.0 - 8.2.4 */ +/* CXL r3.1 Section 8.2.4: CXL.cache and CXL.mem Registers */ #define CXL2_COMPONENT_IO_REGION_SIZE 0x1000 #define CXL2_COMPONENT_CM_REGION_SIZE 0x1000 #define CXL2_COMPONENT_BLOCK_SIZE 0x10000 -#include "qemu/compiler.h" #include "qemu/range.h" -#include "qemu/typedefs.h" +#include "hw/cxl/cxl_cdat.h" #include "hw/register.h" +#include "qapi/error.h" enum reg_type { CXL2_DEVICE, CXL2_TYPE3_DEVICE, CXL2_LOGICAL_DEVICE, CXL2_ROOT_PORT, + CXL2_RC, CXL2_UPSTREAM_PORT, - CXL2_DOWNSTREAM_PORT + CXL2_DOWNSTREAM_PORT, + CXL3_SWITCH_MAILBOX_CCI, }; /* * Capability registers are defined at the top of the CXL.cache/mem region and * are packed. For our purposes we will always define the caps in the same * order. - * CXL 2.0 - 8.2.5 Table 142 for details. + * CXL r3.1 Table 8-22: CXL_CAPABILITY_ID Assignment for details. */ -/* CXL 2.0 - 8.2.5.1 */ +/* CXL r3.1 Section 8.2.4.1: CXL Capability Header Register */ +#define CXL_CAPABILITY_VERSION 1 REG32(CXL_CAPABILITY_HEADER, 0) FIELD(CXL_CAPABILITY_HEADER, ID, 0, 16) FIELD(CXL_CAPABILITY_HEADER, VERSION, 16, 4) @@ -59,31 +62,62 @@ CXLx_CAPABILITY_HEADER(SNOOP, 0x14) * implements. Some of these are specific to certain types of components, but * this implementation leaves enough space regardless. */ -/* 8.2.5.9 - CXL RAS Capability Structure */ +/* CXL r3.1 Section 8.2.4.17: CXL RAS Capability Structure */ +#define CXL_RAS_CAPABILITY_VERSION 3 /* Give ample space for caps before this */ #define CXL_RAS_REGISTERS_OFFSET 0x80 #define CXL_RAS_REGISTERS_SIZE 0x58 REG32(CXL_RAS_UNC_ERR_STATUS, CXL_RAS_REGISTERS_OFFSET) +#define CXL_RAS_UNC_ERR_CACHE_DATA_PARITY 0 +#define CXL_RAS_UNC_ERR_CACHE_ADDRESS_PARITY 1 +#define CXL_RAS_UNC_ERR_CACHE_BE_PARITY 2 +#define CXL_RAS_UNC_ERR_CACHE_DATA_ECC 3 +#define CXL_RAS_UNC_ERR_MEM_DATA_PARITY 4 +#define CXL_RAS_UNC_ERR_MEM_ADDRESS_PARITY 5 +#define CXL_RAS_UNC_ERR_MEM_BE_PARITY 6 +#define CXL_RAS_UNC_ERR_MEM_DATA_ECC 7 +#define CXL_RAS_UNC_ERR_REINIT_THRESHOLD 8 +#define CXL_RAS_UNC_ERR_RSVD_ENCODING 9 +#define CXL_RAS_UNC_ERR_POISON_RECEIVED 10 +#define CXL_RAS_UNC_ERR_RECEIVER_OVERFLOW 11 +#define CXL_RAS_UNC_ERR_INTERNAL 14 +#define CXL_RAS_UNC_ERR_CXL_IDE_TX 15 +#define CXL_RAS_UNC_ERR_CXL_IDE_RX 16 +#define CXL_RAS_UNC_ERR_CXL_UNUSED 63 /* Magic value */ REG32(CXL_RAS_UNC_ERR_MASK, CXL_RAS_REGISTERS_OFFSET + 0x4) REG32(CXL_RAS_UNC_ERR_SEVERITY, CXL_RAS_REGISTERS_OFFSET + 0x8) REG32(CXL_RAS_COR_ERR_STATUS, CXL_RAS_REGISTERS_OFFSET + 0xc) +#define CXL_RAS_COR_ERR_CACHE_DATA_ECC 0 +#define CXL_RAS_COR_ERR_MEM_DATA_ECC 1 +#define CXL_RAS_COR_ERR_CRC_THRESHOLD 2 +#define CXL_RAS_COR_ERR_RETRY_THRESHOLD 3 +#define CXL_RAS_COR_ERR_CACHE_POISON_RECEIVED 4 +#define CXL_RAS_COR_ERR_MEM_POISON_RECEIVED 5 +#define CXL_RAS_COR_ERR_PHYSICAL 6 REG32(CXL_RAS_COR_ERR_MASK, CXL_RAS_REGISTERS_OFFSET + 0x10) REG32(CXL_RAS_ERR_CAP_CTRL, CXL_RAS_REGISTERS_OFFSET + 0x14) + FIELD(CXL_RAS_ERR_CAP_CTRL, FIRST_ERROR_POINTER, 0, 6) + FIELD(CXL_RAS_ERR_CAP_CTRL, MULTIPLE_HEADER_RECORDING_CAP, 9, 1) + FIELD(CXL_RAS_ERR_POISON_ENABLED, POISON_ENABLED, 13, 1) +REG32(CXL_RAS_ERR_HEADER0, CXL_RAS_REGISTERS_OFFSET + 0x18) +#define CXL_RAS_ERR_HEADER_NUM 32 /* Offset 0x18 - 0x58 reserved for RAS logs */ -/* 8.2.5.10 - CXL Security Capability Structure */ +/* CXL r3.1 Section 8.2.4.18: CXL Security Capability Structure */ #define CXL_SEC_REGISTERS_OFFSET \ (CXL_RAS_REGISTERS_OFFSET + CXL_RAS_REGISTERS_SIZE) #define CXL_SEC_REGISTERS_SIZE 0 /* We don't implement 1.1 downstream ports */ -/* 8.2.5.11 - CXL Link Capability Structure */ +/* CXL r3.1 Section 8.2.4.19: CXL Link Capability Structure */ +#define CXL_LINK_CAPABILITY_VERSION 2 #define CXL_LINK_REGISTERS_OFFSET \ (CXL_SEC_REGISTERS_OFFSET + CXL_SEC_REGISTERS_SIZE) -#define CXL_LINK_REGISTERS_SIZE 0x38 +#define CXL_LINK_REGISTERS_SIZE 0x50 -/* 8.2.5.12 - CXL HDM Decoder Capability Structure */ -#define HDM_DECODE_MAX 10 /* 8.2.5.12.1 */ +/* CXL r3.1 Section 8.2.4.20: CXL HDM Decoder Capability Structure */ +#define HDM_DECODE_MAX 10 /* Maximum decoders for Devices */ +#define CXL_HDM_CAPABILITY_VERSION 3 #define CXL_HDM_REGISTERS_OFFSET \ (CXL_LINK_REGISTERS_OFFSET + CXL_LINK_REGISTERS_SIZE) #define CXL_HDM_REGISTERS_SIZE (0x10 + 0x20 * HDM_DECODE_MAX) @@ -106,9 +140,18 @@ REG32(CXL_RAS_ERR_CAP_CTRL, CXL_RAS_REGISTERS_OFFSET + 0x14) FIELD(CXL_HDM_DECODER##n##_CTRL, COMMITTED, 10, 1) \ FIELD(CXL_HDM_DECODER##n##_CTRL, ERR, 11, 1) \ FIELD(CXL_HDM_DECODER##n##_CTRL, TYPE, 12, 1) \ + FIELD(CXL_HDM_DECODER##n##_CTRL, BI, 13, 1) \ + FIELD(CXL_HDM_DECODER##n##_CTRL, UIO, 14, 1) \ + FIELD(CXL_HDM_DECODER##n##_CTRL, UIG, 16, 4) \ + FIELD(CXL_HDM_DECODER##n##_CTRL, UIW, 20, 4) \ + FIELD(CXL_HDM_DECODER##n##_CTRL, ISP, 24, 4) \ REG32(CXL_HDM_DECODER##n##_TARGET_LIST_LO, \ CXL_HDM_REGISTERS_OFFSET + (0x20 * n) + 0x24) \ REG32(CXL_HDM_DECODER##n##_TARGET_LIST_HI, \ + CXL_HDM_REGISTERS_OFFSET + (0x20 * n) + 0x28) \ + REG32(CXL_HDM_DECODER##n##_DPA_SKIP_LO, \ + CXL_HDM_REGISTERS_OFFSET + (0x20 * n) + 0x24) \ + REG32(CXL_HDM_DECODER##n##_DPA_SKIP_HI, \ CXL_HDM_REGISTERS_OFFSET + (0x20 * n) + 0x28) REG32(CXL_HDM_DECODER_CAPABILITY, CXL_HDM_REGISTERS_OFFSET) @@ -117,29 +160,48 @@ REG32(CXL_HDM_DECODER_CAPABILITY, CXL_HDM_REGISTERS_OFFSET) FIELD(CXL_HDM_DECODER_CAPABILITY, INTERLEAVE_256B, 8, 1) FIELD(CXL_HDM_DECODER_CAPABILITY, INTERLEAVE_4K, 9, 1) FIELD(CXL_HDM_DECODER_CAPABILITY, POISON_ON_ERR_CAP, 10, 1) + FIELD(CXL_HDM_DECODER_CAPABILITY, 3_6_12_WAY, 11, 1) + FIELD(CXL_HDM_DECODER_CAPABILITY, 16_WAY, 12, 1) + FIELD(CXL_HDM_DECODER_CAPABILITY, UIO, 13, 1) + FIELD(CXL_HDM_DECODER_CAPABILITY, UIO_DECODER_COUNT, 16, 4) + FIELD(CXL_HDM_DECODER_CAPABILITY, MEMDATA_NXM_CAP, 20, 1) + FIELD(CXL_HDM_DECODER_CAPABILITY, SUPPORTED_COHERENCY_MODEL, 21, 2) REG32(CXL_HDM_DECODER_GLOBAL_CONTROL, CXL_HDM_REGISTERS_OFFSET + 4) FIELD(CXL_HDM_DECODER_GLOBAL_CONTROL, POISON_ON_ERR_EN, 0, 1) FIELD(CXL_HDM_DECODER_GLOBAL_CONTROL, HDM_DECODER_ENABLE, 1, 1) -HDM_DECODER_INIT(0); +/* Support 4 decoders at all levels of topology */ +#define CXL_HDM_DECODER_COUNT 4 -/* 8.2.5.13 - CXL Extended Security Capability Structure (Root complex only) */ +HDM_DECODER_INIT(0); +HDM_DECODER_INIT(1); +HDM_DECODER_INIT(2); +HDM_DECODER_INIT(3); + +/* + * CXL r3.1 Section 8.2.4.21: CXL Extended Security Capability Structure + * (Root complex only) + */ #define EXTSEC_ENTRY_MAX 256 +#define CXL_EXTSEC_CAP_VERSION 2 #define CXL_EXTSEC_REGISTERS_OFFSET \ (CXL_HDM_REGISTERS_OFFSET + CXL_HDM_REGISTERS_SIZE) #define CXL_EXTSEC_REGISTERS_SIZE (8 * EXTSEC_ENTRY_MAX + 4) -/* 8.2.5.14 - CXL IDE Capability Structure */ +/* CXL r3.1 Section 8.2.4.22: CXL IDE Capability Structure */ +#define CXL_IDE_CAP_VERSION 2 #define CXL_IDE_REGISTERS_OFFSET \ (CXL_EXTSEC_REGISTERS_OFFSET + CXL_EXTSEC_REGISTERS_SIZE) -#define CXL_IDE_REGISTERS_SIZE 0x20 +#define CXL_IDE_REGISTERS_SIZE 0x24 -/* 8.2.5.15 - CXL Snoop Filter Capability Structure */ +/* CXL r3.1 Section 8.2.4.23 - CXL Snoop Filter Capability Structure */ +#define CXL_SNOOP_CAP_VERSION 1 #define CXL_SNOOP_REGISTERS_OFFSET \ (CXL_IDE_REGISTERS_OFFSET + CXL_IDE_REGISTERS_SIZE) #define CXL_SNOOP_REGISTERS_SIZE 0x8 -QEMU_BUILD_BUG_MSG((CXL_SNOOP_REGISTERS_OFFSET + CXL_SNOOP_REGISTERS_SIZE) >= 0x1000, +QEMU_BUILD_BUG_MSG((CXL_SNOOP_REGISTERS_OFFSET + + CXL_SNOOP_REGISTERS_SIZE) >= 0x1000, "No space for registers"); typedef struct component_registers { @@ -149,7 +211,7 @@ typedef struct component_registers { MemoryRegion component_registers; /* - * 8.2.4 Table 141: + * CXL r3.1 Table 8-21: CXL Subsystem Component Register Ranges * 0x0000 - 0x0fff CXL.io registers * 0x1000 - 0x1fff CXL.cache and CXL.mem * 0x2000 - 0xdfff Implementation specific @@ -184,6 +246,8 @@ typedef struct cxl_component { struct PCIDevice *pdev; }; }; + + CDATObject cdat; } CXLComponentState; void cxl_component_register_block_init(Object *obj, @@ -197,27 +261,20 @@ void cxl_component_create_dvsec(CXLComponentState *cxl_cstate, enum reg_type cxl_dev_type, uint16_t length, uint16_t type, uint8_t rev, uint8_t *body); -static inline int cxl_decoder_count_enc(int count) -{ - switch (count) { - case 1: return 0; - case 2: return 1; - case 4: return 2; - case 6: return 3; - case 8: return 4; - case 10: return 5; - } - return 0; -} +int cxl_decoder_count_enc(int count); +int cxl_decoder_count_dec(int enc_cnt); uint8_t cxl_interleave_ways_enc(int iw, Error **errp); +int cxl_interleave_ways_dec(uint8_t iw_enc, Error **errp); uint8_t cxl_interleave_granularity_enc(uint64_t gran, Error **errp); -static inline hwaddr cxl_decode_ig(int ig) -{ - return 1 << (ig + 8); -} +hwaddr cxl_decode_ig(int ig); CXLComponentState *cxl_get_hb_cstate(PCIHostState *hb); +bool cxl_get_hb_passthrough(PCIHostState *hb); + +void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp); +void cxl_doe_cdat_release(CXLComponentState *cxl_cstate); +void cxl_doe_cdat_update(CXLComponentState *cxl_cstate, Error **errp); #endif diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 1e141b6621..279b276bda 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -10,7 +10,10 @@ #ifndef CXL_DEVICE_H #define CXL_DEVICE_H +#include "hw/cxl/cxl_component.h" +#include "hw/pci/pci_device.h" #include "hw/register.h" +#include "hw/cxl/cxl_events.h" /* * The following is how a CXL device's Memory Device registers are laid out. @@ -55,18 +58,30 @@ * */ -#define CXL_DEVICE_CAP_HDR1_OFFSET 0x10 /* Figure 138 */ -#define CXL_DEVICE_CAP_REG_SIZE 0x10 /* 8.2.8.2 */ -#define CXL_DEVICE_CAPS_MAX 4 /* 8.2.8.2.1 + 8.2.8.5 */ +/* CXL r3.1 Figure 8-12: CXL Device Registers */ +#define CXL_DEVICE_CAP_HDR1_OFFSET 0x10 +/* CXL r3.1 Section 8.2.8.2: CXL Device Capability Header Register */ +#define CXL_DEVICE_CAP_REG_SIZE 0x10 + +/* + * CXL r3.1 Section 8.2.8.2.1: CXL Device Capabilities + + * CXL r3.1 Section 8.2.8.5: Memory Device Capabilities + */ +#define CXL_DEVICE_CAPS_MAX 4 #define CXL_CAPS_SIZE \ (CXL_DEVICE_CAP_REG_SIZE * (CXL_DEVICE_CAPS_MAX + 1)) /* +1 for header */ #define CXL_DEVICE_STATUS_REGISTERS_OFFSET 0x80 /* Read comment above */ -#define CXL_DEVICE_STATUS_REGISTERS_LENGTH 0x8 /* 8.2.8.3.1 */ +/* + * CXL r3.1 Section 8.2.8.3: Device Status Registers + * As it is the only Device Status Register in CXL r3.1 + */ +#define CXL_DEVICE_STATUS_REGISTERS_LENGTH 0x8 #define CXL_MAILBOX_REGISTERS_OFFSET \ (CXL_DEVICE_STATUS_REGISTERS_OFFSET + CXL_DEVICE_STATUS_REGISTERS_LENGTH) -#define CXL_MAILBOX_REGISTERS_SIZE 0x20 /* 8.2.8.4, Figure 139 */ +/* CXL r3.1 Figure 8-13: Mailbox Registers */ +#define CXL_MAILBOX_REGISTERS_SIZE 0x20 #define CXL_MAILBOX_PAYLOAD_SHIFT 11 #define CXL_MAILBOX_MAX_PAYLOAD_SIZE (1 << CXL_MAILBOX_PAYLOAD_SHIFT) #define CXL_MAILBOX_REGISTERS_LENGTH \ @@ -80,11 +95,113 @@ (CXL_DEVICE_CAP_REG_SIZE + CXL_DEVICE_STATUS_REGISTERS_LENGTH + \ CXL_MAILBOX_REGISTERS_LENGTH + CXL_MEMORY_DEVICE_REGISTERS_LENGTH) +/* CXL r3.1 Table 8-34: Command Return Codes */ +typedef enum { + CXL_MBOX_SUCCESS = 0x0, + CXL_MBOX_BG_STARTED = 0x1, + CXL_MBOX_INVALID_INPUT = 0x2, + CXL_MBOX_UNSUPPORTED = 0x3, + CXL_MBOX_INTERNAL_ERROR = 0x4, + CXL_MBOX_RETRY_REQUIRED = 0x5, + CXL_MBOX_BUSY = 0x6, + CXL_MBOX_MEDIA_DISABLED = 0x7, + CXL_MBOX_FW_XFER_IN_PROGRESS = 0x8, + CXL_MBOX_FW_XFER_OUT_OF_ORDER = 0x9, + CXL_MBOX_FW_AUTH_FAILED = 0xa, + CXL_MBOX_FW_INVALID_SLOT = 0xb, + CXL_MBOX_FW_ROLLEDBACK = 0xc, + CXL_MBOX_FW_REST_REQD = 0xd, + CXL_MBOX_INVALID_HANDLE = 0xe, + CXL_MBOX_INVALID_PA = 0xf, + CXL_MBOX_INJECT_POISON_LIMIT = 0x10, + CXL_MBOX_PERMANENT_MEDIA_FAILURE = 0x11, + CXL_MBOX_ABORTED = 0x12, + CXL_MBOX_INVALID_SECURITY_STATE = 0x13, + CXL_MBOX_INCORRECT_PASSPHRASE = 0x14, + CXL_MBOX_UNSUPPORTED_MAILBOX = 0x15, + CXL_MBOX_INVALID_PAYLOAD_LENGTH = 0x16, + CXL_MBOX_INVALID_LOG = 0x17, + CXL_MBOX_INTERRUPTED = 0x18, + CXL_MBOX_UNSUPPORTED_FEATURE_VERSION = 0x19, + CXL_MBOX_UNSUPPORTED_FEATURE_SELECTION_VALUE = 0x1a, + CXL_MBOX_FEATURE_TRANSFER_IN_PROGRESS = 0x1b, + CXL_MBOX_FEATURE_TRANSFER_OUT_OF_ORDER = 0x1c, + CXL_MBOX_RESOURCES_EXHAUSTED = 0x1d, + CXL_MBOX_INVALID_EXTENT_LIST = 0x1e, + CXL_MBOX_TRANSFER_OUT_OF_ORDER = 0x1f, + CXL_MBOX_REQUEST_ABORT_NOTSUP = 0x20, + CXL_MBOX_MAX = 0x20 +} CXLRetCode; + +typedef struct CXLCCI CXLCCI; +typedef struct cxl_device_state CXLDeviceState; +struct cxl_cmd; +typedef CXLRetCode (*opcode_handler)(const struct cxl_cmd *cmd, + uint8_t *payload_in, size_t len_in, + uint8_t *payload_out, size_t *len_out, + CXLCCI *cci); +struct cxl_cmd { + const char *name; + opcode_handler handler; + ssize_t in; + uint16_t effect; /* Reported in CEL */ +}; + +typedef struct CXLEvent { + CXLEventRecordRaw data; + QSIMPLEQ_ENTRY(CXLEvent) node; +} CXLEvent; + +typedef struct CXLEventLog { + uint16_t next_handle; + uint16_t overflow_err_count; + uint64_t first_overflow_timestamp; + uint64_t last_overflow_timestamp; + bool irq_enabled; + int irq_vec; + QemuMutex lock; + QSIMPLEQ_HEAD(, CXLEvent) events; +} CXLEventLog; + +typedef struct CXLCCI { + const struct cxl_cmd (*cxl_cmd_set)[256]; + struct cel_log { + uint16_t opcode; + uint16_t effect; + } cel_log[1 << 16]; + size_t cel_size; + + /* background command handling (times in ms) */ + struct { + uint16_t opcode; + uint16_t complete_pct; + uint16_t ret_code; /* Current value of retcode */ + uint64_t starttime; + /* set by each bg cmd, cleared by the bg_timer when complete */ + uint64_t runtime; + QEMUTimer *timer; + } bg; + size_t payload_max; + /* Pointer to device hosting the CCI */ + DeviceState *d; + /* Pointer to the device hosting the protocol conversion */ + DeviceState *intf; +} CXLCCI; + typedef struct cxl_device_state { MemoryRegion device_registers; - /* mmio for device capabilities array - 8.2.8.2 */ - MemoryRegion device; + /* CXL r3.1 Section 8.2.8.3: Device Status Registers */ + struct { + MemoryRegion device; + union { + uint8_t dev_reg_state[CXL_DEVICE_STATUS_REGISTERS_LENGTH]; + uint16_t dev_reg_state16[CXL_DEVICE_STATUS_REGISTERS_LENGTH / 2]; + uint32_t dev_reg_state32[CXL_DEVICE_STATUS_REGISTERS_LENGTH / 4]; + uint64_t dev_reg_state64[CXL_DEVICE_STATUS_REGISTERS_LENGTH / 8]; + }; + uint64_t event_status; + }; MemoryRegion memory_device; struct { MemoryRegion caps; @@ -94,63 +211,75 @@ typedef struct cxl_device_state { }; }; - /* mmio for the mailbox registers 8.2.8.4 */ + /* CXL r3.1 Section 8.2.8.4: Mailbox Registers */ struct { MemoryRegion mailbox; uint16_t payload_size; + uint8_t mbox_msi_n; union { uint8_t mbox_reg_state[CXL_MAILBOX_REGISTERS_LENGTH]; uint16_t mbox_reg_state16[CXL_MAILBOX_REGISTERS_LENGTH / 2]; uint32_t mbox_reg_state32[CXL_MAILBOX_REGISTERS_LENGTH / 4]; uint64_t mbox_reg_state64[CXL_MAILBOX_REGISTERS_LENGTH / 8]; }; - struct cel_log { - uint16_t opcode; - uint16_t effect; - } cel_log[1 << 16]; - size_t cel_size; }; + /* Stash the memory device status value */ + uint64_t memdev_status; + struct { bool set; uint64_t last_set; uint64_t host_set; } timestamp; - /* memory region for persistent memory, HDM */ + /* memory region size, HDM */ + uint64_t mem_size; uint64_t pmem_size; + uint64_t vmem_size; + + const struct cxl_cmd (*cxl_cmd_set)[256]; + CXLEventLog event_logs[CXL_EVENT_TYPE_MAX]; } CXLDeviceState; /* Initialize the register block for a device */ -void cxl_device_register_block_init(Object *obj, CXLDeviceState *dev); +void cxl_device_register_block_init(Object *obj, CXLDeviceState *dev, + CXLCCI *cci); +typedef struct CXLType3Dev CXLType3Dev; +typedef struct CSWMBCCIDev CSWMBCCIDev; /* Set up default values for the register block */ -void cxl_device_register_init_common(CXLDeviceState *dev); +void cxl_device_register_init_t3(CXLType3Dev *ct3d); +void cxl_device_register_init_swcci(CSWMBCCIDev *sw); /* - * CXL 2.0 - 8.2.8.1 including errata F4 + * CXL r3.1 Section 8.2.8.1: CXL Device Capabilities Array Register * Documented as a 128 bit register, but 64 bit accesses and the second * 64 bits are currently reserved. */ -REG64(CXL_DEV_CAP_ARRAY, 0) /* Documented as 128 bit register but 64 byte accesses */ +REG64(CXL_DEV_CAP_ARRAY, 0) FIELD(CXL_DEV_CAP_ARRAY, CAP_ID, 0, 16) FIELD(CXL_DEV_CAP_ARRAY, CAP_VERSION, 16, 8) FIELD(CXL_DEV_CAP_ARRAY, CAP_COUNT, 32, 16) +void cxl_event_set_status(CXLDeviceState *cxl_dstate, CXLEventLogType log_type, + bool available); + /* * Helper macro to initialize capability headers for CXL devices. * - * In the 8.2.8.2, this is listed as a 128b register, but in 8.2.8, it says: + * In CXL r3.1 Section 8.2.8.2: CXL Device Capability Header Register, this is + * listed as a 128b register, but in CXL r3.1 Section 8.2.8: CXL Device Register + * Interface, it says: * > No registers defined in Section 8.2.8 are larger than 64-bits wide so that * > is the maximum access size allowed for these registers. If this rule is not - * > followed, the behavior is undefined + * > followed, the behavior is undefined. * - * CXL 2.0 Errata F4 states futher that the layouts in the specification are - * shown as greater than 128 bits, but implementations are expected to - * use any size of access up to 64 bits. + * > To illustrate how the fields fit together, the layouts ... are shown as + * > wider than a 64 bit register. Implementations are expected to use any size + * > accesses for this information up to 64 bits without lost of functionality * - * Here we've chosen to make it 4 dwords. The spec allows any pow2 multiple - * access to be used for a register up to 64 bits. + * Here we've chosen to make it 4 dwords. */ #define CXL_DEVICE_CAPABILITY_HEADER_REGISTER(n, offset) \ REG32(CXL_DEV_##n##_CAP_HDR0, offset) \ @@ -168,10 +297,22 @@ CXL_DEVICE_CAPABILITY_HEADER_REGISTER(MEMORY_DEVICE, CXL_DEVICE_CAP_HDR1_OFFSET + CXL_DEVICE_CAP_REG_SIZE * 2) -int cxl_initialize_mailbox(CXLDeviceState *cxl_dstate); -void cxl_process_mailbox(CXLDeviceState *cxl_dstate); +void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max); +void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf, + DeviceState *d, size_t payload_max); +void cxl_init_cci(CXLCCI *cci, size_t payload_max); +int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, + size_t len_in, uint8_t *pl_in, + size_t *len_out, uint8_t *pl_out, + bool *bg_started); +void cxl_initialize_t3_fm_owned_ld_mctpcci(CXLCCI *cci, DeviceState *d, + DeviceState *intf, + size_t payload_max); -#define cxl_device_cap_init(dstate, reg, cap_id) \ +void cxl_initialize_t3_ld_cci(CXLCCI *cci, DeviceState *d, + DeviceState *intf, size_t payload_max); + +#define cxl_device_cap_init(dstate, reg, cap_id, ver) \ do { \ uint32_t *cap_hdrs = dstate->caps_reg_state32; \ int which = R_CXL_DEV_##reg##_CAP_HDR0; \ @@ -179,7 +320,7 @@ void cxl_process_mailbox(CXLDeviceState *cxl_dstate); FIELD_DP32(cap_hdrs[which], CXL_DEV_##reg##_CAP_HDR0, \ CAP_ID, cap_id); \ cap_hdrs[which] = FIELD_DP32( \ - cap_hdrs[which], CXL_DEV_##reg##_CAP_HDR0, CAP_VERSION, 1); \ + cap_hdrs[which], CXL_DEV_##reg##_CAP_HDR0, CAP_VERSION, ver); \ cap_hdrs[which + 1] = \ FIELD_DP32(cap_hdrs[which + 1], CXL_DEV_##reg##_CAP_HDR1, \ CAP_OFFSET, CXL_##reg##_REGISTERS_OFFSET); \ @@ -188,41 +329,51 @@ void cxl_process_mailbox(CXLDeviceState *cxl_dstate); CAP_LENGTH, CXL_##reg##_REGISTERS_LENGTH); \ } while (0) -/* CXL 2.0 8.2.8.4.3 Mailbox Capabilities Register */ +/* CXL r3.2 Section 8.2.8.3.1: Event Status Register */ +#define CXL_DEVICE_STATUS_VERSION 2 +REG64(CXL_DEV_EVENT_STATUS, 0) + FIELD(CXL_DEV_EVENT_STATUS, EVENT_STATUS, 0, 32) + +#define CXL_DEV_MAILBOX_VERSION 1 +/* CXL r3.1 Section 8.2.8.4.3: Mailbox Capabilities Register */ REG32(CXL_DEV_MAILBOX_CAP, 0) FIELD(CXL_DEV_MAILBOX_CAP, PAYLOAD_SIZE, 0, 5) FIELD(CXL_DEV_MAILBOX_CAP, INT_CAP, 5, 1) FIELD(CXL_DEV_MAILBOX_CAP, BG_INT_CAP, 6, 1) FIELD(CXL_DEV_MAILBOX_CAP, MSI_N, 7, 4) + FIELD(CXL_DEV_MAILBOX_CAP, MBOX_READY_TIME, 11, 8) + FIELD(CXL_DEV_MAILBOX_CAP, TYPE, 19, 4) -/* CXL 2.0 8.2.8.4.4 Mailbox Control Register */ +/* CXL r3.1 Section 8.2.8.4.4: Mailbox Control Register */ REG32(CXL_DEV_MAILBOX_CTRL, 4) FIELD(CXL_DEV_MAILBOX_CTRL, DOORBELL, 0, 1) FIELD(CXL_DEV_MAILBOX_CTRL, INT_EN, 1, 1) FIELD(CXL_DEV_MAILBOX_CTRL, BG_INT_EN, 2, 1) -/* CXL 2.0 8.2.8.4.5 Command Register */ +/* CXL r3.1 Section 8.2.8.4.5: Command Register */ REG64(CXL_DEV_MAILBOX_CMD, 8) FIELD(CXL_DEV_MAILBOX_CMD, COMMAND, 0, 8) FIELD(CXL_DEV_MAILBOX_CMD, COMMAND_SET, 8, 8) FIELD(CXL_DEV_MAILBOX_CMD, LENGTH, 16, 20) -/* CXL 2.0 8.2.8.4.6 Mailbox Status Register */ +/* CXL r3.1 Section 8.2.8.4.6: Mailbox Status Register */ REG64(CXL_DEV_MAILBOX_STS, 0x10) FIELD(CXL_DEV_MAILBOX_STS, BG_OP, 0, 1) FIELD(CXL_DEV_MAILBOX_STS, ERRNO, 32, 16) FIELD(CXL_DEV_MAILBOX_STS, VENDOR_ERRNO, 48, 16) -/* CXL 2.0 8.2.8.4.7 Background Command Status Register */ +/* CXL r3.1 Section 8.2.8.4.7: Background Command Status Register */ REG64(CXL_DEV_BG_CMD_STS, 0x18) FIELD(CXL_DEV_BG_CMD_STS, OP, 0, 16) FIELD(CXL_DEV_BG_CMD_STS, PERCENTAGE_COMP, 16, 7) FIELD(CXL_DEV_BG_CMD_STS, RET_CODE, 32, 16) FIELD(CXL_DEV_BG_CMD_STS, VENDOR_RET_CODE, 48, 16) -/* CXL 2.0 8.2.8.4.8 Command Payload Registers */ +/* CXL r3.1 Section 8.2.8.4.8: Command Payload Registers */ REG32(CXL_DEV_CMD_PAYLOAD, 0x20) +/* CXL r3.1 Section 8.2.8.4.1: Memory Device Status Registers */ +#define CXL_MEM_DEV_STATUS_VERSION 1 REG64(CXL_MEM_DEV_STS, 0) FIELD(CXL_MEM_DEV_STS, FATAL, 0, 1) FIELD(CXL_MEM_DEV_STS, FW_HALT, 1, 1) @@ -230,18 +381,77 @@ REG64(CXL_MEM_DEV_STS, 0) FIELD(CXL_MEM_DEV_STS, MBOX_READY, 4, 1) FIELD(CXL_MEM_DEV_STS, RESET_NEEDED, 5, 3) +static inline void __toggle_media(CXLDeviceState *cxl_dstate, int val) +{ + uint64_t dev_status_reg; + + dev_status_reg = cxl_dstate->memdev_status; + dev_status_reg = FIELD_DP64(dev_status_reg, CXL_MEM_DEV_STS, MEDIA_STATUS, + val); + cxl_dstate->memdev_status = dev_status_reg; +} +#define cxl_dev_disable_media(cxlds) \ + do { __toggle_media((cxlds), 0x3); } while (0) +#define cxl_dev_enable_media(cxlds) \ + do { __toggle_media((cxlds), 0x1); } while (0) + +static inline bool sanitize_running(CXLCCI *cci) +{ + return !!cci->bg.runtime && cci->bg.opcode == 0x4400; +} + +typedef struct CXLError { + QTAILQ_ENTRY(CXLError) node; + int type; /* Error code as per FE definition */ + uint32_t header[CXL_RAS_ERR_HEADER_NUM]; +} CXLError; + +typedef QTAILQ_HEAD(, CXLError) CXLErrorList; + +typedef struct CXLPoison { + uint64_t start, length; + uint8_t type; +#define CXL_POISON_TYPE_EXTERNAL 0x1 +#define CXL_POISON_TYPE_INTERNAL 0x2 +#define CXL_POISON_TYPE_INJECTED 0x3 + QLIST_ENTRY(CXLPoison) node; +} CXLPoison; + +typedef QLIST_HEAD(, CXLPoison) CXLPoisonList; +#define CXL_POISON_LIST_LIMIT 256 + struct CXLType3Dev { /* Private */ PCIDevice parent_obj; /* Properties */ - HostMemoryBackend *hostmem; + HostMemoryBackend *hostmem; /* deprecated */ + HostMemoryBackend *hostvmem; + HostMemoryBackend *hostpmem; HostMemoryBackend *lsa; + uint64_t sn; /* State */ - AddressSpace hostmem_as; + AddressSpace hostvmem_as; + AddressSpace hostpmem_as; CXLComponentState cxl_cstate; CXLDeviceState cxl_dstate; + CXLCCI cci; /* Primary PCI mailbox CCI */ + /* Always initialized as no way to know if a VDM might show up */ + CXLCCI vdm_fm_owned_ld_mctp_cci; + CXLCCI ld0_cci; + + /* DOE */ + DOECap doe_cdat; + + /* Error injection */ + CXLErrorList error_list; + + /* Poison Injection - cache */ + CXLPoisonList poison_list; + unsigned int poison_list_cnt; + bool poison_list_overflowed; + uint64_t poison_list_overflow_ts; }; #define TYPE_CXL_TYPE3 "cxl-type3" @@ -258,11 +468,39 @@ struct CXLType3Class { uint64_t offset); void (*set_lsa)(CXLType3Dev *ct3d, const void *buf, uint64_t size, uint64_t offset); + bool (*set_cacheline)(CXLType3Dev *ct3d, uint64_t dpa_offset, + uint8_t *data); }; +struct CSWMBCCIDev { + PCIDevice parent_obj; + PCIDevice *target; + CXLComponentState cxl_cstate; + CXLDeviceState cxl_dstate; + CXLCCI *cci; +}; + +#define TYPE_CXL_SWITCH_MAILBOX_CCI "cxl-switch-mailbox-cci" +OBJECT_DECLARE_TYPE(CSWMBCCIDev, CSWMBCCIClass, CXL_SWITCH_MAILBOX_CCI) + MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data, unsigned size, MemTxAttrs attrs); MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data, unsigned size, MemTxAttrs attrs); +uint64_t cxl_device_get_timestamp(CXLDeviceState *cxlds); + +void cxl_event_init(CXLDeviceState *cxlds, int start_msg_num); +bool cxl_event_insert(CXLDeviceState *cxlds, CXLEventLogType log_type, + CXLEventRecordRaw *event); +CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl, + uint8_t log_type, int max_recs, + size_t *len); +CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds, + CXLClearEventPayload *pl); + +void cxl_event_irq_assert(CXLType3Dev *ct3d); + +void cxl_set_poison_list_overflowed(CXLType3Dev *ct3d); + #endif diff --git a/include/hw/cxl/cxl_events.h b/include/hw/cxl/cxl_events.h new file mode 100644 index 0000000000..5170b8dbf8 --- /dev/null +++ b/include/hw/cxl/cxl_events.h @@ -0,0 +1,169 @@ +/* + * QEMU CXL Events + * + * Copyright (c) 2022 Intel + * + * This work is licensed under the terms of the GNU GPL, version 2. See the + * COPYING file in the top-level directory. + */ + +#ifndef CXL_EVENTS_H +#define CXL_EVENTS_H + +#include "qemu/uuid.h" + +/* + * CXL r3.1 section 8.2.9.2.2: Get Event Records (Opcode 0100h); Table 8-52 + * + * Define these as the bit position for the event status register for ease of + * setting the status. + */ +typedef enum CXLEventLogType { + CXL_EVENT_TYPE_INFO = 0, + CXL_EVENT_TYPE_WARN = 1, + CXL_EVENT_TYPE_FAIL = 2, + CXL_EVENT_TYPE_FATAL = 3, + CXL_EVENT_TYPE_DYNAMIC_CAP = 4, + CXL_EVENT_TYPE_MAX +} CXLEventLogType; + +/* + * Common Event Record Format + * CXL r3.1 section 8.2.9.2.1: Event Records; Table 8-43 + */ +#define CXL_EVENT_REC_HDR_RES_LEN 0xf +typedef struct CXLEventRecordHdr { + QemuUUID id; + uint8_t length; + uint8_t flags[3]; + uint16_t handle; + uint16_t related_handle; + uint64_t timestamp; + uint8_t maint_op_class; + uint8_t reserved[CXL_EVENT_REC_HDR_RES_LEN]; +} QEMU_PACKED CXLEventRecordHdr; + +#define CXL_EVENT_RECORD_DATA_LENGTH 0x50 +typedef struct CXLEventRecordRaw { + CXLEventRecordHdr hdr; + uint8_t data[CXL_EVENT_RECORD_DATA_LENGTH]; +} QEMU_PACKED CXLEventRecordRaw; +#define CXL_EVENT_RECORD_SIZE (sizeof(CXLEventRecordRaw)) + +/* + * Get Event Records output payload + * CXL r3.1 section 8.2.9.2.2; Table 8-53 + */ +#define CXL_GET_EVENT_FLAG_OVERFLOW BIT(0) +#define CXL_GET_EVENT_FLAG_MORE_RECORDS BIT(1) +typedef struct CXLGetEventPayload { + uint8_t flags; + uint8_t reserved1; + uint16_t overflow_err_count; + uint64_t first_overflow_timestamp; + uint64_t last_overflow_timestamp; + uint16_t record_count; + uint8_t reserved2[0xa]; + CXLEventRecordRaw records[]; +} QEMU_PACKED CXLGetEventPayload; +#define CXL_EVENT_PAYLOAD_HDR_SIZE (sizeof(CXLGetEventPayload)) + +/* + * Clear Event Records input payload + * CXL r3.1 section 8.2.9.2.3; Table 8-54 + */ +typedef struct CXLClearEventPayload { + uint8_t event_log; /* CXLEventLogType */ + uint8_t clear_flags; + uint8_t nr_recs; + uint8_t reserved[3]; + uint16_t handle[]; +} CXLClearEventPayload; + +/* + * Event Interrupt Policy + * + * CXL r3.1 section 8.2.9.2.4; Table 8-55 + */ +typedef enum CXLEventIntMode { + CXL_INT_NONE = 0x00, + CXL_INT_MSI_MSIX = 0x01, + CXL_INT_FW = 0x02, + CXL_INT_RES = 0x03, +} CXLEventIntMode; +#define CXL_EVENT_INT_MODE_MASK 0x3 +#define CXL_EVENT_INT_SETTING(vector) \ + ((((uint8_t)vector & 0xf) << 4) | CXL_INT_MSI_MSIX) +typedef struct CXLEventInterruptPolicy { + uint8_t info_settings; + uint8_t warn_settings; + uint8_t failure_settings; + uint8_t fatal_settings; + uint8_t dyn_cap_settings; +} QEMU_PACKED CXLEventInterruptPolicy; +/* DCD is optional but other fields are not */ +#define CXL_EVENT_INT_SETTING_MIN_LEN 4 + +/* + * General Media Event Record + * CXL r3.1 Section 8.2.9.2.1.1; Table 8-45 + */ +#define CXL_EVENT_GEN_MED_COMP_ID_SIZE 0x10 +#define CXL_EVENT_GEN_MED_RES_SIZE 0x2e +typedef struct CXLEventGenMedia { + CXLEventRecordHdr hdr; + uint64_t phys_addr; + uint8_t descriptor; + uint8_t type; + uint8_t transaction_type; + uint16_t validity_flags; + uint8_t channel; + uint8_t rank; + uint8_t device[3]; + uint8_t component_id[CXL_EVENT_GEN_MED_COMP_ID_SIZE]; + uint8_t reserved[CXL_EVENT_GEN_MED_RES_SIZE]; +} QEMU_PACKED CXLEventGenMedia; + +/* + * DRAM Event Record + * CXL r3.1 Section 8.2.9.2.1.2: Table 8-46 + * All fields little endian. + */ +typedef struct CXLEventDram { + CXLEventRecordHdr hdr; + uint64_t phys_addr; + uint8_t descriptor; + uint8_t type; + uint8_t transaction_type; + uint16_t validity_flags; + uint8_t channel; + uint8_t rank; + uint8_t nibble_mask[3]; + uint8_t bank_group; + uint8_t bank; + uint8_t row[3]; + uint16_t column; + uint64_t correction_mask[4]; + uint8_t reserved[0x17]; +} QEMU_PACKED CXLEventDram; + +/* + * Memory Module Event Record + * CXL r3.1 Section 8.2.9.2.1.3: Table 8-47 + * All fields little endian. + */ +typedef struct CXLEventMemoryModule { + CXLEventRecordHdr hdr; + uint8_t type; + uint8_t health_status; + uint8_t media_status; + uint8_t additional_status; + uint8_t life_used; + int16_t temperature; + uint32_t dirty_shutdown_count; + uint32_t corrected_volatile_error_count; + uint32_t corrected_persistent_error_count; + uint8_t reserved[0x3d]; +} QEMU_PACKED CXLEventMemoryModule; + +#endif /* CXL_EVENTS_H */ diff --git a/include/hw/cxl/cxl_host.h b/include/hw/cxl/cxl_host.h new file mode 100644 index 0000000000..c9bc9c7c50 --- /dev/null +++ b/include/hw/cxl/cxl_host.h @@ -0,0 +1,22 @@ +/* + * QEMU CXL Host Setup + * + * Copyright (c) 2022 Huawei + * + * This work is licensed under the terms of the GNU GPL, version 2. See the + * COPYING file in the top-level directory. + */ + +#include "hw/cxl/cxl.h" +#include "hw/boards.h" + +#ifndef CXL_HOST_H +#define CXL_HOST_H + +void cxl_machine_init(Object *obj, CXLState *state); +void cxl_fmws_link_targets(CXLState *stat, Error **errp); +void cxl_hook_up_pxb_registers(PCIBus *bus, CXLState *state, Error **errp); + +extern const MemoryRegionOps cfmws_ops; + +#endif diff --git a/include/hw/cxl/cxl_pci.h b/include/hw/cxl/cxl_pci.h index 01cf002096..d0855ed78b 100644 --- a/include/hw/cxl/cxl_pci.h +++ b/include/hw/cxl/cxl_pci.h @@ -10,18 +10,14 @@ #ifndef CXL_PCI_H #define CXL_PCI_H -#include "qemu/compiler.h" -#include "hw/pci/pci.h" -#include "hw/pci/pcie.h" #define CXL_VENDOR_ID 0x1e98 #define PCIE_DVSEC_HEADER1_OFFSET 0x4 /* Offset from start of extend cap */ #define PCIE_DVSEC_ID_OFFSET 0x8 -#define PCIE_CXL_DEVICE_DVSEC_LENGTH 0x38 -#define PCIE_CXL1_DEVICE_DVSEC_REVID 0 -#define PCIE_CXL2_DEVICE_DVSEC_REVID 1 +#define PCIE_CXL_DEVICE_DVSEC_LENGTH 0x3C +#define PCIE_CXL31_DEVICE_DVSEC_REVID 3 #define EXTENSIONS_PORT_DVSEC_LENGTH 0x28 #define EXTENSIONS_PORT_DVSEC_REVID 0 @@ -32,8 +28,8 @@ #define GPF_DEVICE_DVSEC_LENGTH 0x10 #define GPF_DEVICE_DVSEC_REVID 0 -#define PCIE_FLEXBUS_PORT_DVSEC_LENGTH_2_0 0x14 -#define PCIE_FLEXBUS_PORT_DVSEC_REVID_2_0 1 +#define PCIE_CXL3_FLEXBUS_PORT_DVSEC_LENGTH 0x20 +#define PCIE_CXL3_FLEXBUS_PORT_DVSEC_REVID 2 #define REG_LOC_DVSEC_LENGTH 0x24 #define REG_LOC_DVSEC_REVID 0 @@ -58,16 +54,26 @@ typedef struct DVSECHeader { QEMU_BUILD_BUG_ON(sizeof(DVSECHeader) != 10); /* - * CXL 2.0 devices must implement certain DVSEC IDs, and can [optionally] + * CXL r3.1 Table 8-2: CXL DVSEC ID Assignment + * Devices must implement certain DVSEC IDs, and can [optionally] * implement others. + * (x) - IDs in Table 8-2. * - * CXL 2.0 Device: 0, [2], 5, 8 - * CXL 2.0 RP: 3, 4, 7, 8 - * CXL 2.0 Upstream Port: [2], 7, 8 - * CXL 2.0 Downstream Port: 3, 4, 7, 8 + * CXL RCD (D1): 0, [2], [5], 7, [8], A - Not emulated yet + * CXL RCD USP (UP1): 7, [8] - Not emulated yet + * CXL RCH DSP (DP1): 7, [8] + * CXL SLD (D2): 0, [2], 5, 7, 8, [A] + * CXL LD (LD): 0, [2], 5, 7, 8 + * CXL RP (R): 3, 4, 7, 8 + * CXL Switch USP (USP): [2], 7, 8 + * CXL Switch DSP (DSP): 3, 4, 7, 8 + * FM-Owned LD (FMLD): 0, [2], 7, 8, 9 */ -/* CXL 2.0 - 8.1.3 (ID 0001) */ +/* + * CXL r3.1 Section 8.1.3: PCIe DVSEC for Devices + * DVSEC ID: 0, Revision: 3 + */ typedef struct CXLDVSECDevice { DVSECHeader hdr; uint16_t cap; @@ -85,11 +91,16 @@ typedef struct CXLDVSECDevice { uint32_t range2_size_lo; uint32_t range2_base_hi; uint32_t range2_base_lo; -} CXLDVSECDevice; -QEMU_BUILD_BUG_ON(sizeof(CXLDVSECDevice) != 0x38); + uint16_t cap3; + uint16_t resv; +} QEMU_PACKED CXLDVSECDevice; +QEMU_BUILD_BUG_ON(sizeof(CXLDVSECDevice) != PCIE_CXL_DEVICE_DVSEC_LENGTH); -/* CXL 2.0 - 8.1.5 (ID 0003) */ -typedef struct CXLDVSECPortExtensions { +/* + * CXL r3.1 Section 8.1.5: CXL Extensions DVSEC for Ports + * DVSEC ID: 3, Revision: 0 + */ +typedef struct CXLDVSECPortExt { DVSECHeader hdr; uint16_t status; uint16_t control; @@ -103,14 +114,17 @@ typedef struct CXLDVSECPortExtensions { uint32_t alt_prefetch_limit_high; uint32_t rcrb_base; uint32_t rcrb_base_high; -} CXLDVSECPortExtensions; -QEMU_BUILD_BUG_ON(sizeof(CXLDVSECPortExtensions) != 0x28); +} CXLDVSECPortExt; +QEMU_BUILD_BUG_ON(sizeof(CXLDVSECPortExt) != 0x28); #define PORT_CONTROL_OFFSET 0xc #define PORT_CONTROL_UNMASK_SBR 1 #define PORT_CONTROL_ALT_MEMID_EN 4 -/* CXL 2.0 - 8.1.6 GPF DVSEC (ID 0004) */ +/* + * CXL r3.1 Section 8.1.6: GPF DVSEC for CXL Port + * DVSEC ID: 4, Revision: 0 + */ typedef struct CXLDVSECPortGPF { DVSECHeader hdr; uint16_t rsvd; @@ -119,7 +133,10 @@ typedef struct CXLDVSECPortGPF { } CXLDVSECPortGPF; QEMU_BUILD_BUG_ON(sizeof(CXLDVSECPortGPF) != 0x10); -/* CXL 2.0 - 8.1.7 GPF DVSEC for CXL Device */ +/* + * CXL r3.1 Section 8.1.7: GPF DVSEC for CXL Device + * DVSEC ID: 5, Revision 0 + */ typedef struct CXLDVSECDeviceGPF { DVSECHeader hdr; uint16_t phase2_duration; @@ -127,17 +144,27 @@ typedef struct CXLDVSECDeviceGPF { } CXLDVSECDeviceGPF; QEMU_BUILD_BUG_ON(sizeof(CXLDVSECDeviceGPF) != 0x10); -/* CXL 2.0 - 8.1.8/8.2.1.3 Flex Bus DVSEC (ID 0007) */ +/* + * CXL r3.1 Section 8.1.8: PCIe DVSEC for Flex Bus Port + * CXL r3.1 Section 8.2.1.3: Flex Bus Port DVSEC + * DVSEC ID: 7, Revision 2 + */ typedef struct CXLDVSECPortFlexBus { DVSECHeader hdr; uint16_t cap; uint16_t ctrl; uint16_t status; uint32_t rcvd_mod_ts_data_phase1; + uint32_t cap2; + uint32_t ctrl2; + uint32_t status2; } CXLDVSECPortFlexBus; -QEMU_BUILD_BUG_ON(sizeof(CXLDVSECPortFlexBus) != 0x14); +QEMU_BUILD_BUG_ON(sizeof(CXLDVSECPortFlexBus) != 0x20); -/* CXL 2.0 - 8.1.9 Register Locator DVSEC (ID 0008) */ +/* + * CXL r3.1 Section 8.1.9: Register Locator DVSEC + * DVSEC ID: 8, Revision 0 + */ typedef struct CXLDVSECRegisterLocator { DVSECHeader hdr; uint16_t rsvd; diff --git a/include/hw/display/bcm2835_fb.h b/include/hw/display/bcm2835_fb.h index 38671afffd..49541bf08f 100644 --- a/include/hw/display/bcm2835_fb.h +++ b/include/hw/display/bcm2835_fb.h @@ -16,6 +16,8 @@ #include "ui/console.h" #include "qom/object.h" +#define UPPER_RAM_BASE 0x40000000 + #define TYPE_BCM2835_FB "bcm2835-fb" OBJECT_DECLARE_SIMPLE_TYPE(BCM2835FBState, BCM2835_FB) diff --git a/include/hw/display/macfb.h b/include/hw/display/macfb.h index 55a50d3fb0..27cebefc9e 100644 --- a/include/hw/display/macfb.h +++ b/include/hw/display/macfb.h @@ -15,9 +15,10 @@ #include "exec/memory.h" #include "hw/irq.h" +#include "hw/nubus/nubus.h" +#include "hw/sysbus.h" #include "ui/console.h" #include "qemu/timer.h" -#include "qom/object.h" typedef enum { MACFB_DISPLAY_APPLE_21_COLOR = 0, diff --git a/include/hw/display/ramfb.h b/include/hw/display/ramfb.h index b33a2c467b..a7e0019144 100644 --- a/include/hw/display/ramfb.h +++ b/include/hw/display/ramfb.h @@ -1,11 +1,15 @@ #ifndef RAMFB_H #define RAMFB_H +#include "migration/vmstate.h" + /* ramfb.c */ typedef struct RAMFBState RAMFBState; void ramfb_display_update(QemuConsole *con, RAMFBState *s); RAMFBState *ramfb_setup(Error **errp); +extern const VMStateDescription ramfb_vmstate; + /* ramfb-standalone.c */ #define TYPE_RAMFB_DEVICE "ramfb" diff --git a/include/hw/dma/i8257.h b/include/hw/dma/i8257.h index f652345d65..4342e4a91e 100644 --- a/include/hw/dma/i8257.h +++ b/include/hw/dma/i8257.h @@ -45,6 +45,6 @@ struct I8257State { PortioList portio_pageh; }; -void i8257_dma_init(ISABus *bus, bool high_page_enable); +void i8257_dma_init(Object *parent, ISABus *bus, bool high_page_enable); #endif diff --git a/include/hw/dma/sifive_pdma.h b/include/hw/dma/sifive_pdma.h index e319bbd6c4..8c6cfa7f32 100644 --- a/include/hw/dma/sifive_pdma.h +++ b/include/hw/dma/sifive_pdma.h @@ -23,6 +23,8 @@ #ifndef SIFIVE_PDMA_H #define SIFIVE_PDMA_H +#include "hw/sysbus.h" + struct sifive_pdma_chan { uint32_t control; uint32_t next_config; diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index 7c3b1d0f6c..9c35d1b9da 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -1,30 +1,30 @@ static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr) { - bswap16s(&ehdr->e_type); /* Object file type */ - bswap16s(&ehdr->e_machine); /* Architecture */ - bswap32s(&ehdr->e_version); /* Object file version */ - bswapSZs(&ehdr->e_entry); /* Entry point virtual address */ - bswapSZs(&ehdr->e_phoff); /* Program header table file offset */ - bswapSZs(&ehdr->e_shoff); /* Section header table file offset */ - bswap32s(&ehdr->e_flags); /* Processor-specific flags */ - bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ - bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ - bswap16s(&ehdr->e_phnum); /* Program header table entry count */ - bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ - bswap16s(&ehdr->e_shnum); /* Section header table entry count */ - bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ + bswap16s(&ehdr->e_type); /* Object file type */ + bswap16s(&ehdr->e_machine); /* Architecture */ + bswap32s(&ehdr->e_version); /* Object file version */ + bswapSZs(&ehdr->e_entry); /* Entry point virtual address */ + bswapSZs(&ehdr->e_phoff); /* Program header table file offset */ + bswapSZs(&ehdr->e_shoff); /* Section header table file offset */ + bswap32s(&ehdr->e_flags); /* Processor-specific flags */ + bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ + bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ + bswap16s(&ehdr->e_phnum); /* Program header table entry count */ + bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ + bswap16s(&ehdr->e_shnum); /* Section header table entry count */ + bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ } static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr) { - bswap32s(&phdr->p_type); /* Segment type */ - bswapSZs(&phdr->p_offset); /* Segment file offset */ - bswapSZs(&phdr->p_vaddr); /* Segment virtual address */ - bswapSZs(&phdr->p_paddr); /* Segment physical address */ - bswapSZs(&phdr->p_filesz); /* Segment size in file */ - bswapSZs(&phdr->p_memsz); /* Segment size in memory */ - bswap32s(&phdr->p_flags); /* Segment flags */ - bswapSZs(&phdr->p_align); /* Segment alignment */ + bswap32s(&phdr->p_type); /* Segment type */ + bswapSZs(&phdr->p_offset); /* Segment file offset */ + bswapSZs(&phdr->p_vaddr); /* Segment virtual address */ + bswapSZs(&phdr->p_paddr); /* Segment physical address */ + bswapSZs(&phdr->p_filesz); /* Segment size in file */ + bswapSZs(&phdr->p_memsz); /* Segment size in memory */ + bswap32s(&phdr->p_flags); /* Segment flags */ + bswapSZs(&phdr->p_align); /* Segment alignment */ } static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr) @@ -117,7 +117,7 @@ static void glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, shdr_table = load_at(fd, ehdr->e_shoff, sizeof(struct elf_shdr) * ehdr->e_shnum); if (!shdr_table) { - return ; + return; } if (must_swab) { @@ -385,10 +385,11 @@ static ssize_t glue(load_elf, SZ)(const char *name, int fd, } if (pflags) { - *pflags = (elf_word)ehdr.e_flags; + *pflags = ehdr.e_flags; + } + if (pentry) { + *pentry = ehdr.e_entry; } - if (pentry) - *pentry = (uint64_t)(elf_sword)ehdr.e_entry; glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb, sym_cb); @@ -499,7 +500,7 @@ static ssize_t glue(load_elf, SZ)(const char *name, int fd, } if (data_swab) { - int j; + elf_word j; for (j = 0; j < file_size; j += (1 << data_swab)) { uint8_t *dp = data + j; switch (data_swab) { @@ -610,10 +611,12 @@ static ssize_t glue(load_elf, SZ)(const char *name, int fd, } } - if (lowaddr) - *lowaddr = (uint64_t)(elf_sword)low; - if (highaddr) - *highaddr = (uint64_t)(elf_sword)high; + if (lowaddr) { + *lowaddr = low; + } + if (highaddr) { + *highaddr = high; + } ret = total_size; fail: if (mapped_file) { diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h index 4b7ad77a44..8d3fb2fb3b 100644 --- a/include/hw/firmware/smbios.h +++ b/include/hw/firmware/smbios.h @@ -2,6 +2,7 @@ #define QEMU_SMBIOS_H #include "qapi/qapi-types-machine.h" +#include "qemu/bitmap.h" /* * SMBIOS Support @@ -16,8 +17,28 @@ * */ +extern uint8_t *usr_blobs; +extern GArray *usr_blobs_sizes; + +typedef struct { + const char *vendor, *version, *date; + bool have_major_minor, uefi; + uint8_t major, minor; +} smbios_type0_t; +extern smbios_type0_t smbios_type0; + +typedef struct { + const char *manufacturer, *product, *version, *serial, *sku, *family; + /* uuid is in qemu_uuid */ +} smbios_type1_t; +extern smbios_type1_t smbios_type1; #define SMBIOS_MAX_TYPE 127 +extern DECLARE_BITMAP(smbios_have_binfile_bitmap, SMBIOS_MAX_TYPE + 1); +extern DECLARE_BITMAP(smbios_have_fields_bitmap, SMBIOS_MAX_TYPE + 1); + +#define offsetofend(TYPE, MEMBER) \ + (offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER)) /* memory area description, used by type 19 table */ struct smbios_phys_mem_area { @@ -187,6 +208,43 @@ struct smbios_type_4 { uint8_t thread_count; uint16_t processor_characteristics; uint16_t processor_family2; + /* SMBIOS spec 3.0.0, Table 21 */ + uint16_t core_count2; + uint16_t core_enabled2; + uint16_t thread_count2; +} QEMU_PACKED; + +typedef enum smbios_type_4_len_ver { + SMBIOS_TYPE_4_LEN_V28 = offsetofend(struct smbios_type_4, + processor_family2), + SMBIOS_TYPE_4_LEN_V30 = offsetofend(struct smbios_type_4, thread_count2), +} smbios_type_4_len_ver; + +/* SMBIOS type 8 - Port Connector Information */ +struct smbios_type_8 { + struct smbios_structure_header header; + uint8_t internal_reference_str; + uint8_t internal_connector_type; + uint8_t external_reference_str; + uint8_t external_connector_type; + uint8_t port_type; +} QEMU_PACKED; + +/* SMBIOS type 9 - System Slots (v2.1+) */ +struct smbios_type_9 { + struct smbios_structure_header header; + uint8_t slot_designation; + uint8_t slot_type; + uint8_t slot_data_bus_width; + uint8_t current_usage; + uint8_t slot_length; + uint16_t slot_id; + uint8_t slot_characteristics1; + uint8_t slot_characteristics2; + /* SMBIOS spec v2.6+ */ + uint16_t segment_group_number; + uint8_t bus_number; + uint8_t device_number; } QEMU_PACKED; /* SMBIOS type 11 - OEM strings */ @@ -268,13 +326,17 @@ struct smbios_type_127 { struct smbios_structure_header header; } QEMU_PACKED; +bool smbios_validate_table(SmbiosEntryPointType ep_type, Error **errp); +void smbios_add_usr_blob_size(size_t size); void smbios_entry_add(QemuOpts *opts, Error **errp); void smbios_set_cpuid(uint32_t version, uint32_t features); void smbios_set_defaults(const char *manufacturer, const char *product, - const char *version, bool legacy_mode, - bool uuid_encoded, SmbiosEntryPointType ep_type); -uint8_t *smbios_get_table_legacy(MachineState *ms, size_t *length); + const char *version, + bool uuid_encoded); +void smbios_set_default_processor_family(uint16_t processor_family); +uint8_t *smbios_get_table_legacy(size_t *length, Error **errp); void smbios_get_tables(MachineState *ms, + SmbiosEntryPointType ep_type, const struct smbios_phys_mem_area *mem_array, const unsigned int mem_array_size, uint8_t **tables, size_t *tables_len, diff --git a/include/hw/fsi/aspeed_apb2opb.h b/include/hw/fsi/aspeed_apb2opb.h new file mode 100644 index 0000000000..f6a2387abf --- /dev/null +++ b/include/hw/fsi/aspeed_apb2opb.h @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 IBM Corp. + * + * ASPEED APB2OPB Bridge + * IBM On-Chip Peripheral Bus + */ +#ifndef FSI_ASPEED_APB2OPB_H +#define FSI_ASPEED_APB2OPB_H + +#include "exec/memory.h" +#include "hw/fsi/fsi-master.h" +#include "hw/sysbus.h" + +#define TYPE_FSI_OPB "fsi.opb" + +#define TYPE_OP_BUS "opb" +OBJECT_DECLARE_SIMPLE_TYPE(OPBus, OP_BUS) + +typedef struct OPBus { + BusState bus; + + MemoryRegion mr; + AddressSpace as; +} OPBus; + +#define TYPE_ASPEED_APB2OPB "aspeed.apb2opb" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedAPB2OPBState, ASPEED_APB2OPB) + +#define ASPEED_APB2OPB_NR_REGS ((0xe8 >> 2) + 1) + +#define ASPEED_FSI_NUM 2 + +typedef struct AspeedAPB2OPBState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + uint32_t regs[ASPEED_APB2OPB_NR_REGS]; + qemu_irq irq; + + OPBus opb[ASPEED_FSI_NUM]; + FSIMasterState fsi[ASPEED_FSI_NUM]; +} AspeedAPB2OPBState; + +#endif /* FSI_ASPEED_APB2OPB_H */ diff --git a/include/hw/fsi/cfam.h b/include/hw/fsi/cfam.h new file mode 100644 index 0000000000..7abc3b287b --- /dev/null +++ b/include/hw/fsi/cfam.h @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 IBM Corp. + * + * IBM Common FRU Access Macro + */ +#ifndef FSI_CFAM_H +#define FSI_CFAM_H + +#include "exec/memory.h" + +#include "hw/fsi/fsi.h" +#include "hw/fsi/lbus.h" + +#define TYPE_FSI_CFAM "cfam" +#define FSI_CFAM(obj) OBJECT_CHECK(FSICFAMState, (obj), TYPE_FSI_CFAM) + +/* P9-ism */ +#define CFAM_CONFIG_NR_REGS 0x28 + +typedef struct FSICFAMState { + /* < private > */ + FSISlaveState parent; + + /* CFAM config address space */ + MemoryRegion config_iomem; + + MemoryRegion mr; + + FSILBus lbus; + FSIScratchPad scratchpad; +} FSICFAMState; + +#endif /* FSI_CFAM_H */ diff --git a/include/hw/fsi/fsi-master.h b/include/hw/fsi/fsi-master.h new file mode 100644 index 0000000000..68e5f56db2 --- /dev/null +++ b/include/hw/fsi/fsi-master.h @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 IBM Corp. + * + * IBM Flexible Service Interface Master + */ +#ifndef FSI_FSI_MASTER_H +#define FSI_FSI_MASTER_H + +#include "exec/memory.h" +#include "hw/qdev-core.h" +#include "hw/fsi/fsi.h" +#include "hw/fsi/cfam.h" + +#define TYPE_FSI_MASTER "fsi.master" +OBJECT_DECLARE_SIMPLE_TYPE(FSIMasterState, FSI_MASTER) + +#define FSI_MASTER_NR_REGS ((0x2e0 >> 2) + 1) + +typedef struct FSIMasterState { + DeviceState parent; + MemoryRegion iomem; + MemoryRegion opb2fsi; + + FSIBus bus; + + uint32_t regs[FSI_MASTER_NR_REGS]; + FSICFAMState cfam; +} FSIMasterState; + + +#endif /* FSI_FSI_H */ diff --git a/include/hw/fsi/fsi.h b/include/hw/fsi/fsi.h new file mode 100644 index 0000000000..e00f6ef078 --- /dev/null +++ b/include/hw/fsi/fsi.h @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 IBM Corp. + * + * IBM Flexible Service Interface + */ +#ifndef FSI_FSI_H +#define FSI_FSI_H + +#include "exec/memory.h" +#include "hw/qdev-core.h" +#include "hw/fsi/lbus.h" +#include "qemu/bitops.h" + +/* Bitwise operations at the word level. */ +#define BE_GENMASK(hb, lb) MAKE_64BIT_MASK((lb), ((hb) - (lb) + 1)) + +#define TYPE_FSI_BUS "fsi.bus" +OBJECT_DECLARE_SIMPLE_TYPE(FSIBus, FSI_BUS) + +typedef struct FSIBus { + BusState bus; +} FSIBus; + +#define TYPE_FSI_SLAVE "fsi.slave" +OBJECT_DECLARE_SIMPLE_TYPE(FSISlaveState, FSI_SLAVE) + +#define FSI_SLAVE_CONTROL_NR_REGS ((0x40 >> 2) + 1) + +typedef struct FSISlaveState { + DeviceState parent; + + MemoryRegion iomem; + uint32_t regs[FSI_SLAVE_CONTROL_NR_REGS]; +} FSISlaveState; + +#endif /* FSI_FSI_H */ diff --git a/include/hw/fsi/lbus.h b/include/hw/fsi/lbus.h new file mode 100644 index 0000000000..558268c013 --- /dev/null +++ b/include/hw/fsi/lbus.h @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 IBM Corp. + * + * IBM Local bus and connected device structures. + */ +#ifndef FSI_LBUS_H +#define FSI_LBUS_H + +#include "hw/qdev-core.h" +#include "qemu/units.h" +#include "exec/memory.h" + +#define TYPE_FSI_LBUS_DEVICE "fsi.lbus.device" +OBJECT_DECLARE_SIMPLE_TYPE(FSILBusDevice, FSI_LBUS_DEVICE) + +typedef struct FSILBusDevice { + DeviceState parent; + + MemoryRegion iomem; +} FSILBusDevice; + +#define TYPE_FSI_LBUS "fsi.lbus" +OBJECT_DECLARE_SIMPLE_TYPE(FSILBus, FSI_LBUS) + +typedef struct FSILBus { + BusState bus; + + MemoryRegion mr; +} FSILBus; + +#define TYPE_FSI_SCRATCHPAD "fsi.scratchpad" +#define SCRATCHPAD(obj) OBJECT_CHECK(FSIScratchPad, (obj), TYPE_FSI_SCRATCHPAD) + +#define FSI_SCRATCHPAD_NR_REGS 4 + +typedef struct FSIScratchPad { + FSILBusDevice parent; + + uint32_t regs[FSI_SCRATCHPAD_NR_REGS]; +} FSIScratchPad; + +#endif /* FSI_LBUS_H */ diff --git a/include/hw/gpio/bcm2838_gpio.h b/include/hw/gpio/bcm2838_gpio.h new file mode 100644 index 0000000000..f2a57a697f --- /dev/null +++ b/include/hw/gpio/bcm2838_gpio.h @@ -0,0 +1,45 @@ +/* + * Raspberry Pi (BCM2838) GPIO Controller + * This implementation is based on bcm2835_gpio (hw/gpio/bcm2835_gpio.c) + * + * Copyright (c) 2022 Auriga LLC + * + * Authors: + * Lotosh, Aleksey + * + * 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 BCM2838_GPIO_H +#define BCM2838_GPIO_H + +#include "hw/sd/sd.h" +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_BCM2838_GPIO "bcm2838-gpio" +OBJECT_DECLARE_SIMPLE_TYPE(BCM2838GpioState, BCM2838_GPIO) + +#define BCM2838_GPIO_REGS_SIZE 0x1000 +#define BCM2838_GPIO_NUM 58 +#define GPIO_PUP_PDN_CNTRL_NUM 4 + +struct BCM2838GpioState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + /* SDBus selector */ + SDBus sdbus; + SDBus *sdbus_sdhci; + SDBus *sdbus_sdhost; + + uint8_t fsel[BCM2838_GPIO_NUM]; + uint32_t lev0, lev1; + uint8_t sd_fsel; + qemu_irq out[BCM2838_GPIO_NUM]; + uint32_t pup_cntrl_reg[GPIO_PUP_PDN_CNTRL_NUM]; +}; + +#endif diff --git a/include/hw/gpio/nrf51_gpio.h b/include/hw/gpio/nrf51_gpio.h index 8f9c2f86da..fcfa2bac17 100644 --- a/include/hw/gpio/nrf51_gpio.h +++ b/include/hw/gpio/nrf51_gpio.h @@ -64,6 +64,7 @@ struct NRF51GPIOState { uint32_t old_out_connected; qemu_irq output[NRF51_GPIO_PINS]; + qemu_irq detect; }; diff --git a/include/hw/misc/pca9552.h b/include/hw/gpio/pca9552.h similarity index 89% rename from include/hw/misc/pca9552.h rename to include/hw/gpio/pca9552.h index b6f4e264fe..c36525f0c3 100644 --- a/include/hw/misc/pca9552.h +++ b/include/hw/gpio/pca9552.h @@ -30,7 +30,8 @@ struct PCA955xState { uint8_t pointer; uint8_t regs[PCA955X_NR_REGS]; - qemu_irq gpio[PCA955X_PIN_COUNT_MAX]; + qemu_irq gpio_out[PCA955X_PIN_COUNT_MAX]; + uint8_t ext_state[PCA955X_PIN_COUNT_MAX]; char *description; /* For debugging purpose only */ }; diff --git a/include/hw/misc/pca9552_regs.h b/include/hw/gpio/pca9552_regs.h similarity index 100% rename from include/hw/misc/pca9552_regs.h rename to include/hw/gpio/pca9552_regs.h diff --git a/include/hw/gpio/pca9554.h b/include/hw/gpio/pca9554.h new file mode 100644 index 0000000000..54bfc4c4c7 --- /dev/null +++ b/include/hw/gpio/pca9554.h @@ -0,0 +1,36 @@ +/* + * PCA9554 I/O port + * + * Copyright (c) 2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef PCA9554_H +#define PCA9554_H + +#include "hw/i2c/i2c.h" +#include "qom/object.h" + +#define TYPE_PCA9554 "pca9554" +typedef struct PCA9554State PCA9554State; +DECLARE_INSTANCE_CHECKER(PCA9554State, PCA9554, + TYPE_PCA9554) + +#define PCA9554_NR_REGS 4 +#define PCA9554_PIN_COUNT 8 + +struct PCA9554State { + /*< private >*/ + I2CSlave i2c; + /*< public >*/ + + uint8_t len; + uint8_t pointer; + + uint8_t regs[PCA9554_NR_REGS]; + qemu_irq gpio_out[PCA9554_PIN_COUNT]; + uint8_t ext_state[PCA9554_PIN_COUNT]; + char *description; /* For debugging purpose only */ +}; + +#endif diff --git a/include/hw/gpio/pca9554_regs.h b/include/hw/gpio/pca9554_regs.h new file mode 100644 index 0000000000..602c4a90e0 --- /dev/null +++ b/include/hw/gpio/pca9554_regs.h @@ -0,0 +1,19 @@ +/* + * PCA9554 I/O port registers + * + * Copyright (c) 2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef PCA9554_REGS_H +#define PCA9554_REGS_H + +/* + * Bits [0:1] are used to address a specific register. + */ +#define PCA9554_INPUT 0 /* read only input register */ +#define PCA9554_OUTPUT 1 /* read/write pin output state */ +#define PCA9554_POLARITY 2 /* Set polarity of input register */ +#define PCA9554_CONFIG 3 /* Set pins as inputs our ouputs */ + +#endif diff --git a/include/hw/gpio/pcf8574.h b/include/hw/gpio/pcf8574.h new file mode 100644 index 0000000000..3291d7dbbc --- /dev/null +++ b/include/hw/gpio/pcf8574.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * NXP PCF8574 8-port I2C GPIO expansion chip. + * + * Copyright (c) 2024 KNS Group (YADRO). + * Written by Dmitrii Sharikhin + */ + +#ifndef _HW_GPIO_PCF8574 +#define _HW_GPIO_PCF8574 + +#define TYPE_PCF8574 "pcf8574" + +#endif /* _HW_GPIO_PCF8574 */ diff --git a/include/hw/gpio/stm32l4x5_gpio.h b/include/hw/gpio/stm32l4x5_gpio.h new file mode 100644 index 0000000000..878bd19fc9 --- /dev/null +++ b/include/hw/gpio/stm32l4x5_gpio.h @@ -0,0 +1,71 @@ +/* + * STM32L4x5 GPIO (General Purpose Input/Ouput) + * + * Copyright (c) 2024 Arnaud Minier + * Copyright (c) 2024 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html + */ + +#ifndef HW_STM32L4X5_GPIO_H +#define HW_STM32L4X5_GPIO_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_STM32L4X5_GPIO "stm32l4x5-gpio" +OBJECT_DECLARE_SIMPLE_TYPE(Stm32l4x5GpioState, STM32L4X5_GPIO) + +#define NUM_GPIOS 8 +#define GPIO_NUM_PINS 16 + +struct Stm32l4x5GpioState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + /* GPIO registers */ + uint32_t moder; + uint32_t otyper; + uint32_t ospeedr; + uint32_t pupdr; + uint32_t idr; + uint32_t odr; + uint32_t lckr; + uint32_t afrl; + uint32_t afrh; + uint32_t ascr; + + /* GPIO registers reset values */ + uint32_t moder_reset; + uint32_t ospeedr_reset; + uint32_t pupdr_reset; + + /* + * External driving of pins. + * The pins can be set externally through the device + * anonymous input GPIOs lines under certain conditions. + * The pin must not be in push-pull output mode, + * and can't be set high in open-drain mode. + * Pins driven externally and configured to + * output mode will in general be "disconnected" + * (see `get_gpio_pinmask_to_disconnect()`) + */ + uint16_t disconnected_pins; + uint16_t pins_connected_high; + + char *name; + Clock *clk; + qemu_irq pin[GPIO_NUM_PINS]; +}; + +#endif diff --git a/include/hw/hotplug.h b/include/hw/hotplug.h index e15f59c8b3..a9840ed485 100644 --- a/include/hw/hotplug.h +++ b/include/hw/hotplug.h @@ -48,6 +48,7 @@ typedef void (*hotplug_fn)(HotplugHandler *plug_handler, * @unplug: unplug callback. * Used for device removal with devices that implement * asynchronous and synchronous (surprise) removal. + * @is_hotpluggable_bus: called to check if bus/its parent allow hotplug on bus */ struct HotplugHandlerClass { /* */ @@ -58,6 +59,7 @@ struct HotplugHandlerClass { hotplug_fn plug; hotplug_fn unplug_request; hotplug_fn unplug; + bool (*is_hotpluggable_bus)(HotplugHandler *plug_handler, BusState *bus); }; /** diff --git a/include/hw/hyperv/dynmem-proto.h b/include/hw/hyperv/dynmem-proto.h new file mode 100644 index 0000000000..68b8b606f2 --- /dev/null +++ b/include/hw/hyperv/dynmem-proto.h @@ -0,0 +1,430 @@ +#ifndef HW_HYPERV_DYNMEM_PROTO_H +#define HW_HYPERV_DYNMEM_PROTO_H + +/* + * Hyper-V Dynamic Memory Protocol definitions + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * Based on drivers/hv/hv_balloon.c from Linux kernel: + * Copyright (c) 2012, Microsoft Corporation. + * + * Author: K. Y. Srinivasan + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +/* + * Protocol versions. The low word is the minor version, the high word the major + * version. + * + * History: + * Initial version 1.0 + * Changed to 0.1 on 2009/03/25 + * Changes to 0.2 on 2009/05/14 + * Changes to 0.3 on 2009/12/03 + * Changed to 1.0 on 2011/04/05 + * Changed to 2.0 on 2019/12/10 + */ + +#define DYNMEM_MAKE_VERSION(Major, Minor) ((uint32_t)(((Major) << 16) | (Minor))) +#define DYNMEM_MAJOR_VERSION(Version) ((uint32_t)(Version) >> 16) +#define DYNMEM_MINOR_VERSION(Version) ((uint32_t)(Version) & 0xff) + +enum { + DYNMEM_PROTOCOL_VERSION_1 = DYNMEM_MAKE_VERSION(0, 3), + DYNMEM_PROTOCOL_VERSION_2 = DYNMEM_MAKE_VERSION(1, 0), + DYNMEM_PROTOCOL_VERSION_3 = DYNMEM_MAKE_VERSION(2, 0), + + DYNMEM_PROTOCOL_VERSION_WIN7 = DYNMEM_PROTOCOL_VERSION_1, + DYNMEM_PROTOCOL_VERSION_WIN8 = DYNMEM_PROTOCOL_VERSION_2, + DYNMEM_PROTOCOL_VERSION_WIN10 = DYNMEM_PROTOCOL_VERSION_3, + + DYNMEM_PROTOCOL_VERSION_CURRENT = DYNMEM_PROTOCOL_VERSION_WIN10 +}; + + + +/* + * Message Types + */ + +enum dm_message_type { + /* + * Version 0.3 + */ + DM_ERROR = 0, + DM_VERSION_REQUEST = 1, + DM_VERSION_RESPONSE = 2, + DM_CAPABILITIES_REPORT = 3, + DM_CAPABILITIES_RESPONSE = 4, + DM_STATUS_REPORT = 5, + DM_BALLOON_REQUEST = 6, + DM_BALLOON_RESPONSE = 7, + DM_UNBALLOON_REQUEST = 8, + DM_UNBALLOON_RESPONSE = 9, + DM_MEM_HOT_ADD_REQUEST = 10, + DM_MEM_HOT_ADD_RESPONSE = 11, + DM_VERSION_03_MAX = 11, + /* + * Version 1.0. + */ + DM_INFO_MESSAGE = 12, + DM_VERSION_1_MAX = 12, + + /* + * Version 2.0 + */ + DM_MEM_HOT_REMOVE_REQUEST = 13, + DM_MEM_HOT_REMOVE_RESPONSE = 14 +}; + + +/* + * Structures defining the dynamic memory management + * protocol. + */ + +union dm_version { + struct { + uint16_t minor_version; + uint16_t major_version; + }; + uint32_t version; +} QEMU_PACKED; + + +union dm_caps { + struct { + uint64_t balloon:1; + uint64_t hot_add:1; + /* + * To support guests that may have alignment + * limitations on hot-add, the guest can specify + * its alignment requirements; a value of n + * represents an alignment of 2^n in mega bytes. + */ + uint64_t hot_add_alignment:4; + uint64_t hot_remove:1; + uint64_t reservedz:57; + } cap_bits; + uint64_t caps; +} QEMU_PACKED; + +union dm_mem_page_range { + struct { + /* + * The PFN number of the first page in the range. + * 40 bits is the architectural limit of a PFN + * number for AMD64. + */ + uint64_t start_page:40; + /* + * The number of pages in the range. + */ + uint64_t page_cnt:24; + } finfo; + uint64_t page_range; +} QEMU_PACKED; + + + +/* + * The header for all dynamic memory messages: + * + * type: Type of the message. + * size: Size of the message in bytes; including the header. + * trans_id: The guest is responsible for manufacturing this ID. + */ + +struct dm_header { + uint16_t type; + uint16_t size; + uint32_t trans_id; +} QEMU_PACKED; + +/* + * A generic message format for dynamic memory. + * Specific message formats are defined later in the file. + */ + +struct dm_message { + struct dm_header hdr; + uint8_t data[]; /* enclosed message */ +} QEMU_PACKED; + + +/* + * Specific message types supporting the dynamic memory protocol. + */ + +/* + * Version negotiation message. Sent from the guest to the host. + * The guest is free to try different versions until the host + * accepts the version. + * + * dm_version: The protocol version requested. + * is_last_attempt: If TRUE, this is the last version guest will request. + * reservedz: Reserved field, set to zero. + */ + +struct dm_version_request { + struct dm_header hdr; + union dm_version version; + uint32_t is_last_attempt:1; + uint32_t reservedz:31; +} QEMU_PACKED; + +/* + * Version response message; Host to Guest and indicates + * if the host has accepted the version sent by the guest. + * + * is_accepted: If TRUE, host has accepted the version and the guest + * should proceed to the next stage of the protocol. FALSE indicates that + * guest should re-try with a different version. + * + * reservedz: Reserved field, set to zero. + */ + +struct dm_version_response { + struct dm_header hdr; + uint64_t is_accepted:1; + uint64_t reservedz:63; +} QEMU_PACKED; + +/* + * Message reporting capabilities. This is sent from the guest to the + * host. + */ + +struct dm_capabilities { + struct dm_header hdr; + union dm_caps caps; + uint64_t min_page_cnt; + uint64_t max_page_number; +} QEMU_PACKED; + +/* + * Response to the capabilities message. This is sent from the host to the + * guest. This message notifies if the host has accepted the guest's + * capabilities. If the host has not accepted, the guest must shutdown + * the service. + * + * is_accepted: Indicates if the host has accepted guest's capabilities. + * reservedz: Must be 0. + */ + +struct dm_capabilities_resp_msg { + struct dm_header hdr; + uint64_t is_accepted:1; + uint64_t hot_remove:1; + uint64_t suppress_pressure_reports:1; + uint64_t reservedz:61; +} QEMU_PACKED; + +/* + * This message is used to report memory pressure from the guest. + * This message is not part of any transaction and there is no + * response to this message. + * + * num_avail: Available memory in pages. + * num_committed: Committed memory in pages. + * page_file_size: The accumulated size of all page files + * in the system in pages. + * zero_free: The number of zero and free pages. + * page_file_writes: The writes to the page file in pages. + * io_diff: An indicator of file cache efficiency or page file activity, + * calculated as File Cache Page Fault Count - Page Read Count. + * This value is in pages. + * + * Some of these metrics are Windows specific and fortunately + * the algorithm on the host side that computes the guest memory + * pressure only uses num_committed value. + */ + +struct dm_status { + struct dm_header hdr; + uint64_t num_avail; + uint64_t num_committed; + uint64_t page_file_size; + uint64_t zero_free; + uint32_t page_file_writes; + uint32_t io_diff; +} QEMU_PACKED; + + +/* + * Message to ask the guest to allocate memory - balloon up message. + * This message is sent from the host to the guest. The guest may not be + * able to allocate as much memory as requested. + * + * num_pages: number of pages to allocate. + */ + +struct dm_balloon { + struct dm_header hdr; + uint32_t num_pages; + uint32_t reservedz; +} QEMU_PACKED; + + +/* + * Balloon response message; this message is sent from the guest + * to the host in response to the balloon message. + * + * reservedz: Reserved; must be set to zero. + * more_pages: If FALSE, this is the last message of the transaction. + * if TRUE there will be at least one more message from the guest. + * + * range_count: The number of ranges in the range array. + * + * range_array: An array of page ranges returned to the host. + * + */ + +struct dm_balloon_response { + struct dm_header hdr; + uint32_t reservedz; + uint32_t more_pages:1; + uint32_t range_count:31; + union dm_mem_page_range range_array[]; +} QEMU_PACKED; + +/* + * Un-balloon message; this message is sent from the host + * to the guest to give guest more memory. + * + * more_pages: If FALSE, this is the last message of the transaction. + * if TRUE there will be at least one more message from the guest. + * + * reservedz: Reserved; must be set to zero. + * + * range_count: The number of ranges in the range array. + * + * range_array: An array of page ranges returned to the host. + * + */ + +struct dm_unballoon_request { + struct dm_header hdr; + uint32_t more_pages:1; + uint32_t reservedz:31; + uint32_t range_count; + union dm_mem_page_range range_array[]; +} QEMU_PACKED; + +/* + * Un-balloon response message; this message is sent from the guest + * to the host in response to an unballoon request. + * + */ + +struct dm_unballoon_response { + struct dm_header hdr; +} QEMU_PACKED; + + +/* + * Hot add request message. Message sent from the host to the guest. + * + * range: Memory range to hot add. + * region: Explicit hot add memory region for guest to use. Optional. + * + */ + +struct dm_hot_add { + struct dm_header hdr; + union dm_mem_page_range range; +} QEMU_PACKED; + +struct dm_hot_add_with_region { + struct dm_header hdr; + union dm_mem_page_range range; + union dm_mem_page_range region; +} QEMU_PACKED; + +/* + * Hot add response message. + * This message is sent by the guest to report the status of a hot add request. + * If page_count is less than the requested page count, then the host should + * assume all further hot add requests will fail, since this indicates that + * the guest has hit an upper physical memory barrier. + * + * Hot adds may also fail due to low resources; in this case, the guest must + * not complete this message until the hot add can succeed, and the host must + * not send a new hot add request until the response is sent. + * If VSC fails to hot add memory DYNMEM_NUMBER_OF_UNSUCCESSFUL_HOTADD_ATTEMPTS + * times it fails the request. + * + * + * page_count: number of pages that were successfully hot added. + * + * result: result of the operation 1: success, 0: failure. + * + */ + +struct dm_hot_add_response { + struct dm_header hdr; + uint32_t page_count; + uint32_t result; +} QEMU_PACKED; + +struct dm_hot_remove { + struct dm_header hdr; + uint32_t virtual_node; + uint32_t page_count; + uint32_t qos_flags; + uint32_t reservedZ; +} QEMU_PACKED; + +struct dm_hot_remove_response { + struct dm_header hdr; + uint32_t result; + uint32_t range_count; + uint64_t more_pages:1; + uint64_t reservedz:63; + union dm_mem_page_range range_array[]; +} QEMU_PACKED; + +#define DM_REMOVE_QOS_LARGE (1 << 0) +#define DM_REMOVE_QOS_LOCAL (1 << 1) +#define DM_REMOVE_QOS_MASK (0x3) + +/* + * Types of information sent from host to the guest. + */ + +enum dm_info_type { + INFO_TYPE_MAX_PAGE_CNT = 0, + MAX_INFO_TYPE +}; + + +/* + * Header for the information message. + */ + +struct dm_info_header { + enum dm_info_type type; + uint32_t data_size; + uint8_t data[]; +} QEMU_PACKED; + +/* + * This message is sent from the host to the guest to pass + * some relevant information (win8 addition). + * + * reserved: no used. + * info_size: size of the information blob. + * info: information blob. + */ + +struct dm_info_msg { + struct dm_header hdr; + uint32_t reserved; + uint32_t info_size; + uint8_t info[]; +}; + +#endif diff --git a/include/hw/hyperv/hv-balloon.h b/include/hw/hyperv/hv-balloon.h new file mode 100644 index 0000000000..c1efe70fc2 --- /dev/null +++ b/include/hw/hyperv/hv-balloon.h @@ -0,0 +1,18 @@ +/* + * QEMU Hyper-V Dynamic Memory Protocol driver + * + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * 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_HV_BALLOON_H +#define HW_HV_BALLOON_H + +#include "qom/object.h" + +#define TYPE_HV_BALLOON "hv-balloon" +OBJECT_DECLARE_SIMPLE_TYPE(HvBalloon, HV_BALLOON) + +#endif diff --git a/include/hw/hyperv/hyperv.h b/include/hw/hyperv/hyperv.h index 015c3524b1..d717b4e13d 100644 --- a/include/hw/hyperv/hyperv.h +++ b/include/hw/hyperv/hyperv.h @@ -139,4 +139,8 @@ typedef struct HvSynDbgMsg { } HvSynDbgMsg; typedef uint16_t (*HvSynDbgHandler)(void *context, HvSynDbgMsg *msg); void hyperv_set_syndbg_handler(HvSynDbgHandler handler, void *context); + +bool hyperv_are_vmbus_recommended_features_enabled(void); +void hyperv_set_vmbus_recommended_features_enabled(void); + #endif diff --git a/include/hw/hyperv/vmbus.h b/include/hw/hyperv/vmbus.h index 8ea660dd8e..5c505852f2 100644 --- a/include/hw/hyperv/vmbus.h +++ b/include/hw/hyperv/vmbus.h @@ -51,7 +51,7 @@ struct VMBusDeviceClass { uint16_t channel_flags; uint16_t mmio_size_mb; - /* Extentions to standard device callbacks */ + /* Extensions to standard device callbacks */ void (*vmdev_realize)(VMBusDevice *vdev, Error **errp); void (*vmdev_unrealize)(VMBusDevice *vdev); void (*vmdev_reset)(VMBusDevice *vdev); diff --git a/include/hw/i2c/allwinner-i2c.h b/include/hw/i2c/allwinner-i2c.h new file mode 100644 index 0000000000..0e325d265e --- /dev/null +++ b/include/hw/i2c/allwinner-i2c.h @@ -0,0 +1,61 @@ +/* + * Allwinner I2C Bus Serial Interface registers definition + * + * Copyright (C) 2022 Strahinja Jankovic. + * + * This file is derived from IMX I2C controller, + * by Jean-Christophe DUBOIS . + * + * 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 ALLWINNER_I2C_H +#define ALLWINNER_I2C_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_AW_I2C "allwinner.i2c" + +/** Allwinner I2C sun6i family and newer (A31, H2+, H3, etc) */ +#define TYPE_AW_I2C_SUN6I TYPE_AW_I2C "-sun6i" + +OBJECT_DECLARE_SIMPLE_TYPE(AWI2CState, AW_I2C) + +#define AW_I2C_MEM_SIZE 0x24 + +struct AWI2CState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + I2CBus *bus; + qemu_irq irq; + + uint8_t addr; + uint8_t xaddr; + uint8_t data; + uint8_t cntr; + uint8_t stat; + uint8_t ccr; + uint8_t srst; + uint8_t efr; + uint8_t lcr; + + bool irq_clear_inverted; +}; + +#endif /* ALLWINNER_I2C_H */ diff --git a/include/hw/i2c/arm_sbcon_i2c.h b/include/hw/i2c/arm_sbcon_i2c.h index f54d1e5413..da9b5e8f83 100644 --- a/include/hw/i2c/arm_sbcon_i2c.h +++ b/include/hw/i2c/arm_sbcon_i2c.h @@ -17,12 +17,10 @@ #include "hw/i2c/bitbang_i2c.h" #include "qom/object.h" -#define TYPE_VERSATILE_I2C "versatile_i2c" -#define TYPE_ARM_SBCON_I2C TYPE_VERSATILE_I2C +#define TYPE_ARM_SBCON_I2C "versatile_i2c" typedef struct ArmSbconI2CState ArmSbconI2CState; -DECLARE_INSTANCE_CHECKER(ArmSbconI2CState, ARM_SBCON_I2C, - TYPE_ARM_SBCON_I2C) +DECLARE_INSTANCE_CHECKER(ArmSbconI2CState, ARM_SBCON_I2C, TYPE_ARM_SBCON_I2C) struct ArmSbconI2CState { /*< private >*/ diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h index 4b9be09274..a064479e59 100644 --- a/include/hw/i2c/aspeed_i2c.h +++ b/include/hw/i2c/aspeed_i2c.h @@ -23,16 +23,208 @@ #include "hw/i2c/i2c.h" #include "hw/sysbus.h" +#include "hw/registerfields.h" #include "qom/object.h" #define TYPE_ASPEED_I2C "aspeed.i2c" #define TYPE_ASPEED_2400_I2C TYPE_ASPEED_I2C "-ast2400" #define TYPE_ASPEED_2500_I2C TYPE_ASPEED_I2C "-ast2500" #define TYPE_ASPEED_2600_I2C TYPE_ASPEED_I2C "-ast2600" +#define TYPE_ASPEED_1030_I2C TYPE_ASPEED_I2C "-ast1030" OBJECT_DECLARE_TYPE(AspeedI2CState, AspeedI2CClass, ASPEED_I2C) #define ASPEED_I2C_NR_BUSSES 16 #define ASPEED_I2C_MAX_POOL_SIZE 0x800 +#define ASPEED_I2C_OLD_NUM_REG 11 +#define ASPEED_I2C_NEW_NUM_REG 22 + +#define A_I2CD_M_STOP_CMD BIT(5) +#define A_I2CD_M_RX_CMD BIT(3) +#define A_I2CD_M_TX_CMD BIT(1) +#define A_I2CD_M_START_CMD BIT(0) + +#define A_I2CD_MASTER_EN BIT(0) + +/* Tx State Machine */ +#define I2CD_TX_STATE_MASK 0xf +#define I2CD_IDLE 0x0 +#define I2CD_MACTIVE 0x8 +#define I2CD_MSTART 0x9 +#define I2CD_MSTARTR 0xa +#define I2CD_MSTOP 0xb +#define I2CD_MTXD 0xc +#define I2CD_MRXACK 0xd +#define I2CD_MRXD 0xe +#define I2CD_MTXACK 0xf +#define I2CD_SWAIT 0x1 +#define I2CD_SRXD 0x4 +#define I2CD_STXACK 0x5 +#define I2CD_STXD 0x6 +#define I2CD_SRXACK 0x7 +#define I2CD_RECOVER 0x3 + +/* I2C Global Register */ +REG32(I2C_CTRL_STATUS, 0x0) /* Device Interrupt Status */ +REG32(I2C_CTRL_ASSIGN, 0x8) /* Device Interrupt Target Assignment */ +REG32(I2C_CTRL_GLOBAL, 0xC) /* Global Control Register */ + FIELD(I2C_CTRL_GLOBAL, REG_MODE, 2, 1) + FIELD(I2C_CTRL_GLOBAL, SRAM_EN, 0, 1) +REG32(I2C_CTRL_NEW_CLK_DIVIDER, 0x10) /* New mode clock divider */ + +/* I2C Old Mode Device (Bus) Register */ +REG32(I2CD_FUN_CTRL, 0x0) /* I2CD Function Control */ + FIELD(I2CD_FUN_CTRL, POOL_PAGE_SEL, 20, 3) /* AST2400 */ + SHARED_FIELD(M_SDA_LOCK_EN, 16, 1) + SHARED_FIELD(MULTI_MASTER_DIS, 15, 1) + SHARED_FIELD(M_SCL_DRIVE_EN, 14, 1) + SHARED_FIELD(MSB_STS, 9, 1) + SHARED_FIELD(SDA_DRIVE_IT_EN, 8, 1) + SHARED_FIELD(M_SDA_DRIVE_IT_EN, 7, 1) + SHARED_FIELD(M_HIGH_SPEED_EN, 6, 1) + SHARED_FIELD(DEF_ADDR_EN, 5, 1) + SHARED_FIELD(DEF_ALERT_EN, 4, 1) + SHARED_FIELD(DEF_ARP_EN, 3, 1) + SHARED_FIELD(DEF_GCALL_EN, 2, 1) + SHARED_FIELD(SLAVE_EN, 1, 1) + SHARED_FIELD(MASTER_EN, 0, 1) +REG32(I2CD_AC_TIMING1, 0x04) /* Clock and AC Timing Control #1 */ +REG32(I2CD_AC_TIMING2, 0x08) /* Clock and AC Timing Control #2 */ +REG32(I2CD_INTR_CTRL, 0x0C) /* I2CD Interrupt Control */ +REG32(I2CD_INTR_STS, 0x10) /* I2CD Interrupt Status */ + SHARED_FIELD(SLAVE_ADDR_MATCH, 31, 1) /* 0: addr1 1: addr2 */ + SHARED_FIELD(SLAVE_ADDR_RX_PENDING, 29, 1) + SHARED_FIELD(SLAVE_INACTIVE_TIMEOUT, 15, 1) + SHARED_FIELD(SDA_DL_TIMEOUT, 14, 1) + SHARED_FIELD(BUS_RECOVER_DONE, 13, 1) + SHARED_FIELD(SMBUS_ALERT, 12, 1) /* Bus [0-3] only */ + FIELD(I2CD_INTR_STS, SMBUS_ARP_ADDR, 11, 1) /* Removed */ + FIELD(I2CD_INTR_STS, SMBUS_DEV_ALERT_ADDR, 10, 1) /* Removed */ + FIELD(I2CD_INTR_STS, SMBUS_DEF_ADDR, 9, 1) /* Removed */ + FIELD(I2CD_INTR_STS, GCALL_ADDR, 8, 1) /* Removed */ + FIELD(I2CD_INTR_STS, SLAVE_ADDR_RX_MATCH, 7, 1) /* use RX_DONE */ + SHARED_FIELD(SCL_TIMEOUT, 6, 1) + SHARED_FIELD(ABNORMAL, 5, 1) + SHARED_FIELD(NORMAL_STOP, 4, 1) + SHARED_FIELD(ARBIT_LOSS, 3, 1) + SHARED_FIELD(RX_DONE, 2, 1) + SHARED_FIELD(TX_NAK, 1, 1) + SHARED_FIELD(TX_ACK, 0, 1) +REG32(I2CD_CMD, 0x14) /* I2CD Command/Status */ + SHARED_FIELD(SDA_OE, 28, 1) + SHARED_FIELD(SDA_O, 27, 1) + SHARED_FIELD(SCL_OE, 26, 1) + SHARED_FIELD(SCL_O, 25, 1) + SHARED_FIELD(TX_TIMING, 23, 2) + SHARED_FIELD(TX_STATE, 19, 4) + SHARED_FIELD(SCL_LINE_STS, 18, 1) + SHARED_FIELD(SDA_LINE_STS, 17, 1) + SHARED_FIELD(BUS_BUSY_STS, 16, 1) + SHARED_FIELD(SDA_OE_OUT_DIR, 15, 1) + SHARED_FIELD(SDA_O_OUT_DIR, 14, 1) + SHARED_FIELD(SCL_OE_OUT_DIR, 13, 1) + SHARED_FIELD(SCL_O_OUT_DIR, 12, 1) + SHARED_FIELD(BUS_RECOVER_CMD_EN, 11, 1) + SHARED_FIELD(S_ALT_EN, 10, 1) + /* Command Bits */ + SHARED_FIELD(RX_DMA_EN, 9, 1) + SHARED_FIELD(TX_DMA_EN, 8, 1) + SHARED_FIELD(RX_BUFF_EN, 7, 1) + SHARED_FIELD(TX_BUFF_EN, 6, 1) + SHARED_FIELD(M_STOP_CMD, 5, 1) + SHARED_FIELD(M_S_RX_CMD_LAST, 4, 1) + SHARED_FIELD(M_RX_CMD, 3, 1) + SHARED_FIELD(S_TX_CMD, 2, 1) + SHARED_FIELD(M_TX_CMD, 1, 1) + SHARED_FIELD(M_START_CMD, 0, 1) +REG32(I2CD_DEV_ADDR, 0x18) /* Slave Device Address */ + SHARED_FIELD(SLAVE_DEV_ADDR1, 0, 7) +REG32(I2CD_POOL_CTRL, 0x1C) /* Pool Buffer Control */ + SHARED_FIELD(RX_COUNT, 24, 6) + SHARED_FIELD(RX_SIZE, 16, 5) + SHARED_FIELD(TX_COUNT, 8, 5) + FIELD(I2CD_POOL_CTRL, OFFSET, 2, 6) /* AST2400 */ + SHARED_FIELD(BUF_ORGANIZATION, 0, 1) /* AST2600 */ +REG32(I2CD_BYTE_BUF, 0x20) /* Transmit/Receive Byte Buffer */ + SHARED_FIELD(RX_BUF, 8, 8) + SHARED_FIELD(TX_BUF, 0, 8) +REG32(I2CD_DMA_ADDR, 0x24) /* DMA Buffer Address */ +REG32(I2CD_DMA_LEN, 0x28) /* DMA Transfer Length < 4KB */ + +/* I2C New Mode Device (Bus) Register */ +REG32(I2CC_FUN_CTRL, 0x0) + FIELD(I2CC_FUN_CTRL, RB_EARLY_DONE_EN, 22, 1) + FIELD(I2CC_FUN_CTRL, DMA_DIS_AUTO_RECOVER, 21, 1) + FIELD(I2CC_FUN_CTRL, S_SAVE_ADDR, 20, 1) + FIELD(I2CC_FUN_CTRL, M_PKT_RETRY_CNT, 18, 2) + /* 17:0 shared with I2CD_FUN_CTRL[17:0] */ +REG32(I2CC_AC_TIMING, 0x04) +REG32(I2CC_MS_TXRX_BYTE_BUF, 0x08) + /* 31:16 shared with I2CD_CMD[31:16] */ + /* 15:0 shared with I2CD_BYTE_BUF[15:0] */ +REG32(I2CC_POOL_CTRL, 0x0c) + /* 31:0 shared with I2CD_POOL_CTRL[31:0] */ +REG32(I2CM_INTR_CTRL, 0x10) +REG32(I2CM_INTR_STS, 0x14) + FIELD(I2CM_INTR_STS, PKT_STATE, 28, 4) + FIELD(I2CM_INTR_STS, PKT_CMD_TIMEOUT, 18, 1) + FIELD(I2CM_INTR_STS, PKT_CMD_FAIL, 17, 1) + FIELD(I2CM_INTR_STS, PKT_CMD_DONE, 16, 1) + FIELD(I2CM_INTR_STS, BUS_RECOVER_FAIL, 15, 1) + /* 14:0 shared with I2CD_INTR_STS[14:0] */ +REG32(I2CM_CMD, 0x18) + FIELD(I2CM_CMD, W1_CTRL, 31, 1) + FIELD(I2CM_CMD, PKT_DEV_ADDR, 24, 7) + FIELD(I2CM_CMD, HS_MASTER_MODE_LSB, 17, 3) + FIELD(I2CM_CMD, PKT_OP_EN, 16, 1) + /* 15:0 shared with I2CD_CMD[15:0] */ +REG32(I2CM_DMA_LEN, 0x1c) + FIELD(I2CM_DMA_LEN, RX_BUF_LEN_W1T, 31, 1) + FIELD(I2CM_DMA_LEN, RX_BUF_LEN, 16, 11) + FIELD(I2CM_DMA_LEN, TX_BUF_LEN_W1T, 15, 1) + FIELD(I2CM_DMA_LEN, TX_BUF_LEN, 0, 11) +REG32(I2CS_INTR_CTRL, 0x20) + FIELD(I2CS_INTR_CTRL, PKT_CMD_FAIL, 17, 1) + FIELD(I2CS_INTR_CTRL, PKT_CMD_DONE, 16, 1) +REG32(I2CS_INTR_STS, 0x24) + /* 31:29 shared with I2CD_INTR_STS[31:29] */ + FIELD(I2CS_INTR_STS, SLAVE_PARKING_STS, 24, 2) + FIELD(I2CS_INTR_STS, SLAVE_ADDR3_NAK, 22, 1) + FIELD(I2CS_INTR_STS, SLAVE_ADDR2_NAK, 21, 1) + FIELD(I2CS_INTR_STS, SLAVE_ADDR1_NAK, 20, 1) + FIELD(I2CS_INTR_STS, SLAVE_ADDR_INDICATOR, 18, 2) + FIELD(I2CS_INTR_STS, PKT_CMD_FAIL, 17, 1) + FIELD(I2CS_INTR_STS, PKT_CMD_DONE, 16, 1) + /* 14:0 shared with I2CD_INTR_STS[14:0] */ + FIELD(I2CS_INTR_STS, SLAVE_ADDR_RX_MATCH, 7, 1) +REG32(I2CS_CMD, 0x28) + FIELD(I2CS_CMD, W1_CTRL, 31, 1) + FIELD(I2CS_CMD, PKT_MODE_ACTIVE_ADDR, 17, 2) + FIELD(I2CS_CMD, PKT_MODE_EN, 16, 1) + FIELD(I2CS_CMD, AUTO_NAK_INACTIVE_ADDR, 15, 1) + FIELD(I2CS_CMD, AUTO_NAK_ACTIVE_ADDR, 14, 1) + /* 13:0 shared with I2CD_CMD[13:0] */ +REG32(I2CS_DMA_LEN, 0x2c) + FIELD(I2CS_DMA_LEN, RX_BUF_LEN_W1T, 31, 1) + FIELD(I2CS_DMA_LEN, RX_BUF_LEN, 16, 11) + FIELD(I2CS_DMA_LEN, TX_BUF_LEN_W1T, 15, 1) + FIELD(I2CS_DMA_LEN, TX_BUF_LEN, 0, 11) +REG32(I2CM_DMA_TX_ADDR, 0x30) + FIELD(I2CM_DMA_TX_ADDR, ADDR, 0, 31) +REG32(I2CM_DMA_RX_ADDR, 0x34) + FIELD(I2CM_DMA_RX_ADDR, ADDR, 0, 31) +REG32(I2CS_DMA_TX_ADDR, 0x38) + FIELD(I2CS_DMA_TX_ADDR, ADDR, 0, 31) +REG32(I2CS_DMA_RX_ADDR, 0x3c) + FIELD(I2CS_DMA_RX_ADDR, ADDR, 0, 31) +REG32(I2CS_DEV_ADDR, 0x40) +REG32(I2CM_DMA_LEN_STS, 0x48) + FIELD(I2CM_DMA_LEN_STS, RX_LEN, 16, 13) + FIELD(I2CM_DMA_LEN_STS, TX_LEN, 0, 13) +REG32(I2CS_DMA_LEN_STS, 0x4c) + FIELD(I2CS_DMA_LEN_STS, RX_LEN, 16, 13) + FIELD(I2CS_DMA_LEN_STS, TX_LEN, 0, 13) +REG32(I2CC_DMA_ADDR, 0x50) +REG32(I2CC_DMA_LEN, 0x54) struct AspeedI2CState; @@ -43,21 +235,16 @@ struct AspeedI2CBus { struct AspeedI2CState *controller; + /* slave mode */ + I2CSlave *slave; + MemoryRegion mr; I2CBus *bus; uint8_t id; qemu_irq irq; - uint32_t ctrl; - uint32_t timing[2]; - uint32_t intr_ctrl; - uint32_t intr_status; - uint32_t cmd; - uint32_t buf; - uint32_t pool_ctrl; - uint32_t dma_addr; - uint32_t dma_len; + uint32_t regs[ASPEED_I2C_NEW_NUM_REG]; }; struct AspeedI2CState { @@ -68,6 +255,7 @@ struct AspeedI2CState { uint32_t intr_status; uint32_t ctrl_global; + uint32_t new_clk_divider; MemoryRegion pool_iomem; uint8_t pool[ASPEED_I2C_MAX_POOL_SIZE]; @@ -76,6 +264,11 @@ struct AspeedI2CState { AddressSpace dram_as; }; +#define TYPE_ASPEED_I2C_BUS_SLAVE "aspeed.i2c.slave" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedI2CBusSlave, ASPEED_I2C_BUS_SLAVE) +struct AspeedI2CBusSlave { + I2CSlave i2c; +}; struct AspeedI2CClass { SysBusDeviceClass parent_class; @@ -93,6 +286,104 @@ struct AspeedI2CClass { }; +static inline bool aspeed_i2c_is_new_mode(AspeedI2CState *s) +{ + return FIELD_EX32(s->ctrl_global, I2C_CTRL_GLOBAL, REG_MODE); +} + +static inline bool aspeed_i2c_bus_pkt_mode_en(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return ARRAY_FIELD_EX32(bus->regs, I2CM_CMD, PKT_OP_EN); + } + return false; +} + +static inline uint32_t aspeed_i2c_bus_ctrl_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CC_FUN_CTRL; + } + return R_I2CD_FUN_CTRL; +} + +static inline uint32_t aspeed_i2c_bus_cmd_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CM_CMD; + } + return R_I2CD_CMD; +} + +static inline uint32_t aspeed_i2c_bus_dev_addr_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CS_DEV_ADDR; + } + return R_I2CD_DEV_ADDR; +} + +static inline uint32_t aspeed_i2c_bus_intr_ctrl_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CM_INTR_CTRL; + } + return R_I2CD_INTR_CTRL; +} + +static inline uint32_t aspeed_i2c_bus_intr_sts_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CM_INTR_STS; + } + return R_I2CD_INTR_STS; +} + +static inline uint32_t aspeed_i2c_bus_pool_ctrl_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CC_POOL_CTRL; + } + return R_I2CD_POOL_CTRL; +} + +static inline uint32_t aspeed_i2c_bus_byte_buf_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CC_MS_TXRX_BYTE_BUF; + } + return R_I2CD_BYTE_BUF; +} + +static inline uint32_t aspeed_i2c_bus_dma_len_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CC_DMA_LEN; + } + return R_I2CD_DMA_LEN; +} + +static inline uint32_t aspeed_i2c_bus_dma_addr_offset(AspeedI2CBus *bus) +{ + if (aspeed_i2c_is_new_mode(bus->controller)) { + return R_I2CC_DMA_ADDR; + } + return R_I2CD_DMA_ADDR; +} + +static inline bool aspeed_i2c_bus_is_master(AspeedI2CBus *bus) +{ + return SHARED_ARRAY_FIELD_EX32(bus->regs, aspeed_i2c_bus_ctrl_offset(bus), + MASTER_EN); +} + +static inline bool aspeed_i2c_bus_is_enabled(AspeedI2CBus *bus) +{ + uint32_t ctrl_reg = aspeed_i2c_bus_ctrl_offset(bus); + return SHARED_ARRAY_FIELD_EX32(bus->regs, ctrl_reg, MASTER_EN) || + SHARED_ARRAY_FIELD_EX32(bus->regs, ctrl_reg, SLAVE_EN); +} + I2CBus *aspeed_i2c_get_bus(AspeedI2CState *s, int busnr); #endif /* ASPEED_I2C_H */ diff --git a/include/hw/i2c/bcm2835_i2c.h b/include/hw/i2c/bcm2835_i2c.h new file mode 100644 index 0000000000..0a56df4720 --- /dev/null +++ b/include/hw/i2c/bcm2835_i2c.h @@ -0,0 +1,80 @@ +/* + * Broadcom Serial Controller (BSC) + * + * Copyright (c) 2024 Rayhan Faizel + * + * 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 "hw/sysbus.h" +#include "hw/i2c/i2c.h" +#include "qom/object.h" + +#define TYPE_BCM2835_I2C "bcm2835-i2c" +OBJECT_DECLARE_SIMPLE_TYPE(BCM2835I2CState, BCM2835_I2C) + +#define BCM2835_I2C_C 0x0 /* Control */ +#define BCM2835_I2C_S 0x4 /* Status */ +#define BCM2835_I2C_DLEN 0x8 /* Data Length */ +#define BCM2835_I2C_A 0xc /* Slave Address */ +#define BCM2835_I2C_FIFO 0x10 /* FIFO */ +#define BCM2835_I2C_DIV 0x14 /* Clock Divider */ +#define BCM2835_I2C_DEL 0x18 /* Data Delay */ +#define BCM2835_I2C_CLKT 0x20 /* Clock Stretch Timeout */ + +#define BCM2835_I2C_C_I2CEN BIT(15) /* I2C enable */ +#define BCM2835_I2C_C_INTR BIT(10) /* Interrupt on RXR */ +#define BCM2835_I2C_C_INTT BIT(9) /* Interrupt on TXW */ +#define BCM2835_I2C_C_INTD BIT(8) /* Interrupt on DONE */ +#define BCM2835_I2C_C_ST BIT(7) /* Start transfer */ +#define BCM2835_I2C_C_CLEAR (BIT(5) | BIT(4)) /* Clear FIFO */ +#define BCM2835_I2C_C_READ BIT(0) /* I2C read mode */ + +#define BCM2835_I2C_S_CLKT BIT(9) /* Clock stretch timeout */ +#define BCM2835_I2C_S_ERR BIT(8) /* Slave error */ +#define BCM2835_I2C_S_RXF BIT(7) /* RX FIFO full */ +#define BCM2835_I2C_S_TXE BIT(6) /* TX FIFO empty */ +#define BCM2835_I2C_S_RXD BIT(5) /* RX bytes available */ +#define BCM2835_I2C_S_TXD BIT(4) /* TX space available */ +#define BCM2835_I2C_S_RXR BIT(3) /* RX FIFO needs reading */ +#define BCM2835_I2C_S_TXW BIT(2) /* TX FIFO needs writing */ +#define BCM2835_I2C_S_DONE BIT(1) /* I2C Transfer complete */ +#define BCM2835_I2C_S_TA BIT(0) /* I2C Transfer active */ + +struct BCM2835I2CState { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion iomem; + I2CBus *bus; + qemu_irq irq; + + uint32_t c; + uint32_t s; + uint32_t dlen; + uint32_t a; + uint32_t div; + uint32_t del; + uint32_t clkt; + + uint32_t last_dlen; +}; diff --git a/include/hw/i2c/bitbang_i2c.h b/include/hw/i2c/bitbang_i2c.h index 92334e9016..a079e6d70f 100644 --- a/include/hw/i2c/bitbang_i2c.h +++ b/include/hw/i2c/bitbang_i2c.h @@ -3,6 +3,8 @@ #include "hw/i2c/i2c.h" +#define TYPE_GPIO_I2C "gpio_i2c" + typedef struct bitbang_i2c_interface bitbang_i2c_interface; #define BITBANG_I2C_SDA 0 diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index 5ca3b708c0..2a3abacd1b 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -12,6 +12,7 @@ enum i2c_event { I2C_START_RECV, I2C_START_SEND, + I2C_START_SEND_ASYNC, I2C_FINISH, I2C_NACK /* Masker NACKed a receive byte. */ }; @@ -28,6 +29,9 @@ struct I2CSlaveClass { /* Master to slave. Returns non-zero for a NAK, 0 for success. */ int (*send)(I2CSlave *s, uint8_t data); + /* Master to slave (asynchronous). Receiving slave must call i2c_ack(). */ + void (*send_async)(I2CSlave *s, uint8_t data); + /* * Slave to master. This cannot fail, the device should always * return something here. @@ -69,13 +73,25 @@ struct I2CNode { QLIST_ENTRY(I2CNode) next; }; +typedef struct I2CPendingMaster I2CPendingMaster; + +struct I2CPendingMaster { + QEMUBH *bh; + QSIMPLEQ_ENTRY(I2CPendingMaster) entry; +}; + typedef QLIST_HEAD(I2CNodeList, I2CNode) I2CNodeList; +typedef QSIMPLEQ_HEAD(I2CPendingMasters, I2CPendingMaster) I2CPendingMasters; struct I2CBus { BusState qbus; I2CNodeList current_devs; + I2CPendingMasters pending_masters; uint8_t saved_address; bool broadcast; + + /* Set from slave currently mastering the bus. */ + QEMUBH *bh; }; I2CBus *i2c_init_bus(DeviceState *parent, const char *name); @@ -115,9 +131,25 @@ int i2c_start_recv(I2CBus *bus, uint8_t address); */ int i2c_start_send(I2CBus *bus, uint8_t address); +/** + * i2c_start_send_async: start an asynchronous 'send' transfer on an I2C bus. + * + * @bus: #I2CBus to be used + * @address: address of the slave + * + * Return: 0 on success, -1 on error + */ +int i2c_start_send_async(I2CBus *bus, uint8_t address); + +void i2c_schedule_pending_master(I2CBus *bus); + void i2c_end_transfer(I2CBus *bus); void i2c_nack(I2CBus *bus); +void i2c_ack(I2CBus *bus); +void i2c_bus_master(I2CBus *bus, QEMUBH *bh); +void i2c_bus_release(I2CBus *bus); int i2c_send(I2CBus *bus, uint8_t data); +int i2c_send_async(I2CBus *bus, uint8_t data); uint8_t i2c_recv(I2CBus *bus); bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, I2CNodeList *current_devs); diff --git a/include/hw/i2c/npcm7xx_smbus.h b/include/hw/i2c/npcm7xx_smbus.h index 7d59ee917e..dc45963c0e 100644 --- a/include/hw/i2c/npcm7xx_smbus.h +++ b/include/hw/i2c/npcm7xx_smbus.h @@ -58,7 +58,7 @@ typedef enum NPCM7xxSMBusStatus { * @sclht: The SCL high time register. * @fif_ctl: The FIFO control register. * @fif_cts: The FIFO control status register. - * @fair_per: The fair preriod register. + * @fair_per: The fair period register. * @txf_ctl: The transmit FIFO control register. * @t_out: The SMBus timeout register. * @txf_sts: The transmit FIFO status register. @@ -68,7 +68,7 @@ typedef enum NPCM7xxSMBusStatus { * @rx_cur: The current position of rx_fifo. * @status: The current status of the SMBus. */ -typedef struct NPCM7xxSMBusState { +struct NPCM7xxSMBusState { SysBusDevice parent; MemoryRegion iomem; @@ -104,10 +104,9 @@ typedef struct NPCM7xxSMBusState { uint8_t rx_cur; NPCM7xxSMBusStatus status; -} NPCM7xxSMBusState; +}; #define TYPE_NPCM7XX_SMBUS "npcm7xx-smbus" -#define NPCM7XX_SMBUS(obj) OBJECT_CHECK(NPCM7xxSMBusState, (obj), \ - TYPE_NPCM7XX_SMBUS) +OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxSMBusState, NPCM7XX_SMBUS) #endif /* NPCM7XX_SMBUS_H */ diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h index 0f4d6b3fad..f195c11384 100644 --- a/include/hw/i2c/pmbus_device.h +++ b/include/hw/i2c/pmbus_device.h @@ -155,6 +155,7 @@ enum pmbus_registers { PMBUS_MFR_MAX_TEMP_1 = 0xC0, /* R/W word */ PMBUS_MFR_MAX_TEMP_2 = 0xC1, /* R/W word */ PMBUS_MFR_MAX_TEMP_3 = 0xC2, /* R/W word */ + PMBUS_IDLE_STATE = 0xFF, }; /* STATUS_WORD */ @@ -242,6 +243,7 @@ OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass, #define PB_HAS_VIN_RATING BIT_ULL(13) #define PB_HAS_VOUT_RATING BIT_ULL(14) #define PB_HAS_VOUT_MODE BIT_ULL(15) +#define PB_HAS_VCAP BIT_ULL(16) #define PB_HAS_IOUT BIT_ULL(21) #define PB_HAS_IIN BIT_ULL(22) #define PB_HAS_IOUT_RATING BIT_ULL(23) @@ -257,6 +259,7 @@ OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass, #define PB_HAS_TEMP2 BIT_ULL(41) #define PB_HAS_TEMP3 BIT_ULL(42) #define PB_HAS_TEMP_RATING BIT_ULL(43) +#define PB_HAS_FAN BIT_ULL(44) #define PB_HAS_MFR_INFO BIT_ULL(50) #define PB_HAS_STATUS_MFR_SPECIFIC BIT_ULL(51) @@ -443,6 +446,14 @@ typedef struct PMBusCoefficients { int32_t R; /* exponent */ } PMBusCoefficients; +/** + * VOUT_Mode bit fields + */ +typedef struct PMBusVoutMode { + uint8_t mode:3; + int8_t exp:5; +} PMBusVoutMode; + /** * Convert sensor values to direct mode format * @@ -500,6 +511,13 @@ void pmbus_send64(PMBusDevice *state, uint64_t data); */ void pmbus_send_string(PMBusDevice *state, const char *data); +/** + * @brief Receive data sent with Block Write. + * @param dest - memory with enough capacity to receive the write + * @param len - the capacity of dest + */ +uint8_t pmbus_receive_block(PMBusDevice *pmdev, uint8_t *dest, size_t len); + /** * @brief Receive data over PMBus * These methods help track how much data is being received over PMBus @@ -527,6 +545,12 @@ int pmbus_page_config(PMBusDevice *pmdev, uint8_t page_index, uint64_t flags); */ void pmbus_check_limits(PMBusDevice *pmdev); +/** + * Enter an idle state where only the PMBUS_ERR_BYTE will be returned + * indefinitely until a new command is issued. + */ +void pmbus_idle(PMBusDevice *pmdev); + extern const VMStateDescription vmstate_pmbus_device; #define VMSTATE_PMBUS_DEVICE(_field, _state) { \ diff --git a/include/hw/i2c/pnv_i2c_regs.h b/include/hw/i2c/pnv_i2c_regs.h new file mode 100644 index 0000000000..85e96ff480 --- /dev/null +++ b/include/hw/i2c/pnv_i2c_regs.h @@ -0,0 +1,143 @@ +/* + * PowerNV I2C Controller Register Definitions + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PNV_I2C_REGS_H +#define PNV_I2C_REGS_H + +/* I2C FIFO register */ +#define I2C_FIFO_REG 0x4 +#define I2C_FIFO PPC_BITMASK(0, 7) + +/* I2C command register */ +#define I2C_CMD_REG 0x5 +#define I2C_CMD_WITH_START PPC_BIT(0) +#define I2C_CMD_WITH_ADDR PPC_BIT(1) +#define I2C_CMD_READ_CONT PPC_BIT(2) +#define I2C_CMD_WITH_STOP PPC_BIT(3) +#define I2C_CMD_INTR_STEERING PPC_BITMASK(6, 7) /* P9 */ +#define I2C_CMD_INTR_STEER_HOST 1 +#define I2C_CMD_INTR_STEER_OCC 2 +#define I2C_CMD_DEV_ADDR PPC_BITMASK(8, 14) +#define I2C_CMD_READ_NOT_WRITE PPC_BIT(15) +#define I2C_CMD_LEN_BYTES PPC_BITMASK(16, 31) +#define I2C_MAX_TFR_LEN 0xfff0ull + +/* I2C mode register */ +#define I2C_MODE_REG 0x6 +#define I2C_MODE_BIT_RATE_DIV PPC_BITMASK(0, 15) +#define I2C_MODE_PORT_NUM PPC_BITMASK(16, 21) +#define I2C_MODE_ENHANCED PPC_BIT(28) +#define I2C_MODE_DIAGNOSTIC PPC_BIT(29) +#define I2C_MODE_PACING_ALLOW PPC_BIT(30) +#define I2C_MODE_WRAP PPC_BIT(31) + +/* I2C watermark register */ +#define I2C_WATERMARK_REG 0x7 +#define I2C_WATERMARK_HIGH PPC_BITMASK(16, 19) +#define I2C_WATERMARK_LOW PPC_BITMASK(24, 27) + +/* + * I2C interrupt mask and condition registers + * + * NB: The function of 0x9 and 0xa changes depending on whether you're reading + * or writing to them. When read they return the interrupt condition bits + * and on writes they update the interrupt mask register. + * + * The bit definitions are the same for all the interrupt registers. + */ +#define I2C_INTR_MASK_REG 0x8 + +#define I2C_INTR_RAW_COND_REG 0x9 /* read */ +#define I2C_INTR_MASK_OR_REG 0x9 /* write*/ + +#define I2C_INTR_COND_REG 0xa /* read */ +#define I2C_INTR_MASK_AND_REG 0xa /* write */ + +#define I2C_INTR_ALL PPC_BITMASK(16, 31) +#define I2C_INTR_INVALID_CMD PPC_BIT(16) +#define I2C_INTR_LBUS_PARITY_ERR PPC_BIT(17) +#define I2C_INTR_BKEND_OVERRUN_ERR PPC_BIT(18) +#define I2C_INTR_BKEND_ACCESS_ERR PPC_BIT(19) +#define I2C_INTR_ARBT_LOST_ERR PPC_BIT(20) +#define I2C_INTR_NACK_RCVD_ERR PPC_BIT(21) +#define I2C_INTR_DATA_REQ PPC_BIT(22) +#define I2C_INTR_CMD_COMP PPC_BIT(23) +#define I2C_INTR_STOP_ERR PPC_BIT(24) +#define I2C_INTR_I2C_BUSY PPC_BIT(25) +#define I2C_INTR_NOT_I2C_BUSY PPC_BIT(26) +#define I2C_INTR_SCL_EQ_1 PPC_BIT(28) +#define I2C_INTR_SCL_EQ_0 PPC_BIT(29) +#define I2C_INTR_SDA_EQ_1 PPC_BIT(30) +#define I2C_INTR_SDA_EQ_0 PPC_BIT(31) + +/* I2C status register */ +#define I2C_RESET_I2C_REG 0xb /* write */ +#define I2C_RESET_ERRORS 0xc +#define I2C_STAT_REG 0xb /* read */ +#define I2C_STAT_INVALID_CMD PPC_BIT(0) +#define I2C_STAT_LBUS_PARITY_ERR PPC_BIT(1) +#define I2C_STAT_BKEND_OVERRUN_ERR PPC_BIT(2) +#define I2C_STAT_BKEND_ACCESS_ERR PPC_BIT(3) +#define I2C_STAT_ARBT_LOST_ERR PPC_BIT(4) +#define I2C_STAT_NACK_RCVD_ERR PPC_BIT(5) +#define I2C_STAT_DATA_REQ PPC_BIT(6) +#define I2C_STAT_CMD_COMP PPC_BIT(7) +#define I2C_STAT_STOP_ERR PPC_BIT(8) +#define I2C_STAT_UPPER_THRS PPC_BITMASK(9, 15) +#define I2C_STAT_ANY_I2C_INTR PPC_BIT(16) +#define I2C_STAT_PORT_HISTORY_BUSY PPC_BIT(19) +#define I2C_STAT_SCL_INPUT_LEVEL PPC_BIT(20) +#define I2C_STAT_SDA_INPUT_LEVEL PPC_BIT(21) +#define I2C_STAT_PORT_BUSY PPC_BIT(22) +#define I2C_STAT_INTERFACE_BUSY PPC_BIT(23) +#define I2C_STAT_FIFO_ENTRY_COUNT PPC_BITMASK(24, 31) + +#define I2C_STAT_ANY_ERR (I2C_STAT_INVALID_CMD | I2C_STAT_LBUS_PARITY_ERR | \ + I2C_STAT_BKEND_OVERRUN_ERR | \ + I2C_STAT_BKEND_ACCESS_ERR | I2C_STAT_ARBT_LOST_ERR | \ + I2C_STAT_NACK_RCVD_ERR | I2C_STAT_STOP_ERR) + + +#define I2C_INTR_ACTIVE \ + ((I2C_STAT_ANY_ERR >> 16) | I2C_INTR_CMD_COMP | I2C_INTR_DATA_REQ) + +/* Pseudo-status used for timeouts */ +#define I2C_STAT_PSEUDO_TIMEOUT PPC_BIT(63) + +/* I2C extended status register */ +#define I2C_EXTD_STAT_REG 0xc +#define I2C_EXTD_STAT_FIFO_SIZE PPC_BITMASK(0, 7) +#define I2C_EXTD_STAT_MSM_CURSTATE PPC_BITMASK(11, 15) +#define I2C_EXTD_STAT_SCL_IN_SYNC PPC_BIT(16) +#define I2C_EXTD_STAT_SDA_IN_SYNC PPC_BIT(17) +#define I2C_EXTD_STAT_S_SCL PPC_BIT(18) +#define I2C_EXTD_STAT_S_SDA PPC_BIT(19) +#define I2C_EXTD_STAT_M_SCL PPC_BIT(20) +#define I2C_EXTD_STAT_M_SDA PPC_BIT(21) +#define I2C_EXTD_STAT_HIGH_WATER PPC_BIT(22) +#define I2C_EXTD_STAT_LOW_WATER PPC_BIT(23) +#define I2C_EXTD_STAT_I2C_BUSY PPC_BIT(24) +#define I2C_EXTD_STAT_SELF_BUSY PPC_BIT(25) +#define I2C_EXTD_STAT_I2C_VERSION PPC_BITMASK(27, 31) + +/* I2C residual front end/back end length */ +#define I2C_RESIDUAL_LEN_REG 0xd +#define I2C_RESIDUAL_FRONT_END PPC_BITMASK(0, 15) +#define I2C_RESIDUAL_BACK_END PPC_BITMASK(16, 31) + +/* Port busy register */ +#define I2C_PORT_BUSY_REG 0xe +#define I2C_SET_S_SCL_REG 0xd +#define I2C_RESET_S_SCL_REG 0xf +#define I2C_SET_S_SDA_REG 0x10 +#define I2C_RESET_S_SDA_REG 0x11 + +#define PNV_I2C_FIFO_SIZE 8 +#define PNV_I2C_MAX_BUSSES 64 + +#endif /* PNV_I2C_REGS_H */ diff --git a/include/hw/i386/apic.h b/include/hw/i386/apic.h index da1d2fe155..eb606d6076 100644 --- a/include/hw/i386/apic.h +++ b/include/hw/i386/apic.h @@ -3,16 +3,14 @@ /* apic.c */ -void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, - uint8_t vector_num, uint8_t trigger_mode); +void apic_set_max_apic_id(uint32_t max_apic_id); int apic_accept_pic_intr(DeviceState *s); void apic_deliver_pic_intr(DeviceState *s, int level); void apic_deliver_nmi(DeviceState *d); int apic_get_interrupt(DeviceState *s); -void apic_reset_irq_delivered(void); -int apic_get_irq_delivered(void); -void cpu_set_apic_base(DeviceState *s, uint64_t val); +int cpu_set_apic_base(DeviceState *s, uint64_t val); uint64_t cpu_get_apic_base(DeviceState *s); +bool cpu_is_apic_enabled(DeviceState *s); void cpu_set_apic_tpr(DeviceState *s, uint8_t val); uint8_t cpu_get_apic_tpr(DeviceState *s); void apic_init_reset(DeviceState *s); @@ -20,6 +18,9 @@ void apic_sipi(DeviceState *s); void apic_poll_irq(DeviceState *d); void apic_designate_bsp(DeviceState *d, bool bsp); int apic_get_highest_priority_irr(DeviceState *dev); +int apic_msr_read(int index, uint64_t *val); +int apic_msr_write(int index, uint64_t val); +bool is_x2apic_mode(DeviceState *d); /* pc.c */ DeviceState *cpu_get_current_apic(void); diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h index c175e7e718..d6e85833da 100644 --- a/include/hw/i386/apic_internal.h +++ b/include/hw/i386/apic_internal.h @@ -46,8 +46,10 @@ #define APIC_DM_EXTINT 7 /* APIC destination mode */ -#define APIC_DESTMODE_FLAT 0xf -#define APIC_DESTMODE_CLUSTER 1 +#define APIC_DESTMODE_PHYSICAL 0 +#define APIC_DESTMODE_LOGICAL 1 +#define APIC_DESTMODE_LOGICAL_FLAT 0xf +#define APIC_DESTMODE_LOGICAL_CLUSTER 0 #define APIC_TRIGGER_EDGE 0 #define APIC_TRIGGER_LEVEL 1 @@ -135,7 +137,7 @@ struct APICCommonClass { DeviceRealize realize; DeviceUnrealize unrealize; - void (*set_base)(APICCommonState *s, uint64_t val); + int (*set_base)(APICCommonState *s, uint64_t val); void (*set_tpr)(APICCommonState *s, uint8_t val); uint8_t (*get_tpr)(APICCommonState *s); void (*enable_tpr_reporting)(APICCommonState *s, bool enable); @@ -187,6 +189,7 @@ struct APICCommonState { DeviceState *vapic; hwaddr vapic_paddr; /* note: persistence via kvmvapic */ bool legacy_instance_id; + uint32_t extended_log_dest; }; typedef struct VAPICState { @@ -199,7 +202,6 @@ typedef struct VAPICState { extern bool apic_report_tpr_access; -void apic_report_irq_delivered(int delivered); bool apic_next_timer(APICCommonState *s, int64_t current_time); void apic_enable_tpr_access_reporting(DeviceState *d, bool enable); void apic_enable_vapic(DeviceState *d, hwaddr paddr); @@ -226,6 +228,6 @@ static inline int apic_get_bit(uint32_t *tab, int index) return !!(tab[i] & mask); } -APICCommonClass *apic_get_class(void); +APICCommonClass *apic_get_class(Error **errp); #endif /* QEMU_APIC_INTERNAL_H */ diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 67653b0f9b..7fa0a695c8 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -58,7 +58,6 @@ typedef struct VTDContextEntry VTDContextEntry; typedef struct VTDContextCacheEntry VTDContextCacheEntry; typedef struct VTDAddressSpace VTDAddressSpace; typedef struct VTDIOTLBEntry VTDIOTLBEntry; -typedef struct VTDBus VTDBus; typedef union VTD_IR_TableEntry VTD_IR_TableEntry; typedef union VTD_IR_MSIAddress VTD_IR_MSIAddress; typedef struct VTDPASIDDirEntry VTDPASIDDirEntry; @@ -98,28 +97,61 @@ struct VTDPASIDEntry { struct VTDAddressSpace { PCIBus *bus; uint8_t devfn; + uint32_t pasid; AddressSpace as; IOMMUMemoryRegion iommu; MemoryRegion root; /* The root container of the device */ MemoryRegion nodmar; /* The alias of shared nodmar MR */ MemoryRegion iommu_ir; /* Interrupt region: 0xfeeXXXXX */ + MemoryRegion iommu_ir_fault; /* Interrupt region for catching fault */ IntelIOMMUState *iommu_state; VTDContextCacheEntry context_cache_entry; QLIST_ENTRY(VTDAddressSpace) next; /* Superset of notifier flags that this address space has */ IOMMUNotifierFlag notifier_flags; - IOVATree *iova_tree; /* Traces mapped IOVA ranges */ -}; - -struct VTDBus { - PCIBus* bus; /* A reference to the bus to provide translation for */ - /* A table of VTDAddressSpace objects indexed by devfn */ - VTDAddressSpace *dev_as[]; + /* + * @iova_tree traces mapped IOVA ranges. + * + * The tree is not needed if no MAP notifier is registered with current + * VTD address space, because all guest invalidate commands can be + * directly passed to the IOMMU UNMAP notifiers without any further + * reshuffling. + * + * The tree OTOH is required for MAP typed iommu notifiers for a few + * reasons. + * + * Firstly, there's no way to identify whether an PSI (Page Selective + * Invalidations) or DSI (Domain Selective Invalidations) event is an + * MAP or UNMAP event within the message itself. Without having prior + * knowledge of existing state vIOMMU doesn't know whether it should + * notify MAP or UNMAP for a PSI message it received when caching mode + * is enabled (for MAP notifiers). + * + * Secondly, PSI messages received from guest driver can be enlarged in + * range, covers but not limited to what the guest driver wanted to + * invalidate. When the range to invalidates gets bigger than the + * limit of a PSI message, it can even become a DSI which will + * invalidate the whole domain. If the vIOMMU directly notifies the + * registered device with the unmodified range, it may confuse the + * registered drivers (e.g. vfio-pci) on either: + * + * (1) Trying to map the same region more than once (for + * VFIO_IOMMU_MAP_DMA, -EEXIST will trigger), or, + * + * (2) Trying to UNMAP a range that is still partially mapped. + * + * That accuracy is not required for UNMAP-only notifiers, but it is a + * must-to-have for notifiers registered with MAP events, because the + * vIOMMU needs to make sure the shadow page table is always in sync + * with the guest IOMMU pgtables for a device. + */ + IOVATree *iova_tree; }; struct VTDIOTLBEntry { uint64_t gfn; uint16_t domain_id; + uint32_t pasid; uint64_t slpte; uint64_t mask; uint8_t access_flags; @@ -146,37 +178,39 @@ enum { union VTD_IR_TableEntry { struct { #if HOST_BIG_ENDIAN - uint32_t __reserved_1:8; /* Reserved 1 */ - uint32_t vector:8; /* Interrupt Vector */ - uint32_t irte_mode:1; /* IRTE Mode */ - uint32_t __reserved_0:3; /* Reserved 0 */ - uint32_t __avail:4; /* Available spaces for software */ - uint32_t delivery_mode:3; /* Delivery Mode */ - uint32_t trigger_mode:1; /* Trigger Mode */ - uint32_t redir_hint:1; /* Redirection Hint */ - uint32_t dest_mode:1; /* Destination Mode */ - uint32_t fault_disable:1; /* Fault Processing Disable */ - uint32_t present:1; /* Whether entry present/available */ + uint64_t dest_id:32; /* Destination ID */ + uint64_t __reserved_1:8; /* Reserved 1 */ + uint64_t vector:8; /* Interrupt Vector */ + uint64_t irte_mode:1; /* IRTE Mode */ + uint64_t __reserved_0:3; /* Reserved 0 */ + uint64_t __avail:4; /* Available spaces for software */ + uint64_t delivery_mode:3; /* Delivery Mode */ + uint64_t trigger_mode:1; /* Trigger Mode */ + uint64_t redir_hint:1; /* Redirection Hint */ + uint64_t dest_mode:1; /* Destination Mode */ + uint64_t fault_disable:1; /* Fault Processing Disable */ + uint64_t present:1; /* Whether entry present/available */ #else - uint32_t present:1; /* Whether entry present/available */ - uint32_t fault_disable:1; /* Fault Processing Disable */ - uint32_t dest_mode:1; /* Destination Mode */ - uint32_t redir_hint:1; /* Redirection Hint */ - uint32_t trigger_mode:1; /* Trigger Mode */ - uint32_t delivery_mode:3; /* Delivery Mode */ - uint32_t __avail:4; /* Available spaces for software */ - uint32_t __reserved_0:3; /* Reserved 0 */ - uint32_t irte_mode:1; /* IRTE Mode */ - uint32_t vector:8; /* Interrupt Vector */ - uint32_t __reserved_1:8; /* Reserved 1 */ + uint64_t present:1; /* Whether entry present/available */ + uint64_t fault_disable:1; /* Fault Processing Disable */ + uint64_t dest_mode:1; /* Destination Mode */ + uint64_t redir_hint:1; /* Redirection Hint */ + uint64_t trigger_mode:1; /* Trigger Mode */ + uint64_t delivery_mode:3; /* Delivery Mode */ + uint64_t __avail:4; /* Available spaces for software */ + uint64_t __reserved_0:3; /* Reserved 0 */ + uint64_t irte_mode:1; /* IRTE Mode */ + uint64_t vector:8; /* Interrupt Vector */ + uint64_t __reserved_1:8; /* Reserved 1 */ + uint64_t dest_id:32; /* Destination ID */ #endif - uint32_t dest_id; /* Destination ID */ - uint16_t source_id; /* Source-ID */ #if HOST_BIG_ENDIAN uint64_t __reserved_2:44; /* Reserved 2 */ uint64_t sid_vtype:2; /* Source-ID Validation Type */ uint64_t sid_q:2; /* Source-ID Qualifier */ + uint64_t source_id:16; /* Source-ID */ #else + uint64_t source_id:16; /* Source-ID */ uint64_t sid_q:2; /* Source-ID Qualifier */ uint64_t sid_vtype:2; /* Source-ID Validation Type */ uint64_t __reserved_2:44; /* Reserved 2 */ @@ -253,8 +287,8 @@ struct IntelIOMMUState { uint32_t context_cache_gen; /* Should be in [1,MAX] */ GHashTable *iotlb; /* IOTLB */ - GHashTable *vtd_as_by_busptr; /* VTDBus objects indexed by PCIBus* reference */ - VTDBus *vtd_as_by_bus_num[VTD_PCI_BUS_MAX]; /* VTDBus objects indexed by bus number */ + GHashTable *vtd_address_spaces; /* VTD address spaces */ + VTDAddressSpace *vtd_as_cache[VTD_PCI_BUS_MAX]; /* VTD address space cache */ /* list of registered notifiers */ QLIST_HEAD(, VTDAddressSpace) vtd_as_with_notifiers; @@ -268,6 +302,7 @@ struct IntelIOMMUState { uint8_t aw_bits; /* Host/IOVA address width (in bits) */ bool dma_drain; /* Whether DMA r/w draining enabled */ bool dma_translation; /* Whether DMA translation supported */ + bool pasid; /* Whether to support PASID */ /* * Protects IOMMU states in general. Currently it protects the @@ -279,6 +314,7 @@ struct IntelIOMMUState { /* Find the VTD Address space associated with the given bus pointer, * create a new one if none exists */ -VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn); +VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, + int devfn, unsigned int pasid); #endif diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index ffcac5121e..27a68071d7 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -9,13 +9,12 @@ #include "hw/block/flash.h" #include "hw/i386/x86.h" -#include "hw/acpi/acpi_dev_interface.h" #include "hw/hotplug.h" #include "qom/object.h" #include "hw/i386/sgx-epc.h" -#include "hw/firmware/smbios.h" +#include "hw/cxl/cxl.h" -#define HPET_INTCAP "hpet-intcap" +#define MAX_IDE_BUS 2 /** * PCMachineState: @@ -32,16 +31,18 @@ typedef struct PCMachineState { Notifier machine_done; /* Pointers to devices and objects: */ - PCIBus *bus; + PCIBus *pcibus; I2CBus *smbus; PFlashCFI01 *flash[2]; ISADevice *pcspk; DeviceState *iommu; + BusState *idebus[MAX_IDE_BUS]; /* Configuration options: */ uint64_t max_ram_below_4g; OnOffAuto vmport; SmbiosEntryPointType smbios_entry_point_type; + const char *south_bridge; bool acpi_build_enabled; bool smbus_enabled; @@ -49,17 +50,18 @@ typedef struct PCMachineState { bool hpet_enabled; bool i8042_enabled; bool default_bus_bypass_iommu; + bool fd_bootchk; uint64_t max_fw_size; /* ACPI Memory hotplug IO base address */ hwaddr memhp_io_base; SGXEPCState sgx_epc; + CXLState cxl_devices_state; } PCMachineState; #define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device" #define PC_MACHINE_MAX_RAM_BELOW_4G "max-ram-below-4g" -#define PC_MACHINE_DEVMEM_REGION_SIZE "device-memory-region-size" #define PC_MACHINE_VMPORT "vmport" #define PC_MACHINE_SMBUS "smbus" #define PC_MACHINE_SATA "sata" @@ -91,8 +93,7 @@ struct PCMachineClass { /* Device configuration: */ bool pci_enabled; - bool kvmclock_enabled; - const char *default_nic_model; + const char *default_south_bridge; /* Compat options: */ @@ -104,19 +105,20 @@ struct PCMachineClass { bool rsdp_in_ram; int legacy_acpi_table_size; unsigned acpi_data_size; - bool do_not_add_smb_acpi; int pci_root_uid; /* SMBIOS compat: */ bool smbios_defaults; bool smbios_legacy_mode; bool smbios_uuid_encoded; + SmbiosEntryPointType default_smbios_ep_type; /* RAM / address space compat: */ bool gigabyte_align; bool has_reserved_memory; bool enforce_aligned_dimm; bool broken_reserved_end; + bool enforce_amd_1tb_hole; /* generate legacy CPU hotplug AML */ bool legacy_cpu_hotplug; @@ -126,6 +128,15 @@ struct PCMachineClass { /* create kvmclock device even when KVM PV features are not exposed */ bool kvmclock_create_always; + + /* resizable acpi blob compat */ + bool resizable_acpi_blob; + + /* + * whether the machine type implements broken 32-bit address space bound + * check for memory. + */ + bool broken_32bit_mem_addr_check; }; #define TYPE_PC_MACHINE "generic-pc-machine" @@ -136,12 +147,13 @@ OBJECT_DECLARE_TYPE(PCMachineState, PCMachineClass, PC_MACHINE) GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled); /* pc.c */ -extern int fd_bootchk; void pc_acpi_smi_interrupt(void *opaque, int irq, int level); -void pc_guest_info_init(PCMachineState *pcms); - +#define PCI_HOST_PROP_RAM_MEM "ram-mem" +#define PCI_HOST_PROP_PCI_MEM "pci-mem" +#define PCI_HOST_PROP_SYSTEM_MEM "system-mem" +#define PCI_HOST_PROP_IO_MEM "io-mem" #define PCI_HOST_PROP_PCI_HOLE_START "pci-hole-start" #define PCI_HOST_PROP_PCI_HOLE_END "pci-hole-end" #define PCI_HOST_PROP_PCI_HOLE64_START "pci-hole64-start" @@ -151,34 +163,25 @@ void pc_guest_info_init(PCMachineState *pcms); #define PCI_HOST_ABOVE_4G_MEM_SIZE "above-4g-mem-size" -void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory, +void pc_pci_as_mapping_init(MemoryRegion *system_memory, MemoryRegion *pci_address_space); void xen_load_linux(PCMachineState *pcms); void pc_memory_init(PCMachineState *pcms, MemoryRegion *system_memory, MemoryRegion *rom_memory, - MemoryRegion **ram_memory); + uint64_t pci_hole64_size); uint64_t pc_pci_hole64_start(void); DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus); void pc_basic_device_init(struct PCMachineState *pcms, ISABus *isa_bus, qemu_irq *gsi, - ISADevice **rtc_state, + ISADevice *rtc_state, bool create_fdctrl, uint32_t hpet_irqs); -void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd); -void pc_cmos_init(PCMachineState *pcms, - BusState *ide0, BusState *ide1, - ISADevice *s); void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus); -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); -ISADevice *pc_find_fdc0(void); - /* port92.c */ #define PORT92_A20_LINE "a20" @@ -192,14 +195,24 @@ bool pc_system_ovmf_table_find(const char *entry, uint8_t **data, int *data_len); void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size); -/* hw/i386/acpi-common.c */ -void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, - const CPUArchIdList *apic_ids, GArray *entry, - bool force_enabled); - /* sgx.c */ void pc_machine_init_sgx_epc(PCMachineState *pcms); +extern GlobalProperty pc_compat_8_2[]; +extern const size_t pc_compat_8_2_len; + +extern GlobalProperty pc_compat_8_1[]; +extern const size_t pc_compat_8_1_len; + +extern GlobalProperty pc_compat_8_0[]; +extern const size_t pc_compat_8_0_len; + +extern GlobalProperty pc_compat_7_2[]; +extern const size_t pc_compat_7_2_len; + +extern GlobalProperty pc_compat_7_1[]; +extern const size_t pc_compat_7_1_len; + extern GlobalProperty pc_compat_7_0[]; extern const size_t pc_compat_7_0_len; @@ -275,26 +288,6 @@ extern const size_t pc_compat_2_1_len; extern GlobalProperty pc_compat_2_0[]; extern const size_t pc_compat_2_0_len; -extern GlobalProperty pc_compat_1_7[]; -extern const size_t pc_compat_1_7_len; - -extern GlobalProperty pc_compat_1_6[]; -extern const size_t pc_compat_1_6_len; - -extern GlobalProperty pc_compat_1_5[]; -extern const size_t pc_compat_1_5_len; - -extern GlobalProperty pc_compat_1_4[]; -extern const size_t pc_compat_1_4_len; - -/* Helper for setting model-id for CPU models that changed model-id - * depending on QEMU versions up to QEMU 2.4. - */ -#define PC_CPU_MODEL_IDS(v) \ - { "qemu32-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ - { "qemu64-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ - { "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, }, - #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \ { \ diff --git a/include/hw/i386/sgx-epc.h b/include/hw/i386/sgx-epc.h index 581fac389a..3e00efd870 100644 --- a/include/hw/i386/sgx-epc.h +++ b/include/hw/i386/sgx-epc.h @@ -12,6 +12,7 @@ #ifndef QEMU_SGX_EPC_H #define QEMU_SGX_EPC_H +#include "hw/qdev-core.h" #include "hw/i386/hostmem-epc.h" #define TYPE_SGX_EPC "sgx-epc" diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h index 81573f6cfd..d4eeb7ab82 100644 --- a/include/hw/i386/topology.h +++ b/include/hw/i386/topology.h @@ -24,14 +24,15 @@ #ifndef HW_I386_TOPOLOGY_H #define HW_I386_TOPOLOGY_H -/* This file implements the APIC-ID-based CPU topology enumeration logic, +/* + * This file implements the APIC-ID-based CPU topology enumeration logic, * documented at the following document: * Intel® 64 Architecture Processor Topology Enumeration * http://software.intel.com/en-us/articles/intel-64-architecture-processor-topology-enumeration/ * * This code should be compatible with AMD's "Extended Method" described at: * AMD CPUID Specification (Publication #25481) - * Section 3: Multiple Core Calcuation + * Section 3: Multiple Core Calculation * as long as: * nr_threads is set to 1; * OFFSET_IDX is assumed to be 0; @@ -41,7 +42,8 @@ #include "qemu/bitops.h" -/* APIC IDs can be 32-bit, but beware: APIC IDs > 255 require x2APIC support +/* + * APIC IDs can be 32-bit, but beware: APIC IDs > 255 require x2APIC support */ typedef uint32_t apic_id_t; @@ -58,8 +60,7 @@ typedef struct X86CPUTopoInfo { unsigned threads_per_core; } X86CPUTopoInfo; -/* Return the bit width needed for 'count' IDs - */ +/* Return the bit width needed for 'count' IDs */ static unsigned apicid_bitwidth_for_count(unsigned count) { g_assert(count >= 1); @@ -67,15 +68,13 @@ static unsigned apicid_bitwidth_for_count(unsigned count) return count ? 32 - clz32(count) : 0; } -/* Bit width of the SMT_ID (thread ID) field on the APIC ID - */ +/* Bit width of the SMT_ID (thread ID) field on the APIC ID */ static inline unsigned apicid_smt_width(X86CPUTopoInfo *topo_info) { return apicid_bitwidth_for_count(topo_info->threads_per_core); } -/* Bit width of the Core_ID field - */ +/* Bit width of the Core_ID field */ static inline unsigned apicid_core_width(X86CPUTopoInfo *topo_info) { return apicid_bitwidth_for_count(topo_info->cores_per_die); @@ -87,8 +86,7 @@ static inline unsigned apicid_die_width(X86CPUTopoInfo *topo_info) return apicid_bitwidth_for_count(topo_info->dies_per_pkg); } -/* Bit offset of the Core_ID field - */ +/* Bit offset of the Core_ID field */ static inline unsigned apicid_core_offset(X86CPUTopoInfo *topo_info) { return apicid_smt_width(topo_info); @@ -100,14 +98,14 @@ static inline unsigned apicid_die_offset(X86CPUTopoInfo *topo_info) return apicid_core_offset(topo_info) + apicid_core_width(topo_info); } -/* Bit offset of the Pkg_ID (socket ID) field - */ +/* Bit offset of the Pkg_ID (socket ID) field */ static inline unsigned apicid_pkg_offset(X86CPUTopoInfo *topo_info) { return apicid_die_offset(topo_info) + apicid_die_width(topo_info); } -/* Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID +/* + * 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. */ @@ -120,7 +118,8 @@ static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info, topo_ids->smt_id; } -/* Calculate thread/core/package IDs for a specific topology, +/* + * Calculate thread/core/package IDs for a specific topology, * based on (contiguous) CPU index */ static inline void x86_topo_ids_from_idx(X86CPUTopoInfo *topo_info, @@ -137,7 +136,8 @@ static inline void x86_topo_ids_from_idx(X86CPUTopoInfo *topo_info, topo_ids->smt_id = cpu_index % nr_threads; } -/* Calculate thread/core/package IDs for a specific topology, +/* + * 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, @@ -155,7 +155,8 @@ static inline void x86_topo_ids_from_apicid(apic_id_t apicid, topo_ids->pkg_id = apicid >> apicid_pkg_offset(topo_info); } -/* Make APIC ID for the CPU 'cpu_index' +/* + * Make APIC ID for the CPU 'cpu_index' * * 'cpu_index' is a sequential, contiguous ID for the CPU. */ diff --git a/include/hw/i386/x86-iommu.h b/include/hw/i386/x86-iommu.h index 7637edb430..bfd21649d0 100644 --- a/include/hw/i386/x86-iommu.h +++ b/include/hw/i386/x86-iommu.h @@ -21,7 +21,6 @@ #define HW_I386_X86_IOMMU_H #include "hw/sysbus.h" -#include "hw/pci/pci.h" #include "hw/pci/msi.h" #include "qom/object.h" @@ -88,40 +87,42 @@ struct X86IOMMU_MSIMessage { union { struct { #if HOST_BIG_ENDIAN - uint32_t __addr_head:12; /* 0xfee */ - uint32_t dest:8; - uint32_t __reserved:8; - uint32_t redir_hint:1; - uint32_t dest_mode:1; - uint32_t __not_used:2; + uint64_t __addr_hi:32; + uint64_t __addr_head:12; /* 0xfee */ + uint64_t dest:8; + uint64_t __reserved:8; + uint64_t redir_hint:1; + uint64_t dest_mode:1; + uint64_t __not_used:2; #else - uint32_t __not_used:2; - uint32_t dest_mode:1; - uint32_t redir_hint:1; - uint32_t __reserved:8; - uint32_t dest:8; - uint32_t __addr_head:12; /* 0xfee */ + uint64_t __not_used:2; + uint64_t dest_mode:1; + uint64_t redir_hint:1; + uint64_t __reserved:8; + uint64_t dest:8; + uint64_t __addr_head:12; /* 0xfee */ + uint64_t __addr_hi:32; #endif - uint32_t __addr_hi; } QEMU_PACKED; uint64_t msi_addr; }; union { struct { #if HOST_BIG_ENDIAN - uint16_t trigger_mode:1; - uint16_t level:1; - uint16_t __resved:3; - uint16_t delivery_mode:3; - uint16_t vector:8; + uint32_t __resved1:16; + uint32_t trigger_mode:1; + uint32_t level:1; + uint32_t __resved:3; + uint32_t delivery_mode:3; + uint32_t vector:8; #else - uint16_t vector:8; - uint16_t delivery_mode:3; - uint16_t __resved:3; - uint16_t level:1; - uint16_t trigger_mode:1; + uint32_t vector:8; + uint32_t delivery_mode:3; + uint32_t __resved:3; + uint32_t level:1; + uint32_t trigger_mode:1; + uint32_t __resved1:16; #endif - uint16_t __resved1; } QEMU_PACKED; uint32_t msi_data; }; diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 9089bdd99c..4dc30dcb4d 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -18,13 +18,10 @@ #define HW_I386_X86_H #include "exec/hwaddr.h" -#include "qemu/notify.h" -#include "hw/i386/topology.h" #include "hw/boards.h" -#include "hw/nmi.h" +#include "hw/intc/ioapic.h" #include "hw/isa/isa.h" -#include "hw/i386/ioapic.h" #include "qom/object.h" struct X86MachineClass { @@ -37,6 +34,8 @@ struct X86MachineClass { bool save_tsc_khz; /* use DMA capable linuxboot option rom */ bool fwcfg_dma_enabled; + /* CPU and apic information: */ + bool apic_xrupt_override; }; struct X86MachineState { @@ -56,8 +55,10 @@ struct X86MachineState { /* RAM information (sizes, addresses, configuration): */ ram_addr_t below_4g_mem_size, above_4g_mem_size; + /* Start address of the initial RAM above 4G */ + uint64_t above_4g_mem_start; + /* CPU and apic information: */ - bool apic_xrupt_override; unsigned pci_irq_mask; unsigned apic_id_limit; uint16_t boot_cpus; @@ -95,8 +96,6 @@ struct X86MachineState { #define TYPE_X86_MACHINE MACHINE_TYPE_NAME("x86") OBJECT_DECLARE_TYPE(X86MachineState, X86MachineClass, 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); @@ -130,7 +129,6 @@ bool x86_machine_is_acpi_enabled(const X86MachineState *x86ms); /* Global System Interrupts */ -#define GSI_NUM_PINS IOAPIC_NUM_PINS #define ACPI_BUILD_PCI_IRQS ((1<<5) | (1<<9) | (1<<10) | (1<<11)) typedef struct GSIState { @@ -141,7 +139,7 @@ typedef struct 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); +void ioapic_init_gsi(GSIState *gsi_state, Object *parent); DeviceState *ioapic_init_secondary(GSIState *gsi_state); /* pc_sysfw.c */ diff --git a/include/hw/i386/xen_arch_hvm.h b/include/hw/i386/xen_arch_hvm.h new file mode 100644 index 0000000000..1000f8f543 --- /dev/null +++ b/include/hw/i386/xen_arch_hvm.h @@ -0,0 +1,11 @@ +#ifndef HW_XEN_ARCH_I386_HVM_H +#define HW_XEN_ARCH_I386_HVM_H + +#include +#include "hw/xen/xen-hvm-common.h" + +void arch_handle_ioreq(XenIOState *state, ioreq_t *req); +void arch_xen_set_memory(XenIOState *state, + MemoryRegionSection *section, + bool add); +#endif diff --git a/include/hw/ide.h b/include/hw/ide.h deleted file mode 100644 index 60f1f4f714..0000000000 --- a/include/hw/ide.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef HW_IDE_H -#define HW_IDE_H - -#include "hw/isa/isa.h" -#include "exec/memory.h" - -/* ide-isa.c */ -ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, - DriveInfo *hd0, DriveInfo *hd1); - -/* ide-mmio.c */ -void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1); - -int ide_get_geometry(BusState *bus, int unit, - int16_t *cyls, int8_t *heads, int8_t *secs); -int ide_get_bios_chs_trans(BusState *bus, int unit); - -/* ide/core.c */ -void ide_drive_get(DriveInfo **hd, int max_bus); - -#endif /* HW_IDE_H */ diff --git a/include/hw/ide/ahci-pci.h b/include/hw/ide/ahci-pci.h new file mode 100644 index 0000000000..c2ee616962 --- /dev/null +++ b/include/hw/ide/ahci-pci.h @@ -0,0 +1,22 @@ +/* + * QEMU AHCI Emulation (PCI devices) + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_IDE_AHCI_PCI_H +#define HW_IDE_AHCI_PCI_H + +#include "qom/object.h" +#include "hw/ide/ahci.h" +#include "hw/pci/pci_device.h" + +#define TYPE_ICH9_AHCI "ich9-ahci" +OBJECT_DECLARE_SIMPLE_TYPE(AHCIPCIState, ICH9_AHCI) + +struct AHCIPCIState { + PCIDevice parent_obj; + + AHCIState ahci; +}; + +#endif diff --git a/include/hw/ide/ahci-sysbus.h b/include/hw/ide/ahci-sysbus.h new file mode 100644 index 0000000000..06eaac8cb6 --- /dev/null +++ b/include/hw/ide/ahci-sysbus.h @@ -0,0 +1,35 @@ +/* + * QEMU AHCI Emulation (MMIO-mapped devices) + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_IDE_AHCI_SYSBUS_H +#define HW_IDE_AHCI_SYSBUS_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "hw/ide/ahci.h" + +#define TYPE_SYSBUS_AHCI "sysbus-ahci" +OBJECT_DECLARE_SIMPLE_TYPE(SysbusAHCIState, SYSBUS_AHCI) + +struct SysbusAHCIState { + SysBusDevice parent_obj; + + AHCIState ahci; +}; + +#define TYPE_ALLWINNER_AHCI "allwinner-ahci" +OBJECT_DECLARE_SIMPLE_TYPE(AllwinnerAHCIState, ALLWINNER_AHCI) + +#define ALLWINNER_AHCI_MMIO_OFF 0x80 +#define ALLWINNER_AHCI_MMIO_SIZE 0x80 + +struct AllwinnerAHCIState { + SysbusAHCIState parent_obj; + + MemoryRegion mmio; + uint32_t regs[ALLWINNER_AHCI_MMIO_SIZE / 4]; +}; + +#endif diff --git a/include/hw/ide/ahci.h b/include/hw/ide/ahci.h index 210e5e734c..ba31e75ff9 100644 --- a/include/hw/ide/ahci.h +++ b/include/hw/ide/ahci.h @@ -24,8 +24,7 @@ #ifndef HW_IDE_AHCI_H #define HW_IDE_AHCI_H -#include "hw/sysbus.h" -#include "qom/object.h" +#include "exec/memory.h" typedef struct AHCIDevice AHCIDevice; @@ -46,43 +45,12 @@ typedef struct AHCIState { MemoryRegion idp; /* Index-Data Pair I/O port space */ unsigned idp_offset; /* Offset of index in I/O port space */ uint32_t idp_index; /* Current IDP index */ - int32_t ports; + uint32_t ports; qemu_irq irq; AddressSpace *as; } AHCIState; -#define TYPE_ICH9_AHCI "ich9-ahci" -OBJECT_DECLARE_SIMPLE_TYPE(AHCIPCIState, ICH9_AHCI) - -int32_t ahci_get_num_ports(PCIDevice *dev); -void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd); - -#define TYPE_SYSBUS_AHCI "sysbus-ahci" -OBJECT_DECLARE_SIMPLE_TYPE(SysbusAHCIState, SYSBUS_AHCI) - -struct SysbusAHCIState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - AHCIState ahci; - uint32_t num_ports; -}; - -#define TYPE_ALLWINNER_AHCI "allwinner-ahci" -OBJECT_DECLARE_SIMPLE_TYPE(AllwinnerAHCIState, ALLWINNER_AHCI) - -#define ALLWINNER_AHCI_MMIO_OFF 0x80 -#define ALLWINNER_AHCI_MMIO_SIZE 0x80 - -struct AllwinnerAHCIState { - /*< private >*/ - SysbusAHCIState parent_obj; - /*< public >*/ - - MemoryRegion mmio; - uint32_t regs[ALLWINNER_AHCI_MMIO_SIZE/4]; -}; +void ahci_ide_create_devs(AHCIState *ahci, DriveInfo **hd); #endif /* HW_IDE_AHCI_H */ diff --git a/include/hw/ide/ide-bus.h b/include/hw/ide/ide-bus.h new file mode 100644 index 0000000000..4841a7dcd6 --- /dev/null +++ b/include/hw/ide/ide-bus.h @@ -0,0 +1,42 @@ +#ifndef HW_IDE_BUS_H +#define HW_IDE_BUS_H + +#include "exec/ioport.h" +#include "hw/ide/ide-dev.h" +#include "hw/ide/ide-dma.h" + +struct IDEBus { + BusState qbus; + IDEDevice *master; + IDEDevice *slave; + IDEState ifs[2]; + QEMUBH *bh; + + int bus_id; + int max_units; + IDEDMA *dma; + uint8_t unit; + uint8_t cmd; + qemu_irq irq; /* bus output */ + + int error_status; + uint8_t retry_unit; + int64_t retry_sector_num; + uint32_t retry_nsector; + PortioList portio_list; + PortioList portio2_list; + VMChangeStateEntry *vmstate; +}; + +#define TYPE_IDE_BUS "IDE" +OBJECT_DECLARE_SIMPLE_TYPE(IDEBus, IDE_BUS) + +void ide_bus_init(IDEBus *idebus, size_t idebus_size, DeviceState *dev, + int bus_id, int max_units); +IDEDevice *ide_bus_create_drive(IDEBus *bus, int unit, DriveInfo *drive); + +int ide_get_geometry(BusState *bus, int unit, + int16_t *cyls, int8_t *heads, int8_t *secs); +int ide_get_bios_chs_trans(BusState *bus, int unit); + +#endif diff --git a/include/hw/ide/ide-dev.h b/include/hw/ide/ide-dev.h new file mode 100644 index 0000000000..9a0d71db4e --- /dev/null +++ b/include/hw/ide/ide-dev.h @@ -0,0 +1,186 @@ +/* + * ide device definitions + * + * Copyright (c) 2009 Gerd Hoffmann + * + * This code is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 IDE_DEV_H +#define IDE_DEV_H + +#include "sysemu/dma.h" +#include "hw/qdev-properties.h" +#include "hw/block/block.h" + +typedef struct IDEDevice IDEDevice; +typedef struct IDEState IDEState; +typedef struct IDEBus IDEBus; + +typedef void EndTransferFunc(IDEState *); + +#define MAX_IDE_DEVS 2 + +#define TYPE_IDE_DEVICE "ide-device" +OBJECT_DECLARE_TYPE(IDEDevice, IDEDeviceClass, IDE_DEVICE) + +typedef enum { IDE_HD, IDE_CD, IDE_CFATA } IDEDriveKind; + +struct unreported_events { + bool eject_request; + bool new_media; +}; + +enum ide_dma_cmd { + IDE_DMA_READ = 0, + IDE_DMA_WRITE, + IDE_DMA_TRIM, + IDE_DMA_ATAPI, + IDE_DMA__COUNT +}; + +/* NOTE: IDEState represents in fact one drive */ +struct IDEState { + IDEBus *bus; + uint8_t unit; + /* ide config */ + IDEDriveKind drive_kind; + int drive_heads, drive_sectors; + int cylinders, heads, sectors, chs_trans; + int64_t nb_sectors; + int mult_sectors; + int identify_set; + uint8_t identify_data[512]; + int drive_serial; + char drive_serial_str[21]; + char drive_model_str[41]; + bool win2k_install_hack; + uint64_t wwn; + /* ide regs */ + uint8_t feature; + uint8_t error; + uint32_t nsector; + uint8_t sector; + uint8_t lcyl; + uint8_t hcyl; + /* other part of tf for lba48 support */ + uint8_t hob_feature; + uint8_t hob_nsector; + uint8_t hob_sector; + uint8_t hob_lcyl; + uint8_t hob_hcyl; + + uint8_t select; + uint8_t status; + + bool io8; + bool reset_reverts; + + /* set for lba48 access */ + uint8_t lba48; + BlockBackend *blk; + char version[9]; + /* ATAPI specific */ + struct unreported_events events; + uint8_t sense_key; + uint8_t asc; + bool tray_open; + bool tray_locked; + uint8_t cdrom_changed; + int packet_transfer_size; + int elementary_transfer_size; + int32_t io_buffer_index; + int lba; + int cd_sector_size; + int atapi_dma; /* true if dma is requested for the packet cmd */ + BlockAcctCookie acct; + BlockAIOCB *pio_aiocb; + QEMUIOVector qiov; + QLIST_HEAD(, IDEBufferedRequest) buffered_requests; + /* ATA DMA state */ + uint64_t io_buffer_offset; + int32_t io_buffer_size; + QEMUSGList sg; + /* PIO transfer handling */ + int req_nb_sectors; /* number of sectors per interrupt */ + EndTransferFunc *end_transfer_func; + uint8_t *data_ptr; + uint8_t *data_end; + uint8_t *io_buffer; + /* PIO save/restore */ + int32_t io_buffer_total_len; + int32_t cur_io_buffer_offset; + int32_t cur_io_buffer_len; + uint8_t end_transfer_fn_idx; + QEMUTimer *sector_write_timer; /* only used for win2k install hack */ + uint32_t irq_count; /* counts IRQs when using win2k install hack */ + /* CF-ATA extended error */ + uint8_t ext_error; + /* CF-ATA metadata storage */ + uint32_t mdata_size; + uint8_t *mdata_storage; + int media_changed; + enum ide_dma_cmd dma_cmd; + /* SMART */ + uint8_t smart_enabled; + uint8_t smart_autosave; + int smart_errors; + uint8_t smart_selftest_count; + uint8_t *smart_selftest_data; + /* AHCI */ + int ncq_queues; +}; + +struct IDEDeviceClass { + DeviceClass parent_class; + void (*realize)(IDEDevice *dev, Error **errp); +}; + +struct IDEDevice { + DeviceState qdev; + uint32_t unit; + BlockConf conf; + int chs_trans; + char *version; + char *serial; + char *model; + uint64_t wwn; + /* + * 0x0000 - rotation rate not reported + * 0x0001 - non-rotating medium (SSD) + * 0x0002-0x0400 - reserved + * 0x0401-0xffe - rotations per minute + * 0xffff - reserved + */ + uint16_t rotation_rate; + bool win2k_install_hack; +}; + +typedef struct IDEDrive { + IDEDevice dev; +} IDEDrive; + +#define DEFINE_IDE_DEV_PROPERTIES() \ + DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), \ + DEFINE_BLOCK_ERROR_PROPERTIES(IDEDrive, dev.conf), \ + DEFINE_PROP_STRING("ver", IDEDrive, dev.version), \ + DEFINE_PROP_UINT64("wwn", IDEDrive, dev.wwn, 0), \ + DEFINE_PROP_STRING("serial", IDEDrive, dev.serial),\ + DEFINE_PROP_STRING("model", IDEDrive, dev.model) + +void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp); + +void ide_drive_get(DriveInfo **hd, int max_bus); + +#endif diff --git a/include/hw/ide/ide-dma.h b/include/hw/ide/ide-dma.h new file mode 100644 index 0000000000..d0b19ac9c5 --- /dev/null +++ b/include/hw/ide/ide-dma.h @@ -0,0 +1,37 @@ +#ifndef HW_IDE_DMA_H +#define HW_IDE_DMA_H + +#include "block/aio.h" +#include "qemu/iov.h" + +typedef struct IDEState IDEState; +typedef struct IDEDMAOps IDEDMAOps; +typedef struct IDEDMA IDEDMA; + +typedef void DMAStartFunc(const IDEDMA *, IDEState *, BlockCompletionFunc *); +typedef void DMAVoidFunc(const IDEDMA *); +typedef int DMAIntFunc(const IDEDMA *, bool); +typedef int32_t DMAInt32Func(const IDEDMA *, int32_t len); +typedef void DMAu32Func(const IDEDMA *, uint32_t); +typedef void DMAStopFunc(const IDEDMA *, bool); + +struct IDEDMAOps { + DMAStartFunc *start_dma; + DMAVoidFunc *pio_transfer; + DMAInt32Func *prepare_buf; + DMAu32Func *commit_buf; + DMAIntFunc *rw_buf; + DMAVoidFunc *restart; + DMAVoidFunc *restart_dma; + DMAStopFunc *set_inactive; + DMAVoidFunc *cmd_done; + DMAVoidFunc *reset; +}; + +struct IDEDMA { + const IDEDMAOps *ops; + QEMUIOVector qiov; + BlockAIOCB *aiocb; +}; + +#endif diff --git a/include/hw/ide/isa.h b/include/hw/ide/isa.h new file mode 100644 index 0000000000..1cd0ff1fa6 --- /dev/null +++ b/include/hw/ide/isa.h @@ -0,0 +1,20 @@ +/* + * QEMU IDE Emulation: ISA Bus support. + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2006 Openedhand Ltd. + * + * SPDX-License-Identifier: MIT + */ +#ifndef HW_IDE_ISA_H +#define HW_IDE_ISA_H + +#include "qom/object.h" + +#define TYPE_ISA_IDE "isa-ide" +OBJECT_DECLARE_SIMPLE_TYPE(ISAIDEState, ISA_IDE) + +ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int irqnum, + DriveInfo *hd0, DriveInfo *hd1); + +#endif diff --git a/include/hw/ide/mmio.h b/include/hw/ide/mmio.h new file mode 100644 index 0000000000..d726a49848 --- /dev/null +++ b/include/hw/ide/mmio.h @@ -0,0 +1,26 @@ +/* + * QEMU IDE Emulation: mmio support (for embedded). + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2006 Openedhand Ltd. + * + * SPDX-License-Identifier: MIT + */ + +#ifndef HW_IDE_MMIO_H +#define HW_IDE_MMIO_H + +#include "qom/object.h" + +/* + * QEMU interface: + * + sysbus IRQ 0: asserted by the IDE channel + * + sysbus MMIO region 0: data registers + * + sysbus MMIO region 1: status & control registers + */ +#define TYPE_MMIO_IDE "mmio-ide" +OBJECT_DECLARE_SIMPLE_TYPE(MMIOIDEState, MMIO_IDE) + +void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1); + +#endif diff --git a/include/hw/ide/pci.h b/include/hw/ide/pci.h index d8384e1c42..ef03764caa 100644 --- a/include/hw/ide/pci.h +++ b/include/hw/ide/pci.h @@ -1,8 +1,8 @@ #ifndef HW_IDE_PCI_H #define HW_IDE_PCI_H -#include "hw/ide/internal.h" -#include "hw/pci/pci.h" +#include "hw/ide/ide-bus.h" +#include "hw/pci/pci_device.h" #include "qom/object.h" #define BM_STATUS_DMAING 0x01 @@ -49,22 +49,19 @@ struct PCIIDEState { IDEBus bus[2]; BMDMAState bmdma[2]; + qemu_irq isa_irq[2]; uint32_t secondary; /* used only for cmd646 */ MemoryRegion bmdma_bar; MemoryRegion cmd_bar[2]; MemoryRegion data_bar[2]; }; -static inline IDEState *bmdma_active_if(BMDMAState *bmdma) -{ - assert(bmdma->bus->retry_unit != (uint8_t)-1); - return bmdma->bus->ifs + bmdma->bus->retry_unit; -} - void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d); void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val); +void bmdma_status_writeb(BMDMAState *bm, uint32_t val); extern MemoryRegionOps bmdma_addr_ioport_ops; void pci_ide_create_devs(PCIDevice *dev); +void pci_ide_update_mode(PCIIDEState *s); extern const VMStateDescription vmstate_ide_pci; extern const MemoryRegionOps pci_ide_cmd_le_ops; diff --git a/include/hw/ide/piix.h b/include/hw/ide/piix.h new file mode 100644 index 0000000000..ef3ef3d62d --- /dev/null +++ b/include/hw/ide/piix.h @@ -0,0 +1,7 @@ +#ifndef HW_IDE_PIIX_H +#define HW_IDE_PIIX_H + +#define TYPE_PIIX3_IDE "piix3-ide" +#define TYPE_PIIX4_IDE "piix4-ide" + +#endif /* HW_IDE_PIIX_H */ diff --git a/include/hw/input/gamepad.h b/include/hw/input/gamepad.h deleted file mode 100644 index 6f6aa2406a..0000000000 --- a/include/hw/input/gamepad.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Gamepad style buttons connected to IRQ/GPIO lines - * - * Copyright (c) 2007 CodeSourcery. - * Written by Paul Brook - * - * 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_INPUT_GAMEPAD_H -#define HW_INPUT_GAMEPAD_H - - -/* stellaris_input.c */ -void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode); - -#endif diff --git a/include/hw/input/i8042.h b/include/hw/input/i8042.h index e070f546e4..e90f008b66 100644 --- a/include/hw/input/i8042.h +++ b/include/hw/input/i8042.h @@ -9,19 +9,86 @@ #define HW_INPUT_I8042_H #include "hw/isa/isa.h" +#include "hw/sysbus.h" +#include "hw/input/ps2.h" #include "qom/object.h" +#define I8042_KBD_IRQ 0 +#define I8042_MOUSE_IRQ 1 + +typedef struct KBDState { + uint8_t write_cmd; /* if non zero, write data to port 60 is expected */ + uint8_t status; + uint8_t mode; + uint8_t outport; + uint32_t migration_flags; + uint32_t obsrc; + bool outport_present; + bool extended_state; + bool extended_state_loaded; + /* Bitmask of devices with data available. */ + uint8_t pending; + uint8_t obdata; + uint8_t cbdata; + uint8_t pending_tmp; + PS2KbdState ps2kbd; + PS2MouseState ps2mouse; + QEMUTimer *throttle_timer; + + qemu_irq irqs[2]; + qemu_irq a20_out; + hwaddr mask; +} KBDState; + +/* + * QEMU interface: + * + Named GPIO input "ps2-kbd-input-irq": set to 1 if the downstream PS2 + * keyboard device has asserted its irq + * + Named GPIO input "ps2-mouse-input-irq": set to 1 if the downstream PS2 + * mouse device has asserted its irq + * + Named GPIO output "a20": A20 line for x86 PCs + * + Unnamed GPIO output 0-1: i8042 output irqs for keyboard (0) or mouse (1) + */ + #define TYPE_I8042 "i8042" OBJECT_DECLARE_SIMPLE_TYPE(ISAKBDState, I8042) +struct ISAKBDState { + ISADevice parent_obj; + + KBDState kbd; + bool kbd_throttle; + MemoryRegion io[2]; + uint8_t kbd_irq; + uint8_t mouse_irq; +}; + +/* + * QEMU interface: + * + sysbus MMIO region 0: MemoryRegion defining the command/status/data + * registers (access determined by mask property and access type) + * + Named GPIO input "ps2-kbd-input-irq": set to 1 if the downstream PS2 + * keyboard device has asserted its irq + * + Named GPIO input "ps2-mouse-input-irq": set to 1 if the downstream PS2 + * mouse device has asserted its irq + * + Unnamed GPIO output 0-1: i8042 output irqs for keyboard (0) or mouse (1) + */ + +#define TYPE_I8042_MMIO "i8042-mmio" +OBJECT_DECLARE_SIMPLE_TYPE(MMIOKBDState, I8042_MMIO) + +struct MMIOKBDState { + SysBusDevice parent_obj; + + KBDState kbd; + uint32_t size; + MemoryRegion region; +}; + #define I8042_A20_LINE "a20" -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(ISAKBDState *isa); -void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out); static inline bool i8042_present(void) { diff --git a/include/hw/input/lasips2.h b/include/hw/input/lasips2.h index 0cd7b59064..01911c50f9 100644 --- a/include/hw/input/lasips2.h +++ b/include/hw/input/lasips2.h @@ -4,13 +4,77 @@ * Copyright (c) 2019 Sven Schnelle * */ + +/* + * QEMU interface: + * + sysbus MMIO region 0: MemoryRegion defining the LASI PS2 keyboard + * registers + * + sysbus MMIO region 1: MemoryRegion defining the LASI PS2 mouse + * registers + * + sysbus IRQ 0: LASI PS2 output irq + * + Named GPIO input "lasips2-port-input-irq[0..1]": set to 1 if the downstream + * LASIPS2Port has asserted its irq + */ + #ifndef HW_INPUT_LASIPS2_H #define HW_INPUT_LASIPS2_H #include "exec/hwaddr.h" +#include "hw/sysbus.h" +#include "hw/input/ps2.h" + +#define TYPE_LASIPS2_PORT "lasips2-port" +OBJECT_DECLARE_TYPE(LASIPS2Port, LASIPS2PortDeviceClass, LASIPS2_PORT) + +struct LASIPS2PortDeviceClass { + DeviceClass parent; + + DeviceRealize parent_realize; +}; + +typedef struct LASIPS2State LASIPS2State; + +struct LASIPS2Port { + DeviceState parent_obj; + + LASIPS2State *lasips2; + MemoryRegion reg; + PS2State *ps2dev; + uint8_t id; + uint8_t control; + uint8_t buf; + bool loopback_rbne; + qemu_irq irq; +}; + +#define TYPE_LASIPS2_KBD_PORT "lasips2-kbd-port" +OBJECT_DECLARE_SIMPLE_TYPE(LASIPS2KbdPort, LASIPS2_KBD_PORT) + +struct LASIPS2KbdPort { + LASIPS2Port parent_obj; + + PS2KbdState kbd; +}; + +#define TYPE_LASIPS2_MOUSE_PORT "lasips2-mouse-port" +OBJECT_DECLARE_SIMPLE_TYPE(LASIPS2MousePort, LASIPS2_MOUSE_PORT) + +struct LASIPS2MousePort { + LASIPS2Port parent_obj; + + PS2MouseState mouse; +}; + +struct LASIPS2State { + SysBusDevice parent_obj; + + LASIPS2KbdPort kbd_port; + LASIPS2MousePort mouse_port; + uint8_t int_status; + qemu_irq irq; +}; #define TYPE_LASIPS2 "lasips2" - -void lasips2_init(MemoryRegion *address_space, hwaddr base, qemu_irq irq); +OBJECT_DECLARE_SIMPLE_TYPE(LASIPS2State, LASIPS2) #endif /* HW_INPUT_LASIPS2_H */ diff --git a/include/hw/input/pl050.h b/include/hw/input/pl050.h new file mode 100644 index 0000000000..4cb8985f31 --- /dev/null +++ b/include/hw/input/pl050.h @@ -0,0 +1,58 @@ +/* + * Arm PrimeCell PL050 Keyboard / Mouse Interface + * + * Copyright (c) 2006-2007 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the GPL. + */ + +#ifndef HW_PL050_H +#define HW_PL050_H + +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "hw/input/ps2.h" +#include "hw/irq.h" + +struct PL050DeviceClass { + SysBusDeviceClass parent_class; + + DeviceRealize parent_realize; +}; + +#define TYPE_PL050 "pl050" +OBJECT_DECLARE_TYPE(PL050State, PL050DeviceClass, PL050) + +struct PL050State { + SysBusDevice parent_obj; + + MemoryRegion iomem; + PS2State *ps2dev; + uint32_t cr; + uint32_t clk; + uint32_t last; + int pending; + qemu_irq irq; + bool is_mouse; +}; + +#define TYPE_PL050_KBD_DEVICE "pl050_keyboard" +OBJECT_DECLARE_SIMPLE_TYPE(PL050KbdState, PL050_KBD_DEVICE) + +struct PL050KbdState { + PL050State parent_obj; + + PS2KbdState kbd; +}; + +#define TYPE_PL050_MOUSE_DEVICE "pl050_mouse" +OBJECT_DECLARE_SIMPLE_TYPE(PL050MouseState, PL050_MOUSE_DEVICE) + +struct PL050MouseState { + PL050State parent_obj; + + PS2MouseState mouse; +}; + +#endif diff --git a/include/hw/input/ps2.h b/include/hw/input/ps2.h index 35d983897a..cd61a634c3 100644 --- a/include/hw/input/ps2.h +++ b/include/hw/input/ps2.h @@ -25,28 +25,89 @@ #ifndef HW_PS2_H #define HW_PS2_H +#include "hw/sysbus.h" + #define PS2_MOUSE_BUTTON_LEFT 0x01 #define PS2_MOUSE_BUTTON_RIGHT 0x02 #define PS2_MOUSE_BUTTON_MIDDLE 0x04 #define PS2_MOUSE_BUTTON_SIDE 0x08 #define PS2_MOUSE_BUTTON_EXTRA 0x10 -typedef struct PS2State PS2State; +struct PS2DeviceClass { + SysBusDeviceClass parent_class; + + ResettablePhases parent_phases; +}; + +/* + * PS/2 buffer size. Keep 256 bytes for compatibility with + * older QEMU versions. + */ +#define PS2_BUFFER_SIZE 256 + +typedef struct { + uint8_t data[PS2_BUFFER_SIZE]; + int rptr, wptr, cwptr, count; +} PS2Queue; + +/* Output IRQ */ +#define PS2_DEVICE_IRQ 0 + +struct PS2State { + SysBusDevice parent_obj; + + PS2Queue queue; + int32_t write_cmd; + qemu_irq irq; +}; + +#define TYPE_PS2_DEVICE "ps2-device" +OBJECT_DECLARE_TYPE(PS2State, PS2DeviceClass, PS2_DEVICE) + +struct PS2KbdState { + PS2State parent_obj; + + int scan_enabled; + int translate; + int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */ + int ledstate; + bool need_high_bit; + unsigned int modifiers; /* bitmask of MOD_* constants above */ +}; + +#define TYPE_PS2_KBD_DEVICE "ps2-kbd" +OBJECT_DECLARE_SIMPLE_TYPE(PS2KbdState, PS2_KBD_DEVICE) + +struct PS2MouseState { + PS2State parent_obj; + + uint8_t mouse_status; + uint8_t mouse_resolution; + uint8_t mouse_sample_rate; + uint8_t mouse_wrap; + uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */ + uint8_t mouse_detect_state; + int mouse_dx; /* current values, needed for 'poll' mode */ + int mouse_dy; + int mouse_dz; + int mouse_dw; + uint8_t mouse_buttons; +}; + +#define TYPE_PS2_MOUSE_DEVICE "ps2-mouse" +OBJECT_DECLARE_SIMPLE_TYPE(PS2MouseState, PS2_MOUSE_DEVICE) /* ps2.c */ -void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg); -void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg); -void ps2_write_mouse(void *, int val); -void ps2_write_keyboard(void *, int val); +void ps2_write_mouse(PS2MouseState *s, int val); +void ps2_write_keyboard(PS2KbdState *s, int val); uint32_t ps2_read_data(PS2State *s); void ps2_queue_noirq(PS2State *s, int b); -void ps2_raise_irq(PS2State *s); void ps2_queue(PS2State *s, int b); void ps2_queue_2(PS2State *s, int b1, int b2); 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); +void ps2_keyboard_set_translation(PS2KbdState *s, int mode); +void ps2_mouse_fake_event(PS2MouseState *s); int ps2_queue_empty(PS2State *s); #endif /* HW_PS2_H */ diff --git a/include/hw/input/stellaris_gamepad.h b/include/hw/input/stellaris_gamepad.h new file mode 100644 index 0000000000..51085e166c --- /dev/null +++ b/include/hw/input/stellaris_gamepad.h @@ -0,0 +1,37 @@ +/* + * Gamepad style buttons connected to IRQ/GPIO lines + * + * Copyright (c) 2007 CodeSourcery. + * Written by Paul Brook + * + * 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_INPUT_STELLARIS_GAMEPAD_H +#define HW_INPUT_STELLARIS_GAMEPAD_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +/* + * QEMU interface: + * + QOM array property "keycodes": uint32_t QEMU keycodes to handle + * (these are QCodes, ie the Q_KEY_* values) + * + unnamed GPIO outputs: one per keycode, in the same order as the + * "keycodes" array property entries; asserted when key is down + */ + +#define TYPE_STELLARIS_GAMEPAD "stellaris-gamepad" +OBJECT_DECLARE_SIMPLE_TYPE(StellarisGamepad, STELLARIS_GAMEPAD) + +struct StellarisGamepad { + SysBusDevice parent_obj; + + uint32_t num_buttons; + qemu_irq *irqs; + uint32_t *keycodes; + uint8_t *pressed; +}; + +#endif diff --git a/include/hw/input/tsc2xxx.h b/include/hw/input/tsc2xxx.h index 5b76ebc177..00eca17674 100644 --- a/include/hw/input/tsc2xxx.h +++ b/include/hw/input/tsc2xxx.h @@ -30,12 +30,12 @@ uWireSlave *tsc2102_init(qemu_irq pint); uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav); I2SCodec *tsc210x_codec(uWireSlave *chip); uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len); -void tsc210x_set_transform(uWireSlave *chip, MouseTransformInfo *info); +void tsc210x_set_transform(uWireSlave *chip, const MouseTransformInfo *info); void tsc210x_key_event(uWireSlave *chip, int key, int down); /* tsc2005.c */ void *tsc2005_init(qemu_irq pintdav); uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len); -void tsc2005_set_transform(void *opaque, MouseTransformInfo *info); +void tsc2005_set_transform(void *opaque, const MouseTransformInfo *info); #endif diff --git a/include/hw/intc/arm_gic.h b/include/hw/intc/arm_gic.h index 116ccbb5a9..48f6a51a70 100644 --- a/include/hw/intc/arm_gic.h +++ b/include/hw/intc/arm_gic.h @@ -86,4 +86,6 @@ struct ARMGICClass { DeviceRealize parent_realize; }; +const char *gic_class_name(void); + #endif diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index ab5182a28a..4e2fb518e7 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -329,4 +329,14 @@ struct ARMGICv3CommonClass { void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, const MemoryRegionOps *ops); +/** + * gicv3_class_name + * + * Return name of GICv3 class to use depending on whether KVM acceleration is + * in use. May throw an error if the chosen implementation is not available. + * + * Returns: class name to use + */ +const char *gicv3_class_name(void); + #endif diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h index a11a0f6654..7dc712b38d 100644 --- a/include/hw/intc/arm_gicv3_its_common.h +++ b/include/hw/intc/arm_gicv3_its_common.h @@ -122,5 +122,14 @@ struct GICv3ITSCommonClass { void (*post_load)(GICv3ITSState *s); }; +/** + * its_class_name: + * + * Return the ITS class name to use depending on whether KVM acceleration + * and KVM CAP_SIGNAL_MSI are supported + * + * Returns: class name to use or NULL + */ +const char *its_class_name(void); #endif diff --git a/include/hw/intc/armv7m_nvic.h b/include/hw/intc/armv7m_nvic.h index 0180c7b0ca..89fe8aedaa 100644 --- a/include/hw/intc/armv7m_nvic.h +++ b/include/hw/intc/armv7m_nvic.h @@ -10,16 +10,13 @@ #ifndef HW_ARM_ARMV7M_NVIC_H #define HW_ARM_ARMV7M_NVIC_H -#include "target/arm/cpu.h" +#include "target/arm/cpu-qom.h" #include "hw/sysbus.h" #include "hw/timer/armv7m_systick.h" #include "qom/object.h" #define TYPE_NVIC "armv7m_nvic" - -typedef struct NVICState NVICState; -DECLARE_INSTANCE_CHECKER(NVICState, NVIC, - TYPE_NVIC) +OBJECT_DECLARE_SIMPLE_TYPE(NVICState, NVIC) /* Highest permitted number of exceptions (architectural limit) */ #define NVIC_MAX_VECTORS 512 @@ -77,7 +74,7 @@ struct NVICState { */ bool vectpending_is_s_banked; int exception_prio; /* group prio of the highest prio active exception */ - int vectpending_prio; /* group prio of the exeception in vectpending */ + int vectpending_prio; /* group prio of the exception in vectpending */ MemoryRegion sysregmem; @@ -86,4 +83,127 @@ struct NVICState { qemu_irq sysresetreq; }; +/* Interface between CPU and Interrupt controller. */ +/** + * armv7m_nvic_set_pending: mark the specified exception as pending + * @s: the NVIC + * @irq: the exception number to mark pending + * @secure: false for non-banked exceptions or for the nonsecure + * version of a banked exception, true for the secure version of a banked + * exception. + * + * Marks the specified exception as pending. Note that we will assert() + * if @secure is true and @irq does not specify one of the fixed set + * of architecturally banked exceptions. + */ +void armv7m_nvic_set_pending(NVICState *s, int irq, bool secure); +/** + * armv7m_nvic_set_pending_derived: mark this derived exception as pending + * @s: the NVIC + * @irq: the exception number to mark pending + * @secure: false for non-banked exceptions or for the nonsecure + * version of a banked exception, true for the secure version of a banked + * exception. + * + * Similar to armv7m_nvic_set_pending(), but specifically for derived + * exceptions (exceptions generated in the course of trying to take + * a different exception). + */ +void armv7m_nvic_set_pending_derived(NVICState *s, int irq, bool secure); +/** + * armv7m_nvic_set_pending_lazyfp: mark this lazy FP exception as pending + * @s: the NVIC + * @irq: the exception number to mark pending + * @secure: false for non-banked exceptions or for the nonsecure + * version of a banked exception, true for the secure version of a banked + * exception. + * + * Similar to armv7m_nvic_set_pending(), but specifically for exceptions + * generated in the course of lazy stacking of FP registers. + */ +void armv7m_nvic_set_pending_lazyfp(NVICState *s, int irq, bool secure); +/** + * armv7m_nvic_get_pending_irq_info: return highest priority pending + * exception, and whether it targets Secure state + * @s: the NVIC + * @pirq: set to pending exception number + * @ptargets_secure: set to whether pending exception targets Secure + * + * This function writes the number of the highest priority pending + * exception (the one which would be made active by + * armv7m_nvic_acknowledge_irq()) to @pirq, and sets @ptargets_secure + * to true if the current highest priority pending exception should + * be taken to Secure state, false for NS. + */ +void armv7m_nvic_get_pending_irq_info(NVICState *s, int *pirq, + bool *ptargets_secure); +/** + * armv7m_nvic_acknowledge_irq: make highest priority pending exception active + * @s: the NVIC + * + * Move the current highest priority pending exception from the pending + * state to the active state, and update v7m.exception to indicate that + * it is the exception currently being handled. + */ +void armv7m_nvic_acknowledge_irq(NVICState *s); +/** + * armv7m_nvic_complete_irq: complete specified interrupt or exception + * @s: the NVIC + * @irq: the exception number to complete + * @secure: true if this exception was secure + * + * Returns: -1 if the irq was not active + * 1 if completing this irq brought us back to base (no active irqs) + * 0 if there is still an irq active after this one was completed + * (Ignoring -1, this is the same as the RETTOBASE value before completion.) + */ +int armv7m_nvic_complete_irq(NVICState *s, int irq, bool secure); +/** + * armv7m_nvic_get_ready_status(void *opaque, int irq, bool secure) + * @s: the NVIC + * @irq: the exception number to mark pending + * @secure: false for non-banked exceptions or for the nonsecure + * version of a banked exception, true for the secure version of a banked + * exception. + * + * Return whether an exception is "ready", i.e. whether the exception is + * enabled and is configured at a priority which would allow it to + * interrupt the current execution priority. This controls whether the + * RDY bit for it in the FPCCR is set. + */ +bool armv7m_nvic_get_ready_status(NVICState *s, int irq, bool secure); +/** + * armv7m_nvic_raw_execution_priority: return the raw execution priority + * @s: the NVIC + * + * Returns: the raw execution priority as defined by the v8M architecture. + * This is the execution priority minus the effects of AIRCR.PRIS, + * and minus any PRIMASK/FAULTMASK/BASEPRI priority boosting. + * (v8M ARM ARM I_PKLD.) + */ +int armv7m_nvic_raw_execution_priority(NVICState *s); +/** + * armv7m_nvic_neg_prio_requested: return true if the requested execution + * priority is negative for the specified security state. + * @s: the NVIC + * @secure: the security state to test + * This corresponds to the pseudocode IsReqExecPriNeg(). + */ +#ifndef CONFIG_USER_ONLY +bool armv7m_nvic_neg_prio_requested(NVICState *s, bool secure); +#else +static inline bool armv7m_nvic_neg_prio_requested(NVICState *s, bool secure) +{ + return false; +} +#endif +#ifndef CONFIG_USER_ONLY +bool armv7m_nvic_can_take_pending_exception(NVICState *s); +#else +static inline bool armv7m_nvic_can_take_pending_exception(NVICState *s) +{ + return true; +} +#endif + #endif diff --git a/include/hw/intc/goldfish_pic.h b/include/hw/intc/goldfish_pic.h index e9d552f796..3e79580367 100644 --- a/include/hw/intc/goldfish_pic.h +++ b/include/hw/intc/goldfish_pic.h @@ -10,6 +10,8 @@ #ifndef HW_INTC_GOLDFISH_PIC_H #define HW_INTC_GOLDFISH_PIC_H +#include "hw/sysbus.h" + #define TYPE_GOLDFISH_PIC "goldfish_pic" OBJECT_DECLARE_SIMPLE_TYPE(GoldfishPICState, GOLDFISH_PIC) diff --git a/include/hw/sparc/grlib.h b/include/hw/intc/grlib_irqmp.h similarity index 83% rename from include/hw/sparc/grlib.h rename to include/hw/intc/grlib_irqmp.h index ef1946c7f8..a76acbf940 100644 --- a/include/hw/sparc/grlib.h +++ b/include/hw/intc/grlib_irqmp.h @@ -1,7 +1,9 @@ /* * QEMU GRLIB Components * - * Copyright (c) 2010-2019 AdaCore + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2010-2024 AdaCore * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,8 +24,8 @@ * THE SOFTWARE. */ -#ifndef GRLIB_H -#define GRLIB_H +#ifndef GRLIB_IRQMP_H +#define GRLIB_IRQMP_H #include "hw/sysbus.h" @@ -34,12 +36,6 @@ /* IRQMP */ #define TYPE_GRLIB_IRQMP "grlib-irqmp" -void grlib_irqmp_ack(DeviceState *dev, int intno); +void grlib_irqmp_ack(DeviceState *dev, unsigned int cpu, int intno); -/* GPTimer */ -#define TYPE_GRLIB_GPTIMER "grlib-gptimer" - -/* APB UART */ -#define TYPE_GRLIB_APB_UART "grlib-apbuart" - -#endif /* GRLIB_H */ +#endif /* GRLIB_IRQMP_H */ diff --git a/include/hw/intc/i8259.h b/include/hw/intc/i8259.h index e2b1e8c59a..c412575775 100644 --- a/include/hw/intc/i8259.h +++ b/include/hw/intc/i8259.h @@ -3,10 +3,18 @@ /* i8259.c */ -extern DeviceState *isa_pic; -qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq); +extern PICCommonState *isa_pic; + +/* + * i8259_init() + * + * Create a i8259 device on an ISA @bus, + * connect its output to @parent_irq_in, + * return an (allocated) array of 16 input IRQs. + */ +qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq_in); qemu_irq *kvm_i8259_init(ISABus *bus); -int pic_get_output(DeviceState *d); -int pic_read_irq(DeviceState *d); +int pic_get_output(PICCommonState *s); +int pic_read_irq(PICCommonState *s); #endif diff --git a/include/hw/i386/ioapic.h b/include/hw/intc/ioapic.h similarity index 93% rename from include/hw/i386/ioapic.h rename to include/hw/intc/ioapic.h index ef37b8a9fd..aa122e25e3 100644 --- a/include/hw/i386/ioapic.h +++ b/include/hw/intc/ioapic.h @@ -17,8 +17,8 @@ * License along with this library; if not, see . */ -#ifndef HW_IOAPIC_H -#define HW_IOAPIC_H +#ifndef HW_INTC_IOAPIC_H +#define HW_INTC_IOAPIC_H #define IOAPIC_NUM_PINS 24 #define IO_APIC_DEFAULT_ADDRESS 0xfec00000 @@ -30,4 +30,4 @@ void ioapic_eoi_broadcast(int vector); -#endif /* HW_IOAPIC_H */ +#endif /* HW_INTC_IOAPIC_H */ diff --git a/include/hw/intc/kvm_irqcount.h b/include/hw/intc/kvm_irqcount.h new file mode 100644 index 0000000000..0ed5999e49 --- /dev/null +++ b/include/hw/intc/kvm_irqcount.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef KVM_IRQCOUNT_H +#define KVM_IRQCOUNT_H + +void kvm_report_irq_delivered(int delivered); +void kvm_reset_irq_delivered(void); +int kvm_get_irq_delivered(void); + +#endif diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index 15b8c999f6..a0a46b888c 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -14,6 +14,8 @@ #define LS3A_INTC_IP 8 #define EXTIOI_IRQS (256) #define EXTIOI_IRQS_BITMAP_SIZE (256 / 8) +/* irq from EXTIOI is routed to no more than 4 cpus */ +#define EXTIOI_CPUS (4) /* map to ipnum per 32 irqs */ #define EXTIOI_IRQS_IPMAP_SIZE (256 / 32) #define EXTIOI_IRQS_COREMAP_SIZE 256 @@ -38,25 +40,29 @@ #define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET) #define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET) +typedef struct ExtIOICore { + uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT]; + DECLARE_BITMAP(sw_isr[LS3A_INTC_IP], EXTIOI_IRQS); + qemu_irq parent_irq[LS3A_INTC_IP]; +} ExtIOICore; + #define TYPE_LOONGARCH_EXTIOI "loongarch.extioi" OBJECT_DECLARE_SIMPLE_TYPE(LoongArchExtIOI, LOONGARCH_EXTIOI) struct LoongArchExtIOI { SysBusDevice parent_obj; + uint32_t num_cpu; /* hardware state */ uint32_t nodetype[EXTIOI_IRQS_NODETYPE_COUNT / 2]; uint32_t bounce[EXTIOI_IRQS_GROUP_COUNT]; uint32_t isr[EXTIOI_IRQS / 32]; - uint32_t coreisr[LOONGARCH_MAX_VCPUS][EXTIOI_IRQS_GROUP_COUNT]; uint32_t enable[EXTIOI_IRQS / 32]; uint32_t ipmap[EXTIOI_IRQS_IPMAP_SIZE / 4]; uint32_t coremap[EXTIOI_IRQS / 4]; uint32_t sw_pending[EXTIOI_IRQS / 32]; - DECLARE_BITMAP(sw_isr[LOONGARCH_MAX_VCPUS][LS3A_INTC_IP], EXTIOI_IRQS); uint8_t sw_ipmap[EXTIOI_IRQS_IPMAP_SIZE]; uint8_t sw_coremap[EXTIOI_IRQS]; - qemu_irq parent_irq[LOONGARCH_MAX_VCPUS][LS3A_INTC_IP]; qemu_irq irq[EXTIOI_IRQS]; - MemoryRegion extioi_iocsr_mem[LOONGARCH_MAX_VCPUS]; + ExtIOICore *cpu; MemoryRegion extioi_system_mem; }; #endif /* LOONGARCH_EXTIOI_H */ diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongarch_ipi.h index 996ed7ea93..1c1e834849 100644 --- a/include/hw/intc/loongarch_ipi.h +++ b/include/hw/intc/loongarch_ipi.h @@ -24,11 +24,11 @@ #define IOCSR_MAIL_SEND 0x48 #define IOCSR_ANY_SEND 0x158 -/* IPI system memory address */ -#define IPI_SYSTEM_MEM 0x1fe01000 +#define MAIL_SEND_ADDR (SMP_IPI_MAILBOX + IOCSR_MAIL_SEND) +#define MAIL_SEND_OFFSET 0 +#define ANY_SEND_OFFSET (IOCSR_ANY_SEND - IOCSR_MAIL_SEND) -#define MAX_IPI_CORE_NUM 4 -#define MAX_IPI_MBX_NUM 4 +#define IPI_MBX_NUM 4 #define TYPE_LOONGARCH_IPI "loongarch_ipi" OBJECT_DECLARE_SIMPLE_TYPE(LoongArchIPI, LOONGARCH_IPI) @@ -39,14 +39,16 @@ typedef struct IPICore { uint32_t set; uint32_t clear; /* 64bit buf divide into 2 32bit buf */ - uint32_t buf[MAX_IPI_MBX_NUM * 2]; + uint32_t buf[IPI_MBX_NUM * 2]; qemu_irq irq; } IPICore; struct LoongArchIPI { SysBusDevice parent_obj; - MemoryRegion ipi_iocsr_mem[MAX_IPI_CORE_NUM]; - MemoryRegion ipi_system_mem[MAX_IPI_CORE_NUM]; + MemoryRegion ipi_iocsr_mem; + MemoryRegion ipi64_iocsr_mem; + uint32_t num_cpu; + IPICore *cpu; }; #endif diff --git a/include/hw/intc/loongarch_pch_msi.h b/include/hw/intc/loongarch_pch_msi.h index f668bfca7a..b8586fb3b6 100644 --- a/include/hw/intc/loongarch_pch_msi.h +++ b/include/hw/intc/loongarch_pch_msi.h @@ -5,16 +5,21 @@ * Copyright (C) 2021 Loongson Technology Corporation Limited */ +#include "hw/sysbus.h" + #define TYPE_LOONGARCH_PCH_MSI "loongarch_pch_msi" OBJECT_DECLARE_SIMPLE_TYPE(LoongArchPCHMSI, LOONGARCH_PCH_MSI) -/* Msi irq start start from 64 to 255 */ -#define PCH_MSI_IRQ_START 64 +/* MSI irq start from 32 to 255 */ +#define PCH_MSI_IRQ_START 32 #define PCH_MSI_IRQ_END 255 -#define PCH_MSI_IRQ_NUM 192 +#define PCH_MSI_IRQ_NUM 224 struct LoongArchPCHMSI { SysBusDevice parent_obj; - qemu_irq pch_msi_irq[PCH_MSI_IRQ_NUM]; + qemu_irq *pch_msi_irq; MemoryRegion msi_mmio; + /* irq base passed to upper extioi intc */ + unsigned int irq_base; + unsigned int irq_num; }; diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h index 2d4aa9ed6f..d5437e88f2 100644 --- a/include/hw/intc/loongarch_pch_pic.h +++ b/include/hw/intc/loongarch_pch_pic.h @@ -5,15 +5,14 @@ * Copyright (c) 2021 Loongson Technology Corporation Limited */ +#include "hw/sysbus.h" + #define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic" #define PCH_PIC_NAME(name) TYPE_LOONGARCH_PCH_PIC#name OBJECT_DECLARE_SIMPLE_TYPE(LoongArchPCHPIC, LOONGARCH_PCH_PIC) -#define PCH_PIC_IRQ_START 0 -#define PCH_PIC_IRQ_END 63 -#define PCH_PIC_IRQ_NUM 64 #define PCH_PIC_INT_ID_VAL 0x7000000UL -#define PCH_PIC_INT_ID_NUM 0x3f0001UL +#define PCH_PIC_INT_ID_VER 0x1UL #define PCH_PIC_INT_ID_LO 0x00 #define PCH_PIC_INT_ID_HI 0x04 @@ -66,4 +65,5 @@ struct LoongArchPCHPIC { MemoryRegion iomem32_low; MemoryRegion iomem32_high; MemoryRegion iomem8; + unsigned int irq_num; }; diff --git a/include/hw/intc/m68k_irqc.h b/include/hw/intc/m68k_irqc.h index ef91f21812..693e33b0aa 100644 --- a/include/hw/intc/m68k_irqc.h +++ b/include/hw/intc/m68k_irqc.h @@ -33,6 +33,7 @@ typedef struct M68KIRQCState { SysBusDevice parent_obj; uint8_t ipr; + ArchCPU *cpu; /* statistics */ uint64_t stats_irq_count[M68K_IRQC_LEVEL_NUM]; diff --git a/include/hw/intc/mips_gic.h b/include/hw/intc/mips_gic.h index eeb136e261..5e4c71edd4 100644 --- a/include/hw/intc/mips_gic.h +++ b/include/hw/intc/mips_gic.h @@ -211,8 +211,8 @@ struct MIPSGICState { /* GIC VP Timer */ MIPSGICTimerState *gic_timer; - int32_t num_vps; - int32_t num_irq; + uint32_t num_vps; + uint32_t num_irq; }; #endif /* MIPS_GIC_H */ diff --git a/include/hw/intc/nios2_vic.h b/include/hw/intc/nios2_vic.h index ac507b9d74..5c975a2ac4 100644 --- a/include/hw/intc/nios2_vic.h +++ b/include/hw/intc/nios2_vic.h @@ -35,6 +35,8 @@ #ifndef HW_INTC_NIOS2_VIC_H #define HW_INTC_NIOS2_VIC_H +#include "hw/sysbus.h" + #define TYPE_NIOS2_VIC "nios2-vic" OBJECT_DECLARE_SIMPLE_TYPE(Nios2VIC, NIOS2_VIC) diff --git a/include/hw/intc/ppc-uic.h b/include/hw/intc/ppc-uic.h index 22dd5e5ac2..4d82e9a3c6 100644 --- a/include/hw/intc/ppc-uic.h +++ b/include/hw/intc/ppc-uic.h @@ -25,8 +25,7 @@ #ifndef HW_INTC_PPC_UIC_H #define HW_INTC_PPC_UIC_H -#include "hw/sysbus.h" -#include "qom/object.h" +#include "hw/ppc/ppc4xx.h" #define TYPE_PPC_UIC "ppc-uic" OBJECT_DECLARE_SIMPLE_TYPE(PPCUIC, PPC_UIC) @@ -56,14 +55,13 @@ enum { struct PPCUIC { /*< private >*/ - SysBusDevice parent_obj; + Ppc4xxDcrDeviceState parent_obj; /*< public >*/ qemu_irq output_int; qemu_irq output_cint; /* properties */ - CPUState *cpu; uint32_t dcr_base; bool use_vectors; diff --git a/include/hw/intc/riscv_aclint.h b/include/hw/intc/riscv_aclint.h index 26d4048687..693415eb6d 100644 --- a/include/hw/intc/riscv_aclint.h +++ b/include/hw/intc/riscv_aclint.h @@ -32,6 +32,8 @@ typedef struct RISCVAclintMTimerState { /*< private >*/ SysBusDevice parent_obj; uint64_t time_delta; + uint64_t *timecmp; + QEMUTimer **timers; /*< public >*/ MemoryRegion mmio; diff --git a/include/hw/intc/sifive_plic.h b/include/hw/intc/sifive_plic.h index 134cf39a96..d3f45ec248 100644 --- a/include/hw/intc/sifive_plic.h +++ b/include/hw/intc/sifive_plic.h @@ -33,7 +33,6 @@ DECLARE_INSTANCE_CHECKER(SiFivePLICState, SIFIVE_PLIC, typedef enum PLICMode { PLICMode_U, PLICMode_S, - PLICMode_H, PLICMode_M } PLICMode; diff --git a/include/hw/isa/i8259_internal.h b/include/hw/isa/i8259_internal.h index d272d879fb..f9dcc4163e 100644 --- a/include/hw/isa/i8259_internal.h +++ b/include/hw/isa/i8259_internal.h @@ -35,7 +35,7 @@ OBJECT_DECLARE_TYPE(PICCommonState, PICCommonClass, PIC_COMMON) struct PICCommonClass { - ISADeviceClass parent_class; + DeviceClass parent_class; void (*pre_save)(PICCommonState *s); void (*post_load)(PICCommonState *s); @@ -61,6 +61,7 @@ struct PICCommonState { uint8_t single_mode; /* true if slave pic is not initialized */ uint8_t elcr; /* PIIX edge/trigger selection*/ uint8_t elcr_mask; + uint8_t ltim; /* Edge/Level Bank Select (pre-PIIX, chip-wide) */ qemu_irq int_out[1]; uint32_t master; /* reflects /SP input pin */ uint32_t iobase; diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index 034d706ba1..40d6224a4e 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -11,25 +11,11 @@ #define ISA_NUM_IRQS 16 #define TYPE_ISA_DEVICE "isa-device" -OBJECT_DECLARE_TYPE(ISADevice, ISADeviceClass, ISA_DEVICE) +OBJECT_DECLARE_SIMPLE_TYPE(ISADevice, ISA_DEVICE) #define TYPE_ISA_BUS "ISA" OBJECT_DECLARE_SIMPLE_TYPE(ISABus, ISA_BUS) -#define TYPE_APPLE_SMC "isa-applesmc" -#define APPLESMC_MAX_DATA_LENGTH 32 -#define APPLESMC_PROP_IO_BASE "iobase" - -static inline uint16_t applesmc_port(void) -{ - Object *obj = object_resolve_path_type("", TYPE_APPLE_SMC, NULL); - - if (obj) { - return object_property_get_uint(obj, APPLESMC_PROP_IO_BASE, NULL); - } - return 0; -} - #define TYPE_ISADMA "isa-dma" typedef struct IsaDmaClass IsaDmaClass; @@ -62,11 +48,6 @@ struct IsaDmaClass { void *opaque); }; -struct ISADeviceClass { - DeviceClass parent_class; - void (*build_aml)(ISADevice *dev, Aml *scope); -}; - struct ISABus { /*< private >*/ BusState parent_obj; @@ -74,7 +55,7 @@ struct ISABus { MemoryRegion *address_space; MemoryRegion *address_space_io; - qemu_irq *irqs; + qemu_irq *irqs_in; IsaDma *dma[2]; }; @@ -88,20 +69,29 @@ 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, unsigned isairq); -void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, unsigned isairq); +void isa_bus_register_input_irqs(ISABus *bus, qemu_irq *irqs_in); void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16); -IsaDma *isa_get_dma(ISABus *bus, int nchan); -MemoryRegion *isa_address_space(ISADevice *dev); -MemoryRegion *isa_address_space_io(ISADevice *dev); +IsaDma *isa_bus_get_dma(ISABus *bus, int nchan); +/** + * isa_bus_get_irq: Return input IRQ on ISA bus. + * @bus: the #ISABus to plug ISA devices on. + * @irqnum: the ISA IRQ number. + * + * Return IRQ @irqnum from the PIC associated on ISA @bus. + */ +qemu_irq isa_bus_get_irq(ISABus *bus, unsigned irqnum); ISADevice *isa_new(const char *name); ISADevice *isa_try_new(const char *name); bool isa_realize_and_unref(ISADevice *dev, ISABus *bus, Error **errp); ISADevice *isa_create_simple(ISABus *bus, const char *name); ISADevice *isa_vga_init(ISABus *bus); -void isa_build_aml(ISABus *bus, Aml *scope); + +qemu_irq isa_get_irq(ISADevice *dev, unsigned isairq); +void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, unsigned isairq); +MemoryRegion *isa_address_space(ISADevice *dev); +MemoryRegion *isa_address_space_io(ISADevice *dev); +ISABus *isa_bus_from_device(ISADevice *dev); /** * isa_register_ioport: Install an I/O port region on the ISA bus. @@ -139,11 +129,4 @@ int isa_register_portio_list(ISADevice *dev, const MemoryRegionPortio *portio, void *opaque, const char *name); -static inline ISABus *isa_bus_from_device(ISADevice *d) -{ - return ISA_BUS(qdev_get_parent_bus(DEVICE(d))); -} - -#define TYPE_PIIX4_PCI_DEVICE "piix4-isa" - #endif diff --git a/include/hw/isa/superio.h b/include/hw/isa/superio.h index b9f5c19155..0dc45104d4 100644 --- a/include/hw/isa/superio.h +++ b/include/hw/isa/superio.h @@ -44,7 +44,7 @@ typedef struct ISASuperIOFuncs { struct ISASuperIOClass { /*< private >*/ - ISADeviceClass parent_class; + DeviceClass parent_class; /*< public >*/ DeviceRealize parent_realize; diff --git a/include/hw/isa/vt82c686.h b/include/hw/isa/vt82c686.h index 56ac141be3..da1722daf2 100644 --- a/include/hw/isa/vt82c686.h +++ b/include/hw/isa/vt82c686.h @@ -1,15 +1,39 @@ #ifndef HW_VT82C686_H #define HW_VT82C686_H -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" +#include "audio/audio.h" #define TYPE_VT82C686B_ISA "vt82c686b-isa" -#define TYPE_VT82C686B_PM "vt82c686b-pm" +#define TYPE_VT82C686B_USB_UHCI "vt82c686b-usb-uhci" #define TYPE_VT8231_ISA "vt8231-isa" -#define TYPE_VT8231_PM "vt8231-pm" #define TYPE_VIA_AC97 "via-ac97" +#define TYPE_VIA_IDE "via-ide" #define TYPE_VIA_MC97 "via-mc97" +typedef struct { + uint8_t stat; + uint8_t type; + uint32_t base; + uint32_t curr; + uint32_t addr; + uint32_t clen; +} ViaAC97SGDChannel; + +OBJECT_DECLARE_SIMPLE_TYPE(ViaAC97State, VIA_AC97); + +struct ViaAC97State { + PCIDevice dev; + QEMUSoundCard card; + MemoryRegion sgd; + MemoryRegion fm; + MemoryRegion midi; + SWVoiceOut *vo; + ViaAC97SGDChannel aur; + uint16_t codec_regs[128]; + uint32_t ac97_cmd; +}; + void via_isa_set_irq(PCIDevice *d, int n, int level); #endif diff --git a/include/hw/loader.h b/include/hw/loader.h index 5572108ba5..8685e27334 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -40,8 +40,8 @@ ssize_t load_image_size(const char *filename, void *addr, size_t size); * * Returns the size of the loaded image on success, -1 otherwise. */ -int load_image_targphys_as(const char *filename, - hwaddr addr, uint64_t max_sz, AddressSpace *as); +ssize_t load_image_targphys_as(const char *filename, + hwaddr addr, uint64_t max_sz, AddressSpace *as); /**load_targphys_hex_as: * @filename: Path to the .hex file @@ -53,14 +53,15 @@ int load_image_targphys_as(const char *filename, * * Returns the size of the loaded .hex file on success, -1 otherwise. */ -int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as); +ssize_t load_targphys_hex_as(const char *filename, hwaddr *entry, + AddressSpace *as); /** load_image_targphys: * Same as load_image_targphys_as(), but doesn't allow the caller to specify * an AddressSpace. */ -int load_image_targphys(const char *filename, hwaddr, - uint64_t max_sz); +ssize_t load_image_targphys(const char *filename, hwaddr, + uint64_t max_sz); /** * load_image_mr: load an image into a memory region @@ -73,7 +74,7 @@ int load_image_targphys(const char *filename, hwaddr, * If the file is larger than the memory region's size the call will fail. * Returns -1 on failure, or the size of the file. */ -int load_image_mr(const char *filename, MemoryRegion *mr); +ssize_t load_image_mr(const char *filename, MemoryRegion *mr); /* This is the limit on the maximum uncompressed image size that * load_image_gzipped_buffer() and load_image_gzipped() will read. It prevents @@ -81,9 +82,28 @@ int load_image_mr(const char *filename, MemoryRegion *mr); */ #define LOAD_IMAGE_MAX_GUNZIP_BYTES (256 << 20) -int load_image_gzipped_buffer(const char *filename, uint64_t max_sz, - uint8_t **buffer); -int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz); +ssize_t load_image_gzipped_buffer(const char *filename, uint64_t max_sz, + uint8_t **buffer); +ssize_t load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz); + +/** + * unpack_efi_zboot_image: + * @buffer: pointer to a variable holding the address of a buffer containing the + * image + * @size: pointer to a variable holding the size of the buffer + * + * Check whether the buffer contains a EFI zboot image, and if it does, extract + * the compressed payload and decompress it into a new buffer. If successful, + * the old buffer is freed, and the *buffer and size variables pointed to by the + * function arguments are updated to refer to the newly populated buffer. + * + * Returns 0 if the image could not be identified as a EFI zboot image. + * Returns -1 if the buffer contents were identified as a EFI zboot image, but + * unpacking failed for any reason. + * Returns the size of the decompressed payload if decompression was performed + * successfully. + */ +ssize_t unpack_efi_zboot_image(uint8_t **buffer, int *size); #define ELF_LOAD_FAILED -1 #define ELF_LOAD_NOT_ELF -2 @@ -183,8 +203,8 @@ ssize_t load_elf(const char *filename, */ void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp); -int load_aout(const char *filename, hwaddr addr, int max_sz, - int bswap_needed, hwaddr target_page_size); +ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, + int bswap_needed, hwaddr target_page_size); #define LOAD_UIMAGE_LOADADDR_INVALID (-1) @@ -205,19 +225,19 @@ int load_aout(const char *filename, hwaddr addr, int max_sz, * * Returns the size of the loaded image on success, -1 otherwise. */ -int load_uimage_as(const char *filename, hwaddr *ep, - hwaddr *loadaddr, int *is_linux, - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, AddressSpace *as); +ssize_t load_uimage_as(const char *filename, hwaddr *ep, + hwaddr *loadaddr, int *is_linux, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, AddressSpace *as); /** load_uimage: * Same as load_uimage_as(), but doesn't allow the caller to specify an * AddressSpace. */ -int load_uimage(const char *filename, hwaddr *ep, - hwaddr *loadaddr, int *is_linux, - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque); +ssize_t load_uimage(const char *filename, hwaddr *ep, + hwaddr *loadaddr, int *is_linux, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque); /** * load_ramdisk_as: @@ -232,15 +252,15 @@ int load_uimage(const char *filename, hwaddr *ep, * * Returns the size of the loaded image on success, -1 otherwise. */ -int load_ramdisk_as(const char *filename, hwaddr addr, uint64_t max_sz, - AddressSpace *as); +ssize_t load_ramdisk_as(const char *filename, hwaddr addr, uint64_t max_sz, + AddressSpace *as); /** * load_ramdisk: * Same as load_ramdisk_as(), but doesn't allow the caller to specify * an AddressSpace. */ -int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz); +ssize_t load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz); ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen); @@ -250,12 +270,9 @@ void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size, const char *source); -extern bool option_rom_has_mr; -extern bool rom_file_has_mr; - -int rom_add_file(const char *file, const char *fw_dir, - hwaddr addr, int32_t bootindex, - bool option_rom, MemoryRegion *mr, AddressSpace *as); +ssize_t rom_add_file(const char *file, const char *fw_dir, + hwaddr addr, int32_t bootindex, + bool has_option_rom, MemoryRegion *mr, AddressSpace *as); MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, size_t max_len, hwaddr addr, const char *fw_file_name, @@ -336,8 +353,8 @@ void hmp_info_roms(Monitor *mon, const QDict *qdict); #define rom_add_blob_fixed_as(_f, _b, _l, _a, _as) \ rom_add_blob(_f, _b, _l, _l, _a, NULL, NULL, NULL, _as, true) -int rom_add_vga(const char *file); -int rom_add_option(const char *file, int32_t bootindex); +ssize_t rom_add_vga(const char *file); +ssize_t rom_add_option(const char *file, int32_t bootindex); /* This is the usual maximum in uboot, so if a uImage overflows this, it would * overflow on real hardware too. */ diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 09a816191c..252f7df7f4 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -12,22 +12,53 @@ #include "hw/boards.h" #include "qemu/queue.h" #include "hw/intc/loongarch_ipi.h" +#include "hw/block/flash.h" -#define LOONGARCH_MAX_VCPUS 4 +#define LOONGARCH_MAX_CPUS 256 -#define LOONGARCH_ISA_IO_BASE 0x18000000UL -#define LOONGARCH_ISA_IO_SIZE 0x0004000 +#define VIRT_FWCFG_BASE 0x1e020000UL +#define VIRT_BIOS_BASE 0x1c000000UL +#define VIRT_BIOS_SIZE (16 * MiB) +#define VIRT_FLASH_SECTOR_SIZE (128 * KiB) +#define VIRT_FLASH0_BASE VIRT_BIOS_BASE +#define VIRT_FLASH0_SIZE VIRT_BIOS_SIZE +#define VIRT_FLASH1_BASE 0x1d000000UL +#define VIRT_FLASH1_SIZE (16 * MiB) + +#define VIRT_LOWMEM_BASE 0 +#define VIRT_LOWMEM_SIZE 0x10000000 +#define VIRT_HIGHMEM_BASE 0x80000000 +#define VIRT_GED_EVT_ADDR 0x100e0000 +#define VIRT_GED_MEM_ADDR (VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN) +#define VIRT_GED_REG_ADDR (VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN) struct LoongArchMachineState { /*< private >*/ MachineState parent_obj; - IPICore ipi_core[MAX_IPI_CORE_NUM]; MemoryRegion lowmem; MemoryRegion highmem; - MemoryRegion isa_io; + MemoryRegion bios; + bool bios_loaded; + /* State for other subsystems/APIs: */ + FWCfgState *fw_cfg; + Notifier machine_done; + Notifier powerdown_notifier; + OnOffAuto acpi; + char *oem_id; + char *oem_table_id; + DeviceState *acpi_ged; + int fdt_size; + DeviceState *platform_bus_dev; + PCIBus *pci_bus; + PFlashCFI01 *flash[2]; + MemoryRegion system_iocsr; + MemoryRegion iocsr_mem; + AddressSpace as_iocsr; }; #define TYPE_LOONGARCH_MACHINE MACHINE_TYPE_NAME("virt") OBJECT_DECLARE_SIMPLE_TYPE(LoongArchMachineState, LOONGARCH_MACHINE) +bool loongarch_is_acpi_enabled(LoongArchMachineState *lams); +void loongarch_acpi_setup(LoongArchMachineState *lams); #endif diff --git a/include/hw/m68k/mcf.h b/include/hw/m68k/mcf.h index 8cbd587bbf..5d9f876ffe 100644 --- a/include/hw/m68k/mcf.h +++ b/include/hw/m68k/mcf.h @@ -10,8 +10,8 @@ uint64_t mcf_uart_read(void *opaque, hwaddr addr, unsigned size); void mcf_uart_write(void *opaque, hwaddr addr, uint64_t val, unsigned size); -void *mcf_uart_init(qemu_irq irq, Chardev *chr); -void mcf_uart_mm_init(hwaddr base, qemu_irq irq, Chardev *chr); +DeviceState *mcf_uart_create(qemu_irq irq, Chardev *chr); +DeviceState *mcf_uart_create_mmap(hwaddr base, qemu_irq irq, Chardev *chr); /* mcf_intc.c */ qemu_irq *mcf_intc_init(struct MemoryRegion *sysmem, diff --git a/include/hw/m68k/q800-glue.h b/include/hw/m68k/q800-glue.h new file mode 100644 index 0000000000..04fac25f6c --- /dev/null +++ b/include/hw/m68k/q800-glue.h @@ -0,0 +1,51 @@ +/* + * QEMU q800 logic GLUE (General Logic Unit) + * + * 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_Q800_GLUE_H +#define HW_Q800_GLUE_H + +#include "hw/sysbus.h" + +#define TYPE_GLUE "q800-glue" +OBJECT_DECLARE_SIMPLE_TYPE(GLUEState, GLUE) + +struct GLUEState { + SysBusDevice parent_obj; + + M68kCPU *cpu; + uint8_t ipr; + uint8_t auxmode; + qemu_irq irqs[2]; + QEMUTimer *nmi_release; +}; + +#define GLUE_IRQ_IN_VIA1 0 +#define GLUE_IRQ_IN_VIA2 1 +#define GLUE_IRQ_IN_SONIC 2 +#define GLUE_IRQ_IN_ESCC 3 +#define GLUE_IRQ_IN_NMI 4 +#define GLUE_IRQ_IN_ASC 5 + +#define GLUE_IRQ_NUBUS_9 0 +#define GLUE_IRQ_ASC 1 + +#endif diff --git a/include/hw/m68k/q800.h b/include/hw/m68k/q800.h new file mode 100644 index 0000000000..34365c9860 --- /dev/null +++ b/include/hw/m68k/q800.h @@ -0,0 +1,78 @@ +/* + * QEMU Motorla 680x0 Macintosh hardware System Emulator + * + * 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_Q800_H +#define HW_Q800_H + +#include "hw/boards.h" +#include "qom/object.h" +#include "target/m68k/cpu-qom.h" +#include "exec/memory.h" +#include "hw/m68k/q800-glue.h" +#include "hw/misc/mac_via.h" +#include "hw/net/dp8393x.h" +#include "hw/char/escc.h" +#include "hw/or-irq.h" +#include "hw/scsi/esp.h" +#include "hw/block/swim.h" +#include "hw/nubus/mac-nubus-bridge.h" +#include "hw/display/macfb.h" +#include "hw/misc/djmemc.h" +#include "hw/misc/iosb.h" +#include "hw/audio/asc.h" + +/* + * The main Q800 machine + */ + +struct Q800MachineState { + MachineState parent_obj; + + bool easc; + M68kCPU cpu; + MemoryRegion rom; + MemoryRegion rom_alias; + GLUEState glue; + MOS6522Q800VIA1State via1; + MOS6522Q800VIA2State via2; + dp8393xState dp8393x; + MemoryRegion dp8393x_prom; + ESCCState escc; + OrIRQState escc_orgate; + SysBusESPState esp; + Swim swim; + MacNubusBridge mac_nubus_bridge; + MacfbNubusState macfb; + DJMEMCState djmemc; + IOSBState iosb; + ASCState asc; + MemoryRegion ramio; + MemoryRegion macio; + MemoryRegion macio_alias; + MemoryRegion machine_id; + MemoryRegion escc_alias; +}; + +#define TYPE_Q800_MACHINE MACHINE_TYPE_NAME("q800") +OBJECT_DECLARE_SIMPLE_TYPE(Q800MachineState, Q800_MACHINE) + +#endif diff --git a/include/hw/mem/memory-device.h b/include/hw/mem/memory-device.h index 48d2611fc5..e0571c8a31 100644 --- a/include/hw/mem/memory-device.h +++ b/include/hw/mem/memory-device.h @@ -37,10 +37,25 @@ typedef struct MemoryDeviceState MemoryDeviceState; * address in guest physical memory can either be specified explicitly * or get assigned automatically. * + * Some memory device might not own a memory region in certain device + * configurations. Such devices can logically get (un)plugged, however, + * empty memory devices are mostly ignored by the memory device code. + * * Conceptually, memory devices only span one memory region. If multiple * successive memory regions are used, a covering memory region has to * be provided. Scattered memory regions are not supported for single * devices. + * + * The device memory region returned via @get_memory_region may either be a + * single RAM memory region or a memory region container with subregions + * that are RAM memory regions or aliases to RAM memory regions. Other + * memory regions or subregions are not supported. + * + * If the device memory region returned via @get_memory_region is a + * memory region container, it's supported to dynamically (un)map subregions + * as long as the number of memslots returned by @get_memslots() won't + * be exceeded and as long as all memory regions are of the same kind (e.g., + * all RAM or all ROM). */ struct MemoryDeviceClass { /* private */ @@ -79,7 +94,8 @@ struct MemoryDeviceClass { uint64_t (*get_plugged_size)(const MemoryDeviceState *md, Error **errp); /* - * Return the memory region of the memory device. + * Return the memory region of the memory device. If the device is + * completely empty, returns NULL without an error. * * Called when (un)plugging the memory device, to (un)map the * memory region in guest physical memory, but also to detect the @@ -88,6 +104,28 @@ struct MemoryDeviceClass { */ MemoryRegion *(*get_memory_region)(MemoryDeviceState *md, Error **errp); + /* + * Optional: Instruct the memory device to decide how many memory slots + * it requires, not exceeding the given limit. + * + * Called exactly once when pre-plugging the memory device, before + * querying the number of memslots using @get_memslots the first time. + */ + void (*decide_memslots)(MemoryDeviceState *md, unsigned int limit); + + /* + * Optional for memory devices that require only a single memslot, + * required for all other memory devices: Return the number of memslots + * (distinct RAM memory regions in the device memory region) that are + * required by the device. + * + * If this function is not implemented, the assumption is "1". + * + * Called when (un)plugging the memory device, to check if the requirements + * can be satisfied, and to do proper accounting. + */ + unsigned int (*get_memslots)(MemoryDeviceState *md); + /* * Optional: Return the desired minimum alignment of the device in guest * physical address space. The final alignment is computed based on this @@ -105,8 +143,31 @@ struct MemoryDeviceClass { MemoryDeviceInfo *info); }; +/* + * Traditionally, KVM/vhost in many setups supported 509 memslots, whereby + * 253 memslots were "reserved" for boot memory and other devices (such + * as PCI BARs, which can get mapped dynamically) and 256 memslots were + * dedicated for DIMMs. These magic numbers worked reliably in the past. + * + * Further, using many memslots can negatively affect performance, so setting + * the soft-limit of memslots used by memory devices to the traditional + * DIMM limit of 256 sounds reasonable. + * + * If we have less than 509 memslots, we will instruct memory devices that + * support automatically deciding how many memslots to use to only use a single + * one. + * + * Hotplugging vhost devices with at least 509 memslots is not expected to + * cause problems, not even when devices automatically decided how many memslots + * to use. + */ +#define MEMORY_DEVICES_SOFT_MEMSLOT_LIMIT 256 +#define MEMORY_DEVICES_SAFE_MAX_MEMSLOTS 509 + MemoryDeviceInfoList *qmp_memory_device_list(void); uint64_t get_plugged_memory_size(void); +unsigned int memory_devices_get_reserved_memslots(void); +bool memory_devices_memslot_auto_decision_active(void); void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms, const uint64_t *legacy_align, Error **errp); void memory_device_plug(MemoryDeviceState *md, MachineState *ms); diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h index cf8f59be44..d3b763453a 100644 --- a/include/hw/mem/nvdimm.h +++ b/include/hw/mem/nvdimm.h @@ -29,14 +29,6 @@ #include "hw/acpi/aml-build.h" #include "qom/object.h" -#define NVDIMM_DEBUG 0 -#define nvdimm_debug(fmt, ...) \ - do { \ - if (NVDIMM_DEBUG) { \ - fprintf(stderr, "nvdimm: " fmt, ## __VA_ARGS__); \ - } \ - } while (0) - /* * The minimum label data size is required by NVDIMM Namespace * specification, see the chapter 2 Namespaces: @@ -85,6 +77,12 @@ struct NVDIMMDevice { */ bool unarmed; + /* + * Whether our DIMM is backed by ROM, and even label data cannot be + * written. If set, implies that "unarmed" is also set. + */ + bool readonly; + /* * The PPC64 - spapr requires each nvdimm device have a uuid. */ diff --git a/include/hw/mips/bios.h b/include/hw/mips/bios.h deleted file mode 100644 index 44acb6815b..0000000000 --- a/include/hw/mips/bios.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef HW_MIPS_BIOS_H -#define HW_MIPS_BIOS_H - -#include "qemu/units.h" -#include "cpu.h" - -#define BIOS_SIZE (4 * MiB) -#if TARGET_BIG_ENDIAN -#define BIOS_FILENAME "mips_bios.bin" -#else -#define BIOS_FILENAME "mipsel_bios.bin" -#endif - -#endif diff --git a/include/hw/mips/bootloader.h b/include/hw/mips/bootloader.h index b5f48d71bb..c32f6c2835 100644 --- a/include/hw/mips/bootloader.h +++ b/include/hw/mips/bootloader.h @@ -11,12 +11,16 @@ #include "exec/cpu-defs.h" -void bl_gen_jump_to(uint32_t **p, target_ulong jump_addr); -void bl_gen_jump_kernel(uint32_t **p, target_ulong sp, target_ulong a0, - target_ulong a1, target_ulong a2, target_ulong a3, +void bl_gen_jump_to(void **ptr, target_ulong jump_addr); +void bl_gen_jump_kernel(void **ptr, + bool set_sp, target_ulong sp, + bool set_a0, target_ulong a0, + bool set_a1, target_ulong a1, + bool set_a2, target_ulong a2, + bool set_a3, target_ulong a3, target_ulong kernel_addr); -void bl_gen_write_ulong(uint32_t **p, target_ulong addr, target_ulong val); -void bl_gen_write_u32(uint32_t **p, target_ulong addr, uint32_t val); -void bl_gen_write_u64(uint32_t **p, target_ulong addr, uint64_t val); +void bl_gen_write_ulong(void **ptr, target_ulong addr, target_ulong val); +void bl_gen_write_u32(void **ptr, target_ulong addr, uint32_t val); +void bl_gen_write_u64(void **ptr, target_ulong addr, uint64_t val); #endif diff --git a/include/hw/mips/cpudevs.h b/include/hw/mips/cpudevs.h deleted file mode 100644 index f7c9728fa9..0000000000 --- a/include/hw/mips/cpudevs.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef HW_MIPS_CPUDEVS_H -#define HW_MIPS_CPUDEVS_H - -#include "target/mips/cpu-qom.h" - -/* Definitions for MIPS CPU internal devices. */ - -/* mips_int.c */ -void cpu_mips_irq_init_cpu(MIPSCPU *cpu); - -/* mips_timer.c */ -void cpu_mips_clock_init(MIPSCPU *cpu); - -#endif diff --git a/include/hw/misc/allwinner-a10-ccm.h b/include/hw/misc/allwinner-a10-ccm.h new file mode 100644 index 0000000000..7f22532efa --- /dev/null +++ b/include/hw/misc/allwinner-a10-ccm.h @@ -0,0 +1,67 @@ +/* + * Allwinner A10 Clock Control Module emulation + * + * Copyright (C) 2022 Strahinja Jankovic + * + * This file is derived from Allwinner H3 CCU, + * by 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_A10_CCM_H +#define HW_MISC_ALLWINNER_A10_CCM_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +/** + * @name Constants + * @{ + */ + +/** Size of register I/O address space used by CCM device */ +#define AW_A10_CCM_IOSIZE (0x400) + +/** Total number of known registers */ +#define AW_A10_CCM_REGS_NUM (AW_A10_CCM_IOSIZE / sizeof(uint32_t)) + +/** @} */ + +/** + * @name Object model + * @{ + */ + +#define TYPE_AW_A10_CCM "allwinner-a10-ccm" +OBJECT_DECLARE_SIMPLE_TYPE(AwA10ClockCtlState, AW_A10_CCM) + +/** @} */ + +/** + * Allwinner A10 CCM object instance state. + */ +struct AwA10ClockCtlState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Array of hardware registers */ + uint32_t regs[AW_A10_CCM_REGS_NUM]; +}; + +#endif /* HW_MISC_ALLWINNER_H3_CCU_H */ diff --git a/include/hw/misc/allwinner-a10-dramc.h b/include/hw/misc/allwinner-a10-dramc.h new file mode 100644 index 0000000000..b61fbecbe7 --- /dev/null +++ b/include/hw/misc/allwinner-a10-dramc.h @@ -0,0 +1,68 @@ +/* + * Allwinner A10 DRAM Controller emulation + * + * Copyright (C) 2022 Strahinja Jankovic + * + * This file is derived from Allwinner H3 DRAMC, + * by 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_A10_DRAMC_H +#define HW_MISC_ALLWINNER_A10_DRAMC_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "hw/register.h" + +/** + * @name Constants + * @{ + */ + +/** Size of register I/O address space used by DRAMC device */ +#define AW_A10_DRAMC_IOSIZE (0x1000) + +/** Total number of known registers */ +#define AW_A10_DRAMC_REGS_NUM (AW_A10_DRAMC_IOSIZE / sizeof(uint32_t)) + +/** @} */ + +/** + * @name Object model + * @{ + */ + +#define TYPE_AW_A10_DRAMC "allwinner-a10-dramc" +OBJECT_DECLARE_SIMPLE_TYPE(AwA10DramControllerState, AW_A10_DRAMC) + +/** @} */ + +/** + * Allwinner A10 DRAMC object instance state. + */ +struct AwA10DramControllerState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Array of hardware registers */ + uint32_t regs[AW_A10_DRAMC_REGS_NUM]; +}; + +#endif /* HW_MISC_ALLWINNER_A10_DRAMC_H */ diff --git a/include/hw/misc/allwinner-r40-ccu.h b/include/hw/misc/allwinner-r40-ccu.h new file mode 100644 index 0000000000..ceb74eff92 --- /dev/null +++ b/include/hw/misc/allwinner-r40-ccu.h @@ -0,0 +1,65 @@ +/* + * Allwinner R40 Clock Control Unit emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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_R40_CCU_H +#define HW_MISC_ALLWINNER_R40_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_R40_CCU_IOSIZE (0x400) + +/** Total number of known registers */ +#define AW_R40_CCU_REGS_NUM (AW_R40_CCU_IOSIZE / sizeof(uint32_t)) + +/** @} */ + +/** + * @name Object model + * @{ + */ + +#define TYPE_AW_R40_CCU "allwinner-r40-ccu" +OBJECT_DECLARE_SIMPLE_TYPE(AwR40ClockCtlState, AW_R40_CCU) + +/** @} */ + +/** + * Allwinner R40 CCU object instance state. + */ +struct AwR40ClockCtlState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Array of hardware registers */ + uint32_t regs[AW_R40_CCU_REGS_NUM]; + +}; + +#endif /* HW_MISC_ALLWINNER_R40_CCU_H */ diff --git a/include/hw/misc/allwinner-r40-dramc.h b/include/hw/misc/allwinner-r40-dramc.h new file mode 100644 index 0000000000..6a1a3a7893 --- /dev/null +++ b/include/hw/misc/allwinner-r40-dramc.h @@ -0,0 +1,108 @@ +/* + * Allwinner R40 SDRAM Controller emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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_R40_DRAMC_H +#define HW_MISC_ALLWINNER_R40_DRAMC_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "exec/hwaddr.h" + +/** + * Constants + * @{ + */ + +/** Highest register address used by DRAMCOM module */ +#define AW_R40_DRAMCOM_REGS_MAXADDR (0x804) + +/** Total number of known DRAMCOM registers */ +#define AW_R40_DRAMCOM_REGS_NUM (AW_R40_DRAMCOM_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** Highest register address used by DRAMCTL module */ +#define AW_R40_DRAMCTL_REGS_MAXADDR (0x88c) + +/** Total number of known DRAMCTL registers */ +#define AW_R40_DRAMCTL_REGS_NUM (AW_R40_DRAMCTL_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** Highest register address used by DRAMPHY module */ +#define AW_R40_DRAMPHY_REGS_MAXADDR (0x4) + +/** Total number of known DRAMPHY registers */ +#define AW_R40_DRAMPHY_REGS_NUM (AW_R40_DRAMPHY_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** @} */ + +/** + * Object model + * @{ + */ + +#define TYPE_AW_R40_DRAMC "allwinner-r40-dramc" +OBJECT_DECLARE_SIMPLE_TYPE(AwR40DramCtlState, AW_R40_DRAMC) + +/** @} */ + +/** + * Allwinner R40 SDRAM Controller object instance state. + */ +struct AwR40DramCtlState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Physical base address for start of RAM */ + hwaddr ram_addr; + + /** Total RAM size in megabytes */ + uint32_t ram_size; + + uint8_t set_row_bits; + uint8_t set_bank_bits; + uint8_t set_col_bits; + + /** + * @name Memory Regions + * @{ + */ + MemoryRegion dramcom_iomem; /**< DRAMCOM module I/O registers */ + MemoryRegion dramctl_iomem; /**< DRAMCTL module I/O registers */ + MemoryRegion dramphy_iomem; /**< DRAMPHY module I/O registers */ + MemoryRegion dram_high; /**< The high 1G dram for dualrank detect */ + MemoryRegion detect_cells; /**< DRAM memory cells for auto detect */ + + /** @} */ + + /** + * @name Hardware Registers + * @{ + */ + + uint32_t dramcom[AW_R40_DRAMCOM_REGS_NUM]; /**< DRAMCOM registers */ + uint32_t dramctl[AW_R40_DRAMCTL_REGS_NUM]; /**< DRAMCTL registers */ + uint32_t dramphy[AW_R40_DRAMPHY_REGS_NUM] ;/**< DRAMPHY registers */ + + /** @} */ + +}; + +#endif /* HW_MISC_ALLWINNER_R40_DRAMC_H */ diff --git a/include/hw/misc/allwinner-sramc.h b/include/hw/misc/allwinner-sramc.h new file mode 100644 index 0000000000..66b01b8d04 --- /dev/null +++ b/include/hw/misc/allwinner-sramc.h @@ -0,0 +1,69 @@ +/* + * Allwinner SRAM controller emulation + * + * Copyright (C) 2023 qianfan Zhao + * + * 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_SRAMC_H +#define HW_MISC_ALLWINNER_SRAMC_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "qemu/uuid.h" + +/** + * Object model + * @{ + */ +#define TYPE_AW_SRAMC "allwinner-sramc" +#define TYPE_AW_SRAMC_SUN8I_R40 TYPE_AW_SRAMC "-sun8i-r40" +OBJECT_DECLARE_TYPE(AwSRAMCState, AwSRAMCClass, AW_SRAMC) + +/** @} */ + +/** + * Allwinner SRAMC object instance state + */ +struct AwSRAMCState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /* registers */ + uint32_t sram_ctl1; + uint32_t sram_ver; + uint32_t sram_soft_entry_reg0; +}; + +/** + * Allwinner SRAM 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. + */ +struct AwSRAMCClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + + uint32_t sram_version_code; +}; + +#endif /* HW_MISC_ALLWINNER_SRAMC_H */ diff --git a/include/hw/misc/aspeed_lpc.h b/include/hw/misc/aspeed_lpc.h index fd228731d2..fa398959af 100644 --- a/include/hw/misc/aspeed_lpc.h +++ b/include/hw/misc/aspeed_lpc.h @@ -12,8 +12,6 @@ #include "hw/sysbus.h" -#include - #define TYPE_ASPEED_LPC "aspeed.lpc" #define ASPEED_LPC(obj) OBJECT_CHECK(AspeedLPCState, (obj), TYPE_ASPEED_LPC) diff --git a/include/hw/misc/aspeed_peci.h b/include/hw/misc/aspeed_peci.h new file mode 100644 index 0000000000..8382707d9f --- /dev/null +++ b/include/hw/misc/aspeed_peci.h @@ -0,0 +1,29 @@ +/* + * Aspeed PECI Controller + * + * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com) + * + * This code is licensed under the GPL version 2 or later. See the COPYING + * file in the top-level directory. + */ + +#ifndef ASPEED_PECI_H +#define ASPEED_PECI_H + +#include "hw/sysbus.h" + +#define ASPEED_PECI_NR_REGS ((0xFC + 4) >> 2) +#define TYPE_ASPEED_PECI "aspeed.peci" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedPECIState, ASPEED_PECI); + +struct AspeedPECIState { + /* */ + SysBusDevice parent; + + MemoryRegion mmio; + qemu_irq irq; + + uint32_t regs[ASPEED_PECI_NR_REGS]; +}; + +#endif diff --git a/include/hw/misc/aspeed_sbc.h b/include/hw/misc/aspeed_sbc.h index 67e43b53ec..405e6782b9 100644 --- a/include/hw/misc/aspeed_sbc.h +++ b/include/hw/misc/aspeed_sbc.h @@ -17,9 +17,22 @@ OBJECT_DECLARE_TYPE(AspeedSBCState, AspeedSBCClass, ASPEED_SBC) #define ASPEED_SBC_NR_REGS (0x93c >> 2) +#define QSR_AES BIT(27) +#define QSR_RSA1024 (0x0 << 12) +#define QSR_RSA2048 (0x1 << 12) +#define QSR_RSA3072 (0x2 << 12) +#define QSR_RSA4096 (0x3 << 12) +#define QSR_SHA224 (0x0 << 10) +#define QSR_SHA256 (0x1 << 10) +#define QSR_SHA384 (0x2 << 10) +#define QSR_SHA512 (0x3 << 10) + struct AspeedSBCState { SysBusDevice parent; + bool emmc_abr; + uint32_t signing_settings; + MemoryRegion iomem; uint32_t regs[ASPEED_SBC_NR_REGS]; diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h index 5c7c04eedf..7cb6018dbc 100644 --- a/include/hw/misc/aspeed_scu.h +++ b/include/hw/misc/aspeed_scu.h @@ -51,7 +51,7 @@ struct AspeedSCUState { #define ASPEED_IS_AST2500(si_rev) ((((si_rev) >> 24) & 0xff) == 0x04) -extern bool is_supported_silicon_rev(uint32_t silicon_rev); +bool is_supported_silicon_rev(uint32_t silicon_rev); struct AspeedSCUClass { diff --git a/include/hw/misc/auxbus.h b/include/hw/misc/auxbus.h index b05799d2f7..03cacdee42 100644 --- a/include/hw/misc/auxbus.h +++ b/include/hw/misc/auxbus.h @@ -106,7 +106,7 @@ void aux_bus_realize(AUXBus *bus); * * Returns the reply of the request. * - * @bus Ths bus where the request happen. + * @bus The bus where the request happen. * @cmd The command requested. * @address The 20bits address of the slave. * @len The length of the read or write. diff --git a/include/hw/misc/bcm2835_property.h b/include/hw/misc/bcm2835_property.h index 712b76b7a3..ba8896610c 100644 --- a/include/hw/misc/bcm2835_property.h +++ b/include/hw/misc/bcm2835_property.h @@ -30,6 +30,7 @@ struct BCM2835PropertyState { MACAddr macaddr; uint32_t board_rev; uint32_t addr; + char *command_line; bool pending; }; diff --git a/include/hw/misc/djmemc.h b/include/hw/misc/djmemc.h new file mode 100644 index 0000000000..82d4e4a2fe --- /dev/null +++ b/include/hw/misc/djmemc.h @@ -0,0 +1,30 @@ +/* + * djMEMC, macintosh memory and interrupt controller + * (Quadra 610/650/800 & Centris 610/650) + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_MISC_DJMEMC_H +#define HW_MISC_DJMEMC_H + +#include "hw/sysbus.h" + +#define DJMEMC_SIZE 0x2000 +#define DJMEMC_NUM_REGS (0x38 / sizeof(uint32_t)) + +#define DJMEMC_MAXBANKS 10 + +struct DJMEMCState { + SysBusDevice parent_obj; + + MemoryRegion mem_regs; + + /* Memory controller */ + uint32_t regs[DJMEMC_NUM_REGS]; +}; + +#define TYPE_DJMEMC "djMEMC" +OBJECT_DECLARE_SIMPLE_TYPE(DJMEMCState, DJMEMC); + +#endif diff --git a/include/hw/misc/imx7_snvs.h b/include/hw/misc/imx7_snvs.h index 14a1d6fe6b..1272076086 100644 --- a/include/hw/misc/imx7_snvs.h +++ b/include/hw/misc/imx7_snvs.h @@ -20,7 +20,9 @@ enum IMX7SNVSRegisters { SNVS_LPCR = 0x38, SNVS_LPCR_TOP = BIT(6), - SNVS_LPCR_DP_EN = BIT(5) + SNVS_LPCR_DP_EN = BIT(5), + SNVS_LPSRTCMR = 0x050, /* Secure Real Time Counter MSB Register */ + SNVS_LPSRTCLR = 0x054, /* Secure Real Time Counter LSB Register */ }; #define TYPE_IMX7_SNVS "imx7.snvs" @@ -31,6 +33,9 @@ struct IMX7SNVSState { SysBusDevice parent_obj; MemoryRegion mmio; + + uint64_t tick_offset; + uint64_t lpcr; }; #endif /* IMX7_SNVS_H */ diff --git a/include/hw/misc/imx7_src.h b/include/hw/misc/imx7_src.h new file mode 100644 index 0000000000..b4b97dcb1c --- /dev/null +++ b/include/hw/misc/imx7_src.h @@ -0,0 +1,66 @@ +/* + * IMX7 System Reset Controller + * + * Copyright (C) 2023 Jean-Christophe Dubois + * + * 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 IMX7_SRC_H +#define IMX7_SRC_H + +#include "hw/sysbus.h" +#include "qemu/bitops.h" +#include "qom/object.h" + +#define SRC_SCR 0 +#define SRC_A7RCR0 1 +#define SRC_A7RCR1 2 +#define SRC_M4RCR 3 +#define SRC_ERCR 5 +#define SRC_HSICPHY_RCR 7 +#define SRC_USBOPHY1_RCR 8 +#define SRC_USBOPHY2_RCR 9 +#define SRC_MPIPHY_RCR 10 +#define SRC_PCIEPHY_RCR 11 +#define SRC_SBMR1 22 +#define SRC_SRSR 23 +#define SRC_SISR 26 +#define SRC_SIMR 27 +#define SRC_SBMR2 28 +#define SRC_GPR1 29 +#define SRC_GPR2 30 +#define SRC_GPR3 31 +#define SRC_GPR4 32 +#define SRC_GPR5 33 +#define SRC_GPR6 34 +#define SRC_GPR7 35 +#define SRC_GPR8 36 +#define SRC_GPR9 37 +#define SRC_GPR10 38 +#define SRC_MAX 39 + +/* SRC_A7SCR1 */ +#define R_CORE1_ENABLE_SHIFT 1 +#define R_CORE1_ENABLE_LENGTH 1 +/* SRC_A7SCR0 */ +#define R_CORE1_RST_SHIFT 5 +#define R_CORE1_RST_LENGTH 1 +#define R_CORE0_RST_SHIFT 4 +#define R_CORE0_RST_LENGTH 1 + +#define TYPE_IMX7_SRC "imx7.src" +OBJECT_DECLARE_SIMPLE_TYPE(IMX7SRCState, IMX7_SRC) + +struct IMX7SRCState { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion iomem; + + uint32_t regs[SRC_MAX]; +}; + +#endif /* IMX7_SRC_H */ diff --git a/include/hw/misc/iosb.h b/include/hw/misc/iosb.h new file mode 100644 index 0000000000..377f8ca7e2 --- /dev/null +++ b/include/hw/misc/iosb.h @@ -0,0 +1,25 @@ +/* + * QEMU IOSB emulation + * + * Copyright (c) 2019 Laurent Vivier + * Copyright (c) 2022 Mark Cave-Ayland + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_MEM_IOSB_H +#define HW_MEM_IOSB_H + +#define IOSB_REGS 7 + +struct IOSBState { + SysBusDevice parent_obj; + + MemoryRegion mem_regs; + uint32_t regs[IOSB_REGS]; +}; + +#define TYPE_IOSB "IOSB" +OBJECT_DECLARE_SIMPLE_TYPE(IOSBState, IOSB); + +#endif diff --git a/include/hw/misc/lasi.h b/include/hw/misc/lasi.h index ecc7065ce8..f01c0f680a 100644 --- a/include/hw/misc/lasi.h +++ b/include/hw/misc/lasi.h @@ -26,9 +26,11 @@ OBJECT_DECLARE_SIMPLE_TYPE(LasiState, LASI_CHIP) #define LASI_IAR 0x10 #define LASI_LPT 0x02000 +#define LASI_AUDIO 0x04000 #define LASI_UART 0x05000 #define LASI_LAN 0x07000 #define LASI_RTC 0x09000 +#define LASI_FDC 0x0A000 #define LASI_PCR 0x0C000 /* LASI Power Control register */ #define LASI_ERRLOG 0x0C004 /* LASI Error Logging register */ @@ -69,8 +71,7 @@ struct LasiState { uint32_t errlog; uint32_t amr; - uint32_t rtc; - time_t rtc_ref; + uint32_t rtc_ref; MemoryRegion this_mem; }; diff --git a/include/hw/misc/mac_via.h b/include/hw/misc/mac_via.h index 5fe7a7f592..63cdcf7c69 100644 --- a/include/hw/misc/mac_via.h +++ b/include/hw/misc/mac_via.h @@ -12,6 +12,7 @@ #include "exec/memory.h" #include "hw/sysbus.h" #include "hw/misc/mos6522.h" +#include "hw/input/adb.h" #include "qom/object.h" @@ -73,6 +74,9 @@ struct MOS6522Q800VIA1State { int64_t next_second; QEMUTimer *sixty_hz_timer; int64_t next_sixty_hz; + + /* SETUPTIMEK hack */ + int timer_hack_state; }; diff --git a/include/hw/misc/macio/cuda.h b/include/hw/misc/macio/cuda.h index a71deec968..8a6678c749 100644 --- a/include/hw/misc/macio/cuda.h +++ b/include/hw/misc/macio/cuda.h @@ -26,6 +26,7 @@ #ifndef CUDA_H #define CUDA_H +#include "hw/input/adb.h" #include "hw/misc/mos6522.h" #include "qom/object.h" diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h index 6c05f3bfd2..2b54da6b31 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -27,17 +27,38 @@ #define MACIO_H #include "hw/char/escc.h" -#include "hw/pci/pci.h" -#include "hw/ide/internal.h" +#include "hw/pci/pci_device.h" +#include "hw/ide/ide-bus.h" #include "hw/intc/heathrow_pic.h" #include "hw/misc/macio/cuda.h" #include "hw/misc/macio/gpio.h" #include "hw/misc/macio/pmu.h" -#include "hw/ppc/mac.h" +#include "hw/nvram/mac_nvram.h" #include "hw/ppc/mac_dbdma.h" #include "hw/ppc/openpic.h" #include "qom/object.h" +/* Old World IRQs */ +#define OLDWORLD_CUDA_IRQ 0x12 +#define OLDWORLD_ESCCB_IRQ 0x10 +#define OLDWORLD_ESCCA_IRQ 0xf +#define OLDWORLD_IDE0_IRQ 0xd +#define OLDWORLD_IDE0_DMA_IRQ 0x2 +#define OLDWORLD_IDE1_IRQ 0xe +#define OLDWORLD_IDE1_DMA_IRQ 0x3 + +/* New World IRQs */ +#define NEWWORLD_CUDA_IRQ 0x19 +#define NEWWORLD_PMU_IRQ 0x19 +#define NEWWORLD_ESCCB_IRQ 0x24 +#define NEWWORLD_ESCCA_IRQ 0x25 +#define NEWWORLD_IDE0_IRQ 0xd +#define NEWWORLD_IDE0_DMA_IRQ 0x2 +#define NEWWORLD_IDE1_IRQ 0xe +#define NEWWORLD_IDE1_DMA_IRQ 0x3 +#define NEWWORLD_EXTING_GPIO1 0x2f +#define NEWWORLD_EXTING_GPIO9 0x37 + /* MacIO virtual bus */ #define TYPE_MACIO_BUS "macio-bus" OBJECT_DECLARE_SIMPLE_TYPE(MacIOBusState, MACIO_BUS) diff --git a/include/hw/misc/macio/pmu.h b/include/hw/misc/macio/pmu.h index 00fcdd23f5..ceb12082ae 100644 --- a/include/hw/misc/macio/pmu.h +++ b/include/hw/misc/macio/pmu.h @@ -10,6 +10,7 @@ #ifndef PMU_H #define PMU_H +#include "hw/input/adb.h" #include "hw/misc/mos6522.h" #include "hw/misc/macio/gpio.h" #include "qom/object.h" @@ -75,7 +76,7 @@ #define PMU_INT_WAITING_CHARGER 0x01 /* ??? */ #define PMU_INT_AUTO_SRQ_POLL 0x02 /* ??? */ -/* Bits in the environement message (either obtained via PMU_GET_COVER, +/* Bits in the environment message (either obtained via PMU_GET_COVER, * or via PMU_INT_ENVIRONMENT on core99 */ #define PMU_ENV_LID_CLOSED 0x01 /* The lid is closed */ diff --git a/include/hw/misc/mchp_pfsoc_dmc.h b/include/hw/misc/mchp_pfsoc_dmc.h index 2baa1413b0..3bc1581e0f 100644 --- a/include/hw/misc/mchp_pfsoc_dmc.h +++ b/include/hw/misc/mchp_pfsoc_dmc.h @@ -23,6 +23,8 @@ #ifndef MCHP_PFSOC_DMC_H #define MCHP_PFSOC_DMC_H +#include "hw/sysbus.h" + /* DDR SGMII PHY module */ #define MCHP_PFSOC_DDR_SGMII_PHY_REG_SIZE 0x1000 diff --git a/include/hw/misc/mchp_pfsoc_ioscb.h b/include/hw/misc/mchp_pfsoc_ioscb.h index 9235523e33..3fd3e74966 100644 --- a/include/hw/misc/mchp_pfsoc_ioscb.h +++ b/include/hw/misc/mchp_pfsoc_ioscb.h @@ -23,13 +23,18 @@ #ifndef MCHP_PFSOC_IOSCB_H #define MCHP_PFSOC_IOSCB_H +#include "hw/sysbus.h" + typedef struct MchpPfSoCIoscbState { SysBusDevice parent; MemoryRegion container; MemoryRegion lane01; MemoryRegion lane23; MemoryRegion ctrl; + MemoryRegion qspixip; + MemoryRegion mailbox; MemoryRegion cfg; + MemoryRegion ccc; MemoryRegion pll_mss; MemoryRegion cfm_mss; MemoryRegion pll_ddr; @@ -40,6 +45,7 @@ typedef struct MchpPfSoCIoscbState { MemoryRegion cfm_sgmii; MemoryRegion bc_sgmii; MemoryRegion io_calib_sgmii; + qemu_irq irq; } MchpPfSoCIoscbState; #define TYPE_MCHP_PFSOC_IOSCB "mchp.pfsoc.ioscb" diff --git a/include/hw/misc/mchp_pfsoc_sysreg.h b/include/hw/misc/mchp_pfsoc_sysreg.h index 546ba68f6a..c2232bd28d 100644 --- a/include/hw/misc/mchp_pfsoc_sysreg.h +++ b/include/hw/misc/mchp_pfsoc_sysreg.h @@ -23,11 +23,14 @@ #ifndef MCHP_PFSOC_SYSREG_H #define MCHP_PFSOC_SYSREG_H +#include "hw/sysbus.h" + #define MCHP_PFSOC_SYSREG_REG_SIZE 0x2000 typedef struct MchpPfSoCSysregState { SysBusDevice parent; MemoryRegion sysreg; + qemu_irq irq; } MchpPfSoCSysregState; #define TYPE_MCHP_PFSOC_SYSREG "mchp.pfsoc.sysreg" diff --git a/include/hw/misc/mips_cmgcr.h b/include/hw/misc/mips_cmgcr.h index 9fa58942d7..db4bf5f449 100644 --- a/include/hw/misc/mips_cmgcr.h +++ b/include/hw/misc/mips_cmgcr.h @@ -75,7 +75,7 @@ struct MIPSGCRState { SysBusDevice parent_obj; int32_t gcr_rev; - int32_t num_vps; + uint32_t num_vps; hwaddr gcr_base; MemoryRegion iomem; MemoryRegion *cpc_mr; diff --git a/include/hw/misc/mips_itu.h b/include/hw/misc/mips_itu.h index 50d961106d..27c9a1090d 100644 --- a/include/hw/misc/mips_itu.h +++ b/include/hw/misc/mips_itu.h @@ -57,8 +57,8 @@ struct MIPSITUState { SysBusDevice parent_obj; /*< public >*/ - int32_t num_fifo; - int32_t num_semaphores; + uint32_t num_fifo; + uint32_t num_semaphores; /* ITC Storage */ ITCStorageCell *cell; @@ -70,11 +70,6 @@ struct MIPSITUState { /* ITU Control Register */ uint64_t icr0; - - /* SAAR */ - bool saar_present; - void *saar; - }; /* Get ITC Configuration Tag memory region. */ diff --git a/include/hw/misc/mos6522.h b/include/hw/misc/mos6522.h index 0bc22a8395..fba45668ab 100644 --- a/include/hw/misc/mos6522.h +++ b/include/hw/misc/mos6522.h @@ -27,9 +27,8 @@ #ifndef MOS6522_H #define MOS6522_H -#include "exec/memory.h" +#include "exec/hwaddr.h" #include "hw/sysbus.h" -#include "hw/input/adb.h" #include "qom/object.h" #define MOS6522_NUM_REGS 16 @@ -157,7 +156,7 @@ OBJECT_DECLARE_TYPE(MOS6522State, MOS6522DeviceClass, MOS6522) struct MOS6522DeviceClass { DeviceClass parent_class; - DeviceReset parent_reset; + ResettablePhases parent_phases; void (*portB_write)(MOS6522State *dev); void (*portA_write)(MOS6522State *dev); /* These are used to influence the CUDA MacOS timebase calibration */ diff --git a/include/hw/misc/mps2-scc.h b/include/hw/misc/mps2-scc.h index 3b2d13ac9c..8ff188c06b 100644 --- a/include/hw/misc/mps2-scc.h +++ b/include/hw/misc/mps2-scc.h @@ -51,6 +51,7 @@ struct MPS2SCC { uint32_t cfg4; uint32_t cfg5; uint32_t cfg6; + uint32_t cfg7; uint32_t cfgdata_rtn; uint32_t cfgdata_out; uint32_t cfgctrl; diff --git a/include/hw/misc/npcm7xx_clk.h b/include/hw/misc/npcm7xx_clk.h index d5c8d16ca4..5ed4a4672b 100644 --- a/include/hw/misc/npcm7xx_clk.h +++ b/include/hw/misc/npcm7xx_clk.h @@ -175,6 +175,6 @@ struct NPCM7xxCLKState { }; #define TYPE_NPCM7XX_CLK "npcm7xx-clk" -#define NPCM7XX_CLK(obj) OBJECT_CHECK(NPCM7xxCLKState, (obj), TYPE_NPCM7XX_CLK) +OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxCLKState, NPCM7XX_CLK) #endif /* NPCM7XX_CLK_H */ diff --git a/include/hw/misc/npcm7xx_gcr.h b/include/hw/misc/npcm7xx_gcr.h index 9419e0a7d2..c0bbdda77e 100644 --- a/include/hw/misc/npcm7xx_gcr.h +++ b/include/hw/misc/npcm7xx_gcr.h @@ -55,7 +55,7 @@ */ #define NPCM7XX_GCR_NR_REGS (0x148 / sizeof(uint32_t)) -typedef struct NPCM7xxGCRState { +struct NPCM7xxGCRState { SysBusDevice parent; MemoryRegion iomem; @@ -65,9 +65,9 @@ typedef struct NPCM7xxGCRState { uint32_t reset_pwron; uint32_t reset_mdlr; uint32_t reset_intcr3; -} NPCM7xxGCRState; +}; #define TYPE_NPCM7XX_GCR "npcm7xx-gcr" -#define NPCM7XX_GCR(obj) OBJECT_CHECK(NPCM7xxGCRState, (obj), TYPE_NPCM7XX_GCR) +OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxGCRState, NPCM7XX_GCR) #endif /* NPCM7XX_GCR_H */ diff --git a/include/hw/misc/npcm7xx_mft.h b/include/hw/misc/npcm7xx_mft.h index 36785e3ba8..d6384382ce 100644 --- a/include/hw/misc/npcm7xx_mft.h +++ b/include/hw/misc/npcm7xx_mft.h @@ -49,7 +49,7 @@ * @max_rpm: The maximum rpm for fans. Order: A0, B0, A1, B1. * @duty: The duty cycles for fans, relative to NPCM7XX_PWM_MAX_DUTY. */ -typedef struct NPCM7xxMFTState { +struct NPCM7xxMFTState { SysBusDevice parent; MemoryRegion iomem; @@ -61,10 +61,9 @@ typedef struct NPCM7xxMFTState { uint32_t max_rpm[NPCM7XX_MFT_FANIN_COUNT]; uint32_t duty[NPCM7XX_MFT_FANIN_COUNT]; -} NPCM7xxMFTState; +}; #define TYPE_NPCM7XX_MFT "npcm7xx-mft" -#define NPCM7XX_MFT(obj) \ - OBJECT_CHECK(NPCM7xxMFTState, (obj), TYPE_NPCM7XX_MFT) +OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxMFTState, NPCM7XX_MFT) #endif /* NPCM7XX_MFT_H */ diff --git a/include/hw/misc/npcm7xx_pwm.h b/include/hw/misc/npcm7xx_pwm.h index 7ad632a93a..bf953440ac 100644 --- a/include/hw/misc/npcm7xx_pwm.h +++ b/include/hw/misc/npcm7xx_pwm.h @@ -101,7 +101,6 @@ struct NPCM7xxPWMState { }; #define TYPE_NPCM7XX_PWM "npcm7xx-pwm" -#define NPCM7XX_PWM(obj) \ - OBJECT_CHECK(NPCM7xxPWMState, (obj), TYPE_NPCM7XX_PWM) +OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxPWMState, NPCM7XX_PWM) #endif /* NPCM7XX_PWM_H */ diff --git a/include/hw/misc/npcm7xx_rng.h b/include/hw/misc/npcm7xx_rng.h index 5e85fd439d..650375dc2c 100644 --- a/include/hw/misc/npcm7xx_rng.h +++ b/include/hw/misc/npcm7xx_rng.h @@ -18,7 +18,7 @@ #include "hw/sysbus.h" -typedef struct NPCM7xxRNGState { +struct NPCM7xxRNGState { SysBusDevice parent; MemoryRegion iomem; @@ -26,9 +26,9 @@ typedef struct NPCM7xxRNGState { uint8_t rngcs; uint8_t rngd; uint8_t rngmode; -} NPCM7xxRNGState; +}; #define TYPE_NPCM7XX_RNG "npcm7xx-rng" -#define NPCM7XX_RNG(obj) OBJECT_CHECK(NPCM7xxRNGState, (obj), TYPE_NPCM7XX_RNG) +OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxRNGState, NPCM7XX_RNG) #endif /* NPCM7XX_RNG_H */ diff --git a/include/hw/misc/pvpanic.h b/include/hw/misc/pvpanic.h index 7f16cc9b16..fab94165d0 100644 --- a/include/hw/misc/pvpanic.h +++ b/include/hw/misc/pvpanic.h @@ -15,6 +15,7 @@ #ifndef HW_MISC_PVPANIC_H #define HW_MISC_PVPANIC_H +#include "exec/memory.h" #include "qom/object.h" #define TYPE_PVPANIC_ISA_DEVICE "pvpanic" @@ -33,13 +34,4 @@ struct PVPanicState { void pvpanic_setup_io(PVPanicState *s, DeviceState *dev, unsigned size); -static inline uint16_t pvpanic_port(void) -{ - Object *o = object_resolve_path_type("", TYPE_PVPANIC_ISA_DEVICE, NULL); - if (!o) { - return 0; - } - return object_property_get_uint(o, PVPANIC_IOPORT_PROP, NULL); -} - #endif diff --git a/include/hw/misc/sifive_e_aon.h b/include/hw/misc/sifive_e_aon.h new file mode 100644 index 0000000000..2ae1c4139c --- /dev/null +++ b/include/hw/misc/sifive_e_aon.h @@ -0,0 +1,60 @@ +/* + * SiFive HiFive1 AON (Always On Domain) interface. + * + * Copyright (c) 2022 SiFive, Inc. All rights reserved. + * + * 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_SIFIVE_AON_H +#define HW_SIFIVE_AON_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_SIFIVE_E_AON "riscv.sifive.e.aon" +OBJECT_DECLARE_SIMPLE_TYPE(SiFiveEAONState, SIFIVE_E_AON) + +#define SIFIVE_E_AON_WDOGKEY (0x51F15E) +#define SIFIVE_E_AON_WDOGFEED (0xD09F00D) +#define SIFIVE_E_LFCLK_DEFAULT_FREQ (32768) + +enum { + SIFIVE_E_AON_WDT = 0x0, + SIFIVE_E_AON_RTC = 0x40, + SIFIVE_E_AON_LFROSC = 0x70, + SIFIVE_E_AON_BACKUP = 0x80, + SIFIVE_E_AON_PMU = 0x100, + SIFIVE_E_AON_MAX = 0x150 +}; + +struct SiFiveEAONState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion mmio; + + /*< watchdog timer >*/ + QEMUTimer *wdog_timer; + qemu_irq wdog_irq; + uint64_t wdog_restart_time; + uint64_t wdogclk_freq; + + uint32_t wdogcfg; + uint16_t wdogcmp0; + uint32_t wdogcount; + uint8_t wdogunlock; +}; + +#endif diff --git a/include/hw/misc/sifive_e_prci.h b/include/hw/misc/sifive_e_prci.h index 262ca16181..6aa949e910 100644 --- a/include/hw/misc/sifive_e_prci.h +++ b/include/hw/misc/sifive_e_prci.h @@ -18,7 +18,8 @@ #ifndef HW_SIFIVE_E_PRCI_H #define HW_SIFIVE_E_PRCI_H -#include "qom/object.h" + +#include "hw/sysbus.h" enum { SIFIVE_E_PRCI_HFROSCCFG = 0x0, diff --git a/include/hw/misc/sifive_u_otp.h b/include/hw/misc/sifive_u_otp.h index 5d0d7df455..170d2148f2 100644 --- a/include/hw/misc/sifive_u_otp.h +++ b/include/hw/misc/sifive_u_otp.h @@ -18,7 +18,8 @@ #ifndef HW_SIFIVE_U_OTP_H #define HW_SIFIVE_U_OTP_H -#include "qom/object.h" + +#include "hw/sysbus.h" #define SIFIVE_U_OTP_PA 0x00 #define SIFIVE_U_OTP_PAIO 0x04 diff --git a/include/hw/misc/sifive_u_prci.h b/include/hw/misc/sifive_u_prci.h index d9ebf40b7f..4d2491ad46 100644 --- a/include/hw/misc/sifive_u_prci.h +++ b/include/hw/misc/sifive_u_prci.h @@ -18,7 +18,8 @@ #ifndef HW_SIFIVE_U_PRCI_H #define HW_SIFIVE_U_PRCI_H -#include "qom/object.h" + +#include "hw/sysbus.h" #define SIFIVE_U_PRCI_HFXOSCCFG 0x00 #define SIFIVE_U_PRCI_COREPLLCFG0 0x04 diff --git a/include/hw/misc/stm32l4x5_exti.h b/include/hw/misc/stm32l4x5_exti.h new file mode 100644 index 0000000000..be961d2f01 --- /dev/null +++ b/include/hw/misc/stm32l4x5_exti.h @@ -0,0 +1,51 @@ +/* + * STM32L4x5 EXTI (Extended interrupts and events controller) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This work is based on the stm32f4xx_exti by Alistair Francis. + * Original code is licensed under the MIT License: + * + * Copyright (c) 2014 Alistair Francis + */ + +/* + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html + */ + +#ifndef HW_STM32L4X5_EXTI_H +#define HW_STM32L4X5_EXTI_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_STM32L4X5_EXTI "stm32l4x5-exti" +OBJECT_DECLARE_SIMPLE_TYPE(Stm32l4x5ExtiState, STM32L4X5_EXTI) + +#define EXTI_NUM_INTERRUPT_OUT_LINES 40 +#define EXTI_NUM_REGISTER 2 + +struct Stm32l4x5ExtiState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t imr[EXTI_NUM_REGISTER]; + uint32_t emr[EXTI_NUM_REGISTER]; + uint32_t rtsr[EXTI_NUM_REGISTER]; + uint32_t ftsr[EXTI_NUM_REGISTER]; + uint32_t swier[EXTI_NUM_REGISTER]; + uint32_t pr[EXTI_NUM_REGISTER]; + + qemu_irq irq[EXTI_NUM_INTERRUPT_OUT_LINES]; +}; + +#endif diff --git a/include/hw/misc/stm32l4x5_rcc.h b/include/hw/misc/stm32l4x5_rcc.h new file mode 100644 index 0000000000..0fbfba5c40 --- /dev/null +++ b/include/hw/misc/stm32l4x5_rcc.h @@ -0,0 +1,239 @@ +/* + * STM32L4X5 RCC (Reset and clock control) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * + * Inspired by the BCM2835 CPRMAN clock manager by Luc Michel. + */ + +#ifndef HW_STM32L4X5_RCC_H +#define HW_STM32L4X5_RCC_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_STM32L4X5_RCC "stm32l4x5-rcc" +OBJECT_DECLARE_SIMPLE_TYPE(Stm32l4x5RccState, STM32L4X5_RCC) + +/* In the Stm32l4x5 clock tree, mux have at most 7 sources */ +#define RCC_NUM_CLOCK_MUX_SRC 7 + +typedef enum PllCommonChannels { + RCC_PLL_COMMON_CHANNEL_P = 0, + RCC_PLL_COMMON_CHANNEL_Q = 1, + RCC_PLL_COMMON_CHANNEL_R = 2, + + RCC_NUM_CHANNEL_PLL_OUT = 3 +} PllCommonChannels; + +/* NB: Prescaler are assimilated to mux with one source and one output */ +typedef enum RccClockMux { + /* Internal muxes that arent't exposed publicly to other peripherals */ + RCC_CLOCK_MUX_SYSCLK, + RCC_CLOCK_MUX_PLL_INPUT, + RCC_CLOCK_MUX_HCLK, + RCC_CLOCK_MUX_PCLK1, + RCC_CLOCK_MUX_PCLK2, + RCC_CLOCK_MUX_HSE_OVER_32, + RCC_CLOCK_MUX_LCD_AND_RTC_COMMON, + + /* Muxes with a publicly available output */ + RCC_CLOCK_MUX_CORTEX_REFCLK, + RCC_CLOCK_MUX_USART1, + RCC_CLOCK_MUX_USART2, + RCC_CLOCK_MUX_USART3, + RCC_CLOCK_MUX_UART4, + RCC_CLOCK_MUX_UART5, + RCC_CLOCK_MUX_LPUART1, + RCC_CLOCK_MUX_I2C1, + RCC_CLOCK_MUX_I2C2, + RCC_CLOCK_MUX_I2C3, + RCC_CLOCK_MUX_LPTIM1, + RCC_CLOCK_MUX_LPTIM2, + RCC_CLOCK_MUX_SWPMI1, + RCC_CLOCK_MUX_MCO, + RCC_CLOCK_MUX_LSCO, + RCC_CLOCK_MUX_DFSDM1, + RCC_CLOCK_MUX_ADC, + RCC_CLOCK_MUX_CLK48, + RCC_CLOCK_MUX_SAI1, + RCC_CLOCK_MUX_SAI2, + + /* + * Mux that have only one input and one output assigned to as peripheral. + * They could be direct lines but it is simpler + * to use the same logic for all outputs. + */ + /* - AHB1 */ + RCC_CLOCK_MUX_TSC, + RCC_CLOCK_MUX_CRC, + RCC_CLOCK_MUX_FLASH, + RCC_CLOCK_MUX_DMA2, + RCC_CLOCK_MUX_DMA1, + + /* - AHB2 */ + RCC_CLOCK_MUX_RNG, + RCC_CLOCK_MUX_AES, + RCC_CLOCK_MUX_OTGFS, + RCC_CLOCK_MUX_GPIOA, + RCC_CLOCK_MUX_GPIOB, + RCC_CLOCK_MUX_GPIOC, + RCC_CLOCK_MUX_GPIOD, + RCC_CLOCK_MUX_GPIOE, + RCC_CLOCK_MUX_GPIOF, + RCC_CLOCK_MUX_GPIOG, + RCC_CLOCK_MUX_GPIOH, + + /* - AHB3 */ + RCC_CLOCK_MUX_QSPI, + RCC_CLOCK_MUX_FMC, + + /* - APB1 */ + RCC_CLOCK_MUX_OPAMP, + RCC_CLOCK_MUX_DAC1, + RCC_CLOCK_MUX_PWR, + RCC_CLOCK_MUX_CAN1, + RCC_CLOCK_MUX_SPI3, + RCC_CLOCK_MUX_SPI2, + RCC_CLOCK_MUX_WWDG, + RCC_CLOCK_MUX_LCD, + RCC_CLOCK_MUX_TIM7, + RCC_CLOCK_MUX_TIM6, + RCC_CLOCK_MUX_TIM5, + RCC_CLOCK_MUX_TIM4, + RCC_CLOCK_MUX_TIM3, + RCC_CLOCK_MUX_TIM2, + + /* - APB2 */ + RCC_CLOCK_MUX_TIM17, + RCC_CLOCK_MUX_TIM16, + RCC_CLOCK_MUX_TIM15, + RCC_CLOCK_MUX_TIM8, + RCC_CLOCK_MUX_SPI1, + RCC_CLOCK_MUX_TIM1, + RCC_CLOCK_MUX_SDMMC1, + RCC_CLOCK_MUX_FW, + RCC_CLOCK_MUX_SYSCFG, + + /* - BDCR */ + RCC_CLOCK_MUX_RTC, + + /* - OTHER */ + RCC_CLOCK_MUX_CORTEX_FCLK, + + RCC_NUM_CLOCK_MUX +} RccClockMux; + +typedef enum RccPll { + RCC_PLL_PLL, + RCC_PLL_PLLSAI1, + RCC_PLL_PLLSAI2, + + RCC_NUM_PLL +} RccPll; + +typedef struct RccClockMuxState { + DeviceState parent_obj; + + RccClockMux id; + Clock *srcs[RCC_NUM_CLOCK_MUX_SRC]; + Clock *out; + bool enabled; + uint32_t src; + uint32_t multiplier; + uint32_t divider; + + /* + * Used by clock srcs update callback to retrieve both the clock and the + * source number. + */ + struct RccClockMuxState *backref[RCC_NUM_CLOCK_MUX_SRC]; +} RccClockMuxState; + +typedef struct RccPllState { + DeviceState parent_obj; + + RccPll id; + Clock *in; + uint32_t vco_multiplier; + Clock *channels[RCC_NUM_CHANNEL_PLL_OUT]; + /* Global pll enabled flag */ + bool enabled; + /* 'enabled' refers to the runtime configuration */ + bool channel_enabled[RCC_NUM_CHANNEL_PLL_OUT]; + /* + * 'exists' refers to the physical configuration + * It should only be set at pll initialization. + * e.g. pllsai2 doesn't have a Q output. + */ + bool channel_exists[RCC_NUM_CHANNEL_PLL_OUT]; + uint32_t channel_divider[RCC_NUM_CHANNEL_PLL_OUT]; +} RccPllState; + +struct Stm32l4x5RccState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t cr; + uint32_t icscr; + uint32_t cfgr; + uint32_t pllcfgr; + uint32_t pllsai1cfgr; + uint32_t pllsai2cfgr; + uint32_t cier; + uint32_t cifr; + uint32_t ahb1rstr; + uint32_t ahb2rstr; + uint32_t ahb3rstr; + uint32_t apb1rstr1; + uint32_t apb1rstr2; + uint32_t apb2rstr; + uint32_t ahb1enr; + uint32_t ahb2enr; + uint32_t ahb3enr; + uint32_t apb1enr1; + uint32_t apb1enr2; + uint32_t apb2enr; + uint32_t ahb1smenr; + uint32_t ahb2smenr; + uint32_t ahb3smenr; + uint32_t apb1smenr1; + uint32_t apb1smenr2; + uint32_t apb2smenr; + uint32_t ccipr; + uint32_t bdcr; + uint32_t csr; + + /* Clock sources */ + Clock *gnd; + Clock *hsi16_rc; + Clock *msi_rc; + Clock *hse; + Clock *lsi_rc; + Clock *lse_crystal; + Clock *sai1_extclk; + Clock *sai2_extclk; + + /* PLLs */ + RccPllState plls[RCC_NUM_PLL]; + + /* Muxes ~= outputs */ + RccClockMuxState clock_muxes[RCC_NUM_CLOCK_MUX]; + + qemu_irq irq; + uint64_t hse_frequency; + uint64_t sai1_extclk_frequency; + uint64_t sai2_extclk_frequency; +}; + +#endif /* HW_STM32L4X5_RCC_H */ diff --git a/include/hw/misc/stm32l4x5_rcc_internals.h b/include/hw/misc/stm32l4x5_rcc_internals.h new file mode 100644 index 0000000000..ff1c834f69 --- /dev/null +++ b/include/hw/misc/stm32l4x5_rcc_internals.h @@ -0,0 +1,1042 @@ +/* + * STM32L4X5 RCC (Reset and clock control) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * + * Inspired by the BCM2835 CPRMAN clock manager implementation by Luc Michel. + */ + +#ifndef HW_STM32L4X5_RCC_INTERNALS_H +#define HW_STM32L4X5_RCC_INTERNALS_H + +#include "hw/registerfields.h" +#include "hw/misc/stm32l4x5_rcc.h" + +#define TYPE_RCC_CLOCK_MUX "stm32l4x5-rcc-clock-mux" +#define TYPE_RCC_PLL "stm32l4x5-rcc-pll" + +OBJECT_DECLARE_SIMPLE_TYPE(RccClockMuxState, RCC_CLOCK_MUX) +OBJECT_DECLARE_SIMPLE_TYPE(RccPllState, RCC_PLL) + +/* Register map */ +REG32(CR, 0x00) + FIELD(CR, PLLSAI2RDY, 29, 1) + FIELD(CR, PLLSAI2ON, 28, 1) + FIELD(CR, PLLSAI1RDY, 27, 1) + FIELD(CR, PLLSAI1ON, 26, 1) + FIELD(CR, PLLRDY, 25, 1) + FIELD(CR, PLLON, 24, 1) + FIELD(CR, CSSON, 19, 1) + FIELD(CR, HSEBYP, 18, 1) + FIELD(CR, HSERDY, 17, 1) + FIELD(CR, HSEON, 16, 1) + FIELD(CR, HSIASFS, 11, 1) + FIELD(CR, HSIRDY, 10, 1) + FIELD(CR, HSIKERON, 9, 1) + FIELD(CR, HSION, 8, 1) + FIELD(CR, MSIRANGE, 4, 4) + FIELD(CR, MSIRGSEL, 3, 1) + FIELD(CR, MSIPLLEN, 2, 1) + FIELD(CR, MSIRDY, 1, 1) + FIELD(CR, MSION, 0, 1) +REG32(ICSCR, 0x04) + FIELD(ICSCR, HSITRIM, 24, 7) + FIELD(ICSCR, HSICAL, 16, 8) + FIELD(ICSCR, MSITRIM, 8, 8) + FIELD(ICSCR, MSICAL, 0, 8) +REG32(CFGR, 0x08) + FIELD(CFGR, MCOPRE, 28, 3) + /* MCOSEL[2:0] only for STM32L475xx/476xx/486xx devices */ + FIELD(CFGR, MCOSEL, 24, 3) + FIELD(CFGR, STOPWUCK, 15, 1) + FIELD(CFGR, PPRE2, 11, 3) + FIELD(CFGR, PPRE1, 8, 3) + FIELD(CFGR, HPRE, 4, 4) + FIELD(CFGR, SWS, 2, 2) + FIELD(CFGR, SW, 0, 2) +REG32(PLLCFGR, 0x0C) + FIELD(PLLCFGR, PLLPDIV, 27, 5) + FIELD(PLLCFGR, PLLR, 25, 2) + FIELD(PLLCFGR, PLLREN, 24, 1) + FIELD(PLLCFGR, PLLQ, 21, 2) + FIELD(PLLCFGR, PLLQEN, 20, 1) + FIELD(PLLCFGR, PLLP, 17, 1) + FIELD(PLLCFGR, PLLPEN, 16, 1) + FIELD(PLLCFGR, PLLN, 8, 7) + FIELD(PLLCFGR, PLLM, 4, 3) + FIELD(PLLCFGR, PLLSRC, 0, 2) +REG32(PLLSAI1CFGR, 0x10) + FIELD(PLLSAI1CFGR, PLLSAI1PDIV, 27, 5) + FIELD(PLLSAI1CFGR, PLLSAI1R, 25, 2) + FIELD(PLLSAI1CFGR, PLLSAI1REN, 24, 1) + FIELD(PLLSAI1CFGR, PLLSAI1Q, 21, 2) + FIELD(PLLSAI1CFGR, PLLSAI1QEN, 20, 1) + FIELD(PLLSAI1CFGR, PLLSAI1P, 17, 1) + FIELD(PLLSAI1CFGR, PLLSAI1PEN, 16, 1) + FIELD(PLLSAI1CFGR, PLLSAI1N, 8, 7) +REG32(PLLSAI2CFGR, 0x14) + FIELD(PLLSAI2CFGR, PLLSAI2PDIV, 27, 5) + FIELD(PLLSAI2CFGR, PLLSAI2R, 25, 2) + FIELD(PLLSAI2CFGR, PLLSAI2REN, 24, 1) + FIELD(PLLSAI2CFGR, PLLSAI2Q, 21, 2) + FIELD(PLLSAI2CFGR, PLLSAI2QEN, 20, 1) + FIELD(PLLSAI2CFGR, PLLSAI2P, 17, 1) + FIELD(PLLSAI2CFGR, PLLSAI2PEN, 16, 1) + FIELD(PLLSAI2CFGR, PLLSAI2N, 8, 7) +REG32(CIER, 0x18) + /* HSI48RDYIE: only on STM32L496xx/4A6xx devices */ + FIELD(CIER, LSECSSIE, 9, 1) + FIELD(CIER, PLLSAI2RDYIE, 7, 1) + FIELD(CIER, PLLSAI1RDYIE, 6, 1) + FIELD(CIER, PLLRDYIE, 5, 1) + FIELD(CIER, HSERDYIE, 4, 1) + FIELD(CIER, HSIRDYIE, 3, 1) + FIELD(CIER, MSIRDYIE, 2, 1) + FIELD(CIER, LSERDYIE, 1, 1) + FIELD(CIER, LSIRDYIE, 0, 1) +REG32(CIFR, 0x1C) + /* HSI48RDYF: only on STM32L496xx/4A6xx devices */ + FIELD(CIFR, LSECSSF, 9, 1) + FIELD(CIFR, CSSF, 8, 1) + FIELD(CIFR, PLLSAI2RDYF, 7, 1) + FIELD(CIFR, PLLSAI1RDYF, 6, 1) + FIELD(CIFR, PLLRDYF, 5, 1) + FIELD(CIFR, HSERDYF, 4, 1) + FIELD(CIFR, HSIRDYF, 3, 1) + FIELD(CIFR, MSIRDYF, 2, 1) + FIELD(CIFR, LSERDYF, 1, 1) + FIELD(CIFR, LSIRDYF, 0, 1) +REG32(CICR, 0x20) + /* HSI48RDYC: only on STM32L496xx/4A6xx devices */ + FIELD(CICR, LSECSSC, 9, 1) + FIELD(CICR, CSSC, 8, 1) + FIELD(CICR, PLLSAI2RDYC, 7, 1) + FIELD(CICR, PLLSAI1RDYC, 6, 1) + FIELD(CICR, PLLRDYC, 5, 1) + FIELD(CICR, HSERDYC, 4, 1) + FIELD(CICR, HSIRDYC, 3, 1) + FIELD(CICR, MSIRDYC, 2, 1) + FIELD(CICR, LSERDYC, 1, 1) + FIELD(CICR, LSIRDYC, 0, 1) +REG32(AHB1RSTR, 0x28) +REG32(AHB2RSTR, 0x2C) +REG32(AHB3RSTR, 0x30) +REG32(APB1RSTR1, 0x38) +REG32(APB1RSTR2, 0x3C) +REG32(APB2RSTR, 0x40) +REG32(AHB1ENR, 0x48) + /* DMA2DEN: reserved for STM32L475xx */ + FIELD(AHB1ENR, TSCEN, 16, 1) + FIELD(AHB1ENR, CRCEN, 12, 1) + FIELD(AHB1ENR, FLASHEN, 8, 1) + FIELD(AHB1ENR, DMA2EN, 1, 1) + FIELD(AHB1ENR, DMA1EN, 0, 1) +REG32(AHB2ENR, 0x4C) + FIELD(AHB2ENR, RNGEN, 18, 1) + /* HASHEN: reserved for STM32L475xx */ + FIELD(AHB2ENR, AESEN, 16, 1) + /* DCMIEN: reserved for STM32L475xx */ + FIELD(AHB2ENR, ADCEN, 13, 1) + FIELD(AHB2ENR, OTGFSEN, 12, 1) + /* GPIOIEN: reserved for STM32L475xx */ + FIELD(AHB2ENR, GPIOHEN, 7, 1) + FIELD(AHB2ENR, GPIOGEN, 6, 1) + FIELD(AHB2ENR, GPIOFEN, 5, 1) + FIELD(AHB2ENR, GPIOEEN, 4, 1) + FIELD(AHB2ENR, GPIODEN, 3, 1) + FIELD(AHB2ENR, GPIOCEN, 2, 1) + FIELD(AHB2ENR, GPIOBEN, 1, 1) + FIELD(AHB2ENR, GPIOAEN, 0, 1) +REG32(AHB3ENR, 0x50) + FIELD(AHB3ENR, QSPIEN, 8, 1) + FIELD(AHB3ENR, FMCEN, 0, 1) +REG32(APB1ENR1, 0x58) + FIELD(APB1ENR1, LPTIM1EN, 31, 1) + FIELD(APB1ENR1, OPAMPEN, 30, 1) + FIELD(APB1ENR1, DAC1EN, 29, 1) + FIELD(APB1ENR1, PWREN, 28, 1) + FIELD(APB1ENR1, CAN2EN, 26, 1) + FIELD(APB1ENR1, CAN1EN, 25, 1) + /* CRSEN: reserved for STM32L475xx */ + FIELD(APB1ENR1, I2C3EN, 23, 1) + FIELD(APB1ENR1, I2C2EN, 22, 1) + FIELD(APB1ENR1, I2C1EN, 21, 1) + FIELD(APB1ENR1, UART5EN, 20, 1) + FIELD(APB1ENR1, UART4EN, 19, 1) + FIELD(APB1ENR1, USART3EN, 18, 1) + FIELD(APB1ENR1, USART2EN, 17, 1) + FIELD(APB1ENR1, SPI3EN, 15, 1) + FIELD(APB1ENR1, SPI2EN, 14, 1) + FIELD(APB1ENR1, WWDGEN, 11, 1) + /* RTCAPBEN: reserved for STM32L475xx */ + FIELD(APB1ENR1, LCDEN, 9, 1) + FIELD(APB1ENR1, TIM7EN, 5, 1) + FIELD(APB1ENR1, TIM6EN, 4, 1) + FIELD(APB1ENR1, TIM5EN, 3, 1) + FIELD(APB1ENR1, TIM4EN, 2, 1) + FIELD(APB1ENR1, TIM3EN, 1, 1) + FIELD(APB1ENR1, TIM2EN, 0, 1) +REG32(APB1ENR2, 0x5C) + FIELD(APB1ENR2, LPTIM2EN, 5, 1) + FIELD(APB1ENR2, SWPMI1EN, 2, 1) + /* I2C4EN: reserved for STM32L475xx */ + FIELD(APB1ENR2, LPUART1EN, 0, 1) +REG32(APB2ENR, 0x60) + FIELD(APB2ENR, DFSDM1EN, 24, 1) + FIELD(APB2ENR, SAI2EN, 22, 1) + FIELD(APB2ENR, SAI1EN, 21, 1) + FIELD(APB2ENR, TIM17EN, 18, 1) + FIELD(APB2ENR, TIM16EN, 17, 1) + FIELD(APB2ENR, TIM15EN, 16, 1) + FIELD(APB2ENR, USART1EN, 14, 1) + FIELD(APB2ENR, TIM8EN, 13, 1) + FIELD(APB2ENR, SPI1EN, 12, 1) + FIELD(APB2ENR, TIM1EN, 11, 1) + FIELD(APB2ENR, SDMMC1EN, 10, 1) + FIELD(APB2ENR, FWEN, 7, 1) + FIELD(APB2ENR, SYSCFGEN, 0, 1) +REG32(AHB1SMENR, 0x68) +REG32(AHB2SMENR, 0x6C) +REG32(AHB3SMENR, 0x70) +REG32(APB1SMENR1, 0x78) +REG32(APB1SMENR2, 0x7C) +REG32(APB2SMENR, 0x80) +REG32(CCIPR, 0x88) + FIELD(CCIPR, DFSDM1SEL, 31, 1) + FIELD(CCIPR, SWPMI1SEL, 30, 1) + FIELD(CCIPR, ADCSEL, 28, 2) + FIELD(CCIPR, CLK48SEL, 26, 2) + FIELD(CCIPR, SAI2SEL, 24, 2) + FIELD(CCIPR, SAI1SEL, 22, 2) + FIELD(CCIPR, LPTIM2SEL, 20, 2) + FIELD(CCIPR, LPTIM1SEL, 18, 2) + FIELD(CCIPR, I2C3SEL, 16, 2) + FIELD(CCIPR, I2C2SEL, 14, 2) + FIELD(CCIPR, I2C1SEL, 12, 2) + FIELD(CCIPR, LPUART1SEL, 10, 2) + FIELD(CCIPR, UART5SEL, 8, 2) + FIELD(CCIPR, UART4SEL, 6, 2) + FIELD(CCIPR, USART3SEL, 4, 2) + FIELD(CCIPR, USART2SEL, 2, 2) + FIELD(CCIPR, USART1SEL, 0, 2) +REG32(BDCR, 0x90) + FIELD(BDCR, LSCOSEL, 25, 1) + FIELD(BDCR, LSCOEN, 24, 1) + FIELD(BDCR, BDRST, 16, 1) + FIELD(BDCR, RTCEN, 15, 1) + FIELD(BDCR, RTCSEL, 8, 2) + FIELD(BDCR, LSECSSD, 6, 1) + FIELD(BDCR, LSECSSON, 5, 1) + FIELD(BDCR, LSEDRV, 3, 2) + FIELD(BDCR, LSEBYP, 2, 1) + FIELD(BDCR, LSERDY, 1, 1) + FIELD(BDCR, LSEON, 0, 1) +REG32(CSR, 0x94) + FIELD(CSR, LPWRRSTF, 31, 1) + FIELD(CSR, WWDGRSTF, 30, 1) + FIELD(CSR, IWWGRSTF, 29, 1) + FIELD(CSR, SFTRSTF, 28, 1) + FIELD(CSR, BORRSTF, 27, 1) + FIELD(CSR, PINRSTF, 26, 1) + FIELD(CSR, OBLRSTF, 25, 1) + FIELD(CSR, FWRSTF, 24, 1) + FIELD(CSR, RMVF, 23, 1) + FIELD(CSR, MSISRANGE, 8, 4) + FIELD(CSR, LSIRDY, 1, 1) + FIELD(CSR, LSION, 0, 1) +/* CRRCR and CCIPR2 registers are present on L496/L4A6 devices only. */ + +/* Read Only masks to prevent writes in unauthorized bits */ +#define CR_READ_ONLY_MASK (R_CR_PLLSAI2RDY_MASK | \ + R_CR_PLLSAI1RDY_MASK | \ + R_CR_PLLRDY_MASK | \ + R_CR_HSERDY_MASK | \ + R_CR_HSIRDY_MASK | \ + R_CR_MSIRDY_MASK) +#define CR_READ_SET_MASK (R_CR_CSSON_MASK | R_CR_MSIRGSEL_MASK) +#define ICSCR_READ_ONLY_MASK (R_ICSCR_HSICAL_MASK | R_ICSCR_MSICAL_MASK) +#define CFGR_READ_ONLY_MASK (R_CFGR_SWS_MASK) +#define CIFR_READ_ONLY_MASK (R_CIFR_LSECSSF_MASK | \ + R_CIFR_CSSF_MASK | \ + R_CIFR_PLLSAI2RDYF_MASK | \ + R_CIFR_PLLSAI1RDYF_MASK | \ + R_CIFR_PLLRDYF_MASK | \ + R_CIFR_HSERDYF_MASK | \ + R_CIFR_HSIRDYF_MASK | \ + R_CIFR_MSIRDYF_MASK | \ + R_CIFR_LSERDYF_MASK | \ + R_CIFR_LSIRDYF_MASK) +#define CIFR_IRQ_MASK CIFR_READ_ONLY_MASK +#define APB2ENR_READ_SET_MASK (R_APB2ENR_FWEN_MASK) +#define BDCR_READ_ONLY_MASK (R_BDCR_LSECSSD_MASK | R_BDCR_LSERDY_MASK) +#define CSR_READ_ONLY_MASK (R_CSR_LPWRRSTF_MASK | \ + R_CSR_WWDGRSTF_MASK | \ + R_CSR_IWWGRSTF_MASK | \ + R_CSR_SFTRSTF_MASK | \ + R_CSR_BORRSTF_MASK | \ + R_CSR_PINRSTF_MASK | \ + R_CSR_OBLRSTF_MASK | \ + R_CSR_FWRSTF_MASK | \ + R_CSR_LSIRDY_MASK) + +/* Pll Channels */ +enum PllChannels { + RCC_PLL_CHANNEL_PLLSAI3CLK = 0, + RCC_PLL_CHANNEL_PLL48M1CLK = 1, + RCC_PLL_CHANNEL_PLLCLK = 2, +}; + +enum PllSai1Channels { + RCC_PLLSAI1_CHANNEL_PLLSAI1CLK = 0, + RCC_PLLSAI1_CHANNEL_PLL48M2CLK = 1, + RCC_PLLSAI1_CHANNEL_PLLADC1CLK = 2, +}; + +enum PllSai2Channels { + RCC_PLLSAI2_CHANNEL_PLLSAI2CLK = 0, + /* No Q channel */ + RCC_PLLSAI2_CHANNEL_PLLADC2CLK = 2, +}; + +typedef enum RccClockMuxSource { + RCC_CLOCK_MUX_SRC_GND = 0, + RCC_CLOCK_MUX_SRC_HSI, + RCC_CLOCK_MUX_SRC_HSE, + RCC_CLOCK_MUX_SRC_MSI, + RCC_CLOCK_MUX_SRC_LSI, + RCC_CLOCK_MUX_SRC_LSE, + RCC_CLOCK_MUX_SRC_SAI1_EXTCLK, + RCC_CLOCK_MUX_SRC_SAI2_EXTCLK, + RCC_CLOCK_MUX_SRC_PLL, + RCC_CLOCK_MUX_SRC_PLLSAI1, + RCC_CLOCK_MUX_SRC_PLLSAI2, + RCC_CLOCK_MUX_SRC_PLLSAI3, + RCC_CLOCK_MUX_SRC_PLL48M1, + RCC_CLOCK_MUX_SRC_PLL48M2, + RCC_CLOCK_MUX_SRC_PLLADC1, + RCC_CLOCK_MUX_SRC_PLLADC2, + RCC_CLOCK_MUX_SRC_SYSCLK, + RCC_CLOCK_MUX_SRC_HCLK, + RCC_CLOCK_MUX_SRC_PCLK1, + RCC_CLOCK_MUX_SRC_PCLK2, + RCC_CLOCK_MUX_SRC_HSE_OVER_32, + RCC_CLOCK_MUX_SRC_LCD_AND_RTC_COMMON, + + RCC_CLOCK_MUX_SRC_NUMBER, +} RccClockMuxSource; + +/* PLL init info */ +typedef struct PllInitInfo { + const char *name; + + const char *channel_name[RCC_NUM_CHANNEL_PLL_OUT]; + bool channel_exists[RCC_NUM_CHANNEL_PLL_OUT]; + uint32_t default_channel_divider[RCC_NUM_CHANNEL_PLL_OUT]; + + RccClockMuxSource src_mapping[RCC_NUM_CLOCK_MUX_SRC]; +} PllInitInfo; + +static const PllInitInfo PLL_INIT_INFO[] = { + [RCC_PLL_PLL] = { + .name = "pll", + .channel_name = { + "pllsai3clk", + "pll48m1clk", + "pllclk" + }, + .channel_exists = { + true, true, true + }, + /* From PLLCFGR register documentation */ + .default_channel_divider = { + 7, 2, 2 + } + }, + [RCC_PLL_PLLSAI1] = { + .name = "pllsai1", + .channel_name = { + "pllsai1clk", + "pll48m2clk", + "plladc1clk" + }, + .channel_exists = { + true, true, true + }, + /* From PLLSAI1CFGR register documentation */ + .default_channel_divider = { + 7, 2, 2 + } + }, + [RCC_PLL_PLLSAI2] = { + .name = "pllsai2", + .channel_name = { + "pllsai2clk", + NULL, + "plladc2clk" + }, + .channel_exists = { + true, false, true + }, + /* From PLLSAI2CFGR register documentation */ + .default_channel_divider = { + 7, 0, 2 + } + } +}; + +static inline void set_pll_init_info(RccPllState *pll, + RccPll id) +{ + int i; + + pll->id = id; + pll->vco_multiplier = 1; + for (i = 0; i < RCC_NUM_CHANNEL_PLL_OUT; i++) { + pll->channel_enabled[i] = false; + pll->channel_exists[i] = PLL_INIT_INFO[id].channel_exists[i]; + pll->channel_divider[i] = PLL_INIT_INFO[id].default_channel_divider[i]; + } +} + +/* Clock mux init info */ +typedef struct ClockMuxInitInfo { + const char *name; + + uint32_t multiplier; + uint32_t divider; + bool enabled; + /* If this is true, the clock will not be exposed outside of the device */ + bool hidden; + + RccClockMuxSource src_mapping[RCC_NUM_CLOCK_MUX_SRC]; +} ClockMuxInitInfo; + +#define FILL_DEFAULT_FACTOR \ + .multiplier = 1, \ + .divider = 1 + +#define FILL_DEFAULT_INIT_ENABLED \ + FILL_DEFAULT_FACTOR, \ + .enabled = true + +#define FILL_DEFAULT_INIT_DISABLED \ + FILL_DEFAULT_FACTOR, \ + .enabled = false + + +static const ClockMuxInitInfo CLOCK_MUX_INIT_INFO[] = { + [RCC_CLOCK_MUX_SYSCLK] = { + .name = "sysclk", + /* Same mapping as: CFGR_SW */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_MSI, + RCC_CLOCK_MUX_SRC_HSI, + RCC_CLOCK_MUX_SRC_HSE, + RCC_CLOCK_MUX_SRC_PLL, + }, + .hidden = true, + FILL_DEFAULT_INIT_ENABLED, + }, + [RCC_CLOCK_MUX_PLL_INPUT] = { + .name = "pll-input", + /* Same mapping as: PLLCFGR_PLLSRC */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_MSI, + RCC_CLOCK_MUX_SRC_HSI, + RCC_CLOCK_MUX_SRC_HSE, + }, + .hidden = true, + FILL_DEFAULT_INIT_ENABLED, + }, + [RCC_CLOCK_MUX_HCLK] = { + .name = "hclk", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + .hidden = true, + FILL_DEFAULT_INIT_ENABLED, + }, + [RCC_CLOCK_MUX_PCLK1] = { + .name = "pclk1", + .src_mapping = { + RCC_CLOCK_MUX_SRC_HCLK, + }, + .hidden = true, + FILL_DEFAULT_INIT_ENABLED, + }, + [RCC_CLOCK_MUX_PCLK2] = { + .name = "pclk2", + .src_mapping = { + RCC_CLOCK_MUX_SRC_HCLK, + }, + .hidden = true, + FILL_DEFAULT_INIT_ENABLED, + }, + [RCC_CLOCK_MUX_HSE_OVER_32] = { + .name = "hse-divided-by-32", + .multiplier = 1, + .divider = 32, + .enabled = true, + .src_mapping = { + RCC_CLOCK_MUX_SRC_HSE, + }, + .hidden = true, + }, + [RCC_CLOCK_MUX_LCD_AND_RTC_COMMON] = { + .name = "lcd-and-rtc-common-mux", + /* Same mapping as: BDCR_RTCSEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_GND, + RCC_CLOCK_MUX_SRC_LSE, + RCC_CLOCK_MUX_SRC_LSI, + RCC_CLOCK_MUX_SRC_HSE_OVER_32, + }, + .hidden = true, + FILL_DEFAULT_INIT_ENABLED, + }, + /* From now on, muxes with a publicly available output */ + [RCC_CLOCK_MUX_CORTEX_REFCLK] = { + .name = "cortex-refclk", + .multiplier = 1, + /* REFCLK is always HCLK/8 */ + .divider = 8, + .enabled = true, + .src_mapping = { + RCC_CLOCK_MUX_SRC_HCLK, + } + }, + [RCC_CLOCK_MUX_USART1] = { + .name = "usart1", + /* Same mapping as: CCIPR_USART1SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK2, + RCC_CLOCK_MUX_SRC_SYSCLK, + RCC_CLOCK_MUX_SRC_HSI, + RCC_CLOCK_MUX_SRC_LSE, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_USART2] = { + .name = "usart2", + /* Same mapping as: CCIPR_USART2SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + RCC_CLOCK_MUX_SRC_SYSCLK, + RCC_CLOCK_MUX_SRC_HSI, + RCC_CLOCK_MUX_SRC_LSE, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_USART3] = { + .name = "usart3", + /* Same mapping as: CCIPR_USART3SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + RCC_CLOCK_MUX_SRC_SYSCLK, + RCC_CLOCK_MUX_SRC_HSI, + RCC_CLOCK_MUX_SRC_LSE, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_UART4] = { + .name = "uart4", + /* Same mapping as: CCIPR_UART4SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + RCC_CLOCK_MUX_SRC_SYSCLK, + RCC_CLOCK_MUX_SRC_HSI, + RCC_CLOCK_MUX_SRC_LSE, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_UART5] = { + .name = "uart5", + /* Same mapping as: CCIPR_UART5SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + RCC_CLOCK_MUX_SRC_SYSCLK, + RCC_CLOCK_MUX_SRC_HSI, + RCC_CLOCK_MUX_SRC_LSE, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_LPUART1] = { + .name = "lpuart1", + /* Same mapping as: CCIPR_LPUART1SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + RCC_CLOCK_MUX_SRC_SYSCLK, + RCC_CLOCK_MUX_SRC_HSI, + RCC_CLOCK_MUX_SRC_LSE, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_I2C1] = { + .name = "i2c1", + /* Same mapping as: CCIPR_I2C1SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + RCC_CLOCK_MUX_SRC_SYSCLK, + RCC_CLOCK_MUX_SRC_HSI, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_I2C2] = { + .name = "i2c2", + /* Same mapping as: CCIPR_I2C2SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + RCC_CLOCK_MUX_SRC_SYSCLK, + RCC_CLOCK_MUX_SRC_HSI, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_I2C3] = { + .name = "i2c3", + /* Same mapping as: CCIPR_I2C3SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + RCC_CLOCK_MUX_SRC_SYSCLK, + RCC_CLOCK_MUX_SRC_HSI, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_LPTIM1] = { + .name = "lptim1", + /* Same mapping as: CCIPR_LPTIM1SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + RCC_CLOCK_MUX_SRC_LSI, + RCC_CLOCK_MUX_SRC_HSI, + RCC_CLOCK_MUX_SRC_LSE, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_LPTIM2] = { + .name = "lptim2", + /* Same mapping as: CCIPR_LPTIM2SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + RCC_CLOCK_MUX_SRC_LSI, + RCC_CLOCK_MUX_SRC_HSI, + RCC_CLOCK_MUX_SRC_LSE, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_SWPMI1] = { + .name = "swpmi1", + /* Same mapping as: CCIPR_SWPMI1SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + RCC_CLOCK_MUX_SRC_HSI, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_MCO] = { + .name = "mco", + /* Same mapping as: CFGR_MCOSEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + RCC_CLOCK_MUX_SRC_MSI, + RCC_CLOCK_MUX_SRC_HSI, + RCC_CLOCK_MUX_SRC_HSE, + RCC_CLOCK_MUX_SRC_PLL, + RCC_CLOCK_MUX_SRC_LSI, + RCC_CLOCK_MUX_SRC_LSE, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_LSCO] = { + .name = "lsco", + /* Same mapping as: BDCR_LSCOSEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_LSI, + RCC_CLOCK_MUX_SRC_LSE, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_DFSDM1] = { + .name = "dfsdm1", + /* Same mapping as: CCIPR_DFSDM1SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK2, + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_ADC] = { + .name = "adc", + /* Same mapping as: CCIPR_ADCSEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_GND, + RCC_CLOCK_MUX_SRC_PLLADC1, + RCC_CLOCK_MUX_SRC_PLLADC2, + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_CLK48] = { + .name = "clk48", + /* Same mapping as: CCIPR_CLK48SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_GND, + RCC_CLOCK_MUX_SRC_PLL48M2, + RCC_CLOCK_MUX_SRC_PLL48M1, + RCC_CLOCK_MUX_SRC_MSI, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_SAI2] = { + .name = "sai2", + /* Same mapping as: CCIPR_SAI2SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PLLSAI1, + RCC_CLOCK_MUX_SRC_PLLSAI2, + RCC_CLOCK_MUX_SRC_PLLSAI3, + RCC_CLOCK_MUX_SRC_SAI2_EXTCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_SAI1] = { + .name = "sai1", + /* Same mapping as: CCIPR_SAI1SEL */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_PLLSAI1, + RCC_CLOCK_MUX_SRC_PLLSAI2, + RCC_CLOCK_MUX_SRC_PLLSAI3, + RCC_CLOCK_MUX_SRC_SAI1_EXTCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + /* From now on, these muxes only have one valid source */ + [RCC_CLOCK_MUX_TSC] = { + .name = "tsc", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_CRC] = { + .name = "crc", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_FLASH] = { + .name = "flash", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_DMA2] = { + .name = "dma2", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_DMA1] = { + .name = "dma1", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_RNG] = { + .name = "rng", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_AES] = { + .name = "aes", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_OTGFS] = { + .name = "otgfs", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_GPIOA] = { + .name = "gpioa", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_GPIOB] = { + .name = "gpiob", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_GPIOC] = { + .name = "gpioc", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_GPIOD] = { + .name = "gpiod", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_GPIOE] = { + .name = "gpioe", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_GPIOF] = { + .name = "gpiof", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_GPIOG] = { + .name = "gpiog", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_GPIOH] = { + .name = "gpioh", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_QSPI] = { + .name = "qspi", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_FMC] = { + .name = "fmc", + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_OPAMP] = { + .name = "opamp", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_DAC1] = { + .name = "dac1", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_PWR] = { + .name = "pwr", + /* + * PWREN is in the APB1ENR1 register, + * but PWR uses SYSCLK according to the clock tree. + */ + .src_mapping = { + RCC_CLOCK_MUX_SRC_SYSCLK, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_CAN1] = { + .name = "can1", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_SPI3] = { + .name = "spi3", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_SPI2] = { + .name = "spi2", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_WWDG] = { + .name = "wwdg", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_LCD] = { + .name = "lcd", + .src_mapping = { + RCC_CLOCK_MUX_SRC_LCD_AND_RTC_COMMON, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_TIM7] = { + .name = "tim7", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_TIM6] = { + .name = "tim6", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_TIM5] = { + .name = "tim5", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_TIM4] = { + .name = "tim4", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_TIM3] = { + .name = "tim3", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_TIM2] = { + .name = "tim2", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK1, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_TIM17] = { + .name = "tim17", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK2, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_TIM16] = { + .name = "tim16", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK2, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_TIM15] = { + .name = "tim15", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK2, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_TIM8] = { + .name = "tim8", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK2, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_SPI1] = { + .name = "spi1", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK2, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_TIM1] = { + .name = "tim1", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK2, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_SDMMC1] = { + .name = "sdmmc1", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK2, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_FW] = { + .name = "fw", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK2, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_SYSCFG] = { + .name = "syscfg", + .src_mapping = { + RCC_CLOCK_MUX_SRC_PCLK2, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_RTC] = { + .name = "rtc", + .src_mapping = { + RCC_CLOCK_MUX_SRC_LCD_AND_RTC_COMMON, + }, + FILL_DEFAULT_INIT_DISABLED, + }, + [RCC_CLOCK_MUX_CORTEX_FCLK] = { + .name = "cortex-fclk", + .src_mapping = { + RCC_CLOCK_MUX_SRC_HCLK, + }, + FILL_DEFAULT_INIT_ENABLED, + }, +}; + +static inline void set_clock_mux_init_info(RccClockMuxState *mux, + RccClockMux id) +{ + mux->id = id; + mux->multiplier = CLOCK_MUX_INIT_INFO[id].multiplier; + mux->divider = CLOCK_MUX_INIT_INFO[id].divider; + mux->enabled = CLOCK_MUX_INIT_INFO[id].enabled; + /* + * Every peripheral has the first source of their source list as + * as their default source. + */ + mux->src = 0; +} + +#endif /* HW_STM32L4X5_RCC_INTERNALS_H */ diff --git a/include/hw/misc/stm32l4x5_syscfg.h b/include/hw/misc/stm32l4x5_syscfg.h new file mode 100644 index 0000000000..23bb564150 --- /dev/null +++ b/include/hw/misc/stm32l4x5_syscfg.h @@ -0,0 +1,53 @@ +/* + * STM32L4x5 SYSCFG (System Configuration Controller) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This work is based on the stm32f4xx_syscfg by Alistair Francis. + * Original code is licensed under the MIT License: + * + * Copyright (c) 2014 Alistair Francis + */ + +/* + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html + */ + +#ifndef HW_STM32L4X5_SYSCFG_H +#define HW_STM32L4X5_SYSCFG_H + +#include "hw/sysbus.h" +#include "qom/object.h" +#include "hw/gpio/stm32l4x5_gpio.h" + +#define TYPE_STM32L4X5_SYSCFG "stm32l4x5-syscfg" +OBJECT_DECLARE_SIMPLE_TYPE(Stm32l4x5SyscfgState, STM32L4X5_SYSCFG) + +#define SYSCFG_NUM_EXTICR 4 + +struct Stm32l4x5SyscfgState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t memrmp; + uint32_t cfgr1; + uint32_t exticr[SYSCFG_NUM_EXTICR]; + uint32_t scsr; + uint32_t cfgr2; + uint32_t swpr; + uint32_t skr; + uint32_t swpr2; + + qemu_irq gpio_out[GPIO_NUM_PINS]; +}; + +#endif diff --git a/include/hw/misc/virt_ctrl.h b/include/hw/misc/virt_ctrl.h index 25a237e518..81346cf017 100644 --- a/include/hw/misc/virt_ctrl.h +++ b/include/hw/misc/virt_ctrl.h @@ -7,6 +7,8 @@ #ifndef VIRT_CTRL_H #define VIRT_CTRL_H +#include "hw/sysbus.h" + #define TYPE_VIRT_CTRL "virt-ctrl" OBJECT_DECLARE_SIMPLE_TYPE(VirtCtrlState, VIRT_CTRL) diff --git a/include/hw/misc/xlnx-cfi-if.h b/include/hw/misc/xlnx-cfi-if.h new file mode 100644 index 0000000000..f9bd12292d --- /dev/null +++ b/include/hw/misc/xlnx-cfi-if.h @@ -0,0 +1,59 @@ +/* + * Xilinx CFI interface + * + * Copyright (C) 2023, Advanced Micro Devices, Inc. + * + * Written by Francisco Iglesias + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef XLNX_CFI_IF_H +#define XLNX_CFI_IF_H 1 + +#include "qemu/help-texts.h" +#include "hw/hw.h" +#include "qom/object.h" + +#define TYPE_XLNX_CFI_IF "xlnx-cfi-if" +typedef struct XlnxCfiIfClass XlnxCfiIfClass; +DECLARE_CLASS_CHECKERS(XlnxCfiIfClass, XLNX_CFI_IF, TYPE_XLNX_CFI_IF) + +#define XLNX_CFI_IF(obj) \ + INTERFACE_CHECK(XlnxCfiIf, (obj), TYPE_XLNX_CFI_IF) + +typedef enum { + PACKET_TYPE_CFU = 0x52, + PACKET_TYPE_CFRAME = 0xA1, +} xlnx_cfi_packet_type; + +typedef enum { + CFRAME_FAR = 1, + CFRAME_SFR = 2, + CFRAME_FDRI = 4, + CFRAME_CMD = 6, +} xlnx_cfi_reg_addr; + +typedef struct XlnxCfiPacket { + uint8_t reg_addr; + uint32_t data[4]; +} XlnxCfiPacket; + +typedef struct XlnxCfiIf { + Object Parent; +} XlnxCfiIf; + +typedef struct XlnxCfiIfClass { + InterfaceClass parent; + + void (*cfi_transfer_packet)(XlnxCfiIf *cfi_if, XlnxCfiPacket *pkt); +} XlnxCfiIfClass; + +/** + * Transfer a XlnxCfiPacket. + * + * @cfi_if: the object implementing this interface + * @XlnxCfiPacket: a pointer to the XlnxCfiPacket to transfer + */ +void xlnx_cfi_transfer_packet(XlnxCfiIf *cfi_if, XlnxCfiPacket *pkt); + +#endif /* XLNX_CFI_IF_H */ diff --git a/include/hw/misc/xlnx-versal-cframe-reg.h b/include/hw/misc/xlnx-versal-cframe-reg.h new file mode 100644 index 0000000000..83f6a07744 --- /dev/null +++ b/include/hw/misc/xlnx-versal-cframe-reg.h @@ -0,0 +1,303 @@ +/* + * QEMU model of the Configuration Frame Control module + * + * Copyright (C) 2023, Advanced Micro Devices, Inc. + * + * Written by Francisco Iglesias + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * References: + * [1] Versal ACAP Technical Reference Manual, + * https://www.xilinx.com/support/documentation/architecture-manuals/am011-versal-acap-trm.pdf + * + * [2] Versal ACAP Register Reference, + * https://docs.xilinx.com/r/en-US/am012-versal-register-reference/CFRAME_REG-Module + */ +#ifndef HW_MISC_XLNX_VERSAL_CFRAME_REG_H +#define HW_MISC_XLNX_VERSAL_CFRAME_REG_H + +#include "hw/sysbus.h" +#include "hw/register.h" +#include "hw/misc/xlnx-cfi-if.h" +#include "hw/misc/xlnx-versal-cfu.h" +#include "qemu/fifo32.h" + +#define TYPE_XLNX_VERSAL_CFRAME_REG "xlnx-cframe-reg" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalCFrameReg, XLNX_VERSAL_CFRAME_REG) + +#define TYPE_XLNX_VERSAL_CFRAME_BCAST_REG "xlnx.cframe-bcast-reg" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalCFrameBcastReg, + XLNX_VERSAL_CFRAME_BCAST_REG) + +/* + * The registers in this module are 128 bits wide but it is ok to write + * and read them through 4 sequential 32 bit accesses (address[3:2] = 0, + * 1, 2, 3). + */ +REG32(CRC0, 0x0) + FIELD(CRC, CRC, 0, 32) +REG32(CRC1, 0x4) +REG32(CRC2, 0x8) +REG32(CRC3, 0xc) +REG32(FAR0, 0x10) + FIELD(FAR0, SEGMENT, 23, 2) + FIELD(FAR0, BLOCKTYPE, 20, 3) + FIELD(FAR0, FRAME_ADDR, 0, 20) +REG32(FAR1, 0x14) +REG32(FAR2, 0x18) +REG32(FAR3, 0x1c) +REG32(FAR_SFR0, 0x20) + FIELD(FAR_SFR0, BLOCKTYPE, 20, 3) + FIELD(FAR_SFR0, FRAME_ADDR, 0, 20) +REG32(FAR_SFR1, 0x24) +REG32(FAR_SFR2, 0x28) +REG32(FAR_SFR3, 0x2c) +REG32(FDRI0, 0x40) +REG32(FDRI1, 0x44) +REG32(FDRI2, 0x48) +REG32(FDRI3, 0x4c) +REG32(FRCNT0, 0x50) + FIELD(FRCNT0, FRCNT, 0, 32) +REG32(FRCNT1, 0x54) +REG32(FRCNT2, 0x58) +REG32(FRCNT3, 0x5c) +REG32(CMD0, 0x60) + FIELD(CMD0, CMD, 0, 5) +REG32(CMD1, 0x64) +REG32(CMD2, 0x68) +REG32(CMD3, 0x6c) +REG32(CR_MASK0, 0x70) +REG32(CR_MASK1, 0x74) +REG32(CR_MASK2, 0x78) +REG32(CR_MASK3, 0x7c) +REG32(CTL0, 0x80) + FIELD(CTL, PER_FRAME_CRC, 0, 1) +REG32(CTL1, 0x84) +REG32(CTL2, 0x88) +REG32(CTL3, 0x8c) +REG32(CFRM_ISR0, 0x150) + FIELD(CFRM_ISR0, READ_BROADCAST_ERROR, 21, 1) + FIELD(CFRM_ISR0, CMD_MISSING_ERROR, 20, 1) + FIELD(CFRM_ISR0, RW_ROWOFF_ERROR, 19, 1) + FIELD(CFRM_ISR0, READ_REG_ADDR_ERROR, 18, 1) + FIELD(CFRM_ISR0, READ_BLK_TYPE_ERROR, 17, 1) + FIELD(CFRM_ISR0, READ_FRAME_ADDR_ERROR, 16, 1) + FIELD(CFRM_ISR0, WRITE_REG_ADDR_ERROR, 15, 1) + FIELD(CFRM_ISR0, WRITE_BLK_TYPE_ERROR, 13, 1) + FIELD(CFRM_ISR0, WRITE_FRAME_ADDR_ERROR, 12, 1) + FIELD(CFRM_ISR0, MFW_OVERRUN_ERROR, 11, 1) + FIELD(CFRM_ISR0, FAR_FIFO_UNDERFLOW, 10, 1) + FIELD(CFRM_ISR0, FAR_FIFO_OVERFLOW, 9, 1) + FIELD(CFRM_ISR0, PER_FRAME_SEQ_ERROR, 8, 1) + FIELD(CFRM_ISR0, CRC_ERROR, 7, 1) + FIELD(CFRM_ISR0, WRITE_OVERRUN_ERROR, 6, 1) + FIELD(CFRM_ISR0, READ_OVERRUN_ERROR, 5, 1) + FIELD(CFRM_ISR0, CMD_INTERRUPT_ERROR, 4, 1) + FIELD(CFRM_ISR0, WRITE_INTERRUPT_ERROR, 3, 1) + FIELD(CFRM_ISR0, READ_INTERRUPT_ERROR, 2, 1) + FIELD(CFRM_ISR0, SEU_CRC_ERROR, 1, 1) + FIELD(CFRM_ISR0, SEU_ECC_ERROR, 0, 1) +REG32(CFRM_ISR1, 0x154) +REG32(CFRM_ISR2, 0x158) +REG32(CFRM_ISR3, 0x15c) +REG32(CFRM_IMR0, 0x160) + FIELD(CFRM_IMR0, READ_BROADCAST_ERROR, 21, 1) + FIELD(CFRM_IMR0, CMD_MISSING_ERROR, 20, 1) + FIELD(CFRM_IMR0, RW_ROWOFF_ERROR, 19, 1) + FIELD(CFRM_IMR0, READ_REG_ADDR_ERROR, 18, 1) + FIELD(CFRM_IMR0, READ_BLK_TYPE_ERROR, 17, 1) + FIELD(CFRM_IMR0, READ_FRAME_ADDR_ERROR, 16, 1) + FIELD(CFRM_IMR0, WRITE_REG_ADDR_ERROR, 15, 1) + FIELD(CFRM_IMR0, WRITE_BLK_TYPE_ERROR, 13, 1) + FIELD(CFRM_IMR0, WRITE_FRAME_ADDR_ERROR, 12, 1) + FIELD(CFRM_IMR0, MFW_OVERRUN_ERROR, 11, 1) + FIELD(CFRM_IMR0, FAR_FIFO_UNDERFLOW, 10, 1) + FIELD(CFRM_IMR0, FAR_FIFO_OVERFLOW, 9, 1) + FIELD(CFRM_IMR0, PER_FRAME_SEQ_ERROR, 8, 1) + FIELD(CFRM_IMR0, CRC_ERROR, 7, 1) + FIELD(CFRM_IMR0, WRITE_OVERRUN_ERROR, 6, 1) + FIELD(CFRM_IMR0, READ_OVERRUN_ERROR, 5, 1) + FIELD(CFRM_IMR0, CMD_INTERRUPT_ERROR, 4, 1) + FIELD(CFRM_IMR0, WRITE_INTERRUPT_ERROR, 3, 1) + FIELD(CFRM_IMR0, READ_INTERRUPT_ERROR, 2, 1) + FIELD(CFRM_IMR0, SEU_CRC_ERROR, 1, 1) + FIELD(CFRM_IMR0, SEU_ECC_ERROR, 0, 1) +REG32(CFRM_IMR1, 0x164) +REG32(CFRM_IMR2, 0x168) +REG32(CFRM_IMR3, 0x16c) +REG32(CFRM_IER0, 0x170) + FIELD(CFRM_IER0, READ_BROADCAST_ERROR, 21, 1) + FIELD(CFRM_IER0, CMD_MISSING_ERROR, 20, 1) + FIELD(CFRM_IER0, RW_ROWOFF_ERROR, 19, 1) + FIELD(CFRM_IER0, READ_REG_ADDR_ERROR, 18, 1) + FIELD(CFRM_IER0, READ_BLK_TYPE_ERROR, 17, 1) + FIELD(CFRM_IER0, READ_FRAME_ADDR_ERROR, 16, 1) + FIELD(CFRM_IER0, WRITE_REG_ADDR_ERROR, 15, 1) + FIELD(CFRM_IER0, WRITE_BLK_TYPE_ERROR, 13, 1) + FIELD(CFRM_IER0, WRITE_FRAME_ADDR_ERROR, 12, 1) + FIELD(CFRM_IER0, MFW_OVERRUN_ERROR, 11, 1) + FIELD(CFRM_IER0, FAR_FIFO_UNDERFLOW, 10, 1) + FIELD(CFRM_IER0, FAR_FIFO_OVERFLOW, 9, 1) + FIELD(CFRM_IER0, PER_FRAME_SEQ_ERROR, 8, 1) + FIELD(CFRM_IER0, CRC_ERROR, 7, 1) + FIELD(CFRM_IER0, WRITE_OVERRUN_ERROR, 6, 1) + FIELD(CFRM_IER0, READ_OVERRUN_ERROR, 5, 1) + FIELD(CFRM_IER0, CMD_INTERRUPT_ERROR, 4, 1) + FIELD(CFRM_IER0, WRITE_INTERRUPT_ERROR, 3, 1) + FIELD(CFRM_IER0, READ_INTERRUPT_ERROR, 2, 1) + FIELD(CFRM_IER0, SEU_CRC_ERROR, 1, 1) + FIELD(CFRM_IER0, SEU_ECC_ERROR, 0, 1) +REG32(CFRM_IER1, 0x174) +REG32(CFRM_IER2, 0x178) +REG32(CFRM_IER3, 0x17c) +REG32(CFRM_IDR0, 0x180) + FIELD(CFRM_IDR0, READ_BROADCAST_ERROR, 21, 1) + FIELD(CFRM_IDR0, CMD_MISSING_ERROR, 20, 1) + FIELD(CFRM_IDR0, RW_ROWOFF_ERROR, 19, 1) + FIELD(CFRM_IDR0, READ_REG_ADDR_ERROR, 18, 1) + FIELD(CFRM_IDR0, READ_BLK_TYPE_ERROR, 17, 1) + FIELD(CFRM_IDR0, READ_FRAME_ADDR_ERROR, 16, 1) + FIELD(CFRM_IDR0, WRITE_REG_ADDR_ERROR, 15, 1) + FIELD(CFRM_IDR0, WRITE_BLK_TYPE_ERROR, 13, 1) + FIELD(CFRM_IDR0, WRITE_FRAME_ADDR_ERROR, 12, 1) + FIELD(CFRM_IDR0, MFW_OVERRUN_ERROR, 11, 1) + FIELD(CFRM_IDR0, FAR_FIFO_UNDERFLOW, 10, 1) + FIELD(CFRM_IDR0, FAR_FIFO_OVERFLOW, 9, 1) + FIELD(CFRM_IDR0, PER_FRAME_SEQ_ERROR, 8, 1) + FIELD(CFRM_IDR0, CRC_ERROR, 7, 1) + FIELD(CFRM_IDR0, WRITE_OVERRUN_ERROR, 6, 1) + FIELD(CFRM_IDR0, READ_OVERRUN_ERROR, 5, 1) + FIELD(CFRM_IDR0, CMD_INTERRUPT_ERROR, 4, 1) + FIELD(CFRM_IDR0, WRITE_INTERRUPT_ERROR, 3, 1) + FIELD(CFRM_IDR0, READ_INTERRUPT_ERROR, 2, 1) + FIELD(CFRM_IDR0, SEU_CRC_ERROR, 1, 1) + FIELD(CFRM_IDR0, SEU_ECC_ERROR, 0, 1) +REG32(CFRM_IDR1, 0x184) +REG32(CFRM_IDR2, 0x188) +REG32(CFRM_IDR3, 0x18c) +REG32(CFRM_ITR0, 0x190) + FIELD(CFRM_ITR0, READ_BROADCAST_ERROR, 21, 1) + FIELD(CFRM_ITR0, CMD_MISSING_ERROR, 20, 1) + FIELD(CFRM_ITR0, RW_ROWOFF_ERROR, 19, 1) + FIELD(CFRM_ITR0, READ_REG_ADDR_ERROR, 18, 1) + FIELD(CFRM_ITR0, READ_BLK_TYPE_ERROR, 17, 1) + FIELD(CFRM_ITR0, READ_FRAME_ADDR_ERROR, 16, 1) + FIELD(CFRM_ITR0, WRITE_REG_ADDR_ERROR, 15, 1) + FIELD(CFRM_ITR0, WRITE_BLK_TYPE_ERROR, 13, 1) + FIELD(CFRM_ITR0, WRITE_FRAME_ADDR_ERROR, 12, 1) + FIELD(CFRM_ITR0, MFW_OVERRUN_ERROR, 11, 1) + FIELD(CFRM_ITR0, FAR_FIFO_UNDERFLOW, 10, 1) + FIELD(CFRM_ITR0, FAR_FIFO_OVERFLOW, 9, 1) + FIELD(CFRM_ITR0, PER_FRAME_SEQ_ERROR, 8, 1) + FIELD(CFRM_ITR0, CRC_ERROR, 7, 1) + FIELD(CFRM_ITR0, WRITE_OVERRUN_ERROR, 6, 1) + FIELD(CFRM_ITR0, READ_OVERRUN_ERROR, 5, 1) + FIELD(CFRM_ITR0, CMD_INTERRUPT_ERROR, 4, 1) + FIELD(CFRM_ITR0, WRITE_INTERRUPT_ERROR, 3, 1) + FIELD(CFRM_ITR0, READ_INTERRUPT_ERROR, 2, 1) + FIELD(CFRM_ITR0, SEU_CRC_ERROR, 1, 1) + FIELD(CFRM_ITR0, SEU_ECC_ERROR, 0, 1) +REG32(CFRM_ITR1, 0x194) +REG32(CFRM_ITR2, 0x198) +REG32(CFRM_ITR3, 0x19c) +REG32(SEU_SYNDRM00, 0x1a0) +REG32(SEU_SYNDRM01, 0x1a4) +REG32(SEU_SYNDRM02, 0x1a8) +REG32(SEU_SYNDRM03, 0x1ac) +REG32(SEU_SYNDRM10, 0x1b0) +REG32(SEU_SYNDRM11, 0x1b4) +REG32(SEU_SYNDRM12, 0x1b8) +REG32(SEU_SYNDRM13, 0x1bc) +REG32(SEU_SYNDRM20, 0x1c0) +REG32(SEU_SYNDRM21, 0x1c4) +REG32(SEU_SYNDRM22, 0x1c8) +REG32(SEU_SYNDRM23, 0x1cc) +REG32(SEU_SYNDRM30, 0x1d0) +REG32(SEU_SYNDRM31, 0x1d4) +REG32(SEU_SYNDRM32, 0x1d8) +REG32(SEU_SYNDRM33, 0x1dc) +REG32(SEU_VIRTUAL_SYNDRM0, 0x1e0) +REG32(SEU_VIRTUAL_SYNDRM1, 0x1e4) +REG32(SEU_VIRTUAL_SYNDRM2, 0x1e8) +REG32(SEU_VIRTUAL_SYNDRM3, 0x1ec) +REG32(SEU_CRC0, 0x1f0) +REG32(SEU_CRC1, 0x1f4) +REG32(SEU_CRC2, 0x1f8) +REG32(SEU_CRC3, 0x1fc) +REG32(CFRAME_FAR_BOT0, 0x200) +REG32(CFRAME_FAR_BOT1, 0x204) +REG32(CFRAME_FAR_BOT2, 0x208) +REG32(CFRAME_FAR_BOT3, 0x20c) +REG32(CFRAME_FAR_TOP0, 0x210) +REG32(CFRAME_FAR_TOP1, 0x214) +REG32(CFRAME_FAR_TOP2, 0x218) +REG32(CFRAME_FAR_TOP3, 0x21c) +REG32(LAST_FRAME_BOT0, 0x220) + FIELD(LAST_FRAME_BOT0, BLOCKTYPE1_LAST_FRAME_LSB, 20, 12) + FIELD(LAST_FRAME_BOT0, BLOCKTYPE0_LAST_FRAME, 0, 20) +REG32(LAST_FRAME_BOT1, 0x224) + FIELD(LAST_FRAME_BOT1, BLOCKTYPE3_LAST_FRAME_LSB, 28, 4) + FIELD(LAST_FRAME_BOT1, BLOCKTYPE2_LAST_FRAME, 8, 20) + FIELD(LAST_FRAME_BOT1, BLOCKTYPE1_LAST_FRAME_MSB, 0, 8) +REG32(LAST_FRAME_BOT2, 0x228) + FIELD(LAST_FRAME_BOT2, BLOCKTYPE3_LAST_FRAME_MSB, 0, 16) +REG32(LAST_FRAME_BOT3, 0x22c) +REG32(LAST_FRAME_TOP0, 0x230) + FIELD(LAST_FRAME_TOP0, BLOCKTYPE5_LAST_FRAME_LSB, 20, 12) + FIELD(LAST_FRAME_TOP0, BLOCKTYPE4_LAST_FRAME, 0, 20) +REG32(LAST_FRAME_TOP1, 0x234) + FIELD(LAST_FRAME_TOP1, BLOCKTYPE6_LAST_FRAME, 8, 20) + FIELD(LAST_FRAME_TOP1, BLOCKTYPE5_LAST_FRAME_MSB, 0, 8) +REG32(LAST_FRAME_TOP2, 0x238) +REG32(LAST_FRAME_TOP3, 0x23c) + +#define CFRAME_REG_R_MAX (R_LAST_FRAME_TOP3 + 1) + +#define FRAME_NUM_QWORDS 25 +#define FRAME_NUM_WORDS (FRAME_NUM_QWORDS * 4) /* 25 * 128 bits */ + +typedef struct XlnxCFrame { + uint32_t data[FRAME_NUM_WORDS]; +} XlnxCFrame; + +struct XlnxVersalCFrameReg { + SysBusDevice parent_obj; + MemoryRegion iomem; + MemoryRegion iomem_fdri; + qemu_irq irq_cfrm_imr; + + /* 128-bit wfifo. */ + uint32_t wfifo[WFIFO_SZ]; + + uint32_t regs[CFRAME_REG_R_MAX]; + RegisterInfo regs_info[CFRAME_REG_R_MAX]; + + bool rowon; + bool wcfg; + bool rcfg; + + GTree *cframes; + Fifo32 new_f_data; + + struct { + XlnxCfiIf *cfu_fdro; + uint32_t blktype_num_frames[7]; + } cfg; + bool row_configured; +}; + +struct XlnxVersalCFrameBcastReg { + SysBusDevice parent_obj; + MemoryRegion iomem_reg; + MemoryRegion iomem_fdri; + + /* 128-bit wfifo. */ + uint32_t wfifo[WFIFO_SZ]; + + struct { + XlnxCfiIf *cframe[15]; + } cfg; +}; + +#endif diff --git a/include/hw/misc/xlnx-versal-cfu.h b/include/hw/misc/xlnx-versal-cfu.h new file mode 100644 index 0000000000..3de3ee4923 --- /dev/null +++ b/include/hw/misc/xlnx-versal-cfu.h @@ -0,0 +1,258 @@ +/* + * QEMU model of the CFU Configuration Unit. + * + * Copyright (C) 2023, Advanced Micro Devices, Inc. + * + * Written by Francisco Iglesias + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * References: + * [1] Versal ACAP Technical Reference Manual, + * https://www.xilinx.com/support/documentation/architecture-manuals/am011-versal-acap-trm.pdf + * + * [2] Versal ACAP Register Reference, + * https://docs.xilinx.com/r/en-US/am012-versal-register-reference/CFU_CSR-Module + */ +#ifndef HW_MISC_XLNX_VERSAL_CFU_APB_H +#define HW_MISC_XLNX_VERSAL_CFU_APB_H + +#include "hw/sysbus.h" +#include "hw/register.h" +#include "hw/misc/xlnx-cfi-if.h" +#include "qemu/fifo32.h" + +#define TYPE_XLNX_VERSAL_CFU_APB "xlnx-versal-cfu-apb" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalCFUAPB, XLNX_VERSAL_CFU_APB) + +#define TYPE_XLNX_VERSAL_CFU_FDRO "xlnx-versal-cfu-fdro" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalCFUFDRO, XLNX_VERSAL_CFU_FDRO) + +#define TYPE_XLNX_VERSAL_CFU_SFR "xlnx-versal-cfu-sfr" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalCFUSFR, XLNX_VERSAL_CFU_SFR) + +REG32(CFU_ISR, 0x0) + FIELD(CFU_ISR, USR_GTS_EVENT, 9, 1) + FIELD(CFU_ISR, USR_GSR_EVENT, 8, 1) + FIELD(CFU_ISR, SLVERR, 7, 1) + FIELD(CFU_ISR, DECOMP_ERROR, 6, 1) + FIELD(CFU_ISR, BAD_CFI_PACKET, 5, 1) + FIELD(CFU_ISR, AXI_ALIGN_ERROR, 4, 1) + FIELD(CFU_ISR, CFI_ROW_ERROR, 3, 1) + FIELD(CFU_ISR, CRC32_ERROR, 2, 1) + FIELD(CFU_ISR, CRC8_ERROR, 1, 1) + FIELD(CFU_ISR, SEU_ENDOFCALIB, 0, 1) +REG32(CFU_IMR, 0x4) + FIELD(CFU_IMR, USR_GTS_EVENT, 9, 1) + FIELD(CFU_IMR, USR_GSR_EVENT, 8, 1) + FIELD(CFU_IMR, SLVERR, 7, 1) + FIELD(CFU_IMR, DECOMP_ERROR, 6, 1) + FIELD(CFU_IMR, BAD_CFI_PACKET, 5, 1) + FIELD(CFU_IMR, AXI_ALIGN_ERROR, 4, 1) + FIELD(CFU_IMR, CFI_ROW_ERROR, 3, 1) + FIELD(CFU_IMR, CRC32_ERROR, 2, 1) + FIELD(CFU_IMR, CRC8_ERROR, 1, 1) + FIELD(CFU_IMR, SEU_ENDOFCALIB, 0, 1) +REG32(CFU_IER, 0x8) + FIELD(CFU_IER, USR_GTS_EVENT, 9, 1) + FIELD(CFU_IER, USR_GSR_EVENT, 8, 1) + FIELD(CFU_IER, SLVERR, 7, 1) + FIELD(CFU_IER, DECOMP_ERROR, 6, 1) + FIELD(CFU_IER, BAD_CFI_PACKET, 5, 1) + FIELD(CFU_IER, AXI_ALIGN_ERROR, 4, 1) + FIELD(CFU_IER, CFI_ROW_ERROR, 3, 1) + FIELD(CFU_IER, CRC32_ERROR, 2, 1) + FIELD(CFU_IER, CRC8_ERROR, 1, 1) + FIELD(CFU_IER, SEU_ENDOFCALIB, 0, 1) +REG32(CFU_IDR, 0xc) + FIELD(CFU_IDR, USR_GTS_EVENT, 9, 1) + FIELD(CFU_IDR, USR_GSR_EVENT, 8, 1) + FIELD(CFU_IDR, SLVERR, 7, 1) + FIELD(CFU_IDR, DECOMP_ERROR, 6, 1) + FIELD(CFU_IDR, BAD_CFI_PACKET, 5, 1) + FIELD(CFU_IDR, AXI_ALIGN_ERROR, 4, 1) + FIELD(CFU_IDR, CFI_ROW_ERROR, 3, 1) + FIELD(CFU_IDR, CRC32_ERROR, 2, 1) + FIELD(CFU_IDR, CRC8_ERROR, 1, 1) + FIELD(CFU_IDR, SEU_ENDOFCALIB, 0, 1) +REG32(CFU_ITR, 0x10) + FIELD(CFU_ITR, USR_GTS_EVENT, 9, 1) + FIELD(CFU_ITR, USR_GSR_EVENT, 8, 1) + FIELD(CFU_ITR, SLVERR, 7, 1) + FIELD(CFU_ITR, DECOMP_ERROR, 6, 1) + FIELD(CFU_ITR, BAD_CFI_PACKET, 5, 1) + FIELD(CFU_ITR, AXI_ALIGN_ERROR, 4, 1) + FIELD(CFU_ITR, CFI_ROW_ERROR, 3, 1) + FIELD(CFU_ITR, CRC32_ERROR, 2, 1) + FIELD(CFU_ITR, CRC8_ERROR, 1, 1) + FIELD(CFU_ITR, SEU_ENDOFCALIB, 0, 1) +REG32(CFU_PROTECT, 0x14) + FIELD(CFU_PROTECT, ACTIVE, 0, 1) +REG32(CFU_FGCR, 0x18) + FIELD(CFU_FGCR, GCLK_CAL, 14, 1) + FIELD(CFU_FGCR, SC_HBC_TRIGGER, 13, 1) + FIELD(CFU_FGCR, GLOW, 12, 1) + FIELD(CFU_FGCR, GPWRDWN, 11, 1) + FIELD(CFU_FGCR, GCAP, 10, 1) + FIELD(CFU_FGCR, GSCWE, 9, 1) + FIELD(CFU_FGCR, GHIGH_B, 8, 1) + FIELD(CFU_FGCR, GMC_B, 7, 1) + FIELD(CFU_FGCR, GWE, 6, 1) + FIELD(CFU_FGCR, GRESTORE, 5, 1) + FIELD(CFU_FGCR, GTS_CFG_B, 4, 1) + FIELD(CFU_FGCR, GLUTMASK, 3, 1) + FIELD(CFU_FGCR, EN_GLOBS_B, 2, 1) + FIELD(CFU_FGCR, EOS, 1, 1) + FIELD(CFU_FGCR, INIT_COMPLETE, 0, 1) +REG32(CFU_CTL, 0x1c) + FIELD(CFU_CTL, GSR_GSC, 15, 1) + FIELD(CFU_CTL, SLVERR_EN, 14, 1) + FIELD(CFU_CTL, CRC32_RESET, 13, 1) + FIELD(CFU_CTL, AXI_ERROR_EN, 12, 1) + FIELD(CFU_CTL, FLUSH_AXI, 11, 1) + FIELD(CFU_CTL, SSI_PER_SLR_PR, 10, 1) + FIELD(CFU_CTL, GCAP_CLK_EN, 9, 1) + FIELD(CFU_CTL, STATUS_SYNC_DISABLE, 8, 1) + FIELD(CFU_CTL, IGNORE_CFI_ERROR, 7, 1) + FIELD(CFU_CTL, CFRAME_DISABLE, 6, 1) + FIELD(CFU_CTL, QWORD_CNT_RESET, 5, 1) + FIELD(CFU_CTL, CRC8_DISABLE, 4, 1) + FIELD(CFU_CTL, CRC32_CHECK, 3, 1) + FIELD(CFU_CTL, DECOMPRESS, 2, 1) + FIELD(CFU_CTL, SEU_GO, 1, 1) + FIELD(CFU_CTL, CFI_LOCAL_RESET, 0, 1) +REG32(CFU_CRAM_RW, 0x20) + FIELD(CFU_CRAM_RW, RFIFO_AFULL_DEPTH, 18, 9) + FIELD(CFU_CRAM_RW, RD_WAVE_CNT_LEFT, 12, 6) + FIELD(CFU_CRAM_RW, RD_WAVE_CNT, 6, 6) + FIELD(CFU_CRAM_RW, WR_WAVE_CNT, 0, 6) +REG32(CFU_MASK, 0x28) +REG32(CFU_CRC_EXPECT, 0x2c) +REG32(CFU_CFRAME_LEFT_T0, 0x60) + FIELD(CFU_CFRAME_LEFT_T0, NUM, 0, 20) +REG32(CFU_CFRAME_LEFT_T1, 0x64) + FIELD(CFU_CFRAME_LEFT_T1, NUM, 0, 20) +REG32(CFU_CFRAME_LEFT_T2, 0x68) + FIELD(CFU_CFRAME_LEFT_T2, NUM, 0, 20) +REG32(CFU_ROW_RANGE, 0x6c) + FIELD(CFU_ROW_RANGE, HALF_FSR, 5, 1) + FIELD(CFU_ROW_RANGE, NUM, 0, 5) +REG32(CFU_STATUS, 0x100) + FIELD(CFU_STATUS, SEU_WRITE_ERROR, 30, 1) + FIELD(CFU_STATUS, FRCNT_ERROR, 29, 1) + FIELD(CFU_STATUS, RSVD_ERROR, 28, 1) + FIELD(CFU_STATUS, FDRO_ERROR, 27, 1) + FIELD(CFU_STATUS, FDRI_ERROR, 26, 1) + FIELD(CFU_STATUS, FDRI_READ_ERROR, 25, 1) + FIELD(CFU_STATUS, READ_FDRI_ERROR, 24, 1) + FIELD(CFU_STATUS, READ_SFR_ERROR, 23, 1) + FIELD(CFU_STATUS, READ_STREAM_ERROR, 22, 1) + FIELD(CFU_STATUS, UNKNOWN_STREAM_PKT, 21, 1) + FIELD(CFU_STATUS, USR_GTS, 20, 1) + FIELD(CFU_STATUS, USR_GSR, 19, 1) + FIELD(CFU_STATUS, AXI_BAD_WSTRB, 18, 1) + FIELD(CFU_STATUS, AXI_BAD_AR_SIZE, 17, 1) + FIELD(CFU_STATUS, AXI_BAD_AW_SIZE, 16, 1) + FIELD(CFU_STATUS, AXI_BAD_ARADDR, 15, 1) + FIELD(CFU_STATUS, AXI_BAD_AWADDR, 14, 1) + FIELD(CFU_STATUS, SCAN_CLEAR_PASS, 13, 1) + FIELD(CFU_STATUS, HC_SEC_ERROR, 12, 1) + FIELD(CFU_STATUS, GHIGH_B_ISHIGH, 11, 1) + FIELD(CFU_STATUS, GHIGH_B_ISLOW, 10, 1) + FIELD(CFU_STATUS, GMC_B_ISHIGH, 9, 1) + FIELD(CFU_STATUS, GMC_B_ISLOW, 8, 1) + FIELD(CFU_STATUS, GPWRDWN_B_ISHIGH, 7, 1) + FIELD(CFU_STATUS, CFI_SEU_CRC_ERROR, 6, 1) + FIELD(CFU_STATUS, CFI_SEU_ECC_ERROR, 5, 1) + FIELD(CFU_STATUS, CFI_SEU_HEARTBEAT, 4, 1) + FIELD(CFU_STATUS, SCAN_CLEAR_DONE, 3, 1) + FIELD(CFU_STATUS, HC_COMPLETE, 2, 1) + FIELD(CFU_STATUS, CFI_CFRAME_BUSY, 1, 1) + FIELD(CFU_STATUS, CFU_STREAM_BUSY, 0, 1) +REG32(CFU_INTERNAL_STATUS, 0x104) + FIELD(CFU_INTERNAL_STATUS, SSI_EOS, 22, 1) + FIELD(CFU_INTERNAL_STATUS, SSI_GWE, 21, 1) + FIELD(CFU_INTERNAL_STATUS, RFIFO_EMPTY, 20, 1) + FIELD(CFU_INTERNAL_STATUS, RFIFO_FULL, 19, 1) + FIELD(CFU_INTERNAL_STATUS, SEL_SFR, 18, 1) + FIELD(CFU_INTERNAL_STATUS, STREAM_CFRAME, 17, 1) + FIELD(CFU_INTERNAL_STATUS, FDRI_PHASE, 16, 1) + FIELD(CFU_INTERNAL_STATUS, CFI_PIPE_EN, 15, 1) + FIELD(CFU_INTERNAL_STATUS, AWFIFO_DCNT, 10, 5) + FIELD(CFU_INTERNAL_STATUS, WFIFO_DCNT, 5, 5) + FIELD(CFU_INTERNAL_STATUS, REPAIR_BUSY, 4, 1) + FIELD(CFU_INTERNAL_STATUS, TRIMU_BUSY, 3, 1) + FIELD(CFU_INTERNAL_STATUS, TRIMB_BUSY, 2, 1) + FIELD(CFU_INTERNAL_STATUS, HCLEANR_BUSY, 1, 1) + FIELD(CFU_INTERNAL_STATUS, HCLEAN_BUSY, 0, 1) +REG32(CFU_QWORD_CNT, 0x108) +REG32(CFU_CRC_LIVE, 0x10c) +REG32(CFU_PENDING_READ_CNT, 0x110) + FIELD(CFU_PENDING_READ_CNT, NUM, 0, 25) +REG32(CFU_FDRI_CNT, 0x114) +REG32(CFU_ECO1, 0x118) +REG32(CFU_ECO2, 0x11c) + +#define R_MAX (R_CFU_ECO2 + 1) + +#define NUM_STREAM 2 +#define WFIFO_SZ 4 + +struct XlnxVersalCFUAPB { + SysBusDevice parent_obj; + MemoryRegion iomem; + MemoryRegion iomem_stream[NUM_STREAM]; + qemu_irq irq_cfu_imr; + + /* 128-bit wfifo. */ + uint32_t wfifo[WFIFO_SZ]; + + uint32_t regs[R_MAX]; + RegisterInfo regs_info[R_MAX]; + + uint8_t fdri_row_addr; + + struct { + XlnxCfiIf *cframe[15]; + } cfg; +}; + + +struct XlnxVersalCFUFDRO { + SysBusDevice parent_obj; + MemoryRegion iomem_fdro; + + Fifo32 fdro_data; +}; + +struct XlnxVersalCFUSFR { + SysBusDevice parent_obj; + MemoryRegion iomem_sfr; + + /* 128-bit wfifo. */ + uint32_t wfifo[WFIFO_SZ]; + + struct { + XlnxVersalCFUAPB *cfu; + } cfg; +}; + +/** + * This is a helper function for updating a CFI data write fifo, an array of 4 + * uint32_t and 128 bits of data that are allowed to be written through 4 + * sequential 32 bit accesses. After the last index has been written into the + * write fifo (wfifo), the data is copied to and returned in a secondary fifo + * provided to the function (wfifo_ret), and the write fifo is cleared + * (zeroized). + * + * @addr: the address used when calculating the wfifo array index to update + * @value: the value to write into the wfifo array + * @wfifo: the wfifo to update + * @wfifo_out: will return the wfifo data when all 128 bits have been written + * + * @return: true if all 128 bits have been updated. + */ +bool update_wfifo(hwaddr addr, uint64_t value, + uint32_t *wfifo, uint32_t *wfifo_ret); + +#endif diff --git a/include/hw/misc/xlnx-versal-crl.h b/include/hw/misc/xlnx-versal-crl.h index 2857f4169a..dba6d3585d 100644 --- a/include/hw/misc/xlnx-versal-crl.h +++ b/include/hw/misc/xlnx-versal-crl.h @@ -11,9 +11,9 @@ #include "hw/sysbus.h" #include "hw/register.h" -#include "target/arm/cpu.h" +#include "target/arm/cpu-qom.h" -#define TYPE_XLNX_VERSAL_CRL "xlnx,versal-crl" +#define TYPE_XLNX_VERSAL_CRL "xlnx-versal-crl" OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalCRL, XLNX_VERSAL_CRL) REG32(ERR_CTRL, 0x0) diff --git a/include/hw/misc/xlnx-versal-pmc-iou-slcr.h b/include/hw/misc/xlnx-versal-pmc-iou-slcr.h index 2170420f01..0c4a4fd66d 100644 --- a/include/hw/misc/xlnx-versal-pmc-iou-slcr.h +++ b/include/hw/misc/xlnx-versal-pmc-iou-slcr.h @@ -34,7 +34,7 @@ * https://www.xilinx.com/support/documentation/architecture-manuals/am011-versal-acap-trm.pdf * * [2] Versal ACAP Register Reference, - * https://www.xilinx.com/html_docs/registers/am012/am012-versal-register-reference.html#mod___pmc_iop_slcr.html + * https://docs.xilinx.com/r/en-US/am012-versal-register-reference/PMC_IOP_SLCR-Module * * QEMU interface: * + sysbus MMIO region 0: MemoryRegion for the device's registers @@ -54,6 +54,7 @@ #ifndef XLNX_VERSAL_PMC_IOU_SLCR_H #define XLNX_VERSAL_PMC_IOU_SLCR_H +#include "hw/sysbus.h" #include "hw/register.h" #define TYPE_XILINX_VERSAL_PMC_IOU_SLCR "xlnx.versal-pmc-iou-slcr" diff --git a/include/hw/misc/xlnx-versal-trng.h b/include/hw/misc/xlnx-versal-trng.h new file mode 100644 index 0000000000..0bcef8a613 --- /dev/null +++ b/include/hw/misc/xlnx-versal-trng.h @@ -0,0 +1,58 @@ +/* + * Non-crypto strength model of the True Random Number Generator + * in the AMD/Xilinx Versal device family. + * + * Copyright (c) 2017-2020 Xilinx Inc. + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * + * 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 XLNX_VERSAL_TRNG_H +#define XLNX_VERSAL_TRNG_H + +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "hw/register.h" + +#define TYPE_XLNX_VERSAL_TRNG "xlnx.versal-trng" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalTRng, XLNX_VERSAL_TRNG); + +#define RMAX_XLNX_VERSAL_TRNG ((0xf0 / 4) + 1) + +typedef struct XlnxVersalTRng { + SysBusDevice parent_obj; + qemu_irq irq; + GRand *prng; + + uint32_t hw_version; + uint32_t forced_faults; + + uint32_t rand_count; + uint64_t rand_reseed; + + uint64_t forced_prng_seed; + uint64_t forced_prng_count; + uint64_t tst_seed[2]; + + uint32_t regs[RMAX_XLNX_VERSAL_TRNG]; + RegisterInfo regs_info[RMAX_XLNX_VERSAL_TRNG]; +} XlnxVersalTRng; + +#undef RMAX_XLNX_VERSAL_TRNG +#endif diff --git a/include/hw/misc/xlnx-zynqmp-apu-ctrl.h b/include/hw/misc/xlnx-zynqmp-apu-ctrl.h index b8ca9434af..c3bf3c1583 100644 --- a/include/hw/misc/xlnx-zynqmp-apu-ctrl.h +++ b/include/hw/misc/xlnx-zynqmp-apu-ctrl.h @@ -13,7 +13,7 @@ #include "hw/sysbus.h" #include "hw/register.h" -#include "target/arm/cpu.h" +#include "target/arm/cpu-qom.h" #define TYPE_XLNX_ZYNQMP_APU_CTRL "xlnx.apu-ctrl" OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPAPUCtrl, XLNX_ZYNQMP_APU_CTRL) diff --git a/include/hw/net/dp8393x.h b/include/hw/net/dp8393x.h new file mode 100644 index 0000000000..4a3f7478be --- /dev/null +++ b/include/hw/net/dp8393x.h @@ -0,0 +1,60 @@ +/* + * QEMU NS SONIC DP8393x netcard + * + * Copyright (c) 2008-2009 Herve Poussineau + * + * 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_DP8393X_H +#define HW_NET_DP8393X_H + +#include "hw/sysbus.h" +#include "net/net.h" +#include "exec/memory.h" + +#define SONIC_REG_COUNT 0x40 + +#define TYPE_DP8393X "dp8393x" +OBJECT_DECLARE_SIMPLE_TYPE(dp8393xState, DP8393X) + +struct dp8393xState { + SysBusDevice parent_obj; + + /* Hardware */ + uint8_t it_shift; + bool big_endian; + bool last_rba_is_full; + qemu_irq irq; + int irq_level; + QEMUTimer *watchdog; + int64_t wt_last_update; + NICConf conf; + NICState *nic; + MemoryRegion mmio; + + /* Registers */ + uint16_t cam[16][3]; + uint16_t regs[SONIC_REG_COUNT]; + + /* Temporaries */ + uint8_t tx_buffer[0x10000]; + int loopback_packet; + + /* Memory access */ + MemoryRegion *dma_mr; + AddressSpace as; +}; + +#endif diff --git a/include/hw/net/imx_fec.h b/include/hw/net/imx_fec.h index e3a8755db9..2d13290c78 100644 --- a/include/hw/net/imx_fec.h +++ b/include/hw/net/imx_fec.h @@ -270,6 +270,8 @@ struct IMXFECState { uint32_t phy_int; uint32_t phy_int_mask; uint32_t phy_num; + bool phy_connected; + struct IMXFECState *phy_consumer; bool is_fec; diff --git a/include/hw/net/lan9118.h b/include/hw/net/lan9118.h index 3d0c67f339..4bf9da7a63 100644 --- a/include/hw/net/lan9118.h +++ b/include/hw/net/lan9118.h @@ -15,6 +15,6 @@ #define TYPE_LAN9118 "lan9118" -void lan9118_init(NICInfo *, uint32_t, qemu_irq); +void lan9118_init(uint32_t, qemu_irq); #endif diff --git a/include/hw/net/lasi_82596.h b/include/hw/net/lasi_82596.h index 7b62b04833..439356ec19 100644 --- a/include/hw/net/lasi_82596.h +++ b/include/hw/net/lasi_82596.h @@ -10,7 +10,7 @@ #include "net/net.h" #include "hw/net/i82596.h" -#include "qom/object.h" +#include "hw/sysbus.h" #define TYPE_LASI_82596 "lasi_82596" typedef struct SysBusI82596State SysBusI82596State; @@ -25,7 +25,7 @@ struct SysBusI82596State { int val_index:1; }; -SysBusI82596State *lasi_82596_init(MemoryRegion *addr_space, - hwaddr hpa, qemu_irq irq); +SysBusI82596State *lasi_82596_init(MemoryRegion *addr_space, hwaddr hpa, + qemu_irq irq, gboolean match_default); #endif diff --git a/include/hw/net/mii.h b/include/hw/net/mii.h index 4ae4dcce7e..f7feddac9b 100644 --- a/include/hw/net/mii.h +++ b/include/hw/net/mii.h @@ -55,6 +55,7 @@ #define MII_BMCR_CTST (1 << 7) /* Collision test */ #define MII_BMCR_SPEED1000 (1 << 6) /* MSB of Speed (1000) */ +#define MII_BMSR_100T4 (1 << 15) /* Can do 100mbps T4 */ #define MII_BMSR_100TX_FD (1 << 14) /* Can do 100mbps, full-duplex */ #define MII_BMSR_100TX_HD (1 << 13) /* Can do 100mbps, half-duplex */ #define MII_BMSR_10T_FD (1 << 12) /* Can do 10mbps, full-duplex */ @@ -70,7 +71,7 @@ #define MII_BMSR_JABBER (1 << 1) /* Jabber detected */ #define MII_BMSR_EXTCAP (1 << 0) /* Ext-reg capability */ -#define MII_ANAR_PAUSE_ASYM (1 << 11) /* Try for asymetric pause */ +#define MII_ANAR_PAUSE_ASYM (1 << 11) /* Try for asymmetric pause */ #define MII_ANAR_PAUSE (1 << 10) /* Try for pause */ #define MII_ANAR_TXFD (1 << 8) #define MII_ANAR_TX (1 << 7) @@ -81,20 +82,31 @@ #define MII_ANLPAR_ACK (1 << 14) #define MII_ANLPAR_PAUSEASY (1 << 11) /* can pause asymmetrically */ #define MII_ANLPAR_PAUSE (1 << 10) /* can pause */ +#define MII_ANLPAR_T4 (1 << 9) #define MII_ANLPAR_TXFD (1 << 8) #define MII_ANLPAR_TX (1 << 7) #define MII_ANLPAR_10FD (1 << 6) #define MII_ANLPAR_10 (1 << 5) #define MII_ANLPAR_CSMACD (1 << 0) -#define MII_ANER_NWAY (1 << 0) /* Can do N-way auto-nego */ +#define MII_ANER_NP (1 << 2) /* Next Page Able */ +#define MII_ANER_NWAY (1 << 0) /* Can do N-way auto-nego */ +#define MII_ANNP_MP (1 << 13) /* Message Page */ + +#define MII_CTRL1000_MASTER (1 << 11) /* MASTER-SLAVE Manual Configuration Value */ +#define MII_CTRL1000_PORT (1 << 10) /* T2_Repeater/DTE bit */ #define MII_CTRL1000_FULL (1 << 9) /* 1000BASE-T full duplex */ #define MII_CTRL1000_HALF (1 << 8) /* 1000BASE-T half duplex */ +#define MII_STAT1000_LOK (1 << 13) /* Local Receiver Status */ +#define MII_STAT1000_ROK (1 << 12) /* Remote Receiver Status */ #define MII_STAT1000_FULL (1 << 11) /* 1000BASE-T full duplex */ #define MII_STAT1000_HALF (1 << 10) /* 1000BASE-T half duplex */ +#define MII_EXTSTAT_1000T_FD (1 << 13) /* 1000BASE-T Full Duplex */ +#define MII_EXTSTAT_1000T_HD (1 << 12) /* 1000BASE-T Half Duplex */ + /* List of vendor identifiers */ /* RealTek 8201 */ #define RTL8201CP_PHYID1 0x0000 diff --git a/include/hw/net/ne2000-isa.h b/include/hw/net/ne2000-isa.h index af59ee0b02..73bae10ad1 100644 --- a/include/hw/net/ne2000-isa.h +++ b/include/hw/net/ne2000-isa.h @@ -22,8 +22,6 @@ static inline ISADevice *isa_ne2000_init(ISABus *bus, int base, int irq, { ISADevice *d; - qemu_check_nic_model(nd, "ne2k_isa"); - d = isa_try_new(TYPE_ISA_NE2000); if (d) { DeviceState *dev = DEVICE(d); diff --git a/include/hw/net/npcm7xx_emc.h b/include/hw/net/npcm7xx_emc.h index eac7f29816..b789007160 100644 --- a/include/hw/net/npcm7xx_emc.h +++ b/include/hw/net/npcm7xx_emc.h @@ -277,10 +277,7 @@ struct NPCM7xxEMCState { bool rx_active; }; -typedef struct NPCM7xxEMCState NPCM7xxEMCState; - #define TYPE_NPCM7XX_EMC "npcm7xx-emc" -#define NPCM7XX_EMC(obj) \ - OBJECT_CHECK(NPCM7xxEMCState, (obj), TYPE_NPCM7XX_EMC) +OBJECT_DECLARE_SIMPLE_TYPE(NPCM7xxEMCState, NPCM7XX_EMC) #endif /* NPCM7XX_EMC_H */ diff --git a/include/hw/net/npcm_gmac.h b/include/hw/net/npcm_gmac.h new file mode 100644 index 0000000000..6340ffe92c --- /dev/null +++ b/include/hw/net/npcm_gmac.h @@ -0,0 +1,343 @@ +/* + * Nuvoton NPCM7xx/8xx GMAC Module + * + * Copyright 2024 Google LLC + * Authors: + * Hao Wu + * Nabih Estefan + * + * 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. + */ + +#ifndef NPCM_GMAC_H +#define NPCM_GMAC_H + +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "net/net.h" + +#define NPCM_GMAC_NR_REGS (0x1060 / sizeof(uint32_t)) + +#define NPCM_GMAC_MAX_PHYS 32 +#define NPCM_GMAC_MAX_PHY_REGS 32 + +struct NPCMGMACRxDesc { + uint32_t rdes0; + uint32_t rdes1; + uint32_t rdes2; + uint32_t rdes3; +}; + +/* NPCMGMACRxDesc.flags values */ +/* RDES2 and RDES3 are buffer addresses */ +/* Owner: 0 = software, 1 = dma */ +#define RX_DESC_RDES0_OWN BIT(31) +/* Destination Address Filter Fail */ +#define RX_DESC_RDES0_DEST_ADDR_FILT_FAIL BIT(30) +/* Frame length */ +#define RX_DESC_RDES0_FRAME_LEN_MASK(word) extract32(word, 16, 14) +/* Frame length Shift*/ +#define RX_DESC_RDES0_FRAME_LEN_SHIFT 16 +/* Error Summary */ +#define RX_DESC_RDES0_ERR_SUMM_MASK BIT(15) +/* Descriptor Error */ +#define RX_DESC_RDES0_DESC_ERR_MASK BIT(14) +/* Source Address Filter Fail */ +#define RX_DESC_RDES0_SRC_ADDR_FILT_FAIL_MASK BIT(13) +/* Length Error */ +#define RX_DESC_RDES0_LEN_ERR_MASK BIT(12) +/* Overflow Error */ +#define RX_DESC_RDES0_OVRFLW_ERR_MASK BIT(11) +/* VLAN Tag */ +#define RX_DESC_RDES0_VLAN_TAG_MASK BIT(10) +/* First Descriptor */ +#define RX_DESC_RDES0_FIRST_DESC_MASK BIT(9) +/* Last Descriptor */ +#define RX_DESC_RDES0_LAST_DESC_MASK BIT(8) +/* IPC Checksum Error/Giant Frame */ +#define RX_DESC_RDES0_IPC_CHKSM_ERR_GNT_FRM_MASK BIT(7) +/* Late Collision */ +#define RX_DESC_RDES0_LT_COLL_MASK BIT(6) +/* Frame Type */ +#define RX_DESC_RDES0_FRM_TYPE_MASK BIT(5) +/* Receive Watchdog Timeout */ +#define RX_DESC_RDES0_REC_WTCHDG_TMT_MASK BIT(4) +/* Receive Error */ +#define RX_DESC_RDES0_RCV_ERR_MASK BIT(3) +/* Dribble Bit Error */ +#define RX_DESC_RDES0_DRBL_BIT_ERR_MASK BIT(2) +/* Cyclcic Redundancy Check Error */ +#define RX_DESC_RDES0_CRC_ERR_MASK BIT(1) +/* Rx MAC Address/Payload Checksum Error */ +#define RC_DESC_RDES0_RCE_MASK BIT(0) + +/* Disable Interrupt on Completion */ +#define RX_DESC_RDES1_DIS_INTR_COMP_MASK BIT(31) +/* Receive end of ring */ +#define RX_DESC_RDES1_RC_END_RING_MASK BIT(25) +/* Second Address Chained */ +#define RX_DESC_RDES1_SEC_ADDR_CHND_MASK BIT(24) +/* Receive Buffer 2 Size */ +#define RX_DESC_RDES1_BFFR2_SZ_SHIFT 11 +#define RX_DESC_RDES1_BFFR2_SZ_MASK(word) extract32(word, \ + RX_DESC_RDES1_BFFR2_SZ_SHIFT, 11) +/* Receive Buffer 1 Size */ +#define RX_DESC_RDES1_BFFR1_SZ_MASK(word) extract32(word, 0, 11) + + +struct NPCMGMACTxDesc { + uint32_t tdes0; + uint32_t tdes1; + uint32_t tdes2; + uint32_t tdes3; +}; + +/* NPCMGMACTxDesc.flags values */ +/* TDES2 and TDES3 are buffer addresses */ +/* Owner: 0 = software, 1 = gmac */ +#define TX_DESC_TDES0_OWN BIT(31) +/* Tx Time Stamp Status */ +#define TX_DESC_TDES0_TTSS_MASK BIT(17) +/* IP Header Error */ +#define TX_DESC_TDES0_IP_HEAD_ERR_MASK BIT(16) +/* Error Summary */ +#define TX_DESC_TDES0_ERR_SUMM_MASK BIT(15) +/* Jabber Timeout */ +#define TX_DESC_TDES0_JBBR_TMT_MASK BIT(14) +/* Frame Flushed */ +#define TX_DESC_TDES0_FRM_FLSHD_MASK BIT(13) +/* Payload Checksum Error */ +#define TX_DESC_TDES0_PYLD_CHKSM_ERR_MASK BIT(12) +/* Loss of Carrier */ +#define TX_DESC_TDES0_LSS_CARR_MASK BIT(11) +/* No Carrier */ +#define TX_DESC_TDES0_NO_CARR_MASK BIT(10) +/* Late Collision */ +#define TX_DESC_TDES0_LATE_COLL_MASK BIT(9) +/* Excessive Collision */ +#define TX_DESC_TDES0_EXCS_COLL_MASK BIT(8) +/* VLAN Frame */ +#define TX_DESC_TDES0_VLAN_FRM_MASK BIT(7) +/* Collision Count */ +#define TX_DESC_TDES0_COLL_CNT_MASK(word) extract32(word, 3, 4) +/* Excessive Deferral */ +#define TX_DESC_TDES0_EXCS_DEF_MASK BIT(2) +/* Underflow Error */ +#define TX_DESC_TDES0_UNDRFLW_ERR_MASK BIT(1) +/* Deferred Bit */ +#define TX_DESC_TDES0_DFRD_BIT_MASK BIT(0) + +/* Interrupt of Completion */ +#define TX_DESC_TDES1_INTERR_COMP_MASK BIT(31) +/* Last Segment */ +#define TX_DESC_TDES1_LAST_SEG_MASK BIT(30) +/* First Segment */ +#define TX_DESC_TDES1_FIRST_SEG_MASK BIT(29) +/* Checksum Insertion Control */ +#define TX_DESC_TDES1_CHKSM_INS_CTRL_MASK(word) extract32(word, 27, 2) +/* Disable Cyclic Redundancy Check */ +#define TX_DESC_TDES1_DIS_CDC_MASK BIT(26) +/* Transmit End of Ring */ +#define TX_DESC_TDES1_TX_END_RING_MASK BIT(25) +/* Secondary Address Chained */ +#define TX_DESC_TDES1_SEC_ADDR_CHND_MASK BIT(24) +/* Transmit Buffer 2 Size */ +#define TX_DESC_TDES1_BFFR2_SZ_MASK(word) extract32(word, 11, 11) +/* Transmit Buffer 1 Size */ +#define TX_DESC_TDES1_BFFR1_SZ_MASK(word) extract32(word, 0, 11) + +typedef struct NPCMGMACState { + SysBusDevice parent; + + MemoryRegion iomem; + qemu_irq irq; + + NICState *nic; + NICConf conf; + + uint32_t regs[NPCM_GMAC_NR_REGS]; + uint16_t phy_regs[NPCM_GMAC_MAX_PHYS][NPCM_GMAC_MAX_PHY_REGS]; +} NPCMGMACState; + +#define TYPE_NPCM_GMAC "npcm-gmac" +OBJECT_DECLARE_SIMPLE_TYPE(NPCMGMACState, NPCM_GMAC) + +/* Mask for RO bits in Status */ +#define NPCM_DMA_STATUS_RO_MASK(word) (word & 0xfffe0000) +/* Mask for RO bits in Status */ +#define NPCM_DMA_STATUS_W1C_MASK(word) (word & 0x1e7ff) + +/* Transmit Process State */ +#define NPCM_DMA_STATUS_TX_PROCESS_STATE_SHIFT 20 +/* Transmit States */ +#define NPCM_DMA_STATUS_TX_STOPPED_STATE \ + (0b000) +#define NPCM_DMA_STATUS_TX_RUNNING_FETCHING_STATE \ + (0b001) +#define NPCM_DMA_STATUS_TX_RUNNING_WAITING_STATE \ + (0b010) +#define NPCM_DMA_STATUS_TX_RUNNING_READ_STATE \ + (0b011) +#define NPCM_DMA_STATUS_TX_SUSPENDED_STATE \ + (0b110) +#define NPCM_DMA_STATUS_TX_RUNNING_CLOSING_STATE \ + (0b111) +/* Transmit Process State */ +#define NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT 17 +/* Receive States */ +#define NPCM_DMA_STATUS_RX_STOPPED_STATE \ + (0b000) +#define NPCM_DMA_STATUS_RX_RUNNING_FETCHING_STATE \ + (0b001) +#define NPCM_DMA_STATUS_RX_RUNNING_WAITING_STATE \ + (0b011) +#define NPCM_DMA_STATUS_RX_SUSPENDED_STATE \ + (0b100) +#define NPCM_DMA_STATUS_RX_RUNNING_CLOSING_STATE \ + (0b101) +#define NPCM_DMA_STATUS_RX_RUNNING_TRANSFERRING_STATE \ + (0b111) + + +/* Early Receive Interrupt */ +#define NPCM_DMA_STATUS_ERI BIT(14) +/* Fatal Bus Error Interrupt */ +#define NPCM_DMA_STATUS_FBI BIT(13) +/* Early transmit Interrupt */ +#define NPCM_DMA_STATUS_ETI BIT(10) +/* Receive Watchdog Timeout */ +#define NPCM_DMA_STATUS_RWT BIT(9) +/* Receive Process Stopped */ +#define NPCM_DMA_STATUS_RPS BIT(8) +/* Receive Buffer Unavailable */ +#define NPCM_DMA_STATUS_RU BIT(7) +/* Receive Interrupt */ +#define NPCM_DMA_STATUS_RI BIT(6) +/* Transmit Underflow */ +#define NPCM_DMA_STATUS_UNF BIT(5) +/* Receive Overflow */ +#define NPCM_DMA_STATUS_OVF BIT(4) +/* Transmit Jabber Timeout */ +#define NPCM_DMA_STATUS_TJT BIT(3) +/* Transmit Buffer Unavailable */ +#define NPCM_DMA_STATUS_TU BIT(2) +/* Transmit Process Stopped */ +#define NPCM_DMA_STATUS_TPS BIT(1) +/* Transmit Interrupt */ +#define NPCM_DMA_STATUS_TI BIT(0) + +/* Normal Interrupt Summary */ +#define NPCM_DMA_STATUS_NIS BIT(16) +/* Interrupts enabled by NIE */ +#define NPCM_DMA_STATUS_NIS_BITS (NPCM_DMA_STATUS_TI | \ + NPCM_DMA_STATUS_TU | \ + NPCM_DMA_STATUS_RI | \ + NPCM_DMA_STATUS_ERI) +/* Abnormal Interrupt Summary */ +#define NPCM_DMA_STATUS_AIS BIT(15) +/* Interrupts enabled by AIE */ +#define NPCM_DMA_STATUS_AIS_BITS (NPCM_DMA_STATUS_TPS | \ + NPCM_DMA_STATUS_TJT | \ + NPCM_DMA_STATUS_OVF | \ + NPCM_DMA_STATUS_UNF | \ + NPCM_DMA_STATUS_RU | \ + NPCM_DMA_STATUS_RPS | \ + NPCM_DMA_STATUS_RWT | \ + NPCM_DMA_STATUS_ETI | \ + NPCM_DMA_STATUS_FBI) + +/* Early Receive Interrupt Enable */ +#define NPCM_DMA_INTR_ENAB_ERE BIT(14) +/* Fatal Bus Error Interrupt Enable */ +#define NPCM_DMA_INTR_ENAB_FBE BIT(13) +/* Early transmit Interrupt Enable */ +#define NPCM_DMA_INTR_ENAB_ETE BIT(10) +/* Receive Watchdog Timout Enable */ +#define NPCM_DMA_INTR_ENAB_RWE BIT(9) +/* Receive Process Stopped Enable */ +#define NPCM_DMA_INTR_ENAB_RSE BIT(8) +/* Receive Buffer Unavailable Enable */ +#define NPCM_DMA_INTR_ENAB_RUE BIT(7) +/* Receive Interrupt Enable */ +#define NPCM_DMA_INTR_ENAB_RIE BIT(6) +/* Transmit Underflow Enable */ +#define NPCM_DMA_INTR_ENAB_UNE BIT(5) +/* Receive Overflow Enable */ +#define NPCM_DMA_INTR_ENAB_OVE BIT(4) +/* Transmit Jabber Timeout Enable */ +#define NPCM_DMA_INTR_ENAB_TJE BIT(3) +/* Transmit Buffer Unavailable Enable */ +#define NPCM_DMA_INTR_ENAB_TUE BIT(2) +/* Transmit Process Stopped Enable */ +#define NPCM_DMA_INTR_ENAB_TSE BIT(1) +/* Transmit Interrupt Enable */ +#define NPCM_DMA_INTR_ENAB_TIE BIT(0) + +/* Normal Interrupt Summary Enable */ +#define NPCM_DMA_INTR_ENAB_NIE BIT(16) +/* Interrupts enabled by NIE Enable */ +#define NPCM_DMA_INTR_ENAB_NIE_BITS (NPCM_DMA_INTR_ENAB_TIE | \ + NPCM_DMA_INTR_ENAB_TUE | \ + NPCM_DMA_INTR_ENAB_RIE | \ + NPCM_DMA_INTR_ENAB_ERE) +/* Abnormal Interrupt Summary Enable */ +#define NPCM_DMA_INTR_ENAB_AIE BIT(15) +/* Interrupts enabled by AIE Enable */ +#define NPCM_DMA_INTR_ENAB_AIE_BITS (NPCM_DMA_INTR_ENAB_TSE | \ + NPCM_DMA_INTR_ENAB_TJE | \ + NPCM_DMA_INTR_ENAB_OVE | \ + NPCM_DMA_INTR_ENAB_UNE | \ + NPCM_DMA_INTR_ENAB_RUE | \ + NPCM_DMA_INTR_ENAB_RSE | \ + NPCM_DMA_INTR_ENAB_RWE | \ + NPCM_DMA_INTR_ENAB_ETE | \ + NPCM_DMA_INTR_ENAB_FBE) + +/* Flushing Disabled */ +#define NPCM_DMA_CONTROL_FLUSH_MASK BIT(24) +/* Start/stop Transmit */ +#define NPCM_DMA_CONTROL_START_STOP_TX BIT(13) +/* Start/stop Receive */ +#define NPCM_DMA_CONTROL_START_STOP_RX BIT(1) +/* Next receive descriptor start address */ +#define NPCM_DMA_HOST_RX_DESC_MASK(word) ((uint32_t) (word) & ~3u) +/* Next transmit descriptor start address */ +#define NPCM_DMA_HOST_TX_DESC_MASK(word) ((uint32_t) (word) & ~3u) + +/* Receive enable */ +#define NPCM_GMAC_MAC_CONFIG_RX_EN BIT(2) +/* Transmit enable */ +#define NPCM_GMAC_MAC_CONFIG_TX_EN BIT(3) + +/* Frame Receive All */ +#define NPCM_GMAC_FRAME_FILTER_REC_ALL_MASK BIT(31) +/* Frame HPF Filter*/ +#define NPCM_GMAC_FRAME_FILTER_HPF_MASK BIT(10) +/* Frame SAF Filter*/ +#define NPCM_GMAC_FRAME_FILTER_SAF_MASK BIT(9) +/* Frame SAIF Filter*/ +#define NPCM_GMAC_FRAME_FILTER_SAIF_MASK BIT(8) +/* Frame PCF Filter*/ +#define NPCM_GMAC_FRAME_FILTER_PCF_MASK BIT(word) extract32((word), 6, 2) +/* Frame DBF Filter*/ +#define NPCM_GMAC_FRAME_FILTER_DBF_MASK BIT(5) +/* Frame PM Filter*/ +#define NPCM_GMAC_FRAME_FILTER_PM_MASK BIT(4) +/* Frame DAIF Filter*/ +#define NPCM_GMAC_FRAME_FILTER_DAIF_MASK BIT(3) +/* Frame HMC Filter*/ +#define NPCM_GMAC_FRAME_FILTER_HMC_MASK BIT(2) +/* Frame HUC Filter*/ +#define NPCM_GMAC_FRAME_FILTER_HUC_MASK BIT(1) +/* Frame PR Filter*/ +#define NPCM_GMAC_FRAME_FILTER_PR_MASK BIT(0) + +#endif /* NPCM_GMAC_H */ diff --git a/include/hw/net/smc91c111.h b/include/hw/net/smc91c111.h index df5b11dcef..dba32a233f 100644 --- a/include/hw/net/smc91c111.h +++ b/include/hw/net/smc91c111.h @@ -13,6 +13,6 @@ #include "net/net.h" -void smc91c111_init(NICInfo *, uint32_t, qemu_irq); +void smc91c111_init(uint32_t, qemu_irq); #endif diff --git a/include/hw/net/xlnx-versal-canfd.h b/include/hw/net/xlnx-versal-canfd.h new file mode 100644 index 0000000000..ad3104dd13 --- /dev/null +++ b/include/hw/net/xlnx-versal-canfd.h @@ -0,0 +1,87 @@ +/* + * QEMU model of the Xilinx Versal CANFD Controller. + * + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * + * Written-by: Vikram Garhwal + * Based on QEMU CANFD Device emulation implemented by Jin Yang, Deniz Eren and + * Pavel Pisa. + * 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_CANFD_XILINX_H +#define HW_CANFD_XILINX_H + +#include "hw/register.h" +#include "hw/ptimer.h" +#include "net/can_emu.h" +#include "hw/qdev-clock.h" + +#define TYPE_XILINX_CANFD "xlnx.versal-canfd" + +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalCANFDState, XILINX_CANFD) + +#define NUM_REGS_PER_MSG_SPACE 18 /* 1 ID + 1 DLC + 16 Data(DW0 - DW15) regs. */ +#define MAX_NUM_RX 64 +#define OFFSET_RX1_DW15 (0x4144 / 4) +#define CANFD_TIMER_MAX 0xFFFFUL +#define CANFD_DEFAULT_CLOCK (25 * 1000 * 1000) + +#define XLNX_VERSAL_CANFD_R_MAX (OFFSET_RX1_DW15 + \ + ((MAX_NUM_RX - 1) * NUM_REGS_PER_MSG_SPACE) + 1) + +typedef struct XlnxVersalCANFDState { + SysBusDevice parent_obj; + MemoryRegion iomem; + + qemu_irq irq_canfd_int; + qemu_irq irq_addr_err; + + RegisterInfo reg_info[XLNX_VERSAL_CANFD_R_MAX]; + RegisterAccessInfo *tx_regs; + RegisterAccessInfo *rx0_regs; + RegisterAccessInfo *rx1_regs; + RegisterAccessInfo *af_regs; + RegisterAccessInfo *txe_regs; + RegisterAccessInfo *rx_mailbox_regs; + RegisterAccessInfo *af_mask_regs_mailbox; + + uint32_t regs[XLNX_VERSAL_CANFD_R_MAX]; + + ptimer_state *canfd_timer; + + CanBusClientState bus_client; + CanBusState *canfdbus; + + struct { + uint8_t rx0_fifo; + uint8_t rx1_fifo; + uint8_t tx_fifo; + bool enable_rx_fifo1; + uint32_t ext_clk_freq; + } cfg; + +} XlnxVersalCANFDState; + +typedef struct tx_ready_reg_info { + uint32_t can_id; + uint32_t reg_num; +} tx_ready_reg_info; + +#endif diff --git a/include/hw/net/xlnx-zynqmp-can.h b/include/hw/net/xlnx-zynqmp-can.h index eb1558708b..fd2aa77760 100644 --- a/include/hw/net/xlnx-zynqmp-can.h +++ b/include/hw/net/xlnx-zynqmp-can.h @@ -30,6 +30,7 @@ #ifndef XLNX_ZYNQMP_CAN_H #define XLNX_ZYNQMP_CAN_H +#include "hw/sysbus.h" #include "hw/register.h" #include "net/can_emu.h" #include "net/can_host.h" diff --git a/include/hw/nubus/nubus-virtio-mmio.h b/include/hw/nubus/nubus-virtio-mmio.h new file mode 100644 index 0000000000..de497b7f76 --- /dev/null +++ b/include/hw/nubus/nubus-virtio-mmio.h @@ -0,0 +1,36 @@ +/* + * QEMU Macintosh Nubus Virtio MMIO card + * + * Copyright (c) 2023 Mark Cave-Ayland + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_NUBUS_VIRTIO_MMIO_H +#define HW_NUBUS_VIRTIO_MMIO_H + +#include "hw/nubus/nubus.h" +#include "qom/object.h" +#include "hw/intc/goldfish_pic.h" +#include "hw/virtio/virtio-mmio.h" + +#define TYPE_NUBUS_VIRTIO_MMIO "nubus-virtio-mmio" +OBJECT_DECLARE_TYPE(NubusVirtioMMIO, NubusVirtioMMIODeviceClass, + NUBUS_VIRTIO_MMIO) + +struct NubusVirtioMMIODeviceClass { + DeviceClass parent_class; + + DeviceRealize parent_realize; +}; + +#define NUBUS_VIRTIO_MMIO_NUM_DEVICES 32 + +struct NubusVirtioMMIO { + NubusDevice parent_obj; + + GoldfishPICState pic; + VirtIOMMIOProxy virtio_mmio[NUBUS_VIRTIO_MMIO_NUM_DEVICES]; +}; + +#endif diff --git a/include/hw/nubus/nubus.h b/include/hw/nubus/nubus.h index b3b4d2eadb..fee79b71d1 100644 --- a/include/hw/nubus/nubus.h +++ b/include/hw/nubus/nubus.h @@ -51,7 +51,7 @@ struct NubusBus { qemu_irq irqs[NUBUS_IRQS]; }; -#define NUBUS_DECL_ROM_MAX_SIZE (128 * KiB) +#define NUBUS_DECL_ROM_MAX_SIZE (1 * MiB) struct NubusDevice { DeviceState qdev; diff --git a/include/hw/nvram/eeprom_at24c.h b/include/hw/nvram/eeprom_at24c.h new file mode 100644 index 0000000000..acb9857b2a --- /dev/null +++ b/include/hw/nvram/eeprom_at24c.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef EEPROM_AT24C_H +#define EEPROM_AT24C_H + +#include "hw/i2c/i2c.h" + +/* + * Create and realize an AT24C EEPROM device on the heap. + * @bus: I2C bus to put it on + * @address: I2C address of the EEPROM slave when put on a bus + * @rom_size: size of the EEPROM + * + * Create the device state structure, initialize it, put it on the specified + * @bus, and drop the reference to it (the device is realized). + */ +I2CSlave *at24c_eeprom_init(I2CBus *bus, uint8_t address, uint32_t rom_size); + + +/* + * Create and realize an AT24C EEPROM device on the heap with initial data. + * @bus: I2C bus to put it on + * @address: I2C address of the EEPROM slave when put on a bus + * @rom_size: size of the EEPROM + * @init_rom: Array of bytes to initialize EEPROM memory with + * @init_rom_size: Size of @init_rom, must be less than or equal to @rom_size + * + * Create the device state structure, initialize it, put it on the specified + * @bus, and drop the reference to it (the device is realized). Copies the data + * from @init_rom to the beginning of the EEPROM memory buffer. + */ +I2CSlave *at24c_eeprom_init_rom(I2CBus *bus, uint8_t address, uint32_t rom_size, + const uint8_t *init_rom, uint32_t init_rom_size); + +#endif diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index 0e7a8bc7af..c1f81a5f13 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -342,4 +342,25 @@ bool fw_cfg_dma_enabled(void *opaque); */ const char *fw_cfg_arch_key_name(uint16_t key); +/** + * load_image_to_fw_cfg() - Load an image file into an fw_cfg entry identified + * by key. + * @fw_cfg: The firmware config instance to store the data in. + * @size_key: The firmware config key to store the size of the loaded + * data under, with fw_cfg_add_i32(). + * @data_key: The firmware config key to store the loaded data under, + * with fw_cfg_add_bytes(). + * @image_name: The name of the image file to load. If it is NULL, the + * function returns without doing anything. + * @try_decompress: Whether the image should be decompressed (gunzipped) before + * adding it to fw_cfg. If decompression fails, the image is + * loaded as-is. + * + * In case of failure, the function prints an error message to stderr and the + * process exits with status 1. + */ +void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key, + uint16_t data_key, const char *image_name, + bool try_decompress); + #endif diff --git a/include/hw/nvram/fw_cfg_acpi.h b/include/hw/nvram/fw_cfg_acpi.h new file mode 100644 index 0000000000..b39eb0490f --- /dev/null +++ b/include/hw/nvram/fw_cfg_acpi.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * ACPI support for fw_cfg + * + */ + +#ifndef FW_CFG_ACPI_H +#define FW_CFG_ACPI_H + +#include "exec/hwaddr.h" + +void fw_cfg_acpi_dsdt_add(Aml *scope, const MemMapEntry *fw_cfg_memmap); + +#endif diff --git a/include/hw/nvram/mac_nvram.h b/include/hw/nvram/mac_nvram.h new file mode 100644 index 0000000000..0c4dfaeff6 --- /dev/null +++ b/include/hw/nvram/mac_nvram.h @@ -0,0 +1,52 @@ +/* + * PowerMac NVRAM emulation + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * 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 MAC_NVRAM_H +#define MAC_NVRAM_H + +#include "exec/memory.h" +#include "hw/sysbus.h" + +#define MACIO_NVRAM_SIZE 0x2000 + +#define TYPE_MACIO_NVRAM "macio-nvram" +OBJECT_DECLARE_SIMPLE_TYPE(MacIONVRAMState, MACIO_NVRAM) + +struct MacIONVRAMState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + uint32_t size; + uint32_t it_shift; + + MemoryRegion mem; + uint8_t *data; + BlockBackend *blk; +}; + +void pmac_format_nvram_partition(MacIONVRAMState *nvr, int len); + +#endif /* MAC_NVRAM_H */ diff --git a/include/hw/nvram/npcm7xx_otp.h b/include/hw/nvram/npcm7xx_otp.h index 156bbd151a..ea4b5d0731 100644 --- a/include/hw/nvram/npcm7xx_otp.h +++ b/include/hw/nvram/npcm7xx_otp.h @@ -73,7 +73,7 @@ typedef struct NPCM7xxOTPClass NPCM7xxOTPClass; * Each nibble of data is encoded into a byte, so the number of bytes written * to the array will be @len * 2. */ -extern void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data, - unsigned int offset, unsigned int len); +void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data, + unsigned int offset, unsigned int len); #endif /* NPCM7XX_OTP_H */ diff --git a/include/hw/nvram/xlnx-bbram.h b/include/hw/nvram/xlnx-bbram.h index 87d59ef3c0..6fc13f8cc1 100644 --- a/include/hw/nvram/xlnx-bbram.h +++ b/include/hw/nvram/xlnx-bbram.h @@ -34,7 +34,7 @@ #define RMAX_XLNX_BBRAM ((0x4c / 4) + 1) -#define TYPE_XLNX_BBRAM "xlnx,bbram-ctrl" +#define TYPE_XLNX_BBRAM "xlnx.bbram-ctrl" OBJECT_DECLARE_SIMPLE_TYPE(XlnxBBRam, XLNX_BBRAM); struct XlnxBBRam { diff --git a/include/hw/nvram/xlnx-efuse.h b/include/hw/nvram/xlnx-efuse.h index 58414e468b..cff7924106 100644 --- a/include/hw/nvram/xlnx-efuse.h +++ b/include/hw/nvram/xlnx-efuse.h @@ -30,7 +30,7 @@ #include "sysemu/block-backend.h" #include "hw/qdev-core.h" -#define TYPE_XLNX_EFUSE "xlnx,efuse" +#define TYPE_XLNX_EFUSE "xlnx-efuse" OBJECT_DECLARE_SIMPLE_TYPE(XlnxEFuse, XLNX_EFUSE); struct XlnxEFuse { diff --git a/include/hw/nvram/xlnx-versal-efuse.h b/include/hw/nvram/xlnx-versal-efuse.h index a873dc5cb0..86e2261b9a 100644 --- a/include/hw/nvram/xlnx-versal-efuse.h +++ b/include/hw/nvram/xlnx-versal-efuse.h @@ -29,8 +29,8 @@ #define XLNX_VERSAL_EFUSE_CTRL_R_MAX ((0x100 / 4) + 1) -#define TYPE_XLNX_VERSAL_EFUSE_CTRL "xlnx,versal-efuse" -#define TYPE_XLNX_VERSAL_EFUSE_CACHE "xlnx,pmc-efuse-cache" +#define TYPE_XLNX_VERSAL_EFUSE_CTRL "xlnx-versal-efuse" +#define TYPE_XLNX_VERSAL_EFUSE_CACHE "xlnx-pmc-efuse-cache" OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalEFuseCtrl, XLNX_VERSAL_EFUSE_CTRL); OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalEFuseCache, XLNX_VERSAL_EFUSE_CACHE); diff --git a/include/hw/nvram/xlnx-zynqmp-efuse.h b/include/hw/nvram/xlnx-zynqmp-efuse.h index 6b051ec4f1..f5beacc2e6 100644 --- a/include/hw/nvram/xlnx-zynqmp-efuse.h +++ b/include/hw/nvram/xlnx-zynqmp-efuse.h @@ -29,7 +29,7 @@ #define XLNX_ZYNQMP_EFUSE_R_MAX ((0x10fc / 4) + 1) -#define TYPE_XLNX_ZYNQMP_EFUSE "xlnx,zynqmp-efuse" +#define TYPE_XLNX_ZYNQMP_EFUSE "xlnx-zynqmp-efuse" OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPEFuse, XLNX_ZYNQMP_EFUSE); struct XlnxZynqMPEFuse { diff --git a/include/hw/openrisc/boot.h b/include/hw/openrisc/boot.h new file mode 100644 index 0000000000..25a313d63a --- /dev/null +++ b/include/hw/openrisc/boot.h @@ -0,0 +1,34 @@ +/* + * QEMU OpenRISC boot helpers. + * + * Copyright (c) 2022 Stafford Horne + * + * 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 OPENRISC_BOOT_H +#define OPENRISC_BOOT_H + +#include "exec/cpu-defs.h" + +hwaddr openrisc_load_kernel(ram_addr_t ram_size, + const char *kernel_filename, + uint32_t *bootstrap_pc); + +hwaddr openrisc_load_initrd(void *fdt, const char *filename, + hwaddr load_start, uint64_t mem_size); + +uint32_t openrisc_load_fdt(void *fdt, hwaddr load_start, + uint64_t mem_size); + +#endif /* OPENRISC_BOOT_H */ diff --git a/include/hw/or-irq.h b/include/hw/or-irq.h index f2f0a27381..c0a42f3711 100644 --- a/include/hw/or-irq.h +++ b/include/hw/or-irq.h @@ -35,10 +35,7 @@ */ #define MAX_OR_LINES 48 -typedef struct OrIRQState qemu_or_irq; - -DECLARE_INSTANCE_CHECKER(qemu_or_irq, OR_IRQ, - TYPE_OR_IRQ) +OBJECT_DECLARE_SIMPLE_TYPE(OrIRQState, OR_IRQ) struct OrIRQState { DeviceState parent_obj; diff --git a/include/hw/pci-bridge/cxl_upstream_port.h b/include/hw/pci-bridge/cxl_upstream_port.h new file mode 100644 index 0000000000..12635139f6 --- /dev/null +++ b/include/hw/pci-bridge/cxl_upstream_port.h @@ -0,0 +1,19 @@ + +#ifndef CXL_USP_H +#define CXL_USP_H +#include "hw/pci/pcie.h" +#include "hw/pci/pcie_port.h" +#include "hw/cxl/cxl.h" + +typedef struct CXLUpstreamPort { + /*< private >*/ + PCIEPort parent_obj; + + /*< public >*/ + CXLComponentState cxl_cstate; + CXLCCI swcci; + DOECap doe_cdat; + uint64_t sn; +} CXLUpstreamPort; + +#endif /* CXL_SUP_H */ diff --git a/include/hw/pci-bridge/pci_expander_bridge.h b/include/hw/pci-bridge/pci_expander_bridge.h new file mode 100644 index 0000000000..0b3856d615 --- /dev/null +++ b/include/hw/pci-bridge/pci_expander_bridge.h @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PCI_EXPANDER_BRIDGE_H +#define PCI_EXPANDER_BRIDGE_H + +#include "hw/cxl/cxl.h" + +void pxb_cxl_hook_up_registers(CXLState *state, PCIBus *bus, Error **errp); + +#endif /* PCI_EXPANDER_BRIDGE_H */ diff --git a/include/hw/pci-host/articia.h b/include/hw/pci-host/articia.h new file mode 100644 index 0000000000..529c240274 --- /dev/null +++ b/include/hw/pci-host/articia.h @@ -0,0 +1,17 @@ +/* + * Mai Logic Articia S emulation + * + * Copyright (c) 2023 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#ifndef ARTICIA_H +#define ARTICIA_H + +#define TYPE_ARTICIA "articia" +#define TYPE_ARTICIA_PCI_HOST "articia-pci-host" +#define TYPE_ARTICIA_PCI_BRIDGE "articia-pci-bridge" + +#endif diff --git a/include/hw/pci-host/astro.h b/include/hw/pci-host/astro.h new file mode 100644 index 0000000000..e2966917cd --- /dev/null +++ b/include/hw/pci-host/astro.h @@ -0,0 +1,94 @@ +/* + * HP-PARISC Astro Bus connector with Elroy PCI host bridges + */ + +#ifndef ASTRO_H +#define ASTRO_H + +#include "hw/pci/pci_host.h" + +#define ASTRO_HPA 0xfed00000 + +#define ROPES_PER_IOC 8 /* per Ike half or Pluto/Astro */ + +#define TYPE_ASTRO_CHIP "astro-chip" +OBJECT_DECLARE_SIMPLE_TYPE(AstroState, ASTRO_CHIP) + +#define TYPE_ELROY_PCI_HOST_BRIDGE "elroy-pcihost" +OBJECT_DECLARE_SIMPLE_TYPE(ElroyState, ELROY_PCI_HOST_BRIDGE) + +#define ELROY_NUM 4 /* # of Elroys */ +#define ELROY_IRQS 8 /* IOSAPIC IRQs */ + +/* ASTRO Memory and I/O regions */ +#define LMMIO_DIST_BASE_ADDR 0xf4000000ULL +#define LMMIO_DIST_BASE_SIZE 0x4000000ULL + +#define IOS_DIST_BASE_ADDR 0xfffee00000ULL +#define IOS_DIST_BASE_SIZE 0x10000ULL + +#define HF_ENABLE 0x40 /* enable HF mode (default is -1 mode) */ + +struct AstroState; + +struct ElroyState { + PCIHostState parent_obj; + + /* parent Astro device */ + struct AstroState *astro; + + /* HPA of this Elroy */ + hwaddr hpa; + + /* PCI bus number (Elroy number) */ + unsigned int pci_bus_num; + + uint64_t config_address; + uint64_t config_reg_elroy; + + uint64_t status_control; + uint64_t arb_mask; + uint64_t mmio_base[(0x0250 - 0x200) / 8]; + uint64_t error_config; + + uint32_t iosapic_reg_select; + uint64_t iosapic_reg[0x20]; + + uint32_t ilr; + + MemoryRegion this_mem; + + MemoryRegion pci_mmio; + MemoryRegion pci_mmio_alias; + MemoryRegion pci_hole; + MemoryRegion pci_io; +}; + +struct AstroState { + PCIHostState parent_obj; + + uint64_t ioc_ctrl; + uint64_t ioc_status_ctrl; + uint64_t ioc_ranges[(0x03d8 - 0x300) / 8]; + uint64_t ioc_rope_config; + uint64_t ioc_status_control; + uint64_t ioc_flush_control; + uint64_t ioc_rope_control[8]; + uint64_t tlb_ibase; + uint64_t tlb_imask; + uint64_t tlb_pcom; + uint64_t tlb_tcnfg; + uint64_t tlb_pdir_base; + + struct ElroyState *elroy[ELROY_NUM]; + + MemoryRegion this_mem; + + MemoryRegion pci_mmio; + MemoryRegion pci_io; + + IOMMUMemoryRegion iommu; + AddressSpace iommu_as; +}; + +#endif diff --git a/include/hw/pci-host/bonito.h b/include/hw/pci-host/bonito.h new file mode 100644 index 0000000000..b8ecf7870a --- /dev/null +++ b/include/hw/pci-host/bonito.h @@ -0,0 +1,18 @@ +/* + * QEMU Bonito64 north bridge support + * + * Copyright (c) 2008 yajin (yajin@vm-kernel.org) + * Copyright (c) 2010 Huacai Chen (zltjiangshi@gmail.com) + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_PCI_HOST_BONITO_H +#define HW_PCI_HOST_BONITO_H + +#include "qom/object.h" + +#define TYPE_BONITO_PCI_HOST_BRIDGE "Bonito-pcihost" +OBJECT_DECLARE_SIMPLE_TYPE(BonitoState, BONITO_PCI_HOST_BRIDGE) + +#endif diff --git a/include/hw/pci-host/designware.h b/include/hw/pci-host/designware.h index 6d9b51ae67..908f3d946b 100644 --- a/include/hw/pci-host/designware.h +++ b/include/hw/pci-host/designware.h @@ -22,9 +22,6 @@ #define DESIGNWARE_H #include "hw/sysbus.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_bus.h" -#include "hw/pci/pcie_host.h" #include "hw/pci/pci_bridge.h" #include "qom/object.h" diff --git a/include/hw/pci-host/dino.h b/include/hw/pci-host/dino.h index a1b0184940..fd7975c798 100644 --- a/include/hw/pci-host/dino.h +++ b/include/hw/pci-host/dino.h @@ -1,5 +1,5 @@ /* - * HP-PARISC Dino PCI chipset emulation, as in B160L and similiar machines + * HP-PARISC Dino PCI chipset emulation, as in B160L and similar machines * * (C) 2017-2019 by Helge Deller * diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h index fcf8b63820..dce883573b 100644 --- a/include/hw/pci-host/gpex.h +++ b/include/hw/pci-host/gpex.h @@ -22,7 +22,7 @@ #include "exec/hwaddr.h" #include "hw/sysbus.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pcie_host.h" #include "qom/object.h" @@ -40,6 +40,15 @@ struct GPEXRootState { /*< public >*/ }; +struct GPEXConfig { + MemMapEntry ecam; + MemMapEntry mmio32; + MemMapEntry mmio64; + MemMapEntry pio; + int irq; + PCIBus *bus; +}; + struct GPEXHost { /*< private >*/ PCIExpressHost parent_obj; @@ -55,19 +64,22 @@ struct GPEXHost { int irq_num[GPEX_NUM_IRQS]; bool allow_unmapped_accesses; -}; -struct GPEXConfig { - MemMapEntry ecam; - MemMapEntry mmio32; - MemMapEntry mmio64; - MemMapEntry pio; - int irq; - PCIBus *bus; + struct GPEXConfig gpex_cfg; }; int gpex_set_irq_num(GPEXHost *s, int index, int gsi); void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg); +void acpi_dsdt_add_gpex_host(Aml *scope, uint32_t irq); + +#define PCI_HOST_PIO_BASE "x-pio-base" +#define PCI_HOST_PIO_SIZE "x-pio-size" +#define PCI_HOST_ECAM_BASE "x-ecam-base" +#define PCI_HOST_ECAM_SIZE "x-ecam-size" +#define PCI_HOST_BELOW_4G_MMIO_BASE "x-below-4g-mmio-base" +#define PCI_HOST_BELOW_4G_MMIO_SIZE "x-below-4g-mmio-size" +#define PCI_HOST_ABOVE_4G_MMIO_BASE "x-above-4g-mmio-base" +#define PCI_HOST_ABOVE_4G_MMIO_SIZE "x-above-4g-mmio-size" #endif /* HW_GPEX_H */ diff --git a/include/hw/pci-host/grackle.h b/include/hw/pci-host/grackle.h new file mode 100644 index 0000000000..7ad3a779f0 --- /dev/null +++ b/include/hw/pci-host/grackle.h @@ -0,0 +1,44 @@ +/* + * QEMU Grackle PCI host (heathrow OldWorld PowerMac) + * + * Copyright (c) 2006-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * 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 GRACKLE_H +#define GRACKLE_H + +#include "hw/pci/pci_host.h" + +#define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" +OBJECT_DECLARE_SIMPLE_TYPE(GrackleState, GRACKLE_PCI_HOST_BRIDGE) + +struct GrackleState { + PCIHostState parent_obj; + + uint32_t ofw_addr; + qemu_irq irqs[4]; + MemoryRegion pci_mmio; + MemoryRegion pci_hole; + MemoryRegion pci_io; +}; + +#endif diff --git a/include/hw/pci-host/i440fx.h b/include/hw/pci-host/i440fx.h index f068aaba8f..c988f70890 100644 --- a/include/hw/pci-host/i440fx.h +++ b/include/hw/pci-host/i440fx.h @@ -11,10 +11,12 @@ #ifndef HW_PCI_I440FX_H #define HW_PCI_I440FX_H -#include "hw/pci/pci_bus.h" +#include "hw/pci/pci_device.h" #include "hw/pci-host/pam.h" #include "qom/object.h" +#define I440FX_HOST_PROP_PCI_TYPE "pci-type" + #define TYPE_I440FX_PCI_HOST_BRIDGE "i440FX-pcihost" #define TYPE_I440FX_PCI_DEVICE "i440FX" @@ -25,9 +27,6 @@ struct PCII440FXState { PCIDevice parent_obj; /*< public >*/ - MemoryRegion *system_memory; - MemoryRegion *pci_address_space; - MemoryRegion *ram_memory; PAMMemoryRegion pam_regions[PAM_REGIONS_COUNT]; MemoryRegion smram_region; MemoryRegion smram, low_smram; @@ -35,15 +34,4 @@ struct PCII440FXState { #define TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE "igd-passthrough-i440FX" -PCIBus *i440fx_init(const char *host_type, const char *pci_type, - PCII440FXState **pi440fx_state, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - ram_addr_t ram_size, - ram_addr_t below_4g_mem_size, - ram_addr_t above_4g_mem_size, - MemoryRegion *pci_memory, - MemoryRegion *ram_memory); - - #endif diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h index 08c5f78be2..e753449593 100644 --- a/include/hw/pci-host/ls7a.h +++ b/include/hw/pci-host/ls7a.h @@ -8,37 +8,43 @@ #ifndef HW_LS7A_H #define HW_LS7A_H -#include "hw/pci/pci.h" -#include "hw/pci/pcie_host.h" #include "hw/pci-host/pam.h" #include "qemu/units.h" #include "qemu/range.h" #include "qom/object.h" -#define LS7A_PCI_MEM_BASE 0x40000000UL -#define LS7A_PCI_MEM_SIZE 0x40000000UL -#define LS7A_PCI_IO_OFFSET 0x4000 -#define LS_PCIECFG_BASE 0x20000000 -#define LS_PCIECFG_SIZE 0x08000000 -#define LS7A_PCI_IO_BASE 0x18004000UL -#define LS7A_PCI_IO_SIZE 0xC000 +#define VIRT_PCI_MEM_BASE 0x40000000UL +#define VIRT_PCI_MEM_SIZE 0x40000000UL +#define VIRT_PCI_IO_OFFSET 0x4000 +#define VIRT_PCI_CFG_BASE 0x20000000 +#define VIRT_PCI_CFG_SIZE 0x08000000 +#define VIRT_PCI_IO_BASE 0x18004000UL +#define VIRT_PCI_IO_SIZE 0xC000 -#define LS7A_PCH_REG_BASE 0x10000000UL -#define LS7A_IOAPIC_REG_BASE (LS7A_PCH_REG_BASE) -#define LS7A_PCH_MSI_ADDR_LOW 0x2FF00000UL +#define VIRT_PCH_REG_BASE 0x10000000UL +#define VIRT_IOAPIC_REG_BASE (VIRT_PCH_REG_BASE) +#define VIRT_PCH_MSI_ADDR_LOW 0x2FF00000UL /* - * According to the kernel pch irq start from 64 offset - * 0 ~ 16 irqs used for non-pci device while 16 ~ 64 irqs - * used for pci device. + * GSI_BASE is hard-coded with 64 in linux kernel, else kernel fails to boot + * 0 - 15 GSI for ISA devices even if there is no ISA devices + * 16 - 63 GSI for CPU devices such as timers/perf monitor etc + * 64 - GSI for external devices */ -#define PCH_PIC_IRQ_OFFSET 64 -#define LS7A_DEVICE_IRQS 16 -#define LS7A_PCI_IRQS 48 -#define LS7A_UART_IRQ (PCH_PIC_IRQ_OFFSET + 2) -#define LS7A_UART_BASE 0x1fe001e0 -#define LS7A_RTC_IRQ (PCH_PIC_IRQ_OFFSET + 3) -#define LS7A_MISC_REG_BASE (LS7A_PCH_REG_BASE + 0x00080000) -#define LS7A_RTC_REG_BASE (LS7A_MISC_REG_BASE + 0x00050100) -#define LS7A_RTC_LEN 0x100 +#define VIRT_PCH_PIC_IRQ_NUM 32 +#define VIRT_GSI_BASE 64 +#define VIRT_DEVICE_IRQS 16 +#define VIRT_UART_IRQ (VIRT_GSI_BASE + 2) +#define VIRT_UART_BASE 0x1fe001e0 +#define VIRT_UART_SIZE 0X100 +#define VIRT_RTC_IRQ (VIRT_GSI_BASE + 3) +#define VIRT_MISC_REG_BASE (VIRT_PCH_REG_BASE + 0x00080000) +#define VIRT_RTC_REG_BASE (VIRT_MISC_REG_BASE + 0x00050100) +#define VIRT_RTC_LEN 0x100 +#define VIRT_SCI_IRQ (VIRT_GSI_BASE + 4) + +#define VIRT_PLATFORM_BUS_BASEADDRESS 0x16000000 +#define VIRT_PLATFORM_BUS_SIZE 0x2000000 +#define VIRT_PLATFORM_BUS_NUM_IRQS 2 +#define VIRT_PLATFORM_BUS_IRQ (VIRT_GSI_BASE + 5) #endif diff --git a/include/hw/pci-host/pam.h b/include/hw/pci-host/pam.h index c1fd06ba2a..005916f826 100644 --- a/include/hw/pci-host/pam.h +++ b/include/hw/pci-host/pam.h @@ -87,8 +87,9 @@ typedef struct PAMMemoryRegion { unsigned current; } PAMMemoryRegion; -void init_pam(DeviceState *dev, MemoryRegion *ram, MemoryRegion *system, - MemoryRegion *pci, PAMMemoryRegion *mem, uint32_t start, uint32_t size); +void init_pam(PAMMemoryRegion *mem, Object *owner, MemoryRegion *ram, + MemoryRegion *system, MemoryRegion *pci, + uint32_t start, uint32_t size); void pam_update(PAMMemoryRegion *mem, int idx, uint8_t val); #endif /* QEMU_PAM_H */ diff --git a/include/hw/pci-host/pnv_phb3.h b/include/hw/pci-host/pnv_phb3.h index af6ec83cf6..d62b3091ac 100644 --- a/include/hw/pci-host/pnv_phb3.h +++ b/include/hw/pci-host/pnv_phb3.h @@ -10,13 +10,11 @@ #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" #include "qom/object.h" +#include "hw/pci-host/pnv_phb.h" typedef struct PnvPHB3 PnvPHB3; -typedef struct PnvChip PnvChip; /* * PHB3 XICS Source for MSIs @@ -103,15 +101,16 @@ struct PnvPBCQState { }; /* - * PHB3 PCIe Root port + * PHB3 PCIe Root Bus */ #define TYPE_PNV_PHB3_ROOT_BUS "pnv-phb3-root" +struct PnvPHB3RootBus { + PCIBus parent; -#define TYPE_PNV_PHB3_ROOT_PORT "pnv-phb3-root-port" - -typedef struct PnvPHB3RootPort { - PCIESlot parent_obj; -} PnvPHB3RootPort; + uint32_t chip_id; + uint32_t phb_id; +}; +OBJECT_DECLARE_SIMPLE_TYPE(PnvPHB3RootBus, PNV_PHB3_ROOT_BUS) /* * PHB3 PCIe Host Bridge for PowerNV machines (POWER8) @@ -127,7 +126,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(PnvPHB3, PNV_PHB3) #define PCI_MMIO_TOTAL_SIZE (0x1ull << 60) struct PnvPHB3 { - PCIExpressHost parent_obj; + DeviceState parent; + + PnvPHB *phb_base; uint32_t chip_id; uint32_t phb_id; @@ -164,5 +165,6 @@ 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); +void pnv_phb3_bus_init(DeviceState *dev, 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 index a174ef1f70..38f8ce9d74 100644 --- a/include/hw/pci-host/pnv_phb3_regs.h +++ b/include/hw/pci-host/pnv_phb3_regs.h @@ -12,22 +12,6 @@ #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 */ diff --git a/include/hw/pci-host/pnv_phb4.h b/include/hw/pci-host/pnv_phb4.h index 19dcbd6f87..3212e68160 100644 --- a/include/hw/pci-host/pnv_phb4.h +++ b/include/hw/pci-host/pnv_phb4.h @@ -10,15 +10,14 @@ #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/pci-host/pnv_phb.h" +#include "hw/pci/pci_bus.h" +#include "hw/ppc/pnv.h" #include "hw/ppc/xive.h" #include "qom/object.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 @@ -45,15 +44,16 @@ typedef struct PnvPhb4DMASpace { } PnvPhb4DMASpace; /* - * PHB4 PCIe Root port + * PHB4 PCIe Root Bus */ #define TYPE_PNV_PHB4_ROOT_BUS "pnv-phb4-root" -#define TYPE_PNV_PHB4_ROOT_PORT "pnv-phb4-root-port" -#define TYPE_PNV_PHB5_ROOT_PORT "pnv-phb5-root-port" +struct PnvPHB4RootBus { + PCIBus parent; -typedef struct PnvPHB4RootPort { - PCIESlot parent_obj; -} PnvPHB4RootPort; + uint32_t chip_id; + uint32_t phb_id; +}; +OBJECT_DECLARE_SIMPLE_TYPE(PnvPHB4RootBus, PNV_PHB4_ROOT_BUS) /* * PHB4 PCIe Host Bridge for PowerNV machines (POWER9) @@ -78,13 +78,13 @@ OBJECT_DECLARE_SIMPLE_TYPE(PnvPHB4, PNV_PHB4) #define PCI_MMIO_TOTAL_SIZE (0x1ull << 60) struct PnvPHB4 { - PCIExpressHost parent_obj; + DeviceState parent; + + PnvPHB *phb_base; uint32_t chip_id; uint32_t phb_id; - uint64_t version; - /* The owner PEC */ PnvPhb4PecState *pec; @@ -117,7 +117,7 @@ struct PnvPHB4 { MemoryRegion pci_regs_mr; /* Nest registers */ -#define PHB4_PEC_NEST_STK_REGS_COUNT 0x17 +#define PHB4_PEC_NEST_STK_REGS_COUNT 0x18 uint64_t nest_regs[PHB4_PEC_NEST_STK_REGS_COUNT]; MemoryRegion nest_regs_mr; @@ -157,6 +157,8 @@ struct PnvPHB4 { void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon); int pnv_phb4_pec_get_phb_id(PnvPhb4PecState *pec, int stack_index); +PnvPhb4PecState *pnv_pec_add_phb(PnvChip *chip, PnvPHB *phb, Error **errp); +void pnv_phb4_bus_init(DeviceState *dev, PnvPHB4 *phb); extern const MemoryRegionOps pnv_phb4_xscom_ops; /* @@ -172,8 +174,6 @@ struct PnvPhb4PecState { 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]; @@ -186,6 +186,8 @@ struct PnvPhb4PecState { /* PHBs */ uint32_t num_phbs; +#define MAX_PHBS_PER_PEC 3 + PnvPHB *phbs[MAX_PHBS_PER_PEC]; PnvChip *chip; }; @@ -205,7 +207,6 @@ struct PnvPhb4PecClass { uint64_t version; const char *phb_type; const uint32_t *num_phbs; - const char *rp_model; }; /* @@ -216,8 +217,7 @@ struct PnvPhb4PecClass { #define PNV_PHB5(obj) \ OBJECT_CHECK(PnvPhb4, (obj), TYPE_PNV_PHB5) -#define PNV_PHB5_VERSION 0x000000a500000001ull -#define PNV_PHB5_DEVICE_ID 0x0652 +#define PNV_PHB5_VERSION 0x000000a500000002ull #define TYPE_PNV_PHB5_PEC "pnv-phb5-pec" #define PNV_PHB5_PEC(obj) \ diff --git a/include/hw/pci-host/pnv_phb4_regs.h b/include/hw/pci-host/pnv_phb4_regs.h index 4a0d3b28ef..bea96f4d91 100644 --- a/include/hw/pci-host/pnv_phb4_regs.h +++ b/include/hw/pci-host/pnv_phb4_regs.h @@ -77,10 +77,12 @@ #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 +#define PEC_NEST_STK_PBCQ_SPARSE_PAGE 0x16 /* P10 */ +#define PEC_NEST_STK_PBCQ_CACHE_INJ 0x17 /* P10 */ /* XSCOM PCI global registers */ #define PEC_PCI_PBAIB_HW_CONFIG 0x00 +#define PEC_PCI_PBAIB_HW_OVR 0x01 #define PEC_PCI_PBAIB_READ_STK_OVR 0x02 /* XSCOM PCI per-stack registers */ diff --git a/include/hw/pci-host/ppc4xx.h b/include/hw/pci-host/ppc4xx.h new file mode 100644 index 0000000000..32396417fc --- /dev/null +++ b/include/hw/pci-host/ppc4xx.h @@ -0,0 +1,17 @@ +/* + * QEMU PowerPC 4xx PCI-host definitions + * + * Copyright (c) 2018-2023 BALATON Zoltan + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_PCIHOST_PPC4XX_H +#define HW_PCIHOST_PPC4XX_H + +#define TYPE_PPC4xx_HOST_BRIDGE "ppc4xx-host-bridge" +#define TYPE_PPC4xx_PCI_HOST "ppc4xx-pci-host" +#define TYPE_PPC440_PCIX_HOST "ppc440-pcix-host" +#define TYPE_PPC460EX_PCIE_HOST "ppc460ex-pcie-host" + +#endif diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h index ab989698ef..bafcbe6752 100644 --- a/include/hw/pci-host/q35.h +++ b/include/hw/pci-host/q35.h @@ -22,7 +22,7 @@ #ifndef HW_Q35_H #define HW_Q35_H -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pcie_host.h" #include "hw/pci-host/pam.h" #include "qemu/units.h" @@ -54,7 +54,6 @@ struct MCHPCIState { uint64_t below_4g_mem_size; uint64_t above_4g_mem_size; uint64_t pci_hole64_size; - uint32_t short_root_bus; uint16_t ext_tseg_mbytes; }; @@ -74,11 +73,6 @@ struct Q35PCIHost { * gmch part */ -#define MCH_HOST_PROP_RAM_MEM "ram-mem" -#define MCH_HOST_PROP_PCI_MEM "pci-mem" -#define MCH_HOST_PROP_SYSTEM_MEM "system-mem" -#define MCH_HOST_PROP_IO_MEM "io-mem" - /* PCI configuration */ #define MCH_HOST_BRIDGE "MCH" diff --git a/include/hw/pci-host/sabre.h b/include/hw/pci-host/sabre.h index 01190241bb..d12de84ea2 100644 --- a/include/hw/pci-host/sabre.h +++ b/include/hw/pci-host/sabre.h @@ -1,7 +1,7 @@ #ifndef HW_PCI_HOST_SABRE_H #define HW_PCI_HOST_SABRE_H -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" #include "hw/sparc/sun4u_iommu.h" #include "qom/object.h" diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 5b03a7b0eb..3778aac27b 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -47,8 +47,6 @@ typedef struct SpaprPciLsi { uint32_t irq; } SpaprPciLsi; -typedef struct SpaprPhbPciNvGpuConfig SpaprPhbPciNvGpuConfig; - struct SpaprPhbState { PCIHostState parent_obj; @@ -90,9 +88,6 @@ struct SpaprPhbState { uint32_t mig_liobn; hwaddr mig_mem_win_addr, mig_mem_win_size; hwaddr mig_io_win_addr, mig_io_win_size; - hwaddr nv2_gpa_win_addr; - hwaddr nv2_atsd_win_addr; - SpaprPhbPciNvGpuConfig *nvgpus; bool pre_5_1_assoc; }; @@ -112,20 +107,6 @@ struct SpaprPhbState { #define SPAPR_PCI_MSI_WINDOW 0x40000000000ULL -#define SPAPR_PCI_NV2RAM64_WIN_BASE SPAPR_PCI_LIMIT -#define SPAPR_PCI_NV2RAM64_WIN_SIZE (2 * TiB) /* For up to 6 GPUs 256GB each */ - -/* Max number of NVLinks per GPU in any physical box */ -#define NVGPU_MAX_LINKS 3 - -/* - * GPU RAM starts at 64TiB so huge DMA window to cover it all ends at 128TiB - * which is enough. We do not need DMA for ATSD so we put them at 128TiB. - */ -#define SPAPR_PCI_NV2ATSD_WIN_BASE (128 * TiB) -#define SPAPR_PCI_NV2ATSD_WIN_SIZE (NVGPU_MAX_NUM * NVGPU_MAX_LINKS * \ - 64 * KiB) - int spapr_dt_phb(SpaprMachineState *spapr, SpaprPhbState *phb, uint32_t intc_phandle, void *fdt, int *node_offset); @@ -149,13 +130,6 @@ int spapr_phb_vfio_eeh_get_state(SpaprPhbState *sphb, int *state); int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option); int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb); void spapr_phb_vfio_reset(DeviceState *qdev); -void spapr_phb_nvgpu_setup(SpaprPhbState *sphb, Error **errp); -void spapr_phb_nvgpu_free(SpaprPhbState *sphb); -void spapr_phb_nvgpu_populate_dt(SpaprPhbState *sphb, void *fdt, int bus_off, - Error **errp); -void spapr_phb_nvgpu_ram_populate_dt(SpaprPhbState *sphb, void *fdt); -void spapr_phb_nvgpu_populate_pcidev_dt(PCIDevice *dev, void *fdt, int offset, - SpaprPhbState *sphb); #else static inline bool spapr_phb_eeh_available(SpaprPhbState *sphb) { @@ -182,25 +156,6 @@ static inline int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb) static inline void spapr_phb_vfio_reset(DeviceState *qdev) { } -static inline void spapr_phb_nvgpu_setup(SpaprPhbState *sphb, Error **errp) -{ -} -static inline void spapr_phb_nvgpu_free(SpaprPhbState *sphb) -{ -} -static inline void spapr_phb_nvgpu_populate_dt(SpaprPhbState *sphb, void *fdt, - int bus_off, Error **errp) -{ -} -static inline void spapr_phb_nvgpu_ram_populate_dt(SpaprPhbState *sphb, - void *fdt) -{ -} -static inline void spapr_phb_nvgpu_populate_pcidev_dt(PCIDevice *dev, void *fdt, - int offset, - SpaprPhbState *sphb) -{ -} #endif void spapr_phb_dma_reset(SpaprPhbState *sphb); diff --git a/include/hw/pci-host/xilinx-pcie.h b/include/hw/pci-host/xilinx-pcie.h index 89be88d87f..e1b3c1c280 100644 --- a/include/hw/pci-host/xilinx-pcie.h +++ b/include/hw/pci-host/xilinx-pcie.h @@ -21,7 +21,6 @@ #define HW_XILINX_PCIE_H #include "hw/sysbus.h" -#include "hw/pci/pci.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pcie_host.h" #include "qom/object.h" diff --git a/include/hw/pci/msi.h b/include/hw/pci/msi.h index 4087688486..abcfd13925 100644 --- a/include/hw/pci/msi.h +++ b/include/hw/pci/msi.h @@ -21,7 +21,7 @@ #ifndef QEMU_MSI_H #define QEMU_MSI_H -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" struct MSIMessage { uint64_t address; @@ -33,6 +33,7 @@ extern bool msi_nonbroken; void msi_set_message(PCIDevice *dev, MSIMessage msg); MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector); bool msi_enabled(const PCIDevice *dev); +void msi_set_enabled(PCIDevice *dev); int msi_init(struct PCIDevice *dev, uint8_t offset, unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask, Error **errp); @@ -43,6 +44,7 @@ void msi_notify(PCIDevice *dev, unsigned int vector); void msi_send_message(PCIDevice *dev, MSIMessage msg); void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len); unsigned int msi_nr_vectors_allocated(const PCIDevice *dev); +void msi_set_mask(PCIDevice *dev, int vector, bool mask, Error **errp); static inline bool msi_present(const PCIDevice *dev) { diff --git a/include/hw/pci/msix.h b/include/hw/pci/msix.h index 4c4a60c739..0e6f257e45 100644 --- a/include/hw/pci/msix.h +++ b/include/hw/pci/msix.h @@ -33,9 +33,10 @@ bool msix_is_masked(PCIDevice *dev, unsigned vector); void msix_set_pending(PCIDevice *dev, unsigned vector); void msix_clr_pending(PCIDevice *dev, int vector); -int msix_vector_use(PCIDevice *dev, unsigned vector); +void msix_vector_use(PCIDevice *dev, unsigned vector); void msix_vector_unuse(PCIDevice *dev, unsigned vector); void msix_unuse_all_vectors(PCIDevice *dev); +void msix_set_mask(PCIDevice *dev, int vector, bool mask); void msix_notify(PCIDevice *dev, unsigned vector); diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 44dacfa224..eaa3fc99d8 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -16,6 +16,7 @@ extern bool pci_available; #define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) #define PCI_FUNC(devfn) ((devfn) & 0x07) #define PCI_BUILD_BDF(bus, devfn) ((bus << 8) | (devfn)) +#define PCI_BDF_TO_DEVFN(x) ((x) & 0xff) #define PCI_BUS_MAX 256 #define PCI_DEVFN_MAX 256 #define PCI_SLOT_MAX 32 @@ -75,6 +76,7 @@ extern bool pci_available; #define PCI_SUBVENDOR_ID_REDHAT_QUMRANET 0x1af4 #define PCI_SUBDEVICE_ID_QEMU 0x1100 +/* legacy virtio-pci devices */ #define PCI_DEVICE_ID_VIRTIO_NET 0x1000 #define PCI_DEVICE_ID_VIRTIO_BLOCK 0x1001 #define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002 @@ -83,9 +85,15 @@ extern bool pci_available; #define PCI_DEVICE_ID_VIRTIO_RNG 0x1005 #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_DEVICE_ID_VIRTIO_MEM 0x1015 + +/* + * modern virtio-pci devices get their id assigned automatically, + * there is no need to add #defines here. It gets calculated as + * + * PCI_DEVICE_ID = PCI_DEVICE_ID_VIRTIO_10_BASE + + * virtio_bus_get_vdev_id(bus) + */ +#define PCI_DEVICE_ID_VIRTIO_10_BASE 0x1040 #define PCI_VENDOR_ID_REDHAT 0x1b36 #define PCI_DEVICE_ID_REDHAT_BRIDGE 0x0001 @@ -106,6 +114,7 @@ extern bool pci_available; #define PCI_DEVICE_ID_REDHAT_NVME 0x0010 #define PCI_DEVICE_ID_REDHAT_PVPANIC 0x0011 #define PCI_DEVICE_ID_REDHAT_ACPI_ERST 0x0012 +#define PCI_DEVICE_ID_REDHAT_UFS 0x0013 #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 @@ -127,6 +136,10 @@ typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num, pcibus_t addr, pcibus_t size, int type); typedef void PCIUnregisterFunc(PCIDevice *pci_dev); +typedef void MSITriggerFunc(PCIDevice *dev, MSIMessage msg); +typedef MSIMessage MSIPrepareMessageFunc(PCIDevice *dev, unsigned vector); +typedef MSIMessage MSIxPrepareMessageFunc(PCIDevice *dev, unsigned vector); + typedef struct PCIIORegion { pcibus_t addr; /* current PCI mapping address. -1 means not mapped */ #define PCI_BAR_UNMAPPED (~(pcibus_t)0) @@ -154,7 +167,6 @@ enum { #define QEMU_PCI_VGA_IO_HI_SIZE 0x20 #include "hw/pci/pci_regs.h" -#include "hw/pci/pcie.h" /* PCI HEADER_TYPE */ #define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 @@ -196,25 +208,12 @@ enum { QEMU_PCIE_EXTCAP_INIT = (1 << QEMU_PCIE_EXTCAP_INIT_BITNR), #define QEMU_PCIE_CXL_BITNR 10 QEMU_PCIE_CAP_CXL = (1 << QEMU_PCIE_CXL_BITNR), +#define QEMU_PCIE_ERR_UNC_MASK_BITNR 11 + QEMU_PCIE_ERR_UNC_MASK = (1 << QEMU_PCIE_ERR_UNC_MASK_BITNR), +#define QEMU_PCIE_ARI_NEXTFN_1_BITNR 12 + QEMU_PCIE_ARI_NEXTFN_1 = (1 << QEMU_PCIE_ARI_NEXTFN_1_BITNR), }; -#define TYPE_PCI_DEVICE "pci-device" -typedef struct PCIDeviceClass PCIDeviceClass; -DECLARE_OBJ_CHECKERS(PCIDevice, PCIDeviceClass, - PCI_DEVICE, TYPE_PCI_DEVICE) - -/* - * Implemented by devices that can be plugged on CXL buses. In the spec, this is - * actually a "CXL Component, but we name it device to match the PCI naming. - */ -#define INTERFACE_CXL_DEVICE "cxl-device" - -/* Implemented by devices that can be plugged on PCI Express buses */ -#define INTERFACE_PCIE_DEVICE "pci-express-device" - -/* Implemented by devices that can be plugged on Conventional PCI buses */ -#define INTERFACE_CONVENTIONAL_PCI_DEVICE "conventional-pci-device" - typedef struct PCIINTxRoute { enum { PCI_INTX_ENABLED, @@ -224,32 +223,6 @@ typedef struct PCIINTxRoute { int irq; } PCIINTxRoute; -struct PCIDeviceClass { - DeviceClass parent_class; - - void (*realize)(PCIDevice *dev, Error **errp); - PCIUnregisterFunc *exit; - PCIConfigReadFunc *config_read; - PCIConfigWriteFunc *config_write; - - uint16_t vendor_id; - uint16_t device_id; - uint8_t revision; - uint16_t class_id; - uint16_t subsystem_vendor_id; /* only for header type = 0 */ - uint16_t subsystem_id; /* only for header type = 0 */ - - /* - * pci-to-pci bridge or normal device. - * This doesn't mean pci host switch. - * When card bus bridge is supported, this would be enhanced. - */ - bool is_bridge; - - /* rom bar */ - const char *romfile; -}; - typedef void (*PCIINTxRoutingNotifier)(PCIDevice *dev); typedef int (*MSIVectorUseNotifier)(PCIDevice *dev, unsigned int vector, MSIMessage msg); @@ -258,118 +231,6 @@ typedef void (*MSIVectorPollNotifier)(PCIDevice *dev, unsigned int vector_start, unsigned int vector_end); -enum PCIReqIDType { - PCI_REQ_ID_INVALID = 0, - PCI_REQ_ID_BDF, - PCI_REQ_ID_SECONDARY_BUS, - PCI_REQ_ID_MAX, -}; -typedef enum PCIReqIDType PCIReqIDType; - -struct PCIReqIDCache { - PCIDevice *dev; - PCIReqIDType type; -}; -typedef struct PCIReqIDCache PCIReqIDCache; - -struct PCIDevice { - DeviceState qdev; - bool partially_hotplugged; - bool has_power; - - /* PCI config space */ - uint8_t *config; - - /* Used to enable config checks on load. Note that writable bits are - * never checked even if set in cmask. */ - uint8_t *cmask; - - /* Used to implement R/W bytes */ - uint8_t *wmask; - - /* Used to implement RW1C(Write 1 to Clear) bytes */ - uint8_t *w1cmask; - - /* Used to allocate config space for capabilities. */ - uint8_t *used; - - /* the following fields are read only */ - int32_t devfn; - /* Cached device to fetch requester ID from, to avoid the PCI - * tree walking every time we invoke PCI request (e.g., - * MSI). For conventional PCI root complex, this field is - * meaningless. */ - PCIReqIDCache requester_id_cache; - char name[64]; - PCIIORegion io_regions[PCI_NUM_REGIONS]; - AddressSpace bus_master_as; - MemoryRegion bus_master_container_region; - MemoryRegion bus_master_enable_region; - - /* do not access the following fields */ - PCIConfigReadFunc *config_read; - PCIConfigWriteFunc *config_write; - - /* Legacy PCI VGA regions */ - MemoryRegion *vga_regions[QEMU_PCI_VGA_NUM_REGIONS]; - bool has_vga; - - /* Current IRQ levels. Used internally by the generic PCI code. */ - uint8_t irq_state; - - /* Capability bits */ - uint32_t cap_present; - - /* Offset of MSI-X capability in config space */ - uint8_t msix_cap; - - /* MSI-X entries */ - int msix_entries_nr; - - /* Space to store MSIX table & pending bit array */ - uint8_t *msix_table; - uint8_t *msix_pba; - /* MemoryRegion container for msix exclusive BAR setup */ - MemoryRegion msix_exclusive_bar; - /* Memory Regions for MSIX table and pending bit entries. */ - MemoryRegion msix_table_mmio; - MemoryRegion msix_pba_mmio; - /* Reference-count for entries actually in use by driver. */ - unsigned *msix_entry_used; - /* MSIX function mask set or MSIX disabled */ - bool msix_function_masked; - /* Version id needed for VMState */ - int32_t version_id; - - /* Offset of MSI capability in config space */ - uint8_t msi_cap; - - /* PCI Express */ - PCIExpressDevice exp; - - /* SHPC */ - SHPCDevice *shpc; - - /* Location of option rom */ - char *romfile; - uint32_t romsize; - bool has_rom; - MemoryRegion rom; - uint32_t rom_bar; - - /* INTx routing notifier */ - PCIINTxRoutingNotifier intx_routing_notifier; - - /* MSI-X notifiers */ - MSIVectorUseNotifier msix_vector_use_notifier; - MSIVectorReleaseNotifier msix_vector_release_notifier; - MSIVectorPollNotifier msix_vector_poll_notifier; - - /* ID of standby device in net_failover pair */ - char *failover_pair_id; - uint32_t acpi_index; -}; - void pci_register_bar(PCIDevice *pci_dev, int region_num, uint8_t attr, MemoryRegion *memory); void pci_register_vga(PCIDevice *pci_dev, MemoryRegion *mem, @@ -414,22 +275,24 @@ typedef void (*pci_bus_dev_fn)(PCIBus *b, PCIDevice *d, void *opaque); typedef void (*pci_bus_fn)(PCIBus *b, void *opaque); typedef void *(*pci_bus_ret_fn)(PCIBus *b, void *opaque); -bool pci_bus_is_express(PCIBus *bus); +bool pci_bus_is_express(const PCIBus *bus); void pci_root_bus_init(PCIBus *bus, size_t bus_size, DeviceState *parent, const char *name, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, + MemoryRegion *mem, MemoryRegion *io, uint8_t devfn_min, const char *typename); PCIBus *pci_root_bus_new(DeviceState *parent, const char *name, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, + MemoryRegion *mem, MemoryRegion *io, uint8_t devfn_min, const char *typename); void pci_root_bus_cleanup(PCIBus *bus); -void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, +void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, void *irq_opaque, int nirq); +void pci_bus_map_irqs(PCIBus *bus, pci_map_irq_fn map_irq); void pci_bus_irqs_cleanup(PCIBus *bus); int pci_bus_get_irq_level(PCIBus *bus, int irq_num); +uint32_t pci_bus_get_slot_reserved_mask(PCIBus *bus); +void pci_bus_set_slot_reserved_mask(PCIBus *bus, uint32_t mask); +void pci_bus_clear_slot_reserved_mask(PCIBus *bus, uint32_t mask); /* 0 <= pin <= 3 0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD */ static inline int pci_swizzle(int slot, int pin) { @@ -439,8 +302,7 @@ int pci_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin); PCIBus *pci_register_root_bus(DeviceState *parent, const char *name, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, + MemoryRegion *mem, MemoryRegion *io, uint8_t devfn_min, int nirq, const char *typename); void pci_unregister_root_bus(PCIBus *bus); @@ -452,10 +314,9 @@ void pci_device_set_intx_routing_notifier(PCIDevice *dev, PCIINTxRoutingNotifier notifier); void pci_device_reset(PCIDevice *dev); -PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus, - const char *default_model, - const char *default_devaddr); - +void pci_init_nic_devices(PCIBus *bus, const char *default_model); +bool pci_init_nic_in_slot(PCIBus *rootbus, const char *default_model, + const char *alias, const char *devaddr); PCIDevice *pci_vga_init(PCIBus *bus); static inline PCIBus *pci_get_bus(const PCIDevice *dev) @@ -501,10 +362,42 @@ void pci_bus_get_w64_range(PCIBus *bus, Range *range); void pci_device_deassert_intx(PCIDevice *dev); -typedef AddressSpace *(*PCIIOMMUFunc)(PCIBus *, void *, int); + +/** + * struct PCIIOMMUOps: callbacks structure for specific IOMMU handlers + * of a PCIBus + * + * Allows to modify the behavior of some IOMMU operations of the PCI + * framework for a set of devices on a PCI bus. + */ +typedef struct PCIIOMMUOps { + /** + * @get_address_space: get the address space for a set of devices + * on a PCI bus. + * + * Mandatory callback which returns a pointer to an #AddressSpace + * + * @bus: the #PCIBus being accessed. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number + */ + AddressSpace * (*get_address_space)(PCIBus *bus, void *opaque, int devfn); +} PCIIOMMUOps; AddressSpace *pci_device_iommu_address_space(PCIDevice *dev); -void pci_setup_iommu(PCIBus *bus, PCIIOMMUFunc fn, void *opaque); + +/** + * pci_setup_iommu: Initialize specific IOMMU handlers for a PCIBus + * + * Let PCI host bridges define specific operations. + * + * @bus: the #PCIBus being updated. + * @ops: the #PCIIOMMUOps + * @opaque: passed to callbacks of the @ops structure. + */ +void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque); pcibus_t pci_bar_address(PCIDevice *d, int reg, uint8_t type, pcibus_t size); @@ -675,69 +568,51 @@ static inline void pci_set_byte_by_mask(uint8_t *config, uint8_t mask, uint8_t reg) { uint8_t val = pci_get_byte(config); - uint8_t rval = reg << ctz32(mask); - pci_set_byte(config, (~mask & val) | (mask & rval)); -} + uint8_t rval; -static inline uint8_t -pci_get_byte_by_mask(uint8_t *config, uint8_t mask) -{ - uint8_t val = pci_get_byte(config); - return (val & mask) >> ctz32(mask); + assert(mask); + rval = reg << ctz32(mask); + pci_set_byte(config, (~mask & val) | (mask & rval)); } static inline void pci_set_word_by_mask(uint8_t *config, uint16_t mask, uint16_t reg) { uint16_t val = pci_get_word(config); - uint16_t rval = reg << ctz32(mask); - pci_set_word(config, (~mask & val) | (mask & rval)); -} + uint16_t rval; -static inline uint16_t -pci_get_word_by_mask(uint8_t *config, uint16_t mask) -{ - uint16_t val = pci_get_word(config); - return (val & mask) >> ctz32(mask); + assert(mask); + rval = reg << ctz32(mask); + pci_set_word(config, (~mask & val) | (mask & rval)); } static inline void pci_set_long_by_mask(uint8_t *config, uint32_t mask, uint32_t reg) { uint32_t val = pci_get_long(config); - uint32_t rval = reg << ctz32(mask); - pci_set_long(config, (~mask & val) | (mask & rval)); -} + uint32_t rval; -static inline uint32_t -pci_get_long_by_mask(uint8_t *config, uint32_t mask) -{ - uint32_t val = pci_get_long(config); - return (val & mask) >> ctz32(mask); + assert(mask); + rval = reg << ctz32(mask); + pci_set_long(config, (~mask & val) | (mask & rval)); } static inline void pci_set_quad_by_mask(uint8_t *config, uint64_t mask, uint64_t reg) { uint64_t val = pci_get_quad(config); - uint64_t rval = reg << ctz32(mask); + uint64_t rval; + + assert(mask); + rval = reg << ctz32(mask); pci_set_quad(config, (~mask & val) | (mask & rval)); } -static inline uint64_t -pci_get_quad_by_mask(uint8_t *config, uint64_t mask) -{ - uint64_t val = pci_get_quad(config); - return (val & mask) >> ctz32(mask); -} - -PCIDevice *pci_new_multifunction(int devfn, bool multifunction, - const char *name); +PCIDevice *pci_new_multifunction(int devfn, const char *name); PCIDevice *pci_new(int devfn, const char *name); bool pci_realize_and_unref(PCIDevice *dev, PCIBus *bus, Error **errp); PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, - bool multifunction, const char *name); PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name); @@ -746,11 +621,6 @@ void lsi53c8xx_handle_legacy_cmdline(DeviceState *lsi_dev); qemu_irq pci_allocate_irq(PCIDevice *pci_dev); void pci_set_irq(PCIDevice *pci_dev, int level); -static inline int pci_intx(PCIDevice *pci_dev) -{ - return pci_get_byte(pci_dev->config + PCI_INTERRUPT_PIN) - 1; -} - static inline void pci_irq_assert(PCIDevice *pci_dev) { pci_set_irq(pci_dev, 1); @@ -771,189 +641,6 @@ static inline void pci_irq_pulse(PCIDevice *pci_dev) pci_irq_deassert(pci_dev); } -static inline int pci_is_cxl(const PCIDevice *d) -{ - return d->cap_present & QEMU_PCIE_CAP_CXL; -} - -static inline int pci_is_express(const PCIDevice *d) -{ - return d->cap_present & QEMU_PCI_CAP_EXPRESS; -} - -static inline int pci_is_express_downstream_port(const PCIDevice *d) -{ - uint8_t type; - - if (!pci_is_express(d) || !d->exp.exp_cap) { - return 0; - } - - type = pcie_cap_get_type(d); - - return type == PCI_EXP_TYPE_DOWNSTREAM || type == PCI_EXP_TYPE_ROOT_PORT; -} - -static inline int pci_is_vf(const PCIDevice *d) -{ - return d->exp.sriov_vf.pf != NULL; -} - -static inline uint32_t pci_config_size(const PCIDevice *d) -{ - return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE; -} - -static inline uint16_t pci_get_bdf(PCIDevice *dev) -{ - return PCI_BUILD_BDF(pci_bus_num(pci_get_bus(dev)), dev->devfn); -} - -uint16_t pci_requester_id(PCIDevice *dev); - -/* DMA access functions */ -static inline AddressSpace *pci_get_address_space(PCIDevice *dev) -{ - return &dev->bus_master_as; -} - -/** - * pci_dma_rw: Read from or write to an address space from PCI device. - * - * Return a MemTxResult indicating whether the operation succeeded - * or failed (eg unassigned memory, device rejected the transaction, - * IOMMU fault). - * - * @dev: #PCIDevice doing the memory access - * @addr: address within the #PCIDevice address space - * @buf: buffer with the data transferred - * @len: the number of bytes to read or write - * @dir: indicates the transfer direction - */ -static inline MemTxResult pci_dma_rw(PCIDevice *dev, dma_addr_t addr, - void *buf, dma_addr_t len, - DMADirection dir, MemTxAttrs attrs) -{ - return dma_memory_rw(pci_get_address_space(dev), addr, buf, len, - dir, attrs); -} - -/** - * pci_dma_read: Read from an address space from PCI device. - * - * Return a MemTxResult indicating whether the operation succeeded - * or failed (eg unassigned memory, device rejected the transaction, - * IOMMU fault). Called within RCU critical section. - * - * @dev: #PCIDevice doing the memory access - * @addr: address within the #PCIDevice address space - * @buf: buffer with the data transferred - * @len: length of the data transferred - */ -static inline MemTxResult pci_dma_read(PCIDevice *dev, dma_addr_t addr, - void *buf, dma_addr_t len) -{ - return pci_dma_rw(dev, addr, buf, len, - DMA_DIRECTION_TO_DEVICE, MEMTXATTRS_UNSPECIFIED); -} - -/** - * pci_dma_write: Write to address space from PCI device. - * - * Return a MemTxResult indicating whether the operation succeeded - * or failed (eg unassigned memory, device rejected the transaction, - * IOMMU fault). - * - * @dev: #PCIDevice doing the memory access - * @addr: address within the #PCIDevice address space - * @buf: buffer with the data transferred - * @len: the number of bytes to write - */ -static inline MemTxResult pci_dma_write(PCIDevice *dev, dma_addr_t addr, - const void *buf, dma_addr_t len) -{ - return pci_dma_rw(dev, addr, (void *) buf, len, - DMA_DIRECTION_FROM_DEVICE, MEMTXATTRS_UNSPECIFIED); -} - -#define PCI_DMA_DEFINE_LDST(_l, _s, _bits) \ - static inline MemTxResult ld##_l##_pci_dma(PCIDevice *dev, \ - dma_addr_t addr, \ - uint##_bits##_t *val, \ - MemTxAttrs attrs) \ - { \ - return ld##_l##_dma(pci_get_address_space(dev), addr, val, attrs); \ - } \ - static inline MemTxResult st##_s##_pci_dma(PCIDevice *dev, \ - dma_addr_t addr, \ - uint##_bits##_t val, \ - MemTxAttrs attrs) \ - { \ - return st##_s##_dma(pci_get_address_space(dev), addr, val, attrs); \ - } - -PCI_DMA_DEFINE_LDST(ub, b, 8); -PCI_DMA_DEFINE_LDST(uw_le, w_le, 16) -PCI_DMA_DEFINE_LDST(l_le, l_le, 32); -PCI_DMA_DEFINE_LDST(q_le, q_le, 64); -PCI_DMA_DEFINE_LDST(uw_be, w_be, 16) -PCI_DMA_DEFINE_LDST(l_be, l_be, 32); -PCI_DMA_DEFINE_LDST(q_be, q_be, 64); - -#undef PCI_DMA_DEFINE_LDST - -/** - * pci_dma_map: Map device PCI address space range into host virtual address - * @dev: #PCIDevice to be accessed - * @addr: address within that device's address space - * @plen: pointer to length of buffer; updated on return to indicate - * if only a subset of the requested range has been mapped - * @dir: indicates the transfer direction - * - * Return: A host pointer, or %NULL if the resources needed to - * perform the mapping are exhausted (in that case *@plen - * is set to zero). - */ -static inline void *pci_dma_map(PCIDevice *dev, dma_addr_t addr, - dma_addr_t *plen, DMADirection dir) -{ - void *buf; - - buf = dma_memory_map(pci_get_address_space(dev), addr, plen, dir, - MEMTXATTRS_UNSPECIFIED); - return buf; -} - -static inline void pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len, - DMADirection dir, dma_addr_t access_len) -{ - dma_memory_unmap(pci_get_address_space(dev), buffer, len, dir, access_len); -} - -static inline void pci_dma_sglist_init(QEMUSGList *qsg, PCIDevice *dev, - int alloc_hint) -{ - qemu_sglist_init(qsg, DEVICE(dev), alloc_hint, pci_get_address_space(dev)); -} - -extern const VMStateDescription vmstate_pci_device; - -#define VMSTATE_PCI_DEVICE(_field, _state) { \ - .name = (stringify(_field)), \ - .size = sizeof(PCIDevice), \ - .vmsd = &vmstate_pci_device, \ - .flags = VMS_STRUCT, \ - .offset = vmstate_offset_value(_state, _field, PCIDevice), \ -} - -#define VMSTATE_PCI_DEVICE_POINTER(_field, _state) { \ - .name = (stringify(_field)), \ - .size = sizeof(PCIDevice), \ - .vmsd = &vmstate_pci_device, \ - .flags = VMS_STRUCT|VMS_POINTER, \ - .offset = vmstate_offset_pointer(_state, _field, PCIDevice), \ -} - MSIMessage pci_get_msi_message(PCIDevice *dev, int vector); void pci_set_power(PCIDevice *pci_dev, bool state); diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h index ba4bafac7c..5cd452115a 100644 --- a/include/hw/pci/pci_bridge.h +++ b/include/hw/pci/pci_bridge.h @@ -26,7 +26,7 @@ #ifndef QEMU_PCI_BRIDGE_H #define QEMU_PCI_BRIDGE_H -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pci_bus.h" #include "hw/cxl/cxl.h" #include "qom/object.h" @@ -53,6 +53,7 @@ struct PCIBridgeWindows { #define TYPE_PCI_BRIDGE "base-pci-bridge" OBJECT_DECLARE_SIMPLE_TYPE(PCIBridge, PCI_BRIDGE) +#define IS_PCI_BRIDGE(dev) object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE) struct PCIBridge { /*< private >*/ @@ -72,10 +73,13 @@ struct PCIBridge { MemoryRegion address_space_mem; MemoryRegion address_space_io; - PCIBridgeWindows *windows; + PCIBridgeWindows windows; pci_map_irq_fn map_irq; const char *bus_name; + + /* SLT is RO for PCIE to PCIE bridges, but old QEMU versions had it RW */ + bool pcie_writeable_slt_bug; }; #define PCI_BRIDGE_DEV_PROP_CHASSIS_NR "chassis_nr" @@ -83,7 +87,7 @@ struct PCIBridge { #define PCI_BRIDGE_DEV_PROP_SHPC "shpc" typedef struct CXLHost CXLHost; -struct PXBDev { +typedef struct PXBDev { /*< private >*/ PCIDevice parent_obj; /*< public >*/ @@ -91,15 +95,27 @@ struct PXBDev { uint8_t bus_nr; uint16_t numa_node; bool bypass_iommu; - struct cxl_dev { - CXLHost *cxl_host_bridge; /* Pointer to a CXLHost */ - } cxl; -}; +} PXBDev; -typedef struct PXBDev PXBDev; -#define TYPE_PXB_CXL_DEVICE "pxb-cxl" -DECLARE_INSTANCE_CHECKER(PXBDev, PXB_CXL_DEV, - TYPE_PXB_CXL_DEVICE) +typedef struct PXBPCIEDev { + /*< private >*/ + PXBDev parent_obj; +} PXBPCIEDev; + +#define TYPE_PXB_DEV "pxb" +OBJECT_DECLARE_SIMPLE_TYPE(PXBDev, PXB_DEV) + +typedef struct PXBCXLDev { + /*< private >*/ + PXBPCIEDev parent_obj; + /*< public >*/ + + bool hdm_for_passthrough; + CXLHost *cxl_host_bridge; /* Pointer to a CXLHost */ +} PXBCXLDev; + +#define TYPE_PXB_CXL_DEV "pxb-cxl" +OBJECT_DECLARE_SIMPLE_TYPE(PXBCXLDev, PXB_CXL_DEV) int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset, uint16_t svid, uint16_t ssid, @@ -136,11 +152,11 @@ void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, pci_map_irq_fn map_irq); /* TODO: add this define to pci_regs.h in linux and then in qemu. */ -#define PCI_BRIDGE_CTL_VGA_16BIT 0x10 /* VGA 16-bit decode */ -#define PCI_BRIDGE_CTL_DISCARD 0x100 /* Primary discard timer */ -#define PCI_BRIDGE_CTL_SEC_DISCARD 0x200 /* Secondary discard timer */ -#define PCI_BRIDGE_CTL_DISCARD_STATUS 0x400 /* Discard timer status */ -#define PCI_BRIDGE_CTL_DISCARD_SERR 0x800 /* Discard timer SERR# enable */ +#define PCI_BRIDGE_CTL_VGA_16BIT 0x10 /* VGA 16-bit decode */ +#define PCI_BRIDGE_CTL_DISCARD 0x100 /* Primary discard timer */ +#define PCI_BRIDGE_CTL_SEC_DISCARD 0x200 /* Secondary discard timer */ +#define PCI_BRIDGE_CTL_DISCARD_STATUS 0x400 /* Discard timer status */ +#define PCI_BRIDGE_CTL_DISCARD_SERR 0x800 /* Discard timer SERR# enable */ typedef struct PCIBridgeQemuCap { uint8_t id; /* Standard PCI capability header field */ diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index eb94e7e85c..2261312546 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -28,10 +28,12 @@ enum PCIBusFlags { PCI_BUS_CXL = 0x0004, }; +#define PCI_NO_PASID UINT32_MAX + struct PCIBus { BusState qbus; enum PCIBusFlags flags; - PCIIOMMUFunc iommu_fn; + const PCIIOMMUOps *iommu_ops; void *iommu_opaque; uint8_t devfn_min; uint32_t slot_reserved_mask; diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h new file mode 100644 index 0000000000..d3dd0f64b2 --- /dev/null +++ b/include/hw/pci/pci_device.h @@ -0,0 +1,350 @@ +#ifndef QEMU_PCI_DEVICE_H +#define QEMU_PCI_DEVICE_H + +#include "hw/pci/pci.h" +#include "hw/pci/pcie.h" + +#define TYPE_PCI_DEVICE "pci-device" +typedef struct PCIDeviceClass PCIDeviceClass; +DECLARE_OBJ_CHECKERS(PCIDevice, PCIDeviceClass, + PCI_DEVICE, TYPE_PCI_DEVICE) + +/* + * Implemented by devices that can be plugged on CXL buses. In the spec, this is + * actually a "CXL Component, but we name it device to match the PCI naming. + */ +#define INTERFACE_CXL_DEVICE "cxl-device" + +/* Implemented by devices that can be plugged on PCI Express buses */ +#define INTERFACE_PCIE_DEVICE "pci-express-device" + +/* Implemented by devices that can be plugged on Conventional PCI buses */ +#define INTERFACE_CONVENTIONAL_PCI_DEVICE "conventional-pci-device" + +struct PCIDeviceClass { + DeviceClass parent_class; + + void (*realize)(PCIDevice *dev, Error **errp); + PCIUnregisterFunc *exit; + PCIConfigReadFunc *config_read; + PCIConfigWriteFunc *config_write; + + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision; + uint16_t class_id; + uint16_t subsystem_vendor_id; /* only for header type = 0 */ + uint16_t subsystem_id; /* only for header type = 0 */ + + const char *romfile; /* rom bar */ +}; + +enum PCIReqIDType { + PCI_REQ_ID_INVALID = 0, + PCI_REQ_ID_BDF, + PCI_REQ_ID_SECONDARY_BUS, + PCI_REQ_ID_MAX, +}; +typedef enum PCIReqIDType PCIReqIDType; + +struct PCIReqIDCache { + PCIDevice *dev; + PCIReqIDType type; +}; +typedef struct PCIReqIDCache PCIReqIDCache; + +struct PCIDevice { + DeviceState qdev; + bool partially_hotplugged; + bool has_power; + + /* PCI config space */ + uint8_t *config; + + /* + * Used to enable config checks on load. Note that writable bits are + * never checked even if set in cmask. + */ + uint8_t *cmask; + + /* Used to implement R/W bytes */ + uint8_t *wmask; + + /* Used to implement RW1C(Write 1 to Clear) bytes */ + uint8_t *w1cmask; + + /* Used to allocate config space for capabilities. */ + uint8_t *used; + + /* the following fields are read only */ + int32_t devfn; + /* + * Cached device to fetch requester ID from, to avoid the PCI tree + * walking every time we invoke PCI request (e.g., MSI). For + * conventional PCI root complex, this field is meaningless. + */ + PCIReqIDCache requester_id_cache; + char name[64]; + PCIIORegion io_regions[PCI_NUM_REGIONS]; + AddressSpace bus_master_as; + MemoryRegion bus_master_container_region; + MemoryRegion bus_master_enable_region; + + /* do not access the following fields */ + PCIConfigReadFunc *config_read; + PCIConfigWriteFunc *config_write; + + /* Legacy PCI VGA regions */ + MemoryRegion *vga_regions[QEMU_PCI_VGA_NUM_REGIONS]; + bool has_vga; + + /* Current IRQ levels. Used internally by the generic PCI code. */ + uint8_t irq_state; + + /* Capability bits */ + uint32_t cap_present; + + /* Offset of MSI-X capability in config space */ + uint8_t msix_cap; + + /* MSI-X entries */ + int msix_entries_nr; + + /* Space to store MSIX table & pending bit array */ + uint8_t *msix_table; + uint8_t *msix_pba; + + /* May be used by INTx or MSI during interrupt notification */ + void *irq_opaque; + + MSITriggerFunc *msi_trigger; + MSIPrepareMessageFunc *msi_prepare_message; + MSIxPrepareMessageFunc *msix_prepare_message; + + /* MemoryRegion container for msix exclusive BAR setup */ + MemoryRegion msix_exclusive_bar; + /* Memory Regions for MSIX table and pending bit entries. */ + MemoryRegion msix_table_mmio; + MemoryRegion msix_pba_mmio; + /* Reference-count for entries actually in use by driver. */ + unsigned *msix_entry_used; + /* MSIX function mask set or MSIX disabled */ + bool msix_function_masked; + /* Version id needed for VMState */ + int32_t version_id; + + /* Offset of MSI capability in config space */ + uint8_t msi_cap; + + /* PCI Express */ + PCIExpressDevice exp; + + /* SHPC */ + SHPCDevice *shpc; + + /* Location of option rom */ + char *romfile; + uint32_t romsize; + bool has_rom; + MemoryRegion rom; + uint32_t rom_bar; + + /* INTx routing notifier */ + PCIINTxRoutingNotifier intx_routing_notifier; + + /* MSI-X notifiers */ + MSIVectorUseNotifier msix_vector_use_notifier; + MSIVectorReleaseNotifier msix_vector_release_notifier; + MSIVectorPollNotifier msix_vector_poll_notifier; + + /* ID of standby device in net_failover pair */ + char *failover_pair_id; + uint32_t acpi_index; +}; + +static inline int pci_intx(PCIDevice *pci_dev) +{ + return pci_get_byte(pci_dev->config + PCI_INTERRUPT_PIN) - 1; +} + +static inline int pci_is_cxl(const PCIDevice *d) +{ + return d->cap_present & QEMU_PCIE_CAP_CXL; +} + +static inline int pci_is_express(const PCIDevice *d) +{ + return d->cap_present & QEMU_PCI_CAP_EXPRESS; +} + +static inline int pci_is_express_downstream_port(const PCIDevice *d) +{ + uint8_t type; + + if (!pci_is_express(d) || !d->exp.exp_cap) { + return 0; + } + + type = pcie_cap_get_type(d); + + return type == PCI_EXP_TYPE_DOWNSTREAM || type == PCI_EXP_TYPE_ROOT_PORT; +} + +static inline int pci_is_vf(const PCIDevice *d) +{ + return d->exp.sriov_vf.pf != NULL; +} + +static inline uint32_t pci_config_size(const PCIDevice *d) +{ + return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE; +} + +static inline uint16_t pci_get_bdf(PCIDevice *dev) +{ + return PCI_BUILD_BDF(pci_bus_num(pci_get_bus(dev)), dev->devfn); +} + +uint16_t pci_requester_id(PCIDevice *dev); + +/* DMA access functions */ +static inline AddressSpace *pci_get_address_space(PCIDevice *dev) +{ + return &dev->bus_master_as; +} + +/** + * pci_dma_rw: Read from or write to an address space from PCI device. + * + * Return a MemTxResult indicating whether the operation succeeded + * or failed (eg unassigned memory, device rejected the transaction, + * IOMMU fault). + * + * @dev: #PCIDevice doing the memory access + * @addr: address within the #PCIDevice address space + * @buf: buffer with the data transferred + * @len: the number of bytes to read or write + * @dir: indicates the transfer direction + */ +static inline MemTxResult pci_dma_rw(PCIDevice *dev, dma_addr_t addr, + void *buf, dma_addr_t len, + DMADirection dir, MemTxAttrs attrs) +{ + return dma_memory_rw(pci_get_address_space(dev), addr, buf, len, + dir, attrs); +} + +/** + * pci_dma_read: Read from an address space from PCI device. + * + * Return a MemTxResult indicating whether the operation succeeded + * or failed (eg unassigned memory, device rejected the transaction, + * IOMMU fault). Called within RCU critical section. + * + * @dev: #PCIDevice doing the memory access + * @addr: address within the #PCIDevice address space + * @buf: buffer with the data transferred + * @len: length of the data transferred + */ +static inline MemTxResult pci_dma_read(PCIDevice *dev, dma_addr_t addr, + void *buf, dma_addr_t len) +{ + return pci_dma_rw(dev, addr, buf, len, + DMA_DIRECTION_TO_DEVICE, MEMTXATTRS_UNSPECIFIED); +} + +/** + * pci_dma_write: Write to address space from PCI device. + * + * Return a MemTxResult indicating whether the operation succeeded + * or failed (eg unassigned memory, device rejected the transaction, + * IOMMU fault). + * + * @dev: #PCIDevice doing the memory access + * @addr: address within the #PCIDevice address space + * @buf: buffer with the data transferred + * @len: the number of bytes to write + */ +static inline MemTxResult pci_dma_write(PCIDevice *dev, dma_addr_t addr, + const void *buf, dma_addr_t len) +{ + return pci_dma_rw(dev, addr, (void *) buf, len, + DMA_DIRECTION_FROM_DEVICE, MEMTXATTRS_UNSPECIFIED); +} + +#define PCI_DMA_DEFINE_LDST(_l, _s, _bits) \ + static inline MemTxResult ld##_l##_pci_dma(PCIDevice *dev, \ + dma_addr_t addr, \ + uint##_bits##_t *val, \ + MemTxAttrs attrs) \ + { \ + return ld##_l##_dma(pci_get_address_space(dev), addr, val, attrs); \ + } \ + static inline MemTxResult st##_s##_pci_dma(PCIDevice *dev, \ + dma_addr_t addr, \ + uint##_bits##_t val, \ + MemTxAttrs attrs) \ + { \ + return st##_s##_dma(pci_get_address_space(dev), addr, val, attrs); \ + } + +PCI_DMA_DEFINE_LDST(ub, b, 8); +PCI_DMA_DEFINE_LDST(uw_le, w_le, 16) +PCI_DMA_DEFINE_LDST(l_le, l_le, 32); +PCI_DMA_DEFINE_LDST(q_le, q_le, 64); +PCI_DMA_DEFINE_LDST(uw_be, w_be, 16) +PCI_DMA_DEFINE_LDST(l_be, l_be, 32); +PCI_DMA_DEFINE_LDST(q_be, q_be, 64); + +#undef PCI_DMA_DEFINE_LDST + +/** + * pci_dma_map: Map device PCI address space range into host virtual address + * @dev: #PCIDevice to be accessed + * @addr: address within that device's address space + * @plen: pointer to length of buffer; updated on return to indicate + * if only a subset of the requested range has been mapped + * @dir: indicates the transfer direction + * + * Return: A host pointer, or %NULL if the resources needed to + * perform the mapping are exhausted (in that case *@plen + * is set to zero). + */ +static inline void *pci_dma_map(PCIDevice *dev, dma_addr_t addr, + dma_addr_t *plen, DMADirection dir) +{ + return dma_memory_map(pci_get_address_space(dev), addr, plen, dir, + MEMTXATTRS_UNSPECIFIED); +} + +static inline void pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len, + DMADirection dir, dma_addr_t access_len) +{ + dma_memory_unmap(pci_get_address_space(dev), buffer, len, dir, access_len); +} + +static inline void pci_dma_sglist_init(QEMUSGList *qsg, PCIDevice *dev, + int alloc_hint) +{ + qemu_sglist_init(qsg, DEVICE(dev), alloc_hint, pci_get_address_space(dev)); +} + +extern const VMStateDescription vmstate_pci_device; + +#define VMSTATE_PCI_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(PCIDevice), \ + .vmsd = &vmstate_pci_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, PCIDevice), \ +} + +#define VMSTATE_PCI_DEVICE_POINTER(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(PCIDevice), \ + .vmsd = &vmstate_pci_device, \ + .flags = VMS_STRUCT | VMS_POINTER, \ + .offset = vmstate_offset_pointer(_state, _field, PCIDevice), \ +} + +#endif diff --git a/include/hw/pci/pci_host.h b/include/hw/pci/pci_host.h index c6f4eb4585..e52d8ec2cd 100644 --- a/include/hw/pci/pci_host.h +++ b/include/hw/pci/pci_host.h @@ -31,6 +31,8 @@ #include "hw/sysbus.h" #include "qom/object.h" +#define PCI_HOST_BYPASS_IOMMU "bypass-iommu" + #define TYPE_PCI_HOST_BRIDGE "pci-host-bridge" OBJECT_DECLARE_TYPE(PCIHostState, PCIHostBridgeClass, PCI_HOST_BRIDGE) diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index 898083b86f..f1a53fea8d 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -26,6 +26,7 @@ #define PCI_CLASS_STORAGE_SATA 0x0106 #define PCI_CLASS_STORAGE_SAS 0x0107 #define PCI_CLASS_STORAGE_EXPRESS 0x0108 +#define PCI_CLASS_STORAGE_UFS 0x0109 #define PCI_CLASS_STORAGE_OTHER 0x0180 #define PCI_BASE_CLASS_NETWORK 0x02 @@ -157,6 +158,9 @@ /* Vendors and devices. Sort key: vendor first, device next. */ +/* Ref: PCIe r6.0 Table 6-32 */ +#define PCI_VENDOR_ID_PCI_SIG 0x0001 + #define PCI_VENDOR_ID_LSI_LOGIC 0x1000 #define PCI_DEVICE_ID_LSI_53C810 0x0001 #define PCI_DEVICE_ID_LSI_53C895A 0x0012 @@ -166,7 +170,6 @@ #define PCI_VENDOR_ID_DEC 0x1011 #define PCI_DEVICE_ID_DEC_21143 0x0019 -#define PCI_DEVICE_ID_DEC_21154 0x0026 #define PCI_VENDOR_ID_CIRRUS 0x1013 @@ -176,6 +179,8 @@ #define PCI_DEVICE_ID_AMD_LANCE 0x2000 #define PCI_DEVICE_ID_AMD_SCSI 0x2020 +#define PCI_VENDOR_ID_HP 0x103c + #define PCI_VENDOR_ID_TI 0x104c #define PCI_VENDOR_ID_MOTOROLA 0x1057 @@ -238,6 +243,7 @@ #define PCI_DEVICE_ID_INTEL_82801BA_11 0x244e #define PCI_DEVICE_ID_INTEL_82801D 0x24CD #define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab +#define PCI_DEVICE_ID_INTEL_NVME 0x5845 #define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 #define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 #define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 798a262a0a..11f5a91bbb 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -27,14 +27,6 @@ #include "hw/pci/pcie_sriov.h" #include "hw/hotplug.h" -typedef enum { - /* for attention and power indicator */ - PCI_EXP_HP_IND_RESERVED = PCI_EXP_SLTCTL_IND_RESERVED, - PCI_EXP_HP_IND_ON = PCI_EXP_SLTCTL_IND_ON, - PCI_EXP_HP_IND_BLINK = PCI_EXP_SLTCTL_IND_BLINK, - PCI_EXP_HP_IND_OFF = PCI_EXP_SLTCTL_IND_OFF, -} PCIExpressIndicator; - typedef enum { /* these bits must match the bits in Slot Control/Status registers. * PCI_EXP_HP_EV_xxx = PCI_EXP_SLTCTL_xxxE = PCI_EXP_SLTSTA_xxx @@ -101,6 +93,7 @@ void pcie_cap_exit(PCIDevice *dev); int pcie_endpoint_cap_v1_init(PCIDevice *dev, uint8_t offset); void pcie_cap_v1_exit(PCIDevice *dev); uint8_t pcie_cap_get_type(const PCIDevice *dev); +uint8_t pcie_cap_get_version(const PCIDevice *dev); void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector); uint8_t pcie_cap_flags_get_vector(PCIDevice *dev); @@ -142,7 +135,7 @@ void pcie_sync_bridge_lnk(PCIDevice *dev); void pcie_acs_init(PCIDevice *dev, uint16_t offset); void pcie_acs_reset(PCIDevice *dev); -void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn); +void pcie_ari_init(PCIDevice *dev, uint16_t offset); void pcie_dev_ser_num_init(PCIDevice *dev, uint16_t offset, uint64_t ser_num); void pcie_ats_init(PCIDevice *dev, uint16_t offset, bool aligned); diff --git a/include/hw/pci/pcie_aer.h b/include/hw/pci/pcie_aer.h index 65e71d98fe..4a9f0ea69d 100644 --- a/include/hw/pci/pcie_aer.h +++ b/include/hw/pci/pcie_aer.h @@ -40,7 +40,7 @@ struct PCIEAERLog { * The specified value will be clipped down to PCIE_AER_LOG_MAX_LIMIT * to avoid unreasonable memory usage. * I bet that 128 log size would be big enough, otherwise too many errors - * for system to function normaly. But could consecutive errors occur? + * for system to function normally. But could consecutive errors occur? */ #define PCIE_AER_LOG_MAX_DEFAULT 8 #define PCIE_AER_LOG_MAX_LIMIT 128 @@ -100,4 +100,5 @@ void pcie_aer_root_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len, uint32_t root_cmd_prev); +int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err); #endif /* QEMU_PCIE_AER_H */ diff --git a/include/hw/pci/pcie_doe.h b/include/hw/pci/pcie_doe.h new file mode 100644 index 0000000000..87dc17dcef --- /dev/null +++ b/include/hw/pci/pcie_doe.h @@ -0,0 +1,122 @@ +/* + * PCIe Data Object Exchange + * + * Copyright (C) 2021 Avery Design Systems, 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 PCIE_DOE_H +#define PCIE_DOE_H + +#include "qemu/range.h" +#include "hw/register.h" + +/* + * Reference: + * PCIe r6.0 - 7.9.24 Data Object Exchange Extended Capability + */ +/* Capabilities Register - r6.0 7.9.24.2 */ +#define PCI_EXP_DOE_CAP 0x04 +REG32(PCI_DOE_CAP_REG, 0) + FIELD(PCI_DOE_CAP_REG, INTR_SUPP, 0, 1) + FIELD(PCI_DOE_CAP_REG, DOE_INTR_MSG_NUM, 1, 11) + +/* Control Register - r6.0 7.9.24.3 */ +#define PCI_EXP_DOE_CTRL 0x08 +REG32(PCI_DOE_CAP_CONTROL, 0) + FIELD(PCI_DOE_CAP_CONTROL, DOE_ABORT, 0, 1) + FIELD(PCI_DOE_CAP_CONTROL, DOE_INTR_EN, 1, 1) + FIELD(PCI_DOE_CAP_CONTROL, DOE_GO, 31, 1) + +/* Status Register - r6.0 7.9.24.4 */ +#define PCI_EXP_DOE_STATUS 0x0c +REG32(PCI_DOE_CAP_STATUS, 0) + FIELD(PCI_DOE_CAP_STATUS, DOE_BUSY, 0, 1) + FIELD(PCI_DOE_CAP_STATUS, DOE_INTR_STATUS, 1, 1) + FIELD(PCI_DOE_CAP_STATUS, DOE_ERROR, 2, 1) + FIELD(PCI_DOE_CAP_STATUS, DATA_OBJ_RDY, 31, 1) + +/* Write Data Mailbox Register - r6.0 7.9.24.5 */ +#define PCI_EXP_DOE_WR_DATA_MBOX 0x10 + +/* Read Data Mailbox Register - 7.9.xx.6 */ +#define PCI_EXP_DOE_RD_DATA_MBOX 0x14 + +/* PCI-SIG defined Data Object Types - r6.0 Table 6-32 */ +#define PCI_SIG_DOE_DISCOVERY 0x00 + +#define PCI_DOE_DW_SIZE_MAX (1 << 18) +#define PCI_DOE_PROTOCOL_NUM_MAX 256 + +#define DATA_OBJ_BUILD_HEADER1(v, p) (((p) << 16) | (v)) +#define DATA_OBJ_LEN_MASK(len) ((len) & (PCI_DOE_DW_SIZE_MAX - 1)) + +typedef struct DOEHeader DOEHeader; +typedef struct DOEProtocol DOEProtocol; +typedef struct DOECap DOECap; + +struct DOEHeader { + uint16_t vendor_id; + uint8_t data_obj_type; + uint8_t reserved; + uint32_t length; +} QEMU_PACKED; + +/* Protocol infos and rsp function callback */ +struct DOEProtocol { + uint16_t vendor_id; + uint8_t data_obj_type; + bool (*handle_request)(DOECap *); +}; + +struct DOECap { + /* Owner */ + PCIDevice *pdev; + + uint16_t offset; + + struct { + bool intr; + uint16_t vec; + } cap; + + struct { + bool abort; + bool intr; + bool go; + } ctrl; + + struct { + bool busy; + bool intr; + bool error; + bool ready; + } status; + + uint32_t *write_mbox; + uint32_t *read_mbox; + + /* Mailbox position indicator */ + uint32_t read_mbox_idx; + uint32_t read_mbox_len; + uint32_t write_mbox_len; + + /* Protocols and its callback response */ + DOEProtocol *protocols; + uint16_t protocol_num; +}; + +void pcie_doe_init(PCIDevice *pdev, DOECap *doe_cap, uint16_t offset, + DOEProtocol *protocols, bool intr, uint16_t vec); +void pcie_doe_fini(DOECap *doe_cap); +bool pcie_doe_read_config(DOECap *doe_cap, uint32_t addr, int size, + uint32_t *buf); +void pcie_doe_write_config(DOECap *doe_cap, uint32_t addr, + uint32_t val, int size); +uint32_t pcie_doe_build_protocol(DOEProtocol *p); +void *pcie_doe_get_write_mbox_ptr(DOECap *doe_cap); +void pcie_doe_set_rsp(DOECap *doe_cap, void *rsp); +uint32_t pcie_doe_get_obj_len(void *obj); +#endif /* PCIE_DOE_H */ diff --git a/include/hw/pci/pcie_port.h b/include/hw/pci/pcie_port.h index 7b8193061a..90e6cf45b8 100644 --- a/include/hw/pci/pcie_port.h +++ b/include/hw/pci/pcie_port.h @@ -23,6 +23,7 @@ #include "hw/pci/pci_bridge.h" #include "hw/pci/pci_bus.h" +#include "hw/pci/pci_device.h" #include "qom/object.h" #define TYPE_PCIE_PORT "pcie-port" @@ -40,6 +41,8 @@ struct PCIEPort { void pcie_port_init_reg(PCIDevice *d); PCIDevice *pcie_find_port_by_pn(PCIBus *bus, uint8_t pn); +PCIDevice *pcie_find_port_first(PCIBus *bus); +int pcie_count_ds_ports(PCIBus *bus); #define TYPE_PCIE_SLOT "pcie-slot" OBJECT_DECLARE_SIMPLE_TYPE(PCIESlot, PCIE_SLOT) @@ -62,7 +65,8 @@ struct PCIESlot { /* Indicates whether any type of hot-plug is allowed on the slot */ bool hotplug; - bool native_hotplug; + /* broken ACPI hotplug compat knob to preserve 6.1 ABI intact */ + bool hide_native_hotplug_cap; QLIST_ENTRY(PCIESlot) next; }; @@ -80,7 +84,7 @@ DECLARE_CLASS_CHECKERS(PCIERootPortClass, PCIE_ROOT_PORT, struct PCIERootPortClass { PCIDeviceClass parent_class; DeviceRealize parent_realize; - DeviceReset parent_reset; + ResettablePhases parent_phases; uint8_t (*aer_vector)(const PCIDevice *dev); int (*interrupts_init)(PCIDevice *dev, Error **errp); diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h index 1db86b0ec4..9d3b6868dc 100644 --- a/include/hw/pci/pcie_regs.h +++ b/include/hw/pci/pcie_regs.h @@ -39,6 +39,8 @@ typedef enum PCIExpLinkSpeed { QEMU_PCI_EXP_LNK_5GT, QEMU_PCI_EXP_LNK_8GT, QEMU_PCI_EXP_LNK_16GT, + QEMU_PCI_EXP_LNK_32GT, + QEMU_PCI_EXP_LNK_64GT, } PCIExpLinkSpeed; #define QEMU_PCI_EXP_LNKCAP_MLS(speed) (speed) @@ -66,20 +68,6 @@ typedef enum PCIExpLinkWidth { #define PCI_EXP_SLTCAP_PSN_SHIFT ctz32(PCI_EXP_SLTCAP_PSN) -#define PCI_EXP_SLTCTL_IND_RESERVED 0x0 -#define PCI_EXP_SLTCTL_IND_ON 0x1 -#define PCI_EXP_SLTCTL_IND_BLINK 0x2 -#define PCI_EXP_SLTCTL_IND_OFF 0x3 -#define PCI_EXP_SLTCTL_AIC_SHIFT ctz32(PCI_EXP_SLTCTL_AIC) -#define PCI_EXP_SLTCTL_AIC_OFF \ - (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_AIC_SHIFT) - -#define PCI_EXP_SLTCTL_PIC_SHIFT ctz32(PCI_EXP_SLTCTL_PIC) -#define PCI_EXP_SLTCTL_PIC_OFF \ - (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_PIC_SHIFT) -#define PCI_EXP_SLTCTL_PIC_ON \ - (PCI_EXP_SLTCTL_IND_ON << PCI_EXP_SLTCTL_PIC_SHIFT) - #define PCI_EXP_SLTCTL_SUPPORTED \ (PCI_EXP_SLTCTL_ABPE | \ PCI_EXP_SLTCTL_PDCE | \ @@ -155,6 +143,9 @@ typedef enum PCIExpLinkWidth { PCI_ERR_UNC_ATOP_EBLOCKED | \ PCI_ERR_UNC_TLP_PRF_BLOCKED) +#define PCI_ERR_UNC_MASK_DEFAULT (PCI_ERR_UNC_INTN | \ + PCI_ERR_UNC_TLP_PRF_BLOCKED) + #define PCI_ERR_UNC_SEVERITY_DEFAULT (PCI_ERR_UNC_DLP | \ PCI_ERR_UNC_SDN | \ PCI_ERR_UNC_FCP | \ @@ -179,4 +170,8 @@ typedef enum PCIExpLinkWidth { #define PCI_ACS_VER 0x1 #define PCI_ACS_SIZEOF 8 +/* DOE Capability Register Fields */ +#define PCI_DOE_VER 0x1 +#define PCI_DOE_SIZEOF 24 + #endif /* QEMU_PCIE_REGS_H */ diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index 80f5c84e75..b77eb7bf58 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -13,6 +13,8 @@ #ifndef QEMU_PCIE_SRIOV_H #define QEMU_PCIE_SRIOV_H +#include "hw/pci/pci.h" + struct PCIESriovPF { uint16_t num_vfs; /* Number of virtual functions created */ uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ @@ -56,8 +58,8 @@ void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize); void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, uint32_t val, int len); -/* Reset SR/IOV VF Enable bit to unregister all VFs */ -void pcie_sriov_pf_disable_vfs(PCIDevice *dev); +/* Reset SR/IOV */ +void pcie_sriov_pf_reset(PCIDevice *dev); /* Get logical VF number of a VF - only valid for VFs */ uint16_t pcie_sriov_vf_number(PCIDevice *dev); @@ -74,4 +76,7 @@ PCIDevice *pcie_sriov_get_pf(PCIDevice *dev); */ PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n); +/* Returns the current number of virtual functions. */ +uint16_t pcie_sriov_num_vfs(PCIDevice *dev); + #endif /* QEMU_PCIE_SRIOV_H */ diff --git a/include/hw/pci/shpc.h b/include/hw/pci/shpc.h index d5683b7399..a0789df153 100644 --- a/include/hw/pci/shpc.h +++ b/include/hw/pci/shpc.h @@ -3,7 +3,7 @@ #include "exec/memory.h" #include "hw/hotplug.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "migration/vmstate.h" struct SHPCDevice { @@ -52,7 +52,7 @@ void shpc_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, void shpc_device_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -extern VMStateInfo shpc_vmstate_info; +extern const VMStateInfo shpc_vmstate_info; #define SHPC_VMSTATE(_field, _type, _test) \ VMSTATE_BUFFER_UNSAFE_INFO_TEST(_field, _type, _test, 0, \ shpc_vmstate_info, 0) diff --git a/include/hw/pcmcia.h b/include/hw/pcmcia.h index e3ba44e0bf..ab26802751 100644 --- a/include/hw/pcmcia.h +++ b/include/hw/pcmcia.h @@ -43,22 +43,22 @@ struct PCMCIACardClass { void (*io_write)(PCMCIACardState *card, uint32_t address, uint16_t value); }; -#define CISTPL_DEVICE 0x01 /* 5V Device Information Tuple */ -#define CISTPL_NO_LINK 0x14 /* No Link Tuple */ -#define CISTPL_VERS_1 0x15 /* Level 1 Version Tuple */ -#define CISTPL_JEDEC_C 0x18 /* JEDEC ID Tuple */ -#define CISTPL_JEDEC_A 0x19 /* JEDEC ID Tuple */ -#define CISTPL_CONFIG 0x1a /* Configuration Tuple */ -#define CISTPL_CFTABLE_ENTRY 0x1b /* 16-bit PCCard Configuration */ -#define CISTPL_DEVICE_OC 0x1c /* Additional Device Information */ -#define CISTPL_DEVICE_OA 0x1d /* Additional Device Information */ -#define CISTPL_DEVICE_GEO 0x1e /* Additional Device Information */ -#define CISTPL_DEVICE_GEO_A 0x1f /* Additional Device Information */ -#define CISTPL_MANFID 0x20 /* Manufacture ID Tuple */ -#define CISTPL_FUNCID 0x21 /* Function ID Tuple */ -#define CISTPL_FUNCE 0x22 /* Function Extension Tuple */ -#define CISTPL_END 0xff /* Tuple End */ -#define CISTPL_ENDMARK 0xff +#define CISTPL_DEVICE 0x01 /* 5V Device Information Tuple */ +#define CISTPL_NO_LINK 0x14 /* No Link Tuple */ +#define CISTPL_VERS_1 0x15 /* Level 1 Version Tuple */ +#define CISTPL_JEDEC_C 0x18 /* JEDEC ID Tuple */ +#define CISTPL_JEDEC_A 0x19 /* JEDEC ID Tuple */ +#define CISTPL_CONFIG 0x1a /* Configuration Tuple */ +#define CISTPL_CFTABLE_ENTRY 0x1b /* 16-bit PCCard Configuration */ +#define CISTPL_DEVICE_OC 0x1c /* Additional Device Information */ +#define CISTPL_DEVICE_OA 0x1d /* Additional Device Information */ +#define CISTPL_DEVICE_GEO 0x1e /* Additional Device Information */ +#define CISTPL_DEVICE_GEO_A 0x1f /* Additional Device Information */ +#define CISTPL_MANFID 0x20 /* Manufacture ID Tuple */ +#define CISTPL_FUNCID 0x21 /* Function ID Tuple */ +#define CISTPL_FUNCE 0x22 /* Function Extension Tuple */ +#define CISTPL_END 0xff /* Tuple End */ +#define CISTPL_ENDMARK 0xff /* dscm1xxxx.c */ PCMCIACardState *dscm1xxxx_init(DriveInfo *bdrv); diff --git a/include/hw/ppc/fdt.h b/include/hw/ppc/fdt.h index a8cd85069f..b56ac2a8cb 100644 --- a/include/hw/ppc/fdt.h +++ b/include/hw/ppc/fdt.h @@ -15,10 +15,10 @@ #define _FDT(exp) \ do { \ - int ret = (exp); \ - if (ret < 0) { \ - error_report("error creating device tree: %s: %s", \ - #exp, fdt_strerror(ret)); \ + int _ret = (exp); \ + if (_ret < 0) { \ + error_report("error creating device tree: %s: %s", \ + #exp, fdt_strerror(_ret)); \ exit(1); \ } \ } while (0) diff --git a/include/hw/ppc/openpic.h b/include/hw/ppc/openpic.h index ebdaf8a493..9c6af8e207 100644 --- a/include/hw/ppc/openpic.h +++ b/include/hw/ppc/openpic.h @@ -14,7 +14,7 @@ enum { OPENPIC_OUTPUT_INT = 0, /* IRQ */ OPENPIC_OUTPUT_CINT, /* critical IRQ */ OPENPIC_OUTPUT_MCK, /* Machine check event */ - OPENPIC_OUTPUT_DEBUG, /* Inconditional debug event */ + OPENPIC_OUTPUT_DEBUG, /* Unconditional debug event */ OPENPIC_OUTPUT_RESET, /* Core reset event */ OPENPIC_OUTPUT_NB, }; @@ -55,7 +55,7 @@ typedef enum IRQType { * Round up to the nearest 64 IRQs so that the queue length * won't change when moving between 32 and 64 bit hosts. */ -#define IRQQUEUE_SIZE_BITS ((OPENPIC_MAX_IRQ + 63) & ~63) +#define IRQQUEUE_SIZE_BITS ROUND_UP(OPENPIC_MAX_IRQ, 64) typedef struct IRQQueue { unsigned long *queue; diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 86cb7d7f97..476b136146 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -20,150 +20,19 @@ #ifndef PPC_PNV_H #define PPC_PNV_H +#include "cpu.h" #include "hw/boards.h" #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" -#include "qom/object.h" #define TYPE_PNV_CHIP "pnv-chip" -OBJECT_DECLARE_TYPE(PnvChip, PnvChipClass, - PNV_CHIP) -struct PnvChip { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - uint32_t chip_id; - uint64_t ram_start; - uint64_t ram_size; - - uint32_t nr_cores; - uint32_t nr_threads; - uint64_t cores_mask; - PnvCore **cores; - - uint32_t num_pecs; - - MemoryRegion xscom_mmio; - MemoryRegion xscom; - AddressSpace xscom_as; - - MemoryRegion *fw_mr; - gchar *dt_isa_nodename; -}; - -#define TYPE_PNV8_CHIP "pnv8-chip" +typedef struct PnvCore PnvCore; +typedef struct PnvChip PnvChip; typedef struct Pnv8Chip Pnv8Chip; -DECLARE_INSTANCE_CHECKER(Pnv8Chip, PNV8_CHIP, - TYPE_PNV8_CHIP) - -struct Pnv8Chip { - /*< private >*/ - PnvChip parent_obj; - - /*< public >*/ - MemoryRegion icp_mmio; - - PnvLpcController lpc; - Pnv8Psi psi; - PnvOCC occ; - PnvHomer homer; - -#define PNV8_CHIP_PHB3_MAX 4 - PnvPHB3 phbs[PNV8_CHIP_PHB3_MAX]; - uint32_t num_phbs; - - XICSFabric *xics; -}; - -#define TYPE_PNV9_CHIP "pnv9-chip" typedef struct Pnv9Chip Pnv9Chip; -DECLARE_INSTANCE_CHECKER(Pnv9Chip, PNV9_CHIP, - TYPE_PNV9_CHIP) - -struct Pnv9Chip { - /*< private >*/ - PnvChip parent_obj; - - /*< public >*/ - PnvXive xive; - Pnv9Psi psi; - PnvLpcController lpc; - PnvOCC occ; - PnvHomer homer; - - uint32_t nr_quads; - PnvQuad *quads; - -#define PNV9_CHIP_MAX_PEC 3 - PnvPhb4PecState pecs[PNV9_CHIP_MAX_PEC]; -}; - -/* - * 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" typedef struct Pnv10Chip Pnv10Chip; -DECLARE_INSTANCE_CHECKER(Pnv10Chip, PNV10_CHIP, - TYPE_PNV10_CHIP) - -struct Pnv10Chip { - /*< private >*/ - PnvChip parent_obj; - - /*< public >*/ - PnvXive2 xive; - Pnv9Psi psi; - PnvLpcController lpc; - PnvOCC occ; - PnvHomer homer; - - uint32_t nr_quads; - PnvQuad *quads; - -#define PNV10_CHIP_MAX_PEC 2 - PnvPhb4PecState pecs[PNV10_CHIP_MAX_PEC]; -}; - -#define PNV10_PIR2FUSEDCORE(pir) (((pir) >> 3) & 0xf) -#define PNV10_PIR2CHIP(pir) (((pir) >> 8) & 0x7f) - -struct PnvChipClass { - /*< private >*/ - SysBusDeviceClass parent_class; - - /*< public >*/ - uint64_t chip_cfam_id; - uint64_t cores_mask; - uint32_t num_pecs; - uint32_t num_phbs; - - DeviceRealize parent_realize; - - uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id); - 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); -}; #define PNV_CHIP_TYPE_SUFFIX "-" TYPE_PNV_CHIP #define PNV_CHIP_TYPE_NAME(cpu_model) cpu_model PNV_CHIP_TYPE_SUFFIX @@ -180,7 +49,7 @@ DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER8, DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER8NVL, TYPE_PNV_CHIP_POWER8NVL) -#define TYPE_PNV_CHIP_POWER9 PNV_CHIP_TYPE_NAME("power9_v2.0") +#define TYPE_PNV_CHIP_POWER9 PNV_CHIP_TYPE_NAME("power9_v2.2") DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER9, TYPE_PNV_CHIP_POWER9) @@ -188,8 +57,10 @@ DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER9, DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER10, TYPE_PNV_CHIP_POWER10) +PnvCore *pnv_chip_find_core(PnvChip *chip, uint32_t core_id); PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir); -void pnv_phb_attach_root_port(PCIHostState *pci, const char *name); + +typedef struct PnvPHB PnvPHB; #define TYPE_PNV_MACHINE MACHINE_TYPE_NAME("powernv") typedef struct PnvMachineClass PnvMachineClass; @@ -207,6 +78,7 @@ struct PnvMachineClass { int compat_size; void (*dt_power_mgt)(PnvMachineState *pnv, void *fdt); + void (*i2c_init)(PnvMachineState *pnv); }; struct PnvMachineState { @@ -231,6 +103,7 @@ struct PnvMachineState { }; PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id); +PnvChip *pnv_chip_add_phb(PnvChip *chip, PnvPHB *phb); #define PNV_FDT_ADDR 0x01000000 #define PNV_TIMEBASE_FREQ 512000000ULL diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h new file mode 100644 index 0000000000..8589f3291e --- /dev/null +++ b/include/hw/ppc/pnv_chip.h @@ -0,0 +1,162 @@ +#ifndef PPC_PNV_CHIP_H +#define PPC_PNV_CHIP_H + +#include "hw/pci-host/pnv_phb4.h" +#include "hw/ppc/pnv_chiptod.h" +#include "hw/ppc/pnv_core.h" +#include "hw/ppc/pnv_homer.h" +#include "hw/ppc/pnv_n1_chiplet.h" +#include "hw/ppc/pnv_lpc.h" +#include "hw/ppc/pnv_occ.h" +#include "hw/ppc/pnv_psi.h" +#include "hw/ppc/pnv_sbe.h" +#include "hw/ppc/pnv_xive.h" +#include "hw/ppc/pnv_i2c.h" +#include "hw/sysbus.h" + +OBJECT_DECLARE_TYPE(PnvChip, PnvChipClass, + PNV_CHIP) + +struct PnvChip { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + uint32_t chip_id; + uint64_t ram_start; + uint64_t ram_size; + + uint32_t nr_cores; + uint32_t nr_threads; + uint64_t cores_mask; + PnvCore **cores; + + uint32_t num_pecs; + + MemoryRegion xscom_mmio; + MemoryRegion xscom; + AddressSpace xscom_as; + + MemoryRegion *fw_mr; + gchar *dt_isa_nodename; +}; + +#define TYPE_PNV8_CHIP "pnv8-chip" +DECLARE_INSTANCE_CHECKER(Pnv8Chip, PNV8_CHIP, + TYPE_PNV8_CHIP) + +struct Pnv8Chip { + /*< private >*/ + PnvChip parent_obj; + + /*< public >*/ + MemoryRegion icp_mmio; + + PnvLpcController lpc; + Pnv8Psi psi; + PnvOCC occ; + PnvHomer homer; + +#define PNV8_CHIP_PHB3_MAX 4 + /* + * The array is used to allow quick access to the phbs by + * pnv_ics_get_child() and pnv_ics_resend_child(). + */ + PnvPHB *phbs[PNV8_CHIP_PHB3_MAX]; + uint32_t num_phbs; + + XICSFabric *xics; +}; + +#define TYPE_PNV9_CHIP "pnv9-chip" +DECLARE_INSTANCE_CHECKER(Pnv9Chip, PNV9_CHIP, + TYPE_PNV9_CHIP) + +struct Pnv9Chip { + /*< private >*/ + PnvChip parent_obj; + + /*< public >*/ + PnvXive xive; + Pnv9Psi psi; + PnvLpcController lpc; + PnvChipTOD chiptod; + PnvOCC occ; + PnvSBE sbe; + PnvHomer homer; + + uint32_t nr_quads; + PnvQuad *quads; + +#define PNV9_CHIP_MAX_PEC 3 + PnvPhb4PecState pecs[PNV9_CHIP_MAX_PEC]; + +#define PNV9_CHIP_MAX_I2C 4 + PnvI2C i2c[PNV9_CHIP_MAX_I2C]; +}; + +/* + * 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" +DECLARE_INSTANCE_CHECKER(Pnv10Chip, PNV10_CHIP, + TYPE_PNV10_CHIP) + +struct Pnv10Chip { + /*< private >*/ + PnvChip parent_obj; + + /*< public >*/ + PnvXive2 xive; + Pnv9Psi psi; + PnvLpcController lpc; + PnvChipTOD chiptod; + PnvOCC occ; + PnvSBE sbe; + PnvHomer homer; + PnvN1Chiplet n1_chiplet; + + uint32_t nr_quads; + PnvQuad *quads; + +#define PNV10_CHIP_MAX_PEC 2 + PnvPhb4PecState pecs[PNV10_CHIP_MAX_PEC]; + +#define PNV10_CHIP_MAX_I2C 4 + PnvI2C i2c[PNV10_CHIP_MAX_I2C]; +}; + +#define PNV10_PIR2FUSEDCORE(pir) (((pir) >> 3) & 0xf) +#define PNV10_PIR2CHIP(pir) (((pir) >> 8) & 0x7f) + +struct PnvChipClass { + /*< private >*/ + SysBusDeviceClass parent_class; + + /*< public >*/ + uint64_t chip_cfam_id; + uint64_t cores_mask; + uint32_t num_pecs; + uint32_t num_phbs; + + uint32_t i2c_num_engines; + const int *i2c_ports_per_engine; + + DeviceRealize parent_realize; + + uint32_t (*chip_pir)(PnvChip *chip, uint32_t core_id, uint32_t thread_id); + 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); +}; + +#endif diff --git a/include/hw/ppc/pnv_chiptod.h b/include/hw/ppc/pnv_chiptod.h new file mode 100644 index 0000000000..fde569bcbf --- /dev/null +++ b/include/hw/ppc/pnv_chiptod.h @@ -0,0 +1,53 @@ +/* + * QEMU PowerPC PowerNV Emulation of some CHIPTOD behaviour + * + * Copyright (c) 2022-2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PPC_PNV_CHIPTOD_H +#define PPC_PNV_CHIPTOD_H + +#include "qom/object.h" + +#define TYPE_PNV_CHIPTOD "pnv-chiptod" +OBJECT_DECLARE_TYPE(PnvChipTOD, PnvChipTODClass, PNV_CHIPTOD) +#define TYPE_PNV9_CHIPTOD TYPE_PNV_CHIPTOD "-POWER9" +DECLARE_INSTANCE_CHECKER(PnvChipTOD, PNV9_CHIPTOD, TYPE_PNV9_CHIPTOD) +#define TYPE_PNV10_CHIPTOD TYPE_PNV_CHIPTOD "-POWER10" +DECLARE_INSTANCE_CHECKER(PnvChipTOD, PNV10_CHIPTOD, TYPE_PNV10_CHIPTOD) + +enum tod_state { + tod_error = 0, + tod_not_set = 7, + tod_running = 2, + tod_stopped = 1, +}; + +typedef struct PnvCore PnvCore; + +struct PnvChipTOD { + DeviceState xd; + + PnvChip *chip; + MemoryRegion xscom_regs; + + bool primary; + bool secondary; + enum tod_state tod_state; + uint64_t tod_error; + uint64_t pss_mss_ctrl_reg; + PnvCore *slave_pc_target; +}; + +struct PnvChipTODClass { + DeviceClass parent_class; + + void (*broadcast_ttype)(PnvChipTOD *sender, uint32_t trigger); + PnvCore *(*tx_ttype_target)(PnvChipTOD *chiptod, uint64_t val); + + int xscom_size; +}; + +#endif /* PPC_PNV_CHIPTOD_H */ diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h index c22eab2e1f..c6d62fd145 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -22,14 +22,13 @@ #include "hw/cpu/core.h" #include "target/ppc/cpu.h" +#include "hw/ppc/pnv.h" #include "qom/object.h" #define TYPE_PNV_CORE "powernv-cpu-core" OBJECT_DECLARE_TYPE(PnvCore, PnvCoreClass, PNV_CORE) -typedef struct PnvChip PnvChip; - struct PnvCore { /*< private >*/ CPUCore parent_obj; @@ -37,6 +36,7 @@ struct PnvCore { /*< public >*/ PowerPCCPU **threads; uint32_t pir; + uint32_t hwid; uint64_t hrmor; PnvChip *chip; @@ -47,6 +47,7 @@ struct PnvCoreClass { DeviceClass parent_class; const MemoryRegionOps *xscom_ops; + uint64_t xscom_size; }; #define PNV_CORE_TYPE_SUFFIX "-" TYPE_PNV_CORE @@ -61,13 +62,28 @@ static inline PnvCPUState *pnv_cpu_state(PowerPCCPU *cpu) return (PnvCPUState *)cpu->machine_data; } +struct PnvQuadClass { + DeviceClass parent_class; + + const MemoryRegionOps *xscom_ops; + uint64_t xscom_size; + + const MemoryRegionOps *xscom_qme_ops; + uint64_t xscom_qme_size; +}; + #define TYPE_PNV_QUAD "powernv-cpu-quad" -OBJECT_DECLARE_SIMPLE_TYPE(PnvQuad, PNV_QUAD) + +#define PNV_QUAD_TYPE_SUFFIX "-" TYPE_PNV_QUAD +#define PNV_QUAD_TYPE_NAME(cpu_model) cpu_model PNV_QUAD_TYPE_SUFFIX + +OBJECT_DECLARE_TYPE(PnvQuad, PnvQuadClass, PNV_QUAD) struct PnvQuad { DeviceState parent_obj; uint32_t quad_id; MemoryRegion xscom_regs; + MemoryRegion xscom_qme_regs; }; #endif /* PPC_PNV_CORE_H */ diff --git a/include/hw/ppc/pnv_homer.h b/include/hw/ppc/pnv_homer.h index 07e8b19311..b1c5d498dc 100644 --- a/include/hw/ppc/pnv_homer.h +++ b/include/hw/ppc/pnv_homer.h @@ -39,7 +39,7 @@ DECLARE_INSTANCE_CHECKER(PnvHomer, PNV10_HOMER, struct PnvHomer { DeviceState parent; - struct PnvChip *chip; + PnvChip *chip; MemoryRegion pba_regs; MemoryRegion regs; }; diff --git a/include/hw/ppc/pnv_i2c.h b/include/hw/ppc/pnv_i2c.h new file mode 100644 index 0000000000..1a37730f1e --- /dev/null +++ b/include/hw/ppc/pnv_i2c.h @@ -0,0 +1,38 @@ +/* + * QEMU PowerPC PowerNV Processor I2C model + * + * Copyright (c) 2019-2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PPC_PNV_I2C_H +#define PPC_PNV_I2C_H + +#include "hw/ppc/pnv.h" +#include "hw/i2c/i2c.h" +#include "qemu/fifo8.h" + +#define TYPE_PNV_I2C "pnv-i2c" +#define PNV_I2C(obj) OBJECT_CHECK(PnvI2C, (obj), TYPE_PNV_I2C) + +#define PNV_I2C_REGS 0x20 + +typedef struct PnvI2C { + DeviceState parent; + + struct PnvChip *chip; + + qemu_irq psi_irq; + + uint64_t regs[PNV_I2C_REGS]; + uint32_t engine; + uint32_t num_busses; + I2CBus **busses; + + MemoryRegion xscom_regs; + + Fifo8 fifo; +} PnvI2C; + +#endif /* PPC_PNV_I2C_H */ diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h index 8a8d1a3d42..5d22c45570 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -20,7 +20,9 @@ #ifndef PPC_PNV_LPC_H #define PPC_PNV_LPC_H -#include "qom/object.h" +#include "exec/memory.h" +#include "hw/ppc/pnv.h" +#include "hw/qdev-core.h" #define TYPE_PNV_LPC "pnv-lpc" typedef struct PnvLpcClass PnvLpcClass; @@ -92,13 +94,8 @@ struct PnvLpcClass { DeviceRealize parent_realize; }; -/* - * Old compilers error on typdef forward declarations. Keep them happy. - */ -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(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_n1_chiplet.h b/include/hw/ppc/pnv_n1_chiplet.h new file mode 100644 index 0000000000..a7ad039668 --- /dev/null +++ b/include/hw/ppc/pnv_n1_chiplet.h @@ -0,0 +1,32 @@ +/* + * QEMU PowerPC N1 chiplet model + * + * Copyright (c) 2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PPC_PNV_N1_CHIPLET_H +#define PPC_PNV_N1_CHIPLET_H + +#include "hw/ppc/pnv_nest_pervasive.h" + +#define TYPE_PNV_N1_CHIPLET "pnv-N1-chiplet" +#define PNV_N1_CHIPLET(obj) OBJECT_CHECK(PnvN1Chiplet, (obj), TYPE_PNV_N1_CHIPLET) + +typedef struct PnvPbScom { + uint64_t mode; + uint64_t hp_mode2_curr; +} PnvPbScom; + +typedef struct PnvN1Chiplet { + DeviceState parent; + MemoryRegion xscom_pb_eq_mr; + MemoryRegion xscom_pb_es_mr; + PnvNestChipletPervasive nest_pervasive; /* common pervasive chiplet unit */ +#define PNV_PB_SCOM_EQ_SIZE 8 + PnvPbScom eq[PNV_PB_SCOM_EQ_SIZE]; +#define PNV_PB_SCOM_ES_SIZE 4 + PnvPbScom es[PNV_PB_SCOM_ES_SIZE]; +} PnvN1Chiplet; +#endif /*PPC_PNV_N1_CHIPLET_H */ diff --git a/include/hw/ppc/pnv_nest_pervasive.h b/include/hw/ppc/pnv_nest_pervasive.h new file mode 100644 index 0000000000..73cacf3823 --- /dev/null +++ b/include/hw/ppc/pnv_nest_pervasive.h @@ -0,0 +1,32 @@ +/* + * QEMU PowerPC nest pervasive common chiplet model + * + * Copyright (c) 2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PPC_PNV_NEST_CHIPLET_PERVASIVE_H +#define PPC_PNV_NEST_CHIPLET_PERVASIVE_H + +#define TYPE_PNV_NEST_CHIPLET_PERVASIVE "pnv-nest-chiplet-pervasive" +#define PNV_NEST_CHIPLET_PERVASIVE(obj) OBJECT_CHECK(PnvNestChipletPervasive, (obj), TYPE_PNV_NEST_CHIPLET_PERVASIVE) + +typedef struct PnvPervasiveCtrlRegs { +#define PNV_CPLT_CTRL_SIZE 6 + uint64_t cplt_ctrl[PNV_CPLT_CTRL_SIZE]; + uint64_t cplt_cfg0; + uint64_t cplt_cfg1; + uint64_t cplt_stat0; + uint64_t cplt_mask0; + uint64_t ctrl_protect_mode; + uint64_t ctrl_atomic_lock; +} PnvPervasiveCtrlRegs; + +typedef struct PnvNestChipletPervasive { + DeviceState parent; + MemoryRegion xscom_ctrl_regs_mr; + PnvPervasiveCtrlRegs control_regs; +} PnvNestChipletPervasive; + +#endif /*PPC_PNV_NEST_CHIPLET_PERVASIVE_H */ diff --git a/include/hw/ppc/pnv_occ.h b/include/hw/ppc/pnv_occ.h index 90a81dae2b..df321244e3 100644 --- a/include/hw/ppc/pnv_occ.h +++ b/include/hw/ppc/pnv_occ.h @@ -20,7 +20,8 @@ #ifndef PPC_PNV_OCC_H #define PPC_PNV_OCC_H -#include "qom/object.h" +#include "exec/memory.h" +#include "hw/qdev-core.h" #define TYPE_PNV_OCC "pnv-occ" OBJECT_DECLARE_TYPE(PnvOCC, PnvOCCClass, diff --git a/include/hw/ppc/pnv_pnor.h b/include/hw/ppc/pnv_pnor.h index bab2f79844..2e37ac88bf 100644 --- a/include/hw/ppc/pnv_pnor.h +++ b/include/hw/ppc/pnv_pnor.h @@ -10,7 +10,7 @@ #ifndef PPC_PNV_PNOR_H #define PPC_PNV_PNOR_H -#include "qom/object.h" +#include "hw/sysbus.h" /* * PNOR offset on the LPC FW address space diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h index 8253469b8f..2a6f715350 100644 --- a/include/hw/ppc/pnv_psi.h +++ b/include/hw/ppc/pnv_psi.h @@ -23,7 +23,7 @@ #include "hw/sysbus.h" #include "hw/ppc/xics.h" #include "hw/ppc/xive.h" -#include "qom/object.h" +#include "hw/qdev-core.h" #define TYPE_PNV_PSI "pnv-psi" OBJECT_DECLARE_TYPE(PnvPsi, PnvPsiClass, diff --git a/include/hw/ppc/pnv_sbe.h b/include/hw/ppc/pnv_sbe.h new file mode 100644 index 0000000000..b6b378ad14 --- /dev/null +++ b/include/hw/ppc/pnv_sbe.h @@ -0,0 +1,56 @@ +/* + * QEMU PowerPC PowerNV Emulation of some SBE behaviour + * + * Copyright (c) 2022, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 PPC_PNV_SBE_H +#define PPC_PNV_SBE_H + +#include "exec/memory.h" +#include "hw/qdev-core.h" + +#define TYPE_PNV_SBE "pnv-sbe" +OBJECT_DECLARE_TYPE(PnvSBE, PnvSBEClass, PNV_SBE) +#define TYPE_PNV9_SBE TYPE_PNV_SBE "-POWER9" +DECLARE_INSTANCE_CHECKER(PnvSBE, PNV9_SBE, TYPE_PNV9_SBE) +#define TYPE_PNV10_SBE TYPE_PNV_SBE "-POWER10" +DECLARE_INSTANCE_CHECKER(PnvSBE, PNV10_SBE, TYPE_PNV10_SBE) + +struct PnvSBE { + DeviceState xd; + + uint64_t mbox[8]; + uint64_t sbe_doorbell; + uint64_t host_doorbell; + + qemu_irq psi_irq; + QEMUTimer *timer; + + MemoryRegion xscom_mbox_regs; + MemoryRegion xscom_ctrl_regs; +}; + +struct PnvSBEClass { + DeviceClass parent_class; + + int xscom_ctrl_size; + int xscom_mbox_size; + const MemoryRegionOps *xscom_ctrl_ops; + const MemoryRegionOps *xscom_mbox_ops; +}; + +#endif /* PPC_PNV_SBE_H */ diff --git a/include/hw/ppc/pnv_xive.h b/include/hw/ppc/pnv_xive.h index b5d91505e5..9c48430ee4 100644 --- a/include/hw/ppc/pnv_xive.h +++ b/include/hw/ppc/pnv_xive.h @@ -10,12 +10,11 @@ #ifndef PPC_PNV_XIVE_H #define PPC_PNV_XIVE_H +#include "hw/ppc/pnv.h" #include "hw/ppc/xive.h" #include "qom/object.h" #include "hw/ppc/xive2.h" -struct PnvChip; - #define TYPE_PNV_XIVE "pnv-xive" OBJECT_DECLARE_TYPE(PnvXive, PnvXiveClass, PNV_XIVE) @@ -31,7 +30,7 @@ struct PnvXive { XiveRouter parent_obj; /* Owning chip */ - struct PnvChip *chip; + PnvChip *chip; /* XSCOM addresses giving access to the controller registers */ MemoryRegion xscom_regs; @@ -106,7 +105,7 @@ typedef struct PnvXive2 { Xive2Router parent_obj; /* Owning chip */ - struct PnvChip *chip; + PnvChip *chip; /* XSCOM addresses giving access to the controller registers */ MemoryRegion xscom_regs; diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 7c7440de0c..6209e18492 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -20,7 +20,8 @@ #ifndef PPC_PNV_XSCOM_H #define PPC_PNV_XSCOM_H -#include "qom/object.h" +#include "exec/memory.h" +#include "hw/ppc/pnv.h" typedef struct PnvXScomInterface PnvXScomInterface; @@ -63,6 +64,9 @@ struct PnvXScomInterfaceClass { #define PNV_XSCOM_PSIHB_BASE 0x2010900 #define PNV_XSCOM_PSIHB_SIZE 0x20 +#define PNV_XSCOM_CHIPTOD_BASE 0x0040000 +#define PNV_XSCOM_CHIPTOD_SIZE 0x31 + #define PNV_XSCOM_OCC_BASE 0x0066000 #define PNV_XSCOM_OCC_SIZE 0x6000 @@ -89,9 +93,21 @@ struct PnvXScomInterfaceClass { ((uint64_t)(((core) & 0x1C) + 0x40) << 22) #define PNV9_XSCOM_EQ_SIZE 0x100000 +#define PNV9_XSCOM_I2CM_BASE 0xa0000 +#define PNV9_XSCOM_I2CM_SIZE 0x1000 + +#define PNV9_XSCOM_CHIPTOD_BASE PNV_XSCOM_CHIPTOD_BASE +#define PNV9_XSCOM_CHIPTOD_SIZE PNV_XSCOM_CHIPTOD_SIZE + #define PNV9_XSCOM_OCC_BASE PNV_XSCOM_OCC_BASE #define PNV9_XSCOM_OCC_SIZE 0x8000 +#define PNV9_XSCOM_SBE_CTRL_BASE 0x00050008 +#define PNV9_XSCOM_SBE_CTRL_SIZE 0x1 + +#define PNV9_XSCOM_SBE_MBOX_BASE 0x000D0050 +#define PNV9_XSCOM_SBE_MBOX_SIZE 0x16 + #define PNV9_XSCOM_PBA_BASE 0x5012b00 #define PNV9_XSCOM_PBA_SIZE 0x40 @@ -120,33 +136,65 @@ struct PnvXScomInterfaceClass { #define PNV10_XSCOM_EC(proc) \ ((0x2 << 16) | ((1 << (3 - (proc))) << 12)) +#define PNV10_XSCOM_QME(chiplet) \ + (PNV10_XSCOM_EQ(chiplet) | (0xE << 16)) + +/* + * Make the region larger by 0x1000 (instead of starting at an offset) so the + * modelled addresses start from 0 + */ +#define PNV10_XSCOM_QME_BASE(core) \ + ((uint64_t) PNV10_XSCOM_QME(PNV10_XSCOM_EQ_CHIPLET(core))) +#define PNV10_XSCOM_QME_SIZE (0x8000 + 0x1000) + #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_EQ_SIZE 0x20000 #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_EC_SIZE 0x1000 #define PNV10_XSCOM_PSIHB_BASE 0x3011D00 #define PNV10_XSCOM_PSIHB_SIZE 0x100 +#define PNV10_XSCOM_I2CM_BASE PNV9_XSCOM_I2CM_BASE +#define PNV10_XSCOM_I2CM_SIZE PNV9_XSCOM_I2CM_SIZE + +#define PNV10_XSCOM_CHIPTOD_BASE PNV9_XSCOM_CHIPTOD_BASE +#define PNV10_XSCOM_CHIPTOD_SIZE PNV9_XSCOM_CHIPTOD_SIZE + #define PNV10_XSCOM_OCC_BASE PNV9_XSCOM_OCC_BASE #define PNV10_XSCOM_OCC_SIZE PNV9_XSCOM_OCC_SIZE +#define PNV10_XSCOM_SBE_CTRL_BASE PNV9_XSCOM_SBE_CTRL_BASE +#define PNV10_XSCOM_SBE_CTRL_SIZE PNV9_XSCOM_SBE_CTRL_SIZE + +#define PNV10_XSCOM_SBE_MBOX_BASE PNV9_XSCOM_SBE_MBOX_BASE +#define PNV10_XSCOM_SBE_MBOX_SIZE PNV9_XSCOM_SBE_MBOX_SIZE + #define PNV10_XSCOM_PBA_BASE 0x01010CDA #define PNV10_XSCOM_PBA_SIZE 0x40 #define PNV10_XSCOM_XIVE2_BASE 0x2010800 #define PNV10_XSCOM_XIVE2_SIZE 0x400 +#define PNV10_XSCOM_N1_CHIPLET_CTRL_REGS_BASE 0x3000000 +#define PNV10_XSCOM_CHIPLET_CTRL_REGS_SIZE 0x400 + +#define PNV10_XSCOM_N1_PB_SCOM_EQ_BASE 0x3011000 +#define PNV10_XSCOM_N1_PB_SCOM_EQ_SIZE 0x200 + +#define PNV10_XSCOM_N1_PB_SCOM_ES_BASE 0x3011300 +#define PNV10_XSCOM_N1_PB_SCOM_ES_SIZE 0x100 + #define PNV10_XSCOM_PEC_NEST_BASE 0x3011800 /* index goes downwards ... */ #define PNV10_XSCOM_PEC_NEST_SIZE 0x100 #define PNV10_XSCOM_PEC_PCI_BASE 0x8010800 /* index goes upwards ... */ #define PNV10_XSCOM_PEC_PCI_SIZE 0x200 -void pnv_xscom_realize(PnvChip *chip, uint64_t size, Error **errp); +void pnv_xscom_init(PnvChip *chip, uint64_t size, hwaddr addr); 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); diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h index 02af03ada2..d5d119ea7f 100644 --- a/include/hw/ppc/ppc.h +++ b/include/hw/ppc/ppc.h @@ -1,11 +1,12 @@ #ifndef HW_PPC_H #define HW_PPC_H -#include "target/ppc/cpu-qom.h" +#include "target/ppc/cpu.h" 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); +int ppc_cpu_tir(PowerPCCPU *cpu); /* PowerPC hardware exceptions management helpers */ typedef void (*clk_setup_cb)(void *opaque, uint32_t freq); @@ -53,7 +54,8 @@ struct ppc_tb_t { */ uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset); -clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq); +void cpu_ppc_tb_init(CPUPPCState *env, uint32_t freq); +void cpu_ppc_tb_reset(CPUPPCState *env); void cpu_ppc_tb_free(CPUPPCState *env); void cpu_ppc_hdecr_init(CPUPPCState *env); void cpu_ppc_hdecr_exit(CPUPPCState *env); diff --git a/include/hw/ppc/ppc4xx.h b/include/hw/ppc/ppc4xx.h index 980f964b5a..1bd9b8821b 100644 --- a/include/hw/ppc/ppc4xx.h +++ b/include/hw/ppc/ppc4xx.h @@ -27,26 +27,127 @@ #include "hw/ppc/ppc.h" #include "exec/memory.h" +#include "hw/sysbus.h" -/* PowerPC 4xx core initialization */ -PowerPCCPU *ppc4xx_init(const char *cpu_model, - clk_setup_t *cpu_clk, clk_setup_t *tb_clk, - uint32_t sysclk); +/* + * Generic DCR device + */ +#define TYPE_PPC4xx_DCR_DEVICE "ppc4xx-dcr-device" +OBJECT_DECLARE_SIMPLE_TYPE(Ppc4xxDcrDeviceState, PPC4xx_DCR_DEVICE); +struct Ppc4xxDcrDeviceState { + SysBusDevice parent_obj; -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[]); + PowerPCCPU *cpu; +}; -void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks, - MemoryRegion ram_memories[], - hwaddr *ram_bases, - hwaddr *ram_sizes, - int do_init); +void ppc4xx_dcr_register(Ppc4xxDcrDeviceState *dev, int dcrn, void *opaque, + dcr_read_cb dcr_read, dcr_write_cb dcr_write); +bool ppc4xx_dcr_realize(Ppc4xxDcrDeviceState *dev, PowerPCCPU *cpu, + Error **errp); -void ppc4xx_mal_init(CPUPPCState *env, uint8_t txcnum, uint8_t rxcnum, - qemu_irq irqs[4]); +/* Memory Access Layer (MAL) */ +#define TYPE_PPC4xx_MAL "ppc4xx-mal" +OBJECT_DECLARE_SIMPLE_TYPE(Ppc4xxMalState, PPC4xx_MAL); +struct Ppc4xxMalState { + Ppc4xxDcrDeviceState parent_obj; -#define TYPE_PPC4xx_PCI_HOST_BRIDGE "ppc4xx-pcihost" + qemu_irq irqs[4]; + uint32_t cfg; + uint32_t esr; + uint32_t ier; + uint32_t txcasr; + uint32_t txcarr; + uint32_t txeobisr; + uint32_t txdeir; + uint32_t rxcasr; + uint32_t rxcarr; + uint32_t rxeobisr; + uint32_t rxdeir; + uint32_t *txctpr; + uint32_t *rxctpr; + uint32_t *rcbs; + uint8_t txcnum; + uint8_t rxcnum; +}; + +/* Peripheral local bus arbitrer */ +#define TYPE_PPC4xx_PLB "ppc4xx-plb" +OBJECT_DECLARE_SIMPLE_TYPE(Ppc4xxPlbState, PPC4xx_PLB); +struct Ppc4xxPlbState { + Ppc4xxDcrDeviceState parent_obj; + + uint32_t acr; + uint32_t bear; + uint32_t besr; +}; + +/* Peripheral controller */ +#define TYPE_PPC4xx_EBC "ppc4xx-ebc" +OBJECT_DECLARE_SIMPLE_TYPE(Ppc4xxEbcState, PPC4xx_EBC); +struct Ppc4xxEbcState { + Ppc4xxDcrDeviceState parent_obj; + + uint32_t addr; + uint32_t bcr[8]; + uint32_t bap[8]; + uint32_t bear; + uint32_t besr0; + uint32_t besr1; + uint32_t cfg; +}; + +/* SDRAM DDR controller */ +typedef struct { + MemoryRegion ram; + MemoryRegion container; /* used for clipping */ + hwaddr base; + hwaddr size; + uint32_t bcr; +} Ppc4xxSdramBank; + +#define SDR0_DDR0_DDRM_ENCODE(n) ((((unsigned long)(n)) & 0x03) << 29) +#define SDR0_DDR0_DDRM_DDR1 0x20000000 +#define SDR0_DDR0_DDRM_DDR2 0x40000000 + +#define TYPE_PPC4xx_SDRAM_DDR "ppc4xx-sdram-ddr" +OBJECT_DECLARE_SIMPLE_TYPE(Ppc4xxSdramDdrState, PPC4xx_SDRAM_DDR); +struct Ppc4xxSdramDdrState { + Ppc4xxDcrDeviceState parent_obj; + + MemoryRegion *dram_mr; + uint32_t nbanks; /* Banks to use from 4, e.g. when board has less slots */ + Ppc4xxSdramBank bank[4]; + qemu_irq irq; + + uint32_t addr; + uint32_t besr0; + uint32_t besr1; + uint32_t bear; + uint32_t cfg; + uint32_t status; + uint32_t rtr; + uint32_t pmit; + uint32_t tr; + uint32_t ecccfg; + uint32_t eccesr; +}; + +void ppc4xx_sdram_ddr_enable(Ppc4xxSdramDdrState *s); + +/* SDRAM DDR2 controller */ +#define TYPE_PPC4xx_SDRAM_DDR2 "ppc4xx-sdram-ddr2" +OBJECT_DECLARE_SIMPLE_TYPE(Ppc4xxSdramDdr2State, PPC4xx_SDRAM_DDR2); +struct Ppc4xxSdramDdr2State { + Ppc4xxDcrDeviceState parent_obj; + + MemoryRegion *dram_mr; + uint32_t nbanks; /* Banks to use from 4, e.g. when board has less slots */ + Ppc4xxSdramBank bank[4]; + + uint32_t addr; + uint32_t mcopt2; +}; + +void ppc4xx_sdram_ddr2_enable(Ppc4xxSdramDdr2State *s); #endif /* PPC4XX_H */ diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 072dda2c72..4aaf23d28f 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -12,7 +12,7 @@ #include "hw/ppc/spapr_xive.h" /* For SpaprXive */ #include "hw/ppc/xics.h" /* For ICSState */ #include "hw/ppc/spapr_tpm_proxy.h" -#include "hw/ppc/vof.h" +#include "hw/ppc/spapr_nested.h" /* For SpaprMachineStateNested */ struct SpaprVioBus; struct SpaprPhbState; @@ -22,6 +22,8 @@ typedef struct SpaprEventLogEntry SpaprEventLogEntry; typedef struct SpaprEventSource SpaprEventSource; typedef struct SpaprPendingHpt SpaprPendingHpt; +typedef struct Vof Vof; + #define HPTE64_V_HPTE_DIRTY 0x0000000000000040ULL #define SPAPR_ENTRY_POINT 0x100 @@ -77,8 +79,12 @@ typedef enum { #define SPAPR_CAP_FWNMI 0x0A /* Support H_RPT_INVALIDATE */ #define SPAPR_CAP_RPT_INVALIDATE 0x0B +/* Support for AIL modes */ +#define SPAPR_CAP_AIL_MODE_3 0x0C +/* Nested PAPR */ +#define SPAPR_CAP_NESTED_PAPR 0x0D /* Num Caps */ -#define SPAPR_CAP_NUM (SPAPR_CAP_RPT_INVALIDATE + 1) +#define SPAPR_CAP_NUM (SPAPR_CAP_NESTED_PAPR + 1) /* * Capability Values @@ -100,11 +106,8 @@ typedef enum { #define FDT_MAX_SIZE 0x200000 -/* Max number of GPUs per system */ -#define NVGPU_MAX_NUM 6 - /* Max number of NUMA nodes */ -#define NUMA_NODES_MAX_NUM (MAX_NODES + NVGPU_MAX_NUM) +#define NUMA_NODES_MAX_NUM (MAX_NODES) /* * NUMA FORM1 macros. FORM1_DIST_REF_POINTS was taken from @@ -157,13 +160,27 @@ struct SpaprMachineClass { bool (*phb_placement)(SpaprMachineState *spapr, uint32_t index, uint64_t *buid, hwaddr *pio, hwaddr *mmio32, hwaddr *mmio64, - unsigned n_dma, uint32_t *liobns, hwaddr *nv2gpa, - hwaddr *nv2atsd, Error **errp); + unsigned n_dma, uint32_t *liobns, Error **errp); SpaprResizeHpt resize_hpt_default; SpaprCapabilities default_caps; SpaprIrq *irq; }; +#define WDT_MAX_WATCHDOGS 4 /* Maximum number of watchdog devices */ + +#define TYPE_SPAPR_WDT "spapr-wdt" +OBJECT_DECLARE_SIMPLE_TYPE(SpaprWatchdog, SPAPR_WDT) + +typedef struct SpaprWatchdog { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + QEMUTimer timer; + uint8_t action; /* One of PSERIES_WDTF_ACTION_xxx */ + uint8_t leave_others; /* leaveOtherWatchdogsRunningOnTimeout */ +} SpaprWatchdog; + /** * SpaprMachineState: */ @@ -179,13 +196,14 @@ struct SpaprMachineState { SpaprResizeHpt resize_hpt; void *htab; uint32_t htab_shift; - uint64_t patb_entry; /* Process tbl registed in H_REGISTER_PROC_TBL */ + uint64_t patb_entry; /* Process tbl registered in H_REGISTER_PROC_TBL */ SpaprPendingHpt *pending_hpt; /* in-progress resize */ hwaddr rma_size; uint32_t fdt_size; uint32_t fdt_initial_size; void *fdt_blob; + uint8_t fdt_rng_seed[32]; long kernel_size; bool kernel_le; uint64_t kernel_addr; @@ -198,7 +216,7 @@ struct SpaprMachineState { uint32_t vsmt; /* Virtual SMT mode (KVM's "core stride") */ /* Nested HV support (TCG only) */ - uint64_t nested_ptcr; + SpaprMachineStateNested nested; Notifier epow_notifier; QTAILQ_HEAD(, SpaprEventLogEntry) pending_events; @@ -257,13 +275,14 @@ struct SpaprMachineState { bool cmd_line_caps[SPAPR_CAP_NUM]; SpaprCapabilities def, eff, mig; - unsigned gpu_numa_id; SpaprTpmProxy *tpm_proxy; uint32_t FORM1_assoc_array[NUMA_NODES_MAX_NUM][FORM1_NUMA_ASSOC_SIZE]; uint32_t FORM2_assoc_array[NUMA_NODES_MAX_NUM][FORM2_NUMA_ASSOC_SIZE]; Error *fwnmi_migration_blocker; + + SpaprWatchdog wds[WDT_MAX_WATCHDOGS]; }; #define H_SUCCESS 0 @@ -344,8 +363,12 @@ struct SpaprMachineState { #define H_P7 -60 #define H_P8 -61 #define H_P9 -62 +#define H_NOOP -63 #define H_UNSUPPORTED -67 #define H_OVERLAP -68 +#define H_STATE -75 +#define H_IN_USE -77 +#define H_INVALID_ELEMENT_VALUE -81 #define H_UNSUPPORTED_FLAG -256 #define H_MULTI_THREADS_ACTIVE -9005 @@ -564,8 +587,17 @@ struct SpaprMachineState { #define H_SCM_HEALTH 0x400 #define H_RPT_INVALIDATE 0x448 #define H_SCM_FLUSH 0x44C +#define H_WATCHDOG 0x45C +#define H_GUEST_GET_CAPABILITIES 0x460 +#define H_GUEST_SET_CAPABILITIES 0x464 +#define H_GUEST_CREATE 0x470 +#define H_GUEST_CREATE_VCPU 0x474 +#define H_GUEST_GET_STATE 0x478 +#define H_GUEST_SET_STATE 0x47C +#define H_GUEST_RUN_VCPU 0x480 +#define H_GUEST_DELETE 0x488 -#define MAX_HCALL_OPCODE H_SCM_FLUSH +#define MAX_HCALL_OPCODE H_GUEST_DELETE /* The hcalls above are standardized in PAPR and implemented by pHyp * as well. @@ -599,66 +631,6 @@ struct SpaprMachineState { #define SVM_H_TPM_COMM 0xEF10 #define SVM_HCALL_MAX SVM_H_TPM_COMM -/* - * Register state for entering a nested guest with H_ENTER_NESTED. - * New member must be added at the end. - */ -struct kvmppc_hv_guest_state { - uint64_t version; /* version of this structure layout, must be first */ - uint32_t lpid; - uint32_t vcpu_token; - /* These registers are hypervisor privileged (at least for writing) */ - uint64_t lpcr; - uint64_t pcr; - uint64_t amor; - uint64_t dpdes; - uint64_t hfscr; - int64_t tb_offset; - uint64_t dawr0; - uint64_t dawrx0; - uint64_t ciabr; - uint64_t hdec_expiry; - uint64_t purr; - uint64_t spurr; - uint64_t ic; - uint64_t vtb; - uint64_t hdar; - uint64_t hdsisr; - uint64_t heir; - uint64_t asdr; - /* These are OS privileged but need to be set late in guest entry */ - uint64_t srr0; - uint64_t srr1; - uint64_t sprg[4]; - uint64_t pidr; - uint64_t cfar; - uint64_t ppr; - /* Version 1 ends here */ - uint64_t dawr1; - uint64_t dawrx1; - /* Version 2 ends here */ -}; - -/* Latest version of hv_guest_state structure */ -#define HV_GUEST_STATE_VERSION 2 - -/* Linux 64-bit powerpc pt_regs struct, used by nested HV */ -struct kvmppc_pt_regs { - uint64_t gpr[32]; - uint64_t nip; - uint64_t msr; - uint64_t orig_gpr3; /* Used for restarting system calls */ - uint64_t ctr; - uint64_t link; - uint64_t xer; - uint64_t ccr; - uint64_t softe; /* Soft enabled/disabled */ - uint64_t trap; /* Reason for being here */ - uint64_t dar; /* Fault registers */ - uint64_t dsisr; /* on 4xx/Book-E used for ESR */ - uint64_t result; /* Result of a system call */ -}; - typedef struct SpaprDeviceTreeUpdateHeader { uint32_t version_id; } SpaprDeviceTreeUpdateHeader; @@ -673,15 +645,17 @@ typedef target_ulong (*spapr_hcall_fn)(PowerPCCPU *cpu, SpaprMachineState *sm, target_ulong *args); void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn); +void spapr_unregister_hypercall(target_ulong opcode); target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, target_ulong *args); -void spapr_exit_nested(PowerPCCPU *cpu, int excp); - -target_ulong softmmu_resize_hpt_prepare(PowerPCCPU *cpu, SpaprMachineState *spapr, +target_ulong vhyp_mmu_resize_hpt_prepare(PowerPCCPU *cpu, + SpaprMachineState *spapr, target_ulong shift); -target_ulong softmmu_resize_hpt_commit(PowerPCCPU *cpu, SpaprMachineState *spapr, - target_ulong flags, target_ulong shift); +target_ulong vhyp_mmu_resize_hpt_commit(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong flags, + target_ulong shift); bool is_ram_address(SpaprMachineState *spapr, hwaddr addr); void push_sregs_to_kvm_pr(SpaprMachineState *spapr); @@ -829,7 +803,8 @@ static inline uint64_t ppc64_phys_to_real(uint64_t addr) static inline uint32_t rtas_ld(target_ulong phys, int n) { - return ldl_be_phys(&address_space_memory, ppc64_phys_to_real(phys + 4*n)); + return ldl_be_phys(&address_space_memory, + ppc64_phys_to_real(phys + 4 * n)); } static inline uint64_t rtas_ldq(target_ulong phys, int n) @@ -839,7 +814,7 @@ static inline uint64_t rtas_ldq(target_ulong phys, int n) static inline void rtas_st(target_ulong phys, int n, uint32_t val) { - stl_be_phys(&address_space_memory, ppc64_phys_to_real(phys + 4*n), val); + stl_be_phys(&address_space_memory, ppc64_phys_to_real(phys + 4 * n), val); } typedef void (*spapr_rtas_fn)(PowerPCCPU *cpu, SpaprMachineState *sm, @@ -902,6 +877,7 @@ struct SpaprTceTable { bool bypass; bool need_vfio; bool skipping_replay; + bool def_win; int fd; MemoryRegion root; IOMMUMemoryRegion iommu; @@ -1023,10 +999,12 @@ extern const VMStateDescription vmstate_spapr_cap_sbbc; extern const VMStateDescription vmstate_spapr_cap_ibs; extern const VMStateDescription vmstate_spapr_cap_hpt_maxpagesize; extern const VMStateDescription vmstate_spapr_cap_nested_kvm_hv; +extern const VMStateDescription vmstate_spapr_cap_nested_papr; extern const VMStateDescription vmstate_spapr_cap_large_decr; extern const VMStateDescription vmstate_spapr_cap_ccf_assist; extern const VMStateDescription vmstate_spapr_cap_fwnmi; extern const VMStateDescription vmstate_spapr_cap_rpt_invalidate; +extern const VMStateDescription vmstate_spapr_wdt; static inline uint8_t spapr_get_cap(SpaprMachineState *spapr, int cap) { @@ -1049,6 +1027,7 @@ bool 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); +void spapr_init_all_lpcrs(target_ulong value, target_ulong mask); hwaddr spapr_get_rtas_addr(void); bool spapr_memory_hot_unplug_supported(SpaprMachineState *spapr); @@ -1063,4 +1042,12 @@ target_ulong spapr_vof_client_architecture_support(MachineState *ms, target_ulong ovec_addr); void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt); +/* H_WATCHDOG */ +void spapr_watchdog_init(SpaprMachineState *spapr); +void spapr_register_nested_hv(void); +void spapr_unregister_nested_hv(void); +void spapr_nested_reset(SpaprMachineState *spapr); +void spapr_register_nested_papr(void); +void spapr_unregister_nested_papr(void); + #endif /* HW_SPAPR_H */ diff --git a/include/hw/ppc/spapr_cpu_core.h b/include/hw/ppc/spapr_cpu_core.h index b560514560..69a52e39b8 100644 --- a/include/hw/ppc/spapr_cpu_core.h +++ b/include/hw/ppc/spapr_cpu_core.h @@ -41,6 +41,8 @@ void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r1, target_ulong r3, target_ulong r4); +struct nested_ppc_state; + typedef struct SpaprCpuState { uint64_t vpa_addr; uint64_t slb_shadow_addr, slb_shadow_size; @@ -51,8 +53,7 @@ typedef struct SpaprCpuState { /* Fields for nested-HV support */ bool in_nested; /* true while the L2 is executing */ - CPUPPCState *nested_host_state; /* holds the L1 state while L2 executes */ - int64_t nested_tb_offset; /* L1->L2 TB offset */ + struct nested_ppc_state *nested_host_state; /* holds the L1 state while L2 executes */ } SpaprCpuState; static inline SpaprCpuState *spapr_cpu_state(PowerPCCPU *cpu) diff --git a/include/hw/ppc/spapr_irq.h b/include/hw/ppc/spapr_irq.h index c22a72c9e2..4fd2d5853d 100644 --- a/include/hw/ppc/spapr_irq.h +++ b/include/hw/ppc/spapr_irq.h @@ -14,9 +14,21 @@ #include "qom/object.h" /* - * IRQ range offsets per device type + * The XIVE IRQ backend uses the same layout as the XICS backend but + * covers the full range of the IRQ number space. The IRQ numbers for + * the CPU IPIs are allocated at the bottom of this space, below 4K, + * to preserve compatibility with XICS which does not use that range. + */ + +/* + * CPU IPI range (XIVE only) */ #define SPAPR_IRQ_IPI 0x0 +#define SPAPR_IRQ_NR_IPIS 0x1000 + +/* + * IRQ range offsets per device type + */ #define SPAPR_XIRQ_BASE XICS_IRQ_BASE /* 0x1000 */ #define SPAPR_IRQ_EPOW (SPAPR_XIRQ_BASE + 0x0000) diff --git a/include/hw/ppc/spapr_nested.h b/include/hw/ppc/spapr_nested.h new file mode 100644 index 0000000000..93ef14adcc --- /dev/null +++ b/include/hw/ppc/spapr_nested.h @@ -0,0 +1,524 @@ +#ifndef HW_SPAPR_NESTED_H +#define HW_SPAPR_NESTED_H + +#include "target/ppc/cpu.h" + +/* Guest State Buffer Element IDs */ +#define GSB_HV_VCPU_IGNORED_ID 0x0000 /* An element whose value is ignored */ +#define GSB_HV_VCPU_STATE_SIZE 0x0001 /* HV internal format VCPU state size */ +#define GSB_VCPU_OUT_BUF_MIN_SZ 0x0002 /* Min size of the Run VCPU o/p buffer */ +#define GSB_VCPU_LPVR 0x0003 /* Logical PVR */ +#define GSB_TB_OFFSET 0x0004 /* Timebase Offset */ +#define GSB_PART_SCOPED_PAGETBL 0x0005 /* Partition Scoped Page Table */ +#define GSB_PROCESS_TBL 0x0006 /* Process Table */ + /* RESERVED 0x0007 - 0x0BFF */ +#define GSB_VCPU_IN_BUFFER 0x0C00 /* Run VCPU Input Buffer */ +#define GSB_VCPU_OUT_BUFFER 0x0C01 /* Run VCPU Out Buffer */ +#define GSB_VCPU_VPA 0x0C02 /* HRA to Guest VCPU VPA */ + /* RESERVED 0x0C03 - 0x0FFF */ +#define GSB_VCPU_GPR0 0x1000 +#define GSB_VCPU_GPR1 0x1001 +#define GSB_VCPU_GPR2 0x1002 +#define GSB_VCPU_GPR3 0x1003 +#define GSB_VCPU_GPR4 0x1004 +#define GSB_VCPU_GPR5 0x1005 +#define GSB_VCPU_GPR6 0x1006 +#define GSB_VCPU_GPR7 0x1007 +#define GSB_VCPU_GPR8 0x1008 +#define GSB_VCPU_GPR9 0x1009 +#define GSB_VCPU_GPR10 0x100A +#define GSB_VCPU_GPR11 0x100B +#define GSB_VCPU_GPR12 0x100C +#define GSB_VCPU_GPR13 0x100D +#define GSB_VCPU_GPR14 0x100E +#define GSB_VCPU_GPR15 0x100F +#define GSB_VCPU_GPR16 0x1010 +#define GSB_VCPU_GPR17 0x1011 +#define GSB_VCPU_GPR18 0x1012 +#define GSB_VCPU_GPR19 0x1013 +#define GSB_VCPU_GPR20 0x1014 +#define GSB_VCPU_GPR21 0x1015 +#define GSB_VCPU_GPR22 0x1016 +#define GSB_VCPU_GPR23 0x1017 +#define GSB_VCPU_GPR24 0x1018 +#define GSB_VCPU_GPR25 0x1019 +#define GSB_VCPU_GPR26 0x101A +#define GSB_VCPU_GPR27 0x101B +#define GSB_VCPU_GPR28 0x101C +#define GSB_VCPU_GPR29 0x101D +#define GSB_VCPU_GPR30 0x101E +#define GSB_VCPU_GPR31 0x101F +#define GSB_VCPU_HDEC_EXPIRY_TB 0x1020 +#define GSB_VCPU_SPR_NIA 0x1021 +#define GSB_VCPU_SPR_MSR 0x1022 +#define GSB_VCPU_SPR_LR 0x1023 +#define GSB_VCPU_SPR_XER 0x1024 +#define GSB_VCPU_SPR_CTR 0x1025 +#define GSB_VCPU_SPR_CFAR 0x1026 +#define GSB_VCPU_SPR_SRR0 0x1027 +#define GSB_VCPU_SPR_SRR1 0x1028 +#define GSB_VCPU_SPR_DAR 0x1029 +#define GSB_VCPU_DEC_EXPIRE_TB 0x102A +#define GSB_VCPU_SPR_VTB 0x102B +#define GSB_VCPU_SPR_LPCR 0x102C +#define GSB_VCPU_SPR_HFSCR 0x102D +#define GSB_VCPU_SPR_FSCR 0x102E +#define GSB_VCPU_SPR_FPSCR 0x102F +#define GSB_VCPU_SPR_DAWR0 0x1030 +#define GSB_VCPU_SPR_DAWR1 0x1031 +#define GSB_VCPU_SPR_CIABR 0x1032 +#define GSB_VCPU_SPR_PURR 0x1033 +#define GSB_VCPU_SPR_SPURR 0x1034 +#define GSB_VCPU_SPR_IC 0x1035 +#define GSB_VCPU_SPR_SPRG0 0x1036 +#define GSB_VCPU_SPR_SPRG1 0x1037 +#define GSB_VCPU_SPR_SPRG2 0x1038 +#define GSB_VCPU_SPR_SPRG3 0x1039 +#define GSB_VCPU_SPR_PPR 0x103A +#define GSB_VCPU_SPR_MMCR0 0x103B +#define GSB_VCPU_SPR_MMCR1 0x103C +#define GSB_VCPU_SPR_MMCR2 0x103D +#define GSB_VCPU_SPR_MMCR3 0x103E +#define GSB_VCPU_SPR_MMCRA 0x103F +#define GSB_VCPU_SPR_SIER 0x1040 +#define GSB_VCPU_SPR_SIER2 0x1041 +#define GSB_VCPU_SPR_SIER3 0x1042 +#define GSB_VCPU_SPR_BESCR 0x1043 +#define GSB_VCPU_SPR_EBBHR 0x1044 +#define GSB_VCPU_SPR_EBBRR 0x1045 +#define GSB_VCPU_SPR_AMR 0x1046 +#define GSB_VCPU_SPR_IAMR 0x1047 +#define GSB_VCPU_SPR_AMOR 0x1048 +#define GSB_VCPU_SPR_UAMOR 0x1049 +#define GSB_VCPU_SPR_SDAR 0x104A +#define GSB_VCPU_SPR_SIAR 0x104B +#define GSB_VCPU_SPR_DSCR 0x104C +#define GSB_VCPU_SPR_TAR 0x104D +#define GSB_VCPU_SPR_DEXCR 0x104E +#define GSB_VCPU_SPR_HDEXCR 0x104F +#define GSB_VCPU_SPR_HASHKEYR 0x1050 +#define GSB_VCPU_SPR_HASHPKEYR 0x1051 +#define GSB_VCPU_SPR_CTRL 0x1052 + /* RESERVED 0x1053 - 0x1FFF */ +#define GSB_VCPU_SPR_CR 0x2000 +#define GSB_VCPU_SPR_PIDR 0x2001 +#define GSB_VCPU_SPR_DSISR 0x2002 +#define GSB_VCPU_SPR_VSCR 0x2003 +#define GSB_VCPU_SPR_VRSAVE 0x2004 +#define GSB_VCPU_SPR_DAWRX0 0x2005 +#define GSB_VCPU_SPR_DAWRX1 0x2006 +#define GSB_VCPU_SPR_PMC1 0x2007 +#define GSB_VCPU_SPR_PMC2 0x2008 +#define GSB_VCPU_SPR_PMC3 0x2009 +#define GSB_VCPU_SPR_PMC4 0x200A +#define GSB_VCPU_SPR_PMC5 0x200B +#define GSB_VCPU_SPR_PMC6 0x200C +#define GSB_VCPU_SPR_WORT 0x200D +#define GSB_VCPU_SPR_PSPB 0x200E + /* RESERVED 0x200F - 0x2FFF */ +#define GSB_VCPU_SPR_VSR0 0x3000 +#define GSB_VCPU_SPR_VSR1 0x3001 +#define GSB_VCPU_SPR_VSR2 0x3002 +#define GSB_VCPU_SPR_VSR3 0x3003 +#define GSB_VCPU_SPR_VSR4 0x3004 +#define GSB_VCPU_SPR_VSR5 0x3005 +#define GSB_VCPU_SPR_VSR6 0x3006 +#define GSB_VCPU_SPR_VSR7 0x3007 +#define GSB_VCPU_SPR_VSR8 0x3008 +#define GSB_VCPU_SPR_VSR9 0x3009 +#define GSB_VCPU_SPR_VSR10 0x300A +#define GSB_VCPU_SPR_VSR11 0x300B +#define GSB_VCPU_SPR_VSR12 0x300C +#define GSB_VCPU_SPR_VSR13 0x300D +#define GSB_VCPU_SPR_VSR14 0x300E +#define GSB_VCPU_SPR_VSR15 0x300F +#define GSB_VCPU_SPR_VSR16 0x3010 +#define GSB_VCPU_SPR_VSR17 0x3011 +#define GSB_VCPU_SPR_VSR18 0x3012 +#define GSB_VCPU_SPR_VSR19 0x3013 +#define GSB_VCPU_SPR_VSR20 0x3014 +#define GSB_VCPU_SPR_VSR21 0x3015 +#define GSB_VCPU_SPR_VSR22 0x3016 +#define GSB_VCPU_SPR_VSR23 0x3017 +#define GSB_VCPU_SPR_VSR24 0x3018 +#define GSB_VCPU_SPR_VSR25 0x3019 +#define GSB_VCPU_SPR_VSR26 0x301A +#define GSB_VCPU_SPR_VSR27 0x301B +#define GSB_VCPU_SPR_VSR28 0x301C +#define GSB_VCPU_SPR_VSR29 0x301D +#define GSB_VCPU_SPR_VSR30 0x301E +#define GSB_VCPU_SPR_VSR31 0x301F +#define GSB_VCPU_SPR_VSR32 0x3020 +#define GSB_VCPU_SPR_VSR33 0x3021 +#define GSB_VCPU_SPR_VSR34 0x3022 +#define GSB_VCPU_SPR_VSR35 0x3023 +#define GSB_VCPU_SPR_VSR36 0x3024 +#define GSB_VCPU_SPR_VSR37 0x3025 +#define GSB_VCPU_SPR_VSR38 0x3026 +#define GSB_VCPU_SPR_VSR39 0x3027 +#define GSB_VCPU_SPR_VSR40 0x3028 +#define GSB_VCPU_SPR_VSR41 0x3029 +#define GSB_VCPU_SPR_VSR42 0x302A +#define GSB_VCPU_SPR_VSR43 0x302B +#define GSB_VCPU_SPR_VSR44 0x302C +#define GSB_VCPU_SPR_VSR45 0x302D +#define GSB_VCPU_SPR_VSR46 0x302E +#define GSB_VCPU_SPR_VSR47 0x302F +#define GSB_VCPU_SPR_VSR48 0x3030 +#define GSB_VCPU_SPR_VSR49 0x3031 +#define GSB_VCPU_SPR_VSR50 0x3032 +#define GSB_VCPU_SPR_VSR51 0x3033 +#define GSB_VCPU_SPR_VSR52 0x3034 +#define GSB_VCPU_SPR_VSR53 0x3035 +#define GSB_VCPU_SPR_VSR54 0x3036 +#define GSB_VCPU_SPR_VSR55 0x3037 +#define GSB_VCPU_SPR_VSR56 0x3038 +#define GSB_VCPU_SPR_VSR57 0x3039 +#define GSB_VCPU_SPR_VSR58 0x303A +#define GSB_VCPU_SPR_VSR59 0x303B +#define GSB_VCPU_SPR_VSR60 0x303C +#define GSB_VCPU_SPR_VSR61 0x303D +#define GSB_VCPU_SPR_VSR62 0x303E +#define GSB_VCPU_SPR_VSR63 0x303F + /* RESERVED 0x3040 - 0xEFFF */ +#define GSB_VCPU_SPR_HDAR 0xF000 +#define GSB_VCPU_SPR_HDSISR 0xF001 +#define GSB_VCPU_SPR_HEIR 0xF002 +#define GSB_VCPU_SPR_ASDR 0xF003 +/* End of list of Guest State Buffer Element IDs */ +#define GSB_LAST GSB_VCPU_SPR_ASDR + +typedef struct SpaprMachineStateNested { + uint64_t ptcr; + uint8_t api; +#define NESTED_API_KVM_HV 1 +#define NESTED_API_PAPR 2 + bool capabilities_set; + uint32_t pvr_base; + GHashTable *guests; +} SpaprMachineStateNested; + +typedef struct SpaprMachineStateNestedGuest { + uint32_t pvr_logical; + unsigned long nr_vcpus; + uint64_t parttbl[2]; + uint64_t tb_offset; + struct SpaprMachineStateNestedGuestVcpu *vcpus; +} SpaprMachineStateNestedGuest; + +/* Nested PAPR API related macros */ +#define H_GUEST_CAPABILITIES_COPY_MEM 0x8000000000000000 +#define H_GUEST_CAPABILITIES_P9_MODE 0x4000000000000000 +#define H_GUEST_CAPABILITIES_P10_MODE 0x2000000000000000 +#define H_GUEST_CAP_VALID_MASK (H_GUEST_CAPABILITIES_P10_MODE | \ + H_GUEST_CAPABILITIES_P9_MODE) +#define H_GUEST_CAP_COPY_MEM_BMAP 0 +#define H_GUEST_CAP_P9_MODE_BMAP 1 +#define H_GUEST_CAP_P10_MODE_BMAP 2 +#define PAPR_NESTED_GUEST_MAX 4096 +#define H_GUEST_DELETE_ALL_FLAG 0x8000000000000000ULL +#define PAPR_NESTED_GUEST_VCPU_MAX 2048 +#define VCPU_OUT_BUF_MIN_SZ 0x80ULL +#define HVMASK_DEFAULT 0xffffffffffffffff +#define HVMASK_LPCR 0x0070000003820800 +#define HVMASK_MSR 0xEBFFFFFFFFBFEFFF +#define HVMASK_HDEXCR 0x00000000FFFFFFFF +#define HVMASK_TB_OFFSET 0x000000FFFFFFFFFF +#define GSB_MAX_BUF_SIZE (1024 * 1024) +#define H_GUEST_GETSET_STATE_FLAG_GUEST_WIDE 0x8000000000000000 +#define GUEST_STATE_REQUEST_GUEST_WIDE 0x1 +#define GUEST_STATE_REQUEST_SET 0x2 + +/* + * As per ISA v3.1B, following bits are reserved: + * 0:2 + * 4:57 (ISA mentions bit 58 as well but it should be used for P10) + * 61:63 (hence, haven't included PCR bits for v2.06 and v2.05 + * in LOW BITS) + */ +#define PCR_LOW_BITS (PCR_COMPAT_3_10 | PCR_COMPAT_3_00) +#define HVMASK_PCR (~PCR_LOW_BITS) + +#define GUEST_STATE_ELEMENT(i, sz, s, f, ptr, c) { \ + .id = (i), \ + .size = (sz), \ + .location = ptr, \ + .offset = offsetof(struct s, f), \ + .copy = (c) \ +} + +#define GSBE_NESTED(i, sz, f, c) { \ + .id = (i), \ + .size = (sz), \ + .location = get_guest_ptr, \ + .offset = offsetof(struct SpaprMachineStateNestedGuest, f),\ + .copy = (c), \ + .mask = HVMASK_DEFAULT \ +} + +#define GSBE_NESTED_MSK(i, sz, f, c, m) { \ + .id = (i), \ + .size = (sz), \ + .location = get_guest_ptr, \ + .offset = offsetof(struct SpaprMachineStateNestedGuest, f),\ + .copy = (c), \ + .mask = (m) \ +} + +#define GSBE_NESTED_VCPU(i, sz, f, c) { \ + .id = (i), \ + .size = (sz), \ + .location = get_vcpu_ptr, \ + .offset = offsetof(struct SpaprMachineStateNestedGuestVcpu, f),\ + .copy = (c), \ + .mask = HVMASK_DEFAULT \ +} + +#define GUEST_STATE_ELEMENT_NOP(i, sz) { \ + .id = (i), \ + .size = (sz), \ + .location = NULL, \ + .offset = 0, \ + .copy = NULL, \ + .mask = HVMASK_DEFAULT \ +} + +#define GUEST_STATE_ELEMENT_NOP_DW(i) \ + GUEST_STATE_ELEMENT_NOP(i, 8) +#define GUEST_STATE_ELEMENT_NOP_W(i) \ + GUEST_STATE_ELEMENT_NOP(i, 4) + +#define GUEST_STATE_ELEMENT_BASE(i, s, c) { \ + .id = (i), \ + .size = (s), \ + .location = get_vcpu_state_ptr, \ + .offset = 0, \ + .copy = (c), \ + .mask = HVMASK_DEFAULT \ + } + +#define GUEST_STATE_ELEMENT_OFF(i, s, f, c) { \ + .id = (i), \ + .size = (s), \ + .location = get_vcpu_state_ptr, \ + .offset = offsetof(struct nested_ppc_state, f), \ + .copy = (c), \ + .mask = HVMASK_DEFAULT \ + } + +#define GUEST_STATE_ELEMENT_MSK(i, s, f, c, m) { \ + .id = (i), \ + .size = (s), \ + .location = get_vcpu_state_ptr, \ + .offset = offsetof(struct nested_ppc_state, f), \ + .copy = (c), \ + .mask = (m) \ + } + +#define GUEST_STATE_ELEMENT_ENV_QW(i, f) \ + GUEST_STATE_ELEMENT_OFF(i, 16, f, copy_state_16to16) +#define GUEST_STATE_ELEMENT_ENV_DW(i, f) \ + GUEST_STATE_ELEMENT_OFF(i, 8, f, copy_state_8to8) +#define GUEST_STATE_ELEMENT_ENV_W(i, f) \ + GUEST_STATE_ELEMENT_OFF(i, 4, f, copy_state_4to8) +#define GUEST_STATE_ELEMENT_ENV_WW(i, f) \ + GUEST_STATE_ELEMENT_OFF(i, 4, f, copy_state_4to4) +#define GSE_ENV_DWM(i, f, m) \ + GUEST_STATE_ELEMENT_MSK(i, 8, f, copy_state_8to8, m) + +struct guest_state_element { + uint16_t id; + uint16_t size; + uint8_t value[]; +} QEMU_PACKED; + +struct guest_state_buffer { + uint32_t num_elements; + struct guest_state_element elements[]; +} QEMU_PACKED; + +/* Actual buffer plus some metadata about the request */ +struct guest_state_request { + struct guest_state_buffer *gsb; + int64_t buf; + int64_t len; + uint16_t flags; +}; + +/* + * Register state for entering a nested guest with H_ENTER_NESTED. + * New member must be added at the end. + */ +struct kvmppc_hv_guest_state { + uint64_t version; /* version of this structure layout, must be first */ + uint32_t lpid; + uint32_t vcpu_token; + /* These registers are hypervisor privileged (at least for writing) */ + uint64_t lpcr; + uint64_t pcr; + uint64_t amor; + uint64_t dpdes; + uint64_t hfscr; + int64_t tb_offset; + uint64_t dawr0; + uint64_t dawrx0; + uint64_t ciabr; + uint64_t hdec_expiry; + uint64_t purr; + uint64_t spurr; + uint64_t ic; + uint64_t vtb; + uint64_t hdar; + uint64_t hdsisr; + uint64_t heir; + uint64_t asdr; + /* These are OS privileged but need to be set late in guest entry */ + uint64_t srr0; + uint64_t srr1; + uint64_t sprg[4]; + uint64_t pidr; + uint64_t cfar; + uint64_t ppr; + /* Version 1 ends here */ + uint64_t dawr1; + uint64_t dawrx1; + /* Version 2 ends here */ +}; + +/* Latest version of hv_guest_state structure */ +#define HV_GUEST_STATE_VERSION 2 + +/* Linux 64-bit powerpc pt_regs struct, used by nested HV */ +struct kvmppc_pt_regs { + uint64_t gpr[32]; + uint64_t nip; + uint64_t msr; + uint64_t orig_gpr3; /* Used for restarting system calls */ + uint64_t ctr; + uint64_t link; + uint64_t xer; + uint64_t ccr; + uint64_t softe; /* Soft enabled/disabled */ + uint64_t trap; /* Reason for being here */ + uint64_t dar; /* Fault registers */ + uint64_t dsisr; /* on 4xx/Book-E used for ESR */ + uint64_t result; /* Result of a system call */ +}; + +/* + * nested_ppc_state is used to save the host CPU state before switching it to + * the guest CPU state, to be restored on H_ENTER_NESTED exit. + */ +struct nested_ppc_state { + uint64_t gpr[32]; + uint64_t lr; + uint64_t ctr; + uint64_t cfar; + uint64_t msr; + uint64_t nip; + uint32_t cr; + + uint64_t xer; + + uint64_t lpcr; + uint64_t lpidr; + uint64_t pidr; + uint64_t pcr; + uint64_t dpdes; + uint64_t hfscr; + uint64_t srr0; + uint64_t srr1; + uint64_t sprg0; + uint64_t sprg1; + uint64_t sprg2; + uint64_t sprg3; + uint64_t ppr; + + int64_t tb_offset; + /* Nested PAPR API */ + uint64_t amor; + uint64_t dawr0; + uint64_t dawrx0; + uint64_t ciabr; + uint64_t purr; + uint64_t spurr; + uint64_t ic; + uint64_t vtb; + uint64_t hdar; + uint64_t hdsisr; + uint64_t heir; + uint64_t asdr; + uint64_t dawr1; + uint64_t dawrx1; + uint64_t dexcr; + uint64_t hdexcr; + uint64_t hashkeyr; + uint64_t hashpkeyr; + ppc_vsr_t vsr[64] QEMU_ALIGNED(16); + uint64_t ebbhr; + uint64_t tar; + uint64_t ebbrr; + uint64_t bescr; + uint64_t iamr; + uint64_t amr; + uint64_t uamor; + uint64_t dscr; + uint64_t fscr; + uint64_t pspb; + uint64_t ctrl; + uint64_t vrsave; + uint64_t dar; + uint64_t dsisr; + uint64_t pmc1; + uint64_t pmc2; + uint64_t pmc3; + uint64_t pmc4; + uint64_t pmc5; + uint64_t pmc6; + uint64_t mmcr0; + uint64_t mmcr1; + uint64_t mmcr2; + uint64_t mmcra; + uint64_t sdar; + uint64_t siar; + uint64_t sier; + uint32_t vscr; + uint64_t fpscr; + int64_t dec_expiry_tb; +}; + +struct SpaprMachineStateNestedGuestVcpuRunBuf { + uint64_t addr; + uint64_t size; +}; + +typedef struct SpaprMachineStateNestedGuestVcpu { + bool enabled; + struct nested_ppc_state state; + struct SpaprMachineStateNestedGuestVcpuRunBuf runbufin; + struct SpaprMachineStateNestedGuestVcpuRunBuf runbufout; + int64_t tb_offset; + uint64_t hdecr_expiry_tb; +} SpaprMachineStateNestedGuestVcpu; + +struct guest_state_element_type { + uint16_t id; + int size; +#define GUEST_STATE_ELEMENT_TYPE_FLAG_GUEST_WIDE 0x1 +#define GUEST_STATE_ELEMENT_TYPE_FLAG_READ_ONLY 0x2 + uint16_t flags; + void *(*location)(SpaprMachineStateNestedGuest *, target_ulong); + size_t offset; + void (*copy)(void *, void *, bool); + uint64_t mask; +}; + +void spapr_exit_nested(PowerPCCPU *cpu, int excp); +typedef struct SpaprMachineState SpaprMachineState; +bool spapr_get_pate_nested_hv(SpaprMachineState *spapr, PowerPCCPU *cpu, + target_ulong lpid, ppc_v3_pate_t *entry); +uint8_t spapr_nested_api(SpaprMachineState *spapr); +void spapr_nested_gsb_init(void); +bool spapr_get_pate_nested_papr(SpaprMachineState *spapr, PowerPCCPU *cpu, + target_ulong lpid, ppc_v3_pate_t *entry); +#endif /* HW_SPAPR_NESTED_H */ diff --git a/include/hw/ppc/spapr_rtas.h b/include/hw/ppc/spapr_rtas.h deleted file mode 100644 index 383611f10f..0000000000 --- a/include/hw/ppc/spapr_rtas.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef HW_SPAPR_RTAS_H -#define HW_SPAPR_RTAS_H -/* - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -uint64_t qtest_rtas_call(char *cmd, uint32_t nargs, uint64_t args, - uint32_t nret, uint64_t rets); -#endif /* HW_SPAPR_RTAS_H */ diff --git a/include/hw/ppc/vof.h b/include/hw/ppc/vof.h index f8c0effcaf..d3f293da8b 100644 --- a/include/hw/ppc/vof.h +++ b/include/hw/ppc/vof.h @@ -9,7 +9,7 @@ #include "qom/object.h" #include "exec/address-spaces.h" #include "exec/memory.h" -#include "cpu.h" +#include "exec/cpu-defs.h" typedef struct Vof { uint64_t top_addr; /* copied from rma_size */ diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 00b80b08c2..95ead0dd7c 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -95,7 +95,7 @@ struct ICSStateClass { DeviceClass parent_class; DeviceRealize parent_realize; - DeviceReset parent_reset; + ResettablePhases parent_phases; void (*reject)(ICSState *s, uint32_t irq); void (*resend)(ICSState *s); diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index f7eea4ca81..f120874e0f 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -187,6 +187,7 @@ struct XiveSource { /* PQ bits and LSI assertion bit */ uint8_t *status; + uint8_t reset_pq; /* PQ state on reset */ /* ESB memory region */ uint64_t esb_flags; @@ -400,6 +401,7 @@ struct XiveRouterClass { int (*write_nvt)(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, XiveNVT *nvt, uint8_t word_number); uint8_t (*get_block_id)(XiveRouter *xrtr); + void (*end_notify)(XiveRouter *xrtr, XiveEAS *eas); }; int xive_router_get_eas(XiveRouter *xrtr, uint8_t eas_blk, uint32_t eas_idx, @@ -413,6 +415,7 @@ int xive_router_get_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, int xive_router_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, XiveNVT *nvt, uint8_t word_number); void xive_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked); +void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas); /* * XIVE Presenter @@ -430,6 +433,8 @@ typedef struct XivePresenterClass XivePresenterClass; DECLARE_CLASS_CHECKERS(XivePresenterClass, XIVE_PRESENTER, TYPE_XIVE_PRESENTER) +#define XIVE_PRESENTER_GEN1_TIMA_OS 0x1 + struct XivePresenterClass { InterfaceClass parent; int (*match_nvt)(XivePresenter *xptr, uint8_t format, @@ -437,6 +442,7 @@ struct XivePresenterClass { bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match); bool (*in_kernel)(const XivePresenter *xptr); + uint32_t (*get_config)(XivePresenter *xptr); }; int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index e9e3ea135e..ab68f8d157 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -11,7 +11,9 @@ #ifndef PPC_XIVE2_H #define PPC_XIVE2_H +#include "hw/ppc/xive.h" #include "hw/ppc/xive2_regs.h" +#include "hw/sysbus.h" /* * XIVE2 Router (POWER10) diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index 14605bd458..816f5d0e84 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -10,6 +10,8 @@ #ifndef PPC_XIVE2_REGS_H #define PPC_XIVE2_REGS_H +#include "qemu/bswap.h" + /* * Thread Interrupt Management Area (TIMA) * diff --git a/include/hw/ppc/xive_regs.h b/include/hw/ppc/xive_regs.h index b7fde2354e..4a3c9badd3 100644 --- a/include/hw/ppc/xive_regs.h +++ b/include/hw/ppc/xive_regs.h @@ -48,6 +48,22 @@ #define TM_SHIFT 16 +/* + * TIMA addresses are 12-bits (4k page). + * The MSB indicates a special op with side effect, which can be + * refined with bit 10 (see below). + * The registers, logically grouped in 4 rings (a quad-word each), are + * defined on the 6 LSBs (offset below 0x40) + * In between, we can add a cache line index from 0...3 (ie, 0, 0x80, + * 0x100, 0x180) to select a specific snooper. Those 'snoop port + * address' bits should be dropped when processing the operations as + * they are all equivalent. + */ +#define TM_ADDRESS_MASK 0xC3F +#define TM_SPECIAL_OP 0x800 +#define TM_RING_OFFSET 0x30 +#define TM_REG_OFFSET 0x3F + /* TM register offsets */ #define TM_QW0_USER 0x000 /* All rings */ #define TM_QW1_OS 0x010 /* Ring 0..2 */ diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 92c3d65208..9228e96c87 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -1,6 +1,7 @@ #ifndef QDEV_CORE_H #define QDEV_CORE_H +#include "qemu/atomic.h" #include "qemu/queue.h" #include "qemu/bitmap.h" #include "qemu/rcu.h" @@ -9,6 +10,65 @@ #include "hw/hotplug.h" #include "hw/resettable.h" +/** + * DOC: The QEMU Device API + * + * All modern devices should represented as a derived QOM class of + * TYPE_DEVICE. The device API introduces the additional methods of + * @realize and @unrealize to represent additional stages in a device + * objects life cycle. + * + * Realization + * ----------- + * + * Devices are constructed in two stages: + * + * 1) object instantiation via object_initialize() and + * 2) device realization via the #DeviceState.realized property + * + * The former may not fail (and must not abort or exit, since it is called + * during device introspection already), and the latter may return error + * information to the caller and must be re-entrant. + * Trivial field initializations should go into #TypeInfo.instance_init. + * Operations depending on @props static properties should go into @realize. + * After successful realization, setting static properties will fail. + * + * As an interim step, the #DeviceState.realized property can also be + * set with qdev_realize(). In the future, devices will propagate this + * state change to their children and along busses they expose. The + * point in time will be deferred to machine creation, so that values + * set in @realize will not be introspectable beforehand. Therefore + * devices must not create children during @realize; they should + * initialize them via object_initialize() in their own + * #TypeInfo.instance_init and forward the realization events + * appropriately. + * + * Any type may override the @realize and/or @unrealize callbacks but needs + * to call the parent type's implementation if keeping their functionality + * is desired. Refer to QOM documentation for further discussion and examples. + * + * .. note:: + * Since TYPE_DEVICE doesn't implement @realize and @unrealize, types + * derived directly from it need not call their parent's @realize and + * @unrealize. For other types consult the documentation and + * implementation of the respective parent types. + * + * Hiding a device + * --------------- + * + * To hide a device, a DeviceListener function hide_device() needs to + * be registered. It can be used to defer adding a device and + * therefore hide it from the guest. The handler registering to this + * DeviceListener can save the QOpts passed to it for re-using it + * later. It must return if it wants the device to be hidden or + * visible. When the handler function decides the device shall be + * visible it will be added with qdev_device_add() and realized as any + * other device. Otherwise qdev_device_add() will return early without + * adding the device. The guest will not see a "hidden" device until + * it was marked visible and qdev_device_add called again. + * + */ + enum { DEV_NVECTORS_UNSPECIFIED = -1, }; @@ -37,7 +97,7 @@ typedef void (*BusRealize)(BusState *bus, Error **errp); typedef void (*BusUnrealize)(BusState *bus); /** - * DeviceClass: + * struct DeviceClass - The base class for all devices. * @props: Properties accessing state fields. * @realize: Callback function invoked when the #DeviceState:realized * property is changed to %true. @@ -46,72 +106,37 @@ typedef void (*BusUnrealize)(BusState *bus); * @hotpluggable: indicates if #DeviceClass is hotpluggable, available * as readonly "hotpluggable" property of #DeviceState instance * - * # Realization # - * Devices are constructed in two stages, - * 1) object instantiation via object_initialize() and - * 2) device realization via #DeviceState:realized property. - * The former may not fail (and must not abort or exit, since it is called - * during device introspection already), and the latter may return error - * information to the caller and must be re-entrant. - * Trivial field initializations should go into #TypeInfo.instance_init. - * Operations depending on @props static properties should go into @realize. - * After successful realization, setting static properties will fail. - * - * As an interim step, the #DeviceState:realized property can also be - * set with qdev_realize(). - * In the future, devices will propagate this state change to their children - * and along busses they expose. - * The point in time will be deferred to machine creation, so that values - * set in @realize will not be introspectable beforehand. Therefore devices - * must not create children during @realize; they should initialize them via - * object_initialize() in their own #TypeInfo.instance_init and forward the - * realization events appropriately. - * - * Any type may override the @realize and/or @unrealize callbacks but needs - * to call the parent type's implementation if keeping their functionality - * is desired. Refer to QOM documentation for further discussion and examples. - * - * - * - * Since TYPE_DEVICE doesn't implement @realize and @unrealize, types - * derived directly from it need not call their parent's @realize and - * @unrealize. - * For other types consult the documentation and implementation of the - * respective parent types. - * - * - * - * # Hiding a device # - * To hide a device, a DeviceListener function hide_device() needs to - * be registered. - * It can be used to defer adding a device and therefore hide it from - * the guest. The handler registering to this DeviceListener can save - * the QOpts passed to it for re-using it later. It must return if it - * wants the device to be hidden or visible. When the handler function - * decides the device shall be visible it will be added with - * qdev_device_add() and realized as any other device. Otherwise - * qdev_device_add() will return early without adding the device. The - * guest will not see a "hidden" device until it was marked visible - * and qdev_device_add called again. - * */ struct DeviceClass { - /*< private >*/ + /* private: */ ObjectClass parent_class; - /*< public >*/ + /* public: */ + + /** + * @categories: device categories device belongs to + */ DECLARE_BITMAP(categories, DEVICE_CATEGORY_MAX); + /** + * @fw_name: name used to identify device to firmware interfaces + */ const char *fw_name; + /** + * @desc: human readable description of device + */ const char *desc; - /* - * The underscore at the end ensures a compile-time error if someone - * assigns to dc->props instead of using device_class_set_props. + /** + * @props_: properties associated with device, should only be + * assigned by using device_class_set_props(). The underscore + * ensures a compile-time error if someone attempts to assign + * dc->props directly. */ Property *props_; - /* - * Can this device be instantiated with -device / device_add? + /** + * @user_creatable: Can user instantiate with -device / device_add? + * * All devices should support instantiation with device_add, and * this flag should not exist. But we're not there, yet. Some * devices fail to instantiate with cryptic error messages. @@ -119,25 +144,35 @@ struct DeviceClass { * behavior would be cruel; clearing this flag will protect them. * It should never be cleared without a comment explaining why it * is cleared. + * * TODO remove once we're there */ bool user_creatable; bool hotpluggable; /* callbacks */ - /* - * Reset method here is deprecated and replaced by methods in the - * resettable class interface to implement a multi-phase reset. + /** + * @reset: deprecated device reset method pointer + * + * Modern code should use the ResettableClass interface to + * implement a multi-phase reset. + * * TODO: remove once every reset callback is unused */ DeviceReset reset; DeviceRealize realize; DeviceUnrealize unrealize; - /* device state */ + /** + * @vmsd: device state serialisation description for + * migration/save/restore + */ const VMStateDescription *vmsd; - /* Private to qdev / bus. */ + /** + * @bus_type: bus type + * private: to qdev / bus. + */ const char *bus_type; }; @@ -162,37 +197,101 @@ struct NamedClockList { QLIST_ENTRY(NamedClockList) node; }; +typedef struct { + bool engaged_in_io; +} MemReentrancyGuard; + + +typedef QLIST_HEAD(, NamedGPIOList) NamedGPIOListHead; +typedef QLIST_HEAD(, NamedClockList) NamedClockListHead; +typedef QLIST_HEAD(, BusState) BusStateHead; + /** - * DeviceState: - * @realized: Indicates whether the device has been fully constructed. - * When accessed outside big qemu lock, must be accessed with - * qatomic_load_acquire() - * @reset: ResettableState for the device; handled by Resettable interface. + * struct DeviceState - common device state, accessed with qdev helpers * * This structure should not be accessed directly. We declare it here * so that it can be embedded in individual device state structures. */ struct DeviceState { - /*< private >*/ + /* private: */ Object parent_obj; - /*< public >*/ + /* public: */ + /** + * @id: global device id + */ char *id; + /** + * @canonical_path: canonical path of realized device in the QOM tree + */ char *canonical_path; + /** + * @realized: has device been realized? + */ bool realized; + /** + * @pending_deleted_event: track pending deletion events during unplug + */ bool pending_deleted_event; + /** + * @pending_deleted_expires_ms: optional timeout for deletion events + */ int64_t pending_deleted_expires_ms; + /** + * @opts: QDict of options for the device + */ QDict *opts; + /** + * @hotplugged: was device added after PHASE_MACHINE_READY? + */ int hotplugged; + /** + * @allow_unplug_during_migration: can device be unplugged during migration + */ bool allow_unplug_during_migration; + /** + * @parent_bus: bus this device belongs to + */ BusState *parent_bus; - QLIST_HEAD(, NamedGPIOList) gpios; - QLIST_HEAD(, NamedClockList) clocks; - QLIST_HEAD(, BusState) child_bus; + /** + * @gpios: QLIST of named GPIOs the device provides. + */ + NamedGPIOListHead gpios; + /** + * @clocks: QLIST of named clocks the device provides. + */ + NamedClockListHead clocks; + /** + * @child_bus: QLIST of child buses + */ + BusStateHead child_bus; + /** + * @num_child_bus: number of @child_bus entries + */ int num_child_bus; + /** + * @instance_id_alias: device alias for handling legacy migration setups + */ int instance_id_alias; + /** + * @alias_required_for_version: indicates @instance_id_alias is + * needed for migration + */ int alias_required_for_version; + /** + * @reset: ResettableState for the device; handled by Resettable interface. + */ ResettableState reset; + /** + * @unplug_blockers: list of reasons to block unplugging of device + */ + GSList *unplug_blockers; + /** + * @mem_reentrancy_guard: Is the device currently in mmio/pio/dma? + * + * Used to prevent re-entrancy confusing things. + */ + MemReentrancyGuard mem_reentrancy_guard; }; struct DeviceListener { @@ -230,8 +329,6 @@ struct BusClass { */ char *(*get_fw_dev_path)(DeviceState *dev); - void (*reset)(BusState *bus); - /* * Return whether the device can be added to @bus, * based on the address that was set (via device properties) @@ -258,13 +355,24 @@ typedef struct BusChild { #define QDEV_HOTPLUG_HANDLER_PROPERTY "hotplug-handler" +typedef QTAILQ_HEAD(, BusChild) BusChildHead; +typedef QLIST_ENTRY(BusState) BusStateEntry; + /** - * BusState: + * struct BusState: + * @obj: parent object + * @parent: parent Device + * @name: name of bus * @hotplug_handler: link to a hotplug handler associated with bus. - * @reset: ResettableState for the bus; handled by Resettable interface. + * @max_index: max number of child buses + * @realized: is the bus itself realized? + * @full: is the bus full? + * @num_children: current number of child buses */ struct BusState { + /* private: */ Object obj; + /* public: */ DeviceState *parent; char *name; HotplugHandler *hotplug_handler; @@ -273,18 +381,24 @@ struct BusState { bool full; int num_children; - /* - * children is a RCU QTAILQ, thus readers must use RCU to access it, - * and writers must hold the big qemu lock + /** + * @children: an RCU protected QTAILQ, thus readers must use RCU + * to access it, and writers must hold the big qemu lock + */ + BusChildHead children; + /** + * @sibling: next bus + */ + BusStateEntry sibling; + /** + * @reset: ResettableState for the bus; handled by Resettable interface. */ - - QTAILQ_HEAD(, BusChild) children; - QLIST_ENTRY(BusState) sibling; ResettableState reset; }; /** - * GlobalProperty: + * typedef GlobalProperty - a global property type + * * @used: Set to true if property was used when initializing a device. * @optional: If set to true, GlobalProperty will be skipped without errors * if the property doesn't exist. @@ -318,7 +432,8 @@ compat_props_add(GPtrArray *arr, * This only allocates the memory and initializes the device state * structure, ready for the caller to set properties if they wish. * The device still needs to be realized. - * The returned object has a reference count of 1. + * + * Return: a derived DeviceState object with a reference count of 1. */ DeviceState *qdev_new(const char *name); @@ -328,9 +443,24 @@ DeviceState *qdev_new(const char *name); * * This is like qdev_new(), except it returns %NULL when type @name * does not exist, rather than asserting. + * + * Return: a derived DeviceState object with a reference count of 1 or + * NULL if type @name does not exist. */ DeviceState *qdev_try_new(const char *name); +/** + * qdev_is_realized() - check if device is realized + * @dev: The device to check. + * + * Context: May be called outside big qemu lock. + * Return: true if the device has been fully constructed, false otherwise. + */ +static inline bool qdev_is_realized(DeviceState *dev) +{ + return qatomic_load_acquire(&dev->realized); +} + /** * qdev_realize: Realize @dev. * @dev: device to realize @@ -342,11 +472,11 @@ DeviceState *qdev_try_new(const char *name); * @dev must not be plugged into a bus already. * If @bus, plug @dev into @bus. This takes a reference to @dev. * If @dev has no QOM parent, make one up, taking another reference. - * On success, return true. - * On failure, store an error through @errp and return false. * * If you created @dev using qdev_new(), you probably want to use * qdev_realize_and_unref() instead. + * + * Return: true on success, else false setting @errp with error */ bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp); @@ -373,6 +503,8 @@ bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp); * for the only reference to the child device to be held by the parent * via the child<> property, and so the reference-count-drop done here * would be incorrect. For that use case you want qdev_realize(). + * + * Return: true on success, else false setting @errp with error */ bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp); @@ -385,7 +517,7 @@ bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp); * * - unrealize any child buses by calling qbus_unrealize() * (this will recursively unrealize any devices on those buses) - * - call the the unrealize method of @dev + * - call the unrealize method of @dev * * The device can then be freed by causing its reference count to go * to zero. @@ -401,16 +533,16 @@ void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); bool qdev_hotplug_allowed(DeviceState *dev, Error **errp); + /** - * qdev_get_hotplug_handler: Get handler responsible for device wiring - * - * Find HOTPLUG_HANDLER for @dev that provides [pre|un]plug callbacks for it. + * qdev_get_hotplug_handler() - Get handler responsible for device wiring + * @dev: the device we want the HOTPLUG_HANDLER for. * * Note: in case @dev has a parent bus, it will be returned as handler unless * machine handler overrides it. * - * Returns: pointer to object that implements TYPE_HOTPLUG_HANDLER interface - * or NULL if there aren't any. + * Return: pointer to object that implements TYPE_HOTPLUG_HANDLER interface + * or NULL if there aren't any. */ HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev); void qdev_unplug(DeviceState *dev, Error **errp); @@ -420,7 +552,35 @@ void qdev_machine_creation_done(void); bool qdev_machine_modified(void); /** - * GpioPolarity: Polarity of a GPIO line + * qdev_add_unplug_blocker: Add an unplug blocker to a device + * + * @dev: Device to be blocked from unplug + * @reason: Reason for blocking + */ +void qdev_add_unplug_blocker(DeviceState *dev, Error *reason); + +/** + * qdev_del_unplug_blocker: Remove an unplug blocker from a device + * + * @dev: Device to be unblocked + * @reason: Pointer to the Error used with qdev_add_unplug_blocker. + * Used as a handle to lookup the blocker for deletion. + */ +void qdev_del_unplug_blocker(DeviceState *dev, Error *reason); + +/** + * qdev_unplug_blocked: Confirm if a device is blocked from unplug + * + * @dev: Device to be tested + * @errp: The reasons why the device is blocked, if any + * + * Returns: true (also setting @errp) if device is blocked from unplug, + * false otherwise + */ +bool qdev_unplug_blocked(DeviceState *dev, Error **errp); + +/** + * typedef GpioPolarity - Polarity of a GPIO line * * GPIO lines use either positive (active-high) logic, * or negative (active-low) logic. @@ -452,6 +612,8 @@ typedef enum { * connect another device's output GPIO line to this input. * * For named input GPIO lines, use qdev_get_gpio_in_named(). + * + * Return: qemu_irq corresponding to anonymous input GPIO line */ qemu_irq qdev_get_gpio_in(DeviceState *dev, int n); @@ -469,6 +631,8 @@ qemu_irq qdev_get_gpio_in(DeviceState *dev, int n); * array); this function will assert() if passed an invalid name or index. * * For anonymous input GPIO lines, use qdev_get_gpio_in(). + * + * Return: qemu_irq corresponding to named input GPIO line */ qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n); @@ -476,7 +640,7 @@ qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n); * qdev_connect_gpio_out: Connect one of a device's anonymous output GPIO lines * @dev: Device whose GPIO to connect * @n: Number of the anonymous output GPIO line (which must be in range) - * @input_pin: qemu_irq to connect the output line to + * @pin: qemu_irq to connect the output line to * * This function connects an anonymous output GPIO line on a device * up to an arbitrary qemu_irq, so that when the device asserts that @@ -547,6 +711,8 @@ void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, * * You probably don't need to use this function -- it is used only * by the platform-bus subsystem. + * + * Return: qemu_irq associated with GPIO or NULL if un-wired. */ qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n); @@ -557,14 +723,17 @@ qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n); * @name: Name of the output GPIO array * @n: Number of the GPIO line in the array * - * This function is provided only for use by the qtest testing framework - * and is not suitable for use in non-testing parts of QEMU. + * .. note:: + * This function is provided only for use by the qtest testing framework + * and is not suitable for use in non-testing parts of QEMU. * * This function breaks an existing connection of an outbound GPIO * line from @dev, and replaces it with the new qemu_irq @icpt, as if * ``qdev_connect_gpio_out_named(dev, icpt, name, n)`` had been called. * The previously connected qemu_irq is returned, so it can be restored * by a second call to qdev_intercept_gpio_out() if desired. + * + * Return: old disconnected qemu_irq if one existed */ qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt, const char *name, int n); @@ -636,9 +805,7 @@ void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, const char *name, int n); /** - * qdev_init_gpio_in_named_with_opaque: create an array of input GPIO lines - * for the specified device - * + * qdev_init_gpio_in_named_with_opaque() - create an array of input GPIO lines * @dev: Device to create input GPIOs for * @handler: Function to call when GPIO line value is set * @opaque: Opaque data pointer to pass to @handler @@ -651,8 +818,11 @@ void qdev_init_gpio_in_named_with_opaque(DeviceState *dev, const char *name, int n); /** - * qdev_init_gpio_in_named: create an array of input GPIO lines - * for the specified device + * qdev_init_gpio_in_named() - create an array of input GPIO lines + * @dev: device to add array to + * @handler: a &typedef qemu_irq_handler function to call when GPIO is set + * @name: Name of the GPIO input (must be unique for this device) + * @n: Number of GPIO lines in this input set * * Like qdev_init_gpio_in_named_with_opaque(), but the opaque pointer * passed to the handler is @dev (which is the most commonly desired behaviour). @@ -686,7 +856,7 @@ static inline void qdev_init_gpio_in_named(DeviceState *dev, void qdev_pass_gpios(DeviceState *dev, DeviceState *container, const char *name); -BusState *qdev_get_parent_bus(DeviceState *dev); +BusState *qdev_get_parent_bus(const DeviceState *dev); /*** BUS API. ***/ @@ -715,40 +885,17 @@ int qdev_walk_children(DeviceState *dev, void *opaque); /** - * @qdev_reset_all: - * Reset @dev. See @qbus_reset_all() for more details. + * device_cold_reset() - perform a recursive cold reset on a device + * @dev: device to reset. * - * 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); - -/** - * @qbus_reset_all: - * @bus: Bus to be reset. - * - * Reset @bus and perform a bus-level ("hard") reset of all devices connected - * to it, including recursive processing of all buses below @bus itself. A - * 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: + * bus_cold_reset() - perform a recursive cold reset on a bus + * @bus: bus to reset * * Reset bus @bus and perform a recursive processing using the resettable * interface. It triggers a RESET_TYPE_COLD. @@ -756,14 +903,18 @@ void device_cold_reset(DeviceState *dev); void bus_cold_reset(BusState *bus); /** - * device_is_in_reset: - * Return true if the device @dev is currently being reset. + * device_is_in_reset() - check device reset state + * @dev: device to check + * + * 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. + * bus_is_in_reset() - check bus reset state + * @bus: bus to check + * + * Return: true if the bus @bus is currently being reset. */ bool bus_is_in_reset(BusState *bus); @@ -774,27 +925,61 @@ char *qdev_get_fw_dev_path(DeviceState *dev); char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev); /** - * device_legacy_reset: + * device_class_set_props(): add a set of properties to an device + * @dc: the parent DeviceClass all devices inherit + * @props: an array of properties, terminate by DEFINE_PROP_END_OF_LIST() * - * 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. + * This will add a set of properties to the object. It will fault if + * you attempt to add an existing property defined by a parent class. + * To modify an inherited property you need to use???? */ -void device_legacy_reset(DeviceState *dev); - void device_class_set_props(DeviceClass *dc, Property *props); /** - * device_class_set_parent_reset: + * device_class_set_parent_reset() - legacy set device reset handlers + * @dc: device class + * @dev_reset: function pointer to reset handler + * @parent_reset: function pointer to parents reset handler + * + * Modern code should use the ResettableClass interface to + * implement a multi-phase reset instead. + * * 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); + +/** + * device_class_set_parent_realize() - set up for chaining realize fns + * @dc: The device class + * @dev_realize: the device realize function + * @parent_realize: somewhere to save the parents realize function + * + * This is intended to be used when the new realize function will + * eventually call its parent realization function during creation. + * This requires storing the function call somewhere (usually in the + * instance structure) so you can eventually call + * dc->parent_realize(dev, errp) + */ void device_class_set_parent_realize(DeviceClass *dc, DeviceRealize dev_realize, DeviceRealize *parent_realize); + + +/** + * device_class_set_parent_unrealize() - set up for chaining unrealize fns + * @dc: The device class + * @dev_unrealize: the device realize function + * @parent_unrealize: somewhere to save the parents unrealize function + * + * This is intended to be used when the new unrealize function will + * eventually call its parent unrealization function during the + * unrealize phase. This requires storing the function call somewhere + * (usually in the instance structure) so you can eventually call + * dc->parent_unrealize(dev); + */ void device_class_set_parent_unrealize(DeviceClass *dc, DeviceUnrealize dev_unrealize, DeviceUnrealize *parent_unrealize); @@ -806,6 +991,20 @@ const char *qdev_fw_name(DeviceState *dev); void qdev_assert_realized_properly(void); Object *qdev_get_machine(void); +/** + * qdev_get_human_name() - Return a human-readable name for a device + * @dev: The device. Must be a valid and non-NULL pointer. + * + * .. note:: + * This function is intended for user friendly error messages. + * + * Returns: A newly allocated string containing the device id if not null, + * else the object canonical path. + * + * Use g_free() to free it. + */ +char *qdev_get_human_name(DeviceState *dev); + /* FIXME: make this a link<> */ bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp); @@ -818,7 +1017,18 @@ void qbus_set_bus_hotplug_handler(BusState *bus); static inline bool qbus_is_hotpluggable(BusState *bus) { - return bus->hotplug_handler; + HotplugHandler *plug_handler = bus->hotplug_handler; + bool ret = !!plug_handler; + + if (plug_handler) { + HotplugHandlerClass *hdc; + + hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + if (hdc->is_hotpluggable_bus) { + ret = hdc->is_hotpluggable_bus(plug_handler, bus); + } + } + return ret; } /** @@ -848,14 +1058,15 @@ void device_listener_register(DeviceListener *listener); void device_listener_unregister(DeviceListener *listener); /** - * @qdev_should_hide_device: + * qdev_should_hide_device() - check if device should be hidden + * * @opts: options QDict * @from_json: true if @opts entries are typed, false for all strings * @errp: pointer to error object * - * Check if a device should be added. - * When a device is added via qdev_device_add() this will be called, - * and return if the device should be added now or not. + * When a device is added via qdev_device_add() this will be called. + * + * Return: if the device should be added now or not. */ bool qdev_should_hide_device(const QDict *opts, bool from_json, Error **errp); @@ -872,6 +1083,11 @@ typedef enum MachineInitPhase { */ PHASE_ACCEL_CREATED, + /* + * Late backend objects have been created and initialized. + */ + PHASE_LATE_BACKENDS_CREATED, + /* * machine_class->init has been called, thus creating any embedded * devices and validating machine properties. Devices created at @@ -887,7 +1103,7 @@ typedef enum MachineInitPhase { PHASE_MACHINE_READY, } MachineInitPhase; -extern bool phase_check(MachineInitPhase phase); -extern void phase_advance(MachineInitPhase phase); +bool phase_check(MachineInitPhase phase); +void phase_advance(MachineInitPhase phase); #endif diff --git a/include/hw/qdev-properties-system.h b/include/hw/qdev-properties-system.h index 0ac327ae60..438f65389f 100644 --- a/include/hw/qdev-properties-system.h +++ b/include/hw/qdev-properties-system.h @@ -7,6 +7,9 @@ extern const PropertyInfo qdev_prop_chr; extern const PropertyInfo qdev_prop_macaddr; extern const PropertyInfo qdev_prop_reserved_region; extern const PropertyInfo qdev_prop_multifd_compression; +extern const PropertyInfo qdev_prop_mig_mode; +extern const PropertyInfo qdev_prop_granule_mode; +extern const PropertyInfo qdev_prop_zero_page_detection; extern const PropertyInfo qdev_prop_losttickpolicy; extern const PropertyInfo qdev_prop_blockdev_on_error; extern const PropertyInfo qdev_prop_bios_chs_trans; @@ -22,6 +25,8 @@ extern const PropertyInfo qdev_prop_audiodev; extern const PropertyInfo qdev_prop_off_auto_pcibar; extern const PropertyInfo qdev_prop_pcie_link_speed; extern const PropertyInfo qdev_prop_pcie_link_width; +extern const PropertyInfo qdev_prop_cpus390entitlement; +extern const PropertyInfo qdev_prop_iothread_vq_mapping_list; #define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pci_devfn, int32_t) @@ -41,6 +46,14 @@ extern const PropertyInfo qdev_prop_pcie_link_width; #define DEFINE_PROP_MULTIFD_COMPRESSION(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_multifd_compression, \ MultiFDCompression) +#define DEFINE_PROP_MIG_MODE(_n, _s, _f, _d) \ + DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_mig_mode, \ + MigMode) +#define DEFINE_PROP_GRANULE_MODE(_n, _s, _f, _d) \ + DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_granule_mode, GranuleMode) +#define DEFINE_PROP_ZERO_PAGE_DETECTION(_n, _s, _f, _d) \ + DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_zero_page_detection, \ + ZeroPageDetection) #define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_losttickpolicy, \ LostTickPolicy) @@ -73,5 +86,12 @@ extern const PropertyInfo qdev_prop_pcie_link_width; #define DEFINE_PROP_UUID_NODEFAULT(_name, _state, _field) \ DEFINE_PROP(_name, _state, _field, qdev_prop_uuid, QemuUUID) +#define DEFINE_PROP_CPUS390ENTITLEMENT(_n, _s, _f, _d) \ + DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_cpus390entitlement, \ + CpuS390Entitlement) + +#define DEFINE_PROP_IOTHREAD_VQ_MAPPING_LIST(_name, _state, _field) \ + DEFINE_PROP(_name, _state, _field, qdev_prop_iothread_vq_mapping_list, \ + IOThreadVirtQueueMappingList *) #endif diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index e1df08876c..09aa04ca1e 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -61,7 +61,7 @@ extern const PropertyInfo qdev_prop_size; extern const PropertyInfo qdev_prop_string; extern const PropertyInfo qdev_prop_on_off_auto; extern const PropertyInfo qdev_prop_size32; -extern const PropertyInfo qdev_prop_arraylen; +extern const PropertyInfo qdev_prop_array; extern const PropertyInfo qdev_prop_link; #define DEFINE_PROP(_name, _state, _field, _prop, _type, ...) { \ @@ -115,8 +115,6 @@ extern const PropertyInfo qdev_prop_link; .bitmask = (_bitmask), \ .set_default = false) -#define PROP_ARRAY_LEN_PREFIX "len-" - /** * DEFINE_PROP_ARRAY: * @_name: name of the array @@ -127,28 +125,25 @@ extern const PropertyInfo qdev_prop_link; * @_arrayprop: PropertyInfo defining what property the array elements have * @_arraytype: C type of the array elements * - * Define device properties for a variable-length array _name. A - * static property "len-arrayname" is defined. When the device creator - * sets this property to the desired length of array, further dynamic - * properties "arrayname[0]", "arrayname[1]", ... are defined so the - * device creator can set the array element values. Setting the - * "len-arrayname" property more than once is an error. + * Define device properties for a variable-length array _name. The array is + * represented as a list in the visitor interface. * - * When the array length is set, the @_field member of the device + * @_arraytype is required to be movable with memcpy(). + * + * When the array property is set, the @_field member of the device * struct is set to the array length, and @_arrayfield is set to point - * to (zero-initialised) memory allocated for the array. For a zero - * length array, @_field will be set to 0 and @_arrayfield to NULL. + * to the memory allocated for the array. + * * It is the responsibility of the device deinit code to free the * @_arrayfield memory. */ -#define DEFINE_PROP_ARRAY(_name, _state, _field, \ - _arrayfield, _arrayprop, _arraytype) \ - DEFINE_PROP((PROP_ARRAY_LEN_PREFIX _name), \ - _state, _field, qdev_prop_arraylen, uint32_t, \ - .set_default = true, \ - .defval.u = 0, \ - .arrayinfo = &(_arrayprop), \ - .arrayfieldsize = sizeof(_arraytype), \ +#define DEFINE_PROP_ARRAY(_name, _state, _field, \ + _arrayfield, _arrayprop, _arraytype) \ + DEFINE_PROP(_name, _state, _field, qdev_prop_array, uint32_t, \ + .set_default = true, \ + .defval.u = 0, \ + .arrayinfo = &(_arrayprop), \ + .arrayfieldsize = sizeof(_arraytype), \ .arrayoffset = offsetof(_state, _arrayfield)) #define DEFINE_PROP_LINK(_name, _state, _field, _type, _ptr_type) \ @@ -206,6 +201,9 @@ 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); +/* Takes ownership of @values */ +void qdev_prop_set_array(DeviceState *dev, const char *name, QList *values); + void *object_field_prop_ptr(Object *obj, Property *prop); void qdev_prop_register_global(GlobalProperty *prop); @@ -232,8 +230,8 @@ void qdev_property_add_static(DeviceState *dev, Property *prop); * @target: Device which has properties to be aliased * @source: Object to add alias properties to * - * Add alias properties to the @source object for all qdev properties on - * the @target DeviceState. + * Add alias properties to the @source object for all properties on the @target + * DeviceState. * * This is useful when @target is an internal implementation object * owned by @source, and you want to expose all the properties of that diff --git a/include/hw/registerfields.h b/include/hw/registerfields.h index 3a88e135d0..1330ca77de 100644 --- a/include/hw/registerfields.h +++ b/include/hw/registerfields.h @@ -154,4 +154,74 @@ #define ARRAY_FIELD_DP64(regs, reg, field, val) \ (regs)[R_ ## reg] = FIELD_DP64((regs)[R_ ## reg], reg, field, val); + +/* + * These macros can be used for defining and extracting fields that have the + * same bit position across multiple registers. + */ + +/* Define shared SHIFT, LENGTH, and MASK constants */ +#define SHARED_FIELD(name, shift, length) \ + enum { name ## _ ## SHIFT = (shift)}; \ + enum { name ## _ ## LENGTH = (length)}; \ + enum { name ## _ ## MASK = MAKE_64BIT_MASK(shift, length)}; + +/* Extract a shared field */ +#define SHARED_FIELD_EX8(storage, field) \ + extract8((storage), field ## _SHIFT, field ## _LENGTH) + +#define SHARED_FIELD_EX16(storage, field) \ + extract16((storage), field ## _SHIFT, field ## _LENGTH) + +#define SHARED_FIELD_EX32(storage, field) \ + extract32((storage), field ## _SHIFT, field ## _LENGTH) + +#define SHARED_FIELD_EX64(storage, field) \ + extract64((storage), field ## _SHIFT, field ## _LENGTH) + +/* Extract a shared field from a register array */ +#define SHARED_ARRAY_FIELD_EX32(regs, offset, field) \ + SHARED_FIELD_EX32((regs)[(offset)], field) +#define SHARED_ARRAY_FIELD_EX64(regs, offset, field) \ + SHARED_FIELD_EX64((regs)[(offset)], field) + +/* Deposit a shared field */ +#define SHARED_FIELD_DP8(storage, field, val) ({ \ + struct { \ + unsigned int v:field ## _LENGTH; \ + } _v = { .v = val }; \ + uint8_t _d; \ + _d = deposit32((storage), field ## _SHIFT, field ## _LENGTH, _v.v); \ + _d; }) + +#define SHARED_FIELD_DP16(storage, field, val) ({ \ + struct { \ + unsigned int v:field ## _LENGTH; \ + } _v = { .v = val }; \ + uint16_t _d; \ + _d = deposit32((storage), field ## _SHIFT, field ## _LENGTH, _v.v); \ + _d; }) + +#define SHARED_FIELD_DP32(storage, field, val) ({ \ + struct { \ + unsigned int v:field ## _LENGTH; \ + } _v = { .v = val }; \ + uint32_t _d; \ + _d = deposit32((storage), field ## _SHIFT, field ## _LENGTH, _v.v); \ + _d; }) + +#define SHARED_FIELD_DP64(storage, field, val) ({ \ + struct { \ + uint64_t v:field ## _LENGTH; \ + } _v = { .v = val }; \ + uint64_t _d; \ + _d = deposit64((storage), field ## _SHIFT, field ## _LENGTH, _v.v); \ + _d; }) + +/* Deposit a shared field to a register array */ +#define SHARED_ARRAY_FIELD_DP32(regs, offset, field, val) \ + (regs)[(offset)] = SHARED_FIELD_DP32((regs)[(offset)], field, val); +#define SHARED_ARRAY_FIELD_DP64(regs, offset, field, val) \ + (regs)[(offset)] = SHARED_FIELD_DP64((regs)[(offset)], field, val); + #endif diff --git a/include/hw/remote/iohub.h b/include/hw/remote/iohub.h index 0bf98e0d78..6a8444f9a9 100644 --- a/include/hw/remote/iohub.h +++ b/include/hw/remote/iohub.h @@ -11,7 +11,7 @@ #ifndef REMOTE_IOHUB_H #define REMOTE_IOHUB_H -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "qemu/event_notifier.h" #include "qemu/thread-posix.h" #include "hw/remote/mpqemu-link.h" diff --git a/include/hw/remote/iommu.h b/include/hw/remote/iommu.h new file mode 100644 index 0000000000..33b68a8f4b --- /dev/null +++ b/include/hw/remote/iommu.h @@ -0,0 +1,40 @@ +/** + * Copyright © 2022 Oracle and/or its affiliates. + * + * 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 REMOTE_IOMMU_H +#define REMOTE_IOMMU_H + +#include "hw/pci/pci_bus.h" +#include "hw/pci/pci.h" + +#ifndef INT2VOIDP +#define INT2VOIDP(i) (void *)(uintptr_t)(i) +#endif + +typedef struct RemoteIommuElem { + MemoryRegion *mr; + + AddressSpace as; +} RemoteIommuElem; + +#define TYPE_REMOTE_IOMMU "x-remote-iommu" +OBJECT_DECLARE_SIMPLE_TYPE(RemoteIommu, REMOTE_IOMMU) + +struct RemoteIommu { + Object parent; + + GHashTable *elem_by_devfn; + + QemuMutex lock; +}; + +void remote_iommu_setup(PCIBus *pci_bus); + +void remote_iommu_unplug_dev(PCIDevice *pci_dev); + +#endif diff --git a/include/hw/remote/machine.h b/include/hw/remote/machine.h index 2a2a33c4b2..ac32fda387 100644 --- a/include/hw/remote/machine.h +++ b/include/hw/remote/machine.h @@ -22,6 +22,10 @@ struct RemoteMachineState { RemotePCIHost *host; RemoteIOHubState iohub; + + bool vfio_user; + + bool auto_shutdown; }; /* Used to pass to co-routine device and ioc. */ diff --git a/include/hw/remote/proxy.h b/include/hw/remote/proxy.h index 741def71f1..0cfd9665be 100644 --- a/include/hw/remote/proxy.h +++ b/include/hw/remote/proxy.h @@ -9,7 +9,7 @@ #ifndef PROXY_H #define PROXY_H -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "io/channel.h" #include "hw/remote/proxy-memory-listener.h" #include "qemu/event_notifier.h" diff --git a/include/hw/remote/vfio-user-obj.h b/include/hw/remote/vfio-user-obj.h new file mode 100644 index 0000000000..87ab78b875 --- /dev/null +++ b/include/hw/remote/vfio-user-obj.h @@ -0,0 +1,6 @@ +#ifndef VFIO_USER_OBJ_H +#define VFIO_USER_OBJ_H + +void vfu_object_set_bus_irq(PCIBus *pci_bus); + +#endif diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h index d2db29721a..a2e4ae9cb0 100644 --- a/include/hw/riscv/boot.h +++ b/include/hw/riscv/boot.h @@ -37,25 +37,30 @@ target_ulong riscv_find_and_load_firmware(MachineState *machine, const char *default_machine_firmware, hwaddr firmware_load_addr, symbol_fn_t sym_cb); -char *riscv_find_firmware(const char *firmware_filename); +const char *riscv_default_firmware_name(RISCVHartArrayState *harts); +char *riscv_find_firmware(const char *firmware_filename, + const char *default_machine_firmware); target_ulong riscv_load_firmware(const char *firmware_filename, hwaddr firmware_load_addr, symbol_fn_t sym_cb); -target_ulong riscv_load_kernel(const char *kernel_filename, +target_ulong riscv_load_kernel(MachineState *machine, + RISCVHartArrayState *harts, target_ulong firmware_end_addr, + bool load_initrd, symbol_fn_t sym_cb); -hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size, - uint64_t kernel_entry, hwaddr *start); -uint64_t riscv_load_fdt(hwaddr dram_start, uint64_t dram_size, void *fdt); +uint64_t riscv_compute_fdt_addr(hwaddr dram_start, uint64_t dram_size, + MachineState *ms); +void riscv_load_fdt(hwaddr fdt_addr, void *fdt); void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts, hwaddr saddr, hwaddr rom_base, hwaddr rom_size, uint64_t kernel_entry, - uint64_t fdt_load_addr, void *fdt); + uint64_t fdt_load_addr); void riscv_rom_copy_firmware_info(MachineState *machine, hwaddr rom_base, hwaddr rom_size, uint32_t reset_vec_size, uint64_t kernel_entry); void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr); +void riscv_setup_firmware_boot(MachineState *machine); #endif /* RISCV_BOOT_H */ diff --git a/include/hw/riscv/boot_opensbi.h b/include/hw/riscv/boot_opensbi.h index c19cad4818..1b749663dc 100644 --- a/include/hw/riscv/boot_opensbi.h +++ b/include/hw/riscv/boot_opensbi.h @@ -8,6 +8,8 @@ #ifndef RISCV_BOOT_OPENSBI_H #define RISCV_BOOT_OPENSBI_H +#include "exec/cpu-defs.h" + /** Expected value of info magic ('OSBI' ascii string in hex) */ #define FW_DYNAMIC_INFO_MAGIC_VALUE 0x4942534f diff --git a/include/hw/riscv/microchip_pfsoc.h b/include/hw/riscv/microchip_pfsoc.h index a0673f5f59..daef086da6 100644 --- a/include/hw/riscv/microchip_pfsoc.h +++ b/include/hw/riscv/microchip_pfsoc.h @@ -22,13 +22,16 @@ #ifndef HW_MICROCHIP_PFSOC_H #define HW_MICROCHIP_PFSOC_H +#include "hw/boards.h" #include "hw/char/mchp_pfsoc_mmuart.h" +#include "hw/cpu/cluster.h" #include "hw/dma/sifive_pdma.h" #include "hw/misc/mchp_pfsoc_dmc.h" #include "hw/misc/mchp_pfsoc_ioscb.h" #include "hw/misc/mchp_pfsoc_sysreg.h" #include "hw/net/cadence_gem.h" #include "hw/sd/cadence_sdhci.h" +#include "hw/riscv/riscv_hart.h" typedef struct MicrochipPFSoCState { /*< private >*/ @@ -88,8 +91,11 @@ enum { MICROCHIP_PFSOC_L2LIM, MICROCHIP_PFSOC_PLIC, MICROCHIP_PFSOC_MMUART0, + MICROCHIP_PFSOC_WDOG0, MICROCHIP_PFSOC_SYSREG, + MICROCHIP_PFSOC_AXISW, MICROCHIP_PFSOC_MPUCFG, + MICROCHIP_PFSOC_FMETER, MICROCHIP_PFSOC_DDR_SGMII_PHY, MICROCHIP_PFSOC_EMMC_SD, MICROCHIP_PFSOC_DDR_CFG, @@ -97,19 +103,30 @@ enum { MICROCHIP_PFSOC_MMUART2, MICROCHIP_PFSOC_MMUART3, MICROCHIP_PFSOC_MMUART4, + MICROCHIP_PFSOC_WDOG1, + MICROCHIP_PFSOC_WDOG2, + MICROCHIP_PFSOC_WDOG3, + MICROCHIP_PFSOC_WDOG4, MICROCHIP_PFSOC_SPI0, MICROCHIP_PFSOC_SPI1, + MICROCHIP_PFSOC_I2C0, MICROCHIP_PFSOC_I2C1, + MICROCHIP_PFSOC_CAN0, + MICROCHIP_PFSOC_CAN1, MICROCHIP_PFSOC_GEM0, MICROCHIP_PFSOC_GEM1, MICROCHIP_PFSOC_GPIO0, MICROCHIP_PFSOC_GPIO1, MICROCHIP_PFSOC_GPIO2, + MICROCHIP_PFSOC_RTC, MICROCHIP_PFSOC_ENVM_CFG, MICROCHIP_PFSOC_ENVM_DATA, + MICROCHIP_PFSOC_USB, MICROCHIP_PFSOC_QSPI_XIP, MICROCHIP_PFSOC_IOSCB, - MICROCHIP_PFSOC_EMMC_SD_MUX, + MICROCHIP_PFSOC_FABRIC_FIC0, + MICROCHIP_PFSOC_FABRIC_FIC1, + MICROCHIP_PFSOC_FABRIC_FIC3, MICROCHIP_PFSOC_DRAM_LO, MICROCHIP_PFSOC_DRAM_LO_ALIAS, MICROCHIP_PFSOC_DRAM_HI, @@ -133,14 +150,15 @@ enum { MICROCHIP_PFSOC_MMUART2_IRQ = 92, MICROCHIP_PFSOC_MMUART3_IRQ = 93, MICROCHIP_PFSOC_MMUART4_IRQ = 94, + MICROCHIP_PFSOC_MAILBOX_IRQ = 96, }; #define MICROCHIP_PFSOC_MANAGEMENT_CPU_COUNT 1 #define MICROCHIP_PFSOC_COMPUTE_CPU_COUNT 4 -#define MICROCHIP_PFSOC_PLIC_NUM_SOURCES 185 +#define MICROCHIP_PFSOC_PLIC_NUM_SOURCES 187 #define MICROCHIP_PFSOC_PLIC_NUM_PRIORITIES 7 -#define MICROCHIP_PFSOC_PLIC_PRIORITY_BASE 0x04 +#define MICROCHIP_PFSOC_PLIC_PRIORITY_BASE 0x00 #define MICROCHIP_PFSOC_PLIC_PENDING_BASE 0x1000 #define MICROCHIP_PFSOC_PLIC_ENABLE_BASE 0x2000 #define MICROCHIP_PFSOC_PLIC_ENABLE_STRIDE 0x80 diff --git a/include/hw/riscv/numa.h b/include/hw/riscv/numa.h index fcce942cee..8f5280211d 100644 --- a/include/hw/riscv/numa.h +++ b/include/hw/riscv/numa.h @@ -19,6 +19,7 @@ #ifndef RISCV_NUMA_H #define RISCV_NUMA_H +#include "hw/boards.h" #include "hw/sysbus.h" #include "sysemu/numa.h" @@ -89,19 +90,19 @@ bool riscv_socket_check_hartids(const MachineState *ms, int socket_id); * @ms: pointer to machine state * @socket_id: socket index * - * Write NUMA node-id FDT property for given FDT node + * Write NUMA node-id FDT property in MachineState->fdt */ -void riscv_socket_fdt_write_id(const MachineState *ms, void *fdt, - const char *node_name, int socket_id); +void riscv_socket_fdt_write_id(const MachineState *ms, const char *node_name, + int socket_id); /** * riscv_socket_fdt_write_distance_matrix: * @ms: pointer to machine state * @socket_id: socket index * - * Write NUMA distance matrix in FDT for given machine + * Write NUMA distance matrix in MachineState->fdt */ -void riscv_socket_fdt_write_distance_matrix(const MachineState *ms, void *fdt); +void riscv_socket_fdt_write_distance_matrix(const MachineState *ms); CpuInstanceProperties riscv_numa_cpu_index_to_props(MachineState *ms, unsigned cpu_index); diff --git a/include/hw/riscv/opentitan.h b/include/hw/riscv/opentitan.h index 68892cd8e5..609473d07b 100644 --- a/include/hw/riscv/opentitan.h +++ b/include/hw/riscv/opentitan.h @@ -24,6 +24,7 @@ #include "hw/char/ibex_uart.h" #include "hw/timer/ibex_timer.h" #include "hw/ssi/ibex_spi_host.h" +#include "hw/boards.h" #include "qom/object.h" #define TYPE_RISCV_IBEX_SOC "riscv.lowrisc.ibex.soc" @@ -46,14 +47,19 @@ struct LowRISCIbexSoCState { IbexTimerState timer; IbexSPIHostState spi_host[OPENTITAN_NUM_SPI_HOSTS]; + uint32_t resetvec; + MemoryRegion flash_mem; MemoryRegion rom; MemoryRegion flash_alias; }; +#define TYPE_OPENTITAN_MACHINE MACHINE_TYPE_NAME("opentitan") +OBJECT_DECLARE_SIMPLE_TYPE(OpenTitanState, OPENTITAN_MACHINE) + typedef struct OpenTitanState { /*< private >*/ - SysBusDevice parent_obj; + MachineState parent_obj; /*< public >*/ LowRISCIbexSoCState soc; @@ -74,11 +80,12 @@ enum { IBEX_DEV_TIMER, IBEX_DEV_SENSOR_CTRL, IBEX_DEV_OTP_CTRL, + IBEX_DEV_LC_CTRL, IBEX_DEV_PWRMGR, IBEX_DEV_RSTMGR, IBEX_DEV_CLKMGR, IBEX_DEV_PINMUX, - IBEX_DEV_PADCTRL, + IBEX_DEV_AON_TIMER, IBEX_DEV_USBDEV, IBEX_DEV_FLASH_CTRL, IBEX_DEV_PLIC, @@ -91,9 +98,9 @@ enum { IBEX_DEV_EDNO, IBEX_DEV_EDN1, IBEX_DEV_ALERT_HANDLER, - IBEX_DEV_NMI_GEN, + IBEX_DEV_SRAM_CTRL, IBEX_DEV_OTBN, - IBEX_DEV_PERI, + IBEX_DEV_IBEX_CFG, }; enum { @@ -105,11 +112,11 @@ enum { IBEX_UART0_RX_BREAK_ERR_IRQ = 6, IBEX_UART0_RX_TIMEOUT_IRQ = 7, IBEX_UART0_RX_PARITY_ERR_IRQ = 8, - IBEX_TIMER_TIMEREXPIRED0_0 = 126, - IBEX_SPI_HOST0_ERR_IRQ = 150, - IBEX_SPI_HOST0_SPI_EVENT_IRQ = 151, - IBEX_SPI_HOST1_ERR_IRQ = 152, - IBEX_SPI_HOST1_SPI_EVENT_IRQ = 153, + IBEX_TIMER_TIMEREXPIRED0_0 = 124, + IBEX_SPI_HOST0_ERR_IRQ = 131, + IBEX_SPI_HOST0_SPI_EVENT_IRQ = 132, + IBEX_SPI_HOST1_ERR_IRQ = 133, + IBEX_SPI_HOST1_SPI_EVENT_IRQ = 134, }; #endif diff --git a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h index bbc21cdc9a..912b4a2682 100644 --- a/include/hw/riscv/riscv_hart.h +++ b/include/hw/riscv/riscv_hart.h @@ -3,7 +3,7 @@ * * Copyright (c) 2017 SiFive, Inc. * - * Holds the state of a heterogenous array of RISC-V harts + * Holds the state of a heterogeneous array of RISC-V harts * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/include/hw/riscv/shakti_c.h b/include/hw/riscv/shakti_c.h index daf0aae13f..539fe1156d 100644 --- a/include/hw/riscv/shakti_c.h +++ b/include/hw/riscv/shakti_c.h @@ -65,7 +65,7 @@ enum { #define SHAKTI_C_PLIC_NUM_SOURCES 28 /* Excluding Priority 0 */ #define SHAKTI_C_PLIC_NUM_PRIORITIES 2 -#define SHAKTI_C_PLIC_PRIORITY_BASE 0x04 +#define SHAKTI_C_PLIC_PRIORITY_BASE 0x00 #define SHAKTI_C_PLIC_PENDING_BASE 0x1000 #define SHAKTI_C_PLIC_ENABLE_BASE 0x2000 #define SHAKTI_C_PLIC_ENABLE_STRIDE 0x80 diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h index 83604da805..31180a680e 100644 --- a/include/hw/riscv/sifive_e.h +++ b/include/hw/riscv/sifive_e.h @@ -22,6 +22,8 @@ #include "hw/riscv/riscv_hart.h" #include "hw/riscv/sifive_cpu.h" #include "hw/gpio/sifive_gpio.h" +#include "hw/misc/sifive_e_aon.h" +#include "hw/boards.h" #define TYPE_RISCV_E_SOC "riscv.sifive.e.soc" #define RISCV_E_SOC(obj) \ @@ -34,6 +36,7 @@ typedef struct SiFiveESoCState { /*< public >*/ RISCVHartArrayState cpus; DeviceState *plic; + SiFiveEAONState aon; SIFIVEGPIOState gpio; MemoryRegion xip_mem; MemoryRegion mask_rom; @@ -41,7 +44,7 @@ typedef struct SiFiveESoCState { typedef struct SiFiveEState { /*< private >*/ - SysBusDevice parent_obj; + MachineState parent_obj; /*< public >*/ SiFiveESoCState soc; @@ -75,15 +78,21 @@ enum { }; enum { - SIFIVE_E_UART0_IRQ = 3, - SIFIVE_E_UART1_IRQ = 4, - SIFIVE_E_GPIO0_IRQ0 = 8 + SIFIVE_E_AON_WDT_IRQ = 1, + SIFIVE_E_UART0_IRQ = 3, + SIFIVE_E_UART1_IRQ = 4, + SIFIVE_E_GPIO0_IRQ0 = 8 }; #define SIFIVE_E_PLIC_HART_CONFIG "M" -#define SIFIVE_E_PLIC_NUM_SOURCES 127 +/* + * Freedom E310 G002 and G003 supports 52 interrupt sources while + * Freedom E310 G000 supports 51 interrupt sources. We use the value + * of G002 and G003, so it is 53 (including interrupt source 0). + */ +#define SIFIVE_E_PLIC_NUM_SOURCES 53 #define SIFIVE_E_PLIC_NUM_PRIORITIES 7 -#define SIFIVE_E_PLIC_PRIORITY_BASE 0x04 +#define SIFIVE_E_PLIC_PRIORITY_BASE 0x00 #define SIFIVE_E_PLIC_PENDING_BASE 0x1000 #define SIFIVE_E_PLIC_ENABLE_BASE 0x2000 #define SIFIVE_E_PLIC_ENABLE_STRIDE 0x80 diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index 8f63a183c4..0696f85942 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -19,6 +19,8 @@ #ifndef HW_SIFIVE_U_H #define HW_SIFIVE_U_H +#include "hw/boards.h" +#include "hw/cpu/cluster.h" #include "hw/dma/sifive_pdma.h" #include "hw/net/cadence_gem.h" #include "hw/riscv/riscv_hart.h" @@ -66,8 +68,6 @@ typedef struct SiFiveUState { /*< public >*/ SiFiveUSoCState soc; - - void *fdt; int fdt_size; bool start_in_flash; @@ -158,7 +158,7 @@ enum { #define SIFIVE_U_PLIC_NUM_SOURCES 54 #define SIFIVE_U_PLIC_NUM_PRIORITIES 7 -#define SIFIVE_U_PLIC_PRIORITY_BASE 0x04 +#define SIFIVE_U_PLIC_PRIORITY_BASE 0x00 #define SIFIVE_U_PLIC_PENDING_BASE 0x1000 #define SIFIVE_U_PLIC_ENABLE_BASE 0x2000 #define SIFIVE_U_PLIC_ENABLE_STRIDE 0x80 diff --git a/include/hw/riscv/spike.h b/include/hw/riscv/spike.h index 73d69234de..0c2a223763 100644 --- a/include/hw/riscv/spike.h +++ b/include/hw/riscv/spike.h @@ -19,9 +19,9 @@ #ifndef HW_RISCV_SPIKE_H #define HW_RISCV_SPIKE_H +#include "hw/boards.h" #include "hw/riscv/riscv_hart.h" #include "hw/sysbus.h" -#include "qom/object.h" #define SPIKE_CPUS_MAX 8 #define SPIKE_SOCKETS_MAX 8 @@ -37,8 +37,6 @@ struct SpikeState { /*< public >*/ RISCVHartArrayState soc[SPIKE_SOCKETS_MAX]; - void *fdt; - int fdt_size; }; enum { diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 984e55c77f..3db839160f 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -19,10 +19,11 @@ #ifndef HW_RISCV_VIRT_H #define HW_RISCV_VIRT_H +#include "hw/boards.h" #include "hw/riscv/riscv_hart.h" #include "hw/sysbus.h" #include "hw/block/flash.h" -#include "qom/object.h" +#include "hw/intc/riscv_imsic.h" #define VIRT_CPUS_MAX_BITS 9 #define VIRT_CPUS_MAX (1 << VIRT_CPUS_MAX_BITS) @@ -56,6 +57,11 @@ struct RISCVVirtState { bool have_aclint; RISCVVirtAIAType aia_type; int aia_guests; + char *oem_id; + char *oem_table_id; + OnOffAuto acpi; + const MemMapEntry *memmap; + struct GPEXHost *gpex_host; }; enum { @@ -87,20 +93,18 @@ enum { VIRTIO_IRQ = 1, /* 1 to 8 */ VIRTIO_COUNT = 8, PCIE_IRQ = 0x20, /* 32 to 35 */ - VIRT_PLATFORM_BUS_IRQ = 64, /* 64 to 96 */ - VIRTIO_NDEV = 96 /* Arbitrary maximum number of interrupts */ + VIRT_PLATFORM_BUS_IRQ = 64, /* 64 to 95 */ }; #define VIRT_PLATFORM_BUS_NUM_IRQS 32 -#define VIRT_IRQCHIP_IPI_MSI 1 #define VIRT_IRQCHIP_NUM_MSIS 255 -#define VIRT_IRQCHIP_NUM_SOURCES VIRTIO_NDEV +#define VIRT_IRQCHIP_NUM_SOURCES 96 #define VIRT_IRQCHIP_NUM_PRIO_BITS 3 #define VIRT_IRQCHIP_MAX_GUESTS_BITS 3 #define VIRT_IRQCHIP_MAX_GUESTS ((1U << VIRT_IRQCHIP_MAX_GUESTS_BITS) - 1U) -#define VIRT_PLIC_PRIORITY_BASE 0x04 +#define VIRT_PLIC_PRIORITY_BASE 0x00 #define VIRT_PLIC_PENDING_BASE 0x1000 #define VIRT_PLIC_ENABLE_BASE 0x2000 #define VIRT_PLIC_ENABLE_STRIDE 0x80 @@ -111,6 +115,7 @@ enum { #define FDT_PCI_ADDR_CELLS 3 #define FDT_PCI_INT_CELLS 1 +#define FDT_PLIC_ADDR_CELLS 0 #define FDT_PLIC_INT_CELLS 1 #define FDT_APLIC_INT_CELLS 2 #define FDT_IMSIC_INT_CELLS 0 @@ -122,4 +127,30 @@ enum { #define FDT_APLIC_INT_MAP_WIDTH (FDT_PCI_ADDR_CELLS + FDT_PCI_INT_CELLS + \ 1 + FDT_APLIC_INT_CELLS) +bool virt_is_acpi_enabled(RISCVVirtState *s); +void virt_acpi_setup(RISCVVirtState *vms); +uint32_t imsic_num_bits(uint32_t count); + +/* + * The virt machine physical address space used by some of the devices + * namely ACLINT, PLIC, APLIC, and IMSIC depend on number of Sockets, + * number of CPUs, and number of IMSIC guest files. + * + * Various limits defined by VIRT_SOCKETS_MAX_BITS, VIRT_CPUS_MAX_BITS, + * and VIRT_IRQCHIP_MAX_GUESTS_BITS are tuned for maximum utilization + * of virt machine physical address space. + */ + +#define VIRT_IMSIC_GROUP_MAX_SIZE (1U << IMSIC_MMIO_GROUP_MIN_SHIFT) +#if VIRT_IMSIC_GROUP_MAX_SIZE < \ + IMSIC_GROUP_SIZE(VIRT_CPUS_MAX_BITS, VIRT_IRQCHIP_MAX_GUESTS_BITS) +#error "Can't accommodate single IMSIC group in address space" +#endif + +#define VIRT_IMSIC_MAX_SIZE (VIRT_SOCKETS_MAX * \ + VIRT_IMSIC_GROUP_MAX_SIZE) +#if 0x4000000 < VIRT_IMSIC_MAX_SIZE +#error "Can't accommodate all IMSIC groups in address space" +#endif + #endif diff --git a/include/hw/rtc/aspeed_rtc.h b/include/hw/rtc/aspeed_rtc.h index df61e46059..596dfebb46 100644 --- a/include/hw/rtc/aspeed_rtc.h +++ b/include/hw/rtc/aspeed_rtc.h @@ -18,7 +18,7 @@ struct AspeedRtcState { qemu_irq irq; uint32_t reg[0x18]; - int offset; + int64_t offset; }; diff --git a/include/hw/rtc/goldfish_rtc.h b/include/hw/rtc/goldfish_rtc.h index 79ca7daf5d..162be33863 100644 --- a/include/hw/rtc/goldfish_rtc.h +++ b/include/hw/rtc/goldfish_rtc.h @@ -42,6 +42,8 @@ struct GoldfishRTCState { uint32_t irq_pending; uint32_t irq_enabled; uint32_t time_high; + + bool big_endian; }; #endif diff --git a/include/hw/rtc/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h index 33d85753c0..97cec0b3e8 100644 --- a/include/hw/rtc/mc146818rtc.h +++ b/include/hw/rtc/mc146818rtc.h @@ -16,9 +16,9 @@ #include "qom/object.h" #define TYPE_MC146818_RTC "mc146818rtc" -OBJECT_DECLARE_SIMPLE_TYPE(RTCState, MC146818_RTC) +OBJECT_DECLARE_SIMPLE_TYPE(MC146818RtcState, MC146818_RTC) -struct RTCState { +struct MC146818RtcState { ISADevice parent_obj; MemoryRegion io; @@ -26,6 +26,7 @@ struct RTCState { uint8_t cmos_data[128]; uint8_t cmos_index; uint8_t isairq; + uint16_t io_base; int32_t base_year; uint64_t base_rtc; uint64_t last_update; @@ -45,15 +46,15 @@ struct RTCState { Notifier clock_reset_notifier; LostTickPolicy lost_tick_policy; Notifier suspend_notifier; - QLIST_ENTRY(RTCState) link; + QLIST_ENTRY(MC146818RtcState) link; }; #define RTC_ISA_IRQ 8 -#define RTC_ISA_BASE 0x70 -ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, - qemu_irq intercept_irq); -void rtc_set_memory(ISADevice *dev, int addr, int val); -int rtc_get_memory(ISADevice *dev, int addr); +MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year, + qemu_irq intercept_irq); +void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val); +int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr); +void qmp_rtc_reset_reinjection(Error **errp); #endif /* HW_RTC_MC146818RTC_H */ diff --git a/include/hw/rtc/sun4v-rtc.h b/include/hw/rtc/sun4v-rtc.h index fc54dfcba4..26a9eb6196 100644 --- a/include/hw/rtc/sun4v-rtc.h +++ b/include/hw/rtc/sun4v-rtc.h @@ -5,7 +5,7 @@ * * Copyright (c) 2016 Artyom Tarasenko * - * This code is licensed under the GNU GPL v3 or (at your option) any later + * This code is licensed under the GNU GPL v2 or (at your option) any later * version. */ diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h index 73ceeb58e5..766fe0e435 100644 --- a/include/hw/rx/rx62n.h +++ b/include/hw/rx/rx62n.h @@ -29,7 +29,6 @@ #include "hw/timer/renesas_tmr.h" #include "hw/timer/renesas_cmt.h" #include "hw/char/renesas_sci.h" -#include "qemu/units.h" #include "qom/object.h" #define TYPE_RX62N_MCU "rx62n-mcu" @@ -68,7 +67,6 @@ struct RX62NState { MemoryRegion iomem2; MemoryRegion iomem3; MemoryRegion c_flash; - qemu_irq irq[NR_IRQS]; /* Input Clock (XTAL) frequency */ uint32_t xtal_freq_hz; diff --git a/include/hw/s390x/cpu-topology.h b/include/hw/s390x/cpu-topology.h new file mode 100644 index 0000000000..c064f427e9 --- /dev/null +++ b/include/hw/s390x/cpu-topology.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * CPU Topology + * + * Copyright IBM Corp. 2022, 2023 + * Author(s): Pierre Morel + * + */ +#ifndef HW_S390X_CPU_TOPOLOGY_H +#define HW_S390X_CPU_TOPOLOGY_H + +#ifndef CONFIG_USER_ONLY + +#include "qemu/queue.h" +#include "hw/boards.h" +#include "qapi/qapi-types-machine-target.h" + +#define S390_TOPOLOGY_CPU_IFL 0x03 + +typedef struct S390TopologyId { + uint8_t sentinel; + uint8_t drawer; + uint8_t book; + uint8_t socket; + uint8_t type; + uint8_t vertical:1; + uint8_t entitlement:2; + uint8_t dedicated; + uint8_t origin; +} S390TopologyId; + +typedef struct S390TopologyEntry { + QTAILQ_ENTRY(S390TopologyEntry) next; + S390TopologyId id; + uint64_t mask; +} S390TopologyEntry; + +typedef struct S390Topology { + uint8_t *cores_per_socket; + CpuS390Polarization polarization; +} S390Topology; + +typedef QTAILQ_HEAD(, S390TopologyEntry) S390TopologyList; + +#ifdef CONFIG_KVM +bool s390_has_topology(void); +void s390_topology_setup_cpu(MachineState *ms, S390CPU *cpu, Error **errp); +void s390_topology_reset(void); +#else +static inline bool s390_has_topology(void) +{ + return false; +} +static inline void s390_topology_setup_cpu(MachineState *ms, + S390CPU *cpu, + Error **errp) {} +static inline void s390_topology_reset(void) +{ + /* Unreachable, CPU topology not implemented for TCG */ + assert(false); +} +#endif + +extern S390Topology s390_topology; + +static inline int s390_std_socket(int n, CpuTopology *smp) +{ + return (n / smp->cores) % smp->sockets; +} + +static inline int s390_std_book(int n, CpuTopology *smp) +{ + return (n / (smp->cores * smp->sockets)) % smp->books; +} + +static inline int s390_std_drawer(int n, CpuTopology *smp) +{ + return (n / (smp->cores * smp->sockets * smp->books)) % smp->drawers; +} + +#endif /* CONFIG_USER_ONLY */ + +#endif diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index 75e5381613..ba72ee3dd2 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -233,7 +233,7 @@ typedef enum { } CssIoAdapterType; void css_adapter_interrupt(CssIoAdapterType type, uint8_t isc); -int css_do_sic(CPUS390XState *env, uint8_t isc, uint16_t mode); +int css_do_sic(S390CPU *cpu, uint8_t isc, uint16_t mode); uint32_t css_get_adapter_id(CssIoAdapterType type, uint8_t isc); void css_register_io_adapters(CssIoAdapterType type, bool swap, bool maskable, uint8_t flags, Error **errp); diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h index da3cde2bb4..2c43ea123f 100644 --- a/include/hw/s390x/s390-pci-bus.h +++ b/include/hw/s390x/s390-pci-bus.h @@ -39,6 +39,9 @@ #define UID_CHECKING_ENABLED 0x01 #define ZPCI_DTSM 0x40 +/* zPCI Function Types */ +#define ZPCI_PFT_ISM 5 + OBJECT_DECLARE_SIMPLE_TYPE(S390pciState, S390_PCI_HOST_BRIDGE) OBJECT_DECLARE_SIMPLE_TYPE(S390PCIBus, S390_PCI_BUS) OBJECT_DECLARE_SIMPLE_TYPE(S390PCIBusDevice, S390_PCI_DEVICE) @@ -181,7 +184,7 @@ enum ZpciIoatDtype { * The following states make up the "configured" meta-state: * disabled: device is configured but not enabled; transition between this * state and enabled via clp enable/disable - * enbaled: device is ready for use; transition to disabled via clp disable; + * enabled: device is ready for use; transition to disabled via clp disable; * may enter an error state * blocked: ignore all DMA and interrupts; transition back to enabled or from * error state via mpcifc @@ -278,6 +281,7 @@ struct S390PCIIOMMU { uint64_t g_iota; uint64_t pba; uint64_t pal; + uint64_t max_dma_limit; GHashTable *iotlb; S390PCIDMACount *dma_limit; }; @@ -315,13 +319,16 @@ typedef struct ZpciFmb { QEMU_BUILD_BUG_MSG(offsetof(ZpciFmb, fmt0) != 48, "padding in ZpciFmb"); #define ZPCI_DEFAULT_FN_GRP 0xFF +#define ZPCI_SIM_GRP_START 0xF0 typedef struct S390PCIGroup { ClpRspQueryPciGrp zpci_group; int id; + int host_id; QTAILQ_ENTRY(S390PCIGroup) link; } S390PCIGroup; -S390PCIGroup *s390_group_create(int id); +S390PCIGroup *s390_group_create(int id, int host_id); S390PCIGroup *s390_group_find(int id); +S390PCIGroup *s390_group_find_host_sim(int host_id); struct S390PCIBusDevice { DeviceState qdev; @@ -340,6 +347,7 @@ struct S390PCIBusDevice { uint16_t noi; uint16_t maxstbl; uint8_t sum; + uint8_t pft; S390PCIGroup *pci_group; ClpRspQueryPci zpci_fn; S390MsixInfo msix; @@ -348,8 +356,12 @@ struct S390PCIBusDevice { MemoryRegion msix_notify_mr; IndAddr *summary_ind; IndAddr *indicator; + Notifier shutdown_notifier; bool pci_unplug_request_processed; bool unplug_requested; + bool interp; + bool forwarding_assist; + bool aif; QTAILQ_ENTRY(S390PCIBusDevice) link; }; @@ -368,6 +380,7 @@ struct S390pciState { QTAILQ_HEAD(, S390PCIBusDevice) zpci_devs; QTAILQ_HEAD(, S390PCIDMACount) zpci_dma_limit; QTAILQ_HEAD(, S390PCIGroup) zpci_groups; + uint8_t next_sim_grp; }; S390pciState *s390_get_phb(void); @@ -388,5 +401,6 @@ S390PCIBusDevice *s390_pci_find_dev_by_target(S390pciState *s, const char *target); S390PCIBusDevice *s390_pci_find_next_avail_dev(S390pciState *s, S390PCIBusDevice *pbdev); +void s390_pci_ism_reset(void); #endif diff --git a/include/hw/s390x/s390-pci-kvm.h b/include/hw/s390x/s390-pci-kvm.h new file mode 100644 index 0000000000..933814a402 --- /dev/null +++ b/include/hw/s390x/s390-pci-kvm.h @@ -0,0 +1,38 @@ +/* + * s390 PCI KVM interfaces + * + * Copyright 2022 IBM Corp. + * Author(s): Matthew Rosato + * + * 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. + */ + +#ifndef HW_S390_PCI_KVM_H +#define HW_S390_PCI_KVM_H + +#include "hw/s390x/s390-pci-bus.h" +#include "hw/s390x/s390-pci-inst.h" + +#ifdef CONFIG_KVM +bool s390_pci_kvm_interp_allowed(void); +int s390_pci_kvm_aif_enable(S390PCIBusDevice *pbdev, ZpciFib *fib, bool assist); +int s390_pci_kvm_aif_disable(S390PCIBusDevice *pbdev); +#else +static inline bool s390_pci_kvm_interp_allowed(void) +{ + return false; +} +static inline int s390_pci_kvm_aif_enable(S390PCIBusDevice *pbdev, ZpciFib *fib, + bool assist) +{ + return -EINVAL; +} +static inline int s390_pci_kvm_aif_disable(S390PCIBusDevice *pbdev) +{ + return -EINVAL; +} +#endif + +#endif diff --git a/include/hw/s390x/s390-pci-vfio.h b/include/hw/s390x/s390-pci-vfio.h index ff708aef50..ae1b126ff7 100644 --- a/include/hw/s390x/s390-pci-vfio.h +++ b/include/hw/s390x/s390-pci-vfio.h @@ -20,6 +20,7 @@ bool s390_pci_update_dma_avail(int fd, unsigned int *avail); S390PCIDMACount *s390_pci_start_dma_count(S390pciState *s, S390PCIBusDevice *pbdev); void s390_pci_end_dma_count(S390pciState *s, S390PCIDMACount *cnt); +bool s390_pci_get_host_fh(S390PCIBusDevice *pbdev, uint32_t *fh); void s390_pci_get_clp_info(S390PCIBusDevice *pbdev); #else static inline bool s390_pci_update_dma_avail(int fd, unsigned int *avail) @@ -33,6 +34,10 @@ static inline S390PCIDMACount *s390_pci_start_dma_count(S390pciState *s, } static inline void s390_pci_end_dma_count(S390pciState *s, S390PCIDMACount *cnt) { } +static inline bool s390_pci_get_host_fh(S390PCIBusDevice *pbdev, uint32_t *fh) +{ + return false; +} static inline void s390_pci_get_clp_info(S390PCIBusDevice *pbdev) { } #endif diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index 3331990e02..c1d46e78af 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -30,6 +30,12 @@ struct S390CcwMachineState { uint8_t loadparm[8]; }; +#define S390_PTF_REASON_NONE (0x00 << 8) +#define S390_PTF_REASON_DONE (0x01 << 8) +#define S390_PTF_REASON_BUSY (0x02 << 8) +#define S390_TOPO_FC_MASK 0xffUL +void s390_handle_ptf(S390CPU *cpu, uint8_t r1, uintptr_t ra); + struct S390CcwMachineClass { /*< private >*/ MachineClass parent_class; @@ -39,6 +45,7 @@ struct S390CcwMachineClass { bool cpu_model_allowed; bool css_migration_enabled; bool hpage_1m_allowed; + int max_threads; }; /* runtime-instrumentation allowed by the machine */ diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index d3ade40a5a..b405a387b6 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -38,10 +38,8 @@ #define MAX_STORAGE_INCREMENTS 1020 /* CPU hotplug SCLP codes */ -#define SCLP_HAS_CPU_INFO 0x0C00000000000000ULL +#define SCLP_HAS_CPU_INFO 0x0800000000000000ULL #define SCLP_CMDW_READ_CPU_INFO 0x00010001 -#define SCLP_CMDW_CONFIGURE_CPU 0x00110001 -#define SCLP_CMDW_DECONFIGURE_CPU 0x00100001 /* SCLP PCI codes */ #define SCLP_HAS_IOA_RECONFIG 0x0000000040000000ULL @@ -87,7 +85,7 @@ * - we work on a private copy of the SCCB, since there are several length * fields, that would cause a security nightmare if we allow the guest to * alter the structure while we parse it. We cannot use ldl_p and friends - * either without doing pointer arithmetics + * either without doing pointer arithmetic * So we have to double check that all users of sclp data structures use the * right endianness wrappers. */ @@ -112,11 +110,13 @@ typedef struct CPUEntry { } QEMU_PACKED CPUEntry; #define SCLP_READ_SCP_INFO_FIXED_CPU_OFFSET 128 +#define SCLP_READ_SCP_INFO_MNEST 4 typedef struct ReadInfo { SCCBHeader h; uint16_t rnmax; uint8_t rnsize; - uint8_t _reserved1[16 - 11]; /* 11-15 */ + uint8_t _reserved1[15 - 11]; /* 11-14 */ + uint8_t stsi_parm; /* 15-15 */ uint16_t entries_cpu; /* 16-17 */ uint16_t offset_cpu; /* 18-19 */ uint8_t _reserved2[24 - 20]; /* 20-23 */ @@ -225,8 +225,7 @@ static inline int sccb_data_len(SCCB *sccb) void s390_sclp_init(void); void sclp_service_interrupt(uint32_t sccb); void raise_irq_cpu_hotplug(void); -int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code); -int sclp_service_call_protected(CPUS390XState *env, uint64_t sccb, - uint32_t code); +int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code); +int sclp_service_call_protected(S390CPU *cpu, uint64_t sccb, uint32_t code); #endif diff --git a/include/hw/s390x/vfio-ccw.h b/include/hw/s390x/vfio-ccw.h index 63a909eb7e..4209d27657 100644 --- a/include/hw/s390x/vfio-ccw.h +++ b/include/hw/s390x/vfio-ccw.h @@ -22,6 +22,4 @@ #define TYPE_VFIO_CCW "vfio-ccw" OBJECT_DECLARE_SIMPLE_TYPE(VFIOCCWDevice, VFIO_CCW) -#define TYPE_VFIO_CCW "vfio-ccw" - #endif diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h index 13b17496f8..533d856aa3 100644 --- a/include/hw/scsi/esp.h +++ b/include/hw/scsi/esp.h @@ -25,7 +25,8 @@ struct ESPState { uint8_t rregs[ESP_REGS]; uint8_t wregs[ESP_REGS]; qemu_irq irq; - qemu_irq irq_data; + qemu_irq drq_irq; + bool drq_state; uint8_t chip_id; bool tchi_written; int32_t ti_size; @@ -40,8 +41,7 @@ struct ESPState { uint8_t lun; uint32_t do_cmd; - bool data_in_ready; - uint8_t ti_cmd; + bool data_ready; int dma_enabled; uint32_t async_len; @@ -51,7 +51,6 @@ struct ESPState { ESPDMAMemoryReadWriteFunc dma_memory_write; void *dma_opaque; void (*dma_cb)(ESPState *s); - uint8_t pdma_cb; uint8_t mig_version_id; @@ -63,6 +62,8 @@ struct ESPState { uint8_t mig_ti_buf[ESP_FIFO_SZ]; uint8_t mig_cmdbuf[ESP_CMDFIFO_SZ]; uint32_t mig_cmdlen; + + uint8_t mig_ti_cmd; }; #define TYPE_SYSBUS_ESP "sysbus-esp" @@ -150,15 +151,6 @@ struct SysBusESPState { #define TCHI_FAS100A 0x4 #define TCHI_AM53C974 0x12 -/* PDMA callbacks */ -enum pdma_cb { - SATN_PDMA_CB = 0, - S_WITHOUT_SATN_PDMA_CB = 1, - SATN_STOP_PDMA_CB = 2, - WRITE_RESPONSE_PDMA_CB = 3, - DO_DMA_PDMA_CB = 4 -}; - void esp_dma_enable(ESPState *s, int irq, int level); void esp_request_cancelled(SCSIRequest *req); void esp_command_complete(SCSIRequest *req, size_t resid); diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index 1ffb367f94..c3d5e17e38 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -8,7 +8,7 @@ #include "qemu/notify.h" #include "qom/object.h" -#define MAX_SCSI_DEVS 255 +#define MAX_SCSI_DEVS 255 typedef struct SCSIBus SCSIBus; typedef struct SCSIBusInfo SCSIBusInfo; @@ -59,7 +59,7 @@ struct SCSIDeviceClass { void (*realize)(SCSIDevice *dev, Error **errp); void (*unrealize)(SCSIDevice *dev); int (*parse_cdb)(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf, - void *hba_private); + size_t buf_len, void *hba_private); SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun, uint8_t *buf, void *hba_private); void (*unit_attention_reported)(SCSIDevice *s); @@ -69,14 +69,19 @@ struct SCSIDevice { DeviceState qdev; VMChangeStateEntry *vmsentry; - QEMUBH *bh; uint32_t id; BlockConf conf; SCSISense unit_attention; bool sense_is_ua; uint8_t sense[SCSI_SENSE_BUF_SIZE]; uint32_t sense_len; + + /* + * The requests list is only accessed from the AioContext that executes + * requests or from the main loop when IOThread processing is stopped. + */ QTAILQ_HEAD(, SCSIRequest) requests; + uint32_t channel; uint32_t lun; int blocksize; @@ -108,6 +113,7 @@ int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num); /* scsi-bus.c */ struct SCSIReqOps { size_t size; + void (*init_req)(SCSIRequest *req); void (*free_req)(SCSIRequest *req); int32_t (*send_command)(SCSIRequest *req, uint8_t *buf); void (*read_data)(SCSIRequest *req); @@ -122,7 +128,7 @@ struct SCSIBusInfo { int tcq; int max_channel, max_target, max_lun; int (*parse_cdb)(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf, - void *hba_private); + size_t buf_len, void *hba_private); void (*transfer_data)(SCSIRequest *req, uint32_t arg); void (*fail)(SCSIRequest *req); void (*complete)(SCSIRequest *req, size_t residual); @@ -133,6 +139,16 @@ struct SCSIBusInfo { void (*save_request)(QEMUFile *f, SCSIRequest *req); void *(*load_request)(QEMUFile *f, SCSIRequest *req); void (*free_request)(SCSIBus *bus, void *priv); + + /* + * Temporarily stop submitting new requests between drained_begin() and + * drained_end(). Called from the main loop thread with the BQL held. + * + * Implement these callbacks if request processing is triggered by a file + * descriptor like an EventNotifier. Otherwise set them to NULL. + */ + void (*drained_begin)(SCSIBus *bus); + void (*drained_end)(SCSIBus *bus); }; #define TYPE_SCSI_BUS "SCSI" @@ -144,6 +160,8 @@ struct SCSIBus { SCSISense unit_attention; const SCSIBusInfo *info; + + int drain_count; /* protected by BQL */ }; /** @@ -181,25 +199,23 @@ static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d) } SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, - int unit, bool removable, int bootindex, - bool share_rw, - BlockdevOnError rerror, - BlockdevOnError werror, + int unit, bool removable, BlockConf *conf, const char *serial, Error **errp); +void scsi_bus_set_ua(SCSIBus *bus, SCSISense sense); void scsi_bus_legacy_handle_cmdline(SCSIBus *bus); -void scsi_legacy_handle_cmdline(void); SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d, uint32_t tag, uint32_t lun, void *hba_private); SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, - uint8_t *buf, void *hba_private); + uint8_t *buf, size_t buf_len, void *hba_private); int32_t scsi_req_enqueue(SCSIRequest *req); SCSIRequest *scsi_req_ref(SCSIRequest *req); void scsi_req_unref(SCSIRequest *req); int scsi_bus_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf, - void *hba_private); -int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf); + size_t buf_len, void *hba_private); +int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf, + size_t buf_len); void scsi_req_build_sense(SCSIRequest *req, SCSISense sense); void scsi_req_print(SCSIRequest *req); void scsi_req_continue(SCSIRequest *req); @@ -212,6 +228,8 @@ void scsi_req_cancel_complete(SCSIRequest *req); void scsi_req_cancel(SCSIRequest *req); void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier); void scsi_req_retry(SCSIRequest *req); +void scsi_device_drained_begin(SCSIDevice *sdev); +void scsi_device_drained_end(SCSIDevice *sdev); void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense); void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense); void scsi_device_report_change(SCSIDevice *dev, SCSISense sense); @@ -226,4 +244,10 @@ SCSIDevice *scsi_device_get(SCSIBus *bus, int channel, int target, int lun); /* scsi-generic.c. */ extern const SCSIReqOps scsi_generic_req_ops; +/* scsi-disk.c */ +#define SCSI_DISK_QUIRK_MODE_PAGE_APPLE_VENDOR 0 +#define SCSI_DISK_QUIRK_MODE_SENSE_ROM_USE_DBD 1 +#define SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE 2 +#define SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED 3 + #endif diff --git a/include/hw/sd/allwinner-sdhost.h b/include/hw/sd/allwinner-sdhost.h index bfe08ff4ef..1b951177dd 100644 --- a/include/hw/sd/allwinner-sdhost.h +++ b/include/hw/sd/allwinner-sdhost.h @@ -38,6 +38,12 @@ /** Allwinner sun5i family and newer (A13, H2+, H3, etc) */ #define TYPE_AW_SDHOST_SUN5I TYPE_AW_SDHOST "-sun5i" +/** Allwinner sun50i-a64 */ +#define TYPE_AW_SDHOST_SUN50I_A64 TYPE_AW_SDHOST "-sun50i-a64" + +/** Allwinner sun50i-a64 emmc */ +#define TYPE_AW_SDHOST_SUN50I_A64_EMMC TYPE_AW_SDHOST "-sun50i-a64-emmc" + /** @} */ /** @@ -110,6 +116,7 @@ struct AwSdHostState { 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 sample_delay; /**< Sample delay control */ uint32_t status_crc; /**< Status CRC */ /** @} */ @@ -130,7 +137,10 @@ struct AwSdHostClass { /** Maximum buffer size in bytes per DMA descriptor */ size_t max_desc_size; + bool is_sun4i; + /** does the IP block support autocalibration? */ + bool can_calibrate; }; #endif /* HW_SD_ALLWINNER_SDHOST_H */ diff --git a/include/hw/sd/npcm7xx_sdhci.h b/include/hw/sd/npcm7xx_sdhci.h index d728f0a40d..ad8002f766 100644 --- a/include/hw/sd/npcm7xx_sdhci.h +++ b/include/hw/sd/npcm7xx_sdhci.h @@ -51,7 +51,7 @@ typedef struct NPCM7xxRegs { uint32_t boottoctrl; } NPCM7xxRegisters; -typedef struct NPCM7xxSDHCIState { +struct NPCM7xxSDHCIState { SysBusDevice parent; MemoryRegion container; @@ -60,6 +60,6 @@ typedef struct NPCM7xxSDHCIState { NPCM7xxRegisters regs; SDHCIState sdhci; -} NPCM7xxSDHCIState; +}; #endif /* NPCM7XX_SDHCI_H */ diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index 47360ba4ee..2c8748fb9b 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -77,10 +77,10 @@ typedef enum { typedef enum { sd_none = -1, - sd_bc = 0, /* broadcast -- no response */ - sd_bcr, /* broadcast with response */ - sd_ac, /* addressed -- no data transfer */ - sd_adtc, /* addressed with data transfer */ + sd_bc = 0, /* broadcast -- no response */ + sd_bcr, /* broadcast with response */ + sd_ac, /* addressed -- no data transfer */ + sd_adtc, /* addressed with data transfer */ } sd_cmd_type_t; typedef struct { @@ -93,6 +93,9 @@ typedef struct { #define TYPE_SD_CARD "sd-card" OBJECT_DECLARE_TYPE(SDState, SDCardClass, SD_CARD) +#define TYPE_SD_CARD_SPI "sd-card-spi" +DECLARE_INSTANCE_CHECKER(SDState, SD_CARD_SPI, TYPE_SD_CARD_SPI) + struct SDCardClass { /*< private >*/ DeviceClass parent_class; @@ -124,6 +127,8 @@ struct SDCardClass { void (*enable)(SDState *sd, bool enable); bool (*get_inserted)(SDState *sd); bool (*get_readonly)(SDState *sd); + + const struct SDProto *proto; }; #define TYPE_SD_BUS "sd-bus" diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h index 01a64c5442..6cd2822f1d 100644 --- a/include/hw/sd/sdhci.h +++ b/include/hw/sd/sdhci.h @@ -25,7 +25,7 @@ #ifndef SDHCI_H #define SDHCI_H -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/sysbus.h" #include "hw/sd/sd.h" #include "qom/object.h" @@ -96,6 +96,7 @@ struct SDHCIState { /* Configurable properties */ bool pending_insert_quirk; /* Quirk for Raspberry Pi card insert int */ uint32_t quirks; + uint8_t endianness; uint8_t sd_spec_version; uint8_t uhs_mode; uint8_t vendor; /* For vendor specific functionality */ diff --git a/include/hw/sensor/isl_pmbus_vr.h b/include/hw/sensor/isl_pmbus_vr.h index 3e47ff7e48..aa2c2767df 100644 --- a/include/hw/sensor/isl_pmbus_vr.h +++ b/include/hw/sensor/isl_pmbus_vr.h @@ -12,12 +12,17 @@ #include "hw/i2c/pmbus_device.h" #include "qom/object.h" +#define TYPE_ISL69259 "isl69259" #define TYPE_ISL69260 "isl69260" #define TYPE_RAA228000 "raa228000" #define TYPE_RAA229004 "raa229004" +#define ISL_MAX_IC_DEVICE_ID_LEN 16 struct ISLState { PMBusDevice parent; + + uint8_t ic_device_id[ISL_MAX_IC_DEVICE_ID_LEN]; + uint8_t ic_device_id_len; }; OBJECT_DECLARE_SIMPLE_TYPE(ISLState, ISL69260) diff --git a/include/hw/i386/ich9.h b/include/hw/southbridge/ich9.h similarity index 90% rename from include/hw/i386/ich9.h rename to include/hw/southbridge/ich9.h index 23ee8e371b..fd01649d04 100644 --- a/include/hw/i386/ich9.h +++ b/include/hw/southbridge/ich9.h @@ -1,24 +1,16 @@ -#ifndef HW_ICH9_H -#define HW_ICH9_H +#ifndef HW_SOUTHBRIDGE_ICH9_H +#define HW_SOUTHBRIDGE_ICH9_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/pci/pci_bridge.h" -#include "hw/acpi/acpi.h" #include "hw/acpi/ich9.h" -#include "hw/pci/pci_bus.h" +#include "hw/intc/ioapic.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" +#include "hw/rtc/mc146818rtc.h" +#include "exec/memory.h" +#include "qemu/notify.h" #include "qom/object.h" -void ich9_lpc_set_irq(void *opaque, int irq_num, int level); -int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx); -PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin); -void ich9_lpc_pm_init(PCIDevice *pci_lpc, bool smm_enabled); -I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base); - void ich9_generate_smi(void); #define ICH9_CC_SIZE (16 * 1024) /* 16KB. Chipset configuration registers */ @@ -39,6 +31,7 @@ struct ICH9LPCState { */ uint8_t irr[PCI_SLOT_MAX][PCI_NUM_PINS]; + MC146818RtcState rtc; APMState apm; ICH9LPCPMRegs pm; uint32_t sci_level; /* track sci level */ @@ -71,15 +64,13 @@ struct ICH9LPCState { * triggers feature lockdown */ uint64_t smi_negotiated_features; /* guest-invisible, host endian */ - /* isa bus */ - ISABus *isa_bus; MemoryRegion rcrb_mem; /* root complex register block */ Notifier machine_ready; - qemu_irq gsi[GSI_NUM_PINS]; + qemu_irq gsi[IOAPIC_NUM_PINS]; }; -#define Q35_MASK(bit, ms_bit, ls_bit) \ +#define ICH9_MASK(bit, ms_bit, ls_bit) \ ((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL << ls_bit) - 1))) /* ICH9: Chipset Configuration Registers */ @@ -141,13 +132,13 @@ struct ICH9LPCState { #define ICH9_LPC_NB_PIRQS 8 /* PCI A-H */ #define ICH9_LPC_PMBASE 0x40 -#define ICH9_LPC_PMBASE_BASE_ADDRESS_MASK Q35_MASK(32, 15, 7) +#define ICH9_LPC_PMBASE_BASE_ADDRESS_MASK ICH9_MASK(32, 15, 7) #define ICH9_LPC_PMBASE_RTE 0x1 #define ICH9_LPC_PMBASE_DEFAULT 0x1 #define ICH9_LPC_ACPI_CTRL 0x44 #define ICH9_LPC_ACPI_CTRL_ACPI_EN 0x80 -#define ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK Q35_MASK(8, 2, 0) +#define ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK ICH9_MASK(8, 2, 0) #define ICH9_LPC_ACPI_CTRL_9 0x0 #define ICH9_LPC_ACPI_CTRL_10 0x1 #define ICH9_LPC_ACPI_CTRL_11 0x2 @@ -166,7 +157,7 @@ struct ICH9LPCState { #define ICH9_LPC_PIRQH_ROUT 0x6b #define ICH9_LPC_PIRQ_ROUT_IRQEN 0x80 -#define ICH9_LPC_PIRQ_ROUT_MASK Q35_MASK(8, 3, 0) +#define ICH9_LPC_PIRQ_ROUT_MASK ICH9_MASK(8, 3, 0) #define ICH9_LPC_PIRQ_ROUT_DEFAULT 0x80 #define ICH9_LPC_GEN_PMCON_1 0xa0 @@ -176,7 +167,7 @@ struct ICH9LPCState { #define ICH9_LPC_GEN_PMCON_LOCK 0xa6 #define ICH9_LPC_RCBA 0xf0 -#define ICH9_LPC_RCBA_BA_MASK Q35_MASK(32, 31, 14) +#define ICH9_LPC_RCBA_BA_MASK ICH9_MASK(32, 31, 14) #define ICH9_LPC_RCBA_EN 0x1 #define ICH9_LPC_RCBA_DEFAULT 0x0 @@ -253,4 +244,4 @@ struct ICH9LPCState { #define ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT 1 #define ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT 2 -#endif /* HW_ICH9_H */ +#endif /* HW_SOUTHBRIDGE_ICH9_H */ diff --git a/include/hw/southbridge/piix.h b/include/hw/southbridge/piix.h index f63f83e5c6..86709ba2e4 100644 --- a/include/hw/southbridge/piix.h +++ b/include/hw/southbridge/piix.h @@ -12,14 +12,11 @@ #ifndef HW_SOUTHBRIDGE_PIIX_H #define HW_SOUTHBRIDGE_PIIX_H -#include "hw/pci/pci.h" -#include "qom/object.h" - -#define TYPE_PIIX4_PM "PIIX4_PM" - -I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, - qemu_irq sci_irq, qemu_irq smi_irq, - int smm_enabled, DeviceState **piix4_pm); +#include "hw/pci/pci_device.h" +#include "hw/acpi/piix4.h" +#include "hw/ide/pci.h" +#include "hw/rtc/mc146818rtc.h" +#include "hw/usb/hcd-uhci.h" /* PIRQRC[A:D]: PIRQx Route Control Registers */ #define PIIX_PIRQCA 0x60 @@ -33,7 +30,6 @@ I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, */ #define PIIX_RCR_IOPORT 0xcf9 -#define PIIX_NUM_PIC_IRQS 16 /* i8259 * 2 */ #define PIIX_NUM_PIRQS 4ULL /* PIRQ[A-D] */ struct PIIXState { @@ -45,33 +41,44 @@ struct PIIXState { * So one PIC level is tracked by PIIX_NUM_PIRQS bits. * * PIRQ is mapped to PIC pins, we track it by - * PIIX_NUM_PIRQS * PIIX_NUM_PIC_IRQS = 64 bits with + * PIIX_NUM_PIRQS * ISA_NUM_IRQS = 64 bits with * pic_irq * PIIX_NUM_PIRQS + pirq */ -#if PIIX_NUM_PIC_IRQS * PIIX_NUM_PIRQS > 64 +#if ISA_NUM_IRQS * PIIX_NUM_PIRQS > 64 #error "unable to encode pic state in 64bit in pic_levels." #endif uint64_t pic_levels; - qemu_irq *pic; + qemu_irq cpu_intr; + qemu_irq isa_irqs_in[ISA_NUM_IRQS]; /* This member isn't used. Just for save/load compatibility */ int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS]; + MC146818RtcState rtc; + PCIIDEState ide; + UHCIState uhci; + PIIX4PMState pm; + + uint32_t smb_io_base; + /* Reset Control Register contents */ uint8_t rcr; /* IO memory region for Reset Control Register (PIIX_RCR_IOPORT) */ MemoryRegion rcr_mem; + + bool has_acpi; + bool has_pic; + bool has_pit; + bool has_usb; + bool smm_enabled; }; -typedef struct PIIXState PIIX3State; -#define TYPE_PIIX3_PCI_DEVICE "pci-piix3" -DECLARE_INSTANCE_CHECKER(PIIX3State, PIIX3_PCI_DEVICE, - TYPE_PIIX3_PCI_DEVICE) +#define TYPE_PIIX_PCI_DEVICE "pci-piix" +OBJECT_DECLARE_SIMPLE_TYPE(PIIXState, PIIX_PCI_DEVICE) -PIIX3State *piix3_create(PCIBus *pci_bus, ISABus **isa_bus); - -DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus); +#define TYPE_PIIX3_DEVICE "PIIX3" +#define TYPE_PIIX4_PCI_DEVICE "piix4-isa" #endif diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index 2d5f8f3d8f..8e1dda556b 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -30,6 +30,7 @@ #include "qom/object.h" struct AspeedSMCState; +struct AspeedSMCClass; #define TYPE_ASPEED_SMC_FLASH "aspeed.smc.flash" OBJECT_DECLARE_SIMPLE_TYPE(AspeedSMCFlash, ASPEED_SMC_FLASH) @@ -37,6 +38,7 @@ struct AspeedSMCFlash { SysBusDevice parent_obj; struct AspeedSMCState *controller; + struct AspeedSMCClass *asc; uint8_t cs; MemoryRegion mmio; diff --git a/include/hw/ssi/bcm2835_spi.h b/include/hw/ssi/bcm2835_spi.h new file mode 100644 index 0000000000..d3f8cec111 --- /dev/null +++ b/include/hw/ssi/bcm2835_spi.h @@ -0,0 +1,81 @@ +/* + * BCM2835 SPI Master Controller + * + * Copyright (c) 2024 Rayhan Faizel + * + * 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 "hw/sysbus.h" +#include "hw/ssi/ssi.h" +#include "qom/object.h" +#include "qemu/fifo8.h" + +#define TYPE_BCM2835_SPI "bcm2835-spi" +OBJECT_DECLARE_SIMPLE_TYPE(BCM2835SPIState, BCM2835_SPI) + +/* + * Though BCM2835 documentation says FIFOs have a capacity of 16, + * FIFOs are actually 16 words in size or effectively 64 bytes when operating + * in non DMA mode. + */ +#define FIFO_SIZE 64 +#define FIFO_SIZE_3_4 48 + +#define RO_MASK 0x1f0000 + +#define BCM2835_SPI_CS 0x00 +#define BCM2835_SPI_FIFO 0x04 +#define BCM2835_SPI_CLK 0x08 +#define BCM2835_SPI_DLEN 0x0c +#define BCM2835_SPI_LTOH 0x10 +#define BCM2835_SPI_DC 0x14 + +#define BCM2835_SPI_CS_RXF BIT(20) +#define BCM2835_SPI_CS_RXR BIT(19) +#define BCM2835_SPI_CS_TXD BIT(18) +#define BCM2835_SPI_CS_RXD BIT(17) +#define BCM2835_SPI_CS_DONE BIT(16) +#define BCM2835_SPI_CS_LEN BIT(13) +#define BCM2835_SPI_CS_REN BIT(12) +#define BCM2835_SPI_CS_INTR BIT(10) +#define BCM2835_SPI_CS_INTD BIT(9) +#define BCM2835_SPI_CS_DMAEN BIT(8) +#define BCM2835_SPI_CS_TA BIT(7) +#define BCM2835_SPI_CLEAR_RX BIT(5) +#define BCM2835_SPI_CLEAR_TX BIT(4) + +struct BCM2835SPIState { + /* */ + SysBusDevice parent_obj; + + /* */ + SSIBus *bus; + MemoryRegion iomem; + qemu_irq irq; + + uint32_t cs; + uint32_t clk; + uint32_t dlen; + uint32_t ltoh; + uint32_t dc; + + Fifo8 tx_fifo; + Fifo8 rx_fifo; +}; diff --git a/include/hw/ssi/ibex_spi_host.h b/include/hw/ssi/ibex_spi_host.h index 3fedcb6805..5bd5557b9a 100644 --- a/include/hw/ssi/ibex_spi_host.h +++ b/include/hw/ssi/ibex_spi_host.h @@ -28,11 +28,9 @@ #define IBEX_SPI_HOST_H #include "hw/sysbus.h" -#include "hw/hw.h" #include "hw/ssi/ssi.h" #include "qemu/fifo8.h" #include "qom/object.h" -#include "hw/registerfields.h" #include "qemu/timer.h" #define TYPE_IBEX_SPI_HOST "ibex-spi" @@ -40,7 +38,7 @@ OBJECT_CHECK(IbexSPIHostState, (obj), TYPE_IBEX_SPI_HOST) /* SPI Registers */ -#define IBEX_SPI_HOST_INTR_STATE (0x00 / 4) /* rw */ +#define IBEX_SPI_HOST_INTR_STATE (0x00 / 4) /* rw1c */ #define IBEX_SPI_HOST_INTR_ENABLE (0x04 / 4) /* rw */ #define IBEX_SPI_HOST_INTR_TEST (0x08 / 4) /* wo */ #define IBEX_SPI_HOST_ALERT_TEST (0x0c / 4) /* wo */ @@ -54,7 +52,7 @@ #define IBEX_SPI_HOST_TXDATA (0x28 / 4) #define IBEX_SPI_HOST_ERROR_ENABLE (0x2c / 4) /* rw */ -#define IBEX_SPI_HOST_ERROR_STATUS (0x30 / 4) /* rw */ +#define IBEX_SPI_HOST_ERROR_STATUS (0x30 / 4) /* rw1c */ #define IBEX_SPI_HOST_EVENT_ENABLE (0x34 / 4) /* rw */ /* FIFO Len in Bytes */ diff --git a/include/hw/ssi/npcm_pspi.h b/include/hw/ssi/npcm_pspi.h new file mode 100644 index 0000000000..37cc784d96 --- /dev/null +++ b/include/hw/ssi/npcm_pspi.h @@ -0,0 +1,53 @@ +/* + * Nuvoton Peripheral SPI Module + * + * Copyright 2023 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef NPCM_PSPI_H +#define NPCM_PSPI_H + +#include "hw/ssi/ssi.h" +#include "hw/sysbus.h" + +/* + * Number of registers in our device state structure. Don't change this without + * incrementing the version_id in the vmstate. + */ +#define NPCM_PSPI_NR_REGS 3 + +/** + * NPCMPSPIState - Device state for one Flash Interface Unit. + * @parent: System bus device. + * @mmio: Memory region for register access. + * @spi: The SPI bus mastered by this controller. + * @regs: Register contents. + * @irq: The interrupt request queue for this module. + * + * Each PSPI has a shared bank of registers, and controls up to four chip + * selects. Each chip select has a dedicated memory region which may be used to + * read and write the flash connected to that chip select as if it were memory. + */ +typedef struct NPCMPSPIState { + SysBusDevice parent; + + MemoryRegion mmio; + + SSIBus *spi; + uint16_t regs[NPCM_PSPI_NR_REGS]; + qemu_irq irq; +} NPCMPSPIState; + +#define TYPE_NPCM_PSPI "npcm-pspi" +OBJECT_DECLARE_SIMPLE_TYPE(NPCMPSPIState, NPCM_PSPI) + +#endif /* NPCM_PSPI_H */ diff --git a/include/hw/ssi/sifive_spi.h b/include/hw/ssi/sifive_spi.h index 47d0d6a47c..d0c40cdb11 100644 --- a/include/hw/ssi/sifive_spi.h +++ b/include/hw/ssi/sifive_spi.h @@ -22,6 +22,9 @@ #ifndef HW_SIFIVE_SPI_H #define HW_SIFIVE_SPI_H +#include "qemu/fifo8.h" +#include "hw/sysbus.h" + #define SIFIVE_SPI_REG_NUM (0x78 / 4) #define TYPE_SIFIVE_SPI "sifive.spi" diff --git a/include/hw/ssi/ssi.h b/include/hw/ssi/ssi.h index f411858ab0..3cdcbd5390 100644 --- a/include/hw/ssi/ssi.h +++ b/include/hw/ssi/ssi.h @@ -59,8 +59,14 @@ struct SSIPeripheralClass { struct SSIPeripheral { DeviceState parent_obj; + /* cache the class */ + SSIPeripheralClass *spc; + /* Chip select state */ bool cs; + + /* Chip select index */ + uint8_t cs_index; }; extern const VMStateDescription vmstate_ssi_peripheral; @@ -106,4 +112,6 @@ SSIBus *ssi_create_bus(DeviceState *parent, const char *name); uint32_t ssi_transfer(SSIBus *bus, uint32_t val); +DeviceState *ssi_get_cs(SSIBus *bus, uint8_t cs_index); + #endif diff --git a/include/hw/ssi/xilinx_spips.h b/include/hw/ssi/xilinx_spips.h index 06bfd18312..7a754bf67a 100644 --- a/include/hw/ssi/xilinx_spips.h +++ b/include/hw/ssi/xilinx_spips.h @@ -33,7 +33,9 @@ typedef struct XilinxSPIPS XilinxSPIPS; +/* For SPIPS, QSPIPS. */ #define XLNX_SPIPS_R_MAX (0x100 / 4) +/* For ZYNQMP_QSPIPS. */ #define XLNX_ZYNQMP_SPIPS_R_MAX (0x200 / 4) /* Bite off 4k chunks at a time */ @@ -104,7 +106,7 @@ struct XlnxZynqMPQSPIPS { uint32_t regs[XLNX_ZYNQMP_SPIPS_R_MAX]; - /* GQSPI has seperate tx/rx fifos */ + /* GQSPI has separate tx/rx fifos */ Fifo8 rx_fifo_g; Fifo8 tx_fifo_g; Fifo32 fifo_g; @@ -125,6 +127,7 @@ struct XilinxSPIPSClass { SysBusDeviceClass parent_class; const MemoryRegionOps *reg_ops; + uint64_t reg_size; uint32_t rx_fifo_size; uint32_t tx_fifo_size; diff --git a/include/hw/ssi/xlnx-versal-ospi.h b/include/hw/ssi/xlnx-versal-ospi.h index 5d131d351d..4ac975aa2f 100644 --- a/include/hw/ssi/xlnx-versal-ospi.h +++ b/include/hw/ssi/xlnx-versal-ospi.h @@ -34,7 +34,7 @@ * https://www.xilinx.com/support/documentation/architecture-manuals/am011-versal-acap-trm.pdf * * [2] Versal ACAP Register Reference, - * https://www.xilinx.com/html_docs/registers/am012/am012-versal-register-reference.html#mod___ospi.html + * https://docs.xilinx.com/r/en-US/am012-versal-register-reference/OSPI-Module * * * QEMU interface: diff --git a/include/hw/sysbus.h b/include/hw/sysbus.h index 3564b7b6a2..3cb29a480e 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -83,9 +83,6 @@ void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr); void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr, int priority); void sysbus_mmio_unmap(SysBusDevice *dev, int n); -void sysbus_add_io(SysBusDevice *dev, hwaddr addr, - MemoryRegion *mem); -MemoryRegion *sysbus_address_space(SysBusDevice *dev); bool sysbus_realize(SysBusDevice *dev, Error **errp); bool sysbus_realize_and_unref(SysBusDevice *dev, Error **errp); diff --git a/include/hw/timer/cmsdk-apb-timer.h b/include/hw/timer/cmsdk-apb-timer.h index c4c7eae849..2dd615d1be 100644 --- a/include/hw/timer/cmsdk-apb-timer.h +++ b/include/hw/timer/cmsdk-apb-timer.h @@ -12,7 +12,6 @@ #ifndef CMSDK_APB_TIMER_H #define CMSDK_APB_TIMER_H -#include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "hw/ptimer.h" #include "hw/clock.h" diff --git a/include/qemu/qemu-options.h b/include/hw/timer/grlib_gptimer.h similarity index 73% rename from include/qemu/qemu-options.h rename to include/hw/timer/grlib_gptimer.h index 4a62c83c45..e56f1b8bf3 100644 --- a/include/qemu/qemu-options.h +++ b/include/hw/timer/grlib_gptimer.h @@ -1,10 +1,9 @@ /* - * qemu-options.h + * QEMU GRLIB GPTimer * - * Defines needed for command line argument processing. + * SPDX-License-Identifier: MIT * - * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2010 Jes Sorensen + * Copyright (c) 2024 AdaCore * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,17 +24,9 @@ * THE SOFTWARE. */ -#ifndef QEMU_OPTIONS_H -#define QEMU_OPTIONS_H +#ifndef GRLIB_GPTIMER_H +#define GRLIB_GPTIMER_H -enum { - -#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \ - opt_enum, -#define DEFHEADING(text) -#define ARCHHEADING(text, arch_mask) - -#include "qemu-options.def" -}; +#define TYPE_GRLIB_GPTIMER "grlib-gptimer" #endif diff --git a/include/hw/timer/hpet.h b/include/hw/timer/hpet.h index f04c4d3238..d17a8d4319 100644 --- a/include/hw/timer/hpet.h +++ b/include/hw/timer/hpet.h @@ -78,6 +78,8 @@ extern struct hpet_fw_config hpet_cfg; #define TYPE_HPET "hpet" +#define HPET_INTCAP "hpet-intcap" + static inline bool hpet_find(void) { return object_resolve_path_type("", TYPE_HPET, NULL); diff --git a/include/hw/timer/i8254.h b/include/hw/timer/i8254.h index 3e569f42b6..8402caad30 100644 --- a/include/hw/timer/i8254.h +++ b/include/hw/timer/i8254.h @@ -56,7 +56,8 @@ static inline ISADevice *i8254_pit_init(ISABus *bus, int base, int isa_irq, qdev_prop_set_uint32(dev, "iobase", base); isa_realize_and_unref(d, bus, &error_fatal); qdev_connect_gpio_out(dev, 0, - isa_irq >= 0 ? isa_get_irq(d, isa_irq) : alt_irq); + isa_irq >= 0 ? isa_bus_get_irq(bus, isa_irq) + : alt_irq); return d; } diff --git a/include/hw/timer/i8254_internal.h b/include/hw/timer/i8254_internal.h index a9a600d941..1761deb4cf 100644 --- a/include/hw/timer/i8254_internal.h +++ b/include/hw/timer/i8254_internal.h @@ -58,7 +58,7 @@ struct PITCommonState { }; struct PITCommonClass { - ISADeviceClass parent_class; + DeviceClass parent_class; void (*set_channel_gate)(PITCommonState *s, PITChannelState *sc, int val); void (*get_channel_info)(PITCommonState *s, PITChannelState *sc, diff --git a/include/hw/timer/ibex_timer.h b/include/hw/timer/ibex_timer.h index 1a0a28d5fa..41f5c82a92 100644 --- a/include/hw/timer/ibex_timer.h +++ b/include/hw/timer/ibex_timer.h @@ -33,6 +33,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(IbexTimerState, IBEX_TIMER) struct IbexTimerState { /* */ SysBusDevice parent_obj; + uint64_t mtimecmp; + QEMUTimer *mtimer; /* Internal timer for M-mode interrupt */ /* */ MemoryRegion mmio; diff --git a/include/hw/timer/imx_epit.h b/include/hw/timer/imx_epit.h index 2acc41e982..79aff0cec2 100644 --- a/include/hw/timer/imx_epit.h +++ b/include/hw/timer/imx_epit.h @@ -43,7 +43,7 @@ #define CR_OCIEN (1 << 2) #define CR_RLD (1 << 3) #define CR_PRESCALE_SHIFT (4) -#define CR_PRESCALE_MASK (0xfff) +#define CR_PRESCALE_BITS (12) #define CR_SWR (1 << 16) #define CR_IOVW (1 << 17) #define CR_DBGEN (1 << 18) @@ -51,7 +51,9 @@ #define CR_DOZEN (1 << 20) #define CR_STOPEN (1 << 21) #define CR_CLKSRC_SHIFT (24) -#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT) +#define CR_CLKSRC_BITS (2) + +#define SR_OCIF (1 << 0) #define EPIT_TIMER_MAX 0XFFFFFFFFUL @@ -72,9 +74,7 @@ struct IMXEPITState { uint32_t sr; uint32_t lr; uint32_t cmp; - uint32_t cnt; - uint32_t freq; qemu_irq irq; }; diff --git a/include/hw/timer/imx_gpt.h b/include/hw/timer/imx_gpt.h index ff5c8a351a..5a1230da35 100644 --- a/include/hw/timer/imx_gpt.h +++ b/include/hw/timer/imx_gpt.h @@ -78,6 +78,7 @@ #define TYPE_IMX25_GPT "imx25.gpt" #define TYPE_IMX31_GPT "imx31.gpt" #define TYPE_IMX6_GPT "imx6.gpt" +#define TYPE_IMX6UL_GPT "imx6ul.gpt" #define TYPE_IMX7_GPT "imx7.gpt" #define TYPE_IMX_GPT TYPE_IMX25_GPT diff --git a/include/hw/timer/sse-timer.h b/include/hw/timer/sse-timer.h index b4ee8e7f6c..265ad32400 100644 --- a/include/hw/timer/sse-timer.h +++ b/include/hw/timer/sse-timer.h @@ -25,6 +25,7 @@ #define SSE_TIMER_H #include "hw/sysbus.h" +#include "qemu/timer.h" #include "qom/object.h" #include "hw/timer/sse-counter.h" diff --git a/include/hw/tricore/triboard.h b/include/hw/tricore/triboard.h index 094c8bd563..4fdd2d7d97 100644 --- a/include/hw/tricore/triboard.h +++ b/include/hw/tricore/triboard.h @@ -18,7 +18,6 @@ * License along with this library; if not, see . */ -#include "qemu/osdep.h" #include "qapi/error.h" #include "hw/boards.h" #include "sysemu/sysemu.h" diff --git a/include/hw/tricore/tricore_testdevice.h b/include/hw/tricore/tricore_testdevice.h index 1e2b8942ac..2c57b62f22 100644 --- a/include/hw/tricore/tricore_testdevice.h +++ b/include/hw/tricore/tricore_testdevice.h @@ -19,19 +19,15 @@ #define HW_TRICORE_TESTDEVICE_H #include "hw/sysbus.h" -#include "hw/hw.h" #define TYPE_TRICORE_TESTDEVICE "tricore_testdevice" #define TRICORE_TESTDEVICE(obj) \ OBJECT_CHECK(TriCoreTestDeviceState, (obj), TYPE_TRICORE_TESTDEVICE) typedef struct { - /* */ SysBusDevice parent_obj; - /* */ MemoryRegion iomem; - } TriCoreTestDeviceState; #endif diff --git a/include/hw/usb.h b/include/hw/usb.h index 33668dd0a9..d46d96779a 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -30,6 +30,7 @@ #include "qemu/iov.h" #include "qemu/queue.h" #include "qom/object.h" +#include "qapi/error.h" /* Constants related to the USB / PCI interaction */ #define USB_SBRN 0x60 /* Serial Bus Release Number Register */ @@ -66,42 +67,42 @@ //#define USB_STATE_POWERED 2 #define USB_STATE_DEFAULT 3 //#define USB_STATE_ADDRESS 4 -//#define USB_STATE_CONFIGURED 5 +//#define USB_STATE_CONFIGURED 5 #define USB_STATE_SUSPENDED 6 -#define USB_CLASS_AUDIO 1 -#define USB_CLASS_COMM 2 -#define USB_CLASS_HID 3 -#define USB_CLASS_PHYSICAL 5 -#define USB_CLASS_STILL_IMAGE 6 -#define USB_CLASS_PRINTER 7 -#define USB_CLASS_MASS_STORAGE 8 -#define USB_CLASS_HUB 9 -#define USB_CLASS_CDC_DATA 0x0a -#define USB_CLASS_CSCID 0x0b -#define USB_CLASS_CONTENT_SEC 0x0d -#define USB_CLASS_APP_SPEC 0xfe -#define USB_CLASS_VENDOR_SPEC 0xff +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_STILL_IMAGE 6 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_CDC_DATA 0x0a +#define USB_CLASS_CSCID 0x0b +#define USB_CLASS_CONTENT_SEC 0x0d +#define USB_CLASS_APP_SPEC 0xfe +#define USB_CLASS_VENDOR_SPEC 0xff #define USB_SUBCLASS_UNDEFINED 0 #define USB_SUBCLASS_AUDIO_CONTROL 1 #define USB_SUBCLASS_AUDIO_STREAMING 2 #define USB_SUBCLASS_AUDIO_MIDISTREAMING 3 -#define USB_DIR_OUT 0 -#define USB_DIR_IN 0x80 +#define USB_DIR_OUT 0 +#define USB_DIR_IN 0x80 -#define USB_TYPE_MASK (0x03 << 5) -#define USB_TYPE_STANDARD (0x00 << 5) -#define USB_TYPE_CLASS (0x01 << 5) -#define USB_TYPE_VENDOR (0x02 << 5) -#define USB_TYPE_RESERVED (0x03 << 5) +#define USB_TYPE_MASK (0x03 << 5) +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) -#define USB_RECIP_MASK 0x1f -#define USB_RECIP_DEVICE 0x00 -#define USB_RECIP_INTERFACE 0x01 -#define USB_RECIP_ENDPOINT 0x02 -#define USB_RECIP_OTHER 0x03 +#define USB_RECIP_MASK 0x1f +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 #define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) #define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) @@ -126,28 +127,28 @@ #define EndpointOutRequest \ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) -#define USB_REQ_GET_STATUS 0x00 -#define USB_REQ_CLEAR_FEATURE 0x01 -#define USB_REQ_SET_FEATURE 0x03 -#define USB_REQ_SET_ADDRESS 0x05 -#define USB_REQ_GET_DESCRIPTOR 0x06 -#define USB_REQ_SET_DESCRIPTOR 0x07 -#define USB_REQ_GET_CONFIGURATION 0x08 -#define USB_REQ_SET_CONFIGURATION 0x09 -#define USB_REQ_GET_INTERFACE 0x0A -#define USB_REQ_SET_INTERFACE 0x0B -#define USB_REQ_SYNCH_FRAME 0x0C +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C #define USB_REQ_SET_SEL 0x30 #define USB_REQ_SET_ISOCH_DELAY 0x31 -#define USB_DEVICE_SELF_POWERED 0 -#define USB_DEVICE_REMOTE_WAKEUP 1 +#define USB_DEVICE_SELF_POWERED 0 +#define USB_DEVICE_REMOTE_WAKEUP 1 -#define USB_DT_DEVICE 0x01 -#define USB_DT_CONFIG 0x02 -#define USB_DT_STRING 0x03 -#define USB_DT_INTERFACE 0x04 -#define USB_DT_ENDPOINT 0x05 +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 #define USB_DT_DEVICE_QUALIFIER 0x06 #define USB_DT_OTHER_SPEED_CONFIG 0x07 #define USB_DT_DEBUG 0x0A @@ -167,10 +168,10 @@ #define USB_CFG_ATT_WAKEUP (1 << 5) #define USB_CFG_ATT_BATTERY (1 << 4) -#define USB_ENDPOINT_XFER_CONTROL 0 -#define USB_ENDPOINT_XFER_ISOC 1 -#define USB_ENDPOINT_XFER_BULK 2 -#define USB_ENDPOINT_XFER_INT 3 +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 #define USB_ENDPOINT_XFER_INVALID 255 #define USB_INTERFACE_INVALID 255 @@ -497,12 +498,8 @@ struct USBBusOps { void usb_bus_new(USBBus *bus, size_t bus_size, USBBusOps *ops, DeviceState *host); void usb_bus_release(USBBus *bus); -USBBus *usb_bus_find(int busnr); void usb_legacy_register(const char *typename, const char *usbdevice_name, USBDevice *(*usbdevice_init)(void)); -USBDevice *usb_new(const char *name); -bool usb_realize_and_unref(USBDevice *dev, USBBus *bus, Error **errp); -USBDevice *usb_create_simple(USBBus *bus, const char *name); USBDevice *usbdevice_create(const char *cmdline); void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, USBPortOps *ops, int speedmask); @@ -569,9 +566,9 @@ static inline bool usb_device_is_scsi_storage(USBDevice *dev) /* quirks.c */ /* In bulk endpoints are streaming data sources (iow behave like isoc eps) */ -#define USB_QUIRK_BUFFER_BULK_IN 0x01 +#define USB_QUIRK_BUFFER_BULK_IN 0x01 /* Bulk pkts in FTDI format, need special handling when combining packets */ -#define USB_QUIRK_IS_FTDI 0x02 +#define USB_QUIRK_IS_FTDI 0x02 int usb_get_quirks(uint16_t vendor_id, uint16_t product_id, uint8_t interface_class, uint8_t interface_subclass, @@ -582,4 +579,27 @@ void usb_pcap_init(FILE *fp); void usb_pcap_ctrl(USBPacket *p, bool setup); void usb_pcap_data(USBPacket *p, bool setup); +static inline USBDevice *usb_new(const char *name) +{ + return USB_DEVICE(qdev_new(name)); +} + +static inline USBDevice *usb_try_new(const char *name) +{ + return USB_DEVICE(qdev_try_new(name)); +} + +static inline bool usb_realize_and_unref(USBDevice *dev, USBBus *bus, Error **errp) +{ + return qdev_realize_and_unref(&dev->qdev, &bus->qbus, errp); +} + +static inline USBDevice *usb_create_simple(USBBus *bus, const char *name) +{ + USBDevice *dev = usb_new(name); + + usb_realize_and_unref(dev, bus, &error_abort); + return dev; +} + #endif diff --git a/include/hw/usb/dwc2-regs.h b/include/hw/usb/dwc2-regs.h index 4015c1d691..0bf3f2aa17 100644 --- a/include/hw/usb/dwc2-regs.h +++ b/include/hw/usb/dwc2-regs.h @@ -42,788 +42,788 @@ #ifndef DWC2_REGS_H #define DWC2_REGS_H -#define HSOTG_REG(x) (x) +#define HSOTG_REG(x) (x) -#define GOTGCTL HSOTG_REG(0x000) -#define GOTGCTL_CHIRPEN BIT(27) -#define GOTGCTL_MULT_VALID_BC_MASK (0x1f << 22) -#define GOTGCTL_MULT_VALID_BC_SHIFT 22 -#define GOTGCTL_OTGVER BIT(20) -#define GOTGCTL_BSESVLD BIT(19) -#define GOTGCTL_ASESVLD BIT(18) -#define GOTGCTL_DBNC_SHORT BIT(17) -#define GOTGCTL_CONID_B BIT(16) -#define GOTGCTL_DBNCE_FLTR_BYPASS BIT(15) -#define GOTGCTL_DEVHNPEN BIT(11) -#define GOTGCTL_HSTSETHNPEN BIT(10) -#define GOTGCTL_HNPREQ BIT(9) -#define GOTGCTL_HSTNEGSCS BIT(8) -#define GOTGCTL_SESREQ BIT(1) -#define GOTGCTL_SESREQSCS BIT(0) +#define GOTGCTL HSOTG_REG(0x000) +#define GOTGCTL_CHIRPEN BIT(27) +#define GOTGCTL_MULT_VALID_BC_MASK (0x1f << 22) +#define GOTGCTL_MULT_VALID_BC_SHIFT 22 +#define GOTGCTL_OTGVER BIT(20) +#define GOTGCTL_BSESVLD BIT(19) +#define GOTGCTL_ASESVLD BIT(18) +#define GOTGCTL_DBNC_SHORT BIT(17) +#define GOTGCTL_CONID_B BIT(16) +#define GOTGCTL_DBNCE_FLTR_BYPASS BIT(15) +#define GOTGCTL_DEVHNPEN BIT(11) +#define GOTGCTL_HSTSETHNPEN BIT(10) +#define GOTGCTL_HNPREQ BIT(9) +#define GOTGCTL_HSTNEGSCS BIT(8) +#define GOTGCTL_SESREQ BIT(1) +#define GOTGCTL_SESREQSCS BIT(0) -#define GOTGINT HSOTG_REG(0x004) -#define GOTGINT_DBNCE_DONE BIT(19) -#define GOTGINT_A_DEV_TOUT_CHG BIT(18) -#define GOTGINT_HST_NEG_DET BIT(17) -#define GOTGINT_HST_NEG_SUC_STS_CHNG BIT(9) -#define GOTGINT_SES_REQ_SUC_STS_CHNG BIT(8) -#define GOTGINT_SES_END_DET BIT(2) +#define GOTGINT HSOTG_REG(0x004) +#define GOTGINT_DBNCE_DONE BIT(19) +#define GOTGINT_A_DEV_TOUT_CHG BIT(18) +#define GOTGINT_HST_NEG_DET BIT(17) +#define GOTGINT_HST_NEG_SUC_STS_CHNG BIT(9) +#define GOTGINT_SES_REQ_SUC_STS_CHNG BIT(8) +#define GOTGINT_SES_END_DET BIT(2) -#define GAHBCFG HSOTG_REG(0x008) -#define GAHBCFG_AHB_SINGLE BIT(23) -#define GAHBCFG_NOTI_ALL_DMA_WRIT BIT(22) -#define GAHBCFG_REM_MEM_SUPP BIT(21) -#define GAHBCFG_P_TXF_EMP_LVL BIT(8) -#define GAHBCFG_NP_TXF_EMP_LVL BIT(7) -#define GAHBCFG_DMA_EN BIT(5) -#define GAHBCFG_HBSTLEN_MASK (0xf << 1) -#define GAHBCFG_HBSTLEN_SHIFT 1 -#define GAHBCFG_HBSTLEN_SINGLE 0 -#define GAHBCFG_HBSTLEN_INCR 1 -#define GAHBCFG_HBSTLEN_INCR4 3 -#define GAHBCFG_HBSTLEN_INCR8 5 -#define GAHBCFG_HBSTLEN_INCR16 7 -#define GAHBCFG_GLBL_INTR_EN BIT(0) -#define GAHBCFG_CTRL_MASK (GAHBCFG_P_TXF_EMP_LVL | \ - GAHBCFG_NP_TXF_EMP_LVL | \ - GAHBCFG_DMA_EN | \ - GAHBCFG_GLBL_INTR_EN) +#define GAHBCFG HSOTG_REG(0x008) +#define GAHBCFG_AHB_SINGLE BIT(23) +#define GAHBCFG_NOTI_ALL_DMA_WRIT BIT(22) +#define GAHBCFG_REM_MEM_SUPP BIT(21) +#define GAHBCFG_P_TXF_EMP_LVL BIT(8) +#define GAHBCFG_NP_TXF_EMP_LVL BIT(7) +#define GAHBCFG_DMA_EN BIT(5) +#define GAHBCFG_HBSTLEN_MASK (0xf << 1) +#define GAHBCFG_HBSTLEN_SHIFT 1 +#define GAHBCFG_HBSTLEN_SINGLE 0 +#define GAHBCFG_HBSTLEN_INCR 1 +#define GAHBCFG_HBSTLEN_INCR4 3 +#define GAHBCFG_HBSTLEN_INCR8 5 +#define GAHBCFG_HBSTLEN_INCR16 7 +#define GAHBCFG_GLBL_INTR_EN BIT(0) +#define GAHBCFG_CTRL_MASK (GAHBCFG_P_TXF_EMP_LVL | \ + GAHBCFG_NP_TXF_EMP_LVL | \ + GAHBCFG_DMA_EN | \ + GAHBCFG_GLBL_INTR_EN) -#define GUSBCFG HSOTG_REG(0x00C) -#define GUSBCFG_FORCEDEVMODE BIT(30) -#define GUSBCFG_FORCEHOSTMODE BIT(29) -#define GUSBCFG_TXENDDELAY BIT(28) -#define GUSBCFG_ICTRAFFICPULLREMOVE BIT(27) -#define GUSBCFG_ICUSBCAP BIT(26) -#define GUSBCFG_ULPI_INT_PROT_DIS BIT(25) -#define GUSBCFG_INDICATORPASSTHROUGH BIT(24) -#define GUSBCFG_INDICATORCOMPLEMENT BIT(23) -#define GUSBCFG_TERMSELDLPULSE BIT(22) -#define GUSBCFG_ULPI_INT_VBUS_IND BIT(21) -#define GUSBCFG_ULPI_EXT_VBUS_DRV BIT(20) -#define GUSBCFG_ULPI_CLK_SUSP_M BIT(19) -#define GUSBCFG_ULPI_AUTO_RES BIT(18) -#define GUSBCFG_ULPI_FS_LS BIT(17) -#define GUSBCFG_OTG_UTMI_FS_SEL BIT(16) -#define GUSBCFG_PHY_LP_CLK_SEL BIT(15) -#define GUSBCFG_USBTRDTIM_MASK (0xf << 10) -#define GUSBCFG_USBTRDTIM_SHIFT 10 -#define GUSBCFG_HNPCAP BIT(9) -#define GUSBCFG_SRPCAP BIT(8) -#define GUSBCFG_DDRSEL BIT(7) -#define GUSBCFG_PHYSEL BIT(6) -#define GUSBCFG_FSINTF BIT(5) -#define GUSBCFG_ULPI_UTMI_SEL BIT(4) -#define GUSBCFG_PHYIF16 BIT(3) -#define GUSBCFG_PHYIF8 (0 << 3) -#define GUSBCFG_TOUTCAL_MASK (0x7 << 0) -#define GUSBCFG_TOUTCAL_SHIFT 0 -#define GUSBCFG_TOUTCAL_LIMIT 0x7 -#define GUSBCFG_TOUTCAL(_x) ((_x) << 0) +#define GUSBCFG HSOTG_REG(0x00C) +#define GUSBCFG_FORCEDEVMODE BIT(30) +#define GUSBCFG_FORCEHOSTMODE BIT(29) +#define GUSBCFG_TXENDDELAY BIT(28) +#define GUSBCFG_ICTRAFFICPULLREMOVE BIT(27) +#define GUSBCFG_ICUSBCAP BIT(26) +#define GUSBCFG_ULPI_INT_PROT_DIS BIT(25) +#define GUSBCFG_INDICATORPASSTHROUGH BIT(24) +#define GUSBCFG_INDICATORCOMPLEMENT BIT(23) +#define GUSBCFG_TERMSELDLPULSE BIT(22) +#define GUSBCFG_ULPI_INT_VBUS_IND BIT(21) +#define GUSBCFG_ULPI_EXT_VBUS_DRV BIT(20) +#define GUSBCFG_ULPI_CLK_SUSP_M BIT(19) +#define GUSBCFG_ULPI_AUTO_RES BIT(18) +#define GUSBCFG_ULPI_FS_LS BIT(17) +#define GUSBCFG_OTG_UTMI_FS_SEL BIT(16) +#define GUSBCFG_PHY_LP_CLK_SEL BIT(15) +#define GUSBCFG_USBTRDTIM_MASK (0xf << 10) +#define GUSBCFG_USBTRDTIM_SHIFT 10 +#define GUSBCFG_HNPCAP BIT(9) +#define GUSBCFG_SRPCAP BIT(8) +#define GUSBCFG_DDRSEL BIT(7) +#define GUSBCFG_PHYSEL BIT(6) +#define GUSBCFG_FSINTF BIT(5) +#define GUSBCFG_ULPI_UTMI_SEL BIT(4) +#define GUSBCFG_PHYIF16 BIT(3) +#define GUSBCFG_PHYIF8 (0 << 3) +#define GUSBCFG_TOUTCAL_MASK (0x7 << 0) +#define GUSBCFG_TOUTCAL_SHIFT 0 +#define GUSBCFG_TOUTCAL_LIMIT 0x7 +#define GUSBCFG_TOUTCAL(_x) ((_x) << 0) -#define GRSTCTL HSOTG_REG(0x010) -#define GRSTCTL_AHBIDLE BIT(31) -#define GRSTCTL_DMAREQ BIT(30) -#define GRSTCTL_TXFNUM_MASK (0x1f << 6) -#define GRSTCTL_TXFNUM_SHIFT 6 -#define GRSTCTL_TXFNUM_LIMIT 0x1f -#define GRSTCTL_TXFNUM(_x) ((_x) << 6) -#define GRSTCTL_TXFFLSH BIT(5) -#define GRSTCTL_RXFFLSH BIT(4) -#define GRSTCTL_IN_TKNQ_FLSH BIT(3) -#define GRSTCTL_FRMCNTRRST BIT(2) -#define GRSTCTL_HSFTRST BIT(1) -#define GRSTCTL_CSFTRST BIT(0) +#define GRSTCTL HSOTG_REG(0x010) +#define GRSTCTL_AHBIDLE BIT(31) +#define GRSTCTL_DMAREQ BIT(30) +#define GRSTCTL_TXFNUM_MASK (0x1f << 6) +#define GRSTCTL_TXFNUM_SHIFT 6 +#define GRSTCTL_TXFNUM_LIMIT 0x1f +#define GRSTCTL_TXFNUM(_x) ((_x) << 6) +#define GRSTCTL_TXFFLSH BIT(5) +#define GRSTCTL_RXFFLSH BIT(4) +#define GRSTCTL_IN_TKNQ_FLSH BIT(3) +#define GRSTCTL_FRMCNTRRST BIT(2) +#define GRSTCTL_HSFTRST BIT(1) +#define GRSTCTL_CSFTRST BIT(0) -#define GINTSTS HSOTG_REG(0x014) -#define GINTMSK HSOTG_REG(0x018) -#define GINTSTS_WKUPINT BIT(31) -#define GINTSTS_SESSREQINT BIT(30) -#define GINTSTS_DISCONNINT BIT(29) -#define GINTSTS_CONIDSTSCHNG BIT(28) -#define GINTSTS_LPMTRANRCVD BIT(27) -#define GINTSTS_PTXFEMP BIT(26) -#define GINTSTS_HCHINT BIT(25) -#define GINTSTS_PRTINT BIT(24) -#define GINTSTS_RESETDET BIT(23) -#define GINTSTS_FET_SUSP BIT(22) -#define GINTSTS_INCOMPL_IP BIT(21) -#define GINTSTS_INCOMPL_SOOUT BIT(21) -#define GINTSTS_INCOMPL_SOIN BIT(20) -#define GINTSTS_OEPINT BIT(19) -#define GINTSTS_IEPINT BIT(18) -#define GINTSTS_EPMIS BIT(17) -#define GINTSTS_RESTOREDONE BIT(16) -#define GINTSTS_EOPF BIT(15) -#define GINTSTS_ISOUTDROP BIT(14) -#define GINTSTS_ENUMDONE BIT(13) -#define GINTSTS_USBRST BIT(12) -#define GINTSTS_USBSUSP BIT(11) -#define GINTSTS_ERLYSUSP BIT(10) -#define GINTSTS_I2CINT BIT(9) -#define GINTSTS_ULPI_CK_INT BIT(8) -#define GINTSTS_GOUTNAKEFF BIT(7) -#define GINTSTS_GINNAKEFF BIT(6) -#define GINTSTS_NPTXFEMP BIT(5) -#define GINTSTS_RXFLVL BIT(4) -#define GINTSTS_SOF BIT(3) -#define GINTSTS_OTGINT BIT(2) -#define GINTSTS_MODEMIS BIT(1) -#define GINTSTS_CURMODE_HOST BIT(0) +#define GINTSTS HSOTG_REG(0x014) +#define GINTMSK HSOTG_REG(0x018) +#define GINTSTS_WKUPINT BIT(31) +#define GINTSTS_SESSREQINT BIT(30) +#define GINTSTS_DISCONNINT BIT(29) +#define GINTSTS_CONIDSTSCHNG BIT(28) +#define GINTSTS_LPMTRANRCVD BIT(27) +#define GINTSTS_PTXFEMP BIT(26) +#define GINTSTS_HCHINT BIT(25) +#define GINTSTS_PRTINT BIT(24) +#define GINTSTS_RESETDET BIT(23) +#define GINTSTS_FET_SUSP BIT(22) +#define GINTSTS_INCOMPL_IP BIT(21) +#define GINTSTS_INCOMPL_SOOUT BIT(21) +#define GINTSTS_INCOMPL_SOIN BIT(20) +#define GINTSTS_OEPINT BIT(19) +#define GINTSTS_IEPINT BIT(18) +#define GINTSTS_EPMIS BIT(17) +#define GINTSTS_RESTOREDONE BIT(16) +#define GINTSTS_EOPF BIT(15) +#define GINTSTS_ISOUTDROP BIT(14) +#define GINTSTS_ENUMDONE BIT(13) +#define GINTSTS_USBRST BIT(12) +#define GINTSTS_USBSUSP BIT(11) +#define GINTSTS_ERLYSUSP BIT(10) +#define GINTSTS_I2CINT BIT(9) +#define GINTSTS_ULPI_CK_INT BIT(8) +#define GINTSTS_GOUTNAKEFF BIT(7) +#define GINTSTS_GINNAKEFF BIT(6) +#define GINTSTS_NPTXFEMP BIT(5) +#define GINTSTS_RXFLVL BIT(4) +#define GINTSTS_SOF BIT(3) +#define GINTSTS_OTGINT BIT(2) +#define GINTSTS_MODEMIS BIT(1) +#define GINTSTS_CURMODE_HOST BIT(0) -#define GRXSTSR HSOTG_REG(0x01C) -#define GRXSTSP HSOTG_REG(0x020) -#define GRXSTS_FN_MASK (0x7f << 25) -#define GRXSTS_FN_SHIFT 25 -#define GRXSTS_PKTSTS_MASK (0xf << 17) -#define GRXSTS_PKTSTS_SHIFT 17 -#define GRXSTS_PKTSTS_GLOBALOUTNAK 1 -#define GRXSTS_PKTSTS_OUTRX 2 -#define GRXSTS_PKTSTS_HCHIN 2 -#define GRXSTS_PKTSTS_OUTDONE 3 -#define GRXSTS_PKTSTS_HCHIN_XFER_COMP 3 -#define GRXSTS_PKTSTS_SETUPDONE 4 -#define GRXSTS_PKTSTS_DATATOGGLEERR 5 -#define GRXSTS_PKTSTS_SETUPRX 6 -#define GRXSTS_PKTSTS_HCHHALTED 7 -#define GRXSTS_HCHNUM_MASK (0xf << 0) -#define GRXSTS_HCHNUM_SHIFT 0 -#define GRXSTS_DPID_MASK (0x3 << 15) -#define GRXSTS_DPID_SHIFT 15 -#define GRXSTS_BYTECNT_MASK (0x7ff << 4) -#define GRXSTS_BYTECNT_SHIFT 4 -#define GRXSTS_EPNUM_MASK (0xf << 0) -#define GRXSTS_EPNUM_SHIFT 0 +#define GRXSTSR HSOTG_REG(0x01C) +#define GRXSTSP HSOTG_REG(0x020) +#define GRXSTS_FN_MASK (0x7f << 25) +#define GRXSTS_FN_SHIFT 25 +#define GRXSTS_PKTSTS_MASK (0xf << 17) +#define GRXSTS_PKTSTS_SHIFT 17 +#define GRXSTS_PKTSTS_GLOBALOUTNAK 1 +#define GRXSTS_PKTSTS_OUTRX 2 +#define GRXSTS_PKTSTS_HCHIN 2 +#define GRXSTS_PKTSTS_OUTDONE 3 +#define GRXSTS_PKTSTS_HCHIN_XFER_COMP 3 +#define GRXSTS_PKTSTS_SETUPDONE 4 +#define GRXSTS_PKTSTS_DATATOGGLEERR 5 +#define GRXSTS_PKTSTS_SETUPRX 6 +#define GRXSTS_PKTSTS_HCHHALTED 7 +#define GRXSTS_HCHNUM_MASK (0xf << 0) +#define GRXSTS_HCHNUM_SHIFT 0 +#define GRXSTS_DPID_MASK (0x3 << 15) +#define GRXSTS_DPID_SHIFT 15 +#define GRXSTS_BYTECNT_MASK (0x7ff << 4) +#define GRXSTS_BYTECNT_SHIFT 4 +#define GRXSTS_EPNUM_MASK (0xf << 0) +#define GRXSTS_EPNUM_SHIFT 0 -#define GRXFSIZ HSOTG_REG(0x024) -#define GRXFSIZ_DEPTH_MASK (0xffff << 0) -#define GRXFSIZ_DEPTH_SHIFT 0 +#define GRXFSIZ HSOTG_REG(0x024) +#define GRXFSIZ_DEPTH_MASK (0xffff << 0) +#define GRXFSIZ_DEPTH_SHIFT 0 -#define GNPTXFSIZ HSOTG_REG(0x028) +#define GNPTXFSIZ HSOTG_REG(0x028) /* Use FIFOSIZE_* constants to access this register */ -#define GNPTXSTS HSOTG_REG(0x02C) -#define GNPTXSTS_NP_TXQ_TOP_MASK (0x7f << 24) -#define GNPTXSTS_NP_TXQ_TOP_SHIFT 24 -#define GNPTXSTS_NP_TXQ_SPC_AVAIL_MASK (0xff << 16) -#define GNPTXSTS_NP_TXQ_SPC_AVAIL_SHIFT 16 -#define GNPTXSTS_NP_TXQ_SPC_AVAIL_GET(_v) (((_v) >> 16) & 0xff) -#define GNPTXSTS_NP_TXF_SPC_AVAIL_MASK (0xffff << 0) -#define GNPTXSTS_NP_TXF_SPC_AVAIL_SHIFT 0 -#define GNPTXSTS_NP_TXF_SPC_AVAIL_GET(_v) (((_v) >> 0) & 0xffff) +#define GNPTXSTS HSOTG_REG(0x02C) +#define GNPTXSTS_NP_TXQ_TOP_MASK (0x7f << 24) +#define GNPTXSTS_NP_TXQ_TOP_SHIFT 24 +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_MASK (0xff << 16) +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_SHIFT 16 +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_GET(_v) (((_v) >> 16) & 0xff) +#define GNPTXSTS_NP_TXF_SPC_AVAIL_MASK (0xffff << 0) +#define GNPTXSTS_NP_TXF_SPC_AVAIL_SHIFT 0 +#define GNPTXSTS_NP_TXF_SPC_AVAIL_GET(_v) (((_v) >> 0) & 0xffff) -#define GI2CCTL HSOTG_REG(0x0030) -#define GI2CCTL_BSYDNE BIT(31) -#define GI2CCTL_RW BIT(30) -#define GI2CCTL_I2CDATSE0 BIT(28) -#define GI2CCTL_I2CDEVADDR_MASK (0x3 << 26) -#define GI2CCTL_I2CDEVADDR_SHIFT 26 -#define GI2CCTL_I2CSUSPCTL BIT(25) -#define GI2CCTL_ACK BIT(24) -#define GI2CCTL_I2CEN BIT(23) -#define GI2CCTL_ADDR_MASK (0x7f << 16) -#define GI2CCTL_ADDR_SHIFT 16 -#define GI2CCTL_REGADDR_MASK (0xff << 8) -#define GI2CCTL_REGADDR_SHIFT 8 -#define GI2CCTL_RWDATA_MASK (0xff << 0) -#define GI2CCTL_RWDATA_SHIFT 0 +#define GI2CCTL HSOTG_REG(0x0030) +#define GI2CCTL_BSYDNE BIT(31) +#define GI2CCTL_RW BIT(30) +#define GI2CCTL_I2CDATSE0 BIT(28) +#define GI2CCTL_I2CDEVADDR_MASK (0x3 << 26) +#define GI2CCTL_I2CDEVADDR_SHIFT 26 +#define GI2CCTL_I2CSUSPCTL BIT(25) +#define GI2CCTL_ACK BIT(24) +#define GI2CCTL_I2CEN BIT(23) +#define GI2CCTL_ADDR_MASK (0x7f << 16) +#define GI2CCTL_ADDR_SHIFT 16 +#define GI2CCTL_REGADDR_MASK (0xff << 8) +#define GI2CCTL_REGADDR_SHIFT 8 +#define GI2CCTL_RWDATA_MASK (0xff << 0) +#define GI2CCTL_RWDATA_SHIFT 0 -#define GPVNDCTL HSOTG_REG(0x0034) -#define GGPIO HSOTG_REG(0x0038) -#define GGPIO_STM32_OTG_GCCFG_PWRDWN BIT(16) +#define GPVNDCTL HSOTG_REG(0x0034) +#define GGPIO HSOTG_REG(0x0038) +#define GGPIO_STM32_OTG_GCCFG_PWRDWN BIT(16) -#define GUID HSOTG_REG(0x003c) -#define GSNPSID HSOTG_REG(0x0040) -#define GHWCFG1 HSOTG_REG(0x0044) -#define GSNPSID_ID_MASK GENMASK(31, 16) +#define GUID HSOTG_REG(0x003c) +#define GSNPSID HSOTG_REG(0x0040) +#define GHWCFG1 HSOTG_REG(0x0044) +#define GSNPSID_ID_MASK GENMASK(31, 16) -#define GHWCFG2 HSOTG_REG(0x0048) -#define GHWCFG2_OTG_ENABLE_IC_USB BIT(31) -#define GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK (0x1f << 26) -#define GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT 26 -#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK (0x3 << 24) -#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT 24 -#define GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK (0x3 << 22) -#define GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT 22 -#define GHWCFG2_MULTI_PROC_INT BIT(20) -#define GHWCFG2_DYNAMIC_FIFO BIT(19) -#define GHWCFG2_PERIO_EP_SUPPORTED BIT(18) -#define GHWCFG2_NUM_HOST_CHAN_MASK (0xf << 14) -#define GHWCFG2_NUM_HOST_CHAN_SHIFT 14 -#define GHWCFG2_NUM_DEV_EP_MASK (0xf << 10) -#define GHWCFG2_NUM_DEV_EP_SHIFT 10 -#define GHWCFG2_FS_PHY_TYPE_MASK (0x3 << 8) -#define GHWCFG2_FS_PHY_TYPE_SHIFT 8 -#define GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED 0 -#define GHWCFG2_FS_PHY_TYPE_DEDICATED 1 -#define GHWCFG2_FS_PHY_TYPE_SHARED_UTMI 2 -#define GHWCFG2_FS_PHY_TYPE_SHARED_ULPI 3 -#define GHWCFG2_HS_PHY_TYPE_MASK (0x3 << 6) -#define GHWCFG2_HS_PHY_TYPE_SHIFT 6 -#define GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 -#define GHWCFG2_HS_PHY_TYPE_UTMI 1 -#define GHWCFG2_HS_PHY_TYPE_ULPI 2 -#define GHWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 -#define GHWCFG2_POINT2POINT BIT(5) -#define GHWCFG2_ARCHITECTURE_MASK (0x3 << 3) -#define GHWCFG2_ARCHITECTURE_SHIFT 3 -#define GHWCFG2_SLAVE_ONLY_ARCH 0 -#define GHWCFG2_EXT_DMA_ARCH 1 -#define GHWCFG2_INT_DMA_ARCH 2 -#define GHWCFG2_OP_MODE_MASK (0x7 << 0) -#define GHWCFG2_OP_MODE_SHIFT 0 -#define GHWCFG2_OP_MODE_HNP_SRP_CAPABLE 0 -#define GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE 1 -#define GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE 2 -#define GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3 -#define GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4 -#define GHWCFG2_OP_MODE_SRP_CAPABLE_HOST 5 -#define GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6 -#define GHWCFG2_OP_MODE_UNDEFINED 7 +#define GHWCFG2 HSOTG_REG(0x0048) +#define GHWCFG2_OTG_ENABLE_IC_USB BIT(31) +#define GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK (0x1f << 26) +#define GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT 26 +#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK (0x3 << 24) +#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT 24 +#define GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK (0x3 << 22) +#define GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT 22 +#define GHWCFG2_MULTI_PROC_INT BIT(20) +#define GHWCFG2_DYNAMIC_FIFO BIT(19) +#define GHWCFG2_PERIO_EP_SUPPORTED BIT(18) +#define GHWCFG2_NUM_HOST_CHAN_MASK (0xf << 14) +#define GHWCFG2_NUM_HOST_CHAN_SHIFT 14 +#define GHWCFG2_NUM_DEV_EP_MASK (0xf << 10) +#define GHWCFG2_NUM_DEV_EP_SHIFT 10 +#define GHWCFG2_FS_PHY_TYPE_MASK (0x3 << 8) +#define GHWCFG2_FS_PHY_TYPE_SHIFT 8 +#define GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED 0 +#define GHWCFG2_FS_PHY_TYPE_DEDICATED 1 +#define GHWCFG2_FS_PHY_TYPE_SHARED_UTMI 2 +#define GHWCFG2_FS_PHY_TYPE_SHARED_ULPI 3 +#define GHWCFG2_HS_PHY_TYPE_MASK (0x3 << 6) +#define GHWCFG2_HS_PHY_TYPE_SHIFT 6 +#define GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 +#define GHWCFG2_HS_PHY_TYPE_UTMI 1 +#define GHWCFG2_HS_PHY_TYPE_ULPI 2 +#define GHWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 +#define GHWCFG2_POINT2POINT BIT(5) +#define GHWCFG2_ARCHITECTURE_MASK (0x3 << 3) +#define GHWCFG2_ARCHITECTURE_SHIFT 3 +#define GHWCFG2_SLAVE_ONLY_ARCH 0 +#define GHWCFG2_EXT_DMA_ARCH 1 +#define GHWCFG2_INT_DMA_ARCH 2 +#define GHWCFG2_OP_MODE_MASK (0x7 << 0) +#define GHWCFG2_OP_MODE_SHIFT 0 +#define GHWCFG2_OP_MODE_HNP_SRP_CAPABLE 0 +#define GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE 1 +#define GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE 2 +#define GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3 +#define GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4 +#define GHWCFG2_OP_MODE_SRP_CAPABLE_HOST 5 +#define GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6 +#define GHWCFG2_OP_MODE_UNDEFINED 7 -#define GHWCFG3 HSOTG_REG(0x004c) -#define GHWCFG3_DFIFO_DEPTH_MASK (0xffff << 16) -#define GHWCFG3_DFIFO_DEPTH_SHIFT 16 -#define GHWCFG3_OTG_LPM_EN BIT(15) -#define GHWCFG3_BC_SUPPORT BIT(14) -#define GHWCFG3_OTG_ENABLE_HSIC BIT(13) -#define GHWCFG3_ADP_SUPP BIT(12) -#define GHWCFG3_SYNCH_RESET_TYPE BIT(11) -#define GHWCFG3_OPTIONAL_FEATURES BIT(10) -#define GHWCFG3_VENDOR_CTRL_IF BIT(9) -#define GHWCFG3_I2C BIT(8) -#define GHWCFG3_OTG_FUNC BIT(7) -#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK (0x7 << 4) -#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT 4 -#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK (0xf << 0) -#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT 0 +#define GHWCFG3 HSOTG_REG(0x004c) +#define GHWCFG3_DFIFO_DEPTH_MASK (0xffff << 16) +#define GHWCFG3_DFIFO_DEPTH_SHIFT 16 +#define GHWCFG3_OTG_LPM_EN BIT(15) +#define GHWCFG3_BC_SUPPORT BIT(14) +#define GHWCFG3_OTG_ENABLE_HSIC BIT(13) +#define GHWCFG3_ADP_SUPP BIT(12) +#define GHWCFG3_SYNCH_RESET_TYPE BIT(11) +#define GHWCFG3_OPTIONAL_FEATURES BIT(10) +#define GHWCFG3_VENDOR_CTRL_IF BIT(9) +#define GHWCFG3_I2C BIT(8) +#define GHWCFG3_OTG_FUNC BIT(7) +#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK (0x7 << 4) +#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT 4 +#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK (0xf << 0) +#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT 0 -#define GHWCFG4 HSOTG_REG(0x0050) -#define GHWCFG4_DESC_DMA_DYN BIT(31) -#define GHWCFG4_DESC_DMA BIT(30) -#define GHWCFG4_NUM_IN_EPS_MASK (0xf << 26) -#define GHWCFG4_NUM_IN_EPS_SHIFT 26 -#define GHWCFG4_DED_FIFO_EN BIT(25) -#define GHWCFG4_DED_FIFO_SHIFT 25 -#define GHWCFG4_SESSION_END_FILT_EN BIT(24) -#define GHWCFG4_B_VALID_FILT_EN BIT(23) -#define GHWCFG4_A_VALID_FILT_EN BIT(22) -#define GHWCFG4_VBUS_VALID_FILT_EN BIT(21) -#define GHWCFG4_IDDIG_FILT_EN BIT(20) -#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_MASK (0xf << 16) -#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16 -#define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14) -#define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14 -#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0 -#define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1 -#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2 -#define GHWCFG4_ACG_SUPPORTED BIT(12) -#define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11) +#define GHWCFG4 HSOTG_REG(0x0050) +#define GHWCFG4_DESC_DMA_DYN BIT(31) +#define GHWCFG4_DESC_DMA BIT(30) +#define GHWCFG4_NUM_IN_EPS_MASK (0xf << 26) +#define GHWCFG4_NUM_IN_EPS_SHIFT 26 +#define GHWCFG4_DED_FIFO_EN BIT(25) +#define GHWCFG4_DED_FIFO_SHIFT 25 +#define GHWCFG4_SESSION_END_FILT_EN BIT(24) +#define GHWCFG4_B_VALID_FILT_EN BIT(23) +#define GHWCFG4_A_VALID_FILT_EN BIT(22) +#define GHWCFG4_VBUS_VALID_FILT_EN BIT(21) +#define GHWCFG4_IDDIG_FILT_EN BIT(20) +#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_MASK (0xf << 16) +#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14) +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2 +#define GHWCFG4_ACG_SUPPORTED BIT(12) +#define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11) #define GHWCFG4_SERVICE_INTERVAL_SUPPORTED BIT(10) -#define GHWCFG4_XHIBER BIT(7) -#define GHWCFG4_HIBER BIT(6) -#define GHWCFG4_MIN_AHB_FREQ BIT(5) -#define GHWCFG4_POWER_OPTIMIZ BIT(4) -#define GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK (0xf << 0) -#define GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT 0 +#define GHWCFG4_XHIBER BIT(7) +#define GHWCFG4_HIBER BIT(6) +#define GHWCFG4_MIN_AHB_FREQ BIT(5) +#define GHWCFG4_POWER_OPTIMIZ BIT(4) +#define GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK (0xf << 0) +#define GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT 0 -#define GLPMCFG HSOTG_REG(0x0054) -#define GLPMCFG_INVSELHSIC BIT(31) -#define GLPMCFG_HSICCON BIT(30) -#define GLPMCFG_RSTRSLPSTS BIT(29) -#define GLPMCFG_ENBESL BIT(28) -#define GLPMCFG_LPM_RETRYCNT_STS_MASK (0x7 << 25) -#define GLPMCFG_LPM_RETRYCNT_STS_SHIFT 25 -#define GLPMCFG_SNDLPM BIT(24) -#define GLPMCFG_RETRY_CNT_MASK (0x7 << 21) -#define GLPMCFG_RETRY_CNT_SHIFT 21 -#define GLPMCFG_LPM_REJECT_CTRL_CONTROL BIT(21) -#define GLPMCFG_LPM_ACCEPT_CTRL_ISOC BIT(22) -#define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17) -#define GLPMCFG_LPM_CHNL_INDX_SHIFT 17 -#define GLPMCFG_L1RESUMEOK BIT(16) -#define GLPMCFG_SLPSTS BIT(15) -#define GLPMCFG_COREL1RES_MASK (0x3 << 13) -#define GLPMCFG_COREL1RES_SHIFT 13 -#define GLPMCFG_HIRD_THRES_MASK (0x1f << 8) -#define GLPMCFG_HIRD_THRES_SHIFT 8 -#define GLPMCFG_HIRD_THRES_EN (0x10 << 8) -#define GLPMCFG_ENBLSLPM BIT(7) -#define GLPMCFG_BREMOTEWAKE BIT(6) -#define GLPMCFG_HIRD_MASK (0xf << 2) -#define GLPMCFG_HIRD_SHIFT 2 -#define GLPMCFG_APPL1RES BIT(1) -#define GLPMCFG_LPMCAP BIT(0) +#define GLPMCFG HSOTG_REG(0x0054) +#define GLPMCFG_INVSELHSIC BIT(31) +#define GLPMCFG_HSICCON BIT(30) +#define GLPMCFG_RSTRSLPSTS BIT(29) +#define GLPMCFG_ENBESL BIT(28) +#define GLPMCFG_LPM_RETRYCNT_STS_MASK (0x7 << 25) +#define GLPMCFG_LPM_RETRYCNT_STS_SHIFT 25 +#define GLPMCFG_SNDLPM BIT(24) +#define GLPMCFG_RETRY_CNT_MASK (0x7 << 21) +#define GLPMCFG_RETRY_CNT_SHIFT 21 +#define GLPMCFG_LPM_REJECT_CTRL_CONTROL BIT(21) +#define GLPMCFG_LPM_ACCEPT_CTRL_ISOC BIT(22) +#define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17) +#define GLPMCFG_LPM_CHNL_INDX_SHIFT 17 +#define GLPMCFG_L1RESUMEOK BIT(16) +#define GLPMCFG_SLPSTS BIT(15) +#define GLPMCFG_COREL1RES_MASK (0x3 << 13) +#define GLPMCFG_COREL1RES_SHIFT 13 +#define GLPMCFG_HIRD_THRES_MASK (0x1f << 8) +#define GLPMCFG_HIRD_THRES_SHIFT 8 +#define GLPMCFG_HIRD_THRES_EN (0x10 << 8) +#define GLPMCFG_ENBLSLPM BIT(7) +#define GLPMCFG_BREMOTEWAKE BIT(6) +#define GLPMCFG_HIRD_MASK (0xf << 2) +#define GLPMCFG_HIRD_SHIFT 2 +#define GLPMCFG_APPL1RES BIT(1) +#define GLPMCFG_LPMCAP BIT(0) -#define GPWRDN HSOTG_REG(0x0058) -#define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24) -#define GPWRDN_MULT_VAL_ID_BC_SHIFT 24 -#define GPWRDN_ADP_INT BIT(23) -#define GPWRDN_BSESSVLD BIT(22) -#define GPWRDN_IDSTS BIT(21) -#define GPWRDN_LINESTATE_MASK (0x3 << 19) -#define GPWRDN_LINESTATE_SHIFT 19 -#define GPWRDN_STS_CHGINT_MSK BIT(18) -#define GPWRDN_STS_CHGINT BIT(17) -#define GPWRDN_SRP_DET_MSK BIT(16) -#define GPWRDN_SRP_DET BIT(15) -#define GPWRDN_CONNECT_DET_MSK BIT(14) -#define GPWRDN_CONNECT_DET BIT(13) -#define GPWRDN_DISCONN_DET_MSK BIT(12) -#define GPWRDN_DISCONN_DET BIT(11) -#define GPWRDN_RST_DET_MSK BIT(10) -#define GPWRDN_RST_DET BIT(9) -#define GPWRDN_LNSTSCHG_MSK BIT(8) -#define GPWRDN_LNSTSCHG BIT(7) -#define GPWRDN_DIS_VBUS BIT(6) -#define GPWRDN_PWRDNSWTCH BIT(5) -#define GPWRDN_PWRDNRSTN BIT(4) -#define GPWRDN_PWRDNCLMP BIT(3) -#define GPWRDN_RESTORE BIT(2) -#define GPWRDN_PMUACTV BIT(1) -#define GPWRDN_PMUINTSEL BIT(0) +#define GPWRDN HSOTG_REG(0x0058) +#define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24) +#define GPWRDN_MULT_VAL_ID_BC_SHIFT 24 +#define GPWRDN_ADP_INT BIT(23) +#define GPWRDN_BSESSVLD BIT(22) +#define GPWRDN_IDSTS BIT(21) +#define GPWRDN_LINESTATE_MASK (0x3 << 19) +#define GPWRDN_LINESTATE_SHIFT 19 +#define GPWRDN_STS_CHGINT_MSK BIT(18) +#define GPWRDN_STS_CHGINT BIT(17) +#define GPWRDN_SRP_DET_MSK BIT(16) +#define GPWRDN_SRP_DET BIT(15) +#define GPWRDN_CONNECT_DET_MSK BIT(14) +#define GPWRDN_CONNECT_DET BIT(13) +#define GPWRDN_DISCONN_DET_MSK BIT(12) +#define GPWRDN_DISCONN_DET BIT(11) +#define GPWRDN_RST_DET_MSK BIT(10) +#define GPWRDN_RST_DET BIT(9) +#define GPWRDN_LNSTSCHG_MSK BIT(8) +#define GPWRDN_LNSTSCHG BIT(7) +#define GPWRDN_DIS_VBUS BIT(6) +#define GPWRDN_PWRDNSWTCH BIT(5) +#define GPWRDN_PWRDNRSTN BIT(4) +#define GPWRDN_PWRDNCLMP BIT(3) +#define GPWRDN_RESTORE BIT(2) +#define GPWRDN_PMUACTV BIT(1) +#define GPWRDN_PMUINTSEL BIT(0) -#define GDFIFOCFG HSOTG_REG(0x005c) -#define GDFIFOCFG_EPINFOBASE_MASK (0xffff << 16) -#define GDFIFOCFG_EPINFOBASE_SHIFT 16 -#define GDFIFOCFG_GDFIFOCFG_MASK (0xffff << 0) -#define GDFIFOCFG_GDFIFOCFG_SHIFT 0 +#define GDFIFOCFG HSOTG_REG(0x005c) +#define GDFIFOCFG_EPINFOBASE_MASK (0xffff << 16) +#define GDFIFOCFG_EPINFOBASE_SHIFT 16 +#define GDFIFOCFG_GDFIFOCFG_MASK (0xffff << 0) +#define GDFIFOCFG_GDFIFOCFG_SHIFT 0 -#define ADPCTL HSOTG_REG(0x0060) -#define ADPCTL_AR_MASK (0x3 << 27) -#define ADPCTL_AR_SHIFT 27 -#define ADPCTL_ADP_TMOUT_INT_MSK BIT(26) -#define ADPCTL_ADP_SNS_INT_MSK BIT(25) -#define ADPCTL_ADP_PRB_INT_MSK BIT(24) -#define ADPCTL_ADP_TMOUT_INT BIT(23) -#define ADPCTL_ADP_SNS_INT BIT(22) -#define ADPCTL_ADP_PRB_INT BIT(21) -#define ADPCTL_ADPENA BIT(20) -#define ADPCTL_ADPRES BIT(19) -#define ADPCTL_ENASNS BIT(18) -#define ADPCTL_ENAPRB BIT(17) -#define ADPCTL_RTIM_MASK (0x7ff << 6) -#define ADPCTL_RTIM_SHIFT 6 -#define ADPCTL_PRB_PER_MASK (0x3 << 4) -#define ADPCTL_PRB_PER_SHIFT 4 -#define ADPCTL_PRB_DELTA_MASK (0x3 << 2) -#define ADPCTL_PRB_DELTA_SHIFT 2 -#define ADPCTL_PRB_DSCHRG_MASK (0x3 << 0) -#define ADPCTL_PRB_DSCHRG_SHIFT 0 +#define ADPCTL HSOTG_REG(0x0060) +#define ADPCTL_AR_MASK (0x3 << 27) +#define ADPCTL_AR_SHIFT 27 +#define ADPCTL_ADP_TMOUT_INT_MSK BIT(26) +#define ADPCTL_ADP_SNS_INT_MSK BIT(25) +#define ADPCTL_ADP_PRB_INT_MSK BIT(24) +#define ADPCTL_ADP_TMOUT_INT BIT(23) +#define ADPCTL_ADP_SNS_INT BIT(22) +#define ADPCTL_ADP_PRB_INT BIT(21) +#define ADPCTL_ADPENA BIT(20) +#define ADPCTL_ADPRES BIT(19) +#define ADPCTL_ENASNS BIT(18) +#define ADPCTL_ENAPRB BIT(17) +#define ADPCTL_RTIM_MASK (0x7ff << 6) +#define ADPCTL_RTIM_SHIFT 6 +#define ADPCTL_PRB_PER_MASK (0x3 << 4) +#define ADPCTL_PRB_PER_SHIFT 4 +#define ADPCTL_PRB_DELTA_MASK (0x3 << 2) +#define ADPCTL_PRB_DELTA_SHIFT 2 +#define ADPCTL_PRB_DSCHRG_MASK (0x3 << 0) +#define ADPCTL_PRB_DSCHRG_SHIFT 0 -#define GREFCLK HSOTG_REG(0x0064) -#define GREFCLK_REFCLKPER_MASK (0x1ffff << 15) -#define GREFCLK_REFCLKPER_SHIFT 15 -#define GREFCLK_REF_CLK_MODE BIT(14) -#define GREFCLK_SOF_CNT_WKUP_ALERT_MASK (0x3ff) +#define GREFCLK HSOTG_REG(0x0064) +#define GREFCLK_REFCLKPER_MASK (0x1ffff << 15) +#define GREFCLK_REFCLKPER_SHIFT 15 +#define GREFCLK_REF_CLK_MODE BIT(14) +#define GREFCLK_SOF_CNT_WKUP_ALERT_MASK (0x3ff) #define GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT 0 -#define GINTMSK2 HSOTG_REG(0x0068) -#define GINTMSK2_WKUP_ALERT_INT_MSK BIT(0) +#define GINTMSK2 HSOTG_REG(0x0068) +#define GINTMSK2_WKUP_ALERT_INT_MSK BIT(0) -#define GINTSTS2 HSOTG_REG(0x006c) -#define GINTSTS2_WKUP_ALERT_INT BIT(0) +#define GINTSTS2 HSOTG_REG(0x006c) +#define GINTSTS2_WKUP_ALERT_INT BIT(0) -#define HPTXFSIZ HSOTG_REG(0x100) +#define HPTXFSIZ HSOTG_REG(0x100) /* Use FIFOSIZE_* constants to access this register */ -#define DPTXFSIZN(_a) HSOTG_REG(0x104 + (((_a) - 1) * 4)) +#define DPTXFSIZN(_a) HSOTG_REG(0x104 + (((_a) - 1) * 4)) /* Use FIFOSIZE_* constants to access this register */ /* These apply to the GNPTXFSIZ, HPTXFSIZ and DPTXFSIZN registers */ -#define FIFOSIZE_DEPTH_MASK (0xffff << 16) -#define FIFOSIZE_DEPTH_SHIFT 16 -#define FIFOSIZE_STARTADDR_MASK (0xffff << 0) -#define FIFOSIZE_STARTADDR_SHIFT 0 -#define FIFOSIZE_DEPTH_GET(_x) (((_x) >> 16) & 0xffff) +#define FIFOSIZE_DEPTH_MASK (0xffff << 16) +#define FIFOSIZE_DEPTH_SHIFT 16 +#define FIFOSIZE_STARTADDR_MASK (0xffff << 0) +#define FIFOSIZE_STARTADDR_SHIFT 0 +#define FIFOSIZE_DEPTH_GET(_x) (((_x) >> 16) & 0xffff) /* Device mode registers */ -#define DCFG HSOTG_REG(0x800) -#define DCFG_DESCDMA_EN BIT(23) -#define DCFG_EPMISCNT_MASK (0x1f << 18) -#define DCFG_EPMISCNT_SHIFT 18 -#define DCFG_EPMISCNT_LIMIT 0x1f -#define DCFG_EPMISCNT(_x) ((_x) << 18) -#define DCFG_IPG_ISOC_SUPPORDED BIT(17) -#define DCFG_PERFRINT_MASK (0x3 << 11) -#define DCFG_PERFRINT_SHIFT 11 -#define DCFG_PERFRINT_LIMIT 0x3 -#define DCFG_PERFRINT(_x) ((_x) << 11) -#define DCFG_DEVADDR_MASK (0x7f << 4) -#define DCFG_DEVADDR_SHIFT 4 -#define DCFG_DEVADDR_LIMIT 0x7f -#define DCFG_DEVADDR(_x) ((_x) << 4) -#define DCFG_NZ_STS_OUT_HSHK BIT(2) -#define DCFG_DEVSPD_MASK (0x3 << 0) -#define DCFG_DEVSPD_SHIFT 0 -#define DCFG_DEVSPD_HS 0 -#define DCFG_DEVSPD_FS 1 -#define DCFG_DEVSPD_LS 2 -#define DCFG_DEVSPD_FS48 3 +#define DCFG HSOTG_REG(0x800) +#define DCFG_DESCDMA_EN BIT(23) +#define DCFG_EPMISCNT_MASK (0x1f << 18) +#define DCFG_EPMISCNT_SHIFT 18 +#define DCFG_EPMISCNT_LIMIT 0x1f +#define DCFG_EPMISCNT(_x) ((_x) << 18) +#define DCFG_IPG_ISOC_SUPPORDED BIT(17) +#define DCFG_PERFRINT_MASK (0x3 << 11) +#define DCFG_PERFRINT_SHIFT 11 +#define DCFG_PERFRINT_LIMIT 0x3 +#define DCFG_PERFRINT(_x) ((_x) << 11) +#define DCFG_DEVADDR_MASK (0x7f << 4) +#define DCFG_DEVADDR_SHIFT 4 +#define DCFG_DEVADDR_LIMIT 0x7f +#define DCFG_DEVADDR(_x) ((_x) << 4) +#define DCFG_NZ_STS_OUT_HSHK BIT(2) +#define DCFG_DEVSPD_MASK (0x3 << 0) +#define DCFG_DEVSPD_SHIFT 0 +#define DCFG_DEVSPD_HS 0 +#define DCFG_DEVSPD_FS 1 +#define DCFG_DEVSPD_LS 2 +#define DCFG_DEVSPD_FS48 3 -#define DCTL HSOTG_REG(0x804) +#define DCTL HSOTG_REG(0x804) #define DCTL_SERVICE_INTERVAL_SUPPORTED BIT(19) -#define DCTL_PWRONPRGDONE BIT(11) -#define DCTL_CGOUTNAK BIT(10) -#define DCTL_SGOUTNAK BIT(9) -#define DCTL_CGNPINNAK BIT(8) -#define DCTL_SGNPINNAK BIT(7) -#define DCTL_TSTCTL_MASK (0x7 << 4) -#define DCTL_TSTCTL_SHIFT 4 -#define DCTL_GOUTNAKSTS BIT(3) -#define DCTL_GNPINNAKSTS BIT(2) -#define DCTL_SFTDISCON BIT(1) -#define DCTL_RMTWKUPSIG BIT(0) +#define DCTL_PWRONPRGDONE BIT(11) +#define DCTL_CGOUTNAK BIT(10) +#define DCTL_SGOUTNAK BIT(9) +#define DCTL_CGNPINNAK BIT(8) +#define DCTL_SGNPINNAK BIT(7) +#define DCTL_TSTCTL_MASK (0x7 << 4) +#define DCTL_TSTCTL_SHIFT 4 +#define DCTL_GOUTNAKSTS BIT(3) +#define DCTL_GNPINNAKSTS BIT(2) +#define DCTL_SFTDISCON BIT(1) +#define DCTL_RMTWKUPSIG BIT(0) -#define DSTS HSOTG_REG(0x808) -#define DSTS_SOFFN_MASK (0x3fff << 8) -#define DSTS_SOFFN_SHIFT 8 -#define DSTS_SOFFN_LIMIT 0x3fff -#define DSTS_SOFFN(_x) ((_x) << 8) -#define DSTS_ERRATICERR BIT(3) -#define DSTS_ENUMSPD_MASK (0x3 << 1) -#define DSTS_ENUMSPD_SHIFT 1 -#define DSTS_ENUMSPD_HS 0 -#define DSTS_ENUMSPD_FS 1 -#define DSTS_ENUMSPD_LS 2 -#define DSTS_ENUMSPD_FS48 3 -#define DSTS_SUSPSTS BIT(0) +#define DSTS HSOTG_REG(0x808) +#define DSTS_SOFFN_MASK (0x3fff << 8) +#define DSTS_SOFFN_SHIFT 8 +#define DSTS_SOFFN_LIMIT 0x3fff +#define DSTS_SOFFN(_x) ((_x) << 8) +#define DSTS_ERRATICERR BIT(3) +#define DSTS_ENUMSPD_MASK (0x3 << 1) +#define DSTS_ENUMSPD_SHIFT 1 +#define DSTS_ENUMSPD_HS 0 +#define DSTS_ENUMSPD_FS 1 +#define DSTS_ENUMSPD_LS 2 +#define DSTS_ENUMSPD_FS48 3 +#define DSTS_SUSPSTS BIT(0) -#define DIEPMSK HSOTG_REG(0x810) -#define DIEPMSK_NAKMSK BIT(13) -#define DIEPMSK_BNAININTRMSK BIT(9) -#define DIEPMSK_TXFIFOUNDRNMSK BIT(8) -#define DIEPMSK_TXFIFOEMPTY BIT(7) -#define DIEPMSK_INEPNAKEFFMSK BIT(6) -#define DIEPMSK_INTKNEPMISMSK BIT(5) -#define DIEPMSK_INTKNTXFEMPMSK BIT(4) -#define DIEPMSK_TIMEOUTMSK BIT(3) -#define DIEPMSK_AHBERRMSK BIT(2) -#define DIEPMSK_EPDISBLDMSK BIT(1) -#define DIEPMSK_XFERCOMPLMSK BIT(0) +#define DIEPMSK HSOTG_REG(0x810) +#define DIEPMSK_NAKMSK BIT(13) +#define DIEPMSK_BNAININTRMSK BIT(9) +#define DIEPMSK_TXFIFOUNDRNMSK BIT(8) +#define DIEPMSK_TXFIFOEMPTY BIT(7) +#define DIEPMSK_INEPNAKEFFMSK BIT(6) +#define DIEPMSK_INTKNEPMISMSK BIT(5) +#define DIEPMSK_INTKNTXFEMPMSK BIT(4) +#define DIEPMSK_TIMEOUTMSK BIT(3) +#define DIEPMSK_AHBERRMSK BIT(2) +#define DIEPMSK_EPDISBLDMSK BIT(1) +#define DIEPMSK_XFERCOMPLMSK BIT(0) -#define DOEPMSK HSOTG_REG(0x814) -#define DOEPMSK_BNAMSK BIT(9) -#define DOEPMSK_BACK2BACKSETUP BIT(6) -#define DOEPMSK_STSPHSERCVDMSK BIT(5) -#define DOEPMSK_OUTTKNEPDISMSK BIT(4) -#define DOEPMSK_SETUPMSK BIT(3) -#define DOEPMSK_AHBERRMSK BIT(2) -#define DOEPMSK_EPDISBLDMSK BIT(1) -#define DOEPMSK_XFERCOMPLMSK BIT(0) +#define DOEPMSK HSOTG_REG(0x814) +#define DOEPMSK_BNAMSK BIT(9) +#define DOEPMSK_BACK2BACKSETUP BIT(6) +#define DOEPMSK_STSPHSERCVDMSK BIT(5) +#define DOEPMSK_OUTTKNEPDISMSK BIT(4) +#define DOEPMSK_SETUPMSK BIT(3) +#define DOEPMSK_AHBERRMSK BIT(2) +#define DOEPMSK_EPDISBLDMSK BIT(1) +#define DOEPMSK_XFERCOMPLMSK BIT(0) -#define DAINT HSOTG_REG(0x818) -#define DAINTMSK HSOTG_REG(0x81C) -#define DAINT_OUTEP_SHIFT 16 -#define DAINT_OUTEP(_x) (1 << ((_x) + 16)) -#define DAINT_INEP(_x) (1 << (_x)) +#define DAINT HSOTG_REG(0x818) +#define DAINTMSK HSOTG_REG(0x81C) +#define DAINT_OUTEP_SHIFT 16 +#define DAINT_OUTEP(_x) (1 << ((_x) + 16)) +#define DAINT_INEP(_x) (1 << (_x)) -#define DTKNQR1 HSOTG_REG(0x820) -#define DTKNQR2 HSOTG_REG(0x824) -#define DTKNQR3 HSOTG_REG(0x830) -#define DTKNQR4 HSOTG_REG(0x834) -#define DIEPEMPMSK HSOTG_REG(0x834) +#define DTKNQR1 HSOTG_REG(0x820) +#define DTKNQR2 HSOTG_REG(0x824) +#define DTKNQR3 HSOTG_REG(0x830) +#define DTKNQR4 HSOTG_REG(0x834) +#define DIEPEMPMSK HSOTG_REG(0x834) -#define DVBUSDIS HSOTG_REG(0x828) -#define DVBUSPULSE HSOTG_REG(0x82C) +#define DVBUSDIS HSOTG_REG(0x828) +#define DVBUSPULSE HSOTG_REG(0x82C) -#define DIEPCTL0 HSOTG_REG(0x900) -#define DIEPCTL(_a) HSOTG_REG(0x900 + ((_a) * 0x20)) +#define DIEPCTL0 HSOTG_REG(0x900) +#define DIEPCTL(_a) HSOTG_REG(0x900 + ((_a) * 0x20)) -#define DOEPCTL0 HSOTG_REG(0xB00) -#define DOEPCTL(_a) HSOTG_REG(0xB00 + ((_a) * 0x20)) +#define DOEPCTL0 HSOTG_REG(0xB00) +#define DOEPCTL(_a) HSOTG_REG(0xB00 + ((_a) * 0x20)) /* EP0 specialness: * bits[29..28] - reserved (no SetD0PID, SetD1PID) * bits[25..22] - should always be zero, this isn't a periodic endpoint * bits[10..0] - MPS setting different for EP0 */ -#define D0EPCTL_MPS_MASK (0x3 << 0) -#define D0EPCTL_MPS_SHIFT 0 -#define D0EPCTL_MPS_64 0 -#define D0EPCTL_MPS_32 1 -#define D0EPCTL_MPS_16 2 -#define D0EPCTL_MPS_8 3 +#define D0EPCTL_MPS_MASK (0x3 << 0) +#define D0EPCTL_MPS_SHIFT 0 +#define D0EPCTL_MPS_64 0 +#define D0EPCTL_MPS_32 1 +#define D0EPCTL_MPS_16 2 +#define D0EPCTL_MPS_8 3 -#define DXEPCTL_EPENA BIT(31) -#define DXEPCTL_EPDIS BIT(30) -#define DXEPCTL_SETD1PID BIT(29) -#define DXEPCTL_SETODDFR BIT(29) -#define DXEPCTL_SETD0PID BIT(28) -#define DXEPCTL_SETEVENFR BIT(28) -#define DXEPCTL_SNAK BIT(27) -#define DXEPCTL_CNAK BIT(26) -#define DXEPCTL_TXFNUM_MASK (0xf << 22) -#define DXEPCTL_TXFNUM_SHIFT 22 -#define DXEPCTL_TXFNUM_LIMIT 0xf -#define DXEPCTL_TXFNUM(_x) ((_x) << 22) -#define DXEPCTL_STALL BIT(21) -#define DXEPCTL_SNP BIT(20) -#define DXEPCTL_EPTYPE_MASK (0x3 << 18) -#define DXEPCTL_EPTYPE_CONTROL (0x0 << 18) -#define DXEPCTL_EPTYPE_ISO (0x1 << 18) -#define DXEPCTL_EPTYPE_BULK (0x2 << 18) -#define DXEPCTL_EPTYPE_INTERRUPT (0x3 << 18) +#define DXEPCTL_EPENA BIT(31) +#define DXEPCTL_EPDIS BIT(30) +#define DXEPCTL_SETD1PID BIT(29) +#define DXEPCTL_SETODDFR BIT(29) +#define DXEPCTL_SETD0PID BIT(28) +#define DXEPCTL_SETEVENFR BIT(28) +#define DXEPCTL_SNAK BIT(27) +#define DXEPCTL_CNAK BIT(26) +#define DXEPCTL_TXFNUM_MASK (0xf << 22) +#define DXEPCTL_TXFNUM_SHIFT 22 +#define DXEPCTL_TXFNUM_LIMIT 0xf +#define DXEPCTL_TXFNUM(_x) ((_x) << 22) +#define DXEPCTL_STALL BIT(21) +#define DXEPCTL_SNP BIT(20) +#define DXEPCTL_EPTYPE_MASK (0x3 << 18) +#define DXEPCTL_EPTYPE_CONTROL (0x0 << 18) +#define DXEPCTL_EPTYPE_ISO (0x1 << 18) +#define DXEPCTL_EPTYPE_BULK (0x2 << 18) +#define DXEPCTL_EPTYPE_INTERRUPT (0x3 << 18) -#define DXEPCTL_NAKSTS BIT(17) -#define DXEPCTL_DPID BIT(16) -#define DXEPCTL_EOFRNUM BIT(16) -#define DXEPCTL_USBACTEP BIT(15) -#define DXEPCTL_NEXTEP_MASK (0xf << 11) -#define DXEPCTL_NEXTEP_SHIFT 11 -#define DXEPCTL_NEXTEP_LIMIT 0xf -#define DXEPCTL_NEXTEP(_x) ((_x) << 11) -#define DXEPCTL_MPS_MASK (0x7ff << 0) -#define DXEPCTL_MPS_SHIFT 0 -#define DXEPCTL_MPS_LIMIT 0x7ff -#define DXEPCTL_MPS(_x) ((_x) << 0) +#define DXEPCTL_NAKSTS BIT(17) +#define DXEPCTL_DPID BIT(16) +#define DXEPCTL_EOFRNUM BIT(16) +#define DXEPCTL_USBACTEP BIT(15) +#define DXEPCTL_NEXTEP_MASK (0xf << 11) +#define DXEPCTL_NEXTEP_SHIFT 11 +#define DXEPCTL_NEXTEP_LIMIT 0xf +#define DXEPCTL_NEXTEP(_x) ((_x) << 11) +#define DXEPCTL_MPS_MASK (0x7ff << 0) +#define DXEPCTL_MPS_SHIFT 0 +#define DXEPCTL_MPS_LIMIT 0x7ff +#define DXEPCTL_MPS(_x) ((_x) << 0) -#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20)) -#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20)) -#define DXEPINT_SETUP_RCVD BIT(15) -#define DXEPINT_NYETINTRPT BIT(14) -#define DXEPINT_NAKINTRPT BIT(13) -#define DXEPINT_BBLEERRINTRPT BIT(12) -#define DXEPINT_PKTDRPSTS BIT(11) -#define DXEPINT_BNAINTR BIT(9) -#define DXEPINT_TXFIFOUNDRN BIT(8) -#define DXEPINT_OUTPKTERR BIT(8) -#define DXEPINT_TXFEMP BIT(7) -#define DXEPINT_INEPNAKEFF BIT(6) -#define DXEPINT_BACK2BACKSETUP BIT(6) -#define DXEPINT_INTKNEPMIS BIT(5) -#define DXEPINT_STSPHSERCVD BIT(5) -#define DXEPINT_INTKNTXFEMP BIT(4) -#define DXEPINT_OUTTKNEPDIS BIT(4) -#define DXEPINT_TIMEOUT BIT(3) -#define DXEPINT_SETUP BIT(3) -#define DXEPINT_AHBERR BIT(2) -#define DXEPINT_EPDISBLD BIT(1) -#define DXEPINT_XFERCOMPL BIT(0) +#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20)) +#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20)) +#define DXEPINT_SETUP_RCVD BIT(15) +#define DXEPINT_NYETINTRPT BIT(14) +#define DXEPINT_NAKINTRPT BIT(13) +#define DXEPINT_BBLEERRINTRPT BIT(12) +#define DXEPINT_PKTDRPSTS BIT(11) +#define DXEPINT_BNAINTR BIT(9) +#define DXEPINT_TXFIFOUNDRN BIT(8) +#define DXEPINT_OUTPKTERR BIT(8) +#define DXEPINT_TXFEMP BIT(7) +#define DXEPINT_INEPNAKEFF BIT(6) +#define DXEPINT_BACK2BACKSETUP BIT(6) +#define DXEPINT_INTKNEPMIS BIT(5) +#define DXEPINT_STSPHSERCVD BIT(5) +#define DXEPINT_INTKNTXFEMP BIT(4) +#define DXEPINT_OUTTKNEPDIS BIT(4) +#define DXEPINT_TIMEOUT BIT(3) +#define DXEPINT_SETUP BIT(3) +#define DXEPINT_AHBERR BIT(2) +#define DXEPINT_EPDISBLD BIT(1) +#define DXEPINT_XFERCOMPL BIT(0) -#define DIEPTSIZ0 HSOTG_REG(0x910) -#define DIEPTSIZ0_PKTCNT_MASK (0x3 << 19) -#define DIEPTSIZ0_PKTCNT_SHIFT 19 -#define DIEPTSIZ0_PKTCNT_LIMIT 0x3 -#define DIEPTSIZ0_PKTCNT(_x) ((_x) << 19) -#define DIEPTSIZ0_XFERSIZE_MASK (0x7f << 0) -#define DIEPTSIZ0_XFERSIZE_SHIFT 0 -#define DIEPTSIZ0_XFERSIZE_LIMIT 0x7f -#define DIEPTSIZ0_XFERSIZE(_x) ((_x) << 0) +#define DIEPTSIZ0 HSOTG_REG(0x910) +#define DIEPTSIZ0_PKTCNT_MASK (0x3 << 19) +#define DIEPTSIZ0_PKTCNT_SHIFT 19 +#define DIEPTSIZ0_PKTCNT_LIMIT 0x3 +#define DIEPTSIZ0_PKTCNT(_x) ((_x) << 19) +#define DIEPTSIZ0_XFERSIZE_MASK (0x7f << 0) +#define DIEPTSIZ0_XFERSIZE_SHIFT 0 +#define DIEPTSIZ0_XFERSIZE_LIMIT 0x7f +#define DIEPTSIZ0_XFERSIZE(_x) ((_x) << 0) -#define DOEPTSIZ0 HSOTG_REG(0xB10) -#define DOEPTSIZ0_SUPCNT_MASK (0x3 << 29) -#define DOEPTSIZ0_SUPCNT_SHIFT 29 -#define DOEPTSIZ0_SUPCNT_LIMIT 0x3 -#define DOEPTSIZ0_SUPCNT(_x) ((_x) << 29) -#define DOEPTSIZ0_PKTCNT BIT(19) -#define DOEPTSIZ0_XFERSIZE_MASK (0x7f << 0) -#define DOEPTSIZ0_XFERSIZE_SHIFT 0 +#define DOEPTSIZ0 HSOTG_REG(0xB10) +#define DOEPTSIZ0_SUPCNT_MASK (0x3 << 29) +#define DOEPTSIZ0_SUPCNT_SHIFT 29 +#define DOEPTSIZ0_SUPCNT_LIMIT 0x3 +#define DOEPTSIZ0_SUPCNT(_x) ((_x) << 29) +#define DOEPTSIZ0_PKTCNT BIT(19) +#define DOEPTSIZ0_XFERSIZE_MASK (0x7f << 0) +#define DOEPTSIZ0_XFERSIZE_SHIFT 0 -#define DIEPTSIZ(_a) HSOTG_REG(0x910 + ((_a) * 0x20)) -#define DOEPTSIZ(_a) HSOTG_REG(0xB10 + ((_a) * 0x20)) -#define DXEPTSIZ_MC_MASK (0x3 << 29) -#define DXEPTSIZ_MC_SHIFT 29 -#define DXEPTSIZ_MC_LIMIT 0x3 -#define DXEPTSIZ_MC(_x) ((_x) << 29) -#define DXEPTSIZ_PKTCNT_MASK (0x3ff << 19) -#define DXEPTSIZ_PKTCNT_SHIFT 19 -#define DXEPTSIZ_PKTCNT_LIMIT 0x3ff -#define DXEPTSIZ_PKTCNT_GET(_v) (((_v) >> 19) & 0x3ff) -#define DXEPTSIZ_PKTCNT(_x) ((_x) << 19) -#define DXEPTSIZ_XFERSIZE_MASK (0x7ffff << 0) -#define DXEPTSIZ_XFERSIZE_SHIFT 0 -#define DXEPTSIZ_XFERSIZE_LIMIT 0x7ffff -#define DXEPTSIZ_XFERSIZE_GET(_v) (((_v) >> 0) & 0x7ffff) -#define DXEPTSIZ_XFERSIZE(_x) ((_x) << 0) +#define DIEPTSIZ(_a) HSOTG_REG(0x910 + ((_a) * 0x20)) +#define DOEPTSIZ(_a) HSOTG_REG(0xB10 + ((_a) * 0x20)) +#define DXEPTSIZ_MC_MASK (0x3 << 29) +#define DXEPTSIZ_MC_SHIFT 29 +#define DXEPTSIZ_MC_LIMIT 0x3 +#define DXEPTSIZ_MC(_x) ((_x) << 29) +#define DXEPTSIZ_PKTCNT_MASK (0x3ff << 19) +#define DXEPTSIZ_PKTCNT_SHIFT 19 +#define DXEPTSIZ_PKTCNT_LIMIT 0x3ff +#define DXEPTSIZ_PKTCNT_GET(_v) (((_v) >> 19) & 0x3ff) +#define DXEPTSIZ_PKTCNT(_x) ((_x) << 19) +#define DXEPTSIZ_XFERSIZE_MASK (0x7ffff << 0) +#define DXEPTSIZ_XFERSIZE_SHIFT 0 +#define DXEPTSIZ_XFERSIZE_LIMIT 0x7ffff +#define DXEPTSIZ_XFERSIZE_GET(_v) (((_v) >> 0) & 0x7ffff) +#define DXEPTSIZ_XFERSIZE(_x) ((_x) << 0) -#define DIEPDMA(_a) HSOTG_REG(0x914 + ((_a) * 0x20)) -#define DOEPDMA(_a) HSOTG_REG(0xB14 + ((_a) * 0x20)) +#define DIEPDMA(_a) HSOTG_REG(0x914 + ((_a) * 0x20)) +#define DOEPDMA(_a) HSOTG_REG(0xB14 + ((_a) * 0x20)) -#define DTXFSTS(_a) HSOTG_REG(0x918 + ((_a) * 0x20)) +#define DTXFSTS(_a) HSOTG_REG(0x918 + ((_a) * 0x20)) -#define PCGCTL HSOTG_REG(0x0e00) -#define PCGCTL_IF_DEV_MODE BIT(31) -#define PCGCTL_P2HD_PRT_SPD_MASK (0x3 << 29) -#define PCGCTL_P2HD_PRT_SPD_SHIFT 29 -#define PCGCTL_P2HD_DEV_ENUM_SPD_MASK (0x3 << 27) -#define PCGCTL_P2HD_DEV_ENUM_SPD_SHIFT 27 -#define PCGCTL_MAC_DEV_ADDR_MASK (0x7f << 20) -#define PCGCTL_MAC_DEV_ADDR_SHIFT 20 -#define PCGCTL_MAX_TERMSEL BIT(19) -#define PCGCTL_MAX_XCVRSELECT_MASK (0x3 << 17) -#define PCGCTL_MAX_XCVRSELECT_SHIFT 17 -#define PCGCTL_PORT_POWER BIT(16) -#define PCGCTL_PRT_CLK_SEL_MASK (0x3 << 14) -#define PCGCTL_PRT_CLK_SEL_SHIFT 14 -#define PCGCTL_ESS_REG_RESTORED BIT(13) -#define PCGCTL_EXTND_HIBER_SWITCH BIT(12) -#define PCGCTL_EXTND_HIBER_PWRCLMP BIT(11) -#define PCGCTL_ENBL_EXTND_HIBER BIT(10) -#define PCGCTL_RESTOREMODE BIT(9) -#define PCGCTL_RESETAFTSUSP BIT(8) -#define PCGCTL_DEEP_SLEEP BIT(7) -#define PCGCTL_PHY_IN_SLEEP BIT(6) -#define PCGCTL_ENBL_SLEEP_GATING BIT(5) -#define PCGCTL_RSTPDWNMODULE BIT(3) -#define PCGCTL_PWRCLMP BIT(2) -#define PCGCTL_GATEHCLK BIT(1) -#define PCGCTL_STOPPCLK BIT(0) +#define PCGCTL HSOTG_REG(0x0e00) +#define PCGCTL_IF_DEV_MODE BIT(31) +#define PCGCTL_P2HD_PRT_SPD_MASK (0x3 << 29) +#define PCGCTL_P2HD_PRT_SPD_SHIFT 29 +#define PCGCTL_P2HD_DEV_ENUM_SPD_MASK (0x3 << 27) +#define PCGCTL_P2HD_DEV_ENUM_SPD_SHIFT 27 +#define PCGCTL_MAC_DEV_ADDR_MASK (0x7f << 20) +#define PCGCTL_MAC_DEV_ADDR_SHIFT 20 +#define PCGCTL_MAX_TERMSEL BIT(19) +#define PCGCTL_MAX_XCVRSELECT_MASK (0x3 << 17) +#define PCGCTL_MAX_XCVRSELECT_SHIFT 17 +#define PCGCTL_PORT_POWER BIT(16) +#define PCGCTL_PRT_CLK_SEL_MASK (0x3 << 14) +#define PCGCTL_PRT_CLK_SEL_SHIFT 14 +#define PCGCTL_ESS_REG_RESTORED BIT(13) +#define PCGCTL_EXTND_HIBER_SWITCH BIT(12) +#define PCGCTL_EXTND_HIBER_PWRCLMP BIT(11) +#define PCGCTL_ENBL_EXTND_HIBER BIT(10) +#define PCGCTL_RESTOREMODE BIT(9) +#define PCGCTL_RESETAFTSUSP BIT(8) +#define PCGCTL_DEEP_SLEEP BIT(7) +#define PCGCTL_PHY_IN_SLEEP BIT(6) +#define PCGCTL_ENBL_SLEEP_GATING BIT(5) +#define PCGCTL_RSTPDWNMODULE BIT(3) +#define PCGCTL_PWRCLMP BIT(2) +#define PCGCTL_GATEHCLK BIT(1) +#define PCGCTL_STOPPCLK BIT(0) #define PCGCCTL1 HSOTG_REG(0xe04) #define PCGCCTL1_TIMER (0x3 << 1) #define PCGCCTL1_GATEEN BIT(0) -#define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000)) +#define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000)) /* Host Mode Registers */ -#define HCFG HSOTG_REG(0x0400) -#define HCFG_MODECHTIMEN BIT(31) -#define HCFG_PERSCHEDENA BIT(26) -#define HCFG_FRLISTEN_MASK (0x3 << 24) -#define HCFG_FRLISTEN_SHIFT 24 -#define HCFG_FRLISTEN_8 (0 << 24) -#define FRLISTEN_8_SIZE 8 -#define HCFG_FRLISTEN_16 BIT(24) -#define FRLISTEN_16_SIZE 16 -#define HCFG_FRLISTEN_32 (2 << 24) -#define FRLISTEN_32_SIZE 32 -#define HCFG_FRLISTEN_64 (3 << 24) -#define FRLISTEN_64_SIZE 64 -#define HCFG_DESCDMA BIT(23) -#define HCFG_RESVALID_MASK (0xff << 8) -#define HCFG_RESVALID_SHIFT 8 -#define HCFG_ENA32KHZ BIT(7) -#define HCFG_FSLSSUPP BIT(2) -#define HCFG_FSLSPCLKSEL_MASK (0x3 << 0) -#define HCFG_FSLSPCLKSEL_SHIFT 0 -#define HCFG_FSLSPCLKSEL_30_60_MHZ 0 -#define HCFG_FSLSPCLKSEL_48_MHZ 1 -#define HCFG_FSLSPCLKSEL_6_MHZ 2 +#define HCFG HSOTG_REG(0x0400) +#define HCFG_MODECHTIMEN BIT(31) +#define HCFG_PERSCHEDENA BIT(26) +#define HCFG_FRLISTEN_MASK (0x3 << 24) +#define HCFG_FRLISTEN_SHIFT 24 +#define HCFG_FRLISTEN_8 (0 << 24) +#define FRLISTEN_8_SIZE 8 +#define HCFG_FRLISTEN_16 BIT(24) +#define FRLISTEN_16_SIZE 16 +#define HCFG_FRLISTEN_32 (2 << 24) +#define FRLISTEN_32_SIZE 32 +#define HCFG_FRLISTEN_64 (3 << 24) +#define FRLISTEN_64_SIZE 64 +#define HCFG_DESCDMA BIT(23) +#define HCFG_RESVALID_MASK (0xff << 8) +#define HCFG_RESVALID_SHIFT 8 +#define HCFG_ENA32KHZ BIT(7) +#define HCFG_FSLSSUPP BIT(2) +#define HCFG_FSLSPCLKSEL_MASK (0x3 << 0) +#define HCFG_FSLSPCLKSEL_SHIFT 0 +#define HCFG_FSLSPCLKSEL_30_60_MHZ 0 +#define HCFG_FSLSPCLKSEL_48_MHZ 1 +#define HCFG_FSLSPCLKSEL_6_MHZ 2 -#define HFIR HSOTG_REG(0x0404) -#define HFIR_FRINT_MASK (0xffff << 0) -#define HFIR_FRINT_SHIFT 0 -#define HFIR_RLDCTRL BIT(16) +#define HFIR HSOTG_REG(0x0404) +#define HFIR_FRINT_MASK (0xffff << 0) +#define HFIR_FRINT_SHIFT 0 +#define HFIR_RLDCTRL BIT(16) -#define HFNUM HSOTG_REG(0x0408) -#define HFNUM_FRREM_MASK (0xffff << 16) -#define HFNUM_FRREM_SHIFT 16 -#define HFNUM_FRNUM_MASK (0xffff << 0) -#define HFNUM_FRNUM_SHIFT 0 -#define HFNUM_MAX_FRNUM 0x3fff +#define HFNUM HSOTG_REG(0x0408) +#define HFNUM_FRREM_MASK (0xffff << 16) +#define HFNUM_FRREM_SHIFT 16 +#define HFNUM_FRNUM_MASK (0xffff << 0) +#define HFNUM_FRNUM_SHIFT 0 +#define HFNUM_MAX_FRNUM 0x3fff -#define HPTXSTS HSOTG_REG(0x0410) -#define TXSTS_QTOP_ODD BIT(31) -#define TXSTS_QTOP_CHNEP_MASK (0xf << 27) -#define TXSTS_QTOP_CHNEP_SHIFT 27 -#define TXSTS_QTOP_TOKEN_MASK (0x3 << 25) -#define TXSTS_QTOP_TOKEN_SHIFT 25 -#define TXSTS_QTOP_TERMINATE BIT(24) -#define TXSTS_QSPCAVAIL_MASK (0xff << 16) -#define TXSTS_QSPCAVAIL_SHIFT 16 -#define TXSTS_FSPCAVAIL_MASK (0xffff << 0) -#define TXSTS_FSPCAVAIL_SHIFT 0 +#define HPTXSTS HSOTG_REG(0x0410) +#define TXSTS_QTOP_ODD BIT(31) +#define TXSTS_QTOP_CHNEP_MASK (0xf << 27) +#define TXSTS_QTOP_CHNEP_SHIFT 27 +#define TXSTS_QTOP_TOKEN_MASK (0x3 << 25) +#define TXSTS_QTOP_TOKEN_SHIFT 25 +#define TXSTS_QTOP_TERMINATE BIT(24) +#define TXSTS_QSPCAVAIL_MASK (0xff << 16) +#define TXSTS_QSPCAVAIL_SHIFT 16 +#define TXSTS_FSPCAVAIL_MASK (0xffff << 0) +#define TXSTS_FSPCAVAIL_SHIFT 0 -#define HAINT HSOTG_REG(0x0414) -#define HAINTMSK HSOTG_REG(0x0418) -#define HFLBADDR HSOTG_REG(0x041c) +#define HAINT HSOTG_REG(0x0414) +#define HAINTMSK HSOTG_REG(0x0418) +#define HFLBADDR HSOTG_REG(0x041c) -#define HPRT0 HSOTG_REG(0x0440) -#define HPRT0_SPD_MASK (0x3 << 17) -#define HPRT0_SPD_SHIFT 17 -#define HPRT0_SPD_HIGH_SPEED 0 -#define HPRT0_SPD_FULL_SPEED 1 -#define HPRT0_SPD_LOW_SPEED 2 -#define HPRT0_TSTCTL_MASK (0xf << 13) -#define HPRT0_TSTCTL_SHIFT 13 -#define HPRT0_PWR BIT(12) -#define HPRT0_LNSTS_MASK (0x3 << 10) -#define HPRT0_LNSTS_SHIFT 10 -#define HPRT0_RST BIT(8) -#define HPRT0_SUSP BIT(7) -#define HPRT0_RES BIT(6) -#define HPRT0_OVRCURRCHG BIT(5) -#define HPRT0_OVRCURRACT BIT(4) -#define HPRT0_ENACHG BIT(3) -#define HPRT0_ENA BIT(2) -#define HPRT0_CONNDET BIT(1) -#define HPRT0_CONNSTS BIT(0) +#define HPRT0 HSOTG_REG(0x0440) +#define HPRT0_SPD_MASK (0x3 << 17) +#define HPRT0_SPD_SHIFT 17 +#define HPRT0_SPD_HIGH_SPEED 0 +#define HPRT0_SPD_FULL_SPEED 1 +#define HPRT0_SPD_LOW_SPEED 2 +#define HPRT0_TSTCTL_MASK (0xf << 13) +#define HPRT0_TSTCTL_SHIFT 13 +#define HPRT0_PWR BIT(12) +#define HPRT0_LNSTS_MASK (0x3 << 10) +#define HPRT0_LNSTS_SHIFT 10 +#define HPRT0_RST BIT(8) +#define HPRT0_SUSP BIT(7) +#define HPRT0_RES BIT(6) +#define HPRT0_OVRCURRCHG BIT(5) +#define HPRT0_OVRCURRACT BIT(4) +#define HPRT0_ENACHG BIT(3) +#define HPRT0_ENA BIT(2) +#define HPRT0_CONNDET BIT(1) +#define HPRT0_CONNSTS BIT(0) -#define HCCHAR(_ch) HSOTG_REG(0x0500 + 0x20 * (_ch)) -#define HCCHAR_CHENA BIT(31) -#define HCCHAR_CHDIS BIT(30) -#define HCCHAR_ODDFRM BIT(29) -#define HCCHAR_DEVADDR_MASK (0x7f << 22) -#define HCCHAR_DEVADDR_SHIFT 22 -#define HCCHAR_MULTICNT_MASK (0x3 << 20) -#define HCCHAR_MULTICNT_SHIFT 20 -#define HCCHAR_EPTYPE_MASK (0x3 << 18) -#define HCCHAR_EPTYPE_SHIFT 18 -#define HCCHAR_LSPDDEV BIT(17) -#define HCCHAR_EPDIR BIT(15) -#define HCCHAR_EPNUM_MASK (0xf << 11) -#define HCCHAR_EPNUM_SHIFT 11 -#define HCCHAR_MPS_MASK (0x7ff << 0) -#define HCCHAR_MPS_SHIFT 0 +#define HCCHAR(_ch) HSOTG_REG(0x0500 + 0x20 * (_ch)) +#define HCCHAR_CHENA BIT(31) +#define HCCHAR_CHDIS BIT(30) +#define HCCHAR_ODDFRM BIT(29) +#define HCCHAR_DEVADDR_MASK (0x7f << 22) +#define HCCHAR_DEVADDR_SHIFT 22 +#define HCCHAR_MULTICNT_MASK (0x3 << 20) +#define HCCHAR_MULTICNT_SHIFT 20 +#define HCCHAR_EPTYPE_MASK (0x3 << 18) +#define HCCHAR_EPTYPE_SHIFT 18 +#define HCCHAR_LSPDDEV BIT(17) +#define HCCHAR_EPDIR BIT(15) +#define HCCHAR_EPNUM_MASK (0xf << 11) +#define HCCHAR_EPNUM_SHIFT 11 +#define HCCHAR_MPS_MASK (0x7ff << 0) +#define HCCHAR_MPS_SHIFT 0 -#define HCSPLT(_ch) HSOTG_REG(0x0504 + 0x20 * (_ch)) -#define HCSPLT_SPLTENA BIT(31) -#define HCSPLT_COMPSPLT BIT(16) -#define HCSPLT_XACTPOS_MASK (0x3 << 14) -#define HCSPLT_XACTPOS_SHIFT 14 -#define HCSPLT_XACTPOS_MID 0 -#define HCSPLT_XACTPOS_END 1 -#define HCSPLT_XACTPOS_BEGIN 2 -#define HCSPLT_XACTPOS_ALL 3 -#define HCSPLT_HUBADDR_MASK (0x7f << 7) -#define HCSPLT_HUBADDR_SHIFT 7 -#define HCSPLT_PRTADDR_MASK (0x7f << 0) -#define HCSPLT_PRTADDR_SHIFT 0 +#define HCSPLT(_ch) HSOTG_REG(0x0504 + 0x20 * (_ch)) +#define HCSPLT_SPLTENA BIT(31) +#define HCSPLT_COMPSPLT BIT(16) +#define HCSPLT_XACTPOS_MASK (0x3 << 14) +#define HCSPLT_XACTPOS_SHIFT 14 +#define HCSPLT_XACTPOS_MID 0 +#define HCSPLT_XACTPOS_END 1 +#define HCSPLT_XACTPOS_BEGIN 2 +#define HCSPLT_XACTPOS_ALL 3 +#define HCSPLT_HUBADDR_MASK (0x7f << 7) +#define HCSPLT_HUBADDR_SHIFT 7 +#define HCSPLT_PRTADDR_MASK (0x7f << 0) +#define HCSPLT_PRTADDR_SHIFT 0 -#define HCINT(_ch) HSOTG_REG(0x0508 + 0x20 * (_ch)) -#define HCINTMSK(_ch) HSOTG_REG(0x050c + 0x20 * (_ch)) -#define HCINTMSK_RESERVED14_31 (0x3ffff << 14) -#define HCINTMSK_FRM_LIST_ROLL BIT(13) -#define HCINTMSK_XCS_XACT BIT(12) -#define HCINTMSK_BNA BIT(11) -#define HCINTMSK_DATATGLERR BIT(10) -#define HCINTMSK_FRMOVRUN BIT(9) -#define HCINTMSK_BBLERR BIT(8) -#define HCINTMSK_XACTERR BIT(7) -#define HCINTMSK_NYET BIT(6) -#define HCINTMSK_ACK BIT(5) -#define HCINTMSK_NAK BIT(4) -#define HCINTMSK_STALL BIT(3) -#define HCINTMSK_AHBERR BIT(2) -#define HCINTMSK_CHHLTD BIT(1) -#define HCINTMSK_XFERCOMPL BIT(0) +#define HCINT(_ch) HSOTG_REG(0x0508 + 0x20 * (_ch)) +#define HCINTMSK(_ch) HSOTG_REG(0x050c + 0x20 * (_ch)) +#define HCINTMSK_RESERVED14_31 (0x3ffff << 14) +#define HCINTMSK_FRM_LIST_ROLL BIT(13) +#define HCINTMSK_XCS_XACT BIT(12) +#define HCINTMSK_BNA BIT(11) +#define HCINTMSK_DATATGLERR BIT(10) +#define HCINTMSK_FRMOVRUN BIT(9) +#define HCINTMSK_BBLERR BIT(8) +#define HCINTMSK_XACTERR BIT(7) +#define HCINTMSK_NYET BIT(6) +#define HCINTMSK_ACK BIT(5) +#define HCINTMSK_NAK BIT(4) +#define HCINTMSK_STALL BIT(3) +#define HCINTMSK_AHBERR BIT(2) +#define HCINTMSK_CHHLTD BIT(1) +#define HCINTMSK_XFERCOMPL BIT(0) -#define HCTSIZ(_ch) HSOTG_REG(0x0510 + 0x20 * (_ch)) -#define TSIZ_DOPNG BIT(31) -#define TSIZ_SC_MC_PID_MASK (0x3 << 29) -#define TSIZ_SC_MC_PID_SHIFT 29 -#define TSIZ_SC_MC_PID_DATA0 0 -#define TSIZ_SC_MC_PID_DATA2 1 -#define TSIZ_SC_MC_PID_DATA1 2 -#define TSIZ_SC_MC_PID_MDATA 3 -#define TSIZ_SC_MC_PID_SETUP 3 -#define TSIZ_PKTCNT_MASK (0x3ff << 19) -#define TSIZ_PKTCNT_SHIFT 19 -#define TSIZ_NTD_MASK (0xff << 8) -#define TSIZ_NTD_SHIFT 8 -#define TSIZ_SCHINFO_MASK (0xff << 0) -#define TSIZ_SCHINFO_SHIFT 0 -#define TSIZ_XFERSIZE_MASK (0x7ffff << 0) -#define TSIZ_XFERSIZE_SHIFT 0 +#define HCTSIZ(_ch) HSOTG_REG(0x0510 + 0x20 * (_ch)) +#define TSIZ_DOPNG BIT(31) +#define TSIZ_SC_MC_PID_MASK (0x3 << 29) +#define TSIZ_SC_MC_PID_SHIFT 29 +#define TSIZ_SC_MC_PID_DATA0 0 +#define TSIZ_SC_MC_PID_DATA2 1 +#define TSIZ_SC_MC_PID_DATA1 2 +#define TSIZ_SC_MC_PID_MDATA 3 +#define TSIZ_SC_MC_PID_SETUP 3 +#define TSIZ_PKTCNT_MASK (0x3ff << 19) +#define TSIZ_PKTCNT_SHIFT 19 +#define TSIZ_NTD_MASK (0xff << 8) +#define TSIZ_NTD_SHIFT 8 +#define TSIZ_SCHINFO_MASK (0xff << 0) +#define TSIZ_SCHINFO_SHIFT 0 +#define TSIZ_XFERSIZE_MASK (0x7ffff << 0) +#define TSIZ_XFERSIZE_SHIFT 0 -#define HCDMA(_ch) HSOTG_REG(0x0514 + 0x20 * (_ch)) +#define HCDMA(_ch) HSOTG_REG(0x0514 + 0x20 * (_ch)) -#define HCDMAB(_ch) HSOTG_REG(0x051c + 0x20 * (_ch)) +#define HCDMAB(_ch) HSOTG_REG(0x051c + 0x20 * (_ch)) -#define HCFIFO(_ch) HSOTG_REG(0x1000 + 0x1000 * (_ch)) +#define HCFIFO(_ch) HSOTG_REG(0x1000 + 0x1000 * (_ch)) /** * struct dwc2_dma_desc - DMA descriptor structure, @@ -836,64 +836,64 @@ * Status quadlet and Data buffer pointer. */ struct dwc2_dma_desc { - uint32_t status; - uint32_t buf; + uint32_t status; + uint32_t buf; } __packed; /* Host Mode DMA descriptor status quadlet */ -#define HOST_DMA_A BIT(31) -#define HOST_DMA_STS_MASK (0x3 << 28) -#define HOST_DMA_STS_SHIFT 28 -#define HOST_DMA_STS_PKTERR BIT(28) -#define HOST_DMA_EOL BIT(26) -#define HOST_DMA_IOC BIT(25) -#define HOST_DMA_SUP BIT(24) -#define HOST_DMA_ALT_QTD BIT(23) -#define HOST_DMA_QTD_OFFSET_MASK (0x3f << 17) -#define HOST_DMA_QTD_OFFSET_SHIFT 17 -#define HOST_DMA_ISOC_NBYTES_MASK (0xfff << 0) -#define HOST_DMA_ISOC_NBYTES_SHIFT 0 -#define HOST_DMA_NBYTES_MASK (0x1ffff << 0) -#define HOST_DMA_NBYTES_SHIFT 0 -#define HOST_DMA_NBYTES_LIMIT 131071 +#define HOST_DMA_A BIT(31) +#define HOST_DMA_STS_MASK (0x3 << 28) +#define HOST_DMA_STS_SHIFT 28 +#define HOST_DMA_STS_PKTERR BIT(28) +#define HOST_DMA_EOL BIT(26) +#define HOST_DMA_IOC BIT(25) +#define HOST_DMA_SUP BIT(24) +#define HOST_DMA_ALT_QTD BIT(23) +#define HOST_DMA_QTD_OFFSET_MASK (0x3f << 17) +#define HOST_DMA_QTD_OFFSET_SHIFT 17 +#define HOST_DMA_ISOC_NBYTES_MASK (0xfff << 0) +#define HOST_DMA_ISOC_NBYTES_SHIFT 0 +#define HOST_DMA_NBYTES_MASK (0x1ffff << 0) +#define HOST_DMA_NBYTES_SHIFT 0 +#define HOST_DMA_NBYTES_LIMIT 131071 /* Device Mode DMA descriptor status quadlet */ -#define DEV_DMA_BUFF_STS_MASK (0x3 << 30) -#define DEV_DMA_BUFF_STS_SHIFT 30 -#define DEV_DMA_BUFF_STS_HREADY 0 -#define DEV_DMA_BUFF_STS_DMABUSY 1 -#define DEV_DMA_BUFF_STS_DMADONE 2 -#define DEV_DMA_BUFF_STS_HBUSY 3 -#define DEV_DMA_STS_MASK (0x3 << 28) -#define DEV_DMA_STS_SHIFT 28 -#define DEV_DMA_STS_SUCC 0 -#define DEV_DMA_STS_BUFF_FLUSH 1 -#define DEV_DMA_STS_BUFF_ERR 3 -#define DEV_DMA_L BIT(27) -#define DEV_DMA_SHORT BIT(26) -#define DEV_DMA_IOC BIT(25) -#define DEV_DMA_SR BIT(24) -#define DEV_DMA_MTRF BIT(23) -#define DEV_DMA_ISOC_PID_MASK (0x3 << 23) -#define DEV_DMA_ISOC_PID_SHIFT 23 -#define DEV_DMA_ISOC_PID_DATA0 0 -#define DEV_DMA_ISOC_PID_DATA2 1 -#define DEV_DMA_ISOC_PID_DATA1 2 -#define DEV_DMA_ISOC_PID_MDATA 3 -#define DEV_DMA_ISOC_FRNUM_MASK (0x7ff << 12) -#define DEV_DMA_ISOC_FRNUM_SHIFT 12 -#define DEV_DMA_ISOC_TX_NBYTES_MASK (0xfff << 0) -#define DEV_DMA_ISOC_TX_NBYTES_LIMIT 0xfff -#define DEV_DMA_ISOC_RX_NBYTES_MASK (0x7ff << 0) -#define DEV_DMA_ISOC_RX_NBYTES_LIMIT 0x7ff -#define DEV_DMA_ISOC_NBYTES_SHIFT 0 -#define DEV_DMA_NBYTES_MASK (0xffff << 0) -#define DEV_DMA_NBYTES_SHIFT 0 -#define DEV_DMA_NBYTES_LIMIT 0xffff +#define DEV_DMA_BUFF_STS_MASK (0x3 << 30) +#define DEV_DMA_BUFF_STS_SHIFT 30 +#define DEV_DMA_BUFF_STS_HREADY 0 +#define DEV_DMA_BUFF_STS_DMABUSY 1 +#define DEV_DMA_BUFF_STS_DMADONE 2 +#define DEV_DMA_BUFF_STS_HBUSY 3 +#define DEV_DMA_STS_MASK (0x3 << 28) +#define DEV_DMA_STS_SHIFT 28 +#define DEV_DMA_STS_SUCC 0 +#define DEV_DMA_STS_BUFF_FLUSH 1 +#define DEV_DMA_STS_BUFF_ERR 3 +#define DEV_DMA_L BIT(27) +#define DEV_DMA_SHORT BIT(26) +#define DEV_DMA_IOC BIT(25) +#define DEV_DMA_SR BIT(24) +#define DEV_DMA_MTRF BIT(23) +#define DEV_DMA_ISOC_PID_MASK (0x3 << 23) +#define DEV_DMA_ISOC_PID_SHIFT 23 +#define DEV_DMA_ISOC_PID_DATA0 0 +#define DEV_DMA_ISOC_PID_DATA2 1 +#define DEV_DMA_ISOC_PID_DATA1 2 +#define DEV_DMA_ISOC_PID_MDATA 3 +#define DEV_DMA_ISOC_FRNUM_MASK (0x7ff << 12) +#define DEV_DMA_ISOC_FRNUM_SHIFT 12 +#define DEV_DMA_ISOC_TX_NBYTES_MASK (0xfff << 0) +#define DEV_DMA_ISOC_TX_NBYTES_LIMIT 0xfff +#define DEV_DMA_ISOC_RX_NBYTES_MASK (0x7ff << 0) +#define DEV_DMA_ISOC_RX_NBYTES_LIMIT 0x7ff +#define DEV_DMA_ISOC_NBYTES_SHIFT 0 +#define DEV_DMA_NBYTES_MASK (0xffff << 0) +#define DEV_DMA_NBYTES_SHIFT 0 +#define DEV_DMA_NBYTES_LIMIT 0xffff -#define MAX_DMA_DESC_NUM_GENERIC 64 -#define MAX_DMA_DESC_NUM_HS_ISOC 256 +#define MAX_DMA_DESC_NUM_GENERIC 64 +#define MAX_DMA_DESC_NUM_HS_ISOC 256 #endif /* DWC2_REGS_H */ diff --git a/include/hw/usb/hcd-dwc3.h b/include/hw/usb/hcd-dwc3.h index 7c804d536d..f752a27e94 100644 --- a/include/hw/usb/hcd-dwc3.h +++ b/include/hw/usb/hcd-dwc3.h @@ -26,6 +26,7 @@ #ifndef HCD_DWC3_H #define HCD_DWC3_H +#include "hw/register.h" #include "hw/usb/hcd-xhci.h" #include "hw/usb/hcd-xhci-sysbus.h" diff --git a/include/hw/usb/hcd-musb.h b/include/hw/usb/hcd-musb.h index f30a26f7f4..4d4b1ec0fc 100644 --- a/include/hw/usb/hcd-musb.h +++ b/include/hw/usb/hcd-musb.h @@ -13,6 +13,8 @@ #ifndef HW_USB_HCD_MUSB_H #define HW_USB_HCD_MUSB_H +#include "exec/hwaddr.h" + enum musb_irq_source_e { musb_irq_suspend = 0, musb_irq_resume, diff --git a/include/hw/usb/msd.h b/include/hw/usb/msd.h index 54e9f38bda..f9fd862b52 100644 --- a/include/hw/usb/msd.h +++ b/include/hw/usb/msd.h @@ -40,6 +40,7 @@ struct MSDState { bool removable; bool commandlog; SCSIDevice *scsi_dev; + bool needs_reset; }; typedef struct MSDState MSDState; diff --git a/include/hw/usb/xlnx-usb-subsystem.h b/include/hw/usb/xlnx-usb-subsystem.h index 5b730abd84..40f9e97e09 100644 --- a/include/hw/usb/xlnx-usb-subsystem.h +++ b/include/hw/usb/xlnx-usb-subsystem.h @@ -25,6 +25,8 @@ #ifndef XLNX_USB_SUBSYSTEM_H #define XLNX_USB_SUBSYSTEM_H +#include "hw/register.h" +#include "hw/sysbus.h" #include "hw/usb/xlnx-versal-usb2-ctrl-regs.h" #include "hw/usb/hcd-dwc3.h" diff --git a/include/hw/usb/xlnx-versal-usb2-ctrl-regs.h b/include/hw/usb/xlnx-versal-usb2-ctrl-regs.h index 633bf3013a..6a502006b0 100644 --- a/include/hw/usb/xlnx-versal-usb2-ctrl-regs.h +++ b/include/hw/usb/xlnx-versal-usb2-ctrl-regs.h @@ -26,6 +26,9 @@ #ifndef XLNX_VERSAL_USB2_CTRL_REGS_H #define XLNX_VERSAL_USB2_CTRL_REGS_H +#include "hw/register.h" +#include "hw/sysbus.h" + #define TYPE_XILINX_VERSAL_USB2_CTRL_REGS "xlnx.versal-usb2-ctrl-regs" #define XILINX_VERSAL_USB2_CTRL_REGS(obj) \ diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index e573f5a9f1..b9da6c08ef 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -30,6 +30,7 @@ #include #endif #include "sysemu/sysemu.h" +#include "hw/vfio/vfio-container-base.h" #define VFIO_MSG_PREFIX "vfio %s: " @@ -61,59 +62,26 @@ typedef struct VFIORegion { typedef struct VFIOMigration { struct VFIODevice *vbasedev; VMChangeStateEntry *vm_state; - VFIORegion region; + NotifierWithReturn migration_state; uint32_t device_state; - int vm_running; - Notifier migration_state; - uint64_t pending_bytes; + int data_fd; + void *data_buffer; + size_t data_buffer_size; + uint64_t mig_flags; + uint64_t precopy_init_size; + uint64_t precopy_dirty_size; + bool initial_data_sent; } VFIOMigration; -typedef struct VFIOAddressSpace { - AddressSpace *as; - QLIST_HEAD(, VFIOContainer) containers; - QLIST_ENTRY(VFIOAddressSpace) list; -} VFIOAddressSpace; - struct VFIOGroup; typedef struct VFIOContainer { - VFIOAddressSpace *space; + VFIOContainerBase bcontainer; int fd; /* /dev/vfio/vfio, empowered by the attached groups */ - MemoryListener listener; - MemoryListener prereg_listener; unsigned iommu_type; - Error *error; - bool initialized; - bool dirty_pages_supported; - uint64_t dirty_pgsizes; - uint64_t max_dirty_bitmap_size; - unsigned long pgsizes; - unsigned int dma_max_mappings; - QLIST_HEAD(, VFIOGuestIOMMU) giommu_list; - QLIST_HEAD(, VFIOHostDMAWindow) hostwin_list; QLIST_HEAD(, VFIOGroup) group_list; - QLIST_HEAD(, VFIORamDiscardListener) vrdl_list; - QLIST_ENTRY(VFIOContainer) next; } VFIOContainer; -typedef struct VFIOGuestIOMMU { - VFIOContainer *container; - IOMMUMemoryRegion *iommu_mr; - hwaddr iommu_offset; - IOMMUNotifier n; - QLIST_ENTRY(VFIOGuestIOMMU) giommu_next; -} VFIOGuestIOMMU; - -typedef struct VFIORamDiscardListener { - VFIOContainer *container; - MemoryRegion *mr; - hwaddr offset_within_address_space; - hwaddr size; - uint64_t granularity; - RamDiscardListener listener; - QLIST_ENTRY(VFIORamDiscardListener) next; -} VFIORamDiscardListener; - typedef struct VFIOHostDMAWindow { hwaddr min_iova; hwaddr max_iova; @@ -121,11 +89,22 @@ typedef struct VFIOHostDMAWindow { QLIST_ENTRY(VFIOHostDMAWindow) hostwin_next; } VFIOHostDMAWindow; +typedef struct IOMMUFDBackend IOMMUFDBackend; + +typedef struct VFIOIOMMUFDContainer { + VFIOContainerBase bcontainer; + IOMMUFDBackend *be; + uint32_t ioas_id; +} VFIOIOMMUFDContainer; + typedef struct VFIODeviceOps VFIODeviceOps; typedef struct VFIODevice { QLIST_ENTRY(VFIODevice) next; + QLIST_ENTRY(VFIODevice) container_next; + QLIST_ENTRY(VFIODevice) global_next; struct VFIOGroup *group; + VFIOContainerBase *bcontainer; char *sysfsdev; char *name; DeviceState *dev; @@ -135,7 +114,7 @@ typedef struct VFIODevice { bool needs_reset; bool no_mmap; bool ram_block_discard_allowed; - bool enable_migration; + OnOffAuto enable_migration; VFIODeviceOps *ops; unsigned int num_irqs; unsigned int num_regions; @@ -143,6 +122,10 @@ typedef struct VFIODevice { VFIOMigration *migration; Error *migration_blocker; OnOffAuto pre_copy_dirty_page_tracking; + bool dirty_pages_supported; + bool dirty_tracking; + int devid; + IOMMUFDBackend *iommufd; } VFIODevice; struct VFIODeviceOps { @@ -190,7 +173,13 @@ typedef struct VFIODisplay { } dmabuf; } VFIODisplay; -void vfio_put_base_device(VFIODevice *vbasedev); +VFIOAddressSpace *vfio_get_address_space(AddressSpace *as); +void vfio_put_address_space(VFIOAddressSpace *space); + +/* SPAPR specific */ +int vfio_spapr_container_init(VFIOContainer *container, Error **errp); +void vfio_spapr_container_deinit(VFIOContainer *container); + void vfio_disable_irqindex(VFIODevice *vbasedev, int index); void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index); void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index); @@ -208,17 +197,33 @@ void vfio_region_unmap(VFIORegion *region); void vfio_region_exit(VFIORegion *region); void vfio_region_finalize(VFIORegion *region); void vfio_reset_handler(void *opaque); -VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp); -void vfio_put_group(VFIOGroup *group); -int vfio_get_device(VFIOGroup *group, const char *name, - VFIODevice *vbasedev, Error **errp); +struct vfio_device_info *vfio_get_device_info(int fd); +int vfio_attach_device(char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp); +void vfio_detach_device(VFIODevice *vbasedev); + +int vfio_kvm_device_add_fd(int fd, Error **errp); +int vfio_kvm_device_del_fd(int fd, Error **errp); + +int vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp); +void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer); extern const MemoryRegionOps vfio_region_ops; typedef QLIST_HEAD(VFIOGroupList, VFIOGroup) VFIOGroupList; +typedef QLIST_HEAD(VFIODeviceList, VFIODevice) VFIODeviceList; extern VFIOGroupList vfio_group_list; +extern VFIODeviceList vfio_device_list; +extern const MemoryListener vfio_memory_listener; +extern int vfio_kvm_device_fd; bool vfio_mig_active(void); +int vfio_block_multiple_devices_migration(VFIODevice *vbasedev, Error **errp); +void vfio_unblock_multiple_devices_migration(void); +bool vfio_viommu_preset(VFIODevice *vbasedev); int64_t vfio_mig_bytes_transferred(void); +void vfio_reset_bytes_transferred(void); +bool vfio_device_state_is_running(VFIODevice *vbasedev); +bool vfio_device_state_is_precopy(VFIODevice *vbasedev); #ifdef CONFIG_LINUX int vfio_get_region_info(VFIODevice *vbasedev, int index, @@ -232,16 +237,27 @@ bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, unsigned int *avail); struct vfio_info_cap_header * vfio_get_device_info_cap(struct vfio_device_info *info, uint16_t id); +struct vfio_info_cap_header * +vfio_get_cap(void *ptr, uint32_t cap_offset, uint16_t id); #endif -extern const MemoryListener vfio_prereg_listener; -int vfio_spapr_create_window(VFIOContainer *container, - MemoryRegionSection *section, - hwaddr *pgsize); -int vfio_spapr_remove_window(VFIOContainer *container, - hwaddr offset_within_address_space); +bool vfio_migration_realize(VFIODevice *vbasedev, Error **errp); +void vfio_migration_exit(VFIODevice *vbasedev); -int vfio_migration_probe(VFIODevice *vbasedev, Error **errp); -void vfio_migration_finalize(VFIODevice *vbasedev); +int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size); +bool +vfio_devices_all_running_and_mig_active(const VFIOContainerBase *bcontainer); +bool +vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer); +int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, + hwaddr size); +int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, + uint64_t size, ram_addr_t ram_addr); +/* Returns 0 on success, or a negative errno. */ +int vfio_device_get_name(VFIODevice *vbasedev, Error **errp); +void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp); +void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops, + DeviceState *dev, bool ram_discard); #endif /* HW_VFIO_VFIO_COMMON_H */ diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h new file mode 100644 index 0000000000..3582d5f97a --- /dev/null +++ b/include/hw/vfio/vfio-container-base.h @@ -0,0 +1,141 @@ +/* + * VFIO BASE CONTAINER + * + * Copyright (C) 2023 Intel Corporation. + * Copyright Red Hat, Inc. 2023 + * + * Authors: Yi Liu + * Eric Auger + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_CONTAINER_BASE_H +#define HW_VFIO_VFIO_CONTAINER_BASE_H + +#include "exec/memory.h" + +typedef struct VFIODevice VFIODevice; +typedef struct VFIOIOMMUClass VFIOIOMMUClass; + +typedef struct { + unsigned long *bitmap; + hwaddr size; + hwaddr pages; +} VFIOBitmap; + +typedef struct VFIOAddressSpace { + AddressSpace *as; + QLIST_HEAD(, VFIOContainerBase) containers; + QLIST_ENTRY(VFIOAddressSpace) list; +} VFIOAddressSpace; + +/* + * This is the base object for vfio container backends + */ +typedef struct VFIOContainerBase { + const VFIOIOMMUClass *ops; + VFIOAddressSpace *space; + MemoryListener listener; + Error *error; + bool initialized; + uint64_t dirty_pgsizes; + uint64_t max_dirty_bitmap_size; + unsigned long pgsizes; + unsigned int dma_max_mappings; + bool dirty_pages_supported; + QLIST_HEAD(, VFIOGuestIOMMU) giommu_list; + QLIST_HEAD(, VFIORamDiscardListener) vrdl_list; + QLIST_ENTRY(VFIOContainerBase) next; + QLIST_HEAD(, VFIODevice) device_list; + GList *iova_ranges; + NotifierWithReturn cpr_reboot_notifier; +} VFIOContainerBase; + +typedef struct VFIOGuestIOMMU { + VFIOContainerBase *bcontainer; + IOMMUMemoryRegion *iommu_mr; + hwaddr iommu_offset; + IOMMUNotifier n; + QLIST_ENTRY(VFIOGuestIOMMU) giommu_next; +} VFIOGuestIOMMU; + +typedef struct VFIORamDiscardListener { + VFIOContainerBase *bcontainer; + MemoryRegion *mr; + hwaddr offset_within_address_space; + hwaddr size; + uint64_t granularity; + RamDiscardListener listener; + QLIST_ENTRY(VFIORamDiscardListener) next; +} VFIORamDiscardListener; + +int vfio_container_dma_map(VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + void *vaddr, bool readonly); +int vfio_container_dma_unmap(VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + IOMMUTLBEntry *iotlb); +int vfio_container_add_section_window(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, + Error **errp); +void vfio_container_del_section_window(VFIOContainerBase *bcontainer, + MemoryRegionSection *section); +int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, + bool start); +int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, + hwaddr iova, hwaddr size); + +void vfio_container_init(VFIOContainerBase *bcontainer, + VFIOAddressSpace *space, + const VFIOIOMMUClass *ops); +void vfio_container_destroy(VFIOContainerBase *bcontainer); + + +#define TYPE_VFIO_IOMMU "vfio-iommu" +#define TYPE_VFIO_IOMMU_LEGACY TYPE_VFIO_IOMMU "-legacy" +#define TYPE_VFIO_IOMMU_SPAPR TYPE_VFIO_IOMMU "-spapr" +#define TYPE_VFIO_IOMMU_IOMMUFD TYPE_VFIO_IOMMU "-iommufd" + +/* + * VFIOContainerBase is not an abstract QOM object because it felt + * unnecessary to expose all the IOMMU backends to the QEMU machine + * and human interface. However, we can still abstract the IOMMU + * backend handlers using a QOM interface class. This provides more + * flexibility when referencing the various implementations. + */ +DECLARE_CLASS_CHECKERS(VFIOIOMMUClass, VFIO_IOMMU, TYPE_VFIO_IOMMU) + +struct VFIOIOMMUClass { + InterfaceClass parent_class; + + /* basic feature */ + int (*setup)(VFIOContainerBase *bcontainer, Error **errp); + int (*dma_map)(const VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + void *vaddr, bool readonly); + int (*dma_unmap)(const VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + IOMMUTLBEntry *iotlb); + int (*attach_device)(const char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp); + void (*detach_device)(VFIODevice *vbasedev); + /* migration feature */ + int (*set_dirty_page_tracking)(const VFIOContainerBase *bcontainer, + bool start); + int (*query_dirty_bitmap)(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, + hwaddr iova, hwaddr size); + /* PCI specific */ + int (*pci_hot_reset)(VFIODevice *vbasedev, bool single); + + /* SPAPR specific */ + int (*add_window)(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, + Error **errp); + void (*del_window)(VFIOContainerBase *bcontainer, + MemoryRegionSection *section); + void (*release)(VFIOContainerBase *bcontainer); +}; +#endif /* HW_VFIO_VFIO_CONTAINER_BASE_H */ diff --git a/include/hw/vfio/vfio.h b/include/hw/vfio/vfio.h deleted file mode 100644 index 86248f5436..0000000000 --- a/include/hw/vfio/vfio.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef HW_VFIO_H -#define HW_VFIO_H - -bool vfio_eeh_as_ok(AddressSpace *as); -int vfio_eeh_as_op(AddressSpace *as, uint32_t op); - -#endif diff --git a/include/hw/virtio/vdpa-dev.h b/include/hw/virtio/vdpa-dev.h new file mode 100644 index 0000000000..4dbf98195c --- /dev/null +++ b/include/hw/virtio/vdpa-dev.h @@ -0,0 +1,43 @@ +/* + * Vhost Vdpa Device + * + * Copyright (c) Huawei Technologies Co., Ltd. 2022. All Rights Reserved. + * + * Authors: + * Longpeng + * + * Largely based on the "vhost-user-blk.h" implemented by: + * Changpeng Liu + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#ifndef _VHOST_VDPA_DEVICE_H +#define _VHOST_VDPA_DEVICE_H + +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-vdpa.h" +#include "qom/object.h" + + +#define TYPE_VHOST_VDPA_DEVICE "vhost-vdpa-device" +OBJECT_DECLARE_SIMPLE_TYPE(VhostVdpaDevice, VHOST_VDPA_DEVICE) + +struct VhostVdpaDevice { + VirtIODevice parent_obj; + char *vhostdev; + int vhostfd; + int32_t bootindex; + uint32_t vdev_id; + uint32_t num_queues; + struct vhost_dev dev; + struct vhost_vdpa vdpa; + VirtQueue **virtqs; + uint8_t *config; + int config_size; + uint16_t queue_size; + bool started; + int (*post_init)(VhostVdpaDevice *v, Error **errp); +}; + +#endif diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index 81bf3109f8..70c2e8ffee 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -22,10 +22,22 @@ typedef enum VhostBackendType { } VhostBackendType; typedef enum VhostSetConfigType { - VHOST_SET_CONFIG_TYPE_MASTER = 0, + VHOST_SET_CONFIG_TYPE_FRONTEND = 0, VHOST_SET_CONFIG_TYPE_MIGRATION = 1, } VhostSetConfigType; +typedef enum VhostDeviceStateDirection { + /* Transfer state from back-end (device) to front-end */ + VHOST_TRANSFER_STATE_DIRECTION_SAVE = 0, + /* Transfer state from front-end to back-end (device) */ + VHOST_TRANSFER_STATE_DIRECTION_LOAD = 1, +} VhostDeviceStateDirection; + +typedef enum VhostDeviceStatePhase { + /* The device (and all its vrings) is stopped */ + VHOST_TRANSFER_STATE_PHASE_STOPPED = 0, +} VhostDeviceStatePhase; + struct vhost_inflight; struct vhost_dev; struct vhost_log; @@ -33,6 +45,8 @@ struct vhost_memory; struct vhost_vring_file; struct vhost_vring_state; struct vhost_vring_addr; +struct vhost_vring_worker; +struct vhost_worker_state; struct vhost_scsi_target; struct vhost_iotlb_msg; struct vhost_virtqueue; @@ -69,8 +83,18 @@ typedef int (*vhost_set_vring_kick_op)(struct vhost_dev *dev, struct vhost_vring_file *file); typedef int (*vhost_set_vring_call_op)(struct vhost_dev *dev, struct vhost_vring_file *file); +typedef int (*vhost_set_vring_err_op)(struct vhost_dev *dev, + struct vhost_vring_file *file); typedef int (*vhost_set_vring_busyloop_timeout_op)(struct vhost_dev *dev, struct vhost_vring_state *r); +typedef int (*vhost_attach_vring_worker_op)(struct vhost_dev *dev, + struct vhost_vring_worker *worker); +typedef int (*vhost_get_vring_worker_op)(struct vhost_dev *dev, + struct vhost_vring_worker *worker); +typedef int (*vhost_new_worker_op)(struct vhost_dev *dev, + struct vhost_worker_state *worker); +typedef int (*vhost_free_worker_op)(struct vhost_dev *dev, + struct vhost_worker_state *worker); typedef int (*vhost_set_features_op)(struct vhost_dev *dev, uint64_t features); typedef int (*vhost_get_features_op)(struct vhost_dev *dev, @@ -84,9 +108,6 @@ typedef int (*vhost_set_vring_enable_op)(struct vhost_dev *dev, typedef bool (*vhost_requires_shm_log_op)(struct vhost_dev *dev); typedef int (*vhost_migration_done_op)(struct vhost_dev *dev, char *mac_addr); -typedef bool (*vhost_backend_can_merge_op)(struct vhost_dev *dev, - uint64_t start1, uint64_t size1, - uint64_t start2, uint64_t size2); typedef int (*vhost_vsock_set_guest_cid_op)(struct vhost_dev *dev, uint64_t guest_cid); typedef int (*vhost_vsock_set_running_op)(struct vhost_dev *dev, int start); @@ -106,8 +127,7 @@ typedef int (*vhost_crypto_create_session_op)(struct vhost_dev *dev, typedef int (*vhost_crypto_close_session_op)(struct vhost_dev *dev, uint64_t session_id); -typedef bool (*vhost_backend_mem_section_filter_op)(struct vhost_dev *dev, - MemoryRegionSection *section); +typedef bool (*vhost_backend_no_private_memslots_op)(struct vhost_dev *dev); typedef int (*vhost_get_inflight_fd_op)(struct vhost_dev *dev, uint16_t queue_size, @@ -126,11 +146,26 @@ typedef int (*vhost_get_device_id_op)(struct vhost_dev *dev, uint32_t *dev_id); typedef bool (*vhost_force_iommu_op)(struct vhost_dev *dev); +typedef int (*vhost_set_config_call_op)(struct vhost_dev *dev, + int fd); + +typedef void (*vhost_reset_status_op)(struct vhost_dev *dev); + +typedef bool (*vhost_supports_device_state_op)(struct vhost_dev *dev); +typedef int (*vhost_set_device_state_fd_op)(struct vhost_dev *dev, + VhostDeviceStateDirection direction, + VhostDeviceStatePhase phase, + int fd, + int *reply_fd, + Error **errp); +typedef int (*vhost_check_device_state_op)(struct vhost_dev *dev, Error **errp); + typedef struct VhostOps { VhostBackendType backend_type; vhost_backend_init vhost_backend_init; vhost_backend_cleanup vhost_backend_cleanup; vhost_backend_memslots_limit vhost_backend_memslots_limit; + vhost_backend_no_private_memslots_op vhost_backend_no_private_memslots; vhost_net_set_backend_op vhost_net_set_backend; vhost_net_set_mtu_op vhost_net_set_mtu; vhost_scsi_set_endpoint_op vhost_scsi_set_endpoint; @@ -145,7 +180,12 @@ typedef struct VhostOps { vhost_get_vring_base_op vhost_get_vring_base; vhost_set_vring_kick_op vhost_set_vring_kick; vhost_set_vring_call_op vhost_set_vring_call; + vhost_set_vring_err_op vhost_set_vring_err; vhost_set_vring_busyloop_timeout_op vhost_set_vring_busyloop_timeout; + vhost_new_worker_op vhost_new_worker; + vhost_free_worker_op vhost_free_worker; + vhost_get_vring_worker_op vhost_get_vring_worker; + vhost_attach_vring_worker_op vhost_attach_vring_worker; vhost_set_features_op vhost_set_features; vhost_get_features_op vhost_get_features; vhost_set_backend_cap_op vhost_set_backend_cap; @@ -155,7 +195,6 @@ typedef struct VhostOps { vhost_set_vring_enable_op vhost_set_vring_enable; vhost_requires_shm_log_op vhost_requires_shm_log; vhost_migration_done_op vhost_migration_done; - vhost_backend_can_merge_op vhost_backend_can_merge; vhost_vsock_set_guest_cid_op vhost_vsock_set_guest_cid; vhost_vsock_set_running_op vhost_vsock_set_running; vhost_set_iotlb_callback_op vhost_set_iotlb_callback; @@ -164,13 +203,17 @@ typedef struct VhostOps { vhost_set_config_op vhost_set_config; vhost_crypto_create_session_op vhost_crypto_create_session; vhost_crypto_close_session_op vhost_crypto_close_session; - vhost_backend_mem_section_filter_op vhost_backend_mem_section_filter; vhost_get_inflight_fd_op vhost_get_inflight_fd; vhost_set_inflight_fd_op vhost_set_inflight_fd; vhost_dev_start_op vhost_dev_start; vhost_vq_get_addr_op vhost_vq_get_addr; vhost_get_device_id_op vhost_get_device_id; vhost_force_iommu_op vhost_force_iommu; + vhost_set_config_call_op vhost_set_config_call; + vhost_reset_status_op vhost_reset_status; + vhost_supports_device_state_op vhost_supports_device_state; + vhost_set_device_state_fd_op vhost_set_device_state_fd; + vhost_check_device_state_op vhost_check_device_state; } VhostOps; int vhost_backend_update_device_iotlb(struct vhost_dev *dev, @@ -186,4 +229,7 @@ int vhost_backend_handle_iotlb_msg(struct vhost_dev *dev, int vhost_user_gpu_set_socket(struct vhost_dev *dev, int fd); +int vhost_user_get_shared_object(struct vhost_dev *dev, unsigned char *uuid, + int *dmabuf_fd); + #endif /* VHOST_BACKEND_H */ diff --git a/include/hw/virtio/vhost-scsi-common.h b/include/hw/virtio/vhost-scsi-common.h index 18f115527c..c5d2c09455 100644 --- a/include/hw/virtio/vhost-scsi-common.h +++ b/include/hw/virtio/vhost-scsi-common.h @@ -39,7 +39,7 @@ struct VHostSCSICommon { struct vhost_inflight *inflight; }; -int vhost_scsi_common_start(VHostSCSICommon *vsc); +int vhost_scsi_common_start(VHostSCSICommon *vsc, Error **errp); void vhost_scsi_common_stop(VHostSCSICommon *vsc); char *vhost_scsi_common_get_fw_dev_path(FWPathProvider *p, BusState *bus, DeviceState *dev); diff --git a/include/hw/virtio/vhost-user-base.h b/include/hw/virtio/vhost-user-base.h new file mode 100644 index 0000000000..51d0968b89 --- /dev/null +++ b/include/hw/virtio/vhost-user-base.h @@ -0,0 +1,49 @@ +/* + * Vhost-user generic virtio device + * + * Copyright (c) 2023 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_VHOST_USER_BASE_H +#define QEMU_VHOST_USER_BASE_H + +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" + +#define TYPE_VHOST_USER_BASE "vhost-user-base" + +OBJECT_DECLARE_TYPE(VHostUserBase, VHostUserBaseClass, VHOST_USER_BASE) + +struct VHostUserBase { + VirtIODevice parent_obj; + + /* Properties */ + CharBackend chardev; + uint16_t virtio_id; + uint32_t num_vqs; + uint32_t vq_size; /* can't exceed VIRTIO_QUEUE_MAX */ + uint32_t config_size; + /* State tracking */ + VhostUserState vhost_user; + struct vhost_virtqueue *vhost_vq; + struct vhost_dev vhost_dev; + GPtrArray *vqs; + bool connected; +}; + +/* + * Needed so we can use the base realize after specialisation + * tweaks + */ +struct VHostUserBaseClass { + VirtioDeviceClass parent_class; + + DeviceRealize parent_realize; +}; + + +#define TYPE_VHOST_USER_DEVICE "vhost-user-device" + +#endif /* QEMU_VHOST_USER_BASE_H */ diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h index 7c91f15040..ea085ee1ed 100644 --- a/include/hw/virtio/vhost-user-blk.h +++ b/include/hw/virtio/vhost-user-blk.h @@ -34,7 +34,6 @@ struct VHostUserBlk { struct virtio_blk_config blkcfg; uint16_t num_queues; uint32_t queue_size; - uint32_t config_wce; struct vhost_dev dev; struct vhost_inflight *inflight; VhostUserState vhost_user; diff --git a/include/hw/virtio/vhost-user-gpio.h b/include/hw/virtio/vhost-user-gpio.h new file mode 100644 index 0000000000..5814a8400a --- /dev/null +++ b/include/hw/virtio/vhost-user-gpio.h @@ -0,0 +1,24 @@ +/* + * Vhost-user GPIO virtio device + * + * Copyright (c) 2021 Viresh Kumar + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef _QEMU_VHOST_USER_GPIO_H +#define _QEMU_VHOST_USER_GPIO_H + +#include "hw/virtio/virtio.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" +#include "hw/virtio/vhost-user-base.h" + +#define TYPE_VHOST_USER_GPIO "vhost-user-gpio-device" +OBJECT_DECLARE_SIMPLE_TYPE(VHostUserGPIO, VHOST_USER_GPIO); + +struct VHostUserGPIO { + VHostUserBase parent_obj; +}; + +#endif /* _QEMU_VHOST_USER_GPIO_H */ diff --git a/include/hw/virtio/vhost-user-i2c.h b/include/hw/virtio/vhost-user-i2c.h index 0f7acd40e3..a9b5612ad0 100644 --- a/include/hw/virtio/vhost-user-i2c.h +++ b/include/hw/virtio/vhost-user-i2c.h @@ -9,23 +9,17 @@ #ifndef QEMU_VHOST_USER_I2C_H #define QEMU_VHOST_USER_I2C_H +#include "hw/virtio/virtio.h" #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-user.h" +#include "hw/virtio/vhost-user-base.h" #define TYPE_VHOST_USER_I2C "vhost-user-i2c-device" + OBJECT_DECLARE_SIMPLE_TYPE(VHostUserI2C, VHOST_USER_I2C) struct VHostUserI2C { - VirtIODevice parent; - CharBackend chardev; - struct vhost_virtqueue *vhost_vq; - struct vhost_dev vhost_dev; - VhostUserState vhost_user; - VirtQueue *vq; - bool connected; + VHostUserBase parent_obj; }; -/* Virtio Feature bits */ -#define VIRTIO_I2C_F_ZERO_LENGTH_REQUEST 0 - #endif /* QEMU_VHOST_USER_I2C_H */ diff --git a/include/hw/virtio/vhost-user-rng.h b/include/hw/virtio/vhost-user-rng.h index ddd9f01eea..10868c7de4 100644 --- a/include/hw/virtio/vhost-user-rng.h +++ b/include/hw/virtio/vhost-user-rng.h @@ -12,22 +12,13 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-user.h" -#include "chardev/char-fe.h" +#include "hw/virtio/vhost-user-base.h" #define TYPE_VHOST_USER_RNG "vhost-user-rng" OBJECT_DECLARE_SIMPLE_TYPE(VHostUserRNG, VHOST_USER_RNG) struct VHostUserRNG { - /*< private >*/ - VirtIODevice parent; - CharBackend chardev; - struct vhost_virtqueue *vhost_vq; - struct vhost_dev vhost_dev; - VhostUserState vhost_user; - VirtQueue *req_vq; - bool connected; - - /*< public >*/ + VHostUserBase parent_obj; }; #endif /* QEMU_VHOST_USER_RNG_H */ diff --git a/include/hw/virtio/vhost-user-scmi.h b/include/hw/virtio/vhost-user-scmi.h new file mode 100644 index 0000000000..c90db77dd5 --- /dev/null +++ b/include/hw/virtio/vhost-user-scmi.h @@ -0,0 +1,31 @@ +/* + * Vhost-user SCMI virtio device + * + * Copyright (c) 2023 Red Hat, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef _QEMU_VHOST_USER_SCMI_H +#define _QEMU_VHOST_USER_SCMI_H + +#include "hw/virtio/virtio.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" + +#define TYPE_VHOST_USER_SCMI "vhost-user-scmi" +OBJECT_DECLARE_SIMPLE_TYPE(VHostUserSCMI, VHOST_USER_SCMI); + +struct VHostUserSCMI { + VirtIODevice parent; + CharBackend chardev; + struct vhost_virtqueue *vhost_vqs; + struct vhost_dev vhost_dev; + VhostUserState vhost_user; + VirtQueue *cmd_vq; + VirtQueue *event_vq; + bool connected; + bool started_vu; +}; + +#endif /* _QEMU_VHOST_USER_SCMI_H */ diff --git a/include/hw/virtio/vhost-user-scsi.h b/include/hw/virtio/vhost-user-scsi.h index 521b08e559..78fe616ccb 100644 --- a/include/hw/virtio/vhost-user-scsi.h +++ b/include/hw/virtio/vhost-user-scsi.h @@ -28,7 +28,13 @@ OBJECT_DECLARE_SIMPLE_TYPE(VHostUserSCSI, VHOST_USER_SCSI) struct VHostUserSCSI { VHostSCSICommon parent_obj; + + /* Properties */ + bool connected; + bool started_vu; + VhostUserState vhost_user; + struct vhost_virtqueue *vhost_vqs; }; #endif /* VHOST_USER_SCSI_H */ diff --git a/include/hw/virtio/vhost-user-snd.h b/include/hw/virtio/vhost-user-snd.h new file mode 100644 index 0000000000..f9260116a7 --- /dev/null +++ b/include/hw/virtio/vhost-user-snd.h @@ -0,0 +1,24 @@ +/* + * Vhost-user Sound virtio device + * + * Copyright (c) 2021 Mathieu Poirier + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_VHOST_USER_SND_H +#define QEMU_VHOST_USER_SND_H + +#include "hw/virtio/virtio.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" +#include "hw/virtio/vhost-user-base.h" + +#define TYPE_VHOST_USER_SND "vhost-user-snd" +OBJECT_DECLARE_SIMPLE_TYPE(VHostUserSound, VHOST_USER_SND) + +struct VHostUserSound { + VHostUserBase parent_obj; +}; + +#endif /* QEMU_VHOST_USER_SND_H */ diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h index c6e693cd3f..d7c09ffd34 100644 --- a/include/hw/virtio/vhost-user.h +++ b/include/hw/virtio/vhost-user.h @@ -11,6 +11,30 @@ #include "chardev/char-fe.h" #include "hw/virtio/virtio.h" +enum VhostUserProtocolFeature { + VHOST_USER_PROTOCOL_F_MQ = 0, + VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1, + VHOST_USER_PROTOCOL_F_RARP = 2, + VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, + VHOST_USER_PROTOCOL_F_NET_MTU = 4, + VHOST_USER_PROTOCOL_F_BACKEND_REQ = 5, + VHOST_USER_PROTOCOL_F_CROSS_ENDIAN = 6, + VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7, + VHOST_USER_PROTOCOL_F_PAGEFAULT = 8, + VHOST_USER_PROTOCOL_F_CONFIG = 9, + VHOST_USER_PROTOCOL_F_BACKEND_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_INBAND_NOTIFICATIONS = 14, + VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS = 15, + VHOST_USER_PROTOCOL_F_STATUS = 16, + /* Feature 17 reserved for VHOST_USER_PROTOCOL_F_XEN_MMAP. */ + VHOST_USER_PROTOCOL_F_SHARED_OBJECT = 18, + VHOST_USER_PROTOCOL_F_DEVICE_STATE = 19, + VHOST_USER_PROTOCOL_F_MAX +}; + /** * VhostUserHostNotifier - notifier information for one queue * @rcu: rcu_head for cleanup @@ -68,4 +92,23 @@ bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp); */ void vhost_user_cleanup(VhostUserState *user); +/** + * vhost_user_async_close() - cleanup vhost-user post connection drop + * @d: DeviceState for the associated device (passed to callback) + * @chardev: the CharBackend associated with the connection + * @vhost: the common vhost device + * @cb: the user callback function to complete the clean-up + * + * This function is used to handle the shutdown of a vhost-user + * connection to a backend. We handle this centrally to make sure we + * do all the steps and handle potential races due to VM shutdowns. + * Once the connection is disabled we call a backhalf to ensure + */ +typedef void (*vu_async_close_fn)(DeviceState *cb); + +void vhost_user_async_close(DeviceState *d, + CharBackend *chardev, struct vhost_dev *vhost, + vu_async_close_fn cb, + IOEventHandler *event_cb); + #endif diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h index a29dbb3f53..0a9575b469 100644 --- a/include/hw/virtio/vhost-vdpa.h +++ b/include/hw/virtio/vhost-vdpa.h @@ -15,28 +15,81 @@ #include #include "hw/virtio/vhost-iova-tree.h" +#include "hw/virtio/vhost-shadow-virtqueue.h" #include "hw/virtio/virtio.h" #include "standard-headers/linux/vhost_types.h" +/* + * ASID dedicated to map guest's addresses. If SVQ is disabled it maps GPA to + * qemu's IOVA. If SVQ is enabled it maps also the SVQ vring here + */ +#define VHOST_VDPA_GUEST_PA_ASID 0 + typedef struct VhostVDPAHostNotifier { MemoryRegion mr; void *addr; } VhostVDPAHostNotifier; -typedef struct vhost_vdpa { +typedef enum SVQTransitionState { + SVQ_TSTATE_DISABLING = -1, + SVQ_TSTATE_DONE, + SVQ_TSTATE_ENABLING +} SVQTransitionState; + +/* Info shared by all vhost_vdpa device models */ +typedef struct vhost_vdpa_shared { int device_fd; - int index; - uint32_t msg_type; - bool iotlb_batch_begin_sent; MemoryListener listener; struct vhost_vdpa_iova_range iova_range; - uint64_t acked_features; - bool shadow_vqs_enabled; + QLIST_HEAD(, vdpa_iommu) iommu_list; + /* IOVA mapping used by the Shadow Virtqueue */ VhostIOVATree *iova_tree; + + /* Copy of backend features */ + uint64_t backend_cap; + + bool iotlb_batch_begin_sent; + + /* Vdpa must send shadow addresses as IOTLB key for data queues, not GPA */ + bool shadow_data; + + /* SVQ switching is in progress, or already completed? */ + SVQTransitionState svq_switching; +} VhostVDPAShared; + +typedef struct vhost_vdpa { + int index; + uint32_t address_space_id; + uint64_t acked_features; + bool shadow_vqs_enabled; + /* Device suspended successfully */ + bool suspended; + VhostVDPAShared *shared; GPtrArray *shadow_vqs; + const VhostShadowVirtqueueOps *shadow_vq_ops; + void *shadow_vq_ops_opaque; struct vhost_dev *dev; + Error *migration_blocker; VhostVDPAHostNotifier notifier[VIRTIO_QUEUE_MAX]; + IOMMUNotifier n; } VhostVDPA; +int vhost_vdpa_get_iova_range(int fd, struct vhost_vdpa_iova_range *iova_range); +int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, unsigned idx); + +int vhost_vdpa_dma_map(VhostVDPAShared *s, uint32_t asid, hwaddr iova, + hwaddr size, void *vaddr, bool readonly); +int vhost_vdpa_dma_unmap(VhostVDPAShared *s, uint32_t asid, hwaddr iova, + hwaddr size); + +typedef struct vdpa_iommu { + VhostVDPAShared *dev_shared; + IOMMUMemoryRegion *iommu_mr; + hwaddr iommu_offset; + IOMMUNotifier n; + QLIST_ENTRY(vdpa_iommu) iommu_next; +} VDPAIOMMUState; + + #endif diff --git a/include/hw/virtio/vhost-vsock-common.h b/include/hw/virtio/vhost-vsock-common.h index 93c782101d..75a74e8a99 100644 --- a/include/hw/virtio/vhost-vsock-common.h +++ b/include/hw/virtio/vhost-vsock-common.h @@ -11,6 +11,7 @@ #ifndef QEMU_VHOST_VSOCK_COMMON_H #define QEMU_VHOST_VSOCK_COMMON_H +#include "qapi/qapi-types-common.h" #include "hw/virtio/virtio.h" #include "hw/virtio/vhost.h" #include "qom/object.h" diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index b291fe4e24..02477788df 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -5,6 +5,11 @@ #include "hw/virtio/virtio.h" #include "exec/memory.h" +#define VHOST_F_DEVICE_IOTLB 63 +#define VHOST_USER_F_PROTOCOL_FEATURES 30 + +#define VU_REALIZE_CONN_RETRIES 3 + /* Generic structures common for any vhost based device. */ struct vhost_inflight { @@ -29,6 +34,8 @@ struct vhost_virtqueue { unsigned long long used_phys; unsigned used_size; EventNotifier masked_notifier; + EventNotifier error_notifier; + EventNotifier masked_config_notifier; struct vhost_dev *dev; }; @@ -37,6 +44,7 @@ typedef unsigned long vhost_log_chunk_t; #define VHOST_LOG_BITS (8 * sizeof(vhost_log_chunk_t)) #define VHOST_LOG_CHUNK (VHOST_LOG_PAGE * VHOST_LOG_BITS) #define VHOST_INVALID_FEATURE_BIT (0xff) +#define VHOST_QUEUE_NUM_CONFIG_INR 0 struct vhost_log { unsigned long long size; @@ -84,12 +92,35 @@ struct vhost_dev { int vq_index_end; /* if non-zero, minimum required value for max_queues */ int num_queues; + /** + * vhost feature handling requires matching the feature set + * offered by a backend which may be a subset of the total + * features eventually offered to the guest. + * + * @features: available features provided by the backend + * @acked_features: final negotiated features with front-end driver + * + * @backend_features: this is used in a couple of places to either + * store VHOST_USER_F_PROTOCOL_FEATURES to apply to + * VHOST_USER_SET_FEATURES or VHOST_NET_F_VIRTIO_NET_HDR. Its + * future use should be discouraged and the variable retired as + * its easy to confuse with the VirtIO backend_features. + */ uint64_t features; uint64_t acked_features; uint64_t backend_features; + + /** + * @protocol_features: is the vhost-user only feature set by + * VHOST_USER_SET_PROTOCOL_FEATURES. Protocol features are only + * negotiated if VHOST_USER_F_PROTOCOL_FEATURES has been offered + * by the backend (see @features). + */ uint64_t protocol_features; + uint64_t max_queues; uint64_t backend_cap; + /* @started: is the vhost device started? */ bool started; bool log_enabled; uint64_t log_size; @@ -160,29 +191,44 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev); * Disable direct notifications to vhost device. */ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev); +bool vhost_config_pending(struct vhost_dev *hdev); +void vhost_config_mask(struct vhost_dev *hdev, VirtIODevice *vdev, bool mask); + +/** + * vhost_dev_is_started() - report status of vhost device + * @hdev: common vhost_dev structure + * + * Return the started status of the vhost device + */ +static inline bool vhost_dev_is_started(struct vhost_dev *hdev) +{ + return hdev->started; +} /** * vhost_dev_start() - start the vhost device * @hdev: common vhost_dev structure * @vdev: the VirtIODevice structure + * @vrings: true to have vrings enabled in this call * * Starts the vhost device. From this point VirtIO feature negotiation * can start and the device can start processing VirtIO transactions. * * Return: 0 on success, < 0 on error. */ -int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev); +int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings); /** * vhost_dev_stop() - stop the vhost device * @hdev: common vhost_dev structure * @vdev: the VirtIODevice structure + * @vrings: true to have vrings disabled in this call * * Stop the vhost device. After the device is stopped the notifiers * can be disabled (@vhost_dev_disable_notifiers) and the device can * be torn down (@vhost_dev_cleanup). */ -void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev); +void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings); /** * DOC: vhost device configuration handling @@ -246,17 +292,45 @@ bool vhost_virtqueue_pending(struct vhost_dev *hdev, int n); */ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, bool mask); + +/** + * vhost_get_features() - return a sanitised set of feature bits + * @hdev: common vhost_dev structure + * @feature_bits: pointer to terminated table of feature bits + * @features: original feature set + * + * This returns a set of features bits that is an intersection of what + * is supported by the vhost backend (hdev->features), the supported + * feature_bits and the requested feature set. + */ uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits, uint64_t features); + +/** + * vhost_ack_features() - set vhost acked_features + * @hdev: common vhost_dev structure + * @feature_bits: pointer to terminated table of feature bits + * @features: requested feature set + * + * This sets the internal hdev->acked_features to the intersection of + * the backends advertised features and the supported feature_bits. + */ void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits, uint64_t features); -bool vhost_has_free_slot(void); +unsigned int vhost_get_max_memslots(void); +unsigned int vhost_get_free_memslots(void); int vhost_net_set_backend(struct vhost_dev *hdev, struct vhost_vring_file *file); +void vhost_toggle_device_iotlb(VirtIODevice *vdev); int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write); +int vhost_virtqueue_start(struct vhost_dev *dev, struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, unsigned idx); +void vhost_virtqueue_stop(struct vhost_dev *dev, struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, unsigned idx); + void vhost_dev_reset_inflight(struct vhost_inflight *inflight); void vhost_dev_free_inflight(struct vhost_inflight *inflight); void vhost_dev_save_inflight(struct vhost_inflight *inflight, QEMUFile *f); @@ -266,4 +340,128 @@ int vhost_dev_set_inflight(struct vhost_dev *dev, struct vhost_inflight *inflight); int vhost_dev_get_inflight(struct vhost_dev *dev, uint16_t queue_size, struct vhost_inflight *inflight); +bool vhost_dev_has_iommu(struct vhost_dev *dev); + +#ifdef CONFIG_VHOST +int vhost_reset_device(struct vhost_dev *hdev); +#else +static inline int vhost_reset_device(struct vhost_dev *hdev) +{ + return -ENOSYS; +} +#endif /* CONFIG_VHOST */ + +/** + * vhost_supports_device_state(): Checks whether the back-end supports + * transferring internal device state for the purpose of migration. + * Support for this feature is required for vhost_set_device_state_fd() + * and vhost_check_device_state(). + * + * @dev: The vhost device + * + * Returns true if the device supports these commands, and false if it + * does not. + */ +bool vhost_supports_device_state(struct vhost_dev *dev); + +/** + * vhost_set_device_state_fd(): Begin transfer of internal state from/to + * the back-end for the purpose of migration. Data is to be transferred + * over a pipe according to @direction and @phase. The sending end must + * only write to the pipe, and the receiving end must only read from it. + * Once the sending end is done, it closes its FD. The receiving end + * must take this as the end-of-transfer signal and close its FD, too. + * + * @fd is the back-end's end of the pipe: The write FD for SAVE, and the + * read FD for LOAD. This function transfers ownership of @fd to the + * back-end, i.e. closes it in the front-end. + * + * The back-end may optionally reply with an FD of its own, if this + * improves efficiency on its end. In this case, the returned FD is + * stored in *reply_fd. The back-end will discard the FD sent to it, + * and the front-end must use *reply_fd for transferring state to/from + * the back-end. + * + * @dev: The vhost device + * @direction: The direction in which the state is to be transferred. + * For outgoing migrations, this is SAVE, and data is read + * from the back-end and stored by the front-end in the + * migration stream. + * For incoming migrations, this is LOAD, and data is read + * by the front-end from the migration stream and sent to + * the back-end to restore the saved state. + * @phase: Which migration phase we are in. Currently, there is only + * STOPPED (device and all vrings are stopped), in the future, + * more phases such as PRE_COPY or POST_COPY may be added. + * @fd: Back-end's end of the pipe through which to transfer state; note + * that ownership is transferred to the back-end, so this function + * closes @fd in the front-end. + * @reply_fd: If the back-end wishes to use a different pipe for state + * transfer, this will contain an FD for the front-end to + * use. Otherwise, -1 is stored here. + * @errp: Potential error description + * + * Returns 0 on success, and -errno on failure. + */ +int vhost_set_device_state_fd(struct vhost_dev *dev, + VhostDeviceStateDirection direction, + VhostDeviceStatePhase phase, + int fd, + int *reply_fd, + Error **errp); + +/** + * vhost_set_device_state_fd(): After transferring state from/to the + * back-end via vhost_set_device_state_fd(), i.e. once the sending end + * has closed the pipe, inquire the back-end to report any potential + * errors that have occurred on its side. This allows to sense errors + * like: + * - During outgoing migration, when the source side had already started + * to produce its state, something went wrong and it failed to finish + * - During incoming migration, when the received state is somehow + * invalid and cannot be processed by the back-end + * + * @dev: The vhost device + * @errp: Potential error description + * + * Returns 0 when the back-end reports successful state transfer and + * processing, and -errno when an error occurred somewhere. + */ +int vhost_check_device_state(struct vhost_dev *dev, Error **errp); + +/** + * vhost_save_backend_state(): High-level function to receive a vhost + * back-end's state, and save it in @f. Uses + * `vhost_set_device_state_fd()` to get the data from the back-end, and + * stores it in consecutive chunks that are each prefixed by their + * respective length (be32). The end is marked by a 0-length chunk. + * + * Must only be called while the device and all its vrings are stopped + * (`VHOST_TRANSFER_STATE_PHASE_STOPPED`). + * + * @dev: The vhost device from which to save the state + * @f: Migration stream in which to save the state + * @errp: Potential error message + * + * Returns 0 on success, and -errno otherwise. + */ +int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp); + +/** + * vhost_load_backend_state(): High-level function to load a vhost + * back-end's state from @f, and send it over to the back-end. Reads + * the data from @f in the format used by `vhost_save_state()`, and uses + * `vhost_set_device_state_fd()` to transfer it to the back-end. + * + * Must only be called while the device and all its vrings are stopped + * (`VHOST_TRANSFER_STATE_PHASE_STOPPED`). + * + * @dev: The vhost device to which to send the state + * @f: Migration stream from which to load the state + * @errp: Potential error message + * + * Returns 0 on success, and -errno otherwise. + */ +int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp); + #endif diff --git a/include/hw/virtio/virtio-acpi.h b/include/hw/virtio/virtio-acpi.h new file mode 100644 index 0000000000..cace2a315f --- /dev/null +++ b/include/hw/virtio/virtio-acpi.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * ACPI support for virtio + */ + +#ifndef VIRTIO_ACPI_H +#define VIRTIO_ACPI_H + +#include "exec/hwaddr.h" + +void virtio_acpi_dsdt_add(Aml *scope, const hwaddr virtio_mmio_base, + const hwaddr virtio_mmio_size, uint32_t mmio_irq, + long int start_index, int num); + +#endif diff --git a/include/hw/virtio/virtio-blk-common.h b/include/hw/virtio/virtio-blk-common.h new file mode 100644 index 0000000000..31daada3e3 --- /dev/null +++ b/include/hw/virtio/virtio-blk-common.h @@ -0,0 +1,20 @@ +/* + * Virtio Block Device common helpers + * + * Copyright IBM, Corp. 2007 + * + * 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 VIRTIO_BLK_COMMON_H +#define VIRTIO_BLK_COMMON_H + +#include "hw/virtio/virtio.h" + +extern const VirtIOConfigSizeParams virtio_blk_cfg_size_params; + +#endif diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h index d311c57cca..5c14110c4b 100644 --- a/include/hw/virtio/virtio-blk.h +++ b/include/hw/virtio/virtio-blk.h @@ -19,7 +19,9 @@ #include "hw/block/block.h" #include "sysemu/iothread.h" #include "sysemu/block-backend.h" +#include "sysemu/block-ram-registrar.h" #include "qom/object.h" +#include "qapi/qapi-types-virtio.h" #define TYPE_VIRTIO_BLK "virtio-blk-device" OBJECT_DECLARE_SIMPLE_TYPE(VirtIOBlock, VIRTIO_BLK) @@ -36,6 +38,7 @@ struct VirtIOBlkConf { BlockConf conf; IOThread *iothread; + IOThreadVirtQueueMappingList *iothread_vq_mapping_list; char *serial; uint32_t request_merging; uint16_t num_queues; @@ -47,23 +50,30 @@ struct VirtIOBlkConf bool x_enable_wce_if_config_wce; }; -struct VirtIOBlockDataPlane; - struct VirtIOBlockReq; struct VirtIOBlock { VirtIODevice parent_obj; BlockBackend *blk; - void *rq; - QEMUBH *bh; + QemuMutex rq_lock; + struct VirtIOBlockReq *rq; /* protected by rq_lock */ VirtIOBlkConf conf; unsigned short sector_mask; bool original_wce; VMChangeStateEntry *change; - bool dataplane_disabled; - bool dataplane_started; - struct VirtIOBlockDataPlane *dataplane; + bool ioeventfd_disabled; + bool ioeventfd_started; + bool ioeventfd_starting; + bool ioeventfd_stopping; + + /* + * The AioContext for each virtqueue. The BlockDriverState will use the + * first element as its AioContext. + */ + AioContext **vq_aio_context; + uint64_t host_features; size_t config_size; + BlockRAMRegistrar blk_ram_registrar; }; typedef struct VirtIOBlockReq { @@ -91,6 +101,5 @@ typedef struct MultiReqBuffer { } MultiReqBuffer; void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq); -void virtio_blk_process_queued_requests(VirtIOBlock *s, bool is_bh); #endif diff --git a/include/hw/virtio/virtio-crypto.h b/include/hw/virtio/virtio-crypto.h index a2228d7b2e..348749f5d5 100644 --- a/include/hw/virtio/virtio-crypto.h +++ b/include/hw/virtio/virtio-crypto.h @@ -50,6 +50,7 @@ typedef struct VirtIOCryptoConf { uint32_t mac_algo_l; uint32_t mac_algo_h; uint32_t aead_algo; + uint32_t akcipher_algo; /* Maximum length of cipher key */ uint32_t max_cipher_key_len; @@ -71,9 +72,7 @@ typedef struct VirtIOCryptoReq { size_t in_len; VirtQueue *vq; struct VirtIOCrypto *vcrypto; - union { - CryptoDevBackendSymOpInfo *sym_op_info; - } u; + CryptoDevBackendOpInfo op_info; } VirtIOCryptoReq; typedef struct VirtIOCryptoQueue { diff --git a/include/hw/virtio/virtio-dmabuf.h b/include/hw/virtio/virtio-dmabuf.h new file mode 100644 index 0000000000..627c3b6db7 --- /dev/null +++ b/include/hw/virtio/virtio-dmabuf.h @@ -0,0 +1,100 @@ +/* + * Virtio Shared dma-buf + * + * Copyright Red Hat, Inc. 2023 + * + * Authors: + * Albert Esteve + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#ifndef VIRTIO_DMABUF_H +#define VIRTIO_DMABUF_H + +#include "qemu/uuid.h" +#include "vhost.h" + +typedef enum SharedObjectType { + TYPE_INVALID = 0, + TYPE_DMABUF, + TYPE_VHOST_DEV, +} SharedObjectType; + +typedef struct VirtioSharedObject { + SharedObjectType type; + gpointer value; +} VirtioSharedObject; + +/** + * virtio_add_dmabuf() - Add a new dma-buf resource to the lookup table + * @uuid: new resource's UUID + * @dmabuf_fd: the dma-buf descriptor that will be stored and shared with + * other virtio devices. The caller retains ownership over the + * descriptor and its lifecycle. + * + * Note: @dmabuf_fd must be a valid (non-negative) file descriptor. + * + * Return: true if the UUID did not exist and the resource has been added, + * false if another resource with the same UUID already existed. + * Note that if it finds a repeated UUID, the resource is not inserted in + * the lookup table. + */ +bool virtio_add_dmabuf(QemuUUID *uuid, int dmabuf_fd); + +/** + * virtio_add_vhost_device() - Add a new exporter vhost device that holds the + * resource with the associated UUID + * @uuid: new resource's UUID + * @dev: the pointer to the vhost device that holds the resource. The caller + * retains ownership over the device struct and its lifecycle. + * + * Return: true if the UUID did not exist and the device has been tracked, + * false if another resource with the same UUID already existed. + * Note that if it finds a repeated UUID, the resource is not inserted in + * the lookup table. + */ +bool virtio_add_vhost_device(QemuUUID *uuid, struct vhost_dev *dev); + +/** + * virtio_remove_resource() - Removes a resource from the lookup table + * @uuid: resource's UUID + * + * Return: true if the UUID has been found and removed from the lookup table. + */ +bool virtio_remove_resource(const QemuUUID *uuid); + +/** + * virtio_lookup_dmabuf() - Looks for a dma-buf resource in the lookup table + * @uuid: resource's UUID + * + * Return: the dma-buf file descriptor integer, or -1 if the key is not found. + */ +int virtio_lookup_dmabuf(const QemuUUID *uuid); + +/** + * virtio_lookup_vhost_device() - Looks for an exporter vhost device in the + * lookup table + * @uuid: resource's UUID + * + * Return: pointer to the vhost_dev struct, or NULL if the key is not found. + */ +struct vhost_dev *virtio_lookup_vhost_device(const QemuUUID *uuid); + +/** + * virtio_object_type() - Looks for the type of resource in the lookup table + * @uuid: resource's UUID + * + * Return: the type of resource associated with the UUID, or TYPE_INVALID if + * the key is not found. + */ +SharedObjectType virtio_object_type(const QemuUUID *uuid); + +/** + * virtio_free_resources() - Destroys all keys and values of the shared + * resources lookup table, and frees them + */ +void virtio_free_resources(void); + +#endif /* VIRTIO_DMABUF_H */ diff --git a/include/hw/virtio/virtio-gpu-bswap.h b/include/hw/virtio/virtio-gpu-bswap.h index 9124108485..dd1975e2d4 100644 --- a/include/hw/virtio/virtio-gpu-bswap.h +++ b/include/hw/virtio/virtio-gpu-bswap.h @@ -63,10 +63,28 @@ virtio_gpu_create_blob_bswap(struct virtio_gpu_resource_create_blob *cblob) { virtio_gpu_ctrl_hdr_bswap(&cblob->hdr); le32_to_cpus(&cblob->resource_id); + le32_to_cpus(&cblob->blob_mem); le32_to_cpus(&cblob->blob_flags); + le32_to_cpus(&cblob->nr_entries); + le64_to_cpus(&cblob->blob_id); le64_to_cpus(&cblob->size); } +static inline void +virtio_gpu_map_blob_bswap(struct virtio_gpu_resource_map_blob *mblob) +{ + virtio_gpu_ctrl_hdr_bswap(&mblob->hdr); + le32_to_cpus(&mblob->resource_id); + le64_to_cpus(&mblob->offset); +} + +static inline void +virtio_gpu_unmap_blob_bswap(struct virtio_gpu_resource_unmap_blob *ublob) +{ + virtio_gpu_ctrl_hdr_bswap(&ublob->hdr); + le32_to_cpus(&ublob->resource_id); +} + static inline void virtio_gpu_scanout_blob_bswap(struct virtio_gpu_set_scanout_blob *ssb) { diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index afff9e158e..ed44cdad6b 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -38,6 +38,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(VirtIOGPUGL, VIRTIO_GPU_GL) #define TYPE_VHOST_USER_GPU "vhost-user-gpu" OBJECT_DECLARE_SIMPLE_TYPE(VhostUserGPU, VHOST_USER_GPU) +#define TYPE_VIRTIO_GPU_RUTABAGA "virtio-gpu-rutabaga-device" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIOGPURutabaga, VIRTIO_GPU_RUTABAGA) + struct virtio_gpu_simple_resource { uint32_t resource_id; uint32_t width; @@ -48,6 +51,9 @@ struct virtio_gpu_simple_resource { unsigned int iov_cnt; uint32_t scanout_bitmask; pixman_image_t *image; +#ifdef WIN32 + HANDLE handle; +#endif uint64_t hostmem; uint64_t blob_size; @@ -75,11 +81,13 @@ struct virtio_gpu_scanout { uint32_t resource_id; struct virtio_gpu_update_cursor cursor; QEMUCursor *current_cursor; + struct virtio_gpu_framebuffer fb; }; struct virtio_gpu_requested_state { uint16_t width_mm, height_mm; uint32_t width, height; + uint32_t refresh_rate; int x, y; }; @@ -89,6 +97,8 @@ enum virtio_gpu_base_conf_flags { VIRTIO_GPU_FLAG_EDID_ENABLED, VIRTIO_GPU_FLAG_DMABUF_ENABLED, VIRTIO_GPU_FLAG_BLOB_ENABLED, + VIRTIO_GPU_FLAG_CONTEXT_INIT_ENABLED, + VIRTIO_GPU_FLAG_RUTABAGA_ENABLED, }; #define virtio_gpu_virgl_enabled(_cfg) \ @@ -101,12 +111,19 @@ enum virtio_gpu_base_conf_flags { (_cfg.flags & (1 << VIRTIO_GPU_FLAG_DMABUF_ENABLED)) #define virtio_gpu_blob_enabled(_cfg) \ (_cfg.flags & (1 << VIRTIO_GPU_FLAG_BLOB_ENABLED)) +#define virtio_gpu_context_init_enabled(_cfg) \ + (_cfg.flags & (1 << VIRTIO_GPU_FLAG_CONTEXT_INIT_ENABLED)) +#define virtio_gpu_rutabaga_enabled(_cfg) \ + (_cfg.flags & (1 << VIRTIO_GPU_FLAG_RUTABAGA_ENABLED)) +#define virtio_gpu_hostmem_enabled(_cfg) \ + (_cfg.hostmem > 0) struct virtio_gpu_base_conf { uint32_t max_outputs; uint32_t flags; uint32_t xres; uint32_t yres; + uint64_t hostmem; }; struct virtio_gpu_ctrl_command { @@ -130,6 +147,8 @@ struct VirtIOGPUBase { int renderer_blocked; int enable; + MemoryRegion hostmem; + struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS]; int enabled_output_bitmask; @@ -165,6 +184,9 @@ struct VirtIOGPU { QEMUBH *ctrl_bh; QEMUBH *cursor_bh; + QEMUBH *reset_bh; + QemuCond reset_cond; + bool reset_finished; QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist; QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq; @@ -198,6 +220,9 @@ struct VirtIOGPUClass { void (*update_cursor_data)(VirtIOGPU *g, struct virtio_gpu_scanout *s, uint32_t resource_id); + void (*resource_destroy)(VirtIOGPU *g, + struct virtio_gpu_simple_resource *res, + Error **errp); }; struct VirtIOGPUGL { @@ -217,14 +242,35 @@ struct VhostUserGPU { bool backend_blocked; }; +#define MAX_SLOTS 4096 + +struct MemoryRegionInfo { + int used; + MemoryRegion mr; + uint32_t resource_id; +}; + +struct rutabaga; + +struct VirtIOGPURutabaga { + VirtIOGPU parent_obj; + struct MemoryRegionInfo memory_regions[MAX_SLOTS]; + uint64_t capset_mask; + char *wayland_socket_path; + char *wsi; + bool headless; + uint32_t num_capsets; + struct rutabaga *rutabaga; +}; + #define VIRTIO_GPU_FILL_CMD(out) do { \ - size_t s; \ - s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \ + size_t virtiogpufillcmd_s_ = \ + iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \ &out, sizeof(out)); \ - if (s != sizeof(out)) { \ + if (virtiogpufillcmd_s_ != sizeof(out)) { \ qemu_log_mask(LOG_GUEST_ERROR, \ "%s: command size incorrect %zu vs %zu\n", \ - __func__, s, sizeof(out)); \ + __func__, virtiogpufillcmd_s_, sizeof(out)); \ return; \ } \ } while (0) @@ -234,11 +280,17 @@ bool virtio_gpu_base_device_realize(DeviceState *qdev, VirtIOHandleOutput ctrl_cb, VirtIOHandleOutput cursor_cb, Error **errp); +void virtio_gpu_base_device_unrealize(DeviceState *qdev); void virtio_gpu_base_reset(VirtIOGPUBase *g); void virtio_gpu_base_fill_display_info(VirtIOGPUBase *g, struct virtio_gpu_resp_display_info *dpy_info); +void virtio_gpu_base_generate_edid(VirtIOGPUBase *g, int scanout, + struct virtio_gpu_resp_edid *edid); /* virtio-gpu.c */ +struct virtio_gpu_simple_resource * +virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id); + void virtio_gpu_ctrl_response(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd, struct virtio_gpu_ctrl_hdr *resp, @@ -257,6 +309,8 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g, uint32_t *niov); void virtio_gpu_cleanup_mapping_iov(VirtIOGPU *g, struct iovec *iov, uint32_t count); +void virtio_gpu_cleanup_mapping(VirtIOGPU *g, + struct virtio_gpu_simple_resource *res); void virtio_gpu_process_cmdq(VirtIOGPU *g); void virtio_gpu_device_realize(DeviceState *qdev, Error **errp); void virtio_gpu_reset(VirtIODevice *vdev); diff --git a/include/hw/virtio/virtio-input.h b/include/hw/virtio/virtio-input.h index f2da63d309..e69c0aeca3 100644 --- a/include/hw/virtio/virtio-input.h +++ b/include/hw/virtio/virtio-input.h @@ -1,6 +1,8 @@ #ifndef QEMU_VIRTIO_INPUT_H #define QEMU_VIRTIO_INPUT_H +#include "hw/virtio/vhost-user.h" +#include "hw/virtio/vhost-user-base.h" #include "ui/input.h" #include "sysemu/vhost-user-backend.h" @@ -24,10 +26,11 @@ OBJECT_DECLARE_TYPE(VirtIOInput, VirtIOInputClass, #define VIRTIO_INPUT_GET_PARENT_CLASS(obj) \ OBJECT_GET_PARENT_CLASS(obj, TYPE_VIRTIO_INPUT) -#define TYPE_VIRTIO_INPUT_HID "virtio-input-hid-device" -#define TYPE_VIRTIO_KEYBOARD "virtio-keyboard-device" -#define TYPE_VIRTIO_MOUSE "virtio-mouse-device" -#define TYPE_VIRTIO_TABLET "virtio-tablet-device" +#define TYPE_VIRTIO_INPUT_HID "virtio-input-hid-device" +#define TYPE_VIRTIO_KEYBOARD "virtio-keyboard-device" +#define TYPE_VIRTIO_MOUSE "virtio-mouse-device" +#define TYPE_VIRTIO_TABLET "virtio-tablet-device" +#define TYPE_VIRTIO_MULTITOUCH "virtio-multitouch-device" OBJECT_DECLARE_SIMPLE_TYPE(VirtIOInputHID, VIRTIO_INPUT_HID) #define VIRTIO_INPUT_HID_GET_PARENT_CLASS(obj) \ @@ -83,7 +86,7 @@ struct VirtIOInputHID { VirtIOInput parent_obj; char *display; uint32_t head; - QemuInputHandler *handler; + const QemuInputHandler *handler; QemuInputHandlerState *hs; int ledstate; bool wheel_axis; @@ -96,9 +99,7 @@ struct VirtIOInputHost { }; struct VHostUserInput { - VirtIOInput parent_obj; - - VhostUserBackend *vhost; + VHostUserBase parent_obj; }; void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event); diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index 84391f8448..83a52cc446 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -24,6 +24,7 @@ #include "hw/virtio/virtio.h" #include "hw/pci/pci.h" #include "qom/object.h" +#include "qapi/qapi-types-virtio.h" #define TYPE_VIRTIO_IOMMU "virtio-iommu-device" #define TYPE_VIRTIO_IOMMU_PCI "virtio-iommu-pci" @@ -37,6 +38,11 @@ typedef struct IOMMUDevice { int devfn; IOMMUMemoryRegion iommu_mr; AddressSpace as; + MemoryRegion root; /* The root container of the device */ + MemoryRegion bypass_mr; /* The alias of shared memory MR */ + GList *resv_regions; + GList *host_resv_ranges; + bool probe_done; } IOMMUDevice; typedef struct IOMMUPciBus { @@ -53,12 +59,16 @@ struct VirtIOIOMMU { GHashTable *as_by_busptr; IOMMUPciBus *iommu_pcibus_by_bus_num[PCI_BUS_MAX]; PCIBus *primary_bus; - ReservedRegion *reserved_regions; - uint32_t nb_reserved_regions; + ReservedRegion *prop_resv_regions; + uint32_t nr_prop_resv_regions; GTree *domains; - QemuMutex mutex; + QemuRecMutex mutex; GTree *endpoints; bool boot_bypass; + Notifier machine_done; + bool granule_frozen; + GranuleMode granule_mode; + uint8_t aw_bits; }; #endif diff --git a/include/hw/virtio/virtio-md-pci.h b/include/hw/virtio/virtio-md-pci.h new file mode 100644 index 0000000000..5912e16674 --- /dev/null +++ b/include/hw/virtio/virtio-md-pci.h @@ -0,0 +1,44 @@ +/* + * Abstract virtio based memory device + * + * Copyright (C) 2023 Red Hat, Inc. + * + * Authors: + * David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_VIRTIO_MD_PCI_H +#define HW_VIRTIO_MD_PCI_H + +#include "hw/virtio/virtio-pci.h" +#include "qom/object.h" + +/* + * virtio-md-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_MD_PCI "virtio-md-pci" + +OBJECT_DECLARE_TYPE(VirtIOMDPCI, VirtIOMDPCIClass, VIRTIO_MD_PCI) + +struct VirtIOMDPCIClass { + /* private */ + VirtioPCIClass parent; + + /* public */ + void (*unplug_request_check)(VirtIOMDPCI *vmd, Error **errp); +}; + +struct VirtIOMDPCI { + VirtIOPCIProxy parent_obj; +}; + +void virtio_md_pci_pre_plug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp); +void virtio_md_pci_plug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp); +void virtio_md_pci_unplug_request(VirtIOMDPCI *vmd, MachineState *ms, + Error **errp); +void virtio_md_pci_unplug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp); + +#endif diff --git a/include/hw/virtio/virtio-mem.h b/include/hw/virtio/virtio-mem.h index 7745cfc1a3..5f5b02b8f9 100644 --- a/include/hw/virtio/virtio-mem.h +++ b/include/hw/virtio/virtio-mem.h @@ -31,7 +31,9 @@ OBJECT_DECLARE_TYPE(VirtIOMEM, VirtIOMEMClass, #define VIRTIO_MEM_BLOCK_SIZE_PROP "block-size" #define VIRTIO_MEM_ADDR_PROP "memaddr" #define VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP "unplugged-inaccessible" +#define VIRTIO_MEM_EARLY_MIGRATION_PROP "x-early-migration" #define VIRTIO_MEM_PREALLOC_PROP "prealloc" +#define VIRTIO_MEM_DYNAMIC_MEMSLOTS_PROP "dynamic-memslots" struct VirtIOMEM { VirtIODevice parent_obj; @@ -43,7 +45,28 @@ struct VirtIOMEM { int32_t bitmap_size; unsigned long *bitmap; - /* assigned memory backend and memory region */ + /* + * With "dynamic-memslots=on": Device memory region in which we dynamically + * map the memslots. + */ + MemoryRegion *mr; + + /* + * With "dynamic-memslots=on": The individual memslots (aliases into the + * memory backend). + */ + MemoryRegion *memslots; + + /* With "dynamic-memslots=on": The total number of memslots. */ + uint16_t nb_memslots; + + /* + * With "dynamic-memslots=on": Size of one memslot (the size of the + * last one can differ). + */ + uint64_t memslot_size; + + /* Assigned memory backend with the RAM memory region. */ HostMemoryBackend *memdev; /* NUMA node */ @@ -74,6 +97,19 @@ struct VirtIOMEM { /* whether to prealloc memory when plugging new blocks */ bool prealloc; + /* + * Whether we migrate properties that are immutable while migration is + * active early, before state of other devices and especially, before + * migrating any RAM content. + */ + bool early_migration; + + /* + * Whether we dynamically map (multiple, if possible) memslots instead of + * statically mapping the whole RAM memory region. + */ + bool dynamic_memslots; + /* notifiers to notify when "size" changes */ NotifierList size_change_notifiers; @@ -88,8 +124,11 @@ struct VirtIOMEMClass { /* public */ void (*fill_device_info)(const VirtIOMEM *vmen, VirtioMEMDeviceInfo *vi); MemoryRegion *(*get_memory_region)(VirtIOMEM *vmem, Error **errp); + void (*decide_memslots)(VirtIOMEM *vmem, unsigned int limit); + unsigned int (*get_memslots)(VirtIOMEM *vmem); void (*add_size_change_notifier)(VirtIOMEM *vmem, Notifier *notifier); void (*remove_size_change_notifier)(VirtIOMEM *vmem, Notifier *notifier); + void (*unplug_request_check)(VirtIOMEM *vmem, Error **errp); }; #endif diff --git a/include/hw/virtio/virtio-mmio.h b/include/hw/virtio/virtio-mmio.h index 090f7730e7..aa49262022 100644 --- a/include/hw/virtio/virtio-mmio.h +++ b/include/hw/virtio/virtio-mmio.h @@ -22,8 +22,8 @@ #ifndef HW_VIRTIO_MMIO_H #define HW_VIRTIO_MMIO_H +#include "hw/sysbus.h" #include "hw/virtio/virtio-bus.h" -#include "qom/object.h" /* QOM macros */ /* virtio-mmio-bus */ diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index eb87032627..060c23c04d 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -35,6 +35,15 @@ OBJECT_DECLARE_SIMPLE_TYPE(VirtIONet, VIRTIO_NET) * and latency. */ #define TX_BURST 256 +/* Maximum VIRTIO_NET_CTRL_MAC_TABLE_SET unicast + multicast entries. */ +#define MAC_TABLE_ENTRIES 64 + +/* + * The maximum number of VLANs in the VLAN filter table + * added by VIRTIO_NET_CTRL_VLAN_ADD + */ +#define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ + typedef struct virtio_net_conf { uint32_t txtimer; @@ -106,7 +115,7 @@ typedef struct VirtioNetRscSeg { size_t size; uint16_t packets; uint16_t dup_ack; - bool is_coalesced; /* need recal ipv4 header checksum, mark here */ + bool is_coalesced; /* need recall ipv4 header checksum, mark here */ VirtioNetRscUnit unit; NetClientState *nc; } VirtioNetRscSeg; @@ -212,13 +221,20 @@ struct VirtIONet { DeviceListener primary_listener; QDict *primary_opts; bool primary_opts_from_json; - Notifier migration_state; + NotifierWithReturn migration_state; VirtioNetRssData rss_data; struct NetRxPkt *rx_pkt; struct EBPFRSSContext ebpf_rss; + uint32_t nr_ebpf_rss_fds; + char **ebpf_rss_fds; }; +size_t virtio_net_handle_ctrl_iov(VirtIODevice *vdev, + const struct iovec *in_sg, unsigned in_num, + const struct iovec *out_sg, + unsigned out_num); void virtio_net_set_netclient_name(VirtIONet *n, const char *name, const char *type); +uint64_t virtio_net_supported_guest_offloads(const VirtIONet *n); #endif diff --git a/include/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h index 2446dcd9ae..59d88018c1 100644 --- a/include/hw/virtio/virtio-pci.h +++ b/include/hw/virtio/virtio-pci.h @@ -117,6 +117,11 @@ typedef struct VirtIOPCIRegion { typedef struct VirtIOPCIQueue { uint16_t num; bool enabled; + /* + * No need to migrate the reset status, because it is always 0 + * when the migration starts. + */ + bool reset; uint32_t desc[2]; uint32_t avail[2]; uint32_t used[2]; @@ -146,6 +151,8 @@ struct VirtIOPCIProxy { bool disable_modern; bool ignore_backend_features; OnOffAuto disable_legacy; + /* Transitional device id */ + uint16_t trans_devid; uint32_t class_code; uint32_t nvectors; uint32_t dfselect; @@ -179,6 +186,9 @@ static inline void virtio_pci_disable_modern(VirtIOPCIProxy *proxy) proxy->disable_modern = true; } +uint16_t virtio_pci_get_trans_devid(uint16_t device_id); +uint16_t virtio_pci_get_class_id(uint16_t device_id); + /* * virtio-input-pci: This extends VirtioPCIProxy. */ @@ -236,6 +246,7 @@ typedef struct VirtioPCIDeviceTypeInfo { size_t instance_size; size_t class_size; void (*instance_init)(Object *obj); + void (*instance_finalize)(Object *obj); void (*class_init)(ObjectClass *klass, void *data); InterfaceInfo *interfaces; } VirtioPCIDeviceTypeInfo; @@ -251,5 +262,11 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t); * @fixed_queues. */ unsigned virtio_pci_optimal_num_queues(unsigned fixed_queues); +void virtio_pci_set_guest_notifier_fd_handler(VirtIODevice *vdev, VirtQueue *vq, + int n, bool assign, + bool with_irqfd); + +int virtio_pci_add_shm_cap(VirtIOPCIProxy *proxy, uint8_t bar, uint64_t offset, + uint64_t length, uint8_t id); #endif diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index a36aad9c86..7be0105918 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -20,7 +20,6 @@ #define VIRTIO_SCSI_SENSE_SIZE 0 #include "standard-headers/linux/virtio_scsi.h" #include "hw/virtio/virtio.h" -#include "hw/pci/pci.h" #include "hw/scsi/scsi.h" #include "chardev/char-fe.h" #include "sysemu/iothread.h" @@ -52,6 +51,7 @@ typedef struct virtio_scsi_config VirtIOSCSIConfig; struct VirtIOSCSIConf { uint32_t num_queues; uint32_t virtqueue_size; + bool worker_per_virtqueue; bool seg_max_adjust; uint32_t max_sectors; uint32_t cmd_per_lun; @@ -75,13 +75,23 @@ struct VirtIOSCSICommon { VirtQueue **cmd_vqs; }; +struct VirtIOSCSIReq; + struct VirtIOSCSI { VirtIOSCSICommon parent_obj; SCSIBus bus; - int resetting; + int resetting; /* written from main loop thread, read from any thread */ bool events_dropped; + /* + * TMFs deferred to main loop BH. These fields are protected by + * tmf_bh_lock. + */ + QemuMutex tmf_bh_lock; + QEMUBH *tmf_bh; + QTAILQ_HEAD(, VirtIOSCSIReq) tmf_bh_list; + /* Fields for dataplane below */ AioContext *ctx; /* one iothread per virtio-scsi-pci for now */ @@ -92,20 +102,6 @@ struct VirtIOSCSI { uint32_t host_features; }; -static inline void virtio_scsi_acquire(VirtIOSCSI *s) -{ - if (s->ctx) { - aio_context_acquire(s->ctx); - } -} - -static inline void virtio_scsi_release(VirtIOSCSI *s) -{ - if (s->ctx) { - aio_context_release(s->ctx); - } -} - void virtio_scsi_common_realize(DeviceState *dev, VirtIOHandleOutput ctrl, VirtIOHandleOutput evt, diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index db1c0ddf6b..7d5ffdc145 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -22,10 +22,15 @@ #include "standard-headers/linux/virtio_config.h" #include "standard-headers/linux/virtio_ring.h" #include "qom/object.h" -#include "hw/virtio/vhost.h" +#include "block/aio.h" -/* A guest should never accept this. It implies negotiation is broken. */ -#define VIRTIO_F_BAD_FEATURE 30 +/* + * A guest should never accept this. It implies negotiation is broken + * between the driver frontend and the device. This bit is re-used for + * vhost-user to advertise VHOST_USER_F_PROTOCOL_FEATURES between QEMU + * and a vhost-user backend. + */ +#define VIRTIO_F_BAD_FEATURE 30 #define VIRTIO_LEGACY_FEATURES ((0x1ULL << VIRTIO_F_BAD_FEATURE) | \ (0x1ULL << VIRTIO_F_NOTIFY_ON_EMPTY) | \ @@ -44,8 +49,14 @@ typedef struct VirtIOFeature { size_t end; } VirtIOFeature; -size_t virtio_feature_get_config_size(const VirtIOFeature *features, - uint64_t host_features); +typedef struct VirtIOConfigSizeParams { + size_t min_size; + size_t max_size; + const VirtIOFeature *feature_sizes; +} VirtIOConfigSizeParams; + +size_t virtio_get_config_size(const VirtIOConfigSizeParams *params, + uint64_t host_features); typedef struct VirtQueue VirtQueue; @@ -68,15 +79,29 @@ typedef struct VirtQueueElement #define VIRTIO_NO_VECTOR 0xffff +/* special index value used internally for config irqs */ +#define VIRTIO_CONFIG_IRQ_IDX -1 + #define TYPE_VIRTIO_DEVICE "virtio-device" OBJECT_DECLARE_TYPE(VirtIODevice, VirtioDeviceClass, VIRTIO_DEVICE) +typedef struct { + int virtio_bit; + const char *feature_desc; +} qmp_virtio_feature_map_t; + enum virtio_device_endian { VIRTIO_DEVICE_ENDIAN_UNKNOWN, VIRTIO_DEVICE_ENDIAN_LITTLE, VIRTIO_DEVICE_ENDIAN_BIG, }; +/** + * struct VirtIODevice - common VirtIO structure + * @name: name of the device + * @status: VirtIO Device Status field + * + */ struct VirtIODevice { DeviceState parent_obj; @@ -84,9 +109,20 @@ struct VirtIODevice uint8_t status; uint8_t isr; uint16_t queue_sel; - uint64_t guest_features; + /** + * These fields represent a set of VirtIO features at various + * levels of the stack. @host_features indicates the complete + * feature set the VirtIO device can offer to the driver. + * @guest_features indicates which features the VirtIO driver has + * selected by writing to the feature register. Finally + * @backend_features represents everything supported by the + * backend (e.g. vhost) and could potentially be a subset of the + * total feature set offered by QEMU. + */ uint64_t host_features; + uint64_t guest_features; uint64_t backend_features; + size_t config_len; void *config; uint16_t config_vector; @@ -95,10 +131,18 @@ struct VirtIODevice VirtQueue *vq; MemoryListener listener; uint16_t device_id; + /* @vm_running: current VM running state via virtio_vmstate_change() */ 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 */ + /** + * @use_started: true if the @started flag should be used to check the + * current state of the VirtIO device. Otherwise status bits + * should be checked for a current status of the device. + * @use_started is only set via QMP and defaults to true for all + * modern machines (since 4.1). + */ bool use_started; bool started; bool start_on_kick; /* when virtio 1.0 feature has not been negotiated */ @@ -107,9 +151,20 @@ struct VirtIODevice VMChangeStateEntry *vmstate; char *bus_name; uint8_t device_endian; + /** + * @user_guest_notifier_mask: gate usage of ->guest_notifier_mask() callback. + * This is used to suppress the masking of guest updates for + * vhost-user devices which are asynchronous by design. + */ bool use_guest_notifier_mask; AddressSpace *dma_as; QLIST_HEAD(, VirtQueue) *vector_queues; + QTAILQ_ENTRY(VirtIODevice) next; + /** + * @config_notifier: the event notifier that handles config events + */ + EventNotifier config_notifier; + bool device_iotlb_enabled; }; struct VirtioDeviceClass { @@ -130,6 +185,10 @@ struct VirtioDeviceClass { void (*set_config)(VirtIODevice *vdev, const uint8_t *config); void (*reset)(VirtIODevice *vdev); void (*set_status)(VirtIODevice *vdev, uint8_t val); + /* Device must validate queue_index. */ + void (*queue_reset)(VirtIODevice *vdev, uint32_t queue_index); + /* Device must validate queue_index. */ + void (*queue_enable)(VirtIODevice *vdev, uint32_t queue_index); /* For transitional devices, this is a bitmap of features * that are only exposed on the legacy interface but not * the modern one. @@ -163,11 +222,18 @@ struct VirtioDeviceClass { const VMStateDescription *vmsd; bool (*primary_unplug_pending)(void *opaque); struct vhost_dev *(*get_vhost)(VirtIODevice *vdev); + void (*toggle_device_iotlb)(VirtIODevice *vdev); }; void virtio_instance_init_common(Object *proxy_obj, void *data, size_t vdev_size, const char *vdev_name); +/** + * virtio_init() - initialise the common VirtIODevice structure + * @vdev: pointer to VirtIODevice + * @device_id: the VirtIO device ID (see virtio_ids.h) + * @config_size: size of the config space + */ void virtio_init(VirtIODevice *vdev, uint16_t device_id, size_t config_size); void virtio_cleanup(VirtIODevice *vdev); @@ -225,6 +291,13 @@ extern const VMStateInfo virtio_vmstate_info; int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id); +/** + * virtio_notify_config() - signal a change to device config + * @vdev: the virtio device + * + * Assuming the virtio device is up (VIRTIO_CONFIG_S_DRIVER_OK) this + * will trigger a guest interrupt and update the config version. + */ void virtio_notify_config(VirtIODevice *vdev); bool virtio_queue_get_notification(VirtQueue *vq); @@ -260,6 +333,7 @@ int virtio_get_num_queues(VirtIODevice *vdev); void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc, hwaddr avail, hwaddr used); void virtio_queue_update_rings(VirtIODevice *vdev, int n); +void virtio_init_region_cache(VirtIODevice *vdev, int n); void virtio_queue_set_align(VirtIODevice *vdev, int n, int align); void virtio_queue_notify(VirtIODevice *vdev, int n); uint16_t virtio_queue_vector(VirtIODevice *vdev, int n); @@ -268,6 +342,8 @@ int virtio_queue_set_host_notifier_mr(VirtIODevice *vdev, int n, MemoryRegion *mr, bool assign); int virtio_set_status(VirtIODevice *vdev, uint8_t val); void virtio_reset(void *opaque); +void virtio_queue_reset(VirtIODevice *vdev, uint32_t queue_index); +void virtio_queue_enable(VirtIODevice *vdev, uint32_t queue_index); void virtio_update_irq(VirtIODevice *vdev); int virtio_set_features(VirtIODevice *vdev, uint64_t val); @@ -291,7 +367,9 @@ typedef struct VirtIORNGConf VirtIORNGConf; DEFINE_PROP_BIT64("iommu_platform", _state, _field, \ VIRTIO_F_IOMMU_PLATFORM, false), \ DEFINE_PROP_BIT64("packed", _state, _field, \ - VIRTIO_F_RING_PACKED, false) + VIRTIO_F_RING_PACKED, false), \ + DEFINE_PROP_BIT64("queue_reset", _state, _field, \ + VIRTIO_F_RING_RESET, true) hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n); bool virtio_queue_enabled_legacy(VirtIODevice *vdev, int n); @@ -324,6 +402,9 @@ void virtio_queue_aio_attach_host_notifier_no_poll(VirtQueue *vq, AioContext *ct void virtio_queue_aio_detach_host_notifier(VirtQueue *vq, AioContext *ctx); VirtQueue *virtio_vector_first_queue(VirtIODevice *vdev, uint16_t vector); VirtQueue *virtio_vector_next_queue(VirtQueue *vq); +EventNotifier *virtio_config_get_guest_notifier(VirtIODevice *vdev); +void virtio_config_set_guest_notifier_fd_handler(VirtIODevice *vdev, + bool assign, bool with_irqfd); static inline void virtio_add_feature(uint64_t *features, unsigned int fbit) { @@ -343,7 +424,7 @@ static inline bool virtio_has_feature(uint64_t features, unsigned int fbit) return !!(features & (1ULL << fbit)); } -static inline bool virtio_vdev_has_feature(VirtIODevice *vdev, +static inline bool virtio_vdev_has_feature(const VirtIODevice *vdev, unsigned int fbit) { return virtio_has_feature(vdev->guest_features, fbit); @@ -365,6 +446,16 @@ static inline bool virtio_is_big_endian(VirtIODevice *vdev) return false; } +/** + * virtio_device_started() - check if device started + * @vdev - the VirtIO device + * @status - the devices status bits + * + * Check if the device is started. For most modern machines this is + * tracked via the @vdev->started field (to support migration), + * otherwise we check for the final negotiated status bit that + * indicates everything is ready. + */ static inline bool virtio_device_started(VirtIODevice *vdev, uint8_t status) { if (vdev->use_started) { @@ -374,6 +465,24 @@ static inline bool virtio_device_started(VirtIODevice *vdev, uint8_t status) return status & VIRTIO_CONFIG_S_DRIVER_OK; } +/** + * virtio_device_should_start() - check if device startable + * @vdev - the VirtIO device + * @status - the devices status bits + * + * This is similar to virtio_device_started() but also encapsulates a + * check on the VM status which would prevent a device starting + * anyway. + */ +static inline bool virtio_device_should_start(VirtIODevice *vdev, uint8_t status) +{ + if (!vdev->vm_running) { + return false; + } + + return virtio_device_started(vdev, status); +} + static inline void virtio_set_started(VirtIODevice *vdev, bool started) { if (started) { @@ -400,4 +509,10 @@ static inline bool virtio_device_disabled(VirtIODevice *vdev) bool virtio_legacy_allowed(VirtIODevice *vdev); bool virtio_legacy_check_disabled(VirtIODevice *vdev); +QEMUBH *virtio_bh_new_guarded_full(DeviceState *dev, + QEMUBHFunc *cb, void *opaque, + const char *name); +#define virtio_bh_new_guarded(dev, cb, opaque) \ + virtio_bh_new_guarded_full((dev), (cb), (opaque), (stringify(cb))) + #endif diff --git a/include/hw/watchdog/allwinner-wdt.h b/include/hw/watchdog/allwinner-wdt.h new file mode 100644 index 0000000000..7fe41e20f2 --- /dev/null +++ b/include/hw/watchdog/allwinner-wdt.h @@ -0,0 +1,123 @@ +/* + * Allwinner Watchdog emulation + * + * Copyright (C) 2023 Strahinja Jankovic + * + * This file is derived from Allwinner RTC, + * by 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_WATCHDOG_ALLWINNER_WDT_H +#define HW_WATCHDOG_ALLWINNER_WDT_H + +#include "qom/object.h" +#include "hw/ptimer.h" +#include "hw/sysbus.h" + +/* + * This is a model of the Allwinner watchdog. + * Since watchdog registers belong to the timer module (and are shared with the + * RTC module), the interrupt line from watchdog is not handled right now. + * In QEMU, we just wire up the watchdog reset to watchdog_perform_action(), + * at least for the moment. + */ + +#define TYPE_AW_WDT "allwinner-wdt" + +/** Allwinner WDT sun4i family (A10, A12), also sun7i (A20) */ +#define TYPE_AW_WDT_SUN4I TYPE_AW_WDT "-sun4i" + +/** Allwinner WDT sun6i family and newer (A31, H2+, H3, etc) */ +#define TYPE_AW_WDT_SUN6I TYPE_AW_WDT "-sun6i" + +/** Number of WDT registers */ +#define AW_WDT_REGS_NUM (5) + +OBJECT_DECLARE_TYPE(AwWdtState, AwWdtClass, AW_WDT) + +/** + * Allwinner WDT object instance state. + */ +struct AwWdtState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + struct ptimer_state *timer; + + uint32_t regs[AW_WDT_REGS_NUM]; +}; + +/** + * Allwinner WDT 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. + */ +struct AwWdtClass { + /*< 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)(AwWdtState *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)(AwWdtState *s, uint32_t offset, uint32_t data); + + /** + * Check if watchdog can generate system reset + * + * @return true if watchdog can generate system reset + */ + bool (*can_reset_system)(AwWdtState *s); + + /** + * Check if provided key is valid + * + * @value: value written to register + * @return true if key is valid, false otherwise + */ + bool (*is_key_valid)(AwWdtState *s, uint32_t val); + + /** + * Get current INTV_VALUE setting + * + * @return current INTV_VALUE (0-15) + */ + uint8_t (*get_intv_value)(AwWdtState *s); +}; + +#endif /* HW_WATCHDOG_ALLWINNER_WDT_H */ diff --git a/include/hw/watchdog/wdt_aspeed.h b/include/hw/watchdog/wdt_aspeed.h index dfa5dfa424..e90ef86651 100644 --- a/include/hw/watchdog/wdt_aspeed.h +++ b/include/hw/watchdog/wdt_aspeed.h @@ -21,7 +21,7 @@ OBJECT_DECLARE_TYPE(AspeedWDTState, AspeedWDTClass, ASPEED_WDT) #define TYPE_ASPEED_2600_WDT TYPE_ASPEED_WDT "-ast2600" #define TYPE_ASPEED_1030_WDT TYPE_ASPEED_WDT "-ast1030" -#define ASPEED_WDT_REGS_MAX (0x20 / 4) +#define ASPEED_WDT_REGS_MAX (0x30 / 4) struct AspeedWDTState { /*< private >*/ @@ -40,7 +40,7 @@ struct AspeedWDTState { struct AspeedWDTClass { SysBusDeviceClass parent_class; - uint32_t offset; + uint32_t iosize; uint32_t ext_pulse_width_mask; uint32_t reset_ctrl_reg; void (*reset_pulse)(AspeedWDTState *s, uint32_t property); diff --git a/include/hw/xen/arch_hvm.h b/include/hw/xen/arch_hvm.h new file mode 100644 index 0000000000..c7c515220d --- /dev/null +++ b/include/hw/xen/arch_hvm.h @@ -0,0 +1,5 @@ +#if defined(TARGET_I386) || defined(TARGET_X86_64) +#include "hw/i386/xen_arch_hvm.h" +#elif defined(TARGET_ARM) || defined(TARGET_ARM_64) +#include "hw/arm/xen_arch_hvm.h" +#endif diff --git a/include/hw/xen/interface/arch-arm.h b/include/hw/xen/interface/arch-arm.h new file mode 100644 index 0000000000..1528ced509 --- /dev/null +++ b/include/hw/xen/interface/arch-arm.h @@ -0,0 +1,509 @@ +/* SPDX-License-Identifier: MIT */ +/****************************************************************************** + * arch-arm.h + * + * Guest OS interface to ARM Xen. + * + * Copyright 2011 (C) Citrix Systems + */ + +#ifndef __XEN_PUBLIC_ARCH_ARM_H__ +#define __XEN_PUBLIC_ARCH_ARM_H__ + +/* + * `incontents 50 arm_abi Hypercall Calling Convention + * + * A hypercall is issued using the ARM HVC instruction. + * + * A hypercall can take up to 5 arguments. These are passed in + * registers, the first argument in x0/r0 (for arm64/arm32 guests + * respectively irrespective of whether the underlying hypervisor is + * 32- or 64-bit), the second argument in x1/r1, the third in x2/r2, + * the forth in x3/r3 and the fifth in x4/r4. + * + * The hypercall number is passed in r12 (arm) or x16 (arm64). In both + * cases the relevant ARM procedure calling convention specifies this + * is an inter-procedure-call scratch register (e.g. for use in linker + * stubs). This use does not conflict with use during a hypercall. + * + * The HVC ISS must contain a Xen specific TAG: XEN_HYPERCALL_TAG. + * + * The return value is in x0/r0. + * + * The hypercall will clobber x16/r12 and the argument registers used + * by that hypercall (except r0 which is the return value) i.e. in + * addition to x16/r12 a 2 argument hypercall will clobber x1/r1 and a + * 4 argument hypercall will clobber x1/r1, x2/r2 and x3/r3. + * + * Parameter structs passed to hypercalls are laid out according to + * the Procedure Call Standard for the ARM Architecture (AAPCS, AKA + * EABI) and Procedure Call Standard for the ARM 64-bit Architecture + * (AAPCS64). Where there is a conflict the 64-bit standard should be + * used regardless of guest type. Structures which are passed as + * hypercall arguments are always little endian. + * + * All memory which is shared with other entities in the system + * (including the hypervisor and other guests) must reside in memory + * which is mapped as Normal Inner Write-Back Outer Write-Back Inner-Shareable. + * This applies to: + * - hypercall arguments passed via a pointer to guest memory. + * - memory shared via the grant table mechanism (including PV I/O + * rings etc). + * - memory shared with the hypervisor (struct shared_info, struct + * vcpu_info, the grant table, etc). + * + * Any cache allocation hints are acceptable. + */ + +/* + * `incontents 55 arm_hcall Supported Hypercalls + * + * Xen on ARM makes extensive use of hardware facilities and therefore + * only a subset of the potential hypercalls are required. + * + * Since ARM uses second stage paging any machine/physical addresses + * passed to hypercalls are Guest Physical Addresses (Intermediate + * Physical Addresses) unless otherwise noted. + * + * The following hypercalls (and sub operations) are supported on the + * ARM platform. Other hypercalls should be considered + * unavailable/unsupported. + * + * HYPERVISOR_memory_op + * All generic sub-operations + * + * HYPERVISOR_domctl + * All generic sub-operations, with the exception of: + * * XEN_DOMCTL_irq_permission (not yet implemented) + * + * HYPERVISOR_sched_op + * All generic sub-operations, with the exception of: + * * SCHEDOP_block -- prefer wfi hardware instruction + * + * HYPERVISOR_console_io + * All generic sub-operations + * + * HYPERVISOR_xen_version + * All generic sub-operations + * + * HYPERVISOR_event_channel_op + * All generic sub-operations + * + * HYPERVISOR_physdev_op + * Exactly these sub-operations are supported: + * PHYSDEVOP_pci_device_add + * PHYSDEVOP_pci_device_remove + * + * HYPERVISOR_sysctl + * All generic sub-operations, with the exception of: + * * XEN_SYSCTL_page_offline_op + * * XEN_SYSCTL_get_pmstat + * * XEN_SYSCTL_pm_op + * + * HYPERVISOR_hvm_op + * Exactly these sub-operations are supported: + * * HVMOP_set_param + * * HVMOP_get_param + * + * HYPERVISOR_grant_table_op + * All generic sub-operations + * + * HYPERVISOR_vcpu_op + * Exactly these sub-operations are supported: + * * VCPUOP_register_vcpu_info + * * VCPUOP_register_runstate_memory_area + * + * HYPERVISOR_argo_op + * All generic sub-operations + * + * Other notes on the ARM ABI: + * + * - struct start_info is not exported to ARM guests. + * + * - struct shared_info is mapped by ARM guests using the + * HYPERVISOR_memory_op sub-op XENMEM_add_to_physmap, passing + * XENMAPSPACE_shared_info as space parameter. + * + * - All the per-cpu struct vcpu_info are mapped by ARM guests using the + * HYPERVISOR_vcpu_op sub-op VCPUOP_register_vcpu_info, including cpu0 + * struct vcpu_info. + * + * - The grant table is mapped using the HYPERVISOR_memory_op sub-op + * XENMEM_add_to_physmap, passing XENMAPSPACE_grant_table as space + * parameter. The memory range specified under the Xen compatible + * hypervisor node on device tree can be used as target gpfn for the + * mapping. + * + * - Xenstore is initialized by using the two hvm_params + * HVM_PARAM_STORE_PFN and HVM_PARAM_STORE_EVTCHN. They can be read + * with the HYPERVISOR_hvm_op sub-op HVMOP_get_param. + * + * - The paravirtualized console is initialized by using the two + * hvm_params HVM_PARAM_CONSOLE_PFN and HVM_PARAM_CONSOLE_EVTCHN. They + * can be read with the HYPERVISOR_hvm_op sub-op HVMOP_get_param. + * + * - Event channel notifications are delivered using the percpu GIC + * interrupt specified under the Xen compatible hypervisor node on + * device tree. + * + * - The device tree Xen compatible node is fully described under Linux + * at Documentation/devicetree/bindings/arm/xen.txt. + */ + +#define XEN_HYPERCALL_TAG 0XEA1 + +#define int64_aligned_t int64_t __attribute__((aligned(8))) +#define uint64_aligned_t uint64_t __attribute__((aligned(8))) + +#ifndef __ASSEMBLY__ +#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ + typedef union { type *p; unsigned long q; } \ + __guest_handle_ ## name; \ + typedef union { type *p; uint64_aligned_t q; } \ + __guest_handle_64_ ## name + +/* + * XEN_GUEST_HANDLE represents a guest pointer, when passed as a field + * in a struct in memory. On ARM is always 8 bytes sizes and 8 bytes + * aligned. + * XEN_GUEST_HANDLE_PARAM represents a guest pointer, when passed as an + * hypercall argument. It is 4 bytes on aarch32 and 8 bytes on aarch64. + */ +#define __DEFINE_XEN_GUEST_HANDLE(name, type) \ + ___DEFINE_XEN_GUEST_HANDLE(name, type); \ + ___DEFINE_XEN_GUEST_HANDLE(const_##name, const type) +#define DEFINE_XEN_GUEST_HANDLE(name) __DEFINE_XEN_GUEST_HANDLE(name, name) +#define __XEN_GUEST_HANDLE(name) __guest_handle_64_ ## name +#define XEN_GUEST_HANDLE(name) __XEN_GUEST_HANDLE(name) +#define XEN_GUEST_HANDLE_PARAM(name) __guest_handle_ ## name +#define set_xen_guest_handle_raw(hnd, val) \ + do { \ + __typeof__(&(hnd)) _sxghr_tmp = &(hnd); \ + _sxghr_tmp->q = 0; \ + _sxghr_tmp->p = val; \ + } while ( 0 ) +#define set_xen_guest_handle(hnd, val) set_xen_guest_handle_raw(hnd, val) + +typedef uint64_t xen_pfn_t; +#define PRI_xen_pfn PRIx64 +#define PRIu_xen_pfn PRIu64 + +/* + * Maximum number of virtual CPUs in legacy multi-processor guests. + * Only one. All other VCPUS must use VCPUOP_register_vcpu_info. + */ +#define XEN_LEGACY_MAX_VCPUS 1 + +typedef uint64_t xen_ulong_t; +#define PRI_xen_ulong PRIx64 + +#if defined(__XEN__) || defined(__XEN_TOOLS__) +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +/* Anonymous union includes both 32- and 64-bit names (e.g., r0/x0). */ +# define __DECL_REG(n64, n32) union { \ + uint64_t n64; \ + uint32_t n32; \ + } +#else +/* Non-gcc sources must always use the proper 64-bit name (e.g., x0). */ +#define __DECL_REG(n64, n32) uint64_t n64 +#endif + +struct vcpu_guest_core_regs +{ + /* Aarch64 Aarch32 */ + __DECL_REG(x0, r0_usr); + __DECL_REG(x1, r1_usr); + __DECL_REG(x2, r2_usr); + __DECL_REG(x3, r3_usr); + __DECL_REG(x4, r4_usr); + __DECL_REG(x5, r5_usr); + __DECL_REG(x6, r6_usr); + __DECL_REG(x7, r7_usr); + __DECL_REG(x8, r8_usr); + __DECL_REG(x9, r9_usr); + __DECL_REG(x10, r10_usr); + __DECL_REG(x11, r11_usr); + __DECL_REG(x12, r12_usr); + + __DECL_REG(x13, sp_usr); + __DECL_REG(x14, lr_usr); + + __DECL_REG(x15, __unused_sp_hyp); + + __DECL_REG(x16, lr_irq); + __DECL_REG(x17, sp_irq); + + __DECL_REG(x18, lr_svc); + __DECL_REG(x19, sp_svc); + + __DECL_REG(x20, lr_abt); + __DECL_REG(x21, sp_abt); + + __DECL_REG(x22, lr_und); + __DECL_REG(x23, sp_und); + + __DECL_REG(x24, r8_fiq); + __DECL_REG(x25, r9_fiq); + __DECL_REG(x26, r10_fiq); + __DECL_REG(x27, r11_fiq); + __DECL_REG(x28, r12_fiq); + + __DECL_REG(x29, sp_fiq); + __DECL_REG(x30, lr_fiq); + + /* Return address and mode */ + __DECL_REG(pc64, pc32); /* ELR_EL2 */ + uint64_t cpsr; /* SPSR_EL2 */ + + union { + uint64_t spsr_el1; /* AArch64 */ + uint32_t spsr_svc; /* AArch32 */ + }; + + /* AArch32 guests only */ + uint32_t spsr_fiq, spsr_irq, spsr_und, spsr_abt; + + /* AArch64 guests only */ + uint64_t sp_el0; + uint64_t sp_el1, elr_el1; +}; +typedef struct vcpu_guest_core_regs vcpu_guest_core_regs_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_guest_core_regs_t); + +#undef __DECL_REG + +struct vcpu_guest_context { +#define _VGCF_online 0 +#define VGCF_online (1<<_VGCF_online) + uint32_t flags; /* VGCF_* */ + + struct vcpu_guest_core_regs user_regs; /* Core CPU registers */ + + uint64_t sctlr; + uint64_t ttbcr, ttbr0, ttbr1; +}; +typedef struct vcpu_guest_context vcpu_guest_context_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t); + +/* + * struct xen_arch_domainconfig's ABI is covered by + * XEN_DOMCTL_INTERFACE_VERSION. + */ +#define XEN_DOMCTL_CONFIG_GIC_NATIVE 0 +#define XEN_DOMCTL_CONFIG_GIC_V2 1 +#define XEN_DOMCTL_CONFIG_GIC_V3 2 + +#define XEN_DOMCTL_CONFIG_TEE_NONE 0 +#define XEN_DOMCTL_CONFIG_TEE_OPTEE 1 + +struct xen_arch_domainconfig { + /* IN/OUT */ + uint8_t gic_version; + /* IN */ + uint16_t tee_type; + /* IN */ + uint32_t nr_spis; + /* + * OUT + * Based on the property clock-frequency in the DT timer node. + * The property may be present when the bootloader/firmware doesn't + * set correctly CNTFRQ which hold the timer frequency. + * + * As it's not possible to trap this register, we have to replicate + * the value in the guest DT. + * + * = 0 => property not present + * > 0 => Value of the property + * + */ + uint32_t clock_frequency; +}; +#endif /* __XEN__ || __XEN_TOOLS__ */ + +struct arch_vcpu_info { +}; +typedef struct arch_vcpu_info arch_vcpu_info_t; + +struct arch_shared_info { +}; +typedef struct arch_shared_info arch_shared_info_t; +typedef uint64_t xen_callback_t; + +#endif + +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +/* PSR bits (CPSR, SPSR) */ + +#define PSR_THUMB (1<<5) /* Thumb Mode enable */ +#define PSR_FIQ_MASK (1<<6) /* Fast Interrupt mask */ +#define PSR_IRQ_MASK (1<<7) /* Interrupt mask */ +#define PSR_ABT_MASK (1<<8) /* Asynchronous Abort mask */ +#define PSR_BIG_ENDIAN (1<<9) /* arm32: Big Endian Mode */ +#define PSR_DBG_MASK (1<<9) /* arm64: Debug Exception mask */ +#define PSR_IT_MASK (0x0600fc00) /* Thumb If-Then Mask */ +#define PSR_JAZELLE (1<<24) /* Jazelle Mode */ +#define PSR_Z (1<<30) /* Zero condition flag */ + +/* 32 bit modes */ +#define PSR_MODE_USR 0x10 +#define PSR_MODE_FIQ 0x11 +#define PSR_MODE_IRQ 0x12 +#define PSR_MODE_SVC 0x13 +#define PSR_MODE_MON 0x16 +#define PSR_MODE_ABT 0x17 +#define PSR_MODE_HYP 0x1a +#define PSR_MODE_UND 0x1b +#define PSR_MODE_SYS 0x1f + +/* 64 bit modes */ +#define PSR_MODE_BIT 0x10 /* Set iff AArch32 */ +#define PSR_MODE_EL3h 0x0d +#define PSR_MODE_EL3t 0x0c +#define PSR_MODE_EL2h 0x09 +#define PSR_MODE_EL2t 0x08 +#define PSR_MODE_EL1h 0x05 +#define PSR_MODE_EL1t 0x04 +#define PSR_MODE_EL0t 0x00 + +/* + * We set PSR_Z to be able to boot Linux kernel versions with an invalid + * encoding of the first 8 NOP instructions. See commit a92882a4d270 in + * Linux. + * + * Note that PSR_Z is also set by U-Boot and QEMU -kernel when loading + * zImage kernels on aarch32. + */ +#define PSR_GUEST32_INIT (PSR_Z|PSR_ABT_MASK|PSR_FIQ_MASK|PSR_IRQ_MASK|PSR_MODE_SVC) +#define PSR_GUEST64_INIT (PSR_ABT_MASK|PSR_FIQ_MASK|PSR_IRQ_MASK|PSR_MODE_EL1h) + +#define SCTLR_GUEST_INIT xen_mk_ullong(0x00c50078) + +/* + * Virtual machine platform (memory layout, interrupts) + * + * These are defined for consistency between the tools and the + * hypervisor. Guests must not rely on these hardcoded values but + * should instead use the FDT. + */ + +/* Physical Address Space */ + +/* Virtio MMIO mappings */ +#define GUEST_VIRTIO_MMIO_BASE xen_mk_ullong(0x02000000) +#define GUEST_VIRTIO_MMIO_SIZE xen_mk_ullong(0x00100000) + +/* + * vGIC mappings: Only one set of mapping is used by the guest. + * Therefore they can overlap. + */ + +/* vGIC v2 mappings */ +#define GUEST_GICD_BASE xen_mk_ullong(0x03001000) +#define GUEST_GICD_SIZE xen_mk_ullong(0x00001000) +#define GUEST_GICC_BASE xen_mk_ullong(0x03002000) +#define GUEST_GICC_SIZE xen_mk_ullong(0x00002000) + +/* vGIC v3 mappings */ +#define GUEST_GICV3_GICD_BASE xen_mk_ullong(0x03001000) +#define GUEST_GICV3_GICD_SIZE xen_mk_ullong(0x00010000) + +#define GUEST_GICV3_RDIST_REGIONS 1 + +#define GUEST_GICV3_GICR0_BASE xen_mk_ullong(0x03020000) /* vCPU0..127 */ +#define GUEST_GICV3_GICR0_SIZE xen_mk_ullong(0x01000000) + +/* + * 256 MB is reserved for VPCI configuration space based on calculation + * 256 buses x 32 devices x 8 functions x 4 KB = 256 MB + */ +#define GUEST_VPCI_ECAM_BASE xen_mk_ullong(0x10000000) +#define GUEST_VPCI_ECAM_SIZE xen_mk_ullong(0x10000000) + +/* ACPI tables physical address */ +#define GUEST_ACPI_BASE xen_mk_ullong(0x20000000) +#define GUEST_ACPI_SIZE xen_mk_ullong(0x02000000) + +/* PL011 mappings */ +#define GUEST_PL011_BASE xen_mk_ullong(0x22000000) +#define GUEST_PL011_SIZE xen_mk_ullong(0x00001000) + +/* Guest PCI-PCIe memory space where config space and BAR will be available.*/ +#define GUEST_VPCI_ADDR_TYPE_MEM xen_mk_ullong(0x02000000) +#define GUEST_VPCI_MEM_ADDR xen_mk_ullong(0x23000000) +#define GUEST_VPCI_MEM_SIZE xen_mk_ullong(0x10000000) + +/* + * 16MB == 4096 pages reserved for guest to use as a region to map its + * grant table in. + */ +#define GUEST_GNTTAB_BASE xen_mk_ullong(0x38000000) +#define GUEST_GNTTAB_SIZE xen_mk_ullong(0x01000000) + +#define GUEST_MAGIC_BASE xen_mk_ullong(0x39000000) +#define GUEST_MAGIC_SIZE xen_mk_ullong(0x01000000) + +#define GUEST_RAM_BANKS 2 + +/* + * The way to find the extended regions (to be exposed to the guest as unused + * address space) relies on the fact that the regions reserved for the RAM + * below are big enough to also accommodate such regions. + */ +#define GUEST_RAM0_BASE xen_mk_ullong(0x40000000) /* 3GB of low RAM @ 1GB */ +#define GUEST_RAM0_SIZE xen_mk_ullong(0xc0000000) + +/* 4GB @ 4GB Prefetch Memory for VPCI */ +#define GUEST_VPCI_ADDR_TYPE_PREFETCH_MEM xen_mk_ullong(0x42000000) +#define GUEST_VPCI_PREFETCH_MEM_ADDR xen_mk_ullong(0x100000000) +#define GUEST_VPCI_PREFETCH_MEM_SIZE xen_mk_ullong(0x100000000) + +#define GUEST_RAM1_BASE xen_mk_ullong(0x0200000000) /* 1016GB of RAM @ 8GB */ +#define GUEST_RAM1_SIZE xen_mk_ullong(0xfe00000000) + +#define GUEST_RAM_BASE GUEST_RAM0_BASE /* Lowest RAM address */ +/* Largest amount of actual RAM, not including holes */ +#define GUEST_RAM_MAX (GUEST_RAM0_SIZE + GUEST_RAM1_SIZE) +/* Suitable for e.g. const uint64_t ramfoo[] = GUEST_RAM_BANK_FOOS; */ +#define GUEST_RAM_BANK_BASES { GUEST_RAM0_BASE, GUEST_RAM1_BASE } +#define GUEST_RAM_BANK_SIZES { GUEST_RAM0_SIZE, GUEST_RAM1_SIZE } + +/* Current supported guest VCPUs */ +#define GUEST_MAX_VCPUS 128 + +/* Interrupts */ +#define GUEST_TIMER_VIRT_PPI 27 +#define GUEST_TIMER_PHYS_S_PPI 29 +#define GUEST_TIMER_PHYS_NS_PPI 30 +#define GUEST_EVTCHN_PPI 31 + +#define GUEST_VPL011_SPI 32 + +#define GUEST_VIRTIO_MMIO_SPI_FIRST 33 +#define GUEST_VIRTIO_MMIO_SPI_LAST 43 + +/* PSCI functions */ +#define PSCI_cpu_suspend 0 +#define PSCI_cpu_off 1 +#define PSCI_cpu_on 2 +#define PSCI_migrate 3 + +#endif + +#ifndef __ASSEMBLY__ +/* Stub definition of PMU structure */ +typedef struct xen_pmu_arch { uint8_t dummy; } xen_pmu_arch_t; +#endif + +#endif /* __XEN_PUBLIC_ARCH_ARM_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/arch-x86/cpuid.h b/include/hw/xen/interface/arch-x86/cpuid.h new file mode 100644 index 0000000000..7ecd16ae05 --- /dev/null +++ b/include/hw/xen/interface/arch-x86/cpuid.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: MIT */ +/****************************************************************************** + * arch-x86/cpuid.h + * + * CPUID interface to Xen. + * + * Copyright (c) 2007 Citrix Systems, Inc. + * + * Authors: + * Keir Fraser + */ + +#ifndef __XEN_PUBLIC_ARCH_X86_CPUID_H__ +#define __XEN_PUBLIC_ARCH_X86_CPUID_H__ + +/* + * For compatibility with other hypervisor interfaces, the Xen cpuid leaves + * can be found at the first otherwise unused 0x100 aligned boundary starting + * from 0x40000000. + * + * e.g If viridian extensions are enabled for an HVM domain, the Xen cpuid + * leaves will start at 0x40000100 + */ + +#define XEN_CPUID_FIRST_LEAF 0x40000000 +#define XEN_CPUID_LEAF(i) (XEN_CPUID_FIRST_LEAF + (i)) + +/* + * Leaf 1 (0x40000x00) + * EAX: Largest Xen-information leaf. All leaves up to an including @EAX + * are supported by the Xen host. + * EBX-EDX: "XenVMMXenVMM" signature, allowing positive identification + * of a Xen host. + */ +#define XEN_CPUID_SIGNATURE_EBX 0x566e6558 /* "XenV" */ +#define XEN_CPUID_SIGNATURE_ECX 0x65584d4d /* "MMXe" */ +#define XEN_CPUID_SIGNATURE_EDX 0x4d4d566e /* "nVMM" */ + +/* + * Leaf 2 (0x40000x01) + * EAX[31:16]: Xen major version. + * EAX[15: 0]: Xen minor version. + * EBX-EDX: Reserved (currently all zeroes). + */ + +/* + * Leaf 3 (0x40000x02) + * EAX: Number of hypercall transfer pages. This register is always guaranteed + * to specify one hypercall page. + * EBX: Base address of Xen-specific MSRs. + * ECX: Features 1. Unused bits are set to zero. + * EDX: Features 2. Unused bits are set to zero. + */ + +/* Does the host support MMU_PT_UPDATE_PRESERVE_AD for this guest? */ +#define _XEN_CPUID_FEAT1_MMU_PT_UPDATE_PRESERVE_AD 0 +#define XEN_CPUID_FEAT1_MMU_PT_UPDATE_PRESERVE_AD (1u<<0) + +/* + * Leaf 4 (0x40000x03) + * Sub-leaf 0: EAX: bit 0: emulated tsc + * bit 1: host tsc is known to be reliable + * bit 2: RDTSCP instruction available + * EBX: tsc_mode: 0=default (emulate if necessary), 1=emulate, + * 2=no emulation, 3=no emulation + TSC_AUX support + * ECX: guest tsc frequency in kHz + * EDX: guest tsc incarnation (migration count) + * Sub-leaf 1: EAX: tsc offset low part + * EBX: tsc offset high part + * ECX: multiplicator for tsc->ns conversion + * EDX: shift amount for tsc->ns conversion + * Sub-leaf 2: EAX: host tsc frequency in kHz + */ + +/* + * Leaf 5 (0x40000x04) + * HVM-specific features + * Sub-leaf 0: EAX: Features + * Sub-leaf 0: EBX: vcpu id (iff EAX has XEN_HVM_CPUID_VCPU_ID_PRESENT flag) + * Sub-leaf 0: ECX: domain id (iff EAX has XEN_HVM_CPUID_DOMID_PRESENT flag) + */ +#define XEN_HVM_CPUID_APIC_ACCESS_VIRT (1u << 0) /* Virtualized APIC registers */ +#define XEN_HVM_CPUID_X2APIC_VIRT (1u << 1) /* Virtualized x2APIC accesses */ +/* Memory mapped from other domains has valid IOMMU entries */ +#define XEN_HVM_CPUID_IOMMU_MAPPINGS (1u << 2) +#define XEN_HVM_CPUID_VCPU_ID_PRESENT (1u << 3) /* vcpu id is present in EBX */ +#define XEN_HVM_CPUID_DOMID_PRESENT (1u << 4) /* domid is present in ECX */ +/* + * With interrupt format set to 0 (non-remappable) bits 55:49 from the + * IO-APIC RTE and bits 11:5 from the MSI address can be used to store + * high bits for the Destination ID. This expands the Destination ID + * field from 8 to 15 bits, allowing to target APIC IDs up 32768. + */ +#define XEN_HVM_CPUID_EXT_DEST_ID (1u << 5) +/* + * Per-vCPU event channel upcalls work correctly with physical IRQs + * bound to event channels. + */ +#define XEN_HVM_CPUID_UPCALL_VECTOR (1u << 6) + +/* + * Leaf 6 (0x40000x05) + * PV-specific parameters + * Sub-leaf 0: EAX: max available sub-leaf + * Sub-leaf 0: EBX: bits 0-7: max machine address width + */ + +/* Max. address width in bits taking memory hotplug into account. */ +#define XEN_CPUID_MACHINE_ADDRESS_WIDTH_MASK (0xffu << 0) + +#define XEN_CPUID_MAX_NUM_LEAVES 5 + +#endif /* __XEN_PUBLIC_ARCH_X86_CPUID_H__ */ diff --git a/include/hw/xen/interface/arch-x86/xen-x86_32.h b/include/hw/xen/interface/arch-x86/xen-x86_32.h new file mode 100644 index 0000000000..139438e835 --- /dev/null +++ b/include/hw/xen/interface/arch-x86/xen-x86_32.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: MIT */ +/****************************************************************************** + * xen-x86_32.h + * + * Guest OS interface to x86 32-bit Xen. + * + * Copyright (c) 2004-2007, K A Fraser + */ + +#ifndef __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__ +#define __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__ + +/* + * Hypercall interface: + * Input: %ebx, %ecx, %edx, %esi, %edi, %ebp (arguments 1-6) + * Output: %eax + * Access is via hypercall page (set up by guest loader or via a Xen MSR): + * call hypercall_page + hypercall-number * 32 + * Clobbered: Argument registers (e.g., 2-arg hypercall clobbers %ebx,%ecx) + */ + +/* + * These flat segments are in the Xen-private section of every GDT. Since these + * are also present in the initial GDT, many OSes will be able to avoid + * installing their own GDT. + */ +#define FLAT_RING1_CS 0xe019 /* GDT index 259 */ +#define FLAT_RING1_DS 0xe021 /* GDT index 260 */ +#define FLAT_RING1_SS 0xe021 /* GDT index 260 */ +#define FLAT_RING3_CS 0xe02b /* GDT index 261 */ +#define FLAT_RING3_DS 0xe033 /* GDT index 262 */ +#define FLAT_RING3_SS 0xe033 /* GDT index 262 */ + +#define FLAT_KERNEL_CS FLAT_RING1_CS +#define FLAT_KERNEL_DS FLAT_RING1_DS +#define FLAT_KERNEL_SS FLAT_RING1_SS +#define FLAT_USER_CS FLAT_RING3_CS +#define FLAT_USER_DS FLAT_RING3_DS +#define FLAT_USER_SS FLAT_RING3_SS + +#define __HYPERVISOR_VIRT_START_PAE 0xF5800000 +#define __MACH2PHYS_VIRT_START_PAE 0xF5800000 +#define __MACH2PHYS_VIRT_END_PAE 0xF6800000 +#define HYPERVISOR_VIRT_START_PAE xen_mk_ulong(__HYPERVISOR_VIRT_START_PAE) +#define MACH2PHYS_VIRT_START_PAE xen_mk_ulong(__MACH2PHYS_VIRT_START_PAE) +#define MACH2PHYS_VIRT_END_PAE xen_mk_ulong(__MACH2PHYS_VIRT_END_PAE) + +/* Non-PAE bounds are obsolete. */ +#define __HYPERVISOR_VIRT_START_NONPAE 0xFC000000 +#define __MACH2PHYS_VIRT_START_NONPAE 0xFC000000 +#define __MACH2PHYS_VIRT_END_NONPAE 0xFC400000 +#define HYPERVISOR_VIRT_START_NONPAE \ + xen_mk_ulong(__HYPERVISOR_VIRT_START_NONPAE) +#define MACH2PHYS_VIRT_START_NONPAE \ + xen_mk_ulong(__MACH2PHYS_VIRT_START_NONPAE) +#define MACH2PHYS_VIRT_END_NONPAE \ + xen_mk_ulong(__MACH2PHYS_VIRT_END_NONPAE) + +#define __HYPERVISOR_VIRT_START __HYPERVISOR_VIRT_START_PAE +#define __MACH2PHYS_VIRT_START __MACH2PHYS_VIRT_START_PAE +#define __MACH2PHYS_VIRT_END __MACH2PHYS_VIRT_END_PAE + +#ifndef HYPERVISOR_VIRT_START +#define HYPERVISOR_VIRT_START xen_mk_ulong(__HYPERVISOR_VIRT_START) +#endif + +#define MACH2PHYS_VIRT_START xen_mk_ulong(__MACH2PHYS_VIRT_START) +#define MACH2PHYS_VIRT_END xen_mk_ulong(__MACH2PHYS_VIRT_END) +#define MACH2PHYS_NR_ENTRIES ((MACH2PHYS_VIRT_END-MACH2PHYS_VIRT_START)>>2) +#ifndef machine_to_phys_mapping +#define machine_to_phys_mapping ((unsigned long *)MACH2PHYS_VIRT_START) +#endif + +/* 32-/64-bit invariability for control interfaces (domctl/sysctl). */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) +#undef ___DEFINE_XEN_GUEST_HANDLE +#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ + typedef struct { type *p; } \ + __guest_handle_ ## name; \ + typedef struct { union { type *p; uint64_aligned_t q; }; } \ + __guest_handle_64_ ## name +#undef set_xen_guest_handle_raw +#define set_xen_guest_handle_raw(hnd, val) \ + do { if ( sizeof(hnd) == 8 ) *(uint64_t *)&(hnd) = 0; \ + (hnd).p = val; \ + } while ( 0 ) +#define int64_aligned_t int64_t __attribute__((aligned(8))) +#define uint64_aligned_t uint64_t __attribute__((aligned(8))) +#define __XEN_GUEST_HANDLE_64(name) __guest_handle_64_ ## name +#define XEN_GUEST_HANDLE_64(name) __XEN_GUEST_HANDLE_64(name) +#endif + +#ifndef __ASSEMBLY__ + +#if defined(XEN_GENERATING_COMPAT_HEADERS) +/* nothing */ +#elif defined(__XEN__) || defined(__XEN_TOOLS__) +/* Anonymous unions include all permissible names (e.g., al/ah/ax/eax). */ +#define __DECL_REG_LO8(which) union { \ + uint32_t e ## which ## x; \ + uint16_t which ## x; \ + struct { \ + uint8_t which ## l; \ + uint8_t which ## h; \ + }; \ +} +#define __DECL_REG_LO16(name) union { \ + uint32_t e ## name, _e ## name; \ + uint16_t name; \ +} +#else +/* Other sources must always use the proper 32-bit name (e.g., eax). */ +#define __DECL_REG_LO8(which) uint32_t e ## which ## x +#define __DECL_REG_LO16(name) uint32_t e ## name +#endif + +struct cpu_user_regs { + __DECL_REG_LO8(b); + __DECL_REG_LO8(c); + __DECL_REG_LO8(d); + __DECL_REG_LO16(si); + __DECL_REG_LO16(di); + __DECL_REG_LO16(bp); + __DECL_REG_LO8(a); + uint16_t error_code; /* private */ + uint16_t entry_vector; /* private */ + __DECL_REG_LO16(ip); + uint16_t cs; + uint8_t saved_upcall_mask; + uint8_t _pad0; + __DECL_REG_LO16(flags); /* eflags.IF == !saved_upcall_mask */ + __DECL_REG_LO16(sp); + uint16_t ss, _pad1; + uint16_t es, _pad2; + uint16_t ds, _pad3; + uint16_t fs, _pad4; + uint16_t gs, _pad5; +}; +typedef struct cpu_user_regs cpu_user_regs_t; +DEFINE_XEN_GUEST_HANDLE(cpu_user_regs_t); + +#undef __DECL_REG_LO8 +#undef __DECL_REG_LO16 + +/* + * Page-directory addresses above 4GB do not fit into architectural %cr3. + * When accessing %cr3, or equivalent field in vcpu_guest_context, guests + * must use the following accessor macros to pack/unpack valid MFNs. + */ +#define xen_pfn_to_cr3(pfn) (((unsigned)(pfn) << 12) | ((unsigned)(pfn) >> 20)) +#define xen_cr3_to_pfn(cr3) (((unsigned)(cr3) >> 12) | ((unsigned)(cr3) << 20)) + +struct arch_vcpu_info { + unsigned long cr2; + unsigned long pad[5]; /* sizeof(vcpu_info_t) == 64 */ +}; +typedef struct arch_vcpu_info arch_vcpu_info_t; + +struct xen_callback { + unsigned long cs; + unsigned long eip; +}; +typedef struct xen_callback xen_callback_t; + +#endif /* !__ASSEMBLY__ */ + +#endif /* __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/arch-x86/xen-x86_64.h b/include/hw/xen/interface/arch-x86/xen-x86_64.h new file mode 100644 index 0000000000..5d9035ed22 --- /dev/null +++ b/include/hw/xen/interface/arch-x86/xen-x86_64.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: MIT */ +/****************************************************************************** + * xen-x86_64.h + * + * Guest OS interface to x86 64-bit Xen. + * + * Copyright (c) 2004-2006, K A Fraser + */ + +#ifndef __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__ +#define __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__ + +/* + * Hypercall interface: + * Input: %rdi, %rsi, %rdx, %r10, %r8, %r9 (arguments 1-6) + * Output: %rax + * Access is via hypercall page (set up by guest loader or via a Xen MSR): + * call hypercall_page + hypercall-number * 32 + * Clobbered: argument registers (e.g., 2-arg hypercall clobbers %rdi,%rsi) + */ + +/* + * 64-bit segment selectors + * These flat segments are in the Xen-private section of every GDT. Since these + * are also present in the initial GDT, many OSes will be able to avoid + * installing their own GDT. + */ + +#define FLAT_RING3_CS32 0xe023 /* GDT index 260 */ +#define FLAT_RING3_CS64 0xe033 /* GDT index 262 */ +#define FLAT_RING3_DS32 0xe02b /* GDT index 261 */ +#define FLAT_RING3_DS64 0x0000 /* NULL selector */ +#define FLAT_RING3_SS32 0xe02b /* GDT index 261 */ +#define FLAT_RING3_SS64 0xe02b /* GDT index 261 */ + +#define FLAT_KERNEL_DS64 FLAT_RING3_DS64 +#define FLAT_KERNEL_DS32 FLAT_RING3_DS32 +#define FLAT_KERNEL_DS FLAT_KERNEL_DS64 +#define FLAT_KERNEL_CS64 FLAT_RING3_CS64 +#define FLAT_KERNEL_CS32 FLAT_RING3_CS32 +#define FLAT_KERNEL_CS FLAT_KERNEL_CS64 +#define FLAT_KERNEL_SS64 FLAT_RING3_SS64 +#define FLAT_KERNEL_SS32 FLAT_RING3_SS32 +#define FLAT_KERNEL_SS FLAT_KERNEL_SS64 + +#define FLAT_USER_DS64 FLAT_RING3_DS64 +#define FLAT_USER_DS32 FLAT_RING3_DS32 +#define FLAT_USER_DS FLAT_USER_DS64 +#define FLAT_USER_CS64 FLAT_RING3_CS64 +#define FLAT_USER_CS32 FLAT_RING3_CS32 +#define FLAT_USER_CS FLAT_USER_CS64 +#define FLAT_USER_SS64 FLAT_RING3_SS64 +#define FLAT_USER_SS32 FLAT_RING3_SS32 +#define FLAT_USER_SS FLAT_USER_SS64 + +#define __HYPERVISOR_VIRT_START 0xFFFF800000000000 +#define __HYPERVISOR_VIRT_END 0xFFFF880000000000 +#define __MACH2PHYS_VIRT_START 0xFFFF800000000000 +#define __MACH2PHYS_VIRT_END 0xFFFF804000000000 + +#ifndef HYPERVISOR_VIRT_START +#define HYPERVISOR_VIRT_START xen_mk_ulong(__HYPERVISOR_VIRT_START) +#define HYPERVISOR_VIRT_END xen_mk_ulong(__HYPERVISOR_VIRT_END) +#endif + +#define MACH2PHYS_VIRT_START xen_mk_ulong(__MACH2PHYS_VIRT_START) +#define MACH2PHYS_VIRT_END xen_mk_ulong(__MACH2PHYS_VIRT_END) +#define MACH2PHYS_NR_ENTRIES ((MACH2PHYS_VIRT_END-MACH2PHYS_VIRT_START)>>3) +#ifndef machine_to_phys_mapping +#define machine_to_phys_mapping ((unsigned long *)HYPERVISOR_VIRT_START) +#endif + +/* + * int HYPERVISOR_set_segment_base(unsigned int which, unsigned long base) + * @which == SEGBASE_* ; @base == 64-bit base address + * Returns 0 on success. + */ +#define SEGBASE_FS 0 +#define SEGBASE_GS_USER 1 +#define SEGBASE_GS_KERNEL 2 +#define SEGBASE_GS_USER_SEL 3 /* Set user %gs specified in base[15:0] */ + +/* + * int HYPERVISOR_iret(void) + * All arguments are on the kernel stack, in the following format. + * Never returns if successful. Current kernel context is lost. + * The saved CS is mapped as follows: + * RING0 -> RING3 kernel mode. + * RING1 -> RING3 kernel mode. + * RING2 -> RING3 kernel mode. + * RING3 -> RING3 user mode. + * However RING0 indicates that the guest kernel should return to iteself + * directly with + * orb $3,1*8(%rsp) + * iretq + * If flags contains VGCF_in_syscall: + * Restore RAX, RIP, RFLAGS, RSP. + * Discard R11, RCX, CS, SS. + * Otherwise: + * Restore RAX, R11, RCX, CS:RIP, RFLAGS, SS:RSP. + * All other registers are saved on hypercall entry and restored to user. + */ +/* Guest exited in SYSCALL context? Return to guest with SYSRET? */ +#define _VGCF_in_syscall 8 +#define VGCF_in_syscall (1<<_VGCF_in_syscall) +#define VGCF_IN_SYSCALL VGCF_in_syscall + +#ifndef __ASSEMBLY__ + +struct iret_context { + /* Top of stack (%rsp at point of hypercall). */ + uint64_t rax, r11, rcx, flags, rip, cs, rflags, rsp, ss; + /* Bottom of iret stack frame. */ +}; + +#if defined(__XEN__) || defined(__XEN_TOOLS__) +/* Anonymous unions include all permissible names (e.g., al/ah/ax/eax/rax). */ +#define __DECL_REG_LOHI(which) union { \ + uint64_t r ## which ## x; \ + uint32_t e ## which ## x; \ + uint16_t which ## x; \ + struct { \ + uint8_t which ## l; \ + uint8_t which ## h; \ + }; \ +} +#define __DECL_REG_LO8(name) union { \ + uint64_t r ## name; \ + uint32_t e ## name; \ + uint16_t name; \ + uint8_t name ## l; \ +} +#define __DECL_REG_LO16(name) union { \ + uint64_t r ## name; \ + uint32_t e ## name; \ + uint16_t name; \ +} +#define __DECL_REG_HI(num) union { \ + uint64_t r ## num; \ + uint32_t r ## num ## d; \ + uint16_t r ## num ## w; \ + uint8_t r ## num ## b; \ +} +#elif defined(__GNUC__) && !defined(__STRICT_ANSI__) +/* Anonymous union includes both 32- and 64-bit names (e.g., eax/rax). */ +#define __DECL_REG(name) union { \ + uint64_t r ## name, e ## name; \ + uint32_t _e ## name; \ +} +#else +/* Non-gcc sources must always use the proper 64-bit name (e.g., rax). */ +#define __DECL_REG(name) uint64_t r ## name +#endif + +#ifndef __DECL_REG_LOHI +#define __DECL_REG_LOHI(name) __DECL_REG(name ## x) +#define __DECL_REG_LO8 __DECL_REG +#define __DECL_REG_LO16 __DECL_REG +#define __DECL_REG_HI(num) uint64_t r ## num +#endif + +struct cpu_user_regs { + __DECL_REG_HI(15); + __DECL_REG_HI(14); + __DECL_REG_HI(13); + __DECL_REG_HI(12); + __DECL_REG_LO8(bp); + __DECL_REG_LOHI(b); + __DECL_REG_HI(11); + __DECL_REG_HI(10); + __DECL_REG_HI(9); + __DECL_REG_HI(8); + __DECL_REG_LOHI(a); + __DECL_REG_LOHI(c); + __DECL_REG_LOHI(d); + __DECL_REG_LO8(si); + __DECL_REG_LO8(di); + uint32_t error_code; /* private */ + uint32_t entry_vector; /* private */ + __DECL_REG_LO16(ip); + uint16_t cs, _pad0[1]; + uint8_t saved_upcall_mask; + uint8_t _pad1[3]; + __DECL_REG_LO16(flags); /* rflags.IF == !saved_upcall_mask */ + __DECL_REG_LO8(sp); + uint16_t ss, _pad2[3]; + uint16_t es, _pad3[3]; + uint16_t ds, _pad4[3]; + uint16_t fs, _pad5[3]; + uint16_t gs, _pad6[3]; +}; +typedef struct cpu_user_regs cpu_user_regs_t; +DEFINE_XEN_GUEST_HANDLE(cpu_user_regs_t); + +#undef __DECL_REG +#undef __DECL_REG_LOHI +#undef __DECL_REG_LO8 +#undef __DECL_REG_LO16 +#undef __DECL_REG_HI + +#define xen_pfn_to_cr3(pfn) ((unsigned long)(pfn) << 12) +#define xen_cr3_to_pfn(cr3) ((unsigned long)(cr3) >> 12) + +struct arch_vcpu_info { + unsigned long cr2; + unsigned long pad; /* sizeof(vcpu_info_t) == 64 */ +}; +typedef struct arch_vcpu_info arch_vcpu_info_t; + +typedef unsigned long xen_callback_t; + +#endif /* !__ASSEMBLY__ */ + +#endif /* __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/arch-x86/xen.h b/include/hw/xen/interface/arch-x86/xen.h new file mode 100644 index 0000000000..c0f4551247 --- /dev/null +++ b/include/hw/xen/interface/arch-x86/xen.h @@ -0,0 +1,378 @@ +/* SPDX-License-Identifier: MIT */ +/****************************************************************************** + * arch-x86/xen.h + * + * Guest OS interface to x86 Xen. + * + * Copyright (c) 2004-2006, K A Fraser + */ + +#include "../xen.h" + +#ifndef __XEN_PUBLIC_ARCH_X86_XEN_H__ +#define __XEN_PUBLIC_ARCH_X86_XEN_H__ + +/* Structural guest handles introduced in 0x00030201. */ +#if __XEN_INTERFACE_VERSION__ >= 0x00030201 +#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ + typedef struct { type *p; } __guest_handle_ ## name +#else +#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \ + typedef type * __guest_handle_ ## name +#endif + +/* + * XEN_GUEST_HANDLE represents a guest pointer, when passed as a field + * in a struct in memory. + * XEN_GUEST_HANDLE_PARAM represent a guest pointer, when passed as an + * hypercall argument. + * XEN_GUEST_HANDLE_PARAM and XEN_GUEST_HANDLE are the same on X86 but + * they might not be on other architectures. + */ +#define __DEFINE_XEN_GUEST_HANDLE(name, type) \ + ___DEFINE_XEN_GUEST_HANDLE(name, type); \ + ___DEFINE_XEN_GUEST_HANDLE(const_##name, const type) +#define DEFINE_XEN_GUEST_HANDLE(name) __DEFINE_XEN_GUEST_HANDLE(name, name) +#define __XEN_GUEST_HANDLE(name) __guest_handle_ ## name +#define XEN_GUEST_HANDLE(name) __XEN_GUEST_HANDLE(name) +#define XEN_GUEST_HANDLE_PARAM(name) XEN_GUEST_HANDLE(name) +#define set_xen_guest_handle_raw(hnd, val) do { (hnd).p = val; } while (0) +#define set_xen_guest_handle(hnd, val) set_xen_guest_handle_raw(hnd, val) + +#if defined(__i386__) +# ifdef __XEN__ +__DeFiNe__ __DECL_REG_LO8(which) uint32_t e ## which ## x +__DeFiNe__ __DECL_REG_LO16(name) union { uint32_t e ## name; } +# endif +#include "xen-x86_32.h" +# ifdef __XEN__ +__UnDeF__ __DECL_REG_LO8 +__UnDeF__ __DECL_REG_LO16 +__DeFiNe__ __DECL_REG_LO8(which) e ## which ## x +__DeFiNe__ __DECL_REG_LO16(name) e ## name +# endif +#elif defined(__x86_64__) +#include "xen-x86_64.h" +#endif + +#ifndef __ASSEMBLY__ +typedef unsigned long xen_pfn_t; +#define PRI_xen_pfn "lx" +#define PRIu_xen_pfn "lu" +#endif + +#define XEN_HAVE_PV_GUEST_ENTRY 1 + +#define XEN_HAVE_PV_UPCALL_MASK 1 + +/* + * `incontents 200 segdesc Segment Descriptor Tables + */ +/* + * ` enum neg_errnoval + * ` HYPERVISOR_set_gdt(const xen_pfn_t frames[], unsigned int entries); + * ` + */ +/* + * A number of GDT entries are reserved by Xen. These are not situated at the + * start of the GDT because some stupid OSes export hard-coded selector values + * in their ABI. These hard-coded values are always near the start of the GDT, + * so Xen places itself out of the way, at the far end of the GDT. + * + * NB The LDT is set using the MMUEXT_SET_LDT op of HYPERVISOR_mmuext_op + */ +#define FIRST_RESERVED_GDT_PAGE 14 +#define FIRST_RESERVED_GDT_BYTE (FIRST_RESERVED_GDT_PAGE * 4096) +#define FIRST_RESERVED_GDT_ENTRY (FIRST_RESERVED_GDT_BYTE / 8) + + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_update_descriptor(u64 pa, u64 desc); + * ` + * ` @pa The machine physical address of the descriptor to + * ` update. Must be either a descriptor page or writable. + * ` @desc The descriptor value to update, in the same format as a + * ` native descriptor table entry. + */ + +/* Maximum number of virtual CPUs in legacy multi-processor guests. */ +#define XEN_LEGACY_MAX_VCPUS 32 + +#ifndef __ASSEMBLY__ + +typedef unsigned long xen_ulong_t; +#define PRI_xen_ulong "lx" + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_stack_switch(unsigned long ss, unsigned long esp); + * ` + * Sets the stack segment and pointer for the current vcpu. + */ + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_set_trap_table(const struct trap_info traps[]); + * ` + */ +/* + * Send an array of these to HYPERVISOR_set_trap_table(). + * Terminate the array with a sentinel entry, with traps[].address==0. + * The privilege level specifies which modes may enter a trap via a software + * interrupt. On x86/64, since rings 1 and 2 are unavailable, we allocate + * privilege levels as follows: + * Level == 0: Noone may enter + * Level == 1: Kernel may enter + * Level == 2: Kernel may enter + * Level == 3: Everyone may enter + * + * Note: For compatibility with kernels not setting up exception handlers + * early enough, Xen will avoid trying to inject #GP (and hence crash + * the domain) when an RDMSR would require this, but no handler was + * set yet. The precise conditions are implementation specific, and + * new code may not rely on such behavior anyway. + */ +#define TI_GET_DPL(_ti) ((_ti)->flags & 3) +#define TI_GET_IF(_ti) ((_ti)->flags & 4) +#define TI_SET_DPL(_ti,_dpl) ((_ti)->flags |= (_dpl)) +#define TI_SET_IF(_ti,_if) ((_ti)->flags |= ((!!(_if))<<2)) +struct trap_info { + uint8_t vector; /* exception vector */ + uint8_t flags; /* 0-3: privilege level; 4: clear event enable? */ + uint16_t cs; /* code selector */ + unsigned long address; /* code offset */ +}; +typedef struct trap_info trap_info_t; +DEFINE_XEN_GUEST_HANDLE(trap_info_t); + +typedef uint64_t tsc_timestamp_t; /* RDTSC timestamp */ + +/* + * The following is all CPU context. Note that the fpu_ctxt block is filled + * in by FXSAVE if the CPU has feature FXSR; otherwise FSAVE is used. + * + * Also note that when calling DOMCTL_setvcpucontext for HVM guests, not all + * information in this structure is updated, the fields read include: fpu_ctxt + * (if VGCT_I387_VALID is set), flags, user_regs and debugreg[*]. + * + * Note: VCPUOP_initialise for HVM guests is non-symetric with + * DOMCTL_setvcpucontext, and uses struct vcpu_hvm_context from hvm/hvm_vcpu.h + */ +struct vcpu_guest_context { + /* FPU registers come first so they can be aligned for FXSAVE/FXRSTOR. */ + struct { char x[512]; } fpu_ctxt; /* User-level FPU registers */ +#define VGCF_I387_VALID (1<<0) +#define VGCF_IN_KERNEL (1<<2) +#define _VGCF_i387_valid 0 +#define VGCF_i387_valid (1<<_VGCF_i387_valid) +#define _VGCF_in_kernel 2 +#define VGCF_in_kernel (1<<_VGCF_in_kernel) +#define _VGCF_failsafe_disables_events 3 +#define VGCF_failsafe_disables_events (1<<_VGCF_failsafe_disables_events) +#define _VGCF_syscall_disables_events 4 +#define VGCF_syscall_disables_events (1<<_VGCF_syscall_disables_events) +#define _VGCF_online 5 +#define VGCF_online (1<<_VGCF_online) + unsigned long flags; /* VGCF_* flags */ + struct cpu_user_regs user_regs; /* User-level CPU registers */ + struct trap_info trap_ctxt[256]; /* Virtual IDT */ + unsigned long ldt_base, ldt_ents; /* LDT (linear address, # ents) */ + unsigned long gdt_frames[16], gdt_ents; /* GDT (machine frames, # ents) */ + unsigned long kernel_ss, kernel_sp; /* Virtual TSS (only SS1/SP1) */ + /* NB. User pagetable on x86/64 is placed in ctrlreg[1]. */ + unsigned long ctrlreg[8]; /* CR0-CR7 (control registers) */ + unsigned long debugreg[8]; /* DB0-DB7 (debug registers) */ +#ifdef __i386__ + unsigned long event_callback_cs; /* CS:EIP of event callback */ + unsigned long event_callback_eip; + unsigned long failsafe_callback_cs; /* CS:EIP of failsafe callback */ + unsigned long failsafe_callback_eip; +#else + unsigned long event_callback_eip; + unsigned long failsafe_callback_eip; +#ifdef __XEN__ + union { + unsigned long syscall_callback_eip; + struct { + unsigned int event_callback_cs; /* compat CS of event cb */ + unsigned int failsafe_callback_cs; /* compat CS of failsafe cb */ + }; + }; +#else + unsigned long syscall_callback_eip; +#endif +#endif + unsigned long vm_assist; /* VMASST_TYPE_* bitmap */ +#ifdef __x86_64__ + /* Segment base addresses. */ + uint64_t fs_base; + uint64_t gs_base_kernel; + uint64_t gs_base_user; +#endif +}; +typedef struct vcpu_guest_context vcpu_guest_context_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t); + +struct arch_shared_info { + /* + * Number of valid entries in the p2m table(s) anchored at + * pfn_to_mfn_frame_list_list and/or p2m_vaddr. + */ + unsigned long max_pfn; + /* + * Frame containing list of mfns containing list of mfns containing p2m. + * A value of 0 indicates it has not yet been set up, ~0 indicates it has + * been set to invalid e.g. due to the p2m being too large for the 3-level + * p2m tree. In this case the linear mapper p2m list anchored at p2m_vaddr + * is to be used. + */ + xen_pfn_t pfn_to_mfn_frame_list_list; + unsigned long nmi_reason; + /* + * Following three fields are valid if p2m_cr3 contains a value different + * from 0. + * p2m_cr3 is the root of the address space where p2m_vaddr is valid. + * p2m_cr3 is in the same format as a cr3 value in the vcpu register state + * and holds the folded machine frame number (via xen_pfn_to_cr3) of a + * L3 or L4 page table. + * p2m_vaddr holds the virtual address of the linear p2m list. All entries + * in the range [0...max_pfn[ are accessible via this pointer. + * p2m_generation will be incremented by the guest before and after each + * change of the mappings of the p2m list. p2m_generation starts at 0 and + * a value with the least significant bit set indicates that a mapping + * update is in progress. This allows guest external software (e.g. in Dom0) + * to verify that read mappings are consistent and whether they have changed + * since the last check. + * Modifying a p2m element in the linear p2m list is allowed via an atomic + * write only. + */ + unsigned long p2m_cr3; /* cr3 value of the p2m address space */ + unsigned long p2m_vaddr; /* virtual address of the p2m list */ + unsigned long p2m_generation; /* generation count of p2m mapping */ +#ifdef __i386__ + /* There's no room for this field in the generic structure. */ + uint32_t wc_sec_hi; +#endif +}; +typedef struct arch_shared_info arch_shared_info_t; + +#if defined(__XEN__) || defined(__XEN_TOOLS__) +/* + * struct xen_arch_domainconfig's ABI is covered by + * XEN_DOMCTL_INTERFACE_VERSION. + */ +struct xen_arch_domainconfig { +#define _XEN_X86_EMU_LAPIC 0 +#define XEN_X86_EMU_LAPIC (1U<<_XEN_X86_EMU_LAPIC) +#define _XEN_X86_EMU_HPET 1 +#define XEN_X86_EMU_HPET (1U<<_XEN_X86_EMU_HPET) +#define _XEN_X86_EMU_PM 2 +#define XEN_X86_EMU_PM (1U<<_XEN_X86_EMU_PM) +#define _XEN_X86_EMU_RTC 3 +#define XEN_X86_EMU_RTC (1U<<_XEN_X86_EMU_RTC) +#define _XEN_X86_EMU_IOAPIC 4 +#define XEN_X86_EMU_IOAPIC (1U<<_XEN_X86_EMU_IOAPIC) +#define _XEN_X86_EMU_PIC 5 +#define XEN_X86_EMU_PIC (1U<<_XEN_X86_EMU_PIC) +#define _XEN_X86_EMU_VGA 6 +#define XEN_X86_EMU_VGA (1U<<_XEN_X86_EMU_VGA) +#define _XEN_X86_EMU_IOMMU 7 +#define XEN_X86_EMU_IOMMU (1U<<_XEN_X86_EMU_IOMMU) +#define _XEN_X86_EMU_PIT 8 +#define XEN_X86_EMU_PIT (1U<<_XEN_X86_EMU_PIT) +#define _XEN_X86_EMU_USE_PIRQ 9 +#define XEN_X86_EMU_USE_PIRQ (1U<<_XEN_X86_EMU_USE_PIRQ) +#define _XEN_X86_EMU_VPCI 10 +#define XEN_X86_EMU_VPCI (1U<<_XEN_X86_EMU_VPCI) + +#define XEN_X86_EMU_ALL (XEN_X86_EMU_LAPIC | XEN_X86_EMU_HPET | \ + XEN_X86_EMU_PM | XEN_X86_EMU_RTC | \ + XEN_X86_EMU_IOAPIC | XEN_X86_EMU_PIC | \ + XEN_X86_EMU_VGA | XEN_X86_EMU_IOMMU | \ + XEN_X86_EMU_PIT | XEN_X86_EMU_USE_PIRQ |\ + XEN_X86_EMU_VPCI) + uint32_t emulation_flags; + +/* + * Select whether to use a relaxed behavior for accesses to MSRs not explicitly + * handled by Xen instead of injecting a #GP to the guest. Note this option + * doesn't allow the guest to read or write to the underlying MSR. + */ +#define XEN_X86_MSR_RELAXED (1u << 0) + uint32_t misc_flags; +}; + +/* Max XEN_X86_* constant. Used for ABI checking. */ +#define XEN_X86_MISC_FLAGS_MAX XEN_X86_MSR_RELAXED + +#endif + +/* + * Representations of architectural CPUID and MSR information. Used as the + * serialised version of Xen's internal representation. + */ +typedef struct xen_cpuid_leaf { +#define XEN_CPUID_NO_SUBLEAF 0xffffffffu + uint32_t leaf, subleaf; + uint32_t a, b, c, d; +} xen_cpuid_leaf_t; +DEFINE_XEN_GUEST_HANDLE(xen_cpuid_leaf_t); + +typedef struct xen_msr_entry { + uint32_t idx; + uint32_t flags; /* Reserved MBZ. */ + uint64_t val; +} xen_msr_entry_t; +DEFINE_XEN_GUEST_HANDLE(xen_msr_entry_t); + +#endif /* !__ASSEMBLY__ */ + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_fpu_taskswitch(int set); + * ` + * Sets (if set!=0) or clears (if set==0) CR0.TS. + */ + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_set_debugreg(int regno, unsigned long value); + * + * ` unsigned long + * ` HYPERVISOR_get_debugreg(int regno); + * For 0<=reg<=7, returns the debug register value. + * For other values of reg, returns ((unsigned long)-EINVAL). + * (Unfortunately, this interface is defective.) + */ + +/* + * Prefix forces emulation of some non-trapping instructions. + * Currently only CPUID. + */ +#ifdef __ASSEMBLY__ +#define XEN_EMULATE_PREFIX .byte 0x0f,0x0b,0x78,0x65,0x6e ; +#define XEN_CPUID XEN_EMULATE_PREFIX cpuid +#else +#define XEN_EMULATE_PREFIX ".byte 0x0f,0x0b,0x78,0x65,0x6e ; " +#define XEN_CPUID XEN_EMULATE_PREFIX "cpuid" +#endif + +/* + * Debug console IO port, also called "port E9 hack". Each character written + * to this IO port will be printed on the hypervisor console, subject to log + * level restrictions. + */ +#define XEN_HVM_DEBUGCONS_IOPORT 0xe9 + +#endif /* __XEN_PUBLIC_ARCH_X86_XEN_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/event_channel.h b/include/hw/xen/interface/event_channel.h new file mode 100644 index 0000000000..0d91a1c4af --- /dev/null +++ b/include/hw/xen/interface/event_channel.h @@ -0,0 +1,371 @@ +/* SPDX-License-Identifier: MIT */ +/****************************************************************************** + * event_channel.h + * + * Event channels between domains. + * + * Copyright (c) 2003-2004, K A Fraser. + */ + +#ifndef __XEN_PUBLIC_EVENT_CHANNEL_H__ +#define __XEN_PUBLIC_EVENT_CHANNEL_H__ + +#include "xen.h" + +/* + * `incontents 150 evtchn Event Channels + * + * Event channels are the basic primitive provided by Xen for event + * notifications. An event is the Xen equivalent of a hardware + * interrupt. They essentially store one bit of information, the event + * of interest is signalled by transitioning this bit from 0 to 1. + * + * Notifications are received by a guest via an upcall from Xen, + * indicating when an event arrives (setting the bit). Further + * notifications are masked until the bit is cleared again (therefore, + * guests must check the value of the bit after re-enabling event + * delivery to ensure no missed notifications). + * + * Event notifications can be masked by setting a flag; this is + * equivalent to disabling interrupts and can be used to ensure + * atomicity of certain operations in the guest kernel. + * + * Event channels are represented by the evtchn_* fields in + * struct shared_info and struct vcpu_info. + */ + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_event_channel_op(enum event_channel_op cmd, void *args) + * ` + * @cmd == EVTCHNOP_* (event-channel operation). + * @args == struct evtchn_* Operation-specific extra arguments (NULL if none). + */ + +/* ` enum event_channel_op { // EVTCHNOP_* => struct evtchn_* */ +#define EVTCHNOP_bind_interdomain 0 +#define EVTCHNOP_bind_virq 1 +#define EVTCHNOP_bind_pirq 2 +#define EVTCHNOP_close 3 +#define EVTCHNOP_send 4 +#define EVTCHNOP_status 5 +#define EVTCHNOP_alloc_unbound 6 +#define EVTCHNOP_bind_ipi 7 +#define EVTCHNOP_bind_vcpu 8 +#define EVTCHNOP_unmask 9 +#define EVTCHNOP_reset 10 +#define EVTCHNOP_init_control 11 +#define EVTCHNOP_expand_array 12 +#define EVTCHNOP_set_priority 13 +#ifdef __XEN__ +#define EVTCHNOP_reset_cont 14 +#endif +/* ` } */ + +typedef uint32_t evtchn_port_t; +DEFINE_XEN_GUEST_HANDLE(evtchn_port_t); + +/* + * EVTCHNOP_alloc_unbound: Allocate a port in domain and mark as + * accepting interdomain bindings from domain . A fresh port + * is allocated in and returned as . + * NOTES: + * 1. If the caller is unprivileged then must be DOMID_SELF. + * 2. may be DOMID_SELF, allowing loopback connections. + */ +struct evtchn_alloc_unbound { + /* IN parameters */ + domid_t dom, remote_dom; + /* OUT parameters */ + evtchn_port_t port; +}; +typedef struct evtchn_alloc_unbound evtchn_alloc_unbound_t; + +/* + * EVTCHNOP_bind_interdomain: Construct an interdomain event channel between + * the calling domain and . must identify + * a port that is unbound and marked as accepting bindings from the calling + * domain. A fresh port is allocated in the calling domain and returned as + * . + * + * In case the peer domain has already tried to set our event channel + * pending, before it was bound, EVTCHNOP_bind_interdomain always sets + * the local event channel pending. + * + * The usual pattern of use, in the guest's upcall (or subsequent + * handler) is as follows: (Re-enable the event channel for subsequent + * signalling and then) check for the existence of whatever condition + * is being waited for by other means, and take whatever action is + * needed (if any). + * + * NOTES: + * 1. may be DOMID_SELF, allowing loopback connections. + */ +struct evtchn_bind_interdomain { + /* IN parameters. */ + domid_t remote_dom; + evtchn_port_t remote_port; + /* OUT parameters. */ + evtchn_port_t local_port; +}; +typedef struct evtchn_bind_interdomain evtchn_bind_interdomain_t; + +/* + * EVTCHNOP_bind_virq: Bind a local event channel to VIRQ on specified + * vcpu. + * NOTES: + * 1. Virtual IRQs are classified as per-vcpu or global. See the VIRQ list + * in xen.h for the classification of each VIRQ. + * 2. Global VIRQs must be allocated on VCPU0 but can subsequently be + * re-bound via EVTCHNOP_bind_vcpu. + * 3. Per-vcpu VIRQs may be bound to at most one event channel per vcpu. + * The allocated event channel is bound to the specified vcpu and the + * binding cannot be changed. + */ +struct evtchn_bind_virq { + /* IN parameters. */ + uint32_t virq; /* enum virq */ + uint32_t vcpu; + /* OUT parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_bind_virq evtchn_bind_virq_t; + +/* + * EVTCHNOP_bind_pirq: Bind a local event channel to a real IRQ (PIRQ ). + * NOTES: + * 1. A physical IRQ may be bound to at most one event channel per domain. + * 2. Only a sufficiently-privileged domain may bind to a physical IRQ. + */ +struct evtchn_bind_pirq { + /* IN parameters. */ + uint32_t pirq; +#define BIND_PIRQ__WILL_SHARE 1 + uint32_t flags; /* BIND_PIRQ__* */ + /* OUT parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_bind_pirq evtchn_bind_pirq_t; + +/* + * EVTCHNOP_bind_ipi: Bind a local event channel to receive events. + * NOTES: + * 1. The allocated event channel is bound to the specified vcpu. The binding + * may not be changed. + */ +struct evtchn_bind_ipi { + uint32_t vcpu; + /* OUT parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_bind_ipi evtchn_bind_ipi_t; + +/* + * EVTCHNOP_close: Close a local event channel . If the channel is + * interdomain then the remote end is placed in the unbound state + * (EVTCHNSTAT_unbound), awaiting a new connection. + */ +struct evtchn_close { + /* IN parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_close evtchn_close_t; + +/* + * EVTCHNOP_send: Send an event to the remote end of the channel whose local + * endpoint is . + */ +struct evtchn_send { + /* IN parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_send evtchn_send_t; + +/* + * EVTCHNOP_status: Get the current status of the communication channel which + * has an endpoint at . + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may obtain the status of an event + * channel for which is not DOMID_SELF. + */ +struct evtchn_status { + /* IN parameters */ + domid_t dom; + evtchn_port_t port; + /* OUT parameters */ +#define EVTCHNSTAT_closed 0 /* Channel is not in use. */ +#define EVTCHNSTAT_unbound 1 /* Channel is waiting interdom connection.*/ +#define EVTCHNSTAT_interdomain 2 /* Channel is connected to remote domain. */ +#define EVTCHNSTAT_pirq 3 /* Channel is bound to a phys IRQ line. */ +#define EVTCHNSTAT_virq 4 /* Channel is bound to a virtual IRQ line */ +#define EVTCHNSTAT_ipi 5 /* Channel is bound to a virtual IPI line */ + uint32_t status; + uint32_t vcpu; /* VCPU to which this channel is bound. */ + union { + struct { + domid_t dom; + } unbound; /* EVTCHNSTAT_unbound */ + struct { + domid_t dom; + evtchn_port_t port; + } interdomain; /* EVTCHNSTAT_interdomain */ + uint32_t pirq; /* EVTCHNSTAT_pirq */ + uint32_t virq; /* EVTCHNSTAT_virq */ + } u; +}; +typedef struct evtchn_status evtchn_status_t; + +/* + * EVTCHNOP_bind_vcpu: Specify which vcpu a channel should notify when an + * event is pending. + * NOTES: + * 1. IPI-bound channels always notify the vcpu specified at bind time. + * This binding cannot be changed. + * 2. Per-VCPU VIRQ channels always notify the vcpu specified at bind time. + * This binding cannot be changed. + * 3. All other channels notify vcpu0 by default. This default is set when + * the channel is allocated (a port that is freed and subsequently reused + * has its binding reset to vcpu0). + */ +struct evtchn_bind_vcpu { + /* IN parameters. */ + evtchn_port_t port; + uint32_t vcpu; +}; +typedef struct evtchn_bind_vcpu evtchn_bind_vcpu_t; + +/* + * EVTCHNOP_unmask: Unmask the specified local event-channel port and deliver + * a notification to the appropriate VCPU if an event is pending. + */ +struct evtchn_unmask { + /* IN parameters. */ + evtchn_port_t port; +}; +typedef struct evtchn_unmask evtchn_unmask_t; + +/* + * EVTCHNOP_reset: Close all event channels associated with specified domain. + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify other than DOMID_SELF. + * 3. Destroys all control blocks and event array, resets event channel + * operations to 2-level ABI if called with == DOMID_SELF and FIFO + * ABI was used. Guests should not bind events during EVTCHNOP_reset call + * as these events are likely to be lost. + */ +struct evtchn_reset { + /* IN parameters. */ + domid_t dom; +}; +typedef struct evtchn_reset evtchn_reset_t; + +/* + * EVTCHNOP_init_control: initialize the control block for the FIFO ABI. + * + * Note: any events that are currently pending will not be resent and + * will be lost. Guests should call this before binding any event to + * avoid losing any events. + */ +struct evtchn_init_control { + /* IN parameters. */ + uint64_t control_gfn; + uint32_t offset; + uint32_t vcpu; + /* OUT parameters. */ + uint8_t link_bits; + uint8_t _pad[7]; +}; +typedef struct evtchn_init_control evtchn_init_control_t; + +/* + * EVTCHNOP_expand_array: add an additional page to the event array. + */ +struct evtchn_expand_array { + /* IN parameters. */ + uint64_t array_gfn; +}; +typedef struct evtchn_expand_array evtchn_expand_array_t; + +/* + * EVTCHNOP_set_priority: set the priority for an event channel. + */ +struct evtchn_set_priority { + /* IN parameters. */ + evtchn_port_t port; + uint32_t priority; +}; +typedef struct evtchn_set_priority evtchn_set_priority_t; + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_event_channel_op_compat(struct evtchn_op *op) + * ` + * Superceded by new event_channel_op() hypercall since 0x00030202. + */ +struct evtchn_op { + uint32_t cmd; /* enum event_channel_op */ + union { + evtchn_alloc_unbound_t alloc_unbound; + evtchn_bind_interdomain_t bind_interdomain; + evtchn_bind_virq_t bind_virq; + evtchn_bind_pirq_t bind_pirq; + evtchn_bind_ipi_t bind_ipi; + evtchn_close_t close; + evtchn_send_t send; + evtchn_status_t status; + evtchn_bind_vcpu_t bind_vcpu; + evtchn_unmask_t unmask; + } u; +}; +typedef struct evtchn_op evtchn_op_t; +DEFINE_XEN_GUEST_HANDLE(evtchn_op_t); + +/* + * 2-level ABI + */ + +#define EVTCHN_2L_NR_CHANNELS (sizeof(xen_ulong_t) * sizeof(xen_ulong_t) * 64) + +/* + * FIFO ABI + */ + +/* Events may have priorities from 0 (highest) to 15 (lowest). */ +#define EVTCHN_FIFO_PRIORITY_MAX 0 +#define EVTCHN_FIFO_PRIORITY_DEFAULT 7 +#define EVTCHN_FIFO_PRIORITY_MIN 15 + +#define EVTCHN_FIFO_MAX_QUEUES (EVTCHN_FIFO_PRIORITY_MIN + 1) + +typedef uint32_t event_word_t; + +#define EVTCHN_FIFO_PENDING 31 +#define EVTCHN_FIFO_MASKED 30 +#define EVTCHN_FIFO_LINKED 29 +#define EVTCHN_FIFO_BUSY 28 + +#define EVTCHN_FIFO_LINK_BITS 17 +#define EVTCHN_FIFO_LINK_MASK ((1 << EVTCHN_FIFO_LINK_BITS) - 1) + +#define EVTCHN_FIFO_NR_CHANNELS (1 << EVTCHN_FIFO_LINK_BITS) + +struct evtchn_fifo_control_block { + uint32_t ready; + uint32_t _rsvd; + uint32_t head[EVTCHN_FIFO_MAX_QUEUES]; +}; +typedef struct evtchn_fifo_control_block evtchn_fifo_control_block_t; + +#endif /* __XEN_PUBLIC_EVENT_CHANNEL_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/features.h b/include/hw/xen/interface/features.h new file mode 100644 index 0000000000..d2a9175aae --- /dev/null +++ b/include/hw/xen/interface/features.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: MIT */ +/****************************************************************************** + * features.h + * + * Feature flags, reported by XENVER_get_features. + * + * Copyright (c) 2006, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_FEATURES_H__ +#define __XEN_PUBLIC_FEATURES_H__ + +/* + * `incontents 200 elfnotes_features XEN_ELFNOTE_FEATURES + * + * The list of all the features the guest supports. They are set by + * parsing the XEN_ELFNOTE_FEATURES and XEN_ELFNOTE_SUPPORTED_FEATURES + * string. The format is the feature names (as given here without the + * "XENFEAT_" prefix) separated by '|' characters. + * If a feature is required for the kernel to function then the feature name + * must be preceded by a '!' character. + * + * Note that if XEN_ELFNOTE_SUPPORTED_FEATURES is used, then in the + * XENFEAT_dom0 MUST be set if the guest is to be booted as dom0, + */ + +/* + * If set, the guest does not need to write-protect its pagetables, and can + * update them via direct writes. + */ +#define XENFEAT_writable_page_tables 0 + +/* + * If set, the guest does not need to write-protect its segment descriptor + * tables, and can update them via direct writes. + */ +#define XENFEAT_writable_descriptor_tables 1 + +/* + * If set, translation between the guest's 'pseudo-physical' address space + * and the host's machine address space are handled by the hypervisor. In this + * mode the guest does not need to perform phys-to/from-machine translations + * when performing page table operations. + */ +#define XENFEAT_auto_translated_physmap 2 + +/* If set, the guest is running in supervisor mode (e.g., x86 ring 0). */ +#define XENFEAT_supervisor_mode_kernel 3 + +/* + * If set, the guest does not need to allocate x86 PAE page directories + * below 4GB. This flag is usually implied by auto_translated_physmap. + */ +#define XENFEAT_pae_pgdir_above_4gb 4 + +/* x86: Does this Xen host support the MMU_PT_UPDATE_PRESERVE_AD hypercall? */ +#define XENFEAT_mmu_pt_update_preserve_ad 5 + +/* x86: Does this Xen host support the MMU_{CLEAR,COPY}_PAGE hypercall? */ +#define XENFEAT_highmem_assist 6 + +/* + * If set, GNTTABOP_map_grant_ref honors flags to be placed into guest kernel + * available pte bits. + */ +#define XENFEAT_gnttab_map_avail_bits 7 + +/* x86: Does this Xen host support the HVM callback vector type? */ +#define XENFEAT_hvm_callback_vector 8 + +/* x86: pvclock algorithm is safe to use on HVM */ +#define XENFEAT_hvm_safe_pvclock 9 + +/* x86: pirq can be used by HVM guests */ +#define XENFEAT_hvm_pirqs 10 + +/* operation as Dom0 is supported */ +#define XENFEAT_dom0 11 + +/* Xen also maps grant references at pfn = mfn. + * This feature flag is deprecated and should not be used. +#define XENFEAT_grant_map_identity 12 + */ + +/* Guest can use XENMEMF_vnode to specify virtual node for memory op. */ +#define XENFEAT_memory_op_vnode_supported 13 + +/* arm: Hypervisor supports ARM SMC calling convention. */ +#define XENFEAT_ARM_SMCCC_supported 14 + +/* + * x86/PVH: If set, ACPI RSDP can be placed at any address. Otherwise RSDP + * must be located in lower 1MB, as required by ACPI Specification for IA-PC + * systems. + * This feature flag is only consulted if XEN_ELFNOTE_GUEST_OS contains + * the "linux" string. + */ +#define XENFEAT_linux_rsdp_unrestricted 15 + +/* + * A direct-mapped (or 1:1 mapped) domain is a domain for which its + * local pages have gfn == mfn. If a domain is direct-mapped, + * XENFEAT_direct_mapped is set; otherwise XENFEAT_not_direct_mapped + * is set. + * + * If neither flag is set (e.g. older Xen releases) the assumptions are: + * - not auto_translated domains (x86 only) are always direct-mapped + * - on x86, auto_translated domains are not direct-mapped + * - on ARM, Dom0 is direct-mapped, DomUs are not + */ +#define XENFEAT_not_direct_mapped 16 +#define XENFEAT_direct_mapped 17 + +#define XENFEAT_NR_SUBMAPS 1 + +#endif /* __XEN_PUBLIC_FEATURES_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/grant_table.h b/include/hw/xen/interface/grant_table.h index 2af0cbdde3..1dfa17a6d0 100644 --- a/include/hw/xen/interface/grant_table.h +++ b/include/hw/xen/interface/grant_table.h @@ -1,36 +1,669 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * grant_table.h * * Interface for granting foreign access to page frames, and receiving * page-ownership transfers. * - * 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. - * * Copyright (c) 2004, K A Fraser */ #ifndef __XEN_PUBLIC_GRANT_TABLE_H__ #define __XEN_PUBLIC_GRANT_TABLE_H__ +#include "xen.h" + +/* + * `incontents 150 gnttab Grant Tables + * + * Xen's grant tables provide a generic mechanism to memory sharing + * between domains. This shared memory interface underpins the split + * device drivers for block and network IO. + * + * Each domain has its own grant table. This is a data structure that + * is shared with Xen; it allows the domain to tell Xen what kind of + * permissions other domains have on its pages. Entries in the grant + * table are identified by grant references. A grant reference is an + * integer, which indexes into the grant table. It acts as a + * capability which the grantee can use to perform operations on the + * granter's memory. + * + * This capability-based system allows shared-memory communications + * between unprivileged domains. A grant reference also encapsulates + * the details of a shared page, removing the need for a domain to + * know the real machine address of a page it is sharing. This makes + * it possible to share memory correctly with domains running in + * fully virtualised memory. + */ + +/*********************************** + * GRANT TABLE REPRESENTATION + */ + +/* Some rough guidelines on accessing and updating grant-table entries + * in a concurrency-safe manner. For more information, Linux contains a + * reference implementation for guest OSes (drivers/xen/grant_table.c, see + * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=drivers/xen/grant-table.c;hb=HEAD + * + * NB. WMB is a no-op on current-generation x86 processors. However, a + * compiler barrier will still be required. + * + * Introducing a valid entry into the grant table: + * 1. Write ent->domid. + * 2. Write ent->frame: + * GTF_permit_access: Frame to which access is permitted. + * GTF_accept_transfer: Pseudo-phys frame slot being filled by new + * frame, or zero if none. + * 3. Write memory barrier (WMB). + * 4. Write ent->flags, inc. valid type. + * + * Invalidating an unused GTF_permit_access entry: + * 1. flags = ent->flags. + * 2. Observe that !(flags & (GTF_reading|GTF_writing)). + * 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0). + * NB. No need for WMB as reuse of entry is control-dependent on success of + * step 3, and all architectures guarantee ordering of ctrl-dep writes. + * + * Invalidating an in-use GTF_permit_access entry: + * This cannot be done directly. Request assistance from the domain controller + * which can set a timeout on the use of a grant entry and take necessary + * action. (NB. This is not yet implemented!). + * + * Invalidating an unused GTF_accept_transfer entry: + * 1. flags = ent->flags. + * 2. Observe that !(flags & GTF_transfer_committed). [*] + * 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0). + * NB. No need for WMB as reuse of entry is control-dependent on success of + * step 3, and all architectures guarantee ordering of ctrl-dep writes. + * [*] If GTF_transfer_committed is set then the grant entry is 'committed'. + * The guest must /not/ modify the grant entry until the address of the + * transferred frame is written. It is safe for the guest to spin waiting + * for this to occur (detect by observing GTF_transfer_completed in + * ent->flags). + * + * Invalidating a committed GTF_accept_transfer entry: + * 1. Wait for (ent->flags & GTF_transfer_completed). + * + * Changing a GTF_permit_access from writable to read-only: + * Use SMP-safe CMPXCHG to set GTF_readonly, while checking !GTF_writing. + * + * Changing a GTF_permit_access from read-only to writable: + * Use SMP-safe bit-setting instruction. + */ + /* * Reference to a grant entry in a specified domain's grant table. */ typedef uint32_t grant_ref_t; +/* + * A grant table comprises a packed array of grant entries in one or more + * page frames shared between Xen and a guest. + * [XEN]: This field is written by Xen and read by the sharing guest. + * [GST]: This field is written by the guest and read by Xen. + */ + +/* + * Version 1 of the grant table entry structure is maintained largely for + * backwards compatibility. New guests are recommended to support using + * version 2 to overcome version 1 limitations, but to default to version 1. + */ +#if __XEN_INTERFACE_VERSION__ < 0x0003020a +#define grant_entry_v1 grant_entry +#define grant_entry_v1_t grant_entry_t +#endif +struct grant_entry_v1 { + /* GTF_xxx: various type and flag information. [XEN,GST] */ + uint16_t flags; + /* The domain being granted foreign privileges. [GST] */ + domid_t domid; + /* + * GTF_permit_access: GFN that @domid is allowed to map and access. [GST] + * GTF_accept_transfer: GFN that @domid is allowed to transfer into. [GST] + * GTF_transfer_completed: MFN whose ownership transferred by @domid + * (non-translated guests only). [XEN] + */ + uint32_t frame; +}; +typedef struct grant_entry_v1 grant_entry_v1_t; + +/* The first few grant table entries will be preserved across grant table + * version changes and may be pre-populated at domain creation by tools. + */ +#define GNTTAB_NR_RESERVED_ENTRIES 8 +#define GNTTAB_RESERVED_CONSOLE 0 +#define GNTTAB_RESERVED_XENSTORE 1 + +/* + * Type of grant entry. + * GTF_invalid: This grant entry grants no privileges. + * GTF_permit_access: Allow @domid to map/access @frame. + * GTF_accept_transfer: Allow @domid to transfer ownership of one page frame + * to this guest. Xen writes the page number to @frame. + * GTF_transitive: Allow @domid to transitively access a subrange of + * @trans_grant in @trans_domid. No mappings are allowed. + */ +#define GTF_invalid (0U<<0) +#define GTF_permit_access (1U<<0) +#define GTF_accept_transfer (2U<<0) +#define GTF_transitive (3U<<0) +#define GTF_type_mask (3U<<0) + +/* + * Subflags for GTF_permit_access and GTF_transitive. + * GTF_readonly: Restrict @domid to read-only mappings and accesses. [GST] + * GTF_reading: Grant entry is currently mapped for reading by @domid. [XEN] + * GTF_writing: Grant entry is currently mapped for writing by @domid. [XEN] + * Further subflags for GTF_permit_access only. + * GTF_PAT, GTF_PWT, GTF_PCD: (x86) cache attribute flags to be used for + * mappings of the grant [GST] + * GTF_sub_page: Grant access to only a subrange of the page. @domid + * will only be allowed to copy from the grant, and not + * map it. [GST] + */ +#define _GTF_readonly (2) +#define GTF_readonly (1U<<_GTF_readonly) +#define _GTF_reading (3) +#define GTF_reading (1U<<_GTF_reading) +#define _GTF_writing (4) +#define GTF_writing (1U<<_GTF_writing) +#define _GTF_PWT (5) +#define GTF_PWT (1U<<_GTF_PWT) +#define _GTF_PCD (6) +#define GTF_PCD (1U<<_GTF_PCD) +#define _GTF_PAT (7) +#define GTF_PAT (1U<<_GTF_PAT) +#define _GTF_sub_page (8) +#define GTF_sub_page (1U<<_GTF_sub_page) + +/* + * Subflags for GTF_accept_transfer: + * GTF_transfer_committed: Xen sets this flag to indicate that it is committed + * to transferring ownership of a page frame. When a guest sees this flag + * it must /not/ modify the grant entry until GTF_transfer_completed is + * set by Xen. + * GTF_transfer_completed: It is safe for the guest to spin-wait on this flag + * after reading GTF_transfer_committed. Xen will always write the frame + * address, followed by ORing this flag, in a timely manner. + */ +#define _GTF_transfer_committed (2) +#define GTF_transfer_committed (1U<<_GTF_transfer_committed) +#define _GTF_transfer_completed (3) +#define GTF_transfer_completed (1U<<_GTF_transfer_completed) + +/* + * Version 2 grant table entries. These fulfil the same role as + * version 1 entries, but can represent more complicated operations. + * Any given domain will have either a version 1 or a version 2 table, + * and every entry in the table will be the same version. + * + * The interface by which domains use grant references does not depend + * on the grant table version in use by the other domain. + */ +#if __XEN_INTERFACE_VERSION__ >= 0x0003020a +/* + * Version 1 and version 2 grant entries share a common prefix. The + * fields of the prefix are documented as part of struct + * grant_entry_v1. + */ +struct grant_entry_header { + uint16_t flags; + domid_t domid; +}; +typedef struct grant_entry_header grant_entry_header_t; + +/* + * Version 2 of the grant entry structure. + */ +union grant_entry_v2 { + grant_entry_header_t hdr; + + /* + * This member is used for V1-style full page grants, where either: + * + * -- hdr.type is GTF_accept_transfer, or + * -- hdr.type is GTF_permit_access and GTF_sub_page is not set. + * + * In that case, the frame field has the same semantics as the + * field of the same name in the V1 entry structure. + */ + struct { + grant_entry_header_t hdr; + uint32_t pad0; + uint64_t frame; + } full_page; + + /* + * If the grant type is GTF_grant_access and GTF_sub_page is set, + * @domid is allowed to access bytes [@page_off,@page_off+@length) + * in frame @frame. + */ + struct { + grant_entry_header_t hdr; + uint16_t page_off; + uint16_t length; + uint64_t frame; + } sub_page; + + /* + * If the grant is GTF_transitive, @domid is allowed to use the + * grant @gref in domain @trans_domid, as if it was the local + * domain. Obviously, the transitive access must be compatible + * with the original grant. + * + * The current version of Xen does not allow transitive grants + * to be mapped. + */ + struct { + grant_entry_header_t hdr; + domid_t trans_domid; + uint16_t pad0; + grant_ref_t gref; + } transitive; + + uint32_t __spacer[4]; /* Pad to a power of two */ +}; +typedef union grant_entry_v2 grant_entry_v2_t; + +typedef uint16_t grant_status_t; + +#endif /* __XEN_INTERFACE_VERSION__ */ + +/*********************************** + * GRANT TABLE QUERIES AND USES + */ + +/* ` enum neg_errnoval + * ` HYPERVISOR_grant_table_op(enum grant_table_op cmd, + * ` void *args, + * ` unsigned int count) + * ` + * + * @args points to an array of a per-command data structure. The array + * has @count members + */ + +/* ` enum grant_table_op { // GNTTABOP_* => struct gnttab_* */ +#define GNTTABOP_map_grant_ref 0 +#define GNTTABOP_unmap_grant_ref 1 +#define GNTTABOP_setup_table 2 +#define GNTTABOP_dump_table 3 +#define GNTTABOP_transfer 4 +#define GNTTABOP_copy 5 +#define GNTTABOP_query_size 6 +#define GNTTABOP_unmap_and_replace 7 +#if __XEN_INTERFACE_VERSION__ >= 0x0003020a +#define GNTTABOP_set_version 8 +#define GNTTABOP_get_status_frames 9 +#define GNTTABOP_get_version 10 +#define GNTTABOP_swap_grant_ref 11 +#define GNTTABOP_cache_flush 12 +#endif /* __XEN_INTERFACE_VERSION__ */ +/* ` } */ + +/* + * Handle to track a mapping created via a grant reference. + */ +typedef uint32_t grant_handle_t; + +/* + * GNTTABOP_map_grant_ref: Map the grant entry (,) for access + * by devices and/or host CPUs. If successful, is a tracking number + * that must be presented later to destroy the mapping(s). On error, + * is a negative status code. + * NOTES: + * 1. If GNTMAP_device_map is specified then is the address + * via which I/O devices may access the granted frame. + * 2. If GNTMAP_host_map is specified then a mapping will be added at + * either a host virtual address in the current address space, or at + * a PTE at the specified machine address. The type of mapping to + * perform is selected through the GNTMAP_contains_pte flag, and the + * address is specified in . + * 3. Mappings should only be destroyed via GNTTABOP_unmap_grant_ref. If a + * host mapping is destroyed by other means then it is *NOT* guaranteed + * to be accounted to the correct grant reference! + */ +struct gnttab_map_grant_ref { + /* IN parameters. */ + uint64_t host_addr; + uint32_t flags; /* GNTMAP_* */ + grant_ref_t ref; + domid_t dom; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ + grant_handle_t handle; + uint64_t dev_bus_addr; +}; +typedef struct gnttab_map_grant_ref gnttab_map_grant_ref_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_map_grant_ref_t); + +/* + * GNTTABOP_unmap_grant_ref: Destroy one or more grant-reference mappings + * tracked by . If or is zero, that + * field is ignored. If non-zero, they must refer to a device/host mapping + * that is tracked by + * NOTES: + * 1. The call may fail in an undefined manner if either mapping is not + * tracked by . + * 3. After executing a batch of unmaps, it is guaranteed that no stale + * mappings will remain in the device or host TLBs. + */ +struct gnttab_unmap_grant_ref { + /* IN parameters. */ + uint64_t host_addr; + uint64_t dev_bus_addr; + grant_handle_t handle; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_unmap_grant_ref gnttab_unmap_grant_ref_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_grant_ref_t); + +/* + * GNTTABOP_setup_table: Set up a grant table for comprising at least + * pages. The frame addresses are written to the . + * Only addresses are written, even if the table is larger. + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify != DOMID_SELF. + * 3. Xen may not support more than a single grant-table page per domain. + */ +struct gnttab_setup_table { + /* IN parameters. */ + domid_t dom; + uint32_t nr_frames; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ +#if __XEN_INTERFACE_VERSION__ < 0x00040300 + XEN_GUEST_HANDLE(ulong) frame_list; +#else + XEN_GUEST_HANDLE(xen_pfn_t) frame_list; +#endif +}; +typedef struct gnttab_setup_table gnttab_setup_table_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_setup_table_t); + +/* + * GNTTABOP_dump_table: Dump the contents of the grant table to the + * xen console. Debugging use only. + */ +struct gnttab_dump_table { + /* IN parameters. */ + domid_t dom; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_dump_table gnttab_dump_table_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_dump_table_t); + +/* + * GNTTABOP_transfer: Transfer to a foreign domain. The foreign domain + * has previously registered its interest in the transfer via . + * + * Note that, even if the transfer fails, the specified page no longer belongs + * to the calling domain *unless* the error is GNTST_bad_page. + * + * Note further that only PV guests can use this operation. + */ +struct gnttab_transfer { + /* IN parameters. */ + xen_pfn_t mfn; + domid_t domid; + grant_ref_t ref; + /* OUT parameters. */ + int16_t status; +}; +typedef struct gnttab_transfer gnttab_transfer_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_transfer_t); + + +/* + * GNTTABOP_copy: Hypervisor based copy + * source and destinations can be eithers MFNs or, for foreign domains, + * grant references. the foreign domain has to grant read/write access + * in its grant table. + * + * The flags specify what type source and destinations are (either MFN + * or grant reference). + * + * Note that this can also be used to copy data between two domains + * via a third party if the source and destination domains had previously + * grant appropriate access to their pages to the third party. + * + * source_offset specifies an offset in the source frame, dest_offset + * the offset in the target frame and len specifies the number of + * bytes to be copied. + */ + +#define _GNTCOPY_source_gref (0) +#define GNTCOPY_source_gref (1<<_GNTCOPY_source_gref) +#define _GNTCOPY_dest_gref (1) +#define GNTCOPY_dest_gref (1<<_GNTCOPY_dest_gref) + +struct gnttab_copy { + /* IN parameters. */ + struct gnttab_copy_ptr { + union { + grant_ref_t ref; + xen_pfn_t gmfn; + } u; + domid_t domid; + uint16_t offset; + } source, dest; + uint16_t len; + uint16_t flags; /* GNTCOPY_* */ + /* OUT parameters. */ + int16_t status; +}; +typedef struct gnttab_copy gnttab_copy_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_copy_t); + +/* + * GNTTABOP_query_size: Query the current and maximum sizes of the shared + * grant table. + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify != DOMID_SELF. + */ +struct gnttab_query_size { + /* IN parameters. */ + domid_t dom; + /* OUT parameters. */ + uint32_t nr_frames; + uint32_t max_nr_frames; + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_query_size gnttab_query_size_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_query_size_t); + +/* + * GNTTABOP_unmap_and_replace: Destroy one or more grant-reference mappings + * tracked by but atomically replace the page table entry with one + * pointing to the machine address under . will be + * redirected to the null entry. + * NOTES: + * 1. The call may fail in an undefined manner if either mapping is not + * tracked by . + * 2. After executing a batch of unmaps, it is guaranteed that no stale + * mappings will remain in the device or host TLBs. + */ +struct gnttab_unmap_and_replace { + /* IN parameters. */ + uint64_t host_addr; + uint64_t new_addr; + grant_handle_t handle; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_unmap_and_replace gnttab_unmap_and_replace_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_and_replace_t); + +#if __XEN_INTERFACE_VERSION__ >= 0x0003020a +/* + * GNTTABOP_set_version: Request a particular version of the grant + * table shared table structure. This operation may be used to toggle + * between different versions, but must be performed while no grants + * are active. The only defined versions are 1 and 2. + */ +struct gnttab_set_version { + /* IN/OUT parameters */ + uint32_t version; +}; +typedef struct gnttab_set_version gnttab_set_version_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_set_version_t); + + +/* + * GNTTABOP_get_status_frames: Get the list of frames used to store grant + * status for . In grant format version 2, the status is separated + * from the other shared grant fields to allow more efficient synchronization + * using barriers instead of atomic cmpexch operations. + * specify the size of vector . + * The frame addresses are returned in the . + * Only addresses are returned, even if the table is larger. + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify != DOMID_SELF. + */ +struct gnttab_get_status_frames { + /* IN parameters. */ + uint32_t nr_frames; + domid_t dom; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ + XEN_GUEST_HANDLE(uint64_t) frame_list; +}; +typedef struct gnttab_get_status_frames gnttab_get_status_frames_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_get_status_frames_t); + +/* + * GNTTABOP_get_version: Get the grant table version which is in + * effect for domain . + */ +struct gnttab_get_version { + /* IN parameters */ + domid_t dom; + uint16_t pad; + /* OUT parameters */ + uint32_t version; +}; +typedef struct gnttab_get_version gnttab_get_version_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_get_version_t); + +/* + * GNTTABOP_swap_grant_ref: Swap the contents of two grant entries. + */ +struct gnttab_swap_grant_ref { + /* IN parameters */ + grant_ref_t ref_a; + grant_ref_t ref_b; + /* OUT parameters */ + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_swap_grant_ref gnttab_swap_grant_ref_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_swap_grant_ref_t); + +/* + * Issue one or more cache maintenance operations on a portion of a + * page granted to the calling domain by a foreign domain. + */ +struct gnttab_cache_flush { + union { + uint64_t dev_bus_addr; + grant_ref_t ref; + } a; + uint16_t offset; /* offset from start of grant */ + uint16_t length; /* size within the grant */ +#define GNTTAB_CACHE_CLEAN (1u<<0) +#define GNTTAB_CACHE_INVAL (1u<<1) +#define GNTTAB_CACHE_SOURCE_GREF (1u<<31) + uint32_t op; +}; +typedef struct gnttab_cache_flush gnttab_cache_flush_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_cache_flush_t); + +#endif /* __XEN_INTERFACE_VERSION__ */ + +/* + * Bitfield values for gnttab_map_grant_ref.flags. + */ + /* Map the grant entry for access by I/O devices. */ +#define _GNTMAP_device_map (0) +#define GNTMAP_device_map (1<<_GNTMAP_device_map) + /* Map the grant entry for access by host CPUs. */ +#define _GNTMAP_host_map (1) +#define GNTMAP_host_map (1<<_GNTMAP_host_map) + /* Accesses to the granted frame will be restricted to read-only access. */ +#define _GNTMAP_readonly (2) +#define GNTMAP_readonly (1<<_GNTMAP_readonly) + /* + * GNTMAP_host_map subflag: + * 0 => The host mapping is usable only by the guest OS. + * 1 => The host mapping is usable by guest OS + current application. + */ +#define _GNTMAP_application_map (3) +#define GNTMAP_application_map (1<<_GNTMAP_application_map) + + /* + * GNTMAP_contains_pte subflag: + * 0 => This map request contains a host virtual address. + * 1 => This map request contains the machine addess of the PTE to update. + */ +#define _GNTMAP_contains_pte (4) +#define GNTMAP_contains_pte (1<<_GNTMAP_contains_pte) + +/* + * Bits to be placed in guest kernel available PTE bits (architecture + * dependent; only supported when XENFEAT_gnttab_map_avail_bits is set). + */ +#define _GNTMAP_guest_avail0 (16) +#define GNTMAP_guest_avail_mask ((uint32_t)~0 << _GNTMAP_guest_avail0) + +/* + * Values for error status returns. All errors are -ve. + */ +/* ` enum grant_status { */ +#define GNTST_okay (0) /* Normal return. */ +#define GNTST_general_error (-1) /* General undefined error. */ +#define GNTST_bad_domain (-2) /* Unrecognsed domain id. */ +#define GNTST_bad_gntref (-3) /* Unrecognised or inappropriate gntref. */ +#define GNTST_bad_handle (-4) /* Unrecognised or inappropriate handle. */ +#define GNTST_bad_virt_addr (-5) /* Inappropriate virtual address to map. */ +#define GNTST_bad_dev_addr (-6) /* Inappropriate device address to unmap.*/ +#define GNTST_no_device_space (-7) /* Out of space in I/O MMU. */ +#define GNTST_permission_denied (-8) /* Not enough privilege for operation. */ +#define GNTST_bad_page (-9) /* Specified page was invalid for op. */ +#define GNTST_bad_copy_arg (-10) /* copy arguments cross page boundary. */ +#define GNTST_address_too_big (-11) /* transfer page address too large. */ +#define GNTST_eagain (-12) /* Operation not done; try again. */ +#define GNTST_no_space (-13) /* Out of space (handles etc). */ +/* ` } */ + +#define GNTTABOP_error_msgs { \ + "okay", \ + "undefined error", \ + "unrecognised domain id", \ + "invalid grant reference", \ + "invalid mapping handle", \ + "invalid virtual address", \ + "invalid device address", \ + "no spare translation slot in the I/O MMU", \ + "permission denied", \ + "bad page", \ + "copy arguments cross page boundary", \ + "page address size too large", \ + "operation not done; try again", \ + "out of space", \ +} + #endif /* __XEN_PUBLIC_GRANT_TABLE_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/hvm/hvm_op.h b/include/hw/xen/interface/hvm/hvm_op.h new file mode 100644 index 0000000000..e22adf0319 --- /dev/null +++ b/include/hw/xen/interface/hvm/hvm_op.h @@ -0,0 +1,378 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2007, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_HVM_HVM_OP_H__ +#define __XEN_PUBLIC_HVM_HVM_OP_H__ + +#include "../xen.h" +#include "../trace.h" +#include "../event_channel.h" + +/* Get/set subcommands: extra argument == pointer to xen_hvm_param struct. */ +#define HVMOP_set_param 0 +#define HVMOP_get_param 1 +struct xen_hvm_param { + domid_t domid; /* IN */ + uint16_t pad; + uint32_t index; /* IN */ + uint64_t value; /* IN/OUT */ +}; +typedef struct xen_hvm_param xen_hvm_param_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_param_t); + +struct xen_hvm_altp2m_suppress_ve { + uint16_t view; + uint8_t suppress_ve; /* Boolean type. */ + uint8_t pad1; + uint32_t pad2; + uint64_t gfn; +}; + +struct xen_hvm_altp2m_suppress_ve_multi { + uint16_t view; + uint8_t suppress_ve; /* Boolean type. */ + uint8_t pad1; + int32_t first_error; /* Should be set to 0. */ + uint64_t first_gfn; /* Value may be updated. */ + uint64_t last_gfn; + uint64_t first_error_gfn; /* Gfn of the first error. */ +}; + +#if __XEN_INTERFACE_VERSION__ < 0x00040900 + +/* Set the logical level of one of a domain's PCI INTx wires. */ +#define HVMOP_set_pci_intx_level 2 +struct xen_hvm_set_pci_intx_level { + /* Domain to be updated. */ + domid_t domid; + /* PCI INTx identification in PCI topology (domain:bus:device:intx). */ + uint8_t domain, bus, device, intx; + /* Assertion level (0 = unasserted, 1 = asserted). */ + uint8_t level; +}; +typedef struct xen_hvm_set_pci_intx_level xen_hvm_set_pci_intx_level_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_pci_intx_level_t); + +/* Set the logical level of one of a domain's ISA IRQ wires. */ +#define HVMOP_set_isa_irq_level 3 +struct xen_hvm_set_isa_irq_level { + /* Domain to be updated. */ + domid_t domid; + /* ISA device identification, by ISA IRQ (0-15). */ + uint8_t isa_irq; + /* Assertion level (0 = unasserted, 1 = asserted). */ + uint8_t level; +}; +typedef struct xen_hvm_set_isa_irq_level xen_hvm_set_isa_irq_level_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_isa_irq_level_t); + +#define HVMOP_set_pci_link_route 4 +struct xen_hvm_set_pci_link_route { + /* Domain to be updated. */ + domid_t domid; + /* PCI link identifier (0-3). */ + uint8_t link; + /* ISA IRQ (1-15), or 0 (disable link). */ + uint8_t isa_irq; +}; +typedef struct xen_hvm_set_pci_link_route xen_hvm_set_pci_link_route_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_pci_link_route_t); + +#endif /* __XEN_INTERFACE_VERSION__ < 0x00040900 */ + +/* Flushes all VCPU TLBs: @arg must be NULL. */ +#define HVMOP_flush_tlbs 5 + +/* + * hvmmem_type_t should not be defined when generating the corresponding + * compat header. This will ensure that the improperly named HVMMEM_(*) + * values are defined only once. + */ +#ifndef XEN_GENERATING_COMPAT_HEADERS + +typedef enum { + HVMMEM_ram_rw, /* Normal read/write guest RAM */ + HVMMEM_ram_ro, /* Read-only; writes are discarded */ + HVMMEM_mmio_dm, /* Reads and write go to the device model */ +#if __XEN_INTERFACE_VERSION__ < 0x00040700 + HVMMEM_mmio_write_dm, /* Read-only; writes go to the device model */ +#else + HVMMEM_unused, /* Placeholder; setting memory to this type + will fail for code after 4.7.0 */ +#endif + HVMMEM_ioreq_server /* Memory type claimed by an ioreq server; type + changes to this value are only allowed after + an ioreq server has claimed its ownership. + Only pages with HVMMEM_ram_rw are allowed to + change to this type; conversely, pages with + this type are only allowed to be changed back + to HVMMEM_ram_rw. */ +} hvmmem_type_t; + +#endif /* XEN_GENERATING_COMPAT_HEADERS */ + +/* Hint from PV drivers for pagetable destruction. */ +#define HVMOP_pagetable_dying 9 +struct xen_hvm_pagetable_dying { + /* Domain with a pagetable about to be destroyed. */ + domid_t domid; + uint16_t pad[3]; /* align next field on 8-byte boundary */ + /* guest physical address of the toplevel pagetable dying */ + uint64_t gpa; +}; +typedef struct xen_hvm_pagetable_dying xen_hvm_pagetable_dying_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_pagetable_dying_t); + +/* Get the current Xen time, in nanoseconds since system boot. */ +#define HVMOP_get_time 10 +struct xen_hvm_get_time { + uint64_t now; /* OUT */ +}; +typedef struct xen_hvm_get_time xen_hvm_get_time_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_get_time_t); + +#define HVMOP_xentrace 11 +struct xen_hvm_xentrace { + uint16_t event, extra_bytes; + uint8_t extra[TRACE_EXTRA_MAX * sizeof(uint32_t)]; +}; +typedef struct xen_hvm_xentrace xen_hvm_xentrace_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_xentrace_t); + +/* Following tools-only interfaces may change in future. */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +/* Deprecated by XENMEM_access_op_set_access */ +#define HVMOP_set_mem_access 12 + +/* Deprecated by XENMEM_access_op_get_access */ +#define HVMOP_get_mem_access 13 + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +#define HVMOP_get_mem_type 15 +/* Return hvmmem_type_t for the specified pfn. */ +struct xen_hvm_get_mem_type { + /* Domain to be queried. */ + domid_t domid; + /* OUT variable. */ + uint16_t mem_type; + uint16_t pad[2]; /* align next field on 8-byte boundary */ + /* IN variable. */ + uint64_t pfn; +}; +typedef struct xen_hvm_get_mem_type xen_hvm_get_mem_type_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_get_mem_type_t); + +/* Following tools-only interfaces may change in future. */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +/* + * Definitions relating to DMOP_create_ioreq_server. (Defined here for + * backwards compatibility). + */ + +#define HVM_IOREQSRV_BUFIOREQ_OFF 0 +#define HVM_IOREQSRV_BUFIOREQ_LEGACY 1 +/* + * Use this when read_pointer gets updated atomically and + * the pointer pair gets read atomically: + */ +#define HVM_IOREQSRV_BUFIOREQ_ATOMIC 2 + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +#if defined(__i386__) || defined(__x86_64__) + +/* + * HVMOP_set_evtchn_upcall_vector: Set a that should be used for event + * channel upcalls on the specified . If set, + * this vector will be used in preference to the + * domain global callback via (see + * HVM_PARAM_CALLBACK_IRQ). + */ +#define HVMOP_set_evtchn_upcall_vector 23 +struct xen_hvm_evtchn_upcall_vector { + uint32_t vcpu; + uint8_t vector; +}; +typedef struct xen_hvm_evtchn_upcall_vector xen_hvm_evtchn_upcall_vector_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_evtchn_upcall_vector_t); + +#endif /* defined(__i386__) || defined(__x86_64__) */ + +#define HVMOP_guest_request_vm_event 24 + +/* HVMOP_altp2m: perform altp2m state operations */ +#define HVMOP_altp2m 25 + +#define HVMOP_ALTP2M_INTERFACE_VERSION 0x00000001 + +struct xen_hvm_altp2m_domain_state { + /* IN or OUT variable on/off */ + uint8_t state; +}; +typedef struct xen_hvm_altp2m_domain_state xen_hvm_altp2m_domain_state_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_domain_state_t); + +struct xen_hvm_altp2m_vcpu_enable_notify { + uint32_t vcpu_id; + uint32_t pad; + /* #VE info area gfn */ + uint64_t gfn; +}; +typedef struct xen_hvm_altp2m_vcpu_enable_notify xen_hvm_altp2m_vcpu_enable_notify_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_vcpu_enable_notify_t); + +struct xen_hvm_altp2m_vcpu_disable_notify { + uint32_t vcpu_id; +}; +typedef struct xen_hvm_altp2m_vcpu_disable_notify xen_hvm_altp2m_vcpu_disable_notify_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_vcpu_disable_notify_t); + +struct xen_hvm_altp2m_view { + /* IN/OUT variable */ + uint16_t view; + uint16_t hvmmem_default_access; /* xenmem_access_t */ +}; +typedef struct xen_hvm_altp2m_view xen_hvm_altp2m_view_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_view_t); + +#if __XEN_INTERFACE_VERSION__ < 0x00040a00 +struct xen_hvm_altp2m_set_mem_access { + /* view */ + uint16_t view; + /* Memory type */ + uint16_t access; /* xenmem_access_t */ + uint32_t pad; + /* gfn */ + uint64_t gfn; +}; +typedef struct xen_hvm_altp2m_set_mem_access xen_hvm_altp2m_set_mem_access_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_set_mem_access_t); +#endif /* __XEN_INTERFACE_VERSION__ < 0x00040a00 */ + +struct xen_hvm_altp2m_mem_access { + /* view */ + uint16_t view; + /* Memory type */ + uint16_t access; /* xenmem_access_t */ + uint32_t pad; + /* gfn */ + uint64_t gfn; +}; +typedef struct xen_hvm_altp2m_mem_access xen_hvm_altp2m_mem_access_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_mem_access_t); + +struct xen_hvm_altp2m_set_mem_access_multi { + /* view */ + uint16_t view; + uint16_t pad; + /* Number of pages */ + uint32_t nr; + /* + * Used for continuation purposes. + * Must be set to zero upon initial invocation. + */ + uint64_t opaque; + /* List of pfns to set access for */ + XEN_GUEST_HANDLE(const_uint64) pfn_list; + /* Corresponding list of access settings for pfn_list */ + XEN_GUEST_HANDLE(const_uint8) access_list; +}; + +struct xen_hvm_altp2m_change_gfn { + /* view */ + uint16_t view; + uint16_t pad1; + uint32_t pad2; + /* old gfn */ + uint64_t old_gfn; + /* new gfn, INVALID_GFN (~0UL) means revert */ + uint64_t new_gfn; +}; +typedef struct xen_hvm_altp2m_change_gfn xen_hvm_altp2m_change_gfn_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_change_gfn_t); + +struct xen_hvm_altp2m_get_vcpu_p2m_idx { + uint32_t vcpu_id; + uint16_t altp2m_idx; +}; + +struct xen_hvm_altp2m_set_visibility { + uint16_t altp2m_idx; + uint8_t visible; + uint8_t pad; +}; + +struct xen_hvm_altp2m_op { + uint32_t version; /* HVMOP_ALTP2M_INTERFACE_VERSION */ + uint32_t cmd; +/* Get/set the altp2m state for a domain */ +#define HVMOP_altp2m_get_domain_state 1 +#define HVMOP_altp2m_set_domain_state 2 +/* Set a given VCPU to receive altp2m event notifications */ +#define HVMOP_altp2m_vcpu_enable_notify 3 +/* Create a new view */ +#define HVMOP_altp2m_create_p2m 4 +/* Destroy a view */ +#define HVMOP_altp2m_destroy_p2m 5 +/* Switch view for an entire domain */ +#define HVMOP_altp2m_switch_p2m 6 +/* Notify that a page of memory is to have specific access types */ +#define HVMOP_altp2m_set_mem_access 7 +/* Change a p2m entry to have a different gfn->mfn mapping */ +#define HVMOP_altp2m_change_gfn 8 +/* Set access for an array of pages */ +#define HVMOP_altp2m_set_mem_access_multi 9 +/* Set the "Suppress #VE" bit on a page */ +#define HVMOP_altp2m_set_suppress_ve 10 +/* Get the "Suppress #VE" bit of a page */ +#define HVMOP_altp2m_get_suppress_ve 11 +/* Get the access of a page of memory from a certain view */ +#define HVMOP_altp2m_get_mem_access 12 +/* Disable altp2m event notifications for a given VCPU */ +#define HVMOP_altp2m_vcpu_disable_notify 13 +/* Get the active vcpu p2m index */ +#define HVMOP_altp2m_get_p2m_idx 14 +/* Set the "Supress #VE" bit for a range of pages */ +#define HVMOP_altp2m_set_suppress_ve_multi 15 +/* Set visibility for a given altp2m view */ +#define HVMOP_altp2m_set_visibility 16 + domid_t domain; + uint16_t pad1; + uint32_t pad2; + union { + struct xen_hvm_altp2m_domain_state domain_state; + struct xen_hvm_altp2m_vcpu_enable_notify enable_notify; + struct xen_hvm_altp2m_view view; +#if __XEN_INTERFACE_VERSION__ < 0x00040a00 + struct xen_hvm_altp2m_set_mem_access set_mem_access; +#endif /* __XEN_INTERFACE_VERSION__ < 0x00040a00 */ + struct xen_hvm_altp2m_mem_access mem_access; + struct xen_hvm_altp2m_change_gfn change_gfn; + struct xen_hvm_altp2m_set_mem_access_multi set_mem_access_multi; + struct xen_hvm_altp2m_suppress_ve suppress_ve; + struct xen_hvm_altp2m_suppress_ve_multi suppress_ve_multi; + struct xen_hvm_altp2m_vcpu_disable_notify disable_notify; + struct xen_hvm_altp2m_get_vcpu_p2m_idx get_vcpu_p2m_idx; + struct xen_hvm_altp2m_set_visibility set_visibility; + uint8_t pad[64]; + } u; +}; +typedef struct xen_hvm_altp2m_op xen_hvm_altp2m_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_hvm_altp2m_op_t); + +#endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/hvm/params.h b/include/hw/xen/interface/hvm/params.h new file mode 100644 index 0000000000..a22b4ed45d --- /dev/null +++ b/include/hw/xen/interface/hvm/params.h @@ -0,0 +1,301 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2007, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_HVM_PARAMS_H__ +#define __XEN_PUBLIC_HVM_PARAMS_H__ + +#include "hvm_op.h" + +/* These parameters are deprecated and their meaning is undefined. */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +#define HVM_PARAM_PAE_ENABLED 4 +#define HVM_PARAM_DM_DOMAIN 13 +#define HVM_PARAM_MEMORY_EVENT_CR0 20 +#define HVM_PARAM_MEMORY_EVENT_CR3 21 +#define HVM_PARAM_MEMORY_EVENT_CR4 22 +#define HVM_PARAM_MEMORY_EVENT_INT3 23 +#define HVM_PARAM_NESTEDHVM 24 +#define HVM_PARAM_MEMORY_EVENT_SINGLE_STEP 25 +#define HVM_PARAM_BUFIOREQ_EVTCHN 26 +#define HVM_PARAM_MEMORY_EVENT_MSR 30 + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +/* + * Parameter space for HVMOP_{set,get}_param. + */ + +#define HVM_PARAM_CALLBACK_IRQ 0 +#define HVM_PARAM_CALLBACK_IRQ_TYPE_MASK xen_mk_ullong(0xFF00000000000000) +/* + * How should CPU0 event-channel notifications be delivered? + * + * If val == 0 then CPU0 event-channel notifications are not delivered. + * If val != 0, val[63:56] encodes the type, as follows: + */ + +#define HVM_PARAM_CALLBACK_TYPE_GSI 0 +/* + * val[55:0] is a delivery GSI. GSI 0 cannot be used, as it aliases val == 0, + * and disables all notifications. + */ + +#define HVM_PARAM_CALLBACK_TYPE_PCI_INTX 1 +/* + * val[55:0] is a delivery PCI INTx line: + * Domain = val[47:32], Bus = val[31:16] DevFn = val[15:8], IntX = val[1:0] + */ + +#if defined(__i386__) || defined(__x86_64__) +#define HVM_PARAM_CALLBACK_TYPE_VECTOR 2 +/* + * val[7:0] is a vector number. Check for XENFEAT_hvm_callback_vector to know + * if this delivery method is available. + */ +#elif defined(__arm__) || defined(__aarch64__) +#define HVM_PARAM_CALLBACK_TYPE_PPI 2 +/* + * val[55:16] needs to be zero. + * val[15:8] is interrupt flag of the PPI used by event-channel: + * bit 8: the PPI is edge(1) or level(0) triggered + * bit 9: the PPI is active low(1) or high(0) + * val[7:0] is a PPI number used by event-channel. + * This is only used by ARM/ARM64 and masking/eoi the interrupt associated to + * the notification is handled by the interrupt controller. + */ +#define HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_MASK 0xFF00 +#define HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_LOW_LEVEL 2 +#endif + +/* + * These are not used by Xen. They are here for convenience of HVM-guest + * xenbus implementations. + */ +#define HVM_PARAM_STORE_PFN 1 +#define HVM_PARAM_STORE_EVTCHN 2 + +#define HVM_PARAM_IOREQ_PFN 5 + +#define HVM_PARAM_BUFIOREQ_PFN 6 + +#if defined(__i386__) || defined(__x86_64__) + +/* + * Viridian enlightenments + * + * (See http://download.microsoft.com/download/A/B/4/AB43A34E-BDD0-4FA6-BDEF-79EEF16E880B/Hypervisor%20Top%20Level%20Functional%20Specification%20v4.0.docx) + * + * To expose viridian enlightenments to the guest set this parameter + * to the desired feature mask. The base feature set must be present + * in any valid feature mask. + */ +#define HVM_PARAM_VIRIDIAN 9 + +/* Base+Freq viridian feature sets: + * + * - Hypercall MSRs (HV_X64_MSR_GUEST_OS_ID and HV_X64_MSR_HYPERCALL) + * - APIC access MSRs (HV_X64_MSR_EOI, HV_X64_MSR_ICR and HV_X64_MSR_TPR) + * - Virtual Processor index MSR (HV_X64_MSR_VP_INDEX) + * - Timer frequency MSRs (HV_X64_MSR_TSC_FREQUENCY and + * HV_X64_MSR_APIC_FREQUENCY) + */ +#define _HVMPV_base_freq 0 +#define HVMPV_base_freq (1 << _HVMPV_base_freq) + +/* Feature set modifications */ + +/* Disable timer frequency MSRs (HV_X64_MSR_TSC_FREQUENCY and + * HV_X64_MSR_APIC_FREQUENCY). + * This modification restores the viridian feature set to the + * original 'base' set exposed in releases prior to Xen 4.4. + */ +#define _HVMPV_no_freq 1 +#define HVMPV_no_freq (1 << _HVMPV_no_freq) + +/* Enable Partition Time Reference Counter (HV_X64_MSR_TIME_REF_COUNT) */ +#define _HVMPV_time_ref_count 2 +#define HVMPV_time_ref_count (1 << _HVMPV_time_ref_count) + +/* Enable Reference TSC Page (HV_X64_MSR_REFERENCE_TSC) */ +#define _HVMPV_reference_tsc 3 +#define HVMPV_reference_tsc (1 << _HVMPV_reference_tsc) + +/* Use Hypercall for remote TLB flush */ +#define _HVMPV_hcall_remote_tlb_flush 4 +#define HVMPV_hcall_remote_tlb_flush (1 << _HVMPV_hcall_remote_tlb_flush) + +/* Use APIC assist */ +#define _HVMPV_apic_assist 5 +#define HVMPV_apic_assist (1 << _HVMPV_apic_assist) + +/* Enable crash MSRs */ +#define _HVMPV_crash_ctl 6 +#define HVMPV_crash_ctl (1 << _HVMPV_crash_ctl) + +/* Enable SYNIC MSRs */ +#define _HVMPV_synic 7 +#define HVMPV_synic (1 << _HVMPV_synic) + +/* Enable STIMER MSRs */ +#define _HVMPV_stimer 8 +#define HVMPV_stimer (1 << _HVMPV_stimer) + +/* Use Synthetic Cluster IPI Hypercall */ +#define _HVMPV_hcall_ipi 9 +#define HVMPV_hcall_ipi (1 << _HVMPV_hcall_ipi) + +/* Enable ExProcessorMasks */ +#define _HVMPV_ex_processor_masks 10 +#define HVMPV_ex_processor_masks (1 << _HVMPV_ex_processor_masks) + +/* Allow more than 64 VPs */ +#define _HVMPV_no_vp_limit 11 +#define HVMPV_no_vp_limit (1 << _HVMPV_no_vp_limit) + +/* Enable vCPU hotplug */ +#define _HVMPV_cpu_hotplug 12 +#define HVMPV_cpu_hotplug (1 << _HVMPV_cpu_hotplug) + +#define HVMPV_feature_mask \ + (HVMPV_base_freq | \ + HVMPV_no_freq | \ + HVMPV_time_ref_count | \ + HVMPV_reference_tsc | \ + HVMPV_hcall_remote_tlb_flush | \ + HVMPV_apic_assist | \ + HVMPV_crash_ctl | \ + HVMPV_synic | \ + HVMPV_stimer | \ + HVMPV_hcall_ipi | \ + HVMPV_ex_processor_masks | \ + HVMPV_no_vp_limit | \ + HVMPV_cpu_hotplug) + +#endif + +/* + * Set mode for virtual timers (currently x86 only): + * delay_for_missed_ticks (default): + * Do not advance a vcpu's time beyond the correct delivery time for + * interrupts that have been missed due to preemption. Deliver missed + * interrupts when the vcpu is rescheduled and advance the vcpu's virtual + * time stepwise for each one. + * no_delay_for_missed_ticks: + * As above, missed interrupts are delivered, but guest time always tracks + * wallclock (i.e., real) time while doing so. + * no_missed_ticks_pending: + * No missed interrupts are held pending. Instead, to ensure ticks are + * delivered at some non-zero rate, if we detect missed ticks then the + * internal tick alarm is not disabled if the VCPU is preempted during the + * next tick period. + * one_missed_tick_pending: + * Missed interrupts are collapsed together and delivered as one 'late tick'. + * Guest time always tracks wallclock (i.e., real) time. + */ +#define HVM_PARAM_TIMER_MODE 10 +#define HVMPTM_delay_for_missed_ticks 0 +#define HVMPTM_no_delay_for_missed_ticks 1 +#define HVMPTM_no_missed_ticks_pending 2 +#define HVMPTM_one_missed_tick_pending 3 + +/* Boolean: Enable virtual HPET (high-precision event timer)? (x86-only) */ +#define HVM_PARAM_HPET_ENABLED 11 + +/* Identity-map page directory used by Intel EPT when CR0.PG=0. */ +#define HVM_PARAM_IDENT_PT 12 + +/* ACPI S state: currently support S0 and S3 on x86. */ +#define HVM_PARAM_ACPI_S_STATE 14 + +/* TSS used on Intel when CR0.PE=0. */ +#define HVM_PARAM_VM86_TSS 15 + +/* Boolean: Enable aligning all periodic vpts to reduce interrupts */ +#define HVM_PARAM_VPT_ALIGN 16 + +/* Console debug shared memory ring and event channel */ +#define HVM_PARAM_CONSOLE_PFN 17 +#define HVM_PARAM_CONSOLE_EVTCHN 18 + +/* + * Select location of ACPI PM1a and TMR control blocks. Currently two locations + * are supported, specified by version 0 or 1 in this parameter: + * - 0: default, use the old addresses + * PM1A_EVT == 0x1f40; PM1A_CNT == 0x1f44; PM_TMR == 0x1f48 + * - 1: use the new default qemu addresses + * PM1A_EVT == 0xb000; PM1A_CNT == 0xb004; PM_TMR == 0xb008 + * You can find these address definitions in + */ +#define HVM_PARAM_ACPI_IOPORTS_LOCATION 19 + +/* Params for the mem event rings */ +#define HVM_PARAM_PAGING_RING_PFN 27 +#define HVM_PARAM_MONITOR_RING_PFN 28 +#define HVM_PARAM_SHARING_RING_PFN 29 + +/* SHUTDOWN_* action in case of a triple fault */ +#define HVM_PARAM_TRIPLE_FAULT_REASON 31 + +#define HVM_PARAM_IOREQ_SERVER_PFN 32 +#define HVM_PARAM_NR_IOREQ_SERVER_PAGES 33 + +/* Location of the VM Generation ID in guest physical address space. */ +#define HVM_PARAM_VM_GENERATION_ID_ADDR 34 + +/* + * Set mode for altp2m: + * disabled: don't activate altp2m (default) + * mixed: allow access to all altp2m ops for both in-guest and external tools + * external: allow access to external privileged tools only + * limited: guest only has limited access (ie. control VMFUNC and #VE) + * + * Note that 'mixed' mode has not been evaluated for safety from a + * security perspective. Before using this mode in a + * security-critical environment, each subop should be evaluated for + * safety, with unsafe subops blacklisted in XSM. + */ +#define HVM_PARAM_ALTP2M 35 +#define XEN_ALTP2M_disabled 0 +#define XEN_ALTP2M_mixed 1 +#define XEN_ALTP2M_external 2 +#define XEN_ALTP2M_limited 3 + +/* + * Size of the x87 FPU FIP/FDP registers that the hypervisor needs to + * save/restore. This is a workaround for a hardware limitation that + * does not allow the full FIP/FDP and FCS/FDS to be restored. + * + * Valid values are: + * + * 8: save/restore 64-bit FIP/FDP and clear FCS/FDS (default if CPU + * has FPCSDS feature). + * + * 4: save/restore 32-bit FIP/FDP, FCS/FDS, and clear upper 32-bits of + * FIP/FDP. + * + * 0: allow hypervisor to choose based on the value of FIP/FDP + * (default if CPU does not have FPCSDS). + * + * If FPCSDS (bit 13 in CPUID leaf 0x7, subleaf 0x0) is set, the CPU + * never saves FCS/FDS and this parameter should be left at the + * default of 8. + */ +#define HVM_PARAM_X87_FIP_WIDTH 36 + +/* + * TSS (and its size) used on Intel when CR0.PE=0. The address occupies + * the low 32 bits, while the size is in the high 32 ones. + */ +#define HVM_PARAM_VM86_TSS_SIZED 37 + +/* Enable MCA capabilities. */ +#define HVM_PARAM_MCA_CAP 38 +#define XEN_HVM_MCA_CAP_LMCE (xen_mk_ullong(1) << 0) +#define XEN_HVM_MCA_CAP_MASK XEN_HVM_MCA_CAP_LMCE + +#define HVM_NR_PARAMS 39 + +#endif /* __XEN_PUBLIC_HVM_PARAMS_H__ */ diff --git a/include/hw/xen/interface/io/blkif.h b/include/hw/xen/interface/io/blkif.h index d07fa1e078..22f1eef0c0 100644 --- a/include/hw/xen/interface/io/blkif.h +++ b/include/hw/xen/interface/io/blkif.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * blkif.h * * Unified block-device I/O interface for Xen guest OSes. * - * 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. - * * Copyright (c) 2003-2004, Keir Fraser * Copyright (c) 2012, Spectra Logic Corporation */ @@ -118,7 +101,7 @@ * * The underlying storage is not affected by the direct IO memory * lifetime bug. See: - * http://lists.xen.org/archives/html/xen-devel/2012-12/msg01154.html + * https://lists.xen.org/archives/html/xen-devel/2012-12/msg01154.html * * Therefore this option gives the backend permission to use * O_DIRECT, notwithstanding that bug. @@ -341,7 +324,7 @@ * access (even when it should be read-only). If the frontend hits the * maximum number of allowed persistently mapped grants, it can fallback * to non persistent mode. This will cause a performance degradation, - * since the backend driver will still try to map those grants + * since the the backend driver will still try to map those grants * persistently. Since the persistent grants protocol is compatible with * the previous protocol, a frontend driver can choose to work in * persistent mode even when the backend doesn't support it. @@ -363,6 +346,14 @@ * that the frontend requires that the logical block size is 512 as it * is hardcoded (which is the case in some frontend implementations). * + * trusted + * Values: 0/1 (boolean) + * Default value: 1 + * + * A value of "0" indicates that the frontend should not trust the + * backend, and should deploy whatever measures available to protect from + * a malicious backend on the other end. + * *------------------------- Virtual Device Properties ------------------------- * * device-type @@ -710,3 +701,13 @@ DEFINE_RING_TYPES(blkif, struct blkif_request, struct blkif_response); #define VDISK_READONLY 0x4 #endif /* __XEN_PUBLIC_IO_BLKIF_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/io/console.h b/include/hw/xen/interface/io/console.h index e2155d1cf5..4509b4b689 100644 --- a/include/hw/xen/interface/io/console.h +++ b/include/hw/xen/interface/io/console.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * console.h * * Console I/O interface for Xen guest OSes. * - * 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. - * * Copyright (c) 2005, Keir Fraser */ @@ -44,3 +27,13 @@ DEFINE_XEN_FLEX_RING(xencons); #endif #endif /* __XEN_PUBLIC_IO_CONSOLE_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/io/fbif.h b/include/hw/xen/interface/io/fbif.h index ea87ebec0a..93c73195d8 100644 --- a/include/hw/xen/interface/io/fbif.h +++ b/include/hw/xen/interface/io/fbif.h @@ -1,24 +1,7 @@ +/* SPDX-License-Identifier: MIT */ /* * fbif.h -- Xen virtual frame buffer device * - * 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. - * * Copyright (C) 2005 Anthony Liguori * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster */ @@ -153,4 +136,24 @@ struct xenfb_page unsigned long pd[256]; }; +/* + * Wart: xenkbd needs to know default resolution. Put it here until a + * better solution is found, but don't leak it to the backend. + */ +#ifdef __KERNEL__ +#define XENFB_WIDTH 800 +#define XENFB_HEIGHT 600 +#define XENFB_DEPTH 32 #endif + +#endif + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/io/kbdif.h b/include/hw/xen/interface/io/kbdif.h index 1d68cd458e..4bde6b3821 100644 --- a/include/hw/xen/interface/io/kbdif.h +++ b/include/hw/xen/interface/io/kbdif.h @@ -1,24 +1,7 @@ +/* SPDX-License-Identifier: MIT */ /* * kbdif.h -- Xen virtual keyboard/mouse * - * 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. - * * Copyright (C) 2005 Anthony Liguori * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster */ @@ -564,3 +547,13 @@ struct xenkbd_page }; #endif /* __XEN_PUBLIC_IO_KBDIF_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/io/netif.h b/include/hw/xen/interface/io/netif.h index 48fa530950..c13b85061d 100644 --- a/include/hw/xen/interface/io/netif.h +++ b/include/hw/xen/interface/io/netif.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * netif.h * * Unified network-device I/O interface for Xen guest OSes. * - * 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. - * * Copyright (c) 2003-2004, Keir Fraser */ @@ -160,6 +143,12 @@ * be applied if it is set. */ +/* + * The setting of "trusted" node to "0" in the frontend path signals that the + * frontend should not trust the backend, and should deploy whatever measures + * available to protect from a malicious backend on the other end. + */ + /* * Control ring * ============ @@ -171,7 +160,7 @@ * The ability of the backend to use a control ring is advertised by * setting: * - * /local/domain/X/backend///feature-ctrl-ring = "1" + * /local/domain/X/backend/vif///feature-ctrl-ring = "1" * * The frontend provides a control ring to the backend by setting: * @@ -190,6 +179,32 @@ * order as requests. */ +/* + * Link state + * ========== + * + * The backend can advertise its current link (carrier) state to the + * frontend using the /local/domain/X/backend/vif///carrier + * node. If this node is not present, then the frontend should assume that + * the link is up (for compatibility with backends that do not implement + * this feature). If this node is present, then a value of "0" should be + * interpreted by the frontend as the link being down (no carrier) and a + * value of "1" should be interpreted as the link being up (carrier + * present). + */ + +/* + * MTU + * === + * + * The toolstack may set a value of MTU for the frontend by setting the + * /local/domain//device/vif//mtu node with the MTU value in + * octets. If this node is absent the frontend should assume an MTU value + * of 1500 octets. A frontend is also at liberty to ignore this value so + * it is only suitable for informing the frontend that a packet payload + * >1500 octets is permitted. + */ + /* * Hash types * ========== @@ -267,6 +282,62 @@ #define XEN_NETIF_CTRL_HASH_ALGORITHM_TOEPLITZ 1 +/* + * This algorithm uses a 'key' as well as the data buffer itself. + * (Buffer[] and Key[] are treated as shift-registers where the MSB of + * Buffer/Key[0] is considered 'left-most' and the LSB of Buffer/Key[N-1] + * is the 'right-most'). + * + * Value = 0 + * For number of bits in Buffer[] + * If (left-most bit of Buffer[] is 1) + * Value ^= left-most 32 bits of Key[] + * Key[] << 1 + * Buffer[] << 1 + * + * The code below is provided for convenience where an operating system + * does not already provide an implementation. + */ +#ifdef XEN_NETIF_DEFINE_TOEPLITZ +static uint32_t xen_netif_toeplitz_hash(const uint8_t *key, + unsigned int keylen, + const uint8_t *buf, + unsigned int buflen) +{ + unsigned int keyi, bufi; + uint64_t prefix = 0; + uint64_t hash = 0; + + /* Pre-load prefix with the first 8 bytes of the key */ + for (keyi = 0; keyi < 8; keyi++) { + prefix <<= 8; + prefix |= (keyi < keylen) ? key[keyi] : 0; + } + + for (bufi = 0; bufi < buflen; bufi++) { + uint8_t byte = buf[bufi]; + unsigned int bit; + + for (bit = 0; bit < 8; bit++) { + if (byte & 0x80) + hash ^= prefix; + prefix <<= 1; + byte <<=1; + } + + /* + * 'prefix' has now been left-shifted by 8, so + * OR in the next byte. + */ + prefix |= (keyi < keylen) ? key[keyi] : 0; + keyi++; + } + + /* The valid part of the hash is in the upper 32 bits. */ + return hash >> 32; +} +#endif /* XEN_NETIF_DEFINE_TOEPLITZ */ + /* * Control requests (struct xen_netif_ctrl_request) * ================================================ @@ -1008,3 +1079,13 @@ DEFINE_RING_TYPES(netif_rx, struct netif_rx_request, struct netif_rx_response); #define NETIF_RSP_NULL 1 #endif + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/io/protocols.h b/include/hw/xen/interface/io/protocols.h index 52b4de0f81..7815e1ff0f 100644 --- a/include/hw/xen/interface/io/protocols.h +++ b/include/hw/xen/interface/io/protocols.h @@ -1,24 +1,7 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * protocols.h * - * 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. - * * Copyright (c) 2008, Keir Fraser */ diff --git a/include/hw/xen/interface/io/ring.h b/include/hw/xen/interface/io/ring.h index 115705f3f4..025939278b 100644 --- a/include/hw/xen/interface/io/ring.h +++ b/include/hw/xen/interface/io/ring.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * ring.h - * + * * Shared producer-consumer ring macros. * - * 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. - * * Tim Deegan and Andrew Warfield November 2004. */ @@ -33,13 +16,6 @@ * - standard integers types (uint8_t, uint16_t, etc) * They are provided by stdint.h of the standard headers. * - * Before using the different macros, you need to provide the following - * macros: - * - xen_mb() a memory barrier - * - xen_rmb() a read memory barrier - * - xen_wmb() a write memory barrier - * Example of those can be found in xenctrl.h. - * * In addition, if you intend to use the FLEX macros, you also need to * provide the following, before invoking the FLEX macros: * - size_t @@ -49,6 +25,14 @@ * and grant_table.h from the Xen public headers. */ +#include "../xen-compat.h" + +#if __XEN_INTERFACE_VERSION__ < 0x00030208 +#define xen_mb() mb() +#define xen_rmb() rmb() +#define xen_wmb() wmb() +#endif + typedef unsigned int RING_IDX; /* Round a 32-bit unsigned constant down to the nearest power of two. */ @@ -61,12 +45,12 @@ typedef unsigned int RING_IDX; /* * Calculate size of a shared ring, given the total available space for the * ring and indexes (_sz), and the name tag of the request/response structure. - * A ring contains as many entries as will fit, rounded down to the nearest + * A ring contains as many entries as will fit, rounded down to the nearest * power of two (so we can mask with (size-1) to loop around). */ #define __CONST_RING_SIZE(_s, _sz) \ (__RD32(((_sz) - offsetof(struct _s##_sring, ring)) / \ - sizeof_field(struct _s##_sring, ring[0]))) + sizeof(((struct _s##_sring *)0)->ring[0]))) /* * The same for passing in an actual pointer instead of a name tag. */ @@ -75,7 +59,7 @@ typedef unsigned int RING_IDX; /* * Macros to make the correct C datatypes for a new kind of ring. - * + * * To make a new ring datatype, you need to have two message structures, * let's say request_t, and response_t already defined. * @@ -85,7 +69,7 @@ typedef unsigned int RING_IDX; * * These expand out to give you a set of types, as you can see below. * The most important of these are: - * + * * mytag_sring_t - The shared ring. * mytag_front_ring_t - The 'front' half of the ring. * mytag_back_ring_t - The 'back' half of the ring. @@ -94,9 +78,8 @@ typedef unsigned int RING_IDX; * of the shared memory area (PAGE_SIZE, for instance). To initialise * the front half: * - * mytag_front_ring_t front_ring; - * SHARED_RING_INIT((mytag_sring_t *)shared_page); - * FRONT_RING_INIT(&front_ring, (mytag_sring_t *)shared_page, PAGE_SIZE); + * mytag_front_ring_t ring; + * XEN_FRONT_RING_INIT(&ring, (mytag_sring_t *)shared_page, PAGE_SIZE); * * Initializing the back follows similarly (note that only the front * initializes the shared ring): @@ -153,15 +136,15 @@ typedef struct __name##_back_ring __name##_back_ring_t /* * Macros for manipulating rings. - * - * FRONT_RING_whatever works on the "front end" of a ring: here + * + * FRONT_RING_whatever works on the "front end" of a ring: here * requests are pushed on to the ring and responses taken off it. - * - * BACK_RING_whatever works on the "back end" of a ring: here + * + * BACK_RING_whatever works on the "back end" of a ring: here * requests are taken off the ring and responses put on. - * - * N.B. these macros do NO INTERLOCKS OR FLOW CONTROL. - * This is OK in 1-for-1 request-response situations where the + * + * N.B. these macros do NO INTERLOCKS OR FLOW CONTROL. + * This is OK in 1-for-1 request-response situations where the * requestor (front end) never has more than RING_SIZE()-1 * outstanding requests. */ @@ -174,20 +157,29 @@ typedef struct __name##_back_ring __name##_back_ring_t (void)memset((_s)->__pad, 0, sizeof((_s)->__pad)); \ } while(0) -#define FRONT_RING_INIT(_r, _s, __size) do { \ - (_r)->req_prod_pvt = 0; \ - (_r)->rsp_cons = 0; \ +#define FRONT_RING_ATTACH(_r, _s, _i, __size) do { \ + (_r)->req_prod_pvt = (_i); \ + (_r)->rsp_cons = (_i); \ (_r)->nr_ents = __RING_SIZE(_s, __size); \ (_r)->sring = (_s); \ } while (0) -#define BACK_RING_INIT(_r, _s, __size) do { \ - (_r)->rsp_prod_pvt = 0; \ - (_r)->req_cons = 0; \ +#define FRONT_RING_INIT(_r, _s, __size) FRONT_RING_ATTACH(_r, _s, 0, __size) + +#define XEN_FRONT_RING_INIT(r, s, size) do { \ + SHARED_RING_INIT(s); \ + FRONT_RING_INIT(r, s, size); \ +} while (0) + +#define BACK_RING_ATTACH(_r, _s, _i, __size) do { \ + (_r)->rsp_prod_pvt = (_i); \ + (_r)->req_cons = (_i); \ (_r)->nr_ents = __RING_SIZE(_s, __size); \ (_r)->sring = (_s); \ } while (0) +#define BACK_RING_INIT(_r, _s, __size) BACK_RING_ATTACH(_r, _s, 0, __size) + /* How big is this ring? */ #define RING_SIZE(_r) \ ((_r)->nr_ents) @@ -203,36 +195,62 @@ typedef struct __name##_back_ring __name##_back_ring_t (RING_FREE_REQUESTS(_r) == 0) /* Test if there are outstanding messages to be processed on a ring. */ -#define RING_HAS_UNCONSUMED_RESPONSES(_r) \ +#define XEN_RING_NR_UNCONSUMED_RESPONSES(_r) \ ((_r)->sring->rsp_prod - (_r)->rsp_cons) -#define RING_HAS_UNCONSUMED_REQUESTS(_r) ({ \ +#ifdef __GNUC__ +#define XEN_RING_NR_UNCONSUMED_REQUESTS(_r) ({ \ unsigned int req = (_r)->sring->req_prod - (_r)->req_cons; \ unsigned int rsp = RING_SIZE(_r) - \ ((_r)->req_cons - (_r)->rsp_prod_pvt); \ req < rsp ? req : rsp; \ }) +#else +/* Same as above, but without the nice GCC ({ ... }) syntax. */ +#define XEN_RING_NR_UNCONSUMED_REQUESTS(_r) \ + ((((_r)->sring->req_prod - (_r)->req_cons) < \ + (RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt))) ? \ + ((_r)->sring->req_prod - (_r)->req_cons) : \ + (RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt))) +#endif + +#ifdef XEN_RING_HAS_UNCONSUMED_IS_BOOL +/* + * These variants should only be used in case no caller is abusing them for + * obtaining the number of unconsumed responses/requests. + */ +#define RING_HAS_UNCONSUMED_RESPONSES(_r) \ + (!!XEN_RING_NR_UNCONSUMED_RESPONSES(_r)) +#define RING_HAS_UNCONSUMED_REQUESTS(_r) \ + (!!XEN_RING_NR_UNCONSUMED_REQUESTS(_r)) +#else +#define RING_HAS_UNCONSUMED_RESPONSES(_r) XEN_RING_NR_UNCONSUMED_RESPONSES(_r) +#define RING_HAS_UNCONSUMED_REQUESTS(_r) XEN_RING_NR_UNCONSUMED_REQUESTS(_r) +#endif /* Direct access to individual ring elements, by index. */ #define RING_GET_REQUEST(_r, _idx) \ (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req)) +#define RING_GET_RESPONSE(_r, _idx) \ + (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].rsp)) + /* - * Get a local copy of a request. + * Get a local copy of a request/response. * - * Use this in preference to RING_GET_REQUEST() so all processing is + * Use this in preference to RING_GET_{REQUEST,RESPONSE}() so all processing is * done on a local copy that cannot be modified by the other end. * * Note that https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145 may cause this - * to be ineffective where _req is a struct which consists of only bitfields. + * to be ineffective where dest is a struct which consists of only bitfields. */ -#define RING_COPY_REQUEST(_r, _idx, _req) do { \ - /* Use volatile to force the copy into _req. */ \ - *(_req) = *(volatile typeof(_req))RING_GET_REQUEST(_r, _idx); \ +#define RING_COPY_(type, r, idx, dest) do { \ + /* Use volatile to force the copy into dest. */ \ + *(dest) = *(volatile __typeof__(dest))RING_GET_##type(r, idx); \ } while (0) -#define RING_GET_RESPONSE(_r, _idx) \ - (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].rsp)) +#define RING_COPY_REQUEST(r, idx, req) RING_COPY_(REQUEST, r, idx, req) +#define RING_COPY_RESPONSE(r, idx, rsp) RING_COPY_(RESPONSE, r, idx, rsp) /* Loop termination condition: Would the specified index overflow the ring? */ #define RING_REQUEST_CONS_OVERFLOW(_r, _cons) \ @@ -242,6 +260,10 @@ typedef struct __name##_back_ring __name##_back_ring_t #define RING_REQUEST_PROD_OVERFLOW(_r, _prod) \ (((_prod) - (_r)->rsp_prod_pvt) > RING_SIZE(_r)) +/* Ill-behaved backend determination: Can there be this many responses? */ +#define RING_RESPONSE_PROD_OVERFLOW(_r, _prod) \ + (((_prod) - (_r)->rsp_cons) > RING_SIZE(_r)) + #define RING_PUSH_REQUESTS(_r) do { \ xen_wmb(); /* back sees requests /before/ updated producer index */ \ (_r)->sring->req_prod = (_r)->req_prod_pvt; \ @@ -254,26 +276,26 @@ typedef struct __name##_back_ring __name##_back_ring_t /* * Notification hold-off (req_event and rsp_event): - * + * * When queueing requests or responses on a shared ring, it may not always be * necessary to notify the remote end. For example, if requests are in flight * in a backend, the front may be able to queue further requests without * notifying the back (if the back checks for new requests when it queues * responses). - * + * * When enqueuing requests or responses: - * + * * Use RING_PUSH_{REQUESTS,RESPONSES}_AND_CHECK_NOTIFY(). The second argument * is a boolean return value. True indicates that the receiver requires an * asynchronous notification. - * + * * After dequeuing requests or responses (before sleeping the connection): - * + * * Use RING_FINAL_CHECK_FOR_REQUESTS() or RING_FINAL_CHECK_FOR_RESPONSES(). * The second argument is a boolean return value. True indicates that there * are pending messages on the ring (i.e., the connection should not be put * to sleep). - * + * * These macros will set the req_event/rsp_event field to trigger a * notification on the very next message that is enqueued. If you want to * create batches of work (i.e., only receive a notification after several diff --git a/include/hw/xen/interface/io/usbif.h b/include/hw/xen/interface/io/usbif.h index c6a58639d6..875af0dc7c 100644 --- a/include/hw/xen/interface/io/usbif.h +++ b/include/hw/xen/interface/io/usbif.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * usbif.h * @@ -5,24 +6,6 @@ * * Copyright (C) 2009, FUJITSU LABORATORIES LTD. * Author: Noboru Iwamatsu - * - * 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 __XEN_PUBLIC_IO_USBIF_H__ @@ -32,6 +15,34 @@ #include "../grant_table.h" /* + * Detailed Interface Description + * ============================== + * The pvUSB interface is using a split driver design: a frontend driver in + * the guest and a backend driver in a driver domain (normally dom0) having + * access to the physical USB device(s) being passed to the guest. + * + * The frontend and backend drivers use XenStore to initiate the connection + * between them, the I/O activity is handled via two shared ring pages and an + * event channel. As the interface between frontend and backend is at the USB + * host connector level, multiple (up to 31) physical USB devices can be + * handled by a single connection. + * + * The Xen pvUSB device name is "qusb", so the frontend's XenStore entries are + * to be found under "device/qusb", while the backend's XenStore entries are + * under "backend//qusb". + * + * When a new pvUSB connection is established, the frontend needs to setup the + * two shared ring pages for communication and the event channel. The ring + * pages need to be made available to the backend via the grant table + * interface. + * + * One of the shared ring pages is used by the backend to inform the frontend + * about USB device plug events (device to be added or removed). This is the + * "conn-ring". + * + * The other ring page is used for USB I/O communication (requests and + * responses). This is the "urb-ring". + * * Feature and Parameter Negotiation * ================================= * The two halves of a Xen pvUSB driver utilize nodes within the XenStore to @@ -99,130 +110,273 @@ * The machine ABI rules governing the format of all ring request and * response structures. * + * Protocol Description + * ==================== + * + *-------------------------- USB device plug events -------------------------- + * + * USB device plug events are send via the "conn-ring" shared page. As only + * events are being sent, the respective requests from the frontend to the + * backend are just dummy ones. + * The events sent to the frontend have the following layout: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | id | portnum | speed | 4 + * +----------------+----------------+----------------+----------------+ + * id - uint16_t, event id (taken from the actual frontend dummy request) + * portnum - uint8_t, port number (1 ... 31) + * speed - uint8_t, device USBIF_SPEED_*, USBIF_SPEED_NONE == unplug + * + * The dummy request: + * 0 1 octet + * +----------------+----------------+ + * | id | 2 + * +----------------+----------------+ + * id - uint16_t, guest supplied value (no need for being unique) + * + *-------------------------- USB I/O request --------------------------------- + * + * A single USB I/O request on the "urb-ring" has the following layout: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | id | nr_buffer_segs | 4 + * +----------------+----------------+----------------+----------------+ + * | pipe | 8 + * +----------------+----------------+----------------+----------------+ + * | transfer_flags | buffer_length | 12 + * +----------------+----------------+----------------+----------------+ + * | request type specific | 16 + * | data | 20 + * +----------------+----------------+----------------+----------------+ + * | seg[0] | 24 + * | data | 28 + * +----------------+----------------+----------------+----------------+ + * |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/| + * +----------------+----------------+----------------+----------------+ + * | seg[USBIF_MAX_SEGMENTS_PER_REQUEST - 1] | 144 + * | data | 148 + * +----------------+----------------+----------------+----------------+ + * Bit field bit number 0 is always least significant bit, undefined bits must + * be zero. + * id - uint16_t, guest supplied value + * nr_buffer_segs - uint16_t, number of segment entries in seg[] array + * pipe - uint32_t, bit field with multiple information: + * bits 0-4: port request to send to + * bit 5: unlink request with specified id (cancel I/O) if set (see below) + * bit 7: direction (1 = read from device) + * bits 8-14: device number on port + * bits 15-18: endpoint of device + * bits 30-31: request type: 00 = isochronous, 01 = interrupt, + * 10 = control, 11 = bulk + * transfer_flags - uint16_t, bit field with processing flags: + * bit 0: less data than specified allowed + * buffer_length - uint16_t, total length of data + * request type specific data - 8 bytes, see below + * seg[] - array with 8 byte elements, see below + * + * Request type specific data for isochronous request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | interval | start_frame | 4 + * +----------------+----------------+----------------+----------------+ + * | number_of_packets | nr_frame_desc_segs | 8 + * +----------------+----------------+----------------+----------------+ + * interval - uint16_t, time interval in msecs between frames + * start_frame - uint16_t, start frame number + * number_of_packets - uint16_t, number of packets to transfer + * nr_frame_desc_segs - uint16_t number of seg[] frame descriptors elements + * + * Request type specific data for interrupt request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | interval | 0 | 4 + * +----------------+----------------+----------------+----------------+ + * | 0 | 8 + * +----------------+----------------+----------------+----------------+ + * interval - uint16_t, time in msecs until interruption + * + * Request type specific data for control request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | data of setup packet | 4 + * | | 8 + * +----------------+----------------+----------------+----------------+ + * + * Request type specific data for bulk request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | 0 | 4 + * | 0 | 8 + * +----------------+----------------+----------------+----------------+ + * + * Request type specific data for unlink request: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | unlink_id | 0 | 4 + * +----------------+----------------+----------------+----------------+ + * | 0 | 8 + * +----------------+----------------+----------------+----------------+ + * unlink_id - uint16_t, request id of request to terminate + * + * seg[] array element layout: + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | gref | 4 + * +----------------+----------------+----------------+----------------+ + * | offset | length | 8 + * +----------------+----------------+----------------+----------------+ + * gref - uint32_t, grant reference of buffer page + * offset - uint16_t, offset of buffer start in page + * length - uint16_t, length of buffer in page + * + *-------------------------- USB I/O response -------------------------------- + * + * 0 1 2 3 octet + * +----------------+----------------+----------------+----------------+ + * | id | start_frame | 4 + * +----------------+----------------+----------------+----------------+ + * | status | 8 + * +----------------+----------------+----------------+----------------+ + * | actual_length | 12 + * +----------------+----------------+----------------+----------------+ + * | error_count | 16 + * +----------------+----------------+----------------+----------------+ + * id - uint16_t, id of the request this response belongs to + * start_frame - uint16_t, start_frame this response (iso requests only) + * status - int32_t, USBIF_STATUS_* (non-iso requests) + * actual_length - uint32_t, actual size of data transferred + * error_count - uint32_t, number of errors (iso requests) */ enum usb_spec_version { - USB_VER_UNKNOWN = 0, - USB_VER_USB11, - USB_VER_USB20, - USB_VER_USB30, /* not supported yet */ + USB_VER_UNKNOWN = 0, + USB_VER_USB11, + USB_VER_USB20, + USB_VER_USB30, /* not supported yet */ }; /* * USB pipe in usbif_request * - * - port number: bits 0-4 - * (USB_MAXCHILDREN is 31) + * - port number: bits 0-4 + * (USB_MAXCHILDREN is 31) * - * - operation flag: bit 5 - * (0 = submit urb, - * 1 = unlink urb) + * - operation flag: bit 5 + * (0 = submit urb, + * 1 = unlink urb) * - * - direction: bit 7 - * (0 = Host-to-Device [Out] - * 1 = Device-to-Host [In]) + * - direction: bit 7 + * (0 = Host-to-Device [Out] + * 1 = Device-to-Host [In]) * - * - device address: bits 8-14 + * - device address: bits 8-14 * - * - endpoint: bits 15-18 + * - endpoint: bits 15-18 * - * - pipe type: bits 30-31 - * (00 = isochronous, 01 = interrupt, - * 10 = control, 11 = bulk) + * - pipe type: bits 30-31 + * (00 = isochronous, 01 = interrupt, + * 10 = control, 11 = bulk) */ -#define USBIF_PIPE_PORT_MASK 0x0000001f -#define USBIF_PIPE_UNLINK 0x00000020 -#define USBIF_PIPE_DIR 0x00000080 -#define USBIF_PIPE_DEV_MASK 0x0000007f -#define USBIF_PIPE_DEV_SHIFT 8 -#define USBIF_PIPE_EP_MASK 0x0000000f -#define USBIF_PIPE_EP_SHIFT 15 -#define USBIF_PIPE_TYPE_MASK 0x00000003 -#define USBIF_PIPE_TYPE_SHIFT 30 -#define USBIF_PIPE_TYPE_ISOC 0 -#define USBIF_PIPE_TYPE_INT 1 -#define USBIF_PIPE_TYPE_CTRL 2 -#define USBIF_PIPE_TYPE_BULK 3 +#define USBIF_PIPE_PORT_MASK 0x0000001f +#define USBIF_PIPE_UNLINK 0x00000020 +#define USBIF_PIPE_DIR 0x00000080 +#define USBIF_PIPE_DEV_MASK 0x0000007f +#define USBIF_PIPE_DEV_SHIFT 8 +#define USBIF_PIPE_EP_MASK 0x0000000f +#define USBIF_PIPE_EP_SHIFT 15 +#define USBIF_PIPE_TYPE_MASK 0x00000003 +#define USBIF_PIPE_TYPE_SHIFT 30 +#define USBIF_PIPE_TYPE_ISOC 0 +#define USBIF_PIPE_TYPE_INT 1 +#define USBIF_PIPE_TYPE_CTRL 2 +#define USBIF_PIPE_TYPE_BULK 3 -#define usbif_pipeportnum(pipe) ((pipe) & USBIF_PIPE_PORT_MASK) -#define usbif_setportnum_pipe(pipe, portnum) ((pipe) | (portnum)) +#define usbif_pipeportnum(pipe) ((pipe) & USBIF_PIPE_PORT_MASK) +#define usbif_setportnum_pipe(pipe, portnum) ((pipe) | (portnum)) -#define usbif_pipeunlink(pipe) ((pipe) & USBIF_PIPE_UNLINK) -#define usbif_pipesubmit(pipe) (!usbif_pipeunlink(pipe)) -#define usbif_setunlink_pipe(pipe) ((pipe) | USBIF_PIPE_UNLINK) +#define usbif_pipeunlink(pipe) ((pipe) & USBIF_PIPE_UNLINK) +#define usbif_pipesubmit(pipe) (!usbif_pipeunlink(pipe)) +#define usbif_setunlink_pipe(pipe) ((pipe) | USBIF_PIPE_UNLINK) -#define usbif_pipein(pipe) ((pipe) & USBIF_PIPE_DIR) -#define usbif_pipeout(pipe) (!usbif_pipein(pipe)) +#define usbif_pipein(pipe) ((pipe) & USBIF_PIPE_DIR) +#define usbif_pipeout(pipe) (!usbif_pipein(pipe)) -#define usbif_pipedevice(pipe) \ - (((pipe) >> USBIF_PIPE_DEV_SHIFT) & USBIF_PIPE_DEV_MASK) +#define usbif_pipedevice(pipe) \ + (((pipe) >> USBIF_PIPE_DEV_SHIFT) & USBIF_PIPE_DEV_MASK) -#define usbif_pipeendpoint(pipe) \ - (((pipe) >> USBIF_PIPE_EP_SHIFT) & USBIF_PIPE_EP_MASK) +#define usbif_pipeendpoint(pipe) \ + (((pipe) >> USBIF_PIPE_EP_SHIFT) & USBIF_PIPE_EP_MASK) -#define usbif_pipetype(pipe) \ - (((pipe) >> USBIF_PIPE_TYPE_SHIFT) & USBIF_PIPE_TYPE_MASK) -#define usbif_pipeisoc(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_ISOC) -#define usbif_pipeint(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_INT) -#define usbif_pipectrl(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_CTRL) -#define usbif_pipebulk(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_BULK) +#define usbif_pipetype(pipe) \ + (((pipe) >> USBIF_PIPE_TYPE_SHIFT) & USBIF_PIPE_TYPE_MASK) +#define usbif_pipeisoc(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_ISOC) +#define usbif_pipeint(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_INT) +#define usbif_pipectrl(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_CTRL) +#define usbif_pipebulk(pipe) (usbif_pipetype(pipe) == USBIF_PIPE_TYPE_BULK) #define USBIF_MAX_SEGMENTS_PER_REQUEST (16) -#define USBIF_MAX_PORTNR 31 -#define USBIF_RING_SIZE 4096 +#define USBIF_MAX_PORTNR 31 +#define USBIF_RING_SIZE 4096 /* * RING for transferring urbs. */ struct usbif_request_segment { - grant_ref_t gref; - uint16_t offset; - uint16_t length; + grant_ref_t gref; + uint16_t offset; + uint16_t length; }; struct usbif_urb_request { - uint16_t id; /* request id */ - uint16_t nr_buffer_segs; /* number of urb->transfer_buffer segments */ + uint16_t id; /* request id */ + uint16_t nr_buffer_segs; /* number of urb->transfer_buffer segments */ - /* basic urb parameter */ - uint32_t pipe; - uint16_t transfer_flags; -#define USBIF_SHORT_NOT_OK 0x0001 - uint16_t buffer_length; - union { - uint8_t ctrl[8]; /* setup_packet (Ctrl) */ + /* basic urb parameter */ + uint32_t pipe; + uint16_t transfer_flags; +#define USBIF_SHORT_NOT_OK 0x0001 + uint16_t buffer_length; + union { + uint8_t ctrl[8]; /* setup_packet (Ctrl) */ - struct { - uint16_t interval; /* maximum (1024*8) in usb core */ - uint16_t start_frame; /* start frame */ - uint16_t number_of_packets; /* number of ISO packet */ - uint16_t nr_frame_desc_segs; /* number of iso_frame_desc segments */ - } isoc; + struct { + uint16_t interval; /* maximum (1024*8) in usb core */ + uint16_t start_frame; /* start frame */ + uint16_t number_of_packets; /* number of ISO packet */ + uint16_t nr_frame_desc_segs; /* number of iso_frame_desc segments */ + } isoc; - struct { - uint16_t interval; /* maximum (1024*8) in usb core */ - uint16_t pad[3]; - } intr; + struct { + uint16_t interval; /* maximum (1024*8) in usb core */ + uint16_t pad[3]; + } intr; - struct { - uint16_t unlink_id; /* unlink request id */ - uint16_t pad[3]; - } unlink; + struct { + uint16_t unlink_id; /* unlink request id */ + uint16_t pad[3]; + } unlink; - } u; + } u; - /* urb data segments */ - struct usbif_request_segment seg[USBIF_MAX_SEGMENTS_PER_REQUEST]; + /* urb data segments */ + struct usbif_request_segment seg[USBIF_MAX_SEGMENTS_PER_REQUEST]; }; typedef struct usbif_urb_request usbif_urb_request_t; struct usbif_urb_response { - uint16_t id; /* request id */ - uint16_t start_frame; /* start frame (ISO) */ - int32_t status; /* status (non-ISO) */ - int32_t actual_length; /* actual transfer length */ - int32_t error_count; /* number of ISO errors */ + uint16_t id; /* request id */ + uint16_t start_frame; /* start frame (ISO) */ + int32_t status; /* status (non-ISO) */ +#define USBIF_STATUS_OK 0 +#define USBIF_STATUS_NODEV (-19) +#define USBIF_STATUS_INVAL (-22) +#define USBIF_STATUS_STALL (-32) +#define USBIF_STATUS_IOERROR (-71) +#define USBIF_STATUS_BABBLE (-75) +#define USBIF_STATUS_SHUTDOWN (-108) + int32_t actual_length; /* actual transfer length */ + int32_t error_count; /* number of ISO errors */ }; typedef struct usbif_urb_response usbif_urb_response_t; @@ -233,18 +387,18 @@ DEFINE_RING_TYPES(usbif_urb, struct usbif_urb_request, struct usbif_urb_response * RING for notifying connect/disconnect events to frontend */ struct usbif_conn_request { - uint16_t id; + uint16_t id; }; typedef struct usbif_conn_request usbif_conn_request_t; struct usbif_conn_response { - uint16_t id; /* request id */ - uint8_t portnum; /* port number */ - uint8_t speed; /* usb_device_speed */ -#define USBIF_SPEED_NONE 0 -#define USBIF_SPEED_LOW 1 -#define USBIF_SPEED_FULL 2 -#define USBIF_SPEED_HIGH 3 + uint16_t id; /* request id */ + uint8_t portnum; /* port number */ + uint8_t speed; /* usb_device_speed */ +#define USBIF_SPEED_NONE 0 +#define USBIF_SPEED_LOW 1 +#define USBIF_SPEED_FULL 2 +#define USBIF_SPEED_HIGH 3 }; typedef struct usbif_conn_response usbif_conn_response_t; diff --git a/include/hw/xen/interface/io/xenbus.h b/include/hw/xen/interface/io/xenbus.h index 2fbf2a7fdc..9cd0cd7c67 100644 --- a/include/hw/xen/interface/io/xenbus.h +++ b/include/hw/xen/interface/io/xenbus.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /***************************************************************************** * xenbus.h * * Xenbus protocol details. * - * 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. - * * Copyright (C) 2005 XenSource Ltd. */ @@ -68,3 +51,13 @@ enum xenbus_state { typedef enum xenbus_state XenbusState; #endif /* _XEN_PUBLIC_IO_XENBUS_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/io/xs_wire.h b/include/hw/xen/interface/io/xs_wire.h new file mode 100644 index 0000000000..04e6849feb --- /dev/null +++ b/include/hw/xen/interface/io/xs_wire.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Details of the "wire" protocol between Xen Store Daemon and client + * library or guest kernel. + * + * Copyright (C) 2005 Rusty Russell IBM Corporation + */ + +#ifndef _XS_WIRE_H +#define _XS_WIRE_H + +enum xsd_sockmsg_type +{ + XS_CONTROL, +#define XS_DEBUG XS_CONTROL + XS_DIRECTORY, + XS_READ, + XS_GET_PERMS, + XS_WATCH, + XS_UNWATCH, + XS_TRANSACTION_START, + XS_TRANSACTION_END, + XS_INTRODUCE, + XS_RELEASE, + XS_GET_DOMAIN_PATH, + XS_WRITE, + XS_MKDIR, + XS_RM, + XS_SET_PERMS, + XS_WATCH_EVENT, + XS_ERROR, + XS_IS_DOMAIN_INTRODUCED, + XS_RESUME, + XS_SET_TARGET, + /* XS_RESTRICT has been removed */ + XS_RESET_WATCHES = XS_SET_TARGET + 2, + XS_DIRECTORY_PART, + + XS_TYPE_COUNT, /* Number of valid types. */ + + XS_INVALID = 0xffff /* Guaranteed to remain an invalid type */ +}; + +#define XS_WRITE_NONE "NONE" +#define XS_WRITE_CREATE "CREATE" +#define XS_WRITE_CREATE_EXCL "CREATE|EXCL" + +/* We hand errors as strings, for portability. */ +struct xsd_errors +{ + int errnum; + const char *errstring; +}; +#ifdef EINVAL +#define XSD_ERROR(x) { x, #x } +/* LINTED: static unused */ +static const struct xsd_errors xsd_errors[] +#if defined(__GNUC__) +__attribute__((unused)) +#endif + = { + /* /!\ New errors should be added at the end of the array. */ + XSD_ERROR(EINVAL), + XSD_ERROR(EACCES), + XSD_ERROR(EEXIST), + XSD_ERROR(EISDIR), + XSD_ERROR(ENOENT), + XSD_ERROR(ENOMEM), + XSD_ERROR(ENOSPC), + XSD_ERROR(EIO), + XSD_ERROR(ENOTEMPTY), + XSD_ERROR(ENOSYS), + XSD_ERROR(EROFS), + XSD_ERROR(EBUSY), + XSD_ERROR(EAGAIN), + XSD_ERROR(EISCONN), + XSD_ERROR(E2BIG), + XSD_ERROR(EPERM), +}; +#endif + +struct xsd_sockmsg +{ + uint32_t type; /* XS_??? */ + uint32_t req_id;/* Request identifier, echoed in daemon's response. */ + uint32_t tx_id; /* Transaction id (0 if not related to a transaction). */ + uint32_t len; /* Length of data following this. */ + + /* Generally followed by nul-terminated string(s). */ +}; + +enum xs_watch_type +{ + XS_WATCH_PATH = 0, + XS_WATCH_TOKEN +}; + +/* + * `incontents 150 xenstore_struct XenStore wire protocol. + * + * Inter-domain shared memory communications. */ +#define XENSTORE_RING_SIZE 1024 +typedef uint32_t XENSTORE_RING_IDX; +#define MASK_XENSTORE_IDX(idx) ((idx) & (XENSTORE_RING_SIZE-1)) +struct xenstore_domain_interface { + char req[XENSTORE_RING_SIZE]; /* Requests to xenstore daemon. */ + char rsp[XENSTORE_RING_SIZE]; /* Replies and async watch events. */ + XENSTORE_RING_IDX req_cons, req_prod; + XENSTORE_RING_IDX rsp_cons, rsp_prod; + uint32_t server_features; /* Bitmap of features supported by the server */ + uint32_t connection; + uint32_t error; +}; + +/* Violating this is very bad. See docs/misc/xenstore.txt. */ +#define XENSTORE_PAYLOAD_MAX 4096 + +/* Violating these just gets you an error back */ +#define XENSTORE_ABS_PATH_MAX 3072 +#define XENSTORE_REL_PATH_MAX 2048 + +/* The ability to reconnect a ring */ +#define XENSTORE_SERVER_FEATURE_RECONNECTION 1 +/* The presence of the "error" field in the ring page */ +#define XENSTORE_SERVER_FEATURE_ERROR 2 + +/* Valid values for the connection field */ +#define XENSTORE_CONNECTED 0 /* the steady-state */ +#define XENSTORE_RECONNECT 1 /* reconnect in progress */ + +/* Valid values for the error field */ +#define XENSTORE_ERROR_NONE 0 /* No error */ +#define XENSTORE_ERROR_COMM 1 /* Communication problem */ +#define XENSTORE_ERROR_RINGIDX 2 /* Invalid ring index */ +#define XENSTORE_ERROR_PROTO 3 /* Protocol violation (payload too long) */ + +#endif /* _XS_WIRE_H */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/memory.h b/include/hw/xen/interface/memory.h new file mode 100644 index 0000000000..29cf5c8239 --- /dev/null +++ b/include/hw/xen/interface/memory.h @@ -0,0 +1,746 @@ +/* SPDX-License-Identifier: MIT */ +/****************************************************************************** + * memory.h + * + * Memory reservation and information. + * + * Copyright (c) 2005, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_MEMORY_H__ +#define __XEN_PUBLIC_MEMORY_H__ + +#include "xen.h" +#include "physdev.h" + +/* + * Increase or decrease the specified domain's memory reservation. Returns the + * number of extents successfully allocated or freed. + * arg == addr of struct xen_memory_reservation. + */ +#define XENMEM_increase_reservation 0 +#define XENMEM_decrease_reservation 1 +#define XENMEM_populate_physmap 6 + +#if __XEN_INTERFACE_VERSION__ >= 0x00030209 +/* + * Maximum # bits addressable by the user of the allocated region (e.g., I/O + * devices often have a 32-bit limitation even in 64-bit systems). If zero + * then the user has no addressing restriction. This field is not used by + * XENMEM_decrease_reservation. + */ +#define XENMEMF_address_bits(x) (x) +#define XENMEMF_get_address_bits(x) ((x) & 0xffu) +/* NUMA node to allocate from. */ +#define XENMEMF_node(x) (((x) + 1) << 8) +#define XENMEMF_get_node(x) ((((x) >> 8) - 1) & 0xffu) +/* Flag to populate physmap with populate-on-demand entries */ +#define XENMEMF_populate_on_demand (1<<16) +/* Flag to request allocation only from the node specified */ +#define XENMEMF_exact_node_request (1<<17) +#define XENMEMF_exact_node(n) (XENMEMF_node(n) | XENMEMF_exact_node_request) +/* Flag to indicate the node specified is virtual node */ +#define XENMEMF_vnode (1<<18) +#endif + +struct xen_memory_reservation { + + /* + * XENMEM_increase_reservation: + * OUT: MFN (*not* GMFN) bases of extents that were allocated + * XENMEM_decrease_reservation: + * IN: GMFN bases of extents to free + * XENMEM_populate_physmap: + * IN: GPFN bases of extents to populate with memory + * OUT: GMFN bases of extents that were allocated + * (NB. This command also updates the mach_to_phys translation table) + * XENMEM_claim_pages: + * IN: must be zero + */ + XEN_GUEST_HANDLE(xen_pfn_t) extent_start; + + /* Number of extents, and size/alignment of each (2^extent_order pages). */ + xen_ulong_t nr_extents; + unsigned int extent_order; + +#if __XEN_INTERFACE_VERSION__ >= 0x00030209 + /* XENMEMF flags. */ + unsigned int mem_flags; +#else + unsigned int address_bits; +#endif + + /* + * Domain whose reservation is being changed. + * Unprivileged domains can specify only DOMID_SELF. + */ + domid_t domid; +}; +typedef struct xen_memory_reservation xen_memory_reservation_t; +DEFINE_XEN_GUEST_HANDLE(xen_memory_reservation_t); + +/* + * An atomic exchange of memory pages. If return code is zero then + * @out.extent_list provides GMFNs of the newly-allocated memory. + * Returns zero on complete success, otherwise a negative error code. + * On complete success then always @nr_exchanged == @in.nr_extents. + * On partial success @nr_exchanged indicates how much work was done. + * + * Note that only PV guests can use this operation. + */ +#define XENMEM_exchange 11 +struct xen_memory_exchange { + /* + * [IN] Details of memory extents to be exchanged (GMFN bases). + * Note that @in.address_bits is ignored and unused. + */ + struct xen_memory_reservation in; + + /* + * [IN/OUT] Details of new memory extents. + * We require that: + * 1. @in.domid == @out.domid + * 2. @in.nr_extents << @in.extent_order == + * @out.nr_extents << @out.extent_order + * 3. @in.extent_start and @out.extent_start lists must not overlap + * 4. @out.extent_start lists GPFN bases to be populated + * 5. @out.extent_start is overwritten with allocated GMFN bases + */ + struct xen_memory_reservation out; + + /* + * [OUT] Number of input extents that were successfully exchanged: + * 1. The first @nr_exchanged input extents were successfully + * deallocated. + * 2. The corresponding first entries in the output extent list correctly + * indicate the GMFNs that were successfully exchanged. + * 3. All other input and output extents are untouched. + * 4. If not all input exents are exchanged then the return code of this + * command will be non-zero. + * 5. THIS FIELD MUST BE INITIALISED TO ZERO BY THE CALLER! + */ + xen_ulong_t nr_exchanged; +}; +typedef struct xen_memory_exchange xen_memory_exchange_t; +DEFINE_XEN_GUEST_HANDLE(xen_memory_exchange_t); + +/* + * Returns the maximum machine frame number of mapped RAM in this system. + * This command always succeeds (it never returns an error code). + * arg == NULL. + */ +#define XENMEM_maximum_ram_page 2 + +struct xen_memory_domain { + /* [IN] Domain information is being queried for. */ + domid_t domid; +}; + +/* + * Returns the current or maximum memory reservation, in pages, of the + * specified domain (may be DOMID_SELF). Returns -ve errcode on failure. + * arg == addr of struct xen_memory_domain. + */ +#define XENMEM_current_reservation 3 +#define XENMEM_maximum_reservation 4 + +/* + * Returns the maximum GFN in use by the specified domain (may be DOMID_SELF). + * Returns -ve errcode on failure. + * arg == addr of struct xen_memory_domain. + */ +#define XENMEM_maximum_gpfn 14 + +/* + * Returns a list of MFN bases of 2MB extents comprising the machine_to_phys + * mapping table. Architectures which do not have a m2p table do not implement + * this command. + * arg == addr of xen_machphys_mfn_list_t. + */ +#define XENMEM_machphys_mfn_list 5 +struct xen_machphys_mfn_list { + /* + * Size of the 'extent_start' array. Fewer entries will be filled if the + * machphys table is smaller than max_extents * 2MB. + */ + unsigned int max_extents; + + /* + * Pointer to buffer to fill with list of extent starts. If there are + * any large discontiguities in the machine address space, 2MB gaps in + * the machphys table will be represented by an MFN base of zero. + */ + XEN_GUEST_HANDLE(xen_pfn_t) extent_start; + + /* + * Number of extents written to the above array. This will be smaller + * than 'max_extents' if the machphys table is smaller than max_e * 2MB. + */ + unsigned int nr_extents; +}; +typedef struct xen_machphys_mfn_list xen_machphys_mfn_list_t; +DEFINE_XEN_GUEST_HANDLE(xen_machphys_mfn_list_t); + +/* + * For a compat caller, this is identical to XENMEM_machphys_mfn_list. + * + * For a non compat caller, this functions similarly to + * XENMEM_machphys_mfn_list, but returns the mfns making up the compatibility + * m2p table. + */ +#define XENMEM_machphys_compat_mfn_list 25 + +/* + * Returns the location in virtual address space of the machine_to_phys + * mapping table. Architectures which do not have a m2p table, or which do not + * map it by default into guest address space, do not implement this command. + * arg == addr of xen_machphys_mapping_t. + */ +#define XENMEM_machphys_mapping 12 +struct xen_machphys_mapping { + xen_ulong_t v_start, v_end; /* Start and end virtual addresses. */ + xen_ulong_t max_mfn; /* Maximum MFN that can be looked up. */ +}; +typedef struct xen_machphys_mapping xen_machphys_mapping_t; +DEFINE_XEN_GUEST_HANDLE(xen_machphys_mapping_t); + +/* Source mapping space. */ +/* ` enum phys_map_space { */ +#define XENMAPSPACE_shared_info 0 /* shared info page */ +#define XENMAPSPACE_grant_table 1 /* grant table page */ +#define XENMAPSPACE_gmfn 2 /* GMFN */ +#define XENMAPSPACE_gmfn_range 3 /* GMFN range, XENMEM_add_to_physmap only. */ +#define XENMAPSPACE_gmfn_foreign 4 /* GMFN from another dom, + * XENMEM_add_to_physmap_batch only. */ +#define XENMAPSPACE_dev_mmio 5 /* device mmio region + ARM only; the region is mapped in + Stage-2 using the Normal Memory + Inner/Outer Write-Back Cacheable + memory attribute. */ +/* ` } */ + +/* + * Sets the GPFN at which a particular page appears in the specified guest's + * physical address space (translated guests only). + * arg == addr of xen_add_to_physmap_t. + */ +#define XENMEM_add_to_physmap 7 +struct xen_add_to_physmap { + /* Which domain to change the mapping for. */ + domid_t domid; + + /* Number of pages to go through for gmfn_range */ + uint16_t size; + + unsigned int space; /* => enum phys_map_space */ + +#define XENMAPIDX_grant_table_status 0x80000000 + + /* Index into space being mapped. */ + xen_ulong_t idx; + + /* GPFN in domid where the source mapping page should appear. */ + xen_pfn_t gpfn; +}; +typedef struct xen_add_to_physmap xen_add_to_physmap_t; +DEFINE_XEN_GUEST_HANDLE(xen_add_to_physmap_t); + +/* A batched version of add_to_physmap. */ +#define XENMEM_add_to_physmap_batch 23 +struct xen_add_to_physmap_batch { + /* IN */ + /* Which domain to change the mapping for. */ + domid_t domid; + uint16_t space; /* => enum phys_map_space */ + + /* Number of pages to go through */ + uint16_t size; + +#if __XEN_INTERFACE_VERSION__ < 0x00040700 + domid_t foreign_domid; /* IFF gmfn_foreign. Should be 0 for other spaces. */ +#else + union xen_add_to_physmap_batch_extra { + domid_t foreign_domid; /* gmfn_foreign */ + uint16_t res0; /* All the other spaces. Should be 0 */ + } u; +#endif + + /* Indexes into space being mapped. */ + XEN_GUEST_HANDLE(xen_ulong_t) idxs; + + /* GPFN in domid where the source mapping page should appear. */ + XEN_GUEST_HANDLE(xen_pfn_t) gpfns; + + /* OUT */ + + /* Per index error code. */ + XEN_GUEST_HANDLE(int) errs; +}; +typedef struct xen_add_to_physmap_batch xen_add_to_physmap_batch_t; +DEFINE_XEN_GUEST_HANDLE(xen_add_to_physmap_batch_t); + +#if __XEN_INTERFACE_VERSION__ < 0x00040400 +#define XENMEM_add_to_physmap_range XENMEM_add_to_physmap_batch +#define xen_add_to_physmap_range xen_add_to_physmap_batch +typedef struct xen_add_to_physmap_batch xen_add_to_physmap_range_t; +DEFINE_XEN_GUEST_HANDLE(xen_add_to_physmap_range_t); +#endif + +/* + * Unmaps the page appearing at a particular GPFN from the specified guest's + * physical address space (translated guests only). + * arg == addr of xen_remove_from_physmap_t. + */ +#define XENMEM_remove_from_physmap 15 +struct xen_remove_from_physmap { + /* Which domain to change the mapping for. */ + domid_t domid; + + /* GPFN of the current mapping of the page. */ + xen_pfn_t gpfn; +}; +typedef struct xen_remove_from_physmap xen_remove_from_physmap_t; +DEFINE_XEN_GUEST_HANDLE(xen_remove_from_physmap_t); + +/*** REMOVED ***/ +/*#define XENMEM_translate_gpfn_list 8*/ + +/* + * Returns the pseudo-physical memory map as it was when the domain + * was started (specified by XENMEM_set_memory_map). + * arg == addr of xen_memory_map_t. + */ +#define XENMEM_memory_map 9 +struct xen_memory_map { + /* + * On call the number of entries which can be stored in buffer. On + * return the number of entries which have been stored in + * buffer. + */ + unsigned int nr_entries; + + /* + * Entries in the buffer are in the same format as returned by the + * BIOS INT 0x15 EAX=0xE820 call. + */ + XEN_GUEST_HANDLE(void) buffer; +}; +typedef struct xen_memory_map xen_memory_map_t; +DEFINE_XEN_GUEST_HANDLE(xen_memory_map_t); + +/* + * Returns the real physical memory map. Passes the same structure as + * XENMEM_memory_map. + * Specifying buffer as NULL will return the number of entries required + * to store the complete memory map. + * arg == addr of xen_memory_map_t. + */ +#define XENMEM_machine_memory_map 10 + +/* + * Set the pseudo-physical memory map of a domain, as returned by + * XENMEM_memory_map. + * arg == addr of xen_foreign_memory_map_t. + */ +#define XENMEM_set_memory_map 13 +struct xen_foreign_memory_map { + domid_t domid; + struct xen_memory_map map; +}; +typedef struct xen_foreign_memory_map xen_foreign_memory_map_t; +DEFINE_XEN_GUEST_HANDLE(xen_foreign_memory_map_t); + +#define XENMEM_set_pod_target 16 +#define XENMEM_get_pod_target 17 +struct xen_pod_target { + /* IN */ + uint64_t target_pages; + /* OUT */ + uint64_t tot_pages; + uint64_t pod_cache_pages; + uint64_t pod_entries; + /* IN */ + domid_t domid; +}; +typedef struct xen_pod_target xen_pod_target_t; + +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +#ifndef uint64_aligned_t +#define uint64_aligned_t uint64_t +#endif + +/* + * Get the number of MFNs saved through memory sharing. + * The call never fails. + */ +#define XENMEM_get_sharing_freed_pages 18 +#define XENMEM_get_sharing_shared_pages 19 + +#define XENMEM_paging_op 20 +#define XENMEM_paging_op_nominate 0 +#define XENMEM_paging_op_evict 1 +#define XENMEM_paging_op_prep 2 + +struct xen_mem_paging_op { + uint8_t op; /* XENMEM_paging_op_* */ + domid_t domain; + + /* IN: (XENMEM_paging_op_prep) buffer to immediately fill page from */ + XEN_GUEST_HANDLE_64(const_uint8) buffer; + /* IN: gfn of page being operated on */ + uint64_aligned_t gfn; +}; +typedef struct xen_mem_paging_op xen_mem_paging_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_mem_paging_op_t); + +#define XENMEM_access_op 21 +#define XENMEM_access_op_set_access 0 +#define XENMEM_access_op_get_access 1 +/* + * XENMEM_access_op_enable_emulate and XENMEM_access_op_disable_emulate are + * currently unused, but since they have been in use please do not reuse them. + * + * #define XENMEM_access_op_enable_emulate 2 + * #define XENMEM_access_op_disable_emulate 3 + */ +#define XENMEM_access_op_set_access_multi 4 + +typedef enum { + XENMEM_access_n, + XENMEM_access_r, + XENMEM_access_w, + XENMEM_access_rw, + XENMEM_access_x, + XENMEM_access_rx, + XENMEM_access_wx, + XENMEM_access_rwx, + /* + * Page starts off as r-x, but automatically + * change to r-w on a write + */ + XENMEM_access_rx2rw, + /* + * Log access: starts off as n, automatically + * goes to rwx, generating an event without + * pausing the vcpu + */ + XENMEM_access_n2rwx, + /* Take the domain default */ + XENMEM_access_default +} xenmem_access_t; + +struct xen_mem_access_op { + /* XENMEM_access_op_* */ + uint8_t op; + /* xenmem_access_t */ + uint8_t access; + domid_t domid; + /* + * Number of pages for set op (or size of pfn_list for + * XENMEM_access_op_set_access_multi) + * Ignored on setting default access and other ops + */ + uint32_t nr; + /* + * First pfn for set op + * pfn for get op + * ~0ull is used to set and get the default access for pages + */ + uint64_aligned_t pfn; + /* + * List of pfns to set access for + * Used only with XENMEM_access_op_set_access_multi + */ + XEN_GUEST_HANDLE(const_uint64) pfn_list; + /* + * Corresponding list of access settings for pfn_list + * Used only with XENMEM_access_op_set_access_multi + */ + XEN_GUEST_HANDLE(const_uint8) access_list; +}; +typedef struct xen_mem_access_op xen_mem_access_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_mem_access_op_t); + +#define XENMEM_sharing_op 22 +#define XENMEM_sharing_op_nominate_gfn 0 +#define XENMEM_sharing_op_nominate_gref 1 +#define XENMEM_sharing_op_share 2 +#define XENMEM_sharing_op_debug_gfn 3 +#define XENMEM_sharing_op_debug_mfn 4 +#define XENMEM_sharing_op_debug_gref 5 +#define XENMEM_sharing_op_add_physmap 6 +#define XENMEM_sharing_op_audit 7 +#define XENMEM_sharing_op_range_share 8 +#define XENMEM_sharing_op_fork 9 +#define XENMEM_sharing_op_fork_reset 10 + +#define XENMEM_SHARING_OP_S_HANDLE_INVALID (-10) +#define XENMEM_SHARING_OP_C_HANDLE_INVALID (-9) + +/* The following allows sharing of grant refs. This is useful + * for sharing utilities sitting as "filters" in IO backends + * (e.g. memshr + blktap(2)). The IO backend is only exposed + * to grant references, and this allows sharing of the grefs */ +#define XENMEM_SHARING_OP_FIELD_IS_GREF_FLAG (xen_mk_ullong(1) << 62) + +#define XENMEM_SHARING_OP_FIELD_MAKE_GREF(field, val) \ + (field) = (XENMEM_SHARING_OP_FIELD_IS_GREF_FLAG | val) +#define XENMEM_SHARING_OP_FIELD_IS_GREF(field) \ + ((field) & XENMEM_SHARING_OP_FIELD_IS_GREF_FLAG) +#define XENMEM_SHARING_OP_FIELD_GET_GREF(field) \ + ((field) & (~XENMEM_SHARING_OP_FIELD_IS_GREF_FLAG)) + +struct xen_mem_sharing_op { + uint8_t op; /* XENMEM_sharing_op_* */ + domid_t domain; + + union { + struct mem_sharing_op_nominate { /* OP_NOMINATE_xxx */ + union { + uint64_aligned_t gfn; /* IN: gfn to nominate */ + uint32_t grant_ref; /* IN: grant ref to nominate */ + } u; + uint64_aligned_t handle; /* OUT: the handle */ + } nominate; + struct mem_sharing_op_share { /* OP_SHARE/ADD_PHYSMAP */ + uint64_aligned_t source_gfn; /* IN: the gfn of the source page */ + uint64_aligned_t source_handle; /* IN: handle to the source page */ + uint64_aligned_t client_gfn; /* IN: the client gfn */ + uint64_aligned_t client_handle; /* IN: handle to the client page */ + domid_t client_domain; /* IN: the client domain id */ + } share; + struct mem_sharing_op_range { /* OP_RANGE_SHARE */ + uint64_aligned_t first_gfn; /* IN: the first gfn */ + uint64_aligned_t last_gfn; /* IN: the last gfn */ + uint64_aligned_t opaque; /* Must be set to 0 */ + domid_t client_domain; /* IN: the client domain id */ + uint16_t _pad[3]; /* Must be set to 0 */ + } range; + struct mem_sharing_op_debug { /* OP_DEBUG_xxx */ + union { + uint64_aligned_t gfn; /* IN: gfn to debug */ + uint64_aligned_t mfn; /* IN: mfn to debug */ + uint32_t gref; /* IN: gref to debug */ + } u; + } debug; + struct mem_sharing_op_fork { /* OP_FORK{,_RESET} */ + domid_t parent_domain; /* IN: parent's domain id */ +/* Only makes sense for short-lived forks */ +#define XENMEM_FORK_WITH_IOMMU_ALLOWED (1u << 0) +/* Only makes sense for short-lived forks */ +#define XENMEM_FORK_BLOCK_INTERRUPTS (1u << 1) +#define XENMEM_FORK_RESET_STATE (1u << 2) +#define XENMEM_FORK_RESET_MEMORY (1u << 3) + uint16_t flags; /* IN: optional settings */ + uint32_t pad; /* Must be set to 0 */ + } fork; + } u; +}; +typedef struct xen_mem_sharing_op xen_mem_sharing_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_mem_sharing_op_t); + +/* + * Attempt to stake a claim for a domain on a quantity of pages + * of system RAM, but _not_ assign specific pageframes. Only + * arithmetic is performed so the hypercall is very fast and need + * not be preemptible, thus sidestepping time-of-check-time-of-use + * races for memory allocation. Returns 0 if the hypervisor page + * allocator has atomically and successfully claimed the requested + * number of pages, else non-zero. + * + * Any domain may have only one active claim. When sufficient memory + * has been allocated to resolve the claim, the claim silently expires. + * Claiming zero pages effectively resets any outstanding claim and + * is always successful. + * + * Note that a valid claim may be staked even after memory has been + * allocated for a domain. In this case, the claim is not incremental, + * i.e. if the domain's total page count is 3, and a claim is staked + * for 10, only 7 additional pages are claimed. + * + * Caller must be privileged or the hypercall fails. + */ +#define XENMEM_claim_pages 24 + +/* + * XENMEM_claim_pages flags - the are no flags at this time. + * The zero value is appropriate. + */ + +/* + * With some legacy devices, certain guest-physical addresses cannot safely + * be used for other purposes, e.g. to map guest RAM. This hypercall + * enumerates those regions so the toolstack can avoid using them. + */ +#define XENMEM_reserved_device_memory_map 27 +struct xen_reserved_device_memory { + xen_pfn_t start_pfn; + xen_ulong_t nr_pages; +}; +typedef struct xen_reserved_device_memory xen_reserved_device_memory_t; +DEFINE_XEN_GUEST_HANDLE(xen_reserved_device_memory_t); + +struct xen_reserved_device_memory_map { +#define XENMEM_RDM_ALL 1 /* Request all regions (ignore dev union). */ + /* IN */ + uint32_t flags; + /* + * IN/OUT + * + * Gets set to the required number of entries when too low, + * signaled by error code -ERANGE. + */ + unsigned int nr_entries; + /* OUT */ + XEN_GUEST_HANDLE(xen_reserved_device_memory_t) buffer; + /* IN */ + union { + physdev_pci_device_t pci; + } dev; +}; +typedef struct xen_reserved_device_memory_map xen_reserved_device_memory_map_t; +DEFINE_XEN_GUEST_HANDLE(xen_reserved_device_memory_map_t); + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +/* + * Get the pages for a particular guest resource, so that they can be + * mapped directly by a tools domain. + */ +#define XENMEM_acquire_resource 28 +struct xen_mem_acquire_resource { + /* IN - The domain whose resource is to be mapped */ + domid_t domid; + /* IN - the type of resource */ + uint16_t type; + +#define XENMEM_resource_ioreq_server 0 +#define XENMEM_resource_grant_table 1 +#define XENMEM_resource_vmtrace_buf 2 + + /* + * IN - a type-specific resource identifier, which must be zero + * unless stated otherwise. + * + * type == XENMEM_resource_ioreq_server -> id == ioreq server id + * type == XENMEM_resource_grant_table -> id defined below + */ + uint32_t id; + +#define XENMEM_resource_grant_table_id_shared 0 +#define XENMEM_resource_grant_table_id_status 1 + + /* + * IN/OUT + * + * As an IN parameter number of frames of the resource to be mapped. + * This value may be updated over the course of the operation. + * + * When frame_list is NULL and nr_frames is 0, this is interpreted as a + * request for the size of the resource, which shall be returned in the + * nr_frames field. + * + * The size of a resource will never be zero, but a nonzero result doesn't + * guarantee that a subsequent mapping request will be successful. There + * are further type/id specific constraints which may change between the + * two calls. + */ + uint32_t nr_frames; + /* + * Padding field, must be zero on input. + * In a previous version this was an output field with the lowest bit + * named XENMEM_rsrc_acq_caller_owned. Future versions of this interface + * will not reuse this bit as an output with the field being zero on + * input. + */ + uint32_t pad; + /* + * IN - the index of the initial frame to be mapped. This parameter + * is ignored if nr_frames is 0. This value may be updated + * over the course of the operation. + */ + uint64_t frame; + +#define XENMEM_resource_ioreq_server_frame_bufioreq 0 +#define XENMEM_resource_ioreq_server_frame_ioreq(n) (1 + (n)) + + /* + * IN/OUT - If the tools domain is PV then, upon return, frame_list + * will be populated with the MFNs of the resource. + * If the tools domain is HVM then it is expected that, on + * entry, frame_list will be populated with a list of GFNs + * that will be mapped to the MFNs of the resource. + * If -EIO is returned then the frame_list has only been + * partially mapped and it is up to the caller to unmap all + * the GFNs. + * This parameter may be NULL if nr_frames is 0. This + * value may be updated over the course of the operation. + */ + XEN_GUEST_HANDLE(xen_pfn_t) frame_list; +}; +typedef struct xen_mem_acquire_resource xen_mem_acquire_resource_t; +DEFINE_XEN_GUEST_HANDLE(xen_mem_acquire_resource_t); + +/* + * XENMEM_get_vnumainfo used by guest to get + * vNUMA topology from hypervisor. + */ +#define XENMEM_get_vnumainfo 26 + +/* vNUMA node memory ranges */ +struct xen_vmemrange { + uint64_t start, end; + unsigned int flags; + unsigned int nid; +}; +typedef struct xen_vmemrange xen_vmemrange_t; +DEFINE_XEN_GUEST_HANDLE(xen_vmemrange_t); + +/* + * vNUMA topology specifies vNUMA node number, distance table, + * memory ranges and vcpu mapping provided for guests. + * XENMEM_get_vnumainfo hypercall expects to see from guest + * nr_vnodes, nr_vmemranges and nr_vcpus to indicate available memory. + * After filling guests structures, nr_vnodes, nr_vmemranges and nr_vcpus + * copied back to guest. Domain returns expected values of nr_vnodes, + * nr_vmemranges and nr_vcpus to guest if the values where incorrect. + */ +struct xen_vnuma_topology_info { + /* IN */ + domid_t domid; + uint16_t pad; + /* IN/OUT */ + unsigned int nr_vnodes; + unsigned int nr_vcpus; + unsigned int nr_vmemranges; + /* OUT */ + union { + XEN_GUEST_HANDLE(uint) h; + uint64_t pad; + } vdistance; + union { + XEN_GUEST_HANDLE(uint) h; + uint64_t pad; + } vcpu_to_vnode; + union { + XEN_GUEST_HANDLE(xen_vmemrange_t) h; + uint64_t pad; + } vmemrange; +}; +typedef struct xen_vnuma_topology_info xen_vnuma_topology_info_t; +DEFINE_XEN_GUEST_HANDLE(xen_vnuma_topology_info_t); + +/* Next available subop number is 29 */ + +#endif /* __XEN_PUBLIC_MEMORY_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/physdev.h b/include/hw/xen/interface/physdev.h new file mode 100644 index 0000000000..f0c0d4727c --- /dev/null +++ b/include/hw/xen/interface/physdev.h @@ -0,0 +1,366 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2006, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_PHYSDEV_H__ +#define __XEN_PUBLIC_PHYSDEV_H__ + +#include "xen.h" + +/* + * Prototype for this hypercall is: + * int physdev_op(int cmd, void *args) + * @cmd == PHYSDEVOP_??? (physdev operation). + * @args == Operation-specific extra arguments (NULL if none). + */ + +/* + * Notify end-of-interrupt (EOI) for the specified IRQ. + * @arg == pointer to physdev_eoi structure. + */ +#define PHYSDEVOP_eoi 12 +struct physdev_eoi { + /* IN */ + uint32_t irq; +}; +typedef struct physdev_eoi physdev_eoi_t; +DEFINE_XEN_GUEST_HANDLE(physdev_eoi_t); + +/* + * Register a shared page for the hypervisor to indicate whether the guest + * must issue PHYSDEVOP_eoi. The semantics of PHYSDEVOP_eoi change slightly + * once the guest used this function in that the associated event channel + * will automatically get unmasked. The page registered is used as a bit + * array indexed by Xen's PIRQ value. + */ +#define PHYSDEVOP_pirq_eoi_gmfn_v1 17 +/* + * Register a shared page for the hypervisor to indicate whether the + * guest must issue PHYSDEVOP_eoi. This hypercall is very similar to + * PHYSDEVOP_pirq_eoi_gmfn_v1 but it doesn't change the semantics of + * PHYSDEVOP_eoi. The page registered is used as a bit array indexed by + * Xen's PIRQ value. + */ +#define PHYSDEVOP_pirq_eoi_gmfn_v2 28 +struct physdev_pirq_eoi_gmfn { + /* IN */ + xen_pfn_t gmfn; +}; +typedef struct physdev_pirq_eoi_gmfn physdev_pirq_eoi_gmfn_t; +DEFINE_XEN_GUEST_HANDLE(physdev_pirq_eoi_gmfn_t); + +/* + * Query the status of an IRQ line. + * @arg == pointer to physdev_irq_status_query structure. + */ +#define PHYSDEVOP_irq_status_query 5 +struct physdev_irq_status_query { + /* IN */ + uint32_t irq; + /* OUT */ + uint32_t flags; /* XENIRQSTAT_* */ +}; +typedef struct physdev_irq_status_query physdev_irq_status_query_t; +DEFINE_XEN_GUEST_HANDLE(physdev_irq_status_query_t); + +/* Need to call PHYSDEVOP_eoi when the IRQ has been serviced? */ +#define _XENIRQSTAT_needs_eoi (0) +#define XENIRQSTAT_needs_eoi (1U<<_XENIRQSTAT_needs_eoi) + +/* IRQ shared by multiple guests? */ +#define _XENIRQSTAT_shared (1) +#define XENIRQSTAT_shared (1U<<_XENIRQSTAT_shared) + +/* + * Set the current VCPU's I/O privilege level. + * @arg == pointer to physdev_set_iopl structure. + */ +#define PHYSDEVOP_set_iopl 6 +struct physdev_set_iopl { + /* IN */ + uint32_t iopl; +}; +typedef struct physdev_set_iopl physdev_set_iopl_t; +DEFINE_XEN_GUEST_HANDLE(physdev_set_iopl_t); + +/* + * Set the current VCPU's I/O-port permissions bitmap. + * @arg == pointer to physdev_set_iobitmap structure. + */ +#define PHYSDEVOP_set_iobitmap 7 +struct physdev_set_iobitmap { + /* IN */ +#if __XEN_INTERFACE_VERSION__ >= 0x00030205 + XEN_GUEST_HANDLE(uint8) bitmap; +#else + uint8_t *bitmap; +#endif + uint32_t nr_ports; +}; +typedef struct physdev_set_iobitmap physdev_set_iobitmap_t; +DEFINE_XEN_GUEST_HANDLE(physdev_set_iobitmap_t); + +/* + * Read or write an IO-APIC register. + * @arg == pointer to physdev_apic structure. + */ +#define PHYSDEVOP_apic_read 8 +#define PHYSDEVOP_apic_write 9 +struct physdev_apic { + /* IN */ + unsigned long apic_physbase; + uint32_t reg; + /* IN or OUT */ + uint32_t value; +}; +typedef struct physdev_apic physdev_apic_t; +DEFINE_XEN_GUEST_HANDLE(physdev_apic_t); + +/* + * Allocate or free a physical upcall vector for the specified IRQ line. + * @arg == pointer to physdev_irq structure. + */ +#define PHYSDEVOP_alloc_irq_vector 10 +#define PHYSDEVOP_free_irq_vector 11 +struct physdev_irq { + /* IN */ + uint32_t irq; + /* IN or OUT */ + uint32_t vector; +}; +typedef struct physdev_irq physdev_irq_t; +DEFINE_XEN_GUEST_HANDLE(physdev_irq_t); + +#define MAP_PIRQ_TYPE_MSI 0x0 +#define MAP_PIRQ_TYPE_GSI 0x1 +#define MAP_PIRQ_TYPE_UNKNOWN 0x2 +#define MAP_PIRQ_TYPE_MSI_SEG 0x3 +#define MAP_PIRQ_TYPE_MULTI_MSI 0x4 + +#define PHYSDEVOP_map_pirq 13 +struct physdev_map_pirq { + domid_t domid; + /* IN */ + int type; + /* IN (ignored for ..._MULTI_MSI) */ + int index; + /* IN or OUT */ + int pirq; + /* IN - high 16 bits hold segment for ..._MSI_SEG and ..._MULTI_MSI */ + int bus; + /* IN */ + int devfn; + /* IN (also OUT for ..._MULTI_MSI) */ + int entry_nr; + /* IN */ + uint64_t table_base; +}; +typedef struct physdev_map_pirq physdev_map_pirq_t; +DEFINE_XEN_GUEST_HANDLE(physdev_map_pirq_t); + +#define PHYSDEVOP_unmap_pirq 14 +struct physdev_unmap_pirq { + domid_t domid; + /* IN */ + int pirq; +}; + +typedef struct physdev_unmap_pirq physdev_unmap_pirq_t; +DEFINE_XEN_GUEST_HANDLE(physdev_unmap_pirq_t); + +#define PHYSDEVOP_manage_pci_add 15 +#define PHYSDEVOP_manage_pci_remove 16 +struct physdev_manage_pci { + /* IN */ + uint8_t bus; + uint8_t devfn; +}; + +typedef struct physdev_manage_pci physdev_manage_pci_t; +DEFINE_XEN_GUEST_HANDLE(physdev_manage_pci_t); + +#define PHYSDEVOP_restore_msi 19 +struct physdev_restore_msi { + /* IN */ + uint8_t bus; + uint8_t devfn; +}; +typedef struct physdev_restore_msi physdev_restore_msi_t; +DEFINE_XEN_GUEST_HANDLE(physdev_restore_msi_t); + +#define PHYSDEVOP_manage_pci_add_ext 20 +struct physdev_manage_pci_ext { + /* IN */ + uint8_t bus; + uint8_t devfn; + uint32_t is_extfn; + uint32_t is_virtfn; + struct { + uint8_t bus; + uint8_t devfn; + } physfn; +}; + +typedef struct physdev_manage_pci_ext physdev_manage_pci_ext_t; +DEFINE_XEN_GUEST_HANDLE(physdev_manage_pci_ext_t); + +/* + * Argument to physdev_op_compat() hypercall. Superceded by new physdev_op() + * hypercall since 0x00030202. + */ +struct physdev_op { + uint32_t cmd; + union { + physdev_irq_status_query_t irq_status_query; + physdev_set_iopl_t set_iopl; + physdev_set_iobitmap_t set_iobitmap; + physdev_apic_t apic_op; + physdev_irq_t irq_op; + } u; +}; +typedef struct physdev_op physdev_op_t; +DEFINE_XEN_GUEST_HANDLE(physdev_op_t); + +#define PHYSDEVOP_setup_gsi 21 +struct physdev_setup_gsi { + int gsi; + /* IN */ + uint8_t triggering; + /* IN */ + uint8_t polarity; + /* IN */ +}; + +typedef struct physdev_setup_gsi physdev_setup_gsi_t; +DEFINE_XEN_GUEST_HANDLE(physdev_setup_gsi_t); + +/* leave PHYSDEVOP 22 free */ + +/* type is MAP_PIRQ_TYPE_GSI or MAP_PIRQ_TYPE_MSI + * the hypercall returns a free pirq */ +#define PHYSDEVOP_get_free_pirq 23 +struct physdev_get_free_pirq { + /* IN */ + int type; + /* OUT */ + uint32_t pirq; +}; + +typedef struct physdev_get_free_pirq physdev_get_free_pirq_t; +DEFINE_XEN_GUEST_HANDLE(physdev_get_free_pirq_t); + +#define XEN_PCI_MMCFG_RESERVED 0x1 + +#define PHYSDEVOP_pci_mmcfg_reserved 24 +struct physdev_pci_mmcfg_reserved { + uint64_t address; + uint16_t segment; + uint8_t start_bus; + uint8_t end_bus; + uint32_t flags; +}; +typedef struct physdev_pci_mmcfg_reserved physdev_pci_mmcfg_reserved_t; +DEFINE_XEN_GUEST_HANDLE(physdev_pci_mmcfg_reserved_t); + +#define XEN_PCI_DEV_EXTFN 0x1 +#define XEN_PCI_DEV_VIRTFN 0x2 +#define XEN_PCI_DEV_PXM 0x4 + +#define PHYSDEVOP_pci_device_add 25 +struct physdev_pci_device_add { + /* IN */ + uint16_t seg; + uint8_t bus; + uint8_t devfn; + uint32_t flags; + struct { + uint8_t bus; + uint8_t devfn; + } physfn; + /* + * Optional parameters array. + * First element ([0]) is PXM domain associated with the device (if + * XEN_PCI_DEV_PXM is set) + */ + uint32_t optarr[XEN_FLEX_ARRAY_DIM]; +}; +typedef struct physdev_pci_device_add physdev_pci_device_add_t; +DEFINE_XEN_GUEST_HANDLE(physdev_pci_device_add_t); + +#define PHYSDEVOP_pci_device_remove 26 +#define PHYSDEVOP_restore_msi_ext 27 +/* + * Dom0 should use these two to announce MMIO resources assigned to + * MSI-X capable devices won't (prepare) or may (release) change. + */ +#define PHYSDEVOP_prepare_msix 30 +#define PHYSDEVOP_release_msix 31 +struct physdev_pci_device { + /* IN */ + uint16_t seg; + uint8_t bus; + uint8_t devfn; +}; +typedef struct physdev_pci_device physdev_pci_device_t; +DEFINE_XEN_GUEST_HANDLE(physdev_pci_device_t); + +#define PHYSDEVOP_DBGP_RESET_PREPARE 1 +#define PHYSDEVOP_DBGP_RESET_DONE 2 + +#define PHYSDEVOP_DBGP_BUS_UNKNOWN 0 +#define PHYSDEVOP_DBGP_BUS_PCI 1 + +#define PHYSDEVOP_dbgp_op 29 +struct physdev_dbgp_op { + /* IN */ + uint8_t op; + uint8_t bus; + union { + physdev_pci_device_t pci; + } u; +}; +typedef struct physdev_dbgp_op physdev_dbgp_op_t; +DEFINE_XEN_GUEST_HANDLE(physdev_dbgp_op_t); + +/* + * Notify that some PIRQ-bound event channels have been unmasked. + * ** This command is obsolete since interface version 0x00030202 and is ** + * ** unsupported by newer versions of Xen. ** + */ +#define PHYSDEVOP_IRQ_UNMASK_NOTIFY 4 + +#if __XEN_INTERFACE_VERSION__ < 0x00040600 +/* + * These all-capitals physdev operation names are superceded by the new names + * (defined above) since interface version 0x00030202. The guard above was + * added post-4.5 only though and hence shouldn't check for 0x00030202. + */ +#define PHYSDEVOP_IRQ_STATUS_QUERY PHYSDEVOP_irq_status_query +#define PHYSDEVOP_SET_IOPL PHYSDEVOP_set_iopl +#define PHYSDEVOP_SET_IOBITMAP PHYSDEVOP_set_iobitmap +#define PHYSDEVOP_APIC_READ PHYSDEVOP_apic_read +#define PHYSDEVOP_APIC_WRITE PHYSDEVOP_apic_write +#define PHYSDEVOP_ASSIGN_VECTOR PHYSDEVOP_alloc_irq_vector +#define PHYSDEVOP_FREE_VECTOR PHYSDEVOP_free_irq_vector +#define PHYSDEVOP_IRQ_NEEDS_UNMASK_NOTIFY XENIRQSTAT_needs_eoi +#define PHYSDEVOP_IRQ_SHARED XENIRQSTAT_shared +#endif + +#if __XEN_INTERFACE_VERSION__ < 0x00040200 +#define PHYSDEVOP_pirq_eoi_gmfn PHYSDEVOP_pirq_eoi_gmfn_v1 +#else +#define PHYSDEVOP_pirq_eoi_gmfn PHYSDEVOP_pirq_eoi_gmfn_v2 +#endif + +#endif /* __XEN_PUBLIC_PHYSDEV_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/sched.h b/include/hw/xen/interface/sched.h new file mode 100644 index 0000000000..b4362c6a1d --- /dev/null +++ b/include/hw/xen/interface/sched.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: MIT */ +/****************************************************************************** + * sched.h + * + * Scheduler state interactions + * + * Copyright (c) 2005, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_SCHED_H__ +#define __XEN_PUBLIC_SCHED_H__ + +#include "event_channel.h" + +/* + * `incontents 150 sched Guest Scheduler Operations + * + * The SCHEDOP interface provides mechanisms for a guest to interact + * with the scheduler, including yield, blocking and shutting itself + * down. + */ + +/* + * The prototype for this hypercall is: + * ` long HYPERVISOR_sched_op(enum sched_op cmd, void *arg, ...) + * + * @cmd == SCHEDOP_??? (scheduler operation). + * @arg == Operation-specific extra argument(s), as described below. + * ... == Additional Operation-specific extra arguments, described below. + * + * Versions of Xen prior to 3.0.2 provided only the following legacy version + * of this hypercall, supporting only the commands yield, block and shutdown: + * long sched_op(int cmd, unsigned long arg) + * @cmd == SCHEDOP_??? (scheduler operation). + * @arg == 0 (SCHEDOP_yield and SCHEDOP_block) + * == SHUTDOWN_* code (SCHEDOP_shutdown) + * + * This legacy version is available to new guests as: + * ` long HYPERVISOR_sched_op_compat(enum sched_op cmd, unsigned long arg) + */ + +/* ` enum sched_op { // SCHEDOP_* => struct sched_* */ +/* + * Voluntarily yield the CPU. + * @arg == NULL. + */ +#define SCHEDOP_yield 0 + +/* + * Block execution of this VCPU until an event is received for processing. + * If called with event upcalls masked, this operation will atomically + * reenable event delivery and check for pending events before blocking the + * VCPU. This avoids a "wakeup waiting" race. + * @arg == NULL. + */ +#define SCHEDOP_block 1 + +/* + * Halt execution of this domain (all VCPUs) and notify the system controller. + * @arg == pointer to sched_shutdown_t structure. + * + * If the sched_shutdown_t reason is SHUTDOWN_suspend then + * x86 PV guests must also set RDX (EDX for 32-bit guests) to the MFN + * of the guest's start info page. RDX/EDX is the third hypercall + * argument. + * + * In addition, which reason is SHUTDOWN_suspend this hypercall + * returns 1 if suspend was cancelled or the domain was merely + * checkpointed, and 0 if it is resuming in a new domain. + */ +#define SCHEDOP_shutdown 2 + +/* + * Poll a set of event-channel ports. Return when one or more are pending. An + * optional timeout may be specified. + * @arg == pointer to sched_poll_t structure. + */ +#define SCHEDOP_poll 3 + +/* + * Declare a shutdown for another domain. The main use of this function is + * in interpreting shutdown requests and reasons for fully-virtualized + * domains. A para-virtualized domain may use SCHEDOP_shutdown directly. + * @arg == pointer to sched_remote_shutdown_t structure. + */ +#define SCHEDOP_remote_shutdown 4 + +/* + * Latch a shutdown code, so that when the domain later shuts down it + * reports this code to the control tools. + * @arg == sched_shutdown_t, as for SCHEDOP_shutdown. + */ +#define SCHEDOP_shutdown_code 5 + +/* + * Setup, poke and destroy a domain watchdog timer. + * @arg == pointer to sched_watchdog_t structure. + * With id == 0, setup a domain watchdog timer to cause domain shutdown + * after timeout, returns watchdog id. + * With id != 0 and timeout == 0, destroy domain watchdog timer. + * With id != 0 and timeout != 0, poke watchdog timer and set new timeout. + */ +#define SCHEDOP_watchdog 6 + +/* + * Override the current vcpu affinity by pinning it to one physical cpu or + * undo this override restoring the previous affinity. + * @arg == pointer to sched_pin_override_t structure. + * + * A negative pcpu value will undo a previous pin override and restore the + * previous cpu affinity. + * This call is allowed for the hardware domain only and requires the cpu + * to be part of the domain's cpupool. + */ +#define SCHEDOP_pin_override 7 +/* ` } */ + +struct sched_shutdown { + unsigned int reason; /* SHUTDOWN_* => enum sched_shutdown_reason */ +}; +typedef struct sched_shutdown sched_shutdown_t; +DEFINE_XEN_GUEST_HANDLE(sched_shutdown_t); + +struct sched_poll { + XEN_GUEST_HANDLE(evtchn_port_t) ports; + unsigned int nr_ports; + uint64_t timeout; +}; +typedef struct sched_poll sched_poll_t; +DEFINE_XEN_GUEST_HANDLE(sched_poll_t); + +struct sched_remote_shutdown { + domid_t domain_id; /* Remote domain ID */ + unsigned int reason; /* SHUTDOWN_* => enum sched_shutdown_reason */ +}; +typedef struct sched_remote_shutdown sched_remote_shutdown_t; +DEFINE_XEN_GUEST_HANDLE(sched_remote_shutdown_t); + +struct sched_watchdog { + uint32_t id; /* watchdog ID */ + uint32_t timeout; /* timeout */ +}; +typedef struct sched_watchdog sched_watchdog_t; +DEFINE_XEN_GUEST_HANDLE(sched_watchdog_t); + +struct sched_pin_override { + int32_t pcpu; +}; +typedef struct sched_pin_override sched_pin_override_t; +DEFINE_XEN_GUEST_HANDLE(sched_pin_override_t); + +/* + * Reason codes for SCHEDOP_shutdown. These may be interpreted by control + * software to determine the appropriate action. For the most part, Xen does + * not care about the shutdown code. + */ +/* ` enum sched_shutdown_reason { */ +#define SHUTDOWN_poweroff 0 /* Domain exited normally. Clean up and kill. */ +#define SHUTDOWN_reboot 1 /* Clean up, kill, and then restart. */ +#define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */ +#define SHUTDOWN_crash 3 /* Tell controller we've crashed. */ +#define SHUTDOWN_watchdog 4 /* Restart because watchdog time expired. */ + +/* + * Domain asked to perform 'soft reset' for it. The expected behavior is to + * reset internal Xen state for the domain returning it to the point where it + * was created but leaving the domain's memory contents and vCPU contexts + * intact. This will allow the domain to start over and set up all Xen specific + * interfaces again. + */ +#define SHUTDOWN_soft_reset 5 +#define SHUTDOWN_MAX 5 /* Maximum valid shutdown reason. */ +/* ` } */ + +#endif /* __XEN_PUBLIC_SCHED_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/trace.h b/include/hw/xen/interface/trace.h new file mode 100644 index 0000000000..62a179971d --- /dev/null +++ b/include/hw/xen/interface/trace.h @@ -0,0 +1,324 @@ +/* SPDX-License-Identifier: MIT */ +/****************************************************************************** + * include/public/trace.h + * + * Mark Williamson, (C) 2004 Intel Research Cambridge + * Copyright (C) 2005 Bin Ren + */ + +#ifndef __XEN_PUBLIC_TRACE_H__ +#define __XEN_PUBLIC_TRACE_H__ + +#define TRACE_EXTRA_MAX 7 +#define TRACE_EXTRA_SHIFT 28 + +/* Trace classes */ +#define TRC_CLS_SHIFT 16 +#define TRC_GEN 0x0001f000 /* General trace */ +#define TRC_SCHED 0x0002f000 /* Xen Scheduler trace */ +#define TRC_DOM0OP 0x0004f000 /* Xen DOM0 operation trace */ +#define TRC_HVM 0x0008f000 /* Xen HVM trace */ +#define TRC_MEM 0x0010f000 /* Xen memory trace */ +#define TRC_PV 0x0020f000 /* Xen PV traces */ +#define TRC_SHADOW 0x0040f000 /* Xen shadow tracing */ +#define TRC_HW 0x0080f000 /* Xen hardware-related traces */ +#define TRC_GUEST 0x0800f000 /* Guest-generated traces */ +#define TRC_ALL 0x0ffff000 +#define TRC_HD_TO_EVENT(x) ((x)&0x0fffffff) +#define TRC_HD_CYCLE_FLAG (1UL<<31) +#define TRC_HD_INCLUDES_CYCLE_COUNT(x) ( !!( (x) & TRC_HD_CYCLE_FLAG ) ) +#define TRC_HD_EXTRA(x) (((x)>>TRACE_EXTRA_SHIFT)&TRACE_EXTRA_MAX) + +/* Trace subclasses */ +#define TRC_SUBCLS_SHIFT 12 + +/* trace subclasses for SVM */ +#define TRC_HVM_ENTRYEXIT 0x00081000 /* VMENTRY and #VMEXIT */ +#define TRC_HVM_HANDLER 0x00082000 /* various HVM handlers */ +#define TRC_HVM_EMUL 0x00084000 /* emulated devices */ + +#define TRC_SCHED_MIN 0x00021000 /* Just runstate changes */ +#define TRC_SCHED_CLASS 0x00022000 /* Scheduler-specific */ +#define TRC_SCHED_VERBOSE 0x00028000 /* More inclusive scheduling */ + +/* + * The highest 3 bits of the last 12 bits of TRC_SCHED_CLASS above are + * reserved for encoding what scheduler produced the information. The + * actual event is encoded in the last 9 bits. + * + * This means we have 8 scheduling IDs available (which means at most 8 + * schedulers generating events) and, in each scheduler, up to 512 + * different events. + */ +#define TRC_SCHED_ID_BITS 3 +#define TRC_SCHED_ID_SHIFT (TRC_SUBCLS_SHIFT - TRC_SCHED_ID_BITS) +#define TRC_SCHED_ID_MASK (((1UL<cpu_offset[cpu]). + */ +struct t_info { + uint16_t tbuf_size; /* Size in pages of each trace buffer */ + uint16_t mfn_offset[]; /* Offset within t_info structure of the page list per cpu */ + /* MFN lists immediately after the header */ +}; + +#endif /* __XEN_PUBLIC_TRACE_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/vcpu.h b/include/hw/xen/interface/vcpu.h new file mode 100644 index 0000000000..81a3b3a743 --- /dev/null +++ b/include/hw/xen/interface/vcpu.h @@ -0,0 +1,231 @@ +/* SPDX-License-Identifier: MIT */ +/****************************************************************************** + * vcpu.h + * + * VCPU initialisation, query, and hotplug. + * + * Copyright (c) 2005, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_VCPU_H__ +#define __XEN_PUBLIC_VCPU_H__ + +#include "xen.h" + +/* + * Prototype for this hypercall is: + * long vcpu_op(int cmd, unsigned int vcpuid, void *extra_args) + * @cmd == VCPUOP_??? (VCPU operation). + * @vcpuid == VCPU to operate on. + * @extra_args == Operation-specific extra arguments (NULL if none). + */ + +/* + * Initialise a VCPU. Each VCPU can be initialised only once. A + * newly-initialised VCPU will not run until it is brought up by VCPUOP_up. + * + * @extra_arg == For PV or ARM guests this is a pointer to a vcpu_guest_context + * structure containing the initial state for the VCPU. For x86 + * HVM based guests this is a pointer to a vcpu_hvm_context + * structure. + */ +#define VCPUOP_initialise 0 + +/* + * Bring up a VCPU. This makes the VCPU runnable. This operation will fail + * if the VCPU has not been initialised (VCPUOP_initialise). + */ +#define VCPUOP_up 1 + +/* + * Bring down a VCPU (i.e., make it non-runnable). + * There are a few caveats that callers should observe: + * 1. This operation may return, and VCPU_is_up may return false, before the + * VCPU stops running (i.e., the command is asynchronous). It is a good + * idea to ensure that the VCPU has entered a non-critical loop before + * bringing it down. Alternatively, this operation is guaranteed + * synchronous if invoked by the VCPU itself. + * 2. After a VCPU is initialised, there is currently no way to drop all its + * references to domain memory. Even a VCPU that is down still holds + * memory references via its pagetable base pointer and GDT. It is good + * practise to move a VCPU onto an 'idle' or default page table, LDT and + * GDT before bringing it down. + */ +#define VCPUOP_down 2 + +/* Returns 1 if the given VCPU is up. */ +#define VCPUOP_is_up 3 + +/* + * Return information about the state and running time of a VCPU. + * @extra_arg == pointer to vcpu_runstate_info structure. + */ +#define VCPUOP_get_runstate_info 4 +struct vcpu_runstate_info { + /* VCPU's current state (RUNSTATE_*). */ + int state; + /* When was current state entered (system time, ns)? */ + uint64_t state_entry_time; + /* + * Update indicator set in state_entry_time: + * When activated via VMASST_TYPE_runstate_update_flag, set during + * updates in guest memory mapped copy of vcpu_runstate_info. + */ +#define XEN_RUNSTATE_UPDATE (xen_mk_ullong(1) << 63) + /* + * Time spent in each RUNSTATE_* (ns). The sum of these times is + * guaranteed not to drift from system time. + */ + uint64_t time[4]; +}; +typedef struct vcpu_runstate_info vcpu_runstate_info_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_runstate_info_t); + +/* VCPU is currently running on a physical CPU. */ +#define RUNSTATE_running 0 + +/* VCPU is runnable, but not currently scheduled on any physical CPU. */ +#define RUNSTATE_runnable 1 + +/* VCPU is blocked (a.k.a. idle). It is therefore not runnable. */ +#define RUNSTATE_blocked 2 + +/* + * VCPU is not runnable, but it is not blocked. + * This is a 'catch all' state for things like hotplug and pauses by the + * system administrator (or for critical sections in the hypervisor). + * RUNSTATE_blocked dominates this state (it is the preferred state). + */ +#define RUNSTATE_offline 3 + +/* + * Register a shared memory area from which the guest may obtain its own + * runstate information without needing to execute a hypercall. + * Notes: + * 1. The registered address may be virtual or physical or guest handle, + * depending on the platform. Virtual address or guest handle should be + * registered on x86 systems. + * 2. Only one shared area may be registered per VCPU. The shared area is + * updated by the hypervisor each time the VCPU is scheduled. Thus + * runstate.state will always be RUNSTATE_running and + * runstate.state_entry_time will indicate the system time at which the + * VCPU was last scheduled to run. + * @extra_arg == pointer to vcpu_register_runstate_memory_area structure. + */ +#define VCPUOP_register_runstate_memory_area 5 +struct vcpu_register_runstate_memory_area { + union { + XEN_GUEST_HANDLE(vcpu_runstate_info_t) h; + struct vcpu_runstate_info *v; + uint64_t p; + } addr; +}; +typedef struct vcpu_register_runstate_memory_area vcpu_register_runstate_memory_area_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_register_runstate_memory_area_t); + +/* + * Set or stop a VCPU's periodic timer. Every VCPU has one periodic timer + * which can be set via these commands. Periods smaller than one millisecond + * may not be supported. + */ +#define VCPUOP_set_periodic_timer 6 /* arg == vcpu_set_periodic_timer_t */ +#define VCPUOP_stop_periodic_timer 7 /* arg == NULL */ +struct vcpu_set_periodic_timer { + uint64_t period_ns; +}; +typedef struct vcpu_set_periodic_timer vcpu_set_periodic_timer_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_set_periodic_timer_t); + +/* + * Set or stop a VCPU's single-shot timer. Every VCPU has one single-shot + * timer which can be set via these commands. + */ +#define VCPUOP_set_singleshot_timer 8 /* arg == vcpu_set_singleshot_timer_t */ +#define VCPUOP_stop_singleshot_timer 9 /* arg == NULL */ +struct vcpu_set_singleshot_timer { + uint64_t timeout_abs_ns; /* Absolute system time value in nanoseconds. */ + uint32_t flags; /* VCPU_SSHOTTMR_??? */ +}; +typedef struct vcpu_set_singleshot_timer vcpu_set_singleshot_timer_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_set_singleshot_timer_t); + +/* Flags to VCPUOP_set_singleshot_timer. */ + /* Require the timeout to be in the future (return -ETIME if it's passed). */ +#define _VCPU_SSHOTTMR_future (0) +#define VCPU_SSHOTTMR_future (1U << _VCPU_SSHOTTMR_future) + +/* + * Register a memory location in the guest address space for the + * vcpu_info structure. This allows the guest to place the vcpu_info + * structure in a convenient place, such as in a per-cpu data area. + * The pointer need not be page aligned, but the structure must not + * cross a page boundary. + * + * This may be called only once per vcpu. + */ +#define VCPUOP_register_vcpu_info 10 /* arg == vcpu_register_vcpu_info_t */ +struct vcpu_register_vcpu_info { + uint64_t mfn; /* mfn of page to place vcpu_info */ + uint32_t offset; /* offset within page */ + uint32_t rsvd; /* unused */ +}; +typedef struct vcpu_register_vcpu_info vcpu_register_vcpu_info_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_register_vcpu_info_t); + +/* Send an NMI to the specified VCPU. @extra_arg == NULL. */ +#define VCPUOP_send_nmi 11 + +/* + * Get the physical ID information for a pinned vcpu's underlying physical + * processor. The physical ID informmation is architecture-specific. + * On x86: id[31:0]=apic_id, id[63:32]=acpi_id. + * This command returns -EINVAL if it is not a valid operation for this VCPU. + */ +#define VCPUOP_get_physid 12 /* arg == vcpu_get_physid_t */ +struct vcpu_get_physid { + uint64_t phys_id; +}; +typedef struct vcpu_get_physid vcpu_get_physid_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_get_physid_t); +#define xen_vcpu_physid_to_x86_apicid(physid) ((uint32_t)(physid)) +#define xen_vcpu_physid_to_x86_acpiid(physid) ((uint32_t)((physid) >> 32)) + +/* + * Register a memory location to get a secondary copy of the vcpu time + * parameters. The master copy still exists as part of the vcpu shared + * memory area, and this secondary copy is updated whenever the master copy + * is updated (and using the same versioning scheme for synchronisation). + * + * The intent is that this copy may be mapped (RO) into userspace so + * that usermode can compute system time using the time info and the + * tsc. Usermode will see an array of vcpu_time_info structures, one + * for each vcpu, and choose the right one by an existing mechanism + * which allows it to get the current vcpu number (such as via a + * segment limit). It can then apply the normal algorithm to compute + * system time from the tsc. + * + * @extra_arg == pointer to vcpu_register_time_info_memory_area structure. + */ +#define VCPUOP_register_vcpu_time_memory_area 13 +DEFINE_XEN_GUEST_HANDLE(vcpu_time_info_t); +struct vcpu_register_time_memory_area { + union { + XEN_GUEST_HANDLE(vcpu_time_info_t) h; + struct vcpu_time_info *v; + uint64_t p; + } addr; +}; +typedef struct vcpu_register_time_memory_area vcpu_register_time_memory_area_t; +DEFINE_XEN_GUEST_HANDLE(vcpu_register_time_memory_area_t); + +#endif /* __XEN_PUBLIC_VCPU_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/version.h b/include/hw/xen/interface/version.h new file mode 100644 index 0000000000..9c78b4f3b6 --- /dev/null +++ b/include/hw/xen/interface/version.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: MIT */ +/****************************************************************************** + * version.h + * + * Xen version, type, and compile information. + * + * Copyright (c) 2005, Nguyen Anh Quynh + * Copyright (c) 2005, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_VERSION_H__ +#define __XEN_PUBLIC_VERSION_H__ + +#include "xen.h" + +/* NB. All ops return zero on success, except XENVER_{version,pagesize} + * XENVER_{version,pagesize,build_id} */ + +/* arg == NULL; returns major:minor (16:16). */ +#define XENVER_version 0 + +/* arg == xen_extraversion_t. */ +#define XENVER_extraversion 1 +typedef char xen_extraversion_t[16]; +#define XEN_EXTRAVERSION_LEN (sizeof(xen_extraversion_t)) + +/* arg == xen_compile_info_t. */ +#define XENVER_compile_info 2 +struct xen_compile_info { + char compiler[64]; + char compile_by[16]; + char compile_domain[32]; + char compile_date[32]; +}; +typedef struct xen_compile_info xen_compile_info_t; + +#define XENVER_capabilities 3 +typedef char xen_capabilities_info_t[1024]; +#define XEN_CAPABILITIES_INFO_LEN (sizeof(xen_capabilities_info_t)) + +#define XENVER_changeset 4 +typedef char xen_changeset_info_t[64]; +#define XEN_CHANGESET_INFO_LEN (sizeof(xen_changeset_info_t)) + +#define XENVER_platform_parameters 5 +struct xen_platform_parameters { + xen_ulong_t virt_start; +}; +typedef struct xen_platform_parameters xen_platform_parameters_t; + +#define XENVER_get_features 6 +struct xen_feature_info { + unsigned int submap_idx; /* IN: which 32-bit submap to return */ + uint32_t submap; /* OUT: 32-bit submap */ +}; +typedef struct xen_feature_info xen_feature_info_t; + +/* Declares the features reported by XENVER_get_features. */ +#include "features.h" + +/* arg == NULL; returns host memory page size. */ +#define XENVER_pagesize 7 + +/* arg == xen_domain_handle_t. + * + * The toolstack fills it out for guest consumption. It is intended to hold + * the UUID of the guest. + */ +#define XENVER_guest_handle 8 + +#define XENVER_commandline 9 +typedef char xen_commandline_t[1024]; + +/* + * Return value is the number of bytes written, or XEN_Exx on error. + * Calling with empty parameter returns the size of build_id. + */ +#define XENVER_build_id 10 +struct xen_build_id { + uint32_t len; /* IN: size of buf[]. */ + unsigned char buf[XEN_FLEX_ARRAY_DIM]; + /* OUT: Variable length buffer with build_id. */ +}; +typedef struct xen_build_id xen_build_id_t; + +#endif /* __XEN_PUBLIC_VERSION_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/interface/xen-compat.h b/include/hw/xen/interface/xen-compat.h new file mode 100644 index 0000000000..97fe698498 --- /dev/null +++ b/include/hw/xen/interface/xen-compat.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: MIT */ +/****************************************************************************** + * xen-compat.h + * + * Guest OS interface to Xen. Compatibility layer. + * + * Copyright (c) 2006, Christian Limpach + */ + +#ifndef __XEN_PUBLIC_XEN_COMPAT_H__ +#define __XEN_PUBLIC_XEN_COMPAT_H__ + +#define __XEN_LATEST_INTERFACE_VERSION__ 0x00040e00 + +#if defined(__XEN__) || defined(__XEN_TOOLS__) +/* Xen is built with matching headers and implements the latest interface. */ +#define __XEN_INTERFACE_VERSION__ __XEN_LATEST_INTERFACE_VERSION__ +#elif !defined(__XEN_INTERFACE_VERSION__) +/* Guests which do not specify a version get the legacy interface. */ +#define __XEN_INTERFACE_VERSION__ 0x00000000 +#endif + +#if __XEN_INTERFACE_VERSION__ > __XEN_LATEST_INTERFACE_VERSION__ +#error "These header files do not support the requested interface version." +#endif + +#define COMPAT_FLEX_ARRAY_DIM XEN_FLEX_ARRAY_DIM + +#endif /* __XEN_PUBLIC_XEN_COMPAT_H__ */ diff --git a/include/hw/xen/interface/xen.h b/include/hw/xen/interface/xen.h new file mode 100644 index 0000000000..920567e006 --- /dev/null +++ b/include/hw/xen/interface/xen.h @@ -0,0 +1,1032 @@ +/* SPDX-License-Identifier: MIT */ +/****************************************************************************** + * xen.h + * + * Guest OS interface to Xen. + * + * Copyright (c) 2004, K A Fraser + */ + +#ifndef __XEN_PUBLIC_XEN_H__ +#define __XEN_PUBLIC_XEN_H__ + +#include "xen-compat.h" + +#if defined(__i386__) || defined(__x86_64__) +#include "arch-x86/xen.h" +#elif defined(__arm__) || defined (__aarch64__) +#include "arch-arm.h" +#else +#error "Unsupported architecture" +#endif + +#ifndef __ASSEMBLY__ +/* Guest handles for primitive C types. */ +DEFINE_XEN_GUEST_HANDLE(char); +__DEFINE_XEN_GUEST_HANDLE(uchar, unsigned char); +DEFINE_XEN_GUEST_HANDLE(int); +__DEFINE_XEN_GUEST_HANDLE(uint, unsigned int); +#if __XEN_INTERFACE_VERSION__ < 0x00040300 +DEFINE_XEN_GUEST_HANDLE(long); +__DEFINE_XEN_GUEST_HANDLE(ulong, unsigned long); +#endif +DEFINE_XEN_GUEST_HANDLE(void); + +DEFINE_XEN_GUEST_HANDLE(uint64_t); +DEFINE_XEN_GUEST_HANDLE(xen_pfn_t); +DEFINE_XEN_GUEST_HANDLE(xen_ulong_t); + +/* Define a variable length array (depends on compiler). */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define XEN_FLEX_ARRAY_DIM +#elif defined(__GNUC__) +#define XEN_FLEX_ARRAY_DIM 0 +#else +#define XEN_FLEX_ARRAY_DIM 1 /* variable size */ +#endif + +/* Turn a plain number into a C unsigned (long (long)) constant. */ +#define __xen_mk_uint(x) x ## U +#define __xen_mk_ulong(x) x ## UL +#ifndef __xen_mk_ullong +# define __xen_mk_ullong(x) x ## ULL +#endif +#define xen_mk_uint(x) __xen_mk_uint(x) +#define xen_mk_ulong(x) __xen_mk_ulong(x) +#define xen_mk_ullong(x) __xen_mk_ullong(x) + +#else + +/* In assembly code we cannot use C numeric constant suffixes. */ +#define xen_mk_uint(x) x +#define xen_mk_ulong(x) x +#define xen_mk_ullong(x) x + +#endif + +/* + * HYPERCALLS + */ + +/* `incontents 100 hcalls List of hypercalls + * ` enum hypercall_num { // __HYPERVISOR_* => HYPERVISOR_*() + */ + +#define __HYPERVISOR_set_trap_table 0 +#define __HYPERVISOR_mmu_update 1 +#define __HYPERVISOR_set_gdt 2 +#define __HYPERVISOR_stack_switch 3 +#define __HYPERVISOR_set_callbacks 4 +#define __HYPERVISOR_fpu_taskswitch 5 +#define __HYPERVISOR_sched_op_compat 6 /* compat since 0x00030101 */ +#define __HYPERVISOR_platform_op 7 +#define __HYPERVISOR_set_debugreg 8 +#define __HYPERVISOR_get_debugreg 9 +#define __HYPERVISOR_update_descriptor 10 +#define __HYPERVISOR_memory_op 12 +#define __HYPERVISOR_multicall 13 +#define __HYPERVISOR_update_va_mapping 14 +#define __HYPERVISOR_set_timer_op 15 +#define __HYPERVISOR_event_channel_op_compat 16 /* compat since 0x00030202 */ +#define __HYPERVISOR_xen_version 17 +#define __HYPERVISOR_console_io 18 +#define __HYPERVISOR_physdev_op_compat 19 /* compat since 0x00030202 */ +#define __HYPERVISOR_grant_table_op 20 +#define __HYPERVISOR_vm_assist 21 +#define __HYPERVISOR_update_va_mapping_otherdomain 22 +#define __HYPERVISOR_iret 23 /* x86 only */ +#define __HYPERVISOR_vcpu_op 24 +#define __HYPERVISOR_set_segment_base 25 /* x86/64 only */ +#define __HYPERVISOR_mmuext_op 26 +#define __HYPERVISOR_xsm_op 27 +#define __HYPERVISOR_nmi_op 28 +#define __HYPERVISOR_sched_op 29 +#define __HYPERVISOR_callback_op 30 +#define __HYPERVISOR_xenoprof_op 31 +#define __HYPERVISOR_event_channel_op 32 +#define __HYPERVISOR_physdev_op 33 +#define __HYPERVISOR_hvm_op 34 +#define __HYPERVISOR_sysctl 35 +#define __HYPERVISOR_domctl 36 +#define __HYPERVISOR_kexec_op 37 +#define __HYPERVISOR_tmem_op 38 +#define __HYPERVISOR_argo_op 39 +#define __HYPERVISOR_xenpmu_op 40 +#define __HYPERVISOR_dm_op 41 +#define __HYPERVISOR_hypfs_op 42 + +/* Architecture-specific hypercall definitions. */ +#define __HYPERVISOR_arch_0 48 +#define __HYPERVISOR_arch_1 49 +#define __HYPERVISOR_arch_2 50 +#define __HYPERVISOR_arch_3 51 +#define __HYPERVISOR_arch_4 52 +#define __HYPERVISOR_arch_5 53 +#define __HYPERVISOR_arch_6 54 +#define __HYPERVISOR_arch_7 55 + +/* ` } */ + +/* + * HYPERCALL COMPATIBILITY. + */ + +/* New sched_op hypercall introduced in 0x00030101. */ +#if __XEN_INTERFACE_VERSION__ < 0x00030101 +#undef __HYPERVISOR_sched_op +#define __HYPERVISOR_sched_op __HYPERVISOR_sched_op_compat +#endif + +/* New event-channel and physdev hypercalls introduced in 0x00030202. */ +#if __XEN_INTERFACE_VERSION__ < 0x00030202 +#undef __HYPERVISOR_event_channel_op +#define __HYPERVISOR_event_channel_op __HYPERVISOR_event_channel_op_compat +#undef __HYPERVISOR_physdev_op +#define __HYPERVISOR_physdev_op __HYPERVISOR_physdev_op_compat +#endif + +/* New platform_op hypercall introduced in 0x00030204. */ +#if __XEN_INTERFACE_VERSION__ < 0x00030204 +#define __HYPERVISOR_dom0_op __HYPERVISOR_platform_op +#endif + +/* + * VIRTUAL INTERRUPTS + * + * Virtual interrupts that a guest OS may receive from Xen. + * + * In the side comments, 'V.' denotes a per-VCPU VIRQ while 'G.' denotes a + * global VIRQ. The former can be bound once per VCPU and cannot be re-bound. + * The latter can be allocated only once per guest: they must initially be + * allocated to VCPU0 but can subsequently be re-bound. + */ +/* ` enum virq { */ +#define VIRQ_TIMER 0 /* V. Timebase update, and/or requested timeout. */ +#define VIRQ_DEBUG 1 /* V. Request guest to dump debug info. */ +#define VIRQ_CONSOLE 2 /* G. (DOM0) Bytes received on emergency console. */ +#define VIRQ_DOM_EXC 3 /* G. (DOM0) Exceptional event for some domain. */ +#define VIRQ_TBUF 4 /* G. (DOM0) Trace buffer has records available. */ +#define VIRQ_DEBUGGER 6 /* G. (DOM0) A domain has paused for debugging. */ +#define VIRQ_XENOPROF 7 /* V. XenOprofile interrupt: new sample available */ +#define VIRQ_CON_RING 8 /* G. (DOM0) Bytes received on console */ +#define VIRQ_PCPU_STATE 9 /* G. (DOM0) PCPU state changed */ +#define VIRQ_MEM_EVENT 10 /* G. (DOM0) A memory event has occurred */ +#define VIRQ_ARGO 11 /* G. Argo interdomain message notification */ +#define VIRQ_ENOMEM 12 /* G. (DOM0) Low on heap memory */ +#define VIRQ_XENPMU 13 /* V. PMC interrupt */ + +/* Architecture-specific VIRQ definitions. */ +#define VIRQ_ARCH_0 16 +#define VIRQ_ARCH_1 17 +#define VIRQ_ARCH_2 18 +#define VIRQ_ARCH_3 19 +#define VIRQ_ARCH_4 20 +#define VIRQ_ARCH_5 21 +#define VIRQ_ARCH_6 22 +#define VIRQ_ARCH_7 23 +/* ` } */ + +#define NR_VIRQS 24 + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_mmu_update(const struct mmu_update reqs[], + * ` unsigned count, unsigned *done_out, + * ` unsigned foreigndom) + * ` + * @reqs is an array of mmu_update_t structures ((ptr, val) pairs). + * @count is the length of the above array. + * @pdone is an output parameter indicating number of completed operations + * @foreigndom[15:0]: FD, the expected owner of data pages referenced in this + * hypercall invocation. Can be DOMID_SELF. + * @foreigndom[31:16]: PFD, the expected owner of pagetable pages referenced + * in this hypercall invocation. The value of this field + * (x) encodes the PFD as follows: + * x == 0 => PFD == DOMID_SELF + * x != 0 => PFD == x - 1 + * + * Sub-commands: ptr[1:0] specifies the appropriate MMU_* command. + * ------------- + * ptr[1:0] == MMU_NORMAL_PT_UPDATE: + * Updates an entry in a page table belonging to PFD. If updating an L1 table, + * and the new table entry is valid/present, the mapped frame must belong to + * FD. If attempting to map an I/O page then the caller assumes the privilege + * of the FD. + * FD == DOMID_IO: Permit /only/ I/O mappings, at the priv level of the caller. + * FD == DOMID_XEN: Map restricted areas of Xen's heap space. + * ptr[:2] -- Machine address of the page-table entry to modify. + * val -- Value to write. + * + * There also certain implicit requirements when using this hypercall. The + * pages that make up a pagetable must be mapped read-only in the guest. + * This prevents uncontrolled guest updates to the pagetable. Xen strictly + * enforces this, and will disallow any pagetable update which will end up + * mapping pagetable page RW, and will disallow using any writable page as a + * pagetable. In practice it means that when constructing a page table for a + * process, thread, etc, we MUST be very dilligient in following these rules: + * 1). Start with top-level page (PGD or in Xen language: L4). Fill out + * the entries. + * 2). Keep on going, filling out the upper (PUD or L3), and middle (PMD + * or L2). + * 3). Start filling out the PTE table (L1) with the PTE entries. Once + * done, make sure to set each of those entries to RO (so writeable bit + * is unset). Once that has been completed, set the PMD (L2) for this + * PTE table as RO. + * 4). When completed with all of the PMD (L2) entries, and all of them have + * been set to RO, make sure to set RO the PUD (L3). Do the same + * operation on PGD (L4) pagetable entries that have a PUD (L3) entry. + * 5). Now before you can use those pages (so setting the cr3), you MUST also + * pin them so that the hypervisor can verify the entries. This is done + * via the HYPERVISOR_mmuext_op(MMUEXT_PIN_L4_TABLE, guest physical frame + * number of the PGD (L4)). And this point the HYPERVISOR_mmuext_op( + * MMUEXT_NEW_BASEPTR, guest physical frame number of the PGD (L4)) can be + * issued. + * For 32-bit guests, the L4 is not used (as there is less pagetables), so + * instead use L3. + * At this point the pagetables can be modified using the MMU_NORMAL_PT_UPDATE + * hypercall. Also if so desired the OS can also try to write to the PTE + * and be trapped by the hypervisor (as the PTE entry is RO). + * + * To deallocate the pages, the operations are the reverse of the steps + * mentioned above. The argument is MMUEXT_UNPIN_TABLE for all levels and the + * pagetable MUST not be in use (meaning that the cr3 is not set to it). + * + * ptr[1:0] == MMU_MACHPHYS_UPDATE: + * Updates an entry in the machine->pseudo-physical mapping table. + * ptr[:2] -- Machine address within the frame whose mapping to modify. + * The frame must belong to the FD, if one is specified. + * val -- Value to write into the mapping entry. + * + * ptr[1:0] == MMU_PT_UPDATE_PRESERVE_AD: + * As MMU_NORMAL_PT_UPDATE above, but A/D bits currently in the PTE are ORed + * with those in @val. + * + * ptr[1:0] == MMU_PT_UPDATE_NO_TRANSLATE: + * As MMU_NORMAL_PT_UPDATE above, but @val is not translated though FD + * page tables. + * + * @val is usually the machine frame number along with some attributes. + * The attributes by default follow the architecture defined bits. Meaning that + * if this is a X86_64 machine and four page table layout is used, the layout + * of val is: + * - 63 if set means No execute (NX) + * - 46-13 the machine frame number + * - 12 available for guest + * - 11 available for guest + * - 10 available for guest + * - 9 available for guest + * - 8 global + * - 7 PAT (PSE is disabled, must use hypercall to make 4MB or 2MB pages) + * - 6 dirty + * - 5 accessed + * - 4 page cached disabled + * - 3 page write through + * - 2 userspace accessible + * - 1 writeable + * - 0 present + * + * The one bits that does not fit with the default layout is the PAGE_PSE + * also called PAGE_PAT). The MMUEXT_[UN]MARK_SUPER arguments to the + * HYPERVISOR_mmuext_op serve as mechanism to set a pagetable to be 4MB + * (or 2MB) instead of using the PAGE_PSE bit. + * + * The reason that the PAGE_PSE (bit 7) is not being utilized is due to Xen + * using it as the Page Attribute Table (PAT) bit - for details on it please + * refer to Intel SDM 10.12. The PAT allows to set the caching attributes of + * pages instead of using MTRRs. + * + * The PAT MSR is as follows (it is a 64-bit value, each entry is 8 bits): + * PAT4 PAT0 + * +-----+-----+----+----+----+-----+----+----+ + * | UC | UC- | WC | WB | UC | UC- | WC | WB | <= Linux + * +-----+-----+----+----+----+-----+----+----+ + * | UC | UC- | WT | WB | UC | UC- | WT | WB | <= BIOS (default when machine boots) + * +-----+-----+----+----+----+-----+----+----+ + * | rsv | rsv | WP | WC | UC | UC- | WT | WB | <= Xen + * +-----+-----+----+----+----+-----+----+----+ + * + * The lookup of this index table translates to looking up + * Bit 7, Bit 4, and Bit 3 of val entry: + * + * PAT/PSE (bit 7) ... PCD (bit 4) .. PWT (bit 3). + * + * If all bits are off, then we are using PAT0. If bit 3 turned on, + * then we are using PAT1, if bit 3 and bit 4, then PAT2.. + * + * As you can see, the Linux PAT1 translates to PAT4 under Xen. Which means + * that if a guest that follows Linux's PAT setup and would like to set Write + * Combined on pages it MUST use PAT4 entry. Meaning that Bit 7 (PAGE_PAT) is + * set. For example, under Linux it only uses PAT0, PAT1, and PAT2 for the + * caching as: + * + * WB = none (so PAT0) + * WC = PWT (bit 3 on) + * UC = PWT | PCD (bit 3 and 4 are on). + * + * To make it work with Xen, it needs to translate the WC bit as so: + * + * PWT (so bit 3 on) --> PAT (so bit 7 is on) and clear bit 3 + * + * And to translate back it would: + * + * PAT (bit 7 on) --> PWT (bit 3 on) and clear bit 7. + */ +#define MMU_NORMAL_PT_UPDATE 0 /* checked '*ptr = val'. ptr is MA. */ +#define MMU_MACHPHYS_UPDATE 1 /* ptr = MA of frame to modify entry for */ +#define MMU_PT_UPDATE_PRESERVE_AD 2 /* atomically: *ptr = val | (*ptr&(A|D)) */ +#define MMU_PT_UPDATE_NO_TRANSLATE 3 /* checked '*ptr = val'. ptr is MA. */ + /* val never translated. */ + +/* + * MMU EXTENDED OPERATIONS + * + * ` enum neg_errnoval + * ` HYPERVISOR_mmuext_op(mmuext_op_t uops[], + * ` unsigned int count, + * ` unsigned int *pdone, + * ` unsigned int foreigndom) + */ +/* HYPERVISOR_mmuext_op() accepts a list of mmuext_op structures. + * A foreigndom (FD) can be specified (or DOMID_SELF for none). + * Where the FD has some effect, it is described below. + * + * cmd: MMUEXT_(UN)PIN_*_TABLE + * mfn: Machine frame number to be (un)pinned as a p.t. page. + * The frame must belong to the FD, if one is specified. + * + * cmd: MMUEXT_NEW_BASEPTR + * mfn: Machine frame number of new page-table base to install in MMU. + * + * cmd: MMUEXT_NEW_USER_BASEPTR [x86/64 only] + * mfn: Machine frame number of new page-table base to install in MMU + * when in user space. + * + * cmd: MMUEXT_TLB_FLUSH_LOCAL + * No additional arguments. Flushes local TLB. + * + * cmd: MMUEXT_INVLPG_LOCAL + * linear_addr: Linear address to be flushed from the local TLB. + * + * cmd: MMUEXT_TLB_FLUSH_MULTI + * vcpumask: Pointer to bitmap of VCPUs to be flushed. + * + * cmd: MMUEXT_INVLPG_MULTI + * linear_addr: Linear address to be flushed. + * vcpumask: Pointer to bitmap of VCPUs to be flushed. + * + * cmd: MMUEXT_TLB_FLUSH_ALL + * No additional arguments. Flushes all VCPUs' TLBs. + * + * cmd: MMUEXT_INVLPG_ALL + * linear_addr: Linear address to be flushed from all VCPUs' TLBs. + * + * cmd: MMUEXT_FLUSH_CACHE + * No additional arguments. Writes back and flushes cache contents. + * + * cmd: MMUEXT_FLUSH_CACHE_GLOBAL + * No additional arguments. Writes back and flushes cache contents + * on all CPUs in the system. + * + * cmd: MMUEXT_SET_LDT + * linear_addr: Linear address of LDT base (NB. must be page-aligned). + * nr_ents: Number of entries in LDT. + * + * cmd: MMUEXT_CLEAR_PAGE + * mfn: Machine frame number to be cleared. + * + * cmd: MMUEXT_COPY_PAGE + * mfn: Machine frame number of the destination page. + * src_mfn: Machine frame number of the source page. + * + * cmd: MMUEXT_[UN]MARK_SUPER + * mfn: Machine frame number of head of superpage to be [un]marked. + */ +/* ` enum mmuext_cmd { */ +#define MMUEXT_PIN_L1_TABLE 0 +#define MMUEXT_PIN_L2_TABLE 1 +#define MMUEXT_PIN_L3_TABLE 2 +#define MMUEXT_PIN_L4_TABLE 3 +#define MMUEXT_UNPIN_TABLE 4 +#define MMUEXT_NEW_BASEPTR 5 +#define MMUEXT_TLB_FLUSH_LOCAL 6 +#define MMUEXT_INVLPG_LOCAL 7 +#define MMUEXT_TLB_FLUSH_MULTI 8 +#define MMUEXT_INVLPG_MULTI 9 +#define MMUEXT_TLB_FLUSH_ALL 10 +#define MMUEXT_INVLPG_ALL 11 +#define MMUEXT_FLUSH_CACHE 12 +#define MMUEXT_SET_LDT 13 +#define MMUEXT_NEW_USER_BASEPTR 15 +#define MMUEXT_CLEAR_PAGE 16 +#define MMUEXT_COPY_PAGE 17 +#define MMUEXT_FLUSH_CACHE_GLOBAL 18 +#define MMUEXT_MARK_SUPER 19 +#define MMUEXT_UNMARK_SUPER 20 +/* ` } */ + +#ifndef __ASSEMBLY__ +struct mmuext_op { + unsigned int cmd; /* => enum mmuext_cmd */ + union { + /* [UN]PIN_TABLE, NEW_BASEPTR, NEW_USER_BASEPTR + * CLEAR_PAGE, COPY_PAGE, [UN]MARK_SUPER */ + xen_pfn_t mfn; + /* INVLPG_LOCAL, INVLPG_ALL, SET_LDT */ + unsigned long linear_addr; + } arg1; + union { + /* SET_LDT */ + unsigned int nr_ents; + /* TLB_FLUSH_MULTI, INVLPG_MULTI */ +#if __XEN_INTERFACE_VERSION__ >= 0x00030205 + XEN_GUEST_HANDLE(const_void) vcpumask; +#else + const void *vcpumask; +#endif + /* COPY_PAGE */ + xen_pfn_t src_mfn; + } arg2; +}; +typedef struct mmuext_op mmuext_op_t; +DEFINE_XEN_GUEST_HANDLE(mmuext_op_t); +#endif + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_update_va_mapping(unsigned long va, u64 val, + * ` enum uvm_flags flags) + * ` + * ` enum neg_errnoval + * ` HYPERVISOR_update_va_mapping_otherdomain(unsigned long va, u64 val, + * ` enum uvm_flags flags, + * ` domid_t domid) + * ` + * ` @va: The virtual address whose mapping we want to change + * ` @val: The new page table entry, must contain a machine address + * ` @flags: Control TLB flushes + */ +/* These are passed as 'flags' to update_va_mapping. They can be ORed. */ +/* When specifying UVMF_MULTI, also OR in a pointer to a CPU bitmap. */ +/* UVMF_LOCAL is merely UVMF_MULTI with a NULL bitmap pointer. */ +/* ` enum uvm_flags { */ +#define UVMF_NONE (xen_mk_ulong(0)<<0) /* No flushing at all. */ +#define UVMF_TLB_FLUSH (xen_mk_ulong(1)<<0) /* Flush entire TLB(s). */ +#define UVMF_INVLPG (xen_mk_ulong(2)<<0) /* Flush only one entry. */ +#define UVMF_FLUSHTYPE_MASK (xen_mk_ulong(3)<<0) +#define UVMF_MULTI (xen_mk_ulong(0)<<2) /* Flush subset of TLBs. */ +#define UVMF_LOCAL (xen_mk_ulong(0)<<2) /* Flush local TLB. */ +#define UVMF_ALL (xen_mk_ulong(1)<<2) /* Flush all TLBs. */ +/* ` } */ + +/* + * ` int + * ` HYPERVISOR_console_io(unsigned int cmd, + * ` unsigned int count, + * ` char buffer[]); + * + * @cmd: Command (see below) + * @count: Size of the buffer to read/write + * @buffer: Pointer in the guest memory + * + * List of commands: + * + * * CONSOLEIO_write: Write the buffer to Xen console. + * For the hardware domain, all the characters in the buffer will + * be written. Characters will be printed directly to the console. + * For all the other domains, only the printable characters will be + * written. Characters may be buffered until a newline (i.e '\n') is + * found. + * @return 0 on success, otherwise return an error code. + * * CONSOLEIO_read: Attempts to read up to @count characters from Xen + * console. The maximum buffer size (i.e. @count) supported is 2GB. + * @return the number of characters read on success, otherwise return + * an error code. + */ +#define CONSOLEIO_write 0 +#define CONSOLEIO_read 1 + +/* + * Commands to HYPERVISOR_vm_assist(). + */ +#define VMASST_CMD_enable 0 +#define VMASST_CMD_disable 1 + +/* x86/32 guests: simulate full 4GB segment limits. */ +#define VMASST_TYPE_4gb_segments 0 + +/* x86/32 guests: trap (vector 15) whenever above vmassist is used. */ +#define VMASST_TYPE_4gb_segments_notify 1 + +/* + * x86 guests: support writes to bottom-level PTEs. + * NB1. Page-directory entries cannot be written. + * NB2. Guest must continue to remove all writable mappings of PTEs. + */ +#define VMASST_TYPE_writable_pagetables 2 + +/* x86/PAE guests: support PDPTs above 4GB. */ +#define VMASST_TYPE_pae_extended_cr3 3 + +/* + * x86 guests: Sane behaviour for virtual iopl + * - virtual iopl updated from do_iret() hypercalls. + * - virtual iopl reported in bounce frames. + * - guest kernels assumed to be level 0 for the purpose of iopl checks. + */ +#define VMASST_TYPE_architectural_iopl 4 + +/* + * All guests: activate update indicator in vcpu_runstate_info + * Enable setting the XEN_RUNSTATE_UPDATE flag in guest memory mapped + * vcpu_runstate_info during updates of the runstate information. + */ +#define VMASST_TYPE_runstate_update_flag 5 + +/* + * x86/64 guests: strictly hide M2P from user mode. + * This allows the guest to control respective hypervisor behavior: + * - when not set, L4 tables get created with the respective slot blank, + * and whenever the L4 table gets used as a kernel one the missing + * mapping gets inserted, + * - when set, L4 tables get created with the respective slot initialized + * as before, and whenever the L4 table gets used as a user one the + * mapping gets zapped. + */ +#define VMASST_TYPE_m2p_strict 32 + +#if __XEN_INTERFACE_VERSION__ < 0x00040600 +#define MAX_VMASST_TYPE 3 +#endif + +/* Domain ids >= DOMID_FIRST_RESERVED cannot be used for ordinary domains. */ +#define DOMID_FIRST_RESERVED xen_mk_uint(0x7FF0) + +/* DOMID_SELF is used in certain contexts to refer to oneself. */ +#define DOMID_SELF xen_mk_uint(0x7FF0) + +/* + * DOMID_IO is used to restrict page-table updates to mapping I/O memory. + * Although no Foreign Domain need be specified to map I/O pages, DOMID_IO + * is useful to ensure that no mappings to the OS's own heap are accidentally + * installed. (e.g., in Linux this could cause havoc as reference counts + * aren't adjusted on the I/O-mapping code path). + * This only makes sense as HYPERVISOR_mmu_update()'s and + * HYPERVISOR_update_va_mapping_otherdomain()'s "foreigndom" argument. For + * HYPERVISOR_mmu_update() context it can be specified by any calling domain, + * otherwise it's only permitted if the caller is privileged. + */ +#define DOMID_IO xen_mk_uint(0x7FF1) + +/* + * DOMID_XEN is used to allow privileged domains to map restricted parts of + * Xen's heap space (e.g., the machine_to_phys table). + * This only makes sense as + * - HYPERVISOR_mmu_update()'s, HYPERVISOR_mmuext_op()'s, or + * HYPERVISOR_update_va_mapping_otherdomain()'s "foreigndom" argument, + * - with XENMAPSPACE_gmfn_foreign, + * and is only permitted if the caller is privileged. + */ +#define DOMID_XEN xen_mk_uint(0x7FF2) + +/* + * DOMID_COW is used as the owner of sharable pages */ +#define DOMID_COW xen_mk_uint(0x7FF3) + +/* DOMID_INVALID is used to identify pages with unknown owner. */ +#define DOMID_INVALID xen_mk_uint(0x7FF4) + +/* Idle domain. */ +#define DOMID_IDLE xen_mk_uint(0x7FFF) + +/* Mask for valid domain id values */ +#define DOMID_MASK xen_mk_uint(0x7FFF) + +#ifndef __ASSEMBLY__ + +typedef uint16_t domid_t; + +/* + * Send an array of these to HYPERVISOR_mmu_update(). + * NB. The fields are natural pointer/address size for this architecture. + */ +struct mmu_update { + uint64_t ptr; /* Machine address of PTE. */ + uint64_t val; /* New contents of PTE. */ +}; +typedef struct mmu_update mmu_update_t; +DEFINE_XEN_GUEST_HANDLE(mmu_update_t); + +/* + * ` enum neg_errnoval + * ` HYPERVISOR_multicall(multicall_entry_t call_list[], + * ` uint32_t nr_calls); + * + * NB. The fields are logically the natural register size for this + * architecture. In cases where xen_ulong_t is larger than this then + * any unused bits in the upper portion must be zero. + */ +struct multicall_entry { + xen_ulong_t op, result; + xen_ulong_t args[6]; +}; +typedef struct multicall_entry multicall_entry_t; +DEFINE_XEN_GUEST_HANDLE(multicall_entry_t); + +#if __XEN_INTERFACE_VERSION__ < 0x00040400 +/* + * Event channel endpoints per domain (when using the 2-level ABI): + * 1024 if a long is 32 bits; 4096 if a long is 64 bits. + */ +#define NR_EVENT_CHANNELS EVTCHN_2L_NR_CHANNELS +#endif + +struct vcpu_time_info { + /* + * Updates to the following values are preceded and followed by an + * increment of 'version'. The guest can therefore detect updates by + * looking for changes to 'version'. If the least-significant bit of + * the version number is set then an update is in progress and the guest + * must wait to read a consistent set of values. + * The correct way to interact with the version number is similar to + * Linux's seqlock: see the implementations of read_seqbegin/read_seqretry. + */ + uint32_t version; + uint32_t pad0; + uint64_t tsc_timestamp; /* TSC at last update of time vals. */ + uint64_t system_time; /* Time, in nanosecs, since boot. */ + /* + * Current system time: + * system_time + + * ((((tsc - tsc_timestamp) << tsc_shift) * tsc_to_system_mul) >> 32) + * CPU frequency (Hz): + * ((10^9 << 32) / tsc_to_system_mul) >> tsc_shift + */ + uint32_t tsc_to_system_mul; + int8_t tsc_shift; +#if __XEN_INTERFACE_VERSION__ > 0x040600 + uint8_t flags; + uint8_t pad1[2]; +#else + int8_t pad1[3]; +#endif +}; /* 32 bytes */ +typedef struct vcpu_time_info vcpu_time_info_t; + +#define XEN_PVCLOCK_TSC_STABLE_BIT (1 << 0) +#define XEN_PVCLOCK_GUEST_STOPPED (1 << 1) + +struct vcpu_info { + /* + * 'evtchn_upcall_pending' is written non-zero by Xen to indicate + * a pending notification for a particular VCPU. It is then cleared + * by the guest OS /before/ checking for pending work, thus avoiding + * a set-and-check race. Note that the mask is only accessed by Xen + * on the CPU that is currently hosting the VCPU. This means that the + * pending and mask flags can be updated by the guest without special + * synchronisation (i.e., no need for the x86 LOCK prefix). + * This may seem suboptimal because if the pending flag is set by + * a different CPU then an IPI may be scheduled even when the mask + * is set. However, note: + * 1. The task of 'interrupt holdoff' is covered by the per-event- + * channel mask bits. A 'noisy' event that is continually being + * triggered can be masked at source at this very precise + * granularity. + * 2. The main purpose of the per-VCPU mask is therefore to restrict + * reentrant execution: whether for concurrency control, or to + * prevent unbounded stack usage. Whatever the purpose, we expect + * that the mask will be asserted only for short periods at a time, + * and so the likelihood of a 'spurious' IPI is suitably small. + * The mask is read before making an event upcall to the guest: a + * non-zero mask therefore guarantees that the VCPU will not receive + * an upcall activation. The mask is cleared when the VCPU requests + * to block: this avoids wakeup-waiting races. + */ + uint8_t evtchn_upcall_pending; +#ifdef XEN_HAVE_PV_UPCALL_MASK + uint8_t evtchn_upcall_mask; +#else /* XEN_HAVE_PV_UPCALL_MASK */ + uint8_t pad0; +#endif /* XEN_HAVE_PV_UPCALL_MASK */ + xen_ulong_t evtchn_pending_sel; + struct arch_vcpu_info arch; + vcpu_time_info_t time; +}; /* 64 bytes (x86) */ +#ifndef __XEN__ +typedef struct vcpu_info vcpu_info_t; +#endif + +/* + * `incontents 200 startofday_shared Start-of-day shared data structure + * Xen/kernel shared data -- pointer provided in start_info. + * + * This structure is defined to be both smaller than a page, and the + * only data on the shared page, but may vary in actual size even within + * compatible Xen versions; guests should not rely on the size + * of this structure remaining constant. + */ +struct shared_info { + struct vcpu_info vcpu_info[XEN_LEGACY_MAX_VCPUS]; + + /* + * A domain can create "event channels" on which it can send and receive + * asynchronous event notifications. There are three classes of event that + * are delivered by this mechanism: + * 1. Bi-directional inter- and intra-domain connections. Domains must + * arrange out-of-band to set up a connection (usually by allocating + * an unbound 'listener' port and avertising that via a storage service + * such as xenstore). + * 2. Physical interrupts. A domain with suitable hardware-access + * privileges can bind an event-channel port to a physical interrupt + * source. + * 3. Virtual interrupts ('events'). A domain can bind an event-channel + * port to a virtual interrupt source, such as the virtual-timer + * device or the emergency console. + * + * Event channels are addressed by a "port index". Each channel is + * associated with two bits of information: + * 1. PENDING -- notifies the domain that there is a pending notification + * to be processed. This bit is cleared by the guest. + * 2. MASK -- if this bit is clear then a 0->1 transition of PENDING + * will cause an asynchronous upcall to be scheduled. This bit is only + * updated by the guest. It is read-only within Xen. If a channel + * becomes pending while the channel is masked then the 'edge' is lost + * (i.e., when the channel is unmasked, the guest must manually handle + * pending notifications as no upcall will be scheduled by Xen). + * + * To expedite scanning of pending notifications, any 0->1 pending + * transition on an unmasked channel causes a corresponding bit in a + * per-vcpu selector word to be set. Each bit in the selector covers a + * 'C long' in the PENDING bitfield array. + */ + xen_ulong_t evtchn_pending[sizeof(xen_ulong_t) * 8]; + xen_ulong_t evtchn_mask[sizeof(xen_ulong_t) * 8]; + + /* + * Wallclock time: updated by control software or RTC emulation. + * Guests should base their gettimeofday() syscall on this + * wallclock-base value. + * The values of wc_sec and wc_nsec are offsets from the Unix epoch + * adjusted by the domain's 'time offset' (in seconds) as set either + * by XEN_DOMCTL_settimeoffset, or adjusted via a guest write to the + * emulated RTC. + */ + uint32_t wc_version; /* Version counter: see vcpu_time_info_t. */ + uint32_t wc_sec; + uint32_t wc_nsec; +#if !defined(__i386__) + uint32_t wc_sec_hi; +# define xen_wc_sec_hi wc_sec_hi +#elif !defined(__XEN__) && !defined(__XEN_TOOLS__) +# define xen_wc_sec_hi arch.wc_sec_hi +#endif + + struct arch_shared_info arch; + +}; +#ifndef __XEN__ +typedef struct shared_info shared_info_t; +#endif + +/* + * `incontents 200 startofday Start-of-day memory layout + * + * 1. The domain is started within contiguous virtual-memory region. + * 2. The contiguous region ends on an aligned 4MB boundary. + * 3. This the order of bootstrap elements in the initial virtual region: + * a. relocated kernel image + * b. initial ram disk [mod_start, mod_len] + * (may be omitted) + * c. list of allocated page frames [mfn_list, nr_pages] + * (unless relocated due to XEN_ELFNOTE_INIT_P2M) + * d. start_info_t structure [register rSI (x86)] + * in case of dom0 this page contains the console info, too + * e. unless dom0: xenstore ring page + * f. unless dom0: console ring page + * g. bootstrap page tables [pt_base and CR3 (x86)] + * h. bootstrap stack [register ESP (x86)] + * 4. Bootstrap elements are packed together, but each is 4kB-aligned. + * 5. The list of page frames forms a contiguous 'pseudo-physical' memory + * layout for the domain. In particular, the bootstrap virtual-memory + * region is a 1:1 mapping to the first section of the pseudo-physical map. + * 6. All bootstrap elements are mapped read-writable for the guest OS. The + * only exception is the bootstrap page table, which is mapped read-only. + * 7. There is guaranteed to be at least 512kB padding after the final + * bootstrap element. If necessary, the bootstrap virtual region is + * extended by an extra 4MB to ensure this. + * + * Note: Prior to 25833:bb85bbccb1c9. ("x86/32-on-64 adjust Dom0 initial page + * table layout") a bug caused the pt_base (3.g above) and cr3 to not point + * to the start of the guest page tables (it was offset by two pages). + * This only manifested itself on 32-on-64 dom0 kernels and not 32-on-64 domU + * or 64-bit kernels of any colour. The page tables for a 32-on-64 dom0 got + * allocated in the order: 'first L1','first L2', 'first L3', so the offset + * to the page table base is by two pages back. The initial domain if it is + * 32-bit and runs under a 64-bit hypervisor should _NOT_ use two of the + * pages preceding pt_base and mark them as reserved/unused. + */ +#ifdef XEN_HAVE_PV_GUEST_ENTRY +struct start_info { + /* THE FOLLOWING ARE FILLED IN BOTH ON INITIAL BOOT AND ON RESUME. */ + char magic[32]; /* "xen--". */ + unsigned long nr_pages; /* Total pages allocated to this domain. */ + unsigned long shared_info; /* MACHINE address of shared info struct. */ + uint32_t flags; /* SIF_xxx flags. */ + xen_pfn_t store_mfn; /* MACHINE page number of shared page. */ + uint32_t store_evtchn; /* Event channel for store communication. */ + union { + struct { + xen_pfn_t mfn; /* MACHINE page number of console page. */ + uint32_t evtchn; /* Event channel for console page. */ + } domU; + struct { + uint32_t info_off; /* Offset of console_info struct. */ + uint32_t info_size; /* Size of console_info struct from start.*/ + } dom0; + } console; + /* THE FOLLOWING ARE ONLY FILLED IN ON INITIAL BOOT (NOT RESUME). */ + unsigned long pt_base; /* VIRTUAL address of page directory. */ + unsigned long nr_pt_frames; /* Number of bootstrap p.t. frames. */ + unsigned long mfn_list; /* VIRTUAL address of page-frame list. */ + unsigned long mod_start; /* VIRTUAL address of pre-loaded module */ + /* (PFN of pre-loaded module if */ + /* SIF_MOD_START_PFN set in flags). */ + unsigned long mod_len; /* Size (bytes) of pre-loaded module. */ +#define MAX_GUEST_CMDLINE 1024 + int8_t cmd_line[MAX_GUEST_CMDLINE]; + /* The pfn range here covers both page table and p->m table frames. */ + unsigned long first_p2m_pfn;/* 1st pfn forming initial P->M table. */ + unsigned long nr_p2m_frames;/* # of pfns forming initial P->M table. */ +}; +typedef struct start_info start_info_t; + +/* New console union for dom0 introduced in 0x00030203. */ +#if __XEN_INTERFACE_VERSION__ < 0x00030203 +#define console_mfn console.domU.mfn +#define console_evtchn console.domU.evtchn +#endif +#endif /* XEN_HAVE_PV_GUEST_ENTRY */ + +/* These flags are passed in the 'flags' field of start_info_t. */ +#define SIF_PRIVILEGED (1<<0) /* Is the domain privileged? */ +#define SIF_INITDOMAIN (1<<1) /* Is this the initial control domain? */ +#define SIF_MULTIBOOT_MOD (1<<2) /* Is mod_start a multiboot module? */ +#define SIF_MOD_START_PFN (1<<3) /* Is mod_start a PFN? */ +#define SIF_VIRT_P2M_4TOOLS (1<<4) /* Do Xen tools understand a virt. mapped */ + /* P->M making the 3 level tree obsolete? */ +#define SIF_PM_MASK (0xFF<<8) /* reserve 1 byte for xen-pm options */ + +/* + * A multiboot module is a package containing modules very similar to a + * multiboot module array. The only differences are: + * - the array of module descriptors is by convention simply at the beginning + * of the multiboot module, + * - addresses in the module descriptors are based on the beginning of the + * multiboot module, + * - the number of modules is determined by a termination descriptor that has + * mod_start == 0. + * + * This permits to both build it statically and reference it in a configuration + * file, and let the PV guest easily rebase the addresses to virtual addresses + * and at the same time count the number of modules. + */ +struct xen_multiboot_mod_list +{ + /* Address of first byte of the module */ + uint32_t mod_start; + /* Address of last byte of the module (inclusive) */ + uint32_t mod_end; + /* Address of zero-terminated command line */ + uint32_t cmdline; + /* Unused, must be zero */ + uint32_t pad; +}; +/* + * `incontents 200 startofday_dom0_console Dom0_console + * + * The console structure in start_info.console.dom0 + * + * This structure includes a variety of information required to + * have a working VGA/VESA console. + */ +typedef struct dom0_vga_console_info { + uint8_t video_type; /* DOM0_VGA_CONSOLE_??? */ +#define XEN_VGATYPE_TEXT_MODE_3 0x03 +#define XEN_VGATYPE_VESA_LFB 0x23 +#define XEN_VGATYPE_EFI_LFB 0x70 + + union { + struct { + /* Font height, in pixels. */ + uint16_t font_height; + /* Cursor location (column, row). */ + uint16_t cursor_x, cursor_y; + /* Number of rows and columns (dimensions in characters). */ + uint16_t rows, columns; + } text_mode_3; + + struct { + /* Width and height, in pixels. */ + uint16_t width, height; + /* Bytes per scan line. */ + uint16_t bytes_per_line; + /* Bits per pixel. */ + uint16_t bits_per_pixel; + /* LFB physical address, and size (in units of 64kB). */ + uint32_t lfb_base; + uint32_t lfb_size; + /* RGB mask offsets and sizes, as defined by VBE 1.2+ */ + uint8_t red_pos, red_size; + uint8_t green_pos, green_size; + uint8_t blue_pos, blue_size; + uint8_t rsvd_pos, rsvd_size; +#if __XEN_INTERFACE_VERSION__ >= 0x00030206 + /* VESA capabilities (offset 0xa, VESA command 0x4f00). */ + uint32_t gbl_caps; + /* Mode attributes (offset 0x0, VESA command 0x4f01). */ + uint16_t mode_attrs; + uint16_t pad; +#endif +#if __XEN_INTERFACE_VERSION__ >= 0x00040d00 + /* high 32 bits of lfb_base */ + uint32_t ext_lfb_base; +#endif + } vesa_lfb; + } u; +} dom0_vga_console_info_t; +#define xen_vga_console_info dom0_vga_console_info +#define xen_vga_console_info_t dom0_vga_console_info_t + +typedef uint8_t xen_domain_handle_t[16]; + +__DEFINE_XEN_GUEST_HANDLE(uint8, uint8_t); +__DEFINE_XEN_GUEST_HANDLE(uint16, uint16_t); +__DEFINE_XEN_GUEST_HANDLE(uint32, uint32_t); +__DEFINE_XEN_GUEST_HANDLE(uint64, uint64_t); + +typedef struct { + uint8_t a[16]; +} xen_uuid_t; + +/* + * XEN_DEFINE_UUID(0x00112233, 0x4455, 0x6677, 0x8899, + * 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff) + * will construct UUID 00112233-4455-6677-8899-aabbccddeeff presented as + * {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + * 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + * + * NB: This is compatible with Linux kernel and with libuuid, but it is not + * compatible with Microsoft, as they use mixed-endian encoding (some + * components are little-endian, some are big-endian). + */ +#define XEN_DEFINE_UUID_(a, b, c, d, e1, e2, e3, e4, e5, e6) \ + {{((a) >> 24) & 0xFF, ((a) >> 16) & 0xFF, \ + ((a) >> 8) & 0xFF, ((a) >> 0) & 0xFF, \ + ((b) >> 8) & 0xFF, ((b) >> 0) & 0xFF, \ + ((c) >> 8) & 0xFF, ((c) >> 0) & 0xFF, \ + ((d) >> 8) & 0xFF, ((d) >> 0) & 0xFF, \ + e1, e2, e3, e4, e5, e6}} + +#if defined(__STDC_VERSION__) ? __STDC_VERSION__ >= 199901L : defined(__GNUC__) +#define XEN_DEFINE_UUID(a, b, c, d, e1, e2, e3, e4, e5, e6) \ + ((xen_uuid_t)XEN_DEFINE_UUID_(a, b, c, d, e1, e2, e3, e4, e5, e6)) +#else +#define XEN_DEFINE_UUID(a, b, c, d, e1, e2, e3, e4, e5, e6) \ + XEN_DEFINE_UUID_(a, b, c, d, e1, e2, e3, e4, e5, e6) +#endif /* __STDC_VERSION__ / __GNUC__ */ + +#endif /* !__ASSEMBLY__ */ + +/* Default definitions for macros used by domctl/sysctl. */ +#if defined(__XEN__) || defined(__XEN_TOOLS__) + +#ifndef int64_aligned_t +#define int64_aligned_t int64_t +#endif +#ifndef uint64_aligned_t +#define uint64_aligned_t uint64_t +#endif +#ifndef XEN_GUEST_HANDLE_64 +#define XEN_GUEST_HANDLE_64(name) XEN_GUEST_HANDLE(name) +#endif + +#ifndef __ASSEMBLY__ +struct xenctl_bitmap { + XEN_GUEST_HANDLE_64(uint8) bitmap; + uint32_t nr_bits; +}; +typedef struct xenctl_bitmap xenctl_bitmap_t; +#endif + +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ + +#endif /* __XEN_PUBLIC_XEN_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/hw/xen/xen-backend.h b/include/hw/xen/xen-backend.h index aac2fd454d..0f01631ae7 100644 --- a/include/hw/xen/xen-backend.h +++ b/include/hw/xen/xen-backend.h @@ -33,6 +33,7 @@ XenDevice *xen_backend_get_device(XenBackendInstance *backend); void xen_backend_register(const XenBackendInfo *info); const char **xen_backend_get_types(unsigned int *nr); +bool xen_backend_exists(const char *type, const char *name); void xen_backend_device_create(XenBus *xenbus, const char *type, const char *name, QDict *opts, Error **errp); bool xen_backend_try_device_destroy(XenDevice *xendev, Error **errp); diff --git a/include/hw/xen/xen-bus-helper.h b/include/hw/xen/xen-bus-helper.h index 629a904d1a..d8dcc2f010 100644 --- a/include/hw/xen/xen-bus-helper.h +++ b/include/hw/xen/xen-bus-helper.h @@ -8,38 +8,40 @@ #ifndef HW_XEN_BUS_HELPER_H #define HW_XEN_BUS_HELPER_H -#include "hw/xen/xen_common.h" +#include "hw/xen/xen_backend_ops.h" const char *xs_strstate(enum xenbus_state state); -void xs_node_create(struct xs_handle *xsh, xs_transaction_t tid, - const char *node, struct xs_permissions perms[], - unsigned int nr_perms, Error **errp); -void xs_node_destroy(struct xs_handle *xsh, xs_transaction_t tid, +void xs_node_create(struct qemu_xs_handle *h, xs_transaction_t tid, + const char *node, unsigned int owner, unsigned int domid, + unsigned int perms, Error **errp); +void xs_node_destroy(struct qemu_xs_handle *h, xs_transaction_t tid, const char *node, Error **errp); /* Write to node/key unless node is empty, in which case write to key */ -void xs_node_vprintf(struct xs_handle *xsh, xs_transaction_t tid, +void xs_node_vprintf(struct qemu_xs_handle *h, xs_transaction_t tid, const char *node, const char *key, Error **errp, const char *fmt, va_list ap) G_GNUC_PRINTF(6, 0); -void xs_node_printf(struct xs_handle *xsh, xs_transaction_t tid, +void xs_node_printf(struct qemu_xs_handle *h, xs_transaction_t tid, const char *node, const char *key, Error **errp, const char *fmt, ...) G_GNUC_PRINTF(6, 7); /* Read from node/key unless node is empty, in which case read from key */ -int xs_node_vscanf(struct xs_handle *xsh, xs_transaction_t tid, +int xs_node_vscanf(struct qemu_xs_handle *h, xs_transaction_t tid, const char *node, const char *key, Error **errp, - const char *fmt, va_list ap); -int xs_node_scanf(struct xs_handle *xsh, xs_transaction_t tid, + const char *fmt, va_list ap) + G_GNUC_SCANF(6, 0); +int xs_node_scanf(struct qemu_xs_handle *h, xs_transaction_t tid, const char *node, const char *key, Error **errp, - const char *fmt, ...); + const char *fmt, ...) + G_GNUC_SCANF(6, 7); /* Watch node/key unless node is empty, in which case watch key */ -void xs_node_watch(struct xs_handle *xsh, const char *node, const char *key, - char *token, Error **errp); -void xs_node_unwatch(struct xs_handle *xsh, const char *node, const char *key, - const char *token, Error **errp); +struct qemu_xs_watch *xs_node_watch(struct qemu_xs_handle *h, const char *node, + const char *key, xs_watch_fn fn, + void *opaque, Error **errp); +void xs_node_unwatch(struct qemu_xs_handle *h, struct qemu_xs_watch *w); #endif /* HW_XEN_BUS_HELPER_H */ diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h index 713e763348..38d40afa37 100644 --- a/include/hw/xen/xen-bus.h +++ b/include/hw/xen/xen-bus.h @@ -8,37 +8,32 @@ #ifndef HW_XEN_BUS_H #define HW_XEN_BUS_H -#include "hw/xen/xen_common.h" +#include "hw/xen/xen_backend_ops.h" #include "hw/sysbus.h" #include "qemu/notify.h" #include "qom/object.h" -typedef void (*XenWatchHandler)(void *opaque); - -typedef struct XenWatchList XenWatchList; -typedef struct XenWatch XenWatch; typedef struct XenEventChannel XenEventChannel; struct XenDevice { DeviceState qdev; domid_t frontend_id; char *name; - struct xs_handle *xsh; - XenWatchList *watch_list; + struct qemu_xs_handle *xsh; char *backend_path, *frontend_path; enum xenbus_state backend_state, frontend_state; Notifier exit; - XenWatch *backend_state_watch, *frontend_state_watch; + struct qemu_xs_watch *backend_state_watch, *frontend_state_watch; bool backend_online; - XenWatch *backend_online_watch; + struct qemu_xs_watch *backend_online_watch; xengnttab_handle *xgth; - bool feature_grant_copy; bool inactive; QLIST_HEAD(, XenEventChannel) event_channels; QLIST_ENTRY(XenDevice) list; }; typedef struct XenDevice XenDevice; +typedef char *(*XenDeviceGetFrontendPath)(XenDevice *xendev, Error **errp); typedef char *(*XenDeviceGetName)(XenDevice *xendev, Error **errp); typedef void (*XenDeviceRealize)(XenDevice *xendev, Error **errp); typedef void (*XenDeviceFrontendChanged)(XenDevice *xendev, @@ -52,6 +47,7 @@ struct XenDeviceClass { /*< public >*/ const char *backend; const char *device; + XenDeviceGetFrontendPath get_frontend_path; XenDeviceGetName get_name; XenDeviceRealize realize; XenDeviceFrontendChanged frontend_changed; @@ -64,10 +60,9 @@ OBJECT_DECLARE_TYPE(XenDevice, XenDeviceClass, XEN_DEVICE) struct XenBus { BusState qbus; domid_t backend_id; - struct xs_handle *xsh; - XenWatchList *watch_list; + struct qemu_xs_handle *xsh; unsigned int backend_types; - XenWatch **backend_watch; + struct qemu_xs_watch **backend_watch; QLIST_HEAD(, XenDevice) inactive_devices; }; @@ -94,14 +89,15 @@ void xen_device_frontend_printf(XenDevice *xendev, const char *key, G_GNUC_PRINTF(3, 4); int xen_device_frontend_scanf(XenDevice *xendev, const char *key, - const char *fmt, ...); + const char *fmt, ...) + G_GNUC_SCANF(3, 4); void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs, Error **errp); void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs, unsigned int nr_refs, int prot, Error **errp); -void xen_device_unmap_grant_refs(XenDevice *xendev, void *map, +void xen_device_unmap_grant_refs(XenDevice *xendev, void *map, uint32_t *refs, unsigned int nr_refs, Error **errp); typedef struct XenDeviceGrantCopySegment { @@ -135,5 +131,6 @@ void xen_device_notify_event_channel(XenDevice *xendev, void xen_device_unbind_event_channel(XenDevice *xendev, XenEventChannel *channel, Error **errp); +unsigned int xen_event_channel_get_local_port(XenEventChannel *channel); #endif /* HW_XEN_BUS_H */ diff --git a/include/hw/xen/xen-hvm-common.h b/include/hw/xen/xen-hvm-common.h new file mode 100644 index 0000000000..65a51aac2e --- /dev/null +++ b/include/hw/xen/xen-hvm-common.h @@ -0,0 +1,98 @@ +#ifndef HW_XEN_HVM_COMMON_H +#define HW_XEN_HVM_COMMON_H + +#include "qemu/units.h" + +#include "cpu.h" +#include "hw/pci/pci.h" +#include "hw/hw.h" +#include "hw/xen/xen_native.h" +#include "hw/xen/xen-legacy-backend.h" +#include "sysemu/runstate.h" +#include "sysemu/sysemu.h" +#include "sysemu/xen.h" +#include "sysemu/xen-mapcache.h" +#include "qemu/error-report.h" +#include + +extern MemoryRegion xen_memory; +extern MemoryListener xen_io_listener; +extern DeviceListener xen_device_listener; + +//#define DEBUG_XEN_HVM + +#ifdef DEBUG_XEN_HVM +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, "xen: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +static inline uint32_t xen_vcpu_eport(shared_iopage_t *shared_page, int i) +{ + return shared_page->vcpu_ioreq[i].vp_eport; +} +static inline ioreq_t *xen_vcpu_ioreq(shared_iopage_t *shared_page, int vcpu) +{ + return &shared_page->vcpu_ioreq[vcpu]; +} + +#define BUFFER_IO_MAX_DELAY 100 + +typedef struct XenPhysmap { + hwaddr start_addr; + ram_addr_t size; + const char *name; + hwaddr phys_offset; + + QLIST_ENTRY(XenPhysmap) list; +} XenPhysmap; + +typedef struct XenPciDevice { + PCIDevice *pci_dev; + uint32_t sbdf; + QLIST_ENTRY(XenPciDevice) entry; +} XenPciDevice; + +typedef struct XenIOState { + ioservid_t ioservid; + shared_iopage_t *shared_page; + buffered_iopage_t *buffered_io_page; + xenforeignmemory_resource_handle *fres; + QEMUTimer *buffered_io_timer; + CPUState **cpu_by_vcpu_id; + /* the evtchn port for polling the notification, */ + evtchn_port_t *ioreq_local_port; + /* evtchn remote and local ports for buffered io */ + evtchn_port_t bufioreq_remote_port; + evtchn_port_t bufioreq_local_port; + /* the evtchn fd for polling */ + xenevtchn_handle *xce_handle; + /* which vcpu we are serving */ + int send_vcpu; + + struct xs_handle *xenstore; + MemoryListener memory_listener; + MemoryListener io_listener; + QLIST_HEAD(, XenPciDevice) dev_list; + DeviceListener device_listener; + + Notifier exit; +} XenIOState; + +void xen_exit_notifier(Notifier *n, void *data); + +void xen_region_add(MemoryListener *listener, MemoryRegionSection *section); +void xen_region_del(MemoryListener *listener, MemoryRegionSection *section); +void xen_io_add(MemoryListener *listener, MemoryRegionSection *section); +void xen_io_del(MemoryListener *listener, MemoryRegionSection *section); +void xen_device_realize(DeviceListener *listener, DeviceState *dev); +void xen_device_unrealize(DeviceListener *listener, DeviceState *dev); + +void xen_hvm_change_state_handler(void *opaque, bool running, RunState rstate); +void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, + const MemoryListener *xen_memory_listener); + +void cpu_ioreq_pio(ioreq_t *req); +#endif /* HW_XEN_HVM_COMMON_H */ diff --git a/include/hw/xen/xen-legacy-backend.h b/include/hw/xen/xen-legacy-backend.h index be281e1f38..2cca174778 100644 --- a/include/hw/xen/xen-legacy-backend.h +++ b/include/hw/xen/xen-legacy-backend.h @@ -1,7 +1,7 @@ #ifndef HW_XEN_LEGACY_BACKEND_H #define HW_XEN_LEGACY_BACKEND_H -#include "hw/xen/xen_common.h" +#include "hw/xen/xen_backend_ops.h" #include "hw/xen/xen_pvdev.h" #include "net/net.h" #include "qom/object.h" @@ -15,7 +15,7 @@ DECLARE_INSTANCE_CHECKER(XenLegacyDevice, XENBACKEND, TYPE_XENBACKEND) /* variables */ -extern struct xs_handle *xenstore; +extern struct qemu_xs_handle *xenstore; extern const char *xen_protocol; extern DeviceState *xen_sysdev; extern BusState *xen_sysbus; @@ -30,9 +30,6 @@ int xenstore_write_be_int64(struct XenLegacyDevice *xendev, const char *node, char *xenstore_read_be_str(struct XenLegacyDevice *xendev, const char *node); int xenstore_read_be_int(struct XenLegacyDevice *xendev, const char *node, int *ival); -void xenstore_update_fe(char *watch, struct XenLegacyDevice *xendev); -void xenstore_update_be(char *watch, char *type, int dom, - struct XenDevOps *ops); char *xenstore_read_fe_str(struct XenLegacyDevice *xendev, const char *node); int xenstore_read_fe_int(struct XenLegacyDevice *xendev, const char *node, int *ival); @@ -42,8 +39,7 @@ int xenstore_read_fe_uint64(struct XenLegacyDevice *xendev, const char *node, void xen_be_check_state(struct XenLegacyDevice *xendev); /* xen backend driver bits */ -int xen_be_init(void); -void xen_be_register_common(void); +void xen_be_init(void); int xen_be_register(const char *type, struct XenDevOps *ops); int xen_be_set_state(struct XenLegacyDevice *xendev, enum xenbus_state state); int xen_be_bind_evtchn(struct XenLegacyDevice *xendev); @@ -52,18 +48,7 @@ void xen_be_set_max_grant_refs(struct XenLegacyDevice *xendev, void *xen_be_map_grant_refs(struct XenLegacyDevice *xendev, uint32_t *refs, unsigned int nr_refs, int prot); void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr, - unsigned int nr_refs); - -typedef struct XenGrantCopySegment { - union { - void *virt; - struct { - uint32_t ref; - off_t offset; - } foreign; - } source, dest; - size_t len; -} XenGrantCopySegment; + uint32_t *refs, unsigned int nr_refs); int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev, bool to_domain, XenGrantCopySegment segs[], @@ -76,9 +61,9 @@ static inline void *xen_be_map_grant_ref(struct XenLegacyDevice *xendev, } static inline void xen_be_unmap_grant_ref(struct XenLegacyDevice *xendev, - void *ptr) + void *ptr, uint32_t ref) { - return xen_be_unmap_grant_refs(xendev, ptr, 1); + return xen_be_unmap_grant_refs(xendev, ptr, &ref, 1); } /* actual backend drivers */ @@ -96,8 +81,6 @@ extern struct XenDevOps xen_usb_ops; /* xen-usb.c */ /* configuration (aka xenbus setup) */ void xen_config_cleanup(void); -int xen_config_dev_blk(DriveInfo *disk); -int xen_config_dev_nic(NICInfo *nic); int xen_config_dev_vfb(int vdev, const char *type); int xen_config_dev_vkbd(int vdev); int xen_config_dev_console(int vdev); diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index 0f9962b1c1..37ecc91fc3 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -1,19 +1,36 @@ -#ifndef QEMU_HW_XEN_H -#define QEMU_HW_XEN_H - /* * public xen header * stuff needed outside xen-*.c, i.e. interfaces to qemu. * must not depend on any xen headers being present in * /usr/include/xen, so it can be included unconditionally. */ +#ifndef QEMU_HW_XEN_H +#define QEMU_HW_XEN_H + +/* + * C files using Xen toolstack libraries will have included those headers + * already via xen_native.h, and having __XEM_TOOLS__ defined will have + * automatically set __XEN_INTERFACE_VERSION__ to the latest supported + * by the *system* Xen headers which were transitively included. + * + * C files which are part of the internal emulation, and which did not + * include xen_native.h, may need this defined so that the Xen headers + * imported to include/hw/xen/interface/ will expose the appropriate API + * version. + * + * This is why there's a rule that xen_native.h must be included first. + */ +#ifndef __XEN_INTERFACE_VERSION__ +#define __XEN_INTERFACE_VERSION__ 0x00040e00 +#endif #include "exec/cpu-common.h" /* xen-machine.c */ enum xen_mode { - XEN_EMULATE = 0, // xen emulation, using xenner (default) - XEN_ATTACH // attach to xen domain created by libxl + XEN_DISABLED = 0, /* xen support disabled (default) */ + XEN_ATTACH, /* attach to xen domain created by libxl */ + XEN_EMULATE, /* emulate Xen within QEMU */ }; extern uint32_t xen_domid; @@ -21,15 +38,13 @@ extern enum xen_mode xen_mode; extern bool xen_domid_restrict; int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num); -void xen_piix3_set_irq(void *opaque, int irq_num, int level); -void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len); +int xen_set_pci_link_route(uint8_t link, uint8_t irq); +void xen_intx_set_irq(void *opaque, int irq_num, int level); void xen_hvm_inject_msi(uint64_t addr, uint32_t data); int xen_is_pirq_msi(uint32_t msi_data); qemu_irq *xen_interrupt_controller_init(void); -void xenstore_store_pv_console_info(int i, Chardev *chr); - void xen_register_framebuffer(struct MemoryRegion *mr); #endif /* QEMU_HW_XEN_H */ diff --git a/include/hw/xen/xen_backend_ops.h b/include/hw/xen/xen_backend_ops.h new file mode 100644 index 0000000000..90cca85f52 --- /dev/null +++ b/include/hw/xen/xen_backend_ops.h @@ -0,0 +1,408 @@ +/* + * QEMU Xen backend support + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Authors: David Woodhouse + * + * 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_XEN_BACKEND_OPS_H +#define QEMU_XEN_BACKEND_OPS_H + +#include "hw/xen/xen.h" +#include "hw/xen/interface/xen.h" +#include "hw/xen/interface/io/xenbus.h" + +/* + * For the time being, these operations map fairly closely to the API of + * the actual Xen libraries, e.g. libxenevtchn. As we complete the migration + * from XenLegacyDevice back ends to the new XenDevice model, they may + * evolve to slightly higher-level APIs. + * + * The internal emulations do not emulate the Xen APIs entirely faithfully; + * only enough to be used by the Xen backend devices. For example, only one + * event channel can be bound to each handle, since that's sufficient for + * the device support (only the true Xen HVM backend uses more). And the + * behaviour of unmask() and pending() is different too because the device + * backends don't care. + */ + +typedef struct xenevtchn_handle xenevtchn_handle; +typedef int xenevtchn_port_or_error_t; +typedef uint32_t evtchn_port_t; +typedef uint16_t domid_t; +typedef uint32_t grant_ref_t; + +#define XEN_PAGE_SHIFT 12 +#define XEN_PAGE_SIZE (1UL << XEN_PAGE_SHIFT) +#define XEN_PAGE_MASK (~(XEN_PAGE_SIZE - 1)) + +#ifndef xen_rmb +#define xen_rmb() smp_rmb() +#endif +#ifndef xen_wmb +#define xen_wmb() smp_wmb() +#endif +#ifndef xen_mb +#define xen_mb() smp_mb() +#endif + +struct evtchn_backend_ops { + xenevtchn_handle *(*open)(void); + int (*bind_interdomain)(xenevtchn_handle *xc, uint32_t domid, + evtchn_port_t guest_port); + int (*unbind)(xenevtchn_handle *xc, evtchn_port_t port); + int (*close)(struct xenevtchn_handle *xc); + int (*get_fd)(struct xenevtchn_handle *xc); + int (*notify)(struct xenevtchn_handle *xc, evtchn_port_t port); + int (*unmask)(struct xenevtchn_handle *xc, evtchn_port_t port); + int (*pending)(struct xenevtchn_handle *xc); +}; + +extern struct evtchn_backend_ops *xen_evtchn_ops; + +static inline xenevtchn_handle *qemu_xen_evtchn_open(void) +{ + if (!xen_evtchn_ops) { + return NULL; + } + return xen_evtchn_ops->open(); +} + +static inline int qemu_xen_evtchn_bind_interdomain(xenevtchn_handle *xc, + uint32_t domid, + evtchn_port_t guest_port) +{ + if (!xen_evtchn_ops) { + return -ENOSYS; + } + return xen_evtchn_ops->bind_interdomain(xc, domid, guest_port); +} + +static inline int qemu_xen_evtchn_unbind(xenevtchn_handle *xc, + evtchn_port_t port) +{ + if (!xen_evtchn_ops) { + return -ENOSYS; + } + return xen_evtchn_ops->unbind(xc, port); +} + +static inline int qemu_xen_evtchn_close(xenevtchn_handle *xc) +{ + if (!xen_evtchn_ops) { + return -ENOSYS; + } + return xen_evtchn_ops->close(xc); +} + +static inline int qemu_xen_evtchn_fd(xenevtchn_handle *xc) +{ + if (!xen_evtchn_ops) { + return -ENOSYS; + } + return xen_evtchn_ops->get_fd(xc); +} + +static inline int qemu_xen_evtchn_notify(xenevtchn_handle *xc, + evtchn_port_t port) +{ + if (!xen_evtchn_ops) { + return -ENOSYS; + } + return xen_evtchn_ops->notify(xc, port); +} + +static inline int qemu_xen_evtchn_unmask(xenevtchn_handle *xc, + evtchn_port_t port) +{ + if (!xen_evtchn_ops) { + return -ENOSYS; + } + return xen_evtchn_ops->unmask(xc, port); +} + +static inline int qemu_xen_evtchn_pending(xenevtchn_handle *xc) +{ + if (!xen_evtchn_ops) { + return -ENOSYS; + } + return xen_evtchn_ops->pending(xc); +} + +typedef struct xengntdev_handle xengnttab_handle; + +typedef struct XenGrantCopySegment { + union { + void *virt; + struct { + uint32_t ref; + off_t offset; + } foreign; + } source, dest; + size_t len; +} XenGrantCopySegment; + +#define XEN_GNTTAB_OP_FEATURE_MAP_MULTIPLE (1U << 0) + +struct gnttab_backend_ops { + uint32_t features; + xengnttab_handle *(*open)(void); + int (*close)(xengnttab_handle *xgt); + int (*grant_copy)(xengnttab_handle *xgt, bool to_domain, uint32_t domid, + XenGrantCopySegment *segs, uint32_t nr_segs, + Error **errp); + int (*set_max_grants)(xengnttab_handle *xgt, uint32_t nr_grants); + void *(*map_refs)(xengnttab_handle *xgt, uint32_t count, uint32_t domid, + uint32_t *refs, int prot); + int (*unmap)(xengnttab_handle *xgt, void *start_address, uint32_t *refs, + uint32_t count); +}; + +extern struct gnttab_backend_ops *xen_gnttab_ops; + +static inline bool qemu_xen_gnttab_can_map_multi(void) +{ + return xen_gnttab_ops && + !!(xen_gnttab_ops->features & XEN_GNTTAB_OP_FEATURE_MAP_MULTIPLE); +} + +static inline xengnttab_handle *qemu_xen_gnttab_open(void) +{ + if (!xen_gnttab_ops) { + return NULL; + } + return xen_gnttab_ops->open(); +} + +static inline int qemu_xen_gnttab_close(xengnttab_handle *xgt) +{ + if (!xen_gnttab_ops) { + return -ENOSYS; + } + return xen_gnttab_ops->close(xgt); +} + +static inline int qemu_xen_gnttab_grant_copy(xengnttab_handle *xgt, + bool to_domain, uint32_t domid, + XenGrantCopySegment *segs, + uint32_t nr_segs, Error **errp) +{ + if (!xen_gnttab_ops) { + return -ENOSYS; + } + + return xen_gnttab_ops->grant_copy(xgt, to_domain, domid, segs, nr_segs, + errp); +} + +static inline int qemu_xen_gnttab_set_max_grants(xengnttab_handle *xgt, + uint32_t nr_grants) +{ + if (!xen_gnttab_ops) { + return -ENOSYS; + } + return xen_gnttab_ops->set_max_grants(xgt, nr_grants); +} + +static inline void *qemu_xen_gnttab_map_refs(xengnttab_handle *xgt, + uint32_t count, uint32_t domid, + uint32_t *refs, int prot) +{ + if (!xen_gnttab_ops) { + return NULL; + } + return xen_gnttab_ops->map_refs(xgt, count, domid, refs, prot); +} + +static inline int qemu_xen_gnttab_unmap(xengnttab_handle *xgt, + void *start_address, uint32_t *refs, + uint32_t count) +{ + if (!xen_gnttab_ops) { + return -ENOSYS; + } + return xen_gnttab_ops->unmap(xgt, start_address, refs, count); +} + +struct foreignmem_backend_ops { + void *(*map)(uint32_t dom, void *addr, int prot, size_t pages, + xen_pfn_t *pfns, int *errs); + int (*unmap)(void *addr, size_t pages); +}; + +extern struct foreignmem_backend_ops *xen_foreignmem_ops; + +static inline void *qemu_xen_foreignmem_map(uint32_t dom, void *addr, int prot, + size_t pages, xen_pfn_t *pfns, + int *errs) +{ + if (!xen_foreignmem_ops) { + return NULL; + } + return xen_foreignmem_ops->map(dom, addr, prot, pages, pfns, errs); +} + +static inline int qemu_xen_foreignmem_unmap(void *addr, size_t pages) +{ + if (!xen_foreignmem_ops) { + return -ENOSYS; + } + return xen_foreignmem_ops->unmap(addr, pages); +} + +typedef void (*xs_watch_fn)(void *opaque, const char *path); + +struct qemu_xs_handle; +struct qemu_xs_watch; +typedef uint32_t xs_transaction_t; + +#define XBT_NULL 0 + +#define XS_PERM_NONE 0x00 +#define XS_PERM_READ 0x01 +#define XS_PERM_WRITE 0x02 + +struct xenstore_backend_ops { + struct qemu_xs_handle *(*open)(void); + void (*close)(struct qemu_xs_handle *h); + char *(*get_domain_path)(struct qemu_xs_handle *h, unsigned int domid); + char **(*directory)(struct qemu_xs_handle *h, xs_transaction_t t, + const char *path, unsigned int *num); + void *(*read)(struct qemu_xs_handle *h, xs_transaction_t t, + const char *path, unsigned int *len); + bool (*write)(struct qemu_xs_handle *h, xs_transaction_t t, + const char *path, const void *data, unsigned int len); + bool (*create)(struct qemu_xs_handle *h, xs_transaction_t t, + unsigned int owner, unsigned int domid, + unsigned int perms, const char *path); + bool (*destroy)(struct qemu_xs_handle *h, xs_transaction_t t, + const char *path); + struct qemu_xs_watch *(*watch)(struct qemu_xs_handle *h, const char *path, + xs_watch_fn fn, void *opaque); + void (*unwatch)(struct qemu_xs_handle *h, struct qemu_xs_watch *w); + xs_transaction_t (*transaction_start)(struct qemu_xs_handle *h); + bool (*transaction_end)(struct qemu_xs_handle *h, xs_transaction_t t, + bool abort); +}; + +extern struct xenstore_backend_ops *xen_xenstore_ops; + +static inline struct qemu_xs_handle *qemu_xen_xs_open(void) +{ + if (!xen_xenstore_ops) { + return NULL; + } + return xen_xenstore_ops->open(); +} + +static inline void qemu_xen_xs_close(struct qemu_xs_handle *h) +{ + if (!xen_xenstore_ops) { + return; + } + xen_xenstore_ops->close(h); +} + +static inline char *qemu_xen_xs_get_domain_path(struct qemu_xs_handle *h, + unsigned int domid) +{ + if (!xen_xenstore_ops) { + return NULL; + } + return xen_xenstore_ops->get_domain_path(h, domid); +} + +static inline char **qemu_xen_xs_directory(struct qemu_xs_handle *h, + xs_transaction_t t, const char *path, + unsigned int *num) +{ + if (!xen_xenstore_ops) { + return NULL; + } + return xen_xenstore_ops->directory(h, t, path, num); +} + +static inline void *qemu_xen_xs_read(struct qemu_xs_handle *h, + xs_transaction_t t, const char *path, + unsigned int *len) +{ + if (!xen_xenstore_ops) { + return NULL; + } + return xen_xenstore_ops->read(h, t, path, len); +} + +static inline bool qemu_xen_xs_write(struct qemu_xs_handle *h, + xs_transaction_t t, const char *path, + const void *data, unsigned int len) +{ + if (!xen_xenstore_ops) { + return false; + } + return xen_xenstore_ops->write(h, t, path, data, len); +} + +static inline bool qemu_xen_xs_create(struct qemu_xs_handle *h, + xs_transaction_t t, unsigned int owner, + unsigned int domid, unsigned int perms, + const char *path) +{ + if (!xen_xenstore_ops) { + return false; + } + return xen_xenstore_ops->create(h, t, owner, domid, perms, path); +} + +static inline bool qemu_xen_xs_destroy(struct qemu_xs_handle *h, + xs_transaction_t t, const char *path) +{ + if (!xen_xenstore_ops) { + return false; + } + return xen_xenstore_ops->destroy(h, t, path); +} + +static inline struct qemu_xs_watch *qemu_xen_xs_watch(struct qemu_xs_handle *h, + const char *path, + xs_watch_fn fn, + void *opaque) +{ + if (!xen_xenstore_ops) { + return NULL; + } + return xen_xenstore_ops->watch(h, path, fn, opaque); +} + +static inline void qemu_xen_xs_unwatch(struct qemu_xs_handle *h, + struct qemu_xs_watch *w) +{ + if (!xen_xenstore_ops) { + return; + } + xen_xenstore_ops->unwatch(h, w); +} + +static inline xs_transaction_t qemu_xen_xs_transaction_start(struct qemu_xs_handle *h) +{ + if (!xen_xenstore_ops) { + return XBT_NULL; + } + return xen_xenstore_ops->transaction_start(h); +} + +static inline bool qemu_xen_xs_transaction_end(struct qemu_xs_handle *h, + xs_transaction_t t, bool abort) +{ + if (!xen_xenstore_ops) { + return false; + } + return xen_xenstore_ops->transaction_end(h, t, abort); +} + +void setup_xen_backend_ops(void); + +#endif /* QEMU_XEN_BACKEND_OPS_H */ diff --git a/include/hw/xen/xen_igd.h b/include/hw/xen/xen_igd.h new file mode 100644 index 0000000000..7ffca06c10 --- /dev/null +++ b/include/hw/xen/xen_igd.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2007, Neocleus Corporation. + * Copyright (c) 2007, Intel Corporation. + * + * SPDX-License-Identifier: GPL-2.0-only + * + * Alex Novik + * Allen Kay + * Guy Zana + */ +#ifndef XEN_IGD_H +#define XEN_IGD_H + +#include "hw/xen/xen-host-pci-device.h" + +typedef struct XenPCIPassthroughState XenPCIPassthroughState; + +bool xen_igd_gfx_pt_enabled(void); +void xen_igd_gfx_pt_set(bool value, Error **errp); + +uint32_t igd_read_opregion(XenPCIPassthroughState *s); +void xen_igd_reserve_slot(PCIBus *pci_bus); +void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val); +void xen_igd_passthrough_isa_bridge_create(XenPCIPassthroughState *s, + XenHostPCIDevice *dev); + +static inline bool is_igd_vga_passthrough(XenHostPCIDevice *dev) +{ + return (xen_igd_gfx_pt_enabled() + && ((dev->class_code >> 0x8) == PCI_CLASS_DISPLAY_VGA)); +} + +#endif diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_native.h similarity index 74% rename from include/hw/xen/xen_common.h rename to include/hw/xen/xen_native.h index 179741ff79..1a5ad693a4 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_native.h @@ -1,5 +1,9 @@ -#ifndef QEMU_HW_XEN_COMMON_H -#define QEMU_HW_XEN_COMMON_H +#ifndef QEMU_HW_XEN_NATIVE_H +#define QEMU_HW_XEN_NATIVE_H + +#ifdef __XEN_INTERFACE_VERSION__ +#error In Xen native files, include xen_native.h before other Xen headers +#endif /* * If we have new enough libxenctrl then we do not want/need these compat @@ -12,69 +16,19 @@ #include #include -#include "hw/xen/interface/io/xenbus.h" #include "hw/xen/xen.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/xen/trace.h" extern xc_interface *xen_xc; /* - * We don't support Xen prior to 4.2.0. + * We don't support Xen prior to 4.7.1. */ -/* Xen 4.2 through 4.6 */ -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40701 - -typedef xc_interface xenforeignmemory_handle; -typedef xc_evtchn xenevtchn_handle; -typedef xc_gnttab xengnttab_handle; -typedef evtchn_port_or_error_t xenevtchn_port_or_error_t; - -#define xenevtchn_open(l, f) xc_evtchn_open(l, f); -#define xenevtchn_close(h) xc_evtchn_close(h) -#define xenevtchn_fd(h) xc_evtchn_fd(h) -#define xenevtchn_pending(h) xc_evtchn_pending(h) -#define xenevtchn_notify(h, p) xc_evtchn_notify(h, p) -#define xenevtchn_bind_interdomain(h, d, p) xc_evtchn_bind_interdomain(h, d, p) -#define xenevtchn_unmask(h, p) xc_evtchn_unmask(h, p) -#define xenevtchn_unbind(h, p) xc_evtchn_unbind(h, p) - -#define xengnttab_open(l, f) xc_gnttab_open(l, f) -#define xengnttab_close(h) xc_gnttab_close(h) -#define xengnttab_set_max_grants(h, n) xc_gnttab_set_max_grants(h, n) -#define xengnttab_map_grant_ref(h, d, r, p) xc_gnttab_map_grant_ref(h, d, r, p) -#define xengnttab_unmap(h, a, n) xc_gnttab_munmap(h, a, n) -#define xengnttab_map_grant_refs(h, c, d, r, p) \ - xc_gnttab_map_grant_refs(h, c, d, r, p) -#define xengnttab_map_domain_grant_refs(h, c, d, r, p) \ - xc_gnttab_map_domain_grant_refs(h, c, d, r, p) - -#define xenforeignmemory_open(l, f) xen_xc -#define xenforeignmemory_close(h) - -static inline void *xenforeignmemory_map(xc_interface *h, uint32_t dom, - int prot, size_t pages, - const xen_pfn_t arr[/*pages*/], - int err[/*pages*/]) -{ - if (err) - return xc_map_foreign_bulk(h, dom, prot, arr, err, pages); - else - return xc_map_foreign_pages(h, dom, prot, arr, pages); -} - -#define xenforeignmemory_unmap(h, p, s) munmap(p, s * XC_PAGE_SIZE) - -#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40701 */ - -#include -#include #include -#endif - extern xenforeignmemory_handle *xen_fmem; #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40900 @@ -182,8 +136,6 @@ static inline xendevicemodel_handle *xendevicemodel_open( return xen_xc; } -#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40500 - static inline int xendevicemodel_create_ioreq_server( xendevicemodel_handle *dmod, domid_t domid, int handle_bufioreq, ioservid_t *id) @@ -245,8 +197,6 @@ static inline int xendevicemodel_set_ioreq_server_state( return xc_hvm_set_ioreq_server_state(dmod, domid, id, enabled); } -#endif /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40500 */ - static inline int xendevicemodel_set_pci_intx_level( xendevicemodel_handle *dmod, domid_t domid, uint16_t segment, uint8_t bus, uint8_t device, uint8_t intx, unsigned int level) @@ -316,12 +266,6 @@ static inline int xen_set_pci_intx_level(domid_t domid, uint16_t segment, device, intx, level); } -static inline int xen_set_pci_link_route(domid_t domid, uint8_t link, - uint8_t irq) -{ - return xendevicemodel_set_pci_link_route(xen_dmod, domid, link, irq); -} - static inline int xen_inject_msi(domid_t domid, uint64_t msi_addr, uint32_t msi_data) { @@ -380,15 +324,6 @@ static inline int xen_get_vmport_regs_pfn(xc_interface *xc, domid_t dom, } #endif -/* Xen before 4.6 */ -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40600 - -#ifndef HVM_IOREQSRV_BUFIOREQ_ATOMIC -#define HVM_IOREQSRV_BUFIOREQ_ATOMIC 2 -#endif - -#endif - static inline int xen_get_default_ioreq_server_info(domid_t dom, xen_pfn_t *ioreq_pfn, xen_pfn_t *bufioreq_pfn, @@ -426,84 +361,6 @@ static inline int xen_get_default_ioreq_server_info(domid_t dom, return 0; } -/* Xen before 4.5 */ -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40500 - -#ifndef HVM_PARAM_BUFIOREQ_EVTCHN -#define HVM_PARAM_BUFIOREQ_EVTCHN 26 -#endif - -#define IOREQ_TYPE_PCI_CONFIG 2 - -typedef uint16_t ioservid_t; - -static inline void xen_map_memory_section(domid_t dom, - ioservid_t ioservid, - MemoryRegionSection *section) -{ -} - -static inline void xen_unmap_memory_section(domid_t dom, - ioservid_t ioservid, - MemoryRegionSection *section) -{ -} - -static inline void xen_map_io_section(domid_t dom, - ioservid_t ioservid, - MemoryRegionSection *section) -{ -} - -static inline void xen_unmap_io_section(domid_t dom, - ioservid_t ioservid, - MemoryRegionSection *section) -{ -} - -static inline void xen_map_pcidev(domid_t dom, - ioservid_t ioservid, - PCIDevice *pci_dev) -{ -} - -static inline void xen_unmap_pcidev(domid_t dom, - ioservid_t ioservid, - PCIDevice *pci_dev) -{ -} - -static inline void xen_create_ioreq_server(domid_t dom, - ioservid_t *ioservid) -{ -} - -static inline void xen_destroy_ioreq_server(domid_t dom, - ioservid_t ioservid) -{ -} - -static inline int xen_get_ioreq_server_info(domid_t dom, - ioservid_t ioservid, - xen_pfn_t *ioreq_pfn, - xen_pfn_t *bufioreq_pfn, - evtchn_port_t *bufioreq_evtchn) -{ - return xen_get_default_ioreq_server_info(dom, ioreq_pfn, - bufioreq_pfn, - bufioreq_evtchn); -} - -static inline int xen_set_ioreq_server_state(domid_t dom, - ioservid_t ioservid, - bool enable) -{ - return 0; -} - -/* Xen 4.5 */ -#else - static bool use_default_ioreq_server; static inline void xen_map_memory_section(domid_t dom, @@ -606,8 +463,8 @@ static inline void xen_unmap_pcidev(domid_t dom, PCI_FUNC(pci_dev->devfn)); } -static inline void xen_create_ioreq_server(domid_t dom, - ioservid_t *ioservid) +static inline int xen_create_ioreq_server(domid_t dom, + ioservid_t *ioservid) { int rc = xendevicemodel_create_ioreq_server(xen_dmod, dom, HVM_IOREQSRV_BUFIOREQ_ATOMIC, @@ -615,12 +472,14 @@ static inline void xen_create_ioreq_server(domid_t dom, if (rc == 0) { trace_xen_ioreq_server_create(*ioservid); - return; + return rc; } *ioservid = 0; use_default_ioreq_server = true; trace_xen_default_ioreq_server(); + + return rc; } static inline void xen_destroy_ioreq_server(domid_t dom, @@ -664,33 +523,28 @@ static inline int xen_set_ioreq_server_state(domid_t dom, enable); } -#endif - -/* Xen before 4.8 */ - -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40800 - -struct xengnttab_grant_copy_segment { - union xengnttab_copy_ptr { - void *virt; - struct { - uint32_t ref; - uint16_t offset; - uint16_t domid; - } foreign; - } source, dest; - uint16_t len; - uint16_t flags; - int16_t status; -}; - -typedef struct xengnttab_grant_copy_segment xengnttab_grant_copy_segment_t; - -static inline int xengnttab_grant_copy(xengnttab_handle *xgt, uint32_t count, - xengnttab_grant_copy_segment_t *segs) +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 41500 +static inline int xendevicemodel_set_irq_level(xendevicemodel_handle *dmod, + domid_t domid, uint32_t irq, + unsigned int level) { - return -ENOSYS; + return -1; } #endif -#endif /* QEMU_HW_XEN_COMMON_H */ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 41700 +#define GUEST_VIRTIO_MMIO_BASE xen_mk_ullong(0x02000000) +#define GUEST_VIRTIO_MMIO_SIZE xen_mk_ullong(0x00100000) +#define GUEST_VIRTIO_MMIO_SPI_FIRST 33 +#define GUEST_VIRTIO_MMIO_SPI_LAST 43 +#endif + +#if defined(__i386__) || defined(__x86_64__) +#define GUEST_RAM_BANKS 2 +#define GUEST_RAM0_BASE 0x40000000ULL /* 3GB of low RAM @ 1GB */ +#define GUEST_RAM0_SIZE 0xc0000000ULL +#define GUEST_RAM1_BASE 0x0200000000ULL /* 1016GB of RAM @ 8GB */ +#define GUEST_RAM1_SIZE 0xfe00000000ULL +#endif + +#endif /* QEMU_HW_XEN_NATIVE_H */ diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h index 7cd4bc2b82..ddad4b9f36 100644 --- a/include/hw/xen/xen_pvdev.h +++ b/include/hw/xen/xen_pvdev.h @@ -1,7 +1,9 @@ #ifndef QEMU_HW_XEN_PVDEV_H #define QEMU_HW_XEN_PVDEV_H -#include "hw/xen/xen_common.h" +#include "hw/qdev-core.h" +#include "hw/xen/xen_backend_ops.h" + /* ------------------------------------------------------------- */ #define XEN_BUFSIZE 1024 @@ -38,6 +40,7 @@ struct XenLegacyDevice { char name[64]; int debug; + struct qemu_xs_watch *watch; enum xenbus_state be_state; enum xenbus_state fe_state; int online; @@ -63,7 +66,6 @@ int xenstore_write_int64(const char *base, const char *node, int64_t ival); char *xenstore_read_str(const char *base, const char *node); int xenstore_read_int(const char *base, const char *node, int *ival); int xenstore_read_uint64(const char *base, const char *node, uint64_t *uval); -void xenstore_update(void *unused); const char *xenbus_strstate(enum xenbus_state state); diff --git a/include/io/channel-command.h b/include/io/channel-command.h index 305ac1d280..98934e6d9e 100644 --- a/include/io/channel-command.h +++ b/include/io/channel-command.h @@ -41,7 +41,10 @@ struct QIOChannelCommand { QIOChannel parent; int writefd; int readfd; - pid_t pid; + GPid pid; +#ifdef WIN32 + bool blocking; +#endif }; diff --git a/include/io/channel-file.h b/include/io/channel-file.h index 50e8eb1138..d373a4e44d 100644 --- a/include/io/channel-file.h +++ b/include/io/channel-file.h @@ -68,6 +68,24 @@ struct QIOChannelFile { QIOChannelFile * qio_channel_file_new_fd(int fd); +/** + * qio_channel_file_new_dupfd: + * @fd: the file descriptor + * @errp: pointer to initialized error object + * + * Create a new IO channel object for a file represented by the @fd + * parameter. Like qio_channel_file_new_fd(), but the @fd is first + * duplicated with dup(). + * + * The channel will own the duplicated file descriptor and will take + * responsibility for closing it, the original FD is owned by the + * caller. + * + * Returns: the new channel object + */ +QIOChannelFile * +qio_channel_file_new_dupfd(int fd, Error **errp); + /** * qio_channel_file_new_path: * @path: the file path diff --git a/include/io/channel-null.h b/include/io/channel-null.h new file mode 100644 index 0000000000..f6d54e63cf --- /dev/null +++ b/include/io/channel-null.h @@ -0,0 +1,55 @@ +/* + * QEMU I/O channels null driver + * + * Copyright (c) 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 QIO_CHANNEL_FILE_H +#define QIO_CHANNEL_FILE_H + +#include "io/channel.h" +#include "qom/object.h" + +#define TYPE_QIO_CHANNEL_NULL "qio-channel-null" +OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelNull, QIO_CHANNEL_NULL) + + +/** + * QIOChannelNull: + * + * The QIOChannelNull object provides a channel implementation + * that discards all writes and returns EOF for all reads. + */ + +struct QIOChannelNull { + QIOChannel parent; + bool closed; +}; + + +/** + * qio_channel_null_new: + * + * Create a new IO channel object that discards all writes + * and returns EOF for all reads. + * + * Returns: the new channel object + */ +QIOChannelNull * +qio_channel_null_new(void); + +#endif /* QIO_CHANNEL_NULL_H */ diff --git a/include/io/channel-socket.h b/include/io/channel-socket.h index 513c428fe4..ab15577d38 100644 --- a/include/io/channel-socket.h +++ b/include/io/channel-socket.h @@ -124,7 +124,7 @@ void qio_channel_socket_connect_async(QIOChannelSocket *ioc, * qio_channel_socket_listen_sync: * @ioc: the socket channel object * @addr: the address to listen to - * @num: the expected ammount of connections + * @num: the expected amount of connections * @errp: pointer to a NULL-initialized error object * * Attempt to listen to the address @addr. This method @@ -141,7 +141,7 @@ int qio_channel_socket_listen_sync(QIOChannelSocket *ioc, * qio_channel_socket_listen_async: * @ioc: the socket channel object * @addr: the address to listen to - * @num: the expected ammount of connections + * @num: the expected amount of connections * @callback: the function to invoke on completion * @opaque: user data to pass to @callback * @destroy: the function to free @opaque diff --git a/include/io/channel-tls.h b/include/io/channel-tls.h index 5672479e9e..26c67f17e2 100644 --- a/include/io/channel-tls.h +++ b/include/io/channel-tls.h @@ -48,6 +48,7 @@ struct QIOChannelTLS { QIOChannel *master; QCryptoTLSSession *session; QIOChannelShutdown shutdown; + guint hs_ioc_tag; }; /** diff --git a/include/io/channel-util.h b/include/io/channel-util.h index a5d720d9a0..fa18a3756d 100644 --- a/include/io/channel-util.h +++ b/include/io/channel-util.h @@ -49,4 +49,27 @@ QIOChannel *qio_channel_new_fd(int fd, Error **errp); +/** + * qio_channel_util_set_aio_fd_handler: + * @read_fd: the file descriptor for the read handler + * @read_ctx: the AioContext for the read handler + * @io_read: the read handler + * @write_fd: the file descriptor for the write handler + * @write_ctx: the AioContext for the write handler + * @io_write: the write handler + * @opaque: the opaque argument to the read and write handler + * + * Set the read and write handlers when @read_ctx and @write_ctx are non-NULL, + * respectively. To leave a handler in its current state, pass a NULL + * AioContext. To clear a handler, pass a non-NULL AioContext and a NULL + * handler. + */ +void qio_channel_util_set_aio_fd_handler(int read_fd, + AioContext *read_ctx, + IOHandler *io_read, + int write_fd, + AioContext *write_ctx, + IOHandler *io_write, + void *opaque); + #endif /* QIO_CHANNEL_UTIL_H */ diff --git a/include/io/channel.h b/include/io/channel.h index c680ee7480..7986c49c71 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -22,7 +22,7 @@ #define QIO_CHANNEL_H #include "qom/object.h" -#include "qemu/coroutine.h" +#include "qemu/coroutine-core.h" #include "block/aio.h" #define TYPE_QIO_CHANNEL "qio-channel" @@ -34,6 +34,8 @@ OBJECT_DECLARE_TYPE(QIOChannel, QIOChannelClass, #define QIO_CHANNEL_WRITE_FLAG_ZERO_COPY 0x1 +#define QIO_CHANNEL_READ_FLAG_MSG_PEEK 0x1 + typedef enum QIOChannelFeature QIOChannelFeature; enum QIOChannelFeature { @@ -41,6 +43,8 @@ enum QIOChannelFeature { QIO_CHANNEL_FEATURE_SHUTDOWN, QIO_CHANNEL_FEATURE_LISTEN, QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY, + QIO_CHANNEL_FEATURE_READ_MSG_PEEK, + QIO_CHANNEL_FEATURE_SEEKABLE, }; @@ -78,9 +82,11 @@ struct QIOChannel { Object parent; unsigned int features; /* bitmask of QIOChannelFeatures */ char *name; - AioContext *ctx; + AioContext *read_ctx; Coroutine *read_coroutine; + AioContext *write_ctx; Coroutine *write_coroutine; + bool follow_coroutine_ctx; #ifdef _WIN32 HANDLE event; /* For use with GSource on Win32 */ #endif @@ -114,6 +120,7 @@ struct QIOChannelClass { size_t niov, int **fds, size_t *nfds, + int flags, Error **errp); int (*io_close)(QIOChannel *ioc, Error **errp); @@ -124,6 +131,16 @@ struct QIOChannelClass { Error **errp); /* Optional callbacks */ + ssize_t (*io_pwritev)(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + off_t offset, + Error **errp); + ssize_t (*io_preadv)(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + off_t offset, + Error **errp); int (*io_shutdown)(QIOChannel *ioc, QIOChannelShutdown how, Error **errp); @@ -136,8 +153,9 @@ struct QIOChannelClass { int whence, Error **errp); void (*io_set_aio_fd_handler)(QIOChannel *ioc, - AioContext *ctx, + AioContext *read_ctx, IOHandler *io_read, + AioContext *write_ctx, IOHandler *io_write, void *opaque); int (*io_flush)(QIOChannel *ioc, @@ -188,6 +206,7 @@ void qio_channel_set_name(QIOChannel *ioc, * @niov: the length of the @iov array * @fds: pointer to an array that will received file handles * @nfds: pointer filled with number of elements in @fds on return + * @flags: read flags (QIO_CHANNEL_READ_FLAG_*) * @errp: pointer to a NULL-initialized error object * * Read data from the IO channel, storing it in the @@ -224,6 +243,7 @@ ssize_t qio_channel_readv_full(QIOChannel *ioc, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp); @@ -295,10 +315,10 @@ ssize_t qio_channel_writev_full(QIOChannel *ioc, * Returns: 1 if all bytes were read, 0 if end-of-file * occurs without data, or -1 on error */ -int qio_channel_readv_all_eof(QIOChannel *ioc, - const struct iovec *iov, - size_t niov, - Error **errp); +int coroutine_mixed_fn qio_channel_readv_all_eof(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp); /** * qio_channel_readv_all: @@ -322,10 +342,10 @@ int qio_channel_readv_all_eof(QIOChannel *ioc, * * Returns: 0 if all bytes were read, or -1 on error */ -int qio_channel_readv_all(QIOChannel *ioc, - const struct iovec *iov, - size_t niov, - Error **errp); +int coroutine_mixed_fn qio_channel_readv_all(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp); /** @@ -347,10 +367,10 @@ int qio_channel_readv_all(QIOChannel *ioc, * * Returns: 0 if all bytes were written, or -1 on error */ -int qio_channel_writev_all(QIOChannel *ioc, - const struct iovec *iov, - size_t niov, - Error **erp); +int coroutine_mixed_fn qio_channel_writev_all(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp); /** * qio_channel_readv: @@ -431,10 +451,10 @@ ssize_t qio_channel_write(QIOChannel *ioc, * Returns: 1 if all bytes were read, 0 if end-of-file occurs * without data, or -1 on error */ -int qio_channel_read_all_eof(QIOChannel *ioc, - char *buf, - size_t buflen, - Error **errp); +int coroutine_mixed_fn qio_channel_read_all_eof(QIOChannel *ioc, + char *buf, + size_t buflen, + Error **errp); /** * qio_channel_read_all: @@ -451,10 +471,10 @@ int qio_channel_read_all_eof(QIOChannel *ioc, * * Returns: 0 if all bytes were read, or -1 on error */ -int qio_channel_read_all(QIOChannel *ioc, - char *buf, - size_t buflen, - Error **errp); +int coroutine_mixed_fn qio_channel_read_all(QIOChannel *ioc, + char *buf, + size_t buflen, + Error **errp); /** * qio_channel_write_all: @@ -470,10 +490,10 @@ int qio_channel_read_all(QIOChannel *ioc, * * Returns: 0 if all bytes were written, or -1 on error */ -int qio_channel_write_all(QIOChannel *ioc, - const char *buf, - size_t buflen, - Error **errp); +int coroutine_mixed_fn qio_channel_write_all(QIOChannel *ioc, + const char *buf, + size_t buflen, + Error **errp); /** * qio_channel_set_blocking: @@ -492,6 +512,21 @@ int qio_channel_set_blocking(QIOChannel *ioc, bool enabled, Error **errp); +/** + * qio_channel_set_follow_coroutine_ctx: + * @ioc: the channel object + * @enabled: whether or not to follow the coroutine's AioContext + * + * If @enabled is true, calls to qio_channel_yield() use the current + * coroutine's AioContext. Usually this is desirable. + * + * If @enabled is false, calls to qio_channel_yield() use the global iohandler + * AioContext. This is may be used by coroutines that run in the main loop and + * do not wish to respond to I/O during nested event loops. This is the + * default for compatibility with code that is not aware of AioContexts. + */ +void qio_channel_set_follow_coroutine_ctx(QIOChannel *ioc, bool enabled); + /** * qio_channel_close: * @ioc: the channel object @@ -504,6 +539,78 @@ int qio_channel_set_blocking(QIOChannel *ioc, int qio_channel_close(QIOChannel *ioc, Error **errp); +/** + * qio_channel_pwritev + * @ioc: the channel object + * @iov: the array of memory regions to write data from + * @niov: the length of the @iov array + * @offset: offset in the channel where writes should begin + * @errp: pointer to a NULL-initialized error object + * + * Not all implementations will support this facility, so may report + * an error. To avoid errors, the caller may check for the feature + * flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method. + * + * Behaves as qio_channel_writev_full, apart from not supporting + * sending of file handles as well as beginning the write at the + * passed @offset + * + */ +ssize_t qio_channel_pwritev(QIOChannel *ioc, const struct iovec *iov, + size_t niov, off_t offset, Error **errp); + +/** + * qio_channel_pwrite + * @ioc: the channel object + * @buf: the memory region to write data into + * @buflen: the number of bytes to @buf + * @offset: offset in the channel where writes should begin + * @errp: pointer to a NULL-initialized error object + * + * Not all implementations will support this facility, so may report + * an error. To avoid errors, the caller may check for the feature + * flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method. + * + */ +ssize_t qio_channel_pwrite(QIOChannel *ioc, char *buf, size_t buflen, + off_t offset, Error **errp); + +/** + * qio_channel_preadv + * @ioc: the channel object + * @iov: the array of memory regions to read data into + * @niov: the length of the @iov array + * @offset: offset in the channel where writes should begin + * @errp: pointer to a NULL-initialized error object + * + * Not all implementations will support this facility, so may report + * an error. To avoid errors, the caller may check for the feature + * flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method. + * + * Behaves as qio_channel_readv_full, apart from not supporting + * receiving of file handles as well as beginning the read at the + * passed @offset + * + */ +ssize_t qio_channel_preadv(QIOChannel *ioc, const struct iovec *iov, + size_t niov, off_t offset, Error **errp); + +/** + * qio_channel_pread + * @ioc: the channel object + * @buf: the memory region to write data into + * @buflen: the number of bytes to @buf + * @offset: offset in the channel where writes should begin + * @errp: pointer to a NULL-initialized error object + * + * Not all implementations will support this facility, so may report + * an error. To avoid errors, the caller may check for the feature + * flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method. + * + */ +ssize_t qio_channel_pread(QIOChannel *ioc, char *buf, size_t buflen, + off_t offset, Error **errp); + /** * qio_channel_shutdown: * @ioc: the channel object @@ -697,41 +804,6 @@ GSource *qio_channel_add_watch_source(QIOChannel *ioc, GDestroyNotify notify, GMainContext *context); -/** - * qio_channel_attach_aio_context: - * @ioc: the channel object - * @ctx: the #AioContext to set the handlers on - * - * Request that qio_channel_yield() sets I/O handlers on - * the given #AioContext. If @ctx is %NULL, qio_channel_yield() - * uses QEMU's main thread event loop. - * - * You can move a #QIOChannel from one #AioContext to another even if - * I/O handlers are set for a coroutine. However, #QIOChannel provides - * no synchronization between the calls to qio_channel_yield() and - * qio_channel_attach_aio_context(). - * - * Therefore you should first call qio_channel_detach_aio_context() - * to ensure that the coroutine is not entered concurrently. Then, - * while the coroutine has yielded, call qio_channel_attach_aio_context(), - * and then aio_co_schedule() to place the coroutine on the new - * #AioContext. The calls to qio_channel_detach_aio_context() - * and qio_channel_attach_aio_context() should be protected with - * aio_context_acquire() and aio_context_release(). - */ -void qio_channel_attach_aio_context(QIOChannel *ioc, - AioContext *ctx); - -/** - * qio_channel_detach_aio_context: - * @ioc: the channel object - * - * Disable any I/O handlers set by qio_channel_yield(). With the - * help of aio_co_schedule(), this allows moving a coroutine that was - * paused by qio_channel_yield() to another context. - */ -void qio_channel_detach_aio_context(QIOChannel *ioc); - /** * qio_channel_yield: * @ioc: the channel object @@ -751,6 +823,16 @@ void qio_channel_detach_aio_context(QIOChannel *ioc); void coroutine_fn qio_channel_yield(QIOChannel *ioc, GIOCondition condition); +/** + * qio_channel_wake_read: + * @ioc: the channel object + * + * If qio_channel_yield() is currently waiting for the channel to become + * readable, interrupt it and reenter immediately. This function is safe to call + * from any thread. + */ +void qio_channel_wake_read(QIOChannel *ioc); + /** * qio_channel_wait: * @ioc: the channel object @@ -769,8 +851,9 @@ void qio_channel_wait(QIOChannel *ioc, /** * qio_channel_set_aio_fd_handler: * @ioc: the channel object - * @ctx: the AioContext to set the handlers on + * @read_ctx: the AioContext to set the read handler on or NULL * @io_read: the read handler + * @write_ctx: the AioContext to set the write handler on or NULL * @io_write: the write handler * @opaque: the opaque value passed to the handler * @@ -778,10 +861,17 @@ void qio_channel_wait(QIOChannel *ioc, * be used by channel implementations to forward the handlers * to another channel (e.g. from #QIOChannelTLS to the * underlying socket). + * + * When @read_ctx is NULL, don't touch the read handler. When @write_ctx is + * NULL, don't touch the write handler. Note that setting the read handler + * clears the write handler, and vice versa, if they share the same AioContext. + * Therefore the caller must pass both handlers together when sharing the same + * AioContext. */ void qio_channel_set_aio_fd_handler(QIOChannel *ioc, - AioContext *ctx, + AioContext *read_ctx, IOHandler *io_read, + AioContext *write_ctx, IOHandler *io_write, void *opaque); @@ -806,11 +896,11 @@ void qio_channel_set_aio_fd_handler(QIOChannel *ioc, * occurs without data, or -1 on error */ -int qio_channel_readv_full_all_eof(QIOChannel *ioc, - const struct iovec *iov, - size_t niov, - int **fds, size_t *nfds, - Error **errp); +int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, size_t *nfds, + Error **errp); /** * qio_channel_readv_full_all: @@ -832,11 +922,11 @@ int qio_channel_readv_full_all_eof(QIOChannel *ioc, * Returns: 0 if all bytes were read, or -1 on error */ -int qio_channel_readv_full_all(QIOChannel *ioc, - const struct iovec *iov, - size_t niov, - int **fds, size_t *nfds, - Error **errp); +int coroutine_mixed_fn qio_channel_readv_full_all(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, size_t *nfds, + Error **errp); /** * qio_channel_writev_full_all: @@ -866,11 +956,11 @@ int qio_channel_readv_full_all(QIOChannel *ioc, * Returns: 0 if all bytes were written, or -1 on error */ -int qio_channel_writev_full_all(QIOChannel *ioc, - const struct iovec *iov, - size_t niov, - int *fds, size_t nfds, - int flags, Error **errp); +int coroutine_mixed_fn qio_channel_writev_full_all(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, size_t nfds, + int flags, Error **errp); /** * qio_channel_flush: diff --git a/include/io/task.h b/include/io/task.h index beec4f5cfd..0b5342ee84 100644 --- a/include/io/task.h +++ b/include/io/task.h @@ -145,11 +145,11 @@ typedef void (*QIOTaskWorker)(QIOTask *task, * The QIOTask module can also be used to perform operations * in a background thread context, while still reporting the * results in the main event thread. This allows code which - * cannot easily be rewritten to be asychronous (such as DNS + * cannot easily be rewritten to be asynchronous (such as DNS * lookups) to be easily run non-blocking. Reporting the * results in the main thread context means that the caller * typically does not need to be concerned about thread - * safety wrt the QEMU global mutex. + * safety wrt the BQL. * * For example, the socket_listen() method will block the caller * while DNS lookups take place if given a name, instead of IP diff --git a/include/migration/blocker.h b/include/migration/blocker.h index 9cebe2ba06..a687ac0efe 100644 --- a/include/migration/blocker.h +++ b/include/migration/blocker.h @@ -14,22 +14,30 @@ #ifndef MIGRATION_BLOCKER_H #define MIGRATION_BLOCKER_H +#include "qapi/qapi-types-migration.h" + +#define MIG_MODE_ALL MIG_MODE__MAX + /** - * @migrate_add_blocker - prevent migration from proceeding + * @migrate_add_blocker - prevent all modes of migration from proceeding * - * @reason - an error to be returned whenever migration is attempted + * @reasonp - address of an error to be returned whenever migration is attempted * * @errp - [out] The reason (if any) we cannot block migration right now. * * @returns - 0 on success, -EBUSY/-EACCES on failure, with errp set. + * + * *@reasonp is freed and set to NULL if failure is returned. + * On success, the caller must not free @reasonp, except by + * calling migrate_del_blocker. */ -int migrate_add_blocker(Error *reason, Error **errp); +int migrate_add_blocker(Error **reasonp, Error **errp); /** - * @migrate_add_blocker_internal - prevent migration from proceeding without - * only-migrate implications + * @migrate_add_blocker_internal - prevent all modes of migration from + * proceeding, but ignore -only-migratable * - * @reason - an error to be returned whenever migration is attempted + * @reasonp - address of an error to be returned whenever migration is attempted * * @errp - [out] The reason (if any) we cannot block migration right now. * @@ -38,14 +46,52 @@ int migrate_add_blocker(Error *reason, Error **errp); * Some of the migration blockers can be temporary (e.g., for a few seconds), * so it shouldn't need to conflict with "-only-migratable". For those cases, * we can call this function rather than @migrate_add_blocker(). + * + * *@reasonp is freed and set to NULL if failure is returned. + * On success, the caller must not free @reasonp, except by + * calling migrate_del_blocker. */ -int migrate_add_blocker_internal(Error *reason, Error **errp); +int migrate_add_blocker_internal(Error **reasonp, Error **errp); /** - * @migrate_del_blocker - remove a blocking error from migration + * @migrate_del_blocker - remove a migration blocker from all modes and free it. * - * @reason - the error blocking migration + * @reasonp - address of the error blocking migration + * + * This function frees *@reasonp and sets it to NULL. */ -void migrate_del_blocker(Error *reason); +void migrate_del_blocker(Error **reasonp); + +/** + * @migrate_add_blocker_normal - prevent normal migration mode from proceeding + * + * @reasonp - address of an error to be returned whenever migration is attempted + * + * @errp - [out] The reason (if any) we cannot block migration right now. + * + * @returns - 0 on success, -EBUSY/-EACCES on failure, with errp set. + * + * *@reasonp is freed and set to NULL if failure is returned. + * On success, the caller must not free @reasonp, except by + * calling migrate_del_blocker. + */ +int migrate_add_blocker_normal(Error **reasonp, Error **errp); + +/** + * @migrate_add_blocker_modes - prevent some modes of migration from proceeding + * + * @reasonp - address of an error to be returned whenever migration is attempted + * + * @errp - [out] The reason (if any) we cannot block migration right now. + * + * @mode - one or more migration modes to be blocked. The list is terminated + * by -1 or MIG_MODE_ALL. For the latter, all modes are blocked. + * + * @returns - 0 on success, -EBUSY/-EACCES on failure, with errp set. + * + * *@reasonp is freed and set to NULL if failure is returned. + * On success, the caller must not free *@reasonp before the blocker is removed. + */ +int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...); #endif diff --git a/include/migration/client-options.h b/include/migration/client-options.h new file mode 100644 index 0000000000..59f4b55cf4 --- /dev/null +++ b/include/migration/client-options.h @@ -0,0 +1,25 @@ +/* + * QEMU public migration capabilities + * + * Copyright (c) 2012-2023 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 QEMU_MIGRATION_CLIENT_OPTIONS_H +#define QEMU_MIGRATION_CLIENT_OPTIONS_H + +/* capabilities */ + +bool migrate_background_snapshot(void); +bool migrate_dirty_limit(void); +bool migrate_postcopy_ram(void); +bool migrate_switchover_ack(void); + +/* parameters */ + +MigMode migrate_mode(void); +uint64_t migrate_vcpu_dirty_limit_period(void); + +#endif diff --git a/include/migration/colo.h b/include/migration/colo.h index 5fbe1a6d5d..eaac07f26d 100644 --- a/include/migration/colo.h +++ b/include/migration/colo.h @@ -28,7 +28,6 @@ bool migration_in_colo_state(void); int migration_incoming_enable_colo(void); void migration_incoming_disable_colo(void); bool migration_incoming_colo_enabled(void); -void *colo_process_incoming_thread(void *opaque); bool migration_incoming_in_colo_state(void); COLOMode get_colo_mode(void); @@ -36,6 +35,21 @@ COLOMode get_colo_mode(void); /* failover */ void colo_do_failover(void); -void colo_checkpoint_notify(void *opaque); +/* + * colo_checkpoint_delay_set + * + * Handles change of x-checkpoint-delay migration parameter, called from + * migrate_params_apply() to notify COLO module about the change. + */ +void colo_checkpoint_delay_set(void); + +/* + * Starts COLO incoming process. Called from process_incoming_migration_co() + * after loading the state. + * + * Called with BQL locked, may temporary release BQL. + */ +int coroutine_fn colo_incoming_co(void); + void colo_shutdown(void); #endif diff --git a/include/migration/global_state.h b/include/migration/global_state.h index 945eb35d5b..d7c2cd3216 100644 --- a/include/migration/global_state.h +++ b/include/migration/global_state.h @@ -16,7 +16,7 @@ #include "qapi/qapi-types-run-state.h" void register_global_state(void); -int global_state_store(void); +void global_state_store(void); void global_state_store_running(void); bool global_state_received(void); RunState global_state_get_runstate(void); diff --git a/include/migration/misc.h b/include/migration/misc.h index 465906710d..c9e200f4eb 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -15,7 +15,9 @@ #define MIGRATION_MISC_H #include "qemu/notify.h" +#include "qapi/qapi-types-migration.h" #include "qapi/qapi-types-net.h" +#include "migration/client-options.h" /* migration/ram.c */ @@ -30,7 +32,6 @@ typedef enum PrecopyNotifyReason { typedef struct PrecopyNotifyData { enum PrecopyNotifyReason reason; - Error **errp; } PrecopyNotifyData; void precopy_infrastructure_init(void); @@ -40,6 +41,7 @@ int precopy_notify(PrecopyNotifyReason reason, Error **errp); void ram_mig_init(void); void qemu_guest_free_page_hint(void *addr, size_t len); +bool migrate_ram_is_ignored(RAMBlock *block); /* migration/block.c */ @@ -58,17 +60,57 @@ void dump_vmstate_json_to_file(FILE *out_fp); void migration_object_init(void); void migration_shutdown(void); bool migration_is_idle(void); -bool migration_is_active(MigrationState *); -void add_migration_state_change_notifier(Notifier *notify); -void remove_migration_state_change_notifier(Notifier *notify); -bool migration_in_setup(MigrationState *); -bool migration_has_finished(MigrationState *); -bool migration_has_failed(MigrationState *); -/* ...and after the device transmission */ -bool migration_in_postcopy_after_devices(MigrationState *); -void migration_global_dump(Monitor *mon); -/* True if incomming migration entered POSTCOPY_INCOMING_DISCARD */ +bool migration_is_active(void); +bool migration_is_device(void); +bool migration_thread_is_self(void); +bool migration_is_setup_or_active(void); + +typedef enum MigrationEventType { + MIG_EVENT_PRECOPY_SETUP, + MIG_EVENT_PRECOPY_DONE, + MIG_EVENT_PRECOPY_FAILED, + MIG_EVENT_MAX +} MigrationEventType; + +typedef struct MigrationEvent { + MigrationEventType type; +} MigrationEvent; + +/* + * A MigrationNotifyFunc may return an error code and an Error object, + * but only when @e->type is MIG_EVENT_PRECOPY_SETUP. The code is an int + * to allow for different failure modes and recovery actions. + */ +typedef int (*MigrationNotifyFunc)(NotifierWithReturn *notify, + MigrationEvent *e, Error **errp); + +/* + * Register the notifier @notify to be called when a migration event occurs + * for MIG_MODE_NORMAL, as specified by the MigrationEvent passed to @func. + * Notifiers may receive events in any of the following orders: + * - MIG_EVENT_PRECOPY_SETUP -> MIG_EVENT_PRECOPY_DONE + * - MIG_EVENT_PRECOPY_SETUP -> MIG_EVENT_PRECOPY_FAILED + * - MIG_EVENT_PRECOPY_FAILED + */ +void migration_add_notifier(NotifierWithReturn *notify, + MigrationNotifyFunc func); + +/* + * Same as migration_add_notifier, but applies to be specified @mode. + */ +void migration_add_notifier_mode(NotifierWithReturn *notify, + MigrationNotifyFunc func, MigMode mode); + +void migration_remove_notifier(NotifierWithReturn *notify); +bool migration_is_running(void); +void migration_file_set_error(int err); + +/* True if incoming migration entered POSTCOPY_INCOMING_DISCARD */ bool migration_in_incoming_postcopy(void); + +/* True if incoming migration entered POSTCOPY_INCOMING_ADVISE */ +bool migration_incoming_postcopy_advised(void); + /* True if background snapshot is active */ bool migration_in_bg_snapshot(void); diff --git a/include/migration/qemu-file-types.h b/include/migration/qemu-file-types.h index 2867e3da84..adec5abc07 100644 --- a/include/migration/qemu-file-types.h +++ b/include/migration/qemu-file-types.h @@ -35,7 +35,7 @@ void qemu_put_byte(QEMUFile *f, int v); void qemu_put_be16(QEMUFile *f, unsigned int v); void qemu_put_be32(QEMUFile *f, unsigned int v); void qemu_put_be64(QEMUFile *f, uint64_t v); -size_t qemu_get_buffer(QEMUFile *f, uint8_t *buf, size_t size); +size_t coroutine_mixed_fn qemu_get_buffer(QEMUFile *f, uint8_t *buf, size_t size); int qemu_get_byte(QEMUFile *f); @@ -50,6 +50,8 @@ unsigned int qemu_get_be16(QEMUFile *f); unsigned int qemu_get_be32(QEMUFile *f); uint64_t qemu_get_be64(QEMUFile *f); +bool qemu_file_is_seekable(QEMUFile *f); + static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv) { qemu_put_be64(f, *pv); @@ -161,10 +163,20 @@ static inline void qemu_get_sbe64s(QEMUFile *f, int64_t *pv) qemu_get_be64s(f, (uint64_t *)pv); } -size_t qemu_get_counted_string(QEMUFile *f, char buf[256]); +size_t coroutine_mixed_fn qemu_get_counted_string(QEMUFile *f, char buf[256]); void qemu_put_counted_string(QEMUFile *f, const char *name); -int qemu_file_rate_limit(QEMUFile *f); +/** + * migration_rate_exceeded: Check if we have exceeded rate for this interval + * + * Checks if we have already transferred more data that we are allowed + * in the current interval. + * + * @f: QEMUFile used for main migration channel + * + * Returns if we should stop sending data for this interval. + */ +bool migration_rate_exceeded(QEMUFile *f); #endif diff --git a/include/migration/register.h b/include/migration/register.h index c1dcff0f90..d7b70a8be6 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -16,66 +16,287 @@ #include "hw/vmstate-if.h" +/** + * struct SaveVMHandlers: handler structure to finely control + * migration of complex subsystems and devices, such as RAM, block and + * VFIO. + */ typedef struct SaveVMHandlers { - /* This runs inside the iothread lock. */ - SaveStateHandler *save_state; + /* The following handlers run inside the BQL. */ + + /** + * @save_state + * + * Saves state section on the source using the latest state format + * version. + * + * Legacy method. Should be deprecated when all users are ported + * to VMStateDescription. + * + * @f: QEMUFile where to send the data + * @opaque: data pointer passed to register_savevm_live() + */ + void (*save_state)(QEMUFile *f, void *opaque); + + /** + * @save_prepare + * + * Called early, even before migration starts, and can be used to + * perform early checks. + * + * @opaque: data pointer passed to register_savevm_live() + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns zero to indicate success and negative for error + */ + int (*save_prepare)(void *opaque, Error **errp); + + /** + * @save_setup + * + * Initializes the data structures on the source and transmits + * first section containing information on the device + * + * @f: QEMUFile where to send the data + * @opaque: data pointer passed to register_savevm_live() + * + * Returns zero to indicate success and negative for error + */ + int (*save_setup)(QEMUFile *f, void *opaque); + + /** + * @save_cleanup + * + * Uninitializes the data structures on the source + * + * @opaque: data pointer passed to register_savevm_live() + */ void (*save_cleanup)(void *opaque); + + /** + * @save_live_complete_postcopy + * + * Called at the end of postcopy for all postcopyable devices. + * + * @f: QEMUFile where to send the data + * @opaque: data pointer passed to register_savevm_live() + * + * Returns zero to indicate success and negative for error + */ int (*save_live_complete_postcopy)(QEMUFile *f, void *opaque); + + /** + * @save_live_complete_precopy + * + * Transmits the last section for the device containing any + * remaining data at the end of a precopy phase. When postcopy is + * enabled, devices that support postcopy will skip this step, + * where the final data will be flushed at the end of postcopy via + * @save_live_complete_postcopy instead. + * + * @f: QEMUFile where to send the data + * @opaque: data pointer passed to register_savevm_live() + * + * Returns zero to indicate success and negative for error + */ int (*save_live_complete_precopy)(QEMUFile *f, void *opaque); - /* This runs both outside and inside the iothread lock. */ + /* This runs both outside and inside the BQL. */ + + /** + * @is_active + * + * Will skip a state section if not active + * + * @opaque: data pointer passed to register_savevm_live() + * + * Returns true if state section is active else false + */ bool (*is_active)(void *opaque); + + /** + * @has_postcopy + * + * Checks if a device supports postcopy + * + * @opaque: data pointer passed to register_savevm_live() + * + * Returns true for postcopy support else false + */ bool (*has_postcopy)(void *opaque); - /* is_active_iterate - * If it is not NULL then qemu_savevm_state_iterate will skip iteration if - * it returns false. For example, it is needed for only-postcopy-states, - * which needs to be handled by qemu_savevm_state_setup and - * qemu_savevm_state_pending, but do not need iterations until not in - * postcopy stage. + /** + * @is_active_iterate + * + * As #SaveVMHandlers.is_active(), will skip an inactive state + * section in qemu_savevm_state_iterate. + * + * For example, it is needed for only-postcopy-states, which needs + * to be handled by qemu_savevm_state_setup() and + * qemu_savevm_state_pending(), but do not need iterations until + * not in postcopy stage. + * + * @opaque: data pointer passed to register_savevm_live() + * + * Returns true if state section is active else false */ bool (*is_active_iterate)(void *opaque); - /* This runs outside the iothread lock in the migration case, and + /* This runs outside the BQL in the migration case, and * within the lock in the savevm case. The callback had better only * use data that is local to the migration thread or protected * by other locks. */ + + /** + * @save_live_iterate + * + * Should send a chunk of data until the point that stream + * bandwidth limits tell it to stop. Each call generates one + * section. + * + * @f: QEMUFile where to send the data + * @opaque: data pointer passed to register_savevm_live() + * + * Returns 0 to indicate that there is still more data to send, + * 1 that there is no more data to send and + * negative to indicate an error. + */ int (*save_live_iterate)(QEMUFile *f, void *opaque); - /* This runs outside the iothread lock! */ - int (*save_setup)(QEMUFile *f, void *opaque); - void (*save_live_pending)(QEMUFile *f, void *opaque, - uint64_t threshold_size, - uint64_t *res_precopy_only, - uint64_t *res_compatible, - uint64_t *res_postcopy_only); - /* Note for save_live_pending: - * - res_precopy_only is for data which must be migrated in precopy phase - * or in stopped state, in other words - before target vm start - * - res_compatible is for data which may be migrated in any phase - * - res_postcopy_only is for data which must be migrated in postcopy phase - * or in stopped state, in other words - after source vm stop + /* This runs outside the BQL! */ + + /** + * @state_pending_estimate * - * Sum of res_postcopy_only, res_compatible and res_postcopy_only is the - * whole amount of pending data. + * This estimates the remaining data to transfer + * + * Sum of @can_postcopy and @must_postcopy is the whole amount of + * pending data. + * + * @opaque: data pointer passed to register_savevm_live() + * @must_precopy: amount of data that must be migrated in precopy + * or in stopped state, i.e. that must be migrated + * before target start. + * @can_postcopy: amount of data that can be migrated in postcopy + * or in stopped state, i.e. after target start. + * Some can also be migrated during precopy (RAM). + * Some must be migrated after source stops + * (block-dirty-bitmap) */ + void (*state_pending_estimate)(void *opaque, uint64_t *must_precopy, + uint64_t *can_postcopy); + /** + * @state_pending_exact + * + * This calculates the exact remaining data to transfer + * + * Sum of @can_postcopy and @must_postcopy is the whole amount of + * pending data. + * + * @opaque: data pointer passed to register_savevm_live() + * @must_precopy: amount of data that must be migrated in precopy + * or in stopped state, i.e. that must be migrated + * before target start. + * @can_postcopy: amount of data that can be migrated in postcopy + * or in stopped state, i.e. after target start. + * Some can also be migrated during precopy (RAM). + * Some must be migrated after source stops + * (block-dirty-bitmap) + */ + void (*state_pending_exact)(void *opaque, uint64_t *must_precopy, + uint64_t *can_postcopy); - LoadStateHandler *load_state; + /** + * @load_state + * + * Load sections generated by any of the save functions that + * generate sections. + * + * Legacy method. Should be deprecated when all users are ported + * to VMStateDescription. + * + * @f: QEMUFile where to receive the data + * @opaque: data pointer passed to register_savevm_live() + * @version_id: the maximum version_id supported + * + * Returns zero to indicate success and negative for error + */ + int (*load_state)(QEMUFile *f, void *opaque, int version_id); + + /** + * @load_setup + * + * Initializes the data structures on the destination. + * + * @f: QEMUFile where to receive the data + * @opaque: data pointer passed to register_savevm_live() + * + * Returns zero to indicate success and negative for error + */ int (*load_setup)(QEMUFile *f, void *opaque); + + /** + * @load_cleanup + * + * Uninitializes the data structures on the destination. + * + * @opaque: data pointer passed to register_savevm_live() + * + * Returns zero to indicate success and negative for error + */ int (*load_cleanup)(void *opaque); - /* Called when postcopy migration wants to resume from failure */ + + /** + * @resume_prepare + * + * Called when postcopy migration wants to resume from failure + * + * @s: Current migration state + * @opaque: data pointer passed to register_savevm_live() + * + * Returns zero to indicate success and negative for error + */ int (*resume_prepare)(MigrationState *s, void *opaque); + + /** + * @switchover_ack_needed + * + * Checks if switchover ack should be used. Called only on + * destination. + * + * @opaque: data pointer passed to register_savevm_live() + * + * Returns true if switchover ack should be used and false + * otherwise + */ + bool (*switchover_ack_needed)(void *opaque); } SaveVMHandlers; +/** + * register_savevm_live: Register a set of custom migration handlers + * + * @idstr: state section identifier + * @instance_id: instance id + * @version_id: version id supported + * @ops: SaveVMHandlers structure + * @opaque: data pointer passed to SaveVMHandlers handlers + */ int register_savevm_live(const char *idstr, uint32_t instance_id, int version_id, const SaveVMHandlers *ops, void *opaque); +/** + * unregister_savevm: Unregister custom migration handlers + * + * @obj: object associated with state section + * @idstr: state section identifier + * @opaque: data pointer passed to register_savevm_live() + */ void unregister_savevm(VMStateIf *obj, const char *idstr, void *opaque); #endif diff --git a/include/migration/snapshot.h b/include/migration/snapshot.h index e72083b117..9e4dcaaa75 100644 --- a/include/migration/snapshot.h +++ b/include/migration/snapshot.h @@ -16,6 +16,7 @@ #define QEMU_MIGRATION_SNAPSHOT_H #include "qapi/qapi-builtin-types.h" +#include "qapi/qapi-types-run-state.h" /** * save_snapshot: Save an internal snapshot. @@ -61,4 +62,10 @@ bool delete_snapshot(const char *name, bool has_devices, strList *devices, Error **errp); +/** + * load_snapshot_resume: Restore runstate after loading snapshot. + * @state: state to restore + */ +void load_snapshot_resume(RunState state); + #endif diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index ad24aa1934..294d2d8486 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -41,9 +41,11 @@ typedef struct VMStateField VMStateField; */ struct VMStateInfo { const char *name; - int (*get)(QEMUFile *f, void *pv, size_t size, const VMStateField *field); - int (*put)(QEMUFile *f, void *pv, size_t size, const VMStateField *field, - JSONWriter *vmdesc); + int coroutine_mixed_fn (*get)(QEMUFile *f, void *pv, size_t size, + const VMStateField *field); + int coroutine_mixed_fn (*put)(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, + JSONWriter *vmdesc); }; enum VMStateFlags { @@ -147,6 +149,9 @@ enum VMStateFlags { * VMStateField.struct_version_id to tell which version of the * structure we are referencing to use. */ VMS_VSTRUCT = 0x8000, + + /* Marker for end of list */ + VMS_END = 0x10000 }; typedef enum { @@ -178,7 +183,21 @@ struct VMStateField { struct VMStateDescription { const char *name; - int unmigratable; + bool unmigratable; + /* + * This VMSD describes something that should be sent during setup phase + * of migration. It plays similar role as save_setup() for explicitly + * registered vmstate entries, so it can be seen as a way to describe + * save_setup() in VMSD structures. + * + * Note that for now, a SaveStateEntry cannot have a VMSD and + * operations (e.g., save_setup()) set at the same time. Consequently, + * save_setup() and a VMSD with early_setup set to true are mutually + * exclusive. For this reason, also early_setup VMSDs are migrated in a + * QEMU_VM_SECTION_FULL section, while save_setup() data is migrated in + * a QEMU_VM_SECTION_START section. + */ + bool early_setup; int version_id; int minimum_version_id; MigrationPriority priority; @@ -190,7 +209,7 @@ struct VMStateDescription { bool (*dev_unplug_pending)(void *opaque); const VMStateField *fields; - const VMStateDescription **subsections; + const VMStateDescription * const *subsections; }; extern const VMStateInfo vmstate_info_bool; @@ -705,8 +724,9 @@ extern const VMStateInfo vmstate_info_qlist; * '_state' type * That the pointer is right at the start of _tmp_type. */ -#define VMSTATE_WITH_TMP(_state, _tmp_type, _vmsd) { \ +#define VMSTATE_WITH_TMP_TEST(_state, _test, _tmp_type, _vmsd) { \ .name = "tmp", \ + .field_exists = (_test), \ .size = sizeof(_tmp_type) + \ QEMU_BUILD_BUG_ON_ZERO(offsetof(_tmp_type, parent) != 0) + \ type_check_pointer(_state, \ @@ -715,6 +735,9 @@ extern const VMStateInfo vmstate_info_qlist; .info = &vmstate_info_tmp, \ } +#define VMSTATE_WITH_TMP(_state, _tmp_type, _vmsd) \ + VMSTATE_WITH_TMP_TEST(_state, NULL, _tmp_type, _vmsd) + #define VMSTATE_UNUSED_BUFFER(_test, _version, _size) { \ .name = "unused", \ .field_exists = (_test), \ @@ -738,8 +761,9 @@ extern const VMStateInfo vmstate_info_qlist; /* _field_size should be a int32_t field in the _state struct giving the * size of the bitmap _field in bits. */ -#define VMSTATE_BITMAP(_field, _state, _version, _field_size) { \ +#define VMSTATE_BITMAP_TEST(_field, _state, _test, _version, _field_size) { \ .name = (stringify(_field)), \ + .field_exists = (_test), \ .version_id = (_version), \ .size_offset = vmstate_offset_value(_state, _field_size, int32_t),\ .info = &vmstate_info_bitmap, \ @@ -747,6 +771,9 @@ extern const VMStateInfo vmstate_info_qlist; .offset = offsetof(_state, _field), \ } +#define VMSTATE_BITMAP(_field, _state, _version, _field_size) \ + VMSTATE_BITMAP_TEST(_field, _state, NULL, _version, _field_size) + /* For migrating a QTAILQ. * Target QTAILQ needs be properly initialized. * _type: type of QTAILQ element @@ -1161,17 +1188,21 @@ extern const VMStateInfo vmstate_info_qlist; VMSTATE_UNUSED_BUFFER(_test, 0, _size) #define VMSTATE_END_OF_LIST() \ - {} + { \ + .flags = VMS_END, \ + } int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id); int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, JSONWriter *vmdesc); +int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque, JSONWriter *vmdesc, Error **errp); int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, JSONWriter *vmdesc, - int version_id); + int version_id, Error **errp); -bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque); +bool vmstate_section_needed(const VMStateDescription *vmsd, void *opaque); #define VMSTATE_INSTANCE_ID_ANY -1 @@ -1182,7 +1213,15 @@ int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id, int required_for_version, Error **errp); -/* Returns: 0 on success, -1 on failure */ +/** + * vmstate_register() - legacy function to register state + * serialisation description + * + * New code shouldn't be using this function as QOM-ified devices have + * dc->vmsd to store the serialisation description. + * + * Returns: 0 on success, -1 on failure + */ static inline int vmstate_register(VMStateIf *obj, int instance_id, const VMStateDescription *vmsd, void *opaque) @@ -1191,6 +1230,34 @@ static inline int vmstate_register(VMStateIf *obj, int instance_id, opaque, -1, 0, NULL); } +/** + * vmstate_replace_hack_for_ppc() - ppc used to abuse vmstate_register + * + * Don't even think about using this function in new code. + * + * Returns: 0 on success, -1 on failure + */ +int vmstate_replace_hack_for_ppc(VMStateIf *obj, int instance_id, + const VMStateDescription *vmsd, + void *opaque); + +/** + * vmstate_register_any() - legacy function to register state + * serialisation description and let the function choose the id + * + * New code shouldn't be using this function as QOM-ified devices have + * dc->vmsd to store the serialisation description. + * + * Returns: 0 on success, -1 on failure + */ +static inline int vmstate_register_any(VMStateIf *obj, + const VMStateDescription *vmsd, + void *opaque) +{ + return vmstate_register_with_alias_id(obj, VMSTATE_INSTANCE_ID_ANY, vmsd, + opaque, -1, 0, NULL); +} + void vmstate_unregister(VMStateIf *obj, const VMStateDescription *vmsd, void *opaque); diff --git a/include/monitor/hmp-target.h b/include/monitor/hmp-target.h index 1891a19b21..d78e979f05 100644 --- a/include/monitor/hmp-target.h +++ b/include/monitor/hmp-target.h @@ -51,5 +51,11 @@ void hmp_info_local_apic(Monitor *mon, const QDict *qdict); void hmp_info_sev(Monitor *mon, const QDict *qdict); void hmp_info_sgx(Monitor *mon, const QDict *qdict); void hmp_info_via(Monitor *mon, const QDict *qdict); +void hmp_memory_dump(Monitor *mon, const QDict *qdict); +void hmp_physical_memory_dump(Monitor *mon, const QDict *qdict); +void hmp_info_registers(Monitor *mon, const QDict *qdict); +void hmp_gva2gpa(Monitor *mon, const QDict *qdict); +void hmp_gpa2hva(Monitor *mon, const QDict *qdict); +void hmp_gpa2hpa(Monitor *mon, const QDict *qdict); #endif /* MONITOR_HMP_TARGET_H */ diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 96d014826a..13f9a2dedb 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -18,6 +18,8 @@ #include "qapi/qapi-types-common.h" bool hmp_handle_error(Monitor *mon, Error *err); +void hmp_help_cmd(Monitor *mon, const char *name); +strList *hmp_split_at_comma(const char *str); void hmp_info_name(Monitor *mon, const QDict *qdict); void hmp_info_version(Monitor *mon, const QDict *qdict); @@ -54,6 +56,7 @@ void hmp_ringbuf_read(Monitor *mon, const QDict *qdict); void hmp_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_nmi(Monitor *mon, const QDict *qdict); +void hmp_info_network(Monitor *mon, const QDict *qdict); void hmp_set_link(Monitor *mon, const QDict *qdict); void hmp_balloon(Monitor *mon, const QDict *qdict); void hmp_loadvm(Monitor *mon, const QDict *qdict); @@ -72,6 +75,14 @@ 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_change(Monitor *mon, const QDict *qdict); +#ifdef CONFIG_VNC +void hmp_change_vnc(Monitor *mon, const char *device, const char *target, + const char *arg, const char *read_only, bool force, + Error **errp); +#endif +void hmp_change_medium(Monitor *mon, const char *device, const char *target, + const char *arg, const char *read_only, bool force, + Error **errp); void hmp_migrate(Monitor *mon, const QDict *qdict); void hmp_device_add(Monitor *mon, const QDict *qdict); void hmp_device_del(Monitor *mon, const QDict *qdict); @@ -80,8 +91,11 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict); void hmp_netdev_del(Monitor *mon, const QDict *qdict); void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); +void hmp_mouse_move(Monitor *mon, const QDict *qdict); +void hmp_mouse_button(Monitor *mon, const QDict *qdict); +void hmp_mouse_set(Monitor *mon, const QDict *qdict); void hmp_sendkey(Monitor *mon, const QDict *qdict); -void hmp_screendump(Monitor *mon, const QDict *qdict); +void coroutine_fn hmp_screendump(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); @@ -95,6 +109,13 @@ void hmp_qom_list(Monitor *mon, const QDict *qdict); void hmp_qom_get(Monitor *mon, const QDict *qdict); void hmp_qom_set(Monitor *mon, const QDict *qdict); void hmp_info_qom_tree(Monitor *mon, const QDict *dict); +void hmp_virtio_query(Monitor *mon, const QDict *qdict); +void hmp_virtio_status(Monitor *mon, const QDict *qdict); +void hmp_virtio_queue_status(Monitor *mon, const QDict *qdict); +void hmp_vhost_queue_status(Monitor *mon, const QDict *qdict); +void hmp_virtio_queue_element(Monitor *mon, const QDict *qdict); +void hmp_xen_event_inject(Monitor *mon, const QDict *qdict); +void hmp_xen_event_list(Monitor *mon, const QDict *qdict); void object_add_completion(ReadLineState *rs, int nb_args, const char *str); void object_del_completion(ReadLineState *rs, int nb_args, const char *str); void device_add_completion(ReadLineState *rs, int nb_args, const char *str); @@ -131,7 +152,34 @@ void hmp_replay_delete_break(Monitor *mon, const QDict *qdict); void hmp_replay_seek(Monitor *mon, const QDict *qdict); void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict); void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict); +void hmp_set_vcpu_dirty_limit(Monitor *mon, const QDict *qdict); +void hmp_cancel_vcpu_dirty_limit(Monitor *mon, const QDict *qdict); +void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict); void hmp_human_readable_text_helper(Monitor *mon, HumanReadableText *(*qmp_handler)(Error **)); +void hmp_info_stats(Monitor *mon, const QDict *qdict); +void hmp_one_insn_per_tb(Monitor *mon, const QDict *qdict); +void hmp_watchdog_action(Monitor *mon, const QDict *qdict); +void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict); +void hmp_info_capture(Monitor *mon, const QDict *qdict); +void hmp_stopcapture(Monitor *mon, const QDict *qdict); +void hmp_wavcapture(Monitor *mon, const QDict *qdict); +void hmp_trace_event(Monitor *mon, const QDict *qdict); +void hmp_trace_file(Monitor *mon, const QDict *qdict); +void hmp_info_trace_events(Monitor *mon, const QDict *qdict); +void hmp_help(Monitor *mon, const QDict *qdict); +void hmp_info_help(Monitor *mon, const QDict *qdict); +void hmp_info_sync_profile(Monitor *mon, const QDict *qdict); +void hmp_info_history(Monitor *mon, const QDict *qdict); +void hmp_logfile(Monitor *mon, const QDict *qdict); +void hmp_log(Monitor *mon, const QDict *qdict); +void hmp_gdbserver(Monitor *mon, const QDict *qdict); +void hmp_print(Monitor *mon, const QDict *qdict); +void hmp_sum(Monitor *mon, const QDict *qdict); +void hmp_ioport_read(Monitor *mon, const QDict *qdict); +void hmp_ioport_write(Monitor *mon, const QDict *qdict); +void hmp_boot_set(Monitor *mon, const QDict *qdict); +void hmp_info_mtree(Monitor *mon, const QDict *qdict); +void hmp_info_cryptodev(Monitor *mon, const QDict *qdict); #endif diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index a4b40e8391..965f5d5450 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -31,13 +31,18 @@ void monitor_resume(Monitor *mon); int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp); int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp); +int monitor_puts(Monitor *mon, const char *str); int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) G_GNUC_PRINTF(2, 0); int monitor_printf(Monitor *mon, const char *fmt, ...) G_GNUC_PRINTF(2, 3); +void monitor_printc(Monitor *mon, int ch); void monitor_flush(Monitor *mon); int monitor_set_cpu(Monitor *mon, int cpu_index); int monitor_get_cpu_index(Monitor *mon); +int monitor_puts_locked(Monitor *mon, const char *str); +void monitor_flush_locked(Monitor *mon); + void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, uint64_t size, Error **errp); void monitor_read_command(MonitorHMP *mon, int show_prompt); @@ -45,8 +50,7 @@ int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func, void *opaque); AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, - bool has_opaque, const char *opaque, - Error **errp); + const char *opaque, Error **errp); int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags); void monitor_fdset_dup_fd_remove(int dup_fd); int64_t monitor_fdset_dup_fd_find(int dup_fd); diff --git a/include/monitor/qmp-helpers.h b/include/monitor/qmp-helpers.h new file mode 100644 index 0000000000..318c3357a2 --- /dev/null +++ b/include/monitor/qmp-helpers.h @@ -0,0 +1,29 @@ +/* + * QMP command helpers + * + * Copyright (c) 2022 Red Hat Inc. + * + * Authors: + * Markus Armbruster + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef MONITOR_QMP_HELPERS_H + +bool qmp_add_client_spice(int fd, bool has_skipauth, bool skipauth, + bool has_tls, bool tls, Error **errp); +#ifdef CONFIG_VNC +bool qmp_add_client_vnc(int fd, bool has_skipauth, bool skipauth, + bool has_tls, bool tls, Error **errp); +#endif +#ifdef CONFIG_DBUS_DISPLAY +bool qmp_add_client_dbus_display(int fd, bool has_skipauth, bool skipauth, + bool has_tls, bool tls, Error **errp); +#endif +bool qmp_add_client_char(int fd, bool has_skipauth, bool skipauth, + bool has_tls, bool tls, const char *protocol, + Error **errp); + +#endif diff --git a/include/net/eth.h b/include/net/eth.h index 6e699b0d7a..3b80b6e07f 100644 --- a/include/net/eth.h +++ b/include/net/eth.h @@ -32,6 +32,8 @@ #define ETH_ALEN 6 #define ETH_HLEN 14 #define ETH_ZLEN 60 /* Min. octets in frame without FCS */ +#define ETH_FCS_LEN 4 +#define ETH_MTU 1500 struct eth_header { uint8_t h_dest[ETH_ALEN]; /* destination eth addr */ @@ -222,6 +224,7 @@ struct tcp_hdr { #define IP_HEADER_VERSION_6 (6) #define IP_PROTO_TCP (6) #define IP_PROTO_UDP (17) +#define IP_PROTO_SCTP (132) #define IPTOS_ECN_MASK 0x03 #define IPTOS_ECN(x) ((x) & IPTOS_ECN_MASK) #define IPTOS_ECN_CE 0x03 @@ -312,10 +315,10 @@ eth_get_l2_hdr_length(const void *p) } static inline uint32_t -eth_get_l2_hdr_length_iov(const struct iovec *iov, int iovcnt) +eth_get_l2_hdr_length_iov(const struct iovec *iov, size_t iovcnt, size_t iovoff) { uint8_t p[sizeof(struct eth_header) + sizeof(struct vlan_header)]; - size_t copied = iov_to_buf(iov, iovcnt, 0, p, ARRAY_SIZE(p)); + size_t copied = iov_to_buf(iov, iovcnt, iovoff, p, ARRAY_SIZE(p)); if (copied < ARRAY_SIZE(p)) { return copied; @@ -340,26 +343,19 @@ eth_get_pkt_tci(const void *p) size_t eth_strip_vlan(const struct iovec *iov, int iovcnt, size_t iovoff, - uint8_t *new_ehdr_buf, + void *new_ehdr_buf, uint16_t *payload_offset, uint16_t *tci); size_t -eth_strip_vlan_ex(const struct iovec *iov, int iovcnt, size_t iovoff, - uint16_t vet, uint8_t *new_ehdr_buf, +eth_strip_vlan_ex(const struct iovec *iov, int iovcnt, size_t iovoff, int index, + uint16_t vet, uint16_t vet_ext, void *new_ehdr_buf, uint16_t *payload_offset, uint16_t *tci); uint16_t eth_get_l3_proto(const struct iovec *l2hdr_iov, int iovcnt, size_t l2hdr_len); -void eth_setup_vlan_headers_ex(struct eth_header *ehdr, uint16_t vlan_tag, - uint16_t vlan_ethtype, bool *is_new); - -static inline void -eth_setup_vlan_headers(struct eth_header *ehdr, uint16_t vlan_tag, - bool *is_new) -{ - eth_setup_vlan_headers_ex(ehdr, vlan_tag, ETH_P_VLAN, is_new); -} +void eth_setup_vlan_headers(struct eth_header *ehdr, size_t *ehdr_size, + uint16_t vlan_tag, uint16_t vlan_ethtype); uint8_t eth_get_gso_type(uint16_t l3_proto, uint8_t *l3_hdr, uint8_t l4proto); @@ -381,18 +377,25 @@ typedef struct eth_ip4_hdr_info_st { bool fragment; } eth_ip4_hdr_info; +typedef enum EthL4HdrProto { + ETH_L4_HDR_PROTO_INVALID, + ETH_L4_HDR_PROTO_TCP, + ETH_L4_HDR_PROTO_UDP, + ETH_L4_HDR_PROTO_SCTP +} EthL4HdrProto; + typedef struct eth_l4_hdr_info_st { union { struct tcp_header tcp; struct udp_header udp; } hdr; + EthL4HdrProto proto; bool has_tcp_data; } eth_l4_hdr_info; -void eth_get_protocols(const struct iovec *iov, int iovcnt, - bool *isip4, bool *isip6, - bool *isudp, bool *istcp, +void eth_get_protocols(const struct iovec *iov, size_t iovcnt, size_t iovoff, + bool *hasip4, bool *hasip6, size_t *l3hdr_off, size_t *l4hdr_off, size_t *l5hdr_off, @@ -400,11 +403,6 @@ void eth_get_protocols(const struct iovec *iov, int iovcnt, eth_ip4_hdr_info *ip4hdr_info, eth_l4_hdr_info *l4hdr_info); -void eth_setup_ip4_fragmentation(const void *l2hdr, size_t l2hdr_len, - void *l3hdr, size_t l3hdr_len, - size_t l3payload_len, - size_t frag_offset, bool more_frags); - void eth_fix_ip4_checksum(void *l3hdr, size_t l3hdr_len); diff --git a/include/net/filter.h b/include/net/filter.h index 27ffc630df..f15f7932b2 100644 --- a/include/net/filter.h +++ b/include/net/filter.h @@ -9,7 +9,7 @@ #ifndef QEMU_NET_FILTER_H #define QEMU_NET_FILTER_H -#include "qapi/qapi-types-net.h" +#include "qapi/qapi-types-common.h" #include "qemu/queue.h" #include "qom/object.h" #include "net/queue.h" diff --git a/include/net/net.h b/include/net/net.h index 523136c7ac..b1f9b35fcc 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -44,6 +44,9 @@ typedef struct NICConf { typedef void (NetPoll)(NetClientState *, bool enable); typedef bool (NetCanReceive)(NetClientState *); +typedef int (NetStart)(NetClientState *); +typedef int (NetLoad)(NetClientState *); +typedef void (NetStop)(NetClientState *); typedef ssize_t (NetReceive)(NetClientState *, const uint8_t *, size_t); typedef ssize_t (NetReceiveIOV)(NetClientState *, const struct iovec *, int); typedef void (NetCleanup) (NetClientState *); @@ -51,10 +54,13 @@ typedef void (LinkStatusChanged)(NetClientState *); typedef void (NetClientDestructor)(NetClientState *); typedef RxFilterInfo *(QueryRxFilter)(NetClientState *); typedef bool (HasUfo)(NetClientState *); +typedef bool (HasUso)(NetClientState *); typedef bool (HasVnetHdr)(NetClientState *); typedef bool (HasVnetHdrLen)(NetClientState *, int); +typedef bool (GetUsingVnetHdr)(NetClientState *); typedef void (UsingVnetHdr)(NetClientState *, bool); -typedef void (SetOffload)(NetClientState *, int, int, int, int, int); +typedef void (SetOffload)(NetClientState *, int, int, int, int, int, int, int); +typedef int (GetVnetHdrLen)(NetClientState *); typedef void (SetVnetHdrLen)(NetClientState *, int); typedef int (SetVnetLE)(NetClientState *, bool); typedef int (SetVnetBE)(NetClientState *, bool); @@ -71,15 +77,21 @@ typedef struct NetClientInfo { NetReceive *receive_raw; NetReceiveIOV *receive_iov; NetCanReceive *can_receive; + NetStart *start; + NetLoad *load; + NetStop *stop; NetCleanup *cleanup; LinkStatusChanged *link_status_changed; QueryRxFilter *query_rx_filter; NetPoll *poll; HasUfo *has_ufo; + HasUso *has_uso; HasVnetHdr *has_vnet_hdr; HasVnetHdrLen *has_vnet_hdr_len; + GetUsingVnetHdr *get_using_vnet_hdr; UsingVnetHdr *using_vnet_hdr; SetOffload *set_offload; + GetVnetHdrLen *get_vnet_hdr_len; SetVnetHdrLen *set_vnet_hdr_len; SetVnetLE *set_vnet_le; SetVnetBE *set_vnet_be; @@ -109,9 +121,12 @@ struct NetClientState { QTAILQ_HEAD(, NetFilterState) filters; }; +typedef QTAILQ_HEAD(NetClientStateList, NetClientState) NetClientStateList; + typedef struct NICState { NetClientState *ncs; NICConf *conf; + MemReentrancyGuard *reentrancy_guard; void *opaque; bool peer_deleted; } NICState; @@ -145,6 +160,7 @@ NICState *qemu_new_nic(NetClientInfo *info, NICConf *conf, const char *model, const char *name, + MemReentrancyGuard *reentrancy_guard, void *opaque); void qemu_del_nic(NICState *nic); NetClientState *qemu_get_subqueue(NICState *nic, int queue_index); @@ -171,29 +187,105 @@ ssize_t qemu_send_packet_async(NetClientState *nc, const uint8_t *buf, void qemu_purge_queued_packets(NetClientState *nc); void qemu_flush_queued_packets(NetClientState *nc); void qemu_flush_or_purge_queued_packets(NetClientState *nc, bool purge); +void qemu_set_info_str(NetClientState *nc, + const char *fmt, ...) G_GNUC_PRINTF(2, 3); void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6]); bool qemu_has_ufo(NetClientState *nc); +bool qemu_has_uso(NetClientState *nc); bool qemu_has_vnet_hdr(NetClientState *nc); bool qemu_has_vnet_hdr_len(NetClientState *nc, int len); +bool qemu_get_using_vnet_hdr(NetClientState *nc); void qemu_using_vnet_hdr(NetClientState *nc, bool enable); void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6, - int ecn, int ufo); + int ecn, int ufo, int uso4, int uso6); +int qemu_get_vnet_hdr_len(NetClientState *nc); void qemu_set_vnet_hdr_len(NetClientState *nc, int len); int qemu_set_vnet_le(NetClientState *nc, bool is_le); int qemu_set_vnet_be(NetClientState *nc, bool is_be); void qemu_macaddr_default_if_unset(MACAddr *macaddr); -int qemu_show_nic_models(const char *arg, const char *const *models); -void qemu_check_nic_model(NICInfo *nd, const char *model); -int qemu_find_nic_model(NICInfo *nd, const char * const *models, - const char *default_model); +/** + * qemu_find_nic_info: Obtain NIC configuration information + * @typename: Name of device object type + * @match_default: Match NIC configurations with no model specified + * @alias: Additional model string to match (for user convenience and + * backward compatibility). + * + * Search for a NIC configuration matching the NIC model constraints. + */ +NICInfo *qemu_find_nic_info(const char *typename, bool match_default, + const char *alias); +/** + * qemu_configure_nic_device: Apply NIC configuration to a given device + * @dev: Network device to be configured + * @match_default: Match NIC configurations with no model specified + * @alias: Additional model string to match + * + * Search for a NIC configuration for the provided device, using the + * additionally specified matching constraints. If found, apply the + * configuration using qdev_set_nic_properties() and return %true. + * + * This is used by platform code which creates the device anyway, + * regardless of whether there is a configuration for it. This tends + * to be platforms which ignore `--nodefaults` and create net devices + * anyway, for example because the Ethernet device on that board is + * always physically present. + */ +bool qemu_configure_nic_device(DeviceState *dev, bool match_default, + const char *alias); +/** + * qemu_create_nic_device: Create a NIC device if a configuration exists for it + * @typename: Object typename of network device + * @match_default: Match NIC configurations with no model specified + * @alias: Additional model string to match + * + * Search for a NIC configuration for the provided device type. If found, + * create an object of the corresponding type and return it. + */ +DeviceState *qemu_create_nic_device(const char *typename, bool match_default, + const char *alias); + +/* + * qemu_create_nic_bus_devices: Create configured NIC devices for a given bus + * @bus: Bus on which to create devices + * @parent_type: Object type for devices to be created (e.g. TYPE_PCI_DEVICE) + * @default_model: Object type name for default NIC model (or %NULL) + * @alias: Additional model string to replace, for user convenience + * @alias_target: Actual object type name to be used in place of @alias + * + * Instantiate dynamic NICs on a given bus, typically a PCI bus. This scans + * for available NIC configurations which either specify a model which is + * a child type of @parent_type, or which do not specify a model when + * @default_model is non-NULL. Each device is instantiated on the given @bus. + * + * A single substitution is supported, e.g. "xen" → "xen-net-device" for the + * Xen bus, or "virtio" → "virtio-net-pci" for PCI. This allows the user to + * specify a more understandable "model=" parameter on the command line, not + * only the real object typename. + */ +void qemu_create_nic_bus_devices(BusState *bus, const char *parent_type, + const char *default_model, + const char *alias, const char *alias_target); void print_net_client(Monitor *mon, NetClientState *nc); -void hmp_info_network(Monitor *mon, const QDict *qdict); void net_socket_rs_init(SocketReadState *rs, SocketReadStateFinalize *finalize, bool vnet_hdr); NetClientState *qemu_get_peer(NetClientState *nc, int queue_index); +/** + * qemu_get_nic_models: + * @device_type: Defines which devices should be taken into consideration + * (e.g. TYPE_DEVICE for all devices, or TYPE_PCI_DEVICE for PCI) + * + * Get an array of pointers to names of NIC devices that are available in + * the QEMU binary. The array is terminated with a NULL pointer entry. + * The caller is responsible for freeing the memory when it is not required + * anymore, e.g. with g_ptr_array_free(..., true). + * + * Returns: Pointer to the array that contains the pointers to the names. + */ +GPtrArray *qemu_get_nic_models(const char *device_type); + /* NIC info */ #define MAX_NICS 8 @@ -209,14 +301,13 @@ struct NICInfo { int nvectors; }; -extern int nb_nics; -extern NICInfo nd_table[MAX_NICS]; -extern const char *host_net_devices[]; - /* from net.c */ -int net_client_parse(QemuOptsList *opts_list, const char *str); +extern NetClientStateList net_clients; +bool netdev_is_modern(const char *optstr); +void netdev_parse_modern(const char *optstr); +void net_client_parse(QemuOptsList *opts_list, const char *optstr); void show_netdevs(void); -int net_init_clients(Error **errp); +void net_init_clients(void); void net_check_clients(void); void net_cleanup(void); void hmp_host_net_add(Monitor *mon, const QDict *qdict); diff --git a/include/net/vhost-user.h b/include/net/vhost-user.h index 5bcd8a6285..35bf619709 100644 --- a/include/net/vhost-user.h +++ b/include/net/vhost-user.h @@ -14,5 +14,6 @@ struct vhost_net; struct vhost_net *vhost_user_get_vhost_net(NetClientState *nc); uint64_t vhost_user_get_acked_features(NetClientState *nc); +void vhost_user_save_acked_features(NetClientState *nc); #endif /* VHOST_USER_H */ diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index 387e913e4e..c6a5361a2a 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -4,9 +4,6 @@ #include "net/net.h" #include "hw/virtio/vhost-backend.h" -#define VHOST_NET_INIT_FAILED \ - "vhost-net requested but could not be initialized" - struct vhost_net; typedef struct vhost_net VHostNetState; @@ -39,6 +36,8 @@ int vhost_net_set_config(struct vhost_net *net, const uint8_t *data, bool vhost_net_virtqueue_pending(VHostNetState *net, int n); void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, int idx, bool mask); +bool vhost_net_config_pending(VHostNetState *net); +void vhost_net_config_mask(VHostNetState *net, VirtIODevice *dev, bool mask); int vhost_net_notify_migration_done(VHostNetState *net, char* mac_addr); VHostNetState *get_vhost_net(NetClientState *nc); @@ -48,4 +47,10 @@ uint64_t vhost_net_get_acked_features(VHostNetState *net); int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu); +void vhost_net_virtqueue_reset(VirtIODevice *vdev, NetClientState *nc, + int vq_index); +int vhost_net_virtqueue_restart(VirtIODevice *vdev, NetClientState *nc, + int vq_index); + +void vhost_net_save_acked_features(NetClientState *nc); #endif diff --git a/include/qapi/error.h b/include/qapi/error.h index d798faeec3..71f8fb2c50 100644 --- a/include/qapi/error.h +++ b/include/qapi/error.h @@ -207,7 +207,7 @@ * * Without ERRP_GUARD(), use of the @errp parameter is restricted: * - It must not be dereferenced, because it may be null. - * - It should not be passed to error_prepend() or + * - It should not be passed to error_prepend(), error_vprepend(), or * error_append_hint(), because that doesn't work with &error_fatal. * ERRP_GUARD() lifts these restrictions. * @@ -519,6 +519,12 @@ static inline void error_propagator_cleanup(ErrorPropagator *prop) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(ErrorPropagator, error_propagator_cleanup); +/* + * Special error destination to warn on error. + * See error_setg() and error_propagate() for details. + */ +extern Error *error_warn; + /* * Special error destination to abort on error. * See error_setg() and error_propagate() for details. diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 1e4240fd0d..f2e956813a 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -55,8 +55,8 @@ bool qmp_command_available(const QmpCommand *cmd, Error **errp); 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(const QmpCommandList *cmds, QObject *request, - bool allow_oob, Monitor *cur_mon); +QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *request, + bool allow_oob, Monitor *cur_mon); bool qmp_is_oob(const QDict *dict); typedef void (*qmp_cmd_callback_fn)(const QmpCommand *cmd, void *opaque); diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 596fce0c54..0c2689cf8a 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -23,15 +23,9 @@ #define QERR_DEVICE_HAS_NO_MEDIUM \ "Device '%s' has no medium" -#define QERR_DEVICE_IN_USE \ - "Device '%s' is in use" - #define QERR_DEVICE_NO_HOTPLUG \ "Device '%s' does not support hotplugging" -#define QERR_FEATURE_DISABLED \ - "The feature '%s' is not enabled" - #define QERR_INVALID_PARAMETER \ "Invalid parameter '%s'" @@ -50,9 +44,6 @@ #define QERR_MISSING_PARAMETER \ "Parameter '%s' is missing" -#define QERR_PERMISSION_DENIED \ - "Insufficient permission to perform this operation" - #define QERR_PROPERTY_VALUE_BAD \ "Property '%s.%s' doesn't take value '%s'" @@ -62,9 +53,6 @@ #define QERR_QGA_COMMAND_FAILED \ "Guest agent command failed, error was '%s'" -#define QERR_REPLAY_NOT_SUPPORTED \ - "Record/replay feature is not supported for '%s'" - #define QERR_UNSUPPORTED \ "this feature or command is not currently supported" diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h index 9003b71fd3..89b97d88bc 100644 --- a/include/qapi/qmp/qobject.h +++ b/include/qapi/qmp/qobject.h @@ -45,10 +45,16 @@ struct QObject { struct QObjectBase_ base; }; -#define QOBJECT(obj) ({ \ +/* + * Preprocessor sorcery ahead: use a different identifier for the + * local variable in each expansion, so we can nest macro calls + * without shadowing variables. + */ +#define QOBJECT_INTERNAL(obj, _obj) ({ \ typeof(obj) _obj = (obj); \ - _obj ? container_of(&(_obj)->base, QObject, base) : NULL; \ + _obj ? container_of(&_obj->base, QObject, base) : NULL; \ }) +#define QOBJECT(obj) QOBJECT_INTERNAL((obj), MAKE_IDENTFIER(_obj)) /* Required for qobject_to() */ #define QTYPE_CAST_TO_QNull QTYPE_QNULL diff --git a/include/qapi/string-output-visitor.h b/include/qapi/string-output-visitor.h index 268dfe9986..b1ee473b30 100644 --- a/include/qapi/string-output-visitor.h +++ b/include/qapi/string-output-visitor.h @@ -26,9 +26,9 @@ typedef struct StringOutputVisitor StringOutputVisitor; * If everything else succeeds, pass @result to visit_complete() to * collect the result of the visit. * - * The string output visitor does not implement support for visiting - * QAPI structs, alternates, null, or arbitrary QTypes. It also - * requires a non-null list argument to visit_start_list(). + * The string output visitor does not implement support for alternates, null, + * or arbitrary QTypes. Struct fields are not shown. It also requires a + * non-null list argument to visit_start_list(). */ Visitor *string_output_visitor_new(bool human, char **result); diff --git a/include/qapi/type-helpers.h b/include/qapi/type-helpers.h index be1f181526..fc8352cdec 100644 --- a/include/qapi/type-helpers.h +++ b/include/qapi/type-helpers.h @@ -12,3 +12,11 @@ #include "qapi/qapi-types-common.h" HumanReadableText *human_readable_text_from_str(GString *str); + +/* + * Produce and return a NULL-terminated array of strings from @list. + * The result is g_malloc()'d and all strings are g_strdup()'d. It + * can be freed with g_strfreev(), or by g_auto(GStrv) automatic + * cleanup. + */ +char **strv_from_str_list(const strList *list); diff --git a/include/qapi/util.h b/include/qapi/util.h index 81a2b13a33..20dfea8a54 100644 --- a/include/qapi/util.h +++ b/include/qapi/util.h @@ -56,4 +56,17 @@ int parse_qapi_name(const char *name, bool complete); (tail) = &(*(tail))->next; \ } while (0) +/* + * For any GenericList @list, return its length. + */ +#define QAPI_LIST_LENGTH(list) \ + ({ \ + size_t _len = 0; \ + typeof(list) _tail; \ + for (_tail = list; _tail != NULL; _tail = _tail->next) { \ + _len++; \ + } \ + _len; \ + }) + #endif diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h index d53a84c9ba..27b85d4700 100644 --- a/include/qapi/visitor.h +++ b/include/qapi/visitor.h @@ -39,7 +39,7 @@ * limitations; see the documentation for each visitor for more * details on what it supports. Also, see visitor-impl.h for the * callback contracts implemented by each visitor, and - * docs/devel/qapi-code-gen.txt for more about the QAPI code + * docs/devel/qapi-code-gen.rst for more about the QAPI code * generator. * * All of the visitors are created via: diff --git a/include/qemu-main.h b/include/qemu-main.h index 6a3e90d0ad..940960a7db 100644 --- a/include/qemu-main.h +++ b/include/qemu-main.h @@ -5,6 +5,7 @@ #ifndef QEMU_MAIN_H #define QEMU_MAIN_H -int qemu_main(int argc, char **argv, char **envp); +int qemu_default_main(void); +extern int (*qemu_main)(void); #endif /* QEMU_MAIN_H */ diff --git a/include/qemu/accel.h b/include/qemu/accel.h index 4f4c283f6f..972a849a2b 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -26,10 +26,10 @@ #include "qom/object.h" #include "exec/hwaddr.h" -typedef struct AccelState { +struct AccelState { /*< private >*/ Object parent_obj; -} AccelState; +}; typedef struct AccelClass { /*< private >*/ @@ -43,6 +43,12 @@ typedef struct AccelClass { bool (*has_memory)(MachineState *ms, AddressSpace *as, hwaddr start_addr, hwaddr size); #endif + bool (*cpu_common_realize)(CPUState *cpu, Error **errp); + void (*cpu_common_unrealize)(CPUState *cpu); + + /* gdbstub related hooks */ + int (*gdbstub_supported_sstep_flags)(void); + bool *allowed; /* * Array of global properties that would be applied when specific @@ -68,6 +74,7 @@ typedef struct AccelClass { AccelClass *accel_find(const char *opt_name); AccelState *current_accel(void); +const char *current_accel_name(void); void accel_init_interfaces(AccelClass *ac); @@ -85,10 +92,24 @@ void accel_setup_post(MachineState *ms); void accel_cpu_instance_init(CPUState *cpu); /** - * accel_cpu_realizefn: + * accel_cpu_common_realize: * @cpu: The CPU that needs to call accel-specific cpu realization. * @errp: currently unused. */ -bool accel_cpu_realizefn(CPUState *cpu, Error **errp); +bool accel_cpu_common_realize(CPUState *cpu, Error **errp); + +/** + * accel_cpu_common_unrealize: + * @cpu: The CPU that needs to call accel-specific cpu unrealization. + */ +void accel_cpu_common_unrealize(CPUState *cpu); + +/** + * accel_supported_gdbstub_sstep_flags: + * + * Returns the supported single step modes for the configured + * accelerator. + */ +int accel_supported_gdbstub_sstep_flags(void); #endif /* QEMU_ACCEL_H */ diff --git a/include/qemu/async-teardown.h b/include/qemu/async-teardown.h new file mode 100644 index 0000000000..b281da005b --- /dev/null +++ b/include/qemu/async-teardown.h @@ -0,0 +1,20 @@ +/* + * Asynchronous teardown + * + * Copyright IBM, Corp. 2022 + * + * Authors: + * Claudio Imbrenda + * + * 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. + * + */ +#ifndef QEMU_ASYNC_TEARDOWN_H +#define QEMU_ASYNC_TEARDOWN_H + +#ifdef CONFIG_LINUX +void init_async_teardown(void); +#endif + +#endif diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index 7e8fc8e7cd..99110abefb 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -133,7 +133,7 @@ #define qatomic_read(ptr) \ ({ \ - QEMU_BUILD_BUG_ON(sizeof(*ptr) > ATOMIC_REG_SIZE); \ + qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ qatomic_read__nocheck(ptr); \ }) @@ -141,7 +141,7 @@ __atomic_store_n(ptr, i, __ATOMIC_RELAXED) #define qatomic_set(ptr, i) do { \ - QEMU_BUILD_BUG_ON(sizeof(*ptr) > ATOMIC_REG_SIZE); \ + qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ qatomic_set__nocheck(ptr, i); \ } while(0) @@ -157,29 +157,36 @@ smp_read_barrier_depends(); #endif -#define qatomic_rcu_read(ptr) \ - ({ \ - QEMU_BUILD_BUG_ON(sizeof(*ptr) > ATOMIC_REG_SIZE); \ - typeof_strip_qual(*ptr) _val; \ - qatomic_rcu_read__nocheck(ptr, &_val); \ - _val; \ +/* + * Preprocessor sorcery ahead: use a different identifier for the + * local variable in each expansion, so we can nest macro calls + * without shadowing variables. + */ +#define qatomic_rcu_read_internal(ptr, _val) \ + ({ \ + qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ + typeof_strip_qual(*ptr) _val; \ + qatomic_rcu_read__nocheck(ptr, &_val); \ + _val; \ }) +#define qatomic_rcu_read(ptr) \ + qatomic_rcu_read_internal((ptr), MAKE_IDENTFIER(_val)) #define qatomic_rcu_set(ptr, i) do { \ - QEMU_BUILD_BUG_ON(sizeof(*ptr) > ATOMIC_REG_SIZE); \ + qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ __atomic_store_n(ptr, i, __ATOMIC_RELEASE); \ } while(0) #define qatomic_load_acquire(ptr) \ ({ \ - QEMU_BUILD_BUG_ON(sizeof(*ptr) > ATOMIC_REG_SIZE); \ + qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ typeof_strip_qual(*ptr) _val; \ __atomic_load(ptr, &_val, __ATOMIC_ACQUIRE); \ _val; \ }) #define qatomic_store_release(ptr, i) do { \ - QEMU_BUILD_BUG_ON(sizeof(*ptr) > ATOMIC_REG_SIZE); \ + qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ __atomic_store_n(ptr, i, __ATOMIC_RELEASE); \ } while(0) @@ -191,11 +198,11 @@ }) #define qatomic_xchg(ptr, i) ({ \ - QEMU_BUILD_BUG_ON(sizeof(*ptr) > ATOMIC_REG_SIZE); \ + qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ qatomic_xchg__nocheck(ptr, i); \ }) -/* Returns the eventual value, failed or not */ +/* Returns the old value of '*ptr' (whether the cmpxchg failed or not) */ #define qatomic_cmpxchg__nocheck(ptr, old, new) ({ \ typeof_strip_qual(*ptr) _old = (old); \ (void)__atomic_compare_exchange_n(ptr, &_old, new, false, \ @@ -204,7 +211,7 @@ }) #define qatomic_cmpxchg(ptr, old, new) ({ \ - QEMU_BUILD_BUG_ON(sizeof(*ptr) > ATOMIC_REG_SIZE); \ + qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ qatomic_cmpxchg__nocheck(ptr, old, new); \ }) @@ -245,23 +252,31 @@ #define smp_wmb() smp_mb_release() #define smp_rmb() smp_mb_acquire() -/* qatomic_mb_read/set semantics map Java volatile variables. They are - * less expensive on some platforms (notably POWER) than fully - * sequentially consistent operations. - * - * As long as they are used as paired operations they are safe to - * use. See docs/devel/atomics.rst for more discussion. +/* + * SEQ_CST is weaker than the older __sync_* builtins and Linux + * kernel read-modify-write atomics. Provide a macro to obtain + * the same semantics. */ +#if !defined(QEMU_SANITIZE_THREAD) && \ + (defined(__i386__) || defined(__x86_64__) || defined(__s390x__)) +# define smp_mb__before_rmw() signal_barrier() +# define smp_mb__after_rmw() signal_barrier() +#else +# define smp_mb__before_rmw() smp_mb() +# define smp_mb__after_rmw() smp_mb() +#endif -#define qatomic_mb_read(ptr) \ - qatomic_load_acquire(ptr) +/* + * On some architectures, qatomic_set_mb is more efficient than a store + * plus a fence. + */ #if !defined(QEMU_SANITIZE_THREAD) && \ (defined(__i386__) || defined(__x86_64__) || defined(__s390x__)) -/* This is more efficient than a store plus a fence. */ -# define qatomic_mb_set(ptr, i) ((void)qatomic_xchg(ptr, i)) +# define qatomic_set_mb(ptr, i) \ + ({ (void)qatomic_xchg(ptr, i); smp_mb__after_rmw(); }) #else -# define qatomic_mb_set(ptr, i) \ +# define qatomic_set_mb(ptr, i) \ ({ qatomic_store_release(ptr, i); smp_mb(); }) #endif diff --git a/include/qemu/atomic128.h b/include/qemu/atomic128.h index adb9a1a260..88af6d4ea3 100644 --- a/include/qemu/atomic128.h +++ b/include/qemu/atomic128.h @@ -15,6 +15,23 @@ #include "qemu/int128.h" +/* + * If __alignof(unsigned __int128) < 16, GCC may refuse to inline atomics + * that are supported by the host, e.g. s390x. We can force the pointer to + * have our known alignment with __builtin_assume_aligned, however prior to + * GCC 13 that was only reliable with optimization enabled. See + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107389 + */ +#if defined(CONFIG_ATOMIC128_OPT) +# if !defined(__OPTIMIZE__) +# define ATTRIBUTE_ATOMIC128_OPT __attribute__((optimize("O1"))) +# endif +# define CONFIG_ATOMIC128 +#endif +#ifndef ATTRIBUTE_ATOMIC128_OPT +# define ATTRIBUTE_ATOMIC128_OPT +#endif + /* * GCC is a house divided about supporting large atomic operations. * @@ -26,8 +43,8 @@ * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878 * * This interpretation is not especially helpful for QEMU. - * For softmmu, all RAM is always read/write from the hypervisor. - * For user-only, if the guest doesn't implement such an __atomic_read + * For system-mode, all RAM is always read/write from the hypervisor. + * For user-mode, if the guest doesn't implement such an __atomic_read * then the host need not worry about it either. * * Moreover, using libatomic is not an option, because its interface is @@ -41,115 +58,7 @@ * Therefore, special case each platform. */ -#if defined(CONFIG_ATOMIC128) -static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) -{ - return qatomic_cmpxchg__nocheck(ptr, cmp, new); -} -# define HAVE_CMPXCHG128 1 -#elif defined(CONFIG_CMPXCHG128) -static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) -{ - return __sync_val_compare_and_swap_16(ptr, cmp, new); -} -# define HAVE_CMPXCHG128 1 -#elif defined(__aarch64__) -/* Through gcc 8, aarch64 has no support for 128-bit at all. */ -static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) -{ - uint64_t cmpl = int128_getlo(cmp), cmph = int128_gethi(cmp); - uint64_t newl = int128_getlo(new), newh = int128_gethi(new); - uint64_t oldl, oldh; - uint32_t tmp; - - asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" - "cmp %[oldl], %[cmpl]\n\t" - "ccmp %[oldh], %[cmph], #0, eq\n\t" - "b.ne 1f\n\t" - "stlxp %w[tmp], %[newl], %[newh], %[mem]\n\t" - "cbnz %w[tmp], 0b\n" - "1:" - : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), - [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) - : [cmpl] "r"(cmpl), [cmph] "r"(cmph), - [newl] "r"(newl), [newh] "r"(newh) - : "memory", "cc"); - - return int128_make128(oldl, oldh); -} -# define HAVE_CMPXCHG128 1 -#else -/* Fallback definition that must be optimized away, or error. */ -Int128 QEMU_ERROR("unsupported atomic") - atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new); -# define HAVE_CMPXCHG128 0 -#endif /* Some definition for HAVE_CMPXCHG128 */ - - -#if defined(CONFIG_ATOMIC128) -static inline Int128 atomic16_read(Int128 *ptr) -{ - return qatomic_read__nocheck(ptr); -} - -static inline void atomic16_set(Int128 *ptr, Int128 val) -{ - qatomic_set__nocheck(ptr, val); -} - -# define HAVE_ATOMIC128 1 -#elif !defined(CONFIG_USER_ONLY) && defined(__aarch64__) -/* We can do better than cmpxchg for AArch64. */ -static inline Int128 atomic16_read(Int128 *ptr) -{ - uint64_t l, h; - uint32_t tmp; - - /* The load must be paired with the store to guarantee not tearing. */ - asm("0: ldxp %[l], %[h], %[mem]\n\t" - "stxp %w[tmp], %[l], %[h], %[mem]\n\t" - "cbnz %w[tmp], 0b" - : [mem] "+m"(*ptr), [tmp] "=r"(tmp), [l] "=r"(l), [h] "=r"(h)); - - return int128_make128(l, h); -} - -static inline void atomic16_set(Int128 *ptr, Int128 val) -{ - uint64_t l = int128_getlo(val), h = int128_gethi(val); - uint64_t t1, t2; - - /* Load into temporaries to acquire the exclusive access lock. */ - asm("0: ldxp %[t1], %[t2], %[mem]\n\t" - "stxp %w[t1], %[l], %[h], %[mem]\n\t" - "cbnz %w[t1], 0b" - : [mem] "+m"(*ptr), [t1] "=&r"(t1), [t2] "=&r"(t2) - : [l] "r"(l), [h] "r"(h)); -} - -# define HAVE_ATOMIC128 1 -#elif !defined(CONFIG_USER_ONLY) && HAVE_CMPXCHG128 -static inline Int128 atomic16_read(Int128 *ptr) -{ - /* Maybe replace 0 with 0, returning the old value. */ - return atomic16_cmpxchg(ptr, 0, 0); -} - -static inline void atomic16_set(Int128 *ptr, Int128 val) -{ - Int128 old = *ptr, cmp; - do { - cmp = old; - old = atomic16_cmpxchg(ptr, cmp, val); - } while (old != cmp); -} - -# define HAVE_ATOMIC128 1 -#else -/* Fallback definitions that must be optimized away, or error. */ -Int128 QEMU_ERROR("unsupported atomic") atomic16_read(Int128 *ptr); -void QEMU_ERROR("unsupported atomic") atomic16_set(Int128 *ptr, Int128 val); -# define HAVE_ATOMIC128 0 -#endif /* Some definition for HAVE_ATOMIC128 */ +#include "host/atomic128-cas.h" +#include "host/atomic128-ldst.h" #endif /* QEMU_ATOMIC128_H */ diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h index 82a1d2f41f..97806811ee 100644 --- a/include/qemu/bitmap.h +++ b/include/qemu/bitmap.h @@ -22,23 +22,23 @@ * Note that nbits should be always a compile time evaluable constant. * Otherwise many inlines will generate horrible code. * - * bitmap_zero(dst, nbits) *dst = 0UL - * bitmap_fill(dst, nbits) *dst = ~0UL - * bitmap_copy(dst, src, nbits) *dst = *src - * bitmap_and(dst, src1, src2, nbits) *dst = *src1 & *src2 - * bitmap_or(dst, src1, src2, nbits) *dst = *src1 | *src2 - * bitmap_xor(dst, src1, src2, nbits) *dst = *src1 ^ *src2 - * bitmap_andnot(dst, src1, src2, nbits) *dst = *src1 & ~(*src2) - * bitmap_complement(dst, src, nbits) *dst = ~(*src) - * bitmap_equal(src1, src2, nbits) Are *src1 and *src2 equal? + * bitmap_zero(dst, nbits) *dst = 0UL + * bitmap_fill(dst, nbits) *dst = ~0UL + * bitmap_copy(dst, src, nbits) *dst = *src + * bitmap_and(dst, src1, src2, nbits) *dst = *src1 & *src2 + * bitmap_or(dst, src1, src2, nbits) *dst = *src1 | *src2 + * bitmap_xor(dst, src1, src2, nbits) *dst = *src1 ^ *src2 + * bitmap_andnot(dst, src1, src2, nbits) *dst = *src1 & ~(*src2) + * bitmap_complement(dst, src, nbits) *dst = ~(*src) + * bitmap_equal(src1, src2, nbits) Are *src1 and *src2 equal? * bitmap_intersects(src1, src2, nbits) Do *src1 and *src2 overlap? - * bitmap_empty(src, nbits) Are all bits zero in *src? - * bitmap_full(src, nbits) Are all bits set in *src? - * bitmap_set(dst, pos, nbits) Set specified bit area - * bitmap_set_atomic(dst, pos, nbits) Set specified bit area with atomic ops - * bitmap_clear(dst, pos, nbits) Clear specified bit area + * bitmap_empty(src, nbits) Are all bits zero in *src? + * bitmap_full(src, nbits) Are all bits set in *src? + * bitmap_set(dst, pos, nbits) Set specified bit area + * bitmap_set_atomic(dst, pos, nbits) Set specified bit area with atomic ops + * bitmap_clear(dst, pos, nbits) Clear specified bit area * bitmap_test_and_clear_atomic(dst, pos, nbits) Test and clear area - * bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area + * bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area * bitmap_to_le(dst, src, nbits) Convert bitmap to little endian * bitmap_from_le(dst, src, nbits) Convert bitmap from little endian * bitmap_copy_with_src_offset(dst, src, offset, nbits) @@ -50,17 +50,17 @@ /* * Also the following operations apply to bitmaps. * - * set_bit(bit, addr) *addr |= bit - * clear_bit(bit, addr) *addr &= ~bit - * change_bit(bit, addr) *addr ^= bit - * test_bit(bit, addr) Is bit set in *addr? - * test_and_set_bit(bit, addr) Set bit and return old value - * test_and_clear_bit(bit, addr) Clear bit and return old value - * test_and_change_bit(bit, addr) Change bit and return old value - * find_first_zero_bit(addr, nbits) Position first zero bit in *addr - * find_first_bit(addr, nbits) Position first set bit in *addr - * find_next_zero_bit(addr, nbits, bit) Position next zero bit in *addr >= bit - * find_next_bit(addr, nbits, bit) Position next set bit in *addr >= bit + * set_bit(bit, addr) *addr |= bit + * clear_bit(bit, addr) *addr &= ~bit + * change_bit(bit, addr) *addr ^= bit + * test_bit(bit, addr) Is bit set in *addr? + * test_and_set_bit(bit, addr) Set bit and return old value + * test_and_clear_bit(bit, addr) Clear bit and return old value + * test_and_change_bit(bit, addr) Change bit and return old value + * find_first_zero_bit(addr, nbits) Position first zero bit in *addr + * find_first_bit(addr, nbits) Position first set bit in *addr + * find_next_zero_bit(addr, nbits, bit) Position next zero bit in *addr >= bit + * find_next_bit(addr, nbits, bit) Position next set bit in *addr >= bit */ #define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1))) @@ -253,6 +253,7 @@ void bitmap_set(unsigned long *map, long i, long len); void bitmap_set_atomic(unsigned long *map, long i, long len); void bitmap_clear(unsigned long *map, long start, long nr); bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr); +bool bitmap_test_and_clear(unsigned long *map, long start, long nr); void bitmap_copy_and_clear_atomic(unsigned long *dst, unsigned long *src, long nr); unsigned long bitmap_find_next_zero_area(unsigned long *map, diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index 03213ce952..2c0a2fe751 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -67,6 +67,19 @@ static inline void clear_bit(long nr, unsigned long *addr) *p &= ~mask; } +/** + * clear_bit_atomic - Clears a bit in memory atomically + * @nr: Bit to clear + * @addr: Address to start counting from + */ +static inline void clear_bit_atomic(long nr, unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = addr + BIT_WORD(nr); + + return qatomic_and(p, ~mask); +} + /** * change_bit - Toggle a bit in memory * @nr: Bit to change @@ -218,7 +231,7 @@ static inline unsigned long find_first_zero_bit(const unsigned long *addr, */ static inline uint8_t rol8(uint8_t word, unsigned int shift) { - return (word << shift) | (word >> ((8 - shift) & 7)); + return (word << (shift & 7)) | (word >> (-shift & 7)); } /** @@ -228,7 +241,7 @@ static inline uint8_t rol8(uint8_t word, unsigned int shift) */ static inline uint8_t ror8(uint8_t word, unsigned int shift) { - return (word >> shift) | (word << ((8 - shift) & 7)); + return (word >> (shift & 7)) | (word << (-shift & 7)); } /** @@ -238,7 +251,7 @@ static inline uint8_t ror8(uint8_t word, unsigned int shift) */ static inline uint16_t rol16(uint16_t word, unsigned int shift) { - return (word << shift) | (word >> ((16 - shift) & 15)); + return (word << (shift & 15)) | (word >> (-shift & 15)); } /** @@ -248,7 +261,7 @@ static inline uint16_t rol16(uint16_t word, unsigned int shift) */ static inline uint16_t ror16(uint16_t word, unsigned int shift) { - return (word >> shift) | (word << ((16 - shift) & 15)); + return (word >> (shift & 15)) | (word << (-shift & 15)); } /** @@ -258,7 +271,7 @@ static inline uint16_t ror16(uint16_t word, unsigned int shift) */ static inline uint32_t rol32(uint32_t word, unsigned int shift) { - return (word << shift) | (word >> ((32 - shift) & 31)); + return (word << (shift & 31)) | (word >> (-shift & 31)); } /** @@ -268,7 +281,7 @@ static inline uint32_t rol32(uint32_t word, unsigned int shift) */ static inline uint32_t ror32(uint32_t word, unsigned int shift) { - return (word >> shift) | (word << ((32 - shift) & 31)); + return (word >> (shift & 31)) | (word << (-shift & 31)); } /** @@ -278,7 +291,7 @@ static inline uint32_t ror32(uint32_t word, unsigned int shift) */ static inline uint64_t rol64(uint64_t word, unsigned int shift) { - return (word << shift) | (word >> ((64 - shift) & 63)); + return (word << (shift & 63)) | (word >> (-shift & 63)); } /** @@ -288,7 +301,7 @@ static inline uint64_t rol64(uint64_t word, unsigned int shift) */ static inline uint64_t ror64(uint64_t word, unsigned int shift) { - return (word >> shift) | (word << ((64 - shift) & 63)); + return (word >> (shift & 63)) | (word << (-shift & 63)); } /** diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index 346d05f2aa..bd67468e5e 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -1,97 +1,54 @@ #ifndef BSWAP_H #define BSWAP_H -#ifdef CONFIG_MACHINE_BSWAP_H -# include -# include -#elif defined(__FreeBSD__) -# include -#elif defined(__HAIKU__) -# include -#elif defined(CONFIG_BYTESWAP_H) -# include -#define BSWAP_FROM_BYTESWAP -# else -#define BSWAP_FROM_FALLBACKS -#endif /* ! CONFIG_MACHINE_BSWAP_H */ +#undef bswap16 +#define bswap16(_x) __builtin_bswap16(_x) +#undef bswap32 +#define bswap32(_x) __builtin_bswap32(_x) +#undef bswap64 +#define bswap64(_x) __builtin_bswap64(_x) -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef BSWAP_FROM_BYTESWAP -static inline uint16_t bswap16(uint16_t x) +static inline uint32_t bswap24(uint32_t x) { - return bswap_16(x); + return (((x & 0x000000ffU) << 16) | + ((x & 0x0000ff00U) << 0) | + ((x & 0x00ff0000U) >> 16)); } -static inline uint32_t bswap32(uint32_t x) -{ - return bswap_32(x); -} - -static inline uint64_t bswap64(uint64_t x) -{ - return bswap_64(x); -} -#endif - -#ifdef BSWAP_FROM_FALLBACKS -static inline uint16_t bswap16(uint16_t x) -{ - return (((x & 0x00ff) << 8) | - ((x & 0xff00) >> 8)); -} - -static inline uint32_t bswap32(uint32_t x) -{ - return (((x & 0x000000ffU) << 24) | - ((x & 0x0000ff00U) << 8) | - ((x & 0x00ff0000U) >> 8) | - ((x & 0xff000000U) >> 24)); -} - -static inline uint64_t bswap64(uint64_t x) -{ - return (((x & 0x00000000000000ffULL) << 56) | - ((x & 0x000000000000ff00ULL) << 40) | - ((x & 0x0000000000ff0000ULL) << 24) | - ((x & 0x00000000ff000000ULL) << 8) | - ((x & 0x000000ff00000000ULL) >> 8) | - ((x & 0x0000ff0000000000ULL) >> 24) | - ((x & 0x00ff000000000000ULL) >> 40) | - ((x & 0xff00000000000000ULL) >> 56)); -} -#endif - -#undef BSWAP_FROM_BYTESWAP -#undef BSWAP_FROM_FALLBACKS - static inline void bswap16s(uint16_t *s) { - *s = bswap16(*s); + *s = __builtin_bswap16(*s); +} + +static inline void bswap24s(uint32_t *s) +{ + *s = bswap24(*s & 0x00ffffffU); } static inline void bswap32s(uint32_t *s) { - *s = bswap32(*s); + *s = __builtin_bswap32(*s); } static inline void bswap64s(uint64_t *s) { - *s = bswap64(*s); + *s = __builtin_bswap64(*s); } #if HOST_BIG_ENDIAN #define be_bswap(v, size) (v) -#define le_bswap(v, size) glue(bswap, size)(v) +#define le_bswap(v, size) glue(__builtin_bswap, size)(v) +#define le_bswap24(v) bswap24(v) #define be_bswaps(v, size) -#define le_bswaps(p, size) do { *p = glue(bswap, size)(*p); } while(0) +#define le_bswaps(p, size) \ + do { *p = glue(__builtin_bswap, size)(*p); } while (0) #else #define le_bswap(v, size) (v) -#define be_bswap(v, size) glue(bswap, size)(v) +#define le_bswap24(v) (v) +#define be_bswap(v, size) glue(__builtin_bswap, size)(v) #define le_bswaps(v, size) -#define be_bswaps(p, size) do { *p = glue(bswap, size)(*p); } while(0) +#define be_bswaps(p, size) \ + do { *p = glue(__builtin_bswap, size)(*p); } while (0) #endif /** @@ -182,11 +139,20 @@ CPU_CONVERT(le, 32, uint32_t) CPU_CONVERT(le, 64, uint64_t) /* - * Same as cpu_to_le{16,32}, except that gcc will figure the result is + * Same as cpu_to_le{16,32,64}, except that gcc will figure the result is * a compile-time constant if you pass in a constant. So this can be * used to initialize static variables. */ #if HOST_BIG_ENDIAN +# define const_le64(_x) \ + ((((_x) & 0x00000000000000ffULL) << 56) | \ + (((_x) & 0x000000000000ff00ULL) << 40) | \ + (((_x) & 0x0000000000ff0000ULL) << 24) | \ + (((_x) & 0x00000000ff000000ULL) << 8) | \ + (((_x) & 0x000000ff00000000ULL) >> 8) | \ + (((_x) & 0x0000ff0000000000ULL) >> 24) | \ + (((_x) & 0x00ff000000000000ULL) >> 40) | \ + (((_x) & 0xff00000000000000ULL) >> 56)) # define const_le32(_x) \ ((((_x) & 0x000000ffU) << 24) | \ (((_x) & 0x0000ff00U) << 8) | \ @@ -196,6 +162,7 @@ CPU_CONVERT(le, 64, uint64_t) ((((_x) & 0x00ff) << 8) | \ (((_x) & 0xff00) >> 8)) #else +# define const_le64(_x) (_x) # define const_le32(_x) (_x) # define const_le16(_x) (_x) #endif @@ -223,6 +190,7 @@ CPU_CONVERT(le, 64, uint64_t) * size is: * b: 8 bits * w: 16 bits + * 24: 24 bits * l: 32 bits * q: 64 bits * @@ -295,6 +263,11 @@ static inline void stw_he_p(void *ptr, uint16_t v) __builtin_memcpy(ptr, &v, sizeof(v)); } +static inline void st24_he_p(void *ptr, uint32_t v) +{ + __builtin_memcpy(ptr, &v, 3); +} + static inline int ldl_he_p(const void *ptr) { int32_t r; @@ -344,6 +317,11 @@ static inline void stw_le_p(void *ptr, uint16_t v) stw_he_p(ptr, le_bswap(v, 16)); } +static inline void st24_le_p(void *ptr, uint32_t v) +{ + st24_he_p(ptr, le_bswap24(v)); +} + static inline void stl_le_p(void *ptr, uint32_t v) { stl_he_p(ptr, le_bswap(v, 32)); @@ -448,8 +426,4 @@ DO_STN_LDN_P(be) #undef le_bswaps #undef be_bswaps -#ifdef __cplusplus -} -#endif - #endif /* BSWAP_H */ diff --git a/include/qemu/chardev_open.h b/include/qemu/chardev_open.h new file mode 100644 index 0000000000..64e8fcfdcb --- /dev/null +++ b/include/qemu/chardev_open.h @@ -0,0 +1,16 @@ +/* + * QEMU Chardev Helper + * + * Copyright (C) 2023 Intel Corporation. + * + * Authors: Yi Liu + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef QEMU_CHARDEV_OPEN_H +#define QEMU_CHARDEV_OPEN_H + +int open_cdev(const char *devpath, dev_t cdev); +#endif diff --git a/include/qemu/clang-tsa.h b/include/qemu/clang-tsa.h new file mode 100644 index 0000000000..ba06fb8c92 --- /dev/null +++ b/include/qemu/clang-tsa.h @@ -0,0 +1,114 @@ +#ifndef CLANG_TSA_H +#define CLANG_TSA_H + +/* + * Copyright 2018 Jarkko Hietaniemi + * + * 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. + */ + +/* http://clang.llvm.org/docs/ThreadSafetyAnalysis.html + * + * TSA is available since clang 3.6-ish. + */ +#ifdef __clang__ +# define TSA(x) __attribute__((x)) +#else +# define TSA(x) /* No TSA, make TSA attributes no-ops. */ +#endif + +/* TSA_CAPABILITY() is used to annotate typedefs: + * + * typedef pthread_mutex_t TSA_CAPABILITY("mutex") tsa_mutex; + */ +#define TSA_CAPABILITY(x) TSA(capability(x)) + +/* TSA_GUARDED_BY() is used to annotate global variables, + * the data is guarded: + * + * Foo foo TSA_GUARDED_BY(mutex); + */ +#define TSA_GUARDED_BY(x) TSA(guarded_by(x)) + +/* TSA_PT_GUARDED_BY() is used to annotate global pointers, the data + * behind the pointer is guarded. + * + * Foo* ptr TSA_PT_GUARDED_BY(mutex); + */ +#define TSA_PT_GUARDED_BY(x) TSA(pt_guarded_by(x)) + +/* The TSA_REQUIRES() is used to annotate functions: the caller of the + * function MUST hold the resource, the function will NOT release it. + * + * More than one mutex may be specified, comma-separated. + * + * void Foo(void) TSA_REQUIRES(mutex); + */ +#define TSA_REQUIRES(...) TSA(requires_capability(__VA_ARGS__)) +#define TSA_REQUIRES_SHARED(...) TSA(requires_shared_capability(__VA_ARGS__)) + +/* TSA_EXCLUDES() is used to annotate functions: the caller of the + * function MUST NOT hold resource, the function first acquires the + * resource, and then releases it. + * + * More than one mutex may be specified, comma-separated. + * + * void Foo(void) TSA_EXCLUDES(mutex); + */ +#define TSA_EXCLUDES(...) TSA(locks_excluded(__VA_ARGS__)) + +/* TSA_ACQUIRE() is used to annotate functions: the caller of the + * function MUST NOT hold the resource, the function will acquire the + * resource, but NOT release it. + * + * More than one mutex may be specified, comma-separated. + * + * void Foo(void) TSA_ACQUIRE(mutex); + */ +#define TSA_ACQUIRE(...) TSA(acquire_capability(__VA_ARGS__)) +#define TSA_ACQUIRE_SHARED(...) TSA(acquire_shared_capability(__VA_ARGS__)) + +/* TSA_RELEASE() is used to annotate functions: the caller of the + * function MUST hold the resource, but the function will then release it. + * + * More than one mutex may be specified, comma-separated. + * + * void Foo(void) TSA_RELEASE(mutex); + */ +#define TSA_RELEASE(...) TSA(release_capability(__VA_ARGS__)) +#define TSA_RELEASE_SHARED(...) TSA(release_shared_capability(__VA_ARGS__)) + +/* TSA_NO_TSA is used to annotate functions. Use only when you need to. + * + * void Foo(void) TSA_NO_TSA; + */ +#define TSA_NO_TSA TSA(no_thread_safety_analysis) + +/* + * TSA_ASSERT() is used to annotate functions: This function will assert that + * the lock is held. When it returns, the caller of the function is assumed to + * already hold the resource. + * + * More than one mutex may be specified, comma-separated. + */ +#define TSA_ASSERT(...) TSA(assert_capability(__VA_ARGS__)) +#define TSA_ASSERT_SHARED(...) TSA(assert_shared_capability(__VA_ARGS__)) + +#endif /* #ifndef CLANG_TSA_H */ diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index f20a76e4a2..c797f0d457 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -33,10 +33,13 @@ #ifndef glue #define xglue(x, y) x ## y #define glue(x, y) xglue(x, y) -#define stringify(s) tostring(s) -#define tostring(s) #s +#define stringify(s) tostring(s) +#define tostring(s) #s #endif +/* Expands into an identifier stemN, where N is another number each time */ +#define MAKE_IDENTFIER(stem) glue(stem, __COUNTER__) + #ifndef likely #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) @@ -184,4 +187,44 @@ #define QEMU_DISABLE_CFI #endif +/* + * Apple clang version 14 has a bug in its __builtin_subcll(); define + * BUILTIN_SUBCLL_BROKEN for the offending versions so we can avoid it. + * When a version of Apple clang which has this bug fixed is released + * we can add an upper bound to this check. + * See https://gitlab.com/qemu-project/qemu/-/issues/1631 + * and https://gitlab.com/qemu-project/qemu/-/issues/1659 for details. + * The bug never made it into any upstream LLVM releases, only Apple ones. + */ +#if defined(__apple_build_version__) && __clang_major__ >= 14 +#define BUILTIN_SUBCLL_BROKEN +#endif + +#if __has_attribute(annotate) +#define QEMU_ANNOTATE(x) __attribute__((annotate(x))) +#else +#define QEMU_ANNOTATE(x) +#endif + +#if __has_attribute(used) +# define QEMU_USED __attribute__((used)) +#else +# define QEMU_USED +#endif + +/* + * Ugly CPP trick that is like "defined FOO", but also works in C + * code. Useful to replace #ifdef with "if" statements; assumes + * the symbol was defined with Meson's "config.set()", so it is empty + * if defined. + */ +#define IS_ENABLED(x) IS_EMPTY(x) + +#define IS_EMPTY_JUNK_ junk, +#define IS_EMPTY(value) IS_EMPTY_(IS_EMPTY_JUNK_##value) + +/* Expands to either SECOND_ARG(junk, 1, 0) or SECOND_ARG(IS_EMPTY_JUNK_CONFIG_FOO 1, 0) */ +#define SECOND_ARG(first, second, ...) second +#define IS_EMPTY_(junk_maybecomma) SECOND_ARG(junk_maybecomma 1, 0) + #endif /* COMPILER_H */ diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h index 321e7c7c03..b82a778123 100644 --- a/include/qemu/config-file.h +++ b/include/qemu/config-file.h @@ -22,7 +22,7 @@ int qemu_read_config_file(const char *filename, QEMUConfigCB *f, Error **errp); /* Parse QDict options as a replacement for a config file (allowing multiple enumerated (0..(n-1)) configuration "sections") */ -void qemu_config_parse_qdict(QDict *options, QemuOptsList **lists, +bool qemu_config_parse_qdict(QDict *options, QemuOptsList **lists, Error **errp); #endif /* QEMU_CONFIG_FILE_H */ diff --git a/include/qemu/coroutine-core.h b/include/qemu/coroutine-core.h new file mode 100644 index 0000000000..503bad6e0e --- /dev/null +++ b/include/qemu/coroutine-core.h @@ -0,0 +1,154 @@ +/* + * QEMU coroutine implementation + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Stefan Hajnoczi + * Kevin Wolf + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef QEMU_COROUTINE_CORE_H +#define QEMU_COROUTINE_CORE_H + +/** + * Coroutines are a mechanism for stack switching and can be used for + * cooperative userspace threading. These functions provide a simple but + * useful flavor of coroutines that is suitable for writing sequential code, + * rather than callbacks, for operations that need to give up control while + * waiting for events to complete. + * + * These functions are re-entrant and may be used outside the BQL. + * + * Functions that execute in coroutine context cannot be called + * directly from normal functions. Use @coroutine_fn to mark such + * functions. For example: + * + * static void coroutine_fn foo(void) { + * .... + * } + * + * In the future it would be nice to have the compiler or a static + * checker catch misuse of such functions. This annotation might make + * it possible and in the meantime it serves as documentation. + */ + +/** + * Mark a function that executes in coroutine context + * + * + * Functions that execute in coroutine context cannot be called + * directly from normal functions. Use @coroutine_fn to mark such + * functions. For example: + * + * static void coroutine_fn foo(void) { + * .... + * } + * + * In the future it would be nice to have the compiler or a static + * checker catch misuse of such functions. This annotation might make + * it possible and in the meantime it serves as documentation. + */ + +typedef struct Coroutine Coroutine; +typedef struct CoMutex CoMutex; + +/** + * Coroutine entry point + * + * When the coroutine is entered for the first time, opaque is passed in as an + * argument. + * + * When this function returns, the coroutine is destroyed automatically and + * execution continues in the caller who last entered the coroutine. + */ +typedef void coroutine_fn CoroutineEntry(void *opaque); + +/** + * Create a new coroutine + * + * Use qemu_coroutine_enter() to actually transfer control to the coroutine. + * The opaque argument is passed as the argument to the entry point. + */ +Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque); + +/** + * Transfer control to a coroutine + */ +void qemu_coroutine_enter(Coroutine *coroutine); + +/** + * Transfer control to a coroutine if it's not active (i.e. part of the call + * stack of the running coroutine). Otherwise, do nothing. + */ +void qemu_coroutine_enter_if_inactive(Coroutine *co); + +/** + * Transfer control to a coroutine and associate it with ctx + */ +void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co); + +/** + * Transfer control back to a coroutine's caller + * + * This function does not return until the coroutine is re-entered using + * qemu_coroutine_enter(). + */ +void coroutine_fn qemu_coroutine_yield(void); + +/** + * Get the AioContext of the given coroutine + */ +AioContext *qemu_coroutine_get_aio_context(Coroutine *co); + +/** + * Get the currently executing coroutine + */ +Coroutine *qemu_coroutine_self(void); + +/** + * Return whether or not currently inside a coroutine + * + * This can be used to write functions that work both when in coroutine context + * and when not in coroutine context. Note that such functions cannot use the + * coroutine_fn annotation since they work outside coroutine context. + */ +bool qemu_in_coroutine(void); + +/** + * Return true if the coroutine is currently entered + * + * A coroutine is "entered" if it has not yielded from the current + * qemu_coroutine_enter() call used to run it. This does not mean that the + * coroutine is currently executing code since it may have transferred control + * to another coroutine using qemu_coroutine_enter(). + * + * When several coroutines enter each other there may be no way to know which + * ones have already been entered. In such situations this function can be + * used to avoid recursively entering coroutines. + */ +bool qemu_coroutine_entered(Coroutine *co); + +/** + * Initialises a CoMutex. This must be called before any other operation is used + * on the CoMutex. + */ +void qemu_co_mutex_init(CoMutex *mutex); + +/** + * Locks the mutex. If the lock cannot be taken immediately, control is + * transferred to the caller of the current coroutine. + */ +void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex); + +/** + * Unlocks the mutex and schedules the next coroutine that was waiting for this + * lock to be run. + */ +void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex); + +#endif diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index d1548d5b11..e6aff45301 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -15,6 +15,7 @@ #ifndef QEMU_COROUTINE_H #define QEMU_COROUTINE_H +#include "qemu/coroutine-core.h" #include "qemu/queue.h" #include "qemu/timer.h" @@ -25,102 +26,20 @@ * rather than callbacks, for operations that need to give up control while * waiting for events to complete. * - * These functions are re-entrant and may be used outside the global mutex. - */ - -/** - * Mark a function that executes in coroutine context + * These functions are re-entrant and may be used outside the BQL. * - * Functions that execute in coroutine context cannot be called directly from - * normal functions. In the future it would be nice to enable compiler or - * static checker support for catching such errors. This annotation might make - * it possible and in the meantime it serves as documentation. - * - * For example: + * Functions that execute in coroutine context cannot be called + * directly from normal functions. Use @coroutine_fn to mark such + * functions. For example: * * static void coroutine_fn foo(void) { * .... * } - */ -#define coroutine_fn - -typedef struct Coroutine Coroutine; - -/** - * Coroutine entry point * - * When the coroutine is entered for the first time, opaque is passed in as an - * argument. - * - * When this function returns, the coroutine is destroyed automatically and - * execution continues in the caller who last entered the coroutine. + * In the future it would be nice to have the compiler or a static + * checker catch misuse of such functions. This annotation might make + * it possible and in the meantime it serves as documentation. */ -typedef void coroutine_fn CoroutineEntry(void *opaque); - -/** - * Create a new coroutine - * - * Use qemu_coroutine_enter() to actually transfer control to the coroutine. - * The opaque argument is passed as the argument to the entry point. - */ -Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque); - -/** - * Transfer control to a coroutine - */ -void qemu_coroutine_enter(Coroutine *coroutine); - -/** - * Transfer control to a coroutine if it's not active (i.e. part of the call - * stack of the running coroutine). Otherwise, do nothing. - */ -void qemu_coroutine_enter_if_inactive(Coroutine *co); - -/** - * Transfer control to a coroutine and associate it with ctx - */ -void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co); - -/** - * Transfer control back to a coroutine's caller - * - * This function does not return until the coroutine is re-entered using - * qemu_coroutine_enter(). - */ -void coroutine_fn qemu_coroutine_yield(void); - -/** - * Get the AioContext of the given coroutine - */ -AioContext *coroutine_fn qemu_coroutine_get_aio_context(Coroutine *co); - -/** - * Get the currently executing coroutine - */ -Coroutine *coroutine_fn qemu_coroutine_self(void); - -/** - * Return whether or not currently inside a coroutine - * - * This can be used to write functions that work both when in coroutine context - * and when not in coroutine context. Note that such functions cannot use the - * coroutine_fn annotation since they work outside coroutine context. - */ -bool qemu_in_coroutine(void); - -/** - * Return true if the coroutine is currently entered - * - * A coroutine is "entered" if it has not yielded from the current - * qemu_coroutine_enter() call used to run it. This does not mean that the - * coroutine is currently executing code since it may have transferred control - * to another coroutine using qemu_coroutine_enter(). - * - * When several coroutines enter each other there may be no way to know which - * ones have already been entered. In such situations this function can be - * used to avoid recursively entering coroutines. - */ -bool qemu_coroutine_entered(Coroutine *co); /** * Provides a mutex that can be used to synchronise coroutines @@ -149,24 +68,6 @@ struct CoMutex { Coroutine *holder; }; -/** - * Initialises a CoMutex. This must be called before any other operation is used - * on the CoMutex. - */ -void qemu_co_mutex_init(CoMutex *mutex); - -/** - * Locks the mutex. If the lock cannot be taken immediately, control is - * transferred to the caller of the current coroutine. - */ -void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex); - -/** - * Unlocks the mutex and schedules the next coroutine that was waiting for this - * lock to be run. - */ -void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex); - /** * Assert that the current coroutine holds @mutex. */ @@ -198,14 +99,25 @@ typedef struct CoQueue { */ void qemu_co_queue_init(CoQueue *queue); +typedef enum { + /* + * Enqueue at front instead of back. Use this to re-queue a request when + * its wait condition is not satisfied after being woken up. + */ + CO_QUEUE_WAIT_FRONT = 0x1, +} CoQueueWaitFlags; + /** * Adds the current coroutine to the CoQueue and transfers control to the * caller of the coroutine. The mutex is unlocked during the wait and * locked again afterwards. */ #define qemu_co_queue_wait(queue, lock) \ - qemu_co_queue_wait_impl(queue, QEMU_MAKE_LOCKABLE(lock)) -void coroutine_fn qemu_co_queue_wait_impl(CoQueue *queue, QemuLockable *lock); + qemu_co_queue_wait_impl(queue, QEMU_MAKE_LOCKABLE(lock), 0) +#define qemu_co_queue_wait_flags(queue, lock, flags) \ + qemu_co_queue_wait_impl(queue, QEMU_MAKE_LOCKABLE(lock), (flags)) +void coroutine_fn qemu_co_queue_wait_impl(CoQueue *queue, QemuLockable *lock, + CoQueueWaitFlags flags); /** * Removes the next coroutine from the CoQueue, and queue it to run after @@ -276,7 +188,7 @@ void qemu_co_rwlock_init(CoRwlock *lock); * of a parallel writer, control is transferred to the caller of the current * coroutine. */ -void qemu_co_rwlock_rdlock(CoRwlock *lock); +void coroutine_fn qemu_co_rwlock_rdlock(CoRwlock *lock); /** * Write Locks the CoRwlock from a reader. This is a bit more efficient than @@ -285,7 +197,7 @@ void qemu_co_rwlock_rdlock(CoRwlock *lock); * to the caller of the current coroutine; another writer might run while * @qemu_co_rwlock_upgrade blocks. */ -void qemu_co_rwlock_upgrade(CoRwlock *lock); +void coroutine_fn qemu_co_rwlock_upgrade(CoRwlock *lock); /** * Downgrades a write-side critical section to a reader. Downgrading with @@ -293,20 +205,20 @@ void qemu_co_rwlock_upgrade(CoRwlock *lock); * followed by @qemu_co_rwlock_rdlock. This makes it more efficient, but * may also sometimes be necessary for correctness. */ -void qemu_co_rwlock_downgrade(CoRwlock *lock); +void coroutine_fn qemu_co_rwlock_downgrade(CoRwlock *lock); /** * Write Locks the mutex. If the lock cannot be taken immediately because * of a parallel reader, control is transferred to the caller of the current * coroutine. */ -void qemu_co_rwlock_wrlock(CoRwlock *lock); +void coroutine_fn qemu_co_rwlock_wrlock(CoRwlock *lock); /** * Unlocks the read/write lock and schedules the next coroutine that was * waiting for this lock to be run. */ -void qemu_co_rwlock_unlock(CoRwlock *lock); +void coroutine_fn qemu_co_rwlock_unlock(CoRwlock *lock); typedef struct QemuCoSleep { Coroutine *to_wake; @@ -331,6 +243,19 @@ static inline void coroutine_fn qemu_co_sleep_ns(QEMUClockType type, int64_t ns) qemu_co_sleep_ns_wakeable(&w, type, ns); } +typedef void CleanupFunc(void *opaque); +/** + * Run entry in a coroutine and start timer. Wait for entry to finish or for + * timer to elapse, what happen first. If entry finished, return 0, if timer + * elapsed earlier, return -ETIMEDOUT. + * + * Be careful, entry execution is not canceled, user should handle it somehow. + * If @clean is provided, it's called after coroutine finish if timeout + * happened. + */ +int coroutine_fn qemu_co_timeout(CoroutineEntry *entry, void *opaque, + uint64_t timeout_ns, CleanupFunc clean); + /** * Wake a coroutine if it is sleeping in qemu_co_sleep_ns. The timer will be * deleted. @sleep_state must be the variable whose address was given to @@ -365,8 +290,9 @@ void qemu_coroutine_dec_pool_size(unsigned int additional_pool_size); * The same interface as qemu_sendv_recvv(), with added yielding. * XXX should mark these as coroutine_fn */ -ssize_t qemu_co_sendv_recvv(int sockfd, struct iovec *iov, unsigned iov_cnt, - size_t offset, size_t bytes, bool do_send); +ssize_t coroutine_fn qemu_co_sendv_recvv(int sockfd, struct iovec *iov, + unsigned iov_cnt, size_t offset, + size_t bytes, bool do_send); #define qemu_co_recvv(sockfd, iov, iov_cnt, offset, bytes) \ qemu_co_sendv_recvv(sockfd, iov, iov_cnt, offset, bytes, false) #define qemu_co_sendv(sockfd, iov, iov_cnt, offset, bytes) \ @@ -375,7 +301,8 @@ ssize_t qemu_co_sendv_recvv(int sockfd, struct iovec *iov, unsigned iov_cnt, /** * The same as above, but with just a single buffer */ -ssize_t qemu_co_send_recv(int sockfd, void *buf, size_t bytes, bool do_send); +ssize_t coroutine_fn qemu_co_send_recv(int sockfd, void *buf, size_t bytes, + bool do_send); #define qemu_co_recv(sockfd, buf, bytes) \ qemu_co_send_recv(sockfd, buf, bytes, false) #define qemu_co_send(sockfd, buf, bytes) \ diff --git a/include/qemu/cpuid.h b/include/qemu/cpuid.h index 7adb12d320..b11161555b 100644 --- a/include/qemu/cpuid.h +++ b/include/qemu/cpuid.h @@ -25,6 +25,9 @@ #endif /* Leaf 1, %ecx */ +#ifndef bit_PCLMUL +#define bit_PCLMUL (1 << 1) +#endif #ifndef bit_SSE4_1 #define bit_SSE4_1 (1 << 19) #endif @@ -71,4 +74,29 @@ #define bit_LZCNT (1 << 5) #endif +/* + * Signatures for different CPU implementations as returned from Leaf 0. + */ + +#ifndef signature_INTEL_ecx +/* "Genu" "ineI" "ntel" */ +#define signature_INTEL_ebx 0x756e6547 +#define signature_INTEL_edx 0x49656e69 +#define signature_INTEL_ecx 0x6c65746e +#endif + +#ifndef signature_AMD_ecx +/* "Auth" "enti" "cAMD" */ +#define signature_AMD_ebx 0x68747541 +#define signature_AMD_edx 0x69746e65 +#define signature_AMD_ecx 0x444d4163 +#endif + +static inline unsigned xgetbv_low(unsigned c) +{ + unsigned a, d; + asm("xgetbv" : "=a"(a), "=d"(d) : "c"(c)); + return a; +} + #endif /* QEMU_CPUID_H */ diff --git a/include/qemu/crc-ccitt.h b/include/qemu/crc-ccitt.h index d6eb49146d..8918dafe07 100644 --- a/include/qemu/crc-ccitt.h +++ b/include/qemu/crc-ccitt.h @@ -17,8 +17,8 @@ extern uint16_t const crc_ccitt_table[256]; extern uint16_t const crc_ccitt_false_table[256]; -extern uint16_t crc_ccitt(uint16_t crc, const uint8_t *buffer, size_t len); -extern uint16_t crc_ccitt_false(uint16_t crc, const uint8_t *buffer, size_t len); +uint16_t crc_ccitt(uint16_t crc, const uint8_t *buffer, size_t len); +uint16_t crc_ccitt_false(uint16_t crc, const uint8_t *buffer, size_t len); static inline uint16_t crc_ccitt_byte(uint16_t crc, const uint8_t c) { diff --git a/include/qemu/crc32c.h b/include/qemu/crc32c.h index 5b78884c38..88b4d2b3b3 100644 --- a/include/qemu/crc32c.h +++ b/include/qemu/crc32c.h @@ -30,5 +30,6 @@ uint32_t crc32c(uint32_t crc, const uint8_t *data, unsigned int length); +uint32_t iov_crc32c(uint32_t crc, const struct iovec *iov, size_t iov_cnt); #endif diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 40e10e19a7..92c927a6a3 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -1,6 +1,24 @@ #ifndef QEMU_CUTILS_H #define QEMU_CUTILS_H +/* + * si_prefix: + * @exp10: exponent of 10, a multiple of 3 between -18 and 18 inclusive. + * + * Return a SI prefix (n, u, m, K, M, etc.) corresponding + * to the given exponent of 10. + */ +const char *si_prefix(unsigned int exp10); + +/* + * iec_binary_prefix: + * @exp2: exponent of 2, a multiple of 10 between 0 and 60 inclusive. + * + * Return an IEC binary prefix (Ki, Mi, etc.) corresponding + * to the given exponent of 2. + */ +const char *iec_binary_prefix(unsigned int exp2); + /** * pstrcpy: * @buf: buffer to copy string into @@ -145,9 +163,8 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base, int qemu_strtod(const char *nptr, const char **endptr, double *result); int qemu_strtod_finite(const char *nptr, const char **endptr, double *result); -int parse_uint(const char *s, unsigned long long *value, char **endptr, - int base); -int parse_uint_full(const char *s, unsigned long long *value, int base); +int parse_uint(const char *s, const char **endptr, int base, uint64_t *value); +int parse_uint_full(const char *s, int base, uint64_t *value); int qemu_strtosz(const char *nptr, const char **end, uint64_t *result); int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result); @@ -206,9 +223,21 @@ const char *qemu_get_exec_dir(void); * @dir: the directory (typically a `CONFIG_*DIR` variable) to be relocated. * * Returns a path for @dir that uses the directory of the running executable - * as the prefix. For example, if `bindir` is `/usr/bin` and @dir is - * `/usr/share/qemu`, the function will append `../share/qemu` to the - * directory that contains the running executable and return the result. + * as the prefix. + * + * When a directory named `qemu-bundle` exists in the directory of the running + * executable, the path to the directory will be prepended to @dir. For + * example, if the directory of the running executable is `/qemu/build` @dir + * is `/usr/share/qemu`, the result will be + * `/qemu/build/qemu-bundle/usr/share/qemu`. The directory is expected to exist + * in the build tree. + * + * Otherwise, the directory of the running executable will be used as the + * prefix and it appends the relative path from `bindir` to @dir. For example, + * if the directory of the running executable is `/opt/qemu/bin`, `bindir` is + * `/usr/bin` and @dir is `/usr/share/qemu`, the result will be + * `/opt/qemu/bin/../share/qemu`. + * * The returned string should be freed by the caller. */ char *get_relocated_path(const char *dir); diff --git a/include/qemu/dbus.h b/include/qemu/dbus.h index 08f00dfd53..81d3de8a5a 100644 --- a/include/qemu/dbus.h +++ b/include/qemu/dbus.h @@ -15,7 +15,6 @@ #include "qom/object.h" #include "chardev/char.h" #include "qemu/notify.h" -#include "qemu/typedefs.h" /* glib/gio 2.68 */ #define DBUS_METHOD_INVOCATION_HANDLED TRUE diff --git a/include/qemu/defer-call.h b/include/qemu/defer-call.h new file mode 100644 index 0000000000..e2c1d24572 --- /dev/null +++ b/include/qemu/defer-call.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Deferred calls + * + * Copyright Red Hat. + */ + +#ifndef QEMU_DEFER_CALL_H +#define QEMU_DEFER_CALL_H + +/* See documentation in util/defer-call.c */ +void defer_call_begin(void); +void defer_call_end(void); +void defer_call(void (*fn)(void *), void *opaque); + +#endif /* QEMU_DEFER_CALL_H */ diff --git a/include/qemu/envlist.h b/include/qemu/envlist.h index b9addcc11f..6006dfae44 100644 --- a/include/qemu/envlist.h +++ b/include/qemu/envlist.h @@ -1,10 +1,6 @@ #ifndef ENVLIST_H #define ENVLIST_H -#ifdef __cplusplus -extern "C" { -#endif - typedef struct envlist envlist_t; envlist_t *envlist_create(void); @@ -15,8 +11,4 @@ int envlist_parse_set(envlist_t *, const char *); int envlist_parse_unset(envlist_t *, const char *); char **envlist_to_environ(const envlist_t *, size_t *); -#ifdef __cplusplus -} -#endif - #endif /* ENVLIST_H */ diff --git a/include/qemu/fifo8.h b/include/qemu/fifo8.h index 28bf2cee57..c6295c6ff0 100644 --- a/include/qemu/fifo8.h +++ b/include/qemu/fifo8.h @@ -46,7 +46,7 @@ void fifo8_push(Fifo8 *fifo, uint8_t data); * fifo8_push_all: * @fifo: FIFO to push to * @data: data to push - * @size: number of bytes to push + * @num: number of bytes to push * * Push a byte array to the FIFO. Behaviour is undefined if the FIFO is full. * Clients are responsible for checking the space left in the FIFO using @@ -71,7 +71,7 @@ uint8_t fifo8_pop(Fifo8 *fifo); * fifo8_pop_buf: * @fifo: FIFO to pop from * @max: maximum number of bytes to pop - * @num: actual number of returned bytes + * @numptr: pointer filled with number of bytes returned (can be NULL) * * Pop a number of elements from the FIFO up to a maximum of max. The buffer * containing the popped data is returned. This buffer points directly into @@ -82,16 +82,43 @@ uint8_t fifo8_pop(Fifo8 *fifo); * around in the ring buffer; in this case only a contiguous part of the data * is returned. * - * The number of valid bytes returned is populated in *num; will always return - * at least 1 byte. max must not be 0 or greater than the number of bytes in - * the FIFO. + * The number of valid bytes returned is populated in *numptr; will always + * return at least 1 byte. max must not be 0 or greater than the number of + * bytes in the FIFO. * * Clients are responsible for checking the availability of requested data * using fifo8_num_used(). * * Returns: A pointer to popped data. */ -const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num); +const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr); + +/** + * fifo8_peek_buf: read upto max bytes from the fifo + * @fifo: FIFO to read from + * @max: maximum number of bytes to peek + * @numptr: pointer filled with number of bytes returned (can be NULL) + * + * Peek into a number of elements from the FIFO up to a maximum of max. + * The buffer containing the data peeked into is returned. This buffer points + * directly into the FIFO backing store. Since data is invalidated once any + * of the fifo8_* APIs are called on the FIFO, it is the caller responsibility + * to access it before doing further API calls. + * + * The function may return fewer bytes than requested when the data wraps + * around in the ring buffer; in this case only a contiguous part of the data + * is returned. + * + * The number of valid bytes returned is populated in *numptr; will always + * return at least 1 byte. max must not be 0 or greater than the number of + * bytes in the FIFO. + * + * Clients are responsible for checking the availability of requested data + * using fifo8_num_used(). + * + * Returns: A pointer to peekable data. + */ +const uint8_t *fifo8_peek_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr); /** * fifo8_reset: diff --git a/include/qemu/guest-random.h b/include/qemu/guest-random.h index 09ff9c2236..5060d49d60 100644 --- a/include/qemu/guest-random.h +++ b/include/qemu/guest-random.h @@ -13,16 +13,16 @@ #define QEMU_GUEST_RANDOM_H /** - * qemu_guest_random_seed_main(const char *optarg, Error **errp) - * @optarg: a non-NULL pointer to a C string + * qemu_guest_random_seed_main(const char *seedstr, Error **errp) + * @seedstr: a non-NULL pointer to a C string * @errp: an error indicator * - * The @optarg value is that which accompanies the -seed argument. + * The @seedstr value is that which accompanies the -seed argument. * This forces qemu_guest_getrandom into deterministic mode. * * Returns 0 on success, < 0 on failure while setting *errp. */ -int qemu_guest_random_seed_main(const char *optarg, Error **errp); +int qemu_guest_random_seed_main(const char *seedstr, Error **errp); /** * qemu_guest_random_seed_thread_part1(void) diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h index 5bd986aa44..8136e33674 100644 --- a/include/qemu/hbitmap.h +++ b/include/qemu/hbitmap.h @@ -76,20 +76,9 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size); * * Store result of merging @a and @b into @result. * @result is allowed to be equal to @a or @b. - * - * Return true if the merge was successful, - * false if it was not attempted. + * All bitmaps must have same size. */ -bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result); - -/** - * hbitmap_can_merge: - * - * hbitmap_can_merge(a, b) && hbitmap_can_merge(a, result) is sufficient and - * necessary for hbitmap_merge will not fail. - * - */ -bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b); +void hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result); /** * hbitmap_empty: @@ -341,7 +330,7 @@ bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end, int64_t *dirty_start, int64_t *dirty_count); /* - * bdrv_dirty_bitmap_status: + * hbitmap_status: * @hb: The HBitmap to operate on * @start: The bit to start from * @count: Number of bits to proceed diff --git a/include/qemu/help-texts.h b/include/qemu/help-texts.h index 4f265fed8d..353ab2ad8b 100644 --- a/include/qemu/help-texts.h +++ b/include/qemu/help-texts.h @@ -2,7 +2,7 @@ #define QEMU_HELP_TEXTS_H /* Copyright string for -version arguments, About dialogs, etc */ -#define QEMU_COPYRIGHT "Copyright (c) 2003-2022 " \ +#define QEMU_COPYRIGHT "Copyright (c) 2003-2024 " \ "Fabrice Bellard and the QEMU Project developers" /* Bug reporting information for --help arguments, About dialogs, etc */ diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index f19bd29105..ead97d354d 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -30,8 +30,8 @@ #ifndef HOST_UTILS_H #define HOST_UTILS_H -#include "qemu/compiler.h" #include "qemu/bswap.h" +#include "qemu/int128.h" #ifdef CONFIG_INT128 static inline void mulu64(uint64_t *plow, uint64_t *phigh, @@ -56,6 +56,11 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) return (__int128_t)a * b / c; } +static inline uint64_t muldiv64_round_up(uint64_t a, uint32_t b, uint32_t c) +{ + return ((__int128_t)a * b + c - 1) / c; +} + static inline uint64_t divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) { @@ -83,7 +88,8 @@ void mulu64(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b); uint64_t divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor); int64_t divs128(uint64_t *plow, int64_t *phigh, int64_t divisor); -static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) +static inline uint64_t muldiv64_rounding(uint64_t a, uint32_t b, uint32_t c, + bool round_up) { union { uint64_t ll; @@ -99,14 +105,57 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) u.ll = a; rl = (uint64_t)u.l.low * (uint64_t)b; + if (round_up) { + rl += c - 1; + } rh = (uint64_t)u.l.high * (uint64_t)b; rh += (rl >> 32); res.l.high = rh / c; res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; return res.ll; } + +static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) +{ + return muldiv64_rounding(a, b, c, false); +} + +static inline uint64_t muldiv64_round_up(uint64_t a, uint32_t b, uint32_t c) +{ + return muldiv64_rounding(a, b, c, true); +} #endif +/** + * clz8 - count leading zeros in a 8-bit value. + * @val: The value to search + * + * Returns 8 if the value is zero. Note that the GCC builtin is + * undefined if the value is zero. + * + * Note that the GCC builtin will upcast its argument to an `unsigned int` + * so this function subtracts off the number of prepended zeroes. + */ +static inline int clz8(uint8_t val) +{ + return val ? __builtin_clz(val) - 24 : 8; +} + +/** + * clz16 - count leading zeros in a 16-bit value. + * @val: The value to search + * + * Returns 16 if the value is zero. Note that the GCC builtin is + * undefined if the value is zero. + * + * Note that the GCC builtin will upcast its argument to an `unsigned int` + * so this function subtracts off the number of prepended zeroes. + */ +static inline int clz16(uint16_t val) +{ + return val ? __builtin_clz(val) - 16 : 16; +} + /** * clz32 - count leading zeros in a 32-bit value. * @val: The value to search @@ -153,6 +202,30 @@ static inline int clo64(uint64_t val) return clz64(~val); } +/** + * ctz8 - count trailing zeros in a 8-bit value. + * @val: The value to search + * + * Returns 8 if the value is zero. Note that the GCC builtin is + * undefined if the value is zero. + */ +static inline int ctz8(uint8_t val) +{ + return val ? __builtin_ctz(val) : 8; +} + +/** + * ctz16 - count trailing zeros in a 16-bit value. + * @val: The value to search + * + * Returns 16 if the value is zero. Note that the GCC builtin is + * undefined if the value is zero. + */ +static inline int ctz16(uint16_t val) +{ + return val ? __builtin_ctz(val) : 16; +} + /** * ctz32 - count trailing zeros in a 32-bit value. * @val: The value to search @@ -375,12 +448,7 @@ static inline uint64_t uabs64(int64_t v) */ static inline bool sadd32_overflow(int32_t x, int32_t y, int32_t *ret) { -#if __has_builtin(__builtin_add_overflow) || __GNUC__ >= 5 return __builtin_add_overflow(x, y, ret); -#else - *ret = x + y; - return ((*ret ^ x) & ~(x ^ y)) < 0; -#endif } /** @@ -393,12 +461,7 @@ static inline bool sadd32_overflow(int32_t x, int32_t y, int32_t *ret) */ static inline bool sadd64_overflow(int64_t x, int64_t y, int64_t *ret) { -#if __has_builtin(__builtin_add_overflow) || __GNUC__ >= 5 return __builtin_add_overflow(x, y, ret); -#else - *ret = x + y; - return ((*ret ^ x) & ~(x ^ y)) < 0; -#endif } /** @@ -411,12 +474,7 @@ static inline bool sadd64_overflow(int64_t x, int64_t y, int64_t *ret) */ static inline bool uadd32_overflow(uint32_t x, uint32_t y, uint32_t *ret) { -#if __has_builtin(__builtin_add_overflow) || __GNUC__ >= 5 return __builtin_add_overflow(x, y, ret); -#else - *ret = x + y; - return *ret < x; -#endif } /** @@ -429,12 +487,7 @@ static inline bool uadd32_overflow(uint32_t x, uint32_t y, uint32_t *ret) */ static inline bool uadd64_overflow(uint64_t x, uint64_t y, uint64_t *ret) { -#if __has_builtin(__builtin_add_overflow) || __GNUC__ >= 5 return __builtin_add_overflow(x, y, ret); -#else - *ret = x + y; - return *ret < x; -#endif } /** @@ -448,12 +501,7 @@ static inline bool uadd64_overflow(uint64_t x, uint64_t y, uint64_t *ret) */ static inline bool ssub32_overflow(int32_t x, int32_t y, int32_t *ret) { -#if __has_builtin(__builtin_sub_overflow) || __GNUC__ >= 5 return __builtin_sub_overflow(x, y, ret); -#else - *ret = x - y; - return ((*ret ^ x) & (x ^ y)) < 0; -#endif } /** @@ -467,12 +515,7 @@ static inline bool ssub32_overflow(int32_t x, int32_t y, int32_t *ret) */ static inline bool ssub64_overflow(int64_t x, int64_t y, int64_t *ret) { -#if __has_builtin(__builtin_sub_overflow) || __GNUC__ >= 5 return __builtin_sub_overflow(x, y, ret); -#else - *ret = x - y; - return ((*ret ^ x) & (x ^ y)) < 0; -#endif } /** @@ -486,12 +529,7 @@ static inline bool ssub64_overflow(int64_t x, int64_t y, int64_t *ret) */ static inline bool usub32_overflow(uint32_t x, uint32_t y, uint32_t *ret) { -#if __has_builtin(__builtin_sub_overflow) || __GNUC__ >= 5 return __builtin_sub_overflow(x, y, ret); -#else - *ret = x - y; - return x < y; -#endif } /** @@ -505,12 +543,7 @@ static inline bool usub32_overflow(uint32_t x, uint32_t y, uint32_t *ret) */ static inline bool usub64_overflow(uint64_t x, uint64_t y, uint64_t *ret) { -#if __has_builtin(__builtin_sub_overflow) || __GNUC__ >= 5 return __builtin_sub_overflow(x, y, ret); -#else - *ret = x - y; - return x < y; -#endif } /** @@ -523,13 +556,7 @@ static inline bool usub64_overflow(uint64_t x, uint64_t y, uint64_t *ret) */ static inline bool smul32_overflow(int32_t x, int32_t y, int32_t *ret) { -#if __has_builtin(__builtin_mul_overflow) || __GNUC__ >= 5 return __builtin_mul_overflow(x, y, ret); -#else - int64_t z = (int64_t)x * y; - *ret = z; - return *ret != z; -#endif } /** @@ -542,14 +569,7 @@ static inline bool smul32_overflow(int32_t x, int32_t y, int32_t *ret) */ static inline bool smul64_overflow(int64_t x, int64_t y, int64_t *ret) { -#if __has_builtin(__builtin_mul_overflow) || __GNUC__ >= 5 return __builtin_mul_overflow(x, y, ret); -#else - uint64_t hi, lo; - muls64(&lo, &hi, x, y); - *ret = lo; - return hi != ((int64_t)lo >> 63); -#endif } /** @@ -562,13 +582,7 @@ static inline bool smul64_overflow(int64_t x, int64_t y, int64_t *ret) */ static inline bool umul32_overflow(uint32_t x, uint32_t y, uint32_t *ret) { -#if __has_builtin(__builtin_mul_overflow) || __GNUC__ >= 5 return __builtin_mul_overflow(x, y, ret); -#else - uint64_t z = (uint64_t)x * y; - *ret = z; - return z > UINT32_MAX; -#endif } /** @@ -581,13 +595,7 @@ static inline bool umul32_overflow(uint32_t x, uint32_t y, uint32_t *ret) */ static inline bool umul64_overflow(uint64_t x, uint64_t y, uint64_t *ret) { -#if __has_builtin(__builtin_mul_overflow) || __GNUC__ >= 5 return __builtin_mul_overflow(x, y, ret); -#else - uint64_t hi; - mulu64(ret, &hi, x, y); - return hi != 0; -#endif } /* @@ -597,8 +605,7 @@ static inline bool umul64_overflow(uint64_t x, uint64_t y, uint64_t *ret) */ static inline bool mulu128(uint64_t *plow, uint64_t *phigh, uint64_t factor) { -#if defined(CONFIG_INT128) && \ - (__has_builtin(__builtin_mul_overflow) || __GNUC__ >= 5) +#if defined(CONFIG_INT128) bool res; __uint128_t r; __uint128_t f = ((__uint128_t)*phigh << 64) | *plow; @@ -661,7 +668,7 @@ static inline uint64_t uadd64_carry(uint64_t x, uint64_t y, bool *pcarry) */ static inline uint64_t usub64_borrow(uint64_t x, uint64_t y, bool *pborrow) { -#if __has_builtin(__builtin_subcll) +#if __has_builtin(__builtin_subcll) && !defined(BUILTIN_SUBCLL_BROKEN) unsigned long long b = *pborrow; x = __builtin_subcll(x, y, b, &b); *pborrow = b & 1; @@ -849,4 +856,6 @@ static inline uint64_t udiv_qrnnd(uint64_t *r, uint64_t n1, #endif } +Int128 divu256(Int128 *plow, Int128 *phigh, Int128 divisor); +Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor); #endif diff --git a/include/qemu/int128.h b/include/qemu/int128.h index ef71f56e3f..174bd7dafb 100644 --- a/include/qemu/int128.h +++ b/include/qemu/int128.h @@ -3,8 +3,14 @@ #include "qemu/bswap.h" -#ifdef CONFIG_INT128 +/* + * With TCI, we need to use libffi for interfacing with TCG helpers. + * But libffi does not support __int128_t, and therefore cannot pass + * or return values of this type, force use of the Int128 struct. + */ +#if defined(CONFIG_INT128) && !defined(CONFIG_TCG_INTERPRETER) typedef __int128_t Int128; +typedef __int128_t __attribute__((aligned(16))) Int128Aligned; static inline Int128 int128_make64(uint64_t a) { @@ -128,11 +134,21 @@ static inline bool int128_ge(Int128 a, Int128 b) return a >= b; } +static inline bool int128_uge(Int128 a, Int128 b) +{ + return ((__uint128_t)a) >= ((__uint128_t)b); +} + static inline bool int128_lt(Int128 a, Int128 b) { return a < b; } +static inline bool int128_ult(Int128 a, Int128 b) +{ + return (__uint128_t)a < (__uint128_t)b; +} + static inline bool int128_le(Int128 a, Int128 b) { return a <= b; @@ -177,6 +193,15 @@ static inline Int128 bswap128(Int128 a) #endif } +static inline int clz128(Int128 a) +{ + if (a >> 64) { + return __builtin_clzll(a >> 64); + } else { + return (a) ? __builtin_clzll((uint64_t)a) + 64 : 128; + } +} + static inline Int128 int128_divu(Int128 a, Int128 b) { return (__uint128_t)a / (__uint128_t)b; @@ -200,6 +225,7 @@ static inline Int128 int128_rems(Int128 a, Int128 b) #else /* !CONFIG_INT128 */ typedef struct Int128 Int128; +typedef struct Int128 __attribute__((aligned(16))) Int128Aligned; /* * We guarantee that the in-memory byte representation of an @@ -373,11 +399,21 @@ static inline bool int128_ge(Int128 a, Int128 b) return a.hi > b.hi || (a.hi == b.hi && a.lo >= b.lo); } +static inline bool int128_uge(Int128 a, Int128 b) +{ + return (uint64_t)a.hi > (uint64_t)b.hi || (a.hi == b.hi && a.lo >= b.lo); +} + static inline bool int128_lt(Int128 a, Int128 b) { return !int128_ge(a, b); } +static inline bool int128_ult(Int128 a, Int128 b) +{ + return !int128_uge(a, b); +} + static inline bool int128_le(Int128 a, Int128 b) { return int128_ge(b, a); @@ -418,12 +454,20 @@ static inline Int128 bswap128(Int128 a) return int128_make128(bswap64(a.hi), bswap64(a.lo)); } +static inline int clz128(Int128 a) +{ + if (a.hi) { + return __builtin_clzll(a.hi); + } else { + return (a.lo) ? __builtin_clzll(a.lo) + 64 : 128; + } +} + Int128 int128_divu(Int128, Int128); Int128 int128_remu(Int128, Int128); Int128 int128_divs(Int128, Int128); Int128 int128_rems(Int128, Int128); - -#endif /* CONFIG_INT128 */ +#endif /* CONFIG_INT128 && !CONFIG_TCG_INTERPRETER */ static inline void bswap128s(Int128 *s) { @@ -434,4 +478,19 @@ static inline void bswap128s(Int128 *s) #define INT128_MAX int128_make128(UINT64_MAX, INT64_MAX) #define INT128_MIN int128_make128(0, INT64_MIN) +/* + * When compiler supports a 128-bit type, define a combination of + * a possible structure and the native types. Ease parameter passing + * via use of the transparent union extension. + */ +#ifdef CONFIG_INT128_TYPE +typedef union { + __uint128_t u; + __int128_t i; + Int128 s; +} Int128Alias __attribute__((transparent_union)); +#else +typedef Int128 Int128Alias; +#endif /* CONFIG_INT128_TYPE */ + #endif /* INT128_H */ diff --git a/include/qemu/interval-tree.h b/include/qemu/interval-tree.h new file mode 100644 index 0000000000..25006debe8 --- /dev/null +++ b/include/qemu/interval-tree.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Interval trees. + * + * Derived from include/linux/interval_tree.h and its dependencies. + */ + +#ifndef QEMU_INTERVAL_TREE_H +#define QEMU_INTERVAL_TREE_H + +/* + * For now, don't expose Linux Red-Black Trees separately, but retain the + * separate type definitions to keep the implementation sane, and allow + * the possibility of disentangling them later. + */ +typedef struct RBNode +{ + /* Encodes parent with color in the lsb. */ + uintptr_t rb_parent_color; + struct RBNode *rb_right; + struct RBNode *rb_left; +} RBNode; + +typedef struct RBRoot +{ + RBNode *rb_node; +} RBRoot; + +typedef struct RBRootLeftCached { + RBRoot rb_root; + RBNode *rb_leftmost; +} RBRootLeftCached; + +typedef struct IntervalTreeNode +{ + RBNode rb; + + uint64_t start; /* Start of interval */ + uint64_t last; /* Last location _in_ interval */ + uint64_t subtree_last; +} IntervalTreeNode; + +typedef RBRootLeftCached IntervalTreeRoot; + +/** + * interval_tree_is_empty + * @root: root of the tree. + * + * Returns true if the tree contains no nodes. + */ +static inline bool interval_tree_is_empty(const IntervalTreeRoot *root) +{ + return root->rb_root.rb_node == NULL; +} + +/** + * interval_tree_insert + * @node: node to insert, + * @root: root of the tree. + * + * Insert @node into @root, and rebalance. + */ +void interval_tree_insert(IntervalTreeNode *node, IntervalTreeRoot *root); + +/** + * interval_tree_remove + * @node: node to remove, + * @root: root of the tree. + * + * Remove @node from @root, and rebalance. + */ +void interval_tree_remove(IntervalTreeNode *node, IntervalTreeRoot *root); + +/** + * interval_tree_iter_first: + * @root: root of the tree, + * @start, @last: the inclusive interval [start, last]. + * + * Locate the "first" of a set of nodes within the tree at @root + * that overlap the interval, where "first" is sorted by start. + * Returns NULL if no overlap found. + */ +IntervalTreeNode *interval_tree_iter_first(IntervalTreeRoot *root, + uint64_t start, uint64_t last); + +/** + * interval_tree_iter_next: + * @node: previous search result + * @start, @last: the inclusive interval [start, last]. + * + * Locate the "next" of a set of nodes within the tree that overlap the + * interval; @next is the result of a previous call to + * interval_tree_iter_{first,next}. Returns NULL if @next was the last + * node in the set. + */ +IntervalTreeNode *interval_tree_iter_next(IntervalTreeNode *node, + uint64_t start, uint64_t last); + +#endif /* QEMU_INTERVAL_TREE_H */ diff --git a/include/qemu/iov.h b/include/qemu/iov.h index 9330746680..63a1c01965 100644 --- a/include/qemu/iov.h +++ b/include/qemu/iov.h @@ -222,13 +222,11 @@ static inline void *qemu_iovec_buf(QEMUIOVector *qiov) void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint); void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov); -int qemu_iovec_init_extended( - QEMUIOVector *qiov, - void *head_buf, size_t head_len, - QEMUIOVector *mid_qiov, size_t mid_offset, size_t mid_len, - void *tail_buf, size_t tail_len); void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source, size_t offset, size_t len); +struct iovec *qemu_iovec_slice(QEMUIOVector *qiov, + size_t offset, size_t len, + size_t *head, size_t *tail, int *niov); int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len); void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len); void qemu_iovec_concat(QEMUIOVector *dst, diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h index c938fb0793..2a10a7052e 100644 --- a/include/qemu/iova-tree.h +++ b/include/qemu/iova-tree.h @@ -15,7 +15,7 @@ * Currently the iova tree will only allow to keep ranges * information, and no extra user data is allowed for each element. A * benefit is that we can merge adjacent ranges internally within the - * tree. It can save a lot of memory when the ranges are splitted but + * tree. It can save a lot of memory when the ranges are split but * mostly continuous. * * Note that current implementation does not provide any thread @@ -72,10 +72,8 @@ int iova_tree_insert(IOVATree *tree, const DMAMap *map); * provided. The range does not need to be exactly what has inserted, * all the mappings that are included in the provided range will be * removed from the tree. Here map->translated_addr is meaningless. - * - * Return: 0 if succeeded, or <0 if error. */ -int iova_tree_remove(IOVATree *tree, const DMAMap *map); +void iova_tree_remove(IOVATree *tree, DMAMap map); /** * iova_tree_find: @@ -130,7 +128,7 @@ const DMAMap *iova_tree_find_address(const IOVATree *tree, hwaddr iova); * iova_tree_foreach: * * @tree: the iova tree to iterate on - * @iterator: the interator for the mappings, return true to stop + * @iterator: the iterator for the mappings, return true to stop * * Iterate over the iova tree. * diff --git a/include/qemu/job.h b/include/qemu/job.h index c105b31076..2b873f2576 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -40,27 +40,60 @@ typedef struct JobTxn JobTxn; * Long-running operation. */ typedef struct Job { + + /* Fields set at initialization (job_create), and never modified */ + /** The ID of the job. May be NULL for internal jobs. */ char *id; - /** The type of this job. */ + /** + * The type of this job. + * All callbacks are called with job_mutex *not* held. + */ const JobDriver *driver; + /** + * The coroutine that executes the job. If not NULL, it is reentered when + * busy is false and the job is cancelled. + * Initialized in job_start() + */ + Coroutine *co; + + /** True if this job should automatically finalize itself */ + bool auto_finalize; + + /** True if this job should automatically dismiss itself */ + bool auto_dismiss; + + /** + * The completion function that will be called when the job completes. + */ + BlockCompletionFunc *cb; + + /** The opaque value that is passed to the completion function. */ + void *opaque; + + /* ProgressMeter API is thread-safe */ + ProgressMeter progress; + + /** + * AioContext to run the job coroutine in. + * The job Aiocontext can be read when holding *either* + * the BQL (so we are in the main loop) or the job_mutex. + * It can only be written when we hold *both* BQL + * and the job_mutex. + */ + AioContext *aio_context; + + + /** Protected by job_mutex */ + /** Reference count of the block job */ int refcnt; /** Current state; See @JobStatus for details. */ JobStatus status; - /** AioContext to run the job coroutine in */ - AioContext *aio_context; - - /** - * The coroutine that executes the job. If not NULL, it is reentered when - * busy is false and the job is cancelled. - */ - Coroutine *co; - /** * Timer that is used by @job_sleep_ns. Accessed under job_mutex (in * job.c). @@ -76,7 +109,7 @@ typedef struct Job { /** * Set to false by the job while the coroutine has yielded and may be * re-entered by job_enter(). There may still be I/O or event loop activity - * pending. Accessed under block_job_mutex (in blockjob.c). + * pending. Accessed under job_mutex. * * When the job is deferred to the main loop, busy is true as long as the * bottom half is still pending. @@ -112,14 +145,6 @@ typedef struct Job { /** Set to true when the job has deferred work to the main loop. */ bool deferred_to_main_loop; - /** True if this job should automatically finalize itself */ - bool auto_finalize; - - /** True if this job should automatically dismiss itself */ - bool auto_dismiss; - - ProgressMeter progress; - /** * Return code from @run and/or @prepare callback(s). * Not final until the job has reached the CONCLUDED status. @@ -134,12 +159,6 @@ typedef struct Job { */ Error *err; - /** The completion function that will be called when the job completes. */ - BlockCompletionFunc *cb; - - /** The opaque value that is passed to the completion function. */ - void *opaque; - /** Notifiers called when a cancelled job is finalised */ NotifierList on_finalize_cancelled; @@ -167,6 +186,7 @@ typedef struct Job { /** * Callbacks and other information about a Job driver. + * All callbacks are invoked with job_mutex *not* held. */ struct JobDriver { @@ -288,7 +308,9 @@ struct JobDriver { bool (*cancel)(Job *job, bool force); - /** Called when the job is freed */ + /** + * Called when the job is freed. + */ void (*free)(Job *job); }; @@ -303,6 +325,30 @@ typedef enum JobCreateFlags { JOB_MANUAL_DISMISS = 0x04, } JobCreateFlags; +extern QemuMutex job_mutex; + +#define JOB_LOCK_GUARD() QEMU_LOCK_GUARD(&job_mutex) + +#define WITH_JOB_LOCK_GUARD() WITH_QEMU_LOCK_GUARD(&job_mutex) + +/** + * job_lock: + * + * Take the mutex protecting the list of jobs and their status. + * Most functions called by the monitor need to call job_lock + * and job_unlock manually. On the other hand, function called + * by the block jobs themselves and by the block layer will take the + * lock for you. + */ +void job_lock(void); + +/** + * job_unlock: + * + * Release the mutex protecting the list of jobs and their status. + */ +void job_unlock(void); + /** * Allocate and return a new job transaction. Jobs can be added to the * transaction using job_txn_add_job(). @@ -319,23 +365,20 @@ JobTxn *job_txn_new(void); /** * Release a reference that was previously acquired with job_txn_add_job or * job_txn_new. If it's the last reference to the object, it will be freed. + * + * Called with job lock *not* held. */ void job_txn_unref(JobTxn *txn); -/** - * @txn: The transaction (may be NULL) - * @job: Job to add to the transaction - * - * Add @job to the transaction. The @job must not already be in a transaction. - * The caller must call either job_txn_unref() or job_completed() to release - * the reference that is automatically grabbed here. - * - * If @txn is NULL, the function does nothing. +/* + * Same as job_txn_unref(), but called with job lock held. + * Might release the lock temporarily. */ -void job_txn_add_job(JobTxn *txn, Job *job); +void job_txn_unref_locked(JobTxn *txn); /** * Create a new long-running job and return it. + * Called with job_mutex *not* held. * * @job_id: The id of the newly-created job, or %NULL for internal jobs * @driver: The class object for the newly-created job. @@ -353,20 +396,26 @@ void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, /** * Add a reference to Job refcnt, it will be decreased with job_unref, and then * be freed if it comes to be the last reference. + * + * Called with job lock held. */ -void job_ref(Job *job); +void job_ref_locked(Job *job); /** - * Release a reference that was previously acquired with job_ref() or + * Release a reference that was previously acquired with job_ref_locked() or * job_create(). If it's the last reference to the object, it will be freed. + * + * Called with job lock held. */ -void job_unref(Job *job); +void job_unref_locked(Job *job); /** * @job: The job that has made progress * @done: How much progress the job made since the last call * * Updates the progress counter of the job. + * + * May be called with mutex held or not held. */ void job_progress_update(Job *job, uint64_t done); @@ -377,6 +426,8 @@ void job_progress_update(Job *job, uint64_t done); * * Sets the expected end value of the progress counter of a job so that a * completion percentage can be calculated when the progress is updated. + * + * May be called with mutex held or not held. */ void job_progress_set_remaining(Job *job, uint64_t remaining); @@ -392,27 +443,27 @@ void job_progress_set_remaining(Job *job, uint64_t remaining); * length before, and job_progress_update() afterwards. * (So the operation acts as a parenthesis in regards to the main job * operation running in background.) + * + * May be called with mutex held or not held. */ void job_progress_increase_remaining(Job *job, uint64_t delta); -/** To be called when a cancelled job is finalised. */ -void job_event_cancelled(Job *job); - -/** To be called when a successfully completed job is finalised. */ -void job_event_completed(Job *job); - /** * Conditionally enter the job coroutine if the job is ready to run, not * already busy and fn() returns true. fn() is called while under the job_lock * critical section. + * + * Called with job lock held, but might release it temporarily. */ -void job_enter_cond(Job *job, bool(*fn)(Job *job)); +void job_enter_cond_locked(Job *job, bool(*fn)(Job *job)); /** * @job: A job that has not yet been started. * * Begins execution of a job. * Takes ownership of one reference to the job object. + * + * Called with job_mutex *not* held. */ void job_start(Job *job); @@ -420,6 +471,7 @@ void job_start(Job *job); * @job: The job to enter. * * Continue the specified job by entering the coroutine. + * Called with job_mutex *not* held. */ void job_enter(Job *job); @@ -428,15 +480,18 @@ void job_enter(Job *job); * * Pause now if job_pause() has been called. Jobs that perform lots of I/O * must call this between requests so that the job can be paused. + * + * Called with job_mutex *not* held. */ -void coroutine_fn job_pause_point(Job *job); +void coroutine_fn GRAPH_UNLOCKED job_pause_point(Job *job); /** * @job: The job that calls the function. * * Yield the job coroutine. + * Called with job_mutex *not* held. */ -void job_yield(Job *job); +void coroutine_fn job_yield(Job *job); /** * @job: The job that calls the function. @@ -445,10 +500,11 @@ void job_yield(Job *job); * Put the job to sleep (assuming that it wasn't canceled) for @ns * %QEMU_CLOCK_REALTIME nanoseconds. Canceling the job will immediately * interrupt the wait. + * + * Called with job_mutex *not* held. */ void coroutine_fn job_sleep_ns(Job *job, int64_t ns); - /** Returns the JobType of a given Job. */ JobType job_type(const Job *job); @@ -458,88 +514,138 @@ const char *job_type_str(const Job *job); /** Returns true if the job should not be visible to the management layer. */ bool job_is_internal(Job *job); -/** Returns whether the job is being cancelled. */ +/** + * Returns whether the job is being cancelled. + * Called with job_mutex *not* held. + */ bool job_is_cancelled(Job *job); +/* Same as job_is_cancelled(), but called with job lock held. */ +bool job_is_cancelled_locked(Job *job); + /** * Returns whether the job is scheduled for cancellation (at an * indefinite point). + * Called with job_mutex *not* held. */ bool job_cancel_requested(Job *job); -/** Returns whether the job is in a completed state. */ -bool job_is_completed(Job *job); +/** + * Returns whether the job is in a completed state. + * Called with job lock held. + */ +bool job_is_completed_locked(Job *job); -/** Returns whether the job is ready to be completed. */ +/** + * Returns whether the job is ready to be completed. + * Called with job_mutex *not* held. + */ bool job_is_ready(Job *job); +/* Same as job_is_ready(), but called with job lock held. */ +bool job_is_ready_locked(Job *job); + /** * Request @job to pause at the next pause point. Must be paired with * job_resume(). If the job is supposed to be resumed by user action, call - * job_user_pause() instead. + * job_user_pause_locked() instead. + * + * Called with job lock *not* held. */ void job_pause(Job *job); -/** Resumes a @job paused with job_pause. */ +/* Same as job_pause(), but called with job lock held. */ +void job_pause_locked(Job *job); + +/** Resumes a @job paused with job_pause. Called with job lock *not* held. */ void job_resume(Job *job); +/* + * Same as job_resume(), but called with job lock held. + * Might release the lock temporarily. + */ +void job_resume_locked(Job *job); + /** * Asynchronously pause the specified @job. * Do not allow a resume until a matching call to job_user_resume. + * Called with job lock held. */ -void job_user_pause(Job *job, Error **errp); +void job_user_pause_locked(Job *job, Error **errp); -/** Returns true if the job is user-paused. */ -bool job_user_paused(Job *job); +/** + * Returns true if the job is user-paused. + * Called with job lock held. + */ +bool job_user_paused_locked(Job *job); /** * Resume the specified @job. - * Must be paired with a preceding job_user_pause. + * Must be paired with a preceding job_user_pause_locked. + * Called with job lock held, but might release it temporarily. */ -void job_user_resume(Job *job, Error **errp); +void job_user_resume_locked(Job *job, Error **errp); /** * Get the next element from the list of block jobs after @job, or the * first one if @job is %NULL. * * Returns the requested job, or %NULL if there are no more jobs left. + * Called with job lock *not* held. */ Job *job_next(Job *job); +/* Same as job_next(), but called with job lock held. */ +Job *job_next_locked(Job *job); + /** * Get the job identified by @id (which must not be %NULL). * * Returns the requested job, or %NULL if it doesn't exist. + * Called with job lock held. */ -Job *job_get(const char *id); +Job *job_get_locked(const char *id); /** * Check whether the verb @verb can be applied to @job in its current state. * Returns 0 if the verb can be applied; otherwise errp is set and -EPERM * returned. + * + * Called with job lock held. */ -int job_apply_verb(Job *job, JobVerb verb, Error **errp); +int job_apply_verb_locked(Job *job, JobVerb verb, Error **errp); -/** The @job could not be started, free it. */ +/** + * The @job could not be started, free it. + * Called with job_mutex *not* held. + */ void job_early_fail(Job *job); -/** Moves the @job from RUNNING to READY */ +/** + * Moves the @job from RUNNING to READY. + * Called with job_mutex *not* held. + */ void job_transition_to_ready(Job *job); -/** Asynchronously complete the specified @job. */ -void job_complete(Job *job, Error **errp); +/** + * Asynchronously complete the specified @job. + * Called with job lock held, but might release it temporarily. + */ +void job_complete_locked(Job *job, Error **errp); /** * Asynchronously cancel the specified @job. If @force is true, the job should * be cancelled immediately without waiting for a consistent state. + * Called with job lock held. */ -void job_cancel(Job *job, bool force); +void job_cancel_locked(Job *job, bool force); /** - * Cancels the specified job like job_cancel(), but may refuse to do so if the - * operation isn't meaningful in the current state of the job. + * Cancels the specified job like job_cancel_locked(), but may refuse + * to do so if the operation isn't meaningful in the current state of the job. + * Called with job lock held. */ -void job_user_cancel(Job *job, bool force, Error **errp); +void job_user_cancel_locked(Job *job, bool force, Error **errp); /** * Synchronously cancel the @job. The completion callback is called @@ -550,16 +656,23 @@ void job_user_cancel(Job *job, bool force, Error **errp); * Returns the return value from the job if the job actually completed * during the call, or -ECANCELED if it was canceled. * - * Callers must hold the AioContext lock of job->aio_context. + * Called with job_lock *not* held. */ int job_cancel_sync(Job *job, bool force); -/** Synchronously force-cancels all jobs using job_cancel_sync(). */ +/* Same as job_cancel_sync, but called with job lock held. */ +int job_cancel_sync_locked(Job *job, bool force); + +/** + * Synchronously force-cancels all jobs using job_cancel_sync_locked(). + * + * Called with job_lock *not* held. + */ void job_cancel_sync_all(void); /** * @job: The job to be completed. - * @errp: Error object which may be set by job_complete(); this is not + * @errp: Error object which may be set by job_complete_locked(); this is not * necessarily set on every error, the job return value has to be * checked as well. * @@ -568,10 +681,9 @@ void job_cancel_sync_all(void); * function). * * Returns the return value from the job. - * - * Callers must hold the AioContext lock of job->aio_context. + * Called with job_lock held. */ -int job_complete_sync(Job *job, Error **errp); +int job_complete_sync_locked(Job *job, Error **errp); /** * For a @job that has finished its work and is pending awaiting explicit @@ -580,14 +692,18 @@ int job_complete_sync(Job *job, Error **errp); * FIXME: Make the below statement universally true: * For jobs that support the manual workflow mode, all graph changes that occur * as a result will occur after this command and before a successful reply. + * + * Called with job lock held. */ -void job_finalize(Job *job, Error **errp); +void job_finalize_locked(Job *job, Error **errp); /** * Remove the concluded @job from the query list and resets the passed pointer * to %NULL. Returns an error if the job is not actually concluded. + * + * Called with job lock held. */ -void job_dismiss(Job **job, Error **errp); +void job_dismiss_locked(Job **job, Error **errp); /** * Synchronously finishes the given @job. If @finish is given, it is called to @@ -596,8 +712,20 @@ void job_dismiss(Job **job, Error **errp); * Returns 0 if the job is successfully completed, -ECANCELED if the job was * cancelled before completing, and -errno in other error cases. * - * Callers must hold the AioContext lock of job->aio_context. + * Called with job_lock held, but might release it temporarily. */ -int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp); +int job_finish_sync_locked(Job *job, void (*finish)(Job *, Error **errp), + Error **errp); + +/** + * Sets the @job->aio_context. + * Called with job_mutex *not* held. + * + * This function must run in the main thread to protect against + * concurrent read in job_finish_sync_locked(), takes the job_mutex + * lock to protect against the read in job_do_yield_locked(), and must + * be called when the job is quiescent. + */ +void job_set_aio_context(Job *job, AioContext *ctx); #endif diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h index 86db7cb04c..9823220446 100644 --- a/include/qemu/lockable.h +++ b/include/qemu/lockable.h @@ -13,7 +13,7 @@ #ifndef QEMU_LOCKABLE_H #define QEMU_LOCKABLE_H -#include "qemu/coroutine.h" +#include "qemu/coroutine-core.h" #include "qemu/thread.h" typedef void QemuLockUnlockFunc(void *); diff --git a/include/qemu/log.h b/include/qemu/log.h index c5643d8dd5..df59bfabcd 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -35,6 +35,7 @@ bool qemu_log_separate(void); /* LOG_STRACE is used for user-mode strace logging. */ #define LOG_STRACE (1 << 19) #define LOG_PER_THREAD (1 << 20) +#define CPU_LOG_TB_VPU (1 << 21) /* Lock/unlock output. */ diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 5518845299..5764db157c 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -157,6 +157,8 @@ typedef void WaitObjectFunc(void *opaque); * in the main loop's calls to WaitForMultipleObjects. When the handle * is in a signaled state, QEMU will call @func. * + * If the same HANDLE is added twice, this function returns -1. + * * @handle: The Windows handle to be observed. * @func: A function to be called when @handle is in a signaled state. * @opaque: A pointer-size value that is passed to @func. @@ -246,19 +248,19 @@ GSource *iohandler_get_g_source(void); AioContext *iohandler_get_aio_context(void); /** - * qemu_mutex_iothread_locked: Return lock status of the main loop mutex. + * bql_locked: Return lock status of the Big QEMU Lock (BQL) * - * The main loop mutex is the coarsest lock in QEMU, and as such it + * The Big QEMU Lock (BQL) is the coarsest lock in QEMU, and as such it * must always be taken outside other locks. This function helps * functions take different paths depending on whether the current - * thread is running within the main loop mutex. + * thread is running within the BQL. * * This function should never be used in the block layer, because * unit tests, block layer tools and qemu-storage-daemon do not * have a BQL. * Please instead refer to qemu_in_main_thread(). */ -bool qemu_mutex_iothread_locked(void); +bool bql_locked(void); /** * qemu_in_main_thread: return whether it's possible to safely access @@ -279,89 +281,116 @@ bool qemu_mutex_iothread_locked(void); */ bool qemu_in_main_thread(void); -/* Mark and check that the function is part of the global state API. */ -#ifdef CONFIG_COCOA /* - * When using the Cocoa UI, addRemovableDevicesMenuItems() is called from - * a thread different from the QEMU main thread and can not take the BQL, - * triggering this assertions in the block layer (commit 0439c5a462). - * As the Cocoa fix is not trivial, disable this assertion for the v7.0.0 - * release (when using Cocoa); we will restore it immediately after the - * release. - * This issue is tracked as https://gitlab.com/qemu-project/qemu/-/issues/926 + * Mark and check that the function is part of the Global State API. + * Please refer to include/block/block-global-state.h for more + * information about GS API. */ -#define GLOBAL_STATE_CODE() -#else #define GLOBAL_STATE_CODE() \ do { \ assert(qemu_in_main_thread()); \ } while (0) -#endif /* CONFIG_COCOA */ -/* Mark and check that the function is part of the I/O API. */ +/* + * Mark and check that the function is part of the I/O API. + * Please refer to include/block/block-io.h for more + * information about IO API. + */ #define IO_CODE() \ do { \ /* nop */ \ } while (0) -/* Mark and check that the function is part of the "I/O OR GS" API. */ +/* + * Mark and check that the function is part of the "I/O OR GS" API. + * Please refer to include/block/block-io.h for more + * information about "IO or GS" API. + */ #define IO_OR_GS_CODE() \ do { \ /* nop */ \ } while (0) /** - * qemu_mutex_lock_iothread: Lock the main loop mutex. + * bql_lock: Lock the Big QEMU Lock (BQL). * - * This function locks the main loop mutex. The mutex is taken by + * This function locks the Big QEMU Lock (BQL). The lock is taken by * main() in vl.c and always taken except while waiting on - * external events (such as with select). The mutex should be taken + * external events (such as with select). The lock should be taken * by threads other than the main loop thread when calling * qemu_bh_new(), qemu_set_fd_handler() and basically all other * functions documented in this file. * - * NOTE: tools currently are single-threaded and qemu_mutex_lock_iothread + * NOTE: tools currently are single-threaded and bql_lock * is a no-op there. */ -#define qemu_mutex_lock_iothread() \ - qemu_mutex_lock_iothread_impl(__FILE__, __LINE__) -void qemu_mutex_lock_iothread_impl(const char *file, int line); +#define bql_lock() bql_lock_impl(__FILE__, __LINE__) +void bql_lock_impl(const char *file, int line); /** - * qemu_mutex_unlock_iothread: Unlock the main loop mutex. + * bql_unlock: Unlock the Big QEMU Lock (BQL). * - * This function unlocks the main loop mutex. The mutex is taken by + * This function unlocks the Big QEMU Lock. The lock is taken by * main() in vl.c and always taken except while waiting on - * external events (such as with select). The mutex should be unlocked + * external events (such as with select). The lock should be unlocked * as soon as possible by threads other than the main loop thread, * because it prevents the main loop from processing callbacks, * including timers and bottom halves. * - * NOTE: tools currently are single-threaded and qemu_mutex_unlock_iothread + * NOTE: tools currently are single-threaded and bql_unlock * is a no-op there. */ -void qemu_mutex_unlock_iothread(void); +void bql_unlock(void); + +/** + * BQL_LOCK_GUARD + * + * Wrap a block of code in a conditional bql_{lock,unlock}. + */ +typedef struct BQLLockAuto BQLLockAuto; + +static inline BQLLockAuto *bql_auto_lock(const char *file, int line) +{ + if (bql_locked()) { + return NULL; + } + bql_lock_impl(file, line); + /* Anything non-NULL causes the cleanup function to be called */ + return (BQLLockAuto *)(uintptr_t)1; +} + +static inline void bql_auto_unlock(BQLLockAuto *l) +{ + bql_unlock(); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(BQLLockAuto, bql_auto_unlock) + +#define BQL_LOCK_GUARD() \ + g_autoptr(BQLLockAuto) _bql_lock_auto __attribute__((unused)) \ + = bql_auto_lock(__FILE__, __LINE__) /* - * qemu_cond_wait_iothread: Wait on condition for the main loop mutex + * qemu_cond_wait_bql: Wait on condition for the Big QEMU Lock (BQL) * - * This function atomically releases the main loop mutex and causes + * This function atomically releases the Big QEMU Lock (BQL) and causes * the calling thread to block on the condition. */ -void qemu_cond_wait_iothread(QemuCond *cond); +void qemu_cond_wait_bql(QemuCond *cond); /* - * qemu_cond_timedwait_iothread: like the previous, but with timeout + * qemu_cond_timedwait_bql: like the previous, but with timeout */ -void qemu_cond_timedwait_iothread(QemuCond *cond, int ms); +void qemu_cond_timedwait_bql(QemuCond *cond, int ms); /* internal interfaces */ -void qemu_fd_register(int fd); - +#define qemu_bh_new_guarded(cb, opaque, guard) \ + qemu_bh_new_full((cb), (opaque), (stringify(cb)), guard) #define qemu_bh_new(cb, opaque) \ - qemu_bh_new_full((cb), (opaque), (stringify(cb))) -QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name); + qemu_bh_new_full((cb), (opaque), (stringify(cb)), NULL) +QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name, + MemReentrancyGuard *reentrancy_guard); void qemu_bh_schedule_idle(QEMUBH *bh); enum { diff --git a/include/qemu/mmap-alloc.h b/include/qemu/mmap-alloc.h index 5076695cc8..8344daaa03 100644 --- a/include/qemu/mmap-alloc.h +++ b/include/qemu/mmap-alloc.h @@ -1,10 +1,15 @@ #ifndef QEMU_MMAP_ALLOC_H #define QEMU_MMAP_ALLOC_H +typedef enum { + QEMU_FS_TYPE_UNKNOWN = 0, + QEMU_FS_TYPE_TMPFS, + QEMU_FS_TYPE_HUGETLBFS, + QEMU_FS_TYPE_NUM, +} QemuFsType; size_t qemu_fd_getpagesize(int fd); - -size_t qemu_mempath_getpagesize(const char *mem_path); +QemuFsType qemu_fd_getfs(int fd); /** * qemu_ram_mmap: mmap anonymous memory, the specified file or device. diff --git a/include/qemu/module.h b/include/qemu/module.h index bd73607104..c37ce74b16 100644 --- a/include/qemu/module.h +++ b/include/qemu/module.h @@ -61,16 +61,43 @@ typedef enum { #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, false) -#define ui_module_load_one(lib) module_load_one("ui-", lib, false) -#define audio_module_load_one(lib) module_load_one("audio-", lib, false) +#define block_module_load(lib, errp) module_load("block-", lib, errp) +#define ui_module_load(lib, errp) module_load("ui-", lib, errp) +#define audio_module_load(lib, errp) module_load("audio-", lib, errp) void register_module_init(void (*fn)(void), module_init_type type); void register_dso_module_init(void (*fn)(void), module_init_type type); void module_call_init(module_init_type type); -bool module_load_one(const char *prefix, const char *lib_name, bool mayfail); -void module_load_qom_one(const char *type); + +/* + * module_load: attempt to load a module from a set of directories + * + * directories searched are: + * - getenv("QEMU_MODULE_DIR") + * - get_relocated_path(CONFIG_QEMU_MODDIR); + * - /var/run/qemu/${version_dir} + * + * prefix: a subsystem prefix, or the empty string ("audio-", ..., "") + * name: name of the module + * errp: error to set in case the module is found, but load failed. + * + * Return value: -1 on error (errp set if not NULL). + * 0 if module or one of its dependencies are not installed, + * 1 if the module is found and loaded, + * 2 if the module is already loaded, or module is built-in. + */ +int module_load(const char *prefix, const char *name, Error **errp); + +/* + * module_load_qom: attempt to load a module to provide a QOM type + * + * type: the type to be provided + * errp: error to set. + * + * Return value: as per module_load. + */ +int module_load_qom(const char *type, Error **errp); void module_load_qom_all(void); void module_allow_arch(const char *arch); diff --git a/include/qemu/notify.h b/include/qemu/notify.h index bcfa70fb2e..abf18dbf59 100644 --- a/include/qemu/notify.h +++ b/include/qemu/notify.h @@ -45,12 +45,16 @@ bool notifier_list_empty(NotifierList *list); /* Same as Notifier but allows .notify() to return errors */ typedef struct NotifierWithReturn NotifierWithReturn; +/* Return int to allow for different failure modes and recovery actions */ +typedef int (*NotifierWithReturnFunc)(NotifierWithReturn *notifier, void *data, + Error **errp); + struct NotifierWithReturn { /** * Return 0 on success (next notifier will be invoked), otherwise * notifier_with_return_list_notify() will stop and return the value. */ - int (*notify)(NotifierWithReturn *notifier, void *data); + NotifierWithReturnFunc notify; QLIST_ENTRY(NotifierWithReturn) node; }; @@ -69,6 +73,6 @@ void notifier_with_return_list_add(NotifierWithReturnList *list, void notifier_with_return_remove(NotifierWithReturn *notifier); int notifier_with_return_list_notify(NotifierWithReturnList *list, - void *data); + void *data, Error **errp); #endif diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index b1c161c035..c7053cdc2b 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -27,6 +27,10 @@ #ifndef QEMU_OSDEP_H #define QEMU_OSDEP_H +#if !defined _FORTIFY_SOURCE && defined __OPTIMIZE__ && __OPTIMIZE__ && defined __linux__ +# define _FORTIFY_SOURCE 2 +#endif + #include "config-host.h" #ifdef NEED_CPU_H #include CONFIG_TARGET @@ -75,7 +79,7 @@ QEMU_EXTERN_C int daemon(int, int); #ifdef _WIN32 /* as defined in sdkddkver.h */ #ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0601 /* Windows 7 API (should be in sync with glib) */ +#define _WIN32_WINNT 0x0602 /* Windows 8 API (should be >= the one from glib) */ #endif /* reduces the number of implicitly included headers */ #ifndef WIN32_LEAN_AND_MEAN @@ -88,6 +92,19 @@ QEMU_EXTERN_C int daemon(int, int); #define __USE_MINGW_ANSI_STDIO 1 #endif +/* + * We need the FreeBSD "legacy" definitions. Rust needs the FreeBSD 11 system + * calls since it doesn't use libc at all, so we have to emulate that despite + * FreeBSD 11 being EOL'd. + */ +#ifdef __FreeBSD__ +#define _WANT_FREEBSD11_STAT +#define _WANT_FREEBSD11_STATFS +#define _WANT_FREEBSD11_DIRENT +#define _WANT_KERNEL_ERRNO +#define _WANT_SEMUN +#endif + #include #include #include @@ -157,6 +174,66 @@ extern "C" { #include "qemu/typedefs.h" +/** + * Mark a function that executes in coroutine context + * + * Functions that execute in coroutine context cannot be called directly from + * normal functions. In the future it would be nice to enable compiler or + * static checker support for catching such errors. This annotation might make + * it possible and in the meantime it serves as documentation. + * + * For example: + * + * static void coroutine_fn foo(void) { + * .... + * } + */ +#ifdef __clang__ +#define coroutine_fn QEMU_ANNOTATE("coroutine_fn") +#else +#define coroutine_fn +#endif + +/** + * Mark a function that can suspend when executed in coroutine context, + * but can handle running in non-coroutine context too. + */ +#ifdef __clang__ +#define coroutine_mixed_fn QEMU_ANNOTATE("coroutine_mixed_fn") +#else +#define coroutine_mixed_fn +#endif + +/** + * Mark a function that should not be called from a coroutine context. + * Usually there will be an analogous, coroutine_fn function that should + * be used instead. + * + * When the function is also marked as coroutine_mixed_fn, the function should + * only be called if the caller does not know whether it is in coroutine + * context. + * + * Functions that are only no_coroutine_fn, on the other hand, should not + * be called from within coroutines at all. This for example includes + * functions that block. + * + * In the future it would be nice to enable compiler or static checker + * support for catching such errors. This annotation is the first step + * towards this, and in the meantime it serves as documentation. + * + * For example: + * + * static void no_coroutine_fn foo(void) { + * .... + * } + */ +#ifdef __clang__ +#define no_coroutine_fn QEMU_ANNOTATE("no_coroutine_fn") +#else +#define no_coroutine_fn +#endif + + /* * For mingw, as of v6.0.0, the function implementing the assert macro is * not marked as noreturn, so the compiler cannot delete code following an @@ -177,7 +254,7 @@ extern "C" { * supports QEMU_ERROR, this will be reported at compile time; otherwise * this will be reported at link time due to the missing symbol. */ -extern G_NORETURN +G_NORETURN void QEMU_ERROR("code path is reachable") qemu_build_not_reached_always(void); #if defined(__OPTIMIZE__) && !defined(__NO_INLINE__) @@ -186,6 +263,14 @@ void QEMU_ERROR("code path is reachable") #define qemu_build_not_reached() g_assert_not_reached() #endif +/** + * qemu_build_assert() + * + * The compiler, during optimization, is expected to prove that the + * assertion is true. + */ +#define qemu_build_assert(test) while (!(test)) qemu_build_not_reached() + /* * According to waitpid man page: * WCOREDUMP @@ -221,9 +306,6 @@ void QEMU_ERROR("code path is reachable") #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif -#ifndef MAP_FIXED_NOREPLACE -#define MAP_FIXED_NOREPLACE 0 -#endif #ifndef MAP_NORESERVE #define MAP_NORESERVE 0 #endif @@ -243,7 +325,13 @@ void QEMU_ERROR("code path is reachable") #define ESHUTDOWN 4099 #endif -#define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) +#define RETRY_ON_EINTR(expr) \ + (__extension__ \ + ({ typeof(expr) __result; \ + do { \ + __result = (expr); \ + } while (__result == -1 && errno == EINTR); \ + __result; })) /* time_t may be either 32 or 64 bits depending on the host OS, and * can be either signed or unsigned, so we can't just hardcode a @@ -299,19 +387,28 @@ void QEMU_ERROR("code path is reachable") * determined by the pre-processor instead of the compiler, you'll * have to open-code it. Sadly, Coverity is severely confused by the * constant variants, so we have to dumb things down there. + * + * Preprocessor sorcery ahead: use different identifiers for the local + * variables in each expansion, so we can nest macro calls without + * shadowing variables. */ -#undef MIN -#define MIN(a, b) \ +#define MIN_INTERNAL(a, b, _a, _b) \ ({ \ typeof(1 ? (a) : (b)) _a = (a), _b = (b); \ _a < _b ? _a : _b; \ }) -#undef MAX -#define MAX(a, b) \ +#undef MIN +#define MIN(a, b) \ + MIN_INTERNAL((a), (b), MAKE_IDENTFIER(_a), MAKE_IDENTFIER(_b)) + +#define MAX_INTERNAL(a, b, _a, _b) \ ({ \ typeof(1 ? (a) : (b)) _a = (a), _b = (b); \ _a > _b ? _a : _b; \ }) +#undef MAX +#define MAX(a, b) \ + MAX_INTERNAL((a), (b), MAKE_IDENTFIER(_a), MAKE_IDENTFIER(_b)) #ifdef __COVERITY__ # define MIN_CONST(a, b) ((a) < (b) ? (a) : (b)) @@ -332,14 +429,18 @@ void QEMU_ERROR("code path is reachable") /* * Minimum function that returns zero only if both values are zero. * Intended for use with unsigned values only. + * + * Preprocessor sorcery ahead: use different identifiers for the local + * variables in each expansion, so we can nest macro calls without + * shadowing variables. */ -#ifndef MIN_NON_ZERO -#define MIN_NON_ZERO(a, b) \ +#define MIN_NON_ZERO_INTERNAL(a, b, _a, _b) \ ({ \ typeof(1 ? (a) : (b)) _a = (a), _b = (b); \ _a == 0 ? _b : (_b == 0 || _b > _a) ? _a : _b; \ }) -#endif +#define MIN_NON_ZERO(a, b) \ + MIN_NON_ZERO_INTERNAL((a), (b), MAKE_IDENTFIER(_a), MAKE_IDENTFIER(_b)) /* * Round number down to multiple. Safe when m is not a power of 2 (see @@ -407,15 +508,17 @@ void qemu_anon_ram_free(void *ptr, size_t size); #ifdef _WIN32 #define HAVE_CHARDEV_SERIAL 1 -#elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ +#define HAVE_CHARDEV_PARALLEL 1 +#else +#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \ || defined(__GLIBC__) || defined(__APPLE__) #define HAVE_CHARDEV_SERIAL 1 #endif - -#if defined(__linux__) || defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || defined(__DragonFly__) -#define HAVE_CHARDEV_PARPORT 1 +#if defined(__linux__) || defined(__FreeBSD__) \ + || defined(__FreeBSD_kernel__) || defined(__DragonFly__) +#define HAVE_CHARDEV_PARALLEL 1 +#endif #endif #if defined(__HAIKU__) @@ -427,7 +530,7 @@ void qemu_anon_ram_free(void *ptr, size_t size); * See MySQL bug #7156 (http://bugs.mysql.com/bug.php?id=7156) for discussion * about Solaris missing the madvise() prototype. */ -extern int madvise(char *, size_t, int); +int madvise(char *, size_t, int); #endif #if defined(CONFIG_LINUX) @@ -451,6 +554,14 @@ extern int madvise(char *, size_t, int); # define QEMU_VMALLOC_ALIGN (256 * 4096) #elif defined(__linux__) && defined(__sparc__) # define QEMU_VMALLOC_ALIGN MAX(qemu_real_host_page_size(), SHMLBA) +#elif defined(__linux__) && defined(__loongarch__) + /* + * For transparent hugepage optimization, it has better be huge page + * aligned. LoongArch host system supports two kinds of pagesize: 4K + * and 16K, here calculate huge page size from host page size + */ +# define QEMU_VMALLOC_ALIGN (qemu_real_host_page_size() * \ + qemu_real_host_page_size() / sizeof(long)) #else # define QEMU_VMALLOC_ALIGN qemu_real_host_page_size() #endif @@ -568,8 +679,41 @@ unsigned long qemu_getauxval(unsigned long type); void qemu_set_tty_echo(int fd, bool echo); -void os_mem_prealloc(int fd, char *area, size_t sz, int smp_cpus, - Error **errp); +typedef struct ThreadContext ThreadContext; + +/** + * qemu_prealloc_mem: + * @fd: the fd mapped into the area, -1 for anonymous memory + * @area: start address of the are to preallocate + * @sz: the size of the area to preallocate + * @max_threads: maximum number of threads to use + * @tc: prealloc context threads pointer, NULL if not in use + * @async: request asynchronous preallocation, requires @tc + * @errp: returns an error if this function fails + * + * Preallocate memory (populate/prefault page tables writable) for the virtual + * memory area starting at @area with the size of @sz. After a successful call, + * each page in the area was faulted in writable at least once, for example, + * after allocating file blocks for mapped files. + * + * When setting @async, allocation might be performed asynchronously. + * qemu_finish_async_prealloc_mem() must be called to finish any asynchronous + * preallocation. + * + * Return: true on success, else false setting @errp with error. + */ +bool qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, + ThreadContext *tc, bool async, Error **errp); + +/** + * qemu_finish_async_prealloc_mem: + * @errp: returns an error if this function fails + * + * Finish all outstanding asynchronous memory preallocation. + * + * Return: true on success, else false setting @errp with error. + */ +bool qemu_finish_async_prealloc_mem(Error **errp); /** * qemu_get_pid_name: @@ -581,20 +725,6 @@ void os_mem_prealloc(int fd, char *area, size_t sz, int smp_cpus, */ char *qemu_get_pid_name(pid_t pid); -/** - * qemu_fork: - * - * A version of fork that avoids signal handler race - * conditions that can lead to child process getting - * signals that are otherwise only expected by the - * parent. It also resets all signal handlers to the - * default settings. - * - * Returns 0 to child process, pid number to parent - * or -1 on failure. - */ -pid_t qemu_fork(Error **errp); - /* Using intptr_t ensures that qemu_*_page_mask is sign-extended even * when intptr_t is 32-bit and we are aligning a long long. */ diff --git a/include/qemu/plugin-event.h b/include/qemu/plugin-event.h new file mode 100644 index 0000000000..7056d8427b --- /dev/null +++ b/include/qemu/plugin-event.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef QEMU_PLUGIN_EVENT_H +#define QEMU_PLUGIN_EVENT_H + +/* + * Events that plugins can subscribe to. + */ +enum qemu_plugin_event { + QEMU_PLUGIN_EV_VCPU_INIT, + QEMU_PLUGIN_EV_VCPU_EXIT, + QEMU_PLUGIN_EV_VCPU_TB_TRANS, + QEMU_PLUGIN_EV_VCPU_IDLE, + QEMU_PLUGIN_EV_VCPU_RESUME, + QEMU_PLUGIN_EV_VCPU_SYSCALL, + QEMU_PLUGIN_EV_VCPU_SYSCALL_RET, + QEMU_PLUGIN_EV_FLUSH, + QEMU_PLUGIN_EV_ATEXIT, + QEMU_PLUGIN_EV_MAX, /* total number of plugin events we support */ +}; + +#endif /* QEMU_PLUGIN_EVENT_H */ diff --git a/include/qemu/plugin-memory.h b/include/qemu/plugin-memory.h index 8ad13c110c..71c1123308 100644 --- a/include/qemu/plugin-memory.h +++ b/include/qemu/plugin-memory.h @@ -9,18 +9,14 @@ #ifndef PLUGIN_MEMORY_H #define PLUGIN_MEMORY_H +#include "exec/cpu-defs.h" +#include "exec/hwaddr.h" + struct qemu_plugin_hwaddr { bool is_io; bool is_store; - union { - struct { - MemoryRegionSection *section; - hwaddr offset; - } io; - struct { - void *hostaddr; - } ram; - } v; + hwaddr phys_addr; + MemoryRegion *mr; }; /** @@ -34,7 +30,7 @@ struct qemu_plugin_hwaddr { * It would only fail if not called from an instrumented memory access * which would be an abuse of the API. */ -bool tlb_plugin_lookup(CPUState *cpu, target_ulong addr, int mmu_idx, +bool tlb_plugin_lookup(CPUState *cpu, vaddr addr, int mmu_idx, bool is_store, struct qemu_plugin_hwaddr *data); #endif /* PLUGIN_MEMORY_H */ diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index 145f8a221a..12a96cea2a 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -12,23 +12,9 @@ #include "qemu/error-report.h" #include "qemu/queue.h" #include "qemu/option.h" +#include "qemu/plugin-event.h" #include "exec/memopidx.h" - -/* - * Events that plugins can subscribe to. - */ -enum qemu_plugin_event { - QEMU_PLUGIN_EV_VCPU_INIT, - QEMU_PLUGIN_EV_VCPU_EXIT, - QEMU_PLUGIN_EV_VCPU_TB_TRANS, - QEMU_PLUGIN_EV_VCPU_IDLE, - QEMU_PLUGIN_EV_VCPU_RESUME, - QEMU_PLUGIN_EV_VCPU_SYSCALL, - QEMU_PLUGIN_EV_VCPU_SYSCALL_RET, - QEMU_PLUGIN_EV_FLUSH, - QEMU_PLUGIN_EV_ATEXIT, - QEMU_PLUGIN_EV_MAX, /* total number of plugin events we support */ -}; +#include "hw/core/cpu.h" /* * Option parsing/processing. @@ -64,7 +50,7 @@ static inline void qemu_plugin_add_opts(void) qemu_add_opts(&qemu_plugin_opts); } -void qemu_plugin_opt_parse(const char *optarg, QemuPluginList *head); +void qemu_plugin_opt_parse(const char *optstr, QemuPluginList *head); int qemu_plugin_load_list(QemuPluginList *head, Error **errp); union qemu_plugin_cb_sig { @@ -87,6 +73,7 @@ enum plugin_dyn_cb_type { enum plugin_dyn_cb_subtype { PLUGIN_CB_REGULAR, + PLUGIN_CB_REGULAR_R, PLUGIN_CB_INLINE, PLUGIN_N_CB_SUBTYPES, }; @@ -105,6 +92,7 @@ struct qemu_plugin_dyn_cb { /* fields specific to each dyn_cb type go here */ union { struct { + qemu_plugin_u64 entry; enum qemu_plugin_op op; uint64_t imm; } inline_insn; @@ -118,10 +106,19 @@ struct qemu_plugin_insn { void *haddr; GArray *cbs[PLUGIN_N_CB_TYPES][PLUGIN_N_CB_SUBTYPES]; bool calls_helpers; + + /* if set, the instruction calls helpers that might access guest memory */ bool mem_helper; + bool mem_only; }; +/* A scoreboard is an array of values, indexed by vcpu_index */ +struct qemu_plugin_scoreboard { + GArray *data; + QLIST_ENTRY(qemu_plugin_scoreboard) entry; +}; + /* * qemu_plugin_insn allocate and cleanup functions. We don't expect to * cleanup many of these structures. They are reused for each fresh @@ -158,6 +155,10 @@ struct qemu_plugin_tb { void *haddr1; void *haddr2; bool mem_only; + + /* if set, the TB calls helpers that might access guest memory */ + bool mem_helper; + GArray *cbs[PLUGIN_N_CB_SUBTYPES]; }; @@ -192,6 +193,19 @@ struct qemu_plugin_insn *qemu_plugin_tb_insn_get(struct qemu_plugin_tb *tb, return insn; } +/** + * struct CPUPluginState - per-CPU state for plugins + * @event_mask: plugin event bitmap. Modified only via async work. + */ +struct CPUPluginState { + DECLARE_BITMAP(event_mask, QEMU_PLUGIN_EV_MAX); +}; + +/** + * qemu_plugin_create_vcpu_state: allocate plugin state + */ +CPUPluginState *qemu_plugin_create_vcpu_state(void); + void qemu_plugin_vcpu_init_hook(CPUState *cpu); void qemu_plugin_vcpu_exit_hook(CPUState *cpu); void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb); @@ -212,7 +226,10 @@ void qemu_plugin_atexit_cb(void); void qemu_plugin_add_dyn_cb_arr(GArray *arr); -void qemu_plugin_disable_mem_helpers(CPUState *cpu); +static inline void qemu_plugin_disable_mem_helpers(CPUState *cpu) +{ + cpu->plugin_mem_cbs = NULL; +} /** * qemu_plugin_user_exit(): clean-up callbacks before calling exit callbacks @@ -224,12 +241,29 @@ void qemu_plugin_disable_mem_helpers(CPUState *cpu); */ void qemu_plugin_user_exit(void); +/** + * qemu_plugin_user_prefork_lock(): take plugin lock before forking + * + * This is a user-mode only helper to take the internal plugin lock + * before a fork event. This is ensure a consistent lock state + */ +void qemu_plugin_user_prefork_lock(void); + +/** + * qemu_plugin_user_postfork(): reset the plugin lock + * @is_child: is this thread the child + * + * This user-mode only helper resets the lock state after a fork so we + * can continue using the plugin interface. + */ +void qemu_plugin_user_postfork(bool is_child); + #else /* !CONFIG_PLUGIN */ static inline void qemu_plugin_add_opts(void) { } -static inline void qemu_plugin_opt_parse(const char *optarg, +static inline void qemu_plugin_opt_parse(const char *optstr, QemuPluginList *head) { error_report("plugin interface not enabled in this build"); @@ -287,6 +321,13 @@ static inline void qemu_plugin_disable_mem_helpers(CPUState *cpu) static inline void qemu_plugin_user_exit(void) { } + +static inline void qemu_plugin_user_prefork_lock(void) +{ } + +static inline void qemu_plugin_user_postfork(bool is_child) +{ } + #endif /* !CONFIG_PLUGIN */ #endif /* QEMU_PLUGIN_H */ diff --git a/include/qemu/processor.h b/include/qemu/processor.h index 8e16c9277d..9f0dcdf28f 100644 --- a/include/qemu/processor.h +++ b/include/qemu/processor.h @@ -7,8 +7,6 @@ #ifndef QEMU_PROCESSOR_H #define QEMU_PROCESSOR_H -#include "qemu/atomic.h" - #if defined(__i386__) || defined(__x86_64__) # define cpu_relax() asm volatile("rep; nop" ::: "memory") diff --git a/include/qemu/progress_meter.h b/include/qemu/progress_meter.h index dadf822bbf..0f2c0a32d2 100644 --- a/include/qemu/progress_meter.h +++ b/include/qemu/progress_meter.h @@ -27,7 +27,7 @@ #ifndef QEMU_PROGRESS_METER_H #define QEMU_PROGRESS_METER_H -#include "qemu/lockable.h" +#include "qemu/thread.h" typedef struct ProgressMeter { /** diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index d0e9d03adf..4fc6c3739b 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -11,6 +11,7 @@ #ifndef QEMU_QEMU_PLUGIN_H #define QEMU_QEMU_PLUGIN_H +#include #include #include #include @@ -22,15 +23,18 @@ * https://gcc.gnu.org/wiki/Visibility */ #if defined _WIN32 || defined __CYGWIN__ - #ifdef BUILDING_DLL - #define QEMU_PLUGIN_EXPORT __declspec(dllexport) - #else + #ifdef CONFIG_PLUGIN #define QEMU_PLUGIN_EXPORT __declspec(dllimport) + #define QEMU_PLUGIN_API __declspec(dllexport) + #else + #define QEMU_PLUGIN_EXPORT __declspec(dllexport) + #define QEMU_PLUGIN_API __declspec(dllimport) #endif #define QEMU_PLUGIN_LOCAL #else #define QEMU_PLUGIN_EXPORT __attribute__((visibility("default"))) #define QEMU_PLUGIN_LOCAL __attribute__((visibility("hidden"))) + #define QEMU_PLUGIN_API #endif /** @@ -47,11 +51,17 @@ typedef uint64_t qemu_plugin_id_t; * * The plugins export the API they were built against by exposing the * symbol qemu_plugin_version which can be checked. + * + * version 2: + * - removed qemu_plugin_n_vcpus and qemu_plugin_n_max_vcpus + * - Remove qemu_plugin_register_vcpu_{tb, insn, mem}_exec_inline. + * Those functions are replaced by *_per_vcpu variants, which guarantee + * thread-safety for operations. */ extern QEMU_PLUGIN_EXPORT int qemu_plugin_version; -#define QEMU_PLUGIN_VERSION 1 +#define QEMU_PLUGIN_VERSION 2 /** * struct qemu_info_t - system information for plugins @@ -147,6 +157,7 @@ typedef void (*qemu_plugin_vcpu_udata_cb_t)(unsigned int vcpu_index, * * Note: Calling this function from qemu_plugin_install() is a bug. */ +QEMU_PLUGIN_API void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb); /** @@ -160,6 +171,7 @@ void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb); * Plugins are reset asynchronously, and therefore the given plugin receives * callbacks until @cb is called. */ +QEMU_PLUGIN_API void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb); /** @@ -171,6 +183,7 @@ void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb); * * See also: qemu_plugin_register_vcpu_exit_cb() */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_simple_cb_t cb); @@ -183,6 +196,7 @@ void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id, * * See also: qemu_plugin_register_vcpu_init_cb() */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_simple_cb_t cb); @@ -193,6 +207,7 @@ void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id, * * The @cb function is called every time a vCPU idles. */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_simple_cb_t cb); @@ -203,6 +218,7 @@ void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id, * * The @cb function is called every time a vCPU resumes execution. */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_simple_cb_t cb); @@ -210,6 +226,19 @@ void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id, struct qemu_plugin_tb; /** struct qemu_plugin_insn - Opaque handle for a translated instruction */ struct qemu_plugin_insn; +/** struct qemu_plugin_scoreboard - Opaque handle for a scoreboard */ +struct qemu_plugin_scoreboard; + +/** + * typedef qemu_plugin_u64 - uint64_t member of an entry in a scoreboard + * + * This field allows to access a specific uint64_t member in one given entry, + * located at a specified offset. Inline operations expect this as entry. + */ +typedef struct { + struct qemu_plugin_scoreboard *score; + size_t offset; +} qemu_plugin_u64; /** * enum qemu_plugin_cb_flags - type of callback @@ -218,8 +247,8 @@ struct qemu_plugin_insn; * @QEMU_PLUGIN_CB_R_REGS: callback reads the CPU's regs * @QEMU_PLUGIN_CB_RW_REGS: callback reads and writes the CPU's regs * - * Note: currently unused, plugins cannot read or change system - * register state. + * Note: currently QEMU_PLUGIN_CB_RW_REGS is unused, plugins cannot change + * system register state. */ enum qemu_plugin_cb_flags { QEMU_PLUGIN_CB_NO_REGS, @@ -253,6 +282,7 @@ typedef void (*qemu_plugin_vcpu_tb_trans_cb_t)(qemu_plugin_id_t id, * callbacks to be triggered when the block or individual instruction * executes. */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_tb_trans_cb_t cb); @@ -265,6 +295,7 @@ void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, * * The @cb function is called every time a translated unit executes. */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, qemu_plugin_vcpu_udata_cb_t cb, enum qemu_plugin_cb_flags flags, @@ -283,22 +314,20 @@ enum qemu_plugin_op { }; /** - * qemu_plugin_register_vcpu_tb_exec_inline() - execution inline op + * qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu() - execution inline op * @tb: the opaque qemu_plugin_tb handle for the translation * @op: the type of qemu_plugin_op (e.g. ADD_U64) - * @ptr: the target memory location for the op + * @entry: entry to run op * @imm: the op data (e.g. 1) * - * Insert an inline op to every time a translated unit executes. - * Useful if you just want to increment a single counter somewhere in - * memory. - * - * Note: ops are not atomic so in multi-threaded/multi-smp situations - * you will get inexact results. + * Insert an inline op on a given scoreboard entry. */ -void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, - enum qemu_plugin_op op, - void *ptr, uint64_t imm); +QEMU_PLUGIN_API +void qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + struct qemu_plugin_tb *tb, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm); /** * qemu_plugin_register_vcpu_insn_exec_cb() - register insn execution cb @@ -309,24 +338,27 @@ void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, * * The @cb function is called every time an instruction is executed */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, qemu_plugin_vcpu_udata_cb_t cb, enum qemu_plugin_cb_flags flags, void *userdata); /** - * qemu_plugin_register_vcpu_insn_exec_inline() - insn execution inline op + * qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu() - insn exec inline op * @insn: the opaque qemu_plugin_insn handle for an instruction * @op: the type of qemu_plugin_op (e.g. ADD_U64) - * @ptr: the target memory location for the op + * @entry: entry to run op * @imm: the op data (e.g. 1) * - * Insert an inline op to every time an instruction executes. Useful - * if you just want to increment a single counter somewhere in memory. + * Insert an inline op to every time an instruction executes. */ -void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn, - enum qemu_plugin_op op, - void *ptr, uint64_t imm); +QEMU_PLUGIN_API +void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + struct qemu_plugin_insn *insn, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm); /** * qemu_plugin_tb_n_insns() - query helper for number of insns in TB @@ -334,6 +366,7 @@ void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn, * * Returns: number of instructions in this block */ +QEMU_PLUGIN_API size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb); /** @@ -342,6 +375,7 @@ size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb); * * Returns: virtual address of block start */ +QEMU_PLUGIN_API uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb); /** @@ -355,6 +389,7 @@ uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb); * * Returns: opaque handle to instruction */ +QEMU_PLUGIN_API struct qemu_plugin_insn * qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx); @@ -368,6 +403,7 @@ qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx); * Returns: pointer to a stream of bytes containing the value of this * instructions opcode. */ +QEMU_PLUGIN_API const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn); /** @@ -376,6 +412,7 @@ const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn); * * Returns: size of instruction in bytes */ +QEMU_PLUGIN_API size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn); /** @@ -384,6 +421,7 @@ size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn); * * Returns: virtual address of instruction */ +QEMU_PLUGIN_API uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn); /** @@ -392,6 +430,7 @@ uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn); * * Returns: hardware (physical) target address of instruction */ +QEMU_PLUGIN_API void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn); /** @@ -410,6 +449,7 @@ struct qemu_plugin_hwaddr; * * Returns: size of access in ^2 (0=byte, 1=16bit, 2=32bit etc...) */ +QEMU_PLUGIN_API unsigned int qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info); /** * qemu_plugin_mem_is_sign_extended() - was the access sign extended @@ -417,6 +457,7 @@ unsigned int qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info); * * Returns: true if it was, otherwise false */ +QEMU_PLUGIN_API bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info); /** * qemu_plugin_mem_is_big_endian() - was the access big endian @@ -424,6 +465,7 @@ bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info); * * Returns: true if it was, otherwise false */ +QEMU_PLUGIN_API bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info); /** * qemu_plugin_mem_is_store() - was the access a store @@ -431,6 +473,7 @@ bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info); * * Returns: true if it was, otherwise false */ +QEMU_PLUGIN_API bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info); /** @@ -446,6 +489,7 @@ bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info); * information about the handle should be recovered before the * callback returns. */ +QEMU_PLUGIN_API struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, uint64_t vaddr); @@ -462,6 +506,7 @@ struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, * Returns true if the handle's memory operation is to memory-mapped IO, or * false if it is to RAM */ +QEMU_PLUGIN_API bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr); /** @@ -473,31 +518,73 @@ bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr); * Note that the returned physical address may not be unique if you are dealing * with multiple address spaces. */ +QEMU_PLUGIN_API uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr); /* * Returns a string representing the device. The string is valid for * the lifetime of the plugin. */ +QEMU_PLUGIN_API const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h); -typedef void -(*qemu_plugin_vcpu_mem_cb_t)(unsigned int vcpu_index, - qemu_plugin_meminfo_t info, uint64_t vaddr, - void *userdata); +/** + * typedef qemu_plugin_vcpu_mem_cb_t - memory callback function type + * @vcpu_index: the executing vCPU + * @info: an opaque handle for further queries about the memory + * @vaddr: the virtual address of the transaction + * @userdata: any user data attached to the callback + */ +typedef void (*qemu_plugin_vcpu_mem_cb_t) (unsigned int vcpu_index, + qemu_plugin_meminfo_t info, + uint64_t vaddr, + void *userdata); +/** + * qemu_plugin_register_vcpu_mem_cb() - register memory access callback + * @insn: handle for instruction to instrument + * @cb: callback of type qemu_plugin_vcpu_mem_cb_t + * @flags: (currently unused) callback flags + * @rw: monitor reads, writes or both + * @userdata: opaque pointer for userdata + * + * This registers a full callback for every memory access generated by + * an instruction. If the instruction doesn't access memory no + * callback will be made. + * + * The callback reports the vCPU the access took place on, the virtual + * address of the access and a handle for further queries. The user + * can attach some userdata to the callback for additional purposes. + * + * Other execution threads will continue to execute during the + * callback so the plugin is responsible for ensuring it doesn't get + * confused by making appropriate use of locking if required. + */ +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, qemu_plugin_vcpu_mem_cb_t cb, enum qemu_plugin_cb_flags flags, enum qemu_plugin_mem_rw rw, void *userdata); -void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn, - enum qemu_plugin_mem_rw rw, - enum qemu_plugin_op op, void *ptr, - uint64_t imm); - - +/** + * qemu_plugin_register_vcpu_mem_inline_per_vcpu() - inline op for mem access + * @insn: handle for instruction to instrument + * @rw: apply to reads, writes or both + * @op: the op, of type qemu_plugin_op + * @entry: entry to run op + * @imm: immediate data for @op + * + * This registers a inline op every memory access generated by the + * instruction. + */ +QEMU_PLUGIN_API +void qemu_plugin_register_vcpu_mem_inline_per_vcpu( + struct qemu_plugin_insn *insn, + enum qemu_plugin_mem_rw rw, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm); typedef void (*qemu_plugin_vcpu_syscall_cb_t)(qemu_plugin_id_t id, unsigned int vcpu_index, @@ -505,6 +592,7 @@ typedef void uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6, uint64_t a7, uint64_t a8); +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_syscall_cb_t cb); @@ -512,6 +600,7 @@ typedef void (*qemu_plugin_vcpu_syscall_ret_cb_t)(qemu_plugin_id_t id, unsigned int vcpu_idx, int64_t num, int64_t ret); +QEMU_PLUGIN_API void qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_syscall_ret_cb_t cb); @@ -524,6 +613,7 @@ qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id, * Returns an allocated string containing the disassembly */ +QEMU_PLUGIN_API char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn); /** @@ -533,6 +623,7 @@ char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn); * Return a static string referring to the symbol. This is dependent * on the binary QEMU is running having provided a symbol table. */ +QEMU_PLUGIN_API const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn); /** @@ -544,9 +635,11 @@ const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn); * * See also: qemu_plugin_register_vcpu_init_cb() */ +QEMU_PLUGIN_API void qemu_plugin_vcpu_for_each(qemu_plugin_id_t id, qemu_plugin_vcpu_simple_cb_t cb); +QEMU_PLUGIN_API void qemu_plugin_register_flush_cb(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb); @@ -563,19 +656,18 @@ void qemu_plugin_register_flush_cb(qemu_plugin_id_t id, * In user-mode it is possible a few un-instrumented instructions from * child threads may run before the host kernel reaps the threads. */ +QEMU_PLUGIN_API void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id, qemu_plugin_udata_cb_t cb, void *userdata); -/* returns -1 in user-mode */ -int qemu_plugin_n_vcpus(void); - -/* returns -1 in user-mode */ -int qemu_plugin_n_max_vcpus(void); +/* returns how many vcpus were started at this point */ +int qemu_plugin_num_vcpus(void); /** * qemu_plugin_outs() - output string via QEMU's logging system * @string: a string */ +QEMU_PLUGIN_API void qemu_plugin_outs(const char *string); /** @@ -589,6 +681,7 @@ void qemu_plugin_outs(const char *string); * returns true if the combination @name=@val parses correctly to a boolean * argument, and false otherwise */ +QEMU_PLUGIN_API bool qemu_plugin_bool_parse(const char *name, const char *val, bool *ret); /** @@ -599,6 +692,7 @@ bool qemu_plugin_bool_parse(const char *name, const char *val, bool *ret); * return NULL. The user should g_free() the string once no longer * needed. */ +QEMU_PLUGIN_API const char *qemu_plugin_path_to_binary(void); /** @@ -607,6 +701,7 @@ const char *qemu_plugin_path_to_binary(void); * Returns the nominal start address of the main text segment in * user-mode. Currently returns 0 for system emulation. */ +QEMU_PLUGIN_API uint64_t qemu_plugin_start_code(void); /** @@ -615,6 +710,7 @@ uint64_t qemu_plugin_start_code(void); * Returns the nominal end address of the main text segment in * user-mode. Currently returns 0 for system emulation. */ +QEMU_PLUGIN_API uint64_t qemu_plugin_end_code(void); /** @@ -623,6 +719,122 @@ uint64_t qemu_plugin_end_code(void); * Returns the nominal entry address of the main text segment in * user-mode. Currently returns 0 for system emulation. */ +QEMU_PLUGIN_API uint64_t qemu_plugin_entry_code(void); +/** struct qemu_plugin_register - Opaque handle for register access */ +struct qemu_plugin_register; + +/** + * typedef qemu_plugin_reg_descriptor - register descriptions + * + * @handle: opaque handle for retrieving value with qemu_plugin_read_register + * @name: register name + * @feature: optional feature descriptor, can be NULL + */ +typedef struct { + struct qemu_plugin_register *handle; + const char *name; + const char *feature; +} qemu_plugin_reg_descriptor; + +/** + * qemu_plugin_get_registers() - return register list for current vCPU + * + * Returns a potentially empty GArray of qemu_plugin_reg_descriptor. + * Caller frees the array (but not the const strings). + * + * Should be used from a qemu_plugin_register_vcpu_init_cb() callback + * after the vCPU is initialised, i.e. in the vCPU context. + */ +QEMU_PLUGIN_API +GArray *qemu_plugin_get_registers(void); + +/** + * qemu_plugin_read_register() - read register for current vCPU + * + * @handle: a @qemu_plugin_reg_handle handle + * @buf: A GByteArray for the data owned by the plugin + * + * This function is only available in a context that register read access is + * explicitly requested via the QEMU_PLUGIN_CB_R_REGS flag. + * + * Returns the size of the read register. The content of @buf is in target byte + * order. On failure returns -1. + */ +QEMU_PLUGIN_API +int qemu_plugin_read_register(struct qemu_plugin_register *handle, + GByteArray *buf); + +/** + * qemu_plugin_scoreboard_new() - alloc a new scoreboard + * + * @element_size: size (in bytes) for one entry + * + * Returns a pointer to a new scoreboard. It must be freed using + * qemu_plugin_scoreboard_free. + */ +QEMU_PLUGIN_API +struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size); + +/** + * qemu_plugin_scoreboard_free() - free a scoreboard + * @score: scoreboard to free + */ +QEMU_PLUGIN_API +void qemu_plugin_scoreboard_free(struct qemu_plugin_scoreboard *score); + +/** + * qemu_plugin_scoreboard_find() - get pointer to an entry of a scoreboard + * @score: scoreboard to query + * @vcpu_index: entry index + * + * Returns address of entry of a scoreboard matching a given vcpu_index. This + * address can be modified later if scoreboard is resized. + */ +QEMU_PLUGIN_API +void *qemu_plugin_scoreboard_find(struct qemu_plugin_scoreboard *score, + unsigned int vcpu_index); + +/* Macros to define a qemu_plugin_u64 */ +#define qemu_plugin_scoreboard_u64(score) \ + (qemu_plugin_u64) {score, 0} +#define qemu_plugin_scoreboard_u64_in_struct(score, type, member) \ + (qemu_plugin_u64) {score, offsetof(type, member)} + +/** + * qemu_plugin_u64_add() - add a value to a qemu_plugin_u64 for a given vcpu + * @entry: entry to query + * @vcpu_index: entry index + * @added: value to add + */ +QEMU_PLUGIN_API +void qemu_plugin_u64_add(qemu_plugin_u64 entry, unsigned int vcpu_index, + uint64_t added); + +/** + * qemu_plugin_u64_get() - get value of a qemu_plugin_u64 for a given vcpu + * @entry: entry to query + * @vcpu_index: entry index + */ +QEMU_PLUGIN_API +uint64_t qemu_plugin_u64_get(qemu_plugin_u64 entry, unsigned int vcpu_index); + +/** + * qemu_plugin_u64_set() - set value of a qemu_plugin_u64 for a given vcpu + * @entry: entry to query + * @vcpu_index: entry index + * @val: new value + */ +QEMU_PLUGIN_API +void qemu_plugin_u64_set(qemu_plugin_u64 entry, unsigned int vcpu_index, + uint64_t val); + +/** + * qemu_plugin_u64_sum() - return sum of all vcpu entries in a scoreboard + * @entry: entry to sum + */ +QEMU_PLUGIN_API +uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry); + #endif /* QEMU_QEMU_PLUGIN_H */ diff --git a/include/qemu/qtree.h b/include/qemu/qtree.h new file mode 100644 index 0000000000..dc2b14d258 --- /dev/null +++ b/include/qemu/qtree.h @@ -0,0 +1,200 @@ +/* + * GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 . + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +/* + * QTree is a partial import of Glib's GTree. The parts excluded correspond + * to API calls either deprecated (e.g. g_tree_traverse) or recently added + * (e.g. g_tree_search_node, added in 2.68); neither have callers in QEMU. + * + * The reason for this import is to allow us to control the memory allocator + * used by the tree implementation. Until Glib 2.75.3, GTree uses Glib's + * slice allocator, which causes problems when forking in user-mode; + * see https://gitlab.com/qemu-project/qemu/-/issues/285 and glib's + * "45b5a6c1e gslice: Remove slice allocator and use malloc() instead". + * + * TODO: remove QTree when QEMU's minimum Glib version is >= 2.75.3. + */ + +#ifndef QEMU_QTREE_H +#define QEMU_QTREE_H + + +#ifdef HAVE_GLIB_WITH_SLICE_ALLOCATOR + +typedef struct _QTree QTree; + +typedef struct _QTreeNode QTreeNode; + +typedef gboolean (*QTraverseNodeFunc)(QTreeNode *node, + gpointer user_data); + +/* + * Balanced binary trees + */ +QTree *q_tree_new(GCompareFunc key_compare_func); +QTree *q_tree_new_with_data(GCompareDataFunc key_compare_func, + gpointer key_compare_data); +QTree *q_tree_new_full(GCompareDataFunc key_compare_func, + gpointer key_compare_data, + GDestroyNotify key_destroy_func, + GDestroyNotify value_destroy_func); +QTree *q_tree_ref(QTree *tree); +void q_tree_unref(QTree *tree); +void q_tree_destroy(QTree *tree); +void q_tree_insert(QTree *tree, + gpointer key, + gpointer value); +void q_tree_replace(QTree *tree, + gpointer key, + gpointer value); +gboolean q_tree_remove(QTree *tree, + gconstpointer key); +gboolean q_tree_steal(QTree *tree, + gconstpointer key); +gpointer q_tree_lookup(QTree *tree, + gconstpointer key); +gboolean q_tree_lookup_extended(QTree *tree, + gconstpointer lookup_key, + gpointer *orig_key, + gpointer *value); +void q_tree_foreach(QTree *tree, + GTraverseFunc func, + gpointer user_data); +gpointer q_tree_search(QTree *tree, + GCompareFunc search_func, + gconstpointer user_data); +gint q_tree_height(QTree *tree); +gint q_tree_nnodes(QTree *tree); + +#else /* !HAVE_GLIB_WITH_SLICE_ALLOCATOR */ + +typedef GTree QTree; +typedef GTreeNode QTreeNode; +typedef GTraverseNodeFunc QTraverseNodeFunc; + +static inline QTree *q_tree_new(GCompareFunc key_compare_func) +{ + return g_tree_new(key_compare_func); +} + +static inline QTree *q_tree_new_with_data(GCompareDataFunc key_compare_func, + gpointer key_compare_data) +{ + return g_tree_new_with_data(key_compare_func, key_compare_data); +} + +static inline QTree *q_tree_new_full(GCompareDataFunc key_compare_func, + gpointer key_compare_data, + GDestroyNotify key_destroy_func, + GDestroyNotify value_destroy_func) +{ + return g_tree_new_full(key_compare_func, key_compare_data, + key_destroy_func, value_destroy_func); +} + +static inline QTree *q_tree_ref(QTree *tree) +{ + return g_tree_ref(tree); +} + +static inline void q_tree_unref(QTree *tree) +{ + g_tree_unref(tree); +} + +static inline void q_tree_destroy(QTree *tree) +{ + g_tree_destroy(tree); +} + +static inline void q_tree_insert(QTree *tree, + gpointer key, + gpointer value) +{ + g_tree_insert(tree, key, value); +} + +static inline void q_tree_replace(QTree *tree, + gpointer key, + gpointer value) +{ + g_tree_replace(tree, key, value); +} + +static inline gboolean q_tree_remove(QTree *tree, + gconstpointer key) +{ + return g_tree_remove(tree, key); +} + +static inline gboolean q_tree_steal(QTree *tree, + gconstpointer key) +{ + return g_tree_steal(tree, key); +} + +static inline gpointer q_tree_lookup(QTree *tree, + gconstpointer key) +{ + return g_tree_lookup(tree, key); +} + +static inline gboolean q_tree_lookup_extended(QTree *tree, + gconstpointer lookup_key, + gpointer *orig_key, + gpointer *value) +{ + return g_tree_lookup_extended(tree, lookup_key, orig_key, value); +} + +static inline void q_tree_foreach(QTree *tree, + GTraverseFunc func, + gpointer user_data) +{ + return g_tree_foreach(tree, func, user_data); +} + +static inline gpointer q_tree_search(QTree *tree, + GCompareFunc search_func, + gconstpointer user_data) +{ + return g_tree_search(tree, search_func, user_data); +} + +static inline gint q_tree_height(QTree *tree) +{ + return g_tree_height(tree); +} + +static inline gint q_tree_nnodes(QTree *tree) +{ + return g_tree_nnodes(tree); +} + +#endif /* HAVE_GLIB_WITH_SLICE_ALLOCATOR */ + +#endif /* QEMU_QTREE_H */ diff --git a/include/qemu/range.h b/include/qemu/range.h index 7e2b1cc447..205e1da76d 100644 --- a/include/qemu/range.h +++ b/include/qemu/range.h @@ -217,6 +217,20 @@ static inline int ranges_overlap(uint64_t first1, uint64_t len1, return !(last2 < first1 || last1 < first2); } +/* + * Return -1 if @a < @b, 1 @a > @b, and 0 if they touch or overlap. + * Both @a and @b must not be empty. + */ +int range_compare(Range *a, Range *b); + GList *range_list_insert(GList *list, Range *data); +/* + * Inverse an array of sorted ranges over the [low, high] span, ie. + * original ranges becomes holes in the newly allocated inv_ranges + */ +void range_inverse_array(GList *in_ranges, + GList **out_ranges, + uint64_t low, uint64_t high); + #endif diff --git a/include/qemu/rcu.h b/include/qemu/rcu.h index b063c6fde8..fea058aa9f 100644 --- a/include/qemu/rcu.h +++ b/include/qemu/rcu.h @@ -31,10 +31,6 @@ #include "qemu/sys_membarrier.h" #include "qemu/coroutine-tls.h" -#ifdef __cplusplus -extern "C" { -#endif - /* * Important ! * @@ -91,7 +87,10 @@ static inline void rcu_read_lock(void) ctr = qatomic_read(&rcu_gp_ctr); qatomic_set(&p_rcu_reader->ctr, ctr); - /* Write p_rcu_reader->ctr before reading RCU-protected pointers. */ + /* + * Read rcu_gp_ptr and write p_rcu_reader->ctr before reading + * RCU-protected pointers. + */ smp_mb_placeholder(); } @@ -119,19 +118,19 @@ static inline void rcu_read_unlock(void) } } -extern void synchronize_rcu(void); +void synchronize_rcu(void); /* * Reader thread registration. */ -extern void rcu_register_thread(void); -extern void rcu_unregister_thread(void); +void rcu_register_thread(void); +void rcu_unregister_thread(void); /* * Support for fork(). fork() support is enabled at startup. */ -extern void rcu_enable_atfork(void); -extern void rcu_disable_atfork(void); +void rcu_enable_atfork(void); +void rcu_disable_atfork(void); struct rcu_head; typedef void RCUCBFunc(struct rcu_head *head); @@ -141,8 +140,8 @@ struct rcu_head { RCUCBFunc *func; }; -extern void call_rcu1(struct rcu_head *head, RCUCBFunc *func); -extern void drain_call_rcu(void); +void call_rcu1(struct rcu_head *head, RCUCBFunc *func); +void drain_call_rcu(void); /* The operands of the minus operator must have the same type, * which must be the one that we specify in the cast. @@ -196,8 +195,4 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(RCUReadAuto, rcu_read_auto_unlock) void rcu_add_force_rcu_notifier(Notifier *n); void rcu_remove_force_rcu_notifier(Notifier *n); -#ifdef __cplusplus -} -#endif - #endif /* QEMU_RCU_H */ diff --git a/include/qemu/rcu_queue.h b/include/qemu/rcu_queue.h index 0e53ddd530..4e6298d473 100644 --- a/include/qemu/rcu_queue.h +++ b/include/qemu/rcu_queue.h @@ -28,11 +28,6 @@ #include "qemu/queue.h" #include "qemu/atomic.h" -#ifdef __cplusplus -extern "C" { -#endif - - /* * List access methods. */ @@ -311,7 +306,4 @@ extern "C" { (var) && ((next) = qatomic_rcu_read(&(var)->field.sle_next), 1); \ (var) = (next)) -#ifdef __cplusplus -} -#endif #endif /* QEMU_RCU_QUEUE_H */ diff --git a/include/qemu/readline.h b/include/qemu/readline.h index 622aa4564f..b05e4782da 100644 --- a/include/qemu/readline.h +++ b/include/qemu/readline.h @@ -44,6 +44,8 @@ typedef struct ReadLineState { } ReadLineState; void readline_add_completion(ReadLineState *rs, const char *str); +void readline_add_completion_of(ReadLineState *rs, + const char *pfx, const char *str); void readline_set_completion_index(ReadLineState *rs, int completion_index); const char *readline_get_history(ReadLineState *rs, unsigned int index); diff --git a/include/qemu/reserved-region.h b/include/qemu/reserved-region.h new file mode 100644 index 0000000000..8e6f0a97e2 --- /dev/null +++ b/include/qemu/reserved-region.h @@ -0,0 +1,32 @@ +/* + * QEMU ReservedRegion helpers + * + * Copyright (c) 2023 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef QEMU_RESERVED_REGION_H +#define QEMU_RESERVED_REGION_H + +#include "exec/memory.h" + +/* + * Insert a new region into a sorted list of reserved regions. In case + * there is overlap with existing regions, the new added region has + * higher priority and replaces the overlapped segment. + */ +GList *resv_region_list_insert(GList *list, ReservedRegion *reg); + +#endif diff --git a/include/qemu/selfmap.h b/include/qemu/selfmap.h index 3479a2a618..1690a74f4b 100644 --- a/include/qemu/selfmap.h +++ b/include/qemu/selfmap.h @@ -9,9 +9,10 @@ #ifndef SELFMAP_H #define SELFMAP_H +#include "qemu/interval-tree.h" + typedef struct { - unsigned long start; - unsigned long end; + IntervalTreeNode itree; /* flags */ bool is_read; @@ -19,26 +20,25 @@ typedef struct { bool is_exec; bool is_priv; - unsigned long offset; - gchar *dev; - uint64_t inode; - gchar *path; + dev_t dev; + ino_t inode; + uint64_t offset; + const char *path; } MapInfo; - /** * read_self_maps: * - * Read /proc/self/maps and return a list of MapInfo structures. + * Read /proc/self/maps and return a tree of MapInfo structures. */ -GSList *read_self_maps(void); +IntervalTreeRoot *read_self_maps(void); /** * free_self_maps: - * @info: a GSlist + * @info: an interval tree * - * Free a list of MapInfo structures. + * Free a tree of MapInfo structures. */ -void free_self_maps(GSList *info); +void free_self_maps(IntervalTreeRoot *root); #endif /* SELFMAP_H */ diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index 038faa157f..d935fd80da 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -14,7 +14,36 @@ int inet_aton(const char *cp, struct in_addr *ia); /* misc helpers */ bool fd_is_socket(int fd); int qemu_socket(int domain, int type, int protocol); + +/** + * qemu_socketpair: + * @domain: specifies a communication domain, such as PF_UNIX + * @type: specifies the socket type. + * @protocol: specifies a particular protocol to be used with the socket + * @sv: an array to store the pair of socket created + * + * Creates an unnamed pair of connected sockets in the specified domain, + * of the specified type, and using the optionally specified protocol. + * And automatically set the close-on-exec flags on the returned sockets + * + * Return 0 on success. + */ +int qemu_socketpair(int domain, int type, int protocol, int sv[2]); + int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); +/* + * A variant of send(2) which handles partial send. + * + * Return the number of bytes transferred over the socket. + * Set errno if fewer than `count' bytes are sent. + * + * This function don't work with non-blocking socket's. + * Any of the possibilities with non-blocking socket's is bad: + * - return a short write (then name is wrong) + * - busy wait adding (errno == EAGAIN) to the loop + */ +ssize_t qemu_send_full(int s, const void *buf, size_t count) + G_GNUC_WARN_UNUSED_RESULT; int socket_set_cork(int fd, int v); int socket_set_nodelay(int fd); void qemu_socket_set_block(int fd); @@ -40,6 +69,7 @@ NetworkAddressFamily inet_netfamily(int family); int unix_listen(const char *path, Error **errp); int unix_connect(const char *path, Error **errp); +char *socket_uri(SocketAddress *addr); SocketAddress *socket_parse(const char *str, Error **errp); int socket_connect(SocketAddress *addr, Error **errp); int socket_listen(SocketAddress *addr, int num, Error **errp); @@ -47,6 +77,8 @@ void socket_listen_cleanup(int fd, Error **errp); int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp); /* Old, ipv4 only bits. Don't use for new code. */ +int convert_host_port(struct sockaddr_in *saddr, const char *host, + const char *port, Error **errp); int parse_host_port(struct sockaddr_in *saddr, const char *str, Error **errp); int socket_init(void); @@ -121,5 +153,4 @@ SocketAddress *socket_address_flatten(SocketAddressLegacy *addr); * Return 0 on success. */ int socket_address_parse_named_fd(SocketAddress *addr, Error **errp); - #endif /* QEMU_SOCKETS_H */ diff --git a/include/qemu/stats64.h b/include/qemu/stats64.h index 802402254b..99b5cb724a 100644 --- a/include/qemu/stats64.h +++ b/include/qemu/stats64.h @@ -40,6 +40,11 @@ static inline uint64_t stat64_get(const Stat64 *s) return qatomic_read__nocheck(&s->value); } +static inline void stat64_set(Stat64 *s, uint64_t value) +{ + qatomic_set__nocheck(&s->value, value); +} + static inline void stat64_add(Stat64 *s, uint64_t value) { qatomic_add(&s->value, value); @@ -62,6 +67,7 @@ static inline void stat64_max(Stat64 *s, uint64_t value) } #else uint64_t stat64_get(const Stat64 *s); +void stat64_set(Stat64 *s, uint64_t value); bool stat64_min_slow(Stat64 *s, uint64_t value); bool stat64_max_slow(Stat64 *s, uint64_t value); bool stat64_add32_carry(Stat64 *s, uint32_t low, uint32_t high); diff --git a/include/qemu/sys_membarrier.h b/include/qemu/sys_membarrier.h index b5bfa21d52..e7774891f8 100644 --- a/include/qemu/sys_membarrier.h +++ b/include/qemu/sys_membarrier.h @@ -14,8 +14,8 @@ * side. The slow side forces processor-level ordering on all other cores * through a system call. */ -extern void smp_mb_global_init(void); -extern void smp_mb_global(void); +void smp_mb_global_init(void); +void smp_mb_global(void); #define smp_mb_placeholder() barrier() #else /* Keep it simple, execute a real memory barrier on both sides. */ diff --git a/include/qemu/thread-context.h b/include/qemu/thread-context.h new file mode 100644 index 0000000000..2ebd6b7fe1 --- /dev/null +++ b/include/qemu/thread-context.h @@ -0,0 +1,57 @@ +/* + * QEMU Thread Context + * + * Copyright Red Hat Inc., 2022 + * + * Authors: + * David Hildenbrand + * + * 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 SYSEMU_THREAD_CONTEXT_H +#define SYSEMU_THREAD_CONTEXT_H + +#include "qapi/qapi-types-machine.h" +#include "qemu/thread.h" +#include "qom/object.h" + +#define TYPE_THREAD_CONTEXT "thread-context" +OBJECT_DECLARE_TYPE(ThreadContext, ThreadContextClass, + THREAD_CONTEXT) + +struct ThreadContextClass { + ObjectClass parent_class; +}; + +struct ThreadContext { + /* private */ + Object parent; + + /* private */ + unsigned int thread_id; + QemuThread thread; + + /* Semaphore to wait for context thread action. */ + QemuSemaphore sem; + /* Semaphore to wait for action in context thread. */ + QemuSemaphore sem_thread; + /* Mutex to synchronize requests. */ + QemuMutex mutex; + + /* Commands for the thread to execute. */ + int thread_cmd; + void *thread_cmd_data; + + /* CPU affinity bitmap used for initialization. */ + unsigned long *init_cpu_bitmap; + int init_cpu_nbits; +}; + +void thread_context_create_thread(ThreadContext *tc, QemuThread *thread, + const char *name, + void *(*start_routine)(void *), void *arg, + int mode); + +#endif /* SYSEMU_THREAD_CONTEXT_H */ diff --git a/include/qemu/thread.h b/include/qemu/thread.h index af19f2b3fc..fb74e21c08 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -3,6 +3,7 @@ #include "qemu/processor.h" #include "qemu/atomic.h" +#include "qemu/clang-tsa.h" typedef struct QemuCond QemuCond; typedef struct QemuSemaphore QemuSemaphore; @@ -24,9 +25,12 @@ typedef struct QemuThread QemuThread; void qemu_mutex_init(QemuMutex *mutex); void qemu_mutex_destroy(QemuMutex *mutex); -int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line); -void qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, const int line); -void qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, const int line); +int TSA_NO_TSA qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, + const int line); +void TSA_NO_TSA qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, + const int line); +void TSA_NO_TSA qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, + const int line); void qemu_rec_mutex_init(QemuRecMutex *mutex); void qemu_rec_mutex_destroy(QemuRecMutex *mutex); @@ -43,7 +47,7 @@ typedef void (*QemuCondWaitFunc)(QemuCond *c, QemuMutex *m, const char *f, typedef bool (*QemuCondTimedWaitFunc)(QemuCond *c, QemuMutex *m, int ms, const char *f, int l); -extern QemuMutexLockFunc qemu_bql_mutex_lock_func; +extern QemuMutexLockFunc bql_mutex_lock_func; extern QemuMutexLockFunc qemu_mutex_lock_func; extern QemuMutexTrylockFunc qemu_mutex_trylock_func; extern QemuRecMutexLockFunc qemu_rec_mutex_lock_func; @@ -153,8 +157,8 @@ void qemu_cond_destroy(QemuCond *cond); */ void qemu_cond_signal(QemuCond *cond); void qemu_cond_broadcast(QemuCond *cond); -void qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, - const char *file, const int line); +void TSA_NO_TSA qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, + const char *file, const int line); bool qemu_cond_timedwait_impl(QemuCond *cond, QemuMutex *mutex, int ms, const char *file, const int line); @@ -185,6 +189,10 @@ void qemu_event_destroy(QemuEvent *ev); void qemu_thread_create(QemuThread *thread, const char *name, void *(*start_routine)(void *), void *arg, int mode); +int qemu_thread_set_affinity(QemuThread *thread, unsigned long *host_cpus, + unsigned long nbits); +int qemu_thread_get_affinity(QemuThread *thread, unsigned long **host_cpus, + unsigned long *nbits); void *qemu_thread_join(QemuThread *thread); void qemu_thread_get_self(QemuThread *thread); bool qemu_thread_is_self(QemuThread *thread); @@ -227,17 +235,16 @@ struct QemuSpin { static inline void qemu_spin_init(QemuSpin *spin) { - __sync_lock_release(&spin->value); + qatomic_set(&spin->value, 0); #ifdef CONFIG_TSAN __tsan_mutex_create(spin, __tsan_mutex_not_static); #endif } -/* const parameter because the only purpose here is the TSAN annotation */ -static inline void qemu_spin_destroy(const QemuSpin *spin) +static inline void qemu_spin_destroy(QemuSpin *spin) { #ifdef CONFIG_TSAN - __tsan_mutex_destroy((void *)spin, __tsan_mutex_not_static); + __tsan_mutex_destroy(spin, __tsan_mutex_not_static); #endif } @@ -246,7 +253,7 @@ static inline void qemu_spin_lock(QemuSpin *spin) #ifdef CONFIG_TSAN __tsan_mutex_pre_lock(spin, 0); #endif - while (unlikely(__sync_lock_test_and_set(&spin->value, true))) { + while (unlikely(qatomic_xchg(&spin->value, 1))) { while (qatomic_read(&spin->value)) { cpu_relax(); } @@ -261,7 +268,7 @@ static inline bool qemu_spin_trylock(QemuSpin *spin) #ifdef CONFIG_TSAN __tsan_mutex_pre_lock(spin, __tsan_mutex_try_lock); #endif - bool busy = __sync_lock_test_and_set(&spin->value, true); + bool busy = qatomic_xchg(&spin->value, true); #ifdef CONFIG_TSAN unsigned flags = __tsan_mutex_try_lock; flags |= busy ? __tsan_mutex_try_lock_failed : 0; @@ -280,7 +287,7 @@ static inline void qemu_spin_unlock(QemuSpin *spin) #ifdef CONFIG_TSAN __tsan_mutex_pre_unlock(spin, 0); #endif - __sync_lock_release(&spin->value); + qatomic_store_release(&spin->value, 0); #ifdef CONFIG_TSAN __tsan_mutex_post_unlock(spin, 0); #endif diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h index 05f6346137..181245d29b 100644 --- a/include/qemu/throttle.h +++ b/include/qemu/throttle.h @@ -99,13 +99,18 @@ typedef struct ThrottleState { int64_t previous_leak; /* timestamp of the last leak done */ } ThrottleState; +typedef enum { + THROTTLE_READ = 0, + THROTTLE_WRITE, + THROTTLE_MAX +} ThrottleDirection; + typedef struct ThrottleTimers { - QEMUTimer *timers[2]; /* timers used to do the throttling */ + QEMUTimer *timers[THROTTLE_MAX]; /* timers used to do the throttling */ QEMUClockType clock_type; /* the clock used */ /* Callbacks */ - QEMUTimerCB *read_timer_cb; - QEMUTimerCB *write_timer_cb; + QEMUTimerCB *timer_cb[THROTTLE_MAX]; void *timer_opaque; } ThrottleTimers; @@ -149,9 +154,10 @@ void throttle_config_init(ThrottleConfig *cfg); /* usage */ bool throttle_schedule_timer(ThrottleState *ts, ThrottleTimers *tt, - bool is_write); + ThrottleDirection direction); -void throttle_account(ThrottleState *ts, bool is_write, uint64_t size); +void throttle_account(ThrottleState *ts, ThrottleDirection direction, + uint64_t size); void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg, Error **errp); void throttle_config_to_limits(ThrottleConfig *cfg, ThrottleLimits *var); diff --git a/include/qemu/timer.h b/include/qemu/timer.h index ee071e07d1..9a366e551f 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -979,6 +979,28 @@ static inline int64_t cpu_get_host_ticks(void) return cur - ofs; } +#elif defined(__riscv) && __riscv_xlen == 32 +static inline int64_t cpu_get_host_ticks(void) +{ + uint32_t lo, hi, tmph; + do { + asm volatile("RDTIMEH %0\n\t" + "RDTIME %1\n\t" + "RDTIMEH %2" + : "=r"(hi), "=r"(lo), "=r"(tmph)); + } while (unlikely(tmph != hi)); + return lo | (uint64_t)hi << 32; +} + +#elif defined(__riscv) && __riscv_xlen > 32 +static inline int64_t cpu_get_host_ticks(void) +{ + int64_t val; + + asm volatile("RDTIME %0" : "=r"(val)); + return val; +} + #else /* The host CPU doesn't have an easily accessible cycle counter. Just return a monotonically increasing value. This will be @@ -989,13 +1011,4 @@ static inline int64_t cpu_get_host_ticks(void) } #endif -#ifdef CONFIG_PROFILER -static inline int64_t profile_getclock(void) -{ - return get_clock(); -} - -extern int64_t dev_time; -#endif - #endif diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 42f4ceb701..50c277cf0b 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -21,6 +21,8 @@ * Incomplete struct types * Please keep this list in case-insensitive alphabetical order. */ +typedef struct AccelCPUState AccelCPUState; +typedef struct AccelState AccelState; typedef struct AdapterInfo AdapterInfo; typedef struct AddressSpace AddressSpace; typedef struct AioContext AioContext; @@ -37,16 +39,20 @@ typedef struct BusState BusState; typedef struct Chardev Chardev; typedef struct Clock Clock; typedef struct CompatProperty CompatProperty; -typedef struct CoMutex CoMutex; typedef struct ConfidentialGuestSupport ConfidentialGuestSupport; typedef struct CPUAddressSpace CPUAddressSpace; typedef struct CPUArchState CPUArchState; +typedef struct CPUPluginState CPUPluginState; +typedef struct CpuInfoFast CpuInfoFast; +typedef struct CPUJumpCache CPUJumpCache; typedef struct CPUState CPUState; +typedef struct CPUTLBEntryFull CPUTLBEntryFull; typedef struct DeviceListener DeviceListener; typedef struct DeviceState DeviceState; typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot; typedef struct DisplayChangeListener DisplayChangeListener; typedef struct DriveInfo DriveInfo; +typedef struct DumpState DumpState; typedef struct Error Error; typedef struct EventNotifier EventNotifier; typedef struct FlatView FlatView; @@ -54,6 +60,7 @@ typedef struct FWCfgEntry FWCfgEntry; typedef struct FWCfgIoState FWCfgIoState; typedef struct FWCfgMemState FWCfgMemState; typedef struct FWCfgState FWCfgState; +typedef struct GraphicHwOps GraphicHwOps; typedef struct HostMemoryBackend HostMemoryBackend; typedef struct I2CBus I2CBus; typedef struct I2SCodec I2SCodec; @@ -88,14 +95,15 @@ typedef struct PCIDevice PCIDevice; typedef struct PCIEAERErr PCIEAERErr; typedef struct PCIEAERLog PCIEAERLog; typedef struct PCIEAERMsg PCIEAERMsg; -typedef struct PCIESriovPF PCIESriovPF; -typedef struct PCIESriovVF PCIESriovVF; typedef struct PCIEPort PCIEPort; typedef struct PCIESlot PCIESlot; +typedef struct PCIESriovPF PCIESriovPF; +typedef struct PCIESriovVF PCIESriovVF; typedef struct PCIExpressDevice PCIExpressDevice; typedef struct PCIExpressHost PCIExpressHost; typedef struct PCIHostDeviceAddress PCIHostDeviceAddress; typedef struct PCIHostState PCIHostState; +typedef struct PICCommonState PICCommonState; typedef struct PostcopyDiscardState PostcopyDiscardState; typedef struct Property Property; typedef struct PropertyInfo PropertyInfo; @@ -103,6 +111,7 @@ typedef struct QBool QBool; typedef struct QDict QDict; typedef struct QEMUBH QEMUBH; typedef struct QemuConsole QemuConsole; +typedef struct QEMUCursor QEMUCursor; typedef struct QEMUFile QEMUFile; typedef struct QemuLockable QemuLockable; typedef struct QemuMutex QemuMutex; @@ -121,9 +130,10 @@ typedef struct QString QString; typedef struct RAMBlock RAMBlock; typedef struct Range Range; typedef struct ReservedRegion ReservedRegion; -typedef struct SavedIOTLB SavedIOTLB; typedef struct SHPCDevice SHPCDevice; typedef struct SSIBus SSIBus; +typedef struct TCGCPUOps TCGCPUOps; +typedef struct TCGHelperInfo TCGHelperInfo; typedef struct TranslationBlock TranslationBlock; typedef struct VirtIODevice VirtIODevice; typedef struct Visitor Visitor; @@ -141,8 +151,6 @@ typedef struct IRQState *qemu_irq; /* * Function types */ -typedef void SaveStateHandler(QEMUFile *f, void *opaque); -typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id); typedef void (*qemu_irq_handler)(void *opaque, int n, int level); #endif /* QEMU_TYPEDEFS_H */ diff --git a/include/qemu/uri.h b/include/qemu/uri.h index d201c61260..255e61f452 100644 --- a/include/qemu/uri.h +++ b/include/qemu/uri.h @@ -41,8 +41,7 @@ * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library. If not, see . * * Authors: * Richard W.M. Jones @@ -53,10 +52,6 @@ #ifndef QEMU_URI_H #define QEMU_URI_H -#ifdef __cplusplus -extern "C" { -#endif - /** * URI: * @@ -64,48 +59,41 @@ extern "C" { * as described in RFC 2396 but separated for further processing. */ typedef struct URI { - char *scheme; /* the URI scheme */ - char *opaque; /* opaque part */ - char *authority; /* the authority part */ - char *server; /* the server part */ - char *user; /* the user part */ - int port; /* the port number */ - char *path; /* the path string */ - char *fragment; /* the fragment identifier */ - int cleanup; /* parsing potentially unclean URI */ - char *query; /* the query string (as it appears in the URI) */ + char *scheme; /* the URI scheme */ + char *opaque; /* opaque part */ + char *authority; /* the authority part */ + char *server; /* the server part */ + char *user; /* the user part */ + int port; /* the port number */ + char *path; /* the path string */ + char *fragment; /* the fragment identifier */ + int cleanup; /* parsing potentially unclean URI */ + char *query; /* the query string (as it appears in the URI) */ } URI; URI *uri_new(void); -char *uri_resolve(const char *URI, const char *base); -char *uri_resolve_relative(const char *URI, const char *base); URI *uri_parse(const char *str); URI *uri_parse_raw(const char *str, int raw); int uri_parse_into(URI *uri, const char *str); char *uri_to_string(URI *uri); -char *uri_string_escape(const char *str, const char *list); -char *uri_string_unescape(const char *str, int len, char *target); void uri_free(URI *uri); /* Single web service query parameter 'name=value'. */ typedef struct QueryParam { - char *name; /* Name (unescaped). */ - char *value; /* Value (unescaped). */ - int ignore; /* Ignore this field in qparam_get_query */ + char *name; /* Name (unescaped). */ + char *value; /* Value (unescaped). */ + int ignore; /* Ignore this field in qparam_get_query */ } QueryParam; /* Set of parameters. */ typedef struct QueryParams { - int n; /* number of parameters used */ - int alloc; /* allocated space */ - QueryParam *p; /* array of parameters */ + int n; /* number of parameters used */ + int alloc; /* allocated space */ + QueryParam *p; /* array of parameters */ } QueryParams; -struct QueryParams *query_params_new (int init_alloc); -extern QueryParams *query_params_parse (const char *query); -extern void query_params_free (QueryParams *ps); +QueryParams *query_params_new(int init_alloc); +QueryParams *query_params_parse(const char *query); +void query_params_free(QueryParams *ps); -#ifdef __cplusplus -} -#endif #endif /* QEMU_URI_H */ diff --git a/include/qemu/userfaultfd.h b/include/qemu/userfaultfd.h index 6b74f92792..18a4314212 100644 --- a/include/qemu/userfaultfd.h +++ b/include/qemu/userfaultfd.h @@ -13,10 +13,19 @@ #ifndef USERFAULTFD_H #define USERFAULTFD_H -#include "qemu/osdep.h" +#ifdef CONFIG_LINUX + #include "exec/hwaddr.h" #include +/** + * uffd_open(): Open an userfaultfd handle for current context. + * + * @flags: The flags we want to pass in when creating the handle. + * + * Returns: the uffd handle if >=0, or <0 if error happens. + */ +int uffd_open(int flags); int uffd_query_features(uint64_t *features); int uffd_create_fd(uint64_t features, bool non_blocking); void uffd_close_fd(int uffd_fd); @@ -32,4 +41,6 @@ int uffd_wakeup(int uffd_fd, void *addr, uint64_t length); int uffd_read_events(int uffd_fd, struct uffd_msg *msgs, int count); bool uffd_poll_events(int uffd_fd, int tmo); +#endif /* CONFIG_LINUX */ + #endif /* USERFAULTFD_H */ diff --git a/include/qemu/uuid.h b/include/qemu/uuid.h index 9925febfa5..869f84af09 100644 --- a/include/qemu/uuid.h +++ b/include/qemu/uuid.h @@ -61,14 +61,27 @@ typedef struct { (clock_seq_hi_and_reserved), (clock_seq_low), (node0), (node1), (node2),\ (node3), (node4), (node5) } +/* Normal (network byte order) UUID */ +#define UUID(time_low, time_mid, time_hi_and_version, \ + clock_seq_hi_and_reserved, clock_seq_low, node0, node1, node2, \ + node3, node4, node5) \ + { ((time_low) >> 24) & 0xff, ((time_low) >> 16) & 0xff, \ + ((time_low) >> 8) & 0xff, (time_low) & 0xff, \ + ((time_mid) >> 8) & 0xff, (time_mid) & 0xff, \ + ((time_hi_and_version) >> 8) & 0xff, (time_hi_and_version) & 0xff, \ + (clock_seq_hi_and_reserved), (clock_seq_low), \ + (node0), (node1), (node2), (node3), (node4), (node5) \ + } + #define UUID_FMT "%02hhx%02hhx%02hhx%02hhx-" \ "%02hhx%02hhx-%02hhx%02hhx-" \ "%02hhx%02hhx-" \ "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" -#define UUID_FMT_LEN 36 - #define UUID_NONE "00000000-0000-0000-0000-000000000000" +QEMU_BUILD_BUG_ON(sizeof(UUID_NONE) - 1 != 36); + +#define UUID_STR_LEN sizeof(UUID_NONE) void qemu_uuid_generate(QemuUUID *out); @@ -84,4 +97,6 @@ int qemu_uuid_parse(const char *str, QemuUUID *uuid); QemuUUID qemu_uuid_bswap(QemuUUID uuid); +uint32_t qemu_uuid_hash(const void *uuid); + #endif diff --git a/include/qemu/vhost-user-server.h b/include/qemu/vhost-user-server.h index cd43193b80..0417ec0533 100644 --- a/include/qemu/vhost-user-server.h +++ b/include/qemu/vhost-user-server.h @@ -15,7 +15,6 @@ #include "io/channel-socket.h" #include "io/channel-file.h" #include "io/net-listener.h" -#include "qemu/error-report.h" #include "qapi/error.h" #include "standard-headers/linux/virtio_blk.h" @@ -41,9 +40,12 @@ typedef struct { int max_queues; const VuDevIface *vu_iface; + unsigned int in_flight; /* atomic */ + /* Protected by ctx lock */ - unsigned int refcount; + bool in_qio_channel_yield; bool wait_idle; + bool quiescing; VuDev vu_dev; QIOChannel *ioc; /* The I/O channel with the client */ QIOChannelSocket *sioc; /* The underlying data channel with the client */ @@ -61,8 +63,9 @@ bool vhost_user_server_start(VuServer *server, void vhost_user_server_stop(VuServer *server); -void vhost_user_server_ref(VuServer *server); -void vhost_user_server_unref(VuServer *server); +void vhost_user_server_inc_in_flight(VuServer *server); +void vhost_user_server_dec_in_flight(VuServer *server); +bool vhost_user_server_has_in_flight(VuServer *server); void vhost_user_server_attach_aio_context(VuServer *server, AioContext *ctx); void vhost_user_server_detach_aio_context(VuServer *server); diff --git a/include/qemu/xattr.h b/include/qemu/xattr.h index f1d0f7be74..b08a934acc 100644 --- a/include/qemu/xattr.h +++ b/include/qemu/xattr.h @@ -25,7 +25,9 @@ # if !defined(ENOATTR) # define ENOATTR ENODATA # endif -# include +# ifndef CONFIG_WIN32 +# include +# endif #endif #endif diff --git a/include/qemu/xxhash.h b/include/qemu/xxhash.h index c2dcccadbf..0259bbef18 100644 --- a/include/qemu/xxhash.h +++ b/include/qemu/xxhash.h @@ -48,8 +48,8 @@ * xxhash32, customized for input variables that are not guaranteed to be * contiguous in memory. */ -static inline uint32_t -qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g) +static inline uint32_t qemu_xxhash8(uint64_t ab, uint64_t cd, uint64_t ef, + uint32_t g, uint32_t h) { uint32_t v1 = QEMU_XXHASH_SEED + PRIME32_1 + PRIME32_2; uint32_t v2 = QEMU_XXHASH_SEED + PRIME32_2; @@ -59,6 +59,8 @@ qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g) uint32_t b = ab >> 32; uint32_t c = cd; uint32_t d = cd >> 32; + uint32_t e = ef; + uint32_t f = ef >> 32; uint32_t h32; v1 += a * PRIME32_2; @@ -89,6 +91,9 @@ qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g) h32 += g * PRIME32_3; h32 = rol32(h32, 17) * PRIME32_4; + h32 += h * PRIME32_3; + h32 = rol32(h32, 17) * PRIME32_4; + h32 ^= h32 >> 15; h32 *= PRIME32_2; h32 ^= h32 >> 13; @@ -100,23 +105,29 @@ qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g) static inline uint32_t qemu_xxhash2(uint64_t ab) { - return qemu_xxhash7(ab, 0, 0, 0, 0); + return qemu_xxhash8(ab, 0, 0, 0, 0); } static inline uint32_t qemu_xxhash4(uint64_t ab, uint64_t cd) { - return qemu_xxhash7(ab, cd, 0, 0, 0); + return qemu_xxhash8(ab, cd, 0, 0, 0); } static inline uint32_t qemu_xxhash5(uint64_t ab, uint64_t cd, uint32_t e) { - return qemu_xxhash7(ab, cd, e, 0, 0); + return qemu_xxhash8(ab, cd, 0, e, 0); } static inline uint32_t qemu_xxhash6(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f) { - return qemu_xxhash7(ab, cd, e, f, 0); + return qemu_xxhash8(ab, cd, 0, e, f); +} + +static inline uint32_t qemu_xxhash7(uint64_t ab, uint64_t cd, uint64_t ef, + uint32_t g) +{ + return qemu_xxhash8(ab, cd, ef, g, 0); } /* diff --git a/include/qemu/yank.h b/include/qemu/yank.h index 5375a1f195..3d88af6996 100644 --- a/include/qemu/yank.h +++ b/include/qemu/yank.h @@ -25,7 +25,7 @@ typedef void (YankFn)(void *opaque); * @instance: The instance. * @errp: Error object. * - * Returns true on success or false if an error occured. + * Returns true on success or false if an error occurred. */ bool yank_register_instance(const YankInstance *instance, Error **errp); @@ -45,7 +45,7 @@ void yank_unregister_instance(const YankInstance *instance); * yank_register_function: Register a yank function * * This registers a yank function. All limitations of qmp oob commands apply - * to the yank function as well. See docs/devel/qapi-code-gen.txt under + * to the yank function as well. See docs/devel/qapi-code-gen.rst under * "An OOB-capable command handler must satisfy the following conditions". * * This function is thread-safe. diff --git a/include/qom/object.h b/include/qom/object.h index 5f3d5b5bf5..13d3a655dd 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -16,7 +16,6 @@ #include "qapi/qapi-builtin-types.h" #include "qemu/module.h" -#include "qom/object.h" struct TypeImpl; typedef struct TypeImpl *Type; @@ -259,6 +258,51 @@ struct Object DECLARE_INSTANCE_CHECKER(InstanceType, MODULE_OBJ_NAME, TYPE_##MODULE_OBJ_NAME) +/** + * DO_OBJECT_DEFINE_TYPE_EXTENDED: + * @ModuleObjName: the object name with initial caps + * @module_obj_name: the object name in lowercase with underscore separators + * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators + * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore + * separators + * @ABSTRACT: boolean flag to indicate whether the object can be instantiated + * @CLASS_SIZE: size of the type's class + * @...: list of initializers for "InterfaceInfo" to declare implemented interfaces + * + * This is the base macro used to implement all the OBJECT_DEFINE_* + * macros. It should never be used directly in a source file. + */ +#define DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ + MODULE_OBJ_NAME, \ + PARENT_MODULE_OBJ_NAME, \ + ABSTRACT, CLASS_SIZE, ...) \ + static void \ + module_obj_name##_finalize(Object *obj); \ + static void \ + module_obj_name##_class_init(ObjectClass *oc, void *data); \ + static void \ + module_obj_name##_init(Object *obj); \ + \ + static const TypeInfo module_obj_name##_info = { \ + .parent = TYPE_##PARENT_MODULE_OBJ_NAME, \ + .name = TYPE_##MODULE_OBJ_NAME, \ + .instance_size = sizeof(ModuleObjName), \ + .instance_align = __alignof__(ModuleObjName), \ + .instance_init = module_obj_name##_init, \ + .instance_finalize = module_obj_name##_finalize, \ + .class_size = CLASS_SIZE, \ + .class_init = module_obj_name##_class_init, \ + .abstract = ABSTRACT, \ + .interfaces = (InterfaceInfo[]) { __VA_ARGS__ } , \ + }; \ + \ + static void \ + module_obj_name##_register_types(void) \ + { \ + type_register_static(&module_obj_name##_info); \ + } \ + type_init(module_obj_name##_register_types); + /** * OBJECT_DEFINE_TYPE_EXTENDED: * @ModuleObjName: the object name with initial caps @@ -285,32 +329,10 @@ struct Object #define OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ ABSTRACT, ...) \ - static void \ - module_obj_name##_finalize(Object *obj); \ - static void \ - module_obj_name##_class_init(ObjectClass *oc, void *data); \ - static void \ - module_obj_name##_init(Object *obj); \ - \ - static const TypeInfo module_obj_name##_info = { \ - .parent = TYPE_##PARENT_MODULE_OBJ_NAME, \ - .name = TYPE_##MODULE_OBJ_NAME, \ - .instance_size = sizeof(ModuleObjName), \ - .instance_align = __alignof__(ModuleObjName), \ - .instance_init = module_obj_name##_init, \ - .instance_finalize = module_obj_name##_finalize, \ - .class_size = sizeof(ModuleObjName##Class), \ - .class_init = module_obj_name##_class_init, \ - .abstract = ABSTRACT, \ - .interfaces = (InterfaceInfo[]) { __VA_ARGS__ } , \ - }; \ - \ - static void \ - module_obj_name##_register_types(void) \ - { \ - type_register_static(&module_obj_name##_info); \ - } \ - type_init(module_obj_name##_register_types); + DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ + MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ + ABSTRACT, sizeof(ModuleObjName##Class), \ + __VA_ARGS__) /** * OBJECT_DEFINE_TYPE: @@ -369,6 +391,45 @@ struct Object MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ true, { NULL }) +/** + * OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES: + * @ModuleObjName: the object name with initial caps + * @module_obj_name: the object name in lowercase with underscore separators + * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators + * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore + * separators + * + * This is a variant of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable for + * the case of a non-abstract type, with interfaces, and with no requirement + * for a class struct. + */ +#define OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(ModuleObjName, \ + module_obj_name, \ + MODULE_OBJ_NAME, \ + PARENT_MODULE_OBJ_NAME, ...) \ + DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ + MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ + false, 0, __VA_ARGS__) + +/** + * OBJECT_DEFINE_SIMPLE_TYPE: + * @ModuleObjName: the object name with initial caps + * @module_obj_name: the object name in lowercase with underscore separators + * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators + * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore + * separators + * + * This is a variant of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable for + * the common case of a non-abstract type, without any interfaces, and with + * no requirement for a class struct. If you declared your type with + * OBJECT_DECLARE_SIMPLE_TYPE then this is probably the right choice for + * defining it. + */ +#define OBJECT_DEFINE_SIMPLE_TYPE(ModuleObjName, module_obj_name, \ + MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME) \ + OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(ModuleObjName, module_obj_name, \ + MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, { NULL }) + /** * struct TypeInfo: * @name: The name of the type. @@ -1094,6 +1155,14 @@ void object_property_set_default_bool(ObjectProperty *prop, bool value); */ void object_property_set_default_str(ObjectProperty *prop, const char *value); +/** + * object_property_set_default_list: + * @prop: the property to set + * + * Set the property default value to be an empty list. + */ +void object_property_set_default_list(ObjectProperty *prop); + /** * object_property_set_default_int: * @prop: the property to set @@ -1543,6 +1612,19 @@ Object *object_resolve_path(const char *path, bool *ambiguous); Object *object_resolve_path_type(const char *path, const char *typename, bool *ambiguous); +/** + * object_resolve_type_unambiguous: + * @typename: the type to look for + * @errp: pointer to error object + * + * Return the only object in the QOM tree of type @typename. + * If no match or more than one match is found, an error is + * returned. + * + * Returns: The matched object or NULL on path lookup failure. + */ +Object *object_resolve_type_unambiguous(const char *typename, Error **errp); + /** * object_resolve_path_at: * @parent: the object in which to resolve the path diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h index 81541e2080..02b11a7ef0 100644 --- a/include/qom/object_interfaces.h +++ b/include/qom/object_interfaces.h @@ -99,7 +99,7 @@ void user_creatable_add_qapi(ObjectOptions *options, Error **errp); /** * user_creatable_parse_str: - * @optarg: the object definition string as passed on the command line + * @str: the object definition string as passed on the command line * @errp: if an error occurs, a pointer to an area to store the error * * Parses the option for the user creatable object with a keyval parser and @@ -110,14 +110,14 @@ void user_creatable_add_qapi(ObjectOptions *options, Error **errp); * Returns: ObjectOptions on success, NULL when an error occurred (*errp is set * then) or help was printed (*errp is not set). */ -ObjectOptions *user_creatable_parse_str(const char *optarg, Error **errp); +ObjectOptions *user_creatable_parse_str(const char *str, Error **errp); /** * user_creatable_add_from_str: - * @optarg: the object definition string as passed on the command line + * @str: the object definition string as passed on the command line * @errp: if an error occurs, a pointer to an area to store the error * - * Create an instance of the user creatable object by parsing optarg + * Create an instance of the user creatable object by parsing @str * with a keyval parser and implicit key 'qom-type', converting the * result to ObjectOptions and calling into qmp_object_add(). * @@ -126,13 +126,13 @@ ObjectOptions *user_creatable_parse_str(const char *optarg, Error **errp); * Returns: true when an object was successfully created, false when an error * occurred (*errp is set then) or help was printed (*errp is not set). */ -bool user_creatable_add_from_str(const char *optarg, Error **errp); +bool user_creatable_add_from_str(const char *str, Error **errp); /** * user_creatable_process_cmdline: - * @optarg: the object definition string as passed on the command line + * @cmdline: the object definition string as passed on the command line * - * Create an instance of the user creatable object by parsing optarg + * Create an instance of the user creatable object by parsing @cmdline * with a keyval parser and implicit key 'qom-type', converting the * result to ObjectOptions and calling into qmp_object_add(). * @@ -141,7 +141,7 @@ bool user_creatable_add_from_str(const char *optarg, Error **errp); * This function is only meant to be called during command line parsing. * It exits the process on failure or after printing help. */ -void user_creatable_process_cmdline(const char *optarg); +void user_creatable_process_cmdline(const char *cmdline); /** * user_creatable_print_help: diff --git a/include/scsi/constants.h b/include/scsi/constants.h index 2a32c08b5e..9b98451912 100644 --- a/include/scsi/constants.h +++ b/include/scsi/constants.h @@ -225,15 +225,18 @@ #define TYPE_NO_LUN 0x7f /* Mode page codes for mode sense/set */ +#define MODE_PAGE_VENDOR_SPECIFIC 0x00 #define MODE_PAGE_R_W_ERROR 0x01 #define MODE_PAGE_HD_GEOMETRY 0x04 #define MODE_PAGE_FLEXIBLE_DISK_GEOMETRY 0x05 #define MODE_PAGE_CACHING 0x08 #define MODE_PAGE_AUDIO_CTL 0x0e +#define MODE_PAGE_CONTROL 0x0a #define MODE_PAGE_POWER 0x1a #define MODE_PAGE_FAULT_FAIL 0x1c #define MODE_PAGE_TO_PROTECT 0x1d #define MODE_PAGE_CAPABILITIES 0x2a +#define MODE_PAGE_APPLE_VENDOR 0x30 #define MODE_PAGE_ALLS 0x3f /* Not in Mt. Fuji, but in ATAPI 2.6 -- deprecated now in favor * of MODE_PAGE_SENSE_POWER */ diff --git a/include/scsi/pr-manager.h b/include/scsi/pr-manager.h index e4ecbe00f6..45de28d354 100644 --- a/include/scsi/pr-manager.h +++ b/include/scsi/pr-manager.h @@ -5,7 +5,6 @@ #include "qapi/visitor.h" #include "qom/object_interfaces.h" #include "block/aio.h" -#include "qemu/coroutine.h" #define TYPE_PR_MANAGER "pr-manager" diff --git a/semihosting/common-semi.h b/include/semihosting/common-semi.h similarity index 96% rename from semihosting/common-semi.h rename to include/semihosting/common-semi.h index 0bfab1c669..0a91db7c41 100644 --- a/semihosting/common-semi.h +++ b/include/semihosting/common-semi.h @@ -34,6 +34,6 @@ #ifndef COMMON_SEMI_H #define COMMON_SEMI_H -target_ulong do_common_semihosting(CPUState *cs); +void do_common_semihosting(CPUState *cs); #endif /* COMMON_SEMI_H */ diff --git a/include/semihosting/console.h b/include/semihosting/console.h index 0238f540f4..bd78e5f03f 100644 --- a/include/semihosting/console.h +++ b/include/semihosting/console.h @@ -12,58 +12,48 @@ #include "cpu.h" /** - * qemu_semihosting_console_outs: - * @env: CPUArchState - * @s: host address of null terminated guest string + * qemu_semihosting_console_read: + * @cs: CPUState + * @buf: host buffer + * @len: buffer size * - * Send a null terminated guest string to the debug console. This may - * be the remote gdb session if a softmmu guest is currently being - * debugged. + * Receive at least one character from debug console. 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: * - * Returns: number of bytes written. - */ -int qemu_semihosting_console_outs(CPUArchState *env, target_ulong s); - -/** - * qemu_semihosting_console_outc: - * @env: CPUArchState - * @s: host address of null terminated guest string - * - * Send single character from guest memory to the debug console. This - * may be the remote gdb session if a softmmu guest is currently being - * debugged. - * - * Returns: nothing - */ -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! + * Returns: number of characters read, OR cpu_loop_exit! */ -target_ulong qemu_semihosting_console_inc(CPUArchState *env); +int qemu_semihosting_console_read(CPUState *cs, void *buf, int len); /** - * qemu_semihosting_log_out: - * @s: pointer to string - * @len: length of string + * qemu_semihosting_console_write: + * @buf: host buffer + * @len: buffer size * - * Send a string to the debug output. Unlike console_out these strings - * can't be sent to a remote gdb instance as they don't exist in guest - * memory. + * Write len bytes from buf to the debug console. * - * Returns: number of bytes written + * Returns: number of bytes written -- this should only ever be short + * on some sort of i/o error. */ -int qemu_semihosting_log_out(const char *s, int len); +int qemu_semihosting_console_write(void *buf, int len); + +/* + * qemu_semihosting_console_block_until_ready: + * @cs: CPUState + * + * If no data is available we suspend the CPU and will re-execute the + * instruction when data is available. + */ +void qemu_semihosting_console_block_until_ready(CPUState *cs); + +/** + * qemu_semihosting_console_ready: + * + * Return true if characters are available for read; does not block. + */ +bool qemu_semihosting_console_ready(void); #endif /* SEMIHOST_CONSOLE_H */ diff --git a/include/semihosting/guestfd.h b/include/semihosting/guestfd.h new file mode 100644 index 0000000000..3d426fedab --- /dev/null +++ b/include/semihosting/guestfd.h @@ -0,0 +1,91 @@ +/* + * Hosted file support for semihosting syscalls. + * + * Copyright (c) 2005, 2007 CodeSourcery. + * Copyright (c) 2019 Linaro + * Copyright © 2020 by Keith Packard + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SEMIHOSTING_GUESTFD_H +#define SEMIHOSTING_GUESTFD_H + +typedef enum GuestFDType { + GuestFDUnused = 0, + GuestFDHost, + GuestFDGDB, + GuestFDStatic, + GuestFDConsole, +} GuestFDType; + +/* + * Guest file descriptors are integer indexes into an array of + * these structures (we will dynamically resize as necessary). + */ +typedef struct GuestFD { + GuestFDType type; + union { + int hostfd; + struct { + const uint8_t *data; + size_t len; + size_t off; + } staticfile; + }; +} GuestFD; + +/* + * For ARM semihosting, we have a separate structure for routing + * data for the console which is outside the guest fd address space. + */ +extern GuestFD console_in_gf; +extern GuestFD console_out_gf; + +/** + * alloc_guestfd: + * + * Allocate an unused GuestFD index. The associated guestfd index + * will still be GuestFDUnused until it is initialized. + */ +int alloc_guestfd(void); + +/** + * dealloc_guestfd: + * @guestfd: GuestFD index + * + * Deallocate a GuestFD index. The associated GuestFD structure + * will be recycled for a subsequent allocation. + */ +void dealloc_guestfd(int guestfd); + +/** + * get_guestfd: + * @guestfd: GuestFD index + * + * Return the GuestFD structure associated with an initialized @guestfd, + * or NULL if it has not been allocated, or hasn't been initialized. + */ +GuestFD *get_guestfd(int guestfd); + +/** + * associate_guestfd: + * @guestfd: GuestFD index + * @hostfd: host file descriptor + * + * Initialize the GuestFD for @guestfd to GuestFDHost using @hostfd. + */ +void associate_guestfd(int guestfd, int hostfd); + +/** + * staticfile_guestfd: + * @guestfd: GuestFD index + * @data: data to be read + * @len: length of @data + * + * Initialize the GuestFD for @guestfd to GuestFDStatic. + * The @len bytes at @data will be returned to the guest on reads. + */ +void staticfile_guestfd(int guestfd, const uint8_t *data, size_t len); + +#endif /* SEMIHOSTING_GUESTFD_H */ diff --git a/include/semihosting/semihost.h b/include/semihosting/semihost.h index 0c55ade3ac..97d2a2ba99 100644 --- a/include/semihosting/semihost.h +++ b/include/semihosting/semihost.h @@ -27,7 +27,7 @@ typedef enum SemihostingTarget { } SemihostingTarget; #ifdef CONFIG_USER_ONLY -static inline bool semihosting_enabled(void) +static inline bool semihosting_enabled(bool is_user) { return true; } @@ -51,27 +51,25 @@ static inline const char *semihosting_get_cmdline(void) { return NULL; } - -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); +/** + * semihosting_enabled: + * @is_user: true if guest code is in usermode (i.e. not privileged) + * + * Return true if guest code is allowed to make semihosting calls. + */ +bool semihosting_enabled(bool is_user); SemihostingTarget semihosting_get_target(void); const char *semihosting_get_arg(int i); int semihosting_get_argc(void); const char *semihosting_get_cmdline(void); void semihosting_arg_fallback(const char *file, const char *cmd); -Chardev *semihosting_get_chardev(void); /* for vl.c hooks */ 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); +int qemu_semihosting_config_options(const char *optstr); +void qemu_semihosting_chardev_init(void); +void qemu_semihosting_console_init(Chardev *); #endif /* CONFIG_USER_ONLY */ +void qemu_semihosting_guestfd_init(void); #endif /* SEMIHOST_H */ diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h new file mode 100644 index 0000000000..3a5ec229eb --- /dev/null +++ b/include/semihosting/syscalls.h @@ -0,0 +1,75 @@ +/* + * Syscall implementations for semihosting. + * + * Copyright (c) 2022 Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SEMIHOSTING_SYSCALLS_H +#define SEMIHOSTING_SYSCALLS_H + +/* + * Argument loading from the guest is performed by the caller; + * results are returned via the 'complete' callback. + * + * String operands are in address/len pairs. The len argument may be 0 + * (when the semihosting abi does not already provide the length), + * or non-zero (where it should include the terminating zero). + */ + +typedef struct GuestFD GuestFD; + +void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + int gdb_flags, int mode); + +void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, + int fd); + +void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong buf, target_ulong len); + +void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len); + +void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong buf, target_ulong len); + +void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len); + +void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, int64_t off, int gdb_whence); + +void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete, + int fd); + +void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, + gdb_syscall_complete_cb flen_cb, + int fd, target_ulong fstat_addr); + +void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong addr); + +void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + target_ulong addr); + +void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len); + +void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong oname, target_ulong oname_len, + target_ulong nname, target_ulong nname_len); + +void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong cmd, target_ulong cmd_len); + +void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong tv_addr, target_ulong tz_addr); + +void semihost_sys_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, GIOCondition cond, int timeout); + +#endif /* SEMIHOSTING_SYSCALLS_H */ diff --git a/include/semihosting/uaccess.h b/include/semihosting/uaccess.h new file mode 100644 index 0000000000..3963eafc3e --- /dev/null +++ b/include/semihosting/uaccess.h @@ -0,0 +1,63 @@ +/* + * Helper routines to provide target memory access for semihosting + * syscalls in system emulation mode. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licensed under the GPL + */ + +#ifndef SEMIHOSTING_UACCESS_H +#define SEMIHOSTING_UACCESS_H + +#ifdef CONFIG_USER_ONLY +#error Cannot include semihosting/uaccess.h from user emulation +#endif + +#include "cpu.h" + +#define get_user_u64(val, addr) \ + ({ uint64_t val_ = 0; \ + int ret_ = cpu_memory_rw_debug(env_cpu(env), (addr), \ + &val_, sizeof(val_), 0); \ + (val) = tswap64(val_); ret_; }) + +#define get_user_u32(val, addr) \ + ({ uint32_t val_ = 0; \ + int ret_ = cpu_memory_rw_debug(env_cpu(env), (addr), \ + &val_, sizeof(val_), 0); \ + (val) = tswap32(val_); ret_; }) + +#define get_user_u8(val, addr) \ + ({ uint8_t val_ = 0; \ + int ret_ = cpu_memory_rw_debug(env_cpu(env), (addr), \ + &val_, sizeof(val_), 0); \ + (val) = val_; ret_; }) + +#define get_user_ual(arg, p) get_user_u32(arg, p) + +#define put_user_u64(val, addr) \ + ({ uint64_t val_ = tswap64(val); \ + cpu_memory_rw_debug(env_cpu(env), (addr), &val_, sizeof(val_), 1); }) + +#define put_user_u32(val, addr) \ + ({ uint32_t val_ = tswap32(val); \ + cpu_memory_rw_debug(env_cpu(env), (addr), &val_, sizeof(val_), 1); }) + +#define put_user_ual(arg, p) put_user_u32(arg, p) + +void *uaccess_lock_user(CPUArchState *env, target_ulong addr, + target_ulong len, bool copy); +#define lock_user(type, p, len, copy) uaccess_lock_user(env, p, len, copy) + +char *uaccess_lock_user_string(CPUArchState *env, target_ulong addr); +#define lock_user_string(p) uaccess_lock_user_string(env, p) + +void uaccess_unlock_user(CPUArchState *env, void *p, + target_ulong addr, target_ulong len); +#define unlock_user(s, args, len) uaccess_unlock_user(env, s, args, len) + +ssize_t uaccess_strlen_user(CPUArchState *env, target_ulong addr); +#define target_strlen(p) uaccess_strlen_user(env, p) + +#endif /* SEMIHOSTING_SOFTMMU_UACCESS_H */ diff --git a/include/standard-headers/asm-m68k/bootinfo-virt.h b/include/standard-headers/asm-m68k/bootinfo-virt.h index 81be1e0924..75ac6bbd7d 100644 --- a/include/standard-headers/asm-m68k/bootinfo-virt.h +++ b/include/standard-headers/asm-m68k/bootinfo-virt.h @@ -13,6 +13,9 @@ #define BI_VIRT_VIRTIO_BASE 0x8004 #define BI_VIRT_CTRL_BASE 0x8005 +/* No longer used -- replaced with BI_RNG_SEED -- but don't reuse this index: + * #define BI_VIRT_RNG_SEED 0x8006 */ + #define VIRT_BOOTI_VERSION MK_BI_VERSION(2, 0) #endif /* _UAPI_ASM_M68K_BOOTINFO_MAC_H */ diff --git a/include/standard-headers/asm-m68k/bootinfo.h b/include/standard-headers/asm-m68k/bootinfo.h index 7b790e8ec8..b7a8dd2514 100644 --- a/include/standard-headers/asm-m68k/bootinfo.h +++ b/include/standard-headers/asm-m68k/bootinfo.h @@ -57,7 +57,13 @@ struct mem_info { /* (struct mem_info) */ #define BI_COMMAND_LINE 0x0007 /* kernel command line parameters */ /* (string) */ - +/* + * A random seed used to initialize the RNG. Record format: + * + * - length [ 2 bytes, 16-bit big endian ] + * - seed data [ `length` bytes, padded to preserve 4-byte struct alignment ] + */ +#define BI_RNG_SEED 0x0008 /* * Linux/m68k Architectures (BI_MACHTYPE) diff --git a/include/standard-headers/asm-x86/bootparam.h b/include/standard-headers/asm-x86/bootparam.h index 072e2ed546..0b06d2bff1 100644 --- a/include/standard-headers/asm-x86/bootparam.h +++ b/include/standard-headers/asm-x86/bootparam.h @@ -10,11 +10,13 @@ #define SETUP_EFI 4 #define SETUP_APPLE_PROPERTIES 5 #define SETUP_JAILHOUSE 6 +#define SETUP_CC_BLOB 7 +#define SETUP_IMA 8 +#define SETUP_RNG_SEED 9 +#define SETUP_ENUM_MAX SETUP_RNG_SEED #define SETUP_INDIRECT (1<<31) - -/* SETUP_INDIRECT | max(SETUP_*) */ -#define SETUP_TYPE_MAX (SETUP_INDIRECT | SETUP_JAILHOUSE) +#define SETUP_TYPE_MAX (SETUP_ENUM_MAX | SETUP_INDIRECT) /* ram_size flags */ #define RAMDISK_IMAGE_START_MASK 0x07FF diff --git a/include/standard-headers/drm/drm_fourcc.h b/include/standard-headers/drm/drm_fourcc.h index 4888f85f69..b72917073d 100644 --- a/include/standard-headers/drm/drm_fourcc.h +++ b/include/standard-headers/drm/drm_fourcc.h @@ -53,7 +53,7 @@ extern "C" { * Format modifiers may change any property of the buffer, including the number * of planes and/or the required allocation size. Format modifiers are * vendor-namespaced, and as such the relationship between a fourcc code and a - * modifier is specific to the modifer being used. For example, some modifiers + * modifier is specific to the modifier being used. For example, some modifiers * may preserve meaning - such as number of planes - from the fourcc code, * whereas others may not. * @@ -78,7 +78,7 @@ extern "C" { * format. * - Higher-level programs interfacing with KMS/GBM/EGL/Vulkan/etc: these users * see modifiers as opaque tokens they can check for equality and intersect. - * These users musn't need to know to reason about the modifier value + * These users mustn't need to know to reason about the modifier value * (i.e. they are not expected to extract information out of the modifier). * * Vendors should document their modifier usage in as much detail as @@ -87,6 +87,18 @@ extern "C" { * * The authoritative list of format modifier codes is found in * `include/uapi/drm/drm_fourcc.h` + * + * Open Source User Waiver + * ----------------------- + * + * Because this is the authoritative source for pixel formats and modifiers + * referenced by GL, Vulkan extensions and other standards and hence used both + * by open source and closed source driver stacks, the usual requirement for an + * upstream in-kernel or open source userspace user does not apply. + * + * To ensure, as much as feasible, compatibility across stacks and avoid + * confusion with incompatible enumerations stakeholders for all relevant driver + * stacks should approve additions. */ #define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \ @@ -98,18 +110,42 @@ extern "C" { #define DRM_FORMAT_INVALID 0 /* color index */ +#define DRM_FORMAT_C1 fourcc_code('C', '1', ' ', ' ') /* [7:0] C0:C1:C2:C3:C4:C5:C6:C7 1:1:1:1:1:1:1:1 eight pixels/byte */ +#define DRM_FORMAT_C2 fourcc_code('C', '2', ' ', ' ') /* [7:0] C0:C1:C2:C3 2:2:2:2 four pixels/byte */ +#define DRM_FORMAT_C4 fourcc_code('C', '4', ' ', ' ') /* [7:0] C0:C1 4:4 two pixels/byte */ #define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */ -/* 8 bpp Red */ +/* 1 bpp Darkness (inverse relationship between channel value and brightness) */ +#define DRM_FORMAT_D1 fourcc_code('D', '1', ' ', ' ') /* [7:0] D0:D1:D2:D3:D4:D5:D6:D7 1:1:1:1:1:1:1:1 eight pixels/byte */ + +/* 2 bpp Darkness (inverse relationship between channel value and brightness) */ +#define DRM_FORMAT_D2 fourcc_code('D', '2', ' ', ' ') /* [7:0] D0:D1:D2:D3 2:2:2:2 four pixels/byte */ + +/* 4 bpp Darkness (inverse relationship between channel value and brightness) */ +#define DRM_FORMAT_D4 fourcc_code('D', '4', ' ', ' ') /* [7:0] D0:D1 4:4 two pixels/byte */ + +/* 8 bpp Darkness (inverse relationship between channel value and brightness) */ +#define DRM_FORMAT_D8 fourcc_code('D', '8', ' ', ' ') /* [7:0] D */ + +/* 1 bpp Red (direct relationship between channel value and brightness) */ +#define DRM_FORMAT_R1 fourcc_code('R', '1', ' ', ' ') /* [7:0] R0:R1:R2:R3:R4:R5:R6:R7 1:1:1:1:1:1:1:1 eight pixels/byte */ + +/* 2 bpp Red (direct relationship between channel value and brightness) */ +#define DRM_FORMAT_R2 fourcc_code('R', '2', ' ', ' ') /* [7:0] R0:R1:R2:R3 2:2:2:2 four pixels/byte */ + +/* 4 bpp Red (direct relationship between channel value and brightness) */ +#define DRM_FORMAT_R4 fourcc_code('R', '4', ' ', ' ') /* [7:0] R0:R1 4:4 two pixels/byte */ + +/* 8 bpp Red (direct relationship between channel value and brightness) */ #define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ -/* 10 bpp Red */ +/* 10 bpp Red (direct relationship between channel value and brightness) */ #define DRM_FORMAT_R10 fourcc_code('R', '1', '0', ' ') /* [15:0] x:R 6:10 little endian */ -/* 12 bpp Red */ +/* 12 bpp Red (direct relationship between channel value and brightness) */ #define DRM_FORMAT_R12 fourcc_code('R', '1', '2', ' ') /* [15:0] x:R 4:12 little endian */ -/* 16 bpp Red */ +/* 16 bpp Red (direct relationship between channel value and brightness) */ #define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */ /* 16 bpp RG */ @@ -204,7 +240,9 @@ extern "C" { #define DRM_FORMAT_VYUY fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */ #define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */ +#define DRM_FORMAT_AVUY8888 fourcc_code('A', 'V', 'U', 'Y') /* [31:0] A:Cr:Cb:Y 8:8:8:8 little endian */ #define DRM_FORMAT_XYUV8888 fourcc_code('X', 'Y', 'U', 'V') /* [31:0] X:Y:Cb:Cr 8:8:8:8 little endian */ +#define DRM_FORMAT_XVUY8888 fourcc_code('X', 'V', 'U', 'Y') /* [31:0] X:Cr:Cb:Y 8:8:8:8 little endian */ #define DRM_FORMAT_VUY888 fourcc_code('V', 'U', '2', '4') /* [23:0] Cr:Cb:Y 8:8:8 little endian */ #define DRM_FORMAT_VUY101010 fourcc_code('V', 'U', '3', '0') /* Y followed by U then V, 10:10:10. Non-linear modifier only */ @@ -284,6 +322,8 @@ extern "C" { * index 1 = Cr:Cb plane, [39:0] Cr1:Cb1:Cr0:Cb0 little endian */ #define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5') /* 2x2 subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV20 fourcc_code('N', 'V', '2', '0') /* 2x1 subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV30 fourcc_code('N', 'V', '3', '0') /* non-subsampled Cr:Cb plane */ /* * 2 plane YCbCr MSB aligned @@ -499,7 +539,7 @@ extern "C" { * This is a tiled layout using 4Kb tiles in row-major layout. * Within the tile pixels are laid out in 16 256 byte units / sub-tiles which * are arranged in four groups (two wide, two high) with column-major layout. - * Each group therefore consits out of four 256 byte units, which are also laid + * Each group therefore consists out of four 256 byte units, which are also laid * out as 2x2 column-major. * 256 byte units are made out of four 64 byte blocks of pixels, producing * either a square block or a 2:1 unit. @@ -558,7 +598,7 @@ extern "C" { * * The main surface is Y-tiled and is at plane index 0 whereas CCS is linear * and at index 1. The clear color is stored at index 2, and the pitch should - * be ignored. The clear color structure is 256 bits. The first 128 bits + * be 64 bytes aligned. The clear color structure is 256 bits. The first 128 bits * represents Raw Clear Color Red, Green, Blue and Alpha color each represented * by 32 bits. The raw clear color is consumed by the 3d engine and generates * the converted clear color of size 64 bits. The first 32 bits store the Lower @@ -571,6 +611,96 @@ extern "C" { */ #define I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC fourcc_mod_code(INTEL, 8) +/* + * Intel Tile 4 layout + * + * This is a tiled layout using 4KB tiles in a row-major layout. It has the same + * shape as Tile Y at two granularities: 4KB (128B x 32) and 64B (16B x 4). It + * only differs from Tile Y at the 256B granularity in between. At this + * granularity, Tile Y has a shape of 16B x 32 rows, but this tiling has a shape + * of 64B x 8 rows. + */ +#define I915_FORMAT_MOD_4_TILED fourcc_mod_code(INTEL, 9) + +/* + * Intel color control surfaces (CCS) for DG2 render compression. + * + * The main surface is Tile 4 and at plane index 0. The CCS data is stored + * outside of the GEM object in a reserved memory area dedicated for the + * storage of the CCS data for all RC/RC_CC/MC compressible GEM objects. The + * main surface pitch is required to be a multiple of four Tile 4 widths. + */ +#define I915_FORMAT_MOD_4_TILED_DG2_RC_CCS fourcc_mod_code(INTEL, 10) + +/* + * Intel color control surfaces (CCS) for DG2 media compression. + * + * The main surface is Tile 4 and at plane index 0. For semi-planar formats + * like NV12, the Y and UV planes are Tile 4 and are located at plane indices + * 0 and 1, respectively. The CCS for all planes are stored outside of the + * GEM object in a reserved memory area dedicated for the storage of the + * CCS data for all RC/RC_CC/MC compressible GEM objects. The main surface + * pitch is required to be a multiple of four Tile 4 widths. + */ +#define I915_FORMAT_MOD_4_TILED_DG2_MC_CCS fourcc_mod_code(INTEL, 11) + +/* + * Intel Color Control Surface with Clear Color (CCS) for DG2 render compression. + * + * The main surface is Tile 4 and at plane index 0. The CCS data is stored + * outside of the GEM object in a reserved memory area dedicated for the + * storage of the CCS data for all RC/RC_CC/MC compressible GEM objects. The + * main surface pitch is required to be a multiple of four Tile 4 widths. The + * clear color is stored at plane index 1 and the pitch should be 64 bytes + * aligned. The format of the 256 bits of clear color data matches the one used + * for the I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC modifier, see its description + * for details. + */ +#define I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC fourcc_mod_code(INTEL, 12) + +/* + * Intel Color Control Surfaces (CCS) for display ver. 14 render compression. + * + * The main surface is tile4 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 + * tile4 widths. + */ +#define I915_FORMAT_MOD_4_TILED_MTL_RC_CCS fourcc_mod_code(INTEL, 13) + +/* + * Intel Color Control Surfaces (CCS) for display ver. 14 media compression + * + * The main surface is tile4 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 + * tile4 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_4_TILED_MTL_MC_CCS fourcc_mod_code(INTEL, 14) + +/* + * Intel Color Control Surface with Clear Color (CCS) for display ver. 14 render + * compression. + * + * The main surface is tile4 and is at plane index 0 whereas CCS is linear + * and at index 1. The clear color is stored at index 2, and the pitch should + * be ignored. The clear color structure is 256 bits. The first 128 bits + * represents Raw Clear Color Red, Green, Blue and Alpha color each represented + * by 32 bits. The raw clear color is consumed by the 3d engine and generates + * the converted clear color of size 64 bits. The first 32 bits store the Lower + * Converted Clear Color value and the next 32 bits store the Higher Converted + * Clear Color value when applicable. The Converted Clear Color values are + * consumed by the DE. The last 64 bits are used to store Color Discard Enable + * and Depth Clear Value Valid which are ignored by the DE. A CCS cache line + * corresponds to an area of 4x1 tiles in the main surface. The main surface + * pitch is required to be a multiple of 4 tile widths. + */ +#define I915_FORMAT_MOD_4_TILED_MTL_RC_CCS_CC fourcc_mod_code(INTEL, 15) + /* * Tiled, NV12MT, grouped in 64 (pixels) x 32 (lines) -sized macroblocks * @@ -608,6 +738,28 @@ extern "C" { */ #define DRM_FORMAT_MOD_QCOM_COMPRESSED fourcc_mod_code(QCOM, 1) +/* + * Qualcomm Tiled Format + * + * Similar to DRM_FORMAT_MOD_QCOM_COMPRESSED but not compressed. + * Implementation may be platform and base-format specific. + * + * Each macrotile consists of m x n (mostly 4 x 4) tiles. + * Pixel data pitch/stride is aligned with macrotile width. + * Pixel data height is aligned with macrotile height. + * Entire pixel data buffer is aligned with 4k(bytes). + */ +#define DRM_FORMAT_MOD_QCOM_TILED3 fourcc_mod_code(QCOM, 3) + +/* + * Qualcomm Alternate Tiled Format + * + * Alternate tiled format typically only used within GMEM. + * Implementation may be platform and base-format specific. + */ +#define DRM_FORMAT_MOD_QCOM_TILED2 fourcc_mod_code(QCOM, 2) + + /* Vivante framebuffer modifiers */ /* @@ -648,6 +800,35 @@ extern "C" { */ #define DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED fourcc_mod_code(VIVANTE, 4) +/* + * Vivante TS (tile-status) buffer modifiers. They can be combined with all of + * the color buffer tiling modifiers defined above. When TS is present it's a + * separate buffer containing the clear/compression status of each tile. The + * modifiers are defined as VIVANTE_MOD_TS_c_s, where c is the color buffer + * tile size in bytes covered by one entry in the status buffer and s is the + * number of status bits per entry. + * We reserve the top 8 bits of the Vivante modifier space for tile status + * clear/compression modifiers, as future cores might add some more TS layout + * variations. + */ +#define VIVANTE_MOD_TS_64_4 (1ULL << 48) +#define VIVANTE_MOD_TS_64_2 (2ULL << 48) +#define VIVANTE_MOD_TS_128_4 (3ULL << 48) +#define VIVANTE_MOD_TS_256_4 (4ULL << 48) +#define VIVANTE_MOD_TS_MASK (0xfULL << 48) + +/* + * Vivante compression modifiers. Those depend on a TS modifier being present + * as the TS bits get reinterpreted as compression tags instead of simple + * clear markers when compression is enabled. + */ +#define VIVANTE_MOD_COMP_DEC400 (1ULL << 52) +#define VIVANTE_MOD_COMP_MASK (0xfULL << 52) + +/* Masking out the extension bits will yield the base modifier. */ +#define VIVANTE_MOD_EXT_MASK (VIVANTE_MOD_TS_MASK | \ + VIVANTE_MOD_COMP_MASK) + /* NVIDIA frame buffer modifiers */ /* @@ -921,7 +1102,7 @@ drm_fourcc_canonicalize_nvidia_format_mod(uint64_t modifier) */ /* - * The top 4 bits (out of the 56 bits alloted for specifying vendor specific + * The top 4 bits (out of the 56 bits allotted for specifying vendor specific * modifiers) denote the category for modifiers. Currently we have three * categories of modifiers ie AFBC, MISC and AFRC. We can have a maximum of * sixteen different categories. @@ -1237,7 +1418,7 @@ drm_fourcc_canonicalize_nvidia_format_mod(uint64_t modifier) * Amlogic FBC Memory Saving mode * * Indicates the storage is packed when pixel size is multiple of word - * boudaries, i.e. 8bit should be stored in this mode to save allocation + * boundaries, i.e. 8bit should be stored in this mode to save allocation * memory. * * This mode reduces body layout to 3072 bytes per 64x32 superblock with @@ -1293,6 +1474,7 @@ drm_fourcc_canonicalize_nvidia_format_mod(uint64_t modifier) #define AMD_FMT_MOD_TILE_VER_GFX9 1 #define AMD_FMT_MOD_TILE_VER_GFX10 2 #define AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS 3 +#define AMD_FMT_MOD_TILE_VER_GFX11 4 /* * 64K_S is the same for GFX9/GFX10/GFX10_RBPLUS and hence has GFX9 as canonical @@ -1308,6 +1490,7 @@ drm_fourcc_canonicalize_nvidia_format_mod(uint64_t modifier) #define AMD_FMT_MOD_TILE_GFX9_64K_S_X 25 #define AMD_FMT_MOD_TILE_GFX9_64K_D_X 26 #define AMD_FMT_MOD_TILE_GFX9_64K_R_X 27 +#define AMD_FMT_MOD_TILE_GFX11_256K_R_X 31 #define AMD_FMT_MOD_DCC_BLOCK_64B 0 #define AMD_FMT_MOD_DCC_BLOCK_128B 1 diff --git a/include/standard-headers/linux/const.h b/include/standard-headers/linux/const.h index 5e48987251..1eb84b5087 100644 --- a/include/standard-headers/linux/const.h +++ b/include/standard-headers/linux/const.h @@ -28,7 +28,7 @@ #define _BITUL(x) (_UL(1) << (x)) #define _BITULL(x) (_ULL(1) << (x)) -#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) +#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (__typeof__(x))(a) - 1) #define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) #define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index 38d5a4cd6e..dfb54eff6f 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -159,8 +159,10 @@ static inline uint32_t ethtool_cmd_speed(const struct ethtool_cmd *ep) * in its bus driver structure (e.g. pci_driver::name). Must * not be an empty string. * @version: Driver version string; may be an empty string - * @fw_version: Firmware version string; may be an empty string - * @erom_version: Expansion ROM version string; may be an empty string + * @fw_version: Firmware version string; driver defined; may be an + * empty string + * @erom_version: Expansion ROM version string; driver defined; may be + * an empty string * @bus_info: Device bus address. This should match the dev_name() * string for the underlying bus device, if there is one. May be * an empty string. @@ -179,10 +181,6 @@ static inline uint32_t ethtool_cmd_speed(const struct ethtool_cmd *ep) * * Users can use the %ETHTOOL_GSSET_INFO command to get the number of * strings in any string set (from Linux 2.6.34). - * - * Drivers should set at most @driver, @version, @fw_version and - * @bus_info in their get_drvinfo() implementation. The ethtool - * core fills in the other fields using other driver operations. */ struct ethtool_drvinfo { uint32_t cmd; @@ -257,7 +255,7 @@ struct ethtool_tunable { uint32_t id; uint32_t type_id; uint32_t len; - void *data[0]; + void *data[]; }; #define DOWNSHIFT_DEV_DEFAULT_COUNT 0xff @@ -322,7 +320,7 @@ struct ethtool_regs { uint32_t cmd; uint32_t version; uint32_t len; - uint8_t data[0]; + uint8_t data[]; }; /** @@ -348,7 +346,7 @@ struct ethtool_eeprom { uint32_t magic; uint32_t offset; uint32_t len; - uint8_t data[0]; + uint8_t data[]; }; /** @@ -713,6 +711,24 @@ enum ethtool_stringset { ETH_SS_COUNT }; +/** + * enum ethtool_mac_stats_src - source of ethtool MAC statistics + * @ETHTOOL_MAC_STATS_SRC_AGGREGATE: + * if device supports a MAC merge layer, this retrieves the aggregate + * statistics of the eMAC and pMAC. Otherwise, it retrieves just the + * statistics of the single (express) MAC. + * @ETHTOOL_MAC_STATS_SRC_EMAC: + * if device supports a MM layer, this retrieves the eMAC statistics. + * Otherwise, it retrieves the statistics of the single (express) MAC. + * @ETHTOOL_MAC_STATS_SRC_PMAC: + * if device supports a MM layer, this retrieves the pMAC statistics. + */ +enum ethtool_mac_stats_src { + ETHTOOL_MAC_STATS_SRC_AGGREGATE, + ETHTOOL_MAC_STATS_SRC_EMAC, + ETHTOOL_MAC_STATS_SRC_PMAC, +}; + /** * enum ethtool_module_power_mode_policy - plug-in module power mode policy * @ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH: Module is always in high power mode. @@ -736,6 +752,76 @@ enum ethtool_module_power_mode { ETHTOOL_MODULE_POWER_MODE_HIGH, }; +/** + * enum ethtool_podl_pse_admin_state - operational state of the PoDL PSE + * functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState + * @ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN: state of PoDL PSE functions are + * unknown + * @ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED: PoDL PSE functions are disabled + * @ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED: PoDL PSE functions are enabled + */ +enum ethtool_podl_pse_admin_state { + ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN = 1, + ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED, + ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED, +}; + +/** + * enum ethtool_podl_pse_pw_d_status - power detection status of the PoDL PSE. + * IEEE 802.3-2018 30.15.1.1.3 aPoDLPSEPowerDetectionStatus: + * @ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN: PoDL PSE + * @ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED: "The enumeration “disabled” is + * asserted true when the PoDL PSE state diagram variable mr_pse_enable is + * false" + * @ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING: "The enumeration “searching” is + * asserted true when either of the PSE state diagram variables + * pi_detecting or pi_classifying is true." + * @ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING: "The enumeration “deliveringPower” + * is asserted true when the PoDL PSE state diagram variable pi_powered is + * true." + * @ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP: "The enumeration “sleep” is asserted + * true when the PoDL PSE state diagram variable pi_sleeping is true." + * @ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE: "The enumeration “idle” is asserted true + * when the logical combination of the PoDL PSE state diagram variables + * pi_prebiased*!pi_sleeping is true." + * @ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR: "The enumeration “error” is asserted + * true when the PoDL PSE state diagram variable overload_held is true." + */ +enum ethtool_podl_pse_pw_d_status { + ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN = 1, + ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED, + ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING, + ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING, + ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP, + ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE, + ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR, +}; + +/** + * enum ethtool_mm_verify_status - status of MAC Merge Verify function + * @ETHTOOL_MM_VERIFY_STATUS_UNKNOWN: + * verification status is unknown + * @ETHTOOL_MM_VERIFY_STATUS_INITIAL: + * the 802.3 Verify State diagram is in the state INIT_VERIFICATION + * @ETHTOOL_MM_VERIFY_STATUS_VERIFYING: + * the Verify State diagram is in the state VERIFICATION_IDLE, + * SEND_VERIFY or WAIT_FOR_RESPONSE + * @ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED: + * indicates that the Verify State diagram is in the state VERIFIED + * @ETHTOOL_MM_VERIFY_STATUS_FAILED: + * the Verify State diagram is in the state VERIFY_FAIL + * @ETHTOOL_MM_VERIFY_STATUS_DISABLED: + * verification of preemption operation is disabled + */ +enum ethtool_mm_verify_status { + ETHTOOL_MM_VERIFY_STATUS_UNKNOWN, + ETHTOOL_MM_VERIFY_STATUS_INITIAL, + ETHTOOL_MM_VERIFY_STATUS_VERIFYING, + ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED, + ETHTOOL_MM_VERIFY_STATUS_FAILED, + ETHTOOL_MM_VERIFY_STATUS_DISABLED, +}; + /** * struct ethtool_gstrings - string set for data tagging * @cmd: Command number = %ETHTOOL_GSTRINGS @@ -752,7 +838,7 @@ struct ethtool_gstrings { uint32_t cmd; uint32_t string_set; uint32_t len; - uint8_t data[0]; + uint8_t data[]; }; /** @@ -777,7 +863,7 @@ struct ethtool_sset_info { uint32_t cmd; uint32_t reserved; uint64_t sset_mask; - uint32_t data[0]; + uint32_t data[]; }; /** @@ -817,7 +903,7 @@ struct ethtool_test { uint32_t flags; uint32_t reserved; uint32_t len; - uint64_t data[0]; + uint64_t data[]; }; /** @@ -834,7 +920,7 @@ struct ethtool_test { struct ethtool_stats { uint32_t cmd; uint32_t n_stats; - uint64_t data[0]; + uint64_t data[]; }; /** @@ -851,7 +937,7 @@ struct ethtool_stats { struct ethtool_perm_addr { uint32_t cmd; uint32_t size; - uint8_t data[0]; + uint8_t data[]; }; /* boolean flags controlling per-interface behavior characteristics. @@ -1140,7 +1226,7 @@ struct ethtool_rxnfc { uint32_t rule_cnt; uint32_t rss_context; }; - uint32_t rule_locs[0]; + uint32_t rule_locs[]; }; @@ -1160,7 +1246,7 @@ struct ethtool_rxnfc { struct ethtool_rxfh_indir { uint32_t cmd; uint32_t size; - uint32_t ring_index[0]; + uint32_t ring_index[]; }; /** @@ -1180,6 +1266,8 @@ struct ethtool_rxfh_indir { * hardware hash key. * @hfunc: Defines the current RSS hash function used by HW (or to be set to). * Valid values are one of the %ETH_RSS_HASH_*. + * @input_xfrm: Defines how the input data is transformed. Valid values are one + * of %RXH_XFRM_*. * @rsvd8: Reserved for future use; see the note on reserved space. * @rsvd32: Reserved for future use; see the note on reserved space. * @rss_config: RX ring/queue index for each hash value i.e., indirection table @@ -1199,9 +1287,10 @@ struct ethtool_rxfh { uint32_t indir_size; uint32_t key_size; uint8_t hfunc; - uint8_t rsvd8[3]; + uint8_t input_xfrm; + uint8_t rsvd8[2]; uint32_t rsvd32; - uint32_t rss_config[0]; + uint32_t rss_config[]; }; #define ETH_RXFH_CONTEXT_ALLOC 0xffffffff #define ETH_RXFH_INDIR_NO_CHANGE 0xffffffff @@ -1286,7 +1375,7 @@ struct ethtool_dump { uint32_t version; uint32_t flag; uint32_t len; - uint8_t data[0]; + uint8_t data[]; }; #define ETH_FW_DUMP_DISABLE 0 @@ -1318,7 +1407,7 @@ struct ethtool_get_features_block { struct ethtool_gfeatures { uint32_t cmd; uint32_t size; - struct ethtool_get_features_block features[0]; + struct ethtool_get_features_block features[]; }; /** @@ -1340,7 +1429,7 @@ struct ethtool_set_features_block { struct ethtool_sfeatures { uint32_t cmd; uint32_t size; - struct ethtool_set_features_block features[0]; + struct ethtool_set_features_block features[]; }; /** @@ -1691,6 +1780,17 @@ enum ethtool_link_mode_bit_indices { ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT = 89, ETHTOOL_LINK_MODE_100baseFX_Half_BIT = 90, ETHTOOL_LINK_MODE_100baseFX_Full_BIT = 91, + ETHTOOL_LINK_MODE_10baseT1L_Full_BIT = 92, + ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT = 93, + ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT = 94, + ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT = 95, + ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT = 96, + ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT = 97, + ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT = 98, + ETHTOOL_LINK_MODE_10baseT1S_Full_BIT = 99, + ETHTOOL_LINK_MODE_10baseT1S_Half_BIT = 100, + ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT = 101, + /* must be last entry */ __ETHTOOL_LINK_MODE_MASK_NBITS }; @@ -1802,6 +1902,7 @@ enum ethtool_link_mode_bit_indices { #define SPEED_100000 100000 #define SPEED_200000 200000 #define SPEED_400000 400000 +#define SPEED_800000 800000 #define SPEED_UNKNOWN -1 @@ -1839,6 +1940,20 @@ static inline int ethtool_validate_duplex(uint8_t duplex) #define MASTER_SLAVE_STATE_SLAVE 3 #define MASTER_SLAVE_STATE_ERR 4 +/* These are used to throttle the rate of data on the phy interface when the + * native speed of the interface is higher than the link speed. These should + * not be used for phy interfaces which natively support multiple speeds (e.g. + * MII or SGMII). + */ +/* No rate matching performed. */ +#define RATE_MATCH_NONE 0 +/* The phy sends pause frames to throttle the MAC. */ +#define RATE_MATCH_PAUSE 1 +/* The phy asserts CRS to prevent the MAC from transmitting. */ +#define RATE_MATCH_CRS 2 +/* The MAC is programmed with a sufficiently-large IPG. */ +#define RATE_MATCH_OPEN_LOOP 3 + /* Which connector port. */ #define PORT_TP 0x00 #define PORT_AUI 0x01 @@ -1880,6 +1995,15 @@ static inline int ethtool_validate_duplex(uint8_t duplex) #define WOL_MODE_COUNT 8 +/* RSS hash function data + * XOR the corresponding source and destination fields of each specified + * protocol. Both copies of the XOR'ed fields are fed into the RSS and RXHASH + * calculation. Note that this XORing reduces the input set entropy and could + * be exploited to reduce the RSS queue spread. + */ +#define RXH_XFRM_SYM_XOR (1 << 0) +#define RXH_XFRM_NO_CHANGE 0xff + /* 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) */ @@ -2016,24 +2140,12 @@ enum ethtool_reset_flags { * refused. For drivers: ignore this field (use kernel's * __ETHTOOL_LINK_MODE_MASK_NBITS instead), any change to it will * be overwritten by kernel. - * @supported: Bitmap with each bit meaning given by - * %ethtool_link_mode_bit_indices for the link modes, physical - * connectors and other link features for which the interface - * supports autonegotiation or auto-detection. Read-only. - * @advertising: Bitmap with each bit meaning given by - * %ethtool_link_mode_bit_indices for the link modes, physical - * connectors and other link features that are advertised through - * autonegotiation or enabled for auto-detection. - * @lp_advertising: Bitmap with each bit meaning given by - * %ethtool_link_mode_bit_indices for the link modes, and other - * link features that the link partner advertised through - * autonegotiation; 0 if unknown or not applicable. Read-only. * @transceiver: Used to distinguish different possible PHY types, * reported consistently by PHYLIB. Read-only. * @master_slave_cfg: Master/slave port mode. * @master_slave_state: Master/slave port state. + * @rate_matching: Rate adaptation performed by the PHY * @reserved: Reserved for future use; see the note on reserved space. - * @reserved1: Reserved for future use; see the note on reserved space. * @link_mode_masks: Variable length bitmaps. * * If autonegotiation is disabled, the speed and @duplex represent the @@ -2069,6 +2181,21 @@ enum ethtool_reset_flags { * %set_link_ksettings() should validate all fields other than @cmd * and @link_mode_masks_nwords that are not described as read-only or * deprecated, and must ignore all fields described as read-only. + * + * @link_mode_masks is divided into three bitfields, each of length + * @link_mode_masks_nwords: + * - supported: Bitmap with each bit meaning given by + * %ethtool_link_mode_bit_indices for the link modes, physical + * connectors and other link features for which the interface + * supports autonegotiation or auto-detection. Read-only. + * - advertising: Bitmap with each bit meaning given by + * %ethtool_link_mode_bit_indices for the link modes, physical + * connectors and other link features that are advertised through + * autonegotiation or enabled for auto-detection. + * - lp_advertising: Bitmap with each bit meaning given by + * %ethtool_link_mode_bit_indices for the link modes, and other + * link features that the link partner advertised through + * autonegotiation; 0 if unknown or not applicable. Read-only. */ struct ethtool_link_settings { uint32_t cmd; @@ -2084,9 +2211,9 @@ struct ethtool_link_settings { uint8_t transceiver; uint8_t master_slave_cfg; uint8_t master_slave_state; - uint8_t reserved1[1]; + uint8_t rate_matching; uint32_t reserved[7]; - uint32_t link_mode_masks[0]; + uint32_t link_mode_masks[]; /* layout of link_mode_masks fields: * uint32_t map_supported[link_mode_masks_nwords]; * uint32_t map_advertising[link_mode_masks_nwords]; diff --git a/include/standard-headers/linux/fuse.h b/include/standard-headers/linux/fuse.h index bda06258be..fc0dcd10ae 100644 --- a/include/standard-headers/linux/fuse.h +++ b/include/standard-headers/linux/fuse.h @@ -194,6 +194,23 @@ * - add FUSE_SECURITY_CTX init flag * - add security context to create, mkdir, symlink, and mknod requests * - add FUSE_HAS_INODE_DAX, FUSE_ATTR_DAX + * + * 7.37 + * - add FUSE_TMPFILE + * + * 7.38 + * - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry + * - add FOPEN_PARALLEL_DIRECT_WRITES + * - add total_extlen to fuse_in_header + * - add FUSE_MAX_NR_SECCTX + * - add extension header + * - add FUSE_EXT_GROUPS + * - add FUSE_CREATE_SUPP_GROUP + * - add FUSE_HAS_EXPIRE_ONLY + * + * 7.39 + * - add FUSE_DIRECT_IO_ALLOW_MMAP + * - add FUSE_STATX and related structures */ #ifndef _LINUX_FUSE_H @@ -225,7 +242,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 36 +#define FUSE_KERNEL_MINOR_VERSION 39 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -252,6 +269,40 @@ struct fuse_attr { uint32_t flags; }; +/* + * The following structures are bit-for-bit compatible with the statx(2) ABI in + * Linux. + */ +struct fuse_sx_time { + int64_t tv_sec; + uint32_t tv_nsec; + int32_t __reserved; +}; + +struct fuse_statx { + uint32_t mask; + uint32_t blksize; + uint64_t attributes; + uint32_t nlink; + uint32_t uid; + uint32_t gid; + uint16_t mode; + uint16_t __spare0[1]; + uint64_t ino; + uint64_t size; + uint64_t blocks; + uint64_t attributes_mask; + struct fuse_sx_time atime; + struct fuse_sx_time btime; + struct fuse_sx_time ctime; + struct fuse_sx_time mtime; + uint32_t rdev_major; + uint32_t rdev_minor; + uint32_t dev_major; + uint32_t dev_minor; + uint64_t __spare2[14]; +}; + struct fuse_kstatfs { uint64_t blocks; uint64_t bfree; @@ -297,6 +348,7 @@ struct fuse_file_lock { * FOPEN_CACHE_DIR: allow caching this directory * FOPEN_STREAM: the file is stream-like (no file position at all) * FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE) + * FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode */ #define FOPEN_DIRECT_IO (1 << 0) #define FOPEN_KEEP_CACHE (1 << 1) @@ -304,6 +356,7 @@ struct fuse_file_lock { #define FOPEN_CACHE_DIR (1 << 3) #define FOPEN_STREAM (1 << 4) #define FOPEN_NOFLUSH (1 << 5) +#define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6) /** * INIT request/reply flags @@ -349,6 +402,10 @@ struct fuse_file_lock { * FUSE_SECURITY_CTX: add security context to create, mkdir, symlink, and * mknod * FUSE_HAS_INODE_DAX: use per inode DAX + * FUSE_CREATE_SUPP_GROUP: add supplementary group info to create, mkdir, + * symlink and mknod (single group that matches parent) + * FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation + * FUSE_DIRECT_IO_ALLOW_MMAP: allow shared mmap in FOPEN_DIRECT_IO mode. */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) @@ -385,6 +442,12 @@ struct fuse_file_lock { /* bits 32..63 get shifted down 32 bits into the flags2 field */ #define FUSE_SECURITY_CTX (1ULL << 32) #define FUSE_HAS_INODE_DAX (1ULL << 33) +#define FUSE_CREATE_SUPP_GROUP (1ULL << 34) +#define FUSE_HAS_EXPIRE_ONLY (1ULL << 35) +#define FUSE_DIRECT_IO_ALLOW_MMAP (1ULL << 36) + +/* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */ +#define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP /** * CUSE INIT request/reply flags @@ -484,6 +547,23 @@ struct fuse_file_lock { */ #define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0) +/** + * notify_inval_entry flags + * FUSE_EXPIRE_ONLY + */ +#define FUSE_EXPIRE_ONLY (1 << 0) + +/** + * extension type + * FUSE_MAX_NR_SECCTX: maximum value of &fuse_secctx_header.nr_secctx + * FUSE_EXT_GROUPS: &fuse_supp_groups extension + */ +enum fuse_ext_type { + /* Types 0..31 are reserved for fuse_secctx_header */ + FUSE_MAX_NR_SECCTX = 31, + FUSE_EXT_GROUPS = 32, +}; + enum fuse_opcode { FUSE_LOOKUP = 1, FUSE_FORGET = 2, /* no reply */ @@ -533,6 +613,8 @@ enum fuse_opcode { FUSE_SETUPMAPPING = 48, FUSE_REMOVEMAPPING = 49, FUSE_SYNCFS = 50, + FUSE_TMPFILE = 51, + FUSE_STATX = 52, /* CUSE specific operations */ CUSE_INIT = 4096, @@ -597,6 +679,22 @@ struct fuse_attr_out { struct fuse_attr attr; }; +struct fuse_statx_in { + uint32_t getattr_flags; + uint32_t reserved; + uint64_t fh; + uint32_t sx_flags; + uint32_t sx_mask; +}; + +struct fuse_statx_out { + uint64_t attr_valid; /* Cache timeout for the attributes */ + uint32_t attr_valid_nsec; + uint32_t flags; + uint64_t spare[2]; + struct fuse_statx stat; +}; + #define FUSE_COMPAT_MKNOD_IN_SIZE 8 struct fuse_mknod_in { @@ -866,7 +964,8 @@ struct fuse_in_header { uint32_t uid; uint32_t gid; uint32_t pid; - uint32_t padding; + uint16_t total_extlen; /* length of extensions in 8byte units */ + uint16_t padding; }; struct fuse_out_header { @@ -911,7 +1010,7 @@ struct fuse_notify_inval_inode_out { struct fuse_notify_inval_entry_out { uint64_t parent; uint32_t namelen; - uint32_t padding; + uint32_t flags; }; struct fuse_notify_delete_out { @@ -1027,4 +1126,27 @@ struct fuse_secctx_header { uint32_t nr_secctx; }; +/** + * struct fuse_ext_header - extension header + * @size: total size of this extension including this header + * @type: type of extension + * + * This is made compatible with fuse_secctx_header by using type values > + * FUSE_MAX_NR_SECCTX + */ +struct fuse_ext_header { + uint32_t size; + uint32_t type; +}; + +/** + * struct fuse_supp_groups - Supplementary group extension + * @nr_groups: number of supplementary groups + * @groups: flexible array of group IDs + */ +struct fuse_supp_groups { + uint32_t nr_groups; + uint32_t groups[]; +}; + #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 50790aee5a..f6bab08540 100644 --- a/include/standard-headers/linux/input-event-codes.h +++ b/include/standard-headers/linux/input-event-codes.h @@ -614,6 +614,9 @@ #define KEY_KBD_LAYOUT_NEXT 0x248 /* AC Next Keyboard Layout Select */ #define KEY_EMOJI_PICKER 0x249 /* Show/hide emoji picker (HUTRR101) */ #define KEY_DICTATE 0x24a /* Start or Stop Voice Dictation Session (HUTRR99) */ +#define KEY_CAMERA_ACCESS_ENABLE 0x24b /* Enables programmatic access to camera devices. (HUTRR72) */ +#define KEY_CAMERA_ACCESS_DISABLE 0x24c /* Disables programmatic access to camera devices. (HUTRR72) */ +#define KEY_CAMERA_ACCESS_TOGGLE 0x24d /* Toggles the current state of the camera access control. (HUTRR72) */ #define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ #define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ @@ -862,6 +865,7 @@ #define ABS_TOOL_WIDTH 0x1c #define ABS_VOLUME 0x20 +#define ABS_PROFILE 0x21 #define ABS_MISC 0x28 diff --git a/include/standard-headers/linux/input.h b/include/standard-headers/linux/input.h index 7822c24178..942ea6aaa9 100644 --- a/include/standard-headers/linux/input.h +++ b/include/standard-headers/linux/input.h @@ -75,10 +75,13 @@ struct input_id { * Note that input core does not clamp reported values to the * [minimum, maximum] limits, such task is left to userspace. * - * The default resolution for main axes (ABS_X, ABS_Y, ABS_Z) - * is reported in units per millimeter (units/mm), resolution - * for rotational axes (ABS_RX, ABS_RY, ABS_RZ) is reported - * in units per radian. + * The default resolution for main axes (ABS_X, ABS_Y, ABS_Z, + * ABS_MT_POSITION_X, ABS_MT_POSITION_Y) is reported in units + * per millimeter (units/mm), resolution for rotational axes + * (ABS_RX, ABS_RY, ABS_RZ) is reported in units per radian. + * The resolution for the size axes (ABS_MT_TOUCH_MAJOR, + * ABS_MT_TOUCH_MINOR, ABS_MT_WIDTH_MAJOR, ABS_MT_WIDTH_MINOR) + * is reported in units per millimeter (units/mm). * When INPUT_PROP_ACCELEROMETER is set the resolution changes. * The main axes (ABS_X, ABS_Y, ABS_Z) are then reported in * units per g (units/g) and in units per degree per second @@ -268,6 +271,7 @@ struct input_mask { #define BUS_RMI 0x1D #define BUS_CEC 0x1E #define BUS_INTEL_ISHTP 0x1F +#define BUS_AMD_SFH 0x20 /* * MT_TOOL types diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index bee1a9ed6e..a39193213f 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -80,6 +80,7 @@ #define PCI_HEADER_TYPE_NORMAL 0 #define PCI_HEADER_TYPE_BRIDGE 1 #define PCI_HEADER_TYPE_CARDBUS 2 +#define PCI_HEADER_TYPE_MFD 0x80 /* Multi-Function Device (possible) */ #define PCI_BIST 0x0f /* 8 bits */ #define PCI_BIST_CODE_MASK 0x0f /* Return result */ @@ -616,6 +617,7 @@ #define PCI_EXP_SLTCTL_PWR_OFF 0x0400 /* Power Off */ #define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */ #define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */ +#define PCI_EXP_SLTCTL_ASPL_DISABLE 0x2000 /* Auto Slot Power Limit Disable */ #define PCI_EXP_SLTCTL_IBPD_DISABLE 0x4000 /* In-band PD disable */ #define PCI_EXP_SLTSTA 0x1a /* Slot Status */ #define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */ @@ -636,6 +638,7 @@ #define PCI_EXP_RTCAP 0x1e /* Root Capabilities */ #define PCI_EXP_RTCAP_CRSVIS 0x0001 /* CRS Software Visibility capability */ #define PCI_EXP_RTSTA 0x20 /* Root Status */ +#define PCI_EXP_RTSTA_PME_RQ_ID 0x0000ffff /* PME Requester ID */ #define PCI_EXP_RTSTA_PME 0x00010000 /* PME status */ #define PCI_EXP_RTSTA_PENDING 0x00020000 /* PME pending */ /* @@ -692,6 +695,7 @@ #define PCI_EXP_LNKCTL2_TX_MARGIN 0x0380 /* Transmit Margin */ #define PCI_EXP_LNKCTL2_HASD 0x0020 /* HW Autonomous Speed Disable */ #define PCI_EXP_LNKSTA2 0x32 /* Link Status 2 */ +#define PCI_EXP_LNKSTA2_FLIT 0x0400 /* Flit Mode Status */ #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 0x32 /* end of v2 EPs w/ link */ #define PCI_EXP_SLTCAP2 0x34 /* Slot Capabilities 2 */ #define PCI_EXP_SLTCAP2_IBPD 0x00000001 /* In-band PD Disable Supported */ @@ -736,7 +740,9 @@ #define PCI_EXT_CAP_ID_DVSEC 0x23 /* Designated Vendor-Specific */ #define PCI_EXT_CAP_ID_DLF 0x25 /* Data Link Feature */ #define PCI_EXT_CAP_ID_PL_16GT 0x26 /* Physical Layer 16.0 GT/s */ -#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_16GT +#define PCI_EXT_CAP_ID_PL_32GT 0x2A /* Physical Layer 32.0 GT/s */ +#define PCI_EXT_CAP_ID_DOE 0x2E /* Data Object Exchange */ +#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_DOE #define PCI_EXT_CAP_DSN_SIZEOF 12 #define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40 @@ -926,12 +932,13 @@ /* Process Address Space ID */ #define PCI_PASID_CAP 0x04 /* PASID feature register */ -#define PCI_PASID_CAP_EXEC 0x02 /* Exec permissions Supported */ -#define PCI_PASID_CAP_PRIV 0x04 /* Privilege Mode Supported */ +#define PCI_PASID_CAP_EXEC 0x0002 /* Exec permissions Supported */ +#define PCI_PASID_CAP_PRIV 0x0004 /* Privilege Mode Supported */ +#define PCI_PASID_CAP_WIDTH 0x1f00 #define PCI_PASID_CTRL 0x06 /* PASID control register */ -#define PCI_PASID_CTRL_ENABLE 0x01 /* Enable bit */ -#define PCI_PASID_CTRL_EXEC 0x02 /* Exec permissions Enable */ -#define PCI_PASID_CTRL_PRIV 0x04 /* Privilege Mode Enable */ +#define PCI_PASID_CTRL_ENABLE 0x0001 /* Enable bit */ +#define PCI_PASID_CTRL_EXEC 0x0002 /* Exec permissions Enable */ +#define PCI_PASID_CTRL_PRIV 0x0004 /* Privilege Mode Enable */ #define PCI_EXT_CAP_PASID_SIZEOF 8 /* Single Root I/O Virtualization */ @@ -971,6 +978,8 @@ #define PCI_LTR_VALUE_MASK 0x000003ff #define PCI_LTR_SCALE_MASK 0x00001c00 #define PCI_LTR_SCALE_SHIFT 10 +#define PCI_LTR_NOSNOOP_VALUE 0x03ff0000 /* Max No-Snoop Latency Value */ +#define PCI_LTR_NOSNOOP_SCALE 0x1c000000 /* Scale for Max Value */ #define PCI_EXT_CAP_LTR_SIZEOF 8 /* Access Control Service */ @@ -1038,9 +1047,16 @@ #define PCI_EXP_DPC_STATUS 0x08 /* DPC Status */ #define PCI_EXP_DPC_STATUS_TRIGGER 0x0001 /* Trigger Status */ #define PCI_EXP_DPC_STATUS_TRIGGER_RSN 0x0006 /* Trigger Reason */ +#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_UNCOR 0x0000 /* Uncorrectable error */ +#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_NFE 0x0002 /* Rcvd ERR_NONFATAL */ +#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_FE 0x0004 /* Rcvd ERR_FATAL */ +#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_IN_EXT 0x0006 /* Reason in Trig Reason Extension field */ #define PCI_EXP_DPC_STATUS_INTERRUPT 0x0008 /* Interrupt Status */ #define PCI_EXP_DPC_RP_BUSY 0x0010 /* Root Port Busy */ #define PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT 0x0060 /* Trig Reason Extension */ +#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_RP_PIO 0x0000 /* RP PIO error */ +#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_SW_TRIGGER 0x0020 /* DPC SW Trigger bit */ +#define PCI_EXP_DPC_RP_PIO_FEP 0x1f00 /* RP PIO First Err Ptr */ #define PCI_EXP_DPC_SOURCE_ID 0x0A /* DPC Source Identifier */ @@ -1056,6 +1072,7 @@ /* Precision Time Measurement */ #define PCI_PTM_CAP 0x04 /* PTM Capability */ #define PCI_PTM_CAP_REQ 0x00000001 /* Requester capable */ +#define PCI_PTM_CAP_RES 0x00000002 /* Responder capable */ #define PCI_PTM_CAP_ROOT 0x00000004 /* Root capable */ #define PCI_PTM_GRANULARITY_MASK 0x0000FF00 /* Clock granularity */ #define PCI_PTM_CTRL 0x08 /* PTM Control */ @@ -1083,6 +1100,8 @@ #define PCI_L1SS_CTL1_LTR_L12_TH_VALUE 0x03ff0000 /* LTR_L1.2_THRESHOLD_Value */ #define PCI_L1SS_CTL1_LTR_L12_TH_SCALE 0xe0000000 /* LTR_L1.2_THRESHOLD_Scale */ #define PCI_L1SS_CTL2 0x0c /* Control 2 Register */ +#define PCI_L1SS_CTL2_T_PWR_ON_SCALE 0x00000003 /* T_POWER_ON Scale */ +#define PCI_L1SS_CTL2_T_PWR_ON_VALUE 0x000000f8 /* T_POWER_ON Value */ /* Designated Vendor-Specific (DVSEC, PCI_EXT_CAP_ID_DVSEC) */ #define PCI_DVSEC_HEADER1 0x4 /* Designated Vendor-Specific Header1 */ @@ -1102,4 +1121,31 @@ #define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_MASK 0x000000F0 #define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_SHIFT 4 +/* Data Object Exchange */ +#define PCI_DOE_CAP 0x04 /* DOE Capabilities Register */ +#define PCI_DOE_CAP_INT_SUP 0x00000001 /* Interrupt Support */ +#define PCI_DOE_CAP_INT_MSG_NUM 0x00000ffe /* Interrupt Message Number */ +#define PCI_DOE_CTRL 0x08 /* DOE Control Register */ +#define PCI_DOE_CTRL_ABORT 0x00000001 /* DOE Abort */ +#define PCI_DOE_CTRL_INT_EN 0x00000002 /* DOE Interrupt Enable */ +#define PCI_DOE_CTRL_GO 0x80000000 /* DOE Go */ +#define PCI_DOE_STATUS 0x0c /* DOE Status Register */ +#define PCI_DOE_STATUS_BUSY 0x00000001 /* DOE Busy */ +#define PCI_DOE_STATUS_INT_STATUS 0x00000002 /* DOE Interrupt Status */ +#define PCI_DOE_STATUS_ERROR 0x00000004 /* DOE Error */ +#define PCI_DOE_STATUS_DATA_OBJECT_READY 0x80000000 /* Data Object Ready */ +#define PCI_DOE_WRITE 0x10 /* DOE Write Data Mailbox Register */ +#define PCI_DOE_READ 0x14 /* DOE Read Data Mailbox Register */ +#define PCI_DOE_CAP_SIZEOF 0x18 /* Size of DOE register block */ + +/* DOE Data Object - note not actually registers */ +#define PCI_DOE_DATA_OBJECT_HEADER_1_VID 0x0000ffff +#define PCI_DOE_DATA_OBJECT_HEADER_1_TYPE 0x00ff0000 +#define PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH 0x0003ffff + +#define PCI_DOE_DATA_OBJECT_DISC_REQ_3_INDEX 0x000000ff +#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID 0x0000ffff +#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL 0x00ff0000 +#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX 0xff000000 + #endif /* LINUX_PCI_REGS_H */ diff --git a/include/standard-headers/linux/vhost_types.h b/include/standard-headers/linux/vhost_types.h index 0bd2684a2a..fd54044936 100644 --- a/include/standard-headers/linux/vhost_types.h +++ b/include/standard-headers/linux/vhost_types.h @@ -47,6 +47,22 @@ struct vhost_vring_addr { uint64_t log_guest_addr; }; +struct vhost_worker_state { + /* + * For VHOST_NEW_WORKER the kernel will return the new vhost_worker id. + * For VHOST_FREE_WORKER this must be set to the id of the vhost_worker + * to free. + */ + unsigned int worker_id; +}; + +struct vhost_vring_worker { + /* vring index */ + unsigned int index; + /* The id of the vhost_worker returned from VHOST_NEW_WORKER */ + unsigned int worker_id; +}; + /* no alignment requirement */ struct vhost_iotlb_msg { uint64_t iova; @@ -87,7 +103,7 @@ struct vhost_msg { struct vhost_msg_v2 { uint32_t type; - uint32_t reserved; + uint32_t asid; union { struct vhost_iotlb_msg iotlb; uint8_t padding[64]; @@ -107,7 +123,7 @@ struct vhost_memory_region { struct vhost_memory { uint32_t nregions; uint32_t padding; - struct vhost_memory_region regions[0]; + struct vhost_memory_region regions[]; }; /* VHOST_SCSI specific definitions */ @@ -135,7 +151,7 @@ struct vhost_scsi_target { struct vhost_vdpa_config { uint32_t off; uint32_t len; - uint8_t buf[0]; + uint8_t buf[]; }; /* vhost vdpa IOVA range @@ -153,4 +169,28 @@ struct vhost_vdpa_iova_range { /* vhost-net should add virtio_net_hdr for RX, and strip for TX packets. */ #define VHOST_NET_F_VIRTIO_NET_HDR 27 +/* Use message type V2 */ +#define VHOST_BACKEND_F_IOTLB_MSG_V2 0x1 +/* IOTLB can accept batching hints */ +#define VHOST_BACKEND_F_IOTLB_BATCH 0x2 +/* IOTLB can accept address space identifier through V2 type of IOTLB + * message + */ +#define VHOST_BACKEND_F_IOTLB_ASID 0x3 +/* Device can be suspended */ +#define VHOST_BACKEND_F_SUSPEND 0x4 +/* Device can be resumed */ +#define VHOST_BACKEND_F_RESUME 0x5 +/* Device supports the driver enabling virtqueues both before and after + * DRIVER_OK + */ +#define VHOST_BACKEND_F_ENABLE_AFTER_DRIVER_OK 0x6 +/* Device may expose the virtqueue's descriptor area, driver area and + * device area to a different group for ASID binding than where its + * buffers may reside. Requires VHOST_BACKEND_F_IOTLB_ASID. + */ +#define VHOST_BACKEND_F_DESC_ASID 0x7 +/* IOTLB don't flush memory mapping across device reset */ +#define VHOST_BACKEND_F_IOTLB_PERSIST 0x8 + #endif diff --git a/include/standard-headers/linux/virtio_9p.h b/include/standard-headers/linux/virtio_9p.h index f5604fc5fb..da61dee98c 100644 --- a/include/standard-headers/linux/virtio_9p.h +++ b/include/standard-headers/linux/virtio_9p.h @@ -38,7 +38,7 @@ struct virtio_9p_config { /* length of the tag name */ __virtio16 tag_len; /* non-NULL terminated tag name */ - uint8_t tag[0]; + uint8_t tag[]; } QEMU_PACKED; #endif /* _LINUX_VIRTIO_9P_H */ diff --git a/include/standard-headers/linux/virtio_blk.h b/include/standard-headers/linux/virtio_blk.h index 2dcc90826a..d7be3cf5e4 100644 --- a/include/standard-headers/linux/virtio_blk.h +++ b/include/standard-headers/linux/virtio_blk.h @@ -40,6 +40,8 @@ #define VIRTIO_BLK_F_MQ 12 /* support more than one vq */ #define VIRTIO_BLK_F_DISCARD 13 /* DISCARD is supported */ #define VIRTIO_BLK_F_WRITE_ZEROES 14 /* WRITE ZEROES is supported */ +#define VIRTIO_BLK_F_SECURE_ERASE 16 /* Secure Erase is supported */ +#define VIRTIO_BLK_F_ZONED 17 /* Zoned block device */ /* Legacy feature bits */ #ifndef VIRTIO_BLK_NO_LEGACY @@ -119,6 +121,31 @@ struct virtio_blk_config { uint8_t write_zeroes_may_unmap; uint8_t unused1[3]; + + /* the next 3 entries are guarded by VIRTIO_BLK_F_SECURE_ERASE */ + /* + * The maximum secure erase sectors (in 512-byte sectors) for + * one segment. + */ + __virtio32 max_secure_erase_sectors; + /* + * The maximum number of secure erase segments in a + * secure erase command. + */ + __virtio32 max_secure_erase_seg; + /* Secure erase commands must be aligned to this number of sectors. */ + __virtio32 secure_erase_sector_alignment; + + /* Zoned block device characteristics (if VIRTIO_BLK_F_ZONED) */ + struct virtio_blk_zoned_characteristics { + __virtio32 zone_sectors; + __virtio32 max_open_zones; + __virtio32 max_active_zones; + __virtio32 max_append_sectors; + __virtio32 write_granularity; + uint8_t model; + uint8_t unused2[3]; + } zoned; } QEMU_PACKED; /* @@ -153,6 +180,30 @@ struct virtio_blk_config { /* Write zeroes command */ #define VIRTIO_BLK_T_WRITE_ZEROES 13 +/* Secure erase command */ +#define VIRTIO_BLK_T_SECURE_ERASE 14 + +/* Zone append command */ +#define VIRTIO_BLK_T_ZONE_APPEND 15 + +/* Report zones command */ +#define VIRTIO_BLK_T_ZONE_REPORT 16 + +/* Open zone command */ +#define VIRTIO_BLK_T_ZONE_OPEN 18 + +/* Close zone command */ +#define VIRTIO_BLK_T_ZONE_CLOSE 20 + +/* Finish zone command */ +#define VIRTIO_BLK_T_ZONE_FINISH 22 + +/* Reset zone command */ +#define VIRTIO_BLK_T_ZONE_RESET 24 + +/* Reset All zones command */ +#define VIRTIO_BLK_T_ZONE_RESET_ALL 26 + #ifndef VIRTIO_BLK_NO_LEGACY /* Barrier before this op. */ #define VIRTIO_BLK_T_BARRIER 0x80000000 @@ -172,6 +223,72 @@ struct virtio_blk_outhdr { __virtio64 sector; }; +/* + * Supported zoned device models. + */ + +/* Regular block device */ +#define VIRTIO_BLK_Z_NONE 0 +/* Host-managed zoned device */ +#define VIRTIO_BLK_Z_HM 1 +/* Host-aware zoned device */ +#define VIRTIO_BLK_Z_HA 2 + +/* + * Zone descriptor. A part of VIRTIO_BLK_T_ZONE_REPORT command reply. + */ +struct virtio_blk_zone_descriptor { + /* Zone capacity */ + __virtio64 z_cap; + /* The starting sector of the zone */ + __virtio64 z_start; + /* Zone write pointer position in sectors */ + __virtio64 z_wp; + /* Zone type */ + uint8_t z_type; + /* Zone state */ + uint8_t z_state; + uint8_t reserved[38]; +}; + +struct virtio_blk_zone_report { + __virtio64 nr_zones; + uint8_t reserved[56]; + struct virtio_blk_zone_descriptor zones[]; +}; + +/* + * Supported zone types. + */ + +/* Conventional zone */ +#define VIRTIO_BLK_ZT_CONV 1 +/* Sequential Write Required zone */ +#define VIRTIO_BLK_ZT_SWR 2 +/* Sequential Write Preferred zone */ +#define VIRTIO_BLK_ZT_SWP 3 + +/* + * Zone states that are available for zones of all types. + */ + +/* Not a write pointer (conventional zones only) */ +#define VIRTIO_BLK_ZS_NOT_WP 0 +/* Empty */ +#define VIRTIO_BLK_ZS_EMPTY 1 +/* Implicitly Open */ +#define VIRTIO_BLK_ZS_IOPEN 2 +/* Explicitly Open */ +#define VIRTIO_BLK_ZS_EOPEN 3 +/* Closed */ +#define VIRTIO_BLK_ZS_CLOSED 4 +/* Read-Only */ +#define VIRTIO_BLK_ZS_RDONLY 13 +/* Full */ +#define VIRTIO_BLK_ZS_FULL 14 +/* Offline */ +#define VIRTIO_BLK_ZS_OFFLINE 15 + /* Unmap this range (only valid for write zeroes command) */ #define VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP 0x00000001 @@ -198,4 +315,11 @@ struct virtio_scsi_inhdr { #define VIRTIO_BLK_S_OK 0 #define VIRTIO_BLK_S_IOERR 1 #define VIRTIO_BLK_S_UNSUPP 2 + +/* Error codes that are specific to zoned block devices */ +#define VIRTIO_BLK_S_ZONE_INVALID_CMD 3 +#define VIRTIO_BLK_S_ZONE_UNALIGNED_WP 4 +#define VIRTIO_BLK_S_ZONE_OPEN_RESOURCE 5 +#define VIRTIO_BLK_S_ZONE_ACTIVE_RESOURCE 6 + #endif /* _LINUX_VIRTIO_BLK_H */ diff --git a/include/standard-headers/linux/virtio_bt.h b/include/standard-headers/linux/virtio_bt.h index 245e1eff4b..a11ecc3f92 100644 --- a/include/standard-headers/linux/virtio_bt.h +++ b/include/standard-headers/linux/virtio_bt.h @@ -9,6 +9,7 @@ #define VIRTIO_BT_F_VND_HCI 0 /* Indicates vendor command support */ #define VIRTIO_BT_F_MSFT_EXT 1 /* Indicates MSFT vendor support */ #define VIRTIO_BT_F_AOSP_EXT 2 /* Indicates AOSP vendor support */ +#define VIRTIO_BT_F_CONFIG_V2 3 /* Use second version configuration */ enum virtio_bt_config_type { VIRTIO_BT_CONFIG_TYPE_PRIMARY = 0, @@ -28,4 +29,11 @@ struct virtio_bt_config { uint16_t msft_opcode; } QEMU_PACKED; +struct virtio_bt_config_v2 { + uint8_t type; + uint8_t alignment; + uint16_t vendor; + uint16_t msft_opcode; +}; + #endif /* _LINUX_VIRTIO_BT_H */ diff --git a/include/standard-headers/linux/virtio_config.h b/include/standard-headers/linux/virtio_config.h index 7acd8d4abc..45be0fa1bc 100644 --- a/include/standard-headers/linux/virtio_config.h +++ b/include/standard-headers/linux/virtio_config.h @@ -52,7 +52,7 @@ * rest are per-device feature bits. */ #define VIRTIO_TRANSPORT_F_START 28 -#define VIRTIO_TRANSPORT_F_END 38 +#define VIRTIO_TRANSPORT_F_END 42 #ifndef VIRTIO_CONFIG_NO_LEGACY /* Do we get callbacks when the ring is completely used, even if we've @@ -96,4 +96,26 @@ * Does the device support Single Root I/O Virtualization? */ #define VIRTIO_F_SR_IOV 37 + +/* + * This feature indicates that the driver passes extra data (besides + * identifying the virtqueue) in its device notifications. + */ +#define VIRTIO_F_NOTIFICATION_DATA 38 + +/* This feature indicates that the driver uses the data provided by the device + * as a virtqueue identifier in available buffer notifications. + */ +#define VIRTIO_F_NOTIF_CONFIG_DATA 39 + +/* + * This feature indicates that the driver can reset a queue individually. + */ +#define VIRTIO_F_RING_RESET 40 + +/* + * This feature indicates that the device support administration virtqueues. + */ +#define VIRTIO_F_ADMIN_VQ 41 + #endif /* _LINUX_VIRTIO_CONFIG_H */ diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h index 80d76b75bc..7aa2eb7662 100644 --- a/include/standard-headers/linux/virtio_ids.h +++ b/include/standard-headers/linux/virtio_ids.h @@ -73,12 +73,12 @@ * Virtio Transitional IDs */ -#define VIRTIO_TRANS_ID_NET 1000 /* transitional virtio net */ -#define VIRTIO_TRANS_ID_BLOCK 1001 /* transitional virtio block */ -#define VIRTIO_TRANS_ID_BALLOON 1002 /* transitional virtio balloon */ -#define VIRTIO_TRANS_ID_CONSOLE 1003 /* transitional virtio console */ -#define VIRTIO_TRANS_ID_SCSI 1004 /* transitional virtio SCSI */ -#define VIRTIO_TRANS_ID_RNG 1005 /* transitional virtio rng */ -#define VIRTIO_TRANS_ID_9P 1009 /* transitional virtio 9p console */ +#define VIRTIO_TRANS_ID_NET 0x1000 /* transitional virtio net */ +#define VIRTIO_TRANS_ID_BLOCK 0x1001 /* transitional virtio block */ +#define VIRTIO_TRANS_ID_BALLOON 0x1002 /* transitional virtio balloon */ +#define VIRTIO_TRANS_ID_CONSOLE 0x1003 /* transitional virtio console */ +#define VIRTIO_TRANS_ID_SCSI 0x1004 /* transitional virtio SCSI */ +#define VIRTIO_TRANS_ID_RNG 0x1005 /* transitional virtio rng */ +#define VIRTIO_TRANS_ID_9P 0x1009 /* transitional virtio 9p console */ #endif /* _LINUX_VIRTIO_IDS_H */ diff --git a/include/standard-headers/linux/virtio_net.h b/include/standard-headers/linux/virtio_net.h index e0a070518f..0f88417742 100644 --- a/include/standard-headers/linux/virtio_net.h +++ b/include/standard-headers/linux/virtio_net.h @@ -56,8 +56,13 @@ #define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow * Steering */ #define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ - +#define VIRTIO_NET_F_VQ_NOTF_COAL 52 /* Device supports virtqueue notification coalescing */ +#define VIRTIO_NET_F_NOTF_COAL 53 /* Device supports notifications coalescing */ +#define VIRTIO_NET_F_GUEST_USO4 54 /* Guest can handle USOv4 in. */ +#define VIRTIO_NET_F_GUEST_USO6 55 /* Guest can handle USOv6 in. */ +#define VIRTIO_NET_F_HOST_USO 56 /* Host can handle USO in. */ #define VIRTIO_NET_F_HASH_REPORT 57 /* Supports hash report */ +#define VIRTIO_NET_F_GUEST_HDRLEN 59 /* Guest provides the exact hdr_len value. */ #define VIRTIO_NET_F_RSS 60 /* Supports RSS RX steering */ #define VIRTIO_NET_F_RSC_EXT 61 /* extended coalescing info */ #define VIRTIO_NET_F_STANDBY 62 /* Act as standby for another device @@ -130,6 +135,7 @@ struct virtio_net_hdr_v1 { #define VIRTIO_NET_HDR_GSO_TCPV4 1 /* GSO frame, IPv4 TCP (TSO) */ #define VIRTIO_NET_HDR_GSO_UDP 3 /* GSO frame, IPv4 UDP (UFO) */ #define VIRTIO_NET_HDR_GSO_TCPV6 4 /* GSO frame, IPv6 TCP */ +#define VIRTIO_NET_HDR_GSO_UDP_L4 5 /* GSO frame, IPv4& IPv6 UDP (USO) */ #define VIRTIO_NET_HDR_GSO_ECN 0x80 /* TCP has ECN set */ uint8_t gso_type; __virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */ @@ -355,4 +361,49 @@ struct virtio_net_hash_config { #define VIRTIO_NET_CTRL_GUEST_OFFLOADS 5 #define VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET 0 +/* + * Control notifications coalescing. + * + * Request the device to change the notifications coalescing parameters. + * + * Available with the VIRTIO_NET_F_NOTF_COAL feature bit. + */ +#define VIRTIO_NET_CTRL_NOTF_COAL 6 +/* + * Set the tx-usecs/tx-max-packets parameters. + */ +struct virtio_net_ctrl_coal_tx { + /* Maximum number of packets to send before a TX notification */ + uint32_t tx_max_packets; + /* Maximum number of usecs to delay a TX notification */ + uint32_t tx_usecs; +}; + +#define VIRTIO_NET_CTRL_NOTF_COAL_TX_SET 0 + +/* + * Set the rx-usecs/rx-max-packets parameters. + */ +struct virtio_net_ctrl_coal_rx { + /* Maximum number of packets to receive before a RX notification */ + uint32_t rx_max_packets; + /* Maximum number of usecs to delay a RX notification */ + uint32_t rx_usecs; +}; + +#define VIRTIO_NET_CTRL_NOTF_COAL_RX_SET 1 +#define VIRTIO_NET_CTRL_NOTF_COAL_VQ_SET 2 +#define VIRTIO_NET_CTRL_NOTF_COAL_VQ_GET 3 + +struct virtio_net_ctrl_coal { + uint32_t max_packets; + uint32_t max_usecs; +}; + +struct virtio_net_ctrl_coal_vq { + uint16_t vqn; + uint16_t reserved; + struct virtio_net_ctrl_coal coal; +}; + #endif /* _LINUX_VIRTIO_NET_H */ diff --git a/include/standard-headers/linux/virtio_pci.h b/include/standard-headers/linux/virtio_pci.h index db7a8e2fcb..3e2bc2c97e 100644 --- a/include/standard-headers/linux/virtio_pci.h +++ b/include/standard-headers/linux/virtio_pci.h @@ -166,6 +166,20 @@ struct virtio_pci_common_cfg { uint32_t queue_used_hi; /* read-write */ }; +/* + * Warning: do not use sizeof on this: use offsetofend for + * specific fields you need. + */ +struct virtio_pci_modern_common_cfg { + struct virtio_pci_common_cfg cfg; + + uint16_t queue_notify_data; /* read-write */ + uint16_t queue_reset; /* read-write */ + + uint16_t admin_queue_index; /* read-only */ + uint16_t admin_queue_num; /* read-only */ +}; + /* Fields in VIRTIO_PCI_CAP_PCI_CFG: */ struct virtio_pci_cfg_cap { struct virtio_pci_cap cap; @@ -202,7 +216,74 @@ struct virtio_pci_cfg_cap { #define VIRTIO_PCI_COMMON_Q_AVAILHI 44 #define VIRTIO_PCI_COMMON_Q_USEDLO 48 #define VIRTIO_PCI_COMMON_Q_USEDHI 52 +#define VIRTIO_PCI_COMMON_Q_NDATA 56 +#define VIRTIO_PCI_COMMON_Q_RESET 58 +#define VIRTIO_PCI_COMMON_ADM_Q_IDX 60 +#define VIRTIO_PCI_COMMON_ADM_Q_NUM 62 #endif /* VIRTIO_PCI_NO_MODERN */ +/* Admin command status. */ +#define VIRTIO_ADMIN_STATUS_OK 0 + +/* Admin command opcode. */ +#define VIRTIO_ADMIN_CMD_LIST_QUERY 0x0 +#define VIRTIO_ADMIN_CMD_LIST_USE 0x1 + +/* Admin command group type. */ +#define VIRTIO_ADMIN_GROUP_TYPE_SRIOV 0x1 + +/* Transitional device admin command. */ +#define VIRTIO_ADMIN_CMD_LEGACY_COMMON_CFG_WRITE 0x2 +#define VIRTIO_ADMIN_CMD_LEGACY_COMMON_CFG_READ 0x3 +#define VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_WRITE 0x4 +#define VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_READ 0x5 +#define VIRTIO_ADMIN_CMD_LEGACY_NOTIFY_INFO 0x6 + +struct QEMU_PACKED virtio_admin_cmd_hdr { + uint16_t opcode; + /* + * 1 - SR-IOV + * 2-65535 - reserved + */ + uint16_t group_type; + /* Unused, reserved for future extensions. */ + uint8_t reserved1[12]; + uint64_t group_member_id; +}; + +struct QEMU_PACKED virtio_admin_cmd_status { + uint16_t status; + uint16_t status_qualifier; + /* Unused, reserved for future extensions. */ + uint8_t reserved2[4]; +}; + +struct QEMU_PACKED virtio_admin_cmd_legacy_wr_data { + uint8_t offset; /* Starting offset of the register(s) to write. */ + uint8_t reserved[7]; + uint8_t registers[]; +}; + +struct QEMU_PACKED virtio_admin_cmd_legacy_rd_data { + uint8_t offset; /* Starting offset of the register(s) to read. */ +}; + +#define VIRTIO_ADMIN_CMD_NOTIFY_INFO_FLAGS_END 0 +#define VIRTIO_ADMIN_CMD_NOTIFY_INFO_FLAGS_OWNER_DEV 0x1 +#define VIRTIO_ADMIN_CMD_NOTIFY_INFO_FLAGS_OWNER_MEM 0x2 + +#define VIRTIO_ADMIN_CMD_MAX_NOTIFY_INFO 4 + +struct QEMU_PACKED virtio_admin_cmd_notify_info_data { + uint8_t flags; /* 0 = end of list, 1 = owner device, 2 = member device */ + uint8_t bar; /* BAR of the member or the owner device */ + uint8_t padding[6]; + uint64_t offset; /* Offset within bar. */ +}; + +struct virtio_admin_cmd_notify_info_result { + struct virtio_admin_cmd_notify_info_data entries[VIRTIO_ADMIN_CMD_MAX_NOTIFY_INFO]; +}; + #endif diff --git a/include/standard-headers/linux/virtio_pmem.h b/include/standard-headers/linux/virtio_pmem.h index fc029de798..1a2576d017 100644 --- a/include/standard-headers/linux/virtio_pmem.h +++ b/include/standard-headers/linux/virtio_pmem.h @@ -14,6 +14,13 @@ #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_config.h" +/* Feature bits */ +/* guest physical address range will be indicated as shared memory region 0 */ +#define VIRTIO_PMEM_F_SHMEM_REGION 0 + +/* shmid of the shared memory region corresponding to the pmem */ +#define VIRTIO_PMEM_SHMEM_REGION_ID 0 + struct virtio_pmem_config { uint64_t start; uint64_t size; diff --git a/include/standard-headers/linux/virtio_ring.h b/include/standard-headers/linux/virtio_ring.h index 0fa0e1067f..22f6eb8ca7 100644 --- a/include/standard-headers/linux/virtio_ring.h +++ b/include/standard-headers/linux/virtio_ring.h @@ -91,15 +91,21 @@ #define VRING_USED_ALIGN_SIZE 4 #define VRING_DESC_ALIGN_SIZE 16 -/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ +/** + * struct vring_desc - Virtio ring descriptors, + * 16 bytes long. These can chain together via @next. + * + * @addr: buffer address (guest-physical) + * @len: buffer length + * @flags: descriptor flags + * @next: index of the next descriptor in the chain, + * if the VRING_DESC_F_NEXT flag is set. We chain unused + * descriptors via this, too. + */ struct vring_desc { - /* Address (guest-physical). */ __virtio64 addr; - /* Length. */ __virtio32 len; - /* The flags as indicated above. */ __virtio16 flags; - /* We chain unused descriptors via this, too */ __virtio16 next; }; diff --git a/include/sysemu/accel-blocker.h b/include/sysemu/accel-blocker.h new file mode 100644 index 0000000000..f07f368358 --- /dev/null +++ b/include/sysemu/accel-blocker.h @@ -0,0 +1,55 @@ +/* + * Accelerator blocking API, to prevent new ioctls from starting and wait the + * running ones finish. + * This mechanism differs from pause/resume_all_vcpus() in that it does not + * release the BQL. + * + * Copyright (c) 2022 Red Hat Inc. + * + * Author: Emanuele Giuseppe Esposito + * + * 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 ACCEL_BLOCKER_H +#define ACCEL_BLOCKER_H + +#include "sysemu/cpus.h" + +void accel_blocker_init(void); + +/* + * accel_{cpu_}ioctl_begin/end: + * Mark when ioctl is about to run or just finished. + * + * accel_{cpu_}ioctl_begin will block after accel_ioctl_inhibit_begin() is + * called, preventing new ioctls to run. They will continue only after + * accel_ioctl_inibith_end(). + */ +void accel_ioctl_begin(void); +void accel_ioctl_end(void); +void accel_cpu_ioctl_begin(CPUState *cpu); +void accel_cpu_ioctl_end(CPUState *cpu); + +/* + * accel_ioctl_inhibit_begin: start critical section + * + * This function makes sure that: + * 1) incoming accel_{cpu_}ioctl_begin() calls block + * 2) wait that all ioctls that were already running reach + * accel_{cpu_}ioctl_end(), kicking vcpus if necessary. + * + * This allows the caller to access shared data or perform operations without + * worrying of concurrent vcpus accesses. + */ +void accel_ioctl_inhibit_begin(void); + +/* + * accel_ioctl_inhibit_end: end critical section started by + * accel_ioctl_inhibit_begin() + * + * This function allows blocked accel_{cpu_}ioctl_begin() to continue. + */ +void accel_ioctl_inhibit_end(void); + +#endif /* ACCEL_BLOCKER_H */ diff --git a/include/sysemu/accel-ops.h b/include/sysemu/accel-ops.h index a0572ea87a..ef91fc28bb 100644 --- a/include/sysemu/accel-ops.h +++ b/include/sysemu/accel-ops.h @@ -10,6 +10,7 @@ #ifndef ACCEL_OPS_H #define ACCEL_OPS_H +#include "exec/cpu-common.h" #include "qom/object.h" #define ACCEL_OPS_SUFFIX "-ops" @@ -29,6 +30,7 @@ struct AccelOpsClass { void (*ops_init)(AccelOpsClass *ops); bool (*cpus_are_resettable)(void); + void (*cpu_reset_hold)(CPUState *cpu); void (*create_vcpu_thread)(CPUState *cpu); /* MANDATORY NON-NULL */ void (*kick_vcpu_thread)(CPUState *cpu); @@ -44,6 +46,13 @@ struct AccelOpsClass { int64_t (*get_virtual_clock)(void); int64_t (*get_elapsed_ticks)(void); + + /* gdbstub hooks */ + bool (*supports_guest_debug)(void); + int (*update_guest_debug)(CPUState *cpu); + int (*insert_breakpoint)(CPUState *cpu, int type, vaddr addr, vaddr len); + int (*remove_breakpoint)(CPUState *cpu, int type, vaddr addr, vaddr len); + void (*remove_all_breakpoints)(CPUState *cpu); }; #endif /* ACCEL_OPS_H */ diff --git a/include/sysemu/block-backend-common.h b/include/sysemu/block-backend-common.h index 2391679c56..780cea7305 100644 --- a/include/sysemu/block-backend-common.h +++ b/include/sysemu/block-backend-common.h @@ -59,6 +59,19 @@ typedef struct BlockDevOps { */ bool (*is_medium_locked)(void *opaque); + /* + * Runs when the backend receives a drain request. + */ + void (*drained_begin)(void *opaque); + /* + * Runs when the backend's last drain request ends. + */ + void (*drained_end)(void *opaque); + /* + * Is the device still busy? + */ + bool (*drained_poll)(void *opaque); + /* * I/O API functions. These functions are thread-safe. * @@ -76,18 +89,6 @@ typedef struct BlockDevOps { * Runs when the size changed (e.g. monitor command block_resize) */ void (*resize_cb)(void *opaque); - /* - * Runs when the backend receives a drain request. - */ - void (*drained_begin)(void *opaque); - /* - * Runs when the backend's last drain request ends. - */ - void (*drained_end)(void *opaque); - /* - * Is the device still busy? - */ - bool (*drained_poll)(void *opaque); } BlockDevOps; /* diff --git a/include/sysemu/block-backend-global-state.h b/include/sysemu/block-backend-global-state.h index 415f0c91d7..49c12b0fa9 100644 --- a/include/sysemu/block-backend-global-state.h +++ b/include/sysemu/block-backend-global-state.h @@ -23,13 +23,29 @@ */ BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm); -BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm, - uint64_t shared_perm, Error **errp); -BlockBackend *blk_new_open(const char *filename, const char *reference, - QDict *options, int flags, Error **errp); + +BlockBackend * no_coroutine_fn +blk_new_with_bs(BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, + Error **errp); + +BlockBackend * coroutine_fn no_co_wrapper +blk_co_new_with_bs(BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, + Error **errp); + +BlockBackend * no_coroutine_fn +blk_new_open(const char *filename, const char *reference, QDict *options, + int flags, Error **errp); + +BlockBackend * coroutine_fn no_co_wrapper +blk_co_new_open(const char *filename, const char *reference, QDict *options, + int flags, Error **errp); + int blk_get_refcnt(BlockBackend *blk); void blk_ref(BlockBackend *blk); -void blk_unref(BlockBackend *blk); + +void no_coroutine_fn blk_unref(BlockBackend *blk); +void coroutine_fn no_co_wrapper blk_co_unref(BlockBackend *blk); + void blk_remove_all_bs(void); BlockBackend *blk_by_name(const char *name); BlockBackend *blk_next(BlockBackend *blk); @@ -43,10 +59,10 @@ BlockBackend *blk_by_public(BlockBackendPublic *public); void blk_remove_bs(BlockBackend *blk); int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp); int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp); -bool bdrv_has_blk(BlockDriverState *bs); -bool bdrv_is_root_node(BlockDriverState *bs); -int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, - Error **errp); +bool GRAPH_RDLOCK bdrv_has_blk(BlockDriverState *bs); +bool GRAPH_RDLOCK bdrv_is_root_node(BlockDriverState *bs); +int GRAPH_UNLOCKED blk_set_perm(BlockBackend *blk, uint64_t perm, + uint64_t shared_perm, Error **errp); void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm); void blk_iostatus_enable(BlockBackend *blk); @@ -65,6 +81,7 @@ void blk_activate(BlockBackend *blk, Error **errp); int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags); void blk_aio_cancel(BlockAIOCB *acb); int blk_commit_all(void); +bool blk_in_drain(BlockBackend *blk); void blk_drain(BlockBackend *blk); void blk_drain_all(void); void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, @@ -106,8 +123,8 @@ void blk_io_limits_enable(BlockBackend *blk, const char *group); void blk_io_limits_update_group(BlockBackend *blk, const char *group); void blk_set_force_allow_inactivate(BlockBackend *blk); -void blk_register_buf(BlockBackend *blk, void *host, size_t size); -void blk_unregister_buf(BlockBackend *blk, void *host); +bool blk_register_buf(BlockBackend *blk, void *host, size_t size, Error **errp); +void blk_unregister_buf(BlockBackend *blk, void *host, size_t size); const BdrvChild *blk_root(BlockBackend *blk); diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h index 6517c39295..d174275a5c 100644 --- a/include/sysemu/block-backend-io.h +++ b/include/sysemu/block-backend-io.h @@ -14,6 +14,7 @@ #define BLOCK_BACKEND_IO_H #include "block-backend-common.h" +#include "block/accounting.h" /* * I/O API functions. These functions are thread-safe. @@ -45,6 +46,16 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, BlockCompletionFunc *cb, void *opaque); BlockAIOCB *blk_aio_flush(BlockBackend *blk, BlockCompletionFunc *cb, void *opaque); +BlockAIOCB *blk_aio_zone_report(BlockBackend *blk, int64_t offset, + unsigned int *nr_zones, + BlockZoneDescriptor *zones, + BlockCompletionFunc *cb, void *opaque); +BlockAIOCB *blk_aio_zone_mgmt(BlockBackend *blk, BlockZoneOp op, + int64_t offset, int64_t len, + BlockCompletionFunc *cb, void *opaque); +BlockAIOCB *blk_aio_zone_append(BlockBackend *blk, int64_t *offset, + QEMUIOVector *qiov, BdrvRequestFlags flags, + BlockCompletionFunc *cb, void *opaque); BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes, BlockCompletionFunc *cb, void *opaque); void blk_aio_cancel_async(BlockAIOCB *acb); @@ -53,13 +64,29 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, void blk_inc_in_flight(BlockBackend *blk); void blk_dec_in_flight(BlockBackend *blk); -bool blk_is_inserted(BlockBackend *blk); -bool blk_is_available(BlockBackend *blk); -void blk_lock_medium(BlockBackend *blk, bool locked); -void blk_eject(BlockBackend *blk, bool eject_flag); -int64_t blk_getlength(BlockBackend *blk); + +bool coroutine_fn GRAPH_RDLOCK blk_co_is_inserted(BlockBackend *blk); +bool co_wrapper_mixed_bdrv_rdlock blk_is_inserted(BlockBackend *blk); + +bool coroutine_fn GRAPH_RDLOCK blk_co_is_available(BlockBackend *blk); +bool co_wrapper_mixed_bdrv_rdlock blk_is_available(BlockBackend *blk); + +void coroutine_fn blk_co_lock_medium(BlockBackend *blk, bool locked); +void co_wrapper blk_lock_medium(BlockBackend *blk, bool locked); + +void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag); +void co_wrapper blk_eject(BlockBackend *blk, bool eject_flag); + +int64_t coroutine_fn blk_co_getlength(BlockBackend *blk); +int64_t co_wrapper_mixed blk_getlength(BlockBackend *blk); + +void coroutine_fn blk_co_get_geometry(BlockBackend *blk, + uint64_t *nb_sectors_ptr); void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr); + +int64_t coroutine_fn blk_co_nb_sectors(BlockBackend *blk); int64_t blk_nb_sectors(BlockBackend *blk); + void *blk_try_blockalign(BlockBackend *blk, size_t size); void *blk_blockalign(BlockBackend *blk, size_t size); bool blk_is_writable(BlockBackend *blk); @@ -72,10 +99,7 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action, void blk_iostatus_set_err(BlockBackend *blk, int error); int blk_get_max_iov(BlockBackend *blk); int blk_get_max_hw_iov(BlockBackend *blk); -void blk_set_guest_block_size(BlockBackend *blk, int align); -void blk_io_plug(BlockBackend *blk); -void blk_io_unplug(BlockBackend *blk); AioContext *blk_get_aio_context(BlockBackend *blk); BlockAcctStats *blk_get_stats(BlockBackend *blk); void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, @@ -93,6 +117,15 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); +int coroutine_fn blk_co_block_status_above(BlockBackend *blk, + BlockDriverState *base, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, + BlockDriverState **file); +int coroutine_fn blk_co_is_allocated_above(BlockBackend *blk, + BlockDriverState *base, + bool include_base, int64_t offset, + int64_t bytes, int64_t *pnum); /* * "I/O or GS" API functions. These functions can run without @@ -102,60 +135,96 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, * the "I/O or GS" API. */ -int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int bytes); -int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int bytes, - BdrvRequestFlags flags); +int co_wrapper_mixed blk_pread(BlockBackend *blk, int64_t offset, + int64_t bytes, void *buf, + BdrvRequestFlags flags); +int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, int64_t bytes, + void *buf, BdrvRequestFlags flags); + +int co_wrapper_mixed blk_preadv(BlockBackend *blk, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); -int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, - int64_t bytes, - QEMUIOVector *qiov, size_t qiov_offset, + +int co_wrapper_mixed blk_preadv_part(BlockBackend *blk, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + size_t qiov_offset, BdrvRequestFlags flags); +int coroutine_fn blk_co_preadv_part(BlockBackend *blk, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + size_t qiov_offset, BdrvRequestFlags flags); + +int co_wrapper_mixed blk_pwrite(BlockBackend *blk, int64_t offset, + int64_t bytes, const void *buf, + BdrvRequestFlags flags); +int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, int64_t bytes, + const void *buf, BdrvRequestFlags flags); + +int co_wrapper_mixed blk_pwritev(BlockBackend *blk, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); -static inline int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, - int64_t bytes, void *buf, - BdrvRequestFlags flags) -{ - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - IO_OR_GS_CODE(); +int co_wrapper_mixed blk_pwritev_part(BlockBackend *blk, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, + size_t qiov_offset, + BdrvRequestFlags flags); +int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, + int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags); - assert(bytes <= SIZE_MAX); +int co_wrapper_mixed blk_pwrite_compressed(BlockBackend *blk, + int64_t offset, int64_t bytes, + const void *buf); +int coroutine_fn blk_co_pwrite_compressed(BlockBackend *blk, int64_t offset, + int64_t bytes, const void *buf); - return blk_co_preadv(blk, offset, bytes, &qiov, flags); -} +int co_wrapper_mixed blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, + int64_t bytes, + BdrvRequestFlags flags); +int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, + int64_t bytes, BdrvRequestFlags flags); -static inline int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, - int64_t bytes, void *buf, - BdrvRequestFlags flags) -{ - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); - IO_OR_GS_CODE(); - - assert(bytes <= SIZE_MAX); - - return blk_co_pwritev(blk, offset, bytes, &qiov, flags); -} +int coroutine_fn blk_co_zone_report(BlockBackend *blk, int64_t offset, + unsigned int *nr_zones, + BlockZoneDescriptor *zones); +int co_wrapper_mixed blk_zone_report(BlockBackend *blk, int64_t offset, + unsigned int *nr_zones, + BlockZoneDescriptor *zones); +int coroutine_fn blk_co_zone_mgmt(BlockBackend *blk, BlockZoneOp op, + int64_t offset, int64_t len); +int co_wrapper_mixed blk_zone_mgmt(BlockBackend *blk, BlockZoneOp op, + int64_t offset, int64_t len); +int coroutine_fn blk_co_zone_append(BlockBackend *blk, int64_t *offset, + QEMUIOVector *qiov, + BdrvRequestFlags flags); +int co_wrapper_mixed blk_zone_append(BlockBackend *blk, int64_t *offset, + QEMUIOVector *qiov, + BdrvRequestFlags flags); +int co_wrapper_mixed blk_pdiscard(BlockBackend *blk, int64_t offset, + int64_t bytes); int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); +int co_wrapper_mixed blk_flush(BlockBackend *blk); int coroutine_fn blk_co_flush(BlockBackend *blk); -int blk_flush(BlockBackend *blk); -int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf); +int co_wrapper_mixed blk_ioctl(BlockBackend *blk, unsigned long int req, + void *buf); +int coroutine_fn blk_co_ioctl(BlockBackend *blk, unsigned long int req, + void *buf); -int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf, - int64_t bytes); -int blk_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes); -int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int64_t bytes, BdrvRequestFlags flags); -int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int64_t bytes, BdrvRequestFlags flags); -int blk_truncate(BlockBackend *blk, int64_t offset, bool exact, - PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); +int co_wrapper_mixed blk_truncate(BlockBackend *blk, int64_t offset, + bool exact, PreallocMode prealloc, + BdrvRequestFlags flags, Error **errp); +int coroutine_fn blk_co_truncate(BlockBackend *blk, int64_t offset, bool exact, + PreallocMode prealloc, BdrvRequestFlags flags, + Error **errp); #endif /* BLOCK_BACKEND_IO_H */ diff --git a/include/sysemu/block-ram-registrar.h b/include/sysemu/block-ram-registrar.h new file mode 100644 index 0000000000..d8b2f7942b --- /dev/null +++ b/include/sysemu/block-ram-registrar.h @@ -0,0 +1,37 @@ +/* + * BlockBackend RAM Registrar + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BLOCK_RAM_REGISTRAR_H +#define BLOCK_RAM_REGISTRAR_H + +#include "exec/ramlist.h" + +/** + * struct BlockRAMRegistrar: + * + * Keeps RAMBlock memory registered with a BlockBackend using + * blk_register_buf() including hotplugged memory. + * + * Emulated devices or other BlockBackend users initialize a BlockRAMRegistrar + * with blk_ram_registrar_init() before submitting I/O requests with the + * BDRV_REQ_REGISTERED_BUF flag set. + */ +typedef struct { + BlockBackend *blk; + RAMBlockNotifier notifier; + bool ok; +} BlockRAMRegistrar; + +void blk_ram_registrar_init(BlockRAMRegistrar *r, BlockBackend *blk); +void blk_ram_registrar_destroy(BlockRAMRegistrar *r); + +/* Have all RAMBlocks been registered successfully? */ +static inline bool blk_ram_registrar_ok(BlockRAMRegistrar *r) +{ + return r->ok; +} + +#endif /* BLOCK_RAM_REGISTRAR_H */ diff --git a/softmmu/timers-state.h b/include/sysemu/cpu-timers-internal.h similarity index 100% rename from softmmu/timers-state.h rename to include/sysemu/cpu-timers-internal.h diff --git a/include/sysemu/cpu-timers.h b/include/sysemu/cpu-timers.h index 2e786fe7fb..d86738a378 100644 --- a/include/sysemu/cpu-timers.h +++ b/include/sysemu/cpu-timers.h @@ -17,18 +17,24 @@ void cpu_timers_init(void); /* icount - Instruction Counter API */ -/* - * icount enablement state: +/** + * ICountMode: icount enablement state: * - * 0 = Disabled - Do not count executed instructions. - * 1 = Enabled - Fixed conversion of insn to ns via "shift" option - * 2 = Enabled - Runtime adaptive algorithm to compute shift + * @ICOUNT_DISABLED: Disabled - Do not count executed instructions. + * @ICOUNT_PRECISE: Enabled - Fixed conversion of insn to ns via "shift" option + * @ICOUNT_ADAPTATIVE: Enabled - Runtime adaptive algorithm to compute shift */ -#ifdef CONFIG_TCG -extern int use_icount; +typedef enum { + ICOUNT_DISABLED = 0, + ICOUNT_PRECISE, + ICOUNT_ADAPTATIVE, +} ICountMode; + +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) +extern ICountMode use_icount; #define icount_enabled() (use_icount) #else -#define icount_enabled() 0 +#define icount_enabled() ICOUNT_DISABLED #endif /* @@ -50,8 +56,14 @@ int64_t icount_get(void); */ int64_t icount_to_ns(int64_t icount); -/* configure the icount options, including "shift" */ -void icount_configure(QemuOpts *opts, Error **errp); +/** + * icount_configure: configure the icount options, including "shift" + * @opts: Options to parse + * @errp: pointer to a NULL-initialized error object + * + * Return: true on success, else false setting @errp with error + */ +bool icount_configure(QemuOpts *opts, Error **errp); /* used by tcg vcpu thread to calc icount budget */ int64_t icount_round(int64_t count); diff --git a/include/sysemu/cpus.h b/include/sysemu/cpus.h index b5c87d48b3..b4a566cfe7 100644 --- a/include/sysemu/cpus.h +++ b/include/sysemu/cpus.h @@ -1,12 +1,14 @@ #ifndef QEMU_CPUS_H #define QEMU_CPUS_H -#include "qemu/timer.h" #include "sysemu/accel-ops.h" /* register accel-specific operations */ void cpus_register_accel(const AccelOpsClass *i); +/* return registers ops */ +const AccelOpsClass *cpus_get_accel(void); + /* accel/dummy-cpus.c */ /* Create a dummy vcpu for AccelOpsClass->create_vcpu_thread */ @@ -48,11 +50,4 @@ void cpu_synchronize_all_post_reset(void); void cpu_synchronize_all_post_init(void); void cpu_synchronize_all_pre_loadvm(void); -#ifndef CONFIG_USER_ONLY -/* vl.c */ -/* *-user doesn't have configurable SMP topology */ -extern int smp_cores; -extern int smp_threads; -#endif - #endif diff --git a/include/sysemu/cryptodev-vhost.h b/include/sysemu/cryptodev-vhost.h index e8cab1356e..4c3c22acae 100644 --- a/include/sysemu/cryptodev-vhost.h +++ b/include/sysemu/cryptodev-vhost.h @@ -79,7 +79,7 @@ cryptodev_vhost_init( * cryptodev_vhost_cleanup: * @crypto: the cryptodev backend common vhost object * - * Clean the resouce associated with @crypto that realizaed + * Clean the resource associated with @crypto that realizaed * by cryptodev_vhost_init() * */ diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h index f4d4057d4d..96d3998b93 100644 --- a/include/sysemu/cryptodev.h +++ b/include/sysemu/cryptodev.h @@ -24,7 +24,9 @@ #define CRYPTODEV_H #include "qemu/queue.h" +#include "qemu/throttle.h" #include "qom/object.h" +#include "qapi/qapi-types-cryptodev.h" /** * CryptoDevBackend: @@ -48,15 +50,9 @@ typedef struct CryptoDevBackendPeers CryptoDevBackendPeers; typedef struct CryptoDevBackendClient CryptoDevBackendClient; -enum CryptoDevBackendAlgType { - CRYPTODEV_BACKEND_ALG_SYM, - CRYPTODEV_BACKEND_ALG__MAX, -}; - /** * CryptoDevBackendSymSessionInfo: * - * @op_code: operation code (refer to virtio_crypto.h) * @cipher_alg: algorithm type of CIPHER * @key_len: byte length of cipher key * @hash_alg: algorithm type of HASH/MAC @@ -74,7 +70,6 @@ enum CryptoDevBackendAlgType { */ typedef struct CryptoDevBackendSymSessionInfo { /* corresponding with virtio crypto spec */ - uint32_t op_code; uint32_t cipher_alg; uint32_t key_len; uint32_t hash_alg; @@ -89,11 +84,37 @@ typedef struct CryptoDevBackendSymSessionInfo { uint8_t *auth_key; } CryptoDevBackendSymSessionInfo; +/** + * CryptoDevBackendAsymSessionInfo: + */ +typedef struct CryptoDevBackendRsaPara { + uint32_t padding_algo; + uint32_t hash_algo; +} CryptoDevBackendRsaPara; + +typedef struct CryptoDevBackendAsymSessionInfo { + /* corresponding with virtio crypto spec */ + uint32_t algo; + uint32_t keytype; + uint32_t keylen; + uint8_t *key; + union { + CryptoDevBackendRsaPara rsa; + } u; +} CryptoDevBackendAsymSessionInfo; + +typedef struct CryptoDevBackendSessionInfo { + uint32_t op_code; + union { + CryptoDevBackendSymSessionInfo sym_sess_info; + CryptoDevBackendAsymSessionInfo asym_sess_info; + } u; + uint64_t session_id; +} CryptoDevBackendSessionInfo; + /** * CryptoDevBackendSymOpInfo: * - * @session_id: session index which was previously - * created by cryptodev_backend_sym_create_session() * @aad_len: byte length of additional authenticated data * @iv_len: byte length of initialization vector or counter * @src_len: byte length of source data @@ -119,7 +140,6 @@ typedef struct CryptoDevBackendSymSessionInfo { * */ typedef struct CryptoDevBackendSymOpInfo { - uint64_t session_id; uint32_t aad_len; uint32_t iv_len; uint32_t src_len; @@ -138,34 +158,63 @@ typedef struct CryptoDevBackendSymOpInfo { uint8_t data[]; } CryptoDevBackendSymOpInfo; + +/** + * CryptoDevBackendAsymOpInfo: + * + * @src_len: byte length of source data + * @dst_len: byte length of destination data + * @src: point to the source data + * @dst: point to the destination data + * + */ +typedef struct CryptoDevBackendAsymOpInfo { + uint32_t src_len; + uint32_t dst_len; + uint8_t *src; + uint8_t *dst; +} CryptoDevBackendAsymOpInfo; + +typedef void (*CryptoDevCompletionFunc) (void *opaque, int ret); + +typedef struct CryptoDevBackendOpInfo { + QCryptodevBackendAlgType algtype; + uint32_t op_code; + uint32_t queue_index; + CryptoDevCompletionFunc cb; + void *opaque; /* argument for cb */ + uint64_t session_id; + union { + CryptoDevBackendSymOpInfo *sym_op_info; + CryptoDevBackendAsymOpInfo *asym_op_info; + } u; + QTAILQ_ENTRY(CryptoDevBackendOpInfo) next; +} CryptoDevBackendOpInfo; + struct CryptoDevBackendClass { ObjectClass parent_class; void (*init)(CryptoDevBackend *backend, Error **errp); void (*cleanup)(CryptoDevBackend *backend, Error **errp); - int64_t (*create_session)(CryptoDevBackend *backend, - CryptoDevBackendSymSessionInfo *sess_info, - uint32_t queue_index, Error **errp); + int (*create_session)(CryptoDevBackend *backend, + CryptoDevBackendSessionInfo *sess_info, + uint32_t queue_index, + CryptoDevCompletionFunc cb, + void *opaque); + int (*close_session)(CryptoDevBackend *backend, - uint64_t session_id, - uint32_t queue_index, Error **errp); - int (*do_sym_op)(CryptoDevBackend *backend, - CryptoDevBackendSymOpInfo *op_info, - uint32_t queue_index, Error **errp); + uint64_t session_id, + uint32_t queue_index, + CryptoDevCompletionFunc cb, + void *opaque); + + int (*do_op)(CryptoDevBackend *backend, + CryptoDevBackendOpInfo *op_info); }; -typedef enum CryptoDevBackendOptionsType { - CRYPTODEV_BACKEND_TYPE_NONE = 0, - CRYPTODEV_BACKEND_TYPE_BUILTIN = 1, - CRYPTODEV_BACKEND_TYPE_VHOST_USER = 2, - CRYPTODEV_BACKEND_TYPE__MAX, -} CryptoDevBackendOptionsType; - struct CryptoDevBackendClient { - CryptoDevBackendOptionsType type; - char *model; - char *name; + QCryptodevBackendType type; char *info_str; unsigned int queue_index; int vring_enable; @@ -190,6 +239,7 @@ struct CryptoDevBackendConf { uint32_t mac_algo_l; uint32_t mac_algo_h; uint32_t aead_algo; + uint32_t akcipher_algo; /* Maximum length of cipher key */ uint32_t max_cipher_key_len; /* Maximum length of authenticated key */ @@ -198,6 +248,24 @@ struct CryptoDevBackendConf { uint64_t max_size; }; +typedef struct CryptodevBackendSymStat { + int64_t encrypt_ops; + int64_t decrypt_ops; + int64_t encrypt_bytes; + int64_t decrypt_bytes; +} CryptodevBackendSymStat; + +typedef struct CryptodevBackendAsymStat { + int64_t encrypt_ops; + int64_t decrypt_ops; + int64_t sign_ops; + int64_t verify_ops; + int64_t encrypt_bytes; + int64_t decrypt_bytes; + int64_t sign_bytes; + int64_t verify_bytes; +} CryptodevBackendAsymStat; + struct CryptoDevBackend { Object parent_obj; @@ -205,15 +273,48 @@ struct CryptoDevBackend { /* Tag the cryptodev backend is used by virtio-crypto or not */ bool is_used; CryptoDevBackendConf conf; + CryptodevBackendSymStat *sym_stat; + CryptodevBackendAsymStat *asym_stat; + + ThrottleState ts; + ThrottleTimers tt; + ThrottleConfig tc; + QTAILQ_HEAD(, CryptoDevBackendOpInfo) opinfos; }; +#define CryptodevSymStatInc(be, op, bytes) do { \ + be->sym_stat->op##_bytes += (bytes); \ + be->sym_stat->op##_ops += 1; \ +} while (/*CONSTCOND*/0) + +#define CryptodevSymStatIncEncrypt(be, bytes) \ + CryptodevSymStatInc(be, encrypt, bytes) + +#define CryptodevSymStatIncDecrypt(be, bytes) \ + CryptodevSymStatInc(be, decrypt, bytes) + +#define CryptodevAsymStatInc(be, op, bytes) do { \ + be->asym_stat->op##_bytes += (bytes); \ + be->asym_stat->op##_ops += 1; \ +} while (/*CONSTCOND*/0) + +#define CryptodevAsymStatIncEncrypt(be, bytes) \ + CryptodevAsymStatInc(be, encrypt, bytes) + +#define CryptodevAsymStatIncDecrypt(be, bytes) \ + CryptodevAsymStatInc(be, decrypt, bytes) + +#define CryptodevAsymStatIncSign(be, bytes) \ + CryptodevAsymStatInc(be, sign, bytes) + +#define CryptodevAsymStatIncVerify(be, bytes) \ + CryptodevAsymStatInc(be, verify, bytes) + + /** * cryptodev_backend_new_client: - * @model: the cryptodev backend model - * @name: the cryptodev backend name, can be NULL * - * Creates a new cryptodev backend client object - * with the @name in the model @model. + * Creates a new cryptodev backend client object. * * The returned object must be released with * cryptodev_backend_free_client() when no @@ -221,9 +322,8 @@ struct CryptoDevBackend { * * Returns: a new cryptodev backend client object */ -CryptoDevBackendClient * -cryptodev_backend_new_client(const char *model, - const char *name); +CryptoDevBackendClient *cryptodev_backend_new_client(void); + /** * cryptodev_backend_free_client: * @cc: the cryptodev backend client object @@ -239,7 +339,7 @@ void cryptodev_backend_free_client( * @backend: the cryptodev backend object * @errp: pointer to a NULL-initialized error object * - * Clean the resouce associated with @backend that realizaed + * Clean the resource associated with @backend that realizaed * by the specific backend's init() callback */ void cryptodev_backend_cleanup( @@ -247,60 +347,67 @@ void cryptodev_backend_cleanup( Error **errp); /** - * cryptodev_backend_sym_create_session: + * cryptodev_backend_create_session: * @backend: the cryptodev backend object * @sess_info: parameters needed by session creating * @queue_index: queue index of cryptodev backend client * @errp: pointer to a NULL-initialized error object + * @cb: callback when session create is compeleted + * @opaque: parameter passed to callback * - * Create a session for symmetric algorithms + * Create a session for symmetric/asymmetric algorithms * - * Returns: session id on success, or -1 on error + * Returns: 0 for success and cb will be called when creation is completed, + * negative value for error, and cb will not be called. */ -int64_t cryptodev_backend_sym_create_session( +int cryptodev_backend_create_session( CryptoDevBackend *backend, - CryptoDevBackendSymSessionInfo *sess_info, - uint32_t queue_index, Error **errp); + CryptoDevBackendSessionInfo *sess_info, + uint32_t queue_index, + CryptoDevCompletionFunc cb, + void *opaque); /** - * cryptodev_backend_sym_close_session: + * cryptodev_backend_close_session: * @backend: the cryptodev backend object * @session_id: the session id * @queue_index: queue index of cryptodev backend client * @errp: pointer to a NULL-initialized error object + * @cb: callback when session create is compeleted + * @opaque: parameter passed to callback * - * Close a session for symmetric algorithms which was previously - * created by cryptodev_backend_sym_create_session() + * Close a session for which was previously + * created by cryptodev_backend_create_session() * - * Returns: 0 on success, or Negative on error + * Returns: 0 for success and cb will be called when creation is completed, + * negative value for error, and cb will not be called. */ -int cryptodev_backend_sym_close_session( +int cryptodev_backend_close_session( CryptoDevBackend *backend, uint64_t session_id, - uint32_t queue_index, Error **errp); + uint32_t queue_index, + CryptoDevCompletionFunc cb, + void *opaque); /** * cryptodev_backend_crypto_operation: * @backend: the cryptodev backend object - * @opaque: pointer to a VirtIOCryptoReq object - * @queue_index: queue index of cryptodev backend client - * @errp: pointer to a NULL-initialized error object + * @op_info: pointer to a CryptoDevBackendOpInfo object * - * Do crypto operation, such as encryption and - * decryption + * Do crypto operation, such as encryption, decryption, signature and + * verification * - * Returns: VIRTIO_CRYPTO_OK on success, - * or -VIRTIO_CRYPTO_* on error + * Returns: 0 for success and cb will be called when creation is completed, + * negative value for error, and cb will not be called. */ int cryptodev_backend_crypto_operation( CryptoDevBackend *backend, - void *opaque, - uint32_t queue_index, Error **errp); + CryptoDevBackendOpInfo *op_info); /** * cryptodev_backend_set_used: * @backend: the cryptodev backend object - * @used: ture or false + * @used: true or false * * Set the cryptodev backend is used by virtio-crypto or not */ @@ -320,7 +427,7 @@ bool cryptodev_backend_is_used(CryptoDevBackend *backend); /** * cryptodev_backend_set_ready: * @backend: the cryptodev backend object - * @ready: ture or false + * @ready: true or false * * Set the cryptodev backend is ready or not, which is called * by the children of the cryptodev banckend interface. diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h index ef060a9759..8eab395934 100644 --- a/include/sysemu/device_tree.h +++ b/include/sysemu/device_tree.h @@ -126,16 +126,15 @@ int qemu_fdt_add_path(void *fdt, const char *path); #define qemu_fdt_setprop_cells(fdt, node_path, property, ...) \ do { \ uint32_t qdt_tmp[] = { __VA_ARGS__ }; \ - int i; \ - \ - for (i = 0; i < ARRAY_SIZE(qdt_tmp); i++) { \ - qdt_tmp[i] = cpu_to_be32(qdt_tmp[i]); \ + for (unsigned i_ = 0; i_ < ARRAY_SIZE(qdt_tmp); i_++) { \ + qdt_tmp[i_] = cpu_to_be32(qdt_tmp[i_]); \ } \ qemu_fdt_setprop(fdt, node_path, property, qdt_tmp, \ sizeof(qdt_tmp)); \ } while (0) void qemu_fdt_dumpdtb(void *fdt, int size); +void hmp_dumpdtb(Monitor *mon, const QDict *qdict); /** * qemu_fdt_setprop_sized_cells_from_array: @@ -196,6 +195,15 @@ int qemu_fdt_setprop_sized_cells_from_array(void *fdt, qdt_tmp); \ }) + +/** + * qemu_fdt_randomize_seeds: + * @fdt: device tree blob + * + * Re-randomize all "rng-seed" properties with new seeds. + */ +void qemu_fdt_randomize_seeds(void *fdt); + #define FDT_PCI_RANGE_RELOCATABLE 0x80000000 #define FDT_PCI_RANGE_PREFETCHABLE 0x40000000 #define FDT_PCI_RANGE_ALIASED 0x20000000 diff --git a/include/sysemu/dirtylimit.h b/include/sysemu/dirtylimit.h new file mode 100644 index 0000000000..d11ebbbbdb --- /dev/null +++ b/include/sysemu/dirtylimit.h @@ -0,0 +1,39 @@ +/* + * Dirty page rate limit common functions + * + * Copyright (c) 2022 CHINA TELECOM CO.,LTD. + * + * Authors: + * Hyman Huang(黄勇) + * + * 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_DIRTYRLIMIT_H +#define QEMU_DIRTYRLIMIT_H + +#define DIRTYLIMIT_CALC_TIME_MS 1000 /* 1000ms */ + +int64_t vcpu_dirty_rate_get(int cpu_index); +void vcpu_dirty_rate_stat_start(void); +void vcpu_dirty_rate_stat_stop(void); +void vcpu_dirty_rate_stat_initialize(void); +void vcpu_dirty_rate_stat_finalize(void); + +void dirtylimit_state_lock(void); +void dirtylimit_state_unlock(void); +void dirtylimit_state_initialize(void); +void dirtylimit_state_finalize(void); +bool dirtylimit_in_service(void); +bool dirtylimit_vcpu_index_valid(int cpu_index); +void dirtylimit_process(void); +void dirtylimit_change(bool start); +void dirtylimit_set_vcpu(int cpu_index, + uint64_t quota, + bool enable); +void dirtylimit_set_all(uint64_t quota, + bool enable); +void dirtylimit_vcpu_execute(CPUState *cpu); +uint64_t dirtylimit_throttle_time_per_round(void); +uint64_t dirtylimit_ring_full_time(void); +#endif diff --git a/include/sysemu/dirtyrate.h b/include/sysemu/dirtyrate.h new file mode 100644 index 0000000000..20813f303f --- /dev/null +++ b/include/sysemu/dirtyrate.h @@ -0,0 +1,30 @@ +/* + * dirty page rate helper functions + * + * Copyright (c) 2022 CHINA TELECOM CO.,LTD. + * + * Authors: + * Hyman Huang(黄勇) + * + * 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_DIRTYRATE_H +#define QEMU_DIRTYRATE_H + +#include "qapi/qapi-types-migration.h" + +typedef struct VcpuStat { + int nvcpu; /* number of vcpu */ + DirtyRateVcpu *rates; /* array of dirty rate for each vcpu */ +} VcpuStat; + +int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms, + VcpuStat *stat, + unsigned int flag, + bool one_shot); + +void global_dirty_log_change(unsigned int flag, + bool start); +#endif diff --git a/include/sysemu/dump-arch.h b/include/sysemu/dump-arch.h index e25b02e990..743916e46c 100644 --- a/include/sysemu/dump-arch.h +++ b/include/sysemu/dump-arch.h @@ -21,6 +21,10 @@ typedef struct ArchDumpInfo { uint32_t page_size; /* The target's page size. If it's variable and * unknown, then this should be the maximum. */ uint64_t phys_base; /* The target's physmem base. */ + void (*arch_sections_add_fn)(DumpState *s); + uint64_t (*arch_sections_write_hdr_fn)(DumpState *s, uint8_t *buff); + int (*arch_sections_write_fn)(DumpState *s, uint8_t *buff); + void (*arch_cleanup_fn)(DumpState *s); } ArchDumpInfo; struct GuestPhysBlockList; /* memory_mapping.h */ diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h index ffc2ea1072..d702854853 100644 --- a/include/sysemu/dump.h +++ b/include/sysemu/dump.h @@ -15,6 +15,7 @@ #define DUMP_H #include "qapi/qapi-types-dump.h" +#include "qemu/thread.h" #define MAKEDUMPFILE_SIGNATURE "makedumpfile" #define MAX_SIZE_MDF_HEADER (4096) /* max size of makedumpfile_header */ @@ -136,7 +137,7 @@ typedef struct QEMU_PACKED KdumpSubHeader64 { } KdumpSubHeader64; typedef struct DataCache { - int fd; /* fd of the file where to write the cached data */ + DumpState *state; /* dump state related to this data */ uint8_t *buf; /* buffer for cached data */ size_t buf_size; /* size of the buf */ size_t data_size; /* size of cached data in buf */ @@ -154,23 +155,36 @@ typedef struct DumpState { GuestPhysBlockList guest_phys_blocks; ArchDumpInfo dump_info; MemoryMappingList list; - uint32_t phdr_num; - uint32_t shdr_num; bool resume; bool detached; + bool kdump_raw; + hwaddr memory_offset; + int fd; + + /* + * Dump filter area variables + * + * A filtered dump only contains the guest memory designated by + * the start address and length variables defined below. + * + * If length is 0, no filtering is applied. + */ + int64_t filter_area_begin; /* Start address of partial guest memory area */ + int64_t filter_area_length; /* Length of partial guest memory area */ + + /* Elf dump related data */ + uint32_t phdr_num; + uint32_t shdr_num; ssize_t note_size; hwaddr shdr_offset; hwaddr phdr_offset; hwaddr section_offset; hwaddr note_offset; - hwaddr memory_offset; - int fd; - GuestPhysBlock *next_block; - ram_addr_t start; - bool has_filter; - int64_t begin; - int64_t length; + void *elf_section_hdrs; /* Pointer to section header buffer */ + void *elf_section_data; /* Pointer to section data buffer */ + uint64_t elf_section_data_size; /* Size of section data */ + GArray *string_table_buf; /* String table data buffer */ uint8_t *note_buf; /* buffer for notes */ size_t note_buf_offset; /* the writing place in note_buf */ @@ -203,4 +217,9 @@ typedef struct DumpState { uint16_t cpu_to_dump16(DumpState *s, uint16_t val); uint32_t cpu_to_dump32(DumpState *s, uint32_t val); uint64_t cpu_to_dump64(DumpState *s, uint64_t val); + +int64_t dump_filtered_memblock_size(GuestPhysBlock *block, int64_t filter_area_start, + int64_t filter_area_length); +int64_t dump_filtered_memblock_start(GuestPhysBlock *block, int64_t filter_area_start, + int64_t filter_area_length); #endif diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h index 2748bf6ae1..a6c24f1351 100644 --- a/include/sysemu/event-loop-base.h +++ b/include/sysemu/event-loop-base.h @@ -14,7 +14,6 @@ #include "qom/object.h" #include "block/aio.h" -#include "qemu/typedefs.h" #define TYPE_EVENT_LOOP_BASE "event-loop-base" OBJECT_DECLARE_TYPE(EventLoopBase, EventLoopBaseClass, diff --git a/include/sysemu/hax.h b/include/sysemu/hax.h deleted file mode 100644 index bf8f99a824..0000000000 --- a/include/sysemu/hax.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * QEMU HAXM support - * - * Copyright IBM, Corp. 2008 - * - * Authors: - * Anthony Liguori - * - * Copyright (c) 2011 Intel Corporation - * Written by: - * Jiang Yunhong - * Xin Xiaohui - * Zhang Xiantao - * - * Copyright 2016 Google, 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 QEMU_HAX_H -#define QEMU_HAX_H - -int hax_sync_vcpus(void); - -#ifdef NEED_CPU_H -# ifdef CONFIG_HAX -# define CONFIG_HAX_IS_POSSIBLE -# endif -#else /* !NEED_CPU_H */ -# define CONFIG_HAX_IS_POSSIBLE -#endif - -#ifdef CONFIG_HAX_IS_POSSIBLE - -extern bool hax_allowed; - -#define hax_enabled() (hax_allowed) - -#else /* !CONFIG_HAX_IS_POSSIBLE */ - -#define hax_enabled() (0) - -#endif /* CONFIG_HAX_IS_POSSIBLE */ - -#endif /* QEMU_HAX_H */ diff --git a/include/sysemu/hostmem.h b/include/sysemu/hostmem.h index 9ff5c16963..0e411aaa29 100644 --- a/include/sysemu/hostmem.h +++ b/include/sysemu/hostmem.h @@ -18,6 +18,7 @@ #include "qom/object.h" #include "exec/memory.h" #include "qemu/bitmap.h" +#include "qemu/thread-context.h" #define TYPE_MEMORY_BACKEND "memory-backend" OBJECT_DECLARE_TYPE(HostMemoryBackend, HostMemoryBackendClass, @@ -46,7 +47,15 @@ OBJECT_DECLARE_TYPE(HostMemoryBackend, HostMemoryBackendClass, struct HostMemoryBackendClass { ObjectClass parent_class; - void (*alloc)(HostMemoryBackend *backend, Error **errp); + /** + * alloc: Allocate memory from backend. + * + * @backend: the #HostMemoryBackend. + * @errp: pointer to Error*, to store an error if it happens. + * + * Return: true on success, else false setting @errp with error. + */ + bool (*alloc)(HostMemoryBackend *backend, Error **errp); }; /** @@ -66,6 +75,7 @@ struct HostMemoryBackend { bool merge, dump, use_canonical_path; bool prealloc, is_mapped, share, reserve; uint32_t prealloc_threads; + ThreadContext *prealloc_context; DECLARE_BITMAP(host_nodes, MAX_NODES + 1); HostMemPolicy policy; diff --git a/include/sysemu/hvf.h b/include/sysemu/hvf.h index bb70082e45..4a7c6af3a5 100644 --- a/include/sysemu/hvf.h +++ b/include/sysemu/hvf.h @@ -17,15 +17,13 @@ #include "qom/object.h" #ifdef NEED_CPU_H +#include "cpu.h" #ifdef CONFIG_HVF -uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, - int reg); extern bool hvf_allowed; #define hvf_enabled() (hvf_allowed) #else /* !CONFIG_HVF */ #define hvf_enabled() 0 -#define hvf_get_supported_cpuid(func, idx, reg) 0 #endif /* !CONFIG_HVF */ #endif /* NEED_CPU_H */ @@ -36,4 +34,38 @@ typedef struct HVFState HVFState; DECLARE_INSTANCE_CHECKER(HVFState, HVF_STATE, TYPE_HVF_ACCEL) +#ifdef NEED_CPU_H +struct hvf_sw_breakpoint { + vaddr pc; + vaddr saved_insn; + int use_count; + QTAILQ_ENTRY(hvf_sw_breakpoint) entry; +}; + +struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, + vaddr pc); +int hvf_sw_breakpoints_active(CPUState *cpu); + +int hvf_arch_insert_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp); +int hvf_arch_remove_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp); +int hvf_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type); +int hvf_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type); +void hvf_arch_remove_all_hw_breakpoints(void); + +/* + * hvf_update_guest_debug: + * @cs: CPUState for the CPU to update + * + * Update guest to enable or disable debugging. Per-arch specifics will be + * handled by calling down to hvf_arch_update_guest_debug. + */ +int hvf_update_guest_debug(CPUState *cpu); +void hvf_arch_update_guest_debug(CPUState *cpu); + +/* + * Return whether the guest supports debugging. + */ +bool hvf_arch_supports_guest_debug(void); +#endif /* NEED_CPU_H */ + #endif diff --git a/include/sysemu/hvf_int.h b/include/sysemu/hvf_int.h index 6545f7cd61..718beddcdd 100644 --- a/include/sysemu/hvf_int.h +++ b/include/sysemu/hvf_int.h @@ -45,14 +45,16 @@ struct HVFState { hvf_vcpu_caps *hvf_caps; uint64_t vtimer_offset; + QTAILQ_HEAD(, hvf_sw_breakpoint) hvf_sw_breakpoints; }; extern HVFState *hvf_state; -struct hvf_vcpu_state { +struct AccelCPUState { uint64_t fd; void *exit; bool vtimer_masked; sigset_t unblock_ipi_mask; + bool guest_debug_enabled; }; void assert_hvf_ok(hv_return_t ret); diff --git a/include/sysemu/hw_accel.h b/include/sysemu/hw_accel.h index 22903a55f7..c71b77e71f 100644 --- a/include/sysemu/hw_accel.h +++ b/include/sysemu/hw_accel.h @@ -12,7 +12,6 @@ #define QEMU_HW_ACCEL_H #include "hw/core/cpu.h" -#include "sysemu/hax.h" #include "sysemu/kvm.h" #include "sysemu/hvf.h" #include "sysemu/whpx.h" diff --git a/include/sysemu/iommufd.h b/include/sysemu/iommufd.h new file mode 100644 index 0000000000..9af27ebd6c --- /dev/null +++ b/include/sysemu/iommufd.h @@ -0,0 +1,36 @@ +#ifndef SYSEMU_IOMMUFD_H +#define SYSEMU_IOMMUFD_H + +#include "qom/object.h" +#include "exec/hwaddr.h" +#include "exec/cpu-common.h" + +#define TYPE_IOMMUFD_BACKEND "iommufd" +OBJECT_DECLARE_TYPE(IOMMUFDBackend, IOMMUFDBackendClass, IOMMUFD_BACKEND) + +struct IOMMUFDBackendClass { + ObjectClass parent_class; +}; + +struct IOMMUFDBackend { + Object parent; + + /*< protected >*/ + int fd; /* /dev/iommu file descriptor */ + bool owned; /* is the /dev/iommu opened internally */ + uint32_t users; + + /*< public >*/ +}; + +int iommufd_backend_connect(IOMMUFDBackend *be, Error **errp); +void iommufd_backend_disconnect(IOMMUFDBackend *be); + +int iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id, + Error **errp); +void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id); +int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, + ram_addr_t size, void *vaddr, bool readonly); +int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, + hwaddr iova, ram_addr_t size); +#endif diff --git a/include/sysemu/iothread.h b/include/sysemu/iothread.h index 8f8601d6ab..2102a90eca 100644 --- a/include/sysemu/iothread.h +++ b/include/sysemu/iothread.h @@ -59,7 +59,7 @@ void iothread_stop(IOThread *iothread); void iothread_destroy(IOThread *iothread); /* - * Returns true if executing withing IOThread context, + * Returns true if executing within IOThread context, * false otherwise. */ bool qemu_in_iothread(void); diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index a783c78868..fad9a7e8ff 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -11,11 +11,11 @@ * */ +/* header to be included in non-KVM-specific code */ + #ifndef QEMU_KVM_H #define QEMU_KVM_H -#include "qemu/queue.h" -#include "hw/core/cpu.h" #include "exec/memattrs.h" #include "qemu/accel.h" #include "qom/object.h" @@ -36,18 +36,12 @@ extern bool kvm_kernel_irqchip; extern bool kvm_split_irqchip; extern bool kvm_async_interrupts_allowed; extern bool kvm_halt_in_kernel_allowed; -extern bool kvm_eventfds_allowed; -extern bool kvm_irqfds_allowed; extern bool kvm_resamplefds_allowed; extern bool kvm_msi_via_irqfd_allowed; extern bool kvm_gsi_routing_allowed; extern bool kvm_gsi_direct_mapping; extern bool kvm_readonly_mem_allowed; -extern bool kvm_direct_msi_allowed; -extern bool kvm_ioeventfd_any_length_allowed; extern bool kvm_msi_use_devid; -extern bool kvm_has_guest_debug; -extern int kvm_sstep_flags; #define kvm_enabled() (kvm_allowed) /** @@ -90,23 +84,16 @@ extern int kvm_sstep_flags; */ #define kvm_halt_in_kernel() (kvm_halt_in_kernel_allowed) -/** - * kvm_eventfds_enabled: - * - * Returns: true if we can use eventfds to receive notifications - * from a KVM CPU (ie the kernel supports eventds and we are running - * with a configuration where it is meaningful to use them). - */ -#define kvm_eventfds_enabled() (kvm_eventfds_allowed) - /** * kvm_irqfds_enabled: * * Returns: true if we can use irqfds to inject interrupts into * a KVM CPU (ie the kernel supports irqfds and we are running * with a configuration where it is meaningful to use them). + * + * Always available if running with in-kernel irqchip. */ -#define kvm_irqfds_enabled() (kvm_irqfds_allowed) +#define kvm_irqfds_enabled() kvm_irqchip_in_kernel() /** * kvm_resamplefds_enabled: @@ -149,19 +136,6 @@ extern int kvm_sstep_flags; */ #define kvm_readonly_mem_enabled() (kvm_readonly_mem_allowed) -/** - * kvm_direct_msi_enabled: - * - * Returns: true if KVM allows direct MSI injection. - */ -#define kvm_direct_msi_enabled() (kvm_direct_msi_allowed) - -/** - * kvm_ioeventfd_any_length_enabled: - * Returns: true if KVM allows any length io eventfd. - */ -#define kvm_ioeventfd_any_length_enabled() (kvm_ioeventfd_any_length_allowed) - /** * kvm_msi_devid_required: * Returns: true if KVM requires a device id to be provided while @@ -169,17 +143,6 @@ extern int kvm_sstep_flags; */ #define kvm_msi_devid_required() (kvm_msi_use_devid) -/* - * Does KVM support guest debugging - */ -#define kvm_supports_guest_debug() (kvm_has_guest_debug) - -/* - * kvm_supported_sstep_flags - * Returns: SSTEP_* flags that KVM supports for guest debug - */ -#define kvm_get_supported_sstep_flags() (kvm_sstep_flags) - #else #define kvm_enabled() (0) @@ -187,23 +150,17 @@ extern int kvm_sstep_flags; #define kvm_irqchip_is_split() (false) #define kvm_async_interrupts_enabled() (false) #define kvm_halt_in_kernel() (false) -#define kvm_eventfds_enabled() (false) #define kvm_irqfds_enabled() (false) #define kvm_resamplefds_enabled() (false) #define kvm_msi_via_irqfd_enabled() (false) #define kvm_gsi_routing_allowed() (false) #define kvm_gsi_direct_mapping() (false) #define kvm_readonly_mem_enabled() (false) -#define kvm_direct_msi_enabled() (false) -#define kvm_ioeventfd_any_length_enabled() (false) #define kvm_msi_devid_required() (false) -#define kvm_supports_guest_debug() (false) -#define kvm_get_supported_sstep_flags() (0) #endif /* CONFIG_KVM_IS_POSSIBLE */ struct kvm_run; -struct kvm_lapic_state; struct kvm_irq_routing_entry; typedef struct KVMCapabilityInfo { @@ -231,16 +188,12 @@ typedef struct KVMRouteChange { /* external API */ -bool kvm_has_free_slot(MachineState *ms); +unsigned int kvm_get_max_memslots(void); +unsigned int kvm_get_free_memslots(void); bool kvm_has_sync_mmu(void); int kvm_has_vcpu_events(void); -int kvm_has_robust_singlestep(void); -int kvm_has_debugregs(void); int kvm_max_nested_state_length(void); -int kvm_has_pit_state2(void); -int kvm_has_many_ioeventfds(void); int kvm_has_gsi_routing(void); -int kvm_has_intx_set_mask(void); /** * kvm_arm_supports_user_irq @@ -262,12 +215,23 @@ int kvm_on_sigbus(int code, void *addr); void kvm_flush_coalesced_mmio_buffer(void); -int kvm_insert_breakpoint(CPUState *cpu, target_ulong addr, - target_ulong len, int type); -int kvm_remove_breakpoint(CPUState *cpu, target_ulong addr, - target_ulong len, int type); -void kvm_remove_all_breakpoints(CPUState *cpu); +/** + * kvm_update_guest_debug(): ensure KVM debug structures updated + * @cs: the CPUState for this cpu + * @reinject_trap: KVM trap injection control + * + * There are usually per-arch specifics which will be handled by + * calling down to kvm_arch_update_guest_debug after the generic + * fields have been set. + */ +#ifdef KVM_CAP_SET_GUEST_DEBUG int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap); +#else +static inline int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap) +{ + return -EINVAL; +} +#endif /* internal API */ @@ -353,6 +317,8 @@ bool kvm_device_supported(int vmfd, uint64_t type); extern const KVMCapabilityInfo kvm_arch_required_capabilities[]; +void kvm_arch_accel_class_init(ObjectClass *oc); + void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run); MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run); @@ -371,6 +337,8 @@ int kvm_arch_get_registers(CPUState *cpu); int kvm_arch_put_registers(CPUState *cpu, int level); +int kvm_arch_get_default_type(MachineState *ms); + int kvm_arch_init(MachineState *ms, KVMState *s); int kvm_arch_init_vcpu(CPUState *cpu); @@ -407,20 +375,18 @@ void kvm_irqchip_add_change_notifier(Notifier *n); void kvm_irqchip_remove_change_notifier(Notifier *n); void kvm_irqchip_change_notify(void); -void kvm_get_apic_state(DeviceState *d, struct kvm_lapic_state *kapic); - struct kvm_guest_debug; struct kvm_debug_exit_arch; struct kvm_sw_breakpoint { - target_ulong pc; - target_ulong saved_insn; + vaddr pc; + vaddr saved_insn; int use_count; QTAILQ_ENTRY(kvm_sw_breakpoint) entry; }; struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu, - target_ulong pc); + vaddr pc); int kvm_sw_breakpoints_active(CPUState *cpu); @@ -428,10 +394,8 @@ int kvm_arch_insert_sw_breakpoint(CPUState *cpu, struct kvm_sw_breakpoint *bp); int kvm_arch_remove_sw_breakpoint(CPUState *cpu, struct kvm_sw_breakpoint *bp); -int kvm_arch_insert_hw_breakpoint(target_ulong addr, - target_ulong len, int type); -int kvm_arch_remove_hw_breakpoint(target_ulong addr, - target_ulong len, int type); +int kvm_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type); +int kvm_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type); void kvm_arch_remove_all_hw_breakpoints(void); void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg); @@ -466,17 +430,10 @@ int kvm_vm_check_extension(KVMState *s, unsigned int extension); kvm_vcpu_ioctl(cpu, KVM_ENABLE_CAP, &cap); \ }) -uint32_t kvm_arch_get_supported_cpuid(KVMState *env, uint32_t function, - uint32_t index, int reg); -uint64_t kvm_arch_get_supported_msr_feature(KVMState *s, uint32_t index); - - void kvm_set_sigmask_len(KVMState *s, unsigned int sigmask_len); -#if !defined(CONFIG_USER_ONLY) int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr, hwaddr *phys_addr); -#endif #endif /* NEED_CPU_H */ @@ -527,7 +484,6 @@ 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_setup_irq_routing(bool pci_enabled); void kvm_init_irq_routing(KVMState *s); bool kvm_kernel_irqchip_allowed(void); @@ -565,8 +521,6 @@ int kvm_set_one_reg(CPUState *cs, uint64_t id, void *source); * Returns: 0 on success, or a negative errno on failure. */ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target); -struct ppc_radix_page_info *kvm_get_radix_page_info(void); -int kvm_get_max_memslots(void); /* Notify resamplefd for EOI of specific interrupts. */ void kvm_resample_fd_notify(int gsi); @@ -582,4 +536,12 @@ bool kvm_cpu_check_are_resettable(void); bool kvm_arch_cpu_check_are_resettable(void); bool kvm_dirty_ring_enabled(void); + +uint32_t kvm_dirty_ring_size(void); + +/** + * kvm_hwpoisoned_mem - indicate if there is any hwpoisoned page + * reported for the VM. + */ +bool kvm_hwpoisoned_mem(void); #endif diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 1f5487d9b7..882e37e12c 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -10,7 +10,9 @@ #define QEMU_KVM_INT_H #include "exec/memory.h" +#include "qapi/qapi-types-common.h" #include "qemu/accel.h" +#include "qemu/queue.h" #include "sysemu/kvm.h" typedef struct KVMSlot @@ -30,12 +32,97 @@ typedef struct KVMSlot ram_addr_t ram_start_offset; } KVMSlot; +typedef struct KVMMemoryUpdate { + QSIMPLEQ_ENTRY(KVMMemoryUpdate) next; + MemoryRegionSection section; +} KVMMemoryUpdate; + typedef struct KVMMemoryListener { MemoryListener listener; KVMSlot *slots; + unsigned int nr_used_slots; int as_id; + QSIMPLEQ_HEAD(, KVMMemoryUpdate) transaction_add; + QSIMPLEQ_HEAD(, KVMMemoryUpdate) transaction_del; } KVMMemoryListener; +#define KVM_MSI_HASHTAB_SIZE 256 + +enum KVMDirtyRingReaperState { + KVM_DIRTY_RING_REAPER_NONE = 0, + /* The reaper is sleeping */ + KVM_DIRTY_RING_REAPER_WAIT, + /* The reaper is reaping for dirty pages */ + KVM_DIRTY_RING_REAPER_REAPING, +}; + +/* + * KVM reaper instance, responsible for collecting the KVM dirty bits + * via the dirty ring. + */ +struct KVMDirtyRingReaper { + /* The reaper thread */ + QemuThread reaper_thr; + volatile uint64_t reaper_iteration; /* iteration number of reaper thr */ + volatile enum KVMDirtyRingReaperState reaper_state; /* reap thr state */ +}; +struct KVMState +{ + AccelState parent_obj; + + int nr_slots; + int fd; + int vmfd; + int coalesced_mmio; + int coalesced_pio; + struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; + bool coalesced_flush_in_progress; + int vcpu_events; +#ifdef KVM_CAP_SET_GUEST_DEBUG + QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; +#endif + int max_nested_state_len; + int kvm_shadow_mem; + bool kernel_irqchip_allowed; + bool kernel_irqchip_required; + OnOffAuto kernel_irqchip_split; + bool sync_mmu; + uint64_t manual_dirty_log_protect; + /* The man page (and posix) say ioctl numbers are signed int, but + * they're not. Linux, glibc and *BSD all treat ioctl numbers as + * unsigned, and treating them as signed here can break things */ + unsigned irq_set_ioctl; + unsigned int sigmask_len; + GHashTable *gsimap; +#ifdef KVM_CAP_IRQ_ROUTING + struct kvm_irq_routing *irq_routes; + int nr_allocated_irq_routes; + unsigned long *used_gsi_bitmap; + unsigned int gsi_count; +#endif + KVMMemoryListener memory_listener; + QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; + + /* For "info mtree -f" to tell if an MR is registered in KVM */ + int nr_as; + struct KVMAs { + KVMMemoryListener *ml; + AddressSpace *as; + } *as; + uint64_t kvm_dirty_ring_bytes; /* Size of the per-vcpu dirty ring */ + uint32_t kvm_dirty_ring_size; /* Number of dirty GFNs per ring */ + bool kvm_dirty_ring_with_bitmap; + uint64_t kvm_eager_split_size; /* Eager Page Splitting chunk size */ + struct KVMDirtyRingReaper reaper; + NotifyVmexitOption notify_vmexit; + uint32_t notify_window; + uint32_t xen_version; + uint32_t xen_caps; + uint16_t xen_gnttab_max_frames; + uint16_t xen_evtchn_max_pirq; + char *device; +}; + void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, AddressSpace *as, int as_id, const char *name); diff --git a/include/sysemu/kvm_xen.h b/include/sysemu/kvm_xen.h new file mode 100644 index 0000000000..961c702c4e --- /dev/null +++ b/include/sysemu/kvm_xen.h @@ -0,0 +1,44 @@ +/* + * Xen HVM emulation support in KVM + * + * Copyright © 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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_SYSEMU_KVM_XEN_H +#define QEMU_SYSEMU_KVM_XEN_H + +/* The KVM API uses these to indicate "no GPA" or "no GFN" */ +#define INVALID_GPA UINT64_MAX +#define INVALID_GFN UINT64_MAX + +/* QEMU plays the rôle of dom0 for "interdomain" communication. */ +#define DOMID_QEMU 0 + +int kvm_xen_soft_reset(void); +uint32_t kvm_xen_get_caps(void); +void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id); +bool kvm_xen_has_vcpu_callback_vector(void); +void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type); +void kvm_xen_set_callback_asserted(void); +int kvm_xen_set_vcpu_virq(uint32_t vcpu_id, uint16_t virq, uint16_t port); +uint16_t kvm_xen_get_gnttab_max_frames(void); +uint16_t kvm_xen_get_evtchn_max_pirq(void); + +#define kvm_xen_has_cap(cap) (!!(kvm_xen_get_caps() & \ + KVM_XEN_HVM_CONFIG_ ## cap)) + +#define XEN_SPECIAL_AREA_ADDR 0xfeff8000UL +#define XEN_SPECIAL_AREA_SIZE 0x4000UL + +#define XEN_SPECIALPAGE_CONSOLE 0 +#define XEN_SPECIALPAGE_XENSTORE 1 + +#define XEN_SPECIAL_PFN(x) ((XEN_SPECIAL_AREA_ADDR >> TARGET_PAGE_BITS) + \ + XEN_SPECIALPAGE_##x) + +#endif /* QEMU_SYSEMU_KVM_XEN_H */ diff --git a/include/sysemu/memory_mapping.h b/include/sysemu/memory_mapping.h index 3bbeb1bcb4..021e0a6230 100644 --- a/include/sysemu/memory_mapping.h +++ b/include/sysemu/memory_mapping.h @@ -71,7 +71,7 @@ void guest_phys_blocks_free(GuestPhysBlockList *list); void guest_phys_blocks_init(GuestPhysBlockList *list); void guest_phys_blocks_append(GuestPhysBlockList *list); -void qemu_get_guest_memory_mapping(MemoryMappingList *list, +bool qemu_get_guest_memory_mapping(MemoryMappingList *list, const GuestPhysBlockList *guest_phys_blocks, Error **errp); diff --git a/include/sysemu/numa.h b/include/sysemu/numa.h index 4173ef2afa..825cfe86bc 100644 --- a/include/sysemu/numa.h +++ b/include/sysemu/numa.h @@ -41,6 +41,7 @@ struct NodeInfo { struct HostMemoryBackend *node_memdev; bool present; bool has_cpu; + bool has_gi; uint8_t lb_info_provided; uint16_t initiator; uint8_t distance[MAX_NODES]; diff --git a/include/sysemu/nvmm.h b/include/sysemu/nvmm.h index 833670fccb..be7bc9a62d 100644 --- a/include/sysemu/nvmm.h +++ b/include/sysemu/nvmm.h @@ -7,6 +7,8 @@ * See the COPYING file in the top-level directory. */ +/* header to be included in non-NVMM-specific code */ + #ifndef QEMU_NVMM_H #define QEMU_NVMM_H diff --git a/include/sysemu/os-posix.h b/include/sysemu/os-posix.h index 58de7c994d..b881ac6c6f 100644 --- a/include/sysemu/os-posix.h +++ b/include/sysemu/os-posix.h @@ -42,20 +42,18 @@ extern "C" { #endif -int os_parse_cmd_args(int index, const char *optarg); void os_set_line_buffering(void); void os_setup_early_signal_handling(void); void os_set_proc_name(const char *s); void os_setup_signal_handling(void); -void os_daemonize(void); -void os_setup_post(void); -int os_mlock(void); - -#define closesocket(s) close(s) -#define ioctlsocket(s, r, v) ioctl(s, r, v) - int os_set_daemonize(bool d); bool is_daemonized(void); +void os_daemonize(void); +bool os_set_runas(const char *user_id); +void os_set_chroot(const char *path); +void os_setup_limits(void); +void os_setup_post(void); +int os_mlock(void); /** * qemu_alloc_stack: diff --git a/include/sysemu/os-win32.h b/include/sysemu/os-win32.h index edc3b38a57..b82a5d3ad9 100644 --- a/include/sysemu/os-win32.h +++ b/include/sysemu/os-win32.h @@ -29,19 +29,57 @@ #include #include #include +#include "qemu/typedefs.h" + +#ifdef HAVE_AFUNIX_H +#include +#else +/* + * Fallback definitions of things we need in afunix.h, if not available from + * the used Windows SDK or MinGW headers. + */ +#define UNIX_PATH_MAX 108 + +typedef struct sockaddr_un { + ADDRESS_FAMILY sun_family; + char sun_path[UNIX_PATH_MAX]; +} SOCKADDR_UN, *PSOCKADDR_UN; + +#define SIO_AF_UNIX_GETPEERPID _WSAIOR(IOC_VENDOR, 256) +#endif #ifdef __cplusplus extern "C" { #endif -#if defined(_WIN64) -/* On w64, setjmp is implemented by _setjmp which needs a second parameter. +#if defined(__aarch64__) +/* + * On windows-arm64, setjmp is available in only one variant, and longjmp always + * does stack unwinding. This crash with generated code. + * Thus, we use another implementation of setjmp (not windows one), coming from + * mingw, which never performs stack unwinding. + */ +#undef setjmp +#undef longjmp +/* + * These functions are not declared in setjmp.h because __aarch64__ defines + * setjmp to _setjmpex instead. However, they are still defined in libmingwex.a, + * which gets linked automatically. + */ +int __mingw_setjmp(jmp_buf); +void __attribute__((noreturn)) __mingw_longjmp(jmp_buf, int); +#define setjmp(env) __mingw_setjmp(env) +#define longjmp(env, val) __mingw_longjmp(env, val) +#elif defined(_WIN64) +/* + * On windows-x64, setjmp is implemented by _setjmp which needs a second parameter. * If this parameter is NULL, longjump does no stack unwinding. * That is what we need for QEMU. Passing the value of register rsp (default) - * lets longjmp try a stack unwinding which will crash with generated code. */ + * lets longjmp try a stack unwinding which will crash with generated code. + */ # undef setjmp # define setjmp(env) _setjmp(env, NULL) -#endif +#endif /* __aarch64__ */ /* QEMU uses sigsetjmp()/siglongjmp() as the portable way to specify * "longjmp and don't touch the signal masks". Since we know that the * savemask parameter will always be zero we can safely define these @@ -63,7 +101,6 @@ static inline void os_setup_signal_handling(void) {} static inline void os_daemonize(void) {} static inline void os_setup_post(void) {} static inline void os_set_proc_name(const char *dummy) {} -static inline int os_parse_cmd_args(int index, const char *optarg) { return -1; } void os_set_line_buffering(void); void os_setup_early_signal_handling(void); @@ -91,6 +128,11 @@ static inline int os_mlock(void) return -ENOSYS; } +static inline void os_setup_limits(void) +{ + return; +} + #define fsync _commit #if !defined(lseek) @@ -127,10 +169,31 @@ static inline void qemu_funlockfile(FILE *f) #endif } -/* We wrap all the sockets functions so that we can - * set errno based on WSAGetLastError() +/* Helper for WSAEventSelect, to report errors */ +bool qemu_socket_select(int sockfd, WSAEVENT hEventObject, + long lNetworkEvents, Error **errp); + +bool qemu_socket_unselect(int sockfd, Error **errp); + +/* We wrap all the sockets functions so that we can set errno based on + * WSAGetLastError(), and use file-descriptors instead of SOCKET. */ +/* + * qemu_close_socket_osfhandle: + * @fd: a file descriptor associated with a SOCKET + * + * Close only the C run-time file descriptor, leave the SOCKET opened. + * + * Returns zero on success. On error, -1 is returned, and errno is set to + * indicate the error. + */ +int qemu_close_socket_osfhandle(int fd); + +#undef close +#define close qemu_close_wrap +int qemu_close_wrap(int fd); + #undef connect #define connect qemu_connect_wrap int qemu_connect_wrap(int sockfd, const struct sockaddr *addr, @@ -162,10 +225,6 @@ int qemu_shutdown_wrap(int sockfd, int how); #define ioctlsocket qemu_ioctlsocket_wrap int qemu_ioctlsocket_wrap(int fd, int req, void *val); -#undef closesocket -#define closesocket qemu_closesocket_wrap -int qemu_closesocket_wrap(int fd); - #undef getsockopt #define getsockopt qemu_getsockopt_wrap int qemu_getsockopt_wrap(int sockfd, int level, int optname, @@ -204,6 +263,13 @@ ssize_t qemu_recv_wrap(int sockfd, void *buf, size_t len, int flags); ssize_t qemu_recvfrom_wrap(int sockfd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen); +EXCEPTION_DISPOSITION +win32_close_exception_handler(struct _EXCEPTION_RECORD*, void*, + struct _CONTEXT*, void*); + +void *qemu_win32_map_alloc(size_t size, HANDLE *h, Error **errp); +void qemu_win32_map_free(void *ptr, HANDLE h, Error **errp); + #ifdef __cplusplus } #endif diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h index 4c53537ef3..b5d5fd3463 100644 --- a/include/sysemu/qtest.h +++ b/include/sysemu/qtest.h @@ -14,6 +14,7 @@ #ifndef QTEST_H #define QTEST_H +#include "chardev/char.h" extern bool qtest_allowed; @@ -22,6 +23,10 @@ static inline bool qtest_enabled(void) return qtest_allowed; } +#ifndef CONFIG_USER_ONLY +void qtest_send_prefix(CharBackend *chr); +void G_GNUC_PRINTF(2, 3) qtest_sendf(CharBackend *chr, const char *fmt, ...); +void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words)); bool qtest_driver(void); void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **errp); @@ -31,5 +36,6 @@ void qtest_server_set_send_handler(void (*send)(void *, const char *), void qtest_server_inproc_recv(void *opaque, const char *buf); int64_t qtest_get_virtual_clock(void); +#endif #endif diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h index 73dee9ccdf..f229b2109c 100644 --- a/include/sysemu/replay.h +++ b/include/sysemu/replay.h @@ -1,8 +1,5 @@ -#ifndef REPLAY_H -#define REPLAY_H - /* - * replay.h + * QEMU replay (system interface) * * Copyright (c) 2010-2015 Institute for System Programming * of the Russian Academy of Sciences. @@ -11,10 +8,16 @@ * See the COPYING file in the top-level directory. * */ +#ifndef SYSEMU_REPLAY_H +#define SYSEMU_REPLAY_H +#ifdef CONFIG_USER_ONLY +#error Cannot include this header from user emulation +#endif + +#include "exec/replay-core.h" #include "qapi/qapi-types-misc.h" #include "qapi/qapi-types-run-state.h" -#include "qapi/qapi-types-replay.h" #include "qapi/qapi-types-ui.h" #include "block/aio.h" @@ -45,8 +48,6 @@ typedef enum ReplayCheckpoint ReplayCheckpoint; typedef struct ReplayNetState ReplayNetState; -extern ReplayMode replay_mode; - /* Name of the initial VM snapshot */ extern char *replay_snapshot; @@ -63,40 +64,6 @@ extern char *replay_snapshot; void replay_mutex_lock(void); void replay_mutex_unlock(void); -/* Replay process control functions */ - -/*! Enables recording or saving event log with specified parameters */ -void replay_configure(struct QemuOpts *opts); -/*! Initializes timers used for snapshotting and enables events recording */ -void replay_start(void); -/*! Closes replay log file and frees other resources. */ -void replay_finish(void); -/*! Adds replay blocker with the specified error description */ -void replay_add_blocker(Error *reason); -/* Returns name of the replay log file */ -const char *replay_get_filename(void); -/* - * Start making one step in backward direction. - * Used by gdbstub for backwards debugging. - * Returns true on success. - */ -bool replay_reverse_step(void); -/* - * Start searching the last breakpoint/watchpoint. - * Used by gdbstub for backwards debugging. - * Returns true if the process successfully started. - */ -bool replay_reverse_continue(void); -/* - * Returns true if replay module is processing - * reverse_continue or reverse_step request - */ -bool replay_running_debug(void); -/* Called in reverse debugging mode to collect breakpoint information */ -void replay_breakpoint(void); -/* Called when gdb is attached to gdbstub */ -void replay_gdb_attached(void); - /* Processing the instructions */ /*! Returns number of executed instructions. */ @@ -106,21 +73,10 @@ int replay_get_instructions(void); /*! Updates instructions counter in replay mode. */ void replay_account_executed_instructions(void); -/* Interrupts and exceptions */ - -/*! Called by exception handler to write or read - exception processing events. */ -bool replay_exception(void); -/*! Used to determine that exception is pending. - Does not proceed to the next event in the log. */ -bool replay_has_exception(void); -/*! Called by interrupt handlers to write or read - interrupt processing events. - \return true if interrupt should be processed */ -bool replay_interrupt(void); -/*! Tries to read interrupt event from the file. - Returns true, when interrupt request is pending */ -bool replay_has_interrupt(void); +/** + * replay_can_wait: check if we should pause for wait-io + */ +bool replay_can_wait(void); /* Processing clocks and other time sources */ @@ -131,25 +87,20 @@ int64_t replay_save_clock(ReplayClockKind kind, int64_t clock, int64_t replay_read_clock(ReplayClockKind kind, int64_t raw_icount); /*! Saves or reads the clock depending on the current replay mode. */ #define REPLAY_CLOCK(clock, value) \ + !icount_enabled() ? (value) : \ (replay_mode == REPLAY_MODE_PLAY \ ? replay_read_clock((clock), icount_get_raw()) \ : replay_mode == REPLAY_MODE_RECORD \ ? replay_save_clock((clock), (value), icount_get_raw()) \ : (value)) #define REPLAY_CLOCK_LOCKED(clock, value) \ + !icount_enabled() ? (value) : \ (replay_mode == REPLAY_MODE_PLAY \ ? replay_read_clock((clock), icount_get_raw_locked()) \ : replay_mode == REPLAY_MODE_RECORD \ ? replay_save_clock((clock), (value), icount_get_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. */ @@ -198,7 +149,7 @@ uint64_t blkreplay_next_id(void); /*! Registers char driver to save it's events */ void replay_register_char_driver(struct Chardev *chr); /*! Saves write to char device event to the log */ -void replay_chr_be_write(struct Chardev *s, uint8_t *buf, int len); +void replay_chr_be_write(struct Chardev *s, const uint8_t *buf, int len); /*! Writes char write return value to the replay log. */ void replay_char_write_event_save(int res, int offset); /*! Reads char write return value from the replay log. */ diff --git a/include/sysemu/reset.h b/include/sysemu/reset.h index 0b0d6d7598..ae436044a9 100644 --- a/include/sysemu/reset.h +++ b/include/sysemu/reset.h @@ -1,10 +1,126 @@ +/* + * Reset handlers. + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2016 Red Hat, Inc. + * Copyright (c) 2024 Linaro, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + #ifndef QEMU_SYSEMU_RESET_H #define QEMU_SYSEMU_RESET_H +#include "qapi/qapi-events-run-state.h" + typedef void QEMUResetHandler(void *opaque); +/** + * qemu_register_resettable: Register an object to be reset + * @obj: object to be reset: it must implement the Resettable interface + * + * Register @obj on the list of objects which will be reset when the + * simulation is reset. These objects will be reset in the order + * they were added, using the three-phase Resettable protocol, + * so first all objects go through the enter phase, then all objects + * go through the hold phase, and then finally all go through the + * exit phase. + * + * It is not permitted to register or unregister reset functions or + * resettable objects from within any of the reset phase methods of @obj. + * + * We assume that the caller holds the BQL. + */ +void qemu_register_resettable(Object *obj); + +/** + * qemu_unregister_resettable: Unregister an object to be reset + * @obj: object to unregister + * + * Remove @obj from the list of objects which are reset when the + * simulation is reset. It must have been previously added to + * the list via qemu_register_resettable(). + * + * We assume that the caller holds the BQL. + */ +void qemu_unregister_resettable(Object *obj); + +/** + * qemu_register_reset: Register a callback for system reset + * @func: function to call + * @opaque: opaque data to pass to @func + * + * Register @func on the list of functions which are called when the + * entire system is reset. Functions registered with this API and + * Resettable objects registered with qemu_register_resettable() are + * handled together, in the order in which they were registered. + * Functions registered with this API are called in the 'hold' phase + * of the 3-phase reset. + * + * In general this function should not be used in new code where possible; + * for instance, device model reset is better accomplished using the + * methods on DeviceState. + * + * It is not permitted to register or unregister reset functions or + * resettable objects from within the @func callback. + * + * We assume that the caller holds the BQL. + */ void qemu_register_reset(QEMUResetHandler *func, void *opaque); + +/** + * qemu_register_reset_nosnapshotload: Register a callback for system reset + * @func: function to call + * @opaque: opaque data to pass to @func + * + * This is the same as qemu_register_reset(), except that @func is + * not called if the reason that the system is being reset is to + * put it into a clean state prior to loading a snapshot (i.e. for + * SHUTDOWN_CAUSE_SNAPSHOT_LOAD). + */ +void qemu_register_reset_nosnapshotload(QEMUResetHandler *func, void *opaque); + +/** + * qemu_unregister_reset: Unregister a system reset callback + * @func: function registered with qemu_register_reset() + * @opaque: the same opaque data that was passed to qemu_register_reset() + * + * Undo the effects of a qemu_register_reset(). The @func and @opaque + * must both match the arguments originally used with qemu_register_reset(). + * + * We assume that the caller holds the BQL. + */ void qemu_unregister_reset(QEMUResetHandler *func, void *opaque); -void qemu_devices_reset(void); + +/** + * qemu_devices_reset: Perform a complete system reset + * @reason: reason for the reset + * + * This function performs the low-level work needed to do a complete reset + * of the system (calling all the callbacks registered with + * qemu_register_reset() and resetting all the Resettable objects registered + * with qemu_register_resettable()). It should only be called by the code in a + * MachineClass reset method. + * + * If you want to trigger a system reset from, for instance, a device + * model, don't use this function. Use qemu_system_reset_request(). + */ +void qemu_devices_reset(ShutdownCause reason); #endif diff --git a/include/sysemu/rtc.h b/include/sysemu/rtc.h index 159702b45b..0fc8ad6fdf 100644 --- a/include/sysemu/rtc.h +++ b/include/sysemu/rtc.h @@ -42,7 +42,7 @@ * The behaviour of the clock whose value this function returns will * depend on the -rtc command line option passed by the user. */ -void qemu_get_timedate(struct tm *tm, int offset); +void qemu_get_timedate(struct tm *tm, time_t offset); /** * qemu_timedate_diff: Return difference between a struct tm and the RTC @@ -53,6 +53,6 @@ void qemu_get_timedate(struct tm *tm, int offset); * a timestamp one hour further ahead than the current RTC time * then this function will return 3600. */ -int qemu_timedate_diff(struct tm *tm); +time_t qemu_timedate_diff(struct tm *tm); #endif diff --git a/include/sysemu/runstate-action.h b/include/sysemu/runstate-action.h index cff45a047b..db4e3099ae 100644 --- a/include/sysemu/runstate-action.h +++ b/include/sysemu/runstate-action.h @@ -11,7 +11,7 @@ #include "qapi/qapi-commands-run-state.h" -/* in softmmu/runstate-action.c */ +/* in system/runstate-action.c */ extern RebootAction reboot_action; extern ShutdownAction shutdown_action; extern PanicAction panic_action; diff --git a/include/sysemu/runstate.h b/include/sysemu/runstate.h index f3ed52548e..0117d243c4 100644 --- a/include/sysemu/runstate.h +++ b/include/sysemu/runstate.h @@ -6,9 +6,9 @@ bool runstate_check(RunState state); void runstate_set(RunState new_state); +RunState runstate_get(void); bool runstate_is_running(void); bool runstate_needs_reset(void); -bool runstate_store(char *str, size_t size); typedef void VMChangeStateHandler(void *opaque, bool running, RunState state); @@ -16,9 +16,16 @@ VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, void *opaque); VMChangeStateEntry *qemu_add_vm_change_state_handler_prio( VMChangeStateHandler *cb, void *opaque, int priority); +VMChangeStateEntry * +qemu_add_vm_change_state_handler_prio_full(VMChangeStateHandler *cb, + VMChangeStateHandler *prepare_cb, + void *opaque, int priority); VMChangeStateEntry *qdev_add_vm_change_state_handler(DeviceState *dev, VMChangeStateHandler *cb, void *opaque); +VMChangeStateEntry *qdev_add_vm_change_state_handler_full( + DeviceState *dev, VMChangeStateHandler *cb, + VMChangeStateHandler *prepare_cb, void *opaque); void qemu_del_vm_change_state_handler(VMChangeStateEntry *e); /** * vm_state_notify: Notify the state of the VM @@ -33,6 +40,15 @@ static inline bool shutdown_caused_by_guest(ShutdownCause cause) return cause >= SHUTDOWN_CAUSE_GUEST_SHUTDOWN; } +/* + * In a "live" state, the vcpu clock is ticking, and the runstate notifiers + * think we are running. + */ +static inline bool runstate_is_live(RunState state) +{ + return state == RUN_STATE_RUNNING || state == RUN_STATE_SUSPENDED; +} + void vm_start(void); /** @@ -41,9 +57,20 @@ void vm_start(void); * @step_pending: whether any of the CPUs is about to be single-stepped by gdb */ int vm_prepare_start(bool step_pending); + +/** + * vm_resume: If @state is a live state, start the vm and set the state, + * else just set the state. + * + * @state: the state to restore + */ +void vm_resume(RunState state); + int vm_stop(RunState state); int vm_stop_force_state(RunState state); int vm_shutdown(void); +void vm_set_suspended(bool suspended); +bool vm_get_suspended(void); typedef enum WakeupReason { /* Always keep QEMU_WAKEUP_REASON_NONE = 0 */ @@ -61,6 +88,8 @@ void qemu_system_wakeup_request(WakeupReason reason, Error **errp); void qemu_system_wakeup_enable(WakeupReason reason, bool enabled); void qemu_register_wakeup_notifier(Notifier *notifier); void qemu_register_wakeup_support(void); +void qemu_system_shutdown_request_with_code(ShutdownCause reason, + int exit_code); void qemu_system_shutdown_request(ShutdownCause reason); void qemu_system_powerdown_request(void); void qemu_register_powerdown_notifier(Notifier *notifier); diff --git a/include/sysemu/stats.h b/include/sysemu/stats.h new file mode 100644 index 0000000000..42c236c795 --- /dev/null +++ b/include/sysemu/stats.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#ifndef STATS_H +#define STATS_H + +#include "qapi/qapi-types-stats.h" + +typedef void StatRetrieveFunc(StatsResultList **result, StatsTarget target, + strList *names, strList *targets, Error **errp); +typedef void SchemaRetrieveFunc(StatsSchemaList **result, Error **errp); + +/* + * Register callbacks for the QMP query-stats command. + * + * @provider: stats provider checked against QMP command arguments + * @stats_fn: routine to query stats: + * @schema_fn: routine to query stat schemas: + */ +void add_stats_callbacks(StatsProvider provider, + StatRetrieveFunc *stats_fn, + SchemaRetrieveFunc *schemas_fn); + +/* + * Helper routines for adding stats entries to the results lists. + */ +void add_stats_entry(StatsResultList **, StatsProvider, const char *id, + StatsList *stats_list); +void add_stats_schema(StatsSchemaList **, StatsProvider, StatsTarget, + StatsSchemaValueList *); + +/* + * True if a string matches the filter passed to the stats_fn callback, + * false otherwise. + * + * Note that an empty list means no filtering, i.e. all strings will + * return true. + */ +bool apply_str_list_filter(const char *string, strList *list); + +#endif /* STATS_H */ diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 812f66a31a..eb1dc1e4ed 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -41,7 +41,6 @@ extern int graphic_height; extern int graphic_depth; extern int display_opengl; extern const char *keyboard_layout; -extern int win2k_install_hack; extern int graphic_rotate; extern int old_param; extern uint8_t *boot_splash_filedata; @@ -61,9 +60,6 @@ extern int nb_option_roms; extern const char *prom_envs[MAX_PROM_ENVS]; extern unsigned int nb_prom_envs; -/* pcie aer error injection */ -void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict); - /* serial ports */ /* Return the Chardev for serial port i, or NULL if none */ @@ -102,9 +98,9 @@ void qemu_boot_set(const char *boot_order, Error **errp); bool defaults_enabled(void); -void qemu_init(int argc, char **argv, char **envp); -void qemu_main_loop(void); -void qemu_cleanup(void); +void qemu_init(int argc, char **argv); +int qemu_main_loop(void); +void qemu_cleanup(int); extern QemuOptsList qemu_legacy_drive_opts; extern QemuOptsList qemu_common_drive_opts; diff --git a/include/sysemu/tcg.h b/include/sysemu/tcg.h index 53352450ff..5e2ca9aab3 100644 --- a/include/sysemu/tcg.h +++ b/include/sysemu/tcg.h @@ -5,6 +5,8 @@ * See the COPYING file in the top-level directory. */ +/* header to be included in non-TCG-specific code */ + #ifndef SYSEMU_TCG_H #define SYSEMU_TCG_H diff --git a/include/sysemu/tpm.h b/include/sysemu/tpm.h index fb40e30ff6..1ee568b3b6 100644 --- a/include/sysemu/tpm.h +++ b/include/sysemu/tpm.h @@ -17,7 +17,7 @@ #ifdef CONFIG_TPM -int tpm_config_parse(QemuOptsList *opts_list, const char *optarg); +int tpm_config_parse(QemuOptsList *opts_list, const char *optstr); int tpm_init(void); void tpm_cleanup(void); @@ -48,6 +48,7 @@ struct TPMIfClass { #define TYPE_TPM_TIS_SYSBUS "tpm-tis-device" #define TYPE_TPM_CRB "tpm-crb" #define TYPE_TPM_SPAPR "tpm-spapr" +#define TYPE_TPM_TIS_I2C "tpm-tis-i2c" #define TPM_IS_TIS_ISA(chr) \ object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS_ISA) @@ -57,6 +58,8 @@ struct TPMIfClass { object_dynamic_cast(OBJECT(chr), TYPE_TPM_CRB) #define TPM_IS_SPAPR(chr) \ object_dynamic_cast(OBJECT(chr), TYPE_TPM_SPAPR) +#define TPM_IS_TIS_I2C(chr) \ + object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS_I2C) /* returns NULL unless there is exactly one TPM device */ static inline TPMIf *tpm_find(void) diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h index 8fd3269c11..7fabafefee 100644 --- a/include/sysemu/tpm_backend.h +++ b/include/sysemu/tpm_backend.h @@ -115,7 +115,7 @@ int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize); /** * tpm_backend_had_startup_error: - * @s: the backend to query for a statup error + * @s: the backend to query for a startup error * * Check whether the backend had an error during startup. Returns * false if no error occurred and the backend can be used, true diff --git a/include/sysemu/watchdog.h b/include/sysemu/watchdog.h index d2d4901dbb..745c89b02b 100644 --- a/include/sysemu/watchdog.h +++ b/include/sysemu/watchdog.h @@ -25,20 +25,8 @@ #include "qemu/queue.h" #include "qapi/qapi-types-run-state.h" -struct WatchdogTimerModel { - QLIST_ENTRY(WatchdogTimerModel) entry; - - /* Short name of the device - used to select it on the command line. */ - const char *wdt_name; - /* Longer description (eg. manufacturer and full model number). */ - const char *wdt_description; -}; -typedef struct WatchdogTimerModel WatchdogTimerModel; - /* in hw/watchdog.c */ -int select_watchdog(const char *p); WatchdogAction get_watchdog_action(void); -void watchdog_add_model(WatchdogTimerModel *model); void watchdog_perform_action(void); #endif /* QEMU_WATCHDOG_H */ diff --git a/include/sysemu/whpx.h b/include/sysemu/whpx.h index 2889fa2278..781ca5b2b6 100644 --- a/include/sysemu/whpx.h +++ b/include/sysemu/whpx.h @@ -10,6 +10,8 @@ * */ +/* header to be included in non-WHPX-specific code */ + #ifndef QEMU_WHPX_H #define QEMU_WHPX_H diff --git a/include/sysemu/xen-mapcache.h b/include/sysemu/xen-mapcache.h index c8e7c2f6cf..10c2e3082a 100644 --- a/include/sysemu/xen-mapcache.h +++ b/include/sysemu/xen-mapcache.h @@ -10,10 +10,11 @@ #define XEN_MAPCACHE_H #include "exec/cpu-common.h" +#include "sysemu/xen.h" typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr phys_offset, ram_addr_t size); -#ifdef CONFIG_XEN +#ifdef CONFIG_XEN_IS_POSSIBLE void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque); diff --git a/include/sysemu/xen.h b/include/sysemu/xen.h index 0ca25697e4..a9f591f26d 100644 --- a/include/sysemu/xen.h +++ b/include/sysemu/xen.h @@ -5,9 +5,15 @@ * See the COPYING file in the top-level directory. */ +/* header to be included in non-Xen-specific code */ + #ifndef SYSEMU_XEN_H #define SYSEMU_XEN_H +#ifdef CONFIG_USER_ONLY +#error Cannot include sysemu/xen.h from user emulation +#endif + #include "exec/cpu-common.h" #ifdef NEED_CPU_H @@ -24,16 +30,13 @@ extern bool xen_allowed; #define xen_enabled() (xen_allowed) -#ifndef CONFIG_USER_ONLY void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length); void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, struct MemoryRegion *mr, Error **errp); -#endif #else /* !CONFIG_XEN_IS_POSSIBLE */ #define xen_enabled() 0 -#ifndef CONFIG_USER_ONLY static inline void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) { /* nothing */ @@ -43,7 +46,6 @@ static inline void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, { g_assert_not_reached(); } -#endif #endif /* CONFIG_XEN_IS_POSSIBLE */ diff --git a/include/tcg/debug-assert.h b/include/tcg/debug-assert.h new file mode 100644 index 0000000000..596765a3d2 --- /dev/null +++ b/include/tcg/debug-assert.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define tcg_debug_assert + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_DEBUG_ASSERT_H +#define TCG_DEBUG_ASSERT_H + +#if defined CONFIG_DEBUG_TCG || defined QEMU_STATIC_ANALYSIS +# define tcg_debug_assert(X) do { assert(X); } while (0) +#else +# define tcg_debug_assert(X) \ + do { if (!(X)) { __builtin_unreachable(); } } while (0) +#endif + +#endif diff --git a/include/tcg/debuginfo.h b/include/tcg/debuginfo.h new file mode 100644 index 0000000000..858535b5da --- /dev/null +++ b/include/tcg/debuginfo.h @@ -0,0 +1,79 @@ +/* + * Debug information support. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TCG_DEBUGINFO_H +#define TCG_DEBUGINFO_H + +#include "qemu/bitops.h" + +/* + * Debuginfo describing a certain address. + */ +struct debuginfo_query { + uint64_t address; /* Input: address. */ + int flags; /* Input: debuginfo subset. */ + const char *symbol; /* Symbol that the address is part of. */ + uint64_t offset; /* Offset from the symbol. */ + const char *file; /* Source file associated with the address. */ + int line; /* Line number in the source file. */ +}; + +/* + * Debuginfo subsets. + */ +#define DEBUGINFO_SYMBOL BIT(1) +#define DEBUGINFO_LINE BIT(2) + +#if defined(CONFIG_TCG) && defined(CONFIG_LIBDW) +/* + * Load debuginfo for the specified guest ELF image. + * Return true on success, false on failure. + */ +void debuginfo_report_elf(const char *name, int fd, uint64_t bias); + +/* + * Take the debuginfo lock. + */ +void debuginfo_lock(void); + +/* + * Fill each on N Qs with the debuginfo about Q->ADDRESS as specified by + * Q->FLAGS: + * + * - DEBUGINFO_SYMBOL: update Q->SYMBOL and Q->OFFSET. If symbol debuginfo is + * missing, then leave them as is. + * - DEBUINFO_LINE: update Q->FILE and Q->LINE. If line debuginfo is missing, + * then leave them as is. + * + * This function must be called under the debuginfo lock. The results can be + * accessed only until the debuginfo lock is released. + */ +void debuginfo_query(struct debuginfo_query *q, size_t n); + +/* + * Release the debuginfo lock. + */ +void debuginfo_unlock(void); +#else +static inline void debuginfo_report_elf(const char *image_name, int image_fd, + uint64_t load_bias) +{ +} + +static inline void debuginfo_lock(void) +{ +} + +static inline void debuginfo_query(struct debuginfo_query *q, size_t n) +{ +} + +static inline void debuginfo_unlock(void) +{ +} +#endif + +#endif diff --git a/include/tcg/helper-info.h b/include/tcg/helper-info.h new file mode 100644 index 0000000000..7c27d6164a --- /dev/null +++ b/include/tcg/helper-info.h @@ -0,0 +1,64 @@ +/* + * TCG Helper Information Structure + * + * Copyright (c) 2023 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TCG_HELPER_INFO_H +#define TCG_HELPER_INFO_H + +#ifdef CONFIG_TCG_INTERPRETER +#include +#endif + +/* + * Describe the calling convention of a given argument type. + */ +typedef enum { + TCG_CALL_RET_NORMAL, /* by registers */ + TCG_CALL_RET_BY_REF, /* for i128, by reference */ + TCG_CALL_RET_BY_VEC, /* for i128, by vector register */ +} TCGCallReturnKind; + +typedef enum { + TCG_CALL_ARG_NORMAL, /* by registers (continuing onto stack) */ + TCG_CALL_ARG_EVEN, /* like normal, but skipping odd slots */ + TCG_CALL_ARG_EXTEND, /* for i32, as a sign/zero-extended i64 */ + TCG_CALL_ARG_EXTEND_U, /* ... as a zero-extended i64 */ + TCG_CALL_ARG_EXTEND_S, /* ... as a sign-extended i64 */ + TCG_CALL_ARG_BY_REF, /* for i128, by reference, first */ + TCG_CALL_ARG_BY_REF_N, /* ... by reference, subsequent */ +} TCGCallArgumentKind; + +typedef struct TCGCallArgumentLoc { + TCGCallArgumentKind kind : 8; + unsigned arg_slot : 8; + unsigned ref_slot : 8; + unsigned arg_idx : 4; + unsigned tmp_subindex : 2; +} TCGCallArgumentLoc; + +struct TCGHelperInfo { + void *func; + const char *name; + + /* Used with g_once_init_enter. */ +#ifdef CONFIG_TCG_INTERPRETER + ffi_cif *cif; +#else + uintptr_t init; +#endif + + unsigned typemask : 32; + unsigned flags : 8; + unsigned nr_in : 8; + unsigned nr_out : 8; + TCGCallReturnKind out_kind : 8; + + /* Maximum physical arguments are constrained by TCG_TYPE_I128. */ + TCGCallArgumentLoc in[MAX_CALL_IARGS * (128 / TCG_TARGET_REG_BITS)]; +}; + +#endif /* TCG_HELPER_INFO_H */ diff --git a/include/tcg/insn-start-words.h b/include/tcg/insn-start-words.h new file mode 100644 index 0000000000..50c18bd326 --- /dev/null +++ b/include/tcg/insn-start-words.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define TARGET_INSN_START_WORDS + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TARGET_INSN_START_WORDS + +#include "cpu.h" + +#ifndef TARGET_INSN_START_EXTRA_WORDS +# define TARGET_INSN_START_WORDS 1 +#else +# define TARGET_INSN_START_WORDS (1 + TARGET_INSN_START_EXTRA_WORDS) +#endif + +#endif /* TARGET_INSN_START_WORDS */ diff --git a/include/tcg/oversized-guest.h b/include/tcg/oversized-guest.h new file mode 100644 index 0000000000..641b9749ff --- /dev/null +++ b/include/tcg/oversized-guest.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define TCG_OVERSIZED_GUEST + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef EXEC_TCG_OVERSIZED_GUEST_H +#define EXEC_TCG_OVERSIZED_GUEST_H + +#include "tcg-target-reg-bits.h" +#include "cpu-param.h" + +/* + * Oversized TCG guests make things like MTTCG hard + * as we can't use atomics for cputlb updates. + */ +#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS +#define TCG_OVERSIZED_GUEST 1 +#else +#define TCG_OVERSIZED_GUEST 0 +#endif + +#endif diff --git a/include/tcg/perf.h b/include/tcg/perf.h new file mode 100644 index 0000000000..c96b5920a3 --- /dev/null +++ b/include/tcg/perf.h @@ -0,0 +1,49 @@ +/* + * Linux perf perf-.map and jit-.dump integration. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TCG_PERF_H +#define TCG_PERF_H + +#if defined(CONFIG_TCG) && defined(CONFIG_LINUX) +/* Start writing perf-.map. */ +void perf_enable_perfmap(void); + +/* Start writing jit-.dump. */ +void perf_enable_jitdump(void); + +/* Add information about TCG prologue to profiler maps. */ +void perf_report_prologue(const void *start, size_t size); + +/* Add information about JITted guest code to profiler maps. */ +void perf_report_code(uint64_t guest_pc, TranslationBlock *tb, + const void *start); + +/* Stop writing perf-.map and/or jit-.dump. */ +void perf_exit(void); +#else +static inline void perf_enable_perfmap(void) +{ +} + +static inline void perf_enable_jitdump(void) +{ +} + +static inline void perf_report_prologue(const void *start, size_t size) +{ +} + +static inline void perf_report_code(uint64_t guest_pc, TranslationBlock *tb, + const void *start) +{ +} + +static inline void perf_exit(void) +{ +} +#endif + +#endif diff --git a/include/tcg/startup.h b/include/tcg/startup.h new file mode 100644 index 0000000000..f71305765c --- /dev/null +++ b/include/tcg/startup.h @@ -0,0 +1,58 @@ +/* + * Tiny Code Generator for QEMU: definitions used by runtime startup + * + * Copyright (c) 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. + */ + +#ifndef TCG_STARTUP_H +#define TCG_STARTUP_H + +/** + * tcg_init: Initialize the TCG runtime + * @tb_size: translation buffer size + * @splitwx: use separate rw and rx mappings + * @max_cpus: number of vcpus in system mode + * + * Allocate and initialize TCG resources, especially the JIT buffer. + * In user-only mode, @max_cpus is unused. + */ +void tcg_init(size_t tb_size, int splitwx, unsigned max_cpus); + +/** + * tcg_register_thread: Register this thread with the TCG runtime + * + * All TCG threads except the parent (i.e. the one that called the TCG + * accelerator's init_machine() method) must register with this + * function before initiating translation. + */ +void tcg_register_thread(void); + +/** + * tcg_prologue_init(): Generate the code for the TCG prologue + * + * In softmmu this is done automatically as part of the TCG + * accelerator's init_machine() method, but for user-mode, the + * user-mode code must call this function after it has loaded + * the guest binary and the value of guest_base is known. + */ +void tcg_prologue_init(void); + +#endif diff --git a/include/tcg/tcg-cond.h b/include/tcg/tcg-cond.h index 2a38a386d4..5cadbd6ff2 100644 --- a/include/tcg/tcg-cond.h +++ b/include/tcg/tcg-cond.h @@ -29,26 +29,34 @@ * Conditions. Note that these are laid out for easy manipulation by * the functions below: * bit 0 is used for inverting; - * bit 1 is signed, - * bit 2 is unsigned, - * bit 3 is used with bit 0 for swapping signed/unsigned. + * bit 1 is used for conditions that need swapping (signed/unsigned). + * bit 2 is used with bit 1 for swapping. + * bit 3 is used for unsigned conditions. */ typedef enum { /* non-signed */ TCG_COND_NEVER = 0 | 0 | 0 | 0, TCG_COND_ALWAYS = 0 | 0 | 0 | 1, + + /* equality */ TCG_COND_EQ = 8 | 0 | 0 | 0, TCG_COND_NE = 8 | 0 | 0 | 1, + + /* "test" i.e. and then compare vs 0 */ + TCG_COND_TSTEQ = 8 | 4 | 0 | 0, + TCG_COND_TSTNE = 8 | 4 | 0 | 1, + /* signed */ TCG_COND_LT = 0 | 0 | 2 | 0, TCG_COND_GE = 0 | 0 | 2 | 1, - TCG_COND_LE = 8 | 0 | 2 | 0, - TCG_COND_GT = 8 | 0 | 2 | 1, + TCG_COND_GT = 0 | 4 | 2 | 0, + TCG_COND_LE = 0 | 4 | 2 | 1, + /* unsigned */ - TCG_COND_LTU = 0 | 4 | 0 | 0, - TCG_COND_GEU = 0 | 4 | 0 | 1, - TCG_COND_LEU = 8 | 4 | 0 | 0, - TCG_COND_GTU = 8 | 4 | 0 | 1, + TCG_COND_LTU = 8 | 0 | 2 | 0, + TCG_COND_GEU = 8 | 0 | 2 | 1, + TCG_COND_GTU = 8 | 4 | 2 | 0, + TCG_COND_LEU = 8 | 4 | 2 | 1, } TCGCond; /* Invert the sense of the comparison. */ @@ -60,25 +68,49 @@ static inline TCGCond tcg_invert_cond(TCGCond c) /* Swap the operands in a comparison. */ static inline TCGCond tcg_swap_cond(TCGCond c) { - return c & 6 ? (TCGCond)(c ^ 9) : c; + return (TCGCond)(c ^ ((c & 2) << 1)); } -/* Create an "unsigned" version of a "signed" comparison. */ -static inline TCGCond tcg_unsigned_cond(TCGCond c) +/* Must a comparison be considered signed? */ +static inline bool is_signed_cond(TCGCond c) { - return c & 2 ? (TCGCond)(c ^ 6) : c; -} - -/* Create a "signed" version of an "unsigned" comparison. */ -static inline TCGCond tcg_signed_cond(TCGCond c) -{ - return c & 4 ? (TCGCond)(c ^ 6) : c; + return (c & (8 | 2)) == 2; } /* Must a comparison be considered unsigned? */ static inline bool is_unsigned_cond(TCGCond c) { - return (c & 4) != 0; + return (c & (8 | 2)) == (8 | 2); +} + +/* Must a comparison be considered a test? */ +static inline bool is_tst_cond(TCGCond c) +{ + return (c | 1) == TCG_COND_TSTNE; +} + +/* Create an "unsigned" version of a "signed" comparison. */ +static inline TCGCond tcg_unsigned_cond(TCGCond c) +{ + return is_signed_cond(c) ? (TCGCond)(c + 8) : c; +} + +/* Create a "signed" version of an "unsigned" comparison. */ +static inline TCGCond tcg_signed_cond(TCGCond c) +{ + return is_unsigned_cond(c) ? (TCGCond)(c - 8) : c; +} + +/* Create the eq/ne version of a tsteq/tstne comparison. */ +static inline TCGCond tcg_tst_eqne_cond(TCGCond c) +{ + return is_tst_cond(c) ? (TCGCond)(c - 4) : c; +} + +/* Create the lt/ge version of a tstne/tsteq comparison of the sign. */ +static inline TCGCond tcg_tst_ltge_cond(TCGCond c) +{ + return is_tst_cond(c) ? (TCGCond)(c ^ 0xf) : c; } /* @@ -92,7 +124,7 @@ static inline TCGCond tcg_high_cond(TCGCond c) case TCG_COND_LE: case TCG_COND_GEU: case TCG_COND_LEU: - return (TCGCond)(c ^ 8); + return (TCGCond)(c ^ (4 | 1)); default: return c; } diff --git a/include/tcg/tcg-ldst.h b/include/tcg/tcg-ldst.h index 2ba22bd5fe..6ccfe9131d 100644 --- a/include/tcg/tcg-ldst.h +++ b/include/tcg/tcg-ldst.h @@ -25,55 +25,39 @@ #ifndef TCG_LDST_H #define TCG_LDST_H -#ifdef CONFIG_SOFTMMU - /* Value zero-extended to tcg register size. */ -tcg_target_ulong helper_ret_ldub_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_le_lduw_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_le_ldul_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -uint64_t helper_le_ldq_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_be_lduw_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_be_ldul_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -uint64_t helper_be_ldq_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); +tcg_target_ulong helper_ldub_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr); +tcg_target_ulong helper_lduw_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr); +tcg_target_ulong helper_ldul_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr); +uint64_t helper_ldq_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr); +Int128 helper_ld16_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr); /* Value sign-extended to tcg register size. */ -tcg_target_ulong helper_ret_ldsb_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_le_ldsw_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_le_ldsl_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_be_ldsw_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); -tcg_target_ulong helper_be_ldsl_mmu(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t retaddr); +tcg_target_ulong helper_ldsb_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr); +tcg_target_ulong helper_ldsw_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr); +tcg_target_ulong helper_ldsl_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr); -void helper_ret_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val, - MemOpIdx oi, uintptr_t retaddr); -void helper_le_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val, - MemOpIdx oi, uintptr_t retaddr); -void helper_le_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, - MemOpIdx oi, uintptr_t retaddr); -void helper_le_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr); -void helper_be_stw_mmu(CPUArchState *env, target_ulong addr, uint16_t val, - MemOpIdx oi, uintptr_t retaddr); -void helper_be_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, - MemOpIdx oi, uintptr_t retaddr); -void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr); +/* + * Value extended to at least uint32_t, so that some ABIs do not require + * zero-extension from uint8_t or uint16_t. + */ +void helper_stb_mmu(CPUArchState *env, uint64_t addr, uint32_t val, + MemOpIdx oi, uintptr_t retaddr); +void helper_stw_mmu(CPUArchState *env, uint64_t addr, uint32_t val, + MemOpIdx oi, uintptr_t retaddr); +void helper_stl_mmu(CPUArchState *env, uint64_t addr, uint32_t val, + MemOpIdx oi, uintptr_t retaddr); +void helper_stq_mmu(CPUArchState *env, uint64_t addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr); +void helper_st16_mmu(CPUArchState *env, uint64_t addr, Int128 val, + MemOpIdx oi, uintptr_t retaddr); -#else - -G_NORETURN void helper_unaligned_ld(CPUArchState *env, target_ulong addr); -G_NORETURN void helper_unaligned_st(CPUArchState *env, target_ulong addr); - -#endif /* CONFIG_SOFTMMU */ #endif /* TCG_LDST_H */ diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h new file mode 100644 index 0000000000..2d932a515e --- /dev/null +++ b/include/tcg/tcg-op-common.h @@ -0,0 +1,561 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Target independent opcode generation functions. + * + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TCG_OP_COMMON_H +#define TCG_TCG_OP_COMMON_H + +#include "tcg/tcg.h" +#include "exec/helper-proto-common.h" +#include "exec/helper-gen-common.h" + +TCGv_i32 tcg_constant_i32(int32_t val); +TCGv_i64 tcg_constant_i64(int64_t val); +TCGv_vec tcg_constant_vec(TCGType type, unsigned vece, int64_t val); +TCGv_vec tcg_constant_vec_matching(TCGv_vec match, unsigned vece, int64_t val); + +TCGv_i32 tcg_temp_new_i32(void); +TCGv_i64 tcg_temp_new_i64(void); +TCGv_ptr tcg_temp_new_ptr(void); +TCGv_i128 tcg_temp_new_i128(void); +TCGv_vec tcg_temp_new_vec(TCGType type); +TCGv_vec tcg_temp_new_vec_matching(TCGv_vec match); + +TCGv_i32 tcg_global_mem_new_i32(TCGv_ptr reg, intptr_t off, const char *name); +TCGv_i64 tcg_global_mem_new_i64(TCGv_ptr reg, intptr_t off, const char *name); +TCGv_ptr tcg_global_mem_new_ptr(TCGv_ptr reg, intptr_t off, const char *name); + +/* Generic ops. */ + +void gen_set_label(TCGLabel *l); +void tcg_gen_br(TCGLabel *l); +void tcg_gen_mb(TCGBar); + +/** + * tcg_gen_exit_tb() - output exit_tb TCG operation + * @tb: The TranslationBlock from which we are exiting + * @idx: Direct jump slot index, or exit request + * + * See tcg/README for more info about this TCG operation. + * See also tcg.h and the block comment above TB_EXIT_MASK. + * + * For a normal exit from the TB, back to the main loop, @tb should + * be NULL and @idx should be 0. Otherwise, @tb should be valid and + * @idx should be one of the TB_EXIT_ values. + */ +void tcg_gen_exit_tb(const TranslationBlock *tb, unsigned idx); + +/** + * tcg_gen_goto_tb() - output goto_tb TCG operation + * @idx: Direct jump slot index (0 or 1) + * + * See tcg/README for more info about this TCG operation. + * + * NOTE: In system emulation, direct jumps with goto_tb are only safe within + * the pages this TB resides in because we don't take care of direct jumps when + * address mapping changes, e.g. in tlb_flush(). In user mode, there's only a + * static address translation, so the destination address is always valid, TBs + * are always invalidated properly, and direct jumps are reset when mapping + * changes. + */ +void tcg_gen_goto_tb(unsigned idx); + +/** + * tcg_gen_lookup_and_goto_ptr() - look up the current TB, jump to it if valid + * @addr: Guest address of the target TB + * + * If the TB is not valid, jump to the epilogue. + * + * This operation is optional. If the TCG backend does not implement goto_ptr, + * this op is equivalent to calling tcg_gen_exit_tb() with 0 as the argument. + */ +void tcg_gen_lookup_and_goto_ptr(void); + +void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr); +void tcg_gen_plugin_cb_end(void); + +/* 32 bit ops */ + +void tcg_gen_movi_i32(TCGv_i32 ret, int32_t arg); +void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2); +void tcg_gen_subi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_sari_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_clz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_clzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2); +void tcg_gen_ctzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2); +void tcg_gen_clrsb_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_ctpop_i32(TCGv_i32 a1, TCGv_i32 a2); +void tcg_gen_rotl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_rotli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_rotr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_rotri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); +void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, + unsigned int ofs, unsigned int len); +void tcg_gen_deposit_z_i32(TCGv_i32 ret, TCGv_i32 arg, + unsigned int ofs, unsigned int len); +void tcg_gen_extract_i32(TCGv_i32 ret, TCGv_i32 arg, + unsigned int ofs, unsigned int len); +void tcg_gen_sextract_i32(TCGv_i32 ret, TCGv_i32 arg, + unsigned int ofs, unsigned int len); +void tcg_gen_extract2_i32(TCGv_i32 ret, TCGv_i32 al, TCGv_i32 ah, + unsigned int ofs); +void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, TCGv_i32 arg2, TCGLabel *); +void tcg_gen_brcondi_i32(TCGCond cond, TCGv_i32 arg1, int32_t arg2, TCGLabel *); +void tcg_gen_setcond_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_setcondi_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, int32_t arg2); +void tcg_gen_negsetcond_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_negsetcondi_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, int32_t arg2); +void tcg_gen_movcond_i32(TCGCond cond, TCGv_i32 ret, TCGv_i32 c1, + TCGv_i32 c2, TCGv_i32 v1, TCGv_i32 v2); +void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, + TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh); +void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, + TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh); +void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_mulsu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_ext8s_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_ext16s_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_ext8u_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_ext_i32(TCGv_i32 ret, TCGv_i32 val, MemOp opc); +void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg, int flags); +void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_hswap_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_smin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_smax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_umin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_umax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_abs_i32(TCGv_i32, TCGv_i32); + +/* Replicate a value of size @vece from @in to all the lanes in @out */ +void tcg_gen_dup_i32(unsigned vece, TCGv_i32 out, TCGv_i32 in); + +void tcg_gen_discard_i32(TCGv_i32 arg); +void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg); + +void tcg_gen_ld8u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld16u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld16s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset); + +void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_st16_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset); + +void tcg_gen_add_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_sub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_or_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_xor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_shl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_shr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_sar_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_mul_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg); + +/* 64 bit ops */ + +void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg); +void tcg_gen_addi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_subfi_i64(TCGv_i64 ret, int64_t arg1, TCGv_i64 arg2); +void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_ori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_eqv_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_nand_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_nor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_clz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_ctz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_clzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2); +void tcg_gen_ctzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2); +void tcg_gen_clrsb_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ctpop_i64(TCGv_i64 a1, TCGv_i64 a2); +void tcg_gen_rotl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_rotli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_rotr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_rotri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); +void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, + unsigned int ofs, unsigned int len); +void tcg_gen_deposit_z_i64(TCGv_i64 ret, TCGv_i64 arg, + unsigned int ofs, unsigned int len); +void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg, + unsigned int ofs, unsigned int len); +void tcg_gen_sextract_i64(TCGv_i64 ret, TCGv_i64 arg, + unsigned int ofs, unsigned int len); +void tcg_gen_extract2_i64(TCGv_i64 ret, TCGv_i64 al, TCGv_i64 ah, + unsigned int ofs); +void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, TCGv_i64 arg2, TCGLabel *); +void tcg_gen_brcondi_i64(TCGCond cond, TCGv_i64 arg1, int64_t arg2, TCGLabel *); +void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_setcondi_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, int64_t arg2); +void tcg_gen_negsetcond_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_negsetcondi_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, int64_t arg2); +void tcg_gen_movcond_i64(TCGCond cond, TCGv_i64 ret, TCGv_i64 c1, + TCGv_i64 c2, TCGv_i64 v1, TCGv_i64 v2); +void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, + TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh); +void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, + TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh); +void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_mulsu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext8s_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext16s_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext32s_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext8u_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext16u_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ext_i64(TCGv_i64 ret, TCGv_i64 val, MemOp opc); +void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg, int flags); +void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags); +void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_hswap_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_wswap_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_smin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_smax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_umin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_umax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_abs_i64(TCGv_i64, TCGv_i64); + +/* Replicate a value of size @vece from @in to all the lanes in @out */ +void tcg_gen_dup_i64(unsigned vece, TCGv_i64 out, TCGv_i64 in); + +void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); + +void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); + +void tcg_gen_discard_i64(TCGv_i64 arg); +void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); +void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg); + + +/* Size changing operations. */ + +void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg); +void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg); +void tcg_gen_concat_i32_i64(TCGv_i64 dest, TCGv_i32 low, TCGv_i32 high); +void tcg_gen_extrl_i64_i32(TCGv_i32 ret, TCGv_i64 arg); +void tcg_gen_extrh_i64_i32(TCGv_i32 ret, TCGv_i64 arg); +void tcg_gen_extr_i64_i32(TCGv_i32 lo, TCGv_i32 hi, TCGv_i64 arg); +void tcg_gen_extr32_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i64 arg); +void tcg_gen_concat32_i64(TCGv_i64 ret, TCGv_i64 lo, TCGv_i64 hi); + +void tcg_gen_extr_i128_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i128 arg); +void tcg_gen_concat_i64_i128(TCGv_i128 ret, TCGv_i64 lo, TCGv_i64 hi); + +/* 128 bit ops */ + +void tcg_gen_mov_i128(TCGv_i128 dst, TCGv_i128 src); +void tcg_gen_ld_i128(TCGv_i128 ret, TCGv_ptr base, tcg_target_long offset); +void tcg_gen_st_i128(TCGv_i128 val, TCGv_ptr base, tcg_target_long offset); + +/* Local load/store bit ops */ + +void tcg_gen_qemu_ld_i32_chk(TCGv_i32, TCGTemp *, TCGArg, MemOp, TCGType); +void tcg_gen_qemu_st_i32_chk(TCGv_i32, TCGTemp *, TCGArg, MemOp, TCGType); +void tcg_gen_qemu_ld_i64_chk(TCGv_i64, TCGTemp *, TCGArg, MemOp, TCGType); +void tcg_gen_qemu_st_i64_chk(TCGv_i64, TCGTemp *, TCGArg, MemOp, TCGType); +void tcg_gen_qemu_ld_i128_chk(TCGv_i128, TCGTemp *, TCGArg, MemOp, TCGType); +void tcg_gen_qemu_st_i128_chk(TCGv_i128, TCGTemp *, TCGArg, MemOp, TCGType); + +/* Atomic ops */ + +void tcg_gen_atomic_cmpxchg_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_cmpxchg_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_cmpxchg_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, + TCGv_i128, TCGArg, MemOp, TCGType); + +void tcg_gen_nonatomic_cmpxchg_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_nonatomic_cmpxchg_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_nonatomic_cmpxchg_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, + TCGv_i128, TCGArg, MemOp, TCGType); + +void tcg_gen_atomic_xchg_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_xchg_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); + +void tcg_gen_atomic_fetch_add_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_add_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_and_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_and_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_or_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_or_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_xor_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_xor_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_smin_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_smin_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_umin_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_umin_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_smax_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_smax_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_umax_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_umax_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); + +void tcg_gen_atomic_add_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_add_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_and_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_and_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_or_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_or_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_xor_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_xor_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_smin_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_smin_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_umin_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_umin_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_smax_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_smax_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_umax_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_umax_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, + TCGArg, MemOp, TCGType); + +/* Vector ops */ + +void tcg_gen_mov_vec(TCGv_vec, TCGv_vec); +void tcg_gen_dup_i32_vec(unsigned vece, TCGv_vec, TCGv_i32); +void tcg_gen_dup_i64_vec(unsigned vece, TCGv_vec, TCGv_i64); +void tcg_gen_dup_mem_vec(unsigned vece, TCGv_vec, TCGv_ptr, tcg_target_long); +void tcg_gen_dupi_vec(unsigned vece, TCGv_vec, uint64_t); +void tcg_gen_add_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_sub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_mul_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_and_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_or_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_xor_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_andc_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_orc_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_nand_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_nor_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_eqv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_not_vec(unsigned vece, TCGv_vec r, TCGv_vec a); +void tcg_gen_neg_vec(unsigned vece, TCGv_vec r, TCGv_vec a); +void tcg_gen_abs_vec(unsigned vece, TCGv_vec r, TCGv_vec a); +void tcg_gen_ssadd_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_usadd_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_sssub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_ussub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_smin_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_umin_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_smax_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_umax_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); + +void tcg_gen_shli_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); +void tcg_gen_shri_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); +void tcg_gen_sari_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); +void tcg_gen_rotli_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); +void tcg_gen_rotri_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); + +void tcg_gen_shls_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); +void tcg_gen_shrs_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); +void tcg_gen_sars_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); +void tcg_gen_rotls_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); + +void tcg_gen_shlv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); +void tcg_gen_shrv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); +void tcg_gen_sarv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); +void tcg_gen_rotlv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); +void tcg_gen_rotrv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); + +void tcg_gen_cmp_vec(TCGCond cond, unsigned vece, TCGv_vec r, + TCGv_vec a, TCGv_vec b); + +void tcg_gen_bitsel_vec(unsigned vece, TCGv_vec r, TCGv_vec a, + TCGv_vec b, TCGv_vec c); +void tcg_gen_cmpsel_vec(TCGCond cond, unsigned vece, TCGv_vec r, + TCGv_vec a, TCGv_vec b, TCGv_vec c, TCGv_vec d); + +void tcg_gen_ld_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset); +void tcg_gen_st_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset); +void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); + +/* Host pointer ops */ + +#if UINTPTR_MAX == UINT32_MAX +# define PTR i32 +# define NAT TCGv_i32 +#else +# define PTR i64 +# define NAT TCGv_i64 +#endif + +TCGv_ptr tcg_constant_ptr_int(intptr_t x); +#define tcg_constant_ptr(X) tcg_constant_ptr_int((intptr_t)(X)) + +static inline void tcg_gen_ld_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o) +{ + glue(tcg_gen_ld_,PTR)((NAT)r, a, o); +} + +static inline void tcg_gen_st_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o) +{ + glue(tcg_gen_st_, PTR)((NAT)r, a, o); +} + +static inline void tcg_gen_discard_ptr(TCGv_ptr a) +{ + glue(tcg_gen_discard_,PTR)((NAT)a); +} + +static inline void tcg_gen_add_ptr(TCGv_ptr r, TCGv_ptr a, TCGv_ptr b) +{ + glue(tcg_gen_add_,PTR)((NAT)r, (NAT)a, (NAT)b); +} + +static inline void tcg_gen_addi_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t b) +{ + glue(tcg_gen_addi_,PTR)((NAT)r, (NAT)a, b); +} + +static inline void tcg_gen_mov_ptr(TCGv_ptr d, TCGv_ptr s) +{ + glue(tcg_gen_mov_,PTR)((NAT)d, (NAT)s); +} + +static inline void tcg_gen_movi_ptr(TCGv_ptr d, intptr_t s) +{ + glue(tcg_gen_movi_,PTR)((NAT)d, s); +} + +static inline void tcg_gen_brcondi_ptr(TCGCond cond, TCGv_ptr a, + intptr_t b, TCGLabel *label) +{ + glue(tcg_gen_brcondi_,PTR)(cond, (NAT)a, b, label); +} + +static inline void tcg_gen_ext_i32_ptr(TCGv_ptr r, TCGv_i32 a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_mov_i32((NAT)r, a); +#else + tcg_gen_ext_i32_i64((NAT)r, a); +#endif +} + +static inline void tcg_gen_trunc_i64_ptr(TCGv_ptr r, TCGv_i64 a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_extrl_i64_i32((NAT)r, a); +#else + tcg_gen_mov_i64((NAT)r, a); +#endif +} + +static inline void tcg_gen_extu_ptr_i64(TCGv_i64 r, TCGv_ptr a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_extu_i32_i64(r, (NAT)a); +#else + tcg_gen_mov_i64(r, (NAT)a); +#endif +} + +static inline void tcg_gen_trunc_ptr_i32(TCGv_i32 r, TCGv_ptr a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_mov_i32(r, (NAT)a); +#else + tcg_gen_extrl_i64_i32(r, (NAT)a); +#endif +} + +#undef PTR +#undef NAT + +#endif /* TCG_TCG_OP_COMMON_H */ diff --git a/include/tcg/tcg-op-gvec-common.h b/include/tcg/tcg-op-gvec-common.h new file mode 100644 index 0000000000..4db8a58c14 --- /dev/null +++ b/include/tcg/tcg-op-gvec-common.h @@ -0,0 +1,432 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Target independent generic vector operation expansion + * + * Copyright (c) 2018 Linaro + */ + +#ifndef TCG_TCG_OP_GVEC_COMMON_H +#define TCG_TCG_OP_GVEC_COMMON_H + +/* + * "Generic" vectors. All operands are given as offsets from ENV, + * and therefore cannot also be allocated via tcg_global_mem_new_*. + * OPRSZ is the byte size of the vector upon which the operation is performed. + * MAXSZ is the byte size of the full vector; bytes beyond OPSZ are cleared. + * + * All sizes must be 8 or any multiple of 16. + * When OPRSZ is 8, the alignment may be 8, otherwise must be 16. + * Operands may completely, but not partially, overlap. + */ + +/* Expand a call to a gvec-style helper, with pointers to two vector + operands, and a descriptor (see tcg-gvec-desc.h). */ +typedef void gen_helper_gvec_2(TCGv_ptr, TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_2_ool(uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz, int32_t data, + gen_helper_gvec_2 *fn); + +/* Similarly, passing an extra data value. */ +typedef void gen_helper_gvec_2i(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv_i32); +void tcg_gen_gvec_2i_ool(uint32_t dofs, uint32_t aofs, TCGv_i64 c, + uint32_t oprsz, uint32_t maxsz, int32_t data, + gen_helper_gvec_2i *fn); + +/* Similarly, passing an extra pointer (e.g. env or float_status). */ +typedef void gen_helper_gvec_2_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_2_ptr(uint32_t dofs, uint32_t aofs, + TCGv_ptr ptr, uint32_t oprsz, uint32_t maxsz, + int32_t data, gen_helper_gvec_2_ptr *fn); + +/* Similarly, with three vector operands. */ +typedef void gen_helper_gvec_3(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_3_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, int32_t data, + gen_helper_gvec_3 *fn); + +/* Similarly, with four vector operands. */ +typedef void gen_helper_gvec_4(TCGv_ptr, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_4_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t cofs, uint32_t oprsz, uint32_t maxsz, + int32_t data, gen_helper_gvec_4 *fn); + +/* Similarly, with five vector operands. */ +typedef void gen_helper_gvec_5(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_5_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t cofs, uint32_t xofs, uint32_t oprsz, + uint32_t maxsz, int32_t data, gen_helper_gvec_5 *fn); + +typedef void gen_helper_gvec_3_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_3_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, + TCGv_ptr ptr, uint32_t oprsz, uint32_t maxsz, + int32_t data, gen_helper_gvec_3_ptr *fn); + +typedef void gen_helper_gvec_4_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_4_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t cofs, TCGv_ptr ptr, uint32_t oprsz, + 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 { + /* Expand inline as a 64-bit or 32-bit integer. + Only one of these will be non-NULL. */ + void (*fni8)(TCGv_i64, TCGv_i64); + void (*fni4)(TCGv_i32, TCGv_i32); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec); + /* Expand out-of-line helper w/descriptor. */ + gen_helper_gvec_2 *fno; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The data argument to the out-of-line helper. */ + int32_t data; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; + /* Load dest as a 2nd source operand. */ + bool load_dest; +} GVecGen2; + +typedef struct { + /* Expand inline as a 64-bit or 32-bit integer. + Only one of these will be non-NULL. */ + void (*fni8)(TCGv_i64, TCGv_i64, int64_t); + void (*fni4)(TCGv_i32, TCGv_i32, int32_t); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec, int64_t); + /* Expand out-of-line helper w/descriptor, data in descriptor. */ + gen_helper_gvec_2 *fno; + /* Expand out-of-line helper w/descriptor, data as argument. */ + gen_helper_gvec_2i *fnoi; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; + /* Load dest as a 3rd source operand. */ + bool load_dest; +} GVecGen2i; + +typedef struct { + /* Expand inline as a 64-bit or 32-bit integer. + Only one of these will be non-NULL. */ + void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64); + void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec); + /* Expand out-of-line helper w/descriptor. */ + gen_helper_gvec_2i *fno; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The data argument to the out-of-line helper. */ + uint32_t data; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; + /* Load scalar as 1st source operand. */ + bool scalar_first; +} GVecGen2s; + +typedef struct { + /* Expand inline as a 64-bit or 32-bit integer. + Only one of these will be non-NULL. */ + void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64); + void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec); + /* Expand out-of-line helper w/descriptor. */ + gen_helper_gvec_3 *fno; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The data argument to the out-of-line helper. */ + int32_t data; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; + /* Load dest as a 3rd source operand. */ + bool load_dest; +} GVecGen3; + +typedef struct { + /* + * Expand inline as a 64-bit or 32-bit integer. Only one of these will be + * non-NULL. + */ + void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64, int64_t); + void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32, int32_t); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, int64_t); + /* Expand out-of-line helper w/descriptor, data in descriptor. */ + gen_helper_gvec_3 *fno; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; + /* Load dest as a 3rd source operand. */ + bool load_dest; +} GVecGen3i; + +typedef struct { + /* Expand inline as a 64-bit or 32-bit integer. + Only one of these will be non-NULL. */ + void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64); + void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, TCGv_vec); + /* Expand out-of-line helper w/descriptor. */ + gen_helper_gvec_4 *fno; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The data argument to the out-of-line helper. */ + int32_t data; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; + /* Write aofs as a 2nd dest operand. */ + bool write_aofs; +} GVecGen4; + +typedef struct { + /* + * Expand inline as a 64-bit or 32-bit integer. Only one of these will be + * non-NULL. + */ + void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64, int64_t); + void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32, int32_t); + /* Expand inline with a host vector type. */ + void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, TCGv_vec, int64_t); + /* Expand out-of-line helper w/descriptor, data in descriptor. */ + gen_helper_gvec_4 *fno; + /* The optional opcodes, if any, utilized by .fniv. */ + const TCGOpcode *opt_opc; + /* The vector element size, if applicable. */ + uint8_t vece; + /* Prefer i64 to v64. */ + bool prefer_i64; +} GVecGen4i; + +void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen2 *); +void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, + uint32_t maxsz, int64_t c, const GVecGen2i *); +void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz, + uint32_t maxsz, TCGv_i64 c, const GVecGen2s *); +void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen3 *); +void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, int64_t c, + const GVecGen3i *); +void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen4 *); +void tcg_gen_gvec_4i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, + uint32_t oprsz, uint32_t maxsz, int64_t c, + const GVecGen4i *); + +/* Expand a specific vector operation. */ + +void tcg_gen_gvec_mov(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_not(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_neg(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_abs(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_add(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_sub(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_mul(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_addi(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_muli(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t c, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_adds(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_subs(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_muls(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); + +/* Saturated arithmetic. */ +void tcg_gen_gvec_ssadd(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_sssub(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_usadd(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_ussub(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); + +/* Min/max. */ +void tcg_gen_gvec_smin(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_umin(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_smax(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_umax(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_and(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_or(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_xor(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_andc(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_orc(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_nand(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_nor(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_eqv(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_andi(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_xori(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_ori(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t c, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_ands(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_andcs(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_xors(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_ors(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t s, uint32_t m); +void tcg_gen_gvec_dup_imm(unsigned vece, uint32_t dofs, uint32_t s, + uint32_t m, uint64_t imm); +void tcg_gen_gvec_dup_i32(unsigned vece, uint32_t dofs, uint32_t s, + uint32_t m, TCGv_i32); +void tcg_gen_gvec_dup_i64(unsigned vece, uint32_t dofs, uint32_t s, + uint32_t m, TCGv_i64); + +void tcg_gen_gvec_shli(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_shri(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_sari(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_rotli(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_rotri(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t shift, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_shls(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_shrs(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_sars(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_rotls(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_rotrs(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); + +/* + * Perform vector shift by vector element, modulo the element size. + * E.g. D[i] = A[i] << (B[i] % (8 << vece)). + */ +void tcg_gen_gvec_shlv(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_shrv(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_sarv(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_rotlv(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_rotrv(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); + +void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs, + uint32_t aofs, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_cmpi(TCGCond cond, unsigned vece, uint32_t dofs, + uint32_t aofs, int64_t c, + uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_cmps(TCGCond cond, unsigned vece, uint32_t dofs, + uint32_t aofs, TCGv_i64 c, + uint32_t oprsz, uint32_t maxsz); + +/* + * Perform vector bit select: d = (b & a) | (c & ~a). + */ +void tcg_gen_gvec_bitsel(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t cofs, + uint32_t oprsz, uint32_t maxsz); + +/* + * 64-bit vector operations. Use these when the register has been allocated + * with tcg_global_mem_new_i64, and so we cannot also address it via pointer. + * OPRSZ = MAXSZ = 8. + */ + +void tcg_gen_vec_neg8_i64(TCGv_i64 d, TCGv_i64 a); +void tcg_gen_vec_neg16_i64(TCGv_i64 d, TCGv_i64 a); +void tcg_gen_vec_neg32_i64(TCGv_i64 d, TCGv_i64 a); + +void tcg_gen_vec_add8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); +void tcg_gen_vec_add16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); +void tcg_gen_vec_add32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); + +void tcg_gen_vec_sub8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); +void tcg_gen_vec_sub16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); +void tcg_gen_vec_sub32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); + +void tcg_gen_vec_shl8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); +void tcg_gen_vec_shl16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); +void tcg_gen_vec_shr8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); +void tcg_gen_vec_shr16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); +void tcg_gen_vec_sar8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); +void tcg_gen_vec_sar16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); +void tcg_gen_vec_rotl8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t c); +void tcg_gen_vec_rotl16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t c); + +/* 32-bit vector operations. */ +void tcg_gen_vec_add8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); +void tcg_gen_vec_add16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); + +void tcg_gen_vec_sub8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); +void tcg_gen_vec_sub16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); + +void tcg_gen_vec_shl8i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); +void tcg_gen_vec_shl16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); +void tcg_gen_vec_shr8i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); +void tcg_gen_vec_shr16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); +void tcg_gen_vec_sar8i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); +void tcg_gen_vec_sar16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); + +#endif diff --git a/include/tcg/tcg-op-gvec.h b/include/tcg/tcg-op-gvec.h index 28cafbcc5c..b0a81ad4bf 100644 --- a/include/tcg/tcg-op-gvec.h +++ b/include/tcg/tcg-op-gvec.h @@ -1,443 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Generic vector operation expansion + * Target dependent generic vector operation expansion * * Copyright (c) 2018 Linaro - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * 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 TCG_TCG_OP_GVEC_H #define TCG_TCG_OP_GVEC_H -/* - * "Generic" vectors. All operands are given as offsets from ENV, - * and therefore cannot also be allocated via tcg_global_mem_new_*. - * OPRSZ is the byte size of the vector upon which the operation is performed. - * MAXSZ is the byte size of the full vector; bytes beyond OPSZ are cleared. - * - * All sizes must be 8 or any multiple of 16. - * When OPRSZ is 8, the alignment may be 8, otherwise must be 16. - * Operands may completely, but not partially, overlap. - */ +#include "tcg/tcg-op-gvec-common.h" -/* Expand a call to a gvec-style helper, with pointers to two vector - operands, and a descriptor (see tcg-gvec-desc.h). */ -typedef void gen_helper_gvec_2(TCGv_ptr, TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_2_ool(uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz, int32_t data, - gen_helper_gvec_2 *fn); - -/* Similarly, passing an extra data value. */ -typedef void gen_helper_gvec_2i(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv_i32); -void tcg_gen_gvec_2i_ool(uint32_t dofs, uint32_t aofs, TCGv_i64 c, - uint32_t oprsz, uint32_t maxsz, int32_t data, - gen_helper_gvec_2i *fn); - -/* Similarly, passing an extra pointer (e.g. env or float_status). */ -typedef void gen_helper_gvec_2_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_2_ptr(uint32_t dofs, uint32_t aofs, - TCGv_ptr ptr, uint32_t oprsz, uint32_t maxsz, - int32_t data, gen_helper_gvec_2_ptr *fn); - -/* Similarly, with three vector operands. */ -typedef void gen_helper_gvec_3(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_3_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, uint32_t maxsz, int32_t data, - gen_helper_gvec_3 *fn); - -/* Similarly, with four vector operands. */ -typedef void gen_helper_gvec_4(TCGv_ptr, TCGv_ptr, TCGv_ptr, - TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_4_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t cofs, uint32_t oprsz, uint32_t maxsz, - int32_t data, gen_helper_gvec_4 *fn); - -/* Similarly, with five vector operands. */ -typedef void gen_helper_gvec_5(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, - TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_5_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t cofs, uint32_t xofs, uint32_t oprsz, - uint32_t maxsz, int32_t data, gen_helper_gvec_5 *fn); - -typedef void gen_helper_gvec_3_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, - TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_3_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, - TCGv_ptr ptr, uint32_t oprsz, uint32_t maxsz, - int32_t data, gen_helper_gvec_3_ptr *fn); - -typedef void gen_helper_gvec_4_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, - TCGv_ptr, TCGv_ptr, TCGv_i32); -void tcg_gen_gvec_4_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t cofs, TCGv_ptr ptr, uint32_t oprsz, - 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 { - /* Expand inline as a 64-bit or 32-bit integer. - Only one of these will be non-NULL. */ - void (*fni8)(TCGv_i64, TCGv_i64); - void (*fni4)(TCGv_i32, TCGv_i32); - /* Expand inline with a host vector type. */ - void (*fniv)(unsigned, TCGv_vec, TCGv_vec); - /* Expand out-of-line helper w/descriptor. */ - gen_helper_gvec_2 *fno; - /* The optional opcodes, if any, utilized by .fniv. */ - const TCGOpcode *opt_opc; - /* The data argument to the out-of-line helper. */ - int32_t data; - /* The vector element size, if applicable. */ - uint8_t vece; - /* Prefer i64 to v64. */ - bool prefer_i64; - /* Load dest as a 2nd source operand. */ - bool load_dest; -} GVecGen2; - -typedef struct { - /* Expand inline as a 64-bit or 32-bit integer. - Only one of these will be non-NULL. */ - void (*fni8)(TCGv_i64, TCGv_i64, int64_t); - void (*fni4)(TCGv_i32, TCGv_i32, int32_t); - /* Expand inline with a host vector type. */ - void (*fniv)(unsigned, TCGv_vec, TCGv_vec, int64_t); - /* Expand out-of-line helper w/descriptor, data in descriptor. */ - gen_helper_gvec_2 *fno; - /* Expand out-of-line helper w/descriptor, data as argument. */ - gen_helper_gvec_2i *fnoi; - /* The optional opcodes, if any, utilized by .fniv. */ - const TCGOpcode *opt_opc; - /* The vector element size, if applicable. */ - uint8_t vece; - /* Prefer i64 to v64. */ - bool prefer_i64; - /* Load dest as a 3rd source operand. */ - bool load_dest; -} GVecGen2i; - -typedef struct { - /* Expand inline as a 64-bit or 32-bit integer. - Only one of these will be non-NULL. */ - void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64); - void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32); - /* Expand inline with a host vector type. */ - void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec); - /* Expand out-of-line helper w/descriptor. */ - gen_helper_gvec_2i *fno; - /* The optional opcodes, if any, utilized by .fniv. */ - const TCGOpcode *opt_opc; - /* The data argument to the out-of-line helper. */ - uint32_t data; - /* The vector element size, if applicable. */ - uint8_t vece; - /* Prefer i64 to v64. */ - bool prefer_i64; - /* Load scalar as 1st source operand. */ - bool scalar_first; -} GVecGen2s; - -typedef struct { - /* Expand inline as a 64-bit or 32-bit integer. - Only one of these will be non-NULL. */ - void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64); - void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32); - /* Expand inline with a host vector type. */ - void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec); - /* Expand out-of-line helper w/descriptor. */ - gen_helper_gvec_3 *fno; - /* The optional opcodes, if any, utilized by .fniv. */ - const TCGOpcode *opt_opc; - /* The data argument to the out-of-line helper. */ - int32_t data; - /* The vector element size, if applicable. */ - uint8_t vece; - /* Prefer i64 to v64. */ - bool prefer_i64; - /* Load dest as a 3rd source operand. */ - bool load_dest; -} GVecGen3; - -typedef struct { - /* - * Expand inline as a 64-bit or 32-bit integer. Only one of these will be - * non-NULL. - */ - void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64, int64_t); - void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32, int32_t); - /* Expand inline with a host vector type. */ - void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, int64_t); - /* Expand out-of-line helper w/descriptor, data in descriptor. */ - gen_helper_gvec_3 *fno; - /* The optional opcodes, if any, utilized by .fniv. */ - const TCGOpcode *opt_opc; - /* The vector element size, if applicable. */ - uint8_t vece; - /* Prefer i64 to v64. */ - bool prefer_i64; - /* Load dest as a 3rd source operand. */ - bool load_dest; -} GVecGen3i; - -typedef struct { - /* Expand inline as a 64-bit or 32-bit integer. - Only one of these will be non-NULL. */ - void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64); - void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); - /* Expand inline with a host vector type. */ - void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, TCGv_vec); - /* Expand out-of-line helper w/descriptor. */ - gen_helper_gvec_4 *fno; - /* The optional opcodes, if any, utilized by .fniv. */ - const TCGOpcode *opt_opc; - /* The data argument to the out-of-line helper. */ - int32_t data; - /* The vector element size, if applicable. */ - uint8_t vece; - /* Prefer i64 to v64. */ - bool prefer_i64; - /* Write aofs as a 2nd dest operand. */ - bool write_aofs; -} GVecGen4; - -typedef struct { - /* - * Expand inline as a 64-bit or 32-bit integer. Only one of these will be - * non-NULL. - */ - void (*fni8)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64, int64_t); - void (*fni4)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32, int32_t); - /* Expand inline with a host vector type. */ - void (*fniv)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, TCGv_vec, int64_t); - /* Expand out-of-line helper w/descriptor, data in descriptor. */ - gen_helper_gvec_4 *fno; - /* The optional opcodes, if any, utilized by .fniv. */ - const TCGOpcode *opt_opc; - /* The vector element size, if applicable. */ - uint8_t vece; - /* Prefer i64 to v64. */ - bool prefer_i64; -} GVecGen4i; - -void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz, const GVecGen2 *); -void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, - uint32_t maxsz, int64_t c, const GVecGen2i *); -void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz, - uint32_t maxsz, TCGv_i64 c, const GVecGen2s *); -void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, uint32_t maxsz, const GVecGen3 *); -void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, uint32_t maxsz, int64_t c, - const GVecGen3i *); -void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, - uint32_t oprsz, uint32_t maxsz, const GVecGen4 *); -void tcg_gen_gvec_4i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, - uint32_t oprsz, uint32_t maxsz, int64_t c, - const GVecGen4i *); - -/* Expand a specific vector operation. */ - -void tcg_gen_gvec_mov(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_not(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_neg(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_abs(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_add(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_sub(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_mul(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_addi(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_muli(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t c, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_adds(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_subs(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_muls(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); - -/* Saturated arithmetic. */ -void tcg_gen_gvec_ssadd(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_sssub(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_usadd(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_ussub(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); - -/* Min/max. */ -void tcg_gen_gvec_smin(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_umin(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_smax(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_umax(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_and(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_or(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_xor(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_andc(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_orc(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_nand(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_nor(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_eqv(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_andi(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_xori(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_ori(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t c, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_ands(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_xors(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_ors(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i64 c, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t s, uint32_t m); -void tcg_gen_gvec_dup_imm(unsigned vece, uint32_t dofs, uint32_t s, - uint32_t m, uint64_t imm); -void tcg_gen_gvec_dup_i32(unsigned vece, uint32_t dofs, uint32_t s, - uint32_t m, TCGv_i32); -void tcg_gen_gvec_dup_i64(unsigned vece, uint32_t dofs, uint32_t s, - uint32_t m, TCGv_i64); - -#if TARGET_LONG_BITS == 64 -# define tcg_gen_gvec_dup_tl tcg_gen_gvec_dup_i64 -#else -# define tcg_gen_gvec_dup_tl tcg_gen_gvec_dup_i32 +#ifndef TARGET_LONG_BITS +#error must include QEMU headers #endif -void tcg_gen_gvec_shli(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_shri(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_sari(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_rotli(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_rotri(unsigned vece, uint32_t dofs, uint32_t aofs, - int64_t shift, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_shls(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_shrs(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_sars(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_rotls(unsigned vece, uint32_t dofs, uint32_t aofs, - TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz); - -/* - * Perform vector shift by vector element, modulo the element size. - * E.g. D[i] = A[i] << (B[i] % (8 << vece)). - */ -void tcg_gen_gvec_shlv(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_shrv(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_sarv(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_rotlv(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); -void tcg_gen_gvec_rotrv(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz); - -void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs, - uint32_t aofs, uint32_t bofs, - uint32_t oprsz, uint32_t maxsz); - -/* - * Perform vector bit select: d = (b & a) | (c & ~a). - */ -void tcg_gen_gvec_bitsel(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t cofs, - uint32_t oprsz, uint32_t maxsz); - -/* - * 64-bit vector operations. Use these when the register has been allocated - * with tcg_global_mem_new_i64, and so we cannot also address it via pointer. - * OPRSZ = MAXSZ = 8. - */ - -void tcg_gen_vec_neg8_i64(TCGv_i64 d, TCGv_i64 a); -void tcg_gen_vec_neg16_i64(TCGv_i64 d, TCGv_i64 a); -void tcg_gen_vec_neg32_i64(TCGv_i64 d, TCGv_i64 a); - -void tcg_gen_vec_add8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); -void tcg_gen_vec_add16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); -void tcg_gen_vec_add32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); - -void tcg_gen_vec_sub8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); -void tcg_gen_vec_sub16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); -void tcg_gen_vec_sub32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); - -void tcg_gen_vec_shl8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); -void tcg_gen_vec_shl16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); -void tcg_gen_vec_shr8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); -void tcg_gen_vec_shr16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); -void tcg_gen_vec_sar8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); -void tcg_gen_vec_sar16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t); -void tcg_gen_vec_rotl8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t c); -void tcg_gen_vec_rotl16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t c); - -/* 32-bit vector operations. */ -void tcg_gen_vec_add8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); -void tcg_gen_vec_add16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); - -void tcg_gen_vec_sub8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); -void tcg_gen_vec_sub16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); - -void tcg_gen_vec_shl8i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); -void tcg_gen_vec_shl16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); -void tcg_gen_vec_shr8i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); -void tcg_gen_vec_shr16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); -void tcg_gen_vec_sar8i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); -void tcg_gen_vec_sar16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); - #if TARGET_LONG_BITS == 64 +#define tcg_gen_gvec_dup_tl tcg_gen_gvec_dup_i64 #define tcg_gen_vec_add8_tl tcg_gen_vec_add8_i64 #define tcg_gen_vec_sub8_tl tcg_gen_vec_sub8_i64 #define tcg_gen_vec_add16_tl tcg_gen_vec_add16_i64 @@ -450,8 +28,8 @@ void tcg_gen_vec_sar16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); #define tcg_gen_vec_shl16i_tl tcg_gen_vec_shl16i_i64 #define tcg_gen_vec_shr16i_tl tcg_gen_vec_shr16i_i64 #define tcg_gen_vec_sar16i_tl tcg_gen_vec_sar16i_i64 - -#else +#elif TARGET_LONG_BITS == 32 +#define tcg_gen_gvec_dup_tl tcg_gen_gvec_dup_i32 #define tcg_gen_vec_add8_tl tcg_gen_vec_add8_i32 #define tcg_gen_vec_sub8_tl tcg_gen_vec_sub8_i32 #define tcg_gen_vec_add16_tl tcg_gen_vec_add16_i32 @@ -464,6 +42,8 @@ void tcg_gen_vec_sar16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t); #define tcg_gen_vec_shl16i_tl tcg_gen_vec_shl16i_i32 #define tcg_gen_vec_shr16i_tl tcg_gen_vec_shr16i_i32 #define tcg_gen_vec_sar16i_tl tcg_gen_vec_sar16i_i32 +#else +# error #endif #endif diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index 209e168305..a02850583b 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -1,1023 +1,164 @@ +/* SPDX-License-Identifier: MIT */ /* - * Tiny Code Generator for QEMU + * Target dependent opcode generation functions. * * Copyright (c) 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. */ #ifndef TCG_TCG_OP_H #define TCG_TCG_OP_H -#include "tcg/tcg.h" -#include "exec/helper-proto.h" -#include "exec/helper-gen.h" - -/* Basic output routines. Not for general consumption. */ - -void tcg_gen_op1(TCGOpcode, TCGArg); -void tcg_gen_op2(TCGOpcode, TCGArg, TCGArg); -void tcg_gen_op3(TCGOpcode, TCGArg, TCGArg, TCGArg); -void tcg_gen_op4(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg); -void tcg_gen_op5(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); -void tcg_gen_op6(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); - -void vec_gen_2(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg); -void vec_gen_3(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg); -void vec_gen_4(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg, TCGArg); - -static inline void tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 a1) -{ - tcg_gen_op1(opc, tcgv_i32_arg(a1)); -} - -static inline void tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 a1) -{ - tcg_gen_op1(opc, tcgv_i64_arg(a1)); -} - -static inline void tcg_gen_op1i(TCGOpcode opc, TCGArg a1) -{ - tcg_gen_op1(opc, a1); -} - -static inline void tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2) -{ - tcg_gen_op2(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2)); -} - -static inline void tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2) -{ - tcg_gen_op2(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2)); -} - -static inline void tcg_gen_op2i_i32(TCGOpcode opc, TCGv_i32 a1, TCGArg a2) -{ - tcg_gen_op2(opc, tcgv_i32_arg(a1), a2); -} - -static inline void tcg_gen_op2i_i64(TCGOpcode opc, TCGv_i64 a1, TCGArg a2) -{ - tcg_gen_op2(opc, tcgv_i64_arg(a1), a2); -} - -static inline void tcg_gen_op2ii(TCGOpcode opc, TCGArg a1, TCGArg a2) -{ - tcg_gen_op2(opc, a1, a2); -} - -static inline void tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 a1, - TCGv_i32 a2, TCGv_i32 a3) -{ - tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3)); -} - -static inline void tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 a1, - TCGv_i64 a2, TCGv_i64 a3) -{ - tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3)); -} - -static inline void tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 a1, - TCGv_i32 a2, TCGArg a3) -{ - tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3); -} - -static inline void tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 a1, - TCGv_i64 a2, TCGArg a3) -{ - tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3); -} - -static inline void tcg_gen_ldst_op_i32(TCGOpcode opc, TCGv_i32 val, - TCGv_ptr base, TCGArg offset) -{ - tcg_gen_op3(opc, tcgv_i32_arg(val), tcgv_ptr_arg(base), offset); -} - -static inline void tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val, - TCGv_ptr base, TCGArg offset) -{ - tcg_gen_op3(opc, tcgv_i64_arg(val), tcgv_ptr_arg(base), offset); -} - -static inline void tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4) -{ - tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4)); -} - -static inline void tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4) -{ - tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4)); -} - -static inline void tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGArg a4) -{ - tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), a4); -} - -static inline void tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGArg a4) -{ - tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), a4); -} - -static inline void tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGArg a3, TCGArg a4) -{ - tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3, a4); -} - -static inline void tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGArg a3, TCGArg a4) -{ - tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3, a4); -} - -static inline void tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, TCGv_i32 a5) -{ - tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5)); -} - -static inline void tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, TCGv_i64 a5) -{ - tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5)); -} - -static inline void tcg_gen_op5i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, TCGArg a5) -{ - tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5); -} - -static inline void tcg_gen_op5i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, TCGArg a5) -{ - tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), a5); -} - -static inline void tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGArg a4, TCGArg a5) -{ - tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), a4, a5); -} - -static inline void tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGArg a4, TCGArg a5) -{ - tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), a4, a5); -} - -static inline void tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, - TCGv_i32 a5, TCGv_i32 a6) -{ - tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), - tcgv_i32_arg(a6)); -} - -static inline void tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, - TCGv_i64 a5, TCGv_i64 a6) -{ - tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), - tcgv_i64_arg(a6)); -} - -static inline void tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, - TCGv_i32 a5, TCGArg a6) -{ - tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), a6); -} - -static inline void tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, - TCGv_i64 a5, TCGArg a6) -{ - tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), a6); -} - -static inline void tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, - TCGArg a5, TCGArg a6) -{ - tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5, a6); -} - -static inline void tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, - TCGArg a5, TCGArg a6) -{ - tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), a5, a6); -} - - -/* Generic ops. */ - -static inline void gen_set_label(TCGLabel *l) -{ - l->present = 1; - tcg_gen_op1(INDEX_op_set_label, label_arg(l)); -} - -static inline void tcg_gen_br(TCGLabel *l) -{ - l->refs++; - tcg_gen_op1(INDEX_op_br, label_arg(l)); -} - -void tcg_gen_mb(TCGBar); - -/* Helper calls. */ - -/* 32 bit ops */ - -void tcg_gen_movi_i32(TCGv_i32 ret, int32_t arg); -void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2); -void tcg_gen_subi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_sari_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_clz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_clzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2); -void tcg_gen_ctzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2); -void tcg_gen_clrsb_i32(TCGv_i32 ret, TCGv_i32 arg); -void tcg_gen_ctpop_i32(TCGv_i32 a1, TCGv_i32 a2); -void tcg_gen_rotl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_rotli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_rotr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_rotri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2); -void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, - unsigned int ofs, unsigned int len); -void tcg_gen_deposit_z_i32(TCGv_i32 ret, TCGv_i32 arg, - unsigned int ofs, unsigned int len); -void tcg_gen_extract_i32(TCGv_i32 ret, TCGv_i32 arg, - unsigned int ofs, unsigned int len); -void tcg_gen_sextract_i32(TCGv_i32 ret, TCGv_i32 arg, - unsigned int ofs, unsigned int len); -void tcg_gen_extract2_i32(TCGv_i32 ret, TCGv_i32 al, TCGv_i32 ah, - unsigned int ofs); -void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, TCGv_i32 arg2, TCGLabel *); -void tcg_gen_brcondi_i32(TCGCond cond, TCGv_i32 arg1, int32_t arg2, TCGLabel *); -void tcg_gen_setcond_i32(TCGCond cond, TCGv_i32 ret, - TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_setcondi_i32(TCGCond cond, TCGv_i32 ret, - TCGv_i32 arg1, int32_t arg2); -void tcg_gen_movcond_i32(TCGCond cond, TCGv_i32 ret, TCGv_i32 c1, - TCGv_i32 c2, TCGv_i32 v1, TCGv_i32 v2); -void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, - TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh); -void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, - TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh); -void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_mulsu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_ext8s_i32(TCGv_i32 ret, TCGv_i32 arg); -void tcg_gen_ext16s_i32(TCGv_i32 ret, TCGv_i32 arg); -void tcg_gen_ext8u_i32(TCGv_i32 ret, TCGv_i32 arg); -void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg); -void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg, int flags); -void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg); -void tcg_gen_hswap_i32(TCGv_i32 ret, TCGv_i32 arg); -void tcg_gen_smin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_smax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_umin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_umax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); -void tcg_gen_abs_i32(TCGv_i32, TCGv_i32); - -/* Replicate a value of size @vece from @in to all the lanes in @out */ -void tcg_gen_dup_i32(unsigned vece, TCGv_i32 out, TCGv_i32 in); - -static inline void tcg_gen_discard_i32(TCGv_i32 arg) -{ - tcg_gen_op1_i32(INDEX_op_discard, arg); -} - -static inline void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (ret != arg) { - tcg_gen_op2_i32(INDEX_op_mov_i32, ret, arg); - } -} - -static inline void tcg_gen_ld8u_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld8u_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld8s_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld16u_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld16u_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld16s_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld16s_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld_i32, ret, arg2, offset); -} - -static inline void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_st8_i32, arg1, arg2, offset); -} - -static inline void tcg_gen_st16_i32(TCGv_i32 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_st16_i32, arg1, arg2, offset); -} - -static inline void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_st_i32, arg1, arg2, offset); -} - -static inline void tcg_gen_add_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_add_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_sub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_sub_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_and_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_or_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_or_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_xor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_xor_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_shl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_shl_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_shr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_shr_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_sar_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_sar_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_mul_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_mul_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_neg_i32) { - tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg); - } else { - tcg_gen_subfi_i32(ret, 0, arg); - } -} - -static inline void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_not_i32) { - tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg); - } else { - tcg_gen_xori_i32(ret, arg, -1); - } -} - -/* 64 bit ops */ - -void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg); -void tcg_gen_addi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_subfi_i64(TCGv_i64 ret, int64_t arg1, TCGv_i64 arg2); -void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_ori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_eqv_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_nand_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_nor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_clz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_ctz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_clzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2); -void tcg_gen_ctzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2); -void tcg_gen_clrsb_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ctpop_i64(TCGv_i64 a1, TCGv_i64 a2); -void tcg_gen_rotl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_rotli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_rotr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_rotri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2); -void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, - unsigned int ofs, unsigned int len); -void tcg_gen_deposit_z_i64(TCGv_i64 ret, TCGv_i64 arg, - unsigned int ofs, unsigned int len); -void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg, - unsigned int ofs, unsigned int len); -void tcg_gen_sextract_i64(TCGv_i64 ret, TCGv_i64 arg, - unsigned int ofs, unsigned int len); -void tcg_gen_extract2_i64(TCGv_i64 ret, TCGv_i64 al, TCGv_i64 ah, - unsigned int ofs); -void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, TCGv_i64 arg2, TCGLabel *); -void tcg_gen_brcondi_i64(TCGCond cond, TCGv_i64 arg1, int64_t arg2, TCGLabel *); -void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, - TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_setcondi_i64(TCGCond cond, TCGv_i64 ret, - TCGv_i64 arg1, int64_t arg2); -void tcg_gen_movcond_i64(TCGCond cond, TCGv_i64 ret, TCGv_i64 c1, - TCGv_i64 c2, TCGv_i64 v1, TCGv_i64 v2); -void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, - TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh); -void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, - TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh); -void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_mulsu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ext8s_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ext16s_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ext32s_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ext8u_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ext16u_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg, int flags); -void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags); -void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_hswap_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_wswap_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_smin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_smax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_umin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_umax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_abs_i64(TCGv_i64, TCGv_i64); - -/* Replicate a value of size @vece from @in to all the lanes in @out */ -void tcg_gen_dup_i64(unsigned vece, TCGv_i64 out, TCGv_i64 in); - -#if TCG_TARGET_REG_BITS == 64 -static inline void tcg_gen_discard_i64(TCGv_i64 arg) -{ - tcg_gen_op1_i64(INDEX_op_discard, arg); -} - -static inline void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (ret != arg) { - tcg_gen_op2_i64(INDEX_op_mov_i64, ret, arg); - } -} - -static inline void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld8u_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld8s_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld16u_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld16s_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld32u_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld32s_i64, ret, arg2, offset); -} - -static inline void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_ld_i64, ret, arg2, offset); -} - -static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_st8_i64, arg1, arg2, offset); -} - -static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_st16_i64, arg1, arg2, offset); -} - -static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_st32_i64, arg1, arg2, offset); -} - -static inline void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i64(INDEX_op_st_i64, arg1, arg2, offset); -} - -static inline void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_add_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_sub_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_and_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_or_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_xor_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_shl_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_shr_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_sar_i64, ret, arg1, arg2); -} - -static inline void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_op3_i64(INDEX_op_mul_i64, ret, arg1, arg2); -} -#else /* TCG_TARGET_REG_BITS == 32 */ -static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_st8_i32(TCGV_LOW(arg1), arg2, offset); -} - -static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_st16_i32(TCGV_LOW(arg1), arg2, offset); -} - -static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); -} - -static inline void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_add2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1), - TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2)); -} - -static inline void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) -{ - tcg_gen_sub2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1), - TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2)); -} - -void tcg_gen_discard_i64(TCGv_i64 arg); -void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg); -void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset); -void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2); -#endif /* TCG_TARGET_REG_BITS */ - -static inline void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg) -{ - if (TCG_TARGET_HAS_neg_i64) { - tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg); - } else { - tcg_gen_subfi_i64(ret, 0, arg); - } -} - -/* Size changing operations. */ - -void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg); -void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg); -void tcg_gen_concat_i32_i64(TCGv_i64 dest, TCGv_i32 low, TCGv_i32 high); -void tcg_gen_extrl_i64_i32(TCGv_i32 ret, TCGv_i64 arg); -void tcg_gen_extrh_i64_i32(TCGv_i32 ret, TCGv_i64 arg); -void tcg_gen_extr_i64_i32(TCGv_i32 lo, TCGv_i32 hi, TCGv_i64 arg); -void tcg_gen_extr32_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i64 arg); - -static inline void tcg_gen_concat32_i64(TCGv_i64 ret, TCGv_i64 lo, TCGv_i64 hi) -{ - tcg_gen_deposit_i64(ret, lo, hi, 32, 32); -} - -/* QEMU specific operations. */ +#include "tcg/tcg-op-common.h" #ifndef TARGET_LONG_BITS #error must include QEMU headers #endif -#if TARGET_INSN_START_WORDS == 1 -# if TARGET_LONG_BITS <= TCG_TARGET_REG_BITS -static inline void tcg_gen_insn_start(target_ulong pc) -{ - tcg_gen_op1(INDEX_op_insn_start, pc); -} -# else -static inline void tcg_gen_insn_start(target_ulong pc) -{ - tcg_gen_op2(INDEX_op_insn_start, (uint32_t)pc, (uint32_t)(pc >> 32)); -} -# endif -#elif TARGET_INSN_START_WORDS == 2 -# if TARGET_LONG_BITS <= TCG_TARGET_REG_BITS -static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1) -{ - tcg_gen_op2(INDEX_op_insn_start, pc, a1); -} -# else -static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1) -{ - tcg_gen_op4(INDEX_op_insn_start, - (uint32_t)pc, (uint32_t)(pc >> 32), - (uint32_t)a1, (uint32_t)(a1 >> 32)); -} -# endif -#elif TARGET_INSN_START_WORDS == 3 -# if TARGET_LONG_BITS <= TCG_TARGET_REG_BITS -static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1, - target_ulong a2) -{ - tcg_gen_op3(INDEX_op_insn_start, pc, a1, a2); -} -# else -static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1, - target_ulong a2) -{ - tcg_gen_op6(INDEX_op_insn_start, - (uint32_t)pc, (uint32_t)(pc >> 32), - (uint32_t)a1, (uint32_t)(a1 >> 32), - (uint32_t)a2, (uint32_t)(a2 >> 32)); -} -# endif +#if TARGET_LONG_BITS == 32 +# define TCG_TYPE_TL TCG_TYPE_I32 +#elif TARGET_LONG_BITS == 64 +# define TCG_TYPE_TL TCG_TYPE_I64 #else -# error "Unhandled number of operands to insn_start" +# error #endif -/** - * tcg_gen_exit_tb() - output exit_tb TCG operation - * @tb: The TranslationBlock from which we are exiting - * @idx: Direct jump slot index, or exit request - * - * See tcg/README for more info about this TCG operation. - * See also tcg.h and the block comment above TB_EXIT_MASK. - * - * For a normal exit from the TB, back to the main loop, @tb should - * be NULL and @idx should be 0. Otherwise, @tb should be valid and - * @idx should be one of the TB_EXIT_ values. - */ -void tcg_gen_exit_tb(const TranslationBlock *tb, unsigned idx); - -/** - * tcg_gen_goto_tb() - output goto_tb TCG operation - * @idx: Direct jump slot index (0 or 1) - * - * See tcg/README for more info about this TCG operation. - * - * NOTE: In softmmu emulation, direct jumps with goto_tb are only safe within - * the pages this TB resides in because we don't take care of direct jumps when - * address mapping changes, e.g. in tlb_flush(). In user mode, there's only a - * static address translation, so the destination address is always valid, TBs - * are always invalidated properly, and direct jumps are reset when mapping - * changes. - */ -void tcg_gen_goto_tb(unsigned idx); - -/** - * tcg_gen_lookup_and_goto_ptr() - look up the current TB, jump to it if valid - * @addr: Guest address of the target TB - * - * If the TB is not valid, jump to the epilogue. - * - * This operation is optional. If the TCG backend does not implement goto_ptr, - * this op is equivalent to calling tcg_gen_exit_tb() with 0 as the argument. - */ -void tcg_gen_lookup_and_goto_ptr(void); - -static inline void tcg_gen_plugin_cb_start(unsigned from, unsigned type, - unsigned wr) +#ifndef TARGET_INSN_START_EXTRA_WORDS +static inline void tcg_gen_insn_start(target_ulong pc) { - tcg_gen_op3(INDEX_op_plugin_cb_start, from, type, wr); + TCGOp *op = tcg_emit_op(INDEX_op_insn_start, 64 / TCG_TARGET_REG_BITS); + tcg_set_insn_start_param(op, 0, pc); } - -static inline void tcg_gen_plugin_cb_end(void) +#elif TARGET_INSN_START_EXTRA_WORDS == 1 +static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1) { - tcg_emit_op(INDEX_op_plugin_cb_end); + TCGOp *op = tcg_emit_op(INDEX_op_insn_start, 2 * 64 / TCG_TARGET_REG_BITS); + tcg_set_insn_start_param(op, 0, pc); + tcg_set_insn_start_param(op, 1, a1); } +#elif TARGET_INSN_START_EXTRA_WORDS == 2 +static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1, + target_ulong a2) +{ + TCGOp *op = tcg_emit_op(INDEX_op_insn_start, 3 * 64 / TCG_TARGET_REG_BITS); + tcg_set_insn_start_param(op, 0, pc); + tcg_set_insn_start_param(op, 1, a1); + tcg_set_insn_start_param(op, 2, a2); +} +#else +#error Unhandled TARGET_INSN_START_EXTRA_WORDS value +#endif #if TARGET_LONG_BITS == 32 +typedef TCGv_i32 TCGv; #define tcg_temp_new() tcg_temp_new_i32() #define tcg_global_mem_new tcg_global_mem_new_i32 -#define tcg_temp_local_new() tcg_temp_local_new_i32() -#define tcg_temp_free tcg_temp_free_i32 +#define tcgv_tl_temp tcgv_i32_temp #define tcg_gen_qemu_ld_tl tcg_gen_qemu_ld_i32 #define tcg_gen_qemu_st_tl tcg_gen_qemu_st_i32 -#else +#elif TARGET_LONG_BITS == 64 +typedef TCGv_i64 TCGv; #define tcg_temp_new() tcg_temp_new_i64() #define tcg_global_mem_new tcg_global_mem_new_i64 -#define tcg_temp_local_new() tcg_temp_local_new_i64() -#define tcg_temp_free tcg_temp_free_i64 +#define tcgv_tl_temp tcgv_i64_temp #define tcg_gen_qemu_ld_tl tcg_gen_qemu_ld_i64 #define tcg_gen_qemu_st_tl tcg_gen_qemu_st_i64 +#else +#error Unhandled TARGET_LONG_BITS value #endif -void tcg_gen_qemu_ld_i32(TCGv_i32, TCGv, TCGArg, MemOp); -void tcg_gen_qemu_st_i32(TCGv_i32, TCGv, TCGArg, MemOp); -void tcg_gen_qemu_ld_i64(TCGv_i64, TCGv, TCGArg, MemOp); -void tcg_gen_qemu_st_i64(TCGv_i64, TCGv, TCGArg, MemOp); - -static inline void tcg_gen_qemu_ld8u(TCGv ret, TCGv addr, int mem_index) +static inline void +tcg_gen_qemu_ld_i32(TCGv_i32 v, TCGv a, TCGArg i, MemOp m) { - tcg_gen_qemu_ld_tl(ret, addr, mem_index, MO_UB); + tcg_gen_qemu_ld_i32_chk(v, tcgv_tl_temp(a), i, m, TCG_TYPE_TL); } -static inline void tcg_gen_qemu_ld8s(TCGv ret, TCGv addr, int mem_index) +static inline void +tcg_gen_qemu_st_i32(TCGv_i32 v, TCGv a, TCGArg i, MemOp m) { - tcg_gen_qemu_ld_tl(ret, addr, mem_index, MO_SB); + tcg_gen_qemu_st_i32_chk(v, tcgv_tl_temp(a), i, m, TCG_TYPE_TL); } -static inline void tcg_gen_qemu_ld16u(TCGv ret, TCGv addr, int mem_index) +static inline void +tcg_gen_qemu_ld_i64(TCGv_i64 v, TCGv a, TCGArg i, MemOp m) { - tcg_gen_qemu_ld_tl(ret, addr, mem_index, MO_TEUW); + tcg_gen_qemu_ld_i64_chk(v, tcgv_tl_temp(a), i, m, TCG_TYPE_TL); } -static inline void tcg_gen_qemu_ld16s(TCGv ret, TCGv addr, int mem_index) +static inline void +tcg_gen_qemu_st_i64(TCGv_i64 v, TCGv a, TCGArg i, MemOp m) { - tcg_gen_qemu_ld_tl(ret, addr, mem_index, MO_TESW); + tcg_gen_qemu_st_i64_chk(v, tcgv_tl_temp(a), i, m, TCG_TYPE_TL); } -static inline void tcg_gen_qemu_ld32u(TCGv ret, TCGv addr, int mem_index) +static inline void +tcg_gen_qemu_ld_i128(TCGv_i128 v, TCGv a, TCGArg i, MemOp m) { - tcg_gen_qemu_ld_tl(ret, addr, mem_index, MO_TEUL); + tcg_gen_qemu_ld_i128_chk(v, tcgv_tl_temp(a), i, m, TCG_TYPE_TL); } -static inline void tcg_gen_qemu_ld32s(TCGv ret, TCGv addr, int mem_index) +static inline void +tcg_gen_qemu_st_i128(TCGv_i128 v, TCGv a, TCGArg i, MemOp m) { - tcg_gen_qemu_ld_tl(ret, addr, mem_index, MO_TESL); + tcg_gen_qemu_st_i128_chk(v, tcgv_tl_temp(a), i, m, TCG_TYPE_TL); } -static inline void tcg_gen_qemu_ld64(TCGv_i64 ret, TCGv addr, int mem_index) -{ - tcg_gen_qemu_ld_i64(ret, addr, mem_index, MO_TEUQ); -} +#define DEF_ATOMIC2(N, S) \ + static inline void N##_##S(TCGv_##S r, TCGv a, TCGv_##S v, \ + TCGArg i, MemOp m) \ + { N##_##S##_chk(r, tcgv_tl_temp(a), v, i, m, TCG_TYPE_TL); } -static inline void tcg_gen_qemu_st8(TCGv arg, TCGv addr, int mem_index) -{ - tcg_gen_qemu_st_tl(arg, addr, mem_index, MO_UB); -} +#define DEF_ATOMIC3(N, S) \ + static inline void N##_##S(TCGv_##S r, TCGv a, TCGv_##S o, \ + TCGv_##S n, TCGArg i, MemOp m) \ + { N##_##S##_chk(r, tcgv_tl_temp(a), o, n, i, m, TCG_TYPE_TL); } -static inline void tcg_gen_qemu_st16(TCGv arg, TCGv addr, int mem_index) -{ - tcg_gen_qemu_st_tl(arg, addr, mem_index, MO_TEUW); -} +DEF_ATOMIC3(tcg_gen_atomic_cmpxchg, i32) +DEF_ATOMIC3(tcg_gen_atomic_cmpxchg, i64) +DEF_ATOMIC3(tcg_gen_atomic_cmpxchg, i128) -static inline void tcg_gen_qemu_st32(TCGv arg, TCGv addr, int mem_index) -{ - tcg_gen_qemu_st_tl(arg, addr, mem_index, MO_TEUL); -} +DEF_ATOMIC3(tcg_gen_nonatomic_cmpxchg, i32) +DEF_ATOMIC3(tcg_gen_nonatomic_cmpxchg, i64) +DEF_ATOMIC3(tcg_gen_nonatomic_cmpxchg, i128) -static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) -{ - tcg_gen_qemu_st_i64(arg, addr, mem_index, MO_TEUQ); -} +DEF_ATOMIC2(tcg_gen_atomic_xchg, i32) +DEF_ATOMIC2(tcg_gen_atomic_xchg, i64) -void tcg_gen_atomic_cmpxchg_i32(TCGv_i32, TCGv, TCGv_i32, TCGv_i32, - TCGArg, MemOp); -void tcg_gen_atomic_cmpxchg_i64(TCGv_i64, TCGv, TCGv_i64, TCGv_i64, - TCGArg, MemOp); +DEF_ATOMIC2(tcg_gen_atomic_fetch_add, i32) +DEF_ATOMIC2(tcg_gen_atomic_fetch_add, i64) +DEF_ATOMIC2(tcg_gen_atomic_fetch_and, i32) +DEF_ATOMIC2(tcg_gen_atomic_fetch_and, i64) +DEF_ATOMIC2(tcg_gen_atomic_fetch_or, i32) +DEF_ATOMIC2(tcg_gen_atomic_fetch_or, i64) +DEF_ATOMIC2(tcg_gen_atomic_fetch_xor, i32) +DEF_ATOMIC2(tcg_gen_atomic_fetch_xor, i64) +DEF_ATOMIC2(tcg_gen_atomic_fetch_smin, i32) +DEF_ATOMIC2(tcg_gen_atomic_fetch_smin, i64) +DEF_ATOMIC2(tcg_gen_atomic_fetch_umin, i32) +DEF_ATOMIC2(tcg_gen_atomic_fetch_umin, i64) +DEF_ATOMIC2(tcg_gen_atomic_fetch_smax, i32) +DEF_ATOMIC2(tcg_gen_atomic_fetch_smax, i64) +DEF_ATOMIC2(tcg_gen_atomic_fetch_umax, i32) +DEF_ATOMIC2(tcg_gen_atomic_fetch_umax, i64) -void tcg_gen_atomic_xchg_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_xchg_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); +DEF_ATOMIC2(tcg_gen_atomic_add_fetch, i32) +DEF_ATOMIC2(tcg_gen_atomic_add_fetch, i64) +DEF_ATOMIC2(tcg_gen_atomic_and_fetch, i32) +DEF_ATOMIC2(tcg_gen_atomic_and_fetch, i64) +DEF_ATOMIC2(tcg_gen_atomic_or_fetch, i32) +DEF_ATOMIC2(tcg_gen_atomic_or_fetch, i64) +DEF_ATOMIC2(tcg_gen_atomic_xor_fetch, i32) +DEF_ATOMIC2(tcg_gen_atomic_xor_fetch, i64) +DEF_ATOMIC2(tcg_gen_atomic_smin_fetch, i32) +DEF_ATOMIC2(tcg_gen_atomic_smin_fetch, i64) +DEF_ATOMIC2(tcg_gen_atomic_umin_fetch, i32) +DEF_ATOMIC2(tcg_gen_atomic_umin_fetch, i64) +DEF_ATOMIC2(tcg_gen_atomic_smax_fetch, i32) +DEF_ATOMIC2(tcg_gen_atomic_smax_fetch, i64) +DEF_ATOMIC2(tcg_gen_atomic_umax_fetch, i32) +DEF_ATOMIC2(tcg_gen_atomic_umax_fetch, i64) -void tcg_gen_atomic_fetch_add_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_fetch_add_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); -void tcg_gen_atomic_fetch_and_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_fetch_and_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); -void tcg_gen_atomic_fetch_or_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_fetch_or_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); -void tcg_gen_atomic_fetch_xor_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_fetch_xor_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); -void tcg_gen_atomic_fetch_smin_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_fetch_smin_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); -void tcg_gen_atomic_fetch_umin_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_fetch_umin_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); -void tcg_gen_atomic_fetch_smax_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_fetch_smax_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); -void tcg_gen_atomic_fetch_umax_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_fetch_umax_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); - -void tcg_gen_atomic_add_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_add_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); -void tcg_gen_atomic_and_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_and_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); -void tcg_gen_atomic_or_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_or_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); -void tcg_gen_atomic_xor_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_xor_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); -void tcg_gen_atomic_smin_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_smin_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); -void tcg_gen_atomic_umin_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_umin_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); -void tcg_gen_atomic_smax_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_smax_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); -void tcg_gen_atomic_umax_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, MemOp); -void tcg_gen_atomic_umax_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, MemOp); - -void tcg_gen_mov_vec(TCGv_vec, TCGv_vec); -void tcg_gen_dup_i32_vec(unsigned vece, TCGv_vec, TCGv_i32); -void tcg_gen_dup_i64_vec(unsigned vece, TCGv_vec, TCGv_i64); -void tcg_gen_dup_mem_vec(unsigned vece, TCGv_vec, TCGv_ptr, tcg_target_long); -void tcg_gen_dupi_vec(unsigned vece, TCGv_vec, uint64_t); -void tcg_gen_add_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_sub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_mul_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_and_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_or_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_xor_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_andc_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_orc_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_nand_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_nor_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_eqv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_not_vec(unsigned vece, TCGv_vec r, TCGv_vec a); -void tcg_gen_neg_vec(unsigned vece, TCGv_vec r, TCGv_vec a); -void tcg_gen_abs_vec(unsigned vece, TCGv_vec r, TCGv_vec a); -void tcg_gen_ssadd_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_usadd_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_sssub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_ussub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_smin_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_umin_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_smax_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); -void tcg_gen_umax_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); - -void tcg_gen_shli_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); -void tcg_gen_shri_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); -void tcg_gen_sari_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); -void tcg_gen_rotli_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); -void tcg_gen_rotri_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); - -void tcg_gen_shls_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); -void tcg_gen_shrs_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); -void tcg_gen_sars_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); -void tcg_gen_rotls_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_i32 s); - -void tcg_gen_shlv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); -void tcg_gen_shrv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); -void tcg_gen_sarv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); -void tcg_gen_rotlv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); -void tcg_gen_rotrv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec s); - -void tcg_gen_cmp_vec(TCGCond cond, unsigned vece, TCGv_vec r, - TCGv_vec a, TCGv_vec b); - -void tcg_gen_bitsel_vec(unsigned vece, TCGv_vec r, TCGv_vec a, - TCGv_vec b, TCGv_vec c); -void tcg_gen_cmpsel_vec(TCGCond cond, unsigned vece, TCGv_vec r, - TCGv_vec a, TCGv_vec b, TCGv_vec c, TCGv_vec d); - -void tcg_gen_ld_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset); -void tcg_gen_st_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset); -void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); +#undef DEF_ATOMIC2 +#undef DEF_ATOMIC3 #if TARGET_LONG_BITS == 64 #define tcg_gen_movi_tl tcg_gen_movi_i64 @@ -1057,6 +198,8 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_brcondi_tl tcg_gen_brcondi_i64 #define tcg_gen_setcond_tl tcg_gen_setcond_i64 #define tcg_gen_setcondi_tl tcg_gen_setcondi_i64 +#define tcg_gen_negsetcond_tl tcg_gen_negsetcond_i64 +#define tcg_gen_negsetcondi_tl tcg_gen_negsetcondi_i64 #define tcg_gen_mul_tl tcg_gen_mul_i64 #define tcg_gen_muli_tl tcg_gen_muli_i64 #define tcg_gen_div_tl tcg_gen_div_i64 @@ -1076,6 +219,7 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_ext16s_tl tcg_gen_ext16s_i64 #define tcg_gen_ext32u_tl tcg_gen_ext32u_i64 #define tcg_gen_ext32s_tl tcg_gen_ext32s_i64 +#define tcg_gen_ext_tl tcg_gen_ext_i64 #define tcg_gen_bswap16_tl tcg_gen_bswap16_i64 #define tcg_gen_bswap32_tl tcg_gen_bswap32_i64 #define tcg_gen_bswap64_tl tcg_gen_bswap64_i64 @@ -1104,9 +248,7 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_extract_tl tcg_gen_extract_i64 #define tcg_gen_sextract_tl tcg_gen_sextract_i64 #define tcg_gen_extract2_tl tcg_gen_extract2_i64 -#define tcg_const_tl tcg_const_i64 #define tcg_constant_tl tcg_constant_i64 -#define tcg_const_local_tl tcg_const_local_i64 #define tcg_gen_movcond_tl tcg_gen_movcond_i64 #define tcg_gen_add2_tl tcg_gen_add2_i64 #define tcg_gen_sub2_tl tcg_gen_sub2_i64 @@ -1137,6 +279,7 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_atomic_umax_fetch_tl tcg_gen_atomic_umax_fetch_i64 #define tcg_gen_dup_tl_vec tcg_gen_dup_i64_vec #define tcg_gen_dup_tl tcg_gen_dup_i64 +#define dup_const_tl dup_const #else #define tcg_gen_movi_tl tcg_gen_movi_i32 #define tcg_gen_mov_tl tcg_gen_mov_i32 @@ -1175,6 +318,8 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_brcondi_tl tcg_gen_brcondi_i32 #define tcg_gen_setcond_tl tcg_gen_setcond_i32 #define tcg_gen_setcondi_tl tcg_gen_setcondi_i32 +#define tcg_gen_negsetcond_tl tcg_gen_negsetcond_i32 +#define tcg_gen_negsetcondi_tl tcg_gen_negsetcondi_i32 #define tcg_gen_mul_tl tcg_gen_mul_i32 #define tcg_gen_muli_tl tcg_gen_muli_i32 #define tcg_gen_div_tl tcg_gen_div_i32 @@ -1194,6 +339,7 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_ext16s_tl tcg_gen_ext16s_i32 #define tcg_gen_ext32u_tl tcg_gen_mov_i32 #define tcg_gen_ext32s_tl tcg_gen_mov_i32 +#define tcg_gen_ext_tl tcg_gen_ext_i32 #define tcg_gen_bswap16_tl tcg_gen_bswap16_i32 #define tcg_gen_bswap32_tl(D, S, F) tcg_gen_bswap32_i32(D, S) #define tcg_gen_bswap_tl tcg_gen_bswap32_i32 @@ -1220,9 +366,7 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_extract_tl tcg_gen_extract_i32 #define tcg_gen_sextract_tl tcg_gen_sextract_i32 #define tcg_gen_extract2_tl tcg_gen_extract2_i32 -#define tcg_const_tl tcg_const_i32 #define tcg_constant_tl tcg_constant_i32 -#define tcg_const_local_tl tcg_const_local_i32 #define tcg_gen_movcond_tl tcg_gen_movcond_i32 #define tcg_gen_add2_tl tcg_gen_add2_i32 #define tcg_gen_sub2_tl tcg_gen_sub2_i32 @@ -1253,89 +397,14 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_atomic_umax_fetch_tl tcg_gen_atomic_umax_fetch_i32 #define tcg_gen_dup_tl_vec tcg_gen_dup_i32_vec #define tcg_gen_dup_tl tcg_gen_dup_i32 -#endif -#if UINTPTR_MAX == UINT32_MAX -# define PTR i32 -# define NAT TCGv_i32 -#else -# define PTR i64 -# define NAT TCGv_i64 -#endif - -static inline void tcg_gen_ld_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o) -{ - glue(tcg_gen_ld_,PTR)((NAT)r, a, o); -} - -static inline void tcg_gen_st_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o) -{ - glue(tcg_gen_st_, PTR)((NAT)r, a, o); -} - -static inline void tcg_gen_discard_ptr(TCGv_ptr a) -{ - glue(tcg_gen_discard_,PTR)((NAT)a); -} - -static inline void tcg_gen_add_ptr(TCGv_ptr r, TCGv_ptr a, TCGv_ptr b) -{ - glue(tcg_gen_add_,PTR)((NAT)r, (NAT)a, (NAT)b); -} - -static inline void tcg_gen_addi_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t b) -{ - glue(tcg_gen_addi_,PTR)((NAT)r, (NAT)a, b); -} - -static inline void tcg_gen_mov_ptr(TCGv_ptr d, TCGv_ptr s) -{ - glue(tcg_gen_mov_,PTR)((NAT)d, (NAT)s); -} - -static inline void tcg_gen_brcondi_ptr(TCGCond cond, TCGv_ptr a, - intptr_t b, TCGLabel *label) -{ - glue(tcg_gen_brcondi_,PTR)(cond, (NAT)a, b, label); -} - -static inline void tcg_gen_ext_i32_ptr(TCGv_ptr r, TCGv_i32 a) -{ -#if UINTPTR_MAX == UINT32_MAX - tcg_gen_mov_i32((NAT)r, a); -#else - tcg_gen_ext_i32_i64((NAT)r, a); -#endif -} - -static inline void tcg_gen_trunc_i64_ptr(TCGv_ptr r, TCGv_i64 a) -{ -#if UINTPTR_MAX == UINT32_MAX - tcg_gen_extrl_i64_i32((NAT)r, a); -#else - tcg_gen_mov_i64((NAT)r, a); -#endif -} - -static inline void tcg_gen_extu_ptr_i64(TCGv_i64 r, TCGv_ptr a) -{ -#if UINTPTR_MAX == UINT32_MAX - tcg_gen_extu_i32_i64(r, (NAT)a); -#else - tcg_gen_mov_i64(r, (NAT)a); -#endif -} - -static inline void tcg_gen_trunc_ptr_i32(TCGv_i32 r, TCGv_ptr a) -{ -#if UINTPTR_MAX == UINT32_MAX - tcg_gen_mov_i32(r, (NAT)a); -#else - tcg_gen_extrl_i64_i32(r, (NAT)a); -#endif -} - -#undef PTR -#undef NAT +#define dup_const_tl(VECE, C) \ + (__builtin_constant_p(VECE) \ + ? ( (VECE) == MO_8 ? 0x01010101ul * (uint8_t)(C) \ + : (VECE) == MO_16 ? 0x00010001ul * (uint16_t)(C) \ + : (VECE) == MO_32 ? 0x00000001ul * (uint32_t)(C) \ + : (qemu_build_not_reached_always(), 0)) \ + : (target_long)dup_const(VECE, C)) +#endif /* TARGET_LONG_BITS == 64 */ #endif /* TCG_TCG_OP_H */ diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index dd444734d9..b80227fa1c 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -46,7 +46,8 @@ DEF(mb, 0, 0, 1, 0) DEF(mov_i32, 1, 1, 0, TCG_OPF_NOT_PRESENT) DEF(setcond_i32, 1, 2, 1, 0) -DEF(movcond_i32, 1, 4, 1, IMPL(TCG_TARGET_HAS_movcond_i32)) +DEF(negsetcond_i32, 1, 2, 1, IMPL(TCG_TARGET_HAS_negsetcond_i32)) +DEF(movcond_i32, 1, 4, 1, 0) /* load/store */ DEF(ld8u_i32, 1, 1, 1, 0) DEF(ld8s_i32, 1, 1, 1, 0) @@ -99,7 +100,7 @@ DEF(ext16u_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_ext16u_i32)) DEF(bswap16_i32, 1, 1, 1, IMPL(TCG_TARGET_HAS_bswap16_i32)) DEF(bswap32_i32, 1, 1, 1, IMPL(TCG_TARGET_HAS_bswap32_i32)) DEF(not_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_not_i32)) -DEF(neg_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_neg_i32)) +DEF(neg_i32, 1, 1, 0, 0) DEF(andc_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_andc_i32)) DEF(orc_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_orc_i32)) DEF(eqv_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_eqv_i32)) @@ -111,7 +112,8 @@ DEF(ctpop_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_ctpop_i32)) DEF(mov_i64, 1, 1, 0, TCG_OPF_64BIT | TCG_OPF_NOT_PRESENT) DEF(setcond_i64, 1, 2, 1, IMPL64) -DEF(movcond_i64, 1, 4, 1, IMPL64 | IMPL(TCG_TARGET_HAS_movcond_i64)) +DEF(negsetcond_i64, 1, 2, 1, IMPL64 | IMPL(TCG_TARGET_HAS_negsetcond_i64)) +DEF(movcond_i64, 1, 4, 1, IMPL64) /* load/store */ DEF(ld8u_i64, 1, 1, 1, IMPL64) DEF(ld8s_i64, 1, 1, 1, IMPL64) @@ -152,10 +154,10 @@ DEF(extract2_i64, 1, 2, 1, IMPL64 | IMPL(TCG_TARGET_HAS_extract2_i64)) DEF(ext_i32_i64, 1, 1, 0, IMPL64) DEF(extu_i32_i64, 1, 1, 0, IMPL64) DEF(extrl_i64_i32, 1, 1, 0, - IMPL(TCG_TARGET_HAS_extrl_i64_i32) + IMPL(TCG_TARGET_HAS_extr_i64_i32) | (TCG_TARGET_REG_BITS == 32 ? TCG_OPF_NOT_PRESENT : 0)) DEF(extrh_i64_i32, 1, 1, 0, - IMPL(TCG_TARGET_HAS_extrh_i64_i32) + IMPL(TCG_TARGET_HAS_extr_i64_i32) | (TCG_TARGET_REG_BITS == 32 ? TCG_OPF_NOT_PRESENT : 0)) DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH | IMPL64) @@ -169,7 +171,7 @@ DEF(bswap16_i64, 1, 1, 1, IMPL64 | IMPL(TCG_TARGET_HAS_bswap16_i64)) DEF(bswap32_i64, 1, 1, 1, IMPL64 | IMPL(TCG_TARGET_HAS_bswap32_i64)) DEF(bswap64_i64, 1, 1, 1, IMPL64 | IMPL(TCG_TARGET_HAS_bswap64_i64)) DEF(not_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_not_i64)) -DEF(neg_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_neg_i64)) +DEF(neg_i64, 1, 1, 0, IMPL64) DEF(andc_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_andc_i64)) DEF(orc_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_orc_i64)) DEF(eqv_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_eqv_i64)) @@ -186,12 +188,11 @@ DEF(muls2_i64, 2, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_muls2_i64)) DEF(muluh_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_muluh_i64)) DEF(mulsh_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_mulsh_i64)) -#define TLADDR_ARGS (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? 1 : 2) #define DATA64_ARGS (TCG_TARGET_REG_BITS == 64 ? 1 : 2) -/* QEMU specific */ -DEF(insn_start, 0, 0, TLADDR_ARGS * TARGET_INSN_START_WORDS, - TCG_OPF_NOT_PRESENT) +/* There are tcg_ctx->insn_start_words here, not just one. */ +DEF(insn_start, 0, 0, DATA64_ARGS, TCG_OPF_NOT_PRESENT) + DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) @@ -199,19 +200,46 @@ DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(plugin_cb_start, 0, 0, 3, TCG_OPF_NOT_PRESENT) DEF(plugin_cb_end, 0, 0, 0, TCG_OPF_NOT_PRESENT) -DEF(qemu_ld_i32, 1, TLADDR_ARGS, 1, +/* Replicate ld/st ops for 32 and 64-bit guest addresses. */ +DEF(qemu_ld_a32_i32, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_st_i32, 0, TLADDR_ARGS + 1, 1, +DEF(qemu_st_a32_i32, 0, 1 + 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_ld_i64, DATA64_ARGS, TLADDR_ARGS, 1, +DEF(qemu_ld_a32_i64, DATA64_ARGS, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT) -DEF(qemu_st_i64, 0, TLADDR_ARGS + DATA64_ARGS, 1, +DEF(qemu_st_a32_i64, 0, DATA64_ARGS + 1, 1, + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT) + +DEF(qemu_ld_a64_i32, 1, DATA64_ARGS, 1, + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st_a64_i32, 0, 1 + DATA64_ARGS, 1, + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld_a64_i64, DATA64_ARGS, DATA64_ARGS, 1, + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT) +DEF(qemu_st_a64_i64, 0, DATA64_ARGS + DATA64_ARGS, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT) /* Only used by i386 to cope with stupid register constraints. */ -DEF(qemu_st8_i32, 0, TLADDR_ARGS + 1, 1, +DEF(qemu_st8_a32_i32, 0, 1 + 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | IMPL(TCG_TARGET_HAS_qemu_st8_i32)) +DEF(qemu_st8_a64_i32, 0, 1 + DATA64_ARGS, 1, + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | + IMPL(TCG_TARGET_HAS_qemu_st8_i32)) + +/* Only for 64-bit hosts at the moment. */ +DEF(qemu_ld_a32_i128, 2, 1, 1, + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT | + IMPL(TCG_TARGET_HAS_qemu_ldst_i128)) +DEF(qemu_ld_a64_i128, 2, 1, 1, + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT | + IMPL(TCG_TARGET_HAS_qemu_ldst_i128)) +DEF(qemu_st_a32_i128, 0, 3, 1, + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT | + IMPL(TCG_TARGET_HAS_qemu_ldst_i128)) +DEF(qemu_st_a64_i128, 0, 3, 1, + TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT | + IMPL(TCG_TARGET_HAS_qemu_ldst_i128)) /* Host vector support. */ @@ -283,7 +311,6 @@ DEF(tci_movi, 1, 0, 1, TCG_OPF_NOT_PRESENT) DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT) #endif -#undef TLADDR_ARGS #undef DATA64_ARGS #undef IMPL #undef IMPL64 diff --git a/include/tcg/tcg-temp-internal.h b/include/tcg/tcg-temp-internal.h new file mode 100644 index 0000000000..44192c55a9 --- /dev/null +++ b/include/tcg/tcg-temp-internal.h @@ -0,0 +1,45 @@ +/* + * TCG internals related to TCG temp allocation + * + * Copyright (c) 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. + */ + +#ifndef TCG_TEMP_INTERNAL_H +#define TCG_TEMP_INTERNAL_H + +/* + * Allocation and freeing of EBB temps is reserved to TCG internals + */ + +void tcg_temp_free_internal(TCGTemp *); + +void tcg_temp_free_i32(TCGv_i32 arg); +void tcg_temp_free_i64(TCGv_i64 arg); +void tcg_temp_free_i128(TCGv_i128 arg); +void tcg_temp_free_ptr(TCGv_ptr arg); +void tcg_temp_free_vec(TCGv_vec arg); + +TCGv_i32 tcg_temp_ebb_new_i32(void); +TCGv_i64 tcg_temp_ebb_new_i64(void); +TCGv_ptr tcg_temp_ebb_new_ptr(void); +TCGv_i128 tcg_temp_ebb_new_i128(void); + +#endif /* TCG_TEMP_FREE_H */ diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 5aaf1a6f40..9d916bad64 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -25,47 +25,25 @@ #ifndef TCG_H #define TCG_H -#include "cpu.h" #include "exec/memop.h" #include "exec/memopidx.h" #include "qemu/bitops.h" #include "qemu/plugin.h" #include "qemu/queue.h" #include "tcg/tcg-mo.h" +#include "tcg-target-reg-bits.h" #include "tcg-target.h" #include "tcg/tcg-cond.h" +#include "tcg/debug-assert.h" /* XXX: make safe guess about sizes */ #define MAX_OP_PER_INSTR 266 -#if HOST_LONG_BITS == 32 -#define MAX_OPC_PARAM_PER_ARG 2 -#else -#define MAX_OPC_PARAM_PER_ARG 1 -#endif -#define MAX_OPC_PARAM_IARGS 7 -#define MAX_OPC_PARAM_OARGS 1 -#define MAX_OPC_PARAM_ARGS (MAX_OPC_PARAM_IARGS + MAX_OPC_PARAM_OARGS) - -/* A Call op needs up to 4 + 2N parameters on 32-bit archs, - * and up to 4 + N parameters on 64-bit archs - * (N = number of input arguments + output arguments). */ -#define MAX_OPC_PARAM (4 + (MAX_OPC_PARAM_PER_ARG * MAX_OPC_PARAM_ARGS)) +#define MAX_CALL_IARGS 7 #define CPU_TEMP_BUF_NLONGS 128 #define TCG_STATIC_FRAME_SIZE (CPU_TEMP_BUF_NLONGS * sizeof(long)) -/* Default target word size to pointer size. */ -#ifndef TCG_TARGET_REG_BITS -# if UINTPTR_MAX == UINT32_MAX -# define TCG_TARGET_REG_BITS 32 -# elif UINTPTR_MAX == UINT64_MAX -# define TCG_TARGET_REG_BITS 64 -# else -# error Unknown pointer size for tcg target -# endif -#endif - #if TCG_TARGET_REG_BITS == 32 typedef int32_t tcg_target_long; typedef uint32_t tcg_target_ulong; @@ -80,15 +58,6 @@ typedef uint64_t tcg_target_ulong; #error unsupported #endif -/* Oversized TCG guests make things like MTTCG hard - * as we can't use atomics for cputlb updates. - */ -#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS -#define TCG_OVERSIZED_GUEST 1 -#else -#define TCG_OVERSIZED_GUEST 0 -#endif - #if TCG_TARGET_NB_REGS <= 32 typedef uint32_t TCGRegSet; #elif TCG_TARGET_NB_REGS <= 64 @@ -99,8 +68,7 @@ typedef uint64_t TCGRegSet; #if TCG_TARGET_REG_BITS == 32 /* Turn some undef macros into false macros. */ -#define TCG_TARGET_HAS_extrl_i64_i32 0 -#define TCG_TARGET_HAS_extrh_i64_i32 0 +#define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_div_i64 0 #define TCG_TARGET_HAS_rem_i64 0 #define TCG_TARGET_HAS_div2_i64 0 @@ -114,7 +82,6 @@ typedef uint64_t TCGRegSet; #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 -#define TCG_TARGET_HAS_neg_i64 0 #define TCG_TARGET_HAS_not_i64 0 #define TCG_TARGET_HAS_andc_i64 0 #define TCG_TARGET_HAS_orc_i64 0 @@ -128,7 +95,7 @@ typedef uint64_t TCGRegSet; #define TCG_TARGET_HAS_extract_i64 0 #define TCG_TARGET_HAS_sextract_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_movcond_i64 0 +#define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 0 @@ -167,13 +134,6 @@ typedef uint64_t TCGRegSet; #define TCG_TARGET_HAS_rem_i64 0 #endif -/* For 32-bit targets, some sort of unsigned widening multiply is required. */ -#if TCG_TARGET_REG_BITS == 32 \ - && !(defined(TCG_TARGET_HAS_mulu2_i32) \ - || defined(TCG_TARGET_HAS_muluh_i32)) -# error "Missing unsigned widening multiply" -#endif - #if !defined(TCG_TARGET_HAS_v64) \ && !defined(TCG_TARGET_HAS_v128) \ && !defined(TCG_TARGET_HAS_v256) @@ -210,12 +170,6 @@ typedef uint64_t TCGRegSet; #define TCG_TARGET_HAS_v256 0 #endif -#ifndef TARGET_INSN_START_EXTRA_WORDS -# define TARGET_INSN_START_WORDS 1 -#else -# define TARGET_INSN_START_WORDS (1 + TARGET_INSN_START_EXTRA_WORDS) -#endif - typedef enum TCGOpcode { #define DEF(name, oargs, iargs, cargs, flags) INDEX_op_ ## name, #include "tcg/tcg-opc.h" @@ -241,14 +195,6 @@ typedef uint64_t tcg_insn_unit; /* The port better have done this. */ #endif - -#if defined CONFIG_DEBUG_TCG || defined QEMU_STATIC_ANALYSIS -# define tcg_debug_assert(X) do { assert(X); } while (0) -#else -# define tcg_debug_assert(X) \ - do { if (!(X)) { __builtin_unreachable(); } } while (0) -#endif - typedef struct TCGRelocation TCGRelocation; struct TCGRelocation { QSIMPLEQ_ENTRY(TCGRelocation) next; @@ -257,16 +203,23 @@ struct TCGRelocation { int type; }; +typedef struct TCGOp TCGOp; +typedef struct TCGLabelUse TCGLabelUse; +struct TCGLabelUse { + QSIMPLEQ_ENTRY(TCGLabelUse) next; + TCGOp *op; +}; + typedef struct TCGLabel TCGLabel; struct TCGLabel { - unsigned present : 1; - unsigned has_value : 1; - unsigned id : 14; - unsigned refs : 16; + bool present; + bool has_value; + uint16_t id; union { uintptr_t value; const tcg_insn_unit *value_ptr; } u; + QSIMPLEQ_HEAD(, TCGLabelUse) branches; QSIMPLEQ_HEAD(, TCGRelocation) relocs; QSIMPLEQ_ENTRY(TCGLabel) next; }; @@ -289,12 +242,14 @@ typedef struct TCGPool { typedef enum TCGType { TCG_TYPE_I32, TCG_TYPE_I64, + TCG_TYPE_I128, TCG_TYPE_V64, TCG_TYPE_V128, TCG_TYPE_V256, - TCG_TYPE_COUNT, /* number of different types */ + /* Number of different types (integer not enum) */ +#define TCG_TYPE_COUNT (TCG_TYPE_V256 + 1) /* An alias for the size of the host register. */ #if TCG_TARGET_REG_BITS == 32 @@ -309,15 +264,24 @@ typedef enum TCGType { #else TCG_TYPE_PTR = TCG_TYPE_I64, #endif - - /* An alias for the size of the target "long", aka register. */ -#if TARGET_LONG_BITS == 64 - TCG_TYPE_TL = TCG_TYPE_I64, -#else - TCG_TYPE_TL = TCG_TYPE_I32, -#endif } TCGType; +/** + * tcg_type_size + * @t: type + * + * Return the size of the type in bytes. + */ +static inline int tcg_type_size(TCGType t) +{ + unsigned i = t; + if (i >= TCG_TYPE_V64) { + tcg_debug_assert(i < TCG_TYPE_COUNT); + i -= TCG_TYPE_V64 - 1; + } + return 4 << i; +} + /** * get_alignment_bits * @memop: MemOp value @@ -338,10 +302,6 @@ static inline unsigned get_alignment_bits(MemOp memop) /* A specific alignment requirement. */ a = a >> MO_ASHIFT; } -#if defined(CONFIG_SOFTMMU) - /* The requested alignment cannot overlap the TLB flags. */ - tcg_debug_assert((TLB_FLAGS_MASK & ((1 << a) - 1)) == 0); -#endif return a; } @@ -353,13 +313,14 @@ typedef tcg_target_ulong TCGArg; in tcg/README. Target CPU front-end code uses these types to deal with TCG variables as it emits TCG code via the tcg_gen_* functions. They come in several flavours: - * TCGv_i32 : 32 bit integer type - * TCGv_i64 : 64 bit integer type - * TCGv_ptr : a host pointer type - * TCGv_vec : a host vector type; the exact size is not exposed - to the CPU front-end code. - * TCGv : an integer type the same size as target_ulong - (an alias for either TCGv_i32 or TCGv_i64) + * TCGv_i32 : 32 bit integer type + * TCGv_i64 : 64 bit integer type + * TCGv_i128 : 128 bit integer type + * TCGv_ptr : a host pointer type + * TCGv_vec : a host vector type; the exact size is not exposed + to the CPU front-end code. + * TCGv : an integer type the same size as target_ulong + (an alias for either TCGv_i32 or TCGv_i64) The compiler's type checking will complain if you mix them up and pass the wrong sized TCGv to a function. @@ -379,16 +340,10 @@ typedef tcg_target_ulong TCGArg; typedef struct TCGv_i32_d *TCGv_i32; typedef struct TCGv_i64_d *TCGv_i64; +typedef struct TCGv_i128_d *TCGv_i128; typedef struct TCGv_ptr_d *TCGv_ptr; typedef struct TCGv_vec_d *TCGv_vec; typedef TCGv_ptr TCGv_env; -#if TARGET_LONG_BITS == 32 -#define TCGv TCGv_i32 -#elif TARGET_LONG_BITS == 64 -#define TCGv TCGv_i64 -#else -#error Unhandled TARGET_LONG_BITS value -#endif /* call flags */ /* Helper does not read globals (either directly or through an exception). It @@ -400,6 +355,8 @@ typedef TCGv_ptr TCGv_env; #define TCG_CALL_NO_SIDE_EFFECTS 0x0004 /* Helper is G_NORETURN. */ #define TCG_CALL_NO_RETURN 0x0008 +/* Helper is part of Plugins. */ +#define TCG_CALL_PLUGIN 0x0010 /* convenience version of most used call flags */ #define TCG_CALL_NO_RWG TCG_CALL_NO_READ_GLOBALS @@ -408,9 +365,6 @@ typedef TCGv_ptr TCGv_env; #define TCG_CALL_NO_RWG_SE (TCG_CALL_NO_RWG | TCG_CALL_NO_SE) #define TCG_CALL_NO_WG_SE (TCG_CALL_NO_WG | TCG_CALL_NO_SE) -/* Used to align parameters. See the comment before tcgv_i32_temp. */ -#define TCG_CALL_DUMMY_ARG ((TCGArg)0) - /* * Flags for the bswap opcodes. * If IZ, the input is zero-extended, otherwise unknown. @@ -431,13 +385,15 @@ typedef enum TCGTempVal { } TCGTempVal; typedef enum TCGTempKind { - /* Temp is dead at the end of all basic blocks. */ - TEMP_NORMAL, - /* Temp is live across conditional branch, but dead otherwise. */ + /* + * Temp is dead at the end of the extended basic block (EBB), + * the single-entry multiple-exit region that falls through + * conditional branches. + */ TEMP_EBB, - /* Temp is saved across basic blocks but dead at the end of TBs. */ - TEMP_LOCAL, - /* Temp is saved across both basic blocks and translation blocks. */ + /* Temp is live across the entire translation block, but dead at end. */ + TEMP_TB, + /* Temp is live across the entire translation block, and between them. */ TEMP_GLOBAL, /* Temp is in a fixed register. */ TEMP_FIXED, @@ -456,6 +412,7 @@ typedef struct TCGTemp { unsigned int mem_coherent:1; unsigned int mem_allocated:1; unsigned int temp_allocated:1; + unsigned int temp_subindex:2; int64_t val; struct TCGTemp *mem_base; @@ -475,35 +432,35 @@ typedef struct TCGTempSet { unsigned long l[BITS_TO_LONGS(TCG_MAX_TEMPS)]; } TCGTempSet; -/* While we limit helpers to 6 arguments, for 32-bit hosts, with padding, - this imples a max of 6*2 (64-bit in) + 2 (64-bit out) = 14 operands. - There are never more than 2 outputs, which means that we can store all - dead + sync data within 16 bits. */ -#define DEAD_ARG 4 -#define SYNC_ARG 1 -typedef uint16_t TCGLifeData; +/* + * With 1 128-bit output, a 32-bit host requires 4 output parameters, + * which leaves a maximum of 28 other slots. Which is enough for 7 + * 128-bit operands. + */ +#define DEAD_ARG (1 << 4) +#define SYNC_ARG (1 << 0) +typedef uint32_t TCGLifeData; -/* The layout here is designed to avoid a bitfield crossing of - a 32-bit boundary, which would cause GCC to add extra padding. */ -typedef struct TCGOp { - TCGOpcode opc : 8; /* 8 */ +struct TCGOp { + TCGOpcode opc : 8; + unsigned nargs : 8; /* Parameters for this opcode. See below. */ - unsigned param1 : 4; /* 12 */ - unsigned param2 : 4; /* 16 */ + unsigned param1 : 8; + unsigned param2 : 8; /* Lifetime data of the operands. */ - unsigned life : 16; /* 32 */ + TCGLifeData life; /* Next and previous opcodes. */ QTAILQ_ENTRY(TCGOp) link; - /* Arguments for the opcode. */ - TCGArg args[MAX_OPC_PARAM]; - /* Register preferences for the output(s). */ TCGRegSet output_pref[2]; -} TCGOp; + + /* Arguments for the opcode. */ + TCGArg args[]; +}; #define TCGOP_CALLI(X) (X)->param1 #define TCGOP_CALLO(X) (X)->param2 @@ -514,26 +471,10 @@ typedef struct TCGOp { /* Make sure operands fit in the bitfields above. */ QEMU_BUILD_BUG_ON(NB_OPS > (1 << 8)); -typedef struct TCGProfile { - int64_t cpu_exec_time; - int64_t tb_count1; - int64_t tb_count; - int64_t op_count; /* total insn count */ - int op_count_max; /* max insn per TB */ - int temp_count_max; - int64_t temp_count; - int64_t del_op_count; - int64_t code_in_len; - int64_t code_out_len; - int64_t search_out_len; - int64_t interm_time; - int64_t code_time; - int64_t la_time; - int64_t opt_time; - int64_t restore_count; - int64_t restore_time; - int64_t table_op_count[NB_OPS]; -} TCGProfile; +static inline TCGRegSet output_pref(const TCGOp *op, unsigned i) +{ + return i < ARRAY_SIZE(op->output_pref) ? op->output_pref[i] : 0; +} struct TCGContext { uint8_t *pool_cur, *pool_end; @@ -543,28 +484,25 @@ struct TCGContext { int nb_temps; int nb_indirects; int nb_ops; + TCGType addr_type; /* TCG_TYPE_I32 or TCG_TYPE_I64 */ - /* goto_tb support */ - tcg_insn_unit *code_buf; - uint16_t *tb_jmp_reset_offset; /* tb->jmp_reset_offset */ - uintptr_t *tb_jmp_insn_offset; /* tb->jmp_target_arg if direct_jump */ - uintptr_t *tb_jmp_target_addr; /* tb->jmp_target_arg if !direct_jump */ + int page_mask; + uint8_t page_bits; + uint8_t tlb_dyn_max_bits; + uint8_t insn_start_words; + TCGBar guest_mo; TCGRegSet reserved_regs; - uint32_t tb_cflags; /* cflags of the current TB */ intptr_t current_frame_offset; intptr_t frame_start; intptr_t frame_end; TCGTemp *frame_temp; - tcg_insn_unit *code_ptr; - -#ifdef CONFIG_PROFILER - TCGProfile prof; -#endif + TranslationBlock *gen_tb; /* tb for which code is being generated */ + tcg_insn_unit *code_buf; /* pointer for start of tb */ + tcg_insn_unit *code_ptr; /* pointer for running end of tb */ #ifdef CONFIG_DEBUG_TCG - int temps_in_use; int goto_tb_issue_mask; const TCGOpcode *vecop_list; #endif @@ -609,18 +547,24 @@ struct TCGContext { #endif GHashTable *const_table[TCG_TYPE_COUNT]; - TCGTempSet free_temps[TCG_TYPE_COUNT * 2]; + TCGTempSet free_temps[TCG_TYPE_COUNT]; TCGTemp temps[TCG_MAX_TEMPS]; /* globals first, temps after */ QTAILQ_HEAD(, TCGOp) ops, free_ops; QSIMPLEQ_HEAD(, TCGLabel) labels; + /* + * When clear, new ops are added to the tail of @ops. + * When set, new ops are added in front of @emit_before_op. + */ + TCGOp *emit_before_op; + /* Tells which temporary holds a given register. It does not take into account fixed registers */ TCGTemp *reg_to_temp[TCG_TARGET_NB_REGS]; uint16_t gen_insn_end_off[TCG_MAX_INSNS]; - target_ulong gen_insn_data[TCG_MAX_INSNS][TARGET_INSN_START_WORDS]; + uint64_t *gen_insn_data; /* Exit to translator on overflow. */ sigjmp_buf jmp_trans; @@ -631,10 +575,16 @@ static inline bool temp_readonly(TCGTemp *ts) return ts->kind >= TEMP_FIXED; } +#ifdef CONFIG_USER_ONLY +extern bool tcg_use_softmmu; +#else +#define tcg_use_softmmu true +#endif + extern __thread TCGContext *tcg_ctx; extern const void *tcg_code_gen_epilogue; extern uintptr_t tcg_splitwx_diff; -extern TCGv_env cpu_env; +extern TCGv_env tcg_env; bool in_code_gen_buffer(const void *p); @@ -653,13 +603,6 @@ static inline void *tcg_splitwx_to_rw(const void *rx) } #endif -static inline size_t temp_idx(TCGTemp *ts) -{ - ptrdiff_t n = ts - tcg_ctx->temps; - tcg_debug_assert(n >= 0 && n < tcg_ctx->nb_temps); - return n; -} - static inline TCGArg temp_arg(TCGTemp *ts) { return (uintptr_t)ts; @@ -670,22 +613,36 @@ static inline TCGTemp *arg_temp(TCGArg a) return (TCGTemp *)(uintptr_t)a; } -/* Using the offset of a temporary, relative to TCGContext, rather than - its index means that we don't use 0. That leaves offset 0 free for - a NULL representation without having to leave index 0 unused. */ -static inline TCGTemp *tcgv_i32_temp(TCGv_i32 v) +#ifdef CONFIG_DEBUG_TCG +size_t temp_idx(TCGTemp *ts); +TCGTemp *tcgv_i32_temp(TCGv_i32 v); +#else +static inline size_t temp_idx(TCGTemp *ts) { - uintptr_t o = (uintptr_t)v; - TCGTemp *t = (void *)tcg_ctx + o; - tcg_debug_assert(offsetof(TCGContext, temps[temp_idx(t)]) == o); - return t; + return ts - tcg_ctx->temps; } +/* + * Using the offset of a temporary, relative to TCGContext, rather than + * its index means that we don't use 0. That leaves offset 0 free for + * a NULL representation without having to leave index 0 unused. + */ +static inline TCGTemp *tcgv_i32_temp(TCGv_i32 v) +{ + return (void *)tcg_ctx + (uintptr_t)v; +} +#endif + static inline TCGTemp *tcgv_i64_temp(TCGv_i64 v) { return tcgv_i32_temp((TCGv_i32)v); } +static inline TCGTemp *tcgv_i128_temp(TCGv_i128 v) +{ + return tcgv_i32_temp((TCGv_i32)v); +} + static inline TCGTemp *tcgv_ptr_temp(TCGv_ptr v) { return tcgv_i32_temp((TCGv_i32)v); @@ -706,6 +663,11 @@ static inline TCGArg tcgv_i64_arg(TCGv_i64 v) return temp_arg(tcgv_i64_temp(v)); } +static inline TCGArg tcgv_i128_arg(TCGv_i128 v) +{ + return temp_arg(tcgv_i128_temp(v)); +} + static inline TCGArg tcgv_ptr_arg(TCGv_ptr v) { return temp_arg(tcgv_ptr_temp(v)); @@ -727,6 +689,11 @@ static inline TCGv_i64 temp_tcgv_i64(TCGTemp *t) return (TCGv_i64)temp_tcgv_i32(t); } +static inline TCGv_i128 temp_tcgv_i128(TCGTemp *t) +{ + return (TCGv_i128)temp_tcgv_i32(t); +} + static inline TCGv_ptr temp_tcgv_ptr(TCGTemp *t) { return (TCGv_ptr)temp_tcgv_i32(t); @@ -737,18 +704,6 @@ static inline TCGv_vec temp_tcgv_vec(TCGTemp *t) return (TCGv_vec)temp_tcgv_i32(t); } -#if TCG_TARGET_REG_BITS == 32 -static inline TCGv_i32 TCGV_LOW(TCGv_i64 t) -{ - return temp_tcgv_i32(tcgv_i64_temp(t)); -} - -static inline TCGv_i32 TCGV_HIGH(TCGv_i64 t) -{ - return temp_tcgv_i32(tcgv_i64_temp(t) + 1); -} -#endif - static inline TCGArg tcg_get_insn_param(TCGOp *op, int arg) { return op->args[arg]; @@ -759,24 +714,24 @@ static inline void tcg_set_insn_param(TCGOp *op, int arg, TCGArg v) op->args[arg] = v; } -static inline target_ulong tcg_get_insn_start_param(TCGOp *op, int arg) +static inline uint64_t tcg_get_insn_start_param(TCGOp *op, int arg) { -#if TARGET_LONG_BITS <= TCG_TARGET_REG_BITS - return tcg_get_insn_param(op, arg); -#else - return tcg_get_insn_param(op, arg * 2) | - ((uint64_t)tcg_get_insn_param(op, arg * 2 + 1) << 32); -#endif + if (TCG_TARGET_REG_BITS == 64) { + return tcg_get_insn_param(op, arg); + } else { + return deposit64(tcg_get_insn_param(op, arg * 2), 32, 32, + tcg_get_insn_param(op, arg * 2 + 1)); + } } -static inline void tcg_set_insn_start_param(TCGOp *op, int arg, target_ulong v) +static inline void tcg_set_insn_start_param(TCGOp *op, int arg, uint64_t v) { -#if TARGET_LONG_BITS <= TCG_TARGET_REG_BITS - tcg_set_insn_param(op, arg, v); -#else - tcg_set_insn_param(op, arg * 2, v); - tcg_set_insn_param(op, arg * 2 + 1, v >> 32); -#endif + if (TCG_TARGET_REG_BITS == 64) { + tcg_set_insn_param(op, arg, v); + } else { + tcg_set_insn_param(op, arg * 2, v); + tcg_set_insn_param(op, arg * 2 + 1, v >> 32); + } } /* The last op that was emitted. */ @@ -835,122 +790,23 @@ static inline void *tcg_malloc(int size) } } -void tcg_init(size_t tb_size, int splitwx, unsigned max_cpus); -void tcg_register_thread(void); -void tcg_prologue_init(TCGContext *s); void tcg_func_start(TCGContext *s); -int tcg_gen_code(TCGContext *s, TranslationBlock *tb); +int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start); + +void tb_target_set_jmp_target(const TranslationBlock *, int, + uintptr_t, uintptr_t); void tcg_set_frame(TCGContext *s, TCGReg reg, intptr_t start, intptr_t size); -TCGTemp *tcg_global_mem_new_internal(TCGType, TCGv_ptr, - intptr_t, const char *); -TCGTemp *tcg_temp_new_internal(TCGType, bool); -void tcg_temp_free_internal(TCGTemp *); -TCGv_vec tcg_temp_new_vec(TCGType type); -TCGv_vec tcg_temp_new_vec_matching(TCGv_vec match); - -static inline void tcg_temp_free_i32(TCGv_i32 arg) -{ - tcg_temp_free_internal(tcgv_i32_temp(arg)); -} - -static inline void tcg_temp_free_i64(TCGv_i64 arg) -{ - tcg_temp_free_internal(tcgv_i64_temp(arg)); -} - -static inline void tcg_temp_free_ptr(TCGv_ptr arg) -{ - tcg_temp_free_internal(tcgv_ptr_temp(arg)); -} - -static inline void tcg_temp_free_vec(TCGv_vec arg) -{ - tcg_temp_free_internal(tcgv_vec_temp(arg)); -} - -static inline TCGv_i32 tcg_global_mem_new_i32(TCGv_ptr reg, intptr_t offset, - const char *name) -{ - TCGTemp *t = tcg_global_mem_new_internal(TCG_TYPE_I32, reg, offset, name); - return temp_tcgv_i32(t); -} - -static inline TCGv_i32 tcg_temp_new_i32(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I32, false); - return temp_tcgv_i32(t); -} - -static inline TCGv_i32 tcg_temp_local_new_i32(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I32, true); - return temp_tcgv_i32(t); -} - -static inline TCGv_i64 tcg_global_mem_new_i64(TCGv_ptr reg, intptr_t offset, - const char *name) -{ - TCGTemp *t = tcg_global_mem_new_internal(TCG_TYPE_I64, reg, offset, name); - return temp_tcgv_i64(t); -} - -static inline TCGv_i64 tcg_temp_new_i64(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I64, false); - return temp_tcgv_i64(t); -} - -static inline TCGv_i64 tcg_temp_local_new_i64(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I64, true); - return temp_tcgv_i64(t); -} - -static inline TCGv_ptr tcg_global_mem_new_ptr(TCGv_ptr reg, intptr_t offset, - const char *name) -{ - TCGTemp *t = tcg_global_mem_new_internal(TCG_TYPE_PTR, reg, offset, name); - return temp_tcgv_ptr(t); -} - -static inline TCGv_ptr tcg_temp_new_ptr(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_PTR, false); - return temp_tcgv_ptr(t); -} - -static inline TCGv_ptr tcg_temp_local_new_ptr(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_PTR, true); - return temp_tcgv_ptr(t); -} - -#if defined(CONFIG_DEBUG_TCG) -/* If you call tcg_clear_temp_count() at the start of a section of - * code which is not supposed to leak any TCG temporaries, then - * calling tcg_check_temp_count() at the end of the section will - * return 1 if the section did in fact leak a temporary. - */ -void tcg_clear_temp_count(void); -int tcg_check_temp_count(void); -#else -#define tcg_clear_temp_count() do { } while (0) -#define tcg_check_temp_count() 0 -#endif - -int64_t tcg_cpu_exec_time(void); -void tcg_dump_info(GString *buf); -void tcg_dump_op_count(GString *buf); - #define TCG_CT_CONST 1 /* any constant of register size */ typedef struct TCGArgConstraint { unsigned ct : 16; unsigned alias_index : 4; unsigned sort_index : 4; + unsigned pair_index : 4; + unsigned pair : 2; /* 0: none, 1: first, 2: second, 3: second alias */ bool oalias : 1; bool ialias : 1; bool newreg : 1; @@ -973,7 +829,7 @@ enum { /* Instruction operands are 64-bits (otherwise 32-bits). */ TCG_OPF_64BIT = 0x10, /* Instruction is optional and not implemented by the host, or insn - is generic and should not be implemened by the host. */ + is generic and should not be implemented by the host. */ TCG_OPF_NOT_PRESENT = 0x20, /* Instruction operands are vectors. */ TCG_OPF_VECTOR = 0x40, @@ -996,20 +852,28 @@ typedef struct TCGTargetOpDef { const char *args_ct_str[TCG_MAX_OP_ARGS]; } TCGTargetOpDef; -#define tcg_abort() \ -do {\ - fprintf(stderr, "%s:%d: tcg fatal error\n", __FILE__, __LINE__);\ - abort();\ -} while (0) - bool tcg_op_supported(TCGOpcode op); -void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args); +void tcg_gen_call0(TCGHelperInfo *, TCGTemp *ret); +void tcg_gen_call1(TCGHelperInfo *, TCGTemp *ret, TCGTemp *); +void tcg_gen_call2(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *); +void tcg_gen_call3(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, + TCGTemp *, TCGTemp *); +void tcg_gen_call4(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, + TCGTemp *, TCGTemp *); +void tcg_gen_call5(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, + TCGTemp *, TCGTemp *, TCGTemp *); +void tcg_gen_call6(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, + TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *); +void tcg_gen_call7(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, + TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *); -TCGOp *tcg_emit_op(TCGOpcode opc); +TCGOp *tcg_emit_op(TCGOpcode opc, unsigned nargs); void tcg_op_remove(TCGContext *s, TCGOp *op); -TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, TCGOpcode opc); -TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, TCGOpcode opc); +TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, + TCGOpcode opc, unsigned nargs); +TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, + TCGOpcode opc, unsigned nargs); /** * tcg_remove_ops_after: @@ -1023,46 +887,6 @@ void tcg_remove_ops_after(TCGOp *op); void tcg_optimize(TCGContext *s); -/* Allocate a new temporary and initialize it with a constant. */ -TCGv_i32 tcg_const_i32(int32_t val); -TCGv_i64 tcg_const_i64(int64_t val); -TCGv_i32 tcg_const_local_i32(int32_t val); -TCGv_i64 tcg_const_local_i64(int64_t val); -TCGv_vec tcg_const_zeros_vec(TCGType); -TCGv_vec tcg_const_ones_vec(TCGType); -TCGv_vec tcg_const_zeros_vec_matching(TCGv_vec); -TCGv_vec tcg_const_ones_vec_matching(TCGv_vec); - -/* - * Locate or create a read-only temporary that is a constant. - * This kind of temporary need not be freed, but for convenience - * will be silently ignored by tcg_temp_free_*. - */ -TCGTemp *tcg_constant_internal(TCGType type, int64_t val); - -static inline TCGv_i32 tcg_constant_i32(int32_t val) -{ - return temp_tcgv_i32(tcg_constant_internal(TCG_TYPE_I32, val)); -} - -static inline TCGv_i64 tcg_constant_i64(int64_t val) -{ - return temp_tcgv_i64(tcg_constant_internal(TCG_TYPE_I64, val)); -} - -TCGv_vec tcg_constant_vec(TCGType type, unsigned vece, int64_t val); -TCGv_vec tcg_constant_vec_matching(TCGv_vec match, unsigned vece, int64_t val); - -#if UINTPTR_MAX == UINT32_MAX -# define tcg_const_ptr(x) ((TCGv_ptr)tcg_const_i32((intptr_t)(x))) -# define tcg_const_local_ptr(x) ((TCGv_ptr)tcg_const_local_i32((intptr_t)(x))) -# define tcg_constant_ptr(x) ((TCGv_ptr)tcg_constant_i32((intptr_t)(x))) -#else -# define tcg_const_ptr(x) ((TCGv_ptr)tcg_const_i64((intptr_t)(x))) -# define tcg_const_local_ptr(x) ((TCGv_ptr)tcg_const_local_i64((intptr_t)(x))) -# define tcg_constant_ptr(x) ((TCGv_ptr)tcg_constant_i64((intptr_t)(x))) -#endif - TCGLabel *gen_new_label(void); /** @@ -1223,7 +1047,7 @@ static inline int tcg_can_emit_vec_op(TCGOpcode o, TCGType t, unsigned ve) /* Expand the tuple (opc, type, vece) on the given arguments. */ void tcg_expand_vec_op(TCGOpcode, TCGType, unsigned, TCGArg, ...); -/* Replicate a constant C accoring to the log2 of the element size. */ +/* Replicate a constant C according to the log2 of the element size. */ uint64_t dup_const(unsigned vece, uint64_t c); #define dup_const(VECE, C) \ @@ -1235,24 +1059,6 @@ uint64_t dup_const(unsigned vece, uint64_t c); : (qemu_build_not_reached_always(), 0)) \ : dup_const(VECE, C)) -#if TARGET_LONG_BITS == 64 -# define dup_const_tl dup_const -#else -# define dup_const_tl(VECE, C) \ - (__builtin_constant_p(VECE) \ - ? ( (VECE) == MO_8 ? 0x01010101ul * (uint8_t)(C) \ - : (VECE) == MO_16 ? 0x00010001ul * (uint16_t)(C) \ - : (VECE) == MO_32 ? 0x00000001ul * (uint32_t)(C) \ - : (qemu_build_not_reached_always(), 0)) \ - : (target_long)dup_const(VECE, C)) -#endif - -#ifdef CONFIG_DEBUG_TCG -void tcg_assert_listed_vecop(TCGOpcode); -#else -static inline void tcg_assert_listed_vecop(TCGOpcode op) { } -#endif - static inline const TCGOpcode *tcg_swap_vecop_list(const TCGOpcode *n) { #ifdef CONFIG_DEBUG_TCG diff --git a/include/ui/clipboard.h b/include/ui/clipboard.h index ce76aa451f..ab6acdbd8a 100644 --- a/include/ui/clipboard.h +++ b/include/ui/clipboard.h @@ -170,7 +170,7 @@ void qemu_clipboard_peer_release(QemuClipboardPeer *peer, * * @selection: clipboard selection. * - * Return the current clipboard data & owner informations. + * Return the current clipboard data & owner information. */ QemuClipboardInfo *qemu_clipboard_info(QemuClipboardSelection selection); diff --git a/include/ui/console.h b/include/ui/console.h index c44b28a972..0bc7a00ac0 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -4,13 +4,30 @@ #include "ui/qemu-pixman.h" #include "qom/object.h" #include "qemu/notify.h" -#include "qemu/error-report.h" #include "qapi/qapi-types-ui.h" +#include "ui/input.h" +#include "ui/surface.h" -#ifdef CONFIG_OPENGL -# include -# include "ui/shader.h" -#endif +#define TYPE_QEMU_CONSOLE "qemu-console" +OBJECT_DECLARE_TYPE(QemuConsole, QemuConsoleClass, QEMU_CONSOLE) + +#define TYPE_QEMU_GRAPHIC_CONSOLE "qemu-graphic-console" +OBJECT_DECLARE_SIMPLE_TYPE(QemuGraphicConsole, QEMU_GRAPHIC_CONSOLE) + +#define TYPE_QEMU_TEXT_CONSOLE "qemu-text-console" +OBJECT_DECLARE_SIMPLE_TYPE(QemuTextConsole, QEMU_TEXT_CONSOLE) + +#define TYPE_QEMU_FIXED_TEXT_CONSOLE "qemu-fixed-text-console" +OBJECT_DECLARE_SIMPLE_TYPE(QemuFixedTextConsole, QEMU_FIXED_TEXT_CONSOLE) + +#define QEMU_IS_GRAPHIC_CONSOLE(c) \ + object_dynamic_cast(OBJECT(c), TYPE_QEMU_GRAPHIC_CONSOLE) + +#define QEMU_IS_TEXT_CONSOLE(c) \ + object_dynamic_cast(OBJECT(c), TYPE_QEMU_TEXT_CONSOLE) + +#define QEMU_IS_FIXED_TEXT_CONSOLE(c) \ + object_dynamic_cast(OBJECT(c), TYPE_QEMU_FIXED_TEXT_CONSOLE) /* keyboard/mouse support */ @@ -65,11 +82,12 @@ void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry); void kbd_put_ledstate(int ledstate); -void hmp_mouse_set(Monitor *mon, const QDict *qdict); +bool qemu_mouse_set(int index, Error **errp); /* keysym is a unicode code except for special keys (see QEMU_KEY_xxx constants) */ #define QEMU_KEY_ESC1(c) ((c) | 0xe100) +#define QEMU_KEY_TAB 0x0009 #define QEMU_KEY_BACKSPACE 0x007f #define QEMU_KEY_UP QEMU_KEY_ESC1('A') #define QEMU_KEY_DOWN QEMU_KEY_ESC1('B') @@ -90,24 +108,30 @@ void hmp_mouse_set(Monitor *mon, const QDict *qdict); #define QEMU_KEY_CTRL_PAGEUP 0xe406 #define QEMU_KEY_CTRL_PAGEDOWN 0xe407 -void kbd_put_keysym_console(QemuConsole *s, int keysym); -bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl); -void kbd_put_string_console(QemuConsole *s, const char *str, int len); -void kbd_put_keysym(int keysym); +void qemu_text_console_put_keysym(QemuTextConsole *s, int keysym); +bool qemu_text_console_put_qcode(QemuTextConsole *s, int qcode, bool ctrl); +void qemu_text_console_put_string(QemuTextConsole *s, const char *str, int len); +/* Touch devices */ +typedef struct touch_slot { + int x; + int y; + int tracking_id; +} touch_slot; + +void console_handle_touch_event(QemuConsole *con, + struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX], + uint64_t num_slot, + int width, int height, + double x, double y, + InputMultiTouchType type, + Error **errp); /* consoles */ -#define TYPE_QEMU_CONSOLE "qemu-console" -OBJECT_DECLARE_TYPE(QemuConsole, QemuConsoleClass, QEMU_CONSOLE) - - struct QemuConsoleClass { ObjectClass parent_class; }; -#define QEMU_ALLOCATED_FLAG 0x01 -#define QEMU_PLACEHOLDER_FLAG 0x02 - typedef struct ScanoutTexture { uint32_t backing_id; bool backing_y_0_top; @@ -117,19 +141,9 @@ typedef struct ScanoutTexture { uint32_t y; uint32_t width; uint32_t height; + void *d3d_tex2d; } ScanoutTexture; -typedef struct DisplaySurface { - pixman_format_code_t format; - pixman_image_t *image; - uint8_t flags; -#ifdef CONFIG_OPENGL - GLenum glformat; - GLenum gltype; - GLuint texture; -#endif -} DisplaySurface; - typedef struct QemuUIInfo { /* physical dimension */ uint16_t width_mm; @@ -139,19 +153,20 @@ typedef struct QemuUIInfo { int yoff; uint32_t width; uint32_t height; + uint32_t refresh_rate; } QemuUIInfo; /* cursor data format is 32bit RGBA */ typedef struct QEMUCursor { - int width, height; + uint16_t width, height; int hot_x, hot_y; int refcount; uint32_t data[]; } QEMUCursor; -QEMUCursor *cursor_alloc(int width, int height); -void cursor_get(QEMUCursor *c); -void cursor_put(QEMUCursor *c); +QEMUCursor *cursor_alloc(uint16_t width, uint16_t height); +QEMUCursor *cursor_ref(QEMUCursor *c); +void cursor_unref(QEMUCursor *c); QEMUCursor *cursor_builtin_hidden(void); QEMUCursor *cursor_builtin_left_ptr(void); void cursor_print_ascii_art(QEMUCursor *c, const char *prefix); @@ -180,8 +195,8 @@ typedef struct QemuDmaBuf { uint32_t texture; uint32_t x; uint32_t y; - uint32_t scanout_width; - uint32_t scanout_height; + uint32_t backing_width; + uint32_t backing_height; bool y0_top; void *sync; int fence_fd; @@ -250,7 +265,8 @@ typedef struct DisplayChangeListenerOps { uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, - uint32_t w, uint32_t h); + uint32_t w, uint32_t h, + void *d3d_tex2d); /* optional (default to true if has dpy_gl_scanout_dmabuf) */ bool (*dpy_has_dmabuf)(DisplayChangeListener *dcl); /* optional */ @@ -307,33 +323,13 @@ struct DisplayGLCtx { }; DisplayState *init_displaystate(void); -DisplaySurface *qemu_create_displaysurface_from(int width, int height, - pixman_format_code_t format, - int linesize, uint8_t *data); -DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image); -DisplaySurface *qemu_create_placeholder_surface(int w, int h, - const char *msg); -PixelFormat qemu_default_pixelformat(int bpp); - -DisplaySurface *qemu_create_displaysurface(int width, int height); -void qemu_free_displaysurface(DisplaySurface *surface); - -static inline int is_buffer_shared(DisplaySurface *surface) -{ - return !(surface->flags & QEMU_ALLOCATED_FLAG); -} - -static inline int is_placeholder(DisplaySurface *surface) -{ - return surface->flags & QEMU_PLACEHOLDER_FLAG; -} void register_displaychangelistener(DisplayChangeListener *dcl); void update_displaychangelistener(DisplayChangeListener *dcl, uint64_t interval); void unregister_displaychangelistener(DisplayChangeListener *dcl); -bool dpy_ui_info_supported(QemuConsole *con); +bool dpy_ui_info_supported(const QemuConsole *con); const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con); int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay); @@ -354,7 +350,8 @@ void dpy_gl_scanout_disable(QemuConsole *con); void dpy_gl_scanout_texture(QemuConsole *con, uint32_t backing_id, bool backing_y_0_top, uint32_t backing_width, uint32_t backing_height, - uint32_t x, uint32_t y, uint32_t w, uint32_t h); + uint32_t x, uint32_t y, uint32_t w, uint32_t h, + void *d3d_tex2d); void dpy_gl_scanout_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf); void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf, @@ -373,43 +370,6 @@ int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx); bool console_has_gl(QemuConsole *con); -static inline int surface_stride(DisplaySurface *s) -{ - return pixman_image_get_stride(s->image); -} - -static inline void *surface_data(DisplaySurface *s) -{ - return pixman_image_get_data(s->image); -} - -static inline int surface_width(DisplaySurface *s) -{ - return pixman_image_get_width(s->image); -} - -static inline int surface_height(DisplaySurface *s) -{ - return pixman_image_get_height(s->image); -} - -static inline int surface_bits_per_pixel(DisplaySurface *s) -{ - int bits = PIXMAN_FORMAT_BPP(s->format); - return bits; -} - -static inline int surface_bytes_per_pixel(DisplaySurface *s) -{ - int bits = PIXMAN_FORMAT_BPP(s->format); - return DIV_ROUND_UP(bits, 8); -} - -static inline pixman_format_code_t surface_format(DisplaySurface *s) -{ - return s->format; -} - typedef uint32_t console_ch_t; static inline void console_write_ch(console_ch_t *dest, uint32_t ch) @@ -431,8 +391,7 @@ typedef struct GraphicHwOps { 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); + void (*ui_info)(void *opaque, uint32_t head, QemuUIInfo *info); void (*gl_block)(void *opaque, bool block); } GraphicHwOps; @@ -454,11 +413,12 @@ void qemu_console_early_init(void); void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *ctx); +QemuConsole *qemu_console_lookup_default(void); QemuConsole *qemu_console_lookup_by_index(unsigned int index); QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head); QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, uint32_t head, Error **errp); -QemuConsole *qemu_console_lookup_unused(void); +QEMUCursor *qemu_console_get_cursor(QemuConsole *con); bool qemu_console_is_visible(QemuConsole *con); bool qemu_console_is_graphic(QemuConsole *con); bool qemu_console_is_fixedsize(QemuConsole *con); @@ -473,9 +433,10 @@ int qemu_console_get_window_id(QemuConsole *con); /* Set the low-level window id for the console */ void qemu_console_set_window_id(QemuConsole *con, int window_id); -void console_select(unsigned int index); void qemu_console_resize(QemuConsole *con, int width, int height); DisplaySurface *qemu_console_surface(QemuConsole *con); +void coroutine_fn qemu_console_co_wait_update(QemuConsole *con); +int qemu_invalidate_text_consoles(void); /* console-gl.c */ #ifdef CONFIG_OPENGL @@ -501,12 +462,14 @@ struct QemuDisplay { DisplayType type; void (*early_init)(DisplayOptions *opts); void (*init)(DisplayState *ds, DisplayOptions *opts); + const char *vc; }; 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); +const char *qemu_display_get_vc(DisplayOptions *opts); void qemu_display_help(void); /* vnc.c */ diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h index 2fb6e0dd6b..4b8c0d2281 100644 --- a/include/ui/egl-helpers.h +++ b/include/ui/egl-helpers.h @@ -12,6 +12,7 @@ extern EGLDisplay *qemu_egl_display; extern EGLConfig qemu_egl_config; extern DisplayGLMode qemu_egl_mode; +extern bool qemu_egl_angle_d3d; typedef struct egl_fb { int width; @@ -22,6 +23,8 @@ typedef struct egl_fb { QemuDmaBuf *dmabuf; } egl_fb; +#define EGL_FB_INIT { 0, } + void egl_fb_destroy(egl_fb *fb); void egl_fb_setup_default(egl_fb *fb, int width, int height); void egl_fb_setup_for_tex(egl_fb *fb, int width, int height, @@ -29,16 +32,18 @@ void egl_fb_setup_for_tex(egl_fb *fb, int width, int height, void egl_fb_setup_new_tex(egl_fb *fb, int width, int height); void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip); void egl_fb_read(DisplaySurface *dst, egl_fb *src); +void egl_fb_read_rect(DisplaySurface *dst, egl_fb *src, int x, int y, int w, int h); void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip); void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip, int x, int y, double scale_x, double scale_y); +extern EGLContext qemu_egl_rn_ctx; + #ifdef CONFIG_GBM extern int qemu_egl_rn_fd; extern struct gbm_device *qemu_egl_rn_gbm_dev; -extern EGLContext qemu_egl_rn_ctx; int egl_rendernode_init(const char *rendernode, DisplayGLMode mode); int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc, @@ -60,7 +65,15 @@ int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode); #endif +#ifdef WIN32 +int qemu_egl_init_dpy_win32(EGLNativeDisplayType dpy, DisplayGLMode mode); +#endif + EGLContext qemu_egl_init_ctx(void); bool qemu_egl_has_dmabuf(void); +bool egl_init(const char *rendernode, DisplayGLMode mode, Error **errp); + +const char *qemu_egl_get_error_string(void); + #endif /* EGL_HELPERS_H */ diff --git a/include/ui/gtk.h b/include/ui/gtk.h index 101b147d1b..aa3d637029 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -155,7 +155,7 @@ extern bool gtk_use_gl_area; /* ui/gtk.c */ void gd_update_windowsize(VirtualConsole *vc); -int gd_monitor_update_interval(GtkWidget *widget); +void gd_update_monitor_refresh_rate(VirtualConsole *vc, GtkWidget *widget); void gd_hw_gl_flushed(void *vc); /* ui/gtk-egl.c */ @@ -175,7 +175,8 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, - uint32_t w, uint32_t h); + uint32_t w, uint32_t h, + void *d3d_tex2d); void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf); void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl, @@ -211,7 +212,8 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, - uint32_t w, uint32_t h); + uint32_t w, uint32_t h, + void *d3d_tex2d); void gd_gl_area_scanout_disable(DisplayChangeListener *dcl); void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h); diff --git a/include/ui/input.h b/include/ui/input.h index c86219a1c1..8f9aac562e 100644 --- a/include/ui/input.h +++ b/include/ui/input.h @@ -8,9 +8,12 @@ #define INPUT_EVENT_MASK_BTN (1<> (ofs)) & ((1 << (num)) - 1)) << ((val >> 22) & 3)) + +#define PIXMAN_FORMAT_BPP(f) PIXMAN_FORMAT_RESHIFT(f, 24, 8) +#define PIXMAN_FORMAT_TYPE(f) (((f) >> 16) & 0x3f) +#define PIXMAN_FORMAT_A(f) PIXMAN_FORMAT_RESHIFT(f, 12, 4) +#define PIXMAN_FORMAT_R(f) PIXMAN_FORMAT_RESHIFT(f, 8, 4) +#define PIXMAN_FORMAT_G(f) PIXMAN_FORMAT_RESHIFT(f, 4, 4) +#define PIXMAN_FORMAT_B(f) PIXMAN_FORMAT_RESHIFT(f, 0, 4) +#define PIXMAN_FORMAT_DEPTH(f) (PIXMAN_FORMAT_A(f) + \ + PIXMAN_FORMAT_R(f) + \ + PIXMAN_FORMAT_G(f) + \ + PIXMAN_FORMAT_B(f)) + +typedef enum { + /* 32bpp formats */ + PIXMAN_a8r8g8b8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_ARGB, 8, 8, 8, 8), + PIXMAN_x8r8g8b8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_ARGB, 0, 8, 8, 8), + PIXMAN_a8b8g8r8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_ABGR, 8, 8, 8, 8), + PIXMAN_x8b8g8r8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_ABGR, 0, 8, 8, 8), + PIXMAN_b8g8r8a8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_BGRA, 8, 8, 8, 8), + PIXMAN_b8g8r8x8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_BGRA, 0, 8, 8, 8), + PIXMAN_r8g8b8a8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_RGBA, 8, 8, 8, 8), + PIXMAN_r8g8b8x8 = PIXMAN_FORMAT(32, PIXMAN_TYPE_RGBA, 0, 8, 8, 8), + /* 24bpp formats */ + PIXMAN_r8g8b8 = PIXMAN_FORMAT(24, PIXMAN_TYPE_ARGB, 0, 8, 8, 8), + PIXMAN_b8g8r8 = PIXMAN_FORMAT(24, PIXMAN_TYPE_ABGR, 0, 8, 8, 8), + /* 16bpp formats */ + PIXMAN_r5g6b5 = PIXMAN_FORMAT(16, PIXMAN_TYPE_ARGB, 0, 5, 6, 5), + PIXMAN_a1r5g5b5 = PIXMAN_FORMAT(16, PIXMAN_TYPE_ARGB, 1, 5, 5, 5), + PIXMAN_x1r5g5b5 = PIXMAN_FORMAT(16, PIXMAN_TYPE_ARGB, 0, 5, 5, 5), +} pixman_format_code_t; + +typedef struct pixman_image pixman_image_t; + +typedef void (*pixman_image_destroy_func_t)(pixman_image_t *image, void *data); + +struct pixman_image { + int ref_count; + pixman_format_code_t format; + int width; + int height; + int stride; + uint32_t *data; + uint32_t *free_me; + pixman_image_destroy_func_t destroy_func; + void *destroy_data; +}; + +typedef struct pixman_color { + uint16_t red; + uint16_t green; + uint16_t blue; + uint16_t alpha; +} pixman_color_t; + +static inline uint32_t *create_bits(pixman_format_code_t format, + int width, + int height, + int *rowstride_bytes) +{ + int stride = 0; + size_t buf_size = 0; + int bpp = PIXMAN_FORMAT_BPP(format); + + /* + * Calculate the following while checking for overflow truncation: + * stride = ((width * bpp + 0x1f) >> 5) * sizeof(uint32_t); + */ + + if (unlikely(__builtin_mul_overflow(width, bpp, &stride))) { + return NULL; + } + + if (unlikely(__builtin_add_overflow(stride, 0x1f, &stride))) { + return NULL; + } + + stride >>= 5; + + stride *= sizeof(uint32_t); + + if (unlikely(__builtin_mul_overflow((size_t) height, + (size_t) stride, + &buf_size))) { + return NULL; + } + + if (rowstride_bytes) { + *rowstride_bytes = stride; + } + + return g_malloc0(buf_size); +} + +static inline pixman_image_t *pixman_image_create_bits(pixman_format_code_t format, + int width, + int height, + uint32_t *bits, + int rowstride_bytes) +{ + pixman_image_t *i = g_new0(pixman_image_t, 1); + + i->width = width; + i->height = height; + i->format = format; + if (bits) { + i->data = bits; + } else { + i->free_me = i->data = + create_bits(format, width, height, &rowstride_bytes); + if (width && height) { + assert(i->data); + } + } + i->stride = rowstride_bytes ? rowstride_bytes : + width * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8); + i->ref_count = 1; + + return i; +} + +static inline pixman_image_t *pixman_image_ref(pixman_image_t *i) +{ + i->ref_count++; + return i; +} + +static inline bool pixman_image_unref(pixman_image_t *i) +{ + i->ref_count--; + + if (i->ref_count == 0) { + if (i->destroy_func) { + i->destroy_func(i, i->destroy_data); + } + g_free(i->free_me); + g_free(i); + + return true; + } + + return false; +} + +static inline void pixman_image_set_destroy_function(pixman_image_t *i, + pixman_image_destroy_func_t func, + void *data) + +{ + i->destroy_func = func; + i->destroy_data = data; +} + +static inline uint32_t *pixman_image_get_data(pixman_image_t *i) +{ + return i->data; +} + +static inline int pixman_image_get_height(pixman_image_t *i) +{ + return i->height; +} + +static inline int pixman_image_get_width(pixman_image_t *i) +{ + return i->width; +} + +static inline int pixman_image_get_stride(pixman_image_t *i) +{ + return i->stride; +} + +static inline pixman_format_code_t pixman_image_get_format(pixman_image_t *i) +{ + return i->format; +} + +#endif /* PIXMAN_MINIMAL_H */ diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index 0c775604d1..ef13a8210c 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -6,11 +6,11 @@ #ifndef QEMU_PIXMAN_H #define QEMU_PIXMAN_H -/* pixman-0.16.0 headers have a redundant declaration */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wredundant-decls" +#ifdef CONFIG_PIXMAN #include -#pragma GCC diagnostic pop +#else +#include "pixman-minimal.h" +#endif /* * pixman image formats are defined to be native endian, @@ -32,6 +32,8 @@ # define PIXMAN_LE_r8g8b8 PIXMAN_b8g8r8 # define PIXMAN_LE_a8r8g8b8 PIXMAN_b8g8r8a8 # define PIXMAN_LE_x8r8g8b8 PIXMAN_b8g8r8x8 +# define PIXMAN_LE_a8b8g8r8 PIXMAN_r8g8b8a8 +# define PIXMAN_LE_x8b8g8r8 PIXMAN_r8g8b8x8 #else # define PIXMAN_BE_r8g8b8 PIXMAN_b8g8r8 # define PIXMAN_BE_x8r8g8b8 PIXMAN_b8g8r8x8 @@ -45,8 +47,16 @@ # define PIXMAN_LE_r8g8b8 PIXMAN_r8g8b8 # define PIXMAN_LE_a8r8g8b8 PIXMAN_a8r8g8b8 # define PIXMAN_LE_x8r8g8b8 PIXMAN_x8r8g8b8 +# define PIXMAN_LE_a8b8g8r8 PIXMAN_a8b8g8r8 +# define PIXMAN_LE_x8b8g8r8 PIXMAN_x8b8g8r8 #endif +#define QEMU_PIXMAN_COLOR(r, g, b) \ + { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff } + +#define QEMU_PIXMAN_COLOR_BLACK QEMU_PIXMAN_COLOR(0x00, 0x00, 0x00) +#define QEMU_PIXMAN_COLOR_GRAY QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0xaa) + /* -------------------------------------------------------------------- */ typedef struct PixelFormat { @@ -64,21 +74,18 @@ pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian); pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format); uint32_t qemu_pixman_to_drm_format(pixman_format_code_t pixman); int qemu_pixman_get_type(int rshift, int gshift, int bshift); -pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf); bool qemu_pixman_check_format(DisplayChangeListener *dcl, pixman_format_code_t format); +#ifdef CONFIG_PIXMAN +pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf); pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format, int width); void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb, int width, int x, int y); -void qemu_pixman_linebuf_copy(pixman_image_t *fb, int width, int x, int y, - pixman_image_t *linebuf); pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format, pixman_image_t *image); -void qemu_pixman_image_unref(pixman_image_t *image); -pixman_color_t qemu_pixman_color(PixelFormat *pf, uint32_t color); pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font, unsigned int ch); void qemu_pixman_glyph_render(pixman_image_t *glyph, @@ -86,6 +93,9 @@ void qemu_pixman_glyph_render(pixman_image_t *glyph, pixman_color_t *fgcol, pixman_color_t *bgcol, int x, int y, int cw, int ch); +#endif + +void qemu_pixman_image_unref(pixman_image_t *image); G_DEFINE_AUTOPTR_CLEANUP_FUNC(pixman_image_t, qemu_pixman_image_unref) diff --git a/include/ui/qemu-spice.h b/include/ui/qemu-spice.h index 21fe195e18..b7d493742c 100644 --- a/include/ui/qemu-spice.h +++ b/include/ui/qemu-spice.h @@ -34,13 +34,7 @@ int qemu_spice_add_display_interface(QXLInstance *qxlin, QemuConsole *con); int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, const char *subject); -#if !defined(SPICE_SERVER_VERSION) || (SPICE_SERVER_VERSION < 0xc06) -#define SPICE_NEEDS_SET_MM_TIME 1 -#else -#define SPICE_NEEDS_SET_MM_TIME 0 -#endif - -#if defined(SPICE_SERVER_VERSION) && (SPICE_SERVER_VERSION >= 0x000f00) +#if SPICE_SERVER_VERSION >= 0x000f00 /* release 0.15.0 */ #define SPICE_HAS_ATTACHED_WORKER 1 #else #define SPICE_HAS_ATTACHED_WORKER 0 diff --git a/include/ui/rect.h b/include/ui/rect.h new file mode 100644 index 0000000000..7ebf47ebcd --- /dev/null +++ b/include/ui/rect.h @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef QEMU_RECT_H +#define QEMU_RECT_H + + +typedef struct QemuRect { + int16_t x; + int16_t y; + uint16_t width; + uint16_t height; +} QemuRect; + +static inline void qemu_rect_init(QemuRect *rect, + int16_t x, int16_t y, + uint16_t width, uint16_t height) +{ + rect->x = x; + rect->y = y; + rect->width = width; + rect->height = height; +} + +static inline void qemu_rect_translate(QemuRect *rect, + int16_t dx, int16_t dy) +{ + rect->x += dx; + rect->y += dy; +} + +static inline bool qemu_rect_intersect(const QemuRect *a, const QemuRect *b, + QemuRect *res) +{ + int16_t x1, x2, y1, y2; + + x1 = MAX(a->x, b->x); + y1 = MAX(a->y, b->y); + x2 = MIN(a->x + a->width, b->x + b->width); + y2 = MIN(a->y + a->height, b->y + b->height); + + if (x1 >= x2 || y1 >= y2) { + if (res) { + qemu_rect_init(res, 0, 0, 0, 0); + } + + return false; + } + + if (res) { + qemu_rect_init(res, x1, y1, x2 - x1, y2 - y1); + } + + return true; +} + +#endif diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h index 8fb7e08262..e3acc7c82a 100644 --- a/include/ui/sdl2.h +++ b/include/ui/sdl2.h @@ -90,7 +90,8 @@ void sdl2_gl_scanout_texture(DisplayChangeListener *dcl, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, - uint32_t w, uint32_t h); + uint32_t w, uint32_t h, + void *d3d_tex2d); void sdl2_gl_scanout_flush(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h); diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index e271e011da..e1a9b36185 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -28,11 +28,9 @@ #include "ui/console.h" #if defined(CONFIG_OPENGL) && defined(CONFIG_GBM) -# if SPICE_SERVER_VERSION >= 0x000d01 /* release 0.13.1 */ # define HAVE_SPICE_GL 1 # include "ui/egl-helpers.h" # include "ui/egl-context.h" -# endif #endif #define NUM_MEMSLOTS 8 @@ -44,7 +42,7 @@ #define NUM_MEMSLOTS_GROUPS 2 /* - * Internal enum to differenciate between options for + * Internal enum to differentiate between options for * io calls that have a sync (old) version and an _async (new) * version: * QXL_SYNC: use the old version diff --git a/include/ui/surface.h b/include/ui/surface.h new file mode 100644 index 0000000000..4244e0ca4a --- /dev/null +++ b/include/ui/surface.h @@ -0,0 +1,95 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * QEMU UI Console + */ +#ifndef SURFACE_H +#define SURFACE_H + +#include "ui/qemu-pixman.h" + +#ifdef CONFIG_OPENGL +# include +# include "ui/shader.h" +#endif + +#define QEMU_ALLOCATED_FLAG 0x01 +#define QEMU_PLACEHOLDER_FLAG 0x02 + +typedef struct DisplaySurface { + pixman_image_t *image; + uint8_t flags; +#ifdef CONFIG_OPENGL + GLenum glformat; + GLenum gltype; + GLuint texture; +#endif +#ifdef WIN32 + HANDLE handle; + uint32_t handle_offset; +#endif +} DisplaySurface; + +PixelFormat qemu_default_pixelformat(int bpp); + +DisplaySurface *qemu_create_displaysurface_from(int width, int height, + pixman_format_code_t format, + int linesize, uint8_t *data); +DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image); +DisplaySurface *qemu_create_placeholder_surface(int w, int h, + const char *msg); +#ifdef WIN32 +void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, + HANDLE h, uint32_t offset); +#endif + +DisplaySurface *qemu_create_displaysurface(int width, int height); +void qemu_free_displaysurface(DisplaySurface *surface); + +static inline int is_buffer_shared(DisplaySurface *surface) +{ + return !(surface->flags & QEMU_ALLOCATED_FLAG); +} + +static inline int is_placeholder(DisplaySurface *surface) +{ + return surface->flags & QEMU_PLACEHOLDER_FLAG; +} + +static inline int surface_stride(DisplaySurface *s) +{ + return pixman_image_get_stride(s->image); +} + +static inline void *surface_data(DisplaySurface *s) +{ + return pixman_image_get_data(s->image); +} + +static inline int surface_width(DisplaySurface *s) +{ + return pixman_image_get_width(s->image); +} + +static inline int surface_height(DisplaySurface *s) +{ + return pixman_image_get_height(s->image); +} + +static inline pixman_format_code_t surface_format(DisplaySurface *s) +{ + return pixman_image_get_format(s->image); +} + +static inline int surface_bits_per_pixel(DisplaySurface *s) +{ + int bits = PIXMAN_FORMAT_BPP(surface_format(s)); + return bits; +} + +static inline int surface_bytes_per_pixel(DisplaySurface *s) +{ + int bits = PIXMAN_FORMAT_BPP(surface_format(s)); + return DIV_ROUND_UP(bits, 8); +} + +#endif diff --git a/include/user/safe-syscall.h b/include/user/safe-syscall.h index 61a04e2b5a..aa075f4d5c 100644 --- a/include/user/safe-syscall.h +++ b/include/user/safe-syscall.h @@ -70,7 +70,7 @@ * If the host libc is used then the implementation will appear to work * most of the time, but there will be a race condition where a * signal could arrive just before we make the host syscall inside libc, - * and then then guest syscall will not correctly be interrupted. + * and then the guest syscall will not correctly be interrupted. * Instead the implementation of the guest syscall can use the safe_syscall * function but otherwise just return the result or errno in the usual * way; the main loop code will take care of restarting the syscall @@ -91,7 +91,7 @@ * * The basic setup is that we make the host syscall via a known * section of host native assembly. If a signal occurs, our signal - * handler checks the interrupted host PC against the addresse of that + * handler checks the interrupted host PC against the address of that * known section. If the PC is before or at the address of the syscall * instruction then we change the PC to point at a "return * -QEMU_ERESTARTSYS" code path instead, and then exit the signal handler @@ -126,15 +126,15 @@ */ /* The core part of this function is implemented in assembly */ -extern long safe_syscall_base(int *pending, long number, ...); -extern long safe_syscall_set_errno_tail(int value); +long safe_syscall_base(int *pending, long number, ...); +long safe_syscall_set_errno_tail(int value); /* These are defined by the safe-syscall.inc.S file */ extern char safe_syscall_start[]; extern char safe_syscall_end[]; #define safe_syscall(...) \ - safe_syscall_base(&((TaskState *)thread_cpu->opaque)->signal_pending, \ + safe_syscall_base(&get_task_state(thread_cpu)->signal_pending, \ __VA_ARGS__) #endif diff --git a/include/user/syscall-trace.h b/include/user/syscall-trace.h index b4e53d3870..b48b2b2d0a 100644 --- a/include/user/syscall-trace.h +++ b/include/user/syscall-trace.h @@ -10,6 +10,9 @@ #ifndef SYSCALL_TRACE_H #define SYSCALL_TRACE_H +#include "exec/user/abitypes.h" +#include "gdbstub/user.h" +#include "qemu/plugin.h" #include "trace/trace-root.h" /* @@ -18,24 +21,22 @@ * could potentially unify the -strace code here as well. */ -static inline void record_syscall_start(void *cpu, int num, +static inline void record_syscall_start(CPUState *cpu, 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) { - trace_guest_user_syscall(cpu, num, - arg1, arg2, arg3, arg4, - arg5, arg6, arg7, arg8); qemu_plugin_vcpu_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + gdb_syscall_entry(cpu, num); } -static inline void record_syscall_return(void *cpu, int num, abi_long ret) +static inline void record_syscall_return(CPUState *cpu, int num, abi_long ret) { - trace_guest_user_syscall_ret(cpu, num, ret); qemu_plugin_vcpu_syscall_ret(cpu, num, ret); + gdb_syscall_return(cpu, num); } diff --git a/io/channel-buffer.c b/io/channel-buffer.c index bf52011be2..8096180f85 100644 --- a/io/channel-buffer.c +++ b/io/channel-buffer.c @@ -54,6 +54,7 @@ static ssize_t qio_channel_buffer_readv(QIOChannel *ioc, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp) { QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc); diff --git a/io/channel-command.c b/io/channel-command.c index 9f2f4a1793..6d5f64e146 100644 --- a/io/channel-command.c +++ b/io/channel-command.c @@ -20,18 +20,18 @@ #include "qemu/osdep.h" #include "io/channel-command.h" +#include "io/channel-util.h" #include "io/channel-watch.h" #include "qapi/error.h" #include "qemu/module.h" #include "qemu/sockets.h" #include "trace.h" -#ifndef WIN32 /** * qio_channel_command_new_pid: * @writefd: the FD connected to the command's stdin * @readfd: the FD connected to the command's stdout - * @pid: the PID of the running child command + * @pid: the PID/HANDLE of the running child command * @errp: pointer to a NULL-initialized error object * * Create a channel for performing I/O with the @@ -50,7 +50,7 @@ static QIOChannelCommand * qio_channel_command_new_pid(int writefd, int readfd, - pid_t pid) + GPid pid) { QIOChannelCommand *ioc; @@ -60,7 +60,13 @@ qio_channel_command_new_pid(int writefd, ioc->writefd = writefd; ioc->pid = pid; - trace_qio_channel_command_new_pid(ioc, writefd, readfd, pid); + trace_qio_channel_command_new_pid(ioc, writefd, readfd, +#ifdef WIN32 + GetProcessId(pid) +#else + pid +#endif + ); return ioc; } @@ -69,108 +75,26 @@ qio_channel_command_new_spawn(const char *const argv[], int flags, Error **errp) { - pid_t pid = -1; - int stdinfd[2] = { -1, -1 }; - int stdoutfd[2] = { -1, -1 }; - int devnull = -1; - bool stdinnull = false, stdoutnull = false; - QIOChannelCommand *ioc; + g_autoptr(GError) err = NULL; + GPid pid = 0; + GSpawnFlags gflags = G_SPAWN_CLOEXEC_PIPES | G_SPAWN_DO_NOT_REAP_CHILD; + int stdinfd = -1, stdoutfd = -1; flags = flags & O_ACCMODE; + gflags |= flags == O_WRONLY ? G_SPAWN_STDOUT_TO_DEV_NULL : 0; - if (flags == O_RDONLY) { - stdinnull = true; - } - if (flags == O_WRONLY) { - stdoutnull = true; + if (!g_spawn_async_with_pipes(NULL, (char **)argv, NULL, gflags, NULL, NULL, + &pid, + flags == O_RDONLY ? NULL : &stdinfd, + flags == O_WRONLY ? NULL : &stdoutfd, + NULL, &err)) { + error_setg(errp, "%s", err->message); + return NULL; } - if (stdinnull || stdoutnull) { - devnull = open("/dev/null", O_RDWR); - if (devnull < 0) { - error_setg_errno(errp, errno, - "Unable to open /dev/null"); - goto error; - } - } - - if ((!stdinnull && !g_unix_open_pipe(stdinfd, FD_CLOEXEC, NULL)) || - (!stdoutnull && !g_unix_open_pipe(stdoutfd, FD_CLOEXEC, NULL))) { - error_setg_errno(errp, errno, - "Unable to open pipe"); - goto error; - } - - pid = qemu_fork(errp); - if (pid < 0) { - goto error; - } - - if (pid == 0) { /* child */ - dup2(stdinnull ? devnull : stdinfd[0], STDIN_FILENO); - dup2(stdoutnull ? devnull : stdoutfd[1], STDOUT_FILENO); - /* Leave stderr connected to qemu's stderr */ - - if (!stdinnull) { - close(stdinfd[0]); - close(stdinfd[1]); - } - if (!stdoutnull) { - close(stdoutfd[0]); - close(stdoutfd[1]); - } - if (devnull != -1) { - close(devnull); - } - - execv(argv[0], (char * const *)argv); - _exit(1); - } - - if (!stdinnull) { - close(stdinfd[0]); - } - if (!stdoutnull) { - close(stdoutfd[1]); - } - - ioc = qio_channel_command_new_pid(stdinnull ? devnull : stdinfd[1], - stdoutnull ? devnull : stdoutfd[0], - pid); - trace_qio_channel_command_new_spawn(ioc, argv[0], flags); - return ioc; - - error: - if (devnull != -1) { - close(devnull); - } - if (stdinfd[0] != -1) { - close(stdinfd[0]); - } - if (stdinfd[1] != -1) { - close(stdinfd[1]); - } - if (stdoutfd[0] != -1) { - close(stdoutfd[0]); - } - if (stdoutfd[1] != -1) { - close(stdoutfd[1]); - } - return NULL; + return qio_channel_command_new_pid(stdinfd, stdoutfd, pid); } -#else /* WIN32 */ -QIOChannelCommand * -qio_channel_command_new_spawn(const char *const argv[], - int flags, - Error **errp) -{ - error_setg_errno(errp, ENOSYS, - "Command spawn not supported on this platform"); - return NULL; -} -#endif /* WIN32 */ - #ifndef WIN32 static int qio_channel_command_abort(QIOChannelCommand *ioc, Error **errp) @@ -213,6 +137,23 @@ static int qio_channel_command_abort(QIOChannelCommand *ioc, return 0; } +#else +static int qio_channel_command_abort(QIOChannelCommand *ioc, + Error **errp) +{ + DWORD ret; + + TerminateProcess(ioc->pid, 0); + ret = WaitForSingleObject(ioc->pid, 1000); + if (ret != WAIT_OBJECT_0) { + error_setg(errp, + "Process %llu refused to die", + (unsigned long long)GetProcessId(ioc->pid)); + return -1; + } + + return 0; +} #endif /* ! WIN32 */ @@ -221,7 +162,7 @@ static void qio_channel_command_init(Object *obj) QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj); ioc->readfd = -1; ioc->writefd = -1; - ioc->pid = -1; + ioc->pid = 0; } static void qio_channel_command_finalize(Object *obj) @@ -236,23 +177,45 @@ static void qio_channel_command_finalize(Object *obj) } ioc->writefd = ioc->readfd = -1; if (ioc->pid > 0) { -#ifndef WIN32 qio_channel_command_abort(ioc, NULL); -#endif + g_spawn_close_pid(ioc->pid); } } +#ifdef WIN32 +static bool win32_fd_poll(int fd, gushort events) +{ + GPollFD pfd = { .fd = _get_osfhandle(fd), .events = events }; + int res; + + do { + res = g_poll(&pfd, 1, 0); + } while (res < 0 && errno == EINTR); + if (res == 0) { + return false; + } + + return true; +} +#endif static ssize_t qio_channel_command_readv(QIOChannel *ioc, const struct iovec *iov, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp) { QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); ssize_t ret; +#ifdef WIN32 + if (!cioc->blocking && !win32_fd_poll(cioc->readfd, G_IO_IN)) { + return QIO_CHANNEL_ERR_BLOCK; + } +#endif + retry: ret = readv(cioc->readfd, iov, niov); if (ret < 0) { @@ -282,6 +245,12 @@ static ssize_t qio_channel_command_writev(QIOChannel *ioc, QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); ssize_t ret; +#ifdef WIN32 + if (!cioc->blocking && !win32_fd_poll(cioc->writefd, G_IO_OUT)) { + return QIO_CHANNEL_ERR_BLOCK; + } +#endif + retry: ret = writev(cioc->writefd, iov, niov); if (ret <= 0) { @@ -302,14 +271,14 @@ static int qio_channel_command_set_blocking(QIOChannel *ioc, bool enabled, Error **errp) { -#ifdef WIN32 - /* command spawn is not supported on win32 */ - g_assert_not_reached(); -#else QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); - if (!g_unix_set_fd_nonblocking(cioc->writefd, !enabled, NULL) || - !g_unix_set_fd_nonblocking(cioc->readfd, !enabled, NULL)) { +#ifdef WIN32 + cioc->blocking = enabled; +#else + + if ((cioc->writefd >= 0 && !g_unix_set_fd_nonblocking(cioc->writefd, !enabled, NULL)) || + (cioc->readfd >= 0 && !g_unix_set_fd_nonblocking(cioc->readfd, !enabled, NULL))) { error_setg_errno(errp, errno, "Failed to set FD nonblocking"); return -1; } @@ -350,6 +319,8 @@ static int qio_channel_command_close(QIOChannel *ioc, (unsigned long long)cioc->pid); return -1; } +#else + WaitForSingleObject(cioc->pid, INFINITE); #endif if (rv < 0) { @@ -361,16 +332,17 @@ static int qio_channel_command_close(QIOChannel *ioc, static void qio_channel_command_set_aio_fd_handler(QIOChannel *ioc, - AioContext *ctx, + AioContext *read_ctx, IOHandler *io_read, + AioContext *write_ctx, IOHandler *io_write, void *opaque) { QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); - aio_set_fd_handler(ctx, cioc->readfd, false, - io_read, NULL, NULL, NULL, opaque); - aio_set_fd_handler(ctx, cioc->writefd, false, - NULL, io_write, NULL, NULL, opaque); + + qio_channel_util_set_aio_fd_handler(cioc->readfd, read_ctx, io_read, + cioc->writefd, write_ctx, io_write, + opaque); } diff --git a/io/channel-file.c b/io/channel-file.c index b67687c2aa..6436cfb6ae 100644 --- a/io/channel-file.c +++ b/io/channel-file.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "io/channel-file.h" +#include "io/channel-util.h" #include "io/channel-watch.h" #include "qapi/error.h" #include "qemu/module.h" @@ -35,11 +36,27 @@ qio_channel_file_new_fd(int fd) ioc->fd = fd; + if (lseek(fd, 0, SEEK_CUR) != (off_t)-1) { + qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE); + } + trace_qio_channel_file_new_fd(ioc, fd); return ioc; } +QIOChannelFile * +qio_channel_file_new_dupfd(int fd, Error **errp) +{ + int newfd = dup(fd); + + if (newfd < 0) { + error_setg_errno(errp, errno, "Could not dup FD %d", fd); + return NULL; + } + + return qio_channel_file_new_fd(newfd); +} QIOChannelFile * qio_channel_file_new_path(const char *path, @@ -59,6 +76,10 @@ qio_channel_file_new_path(const char *path, return NULL; } + if (lseek(ioc->fd, 0, SEEK_CUR) != (off_t)-1) { + qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE); + } + trace_qio_channel_file_new_path(ioc, path, flags, mode, ioc->fd); return ioc; @@ -86,6 +107,7 @@ static ssize_t qio_channel_file_readv(QIOChannel *ioc, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp) { QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); @@ -136,6 +158,58 @@ static ssize_t qio_channel_file_writev(QIOChannel *ioc, return ret; } +#ifdef CONFIG_PREADV +static ssize_t qio_channel_file_preadv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + off_t offset, + Error **errp) +{ + QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); + ssize_t ret; + + retry: + ret = preadv(fioc->fd, iov, niov, offset); + if (ret < 0) { + if (errno == EAGAIN) { + return QIO_CHANNEL_ERR_BLOCK; + } + if (errno == EINTR) { + goto retry; + } + + error_setg_errno(errp, errno, "Unable to read from file"); + return -1; + } + + return ret; +} + +static ssize_t qio_channel_file_pwritev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + off_t offset, + Error **errp) +{ + QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); + ssize_t ret; + + retry: + ret = pwritev(fioc->fd, iov, niov, offset); + if (ret <= 0) { + if (errno == EAGAIN) { + return QIO_CHANNEL_ERR_BLOCK; + } + if (errno == EINTR) { + goto retry; + } + error_setg_errno(errp, errno, "Unable to write to file"); + return -1; + } + return ret; +} +#endif /* CONFIG_PREADV */ + static int qio_channel_file_set_blocking(QIOChannel *ioc, bool enabled, Error **errp) @@ -191,14 +265,17 @@ static int qio_channel_file_close(QIOChannel *ioc, static void qio_channel_file_set_aio_fd_handler(QIOChannel *ioc, - AioContext *ctx, + AioContext *read_ctx, IOHandler *io_read, + AioContext *write_ctx, IOHandler *io_write, void *opaque) { QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); - aio_set_fd_handler(ctx, fioc->fd, false, io_read, io_write, - NULL, NULL, opaque); + + qio_channel_util_set_aio_fd_handler(fioc->fd, read_ctx, io_read, + fioc->fd, write_ctx, io_write, + opaque); } static GSource *qio_channel_file_create_watch(QIOChannel *ioc, @@ -218,6 +295,10 @@ static void qio_channel_file_class_init(ObjectClass *klass, ioc_klass->io_writev = qio_channel_file_writev; ioc_klass->io_readv = qio_channel_file_readv; ioc_klass->io_set_blocking = qio_channel_file_set_blocking; +#ifdef CONFIG_PREADV + ioc_klass->io_pwritev = qio_channel_file_pwritev; + ioc_klass->io_preadv = qio_channel_file_preadv; +#endif ioc_klass->io_seek = qio_channel_file_seek; ioc_klass->io_close = qio_channel_file_close; ioc_klass->io_create_watch = qio_channel_file_create_watch; diff --git a/io/channel-null.c b/io/channel-null.c new file mode 100644 index 0000000000..ef99586348 --- /dev/null +++ b/io/channel-null.c @@ -0,0 +1,239 @@ +/* + * QEMU I/O channels null driver + * + * Copyright (c) 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "io/channel-null.h" +#include "io/channel-watch.h" +#include "qapi/error.h" +#include "trace.h" +#include "qemu/iov.h" + +typedef struct QIOChannelNullSource QIOChannelNullSource; +struct QIOChannelNullSource { + GSource parent; + QIOChannel *ioc; + GIOCondition condition; +}; + + +QIOChannelNull * +qio_channel_null_new(void) +{ + QIOChannelNull *ioc; + + ioc = QIO_CHANNEL_NULL(object_new(TYPE_QIO_CHANNEL_NULL)); + + trace_qio_channel_null_new(ioc); + + return ioc; +} + + +static void +qio_channel_null_init(Object *obj) +{ + QIOChannelNull *ioc = QIO_CHANNEL_NULL(obj); + ioc->closed = false; +} + + +static ssize_t +qio_channel_null_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds G_GNUC_UNUSED, + size_t *nfds G_GNUC_UNUSED, + int flags, + Error **errp) +{ + QIOChannelNull *nioc = QIO_CHANNEL_NULL(ioc); + + if (nioc->closed) { + error_setg_errno(errp, EINVAL, + "Channel is closed"); + return -1; + } + + return 0; +} + + +static ssize_t +qio_channel_null_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds G_GNUC_UNUSED, + size_t nfds G_GNUC_UNUSED, + int flags G_GNUC_UNUSED, + Error **errp) +{ + QIOChannelNull *nioc = QIO_CHANNEL_NULL(ioc); + + if (nioc->closed) { + error_setg_errno(errp, EINVAL, + "Channel is closed"); + return -1; + } + + return iov_size(iov, niov); +} + + +static int +qio_channel_null_set_blocking(QIOChannel *ioc G_GNUC_UNUSED, + bool enabled G_GNUC_UNUSED, + Error **errp G_GNUC_UNUSED) +{ + return 0; +} + + +static off_t +qio_channel_null_seek(QIOChannel *ioc G_GNUC_UNUSED, + off_t offset G_GNUC_UNUSED, + int whence G_GNUC_UNUSED, + Error **errp G_GNUC_UNUSED) +{ + return 0; +} + + +static int +qio_channel_null_close(QIOChannel *ioc, + Error **errp G_GNUC_UNUSED) +{ + QIOChannelNull *nioc = QIO_CHANNEL_NULL(ioc); + + nioc->closed = true; + return 0; +} + + +static void +qio_channel_null_set_aio_fd_handler(QIOChannel *ioc G_GNUC_UNUSED, + AioContext *read_ctx G_GNUC_UNUSED, + IOHandler *io_read G_GNUC_UNUSED, + AioContext *write_ctx G_GNUC_UNUSED, + IOHandler *io_write G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ +} + + +static gboolean +qio_channel_null_source_prepare(GSource *source G_GNUC_UNUSED, + gint *timeout) +{ + *timeout = -1; + + return TRUE; +} + + +static gboolean +qio_channel_null_source_check(GSource *source G_GNUC_UNUSED) +{ + return TRUE; +} + + +static gboolean +qio_channel_null_source_dispatch(GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + QIOChannelFunc func = (QIOChannelFunc)callback; + QIOChannelNullSource *ssource = (QIOChannelNullSource *)source; + + return (*func)(ssource->ioc, + ssource->condition, + user_data); +} + + +static void +qio_channel_null_source_finalize(GSource *source) +{ + QIOChannelNullSource *ssource = (QIOChannelNullSource *)source; + + object_unref(OBJECT(ssource->ioc)); +} + + +GSourceFuncs qio_channel_null_source_funcs = { + qio_channel_null_source_prepare, + qio_channel_null_source_check, + qio_channel_null_source_dispatch, + qio_channel_null_source_finalize +}; + + +static GSource * +qio_channel_null_create_watch(QIOChannel *ioc, + GIOCondition condition) +{ + GSource *source; + QIOChannelNullSource *ssource; + + source = g_source_new(&qio_channel_null_source_funcs, + sizeof(QIOChannelNullSource)); + ssource = (QIOChannelNullSource *)source; + + ssource->ioc = ioc; + object_ref(OBJECT(ioc)); + + ssource->condition = condition; + + return source; +} + + +static void +qio_channel_null_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); + + ioc_klass->io_writev = qio_channel_null_writev; + ioc_klass->io_readv = qio_channel_null_readv; + ioc_klass->io_set_blocking = qio_channel_null_set_blocking; + ioc_klass->io_seek = qio_channel_null_seek; + ioc_klass->io_close = qio_channel_null_close; + ioc_klass->io_create_watch = qio_channel_null_create_watch; + ioc_klass->io_set_aio_fd_handler = qio_channel_null_set_aio_fd_handler; +} + + +static const TypeInfo qio_channel_null_info = { + .parent = TYPE_QIO_CHANNEL, + .name = TYPE_QIO_CHANNEL_NULL, + .instance_size = sizeof(QIOChannelNull), + .instance_init = qio_channel_null_init, + .class_init = qio_channel_null_class_init, +}; + + +static void +qio_channel_null_register_types(void) +{ + type_register_static(&qio_channel_null_info); +} + +type_init(qio_channel_null_register_types); diff --git a/io/channel-socket.c b/io/channel-socket.c index dc9c165de1..3a899b0608 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -22,6 +22,7 @@ #include "qapi/qapi-visit-sockets.h" #include "qemu/module.h" #include "io/channel-socket.h" +#include "io/channel-util.h" #include "io/channel-watch.h" #include "trace.h" #include "qapi/clone-visitor.h" @@ -173,6 +174,9 @@ int qio_channel_socket_connect_sync(QIOChannelSocket *ioc, } #endif + qio_channel_set_feature(QIO_CHANNEL(ioc), + QIO_CHANNEL_FEATURE_READ_MSG_PEEK); + return 0; } @@ -406,6 +410,9 @@ qio_channel_socket_accept(QIOChannelSocket *ioc, } #endif /* WIN32 */ + qio_channel_set_feature(QIO_CHANNEL(cioc), + QIO_CHANNEL_FEATURE_READ_MSG_PEEK); + trace_qio_channel_socket_accept_complete(ioc, cioc, cioc->fd); return cioc; @@ -436,9 +443,9 @@ static void qio_channel_socket_finalize(Object *obj) } } #ifdef WIN32 - WSAEventSelect(ioc->fd, NULL, 0); + qemu_socket_unselect(ioc->fd, NULL); #endif - closesocket(ioc->fd); + close(ioc->fd); ioc->fd = -1; } } @@ -496,6 +503,7 @@ static ssize_t qio_channel_socket_readv(QIOChannel *ioc, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp) { QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); @@ -517,6 +525,10 @@ static ssize_t qio_channel_socket_readv(QIOChannel *ioc, } + if (flags & QIO_CHANNEL_READ_FLAG_MSG_PEEK) { + sflags |= MSG_PEEK; + } + retry: ret = recvmsg(sioc->fd, &msg, sflags); if (ret < 0) { @@ -578,11 +590,17 @@ static ssize_t qio_channel_socket_writev(QIOChannel *ioc, memcpy(CMSG_DATA(cmsg), fds, fdsize); } -#ifdef QEMU_MSG_ZEROCOPY if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) { +#ifdef QEMU_MSG_ZEROCOPY sflags = MSG_ZEROCOPY; - } +#else + /* + * We expect QIOChannel class entry point to have + * blocked this code path already + */ + g_assert_not_reached(); #endif + } retry: ret = sendmsg(sioc->fd, &msg, sflags); @@ -592,21 +610,24 @@ static ssize_t qio_channel_socket_writev(QIOChannel *ioc, return QIO_CHANNEL_ERR_BLOCK; case EINTR: goto retry; -#ifdef QEMU_MSG_ZEROCOPY case ENOBUFS: - if (sflags & MSG_ZEROCOPY) { + if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) { error_setg_errno(errp, errno, "Process can't lock enough memory for using MSG_ZEROCOPY"); return -1; } break; -#endif } error_setg_errno(errp, errno, "Unable to write to socket"); return -1; } + + if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) { + sioc->zero_copy_queued++; + } + return ret; } #else /* WIN32 */ @@ -615,11 +636,17 @@ static ssize_t qio_channel_socket_readv(QIOChannel *ioc, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp) { QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ssize_t done = 0; ssize_t i; + int sflags = 0; + + if (flags & QIO_CHANNEL_READ_FLAG_MSG_PEEK) { + sflags |= MSG_PEEK; + } for (i = 0; i < niov; i++) { ssize_t ret; @@ -627,7 +654,7 @@ static ssize_t qio_channel_socket_readv(QIOChannel *ioc, ret = recv(sioc->fd, iov[i].iov_base, iov[i].iov_len, - 0); + sflags); if (ret < 0) { if (errno == EAGAIN) { if (done) { @@ -707,12 +734,18 @@ static int qio_channel_socket_flush(QIOChannel *ioc, struct cmsghdr *cm; char control[CMSG_SPACE(sizeof(*serr))]; int received; - int ret = 1; + int ret; + + if (sioc->zero_copy_queued == sioc->zero_copy_sent) { + return 0; + } msg.msg_control = control; msg.msg_controllen = sizeof(control); memset(control, 0, sizeof(control)); + ret = 1; + while (sioc->zero_copy_sent < sioc->zero_copy_queued) { received = recvmsg(sioc->fd, &msg, MSG_ERRQUEUE); if (received < 0) { @@ -731,8 +764,8 @@ static int qio_channel_socket_flush(QIOChannel *ioc, } cm = CMSG_FIRSTHDR(&msg); - if (cm->cmsg_level != SOL_IP && - cm->cmsg_type != IP_RECVERR) { + if (cm->cmsg_level != SOL_IP && cm->cmsg_type != IP_RECVERR && + cm->cmsg_level != SOL_IPV6 && cm->cmsg_type != IPV6_RECVERR) { error_setg_errno(errp, EPROTOTYPE, "Wrong cmsg in errqueue"); return -1; @@ -749,6 +782,11 @@ static int qio_channel_socket_flush(QIOChannel *ioc, "Error not from zero copy"); return -1; } + if (serr->ee_data < serr->ee_info) { + error_setg_errno(errp, serr->ee_origin, + "Wrong notification bounds"); + return -1; + } /* No errors, count successfully finished sendmsg()*/ sioc->zero_copy_sent += serr->ee_data - serr->ee_info + 1; @@ -814,13 +852,13 @@ qio_channel_socket_close(QIOChannel *ioc, if (sioc->fd != -1) { #ifdef WIN32 - WSAEventSelect(sioc->fd, NULL, 0); + qemu_socket_unselect(sioc->fd, NULL); #endif if (qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_LISTEN)) { socket_listen_cleanup(sioc->fd, errp); } - if (closesocket(sioc->fd) < 0) { + if (close(sioc->fd) < 0) { sioc->fd = -1; error_setg_errno(&err, errno, "Unable to close socket"); error_propagate(errp, err); @@ -861,14 +899,17 @@ qio_channel_socket_shutdown(QIOChannel *ioc, } static void qio_channel_socket_set_aio_fd_handler(QIOChannel *ioc, - AioContext *ctx, + AioContext *read_ctx, IOHandler *io_read, + AioContext *write_ctx, IOHandler *io_write, void *opaque) { QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); - aio_set_fd_handler(ctx, sioc->fd, false, - io_read, io_write, NULL, NULL, opaque); + + qio_channel_util_set_aio_fd_handler(sioc->fd, read_ctx, io_read, + sioc->fd, write_ctx, io_write, + opaque); } static GSource *qio_channel_socket_create_watch(QIOChannel *ioc, diff --git a/io/channel-tls.c b/io/channel-tls.c index 4ce890a538..1d9c9c72bf 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -74,6 +74,9 @@ qio_channel_tls_new_server(QIOChannel *master, ioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS)); ioc->master = master; + if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) { + qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SHUTDOWN); + } object_ref(OBJECT(master)); ioc->session = qcrypto_tls_session_new( @@ -195,12 +198,13 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, } trace_qio_channel_tls_handshake_pending(ioc, status); - qio_channel_add_watch_full(ioc->master, - condition, - qio_channel_tls_handshake_io, - data, - NULL, - context); + ioc->hs_ioc_tag = + qio_channel_add_watch_full(ioc->master, + condition, + qio_channel_tls_handshake_io, + data, + NULL, + context); } } @@ -215,6 +219,7 @@ static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc, QIOChannelTLS *tioc = QIO_CHANNEL_TLS( qio_task_get_source(task)); + tioc->hs_ioc_tag = 0; g_free(data); qio_channel_tls_handshake_task(tioc, task, context); @@ -260,6 +265,7 @@ static ssize_t qio_channel_tls_readv(QIOChannel *ioc, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp) { QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); @@ -374,26 +380,98 @@ static int qio_channel_tls_close(QIOChannel *ioc, { QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); + if (tioc->hs_ioc_tag) { + trace_qio_channel_tls_handshake_cancel(ioc); + g_clear_handle_id(&tioc->hs_ioc_tag, g_source_remove); + } + return qio_channel_close(tioc->master, errp); } static void qio_channel_tls_set_aio_fd_handler(QIOChannel *ioc, - AioContext *ctx, + AioContext *read_ctx, IOHandler *io_read, + AioContext *write_ctx, IOHandler *io_write, void *opaque) { QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); - qio_channel_set_aio_fd_handler(tioc->master, ctx, io_read, io_write, opaque); + qio_channel_set_aio_fd_handler(tioc->master, read_ctx, io_read, + write_ctx, io_write, opaque); +} + +typedef struct QIOChannelTLSSource QIOChannelTLSSource; +struct QIOChannelTLSSource { + GSource parent; + QIOChannelTLS *tioc; +}; + +static gboolean +qio_channel_tls_source_check(GSource *source) +{ + QIOChannelTLSSource *tsource = (QIOChannelTLSSource *)source; + + return qcrypto_tls_session_check_pending(tsource->tioc->session) > 0; +} + +static gboolean +qio_channel_tls_source_prepare(GSource *source, gint *timeout) +{ + *timeout = -1; + return qio_channel_tls_source_check(source); +} + +static gboolean +qio_channel_tls_source_dispatch(GSource *source, GSourceFunc callback, + gpointer user_data) +{ + return G_SOURCE_CONTINUE; +} + +static void +qio_channel_tls_source_finalize(GSource *source) +{ + QIOChannelTLSSource *tsource = (QIOChannelTLSSource *)source; + + object_unref(OBJECT(tsource->tioc)); +} + +static GSourceFuncs qio_channel_tls_source_funcs = { + qio_channel_tls_source_prepare, + qio_channel_tls_source_check, + qio_channel_tls_source_dispatch, + qio_channel_tls_source_finalize +}; + +static void +qio_channel_tls_read_watch(QIOChannelTLS *tioc, GSource *source) +{ + GSource *child; + QIOChannelTLSSource *tlssource; + + child = g_source_new(&qio_channel_tls_source_funcs, + sizeof(QIOChannelTLSSource)); + tlssource = (QIOChannelTLSSource *)child; + + tlssource->tioc = tioc; + object_ref(OBJECT(tioc)); + + g_source_add_child_source(source, child); + g_source_unref(child); } static GSource *qio_channel_tls_create_watch(QIOChannel *ioc, GIOCondition condition) { QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); + GSource *source = qio_channel_create_watch(tioc->master, condition); - return qio_channel_create_watch(tioc->master, condition); + if (condition & G_IO_IN) { + qio_channel_tls_read_watch(tioc, source); + } + + return source; } QCryptoTLSSession * diff --git a/io/channel-util.c b/io/channel-util.c index 848a7a43d6..4b340d46d7 100644 --- a/io/channel-util.c +++ b/io/channel-util.c @@ -36,3 +36,27 @@ QIOChannel *qio_channel_new_fd(int fd, } return ioc; } + + +void qio_channel_util_set_aio_fd_handler(int read_fd, + AioContext *read_ctx, + IOHandler *io_read, + int write_fd, + AioContext *write_ctx, + IOHandler *io_write, + void *opaque) +{ + if (read_fd == write_fd && read_ctx == write_ctx) { + aio_set_fd_handler(read_ctx, read_fd, io_read, io_write, + NULL, NULL, opaque); + } else { + if (read_ctx) { + aio_set_fd_handler(read_ctx, read_fd, io_read, NULL, + NULL, NULL, opaque); + } + if (write_ctx) { + aio_set_fd_handler(write_ctx, write_fd, NULL, io_write, + NULL, NULL, opaque); + } + } +} diff --git a/io/channel-watch.c b/io/channel-watch.c index 0289b3647c..64b486e378 100644 --- a/io/channel-watch.c +++ b/io/channel-watch.c @@ -115,28 +115,24 @@ static gboolean qio_channel_socket_source_check(GSource *source) { static struct timeval tv0; - QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source; - WSANETWORKEVENTS ev; fd_set rfds, wfds, xfds; if (!ssource->condition) { return 0; } - WSAEnumNetworkEvents(ssource->socket, ssource->ioc->event, &ev); - FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&xfds); if (ssource->condition & G_IO_IN) { - FD_SET((SOCKET)ssource->socket, &rfds); + FD_SET(ssource->socket, &rfds); } if (ssource->condition & G_IO_OUT) { - FD_SET((SOCKET)ssource->socket, &wfds); + FD_SET(ssource->socket, &wfds); } if (ssource->condition & G_IO_PRI) { - FD_SET((SOCKET)ssource->socket, &xfds); + FD_SET(ssource->socket, &xfds); } ssource->revents = 0; if (select(0, &rfds, &wfds, &xfds, &tv0) == 0) { @@ -279,17 +275,15 @@ GSource *qio_channel_create_fd_watch(QIOChannel *ioc, #ifdef CONFIG_WIN32 GSource *qio_channel_create_socket_watch(QIOChannel *ioc, - int socket, + int sockfd, GIOCondition condition) { GSource *source; QIOChannelSocketSource *ssource; -#ifdef WIN32 - WSAEventSelect(socket, ioc->event, - FD_READ | FD_ACCEPT | FD_CLOSE | - FD_CONNECT | FD_WRITE | FD_OOB); -#endif + qemu_socket_select(sockfd, ioc->event, + FD_READ | FD_ACCEPT | FD_CLOSE | + FD_CONNECT | FD_WRITE | FD_OOB, NULL); source = g_source_new(&qio_channel_socket_source_funcs, sizeof(QIOChannelSocketSource)); @@ -299,7 +293,7 @@ GSource *qio_channel_create_socket_watch(QIOChannel *ioc, object_ref(OBJECT(ioc)); ssource->condition = condition; - ssource->socket = socket; + ssource->socket = _get_osfhandle(sockfd); ssource->revents = 0; ssource->fd.fd = (gintptr)ioc->event; diff --git a/io/channel-websock.c b/io/channel-websock.c index 9619906ac3..a12acc27cf 100644 --- a/io/channel-websock.c +++ b/io/channel-websock.c @@ -32,7 +32,7 @@ #define QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN 24 #define QIO_CHANNEL_WEBSOCK_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" -#define QIO_CHANNEL_WEBSOCK_GUID_LEN strlen(QIO_CHANNEL_WEBSOCK_GUID) +#define QIO_CHANNEL_WEBSOCK_GUID_LEN (sizeof(QIO_CHANNEL_WEBSOCK_GUID) - 1) #define QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL "sec-websocket-protocol" #define QIO_CHANNEL_WEBSOCK_HEADER_VERSION "sec-websocket-version" @@ -1081,6 +1081,7 @@ static ssize_t qio_channel_websock_readv(QIOChannel *ioc, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp) { QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc); diff --git a/io/channel.c b/io/channel.c index 0640941ac5..a1f12f8e90 100644 --- a/io/channel.c +++ b/io/channel.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "block/aio-wait.h" #include "io/channel.h" #include "qapi/error.h" #include "qemu/main-loop.h" @@ -52,6 +53,7 @@ ssize_t qio_channel_readv_full(QIOChannel *ioc, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp) { QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); @@ -63,7 +65,14 @@ ssize_t qio_channel_readv_full(QIOChannel *ioc, return -1; } - return klass->io_readv(ioc, iov, niov, fds, nfds, errp); + if ((flags & QIO_CHANNEL_READ_FLAG_MSG_PEEK) && + !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_READ_MSG_PEEK)) { + error_setg_errno(errp, EINVAL, + "Channel does not support peek read"); + return -1; + } + + return klass->io_readv(ioc, iov, niov, fds, nfds, flags, errp); } @@ -101,27 +110,27 @@ ssize_t qio_channel_writev_full(QIOChannel *ioc, } -int qio_channel_readv_all_eof(QIOChannel *ioc, - const struct iovec *iov, - size_t niov, - Error **errp) +int coroutine_mixed_fn qio_channel_readv_all_eof(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp) { return qio_channel_readv_full_all_eof(ioc, iov, niov, NULL, NULL, errp); } -int qio_channel_readv_all(QIOChannel *ioc, - const struct iovec *iov, - size_t niov, - Error **errp) +int coroutine_mixed_fn qio_channel_readv_all(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp) { return qio_channel_readv_full_all(ioc, iov, niov, NULL, NULL, errp); } -int qio_channel_readv_full_all_eof(QIOChannel *ioc, - const struct iovec *iov, - size_t niov, - int **fds, size_t *nfds, - Error **errp) +int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, size_t *nfds, + Error **errp) { int ret = -1; struct iovec *local_iov = g_new(struct iovec, niov); @@ -146,7 +155,7 @@ int qio_channel_readv_full_all_eof(QIOChannel *ioc, while ((nlocal_iov > 0) || local_fds) { ssize_t len; len = qio_channel_readv_full(ioc, local_iov, nlocal_iov, local_fds, - local_nfds, errp); + local_nfds, 0, errp); if (len == QIO_CHANNEL_ERR_BLOCK) { if (qemu_in_coroutine()) { qio_channel_yield(ioc, G_IO_IN); @@ -207,11 +216,11 @@ next_iter: return ret; } -int qio_channel_readv_full_all(QIOChannel *ioc, - const struct iovec *iov, - size_t niov, - int **fds, size_t *nfds, - Error **errp) +int coroutine_mixed_fn qio_channel_readv_full_all(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, size_t *nfds, + Error **errp) { int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, errp); @@ -226,19 +235,19 @@ int qio_channel_readv_full_all(QIOChannel *ioc, return ret; } -int qio_channel_writev_all(QIOChannel *ioc, - const struct iovec *iov, - size_t niov, - Error **errp) +int coroutine_mixed_fn qio_channel_writev_all(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp) { return qio_channel_writev_full_all(ioc, iov, niov, NULL, 0, 0, errp); } -int qio_channel_writev_full_all(QIOChannel *ioc, - const struct iovec *iov, - size_t niov, - int *fds, size_t nfds, - int flags, Error **errp) +int coroutine_mixed_fn qio_channel_writev_full_all(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, size_t nfds, + int flags, Error **errp) { int ret = -1; struct iovec *local_iov = g_new(struct iovec, niov); @@ -284,7 +293,7 @@ ssize_t qio_channel_readv(QIOChannel *ioc, size_t niov, Error **errp) { - return qio_channel_readv_full(ioc, iov, niov, NULL, NULL, errp); + return qio_channel_readv_full(ioc, iov, niov, NULL, NULL, 0, errp); } @@ -303,7 +312,7 @@ ssize_t qio_channel_read(QIOChannel *ioc, Error **errp) { struct iovec iov = { .iov_base = buf, .iov_len = buflen }; - return qio_channel_readv_full(ioc, &iov, 1, NULL, NULL, errp); + return qio_channel_readv_full(ioc, &iov, 1, NULL, NULL, 0, errp); } @@ -317,30 +326,30 @@ ssize_t qio_channel_write(QIOChannel *ioc, } -int qio_channel_read_all_eof(QIOChannel *ioc, - char *buf, - size_t buflen, - Error **errp) +int coroutine_mixed_fn qio_channel_read_all_eof(QIOChannel *ioc, + char *buf, + size_t buflen, + Error **errp) { struct iovec iov = { .iov_base = buf, .iov_len = buflen }; return qio_channel_readv_all_eof(ioc, &iov, 1, errp); } -int qio_channel_read_all(QIOChannel *ioc, - char *buf, - size_t buflen, - Error **errp) +int coroutine_mixed_fn qio_channel_read_all(QIOChannel *ioc, + char *buf, + size_t buflen, + Error **errp) { struct iovec iov = { .iov_base = buf, .iov_len = buflen }; return qio_channel_readv_all(ioc, &iov, 1, errp); } -int qio_channel_write_all(QIOChannel *ioc, - const char *buf, - size_t buflen, - Error **errp) +int coroutine_mixed_fn qio_channel_write_all(QIOChannel *ioc, + const char *buf, + size_t buflen, + Error **errp) { struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen }; return qio_channel_writev_all(ioc, &iov, 1, errp); @@ -356,6 +365,12 @@ int qio_channel_set_blocking(QIOChannel *ioc, } +void qio_channel_set_follow_coroutine_ctx(QIOChannel *ioc, bool enabled) +{ + ioc->follow_coroutine_ctx = enabled; +} + + int qio_channel_close(QIOChannel *ioc, Error **errp) { @@ -379,14 +394,16 @@ GSource *qio_channel_create_watch(QIOChannel *ioc, void qio_channel_set_aio_fd_handler(QIOChannel *ioc, - AioContext *ctx, + AioContext *read_ctx, IOHandler *io_read, + AioContext *write_ctx, IOHandler *io_write, void *opaque) { QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); - klass->io_set_aio_fd_handler(ioc, ctx, io_read, io_write, opaque); + klass->io_set_aio_fd_handler(ioc, read_ctx, io_read, write_ctx, io_write, + opaque); } guint qio_channel_add_watch_full(QIOChannel *ioc, @@ -437,6 +454,64 @@ GSource *qio_channel_add_watch_source(QIOChannel *ioc, } +ssize_t qio_channel_pwritev(QIOChannel *ioc, const struct iovec *iov, + size_t niov, off_t offset, Error **errp) +{ + QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); + + if (!klass->io_pwritev) { + error_setg(errp, "Channel does not support pwritev"); + return -1; + } + + if (!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_SEEKABLE)) { + error_setg_errno(errp, EINVAL, "Requested channel is not seekable"); + return -1; + } + + return klass->io_pwritev(ioc, iov, niov, offset, errp); +} + +ssize_t qio_channel_pwrite(QIOChannel *ioc, char *buf, size_t buflen, + off_t offset, Error **errp) +{ + struct iovec iov = { + .iov_base = buf, + .iov_len = buflen + }; + + return qio_channel_pwritev(ioc, &iov, 1, offset, errp); +} + +ssize_t qio_channel_preadv(QIOChannel *ioc, const struct iovec *iov, + size_t niov, off_t offset, Error **errp) +{ + QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); + + if (!klass->io_preadv) { + error_setg(errp, "Channel does not support preadv"); + return -1; + } + + if (!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_SEEKABLE)) { + error_setg_errno(errp, EINVAL, "Requested channel is not seekable"); + return -1; + } + + return klass->io_preadv(ioc, iov, niov, offset, errp); +} + +ssize_t qio_channel_pread(QIOChannel *ioc, char *buf, size_t buflen, + off_t offset, Error **errp) +{ + struct iovec iov = { + .iov_base = buf, + .iov_len = buflen + }; + + return qio_channel_preadv(ioc, &iov, 1, offset, errp); +} + int qio_channel_shutdown(QIOChannel *ioc, QIOChannelShutdown how, Error **errp) @@ -506,7 +581,11 @@ int qio_channel_flush(QIOChannel *ioc, static void qio_channel_restart_read(void *opaque) { QIOChannel *ioc = opaque; - Coroutine *co = ioc->read_coroutine; + Coroutine *co = qatomic_xchg(&ioc->read_coroutine, NULL); + + if (!co) { + return; + } /* Assert that aio_co_wake() reenters the coroutine directly */ assert(qemu_get_current_aio_context() == @@ -517,7 +596,11 @@ static void qio_channel_restart_read(void *opaque) static void qio_channel_restart_write(void *opaque) { QIOChannel *ioc = opaque; - Coroutine *co = ioc->write_coroutine; + Coroutine *co = qatomic_xchg(&ioc->write_coroutine, NULL); + + if (!co) { + return; + } /* Assert that aio_co_wake() reenters the coroutine directly */ assert(qemu_get_current_aio_context() == @@ -525,65 +608,121 @@ static void qio_channel_restart_write(void *opaque) aio_co_wake(co); } -static void qio_channel_set_aio_fd_handlers(QIOChannel *ioc) +static void coroutine_fn +qio_channel_set_fd_handlers(QIOChannel *ioc, GIOCondition condition) { - IOHandler *rd_handler = NULL, *wr_handler = NULL; + AioContext *ctx = ioc->follow_coroutine_ctx ? + qemu_coroutine_get_aio_context(qemu_coroutine_self()) : + iohandler_get_aio_context(); + AioContext *read_ctx = NULL; + IOHandler *io_read = NULL; + AioContext *write_ctx = NULL; + IOHandler *io_write = NULL; + + if (condition == G_IO_IN) { + ioc->read_coroutine = qemu_coroutine_self(); + ioc->read_ctx = ctx; + read_ctx = ctx; + io_read = qio_channel_restart_read; + + /* + * Thread safety: if the other coroutine is set and its AioContext + * matches ours, then there is mutual exclusion between read and write + * because they share a single thread and it's safe to set both read + * and write fd handlers here. If the AioContext does not match ours, + * then both threads may run in parallel but there is no shared state + * to worry about. + */ + if (ioc->write_coroutine && ioc->write_ctx == ctx) { + write_ctx = ctx; + io_write = qio_channel_restart_write; + } + } else if (condition == G_IO_OUT) { + ioc->write_coroutine = qemu_coroutine_self(); + ioc->write_ctx = ctx; + write_ctx = ctx; + io_write = qio_channel_restart_write; + if (ioc->read_coroutine && ioc->read_ctx == ctx) { + read_ctx = ctx; + io_read = qio_channel_restart_read; + } + } else { + abort(); + } + + qio_channel_set_aio_fd_handler(ioc, read_ctx, io_read, + write_ctx, io_write, ioc); +} + +static void coroutine_fn +qio_channel_clear_fd_handlers(QIOChannel *ioc, GIOCondition condition) +{ + AioContext *read_ctx = NULL; + IOHandler *io_read = NULL; + AioContext *write_ctx = NULL; + IOHandler *io_write = NULL; AioContext *ctx; - if (ioc->read_coroutine) { - rd_handler = qio_channel_restart_read; - } - if (ioc->write_coroutine) { - wr_handler = qio_channel_restart_write; + if (condition == G_IO_IN) { + ctx = ioc->read_ctx; + read_ctx = ctx; + io_read = NULL; + if (ioc->write_coroutine && ioc->write_ctx == ctx) { + write_ctx = ctx; + io_write = qio_channel_restart_write; + } + } else if (condition == G_IO_OUT) { + ctx = ioc->write_ctx; + write_ctx = ctx; + io_write = NULL; + if (ioc->read_coroutine && ioc->read_ctx == ctx) { + read_ctx = ctx; + io_read = qio_channel_restart_read; + } + } else { + abort(); } - ctx = ioc->ctx ? ioc->ctx : iohandler_get_aio_context(); - qio_channel_set_aio_fd_handler(ioc, ctx, rd_handler, wr_handler, ioc); -} - -void qio_channel_attach_aio_context(QIOChannel *ioc, - AioContext *ctx) -{ - assert(!ioc->read_coroutine); - assert(!ioc->write_coroutine); - ioc->ctx = ctx; -} - -void qio_channel_detach_aio_context(QIOChannel *ioc) -{ - ioc->read_coroutine = NULL; - ioc->write_coroutine = NULL; - qio_channel_set_aio_fd_handlers(ioc); - ioc->ctx = NULL; + qio_channel_set_aio_fd_handler(ioc, read_ctx, io_read, + write_ctx, io_write, ioc); } void coroutine_fn qio_channel_yield(QIOChannel *ioc, GIOCondition condition) { + AioContext *ioc_ctx; + assert(qemu_in_coroutine()); + ioc_ctx = qemu_coroutine_get_aio_context(qemu_coroutine_self()); + if (condition == G_IO_IN) { assert(!ioc->read_coroutine); - ioc->read_coroutine = qemu_coroutine_self(); } else if (condition == G_IO_OUT) { assert(!ioc->write_coroutine); - ioc->write_coroutine = qemu_coroutine_self(); } else { abort(); } - qio_channel_set_aio_fd_handlers(ioc); + qio_channel_set_fd_handlers(ioc, condition); qemu_coroutine_yield(); + assert(in_aio_context_home_thread(ioc_ctx)); /* Allow interrupting the operation by reentering the coroutine other than * through the aio_fd_handlers. */ - if (condition == G_IO_IN && ioc->read_coroutine) { - ioc->read_coroutine = NULL; - qio_channel_set_aio_fd_handlers(ioc); - } else if (condition == G_IO_OUT && ioc->write_coroutine) { - ioc->write_coroutine = NULL; - qio_channel_set_aio_fd_handlers(ioc); + if (condition == G_IO_IN) { + assert(ioc->read_coroutine == NULL); + } else if (condition == G_IO_OUT) { + assert(ioc->write_coroutine == NULL); } + qio_channel_clear_fd_handlers(ioc, condition); } +void qio_channel_wake_read(QIOChannel *ioc) +{ + Coroutine *co = qatomic_xchg(&ioc->read_coroutine, NULL); + if (co) { + aio_co_wake(co); + } +} static gboolean qio_channel_wait_complete(QIOChannel *ioc, GIOCondition condition, @@ -624,6 +763,10 @@ static void qio_channel_finalize(Object *obj) { QIOChannel *ioc = QIO_CHANNEL(obj); + /* Must not have coroutines in qio_channel_yield() */ + assert(!ioc->read_coroutine); + assert(!ioc->write_coroutine); + g_free(ioc->name); #ifdef _WIN32 diff --git a/io/meson.build b/io/meson.build index bbcd3c53a4..283b9b2bdb 100644 --- a/io/meson.build +++ b/io/meson.build @@ -3,6 +3,7 @@ io_ss.add(files( 'channel-buffer.c', 'channel-command.c', 'channel-file.c', + 'channel-null.c', 'channel-socket.c', 'channel-tls.c', 'channel-util.c', diff --git a/io/net-listener.c b/io/net-listener.c index 1c984d69c6..47405965a6 100644 --- a/io/net-listener.c +++ b/io/net-listener.c @@ -109,9 +109,7 @@ void qio_net_listener_add(QIONetListener *listener, QIOChannelSocket *sioc) { if (listener->name) { - char *name = g_strdup_printf("%s-listen", listener->name); - qio_channel_set_name(QIO_CHANNEL(sioc), name); - g_free(name); + qio_channel_set_name(QIO_CHANNEL(sioc), listener->name); } listener->sioc = g_renew(QIOChannelSocket *, listener->sioc, diff --git a/io/trace-events b/io/trace-events index c5e814eb44..d4c0f84a9a 100644 --- a/io/trace-events +++ b/io/trace-events @@ -10,6 +10,9 @@ qio_task_thread_result(void *task) "Task thread result task=%p" qio_task_thread_source_attach(void *task, void *source) "Task thread source attach task=%p source=%p" qio_task_thread_source_cancel(void *task, void *source) "Task thread source cancel task=%p source=%p" +# channel-null.c +qio_channel_null_new(void *ioc) "Null new ioc=%p" + # channel-socket.c qio_channel_socket_new(void *ioc) "Socket new ioc=%p" qio_channel_socket_new_fd(void *ioc, int fd) "Socket new ioc=%p fd=%d" @@ -40,6 +43,7 @@ qio_channel_tls_handshake_start(void *ioc) "TLS handshake start ioc=%p" qio_channel_tls_handshake_pending(void *ioc, int status) "TLS handshake pending ioc=%p status=%d" qio_channel_tls_handshake_fail(void *ioc) "TLS handshake fail ioc=%p" qio_channel_tls_handshake_complete(void *ioc) "TLS handshake complete ioc=%p" +qio_channel_tls_handshake_cancel(void *ioc) "TLS handshake cancel ioc=%p" qio_channel_tls_credentials_allow(void *ioc) "TLS credentials allow ioc=%p" qio_channel_tls_credentials_deny(void *ioc) "TLS credentials deny ioc=%p" diff --git a/iothread.c b/iothread.c index 529194a566..e1e9e04736 100644 --- a/iothread.c +++ b/iothread.c @@ -25,10 +25,6 @@ #include "qemu/rcu.h" #include "qemu/main-loop.h" -typedef ObjectClass IOThreadClass; - -DECLARE_CLASS_CHECKERS(IOThreadClass, IOTHREAD, - TYPE_IOTHREAD) #ifdef CONFIG_POSIX /* Benchmark results from 2016 on NVMe SSD drives show max polling times around @@ -142,12 +138,14 @@ static void iothread_instance_finalize(Object *obj) qemu_sem_destroy(&iothread->init_done_sem); } -static void iothread_init_gcontext(IOThread *iothread) +static void iothread_init_gcontext(IOThread *iothread, const char *thread_name) { GSource *source; + g_autofree char *name = g_strdup_printf("%s aio-context", thread_name); iothread->worker_context = g_main_context_new(); source = aio_get_g_source(iothread_get_aio_context(iothread)); + g_source_set_name(source, name); g_source_attach(source, iothread->worker_context); g_source_unref(source); iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE); @@ -155,8 +153,8 @@ static void iothread_init_gcontext(IOThread *iothread) static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp) { - IOThread *iothread = IOTHREAD(base); ERRP_GUARD(); + IOThread *iothread = IOTHREAD(base); if (!iothread->ctx) { return; @@ -172,8 +170,7 @@ static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp) } aio_context_set_aio_params(iothread->ctx, - iothread->parent_obj.aio_max_batch, - errp); + iothread->parent_obj.aio_max_batch); aio_context_set_thread_pool_params(iothread->ctx, base->thread_pool_min, base->thread_pool_max, errp); @@ -184,7 +181,7 @@ static void iothread_init(EventLoopBase *base, Error **errp) { Error *local_error = NULL; IOThread *iothread = IOTHREAD(base); - char *thread_name; + g_autofree char *thread_name = NULL; iothread->stopping = false; iothread->running = true; @@ -193,11 +190,14 @@ static void iothread_init(EventLoopBase *base, Error **errp) return; } + thread_name = g_strdup_printf("IO %s", + object_get_canonical_path_component(OBJECT(base))); + /* * Init one GMainContext for the iothread unconditionally, even if * it's not used */ - iothread_init_gcontext(iothread); + iothread_init_gcontext(iothread, thread_name); iothread_set_aio_context_params(base, &local_error); if (local_error) { @@ -210,11 +210,8 @@ static void iothread_init(EventLoopBase *base, Error **errp) /* This assumes we are called from a thread with useful CPU affinity for us * to inherit. */ - thread_name = g_strdup_printf("IO %s", - object_get_canonical_path_component(OBJECT(base))); qemu_thread_create(&iothread->thread, thread_name, iothread_run, iothread, QEMU_THREAD_JOINABLE); - g_free(thread_name); /* Wait for initialization to complete */ while (iothread->thread_id == -1) { @@ -407,6 +404,5 @@ IOThread *iothread_by_id(const char *id) bool qemu_in_iothread(void) { - return qemu_get_current_aio_context() == qemu_get_aio_context() ? - false : true; + return qemu_get_current_aio_context() != qemu_get_aio_context(); } diff --git a/job-qmp.c b/job-qmp.c index 829a28aa70..9e26fa899f 100644 --- a/job-qmp.c +++ b/job-qmp.c @@ -29,119 +29,117 @@ #include "qapi/error.h" #include "trace/trace-root.h" -/* Get a job using its ID and acquire its AioContext */ -static Job *find_job(const char *id, AioContext **aio_context, Error **errp) +/* + * Get a job using its ID. Called with job_mutex held. + */ +static Job *find_job_locked(const char *id, Error **errp) { Job *job; - *aio_context = NULL; - - job = job_get(id); + job = job_get_locked(id); if (!job) { error_setg(errp, "Job not found"); return NULL; } - *aio_context = job->aio_context; - aio_context_acquire(*aio_context); - return job; } void qmp_job_cancel(const char *id, Error **errp) { - AioContext *aio_context; - Job *job = find_job(id, &aio_context, errp); + Job *job; + + JOB_LOCK_GUARD(); + job = find_job_locked(id, errp); if (!job) { return; } trace_qmp_job_cancel(job); - job_user_cancel(job, true, errp); - aio_context_release(aio_context); + job_user_cancel_locked(job, true, errp); } void qmp_job_pause(const char *id, Error **errp) { - AioContext *aio_context; - Job *job = find_job(id, &aio_context, errp); + Job *job; + + JOB_LOCK_GUARD(); + job = find_job_locked(id, errp); if (!job) { return; } trace_qmp_job_pause(job); - job_user_pause(job, errp); - aio_context_release(aio_context); + job_user_pause_locked(job, errp); } void qmp_job_resume(const char *id, Error **errp) { - AioContext *aio_context; - Job *job = find_job(id, &aio_context, errp); + Job *job; + + JOB_LOCK_GUARD(); + job = find_job_locked(id, errp); if (!job) { return; } trace_qmp_job_resume(job); - job_user_resume(job, errp); - aio_context_release(aio_context); + job_user_resume_locked(job, errp); } void qmp_job_complete(const char *id, Error **errp) { - AioContext *aio_context; - Job *job = find_job(id, &aio_context, errp); + Job *job; + + JOB_LOCK_GUARD(); + job = find_job_locked(id, errp); if (!job) { return; } trace_qmp_job_complete(job); - job_complete(job, errp); - aio_context_release(aio_context); + job_complete_locked(job, errp); } void qmp_job_finalize(const char *id, Error **errp) { - AioContext *aio_context; - Job *job = find_job(id, &aio_context, errp); + Job *job; + + JOB_LOCK_GUARD(); + job = find_job_locked(id, errp); if (!job) { return; } trace_qmp_job_finalize(job); - job_ref(job); - job_finalize(job, errp); + job_ref_locked(job); + job_finalize_locked(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); + job_unref_locked(job); } void qmp_job_dismiss(const char *id, Error **errp) { - AioContext *aio_context; - Job *job = find_job(id, &aio_context, errp); + Job *job; + + JOB_LOCK_GUARD(); + job = find_job_locked(id, errp); if (!job) { return; } trace_qmp_job_dismiss(job); - job_dismiss(&job, errp); - aio_context_release(aio_context); + job_dismiss_locked(&job, errp); } -static JobInfo *job_query_single(Job *job, Error **errp) +/* Called with job_mutex held. */ +static JobInfo *job_query_single_locked(Job *job, Error **errp) { JobInfo *info; uint64_t progress_current; @@ -158,8 +156,7 @@ static JobInfo *job_query_single(Job *job, Error **errp) .status = job->status, .current_progress = progress_current, .total_progress = progress_total, - .has_error = !!job->err, - .error = job->err ? \ + .error = job->err ? g_strdup(error_get_pretty(job->err)) : NULL, }; @@ -171,17 +168,15 @@ JobInfoList *qmp_query_jobs(Error **errp) JobInfoList *head = NULL, **tail = &head; Job *job; - for (job = job_next(NULL); job; job = job_next(job)) { + JOB_LOCK_GUARD(); + + for (job = job_next_locked(NULL); job; job = job_next_locked(job)) { JobInfo *value; - AioContext *aio_context; if (job_is_internal(job)) { continue; } - aio_context = job->aio_context; - aio_context_acquire(aio_context); - value = job_query_single(job, errp); - aio_context_release(aio_context); + value = job_query_single_locked(job, errp); if (!value) { qapi_free_JobInfoList(head); return NULL; diff --git a/job.c b/job.c index 075c6f3a20..660ce22c56 100644 --- a/job.c +++ b/job.c @@ -32,6 +32,27 @@ #include "trace/trace-root.h" #include "qapi/qapi-events-job.h" +/* + * The job API is composed of two categories of functions. + * + * The first includes functions used by the monitor. The monitor is + * peculiar in that it accesses the job list with job_get, and + * therefore needs consistency across job_get and the actual operation + * (e.g. job_user_cancel). To achieve this consistency, the caller + * calls job_lock/job_unlock itself around the whole operation. + * + * + * The second includes functions used by the job drivers and sometimes + * by the core block layer. These delegate the locking to the callee instead. + */ + +/* + * job_mutex protects the jobs list, but also makes the + * struct job fields thread-safe. + */ +QemuMutex job_mutex; + +/* Protected by job_mutex */ static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); /* Job State Transition Table */ @@ -59,6 +80,7 @@ bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { [JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0}, [JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, + [JOB_VERB_CHANGE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, }; /* Transactional group of jobs */ @@ -74,17 +96,12 @@ struct JobTxn { int refcnt; }; -/* Right now, this mutex is only needed to synchronize accesses to job->busy - * and job->sleep_timer, such as concurrent calls to job_do_yield and - * job_enter. */ -static QemuMutex job_mutex; - -static void job_lock(void) +void job_lock(void) { qemu_mutex_lock(&job_mutex); } -static void job_unlock(void) +void job_unlock(void) { qemu_mutex_unlock(&job_mutex); } @@ -102,19 +119,38 @@ JobTxn *job_txn_new(void) return txn; } -static void job_txn_ref(JobTxn *txn) +/* Called with job_mutex held. */ +static void job_txn_ref_locked(JobTxn *txn) { txn->refcnt++; } -void job_txn_unref(JobTxn *txn) +void job_txn_unref_locked(JobTxn *txn) { if (txn && --txn->refcnt == 0) { g_free(txn); } } -void job_txn_add_job(JobTxn *txn, Job *job) +void job_txn_unref(JobTxn *txn) +{ + JOB_LOCK_GUARD(); + job_txn_unref_locked(txn); +} + +/** + * @txn: The transaction (may be NULL) + * @job: Job to add to the transaction + * + * Add @job to the transaction. The @job must not already be in a transaction. + * The caller must call either job_txn_unref() or job_completed() to release + * the reference that is automatically grabbed here. + * + * If @txn is NULL, the function does nothing. + * + * Called with job_mutex held. + */ +static void job_txn_add_job_locked(JobTxn *txn, Job *job) { if (!txn) { return; @@ -124,21 +160,22 @@ void job_txn_add_job(JobTxn *txn, Job *job) job->txn = txn; QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); - job_txn_ref(txn); + job_txn_ref_locked(txn); } -static void job_txn_del_job(Job *job) +/* Called with job_mutex held. */ +static void job_txn_del_job_locked(Job *job) { if (job->txn) { QLIST_REMOVE(job, txn_list); - job_txn_unref(job->txn); + job_txn_unref_locked(job->txn); job->txn = NULL; } } -static int job_txn_apply(Job *job, int fn(Job *)) +/* Called with job_mutex held, but releases it temporarily. */ +static int job_txn_apply_locked(Job *job, int fn(Job *)) { - AioContext *inner_ctx; Job *other_job, *next; JobTxn *txn = job->txn; int rc = 0; @@ -149,25 +186,16 @@ static int job_txn_apply(Job *job, int fn(Job *)) * 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); + job_ref_locked(job); 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); + job_unref_locked(job); return rc; } @@ -176,7 +204,8 @@ bool job_is_internal(Job *job) return (job->id == NULL); } -static void job_state_transition(Job *job, JobStatus s1) +/* Called with job_mutex held. */ +static void job_state_transition_locked(Job *job, JobStatus s1) { JobStatus s0 = job->status; assert(s1 >= 0 && s1 < JOB_STATUS__MAX); @@ -191,7 +220,7 @@ static void job_state_transition(Job *job, JobStatus s1) } } -int job_apply_verb(Job *job, JobVerb verb, Error **errp) +int job_apply_verb_locked(Job *job, JobVerb verb, Error **errp) { JobStatus s0 = job->status; assert(verb >= 0 && verb < JOB_VERB__MAX); @@ -215,41 +244,60 @@ const char *job_type_str(const Job *job) return JobType_str(job_type(job)); } -bool job_is_cancelled(Job *job) +bool job_is_cancelled_locked(Job *job) { /* force_cancel may be true only if cancelled is true, too */ assert(job->cancelled || !job->force_cancel); return job->force_cancel; } -bool job_cancel_requested(Job *job) +bool job_is_cancelled(Job *job) +{ + JOB_LOCK_GUARD(); + return job_is_cancelled_locked(job); +} + +/* Called with job_mutex held. */ +static bool job_cancel_requested_locked(Job *job) { return job->cancelled; } +bool job_cancel_requested(Job *job) +{ + JOB_LOCK_GUARD(); + return job_cancel_requested_locked(job); +} + +bool job_is_ready_locked(Job *job) +{ + switch (job->status) { + case JOB_STATUS_UNDEFINED: + case JOB_STATUS_CREATED: + case JOB_STATUS_RUNNING: + case JOB_STATUS_PAUSED: + case JOB_STATUS_WAITING: + case JOB_STATUS_PENDING: + case JOB_STATUS_ABORTING: + case JOB_STATUS_CONCLUDED: + case JOB_STATUS_NULL: + return false; + case JOB_STATUS_READY: + case JOB_STATUS_STANDBY: + return true; + default: + g_assert_not_reached(); + } + return false; +} + bool job_is_ready(Job *job) { - switch (job->status) { - case JOB_STATUS_UNDEFINED: - case JOB_STATUS_CREATED: - case JOB_STATUS_RUNNING: - case JOB_STATUS_PAUSED: - case JOB_STATUS_WAITING: - case JOB_STATUS_PENDING: - case JOB_STATUS_ABORTING: - case JOB_STATUS_CONCLUDED: - case JOB_STATUS_NULL: - return false; - case JOB_STATUS_READY: - case JOB_STATUS_STANDBY: - return true; - default: - g_assert_not_reached(); - } - return false; + JOB_LOCK_GUARD(); + return job_is_ready_locked(job); } -bool job_is_completed(Job *job) +bool job_is_completed_locked(Job *job) { switch (job->status) { case JOB_STATUS_UNDEFINED: @@ -271,17 +319,24 @@ bool job_is_completed(Job *job) return false; } -static bool job_started(Job *job) +static bool job_is_completed(Job *job) +{ + JOB_LOCK_GUARD(); + return job_is_completed_locked(job); +} + +static bool job_started_locked(Job *job) { return job->co; } -static bool job_should_pause(Job *job) +/* Called with job_mutex held. */ +static bool job_should_pause_locked(Job *job) { return job->pause_count > 0; } -Job *job_next(Job *job) +Job *job_next_locked(Job *job) { if (!job) { return QLIST_FIRST(&jobs); @@ -289,7 +344,13 @@ Job *job_next(Job *job) return QLIST_NEXT(job, job_list); } -Job *job_get(const char *id) +Job *job_next(Job *job) +{ + JOB_LOCK_GUARD(); + return job_next_locked(job); +} + +Job *job_get_locked(const char *id) { Job *job; @@ -302,6 +363,18 @@ Job *job_get(const char *id) return NULL; } +void job_set_aio_context(Job *job, AioContext *ctx) +{ + /* protect against read in job_finish_sync_locked and job_start */ + GLOBAL_STATE_CODE(); + /* protect against read in job_do_yield_locked */ + JOB_LOCK_GUARD(); + /* ensure the job is quiescent while the AioContext is changed */ + assert(job->paused || job_is_completed_locked(job)); + job->aio_context = ctx; +} + +/* Called with job_mutex *not* held. */ static void job_sleep_timer_cb(void *opaque) { Job *job = opaque; @@ -315,6 +388,8 @@ void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, { Job *job; + JOB_LOCK_GUARD(); + if (job_id) { if (flags & JOB_INTERNAL) { error_setg(errp, "Cannot specify job ID for internal job"); @@ -324,7 +399,7 @@ void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, error_setg(errp, "Invalid job ID '%s'", job_id); return NULL; } - if (job_get(job_id)) { + if (job_get_locked(job_id)) { error_setg(errp, "Job ID '%s' already in use", job_id); return NULL; } @@ -354,7 +429,7 @@ void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, notifier_list_init(&job->on_ready); notifier_list_init(&job->on_idle); - job_state_transition(job, JOB_STATUS_CREATED); + job_state_transition_locked(job, JOB_STATUS_CREATED); aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, QEMU_CLOCK_REALTIME, SCALE_NS, job_sleep_timer_cb, job); @@ -365,21 +440,21 @@ void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, * consolidating the job management logic */ if (!txn) { txn = job_txn_new(); - job_txn_add_job(txn, job); - job_txn_unref(txn); + job_txn_add_job_locked(txn, job); + job_txn_unref_locked(txn); } else { - job_txn_add_job(txn, job); + job_txn_add_job_locked(txn, job); } return job; } -void job_ref(Job *job) +void job_ref_locked(Job *job) { ++job->refcnt; } -void job_unref(Job *job) +void job_unref_locked(Job *job) { GLOBAL_STATE_CODE(); @@ -389,7 +464,9 @@ void job_unref(Job *job) assert(!job->txn); if (job->driver->free) { + job_unlock(); job->driver->free(job); + job_lock(); } QLIST_REMOVE(job, job_list); @@ -416,48 +493,56 @@ void job_progress_increase_remaining(Job *job, uint64_t delta) progress_increase_remaining(&job->progress, delta); } -void job_event_cancelled(Job *job) +/** + * To be called when a cancelled job is finalised. + * Called with job_mutex held. + */ +static void job_event_cancelled_locked(Job *job) { notifier_list_notify(&job->on_finalize_cancelled, job); } -void job_event_completed(Job *job) +/** + * To be called when a successfully completed job is finalised. + * Called with job_mutex held. + */ +static void job_event_completed_locked(Job *job) { notifier_list_notify(&job->on_finalize_completed, job); } -static void job_event_pending(Job *job) +/* Called with job_mutex held. */ +static void job_event_pending_locked(Job *job) { notifier_list_notify(&job->on_pending, job); } -static void job_event_ready(Job *job) +/* Called with job_mutex held. */ +static void job_event_ready_locked(Job *job) { notifier_list_notify(&job->on_ready, job); } -static void job_event_idle(Job *job) +/* Called with job_mutex held. */ +static void job_event_idle_locked(Job *job) { notifier_list_notify(&job->on_idle, job); } -void job_enter_cond(Job *job, bool(*fn)(Job *job)) +void job_enter_cond_locked(Job *job, bool(*fn)(Job *job)) { - if (!job_started(job)) { + if (!job_started_locked(job)) { return; } if (job->deferred_to_main_loop) { return; } - job_lock(); if (job->busy) { - job_unlock(); return; } if (fn && !fn(job)) { - job_unlock(); return; } @@ -465,12 +550,14 @@ void job_enter_cond(Job *job, bool(*fn)(Job *job)) timer_del(&job->sleep_timer); job->busy = true; job_unlock(); - aio_co_enter(job->aio_context, job->co); + aio_co_wake(job->co); + job_lock(); } void job_enter(Job *job) { - job_enter_cond(job, NULL); + JOB_LOCK_GUARD(); + job_enter_cond_locked(job, NULL); } /* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. @@ -478,100 +565,137 @@ void job_enter(Job *job) * is allowed and cancels the timer. * * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be - * called explicitly. */ -static void coroutine_fn job_do_yield(Job *job, uint64_t ns) + * called explicitly. + * + * Called with job_mutex held, but releases it temporarily. + */ +static void coroutine_fn job_do_yield_locked(Job *job, uint64_t ns) { - job_lock(); + AioContext *next_aio_context; + if (ns != -1) { timer_mod(&job->sleep_timer, ns); } job->busy = false; - job_event_idle(job); + job_event_idle_locked(job); job_unlock(); qemu_coroutine_yield(); + job_lock(); - /* Set by job_enter_cond() before re-entering the coroutine. */ + next_aio_context = job->aio_context; + /* + * Coroutine has resumed, but in the meanwhile the job AioContext + * might have changed via bdrv_try_change_aio_context(), so we need to move + * the coroutine too in the new aiocontext. + */ + while (qemu_get_current_aio_context() != next_aio_context) { + job_unlock(); + aio_co_reschedule_self(next_aio_context); + job_lock(); + next_aio_context = job->aio_context; + } + + /* Set by job_enter_cond_locked() before re-entering the coroutine. */ assert(job->busy); } -void coroutine_fn job_pause_point(Job *job) +/* Called with job_mutex held, but releases it temporarily. */ +static void coroutine_fn job_pause_point_locked(Job *job) { - assert(job && job_started(job)); + assert(job && job_started_locked(job)); - if (!job_should_pause(job)) { + if (!job_should_pause_locked(job)) { return; } - if (job_is_cancelled(job)) { + if (job_is_cancelled_locked(job)) { return; } if (job->driver->pause) { + job_unlock(); job->driver->pause(job); + job_lock(); } - if (job_should_pause(job) && !job_is_cancelled(job)) { + if (job_should_pause_locked(job) && !job_is_cancelled_locked(job)) { JobStatus status = job->status; - job_state_transition(job, status == JOB_STATUS_READY - ? JOB_STATUS_STANDBY - : JOB_STATUS_PAUSED); + job_state_transition_locked(job, status == JOB_STATUS_READY + ? JOB_STATUS_STANDBY + : JOB_STATUS_PAUSED); job->paused = true; - job_do_yield(job, -1); + job_do_yield_locked(job, -1); job->paused = false; - job_state_transition(job, status); + job_state_transition_locked(job, status); } if (job->driver->resume) { + job_unlock(); job->driver->resume(job); + job_lock(); } } -void job_yield(Job *job) +void coroutine_fn job_pause_point(Job *job) { + JOB_LOCK_GUARD(); + job_pause_point_locked(job); +} + +void coroutine_fn job_yield(Job *job) +{ + JOB_LOCK_GUARD(); assert(job->busy); /* Check cancellation *before* setting busy = false, too! */ - if (job_is_cancelled(job)) { + if (job_is_cancelled_locked(job)) { return; } - if (!job_should_pause(job)) { - job_do_yield(job, -1); + if (!job_should_pause_locked(job)) { + job_do_yield_locked(job, -1); } - job_pause_point(job); + job_pause_point_locked(job); } void coroutine_fn job_sleep_ns(Job *job, int64_t ns) { + JOB_LOCK_GUARD(); assert(job->busy); /* Check cancellation *before* setting busy = false, too! */ - if (job_is_cancelled(job)) { + if (job_is_cancelled_locked(job)) { return; } - if (!job_should_pause(job)) { - job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); + if (!job_should_pause_locked(job)) { + job_do_yield_locked(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); } - job_pause_point(job); + job_pause_point_locked(job); } -/* Assumes the block_job_mutex is held */ -static bool job_timer_not_pending(Job *job) +/* Assumes the job_mutex is held */ +static bool job_timer_not_pending_locked(Job *job) { return !timer_pending(&job->sleep_timer); } -void job_pause(Job *job) +void job_pause_locked(Job *job) { job->pause_count++; if (!job->paused) { - job_enter(job); + job_enter_cond_locked(job, NULL); } } -void job_resume(Job *job) +void job_pause(Job *job) +{ + JOB_LOCK_GUARD(); + job_pause_locked(job); +} + +void job_resume_locked(Job *job) { assert(job->pause_count > 0); job->pause_count--; @@ -580,12 +704,18 @@ void job_resume(Job *job) } /* kick only if no timer is pending */ - job_enter_cond(job, job_timer_not_pending); + job_enter_cond_locked(job, job_timer_not_pending_locked); } -void job_user_pause(Job *job, Error **errp) +void job_resume(Job *job) { - if (job_apply_verb(job, JOB_VERB_PAUSE, errp)) { + JOB_LOCK_GUARD(); + job_resume_locked(job); +} + +void job_user_pause_locked(Job *job, Error **errp) +{ + if (job_apply_verb_locked(job, JOB_VERB_PAUSE, errp)) { return; } if (job->user_paused) { @@ -593,15 +723,15 @@ void job_user_pause(Job *job, Error **errp) return; } job->user_paused = true; - job_pause(job); + job_pause_locked(job); } -bool job_user_paused(Job *job) +bool job_user_paused_locked(Job *job) { return job->user_paused; } -void job_user_resume(Job *job, Error **errp) +void job_user_resume_locked(Job *job, Error **errp) { assert(job); GLOBAL_STATE_CODE(); @@ -609,66 +739,72 @@ void job_user_resume(Job *job, Error **errp) error_setg(errp, "Can't resume a job that was not paused"); return; } - if (job_apply_verb(job, JOB_VERB_RESUME, errp)) { + if (job_apply_verb_locked(job, JOB_VERB_RESUME, errp)) { return; } if (job->driver->user_resume) { + job_unlock(); job->driver->user_resume(job); + job_lock(); } job->user_paused = false; - job_resume(job); + job_resume_locked(job); } -static void job_do_dismiss(Job *job) +/* Called with job_mutex held, but releases it temporarily. */ +static void job_do_dismiss_locked(Job *job) { assert(job); job->busy = false; job->paused = false; job->deferred_to_main_loop = true; - job_txn_del_job(job); + job_txn_del_job_locked(job); - job_state_transition(job, JOB_STATUS_NULL); - job_unref(job); + job_state_transition_locked(job, JOB_STATUS_NULL); + job_unref_locked(job); } -void job_dismiss(Job **jobptr, Error **errp) +void job_dismiss_locked(Job **jobptr, Error **errp) { Job *job = *jobptr; /* similarly to _complete, this is QMP-interface only. */ assert(job->id); - if (job_apply_verb(job, JOB_VERB_DISMISS, errp)) { + if (job_apply_verb_locked(job, JOB_VERB_DISMISS, errp)) { return; } - job_do_dismiss(job); + job_do_dismiss_locked(job); *jobptr = NULL; } void job_early_fail(Job *job) { + JOB_LOCK_GUARD(); assert(job->status == JOB_STATUS_CREATED); - job_do_dismiss(job); + job_do_dismiss_locked(job); } -static void job_conclude(Job *job) +/* Called with job_mutex held. */ +static void job_conclude_locked(Job *job) { - job_state_transition(job, JOB_STATUS_CONCLUDED); - if (job->auto_dismiss || !job_started(job)) { - job_do_dismiss(job); + job_state_transition_locked(job, JOB_STATUS_CONCLUDED); + if (job->auto_dismiss || !job_started_locked(job)) { + job_do_dismiss_locked(job); } } -static void job_update_rc(Job *job) +/* Called with job_mutex held. */ +static void job_update_rc_locked(Job *job) { - if (!job->ret && job_is_cancelled(job)) { + if (!job->ret && job_is_cancelled_locked(job)) { job->ret = -ECANCELED; } if (job->ret) { if (!job->err) { error_setg(&job->err, "%s", strerror(-job->ret)); } - job_state_transition(job, JOB_STATUS_ABORTING); + job_state_transition_locked(job, JOB_STATUS_ABORTING); } } @@ -698,14 +834,22 @@ static void job_clean(Job *job) } } -static int job_finalize_single(Job *job) +/* + * Called with job_mutex held, but releases it temporarily. + */ +static int job_finalize_single_locked(Job *job) { - assert(job_is_completed(job)); + int job_ret; + + assert(job_is_completed_locked(job)); /* Ensure abort is called for late-transactional failures */ - job_update_rc(job); + job_update_rc_locked(job); - if (!job->ret) { + job_ret = job->ret; + job_unlock(); + + if (!job_ret) { job_commit(job); } else { job_abort(job); @@ -713,28 +857,35 @@ static int job_finalize_single(Job *job) job_clean(job); if (job->cb) { - job->cb(job->opaque, job->ret); + job->cb(job->opaque, job_ret); } + job_lock(); + /* Emit events only if we actually started */ - if (job_started(job)) { - if (job_is_cancelled(job)) { - job_event_cancelled(job); + if (job_started_locked(job)) { + if (job_is_cancelled_locked(job)) { + job_event_cancelled_locked(job); } else { - job_event_completed(job); + job_event_completed_locked(job); } } - job_txn_del_job(job); - job_conclude(job); + job_txn_del_job_locked(job); + job_conclude_locked(job); return 0; } -static void job_cancel_async(Job *job, bool force) +/* + * Called with job_mutex held, but releases it temporarily. + */ +static void job_cancel_async_locked(Job *job, bool force) { GLOBAL_STATE_CODE(); if (job->driver->cancel) { + job_unlock(); force = job->driver->cancel(job, force); + job_lock(); } else { /* No .cancel() means the job will behave as if force-cancelled */ force = true; @@ -743,7 +894,9 @@ static void job_cancel_async(Job *job, bool force) if (job->user_paused) { /* Do not call job_enter here, the caller will handle it. */ if (job->driver->user_resume) { + job_unlock(); job->driver->user_resume(job); + job_lock(); } job->user_paused = false; assert(job->pause_count > 0); @@ -764,9 +917,11 @@ static void job_cancel_async(Job *job, bool force) } } -static void job_completed_txn_abort(Job *job) +/* + * Called with job_mutex held, but releases it temporarily. + */ +static void job_completed_txn_abort_locked(Job *job) { - AioContext *ctx; JobTxn *txn = job->txn; Job *other_job; @@ -777,178 +932,161 @@ static void job_completed_txn_abort(Job *job) return; } txn->aborting = true; - job_txn_ref(txn); + job_txn_ref_locked(txn); - /* - * We can only hold the single job's AioContext lock while calling - * job_finalize_single() because the finalization callbacks can involve - * calls of AIO_WAIT_WHILE(), which could deadlock otherwise. - * Note that the job's AioContext may change when it is finalized. - */ - job_ref(job); - aio_context_release(job->aio_context); + job_ref_locked(job); /* Other jobs are effectively cancelled by us, set the status for * them; this job, however, may or may not be cancelled, depending * on the caller, so leave it. */ QLIST_FOREACH(other_job, &txn->jobs, txn_list) { if (other_job != job) { - ctx = other_job->aio_context; - aio_context_acquire(ctx); /* * This is a transaction: If one job failed, no result will matter. * Therefore, pass force=true to terminate all other jobs as quickly * as possible. */ - job_cancel_async(other_job, true); - aio_context_release(ctx); + job_cancel_async_locked(other_job, true); } } while (!QLIST_EMPTY(&txn->jobs)) { other_job = QLIST_FIRST(&txn->jobs); - /* - * The job's AioContext may change, so store it in @ctx so we - * release the same context that we have acquired before. - */ - ctx = other_job->aio_context; - aio_context_acquire(ctx); - if (!job_is_completed(other_job)) { - assert(job_cancel_requested(other_job)); - job_finish_sync(other_job, NULL, NULL); + if (!job_is_completed_locked(other_job)) { + assert(job_cancel_requested_locked(other_job)); + job_finish_sync_locked(other_job, NULL, NULL); } - job_finalize_single(other_job); - aio_context_release(ctx); + job_finalize_single_locked(other_job); } - /* - * Use job_ref()/job_unref() so we can read the AioContext here - * even if the job went away during job_finalize_single(). - */ - aio_context_acquire(job->aio_context); - job_unref(job); - - job_txn_unref(txn); + job_unref_locked(job); + job_txn_unref_locked(txn); } -static int job_prepare(Job *job) +/* Called with job_mutex held, but releases it temporarily */ +static int job_prepare_locked(Job *job) { + int ret; + GLOBAL_STATE_CODE(); + if (job->ret == 0 && job->driver->prepare) { - job->ret = job->driver->prepare(job); - job_update_rc(job); + job_unlock(); + ret = job->driver->prepare(job); + job_lock(); + job->ret = ret; + job_update_rc_locked(job); } + return job->ret; } -static int job_needs_finalize(Job *job) +/* Called with job_mutex held */ +static int job_needs_finalize_locked(Job *job) { return !job->auto_finalize; } -static void job_do_finalize(Job *job) +/* Called with job_mutex held */ +static void job_do_finalize_locked(Job *job) { int rc; assert(job && job->txn); /* prepare the transaction to complete */ - rc = job_txn_apply(job, job_prepare); + rc = job_txn_apply_locked(job, job_prepare_locked); if (rc) { - job_completed_txn_abort(job); + job_completed_txn_abort_locked(job); } else { - job_txn_apply(job, job_finalize_single); + job_txn_apply_locked(job, job_finalize_single_locked); } } -void job_finalize(Job *job, Error **errp) +void job_finalize_locked(Job *job, Error **errp) { assert(job && job->id); - if (job_apply_verb(job, JOB_VERB_FINALIZE, errp)) { + if (job_apply_verb_locked(job, JOB_VERB_FINALIZE, errp)) { return; } - job_do_finalize(job); + job_do_finalize_locked(job); } -static int job_transition_to_pending(Job *job) +/* Called with job_mutex held. */ +static int job_transition_to_pending_locked(Job *job) { - job_state_transition(job, JOB_STATUS_PENDING); + job_state_transition_locked(job, JOB_STATUS_PENDING); if (!job->auto_finalize) { - job_event_pending(job); + job_event_pending_locked(job); } return 0; } void job_transition_to_ready(Job *job) { - job_state_transition(job, JOB_STATUS_READY); - job_event_ready(job); + JOB_LOCK_GUARD(); + job_state_transition_locked(job, JOB_STATUS_READY); + job_event_ready_locked(job); } -static void job_completed_txn_success(Job *job) +/* Called with job_mutex held. */ +static void job_completed_txn_success_locked(Job *job) { JobTxn *txn = job->txn; Job *other_job; - job_state_transition(job, JOB_STATUS_WAITING); + job_state_transition_locked(job, JOB_STATUS_WAITING); /* * Successful completion, see if there are other running jobs in this * txn. */ QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - if (!job_is_completed(other_job)) { + if (!job_is_completed_locked(other_job)) { return; } assert(other_job->ret == 0); } - job_txn_apply(job, job_transition_to_pending); + job_txn_apply_locked(job, job_transition_to_pending_locked); /* If no jobs need manual finalization, automatically do so */ - if (job_txn_apply(job, job_needs_finalize) == 0) { - job_do_finalize(job); + if (job_txn_apply_locked(job, job_needs_finalize_locked) == 0) { + job_do_finalize_locked(job); } } -static void job_completed(Job *job) +/* Called with job_mutex held. */ +static void job_completed_locked(Job *job) { - assert(job && job->txn && !job_is_completed(job)); + assert(job && job->txn && !job_is_completed_locked(job)); - job_update_rc(job); + job_update_rc_locked(job); trace_job_completed(job, job->ret); if (job->ret) { - job_completed_txn_abort(job); + job_completed_txn_abort_locked(job); } else { - job_completed_txn_success(job); + job_completed_txn_success_locked(job); } } -/** Useful only as a type shim for aio_bh_schedule_oneshot. */ +/** + * Useful only as a type shim for aio_bh_schedule_oneshot. + * Called with job_mutex *not* held. + */ static void job_exit(void *opaque) { Job *job = (Job *)opaque; - AioContext *ctx; - - job_ref(job); - aio_context_acquire(job->aio_context); + JOB_LOCK_GUARD(); + job_ref_locked(job); /* This is a lie, we're not quiescent, but still doing the completion * callbacks. However, completion callbacks tend to involve operations that * drain block nodes, and if .drained_poll still returned true, we would * deadlock. */ job->busy = false; - job_event_idle(job); + job_event_idle_locked(job); - 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); + job_completed_locked(job); + job_unref_locked(job); } /** @@ -958,37 +1096,47 @@ static void job_exit(void *opaque) static void coroutine_fn job_co_entry(void *opaque) { Job *job = opaque; + int ret; assert(job && job->driver && job->driver->run); - assert(job->aio_context == qemu_get_current_aio_context()); - job_pause_point(job); - job->ret = job->driver->run(job, &job->err); - job->deferred_to_main_loop = true; - job->busy = true; + WITH_JOB_LOCK_GUARD() { + assert(job->aio_context == qemu_get_current_aio_context()); + job_pause_point_locked(job); + } + ret = job->driver->run(job, &job->err); + WITH_JOB_LOCK_GUARD() { + job->ret = ret; + job->deferred_to_main_loop = true; + job->busy = true; + } aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job); } void job_start(Job *job) { - assert(job && !job_started(job) && job->paused && - job->driver && job->driver->run); - job->co = qemu_coroutine_create(job_co_entry, job); - job->pause_count--; - job->busy = true; - job->paused = false; - job_state_transition(job, JOB_STATUS_RUNNING); + assert(qemu_in_main_thread()); + + WITH_JOB_LOCK_GUARD() { + assert(job && !job_started_locked(job) && job->paused && + job->driver && job->driver->run); + job->co = qemu_coroutine_create(job_co_entry, job); + job->pause_count--; + job->busy = true; + job->paused = false; + job_state_transition_locked(job, JOB_STATUS_RUNNING); + } aio_co_enter(job->aio_context, job->co); } -void job_cancel(Job *job, bool force) +void job_cancel_locked(Job *job, bool force) { if (job->status == JOB_STATUS_CONCLUDED) { - job_do_dismiss(job); + job_do_dismiss_locked(job); return; } - job_cancel_async(job, force); - if (!job_started(job)) { - job_completed(job); + job_cancel_async_locked(job, force); + if (!job_started_locked(job)) { + job_completed_locked(job); } else if (job->deferred_to_main_loop) { /* * job_cancel_async() ignores soft-cancel requests for jobs @@ -1000,102 +1148,117 @@ void job_cancel(Job *job, bool force) * choose to call job_is_cancelled() to show that we invoke * job_completed_txn_abort() only for force-cancelled jobs.) */ - if (job_is_cancelled(job)) { - job_completed_txn_abort(job); + if (job_is_cancelled_locked(job)) { + job_completed_txn_abort_locked(job); } } else { - job_enter(job); + job_enter_cond_locked(job, NULL); } } -void job_user_cancel(Job *job, bool force, Error **errp) +void job_user_cancel_locked(Job *job, bool force, Error **errp) { - if (job_apply_verb(job, JOB_VERB_CANCEL, errp)) { + if (job_apply_verb_locked(job, JOB_VERB_CANCEL, errp)) { return; } - job_cancel(job, force); + job_cancel_locked(job, force); } -/* A wrapper around job_cancel() taking an Error ** parameter so it may be - * used with job_finish_sync() without the need for (rather nasty) function - * pointer casts there. */ -static void job_cancel_err(Job *job, Error **errp) +/* A wrapper around job_cancel_locked() taking an Error ** parameter so it may + * be used with job_finish_sync_locked() without the need for (rather nasty) + * function pointer casts there. + * + * Called with job_mutex held. + */ +static void job_cancel_err_locked(Job *job, Error **errp) { - job_cancel(job, false); + job_cancel_locked(job, false); } /** * Same as job_cancel_err(), but force-cancel. + * Called with job_mutex held. */ -static void job_force_cancel_err(Job *job, Error **errp) +static void job_force_cancel_err_locked(Job *job, Error **errp) { - job_cancel(job, true); + job_cancel_locked(job, true); +} + +int job_cancel_sync_locked(Job *job, bool force) +{ + if (force) { + return job_finish_sync_locked(job, &job_force_cancel_err_locked, NULL); + } else { + return job_finish_sync_locked(job, &job_cancel_err_locked, NULL); + } } int job_cancel_sync(Job *job, bool force) { - if (force) { - return job_finish_sync(job, &job_force_cancel_err, NULL); - } else { - return job_finish_sync(job, &job_cancel_err, NULL); - } + JOB_LOCK_GUARD(); + return job_cancel_sync_locked(job, force); } void job_cancel_sync_all(void) { Job *job; - AioContext *aio_context; + JOB_LOCK_GUARD(); - while ((job = job_next(NULL))) { - aio_context = job->aio_context; - aio_context_acquire(aio_context); - job_cancel_sync(job, true); - aio_context_release(aio_context); + while ((job = job_next_locked(NULL))) { + job_cancel_sync_locked(job, true); } } -int job_complete_sync(Job *job, Error **errp) +int job_complete_sync_locked(Job *job, Error **errp) { - return job_finish_sync(job, job_complete, errp); + return job_finish_sync_locked(job, job_complete_locked, errp); } -void job_complete(Job *job, Error **errp) +void job_complete_locked(Job *job, Error **errp) { /* Should not be reachable via external interface for internal jobs */ assert(job->id); GLOBAL_STATE_CODE(); - if (job_apply_verb(job, JOB_VERB_COMPLETE, errp)) { + if (job_apply_verb_locked(job, JOB_VERB_COMPLETE, errp)) { return; } - if (job_cancel_requested(job) || !job->driver->complete) { + if (job_cancel_requested_locked(job) || !job->driver->complete) { error_setg(errp, "The active block job '%s' cannot be completed", job->id); return; } + job_unlock(); job->driver->complete(job, errp); + job_lock(); } -int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) +int job_finish_sync_locked(Job *job, + void (*finish)(Job *, Error **errp), + Error **errp) { Error *local_err = NULL; int ret; + GLOBAL_STATE_CODE(); - job_ref(job); + job_ref_locked(job); if (finish) { finish(job, &local_err); } if (local_err) { error_propagate(errp, local_err); - job_unref(job); + job_unref_locked(job); return -EBUSY; } - AIO_WAIT_WHILE(job->aio_context, - (job_enter(job), !job_is_completed(job))); + job_unlock(); + AIO_WAIT_WHILE_UNLOCKED(job->aio_context, + (job_enter(job), !job_is_completed(job))); + job_lock(); - ret = (job_is_cancelled(job) && job->ret == 0) ? -ECANCELED : job->ret; - job_unref(job); + ret = (job_is_cancelled_locked(job) && job->ret == 0) + ? -ECANCELED : job->ret; + job_unref_locked(job); return ret; } diff --git a/libdecnumber/dpd/decimal64.c b/libdecnumber/dpd/decimal64.c index 4816176410..290dbe8177 100644 --- a/libdecnumber/dpd/decimal64.c +++ b/libdecnumber/dpd/decimal64.c @@ -617,7 +617,6 @@ static const uInt multies[]={131073, 26215, 5243, 1049, 210}; #endif void decDigitsToDPD(const decNumber *dn, uInt *targ, Int shift) { Int cut; /* work */ - Int n; /* output bunch counter */ Int digits=dn->digits; /* digit countdown */ uInt dpd; /* densely packed decimal value */ uInt bin; /* binary value 0-999 */ @@ -676,7 +675,7 @@ void decDigitsToDPD(const decNumber *dn, uInt *targ, Int shift) { bin=0; /* [keep compiler quiet] */ #endif - for(n=0; digits>0; n++) { /* each output bunch */ + while (digits > 0) { /* each output bunch */ #if DECDPUN==3 /* fast path, 3-at-a-time */ bin=*inu; /* 3 digits ready for convert */ digits-=3; /* [may go negative] */ diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index 5c28a9737a..c59ea55cd8 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -43,6 +43,7 @@ #define __KVM_HAVE_VCPU_EVENTS #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 +#define KVM_DIRTY_LOG_PAGE_OFFSET 64 #define KVM_REG_SIZE(id) \ (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) @@ -75,9 +76,11 @@ struct kvm_regs { /* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */ #define KVM_ARM_DEVICE_TYPE_SHIFT 0 -#define KVM_ARM_DEVICE_TYPE_MASK (0xffff << KVM_ARM_DEVICE_TYPE_SHIFT) +#define KVM_ARM_DEVICE_TYPE_MASK GENMASK(KVM_ARM_DEVICE_TYPE_SHIFT + 15, \ + KVM_ARM_DEVICE_TYPE_SHIFT) #define KVM_ARM_DEVICE_ID_SHIFT 16 -#define KVM_ARM_DEVICE_ID_MASK (0xffff << KVM_ARM_DEVICE_ID_SHIFT) +#define KVM_ARM_DEVICE_ID_MASK GENMASK(KVM_ARM_DEVICE_ID_SHIFT + 15, \ + KVM_ARM_DEVICE_ID_SHIFT) /* Supported device IDs */ #define KVM_ARM_DEVICE_VGIC_V2 0 @@ -106,6 +109,7 @@ struct kvm_regs { #define KVM_ARM_VCPU_SVE 4 /* enable SVE for this CPU */ #define KVM_ARM_VCPU_PTRAUTH_ADDRESS 5 /* VCPU uses address authentication */ #define KVM_ARM_VCPU_PTRAUTH_GENERIC 6 /* VCPU uses generic authentication */ +#define KVM_ARM_VCPU_HAS_EL2 7 /* Support nested virtualization */ struct kvm_vcpu_init { __u32 target; @@ -139,8 +143,10 @@ struct kvm_guest_debug_arch { __u64 dbg_wvr[KVM_ARM_MAX_DBG_REGS]; }; +#define KVM_DEBUG_ARCH_HSR_HIGH_VALID (1 << 0) struct kvm_debug_exit_arch { __u32 hsr; + __u32 hsr_high; /* ESR_EL2[61:32] */ __u64 far; /* used for watchpoints */ }; @@ -192,6 +198,15 @@ struct kvm_arm_copy_mte_tags { __u64 reserved[2]; }; +/* + * Counter/Timer offset structure. Describe the virtual/physical offset. + * To be used with KVM_ARM_SET_COUNTER_OFFSET. + */ +struct kvm_arm_counter_offset { + __u64 counter_offset; + __u64 reserved; +}; + #define KVM_ARM_TAGS_TO_GUEST 0 #define KVM_ARM_TAGS_FROM_GUEST 1 @@ -332,6 +347,35 @@ struct kvm_arm_copy_mte_tags { #define KVM_ARM64_SVE_VLS_WORDS \ ((KVM_ARM64_SVE_VQ_MAX - KVM_ARM64_SVE_VQ_MIN) / 64 + 1) +/* Bitmap feature firmware registers */ +#define KVM_REG_ARM_FW_FEAT_BMAP (0x0016 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_FW_FEAT_BMAP_REG(r) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ + KVM_REG_ARM_FW_FEAT_BMAP | \ + ((r) & 0xffff)) + +#define KVM_REG_ARM_STD_BMAP KVM_REG_ARM_FW_FEAT_BMAP_REG(0) + +enum { + KVM_REG_ARM_STD_BIT_TRNG_V1_0 = 0, +}; + +#define KVM_REG_ARM_STD_HYP_BMAP KVM_REG_ARM_FW_FEAT_BMAP_REG(1) + +enum { + KVM_REG_ARM_STD_HYP_BIT_PV_TIME = 0, +}; + +#define KVM_REG_ARM_VENDOR_HYP_BMAP KVM_REG_ARM_FW_FEAT_BMAP_REG(2) + +enum { + KVM_REG_ARM_VENDOR_HYP_BIT_FUNC_FEAT = 0, + KVM_REG_ARM_VENDOR_HYP_BIT_PTP = 1, +}; + +/* Device Control API on vm fd */ +#define KVM_ARM_VM_SMCCC_CTRL 0 +#define KVM_ARM_VM_SMCCC_FILTER 0 + /* Device Control API: ARM VGIC */ #define KVM_DEV_ARM_VGIC_GRP_ADDR 0 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1 @@ -371,6 +415,8 @@ struct kvm_arm_copy_mte_tags { #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_TIMER_IRQ_HVTIMER 2 +#define KVM_ARM_VCPU_TIMER_IRQ_HPTIMER 3 #define KVM_ARM_VCPU_PVTIME_CTRL 2 #define KVM_ARM_VCPU_PVTIME_IPA 0 @@ -427,6 +473,56 @@ struct kvm_arm_copy_mte_tags { /* run->fail_entry.hardware_entry_failure_reason codes. */ #define KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED (1ULL << 0) +enum kvm_smccc_filter_action { + KVM_SMCCC_FILTER_HANDLE = 0, + KVM_SMCCC_FILTER_DENY, + KVM_SMCCC_FILTER_FWD_TO_USER, + +}; + +struct kvm_smccc_filter { + __u32 base; + __u32 nr_functions; + __u8 action; + __u8 pad[15]; +}; + +/* arm64-specific KVM_EXIT_HYPERCALL flags */ +#define KVM_HYPERCALL_EXIT_SMC (1U << 0) +#define KVM_HYPERCALL_EXIT_16BIT (1U << 1) + +/* + * Get feature ID registers userspace writable mask. + * + * From DDI0487J.a, D19.2.66 ("ID_AA64MMFR2_EL1, AArch64 Memory Model + * Feature Register 2"): + * + * "The Feature ID space is defined as the System register space in + * AArch64 with op0==3, op1=={0, 1, 3}, CRn==0, CRm=={0-7}, + * op2=={0-7}." + * + * This covers all currently known R/O registers that indicate + * anything useful feature wise, including the ID registers. + * + * If we ever need to introduce a new range, it will be described as + * such in the range field. + */ +#define KVM_ARM_FEATURE_ID_RANGE_IDX(op0, op1, crn, crm, op2) \ + ({ \ + __u64 __op1 = (op1) & 3; \ + __op1 -= (__op1 == 3); \ + (__op1 << 6 | ((crm) & 7) << 3 | (op2)); \ + }) + +#define KVM_ARM_FEATURE_ID_RANGE 0 +#define KVM_ARM_FEATURE_ID_RANGE_SIZE (3 * 8 * 8) + +struct reg_mask_range { + __u64 addr; /* Pointer to mask array */ + __u32 range; /* Requested range */ + __u32 reserved[13]; +}; + #endif #endif /* __ARM_KVM_H__ */ diff --git a/linux-headers/asm-generic/bitsperlong.h b/linux-headers/asm-generic/bitsperlong.h index 0aac245b6b..75f320fa91 100644 --- a/linux-headers/asm-generic/bitsperlong.h +++ b/linux-headers/asm-generic/bitsperlong.h @@ -2,6 +2,17 @@ #ifndef __ASM_GENERIC_BITS_PER_LONG #define __ASM_GENERIC_BITS_PER_LONG +#ifndef __BITS_PER_LONG +/* + * In order to keep safe and avoid regression, only unify uapi + * bitsperlong.h for some archs which are using newer toolchains + * that have the definitions of __CHAR_BIT__ and __SIZEOF_LONG__. + * See the following link for more info: + * https://lore.kernel.org/linux-arch/b9624545-2c80-49a1-ac3c-39264a591f7b@app.fastmail.com/ + */ +#if defined(__CHAR_BIT__) && defined(__SIZEOF_LONG__) +#define __BITS_PER_LONG (__CHAR_BIT__ * __SIZEOF_LONG__) +#else /* * There seems to be no way of detecting this automatically from user * space, so 64 bit architectures should override this in their @@ -9,8 +20,8 @@ * both 32 and 64 bit user space must not rely on CONFIG_64BIT * to decide it, but rather check a compiler provided macro. */ -#ifndef __BITS_PER_LONG #define __BITS_PER_LONG 32 #endif +#endif #endif /* __ASM_GENERIC_BITS_PER_LONG */ diff --git a/linux-headers/asm-generic/hugetlb_encode.h b/linux-headers/asm-generic/hugetlb_encode.h index 4f3d5aaa11..de687009bf 100644 --- a/linux-headers/asm-generic/hugetlb_encode.h +++ b/linux-headers/asm-generic/hugetlb_encode.h @@ -20,18 +20,18 @@ #define HUGETLB_FLAG_ENCODE_SHIFT 26 #define HUGETLB_FLAG_ENCODE_MASK 0x3f -#define HUGETLB_FLAG_ENCODE_16KB (14 << HUGETLB_FLAG_ENCODE_SHIFT) -#define HUGETLB_FLAG_ENCODE_64KB (16 << HUGETLB_FLAG_ENCODE_SHIFT) -#define HUGETLB_FLAG_ENCODE_512KB (19 << HUGETLB_FLAG_ENCODE_SHIFT) -#define HUGETLB_FLAG_ENCODE_1MB (20 << HUGETLB_FLAG_ENCODE_SHIFT) -#define HUGETLB_FLAG_ENCODE_2MB (21 << HUGETLB_FLAG_ENCODE_SHIFT) -#define HUGETLB_FLAG_ENCODE_8MB (23 << HUGETLB_FLAG_ENCODE_SHIFT) -#define HUGETLB_FLAG_ENCODE_16MB (24 << HUGETLB_FLAG_ENCODE_SHIFT) -#define HUGETLB_FLAG_ENCODE_32MB (25 << HUGETLB_FLAG_ENCODE_SHIFT) -#define HUGETLB_FLAG_ENCODE_256MB (28 << HUGETLB_FLAG_ENCODE_SHIFT) -#define HUGETLB_FLAG_ENCODE_512MB (29 << HUGETLB_FLAG_ENCODE_SHIFT) -#define HUGETLB_FLAG_ENCODE_1GB (30 << HUGETLB_FLAG_ENCODE_SHIFT) -#define HUGETLB_FLAG_ENCODE_2GB (31 << HUGETLB_FLAG_ENCODE_SHIFT) -#define HUGETLB_FLAG_ENCODE_16GB (34 << HUGETLB_FLAG_ENCODE_SHIFT) +#define HUGETLB_FLAG_ENCODE_16KB (14U << HUGETLB_FLAG_ENCODE_SHIFT) +#define HUGETLB_FLAG_ENCODE_64KB (16U << HUGETLB_FLAG_ENCODE_SHIFT) +#define HUGETLB_FLAG_ENCODE_512KB (19U << HUGETLB_FLAG_ENCODE_SHIFT) +#define HUGETLB_FLAG_ENCODE_1MB (20U << HUGETLB_FLAG_ENCODE_SHIFT) +#define HUGETLB_FLAG_ENCODE_2MB (21U << HUGETLB_FLAG_ENCODE_SHIFT) +#define HUGETLB_FLAG_ENCODE_8MB (23U << HUGETLB_FLAG_ENCODE_SHIFT) +#define HUGETLB_FLAG_ENCODE_16MB (24U << HUGETLB_FLAG_ENCODE_SHIFT) +#define HUGETLB_FLAG_ENCODE_32MB (25U << HUGETLB_FLAG_ENCODE_SHIFT) +#define HUGETLB_FLAG_ENCODE_256MB (28U << HUGETLB_FLAG_ENCODE_SHIFT) +#define HUGETLB_FLAG_ENCODE_512MB (29U << HUGETLB_FLAG_ENCODE_SHIFT) +#define HUGETLB_FLAG_ENCODE_1GB (30U << HUGETLB_FLAG_ENCODE_SHIFT) +#define HUGETLB_FLAG_ENCODE_2GB (31U << HUGETLB_FLAG_ENCODE_SHIFT) +#define HUGETLB_FLAG_ENCODE_16GB (34U << HUGETLB_FLAG_ENCODE_SHIFT) #endif /* _ASM_GENERIC_HUGETLB_ENCODE_H_ */ diff --git a/linux-headers/asm-generic/mman-common.h b/linux-headers/asm-generic/mman-common.h index 6c1aa92a92..6ce1f1ceb4 100644 --- a/linux-headers/asm-generic/mman-common.h +++ b/linux-headers/asm-generic/mman-common.h @@ -77,6 +77,8 @@ #define MADV_DONTNEED_LOCKED 24 /* like DONTNEED, but drop locked pages too */ +#define MADV_COLLAPSE 25 /* Synchronous hugepage collapse */ + /* compatibility flags */ #define MAP_FILE 0 diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h index 1c48b0ae3b..75f00965ab 100644 --- a/linux-headers/asm-generic/unistd.h +++ b/linux-headers/asm-generic/unistd.h @@ -38,12 +38,12 @@ __SYSCALL(__NR_io_destroy, sys_io_destroy) __SC_COMP(__NR_io_submit, sys_io_submit, compat_sys_io_submit) #define __NR_io_cancel 3 __SYSCALL(__NR_io_cancel, sys_io_cancel) + #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_io_getevents 4 __SC_3264(__NR_io_getevents, sys_io_getevents_time32, sys_io_getevents) #endif -/* fs/xattr.c */ #define __NR_setxattr 5 __SYSCALL(__NR_setxattr, sys_setxattr) #define __NR_lsetxattr 6 @@ -68,58 +68,38 @@ __SYSCALL(__NR_removexattr, sys_removexattr) __SYSCALL(__NR_lremovexattr, sys_lremovexattr) #define __NR_fremovexattr 16 __SYSCALL(__NR_fremovexattr, sys_fremovexattr) - -/* fs/dcache.c */ #define __NR_getcwd 17 __SYSCALL(__NR_getcwd, sys_getcwd) - -/* fs/cookies.c */ #define __NR_lookup_dcookie 18 -__SC_COMP(__NR_lookup_dcookie, sys_lookup_dcookie, compat_sys_lookup_dcookie) - -/* fs/eventfd.c */ +__SYSCALL(__NR_lookup_dcookie, sys_ni_syscall) #define __NR_eventfd2 19 __SYSCALL(__NR_eventfd2, sys_eventfd2) - -/* fs/eventpoll.c */ #define __NR_epoll_create1 20 __SYSCALL(__NR_epoll_create1, sys_epoll_create1) #define __NR_epoll_ctl 21 __SYSCALL(__NR_epoll_ctl, sys_epoll_ctl) #define __NR_epoll_pwait 22 __SC_COMP(__NR_epoll_pwait, sys_epoll_pwait, compat_sys_epoll_pwait) - -/* fs/fcntl.c */ #define __NR_dup 23 __SYSCALL(__NR_dup, sys_dup) #define __NR_dup3 24 __SYSCALL(__NR_dup3, sys_dup3) #define __NR3264_fcntl 25 __SC_COMP_3264(__NR3264_fcntl, sys_fcntl64, sys_fcntl, compat_sys_fcntl64) - -/* fs/inotify_user.c */ #define __NR_inotify_init1 26 __SYSCALL(__NR_inotify_init1, sys_inotify_init1) #define __NR_inotify_add_watch 27 __SYSCALL(__NR_inotify_add_watch, sys_inotify_add_watch) #define __NR_inotify_rm_watch 28 __SYSCALL(__NR_inotify_rm_watch, sys_inotify_rm_watch) - -/* fs/ioctl.c */ #define __NR_ioctl 29 __SC_COMP(__NR_ioctl, sys_ioctl, compat_sys_ioctl) - -/* fs/ioprio.c */ #define __NR_ioprio_set 30 __SYSCALL(__NR_ioprio_set, sys_ioprio_set) #define __NR_ioprio_get 31 __SYSCALL(__NR_ioprio_get, sys_ioprio_get) - -/* fs/locks.c */ #define __NR_flock 32 __SYSCALL(__NR_flock, sys_flock) - -/* fs/namei.c */ #define __NR_mknodat 33 __SYSCALL(__NR_mknodat, sys_mknodat) #define __NR_mkdirat 34 @@ -130,25 +110,21 @@ __SYSCALL(__NR_unlinkat, sys_unlinkat) __SYSCALL(__NR_symlinkat, sys_symlinkat) #define __NR_linkat 37 __SYSCALL(__NR_linkat, sys_linkat) + #ifdef __ARCH_WANT_RENAMEAT /* renameat is superseded with flags by renameat2 */ #define __NR_renameat 38 __SYSCALL(__NR_renameat, sys_renameat) #endif /* __ARCH_WANT_RENAMEAT */ -/* fs/namespace.c */ #define __NR_umount2 39 __SYSCALL(__NR_umount2, sys_umount) #define __NR_mount 40 __SYSCALL(__NR_mount, sys_mount) #define __NR_pivot_root 41 __SYSCALL(__NR_pivot_root, sys_pivot_root) - -/* fs/nfsctl.c */ #define __NR_nfsservctl 42 __SYSCALL(__NR_nfsservctl, sys_ni_syscall) - -/* fs/open.c */ #define __NR3264_statfs 43 __SC_COMP_3264(__NR3264_statfs, sys_statfs64, sys_statfs, \ compat_sys_statfs64) @@ -161,7 +137,6 @@ __SC_COMP_3264(__NR3264_truncate, sys_truncate64, sys_truncate, \ #define __NR3264_ftruncate 46 __SC_COMP_3264(__NR3264_ftruncate, sys_ftruncate64, sys_ftruncate, \ compat_sys_ftruncate64) - #define __NR_fallocate 47 __SC_COMP(__NR_fallocate, sys_fallocate, compat_sys_fallocate) #define __NR_faccessat 48 @@ -186,20 +161,12 @@ __SYSCALL(__NR_openat, sys_openat) __SYSCALL(__NR_close, sys_close) #define __NR_vhangup 58 __SYSCALL(__NR_vhangup, sys_vhangup) - -/* fs/pipe.c */ #define __NR_pipe2 59 __SYSCALL(__NR_pipe2, sys_pipe2) - -/* fs/quota.c */ #define __NR_quotactl 60 __SYSCALL(__NR_quotactl, sys_quotactl) - -/* fs/readdir.c */ #define __NR_getdents64 61 __SYSCALL(__NR_getdents64, sys_getdents64) - -/* fs/read_write.c */ #define __NR3264_lseek 62 __SC_3264(__NR3264_lseek, sys_llseek, sys_lseek) #define __NR_read 63 @@ -218,12 +185,9 @@ __SC_COMP(__NR_pwrite64, sys_pwrite64, compat_sys_pwrite64) __SC_COMP(__NR_preadv, sys_preadv, compat_sys_preadv) #define __NR_pwritev 70 __SC_COMP(__NR_pwritev, sys_pwritev, compat_sys_pwritev) - -/* fs/sendfile.c */ #define __NR3264_sendfile 71 __SYSCALL(__NR3264_sendfile, sys_sendfile64) -/* fs/select.c */ #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_pselect6 72 __SC_COMP_3264(__NR_pselect6, sys_pselect6_time32, sys_pselect6, compat_sys_pselect6_time32) @@ -231,21 +195,17 @@ __SC_COMP_3264(__NR_pselect6, sys_pselect6_time32, sys_pselect6, compat_sys_psel __SC_COMP_3264(__NR_ppoll, sys_ppoll_time32, sys_ppoll, compat_sys_ppoll_time32) #endif -/* fs/signalfd.c */ #define __NR_signalfd4 74 __SC_COMP(__NR_signalfd4, sys_signalfd4, compat_sys_signalfd4) - -/* fs/splice.c */ #define __NR_vmsplice 75 __SYSCALL(__NR_vmsplice, sys_vmsplice) #define __NR_splice 76 __SYSCALL(__NR_splice, sys_splice) #define __NR_tee 77 __SYSCALL(__NR_tee, sys_tee) - -/* fs/stat.c */ #define __NR_readlinkat 78 __SYSCALL(__NR_readlinkat, sys_readlinkat) + #if defined(__ARCH_WANT_NEW_STAT) || defined(__ARCH_WANT_STAT64) #define __NR3264_fstatat 79 __SC_3264(__NR3264_fstatat, sys_fstatat64, sys_newfstatat) @@ -253,13 +213,13 @@ __SC_3264(__NR3264_fstatat, sys_fstatat64, sys_newfstatat) __SC_3264(__NR3264_fstat, sys_fstat64, sys_newfstat) #endif -/* fs/sync.c */ #define __NR_sync 81 __SYSCALL(__NR_sync, sys_sync) #define __NR_fsync 82 __SYSCALL(__NR_fsync, sys_fsync) #define __NR_fdatasync 83 __SYSCALL(__NR_fdatasync, sys_fdatasync) + #ifdef __ARCH_WANT_SYNC_FILE_RANGE2 #define __NR_sync_file_range2 84 __SC_COMP(__NR_sync_file_range2, sys_sync_file_range2, \ @@ -270,9 +230,9 @@ __SC_COMP(__NR_sync_file_range, sys_sync_file_range, \ compat_sys_sync_file_range) #endif -/* fs/timerfd.c */ #define __NR_timerfd_create 85 __SYSCALL(__NR_timerfd_create, sys_timerfd_create) + #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_timerfd_settime 86 __SC_3264(__NR_timerfd_settime, sys_timerfd_settime32, \ @@ -282,45 +242,35 @@ __SC_3264(__NR_timerfd_gettime, sys_timerfd_gettime32, \ sys_timerfd_gettime) #endif -/* fs/utimes.c */ #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_utimensat 88 __SC_3264(__NR_utimensat, sys_utimensat_time32, sys_utimensat) #endif -/* kernel/acct.c */ #define __NR_acct 89 __SYSCALL(__NR_acct, sys_acct) - -/* kernel/capability.c */ #define __NR_capget 90 __SYSCALL(__NR_capget, sys_capget) #define __NR_capset 91 __SYSCALL(__NR_capset, sys_capset) - -/* kernel/exec_domain.c */ #define __NR_personality 92 __SYSCALL(__NR_personality, sys_personality) - -/* kernel/exit.c */ #define __NR_exit 93 __SYSCALL(__NR_exit, sys_exit) #define __NR_exit_group 94 __SYSCALL(__NR_exit_group, sys_exit_group) #define __NR_waitid 95 __SC_COMP(__NR_waitid, sys_waitid, compat_sys_waitid) - -/* kernel/fork.c */ #define __NR_set_tid_address 96 __SYSCALL(__NR_set_tid_address, sys_set_tid_address) #define __NR_unshare 97 __SYSCALL(__NR_unshare, sys_unshare) -/* kernel/futex.c */ #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_futex 98 __SC_3264(__NR_futex, sys_futex_time32, sys_futex) #endif + #define __NR_set_robust_list 99 __SC_COMP(__NR_set_robust_list, sys_set_robust_list, \ compat_sys_set_robust_list) @@ -328,43 +278,40 @@ __SC_COMP(__NR_set_robust_list, sys_set_robust_list, \ __SC_COMP(__NR_get_robust_list, sys_get_robust_list, \ compat_sys_get_robust_list) -/* kernel/hrtimer.c */ #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_nanosleep 101 __SC_3264(__NR_nanosleep, sys_nanosleep_time32, sys_nanosleep) #endif -/* kernel/itimer.c */ #define __NR_getitimer 102 __SC_COMP(__NR_getitimer, sys_getitimer, compat_sys_getitimer) #define __NR_setitimer 103 __SC_COMP(__NR_setitimer, sys_setitimer, compat_sys_setitimer) - -/* kernel/kexec.c */ #define __NR_kexec_load 104 __SC_COMP(__NR_kexec_load, sys_kexec_load, compat_sys_kexec_load) - -/* kernel/module.c */ #define __NR_init_module 105 __SYSCALL(__NR_init_module, sys_init_module) #define __NR_delete_module 106 __SYSCALL(__NR_delete_module, sys_delete_module) - -/* kernel/posix-timers.c */ #define __NR_timer_create 107 __SC_COMP(__NR_timer_create, sys_timer_create, compat_sys_timer_create) + #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_timer_gettime 108 __SC_3264(__NR_timer_gettime, sys_timer_gettime32, sys_timer_gettime) #endif + #define __NR_timer_getoverrun 109 __SYSCALL(__NR_timer_getoverrun, sys_timer_getoverrun) + #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_timer_settime 110 __SC_3264(__NR_timer_settime, sys_timer_settime32, sys_timer_settime) #endif + #define __NR_timer_delete 111 __SYSCALL(__NR_timer_delete, sys_timer_delete) + #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_clock_settime 112 __SC_3264(__NR_clock_settime, sys_clock_settime32, sys_clock_settime) @@ -377,15 +324,10 @@ __SC_3264(__NR_clock_nanosleep, sys_clock_nanosleep_time32, \ sys_clock_nanosleep) #endif -/* kernel/printk.c */ #define __NR_syslog 116 __SYSCALL(__NR_syslog, sys_syslog) - -/* kernel/ptrace.c */ #define __NR_ptrace 117 -__SYSCALL(__NR_ptrace, sys_ptrace) - -/* kernel/sched/core.c */ +__SC_COMP(__NR_ptrace, sys_ptrace, compat_sys_ptrace) #define __NR_sched_setparam 118 __SYSCALL(__NR_sched_setparam, sys_sched_setparam) #define __NR_sched_setscheduler 119 @@ -406,13 +348,13 @@ __SYSCALL(__NR_sched_yield, sys_sched_yield) __SYSCALL(__NR_sched_get_priority_max, sys_sched_get_priority_max) #define __NR_sched_get_priority_min 126 __SYSCALL(__NR_sched_get_priority_min, sys_sched_get_priority_min) + #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_sched_rr_get_interval 127 __SC_3264(__NR_sched_rr_get_interval, sys_sched_rr_get_interval_time32, \ sys_sched_rr_get_interval) #endif -/* kernel/signal.c */ #define __NR_restart_syscall 128 __SYSCALL(__NR_restart_syscall, sys_restart_syscall) #define __NR_kill 129 @@ -431,18 +373,18 @@ __SC_COMP(__NR_rt_sigaction, sys_rt_sigaction, compat_sys_rt_sigaction) __SC_COMP(__NR_rt_sigprocmask, sys_rt_sigprocmask, compat_sys_rt_sigprocmask) #define __NR_rt_sigpending 136 __SC_COMP(__NR_rt_sigpending, sys_rt_sigpending, compat_sys_rt_sigpending) + #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_rt_sigtimedwait 137 __SC_COMP_3264(__NR_rt_sigtimedwait, sys_rt_sigtimedwait_time32, \ sys_rt_sigtimedwait, compat_sys_rt_sigtimedwait_time32) #endif + #define __NR_rt_sigqueueinfo 138 __SC_COMP(__NR_rt_sigqueueinfo, sys_rt_sigqueueinfo, \ compat_sys_rt_sigqueueinfo) #define __NR_rt_sigreturn 139 __SC_COMP(__NR_rt_sigreturn, sys_rt_sigreturn, compat_sys_rt_sigreturn) - -/* kernel/sys.c */ #define __NR_setpriority 140 __SYSCALL(__NR_setpriority, sys_setpriority) #define __NR_getpriority 141 @@ -507,7 +449,6 @@ __SYSCALL(__NR_prctl, sys_prctl) #define __NR_getcpu 168 __SYSCALL(__NR_getcpu, sys_getcpu) -/* kernel/time.c */ #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_gettimeofday 169 __SC_COMP(__NR_gettimeofday, sys_gettimeofday, compat_sys_gettimeofday) @@ -517,7 +458,6 @@ __SC_COMP(__NR_settimeofday, sys_settimeofday, compat_sys_settimeofday) __SC_3264(__NR_adjtimex, sys_adjtimex_time32, sys_adjtimex) #endif -/* kernel/sys.c */ #define __NR_getpid 172 __SYSCALL(__NR_getpid, sys_getpid) #define __NR_getppid 173 @@ -534,12 +474,11 @@ __SYSCALL(__NR_getegid, sys_getegid) __SYSCALL(__NR_gettid, sys_gettid) #define __NR_sysinfo 179 __SC_COMP(__NR_sysinfo, sys_sysinfo, compat_sys_sysinfo) - -/* ipc/mqueue.c */ #define __NR_mq_open 180 __SC_COMP(__NR_mq_open, sys_mq_open, compat_sys_mq_open) #define __NR_mq_unlink 181 __SYSCALL(__NR_mq_unlink, sys_mq_unlink) + #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_mq_timedsend 182 __SC_3264(__NR_mq_timedsend, sys_mq_timedsend_time32, sys_mq_timedsend) @@ -547,12 +486,11 @@ __SC_3264(__NR_mq_timedsend, sys_mq_timedsend_time32, sys_mq_timedsend) __SC_3264(__NR_mq_timedreceive, sys_mq_timedreceive_time32, \ sys_mq_timedreceive) #endif + #define __NR_mq_notify 184 __SC_COMP(__NR_mq_notify, sys_mq_notify, compat_sys_mq_notify) #define __NR_mq_getsetattr 185 __SC_COMP(__NR_mq_getsetattr, sys_mq_getsetattr, compat_sys_mq_getsetattr) - -/* ipc/msg.c */ #define __NR_msgget 186 __SYSCALL(__NR_msgget, sys_msgget) #define __NR_msgctl 187 @@ -561,20 +499,18 @@ __SC_COMP(__NR_msgctl, sys_msgctl, compat_sys_msgctl) __SC_COMP(__NR_msgrcv, sys_msgrcv, compat_sys_msgrcv) #define __NR_msgsnd 189 __SC_COMP(__NR_msgsnd, sys_msgsnd, compat_sys_msgsnd) - -/* ipc/sem.c */ #define __NR_semget 190 __SYSCALL(__NR_semget, sys_semget) #define __NR_semctl 191 __SC_COMP(__NR_semctl, sys_semctl, compat_sys_semctl) + #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_semtimedop 192 __SC_3264(__NR_semtimedop, sys_semtimedop_time32, sys_semtimedop) #endif + #define __NR_semop 193 __SYSCALL(__NR_semop, sys_semop) - -/* ipc/shm.c */ #define __NR_shmget 194 __SYSCALL(__NR_shmget, sys_shmget) #define __NR_shmctl 195 @@ -583,8 +519,6 @@ __SC_COMP(__NR_shmctl, sys_shmctl, compat_sys_shmctl) __SC_COMP(__NR_shmat, sys_shmat, compat_sys_shmat) #define __NR_shmdt 197 __SYSCALL(__NR_shmdt, sys_shmdt) - -/* net/socket.c */ #define __NR_socket 198 __SYSCALL(__NR_socket, sys_socket) #define __NR_socketpair 199 @@ -615,40 +549,30 @@ __SYSCALL(__NR_shutdown, sys_shutdown) __SC_COMP(__NR_sendmsg, sys_sendmsg, compat_sys_sendmsg) #define __NR_recvmsg 212 __SC_COMP(__NR_recvmsg, sys_recvmsg, compat_sys_recvmsg) - -/* mm/filemap.c */ #define __NR_readahead 213 __SC_COMP(__NR_readahead, sys_readahead, compat_sys_readahead) - -/* mm/nommu.c, also with MMU */ #define __NR_brk 214 __SYSCALL(__NR_brk, sys_brk) #define __NR_munmap 215 __SYSCALL(__NR_munmap, sys_munmap) #define __NR_mremap 216 __SYSCALL(__NR_mremap, sys_mremap) - -/* security/keys/keyctl.c */ #define __NR_add_key 217 __SYSCALL(__NR_add_key, sys_add_key) #define __NR_request_key 218 __SYSCALL(__NR_request_key, sys_request_key) #define __NR_keyctl 219 __SC_COMP(__NR_keyctl, sys_keyctl, compat_sys_keyctl) - -/* arch/example/kernel/sys_example.c */ #define __NR_clone 220 __SYSCALL(__NR_clone, sys_clone) #define __NR_execve 221 __SC_COMP(__NR_execve, sys_execve, compat_sys_execve) - #define __NR3264_mmap 222 __SC_3264(__NR3264_mmap, sys_mmap2, sys_mmap) -/* mm/fadvise.c */ #define __NR3264_fadvise64 223 __SC_COMP(__NR3264_fadvise64, sys_fadvise64_64, compat_sys_fadvise64_64) -/* mm/, CONFIG_MMU only */ +/* CONFIG_MMU only */ #ifndef __ARCH_NOMMU #define __NR_swapon 224 __SYSCALL(__NR_swapon, sys_swapon) @@ -691,6 +615,7 @@ __SC_COMP(__NR_rt_tgsigqueueinfo, sys_rt_tgsigqueueinfo, \ __SYSCALL(__NR_perf_event_open, sys_perf_event_open) #define __NR_accept4 242 __SYSCALL(__NR_accept4, sys_accept4) + #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_recvmmsg 243 __SC_COMP_3264(__NR_recvmmsg, sys_recvmmsg_time32, sys_recvmmsg, compat_sys_recvmmsg_time32) @@ -706,6 +631,7 @@ __SC_COMP_3264(__NR_recvmmsg, sys_recvmmsg_time32, sys_recvmmsg, compat_sys_recv #define __NR_wait4 260 __SC_COMP(__NR_wait4, sys_wait4, compat_sys_wait4) #endif + #define __NR_prlimit64 261 __SYSCALL(__NR_prlimit64, sys_prlimit64) #define __NR_fanotify_init 262 @@ -716,10 +642,12 @@ __SYSCALL(__NR_fanotify_mark, sys_fanotify_mark) __SYSCALL(__NR_name_to_handle_at, sys_name_to_handle_at) #define __NR_open_by_handle_at 265 __SYSCALL(__NR_open_by_handle_at, sys_open_by_handle_at) + #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_clock_adjtime 266 __SC_3264(__NR_clock_adjtime, sys_clock_adjtime32, sys_clock_adjtime) #endif + #define __NR_syncfs 267 __SYSCALL(__NR_syncfs, sys_syncfs) #define __NR_setns 268 @@ -770,16 +698,20 @@ __SYSCALL(__NR_pkey_alloc, sys_pkey_alloc) __SYSCALL(__NR_pkey_free, sys_pkey_free) #define __NR_statx 291 __SYSCALL(__NR_statx, sys_statx) + #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32 #define __NR_io_pgetevents 292 __SC_COMP_3264(__NR_io_pgetevents, sys_io_pgetevents_time32, sys_io_pgetevents, compat_sys_io_pgetevents) #endif + #define __NR_rseq 293 __SYSCALL(__NR_rseq, sys_rseq) #define __NR_kexec_file_load 294 __SYSCALL(__NR_kexec_file_load, sys_kexec_file_load) + /* 295 through 402 are unassigned to sync up with generic numbers, don't use */ -#if __BITS_PER_LONG == 32 + +#if defined(__SYSCALL_COMPAT) || __BITS_PER_LONG == 32 #define __NR_clock_gettime64 403 __SYSCALL(__NR_clock_gettime64, sys_clock_gettime) #define __NR_clock_settime64 404 @@ -844,13 +776,14 @@ __SYSCALL(__NR_fsmount, sys_fsmount) __SYSCALL(__NR_fspick, sys_fspick) #define __NR_pidfd_open 434 __SYSCALL(__NR_pidfd_open, sys_pidfd_open) + #ifdef __ARCH_WANT_SYS_CLONE3 #define __NR_clone3 435 __SYSCALL(__NR_clone3, sys_clone3) #endif + #define __NR_close_range 436 __SYSCALL(__NR_close_range, sys_close_range) - #define __NR_openat2 437 __SYSCALL(__NR_openat2, sys_openat2) #define __NR_pidfd_getfd 438 @@ -865,7 +798,6 @@ __SC_COMP(__NR_epoll_pwait2, sys_epoll_pwait2, compat_sys_epoll_pwait2) __SYSCALL(__NR_mount_setattr, sys_mount_setattr) #define __NR_quotactl_fd 443 __SYSCALL(__NR_quotactl_fd, sys_quotactl_fd) - #define __NR_landlock_create_ruleset 444 __SYSCALL(__NR_landlock_create_ruleset, sys_landlock_create_ruleset) #define __NR_landlock_add_rule 445 @@ -877,17 +809,41 @@ __SYSCALL(__NR_landlock_restrict_self, sys_landlock_restrict_self) #define __NR_memfd_secret 447 __SYSCALL(__NR_memfd_secret, sys_memfd_secret) #endif + #define __NR_process_mrelease 448 __SYSCALL(__NR_process_mrelease, sys_process_mrelease) - #define __NR_futex_waitv 449 __SYSCALL(__NR_futex_waitv, sys_futex_waitv) - #define __NR_set_mempolicy_home_node 450 __SYSCALL(__NR_set_mempolicy_home_node, sys_set_mempolicy_home_node) +#define __NR_cachestat 451 +__SYSCALL(__NR_cachestat, sys_cachestat) +#define __NR_fchmodat2 452 +__SYSCALL(__NR_fchmodat2, sys_fchmodat2) +#define __NR_map_shadow_stack 453 +__SYSCALL(__NR_map_shadow_stack, sys_map_shadow_stack) +#define __NR_futex_wake 454 +__SYSCALL(__NR_futex_wake, sys_futex_wake) +#define __NR_futex_wait 455 +__SYSCALL(__NR_futex_wait, sys_futex_wait) +#define __NR_futex_requeue 456 +__SYSCALL(__NR_futex_requeue, sys_futex_requeue) + +#define __NR_statmount 457 +__SYSCALL(__NR_statmount, sys_statmount) + +#define __NR_listmount 458 +__SYSCALL(__NR_listmount, sys_listmount) + +#define __NR_lsm_get_self_attr 459 +__SYSCALL(__NR_lsm_get_self_attr, sys_lsm_get_self_attr) +#define __NR_lsm_set_self_attr 460 +__SYSCALL(__NR_lsm_set_self_attr, sys_lsm_set_self_attr) +#define __NR_lsm_list_modules 461 +__SYSCALL(__NR_lsm_list_modules, sys_lsm_list_modules) #undef __NR_syscalls -#define __NR_syscalls 451 +#define __NR_syscalls 462 /* * 32 bit systems traditionally used different diff --git a/linux-headers/asm-loongarch/bitsperlong.h b/linux-headers/asm-loongarch/bitsperlong.h new file mode 100644 index 0000000000..6dc0bb0c13 --- /dev/null +++ b/linux-headers/asm-loongarch/bitsperlong.h @@ -0,0 +1 @@ +#include diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h new file mode 100644 index 0000000000..923d0bd382 --- /dev/null +++ b/linux-headers/asm-loongarch/kvm.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited + */ + +#ifndef __UAPI_ASM_LOONGARCH_KVM_H +#define __UAPI_ASM_LOONGARCH_KVM_H + +#include + +/* + * KVM LoongArch specific structures and definitions. + * + * Some parts derived from the x86 version of this file. + */ + +#define __KVM_HAVE_READONLY_MEM + +#define KVM_COALESCED_MMIO_PAGE_OFFSET 1 +#define KVM_DIRTY_LOG_PAGE_OFFSET 64 + +/* + * for KVM_GET_REGS and KVM_SET_REGS + */ +struct kvm_regs { + /* out (KVM_GET_REGS) / in (KVM_SET_REGS) */ + __u64 gpr[32]; + __u64 pc; +}; + +/* + * for KVM_GET_FPU and KVM_SET_FPU + */ +struct kvm_fpu { + __u32 fcsr; + __u64 fcc; /* 8x8 */ + struct kvm_fpureg { + __u64 val64[4]; + } fpr[32]; +}; + +/* + * For LoongArch, we use KVM_SET_ONE_REG and KVM_GET_ONE_REG to access various + * registers. The id field is broken down as follows: + * + * bits[63..52] - As per linux/kvm.h + * bits[51..32] - Must be zero. + * bits[31..16] - Register set. + * + * Register set = 0: GP registers from kvm_regs (see definitions below). + * + * Register set = 1: CSR registers. + * + * Register set = 2: KVM specific registers (see definitions below). + * + * Register set = 3: FPU / SIMD registers (see definitions below). + * + * Other sets registers may be added in the future. Each set would + * have its own identifier in bits[31..16]. + */ + +#define KVM_REG_LOONGARCH_GPR (KVM_REG_LOONGARCH | 0x00000ULL) +#define KVM_REG_LOONGARCH_CSR (KVM_REG_LOONGARCH | 0x10000ULL) +#define KVM_REG_LOONGARCH_KVM (KVM_REG_LOONGARCH | 0x20000ULL) +#define KVM_REG_LOONGARCH_FPSIMD (KVM_REG_LOONGARCH | 0x30000ULL) +#define KVM_REG_LOONGARCH_CPUCFG (KVM_REG_LOONGARCH | 0x40000ULL) +#define KVM_REG_LOONGARCH_MASK (KVM_REG_LOONGARCH | 0x70000ULL) +#define KVM_CSR_IDX_MASK 0x7fff +#define KVM_CPUCFG_IDX_MASK 0x7fff + +/* + * KVM_REG_LOONGARCH_KVM - KVM specific control registers. + */ + +#define KVM_REG_LOONGARCH_COUNTER (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 1) +#define KVM_REG_LOONGARCH_VCPU_RESET (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 2) + +#define LOONGARCH_REG_SHIFT 3 +#define LOONGARCH_REG_64(TYPE, REG) (TYPE | KVM_REG_SIZE_U64 | (REG << LOONGARCH_REG_SHIFT)) +#define KVM_IOC_CSRID(REG) LOONGARCH_REG_64(KVM_REG_LOONGARCH_CSR, REG) +#define KVM_IOC_CPUCFG(REG) LOONGARCH_REG_64(KVM_REG_LOONGARCH_CPUCFG, REG) +#define KVM_LOONGARCH_VCPU_CPUCFG 0 + +struct kvm_debug_exit_arch { +}; + +/* for KVM_SET_GUEST_DEBUG */ +struct kvm_guest_debug_arch { +}; + +/* definition of registers in kvm_run */ +struct kvm_sync_regs { +}; + +/* dummy definition */ +struct kvm_sregs { +}; + +struct kvm_iocsr_entry { + __u32 addr; + __u32 pad; + __u64 data; +}; + +#define KVM_NR_IRQCHIPS 1 +#define KVM_IRQCHIP_NUM_PINS 64 +#define KVM_MAX_CORES 256 + +#endif /* __UAPI_ASM_LOONGARCH_KVM_H */ diff --git a/linux-headers/asm-loongarch/mman.h b/linux-headers/asm-loongarch/mman.h new file mode 100644 index 0000000000..8eebf89f5a --- /dev/null +++ b/linux-headers/asm-loongarch/mman.h @@ -0,0 +1 @@ +#include diff --git a/linux-headers/asm-loongarch/unistd.h b/linux-headers/asm-loongarch/unistd.h new file mode 100644 index 0000000000..fcb668984f --- /dev/null +++ b/linux-headers/asm-loongarch/unistd.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#define __ARCH_WANT_SYS_CLONE +#define __ARCH_WANT_SYS_CLONE3 + +#include diff --git a/linux-headers/asm-mips/mman.h b/linux-headers/asm-mips/mman.h index 1be428663c..9c48d9a21a 100644 --- a/linux-headers/asm-mips/mman.h +++ b/linux-headers/asm-mips/mman.h @@ -88,7 +88,7 @@ #define MADV_HUGEPAGE 14 /* Worth backing with hugepages */ #define MADV_NOHUGEPAGE 15 /* Not worth backing with hugepages */ -#define MADV_DONTDUMP 16 /* Explicity exclude from the core dump, +#define MADV_DONTDUMP 16 /* Explicitly exclude from core dump, overrides the coredump filter bits */ #define MADV_DODUMP 17 /* Clear the MADV_NODUMP flag */ @@ -103,6 +103,8 @@ #define MADV_DONTNEED_LOCKED 24 /* like DONTNEED, but drop locked pages too */ +#define MADV_COLLAPSE 25 /* Synchronous hugepage collapse */ + /* compatibility flags */ #define MAP_FILE 0 diff --git a/linux-headers/asm-mips/unistd_n32.h b/linux-headers/asm-mips/unistd_n32.h index 1f14a6fad3..ce2e050a9b 100644 --- a/linux-headers/asm-mips/unistd_n32.h +++ b/linux-headers/asm-mips/unistd_n32.h @@ -379,5 +379,16 @@ #define __NR_process_mrelease (__NR_Linux + 448) #define __NR_futex_waitv (__NR_Linux + 449) #define __NR_set_mempolicy_home_node (__NR_Linux + 450) +#define __NR_cachestat (__NR_Linux + 451) +#define __NR_fchmodat2 (__NR_Linux + 452) +#define __NR_map_shadow_stack (__NR_Linux + 453) +#define __NR_futex_wake (__NR_Linux + 454) +#define __NR_futex_wait (__NR_Linux + 455) +#define __NR_futex_requeue (__NR_Linux + 456) +#define __NR_statmount (__NR_Linux + 457) +#define __NR_listmount (__NR_Linux + 458) +#define __NR_lsm_get_self_attr (__NR_Linux + 459) +#define __NR_lsm_set_self_attr (__NR_Linux + 460) +#define __NR_lsm_list_modules (__NR_Linux + 461) #endif /* _ASM_UNISTD_N32_H */ diff --git a/linux-headers/asm-mips/unistd_n64.h b/linux-headers/asm-mips/unistd_n64.h index e5a8ebec78..5bfb3733ff 100644 --- a/linux-headers/asm-mips/unistd_n64.h +++ b/linux-headers/asm-mips/unistd_n64.h @@ -355,5 +355,16 @@ #define __NR_process_mrelease (__NR_Linux + 448) #define __NR_futex_waitv (__NR_Linux + 449) #define __NR_set_mempolicy_home_node (__NR_Linux + 450) +#define __NR_cachestat (__NR_Linux + 451) +#define __NR_fchmodat2 (__NR_Linux + 452) +#define __NR_map_shadow_stack (__NR_Linux + 453) +#define __NR_futex_wake (__NR_Linux + 454) +#define __NR_futex_wait (__NR_Linux + 455) +#define __NR_futex_requeue (__NR_Linux + 456) +#define __NR_statmount (__NR_Linux + 457) +#define __NR_listmount (__NR_Linux + 458) +#define __NR_lsm_get_self_attr (__NR_Linux + 459) +#define __NR_lsm_set_self_attr (__NR_Linux + 460) +#define __NR_lsm_list_modules (__NR_Linux + 461) #endif /* _ASM_UNISTD_N64_H */ diff --git a/linux-headers/asm-mips/unistd_o32.h b/linux-headers/asm-mips/unistd_o32.h index 871d57168f..02eaecd020 100644 --- a/linux-headers/asm-mips/unistd_o32.h +++ b/linux-headers/asm-mips/unistd_o32.h @@ -425,5 +425,16 @@ #define __NR_process_mrelease (__NR_Linux + 448) #define __NR_futex_waitv (__NR_Linux + 449) #define __NR_set_mempolicy_home_node (__NR_Linux + 450) +#define __NR_cachestat (__NR_Linux + 451) +#define __NR_fchmodat2 (__NR_Linux + 452) +#define __NR_map_shadow_stack (__NR_Linux + 453) +#define __NR_futex_wake (__NR_Linux + 454) +#define __NR_futex_wait (__NR_Linux + 455) +#define __NR_futex_requeue (__NR_Linux + 456) +#define __NR_statmount (__NR_Linux + 457) +#define __NR_listmount (__NR_Linux + 458) +#define __NR_lsm_get_self_attr (__NR_Linux + 459) +#define __NR_lsm_set_self_attr (__NR_Linux + 460) +#define __NR_lsm_list_modules (__NR_Linux + 461) #endif /* _ASM_UNISTD_O32_H */ diff --git a/linux-headers/asm-powerpc/unistd_32.h b/linux-headers/asm-powerpc/unistd_32.h index 585c7fefbc..bbab08d6ec 100644 --- a/linux-headers/asm-powerpc/unistd_32.h +++ b/linux-headers/asm-powerpc/unistd_32.h @@ -432,6 +432,17 @@ #define __NR_process_mrelease 448 #define __NR_futex_waitv 449 #define __NR_set_mempolicy_home_node 450 +#define __NR_cachestat 451 +#define __NR_fchmodat2 452 +#define __NR_map_shadow_stack 453 +#define __NR_futex_wake 454 +#define __NR_futex_wait 455 +#define __NR_futex_requeue 456 +#define __NR_statmount 457 +#define __NR_listmount 458 +#define __NR_lsm_get_self_attr 459 +#define __NR_lsm_set_self_attr 460 +#define __NR_lsm_list_modules 461 #endif /* _ASM_UNISTD_32_H */ diff --git a/linux-headers/asm-powerpc/unistd_64.h b/linux-headers/asm-powerpc/unistd_64.h index 350f7ec0ac..af34cde70f 100644 --- a/linux-headers/asm-powerpc/unistd_64.h +++ b/linux-headers/asm-powerpc/unistd_64.h @@ -404,6 +404,17 @@ #define __NR_process_mrelease 448 #define __NR_futex_waitv 449 #define __NR_set_mempolicy_home_node 450 +#define __NR_cachestat 451 +#define __NR_fchmodat2 452 +#define __NR_map_shadow_stack 453 +#define __NR_futex_wake 454 +#define __NR_futex_wait 455 +#define __NR_futex_requeue 456 +#define __NR_statmount 457 +#define __NR_listmount 458 +#define __NR_lsm_get_self_attr 459 +#define __NR_lsm_set_self_attr 460 +#define __NR_lsm_list_modules 461 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-riscv/kvm.h b/linux-headers/asm-riscv/kvm.h index f808ad1ce5..7499e88a94 100644 --- a/linux-headers/asm-riscv/kvm.h +++ b/linux-headers/asm-riscv/kvm.h @@ -12,8 +12,10 @@ #ifndef __ASSEMBLY__ #include +#include #include +#define __KVM_HAVE_IRQ_LINE #define __KVM_HAVE_READONLY_MEM #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 @@ -48,6 +50,12 @@ struct kvm_sregs { /* CONFIG registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ struct kvm_riscv_config { unsigned long isa; + unsigned long zicbom_block_size; + unsigned long mvendorid; + unsigned long marchid; + unsigned long mimpid; + unsigned long zicboz_block_size; + unsigned long satp_mode; }; /* CORE registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ @@ -60,7 +68,7 @@ struct kvm_riscv_core { #define KVM_RISCV_MODE_S 1 #define KVM_RISCV_MODE_U 0 -/* CSR registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ +/* General CSR registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ struct kvm_riscv_csr { unsigned long sstatus; unsigned long sie; @@ -72,6 +80,23 @@ struct kvm_riscv_csr { unsigned long sip; unsigned long satp; unsigned long scounteren; + unsigned long senvcfg; +}; + +/* AIA CSR registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ +struct kvm_riscv_aia_csr { + unsigned long siselect; + unsigned long iprio1; + unsigned long iprio2; + unsigned long sieh; + unsigned long siph; + unsigned long iprio1h; + unsigned long iprio2h; +}; + +/* Smstateen CSR for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ +struct kvm_riscv_smstateen_csr { + unsigned long sstateen0; }; /* TIMER registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ @@ -82,6 +107,93 @@ struct kvm_riscv_timer { __u64 state; }; +/* + * ISA extension IDs specific to KVM. This is not the same as the host ISA + * extension IDs as that is internal to the host and should not be exposed + * to the guest. This should always be contiguous to keep the mapping simple + * in KVM implementation. + */ +enum KVM_RISCV_ISA_EXT_ID { + KVM_RISCV_ISA_EXT_A = 0, + KVM_RISCV_ISA_EXT_C, + KVM_RISCV_ISA_EXT_D, + KVM_RISCV_ISA_EXT_F, + KVM_RISCV_ISA_EXT_H, + KVM_RISCV_ISA_EXT_I, + KVM_RISCV_ISA_EXT_M, + KVM_RISCV_ISA_EXT_SVPBMT, + KVM_RISCV_ISA_EXT_SSTC, + KVM_RISCV_ISA_EXT_SVINVAL, + KVM_RISCV_ISA_EXT_ZIHINTPAUSE, + KVM_RISCV_ISA_EXT_ZICBOM, + KVM_RISCV_ISA_EXT_ZICBOZ, + KVM_RISCV_ISA_EXT_ZBB, + KVM_RISCV_ISA_EXT_SSAIA, + KVM_RISCV_ISA_EXT_V, + KVM_RISCV_ISA_EXT_SVNAPOT, + KVM_RISCV_ISA_EXT_ZBA, + KVM_RISCV_ISA_EXT_ZBS, + KVM_RISCV_ISA_EXT_ZICNTR, + KVM_RISCV_ISA_EXT_ZICSR, + KVM_RISCV_ISA_EXT_ZIFENCEI, + KVM_RISCV_ISA_EXT_ZIHPM, + KVM_RISCV_ISA_EXT_SMSTATEEN, + KVM_RISCV_ISA_EXT_ZICOND, + KVM_RISCV_ISA_EXT_ZBC, + KVM_RISCV_ISA_EXT_ZBKB, + KVM_RISCV_ISA_EXT_ZBKC, + KVM_RISCV_ISA_EXT_ZBKX, + KVM_RISCV_ISA_EXT_ZKND, + KVM_RISCV_ISA_EXT_ZKNE, + KVM_RISCV_ISA_EXT_ZKNH, + KVM_RISCV_ISA_EXT_ZKR, + KVM_RISCV_ISA_EXT_ZKSED, + KVM_RISCV_ISA_EXT_ZKSH, + KVM_RISCV_ISA_EXT_ZKT, + KVM_RISCV_ISA_EXT_ZVBB, + KVM_RISCV_ISA_EXT_ZVBC, + KVM_RISCV_ISA_EXT_ZVKB, + KVM_RISCV_ISA_EXT_ZVKG, + KVM_RISCV_ISA_EXT_ZVKNED, + KVM_RISCV_ISA_EXT_ZVKNHA, + KVM_RISCV_ISA_EXT_ZVKNHB, + KVM_RISCV_ISA_EXT_ZVKSED, + KVM_RISCV_ISA_EXT_ZVKSH, + KVM_RISCV_ISA_EXT_ZVKT, + KVM_RISCV_ISA_EXT_ZFH, + KVM_RISCV_ISA_EXT_ZFHMIN, + KVM_RISCV_ISA_EXT_ZIHINTNTL, + KVM_RISCV_ISA_EXT_ZVFH, + KVM_RISCV_ISA_EXT_ZVFHMIN, + KVM_RISCV_ISA_EXT_ZFA, + KVM_RISCV_ISA_EXT_MAX, +}; + +/* + * SBI extension IDs specific to KVM. This is not the same as the SBI + * extension IDs defined by the RISC-V SBI specification. + */ +enum KVM_RISCV_SBI_EXT_ID { + KVM_RISCV_SBI_EXT_V01 = 0, + KVM_RISCV_SBI_EXT_TIME, + KVM_RISCV_SBI_EXT_IPI, + KVM_RISCV_SBI_EXT_RFENCE, + KVM_RISCV_SBI_EXT_SRST, + KVM_RISCV_SBI_EXT_HSM, + KVM_RISCV_SBI_EXT_PMU, + KVM_RISCV_SBI_EXT_EXPERIMENTAL, + KVM_RISCV_SBI_EXT_VENDOR, + KVM_RISCV_SBI_EXT_DBCN, + KVM_RISCV_SBI_EXT_STA, + KVM_RISCV_SBI_EXT_MAX, +}; + +/* SBI STA extension registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ +struct kvm_riscv_sbi_sta { + unsigned long shmem_lo; + unsigned long shmem_hi; +}; + /* Possible states for kvm_riscv_timer */ #define KVM_RISCV_TIMER_STATE_OFF 0 #define KVM_RISCV_TIMER_STATE_ON 1 @@ -92,6 +204,8 @@ struct kvm_riscv_timer { /* If you need to interpret the index values, here is the key: */ #define KVM_REG_RISCV_TYPE_MASK 0x00000000FF000000 #define KVM_REG_RISCV_TYPE_SHIFT 24 +#define KVM_REG_RISCV_SUBTYPE_MASK 0x0000000000FF0000 +#define KVM_REG_RISCV_SUBTYPE_SHIFT 16 /* Config registers are mapped as type 1 */ #define KVM_REG_RISCV_CONFIG (0x01 << KVM_REG_RISCV_TYPE_SHIFT) @@ -105,8 +219,15 @@ struct kvm_riscv_timer { /* Control and status registers are mapped as type 3 */ #define KVM_REG_RISCV_CSR (0x03 << KVM_REG_RISCV_TYPE_SHIFT) +#define KVM_REG_RISCV_CSR_GENERAL (0x0 << KVM_REG_RISCV_SUBTYPE_SHIFT) +#define KVM_REG_RISCV_CSR_AIA (0x1 << KVM_REG_RISCV_SUBTYPE_SHIFT) +#define KVM_REG_RISCV_CSR_SMSTATEEN (0x2 << KVM_REG_RISCV_SUBTYPE_SHIFT) #define KVM_REG_RISCV_CSR_REG(name) \ (offsetof(struct kvm_riscv_csr, name) / sizeof(unsigned long)) +#define KVM_REG_RISCV_CSR_AIA_REG(name) \ + (offsetof(struct kvm_riscv_aia_csr, name) / sizeof(unsigned long)) +#define KVM_REG_RISCV_CSR_SMSTATEEN_REG(name) \ + (offsetof(struct kvm_riscv_smstateen_csr, name) / sizeof(unsigned long)) /* Timer registers are mapped as type 4 */ #define KVM_REG_RISCV_TIMER (0x04 << KVM_REG_RISCV_TYPE_SHIFT) @@ -123,6 +244,114 @@ struct kvm_riscv_timer { #define KVM_REG_RISCV_FP_D_REG(name) \ (offsetof(struct __riscv_d_ext_state, name) / sizeof(__u64)) +/* ISA Extension registers are mapped as type 7 */ +#define KVM_REG_RISCV_ISA_EXT (0x07 << KVM_REG_RISCV_TYPE_SHIFT) +#define KVM_REG_RISCV_ISA_SINGLE (0x0 << KVM_REG_RISCV_SUBTYPE_SHIFT) +#define KVM_REG_RISCV_ISA_MULTI_EN (0x1 << KVM_REG_RISCV_SUBTYPE_SHIFT) +#define KVM_REG_RISCV_ISA_MULTI_DIS (0x2 << KVM_REG_RISCV_SUBTYPE_SHIFT) +#define KVM_REG_RISCV_ISA_MULTI_REG(__ext_id) \ + ((__ext_id) / __BITS_PER_LONG) +#define KVM_REG_RISCV_ISA_MULTI_MASK(__ext_id) \ + (1UL << ((__ext_id) % __BITS_PER_LONG)) +#define KVM_REG_RISCV_ISA_MULTI_REG_LAST \ + KVM_REG_RISCV_ISA_MULTI_REG(KVM_RISCV_ISA_EXT_MAX - 1) + +/* SBI extension registers are mapped as type 8 */ +#define KVM_REG_RISCV_SBI_EXT (0x08 << KVM_REG_RISCV_TYPE_SHIFT) +#define KVM_REG_RISCV_SBI_SINGLE (0x0 << KVM_REG_RISCV_SUBTYPE_SHIFT) +#define KVM_REG_RISCV_SBI_MULTI_EN (0x1 << KVM_REG_RISCV_SUBTYPE_SHIFT) +#define KVM_REG_RISCV_SBI_MULTI_DIS (0x2 << KVM_REG_RISCV_SUBTYPE_SHIFT) +#define KVM_REG_RISCV_SBI_MULTI_REG(__ext_id) \ + ((__ext_id) / __BITS_PER_LONG) +#define KVM_REG_RISCV_SBI_MULTI_MASK(__ext_id) \ + (1UL << ((__ext_id) % __BITS_PER_LONG)) +#define KVM_REG_RISCV_SBI_MULTI_REG_LAST \ + KVM_REG_RISCV_SBI_MULTI_REG(KVM_RISCV_SBI_EXT_MAX - 1) + +/* V extension registers are mapped as type 9 */ +#define KVM_REG_RISCV_VECTOR (0x09 << KVM_REG_RISCV_TYPE_SHIFT) +#define KVM_REG_RISCV_VECTOR_CSR_REG(name) \ + (offsetof(struct __riscv_v_ext_state, name) / sizeof(unsigned long)) +#define KVM_REG_RISCV_VECTOR_REG(n) \ + ((n) + sizeof(struct __riscv_v_ext_state) / sizeof(unsigned long)) + +/* Registers for specific SBI extensions are mapped as type 10 */ +#define KVM_REG_RISCV_SBI_STATE (0x0a << KVM_REG_RISCV_TYPE_SHIFT) +#define KVM_REG_RISCV_SBI_STA (0x0 << KVM_REG_RISCV_SUBTYPE_SHIFT) +#define KVM_REG_RISCV_SBI_STA_REG(name) \ + (offsetof(struct kvm_riscv_sbi_sta, name) / sizeof(unsigned long)) + +/* Device Control API: RISC-V AIA */ +#define KVM_DEV_RISCV_APLIC_ALIGN 0x1000 +#define KVM_DEV_RISCV_APLIC_SIZE 0x4000 +#define KVM_DEV_RISCV_APLIC_MAX_HARTS 0x4000 +#define KVM_DEV_RISCV_IMSIC_ALIGN 0x1000 +#define KVM_DEV_RISCV_IMSIC_SIZE 0x1000 + +#define KVM_DEV_RISCV_AIA_GRP_CONFIG 0 +#define KVM_DEV_RISCV_AIA_CONFIG_MODE 0 +#define KVM_DEV_RISCV_AIA_CONFIG_IDS 1 +#define KVM_DEV_RISCV_AIA_CONFIG_SRCS 2 +#define KVM_DEV_RISCV_AIA_CONFIG_GROUP_BITS 3 +#define KVM_DEV_RISCV_AIA_CONFIG_GROUP_SHIFT 4 +#define KVM_DEV_RISCV_AIA_CONFIG_HART_BITS 5 +#define KVM_DEV_RISCV_AIA_CONFIG_GUEST_BITS 6 + +/* + * Modes of RISC-V AIA device: + * 1) EMUL (aka Emulation): Trap-n-emulate IMSIC + * 2) HWACCEL (aka HW Acceleration): Virtualize IMSIC using IMSIC guest files + * 3) AUTO (aka Automatic): Virtualize IMSIC using IMSIC guest files whenever + * available otherwise fallback to trap-n-emulation + */ +#define KVM_DEV_RISCV_AIA_MODE_EMUL 0 +#define KVM_DEV_RISCV_AIA_MODE_HWACCEL 1 +#define KVM_DEV_RISCV_AIA_MODE_AUTO 2 + +#define KVM_DEV_RISCV_AIA_IDS_MIN 63 +#define KVM_DEV_RISCV_AIA_IDS_MAX 2048 +#define KVM_DEV_RISCV_AIA_SRCS_MAX 1024 +#define KVM_DEV_RISCV_AIA_GROUP_BITS_MAX 8 +#define KVM_DEV_RISCV_AIA_GROUP_SHIFT_MIN 24 +#define KVM_DEV_RISCV_AIA_GROUP_SHIFT_MAX 56 +#define KVM_DEV_RISCV_AIA_HART_BITS_MAX 16 +#define KVM_DEV_RISCV_AIA_GUEST_BITS_MAX 8 + +#define KVM_DEV_RISCV_AIA_GRP_ADDR 1 +#define KVM_DEV_RISCV_AIA_ADDR_APLIC 0 +#define KVM_DEV_RISCV_AIA_ADDR_IMSIC(__vcpu) (1 + (__vcpu)) +#define KVM_DEV_RISCV_AIA_ADDR_MAX \ + (1 + KVM_DEV_RISCV_APLIC_MAX_HARTS) + +#define KVM_DEV_RISCV_AIA_GRP_CTRL 2 +#define KVM_DEV_RISCV_AIA_CTRL_INIT 0 + +/* + * The device attribute type contains the memory mapped offset of the + * APLIC register (range 0x0000-0x3FFF) and it must be 4-byte aligned. + */ +#define KVM_DEV_RISCV_AIA_GRP_APLIC 3 + +/* + * The lower 12-bits of the device attribute type contains the iselect + * value of the IMSIC register (range 0x70-0xFF) whereas the higher order + * bits contains the VCPU id. + */ +#define KVM_DEV_RISCV_AIA_GRP_IMSIC 4 +#define KVM_DEV_RISCV_AIA_IMSIC_ISEL_BITS 12 +#define KVM_DEV_RISCV_AIA_IMSIC_ISEL_MASK \ + ((1U << KVM_DEV_RISCV_AIA_IMSIC_ISEL_BITS) - 1) +#define KVM_DEV_RISCV_AIA_IMSIC_MKATTR(__vcpu, __isel) \ + (((__vcpu) << KVM_DEV_RISCV_AIA_IMSIC_ISEL_BITS) | \ + ((__isel) & KVM_DEV_RISCV_AIA_IMSIC_ISEL_MASK)) +#define KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(__attr) \ + ((__attr) & KVM_DEV_RISCV_AIA_IMSIC_ISEL_MASK) +#define KVM_DEV_RISCV_AIA_IMSIC_GET_VCPU(__attr) \ + ((__attr) >> KVM_DEV_RISCV_AIA_IMSIC_ISEL_BITS) + +/* One single KVM irqchip, ie. the AIA */ +#define KVM_NR_IRQCHIPS 1 + #endif #endif /* __LINUX_KVM_RISCV_H */ diff --git a/linux-headers/asm-riscv/ptrace.h b/linux-headers/asm-riscv/ptrace.h new file mode 100644 index 0000000000..1e3166caca --- /dev/null +++ b/linux-headers/asm-riscv/ptrace.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * Copyright (C) 2012 Regents of the University of California + */ + +#ifndef _ASM_RISCV_PTRACE_H +#define _ASM_RISCV_PTRACE_H + +#ifndef __ASSEMBLY__ + +#include + +#define PTRACE_GETFDPIC 33 + +#define PTRACE_GETFDPIC_EXEC 0 +#define PTRACE_GETFDPIC_INTERP 1 + +/* + * User-mode register state for core dumps, ptrace, sigcontext + * + * This decouples struct pt_regs from the userspace ABI. + * struct user_regs_struct must form a prefix of struct pt_regs. + */ +struct user_regs_struct { + unsigned long pc; + unsigned long ra; + unsigned long sp; + unsigned long gp; + unsigned long tp; + unsigned long t0; + unsigned long t1; + unsigned long t2; + unsigned long s0; + unsigned long s1; + unsigned long a0; + unsigned long a1; + unsigned long a2; + unsigned long a3; + unsigned long a4; + unsigned long a5; + unsigned long a6; + unsigned long a7; + unsigned long s2; + unsigned long s3; + unsigned long s4; + unsigned long s5; + unsigned long s6; + unsigned long s7; + unsigned long s8; + unsigned long s9; + unsigned long s10; + unsigned long s11; + unsigned long t3; + unsigned long t4; + unsigned long t5; + unsigned long t6; +}; + +struct __riscv_f_ext_state { + __u32 f[32]; + __u32 fcsr; +}; + +struct __riscv_d_ext_state { + __u64 f[32]; + __u32 fcsr; +}; + +struct __riscv_q_ext_state { + __u64 f[64] __attribute__((aligned(16))); + __u32 fcsr; + /* + * Reserved for expansion of sigcontext structure. Currently zeroed + * upon signal, and must be zero upon sigreturn. + */ + __u32 reserved[3]; +}; + +struct __riscv_ctx_hdr { + __u32 magic; + __u32 size; +}; + +struct __riscv_extra_ext_header { + __u32 __padding[129] __attribute__((aligned(16))); + /* + * Reserved for expansion of sigcontext structure. Currently zeroed + * upon signal, and must be zero upon sigreturn. + */ + __u32 reserved; + struct __riscv_ctx_hdr hdr; +}; + +union __riscv_fp_state { + struct __riscv_f_ext_state f; + struct __riscv_d_ext_state d; + struct __riscv_q_ext_state q; +}; + +struct __riscv_v_ext_state { + unsigned long vstart; + unsigned long vl; + unsigned long vtype; + unsigned long vcsr; + unsigned long vlenb; + void *datap; + /* + * In signal handler, datap will be set a correct user stack offset + * and vector registers will be copied to the address of datap + * pointer. + */ +}; + +struct __riscv_v_regset_state { + unsigned long vstart; + unsigned long vl; + unsigned long vtype; + unsigned long vcsr; + unsigned long vlenb; + char vreg[]; +}; + +/* + * According to spec: The number of bits in a single vector register, + * VLEN >= ELEN, which must be a power of 2, and must be no greater than + * 2^16 = 65536bits = 8192bytes + */ +#define RISCV_MAX_VLENB (8192) + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_RISCV_PTRACE_H */ diff --git a/linux-headers/asm-riscv/unistd.h b/linux-headers/asm-riscv/unistd.h index 8062996c2d..950ab3fd44 100644 --- a/linux-headers/asm-riscv/unistd.h +++ b/linux-headers/asm-riscv/unistd.h @@ -15,12 +15,13 @@ * along with this program. If not, see . */ -#ifdef __LP64__ +#if defined(__LP64__) && !defined(__SYSCALL_COMPAT) #define __ARCH_WANT_NEW_STAT #define __ARCH_WANT_SET_GET_RLIMIT #endif /* __LP64__ */ #define __ARCH_WANT_SYS_CLONE3 +#define __ARCH_WANT_MEMFD_SECRET #include @@ -42,3 +43,12 @@ #define __NR_riscv_flush_icache (__NR_arch_specific_syscall + 15) #endif __SYSCALL(__NR_riscv_flush_icache, sys_riscv_flush_icache) + +/* + * Allows userspace to query the kernel for CPU architecture and + * microarchitecture details across a given set of CPUs. + */ +#ifndef __NR_riscv_hwprobe +#define __NR_riscv_hwprobe (__NR_arch_specific_syscall + 14) +#endif +__SYSCALL(__NR_riscv_hwprobe, sys_riscv_hwprobe) diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h index f053b8304a..023a2763a9 100644 --- a/linux-headers/asm-s390/kvm.h +++ b/linux-headers/asm-s390/kvm.h @@ -74,6 +74,7 @@ struct kvm_s390_io_adapter_req { #define KVM_S390_VM_CRYPTO 2 #define KVM_S390_VM_CPU_MODEL 3 #define KVM_S390_VM_MIGRATION 4 +#define KVM_S390_VM_CPU_TOPOLOGY 5 /* kvm attributes for mem_ctrl */ #define KVM_S390_VM_MEM_ENABLE_CMMA 0 @@ -158,6 +159,22 @@ struct kvm_s390_vm_cpu_subfunc { __u8 reserved[1728]; }; +#define KVM_S390_VM_CPU_PROCESSOR_UV_FEAT_GUEST 6 +#define KVM_S390_VM_CPU_MACHINE_UV_FEAT_GUEST 7 + +#define KVM_S390_VM_CPU_UV_FEAT_NR_BITS 64 +struct kvm_s390_vm_cpu_uv_feat { + union { + struct { + __u64 : 4; + __u64 ap : 1; /* bit 4 */ + __u64 ap_intr : 1; /* bit 5 */ + __u64 : 58; + }; + __u64 feat; + }; +}; + /* kvm attributes for crypto */ #define KVM_S390_VM_CRYPTO_ENABLE_AES_KW 0 #define KVM_S390_VM_CRYPTO_ENABLE_DEA_KW 1 diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h index 8e644d65f5..a3ece69d82 100644 --- a/linux-headers/asm-s390/unistd_32.h +++ b/linux-headers/asm-s390/unistd_32.h @@ -419,8 +419,20 @@ #define __NR_landlock_create_ruleset 444 #define __NR_landlock_add_rule 445 #define __NR_landlock_restrict_self 446 +#define __NR_memfd_secret 447 #define __NR_process_mrelease 448 #define __NR_futex_waitv 449 #define __NR_set_mempolicy_home_node 450 +#define __NR_cachestat 451 +#define __NR_fchmodat2 452 +#define __NR_map_shadow_stack 453 +#define __NR_futex_wake 454 +#define __NR_futex_wait 455 +#define __NR_futex_requeue 456 +#define __NR_statmount 457 +#define __NR_listmount 458 +#define __NR_lsm_get_self_attr 459 +#define __NR_lsm_set_self_attr 460 +#define __NR_lsm_list_modules 461 #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 51da542fec..8c5fd93495 100644 --- a/linux-headers/asm-s390/unistd_64.h +++ b/linux-headers/asm-s390/unistd_64.h @@ -367,8 +367,20 @@ #define __NR_landlock_create_ruleset 444 #define __NR_landlock_add_rule 445 #define __NR_landlock_restrict_self 446 +#define __NR_memfd_secret 447 #define __NR_process_mrelease 448 #define __NR_futex_waitv 449 #define __NR_set_mempolicy_home_node 450 +#define __NR_cachestat 451 +#define __NR_fchmodat2 452 +#define __NR_map_shadow_stack 453 +#define __NR_futex_wake 454 +#define __NR_futex_wait 455 +#define __NR_futex_requeue 456 +#define __NR_statmount 457 +#define __NR_listmount 458 +#define __NR_lsm_get_self_attr 459 +#define __NR_lsm_set_self_attr 460 +#define __NR_lsm_list_modules 461 #endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index bf6e96011d..003fb74534 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -9,6 +9,7 @@ #include #include +#include #define KVM_PIO_PAGE_OFFSET 1 #define KVM_COALESCED_MMIO_PAGE_OFFSET 2 @@ -53,14 +54,6 @@ /* Architectural interrupt line count. */ #define KVM_NR_INTERRUPTS 256 -struct kvm_memory_alias { - __u32 slot; /* this has a different namespace than memory slots */ - __u32 flags; - __u64 guest_phys_addr; - __u64 memory_size; - __u64 target_phys_addr; -}; - /* for KVM_GET_IRQCHIP and KVM_SET_IRQCHIP */ struct kvm_pic_state { __u8 last_irr; /* edge detection */ @@ -198,13 +191,13 @@ struct kvm_msrs { __u32 nmsrs; /* number of msrs in entries */ __u32 pad; - struct kvm_msr_entry entries[0]; + struct kvm_msr_entry entries[]; }; /* for KVM_GET_MSR_INDEX_LIST */ struct kvm_msr_list { __u32 nmsrs; /* number of msrs in entries */ - __u32 indices[0]; + __u32 indices[]; }; /* Maximum size of any access bitmap in bytes */ @@ -214,6 +207,8 @@ struct kvm_msr_list { struct kvm_msr_filter_range { #define KVM_MSR_FILTER_READ (1 << 0) #define KVM_MSR_FILTER_WRITE (1 << 1) +#define KVM_MSR_FILTER_RANGE_VALID_MASK (KVM_MSR_FILTER_READ | \ + KVM_MSR_FILTER_WRITE) __u32 flags; __u32 nmsrs; /* number of msrs in bitmap */ __u32 base; /* MSR index the bitmap starts at */ @@ -224,6 +219,7 @@ struct kvm_msr_filter_range { struct kvm_msr_filter { #define KVM_MSR_FILTER_DEFAULT_ALLOW (0 << 0) #define KVM_MSR_FILTER_DEFAULT_DENY (1 << 0) +#define KVM_MSR_FILTER_VALID_MASK (KVM_MSR_FILTER_DEFAULT_DENY) __u32 flags; struct kvm_msr_filter_range ranges[KVM_MSR_FILTER_MAX_RANGES]; }; @@ -241,7 +237,7 @@ struct kvm_cpuid_entry { struct kvm_cpuid { __u32 nent; __u32 padding; - struct kvm_cpuid_entry entries[0]; + struct kvm_cpuid_entry entries[]; }; struct kvm_cpuid_entry2 { @@ -263,7 +259,7 @@ struct kvm_cpuid_entry2 { struct kvm_cpuid2 { __u32 nent; __u32 padding; - struct kvm_cpuid_entry2 entries[0]; + struct kvm_cpuid_entry2 entries[]; }; /* for KVM_GET_PIT and KVM_SET_PIT */ @@ -306,7 +302,8 @@ struct kvm_pit_state { struct kvm_pit_channel_state channels[3]; }; -#define KVM_PIT_FLAGS_HPET_LEGACY 0x00000001 +#define KVM_PIT_FLAGS_HPET_LEGACY 0x00000001 +#define KVM_PIT_FLAGS_SPEAKER_DATA_ON 0x00000002 struct kvm_pit_state2 { struct kvm_pit_channel_state channels[3]; @@ -325,6 +322,7 @@ struct kvm_reinject_control { #define KVM_VCPUEVENT_VALID_SHADOW 0x00000004 #define KVM_VCPUEVENT_VALID_SMM 0x00000008 #define KVM_VCPUEVENT_VALID_PAYLOAD 0x00000010 +#define KVM_VCPUEVENT_VALID_TRIPLE_FAULT 0x00000020 /* Interrupt shadow states */ #define KVM_X86_SHADOW_INT_MOV_SS 0x01 @@ -359,7 +357,10 @@ struct kvm_vcpu_events { __u8 smm_inside_nmi; __u8 latched_init; } smi; - __u8 reserved[27]; + struct { + __u8 pending; + } triple_fault; + __u8 reserved[26]; __u8 exception_has_payload; __u64 exception_payload; }; @@ -389,7 +390,7 @@ struct kvm_xsave { * the contents of CPUID leaf 0xD on the host. */ __u32 region[1024]; - __u32 extra[0]; + __u32 extra[]; }; #define KVM_MAX_XCRS 16 @@ -428,11 +429,13 @@ struct kvm_sync_regs { struct kvm_vcpu_events events; }; -#define KVM_X86_QUIRK_LINT0_REENABLED (1 << 0) -#define KVM_X86_QUIRK_CD_NW_CLEARED (1 << 1) -#define KVM_X86_QUIRK_LAPIC_MMIO_HOLE (1 << 2) -#define KVM_X86_QUIRK_OUT_7E_INC_RIP (1 << 3) -#define KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT (1 << 4) +#define KVM_X86_QUIRK_LINT0_REENABLED (1 << 0) +#define KVM_X86_QUIRK_CD_NW_CLEARED (1 << 1) +#define KVM_X86_QUIRK_LAPIC_MMIO_HOLE (1 << 2) +#define KVM_X86_QUIRK_OUT_7E_INC_RIP (1 << 3) +#define KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT (1 << 4) +#define KVM_X86_QUIRK_FIX_HYPERCALL_INSN (1 << 5) +#define KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS (1 << 6) #define KVM_STATE_NESTED_FORMAT_VMX 0 #define KVM_STATE_NESTED_FORMAT_SVM 1 @@ -503,8 +506,8 @@ struct kvm_nested_state { * KVM_{GET,PUT}_NESTED_STATE ioctl values. */ union { - struct kvm_vmx_nested_state_data vmx[0]; - struct kvm_svm_nested_state_data svm[0]; + __DECLARE_FLEX_ARRAY(struct kvm_vmx_nested_state_data, vmx); + __DECLARE_FLEX_ARRAY(struct kvm_svm_nested_state_data, svm); } data; }; @@ -515,14 +518,49 @@ struct kvm_pmu_event_filter { __u32 fixed_counter_bitmap; __u32 flags; __u32 pad[4]; - __u64 events[0]; + __u64 events[]; }; #define KVM_PMU_EVENT_ALLOW 0 #define KVM_PMU_EVENT_DENY 1 +#define KVM_PMU_EVENT_FLAG_MASKED_EVENTS BIT(0) +#define KVM_PMU_EVENT_FLAGS_VALID_MASK (KVM_PMU_EVENT_FLAG_MASKED_EVENTS) + +/* + * Masked event layout. + * Bits Description + * ---- ----------- + * 7:0 event select (low bits) + * 15:8 umask match + * 31:16 unused + * 35:32 event select (high bits) + * 36:54 unused + * 55 exclude bit + * 63:56 umask mask + */ + +#define KVM_PMU_ENCODE_MASKED_ENTRY(event_select, mask, match, exclude) \ + (((event_select) & 0xFFULL) | (((event_select) & 0XF00ULL) << 24) | \ + (((mask) & 0xFFULL) << 56) | \ + (((match) & 0xFFULL) << 8) | \ + ((__u64)(!!(exclude)) << 55)) + +#define KVM_PMU_MASKED_ENTRY_EVENT_SELECT \ + (GENMASK_ULL(7, 0) | GENMASK_ULL(35, 32)) +#define KVM_PMU_MASKED_ENTRY_UMASK_MASK (GENMASK_ULL(63, 56)) +#define KVM_PMU_MASKED_ENTRY_UMASK_MATCH (GENMASK_ULL(15, 8)) +#define KVM_PMU_MASKED_ENTRY_EXCLUDE (BIT_ULL(55)) +#define KVM_PMU_MASKED_ENTRY_UMASK_MASK_SHIFT (56) + /* for KVM_{GET,SET,HAS}_DEVICE_ATTR */ #define KVM_VCPU_TSC_CTRL 0 /* control group for the timestamp counter (TSC) */ #define KVM_VCPU_TSC_OFFSET 0 /* attribute for the TSC offset */ +/* x86-specific KVM_EXIT_HYPERCALL flags. */ +#define KVM_EXIT_HYPERCALL_LONG_MODE BIT(0) + +#define KVM_X86_DEFAULT_VM 0 +#define KVM_X86_SW_PROTECTED_VM 1 + #endif /* _ASM_X86_KVM_H */ diff --git a/linux-headers/asm-x86/mman.h b/linux-headers/asm-x86/mman.h index d4a8d0424b..46cdc941f9 100644 --- a/linux-headers/asm-x86/mman.h +++ b/linux-headers/asm-x86/mman.h @@ -3,28 +3,10 @@ #define _ASM_X86_MMAN_H #define MAP_32BIT 0x40 /* only give out 32bit addresses */ +#define MAP_ABOVE4G 0x80 /* only map above 4GB */ -#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS -/* - * Take the 4 protection key bits out of the vma->vm_flags - * value and turn them in to the bits that we can put in - * to a pte. - * - * Only override these if Protection Keys are available - * (which is only on 64-bit). - */ -#define arch_vm_get_page_prot(vm_flags) __pgprot( \ - ((vm_flags) & VM_PKEY_BIT0 ? _PAGE_PKEY_BIT0 : 0) | \ - ((vm_flags) & VM_PKEY_BIT1 ? _PAGE_PKEY_BIT1 : 0) | \ - ((vm_flags) & VM_PKEY_BIT2 ? _PAGE_PKEY_BIT2 : 0) | \ - ((vm_flags) & VM_PKEY_BIT3 ? _PAGE_PKEY_BIT3 : 0)) - -#define arch_calc_vm_prot_bits(prot, key) ( \ - ((key) & 0x1 ? VM_PKEY_BIT0 : 0) | \ - ((key) & 0x2 ? VM_PKEY_BIT1 : 0) | \ - ((key) & 0x4 ? VM_PKEY_BIT2 : 0) | \ - ((key) & 0x8 ? VM_PKEY_BIT3 : 0)) -#endif +/* Flags for map_shadow_stack(2) */ +#define SHADOW_STACK_SET_TOKEN (1ULL << 0) /* Set up a restore token in the shadow stack */ #include diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h index 87e1e977af..5c9c329e93 100644 --- a/linux-headers/asm-x86/unistd_32.h +++ b/linux-headers/asm-x86/unistd_32.h @@ -441,6 +441,17 @@ #define __NR_process_mrelease 448 #define __NR_futex_waitv 449 #define __NR_set_mempolicy_home_node 450 +#define __NR_cachestat 451 +#define __NR_fchmodat2 452 +#define __NR_map_shadow_stack 453 +#define __NR_futex_wake 454 +#define __NR_futex_wait 455 +#define __NR_futex_requeue 456 +#define __NR_statmount 457 +#define __NR_listmount 458 +#define __NR_lsm_get_self_attr 459 +#define __NR_lsm_set_self_attr 460 +#define __NR_lsm_list_modules 461 #endif /* _ASM_UNISTD_32_H */ diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h index 147a78d623..d9aab7ae87 100644 --- a/linux-headers/asm-x86/unistd_64.h +++ b/linux-headers/asm-x86/unistd_64.h @@ -363,6 +363,17 @@ #define __NR_process_mrelease 448 #define __NR_futex_waitv 449 #define __NR_set_mempolicy_home_node 450 +#define __NR_cachestat 451 +#define __NR_fchmodat2 452 +#define __NR_map_shadow_stack 453 +#define __NR_futex_wake 454 +#define __NR_futex_wait 455 +#define __NR_futex_requeue 456 +#define __NR_statmount 457 +#define __NR_listmount 458 +#define __NR_lsm_get_self_attr 459 +#define __NR_lsm_set_self_attr 460 +#define __NR_lsm_list_modules 461 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h index 27098db7fb..63cdd1ee43 100644 --- a/linux-headers/asm-x86/unistd_x32.h +++ b/linux-headers/asm-x86/unistd_x32.h @@ -316,6 +316,16 @@ #define __NR_process_mrelease (__X32_SYSCALL_BIT + 448) #define __NR_futex_waitv (__X32_SYSCALL_BIT + 449) #define __NR_set_mempolicy_home_node (__X32_SYSCALL_BIT + 450) +#define __NR_cachestat (__X32_SYSCALL_BIT + 451) +#define __NR_fchmodat2 (__X32_SYSCALL_BIT + 452) +#define __NR_futex_wake (__X32_SYSCALL_BIT + 454) +#define __NR_futex_wait (__X32_SYSCALL_BIT + 455) +#define __NR_futex_requeue (__X32_SYSCALL_BIT + 456) +#define __NR_statmount (__X32_SYSCALL_BIT + 457) +#define __NR_listmount (__X32_SYSCALL_BIT + 458) +#define __NR_lsm_get_self_attr (__X32_SYSCALL_BIT + 459) +#define __NR_lsm_set_self_attr (__X32_SYSCALL_BIT + 460) +#define __NR_lsm_list_modules (__X32_SYSCALL_BIT + 461) #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/const.h b/linux-headers/linux/const.h new file mode 100644 index 0000000000..1eb84b5087 --- /dev/null +++ b/linux-headers/linux/const.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* const.h: Macros for dealing with constants. */ + +#ifndef _LINUX_CONST_H +#define _LINUX_CONST_H + +/* Some constant macros are used in both assembler and + * C code. Therefore we cannot annotate them always with + * 'UL' and other type specifiers unilaterally. We + * use the following macros to deal with this. + * + * Similarly, _AT() will cast an expression with a type in C, but + * leave it unchanged in asm. + */ + +#ifdef __ASSEMBLY__ +#define _AC(X,Y) X +#define _AT(T,X) X +#else +#define __AC(X,Y) (X##Y) +#define _AC(X,Y) __AC(X,Y) +#define _AT(T,X) ((T)(X)) +#endif + +#define _UL(x) (_AC(x, UL)) +#define _ULL(x) (_AC(x, ULL)) + +#define _BITUL(x) (_UL(1) << (x)) +#define _BITULL(x) (_ULL(1) << (x)) + +#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (__typeof__(x))(a) - 1) +#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) + +#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +#endif /* _LINUX_CONST_H */ diff --git a/linux-headers/linux/iommufd.h b/linux-headers/linux/iommufd.h new file mode 100644 index 0000000000..72e8f4b9dd --- /dev/null +++ b/linux-headers/linux/iommufd.h @@ -0,0 +1,695 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES. + */ +#ifndef _IOMMUFD_H +#define _IOMMUFD_H + +#include +#include + +#define IOMMUFD_TYPE (';') + +/** + * DOC: General ioctl format + * + * The ioctl interface follows a general format to allow for extensibility. Each + * ioctl is passed in a structure pointer as the argument providing the size of + * the structure in the first u32. The kernel checks that any structure space + * beyond what it understands is 0. This allows userspace to use the backward + * compatible portion while consistently using the newer, larger, structures. + * + * ioctls use a standard meaning for common errnos: + * + * - ENOTTY: The IOCTL number itself is not supported at all + * - E2BIG: The IOCTL number is supported, but the provided structure has + * non-zero in a part the kernel does not understand. + * - EOPNOTSUPP: The IOCTL number is supported, and the structure is + * understood, however a known field has a value the kernel does not + * understand or support. + * - EINVAL: Everything about the IOCTL was understood, but a field is not + * correct. + * - ENOENT: An ID or IOVA provided does not exist. + * - ENOMEM: Out of memory. + * - EOVERFLOW: Mathematics overflowed. + * + * As well as additional errnos, within specific ioctls. + */ +enum { + IOMMUFD_CMD_BASE = 0x80, + IOMMUFD_CMD_DESTROY = IOMMUFD_CMD_BASE, + IOMMUFD_CMD_IOAS_ALLOC, + IOMMUFD_CMD_IOAS_ALLOW_IOVAS, + IOMMUFD_CMD_IOAS_COPY, + IOMMUFD_CMD_IOAS_IOVA_RANGES, + IOMMUFD_CMD_IOAS_MAP, + IOMMUFD_CMD_IOAS_UNMAP, + IOMMUFD_CMD_OPTION, + IOMMUFD_CMD_VFIO_IOAS, + IOMMUFD_CMD_HWPT_ALLOC, + IOMMUFD_CMD_GET_HW_INFO, + IOMMUFD_CMD_HWPT_SET_DIRTY_TRACKING, + IOMMUFD_CMD_HWPT_GET_DIRTY_BITMAP, + IOMMUFD_CMD_HWPT_INVALIDATE, +}; + +/** + * struct iommu_destroy - ioctl(IOMMU_DESTROY) + * @size: sizeof(struct iommu_destroy) + * @id: iommufd object ID to destroy. Can be any destroyable object type. + * + * Destroy any object held within iommufd. + */ +struct iommu_destroy { + __u32 size; + __u32 id; +}; +#define IOMMU_DESTROY _IO(IOMMUFD_TYPE, IOMMUFD_CMD_DESTROY) + +/** + * struct iommu_ioas_alloc - ioctl(IOMMU_IOAS_ALLOC) + * @size: sizeof(struct iommu_ioas_alloc) + * @flags: Must be 0 + * @out_ioas_id: Output IOAS ID for the allocated object + * + * Allocate an IO Address Space (IOAS) which holds an IO Virtual Address (IOVA) + * to memory mapping. + */ +struct iommu_ioas_alloc { + __u32 size; + __u32 flags; + __u32 out_ioas_id; +}; +#define IOMMU_IOAS_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_ALLOC) + +/** + * struct iommu_iova_range - ioctl(IOMMU_IOVA_RANGE) + * @start: First IOVA + * @last: Inclusive last IOVA + * + * An interval in IOVA space. + */ +struct iommu_iova_range { + __aligned_u64 start; + __aligned_u64 last; +}; + +/** + * struct iommu_ioas_iova_ranges - ioctl(IOMMU_IOAS_IOVA_RANGES) + * @size: sizeof(struct iommu_ioas_iova_ranges) + * @ioas_id: IOAS ID to read ranges from + * @num_iovas: Input/Output total number of ranges in the IOAS + * @__reserved: Must be 0 + * @allowed_iovas: Pointer to the output array of struct iommu_iova_range + * @out_iova_alignment: Minimum alignment required for mapping IOVA + * + * Query an IOAS for ranges of allowed IOVAs. Mapping IOVA outside these ranges + * is not allowed. num_iovas will be set to the total number of iovas and + * the allowed_iovas[] will be filled in as space permits. + * + * The allowed ranges are dependent on the HW path the DMA operation takes, and + * can change during the lifetime of the IOAS. A fresh empty IOAS will have a + * full range, and each attached device will narrow the ranges based on that + * device's HW restrictions. Detaching a device can widen the ranges. Userspace + * should query ranges after every attach/detach to know what IOVAs are valid + * for mapping. + * + * On input num_iovas is the length of the allowed_iovas array. On output it is + * the total number of iovas filled in. The ioctl will return -EMSGSIZE and set + * num_iovas to the required value if num_iovas is too small. In this case the + * caller should allocate a larger output array and re-issue the ioctl. + * + * out_iova_alignment returns the minimum IOVA alignment that can be given + * to IOMMU_IOAS_MAP/COPY. IOVA's must satisfy:: + * + * starting_iova % out_iova_alignment == 0 + * (starting_iova + length) % out_iova_alignment == 0 + * + * out_iova_alignment can be 1 indicating any IOVA is allowed. It cannot + * be higher than the system PAGE_SIZE. + */ +struct iommu_ioas_iova_ranges { + __u32 size; + __u32 ioas_id; + __u32 num_iovas; + __u32 __reserved; + __aligned_u64 allowed_iovas; + __aligned_u64 out_iova_alignment; +}; +#define IOMMU_IOAS_IOVA_RANGES _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_IOVA_RANGES) + +/** + * struct iommu_ioas_allow_iovas - ioctl(IOMMU_IOAS_ALLOW_IOVAS) + * @size: sizeof(struct iommu_ioas_allow_iovas) + * @ioas_id: IOAS ID to allow IOVAs from + * @num_iovas: Input/Output total number of ranges in the IOAS + * @__reserved: Must be 0 + * @allowed_iovas: Pointer to array of struct iommu_iova_range + * + * Ensure a range of IOVAs are always available for allocation. If this call + * succeeds then IOMMU_IOAS_IOVA_RANGES will never return a list of IOVA ranges + * that are narrower than the ranges provided here. This call will fail if + * IOMMU_IOAS_IOVA_RANGES is currently narrower than the given ranges. + * + * When an IOAS is first created the IOVA_RANGES will be maximally sized, and as + * devices are attached the IOVA will narrow based on the device restrictions. + * When an allowed range is specified any narrowing will be refused, ie device + * attachment can fail if the device requires limiting within the allowed range. + * + * Automatic IOVA allocation is also impacted by this call. MAP will only + * allocate within the allowed IOVAs if they are present. + * + * This call replaces the entire allowed list with the given list. + */ +struct iommu_ioas_allow_iovas { + __u32 size; + __u32 ioas_id; + __u32 num_iovas; + __u32 __reserved; + __aligned_u64 allowed_iovas; +}; +#define IOMMU_IOAS_ALLOW_IOVAS _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_ALLOW_IOVAS) + +/** + * enum iommufd_ioas_map_flags - Flags for map and copy + * @IOMMU_IOAS_MAP_FIXED_IOVA: If clear the kernel will compute an appropriate + * IOVA to place the mapping at + * @IOMMU_IOAS_MAP_WRITEABLE: DMA is allowed to write to this mapping + * @IOMMU_IOAS_MAP_READABLE: DMA is allowed to read from this mapping + */ +enum iommufd_ioas_map_flags { + IOMMU_IOAS_MAP_FIXED_IOVA = 1 << 0, + IOMMU_IOAS_MAP_WRITEABLE = 1 << 1, + IOMMU_IOAS_MAP_READABLE = 1 << 2, +}; + +/** + * struct iommu_ioas_map - ioctl(IOMMU_IOAS_MAP) + * @size: sizeof(struct iommu_ioas_map) + * @flags: Combination of enum iommufd_ioas_map_flags + * @ioas_id: IOAS ID to change the mapping of + * @__reserved: Must be 0 + * @user_va: Userspace pointer to start mapping from + * @length: Number of bytes to map + * @iova: IOVA the mapping was placed at. If IOMMU_IOAS_MAP_FIXED_IOVA is set + * then this must be provided as input. + * + * Set an IOVA mapping from a user pointer. If FIXED_IOVA is specified then the + * mapping will be established at iova, otherwise a suitable location based on + * the reserved and allowed lists will be automatically selected and returned in + * iova. + * + * If IOMMU_IOAS_MAP_FIXED_IOVA is specified then the iova range must currently + * be unused, existing IOVA cannot be replaced. + */ +struct iommu_ioas_map { + __u32 size; + __u32 flags; + __u32 ioas_id; + __u32 __reserved; + __aligned_u64 user_va; + __aligned_u64 length; + __aligned_u64 iova; +}; +#define IOMMU_IOAS_MAP _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_MAP) + +/** + * struct iommu_ioas_copy - ioctl(IOMMU_IOAS_COPY) + * @size: sizeof(struct iommu_ioas_copy) + * @flags: Combination of enum iommufd_ioas_map_flags + * @dst_ioas_id: IOAS ID to change the mapping of + * @src_ioas_id: IOAS ID to copy from + * @length: Number of bytes to copy and map + * @dst_iova: IOVA the mapping was placed at. If IOMMU_IOAS_MAP_FIXED_IOVA is + * set then this must be provided as input. + * @src_iova: IOVA to start the copy + * + * Copy an already existing mapping from src_ioas_id and establish it in + * dst_ioas_id. The src iova/length must exactly match a range used with + * IOMMU_IOAS_MAP. + * + * This may be used to efficiently clone a subset of an IOAS to another, or as a + * kind of 'cache' to speed up mapping. Copy has an efficiency advantage over + * establishing equivalent new mappings, as internal resources are shared, and + * the kernel will pin the user memory only once. + */ +struct iommu_ioas_copy { + __u32 size; + __u32 flags; + __u32 dst_ioas_id; + __u32 src_ioas_id; + __aligned_u64 length; + __aligned_u64 dst_iova; + __aligned_u64 src_iova; +}; +#define IOMMU_IOAS_COPY _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_COPY) + +/** + * struct iommu_ioas_unmap - ioctl(IOMMU_IOAS_UNMAP) + * @size: sizeof(struct iommu_ioas_unmap) + * @ioas_id: IOAS ID to change the mapping of + * @iova: IOVA to start the unmapping at + * @length: Number of bytes to unmap, and return back the bytes unmapped + * + * Unmap an IOVA range. The iova/length must be a superset of a previously + * mapped range used with IOMMU_IOAS_MAP or IOMMU_IOAS_COPY. Splitting or + * truncating ranges is not allowed. The values 0 to U64_MAX will unmap + * everything. + */ +struct iommu_ioas_unmap { + __u32 size; + __u32 ioas_id; + __aligned_u64 iova; + __aligned_u64 length; +}; +#define IOMMU_IOAS_UNMAP _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_UNMAP) + +/** + * enum iommufd_option - ioctl(IOMMU_OPTION_RLIMIT_MODE) and + * ioctl(IOMMU_OPTION_HUGE_PAGES) + * @IOMMU_OPTION_RLIMIT_MODE: + * Change how RLIMIT_MEMLOCK accounting works. The caller must have privilege + * to invoke this. Value 0 (default) is user based accouting, 1 uses process + * based accounting. Global option, object_id must be 0 + * @IOMMU_OPTION_HUGE_PAGES: + * Value 1 (default) allows contiguous pages to be combined when generating + * iommu mappings. Value 0 disables combining, everything is mapped to + * PAGE_SIZE. This can be useful for benchmarking. This is a per-IOAS + * option, the object_id must be the IOAS ID. + */ +enum iommufd_option { + IOMMU_OPTION_RLIMIT_MODE = 0, + IOMMU_OPTION_HUGE_PAGES = 1, +}; + +/** + * enum iommufd_option_ops - ioctl(IOMMU_OPTION_OP_SET) and + * ioctl(IOMMU_OPTION_OP_GET) + * @IOMMU_OPTION_OP_SET: Set the option's value + * @IOMMU_OPTION_OP_GET: Get the option's value + */ +enum iommufd_option_ops { + IOMMU_OPTION_OP_SET = 0, + IOMMU_OPTION_OP_GET = 1, +}; + +/** + * struct iommu_option - iommu option multiplexer + * @size: sizeof(struct iommu_option) + * @option_id: One of enum iommufd_option + * @op: One of enum iommufd_option_ops + * @__reserved: Must be 0 + * @object_id: ID of the object if required + * @val64: Option value to set or value returned on get + * + * Change a simple option value. This multiplexor allows controlling options + * on objects. IOMMU_OPTION_OP_SET will load an option and IOMMU_OPTION_OP_GET + * will return the current value. + */ +struct iommu_option { + __u32 size; + __u32 option_id; + __u16 op; + __u16 __reserved; + __u32 object_id; + __aligned_u64 val64; +}; +#define IOMMU_OPTION _IO(IOMMUFD_TYPE, IOMMUFD_CMD_OPTION) + +/** + * enum iommufd_vfio_ioas_op - IOMMU_VFIO_IOAS_* ioctls + * @IOMMU_VFIO_IOAS_GET: Get the current compatibility IOAS + * @IOMMU_VFIO_IOAS_SET: Change the current compatibility IOAS + * @IOMMU_VFIO_IOAS_CLEAR: Disable VFIO compatibility + */ +enum iommufd_vfio_ioas_op { + IOMMU_VFIO_IOAS_GET = 0, + IOMMU_VFIO_IOAS_SET = 1, + IOMMU_VFIO_IOAS_CLEAR = 2, +}; + +/** + * struct iommu_vfio_ioas - ioctl(IOMMU_VFIO_IOAS) + * @size: sizeof(struct iommu_vfio_ioas) + * @ioas_id: For IOMMU_VFIO_IOAS_SET the input IOAS ID to set + * For IOMMU_VFIO_IOAS_GET will output the IOAS ID + * @op: One of enum iommufd_vfio_ioas_op + * @__reserved: Must be 0 + * + * The VFIO compatibility support uses a single ioas because VFIO APIs do not + * support the ID field. Set or Get the IOAS that VFIO compatibility will use. + * When VFIO_GROUP_SET_CONTAINER is used on an iommufd it will get the + * compatibility ioas, either by taking what is already set, or auto creating + * one. From then on VFIO will continue to use that ioas and is not effected by + * this ioctl. SET or CLEAR does not destroy any auto-created IOAS. + */ +struct iommu_vfio_ioas { + __u32 size; + __u32 ioas_id; + __u16 op; + __u16 __reserved; +}; +#define IOMMU_VFIO_IOAS _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VFIO_IOAS) + +/** + * enum iommufd_hwpt_alloc_flags - Flags for HWPT allocation + * @IOMMU_HWPT_ALLOC_NEST_PARENT: If set, allocate a HWPT that can serve as + * the parent HWPT in a nesting configuration. + * @IOMMU_HWPT_ALLOC_DIRTY_TRACKING: Dirty tracking support for device IOMMU is + * enforced on device attachment + */ +enum iommufd_hwpt_alloc_flags { + IOMMU_HWPT_ALLOC_NEST_PARENT = 1 << 0, + IOMMU_HWPT_ALLOC_DIRTY_TRACKING = 1 << 1, +}; + +/** + * enum iommu_hwpt_vtd_s1_flags - Intel VT-d stage-1 page table + * entry attributes + * @IOMMU_VTD_S1_SRE: Supervisor request + * @IOMMU_VTD_S1_EAFE: Extended access enable + * @IOMMU_VTD_S1_WPE: Write protect enable + */ +enum iommu_hwpt_vtd_s1_flags { + IOMMU_VTD_S1_SRE = 1 << 0, + IOMMU_VTD_S1_EAFE = 1 << 1, + IOMMU_VTD_S1_WPE = 1 << 2, +}; + +/** + * struct iommu_hwpt_vtd_s1 - Intel VT-d stage-1 page table + * info (IOMMU_HWPT_DATA_VTD_S1) + * @flags: Combination of enum iommu_hwpt_vtd_s1_flags + * @pgtbl_addr: The base address of the stage-1 page table. + * @addr_width: The address width of the stage-1 page table + * @__reserved: Must be 0 + */ +struct iommu_hwpt_vtd_s1 { + __aligned_u64 flags; + __aligned_u64 pgtbl_addr; + __u32 addr_width; + __u32 __reserved; +}; + +/** + * enum iommu_hwpt_data_type - IOMMU HWPT Data Type + * @IOMMU_HWPT_DATA_NONE: no data + * @IOMMU_HWPT_DATA_VTD_S1: Intel VT-d stage-1 page table + */ +enum iommu_hwpt_data_type { + IOMMU_HWPT_DATA_NONE, + IOMMU_HWPT_DATA_VTD_S1, +}; + +/** + * struct iommu_hwpt_alloc - ioctl(IOMMU_HWPT_ALLOC) + * @size: sizeof(struct iommu_hwpt_alloc) + * @flags: Combination of enum iommufd_hwpt_alloc_flags + * @dev_id: The device to allocate this HWPT for + * @pt_id: The IOAS or HWPT to connect this HWPT to + * @out_hwpt_id: The ID of the new HWPT + * @__reserved: Must be 0 + * @data_type: One of enum iommu_hwpt_data_type + * @data_len: Length of the type specific data + * @data_uptr: User pointer to the type specific data + * + * Explicitly allocate a hardware page table object. This is the same object + * type that is returned by iommufd_device_attach() and represents the + * underlying iommu driver's iommu_domain kernel object. + * + * A kernel-managed HWPT will be created with the mappings from the given + * IOAS via the @pt_id. The @data_type for this allocation must be set to + * IOMMU_HWPT_DATA_NONE. The HWPT can be allocated as a parent HWPT for a + * nesting configuration by passing IOMMU_HWPT_ALLOC_NEST_PARENT via @flags. + * + * A user-managed nested HWPT will be created from a given parent HWPT via + * @pt_id, in which the parent HWPT must be allocated previously via the + * same ioctl from a given IOAS (@pt_id). In this case, the @data_type + * must be set to a pre-defined type corresponding to an I/O page table + * type supported by the underlying IOMMU hardware. + * + * If the @data_type is set to IOMMU_HWPT_DATA_NONE, @data_len and + * @data_uptr should be zero. Otherwise, both @data_len and @data_uptr + * must be given. + */ +struct iommu_hwpt_alloc { + __u32 size; + __u32 flags; + __u32 dev_id; + __u32 pt_id; + __u32 out_hwpt_id; + __u32 __reserved; + __u32 data_type; + __u32 data_len; + __aligned_u64 data_uptr; +}; +#define IOMMU_HWPT_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_HWPT_ALLOC) + +/** + * enum iommu_hw_info_vtd_flags - Flags for VT-d hw_info + * @IOMMU_HW_INFO_VTD_ERRATA_772415_SPR17: If set, disallow read-only mappings + * on a nested_parent domain. + * https://www.intel.com/content/www/us/en/content-details/772415/content-details.html + */ +enum iommu_hw_info_vtd_flags { + IOMMU_HW_INFO_VTD_ERRATA_772415_SPR17 = 1 << 0, +}; + +/** + * struct iommu_hw_info_vtd - Intel VT-d hardware information + * + * @flags: Combination of enum iommu_hw_info_vtd_flags + * @__reserved: Must be 0 + * + * @cap_reg: Value of Intel VT-d capability register defined in VT-d spec + * section 11.4.2 Capability Register. + * @ecap_reg: Value of Intel VT-d capability register defined in VT-d spec + * section 11.4.3 Extended Capability Register. + * + * User needs to understand the Intel VT-d specification to decode the + * register value. + */ +struct iommu_hw_info_vtd { + __u32 flags; + __u32 __reserved; + __aligned_u64 cap_reg; + __aligned_u64 ecap_reg; +}; + +/** + * enum iommu_hw_info_type - IOMMU Hardware Info Types + * @IOMMU_HW_INFO_TYPE_NONE: Used by the drivers that do not report hardware + * info + * @IOMMU_HW_INFO_TYPE_INTEL_VTD: Intel VT-d iommu info type + */ +enum iommu_hw_info_type { + IOMMU_HW_INFO_TYPE_NONE, + IOMMU_HW_INFO_TYPE_INTEL_VTD, +}; + +/** + * enum iommufd_hw_capabilities + * @IOMMU_HW_CAP_DIRTY_TRACKING: IOMMU hardware support for dirty tracking + * If available, it means the following APIs + * are supported: + * + * IOMMU_HWPT_GET_DIRTY_BITMAP + * IOMMU_HWPT_SET_DIRTY_TRACKING + * + */ +enum iommufd_hw_capabilities { + IOMMU_HW_CAP_DIRTY_TRACKING = 1 << 0, +}; + +/** + * struct iommu_hw_info - ioctl(IOMMU_GET_HW_INFO) + * @size: sizeof(struct iommu_hw_info) + * @flags: Must be 0 + * @dev_id: The device bound to the iommufd + * @data_len: Input the length of a user buffer in bytes. Output the length of + * data that kernel supports + * @data_uptr: User pointer to a user-space buffer used by the kernel to fill + * the iommu type specific hardware information data + * @out_data_type: Output the iommu hardware info type as defined in the enum + * iommu_hw_info_type. + * @out_capabilities: Output the generic iommu capability info type as defined + * in the enum iommu_hw_capabilities. + * @__reserved: Must be 0 + * + * Query an iommu type specific hardware information data from an iommu behind + * a given device that has been bound to iommufd. This hardware info data will + * be used to sync capabilities between the virtual iommu and the physical + * iommu, e.g. a nested translation setup needs to check the hardware info, so + * a guest stage-1 page table can be compatible with the physical iommu. + * + * To capture an iommu type specific hardware information data, @data_uptr and + * its length @data_len must be provided. Trailing bytes will be zeroed if the + * user buffer is larger than the data that kernel has. Otherwise, kernel only + * fills the buffer using the given length in @data_len. If the ioctl succeeds, + * @data_len will be updated to the length that kernel actually supports, + * @out_data_type will be filled to decode the data filled in the buffer + * pointed by @data_uptr. Input @data_len == zero is allowed. + */ +struct iommu_hw_info { + __u32 size; + __u32 flags; + __u32 dev_id; + __u32 data_len; + __aligned_u64 data_uptr; + __u32 out_data_type; + __u32 __reserved; + __aligned_u64 out_capabilities; +}; +#define IOMMU_GET_HW_INFO _IO(IOMMUFD_TYPE, IOMMUFD_CMD_GET_HW_INFO) + +/* + * enum iommufd_hwpt_set_dirty_tracking_flags - Flags for steering dirty + * tracking + * @IOMMU_HWPT_DIRTY_TRACKING_ENABLE: Enable dirty tracking + */ +enum iommufd_hwpt_set_dirty_tracking_flags { + IOMMU_HWPT_DIRTY_TRACKING_ENABLE = 1, +}; + +/** + * struct iommu_hwpt_set_dirty_tracking - ioctl(IOMMU_HWPT_SET_DIRTY_TRACKING) + * @size: sizeof(struct iommu_hwpt_set_dirty_tracking) + * @flags: Combination of enum iommufd_hwpt_set_dirty_tracking_flags + * @hwpt_id: HW pagetable ID that represents the IOMMU domain + * @__reserved: Must be 0 + * + * Toggle dirty tracking on an HW pagetable. + */ +struct iommu_hwpt_set_dirty_tracking { + __u32 size; + __u32 flags; + __u32 hwpt_id; + __u32 __reserved; +}; +#define IOMMU_HWPT_SET_DIRTY_TRACKING _IO(IOMMUFD_TYPE, \ + IOMMUFD_CMD_HWPT_SET_DIRTY_TRACKING) + +/** + * enum iommufd_hwpt_get_dirty_bitmap_flags - Flags for getting dirty bits + * @IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR: Just read the PTEs without clearing + * any dirty bits metadata. This flag + * can be passed in the expectation + * where the next operation is an unmap + * of the same IOVA range. + * + */ +enum iommufd_hwpt_get_dirty_bitmap_flags { + IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR = 1, +}; + +/** + * struct iommu_hwpt_get_dirty_bitmap - ioctl(IOMMU_HWPT_GET_DIRTY_BITMAP) + * @size: sizeof(struct iommu_hwpt_get_dirty_bitmap) + * @hwpt_id: HW pagetable ID that represents the IOMMU domain + * @flags: Combination of enum iommufd_hwpt_get_dirty_bitmap_flags + * @__reserved: Must be 0 + * @iova: base IOVA of the bitmap first bit + * @length: IOVA range size + * @page_size: page size granularity of each bit in the bitmap + * @data: bitmap where to set the dirty bits. The bitmap bits each + * represent a page_size which you deviate from an arbitrary iova. + * + * Checking a given IOVA is dirty: + * + * data[(iova / page_size) / 64] & (1ULL << ((iova / page_size) % 64)) + * + * Walk the IOMMU pagetables for a given IOVA range to return a bitmap + * with the dirty IOVAs. In doing so it will also by default clear any + * dirty bit metadata set in the IOPTE. + */ +struct iommu_hwpt_get_dirty_bitmap { + __u32 size; + __u32 hwpt_id; + __u32 flags; + __u32 __reserved; + __aligned_u64 iova; + __aligned_u64 length; + __aligned_u64 page_size; + __aligned_u64 data; +}; +#define IOMMU_HWPT_GET_DIRTY_BITMAP _IO(IOMMUFD_TYPE, \ + IOMMUFD_CMD_HWPT_GET_DIRTY_BITMAP) + +/** + * enum iommu_hwpt_invalidate_data_type - IOMMU HWPT Cache Invalidation + * Data Type + * @IOMMU_HWPT_INVALIDATE_DATA_VTD_S1: Invalidation data for VTD_S1 + */ +enum iommu_hwpt_invalidate_data_type { + IOMMU_HWPT_INVALIDATE_DATA_VTD_S1, +}; + +/** + * enum iommu_hwpt_vtd_s1_invalidate_flags - Flags for Intel VT-d + * stage-1 cache invalidation + * @IOMMU_VTD_INV_FLAGS_LEAF: Indicates whether the invalidation applies + * to all-levels page structure cache or just + * the leaf PTE cache. + */ +enum iommu_hwpt_vtd_s1_invalidate_flags { + IOMMU_VTD_INV_FLAGS_LEAF = 1 << 0, +}; + +/** + * struct iommu_hwpt_vtd_s1_invalidate - Intel VT-d cache invalidation + * (IOMMU_HWPT_INVALIDATE_DATA_VTD_S1) + * @addr: The start address of the range to be invalidated. It needs to + * be 4KB aligned. + * @npages: Number of contiguous 4K pages to be invalidated. + * @flags: Combination of enum iommu_hwpt_vtd_s1_invalidate_flags + * @__reserved: Must be 0 + * + * The Intel VT-d specific invalidation data for user-managed stage-1 cache + * invalidation in nested translation. Userspace uses this structure to + * tell the impacted cache scope after modifying the stage-1 page table. + * + * Invalidating all the caches related to the page table by setting @addr + * to be 0 and @npages to be U64_MAX. + * + * The device TLB will be invalidated automatically if ATS is enabled. + */ +struct iommu_hwpt_vtd_s1_invalidate { + __aligned_u64 addr; + __aligned_u64 npages; + __u32 flags; + __u32 __reserved; +}; + +/** + * struct iommu_hwpt_invalidate - ioctl(IOMMU_HWPT_INVALIDATE) + * @size: sizeof(struct iommu_hwpt_invalidate) + * @hwpt_id: ID of a nested HWPT for cache invalidation + * @data_uptr: User pointer to an array of driver-specific cache invalidation + * data. + * @data_type: One of enum iommu_hwpt_invalidate_data_type, defining the data + * type of all the entries in the invalidation request array. It + * should be a type supported by the hwpt pointed by @hwpt_id. + * @entry_len: Length (in bytes) of a request entry in the request array + * @entry_num: Input the number of cache invalidation requests in the array. + * Output the number of requests successfully handled by kernel. + * @__reserved: Must be 0. + * + * Invalidate the iommu cache for user-managed page table. Modifications on a + * user-managed page table should be followed by this operation to sync cache. + * Each ioctl can support one or more cache invalidation requests in the array + * that has a total size of @entry_len * @entry_num. + * + * An empty invalidation request array by setting @entry_num==0 is allowed, and + * @entry_len and @data_uptr would be ignored in this case. This can be used to + * check if the given @data_type is supported or not by kernel. + */ +struct iommu_hwpt_invalidate { + __u32 size; + __u32 hwpt_id; + __aligned_u64 data_uptr; + __u32 data_type; + __u32 entry_len; + __u32 entry_num; + __u32 __reserved; +}; +#define IOMMU_HWPT_INVALIDATE _IO(IOMMUFD_TYPE, IOMMUFD_CMD_HWPT_INVALIDATE) +#endif diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 0d05d02ee4..17839229b2 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -16,84 +16,6 @@ #define KVM_API_VERSION 12 -/* *** Deprecated interfaces *** */ - -#define KVM_TRC_SHIFT 16 - -#define KVM_TRC_ENTRYEXIT (1 << KVM_TRC_SHIFT) -#define KVM_TRC_HANDLER (1 << (KVM_TRC_SHIFT + 1)) - -#define KVM_TRC_VMENTRY (KVM_TRC_ENTRYEXIT + 0x01) -#define KVM_TRC_VMEXIT (KVM_TRC_ENTRYEXIT + 0x02) -#define KVM_TRC_PAGE_FAULT (KVM_TRC_HANDLER + 0x01) - -#define KVM_TRC_HEAD_SIZE 12 -#define KVM_TRC_CYCLE_SIZE 8 -#define KVM_TRC_EXTRA_MAX 7 - -#define KVM_TRC_INJ_VIRQ (KVM_TRC_HANDLER + 0x02) -#define KVM_TRC_REDELIVER_EVT (KVM_TRC_HANDLER + 0x03) -#define KVM_TRC_PEND_INTR (KVM_TRC_HANDLER + 0x04) -#define KVM_TRC_IO_READ (KVM_TRC_HANDLER + 0x05) -#define KVM_TRC_IO_WRITE (KVM_TRC_HANDLER + 0x06) -#define KVM_TRC_CR_READ (KVM_TRC_HANDLER + 0x07) -#define KVM_TRC_CR_WRITE (KVM_TRC_HANDLER + 0x08) -#define KVM_TRC_DR_READ (KVM_TRC_HANDLER + 0x09) -#define KVM_TRC_DR_WRITE (KVM_TRC_HANDLER + 0x0A) -#define KVM_TRC_MSR_READ (KVM_TRC_HANDLER + 0x0B) -#define KVM_TRC_MSR_WRITE (KVM_TRC_HANDLER + 0x0C) -#define KVM_TRC_CPUID (KVM_TRC_HANDLER + 0x0D) -#define KVM_TRC_INTR (KVM_TRC_HANDLER + 0x0E) -#define KVM_TRC_NMI (KVM_TRC_HANDLER + 0x0F) -#define KVM_TRC_VMMCALL (KVM_TRC_HANDLER + 0x10) -#define KVM_TRC_HLT (KVM_TRC_HANDLER + 0x11) -#define KVM_TRC_CLTS (KVM_TRC_HANDLER + 0x12) -#define KVM_TRC_LMSW (KVM_TRC_HANDLER + 0x13) -#define KVM_TRC_APIC_ACCESS (KVM_TRC_HANDLER + 0x14) -#define KVM_TRC_TDP_FAULT (KVM_TRC_HANDLER + 0x15) -#define KVM_TRC_GTLB_WRITE (KVM_TRC_HANDLER + 0x16) -#define KVM_TRC_STLB_WRITE (KVM_TRC_HANDLER + 0x17) -#define KVM_TRC_STLB_INVAL (KVM_TRC_HANDLER + 0x18) -#define KVM_TRC_PPC_INSTR (KVM_TRC_HANDLER + 0x19) - -struct kvm_user_trace_setup { - __u32 buf_size; - __u32 buf_nr; -}; - -#define __KVM_DEPRECATED_MAIN_W_0x06 \ - _IOW(KVMIO, 0x06, struct kvm_user_trace_setup) -#define __KVM_DEPRECATED_MAIN_0x07 _IO(KVMIO, 0x07) -#define __KVM_DEPRECATED_MAIN_0x08 _IO(KVMIO, 0x08) - -#define __KVM_DEPRECATED_VM_R_0x70 _IOR(KVMIO, 0x70, struct kvm_assigned_irq) - -struct kvm_breakpoint { - __u32 enabled; - __u32 padding; - __u64 address; -}; - -struct kvm_debug_guest { - __u32 enabled; - __u32 pad; - struct kvm_breakpoint breakpoints[4]; - __u32 singlestep; -}; - -#define __KVM_DEPRECATED_VCPU_W_0x87 _IOW(KVMIO, 0x87, struct kvm_debug_guest) - -/* *** End of deprecated interfaces *** */ - - -/* for KVM_CREATE_MEMORY_REGION */ -struct kvm_memory_region { - __u32 slot; - __u32 flags; - __u64 guest_phys_addr; - __u64 memory_size; /* bytes */ -}; - /* for KVM_SET_USER_MEMORY_REGION */ struct kvm_userspace_memory_region { __u32 slot; @@ -103,13 +25,27 @@ struct kvm_userspace_memory_region { __u64 userspace_addr; /* start of the userspace allocated memory */ }; +/* for KVM_SET_USER_MEMORY_REGION2 */ +struct kvm_userspace_memory_region2 { + __u32 slot; + __u32 flags; + __u64 guest_phys_addr; + __u64 memory_size; + __u64 userspace_addr; + __u64 guest_memfd_offset; + __u32 guest_memfd; + __u32 pad1; + __u64 pad2[14]; +}; + /* - * The bit 0 ~ bit 15 of kvm_memory_region::flags are visible for userspace, - * other bits are reserved for kvm internal use which are defined in - * include/linux/kvm_host.h. + * The bit 0 ~ bit 15 of kvm_userspace_memory_region::flags are visible for + * userspace, other bits are reserved for kvm internal use which are defined + * in include/linux/kvm_host.h. */ #define KVM_MEM_LOG_DIRTY_PAGES (1UL << 0) #define KVM_MEM_READONLY (1UL << 1) +#define KVM_MEM_GUEST_MEMFD (1UL << 2) /* for KVM_IRQ_LINE */ struct kvm_irq_level { @@ -270,6 +206,10 @@ struct kvm_xen_exit { #define KVM_EXIT_X86_BUS_LOCK 33 #define KVM_EXIT_XEN 34 #define KVM_EXIT_RISCV_SBI 35 +#define KVM_EXIT_RISCV_CSR 36 +#define KVM_EXIT_NOTIFY 37 +#define KVM_EXIT_LOONGARCH_IOCSR 38 +#define KVM_EXIT_MEMORY_FAULT 39 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -342,13 +282,23 @@ struct kvm_run { __u32 len; __u8 is_write; } mmio; + /* KVM_EXIT_LOONGARCH_IOCSR */ + struct { + __u64 phys_addr; + __u8 data[8]; + __u32 len; + __u8 is_write; + } iocsr_io; /* KVM_EXIT_HYPERCALL */ struct { __u64 nr; __u64 args[6]; __u64 ret; - __u32 longmode; - __u32 pad; + + union { + __u32 longmode; + __u64 flags; + }; } hypercall; /* KVM_EXIT_TPR_ACCESS */ struct { @@ -444,6 +394,9 @@ struct kvm_run { #define KVM_SYSTEM_EVENT_SHUTDOWN 1 #define KVM_SYSTEM_EVENT_RESET 2 #define KVM_SYSTEM_EVENT_CRASH 3 +#define KVM_SYSTEM_EVENT_WAKEUP 4 +#define KVM_SYSTEM_EVENT_SUSPEND 5 +#define KVM_SYSTEM_EVENT_SEV_TERM 6 __u32 type; __u32 ndata; union { @@ -478,6 +431,9 @@ struct kvm_run { #define KVM_MSR_EXIT_REASON_INVAL (1 << 0) #define KVM_MSR_EXIT_REASON_UNKNOWN (1 << 1) #define KVM_MSR_EXIT_REASON_FILTER (1 << 2) +#define KVM_MSR_EXIT_REASON_VALID_MASK (KVM_MSR_EXIT_REASON_INVAL | \ + KVM_MSR_EXIT_REASON_UNKNOWN | \ + KVM_MSR_EXIT_REASON_FILTER) __u32 reason; /* kernel -> user */ __u32 index; /* kernel -> user */ __u64 data; /* kernel <-> user */ @@ -491,6 +447,25 @@ struct kvm_run { unsigned long args[6]; unsigned long ret[2]; } riscv_sbi; + /* KVM_EXIT_RISCV_CSR */ + struct { + unsigned long csr_num; + unsigned long new_value; + unsigned long write_mask; + unsigned long ret_value; + } riscv_csr; + /* KVM_EXIT_NOTIFY */ + struct { +#define KVM_NOTIFY_CONTEXT_INVALID (1 << 0) + __u32 flags; + } notify; + /* KVM_EXIT_MEMORY_FAULT */ + struct { +#define KVM_MEMORY_EXIT_FLAG_PRIVATE (1ULL << 3) + __u64 flags; + __u64 gpa; + __u64 size; + } memory_fault; /* Fix the size of the union. */ char padding[256]; }; @@ -537,7 +512,7 @@ struct kvm_coalesced_mmio { struct kvm_coalesced_mmio_ring { __u32 first, last; - struct kvm_coalesced_mmio coalesced_mmio[0]; + struct kvm_coalesced_mmio coalesced_mmio[]; }; #define KVM_COALESCED_MMIO_MAX \ @@ -569,6 +544,8 @@ struct kvm_s390_mem_op { struct { __u8 ar; /* the access register number */ __u8 key; /* access key, ignored if flag unset */ + __u8 pad1[6]; /* ignored */ + __u64 old_addr; /* ignored if cmpxchg flag unset */ }; __u32 sida_offset; /* offset into the sida */ __u8 reserved[32]; /* ignored */ @@ -581,11 +558,17 @@ struct kvm_s390_mem_op { #define KVM_S390_MEMOP_SIDA_WRITE 3 #define KVM_S390_MEMOP_ABSOLUTE_READ 4 #define KVM_S390_MEMOP_ABSOLUTE_WRITE 5 +#define KVM_S390_MEMOP_ABSOLUTE_CMPXCHG 6 + /* flags for kvm_s390_mem_op->flags */ #define KVM_S390_MEMOP_F_CHECK_ONLY (1ULL << 0) #define KVM_S390_MEMOP_F_INJECT_EXCEPTION (1ULL << 1) #define KVM_S390_MEMOP_F_SKEY_PROTECTION (1ULL << 2) +/* flags specifying extension support via KVM_CAP_S390_MEM_OP_EXTENSION */ +#define KVM_S390_MEMOP_EXTENSION_CAP_BASE (1 << 0) +#define KVM_S390_MEMOP_EXTENSION_CAP_CMPXCHG (1 << 1) + /* for KVM_INTERRUPT */ struct kvm_interrupt { /* in */ @@ -616,7 +599,7 @@ struct kvm_clear_dirty_log { /* for KVM_SET_SIGNAL_MASK */ struct kvm_signal_mask { __u32 len; - __u8 sigset[0]; + __u8 sigset[]; }; /* for KVM_TPR_ACCESS_REPORTING */ @@ -644,6 +627,7 @@ struct kvm_vapic_addr { #define KVM_MP_STATE_OPERATING 7 #define KVM_MP_STATE_LOAD 8 #define KVM_MP_STATE_AP_RESET_HOLD 9 +#define KVM_MP_STATE_SUSPENDED 10 struct kvm_mp_state { __u32 mp_state; @@ -909,9 +893,6 @@ struct kvm_ppc_resize_hpt { */ #define KVM_GET_VCPU_MMAP_SIZE _IO(KVMIO, 0x04) /* in bytes */ #define KVM_GET_SUPPORTED_CPUID _IOWR(KVMIO, 0x05, struct kvm_cpuid2) -#define KVM_TRACE_ENABLE __KVM_DEPRECATED_MAIN_W_0x06 -#define KVM_TRACE_PAUSE __KVM_DEPRECATED_MAIN_0x07 -#define KVM_TRACE_DISABLE __KVM_DEPRECATED_MAIN_0x08 #define KVM_GET_EMULATED_CPUID _IOWR(KVMIO, 0x09, struct kvm_cpuid2) #define KVM_GET_MSR_FEATURE_INDEX_LIST _IOWR(KVMIO, 0x0a, struct kvm_msr_list) @@ -1148,8 +1129,28 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_S390_MEM_OP_EXTENSION 211 #define KVM_CAP_PMU_CAPABILITY 212 #define KVM_CAP_DISABLE_QUIRKS2 213 -/* #define KVM_CAP_VM_TSC_CONTROL 214 */ +#define KVM_CAP_VM_TSC_CONTROL 214 #define KVM_CAP_SYSTEM_EVENT_DATA 215 +#define KVM_CAP_ARM_SYSTEM_SUSPEND 216 +#define KVM_CAP_S390_PROTECTED_DUMP 217 +#define KVM_CAP_X86_TRIPLE_FAULT_EVENT 218 +#define KVM_CAP_X86_NOTIFY_VMEXIT 219 +#define KVM_CAP_VM_DISABLE_NX_HUGE_PAGES 220 +#define KVM_CAP_S390_ZPCI_OP 221 +#define KVM_CAP_S390_CPU_TOPOLOGY 222 +#define KVM_CAP_DIRTY_LOG_RING_ACQ_REL 223 +#define KVM_CAP_S390_PROTECTED_ASYNC_DISABLE 224 +#define KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP 225 +#define KVM_CAP_PMU_EVENT_MASKED_EVENTS 226 +#define KVM_CAP_COUNTER_OFFSET 227 +#define KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE 228 +#define KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES 229 +#define KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES 230 +#define KVM_CAP_USER_MEMORY2 231 +#define KVM_CAP_MEMORY_FAULT_INFO 232 +#define KVM_CAP_MEMORY_ATTRIBUTES 233 +#define KVM_CAP_GUEST_MEMFD 234 +#define KVM_CAP_VM_TYPES 235 #ifdef KVM_CAP_IRQ_ROUTING @@ -1214,7 +1215,7 @@ struct kvm_irq_routing_entry { struct kvm_irq_routing { __u32 nr; __u32 flags; - struct kvm_irq_routing_entry entries[0]; + struct kvm_irq_routing_entry entries[]; }; #endif @@ -1238,6 +1239,9 @@ struct kvm_x86_mce { #define KVM_XEN_HVM_CONFIG_SHARED_INFO (1 << 2) #define KVM_XEN_HVM_CONFIG_RUNSTATE (1 << 3) #define KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL (1 << 4) +#define KVM_XEN_HVM_CONFIG_EVTCHN_SEND (1 << 5) +#define KVM_XEN_HVM_CONFIG_RUNSTATE_UPDATE_FLAG (1 << 6) +#define KVM_XEN_HVM_CONFIG_PVCLOCK_TSC_UNSTABLE (1 << 7) struct kvm_xen_hvm_config { __u32 flags; @@ -1318,6 +1322,7 @@ struct kvm_dirty_tlb { #define KVM_REG_ARM64 0x6000000000000000ULL #define KVM_REG_MIPS 0x7000000000000000ULL #define KVM_REG_RISCV 0x8000000000000000ULL +#define KVM_REG_LOONGARCH 0x9000000000000000ULL #define KVM_REG_SIZE_SHIFT 52 #define KVM_REG_SIZE_MASK 0x00f0000000000000ULL @@ -1333,7 +1338,7 @@ struct kvm_dirty_tlb { struct kvm_reg_list { __u64 n; /* number of regs */ - __u64 reg[0]; + __u64 reg[]; }; struct kvm_one_reg { @@ -1374,9 +1379,16 @@ struct kvm_device_attr { __u64 addr; /* userspace address of attr data */ }; -#define KVM_DEV_VFIO_GROUP 1 -#define KVM_DEV_VFIO_GROUP_ADD 1 -#define KVM_DEV_VFIO_GROUP_DEL 2 +#define KVM_DEV_VFIO_FILE 1 + +#define KVM_DEV_VFIO_FILE_ADD 1 +#define KVM_DEV_VFIO_FILE_DEL 2 + +/* KVM_DEV_VFIO_GROUP aliases are for compile time uapi compatibility */ +#define KVM_DEV_VFIO_GROUP KVM_DEV_VFIO_FILE + +#define KVM_DEV_VFIO_GROUP_ADD KVM_DEV_VFIO_FILE_ADD +#define KVM_DEV_VFIO_GROUP_DEL KVM_DEV_VFIO_FILE_DEL #define KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE 3 enum kvm_device_type { @@ -1400,6 +1412,8 @@ enum kvm_device_type { #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_RISCV_AIA, +#define KVM_DEV_TYPE_RISCV_AIA KVM_DEV_TYPE_RISCV_AIA KVM_DEV_TYPE_MAX, }; @@ -1408,24 +1422,20 @@ struct kvm_vfio_spapr_tce { __s32 tablefd; }; -/* - * ioctls for VM fds - */ -#define KVM_SET_MEMORY_REGION _IOW(KVMIO, 0x40, struct kvm_memory_region) /* * KVM_CREATE_VCPU receives as a parameter the vcpu slot, and returns * a vcpu fd. */ #define KVM_CREATE_VCPU _IO(KVMIO, 0x41) #define KVM_GET_DIRTY_LOG _IOW(KVMIO, 0x42, struct kvm_dirty_log) -/* KVM_SET_MEMORY_ALIAS is obsolete: */ -#define KVM_SET_MEMORY_ALIAS _IOW(KVMIO, 0x43, struct kvm_memory_alias) #define KVM_SET_NR_MMU_PAGES _IO(KVMIO, 0x44) -#define KVM_GET_NR_MMU_PAGES _IO(KVMIO, 0x45) +#define KVM_GET_NR_MMU_PAGES _IO(KVMIO, 0x45) /* deprecated */ #define KVM_SET_USER_MEMORY_REGION _IOW(KVMIO, 0x46, \ struct kvm_userspace_memory_region) #define KVM_SET_TSS_ADDR _IO(KVMIO, 0x47) #define KVM_SET_IDENTITY_MAP_ADDR _IOW(KVMIO, 0x48, __u64) +#define KVM_SET_USER_MEMORY_REGION2 _IOW(KVMIO, 0x49, \ + struct kvm_userspace_memory_region2) /* enable ucontrol for s390 */ struct kvm_s390_ucas_mapping { @@ -1450,20 +1460,8 @@ struct kvm_s390_ucas_mapping { _IOW(KVMIO, 0x67, struct kvm_coalesced_mmio_zone) #define KVM_UNREGISTER_COALESCED_MMIO \ _IOW(KVMIO, 0x68, struct kvm_coalesced_mmio_zone) -#define KVM_ASSIGN_PCI_DEVICE _IOR(KVMIO, 0x69, \ - struct kvm_assigned_pci_dev) #define KVM_SET_GSI_ROUTING _IOW(KVMIO, 0x6a, struct kvm_irq_routing) -/* deprecated, replaced by KVM_ASSIGN_DEV_IRQ */ -#define KVM_ASSIGN_IRQ __KVM_DEPRECATED_VM_R_0x70 -#define KVM_ASSIGN_DEV_IRQ _IOW(KVMIO, 0x70, struct kvm_assigned_irq) #define KVM_REINJECT_CONTROL _IO(KVMIO, 0x71) -#define KVM_DEASSIGN_PCI_DEVICE _IOW(KVMIO, 0x72, \ - struct kvm_assigned_pci_dev) -#define KVM_ASSIGN_SET_MSIX_NR _IOW(KVMIO, 0x73, \ - struct kvm_assigned_msix_nr) -#define KVM_ASSIGN_SET_MSIX_ENTRY _IOW(KVMIO, 0x74, \ - struct kvm_assigned_msix_entry) -#define KVM_DEASSIGN_DEV_IRQ _IOW(KVMIO, 0x75, struct kvm_assigned_irq) #define KVM_IRQFD _IOW(KVMIO, 0x76, struct kvm_irqfd) #define KVM_CREATE_PIT2 _IOW(KVMIO, 0x77, struct kvm_pit_config) #define KVM_SET_BOOT_CPU_ID _IO(KVMIO, 0x78) @@ -1476,12 +1474,10 @@ struct kvm_s390_ucas_mapping { #define KVM_SET_PIT2 _IOW(KVMIO, 0xa0, struct kvm_pit_state2) /* Available with KVM_CAP_PPC_GET_PVINFO */ #define KVM_PPC_GET_PVINFO _IOW(KVMIO, 0xa1, struct kvm_ppc_pvinfo) -/* Available with KVM_CAP_TSC_CONTROL */ +/* Available with KVM_CAP_TSC_CONTROL for a vCPU, or with +* KVM_CAP_VM_TSC_CONTROL to set defaults for a VM */ #define KVM_SET_TSC_KHZ _IO(KVMIO, 0xa2) #define KVM_GET_TSC_KHZ _IO(KVMIO, 0xa3) -/* Available with KVM_CAP_PCI_2_3 */ -#define KVM_ASSIGN_SET_INTX_MASK _IOW(KVMIO, 0xa4, \ - struct kvm_assigned_pci_dev) /* Available with KVM_CAP_SIGNAL_MSI */ #define KVM_SIGNAL_MSI _IOW(KVMIO, 0xa5, struct kvm_msi) /* Available with KVM_CAP_PPC_GET_SMMU_INFO */ @@ -1512,6 +1508,9 @@ struct kvm_s390_ucas_mapping { #define KVM_SET_PMU_EVENT_FILTER _IOW(KVMIO, 0xb2, struct kvm_pmu_event_filter) #define KVM_PPC_SVM_OFF _IO(KVMIO, 0xb3) #define KVM_ARM_MTE_COPY_TAGS _IOR(KVMIO, 0xb4, struct kvm_arm_copy_mte_tags) +/* Available with KVM_CAP_COUNTER_OFFSET */ +#define KVM_ARM_SET_COUNTER_OFFSET _IOW(KVMIO, 0xb5, struct kvm_arm_counter_offset) +#define KVM_ARM_GET_REG_WRITABLE_MASKS _IOR(KVMIO, 0xb6, struct reg_mask_range) /* ioctl for vm fd */ #define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device) @@ -1531,8 +1530,6 @@ struct kvm_s390_ucas_mapping { #define KVM_SET_SREGS _IOW(KVMIO, 0x84, struct kvm_sregs) #define KVM_TRANSLATE _IOWR(KVMIO, 0x85, struct kvm_translation) #define KVM_INTERRUPT _IOW(KVMIO, 0x86, struct kvm_interrupt) -/* KVM_DEBUG_GUEST is no longer supported, use KVM_SET_GUEST_DEBUG instead */ -#define KVM_DEBUG_GUEST __KVM_DEPRECATED_VCPU_W_0x87 #define KVM_GET_MSRS _IOWR(KVMIO, 0x88, struct kvm_msrs) #define KVM_SET_MSRS _IOW(KVMIO, 0x89, struct kvm_msrs) #define KVM_SET_CPUID _IOW(KVMIO, 0x8a, struct kvm_cpuid) @@ -1574,7 +1571,7 @@ struct kvm_s390_ucas_mapping { #define KVM_GET_DEBUGREGS _IOR(KVMIO, 0xa1, struct kvm_debugregs) #define KVM_SET_DEBUGREGS _IOW(KVMIO, 0xa2, struct kvm_debugregs) /* - * vcpu version available with KVM_ENABLE_CAP + * vcpu version available with KVM_CAP_ENABLE_CAP * vm version available with KVM_CAP_ENABLE_CAP_VM */ #define KVM_ENABLE_CAP _IOW(KVMIO, 0xa3, struct kvm_enable_cap) @@ -1651,6 +1648,55 @@ struct kvm_s390_pv_unp { __u64 tweak; }; +enum pv_cmd_dmp_id { + KVM_PV_DUMP_INIT, + KVM_PV_DUMP_CONFIG_STOR_STATE, + KVM_PV_DUMP_COMPLETE, + KVM_PV_DUMP_CPU, +}; + +struct kvm_s390_pv_dmp { + __u64 subcmd; + __u64 buff_addr; + __u64 buff_len; + __u64 gaddr; /* For dump storage state */ + __u64 reserved[4]; +}; + +enum pv_cmd_info_id { + KVM_PV_INFO_VM, + KVM_PV_INFO_DUMP, +}; + +struct kvm_s390_pv_info_dump { + __u64 dump_cpu_buffer_len; + __u64 dump_config_mem_buffer_per_1m; + __u64 dump_config_finalize_len; +}; + +struct kvm_s390_pv_info_vm { + __u64 inst_calls_list[4]; + __u64 max_cpus; + __u64 max_guests; + __u64 max_guest_addr; + __u64 feature_indication; +}; + +struct kvm_s390_pv_info_header { + __u32 id; + __u32 len_max; + __u32 len_written; + __u32 reserved; +}; + +struct kvm_s390_pv_info { + struct kvm_s390_pv_info_header header; + union { + struct kvm_s390_pv_info_dump dump; + struct kvm_s390_pv_info_vm vm; + }; +}; + enum pv_cmd_id { KVM_PV_ENABLE, KVM_PV_DISABLE, @@ -1659,6 +1705,10 @@ enum pv_cmd_id { KVM_PV_VERIFY, KVM_PV_PREP_RESET, KVM_PV_UNSHARE_ALL, + KVM_PV_INFO, + KVM_PV_DUMP, + KVM_PV_ASYNC_CLEANUP_PREPARE, + KVM_PV_ASYNC_CLEANUP_PERFORM, }; struct kvm_pv_cmd { @@ -1689,22 +1739,59 @@ struct kvm_xen_hvm_attr { union { __u8 long_mode; __u8 vector; + __u8 runstate_update_flag; struct { __u64 gfn; +#define KVM_XEN_INVALID_GFN ((__u64)-1) } shared_info; + struct { + __u32 send_port; + __u32 type; /* EVTCHNSTAT_ipi / EVTCHNSTAT_interdomain */ + __u32 flags; +#define KVM_XEN_EVTCHN_DEASSIGN (1 << 0) +#define KVM_XEN_EVTCHN_UPDATE (1 << 1) +#define KVM_XEN_EVTCHN_RESET (1 << 2) + /* + * Events sent by the guest are either looped back to + * the guest itself (potentially on a different port#) + * or signalled via an eventfd. + */ + union { + struct { + __u32 port; + __u32 vcpu; + __u32 priority; + } port; + struct { + __u32 port; /* Zero for eventfd */ + __s32 fd; + } eventfd; + __u32 padding[4]; + } deliver; + } evtchn; + __u32 xen_version; __u64 pad[8]; } u; }; + /* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO */ #define KVM_XEN_ATTR_TYPE_LONG_MODE 0x0 #define KVM_XEN_ATTR_TYPE_SHARED_INFO 0x1 #define KVM_XEN_ATTR_TYPE_UPCALL_VECTOR 0x2 +/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_EVTCHN_SEND */ +#define KVM_XEN_ATTR_TYPE_EVTCHN 0x3 +#define KVM_XEN_ATTR_TYPE_XEN_VERSION 0x4 +/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_RUNSTATE_UPDATE_FLAG */ +#define KVM_XEN_ATTR_TYPE_RUNSTATE_UPDATE_FLAG 0x5 /* Per-vCPU Xen attributes */ #define KVM_XEN_VCPU_GET_ATTR _IOWR(KVMIO, 0xca, struct kvm_xen_vcpu_attr) #define KVM_XEN_VCPU_SET_ATTR _IOW(KVMIO, 0xcb, struct kvm_xen_vcpu_attr) +/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_EVTCHN_SEND */ +#define KVM_XEN_HVM_EVTCHN_SEND _IOW(KVMIO, 0xd0, struct kvm_irq_routing_xen_evtchn) + #define KVM_GET_SREGS2 _IOR(KVMIO, 0xcc, struct kvm_sregs2) #define KVM_SET_SREGS2 _IOW(KVMIO, 0xcd, struct kvm_sregs2) @@ -1713,6 +1800,7 @@ struct kvm_xen_vcpu_attr { __u16 pad[3]; union { __u64 gpa; +#define KVM_XEN_INVALID_GPA ((__u64)-1) __u64 pad[8]; struct { __u64 state; @@ -1722,6 +1810,13 @@ struct kvm_xen_vcpu_attr { __u64 time_blocked; __u64 time_offline; } runstate; + __u32 vcpu_id; + struct { + __u32 port; + __u32 priority; + __u64 expires_ns; + } timer; + __u8 vector; } u; }; @@ -1732,6 +1827,10 @@ struct kvm_xen_vcpu_attr { #define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_CURRENT 0x3 #define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA 0x4 #define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST 0x5 +/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_EVTCHN_SEND */ +#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID 0x6 +#define KVM_XEN_VCPU_ATTR_TYPE_TIMER 0x7 +#define KVM_XEN_VCPU_ATTR_TYPE_UPCALL_VECTOR 0x8 /* Secure Encrypted Virtualization command */ enum sev_cmd_id { @@ -2031,7 +2130,8 @@ struct kvm_stats_header { #define KVM_STATS_UNIT_BYTES (0x1 << KVM_STATS_UNIT_SHIFT) #define KVM_STATS_UNIT_SECONDS (0x2 << KVM_STATS_UNIT_SHIFT) #define KVM_STATS_UNIT_CYCLES (0x3 << KVM_STATS_UNIT_SHIFT) -#define KVM_STATS_UNIT_MAX KVM_STATS_UNIT_CYCLES +#define KVM_STATS_UNIT_BOOLEAN (0x4 << KVM_STATS_UNIT_SHIFT) +#define KVM_STATS_UNIT_MAX KVM_STATS_UNIT_BOOLEAN #define KVM_STATS_BASE_SHIFT 8 #define KVM_STATS_BASE_MASK (0xF << KVM_STATS_BASE_SHIFT) @@ -2066,4 +2166,61 @@ struct kvm_stats_desc { /* Available with KVM_CAP_XSAVE2 */ #define KVM_GET_XSAVE2 _IOR(KVMIO, 0xcf, struct kvm_xsave) +/* Available with KVM_CAP_S390_PROTECTED_DUMP */ +#define KVM_S390_PV_CPU_COMMAND _IOWR(KVMIO, 0xd0, struct kvm_pv_cmd) + +/* Available with KVM_CAP_X86_NOTIFY_VMEXIT */ +#define KVM_X86_NOTIFY_VMEXIT_ENABLED (1ULL << 0) +#define KVM_X86_NOTIFY_VMEXIT_USER (1ULL << 1) + +/* Available with KVM_CAP_S390_ZPCI_OP */ +#define KVM_S390_ZPCI_OP _IOW(KVMIO, 0xd1, struct kvm_s390_zpci_op) + +struct kvm_s390_zpci_op { + /* in */ + __u32 fh; /* target device */ + __u8 op; /* operation to perform */ + __u8 pad[3]; + union { + /* for KVM_S390_ZPCIOP_REG_AEN */ + struct { + __u64 ibv; /* Guest addr of interrupt bit vector */ + __u64 sb; /* Guest addr of summary bit */ + __u32 flags; + __u32 noi; /* Number of interrupts */ + __u8 isc; /* Guest interrupt subclass */ + __u8 sbo; /* Offset of guest summary bit vector */ + __u16 pad; + } reg_aen; + __u64 reserved[8]; + } u; +}; + +/* types for kvm_s390_zpci_op->op */ +#define KVM_S390_ZPCIOP_REG_AEN 0 +#define KVM_S390_ZPCIOP_DEREG_AEN 1 + +/* flags for kvm_s390_zpci_op->u.reg_aen.flags */ +#define KVM_S390_ZPCIOP_REGAEN_HOST (1 << 0) + +/* Available with KVM_CAP_MEMORY_ATTRIBUTES */ +#define KVM_SET_MEMORY_ATTRIBUTES _IOW(KVMIO, 0xd2, struct kvm_memory_attributes) + +struct kvm_memory_attributes { + __u64 address; + __u64 size; + __u64 attributes; + __u64 flags; +}; + +#define KVM_MEMORY_ATTRIBUTE_PRIVATE (1ULL << 3) + +#define KVM_CREATE_GUEST_MEMFD _IOWR(KVMIO, 0xd4, struct kvm_create_guest_memfd) + +struct kvm_create_guest_memfd { + __u64 size; + __u64 flags; + __u64 reserved[6]; +}; + #endif /* __LINUX_KVM_H */ diff --git a/linux-headers/linux/memfd.h b/linux-headers/linux/memfd.h new file mode 100644 index 0000000000..01c0324e77 --- /dev/null +++ b/linux-headers/linux/memfd.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_MEMFD_H +#define _LINUX_MEMFD_H + +#include + +/* flags for memfd_create(2) (unsigned int) */ +#define MFD_CLOEXEC 0x0001U +#define MFD_ALLOW_SEALING 0x0002U +#define MFD_HUGETLB 0x0004U +/* not executable and sealed to prevent changing to executable. */ +#define MFD_NOEXEC_SEAL 0x0008U +/* executable */ +#define MFD_EXEC 0x0010U + +/* + * Huge page size encoding when MFD_HUGETLB is specified, and a huge page + * size other than the default is desired. See hugetlb_encode.h. + * All known huge page size encodings are provided here. It is the + * responsibility of the application to know which sizes are supported on + * the running system. See mmap(2) man page for details. + */ +#define MFD_HUGE_SHIFT HUGETLB_FLAG_ENCODE_SHIFT +#define MFD_HUGE_MASK HUGETLB_FLAG_ENCODE_MASK + +#define MFD_HUGE_64KB HUGETLB_FLAG_ENCODE_64KB +#define MFD_HUGE_512KB HUGETLB_FLAG_ENCODE_512KB +#define MFD_HUGE_1MB HUGETLB_FLAG_ENCODE_1MB +#define MFD_HUGE_2MB HUGETLB_FLAG_ENCODE_2MB +#define MFD_HUGE_8MB HUGETLB_FLAG_ENCODE_8MB +#define MFD_HUGE_16MB HUGETLB_FLAG_ENCODE_16MB +#define MFD_HUGE_32MB HUGETLB_FLAG_ENCODE_32MB +#define MFD_HUGE_256MB HUGETLB_FLAG_ENCODE_256MB +#define MFD_HUGE_512MB HUGETLB_FLAG_ENCODE_512MB +#define MFD_HUGE_1GB HUGETLB_FLAG_ENCODE_1GB +#define MFD_HUGE_2GB HUGETLB_FLAG_ENCODE_2GB +#define MFD_HUGE_16GB HUGETLB_FLAG_ENCODE_16GB + +#endif /* _LINUX_MEMFD_H */ diff --git a/linux-headers/linux/mman.h b/linux-headers/linux/mman.h index 434986fbe3..4e8cb60780 100644 --- a/linux-headers/linux/mman.h +++ b/linux-headers/linux/mman.h @@ -4,6 +4,7 @@ #include #include +#include #define MREMAP_MAYMOVE 1 #define MREMAP_FIXED 2 @@ -41,4 +42,17 @@ #define MAP_HUGE_2GB HUGETLB_FLAG_ENCODE_2GB #define MAP_HUGE_16GB HUGETLB_FLAG_ENCODE_16GB +struct cachestat_range { + __u64 off; + __u64 len; +}; + +struct cachestat { + __u64 nr_cache; + __u64 nr_dirty; + __u64 nr_writeback; + __u64 nr_evicted; + __u64 nr_recently_evicted; +}; + #endif /* _LINUX_MMAN_H */ diff --git a/linux-headers/linux/nvme_ioctl.h b/linux-headers/linux/nvme_ioctl.h new file mode 100644 index 0000000000..f8df31dbc4 --- /dev/null +++ b/linux-headers/linux/nvme_ioctl.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Definitions for the NVM Express ioctl interface + * Copyright (c) 2011-2014, Intel Corporation. + */ + +#ifndef _LINUX_NVME_IOCTL_H +#define _LINUX_NVME_IOCTL_H + +#include + +struct nvme_user_io { + __u8 opcode; + __u8 flags; + __u16 control; + __u16 nblocks; + __u16 rsvd; + __u64 metadata; + __u64 addr; + __u64 slba; + __u32 dsmgmt; + __u32 reftag; + __u16 apptag; + __u16 appmask; +}; + +struct nvme_passthru_cmd { + __u8 opcode; + __u8 flags; + __u16 rsvd1; + __u32 nsid; + __u32 cdw2; + __u32 cdw3; + __u64 metadata; + __u64 addr; + __u32 metadata_len; + __u32 data_len; + __u32 cdw10; + __u32 cdw11; + __u32 cdw12; + __u32 cdw13; + __u32 cdw14; + __u32 cdw15; + __u32 timeout_ms; + __u32 result; +}; + +struct nvme_passthru_cmd64 { + __u8 opcode; + __u8 flags; + __u16 rsvd1; + __u32 nsid; + __u32 cdw2; + __u32 cdw3; + __u64 metadata; + __u64 addr; + __u32 metadata_len; + union { + __u32 data_len; /* for non-vectored io */ + __u32 vec_cnt; /* for vectored io */ + }; + __u32 cdw10; + __u32 cdw11; + __u32 cdw12; + __u32 cdw13; + __u32 cdw14; + __u32 cdw15; + __u32 timeout_ms; + __u32 rsvd2; + __u64 result; +}; + +/* same as struct nvme_passthru_cmd64, minus the 8b result field */ +struct nvme_uring_cmd { + __u8 opcode; + __u8 flags; + __u16 rsvd1; + __u32 nsid; + __u32 cdw2; + __u32 cdw3; + __u64 metadata; + __u64 addr; + __u32 metadata_len; + __u32 data_len; + __u32 cdw10; + __u32 cdw11; + __u32 cdw12; + __u32 cdw13; + __u32 cdw14; + __u32 cdw15; + __u32 timeout_ms; + __u32 rsvd2; +}; + +#define nvme_admin_cmd nvme_passthru_cmd + +#define NVME_IOCTL_ID _IO('N', 0x40) +#define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct nvme_admin_cmd) +#define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x42, struct nvme_user_io) +#define NVME_IOCTL_IO_CMD _IOWR('N', 0x43, struct nvme_passthru_cmd) +#define NVME_IOCTL_RESET _IO('N', 0x44) +#define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45) +#define NVME_IOCTL_RESCAN _IO('N', 0x46) +#define NVME_IOCTL_ADMIN64_CMD _IOWR('N', 0x47, struct nvme_passthru_cmd64) +#define NVME_IOCTL_IO64_CMD _IOWR('N', 0x48, struct nvme_passthru_cmd64) +#define NVME_IOCTL_IO64_CMD_VEC _IOWR('N', 0x49, struct nvme_passthru_cmd64) + +/* io_uring async commands: */ +#define NVME_URING_CMD_IO _IOWR('N', 0x80, struct nvme_uring_cmd) +#define NVME_URING_CMD_IO_VEC _IOWR('N', 0x81, struct nvme_uring_cmd) +#define NVME_URING_CMD_ADMIN _IOWR('N', 0x82, struct nvme_uring_cmd) +#define NVME_URING_CMD_ADMIN_VEC _IOWR('N', 0x83, struct nvme_uring_cmd) + +#endif /* _LINUX_NVME_IOCTL_H */ diff --git a/linux-headers/linux/psci.h b/linux-headers/linux/psci.h index 213b2a0f70..74f3cb5007 100644 --- a/linux-headers/linux/psci.h +++ b/linux-headers/linux/psci.h @@ -48,12 +48,26 @@ #define PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU PSCI_0_2_FN64(7) #define PSCI_1_0_FN_PSCI_FEATURES PSCI_0_2_FN(10) +#define PSCI_1_0_FN_CPU_FREEZE PSCI_0_2_FN(11) +#define PSCI_1_0_FN_CPU_DEFAULT_SUSPEND PSCI_0_2_FN(12) +#define PSCI_1_0_FN_NODE_HW_STATE PSCI_0_2_FN(13) #define PSCI_1_0_FN_SYSTEM_SUSPEND PSCI_0_2_FN(14) #define PSCI_1_0_FN_SET_SUSPEND_MODE PSCI_0_2_FN(15) -#define PSCI_1_1_FN_SYSTEM_RESET2 PSCI_0_2_FN(18) +#define PSCI_1_0_FN_STAT_RESIDENCY PSCI_0_2_FN(16) +#define PSCI_1_0_FN_STAT_COUNT PSCI_0_2_FN(17) +#define PSCI_1_1_FN_SYSTEM_RESET2 PSCI_0_2_FN(18) +#define PSCI_1_1_FN_MEM_PROTECT PSCI_0_2_FN(19) +#define PSCI_1_1_FN_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN(20) + +#define PSCI_1_0_FN64_CPU_DEFAULT_SUSPEND PSCI_0_2_FN64(12) +#define PSCI_1_0_FN64_NODE_HW_STATE PSCI_0_2_FN64(13) #define PSCI_1_0_FN64_SYSTEM_SUSPEND PSCI_0_2_FN64(14) +#define PSCI_1_0_FN64_STAT_RESIDENCY PSCI_0_2_FN64(16) +#define PSCI_1_0_FN64_STAT_COUNT PSCI_0_2_FN64(17) + #define PSCI_1_1_FN64_SYSTEM_RESET2 PSCI_0_2_FN64(18) +#define PSCI_1_1_FN64_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN64(20) /* PSCI v0.2 power state encoding for CPU_SUSPEND function */ #define PSCI_0_2_POWER_STATE_ID_MASK 0xffff diff --git a/linux-headers/linux/psp-sev.h b/linux-headers/linux/psp-sev.h index 51d8b3940e..bcb21339ee 100644 --- a/linux-headers/linux/psp-sev.h +++ b/linux-headers/linux/psp-sev.h @@ -36,6 +36,13 @@ enum { * SEV Firmware status code */ typedef enum { + /* + * This error code is not in the SEV spec. Its purpose is to convey that + * there was an error that prevented the SEV firmware from being called. + * The SEV API error codes are 16 bits, so the -1 value will not overlap + * with possible values from the specification. + */ + SEV_RET_NO_FW_CALL = -1, SEV_RET_SUCCESS = 0, SEV_RET_INVALID_PLATFORM_STATE, SEV_RET_INVALID_GUEST_STATE, @@ -61,6 +68,7 @@ typedef enum { SEV_RET_INVALID_PARAM, SEV_RET_RESOURCE_LIMIT, SEV_RET_SECURE_DATA_INVALID, + SEV_RET_INVALID_KEY = 0x27, SEV_RET_MAX, } sev_ret_code; diff --git a/linux-headers/linux/stddef.h b/linux-headers/linux/stddef.h new file mode 100644 index 0000000000..bf9749dd14 --- /dev/null +++ b/linux-headers/linux/stddef.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_STDDEF_H +#define _LINUX_STDDEF_H + + + +#ifndef __always_inline +#define __always_inline __inline__ +#endif + +/** + * __struct_group() - Create a mirrored named and anonyomous struct + * + * @TAG: The tag name for the named sub-struct (usually empty) + * @NAME: The identifier name of the mirrored sub-struct + * @ATTRS: Any struct attributes (usually empty) + * @MEMBERS: The member declarations for the mirrored structs + * + * Used to create an anonymous union of two structs with identical layout + * and size: one anonymous and one named. The former's members can be used + * normally without sub-struct naming, and the latter can be used to + * reason about the start, end, and size of the group of struct members. + * The named struct can also be explicitly tagged for layer reuse, as well + * as both having struct attributes appended. + */ +#define __struct_group(TAG, NAME, ATTRS, MEMBERS...) \ + union { \ + struct { MEMBERS } ATTRS; \ + struct TAG { MEMBERS } ATTRS NAME; \ + } ATTRS + +#ifdef __cplusplus +/* sizeof(struct{}) is 1 in C++, not 0, can't use C version of the macro. */ +#define __DECLARE_FLEX_ARRAY(T, member) \ + T member[0] +#else +/** + * __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union + * + * @TYPE: The type of each flexible array element + * @NAME: The name of the flexible array member + * + * In order to have a flexible array member in a union or alone in a + * struct, it needs to be wrapped in an anonymous struct with at least 1 + * named member, but that member can be empty. + */ +#define __DECLARE_FLEX_ARRAY(TYPE, NAME) \ + struct { \ + struct { } __empty_ ## NAME; \ + TYPE NAME[]; \ + } +#endif + +#ifndef __counted_by +#define __counted_by(m) +#endif + +#endif /* _LINUX_STDDEF_H */ diff --git a/linux-headers/linux/userfaultfd.h b/linux-headers/linux/userfaultfd.h index 769b8379e4..4283de22d5 100644 --- a/linux-headers/linux/userfaultfd.h +++ b/linux-headers/linux/userfaultfd.h @@ -12,6 +12,10 @@ #include +/* ioctls for /dev/userfaultfd */ +#define USERFAULTFD_IOC 0xAA +#define USERFAULTFD_IOC_NEW _IO(USERFAULTFD_IOC, 0x00) + /* * If the UFFDIO_API is upgraded someday, the UFFDIO_UNREGISTER and * UFFDIO_WAKE ioctls should be defined as _IOW and not as _IOR. In @@ -33,7 +37,12 @@ UFFD_FEATURE_THREAD_ID | \ UFFD_FEATURE_MINOR_HUGETLBFS | \ UFFD_FEATURE_MINOR_SHMEM | \ - UFFD_FEATURE_EXACT_ADDRESS) + UFFD_FEATURE_EXACT_ADDRESS | \ + UFFD_FEATURE_WP_HUGETLBFS_SHMEM | \ + UFFD_FEATURE_WP_UNPOPULATED | \ + UFFD_FEATURE_POISON | \ + UFFD_FEATURE_WP_ASYNC | \ + UFFD_FEATURE_MOVE) #define UFFD_API_IOCTLS \ ((__u64)1 << _UFFDIO_REGISTER | \ (__u64)1 << _UFFDIO_UNREGISTER | \ @@ -42,12 +51,16 @@ ((__u64)1 << _UFFDIO_WAKE | \ (__u64)1 << _UFFDIO_COPY | \ (__u64)1 << _UFFDIO_ZEROPAGE | \ + (__u64)1 << _UFFDIO_MOVE | \ (__u64)1 << _UFFDIO_WRITEPROTECT | \ - (__u64)1 << _UFFDIO_CONTINUE) + (__u64)1 << _UFFDIO_CONTINUE | \ + (__u64)1 << _UFFDIO_POISON) #define UFFD_API_RANGE_IOCTLS_BASIC \ ((__u64)1 << _UFFDIO_WAKE | \ (__u64)1 << _UFFDIO_COPY | \ - (__u64)1 << _UFFDIO_CONTINUE) + (__u64)1 << _UFFDIO_WRITEPROTECT | \ + (__u64)1 << _UFFDIO_CONTINUE | \ + (__u64)1 << _UFFDIO_POISON) /* * Valid ioctl command number range with this API is from 0x00 to @@ -62,8 +75,10 @@ #define _UFFDIO_WAKE (0x02) #define _UFFDIO_COPY (0x03) #define _UFFDIO_ZEROPAGE (0x04) +#define _UFFDIO_MOVE (0x05) #define _UFFDIO_WRITEPROTECT (0x06) #define _UFFDIO_CONTINUE (0x07) +#define _UFFDIO_POISON (0x08) #define _UFFDIO_API (0x3F) /* userfaultfd ioctl ids */ @@ -80,10 +95,14 @@ struct uffdio_copy) #define UFFDIO_ZEROPAGE _IOWR(UFFDIO, _UFFDIO_ZEROPAGE, \ struct uffdio_zeropage) +#define UFFDIO_MOVE _IOWR(UFFDIO, _UFFDIO_MOVE, \ + struct uffdio_move) #define UFFDIO_WRITEPROTECT _IOWR(UFFDIO, _UFFDIO_WRITEPROTECT, \ struct uffdio_writeprotect) #define UFFDIO_CONTINUE _IOWR(UFFDIO, _UFFDIO_CONTINUE, \ struct uffdio_continue) +#define UFFDIO_POISON _IOWR(UFFDIO, _UFFDIO_POISON, \ + struct uffdio_poison) /* read() structure */ struct uffd_msg { @@ -194,6 +213,23 @@ struct uffdio_api { * UFFD_FEATURE_EXACT_ADDRESS indicates that the exact address of page * faults would be provided and the offset within the page would not be * masked. + * + * UFFD_FEATURE_WP_HUGETLBFS_SHMEM indicates that userfaultfd + * write-protection mode is supported on both shmem and hugetlbfs. + * + * UFFD_FEATURE_WP_UNPOPULATED indicates that userfaultfd + * write-protection mode will always apply to unpopulated pages + * (i.e. empty ptes). This will be the default behavior for shmem + * & hugetlbfs, so this flag only affects anonymous memory behavior + * when userfault write-protection mode is registered. + * + * UFFD_FEATURE_WP_ASYNC indicates that userfaultfd write-protection + * asynchronous mode is supported in which the write fault is + * automatically resolved and write-protection is un-set. + * It implies UFFD_FEATURE_WP_UNPOPULATED. + * + * UFFD_FEATURE_MOVE indicates that the kernel supports moving an + * existing page contents from userspace. */ #define UFFD_FEATURE_PAGEFAULT_FLAG_WP (1<<0) #define UFFD_FEATURE_EVENT_FORK (1<<1) @@ -207,6 +243,11 @@ struct uffdio_api { #define UFFD_FEATURE_MINOR_HUGETLBFS (1<<9) #define UFFD_FEATURE_MINOR_SHMEM (1<<10) #define UFFD_FEATURE_EXACT_ADDRESS (1<<11) +#define UFFD_FEATURE_WP_HUGETLBFS_SHMEM (1<<12) +#define UFFD_FEATURE_WP_UNPOPULATED (1<<13) +#define UFFD_FEATURE_POISON (1<<14) +#define UFFD_FEATURE_WP_ASYNC (1<<15) +#define UFFD_FEATURE_MOVE (1<<16) __u64 features; __u64 ioctls; @@ -287,6 +328,13 @@ struct uffdio_writeprotect { struct uffdio_continue { struct uffdio_range range; #define UFFDIO_CONTINUE_MODE_DONTWAKE ((__u64)1<<0) + /* + * UFFDIO_CONTINUE_MODE_WP will map the page write protected on + * the fly. UFFDIO_CONTINUE_MODE_WP is available only if the + * write protected ioctl is implemented for the range + * according to the uffdio_register.ioctls. + */ +#define UFFDIO_CONTINUE_MODE_WP ((__u64)1<<1) __u64 mode; /* @@ -296,6 +344,36 @@ struct uffdio_continue { __s64 mapped; }; +struct uffdio_poison { + struct uffdio_range range; +#define UFFDIO_POISON_MODE_DONTWAKE ((__u64)1<<0) + __u64 mode; + + /* + * Fields below here are written by the ioctl and must be at the end: + * the copy_from_user will not read past here. + */ + __s64 updated; +}; + +struct uffdio_move { + __u64 dst; + __u64 src; + __u64 len; + /* + * Especially if used to atomically remove memory from the + * address space the wake on the dst range is not needed. + */ +#define UFFDIO_MOVE_MODE_DONTWAKE ((__u64)1<<0) +#define UFFDIO_MOVE_MODE_ALLOW_SRC_HOLES ((__u64)1<<1) + __u64 mode; + /* + * "move" is written by the ioctl and must be at the end: the + * copy_from_user will not read the last 8 bytes. + */ + __s64 move; +}; + /* * Flags for the userfaultfd(2) system call itself. */ diff --git a/linux-headers/linux/vduse.h b/linux-headers/linux/vduse.h new file mode 100644 index 0000000000..6d2ca064b5 --- /dev/null +++ b/linux-headers/linux/vduse.h @@ -0,0 +1,353 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _VDUSE_H_ +#define _VDUSE_H_ + +#include + +#define VDUSE_BASE 0x81 + +/* The ioctls for control device (/dev/vduse/control) */ + +#define VDUSE_API_VERSION 0 + +/* + * Get the version of VDUSE API that kernel supported (VDUSE_API_VERSION). + * This is used for future extension. + */ +#define VDUSE_GET_API_VERSION _IOR(VDUSE_BASE, 0x00, __u64) + +/* Set the version of VDUSE API that userspace supported. */ +#define VDUSE_SET_API_VERSION _IOW(VDUSE_BASE, 0x01, __u64) + +/** + * struct vduse_dev_config - basic configuration of a VDUSE device + * @name: VDUSE device name, needs to be NUL terminated + * @vendor_id: virtio vendor id + * @device_id: virtio device id + * @features: virtio features + * @vq_num: the number of virtqueues + * @vq_align: the allocation alignment of virtqueue's metadata + * @reserved: for future use, needs to be initialized to zero + * @config_size: the size of the configuration space + * @config: the buffer of the configuration space + * + * Structure used by VDUSE_CREATE_DEV ioctl to create VDUSE device. + */ +struct vduse_dev_config { +#define VDUSE_NAME_MAX 256 + char name[VDUSE_NAME_MAX]; + __u32 vendor_id; + __u32 device_id; + __u64 features; + __u32 vq_num; + __u32 vq_align; + __u32 reserved[13]; + __u32 config_size; + __u8 config[]; +}; + +/* Create a VDUSE device which is represented by a char device (/dev/vduse/$NAME) */ +#define VDUSE_CREATE_DEV _IOW(VDUSE_BASE, 0x02, struct vduse_dev_config) + +/* + * Destroy a VDUSE device. Make sure there are no more references + * to the char device (/dev/vduse/$NAME). + */ +#define VDUSE_DESTROY_DEV _IOW(VDUSE_BASE, 0x03, char[VDUSE_NAME_MAX]) + +/* The ioctls for VDUSE device (/dev/vduse/$NAME) */ + +/** + * struct vduse_iotlb_entry - entry of IOTLB to describe one IOVA region [start, last] + * @offset: the mmap offset on returned file descriptor + * @start: start of the IOVA region + * @last: last of the IOVA region + * @perm: access permission of the IOVA region + * + * Structure used by VDUSE_IOTLB_GET_FD ioctl to find an overlapped IOVA region. + */ +struct vduse_iotlb_entry { + __u64 offset; + __u64 start; + __u64 last; +#define VDUSE_ACCESS_RO 0x1 +#define VDUSE_ACCESS_WO 0x2 +#define VDUSE_ACCESS_RW 0x3 + __u8 perm; +}; + +/* + * Find the first IOVA region that overlaps with the range [start, last] + * and return the corresponding file descriptor. Return -EINVAL means the + * IOVA region doesn't exist. Caller should set start and last fields. + */ +#define VDUSE_IOTLB_GET_FD _IOWR(VDUSE_BASE, 0x10, struct vduse_iotlb_entry) + +/* + * Get the negotiated virtio features. It's a subset of the features in + * struct vduse_dev_config which can be accepted by virtio driver. It's + * only valid after FEATURES_OK status bit is set. + */ +#define VDUSE_DEV_GET_FEATURES _IOR(VDUSE_BASE, 0x11, __u64) + +/** + * struct vduse_config_data - data used to update configuration space + * @offset: the offset from the beginning of configuration space + * @length: the length to write to configuration space + * @buffer: the buffer used to write from + * + * Structure used by VDUSE_DEV_SET_CONFIG ioctl to update device + * configuration space. + */ +struct vduse_config_data { + __u32 offset; + __u32 length; + __u8 buffer[]; +}; + +/* Set device configuration space */ +#define VDUSE_DEV_SET_CONFIG _IOW(VDUSE_BASE, 0x12, struct vduse_config_data) + +/* + * Inject a config interrupt. It's usually used to notify virtio driver + * that device configuration space has changed. + */ +#define VDUSE_DEV_INJECT_CONFIG_IRQ _IO(VDUSE_BASE, 0x13) + +/** + * struct vduse_vq_config - basic configuration of a virtqueue + * @index: virtqueue index + * @max_size: the max size of virtqueue + * @reserved: for future use, needs to be initialized to zero + * + * Structure used by VDUSE_VQ_SETUP ioctl to setup a virtqueue. + */ +struct vduse_vq_config { + __u32 index; + __u16 max_size; + __u16 reserved[13]; +}; + +/* + * Setup the specified virtqueue. Make sure all virtqueues have been + * configured before the device is attached to vDPA bus. + */ +#define VDUSE_VQ_SETUP _IOW(VDUSE_BASE, 0x14, struct vduse_vq_config) + +/** + * struct vduse_vq_state_split - split virtqueue state + * @avail_index: available index + */ +struct vduse_vq_state_split { + __u16 avail_index; +}; + +/** + * struct vduse_vq_state_packed - packed virtqueue state + * @last_avail_counter: last driver ring wrap counter observed by device + * @last_avail_idx: device available index + * @last_used_counter: device ring wrap counter + * @last_used_idx: used index + */ +struct vduse_vq_state_packed { + __u16 last_avail_counter; + __u16 last_avail_idx; + __u16 last_used_counter; + __u16 last_used_idx; +}; + +/** + * struct vduse_vq_info - information of a virtqueue + * @index: virtqueue index + * @num: the size of virtqueue + * @desc_addr: address of desc area + * @driver_addr: address of driver area + * @device_addr: address of device area + * @split: split virtqueue state + * @packed: packed virtqueue state + * @ready: ready status of virtqueue + * + * Structure used by VDUSE_VQ_GET_INFO ioctl to get virtqueue's information. + */ +struct vduse_vq_info { + __u32 index; + __u32 num; + __u64 desc_addr; + __u64 driver_addr; + __u64 device_addr; + union { + struct vduse_vq_state_split split; + struct vduse_vq_state_packed packed; + }; + __u8 ready; +}; + +/* Get the specified virtqueue's information. Caller should set index field. */ +#define VDUSE_VQ_GET_INFO _IOWR(VDUSE_BASE, 0x15, struct vduse_vq_info) + +/** + * struct vduse_vq_eventfd - eventfd configuration for a virtqueue + * @index: virtqueue index + * @fd: eventfd, -1 means de-assigning the eventfd + * + * Structure used by VDUSE_VQ_SETUP_KICKFD ioctl to setup kick eventfd. + */ +struct vduse_vq_eventfd { + __u32 index; +#define VDUSE_EVENTFD_DEASSIGN -1 + int fd; +}; + +/* + * Setup kick eventfd for specified virtqueue. The kick eventfd is used + * by VDUSE kernel module to notify userspace to consume the avail vring. + */ +#define VDUSE_VQ_SETUP_KICKFD _IOW(VDUSE_BASE, 0x16, struct vduse_vq_eventfd) + +/* + * Inject an interrupt for specific virtqueue. It's used to notify virtio driver + * to consume the used vring. + */ +#define VDUSE_VQ_INJECT_IRQ _IOW(VDUSE_BASE, 0x17, __u32) + +/** + * struct vduse_iova_umem - userspace memory configuration for one IOVA region + * @uaddr: start address of userspace memory, it must be aligned to page size + * @iova: start of the IOVA region + * @size: size of the IOVA region + * @reserved: for future use, needs to be initialized to zero + * + * Structure used by VDUSE_IOTLB_REG_UMEM and VDUSE_IOTLB_DEREG_UMEM + * ioctls to register/de-register userspace memory for IOVA regions + */ +struct vduse_iova_umem { + __u64 uaddr; + __u64 iova; + __u64 size; + __u64 reserved[3]; +}; + +/* Register userspace memory for IOVA regions */ +#define VDUSE_IOTLB_REG_UMEM _IOW(VDUSE_BASE, 0x18, struct vduse_iova_umem) + +/* De-register the userspace memory. Caller should set iova and size field. */ +#define VDUSE_IOTLB_DEREG_UMEM _IOW(VDUSE_BASE, 0x19, struct vduse_iova_umem) + +/** + * struct vduse_iova_info - information of one IOVA region + * @start: start of the IOVA region + * @last: last of the IOVA region + * @capability: capability of the IOVA regsion + * @reserved: for future use, needs to be initialized to zero + * + * Structure used by VDUSE_IOTLB_GET_INFO ioctl to get information of + * one IOVA region. + */ +struct vduse_iova_info { + __u64 start; + __u64 last; +#define VDUSE_IOVA_CAP_UMEM (1 << 0) + __u64 capability; + __u64 reserved[3]; +}; + +/* + * Find the first IOVA region that overlaps with the range [start, last] + * and return some information on it. Caller should set start and last fields. + */ +#define VDUSE_IOTLB_GET_INFO _IOWR(VDUSE_BASE, 0x1a, struct vduse_iova_info) + +/* The control messages definition for read(2)/write(2) on /dev/vduse/$NAME */ + +/** + * enum vduse_req_type - request type + * @VDUSE_GET_VQ_STATE: get the state for specified virtqueue from userspace + * @VDUSE_SET_STATUS: set the device status + * @VDUSE_UPDATE_IOTLB: Notify userspace to update the memory mapping for + * specified IOVA range via VDUSE_IOTLB_GET_FD ioctl + */ +enum vduse_req_type { + VDUSE_GET_VQ_STATE, + VDUSE_SET_STATUS, + VDUSE_UPDATE_IOTLB, +}; + +/** + * struct vduse_vq_state - virtqueue state + * @index: virtqueue index + * @split: split virtqueue state + * @packed: packed virtqueue state + */ +struct vduse_vq_state { + __u32 index; + union { + struct vduse_vq_state_split split; + struct vduse_vq_state_packed packed; + }; +}; + +/** + * struct vduse_dev_status - device status + * @status: device status + */ +struct vduse_dev_status { + __u8 status; +}; + +/** + * struct vduse_iova_range - IOVA range [start, last] + * @start: start of the IOVA range + * @last: last of the IOVA range + */ +struct vduse_iova_range { + __u64 start; + __u64 last; +}; + +/** + * struct vduse_dev_request - control request + * @type: request type + * @request_id: request id + * @reserved: for future use + * @vq_state: virtqueue state, only index field is available + * @s: device status + * @iova: IOVA range for updating + * @padding: padding + * + * Structure used by read(2) on /dev/vduse/$NAME. + */ +struct vduse_dev_request { + __u32 type; + __u32 request_id; + __u32 reserved[4]; + union { + struct vduse_vq_state vq_state; + struct vduse_dev_status s; + struct vduse_iova_range iova; + __u32 padding[32]; + }; +}; + +/** + * struct vduse_dev_response - response to control request + * @request_id: corresponding request id + * @result: the result of request + * @reserved: for future use, needs to be initialized to zero + * @vq_state: virtqueue state + * @padding: padding + * + * Structure used by write(2) on /dev/vduse/$NAME. + */ +struct vduse_dev_response { + __u32 request_id; +#define VDUSE_REQ_RESULT_OK 0x00 +#define VDUSE_REQ_RESULT_FAILED 0x01 + __u32 result; + __u32 reserved[4]; + union { + struct vduse_vq_state vq_state; + __u32 padding[32]; + }; +}; + +#endif /* _VDUSE_H_ */ diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index e9f7795c39..b4be37b225 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -49,7 +49,11 @@ /* Supports VFIO_DMA_UNMAP_FLAG_ALL */ #define VFIO_UNMAP_ALL 9 -/* Supports the vaddr flag for DMA map and unmap */ +/* + * Supports the vaddr flag for DMA map and unmap. Not supported for mediated + * devices, so this capability is subject to change as groups are added or + * removed. + */ #define VFIO_UPDATE_VADDR 10 /* @@ -209,9 +213,11 @@ struct vfio_device_info { #define VFIO_DEVICE_FLAGS_AP (1 << 5) /* vfio-ap device */ #define VFIO_DEVICE_FLAGS_FSL_MC (1 << 6) /* vfio-fsl-mc device */ #define VFIO_DEVICE_FLAGS_CAPS (1 << 7) /* Info supports caps */ +#define VFIO_DEVICE_FLAGS_CDX (1 << 8) /* vfio-cdx device */ __u32 num_regions; /* Max region index + 1 */ __u32 num_irqs; /* Max IRQ index + 1 */ __u32 cap_offset; /* Offset within info struct of first cap */ + __u32 pad; }; #define VFIO_DEVICE_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 7) @@ -236,6 +242,20 @@ struct vfio_device_info { #define VFIO_DEVICE_INFO_CAP_ZPCI_UTIL 3 #define VFIO_DEVICE_INFO_CAP_ZPCI_PFIP 4 +/* + * The following VFIO_DEVICE_INFO capability reports support for PCIe AtomicOp + * completion to the root bus with supported widths provided via flags. + */ +#define VFIO_DEVICE_INFO_CAP_PCI_ATOMIC_COMP 5 +struct vfio_device_info_cap_pci_atomic_comp { + struct vfio_info_cap_header header; + __u32 flags; +#define VFIO_PCI_ATOMIC_COMP32 (1 << 0) +#define VFIO_PCI_ATOMIC_COMP64 (1 << 1) +#define VFIO_PCI_ATOMIC_COMP128 (1 << 2) + __u32 reserved; +}; + /** * VFIO_DEVICE_GET_REGION_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 8, * struct vfio_region_info) @@ -257,8 +277,8 @@ struct vfio_region_info { #define VFIO_REGION_INFO_FLAG_CAPS (1 << 3) /* Info supports caps */ __u32 index; /* Region index */ __u32 cap_offset; /* Offset within info struct of first cap */ - __u64 size; /* Region size (bytes) */ - __u64 offset; /* Region offset from start of device fd */ + __aligned_u64 size; /* Region size (bytes) */ + __aligned_u64 offset; /* Region offset from start of device fd */ }; #define VFIO_DEVICE_GET_REGION_INFO _IO(VFIO_TYPE, VFIO_BASE + 8) @@ -274,8 +294,8 @@ struct vfio_region_info { #define VFIO_REGION_INFO_CAP_SPARSE_MMAP 1 struct vfio_region_sparse_mmap_area { - __u64 offset; /* Offset of mmap'able area within region */ - __u64 size; /* Size of mmap'able area */ + __aligned_u64 offset; /* Offset of mmap'able area within region */ + __aligned_u64 size; /* Size of mmap'able area */ }; struct vfio_region_info_cap_sparse_mmap { @@ -430,9 +450,9 @@ struct vfio_device_migration_info { VFIO_DEVICE_STATE_V1_RESUMING) __u32 reserved; - __u64 pending_bytes; - __u64 data_offset; - __u64 data_size; + __aligned_u64 pending_bytes; + __aligned_u64 data_offset; + __aligned_u64 data_size; }; /* @@ -456,7 +476,7 @@ struct vfio_device_migration_info { struct vfio_region_info_cap_nvlink2_ssatgt { struct vfio_info_cap_header header; - __u64 tgt; + __aligned_u64 tgt; }; /* @@ -507,6 +527,9 @@ struct vfio_region_info_cap_nvlink2_lnkspd { * then add and unmask vectors, it's up to userspace to make the decision * whether to allocate the maximum supported number of vectors or tear * down setup and incrementally increase the vectors as each is enabled. + * Absence of the NORESIZE flag indicates that vectors can be enabled + * and disabled dynamically without impacting other vectors within the + * index. */ struct vfio_irq_info { __u32 argsz; @@ -642,15 +665,73 @@ enum { VFIO_CCW_NUM_IRQS }; +/* + * The vfio-ap bus driver makes use of the following IRQ index mapping. + * Unimplemented IRQ types return a count of zero. + */ +enum { + VFIO_AP_REQ_IRQ_INDEX, + VFIO_AP_NUM_IRQS +}; + /** - * VFIO_DEVICE_GET_PCI_HOT_RESET_INFO - _IORW(VFIO_TYPE, VFIO_BASE + 12, + * VFIO_DEVICE_GET_PCI_HOT_RESET_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 12, * struct vfio_pci_hot_reset_info) * + * This command is used to query the affected devices in the hot reset for + * a given device. + * + * This command always reports the segment, bus, and devfn information for + * each affected device, and selectively reports the group_id or devid per + * the way how the calling device is opened. + * + * - If the calling device is opened via the traditional group/container + * API, group_id is reported. User should check if it has owned all + * the affected devices and provides a set of group fds to prove the + * ownership in VFIO_DEVICE_PCI_HOT_RESET ioctl. + * + * - If the calling device is opened as a cdev, devid is reported. + * Flag VFIO_PCI_HOT_RESET_FLAG_DEV_ID is set to indicate this + * data type. All the affected devices should be represented in + * the dev_set, ex. bound to a vfio driver, and also be owned by + * this interface which is determined by the following conditions: + * 1) Has a valid devid within the iommufd_ctx of the calling device. + * Ownership cannot be determined across separate iommufd_ctx and + * the cdev calling conventions do not support a proof-of-ownership + * model as provided in the legacy group interface. In this case + * valid devid with value greater than zero is provided in the return + * structure. + * 2) Does not have a valid devid within the iommufd_ctx of the calling + * device, but belongs to the same IOMMU group as the calling device + * or another opened device that has a valid devid within the + * iommufd_ctx of the calling device. This provides implicit ownership + * for devices within the same DMA isolation context. In this case + * the devid value of VFIO_PCI_DEVID_OWNED is provided in the return + * structure. + * + * A devid value of VFIO_PCI_DEVID_NOT_OWNED is provided in the return + * structure for affected devices where device is NOT represented in the + * dev_set or ownership is not available. Such devices prevent the use + * of VFIO_DEVICE_PCI_HOT_RESET ioctl outside of the proof-of-ownership + * calling conventions (ie. via legacy group accessed devices). Flag + * VFIO_PCI_HOT_RESET_FLAG_DEV_ID_OWNED would be set when all the + * affected devices are represented in the dev_set and also owned by + * the user. This flag is available only when + * flag VFIO_PCI_HOT_RESET_FLAG_DEV_ID is set, otherwise reserved. + * When set, user could invoke VFIO_DEVICE_PCI_HOT_RESET with a zero + * length fd array on the calling device as the ownership is validated + * by iommufd_ctx. + * * Return: 0 on success, -errno on failure: * -enospc = insufficient buffer, -enodev = unsupported for device. */ struct vfio_pci_dependent_device { - __u32 group_id; + union { + __u32 group_id; + __u32 devid; +#define VFIO_PCI_DEVID_OWNED 0 +#define VFIO_PCI_DEVID_NOT_OWNED -1 + }; __u16 segment; __u8 bus; __u8 devfn; /* Use PCI_SLOT/PCI_FUNC */ @@ -659,6 +740,8 @@ struct vfio_pci_dependent_device { struct vfio_pci_hot_reset_info { __u32 argsz; __u32 flags; +#define VFIO_PCI_HOT_RESET_FLAG_DEV_ID (1 << 0) +#define VFIO_PCI_HOT_RESET_FLAG_DEV_ID_OWNED (1 << 1) __u32 count; struct vfio_pci_dependent_device devices[]; }; @@ -669,6 +752,24 @@ struct vfio_pci_hot_reset_info { * VFIO_DEVICE_PCI_HOT_RESET - _IOW(VFIO_TYPE, VFIO_BASE + 13, * struct vfio_pci_hot_reset) * + * A PCI hot reset results in either a bus or slot reset which may affect + * other devices sharing the bus/slot. The calling user must have + * ownership of the full set of affected devices as determined by the + * VFIO_DEVICE_GET_PCI_HOT_RESET_INFO ioctl. + * + * When called on a device file descriptor acquired through the vfio + * group interface, the user is required to provide proof of ownership + * of those affected devices via the group_fds array in struct + * vfio_pci_hot_reset. + * + * When called on a direct cdev opened vfio device, the flags field of + * struct vfio_pci_hot_reset_info reports the ownership status of the + * affected devices and this ioctl must be called with an empty group_fds + * array. See above INFO ioctl definition for ownership requirements. + * + * Mixed usage of legacy groups and cdevs across the set of affected + * devices is not supported. + * * Return: 0 on success, -errno on failure. */ struct vfio_pci_hot_reset { @@ -715,7 +816,7 @@ struct vfio_device_gfx_plane_info { __u32 drm_plane_type; /* type of plane: DRM_PLANE_TYPE_* */ /* out */ __u32 drm_format; /* drm format of plane */ - __u64 drm_format_mod; /* tiled mode */ + __aligned_u64 drm_format_mod; /* tiled mode */ __u32 width; /* width of plane */ __u32 height; /* height of plane */ __u32 stride; /* stride of plane */ @@ -728,6 +829,7 @@ struct vfio_device_gfx_plane_info { __u32 region_index; /* region index */ __u32 dmabuf_id; /* dma-buf id */ }; + __u32 reserved; }; #define VFIO_DEVICE_QUERY_GFX_PLANE _IO(VFIO_TYPE, VFIO_BASE + 14) @@ -762,15 +864,16 @@ struct vfio_device_ioeventfd { #define VFIO_DEVICE_IOEVENTFD_32 (1 << 2) /* 4-byte write */ #define VFIO_DEVICE_IOEVENTFD_64 (1 << 3) /* 8-byte write */ #define VFIO_DEVICE_IOEVENTFD_SIZE_MASK (0xf) - __u64 offset; /* device fd offset of write */ - __u64 data; /* data to be written */ + __aligned_u64 offset; /* device fd offset of write */ + __aligned_u64 data; /* data to be written */ __s32 fd; /* -1 for de-assignment */ + __u32 reserved; }; #define VFIO_DEVICE_IOEVENTFD _IO(VFIO_TYPE, VFIO_BASE + 16) /** - * VFIO_DEVICE_FEATURE - _IORW(VFIO_TYPE, VFIO_BASE + 17, + * VFIO_DEVICE_FEATURE - _IOWR(VFIO_TYPE, VFIO_BASE + 17, * struct vfio_device_feature) * * Get, set, or probe feature data of the device. The feature is selected @@ -797,6 +900,83 @@ struct vfio_device_feature { #define VFIO_DEVICE_FEATURE _IO(VFIO_TYPE, VFIO_BASE + 17) +/* + * VFIO_DEVICE_BIND_IOMMUFD - _IOR(VFIO_TYPE, VFIO_BASE + 18, + * struct vfio_device_bind_iommufd) + * @argsz: User filled size of this data. + * @flags: Must be 0. + * @iommufd: iommufd to bind. + * @out_devid: The device id generated by this bind. devid is a handle for + * this device/iommufd bond and can be used in IOMMUFD commands. + * + * Bind a vfio_device to the specified iommufd. + * + * User is restricted from accessing the device before the binding operation + * is completed. Only allowed on cdev fds. + * + * Unbind is automatically conducted when device fd is closed. + * + * Return: 0 on success, -errno on failure. + */ +struct vfio_device_bind_iommufd { + __u32 argsz; + __u32 flags; + __s32 iommufd; + __u32 out_devid; +}; + +#define VFIO_DEVICE_BIND_IOMMUFD _IO(VFIO_TYPE, VFIO_BASE + 18) + +/* + * VFIO_DEVICE_ATTACH_IOMMUFD_PT - _IOW(VFIO_TYPE, VFIO_BASE + 19, + * struct vfio_device_attach_iommufd_pt) + * @argsz: User filled size of this data. + * @flags: Must be 0. + * @pt_id: Input the target id which can represent an ioas or a hwpt + * allocated via iommufd subsystem. + * Output the input ioas id or the attached hwpt id which could + * be the specified hwpt itself or a hwpt automatically created + * for the specified ioas by kernel during the attachment. + * + * Associate the device with an address space within the bound iommufd. + * Undo by VFIO_DEVICE_DETACH_IOMMUFD_PT or device fd close. This is only + * allowed on cdev fds. + * + * If a vfio device is currently attached to a valid hw_pagetable, without doing + * a VFIO_DEVICE_DETACH_IOMMUFD_PT, a second VFIO_DEVICE_ATTACH_IOMMUFD_PT ioctl + * passing in another hw_pagetable (hwpt) id is allowed. This action, also known + * as a hw_pagetable replacement, will replace the device's currently attached + * hw_pagetable with a new hw_pagetable corresponding to the given pt_id. + * + * Return: 0 on success, -errno on failure. + */ +struct vfio_device_attach_iommufd_pt { + __u32 argsz; + __u32 flags; + __u32 pt_id; +}; + +#define VFIO_DEVICE_ATTACH_IOMMUFD_PT _IO(VFIO_TYPE, VFIO_BASE + 19) + +/* + * VFIO_DEVICE_DETACH_IOMMUFD_PT - _IOW(VFIO_TYPE, VFIO_BASE + 20, + * struct vfio_device_detach_iommufd_pt) + * @argsz: User filled size of this data. + * @flags: Must be 0. + * + * Remove the association of the device and its current associated address + * space. After it, the device should be in a blocking DMA state. This is only + * allowed on cdev fds. + * + * Return: 0 on success, -errno on failure. + */ +struct vfio_device_detach_iommufd_pt { + __u32 argsz; + __u32 flags; +}; + +#define VFIO_DEVICE_DETACH_IOMMUFD_PT _IO(VFIO_TYPE, VFIO_BASE + 20) + /* * Provide support for setting a PCI VF Token, which is used as a shared * secret between PF and VF drivers. This feature may only be set on a @@ -819,12 +999,20 @@ struct vfio_device_feature { * VFIO_MIGRATION_STOP_COPY | VFIO_MIGRATION_P2P means that RUNNING_P2P * is supported in addition to the STOP_COPY states. * + * VFIO_MIGRATION_STOP_COPY | VFIO_MIGRATION_PRE_COPY means that + * PRE_COPY is supported in addition to the STOP_COPY states. + * + * VFIO_MIGRATION_STOP_COPY | VFIO_MIGRATION_P2P | VFIO_MIGRATION_PRE_COPY + * means that RUNNING_P2P, PRE_COPY and PRE_COPY_P2P are supported + * in addition to the STOP_COPY states. + * * Other combinations of flags have behavior to be defined in the future. */ struct vfio_device_feature_migration { __aligned_u64 flags; #define VFIO_MIGRATION_STOP_COPY (1 << 0) #define VFIO_MIGRATION_P2P (1 << 1) +#define VFIO_MIGRATION_PRE_COPY (1 << 2) }; #define VFIO_DEVICE_FEATURE_MIGRATION 1 @@ -875,8 +1063,13 @@ struct vfio_device_feature_mig_state { * RESUMING - The device is stopped and is loading a new internal state * ERROR - The device has failed and must be reset * - * And 1 optional state to support VFIO_MIGRATION_P2P: + * And optional states to support VFIO_MIGRATION_P2P: * RUNNING_P2P - RUNNING, except the device cannot do peer to peer DMA + * And VFIO_MIGRATION_PRE_COPY: + * PRE_COPY - The device is running normally but tracking internal state + * changes + * And VFIO_MIGRATION_P2P | VFIO_MIGRATION_PRE_COPY: + * PRE_COPY_P2P - PRE_COPY, except the device cannot do peer to peer DMA * * The FSM takes actions on the arcs between FSM states. The driver implements * the following behavior for the FSM arcs: @@ -908,20 +1101,48 @@ struct vfio_device_feature_mig_state { * * To abort a RESUMING session the device must be reset. * + * PRE_COPY -> RUNNING * RUNNING_P2P -> RUNNING * While in RUNNING the device is fully operational, the device may generate * interrupts, DMA, respond to MMIO, all vfio device regions are functional, * and the device may advance its internal state. * + * The PRE_COPY arc will terminate a data transfer session. + * + * PRE_COPY_P2P -> RUNNING_P2P * RUNNING -> RUNNING_P2P * STOP -> RUNNING_P2P * While in RUNNING_P2P the device is partially running in the P2P quiescent * state defined below. * - * STOP -> STOP_COPY - * This arc begin the process of saving the device state and will return a - * new data_fd. + * The PRE_COPY_P2P arc will terminate a data transfer session. * + * RUNNING -> PRE_COPY + * RUNNING_P2P -> PRE_COPY_P2P + * STOP -> STOP_COPY + * PRE_COPY, PRE_COPY_P2P and STOP_COPY form the "saving group" of states + * which share a data transfer session. Moving between these states alters + * what is streamed in session, but does not terminate or otherwise affect + * the associated fd. + * + * These arcs begin the process of saving the device state and will return a + * new data_fd. The migration driver may perform actions such as enabling + * dirty logging of device state when entering PRE_COPY or PER_COPY_P2P. + * + * Each arc does not change the device operation, the device remains + * RUNNING, P2P quiesced or in STOP. The STOP_COPY state is described below + * in PRE_COPY_P2P -> STOP_COPY. + * + * PRE_COPY -> PRE_COPY_P2P + * Entering PRE_COPY_P2P continues all the behaviors of PRE_COPY above. + * However, while in the PRE_COPY_P2P state, the device is partially running + * in the P2P quiescent state defined below, like RUNNING_P2P. + * + * PRE_COPY_P2P -> PRE_COPY + * This arc allows returning the device to a full RUNNING behavior while + * continuing all the behaviors of PRE_COPY. + * + * PRE_COPY_P2P -> STOP_COPY * While in the STOP_COPY state the device has the same behavior as STOP * with the addition that the data transfers session continues to stream the * migration state. End of stream on the FD indicates the entire device @@ -939,6 +1160,13 @@ struct vfio_device_feature_mig_state { * device state for this arc if required to prepare the device to receive the * migration data. * + * STOP_COPY -> PRE_COPY + * STOP_COPY -> PRE_COPY_P2P + * These arcs are not permitted and return error if requested. Future + * revisions of this API may define behaviors for these arcs, in this case + * support will be discoverable by a new flag in + * VFIO_DEVICE_FEATURE_MIGRATION. + * * any -> ERROR * ERROR cannot be specified as a device state, however any transition request * can be failed with an errno return and may then move the device_state into @@ -950,7 +1178,7 @@ struct vfio_device_feature_mig_state { * The optional peer to peer (P2P) quiescent state is intended to be a quiescent * state for the device for the purposes of managing multiple devices within a * user context where peer-to-peer DMA between devices may be active. The - * RUNNING_P2P states must prevent the device from initiating + * RUNNING_P2P and PRE_COPY_P2P states must prevent the device from initiating * any new P2P DMA transactions. If the device can identify P2P transactions * then it can stop only P2P DMA, otherwise it must stop all DMA. The migration * driver must complete any such outstanding operations prior to completing the @@ -963,6 +1191,8 @@ struct vfio_device_feature_mig_state { * above FSM arcs. As there are multiple paths through the FSM arcs the path * should be selected based on the following rules: * - Select the shortest path. + * - The path cannot have saving group states as interior arcs, only + * starting/end states. * Refer to vfio_mig_get_next_state() for the result of the algorithm. * * The automatic transit through the FSM arcs that make up the combination @@ -976,6 +1206,9 @@ struct vfio_device_feature_mig_state { * support them. The user can discover if these states are supported by using * VFIO_DEVICE_FEATURE_MIGRATION. By using combination transitions the user can * avoid knowing about these optional states if the kernel driver supports them. + * + * Arcs touching PRE_COPY and PRE_COPY_P2P are removed if support for PRE_COPY + * is not present. */ enum vfio_device_mig_state { VFIO_DEVICE_STATE_ERROR = 0, @@ -984,8 +1217,247 @@ enum vfio_device_mig_state { VFIO_DEVICE_STATE_STOP_COPY = 3, VFIO_DEVICE_STATE_RESUMING = 4, VFIO_DEVICE_STATE_RUNNING_P2P = 5, + VFIO_DEVICE_STATE_PRE_COPY = 6, + VFIO_DEVICE_STATE_PRE_COPY_P2P = 7, + VFIO_DEVICE_STATE_NR, }; +/** + * VFIO_MIG_GET_PRECOPY_INFO - _IO(VFIO_TYPE, VFIO_BASE + 21) + * + * This ioctl is used on the migration data FD in the precopy phase of the + * migration data transfer. It returns an estimate of the current data sizes + * remaining to be transferred. It allows the user to judge when it is + * appropriate to leave PRE_COPY for STOP_COPY. + * + * This ioctl is valid only in PRE_COPY states and kernel driver should + * return -EINVAL from any other migration state. + * + * The vfio_precopy_info data structure returned by this ioctl provides + * estimates of data available from the device during the PRE_COPY states. + * This estimate is split into two categories, initial_bytes and + * dirty_bytes. + * + * The initial_bytes field indicates the amount of initial precopy + * data available from the device. This field should have a non-zero initial + * value and decrease as migration data is read from the device. + * It is recommended to leave PRE_COPY for STOP_COPY only after this field + * reaches zero. Leaving PRE_COPY earlier might make things slower. + * + * The dirty_bytes field tracks device state changes relative to data + * previously retrieved. This field starts at zero and may increase as + * the internal device state is modified or decrease as that modified + * state is read from the device. + * + * Userspace may use the combination of these fields to estimate the + * potential data size available during the PRE_COPY phases, as well as + * trends relative to the rate the device is dirtying its internal + * state, but these fields are not required to have any bearing relative + * to the data size available during the STOP_COPY phase. + * + * Drivers have a lot of flexibility in when and what they transfer during the + * PRE_COPY phase, and how they report this from VFIO_MIG_GET_PRECOPY_INFO. + * + * During pre-copy the migration data FD has a temporary "end of stream" that is + * reached when both initial_bytes and dirty_byte are zero. For instance, this + * may indicate that the device is idle and not currently dirtying any internal + * state. When read() is done on this temporary end of stream the kernel driver + * should return ENOMSG from read(). Userspace can wait for more data (which may + * never come) by using poll. + * + * Once in STOP_COPY the migration data FD has a permanent end of stream + * signaled in the usual way by read() always returning 0 and poll always + * returning readable. ENOMSG may not be returned in STOP_COPY. + * Support for this ioctl is mandatory if a driver claims to support + * VFIO_MIGRATION_PRE_COPY. + * + * Return: 0 on success, -1 and errno set on failure. + */ +struct vfio_precopy_info { + __u32 argsz; + __u32 flags; + __aligned_u64 initial_bytes; + __aligned_u64 dirty_bytes; +}; + +#define VFIO_MIG_GET_PRECOPY_INFO _IO(VFIO_TYPE, VFIO_BASE + 21) + +/* + * Upon VFIO_DEVICE_FEATURE_SET, allow the device to be moved into a low power + * state with the platform-based power management. Device use of lower power + * states depends on factors managed by the runtime power management core, + * including system level support and coordinating support among dependent + * devices. Enabling device low power entry does not guarantee lower power + * usage by the device, nor is a mechanism provided through this feature to + * know the current power state of the device. If any device access happens + * (either from the host or through the vfio uAPI) when the device is in the + * low power state, then the host will move the device out of the low power + * state as necessary prior to the access. Once the access is completed, the + * device may re-enter the low power state. For single shot low power support + * with wake-up notification, see + * VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY_WITH_WAKEUP below. Access to mmap'd + * device regions is disabled on LOW_POWER_ENTRY and may only be resumed after + * calling LOW_POWER_EXIT. + */ +#define VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY 3 + +/* + * This device feature has the same behavior as + * VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY with the exception that the user + * provides an eventfd for wake-up notification. When the device moves out of + * the low power state for the wake-up, the host will not allow the device to + * re-enter a low power state without a subsequent user call to one of the low + * power entry device feature IOCTLs. Access to mmap'd device regions is + * disabled on LOW_POWER_ENTRY_WITH_WAKEUP and may only be resumed after the + * low power exit. The low power exit can happen either through LOW_POWER_EXIT + * or through any other access (where the wake-up notification has been + * generated). The access to mmap'd device regions will not trigger low power + * exit. + * + * The notification through the provided eventfd will be generated only when + * the device has entered and is resumed from a low power state after + * calling this device feature IOCTL. A device that has not entered low power + * state, as managed through the runtime power management core, will not + * generate a notification through the provided eventfd on access. Calling the + * LOW_POWER_EXIT feature is optional in the case where notification has been + * signaled on the provided eventfd that a resume from low power has occurred. + */ +struct vfio_device_low_power_entry_with_wakeup { + __s32 wakeup_eventfd; + __u32 reserved; +}; + +#define VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY_WITH_WAKEUP 4 + +/* + * Upon VFIO_DEVICE_FEATURE_SET, disallow use of device low power states as + * previously enabled via VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY or + * VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY_WITH_WAKEUP device features. + * This device feature IOCTL may itself generate a wakeup eventfd notification + * in the latter case if the device had previously entered a low power state. + */ +#define VFIO_DEVICE_FEATURE_LOW_POWER_EXIT 5 + +/* + * Upon VFIO_DEVICE_FEATURE_SET start/stop device DMA logging. + * VFIO_DEVICE_FEATURE_PROBE can be used to detect if the device supports + * DMA logging. + * + * DMA logging allows a device to internally record what DMAs the device is + * initiating and report them back to userspace. It is part of the VFIO + * migration infrastructure that allows implementing dirty page tracking + * during the pre copy phase of live migration. Only DMA WRITEs are logged, + * and this API is not connected to VFIO_DEVICE_FEATURE_MIG_DEVICE_STATE. + * + * When DMA logging is started a range of IOVAs to monitor is provided and the + * device can optimize its logging to cover only the IOVA range given. Each + * DMA that the device initiates inside the range will be logged by the device + * for later retrieval. + * + * page_size is an input that hints what tracking granularity the device + * should try to achieve. If the device cannot do the hinted page size then + * it's the driver choice which page size to pick based on its support. + * On output the device will return the page size it selected. + * + * ranges is a pointer to an array of + * struct vfio_device_feature_dma_logging_range. + * + * The core kernel code guarantees to support by minimum num_ranges that fit + * into a single kernel page. User space can try higher values but should give + * up if the above can't be achieved as of some driver limitations. + * + * A single call to start device DMA logging can be issued and a matching stop + * should follow at the end. Another start is not allowed in the meantime. + */ +struct vfio_device_feature_dma_logging_control { + __aligned_u64 page_size; + __u32 num_ranges; + __u32 __reserved; + __aligned_u64 ranges; +}; + +struct vfio_device_feature_dma_logging_range { + __aligned_u64 iova; + __aligned_u64 length; +}; + +#define VFIO_DEVICE_FEATURE_DMA_LOGGING_START 6 + +/* + * Upon VFIO_DEVICE_FEATURE_SET stop device DMA logging that was started + * by VFIO_DEVICE_FEATURE_DMA_LOGGING_START + */ +#define VFIO_DEVICE_FEATURE_DMA_LOGGING_STOP 7 + +/* + * Upon VFIO_DEVICE_FEATURE_GET read back and clear the device DMA log + * + * Query the device's DMA log for written pages within the given IOVA range. + * During querying the log is cleared for the IOVA range. + * + * bitmap is a pointer to an array of u64s that will hold the output bitmap + * with 1 bit reporting a page_size unit of IOVA. The mapping of IOVA to bits + * is given by: + * bitmap[(addr - iova)/page_size] & (1ULL << (addr % 64)) + * + * The input page_size can be any power of two value and does not have to + * match the value given to VFIO_DEVICE_FEATURE_DMA_LOGGING_START. The driver + * will format its internal logging to match the reporting page size, possibly + * by replicating bits if the internal page size is lower than requested. + * + * The LOGGING_REPORT will only set bits in the bitmap and never clear or + * perform any initialization of the user provided bitmap. + * + * If any error is returned userspace should assume that the dirty log is + * corrupted. Error recovery is to consider all memory dirty and try to + * restart the dirty tracking, or to abort/restart the whole migration. + * + * If DMA logging is not enabled, an error will be returned. + * + */ +struct vfio_device_feature_dma_logging_report { + __aligned_u64 iova; + __aligned_u64 length; + __aligned_u64 page_size; + __aligned_u64 bitmap; +}; + +#define VFIO_DEVICE_FEATURE_DMA_LOGGING_REPORT 8 + +/* + * Upon VFIO_DEVICE_FEATURE_GET read back the estimated data length that will + * be required to complete stop copy. + * + * Note: Can be called on each device state. + */ + +struct vfio_device_feature_mig_data_size { + __aligned_u64 stop_copy_length; +}; + +#define VFIO_DEVICE_FEATURE_MIG_DATA_SIZE 9 + +/** + * Upon VFIO_DEVICE_FEATURE_SET, set or clear the BUS mastering for the device + * based on the operation specified in op flag. + * + * The functionality is incorporated for devices that needs bus master control, + * but the in-band device interface lacks the support. Consequently, it is not + * applicable to PCI devices, as bus master control for PCI devices is managed + * in-band through the configuration space. At present, this feature is supported + * only for CDX devices. + * When the device's BUS MASTER setting is configured as CLEAR, it will result in + * blocking all incoming DMA requests from the device. On the other hand, configuring + * the device's BUS MASTER setting as SET (enable) will grant the device the + * capability to perform DMA to the host memory. + */ +struct vfio_device_feature_bus_master { + __u32 op; +#define VFIO_DEVICE_FEATURE_CLEAR_MASTER 0 /* Clear Bus Master */ +#define VFIO_DEVICE_FEATURE_SET_MASTER 1 /* Set Bus Master */ +}; +#define VFIO_DEVICE_FEATURE_BUS_MASTER 10 + /* -------- API for Type1 VFIO IOMMU -------- */ /** @@ -1001,8 +1473,9 @@ struct vfio_iommu_type1_info { __u32 flags; #define VFIO_IOMMU_INFO_PGSIZES (1 << 0) /* supported page sizes info */ #define VFIO_IOMMU_INFO_CAPS (1 << 1) /* Info supports caps */ - __u64 iova_pgsizes; /* Bitmap of supported page sizes */ + __aligned_u64 iova_pgsizes; /* Bitmap of supported page sizes */ __u32 cap_offset; /* Offset within info struct of first cap */ + __u32 pad; }; /* @@ -1073,8 +1546,7 @@ struct vfio_iommu_type1_info_dma_avail { * Map process virtual addresses to IO virtual addresses using the * provided struct vfio_dma_map. Caller sets argsz. READ &/ WRITE required. * - * If flags & VFIO_DMA_MAP_FLAG_VADDR, update the base vaddr for iova, and - * unblock translation of host virtual addresses in the iova range. The vaddr + * If flags & VFIO_DMA_MAP_FLAG_VADDR, update the base vaddr for iova. The vaddr * must have previously been invalidated with VFIO_DMA_UNMAP_FLAG_VADDR. To * maintain memory consistency within the user application, the updated vaddr * must address the same memory object as originally mapped. Failure to do so @@ -1125,9 +1597,9 @@ struct vfio_bitmap { * must be 0. This cannot be combined with the get-dirty-bitmap flag. * * If flags & VFIO_DMA_UNMAP_FLAG_VADDR, do not unmap, but invalidate host - * virtual addresses in the iova range. Tasks that attempt to translate an - * iova's vaddr will block. DMA to already-mapped pages continues. This - * cannot be combined with the get-dirty-bitmap flag. + * virtual addresses in the iova range. DMA to already-mapped pages continues. + * Groups may not be added to the container while any addresses are invalid. + * This cannot be combined with the get-dirty-bitmap flag. */ struct vfio_iommu_type1_dma_unmap { __u32 argsz; diff --git a/linux-headers/linux/vfio_zdev.h b/linux-headers/linux/vfio_zdev.h index b4309397b6..77f2aff1f2 100644 --- a/linux-headers/linux/vfio_zdev.h +++ b/linux-headers/linux/vfio_zdev.h @@ -29,6 +29,9 @@ struct vfio_device_info_cap_zpci_base { __u16 fmb_length; /* Measurement Block Length (in bytes) */ __u8 pft; /* PCI Function Type */ __u8 gid; /* PCI function group ID */ + /* End of version 1 */ + __u32 fh; /* PCI function handle */ + /* End of version 2 */ }; /** @@ -47,6 +50,10 @@ struct vfio_device_info_cap_zpci_group { __u16 noi; /* Maximum number of MSIs */ __u16 maxstbl; /* Maximum Store Block Length */ __u8 version; /* Supported PCI Version */ + /* End of version 1 */ + __u8 reserved; + __u16 imaxstbl; /* Maximum Interpreted Store Block Length */ + /* End of version 2 */ }; /** diff --git a/linux-headers/linux/vhost.h b/linux-headers/linux/vhost.h index 5d99e7c242..649560c685 100644 --- a/linux-headers/linux/vhost.h +++ b/linux-headers/linux/vhost.h @@ -45,6 +45,25 @@ #define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64) /* Specify an eventfd file descriptor to signal on log write. */ #define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int) +/* By default, a device gets one vhost_worker that its virtqueues share. This + * command allows the owner of the device to create an additional vhost_worker + * for the device. It can later be bound to 1 or more of its virtqueues using + * the VHOST_ATTACH_VRING_WORKER command. + * + * This must be called after VHOST_SET_OWNER and the caller must be the owner + * of the device. The new thread will inherit caller's cgroups and namespaces, + * and will share the caller's memory space. The new thread will also be + * counted against the caller's RLIMIT_NPROC value. + * + * The worker's ID used in other commands will be returned in + * vhost_worker_state. + */ +#define VHOST_NEW_WORKER _IOR(VHOST_VIRTIO, 0x8, struct vhost_worker_state) +/* Free a worker created with VHOST_NEW_WORKER if it's not attached to any + * virtqueue. If userspace is not able to call this for workers its created, + * the kernel will free all the device's workers when the device is closed. + */ +#define VHOST_FREE_WORKER _IOW(VHOST_VIRTIO, 0x9, struct vhost_worker_state) /* Ring setup. */ /* Set number of descriptors in ring. This parameter can not @@ -70,6 +89,18 @@ #define VHOST_VRING_BIG_ENDIAN 1 #define VHOST_SET_VRING_ENDIAN _IOW(VHOST_VIRTIO, 0x13, struct vhost_vring_state) #define VHOST_GET_VRING_ENDIAN _IOW(VHOST_VIRTIO, 0x14, struct vhost_vring_state) +/* Attach a vhost_worker created with VHOST_NEW_WORKER to one of the device's + * virtqueues. + * + * This will replace the virtqueue's existing worker. If the replaced worker + * is no longer attached to any virtqueues, it can be freed with + * VHOST_FREE_WORKER. + */ +#define VHOST_ATTACH_VRING_WORKER _IOW(VHOST_VIRTIO, 0x15, \ + struct vhost_vring_worker) +/* Return the vring worker's ID */ +#define VHOST_GET_VRING_WORKER _IOWR(VHOST_VIRTIO, 0x16, \ + struct vhost_vring_worker) /* The following ioctls use eventfd file descriptors to signal and poll * for events. */ @@ -89,11 +120,6 @@ /* Set or get vhost backend capability */ -/* Use message type V2 */ -#define VHOST_BACKEND_F_IOTLB_MSG_V2 0x1 -/* IOTLB can accept batching hints */ -#define VHOST_BACKEND_F_IOTLB_BATCH 0x2 - #define VHOST_SET_BACKEND_FEATURES _IOW(VHOST_VIRTIO, 0x25, __u64) #define VHOST_GET_BACKEND_FEATURES _IOR(VHOST_VIRTIO, 0x26, __u64) @@ -150,11 +176,55 @@ /* Get the valid iova range */ #define VHOST_VDPA_GET_IOVA_RANGE _IOR(VHOST_VIRTIO, 0x78, \ struct vhost_vdpa_iova_range) - /* Get the config size */ #define VHOST_VDPA_GET_CONFIG_SIZE _IOR(VHOST_VIRTIO, 0x79, __u32) /* Get the count of all virtqueues */ #define VHOST_VDPA_GET_VQS_COUNT _IOR(VHOST_VIRTIO, 0x80, __u32) +/* Get the number of virtqueue groups. */ +#define VHOST_VDPA_GET_GROUP_NUM _IOR(VHOST_VIRTIO, 0x81, __u32) + +/* Get the number of address spaces. */ +#define VHOST_VDPA_GET_AS_NUM _IOR(VHOST_VIRTIO, 0x7A, unsigned int) + +/* Get the group for a virtqueue: read index, write group in num, + * The virtqueue index is stored in the index field of + * vhost_vring_state. The group for this specific virtqueue is + * returned via num field of vhost_vring_state. + */ +#define VHOST_VDPA_GET_VRING_GROUP _IOWR(VHOST_VIRTIO, 0x7B, \ + struct vhost_vring_state) +/* Set the ASID for a virtqueue group. The group index is stored in + * the index field of vhost_vring_state, the ASID associated with this + * group is stored at num field of vhost_vring_state. + */ +#define VHOST_VDPA_SET_GROUP_ASID _IOW(VHOST_VIRTIO, 0x7C, \ + struct vhost_vring_state) + +/* Suspend a device so it does not process virtqueue requests anymore + * + * After the return of ioctl the device must preserve all the necessary state + * (the virtqueue vring base plus the possible device specific states) that is + * required for restoring in the future. The device must not change its + * configuration after that point. + */ +#define VHOST_VDPA_SUSPEND _IO(VHOST_VIRTIO, 0x7D) + +/* Resume a device so it can resume processing virtqueue requests + * + * After the return of this ioctl the device will have restored all the + * necessary states and it is fully operational to continue processing the + * virtqueue descriptors. + */ +#define VHOST_VDPA_RESUME _IO(VHOST_VIRTIO, 0x7E) + +/* Get the group for the descriptor table including driver & device areas + * of a virtqueue: read index, write group in num. + * The virtqueue index is stored in the index field of vhost_vring_state. + * The group ID of the descriptor table for this specific virtqueue + * is returned via num field of vhost_vring_state. + */ +#define VHOST_VDPA_GET_VRING_DESC_GROUP _IOWR(VHOST_VIRTIO, 0x7F, \ + struct vhost_vring_state) #endif diff --git a/linux-user/aarch64/Makefile.vdso b/linux-user/aarch64/Makefile.vdso new file mode 100644 index 0000000000..599958116b --- /dev/null +++ b/linux-user/aarch64/Makefile.vdso @@ -0,0 +1,15 @@ +include $(BUILD_DIR)/tests/tcg/aarch64-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/aarch64 +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso-be.so $(SUBDIR)/vdso-le.so + +LDFLAGS = -nostdlib -shared -Wl,-h,linux-vdso.so.1 -Wl,--build-id=sha1 \ + -Wl,--hash-style=both -Wl,-T,$(SUBDIR)/vdso.ld + +$(SUBDIR)/vdso-be.so: vdso.S vdso.ld + $(CC) -o $@ $(LDFLAGS) -mbig-endian $< + +$(SUBDIR)/vdso-le.so: vdso.S vdso.ld + $(CC) -o $@ $(LDFLAGS) -mlittle-endian $< diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 3b273f6299..71cdc8be50 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -25,6 +25,7 @@ #include "qemu/guest-random.h" #include "semihosting/common-semi.h" #include "target/arm/syndrome.h" +#include "target/arm/cpu-features.h" #define get_user_code_u32(x, gaddr, env) \ ({ abi_long __r = get_user_u32((x), (gaddr)); \ @@ -89,6 +90,8 @@ void cpu_loop(CPUARMState *env) switch (trapnr) { case EXCP_SWI: + /* On syscall, PSTATE.ZA is preserved, PSTATE.SM is cleared. */ + aarch64_set_svcr(env, 0, R_SVCR_SM_MASK); ret = do_syscall(env, env->xregs[8], env->xregs[0], @@ -154,7 +157,7 @@ void cpu_loop(CPUARMState *env) force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); break; case EXCP_SEMIHOST: - env->xregs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->pc += 4; break; case EXCP_YIELD: @@ -186,7 +189,7 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { ARMCPU *cpu = env_archcpu(env); CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); struct image_info *info = ts->info; int i; diff --git a/linux-user/aarch64/meson.build b/linux-user/aarch64/meson.build new file mode 100644 index 0000000000..248c578d15 --- /dev/null +++ b/linux-user/aarch64/meson.build @@ -0,0 +1,11 @@ +# TARGET_BIG_ENDIAN is defined to 'n' for little-endian; which means it +# is always true as far as source_set.apply() is concerned. Always build +# both header files and include the right one via #if. + +vdso_be_inc = gen_vdso.process('vdso-be.so', + extra_args: ['-r', '__kernel_rt_sigreturn']) + +vdso_le_inc = gen_vdso.process('vdso-le.so', + extra_args: ['-r', '__kernel_rt_sigreturn']) + +linux_user_ss.add(when: 'TARGET_AARCH64', if_true: [vdso_be_inc, vdso_le_inc]) diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index 7da0e36c6d..bc7a13800d 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -21,6 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "target/arm/cpu-features.h" struct target_sigcontext { uint64_t fault_address; @@ -78,7 +79,8 @@ struct target_extra_context { struct target_sve_context { struct target_aarch64_ctx head; uint16_t vl; - uint16_t reserved[3]; + uint16_t flags; + uint16_t reserved[2]; /* The actual SVE data immediately follows. It is laid out * according to TARGET_SVE_SIG_{Z,P}REG_OFFSET, based off of * the original struct pointer. @@ -101,6 +103,24 @@ struct target_sve_context { #define TARGET_SVE_SIG_CONTEXT_SIZE(VQ) \ (TARGET_SVE_SIG_PREG_OFFSET(VQ, 17)) +#define TARGET_SVE_SIG_FLAG_SM 1 + +#define TARGET_ZA_MAGIC 0x54366345 + +struct target_za_context { + struct target_aarch64_ctx head; + uint16_t vl; + uint16_t reserved[3]; + /* The actual ZA data immediately follows. */ +}; + +#define TARGET_ZA_SIG_REGS_OFFSET \ + QEMU_ALIGN_UP(sizeof(struct target_za_context), TARGET_SVE_VQ_BYTES) +#define TARGET_ZA_SIG_ZAV_OFFSET(VQ, N) \ + (TARGET_ZA_SIG_REGS_OFFSET + (VQ) * TARGET_SVE_VQ_BYTES * (N)) +#define TARGET_ZA_SIG_CONTEXT_SIZE(VQ) \ + TARGET_ZA_SIG_ZAV_OFFSET(VQ, VQ * TARGET_SVE_VQ_BYTES) + struct target_rt_sigframe { struct target_siginfo info; struct target_ucontext uc; @@ -173,13 +193,17 @@ static void target_setup_end_record(struct target_aarch64_ctx *end) } static void target_setup_sve_record(struct target_sve_context *sve, - CPUARMState *env, int vq, int size) + CPUARMState *env, int size) { - int i, j; + int i, j, vq = sve_vq(env); + memset(sve, 0, sizeof(*sve)); __put_user(TARGET_SVE_MAGIC, &sve->head.magic); __put_user(size, &sve->head.size); __put_user(vq * TARGET_SVE_VQ_BYTES, &sve->vl); + if (FIELD_EX64(env->svcr, SVCR, SM)) { + __put_user(TARGET_SVE_SIG_FLAG_SM, &sve->flags); + } /* Note that SVE regs are stored as a byte stream, with each byte element * at a subsequent address. This corresponds to a little-endian store @@ -200,6 +224,35 @@ static void target_setup_sve_record(struct target_sve_context *sve, } } +static void target_setup_za_record(struct target_za_context *za, + CPUARMState *env, int size) +{ + int vq = sme_vq(env); + int vl = vq * TARGET_SVE_VQ_BYTES; + int i, j; + + memset(za, 0, sizeof(*za)); + __put_user(TARGET_ZA_MAGIC, &za->head.magic); + __put_user(size, &za->head.size); + __put_user(vl, &za->vl); + + if (size == TARGET_ZA_SIG_CONTEXT_SIZE(0)) { + return; + } + assert(size == TARGET_ZA_SIG_CONTEXT_SIZE(vq)); + + /* + * Note that ZA vectors are stored as a byte stream, + * with each byte element at a subsequent address. + */ + for (i = 0; i < vl; ++i) { + uint64_t *z = (void *)za + TARGET_ZA_SIG_ZAV_OFFSET(vq, i); + for (j = 0; j < vq * 2; ++j) { + __put_user_e(env->zarray[i].d[j], z + j, le); + } + } +} + static void target_restore_general_frame(CPUARMState *env, struct target_rt_sigframe *sf) { @@ -243,12 +296,50 @@ static void target_restore_fpsimd_record(CPUARMState *env, } } -static void target_restore_sve_record(CPUARMState *env, - struct target_sve_context *sve, int vq) +static bool target_restore_sve_record(CPUARMState *env, + struct target_sve_context *sve, + int size, int *svcr) { - int i, j; + int i, j, vl, vq, flags; + bool sm; - /* Note that SVE regs are stored as a byte stream, with each byte element + __get_user(vl, &sve->vl); + __get_user(flags, &sve->flags); + + sm = flags & TARGET_SVE_SIG_FLAG_SM; + + /* The cpu must support Streaming or Non-streaming SVE. */ + if (sm + ? !cpu_isar_feature(aa64_sme, env_archcpu(env)) + : !cpu_isar_feature(aa64_sve, env_archcpu(env))) { + return false; + } + + /* + * Note that we cannot use sve_vq() because that depends on the + * current setting of PSTATE.SM, not the state to be restored. + */ + vq = sve_vqm1_for_el_sm(env, 0, sm) + 1; + + /* Reject mismatched VL. */ + if (vl != vq * TARGET_SVE_VQ_BYTES) { + return false; + } + + /* Accept empty record -- used to clear PSTATE.SM. */ + if (size <= sizeof(*sve)) { + return true; + } + + /* Reject non-empty but incomplete record. */ + if (size < TARGET_SVE_SIG_CONTEXT_SIZE(vq)) { + return false; + } + + *svcr = FIELD_DP64(*svcr, SVCR, SM, sm); + + /* + * Note that SVE regs are stored as a byte stream, with each byte element * at a subsequent address. This corresponds to a little-endian load * of our 64-bit hunks. */ @@ -270,6 +361,46 @@ static void target_restore_sve_record(CPUARMState *env, } } } + return true; +} + +static bool target_restore_za_record(CPUARMState *env, + struct target_za_context *za, + int size, int *svcr) +{ + int i, j, vl, vq; + + if (!cpu_isar_feature(aa64_sme, env_archcpu(env))) { + return false; + } + + __get_user(vl, &za->vl); + vq = sme_vq(env); + + /* Reject mismatched VL. */ + if (vl != vq * TARGET_SVE_VQ_BYTES) { + return false; + } + + /* Accept empty record -- used to clear PSTATE.ZA. */ + if (size <= TARGET_ZA_SIG_CONTEXT_SIZE(0)) { + return true; + } + + /* Reject non-empty but incomplete record. */ + if (size < TARGET_ZA_SIG_CONTEXT_SIZE(vq)) { + return false; + } + + *svcr = FIELD_DP64(*svcr, SVCR, ZA, 1); + + for (i = 0; i < vl; ++i) { + uint64_t *z = (void *)za + TARGET_ZA_SIG_ZAV_OFFSET(vq, i); + for (j = 0; j < vq * 2; ++j) { + __get_user_e(env->zarray[i].d[j], z + j, le); + } + } + return true; } static int target_restore_sigframe(CPUARMState *env, @@ -278,10 +409,12 @@ static int target_restore_sigframe(CPUARMState *env, struct target_aarch64_ctx *ctx, *extra = NULL; struct target_fpsimd_context *fpsimd = NULL; struct target_sve_context *sve = NULL; + struct target_za_context *za = NULL; uint64_t extra_datap = 0; bool used_extra = false; - bool err = false; - int vq = 0, sve_size = 0; + int sve_size = 0; + int za_size = 0; + int svcr = 0; target_restore_general_frame(env, sf); @@ -294,8 +427,7 @@ static int target_restore_sigframe(CPUARMState *env, switch (magic) { case 0: if (size != 0) { - err = true; - goto exit; + goto err; } if (used_extra) { ctx = NULL; @@ -307,42 +439,46 @@ static int target_restore_sigframe(CPUARMState *env, case TARGET_FPSIMD_MAGIC: if (fpsimd || size != sizeof(struct target_fpsimd_context)) { - err = true; - goto exit; + goto err; } fpsimd = (struct target_fpsimd_context *)ctx; break; case TARGET_SVE_MAGIC: - if (cpu_isar_feature(aa64_sve, env_archcpu(env))) { - vq = sve_vq(env); - sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); - if (!sve && size == sve_size) { - sve = (struct target_sve_context *)ctx; - break; - } + if (sve || size < sizeof(struct target_sve_context)) { + goto err; } - err = true; - goto exit; + sve = (struct target_sve_context *)ctx; + sve_size = size; + break; + + case TARGET_ZA_MAGIC: + if (za || size < sizeof(struct target_za_context)) { + goto err; + } + za = (struct target_za_context *)ctx; + za_size = size; + break; case TARGET_EXTRA_MAGIC: if (extra || size != sizeof(struct target_extra_context)) { - err = true; - goto exit; + goto err; } __get_user(extra_datap, &((struct target_extra_context *)ctx)->datap); __get_user(extra_size, &((struct target_extra_context *)ctx)->size); extra = lock_user(VERIFY_READ, extra_datap, extra_size, 0); + if (!extra) { + return 1; + } break; default: /* Unknown record -- we certainly didn't generate it. * Did we in fact get out of sync? */ - err = true; - goto exit; + goto err; } ctx = (void *)ctx + size; } @@ -351,17 +487,26 @@ static int target_restore_sigframe(CPUARMState *env, if (fpsimd) { target_restore_fpsimd_record(env, fpsimd); } else { - err = true; + goto err; } /* SVE data, if present, overwrites FPSIMD data. */ - if (sve) { - target_restore_sve_record(env, sve, vq); + if (sve && !target_restore_sve_record(env, sve, sve_size, &svcr)) { + goto err; + } + if (za && !target_restore_za_record(env, za, za_size, &svcr)) { + goto err; + } + if (env->svcr != svcr) { + env->svcr = svcr; + arm_rebuild_hflags(env); } - - exit: unlock_user(extra, extra_datap, 0); - return err; + return 0; + + err: + unlock_user(extra, extra_datap, 0); + return 1; } static abi_ulong get_sigframe(struct target_sigaction *ka, @@ -423,7 +568,8 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, .total_size = offsetof(struct target_rt_sigframe, uc.tuc_mcontext.__reserved), }; - int fpsimd_ofs, fr_ofs, sve_ofs = 0, vq = 0, sve_size = 0; + int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0; + int sve_size = 0, za_size = 0; struct target_rt_sigframe *frame; struct target_rt_frame_record *fr; abi_ulong frame_addr, return_addr; @@ -433,11 +579,20 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, &layout); /* SVE state needs saving only if it exists. */ - if (cpu_isar_feature(aa64_sve, env_archcpu(env))) { - vq = sve_vq(env); - sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); + if (cpu_isar_feature(aa64_sve, env_archcpu(env)) || + cpu_isar_feature(aa64_sme, env_archcpu(env))) { + sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(sve_vq(env)), 16); sve_ofs = alloc_sigframe_space(sve_size, &layout); } + if (cpu_isar_feature(aa64_sme, env_archcpu(env))) { + /* ZA state needs saving only if it is enabled. */ + if (FIELD_EX64(env->svcr, SVCR, ZA)) { + za_size = TARGET_ZA_SIG_CONTEXT_SIZE(sme_vq(env)); + } else { + za_size = TARGET_ZA_SIG_CONTEXT_SIZE(0); + } + za_ofs = alloc_sigframe_space(za_size, &layout); + } if (layout.extra_ofs) { /* Reserve space for the extra end marker. The standard end marker @@ -484,7 +639,10 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, target_setup_end_record((void *)frame + layout.extra_end_ofs); } if (sve_ofs) { - target_setup_sve_record((void *)frame + sve_ofs, env, vq, sve_size); + target_setup_sve_record((void *)frame + sve_ofs, env, sve_size); + } + if (za_ofs) { + target_setup_za_record((void *)frame + za_ofs, env, za_size); } /* Set up the stack frame for unwinding. */ @@ -508,8 +666,11 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, env->btype = 2; } + /* Invoke the signal handler with both SM and ZA disabled. */ + aarch64_set_svcr(env, 0, R_SVCR_SM_MASK | R_SVCR_ZA_MASK); + if (info) { - tswap_siginfo(&frame->info, info); + frame->info = *info; env->xregs[1] = frame_addr + offsetof(struct target_rt_sigframe, info); env->xregs[2] = frame_addr + offsetof(struct target_rt_sigframe, uc); } diff --git a/linux-user/aarch64/target_cpu.h b/linux-user/aarch64/target_cpu.h index 97a477bd3e..f90359faf2 100644 --- a/linux-user/aarch64/target_cpu.h +++ b/linux-user/aarch64/target_cpu.h @@ -34,10 +34,13 @@ static inline void cpu_clone_regs_parent(CPUARMState *env, unsigned flags) static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls) { - /* Note that AArch64 Linux keeps the TLS pointer in TPIDR; this is + /* + * Note that AArch64 Linux keeps the TLS pointer in TPIDR; this is * different from AArch32 Linux, which uses TPIDRRO. */ env->cp15.tpidr_el[0] = newtls; + /* TPIDR2_EL0 is cleared with CLONE_SETTLS. */ + env->cp15.tpidr2_el0 = 0; } static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) diff --git a/linux-user/aarch64/target_flat.h b/linux-user/aarch64/target_flat.h new file mode 100644 index 0000000000..bc83224cea --- /dev/null +++ b/linux-user/aarch64/target_flat.h @@ -0,0 +1 @@ +#include "../generic/target_flat.h" diff --git a/linux-user/aarch64/target_mman.h b/linux-user/aarch64/target_mman.h new file mode 100644 index 0000000000..69ec5d5739 --- /dev/null +++ b/linux-user/aarch64/target_mman.h @@ -0,0 +1,22 @@ +#ifndef AARCH64_TARGET_MMAN_H +#define AARCH64_TARGET_MMAN_H + +#define TARGET_PROT_BTI 0x10 +#define TARGET_PROT_MTE 0x20 + +/* + * arch/arm64/include/asm/processor.h: + * + * TASK_UNMAPPED_BASE DEFAULT_MAP_WINDOW / 4 + * DEFAULT_MAP_WINDOW DEFAULT_MAP_WINDOW_64 + * DEFAULT_MAP_WINDOW_64 UL(1) << VA_BITS_MIN + * VA_BITS_MIN 48 (unless explicitly configured smaller) + */ +#define TASK_UNMAPPED_BASE (1ull << (48 - 2)) + +/* arch/arm64/include/asm/elf.h */ +#define ELF_ET_DYN_BASE TARGET_PAGE_ALIGN((1ull << 48) / 3 * 2) + +#include "../generic/target_mman.h" + +#endif diff --git a/linux-user/aarch64/target_prctl.h b/linux-user/aarch64/target_prctl.h index 1d440ffbea..aa8e203c15 100644 --- a/linux-user/aarch64/target_prctl.h +++ b/linux-user/aarch64/target_prctl.h @@ -6,17 +6,20 @@ #ifndef AARCH64_TARGET_PRCTL_H #define AARCH64_TARGET_PRCTL_H -static abi_long do_prctl_get_vl(CPUArchState *env) +#include "target/arm/cpu-features.h" + +static abi_long do_prctl_sve_get_vl(CPUArchState *env) { ARMCPU *cpu = env_archcpu(env); if (cpu_isar_feature(aa64_sve, cpu)) { + /* PSTATE.SM is always unset on syscall entry. */ return sve_vq(env) * 16; } return -TARGET_EINVAL; } -#define do_prctl_get_vl do_prctl_get_vl +#define do_prctl_sve_get_vl do_prctl_sve_get_vl -static abi_long do_prctl_set_vl(CPUArchState *env, abi_long arg2) +static abi_long do_prctl_sve_set_vl(CPUArchState *env, abi_long arg2) { /* * We cannot support either PR_SVE_SET_VL_ONEXEC or PR_SVE_VL_INHERIT. @@ -27,6 +30,7 @@ static abi_long do_prctl_set_vl(CPUArchState *env, abi_long arg2) && arg2 >= 0 && arg2 <= 512 * 16 && !(arg2 & 15)) { uint32_t vq, old_vq; + /* PSTATE.SM is always unset on syscall entry. */ old_vq = sve_vq(env); /* @@ -47,7 +51,59 @@ static abi_long do_prctl_set_vl(CPUArchState *env, abi_long arg2) } return -TARGET_EINVAL; } -#define do_prctl_set_vl do_prctl_set_vl +#define do_prctl_sve_set_vl do_prctl_sve_set_vl + +static abi_long do_prctl_sme_get_vl(CPUArchState *env) +{ + ARMCPU *cpu = env_archcpu(env); + if (cpu_isar_feature(aa64_sme, cpu)) { + return sme_vq(env) * 16; + } + return -TARGET_EINVAL; +} +#define do_prctl_sme_get_vl do_prctl_sme_get_vl + +static abi_long do_prctl_sme_set_vl(CPUArchState *env, abi_long arg2) +{ + /* + * We cannot support either PR_SME_SET_VL_ONEXEC or PR_SME_VL_INHERIT. + * Note the kernel definition of sve_vl_valid allows for VQ=512, + * i.e. VL=8192, even though the architectural maximum is VQ=16. + */ + if (cpu_isar_feature(aa64_sme, env_archcpu(env)) + && arg2 >= 0 && arg2 <= 512 * 16 && !(arg2 & 15)) { + int vq, old_vq; + + old_vq = sme_vq(env); + + /* + * Bound the value of vq, so that we know that it fits into + * the 4-bit field in SMCR_EL1. Because PSTATE.SM is cleared + * on syscall entry, we are not modifying the current SVE + * vector length. + */ + vq = MAX(arg2 / 16, 1); + vq = MIN(vq, 16); + env->vfp.smcr_el[1] = + FIELD_DP64(env->vfp.smcr_el[1], SMCR, LEN, vq - 1); + + /* Delay rebuilding hflags until we know if ZA must change. */ + vq = sve_vqm1_for_el_sm(env, 0, true) + 1; + + if (vq != old_vq) { + /* + * PSTATE.ZA state is cleared on any change to SVL. + * We need not call arm_rebuild_hflags because PSTATE.SM was + * cleared on syscall entry, so this hasn't changed VL. + */ + env->svcr = FIELD_DP64(env->svcr, SVCR, ZA, 0); + arm_rebuild_hflags(env); + } + return vq * 16; + } + return -TARGET_EINVAL; +} +#define do_prctl_sme_set_vl do_prctl_sme_set_vl static abi_long do_prctl_reset_keys(CPUArchState *env, abi_long arg2) { @@ -117,21 +173,26 @@ static abi_long do_prctl_set_tagged_addr_ctrl(CPUArchState *env, abi_long arg2) env->tagged_addr_enable = arg2 & PR_TAGGED_ADDR_ENABLE; if (cpu_isar_feature(aa64_mte, cpu)) { - switch (arg2 & PR_MTE_TCF_MASK) { - case PR_MTE_TCF_NONE: - case PR_MTE_TCF_SYNC: - case PR_MTE_TCF_ASYNC: - break; - default: - return -EINVAL; - } - /* * Write PR_MTE_TCF to SCTLR_EL1[TCF0]. - * Note that the syscall values are consistent with hw. + * + * The kernel has a per-cpu configuration for the sysadmin, + * /sys/devices/system/cpu/cpu/mte_tcf_preferred, + * which qemu does not implement. + * + * Because there is no performance difference between the modes, and + * because SYNC is most useful for debugging MTE errors, choose SYNC + * as the preferred mode. With this preference, and the way the API + * uses only two bits, there is no way for the program to select + * ASYMM mode. */ - env->cp15.sctlr_el[1] = - deposit64(env->cp15.sctlr_el[1], 38, 2, arg2 >> PR_MTE_TCF_SHIFT); + unsigned tcf = 0; + if (arg2 & PR_MTE_TCF_SYNC) { + tcf = 1; + } else if (arg2 & PR_MTE_TCF_ASYNC) { + tcf = 2; + } + env->cp15.sctlr_el[1] = deposit64(env->cp15.sctlr_el[1], 38, 2, tcf); /* * Write PR_MTE_TAG to GCR_EL1[Exclude]. diff --git a/linux-user/aarch64/target_proc.h b/linux-user/aarch64/target_proc.h new file mode 100644 index 0000000000..907df4dcd2 --- /dev/null +++ b/linux-user/aarch64/target_proc.h @@ -0,0 +1 @@ +#include "../arm/target_proc.h" diff --git a/linux-user/aarch64/vdso-be.so b/linux-user/aarch64/vdso-be.so new file mode 100755 index 0000000000..808206ade8 Binary files /dev/null and b/linux-user/aarch64/vdso-be.so differ diff --git a/linux-user/aarch64/vdso-le.so b/linux-user/aarch64/vdso-le.so new file mode 100755 index 0000000000..941aaf2993 Binary files /dev/null and b/linux-user/aarch64/vdso-le.so differ diff --git a/linux-user/aarch64/vdso.S b/linux-user/aarch64/vdso.S new file mode 100644 index 0000000000..a0ac1487b0 --- /dev/null +++ b/linux-user/aarch64/vdso.S @@ -0,0 +1,75 @@ +/* + * aarch64 linux replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +/* ??? These are in include/elf.h, which is not ready for inclusion in asm. */ +#define NT_GNU_PROPERTY_TYPE_0 5 +#define GNU_PROPERTY_AARCH64_FEATURE_1_AND 0xc0000000 +#define GNU_PROPERTY_AARCH64_FEATURE_1_BTI (1U << 0) +#define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1U << 1) + +#define GNU_PROPERTY_AARCH64_FEATURE_1_DEFAULT \ + (GNU_PROPERTY_AARCH64_FEATURE_1_BTI | GNU_PROPERTY_AARCH64_FEATURE_1_PAC) + + .section .note.gnu.property + .align 3 + .long 2f - 1f + .long 6f - 3f + .long NT_GNU_PROPERTY_TYPE_0 +1: .string "GNU" +2: .align 3 +3: .long GNU_PROPERTY_AARCH64_FEATURE_1_AND + .long 5f - 4f +4: .long GNU_PROPERTY_AARCH64_FEATURE_1_DEFAULT +5: .align 3 +6: + + .text + +.macro endf name + .globl \name + .type \name, @function + .size \name, . - \name +.endm + +.macro vdso_syscall name, nr +\name: + bti c + mov x8, #\nr + svc #0 + ret +endf \name +.endm + + .cfi_startproc + +vdso_syscall __kernel_gettimeofday, __NR_gettimeofday +vdso_syscall __kernel_clock_gettime, __NR_clock_gettime +vdso_syscall __kernel_clock_getres, __NR_clock_getres + + .cfi_endproc + + +/* + * TODO: The kernel makes a big deal of turning off the .cfi directives, + * because they cause libgcc to crash, but that's because they're wrong. + * + * For now, elide the unwind info for __kernel_rt_sigreturn and rely on + * the libgcc fallback routine as we have always done. This requires + * that the code sequence used be exact. + * + * Add a nop as a spacer to ensure that unwind does not pick up the + * unwind info from the preceding syscall. + */ + nop +__kernel_rt_sigreturn: + /* No BTI C insn here -- we arrive via RET. */ + mov x8, #__NR_rt_sigreturn + svc #0 +endf __kernel_rt_sigreturn diff --git a/linux-user/aarch64/vdso.ld b/linux-user/aarch64/vdso.ld new file mode 100644 index 0000000000..4c12f33352 --- /dev/null +++ b/linux-user/aarch64/vdso.ld @@ -0,0 +1,72 @@ +/* + * Linker script for linux aarch64 replacement vdso. + * + * Copyright 2021 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_2.6.39 { + global: + __kernel_rt_sigreturn; + __kernel_gettimeofday; + __kernel_clock_gettime; + __kernel_clock_getres; + + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + /* + * We can't prelink to any address without knowing something about + * the virtual memory space of the host, since that leaks over into + * the available memory space of the guest. + */ + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + .data : { + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load =0xd503201f +} diff --git a/linux-user/alpha/signal.c b/linux-user/alpha/signal.c index 4ec42994d4..896c2c148a 100644 --- a/linux-user/alpha/signal.c +++ b/linux-user/alpha/signal.c @@ -173,7 +173,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, goto give_sigsegv; } - tswap_siginfo(&frame->info, info); + frame->info = *info; __put_user(0, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_link); diff --git a/linux-user/alpha/target_elf.h b/linux-user/alpha/target_elf.h index 344e9f4d39..b77d638f6d 100644 --- a/linux-user/alpha/target_elf.h +++ b/linux-user/alpha/target_elf.h @@ -9,6 +9,6 @@ #define ALPHA_TARGET_ELF_H static inline const char *cpu_get_model(uint32_t eflags) { - return "any"; + return "ev67"; } #endif diff --git a/linux-user/alpha/target_mman.h b/linux-user/alpha/target_mman.h new file mode 100644 index 0000000000..8edfe2b88c --- /dev/null +++ b/linux-user/alpha/target_mman.h @@ -0,0 +1,36 @@ +#ifndef ALPHA_TARGET_MMAN_H +#define ALPHA_TARGET_MMAN_H + +#define TARGET_MAP_ANONYMOUS 0x10 +#define TARGET_MAP_FIXED 0x100 +#define TARGET_MAP_GROWSDOWN 0x01000 +#define TARGET_MAP_DENYWRITE 0x02000 +#define TARGET_MAP_EXECUTABLE 0x04000 +#define TARGET_MAP_LOCKED 0x08000 +#define TARGET_MAP_NORESERVE 0x10000 +#define TARGET_MAP_POPULATE 0x20000 +#define TARGET_MAP_NONBLOCK 0x40000 +#define TARGET_MAP_STACK 0x80000 +#define TARGET_MAP_HUGETLB 0x100000 +#define TARGET_MAP_FIXED_NOREPLACE 0x200000 + +#define TARGET_MADV_DONTNEED 6 + +#define TARGET_MS_ASYNC 1 +#define TARGET_MS_SYNC 2 +#define TARGET_MS_INVALIDATE 4 + +/* + * arch/alpha/include/asm/processor.h: + * + * TASK_UNMAPPED_BASE TASK_SIZE / 2 + * TASK_SIZE 0x40000000000UL + */ +#define TASK_UNMAPPED_BASE 0x20000000000ull + +/* arch/alpha/include/asm/elf.h */ +#define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE + 0x1000000) + +#include "../generic/target_mman.h" + +#endif diff --git a/linux-user/alpha/target_proc.h b/linux-user/alpha/target_proc.h new file mode 100644 index 0000000000..dac37dffc9 --- /dev/null +++ b/linux-user/alpha/target_proc.h @@ -0,0 +1,67 @@ +/* + * Alpha specific proc functions for linux-user + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef ALPHA_TARGET_PROC_H +#define ALPHA_TARGET_PROC_H + +static int open_cpuinfo(CPUArchState *cpu_env, int fd) +{ + int max_cpus = sysconf(_SC_NPROCESSORS_CONF); + int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + unsigned long cpu_mask; + char model[32]; + const char *p, *q; + int t; + + p = object_class_get_name(OBJECT_CLASS(CPU_GET_CLASS(env_cpu(cpu_env)))); + q = strchr(p, '-'); + t = q - p; + assert(t < sizeof(model)); + memcpy(model, p, t); + model[t] = 0; + + t = sched_getaffinity(getpid(), sizeof(cpu_mask), (cpu_set_t *)&cpu_mask); + if (t < 0) { + if (num_cpus >= sizeof(cpu_mask) * 8) { + cpu_mask = -1; + } else { + cpu_mask = (1UL << num_cpus) - 1; + } + } + + dprintf(fd, + "cpu\t\t\t: Alpha\n" + "cpu model\t\t: %s\n" + "cpu variation\t\t: 0\n" + "cpu revision\t\t: 0\n" + "cpu serial number\t: JA00000000\n" + "system type\t\t: QEMU\n" + "system variation\t: QEMU_v" QEMU_VERSION "\n" + "system revision\t\t: 0\n" + "system serial number\t: AY00000000\n" + "cycle frequency [Hz]\t: 250000000\n" + "timer frequency [Hz]\t: 250.00\n" + "page size [bytes]\t: %d\n" + "phys. address bits\t: %d\n" + "max. addr. space #\t: 255\n" + "BogoMIPS\t\t: 2500.00\n" + "kernel unaligned acc\t: 0 (pc=0,va=0)\n" + "user unaligned acc\t: 0 (pc=0,va=0)\n" + "platform string\t\t: AlphaServer QEMU user-mode VM\n" + "cpus detected\t\t: %d\n" + "cpus active\t\t: %d\n" + "cpu active mask\t\t: %016lx\n" + "L1 Icache\t\t: n/a\n" + "L1 Dcache\t\t: n/a\n" + "L2 cache\t\t: n/a\n" + "L3 cache\t\t: n/a\n", + model, TARGET_PAGE_SIZE, TARGET_PHYS_ADDR_SPACE_BITS, + max_cpus, num_cpus, cpu_mask); + + return 0; +} +#define HAVE_ARCH_PROC_CPUINFO + +#endif /* ALPHA_TARGET_PROC_H */ diff --git a/linux-user/arm/Makefile.vdso b/linux-user/arm/Makefile.vdso new file mode 100644 index 0000000000..2d098a5748 --- /dev/null +++ b/linux-user/arm/Makefile.vdso @@ -0,0 +1,17 @@ +include $(BUILD_DIR)/tests/tcg/arm-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/arm +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso-be.so $(SUBDIR)/vdso-le.so + +# Adding -use-blx disables unneeded interworking without actually using blx. +LDFLAGS = -nostdlib -shared -Wl,-use-blx \ + -Wl,-h,linux-vdso.so.1 -Wl,--build-id=sha1 \ + -Wl,--hash-style=both -Wl,-T,$(SUBDIR)/vdso.ld + +$(SUBDIR)/vdso-be.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS) -mbig-endian $< + +$(SUBDIR)/vdso-le.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS) -mlittle-endian $< diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index d950409d5b..db1a41e27f 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -117,8 +117,9 @@ static void arm_kernel_cmpxchg32_helper(CPUARMState *env) { uint32_t oldval, newval, val, addr, cpsr, *host_addr; - oldval = env->regs[0]; - newval = env->regs[1]; + /* Swap if host != guest endianness, for the host cmpxchg below */ + oldval = tswap32(env->regs[0]); + newval = tswap32(env->regs[1]); addr = env->regs[2]; mmap_lock(); @@ -174,6 +175,10 @@ static void arm_kernel_cmpxchg64_helper(CPUARMState *env) return; } + /* Swap if host != guest endianness, for the host cmpxchg below */ + oldval = tswap64(oldval); + newval = tswap64(newval); + #ifdef CONFIG_ATOMIC64 val = qatomic_cmpxchg__nocheck(host_addr, oldval, newval); cpsr = (val == oldval) * CPSR_C; @@ -258,7 +263,7 @@ static bool insn_is_linux_bkpt(uint32_t opcode, bool is_thumb) static bool emulate_arm_fpa11(CPUARMState *env, uint32_t opcode) { - TaskState *ts = env_cpu(env)->opaque; + TaskState *ts = get_task_state(env_cpu(env)); int rc = EmulateAll(opcode, &ts->fpa, env); int raise, enabled; @@ -356,7 +361,7 @@ void cpu_loop(CPUARMState *env) break; case EXCP_SWI: { - env->eabi = 1; + env->eabi = true; /* system call */ if (env->thumb) { /* Thumb is always EABI style with syscall number in r7 */ @@ -382,7 +387,7 @@ void cpu_loop(CPUARMState *env) * > 0xfffff and are handled below as out-of-range. */ n ^= ARM_SYSCALL_BASE; - env->eabi = 0; + env->eabi = false; } } @@ -449,7 +454,7 @@ void cpu_loop(CPUARMState *env) } break; case EXCP_SEMIHOST: - env->regs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->regs[15] += env->thumb ? 2 : 4; break; case EXCP_INTERRUPT: @@ -509,7 +514,7 @@ void cpu_loop(CPUARMState *env) void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); struct image_info *info = ts->info; int i; diff --git a/linux-user/arm/meson.build b/linux-user/arm/meson.build index 5a93c925cf..c4bb9af5b8 100644 --- a/linux-user/arm/meson.build +++ b/linux-user/arm/meson.build @@ -5,3 +5,15 @@ syscall_nr_generators += { arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], output: '@BASENAME@_nr.h') } + +# TARGET_BIG_ENDIAN is defined to 'n' for little-endian; which means it +# is always true as far as source_set.apply() is concerned. Always build +# both header files and include the right one via #if. + +vdso_be_inc = gen_vdso.process('vdso-be.so', + extra_args: ['-s', 'sigreturn_codes']) + +vdso_le_inc = gen_vdso.process('vdso-le.so', + extra_args: ['-s', 'sigreturn_codes']) + +linux_user_ss.add(when: 'TARGET_ARM', if_true: [vdso_be_inc, vdso_le_inc]) diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index cf99fd7b8a..8db1c4b233 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -21,6 +21,8 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "target/arm/cpu-features.h" +#include "vdso-asmoffset.h" struct target_sigcontext { abi_ulong trap_no; @@ -102,6 +104,11 @@ struct rt_sigframe struct sigframe sig; }; +QEMU_BUILD_BUG_ON(offsetof(struct sigframe, retcode[3]) + != SIGFRAME_RC3_OFFSET); +QEMU_BUILD_BUG_ON(offsetof(struct rt_sigframe, sig.retcode[3]) + != RT_SIGFRAME_RC3_OFFSET); + static abi_ptr sigreturn_fdpic_tramp; /* @@ -160,6 +167,9 @@ get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize) return (sp - framesize) & ~7; } +static void write_arm_sigreturn(uint32_t *rc, int syscall); +static void write_arm_fdpic_sigreturn(uint32_t *rc, int ofs); + static int setup_return(CPUARMState *env, struct target_sigaction *ka, int usig, struct sigframe *frame, abi_ulong sp_addr) @@ -167,9 +177,9 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, int usig, abi_ulong handler = 0; abi_ulong handler_fdpic_GOT = 0; abi_ulong retcode; - int thumb, retcode_idx; - int is_fdpic = info_is_fdpic(((TaskState *)thread_cpu->opaque)->info); - bool copy_retcode; + bool is_fdpic = info_is_fdpic(get_task_state(thread_cpu)->info); + bool is_rt = ka->sa_flags & TARGET_SA_SIGINFO; + bool thumb; if (is_fdpic) { /* In FDPIC mode, ka->_sa_handler points to a function @@ -184,9 +194,7 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, int usig, } else { handler = ka->_sa_handler; } - thumb = handler & 1; - retcode_idx = thumb + (ka->sa_flags & TARGET_SA_SIGINFO ? 2 : 0); uint32_t cpsr = cpsr_read(env); @@ -202,24 +210,32 @@ setup_return(CPUARMState *env, struct target_sigaction *ka, int usig, cpsr &= ~CPSR_E; } - if (ka->sa_flags & TARGET_SA_RESTORER) { - if (is_fdpic) { - __put_user((abi_ulong)ka->sa_restorer, &frame->retcode[3]); - retcode = (sigreturn_fdpic_tramp + - retcode_idx * RETCODE_BYTES + thumb); - copy_retcode = true; - } else { - retcode = ka->sa_restorer; - copy_retcode = false; - } + /* Our vdso default_sigreturn label is a table of entry points. */ + retcode = default_sigreturn + (is_fdpic * 2 + is_rt) * 8; + + /* + * Put the sigreturn code on the stack no matter which return + * mechanism we use in order to remain ABI compliant. + * Because this is about ABI, always use the A32 instructions, + * despite the fact that our actual vdso trampoline is T16. + */ + if (is_fdpic) { + write_arm_fdpic_sigreturn(frame->retcode, + is_rt ? RT_SIGFRAME_RC3_OFFSET + : SIGFRAME_RC3_OFFSET); } else { - retcode = default_sigreturn + retcode_idx * RETCODE_BYTES + thumb; - copy_retcode = true; + write_arm_sigreturn(frame->retcode, + is_rt ? TARGET_NR_rt_sigreturn + : TARGET_NR_sigreturn); } - /* Copy the code to the stack slot for ABI compatibility. */ - if (copy_retcode) { - memcpy(frame->retcode, g2h_untagged(retcode & ~1), RETCODE_BYTES); + if (ka->sa_flags & TARGET_SA_RESTORER) { + if (is_fdpic) { + /* Place the function descriptor in slot 3. */ + __put_user((abi_ulong)ka->sa_restorer, &frame->retcode[3]); + } else { + retcode = ka->sa_restorer; + } } env->regs[0] = usig; @@ -341,7 +357,7 @@ void setup_rt_frame(int usig, struct target_sigaction *ka, info_addr = frame_addr + offsetof(struct rt_sigframe, info); uc_addr = frame_addr + offsetof(struct rt_sigframe, sig.uc); - tswap_siginfo(&frame->info, info); + frame->info = *info; setup_sigframe(&frame->sig.uc, set, env); diff --git a/linux-user/arm/target_cpu.h b/linux-user/arm/target_cpu.h index 709d19bc9e..f6383a7cd1 100644 --- a/linux-user/arm/target_cpu.h +++ b/linux-user/arm/target_cpu.h @@ -30,13 +30,13 @@ static inline unsigned long arm_max_reserved_va(CPUState *cs) * the high addresses. Restrict linux-user to the * cached write-back RAM in the system map. */ - return 0x80000000ul; + return 0x7ffffffful; } else { /* * We need to be able to map the commpage. - * See validate_guest_space in linux-user/elfload.c. + * See init_guest_commpage in linux-user/elfload.c. */ - return 0xffff0000ul; + return 0xfffffffful; } } #define MAX_RESERVED_VA arm_max_reserved_va diff --git a/linux-user/arm/target_flat.h b/linux-user/arm/target_flat.h new file mode 100644 index 0000000000..bc83224cea --- /dev/null +++ b/linux-user/arm/target_flat.h @@ -0,0 +1 @@ +#include "../generic/target_flat.h" diff --git a/linux-user/arm/target_mman.h b/linux-user/arm/target_mman.h new file mode 100644 index 0000000000..51005da869 --- /dev/null +++ b/linux-user/arm/target_mman.h @@ -0,0 +1,12 @@ +/* + * arch/arm/include/asm/memory.h + * TASK_UNMAPPED_BASE ALIGN(TASK_SIZE / 3, SZ_16M) + * TASK_SIZE CONFIG_PAGE_OFFSET + * CONFIG_PAGE_OFFSET 0xC0000000 (default in Kconfig) + */ +#define TASK_UNMAPPED_BASE 0x40000000 + +/* arch/arm/include/asm/elf.h */ +#define ELF_ET_DYN_BASE 0x00400000 + +#include "../generic/target_mman.h" diff --git a/linux-user/arm/target_proc.h b/linux-user/arm/target_proc.h new file mode 100644 index 0000000000..ac75af9ca6 --- /dev/null +++ b/linux-user/arm/target_proc.h @@ -0,0 +1,101 @@ +/* + * Arm specific proc functions for linux-user + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef ARM_TARGET_PROC_H +#define ARM_TARGET_PROC_H + +static int open_cpuinfo(CPUArchState *cpu_env, int fd) +{ + ARMCPU *cpu = env_archcpu(cpu_env); + int arch, midr_rev, midr_part, midr_var, midr_impl; + target_ulong elf_hwcap = get_elf_hwcap(); + target_ulong elf_hwcap2 = get_elf_hwcap2(); + const char *elf_name; + int num_cpus, len_part, len_var; + +#if TARGET_BIG_ENDIAN +# define END_SUFFIX "b" +#else +# define END_SUFFIX "l" +#endif + + arch = 8; + elf_name = "v8" END_SUFFIX; + midr_rev = FIELD_EX32(cpu->midr, MIDR_EL1, REVISION); + midr_part = FIELD_EX32(cpu->midr, MIDR_EL1, PARTNUM); + midr_var = FIELD_EX32(cpu->midr, MIDR_EL1, VARIANT); + midr_impl = FIELD_EX32(cpu->midr, MIDR_EL1, IMPLEMENTER); + len_part = 3; + len_var = 1; + +#ifndef TARGET_AARCH64 + /* For simplicity, treat ARMv8 as an arm64 kernel with CONFIG_COMPAT. */ + if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) { + if (arm_feature(&cpu->env, ARM_FEATURE_V7)) { + arch = 7; + midr_var = (cpu->midr >> 16) & 0x7f; + len_var = 2; + if (arm_feature(&cpu->env, ARM_FEATURE_M)) { + elf_name = "armv7m" END_SUFFIX; + } else { + elf_name = "armv7" END_SUFFIX; + } + } else { + midr_part = cpu->midr >> 4; + len_part = 7; + if (arm_feature(&cpu->env, ARM_FEATURE_V6)) { + arch = 6; + elf_name = "armv6" END_SUFFIX; + } else if (arm_feature(&cpu->env, ARM_FEATURE_V5)) { + arch = 5; + elf_name = "armv5t" END_SUFFIX; + } else { + arch = 4; + elf_name = "armv4" END_SUFFIX; + } + } + } +#endif + +#undef END_SUFFIX + + num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + for (int i = 0; i < num_cpus; i++) { + dprintf(fd, + "processor\t: %d\n" + "model name\t: ARMv%d Processor rev %d (%s)\n" + "BogoMIPS\t: 100.00\n" + "Features\t:", + i, arch, midr_rev, elf_name); + + for (target_ulong j = elf_hwcap; j ; j &= j - 1) { + dprintf(fd, " %s", elf_hwcap_str(ctz64(j))); + } + for (target_ulong j = elf_hwcap2; j ; j &= j - 1) { + dprintf(fd, " %s", elf_hwcap2_str(ctz64(j))); + } + + dprintf(fd, "\n" + "CPU implementer\t: 0x%02x\n" + "CPU architecture: %d\n" + "CPU variant\t: 0x%0*x\n", + midr_impl, arch, len_var, midr_var); + if (arch >= 7) { + dprintf(fd, "CPU part\t: 0x%0*x\n", len_part, midr_part); + } + dprintf(fd, "CPU revision\t: %d\n\n", midr_rev); + } + + if (arch < 8) { + dprintf(fd, "Hardware\t: QEMU v%s %s\n", QEMU_VERSION, + cpu->dtb_compatible ? : ""); + dprintf(fd, "Revision\t: 0000\n"); + dprintf(fd, "Serial\t\t: 0000000000000000\n"); + } + return 0; +} +#define HAVE_ARCH_PROC_CPUINFO + +#endif /* ARM_TARGET_PROC_H */ diff --git a/linux-user/arm/vdso-asmoffset.h b/linux-user/arm/vdso-asmoffset.h new file mode 100644 index 0000000000..252a95c46e --- /dev/null +++ b/linux-user/arm/vdso-asmoffset.h @@ -0,0 +1,3 @@ +/* offsetof(struct sigframe, retcode[3]) */ +#define SIGFRAME_RC3_OFFSET 756 +#define RT_SIGFRAME_RC3_OFFSET 884 diff --git a/linux-user/arm/vdso-be.so b/linux-user/arm/vdso-be.so new file mode 100755 index 0000000000..69cafbb956 Binary files /dev/null and b/linux-user/arm/vdso-be.so differ diff --git a/linux-user/arm/vdso-le.so b/linux-user/arm/vdso-le.so new file mode 100755 index 0000000000..ad05a12518 Binary files /dev/null and b/linux-user/arm/vdso-le.so differ diff --git a/linux-user/arm/vdso.S b/linux-user/arm/vdso.S new file mode 100644 index 0000000000..b3bb6491dc --- /dev/null +++ b/linux-user/arm/vdso.S @@ -0,0 +1,174 @@ +/* + * arm linux replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "vdso-asmoffset.h" + +/* + * All supported cpus have T16 instructions: at least arm4t. + * + * We support user-user with m-profile cpus as an extension, because it + * is useful for testing gcc, which requires we avoid A32 instructions. + */ + .thumb + .arch armv4t + .eabi_attribute Tag_FP_arch, 0 + .eabi_attribute Tag_ARM_ISA_use, 0 + + .text + +.macro raw_syscall n + .ifne \n < 0x100 + mov r7, #\n + .elseif \n < 0x1ff + mov r7, #0xff + add r7, #(\n - 0xff) + .else + .err + .endif + swi #0 +.endm + +.macro fdpic_thunk ofs + ldr r3, [sp, #\ofs] + ldmia r2, {r2, r3} + mov r9, r3 + bx r2 +.endm + +.macro endf name + .globl \name + .type \name, %function + .size \name, . - \name +.endm + +/* + * We must save/restore r7 for the EABI syscall number. + * While we're doing that, we might as well save LR to get a free return, + * and a branch that is interworking back to ARMv5. + */ + +.macro SYSCALL name, nr +\name: + .cfi_startproc + push {r7, lr} + .cfi_adjust_cfa_offset 8 + .cfi_offset r7, -8 + .cfi_offset lr, -4 + raw_syscall \nr + pop {r7, pc} + .cfi_endproc +endf \name +.endm + +SYSCALL __vdso_clock_gettime, __NR_clock_gettime +SYSCALL __vdso_clock_gettime64, __NR_clock_gettime64 +SYSCALL __vdso_clock_getres, __NR_clock_getres +SYSCALL __vdso_gettimeofday, __NR_gettimeofday + + +/* + * We, like the real kernel, use a table of sigreturn trampolines. + * Unlike the real kernel, we do not attempt to pack this into as + * few bytes as possible -- simply use 8 bytes per slot. + * + * Within each slot, use the exact same code sequence as the kernel, + * lest we trip up someone doing code inspection. + */ + +.macro slot n + .balign 8 + .org sigreturn_codes + 8 * \n +.endm + +.macro cfi_fdpic_r9 ofs + /* + * fd = *(r13 + ofs) + * r9 = *(fd + 4) + * + * DW_CFA_expression r9, length (7), + * DW_OP_breg13, ofs, DW_OP_deref, + * DW_OP_plus_uconst, 4, DW_OP_deref + */ + .cfi_escape 0x10, 9, 7, 0x7d, (\ofs & 0x7f) + 0x80, (\ofs >> 7), 0x06, 0x23, 4, 0x06 +.endm + +.macro cfi_fdpic_pc ofs + /* + * fd = *(r13 + ofs) + * pc = *fd + * + * DW_CFA_expression lr (14), length (5), + * DW_OP_breg13, ofs, DW_OP_deref, DW_OP_deref + */ + .cfi_escape 0x10, 14, 5, 0x7d, (\ofs & 0x7f) + 0x80, (\ofs >> 7), 0x06, 0x06 +.endm + +/* + * Start the unwind info at least one instruction before the signal + * trampoline, because the unwinder will assume we are returning + * after a call site. + */ + .cfi_startproc simple + .cfi_signal_frame + .cfi_return_column 15 + + .cfi_def_cfa sp, 32 + 64 + .cfi_offset r0, -16 * 4 + .cfi_offset r1, -15 * 4 + .cfi_offset r2, -14 * 4 + .cfi_offset r3, -13 * 4 + .cfi_offset r4, -12 * 4 + .cfi_offset r5, -11 * 4 + .cfi_offset r6, -10 * 4 + .cfi_offset r7, -9 * 4 + .cfi_offset r8, -8 * 4 + .cfi_offset r9, -7 * 4 + .cfi_offset r10, -6 * 4 + .cfi_offset r11, -5 * 4 + .cfi_offset r12, -4 * 4 + .cfi_offset r13, -3 * 4 + .cfi_offset r14, -2 * 4 + .cfi_offset r15, -1 * 4 + + nop + + .balign 16 +sigreturn_codes: + /* [EO]ABI sigreturn */ + slot 0 + raw_syscall __NR_sigreturn + + .cfi_def_cfa_offset 160 + 64 + + /* [EO]ABI rt_sigreturn */ + slot 1 + raw_syscall __NR_rt_sigreturn + + .cfi_endproc + + /* FDPIC sigreturn */ + .cfi_startproc + cfi_fdpic_pc SIGFRAME_RC3_OFFSET + cfi_fdpic_r9 SIGFRAME_RC3_OFFSET + + slot 2 + fdpic_thunk SIGFRAME_RC3_OFFSET + .cfi_endproc + + /* FDPIC rt_sigreturn */ + .cfi_startproc + cfi_fdpic_pc RT_SIGFRAME_RC3_OFFSET + cfi_fdpic_r9 RT_SIGFRAME_RC3_OFFSET + + slot 3 + fdpic_thunk RT_SIGFRAME_RC3_OFFSET + .cfi_endproc + + .balign 16 +endf sigreturn_codes diff --git a/linux-user/arm/vdso.ld b/linux-user/arm/vdso.ld new file mode 100644 index 0000000000..3b00adf27a --- /dev/null +++ b/linux-user/arm/vdso.ld @@ -0,0 +1,67 @@ +/* + * Linker script for linux arm replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_2.6 { + global: + __vdso_clock_gettime; + __vdso_gettimeofday; + __vdso_clock_getres; + __vdso_clock_gettime64; + + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; /* FLAGS=RWX */ + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + .data : { + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load +} diff --git a/linux-user/cpu_loop-common.h b/linux-user/cpu_loop-common.h index dc0042e4de..e644d2ef90 100644 --- a/linux-user/cpu_loop-common.h +++ b/linux-user/cpu_loop-common.h @@ -23,16 +23,9 @@ #include "exec/log.h" #include "special-errno.h" -#define EXCP_DUMP(env, fmt, ...) \ -do { \ - CPUState *cs = env_cpu(env); \ - fprintf(stderr, fmt , ## __VA_ARGS__); \ - cpu_dump_state(cs, stderr, 0); \ - if (qemu_log_separate()) { \ - qemu_log(fmt, ## __VA_ARGS__); \ - log_cpu_state(cs, 0); \ - } \ -} while (0) +void target_exception_dump(CPUArchState *env, const char *fmt, int code); +#define EXCP_DUMP(env, fmt, code) \ + target_exception_dump(env, fmt, code) void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs); #endif diff --git a/linux-user/cris/cpu_loop.c b/linux-user/cris/cpu_loop.c index 01e6ff16fc..04c9086b6d 100644 --- a/linux-user/cris/cpu_loop.c +++ b/linux-user/cris/cpu_loop.c @@ -72,7 +72,7 @@ void cpu_loop(CPUCRISState *env) void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); struct image_info *info = ts->info; env->regs[0] = regs->r0; diff --git a/linux-user/cris/target_mman.h b/linux-user/cris/target_mman.h new file mode 100644 index 0000000000..9ace8ac292 --- /dev/null +++ b/linux-user/cris/target_mman.h @@ -0,0 +1,13 @@ +/* + * arch/cris/include/asm/processor.h: + * TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 3)) + * + * arch/cris/include/arch-v32/arch/processor.h + * TASK_SIZE 0xb0000000 + */ +#define TASK_UNMAPPED_BASE TARGET_PAGE_ALIGN(0xb0000000 / 3) + +/* arch/cris/include/uapi/asm/elf.h */ +#define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE * 2) + +#include "../generic/target_mman.h" diff --git a/linux-user/cris/target_proc.h b/linux-user/cris/target_proc.h new file mode 100644 index 0000000000..43fe29ca72 --- /dev/null +++ b/linux-user/cris/target_proc.h @@ -0,0 +1 @@ +/* No target-specific /proc support */ diff --git a/linux-user/elfload.c b/linux-user/elfload.c index d1f42252ff..25afece641 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2,6 +2,7 @@ #include "qemu/osdep.h" #include +#include #include #include @@ -17,8 +18,15 @@ #include "qemu/guest-random.h" #include "qemu/units.h" #include "qemu/selfmap.h" +#include "qemu/lockable.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "target_signal.h" +#include "tcg/debuginfo.h" + +#ifdef TARGET_ARM +#include "target/arm/cpu-features.h" +#endif #ifdef _ARCH_PPC64 #undef ARCH_DLINFO @@ -30,6 +38,19 @@ #undef ELF_ARCH #endif +#ifndef TARGET_ARCH_HAS_SIGTRAMP_PAGE +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 +#endif + +typedef struct { + const uint8_t *image; + const uint32_t *relocs; + unsigned image_size; + unsigned reloc_count; + unsigned sigreturn_ofs; + unsigned rt_sigreturn_ofs; +} VdsoImageInfo; + #define ELF_OSABI ELFOSABI_SYSV /* from personality.h */ @@ -130,19 +151,6 @@ typedef abi_int target_pid_t; #ifdef TARGET_I386 -#define ELF_PLATFORM get_elf_platform() - -static const char *get_elf_platform(void) -{ - static char elf_platform[] = "i386"; - int family = object_property_get_int(OBJECT(thread_cpu), "family", NULL); - if (family > 6) - family = 6; - if (family >= 3) - elf_platform[1] = '0' + family; - return elf_platform; -} - #define ELF_HWCAP get_elf_hwcap() static uint32_t get_elf_hwcap(void) @@ -153,11 +161,11 @@ static uint32_t get_elf_hwcap(void) } #ifdef TARGET_X86_64 -#define ELF_START_MMAP 0x2aaaaab000ULL - #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 +#define ELF_PLATFORM "x86_64" + static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { regs->rax = 0; @@ -206,10 +214,29 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en (*regs)[26] = tswapreg(env->segs[R_GS].selector & 0xffff); } +#if ULONG_MAX > UINT32_MAX +#define INIT_GUEST_COMMPAGE +static bool init_guest_commpage(void) +{ + /* + * The vsyscall page is at a high negative address aka kernel space, + * which means that we cannot actually allocate it with target_mmap. + * We still should be able to use page_set_flags, unless the user + * has specified -R reserved_va, which would trigger an assert(). + */ + if (reserved_va != 0 && + TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE - 1 > reserved_va) { + error_report("Cannot allocate vsyscall page"); + exit(EXIT_FAILURE); + } + page_set_flags(TARGET_VSYSCALL_PAGE, + TARGET_VSYSCALL_PAGE | ~TARGET_PAGE_MASK, + PAGE_EXEC | PAGE_VALID); + return true; +} +#endif #else -#define ELF_START_MMAP 0x80000000 - /* * This is used to ensure we don't load something for the wrong architecture. */ @@ -221,6 +248,22 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_386 +#define ELF_PLATFORM get_elf_platform() +#define EXSTACK_DEFAULT true + +static const char *get_elf_platform(void) +{ + static char elf_platform[] = "i386"; + int family = object_property_get_int(OBJECT(thread_cpu), "family", NULL); + if (family > 6) { + family = 6; + } + if (family >= 3) { + elf_platform[1] = '0' + family; + } + return elf_platform; +} + static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { @@ -267,22 +310,36 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en (*regs)[15] = tswapreg(env->regs[R_ESP]); (*regs)[16] = tswapreg(env->segs[R_SS].selector & 0xffff); } -#endif + +/* + * i386 is the only target which supplies AT_SYSINFO for the vdso. + * All others only supply AT_SYSINFO_EHDR. + */ +#define DLINFO_ARCH_ITEMS (vdso_info != NULL) +#define ARCH_DLINFO \ + do { \ + if (vdso_info) { \ + NEW_AUX_ENT(AT_SYSINFO, vdso_info->entry); \ + } \ + } while (0) + +#endif /* TARGET_X86_64 */ + +#define VDSO_HEADER "vdso.c.inc" #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#endif +#endif /* TARGET_I386 */ #ifdef TARGET_ARM #ifndef TARGET_AARCH64 /* 32 bit ARM definitions */ -#define ELF_START_MMAP 0x80000000 - #define ELF_ARCH EM_ARM #define ELF_CLASS ELFCLASS32 +#define EXSTACK_DEFAULT true static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) @@ -378,6 +435,12 @@ enum ARM_HWCAP_ARM_VFPD32 = 1 << 19, ARM_HWCAP_ARM_LPAE = 1 << 20, ARM_HWCAP_ARM_EVTSTRM = 1 << 21, + ARM_HWCAP_ARM_FPHP = 1 << 22, + ARM_HWCAP_ARM_ASIMDHP = 1 << 23, + ARM_HWCAP_ARM_ASIMDDP = 1 << 24, + ARM_HWCAP_ARM_ASIMDFHM = 1 << 25, + ARM_HWCAP_ARM_ASIMDBF16 = 1 << 26, + ARM_HWCAP_ARM_I8MM = 1 << 27, }; enum { @@ -386,6 +449,8 @@ enum { ARM_HWCAP2_ARM_SHA1 = 1 << 2, ARM_HWCAP2_ARM_SHA2 = 1 << 3, ARM_HWCAP2_ARM_CRC32 = 1 << 4, + ARM_HWCAP2_ARM_SB = 1 << 5, + ARM_HWCAP2_ARM_SSBS = 1 << 6, }; /* The commpage only exists for 32 bit kernels */ @@ -394,9 +459,26 @@ enum { static bool init_guest_commpage(void) { - void *want = g2h_untagged(HI_COMMPAGE & -qemu_host_page_size); - void *addr = mmap(want, qemu_host_page_size, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + ARMCPU *cpu = ARM_CPU(thread_cpu); + int host_page_size = qemu_real_host_page_size(); + abi_ptr commpage; + void *want; + void *addr; + + /* + * M-profile allocates maximum of 2GB address space, so can never + * allocate the commpage. Skip it. + */ + if (arm_feature(&cpu->env, ARM_FEATURE_M)) { + return true; + } + + commpage = HI_COMMPAGE & -host_page_size; + want = g2h_untagged(commpage); + addr = mmap(want, host_page_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | + (commpage < reserved_va ? MAP_FIXED : MAP_FIXED_NOREPLACE), + -1, 0); if (addr == MAP_FAILED) { perror("Allocating guest commpage"); @@ -409,17 +491,20 @@ static bool init_guest_commpage(void) /* Set kernel helper versions; rest of page is 0. */ __put_user(5, (uint32_t *)g2h_untagged(0xffff0ffcu)); - if (mprotect(addr, qemu_host_page_size, PROT_READ)) { + if (mprotect(addr, host_page_size, PROT_READ)) { perror("Protecting guest commpage"); exit(EXIT_FAILURE); } + + page_set_flags(commpage, commpage | (host_page_size - 1), + PAGE_READ | PAGE_EXEC | PAGE_VALID); return true; } #define ELF_HWCAP get_elf_hwcap() #define ELF_HWCAP2 get_elf_hwcap2() -static uint32_t get_elf_hwcap(void) +uint32_t get_elf_hwcap(void) { ARMCPU *cpu = ARM_CPU(thread_cpu); uint32_t hwcaps = 0; @@ -457,23 +542,86 @@ static uint32_t get_elf_hwcap(void) } } GET_FEATURE_ID(aa32_simdfmac, ARM_HWCAP_ARM_VFPv4); + /* + * MVFR1.FPHP and .SIMDHP must be in sync, and QEMU uses the same + * isar_feature function for both. The kernel reports them as two hwcaps. + */ + GET_FEATURE_ID(aa32_fp16_arith, ARM_HWCAP_ARM_FPHP); + GET_FEATURE_ID(aa32_fp16_arith, ARM_HWCAP_ARM_ASIMDHP); + GET_FEATURE_ID(aa32_dp, ARM_HWCAP_ARM_ASIMDDP); + GET_FEATURE_ID(aa32_fhm, ARM_HWCAP_ARM_ASIMDFHM); + GET_FEATURE_ID(aa32_bf16, ARM_HWCAP_ARM_ASIMDBF16); + GET_FEATURE_ID(aa32_i8mm, ARM_HWCAP_ARM_I8MM); return hwcaps; } -static uint32_t get_elf_hwcap2(void) +uint64_t get_elf_hwcap2(void) { ARMCPU *cpu = ARM_CPU(thread_cpu); - uint32_t hwcaps = 0; + uint64_t hwcaps = 0; GET_FEATURE_ID(aa32_aes, ARM_HWCAP2_ARM_AES); GET_FEATURE_ID(aa32_pmull, ARM_HWCAP2_ARM_PMULL); GET_FEATURE_ID(aa32_sha1, ARM_HWCAP2_ARM_SHA1); GET_FEATURE_ID(aa32_sha2, ARM_HWCAP2_ARM_SHA2); GET_FEATURE_ID(aa32_crc32, ARM_HWCAP2_ARM_CRC32); + GET_FEATURE_ID(aa32_sb, ARM_HWCAP2_ARM_SB); + GET_FEATURE_ID(aa32_ssbs, ARM_HWCAP2_ARM_SSBS); return hwcaps; } +const char *elf_hwcap_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP_ARM_SWP )] = "swp", + [__builtin_ctz(ARM_HWCAP_ARM_HALF )] = "half", + [__builtin_ctz(ARM_HWCAP_ARM_THUMB )] = "thumb", + [__builtin_ctz(ARM_HWCAP_ARM_26BIT )] = "26bit", + [__builtin_ctz(ARM_HWCAP_ARM_FAST_MULT)] = "fast_mult", + [__builtin_ctz(ARM_HWCAP_ARM_FPA )] = "fpa", + [__builtin_ctz(ARM_HWCAP_ARM_VFP )] = "vfp", + [__builtin_ctz(ARM_HWCAP_ARM_EDSP )] = "edsp", + [__builtin_ctz(ARM_HWCAP_ARM_JAVA )] = "java", + [__builtin_ctz(ARM_HWCAP_ARM_IWMMXT )] = "iwmmxt", + [__builtin_ctz(ARM_HWCAP_ARM_CRUNCH )] = "crunch", + [__builtin_ctz(ARM_HWCAP_ARM_THUMBEE )] = "thumbee", + [__builtin_ctz(ARM_HWCAP_ARM_NEON )] = "neon", + [__builtin_ctz(ARM_HWCAP_ARM_VFPv3 )] = "vfpv3", + [__builtin_ctz(ARM_HWCAP_ARM_VFPv3D16 )] = "vfpv3d16", + [__builtin_ctz(ARM_HWCAP_ARM_TLS )] = "tls", + [__builtin_ctz(ARM_HWCAP_ARM_VFPv4 )] = "vfpv4", + [__builtin_ctz(ARM_HWCAP_ARM_IDIVA )] = "idiva", + [__builtin_ctz(ARM_HWCAP_ARM_IDIVT )] = "idivt", + [__builtin_ctz(ARM_HWCAP_ARM_VFPD32 )] = "vfpd32", + [__builtin_ctz(ARM_HWCAP_ARM_LPAE )] = "lpae", + [__builtin_ctz(ARM_HWCAP_ARM_EVTSTRM )] = "evtstrm", + [__builtin_ctz(ARM_HWCAP_ARM_FPHP )] = "fphp", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDHP )] = "asimdhp", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDDP )] = "asimddp", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDFHM )] = "asimdfhm", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDBF16)] = "asimdbf16", + [__builtin_ctz(ARM_HWCAP_ARM_I8MM )] = "i8mm", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} + +const char *elf_hwcap2_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP2_ARM_AES )] = "aes", + [__builtin_ctz(ARM_HWCAP2_ARM_PMULL)] = "pmull", + [__builtin_ctz(ARM_HWCAP2_ARM_SHA1 )] = "sha1", + [__builtin_ctz(ARM_HWCAP2_ARM_SHA2 )] = "sha2", + [__builtin_ctz(ARM_HWCAP2_ARM_CRC32)] = "crc32", + [__builtin_ctz(ARM_HWCAP2_ARM_SB )] = "sb", + [__builtin_ctz(ARM_HWCAP2_ARM_SSBS )] = "ssbs", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} + #undef GET_FEATURE #undef GET_FEATURE_ID @@ -481,7 +629,7 @@ static uint32_t get_elf_hwcap2(void) static const char *get_elf_platform(void) { - CPUARMState *env = thread_cpu->env_ptr; + CPUARMState *env = cpu_env(thread_cpu); #if TARGET_BIG_ENDIAN # define END "b" @@ -510,7 +658,6 @@ static const char *get_elf_platform(void) #else /* 64 bit ARM definitions */ -#define ELF_START_MMAP 0x80000000 #define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 @@ -601,6 +748,32 @@ enum { ARM_HWCAP2_A64_RNG = 1 << 16, ARM_HWCAP2_A64_BTI = 1 << 17, ARM_HWCAP2_A64_MTE = 1 << 18, + ARM_HWCAP2_A64_ECV = 1 << 19, + ARM_HWCAP2_A64_AFP = 1 << 20, + ARM_HWCAP2_A64_RPRES = 1 << 21, + ARM_HWCAP2_A64_MTE3 = 1 << 22, + ARM_HWCAP2_A64_SME = 1 << 23, + ARM_HWCAP2_A64_SME_I16I64 = 1 << 24, + ARM_HWCAP2_A64_SME_F64F64 = 1 << 25, + ARM_HWCAP2_A64_SME_I8I32 = 1 << 26, + ARM_HWCAP2_A64_SME_F16F32 = 1 << 27, + ARM_HWCAP2_A64_SME_B16F32 = 1 << 28, + ARM_HWCAP2_A64_SME_F32F32 = 1 << 29, + ARM_HWCAP2_A64_SME_FA64 = 1 << 30, + ARM_HWCAP2_A64_WFXT = 1ULL << 31, + ARM_HWCAP2_A64_EBF16 = 1ULL << 32, + ARM_HWCAP2_A64_SVE_EBF16 = 1ULL << 33, + ARM_HWCAP2_A64_CSSC = 1ULL << 34, + ARM_HWCAP2_A64_RPRFM = 1ULL << 35, + ARM_HWCAP2_A64_SVE2P1 = 1ULL << 36, + ARM_HWCAP2_A64_SME2 = 1ULL << 37, + ARM_HWCAP2_A64_SME2P1 = 1ULL << 38, + ARM_HWCAP2_A64_SME_I16I32 = 1ULL << 39, + ARM_HWCAP2_A64_SME_BI32I32 = 1ULL << 40, + ARM_HWCAP2_A64_SME_B16B16 = 1ULL << 41, + ARM_HWCAP2_A64_SME_F16F16 = 1ULL << 42, + ARM_HWCAP2_A64_MOPS = 1ULL << 43, + ARM_HWCAP2_A64_HBC = 1ULL << 44, }; #define ELF_HWCAP get_elf_hwcap() @@ -609,7 +782,7 @@ enum { #define GET_FEATURE_ID(feat, hwcap) \ do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) -static uint32_t get_elf_hwcap(void) +uint32_t get_elf_hwcap(void) { ARMCPU *cpu = ARM_CPU(thread_cpu); uint32_t hwcaps = 0; @@ -631,12 +804,14 @@ static uint32_t get_elf_hwcap(void) GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); + GET_FEATURE_ID(aa64_lse2, ARM_HWCAP_A64_USCAT); GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA); GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE); GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG); GET_FEATURE_ID(aa64_fhm, ARM_HWCAP_A64_ASIMDFHM); + GET_FEATURE_ID(aa64_dit, ARM_HWCAP_A64_DIT); 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); @@ -647,10 +822,10 @@ static uint32_t get_elf_hwcap(void) return hwcaps; } -static uint32_t get_elf_hwcap2(void) +uint64_t get_elf_hwcap2(void) { ARMCPU *cpu = ARM_CPU(thread_cpu); - uint32_t hwcaps = 0; + uint64_t hwcaps = 0; GET_FEATURE_ID(aa64_dcpodp, ARM_HWCAP2_A64_DCPODP); GET_FEATURE_ID(aa64_sve2, ARM_HWCAP2_A64_SVE2); @@ -670,19 +845,129 @@ static uint32_t get_elf_hwcap2(void) GET_FEATURE_ID(aa64_rndr, ARM_HWCAP2_A64_RNG); GET_FEATURE_ID(aa64_bti, ARM_HWCAP2_A64_BTI); GET_FEATURE_ID(aa64_mte, ARM_HWCAP2_A64_MTE); + GET_FEATURE_ID(aa64_mte3, ARM_HWCAP2_A64_MTE3); + GET_FEATURE_ID(aa64_sme, (ARM_HWCAP2_A64_SME | + ARM_HWCAP2_A64_SME_F32F32 | + ARM_HWCAP2_A64_SME_B16F32 | + ARM_HWCAP2_A64_SME_F16F32 | + ARM_HWCAP2_A64_SME_I8I32)); + GET_FEATURE_ID(aa64_sme_f64f64, ARM_HWCAP2_A64_SME_F64F64); + GET_FEATURE_ID(aa64_sme_i16i64, ARM_HWCAP2_A64_SME_I16I64); + GET_FEATURE_ID(aa64_sme_fa64, ARM_HWCAP2_A64_SME_FA64); + GET_FEATURE_ID(aa64_hbc, ARM_HWCAP2_A64_HBC); + GET_FEATURE_ID(aa64_mops, ARM_HWCAP2_A64_MOPS); return hwcaps; } +const char *elf_hwcap_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP_A64_FP )] = "fp", + [__builtin_ctz(ARM_HWCAP_A64_ASIMD )] = "asimd", + [__builtin_ctz(ARM_HWCAP_A64_EVTSTRM )] = "evtstrm", + [__builtin_ctz(ARM_HWCAP_A64_AES )] = "aes", + [__builtin_ctz(ARM_HWCAP_A64_PMULL )] = "pmull", + [__builtin_ctz(ARM_HWCAP_A64_SHA1 )] = "sha1", + [__builtin_ctz(ARM_HWCAP_A64_SHA2 )] = "sha2", + [__builtin_ctz(ARM_HWCAP_A64_CRC32 )] = "crc32", + [__builtin_ctz(ARM_HWCAP_A64_ATOMICS )] = "atomics", + [__builtin_ctz(ARM_HWCAP_A64_FPHP )] = "fphp", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDHP )] = "asimdhp", + [__builtin_ctz(ARM_HWCAP_A64_CPUID )] = "cpuid", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDRDM)] = "asimdrdm", + [__builtin_ctz(ARM_HWCAP_A64_JSCVT )] = "jscvt", + [__builtin_ctz(ARM_HWCAP_A64_FCMA )] = "fcma", + [__builtin_ctz(ARM_HWCAP_A64_LRCPC )] = "lrcpc", + [__builtin_ctz(ARM_HWCAP_A64_DCPOP )] = "dcpop", + [__builtin_ctz(ARM_HWCAP_A64_SHA3 )] = "sha3", + [__builtin_ctz(ARM_HWCAP_A64_SM3 )] = "sm3", + [__builtin_ctz(ARM_HWCAP_A64_SM4 )] = "sm4", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDDP )] = "asimddp", + [__builtin_ctz(ARM_HWCAP_A64_SHA512 )] = "sha512", + [__builtin_ctz(ARM_HWCAP_A64_SVE )] = "sve", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDFHM)] = "asimdfhm", + [__builtin_ctz(ARM_HWCAP_A64_DIT )] = "dit", + [__builtin_ctz(ARM_HWCAP_A64_USCAT )] = "uscat", + [__builtin_ctz(ARM_HWCAP_A64_ILRCPC )] = "ilrcpc", + [__builtin_ctz(ARM_HWCAP_A64_FLAGM )] = "flagm", + [__builtin_ctz(ARM_HWCAP_A64_SSBS )] = "ssbs", + [__builtin_ctz(ARM_HWCAP_A64_SB )] = "sb", + [__builtin_ctz(ARM_HWCAP_A64_PACA )] = "paca", + [__builtin_ctz(ARM_HWCAP_A64_PACG )] = "pacg", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} + +const char *elf_hwcap2_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP2_A64_DCPODP )] = "dcpodp", + [__builtin_ctz(ARM_HWCAP2_A64_SVE2 )] = "sve2", + [__builtin_ctz(ARM_HWCAP2_A64_SVEAES )] = "sveaes", + [__builtin_ctz(ARM_HWCAP2_A64_SVEPMULL )] = "svepmull", + [__builtin_ctz(ARM_HWCAP2_A64_SVEBITPERM )] = "svebitperm", + [__builtin_ctz(ARM_HWCAP2_A64_SVESHA3 )] = "svesha3", + [__builtin_ctz(ARM_HWCAP2_A64_SVESM4 )] = "svesm4", + [__builtin_ctz(ARM_HWCAP2_A64_FLAGM2 )] = "flagm2", + [__builtin_ctz(ARM_HWCAP2_A64_FRINT )] = "frint", + [__builtin_ctz(ARM_HWCAP2_A64_SVEI8MM )] = "svei8mm", + [__builtin_ctz(ARM_HWCAP2_A64_SVEF32MM )] = "svef32mm", + [__builtin_ctz(ARM_HWCAP2_A64_SVEF64MM )] = "svef64mm", + [__builtin_ctz(ARM_HWCAP2_A64_SVEBF16 )] = "svebf16", + [__builtin_ctz(ARM_HWCAP2_A64_I8MM )] = "i8mm", + [__builtin_ctz(ARM_HWCAP2_A64_BF16 )] = "bf16", + [__builtin_ctz(ARM_HWCAP2_A64_DGH )] = "dgh", + [__builtin_ctz(ARM_HWCAP2_A64_RNG )] = "rng", + [__builtin_ctz(ARM_HWCAP2_A64_BTI )] = "bti", + [__builtin_ctz(ARM_HWCAP2_A64_MTE )] = "mte", + [__builtin_ctz(ARM_HWCAP2_A64_ECV )] = "ecv", + [__builtin_ctz(ARM_HWCAP2_A64_AFP )] = "afp", + [__builtin_ctz(ARM_HWCAP2_A64_RPRES )] = "rpres", + [__builtin_ctz(ARM_HWCAP2_A64_MTE3 )] = "mte3", + [__builtin_ctz(ARM_HWCAP2_A64_SME )] = "sme", + [__builtin_ctz(ARM_HWCAP2_A64_SME_I16I64 )] = "smei16i64", + [__builtin_ctz(ARM_HWCAP2_A64_SME_F64F64 )] = "smef64f64", + [__builtin_ctz(ARM_HWCAP2_A64_SME_I8I32 )] = "smei8i32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_F16F32 )] = "smef16f32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_B16F32 )] = "smeb16f32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_F32F32 )] = "smef32f32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_FA64 )] = "smefa64", + [__builtin_ctz(ARM_HWCAP2_A64_WFXT )] = "wfxt", + [__builtin_ctzll(ARM_HWCAP2_A64_EBF16 )] = "ebf16", + [__builtin_ctzll(ARM_HWCAP2_A64_SVE_EBF16 )] = "sveebf16", + [__builtin_ctzll(ARM_HWCAP2_A64_CSSC )] = "cssc", + [__builtin_ctzll(ARM_HWCAP2_A64_RPRFM )] = "rprfm", + [__builtin_ctzll(ARM_HWCAP2_A64_SVE2P1 )] = "sve2p1", + [__builtin_ctzll(ARM_HWCAP2_A64_SME2 )] = "sme2", + [__builtin_ctzll(ARM_HWCAP2_A64_SME2P1 )] = "sme2p1", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_I16I32 )] = "smei16i32", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_BI32I32)] = "smebi32i32", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_B16B16 )] = "smeb16b16", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_F16F16 )] = "smef16f16", + [__builtin_ctzll(ARM_HWCAP2_A64_MOPS )] = "mops", + [__builtin_ctzll(ARM_HWCAP2_A64_HBC )] = "hbc", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} + #undef GET_FEATURE_ID #endif /* not TARGET_AARCH64 */ + +#if TARGET_BIG_ENDIAN +# define VDSO_HEADER "vdso-be.c.inc" +#else +# define VDSO_HEADER "vdso-le.c.inc" +#endif + #endif /* TARGET_ARM */ #ifdef TARGET_SPARC #ifdef TARGET_SPARC64 -#define ELF_START_MMAP 0x80000000 #define ELF_HWCAP (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | HWCAP_SPARC_SWAP \ | HWCAP_SPARC_MULDIV | HWCAP_SPARC_V9) #ifndef TARGET_ABI32 @@ -694,7 +979,6 @@ static uint32_t get_elf_hwcap2(void) #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_SPARCV9 #else -#define ELF_START_MMAP 0x80000000 #define ELF_HWCAP (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | HWCAP_SPARC_SWAP \ | HWCAP_SPARC_MULDIV) #define ELF_CLASS ELFCLASS32 @@ -716,7 +1000,6 @@ static inline void init_thread(struct target_pt_regs *regs, #ifdef TARGET_PPC #define ELF_MACHINE PPC_ELF_MACHINE -#define ELF_START_MMAP 0x80000000 #if defined(TARGET_PPC64) @@ -727,6 +1010,7 @@ static inline void init_thread(struct target_pt_regs *regs, #else #define ELF_CLASS ELFCLASS32 +#define EXSTACK_DEFAULT true #endif @@ -907,20 +1191,124 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en (*regs)[36] = tswapreg(env->lr); (*regs)[37] = tswapreg(cpu_read_xer(env)); - for (i = 0; i < ARRAY_SIZE(env->crf); i++) { - ccr |= env->crf[i] << (32 - ((i + 1) * 4)); - } + ccr = ppc_get_cr(env); (*regs)[38] = tswapreg(ccr); } #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 +#ifndef TARGET_PPC64 +# define VDSO_HEADER "vdso-32.c.inc" +#elif TARGET_BIG_ENDIAN +# define VDSO_HEADER "vdso-64.c.inc" +#else +# define VDSO_HEADER "vdso-64le.c.inc" #endif -#ifdef TARGET_MIPS +#endif -#define ELF_START_MMAP 0x80000000 +#ifdef TARGET_LOONGARCH64 + +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_LOONGARCH +#define EXSTACK_DEFAULT true + +#define elf_check_arch(x) ((x) == EM_LOONGARCH) + +#define VDSO_HEADER "vdso.c.inc" + +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) +{ + /*Set crmd PG,DA = 1,0 */ + regs->csr.crmd = 2 << 3; + regs->csr.era = infop->entry; + regs->regs[3] = infop->start_stack; +} + +/* See linux kernel: arch/loongarch/include/asm/elf.h */ +#define ELF_NREG 45 +typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; + +enum { + TARGET_EF_R0 = 0, + TARGET_EF_CSR_ERA = TARGET_EF_R0 + 33, + TARGET_EF_CSR_BADV = TARGET_EF_R0 + 34, +}; + +static void elf_core_copy_regs(target_elf_gregset_t *regs, + const CPULoongArchState *env) +{ + int i; + + (*regs)[TARGET_EF_R0] = 0; + + for (i = 1; i < ARRAY_SIZE(env->gpr); i++) { + (*regs)[TARGET_EF_R0 + i] = tswapreg(env->gpr[i]); + } + + (*regs)[TARGET_EF_CSR_ERA] = tswapreg(env->pc); + (*regs)[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +#define ELF_HWCAP get_elf_hwcap() + +/* See arch/loongarch/include/uapi/asm/hwcap.h */ +enum { + HWCAP_LOONGARCH_CPUCFG = (1 << 0), + HWCAP_LOONGARCH_LAM = (1 << 1), + HWCAP_LOONGARCH_UAL = (1 << 2), + HWCAP_LOONGARCH_FPU = (1 << 3), + HWCAP_LOONGARCH_LSX = (1 << 4), + HWCAP_LOONGARCH_LASX = (1 << 5), + HWCAP_LOONGARCH_CRC32 = (1 << 6), + HWCAP_LOONGARCH_COMPLEX = (1 << 7), + HWCAP_LOONGARCH_CRYPTO = (1 << 8), + HWCAP_LOONGARCH_LVZ = (1 << 9), + HWCAP_LOONGARCH_LBT_X86 = (1 << 10), + HWCAP_LOONGARCH_LBT_ARM = (1 << 11), + HWCAP_LOONGARCH_LBT_MIPS = (1 << 12), +}; + +static uint32_t get_elf_hwcap(void) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(thread_cpu); + uint32_t hwcaps = 0; + + hwcaps |= HWCAP_LOONGARCH_CRC32; + + if (FIELD_EX32(cpu->env.cpucfg[1], CPUCFG1, UAL)) { + hwcaps |= HWCAP_LOONGARCH_UAL; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, FP)) { + hwcaps |= HWCAP_LOONGARCH_FPU; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LAM)) { + hwcaps |= HWCAP_LOONGARCH_LAM; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { + hwcaps |= HWCAP_LOONGARCH_LSX; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { + hwcaps |= HWCAP_LOONGARCH_LASX; + } + + return hwcaps; +} + +#define ELF_PLATFORM "loongarch" + +#endif /* TARGET_LOONGARCH64 */ + +#ifdef TARGET_MIPS #ifdef TARGET_MIPS64 #define ELF_CLASS ELFCLASS64 @@ -928,6 +1316,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #define ELF_CLASS ELFCLASS32 #endif #define ELF_ARCH EM_MIPS +#define EXSTACK_DEFAULT true #ifdef TARGET_ABI_MIPSN32 #define elf_check_abi(x) ((x) & EF_MIPS_ABI2) @@ -935,6 +1324,37 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) #endif +#define ELF_BASE_PLATFORM get_elf_base_platform() + +#define MATCH_PLATFORM_INSN(_flags, _base_platform) \ + do { if ((cpu->env.insn_flags & (_flags)) == _flags) \ + { return _base_platform; } } while (0) + +static const char *get_elf_base_platform(void) +{ + MIPSCPU *cpu = MIPS_CPU(thread_cpu); + + /* 64 bit ISAs goes first */ + MATCH_PLATFORM_INSN(CPU_MIPS64R6, "mips64r6"); + MATCH_PLATFORM_INSN(CPU_MIPS64R5, "mips64r5"); + MATCH_PLATFORM_INSN(CPU_MIPS64R2, "mips64r2"); + MATCH_PLATFORM_INSN(CPU_MIPS64R1, "mips64"); + MATCH_PLATFORM_INSN(CPU_MIPS5, "mips5"); + MATCH_PLATFORM_INSN(CPU_MIPS4, "mips4"); + MATCH_PLATFORM_INSN(CPU_MIPS3, "mips3"); + + /* 32 bit ISAs */ + MATCH_PLATFORM_INSN(CPU_MIPS32R6, "mips32r6"); + MATCH_PLATFORM_INSN(CPU_MIPS32R5, "mips32r5"); + MATCH_PLATFORM_INSN(CPU_MIPS32R2, "mips32r2"); + MATCH_PLATFORM_INSN(CPU_MIPS32R1, "mips32"); + MATCH_PLATFORM_INSN(CPU_MIPS2, "mips2"); + + /* Fallback */ + return "mips"; +} +#undef MATCH_PLATFORM_INSN + static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { @@ -1047,8 +1467,6 @@ static uint32_t get_elf_hwcap(void) #ifdef TARGET_MICROBLAZE -#define ELF_START_MMAP 0x80000000 - #define elf_check_arch(x) ( (x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) #define ELF_CLASS ELFCLASS32 @@ -1089,8 +1507,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMBState *env #ifdef TARGET_NIOS2 -#define ELF_START_MMAP 0x80000000 - #define elf_check_arch(x) ((x) == EM_ALTERA_NIOS2) #define ELF_CLASS ELFCLASS32 @@ -1119,10 +1535,14 @@ static bool init_guest_commpage(void) 0x3a, 0x68, 0x3b, 0x00, /* trap 0 */ }; - void *want = g2h_untagged(LO_COMMPAGE & -qemu_host_page_size); - void *addr = mmap(want, qemu_host_page_size, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + int host_page_size = qemu_real_host_page_size(); + void *want, *addr; + want = g2h_untagged(LO_COMMPAGE & -host_page_size); + addr = mmap(want, host_page_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | + (reserved_va ? MAP_FIXED : MAP_FIXED_NOREPLACE), + -1, 0); if (addr == MAP_FAILED) { perror("Allocating guest commpage"); exit(EXIT_FAILURE); @@ -1131,14 +1551,14 @@ static bool init_guest_commpage(void) return false; } - memcpy(addr, kuser_page, sizeof(kuser_page)); + memcpy(g2h_untagged(LO_COMMPAGE), kuser_page, sizeof(kuser_page)); - if (mprotect(addr, qemu_host_page_size, PROT_READ)) { + if (mprotect(addr, host_page_size, PROT_READ)) { perror("Protecting guest commpage"); exit(EXIT_FAILURE); } - page_set_flags(LO_COMMPAGE, LO_COMMPAGE + TARGET_PAGE_SIZE, + page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK, PAGE_READ | PAGE_EXEC | PAGE_VALID); return true; } @@ -1186,8 +1606,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #ifdef TARGET_OPENRISC -#define ELF_START_MMAP 0x08000000 - #define ELF_ARCH EM_OPENRISC #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2MSB @@ -1224,8 +1642,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #ifdef TARGET_SH4 -#define ELF_START_MMAP 0x80000000 - #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_SH @@ -1306,8 +1722,6 @@ static uint32_t get_elf_hwcap(void) #ifdef TARGET_CRIS -#define ELF_START_MMAP 0x80000000 - #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_CRIS @@ -1323,8 +1737,6 @@ static inline void init_thread(struct target_pt_regs *regs, #ifdef TARGET_M68K -#define ELF_START_MMAP 0x80000000 - #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_68K @@ -1374,8 +1786,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUM68KState *e #ifdef TARGET_ALPHA -#define ELF_START_MMAP (0x30000000000ULL) - #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_ALPHA @@ -1393,8 +1803,6 @@ static inline void init_thread(struct target_pt_regs *regs, #ifdef TARGET_S390X -#define ELF_START_MMAP (0x20000000000ULL) - #define ELF_CLASS ELFCLASS64 #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 @@ -1406,7 +1814,7 @@ static inline void init_thread(struct target_pt_regs *regs, #define GET_FEATURE(_feat, _hwcap) \ do { if (s390_has_feat(_feat)) { hwcap |= _hwcap; } } while (0) -static uint32_t get_elf_hwcap(void) +uint32_t get_elf_hwcap(void) { /* * Let's assume we always have esan3 and zarch. @@ -1424,14 +1832,47 @@ static uint32_t get_elf_hwcap(void) } GET_FEATURE(S390_FEAT_VECTOR, HWCAP_S390_VXRS); GET_FEATURE(S390_FEAT_VECTOR_ENH, HWCAP_S390_VXRS_EXT); + GET_FEATURE(S390_FEAT_VECTOR_ENH2, HWCAP_S390_VXRS_EXT2); return hwcap; } +const char *elf_hwcap_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [HWCAP_S390_NR_ESAN3] = "esan3", + [HWCAP_S390_NR_ZARCH] = "zarch", + [HWCAP_S390_NR_STFLE] = "stfle", + [HWCAP_S390_NR_MSA] = "msa", + [HWCAP_S390_NR_LDISP] = "ldisp", + [HWCAP_S390_NR_EIMM] = "eimm", + [HWCAP_S390_NR_DFP] = "dfp", + [HWCAP_S390_NR_HPAGE] = "edat", + [HWCAP_S390_NR_ETF3EH] = "etf3eh", + [HWCAP_S390_NR_HIGH_GPRS] = "highgprs", + [HWCAP_S390_NR_TE] = "te", + [HWCAP_S390_NR_VXRS] = "vx", + [HWCAP_S390_NR_VXRS_BCD] = "vxd", + [HWCAP_S390_NR_VXRS_EXT] = "vxe", + [HWCAP_S390_NR_GS] = "gs", + [HWCAP_S390_NR_VXRS_EXT2] = "vxe2", + [HWCAP_S390_NR_VXRS_PDE] = "vxp", + [HWCAP_S390_NR_SORT] = "sort", + [HWCAP_S390_NR_DFLT] = "dflt", + [HWCAP_S390_NR_NNPA] = "nnpa", + [HWCAP_S390_NR_PCI_MIO] = "pcimio", + [HWCAP_S390_NR_SIE] = "sie", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} + static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { regs->psw.addr = infop->entry; - regs->psw.mask = PSW_MASK_64 | PSW_MASK_32; + regs->psw.mask = PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT | \ + PSW_MASK_MCHECK | PSW_MASK_PSTATE | PSW_MASK_64 | \ + PSW_MASK_32; regs->gprs[15] = infop->start_stack; } @@ -1468,17 +1909,20 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 +#define VDSO_HEADER "vdso.c.inc" + #endif /* TARGET_S390X */ #ifdef TARGET_RISCV -#define ELF_START_MMAP 0x80000000 #define ELF_ARCH EM_RISCV #ifdef TARGET_RISCV32 #define ELF_CLASS ELFCLASS32 +#define VDSO_HEADER "vdso-32.c.inc" #else #define ELF_CLASS ELFCLASS64 +#define VDSO_HEADER "vdso-64.c.inc" #endif #define ELF_HWCAP get_elf_hwcap() @@ -1488,7 +1932,8 @@ static uint32_t get_elf_hwcap(void) #define MISA_BIT(EXT) (1 << (EXT - 'A')) RISCVCPU *cpu = RISCV_CPU(thread_cpu); uint32_t mask = MISA_BIT('I') | MISA_BIT('M') | MISA_BIT('A') - | MISA_BIT('F') | MISA_BIT('D') | MISA_BIT('C'); + | MISA_BIT('F') | MISA_BIT('D') | MISA_BIT('C') + | MISA_BIT('V'); return cpu->env.misa_ext & mask; #undef MISA_BIT @@ -1507,13 +1952,14 @@ static inline void init_thread(struct target_pt_regs *regs, #ifdef TARGET_HPPA -#define ELF_START_MMAP 0x80000000 #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_PARISC #define ELF_PLATFORM "PARISC" #define STACK_GROWS_DOWN 0 #define STACK_ALIGNMENT 64 +#define VDSO_HEADER "vdso.c.inc" + static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { @@ -1527,12 +1973,42 @@ static inline void init_thread(struct target_pt_regs *regs, regs->gr[31] = infop->entry; } +#define LO_COMMPAGE 0 + +static bool init_guest_commpage(void) +{ + /* If reserved_va, then we have already mapped 0 page on the host. */ + if (!reserved_va) { + void *want, *addr; + + want = g2h_untagged(LO_COMMPAGE); + addr = mmap(want, TARGET_PAGE_SIZE, PROT_NONE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0); + if (addr == MAP_FAILED) { + perror("Allocating guest commpage"); + exit(EXIT_FAILURE); + } + if (addr != want) { + return false; + } + } + + /* + * On Linux, page zero is normally marked execute only + gateway. + * Normal read or write is supposed to fail (thus PROT_NONE above), + * but specific offsets have kernel code mapped to raise permissions + * and implement syscalls. Here, simply mark the page executable. + * Special case the entry points during translation (see do_page_zero). + */ + page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK, + PAGE_EXEC | PAGE_VALID); + return true; +} + #endif /* TARGET_HPPA */ #ifdef TARGET_XTENSA -#define ELF_START_MMAP 0x20000000 - #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_XTENSA @@ -1543,6 +2019,15 @@ static inline void init_thread(struct target_pt_regs *regs, regs->windowstart = 1; regs->areg[1] = infop->start_stack; regs->pc = infop->entry; + if (info_is_fdpic(infop)) { + regs->areg[4] = infop->loadmap_addr; + regs->areg[5] = infop->interpreter_loadmap_addr; + if (infop->interpreter_loadmap_addr) { + regs->areg[6] = infop->interpreter_pt_dynamic_addr; + } else { + regs->areg[6] = infop->pt_dynamic_addr; + } + } } /* See linux kernel: arch/xtensa/include/asm/elf.h. */ @@ -1589,8 +2074,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #ifdef TARGET_HEXAGON -#define ELF_START_MMAP 0x20000000 - #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_HEXAGON @@ -1638,6 +2121,10 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUE2KState *en #endif /* TARGET_E2K */ +#ifndef ELF_BASE_PLATFORM +#define ELF_BASE_PLATFORM (NULL) +#endif + #ifndef ELF_PLATFORM #define ELF_PLATFORM (NULL) #endif @@ -1673,6 +2160,10 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUE2KState *en #define bswaptls(ptr) bswap32s(ptr) #endif +#ifndef EXSTACK_DEFAULT +#define EXSTACK_DEFAULT false +#endif + #include "elf.h" /* We must delay the following stanzas until after "elf.h". */ @@ -1727,15 +2218,6 @@ struct exec #define ZMAGIC 0413 #define QMAGIC 0314 -/* Necessary parameters */ -#define TARGET_ELF_EXEC_PAGESIZE \ - (((eppnt->p_align & ~qemu_host_page_mask) != 0) ? \ - TARGET_PAGE_SIZE : MAX(qemu_host_page_size, TARGET_PAGE_SIZE)) -#define TARGET_ELF_PAGELENGTH(_v) ROUND_UP((_v), TARGET_ELF_EXEC_PAGESIZE) -#define TARGET_ELF_PAGESTART(_v) ((_v) & \ - ~(abi_ulong)(TARGET_ELF_EXEC_PAGESIZE-1)) -#define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1)) - #define DLINFO_ITEMS 16 static inline void memcpy_fromfs(void * to, const void * from, unsigned long n) @@ -1824,7 +2306,8 @@ static inline void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) { } #ifdef USE_ELF_CORE_DUMP static int elf_core_dump(int, const CPUArchState *); #endif /* USE_ELF_CORE_DUMP */ -static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias); +static void load_symbols(struct elfhdr *hdr, const ImageSource *src, + abi_ulong load_bias); /* Verify the portions of EHDR within E_IDENT for the target. This can be performed before bswapping the entire header. */ @@ -1948,17 +2431,28 @@ static abi_ulong setup_arg_pages(struct linux_binprm *bprm, struct image_info *info) { abi_ulong size, error, guard; + int prot; size = guest_stack_size; if (size < STACK_LOWER_LIMIT) { size = STACK_LOWER_LIMIT; } - guard = TARGET_PAGE_SIZE; - if (guard < qemu_real_host_page_size()) { - guard = qemu_real_host_page_size(); + + if (STACK_GROWS_DOWN) { + guard = TARGET_PAGE_SIZE; + if (guard < qemu_real_host_page_size()) { + guard = qemu_real_host_page_size(); + } + } else { + /* no guard page for hppa target where stack grows upwards. */ + guard = 0; } - error = target_mmap(0, size + guard, PROT_READ | PROT_WRITE, + prot = PROT_READ | PROT_WRITE; + if (info->exec_stack) { + prot |= PROT_EXEC; + } + error = target_mmap(0, size + guard, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (error == -1) { perror("mmap stack"); @@ -1971,59 +2465,81 @@ static abi_ulong setup_arg_pages(struct linux_binprm *bprm, info->stack_limit = error + guard; return info->stack_limit + size - sizeof(void *); } else { - target_mprotect(error + size, guard, PROT_NONE); info->stack_limit = error + size; return error; } } -/* Map and zero the bss. We need to explicitly zero any fractional pages - after the data section (i.e. bss). */ -static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot) +/** + * zero_bss: + * + * Map and zero the bss. We need to explicitly zero any fractional pages + * after the data section (i.e. bss). Return false on mapping failure. + */ +static bool zero_bss(abi_ulong start_bss, abi_ulong end_bss, + int prot, Error **errp) { - uintptr_t host_start, host_map_start, host_end; + abi_ulong align_bss; - last_bss = TARGET_PAGE_ALIGN(last_bss); + /* We only expect writable bss; the code segment shouldn't need this. */ + if (!(prot & PROT_WRITE)) { + error_setg(errp, "PT_LOAD with non-writable bss"); + return false; + } - /* ??? There is confusion between qemu_real_host_page_size and - qemu_host_page_size here and elsewhere in target_mmap, which - may lead to the end of the data section mapping from the file - not being mapped. At least there was an explicit test and - comment for that here, suggesting that "the file size must - be known". The comment probably pre-dates the introduction - of the fstat system call in target_mmap which does in fact - find out the size. What isn't clear is if the workaround - here is still actually needed. For now, continue with it, - but merge it with the "normal" mmap that would allocate the bss. */ + align_bss = TARGET_PAGE_ALIGN(start_bss); + end_bss = TARGET_PAGE_ALIGN(end_bss); - host_start = (uintptr_t) g2h_untagged(elf_bss); - host_end = (uintptr_t) g2h_untagged(last_bss); - host_map_start = REAL_HOST_PAGE_ALIGN(host_start); + if (start_bss < align_bss) { + int flags = page_get_flags(start_bss); - if (host_map_start < host_end) { - void *p = mmap((void *)host_map_start, host_end - host_map_start, - prot, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (p == MAP_FAILED) { - perror("cannot mmap brk"); - exit(-1); + if (!(flags & PAGE_BITS)) { + /* + * The whole address space of the executable was reserved + * at the start, therefore all pages will be VALID. + * But assuming there are no PROT_NONE PT_LOAD segments, + * a PROT_NONE page means no data all bss, and we can + * simply extend the new anon mapping back to the start + * of the page of bss. + */ + align_bss -= TARGET_PAGE_SIZE; + } else { + /* + * The start of the bss shares a page with something. + * The only thing that we expect is the data section, + * which would already be marked writable. + * Overlapping the RX code segment seems malformed. + */ + if (!(flags & PAGE_WRITE)) { + error_setg(errp, "PT_LOAD with bss overlapping " + "non-writable page"); + return false; + } + + /* The page is already mapped and writable. */ + memset(g2h_untagged(start_bss), 0, align_bss - start_bss); } } - /* Ensure that the bss page(s) are valid */ - if ((page_get_flags(last_bss-1) & prot) != prot) { - page_set_flags(elf_bss & TARGET_PAGE_MASK, last_bss, prot | PAGE_VALID); - } - - if (host_start < host_map_start) { - memset((void *)host_start, 0, host_map_start - host_start); + if (align_bss < end_bss && + target_mmap(align_bss, end_bss - align_bss, prot, + MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0) == -1) { + error_setg_errno(errp, errno, "Error mapping bss"); + return false; } + return true; } -#ifdef TARGET_ARM +#if defined(TARGET_ARM) static int elf_is_fdpic(struct elfhdr *exec) { return exec->e_ident[EI_OSABI] == ELFOSABI_ARM_FDPIC; } +#elif defined(TARGET_XTENSA) +static int elf_is_fdpic(struct elfhdr *exec) +{ + return exec->e_ident[EI_OSABI] == ELFOSABI_XTENSA_FDPIC; +} #else /* Default implementation, always false. */ static int elf_is_fdpic(struct elfhdr *exec) @@ -2060,7 +2576,8 @@ static abi_ulong loader_build_fdpic_loadmap(struct image_info *info, abi_ulong s static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, struct elfhdr *exec, struct image_info *info, - struct image_info *interp_info) + struct image_info *interp_info, + struct image_info *vdso_info) { abi_ulong sp; abi_ulong u_argc, u_argv, u_envp, u_auxv; @@ -2068,8 +2585,8 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, int i; abi_ulong u_rand_bytes; uint8_t k_rand_bytes[16]; - abi_ulong u_platform; - const char *k_platform; + abi_ulong u_platform, u_base_platform; + const char *k_platform, *k_base_platform; const int n = sizeof(elf_addr_t); sp = p; @@ -2091,6 +2608,22 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, } } + u_base_platform = 0; + k_base_platform = ELF_BASE_PLATFORM; + if (k_base_platform) { + size_t len = strlen(k_base_platform) + 1; + if (STACK_GROWS_DOWN) { + sp -= (len + n - 1) & ~(n - 1); + u_base_platform = sp; + /* FIXME - check return value of memcpy_to_target() for failure */ + memcpy_to_target(sp, k_base_platform, len); + } else { + memcpy_to_target(sp, k_base_platform, len); + u_base_platform = sp; + sp += len + 1; + } + } + u_platform = 0; k_platform = ELF_PLATFORM; if (k_platform) { @@ -2132,8 +2665,15 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, } size = (DLINFO_ITEMS + 1) * 2; - if (k_platform) + if (k_base_platform) { size += 2; + } + if (k_platform) { + size += 2; + } + if (vdso_info) { + size += 2; + } #ifdef DLINFO_ARCH_ITEMS size += DLINFO_ARCH_ITEMS * 2; #endif @@ -2185,13 +2725,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, NEW_AUX_ENT(AT_PHDR, (abi_ulong)(info->load_addr + exec->e_phoff)); NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr))); NEW_AUX_ENT(AT_PHNUM, (abi_ulong)(exec->e_phnum)); - if ((info->alignment & ~qemu_host_page_mask) != 0) { - /* Target doesn't support host page size alignment */ - NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(TARGET_PAGE_SIZE)); - } else { - NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(MAX(TARGET_PAGE_SIZE, - qemu_host_page_size))); - } + NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(TARGET_PAGE_SIZE)); NEW_AUX_ENT(AT_BASE, (abi_ulong)(interp_info ? interp_info->load_addr : 0)); NEW_AUX_ENT(AT_FLAGS, (abi_ulong)0); NEW_AUX_ENT(AT_ENTRY, info->entry); @@ -2209,9 +2743,15 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, NEW_AUX_ENT(AT_HWCAP2, (abi_ulong) ELF_HWCAP2); #endif + if (u_base_platform) { + NEW_AUX_ENT(AT_BASE_PLATFORM, u_base_platform); + } if (u_platform) { NEW_AUX_ENT(AT_PLATFORM, u_platform); } + if (vdso_info) { + NEW_AUX_ENT(AT_SYSINFO_EHDR, vdso_info->load_addr); + } NEW_AUX_ENT (AT_NULL, 0); #undef NEW_AUX_ENT @@ -2242,14 +2782,167 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, } #if defined(HI_COMMPAGE) -#define LO_COMMPAGE 0 +#define LO_COMMPAGE -1 #elif defined(LO_COMMPAGE) #define HI_COMMPAGE 0 #else #define HI_COMMPAGE 0 -#define LO_COMMPAGE 0 +#define LO_COMMPAGE -1 +#ifndef INIT_GUEST_COMMPAGE #define init_guest_commpage() true #endif +#endif + +/** + * pgb_try_mmap: + * @addr: host start address + * @addr_last: host last address + * @keep: do not unmap the probe region + * + * Return 1 if [@addr, @addr_last] is not mapped in the host, + * return 0 if it is not available to map, and -1 on mmap error. + * If @keep, the region is left mapped on success, otherwise unmapped. + */ +static int pgb_try_mmap(uintptr_t addr, uintptr_t addr_last, bool keep) +{ + size_t size = addr_last - addr + 1; + void *p = mmap((void *)addr, size, PROT_NONE, + MAP_ANONYMOUS | MAP_PRIVATE | + MAP_NORESERVE | MAP_FIXED_NOREPLACE, -1, 0); + int ret; + + if (p == MAP_FAILED) { + return errno == EEXIST ? 0 : -1; + } + ret = p == (void *)addr; + if (!keep || !ret) { + munmap(p, size); + } + return ret; +} + +/** + * pgb_try_mmap_skip_brk(uintptr_t addr, uintptr_t size, uintptr_t brk) + * @addr: host address + * @addr_last: host last address + * @brk: host brk + * + * Like pgb_try_mmap, but additionally reserve some memory following brk. + */ +static int pgb_try_mmap_skip_brk(uintptr_t addr, uintptr_t addr_last, + uintptr_t brk, bool keep) +{ + uintptr_t brk_last = brk + 16 * MiB - 1; + + /* Do not map anything close to the host brk. */ + if (addr <= brk_last && brk <= addr_last) { + return 0; + } + return pgb_try_mmap(addr, addr_last, keep); +} + +/** + * pgb_try_mmap_set: + * @ga: set of guest addrs + * @base: guest_base + * @brk: host brk + * + * Return true if all @ga can be mapped by the host at @base. + * On success, retain the mapping at index 0 for reserved_va. + */ + +typedef struct PGBAddrs { + uintptr_t bounds[3][2]; /* start/last pairs */ + int nbounds; +} PGBAddrs; + +static bool pgb_try_mmap_set(const PGBAddrs *ga, uintptr_t base, uintptr_t brk) +{ + for (int i = ga->nbounds - 1; i >= 0; --i) { + if (pgb_try_mmap_skip_brk(ga->bounds[i][0] + base, + ga->bounds[i][1] + base, + brk, i == 0 && reserved_va) <= 0) { + return false; + } + } + return true; +} + +/** + * pgb_addr_set: + * @ga: output set of guest addrs + * @guest_loaddr: guest image low address + * @guest_loaddr: guest image high address + * @identity: create for identity mapping + * + * Fill in @ga with the image, COMMPAGE and NULL page. + */ +static bool pgb_addr_set(PGBAddrs *ga, abi_ulong guest_loaddr, + abi_ulong guest_hiaddr, bool try_identity) +{ + int n; + + /* + * With a low commpage, or a guest mapped very low, + * we may not be able to use the identity map. + */ + if (try_identity) { + if (LO_COMMPAGE != -1 && LO_COMMPAGE < mmap_min_addr) { + return false; + } + if (guest_loaddr != 0 && guest_loaddr < mmap_min_addr) { + return false; + } + } + + memset(ga, 0, sizeof(*ga)); + n = 0; + + if (reserved_va) { + ga->bounds[n][0] = try_identity ? mmap_min_addr : 0; + ga->bounds[n][1] = reserved_va; + n++; + /* LO_COMMPAGE and NULL handled by reserving from 0. */ + } else { + /* Add any LO_COMMPAGE or NULL page. */ + if (LO_COMMPAGE != -1) { + ga->bounds[n][0] = 0; + ga->bounds[n][1] = LO_COMMPAGE + TARGET_PAGE_SIZE - 1; + n++; + } else if (!try_identity) { + ga->bounds[n][0] = 0; + ga->bounds[n][1] = TARGET_PAGE_SIZE - 1; + n++; + } + + /* Add the guest image for ET_EXEC. */ + if (guest_loaddr) { + ga->bounds[n][0] = guest_loaddr; + ga->bounds[n][1] = guest_hiaddr; + n++; + } + } + + /* + * Temporarily disable + * "comparison is always false due to limited range of data type" + * due to comparison between unsigned and (possible) 0. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" + + /* Add any HI_COMMPAGE not covered by reserved_va. */ + if (reserved_va < HI_COMMPAGE) { + ga->bounds[n][0] = HI_COMMPAGE & qemu_real_host_page_mask(); + ga->bounds[n][1] = HI_COMMPAGE + TARGET_PAGE_SIZE - 1; + n++; + } + +#pragma GCC diagnostic pop + + ga->nbounds = n; + return true; +} static void pgb_fail_in_use(const char *image_name) { @@ -2259,19 +2952,169 @@ static void pgb_fail_in_use(const char *image_name) exit(EXIT_FAILURE); } -static void pgb_have_guest_base(const char *image_name, abi_ulong guest_loaddr, - abi_ulong guest_hiaddr, long align) +static void pgb_fixed(const char *image_name, uintptr_t guest_loaddr, + uintptr_t guest_hiaddr, uintptr_t align) { - const int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE; - void *addr, *test; + PGBAddrs ga; + uintptr_t brk = (uintptr_t)sbrk(0); if (!QEMU_IS_ALIGNED(guest_base, align)) { fprintf(stderr, "Requested guest base %p does not satisfy " - "host minimum alignment (0x%lx)\n", + "host minimum alignment (0x%" PRIxPTR ")\n", (void *)guest_base, align); exit(EXIT_FAILURE); } + if (!pgb_addr_set(&ga, guest_loaddr, guest_hiaddr, !guest_base) + || !pgb_try_mmap_set(&ga, guest_base, brk)) { + pgb_fail_in_use(image_name); + } +} + +/** + * pgb_find_fallback: + * + * This is a fallback method for finding holes in the host address space + * if we don't have the benefit of being able to access /proc/self/map. + * It can potentially take a very long time as we can only dumbly iterate + * up the host address space seeing if the allocation would work. + */ +static uintptr_t pgb_find_fallback(const PGBAddrs *ga, uintptr_t align, + uintptr_t brk) +{ + /* TODO: come up with a better estimate of how much to skip. */ + uintptr_t skip = sizeof(uintptr_t) == 4 ? MiB : GiB; + + for (uintptr_t base = skip; ; base += skip) { + base = ROUND_UP(base, align); + if (pgb_try_mmap_set(ga, base, brk)) { + return base; + } + if (base >= -skip) { + return -1; + } + } +} + +static uintptr_t pgb_try_itree(const PGBAddrs *ga, uintptr_t base, + IntervalTreeRoot *root) +{ + for (int i = ga->nbounds - 1; i >= 0; --i) { + uintptr_t s = base + ga->bounds[i][0]; + uintptr_t l = base + ga->bounds[i][1]; + IntervalTreeNode *n; + + if (l < s) { + /* Wraparound. Skip to advance S to mmap_min_addr. */ + return mmap_min_addr - s; + } + + n = interval_tree_iter_first(root, s, l); + if (n != NULL) { + /* Conflict. Skip to advance S to LAST + 1. */ + return n->last - s + 1; + } + } + return 0; /* success */ +} + +static uintptr_t pgb_find_itree(const PGBAddrs *ga, IntervalTreeRoot *root, + uintptr_t align, uintptr_t brk) +{ + uintptr_t last = mmap_min_addr; + uintptr_t base, skip; + + while (true) { + base = ROUND_UP(last, align); + if (base < last) { + return -1; + } + + skip = pgb_try_itree(ga, base, root); + if (skip == 0) { + break; + } + + last = base + skip; + if (last < base) { + return -1; + } + } + + /* + * We've chosen 'base' based on holes in the interval tree, + * but we don't yet know if it is a valid host address. + * Because it is the first matching hole, if the host addresses + * are invalid we know there are no further matches. + */ + return pgb_try_mmap_set(ga, base, brk) ? base : -1; +} + +static void pgb_dynamic(const char *image_name, uintptr_t guest_loaddr, + uintptr_t guest_hiaddr, uintptr_t align) +{ + IntervalTreeRoot *root; + uintptr_t brk, ret; + PGBAddrs ga; + + /* Try the identity map first. */ + if (pgb_addr_set(&ga, guest_loaddr, guest_hiaddr, true)) { + brk = (uintptr_t)sbrk(0); + if (pgb_try_mmap_set(&ga, 0, brk)) { + guest_base = 0; + return; + } + } + + /* + * Rebuild the address set for non-identity map. + * This differs in the mapping of the guest NULL page. + */ + pgb_addr_set(&ga, guest_loaddr, guest_hiaddr, false); + + root = read_self_maps(); + + /* Read brk after we've read the maps, which will malloc. */ + brk = (uintptr_t)sbrk(0); + + if (!root) { + ret = pgb_find_fallback(&ga, align, brk); + } else { + /* + * Reserve the area close to the host brk. + * This will be freed with the rest of the tree. + */ + IntervalTreeNode *b = g_new0(IntervalTreeNode, 1); + b->start = brk; + b->last = brk + 16 * MiB - 1; + interval_tree_insert(b, root); + + ret = pgb_find_itree(&ga, root, align, brk); + free_self_maps(root); + } + + if (ret == -1) { + int w = TARGET_LONG_BITS / 4; + + error_report("%s: Unable to find a guest_base to satisfy all " + "guest address mapping requirements", image_name); + + for (int i = 0; i < ga.nbounds; ++i) { + error_printf(" %0*" PRIx64 "-%0*" PRIx64 "\n", + w, (uint64_t)ga.bounds[i][0], + w, (uint64_t)ga.bounds[i][1]); + } + exit(EXIT_FAILURE); + } + guest_base = ret; +} + +void probe_guest_base(const char *image_name, abi_ulong guest_loaddr, + abi_ulong guest_hiaddr) +{ + /* In order to use host shmat, we must be able to honor SHMLBA. */ + uintptr_t align = MAX(SHMLBA, TARGET_PAGE_SIZE); + /* Sanity check the guest binary. */ if (reserved_va) { if (guest_hiaddr > reserved_va) { @@ -2281,304 +3124,24 @@ static void pgb_have_guest_base(const char *image_name, abi_ulong guest_loaddr, exit(EXIT_FAILURE); } } else { -#if HOST_LONG_BITS < TARGET_ABI_BITS - if ((guest_hiaddr - guest_base) > ~(uintptr_t)0) { + if (guest_hiaddr != (uintptr_t)guest_hiaddr) { error_report("%s: requires more virtual address space " "than the host can provide (0x%" PRIx64 ")", - image_name, (uint64_t)guest_hiaddr - guest_base); + image_name, (uint64_t)guest_hiaddr + 1); exit(EXIT_FAILURE); } -#endif } - /* - * Expand the allocation to the entire reserved_va. - * Exclude the mmap_min_addr hole. - */ - if (reserved_va) { - guest_loaddr = (guest_base >= mmap_min_addr ? 0 - : mmap_min_addr - guest_base); - guest_hiaddr = reserved_va; - } - - /* Reserve the address space for the binary, or reserved_va. */ - test = g2h_untagged(guest_loaddr); - addr = mmap(test, guest_hiaddr - guest_loaddr, PROT_NONE, flags, -1, 0); - if (test != addr) { - pgb_fail_in_use(image_name); - } - qemu_log_mask(CPU_LOG_PAGE, - "%s: base @ %p for " TARGET_ABI_FMT_ld " bytes\n", - __func__, addr, guest_hiaddr - guest_loaddr); -} - -/** - * pgd_find_hole_fallback: potential mmap address - * @guest_size: size of available space - * @brk: location of break - * @align: memory alignment - * - * This is a fallback method for finding a hole in the host address - * space if we don't have the benefit of being able to access - * /proc/self/map. It can potentially take a very long time as we can - * only dumbly iterate up the host address space seeing if the - * allocation would work. - */ -static uintptr_t pgd_find_hole_fallback(uintptr_t guest_size, uintptr_t brk, - long align, uintptr_t offset) -{ - uintptr_t base; - - /* Start (aligned) at the bottom and work our way up */ - base = ROUND_UP(mmap_min_addr, align); - - while (true) { - uintptr_t align_start, end; - align_start = ROUND_UP(base, align); - end = align_start + guest_size + offset; - - /* if brk is anywhere in the range give ourselves some room to grow. */ - if (align_start <= brk && brk < end) { - base = brk + (16 * MiB); - continue; - } else if (align_start + guest_size < align_start) { - /* we have run out of space */ - return -1; - } else { - int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE | - MAP_FIXED_NOREPLACE; - void * mmap_start = mmap((void *) align_start, guest_size, - PROT_NONE, flags, -1, 0); - if (mmap_start != MAP_FAILED) { - munmap(mmap_start, guest_size); - if (mmap_start == (void *) align_start) { - qemu_log_mask(CPU_LOG_PAGE, - "%s: base @ %p for %" PRIdPTR" bytes\n", - __func__, mmap_start + offset, guest_size); - return (uintptr_t) mmap_start + offset; - } - } - base += qemu_host_page_size; - } - } -} - -/* Return value for guest_base, or -1 if no hole found. */ -static uintptr_t pgb_find_hole(uintptr_t guest_loaddr, uintptr_t guest_size, - long align, uintptr_t offset) -{ - GSList *maps, *iter; - uintptr_t this_start, this_end, next_start, brk; - intptr_t ret = -1; - - assert(QEMU_IS_ALIGNED(guest_loaddr, align)); - - maps = read_self_maps(); - - /* Read brk after we've read the maps, which will malloc. */ - brk = (uintptr_t)sbrk(0); - - if (!maps) { - return pgd_find_hole_fallback(guest_size, brk, align, offset); - } - - /* The first hole is before the first map entry. */ - this_start = mmap_min_addr; - - for (iter = maps; iter; - this_start = next_start, iter = g_slist_next(iter)) { - uintptr_t align_start, hole_size; - - this_end = ((MapInfo *)iter->data)->start; - next_start = ((MapInfo *)iter->data)->end; - align_start = ROUND_UP(this_start + offset, align); - - /* Skip holes that are too small. */ - if (align_start >= this_end) { - continue; - } - hole_size = this_end - align_start; - if (hole_size < guest_size) { - continue; - } - - /* If this hole contains brk, give ourselves some room to grow. */ - if (this_start <= brk && brk < this_end) { - hole_size -= guest_size; - if (sizeof(uintptr_t) == 8 && hole_size >= 1 * GiB) { - align_start += 1 * GiB; - } else if (hole_size >= 16 * MiB) { - align_start += 16 * MiB; - } else { - align_start = (this_end - guest_size) & -align; - if (align_start < this_start) { - continue; - } - } - } - - /* Record the lowest successful match. */ - if (ret < 0) { - ret = align_start; - } - /* If this hole contains the identity map, select it. */ - if (align_start <= guest_loaddr && - guest_loaddr + guest_size <= this_end) { - ret = 0; - } - /* If this hole ends above the identity map, stop looking. */ - if (this_end >= guest_loaddr) { - break; - } - } - free_self_maps(maps); - - if (ret != -1) { - qemu_log_mask(CPU_LOG_PAGE, "%s: base @ %" PRIxPTR - " for %" PRIuPTR " bytes\n", - __func__, ret, guest_size); - } - - return ret; -} - -static void pgb_static(const char *image_name, abi_ulong orig_loaddr, - abi_ulong orig_hiaddr, long align) -{ - uintptr_t loaddr = orig_loaddr; - uintptr_t hiaddr = orig_hiaddr; - uintptr_t offset = 0; - uintptr_t addr; - - if (hiaddr != orig_hiaddr) { - error_report("%s: requires virtual address space that the " - "host cannot provide (0x%" PRIx64 ")", - image_name, (uint64_t)orig_hiaddr); - exit(EXIT_FAILURE); - } - - loaddr &= -align; - if (HI_COMMPAGE) { - /* - * Extend the allocation to include the commpage. - * For a 64-bit host, this is just 4GiB; for a 32-bit host we - * need to ensure there is space bellow the guest_base so we - * can map the commpage in the place needed when the address - * arithmetic wraps around. - */ - if (sizeof(uintptr_t) == 8 || loaddr >= 0x80000000u) { - hiaddr = (uintptr_t) 4 << 30; - } else { - offset = -(HI_COMMPAGE & -align); - } - } else if (LO_COMMPAGE != 0) { - loaddr = MIN(loaddr, LO_COMMPAGE & -align); - } - - addr = pgb_find_hole(loaddr, hiaddr - loaddr, align, offset); - if (addr == -1) { - /* - * If HI_COMMPAGE, there *might* be a non-consecutive allocation - * that can satisfy both. But as the normal arm32 link base address - * is ~32k, and we extend down to include the commpage, making the - * overhead only ~96k, this is unlikely. - */ - error_report("%s: Unable to allocate %#zx bytes of " - "virtual address space", image_name, - (size_t)(hiaddr - loaddr)); - exit(EXIT_FAILURE); - } - - guest_base = addr; - - qemu_log_mask(CPU_LOG_PAGE, "%s: base @ %"PRIxPTR" for %" PRIuPTR" bytes\n", - __func__, addr, hiaddr - loaddr); -} - -static void pgb_dynamic(const char *image_name, long align) -{ - /* - * The executable is dynamic and does not require a fixed address. - * All we need is a commpage that satisfies align. - * If we do not need a commpage, leave guest_base == 0. - */ - if (HI_COMMPAGE) { - uintptr_t addr, commpage; - - /* 64-bit hosts should have used reserved_va. */ - assert(sizeof(uintptr_t) == 4); - - /* - * By putting the commpage at the first hole, that puts guest_base - * just above that, and maximises the positive guest addresses. - */ - commpage = HI_COMMPAGE & -align; - addr = pgb_find_hole(commpage, -commpage, align, 0); - assert(addr != -1); - guest_base = addr; - } -} - -static void pgb_reserved_va(const char *image_name, abi_ulong guest_loaddr, - abi_ulong guest_hiaddr, long align) -{ - int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE; - void *addr, *test; - - if (guest_hiaddr > reserved_va) { - error_report("%s: requires more than reserved virtual " - "address space (0x%" PRIx64 " > 0x%lx)", - image_name, (uint64_t)guest_hiaddr, reserved_va); - exit(EXIT_FAILURE); - } - - /* Widen the "image" to the entire reserved address space. */ - pgb_static(image_name, 0, reserved_va, align); - - /* osdep.h defines this as 0 if it's missing */ - flags |= MAP_FIXED_NOREPLACE; - - /* Reserve the memory on the host. */ - assert(guest_base != 0); - test = g2h_untagged(0); - addr = mmap(test, reserved_va, PROT_NONE, flags, -1, 0); - if (addr == MAP_FAILED || addr != test) { - error_report("Unable to reserve 0x%lx bytes of virtual address " - "space at %p (%s) for use as guest address space (check your " - "virtual memory ulimit setting, min_mmap_addr or reserve less " - "using -R option)", reserved_va, test, strerror(errno)); - exit(EXIT_FAILURE); - } - - qemu_log_mask(CPU_LOG_PAGE, "%s: base @ %p for %lu bytes\n", - __func__, addr, reserved_va); -} - -void probe_guest_base(const char *image_name, abi_ulong guest_loaddr, - abi_ulong guest_hiaddr) -{ - /* In order to use host shmat, we must be able to honor SHMLBA. */ - uintptr_t align = MAX(SHMLBA, qemu_host_page_size); - if (have_guest_base) { - pgb_have_guest_base(image_name, guest_loaddr, guest_hiaddr, align); - } else if (reserved_va) { - pgb_reserved_va(image_name, guest_loaddr, guest_hiaddr, align); - } else if (guest_loaddr) { - pgb_static(image_name, guest_loaddr, guest_hiaddr, align); + pgb_fixed(image_name, guest_loaddr, guest_hiaddr, align); } else { - pgb_dynamic(image_name, align); + pgb_dynamic(image_name, guest_loaddr, guest_hiaddr, align); } /* Reserve and initialize the commpage. */ if (!init_guest_commpage()) { - /* - * With have_guest_base, the user has selected the address and - * we are trying to work with that. Otherwise, we have selected - * free space and init_guest_commpage must succeeded. - */ - assert(have_guest_base); - pgb_fail_in_use(image_name); + /* We have already probed for the commpage being free. */ + g_assert_not_reached(); } assert(QEMU_IS_ALIGNED(guest_base, align)); @@ -2646,10 +3209,9 @@ static bool parse_elf_property(const uint32_t *data, int *off, int datasz, } /* Process NT_GNU_PROPERTY_TYPE_0. */ -static bool parse_elf_properties(int image_fd, +static bool parse_elf_properties(const ImageSource *src, struct image_info *info, const struct elf_phdr *phdr, - char bprm_buf[BPRM_BUF_SIZE], Error **errp) { union { @@ -2677,14 +3239,8 @@ static bool parse_elf_properties(int image_fd, return false; } - if (phdr->p_offset + n <= BPRM_BUF_SIZE) { - memcpy(¬e, bprm_buf + phdr->p_offset, n); - } else { - ssize_t len = pread(image_fd, ¬e, n, phdr->p_offset); - if (len != n) { - error_setg_errno(errp, errno, "Error reading file header"); - return false; - } + if (!imgsrc_read(¬e, phdr->p_offset, n, src, errp)) { + return false; } /* @@ -2730,29 +3286,34 @@ static bool parse_elf_properties(int image_fd, } } -/* Load an ELF image into the address space. +/** + * load_elf_image: Load an ELF image into the address space. + * @image_name: the filename of the image, to use in error messages. + * @src: the ImageSource from which to read. + * @info: info collected from the loaded image. + * @ehdr: the ELF header, not yet bswapped. + * @pinterp_name: record any PT_INTERP string found. + * + * On return: @info values will be filled in, as necessary or available. + */ - IMAGE_NAME is the filename of the image, to use in error messages. - IMAGE_FD is the open file descriptor for the image. - - BPRM_BUF is a copy of the beginning of the file; this of course - contains the elf file header at offset 0. It is assumed that this - buffer is sufficiently aligned to present no problems to the host - in accessing data at aligned offsets within the buffer. - - On return: INFO values will be filled in, as necessary or available. */ - -static void load_elf_image(const char *image_name, int image_fd, - struct image_info *info, char **pinterp_name, - char bprm_buf[BPRM_BUF_SIZE]) +static void load_elf_image(const char *image_name, const ImageSource *src, + struct image_info *info, struct elfhdr *ehdr, + char **pinterp_name) { - struct elfhdr *ehdr = (struct elfhdr *)bprm_buf; - struct elf_phdr *phdr; + g_autofree struct elf_phdr *phdr = NULL; abi_ulong load_addr, load_bias, loaddr, hiaddr, error; - int i, retval, prot_exec; + int i, prot_exec; Error *err = NULL; - /* First of all, some simple consistency checks */ + /* + * First of all, some simple consistency checks. + * Note that we rely on the bswapped ehdr staying in bprm_buf, + * for later use by load_elf_binary and create_elf_tables. + */ + if (!imgsrc_read(ehdr, 0, sizeof(*ehdr), src, &err)) { + goto exit_errmsg; + } if (!elf_check_ident(ehdr)) { error_setg(&err, "Invalid ELF image for this architecture"); goto exit_errmsg; @@ -2763,15 +3324,11 @@ static void load_elf_image(const char *image_name, int image_fd, goto exit_errmsg; } - i = ehdr->e_phnum * sizeof(struct elf_phdr); - if (ehdr->e_phoff + i <= BPRM_BUF_SIZE) { - phdr = (struct elf_phdr *)(bprm_buf + ehdr->e_phoff); - } else { - phdr = (struct elf_phdr *) alloca(i); - retval = pread(image_fd, phdr, i, ehdr->e_phoff); - if (retval != i) { - goto exit_read; - } + phdr = imgsrc_read_alloc(ehdr->e_phoff, + ehdr->e_phnum * sizeof(struct elf_phdr), + src, &err); + if (phdr == NULL) { + goto exit_errmsg; } bswap_phdr(phdr, ehdr->e_phnum); @@ -2786,14 +3343,15 @@ static void load_elf_image(const char *image_name, int image_fd, */ loaddr = -1, hiaddr = 0; info->alignment = 0; + info->exec_stack = EXSTACK_DEFAULT; for (i = 0; i < ehdr->e_phnum; ++i) { struct elf_phdr *eppnt = phdr + i; if (eppnt->p_type == PT_LOAD) { - abi_ulong a = eppnt->p_vaddr - eppnt->p_offset; + abi_ulong a = eppnt->p_vaddr & TARGET_PAGE_MASK; if (a < loaddr) { loaddr = a; } - a = eppnt->p_vaddr + eppnt->p_memsz; + a = eppnt->p_vaddr + eppnt->p_memsz - 1; if (a > hiaddr) { hiaddr = a; } @@ -2807,17 +3365,10 @@ static void load_elf_image(const char *image_name, int image_fd, goto exit_errmsg; } - interp_name = g_malloc(eppnt->p_filesz); - - if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) { - memcpy(interp_name, bprm_buf + eppnt->p_offset, - eppnt->p_filesz); - } else { - retval = pread(image_fd, interp_name, eppnt->p_filesz, - eppnt->p_offset); - if (retval != eppnt->p_filesz) { - goto exit_read; - } + interp_name = imgsrc_read_alloc(eppnt->p_offset, eppnt->p_filesz, + src, &err); + if (interp_name == NULL) { + goto exit_errmsg; } if (interp_name[eppnt->p_filesz - 1] != 0) { error_setg(&err, "Invalid PT_INTERP entry"); @@ -2825,34 +3376,17 @@ static void load_elf_image(const char *image_name, int image_fd, } *pinterp_name = g_steal_pointer(&interp_name); } else if (eppnt->p_type == PT_GNU_PROPERTY) { - if (!parse_elf_properties(image_fd, info, eppnt, bprm_buf, &err)) { + if (!parse_elf_properties(src, info, eppnt, &err)) { goto exit_errmsg; } + } else if (eppnt->p_type == PT_GNU_STACK) { + info->exec_stack = eppnt->p_flags & PF_X; } } - 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. However some 64 bit guests (e.g. s390x) - * attempt to place their heap further ahead and currently - * nothing stops them smashing into QEMUs address space. - */ -#if TARGET_LONG_BITS == 64 - info->reserve_brk = 32 * MiB; -#else - info->reserve_brk = 16 * MiB; -#endif - hiaddr += info->reserve_brk; + load_addr = loaddr; + if (pinterp_name != NULL) { if (ehdr->e_type == ET_EXEC) { /* * Make sure that the low address does not conflict with @@ -2860,31 +3394,55 @@ static void load_elf_image(const char *image_name, int image_fd, */ probe_guest_base(image_name, loaddr, hiaddr); } else { + abi_ulong align; + /* * The binary is dynamic, but we still need to * select guest_base. In this case we pass a size. */ probe_guest_base(image_name, 0, hiaddr - loaddr); + + /* + * Avoid collision with the loader by providing a different + * default load address. + */ + load_addr += elf_et_dyn_base; + + /* + * TODO: Better support for mmap alignment is desirable. + * Since we do not have complete control over the guest + * address space, we prefer the kernel to choose some address + * rather than force the use of LOAD_ADDR via MAP_FIXED. + * But without MAP_FIXED we cannot guarantee alignment, + * only suggest it. + */ + align = pow2ceil(info->alignment); + if (align) { + load_addr &= -align; + } } } /* * 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. + * In the case of ET_EXEC, we supply MAP_FIXED_NOREPLACE so that we get + * exactly the address range that is required. Without reserved_va, + * the guest address space is not isolated. We have attempted to avoid + * conflict with the host program itself via probe_guest_base, but using + * MAP_FIXED_NOREPLACE instead of MAP_FIXED provides an extra check. * * 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 + * pre-linked, LOAD_ADDR 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, + load_addr = target_mmap(load_addr, (size_t)hiaddr - loaddr + 1, PROT_NONE, MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | - (ehdr->e_type == ET_EXEC ? MAP_FIXED : 0), + (ehdr->e_type == ET_EXEC ? MAP_FIXED_NOREPLACE : 0), -1, 0); if (load_addr == -1) { goto exit_mmap; @@ -2919,7 +3477,8 @@ static void load_elf_image(const char *image_name, int image_fd, info->end_code = 0; info->start_data = -1; info->end_data = 0; - info->brk = 0; + /* Usual start for brk is after all sections of the main executable. */ + info->brk = TARGET_PAGE_ALIGN(hiaddr + load_bias); info->elf_flags = ehdr->e_flags; prot_exec = PROT_EXEC; @@ -2945,7 +3504,7 @@ static void load_elf_image(const char *image_name, int image_fd, for (i = 0; i < ehdr->e_phnum; i++) { struct elf_phdr *eppnt = phdr + i; if (eppnt->p_type == PT_LOAD) { - abi_ulong vaddr, vaddr_po, vaddr_ps, vaddr_ef, vaddr_em, vaddr_len; + abi_ulong vaddr, vaddr_po, vaddr_ps, vaddr_ef, vaddr_em; int elf_prot = 0; if (eppnt->p_flags & PF_R) { @@ -2959,8 +3518,8 @@ static void load_elf_image(const char *image_name, int image_fd, } vaddr = load_bias + eppnt->p_vaddr; - vaddr_po = TARGET_ELF_PAGEOFFSET(vaddr); - vaddr_ps = TARGET_ELF_PAGESTART(vaddr); + vaddr_po = vaddr & ~TARGET_PAGE_MASK; + vaddr_ps = vaddr & TARGET_PAGE_MASK; vaddr_ef = vaddr + eppnt->p_filesz; vaddr_em = vaddr + eppnt->p_memsz; @@ -2970,30 +3529,18 @@ static void load_elf_image(const char *image_name, int image_fd, * but no backing file segment. */ if (eppnt->p_filesz != 0) { - vaddr_len = TARGET_ELF_PAGELENGTH(eppnt->p_filesz + vaddr_po); - error = target_mmap(vaddr_ps, vaddr_len, elf_prot, - MAP_PRIVATE | MAP_FIXED, - image_fd, eppnt->p_offset - vaddr_po); - + error = imgsrc_mmap(vaddr_ps, eppnt->p_filesz + vaddr_po, + elf_prot, MAP_PRIVATE | MAP_FIXED, + src, eppnt->p_offset - vaddr_po); if (error == -1) { goto exit_mmap; } + } - /* - * If the load segment requests extra zeros (e.g. bss), map it. - */ - if (eppnt->p_filesz < eppnt->p_memsz) { - zero_bss(vaddr_ef, vaddr_em, elf_prot); - } - } else if (eppnt->p_memsz != 0) { - vaddr_len = TARGET_ELF_PAGELENGTH(eppnt->p_memsz + vaddr_po); - error = target_mmap(vaddr_ps, vaddr_len, elf_prot, - MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, - -1, 0); - - if (error == -1) { - goto exit_mmap; - } + /* If the load segment requests extra zeros (e.g. bss), map it. */ + if (vaddr_ef < vaddr_em && + !zero_bss(vaddr_ef, vaddr_em, elf_prot, &err)) { + goto exit_errmsg; } /* Find the full program boundaries. */ @@ -3013,26 +3560,14 @@ static void load_elf_image(const char *image_name, int image_fd, info->end_data = vaddr_ef; } } - if (vaddr_em > info->brk) { - info->brk = vaddr_em; - } #ifdef TARGET_MIPS } else if (eppnt->p_type == PT_MIPS_ABIFLAGS) { Mips_elf_abiflags_v0 abiflags; - if (eppnt->p_filesz < sizeof(Mips_elf_abiflags_v0)) { - error_setg(&err, "Invalid PT_MIPS_ABIFLAGS entry"); + + if (!imgsrc_read(&abiflags, eppnt->p_offset, sizeof(abiflags), + src, &err)) { goto exit_errmsg; } - if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) { - memcpy(&abiflags, bprm_buf + eppnt->p_offset, - sizeof(Mips_elf_abiflags_v0)); - } else { - retval = pread(image_fd, &abiflags, sizeof(Mips_elf_abiflags_v0), - eppnt->p_offset); - if (retval != sizeof(Mips_elf_abiflags_v0)) { - goto exit_read; - } - } bswap_mips_abiflags(&abiflags); info->fp_abi = abiflags.fp_abi; #endif @@ -3045,21 +3580,16 @@ static void load_elf_image(const char *image_name, int image_fd, } if (qemu_log_enabled()) { - load_symbols(ehdr, image_fd, load_bias); + load_symbols(ehdr, src, load_bias); } + debuginfo_report_elf(image_name, src->fd, load_bias); + mmap_unlock(); - close(image_fd); + close(src->fd); return; - exit_read: - if (retval >= 0) { - error_setg(&err, "Incomplete read of file header"); - } else { - error_setg_errno(&err, errno, "Error reading file header"); - } - goto exit_errmsg; exit_mmap: error_setg_errno(&err, errno, "Error mapping file"); goto exit_errmsg; @@ -3071,6 +3601,8 @@ static void load_elf_image(const char *image_name, int image_fd, static void load_elf_interp(const char *filename, struct image_info *info, char bprm_buf[BPRM_BUF_SIZE]) { + struct elfhdr ehdr; + ImageSource src; int fd, retval; Error *err = NULL; @@ -3088,18 +3620,65 @@ static void load_elf_interp(const char *filename, struct image_info *info, exit(-1); } - if (retval < BPRM_BUF_SIZE) { - memset(bprm_buf + retval, 0, BPRM_BUF_SIZE - retval); + src.fd = fd; + src.cache = bprm_buf; + src.cache_size = retval; + + load_elf_image(filename, &src, info, &ehdr, NULL); +} + +#ifdef VDSO_HEADER +#include VDSO_HEADER +#define vdso_image_info() &vdso_image_info +#else +#define vdso_image_info() NULL +#endif + +static void load_elf_vdso(struct image_info *info, const VdsoImageInfo *vdso) +{ + ImageSource src; + struct elfhdr ehdr; + abi_ulong load_bias, load_addr; + + src.fd = -1; + src.cache = vdso->image; + src.cache_size = vdso->image_size; + + load_elf_image("", &src, info, &ehdr, NULL); + load_addr = info->load_addr; + load_bias = info->load_bias; + + /* + * We need to relocate the VDSO image. The one built into the kernel + * is built for a fixed address. The one built for QEMU is not, since + * that requires close control of the guest address space. + * We pre-processed the image to locate all of the addresses that need + * to be updated. + */ + for (unsigned i = 0, n = vdso->reloc_count; i < n; i++) { + abi_ulong *addr = g2h_untagged(load_addr + vdso->relocs[i]); + *addr = tswapal(tswapal(*addr) + load_bias); } - load_elf_image(filename, fd, info, NULL, bprm_buf); + /* Install signal trampolines, if present. */ + if (vdso->sigreturn_ofs) { + default_sigreturn = load_addr + vdso->sigreturn_ofs; + } + if (vdso->rt_sigreturn_ofs) { + default_rt_sigreturn = load_addr + vdso->rt_sigreturn_ofs; + } + + /* Remove write from VDSO segment. */ + target_mprotect(info->start_data, info->end_data - info->start_data, + PROT_READ | PROT_EXEC); } static int symfind(const void *s0, const void *s1) { - target_ulong addr = *(target_ulong *)s0; struct elf_sym *sym = (struct elf_sym *)s1; + __typeof(sym->st_value) addr = *(uint64_t *)s0; int result = 0; + if (addr < sym->st_value) { result = -1; } else if (addr >= sym->st_value + sym->st_size) { @@ -3108,7 +3687,7 @@ static int symfind(const void *s0, const void *s1) return result; } -static const char *lookup_symbolxx(struct syminfo *s, target_ulong orig_addr) +static const char *lookup_symbolxx(struct syminfo *s, uint64_t orig_addr) { #if ELF_CLASS == ELFCLASS32 struct elf_sym *syms = s->disas_symtab.elf32; @@ -3138,19 +3717,20 @@ static int symcmp(const void *s0, const void *s1) } /* Best attempt to load symbols from this ELF object. */ -static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias) +static void load_symbols(struct elfhdr *hdr, const ImageSource *src, + abi_ulong load_bias) { int i, shnum, nsyms, sym_idx = 0, str_idx = 0; - uint64_t segsz; - struct elf_shdr *shdr; + g_autofree struct elf_shdr *shdr = NULL; char *strings = NULL; - struct syminfo *s = NULL; - struct elf_sym *new_syms, *syms = NULL; + struct elf_sym *syms = NULL; + struct elf_sym *new_syms; + uint64_t segsz; shnum = hdr->e_shnum; - i = shnum * sizeof(struct elf_shdr); - shdr = (struct elf_shdr *)alloca(i); - if (pread(fd, shdr, i, hdr->e_shoff) != i) { + shdr = imgsrc_read_alloc(hdr->e_shoff, shnum * sizeof(struct elf_shdr), + src, NULL); + if (shdr == NULL) { return; } @@ -3168,31 +3748,33 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias) found: /* Now know where the strtab and symtab are. Snarf them. */ - s = g_try_new(struct syminfo, 1); - if (!s) { - goto give_up; - } segsz = shdr[str_idx].sh_size; - s->disas_strtab = strings = g_try_malloc(segsz); - if (!strings || - pread(fd, strings, segsz, shdr[str_idx].sh_offset) != segsz) { + strings = g_try_malloc(segsz); + if (!strings) { + goto give_up; + } + if (!imgsrc_read(strings, shdr[str_idx].sh_offset, segsz, src, NULL)) { goto give_up; } segsz = shdr[sym_idx].sh_size; - syms = g_try_malloc(segsz); - if (!syms || pread(fd, syms, segsz, shdr[sym_idx].sh_offset) != segsz) { - goto give_up; - } - if (segsz / sizeof(struct elf_sym) > INT_MAX) { - /* Implausibly large symbol table: give up rather than ploughing - * on with the number of symbols calculation overflowing + /* + * Implausibly large symbol table: give up rather than ploughing + * on with the number of symbols calculation overflowing. */ goto give_up; } nsyms = segsz / sizeof(struct elf_sym); + syms = g_try_malloc(segsz); + if (!syms) { + goto give_up; + } + if (!imgsrc_read(syms, shdr[sym_idx].sh_offset, segsz, src, NULL)) { + goto give_up; + } + for (i = 0; i < nsyms; ) { bswap_sym(syms + i); /* Throw away entries which we do not need. */ @@ -3217,10 +3799,12 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias) goto give_up; } - /* Attempt to free the storage associated with the local symbols - that we threw away. Whether or not this has any effect on the - memory allocation depends on the malloc implementation and how - many symbols we managed to discard. */ + /* + * Attempt to free the storage associated with the local symbols + * that we threw away. Whether or not this has any effect on the + * memory allocation depends on the malloc implementation and how + * many symbols we managed to discard. + */ new_syms = g_try_renew(struct elf_sym, syms, nsyms); if (new_syms == NULL) { goto give_up; @@ -3229,20 +3813,23 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias) qsort(syms, nsyms, sizeof(*syms), symcmp); - s->disas_num_syms = nsyms; -#if ELF_CLASS == ELFCLASS32 - s->disas_symtab.elf32 = syms; -#else - s->disas_symtab.elf64 = syms; -#endif - s->lookup_symbol = lookup_symbolxx; - s->next = syminfos; - syminfos = s; + { + struct syminfo *s = g_new(struct syminfo, 1); + s->disas_strtab = strings; + s->disas_num_syms = nsyms; +#if ELF_CLASS == ELFCLASS32 + s->disas_symtab.elf32 = syms; +#else + s->disas_symtab.elf64 = syms; +#endif + s->lookup_symbol = lookup_symbolxx; + s->next = syminfos; + syminfos = s; + } return; -give_up: - g_free(s); + give_up: g_free(strings); g_free(syms); } @@ -3284,8 +3871,14 @@ uint32_t get_elf_eflags(int fd) int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) { - struct image_info interp_info; - struct elfhdr elf_ex; + /* + * We need a copy of the elf header for passing to create_elf_tables. + * We will have overwritten the original when we re-use bprm->buf + * while loading the interpreter. Allocate the storage for this now + * and let elf_load_image do any swapping that may be required. + */ + struct elfhdr ehdr; + struct image_info interp_info, vdso_info; char *elf_interpreter = NULL; char *scratch; @@ -3294,15 +3887,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) interp_info.fp_abi = MIPS_ABI_FP_UNKNOWN; #endif - info->start_mmap = (abi_ulong)ELF_START_MMAP; - - load_elf_image(bprm->filename, bprm->fd, info, - &elf_interpreter, bprm->buf); - - /* ??? We need a copy of the elf header for passing to create_elf_tables. - If we do nothing, we'll have overwritten this when we re-use bprm->buf - when we load the interpreter. */ - elf_ex = *(struct elfhdr *)bprm->buf; + load_elf_image(bprm->filename, &bprm->src, info, &ehdr, &elf_interpreter); /* Do this so that we can load the interpreter, if need be. We will change some of these later */ @@ -3341,6 +3926,19 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) if (elf_interpreter) { load_elf_interp(elf_interpreter, &interp_info, bprm->buf); + /* + * While unusual because of ELF_ET_DYN_BASE, if we are unlucky + * with the mappings the interpreter can be loaded above but + * near the main executable, which can leave very little room + * for the heap. + * If the current brk has less than 16MB, use the end of the + * interpreter. + */ + if (interp_info.brk > info->brk && + interp_info.load_bias - info->brk < 16 * MiB) { + info->brk = interp_info.brk; + } + /* If the program interpreter is one of these two, then assume an iBCS2 image. Otherwise assume a native linux image. */ @@ -3352,8 +3950,9 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) and some applications "depend" upon this behavior. Since we do not have the power to recompile these, we emulate the SVr4 behavior. Sigh. */ - target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + target_mmap(0, TARGET_PAGE_SIZE, PROT_READ | PROT_EXEC, + MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); } #ifdef TARGET_MIPS info->interp_fp_abi = interp_info.fp_abi; @@ -3361,10 +3960,14 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) } /* - * TODO: load a vdso, which would also contain the signal trampolines. - * Otherwise, allocate a private page to hold them. + * Load a vdso if available, which will amongst other things contain the + * signal trampolines. Otherwise, allocate a separate page for them. */ - if (TARGET_ARCH_HAS_SIGTRAMP_PAGE) { + const VdsoImageInfo *vdso = vdso_image_info(); + if (vdso) { + load_elf_vdso(&vdso_info, vdso); + info->vdso = vdso_info.load_bias; + } else if (TARGET_ARCH_HAS_SIGTRAMP_PAGE) { abi_long tramp_page = target_mmap(0, TARGET_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); @@ -3376,8 +3979,9 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) target_mprotect(tramp_page, TARGET_PAGE_SIZE, PROT_READ | PROT_EXEC); } - bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &elf_ex, - info, (elf_interpreter ? &interp_info : NULL)); + bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &ehdr, info, + elf_interpreter ? &interp_info : NULL, + vdso ? &vdso_info : NULL); info->start_stack = bprm->p; /* If we have an interpreter, set that as the program's entry point. @@ -3394,21 +3998,12 @@ 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; } #ifdef USE_ELF_CORE_DUMP +#include "exec/translate-all.h" + /* * Definitions to generate Intel SVR4-like core files. * These mostly have the same names as the SVR4 types with "target_elf_" @@ -3448,18 +4043,6 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) * Example for ARM target is provided in this file. */ -/* An ELF note in memory */ -struct memelfnote { - const char *name; - size_t namesz; - size_t namesz_rounded; - int type; - size_t datasz; - size_t datasz_rounded; - void *data; - size_t notesz; -}; - struct target_elf_siginfo { abi_int si_signo; /* signal number */ abi_int si_code; /* extra code */ @@ -3499,77 +4082,6 @@ struct target_elf_prpsinfo { char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ }; -/* Here is the structure in which status of each thread is captured. */ -struct elf_thread_status { - QTAILQ_ENTRY(elf_thread_status) ets_link; - struct target_elf_prstatus prstatus; /* NT_PRSTATUS */ -#if 0 - elf_fpregset_t fpu; /* NT_PRFPREG */ - struct task_struct *thread; - elf_fpxregset_t xfpu; /* ELF_CORE_XFPREG_TYPE */ -#endif - struct memelfnote notes[1]; - int num_notes; -}; - -struct elf_note_info { - struct memelfnote *notes; - struct target_elf_prstatus *prstatus; /* NT_PRSTATUS */ - struct target_elf_prpsinfo *psinfo; /* NT_PRPSINFO */ - - QTAILQ_HEAD(, elf_thread_status) thread_list; -#if 0 - /* - * Current version of ELF coredump doesn't support - * dumping fp regs etc. - */ - elf_fpregset_t *fpu; - elf_fpxregset_t *xfpu; - int thread_status_size; -#endif - int notes_size; - int numnote; -}; - -struct vm_area_struct { - target_ulong vma_start; /* start vaddr of memory region */ - target_ulong vma_end; /* end vaddr of memory region */ - abi_ulong vma_flags; /* protection etc. flags for the region */ - QTAILQ_ENTRY(vm_area_struct) vma_link; -}; - -struct mm_struct { - QTAILQ_HEAD(, vm_area_struct) mm_mmap; - int mm_count; /* number of mappings */ -}; - -static struct mm_struct *vma_init(void); -static void vma_delete(struct mm_struct *); -static int vma_add_mapping(struct mm_struct *, target_ulong, - target_ulong, abi_ulong); -static int vma_get_mapping_count(const struct mm_struct *); -static struct vm_area_struct *vma_first(const struct mm_struct *); -static struct vm_area_struct *vma_next(struct vm_area_struct *); -static abi_ulong vma_dump_size(const struct vm_area_struct *); -static int vma_walker(void *priv, target_ulong start, target_ulong end, - unsigned long flags); - -static void fill_elf_header(struct elfhdr *, int, uint16_t, uint32_t); -static void fill_note(struct memelfnote *, const char *, int, - unsigned int, void *); -static void fill_prstatus(struct target_elf_prstatus *, const TaskState *, int); -static int fill_psinfo(struct target_elf_prpsinfo *, const TaskState *); -static void fill_auxv_note(struct memelfnote *, const TaskState *); -static void fill_elf_note_phdr(struct elf_phdr *, int, off_t); -static size_t note_size(const struct memelfnote *); -static void free_note_info(struct elf_note_info *); -static int fill_note_info(struct elf_note_info *, long, const CPUArchState *); -static void fill_thread_info(struct elf_note_info *, const CPUArchState *); - -static int dump_write(int, const void *, size_t); -static int write_note(struct memelfnote *, int); -static int write_note_info(struct elf_note_info *, int); - #ifdef BSWAP_NEEDED static void bswap_prstatus(struct target_elf_prstatus *prstatus) { @@ -3611,146 +4123,67 @@ static inline void bswap_psinfo(struct target_elf_prpsinfo *p) {} static inline void bswap_note(struct elf_note *en) { } #endif /* BSWAP_NEEDED */ -/* - * Minimal support for linux memory regions. These are needed - * when we are finding out what memory exactly belongs to - * emulated process. No locks needed here, as long as - * thread that received the signal is stopped. - */ - -static struct mm_struct *vma_init(void) -{ - struct mm_struct *mm; - - if ((mm = g_malloc(sizeof (*mm))) == NULL) - return (NULL); - - mm->mm_count = 0; - QTAILQ_INIT(&mm->mm_mmap); - - return (mm); -} - -static void vma_delete(struct mm_struct *mm) -{ - struct vm_area_struct *vma; - - while ((vma = vma_first(mm)) != NULL) { - QTAILQ_REMOVE(&mm->mm_mmap, vma, vma_link); - g_free(vma); - } - g_free(mm); -} - -static int vma_add_mapping(struct mm_struct *mm, target_ulong start, - target_ulong end, abi_ulong flags) -{ - struct vm_area_struct *vma; - - if ((vma = g_malloc0(sizeof (*vma))) == NULL) - return (-1); - - vma->vma_start = start; - vma->vma_end = end; - vma->vma_flags = flags; - - QTAILQ_INSERT_TAIL(&mm->mm_mmap, vma, vma_link); - mm->mm_count++; - - return (0); -} - -static struct vm_area_struct *vma_first(const struct mm_struct *mm) -{ - return (QTAILQ_FIRST(&mm->mm_mmap)); -} - -static struct vm_area_struct *vma_next(struct vm_area_struct *vma) -{ - return (QTAILQ_NEXT(vma, vma_link)); -} - -static int vma_get_mapping_count(const struct mm_struct *mm) -{ - return (mm->mm_count); -} - /* * Calculate file (dump) size of given memory region. */ -static abi_ulong vma_dump_size(const struct vm_area_struct *vma) +static size_t vma_dump_size(target_ulong start, target_ulong end, + unsigned long flags) { - /* if we cannot even read the first page, skip it */ - if (!access_ok_untagged(VERIFY_READ, vma->vma_start, TARGET_PAGE_SIZE)) - return (0); + /* The area must be readable. */ + if (!(flags & PAGE_READ)) { + return 0; + } /* * Usually we don't dump executable pages as they contain * non-writable code that debugger can read directly from - * target library etc. However, thread stacks are marked - * also executable so we read in first page of given region - * and check whether it contains elf header. If there is - * no elf header, we dump it. + * target library etc. If there is no elf header, we dump it. */ - if (vma->vma_flags & PROT_EXEC) { - char page[TARGET_PAGE_SIZE]; - - if (copy_from_user(page, vma->vma_start, sizeof (page))) { - return 0; - } - if ((page[EI_MAG0] == ELFMAG0) && - (page[EI_MAG1] == ELFMAG1) && - (page[EI_MAG2] == ELFMAG2) && - (page[EI_MAG3] == ELFMAG3)) { - /* - * Mappings are possibly from ELF binary. Don't dump - * them. - */ - return (0); - } + if (!(flags & PAGE_WRITE_ORG) && + (flags & PAGE_EXEC) && + memcmp(g2h_untagged(start), ELFMAG, SELFMAG) == 0) { + return 0; } - return (vma->vma_end - vma->vma_start); + return end - start; } -static int vma_walker(void *priv, target_ulong start, target_ulong end, - unsigned long flags) +static size_t size_note(const char *name, size_t datasz) { - struct mm_struct *mm = (struct mm_struct *)priv; + size_t namesz = strlen(name) + 1; - vma_add_mapping(mm, start, end, flags); - return (0); + namesz = ROUND_UP(namesz, 4); + datasz = ROUND_UP(datasz, 4); + + return sizeof(struct elf_note) + namesz + datasz; } -static void fill_note(struct memelfnote *note, const char *name, int type, - unsigned int sz, void *data) +static void *fill_note(void **pptr, int type, const char *name, size_t datasz) { - unsigned int namesz; + void *ptr = *pptr; + struct elf_note *n = ptr; + size_t namesz = strlen(name) + 1; - namesz = strlen(name) + 1; - note->name = name; - note->namesz = namesz; - note->namesz_rounded = roundup(namesz, sizeof (int32_t)); - note->type = type; - note->datasz = sz; - note->datasz_rounded = roundup(sz, sizeof (int32_t)); + n->n_namesz = namesz; + n->n_descsz = datasz; + n->n_type = type; + bswap_note(n); - note->data = data; + ptr += sizeof(*n); + memcpy(ptr, name, namesz); - /* - * We calculate rounded up note size here as specified by - * ELF document. - */ - note->notesz = sizeof (struct elf_note) + - note->namesz_rounded + note->datasz_rounded; + namesz = ROUND_UP(namesz, 4); + datasz = ROUND_UP(datasz, 4); + + *pptr = ptr + namesz + datasz; + return ptr + namesz; } static void fill_elf_header(struct elfhdr *elf, int segs, uint16_t machine, uint32_t flags) { - (void) memset(elf, 0, sizeof(*elf)); + memcpy(elf->e_ident, ELFMAG, SELFMAG); - (void) memcpy(elf->e_ident, ELFMAG, SELFMAG); elf->e_ident[EI_CLASS] = ELF_CLASS; elf->e_ident[EI_DATA] = ELF_DATA; elf->e_ident[EI_VERSION] = EV_CURRENT; @@ -3768,95 +4201,79 @@ static void fill_elf_header(struct elfhdr *elf, int segs, uint16_t machine, bswap_ehdr(elf); } -static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, off_t offset) +static void fill_elf_note_phdr(struct elf_phdr *phdr, size_t sz, off_t offset) { phdr->p_type = PT_NOTE; phdr->p_offset = offset; - phdr->p_vaddr = 0; - phdr->p_paddr = 0; phdr->p_filesz = sz; - phdr->p_memsz = 0; - phdr->p_flags = 0; - phdr->p_align = 0; bswap_phdr(phdr, 1); } -static size_t note_size(const struct memelfnote *note) +static void fill_prstatus_note(void *data, const TaskState *ts, + CPUState *cpu, int signr) { - return (note->notesz); + /* + * Because note memory is only aligned to 4, and target_elf_prstatus + * may well have higher alignment requirements, fill locally and + * memcpy to the destination afterward. + */ + struct target_elf_prstatus prstatus = { + .pr_info.si_signo = signr, + .pr_cursig = signr, + .pr_pid = ts->ts_tid, + .pr_ppid = getppid(), + .pr_pgrp = getpgrp(), + .pr_sid = getsid(0), + }; + + elf_core_copy_regs(&prstatus.pr_reg, cpu_env(cpu)); + bswap_prstatus(&prstatus); + memcpy(data, &prstatus, sizeof(prstatus)); } -static void fill_prstatus(struct target_elf_prstatus *prstatus, - const TaskState *ts, int signr) -{ - (void) memset(prstatus, 0, sizeof (*prstatus)); - prstatus->pr_info.si_signo = prstatus->pr_cursig = signr; - prstatus->pr_pid = ts->ts_tid; - prstatus->pr_ppid = getppid(); - prstatus->pr_pgrp = getpgrp(); - prstatus->pr_sid = getsid(0); - - bswap_prstatus(prstatus); -} - -static int fill_psinfo(struct target_elf_prpsinfo *psinfo, const TaskState *ts) +static void fill_prpsinfo_note(void *data, const TaskState *ts) { + /* + * Because note memory is only aligned to 4, and target_elf_prpsinfo + * may well have higher alignment requirements, fill locally and + * memcpy to the destination afterward. + */ + struct target_elf_prpsinfo psinfo = { + .pr_pid = getpid(), + .pr_ppid = getppid(), + .pr_pgrp = getpgrp(), + .pr_sid = getsid(0), + .pr_uid = getuid(), + .pr_gid = getgid(), + }; char *base_filename; - unsigned int i, len; - - (void) memset(psinfo, 0, sizeof (*psinfo)); + size_t len; len = ts->info->env_strings - ts->info->arg_strings; - if (len >= ELF_PRARGSZ) - len = ELF_PRARGSZ - 1; - if (copy_from_user(&psinfo->pr_psargs, ts->info->arg_strings, len)) { - return -EFAULT; + len = MIN(len, ELF_PRARGSZ); + memcpy(&psinfo.pr_psargs, g2h_untagged(ts->info->arg_strings), len); + for (size_t i = 0; i < len; i++) { + if (psinfo.pr_psargs[i] == 0) { + psinfo.pr_psargs[i] = ' '; + } } - for (i = 0; i < len; i++) - if (psinfo->pr_psargs[i] == 0) - psinfo->pr_psargs[i] = ' '; - psinfo->pr_psargs[len] = 0; - - psinfo->pr_pid = getpid(); - psinfo->pr_ppid = getppid(); - psinfo->pr_pgrp = getpgrp(); - psinfo->pr_sid = getsid(0); - psinfo->pr_uid = getuid(); - psinfo->pr_gid = getgid(); base_filename = g_path_get_basename(ts->bprm->filename); /* * Using strncpy here is fine: at max-length, * this field is not NUL-terminated. */ - (void) strncpy(psinfo->pr_fname, base_filename, - sizeof(psinfo->pr_fname)); - + strncpy(psinfo.pr_fname, base_filename, sizeof(psinfo.pr_fname)); g_free(base_filename); - bswap_psinfo(psinfo); - return (0); + + bswap_psinfo(&psinfo); + memcpy(data, &psinfo, sizeof(psinfo)); } -static void fill_auxv_note(struct memelfnote *note, const TaskState *ts) +static void fill_auxv_note(void *data, const TaskState *ts) { - elf_addr_t auxv = (elf_addr_t)ts->info->saved_auxv; - elf_addr_t orig_auxv = auxv; - void *ptr; - int len = ts->info->auxv_len; - - /* - * Auxiliary vector is stored in target process stack. It contains - * {type, value} pairs that we need to dump into note. This is not - * strictly necessary but we do it here for sake of completeness. - */ - - /* read in whole auxv vector and copy it to memelfnote */ - ptr = lock_user(VERIFY_READ, orig_auxv, len, 0); - if (ptr != NULL) { - fill_note(note, "CORE", NT_AUXV, len, ptr); - unlock_user(ptr, auxv, len); - } + memcpy(data, g2h_untagged(ts->info->saved_auxv), ts->info->auxv_len); } /* @@ -3880,27 +4297,9 @@ static int dump_write(int fd, const void *ptr, size_t size) { const char *bufp = (const char *)ptr; ssize_t bytes_written, bytes_left; - struct rlimit dumpsize; - off_t pos; bytes_written = 0; - getrlimit(RLIMIT_CORE, &dumpsize); - if ((pos = lseek(fd, 0, SEEK_CUR))==-1) { - if (errno == ESPIPE) { /* not a seekable stream */ - bytes_left = size; - } else { - return pos; - } - } else { - if (dumpsize.rlim_cur <= pos) { - return -1; - } else if (dumpsize.rlim_cur == RLIM_INFINITY) { - bytes_left = size; - } else { - size_t limit_left=dumpsize.rlim_cur - pos; - bytes_left = limit_left >= size ? size : limit_left ; - } - } + bytes_left = size; /* * In normal conditions, single write(2) should do but @@ -3922,135 +4321,76 @@ static int dump_write(int fd, const void *ptr, size_t size) return (0); } -static int write_note(struct memelfnote *men, int fd) +static int wmr_page_unprotect_regions(void *opaque, target_ulong start, + target_ulong end, unsigned long flags) { - struct elf_note en; + if ((flags & (PAGE_WRITE | PAGE_WRITE_ORG)) == PAGE_WRITE_ORG) { + size_t step = MAX(TARGET_PAGE_SIZE, qemu_real_host_page_size()); - en.n_namesz = men->namesz; - en.n_type = men->type; - en.n_descsz = men->datasz; - - bswap_note(&en); - - if (dump_write(fd, &en, sizeof(en)) != 0) - return (-1); - if (dump_write(fd, men->name, men->namesz_rounded) != 0) - return (-1); - if (dump_write(fd, men->data, men->datasz_rounded) != 0) - return (-1); - - return (0); -} - -static void fill_thread_info(struct elf_note_info *info, const CPUArchState *env) -{ - CPUState *cpu = env_cpu((CPUArchState *)env); - TaskState *ts = (TaskState *)cpu->opaque; - struct elf_thread_status *ets; - - ets = g_malloc0(sizeof (*ets)); - ets->num_notes = 1; /* only prstatus is dumped */ - fill_prstatus(&ets->prstatus, ts, 0); - elf_core_copy_regs(&ets->prstatus.pr_reg, env); - fill_note(&ets->notes[0], "CORE", NT_PRSTATUS, sizeof (ets->prstatus), - &ets->prstatus); - - QTAILQ_INSERT_TAIL(&info->thread_list, ets, ets_link); - - info->notes_size += note_size(&ets->notes[0]); -} - -static void init_note_info(struct elf_note_info *info) -{ - /* Initialize the elf_note_info structure so that it is at - * least safe to call free_note_info() on it. Must be - * called before calling fill_note_info(). - */ - memset(info, 0, sizeof (*info)); - QTAILQ_INIT(&info->thread_list); -} - -static int fill_note_info(struct elf_note_info *info, - long signr, const CPUArchState *env) -{ -#define NUMNOTES 3 - CPUState *cpu = env_cpu((CPUArchState *)env); - TaskState *ts = (TaskState *)cpu->opaque; - int i; - - info->notes = g_new0(struct memelfnote, NUMNOTES); - if (info->notes == NULL) - return (-ENOMEM); - info->prstatus = g_malloc0(sizeof (*info->prstatus)); - if (info->prstatus == NULL) - return (-ENOMEM); - info->psinfo = g_malloc0(sizeof (*info->psinfo)); - if (info->prstatus == NULL) - return (-ENOMEM); - - /* - * First fill in status (and registers) of current thread - * including process info & aux vector. - */ - fill_prstatus(info->prstatus, ts, signr); - elf_core_copy_regs(&info->prstatus->pr_reg, env); - fill_note(&info->notes[0], "CORE", NT_PRSTATUS, - sizeof (*info->prstatus), info->prstatus); - fill_psinfo(info->psinfo, ts); - fill_note(&info->notes[1], "CORE", NT_PRPSINFO, - sizeof (*info->psinfo), info->psinfo); - fill_auxv_note(&info->notes[2], ts); - info->numnote = 3; - - info->notes_size = 0; - for (i = 0; i < info->numnote; i++) - info->notes_size += note_size(&info->notes[i]); - - /* read and fill status of all threads */ - cpu_list_lock(); - CPU_FOREACH(cpu) { - if (cpu == thread_cpu) { - continue; + while (1) { + page_unprotect(start, 0); + if (end - start <= step) { + break; + } + start += step; } - fill_thread_info(info, cpu->env_ptr); } - cpu_list_unlock(); - - return (0); + return 0; } -static void free_note_info(struct elf_note_info *info) +typedef struct { + unsigned count; + size_t size; +} CountAndSizeRegions; + +static int wmr_count_and_size_regions(void *opaque, target_ulong start, + target_ulong end, unsigned long flags) { - struct elf_thread_status *ets; + CountAndSizeRegions *css = opaque; - while (!QTAILQ_EMPTY(&info->thread_list)) { - ets = QTAILQ_FIRST(&info->thread_list); - QTAILQ_REMOVE(&info->thread_list, ets, ets_link); - g_free(ets); - } - - g_free(info->prstatus); - g_free(info->psinfo); - g_free(info->notes); + css->count++; + css->size += vma_dump_size(start, end, flags); + return 0; } -static int write_note_info(struct elf_note_info *info, int fd) +typedef struct { + struct elf_phdr *phdr; + off_t offset; +} FillRegionPhdr; + +static int wmr_fill_region_phdr(void *opaque, target_ulong start, + target_ulong end, unsigned long flags) { - struct elf_thread_status *ets; - int i, error = 0; + FillRegionPhdr *d = opaque; + struct elf_phdr *phdr = d->phdr; - /* write prstatus, psinfo and auxv for current thread */ - for (i = 0; i < info->numnote; i++) - if ((error = write_note(&info->notes[i], fd)) != 0) - return (error); + phdr->p_type = PT_LOAD; + phdr->p_vaddr = start; + phdr->p_paddr = 0; + phdr->p_filesz = vma_dump_size(start, end, flags); + phdr->p_offset = d->offset; + d->offset += phdr->p_filesz; + phdr->p_memsz = end - start; + phdr->p_flags = (flags & PAGE_READ ? PF_R : 0) + | (flags & PAGE_WRITE_ORG ? PF_W : 0) + | (flags & PAGE_EXEC ? PF_X : 0); + phdr->p_align = ELF_EXEC_PAGESIZE; - /* write prstatus for each thread */ - QTAILQ_FOREACH(ets, &info->thread_list, ets_link) { - if ((error = write_note(&ets->notes[0], fd)) != 0) - return (error); + bswap_phdr(phdr, 1); + d->phdr = phdr + 1; + return 0; +} + +static int wmr_write_region(void *opaque, target_ulong start, + target_ulong end, unsigned long flags) +{ + int fd = *(int *)opaque; + size_t size = vma_dump_size(start, end, flags); + + if (!size) { + return 0; } - - return (0); + return dump_write(fd, g2h_untagged(start), size); } /* @@ -4099,147 +4439,128 @@ static int write_note_info(struct elf_note_info *info, int fd) static int elf_core_dump(int signr, const CPUArchState *env) { const CPUState *cpu = env_cpu((CPUArchState *)env); - const TaskState *ts = (const TaskState *)cpu->opaque; - struct vm_area_struct *vma = NULL; - g_autofree char *corefile = NULL; - struct elf_note_info info; - struct elfhdr elf; - struct elf_phdr phdr; + const TaskState *ts = (const TaskState *)get_task_state((CPUState *)cpu); struct rlimit dumpsize; - struct mm_struct *mm = NULL; - off_t offset = 0, data_offset = 0; - int segs = 0; + CountAndSizeRegions css; + off_t offset, note_offset, data_offset; + size_t note_size; + int cpus, ret; int fd = -1; + CPUState *cpu_iter; - init_note_info(&info); - - errno = 0; - getrlimit(RLIMIT_CORE, &dumpsize); - if (dumpsize.rlim_cur == 0) + if (prctl(PR_GET_DUMPABLE) == 0) { return 0; + } - corefile = core_dump_filename(ts); + if (getrlimit(RLIMIT_CORE, &dumpsize) < 0 || dumpsize.rlim_cur == 0) { + return 0; + } - if ((fd = open(corefile, O_WRONLY | O_CREAT, - S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) - return (-errno); + cpu_list_lock(); + mmap_lock(); + + /* By unprotecting, we merge vmas that might be split. */ + walk_memory_regions(NULL, wmr_page_unprotect_regions); /* * Walk through target process memory mappings and - * set up structure containing this information. After - * this point vma_xxx functions can be used. + * set up structure containing this information. */ - if ((mm = vma_init()) == NULL) - goto out; + memset(&css, 0, sizeof(css)); + walk_memory_regions(&css, wmr_count_and_size_regions); - walk_memory_regions(mm, vma_walker); - segs = vma_get_mapping_count(mm); + cpus = 0; + CPU_FOREACH(cpu_iter) { + cpus++; + } + + offset = sizeof(struct elfhdr); + offset += (css.count + 1) * sizeof(struct elf_phdr); + note_offset = offset; + + offset += size_note("CORE", ts->info->auxv_len); + offset += size_note("CORE", sizeof(struct target_elf_prpsinfo)); + offset += size_note("CORE", sizeof(struct target_elf_prstatus)) * cpus; + note_size = offset - note_offset; + data_offset = ROUND_UP(offset, ELF_EXEC_PAGESIZE); + + /* Do not dump if the corefile size exceeds the limit. */ + if (dumpsize.rlim_cur != RLIM_INFINITY + && dumpsize.rlim_cur < data_offset + css.size) { + errno = 0; + goto out; + } + + { + g_autofree char *corefile = core_dump_filename(ts); + fd = open(corefile, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + } + if (fd < 0) { + goto out; + } /* - * Construct valid coredump ELF header. We also - * add one more segment for notes. + * There is a fair amount of alignment padding within the notes + * as well as preceeding the process memory. Allocate a zeroed + * block to hold it all. Write all of the headers directly into + * this buffer and then write it out as a block. */ - fill_elf_header(&elf, segs + 1, ELF_MACHINE, 0); - if (dump_write(fd, &elf, sizeof (elf)) != 0) - goto out; + { + g_autofree void *header = g_malloc0(data_offset); + FillRegionPhdr frp; + void *hptr, *dptr; - /* fill in the in-memory version of notes */ - if (fill_note_info(&info, signr, env) < 0) - goto out; + /* Create elf file header. */ + hptr = header; + fill_elf_header(hptr, css.count + 1, ELF_MACHINE, 0); + hptr += sizeof(struct elfhdr); - offset += sizeof (elf); /* elf header */ - offset += (segs + 1) * sizeof (struct elf_phdr); /* program headers */ + /* Create elf program headers. */ + fill_elf_note_phdr(hptr, note_size, note_offset); + hptr += sizeof(struct elf_phdr); - /* write out notes program header */ - fill_elf_note_phdr(&phdr, info.notes_size, offset); + frp.phdr = hptr; + frp.offset = data_offset; + walk_memory_regions(&frp, wmr_fill_region_phdr); + hptr = frp.phdr; - offset += info.notes_size; - if (dump_write(fd, &phdr, sizeof (phdr)) != 0) - goto out; + /* Create the notes. */ + dptr = fill_note(&hptr, NT_AUXV, "CORE", ts->info->auxv_len); + fill_auxv_note(dptr, ts); - /* - * ELF specification wants data to start at page boundary so - * we align it here. - */ - data_offset = offset = roundup(offset, ELF_EXEC_PAGESIZE); + dptr = fill_note(&hptr, NT_PRPSINFO, "CORE", + sizeof(struct target_elf_prpsinfo)); + fill_prpsinfo_note(dptr, ts); - /* - * Write program headers for memory regions mapped in - * the target process. - */ - for (vma = vma_first(mm); vma != NULL; vma = vma_next(vma)) { - (void) memset(&phdr, 0, sizeof (phdr)); + CPU_FOREACH(cpu_iter) { + dptr = fill_note(&hptr, NT_PRSTATUS, "CORE", + sizeof(struct target_elf_prstatus)); + fill_prstatus_note(dptr, ts, cpu_iter, + cpu_iter == cpu ? signr : 0); + } - phdr.p_type = PT_LOAD; - phdr.p_offset = offset; - phdr.p_vaddr = vma->vma_start; - phdr.p_paddr = 0; - phdr.p_filesz = vma_dump_size(vma); - offset += phdr.p_filesz; - phdr.p_memsz = vma->vma_end - vma->vma_start; - phdr.p_flags = vma->vma_flags & PROT_READ ? PF_R : 0; - if (vma->vma_flags & PROT_WRITE) - phdr.p_flags |= PF_W; - if (vma->vma_flags & PROT_EXEC) - phdr.p_flags |= PF_X; - phdr.p_align = ELF_EXEC_PAGESIZE; - - bswap_phdr(&phdr, 1); - if (dump_write(fd, &phdr, sizeof(phdr)) != 0) { + if (dump_write(fd, header, data_offset) < 0) { goto out; } } /* - * Next we write notes just after program headers. No - * alignment needed here. + * Finally write process memory into the corefile as well. */ - if (write_note_info(&info, fd) < 0) + if (walk_memory_regions(&fd, wmr_write_region) < 0) { goto out; - - /* align data to page boundary */ - if (lseek(fd, data_offset, SEEK_SET) != data_offset) - goto out; - - /* - * Finally we can dump process memory into corefile as well. - */ - for (vma = vma_first(mm); vma != NULL; vma = vma_next(vma)) { - abi_ulong addr; - abi_ulong end; - - end = vma->vma_start + vma_dump_size(vma); - - for (addr = vma->vma_start; addr < end; - addr += TARGET_PAGE_SIZE) { - char page[TARGET_PAGE_SIZE]; - int error; - - /* - * Read in page from target process memory and - * write it to coredump file. - */ - error = copy_from_user(page, addr, sizeof (page)); - if (error != 0) { - (void) fprintf(stderr, "unable to dump " TARGET_ABI_FMT_lx "\n", - addr); - errno = -error; - goto out; - } - if (dump_write(fd, page, TARGET_PAGE_SIZE) < 0) - goto out; - } } + errno = 0; out: - free_note_info(&info); - if (mm != NULL) - vma_delete(mm); - (void) close(fd); - - if (errno != 0) - return (-errno); - return (0); + ret = -errno; + mmap_unlock(); + cpu_list_unlock(); + if (fd >= 0) { + close(fd); + } + return ret; } #endif /* USE_ELF_CORE_DUMP */ diff --git a/linux-user/exit.c b/linux-user/exit.c index fa6ef0b9b4..1ff8fe4f07 100644 --- a/linux-user/exit.c +++ b/linux-user/exit.c @@ -17,12 +17,11 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" -#include "exec/gdbstub.h" +#include "tcg/perf.h" +#include "gdbstub/syscalls.h" #include "qemu.h" #include "user-internals.h" -#ifdef CONFIG_GPROF -#include -#endif +#include "qemu/plugin.h" #ifdef CONFIG_GCOV extern void __gcov_dump(void); @@ -30,12 +29,10 @@ extern void __gcov_dump(void); void preexit_cleanup(CPUArchState *env, int code) { -#ifdef CONFIG_GPROF - _mcleanup(); -#endif #ifdef CONFIG_GCOV __gcov_dump(); #endif gdb_exit(code); qemu_plugin_user_exit(); + perf_exit(); } diff --git a/linux-user/fd-trans.c b/linux-user/fd-trans.c index 7b25468d02..c04a97c73a 100644 --- a/linux-user/fd-trans.c +++ b/linux-user/fd-trans.c @@ -1284,6 +1284,49 @@ static inline abi_long host_to_target_nlmsg_route(struct nlmsghdr *nlh, return host_to_target_for_each_nlmsg(nlh, len, host_to_target_data_route); } +static abi_long target_to_host_for_each_nlattr(struct nlattr *nlattr, + size_t len, + abi_long (*target_to_host_nlattr) + (struct nlattr *)) +{ + unsigned short aligned_nla_len; + abi_long ret; + + while (len > sizeof(struct nlattr)) { + if (tswap16(nlattr->nla_len) < sizeof(struct rtattr) || + tswap16(nlattr->nla_len) > len) { + break; + } + nlattr->nla_len = tswap16(nlattr->nla_len); + nlattr->nla_type = tswap16(nlattr->nla_type); + ret = target_to_host_nlattr(nlattr); + if (ret < 0) { + return ret; + } + + aligned_nla_len = NLA_ALIGN(nlattr->nla_len); + if (aligned_nla_len >= len) { + break; + } + len -= aligned_nla_len; + nlattr = (struct nlattr *)(((char *)nlattr) + aligned_nla_len); + } + return 0; +} + +static abi_long target_to_host_data_inet6_nlattr(struct nlattr *nlattr) +{ + switch (nlattr->nla_type) { + /* uint8_t */ + case QEMU_IFLA_INET6_ADDR_GEN_MODE: + break; + default: + qemu_log_mask(LOG_UNIMP, "Unknown target AF_INET6 type: %d\n", + nlattr->nla_type); + } + return 0; +} + static abi_long target_to_host_for_each_rtattr(struct rtattr *rtattr, size_t len, abi_long (*target_to_host_rtattr) @@ -1314,16 +1357,35 @@ static abi_long target_to_host_for_each_rtattr(struct rtattr *rtattr, return 0; } +static abi_long target_to_host_data_spec_nlattr(struct nlattr *nlattr) +{ + switch (nlattr->nla_type & NLA_TYPE_MASK) { + case AF_INET6: + return target_to_host_for_each_nlattr(NLA_DATA(nlattr), nlattr->nla_len, + target_to_host_data_inet6_nlattr); + default: + qemu_log_mask(LOG_UNIMP, "Unknown target AF_SPEC type: %d\n", + nlattr->nla_type); + break; + } + return 0; +} + static abi_long target_to_host_data_link_rtattr(struct rtattr *rtattr) { uint32_t *u32; - switch (rtattr->rta_type) { + switch (rtattr->rta_type & NLA_TYPE_MASK) { /* uint32_t */ + case QEMU_IFLA_MTU: + case QEMU_IFLA_TXQLEN: case QEMU_IFLA_EXT_MASK: u32 = RTA_DATA(rtattr); *u32 = tswap32(*u32); break; + case QEMU_IFLA_AF_SPEC: + return target_to_host_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, + target_to_host_data_spec_nlattr); default: qemu_log_mask(LOG_UNIMP, "Unknown target QEMU_IFLA type: %d\n", rtattr->rta_type); @@ -1622,7 +1684,7 @@ TargetFdTrans target_signalfd_trans = { .host_to_target_data = host_to_target_data_signalfd, }; -static abi_long swap_data_eventfd(void *buf, size_t len) +static abi_long swap_data_u64(void *buf, size_t len) { uint64_t *counter = buf; int i; @@ -1640,8 +1702,12 @@ static abi_long swap_data_eventfd(void *buf, size_t len) } TargetFdTrans target_eventfd_trans = { - .host_to_target_data = swap_data_eventfd, - .target_to_host_data = swap_data_eventfd, + .host_to_target_data = swap_data_u64, + .target_to_host_data = swap_data_u64, +}; + +TargetFdTrans target_timerfd_trans = { + .host_to_target_data = swap_data_u64, }; #if defined(CONFIG_INOTIFY) && (defined(TARGET_NR_inotify_init) || \ diff --git a/linux-user/fd-trans.h b/linux-user/fd-trans.h index 1b9fa2041c..910faaf237 100644 --- a/linux-user/fd-trans.h +++ b/linux-user/fd-trans.h @@ -130,6 +130,7 @@ extern TargetFdTrans target_netlink_route_trans; extern TargetFdTrans target_netlink_audit_trans; extern TargetFdTrans target_signalfd_trans; extern TargetFdTrans target_eventfd_trans; +extern TargetFdTrans target_timerfd_trans; #if (defined(TARGET_NR_inotify_init) && defined(__NR_inotify_init)) || \ (defined(CONFIG_INOTIFY1) && defined(TARGET_NR_inotify_init1) && \ defined(__NR_inotify_init1)) diff --git a/linux-user/flatload.c b/linux-user/flatload.c index e4c2f89a22..5b62aa0a2b 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -448,7 +448,7 @@ static int load_flat_file(struct linux_binprm * bprm, * Allocate the address space. */ probe_guest_base(bprm->filename, 0, - text_len + data_len + extra + indx_len); + text_len + data_len + extra + indx_len - 1); /* * there are a couple of cases here, the separate code/data @@ -463,7 +463,7 @@ static int load_flat_file(struct linux_binprm * bprm, DBG_FLT("BINFMT_FLAT: ROM mapping of file (we hope)\n"); textpos = target_mmap(0, text_len, PROT_READ|PROT_EXEC, - MAP_PRIVATE, bprm->fd, 0); + MAP_PRIVATE, bprm->src.fd, 0); if (textpos == -1) { fprintf(stderr, "Unable to mmap process text\n"); return -1; @@ -490,7 +490,7 @@ static int load_flat_file(struct linux_binprm * bprm, } else #endif { - result = target_pread(bprm->fd, datapos, + result = target_pread(bprm->src.fd, datapos, data_len + (relocs * sizeof(abi_ulong)), fpos); } @@ -540,10 +540,10 @@ static int load_flat_file(struct linux_binprm * bprm, else #endif { - result = target_pread(bprm->fd, textpos, + result = target_pread(bprm->src.fd, textpos, text_len, 0); if (result >= 0) { - result = target_pread(bprm->fd, datapos, + result = target_pread(bprm->src.fd, datapos, data_len + (relocs * sizeof(abi_ulong)), ntohl(hdr->data_start)); } @@ -755,15 +755,15 @@ int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) /* Update data segment pointers for all libraries */ for (i=0; ienvc + bprm->argc + 2; - stack_len += flat_argvp_envp_on_stack() ? 2 : 0; /* arvg, argp */ + stack_len += flat_argvp_envp_on_stack() ? 2 : 0; /* argv, argp */ stack_len += 1; /* argc */ stack_len *= sizeof(abi_ulong); sp -= (sp - stack_len) & 15; @@ -808,10 +808,10 @@ int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) /* Stash our initial stack pointer into the mm structure */ info->start_code = libinfo[0].start_code; - info->end_code = libinfo[0].start_code = libinfo[0].text_len; + info->end_code = libinfo[0].start_code + libinfo[0].text_len; info->start_data = libinfo[0].start_data; info->end_data = libinfo[0].end_data; - info->start_brk = libinfo[0].start_brk; + info->brk = libinfo[0].start_brk; info->start_stack = sp; info->stack_limit = libinfo[0].start_brk; info->entry = start_addr; diff --git a/linux-user/gen-vdso-elfn.c.inc b/linux-user/gen-vdso-elfn.c.inc new file mode 100644 index 0000000000..95856eb839 --- /dev/null +++ b/linux-user/gen-vdso-elfn.c.inc @@ -0,0 +1,314 @@ +/* + * Post-process a vdso elf image for inclusion into qemu. + * Elf size specialization. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +static void elfN(bswap_ehdr)(ElfN(Ehdr) *ehdr) +{ + bswaps(&ehdr->e_type); /* Object file type */ + bswaps(&ehdr->e_machine); /* Architecture */ + bswaps(&ehdr->e_version); /* Object file version */ + bswaps(&ehdr->e_entry); /* Entry point virtual address */ + bswaps(&ehdr->e_phoff); /* Program header table file offset */ + bswaps(&ehdr->e_shoff); /* Section header table file offset */ + bswaps(&ehdr->e_flags); /* Processor-specific flags */ + bswaps(&ehdr->e_ehsize); /* ELF header size in bytes */ + bswaps(&ehdr->e_phentsize); /* Program header table entry size */ + bswaps(&ehdr->e_phnum); /* Program header table entry count */ + bswaps(&ehdr->e_shentsize); /* Section header table entry size */ + bswaps(&ehdr->e_shnum); /* Section header table entry count */ + bswaps(&ehdr->e_shstrndx); /* Section header string table index */ +} + +static void elfN(bswap_phdr)(ElfN(Phdr) *phdr) +{ + bswaps(&phdr->p_type); /* Segment type */ + bswaps(&phdr->p_flags); /* Segment flags */ + bswaps(&phdr->p_offset); /* Segment file offset */ + bswaps(&phdr->p_vaddr); /* Segment virtual address */ + bswaps(&phdr->p_paddr); /* Segment physical address */ + bswaps(&phdr->p_filesz); /* Segment size in file */ + bswaps(&phdr->p_memsz); /* Segment size in memory */ + bswaps(&phdr->p_align); /* Segment alignment */ +} + +static void elfN(bswap_shdr)(ElfN(Shdr) *shdr) +{ + bswaps(&shdr->sh_name); + bswaps(&shdr->sh_type); + bswaps(&shdr->sh_flags); + bswaps(&shdr->sh_addr); + bswaps(&shdr->sh_offset); + bswaps(&shdr->sh_size); + bswaps(&shdr->sh_link); + bswaps(&shdr->sh_info); + bswaps(&shdr->sh_addralign); + bswaps(&shdr->sh_entsize); +} + +static void elfN(bswap_sym)(ElfN(Sym) *sym) +{ + bswaps(&sym->st_name); + bswaps(&sym->st_value); + bswaps(&sym->st_size); + bswaps(&sym->st_shndx); +} + +static void elfN(bswap_dyn)(ElfN(Dyn) *dyn) +{ + bswaps(&dyn->d_tag); /* Dynamic type tag */ + bswaps(&dyn->d_un.d_ptr); /* Dynamic ptr or val, in union */ +} + +static void elfN(search_symtab)(ElfN(Shdr) *shdr, unsigned sym_idx, + void *buf, bool need_bswap) +{ + unsigned str_idx = shdr[sym_idx].sh_link; + ElfN(Sym) *sym = buf + shdr[sym_idx].sh_offset; + unsigned sym_n = shdr[sym_idx].sh_size / sizeof(*sym); + const char *str = buf + shdr[str_idx].sh_offset; + + for (unsigned i = 0; i < sym_n; ++i) { + const char *name; + + if (need_bswap) { + elfN(bswap_sym)(sym + i); + } + name = str + sym[i].st_name; + + if (sigreturn_sym && strcmp(sigreturn_sym, name) == 0) { + sigreturn_addr = sym[i].st_value; + } + if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) { + rt_sigreturn_addr = sym[i].st_value; + } + } +} + +static void elfN(process)(FILE *outf, void *buf, bool need_bswap) +{ + ElfN(Ehdr) *ehdr = buf; + ElfN(Phdr) *phdr; + ElfN(Shdr) *shdr; + unsigned phnum, shnum; + unsigned dynamic_ofs = 0; + unsigned dynamic_addr = 0; + unsigned symtab_idx = 0; + unsigned dynsym_idx = 0; + unsigned first_segsz = 0; + int errors = 0; + + if (need_bswap) { + elfN(bswap_ehdr)(ehdr); + } + + phnum = ehdr->e_phnum; + phdr = buf + ehdr->e_phoff; + if (need_bswap) { + for (unsigned i = 0; i < phnum; ++i) { + elfN(bswap_phdr)(phdr + i); + } + } + + shnum = ehdr->e_shnum; + shdr = buf + ehdr->e_shoff; + if (need_bswap) { + for (unsigned i = 0; i < shnum; ++i) { + elfN(bswap_shdr)(shdr + i); + } + } + for (unsigned i = 0; i < shnum; ++i) { + switch (shdr[i].sh_type) { + case SHT_SYMTAB: + symtab_idx = i; + break; + case SHT_DYNSYM: + dynsym_idx = i; + break; + } + } + + /* + * Validate the VDSO is created as we expect: that PT_PHDR, + * PT_DYNAMIC, and PT_NOTE located in a writable data segment. + * PHDR and DYNAMIC require relocation, and NOTE will get the + * linux version number. + */ + for (unsigned i = 0; i < phnum; ++i) { + if (phdr[i].p_type != PT_LOAD) { + continue; + } + if (first_segsz != 0) { + fprintf(stderr, "Multiple LOAD segments\n"); + errors++; + } + if (phdr[i].p_offset != 0) { + fprintf(stderr, "LOAD segment does not cover EHDR\n"); + errors++; + } + if (phdr[i].p_vaddr != 0) { + fprintf(stderr, "LOAD segment not loaded at address 0\n"); + errors++; + } + first_segsz = phdr[i].p_filesz; + if (first_segsz < ehdr->e_phoff + phnum * sizeof(*phdr)) { + fprintf(stderr, "LOAD segment does not cover PHDRs\n"); + errors++; + } + if ((phdr[i].p_flags & (PF_R | PF_W)) != (PF_R | PF_W)) { + fprintf(stderr, "LOAD segment is not read-write\n"); + errors++; + } + } + for (unsigned i = 0; i < phnum; ++i) { + const char *which; + + switch (phdr[i].p_type) { + case PT_PHDR: + which = "PT_PHDR"; + break; + case PT_NOTE: + which = "PT_NOTE"; + break; + case PT_DYNAMIC: + dynamic_ofs = phdr[i].p_offset; + dynamic_addr = phdr[i].p_vaddr; + which = "PT_DYNAMIC"; + break; + default: + continue; + } + if (first_segsz < phdr[i].p_vaddr + phdr[i].p_filesz) { + fprintf(stderr, "LOAD segment does not cover %s\n", which); + errors++; + } + } + if (errors) { + exit(EXIT_FAILURE); + } + + /* Relocate the program headers. */ + for (unsigned i = 0; i < phnum; ++i) { + output_reloc(outf, buf, &phdr[i].p_vaddr); + output_reloc(outf, buf, &phdr[i].p_paddr); + } + + /* Relocate the DYNAMIC entries. */ + if (dynamic_addr) { + ElfN(Dyn) *dyn = buf + dynamic_ofs; + __typeof(dyn->d_tag) tag; + + do { + + if (need_bswap) { + elfN(bswap_dyn)(dyn); + } + tag = dyn->d_tag; + + switch (tag) { + case DT_HASH: + case DT_SYMTAB: + case DT_STRTAB: + case DT_VERDEF: + case DT_VERSYM: + case DT_PLTGOT: + case DT_ADDRRNGLO ... DT_ADDRRNGHI: + /* These entries store an address in the entry. */ + output_reloc(outf, buf, &dyn->d_un.d_val); + break; + + case DT_NULL: + case DT_STRSZ: + case DT_SONAME: + case DT_DEBUG: + case DT_FLAGS: + case DT_FLAGS_1: + case DT_SYMBOLIC: + case DT_BIND_NOW: + case DT_VERDEFNUM: + case DT_VALRNGLO ... DT_VALRNGHI: + /* These entries store an integer in the entry. */ + break; + + case DT_SYMENT: + if (dyn->d_un.d_val != sizeof(ElfN(Sym))) { + fprintf(stderr, "VDSO has incorrect dynamic symbol size\n"); + errors++; + } + break; + + case DT_REL: + case DT_RELSZ: + case DT_RELA: + case DT_RELASZ: + /* + * These entries indicate that the VDSO was built incorrectly. + * It should not have any real relocations. + * ??? The RISC-V toolchain will emit these even when there + * are no relocations. Validate zeros. + */ + if (dyn->d_un.d_val != 0) { + fprintf(stderr, "VDSO has dynamic relocations\n"); + errors++; + } + break; + case DT_RELENT: + case DT_RELAENT: + case DT_TEXTREL: + /* These entries store an integer in the entry. */ + /* Should not be required; see above. */ + break; + + case DT_NEEDED: + case DT_VERNEED: + case DT_PLTREL: + case DT_JMPREL: + case DT_RPATH: + case DT_RUNPATH: + fprintf(stderr, "VDSO has external dependencies\n"); + errors++; + break; + + case PT_LOPROC + 3: + if (ehdr->e_machine == EM_PPC64) { + break; /* DT_PPC64_OPT: integer bitmask */ + } + goto do_default; + + default: + do_default: + /* This is probably something target specific. */ + fprintf(stderr, "VDSO has unknown DYNAMIC entry (%lx)\n", + (unsigned long)tag); + errors++; + break; + } + dyn++; + } while (tag != DT_NULL); + if (errors) { + exit(EXIT_FAILURE); + } + } + + /* Relocate the dynamic symbol table. */ + if (dynsym_idx) { + ElfN(Sym) *sym = buf + shdr[dynsym_idx].sh_offset; + unsigned sym_n = shdr[dynsym_idx].sh_size / sizeof(*sym); + + for (unsigned i = 0; i < sym_n; ++i) { + output_reloc(outf, buf, &sym[i].st_value); + } + } + + /* Search both dynsym and symtab for the signal return symbols. */ + if (dynsym_idx) { + elfN(search_symtab)(shdr, dynsym_idx, buf, need_bswap); + } + if (symtab_idx) { + elfN(search_symtab)(shdr, symtab_idx, buf, need_bswap); + } +} diff --git a/linux-user/gen-vdso.c b/linux-user/gen-vdso.c new file mode 100644 index 0000000000..31e333be80 --- /dev/null +++ b/linux-user/gen-vdso.c @@ -0,0 +1,223 @@ +/* + * Post-process a vdso elf image for inclusion into qemu. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "elf.h" + + +#define bswap_(p) _Generic(*(p), \ + uint16_t: __builtin_bswap16, \ + uint32_t: __builtin_bswap32, \ + uint64_t: __builtin_bswap64, \ + int16_t: __builtin_bswap16, \ + int32_t: __builtin_bswap32, \ + int64_t: __builtin_bswap64) +#define bswaps(p) (*(p) = bswap_(p)(*(p))) + +static void output_reloc(FILE *outf, void *buf, void *loc) +{ + fprintf(outf, " 0x%08tx,\n", loc - buf); +} + +static const char *sigreturn_sym; +static const char *rt_sigreturn_sym; + +static unsigned sigreturn_addr; +static unsigned rt_sigreturn_addr; + +#define N 32 +#define elfN(x) elf32_##x +#define ElfN(x) Elf32_##x +#include "gen-vdso-elfn.c.inc" +#undef N +#undef elfN +#undef ElfN + +#define N 64 +#define elfN(x) elf64_##x +#define ElfN(x) Elf64_##x +#include "gen-vdso-elfn.c.inc" +#undef N +#undef elfN +#undef ElfN + + +int main(int argc, char **argv) +{ + FILE *inf, *outf; + long total_len; + const char *prefix = "vdso"; + const char *inf_name; + const char *outf_name = NULL; + unsigned char *buf; + bool need_bswap; + + while (1) { + int opt = getopt(argc, argv, "o:p:r:s:"); + if (opt < 0) { + break; + } + switch (opt) { + case 'o': + outf_name = optarg; + break; + case 'p': + prefix = optarg; + break; + case 'r': + rt_sigreturn_sym = optarg; + break; + case 's': + sigreturn_sym = optarg; + break; + default: + usage: + fprintf(stderr, "usage: [-p prefix] [-r rt-sigreturn-name] " + "[-s sigreturn-name] -o output-file input-file\n"); + return EXIT_FAILURE; + } + } + + if (optind >= argc || outf_name == NULL) { + goto usage; + } + inf_name = argv[optind]; + + /* + * Open the input and output files. + */ + inf = fopen(inf_name, "rb"); + if (inf == NULL) { + goto perror_inf; + } + outf = fopen(outf_name, "w"); + if (outf == NULL) { + goto perror_outf; + } + + /* + * Read the input file into a buffer. + * We expect the vdso to be small, on the order of one page, + * therefore we do not expect a partial read. + */ + fseek(inf, 0, SEEK_END); + total_len = ftell(inf); + fseek(inf, 0, SEEK_SET); + + buf = malloc(total_len); + if (buf == NULL) { + goto perror_inf; + } + + errno = 0; + if (fread(buf, 1, total_len, inf) != total_len) { + if (errno) { + goto perror_inf; + } + fprintf(stderr, "%s: incomplete read\n", inf_name); + return EXIT_FAILURE; + } + fclose(inf); + + /* + * Write out the vdso image now, before we make local changes. + */ + + fprintf(outf, + "/* Automatically generated from linux-user/gen-vdso.c. */\n" + "\n" + "static const uint8_t %s_image[] = {", + prefix); + for (long i = 0; i < total_len; ++i) { + if (i % 12 == 0) { + fputs("\n ", outf); + } + fprintf(outf, " 0x%02x,", buf[i]); + } + fprintf(outf, "\n};\n\n"); + + /* + * Identify which elf flavor we're processing. + * The first 16 bytes of the file are e_ident. + */ + + if (buf[EI_MAG0] != ELFMAG0 || buf[EI_MAG1] != ELFMAG1 || + buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) { + fprintf(stderr, "%s: not an elf file\n", inf_name); + return EXIT_FAILURE; + } + switch (buf[EI_DATA]) { + case ELFDATA2LSB: + need_bswap = BYTE_ORDER != LITTLE_ENDIAN; + break; + case ELFDATA2MSB: + need_bswap = BYTE_ORDER != BIG_ENDIAN; + break; + default: + fprintf(stderr, "%s: invalid elf EI_DATA (%u)\n", + inf_name, buf[EI_DATA]); + return EXIT_FAILURE; + } + + /* + * We need to relocate the VDSO image. The one built into the kernel + * is built for a fixed address. The one we built for QEMU is not, + * since that requires close control of the guest address space. + * + * Output relocation addresses as we go. + */ + + fprintf(outf, "static const unsigned %s_relocs[] = {\n", prefix); + + switch (buf[EI_CLASS]) { + case ELFCLASS32: + elf32_process(outf, buf, need_bswap); + break; + case ELFCLASS64: + elf64_process(outf, buf, need_bswap); + break; + default: + fprintf(stderr, "%s: invalid elf EI_CLASS (%u)\n", + inf_name, buf[EI_CLASS]); + return EXIT_FAILURE; + } + + fprintf(outf, "};\n\n"); /* end vdso_relocs. */ + + fprintf(outf, "static const VdsoImageInfo %s_image_info = {\n", prefix); + fprintf(outf, " .image = %s_image,\n", prefix); + fprintf(outf, " .relocs = %s_relocs,\n", prefix); + fprintf(outf, " .image_size = sizeof(%s_image),\n", prefix); + fprintf(outf, " .reloc_count = ARRAY_SIZE(%s_relocs),\n", prefix); + fprintf(outf, " .sigreturn_ofs = 0x%x,\n", sigreturn_addr); + fprintf(outf, " .rt_sigreturn_ofs = 0x%x,\n", rt_sigreturn_addr); + fprintf(outf, "};\n"); + + /* + * Everything should have gone well. + */ + if (fclose(outf)) { + goto perror_outf; + } + return EXIT_SUCCESS; + + perror_inf: + perror(inf_name); + return EXIT_FAILURE; + + perror_outf: + perror(outf_name); + return EXIT_FAILURE; +} diff --git a/linux-user/target_flat.h b/linux-user/generic/target_flat.h similarity index 100% rename from linux-user/target_flat.h rename to linux-user/generic/target_flat.h diff --git a/linux-user/generic/target_mman.h b/linux-user/generic/target_mman.h new file mode 100644 index 0000000000..ec76a91b46 --- /dev/null +++ b/linux-user/generic/target_mman.h @@ -0,0 +1,163 @@ +#ifndef LINUX_USER_TARGET_MMAN_H +#define LINUX_USER_TARGET_MMAN_H + +/* These are defined in linux/mmap.h */ +#define TARGET_MAP_SHARED 0x01 +#define TARGET_MAP_PRIVATE 0x02 +#define TARGET_MAP_SHARED_VALIDATE 0x03 + +/* 0x0100 - 0x4000 flags are defined in asm-generic/mman.h */ +#ifndef TARGET_MAP_GROWSDOWN +#define TARGET_MAP_GROWSDOWN 0x0100 +#endif +#ifndef TARGET_MAP_DENYWRITE +#define TARGET_MAP_DENYWRITE 0x0800 +#endif +#ifndef TARGET_MAP_EXECUTABLE +#define TARGET_MAP_EXECUTABLE 0x1000 +#endif +#ifndef TARGET_MAP_LOCKED +#define TARGET_MAP_LOCKED 0x2000 +#endif +#ifndef TARGET_MAP_NORESERVE +#define TARGET_MAP_NORESERVE 0x4000 +#endif + +/* Defined in asm-generic/mman-common.h */ +#ifndef TARGET_PROT_SEM +#define TARGET_PROT_SEM 0x08 +#endif + +#ifndef TARGET_MAP_TYPE +#define TARGET_MAP_TYPE 0x0f +#endif +#ifndef TARGET_MAP_FIXED +#define TARGET_MAP_FIXED 0x10 +#endif +#ifndef TARGET_MAP_ANONYMOUS +#define TARGET_MAP_ANONYMOUS 0x20 +#endif +#ifndef TARGET_MAP_POPULATE +#define TARGET_MAP_POPULATE 0x008000 +#endif +#ifndef TARGET_MAP_NONBLOCK +#define TARGET_MAP_NONBLOCK 0x010000 +#endif +#ifndef TARGET_MAP_STACK +#define TARGET_MAP_STACK 0x020000 +#endif +#ifndef TARGET_MAP_HUGETLB +#define TARGET_MAP_HUGETLB 0x040000 +#endif +#ifndef TARGET_MAP_SYNC +#define TARGET_MAP_SYNC 0x080000 +#endif +#ifndef TARGET_MAP_FIXED_NOREPLACE +#define TARGET_MAP_FIXED_NOREPLACE 0x100000 +#endif +#ifndef TARGET_MAP_UNINITIALIZED +#define TARGET_MAP_UNINITIALIZED 0x4000000 +#endif + +#ifndef TARGET_MADV_NORMAL +#define TARGET_MADV_NORMAL 0 +#endif + +#ifndef TARGET_MADV_RANDOM +#define TARGET_MADV_RANDOM 1 +#endif + +#ifndef TARGET_MADV_SEQUENTIAL +#define TARGET_MADV_SEQUENTIAL 2 +#endif + +#ifndef TARGET_MADV_WILLNEED +#define TARGET_MADV_WILLNEED 3 +#endif + +#ifndef TARGET_MADV_DONTNEED +#define TARGET_MADV_DONTNEED 4 +#endif + +#ifndef TARGET_MADV_FREE +#define TARGET_MADV_FREE 8 +#endif + +#ifndef TARGET_MADV_REMOVE +#define TARGET_MADV_REMOVE 9 +#endif + +#ifndef TARGET_MADV_DONTFORK +#define TARGET_MADV_DONTFORK 10 +#endif + +#ifndef TARGET_MADV_DOFORK +#define TARGET_MADV_DOFORK 11 +#endif + +#ifndef TARGET_MADV_MERGEABLE +#define TARGET_MADV_MERGEABLE 12 +#endif + +#ifndef TARGET_MADV_UNMERGEABLE +#define TARGET_MADV_UNMERGEABLE 13 +#endif + +#ifndef TARGET_MADV_HUGEPAGE +#define TARGET_MADV_HUGEPAGE 14 +#endif + +#ifndef TARGET_MADV_NOHUGEPAGE +#define TARGET_MADV_NOHUGEPAGE 15 +#endif + +#ifndef TARGET_MADV_DONTDUMP +#define TARGET_MADV_DONTDUMP 16 +#endif + +#ifndef TARGET_MADV_DODUMP +#define TARGET_MADV_DODUMP 17 +#endif + +#ifndef TARGET_MADV_WIPEONFORK +#define TARGET_MADV_WIPEONFORK 18 +#endif + +#ifndef TARGET_MADV_KEEPONFORK +#define TARGET_MADV_KEEPONFORK 19 +#endif + +#ifndef TARGET_MADV_COLD +#define TARGET_MADV_COLD 20 +#endif + +#ifndef TARGET_MADV_PAGEOUT +#define TARGET_MADV_PAGEOUT 21 +#endif + +#ifndef TARGET_MADV_POPULATE_READ +#define TARGET_MADV_POPULATE_READ 22 +#endif + +#ifndef TARGET_MADV_POPULATE_WRITE +#define TARGET_MADV_POPULATE_WRITE 23 +#endif + +#ifndef TARGET_MADV_DONTNEED_LOCKED +#define TARGET_MADV_DONTNEED_LOCKED 24 +#endif + + +#ifndef TARGET_MS_ASYNC +#define TARGET_MS_ASYNC 1 +#endif + +#ifndef TARGET_MS_INVALIDATE +#define TARGET_MS_INVALIDATE 2 +#endif + +#ifndef TARGET_MS_SYNC +#define TARGET_MS_SYNC 4 +#endif + +#endif diff --git a/linux-user/generic/target_resource.h b/linux-user/generic/target_resource.h index 539d8c4677..37d3eb09b3 100644 --- a/linux-user/generic/target_resource.h +++ b/linux-user/generic/target_resource.h @@ -12,8 +12,8 @@ struct target_rlimit { }; struct target_rlimit64 { - uint64_t rlim_cur; - uint64_t rlim_max; + abi_ullong rlim_cur; + abi_ullong rlim_max; }; #define TARGET_RLIM_INFINITY ((abi_ulong)-1) diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index b84e25bf71..7f1499ed28 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -63,6 +63,9 @@ void cpu_loop(CPUHexagonState *env) case EXCP_ATOMIC: cpu_exec_step_atomic(cs); break; + case EXCP_DEBUG: + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, 0); + break; default: EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", trapnr); diff --git a/linux-user/hexagon/signal.c b/linux-user/hexagon/signal.c index ad4e3822d5..492b51f155 100644 --- a/linux-user/hexagon/signal.c +++ b/linux-user/hexagon/signal.c @@ -39,15 +39,12 @@ struct target_sigcontext { target_ulong m0; target_ulong m1; target_ulong usr; - target_ulong p3_0; target_ulong gp; target_ulong ugp; target_ulong pc; target_ulong cause; target_ulong badva; - target_ulong pad1; - target_ulong pad2; - target_ulong pad3; + target_ulong pred[NUM_PREGS]; }; struct target_ucontext { @@ -118,10 +115,14 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPUHexagonState *env) __put_user(env->gpr[HEX_REG_M0], &sc->m0); __put_user(env->gpr[HEX_REG_M1], &sc->m1); __put_user(env->gpr[HEX_REG_USR], &sc->usr); - __put_user(env->gpr[HEX_REG_P3_0], &sc->p3_0); __put_user(env->gpr[HEX_REG_GP], &sc->gp); __put_user(env->gpr[HEX_REG_UGP], &sc->ugp); __put_user(env->gpr[HEX_REG_PC], &sc->pc); + + int i; + for (i = 0; i < NUM_PREGS; i++) { + __put_user(env->pred[i], &(sc->pred[i])); + } } static void setup_ucontext(struct target_ucontext *uc, @@ -161,7 +162,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, } setup_ucontext(&frame->uc, env, set); - tswap_siginfo(&frame->info, info); + frame->info = *info; /* * The on-stack signal trampoline is no longer executed; * however, the libgcc signal frame unwinding code checks @@ -230,10 +231,14 @@ static void restore_sigcontext(CPUHexagonState *env, __get_user(env->gpr[HEX_REG_M0], &sc->m0); __get_user(env->gpr[HEX_REG_M1], &sc->m1); __get_user(env->gpr[HEX_REG_USR], &sc->usr); - __get_user(env->gpr[HEX_REG_P3_0], &sc->p3_0); __get_user(env->gpr[HEX_REG_GP], &sc->gp); __get_user(env->gpr[HEX_REG_UGP], &sc->ugp); __get_user(env->gpr[HEX_REG_PC], &sc->pc); + + int i; + for (i = 0; i < NUM_PREGS; i++) { + __get_user(env->pred[i], &(sc->pred[i])); + } } static void restore_ucontext(CPUHexagonState *env, struct target_ucontext *uc) diff --git a/linux-user/hexagon/target_elf.h b/linux-user/hexagon/target_elf.h index b4e9f40527..36056fc9f0 100644 --- a/linux-user/hexagon/target_elf.h +++ b/linux-user/hexagon/target_elf.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,7 +20,10 @@ static inline const char *cpu_get_model(uint32_t eflags) { - /* For now, treat anything newer than v5 as a v67 */ + static char buf[32]; + int err; + + /* For now, treat anything newer than v5 as a v73 */ /* FIXME - Disable instructions that are newer than the specified arch */ if (eflags == 0x04 || /* v5 */ eflags == 0x05 || /* v55 */ @@ -30,11 +33,18 @@ static inline const char *cpu_get_model(uint32_t eflags) eflags == 0x65 || /* v65 */ eflags == 0x66 || /* v66 */ eflags == 0x67 || /* v67 */ - eflags == 0x8067 /* v67t */ + eflags == 0x8067 || /* v67t */ + eflags == 0x68 || /* v68 */ + eflags == 0x69 || /* v69 */ + eflags == 0x71 || /* v71 */ + eflags == 0x8071 || /* v71t */ + eflags == 0x73 /* v73 */ ) { - return "v67"; + return "v73"; } - return "unknown"; + + err = snprintf(buf, sizeof(buf), "unknown (0x%x)", eflags); + return err >= 0 && err < sizeof(buf) ? buf : "unknown"; } #endif diff --git a/linux-user/hexagon/target_mman.h b/linux-user/hexagon/target_mman.h new file mode 100644 index 0000000000..e6b5e2ca36 --- /dev/null +++ b/linux-user/hexagon/target_mman.h @@ -0,0 +1,14 @@ +/* + * arch/hexgon/include/asm/processor.h + * TASK_UNMAPPED_BASE PAGE_ALIGN(TASK_SIZE / 3) + * + * arch/hexagon/include/asm/mem-layout.h + * TASK_SIZE PAGE_OFFSET + * PAGE_OFFSET 0xc0000000 + */ +#define TASK_UNMAPPED_BASE 0x40000000 + +/* arch/hexagon/include/asm/elf.h */ +#define ELF_ET_DYN_BASE 0x08000000 + +#include "../generic/target_mman.h" diff --git a/linux-user/hexagon/target_proc.h b/linux-user/hexagon/target_proc.h new file mode 100644 index 0000000000..43fe29ca72 --- /dev/null +++ b/linux-user/hexagon/target_proc.h @@ -0,0 +1 @@ +/* No target-specific /proc support */ diff --git a/linux-user/hppa/Makefile.vdso b/linux-user/hppa/Makefile.vdso new file mode 100644 index 0000000000..f4537ae716 --- /dev/null +++ b/linux-user/hppa/Makefile.vdso @@ -0,0 +1,11 @@ +include $(BUILD_DIR)/tests/tcg/hppa-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/hppa +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso.so + +$(SUBDIR)/vdso.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ -nostdlib -shared -Wl,-h,linux-vdso32.so.1 \ + -Wl,--build-id=sha1 -Wl,--hash-style=both \ + -Wl,-T,$(SUBDIR)/vdso.ld $< diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index a576d1a249..d5232f37fe 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -143,11 +143,19 @@ void cpu_loop(CPUHPPAState *env) env->iaoq_f = env->gr[31]; env->iaoq_b = env->gr[31] + 4; break; + case EXCP_IMP: + force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, env->iaoq_f); + break; case EXCP_ILL: - force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPN, env->iaoq_f); + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->iaoq_f); break; case EXCP_PRIV_OPR: - force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->iaoq_f); + /* check for glibc ABORT_INSTRUCTION "iitlbp %r0,(%sr0, %r0)" */ + if (env->cr[CR_IIR] == 0x04000000) { + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->iaoq_f); + } else { + force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->iaoq_f); + } break; case EXCP_PRIV_REG: force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVREG, env->iaoq_f); @@ -161,6 +169,9 @@ void cpu_loop(CPUHPPAState *env) case EXCP_ASSIST: force_sig_fault(TARGET_SIGFPE, 0, env->iaoq_f); break; + case EXCP_BREAK: + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f & ~3); + break; case EXCP_DEBUG: force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f); break; @@ -168,7 +179,8 @@ void cpu_loop(CPUHPPAState *env) /* just indicate that signals should be handled asap */ break; default: - g_assert_not_reached(); + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); + abort(); } process_pending_signals(env); } diff --git a/linux-user/hppa/meson.build b/linux-user/hppa/meson.build index 4709508a09..aa2d9a87a6 100644 --- a/linux-user/hppa/meson.build +++ b/linux-user/hppa/meson.build @@ -3,3 +3,8 @@ syscall_nr_generators += { arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], output: '@BASENAME@_nr.h') } + +vdso_inc = gen_vdso.process('vdso.so', + extra_args: [ '-r', '__kernel_sigtramp_rt' ]) + +linux_user_ss.add(when: 'TARGET_HPPA', if_true: vdso_inc) diff --git a/linux-user/hppa/signal.c b/linux-user/hppa/signal.c index 962f551c04..682ba25922 100644 --- a/linux-user/hppa/signal.c +++ b/linux-user/hppa/signal.c @@ -21,11 +21,12 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "vdso-asmoffset.h" struct target_sigcontext { abi_ulong sc_flags; abi_ulong sc_gr[32]; - uint64_t sc_fr[32]; + abi_ullong sc_fr[32]; abi_ulong sc_iasq[2]; abi_ulong sc_iaoq[2]; abi_ulong sc_sar; @@ -41,31 +42,34 @@ struct target_ucontext { }; struct target_rt_sigframe { - abi_uint tramp[9]; + abi_uint tramp[2]; /* syscall restart return address */ target_siginfo_t info; struct target_ucontext uc; /* hidden location of upper halves of pa2.0 64-bit gregs */ }; +QEMU_BUILD_BUG_ON(sizeof(struct target_rt_sigframe) != sizeof_rt_sigframe); +QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, uc.tuc_mcontext) + != offsetof_sigcontext); +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_gr) + != offsetof_sigcontext_gr); +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_fr) + != offsetof_sigcontext_fr); +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_iaoq) + != offsetof_sigcontext_iaoq); +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_sar) + != offsetof_sigcontext_sar); + + static void setup_sigcontext(struct target_sigcontext *sc, CPUArchState *env) { - int flags = 0; int i; - /* ??? if on_sig_stack, flags |= 1 (PARISC_SC_FLAG_ONSTACK). */ - - if (env->iaoq_f < TARGET_PAGE_SIZE) { - /* In the gateway page, executing a syscall. */ - flags |= 2; /* PARISC_SC_FLAG_IN_SYSCALL */ - __put_user(env->gr[31], &sc->sc_iaoq[0]); - __put_user(env->gr[31] + 4, &sc->sc_iaoq[1]); - } else { - __put_user(env->iaoq_f, &sc->sc_iaoq[0]); - __put_user(env->iaoq_b, &sc->sc_iaoq[1]); - } + __put_user(env->iaoq_f, &sc->sc_iaoq[0]); + __put_user(env->iaoq_b, &sc->sc_iaoq[1]); __put_user(0, &sc->sc_iasq[0]); __put_user(0, &sc->sc_iasq[1]); - __put_user(flags, &sc->sc_flags); + __put_user(0, &sc->sc_flags); __put_user(cpu_hppa_get_psw(env), &sc->sc_gr[0]); for (i = 1; i < 32; ++i) { @@ -82,7 +86,7 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPUArchState *env) static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc) { - target_ulong psw; + abi_ulong psw; int i; __get_user(psw, &sc->sc_gr[0]); @@ -101,10 +105,6 @@ static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc) __get_user(env->cr[CR_SAR], &sc->sc_sar); } -/* No, this doesn't look right, but it's copied straight from the kernel. */ -#define PARISC_RT_SIGFRAME_SIZE32 \ - ((sizeof(struct target_rt_sigframe) + 48 + 64) & -64) - void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUArchState *env) @@ -112,13 +112,13 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, abi_ulong frame_addr, sp, haddr; struct target_rt_sigframe *frame; int i; - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); sp = get_sp_from_cpustate(env); if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { sp = (ts->sigaltstack_used.ss_sp + 0x7f) & ~0x3f; } - frame_addr = QEMU_ALIGN_UP(sp, 64); + frame_addr = QEMU_ALIGN_UP(sp, SIGFRAME); sp = frame_addr + PARISC_RT_SIGFRAME_SIZE32; trace_user_setup_rt_frame(env, frame_addr); @@ -127,7 +127,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, goto give_sigsegv; } - tswap_siginfo(&frame->info, info); + frame->info = *info; frame->uc.tuc_flags = 0; frame->uc.tuc_link = 0; @@ -139,14 +139,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, setup_sigcontext(&frame->uc.tuc_mcontext, env); - __put_user(0x34190000, frame->tramp + 0); /* ldi 0,%r25 */ - __put_user(0x3414015a, frame->tramp + 1); /* ldi __NR_rt_sigreturn,%r20 */ - __put_user(0xe4008200, frame->tramp + 2); /* be,l 0x100(%sr2,%r0) */ - __put_user(0x08000240, frame->tramp + 3); /* nop */ - unlock_user_struct(frame, frame_addr, 1); - env->gr[2] = h2g(frame->tramp); + env->gr[2] = default_rt_sigreturn; env->gr[30] = sp; env->gr[26] = sig; env->gr[25] = h2g(&frame->info); @@ -155,19 +150,21 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, haddr = ka->_sa_handler; if (haddr & 2) { /* Function descriptor. */ - target_ulong *fdesc, dest; + abi_ptr *fdesc, dest; haddr &= -4; - if (!lock_user_struct(VERIFY_READ, fdesc, haddr, 1)) { + fdesc = lock_user(VERIFY_READ, haddr, 2 * sizeof(abi_ptr), 1); + if (!fdesc) { goto give_sigsegv; } __get_user(dest, fdesc); __get_user(env->gr[19], fdesc + 1); - unlock_user_struct(fdesc, haddr, 1); + unlock_user(fdesc, haddr, 0); haddr = dest; } env->iaoq_f = haddr; env->iaoq_b = haddr + 4; + env->psw_n = 0; return; give_sigsegv: @@ -197,3 +194,23 @@ long do_rt_sigreturn(CPUArchState *env) force_sig(TARGET_SIGSEGV); return -QEMU_ESIGRETURN; } + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 6*4, 0); + abi_ulong SIGFRAME_CONTEXT_REGS32; + assert(tramp != NULL); + + SIGFRAME_CONTEXT_REGS32 = offsetof(struct target_rt_sigframe, uc.tuc_mcontext); + SIGFRAME_CONTEXT_REGS32 -= PARISC_RT_SIGFRAME_SIZE32; + + __put_user(SIGFRAME_CONTEXT_REGS32, tramp + 0); + __put_user(0x08000240, tramp + 1); /* nop - b/c dwarf2 unwind routines */ + __put_user(0x34190000, tramp + 2); /* ldi 0, %r25 (in_syscall=0) */ + __put_user(0x3414015a, tramp + 3); /* ldi __NR_rt_sigreturn, %r20 */ + __put_user(0xe4008200, tramp + 4); /* ble 0x100(%sr2, %r0) */ + __put_user(0x08000240, tramp + 5); /* nop */ + + default_rt_sigreturn = (sigtramp_page + 8) | 3; + unlock_user(tramp, sigtramp_page, 6*4); +} diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index 82b4e9535e..19cae8bd65 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -9,6 +9,6 @@ #define HPPA_TARGET_ELF_H static inline const char *cpu_get_model(uint32_t eflags) { - return "any"; + return "hppa"; } #endif diff --git a/linux-user/hppa/target_mman.h b/linux-user/hppa/target_mman.h new file mode 100644 index 0000000000..ccda46e842 --- /dev/null +++ b/linux-user/hppa/target_mman.h @@ -0,0 +1,35 @@ +#ifndef HPPA_TARGET_MMAN_H +#define HPPA_TARGET_MMAN_H + +#define TARGET_MAP_TYPE 0x2b +#define TARGET_MAP_FIXED 0x04 +#define TARGET_MAP_ANONYMOUS 0x10 +#define TARGET_MAP_GROWSDOWN 0x8000 +#define TARGET_MAP_POPULATE 0x10000 +#define TARGET_MAP_NONBLOCK 0x20000 +#define TARGET_MAP_STACK 0x40000 +#define TARGET_MAP_HUGETLB 0x80000 +#define TARGET_MAP_UNINITIALIZED 0 + +#define TARGET_MADV_MERGEABLE 65 +#define TARGET_MADV_UNMERGEABLE 66 +#define TARGET_MADV_HUGEPAGE 67 +#define TARGET_MADV_NOHUGEPAGE 68 +#define TARGET_MADV_DONTDUMP 69 +#define TARGET_MADV_DODUMP 70 +#define TARGET_MADV_WIPEONFORK 71 +#define TARGET_MADV_KEEPONFORK 72 + +#define TARGET_MS_SYNC 1 +#define TARGET_MS_ASYNC 2 +#define TARGET_MS_INVALIDATE 4 + +/* arch/parisc/include/asm/processor.h: DEFAULT_MAP_BASE32 */ +#define TASK_UNMAPPED_BASE 0x40000000 + +/* arch/parisc/include/asm/elf.h */ +#define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE + 0x01000000) + +#include "../generic/target_mman.h" + +#endif diff --git a/linux-user/hppa/target_proc.h b/linux-user/hppa/target_proc.h new file mode 100644 index 0000000000..9340c3b6af --- /dev/null +++ b/linux-user/hppa/target_proc.h @@ -0,0 +1,26 @@ +/* + * HPPA specific proc functions for linux-user + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HPPA_TARGET_PROC_H +#define HPPA_TARGET_PROC_H + +static int open_cpuinfo(CPUArchState *cpu_env, int fd) +{ + int i, num_cpus; + + num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + for (i = 0; i < num_cpus; i++) { + dprintf(fd, "processor\t: %d\n", i); + dprintf(fd, "cpu family\t: PA-RISC 1.1e\n"); + dprintf(fd, "cpu\t\t: PA7300LC (PCX-L2)\n"); + dprintf(fd, "capabilities\t: os32\n"); + dprintf(fd, "model\t\t: 9000/778/B160L - " + "Merlin L2 160 QEMU (9000/778/B160L)\n\n"); + } + return 0; +} +#define HAVE_ARCH_PROC_CPUINFO + +#endif /* HPPA_TARGET_PROC_H */ diff --git a/linux-user/hppa/target_signal.h b/linux-user/hppa/target_signal.h index af6c2fce58..190bb3d653 100644 --- a/linux-user/hppa/target_signal.h +++ b/linux-user/hppa/target_signal.h @@ -70,18 +70,6 @@ typedef struct target_sigaltstack { /* mask for all SS_xxx flags */ #define TARGET_SS_FLAG_BITS TARGET_SS_AUTODISARM -/* - * We cannot use a bare sigtramp page for hppa-linux. - * - * Unlike other guests where we use the instructions at PC to validate - * an offset from SP, the hppa libgcc signal frame fallback unwinding uses - * the PC address itself to find the frame. This is due to the fact that - * the hppa grows the stack upward, and the frame is of unknown size. - * - * TODO: We should be able to use a VDSO to address this, by providing - * proper unwind info for the sigtramp code, at which point the fallback - * unwinder will not be used. - */ -#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 #endif /* HPPA_TARGET_SIGNAL_H */ diff --git a/linux-user/hppa/target_syscall.h b/linux-user/hppa/target_syscall.h index 4b382c1fcf..9a8f8ca628 100644 --- a/linux-user/hppa/target_syscall.h +++ b/linux-user/hppa/target_syscall.h @@ -26,4 +26,6 @@ struct target_pt_regs { #define TARGET_MCL_FUTURE 2 #define TARGET_MCL_ONFAULT 4 +#define TARGET_DEFAULT_STACK_SIZE 80 * 1024 * 1024UL + #endif /* HPPA_TARGET_SYSCALL_H */ diff --git a/linux-user/hppa/vdso-asmoffset.h b/linux-user/hppa/vdso-asmoffset.h new file mode 100644 index 0000000000..c8b40c0332 --- /dev/null +++ b/linux-user/hppa/vdso-asmoffset.h @@ -0,0 +1,12 @@ +#define sizeof_rt_sigframe 584 +#define offsetof_sigcontext 160 +#define offsetof_sigcontext_gr 0x4 +#define offsetof_sigcontext_fr 0x88 +#define offsetof_sigcontext_iaoq 0x190 +#define offsetof_sigcontext_sar 0x198 + +/* arch/parisc/include/asm/rt_sigframe.h */ +#define SIGFRAME 64 +#define FUNCTIONCALLFRAME 48 +#define PARISC_RT_SIGFRAME_SIZE32 \ + (((sizeof_rt_sigframe) + FUNCTIONCALLFRAME + SIGFRAME) & -SIGFRAME) diff --git a/linux-user/hppa/vdso.S b/linux-user/hppa/vdso.S new file mode 100644 index 0000000000..5be14d2f70 --- /dev/null +++ b/linux-user/hppa/vdso.S @@ -0,0 +1,165 @@ +/* + * hppa linux kernel vdso replacement. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "vdso-asmoffset.h" + + .text + + +/* + * arch/parisc/kernel/vdso32/sigtramp.S: + * Gdb expects the trampoline is on the stack and the pc is offset from + * a 64-byte boundary by 0, 4 or 5 instructions. Since the vdso trampoline + * is not on the stack, we need a new variant with different offsets and + * data to tell gdb where to find the signal context on the stack. + * + * Here we put the offset to the context data at the start of the trampoline + * region and offset the first trampoline by 2 instructions. Please do + * not change the trampoline as the code in gdb depends on the following + * instruction sequence exactly. + */ + +/* arch/parisc/kernel/asm-offsets.c */ +#define SIGFRAME_CONTEXT_REGS32 \ + (offsetof_sigcontext - PARISC_RT_SIGFRAME_SIZE32) + + .align 64 + .word SIGFRAME_CONTEXT_REGS32 + +/* + * All that said, we can provide a proper unwind record, which means that + * GDB should not actually need the offset magic. + * + * The return address that arrived here, from the inner frame, is + * not marked as a signal frame and so the unwinder still tries to + * subtract 1 to examine the presumed call insn. Thus we must + * extend the unwind info to a nop before the start. + */ + + .cfi_startproc simple + .cfi_signal_frame + + /* Compare pa32_fallback_frame_state from libgcc. */ + + /* + * Place the CFA at the start of sigcontext for convenience. + * The previous CFA will be restored from the saved stack pointer. + */ + .cfi_def_cfa 30, -PARISC_RT_SIGFRAME_SIZE32 + offsetof_sigcontext + + /* Record save offset of general registers. */ + .cfi_offset 1, offsetof_sigcontext_gr + 1 * 4 + .cfi_offset 2, offsetof_sigcontext_gr + 2 * 4 + .cfi_offset 3, offsetof_sigcontext_gr + 3 * 4 + .cfi_offset 4, offsetof_sigcontext_gr + 4 * 4 + .cfi_offset 5, offsetof_sigcontext_gr + 5 * 4 + .cfi_offset 6, offsetof_sigcontext_gr + 6 * 4 + .cfi_offset 7, offsetof_sigcontext_gr + 7 * 4 + .cfi_offset 8, offsetof_sigcontext_gr + 8 * 4 + .cfi_offset 9, offsetof_sigcontext_gr + 9 * 4 + .cfi_offset 10, offsetof_sigcontext_gr + 10 * 4 + .cfi_offset 11, offsetof_sigcontext_gr + 11 * 4 + .cfi_offset 12, offsetof_sigcontext_gr + 12 * 4 + .cfi_offset 13, offsetof_sigcontext_gr + 13 * 4 + .cfi_offset 14, offsetof_sigcontext_gr + 14 * 4 + .cfi_offset 15, offsetof_sigcontext_gr + 15 * 4 + .cfi_offset 16, offsetof_sigcontext_gr + 16 * 4 + .cfi_offset 17, offsetof_sigcontext_gr + 17 * 4 + .cfi_offset 18, offsetof_sigcontext_gr + 18 * 4 + .cfi_offset 19, offsetof_sigcontext_gr + 19 * 4 + .cfi_offset 20, offsetof_sigcontext_gr + 20 * 4 + .cfi_offset 21, offsetof_sigcontext_gr + 21 * 4 + .cfi_offset 22, offsetof_sigcontext_gr + 22 * 4 + .cfi_offset 23, offsetof_sigcontext_gr + 23 * 4 + .cfi_offset 24, offsetof_sigcontext_gr + 24 * 4 + .cfi_offset 25, offsetof_sigcontext_gr + 25 * 4 + .cfi_offset 26, offsetof_sigcontext_gr + 26 * 4 + .cfi_offset 27, offsetof_sigcontext_gr + 27 * 4 + .cfi_offset 28, offsetof_sigcontext_gr + 28 * 4 + .cfi_offset 29, offsetof_sigcontext_gr + 29 * 4 + .cfi_offset 30, offsetof_sigcontext_gr + 30 * 4 + .cfi_offset 31, offsetof_sigcontext_gr + 31 * 4 + + /* Record save offset of fp registers, left and right halves. */ + .cfi_offset 32, offsetof_sigcontext_fr + 4 * 8 + .cfi_offset 33, offsetof_sigcontext_fr + 4 * 8 + 4 + .cfi_offset 34, offsetof_sigcontext_fr + 5 * 8 + .cfi_offset 35, offsetof_sigcontext_fr + 5 * 8 + 4 + .cfi_offset 36, offsetof_sigcontext_fr + 6 * 8 + .cfi_offset 37, offsetof_sigcontext_fr + 6 * 8 + 4 + .cfi_offset 38, offsetof_sigcontext_fr + 7 * 8 + .cfi_offset 39, offsetof_sigcontext_fr + 7 * 8 + 4 + .cfi_offset 40, offsetof_sigcontext_fr + 8 * 8 + .cfi_offset 41, offsetof_sigcontext_fr + 8 * 8 + 4 + .cfi_offset 42, offsetof_sigcontext_fr + 9 * 8 + .cfi_offset 43, offsetof_sigcontext_fr + 9 * 8 + 4 + .cfi_offset 44, offsetof_sigcontext_fr + 10 * 8 + .cfi_offset 45, offsetof_sigcontext_fr + 10 * 8 + 4 + .cfi_offset 46, offsetof_sigcontext_fr + 11 * 8 + .cfi_offset 47, offsetof_sigcontext_fr + 11 * 8 + 4 + .cfi_offset 48, offsetof_sigcontext_fr + 12 * 8 + .cfi_offset 49, offsetof_sigcontext_fr + 12 * 8 + 4 + .cfi_offset 50, offsetof_sigcontext_fr + 13 * 8 + .cfi_offset 51, offsetof_sigcontext_fr + 13 * 8 + 4 + .cfi_offset 52, offsetof_sigcontext_fr + 14 * 8 + .cfi_offset 53, offsetof_sigcontext_fr + 14 * 8 + 4 + .cfi_offset 54, offsetof_sigcontext_fr + 15 * 8 + .cfi_offset 55, offsetof_sigcontext_fr + 15 * 8 + 4 + .cfi_offset 56, offsetof_sigcontext_fr + 16 * 8 + .cfi_offset 57, offsetof_sigcontext_fr + 16 * 8 + 4 + .cfi_offset 58, offsetof_sigcontext_fr + 17 * 8 + .cfi_offset 59, offsetof_sigcontext_fr + 17 * 8 + 4 + .cfi_offset 60, offsetof_sigcontext_fr + 18 * 8 + .cfi_offset 61, offsetof_sigcontext_fr + 18 * 8 + 4 + .cfi_offset 62, offsetof_sigcontext_fr + 19 * 8 + .cfi_offset 63, offsetof_sigcontext_fr + 19 * 8 + 4 + .cfi_offset 64, offsetof_sigcontext_fr + 20 * 8 + .cfi_offset 65, offsetof_sigcontext_fr + 20 * 8 + 4 + .cfi_offset 66, offsetof_sigcontext_fr + 21 * 8 + .cfi_offset 67, offsetof_sigcontext_fr + 21 * 8 + 4 + .cfi_offset 68, offsetof_sigcontext_fr + 22 * 8 + .cfi_offset 69, offsetof_sigcontext_fr + 22 * 8 + 4 + .cfi_offset 70, offsetof_sigcontext_fr + 23 * 8 + .cfi_offset 71, offsetof_sigcontext_fr + 23 * 8 + 4 + .cfi_offset 72, offsetof_sigcontext_fr + 24 * 8 + .cfi_offset 73, offsetof_sigcontext_fr + 24 * 8 + 4 + .cfi_offset 74, offsetof_sigcontext_fr + 25 * 8 + .cfi_offset 75, offsetof_sigcontext_fr + 25 * 8 + 4 + .cfi_offset 76, offsetof_sigcontext_fr + 26 * 8 + .cfi_offset 77, offsetof_sigcontext_fr + 26 * 8 + 4 + .cfi_offset 78, offsetof_sigcontext_fr + 27 * 8 + .cfi_offset 79, offsetof_sigcontext_fr + 27 * 8 + 4 + .cfi_offset 80, offsetof_sigcontext_fr + 28 * 8 + .cfi_offset 81, offsetof_sigcontext_fr + 28 * 8 + 4 + .cfi_offset 82, offsetof_sigcontext_fr + 29 * 8 + .cfi_offset 83, offsetof_sigcontext_fr + 29 * 8 + 4 + .cfi_offset 84, offsetof_sigcontext_fr + 30 * 8 + .cfi_offset 85, offsetof_sigcontext_fr + 30 * 8 + 4 + .cfi_offset 86, offsetof_sigcontext_fr + 31 * 8 + .cfi_offset 87, offsetof_sigcontext_fr + 31 * 8 + 4 + + /* Record save offset of %sar */ + .cfi_offset 88, offsetof_sigcontext_sar + + /* Record save offset of return address, iaoq[0]. */ + .cfi_return_column 89 + .cfi_offset 89, offsetof_sigcontext_iaoq + + nop + +__kernel_sigtramp_rt: + ldi 0, %r25 + ldi __NR_rt_sigreturn, %r20 + be,l 0x100(%sr2, %r0), %sr0, %r31 + nop + + .cfi_endproc + .size __kernel_sigtramp_rt, . - __kernel_sigtramp_rt + .type __kernel_sigtramp_rt, @function + .globl __kernel_sigtramp_rt diff --git a/linux-user/hppa/vdso.ld b/linux-user/hppa/vdso.ld new file mode 100644 index 0000000000..b17ad974f3 --- /dev/null +++ b/linux-user/hppa/vdso.ld @@ -0,0 +1,77 @@ +/* + * Linker script for linux hppa vdso. + * + * 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 . + */ + +VERSION { + /* + * The kernel's vdso32.lds.S attempts to export + * __kernel_sigtramp_rt32 + * __kernel_restart_syscall32 + * except that those symbols don't exist. The actual symbols are + * __kernel_sigtramp_rt + * __kernel_restart_syscall + * which means that nothing is exported at all. + * QEMU handles syscall restart internally, so we don't + * need to implement __kernel_restart_syscall at all. + */ + LINUX_5.18 { + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; + dynamic PT_DYNAMIC FLAGS(4); + note PT_NOTE FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; +} + +SECTIONS { + . = SIZEOF_HEADERS; + + /* The following, including the FILEHDRS and PHDRS, are modified + when we relocate the binary. We want them to be initially + writable for the relocation; we'll force them read-only after. */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + .data : { + /* There ought not be any real read-write data. + But since we manipulated the segment layout, + we have to put these sections somewhere. */ + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load +} diff --git a/linux-user/hppa/vdso.so b/linux-user/hppa/vdso.so new file mode 100755 index 0000000000..e1ddd70c37 Binary files /dev/null and b/linux-user/hppa/vdso.so differ diff --git a/linux-user/i386/Makefile.vdso b/linux-user/i386/Makefile.vdso new file mode 100644 index 0000000000..95bc616f6d --- /dev/null +++ b/linux-user/i386/Makefile.vdso @@ -0,0 +1,11 @@ +include $(BUILD_DIR)/tests/tcg/i386-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/i386 +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso.so + +$(SUBDIR)/vdso.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ -m32 -nostdlib -shared -Wl,-h,linux-gate.so.1 \ + -Wl,--build-id=sha1 -Wl,--hash-style=both \ + -Wl,-T,$(SUBDIR)/vdso.ld $< diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c index 492701dd9a..92beb6830c 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -47,7 +47,7 @@ static void write_dt(void *ptr, unsigned long addr, unsigned long limit, } static uint64_t *idt_table; -#ifdef TARGET_X86_64 + static void set_gate64(void *ptr, unsigned int type, unsigned int dpl, uint64_t addr, unsigned int sel) { @@ -60,8 +60,10 @@ static void set_gate64(void *ptr, unsigned int type, unsigned int dpl, p[2] = tswap32(addr >> 32); p[3] = 0; } + +#ifdef TARGET_X86_64 /* only dpl matters as we do only user space emulation */ -static void set_idt(int n, unsigned int dpl) +static void set_idt(int n, unsigned int dpl, bool is64) { set_gate64(idt_table + n * 2, 0, dpl, 0, 0); } @@ -78,9 +80,13 @@ static void set_gate(void *ptr, unsigned int type, unsigned int dpl, } /* only dpl matters as we do only user space emulation */ -static void set_idt(int n, unsigned int dpl) +static void set_idt(int n, unsigned int dpl, bool is64) { - set_gate(idt_table + n, 0, dpl, 0, 0); + if (is64) { + set_gate64(idt_table + n * 2, 0, dpl, 0, 0); + } else { + set_gate(idt_table + n, 0, dpl, 0, 0); + } } #endif @@ -137,7 +143,7 @@ static void emulate_vsyscall(CPUX86State *env) } /* - * Validate the the pointer arguments. + * Validate the pointer arguments. */ switch (syscall) { case TARGET_NR_gettimeofday: @@ -201,7 +207,6 @@ void cpu_loop(CPUX86State *env) { CPUState *cs = env_cpu(env); int trapnr; - abi_ulong pc; abi_ulong ret; for(;;) { @@ -212,6 +217,9 @@ void cpu_loop(CPUX86State *env) switch(trapnr) { case 0x80: +#ifndef TARGET_X86_64 + case EXCP_SYSCALL: +#endif /* linux syscall from int $0x80 */ ret = do_syscall(env, env->regs[R_EAX], @@ -228,9 +236,9 @@ void cpu_loop(CPUX86State *env) env->regs[R_EAX] = ret; } break; -#ifndef TARGET_ABI32 +#ifdef TARGET_X86_64 case EXCP_SYSCALL: - /* linux syscall from syscall instruction */ + /* linux syscall from syscall instruction. */ ret = do_syscall(env, env->regs[R_EAX], env->regs[R_EDI], @@ -246,8 +254,6 @@ void cpu_loop(CPUX86State *env) env->regs[R_EAX] = ret; } break; -#endif -#ifdef TARGET_X86_64 case EXCP_VSYSCALL: emulate_vsyscall(env); break; @@ -307,32 +313,46 @@ void cpu_loop(CPUX86State *env) cpu_exec_step_atomic(cs); break; default: - pc = env->segs[R_CS].base + env->eip; - EXCP_DUMP(env, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n", - (long)pc, trapnr); + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", + trapnr); abort(); } process_pending_signals(env); } } +static void target_cpu_free(void *obj) +{ + target_munmap(cpu_env(obj)->gdt.base, + sizeof(uint64_t) * TARGET_GDT_ENTRIES); + g_free(obj); +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + CPUState *cpu = env_cpu(env); + bool is64 = (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) != 0; + int i; + + OBJECT(cpu)->free = target_cpu_free; env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; env->hflags |= HF_PE_MASK | HF_CPL_MASK; if (env->features[FEAT_1_EDX] & CPUID_SSE) { env->cr[4] |= CR4_OSFXSR_MASK; env->hflags |= HF_OSFXSR_MASK; } -#ifndef TARGET_ABI32 + /* enable 64 bit mode if possible */ - if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM)) { + if (is64) { + env->cr[4] |= CR4_PAE_MASK; + env->efer |= MSR_EFER_LMA | MSR_EFER_LME; + env->hflags |= HF_LMA_MASK; + } +#ifndef TARGET_ABI32 + else { fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n"); exit(EXIT_FAILURE); } - env->cr[4] |= CR4_PAE_MASK; - env->efer |= MSR_EFER_LMA | MSR_EFER_LME; - env->hflags |= HF_LMA_MASK; #endif /* flags setup : we activate the IRQs by default as in user mode */ @@ -371,27 +391,12 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); idt_table = g2h_untagged(env->idt.base); - set_idt(0, 0); - set_idt(1, 0); - set_idt(2, 0); - set_idt(3, 3); - set_idt(4, 3); - set_idt(5, 0); - set_idt(6, 0); - set_idt(7, 0); - set_idt(8, 0); - set_idt(9, 0); - set_idt(10, 0); - set_idt(11, 0); - set_idt(12, 0); - set_idt(13, 0); - set_idt(14, 0); - set_idt(15, 0); - set_idt(16, 0); - set_idt(17, 0); - set_idt(18, 0); - set_idt(19, 0); - set_idt(0x80, 3); + for (i = 0; i < 20; i++) { + set_idt(i, 0, is64); + } + set_idt(3, 3, is64); + set_idt(4, 3, is64); + set_idt(0x80, 3, is64); /* linux segment setup */ { diff --git a/linux-user/i386/meson.build b/linux-user/i386/meson.build index ee523019a5..d42fc6cbc9 100644 --- a/linux-user/i386/meson.build +++ b/linux-user/i386/meson.build @@ -3,3 +3,10 @@ syscall_nr_generators += { arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], output: '@BASENAME@_nr.h') } + +vdso_inc = gen_vdso.process('vdso.so', extra_args: [ + '-s', '__kernel_sigreturn', + '-r', '__kernel_rt_sigreturn' + ]) + +linux_user_ss.add(when: 'TARGET_I386', if_true: vdso_inc) diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 4372621a4d..cfe70fc5cf 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -24,6 +24,10 @@ /* from the Linux kernel - /arch/x86/include/uapi/asm/sigcontext.h */ +#define TARGET_FP_XSTATE_MAGIC1 0x46505853U /* FPXS */ +#define TARGET_FP_XSTATE_MAGIC2 0x46505845U /* FPXE */ +#define TARGET_FP_XSTATE_MAGIC2_SIZE 4 + struct target_fpreg { uint16_t significand[4]; uint16_t exponent; @@ -39,6 +43,35 @@ struct target_xmmreg { uint32_t element[4]; }; +struct target_fpx_sw_bytes { + uint32_t magic1; + uint32_t extended_size; + uint64_t xfeatures; + uint32_t xstate_size; + uint32_t reserved[7]; +}; +QEMU_BUILD_BUG_ON(sizeof(struct target_fpx_sw_bytes) != 12*4); + +struct target_fpstate_fxsave { + /* FXSAVE format */ + uint16_t cw; + uint16_t sw; + uint16_t twd; + uint16_t fop; + uint64_t rip; + uint64_t rdp; + uint32_t mxcsr; + uint32_t mxcsr_mask; + uint32_t st_space[32]; + uint32_t xmm_space[64]; + uint32_t hw_reserved[12]; + struct target_fpx_sw_bytes sw_reserved; + uint8_t xfeatures[]; +}; +#define TARGET_FXSAVE_SIZE sizeof(struct target_fpstate_fxsave) +QEMU_BUILD_BUG_ON(TARGET_FXSAVE_SIZE != 512); +QEMU_BUILD_BUG_ON(offsetof(struct target_fpstate_fxsave, sw_reserved) != 464); + struct target_fpstate_32 { /* Regular FPU environment */ uint32_t cw; @@ -51,35 +84,21 @@ struct target_fpstate_32 { struct target_fpreg st[8]; uint16_t status; uint16_t magic; /* 0xffff = regular FPU data only */ - - /* FXSR FPU environment */ - uint32_t _fxsr_env[6]; /* FXSR FPU env is ignored */ - uint32_t mxcsr; - uint32_t reserved; - struct target_fpxreg fxsr_st[8]; /* FXSR FPU reg data is ignored */ - struct target_xmmreg xmm[8]; - uint32_t padding[56]; + struct target_fpstate_fxsave fxsave; }; -struct target_fpstate_64 { - /* FXSAVE format */ - uint16_t cw; - uint16_t sw; - uint16_t twd; - uint16_t fop; - uint64_t rip; - uint64_t rdp; - uint32_t mxcsr; - uint32_t mxcsr_mask; - uint32_t st_space[32]; - uint32_t xmm_space[64]; - uint32_t reserved[24]; -}; +/* + * For simplicity, setup_frame aligns struct target_fpstate_32 to + * 16 bytes, so ensure that the FXSAVE area is also aligned. + */ +QEMU_BUILD_BUG_ON(offsetof(struct target_fpstate_32, fxsave) & 15); #ifndef TARGET_X86_64 # define target_fpstate target_fpstate_32 +# define TARGET_FPSTATE_FXSAVE_OFFSET offsetof(struct target_fpstate_32, fxsave) #else -# define target_fpstate target_fpstate_64 +# define target_fpstate target_fpstate_fxsave +# define TARGET_FPSTATE_FXSAVE_OFFSET 0 #endif struct target_sigcontext_32 { @@ -163,10 +182,25 @@ struct sigframe { abi_ulong pretcode; int sig; struct target_sigcontext sc; - struct target_fpstate fpstate; + /* + * The actual fpstate is placed after retcode[] below, to make + * room for the variable-sized xsave data. The older unused fpstate + * has to be kept to avoid changing the offset of extramask[], which + * is part of the ABI. + */ + struct target_fpstate fpstate_unused; abi_ulong extramask[TARGET_NSIG_WORDS-1]; char retcode[8]; + + /* + * This field will be 16-byte aligned in memory. Applying QEMU_ALIGNED + * to it ensures that the base of the frame has an appropriate alignment + * too. + */ + struct target_fpstate fpstate QEMU_ALIGNED(8); }; +#define TARGET_SIGFRAME_FXSAVE_OFFSET ( \ + offsetof(struct sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET) struct rt_sigframe { abi_ulong pretcode; @@ -175,9 +209,21 @@ struct rt_sigframe { abi_ulong puc; struct target_siginfo info; struct target_ucontext uc; - struct target_fpstate fpstate; char retcode[8]; + struct target_fpstate fpstate QEMU_ALIGNED(8); }; +#define TARGET_RT_SIGFRAME_FXSAVE_OFFSET ( \ + offsetof(struct rt_sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET) + +/* + * Verify that vdso-asmoffset.h constants match. + */ +#include "i386/vdso-asmoffset.h" + +QEMU_BUILD_BUG_ON(offsetof(struct sigframe, sc.eip) + != SIGFRAME_SIGCONTEXT_eip); +QEMU_BUILD_BUG_ON(offsetof(struct rt_sigframe, uc.tuc_mcontext.eip) + != RT_SIGFRAME_SIGCONTEXT_eip); #else @@ -185,16 +231,51 @@ struct rt_sigframe { abi_ulong pretcode; struct target_ucontext uc; struct target_siginfo info; - struct target_fpstate fpstate; + struct target_fpstate fpstate QEMU_ALIGNED(16); }; - +#define TARGET_RT_SIGFRAME_FXSAVE_OFFSET ( \ + offsetof(struct rt_sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET) #endif /* * Set up a signal frame. */ -/* XXX: save x87 state */ +static void xsave_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxsave, + abi_ulong fxsave_addr) +{ + if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) { + /* fxsave_addr must be 16 byte aligned for fxsave */ + assert(!(fxsave_addr & 0xf)); + + cpu_x86_fxsave(env, fxsave_addr); + __put_user(0, &fxsave->sw_reserved.magic1); + } else { + uint32_t xstate_size = xsave_area_size(env->xcr0, false); + uint32_t xfeatures_size = xstate_size - TARGET_FXSAVE_SIZE; + + /* + * extended_size is the offset from fpstate_addr to right after the end + * of the extended save states. On 32-bit that includes the legacy + * FSAVE area. + */ + uint32_t extended_size = TARGET_FPSTATE_FXSAVE_OFFSET + + xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE; + + /* fxsave_addr must be 64 byte aligned for xsave */ + assert(!(fxsave_addr & 0x3f)); + + /* Zero the header, XSAVE *adds* features to an existing save state. */ + memset(fxsave->xfeatures, 0, 64); + cpu_x86_xsave(env, fxsave_addr); + __put_user(TARGET_FP_XSTATE_MAGIC1, &fxsave->sw_reserved.magic1); + __put_user(extended_size, &fxsave->sw_reserved.extended_size); + __put_user(env->xcr0, &fxsave->sw_reserved.xfeatures); + __put_user(xstate_size, &fxsave->sw_reserved.xstate_size); + __put_user(TARGET_FP_XSTATE_MAGIC2, (uint32_t *) &fxsave->xfeatures[xfeatures_size]); + } +} + static void setup_sigcontext(struct target_sigcontext *sc, struct target_fpstate *fpstate, CPUX86State *env, abi_ulong mask, abi_ulong fpstate_addr) @@ -226,13 +307,14 @@ static void setup_sigcontext(struct target_sigcontext *sc, cpu_x86_fsave(env, fpstate_addr, 1); fpstate->status = fpstate->sw; - magic = 0xffff; + if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) { + magic = 0xffff; + } else { + xsave_sigcontext(env, &fpstate->fxsave, + fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET); + magic = 0; + } __put_user(magic, &fpstate->magic); - __put_user(fpstate_addr, &sc->fpstate); - - /* non-iBCS2 extensions.. */ - __put_user(mask, &sc->oldmask); - __put_user(env->cr[2], &sc->cr2); #else __put_user(env->regs[R_EDI], &sc->rdi); __put_user(env->regs[R_ESI], &sc->rsi); @@ -262,15 +344,14 @@ static void setup_sigcontext(struct target_sigcontext *sc, __put_user((uint16_t)0, &sc->fs); __put_user(env->segs[R_SS].selector, &sc->ss); + xsave_sigcontext(env, fpstate, fpstate_addr); +#endif + + __put_user(fpstate_addr, &sc->fpstate); + + /* non-iBCS2 extensions.. */ __put_user(mask, &sc->oldmask); __put_user(env->cr[2], &sc->cr2); - - /* fpstate_addr must be 16 byte aligned for fxsave */ - assert(!(fpstate_addr & 0xf)); - - cpu_x86_fxsave(env, fpstate_addr); - __put_user(fpstate_addr, &sc->fpstate); -#endif } /* @@ -278,7 +359,7 @@ static void setup_sigcontext(struct target_sigcontext *sc, */ static inline abi_ulong -get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size) +get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t fxsave_offset) { unsigned long esp; @@ -302,11 +383,15 @@ get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size) #endif } -#ifndef TARGET_X86_64 - return (esp - frame_size) & -8ul; -#else - return ((esp - frame_size) & (~15ul)) - 8; -#endif + if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) { + return (esp - (fxsave_offset + TARGET_FXSAVE_SIZE)) & -8ul; + } else if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) { + return ((esp - TARGET_FXSAVE_SIZE) & -16ul) - fxsave_offset; + } else { + size_t xstate_size = + xsave_area_size(env->xcr0, false) + TARGET_FP_XSTATE_MAGIC2_SIZE; + return ((esp - xstate_size) & -64ul) - fxsave_offset; + } } #ifndef TARGET_X86_64 @@ -334,7 +419,7 @@ void setup_frame(int sig, struct target_sigaction *ka, struct sigframe *frame; int i; - frame_addr = get_sigframe(ka, env, sizeof(*frame)); + frame_addr = get_sigframe(ka, env, TARGET_SIGFRAME_FXSAVE_OFFSET); trace_user_setup_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) @@ -345,7 +430,7 @@ void setup_frame(int sig, struct target_sigaction *ka, setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0], frame_addr + offsetof(struct sigframe, fpstate)); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { + for (i = 1; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &frame->extramask[i - 1]); } @@ -390,7 +475,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, struct rt_sigframe *frame; int i; - frame_addr = get_sigframe(ka, env, sizeof(*frame)); + frame_addr = get_sigframe(ka, env, TARGET_RT_SIGFRAME_FXSAVE_OFFSET); trace_user_setup_rt_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) @@ -405,17 +490,21 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(addr, &frame->puc); #endif if (ka->sa_flags & TARGET_SA_SIGINFO) { - tswap_siginfo(&frame->info, info); + frame->info = *info; } /* Create the ucontext. */ - __put_user(0, &frame->uc.tuc_flags); + if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) { + __put_user(1, &frame->uc.tuc_flags); + } else { + __put_user(0, &frame->uc.tuc_flags); + } __put_user(0, &frame->uc.tuc_link); target_save_altstack(&frame->uc.tuc_stack, env); setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate, env, set->sig[0], frame_addr + offsetof(struct rt_sigframe, fpstate)); - for(i = 0; i < TARGET_NSIG_WORDS; i++) { + for (i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); } @@ -463,10 +552,37 @@ give_sigsegv: force_sigsegv(sig); } +static int xrstor_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxsave, + abi_ulong fxsave_addr) +{ + if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) { + uint32_t extended_size = tswapl(fxsave->sw_reserved.extended_size); + uint32_t xstate_size = tswapl(fxsave->sw_reserved.xstate_size); + uint32_t xfeatures_size = xstate_size - TARGET_FXSAVE_SIZE; + + /* Linux checks MAGIC2 using xstate_size, not extended_size. */ + if (tswapl(fxsave->sw_reserved.magic1) == TARGET_FP_XSTATE_MAGIC1 && + extended_size >= TARGET_FPSTATE_FXSAVE_OFFSET + xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE) { + if (!access_ok(env_cpu(env), VERIFY_READ, fxsave_addr, + extended_size - TARGET_FPSTATE_FXSAVE_OFFSET)) { + return 1; + } + if (tswapl(*(uint32_t *) &fxsave->xfeatures[xfeatures_size]) == TARGET_FP_XSTATE_MAGIC2) { + cpu_x86_xrstor(env, fxsave_addr); + return 0; + } + } + /* fall through to fxrstor */ + } + + cpu_x86_fxrstor(env, fxsave_addr); + return 0; +} + static int restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) { - unsigned int err = 0; + int err = 1; abi_ulong fpstate_addr; unsigned int tmpflags; @@ -517,20 +633,28 @@ restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) fpstate_addr = tswapl(sc->fpstate); if (fpstate_addr != 0) { - if (!access_ok(env_cpu(env), VERIFY_READ, fpstate_addr, - sizeof(struct target_fpstate))) { - goto badframe; + struct target_fpstate *fpstate; + if (!lock_user_struct(VERIFY_READ, fpstate, fpstate_addr, + sizeof(struct target_fpstate))) { + return err; } #ifndef TARGET_X86_64 - cpu_x86_frstor(env, fpstate_addr, 1); + if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) { + cpu_x86_frstor(env, fpstate_addr, 1); + err = 0; + } else { + err = xrstor_sigcontext(env, &fpstate->fxsave, + fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET); + } #else - cpu_x86_fxrstor(env, fpstate_addr); + err = xrstor_sigcontext(env, fpstate, fpstate_addr); #endif + unlock_user_struct(fpstate, fpstate_addr, 0); + } else { + err = 0; } return err; -badframe: - return 1; } /* Note: there is no sigreturn on x86_64, there is only rt_sigreturn */ diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index 1c6142e7da..238a9aba73 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -9,6 +9,6 @@ #define I386_TARGET_ELF_H static inline const char *cpu_get_model(uint32_t eflags) { - return "qemu32"; + return "max"; } #endif diff --git a/linux-user/i386/target_mman.h b/linux-user/i386/target_mman.h new file mode 100644 index 0000000000..e3b8e1eaa6 --- /dev/null +++ b/linux-user/i386/target_mman.h @@ -0,0 +1,17 @@ +/* + * arch/x86/include/asm/processor.h: + * TASK_UNMAPPED_BASE __TASK_UNMAPPED_BASE(TASK_SIZE_LOW) + * __TASK_UNMAPPED_BASE(S) PAGE_ALIGN(S / 3) + * + * arch/x86/include/asm/page_32_types.h: + * TASK_SIZE_LOW TASK_SIZE + * TASK_SIZE __PAGE_OFFSET + * __PAGE_OFFSET CONFIG_PAGE_OFFSET + * CONFIG_PAGE_OFFSET 0xc0000000 (default in Kconfig) + */ +#define TASK_UNMAPPED_BASE 0x40000000 + +/* arch/x86/include/asm/elf.h */ +#define ELF_ET_DYN_BASE 0x00400000 + +#include "../generic/target_mman.h" diff --git a/linux-user/i386/target_proc.h b/linux-user/i386/target_proc.h new file mode 100644 index 0000000000..43fe29ca72 --- /dev/null +++ b/linux-user/i386/target_proc.h @@ -0,0 +1 @@ +/* No target-specific /proc support */ diff --git a/linux-user/i386/vdso-asmoffset.h b/linux-user/i386/vdso-asmoffset.h new file mode 100644 index 0000000000..4e5ee0dd49 --- /dev/null +++ b/linux-user/i386/vdso-asmoffset.h @@ -0,0 +1,6 @@ +/* + * offsetof(struct sigframe, sc.eip) + * offsetof(struct rt_sigframe, uc.tuc_mcontext.eip) + */ +#define SIGFRAME_SIGCONTEXT_eip 64 +#define RT_SIGFRAME_SIGCONTEXT_eip 220 diff --git a/linux-user/i386/vdso.S b/linux-user/i386/vdso.S new file mode 100644 index 0000000000..e7a1f333a1 --- /dev/null +++ b/linux-user/i386/vdso.S @@ -0,0 +1,143 @@ +/* + * i386 linux replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "vdso-asmoffset.h" + +.macro endf name + .globl \name + .type \name, @function + .size \name, . - \name +.endm + +.macro vdso_syscall1 name, nr +\name: + .cfi_startproc + mov %ebx, %edx + .cfi_register %ebx, %edx + mov 4(%esp), %ebx + mov $\nr, %eax + int $0x80 + mov %edx, %ebx + ret + .cfi_endproc +endf \name +.endm + +.macro vdso_syscall2 name, nr +\name: + .cfi_startproc + mov %ebx, %edx + .cfi_register %ebx, %edx + mov 4(%esp), %ebx + mov 8(%esp), %ecx + mov $\nr, %eax + int $0x80 + mov %edx, %ebx + ret + .cfi_endproc +endf \name +.endm + +.macro vdso_syscall3 name, nr +\name: + .cfi_startproc + push %ebx + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset %ebx, 0 + mov 8(%esp), %ebx + mov 12(%esp), %ecx + mov 16(%esp), %edx + mov $\nr, %eax + int $0x80 + pop %ebx + .cfi_adjust_cfa_offset -4 + .cfi_restore %ebx + ret + .cfi_endproc +endf \name +.endm + +__kernel_vsyscall: + .cfi_startproc + int $0x80 + ret + .cfi_endproc +endf __kernel_vsyscall + +vdso_syscall2 __vdso_clock_gettime, __NR_clock_gettime +vdso_syscall2 __vdso_clock_gettime64, __NR_clock_gettime64 +vdso_syscall2 __vdso_clock_getres, __NR_clock_getres +vdso_syscall2 __vdso_gettimeofday, __NR_gettimeofday +vdso_syscall1 __vdso_time, __NR_time +vdso_syscall3 __vdso_getcpu, __NR_gettimeofday + +/* + * Signal return handlers. + */ + + .cfi_startproc simple + .cfi_signal_frame + +/* + * For convenience, put the cfa just above eip in sigcontext, and count + * offsets backward from there. Re-compute the cfa in the two contexts + * we have for signal unwinding. This is far simpler than the + * DW_CFA_expression form that the kernel uses, and is equally correct. + */ + + .cfi_def_cfa %esp, SIGFRAME_SIGCONTEXT_eip + 4 + + .cfi_offset %eip, -4 + /* err, -8 */ + /* trapno, -12 */ + .cfi_offset %eax, -16 + .cfi_offset %ecx, -20 + .cfi_offset %edx, -24 + .cfi_offset %ebx, -28 + .cfi_offset %esp, -32 + .cfi_offset %ebp, -36 + .cfi_offset %esi, -40 + .cfi_offset %edi, -44 + +/* + * While this frame is marked as a signal frame, that only applies to how + * the return address is handled for the outer frame. The return address + * that arrived here, from the inner frame, is not marked as a signal frame + * and so the unwinder still tries to subtract 1 to examine the presumed + * call insn. Thus we must extend the unwind info to a nop before the start. + */ + nop + +__kernel_sigreturn: + popl %eax /* pop sig */ + .cfi_adjust_cfa_offset -4 + movl $__NR_sigreturn, %eax + int $0x80 +endf __kernel_sigreturn + + .cfi_def_cfa_offset RT_SIGFRAME_SIGCONTEXT_eip + 4 + nop + +__kernel_rt_sigreturn: + movl $__NR_rt_sigreturn, %eax + int $0x80 +endf __kernel_rt_sigreturn + + .cfi_endproc + +/* + * TODO: Add elf notes. E.g. + * + * #include + * ELFNOTE_START(Linux, 0, "a") + * .long LINUX_VERSION_CODE + * ELFNOTE_END + * + * but what version number would we set for QEMU? + */ diff --git a/linux-user/i386/vdso.ld b/linux-user/i386/vdso.ld new file mode 100644 index 0000000000..326b7a8f98 --- /dev/null +++ b/linux-user/i386/vdso.ld @@ -0,0 +1,76 @@ +/* + * Linker script for linux i386 replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +ENTRY(__kernel_vsyscall) + +VERSION { + LINUX_2.6 { + global: + __vdso_clock_gettime; + __vdso_gettimeofday; + __vdso_time; + __vdso_clock_getres; + __vdso_clock_gettime64; + __vdso_getcpu; + }; + + LINUX_2.5 { + global: + __kernel_vsyscall; + __kernel_sigreturn; + __kernel_rt_sigreturn; + local: *; + }; +} + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; /* FLAGS=RWX */ + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + .data : { + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load =0x90909090 +} diff --git a/linux-user/i386/vdso.so b/linux-user/i386/vdso.so new file mode 100755 index 0000000000..bdece5dfcf Binary files /dev/null and b/linux-user/i386/vdso.so differ diff --git a/linux-user/include/host/alpha/host-signal.h b/linux-user/include/host/alpha/host-signal.h deleted file mode 100644 index 4f9e2abc4b..0000000000 --- a/linux-user/include/host/alpha/host-signal.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * host-signal.h: signal info dependent on the host architecture - * - * Copyright (c) 2003-2005 Fabrice Bellard - * Copyright (c) 2021 Linaro Limited - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef ALPHA_HOST_SIGNAL_H -#define ALPHA_HOST_SIGNAL_H - -/* The third argument to a SA_SIGINFO handler is ucontext_t. */ -typedef ucontext_t host_sigcontext; - -static inline uintptr_t host_signal_pc(host_sigcontext *uc) -{ - return uc->uc_mcontext.sc_pc; -} - -static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) -{ - uc->uc_mcontext.sc_pc = pc; -} - -static inline void *host_signal_mask(host_sigcontext *uc) -{ - return &uc->uc_sigmask; -} - -static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) -{ - uint32_t *pc = (uint32_t *)host_signal_pc(uc); - uint32_t insn = *pc; - - /* XXX: need kernel patch to get write flag faster */ - switch (insn >> 26) { - case 0x0d: /* stw */ - case 0x0e: /* stb */ - case 0x0f: /* stq_u */ - case 0x24: /* stf */ - case 0x25: /* stg */ - case 0x26: /* sts */ - case 0x27: /* stt */ - case 0x2c: /* stl */ - case 0x2d: /* stq */ - case 0x2e: /* stl_c */ - case 0x2f: /* stq_c */ - return true; - } - return false; -} - -#endif diff --git a/linux-user/include/host/ppc/host-signal.h b/linux-user/include/host/ppc/host-signal.h new file mode 100644 index 0000000000..de25c803f5 --- /dev/null +++ b/linux-user/include/host/ppc/host-signal.h @@ -0,0 +1,39 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2022 Linaro Ltd. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef PPC_HOST_SIGNAL_H +#define PPC_HOST_SIGNAL_H + +#include + +/* The third argument to a SA_SIGINFO handler is ucontext_t. */ +typedef ucontext_t host_sigcontext; + +static inline uintptr_t host_signal_pc(host_sigcontext *uc) +{ + return uc->uc_mcontext.regs->nip; +} + +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) +{ + uc->uc_mcontext.regs->nip = pc; +} + +static inline void *host_signal_mask(host_sigcontext *uc) +{ + return &uc->uc_sigmask; +} + +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) +{ + return uc->uc_mcontext.regs->trap != 0x400 + && (uc->uc_mcontext.regs->dsisr & 0x02000000); +} + +#endif diff --git a/linux-user/include/host/s390/host-signal.h b/linux-user/include/host/s390/host-signal.h deleted file mode 100644 index 25fefa00bd..0000000000 --- a/linux-user/include/host/s390/host-signal.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * host-signal.h: signal info dependent on the host architecture - * - * Copyright (c) 2003-2005 Fabrice Bellard - * Copyright (c) 2021 Linaro Limited - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef S390_HOST_SIGNAL_H -#define S390_HOST_SIGNAL_H - -/* The third argument to a SA_SIGINFO handler is ucontext_t. */ -typedef ucontext_t host_sigcontext; - -static inline uintptr_t host_signal_pc(host_sigcontext *uc) -{ - return uc->uc_mcontext.psw.addr; -} - -static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) -{ - uc->uc_mcontext.psw.addr = pc; -} - -static inline void *host_signal_mask(host_sigcontext *uc) -{ - return &uc->uc_sigmask; -} - -static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) -{ - uint16_t *pinsn = (uint16_t *)host_signal_pc(uc); - - /* - * ??? On linux, the non-rt signal handler has 4 (!) arguments instead - * of the normal 2 arguments. The 4th argument contains the "Translation- - * Exception Identification for DAT Exceptions" from the hardware (aka - * "int_parm_long"), which does in fact contain the is_write value. - * The rt signal handler, as far as I can tell, does not give this value - * at all. Not that we could get to it from here even if it were. - * So fall back to parsing instructions. Treat read-modify-write ones as - * writes, which is not fully correct, but for tracking self-modifying code - * this is better than treating them as reads. Checking si_addr page flags - * might be a viable improvement, albeit a racy one. - */ - /* ??? This is not even close to complete. */ - switch (pinsn[0] >> 8) { - case 0x50: /* ST */ - case 0x42: /* STC */ - case 0x40: /* STH */ - case 0x44: /* EX */ - case 0xba: /* CS */ - case 0xbb: /* CDS */ - return true; - case 0xc4: /* RIL format insns */ - switch (pinsn[0] & 0xf) { - case 0xf: /* STRL */ - case 0xb: /* STGRL */ - case 0x7: /* STHRL */ - return true; - } - break; - case 0xc6: /* RIL-b format insns */ - switch (pinsn[0] & 0xf) { - case 0x0: /* EXRL */ - return true; - } - break; - case 0xc8: /* SSF format insns */ - switch (pinsn[0] & 0xf) { - case 0x2: /* CSST */ - return true; - } - break; - case 0xe3: /* RXY format insns */ - switch (pinsn[2] & 0xff) { - case 0x50: /* STY */ - case 0x24: /* STG */ - case 0x72: /* STCY */ - case 0x70: /* STHY */ - case 0x8e: /* STPQ */ - case 0x3f: /* STRVH */ - case 0x3e: /* STRV */ - case 0x2f: /* STRVG */ - return true; - } - break; - case 0xeb: /* RSY format insns */ - switch (pinsn[2] & 0xff) { - case 0x14: /* CSY */ - case 0x30: /* CSG */ - case 0x31: /* CDSY */ - case 0x3e: /* CDSG */ - case 0xe4: /* LANG */ - case 0xe6: /* LAOG */ - case 0xe7: /* LAXG */ - case 0xe8: /* LAAG */ - case 0xea: /* LAALG */ - case 0xf4: /* LAN */ - case 0xf6: /* LAO */ - case 0xf7: /* LAX */ - case 0xfa: /* LAAL */ - case 0xf8: /* LAA */ - return true; - } - break; - } - return false; -} - -#endif diff --git a/linux-user/include/host/s390x/host-signal.h b/linux-user/include/host/s390x/host-signal.h index 0e83f9358d..e6d3ec26dc 100644 --- a/linux-user/include/host/s390x/host-signal.h +++ b/linux-user/include/host/s390x/host-signal.h @@ -1 +1,138 @@ -#include "../s390/host-signal.h" +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef S390_HOST_SIGNAL_H +#define S390_HOST_SIGNAL_H + +/* The third argument to a SA_SIGINFO handler is ucontext_t. */ +typedef ucontext_t host_sigcontext; + +static inline uintptr_t host_signal_pc(host_sigcontext *uc) +{ + return uc->uc_mcontext.psw.addr; +} + +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) +{ + uc->uc_mcontext.psw.addr = pc; +} + +static inline void *host_signal_mask(host_sigcontext *uc) +{ + return &uc->uc_sigmask; +} + +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) +{ + uint16_t *pinsn = (uint16_t *)host_signal_pc(uc); + + /* + * ??? On linux, the non-rt signal handler has 4 (!) arguments instead + * of the normal 2 arguments. The 4th argument contains the "Translation- + * Exception Identification for DAT Exceptions" from the hardware (aka + * "int_parm_long"), which does in fact contain the is_write value. + * The rt signal handler, as far as I can tell, does not give this value + * at all. Not that we could get to it from here even if it were. + * So fall back to parsing instructions. Treat read-modify-write ones as + * writes, which is not fully correct, but for tracking self-modifying code + * this is better than treating them as reads. Checking si_addr page flags + * might be a viable improvement, albeit a racy one. + */ + /* ??? This is not even close to complete. */ + switch (pinsn[0] >> 8) { + case 0x50: /* ST */ + case 0x42: /* STC */ + case 0x40: /* STH */ + case 0x44: /* EX */ + case 0xba: /* CS */ + case 0xbb: /* CDS */ + return true; + case 0xc4: /* RIL format insns */ + switch (pinsn[0] & 0xf) { + case 0xf: /* STRL */ + case 0xb: /* STGRL */ + case 0x7: /* STHRL */ + return true; + } + break; + case 0xc6: /* RIL-b format insns */ + switch (pinsn[0] & 0xf) { + case 0x0: /* EXRL */ + return true; + } + break; + case 0xc8: /* SSF format insns */ + switch (pinsn[0] & 0xf) { + case 0x2: /* CSST */ + return true; + } + break; + case 0xe3: /* RXY format insns */ + switch (pinsn[2] & 0xff) { + case 0x50: /* STY */ + case 0x24: /* STG */ + case 0x72: /* STCY */ + case 0x70: /* STHY */ + case 0x8e: /* STPQ */ + case 0x3f: /* STRVH */ + case 0x3e: /* STRV */ + case 0x2f: /* STRVG */ + return true; + } + break; + case 0xe6: + switch (pinsn[2] & 0xff) { + case 0x09: /* VSTEBRH */ + case 0x0a: /* VSTEBRG */ + case 0x0b: /* VSTEBRF */ + case 0x0e: /* VSTBR */ + case 0x0f: /* VSTER */ + case 0x3f: /* VSTRLR */ + return true; + } + break; + case 0xe7: + switch (pinsn[2] & 0xff) { + case 0x08: /* VSTEB */ + case 0x09: /* VSTEH */ + case 0x0a: /* VSTEG */ + case 0x0b: /* VSTEF */ + case 0x0e: /* VST */ + case 0x1a: /* VSCEG */ + case 0x1b: /* VSCEF */ + case 0x3e: /* VSTM */ + case 0x3f: /* VSTL */ + return true; + } + break; + case 0xeb: /* RSY format insns */ + switch (pinsn[2] & 0xff) { + case 0x14: /* CSY */ + case 0x30: /* CSG */ + case 0x31: /* CDSY */ + case 0x3e: /* CDSG */ + case 0xe4: /* LANG */ + case 0xe6: /* LAOG */ + case 0xe7: /* LAXG */ + case 0xe8: /* LAAG */ + case 0xea: /* LAALG */ + case 0xf4: /* LAN */ + case 0xf6: /* LAO */ + case 0xf7: /* LAX */ + case 0xfa: /* LAAL */ + case 0xf8: /* LAA */ + return true; + } + break; + } + return false; +} + +#endif diff --git a/linux-user/include/host/x32/host-signal.h b/linux-user/include/host/x32/host-signal.h deleted file mode 100644 index 26800591d3..0000000000 --- a/linux-user/include/host/x32/host-signal.h +++ /dev/null @@ -1 +0,0 @@ -#include "../x86_64/host-signal.h" diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index f182d40190..1aec9d5836 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -96,9 +96,7 @@ IOCTL(BLKROGET, IOC_R, MK_PTR(TYPE_INT)) IOCTL(BLKRRPART, 0, TYPE_NULL) IOCTL(BLKGETSIZE, IOC_R, MK_PTR(TYPE_ULONG)) -#ifdef BLKGETSIZE64 IOCTL(BLKGETSIZE64, IOC_R, MK_PTR(TYPE_ULONGLONG)) -#endif IOCTL(BLKFLSBUF, 0, TYPE_NULL) IOCTL(BLKRASET, 0, TYPE_INT) IOCTL(BLKRAGET, IOC_R, MK_PTR(TYPE_LONG)) @@ -107,33 +105,15 @@ IOCTL_SPECIAL(BLKPG, IOC_W, do_ioctl_blkpg, MK_PTR(MK_STRUCT(STRUCT_blkpg_ioctl_arg))) -#ifdef BLKDISCARD IOCTL(BLKDISCARD, IOC_W, MK_PTR(MK_ARRAY(TYPE_ULONGLONG, 2))) -#endif -#ifdef BLKIOMIN IOCTL(BLKIOMIN, IOC_R, MK_PTR(TYPE_INT)) -#endif -#ifdef BLKIOOPT IOCTL(BLKIOOPT, IOC_R, MK_PTR(TYPE_INT)) -#endif -#ifdef BLKALIGNOFF IOCTL(BLKALIGNOFF, IOC_R, MK_PTR(TYPE_INT)) -#endif -#ifdef BLKPBSZGET IOCTL(BLKPBSZGET, IOC_R, MK_PTR(TYPE_INT)) -#endif -#ifdef BLKDISCARDZEROES IOCTL(BLKDISCARDZEROES, IOC_R, MK_PTR(TYPE_INT)) -#endif -#ifdef BLKSECDISCARD IOCTL(BLKSECDISCARD, IOC_W, MK_PTR(MK_ARRAY(TYPE_ULONGLONG, 2))) -#endif -#ifdef BLKROTATIONAL IOCTL(BLKROTATIONAL, IOC_R, MK_PTR(TYPE_SHORT)) -#endif -#ifdef BLKZEROOUT IOCTL(BLKZEROOUT, IOC_W, MK_PTR(MK_ARRAY(TYPE_ULONGLONG, 2))) -#endif IOCTL(FDMSGON, 0, TYPE_NULL) IOCTL(FDMSGOFF, 0, TYPE_NULL) @@ -149,17 +129,19 @@ IOCTL(FDTWADDLE, 0, TYPE_NULL) IOCTL(FDEJECT, 0, TYPE_NULL) -#ifdef FIBMAP IOCTL(FIBMAP, IOC_W | IOC_R, MK_PTR(TYPE_LONG)) -#endif #ifdef FICLONE IOCTL(FICLONE, IOC_W, TYPE_INT) IOCTL(FICLONERANGE, IOC_W, MK_PTR(MK_STRUCT(STRUCT_file_clone_range))) #endif - -#ifdef FIGETBSZ - IOCTL(FIGETBSZ, IOC_R, MK_PTR(TYPE_LONG)) +#ifdef FIFREEZE + IOCTL(FIFREEZE, IOC_W | IOC_R, TYPE_INT) #endif +#ifdef FITHAW + IOCTL(FITHAW, IOC_W | IOC_R, TYPE_INT) +#endif + + IOCTL(FIGETBSZ, IOC_R, MK_PTR(TYPE_LONG)) #ifdef CONFIG_FIEMAP IOCTL_SPECIAL(FS_IOC_FIEMAP, IOC_W | IOC_R, do_ioctl_fs_ioc_fiemap, MK_PTR(MK_STRUCT(STRUCT_fiemap))) diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c index 745cce70ab..37f132be4a 100644 --- a/linux-user/linuxload.c +++ b/linux-user/linuxload.c @@ -3,7 +3,9 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" +#include "user-mmap.h" #include "loader.h" +#include "qapi/error.h" #define NGROUPS 32 @@ -37,7 +39,7 @@ static int prepare_binprm(struct linux_binprm *bprm) int mode; int retval; - if (fstat(bprm->fd, &st) < 0) { + if (fstat(bprm->src.fd, &st) < 0) { return -errno; } @@ -67,7 +69,7 @@ static int prepare_binprm(struct linux_binprm *bprm) bprm->e_gid = st.st_gid; } - retval = read(bprm->fd, bprm->buf, BPRM_BUF_SIZE); + retval = read(bprm->src.fd, bprm->buf, BPRM_BUF_SIZE); if (retval < 0) { perror("prepare_binprm"); exit(-1); @@ -76,6 +78,10 @@ static int prepare_binprm(struct linux_binprm *bprm) /* Make sure the rest of the loader won't read garbage. */ memset(bprm->buf + retval, 0, BPRM_BUF_SIZE - retval); } + + bprm->src.cache = bprm->buf; + bprm->src.cache_size = retval; + return retval; } @@ -83,7 +89,7 @@ static int prepare_binprm(struct linux_binprm *bprm) abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, abi_ulong stringp, int push_ptr) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); int n = sizeof(abi_ulong); abi_ulong envp; abi_ulong argv; @@ -138,7 +144,7 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, { int retval; - bprm->fd = fdexec; + bprm->src.fd = fdexec; bprm->filename = (char *)filename; bprm->argc = count(argv); bprm->argv = argv; @@ -147,29 +153,112 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, retval = prepare_binprm(bprm); - if (retval >= 0) { - if (bprm->buf[0] == 0x7f - && bprm->buf[1] == 'E' - && bprm->buf[2] == 'L' - && bprm->buf[3] == 'F') { - retval = load_elf_binary(bprm, infop); -#if defined(TARGET_HAS_BFLT) - } else if (bprm->buf[0] == 'b' - && bprm->buf[1] == 'F' - && bprm->buf[2] == 'L' - && bprm->buf[3] == 'T') { - retval = load_flt_binary(bprm, infop); -#endif - } else { - return -ENOEXEC; - } + if (retval < 4) { + return -ENOEXEC; } - - if (retval >= 0) { - /* success. Initialize important registers */ - do_init_thread(regs, infop); + if (bprm->buf[0] == 0x7f + && bprm->buf[1] == 'E' + && bprm->buf[2] == 'L' + && bprm->buf[3] == 'F') { + retval = load_elf_binary(bprm, infop); +#if defined(TARGET_HAS_BFLT) + } else if (bprm->buf[0] == 'b' + && bprm->buf[1] == 'F' + && bprm->buf[2] == 'L' + && bprm->buf[3] == 'T') { + retval = load_flt_binary(bprm, infop); +#endif + } else { + return -ENOEXEC; + } + if (retval < 0) { return retval; } - return retval; + /* Success. Initialize important registers. */ + do_init_thread(regs, infop); + return 0; +} + +bool imgsrc_read(void *dst, off_t offset, size_t len, + const ImageSource *img, Error **errp) +{ + ssize_t ret; + + if (offset + len <= img->cache_size) { + memcpy(dst, img->cache + offset, len); + return true; + } + + if (img->fd < 0) { + error_setg(errp, "read past end of buffer"); + return false; + } + + ret = pread(img->fd, dst, len, offset); + if (ret == len) { + return true; + } + if (ret < 0) { + error_setg_errno(errp, errno, "Error reading file header"); + } else { + error_setg(errp, "Incomplete read of file header"); + } + return false; +} + +void *imgsrc_read_alloc(off_t offset, size_t len, + const ImageSource *img, Error **errp) +{ + void *alloc = g_malloc(len); + bool ok = imgsrc_read(alloc, offset, len, img, errp); + + if (!ok) { + g_free(alloc); + alloc = NULL; + } + return alloc; +} + +abi_long imgsrc_mmap(abi_ulong start, abi_ulong len, int prot, + int flags, const ImageSource *src, abi_ulong offset) +{ + const int prot_write = PROT_READ | PROT_WRITE; + abi_long ret; + void *haddr; + + assert(flags == (MAP_PRIVATE | MAP_FIXED)); + + if (src->fd >= 0) { + return target_mmap(start, len, prot, flags, src->fd, offset); + } + + /* + * This case is for the vdso; we don't expect bad images. + * The mmap may extend beyond the end of the image, especially + * to the end of the page. Zero fill. + */ + assert(offset < src->cache_size); + + ret = target_mmap(start, len, prot_write, flags | MAP_ANON, -1, 0); + if (ret == -1) { + return ret; + } + + haddr = lock_user(VERIFY_WRITE, start, len, 0); + assert(haddr != NULL); + if (offset + len <= src->cache_size) { + memcpy(haddr, src->cache + offset, len); + } else { + size_t rest = src->cache_size - offset; + memcpy(haddr, src->cache + offset, rest); + memset(haddr + rest, 0, len - rest); + } + unlock_user(haddr, start, len); + + if (prot != prot_write) { + target_mprotect(start, len, prot); + } + + return ret; } diff --git a/linux-user/loader.h b/linux-user/loader.h index f375ee0679..e102e6f410 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -18,6 +18,48 @@ #ifndef LINUX_USER_LOADER_H #define LINUX_USER_LOADER_H +typedef struct { + const void *cache; + unsigned int cache_size; + int fd; +} ImageSource; + +/** + * imgsrc_read: Read from ImageSource + * @dst: destination for read + * @offset: offset within file for read + * @len: size of the read + * @img: ImageSource to read from + * @errp: Error details. + * + * Read into @dst, using the cache when possible. + */ +bool imgsrc_read(void *dst, off_t offset, size_t len, + const ImageSource *img, Error **errp); + +/** + * imgsrc_read_alloc: Read from ImageSource + * @offset: offset within file for read + * @size: size of the read + * @img: ImageSource to read from + * @errp: Error details. + * + * Read into newly allocated memory, using the cache when possible. + */ +void *imgsrc_read_alloc(off_t offset, size_t len, + const ImageSource *img, Error **errp); + +/** + * imgsrc_mmap: Map from ImageSource + * + * If @src has a file descriptor, pass on to target_mmap. Otherwise, + * this is "mapping" from a host buffer, which resolves to memcpy. + * Therefore, flags must be MAP_PRIVATE | MAP_FIXED; the argument is + * retained for clarity. + */ +abi_long imgsrc_mmap(abi_ulong start, abi_ulong len, int prot, + int flags, const ImageSource *src, abi_ulong offset); + /* * Read a good amount of data initially, to hopefully get all the * program headers loaded. @@ -29,15 +71,15 @@ * used when loading binaries. */ struct linux_binprm { - char buf[BPRM_BUF_SIZE] __attribute__((aligned)); - abi_ulong p; - int fd; - int e_uid, e_gid; - int argc, envc; - char **argv; - char **envp; - char *filename; /* Name of binary */ - int (*core_dump)(int, const CPUArchState *); /* coredump routine */ + char buf[BPRM_BUF_SIZE] __attribute__((aligned)); + ImageSource src; + abi_ulong p; + int e_uid, e_gid; + int argc, envc; + char **argv; + char **envp; + char *filename; /* Name of binary */ + int (*core_dump)(int, const CPUArchState *); /* coredump routine */ }; void do_init_thread(struct target_pt_regs *regs, struct image_info *infop); @@ -56,4 +98,13 @@ abi_long memcpy_to_target(abi_ulong dest, const void *src, extern unsigned long guest_stack_size; +#if defined(TARGET_S390X) || defined(TARGET_AARCH64) || defined(TARGET_ARM) +uint32_t get_elf_hwcap(void); +const char *elf_hwcap_str(uint32_t bit); +#endif +#if defined(TARGET_AARCH64) || defined(TARGET_ARM) +uint64_t get_elf_hwcap2(void); +const char *elf_hwcap2_str(uint32_t bit); +#endif + #endif /* LINUX_USER_LOADER_H */ diff --git a/linux-user/loongarch64/Makefile.vdso b/linux-user/loongarch64/Makefile.vdso new file mode 100644 index 0000000000..369de13344 --- /dev/null +++ b/linux-user/loongarch64/Makefile.vdso @@ -0,0 +1,11 @@ +include $(BUILD_DIR)/tests/tcg/loongarch64-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/loongarch64 +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso.so + +$(SUBDIR)/vdso.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ -nostdlib -shared -fpic -Wl,-h,linux-vdso.so.1 \ + -Wl,--build-id=sha1 -Wl,--hash-style=both \ + -Wl,--no-warn-rwx-segments -Wl,-T,$(SUBDIR)/vdso.ld $< diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c new file mode 100644 index 0000000000..73d7b6796a --- /dev/null +++ b/linux-user/loongarch64/cpu_loop.c @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch user cpu_loop. + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "user-internals.h" +#include "cpu_loop-common.h" +#include "signal-common.h" + +void cpu_loop(CPULoongArchState *env) +{ + CPUState *cs = env_cpu(env); + int trapnr, si_code; + abi_long ret; + + for (;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCCODE_SYS: + env->pc += 4; + ret = do_syscall(env, env->gpr[11], + env->gpr[4], env->gpr[5], + env->gpr[6], env->gpr[7], + env->gpr[8], env->gpr[9], + -1, -1); + if (ret == -QEMU_ERESTARTSYS) { + env->pc -= 4; + break; + } + if (ret == -QEMU_ESIGRETURN) { + /* + * Returning from a successful sigreturn syscall. + * Avoid clobbering register state. + */ + break; + } + env->gpr[4] = ret; + break; + case EXCCODE_INE: + force_sig_fault(TARGET_SIGILL, 0, env->pc); + break; + case EXCCODE_FPE: + si_code = TARGET_FPE_FLTUNK; + if (GET_FP_CAUSE(env->fcsr0) & FP_INVALID) { + si_code = TARGET_FPE_FLTINV; + } else if (GET_FP_CAUSE(env->fcsr0) & FP_DIV0) { + si_code = TARGET_FPE_FLTDIV; + } else if (GET_FP_CAUSE(env->fcsr0) & FP_OVERFLOW) { + si_code = TARGET_FPE_FLTOVF; + } else if (GET_FP_CAUSE(env->fcsr0) & FP_UNDERFLOW) { + si_code = TARGET_FPE_FLTUND; + } else if (GET_FP_CAUSE(env->fcsr0) & FP_INEXACT) { + si_code = TARGET_FPE_FLTRES; + } + force_sig_fault(TARGET_SIGFPE, si_code, env->pc); + break; + case EXCP_DEBUG: + case EXCCODE_BRK: + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); + break; + case EXCCODE_BCE: + force_sig_fault(TARGET_SIGSYS, TARGET_SI_KERNEL, env->pc); + break; + + /* + * Begin with LSX and LASX disabled, then enable on the first trap. + * In this way we can tell if the unit is in use. This is used to + * choose the layout of any signal frame. + */ + case EXCCODE_SXD: + env->CSR_EUEN |= R_CSR_EUEN_SXE_MASK; + break; + case EXCCODE_ASXD: + env->CSR_EUEN |= R_CSR_EUEN_ASXE_MASK; + break; + + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", + trapnr); + exit(EXIT_FAILURE); + } + process_pending_signals(env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + int i; + + for (i = 0; i < 32; i++) { + env->gpr[i] = regs->regs[i]; + } + env->pc = regs->csr.era; + +} diff --git a/linux-user/loongarch64/meson.build b/linux-user/loongarch64/meson.build new file mode 100644 index 0000000000..17896535f0 --- /dev/null +++ b/linux-user/loongarch64/meson.build @@ -0,0 +1,4 @@ +vdso_inc = gen_vdso.process('vdso.so', + extra_args: ['-r', '__vdso_rt_sigreturn']) + +linux_user_ss.add(when: 'TARGET_LOONGARCH64', if_true: vdso_inc) diff --git a/linux-user/loongarch64/signal.c b/linux-user/loongarch64/signal.c new file mode 100644 index 0000000000..1a322f9697 --- /dev/null +++ b/linux-user/loongarch64/signal.c @@ -0,0 +1,454 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch emulation of Linux signals + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "user-internals.h" +#include "signal-common.h" +#include "linux-user/trace.h" +#include "target/loongarch/internals.h" +#include "target/loongarch/vec.h" +#include "vdso-asmoffset.h" + +/* FP context was used */ +#define SC_USED_FP (1 << 0) + +struct target_sigcontext { + abi_ulong sc_pc; + abi_ulong sc_regs[32]; + abi_uint sc_flags; + abi_ulong sc_extcontext[0] QEMU_ALIGNED(16); +}; + +QEMU_BUILD_BUG_ON(sizeof(struct target_sigcontext) != sizeof_sigcontext); +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_pc) + != offsetof_sigcontext_pc); +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_regs) + != offsetof_sigcontext_gr); + +#define FPU_CTX_MAGIC 0x46505501 +#define FPU_CTX_ALIGN 8 +struct target_fpu_context { + abi_ulong regs[32]; + abi_ulong fcc; + abi_uint fcsr; +} QEMU_ALIGNED(FPU_CTX_ALIGN); + +QEMU_BUILD_BUG_ON(offsetof(struct target_fpu_context, regs) + != offsetof_fpucontext_fr); + +#define LSX_CTX_MAGIC 0x53580001 +#define LSX_CTX_ALIGN 16 +struct target_lsx_context { + abi_ulong regs[2 * 32]; + abi_ulong fcc; + abi_uint fcsr; +} QEMU_ALIGNED(LSX_CTX_ALIGN); + +#define LASX_CTX_MAGIC 0x41535801 +#define LASX_CTX_ALIGN 32 +struct target_lasx_context { + abi_ulong regs[4 * 32]; + abi_ulong fcc; + abi_uint fcsr; +} QEMU_ALIGNED(LASX_CTX_ALIGN); + +#define CONTEXT_INFO_ALIGN 16 +struct target_sctx_info { + abi_uint magic; + abi_uint size; + abi_ulong padding; +} QEMU_ALIGNED(CONTEXT_INFO_ALIGN); + +QEMU_BUILD_BUG_ON(sizeof(struct target_sctx_info) != sizeof_sctx_info); + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ptr tuc_link; + target_stack_t tuc_stack; + target_sigset_t tuc_sigmask; + uint8_t __unused[1024 / 8 - sizeof(target_sigset_t)]; + struct target_sigcontext tuc_mcontext; +}; + +struct target_rt_sigframe { + struct target_siginfo rs_info; + struct target_ucontext rs_uc; +}; + +QEMU_BUILD_BUG_ON(sizeof(struct target_rt_sigframe) + != sizeof_rt_sigframe); +QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, rs_uc.tuc_mcontext) + != offsetof_sigcontext); + +/* + * These two structures are not present in guest memory, are private + * to the signal implementation, but are largely copied from the + * kernel's signal implementation. + */ +struct ctx_layout { + void *haddr; + abi_ptr gaddr; + unsigned int size; +}; + +struct extctx_layout { + unsigned long size; + unsigned int flags; + struct ctx_layout fpu; + struct ctx_layout lsx; + struct ctx_layout lasx; + struct ctx_layout end; +}; + +static abi_ptr extframe_alloc(struct extctx_layout *extctx, + struct ctx_layout *sctx, unsigned size, + unsigned align, abi_ptr orig_sp) +{ + abi_ptr sp = orig_sp; + + sp -= sizeof(struct target_sctx_info) + size; + align = MAX(align, CONTEXT_INFO_ALIGN); + sp = ROUND_DOWN(sp, align); + sctx->gaddr = sp; + + size = orig_sp - sp; + sctx->size = size; + extctx->size += size; + + return sp; +} + +static abi_ptr setup_extcontext(CPULoongArchState *env, + struct extctx_layout *extctx, abi_ptr sp) +{ + memset(extctx, 0, sizeof(struct extctx_layout)); + + /* Grow down, alloc "end" context info first. */ + sp = extframe_alloc(extctx, &extctx->end, 0, CONTEXT_INFO_ALIGN, sp); + + /* For qemu, there is no lazy fp context switch, so fp always present. */ + extctx->flags = SC_USED_FP; + + if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) { + sp = extframe_alloc(extctx, &extctx->lasx, + sizeof(struct target_lasx_context), LASX_CTX_ALIGN, sp); + } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { + sp = extframe_alloc(extctx, &extctx->lsx, + sizeof(struct target_lsx_context), LSX_CTX_ALIGN, sp); + } else { + sp = extframe_alloc(extctx, &extctx->fpu, + sizeof(struct target_fpu_context), FPU_CTX_ALIGN, sp); + } + + return sp; +} + +static void setup_sigframe(CPULoongArchState *env, + struct target_sigcontext *sc, + struct extctx_layout *extctx) +{ + struct target_sctx_info *info; + int i; + + __put_user(extctx->flags, &sc->sc_flags); + __put_user(env->pc, &sc->sc_pc); + __put_user(0, &sc->sc_regs[0]); + for (i = 1; i < 32; ++i) { + __put_user(env->gpr[i], &sc->sc_regs[i]); + } + + /* + * Set extension context + */ + + if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) { + struct target_lasx_context *lasx_ctx; + info = extctx->lasx.haddr; + + __put_user(LASX_CTX_MAGIC, &info->magic); + __put_user(extctx->lasx.size, &info->size); + + lasx_ctx = (struct target_lasx_context *)(info + 1); + + for (i = 0; i < 32; ++i) { + __put_user(env->fpr[i].vreg.UD(0), &lasx_ctx->regs[4 * i]); + __put_user(env->fpr[i].vreg.UD(1), &lasx_ctx->regs[4 * i + 1]); + __put_user(env->fpr[i].vreg.UD(2), &lasx_ctx->regs[4 * i + 2]); + __put_user(env->fpr[i].vreg.UD(3), &lasx_ctx->regs[4 * i + 3]); + } + __put_user(read_fcc(env), &lasx_ctx->fcc); + __put_user(env->fcsr0, &lasx_ctx->fcsr); + } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { + struct target_lsx_context *lsx_ctx; + info = extctx->lsx.haddr; + + __put_user(LSX_CTX_MAGIC, &info->magic); + __put_user(extctx->lsx.size, &info->size); + + lsx_ctx = (struct target_lsx_context *)(info + 1); + + for (i = 0; i < 32; ++i) { + __put_user(env->fpr[i].vreg.UD(0), &lsx_ctx->regs[2 * i]); + __put_user(env->fpr[i].vreg.UD(1), &lsx_ctx->regs[2 * i + 1]); + } + __put_user(read_fcc(env), &lsx_ctx->fcc); + __put_user(env->fcsr0, &lsx_ctx->fcsr); + } else { + struct target_fpu_context *fpu_ctx; + info = extctx->fpu.haddr; + + __put_user(FPU_CTX_MAGIC, &info->magic); + __put_user(extctx->fpu.size, &info->size); + + fpu_ctx = (struct target_fpu_context *)(info + 1); + + for (i = 0; i < 32; ++i) { + __put_user(env->fpr[i].vreg.UD(0), &fpu_ctx->regs[i]); + } + __put_user(read_fcc(env), &fpu_ctx->fcc); + __put_user(env->fcsr0, &fpu_ctx->fcsr); + } + + /* + * Set end context + */ + info = extctx->end.haddr; + __put_user(0, &info->magic); + __put_user(0, &info->size); +} + +static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame) +{ + memset(extctx, 0, sizeof(*extctx)); + + while (1) { + abi_uint magic, size; + + if (get_user_u32(magic, frame) || get_user_u32(size, frame + 4)) { + return false; + } + + switch (magic) { + case 0: /* END */ + extctx->end.gaddr = frame; + extctx->end.size = size; + extctx->size += size; + return true; + + case FPU_CTX_MAGIC: + if (size < (sizeof(struct target_sctx_info) + + sizeof(struct target_fpu_context))) { + return false; + } + extctx->fpu.gaddr = frame; + extctx->fpu.size = size; + extctx->size += size; + break; + case LSX_CTX_MAGIC: + if (size < (sizeof(struct target_sctx_info) + + sizeof(struct target_lsx_context))) { + return false; + } + extctx->lsx.gaddr = frame; + extctx->lsx.size = size; + extctx->size += size; + break; + case LASX_CTX_MAGIC: + if (size < (sizeof(struct target_sctx_info) + + sizeof(struct target_lasx_context))) { + return false; + } + extctx->lasx.gaddr = frame; + extctx->lasx.size = size; + extctx->size += size; + break; + default: + return false; + } + + frame += size; + } +} + +static void restore_sigframe(CPULoongArchState *env, + struct target_sigcontext *sc, + struct extctx_layout *extctx) +{ + int i; + abi_ulong fcc; + + __get_user(env->pc, &sc->sc_pc); + for (i = 1; i < 32; ++i) { + __get_user(env->gpr[i], &sc->sc_regs[i]); + } + + if (extctx->lasx.haddr) { + struct target_lasx_context *lasx_ctx = + extctx->lasx.haddr + sizeof(struct target_sctx_info); + + for (i = 0; i < 32; ++i) { + __get_user(env->fpr[i].vreg.UD(0), &lasx_ctx->regs[4 * i]); + __get_user(env->fpr[i].vreg.UD(1), &lasx_ctx->regs[4 * i + 1]); + __get_user(env->fpr[i].vreg.UD(2), &lasx_ctx->regs[4 * i + 2]); + __get_user(env->fpr[i].vreg.UD(3), &lasx_ctx->regs[4 * i + 3]); + } + __get_user(fcc, &lasx_ctx->fcc); + write_fcc(env, fcc); + __get_user(env->fcsr0, &lasx_ctx->fcsr); + restore_fp_status(env); + } else if (extctx->lsx.haddr) { + struct target_lsx_context *lsx_ctx = + extctx->lsx.haddr + sizeof(struct target_sctx_info); + + for (i = 0; i < 32; ++i) { + __get_user(env->fpr[i].vreg.UD(0), &lsx_ctx->regs[2 * i]); + __get_user(env->fpr[i].vreg.UD(1), &lsx_ctx->regs[2 * i + 1]); + } + __get_user(fcc, &lsx_ctx->fcc); + write_fcc(env, fcc); + __get_user(env->fcsr0, &lsx_ctx->fcsr); + restore_fp_status(env); + } else if (extctx->fpu.haddr) { + struct target_fpu_context *fpu_ctx = + extctx->fpu.haddr + sizeof(struct target_sctx_info); + + for (i = 0; i < 32; ++i) { + __get_user(env->fpr[i].vreg.UD(0), &fpu_ctx->regs[i]); + } + __get_user(fcc, &fpu_ctx->fcc); + write_fcc(env, fcc); + __get_user(env->fcsr0, &fpu_ctx->fcsr); + restore_fp_status(env); + } +} + +/* + * Determine which stack to use. + */ +static abi_ptr get_sigframe(struct target_sigaction *ka, + CPULoongArchState *env, + struct extctx_layout *extctx) +{ + abi_ulong sp; + + sp = target_sigsp(get_sp_from_cpustate(env), ka); + sp = ROUND_DOWN(sp, 16); + sp = setup_extcontext(env, extctx, sp); + sp -= sizeof(struct target_rt_sigframe); + + assert(QEMU_IS_ALIGNED(sp, 16)); + + return sp; +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPULoongArchState *env) +{ + struct target_rt_sigframe *frame; + struct extctx_layout extctx; + abi_ptr frame_addr; + int i; + + frame_addr = get_sigframe(ka, env, &extctx); + trace_user_setup_rt_frame(env, frame_addr); + + frame = lock_user(VERIFY_WRITE, frame_addr, + sizeof(*frame) + extctx.size, 0); + if (!frame) { + force_sigsegv(sig); + return; + } + + if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) { + extctx.lasx.haddr = (void *)frame + (extctx.lasx.gaddr - frame_addr); + extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr); + } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { + extctx.lsx.haddr = (void *)frame + (extctx.lsx.gaddr - frame_addr); + extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr); + } else { + extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr); + extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr); + } + + frame->rs_info = *info; + + __put_user(0, &frame->rs_uc.tuc_flags); + __put_user(0, &frame->rs_uc.tuc_link); + target_save_altstack(&frame->rs_uc.tuc_stack, env); + + setup_sigframe(env, &frame->rs_uc.tuc_mcontext, &extctx); + + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]); + } + + env->gpr[4] = sig; + env->gpr[5] = frame_addr + offsetof(struct target_rt_sigframe, rs_info); + env->gpr[6] = frame_addr + offsetof(struct target_rt_sigframe, rs_uc); + env->gpr[3] = frame_addr; + env->gpr[1] = default_rt_sigreturn; + + env->pc = ka->_sa_handler; + unlock_user(frame, frame_addr, sizeof(*frame) + extctx.size); +} + +long do_rt_sigreturn(CPULoongArchState *env) +{ + struct target_rt_sigframe *frame; + struct extctx_layout extctx; + abi_ulong frame_addr; + sigset_t blocked; + + frame_addr = env->gpr[3]; + trace_user_do_rt_sigreturn(env, frame_addr); + + if (!parse_extcontext(&extctx, frame_addr + sizeof(*frame))) { + goto badframe; + } + + frame = lock_user(VERIFY_READ, frame_addr, + sizeof(*frame) + extctx.size, 1); + if (!frame) { + goto badframe; + } + + if (extctx.lasx.gaddr) { + extctx.lasx.haddr = (void *)frame + (extctx.lasx.gaddr - frame_addr); + } else if (extctx.lsx.gaddr) { + extctx.lsx.haddr = (void *)frame + (extctx.lsx.gaddr - frame_addr); + } else if (extctx.fpu.gaddr) { + extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr); + } + + target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask); + set_sigmask(&blocked); + + restore_sigframe(env, &frame->rs_uc.tuc_mcontext, &extctx); + + target_restore_altstack(&frame->rs_uc.tuc_stack, env); + + unlock_user(frame, frame_addr, 0); + return -QEMU_ESIGRETURN; + + badframe: + force_sig(TARGET_SIGSEGV); + return -QEMU_ESIGRETURN; +} + +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0); + assert(tramp != NULL); + + __put_user(0x03822c0b, tramp + 0); /* ori a7, zero, 0x8b */ + __put_user(0x002b0000, tramp + 1); /* syscall 0 */ + + default_rt_sigreturn = sigtramp_page; + unlock_user(tramp, sigtramp_page, 8); +} diff --git a/linux-user/loongarch64/sockbits.h b/linux-user/loongarch64/sockbits.h new file mode 100644 index 0000000000..1cffcae120 --- /dev/null +++ b/linux-user/loongarch64/sockbits.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_SOCKBITS_H +#define LOONGARCH_TARGET_SOCKBITS_H + +#include "../generic/sockbits.h" + +#endif diff --git a/linux-user/loongarch64/syscall_nr.h b/linux-user/loongarch64/syscall_nr.h new file mode 100644 index 0000000000..be00915adf --- /dev/null +++ b/linux-user/loongarch64/syscall_nr.h @@ -0,0 +1,312 @@ +/* + * This file contains the system call numbers. + * Do not modify. + * This file is generated by scripts/gensyscalls.sh + */ +#ifndef LINUX_USER_LOONGARCH_SYSCALL_NR_H +#define LINUX_USER_LOONGARCH_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_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_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_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_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_close_range 436 +#define TARGET_NR_openat2 437 +#define TARGET_NR_pidfd_getfd 438 +#define TARGET_NR_faccessat2 439 +#define TARGET_NR_process_madvise 440 +#define TARGET_NR_epoll_pwait2 441 +#define TARGET_NR_mount_setattr 442 +#define TARGET_NR_quotactl_fd 443 +#define TARGET_NR_landlock_create_ruleset 444 +#define TARGET_NR_landlock_add_rule 445 +#define TARGET_NR_landlock_restrict_self 446 +#define TARGET_NR_process_mrelease 448 +#define TARGET_NR_futex_waitv 449 +#define TARGET_NR_set_mempolicy_home_node 450 +#define TARGET_NR_syscalls 451 + +#endif /* LINUX_USER_LOONGARCH_SYSCALL_NR_H */ diff --git a/linux-user/loongarch64/target_cpu.h b/linux-user/loongarch64/target_cpu.h new file mode 100644 index 0000000000..a29af66156 --- /dev/null +++ b/linux-user/loongarch64/target_cpu.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch specific CPU ABI and functions for linux-user + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_CPU_H +#define LOONGARCH_TARGET_CPU_H + +static inline void cpu_clone_regs_child(CPULoongArchState *env, + target_ulong newsp, unsigned flags) +{ + if (newsp) { + env->gpr[3] = newsp; + } + env->gpr[4] = 0; +} + +static inline void cpu_clone_regs_parent(CPULoongArchState *env, + unsigned flags) +{ +} + +static inline void cpu_set_tls(CPULoongArchState *env, target_ulong newtls) +{ + env->gpr[2] = newtls; +} + +static inline abi_ulong get_sp_from_cpustate(CPULoongArchState *state) +{ + return state->gpr[3]; +} +#endif diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h new file mode 100644 index 0000000000..95c3f05a46 --- /dev/null +++ b/linux-user/loongarch64/target_elf.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_ELF_H +#define LOONGARCH_TARGET_ELF_H +static inline const char *cpu_get_model(uint32_t eflags) +{ + return "la464"; +} +#endif diff --git a/linux-user/loongarch64/target_errno_defs.h b/linux-user/loongarch64/target_errno_defs.h new file mode 100644 index 0000000000..c198b8aca9 --- /dev/null +++ b/linux-user/loongarch64/target_errno_defs.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_ERRNO_DEFS_H +#define LOONGARCH_TARGET_ERRNO_DEFS_H + +/* Target uses generic errno */ +#include "../generic/target_errno_defs.h" + +#endif diff --git a/linux-user/loongarch64/target_fcntl.h b/linux-user/loongarch64/target_fcntl.h new file mode 100644 index 0000000000..99bf586854 --- /dev/null +++ b/linux-user/loongarch64/target_fcntl.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_FCNTL_H +#define LOONGARCH_TARGET_FCNTL_H + +#include "../generic/fcntl.h" + +#endif diff --git a/linux-user/loongarch64/target_mman.h b/linux-user/loongarch64/target_mman.h new file mode 100644 index 0000000000..8c2a3d5596 --- /dev/null +++ b/linux-user/loongarch64/target_mman.h @@ -0,0 +1,12 @@ +/* + * arch/loongarch/include/asm/processor.h: + * TASK_UNMAPPED_BASE PAGE_ALIGN(TASK_SIZE / 3) + * TASK_SIZE64 0x1UL << (... ? VA_BITS : ...) + */ +#define TASK_UNMAPPED_BASE \ + TARGET_PAGE_ALIGN((1ull << TARGET_VIRT_ADDR_SPACE_BITS) / 3) + +/* arch/loongarch/include/asm/elf.h */ +#define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE * 2) + +#include "../generic/target_mman.h" diff --git a/linux-user/loongarch64/target_prctl.h b/linux-user/loongarch64/target_prctl.h new file mode 100644 index 0000000000..eb53b31ad5 --- /dev/null +++ b/linux-user/loongarch64/target_prctl.h @@ -0,0 +1 @@ +/* No special prctl support required. */ diff --git a/linux-user/loongarch64/target_proc.h b/linux-user/loongarch64/target_proc.h new file mode 100644 index 0000000000..43fe29ca72 --- /dev/null +++ b/linux-user/loongarch64/target_proc.h @@ -0,0 +1 @@ +/* No target-specific /proc support */ diff --git a/linux-user/loongarch64/target_resource.h b/linux-user/loongarch64/target_resource.h new file mode 100644 index 0000000000..0f86bf24ee --- /dev/null +++ b/linux-user/loongarch64/target_resource.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_RESOURCE_H +#define LOONGARCH_TARGET_RESOURCE_H + +#include "../generic/target_resource.h" + +#endif diff --git a/linux-user/loongarch64/target_signal.h b/linux-user/loongarch64/target_signal.h new file mode 100644 index 0000000000..ad3aaffcb4 --- /dev/null +++ b/linux-user/loongarch64/target_signal.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_SIGNAL_H +#define LOONGARCH_TARGET_SIGNAL_H + +#include "../generic/signal.h" + +#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 + +#endif /* LOONGARCH_TARGET_SIGNAL_H */ diff --git a/linux-user/loongarch64/target_structs.h b/linux-user/loongarch64/target_structs.h new file mode 100644 index 0000000000..6041441e15 --- /dev/null +++ b/linux-user/loongarch64/target_structs.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_STRUCTS_H +#define LOONGARCH_TARGET_STRUCTS_H + +#include "../generic/target_structs.h" + +#endif diff --git a/linux-user/loongarch64/target_syscall.h b/linux-user/loongarch64/target_syscall.h new file mode 100644 index 0000000000..39f229bb9c --- /dev/null +++ b/linux-user/loongarch64/target_syscall.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_SYSCALL_H +#define LOONGARCH_TARGET_SYSCALL_H + +#include "qemu/units.h" + +/* + * this struct defines the way the registers are stored on the + * stack during a system call. + */ + +struct target_pt_regs { + /* Saved main processor registers. */ + target_ulong regs[32]; + + /* Saved special registers. */ + struct { + target_ulong era; + target_ulong badv; + target_ulong crmd; + target_ulong prmd; + target_ulong euen; + target_ulong ecfg; + target_ulong estat; + } csr; + target_ulong orig_a0; + target_ulong __last[0]; +}; + +#define UNAME_MACHINE "loongarch64" +#define UNAME_MINIMUM_RELEASE "5.19.0" + +#define TARGET_MCL_CURRENT 1 +#define TARGET_MCL_FUTURE 2 +#define TARGET_MCL_ONFAULT 4 + +#endif diff --git a/linux-user/loongarch64/termbits.h b/linux-user/loongarch64/termbits.h new file mode 100644 index 0000000000..d425db8748 --- /dev/null +++ b/linux-user/loongarch64/termbits.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_TARGET_TERMBITS_H +#define LOONGARCH_TARGET_TERMBITS_H + +#include "../generic/termbits.h" + +#endif diff --git a/linux-user/loongarch64/vdso-asmoffset.h b/linux-user/loongarch64/vdso-asmoffset.h new file mode 100644 index 0000000000..60d113822f --- /dev/null +++ b/linux-user/loongarch64/vdso-asmoffset.h @@ -0,0 +1,8 @@ +#define sizeof_rt_sigframe 0x240 +#define sizeof_sigcontext 0x110 +#define sizeof_sctx_info 0x10 + +#define offsetof_sigcontext 0x130 +#define offsetof_sigcontext_pc 0 +#define offsetof_sigcontext_gr 8 +#define offsetof_fpucontext_fr 0 diff --git a/linux-user/loongarch64/vdso.S b/linux-user/loongarch64/vdso.S new file mode 100644 index 0000000000..780a5fda12 --- /dev/null +++ b/linux-user/loongarch64/vdso.S @@ -0,0 +1,130 @@ +/* + * Loongarch64 linux replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include "vdso-asmoffset.h" + + + .text + +.macro endf name + .globl \name + .type \name, @function + .size \name, . - \name +.endm + +.macro vdso_syscall name, nr +\name: + li.w $a7, \nr + syscall 0 + jr $ra +endf \name +.endm + + .cfi_startproc + +vdso_syscall __vdso_gettimeofday, __NR_gettimeofday +vdso_syscall __vdso_clock_gettime, __NR_clock_gettime +vdso_syscall __vdso_clock_getres, __NR_clock_getres +vdso_syscall __vdso_getcpu, __NR_getcpu + + .cfi_endproc + +/* + * Start the unwind info at least one instruction before the signal + * trampoline, because the unwinder will assume we are returning + * after a call site. + */ + + .cfi_startproc simple + .cfi_signal_frame + +#define B_GR offsetof_sigcontext_gr +#define B_FR sizeof_sigcontext + sizeof_sctx_info + offsetof_fpucontext_fr + + .cfi_def_cfa 2, offsetof_sigcontext + + /* Return address */ + .cfi_return_column 64 + .cfi_offset 64, offsetof_sigcontext_pc /* pc */ + + /* Integer registers */ + .cfi_offset 1, B_GR + 1 * 8 + .cfi_offset 2, B_GR + 2 * 8 + .cfi_offset 3, B_GR + 3 * 8 + .cfi_offset 4, B_GR + 4 * 8 + .cfi_offset 5, B_GR + 5 * 8 + .cfi_offset 6, B_GR + 6 * 8 + .cfi_offset 7, B_GR + 7 * 8 + .cfi_offset 8, B_GR + 8 * 8 + .cfi_offset 9, B_GR + 9 * 8 + .cfi_offset 10, B_GR + 10 * 8 + .cfi_offset 11, B_GR + 11 * 8 + .cfi_offset 12, B_GR + 12 * 8 + .cfi_offset 13, B_GR + 13 * 8 + .cfi_offset 14, B_GR + 14 * 8 + .cfi_offset 15, B_GR + 15 * 8 + .cfi_offset 16, B_GR + 16 * 8 + .cfi_offset 17, B_GR + 17 * 8 + .cfi_offset 18, B_GR + 18 * 8 + .cfi_offset 19, B_GR + 19 * 8 + .cfi_offset 20, B_GR + 20 * 8 + .cfi_offset 21, B_GR + 21 * 8 + .cfi_offset 22, B_GR + 22 * 8 + .cfi_offset 23, B_GR + 23 * 8 + .cfi_offset 24, B_GR + 24 * 8 + .cfi_offset 25, B_GR + 25 * 8 + .cfi_offset 26, B_GR + 26 * 8 + .cfi_offset 27, B_GR + 27 * 8 + .cfi_offset 28, B_GR + 28 * 8 + .cfi_offset 29, B_GR + 29 * 8 + .cfi_offset 30, B_GR + 30 * 8 + .cfi_offset 31, B_GR + 31 * 8 + + /* Floating point registers */ + .cfi_offset 32, B_FR + 0 + .cfi_offset 33, B_FR + 1 * 8 + .cfi_offset 34, B_FR + 2 * 8 + .cfi_offset 35, B_FR + 3 * 8 + .cfi_offset 36, B_FR + 4 * 8 + .cfi_offset 37, B_FR + 5 * 8 + .cfi_offset 38, B_FR + 6 * 8 + .cfi_offset 39, B_FR + 7 * 8 + .cfi_offset 40, B_FR + 8 * 8 + .cfi_offset 41, B_FR + 9 * 8 + .cfi_offset 42, B_FR + 10 * 8 + .cfi_offset 43, B_FR + 11 * 8 + .cfi_offset 44, B_FR + 12 * 8 + .cfi_offset 45, B_FR + 13 * 8 + .cfi_offset 46, B_FR + 14 * 8 + .cfi_offset 47, B_FR + 15 * 8 + .cfi_offset 48, B_FR + 16 * 8 + .cfi_offset 49, B_FR + 17 * 8 + .cfi_offset 50, B_FR + 18 * 8 + .cfi_offset 51, B_FR + 19 * 8 + .cfi_offset 52, B_FR + 20 * 8 + .cfi_offset 53, B_FR + 21 * 8 + .cfi_offset 54, B_FR + 22 * 8 + .cfi_offset 55, B_FR + 23 * 8 + .cfi_offset 56, B_FR + 24 * 8 + .cfi_offset 57, B_FR + 25 * 8 + .cfi_offset 58, B_FR + 26 * 8 + .cfi_offset 59, B_FR + 27 * 8 + .cfi_offset 60, B_FR + 28 * 8 + .cfi_offset 61, B_FR + 29 * 8 + .cfi_offset 62, B_FR + 30 * 8 + .cfi_offset 63, B_FR + 31 * 8 + + nop + +__vdso_rt_sigreturn: + li.w $a7, __NR_rt_sigreturn + syscall 0 + .cfi_endproc +endf __vdso_rt_sigreturn diff --git a/linux-user/loongarch64/vdso.ld b/linux-user/loongarch64/vdso.ld new file mode 100644 index 0000000000..682446ed0c --- /dev/null +++ b/linux-user/loongarch64/vdso.ld @@ -0,0 +1,73 @@ +/* + * Linker script for linux loongarch64 replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_5.10 { + global: + __vdso_getcpu; + __vdso_clock_getres; + __vdso_clock_gettime; + __vdso_gettimeofday; + __vdso_rt_sigreturn; + + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + /* + * We can't prelink to any address without knowing something about + * the virtual memory space of the host, since that leaks over into + * the available memory space of the guest. + */ + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + .data : { + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load =0xd503201f +} diff --git a/linux-user/loongarch64/vdso.so b/linux-user/loongarch64/vdso.so new file mode 100755 index 0000000000..bfaa26f2bf Binary files /dev/null and b/linux-user/loongarch64/vdso.so differ diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index 3d3033155f..f79b8e4ab0 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -36,11 +36,6 @@ void cpu_loop(CPUM68KState *env) process_queued_cpu_work(cs); switch(trapnr) { - case EXCP_HALT_INSN: - /* Semihosing syscall. */ - env->pc += 4; - do_m68k_semihosting(env, env->dregs[0]); - break; case EXCP_ILLEGAL: case EXCP_LINEA: case EXCP_LINEF: @@ -100,7 +95,7 @@ void cpu_loop(CPUM68KState *env) void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); struct image_info *info = ts->info; env->pc = regs->pc; diff --git a/linux-user/m68k/signal.c b/linux-user/m68k/signal.c index 5f35354487..77555781aa 100644 --- a/linux-user/m68k/signal.c +++ b/linux-user/m68k/signal.c @@ -295,7 +295,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc); __put_user(uc_addr, &frame->puc); - tswap_siginfo(&frame->info, info); + frame->info = *info; /* Create the ucontext */ @@ -307,7 +307,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, if (err) goto give_sigsegv; - for(i = 0; i < TARGET_NSIG_WORDS; i++) { + for (i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); } diff --git a/linux-user/m68k/target_cpu.h b/linux-user/m68k/target_cpu.h index c3f288dfe8..4b40c09a8d 100644 --- a/linux-user/m68k/target_cpu.h +++ b/linux-user/m68k/target_cpu.h @@ -37,7 +37,7 @@ static inline void cpu_clone_regs_parent(CPUM68KState *env, unsigned flags) static inline void cpu_set_tls(CPUM68KState *env, target_ulong newtls) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); ts->tp_value = newtls; } diff --git a/linux-user/m68k/target_flat.h b/linux-user/m68k/target_flat.h new file mode 100644 index 0000000000..bc83224cea --- /dev/null +++ b/linux-user/m68k/target_flat.h @@ -0,0 +1 @@ +#include "../generic/target_flat.h" diff --git a/linux-user/m68k/target_mman.h b/linux-user/m68k/target_mman.h new file mode 100644 index 0000000000..20cfe750c5 --- /dev/null +++ b/linux-user/m68k/target_mman.h @@ -0,0 +1,6 @@ +/* arch/m68k/include/asm/processor.h */ +#define TASK_UNMAPPED_BASE 0xC0000000 +/* arch/m68k/include/asm/elf.h */ +#define ELF_ET_DYN_BASE 0xD0000000 + +#include "../generic/target_mman.h" diff --git a/linux-user/m68k/target_proc.h b/linux-user/m68k/target_proc.h new file mode 100644 index 0000000000..3df8f28e22 --- /dev/null +++ b/linux-user/m68k/target_proc.h @@ -0,0 +1,16 @@ +/* + * M68K specific proc functions for linux-user + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef M68K_TARGET_PROC_H +#define M68K_TARGET_PROC_H + +static int open_hardware(CPUArchState *cpu_env, int fd) +{ + dprintf(fd, "Model:\t\tqemu-m68k\n"); + return 0; +} +#define HAVE_ARCH_PROC_HARDWARE + +#endif /* M68K_TARGET_PROC_H */ diff --git a/linux-user/main.c b/linux-user/main.c index 651e32f5f2..149e35432e 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -40,7 +40,8 @@ #include "qemu/plugin.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" -#include "tcg/tcg.h" +#include "gdbstub/user.h" +#include "tcg/startup.h" #include "qemu/timer.h" #include "qemu/envlist.h" #include "qemu/guest-random.h" @@ -53,6 +54,12 @@ #include "signal-common.h" #include "loader.h" #include "user-mmap.h" +#include "tcg/perf.h" +#include "exec/page-vary.h" + +#ifdef CONFIG_SEMIHOSTING +#include "semihosting/semihost.h" +#endif #ifndef AT_FLAGS_PRESERVE_ARGV0 #define AT_FLAGS_PRESERVE_ARGV0_BIT 0 @@ -60,8 +67,9 @@ #endif char *exec_path; +char real_exec_path[PATH_MAX]; -int singlestep; +static bool opt_one_insn_per_tb; static const char *argv0; static const char *gdbstub; static envlist_t *envlist; @@ -102,11 +110,9 @@ static const char *last_log_filename; # if HOST_LONG_BITS > TARGET_VIRT_ADDR_SPACE_BITS # if TARGET_VIRT_ADDR_SPACE_BITS == 32 && \ (TARGET_LONG_BITS == 32 || defined(TARGET_ABI32)) -/* There are a number of places where we assign reserved_va to a variable - of type abi_ulong and expect it to fit. Avoid the last page. */ -# define MAX_RESERVED_VA(CPU) (0xfffffffful & TARGET_PAGE_MASK) +# define MAX_RESERVED_VA(CPU) 0xfffffffful # else -# define MAX_RESERVED_VA(CPU) (1ul << TARGET_VIRT_ADDR_SPACE_BITS) +# define MAX_RESERVED_VA(CPU) ((1ul << TARGET_VIRT_ADDR_SPACE_BITS) - 1) # endif # else # define MAX_RESERVED_VA(CPU) 0 @@ -120,10 +126,14 @@ static void usage(int exitcode); static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; const char *qemu_uname_release; +#if !defined(TARGET_DEFAULT_STACK_SIZE) /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so we allocate a bigger stack. Need a better solution, for example by remapping the process stack directly at the right place */ -unsigned long guest_stack_size = 8 * 1024 * 1024UL; +#define TARGET_DEFAULT_STACK_SIZE 8 * 1024 * 1024UL +#endif + +unsigned long guest_stack_size = TARGET_DEFAULT_STACK_SIZE; /***********************************************************/ /* Helper routines for implementing atomic operations. */ @@ -134,10 +144,15 @@ void fork_start(void) start_exclusive(); mmap_fork_start(); cpu_list_lock(); + qemu_plugin_user_prefork_lock(); + gdbserver_fork_start(); } -void fork_end(int child) +void fork_end(pid_t pid) { + bool child = pid == 0; + + qemu_plugin_user_postfork(child); mmap_fork_end(child); if (child) { CPUState *cpu, *next_cpu; @@ -145,18 +160,21 @@ void fork_end(int child) Discard information about the parent threads. */ CPU_FOREACH_SAFE(cpu, next_cpu) { if (cpu != thread_cpu) { - QTAILQ_REMOVE_RCU(&cpus, cpu, node); + QTAILQ_REMOVE_RCU(&cpus_queue, cpu, node); } } qemu_init_cpu_list(); - gdbserver_fork(thread_cpu); - /* qemu_init_cpu_list() takes care of reinitializing the - * exclusive state, so we don't need to end_exclusive() here. - */ + get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id(); } else { cpu_list_unlock(); - end_exclusive(); } + gdbserver_fork_end(thread_cpu, pid); + /* + * qemu_init_cpu_list() reinitialized the child exclusive state, but we + * also need to keep current_cpu consistent, so call end_exclusive() for + * both child and parent. + */ + end_exclusive(); } __thread CPUState *thread_cpu; @@ -216,7 +234,7 @@ CPUArchState *cpu_copy(CPUArchState *env) { CPUState *cpu = env_cpu(env); CPUState *new_cpu = cpu_create(cpu_type); - CPUArchState *new_env = new_cpu->env_ptr; + CPUArchState *new_env = cpu_env(new_cpu); CPUBreakpoint *bp; /* Reset non arch specific state */ @@ -224,6 +242,14 @@ CPUArchState *cpu_copy(CPUArchState *env) new_cpu->tcg_cflags = cpu->tcg_cflags; memcpy(new_env, env, sizeof(CPUArchState)); +#if defined(TARGET_I386) || defined(TARGET_X86_64) + new_env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES, + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + memcpy(g2h_untagged(new_env->gdt.base), g2h_untagged(env->gdt.base), + sizeof(uint64_t) * TARGET_GDT_ENTRIES); + OBJECT(new_cpu)->free = OBJECT(cpu)->free; +#endif /* Clone all break/watchpoints. Note: Once we support ptrace with hw-debug register access, make sure @@ -311,11 +337,11 @@ static void handle_arg_ld_prefix(const char *arg) static void handle_arg_pagesize(const char *arg) { - qemu_host_page_size = atoi(arg); - if (qemu_host_page_size == 0 || - (qemu_host_page_size & (qemu_host_page_size - 1)) != 0) { - fprintf(stderr, "page size must be a power of two\n"); - exit(EXIT_FAILURE); + unsigned size, want = qemu_real_host_page_size(); + + if (qemu_strtoui(arg, NULL, 10, &size) || size != want) { + warn_report("Deprecated page size option cannot " + "change host page size (%u)", want); } } @@ -338,10 +364,7 @@ static void handle_arg_cpu(const char *arg) { cpu_model = strdup(arg); if (cpu_model == NULL || is_help_option(cpu_model)) { - /* XXX: implement xxx_cpu_list for targets that still miss it */ -#if defined(cpu_list) - cpu_list(); -#endif + list_cpus(); exit(EXIT_FAILURE); } } @@ -356,7 +379,9 @@ static void handle_arg_reserved_va(const char *arg) { char *p; int shift = 0; - reserved_va = strtoul(arg, &p, 0); + unsigned long val; + + val = strtoul(arg, &p, 0); switch (*p) { case 'k': case 'K': @@ -370,10 +395,10 @@ static void handle_arg_reserved_va(const char *arg) break; } if (shift) { - unsigned long unshifted = reserved_va; + unsigned long unshifted = val; p++; - reserved_va <<= shift; - if (reserved_va >> shift != unshifted) { + val <<= shift; + if (val >> shift != unshifted) { fprintf(stderr, "Reserved virtual address too big\n"); exit(EXIT_FAILURE); } @@ -382,11 +407,13 @@ static void handle_arg_reserved_va(const char *arg) fprintf(stderr, "Unrecognised -R size suffix '%s'\n", p); exit(EXIT_FAILURE); } + /* The representation is size - 1, with 0 remaining "default". */ + reserved_va = val ? val - 1 : 0; } -static void handle_arg_singlestep(const char *arg) +static void handle_arg_one_insn_per_tb(const char *arg) { - singlestep = 1; + opt_one_insn_per_tb = true; } static void handle_arg_strace(const char *arg) @@ -413,6 +440,16 @@ static void handle_arg_abi_call0(const char *arg) } #endif +static void handle_arg_perfmap(const char *arg) +{ + perf_enable_perfmap(); +} + +static void handle_arg_jitdump(const char *arg) +{ + perf_enable_jitdump(); +} + static QemuPluginList plugins = QTAILQ_HEAD_INITIALIZER(plugins); #ifdef CONFIG_PLUGIN @@ -464,9 +501,10 @@ static const struct qemu_argument arg_table[] = { {"D", "QEMU_LOG_FILENAME", true, handle_arg_log_filename, "logfile", "write logs to 'logfile' (default stderr)"}, {"p", "QEMU_PAGESIZE", true, handle_arg_pagesize, - "pagesize", "set the host page size to 'pagesize'"}, - {"singlestep", "QEMU_SINGLESTEP", false, handle_arg_singlestep, - "", "run in singlestep mode"}, + "pagesize", "deprecated change to host page size"}, + {"one-insn-per-tb", + "QEMU_ONE_INSN_PER_TB", false, handle_arg_one_insn_per_tb, + "", "run with one guest instruction per emulated TB"}, {"strace", "QEMU_STRACE", false, handle_arg_strace, "", "log system calls"}, {"seed", "QEMU_RAND_SEED", true, handle_arg_seed, @@ -483,6 +521,10 @@ static const struct qemu_argument arg_table[] = { {"xtensa-abi-call0", "QEMU_XTENSA_ABI_CALL0", false, handle_arg_abi_call0, "", "assume CALL0 Xtensa ABI"}, #endif + {"perfmap", "QEMU_PERFMAP", false, handle_arg_perfmap, + "", "Generate a /tmp/perf-${pid}.map file for perf"}, + {"jitdump", "QEMU_JITDUMP", false, handle_arg_jitdump, + "", "Generate a jit-${pid}.dump file for perf"}, {NULL, NULL, false, NULL, NULL, NULL} }; @@ -643,6 +685,7 @@ int main(int argc, char **argv, char **envp) int i; int ret; int execfd; + int host_page_size; unsigned long max_reserved_va; bool preserve_argv0; @@ -653,8 +696,16 @@ int main(int argc, char **argv, char **envp) envlist = envlist_create(); - /* add current environment into the list */ + /* + * add current environment into the list + * envlist_setenv adds to the front of the list; to preserve environ + * order add from back to front + */ for (wrk = environ; *wrk != NULL; wrk++) { + continue; + } + while (wrk != environ) { + wrk--; (void) envlist_setenv(envlist, *wrk); } @@ -664,7 +715,8 @@ int main(int argc, char **argv, char **envp) struct rlimit lim; if (getrlimit(RLIMIT_STACK, &lim) == 0 && lim.rlim_cur != RLIM_INFINITY - && lim.rlim_cur == (target_long)lim.rlim_cur) { + && lim.rlim_cur == (target_long)lim.rlim_cur + && lim.rlim_cur > guest_stack_size) { guest_stack_size = lim.rlim_cur; } } @@ -711,6 +763,11 @@ int main(int argc, char **argv, char **envp) } } + /* Resolve executable file name to full path name */ + if (realpath(exec_path, real_exec_path)) { + exec_path = real_exec_path; + } + /* * get binfmt_misc flags */ @@ -730,15 +787,28 @@ int main(int argc, char **argv, char **envp) } cpu_type = parse_cpu_option(cpu_model); - /* init tcg before creating CPUs and to get qemu_host_page_size */ + /* init tcg before creating CPUs */ { - AccelClass *ac = ACCEL_GET_CLASS(current_accel()); + AccelState *accel = current_accel(); + AccelClass *ac = ACCEL_GET_CLASS(accel); accel_init_interfaces(ac); + object_property_set_bool(OBJECT(accel), "one-insn-per-tb", + opt_one_insn_per_tb, &error_abort); ac->init_machine(NULL); } + + /* + * Finalize page size before creating CPUs. + * This will do nothing if !TARGET_PAGE_BITS_VARY. + * The most efficient setting is to match the host. + */ + host_page_size = qemu_real_host_page_size(); + set_preferred_target_page_bits(ctz32(host_page_size)); + finalize_target_page_bits(); + cpu = cpu_create(cpu_type); - env = cpu->env_ptr; + env = cpu_env(cpu); cpu_reset(cpu); thread_cpu = cpu; @@ -750,18 +820,64 @@ int main(int argc, char **argv, char **envp) */ max_reserved_va = MAX_RESERVED_VA(cpu); if (reserved_va != 0) { + if ((reserved_va + 1) % host_page_size) { + char *s = size_to_str(host_page_size); + fprintf(stderr, "Reserved virtual address not aligned mod %s\n", s); + g_free(s); + exit(EXIT_FAILURE); + } if (max_reserved_va && reserved_va > max_reserved_va) { fprintf(stderr, "Reserved virtual address too big\n"); exit(EXIT_FAILURE); } } else if (HOST_LONG_BITS == 64 && TARGET_VIRT_ADDR_SPACE_BITS <= 32) { - /* - * reserved_va must be aligned with the host page size - * as it is used with mmap() - */ - reserved_va = max_reserved_va & qemu_host_page_mask; + /* MAX_RESERVED_VA + 1 is a large power of 2, so is aligned. */ + reserved_va = max_reserved_va; } + /* + * Temporarily disable + * "comparison is always false due to limited range of data type" + * due to comparison between (possible) uint64_t and uintptr_t. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" + + /* + * Select an initial value for task_unmapped_base that is in range. + */ + if (reserved_va) { + if (TASK_UNMAPPED_BASE < reserved_va) { + task_unmapped_base = TASK_UNMAPPED_BASE; + } else { + /* The most common default formula is TASK_SIZE / 3. */ + task_unmapped_base = TARGET_PAGE_ALIGN(reserved_va / 3); + } + } else if (TASK_UNMAPPED_BASE < UINTPTR_MAX) { + task_unmapped_base = TASK_UNMAPPED_BASE; + } else { + /* 32-bit host: pick something medium size. */ + task_unmapped_base = 0x10000000; + } + mmap_next_start = task_unmapped_base; + + /* Similarly for elf_et_dyn_base. */ + if (reserved_va) { + if (ELF_ET_DYN_BASE < reserved_va) { + elf_et_dyn_base = ELF_ET_DYN_BASE; + } else { + /* The most common default formula is TASK_SIZE / 3 * 2. */ + elf_et_dyn_base = TARGET_PAGE_ALIGN(reserved_va / 3) * 2; + } + } else if (ELF_ET_DYN_BASE < UINTPTR_MAX) { + elf_et_dyn_base = ELF_ET_DYN_BASE; + } else { + /* 32-bit host: pick something medium size. */ + elf_et_dyn_base = 0x18000000; + } + +#pragma GCC diagnostic pop + { Error *err = NULL; if (seed_optarg != NULL) { @@ -789,7 +905,7 @@ int main(int argc, char **argv, char **envp) if ((fp = fopen("/proc/sys/vm/mmap_min_addr", "r")) != NULL) { unsigned long tmp; if (fscanf(fp, "%lu", &tmp) == 1 && tmp != 0) { - mmap_min_addr = tmp; + mmap_min_addr = MAX(tmp, host_page_size); qemu_log_mask(CPU_LOG_PAGE, "host mmap_min_addr=0x%lx\n", mmap_min_addr); } @@ -802,7 +918,7 @@ int main(int argc, char **argv, char **envp) * If we're in a chroot with no /proc, fall back to 1 page. */ if (mmap_min_addr == 0) { - mmap_min_addr = qemu_host_page_size; + mmap_min_addr = host_page_size; qemu_log_mask(CPU_LOG_PAGE, "host mmap_min_addr=0x%lx (fallback)\n", mmap_min_addr); @@ -812,11 +928,7 @@ int main(int argc, char **argv, char **envp) * Prepare copy of argv vector for target. */ target_argc = argc - optind; - target_argv = calloc(target_argc + 1, sizeof (char *)); - if (target_argv == NULL) { - (void) fprintf(stderr, "Unable to allocate memory for target_argv\n"); - exit(EXIT_FAILURE); - } + target_argv = g_new0(char *, target_argc + 1); /* * If argv0 is specified (using '-0' switch) we replace @@ -861,8 +973,6 @@ int main(int argc, char **argv, char **envp) fprintf(f, "page layout changed following binary load\n"); page_dump(f); - fprintf(f, "start_brk 0x" TARGET_ABI_FMT_lx "\n", - info->start_brk); fprintf(f, "end_code 0x" TARGET_ABI_FMT_lx "\n", info->end_code); fprintf(f, "start_code 0x" TARGET_ABI_FMT_lx "\n", @@ -894,7 +1004,7 @@ int main(int argc, char **argv, char **envp) /* Now that we've loaded the binary, GUEST_BASE is fixed. Delay generating the prologue until now so that the prologue can take the real value of GUEST_BASE into account. */ - tcg_prologue_init(tcg_ctx); + tcg_prologue_init(); target_cpu_copy_regs(env, regs); @@ -904,8 +1014,13 @@ int main(int argc, char **argv, char **envp) gdbstub); exit(EXIT_FAILURE); } - gdb_handlesig(cpu, 0); + gdb_handlesig(cpu, 0, NULL, NULL, 0); } + +#ifdef CONFIG_SEMIHOSTING + qemu_semihosting_guestfd_init(); +#endif + cpu_loop(env); /* never exits */ return 0; diff --git a/linux-user/meson.build b/linux-user/meson.build index de4320af05..bc41e8c3bc 100644 --- a/linux-user/meson.build +++ b/linux-user/meson.build @@ -22,23 +22,31 @@ linux_user_ss.add(files( 'uname.c', )) linux_user_ss.add(rt) +linux_user_ss.add(libdw) linux_user_ss.add(when: 'TARGET_HAS_BFLT', if_true: files('flatload.c')) linux_user_ss.add(when: 'TARGET_I386', if_true: files('vm86.c')) linux_user_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING', if_true: files('semihost.c')) - syscall_nr_generators = {} +gen_vdso_exe = executable('gen-vdso', 'gen-vdso.c', + native: true, build_by_default: false) +gen_vdso = generator(gen_vdso_exe, output: '@BASENAME@.c.inc', + arguments: ['-o', '@OUTPUT@', '@EXTRA_ARGS@', '@INPUT@']) + +subdir('aarch64') subdir('alpha') subdir('arm') subdir('hppa') subdir('i386') +subdir('loongarch64') subdir('m68k') subdir('microblaze') subdir('mips64') subdir('mips') subdir('ppc') +subdir('riscv') subdir('s390x') subdir('sh4') subdir('sparc') diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index 5ccf9e942e..212e62d0a6 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -25,8 +25,8 @@ void cpu_loop(CPUMBState *env) { + int trapnr, ret, si_code, sig; CPUState *cs = env_cpu(env); - int trapnr, ret, si_code; while (1) { cpu_exec_start(cs); @@ -76,6 +76,7 @@ void cpu_loop(CPUMBState *env) env->iflags &= ~(IMM_FLAG | D_FLAG); switch (env->esr & 31) { case ESR_EC_DIVZERO: + sig = TARGET_SIGFPE; si_code = TARGET_FPE_INTDIV; break; case ESR_EC_FPU: @@ -84,6 +85,7 @@ void cpu_loop(CPUMBState *env) * if there's no recognized bit set. Possibly this * implies that si_code is 0, but follow the structure. */ + sig = TARGET_SIGFPE; si_code = env->fsr; if (si_code & FSR_IO) { si_code = TARGET_FPE_FLTINV; @@ -97,13 +99,17 @@ void cpu_loop(CPUMBState *env) si_code = TARGET_FPE_FLTRES; } break; + case ESR_EC_PRIVINSN: + sig = SIGILL; + si_code = ILL_PRVOPC; + break; default: fprintf(stderr, "Unhandled hw-exception: 0x%x\n", env->esr & ESR_EC_MASK); cpu_dump_state(cs, stderr, 0); exit(EXIT_FAILURE); } - force_sig_fault(TARGET_SIGFPE, si_code, env->pc); + force_sig_fault(sig, si_code, env->pc); break; case EXCP_DEBUG: diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c index 5188d74025..f6d47d76ff 100644 --- a/linux-user/microblaze/signal.c +++ b/linux-user/microblaze/signal.c @@ -147,7 +147,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, return; } - tswap_siginfo(&frame->info, info); + frame->info = *info; __put_user(0, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_link); diff --git a/linux-user/microblaze/target_flat.h b/linux-user/microblaze/target_flat.h new file mode 100644 index 0000000000..bc83224cea --- /dev/null +++ b/linux-user/microblaze/target_flat.h @@ -0,0 +1 @@ +#include "../generic/target_flat.h" diff --git a/linux-user/microblaze/target_mman.h b/linux-user/microblaze/target_mman.h new file mode 100644 index 0000000000..6b3dd54f89 --- /dev/null +++ b/linux-user/microblaze/target_mman.h @@ -0,0 +1,12 @@ +/* + * arch/microblaze/include/asm/processor.h: + * TASK_UNMAPPED_BASE (TASK_SIZE / 8 * 3) + * TASK_SIZE CONFIG_KERNEL_START + * CONFIG_KERNEL_START 0xc0000000 (default in Kconfig) + */ +#define TASK_UNMAPPED_BASE 0x48000000 + +/* arch/microblaze/include/uapi/asm/elf.h */ +#define ELF_ET_DYN_BASE 0x08000000 + +#include "../generic/target_mman.h" diff --git a/linux-user/microblaze/target_proc.h b/linux-user/microblaze/target_proc.h new file mode 100644 index 0000000000..43fe29ca72 --- /dev/null +++ b/linux-user/microblaze/target_proc.h @@ -0,0 +1 @@ +/* No target-specific /proc support */ diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index d5c1c7941d..462387a073 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -180,7 +180,9 @@ done_syscall: } force_sig_fault(TARGET_SIGFPE, si_code, env->active_tc.PC); break; - + case EXCP_OVERFLOW: + force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTOVF, env->active_tc.PC); + break; /* The code below was inspired by the MIPS Linux kernel trap * handling code in arch/mips/kernel/traps.c. */ @@ -212,7 +214,7 @@ done_syscall: void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); struct image_info *info = ts->info; int i; @@ -290,7 +292,10 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) env->CP0_Status |= (1 << CP0St_FR); env->hflags |= MIPS_HFLAG_F64; } - } else if (!prog_req.fre && !prog_req.frdefault && + } else if (prog_req.fr1) { + env->CP0_Status |= (1 << CP0St_FR); + env->hflags |= MIPS_HFLAG_F64; + } else if (!prog_req.fre && !prog_req.frdefault && !prog_req.fr1 && !prog_req.single && !prog_req.soft) { fprintf(stderr, "qemu: Can't find a matching FPU mode\n"); exit(1); diff --git a/linux-user/mips/signal.c b/linux-user/mips/signal.c index 58a9d7a8a3..d69a5d73dd 100644 --- a/linux-user/mips/signal.c +++ b/linux-user/mips/signal.c @@ -303,7 +303,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, goto give_sigsegv; } - tswap_siginfo(&frame->rs_info, info); + frame->rs_info = *info; __put_user(0, &frame->rs_uc.tuc_flags); __put_user(0, &frame->rs_uc.tuc_link); @@ -311,7 +311,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, setup_sigcontext(env, &frame->rs_uc.tuc_mcontext); - for(i = 0; i < TARGET_NSIG_WORDS; i++) { + for (i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]); } diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index a98c9bd6ad..b965e86b2b 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -15,6 +15,9 @@ static inline const char *cpu_get_model(uint32_t eflags) if ((eflags & EF_MIPS_MACH) == EF_MIPS_MACH_5900) { return "R5900"; } + if (eflags & EF_MIPS_NAN2008) { + return "P5600"; + } return "24Kf"; } #endif diff --git a/linux-user/mips/target_mman.h b/linux-user/mips/target_mman.h new file mode 100644 index 0000000000..b84fe1e8a8 --- /dev/null +++ b/linux-user/mips/target_mman.h @@ -0,0 +1,29 @@ +#ifndef MIPS_TARGET_MMAN_H +#define MIPS_TARGET_MMAN_H + +#define TARGET_PROT_SEM 0x10 + +#define TARGET_MAP_NORESERVE 0x0400 +#define TARGET_MAP_ANONYMOUS 0x0800 +#define TARGET_MAP_GROWSDOWN 0x1000 +#define TARGET_MAP_DENYWRITE 0x2000 +#define TARGET_MAP_EXECUTABLE 0x4000 +#define TARGET_MAP_LOCKED 0x8000 +#define TARGET_MAP_POPULATE 0x10000 +#define TARGET_MAP_NONBLOCK 0x20000 +#define TARGET_MAP_STACK 0x40000 +#define TARGET_MAP_HUGETLB 0x80000 + +/* + * arch/mips/include/asm/processor.h: + * TASK_UNMAPPED_BASE PAGE_ALIGN(TASK_SIZE / 3) + */ +#define TASK_UNMAPPED_BASE \ + TARGET_PAGE_ALIGN((1ull << TARGET_VIRT_ADDR_SPACE_BITS) / 3) + +/* arch/mips/include/asm/elf.h */ +#define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE * 2) + +#include "../generic/target_mman.h" + +#endif diff --git a/linux-user/mips/target_proc.h b/linux-user/mips/target_proc.h new file mode 100644 index 0000000000..43fe29ca72 --- /dev/null +++ b/linux-user/mips/target_proc.h @@ -0,0 +1 @@ +/* No target-specific /proc support */ diff --git a/linux-user/mips64/target_mman.h b/linux-user/mips64/target_mman.h new file mode 100644 index 0000000000..7bdc47d902 --- /dev/null +++ b/linux-user/mips64/target_mman.h @@ -0,0 +1 @@ +#include "../mips/target_mman.h" diff --git a/linux-user/mips64/target_proc.h b/linux-user/mips64/target_proc.h new file mode 100644 index 0000000000..43fe29ca72 --- /dev/null +++ b/linux-user/mips64/target_proc.h @@ -0,0 +1 @@ +/* No target-specific /proc support */ diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 1d1951f9f4..dd45a4e31e 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -17,11 +17,18 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" +#include #include "trace.h" #include "exec/log.h" #include "qemu.h" #include "user-internals.h" #include "user-mmap.h" +#include "target_mman.h" +#include "qemu/interval-tree.h" + +#ifdef TARGET_ARM +#include "target/arm/cpu-features.h" +#endif static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER; static __thread int mmap_lock_count; @@ -35,6 +42,7 @@ void mmap_lock(void) void mmap_unlock(void) { + assert(mmap_lock_count > 0); if (--mmap_lock_count == 0) { pthread_mutex_unlock(&mmap_mutex); } @@ -55,10 +63,49 @@ void mmap_fork_start(void) void mmap_fork_end(int child) { - if (child) + if (child) { pthread_mutex_init(&mmap_mutex, NULL); - else + } else { pthread_mutex_unlock(&mmap_mutex); + } +} + +/* Protected by mmap_lock. */ +static IntervalTreeRoot shm_regions; + +static void shm_region_add(abi_ptr start, abi_ptr last) +{ + IntervalTreeNode *i = g_new0(IntervalTreeNode, 1); + + i->start = start; + i->last = last; + interval_tree_insert(i, &shm_regions); +} + +static abi_ptr shm_region_find(abi_ptr start) +{ + IntervalTreeNode *i; + + for (i = interval_tree_iter_first(&shm_regions, start, start); i; + i = interval_tree_iter_next(i, start, start)) { + if (i->start == start) { + return i->last; + } + } + return 0; +} + +static void shm_region_rm_complete(abi_ptr start, abi_ptr last) +{ + IntervalTreeNode *i, *n; + + for (i = interval_tree_iter_first(&shm_regions, start, last); i; i = n) { + n = interval_tree_iter_next(i, start, last); + if (i->start >= start && i->last <= last) { + interval_tree_remove(i, &shm_regions); + g_free(i); + } + } } /* @@ -67,24 +114,11 @@ void mmap_fork_end(int child) * Return 0 if the target prot bitmask is invalid, otherwise * the internal qemu page_flags (which will include PAGE_VALID). */ -static int validate_prot_to_pageflags(int *host_prot, int prot) +static int validate_prot_to_pageflags(int prot) { int valid = PROT_READ | PROT_WRITE | PROT_EXEC | TARGET_PROT_SEM; int page_flags = (prot & PAGE_BITS) | PAGE_VALID; - /* - * For the host, we need not pass anything except read/write/exec. - * While PROT_SEM is allowed by all hosts, it is also ignored, so - * don't bother transforming guest bit to host bit. Any other - * target-specific prot bits will not be understood by the host - * and will need to be encoded into page_flags for qemu emulation. - * - * Pages that are executable by the guest will never be executed - * by the host, but the host will need to be able to read them. - */ - *host_prot = (prot & (PROT_READ | PROT_WRITE)) - | (prot & PROT_EXEC ? PROT_READ : 0); - #ifdef TARGET_AARCH64 { ARMCPU *cpu = ARM_CPU(thread_cpu); @@ -105,206 +139,256 @@ static int validate_prot_to_pageflags(int *host_prot, int prot) page_flags |= PAGE_MTE; } } +#elif defined(TARGET_HPPA) + valid |= PROT_GROWSDOWN | PROT_GROWSUP; #endif return prot & ~valid ? 0 : page_flags; } +/* + * For the host, we need not pass anything except read/write/exec. + * While PROT_SEM is allowed by all hosts, it is also ignored, so + * don't bother transforming guest bit to host bit. Any other + * target-specific prot bits will not be understood by the host + * and will need to be encoded into page_flags for qemu emulation. + * + * Pages that are executable by the guest will never be executed + * by the host, but the host will need to be able to read them. + */ +static int target_to_host_prot(int prot) +{ + return (prot & (PROT_READ | PROT_WRITE)) | + (prot & PROT_EXEC ? PROT_READ : 0); +} + /* NOTE: all the constants are the HOST ones, but addresses are target. */ int target_mprotect(abi_ulong start, abi_ulong len, int target_prot) { - abi_ulong end, host_start, host_end, addr; - int prot1, ret, page_flags, host_prot; + int host_page_size = qemu_real_host_page_size(); + abi_ulong starts[3]; + abi_ulong lens[3]; + int prots[3]; + abi_ulong host_start, host_last, last; + int prot1, ret, page_flags, nranges; trace_target_mprotect(start, len, target_prot); if ((start & ~TARGET_PAGE_MASK) != 0) { return -TARGET_EINVAL; } - page_flags = validate_prot_to_pageflags(&host_prot, target_prot); + page_flags = validate_prot_to_pageflags(target_prot); if (!page_flags) { return -TARGET_EINVAL; } - len = TARGET_PAGE_ALIGN(len); - end = start + len; - if (!guest_range_valid_untagged(start, len)) { - return -TARGET_ENOMEM; - } if (len == 0) { return 0; } + len = TARGET_PAGE_ALIGN(len); + if (!guest_range_valid_untagged(start, len)) { + return -TARGET_ENOMEM; + } + + last = start + len - 1; + host_start = start & -host_page_size; + host_last = ROUND_UP(last, host_page_size) - 1; + nranges = 0; mmap_lock(); - host_start = start & qemu_host_page_mask; - host_end = HOST_PAGE_ALIGN(end); - if (start > host_start) { - /* handle host page containing start */ - prot1 = host_prot; - for (addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) { - prot1 |= page_get_flags(addr); + + if (host_last - host_start < host_page_size) { + /* Single host page contains all guest pages: sum the prot. */ + prot1 = target_prot; + for (abi_ulong a = host_start; a < start; a += TARGET_PAGE_SIZE) { + prot1 |= page_get_flags(a); } - if (host_end == host_start + qemu_host_page_size) { - for (addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) { - prot1 |= page_get_flags(addr); + for (abi_ulong a = last; a < host_last; a += TARGET_PAGE_SIZE) { + prot1 |= page_get_flags(a + 1); + } + starts[nranges] = host_start; + lens[nranges] = host_page_size; + prots[nranges] = prot1; + nranges++; + } else { + if (host_start < start) { + /* Host page contains more than one guest page: sum the prot. */ + prot1 = target_prot; + for (abi_ulong a = host_start; a < start; a += TARGET_PAGE_SIZE) { + prot1 |= page_get_flags(a); + } + /* If the resulting sum differs, create a new range. */ + if (prot1 != target_prot) { + starts[nranges] = host_start; + lens[nranges] = host_page_size; + prots[nranges] = prot1; + nranges++; + host_start += host_page_size; } - end = host_end; } - ret = mprotect(g2h_untagged(host_start), qemu_host_page_size, - prot1 & PAGE_BITS); - if (ret != 0) { - goto error; + + if (last < host_last) { + /* Host page contains more than one guest page: sum the prot. */ + prot1 = target_prot; + for (abi_ulong a = last; a < host_last; a += TARGET_PAGE_SIZE) { + prot1 |= page_get_flags(a + 1); + } + /* If the resulting sum differs, create a new range. */ + if (prot1 != target_prot) { + host_last -= host_page_size; + starts[nranges] = host_last + 1; + lens[nranges] = host_page_size; + prots[nranges] = prot1; + nranges++; + } } - host_start += qemu_host_page_size; - } - if (end < host_end) { - prot1 = host_prot; - for (addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) { - prot1 |= page_get_flags(addr); + + /* Create a range for the middle, if any remains. */ + if (host_start < host_last) { + starts[nranges] = host_start; + lens[nranges] = host_last - host_start + 1; + prots[nranges] = target_prot; + nranges++; } - ret = mprotect(g2h_untagged(host_end - qemu_host_page_size), - qemu_host_page_size, prot1 & PAGE_BITS); - if (ret != 0) { - goto error; - } - host_end -= qemu_host_page_size; } - /* handle the pages in the middle */ - if (host_start < host_end) { - ret = mprotect(g2h_untagged(host_start), - host_end - host_start, host_prot); + for (int i = 0; i < nranges; ++i) { + ret = mprotect(g2h_untagged(starts[i]), lens[i], + target_to_host_prot(prots[i])); if (ret != 0) { goto error; } } - page_set_flags(start, start + len, page_flags); - mmap_unlock(); - return 0; -error: + + page_set_flags(start, last, page_flags); + ret = 0; + + error: mmap_unlock(); return ret; } -/* map an incomplete host page */ -static int mmap_frag(abi_ulong real_start, - abi_ulong start, abi_ulong end, - int prot, int flags, int fd, abi_ulong offset) +/* + * Perform munmap on behalf of the target, with host parameters. + * If reserved_va, we must replace the memory reservation. + */ +static int do_munmap(void *addr, size_t len) { - abi_ulong real_end, addr; - void *host_start; - int prot1, prot_new; - - real_end = real_start + qemu_host_page_size; - host_start = g2h_untagged(real_start); - - /* get the protection of the target pages outside the mapping */ - prot1 = 0; - for(addr = real_start; addr < real_end; addr++) { - if (addr < start || addr >= end) - prot1 |= page_get_flags(addr); + if (reserved_va) { + void *ptr = mmap(addr, len, PROT_NONE, + MAP_FIXED | MAP_ANONYMOUS + | MAP_PRIVATE | MAP_NORESERVE, -1, 0); + return ptr == addr ? 0 : -1; } - - if (prot1 == 0) { - /* no page was there, so we allocate one */ - void *p = mmap(host_start, qemu_host_page_size, prot, - flags | MAP_ANONYMOUS, -1, 0); - if (p == MAP_FAILED) - return -1; - prot1 = prot; - } - prot1 &= PAGE_BITS; - - prot_new = prot | prot1; - if (!(flags & MAP_ANONYMOUS)) { - /* msync() won't work here, so we return an error if write is - possible while it is a shared mapping */ - if ((flags & MAP_TYPE) == MAP_SHARED && - (prot & PROT_WRITE)) - return -1; - - /* adjust protection to be able to read */ - if (!(prot1 & PROT_WRITE)) - mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE); - - /* read the corresponding file data */ - if (pread(fd, g2h_untagged(start), end - start, offset) == -1) - return -1; - - /* put final protection */ - if (prot_new != (prot1 | PROT_WRITE)) - mprotect(host_start, qemu_host_page_size, prot_new); - } else { - if (prot_new != prot1) { - mprotect(host_start, qemu_host_page_size, prot_new); - } - if (prot_new & PROT_WRITE) { - memset(g2h_untagged(start), 0, end - start); - } - } - return 0; + return munmap(addr, len); } -#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 -abi_ulong mmap_next_start = TASK_UNMAPPED_BASE; +/* + * Map an incomplete host page. + * + * Here be dragons. This case will not work if there is an existing + * overlapping host page, which is file mapped, and for which the mapping + * is beyond the end of the file. In that case, we will see SIGBUS when + * trying to write a portion of this page. + * + * FIXME: Work around this with a temporary signal handler and longjmp. + */ +static bool mmap_frag(abi_ulong real_start, abi_ulong start, abi_ulong last, + int prot, int flags, int fd, off_t offset) +{ + int host_page_size = qemu_real_host_page_size(); + abi_ulong real_last; + void *host_start; + int prot_old, prot_new; + int host_prot_old, host_prot_new; -unsigned long last_brk; + if (!(flags & MAP_ANONYMOUS) + && (flags & MAP_TYPE) == MAP_SHARED + && (prot & PROT_WRITE)) { + /* + * msync() won't work with the partial page, so we return an + * error if write is possible while it is a shared mapping. + */ + errno = EINVAL; + return false; + } -/* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk - of guest address space. */ + real_last = real_start + host_page_size - 1; + host_start = g2h_untagged(real_start); + + /* Get the protection of the target pages outside the mapping. */ + prot_old = 0; + for (abi_ulong a = real_start; a < start; a += TARGET_PAGE_SIZE) { + prot_old |= page_get_flags(a); + } + for (abi_ulong a = real_last; a > last; a -= TARGET_PAGE_SIZE) { + prot_old |= page_get_flags(a); + } + + if (prot_old == 0) { + /* + * Since !(prot_old & PAGE_VALID), there were no guest pages + * outside of the fragment we need to map. Allocate a new host + * page to cover, discarding whatever else may have been present. + */ + void *p = mmap(host_start, host_page_size, + target_to_host_prot(prot), + flags | MAP_ANONYMOUS, -1, 0); + if (p != host_start) { + if (p != MAP_FAILED) { + do_munmap(p, host_page_size); + errno = EEXIST; + } + return false; + } + prot_old = prot; + } + prot_new = prot | prot_old; + + host_prot_old = target_to_host_prot(prot_old); + host_prot_new = target_to_host_prot(prot_new); + + /* Adjust protection to be able to write. */ + if (!(host_prot_old & PROT_WRITE)) { + host_prot_old |= PROT_WRITE; + mprotect(host_start, host_page_size, host_prot_old); + } + + /* Read or zero the new guest pages. */ + if (flags & MAP_ANONYMOUS) { + memset(g2h_untagged(start), 0, last - start + 1); + } else { + if (pread(fd, g2h_untagged(start), last - start + 1, offset) == -1) { + return false; + } + } + + /* Put final protection */ + if (host_prot_new != host_prot_old) { + mprotect(host_start, host_page_size, host_prot_new); + } + return true; +} + +abi_ulong task_unmapped_base; +abi_ulong elf_et_dyn_base; +abi_ulong mmap_next_start; + +/* + * Subroutine of mmap_find_vma, used when we have pre-allocated + * a chunk of guest address space. + */ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size, abi_ulong align) { - abi_ulong addr, end_addr, incr = qemu_host_page_size; - int prot; - bool looped = false; + target_ulong ret; - if (size > reserved_va) { - return (abi_ulong)-1; + ret = page_find_range_empty(start, reserved_va, size, align); + if (ret == -1 && start > mmap_min_addr) { + /* Restart at the beginning of the address space. */ + ret = page_find_range_empty(mmap_min_addr, start - 1, size, align); } - /* Note that start and size have already been aligned by mmap_find_vma. */ - - end_addr = start + size; - if (start > reserved_va - size) { - /* Start at the top of the address space. */ - end_addr = ((reserved_va - size) & -align) + size; - looped = true; - } - - /* Search downward from END_ADDR, checking to see if a page is in use. */ - addr = end_addr; - while (1) { - addr -= incr; - if (addr > end_addr) { - if (looped) { - /* Failure. The entire address space has been searched. */ - return (abi_ulong)-1; - } - /* Re-start at the top of the address space. */ - addr = end_addr = ((reserved_va - size) & -align) + size; - looped = true; - } else { - prot = page_get_flags(addr); - if (prot) { - /* Page in use. Restart below this page. */ - addr = end_addr = ((addr - size) & -align) + size; - } else if (addr && addr + size == end_addr) { - /* Success! All pages between ADDR and END_ADDR are free. */ - if (start == mmap_next_start) { - mmap_next_start = addr; - } - return addr; - } - } - } + return ret; } /* @@ -315,21 +399,21 @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size, */ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align) { + int host_page_size = qemu_real_host_page_size(); void *ptr, *prev; abi_ulong addr; int wrapped, repeat; - align = MAX(align, qemu_host_page_size); + align = MAX(align, host_page_size); /* If 'start' == 0, then a default start address is used. */ if (start == 0) { start = mmap_next_start; } else { - start &= qemu_host_page_mask; + start &= -host_page_size; } start = ROUND_UP(start, align); - - size = HOST_PAGE_ALIGN(size); + size = ROUND_UP(size, host_page_size); if (reserved_va) { return mmap_find_vma_reserved(start, size, align); @@ -348,15 +432,17 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align) * - shmat() with SHM_REMAP flag */ ptr = mmap(g2h_untagged(addr), size, PROT_NONE, - MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0); + MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0); /* ENOMEM, if host address space has no memory */ if (ptr == MAP_FAILED) { return (abi_ulong)-1; } - /* Count the number of sequential returns of the same address. - This is used to modify the search algorithm below. */ + /* + * Count the number of sequential returns of the same address. + * This is used to modify the search algorithm below. + */ repeat = (ptr == prev ? repeat + 1 : 0); if (h2g_valid(ptr + size - 1)) { @@ -364,7 +450,7 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align) if ((addr & (align - 1)) == 0) { /* Success. */ - if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) { + if (start == mmap_next_start && addr >= task_unmapped_base) { mmap_next_start = addr + size; } return addr; @@ -373,14 +459,18 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align) /* The address is not properly aligned for the target. */ switch (repeat) { case 0: - /* Assume the result that the kernel gave us is the - first with enough free space, so start again at the - next higher target page. */ + /* + * Assume the result that the kernel gave us is the + * first with enough free space, so start again at the + * next higher target page. + */ addr = ROUND_UP(addr, align); break; case 1: - /* Sometimes the kernel decides to perform the allocation - at the top end of memory instead. */ + /* + * Sometimes the kernel decides to perform the allocation + * at the top end of memory instead. + */ addr &= -align; break; case 2: @@ -393,8 +483,10 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align) break; } } else { - /* Since the result the kernel gave didn't fit, start - again at low memory. If any repetition, fail. */ + /* + * Since the result the kernel gave didn't fit, start + * again at low memory. If any repetition, fail. + */ addr = (repeat ? -1 : 0); } @@ -409,8 +501,10 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align) return (abi_ulong)-1; } wrapped = 1; - /* Don't actually use 0 when wrapping, instead indicate - that we'd truly like an allocation in low memory. */ + /* + * Don't actually use 0 when wrapping, instead indicate + * that we'd truly like an allocation in low memory. + */ addr = (mmap_min_addr > TARGET_PAGE_SIZE ? TARGET_PAGE_ALIGN(mmap_min_addr) : TARGET_PAGE_SIZE); @@ -420,214 +514,31 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align) } } -/* NOTE: all the constants are the HOST ones */ -abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot, - int flags, int fd, abi_ulong offset) +/* + * Record a successful mmap within the user-exec interval tree. + */ +static abi_long mmap_end(abi_ulong start, abi_ulong last, + abi_ulong passthrough_start, + abi_ulong passthrough_last, + int flags, int page_flags) { - abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; - int page_flags, host_prot; - - mmap_lock(); - trace_target_mmap(start, len, target_prot, flags, fd, offset); - - if (!len) { - errno = EINVAL; - goto fail; - } - - page_flags = validate_prot_to_pageflags(&host_prot, target_prot); - if (!page_flags) { - errno = EINVAL; - goto fail; - } - - /* Also check for overflows... */ - len = TARGET_PAGE_ALIGN(len); - if (!len) { - errno = ENOMEM; - goto fail; - } - - if (offset & ~TARGET_PAGE_MASK) { - errno = EINVAL; - goto fail; - } - - /* - * If we're mapping shared memory, ensure we generate code for parallel - * execution and flush old translations. This will work up to the level - * supported by the host -- anything that requires EXCP_ATOMIC will not - * be atomic with respect to an external process. - */ - if (flags & MAP_SHARED) { - CPUState *cpu = thread_cpu; - if (!(cpu->tcg_cflags & CF_PARALLEL)) { - cpu->tcg_cflags |= CF_PARALLEL; - tb_flush(cpu); - } - } - - real_start = start & qemu_host_page_mask; - host_offset = offset & qemu_host_page_mask; - - /* If the user is asking for the kernel to find a location, do that - before we truncate the length for mapping files below. */ - if (!(flags & MAP_FIXED)) { - host_len = len + offset - host_offset; - host_len = HOST_PAGE_ALIGN(host_len); - start = mmap_find_vma(real_start, host_len, TARGET_PAGE_SIZE); - if (start == (abi_ulong)-1) { - errno = ENOMEM; - goto fail; - } - } - - /* When mapping files into a memory area larger than the file, accesses - to pages beyond the file size will cause a SIGBUS. - - For example, if mmaping a file of 100 bytes on a host with 4K pages - emulating a target with 8K pages, the target expects to be able to - access the first 8K. But the host will trap us on any access beyond - 4K. - - When emulating a target with a larger page-size than the hosts, we - may need to truncate file maps at EOF and add extra anonymous pages - up to the targets page boundary. */ - - if ((qemu_real_host_page_size() < qemu_host_page_size) && - !(flags & MAP_ANONYMOUS)) { - struct stat sb; - - if (fstat (fd, &sb) == -1) - goto fail; - - /* Are we trying to create a map beyond EOF?. */ - if (offset + len > sb.st_size) { - /* If so, truncate the file map at eof aligned with - the hosts real pagesize. Additional anonymous maps - will be created beyond EOF. */ - len = REAL_HOST_PAGE_ALIGN(sb.st_size - offset); - } - } - - if (!(flags & MAP_FIXED)) { - unsigned long host_start; - void *p; - - host_len = len + offset - host_offset; - host_len = HOST_PAGE_ALIGN(host_len); - - /* Note: we prefer to control the mapping address. It is - especially important if qemu_host_page_size > - qemu_real_host_page_size */ - p = mmap(g2h_untagged(start), host_len, host_prot, - flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0); - if (p == MAP_FAILED) { - goto fail; - } - /* update start so that it points to the file position at 'offset' */ - host_start = (unsigned long)p; - if (!(flags & MAP_ANONYMOUS)) { - p = mmap(g2h_untagged(start), len, host_prot, - flags | MAP_FIXED, fd, host_offset); - if (p == MAP_FAILED) { - munmap(g2h_untagged(start), host_len); - goto fail; - } - host_start += offset - host_offset; - } - start = h2g(host_start); - } else { - if (start & ~TARGET_PAGE_MASK) { - errno = EINVAL; - goto fail; - } - end = start + len; - real_end = HOST_PAGE_ALIGN(end); - - /* - * Test if requested memory area fits target address space - * It can fail only on 64-bit host with 32-bit target. - * On any other target/host host mmap() handles this error correctly. - */ - if (end < start || !guest_range_valid_untagged(start, len)) { - errno = ENOMEM; - goto fail; - } - - /* worst case: we cannot map the file because the offset is not - aligned, so we read it */ - if (!(flags & MAP_ANONYMOUS) && - (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) { - /* msync() won't work here, so we return an error if write is - possible while it is a shared mapping */ - if ((flags & MAP_TYPE) == MAP_SHARED && - (host_prot & PROT_WRITE)) { - errno = EINVAL; - goto fail; - } - retaddr = target_mmap(start, len, target_prot | PROT_WRITE, - MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, - -1, 0); - if (retaddr == -1) - goto fail; - if (pread(fd, g2h_untagged(start), len, offset) == -1) - goto fail; - if (!(host_prot & PROT_WRITE)) { - ret = target_mprotect(start, len, target_prot); - assert(ret == 0); - } - goto the_end; - } - - /* handle the start of the mapping */ - if (start > real_start) { - if (real_end == real_start + qemu_host_page_size) { - /* one single host page */ - ret = mmap_frag(real_start, start, end, - host_prot, flags, fd, offset); - if (ret == -1) - goto fail; - goto the_end1; - } - ret = mmap_frag(real_start, start, real_start + qemu_host_page_size, - host_prot, flags, fd, offset); - if (ret == -1) - goto fail; - real_start += qemu_host_page_size; - } - /* handle the end of the mapping */ - if (end < real_end) { - ret = mmap_frag(real_end - qemu_host_page_size, - real_end - qemu_host_page_size, end, - host_prot, flags, fd, - offset + real_end - qemu_host_page_size - start); - if (ret == -1) - goto fail; - real_end -= qemu_host_page_size; - } - - /* map the middle (easier) */ - if (real_start < real_end) { - void *p; - unsigned long offset1; - if (flags & MAP_ANONYMOUS) - offset1 = 0; - else - offset1 = offset + real_start - start; - p = mmap(g2h_untagged(real_start), real_end - real_start, - host_prot, flags, fd, offset1); - if (p == MAP_FAILED) - goto fail; - } - } - the_end1: if (flags & MAP_ANONYMOUS) { page_flags |= PAGE_ANON; } page_flags |= PAGE_RESET; - page_set_flags(start, start + len, page_flags); - the_end: + if (passthrough_start > passthrough_last) { + page_set_flags(start, last, page_flags); + } else { + if (start < passthrough_start) { + page_set_flags(start, passthrough_start - 1, page_flags); + } + page_set_flags(passthrough_start, passthrough_last, + page_flags | PAGE_PASSTHROUGH); + if (passthrough_last < last) { + page_set_flags(passthrough_last + 1, last, page_flags); + } + } + shm_region_rm_complete(start, last); trace_target_mmap_complete(start); if (qemu_loglevel_mask(CPU_LOG_PAGE)) { FILE *f = qemu_log_trylock(); @@ -637,12 +548,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot, qemu_log_unlock(f); } } - tb_invalidate_phys_range(start, start + len); - mmap_unlock(); return start; -fail: - mmap_unlock(); - return -1; } #ifdef TARGET_E2K @@ -670,105 +576,505 @@ abi_ulong e2k_mmap(abi_ulong size) } #endif -static void mmap_reserve(abi_ulong start, abi_ulong size) +/* + * Special case host page size == target page size, + * where there are no edge conditions. + */ +static abi_long mmap_h_eq_g(abi_ulong start, abi_ulong len, + int host_prot, int flags, int page_flags, + int fd, off_t offset) { + void *p, *want_p = g2h_untagged(start); + abi_ulong last; + + p = mmap(want_p, len, host_prot, flags, fd, offset); + if (p == MAP_FAILED) { + return -1; + } + /* If the host kernel does not support MAP_FIXED_NOREPLACE, emulate. */ + if ((flags & MAP_FIXED_NOREPLACE) && p != want_p) { + do_munmap(p, len); + errno = EEXIST; + return -1; + } + + start = h2g(p); + last = start + len - 1; + return mmap_end(start, last, start, last, flags, page_flags); +} + +/* + * Special case host page size < target page size. + * + * The two special cases are increased guest alignment, and mapping + * past the end of a file. + * + * When mapping files into a memory area larger than the file, + * accesses to pages beyond the file size will cause a SIGBUS. + * + * For example, if mmaping a file of 100 bytes on a host with 4K + * pages emulating a target with 8K pages, the target expects to + * be able to access the first 8K. But the host will trap us on + * any access beyond 4K. + * + * When emulating a target with a larger page-size than the hosts, + * we may need to truncate file maps at EOF and add extra anonymous + * pages up to the targets page boundary. + * + * This workaround only works for files that do not change. + * If the file is later extended (e.g. ftruncate), the SIGBUS + * vanishes and the proper behaviour is that changes within the + * anon page should be reflected in the file. + * + * However, this case is rather common with executable images, + * so the workaround is important for even trivial tests, whereas + * the mmap of of a file being extended is less common. + */ +static abi_long mmap_h_lt_g(abi_ulong start, abi_ulong len, int host_prot, + int mmap_flags, int page_flags, int fd, + off_t offset, int host_page_size) +{ + void *p, *want_p = g2h_untagged(start); + off_t fileend_adj = 0; + int flags = mmap_flags; + abi_ulong last, pass_last; + + if (!(flags & MAP_ANONYMOUS)) { + struct stat sb; + + if (fstat(fd, &sb) == -1) { + return -1; + } + if (offset >= sb.st_size) { + /* + * The entire map is beyond the end of the file. + * Transform it to an anonymous mapping. + */ + flags |= MAP_ANONYMOUS; + fd = -1; + offset = 0; + } else if (offset + len > sb.st_size) { + /* + * A portion of the map is beyond the end of the file. + * Truncate the file portion of the allocation. + */ + fileend_adj = offset + len - sb.st_size; + } + } + + if (flags & (MAP_FIXED | MAP_FIXED_NOREPLACE)) { + if (fileend_adj) { + p = mmap(want_p, len, host_prot, flags | MAP_ANONYMOUS, -1, 0); + } else { + p = mmap(want_p, len, host_prot, flags, fd, offset); + } + if (p != want_p) { + if (p != MAP_FAILED) { + /* Host does not support MAP_FIXED_NOREPLACE: emulate. */ + do_munmap(p, len); + errno = EEXIST; + } + return -1; + } + + if (fileend_adj) { + void *t = mmap(p, len - fileend_adj, host_prot, + (flags & ~MAP_FIXED_NOREPLACE) | MAP_FIXED, + fd, offset); + + if (t == MAP_FAILED) { + int save_errno = errno; + + /* + * We failed a map over the top of the successful anonymous + * mapping above. The only failure mode is running out of VMAs, + * and there's nothing that we can do to detect that earlier. + * If we have replaced an existing mapping with MAP_FIXED, + * then we cannot properly recover. It's a coin toss whether + * it would be better to exit or continue here. + */ + if (!(flags & MAP_FIXED_NOREPLACE) && + !page_check_range_empty(start, start + len - 1)) { + qemu_log("QEMU target_mmap late failure: %s", + strerror(save_errno)); + } + + do_munmap(want_p, len); + errno = save_errno; + return -1; + } + } + } else { + size_t host_len, part_len; + + /* + * Take care to align the host memory. Perform a larger anonymous + * allocation and extract the aligned portion. Remap the file on + * top of that. + */ + host_len = len + TARGET_PAGE_SIZE - host_page_size; + p = mmap(want_p, host_len, host_prot, flags | MAP_ANONYMOUS, -1, 0); + if (p == MAP_FAILED) { + return -1; + } + + part_len = (uintptr_t)p & (TARGET_PAGE_SIZE - 1); + if (part_len) { + part_len = TARGET_PAGE_SIZE - part_len; + do_munmap(p, part_len); + p += part_len; + host_len -= part_len; + } + if (len < host_len) { + do_munmap(p + len, host_len - len); + } + + if (!(flags & MAP_ANONYMOUS)) { + void *t = mmap(p, len - fileend_adj, host_prot, + flags | MAP_FIXED, fd, offset); + + if (t == MAP_FAILED) { + int save_errno = errno; + do_munmap(p, len); + errno = save_errno; + return -1; + } + } + + start = h2g(p); + } + + last = start + len - 1; + if (fileend_adj) { + pass_last = ROUND_UP(last - fileend_adj, host_page_size) - 1; + } else { + pass_last = last; + } + return mmap_end(start, last, start, pass_last, mmap_flags, page_flags); +} + +/* + * Special case host page size > target page size. + * + * The two special cases are address and file offsets that are valid + * for the guest that cannot be directly represented by the host. + */ +static abi_long mmap_h_gt_g(abi_ulong start, abi_ulong len, + int target_prot, int host_prot, + int flags, int page_flags, int fd, + off_t offset, int host_page_size) +{ + void *p, *want_p = g2h_untagged(start); + off_t host_offset = offset & -host_page_size; + abi_ulong last, real_start, real_last; + bool misaligned_offset = false; + size_t host_len; + + if (!(flags & (MAP_FIXED | MAP_FIXED_NOREPLACE))) { + /* + * Adjust the offset to something representable on the host. + */ + host_len = len + offset - host_offset; + p = mmap(want_p, host_len, host_prot, flags, fd, host_offset); + if (p == MAP_FAILED) { + return -1; + } + + /* Update start to the file position at offset. */ + p += offset - host_offset; + + start = h2g(p); + last = start + len - 1; + return mmap_end(start, last, start, last, flags, page_flags); + } + + if (!(flags & MAP_ANONYMOUS)) { + misaligned_offset = (start ^ offset) & (host_page_size - 1); + + /* + * The fallback for misalignment is a private mapping + read. + * This carries none of semantics required of MAP_SHARED. + */ + if (misaligned_offset && (flags & MAP_TYPE) != MAP_PRIVATE) { + errno = EINVAL; + return -1; + } + } + + last = start + len - 1; + real_start = start & -host_page_size; + real_last = ROUND_UP(last, host_page_size) - 1; + + /* + * Handle the start and end of the mapping. + */ + if (real_start < start) { + abi_ulong real_page_last = real_start + host_page_size - 1; + if (last <= real_page_last) { + /* Entire allocation a subset of one host page. */ + if (!mmap_frag(real_start, start, last, target_prot, + flags, fd, offset)) { + return -1; + } + return mmap_end(start, last, -1, 0, flags, page_flags); + } + + if (!mmap_frag(real_start, start, real_page_last, target_prot, + flags, fd, offset)) { + return -1; + } + real_start = real_page_last + 1; + } + + if (last < real_last) { + abi_ulong real_page_start = real_last - host_page_size + 1; + if (!mmap_frag(real_page_start, real_page_start, last, + target_prot, flags, fd, + offset + real_page_start - start)) { + return -1; + } + real_last = real_page_start - 1; + } + + if (real_start > real_last) { + return mmap_end(start, last, -1, 0, flags, page_flags); + } + + /* + * Handle the middle of the mapping. + */ + + host_len = real_last - real_start + 1; + want_p += real_start - start; + + if (flags & MAP_ANONYMOUS) { + p = mmap(want_p, host_len, host_prot, flags, -1, 0); + } else if (!misaligned_offset) { + p = mmap(want_p, host_len, host_prot, flags, fd, + offset + real_start - start); + } else { + p = mmap(want_p, host_len, host_prot | PROT_WRITE, + flags | MAP_ANONYMOUS, -1, 0); + } + if (p != want_p) { + if (p != MAP_FAILED) { + do_munmap(p, host_len); + errno = EEXIST; + } + return -1; + } + + if (misaligned_offset) { + /* TODO: The read could be short. */ + if (pread(fd, p, host_len, offset + real_start - start) != host_len) { + do_munmap(p, host_len); + return -1; + } + if (!(host_prot & PROT_WRITE)) { + mprotect(p, host_len, host_prot); + } + } + + return mmap_end(start, last, -1, 0, flags, page_flags); +} + +static abi_long target_mmap__locked(abi_ulong start, abi_ulong len, + int target_prot, int flags, int page_flags, + int fd, off_t offset) +{ + int host_page_size = qemu_real_host_page_size(); + int host_prot; + + /* + * For reserved_va, we are in full control of the allocation. + * Find a suitable hole and convert to MAP_FIXED. + */ + if (reserved_va) { + if (flags & MAP_FIXED_NOREPLACE) { + /* Validate that the chosen range is empty. */ + if (!page_check_range_empty(start, start + len - 1)) { + errno = EEXIST; + return -1; + } + flags = (flags & ~MAP_FIXED_NOREPLACE) | MAP_FIXED; + } else if (!(flags & MAP_FIXED)) { + abi_ulong real_start = start & -host_page_size; + off_t host_offset = offset & -host_page_size; + size_t real_len = len + offset - host_offset; + abi_ulong align = MAX(host_page_size, TARGET_PAGE_SIZE); + + start = mmap_find_vma(real_start, real_len, align); + if (start == (abi_ulong)-1) { + errno = ENOMEM; + return -1; + } + start += offset - host_offset; + flags |= MAP_FIXED; + } + } + + host_prot = target_to_host_prot(target_prot); + + if (host_page_size == TARGET_PAGE_SIZE) { + return mmap_h_eq_g(start, len, host_prot, flags, + page_flags, fd, offset); + } else if (host_page_size < TARGET_PAGE_SIZE) { + return mmap_h_lt_g(start, len, host_prot, flags, + page_flags, fd, offset, host_page_size); + } else { + return mmap_h_gt_g(start, len, target_prot, host_prot, flags, + page_flags, fd, offset, host_page_size); + } +} + +/* NOTE: all the constants are the HOST ones */ +abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot, + int flags, int fd, off_t offset) +{ + abi_long ret; + int page_flags; + + trace_target_mmap(start, len, target_prot, flags, fd, offset); + + if (!len) { + errno = EINVAL; + return -1; + } + + page_flags = validate_prot_to_pageflags(target_prot); + if (!page_flags) { + errno = EINVAL; + return -1; + } + + /* Also check for overflows... */ + len = TARGET_PAGE_ALIGN(len); + if (!len || len != (size_t)len) { + errno = ENOMEM; + return -1; + } + + if (offset & ~TARGET_PAGE_MASK) { + errno = EINVAL; + return -1; + } + if (flags & (MAP_FIXED | MAP_FIXED_NOREPLACE)) { + if (start & ~TARGET_PAGE_MASK) { + errno = EINVAL; + return -1; + } + if (!guest_range_valid_untagged(start, len)) { + errno = ENOMEM; + return -1; + } + } + + mmap_lock(); + + ret = target_mmap__locked(start, len, target_prot, flags, + page_flags, fd, offset); + + mmap_unlock(); + + /* + * If we're mapping shared memory, ensure we generate code for parallel + * execution and flush old translations. This will work up to the level + * supported by the host -- anything that requires EXCP_ATOMIC will not + * be atomic with respect to an external process. + */ + if (ret != -1 && (flags & MAP_TYPE) != MAP_PRIVATE) { + CPUState *cpu = thread_cpu; + if (!(cpu->tcg_cflags & CF_PARALLEL)) { + cpu->tcg_cflags |= CF_PARALLEL; + tb_flush(cpu); + } + } + + return ret; +} + +static int mmap_reserve_or_unmap(abi_ulong start, abi_ulong len) +{ + int host_page_size = qemu_real_host_page_size(); abi_ulong real_start; - abi_ulong real_end; - abi_ulong addr; - abi_ulong end; + abi_ulong real_last; + abi_ulong real_len; + abi_ulong last; + abi_ulong a; + void *host_start; int prot; - real_start = start & qemu_host_page_mask; - real_end = HOST_PAGE_ALIGN(start + size); - end = start + size; - if (start > real_start) { - /* handle host page containing start */ + last = start + len - 1; + real_start = start & -host_page_size; + real_last = ROUND_UP(last, host_page_size) - 1; + + /* + * If guest pages remain on the first or last host pages, + * adjust the deallocation to retain those guest pages. + * The single page special case is required for the last page, + * lest real_start overflow to zero. + */ + if (real_last - real_start < host_page_size) { prot = 0; - for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) { - prot |= page_get_flags(addr); + for (a = real_start; a < start; a += TARGET_PAGE_SIZE) { + prot |= page_get_flags(a); } - if (real_end == real_start + qemu_host_page_size) { - for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { - prot |= page_get_flags(addr); - } - end = real_end; + for (a = last; a < real_last; a += TARGET_PAGE_SIZE) { + prot |= page_get_flags(a + 1); } - if (prot != 0) - real_start += qemu_host_page_size; - } - if (end < real_end) { - prot = 0; - for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { - prot |= page_get_flags(addr); + if (prot != 0) { + return 0; + } + } else { + for (prot = 0, a = real_start; a < start; a += TARGET_PAGE_SIZE) { + prot |= page_get_flags(a); + } + if (prot != 0) { + real_start += host_page_size; + } + + for (prot = 0, a = last; a < real_last; a += TARGET_PAGE_SIZE) { + prot |= page_get_flags(a + 1); + } + if (prot != 0) { + real_last -= host_page_size; + } + + if (real_last < real_start) { + return 0; } - if (prot != 0) - real_end -= qemu_host_page_size; - } - if (real_start != real_end) { - mmap(g2h_untagged(real_start), real_end - real_start, PROT_NONE, - MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, - -1, 0); } + + real_len = real_last - real_start + 1; + host_start = g2h_untagged(real_start); + + return do_munmap(host_start, real_len); } int target_munmap(abi_ulong start, abi_ulong len) { - abi_ulong end, real_start, real_end, addr; - int prot, ret; + int ret; trace_target_munmap(start, len); - if (start & ~TARGET_PAGE_MASK) - return -TARGET_EINVAL; + if (start & ~TARGET_PAGE_MASK) { + errno = EINVAL; + return -1; + } len = TARGET_PAGE_ALIGN(len); if (len == 0 || !guest_range_valid_untagged(start, len)) { - return -TARGET_EINVAL; + errno = EINVAL; + return -1; } mmap_lock(); - end = start + len; - real_start = start & qemu_host_page_mask; - real_end = HOST_PAGE_ALIGN(end); - - if (start > real_start) { - /* handle host page containing start */ - prot = 0; - for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) { - prot |= page_get_flags(addr); - } - if (real_end == real_start + qemu_host_page_size) { - for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { - prot |= page_get_flags(addr); - } - end = real_end; - } - if (prot != 0) - real_start += qemu_host_page_size; - } - if (end < real_end) { - prot = 0; - for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { - prot |= page_get_flags(addr); - } - if (prot != 0) - real_end -= qemu_host_page_size; - } - - ret = 0; - /* unmap what we can */ - if (real_start < real_end) { - if (reserved_va) { - mmap_reserve(real_start, real_end - real_start); - } else { - ret = munmap(g2h_untagged(real_start), real_end - real_start); - } - } - - if (ret == 0) { - page_set_flags(start, start + len, 0); - tb_invalidate_phys_range(start, start + len); + ret = mmap_reserve_or_unmap(start, len); + if (likely(ret == 0)) { + page_set_flags(start, start + len - 1, 0); + shm_region_rm_complete(start, start + len - 1); } mmap_unlock(); + return ret; } @@ -795,9 +1101,11 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, flags, g2h_untagged(new_addr)); if (reserved_va && host_addr != MAP_FAILED) { - /* If new and old addresses overlap then the above mremap will - already have failed with EINVAL. */ - mmap_reserve(old_addr, old_size); + /* + * If new and old addresses overlap then the above mremap will + * already have failed with EINVAL. + */ + mmap_reserve_or_unmap(old_addr, old_size); } } else if (flags & MREMAP_MAYMOVE) { abi_ulong mmap_start; @@ -812,20 +1120,20 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, flags | MREMAP_FIXED, g2h_untagged(mmap_start)); if (reserved_va) { - mmap_reserve(old_addr, old_size); + mmap_reserve_or_unmap(old_addr, old_size); } } } else { - int prot = 0; + int page_flags = 0; if (reserved_va && old_size < new_size) { abi_ulong addr; for (addr = old_addr + old_size; addr < old_addr + new_size; addr++) { - prot |= page_get_flags(addr); + page_flags |= page_get_flags(addr); } } - if (prot == 0) { + if (page_flags == 0) { host_addr = mremap(g2h_untagged(old_addr), old_size, new_size, flags); @@ -838,7 +1146,8 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, errno = ENOMEM; host_addr = MAP_FAILED; } else if (reserved_va && old_size > new_size) { - mmap_reserve(old_addr + old_size, old_size - new_size); + mmap_reserve_or_unmap(old_addr + old_size, + old_size - new_size); } } } else { @@ -852,11 +1161,305 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, } else { new_addr = h2g(host_addr); prot = page_get_flags(old_addr); - page_set_flags(old_addr, old_addr + old_size, 0); - page_set_flags(new_addr, new_addr + new_size, + page_set_flags(old_addr, old_addr + old_size - 1, 0); + shm_region_rm_complete(old_addr, old_addr + old_size - 1); + page_set_flags(new_addr, new_addr + new_size - 1, prot | PAGE_VALID | PAGE_RESET); + shm_region_rm_complete(new_addr, new_addr + new_size - 1); } - tb_invalidate_phys_range(new_addr, new_addr + new_size); mmap_unlock(); return new_addr; } + +abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice) +{ + abi_ulong len; + int ret = 0; + + if (start & ~TARGET_PAGE_MASK) { + return -TARGET_EINVAL; + } + if (len_in == 0) { + return 0; + } + len = TARGET_PAGE_ALIGN(len_in); + if (len == 0 || !guest_range_valid_untagged(start, len)) { + return -TARGET_EINVAL; + } + + /* Translate for some architectures which have different MADV_xxx values */ + switch (advice) { + case TARGET_MADV_DONTNEED: /* alpha */ + advice = MADV_DONTNEED; + break; + case TARGET_MADV_WIPEONFORK: /* parisc */ + advice = MADV_WIPEONFORK; + break; + case TARGET_MADV_KEEPONFORK: /* parisc */ + advice = MADV_KEEPONFORK; + break; + /* we do not care about the other MADV_xxx values yet */ + } + + /* + * Most advice values are hints, so ignoring and returning success is ok. + * + * However, some advice values such as MADV_DONTNEED, MADV_WIPEONFORK and + * MADV_KEEPONFORK are not hints and need to be emulated. + * + * A straight passthrough for those may not be safe because qemu sometimes + * turns private file-backed mappings into anonymous mappings. + * If all guest pages have PAGE_PASSTHROUGH set, mappings have the + * same semantics for the host as for the guest. + * + * We pass through MADV_WIPEONFORK and MADV_KEEPONFORK if possible and + * return failure if not. + * + * MADV_DONTNEED is passed through as well, if possible. + * If passthrough isn't possible, we nevertheless (wrongly!) return + * success, which is broken but some userspace programs fail to work + * otherwise. Completely implementing such emulation is quite complicated + * though. + */ + mmap_lock(); + switch (advice) { + case MADV_WIPEONFORK: + case MADV_KEEPONFORK: + ret = -EINVAL; + /* fall through */ + case MADV_DONTNEED: + if (page_check_range(start, len, PAGE_PASSTHROUGH)) { + ret = get_errno(madvise(g2h_untagged(start), len, advice)); + if ((advice == MADV_DONTNEED) && (ret == 0)) { + page_reset_target_data(start, start + len - 1); + } + } + } + mmap_unlock(); + + return ret; +} + +#ifndef TARGET_FORCE_SHMLBA +/* + * For most architectures, SHMLBA is the same as the page size; + * some architectures have larger values, in which case they should + * define TARGET_FORCE_SHMLBA and provide a target_shmlba() function. + * This corresponds to the kernel arch code defining __ARCH_FORCE_SHMLBA + * and defining its own value for SHMLBA. + * + * The kernel also permits SHMLBA to be set by the architecture to a + * value larger than the page size without setting __ARCH_FORCE_SHMLBA; + * this means that addresses are rounded to the large size if + * SHM_RND is set but addresses not aligned to that size are not rejected + * as long as they are at least page-aligned. Since the only architecture + * which uses this is ia64 this code doesn't provide for that oddity. + */ +static inline abi_ulong target_shmlba(CPUArchState *cpu_env) +{ + return TARGET_PAGE_SIZE; +} +#endif + +#if defined(__arm__) || defined(__mips__) || defined(__sparc__) +#define HOST_FORCE_SHMLBA 1 +#else +#define HOST_FORCE_SHMLBA 0 +#endif + +abi_ulong target_shmat(CPUArchState *cpu_env, int shmid, + abi_ulong shmaddr, int shmflg) +{ + CPUState *cpu = env_cpu(cpu_env); + struct shmid_ds shm_info; + int ret; + int h_pagesize; + int t_shmlba, h_shmlba, m_shmlba; + size_t t_len, h_len, m_len; + + /* shmat pointers are always untagged */ + + /* + * Because we can't use host shmat() unless the address is sufficiently + * aligned for the host, we'll need to check both. + * TODO: Could be fixed with softmmu. + */ + t_shmlba = target_shmlba(cpu_env); + h_pagesize = qemu_real_host_page_size(); + h_shmlba = (HOST_FORCE_SHMLBA ? SHMLBA : h_pagesize); + m_shmlba = MAX(t_shmlba, h_shmlba); + + if (shmaddr) { + if (shmaddr & (m_shmlba - 1)) { + if (shmflg & SHM_RND) { + /* + * The guest is allowing the kernel to round the address. + * Assume that the guest is ok with us rounding to the + * host required alignment too. Anyway if we don't, we'll + * get an error from the kernel. + */ + shmaddr &= ~(m_shmlba - 1); + if (shmaddr == 0 && (shmflg & SHM_REMAP)) { + return -TARGET_EINVAL; + } + } else { + int require = TARGET_PAGE_SIZE; +#ifdef TARGET_FORCE_SHMLBA + require = t_shmlba; +#endif + /* + * Include host required alignment, as otherwise we cannot + * use host shmat at all. + */ + require = MAX(require, h_shmlba); + if (shmaddr & (require - 1)) { + return -TARGET_EINVAL; + } + } + } + } else { + if (shmflg & SHM_REMAP) { + return -TARGET_EINVAL; + } + } + /* All rounding now manually concluded. */ + shmflg &= ~SHM_RND; + + /* Find out the length of the shared memory segment. */ + ret = get_errno(shmctl(shmid, IPC_STAT, &shm_info)); + if (is_error(ret)) { + /* can't get length, bail out */ + return ret; + } + t_len = TARGET_PAGE_ALIGN(shm_info.shm_segsz); + h_len = ROUND_UP(shm_info.shm_segsz, h_pagesize); + m_len = MAX(t_len, h_len); + + if (!guest_range_valid_untagged(shmaddr, m_len)) { + return -TARGET_EINVAL; + } + + WITH_MMAP_LOCK_GUARD() { + bool mapped = false; + void *want, *test; + abi_ulong last; + + if (!shmaddr) { + shmaddr = mmap_find_vma(0, m_len, m_shmlba); + if (shmaddr == -1) { + return -TARGET_ENOMEM; + } + mapped = !reserved_va; + } else if (shmflg & SHM_REMAP) { + /* + * If host page size > target page size, the host shmat may map + * more memory than the guest expects. Reject a mapping that + * would replace memory in the unexpected gap. + * TODO: Could be fixed with softmmu. + */ + if (t_len < h_len && + !page_check_range_empty(shmaddr + t_len, + shmaddr + h_len - 1)) { + return -TARGET_EINVAL; + } + } else { + if (!page_check_range_empty(shmaddr, shmaddr + m_len - 1)) { + return -TARGET_EINVAL; + } + } + + /* All placement is now complete. */ + want = (void *)g2h_untagged(shmaddr); + + /* + * Map anonymous pages across the entire range, then remap with + * the shared memory. This is required for a number of corner + * cases for which host and guest page sizes differ. + */ + if (h_len != t_len) { + int mmap_p = PROT_READ | (shmflg & SHM_RDONLY ? 0 : PROT_WRITE); + int mmap_f = MAP_PRIVATE | MAP_ANONYMOUS + | (reserved_va || mapped || (shmflg & SHM_REMAP) + ? MAP_FIXED : MAP_FIXED_NOREPLACE); + + test = mmap(want, m_len, mmap_p, mmap_f, -1, 0); + if (unlikely(test != want)) { + /* shmat returns EINVAL not EEXIST like mmap. */ + ret = (test == MAP_FAILED && errno != EEXIST + ? get_errno(-1) : -TARGET_EINVAL); + if (mapped) { + do_munmap(want, m_len); + } + return ret; + } + mapped = true; + } + + if (reserved_va || mapped) { + shmflg |= SHM_REMAP; + } + test = shmat(shmid, want, shmflg); + if (test == MAP_FAILED) { + ret = get_errno(-1); + if (mapped) { + do_munmap(want, m_len); + } + return ret; + } + assert(test == want); + + last = shmaddr + m_len - 1; + page_set_flags(shmaddr, last, + PAGE_VALID | PAGE_RESET | PAGE_READ | + (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE) | + (shmflg & SHM_EXEC ? PAGE_EXEC : 0)); + + shm_region_rm_complete(shmaddr, last); + shm_region_add(shmaddr, last); + } + + /* + * We're mapping shared memory, so ensure we generate code for parallel + * execution and flush old translations. This will work up to the level + * supported by the host -- anything that requires EXCP_ATOMIC will not + * be atomic with respect to an external process. + */ + if (!(cpu->tcg_cflags & CF_PARALLEL)) { + cpu->tcg_cflags |= CF_PARALLEL; + tb_flush(cpu); + } + + if (qemu_loglevel_mask(CPU_LOG_PAGE)) { + FILE *f = qemu_log_trylock(); + if (f) { + fprintf(f, "page layout changed following shmat\n"); + page_dump(f); + qemu_log_unlock(f); + } + } + return shmaddr; +} + +abi_long target_shmdt(abi_ulong shmaddr) +{ + abi_long rv; + + /* shmdt pointers are always untagged */ + + WITH_MMAP_LOCK_GUARD() { + abi_ulong last = shm_region_find(shmaddr); + if (last == 0) { + return -TARGET_EINVAL; + } + + rv = get_errno(shmdt(g2h_untagged(shmaddr))); + if (rv == 0) { + abi_ulong size = last - shmaddr + 1; + + page_set_flags(shmaddr, last, 0); + shm_region_rm_complete(shmaddr, last); + mmap_reserve_or_unmap(shmaddr, size); + } + } + return rv; +} diff --git a/linux-user/nios2/cpu_loop.c b/linux-user/nios2/cpu_loop.c index da77ede76b..7fe08c8750 100644 --- a/linux-user/nios2/cpu_loop.c +++ b/linux-user/nios2/cpu_loop.c @@ -32,6 +32,7 @@ void cpu_loop(CPUNios2State *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); + process_queued_cpu_work(cs); switch (trapnr) { case EXCP_INTERRUPT: diff --git a/linux-user/nios2/signal.c b/linux-user/nios2/signal.c index 32b3dc99c6..64c345f409 100644 --- a/linux-user/nios2/signal.c +++ b/linux-user/nios2/signal.c @@ -157,7 +157,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, return; } - tswap_siginfo(&frame->info, info); + frame->info = *info; /* Create the ucontext. */ __put_user(0, &frame->uc.tuc_flags); diff --git a/linux-user/nios2/target_mman.h b/linux-user/nios2/target_mman.h new file mode 100644 index 0000000000..ab16ad4f03 --- /dev/null +++ b/linux-user/nios2/target_mman.h @@ -0,0 +1,11 @@ +/* + * arch/nios2/include/asm/processor.h: + * TASK_UNMAPPED_BASE PAGE_ALIGN(TASK_SIZE / 3) + * TASK_SIZE 0x7FFF0000UL + */ +#define TASK_UNMAPPED_BASE TARGET_PAGE_ALIGN(0x7FFF0000 / 3) + +/* arch/nios2/include/asm/elf.h */ +#define ELF_ET_DYN_BASE 0xD0000000 + +#include "../generic/target_mman.h" diff --git a/linux-user/nios2/target_proc.h b/linux-user/nios2/target_proc.h new file mode 100644 index 0000000000..43fe29ca72 --- /dev/null +++ b/linux-user/nios2/target_proc.h @@ -0,0 +1 @@ +/* No target-specific /proc support */ diff --git a/linux-user/openrisc/signal.c b/linux-user/openrisc/signal.c index be8b68784a..cb74a9fe5e 100644 --- a/linux-user/openrisc/signal.c +++ b/linux-user/openrisc/signal.c @@ -103,7 +103,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, } if (ka->sa_flags & SA_SIGINFO) { - tswap_siginfo(&frame->info, info); + frame->info = *info; } __put_user(0, &frame->uc.tuc_flags); diff --git a/linux-user/openrisc/target_mman.h b/linux-user/openrisc/target_mman.h new file mode 100644 index 0000000000..243c1d5f26 --- /dev/null +++ b/linux-user/openrisc/target_mman.h @@ -0,0 +1,11 @@ +/* + * arch/openrisc/include/asm/processor.h: + * TASK_UNMAPPED_BASE (TASK_SIZE / 8 * 3) + * TASK_SIZE (0x80000000UL) + */ +#define TASK_UNMAPPED_BASE 0x30000000 + +/* arch/openrisc/include/asm/elf.h */ +#define ELF_ET_DYN_BASE 0x08000000 + +#include "../generic/target_mman.h" diff --git a/linux-user/openrisc/target_proc.h b/linux-user/openrisc/target_proc.h new file mode 100644 index 0000000000..43fe29ca72 --- /dev/null +++ b/linux-user/openrisc/target_proc.h @@ -0,0 +1 @@ +/* No target-specific /proc support */ diff --git a/linux-user/ppc/Makefile.vdso b/linux-user/ppc/Makefile.vdso new file mode 100644 index 0000000000..3ca3c6b83e --- /dev/null +++ b/linux-user/ppc/Makefile.vdso @@ -0,0 +1,20 @@ +include $(BUILD_DIR)/tests/tcg/ppc64-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/ppc +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso-32.so $(SUBDIR)/vdso-64.so $(SUBDIR)/vdso-64le.so + +LDFLAGS32 = -nostdlib -shared -Wl,-T,$(SUBDIR)/vdso-32.ld \ + -Wl,-h,linux-vdso32.so.1 -Wl,--hash-style=both -Wl,--build-id=sha1 +LDFLAGS64 = -nostdlib -shared -Wl,-T,$(SUBDIR)/vdso-64.ld \ + -Wl,-h,linux-vdso64.so.1 -Wl,--hash-style=both -Wl,--build-id=sha1 + +$(SUBDIR)/vdso-32.so: vdso.S vdso-32.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS32) -m32 $< + +$(SUBDIR)/vdso-64.so: vdso.S vdso-64.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS64) -mbig-endian $< + +$(SUBDIR)/vdso-64le.so: vdso.S vdso-64.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS64) -mlittle-endian $< diff --git a/linux-user/ppc/meson.build b/linux-user/ppc/meson.build index 19fead7bc8..80cacae396 100644 --- a/linux-user/ppc/meson.build +++ b/linux-user/ppc/meson.build @@ -3,3 +3,15 @@ syscall_nr_generators += { arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], output: '@BASENAME@_nr.h') } + +vdso_32_inc = gen_vdso.process('vdso-32.so', extra_args: [ + '-s', '__kernel_sigtramp32', + '-r', '__kernel_sigtramp_rt32' + ]) +linux_user_ss.add(when: 'TARGET_PPC', if_true: vdso_32_inc) + +vdso_64_inc = gen_vdso.process('vdso-64.so', + extra_args: ['-r', '__kernel_sigtramp_rt64']) +vdso_64le_inc = gen_vdso.process('vdso-64le.so', + extra_args: ['-r', '__kernel_sigtramp_rt64']) +linux_user_ss.add(when: 'TARGET_PPC64', if_true: [vdso_64_inc, vdso_64le_inc]) diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index 07729c1653..652038a53c 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -21,14 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" - -/* Size of dummy stack frame allocated when calling signal handler. - See arch/powerpc/include/asm/ptrace.h. */ -#if defined(TARGET_PPC64) -#define SIGNAL_FRAMESIZE 128 -#else -#define SIGNAL_FRAMESIZE 64 -#endif +#include "vdso-asmoffset.h" /* See arch/powerpc/include/asm/ucontext.h. Only used for 32-bit PPC; on 64-bit PPC, sigcontext and mcontext are one and the same. */ @@ -73,6 +66,16 @@ struct target_mcontext { #endif }; +QEMU_BUILD_BUG_ON(offsetof(struct target_mcontext, mc_fregs) + != offsetof_mcontext_fregs); +#if defined(TARGET_PPC64) +QEMU_BUILD_BUG_ON(offsetof(struct target_mcontext, v_regs) + != offsetof_mcontext_vregs_ptr); +#else +QEMU_BUILD_BUG_ON(offsetof(struct target_mcontext, mc_vregs) + != offsetof_mcontext_vregs); +#endif + /* See arch/powerpc/include/asm/sigcontext.h. */ struct target_sigcontext { target_ulong _unused[4]; @@ -161,6 +164,7 @@ struct target_ucontext { #endif }; +#if !defined(TARGET_PPC64) /* See arch/powerpc/kernel/signal_32.c. */ struct target_sigframe { struct target_sigcontext sctx; @@ -168,6 +172,10 @@ struct target_sigframe { int32_t abigap[56]; }; +QEMU_BUILD_BUG_ON(offsetof(struct target_sigframe, mctx) + != offsetof_sigframe_mcontext); +#endif + #if defined(TARGET_PPC64) #define TARGET_TRAMP_SIZE 6 @@ -184,6 +192,10 @@ struct target_rt_sigframe { char abigap[288]; } __attribute__((aligned(16))); +QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, + uc.tuc_sigcontext.mcontext) + != offsetof_rt_sigframe_mcontext); + #else struct target_rt_sigframe { @@ -192,6 +204,9 @@ struct target_rt_sigframe { int32_t abigap[56]; }; +QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, uc.tuc_mcontext) + != offsetof_rt_sigframe_mcontext); + #endif #if defined(TARGET_PPC64) @@ -243,9 +258,7 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) __put_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); __put_user(cpu_read_xer(env), &frame->mc_gregs[TARGET_PT_XER]); - for (i = 0; i < ARRAY_SIZE(env->crf); i++) { - ccr |= env->crf[i] << (32 - ((i + 1) * 4)); - } + ccr = ppc_get_cr(env); __put_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); /* Save Altivec registers if necessary. */ @@ -335,10 +348,7 @@ static void restore_user_regs(CPUPPCState *env, cpu_write_xer(env, xer); __get_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); - for (i = 0; i < ARRAY_SIZE(env->crf); i++) { - env->crf[i] = (ccr >> (32 - ((i + 1) * 4))) & 0xf; - } - + ppc_set_cr(env, ccr); if (!sig) { env->gpr[2] = save_r2; } @@ -476,14 +486,14 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, int i, err = 0; #if defined(TARGET_PPC64) struct target_sigcontext *sc = 0; - struct image_info *image = ((TaskState *)thread_cpu->opaque)->info; + struct image_info *image = get_task_state(thread_cpu)->info; #endif rt_sf_addr = get_sigframe(ka, env, sizeof(*rt_sf)); if (!lock_user_struct(VERIFY_WRITE, rt_sf, rt_sf_addr, 1)) goto sigsegv; - tswap_siginfo(&rt_sf->info, info); + rt_sf->info = *info; __put_user(0, &rt_sf->uc.tuc_flags); __put_user(0, &rt_sf->uc.tuc_link); @@ -492,7 +502,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(h2g (&rt_sf->uc.tuc_mcontext), &rt_sf->uc.tuc_regs); #endif - for(i = 0; i < TARGET_NSIG_WORDS; i++) { + for (i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &rt_sf->uc.tuc_sigmask.sig[i]); } @@ -663,7 +673,7 @@ abi_long do_swapcontext(CPUArchState *env, abi_ulong uold_ctx, } if (uold_ctx) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); if (!lock_user_struct(VERIFY_WRITE, uctx, uold_ctx, 1)) { return -TARGET_EFAULT; diff --git a/linux-user/ppc/target_mman.h b/linux-user/ppc/target_mman.h new file mode 100644 index 0000000000..646d1ccae7 --- /dev/null +++ b/linux-user/ppc/target_mman.h @@ -0,0 +1,29 @@ +#ifndef PPC_TARGET_MMAN_H +#define PPC_TARGET_MMAN_H + +#define TARGET_MAP_NORESERVE 0x40 +#define TARGET_MAP_LOCKED 0x80 + +/* + * arch/powerpc/include/asm/task_size_64.h + * TASK_UNMAPPED_BASE_USER32 (PAGE_ALIGN(TASK_SIZE_USER32 / 4)) + * TASK_UNMAPPED_BASE_USER64 (PAGE_ALIGN(DEFAULT_MAP_WINDOW_USER64 / 4)) + * TASK_SIZE_USER32 (0x0000000100000000UL - (1 * PAGE_SIZE)) + * DEFAULT_MAP_WINDOW_USER64 TASK_SIZE_64TB (with 4k pages) + */ +#ifdef TARGET_PPC64 +#define TASK_UNMAPPED_BASE 0x0000100000000000ull +#else +#define TASK_UNMAPPED_BASE 0x40000000 +#endif + +/* arch/powerpc/include/asm/elf.h */ +#ifdef TARGET_PPC64 +#define ELF_ET_DYN_BASE 0x100000000ull +#else +#define ELF_ET_DYN_BASE 0x000400000 +#endif + +#include "../generic/target_mman.h" + +#endif diff --git a/linux-user/ppc/target_proc.h b/linux-user/ppc/target_proc.h new file mode 100644 index 0000000000..43fe29ca72 --- /dev/null +++ b/linux-user/ppc/target_proc.h @@ -0,0 +1 @@ +/* No target-specific /proc support */ diff --git a/linux-user/ppc/vdso-32.ld b/linux-user/ppc/vdso-32.ld new file mode 100644 index 0000000000..6962696540 --- /dev/null +++ b/linux-user/ppc/vdso-32.ld @@ -0,0 +1,70 @@ +/* + * Linker script for linux powerpc64 replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_2.6.15 { + global: + __kernel_gettimeofday; + __kernel_clock_gettime; + __kernel_clock_gettime64; + __kernel_clock_getres; + __kernel_time; + __kernel_sync_dicache; + __kernel_sigtramp32; + __kernel_sigtramp_rt32; + __kernel_getcpu; + local: *; + }; +} + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; /* FLAGS=RWX */ + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + .data : { + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load +} diff --git a/linux-user/ppc/vdso-32.so b/linux-user/ppc/vdso-32.so new file mode 100755 index 0000000000..b19baafb0d Binary files /dev/null and b/linux-user/ppc/vdso-32.so differ diff --git a/linux-user/ppc/vdso-64.ld b/linux-user/ppc/vdso-64.ld new file mode 100644 index 0000000000..a55c65ed54 --- /dev/null +++ b/linux-user/ppc/vdso-64.ld @@ -0,0 +1,68 @@ +/* + * Linker script for linux powerpc64 replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_2.6.15 { + global: + __kernel_gettimeofday; + __kernel_clock_gettime; + __kernel_clock_getres; + __kernel_sync_dicache; + __kernel_sigtramp_rt64; + __kernel_getcpu; + __kernel_time; + local: *; + }; +} + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; /* FLAGS=RWX */ + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + .data : { + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load +} diff --git a/linux-user/ppc/vdso-64.so b/linux-user/ppc/vdso-64.so new file mode 100755 index 0000000000..913c831b38 Binary files /dev/null and b/linux-user/ppc/vdso-64.so differ diff --git a/linux-user/ppc/vdso-64le.so b/linux-user/ppc/vdso-64le.so new file mode 100755 index 0000000000..258a03b807 Binary files /dev/null and b/linux-user/ppc/vdso-64le.so differ diff --git a/linux-user/ppc/vdso-asmoffset.h b/linux-user/ppc/vdso-asmoffset.h new file mode 100644 index 0000000000..6844c8c81c --- /dev/null +++ b/linux-user/ppc/vdso-asmoffset.h @@ -0,0 +1,20 @@ +/* + * Size of dummy stack frame allocated when calling signal handler. + * See arch/powerpc/include/asm/ptrace.h. + */ +#ifdef TARGET_ABI32 +# define SIGNAL_FRAMESIZE 64 +#else +# define SIGNAL_FRAMESIZE 128 +#endif + +#ifdef TARGET_ABI32 +# define offsetof_sigframe_mcontext 0x20 +# define offsetof_rt_sigframe_mcontext 0x140 +# define offsetof_mcontext_fregs 0xc0 +# define offsetof_mcontext_vregs 0x1d0 +#else +# define offsetof_rt_sigframe_mcontext 0xe8 +# define offsetof_mcontext_fregs 0x180 +# define offsetof_mcontext_vregs_ptr 0x288 +#endif diff --git a/linux-user/ppc/vdso.S b/linux-user/ppc/vdso.S new file mode 100644 index 0000000000..2e79ea9808 --- /dev/null +++ b/linux-user/ppc/vdso.S @@ -0,0 +1,239 @@ +/* + * PowerPC linux replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#ifndef _ARCH_PPC64 +# define TARGET_ABI32 +#endif +#include "vdso-asmoffset.h" + + + .text + +.macro endf name + .globl \name + .size \name, .-\name + /* For PPC64, functions have special linkage; we export pointers. */ +#ifndef _ARCH_PPC64 + .type \name, @function +#endif +.endm + +.macro raw_syscall nr + addi 0, 0, \nr + sc +.endm + +.macro vdso_syscall name, nr +\name: + raw_syscall \nr + blr +endf \name +.endm + + .cfi_startproc + +vdso_syscall __kernel_gettimeofday, __NR_gettimeofday +vdso_syscall __kernel_clock_gettime, __NR_clock_gettime +vdso_syscall __kernel_clock_getres, __NR_clock_getres +vdso_syscall __kernel_getcpu, __NR_getcpu +vdso_syscall __kernel_time, __NR_time + +#ifdef __NR_clock_gettime64 +vdso_syscall __kernel_clock_gettime64, __NR_clock_gettime64 +#endif + +__kernel_sync_dicache: + /* qemu does not need to flush caches */ + blr +endf __kernel_sync_dicache + + .cfi_endproc + +/* + * TODO: __kernel_get_tbfreq + * This is probably a constant for QEMU. + */ + +/* + * Start the unwind info at least one instruction before the signal + * trampoline, because the unwinder will assume we are returning + * after a call site. + */ + + .cfi_startproc simple + .cfi_signal_frame + +#ifdef _ARCH_PPC64 +# define __kernel_sigtramp_rt __kernel_sigtramp_rt64 +# define sizeof_reg 8 +#else +# define __kernel_sigtramp_rt __kernel_sigtramp_rt32 +# define sizeof_reg 4 +#endif +#define sizeof_freg 8 +#define sizeof_vreg 16 + + .cfi_def_cfa 1, SIGNAL_FRAMESIZE + offsetof_rt_sigframe_mcontext + + /* Return address */ + .cfi_return_column 67 + .cfi_offset 67, 32 * sizeof_reg /* nip */ + + /* Integer registers */ + .cfi_offset 0, 0 * sizeof_reg + .cfi_offset 1, 1 * sizeof_reg + .cfi_offset 2, 2 * sizeof_reg + .cfi_offset 3, 3 * sizeof_reg + .cfi_offset 4, 4 * sizeof_reg + .cfi_offset 5, 5 * sizeof_reg + .cfi_offset 6, 6 * sizeof_reg + .cfi_offset 7, 7 * sizeof_reg + .cfi_offset 8, 8 * sizeof_reg + .cfi_offset 9, 9 * sizeof_reg + .cfi_offset 10, 10 * sizeof_reg + .cfi_offset 11, 11 * sizeof_reg + .cfi_offset 12, 12 * sizeof_reg + .cfi_offset 13, 13 * sizeof_reg + .cfi_offset 14, 14 * sizeof_reg + .cfi_offset 15, 15 * sizeof_reg + .cfi_offset 16, 16 * sizeof_reg + .cfi_offset 17, 17 * sizeof_reg + .cfi_offset 18, 18 * sizeof_reg + .cfi_offset 19, 19 * sizeof_reg + .cfi_offset 20, 20 * sizeof_reg + .cfi_offset 21, 21 * sizeof_reg + .cfi_offset 22, 22 * sizeof_reg + .cfi_offset 23, 23 * sizeof_reg + .cfi_offset 24, 24 * sizeof_reg + .cfi_offset 25, 25 * sizeof_reg + .cfi_offset 26, 26 * sizeof_reg + .cfi_offset 27, 27 * sizeof_reg + .cfi_offset 28, 28 * sizeof_reg + .cfi_offset 29, 29 * sizeof_reg + .cfi_offset 30, 30 * sizeof_reg + .cfi_offset 31, 31 * sizeof_reg + .cfi_offset 65, 36 * sizeof_reg /* lr */ + .cfi_offset 70, 38 * sizeof_reg /* ccr */ + + /* Floating point registers */ + .cfi_offset 32, offsetof_mcontext_fregs + .cfi_offset 33, offsetof_mcontext_fregs + 1 * sizeof_freg + .cfi_offset 34, offsetof_mcontext_fregs + 2 * sizeof_freg + .cfi_offset 35, offsetof_mcontext_fregs + 3 * sizeof_freg + .cfi_offset 36, offsetof_mcontext_fregs + 4 * sizeof_freg + .cfi_offset 37, offsetof_mcontext_fregs + 5 * sizeof_freg + .cfi_offset 38, offsetof_mcontext_fregs + 6 * sizeof_freg + .cfi_offset 39, offsetof_mcontext_fregs + 7 * sizeof_freg + .cfi_offset 40, offsetof_mcontext_fregs + 8 * sizeof_freg + .cfi_offset 41, offsetof_mcontext_fregs + 9 * sizeof_freg + .cfi_offset 42, offsetof_mcontext_fregs + 10 * sizeof_freg + .cfi_offset 43, offsetof_mcontext_fregs + 11 * sizeof_freg + .cfi_offset 44, offsetof_mcontext_fregs + 12 * sizeof_freg + .cfi_offset 45, offsetof_mcontext_fregs + 13 * sizeof_freg + .cfi_offset 46, offsetof_mcontext_fregs + 14 * sizeof_freg + .cfi_offset 47, offsetof_mcontext_fregs + 15 * sizeof_freg + .cfi_offset 48, offsetof_mcontext_fregs + 16 * sizeof_freg + .cfi_offset 49, offsetof_mcontext_fregs + 17 * sizeof_freg + .cfi_offset 50, offsetof_mcontext_fregs + 18 * sizeof_freg + .cfi_offset 51, offsetof_mcontext_fregs + 19 * sizeof_freg + .cfi_offset 52, offsetof_mcontext_fregs + 20 * sizeof_freg + .cfi_offset 53, offsetof_mcontext_fregs + 21 * sizeof_freg + .cfi_offset 54, offsetof_mcontext_fregs + 22 * sizeof_freg + .cfi_offset 55, offsetof_mcontext_fregs + 23 * sizeof_freg + .cfi_offset 56, offsetof_mcontext_fregs + 24 * sizeof_freg + .cfi_offset 57, offsetof_mcontext_fregs + 25 * sizeof_freg + .cfi_offset 58, offsetof_mcontext_fregs + 26 * sizeof_freg + .cfi_offset 59, offsetof_mcontext_fregs + 27 * sizeof_freg + .cfi_offset 60, offsetof_mcontext_fregs + 28 * sizeof_freg + .cfi_offset 61, offsetof_mcontext_fregs + 29 * sizeof_freg + .cfi_offset 62, offsetof_mcontext_fregs + 30 * sizeof_freg + .cfi_offset 63, offsetof_mcontext_fregs + 31 * sizeof_freg + + /* + * Unlike the kernel, unconditionally represent the Altivec/VSX regs. + * The space within the stack frame is always available, and most of + * our supported processors have them enabled. The only complication + * for PPC64 is the misalignment, so that we have to use indirection. + */ +.macro save_vreg_ofs reg, ofs +#ifdef _ARCH_PPC64 + /* + * vreg = *(cfa + offsetof(v_regs)) + ofs + * + * The CFA is input to the expression on the stack, so: + * DW_CFA_expression reg, length (7), + * DW_OP_plus_uconst (0x23), vreg_ptr, DW_OP_deref (0x06), + * DW_OP_plus_uconst (0x23), ofs + */ + .cfi_escape 0x10, 77 + \reg, 7, 0x23, (offsetof_mcontext_vregs_ptr & 0x7f) + 0x80, offsetof_mcontext_vregs_ptr >> 7, 0x06, 0x23, (\ofs & 0x7f) | 0x80, \ofs >> 7 +#else + .cfi_offset 77 + \reg, offsetof_mcontext_vregs + \ofs +#endif +.endm + +.macro save_vreg reg + save_vreg_ofs \reg, (\reg * sizeof_vreg) +.endm + + save_vreg 0 + save_vreg 1 + save_vreg 2 + save_vreg 3 + save_vreg 4 + save_vreg 5 + save_vreg 6 + save_vreg 7 + save_vreg 8 + save_vreg 9 + save_vreg 10 + save_vreg 11 + save_vreg 12 + save_vreg 13 + save_vreg 14 + save_vreg 15 + save_vreg 16 + save_vreg 17 + save_vreg 18 + save_vreg 19 + save_vreg 20 + save_vreg 21 + save_vreg 22 + save_vreg 23 + save_vreg 24 + save_vreg 25 + save_vreg 26 + save_vreg 27 + save_vreg 28 + save_vreg 29 + save_vreg 30 + save_vreg 31 + save_vreg 32 + save_vreg_ofs 33, (32 * sizeof_vreg + 12) + + nop + +__kernel_sigtramp_rt: + raw_syscall __NR_rt_sigreturn +endf __kernel_sigtramp_rt + +#ifndef _ARCH_PPC64 + /* + * The non-rt sigreturn has the same layout at a different offset. + * Move the CFA and leave all the other descriptions the same. + */ + .cfi_def_cfa 1, SIGNAL_FRAMESIZE + offsetof_sigframe_mcontext + nop +__kernel_sigtramp32: + raw_syscall __NR_sigreturn +endf __kernel_sigtramp32 +#endif + + .cfi_endproc diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 7d90de1b15..32cd43d9ef 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -29,12 +29,10 @@ struct image_info { abi_ulong end_code; abi_ulong start_data; 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; + abi_ulong vdso; abi_ulong entry; abi_ulong code_offset; abi_ulong data_offset; @@ -48,6 +46,7 @@ struct image_info { uint32_t elf_flags; int personality; abi_ulong alignment; + bool exec_stack; /* Generic semihosting knows about these pointers. */ abi_ulong arg_strings; /* strings for argv */ @@ -163,10 +162,19 @@ typedef struct TaskState { uint64_t start_boottime; } TaskState; +static inline TaskState *get_task_state(CPUState *cs) +{ + return cs->opaque; +} + abi_long do_brk(abi_ulong new_brk); +int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, + int flags, mode_t mode, bool safe); +ssize_t do_guest_readlink(const char *pathname, char *buf, size_t bufsiz); /* user access */ +#define VERIFY_NONE 0 #define VERIFY_READ PAGE_READ #define VERIFY_WRITE (PAGE_READ | PAGE_WRITE) @@ -177,7 +185,7 @@ static inline bool access_ok_untagged(int type, abi_ulong addr, abi_ulong size) : !guest_range_valid_untagged(addr, size)) { return false; } - return page_check_range((target_ulong)addr, size, type) == 0; + return page_check_range((target_ulong)addr, size, type); } static inline bool access_ok(CPUState *cpu, int type, diff --git a/linux-user/riscv/Makefile.vdso b/linux-user/riscv/Makefile.vdso new file mode 100644 index 0000000000..2c257dbfda --- /dev/null +++ b/linux-user/riscv/Makefile.vdso @@ -0,0 +1,15 @@ +include $(BUILD_DIR)/tests/tcg/riscv64-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/riscv +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso-32.so $(SUBDIR)/vdso-64.so + +LDFLAGS = -nostdlib -shared -fpic -Wl,-h,linux-vdso.so.1 -Wl,--build-id=sha1 \ + -Wl,--hash-style=both -Wl,-T,$(SUBDIR)/vdso.ld + +$(SUBDIR)/vdso-32.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS) -mabi=ilp32d -march=rv32g $< + +$(SUBDIR)/vdso-64.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS) -mabi=lp64d -march=rv64g $< diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 29084c1421..52c49c2e42 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -81,7 +81,7 @@ void cpu_loop(CPURISCVState *env) force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); break; case RISCV_EXCP_SEMIHOST: - env->gpr[xA0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->pc += 4; break; default: @@ -97,7 +97,7 @@ void cpu_loop(CPURISCVState *env) void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); struct image_info *info = ts->info; env->pc = regs->sepc; diff --git a/linux-user/riscv/meson.build b/linux-user/riscv/meson.build new file mode 100644 index 0000000000..beb989a7ca --- /dev/null +++ b/linux-user/riscv/meson.build @@ -0,0 +1,7 @@ +vdso_32_inc = gen_vdso.process('vdso-32.so', + extra_args: ['-r', '__vdso_rt_sigreturn']) +vdso_64_inc = gen_vdso.process('vdso-64.so', + extra_args: ['-r', '__vdso_rt_sigreturn']) + +linux_user_ss.add(when: 'TARGET_RISCV32', if_true: vdso_32_inc) +linux_user_ss.add(when: 'TARGET_RISCV64', if_true: vdso_64_inc) diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c index 296e39fbf0..358fa1d82d 100644 --- a/linux-user/riscv/signal.c +++ b/linux-user/riscv/signal.c @@ -21,6 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "vdso-asmoffset.h" /* Signal handler invocation must be transparent for the code being interrupted. Complete CPU (hart) state is saved on entry and restored @@ -37,9 +38,11 @@ struct target_sigcontext { uint32_t fcsr; }; /* cf. riscv-linux:arch/riscv/include/uapi/asm/ptrace.h */ +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, fpr) != offsetof_freg0); + struct target_ucontext { - unsigned long uc_flags; - struct target_ucontext *uc_link; + abi_ulong uc_flags; + abi_ptr uc_link; target_stack_t uc_stack; target_sigset_t uc_sigmask; uint8_t __unused[1024 / 8 - sizeof(target_sigset_t)]; @@ -51,6 +54,11 @@ struct target_rt_sigframe { struct target_ucontext uc; }; +QEMU_BUILD_BUG_ON(sizeof(struct target_rt_sigframe) + != sizeof_rt_sigframe); +QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, uc.uc_mcontext) + != offsetof_uc_mcontext); + static abi_ulong get_sigframe(struct target_sigaction *ka, CPURISCVState *regs, size_t framesize) { @@ -64,9 +72,7 @@ static abi_ulong get_sigframe(struct target_sigaction *ka, /* This is the X/Open sanctioned signal stack switching. */ sp = target_sigsp(sp, ka) - framesize; - - /* XXX: kernel aligns with 0xf ? */ - sp &= ~3UL; /* align sp on 4-byte boundary */ + sp &= ~0xf; return sp; } @@ -119,7 +125,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, } setup_ucontext(&frame->uc, env, set); - tswap_siginfo(&frame->info, info); + frame->info = *info; env->pc = ka->_sa_handler; env->gpr[xSP] = frame_addr; diff --git a/linux-user/riscv/syscall32_nr.h b/linux-user/riscv/syscall32_nr.h index 1327d7dffa..412e58e5b2 100644 --- a/linux-user/riscv/syscall32_nr.h +++ b/linux-user/riscv/syscall32_nr.h @@ -228,6 +228,7 @@ #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_riscv_hwprobe (TARGET_NR_arch_specific_syscall + 14) #define TARGET_NR_prlimit64 261 #define TARGET_NR_fanotify_init 262 #define TARGET_NR_fanotify_mark 263 diff --git a/linux-user/riscv/syscall64_nr.h b/linux-user/riscv/syscall64_nr.h index 6659751933..29e1eb2075 100644 --- a/linux-user/riscv/syscall64_nr.h +++ b/linux-user/riscv/syscall64_nr.h @@ -251,6 +251,7 @@ #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_riscv_hwprobe (TARGET_NR_arch_specific_syscall + 14) #define TARGET_NR_wait4 260 #define TARGET_NR_prlimit64 261 #define TARGET_NR_fanotify_init 262 diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h index 9dd65652ee..dedd5956f3 100644 --- a/linux-user/riscv/target_elf.h +++ b/linux-user/riscv/target_elf.h @@ -9,7 +9,6 @@ #define RISCV_TARGET_ELF_H static inline const char *cpu_get_model(uint32_t eflags) { - /* TYPE_RISCV_CPU_ANY */ - return "any"; + return "max"; } #endif diff --git a/linux-user/riscv/target_mman.h b/linux-user/riscv/target_mman.h new file mode 100644 index 0000000000..3049bcc67d --- /dev/null +++ b/linux-user/riscv/target_mman.h @@ -0,0 +1,11 @@ +/* + * arch/loongarch/include/asm/processor.h: + * TASK_UNMAPPED_BASE PAGE_ALIGN(TASK_SIZE / 3) + */ +#define TASK_UNMAPPED_BASE \ + TARGET_PAGE_ALIGN((1ull << (TARGET_VIRT_ADDR_SPACE_BITS - 1)) / 3) + +/* arch/riscv/include/asm/elf.h */ +#define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE * 2) + +#include "../generic/target_mman.h" diff --git a/linux-user/riscv/target_proc.h b/linux-user/riscv/target_proc.h new file mode 100644 index 0000000000..c77c003d65 --- /dev/null +++ b/linux-user/riscv/target_proc.h @@ -0,0 +1,37 @@ +/* + * RISC-V specific proc functions for linux-user + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef RISCV_TARGET_PROC_H +#define RISCV_TARGET_PROC_H + +static int open_cpuinfo(CPUArchState *cpu_env, int fd) +{ + int i; + int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + RISCVCPU *cpu = env_archcpu(cpu_env); + const RISCVCPUConfig *cfg = riscv_cpu_cfg((CPURISCVState *) cpu_env); + char *isa_string = riscv_isa_string(cpu); + const char *mmu; + + if (cfg->mmu) { + mmu = (cpu_env->xl == MXL_RV32) ? "sv32" : "sv48"; + } else { + mmu = "none"; + } + + for (i = 0; i < num_cpus; i++) { + dprintf(fd, "processor\t: %d\n", i); + dprintf(fd, "hart\t\t: %d\n", i); + dprintf(fd, "isa\t\t: %s\n", isa_string); + dprintf(fd, "mmu\t\t: %s\n", mmu); + dprintf(fd, "uarch\t\t: qemu\n\n"); + } + + g_free(isa_string); + return 0; +} +#define HAVE_ARCH_PROC_CPUINFO + +#endif /* RISCV_TARGET_PROC_H */ diff --git a/linux-user/riscv/vdso-32.so b/linux-user/riscv/vdso-32.so new file mode 100755 index 0000000000..c2ce2a4757 Binary files /dev/null and b/linux-user/riscv/vdso-32.so differ diff --git a/linux-user/riscv/vdso-64.so b/linux-user/riscv/vdso-64.so new file mode 100755 index 0000000000..ae49f5b043 Binary files /dev/null and b/linux-user/riscv/vdso-64.so differ diff --git a/linux-user/riscv/vdso-asmoffset.h b/linux-user/riscv/vdso-asmoffset.h new file mode 100644 index 0000000000..123902ef61 --- /dev/null +++ b/linux-user/riscv/vdso-asmoffset.h @@ -0,0 +1,9 @@ +#ifdef TARGET_ABI32 +# define sizeof_rt_sigframe 0x2b0 +# define offsetof_uc_mcontext 0x120 +# define offsetof_freg0 0x80 +#else +# define sizeof_rt_sigframe 0x340 +# define offsetof_uc_mcontext 0x130 +# define offsetof_freg0 0x100 +#endif diff --git a/linux-user/riscv/vdso.S b/linux-user/riscv/vdso.S new file mode 100644 index 0000000000..c37275233a --- /dev/null +++ b/linux-user/riscv/vdso.S @@ -0,0 +1,187 @@ +/* + * RISC-V linux replacement vdso. + * + * Copyright 2021 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#if __riscv_xlen == 32 +# define TARGET_ABI32 +#endif +#include "vdso-asmoffset.h" + + .text + +.macro endf name + .globl \name + .type \name, @function + .size \name, . - \name +.endm + +.macro raw_syscall nr + li a7, \nr + ecall +.endm + +.macro vdso_syscall name, nr +\name: + raw_syscall \nr + ret +endf \name +.endm + +__vdso_gettimeofday: + .cfi_startproc +#ifdef __NR_gettimeofday + raw_syscall __NR_gettimeofday + ret +#else + /* No gettimeofday, fall back to clock_gettime64. */ + beq a1, zero, 1f + sw zero, 0(a1) /* tz->tz_minuteswest = 0 */ + sw zero, 4(a1) /* tz->tz_dsttime = 0 */ +1: addi sp, sp, -32 + .cfi_adjust_cfa_offset 32 + sw a0, 16(sp) /* save tv */ + mv a0, sp + raw_syscall __NR_clock_gettime64 + lw t0, 0(sp) /* timespec.tv_sec.low */ + lw t1, 4(sp) /* timespec.tv_sec.high */ + lw t2, 8(sp) /* timespec.tv_nsec.low */ + lw a1, 16(sp) /* restore tv */ + addi sp, sp, 32 + .cfi_adjust_cfa_offset -32 + bne a0, zero, 9f /* syscall error? */ + li a0, -EOVERFLOW + bne t1, zero, 9f /* y2038? */ + li a0, 0 + li t3, 1000 + divu t2, t2, t3 /* nsec -> usec */ + sw t0, 0(a1) /* tz->tv_sec */ + sw t2, 4(a1) /* tz->tv_usec */ +9: ret +#endif + .cfi_endproc +endf __vdso_gettimeofday + + .cfi_startproc + +#ifdef __NR_clock_gettime +vdso_syscall __vdso_clock_gettime, __NR_clock_gettime +#else +vdso_syscall __vdso_clock_gettime, __NR_clock_gettime64 +#endif + +#ifdef __NR_clock_getres +vdso_syscall __vdso_clock_getres, __NR_clock_getres +#else +vdso_syscall __vdso_clock_getres, __NR_clock_getres_time64 +#endif + +vdso_syscall __vdso_getcpu, __NR_getcpu + +__vdso_flush_icache: + /* qemu does not need to flush the icache */ + li a0, 0 + ret +endf __vdso_flush_icache + + .cfi_endproc + +/* + * Start the unwind info at least one instruction before the signal + * trampoline, because the unwinder will assume we are returning + * after a call site. + */ + + .cfi_startproc simple + .cfi_signal_frame + +#define sizeof_reg (__riscv_xlen / 8) +#define sizeof_freg 8 +#define B_GR 0 +#define B_FR offsetof_freg0 + + .cfi_def_cfa 2, offsetof_uc_mcontext + + /* Return address */ + .cfi_return_column 64 + .cfi_offset 64, B_GR + 0 /* pc */ + + /* Integer registers */ + .cfi_offset 1, B_GR + 1 * sizeof_reg /* r1 (ra) */ + .cfi_offset 2, B_GR + 2 * sizeof_reg /* r2 (sp) */ + .cfi_offset 3, B_GR + 3 * sizeof_reg + .cfi_offset 4, B_GR + 4 * sizeof_reg + .cfi_offset 5, B_GR + 5 * sizeof_reg + .cfi_offset 6, B_GR + 6 * sizeof_reg + .cfi_offset 7, B_GR + 7 * sizeof_reg + .cfi_offset 8, B_GR + 8 * sizeof_reg + .cfi_offset 9, B_GR + 9 * sizeof_reg + .cfi_offset 10, B_GR + 10 * sizeof_reg + .cfi_offset 11, B_GR + 11 * sizeof_reg + .cfi_offset 12, B_GR + 12 * sizeof_reg + .cfi_offset 13, B_GR + 13 * sizeof_reg + .cfi_offset 14, B_GR + 14 * sizeof_reg + .cfi_offset 15, B_GR + 15 * sizeof_reg + .cfi_offset 16, B_GR + 16 * sizeof_reg + .cfi_offset 17, B_GR + 17 * sizeof_reg + .cfi_offset 18, B_GR + 18 * sizeof_reg + .cfi_offset 19, B_GR + 19 * sizeof_reg + .cfi_offset 20, B_GR + 20 * sizeof_reg + .cfi_offset 21, B_GR + 21 * sizeof_reg + .cfi_offset 22, B_GR + 22 * sizeof_reg + .cfi_offset 23, B_GR + 23 * sizeof_reg + .cfi_offset 24, B_GR + 24 * sizeof_reg + .cfi_offset 25, B_GR + 25 * sizeof_reg + .cfi_offset 26, B_GR + 26 * sizeof_reg + .cfi_offset 27, B_GR + 27 * sizeof_reg + .cfi_offset 28, B_GR + 28 * sizeof_reg + .cfi_offset 29, B_GR + 29 * sizeof_reg + .cfi_offset 30, B_GR + 30 * sizeof_reg + .cfi_offset 31, B_GR + 31 * sizeof_reg /* r31 */ + + .cfi_offset 32, B_FR + 0 /* f0 */ + .cfi_offset 33, B_FR + 1 * sizeof_freg /* f1 */ + .cfi_offset 34, B_FR + 2 * sizeof_freg + .cfi_offset 35, B_FR + 3 * sizeof_freg + .cfi_offset 36, B_FR + 4 * sizeof_freg + .cfi_offset 37, B_FR + 5 * sizeof_freg + .cfi_offset 38, B_FR + 6 * sizeof_freg + .cfi_offset 39, B_FR + 7 * sizeof_freg + .cfi_offset 40, B_FR + 8 * sizeof_freg + .cfi_offset 41, B_FR + 9 * sizeof_freg + .cfi_offset 42, B_FR + 10 * sizeof_freg + .cfi_offset 43, B_FR + 11 * sizeof_freg + .cfi_offset 44, B_FR + 12 * sizeof_freg + .cfi_offset 45, B_FR + 13 * sizeof_freg + .cfi_offset 46, B_FR + 14 * sizeof_freg + .cfi_offset 47, B_FR + 15 * sizeof_freg + .cfi_offset 48, B_FR + 16 * sizeof_freg + .cfi_offset 49, B_FR + 17 * sizeof_freg + .cfi_offset 50, B_FR + 18 * sizeof_freg + .cfi_offset 51, B_FR + 19 * sizeof_freg + .cfi_offset 52, B_FR + 20 * sizeof_freg + .cfi_offset 53, B_FR + 21 * sizeof_freg + .cfi_offset 54, B_FR + 22 * sizeof_freg + .cfi_offset 55, B_FR + 23 * sizeof_freg + .cfi_offset 56, B_FR + 24 * sizeof_freg + .cfi_offset 57, B_FR + 25 * sizeof_freg + .cfi_offset 58, B_FR + 26 * sizeof_freg + .cfi_offset 59, B_FR + 27 * sizeof_freg + .cfi_offset 60, B_FR + 28 * sizeof_freg + .cfi_offset 61, B_FR + 29 * sizeof_freg + .cfi_offset 62, B_FR + 30 * sizeof_freg + .cfi_offset 63, B_FR + 31 * sizeof_freg /* f31 */ + + nop + +__vdso_rt_sigreturn: + raw_syscall __NR_rt_sigreturn +endf __vdso_rt_sigreturn + + .cfi_endproc diff --git a/linux-user/riscv/vdso.ld b/linux-user/riscv/vdso.ld new file mode 100644 index 0000000000..aabe2b0ab3 --- /dev/null +++ b/linux-user/riscv/vdso.ld @@ -0,0 +1,74 @@ +/* + * Linker script for linux riscv replacement vdso. + * + * Copyright 2021 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_4.15 { + global: + __vdso_rt_sigreturn; + __vdso_gettimeofday; + __vdso_clock_gettime; + __vdso_clock_getres; + __vdso_getcpu; + __vdso_flush_icache; + + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + /* + * We can't prelink to any address without knowing something about + * the virtual memory space of the host, since that leaks over into + * the available memory space of the guest. + */ + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + .data : { + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load =0xd503201f +} diff --git a/linux-user/s390x/Makefile.vdso b/linux-user/s390x/Makefile.vdso new file mode 100644 index 0000000000..e82bf9e29f --- /dev/null +++ b/linux-user/s390x/Makefile.vdso @@ -0,0 +1,11 @@ +include $(BUILD_DIR)/tests/tcg/s390x-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/s390x +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso.so + +$(SUBDIR)/vdso.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ -nostdlib -shared -Wl,-h,linux-vdso64.so.1 \ + -Wl,--build-id=sha1 -Wl,--hash-style=both \ + -Wl,-T,$(SUBDIR)/vdso.ld $< diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index 285bc60071..8b7ac2879e 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -86,6 +86,15 @@ void cpu_loop(CPUS390XState *env) } else if (ret != -QEMU_ESIGRETURN) { env->regs[2] = ret; } + + if (unlikely(cs->singlestep_enabled)) { + /* + * cpu_tb_exec() did not raise EXCP_DEBUG, because it has seen + * that EXCP_SVC was already pending. + */ + cs->exception_index = EXCP_DEBUG; + } + break; case EXCP_DEBUG: diff --git a/linux-user/s390x/meson.build b/linux-user/s390x/meson.build index 0781ccea1d..a7a25ed9ce 100644 --- a/linux-user/s390x/meson.build +++ b/linux-user/s390x/meson.build @@ -3,3 +3,9 @@ syscall_nr_generators += { arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], output: '@BASENAME@_nr.h') } + +vdso_inc = gen_vdso.process('vdso.so', extra_args: [ + '-s', '__kernel_sigreturn', + '-r', '__kernel_rt_sigreturn' + ]) +linux_user_ss.add(when: 'TARGET_S390X', if_true: vdso_inc) diff --git a/linux-user/s390x/signal.c b/linux-user/s390x/signal.c index 4979c4b017..df49c24708 100644 --- a/linux-user/s390x/signal.c +++ b/linux-user/s390x/signal.c @@ -21,13 +21,12 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "vdso-asmoffset.h" #define __NUM_GPRS 16 #define __NUM_FPRS 16 #define __NUM_ACRS 16 -#define __SIGNAL_FRAMESIZE 160 /* FIXME: 31-bit mode -> 96 */ - #define _SIGCONTEXT_NSIG 64 #define _SIGCONTEXT_NSIG_BPW 64 /* FIXME: 31-bit mode -> 32 */ #define _SIGCONTEXT_NSIG_WORDS (_SIGCONTEXT_NSIG / _SIGCONTEXT_NSIG_BPW) @@ -63,7 +62,7 @@ typedef struct { } target_sigcontext; typedef struct { - uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; + uint8_t callee_used_stack[STACK_FRAME_OVERHEAD]; target_sigcontext sc; target_sigregs sregs; int signo; @@ -83,7 +82,7 @@ struct target_ucontext { }; typedef struct { - uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; + uint8_t callee_used_stack[STACK_FRAME_OVERHEAD]; /* * This field is no longer initialized by the kernel, but it's still a part * of the ABI. @@ -146,6 +145,7 @@ static void save_sigregs(CPUS390XState *env, target_sigregs *sregs) * We have to store the fp registers to current->thread.fp_regs * to merge them with the emulated registers. */ + __put_user(env->fpc, &sregs->fpregs.fpc); for (i = 0; i < 16; i++) { __put_user(*get_freg(env, i), &sregs->fpregs.fprs[i]); } @@ -267,7 +267,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, } /* Create siginfo on the signal stack. */ - tswap_siginfo(&frame->info, info); + frame->info = *info; /* Create ucontext on the signal stack. */ uc_flags = 0; @@ -331,6 +331,7 @@ static void restore_sigregs(CPUS390XState *env, target_sigregs *sc) for (i = 0; i < 16; i++) { __get_user(env->aregs[i], &sc->regs.acrs[i]); } + __get_user(env->fpc, &sc->fpregs.fpc); for (i = 0; i < 16; i++) { __get_user(*get_freg(env, i), &sc->fpregs.fprs[i]); } diff --git a/linux-user/s390x/target_mman.h b/linux-user/s390x/target_mman.h new file mode 100644 index 0000000000..c82435e381 --- /dev/null +++ b/linux-user/s390x/target_mman.h @@ -0,0 +1,21 @@ +/* + * arch/s390/include/asm/processor.h: + * TASK_UNMAPPED_BASE (... : (_REGION2_SIZE >> 1)) + * + * arch/s390/include/asm/pgtable.h: + * _REGION2_SIZE (1UL << _REGION2_SHIFT) + * _REGION2_SHIFT 42 + */ +#define TASK_UNMAPPED_BASE (1ull << 41) + +/* + * arch/s390/include/asm/elf.h: + * ELF_ET_DYN_BASE (STACK_TOP / 3 * 2) & ~((1UL << 32) - 1) + * + * arch/s390/include/asm/processor.h: + * STACK_TOP VDSO_LIMIT - VDSO_SIZE - PAGE_SIZE + * VDSO_LIMIT _REGION2_SIZE + */ +#define ELF_ET_DYN_BASE (((1ull << 42) / 3 * 2) & ~0xffffffffull) + +#include "../generic/target_mman.h" diff --git a/linux-user/s390x/target_proc.h b/linux-user/s390x/target_proc.h new file mode 100644 index 0000000000..a4a4821ea5 --- /dev/null +++ b/linux-user/s390x/target_proc.h @@ -0,0 +1,109 @@ +/* + * S390X specific proc functions for linux-user + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef S390X_TARGET_PROC_H +#define S390X_TARGET_PROC_H + +/* + * Emulate what a Linux kernel running in qemu-system-s390x -M accel=tcg would + * show in /proc/cpuinfo. + * + * Skip the following in order to match the missing support in op_ecag(): + * - show_cacheinfo(). + * - show_cpu_topology(). + * - show_cpu_mhz(). + * + * Use fixed values for certain fields: + * - bogomips per cpu - from a qemu-system-s390x run. + * - max thread id = 0, since SMT / SIGP_SET_MULTI_THREADING is not supported. + * + * Keep the code structure close to arch/s390/kernel/processor.c. + */ + +static void show_facilities(int fd) +{ + size_t sizeof_stfl_bytes = 2048; + g_autofree uint8_t *stfl_bytes = g_new0(uint8_t, sizeof_stfl_bytes); + unsigned int bit; + + dprintf(fd, "facilities :"); + s390_get_feat_block(S390_FEAT_TYPE_STFL, stfl_bytes); + for (bit = 0; bit < sizeof_stfl_bytes * 8; bit++) { + if (test_be_bit(bit, stfl_bytes)) { + dprintf(fd, " %d", bit); + } + } + dprintf(fd, "\n"); +} + +static int cpu_ident(unsigned long n) +{ + return deposit32(0, CPU_ID_BITS - CPU_PHYS_ADDR_BITS, CPU_PHYS_ADDR_BITS, + n); +} + +static void show_cpu_summary(CPUArchState *cpu_env, int fd) +{ + S390CPUModel *model = env_archcpu(cpu_env)->model; + int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + uint32_t elf_hwcap = get_elf_hwcap(); + const char *hwcap_str; + int i; + + dprintf(fd, "vendor_id : IBM/S390\n" + "# processors : %i\n" + "bogomips per cpu: 13370.00\n", + num_cpus); + dprintf(fd, "max thread id : 0\n"); + dprintf(fd, "features\t: "); + for (i = 0; i < sizeof(elf_hwcap) * 8; i++) { + if (!(elf_hwcap & (1 << i))) { + continue; + } + hwcap_str = elf_hwcap_str(i); + if (hwcap_str) { + dprintf(fd, "%s ", hwcap_str); + } + } + dprintf(fd, "\n"); + show_facilities(fd); + for (i = 0; i < num_cpus; i++) { + dprintf(fd, "processor %d: " + "version = %02X, " + "identification = %06X, " + "machine = %04X\n", + i, model->cpu_ver, cpu_ident(i), model->def->type); + } +} + +static void show_cpu_ids(CPUArchState *cpu_env, int fd, unsigned long n) +{ + S390CPUModel *model = env_archcpu(cpu_env)->model; + + dprintf(fd, "version : %02X\n", model->cpu_ver); + dprintf(fd, "identification : %06X\n", cpu_ident(n)); + dprintf(fd, "machine : %04X\n", model->def->type); +} + +static void show_cpuinfo(CPUArchState *cpu_env, int fd, unsigned long n) +{ + dprintf(fd, "\ncpu number : %ld\n", n); + show_cpu_ids(cpu_env, fd, n); +} + +static int open_cpuinfo(CPUArchState *cpu_env, int fd) +{ + int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + int i; + + show_cpu_summary(cpu_env, fd); + for (i = 0; i < num_cpus; i++) { + show_cpuinfo(cpu_env, fd, i); + } + return 0; +} +#define HAVE_ARCH_PROC_CPUINFO + +#endif /* S390X_TARGET_PROC_H */ diff --git a/linux-user/s390x/vdso-asmoffset.h b/linux-user/s390x/vdso-asmoffset.h new file mode 100644 index 0000000000..27a062d6c1 --- /dev/null +++ b/linux-user/s390x/vdso-asmoffset.h @@ -0,0 +1,2 @@ +/* Minimum stack frame size */ +#define STACK_FRAME_OVERHEAD 160 diff --git a/linux-user/s390x/vdso.S b/linux-user/s390x/vdso.S new file mode 100644 index 0000000000..3332492477 --- /dev/null +++ b/linux-user/s390x/vdso.S @@ -0,0 +1,61 @@ +/* + * s390x linux replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "vdso-asmoffset.h" + +.macro endf name + .globl \name + .type \name, @function + .size \name, . - \name +.endm + +.macro raw_syscall n + .ifne \n < 0x100 + svc \n + .else + lghi %r1, \n + svc 0 + .endif +.endm + +.macro vdso_syscall name, nr +\name: + .cfi_startproc + aghi %r15, -(STACK_FRAME_OVERHEAD + 16) + .cfi_adjust_cfa_offset STACK_FRAME_OVERHEAD + 16 + stg %r14, STACK_FRAME_OVERHEAD(%r15) + .cfi_rel_offset %r14, STACK_FRAME_OVERHEAD + raw_syscall \nr + lg %r14, STACK_FRAME_OVERHEAD(%r15) + aghi %r15, STACK_FRAME_OVERHEAD + 16 + .cfi_restore %r14 + .cfi_adjust_cfa_offset -(STACK_FRAME_OVERHEAD + 16) + br %r14 + .cfi_endproc +endf \name +.endm + +vdso_syscall __kernel_gettimeofday, __NR_gettimeofday +vdso_syscall __kernel_clock_gettime, __NR_clock_gettime +vdso_syscall __kernel_clock_getres, __NR_clock_getres +vdso_syscall __kernel_getcpu, __NR_getcpu + +/* + * TODO unwind info, though we're ok without it. + * The kernel supplies bogus empty unwind info, and it is likely ignored + * by all users. Without it we get the fallback signal frame handling. + */ + +__kernel_sigreturn: + raw_syscall __NR_sigreturn +endf __kernel_sigreturn + +__kernel_rt_sigreturn: + raw_syscall __NR_rt_sigreturn +endf __kernel_rt_sigreturn diff --git a/linux-user/s390x/vdso.ld b/linux-user/s390x/vdso.ld new file mode 100644 index 0000000000..d3f1d1b164 --- /dev/null +++ b/linux-user/s390x/vdso.ld @@ -0,0 +1,72 @@ +/* + * Linker script for linux s390x replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_2.6.29 { + global: + __kernel_gettimeofday; + __kernel_clock_gettime; + __kernel_clock_getres; + __kernel_getcpu; + __kernel_rt_sigreturn; + __kernel_sigreturn; + /* + * QEMU handles syscall restart internally, so we don't + * need the __kernel_restart_syscall entry point. + */ + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; /* FLAGS=RWX */ + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + .data : { + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load +} diff --git a/linux-user/s390x/vdso.so b/linux-user/s390x/vdso.so new file mode 100755 index 0000000000..64130f6f33 Binary files /dev/null and b/linux-user/s390x/vdso.so differ diff --git a/linux-user/semihost.c b/linux-user/semihost.c index 17f074ac56..cee62a365c 100644 --- a/linux-user/semihost.c +++ b/linux-user/semihost.c @@ -16,39 +16,6 @@ #include "user-internals.h" #include -int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr) -{ - int len = target_strlen(addr); - void *s; - if (len < 0){ - qemu_log_mask(LOG_GUEST_ERROR, - "%s: passed inaccessible address " TARGET_FMT_lx, - __func__, addr); - return 0; - } - s = lock_user(VERIFY_READ, addr, (long)(len + 1), 1); - g_assert(s); /* target_strlen has already verified this will work */ - len = write(STDERR_FILENO, s, len); - unlock_user(s, addr, 0); - return len; -} - -void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) -{ - char c; - - if (get_user_u8(c, addr)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: passed inaccessible address " TARGET_FMT_lx, - __func__, addr); - } else { - if (write(STDERR_FILENO, &c, 1) != 1) { - qemu_log_mask(LOG_UNIMP, "%s: unexpected write to stdout failure", - __func__); - } - } -} - /* * 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 @@ -56,21 +23,28 @@ void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) * 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) +int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) { - uint8_t c; + int ret; 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); + new_tio.c_cc[VMIN] = 1; + new_tio.c_cc[VTIME] = 0; tcsetattr(STDIN_FILENO, TCSANOW, &new_tio); - c = getchar(); + ret = fread(buf, 1, len, stdin); /* restore config */ tcsetattr(STDIN_FILENO, TCSANOW, &old_tio); - return (target_ulong) c; + return ret; +} + +int qemu_semihosting_console_write(void *buf, int len) +{ + return fwrite(buf, 1, len, stderr); } diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c index f6a18bc6b5..9ecc026fae 100644 --- a/linux-user/sh4/signal.c +++ b/linux-user/sh4/signal.c @@ -104,6 +104,14 @@ static void unwind_gusa(CPUSH4State *regs) /* Reset the SP to the saved version in R1. */ regs->gregs[15] = regs->gregs[1]; + } else if (regs->gregs[15] >= -128u && regs->pc == regs->gregs[0]) { + /* If we are on the last instruction of a gUSA region, we must reset + the SP, otherwise we would be pushing the signal context to + invalid memory. */ + regs->gregs[15] = regs->gregs[1]; + } else if (regs->flags & TB_FLAG_DELAY_SLOT) { + /* If we are in a delay slot, push the previous instruction. */ + regs->pc -= 2; } } @@ -161,7 +169,7 @@ static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc) __get_user(regs->fpul, &sc->sc_fpul); regs->tra = -1; /* disable syscall checks */ - regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); + regs->flags = 0; } void setup_frame(int sig, struct target_sigaction *ka, @@ -199,7 +207,7 @@ void setup_frame(int sig, struct target_sigaction *ka, regs->gregs[5] = 0; regs->gregs[6] = frame_addr += offsetof(typeof(*frame), sc); regs->pc = (unsigned long) ka->_sa_handler; - regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); + regs->flags &= ~(TB_FLAG_DELAY_SLOT_MASK | TB_FLAG_GUSA_MASK); unlock_user_struct(frame, frame_addr, 1); return; @@ -225,7 +233,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, goto give_sigsegv; } - tswap_siginfo(&frame->info, info); + frame->info = *info; /* Create the ucontext. */ __put_user(0, &frame->uc.tuc_flags); @@ -251,7 +259,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, regs->gregs[5] = frame_addr + offsetof(typeof(*frame), info); regs->gregs[6] = frame_addr + offsetof(typeof(*frame), uc); regs->pc = (unsigned long) ka->_sa_handler; - regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); + regs->flags &= ~(TB_FLAG_DELAY_SLOT_MASK | TB_FLAG_GUSA_MASK); unlock_user_struct(frame, frame_addr, 1); return; diff --git a/linux-user/sh4/target_flat.h b/linux-user/sh4/target_flat.h new file mode 100644 index 0000000000..bc83224cea --- /dev/null +++ b/linux-user/sh4/target_flat.h @@ -0,0 +1 @@ +#include "../generic/target_flat.h" diff --git a/linux-user/sh4/target_mman.h b/linux-user/sh4/target_mman.h new file mode 100644 index 0000000000..dd9016081e --- /dev/null +++ b/linux-user/sh4/target_mman.h @@ -0,0 +1,8 @@ +/* arch/sh/include/asm/processor_32.h */ +#define TASK_UNMAPPED_BASE \ + TARGET_PAGE_ALIGN((1u << TARGET_VIRT_ADDR_SPACE_BITS) / 3) + +/* arch/sh/include/asm/elf.h */ +#define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE * 2) + +#include "../generic/target_mman.h" diff --git a/linux-user/sh4/target_proc.h b/linux-user/sh4/target_proc.h new file mode 100644 index 0000000000..43fe29ca72 --- /dev/null +++ b/linux-user/sh4/target_proc.h @@ -0,0 +1 @@ +/* No target-specific /proc support */ diff --git a/linux-user/sh4/termbits.h b/linux-user/sh4/termbits.h index eeabd2d7a9..28e79f2c9a 100644 --- a/linux-user/sh4/termbits.h +++ b/linux-user/sh4/termbits.h @@ -39,86 +39,86 @@ struct target_termios { #define TARGET_VEOL2 16 /* c_iflag bits */ -#define TARGET_IGNBRK 0000001 -#define TARGET_BRKINT 0000002 -#define TARGET_IGNPAR 0000004 -#define TARGET_PARMRK 0000010 -#define TARGET_INPCK 0000020 -#define TARGET_ISTRIP 0000040 -#define TARGET_INLCR 0000100 -#define TARGET_IGNCR 0000200 -#define TARGET_ICRNL 0000400 -#define TARGET_IUCLC 0001000 -#define TARGET_IXON 0002000 -#define TARGET_IXANY 0004000 -#define TARGET_IXOFF 0010000 -#define TARGET_IMAXBEL 0020000 -#define TARGET_IUTF8 0040000 +#define TARGET_IGNBRK 0000001 +#define TARGET_BRKINT 0000002 +#define TARGET_IGNPAR 0000004 +#define TARGET_PARMRK 0000010 +#define TARGET_INPCK 0000020 +#define TARGET_ISTRIP 0000040 +#define TARGET_INLCR 0000100 +#define TARGET_IGNCR 0000200 +#define TARGET_ICRNL 0000400 +#define TARGET_IUCLC 0001000 +#define TARGET_IXON 0002000 +#define TARGET_IXANY 0004000 +#define TARGET_IXOFF 0010000 +#define TARGET_IMAXBEL 0020000 +#define TARGET_IUTF8 0040000 /* c_oflag bits */ -#define TARGET_OPOST 0000001 -#define TARGET_OLCUC 0000002 -#define TARGET_ONLCR 0000004 -#define TARGET_OCRNL 0000010 -#define TARGET_ONOCR 0000020 -#define TARGET_ONLRET 0000040 -#define TARGET_OFILL 0000100 -#define TARGET_OFDEL 0000200 -#define TARGET_NLDLY 0000400 -#define TARGET_NL0 0000000 -#define TARGET_NL1 0000400 -#define TARGET_CRDLY 0003000 -#define TARGET_CR0 0000000 -#define TARGET_CR1 0001000 -#define TARGET_CR2 0002000 -#define TARGET_CR3 0003000 -#define TARGET_TABDLY 0014000 -#define TARGET_TAB0 0000000 -#define TARGET_TAB1 0004000 -#define TARGET_TAB2 0010000 -#define TARGET_TAB3 0014000 -#define TARGET_XTABS 0014000 -#define TARGET_BSDLY 0020000 -#define TARGET_BS0 0000000 -#define TARGET_BS1 0020000 -#define TARGET_VTDLY 0040000 -#define TARGET_VT0 0000000 -#define TARGET_VT1 0040000 -#define TARGET_FFDLY 0100000 -#define TARGET_FF0 0000000 -#define TARGET_FF1 0100000 +#define TARGET_OPOST 0000001 +#define TARGET_OLCUC 0000002 +#define TARGET_ONLCR 0000004 +#define TARGET_OCRNL 0000010 +#define TARGET_ONOCR 0000020 +#define TARGET_ONLRET 0000040 +#define TARGET_OFILL 0000100 +#define TARGET_OFDEL 0000200 +#define TARGET_NLDLY 0000400 +#define TARGET_NL0 0000000 +#define TARGET_NL1 0000400 +#define TARGET_CRDLY 0003000 +#define TARGET_CR0 0000000 +#define TARGET_CR1 0001000 +#define TARGET_CR2 0002000 +#define TARGET_CR3 0003000 +#define TARGET_TABDLY 0014000 +#define TARGET_TAB0 0000000 +#define TARGET_TAB1 0004000 +#define TARGET_TAB2 0010000 +#define TARGET_TAB3 0014000 +#define TARGET_XTABS 0014000 +#define TARGET_BSDLY 0020000 +#define TARGET_BS0 0000000 +#define TARGET_BS1 0020000 +#define TARGET_VTDLY 0040000 +#define TARGET_VT0 0000000 +#define TARGET_VT1 0040000 +#define TARGET_FFDLY 0100000 +#define TARGET_FF0 0000000 +#define TARGET_FF1 0100000 /* c_cflag bit meaning */ -#define TARGET_CBAUD 0010017 -#define TARGET_B0 0000000 /* hang up */ -#define TARGET_B50 0000001 -#define TARGET_B75 0000002 -#define TARGET_B110 0000003 -#define TARGET_B134 0000004 -#define TARGET_B150 0000005 -#define TARGET_B200 0000006 -#define TARGET_B300 0000007 -#define TARGET_B600 0000010 -#define TARGET_B1200 0000011 -#define TARGET_B1800 0000012 -#define TARGET_B2400 0000013 -#define TARGET_B4800 0000014 -#define TARGET_B9600 0000015 -#define TARGET_B19200 0000016 -#define TARGET_B38400 0000017 +#define TARGET_CBAUD 0010017 +#define TARGET_B0 0000000 /* hang up */ +#define TARGET_B50 0000001 +#define TARGET_B75 0000002 +#define TARGET_B110 0000003 +#define TARGET_B134 0000004 +#define TARGET_B150 0000005 +#define TARGET_B200 0000006 +#define TARGET_B300 0000007 +#define TARGET_B600 0000010 +#define TARGET_B1200 0000011 +#define TARGET_B1800 0000012 +#define TARGET_B2400 0000013 +#define TARGET_B4800 0000014 +#define TARGET_B9600 0000015 +#define TARGET_B19200 0000016 +#define TARGET_B38400 0000017 #define TARGET_EXTA B19200 #define TARGET_EXTB B38400 -#define TARGET_CSIZE 0000060 -#define TARGET_CS5 0000000 -#define TARGET_CS6 0000020 -#define TARGET_CS7 0000040 -#define TARGET_CS8 0000060 -#define TARGET_CSTOPB 0000100 -#define TARGET_CREAD 0000200 -#define TARGET_PARENB 0000400 -#define TARGET_PARODD 0001000 -#define TARGET_HUPCL 0002000 -#define TARGET_CLOCAL 0004000 +#define TARGET_CSIZE 0000060 +#define TARGET_CS5 0000000 +#define TARGET_CS6 0000020 +#define TARGET_CS7 0000040 +#define TARGET_CS8 0000060 +#define TARGET_CSTOPB 0000100 +#define TARGET_CREAD 0000200 +#define TARGET_PARENB 0000400 +#define TARGET_PARODD 0001000 +#define TARGET_HUPCL 0002000 +#define TARGET_CLOCAL 0004000 #define TARGET_CBAUDEX 0010000 #define TARGET_B57600 0010001 #define TARGET_B115200 0010002 @@ -135,44 +135,44 @@ struct target_termios { #define TARGET_B3000000 0010015 #define TARGET_B3500000 0010016 #define TARGET_B4000000 0010017 -#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */ -#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */ -#define TARGET_CRTSCTS 020000000000 /* flow control */ +#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */ +#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */ +#define TARGET_CRTSCTS 020000000000 /* flow control */ /* c_lflag bits */ -#define TARGET_ISIG 0000001 -#define TARGET_ICANON 0000002 -#define TARGET_XCASE 0000004 -#define TARGET_ECHO 0000010 -#define TARGET_ECHOE 0000020 -#define TARGET_ECHOK 0000040 -#define TARGET_ECHONL 0000100 -#define TARGET_NOFLSH 0000200 -#define TARGET_TOSTOP 0000400 -#define TARGET_ECHOCTL 0001000 -#define TARGET_ECHOPRT 0002000 -#define TARGET_ECHOKE 0004000 -#define TARGET_FLUSHO 0010000 -#define TARGET_PENDIN 0040000 -#define TARGET_IEXTEN 0100000 +#define TARGET_ISIG 0000001 +#define TARGET_ICANON 0000002 +#define TARGET_XCASE 0000004 +#define TARGET_ECHO 0000010 +#define TARGET_ECHOE 0000020 +#define TARGET_ECHOK 0000040 +#define TARGET_ECHONL 0000100 +#define TARGET_NOFLSH 0000200 +#define TARGET_TOSTOP 0000400 +#define TARGET_ECHOCTL 0001000 +#define TARGET_ECHOPRT 0002000 +#define TARGET_ECHOKE 0004000 +#define TARGET_FLUSHO 0010000 +#define TARGET_PENDIN 0040000 +#define TARGET_IEXTEN 0100000 #define TARGET_EXTPROC 0200000 /* tcflow() and TCXONC use these */ -#define TARGET_TCOOFF 0 -#define TARGET_TCOON 1 -#define TARGET_TCIOFF 2 -#define TARGET_TCION 3 +#define TARGET_TCOOFF 0 +#define TARGET_TCOON 1 +#define TARGET_TCIOFF 2 +#define TARGET_TCION 3 /* tcflush() and TCFLSH use these */ -#define TARGET_TCIFLUSH 0 -#define TARGET_TCOFLUSH 1 -#define TARGET_TCIOFLUSH 2 +#define TARGET_TCIFLUSH 0 +#define TARGET_TCOFLUSH 1 +#define TARGET_TCIOFLUSH 2 /* tcsetattr uses these */ -#define TARGET_TCSANOW 0 -#define TARGET_TCSADRAIN 1 -#define TARGET_TARGET_TCSAFLUSH 2 +#define TARGET_TCSANOW 0 +#define TARGET_TCSADRAIN 1 +#define TARGET_TARGET_TCSAFLUSH 2 /* ioctl */ #define TARGET_FIOCLEX TARGET_IO('f', 1) diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h index 6a7e4a93fc..f4cbe6185e 100644 --- a/linux-user/signal-common.h +++ b/linux-user/signal-common.h @@ -43,8 +43,6 @@ void host_to_target_sigset_internal(target_sigset_t *d, const sigset_t *s); void target_to_host_sigset_internal(sigset_t *d, const target_sigset_t *s); -void tswap_siginfo(target_siginfo_t *tinfo, - const target_siginfo_t *info); void set_sigmask(const sigset_t *set); void force_sig(int sig); void force_sigsegv(int oldsig); @@ -113,9 +111,55 @@ int process_sigsuspend_mask(sigset_t **pset, target_ulong sigset, static inline void finish_sigsuspend_mask(int ret) { if (ret != -QEMU_ERESTARTSYS) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); ts->in_sigsuspend = 1; } } +#if defined(SIGSTKFLT) && defined(TARGET_SIGSTKFLT) +#define MAKE_SIG_ENTRY_SIGSTKFLT MAKE_SIG_ENTRY(SIGSTKFLT) +#else +#define MAKE_SIG_ENTRY_SIGSTKFLT +#endif + +#if defined(SIGIOT) && defined(TARGET_SIGIOT) +#define MAKE_SIG_ENTRY_SIGIOT MAKE_SIG_ENTRY(SIGIOT) +#else +#define MAKE_SIG_ENTRY_SIGIOT +#endif + +#define MAKE_SIGNAL_LIST \ + MAKE_SIG_ENTRY(SIGHUP) \ + MAKE_SIG_ENTRY(SIGINT) \ + MAKE_SIG_ENTRY(SIGQUIT) \ + MAKE_SIG_ENTRY(SIGILL) \ + MAKE_SIG_ENTRY(SIGTRAP) \ + MAKE_SIG_ENTRY(SIGABRT) \ + MAKE_SIG_ENTRY(SIGBUS) \ + MAKE_SIG_ENTRY(SIGFPE) \ + MAKE_SIG_ENTRY(SIGKILL) \ + MAKE_SIG_ENTRY(SIGUSR1) \ + MAKE_SIG_ENTRY(SIGSEGV) \ + MAKE_SIG_ENTRY(SIGUSR2) \ + MAKE_SIG_ENTRY(SIGPIPE) \ + MAKE_SIG_ENTRY(SIGALRM) \ + MAKE_SIG_ENTRY(SIGTERM) \ + MAKE_SIG_ENTRY(SIGCHLD) \ + MAKE_SIG_ENTRY(SIGCONT) \ + MAKE_SIG_ENTRY(SIGSTOP) \ + MAKE_SIG_ENTRY(SIGTSTP) \ + MAKE_SIG_ENTRY(SIGTTIN) \ + MAKE_SIG_ENTRY(SIGTTOU) \ + MAKE_SIG_ENTRY(SIGURG) \ + MAKE_SIG_ENTRY(SIGXCPU) \ + MAKE_SIG_ENTRY(SIGXFSZ) \ + MAKE_SIG_ENTRY(SIGVTALRM) \ + MAKE_SIG_ENTRY(SIGPROF) \ + MAKE_SIG_ENTRY(SIGWINCH) \ + MAKE_SIG_ENTRY(SIGIO) \ + MAKE_SIG_ENTRY(SIGPWR) \ + MAKE_SIG_ENTRY(SIGSYS) \ + MAKE_SIG_ENTRY_SIGSTKFLT \ + MAKE_SIG_ENTRY_SIGIOT + #endif diff --git a/linux-user/signal.c b/linux-user/signal.c index 8d29bfaa6b..05dc4afb52 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -18,7 +18,7 @@ */ #include "qemu/osdep.h" #include "qemu/bitops.h" -#include "exec/gdbstub.h" +#include "gdbstub/user.h" #include "hw/core/tcg-cpu-ops.h" #include @@ -32,6 +32,10 @@ #include "signal-common.h" #include "host-signal.h" #include "user/safe-syscall.h" +#include "tcg/tcg.h" + +/* target_siginfo_t must fit in gdbstub's siginfo save area. */ +QEMU_BUILD_BUG_ON(sizeof(target_siginfo_t) > MAX_SIGINFO_LENGTH); static struct target_sigaction sigact_table[TARGET_NSIG]; @@ -43,9 +47,8 @@ abi_ulong default_sigreturn; abi_ulong default_rt_sigreturn; /* - * 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 + * System includes define _NSIG as SIGRTMAX + 1, but qemu (like the kernel) + * defines TARGET_NSIG as TARGET_SIGRTMAX and the first signal is 1. * Signal number 0 is reserved for use as kill(pid, 0), to test whether * a process exists without sending it a signal. */ @@ -53,41 +56,9 @@ abi_ulong default_rt_sigreturn; QEMU_BUILD_BUG_ON(__SIGRTMAX + 1 != _NSIG); #endif static uint8_t host_to_target_signal_table[_NSIG] = { - [SIGHUP] = TARGET_SIGHUP, - [SIGINT] = TARGET_SIGINT, - [SIGQUIT] = TARGET_SIGQUIT, - [SIGILL] = TARGET_SIGILL, - [SIGTRAP] = TARGET_SIGTRAP, - [SIGABRT] = TARGET_SIGABRT, -/* [SIGIOT] = TARGET_SIGIOT,*/ - [SIGBUS] = TARGET_SIGBUS, - [SIGFPE] = TARGET_SIGFPE, - [SIGKILL] = TARGET_SIGKILL, - [SIGUSR1] = TARGET_SIGUSR1, - [SIGSEGV] = TARGET_SIGSEGV, - [SIGUSR2] = TARGET_SIGUSR2, - [SIGPIPE] = TARGET_SIGPIPE, - [SIGALRM] = TARGET_SIGALRM, - [SIGTERM] = TARGET_SIGTERM, -#ifdef SIGSTKFLT - [SIGSTKFLT] = TARGET_SIGSTKFLT, -#endif - [SIGCHLD] = TARGET_SIGCHLD, - [SIGCONT] = TARGET_SIGCONT, - [SIGSTOP] = TARGET_SIGSTOP, - [SIGTSTP] = TARGET_SIGTSTP, - [SIGTTIN] = TARGET_SIGTTIN, - [SIGTTOU] = TARGET_SIGTTOU, - [SIGURG] = TARGET_SIGURG, - [SIGXCPU] = TARGET_SIGXCPU, - [SIGXFSZ] = TARGET_SIGXFSZ, - [SIGVTALRM] = TARGET_SIGVTALRM, - [SIGPROF] = TARGET_SIGPROF, - [SIGWINCH] = TARGET_SIGWINCH, - [SIGIO] = TARGET_SIGIO, - [SIGPWR] = TARGET_SIGPWR, - [SIGSYS] = TARGET_SIGSYS, - /* next signals stay the same */ +#define MAKE_SIG_ENTRY(sig) [sig] = TARGET_##sig, + MAKE_SIGNAL_LIST +#undef MAKE_SIG_ENTRY }; static uint8_t target_to_host_signal_table[TARGET_NSIG + 1]; @@ -95,18 +66,24 @@ 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 < 1 || sig >= _NSIG) { + if (sig < 1) { return sig; } + if (sig >= _NSIG) { + return TARGET_NSIG + 1; + } return host_to_target_signal_table[sig]; } /* valid sig is between 1 and TARGET_NSIG */ int target_to_host_signal(int sig) { - if (sig < 1 || sig > TARGET_NSIG) { + if (sig < 1) { return sig; } + if (sig > TARGET_NSIG) { + return _NSIG; + } return target_to_host_signal_table[sig]; } @@ -198,7 +175,7 @@ void target_to_host_old_sigset(sigset_t *sigset, int block_signals(void) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); sigset_t set; /* It's OK to block everything including SIGSEGV, because we won't @@ -220,7 +197,7 @@ int block_signals(void) */ int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); if (oldset) { *oldset = ts->signal_mask; @@ -263,7 +240,7 @@ int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset) */ void set_sigmask(const sigset_t *set) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); ts->signal_mask = *set; } @@ -272,7 +249,7 @@ void set_sigmask(const sigset_t *set) int on_sig_stack(unsigned long sp) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); return (sp - ts->sigaltstack_used.ss_sp < ts->sigaltstack_used.ss_size); @@ -280,7 +257,7 @@ int on_sig_stack(unsigned long sp) int sas_ss_flags(unsigned long sp) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); return (ts->sigaltstack_used.ss_size == 0 ? SS_DISABLE : on_sig_stack(sp) ? SS_ONSTACK : 0); @@ -291,7 +268,7 @@ abi_ulong target_sigsp(abi_ulong sp, struct target_sigaction *ka) /* * This is the X/Open sanctioned signal stack switching. */ - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { return ts->sigaltstack_used.ss_sp + ts->sigaltstack_used.ss_size; @@ -301,7 +278,7 @@ abi_ulong target_sigsp(abi_ulong sp, struct target_sigaction *ka) void target_save_altstack(target_stack_t *uss, CPUArchState *env) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); __put_user(ts->sigaltstack_used.ss_sp, &uss->ss_sp); __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &uss->ss_flags); @@ -310,7 +287,7 @@ void target_save_altstack(target_stack_t *uss, CPUArchState *env) abi_long target_restore_altstack(target_stack_t *uss, CPUArchState *env) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); size_t minstacksize = TARGET_MINSIGSTKSZ; target_stack_t ss; @@ -435,8 +412,8 @@ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, tinfo->si_code = deposit32(si_code, 16, 16, si_type); } -void tswap_siginfo(target_siginfo_t *tinfo, - const target_siginfo_t *info) +static void tswap_siginfo(target_siginfo_t *tinfo, + const target_siginfo_t *info) { int si_type = extract32(info->si_code, 16, 16); int si_code = sextract32(info->si_code, 0, 16); @@ -518,26 +495,6 @@ void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo) info->si_value.sival_ptr = (void *)(long)sival_ptr; } -static int fatal_signal (int sig) -{ - switch (sig) { - case TARGET_SIGCHLD: - case TARGET_SIGURG: - case TARGET_SIGWINCH: - /* Ignored by default. */ - return 0; - case TARGET_SIGCONT: - case TARGET_SIGSTOP: - case TARGET_SIGTSTP: - case TARGET_SIGTTIN: - case TARGET_SIGTTOU: - /* Job control signals. */ - return 0; - default: - return 1; - } -} - /* returns 1 if given signal should dump core if not handled */ static int core_dump_signal(int sig) { @@ -557,57 +514,68 @@ static int core_dump_signal(int sig) static void signal_table_init(void) { - int host_sig, target_sig, count; + int hsig, tsig, 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. + * until we run out of host realtime signals. Glibc uses the lower 2 + * RT signals and (hopefully) nobody uses the upper ones. + * This is why SIGRTMIN (34) is generally greater than __SIGRTMIN (32). + * To fix this properly we would need to do manual signal delivery + * multiplexed over a single host signal. * Attempts for configure "missing" signals via sigaction will be * silently ignored. + * + * Remap the target SIGABRT, so that we can distinguish host abort + * from guest abort. When the guest registers a signal handler or + * calls raise(SIGABRT), the host will raise SIG_RTn. If the guest + * arrives at dump_core_and_abort(), we will map back to host SIGABRT + * so that the parent (native or emulated) sees the correct signal. + * Finally, also map host to guest SIGABRT so that the emulated + * parent sees the correct mapping from wait status. */ - 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; + + hsig = SIGRTMIN; + host_to_target_signal_table[SIGABRT] = 0; + host_to_target_signal_table[hsig++] = TARGET_SIGABRT; + + for (tsig = TARGET_SIGRTMIN; + hsig <= SIGRTMAX && tsig <= TARGET_NSIG; + hsig++, tsig++) { + host_to_target_signal_table[hsig] = tsig; + } + + /* Invert the mapping that has already been assigned. */ + for (hsig = 1; hsig < _NSIG; hsig++) { + tsig = host_to_target_signal_table[hsig]; + if (tsig) { + assert(target_to_host_signal_table[tsig] == 0); + target_to_host_signal_table[tsig] = hsig; } } - /* 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; + host_to_target_signal_table[SIGABRT] = TARGET_SIGABRT; + + /* Map everything else out-of-bounds. */ + for (hsig = 1; hsig < _NSIG; hsig++) { + if (host_to_target_signal_table[hsig] == 0) { + host_to_target_signal_table[hsig] = TARGET_NSIG + 1; } - target_sig = host_to_target_signal_table[host_sig]; - if (target_sig <= TARGET_NSIG) { - target_to_host_signal_table[target_sig] = host_sig; + } + for (count = 0, tsig = 1; tsig <= TARGET_NSIG; tsig++) { + if (target_to_host_signal_table[tsig] == 0) { + target_to_host_signal_table[tsig] = _NSIG; + count++; } } - 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); - } + trace_signal_table_init(count); } void signal_init(void) { - TaskState *ts = (TaskState *)thread_cpu->opaque; - struct sigaction act; - struct sigaction oact; - int i; - int host_sig; + TaskState *ts = get_task_state(thread_cpu); + struct sigaction act, oact; /* initialize signal conversion tables */ signal_table_init(); @@ -618,27 +586,36 @@ void signal_init(void) sigfillset(&act.sa_mask); act.sa_flags = SA_SIGINFO; act.sa_sigaction = host_signal_handler; - for(i = 1; i <= TARGET_NSIG; i++) { -#ifdef CONFIG_GPROF - if (i == TARGET_SIGPROF) { + + /* + * A parent process may configure ignored signals, but all other + * signals are default. For any target signals that have no host + * mapping, set to ignore. For all core_dump_signal, install our + * host signal handler so that we may invoke dump_core_and_abort. + * This includes SIGSEGV and SIGBUS, which are also need our signal + * handler for paging and exceptions. + */ + for (int tsig = 1; tsig <= TARGET_NSIG; tsig++) { + int hsig = target_to_host_signal(tsig); + abi_ptr thand = TARGET_SIG_IGN; + + if (hsig >= _NSIG) { continue; } -#endif - host_sig = target_to_host_signal(i); - sigaction(host_sig, NULL, &oact); - if (oact.sa_sigaction == (void *)SIG_IGN) { - sigact_table[i - 1]._sa_handler = TARGET_SIG_IGN; - } else if (oact.sa_sigaction == (void *)SIG_DFL) { - sigact_table[i - 1]._sa_handler = TARGET_SIG_DFL; + + /* As we force remap SIGABRT, cannot probe and install in one step. */ + if (tsig == TARGET_SIGABRT) { + sigaction(SIGABRT, NULL, &oact); + sigaction(hsig, &act, NULL); + } else { + struct sigaction *iact = core_dump_signal(tsig) ? &act : NULL; + sigaction(hsig, iact, &oact); } - /* If there's already a handler installed then something has - gone horribly wrong, so don't even try to handle that case. */ - /* Install some handlers for our own use. We need at least - SIGSEGV and SIGBUS, to detect exceptions. We can not just - trap all signals because it affects syscall interrupt - behavior. But do trap all default-fatal signals. */ - if (fatal_signal (i)) - sigaction(host_sig, &act, NULL); + + if (oact.sa_sigaction != (void *)SIG_IGN) { + thand = TARGET_SIG_DFL; + } + sigact_table[tsig - 1]._sa_handler = thand; } } @@ -649,7 +626,6 @@ void signal_init(void) void force_sig(int sig) { CPUState *cpu = thread_cpu; - CPUArchState *env = cpu->env_ptr; target_siginfo_t info = {}; info.si_signo = sig; @@ -657,7 +633,7 @@ void force_sig(int sig) info.si_code = TARGET_SI_KERNEL; info._sifields._kill._pid = 0; info._sifields._kill._uid = 0; - queue_signal(env, info.si_signo, QEMU_SI_KILL, &info); + queue_signal(cpu_env(cpu), info.si_signo, QEMU_SI_KILL, &info); } /* @@ -667,14 +643,13 @@ void force_sig(int sig) void force_sig_fault(int sig, int code, abi_ulong addr) { CPUState *cpu = thread_cpu; - CPUArchState *env = cpu->env_ptr; target_siginfo_t info = {}; info.si_signo = sig; info.si_errno = 0; info.si_code = code; info._sifields._sigfault._addr = addr; - queue_signal(env, sig, QEMU_SI_FAULT, &info); + queue_signal(cpu_env(cpu), sig, QEMU_SI_FAULT, &info); } /* Force a SIGSEGV if we couldn't write to memory trying to set @@ -697,7 +672,7 @@ void force_sigsegv(int oldsig) void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, MMUAccessType access_type, bool maperr, uintptr_t ra) { - const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + const TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; if (tcg_ops->record_sigsegv) { tcg_ops->record_sigsegv(cpu, addr, access_type, maperr, ra); @@ -713,7 +688,7 @@ void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, MMUAccessType access_type, uintptr_t ra) { - const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + const TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; if (tcg_ops->record_sigbus) { tcg_ops->record_sigbus(cpu, addr, access_type, ra); @@ -726,15 +701,45 @@ void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, /* abort execution with signal */ static G_NORETURN -void dump_core_and_abort(int target_sig) +void die_with_signal(int host_sig) { - CPUState *cpu = thread_cpu; - CPUArchState *env = cpu->env_ptr; - TaskState *ts = (TaskState *)cpu->opaque; - int host_sig, core_dumped = 0; - struct sigaction act; + struct sigaction act = { + .sa_handler = SIG_DFL, + }; - host_sig = target_to_host_signal(target_sig); + /* + * The proper exit code for dying from an uncaught signal is -. + * The kernel doesn't allow exit() or _exit() to pass a negative value. + * To get the proper exit code we need to actually die from an uncaught + * signal. Here the default signal handler is installed, we send + * the signal and we wait for it to arrive. + */ + sigfillset(&act.sa_mask); + sigaction(host_sig, &act, NULL); + + kill(getpid(), host_sig); + + /* Make sure the signal isn't masked (reusing the mask inside of act). */ + sigdelset(&act.sa_mask, host_sig); + sigsuspend(&act.sa_mask); + + /* unreachable */ + _exit(EXIT_FAILURE); +} + +static G_NORETURN +void dump_core_and_abort(CPUArchState *env, int target_sig) +{ + CPUState *cpu = env_cpu(env); + TaskState *ts = get_task_state(cpu); + int host_sig, core_dumped = 0; + + /* On exit, undo the remapping of SIGABRT. */ + if (target_sig == TARGET_SIGABRT) { + host_sig = SIGABRT; + } else { + host_sig = target_to_host_signal(target_sig); + } trace_user_dump_core_and_abort(env, target_sig, host_sig); gdb_signalled(env, target_sig); @@ -755,28 +760,8 @@ void dump_core_and_abort(int target_sig) target_sig, strsignal(host_sig), "core dumped" ); } - /* The proper exit code for dying from an uncaught signal is - * -. The kernel doesn't allow exit() or _exit() to pass - * a negative value. To get the proper exit code we need to - * actually die from an uncaught signal. Here the default signal - * handler is installed, we send ourself a signal and we wait for - * it to arrive. */ - sigfillset(&act.sa_mask); - act.sa_handler = SIG_DFL; - act.sa_flags = 0; - sigaction(host_sig, &act, NULL); - - /* For some reason raise(host_sig) doesn't send the signal when - * statically linked on x86-64. */ - kill(getpid(), host_sig); - - /* Make sure the signal isn't masked (just reuse the mask inside - of act) */ - sigdelset(&act.sa_mask, host_sig); - sigsuspend(&act.sa_mask); - - /* unreachable */ - abort(); + preexit_cleanup(env, 128 + target_sig); + die_with_signal(host_sig); } /* queue a signal so that it will be send to the virtual CPU as soon @@ -785,7 +770,7 @@ void queue_signal(CPUArchState *env, int sig, int si_type, target_siginfo_t *info) { CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); trace_user_queue_signal(env, sig); @@ -810,72 +795,195 @@ static inline void rewind_if_in_safe_syscall(void *puc) } } +static G_NORETURN +void die_from_signal(siginfo_t *info) +{ + char sigbuf[4], codebuf[12]; + const char *sig, *code = NULL; + + switch (info->si_signo) { + case SIGSEGV: + sig = "SEGV"; + switch (info->si_code) { + case SEGV_MAPERR: + code = "MAPERR"; + break; + case SEGV_ACCERR: + code = "ACCERR"; + break; + } + break; + case SIGBUS: + sig = "BUS"; + switch (info->si_code) { + case BUS_ADRALN: + code = "ADRALN"; + break; + case BUS_ADRERR: + code = "ADRERR"; + break; + } + break; + case SIGILL: + sig = "ILL"; + switch (info->si_code) { + case ILL_ILLOPC: + code = "ILLOPC"; + break; + case ILL_ILLOPN: + code = "ILLOPN"; + break; + case ILL_ILLADR: + code = "ILLADR"; + break; + case ILL_PRVOPC: + code = "PRVOPC"; + break; + case ILL_PRVREG: + code = "PRVREG"; + break; + case ILL_COPROC: + code = "COPROC"; + break; + } + break; + case SIGFPE: + sig = "FPE"; + switch (info->si_code) { + case FPE_INTDIV: + code = "INTDIV"; + break; + case FPE_INTOVF: + code = "INTOVF"; + break; + } + break; + case SIGTRAP: + sig = "TRAP"; + break; + default: + snprintf(sigbuf, sizeof(sigbuf), "%d", info->si_signo); + sig = sigbuf; + break; + } + if (code == NULL) { + snprintf(codebuf, sizeof(sigbuf), "%d", info->si_code); + code = codebuf; + } + + error_report("QEMU internal SIG%s {code=%s, addr=%p}", + sig, code, info->si_addr); + die_with_signal(info->si_signo); +} + +static void host_sigsegv_handler(CPUState *cpu, siginfo_t *info, + host_sigcontext *uc) +{ + uintptr_t host_addr = (uintptr_t)info->si_addr; + /* + * Convert forcefully to guest address space: addresses outside + * reserved_va are still valid to report via SEGV_MAPERR. + */ + bool is_valid = h2g_valid(host_addr); + abi_ptr guest_addr = h2g_nocheck(host_addr); + uintptr_t pc = host_signal_pc(uc); + bool is_write = host_signal_write(info, uc); + MMUAccessType access_type = adjust_signal_pc(&pc, is_write); + bool maperr; + + /* If this was a write to a TB protected page, restart. */ + if (is_write + && is_valid + && info->si_code == SEGV_ACCERR + && handle_sigsegv_accerr_write(cpu, host_signal_mask(uc), + pc, guest_addr)) { + return; + } + + /* + * If the access was not on behalf of the guest, within the executable + * mapping of the generated code buffer, then it is a host bug. + */ + if (access_type != MMU_INST_FETCH + && !in_code_gen_buffer((void *)(pc - tcg_splitwx_diff))) { + die_from_signal(info); + } + + maperr = true; + if (is_valid && info->si_code == SEGV_ACCERR) { + /* + * With reserved_va, the whole address space is PROT_NONE, + * which means that we may get ACCERR when we want MAPERR. + */ + if (page_get_flags(guest_addr) & PAGE_VALID) { + maperr = false; + } else { + info->si_code = SEGV_MAPERR; + } + } + + sigprocmask(SIG_SETMASK, host_signal_mask(uc), NULL); + cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc); +} + +static uintptr_t host_sigbus_handler(CPUState *cpu, siginfo_t *info, + host_sigcontext *uc) +{ + uintptr_t pc = host_signal_pc(uc); + bool is_write = host_signal_write(info, uc); + MMUAccessType access_type = adjust_signal_pc(&pc, is_write); + + /* + * If the access was not on behalf of the guest, within the executable + * mapping of the generated code buffer, then it is a host bug. + */ + if (!in_code_gen_buffer((void *)(pc - tcg_splitwx_diff))) { + die_from_signal(info); + } + + if (info->si_code == BUS_ADRALN) { + uintptr_t host_addr = (uintptr_t)info->si_addr; + abi_ptr guest_addr = h2g_nocheck(host_addr); + + sigprocmask(SIG_SETMASK, host_signal_mask(uc), NULL); + cpu_loop_exit_sigbus(cpu, guest_addr, access_type, pc); + } + return pc; +} + static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) { - CPUArchState *env = thread_cpu->env_ptr; - CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; + CPUState *cpu = thread_cpu; + CPUArchState *env = cpu_env(cpu); + TaskState *ts = get_task_state(cpu); target_siginfo_t tinfo; host_sigcontext *uc = puc; struct emulated_sigtable *k; int guest_sig; uintptr_t pc = 0; bool sync_sig = false; - void *sigmask = host_signal_mask(uc); + void *sigmask; /* * Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special - * handling wrt signal blocking and unwinding. + * handling wrt signal blocking and unwinding. Non-spoofed SIGILL, + * SIGFPE, SIGTRAP are always host bugs. */ - if ((host_sig == SIGSEGV || host_sig == SIGBUS) && info->si_code > 0) { - MMUAccessType access_type; - uintptr_t host_addr; - abi_ptr guest_addr; - bool is_write; - - host_addr = (uintptr_t)info->si_addr; - - /* - * Convert forcefully to guest address space: addresses outside - * reserved_va are still valid to report via SEGV_MAPERR. - */ - guest_addr = h2g_nocheck(host_addr); - - pc = host_signal_pc(uc); - is_write = host_signal_write(info, uc); - access_type = adjust_signal_pc(&pc, is_write); - - if (host_sig == SIGSEGV) { - bool maperr = true; - - if (info->si_code == SEGV_ACCERR && h2g_valid(host_addr)) { - /* If this was a write to a TB protected page, restart. */ - if (is_write && - handle_sigsegv_accerr_write(cpu, sigmask, pc, guest_addr)) { - return; - } - - /* - * With reserved_va, the whole address space is PROT_NONE, - * which means that we may get ACCERR when we want MAPERR. - */ - if (page_get_flags(guest_addr) & PAGE_VALID) { - maperr = false; - } else { - info->si_code = SEGV_MAPERR; - } - } - - sigprocmask(SIG_SETMASK, sigmask, NULL); - cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc); - } else { - sigprocmask(SIG_SETMASK, sigmask, NULL); - if (info->si_code == BUS_ADRALN) { - cpu_loop_exit_sigbus(cpu, guest_addr, access_type, pc); - } + if (info->si_code > 0) { + switch (host_sig) { + case SIGSEGV: + /* Only returns on handle_sigsegv_accerr_write success. */ + host_sigsegv_handler(cpu, info, uc); + return; + case SIGBUS: + pc = host_sigbus_handler(cpu, info, uc); + sync_sig = true; + break; + case SIGILL: + case SIGFPE: + case SIGTRAP: + die_from_signal(info); } - - sync_sig = true; } /* get target signal number */ @@ -916,6 +1024,7 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) * would write 0xff bytes off the end of the structure and trash * data on the struct. */ + sigmask = host_signal_mask(uc); memset(sigmask, 0xff, SIGSET_T_SIZE); sigdelset(sigmask, SIGSEGV); sigdelset(sigmask, SIGBUS); @@ -971,7 +1080,6 @@ int do_sigaction(int sig, const struct target_sigaction *act, struct target_sigaction *oact, abi_ulong ka_restorer) { struct target_sigaction *k; - struct sigaction act1; int host_sig; int ret = 0; @@ -1031,22 +1139,27 @@ int do_sigaction(int sig, const struct target_sigaction *act, return 0; } if (host_sig != SIGSEGV && host_sig != SIGBUS) { + struct sigaction act1; + sigfillset(&act1.sa_mask); act1.sa_flags = SA_SIGINFO; - if (k->sa_flags & TARGET_SA_RESTART) - act1.sa_flags |= SA_RESTART; - /* NOTE: it is important to update the host kernel signal - ignore state to avoid getting unexpected interrupted - syscalls */ if (k->_sa_handler == TARGET_SIG_IGN) { + /* + * It is important to update the host kernel signal ignore + * state to avoid getting unexpected interrupted syscalls. + */ act1.sa_sigaction = (void *)SIG_IGN; } else if (k->_sa_handler == TARGET_SIG_DFL) { - if (fatal_signal (sig)) + if (core_dump_signal(sig)) { act1.sa_sigaction = host_signal_handler; - else + } else { act1.sa_sigaction = (void *)SIG_DFL; + } } else { act1.sa_sigaction = host_signal_handler; + if (k->sa_flags & TARGET_SA_RESTART) { + act1.sa_flags |= SA_RESTART; + } } ret = sigaction(host_sig, &act1, NULL); } @@ -1060,15 +1173,27 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, CPUState *cpu = env_cpu(cpu_env); abi_ulong handler; sigset_t set; + target_siginfo_t unswapped; target_sigset_t target_old_set; struct target_sigaction *sa; - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); trace_user_handle_signal(cpu_env, sig); /* dequeue signal */ k->pending = 0; - sig = gdb_handlesig(cpu, sig); + /* + * Writes out siginfo values byteswapped, accordingly to the target. + * It also cleans the si_type from si_code making it correct for + * the target. We must hold on to the original unswapped copy for + * strace below, because si_type is still required there. + */ + if (unlikely(qemu_loglevel_mask(LOG_STRACE))) { + unswapped = k->info; + } + tswap_siginfo(&k->info, &k->info); + + sig = gdb_handlesig(cpu, sig, NULL, &k->info, sizeof(k->info)); if (!sig) { sa = NULL; handler = TARGET_SIG_IGN; @@ -1078,7 +1203,7 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, } if (unlikely(qemu_loglevel_mask(LOG_STRACE))) { - print_taken_signal(sig, &k->info); + print_taken_signal(sig, &unswapped); } if (handler == TARGET_SIG_DFL) { @@ -1089,12 +1214,12 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, sig != TARGET_SIGURG && sig != TARGET_SIGWINCH && sig != TARGET_SIGCONT) { - dump_core_and_abort(sig); + dump_core_and_abort(cpu_env, sig); } } else if (handler == TARGET_SIG_IGN) { /* ignore sig */ } else if (handler == TARGET_SIG_ERR) { - dump_core_and_abort(sig); + dump_core_and_abort(cpu_env, sig); } else { /* compute the blocked signals during the handler execution */ sigset_t *blocked_set; @@ -1144,7 +1269,7 @@ void process_pending_signals(CPUArchState *cpu_env) { CPUState *cpu = env_cpu(cpu_env); int sig; - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); sigset_t set; sigset_t *blocked_set; @@ -1204,7 +1329,7 @@ void process_pending_signals(CPUArchState *cpu_env) int process_sigsuspend_mask(sigset_t **pset, target_ulong sigset, target_ulong sigsize) { - TaskState *ts = (TaskState *)thread_cpu->opaque; + TaskState *ts = get_task_state(thread_cpu); sigset_t *host_set = &ts->sigsuspend_mask; target_sigset_t *target_sigset; diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index 434c90a55f..50424a54df 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -149,6 +149,67 @@ static void flush_windows(CPUSPARCState *env) #endif } +static void next_instruction(CPUSPARCState *env) +{ + env->pc = env->npc; + env->npc = env->npc + 4; +} + +static uint32_t do_getcc(CPUSPARCState *env) +{ +#ifdef TARGET_SPARC64 + return cpu_get_ccr(env) & 0xf; +#else + return extract32(cpu_get_psr(env), 20, 4); +#endif +} + +static void do_setcc(CPUSPARCState *env, uint32_t icc) +{ +#ifdef TARGET_SPARC64 + cpu_put_ccr(env, (cpu_get_ccr(env) & 0xf0) | (icc & 0xf)); +#else + cpu_put_psr(env, deposit32(cpu_get_psr(env), 20, 4, icc)); +#endif +} + +static uint32_t do_getpsr(CPUSPARCState *env) +{ +#ifdef TARGET_SPARC64 + const uint64_t TSTATE_CWP = 0x1f; + const uint64_t TSTATE_ICC = 0xfull << 32; + const uint64_t TSTATE_XCC = 0xfull << 36; + const uint32_t PSR_S = 0x00000080u; + const uint32_t PSR_V8PLUS = 0xff000000u; + uint64_t tstate = sparc64_tstate(env); + + /* See , tstate_to_psr. */ + return ((tstate & TSTATE_CWP) | + PSR_S | + ((tstate & TSTATE_ICC) >> 12) | + ((tstate & TSTATE_XCC) >> 20) | + PSR_V8PLUS); +#else + return (cpu_get_psr(env) & (PSR_ICC | PSR_CWP)) | PSR_S; +#endif +} + +/* Avoid ifdefs below for the abi32 and abi64 paths. */ +#ifdef TARGET_ABI32 +#define TARGET_TT_SYSCALL (TT_TRAP + 0x10) /* t_linux */ +#else +#define TARGET_TT_SYSCALL (TT_TRAP + 0x6d) /* tl0_linux64 */ +#endif + +/* Avoid ifdefs below for the v9 and pre-v9 hw traps. */ +#ifdef TARGET_SPARC64 +#define TARGET_TT_SPILL TT_SPILL +#define TARGET_TT_FILL TT_FILL +#else +#define TARGET_TT_SPILL TT_WIN_OVF +#define TARGET_TT_FILL TT_WIN_UNF +#endif + void cpu_loop (CPUSPARCState *env) { CPUState *cs = env_cpu(env); @@ -161,19 +222,8 @@ void cpu_loop (CPUSPARCState *env) cpu_exec_end(cs); process_queued_cpu_work(cs); - /* Compute PSR before exposing state. */ - if (env->cc_op != CC_OP_FLAGS) { - cpu_get_psr(env); - } - switch (trapnr) { -#ifndef TARGET_SPARC64 - case 0x88: - case 0x90: -#else - case 0x110: - case 0x16d: -#endif + case TARGET_TT_SYSCALL: ret = do_syscall (env, env->gregs[1], env->regwptr[0], env->regwptr[1], env->regwptr[2], env->regwptr[3], @@ -183,71 +233,122 @@ void cpu_loop (CPUSPARCState *env) break; } if ((abi_ulong)ret >= (abi_ulong)(-515)) { -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) - env->xcc |= PSR_CARRY; -#else - env->psr |= PSR_CARRY; -#endif + set_syscall_C(env, 1); ret = -ret; } else { -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) - env->xcc &= ~PSR_CARRY; -#else - env->psr &= ~PSR_CARRY; -#endif + set_syscall_C(env, 0); } env->regwptr[0] = ret; /* next instruction */ env->pc = env->npc; env->npc = env->npc + 4; break; - case 0x83: /* flush windows */ -#ifdef TARGET_ABI32 - case 0x103: -#endif + + case TT_TRAP + 0x01: /* breakpoint */ + case EXCP_DEBUG: + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); + break; + + case TT_TRAP + 0x02: /* div0 */ + case TT_DIV_ZERO: + force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, env->pc); + break; + + case TT_TRAP + 0x03: /* flush windows */ flush_windows(env); - /* next instruction */ - env->pc = env->npc; - env->npc = env->npc + 4; + next_instruction(env); break; -#ifndef TARGET_SPARC64 - case TT_WIN_OVF: /* window overflow */ - save_window(env); + + case TT_TRAP + 0x20: /* getcc */ + env->gregs[1] = do_getcc(env); + next_instruction(env); break; - case TT_WIN_UNF: /* window underflow */ - restore_window(env); + case TT_TRAP + 0x21: /* setcc */ + do_setcc(env, env->gregs[1]); + next_instruction(env); break; -#else - case TT_SPILL: /* window overflow */ - save_window(env); + case TT_TRAP + 0x22: /* getpsr */ + env->gregs[1] = do_getpsr(env); + next_instruction(env); break; - case TT_FILL: /* window underflow */ - restore_window(env); - break; -#ifndef TARGET_ABI32 - case 0x16e: + +#ifdef TARGET_SPARC64 + case TT_TRAP + 0x6e: flush_windows(env); sparc64_get_context(env); break; - case 0x16f: + case TT_TRAP + 0x6f: flush_windows(env); sparc64_set_context(env); break; #endif -#endif + + case TARGET_TT_SPILL: /* window overflow */ + save_window(env); + break; + case TARGET_TT_FILL: /* window underflow */ + restore_window(env); + break; + + case TT_FP_EXCP: + { + int code = TARGET_FPE_FLTUNK; + target_ulong fsr = cpu_get_fsr(env); + + if ((fsr & FSR_FTT_MASK) == FSR_FTT_IEEE_EXCP) { + if (fsr & FSR_NVC) { + code = TARGET_FPE_FLTINV; + } else if (fsr & FSR_OFC) { + code = TARGET_FPE_FLTOVF; + } else if (fsr & FSR_UFC) { + code = TARGET_FPE_FLTUND; + } else if (fsr & FSR_DZC) { + code = TARGET_FPE_FLTDIV; + } else if (fsr & FSR_NXC) { + code = TARGET_FPE_FLTRES; + } + } + force_sig_fault(TARGET_SIGFPE, code, env->pc); + } + break; + case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; case TT_ILL_INSN: force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->pc); break; - case EXCP_DEBUG: - force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); + case TT_PRIV_INSN: + force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->pc); break; + case TT_TOVF: + force_sig_fault(TARGET_SIGEMT, TARGET_EMT_TAGOVF, env->pc); + break; +#ifdef TARGET_SPARC64 + case TT_PRIV_ACT: + /* Note do_privact defers to do_privop. */ + force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->pc); + break; +#else + case TT_NCP_INSN: + force_sig_fault(TARGET_SIGILL, TARGET_ILL_COPROC, env->pc); + break; + case TT_UNIMP_FLUSH: + next_instruction(env); + break; +#endif case EXCP_ATOMIC: cpu_exec_step_atomic(cs); break; default: + /* + * Most software trap numbers vector to BAD_TRAP. + * Handle anything not explicitly matched above. + */ + if (trapnr >= TT_TRAP && trapnr <= TT_TRAP + 0x7f) { + force_sig_fault(TARGET_SIGILL, ILL_ILLTRP, env->pc); + break; + } fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, 0); exit(EXIT_FAILURE); diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index b501750fe0..f164b74032 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -164,7 +164,7 @@ static void restore_pt_regs(struct target_pt_regs *regs, CPUSPARCState *env) */ uint32_t psr; __get_user(psr, ®s->psr); - env->psr = (psr & PSR_ICC) | (env->psr & ~PSR_ICC); + cpu_put_psr_icc(env, psr); #endif /* Note that pc and npc are handled in the caller. */ @@ -199,20 +199,21 @@ static void save_fpu(struct target_siginfo_fpu *fpu, CPUSPARCState *env) for (i = 0; i < 32; ++i) { __put_user(env->fpr[i].ll, &fpu->si_double_regs[i]); } - __put_user(env->fsr, &fpu->si_fsr); + __put_user(cpu_get_fsr(env), &fpu->si_fsr); __put_user(env->gsr, &fpu->si_gsr); __put_user(env->fprs, &fpu->si_fprs); #else for (i = 0; i < 16; ++i) { __put_user(env->fpr[i].ll, &fpu->si_double_regs[i]); } - __put_user(env->fsr, &fpu->si_fsr); + __put_user(cpu_get_fsr(env), &fpu->si_fsr); __put_user(0, &fpu->si_fpqdepth); #endif } static void restore_fpu(struct target_siginfo_fpu *fpu, CPUSPARCState *env) { + target_ulong fsr; int i; #ifdef TARGET_SPARC64 @@ -230,15 +231,16 @@ static void restore_fpu(struct target_siginfo_fpu *fpu, CPUSPARCState *env) __get_user(env->fpr[i].ll, &fpu->si_double_regs[i]); } } - __get_user(env->fsr, &fpu->si_fsr); __get_user(env->gsr, &fpu->si_gsr); env->fprs |= fprs; #else for (i = 0; i < 16; ++i) { __get_user(env->fpr[i].ll, &fpu->si_double_regs[i]); } - __get_user(env->fsr, &fpu->si_fsr); #endif + + __get_user(fsr, &fpu->si_fsr); + cpu_put_fsr(env, fsr); } #ifdef TARGET_ARCH_HAS_SETUP_FRAME @@ -331,7 +333,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(0, &sf->rwin_save); /* TODO: save_rwin_state */ - tswap_siginfo(&sf->info, info); + sf->info = *info; tswap_sigset(&sf->mask, set); target_save_altstack(&sf->stack, env); @@ -503,7 +505,23 @@ long do_rt_sigreturn(CPUSPARCState *env) return -QEMU_ESIGRETURN; } -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) +#ifdef TARGET_ABI32 +void setup_sigtramp(abi_ulong sigtramp_page) +{ + uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 8, 0); + assert(tramp != NULL); + + default_sigreturn = sigtramp_page; + install_sigtramp(tramp, TARGET_NR_sigreturn); + + default_rt_sigreturn = sigtramp_page + 8; + install_sigtramp(tramp + 2, TARGET_NR_rt_sigreturn); + + unlock_user(tramp, sigtramp_page, 2 * 8); +} +#endif + +#ifdef TARGET_SPARC64 #define SPARC_MC_TSTATE 0 #define SPARC_MC_PC 1 #define SPARC_MC_NPC 2 @@ -575,7 +593,7 @@ void sparc64_set_context(CPUSPARCState *env) struct target_ucontext *ucp; target_mc_gregset_t *grp; target_mc_fpu_t *fpup; - abi_ulong pc, npc, tstate; + target_ulong pc, npc, tstate; unsigned int i; unsigned char fenab; @@ -646,6 +664,7 @@ void sparc64_set_context(CPUSPARCState *env) __get_user(fenab, &(fpup->mcfpu_enab)); if (fenab) { abi_ulong fprs; + abi_ulong fsr; /* * We use the FPRS from the guest only in deciding whether @@ -674,7 +693,8 @@ void sparc64_set_context(CPUSPARCState *env) __get_user(env->fpr[i].ll, &(fpup->mcfpu_fregs.dregs[i])); } } - __get_user(env->fsr, &(fpup->mcfpu_fsr)); + __get_user(fsr, &(fpup->mcfpu_fsr)); + cpu_put_fsr(env, fsr); __get_user(env->gsr, &(fpup->mcfpu_gsr)); } unlock_user_struct(ucp, ucp_addr, 0); @@ -773,18 +793,4 @@ do_sigsegv: unlock_user_struct(ucp, ucp_addr, 1); force_sig(TARGET_SIGSEGV); } -#else -void setup_sigtramp(abi_ulong sigtramp_page) -{ - uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 8, 0); - assert(tramp != NULL); - - default_sigreturn = sigtramp_page; - install_sigtramp(tramp, TARGET_NR_sigreturn); - - default_rt_sigreturn = sigtramp_page + 8; - install_sigtramp(tramp + 2, TARGET_NR_rt_sigreturn); - - unlock_user(tramp, sigtramp_page, 2 * 8); -} -#endif +#endif /* TARGET_SPARC64 */ diff --git a/linux-user/sparc/target_cpu.h b/linux-user/sparc/target_cpu.h index 1f4bed50f4..5f62c5eb75 100644 --- a/linux-user/sparc/target_cpu.h +++ b/linux-user/sparc/target_cpu.h @@ -26,6 +26,17 @@ # define TARGET_STACK_BIAS 0 #endif +static void set_syscall_C(CPUSPARCState *env, bool val) +{ +#ifndef TARGET_SPARC64 + env->icc_C = val; +#elif defined(TARGET_ABI32) + env->icc_C = (uint64_t)val << 32; +#else + env->xcc_C = val; +#endif +} + static inline void cpu_clone_regs_child(CPUSPARCState *env, target_ulong newsp, unsigned flags) { @@ -58,11 +69,7 @@ static inline void cpu_clone_regs_child(CPUSPARCState *env, target_ulong newsp, * do the pc advance twice. */ env->regwptr[WREG_O0] = 0; -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) - env->xcc &= ~PSR_CARRY; -#else - env->psr &= ~PSR_CARRY; -#endif + set_syscall_C(env, 0); env->pc = env->npc; env->npc = env->npc + 4; } diff --git a/linux-user/sparc/target_mman.h b/linux-user/sparc/target_mman.h new file mode 100644 index 0000000000..696ca73fe4 --- /dev/null +++ b/linux-user/sparc/target_mman.h @@ -0,0 +1,35 @@ +#ifndef SPARC_TARGET_MMAN_H +#define SPARC_TARGET_MMAN_H + +#define TARGET_MAP_NORESERVE 0x40 +#define TARGET_MAP_LOCKED 0x100 +#define TARGET_MAP_GROWSDOWN 0x0200 + +/* + * arch/sparc/include/asm/page_64.h: + * TASK_UNMAPPED_BASE (test_thread_flag(TIF_32BIT) ? \ + * _AC(0x0000000070000000,UL) : \ + * VA_EXCLUDE_END) + * But VA_EXCLUDE_END is > 0xffff800000000000UL which doesn't work + * in userland emulation. + */ +#ifdef TARGET_ABI32 +#define TASK_UNMAPPED_BASE 0x70000000 +#else +#define TASK_UNMAPPED_BASE (1ull << (TARGET_VIRT_ADDR_SPACE_BITS - 2)) +#endif + +/* + * arch/sparc/include/asm/elf_64.h + * Except that COMPAT_ELF_ET_DYN_BASE exactly matches TASK_UNMAPPED_BASE, + * so move it up a bit. + */ +#ifdef TARGET_ABI32 +#define ELF_ET_DYN_BASE 0x78000000 +#else +#define ELF_ET_DYN_BASE 0x0000010000000000ull +#endif + +#include "../generic/target_mman.h" + +#endif diff --git a/linux-user/sparc/target_proc.h b/linux-user/sparc/target_proc.h new file mode 100644 index 0000000000..3bb3134a47 --- /dev/null +++ b/linux-user/sparc/target_proc.h @@ -0,0 +1,16 @@ +/* + * Sparc specific proc functions for linux-user + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef SPARC_TARGET_PROC_H +#define SPARC_TARGET_PROC_H + +static int open_cpuinfo(CPUArchState *cpu_env, int fd) +{ + dprintf(fd, "type\t\t: sun4u\n"); + return 0; +} +#define HAVE_ARCH_PROC_CPUINFO + +#endif /* SPARC_TARGET_PROC_H */ diff --git a/linux-user/sparc/target_signal.h b/linux-user/sparc/target_signal.h index 87757f0c4e..f223eb4af6 100644 --- a/linux-user/sparc/target_signal.h +++ b/linux-user/sparc/target_signal.h @@ -8,7 +8,7 @@ #define TARGET_SIGTRAP 5 #define TARGET_SIGABRT 6 #define TARGET_SIGIOT 6 -#define TARGET_SIGSTKFLT 7 /* actually EMT */ +#define TARGET_SIGEMT 7 #define TARGET_SIGFPE 8 #define TARGET_SIGKILL 9 #define TARGET_SIGBUS 10 diff --git a/linux-user/sparc/target_syscall.h b/linux-user/sparc/target_syscall.h index be77e44eb8..e421165357 100644 --- a/linux-user/sparc/target_syscall.h +++ b/linux-user/sparc/target_syscall.h @@ -50,11 +50,7 @@ static inline abi_ulong target_shmlba(CPUSPARCState *env) #ifdef TARGET_SPARC64 return MAX(TARGET_PAGE_SIZE, 16 * 1024); #else - if (!(env->def.features & CPU_FEATURE_FLUSH)) { - return 64 * 1024; - } else { - return 256 * 1024; - } + return 256 * 1024; #endif } diff --git a/linux-user/strace.c b/linux-user/strace.c index 7f1d7b3274..df88687a1a 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -17,6 +17,8 @@ #include "qemu.h" #include "user-internals.h" #include "strace.h" +#include "signal-common.h" +#include "target_mman.h" struct syscallname { int nr; @@ -44,15 +46,21 @@ struct syscallname { */ struct flags { abi_long f_value; /* flag */ + abi_long f_mask; /* mask */ const char *f_string; /* stringified flag */ }; +/* No 'struct flags' element should have a zero mask. */ +#define FLAG_BASIC(V, M, N) { V, M | QEMU_BUILD_BUG_ON_ZERO(!(M)), N } + /* common flags for all architectures */ -#define FLAG_GENERIC(name) { name, #name } +#define FLAG_GENERIC_MASK(V, M) FLAG_BASIC(V, M, #V) +#define FLAG_GENERIC(V) FLAG_BASIC(V, V, #V) /* target specific flags (syscall_defs.h has TARGET_) */ -#define FLAG_TARGET(name) { TARGET_ ## name, #name } +#define FLAG_TARGET_MASK(V, M) FLAG_BASIC(TARGET_##V, TARGET_##M, #V) +#define FLAG_TARGET(V) FLAG_BASIC(TARGET_##V, TARGET_##V, #V) /* end of flags array */ -#define FLAG_END { 0, NULL } +#define FLAG_END { 0, 0, NULL } /* Structure used to translate enumerated values into strings */ struct enums { @@ -79,8 +87,10 @@ UNUSED static void print_syscall_epilogue(const struct syscallname *); UNUSED static void print_string(abi_long, int); UNUSED static void print_buf(abi_long addr, abi_long len, int last); UNUSED static void print_raw_param(const char *, abi_long, int); +UNUSED static void print_raw_param64(const char *, long long, int last); UNUSED static void print_timeval(abi_ulong, int); UNUSED static void print_timespec(abi_ulong, int); +UNUSED static void print_timespec64(abi_ulong, int); UNUSED static void print_timezone(abi_ulong, int); UNUSED static void print_itimerval(abi_ulong, int); UNUSED static void print_number(abi_long, int); @@ -141,30 +151,21 @@ if( cmd == val ) { \ qemu_log("%d", cmd); } +static const char * const target_signal_name[] = { +#define MAKE_SIG_ENTRY(sig) [TARGET_##sig] = #sig, + MAKE_SIGNAL_LIST +#undef MAKE_SIG_ENTRY +}; + static void print_signal(abi_ulong arg, int last) { const char *signal_name = NULL; - switch(arg) { - case TARGET_SIGHUP: signal_name = "SIGHUP"; break; - case TARGET_SIGINT: signal_name = "SIGINT"; break; - case TARGET_SIGQUIT: signal_name = "SIGQUIT"; break; - case TARGET_SIGILL: signal_name = "SIGILL"; break; - case TARGET_SIGABRT: signal_name = "SIGABRT"; break; - case TARGET_SIGFPE: signal_name = "SIGFPE"; break; - case TARGET_SIGKILL: signal_name = "SIGKILL"; break; - case TARGET_SIGSEGV: signal_name = "SIGSEGV"; break; - case TARGET_SIGPIPE: signal_name = "SIGPIPE"; break; - case TARGET_SIGALRM: signal_name = "SIGALRM"; break; - case TARGET_SIGTERM: signal_name = "SIGTERM"; break; - case TARGET_SIGUSR1: signal_name = "SIGUSR1"; break; - case TARGET_SIGUSR2: signal_name = "SIGUSR2"; break; - case TARGET_SIGCHLD: signal_name = "SIGCHLD"; break; - case TARGET_SIGCONT: signal_name = "SIGCONT"; break; - case TARGET_SIGSTOP: signal_name = "SIGSTOP"; break; - case TARGET_SIGTTIN: signal_name = "SIGTTIN"; break; - case TARGET_SIGTTOU: signal_name = "SIGTTOU"; break; + + if (arg < ARRAY_SIZE(target_signal_name)) { + signal_name = target_signal_name[arg]; } + if (signal_name == NULL) { print_raw_param("%ld", arg, last); return; @@ -366,7 +367,6 @@ print_sockaddr(abi_ulong addr, abi_long addrlen, int last) switch (sa_family) { case AF_UNIX: { struct target_sockaddr_un *un = (struct target_sockaddr_un *)sa; - int i; qemu_log("{sun_family=AF_UNIX,sun_path=\""); for (i = 0; i < addrlen - offsetof(struct target_sockaddr_un, sun_path) && @@ -512,21 +512,69 @@ print_socket_protocol(int domain, int type, int protocol) case NETLINK_ROUTE: qemu_log("NETLINK_ROUTE"); break; + case NETLINK_UNUSED: + qemu_log("NETLINK_UNUSED"); + break; + case NETLINK_USERSOCK: + qemu_log("NETLINK_USERSOCK"); + break; + case NETLINK_FIREWALL: + qemu_log("NETLINK_FIREWALL"); + break; + case NETLINK_SOCK_DIAG: + qemu_log("NETLINK_SOCK_DIAG"); + break; + case NETLINK_NFLOG: + qemu_log("NETLINK_NFLOG"); + break; + case NETLINK_XFRM: + qemu_log("NETLINK_XFRM"); + break; + case NETLINK_SELINUX: + qemu_log("NETLINK_SELINUX"); + break; + case NETLINK_ISCSI: + qemu_log("NETLINK_ISCSI"); + break; case NETLINK_AUDIT: qemu_log("NETLINK_AUDIT"); break; + case NETLINK_FIB_LOOKUP: + qemu_log("NETLINK_FIB_LOOKUP"); + break; + case NETLINK_CONNECTOR: + qemu_log("NETLINK_CONNECTOR"); + break; case NETLINK_NETFILTER: qemu_log("NETLINK_NETFILTER"); break; + case NETLINK_IP6_FW: + qemu_log("NETLINK_IP6_FW"); + break; + case NETLINK_DNRTMSG: + qemu_log("NETLINK_DNRTMSG"); + break; case NETLINK_KOBJECT_UEVENT: qemu_log("NETLINK_KOBJECT_UEVENT"); break; + case NETLINK_GENERIC: + qemu_log("NETLINK_GENERIC"); + break; + case NETLINK_SCSITRANSPORT: + qemu_log("NETLINK_SCSITRANSPORT"); + break; + case NETLINK_ECRYPTFS: + qemu_log("NETLINK_ECRYPTFS"); + break; case NETLINK_RDMA: qemu_log("NETLINK_RDMA"); break; case NETLINK_CRYPTO: qemu_log("NETLINK_CRYPTO"); break; + case NETLINK_SMC: + qemu_log("NETLINK_SMC"); + break; default: qemu_log("%d", protocol); break; @@ -609,7 +657,6 @@ print_newselect(CPUArchState *cpu_env, const struct syscallname *name, } #endif -#ifdef TARGET_NR_semctl static void print_semctl(CPUArchState *cpu_env, const struct syscallname *name, abi_long arg1, abi_long arg2, abi_long arg3, @@ -620,38 +667,25 @@ print_semctl(CPUArchState *cpu_env, const struct syscallname *name, print_ipc_cmd(arg3); qemu_log(",0x" TARGET_ABI_FMT_lx ")", arg4); } -#endif static void -print_execve(CPUArchState *cpu_env, const struct syscallname *name, - abi_long arg1, abi_long arg2, abi_long arg3, - abi_long arg4, abi_long arg5, abi_long arg6) +print_shmat(CPUArchState *cpu_env, const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) { - abi_ulong arg_ptr_addr; - char *s; + static const struct flags shmat_flags[] = { + FLAG_GENERIC(SHM_RND), + FLAG_GENERIC(SHM_REMAP), + FLAG_GENERIC(SHM_RDONLY), + FLAG_GENERIC(SHM_EXEC), + FLAG_END + }; - if (!(s = lock_user_string(arg1))) - return; - qemu_log("%s(\"%s\",{", name->name, s); - unlock_user(s, arg1, 0); - - for (arg_ptr_addr = arg2; ; arg_ptr_addr += sizeof(abi_ulong)) { - abi_ulong *arg_ptr, arg_addr; - - arg_ptr = lock_user(VERIFY_READ, arg_ptr_addr, sizeof(abi_ulong), 1); - if (!arg_ptr) - return; - arg_addr = tswapal(*arg_ptr); - unlock_user(arg_ptr, arg_ptr_addr, 0); - if (!arg_addr) - break; - if ((s = lock_user_string(arg_addr))) { - qemu_log("\"%s\",", s); - unlock_user(s, arg_addr, 0); - } - } - - qemu_log("NULL})"); + print_syscall_prologue(name); + print_raw_param(TARGET_ABI_FMT_ld, arg0, 0); + print_pointer(arg1, 0); + print_flags(shmat_flags, arg2, 1); + print_syscall_epilogue(name); } #ifdef TARGET_NR_ipc @@ -662,10 +696,12 @@ print_ipc(CPUArchState *cpu_env, const struct syscallname *name, { switch(arg1) { case IPCOP_semctl: - qemu_log("semctl(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ",", - arg1, arg2); - print_ipc_cmd(arg3); - qemu_log(",0x" TARGET_ABI_FMT_lx ")", arg4); + print_semctl(cpu_env, &(const struct syscallname){ .name = "semctl" }, + arg2, arg3, arg4, arg5, 0, 0); + break; + case IPCOP_shmat: + print_shmat(cpu_env, &(const struct syscallname){ .name = "shmat" }, + arg2, arg5, arg3, 0, 0, 0); break; default: qemu_log(("%s(" @@ -803,6 +839,24 @@ print_syscall_ret_clock_gettime(CPUArchState *cpu_env, const struct syscallname #define print_syscall_ret_clock_getres print_syscall_ret_clock_gettime #endif +#if defined(TARGET_NR_clock_gettime64) +static void +print_syscall_ret_clock_gettime64(CPUArchState *cpu_env, const struct syscallname *name, + abi_long ret, abi_long arg0, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5) +{ + if (!print_syscall_err(ret)) { + qemu_log(TARGET_ABI_FMT_ld, ret); + qemu_log(" ("); + print_timespec64(arg1, 1); + qemu_log(")"); + } + + qemu_log("\n"); +} +#endif + #ifdef TARGET_NR_gettimeofday static void print_syscall_ret_gettimeofday(CPUArchState *cpu_env, const struct syscallname *name, @@ -933,15 +987,15 @@ print_syscall_ret_ioctl(CPUArchState *cpu_env, const struct syscallname *name, } #endif -UNUSED static struct flags access_flags[] = { - FLAG_GENERIC(F_OK), +UNUSED static const struct flags access_flags[] = { + FLAG_GENERIC_MASK(F_OK, R_OK | W_OK | X_OK), FLAG_GENERIC(R_OK), FLAG_GENERIC(W_OK), FLAG_GENERIC(X_OK), FLAG_END, }; -UNUSED static struct flags at_file_flags[] = { +UNUSED static const struct flags at_file_flags[] = { #ifdef AT_EACCESS FLAG_GENERIC(AT_EACCESS), #endif @@ -951,14 +1005,14 @@ UNUSED static struct flags at_file_flags[] = { FLAG_END, }; -UNUSED static struct flags unlinkat_flags[] = { +UNUSED static const struct flags unlinkat_flags[] = { #ifdef AT_REMOVEDIR FLAG_GENERIC(AT_REMOVEDIR), #endif FLAG_END, }; -UNUSED static struct flags mode_flags[] = { +UNUSED static const struct flags mode_flags[] = { FLAG_GENERIC(S_IFSOCK), FLAG_GENERIC(S_IFLNK), FLAG_GENERIC(S_IFREG), @@ -969,19 +1023,21 @@ UNUSED static struct flags mode_flags[] = { FLAG_END, }; -UNUSED static struct flags open_access_flags[] = { - FLAG_TARGET(O_RDONLY), - FLAG_TARGET(O_WRONLY), - FLAG_TARGET(O_RDWR), +UNUSED static const struct flags open_access_flags[] = { + FLAG_TARGET_MASK(O_RDONLY, O_ACCMODE), + FLAG_TARGET_MASK(O_WRONLY, O_ACCMODE), + FLAG_TARGET_MASK(O_RDWR, O_ACCMODE), FLAG_END, }; -UNUSED static struct flags open_flags[] = { +UNUSED static const struct flags open_flags[] = { FLAG_TARGET(O_APPEND), FLAG_TARGET(O_CREAT), FLAG_TARGET(O_DIRECTORY), FLAG_TARGET(O_EXCL), +#if TARGET_O_LARGEFILE != 0 FLAG_TARGET(O_LARGEFILE), +#endif FLAG_TARGET(O_NOCTTY), FLAG_TARGET(O_NOFOLLOW), FLAG_TARGET(O_NONBLOCK), /* also O_NDELAY */ @@ -1007,7 +1063,7 @@ UNUSED static struct flags open_flags[] = { FLAG_END, }; -UNUSED static struct flags mount_flags[] = { +UNUSED static const struct flags mount_flags[] = { #ifdef MS_BIND FLAG_GENERIC(MS_BIND), #endif @@ -1032,7 +1088,7 @@ UNUSED static struct flags mount_flags[] = { FLAG_END, }; -UNUSED static struct flags umount2_flags[] = { +UNUSED static const struct flags umount2_flags[] = { #ifdef MNT_FORCE FLAG_GENERIC(MNT_FORCE), #endif @@ -1045,8 +1101,8 @@ UNUSED static struct flags umount2_flags[] = { FLAG_END, }; -UNUSED static struct flags mmap_prot_flags[] = { - FLAG_GENERIC(PROT_NONE), +UNUSED static const struct flags mmap_prot_flags[] = { + FLAG_GENERIC_MASK(PROT_NONE, PROT_READ | PROT_WRITE | PROT_EXEC), FLAG_GENERIC(PROT_EXEC), FLAG_GENERIC(PROT_READ), FLAG_GENERIC(PROT_WRITE), @@ -1056,35 +1112,39 @@ UNUSED static struct flags mmap_prot_flags[] = { FLAG_END, }; -UNUSED static struct flags mmap_flags[] = { - FLAG_TARGET(MAP_SHARED), - FLAG_TARGET(MAP_PRIVATE), +UNUSED static const struct flags mmap_flags[] = { + FLAG_TARGET_MASK(MAP_SHARED, MAP_TYPE), + FLAG_TARGET_MASK(MAP_PRIVATE, MAP_TYPE), + FLAG_TARGET_MASK(MAP_SHARED_VALIDATE, MAP_TYPE), FLAG_TARGET(MAP_ANONYMOUS), FLAG_TARGET(MAP_DENYWRITE), - FLAG_TARGET(MAP_FIXED), - FLAG_TARGET(MAP_GROWSDOWN), FLAG_TARGET(MAP_EXECUTABLE), -#ifdef MAP_LOCKED + FLAG_TARGET(MAP_FIXED), + FLAG_TARGET(MAP_FIXED_NOREPLACE), + FLAG_TARGET(MAP_GROWSDOWN), + FLAG_TARGET(MAP_HUGETLB), FLAG_TARGET(MAP_LOCKED), -#endif -#ifdef MAP_NONBLOCK FLAG_TARGET(MAP_NONBLOCK), -#endif FLAG_TARGET(MAP_NORESERVE), -#ifdef MAP_POPULATE FLAG_TARGET(MAP_POPULATE), -#endif -#ifdef TARGET_MAP_UNINITIALIZED + FLAG_TARGET(MAP_STACK), + FLAG_TARGET(MAP_SYNC), +#if TARGET_MAP_UNINITIALIZED != 0 FLAG_TARGET(MAP_UNINITIALIZED), #endif FLAG_END, }; -UNUSED static struct flags clone_flags[] = { +#ifndef CLONE_PIDFD +# define CLONE_PIDFD 0x00001000 +#endif + +UNUSED static const struct flags clone_flags[] = { FLAG_GENERIC(CLONE_VM), FLAG_GENERIC(CLONE_FS), FLAG_GENERIC(CLONE_FILES), FLAG_GENERIC(CLONE_SIGHAND), + FLAG_GENERIC(CLONE_PIDFD), FLAG_GENERIC(CLONE_PTRACE), FLAG_GENERIC(CLONE_VFORK), FLAG_GENERIC(CLONE_PARENT), @@ -1124,7 +1184,17 @@ UNUSED static struct flags clone_flags[] = { FLAG_END, }; -UNUSED static struct flags msg_flags[] = { +UNUSED static const struct flags execveat_flags[] = { +#ifdef AT_EMPTY_PATH + FLAG_GENERIC(AT_EMPTY_PATH), +#endif +#ifdef AT_SYMLINK_NOFOLLOW + FLAG_GENERIC(AT_SYMLINK_NOFOLLOW), +#endif + FLAG_END, +}; + +UNUSED static const struct flags msg_flags[] = { /* send */ FLAG_GENERIC(MSG_CONFIRM), FLAG_GENERIC(MSG_DONTROUTE), @@ -1144,7 +1214,7 @@ UNUSED static struct flags msg_flags[] = { FLAG_END, }; -UNUSED static struct flags statx_flags[] = { +UNUSED static const struct flags statx_flags[] = { #ifdef AT_EMPTY_PATH FLAG_GENERIC(AT_EMPTY_PATH), #endif @@ -1155,18 +1225,18 @@ UNUSED static struct flags statx_flags[] = { FLAG_GENERIC(AT_SYMLINK_NOFOLLOW), #endif #ifdef AT_STATX_SYNC_AS_STAT - FLAG_GENERIC(AT_STATX_SYNC_AS_STAT), + FLAG_GENERIC_MASK(AT_STATX_SYNC_AS_STAT, AT_STATX_SYNC_TYPE), #endif #ifdef AT_STATX_FORCE_SYNC - FLAG_GENERIC(AT_STATX_FORCE_SYNC), + FLAG_GENERIC_MASK(AT_STATX_FORCE_SYNC, AT_STATX_SYNC_TYPE), #endif #ifdef AT_STATX_DONT_SYNC - FLAG_GENERIC(AT_STATX_DONT_SYNC), + FLAG_GENERIC_MASK(AT_STATX_DONT_SYNC, AT_STATX_SYNC_TYPE), #endif FLAG_END, }; -UNUSED static struct flags statx_mask[] = { +UNUSED static const struct flags statx_mask[] = { /* This must come first, because it includes everything. */ #ifdef STATX_ALL FLAG_GENERIC(STATX_ALL), @@ -1214,7 +1284,7 @@ UNUSED static struct flags statx_mask[] = { FLAG_END, }; -UNUSED static struct flags falloc_flags[] = { +UNUSED static const struct flags falloc_flags[] = { FLAG_GENERIC(FALLOC_FL_KEEP_SIZE), FLAG_GENERIC(FALLOC_FL_PUNCH_HOLE), #ifdef FALLOC_FL_NO_HIDE_STALE @@ -1234,7 +1304,7 @@ UNUSED static struct flags falloc_flags[] = { #endif }; -UNUSED static struct flags termios_iflags[] = { +UNUSED static const struct flags termios_iflags[] = { FLAG_TARGET(IGNBRK), FLAG_TARGET(BRKINT), FLAG_TARGET(IGNPAR), @@ -1253,7 +1323,7 @@ UNUSED static struct flags termios_iflags[] = { FLAG_END, }; -UNUSED static struct flags termios_oflags[] = { +UNUSED static const struct flags termios_oflags[] = { FLAG_TARGET(OPOST), FLAG_TARGET(OLCUC), FLAG_TARGET(ONLCR), @@ -1337,7 +1407,7 @@ UNUSED static struct enums termios_cflags_CSIZE[] = { ENUM_END, }; -UNUSED static struct flags termios_cflags[] = { +UNUSED static const struct flags termios_cflags[] = { FLAG_TARGET(CSTOPB), FLAG_TARGET(CREAD), FLAG_TARGET(PARENB), @@ -1348,7 +1418,7 @@ UNUSED static struct flags termios_cflags[] = { FLAG_END, }; -UNUSED static struct flags termios_lflags[] = { +UNUSED static const struct flags termios_lflags[] = { FLAG_TARGET(ISIG), FLAG_TARGET(ICANON), FLAG_TARGET(XCASE), @@ -1368,7 +1438,8 @@ UNUSED static struct flags termios_lflags[] = { FLAG_END, }; -UNUSED static struct flags mlockall_flags[] = { +#ifdef TARGET_NR_mlockall +static const struct flags mlockall_flags[] = { FLAG_TARGET(MCL_CURRENT), FLAG_TARGET(MCL_FUTURE), #ifdef MCL_ONFAULT @@ -1376,6 +1447,7 @@ UNUSED static struct flags mlockall_flags[] = { #endif FLAG_END, }; +#endif /* IDs of the various system clocks */ #define TARGET_CLOCK_REALTIME 0 @@ -1449,14 +1521,10 @@ print_flags(const struct flags *f, abi_long flags, int last) const char *sep = ""; int n; - if ((flags == 0) && (f->f_value == 0)) { - 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)) { + if ((flags & f->f_mask) == f->f_value) { qemu_log("%s%s", sep, f->f_string); - flags &= ~f->f_value; + flags &= ~f->f_mask; sep = "|"; n++; } @@ -1510,6 +1578,11 @@ print_file_mode(abi_long mode, int last) const char *sep = ""; const struct flags *m; + if (mode == 0) { + qemu_log("000%s", get_comma(last)); + return; + } + for (m = &mode_flags[0]; m->f_string != NULL; m++) { if ((m->f_value & mode) == m->f_value) { qemu_log("%s%s", m->f_string, sep); @@ -1611,6 +1684,19 @@ print_raw_param(const char *fmt, abi_long param, int last) qemu_log(format, param); } +/* + * Same as print_raw_param() but prints out raw 64-bit parameter. + */ +static void +print_raw_param64(const char *fmt, long long param, int last) +{ + char format[64]; + + (void)snprintf(format, sizeof(format), "%s%s", fmt, get_comma(last)); + qemu_log(format, param); +} + + static void print_pointer(abi_long p, int last) { @@ -1676,6 +1762,25 @@ print_timespec(abi_ulong ts_addr, int last) } } +static void +print_timespec64(abi_ulong ts_addr, int last) +{ + if (ts_addr) { + struct target__kernel_timespec *ts; + + ts = lock_user(VERIFY_READ, ts_addr, sizeof(*ts), 1); + if (!ts) { + print_pointer(ts_addr, last); + return; + } + print_raw_param64("{tv_sec=%" PRId64, tswap64(ts->tv_sec), 0); + print_raw_param64("tv_nsec=%" PRId64 "}", tswap64(ts->tv_nsec), last); + unlock_user(ts, ts_addr, 0); + } else { + qemu_log("NULL%s", get_comma(last)); + } +} + static void print_timezone(abi_ulong tz_addr, int last) { @@ -1947,7 +2052,59 @@ print_execv(CPUArchState *cpu_env, const struct syscallname *name, } #endif -#ifdef TARGET_NR_faccessat +static void +print_execve_argv(abi_long argv, int last) +{ + abi_ulong arg_ptr_addr; + char *s; + + qemu_log("{"); + for (arg_ptr_addr = argv; ; arg_ptr_addr += sizeof(abi_ulong)) { + abi_ulong *arg_ptr, arg_addr; + + arg_ptr = lock_user(VERIFY_READ, arg_ptr_addr, sizeof(abi_ulong), 1); + if (!arg_ptr) { + return; + } + arg_addr = tswapal(*arg_ptr); + unlock_user(arg_ptr, arg_ptr_addr, 0); + if (!arg_addr) { + break; + } + s = lock_user_string(arg_addr); + if (s) { + qemu_log("\"%s\",", s); + unlock_user(s, arg_addr, 0); + } + } + qemu_log("NULL}%s", get_comma(last)); +} + +static void +print_execve(CPUArchState *cpu_env, const struct syscallname *name, + abi_long arg1, abi_long arg2, abi_long arg3, + abi_long arg4, abi_long arg5, abi_long arg6) +{ + print_syscall_prologue(name); + print_string(arg1, 0); + print_execve_argv(arg2, 1); + print_syscall_epilogue(name); +} + +static void +print_execveat(CPUArchState *cpu_env, const struct syscallname *name, + abi_long arg1, abi_long arg2, abi_long arg3, + abi_long arg4, abi_long arg5, abi_long arg6) +{ + print_syscall_prologue(name); + print_at_dirfd(arg1, 0); + print_string(arg2, 0); + print_execve_argv(arg3, 0); + print_flags(execveat_flags, arg5, 1); + print_syscall_epilogue(name); +} + +#if defined(TARGET_NR_faccessat) || defined(TARGET_NR_faccessat2) static void print_faccessat(CPUArchState *cpu_env, const struct syscallname *name, abi_long arg0, abi_long arg1, abi_long arg2, @@ -2291,6 +2448,19 @@ print_clock_gettime(CPUArchState *cpu_env, const struct syscallname *name, #define print_clock_getres print_clock_gettime #endif +#if defined(TARGET_NR_clock_gettime64) +static void +print_clock_gettime64(CPUArchState *cpu_env, const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_enums(clockids, arg0, 0); + print_pointer(arg1, 1); + print_syscall_epilogue(name); +} +#endif + #ifdef TARGET_NR_clock_settime static void print_clock_settime(CPUArchState *cpu_env, const struct syscallname *name, @@ -2985,6 +3155,46 @@ print_stat(CPUArchState *cpu_env, const struct syscallname *name, #define print_lstat64 print_stat #endif +#if defined(TARGET_NR_madvise) +static struct enums madvise_advice[] = { + ENUM_TARGET(MADV_NORMAL), + ENUM_TARGET(MADV_RANDOM), + ENUM_TARGET(MADV_SEQUENTIAL), + ENUM_TARGET(MADV_WILLNEED), + ENUM_TARGET(MADV_DONTNEED), + ENUM_TARGET(MADV_FREE), + ENUM_TARGET(MADV_REMOVE), + ENUM_TARGET(MADV_DONTFORK), + ENUM_TARGET(MADV_DOFORK), + ENUM_TARGET(MADV_MERGEABLE), + ENUM_TARGET(MADV_UNMERGEABLE), + ENUM_TARGET(MADV_HUGEPAGE), + ENUM_TARGET(MADV_NOHUGEPAGE), + ENUM_TARGET(MADV_DONTDUMP), + ENUM_TARGET(MADV_DODUMP), + ENUM_TARGET(MADV_WIPEONFORK), + ENUM_TARGET(MADV_KEEPONFORK), + ENUM_TARGET(MADV_COLD), + ENUM_TARGET(MADV_PAGEOUT), + ENUM_TARGET(MADV_POPULATE_READ), + ENUM_TARGET(MADV_POPULATE_WRITE), + ENUM_TARGET(MADV_DONTNEED_LOCKED), + ENUM_END, +}; + +static void +print_madvise(CPUArchState *cpu_env, const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_pointer(arg0, 0); + print_raw_param("%d", arg1, 0); + print_enums(madvise_advice, arg2, 1); + print_syscall_epilogue(name); +} +#endif + #if defined(TARGET_NR_fstat) || defined(TARGET_NR_fstat64) static void print_fstat(CPUArchState *cpu_env, const struct syscallname *name, @@ -3067,7 +3277,8 @@ print_rt_sigprocmask(CPUArchState *cpu_env, const struct syscallname *name, } qemu_log("%s,", how); print_pointer(arg1, 0); - print_pointer(arg2, 1); + print_pointer(arg2, 0); + print_raw_param("%u", arg3, 1); print_syscall_epilogue(name); } #endif @@ -3288,6 +3499,34 @@ print_openat(CPUArchState *cpu_env, const struct syscallname *name, } #endif +#ifdef TARGET_NR_pidfd_send_signal +static void +print_pidfd_send_signal(CPUArchState *cpu_env, const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + void *p; + target_siginfo_t uinfo; + + print_syscall_prologue(name); + print_raw_param("%d", arg0, 0); + print_signal(arg1, 0); + + p = lock_user(VERIFY_READ, arg2, sizeof(target_siginfo_t), 1); + if (p) { + get_target_siginfo(&uinfo, p); + print_siginfo(&uinfo); + + unlock_user(p, arg2, 0); + } else { + print_pointer(arg2, 0); + } + + print_raw_param("%u", arg3, 1); + print_syscall_epilogue(name); +} +#endif + #ifdef TARGET_NR_mq_unlink static void print_mq_unlink(CPUArchState *cpu_env, const struct syscallname *name, @@ -3505,6 +3744,21 @@ print_unshare(CPUArchState *cpu_env, const struct syscallname *name, } #endif +#ifdef TARGET_NR_clock_nanosleep +static void +print_clock_nanosleep(CPUArchState *cpu_env, const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_enums(clockids, arg0, 0); + print_raw_param("%d", arg1, 0); + print_timespec(arg2, 0); + print_timespec(arg3, 1); + print_syscall_epilogue(name); +} +#endif + #ifdef TARGET_NR_utime static void print_utime(CPUArchState *cpu_env, const struct syscallname *name, @@ -3548,10 +3802,24 @@ print_utimensat(CPUArchState *cpu_env, const struct syscallname *name, #if defined(TARGET_NR_mmap) || defined(TARGET_NR_mmap2) static void -print_mmap(CPUArchState *cpu_env, const struct syscallname *name, +print_mmap_both(CPUArchState *cpu_env, const struct syscallname *name, abi_long arg0, abi_long arg1, abi_long arg2, - abi_long arg3, abi_long arg4, abi_long arg5) + abi_long arg3, abi_long arg4, abi_long arg5, + bool is_old_mmap) { + if (is_old_mmap) { + abi_ulong *v; + abi_ulong argp = arg0; + if (!(v = lock_user(VERIFY_READ, argp, 6 * sizeof(abi_ulong), 1))) + return; + arg0 = tswapal(v[0]); + arg1 = tswapal(v[1]); + arg2 = tswapal(v[2]); + arg3 = tswapal(v[3]); + arg4 = tswapal(v[4]); + arg5 = tswapal(v[5]); + unlock_user(v, argp, 0); + } print_syscall_prologue(name); print_pointer(arg0, 0); print_raw_param("%d", arg1, 0); @@ -3561,7 +3829,34 @@ print_mmap(CPUArchState *cpu_env, const struct syscallname *name, print_raw_param("%#x", arg5, 1); print_syscall_epilogue(name); } -#define print_mmap2 print_mmap +#endif + +#if defined(TARGET_NR_mmap) +static void +print_mmap(CPUArchState *cpu_env, const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + return print_mmap_both(cpu_env, name, arg0, arg1, arg2, arg3, + arg4, arg5, +#if defined(TARGET_NR_mmap2) + true +#else + false +#endif + ); +} +#endif + +#if defined(TARGET_NR_mmap2) +static void +print_mmap2(CPUArchState *cpu_env, const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + return print_mmap_both(cpu_env, name, arg0, arg1, arg2, arg3, + arg4, arg5, false); +} #endif #ifdef TARGET_NR_mprotect @@ -3592,44 +3887,37 @@ print_munmap(CPUArchState *cpu_env, const struct syscallname *name, #endif #ifdef TARGET_NR_futex -static void print_futex_op(abi_long tflag, int last) +static void print_futex_op(int cmd, int last) { -#define print_op(val) \ -if( cmd == val ) { \ - qemu_log(#val); \ - return; \ -} + static const char * const futex_names[] = { +#define NAME(X) [X] = #X + NAME(FUTEX_WAIT), + NAME(FUTEX_WAKE), + NAME(FUTEX_FD), + NAME(FUTEX_REQUEUE), + NAME(FUTEX_CMP_REQUEUE), + NAME(FUTEX_WAKE_OP), + NAME(FUTEX_LOCK_PI), + NAME(FUTEX_UNLOCK_PI), + NAME(FUTEX_TRYLOCK_PI), + NAME(FUTEX_WAIT_BITSET), + NAME(FUTEX_WAKE_BITSET), + NAME(FUTEX_WAIT_REQUEUE_PI), + NAME(FUTEX_CMP_REQUEUE_PI), + NAME(FUTEX_LOCK_PI2), +#undef NAME + }; - int cmd = (int)tflag; -#ifdef FUTEX_PRIVATE_FLAG - if (cmd & FUTEX_PRIVATE_FLAG) { - qemu_log("FUTEX_PRIVATE_FLAG|"); - cmd &= ~FUTEX_PRIVATE_FLAG; + unsigned base_cmd = cmd & FUTEX_CMD_MASK; + + if (base_cmd < ARRAY_SIZE(futex_names)) { + qemu_log("%s%s%s", + (cmd & FUTEX_PRIVATE_FLAG ? "FUTEX_PRIVATE_FLAG|" : ""), + (cmd & FUTEX_CLOCK_REALTIME ? "FUTEX_CLOCK_REALTIME|" : ""), + futex_names[base_cmd]); + } else { + qemu_log("0x%x", cmd); } -#endif -#ifdef FUTEX_CLOCK_REALTIME - if (cmd & FUTEX_CLOCK_REALTIME) { - qemu_log("FUTEX_CLOCK_REALTIME|"); - cmd &= ~FUTEX_CLOCK_REALTIME; - } -#endif - print_op(FUTEX_WAIT) - print_op(FUTEX_WAKE) - print_op(FUTEX_FD) - print_op(FUTEX_REQUEUE) - print_op(FUTEX_CMP_REQUEUE) - print_op(FUTEX_WAKE_OP) - print_op(FUTEX_LOCK_PI) - print_op(FUTEX_UNLOCK_PI) - print_op(FUTEX_TRYLOCK_PI) -#ifdef FUTEX_WAIT_BITSET - print_op(FUTEX_WAIT_BITSET) -#endif -#ifdef FUTEX_WAKE_BITSET - print_op(FUTEX_WAKE_BITSET) -#endif - /* unknown values */ - qemu_log("%d", cmd); } static void @@ -3637,17 +3925,117 @@ print_futex(CPUArchState *cpu_env, const struct syscallname *name, abi_long arg0, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5) { + abi_long op = arg1 & FUTEX_CMD_MASK; print_syscall_prologue(name); print_pointer(arg0, 0); print_futex_op(arg1, 0); print_raw_param(",%d", arg2, 0); - print_pointer(arg3, 0); /* struct timespec */ + switch (op) { + case FUTEX_WAIT: + case FUTEX_WAIT_BITSET: + case FUTEX_LOCK_PI: + case FUTEX_LOCK_PI2: + case FUTEX_WAIT_REQUEUE_PI: + print_timespec(arg3, 0); + break; + default: + print_pointer(arg3, 0); + break; + } print_pointer(arg4, 0); print_raw_param("%d", arg4, 1); print_syscall_epilogue(name); } #endif +#ifdef TARGET_NR_prlimit64 +static const char *target_ressource_string(abi_ulong r) +{ + #define RET_RES_ENTRY(res) case TARGET_##res: return #res; + switch (r) { + RET_RES_ENTRY(RLIMIT_AS); + RET_RES_ENTRY(RLIMIT_CORE); + RET_RES_ENTRY(RLIMIT_CPU); + RET_RES_ENTRY(RLIMIT_DATA); + RET_RES_ENTRY(RLIMIT_FSIZE); + RET_RES_ENTRY(RLIMIT_LOCKS); + RET_RES_ENTRY(RLIMIT_MEMLOCK); + RET_RES_ENTRY(RLIMIT_MSGQUEUE); + RET_RES_ENTRY(RLIMIT_NICE); + RET_RES_ENTRY(RLIMIT_NOFILE); + RET_RES_ENTRY(RLIMIT_NPROC); + RET_RES_ENTRY(RLIMIT_RSS); + RET_RES_ENTRY(RLIMIT_RTPRIO); +#ifdef RLIMIT_RTTIME + RET_RES_ENTRY(RLIMIT_RTTIME); +#endif + RET_RES_ENTRY(RLIMIT_SIGPENDING); + RET_RES_ENTRY(RLIMIT_STACK); + default: + return NULL; + } + #undef RET_RES_ENTRY +} + +static void +print_rlimit64(abi_ulong rlim_addr, int last) +{ + if (rlim_addr) { + struct target_rlimit64 *rl; + + rl = lock_user(VERIFY_READ, rlim_addr, sizeof(*rl), 1); + if (!rl) { + print_pointer(rlim_addr, last); + return; + } + print_raw_param64("{rlim_cur=%" PRId64, tswap64(rl->rlim_cur), 0); + print_raw_param64("rlim_max=%" PRId64 "}", tswap64(rl->rlim_max), + last); + unlock_user(rl, rlim_addr, 0); + } else { + qemu_log("NULL%s", get_comma(last)); + } +} + +static void +print_prlimit64(CPUArchState *cpu_env, const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + const char *rlim_name; + + print_syscall_prologue(name); + print_raw_param("%d", arg0, 0); + rlim_name = target_ressource_string(arg1); + if (rlim_name) { + qemu_log("%s,", rlim_name); + } else { + print_raw_param("%d", arg1, 0); + } + print_rlimit64(arg2, 0); + print_pointer(arg3, 1); + print_syscall_epilogue(name); +} + +static void +print_syscall_ret_prlimit64(CPUArchState *cpu_env, + const struct syscallname *name, + abi_long ret, abi_long arg0, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5) +{ + if (!print_syscall_err(ret)) { + qemu_log(TARGET_ABI_FMT_ld, ret); + if (arg3) { + qemu_log(" ("); + print_rlimit64(arg3, 1); + qemu_log(")"); + } + } + qemu_log("\n"); +} +#endif + #ifdef TARGET_NR_kill static void print_kill(CPUArchState *cpu_env, const struct syscallname *name, @@ -3688,6 +4076,25 @@ print_tgkill(CPUArchState *cpu_env, const struct syscallname *name, } #endif +#if defined(TARGET_NR_pread64) || defined(TARGET_NR_pwrite64) +static void +print_pread64(CPUArchState *cpu_env, const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + if (regpairs_aligned(cpu_env, TARGET_NR_pread64)) { + arg3 = arg4; + arg4 = arg5; + } + print_syscall_prologue(name); + print_raw_param("%d", arg0, 0); + print_pointer(arg1, 0); + print_raw_param("%d", arg2, 0); + print_raw_param("%" PRIu64, target_offset64(arg3, arg4), 1); + print_syscall_epilogue(name); +} +#endif + #ifdef TARGET_NR_statx static void print_statx(CPUArchState *cpu_env, const struct syscallname *name, @@ -3812,26 +4219,37 @@ print_syscall(CPUArchState *cpu_env, int num, abi_long arg4, abi_long arg5, abi_long arg6) { 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 ")"; + FILE *f; + 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 ")"; - qemu_log("%d ", getpid()); + f = qemu_log_trylock(); + if (!f) { + return; + } + fprintf(f, "%d ", getpid()); - for(i=0;i #include #include @@ -139,7 +142,7 @@ #include "special-errno.h" #include "qapi/error.h" #include "fd-trans.h" -#include "tcg/tcg.h" +#include "cpu_loop-common.h" #ifndef CLONE_IO #define CLONE_IO 0x80000000 /* Clone io context */ @@ -167,9 +170,13 @@ #define CLONE_IGNORED_FLAGS \ (CLONE_DETACHED | CLONE_IO) +#ifndef CLONE_PIDFD +# define CLONE_PIDFD 0x00001000 +#endif + /* Flags for fork which we can implement within QEMU itself */ #define CLONE_OPTIONAL_FORK_FLAGS \ - (CLONE_SETTLS | CLONE_PARENT_SETTID | \ + (CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_PIDFD | \ CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID) /* Flags for thread creation which we can implement within QEMU itself */ @@ -302,16 +309,16 @@ _syscall0(int, sys_gettid) #endif #if defined(TARGET_NR_getdents) && defined(EMULATE_GETDENTS_WITH_GETDENTS) -_syscall3(int, sys_getdents, uint, fd, struct linux_dirent *, dirp, uint, count); +_syscall3(int, sys_getdents, unsigned int, fd, struct linux_dirent *, dirp, unsigned int, count); #endif #if (defined(TARGET_NR_getdents) && \ !defined(EMULATE_GETDENTS_WITH_GETDENTS)) || \ (defined(TARGET_NR_getdents64) && defined(__NR_getdents64)) -_syscall3(int, sys_getdents64, uint, fd, struct linux_dirent64 *, dirp, uint, count); +_syscall3(int, sys_getdents64, unsigned int, fd, struct linux_dirent64 *, dirp, unsigned int, count); #endif #if defined(TARGET_NR__llseek) && defined(__NR_llseek) -_syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo, - loff_t *, res, uint, wh); +_syscall5(int, _llseek, unsigned int, fd, unsigned long, hi, unsigned long, lo, + loff_t *, res, unsigned int, wh); #endif _syscall3(int, sys_rt_sigqueueinfo, pid_t, pid, int, sig, siginfo_t *, uinfo) _syscall4(int, sys_rt_tgsigqueueinfo, pid_t, pid, pid_t, tid, int, sig, @@ -320,8 +327,12 @@ _syscall3(int,sys_syslog,int,type,char*,bufp,int,len) #ifdef __NR_exit_group _syscall1(int,exit_group,int,error_code) #endif -#if defined(TARGET_NR_set_tid_address) && defined(__NR_set_tid_address) -_syscall1(int,set_tid_address,int *,tidptr) +#if defined(__NR_close_range) && defined(TARGET_NR_close_range) +#define __NR_sys_close_range __NR_close_range +_syscall3(int,sys_close_range,int,first,int,last,int,flags) +#ifndef CLOSE_RANGE_CLOEXEC +#define CLOSE_RANGE_CLOEXEC (1U << 2) +#endif #endif #if defined(__NR_futex) _syscall6(int,sys_futex,int *,uaddr,int,op,int,val, @@ -331,6 +342,16 @@ _syscall6(int,sys_futex,int *,uaddr,int,op,int,val, _syscall6(int,sys_futex_time64,int *,uaddr,int,op,int,val, const struct timespec *,timeout,int *,uaddr2,int,val3) #endif +#if defined(__NR_pidfd_open) && defined(TARGET_NR_pidfd_open) +_syscall2(int, pidfd_open, pid_t, pid, unsigned int, flags); +#endif +#if defined(__NR_pidfd_send_signal) && defined(TARGET_NR_pidfd_send_signal) +_syscall4(int, pidfd_send_signal, int, pidfd, int, sig, siginfo_t *, info, + unsigned int, flags); +#endif +#if defined(__NR_pidfd_getfd) && defined(TARGET_NR_pidfd_getfd) +_syscall3(int, pidfd_getfd, int, pidfd, int, targetfd, unsigned int, flags); +#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); @@ -434,7 +455,6 @@ static const bitmask_transtbl fcntl_flags_tbl[] = { #if TARGET_O_LARGEFILE != 0 || O_LARGEFILE != 0 { TARGET_O_LARGEFILE, TARGET_O_LARGEFILE, O_LARGEFILE, O_LARGEFILE, }, #endif - { 0, 0, 0, 0 } }; _syscall2(int, sys_getcwd1, char *, buf, size_t, size) @@ -500,20 +520,25 @@ _syscall4(int, sys_prlimit64, pid_t, pid, int, resource, #if defined(TARGET_NR_timer_create) /* Maximum of 32 active POSIX timers allowed at any one time. */ -static timer_t g_posix_timers[32] = { 0, } ; +#define GUEST_TIMER_MAX 32 +static timer_t g_posix_timers[GUEST_TIMER_MAX]; +static int g_posix_timer_allocated[GUEST_TIMER_MAX]; static inline int next_free_host_timer(void) { - int k ; - /* FIXME: Does finding the next free slot require a lock? */ - for (k = 0; k < ARRAY_SIZE(g_posix_timers); k++) { - if (g_posix_timers[k] == 0) { - g_posix_timers[k] = (timer_t) 1; + int k; + for (k = 0; k < ARRAY_SIZE(g_posix_timer_allocated); k++) { + if (qatomic_xchg(g_posix_timer_allocated + k, 1) == 0) { return k; } } return -1; } + +static inline void free_host_timer_slot(int id) +{ + qatomic_store_release(g_posix_timer_allocated + id, 0); +} #endif static inline int host_to_target_errno(int host_errno) @@ -538,7 +563,7 @@ static inline int target_to_host_errno(int target_errno) } } -static inline abi_long get_errno(abi_long ret) +abi_long get_errno(abi_long ret) { if (ret == -1) return -host_to_target_errno(errno); @@ -634,6 +659,8 @@ safe_syscall4(pid_t, wait4, pid_t, pid, int *, status, int, options, \ 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) +safe_syscall5(int, execveat, int, dirfd, const char *, filename, + char **, argv, char **, envp, int, flags) #if defined(TARGET_NR_select) || defined(TARGET_NR__newselect) || \ defined(TARGET_NR_pselect6) || defined(TARGET_NR_pselect6_time64) safe_syscall6(int, pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds, \ @@ -773,88 +800,53 @@ static inline int host_to_target_sock_type(int host_type) return target_type; } -static abi_ulong target_brk; -static abi_ulong target_original_brk; -static abi_ulong brk_page; +static abi_ulong target_brk, initial_target_brk; void target_set_brk(abi_ulong new_brk) { - target_original_brk = target_brk = HOST_PAGE_ALIGN(new_brk); - brk_page = HOST_PAGE_ALIGN(target_brk); + target_brk = TARGET_PAGE_ALIGN(new_brk); + initial_target_brk = target_brk; } -//#define DEBUGF_BRK(message, args...) do { fprintf(stderr, (message), ## args); } while (0) -#define DEBUGF_BRK(message, args...) - /* do_brk() must return target values and target errnos. */ -abi_long do_brk(abi_ulong new_brk) +abi_long do_brk(abi_ulong brk_val) { abi_long mapped_addr; - abi_ulong new_alloc_size; + abi_ulong new_brk; + abi_ulong old_brk; /* brk pointers are always untagged */ - DEBUGF_BRK("do_brk(" TARGET_ABI_FMT_lx ") -> ", new_brk); - - if (!new_brk) { - DEBUGF_BRK(TARGET_ABI_FMT_lx " (!new_brk)\n", target_brk); - return target_brk; - } - if (new_brk < target_original_brk) { - DEBUGF_BRK(TARGET_ABI_FMT_lx " (new_brk < target_original_brk)\n", - target_brk); + /* do not allow to shrink below initial brk value */ + if (brk_val < initial_target_brk) { return target_brk; } - /* If the new brk is less than the highest page reserved to the - * target heap allocation, set it and we're almost done... */ - if (new_brk <= brk_page) { - /* Heap contents are initialized to zero, as for anonymous - * mapped pages. */ - if (new_brk > target_brk) { - memset(g2h_untagged(target_brk), 0, new_brk - target_brk); - } - target_brk = new_brk; - DEBUGF_BRK(TARGET_ABI_FMT_lx " (new_brk <= brk_page)\n", target_brk); - return target_brk; - } + new_brk = TARGET_PAGE_ALIGN(brk_val); + old_brk = TARGET_PAGE_ALIGN(target_brk); - /* We need to allocate more memory after the brk... Note that - * we don't use MAP_FIXED because that will map over the top of - * any existing mapping (like the one with the host libc or qemu - * itself); instead we treat "mapped but at wrong address" as - * a failure and unmap again. - */ - new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page); - mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size, - PROT_READ|PROT_WRITE, - MAP_ANON|MAP_PRIVATE, 0, 0)); - - if (mapped_addr == brk_page) { - /* Heap contents are initialized to zero, as for anonymous - * mapped pages. Technically the new pages are already - * initialized to zero since they *are* anonymous mapped - * pages, however we have to take care with the contents that - * come from the remaining part of the previous page: it may - * contains garbage data due to a previous heap usage (grown - * then shrunken). */ - memset(g2h_untagged(target_brk), 0, brk_page - target_brk); - - target_brk = new_brk; - brk_page = HOST_PAGE_ALIGN(target_brk); - DEBUGF_BRK(TARGET_ABI_FMT_lx " (mapped_addr == brk_page)\n", - target_brk); + /* new and old target_brk might be on the same page */ + if (new_brk == old_brk) { + target_brk = brk_val; return target_brk; - } else if (mapped_addr != -1) { - /* Mapped but at wrong address, meaning there wasn't actually - * enough space for this brk. - */ - target_munmap(mapped_addr, new_alloc_size); - mapped_addr = -1; - DEBUGF_BRK(TARGET_ABI_FMT_lx " (mapped_addr != -1)\n", target_brk); } - else { - DEBUGF_BRK(TARGET_ABI_FMT_lx " (otherwise)\n", target_brk); + + /* Release heap if necessary */ + if (new_brk < old_brk) { + target_munmap(new_brk, old_brk - new_brk); + + target_brk = brk_val; + return target_brk; + } + + mapped_addr = target_mmap(old_brk, new_brk - old_brk, + PROT_READ | PROT_WRITE, + MAP_FIXED_NOREPLACE | MAP_ANON | MAP_PRIVATE, + -1, 0); + + if (mapped_addr == old_brk) { + target_brk = brk_val; + return target_brk; } #if defined(TARGET_ALPHA) @@ -1589,21 +1581,12 @@ static abi_long do_ppoll(abi_long arg1, abi_long arg2, abi_long arg3, } #endif -static abi_long do_pipe2(int host_pipe[], int flags) -{ -#ifdef CONFIG_PIPE2 - return pipe2(host_pipe, flags); -#else - return -ENOSYS; -#endif -} - static abi_long do_pipe(CPUArchState *cpu_env, abi_ulong pipedes, int flags, int is_pipe2) { int host_pipe[2]; abi_long ret; - ret = flags ? do_pipe2(host_pipe, flags) : pipe(host_pipe); + ret = pipe2(host_pipe, flags); if (is_error(ret)) return get_errno(ret); @@ -1627,7 +1610,7 @@ static abi_long do_pipe(CPUArchState *cpu_env, abi_ulong pipedes, } if (put_user_s32(host_pipe[0], pipedes) - || put_user_s32(host_pipe[1], pipedes + sizeof(host_pipe[0]))) + || put_user_s32(host_pipe[1], pipedes + sizeof(abi_int))) return -TARGET_EFAULT; return get_errno(ret); } @@ -1701,6 +1684,11 @@ static inline abi_long target_to_host_sockaddr(int fd, struct sockaddr *addr, lladdr = (struct target_sockaddr_ll *)addr; lladdr->sll_ifindex = tswap32(lladdr->sll_ifindex); lladdr->sll_hatype = tswap16(lladdr->sll_hatype); + } else if (sa_family == AF_INET6) { + struct sockaddr_in6 *in6addr; + + in6addr = (struct sockaddr_in6 *)addr; + in6addr->sin6_scope_id = tswap32(in6addr->sin6_scope_id); } unlock_user(target_saddr, target_addr, 0); @@ -1817,6 +1805,14 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, __get_user(cred->pid, &target_cred->pid); __get_user(cred->uid, &target_cred->uid); __get_user(cred->gid, &target_cred->gid); + } else if (cmsg->cmsg_level == SOL_ALG) { + uint32_t *dst = (uint32_t *)data; + + memcpy(dst, target_data, len); + /* fix endianness of first 32-bit word */ + if (len >= sizeof(uint32_t)) { + *dst = tswap32(*dst); + } } else { qemu_log_mask(LOG_UNIMP, "Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type); @@ -2746,8 +2742,13 @@ get_timeout: ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); if (ret < 0) return ret; - if (optname == SO_TYPE) { + switch (optname) { + case SO_TYPE: val = host_to_target_sock_type(val); + break; + case SO_ERROR: + val = host_to_target_errno(val); + break; } if (len > lv) len = lv; @@ -2919,7 +2920,7 @@ get_timeout: unlock_user(results, optval_addr, 0); return ret; } - /* swap host endianess to target endianess. */ + /* swap host endianness to target endianness. */ for (i = 0; i < (len / sizeof(uint32_t)); i++) { results[i] = tswap32(results[i]); } @@ -3268,7 +3269,10 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp, target_vec, count, send); if (vec == NULL) { ret = -host_to_target_errno(errno); - goto out2; + /* allow sending packet without any iov, e.g. with MSG_MORE flag */ + if (!send || ret) { + goto out2; + } } msg.msg_iovlen = count; msg.msg_iov = vec; @@ -3299,7 +3303,8 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp, if (fd_trans_host_to_target_data(fd)) { ret = fd_trans_host_to_target_data(fd)(msg.msg_iov->iov_base, MIN(msg.msg_iov->iov_len, len)); - } else { + } + if (!is_error(ret)) { ret = host_to_target_cmsg(msgp, &msg); } if (!is_error(ret)) { @@ -3319,7 +3324,9 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp, } out: - unlock_iovec(vec, target_vec, count, !send); + if (vec) { + unlock_iovec(vec, target_vec, count, !send); + } out2: return ret; } @@ -3397,7 +3404,17 @@ static abi_long do_accept4(int fd, abi_ulong target_addr, abi_long ret; int host_flags; - host_flags = target_to_host_bitmask(flags, fcntl_flags_tbl); + if (flags & ~(TARGET_SOCK_CLOEXEC | TARGET_SOCK_NONBLOCK)) { + return -TARGET_EINVAL; + } + + host_flags = 0; + if (flags & TARGET_SOCK_NONBLOCK) { + host_flags |= SOCK_NONBLOCK; + } + if (flags & TARGET_SOCK_CLOEXEC) { + host_flags |= SOCK_CLOEXEC; + } if (target_addr == 0) { return get_errno(safe_accept4(fd, NULL, NULL, host_flags)); @@ -3708,14 +3725,6 @@ static abi_long do_socketcall(int num, abi_ulong vptr) } #endif -#define N_SHM_REGIONS 32 - -static struct shm_region { - abi_ulong start; - abi_ulong size; - bool in_use; -} shm_regions[N_SHM_REGIONS]; - #ifndef TARGET_SEMID64_DS /* asm-generic version of this struct */ struct target_semid64_ds @@ -4465,134 +4474,6 @@ static inline abi_long do_shmctl(int shmid, int cmd, abi_long buf) return ret; } -#ifndef TARGET_FORCE_SHMLBA -/* For most architectures, SHMLBA is the same as the page size; - * some architectures have larger values, in which case they should - * define TARGET_FORCE_SHMLBA and provide a target_shmlba() function. - * This corresponds to the kernel arch code defining __ARCH_FORCE_SHMLBA - * and defining its own value for SHMLBA. - * - * The kernel also permits SHMLBA to be set by the architecture to a - * value larger than the page size without setting __ARCH_FORCE_SHMLBA; - * this means that addresses are rounded to the large size if - * SHM_RND is set but addresses not aligned to that size are not rejected - * as long as they are at least page-aligned. Since the only architecture - * which uses this is ia64 this code doesn't provide for that oddity. - */ -static inline abi_ulong target_shmlba(CPUArchState *cpu_env) -{ - return TARGET_PAGE_SIZE; -} -#endif - -static inline abi_ulong do_shmat(CPUArchState *cpu_env, - int shmid, abi_ulong shmaddr, int shmflg) -{ - CPUState *cpu = env_cpu(cpu_env); - abi_long raddr; - void *host_raddr; - struct shmid_ds shm_info; - int i,ret; - abi_ulong shmlba; - - /* shmat pointers are always untagged */ - - /* find out the length of the shared memory segment */ - ret = get_errno(shmctl(shmid, IPC_STAT, &shm_info)); - if (is_error(ret)) { - /* can't get length, bail out */ - return ret; - } - - shmlba = target_shmlba(cpu_env); - - if (shmaddr & (shmlba - 1)) { - if (shmflg & SHM_RND) { - shmaddr &= ~(shmlba - 1); - } else { - return -TARGET_EINVAL; - } - } - if (!guest_range_valid_untagged(shmaddr, shm_info.shm_segsz)) { - return -TARGET_EINVAL; - } - - mmap_lock(); - - /* - * We're mapping shared memory, so ensure we generate code for parallel - * execution and flush old translations. This will work up to the level - * supported by the host -- anything that requires EXCP_ATOMIC will not - * be atomic with respect to an external process. - */ - if (!(cpu->tcg_cflags & CF_PARALLEL)) { - cpu->tcg_cflags |= CF_PARALLEL; - tb_flush(cpu); - } - - if (shmaddr) - host_raddr = shmat(shmid, (void *)g2h_untagged(shmaddr), shmflg); - else { - abi_ulong mmap_start; - - /* In order to use the host shmat, we need to honor host SHMLBA. */ - mmap_start = mmap_find_vma(0, shm_info.shm_segsz, MAX(SHMLBA, shmlba)); - - if (mmap_start == -1) { - errno = ENOMEM; - host_raddr = (void *)-1; - } else - host_raddr = shmat(shmid, g2h_untagged(mmap_start), - shmflg | SHM_REMAP); - } - - if (host_raddr == (void *)-1) { - mmap_unlock(); - return get_errno((long)host_raddr); - } - raddr=h2g((unsigned long)host_raddr); - - page_set_flags(raddr, raddr + shm_info.shm_segsz, - PAGE_VALID | PAGE_RESET | PAGE_READ | - (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE)); - - for (i = 0; i < N_SHM_REGIONS; i++) { - if (!shm_regions[i].in_use) { - shm_regions[i].in_use = true; - shm_regions[i].start = raddr; - shm_regions[i].size = shm_info.shm_segsz; - break; - } - } - - mmap_unlock(); - return raddr; - -} - -static inline abi_long do_shmdt(abi_ulong shmaddr) -{ - int i; - abi_long rv; - - /* shmdt pointers are always untagged */ - - mmap_lock(); - - for (i = 0; i < N_SHM_REGIONS; ++i) { - if (shm_regions[i].in_use && shm_regions[i].start == shmaddr) { - shm_regions[i].in_use = false; - page_set_flags(shmaddr, shmaddr + shm_regions[i].size, 0); - break; - } - } - rv = get_errno(shmdt(g2h_untagged(shmaddr))); - - mmap_unlock(); - - return rv; -} - #ifdef TARGET_NR_ipc /* ??? This only works with linear mappings. */ /* do_ipc() must return target values and target errnos. */ @@ -4679,7 +4560,7 @@ static abi_long do_ipc(CPUArchState *cpu_env, default: { abi_ulong raddr; - raddr = do_shmat(cpu_env, first, ptr, second); + raddr = target_shmat(cpu_env, first, ptr, second); if (is_error(raddr)) return get_errno(raddr); if (put_user_ual(raddr, third)) @@ -4692,7 +4573,7 @@ static abi_long do_ipc(CPUArchState *cpu_env, } break; case IPCOP_shmdt: - ret = do_shmdt(ptr); + ret = target_shmdt(ptr); break; case IPCOP_shmget: @@ -5169,8 +5050,8 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, { void *gspec = argptr; void *cur_data = host_data; - const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) }; - int spec_size = thunk_type_size(arg_type, 0); + const argtype dm_arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) }; + int spec_size = thunk_type_size(dm_arg_type, 0); int i; for (i = 0; i < host_dm->target_count; i++) { @@ -5178,7 +5059,7 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, uint32_t next; int slen; - thunk_convert(spec, gspec, arg_type, THUNK_HOST); + thunk_convert(spec, gspec, dm_arg_type, THUNK_HOST); slen = strlen((char*)gspec + spec_size) + 1; next = spec->next; spec->next = sizeof(*spec) + slen; @@ -5218,7 +5099,7 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, struct dm_name_list *nl = (void*)host_dm + host_dm->data_start; uint32_t remaining_data = guest_data_size; void *cur_data = argptr; - const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_name_list) }; + const argtype dm_arg_type[] = { MK_STRUCT(STRUCT_dm_name_list) }; int nl_size = 12; /* can't use thunk_size due to alignment */ while (1) { @@ -5230,7 +5111,7 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, host_dm->flags |= DM_BUFFER_FULL_FLAG; break; } - thunk_convert(cur_data, nl, arg_type, THUNK_TARGET); + thunk_convert(cur_data, nl, dm_arg_type, THUNK_TARGET); strcpy(cur_data + nl_size, nl->name); cur_data += nl->next; remaining_data -= nl->next; @@ -5246,8 +5127,8 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, { struct dm_target_spec *spec = (void*)host_dm + host_dm->data_start; void *cur_data = argptr; - const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) }; - int spec_size = thunk_type_size(arg_type, 0); + const argtype dm_arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) }; + int spec_size = thunk_type_size(dm_arg_type, 0); int i; for (i = 0; i < host_dm->target_count; i++) { @@ -5258,7 +5139,7 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, host_dm->flags |= DM_BUFFER_FULL_FLAG; break; } - thunk_convert(cur_data, spec, arg_type, THUNK_TARGET); + thunk_convert(cur_data, spec, dm_arg_type, THUNK_TARGET); strcpy(cur_data + spec_size, (char*)&spec[1]); cur_data = argptr + spec->next; spec = (void*)host_dm + host_dm->data_start + next; @@ -5286,8 +5167,8 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, struct dm_target_versions *vers = (void*)host_dm + host_dm->data_start; uint32_t remaining_data = guest_data_size; void *cur_data = argptr; - const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_versions) }; - int vers_size = thunk_type_size(arg_type, 0); + const argtype dm_arg_type[] = { MK_STRUCT(STRUCT_dm_target_versions) }; + int vers_size = thunk_type_size(dm_arg_type, 0); while (1) { uint32_t next = vers->next; @@ -5298,7 +5179,7 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, host_dm->flags |= DM_BUFFER_FULL_FLAG; break; } - thunk_convert(cur_data, vers, arg_type, THUNK_TARGET); + thunk_convert(cur_data, vers, dm_arg_type, THUNK_TARGET); strcpy(cur_data + vers_size, vers->name); cur_data += vers->next; remaining_data -= vers->next; @@ -5416,7 +5297,7 @@ static abi_long do_ioctl_rt(const IOCTLEntry *ie, uint8_t *buf_temp, for (i = 0; i < se->nb_fields; i++) { if (dst_offsets[i] == offsetof(struct rtentry, rt_dev)) { assert(*field_types == TYPE_PTRVOID); - target_rt_dev_ptr = (abi_ulong *)(argptr + src_offsets[i]); + target_rt_dev_ptr = argptr + src_offsets[i]; host_rt_dev_ptr = (unsigned long *)(buf_temp + dst_offsets[i]); if (*target_rt_dev_ptr != 0) { *host_rt_dev_ptr = (unsigned long)lock_user_string( @@ -5704,7 +5585,7 @@ static abi_long do_ioctl(int fd, int cmd, abi_long arg) if (ie->target_cmd == 0) { qemu_log_mask( LOG_UNIMP, "Unsupported ioctl: cmd=0x%04lx\n", (long)cmd); - return -TARGET_ENOSYS; + return -TARGET_ENOTTY; } if (ie->target_cmd == cmd) break; @@ -5716,7 +5597,7 @@ static abi_long do_ioctl(int fd, int cmd, abi_long arg) } else if (!ie->host_cmd) { /* Some architectures define BSD ioctls in their headers that are not implemented in Linux. */ - return -TARGET_ENOSYS; + return -TARGET_ENOTTY; } switch(arg_type[0]) { @@ -5774,7 +5655,7 @@ static abi_long do_ioctl(int fd, int cmd, abi_long arg) qemu_log_mask(LOG_UNIMP, "Unsupported ioctl type: cmd=0x%04lx type=%d\n", (long)cmd, arg_type[0]); - ret = -TARGET_ENOSYS; + ret = -TARGET_ENOTTY; break; } return ret; @@ -5796,7 +5677,6 @@ static const bitmask_transtbl iflag_tbl[] = { { TARGET_IXOFF, TARGET_IXOFF, IXOFF, IXOFF }, { TARGET_IMAXBEL, TARGET_IMAXBEL, IMAXBEL, IMAXBEL }, { TARGET_IUTF8, TARGET_IUTF8, IUTF8, IUTF8}, - { 0, 0, 0, 0 } }; static const bitmask_transtbl oflag_tbl[] = { @@ -5824,7 +5704,6 @@ static const bitmask_transtbl oflag_tbl[] = { { TARGET_VTDLY, TARGET_VT1, VTDLY, VT1 }, { TARGET_FFDLY, TARGET_FF0, FFDLY, FF0 }, { TARGET_FFDLY, TARGET_FF1, FFDLY, FF1 }, - { 0, 0, 0, 0 } }; static const bitmask_transtbl cflag_tbl[] = { @@ -5859,7 +5738,6 @@ static const bitmask_transtbl cflag_tbl[] = { { TARGET_HUPCL, TARGET_HUPCL, HUPCL, HUPCL }, { TARGET_CLOCAL, TARGET_CLOCAL, CLOCAL, CLOCAL }, { TARGET_CRTSCTS, TARGET_CRTSCTS, CRTSCTS, CRTSCTS }, - { 0, 0, 0, 0 } }; static const bitmask_transtbl lflag_tbl[] = { @@ -5879,7 +5757,6 @@ static const bitmask_transtbl lflag_tbl[] = { { TARGET_PENDIN, TARGET_PENDIN, PENDIN, PENDIN }, { TARGET_IEXTEN, TARGET_IEXTEN, IEXTEN, IEXTEN }, { TARGET_EXTPROC, TARGET_EXTPROC, EXTPROC, EXTPROC}, - { 0, 0, 0, 0 } }; static void target_to_host_termios (void *dst, const void *src) @@ -5959,9 +5836,15 @@ static const StructEntry struct_termios_def = { .print = print_termios, }; +/* If the host does not provide these bits, they may be safely discarded. */ +#ifndef MAP_SYNC +#define MAP_SYNC 0 +#endif +#ifndef MAP_UNINITIALIZED +#define MAP_UNINITIALIZED 0 +#endif + static const bitmask_transtbl mmap_flags_tbl[] = { - { TARGET_MAP_SHARED, TARGET_MAP_SHARED, MAP_SHARED, MAP_SHARED }, - { TARGET_MAP_PRIVATE, TARGET_MAP_PRIVATE, MAP_PRIVATE, MAP_PRIVATE }, { TARGET_MAP_FIXED, TARGET_MAP_FIXED, MAP_FIXED, MAP_FIXED }, { TARGET_MAP_ANONYMOUS, TARGET_MAP_ANONYMOUS, MAP_ANONYMOUS, MAP_ANONYMOUS }, @@ -5979,9 +5862,83 @@ static const bitmask_transtbl mmap_flags_tbl[] = { Recognize it for the target insofar as we do not want to pass it through to the host. */ { TARGET_MAP_STACK, TARGET_MAP_STACK, 0, 0 }, - { 0, 0, 0, 0 } + { TARGET_MAP_NONBLOCK, TARGET_MAP_NONBLOCK, MAP_NONBLOCK, MAP_NONBLOCK }, + { TARGET_MAP_POPULATE, TARGET_MAP_POPULATE, MAP_POPULATE, MAP_POPULATE }, + { TARGET_MAP_FIXED_NOREPLACE, TARGET_MAP_FIXED_NOREPLACE, + MAP_FIXED_NOREPLACE, MAP_FIXED_NOREPLACE }, + { TARGET_MAP_UNINITIALIZED, TARGET_MAP_UNINITIALIZED, + MAP_UNINITIALIZED, MAP_UNINITIALIZED }, }; +/* + * Arrange for legacy / undefined architecture specific flags to be + * ignored by mmap handling code. + */ +#ifndef TARGET_MAP_32BIT +#define TARGET_MAP_32BIT 0 +#endif +#ifndef TARGET_MAP_HUGE_2MB +#define TARGET_MAP_HUGE_2MB 0 +#endif +#ifndef TARGET_MAP_HUGE_1GB +#define TARGET_MAP_HUGE_1GB 0 +#endif + +static abi_long do_mmap(abi_ulong addr, abi_ulong len, int prot, + int target_flags, int fd, off_t offset) +{ + /* + * The historical set of flags that all mmap types implicitly support. + */ + enum { + TARGET_LEGACY_MAP_MASK = TARGET_MAP_SHARED + | TARGET_MAP_PRIVATE + | TARGET_MAP_FIXED + | TARGET_MAP_ANONYMOUS + | TARGET_MAP_DENYWRITE + | TARGET_MAP_EXECUTABLE + | TARGET_MAP_UNINITIALIZED + | TARGET_MAP_GROWSDOWN + | TARGET_MAP_LOCKED + | TARGET_MAP_NORESERVE + | TARGET_MAP_POPULATE + | TARGET_MAP_NONBLOCK + | TARGET_MAP_STACK + | TARGET_MAP_HUGETLB + | TARGET_MAP_32BIT + | TARGET_MAP_HUGE_2MB + | TARGET_MAP_HUGE_1GB + }; + int host_flags; + + switch (target_flags & TARGET_MAP_TYPE) { + case TARGET_MAP_PRIVATE: + host_flags = MAP_PRIVATE; + break; + case TARGET_MAP_SHARED: + host_flags = MAP_SHARED; + break; + case TARGET_MAP_SHARED_VALIDATE: + /* + * MAP_SYNC is only supported for MAP_SHARED_VALIDATE, and is + * therefore omitted from mmap_flags_tbl and TARGET_LEGACY_MAP_MASK. + */ + if (target_flags & ~(TARGET_LEGACY_MAP_MASK | TARGET_MAP_SYNC)) { + return -TARGET_EOPNOTSUPP; + } + host_flags = MAP_SHARED_VALIDATE; + if (target_flags & TARGET_MAP_SYNC) { + host_flags |= MAP_SYNC; + } + break; + default: + return -TARGET_EINVAL; + } + host_flags |= target_to_host_bitmask(target_flags, mmap_flags_tbl); + + return get_errno(target_mmap(addr, len, prot, host_flags, fd, offset)); +} + /* * NOTE: TARGET_ABI32 is defined for TARGET_I386 (but not for TARGET_X86_64) * TARGET_I386 is defined if TARGET_X86_64 is defined @@ -6346,6 +6303,12 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) #ifndef PR_SET_SYSCALL_USER_DISPATCH # define PR_SET_SYSCALL_USER_DISPATCH 59 #endif +#ifndef PR_SME_SET_VL +# define PR_SME_SET_VL 63 +# define PR_SME_GET_VL 64 +# define PR_SME_VL_LEN_MASK 0xffff +# define PR_SME_VL_INHERIT (1 << 17) +#endif #include "target_prctl.h" @@ -6365,11 +6328,11 @@ static abi_long do_prctl_inval1(CPUArchState *env, abi_long arg2) #ifndef do_prctl_set_fp_mode #define do_prctl_set_fp_mode do_prctl_inval1 #endif -#ifndef do_prctl_get_vl -#define do_prctl_get_vl do_prctl_inval0 +#ifndef do_prctl_sve_get_vl +#define do_prctl_sve_get_vl do_prctl_inval0 #endif -#ifndef do_prctl_set_vl -#define do_prctl_set_vl do_prctl_inval1 +#ifndef do_prctl_sve_set_vl +#define do_prctl_sve_set_vl do_prctl_inval1 #endif #ifndef do_prctl_reset_keys #define do_prctl_reset_keys do_prctl_inval1 @@ -6386,6 +6349,12 @@ static abi_long do_prctl_inval1(CPUArchState *env, abi_long arg2) #ifndef do_prctl_set_unalign #define do_prctl_set_unalign do_prctl_inval1 #endif +#ifndef do_prctl_sme_get_vl +#define do_prctl_sme_get_vl do_prctl_inval0 +#endif +#ifndef do_prctl_sme_set_vl +#define do_prctl_sme_set_vl do_prctl_inval1 +#endif static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5) @@ -6434,9 +6403,13 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2, case PR_SET_FP_MODE: return do_prctl_set_fp_mode(env, arg2); case PR_SVE_GET_VL: - return do_prctl_get_vl(env); + return do_prctl_sve_get_vl(env); case PR_SVE_SET_VL: - return do_prctl_set_vl(env, arg2); + return do_prctl_sve_set_vl(env, arg2); + case PR_SME_GET_VL: + return do_prctl_sme_get_vl(env); + case PR_SME_SET_VL: + return do_prctl_sme_set_vl(env, arg2); case PR_PAC_RESET_KEYS: if (arg3 || arg4 || arg5) { return -TARGET_EINVAL; @@ -6477,16 +6450,28 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2, case PR_SET_NO_NEW_PRIVS: case PR_GET_IO_FLUSHER: case PR_SET_IO_FLUSHER: + case PR_SET_CHILD_SUBREAPER: + case PR_GET_SPECULATION_CTRL: + case PR_SET_SPECULATION_CTRL: /* Some prctl options have no pointer arguments and we can pass on. */ return get_errno(prctl(option, arg2, arg3, arg4, arg5)); case PR_GET_CHILD_SUBREAPER: - case PR_SET_CHILD_SUBREAPER: - case PR_GET_SPECULATION_CTRL: - case PR_SET_SPECULATION_CTRL: + { + int val; + ret = get_errno(prctl(PR_GET_CHILD_SUBREAPER, &val, + arg3, arg4, arg5)); + if (!is_error(ret) && put_user_s32(val, arg2)) { + return -TARGET_EFAULT; + } + return ret; + } + case PR_GET_TID_ADDRESS: - /* TODO */ - return -TARGET_EINVAL; + { + TaskState *ts = env_cpu(env)->opaque; + return put_user_ual(ts->child_tidptr, arg2); + } case PR_GET_FPEXC: case PR_SET_FPEXC: @@ -6542,7 +6527,7 @@ static void *clone_func(void *arg) env = info->env; cpu = env_cpu(env); thread_cpu = cpu; - ts = (TaskState *)cpu->opaque; + ts = get_task_state(cpu); info->tid = sys_gettid(); task_settid(ts); if (info->child_tidptr) @@ -6584,7 +6569,7 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, flags &= ~(CLONE_VFORK | CLONE_VM); if (flags & CLONE_VM) { - TaskState *parent_ts = (TaskState *)cpu->opaque; + TaskState *parent_ts = get_task_state(cpu); new_thread_info info; pthread_attr_t attr; @@ -6676,6 +6661,17 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, return -TARGET_EINVAL; } +#if !defined(__NR_pidfd_open) || !defined(TARGET_NR_pidfd_open) + if (flags & CLONE_PIDFD) { + return -TARGET_EINVAL; + } +#endif + + /* Can not allow CLONE_PIDFD with CLONE_PARENT_SETTID */ + if ((flags & CLONE_PIDFD) && (flags & CLONE_PARENT_SETTID)) { + return -TARGET_EINVAL; + } + if (block_signals()) { return -QEMU_ERESTARTSYS; } @@ -6685,7 +6681,7 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, if (ret == 0) { /* Child Process. */ cpu_clone_regs_child(env, newsp, flags); - fork_end(1); + fork_end(ret); /* There is a race condition here. The parent process could theoretically read the TID in the child process before the child tid is set. This would require using either ptrace @@ -6696,15 +6692,30 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, put_user_u32(sys_gettid(), child_tidptr); if (flags & CLONE_PARENT_SETTID) put_user_u32(sys_gettid(), parent_tidptr); - ts = (TaskState *)cpu->opaque; + ts = get_task_state(cpu); if (flags & CLONE_SETTLS) cpu_set_tls (env, newtls); if (flags & CLONE_CHILD_CLEARTID) ts->child_tidptr = child_tidptr; } else { cpu_clone_regs_parent(env, flags); - fork_end(0); + if (flags & CLONE_PIDFD) { + int pid_fd = 0; +#if defined(__NR_pidfd_open) && defined(TARGET_NR_pidfd_open) + int pid_child = ret; + pid_fd = pidfd_open(pid_child, 0); + if (pid_fd >= 0) { + fcntl(pid_fd, F_SETFD, fcntl(pid_fd, F_GETFL) + | FD_CLOEXEC); + } else { + pid_fd = 0; + } +#endif + put_user_u32(pid_fd, parent_tidptr); + } + fork_end(ret); } + g_assert(!cpu_in_exclusive_context(cpu)); } return ret; } @@ -7567,6 +7578,10 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) ret = get_errno(safe_fcntl(fd, host_cmd, arg)); if (ret >= 0) { ret = host_to_target_bitmask(ret, fcntl_flags_tbl); + /* tell 32-bit guests it uses largefile on 64-bit hosts: */ + if (O_LARGEFILE == 0 && HOST_LONG_BITS == 64) { + ret |= TARGET_O_LARGEFILE; + } } break; @@ -8078,6 +8093,14 @@ static inline int target_to_host_mlockall_arg(int arg) } #endif +static inline int target_to_host_msync_arg(abi_long arg) +{ + return ((arg & TARGET_MS_ASYNC) ? MS_ASYNC : 0) | + ((arg & TARGET_MS_INVALIDATE) ? MS_INVALIDATE : 0) | + ((arg & TARGET_MS_SYNC) ? MS_SYNC : 0) | + (arg & ~(TARGET_MS_ASYNC | TARGET_MS_INVALIDATE | TARGET_MS_SYNC)); +} + #if (defined(TARGET_NR_stat64) || defined(TARGET_NR_lstat64) || \ defined(TARGET_NR_fstat64) || defined(TARGET_NR_fstatat64) || \ defined(TARGET_NR_newfstatat)) @@ -8251,15 +8274,16 @@ static int do_safe_futex(int *uaddr, int op, int val, 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(CPUState *cpu, target_ulong uaddr, int op, int val, - target_ulong timeout, target_ulong uaddr2, int val3) +#if defined(TARGET_NR_futex) || defined(TARGET_NR_futex_time64) +static int do_futex(CPUState *cpu, bool time64, target_ulong uaddr, + int op, int val, target_ulong timeout, + target_ulong uaddr2, int val3) { - struct timespec ts, *pts; + struct timespec ts, *pts = NULL; + void *haddr2 = NULL; int base_op; - /* ??? We assume FUTEX_* constants are the same on both host - and target. */ + /* We assume FUTEX_* constants are the same on both host and target. */ #ifdef FUTEX_CMD_MASK base_op = op & FUTEX_CMD_MASK; #else @@ -8268,85 +8292,53 @@ static int do_futex(CPUState *cpu, target_ulong uaddr, int op, int val, switch (base_op) { case FUTEX_WAIT: case FUTEX_WAIT_BITSET: - if (timeout) { - pts = &ts; - target_to_host_timespec(pts, timeout); - } else { - pts = NULL; - } - return do_safe_futex(g2h(cpu, uaddr), - op, tswap32(val), pts, NULL, val3); + val = tswap32(val); + break; + case FUTEX_WAIT_REQUEUE_PI: + val = tswap32(val); + haddr2 = g2h(cpu, uaddr2); + break; + case FUTEX_LOCK_PI: + case FUTEX_LOCK_PI2: + break; case FUTEX_WAKE: - return do_safe_futex(g2h(cpu, uaddr), - op, val, NULL, NULL, 0); + case FUTEX_WAKE_BITSET: + case FUTEX_TRYLOCK_PI: + case FUTEX_UNLOCK_PI: + timeout = 0; + break; case FUTEX_FD: - return do_safe_futex(g2h(cpu, uaddr), - op, val, NULL, NULL, 0); - case FUTEX_REQUEUE: + val = target_to_host_signal(val); + timeout = 0; + break; case FUTEX_CMP_REQUEUE: + case FUTEX_CMP_REQUEUE_PI: + val3 = tswap32(val3); + /* fall through */ + case FUTEX_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(cpu, uaddr), op, val, pts, g2h(cpu, uaddr2), - (base_op == FUTEX_CMP_REQUEUE - ? tswap32(val3) : val3)); + /* + * For these, the 4th argument is not TIMEOUT, but VAL2. + * But the prototype of do_safe_futex takes a pointer, so + * insert casts to satisfy the compiler. We do not need + * to tswap VAL2 since it's not compared to guest memory. + */ + pts = (struct timespec *)(uintptr_t)timeout; + timeout = 0; + haddr2 = g2h(cpu, uaddr2); + break; default: return -TARGET_ENOSYS; } -} -#endif - -#if defined(TARGET_NR_futex_time64) -static int do_futex_time64(CPUState *cpu, 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; - if (target_to_host_timespec64(pts, timeout)) { - return -TARGET_EFAULT; - } - } else { - pts = NULL; + if (timeout) { + pts = &ts; + if (time64 + ? target_to_host_timespec64(pts, timeout) + : target_to_host_timespec(pts, timeout)) { + return -TARGET_EFAULT; } - return do_safe_futex(g2h(cpu, uaddr), op, - tswap32(val), pts, NULL, val3); - case FUTEX_WAKE: - return do_safe_futex(g2h(cpu, uaddr), op, val, NULL, NULL, 0); - case FUTEX_FD: - return do_safe_futex(g2h(cpu, 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(cpu, uaddr), op, val, pts, g2h(cpu, uaddr2), - (base_op == FUTEX_CMP_REQUEUE - ? tswap32(val3) : val3)); - default: - return -TARGET_ENOSYS; } + return do_safe_futex(g2h(cpu, uaddr), op, val, pts, haddr2, val3); } #endif @@ -8486,7 +8478,7 @@ int host_to_target_waitstatus(int status) static int open_self_cmdline(CPUArchState *cpu_env, int fd) { CPUState *cpu = env_cpu(cpu_env); - struct linux_binprm *bprm = ((TaskState *)cpu->opaque)->bprm; + struct linux_binprm *bprm = get_task_state(cpu)->bprm; int i; for (i = 0; i < bprm->argc; i++) { @@ -8500,72 +8492,193 @@ static int open_self_cmdline(CPUArchState *cpu_env, int fd) return 0; } -static int open_self_maps(CPUArchState *cpu_env, int fd) -{ - CPUState *cpu = env_cpu(cpu_env); - TaskState *ts = cpu->opaque; - GSList *map_info = read_self_maps(); - GSList *s; - int count; +struct open_self_maps_data { + TaskState *ts; + IntervalTreeRoot *host_maps; + int fd; + bool smaps; +}; - for (s = map_info; s; s = g_slist_next(s)) { - MapInfo *e = (MapInfo *) s->data; +/* + * Subroutine to output one line of /proc/self/maps, + * or one region of /proc/self/smaps. + */ - if (h2g_valid(e->start)) { - unsigned long min = e->start; - unsigned long max = e->end; - int flags = page_get_flags(h2g(min)); - const char *path; - - max = h2g_valid(max - 1) ? - max : (uintptr_t) g2h_untagged(GUEST_ADDR_MAX) + 1; - - if (page_check_range(h2g(min), max - min, flags) == -1) { - continue; - } - - if (h2g(min) == ts->info->stack_limit) { - 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, - (flags & PAGE_READ) ? 'r' : '-', - (flags & PAGE_WRITE_ORG) ? 'w' : '-', - (flags & PAGE_EXEC) ? 'x' : '-', - e->is_priv ? 'p' : 's', - (uint64_t) e->offset, e->dev, e->inode); - if (path) { - dprintf(fd, "%*s%s\n", 73 - count, "", path); - } else { - dprintf(fd, "\n"); - } - } - } - - 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]"); +#ifdef TARGET_HPPA +# define test_stack(S, E, L) (E == L) +#else +# define test_stack(S, E, L) (S == L) #endif +static void open_self_maps_4(const struct open_self_maps_data *d, + const MapInfo *mi, abi_ptr start, + abi_ptr end, unsigned flags) +{ + const struct image_info *info = d->ts->info; + const char *path = mi->path; + uint64_t offset; + int fd = d->fd; + int count; + + if (test_stack(start, end, info->stack_limit)) { + path = "[stack]"; + } else if (start == info->brk) { + path = "[heap]"; + } else if (start == info->vdso) { + path = "[vdso]"; +#ifdef TARGET_X86_64 + } else if (start == TARGET_VSYSCALL_PAGE) { + path = "[vsyscall]"; +#endif + } + + /* Except null device (MAP_ANON), adjust offset for this fragment. */ + offset = mi->offset; + if (mi->dev) { + uintptr_t hstart = (uintptr_t)g2h_untagged(start); + offset += hstart - mi->itree.start; + } + + count = dprintf(fd, TARGET_ABI_FMT_ptr "-" TARGET_ABI_FMT_ptr + " %c%c%c%c %08" PRIx64 " %02x:%02x %"PRId64, + start, end, + (flags & PAGE_READ) ? 'r' : '-', + (flags & PAGE_WRITE_ORG) ? 'w' : '-', + (flags & PAGE_EXEC) ? 'x' : '-', + mi->is_priv ? 'p' : 's', + offset, major(mi->dev), minor(mi->dev), + (uint64_t)mi->inode); + if (path) { + dprintf(fd, "%*s%s\n", 73 - count, "", path); + } else { + dprintf(fd, "\n"); + } + + if (d->smaps) { + unsigned long size = end - start; + unsigned long page_size_kb = TARGET_PAGE_SIZE >> 10; + unsigned long size_kb = size >> 10; + + dprintf(fd, "Size: %lu kB\n" + "KernelPageSize: %lu kB\n" + "MMUPageSize: %lu kB\n" + "Rss: 0 kB\n" + "Pss: 0 kB\n" + "Pss_Dirty: 0 kB\n" + "Shared_Clean: 0 kB\n" + "Shared_Dirty: 0 kB\n" + "Private_Clean: 0 kB\n" + "Private_Dirty: 0 kB\n" + "Referenced: 0 kB\n" + "Anonymous: %lu kB\n" + "LazyFree: 0 kB\n" + "AnonHugePages: 0 kB\n" + "ShmemPmdMapped: 0 kB\n" + "FilePmdMapped: 0 kB\n" + "Shared_Hugetlb: 0 kB\n" + "Private_Hugetlb: 0 kB\n" + "Swap: 0 kB\n" + "SwapPss: 0 kB\n" + "Locked: 0 kB\n" + "THPeligible: 0\n" + "VmFlags:%s%s%s%s%s%s%s%s\n", + size_kb, page_size_kb, page_size_kb, + (flags & PAGE_ANON ? size_kb : 0), + (flags & PAGE_READ) ? " rd" : "", + (flags & PAGE_WRITE_ORG) ? " wr" : "", + (flags & PAGE_EXEC) ? " ex" : "", + mi->is_priv ? "" : " sh", + (flags & PAGE_READ) ? " mr" : "", + (flags & PAGE_WRITE_ORG) ? " mw" : "", + (flags & PAGE_EXEC) ? " me" : "", + mi->is_priv ? "" : " ms"); + } +} + +/* + * Callback for walk_memory_regions, when read_self_maps() fails. + * Proceed without the benefit of host /proc/self/maps cross-check. + */ +static int open_self_maps_3(void *opaque, target_ulong guest_start, + target_ulong guest_end, unsigned long flags) +{ + static const MapInfo mi = { .is_priv = true }; + + open_self_maps_4(opaque, &mi, guest_start, guest_end, flags); return 0; } +/* + * Callback for walk_memory_regions, when read_self_maps() succeeds. + */ +static int open_self_maps_2(void *opaque, target_ulong guest_start, + target_ulong guest_end, unsigned long flags) +{ + const struct open_self_maps_data *d = opaque; + uintptr_t host_start = (uintptr_t)g2h_untagged(guest_start); + uintptr_t host_last = (uintptr_t)g2h_untagged(guest_end - 1); + +#ifdef TARGET_X86_64 + /* + * Because of the extremely high position of the page within the guest + * virtual address space, this is not backed by host memory at all. + * Therefore the loop below would fail. This is the only instance + * of not having host backing memory. + */ + if (guest_start == TARGET_VSYSCALL_PAGE) { + return open_self_maps_3(opaque, guest_start, guest_end, flags); + } +#endif + + while (1) { + IntervalTreeNode *n = + interval_tree_iter_first(d->host_maps, host_start, host_start); + MapInfo *mi = container_of(n, MapInfo, itree); + uintptr_t this_hlast = MIN(host_last, n->last); + target_ulong this_gend = h2g(this_hlast) + 1; + + open_self_maps_4(d, mi, guest_start, this_gend, flags); + + if (this_hlast == host_last) { + return 0; + } + host_start = this_hlast + 1; + guest_start = h2g(host_start); + } +} + +static int open_self_maps_1(CPUArchState *env, int fd, bool smaps) +{ + struct open_self_maps_data d = { + .ts = env_cpu(env)->opaque, + .host_maps = read_self_maps(), + .fd = fd, + .smaps = smaps + }; + + if (d.host_maps) { + walk_memory_regions(&d, open_self_maps_2); + free_self_maps(d.host_maps); + } else { + walk_memory_regions(&d, open_self_maps_3); + } + return 0; +} + +static int open_self_maps(CPUArchState *cpu_env, int fd) +{ + return open_self_maps_1(cpu_env, fd, false); +} + +static int open_self_smaps(CPUArchState *cpu_env, int fd) +{ + return open_self_maps_1(cpu_env, fd, true); +} + static int open_self_stat(CPUArchState *cpu_env, int fd) { CPUState *cpu = env_cpu(cpu_env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); g_autoptr(GString) buf = g_string_new(NULL); int i; @@ -8578,6 +8691,9 @@ static int open_self_stat(CPUArchState *cpu_env, int fd) 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 == 2) { + /* task state */ + g_string_assign(buf, "R "); /* we are running right now */ } else if (i == 3) { /* ppid */ g_string_printf(buf, FMT_pid " ", getppid()); @@ -8603,7 +8719,7 @@ static int open_self_stat(CPUArchState *cpu_env, int fd) static int open_self_auxv(CPUArchState *cpu_env, int fd) { CPUState *cpu = env_cpu(cpu_env); - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); abi_ulong auxv = ts->info->saved_auxv; abi_ulong len = ts->info->auxv_len; char *ptr; @@ -8654,8 +8770,38 @@ static int is_proc_myself(const char *filename, const char *entry) return 0; } +static void excp_dump_file(FILE *logfile, CPUArchState *env, + const char *fmt, int code) +{ + if (logfile) { + CPUState *cs = env_cpu(env); + + fprintf(logfile, fmt, code); + fprintf(logfile, "Failing executable: %s\n", exec_path); + cpu_dump_state(cs, logfile, 0); + open_self_maps(env, fileno(logfile)); + } +} + +void target_exception_dump(CPUArchState *env, const char *fmt, int code) +{ + /* dump to console */ + excp_dump_file(stderr, env, fmt, code); + + /* dump to log file */ + if (qemu_log_separate()) { + FILE *logfile = qemu_log_trylock(); + + excp_dump_file(logfile, env, fmt, code); + qemu_log_unlock(logfile); + } +} + +#include "target_proc.h" + #if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN || \ - defined(TARGET_SPARC) || defined(TARGET_M68K) || defined(TARGET_HPPA) + defined(HAVE_ARCH_PROC_CPUINFO) || \ + defined(HAVE_ARCH_PROC_HARDWARE) static int is_proc(const char *filename, const char *entry) { return strcmp(filename, entry) == 0; @@ -8707,36 +8853,11 @@ static int open_net_route(CPUArchState *cpu_env, int fd) } #endif -#if defined(TARGET_SPARC) -static int open_cpuinfo(CPUArchState *cpu_env, int fd) -{ - dprintf(fd, "type\t\t: sun4u\n"); - return 0; -} -#endif - -#if defined(TARGET_HPPA) -static int open_cpuinfo(CPUArchState *cpu_env, int fd) -{ - dprintf(fd, "cpu family\t: PA-RISC 1.1e\n"); - dprintf(fd, "cpu\t\t: PA7300LC (PCX-L2)\n"); - dprintf(fd, "capabilities\t: os32\n"); - dprintf(fd, "model\t\t: 9000/778/B160L\n"); - dprintf(fd, "model name\t: Merlin L2 160 QEMU (9000/778/B160L)\n"); - return 0; -} -#endif - -#if defined(TARGET_M68K) -static int open_hardware(CPUArchState *cpu_env, int fd) -{ - dprintf(fd, "Model:\t\tqemu-m68k\n"); - return 0; -} -#endif - -static int do_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int flags, mode_t mode) +int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *fname, + int flags, mode_t mode, bool safe) { + g_autofree char *proc_name = NULL; + const char *pathname; struct fake_open { const char *filename; int (*fill)(CPUArchState *cpu_env, int fd); @@ -8745,24 +8866,36 @@ static int do_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int const struct fake_open *fake_open; static const struct fake_open fakes[] = { { "maps", open_self_maps, is_proc_myself }, + { "smaps", open_self_smaps, is_proc_myself }, { "stat", open_self_stat, is_proc_myself }, { "auxv", open_self_auxv, is_proc_myself }, { "cmdline", open_self_cmdline, is_proc_myself }, #if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN { "/proc/net/route", open_net_route, is_proc }, #endif -#if defined(TARGET_SPARC) || defined(TARGET_HPPA) +#if defined(HAVE_ARCH_PROC_CPUINFO) { "/proc/cpuinfo", open_cpuinfo, is_proc }, #endif -#if defined(TARGET_M68K) +#if defined(HAVE_ARCH_PROC_HARDWARE) { "/proc/hardware", open_hardware, is_proc }, #endif { NULL, NULL, NULL } }; + /* if this is a file from /proc/ filesystem, expand full name */ + proc_name = realpath(fname, NULL); + if (proc_name && strncmp(proc_name, "/proc/", 6) == 0) { + pathname = proc_name; + } else { + pathname = fname; + } + if (is_proc_myself(pathname, "exe")) { - int execfd = qemu_getauxval(AT_EXECFD); - return execfd ? execfd : safe_openat(dirfd, exec_path, flags, mode); + if (safe) { + return safe_openat(dirfd, exec_path, flags, mode); + } else { + return openat(dirfd, exec_path, flags, mode); + } } for (fake_open = fakes; fake_open->filename; fake_open++) { @@ -8776,16 +8909,22 @@ static int do_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int char filename[PATH_MAX]; int fd, r; - /* create temporary file to map stat to */ - tmpdir = getenv("TMPDIR"); - if (!tmpdir) - tmpdir = "/tmp"; - snprintf(filename, sizeof(filename), "%s/qemu-open.XXXXXX", tmpdir); - fd = mkstemp(filename); + fd = memfd_create("qemu-open", 0); if (fd < 0) { - return fd; + if (errno != ENOSYS) { + return fd; + } + /* create temporary file to map stat to */ + tmpdir = getenv("TMPDIR"); + if (!tmpdir) + tmpdir = "/tmp"; + snprintf(filename, sizeof(filename), "%s/qemu-open.XXXXXX", tmpdir); + fd = mkstemp(filename); + if (fd < 0) { + return fd; + } + unlink(filename); } - unlink(filename); if ((r = fake_open->fill(cpu_env, fd))) { int e = errno; @@ -8798,7 +8937,157 @@ static int do_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int return fd; } - return safe_openat(dirfd, path(pathname), flags, mode); + if (safe) { + return safe_openat(dirfd, path(pathname), flags, mode); + } else { + return openat(dirfd, path(pathname), flags, mode); + } +} + +ssize_t do_guest_readlink(const char *pathname, char *buf, size_t bufsiz) +{ + ssize_t ret; + + if (!pathname || !buf) { + errno = EFAULT; + return -1; + } + + if (!bufsiz) { + /* Short circuit this for the magic exe check. */ + errno = EINVAL; + return -1; + } + + if (is_proc_myself((const char *)pathname, "exe")) { + /* + * Don't worry about sign mismatch as earlier mapping + * logic would have thrown a bad address error. + */ + ret = MIN(strlen(exec_path), bufsiz); + /* We cannot NUL terminate the string. */ + memcpy(buf, exec_path, ret); + } else { + ret = readlink(path(pathname), buf, bufsiz); + } + + return ret; +} + +static int do_execv(CPUArchState *cpu_env, int dirfd, + abi_long pathname, abi_long guest_argp, + abi_long guest_envp, int flags, bool is_execveat) +{ + int ret; + char **argp, **envp; + int argc, envc; + abi_ulong gp; + abi_ulong addr; + char **q; + void *p; + + argc = 0; + + for (gp = guest_argp; gp; gp += sizeof(abi_ulong)) { + if (get_user_ual(addr, gp)) { + return -TARGET_EFAULT; + } + if (!addr) { + break; + } + argc++; + } + envc = 0; + for (gp = guest_envp; gp; gp += sizeof(abi_ulong)) { + if (get_user_ual(addr, gp)) { + return -TARGET_EFAULT; + } + if (!addr) { + break; + } + envc++; + } + + argp = g_new0(char *, argc + 1); + envp = g_new0(char *, envc + 1); + + for (gp = guest_argp, q = argp; gp; gp += sizeof(abi_ulong), q++) { + if (get_user_ual(addr, gp)) { + goto execve_efault; + } + if (!addr) { + break; + } + *q = lock_user_string(addr); + if (!*q) { + goto execve_efault; + } + } + *q = NULL; + + for (gp = guest_envp, q = envp; gp; gp += sizeof(abi_ulong), q++) { + if (get_user_ual(addr, gp)) { + goto execve_efault; + } + if (!addr) { + break; + } + *q = lock_user_string(addr); + if (!*q) { + goto execve_efault; + } + } + *q = NULL; + + /* + * Although execve() is not an interruptible syscall it is + * a special case where we must use the safe_syscall wrapper: + * if we allow a signal to happen before we make the host + * syscall then we will 'lose' it, because at the point of + * execve the process leaves QEMU's control. So we use the + * safe syscall wrapper to ensure that we either take the + * signal as a guest signal, or else it does not happen + * before the execve completes and makes it the other + * program's problem. + */ + p = lock_user_string(pathname); + if (!p) { + goto execve_efault; + } + + const char *exe = p; + if (is_proc_myself(p, "exe")) { + exe = exec_path; + } + ret = is_execveat + ? safe_execveat(dirfd, exe, argp, envp, flags) + : safe_execve(exe, argp, envp); + ret = get_errno(ret); + + unlock_user(p, pathname, 0); + + goto execve_end; + +execve_efault: + ret = -TARGET_EFAULT; + +execve_end: + for (gp = guest_argp, q = argp; *q; gp += sizeof(abi_ulong), q++) { + if (get_user_ual(addr, gp) || !addr) { + break; + } + unlock_user(*q, addr, 0); + } + for (gp = guest_envp, q = envp; *q; gp += sizeof(abi_ulong), q++) { + if (get_user_ual(addr, gp) || !addr) { + break; + } + unlock_user(*q, addr, 0); + } + + g_free(argp); + g_free(envp); + return ret; } #define TIMER_MAGIC 0x0caf0000 @@ -9042,10 +9331,271 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count) } #endif /* TARGET_NR_getdents64 */ +#if defined(TARGET_NR_riscv_hwprobe) + +#define RISCV_HWPROBE_KEY_MVENDORID 0 +#define RISCV_HWPROBE_KEY_MARCHID 1 +#define RISCV_HWPROBE_KEY_MIMPID 2 + +#define RISCV_HWPROBE_KEY_BASE_BEHAVIOR 3 +#define RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1 << 0) + +#define RISCV_HWPROBE_KEY_IMA_EXT_0 4 +#define RISCV_HWPROBE_IMA_FD (1 << 0) +#define RISCV_HWPROBE_IMA_C (1 << 1) +#define RISCV_HWPROBE_IMA_V (1 << 2) +#define RISCV_HWPROBE_EXT_ZBA (1 << 3) +#define RISCV_HWPROBE_EXT_ZBB (1 << 4) +#define RISCV_HWPROBE_EXT_ZBS (1 << 5) +#define RISCV_HWPROBE_EXT_ZICBOZ (1 << 6) +#define RISCV_HWPROBE_EXT_ZBC (1 << 7) +#define RISCV_HWPROBE_EXT_ZBKB (1 << 8) +#define RISCV_HWPROBE_EXT_ZBKC (1 << 9) +#define RISCV_HWPROBE_EXT_ZBKX (1 << 10) +#define RISCV_HWPROBE_EXT_ZKND (1 << 11) +#define RISCV_HWPROBE_EXT_ZKNE (1 << 12) +#define RISCV_HWPROBE_EXT_ZKNH (1 << 13) +#define RISCV_HWPROBE_EXT_ZKSED (1 << 14) +#define RISCV_HWPROBE_EXT_ZKSH (1 << 15) +#define RISCV_HWPROBE_EXT_ZKT (1 << 16) +#define RISCV_HWPROBE_EXT_ZVBB (1 << 17) +#define RISCV_HWPROBE_EXT_ZVBC (1 << 18) +#define RISCV_HWPROBE_EXT_ZVKB (1 << 19) +#define RISCV_HWPROBE_EXT_ZVKG (1 << 20) +#define RISCV_HWPROBE_EXT_ZVKNED (1 << 21) +#define RISCV_HWPROBE_EXT_ZVKNHA (1 << 22) +#define RISCV_HWPROBE_EXT_ZVKNHB (1 << 23) +#define RISCV_HWPROBE_EXT_ZVKSED (1 << 24) +#define RISCV_HWPROBE_EXT_ZVKSH (1 << 25) +#define RISCV_HWPROBE_EXT_ZVKT (1 << 26) +#define RISCV_HWPROBE_EXT_ZFH (1 << 27) +#define RISCV_HWPROBE_EXT_ZFHMIN (1 << 28) +#define RISCV_HWPROBE_EXT_ZIHINTNTL (1 << 29) +#define RISCV_HWPROBE_EXT_ZVFH (1 << 30) +#define RISCV_HWPROBE_EXT_ZVFHMIN (1 << 31) +#define RISCV_HWPROBE_EXT_ZFA (1ULL << 32) +#define RISCV_HWPROBE_EXT_ZTSO (1ULL << 33) +#define RISCV_HWPROBE_EXT_ZACAS (1ULL << 34) +#define RISCV_HWPROBE_EXT_ZICOND (1ULL << 35) + +#define RISCV_HWPROBE_KEY_CPUPERF_0 5 +#define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0) +#define RISCV_HWPROBE_MISALIGNED_EMULATED (1 << 0) +#define RISCV_HWPROBE_MISALIGNED_SLOW (2 << 0) +#define RISCV_HWPROBE_MISALIGNED_FAST (3 << 0) +#define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0) +#define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0) + +#define RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE 6 + +struct riscv_hwprobe { + abi_llong key; + abi_ullong value; +}; + +static void risc_hwprobe_fill_pairs(CPURISCVState *env, + struct riscv_hwprobe *pair, + size_t pair_count) +{ + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); + + for (; pair_count > 0; pair_count--, pair++) { + abi_llong key; + abi_ullong value; + __put_user(0, &pair->value); + __get_user(key, &pair->key); + switch (key) { + case RISCV_HWPROBE_KEY_MVENDORID: + __put_user(cfg->mvendorid, &pair->value); + break; + case RISCV_HWPROBE_KEY_MARCHID: + __put_user(cfg->marchid, &pair->value); + break; + case RISCV_HWPROBE_KEY_MIMPID: + __put_user(cfg->mimpid, &pair->value); + break; + case RISCV_HWPROBE_KEY_BASE_BEHAVIOR: + value = riscv_has_ext(env, RVI) && + riscv_has_ext(env, RVM) && + riscv_has_ext(env, RVA) ? + RISCV_HWPROBE_BASE_BEHAVIOR_IMA : 0; + __put_user(value, &pair->value); + break; + case RISCV_HWPROBE_KEY_IMA_EXT_0: + value = riscv_has_ext(env, RVF) && + riscv_has_ext(env, RVD) ? + RISCV_HWPROBE_IMA_FD : 0; + value |= riscv_has_ext(env, RVC) ? + RISCV_HWPROBE_IMA_C : 0; + value |= riscv_has_ext(env, RVV) ? + RISCV_HWPROBE_IMA_V : 0; + value |= cfg->ext_zba ? + RISCV_HWPROBE_EXT_ZBA : 0; + value |= cfg->ext_zbb ? + RISCV_HWPROBE_EXT_ZBB : 0; + value |= cfg->ext_zbs ? + RISCV_HWPROBE_EXT_ZBS : 0; + value |= cfg->ext_zicboz ? + RISCV_HWPROBE_EXT_ZICBOZ : 0; + value |= cfg->ext_zbc ? + RISCV_HWPROBE_EXT_ZBC : 0; + value |= cfg->ext_zbkb ? + RISCV_HWPROBE_EXT_ZBKB : 0; + value |= cfg->ext_zbkc ? + RISCV_HWPROBE_EXT_ZBKC : 0; + value |= cfg->ext_zbkx ? + RISCV_HWPROBE_EXT_ZBKX : 0; + value |= cfg->ext_zknd ? + RISCV_HWPROBE_EXT_ZKND : 0; + value |= cfg->ext_zkne ? + RISCV_HWPROBE_EXT_ZKNE : 0; + value |= cfg->ext_zknh ? + RISCV_HWPROBE_EXT_ZKNH : 0; + value |= cfg->ext_zksed ? + RISCV_HWPROBE_EXT_ZKSED : 0; + value |= cfg->ext_zksh ? + RISCV_HWPROBE_EXT_ZKSH : 0; + value |= cfg->ext_zkt ? + RISCV_HWPROBE_EXT_ZKT : 0; + value |= cfg->ext_zvbb ? + RISCV_HWPROBE_EXT_ZVBB : 0; + value |= cfg->ext_zvbc ? + RISCV_HWPROBE_EXT_ZVBC : 0; + value |= cfg->ext_zvkb ? + RISCV_HWPROBE_EXT_ZVKB : 0; + value |= cfg->ext_zvkg ? + RISCV_HWPROBE_EXT_ZVKG : 0; + value |= cfg->ext_zvkned ? + RISCV_HWPROBE_EXT_ZVKNED : 0; + value |= cfg->ext_zvknha ? + RISCV_HWPROBE_EXT_ZVKNHA : 0; + value |= cfg->ext_zvknhb ? + RISCV_HWPROBE_EXT_ZVKNHB : 0; + value |= cfg->ext_zvksed ? + RISCV_HWPROBE_EXT_ZVKSED : 0; + value |= cfg->ext_zvksh ? + RISCV_HWPROBE_EXT_ZVKSH : 0; + value |= cfg->ext_zvkt ? + RISCV_HWPROBE_EXT_ZVKT : 0; + value |= cfg->ext_zfh ? + RISCV_HWPROBE_EXT_ZFH : 0; + value |= cfg->ext_zfhmin ? + RISCV_HWPROBE_EXT_ZFHMIN : 0; + value |= cfg->ext_zihintntl ? + RISCV_HWPROBE_EXT_ZIHINTNTL : 0; + value |= cfg->ext_zvfh ? + RISCV_HWPROBE_EXT_ZVFH : 0; + value |= cfg->ext_zvfhmin ? + RISCV_HWPROBE_EXT_ZVFHMIN : 0; + value |= cfg->ext_zfa ? + RISCV_HWPROBE_EXT_ZFA : 0; + value |= cfg->ext_ztso ? + RISCV_HWPROBE_EXT_ZTSO : 0; + value |= cfg->ext_zacas ? + RISCV_HWPROBE_EXT_ZACAS : 0; + value |= cfg->ext_zicond ? + RISCV_HWPROBE_EXT_ZICOND : 0; + __put_user(value, &pair->value); + break; + case RISCV_HWPROBE_KEY_CPUPERF_0: + __put_user(RISCV_HWPROBE_MISALIGNED_FAST, &pair->value); + break; + case RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE: + value = cfg->ext_zicboz ? cfg->cboz_blocksize : 0; + __put_user(value, &pair->value); + break; + default: + __put_user(-1, &pair->key); + break; + } + } +} + +static int cpu_set_valid(abi_long arg3, abi_long arg4) +{ + int ret, i, tmp; + size_t host_mask_size, target_mask_size; + unsigned long *host_mask; + + /* + * cpu_set_t represent CPU masks as bit masks of type unsigned long *. + * arg3 contains the cpu count. + */ + tmp = (8 * sizeof(abi_ulong)); + target_mask_size = ((arg3 + tmp - 1) / tmp) * sizeof(abi_ulong); + host_mask_size = (target_mask_size + (sizeof(*host_mask) - 1)) & + ~(sizeof(*host_mask) - 1); + + host_mask = alloca(host_mask_size); + + ret = target_to_host_cpu_mask(host_mask, host_mask_size, + arg4, target_mask_size); + if (ret != 0) { + return ret; + } + + for (i = 0 ; i < host_mask_size / sizeof(*host_mask); i++) { + if (host_mask[i] != 0) { + return 0; + } + } + return -TARGET_EINVAL; +} + +static abi_long do_riscv_hwprobe(CPUArchState *cpu_env, abi_long arg1, + abi_long arg2, abi_long arg3, + abi_long arg4, abi_long arg5) +{ + int ret; + struct riscv_hwprobe *host_pairs; + + /* flags must be 0 */ + if (arg5 != 0) { + return -TARGET_EINVAL; + } + + /* check cpu_set */ + if (arg3 != 0) { + ret = cpu_set_valid(arg3, arg4); + if (ret != 0) { + return ret; + } + } else if (arg4 != 0) { + return -TARGET_EINVAL; + } + + /* no pairs */ + if (arg2 == 0) { + return 0; + } + + host_pairs = lock_user(VERIFY_WRITE, arg1, + sizeof(*host_pairs) * (size_t)arg2, 0); + if (host_pairs == NULL) { + return -TARGET_EFAULT; + } + risc_hwprobe_fill_pairs(cpu_env, host_pairs, arg2); + unlock_user(host_pairs, arg1, sizeof(*host_pairs) * (size_t)arg2); + return 0; +} +#endif /* TARGET_NR_riscv_hwprobe */ + #if defined(TARGET_NR_pivot_root) && defined(__NR_pivot_root) _syscall2(int, pivot_root, const char *, new_root, const char *, put_old) #endif +#if defined(TARGET_NR_open_tree) && defined(__NR_open_tree) +#define __NR_sys_open_tree __NR_open_tree +_syscall3(int, sys_open_tree, int, __dfd, const char *, __filename, + unsigned int, __flags) +#endif + +#if defined(TARGET_NR_move_mount) && defined(__NR_move_mount) +#define __NR_sys_move_mount __NR_move_mount +_syscall5(int, sys_move_mount, int, __from_dfd, const char *, __from_pathname, + int, __to_dfd, const char *, __to_pathname, unsigned int, flag) +#endif + /* This is an internal helper for do_syscall so that it is easier * to have a single return point, so that actions, such as logging * of syscall results, can be performed. @@ -9084,9 +9634,15 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, pthread_mutex_lock(&clone_lock); if (CPU_NEXT(first_cpu)) { - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); - object_property_set_bool(OBJECT(cpu), "realized", false, NULL); + if (ts->child_tidptr) { + put_user_u32(0, ts->child_tidptr); + do_sys_futex(g2h(cpu, ts->child_tidptr), + FUTEX_WAKE, INT_MAX, NULL, NULL, 0); + } + + object_unparent(OBJECT(cpu)); object_unref(OBJECT(cpu)); /* * At this point the CPU should be unrealized and removed @@ -9096,11 +9652,6 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, pthread_mutex_unlock(&clone_lock); - if (ts->child_tidptr) { - put_user_u32(0, ts->child_tidptr); - do_sys_futex(g2h(cpu, ts->child_tidptr), - FUTEX_WAKE, INT_MAX, NULL, NULL, 0); - } thread_cpu = NULL; g_free(ts); rcu_unregister_thread(); @@ -9149,9 +9700,9 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, case TARGET_NR_open: if (!(p = lock_user_string(arg1))) return -TARGET_EFAULT; - ret = get_errno(do_openat(cpu_env, AT_FDCWD, p, + ret = get_errno(do_guest_openat(cpu_env, AT_FDCWD, p, target_to_host_bitmask(arg2, fcntl_flags_tbl), - arg3)); + arg3, true)); fd_trans_unregister(ret); unlock_user(p, arg1, 0); return ret; @@ -9159,9 +9710,9 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, case TARGET_NR_openat: if (!(p = lock_user_string(arg2))) return -TARGET_EFAULT; - ret = get_errno(do_openat(cpu_env, arg1, p, + ret = get_errno(do_guest_openat(cpu_env, arg1, p, target_to_host_bitmask(arg3, fcntl_flags_tbl), - arg4)); + arg4, true)); fd_trans_unregister(ret); unlock_user(p, arg2, 0); return ret; @@ -9175,10 +9726,51 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, ret = do_open_by_handle_at(arg1, arg2, arg3); fd_trans_unregister(ret); return ret; +#endif +#if defined(__NR_pidfd_open) && defined(TARGET_NR_pidfd_open) + case TARGET_NR_pidfd_open: + return get_errno(pidfd_open(arg1, arg2)); +#endif +#if defined(__NR_pidfd_send_signal) && defined(TARGET_NR_pidfd_send_signal) + case TARGET_NR_pidfd_send_signal: + { + siginfo_t uinfo, *puinfo; + + if (arg3) { + p = lock_user(VERIFY_READ, arg3, sizeof(target_siginfo_t), 1); + if (!p) { + return -TARGET_EFAULT; + } + target_to_host_siginfo(&uinfo, p); + unlock_user(p, arg3, 0); + puinfo = &uinfo; + } else { + puinfo = NULL; + } + ret = get_errno(pidfd_send_signal(arg1, target_to_host_signal(arg2), + puinfo, arg4)); + } + return ret; +#endif +#if defined(__NR_pidfd_getfd) && defined(TARGET_NR_pidfd_getfd) + case TARGET_NR_pidfd_getfd: + return get_errno(pidfd_getfd(arg1, arg2, arg3)); #endif case TARGET_NR_close: fd_trans_unregister(arg1); return get_errno(close(arg1)); +#if defined(__NR_close_range) && defined(TARGET_NR_close_range) + case TARGET_NR_close_range: + ret = get_errno(sys_close_range(arg1, arg2, arg3)); + if (ret == 0 && !(arg3 & CLOSE_RANGE_CLOEXEC)) { + abi_long fd, maxfd; + maxfd = MIN(arg2, target_fd_max); + for (fd = arg1; fd < maxfd; fd++) { + fd_trans_unregister(fd); + } + } + return ret; +#endif case TARGET_NR_brk: return do_brk(arg1); @@ -9200,14 +9792,24 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_waitid case TARGET_NR_waitid: { + struct rusage ru; siginfo_t info; - info.si_pid = 0; - ret = get_errno(safe_waitid(arg1, arg2, &info, arg4, NULL)); - if (!is_error(ret) && arg3 && info.si_pid != 0) { - if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_siginfo_t), 0))) + + ret = get_errno(safe_waitid(arg1, arg2, (arg3 ? &info : NULL), + arg4, (arg5 ? &ru : NULL))); + if (!is_error(ret)) { + if (arg3) { + p = lock_user(VERIFY_WRITE, arg3, + sizeof(target_siginfo_t), 0); + if (!p) { + return -TARGET_EFAULT; + } + host_to_target_siginfo(p, &info); + unlock_user(p, arg3, sizeof(target_siginfo_t)); + } + if (arg5 && host_to_target_rusage(arg5, &ru)) { return -TARGET_EFAULT; - host_to_target_siginfo(p, &info); - unlock_user(p, arg3, sizeof(target_siginfo_t)); + } } } return ret; @@ -9269,100 +9871,10 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, unlock_user(p, arg2, 0); return ret; #endif + case TARGET_NR_execveat: + return do_execv(cpu_env, arg1, arg2, arg3, arg4, arg5, true); case TARGET_NR_execve: - { - char **argp, **envp; - int argc, envc; - abi_ulong gp; - abi_ulong guest_argp; - abi_ulong guest_envp; - abi_ulong addr; - char **q; - - argc = 0; - guest_argp = arg2; - for (gp = guest_argp; gp; gp += sizeof(abi_ulong)) { - if (get_user_ual(addr, gp)) - return -TARGET_EFAULT; - if (!addr) - break; - argc++; - } - envc = 0; - guest_envp = arg3; - for (gp = guest_envp; gp; gp += sizeof(abi_ulong)) { - if (get_user_ual(addr, gp)) - return -TARGET_EFAULT; - if (!addr) - break; - envc++; - } - - argp = g_new0(char *, argc + 1); - envp = g_new0(char *, envc + 1); - - for (gp = guest_argp, q = argp; gp; - gp += sizeof(abi_ulong), q++) { - if (get_user_ual(addr, gp)) - goto execve_efault; - if (!addr) - break; - if (!(*q = lock_user_string(addr))) - goto execve_efault; - } - *q = NULL; - - for (gp = guest_envp, q = envp; gp; - gp += sizeof(abi_ulong), q++) { - if (get_user_ual(addr, gp)) - goto execve_efault; - if (!addr) - break; - if (!(*q = lock_user_string(addr))) - goto execve_efault; - } - *q = NULL; - - if (!(p = lock_user_string(arg1))) - goto execve_efault; - /* Although execve() is not an interruptible syscall it is - * a special case where we must use the safe_syscall wrapper: - * if we allow a signal to happen before we make the host - * syscall then we will 'lose' it, because at the point of - * execve the process leaves QEMU's control. So we use the - * safe syscall wrapper to ensure that we either take the - * signal as a guest signal, or else it does not happen - * before the execve completes and makes it the other - * program's problem. - */ - ret = get_errno(safe_execve(p, argp, envp)); - unlock_user(p, arg1, 0); - - goto execve_end; - - execve_efault: - ret = -TARGET_EFAULT; - - execve_end: - for (gp = guest_argp, q = argp; *q; - gp += sizeof(abi_ulong), q++) { - if (get_user_ual(addr, gp) - || !addr) - break; - unlock_user(*q, addr, 0); - } - for (gp = guest_envp, q = envp; *q; - gp += sizeof(abi_ulong), q++) { - if (get_user_ual(addr, gp) - || !addr) - break; - unlock_user(*q, addr, 0); - } - - g_free(argp); - g_free(envp); - } - return ret; + return do_execv(cpu_env, AT_FDCWD, arg1, arg2, arg3, 0, false); case TARGET_NR_chdir: if (!(p = lock_user_string(arg1))) return -TARGET_EFAULT; @@ -9487,6 +9999,60 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); return ret; #endif +#if defined(TARGET_NR_move_mount) && defined(__NR_move_mount) + case TARGET_NR_move_mount: + { + void *p2, *p4; + + if (!arg2 || !arg4) { + return -TARGET_EFAULT; + } + + p2 = lock_user_string(arg2); + if (!p2) { + return -TARGET_EFAULT; + } + + p4 = lock_user_string(arg4); + if (!p4) { + unlock_user(p2, arg2, 0); + return -TARGET_EFAULT; + } + ret = get_errno(sys_move_mount(arg1, p2, arg3, p4, arg5)); + + unlock_user(p2, arg2, 0); + unlock_user(p4, arg4, 0); + + return ret; + } +#endif +#if defined(TARGET_NR_open_tree) && defined(__NR_open_tree) + case TARGET_NR_open_tree: + { + void *p2; + int host_flags; + + if (!arg2) { + return -TARGET_EFAULT; + } + + p2 = lock_user_string(arg2); + if (!p2) { + return -TARGET_EFAULT; + } + + host_flags = arg3 & ~TARGET_O_CLOEXEC; + if (arg3 & TARGET_O_CLOEXEC) { + host_flags |= O_CLOEXEC; + } + + ret = get_errno(sys_open_tree(arg1, p2, host_flags)); + + unlock_user(p2, arg2, 0); + + return ret; + } +#endif #ifdef TARGET_NR_stime /* not on alpha */ case TARGET_NR_stime: { @@ -9505,7 +10071,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_pause /* not on alpha */ case TARGET_NR_pause: if (!block_signals()) { - sigsuspend(&((TaskState *)cpu->opaque)->signal_mask); + sigsuspend(&get_task_state(cpu)->signal_mask); } return -TARGET_EINTR; #endif @@ -9590,6 +10156,15 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, unlock_user(p, arg2, 0); return ret; #endif +#if defined(TARGET_NR_faccessat2) + case TARGET_NR_faccessat2: + if (!(p = lock_user_string(arg2))) { + return -TARGET_EFAULT; + } + ret = get_errno(faccessat(arg1, p, arg3, arg4)); + unlock_user(p, arg2, 0); + return ret; +#endif #ifdef TARGET_NR_nice /* not on alpha */ case TARGET_NR_nice: return get_errno(nice(arg1)); @@ -10062,7 +10637,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, sigset_t *set; #if defined(TARGET_ALPHA) - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); /* target_to_host_old_sigset will bswap back */ abi_ulong mask = tswapal(arg1); set = &ts->sigsuspend_mask; @@ -10182,7 +10757,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, } target_to_host_siginfo(&uinfo, p); unlock_user(p, arg3, 0); - ret = get_errno(sys_rt_sigqueueinfo(arg1, arg2, &uinfo)); + ret = get_errno(sys_rt_sigqueueinfo(arg1, target_to_host_signal(arg2), &uinfo)); } return ret; case TARGET_NR_rt_tgsigqueueinfo: @@ -10195,7 +10770,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, } target_to_host_siginfo(&uinfo, p); unlock_user(p, arg4, 0); - ret = get_errno(sys_rt_tgsigqueueinfo(arg1, arg2, arg3, &uinfo)); + ret = get_errno(sys_rt_tgsigqueueinfo(arg1, arg2, target_to_host_signal(arg3), &uinfo)); } return ret; #ifdef TARGET_NR_sigreturn @@ -10370,27 +10945,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, void *p2; p = lock_user_string(arg1); p2 = lock_user(VERIFY_WRITE, arg2, arg3, 0); - if (!p || !p2) { - ret = -TARGET_EFAULT; - } else if (!arg3) { - /* Short circuit this for the magic exe check. */ - ret = -TARGET_EINVAL; - } else if (is_proc_myself((const char *)p, "exe")) { - char real[PATH_MAX], *temp; - temp = realpath(exec_path, real); - /* Return value is # of bytes that we wrote to the buffer. */ - if (temp == NULL) { - ret = get_errno(-1); - } else { - /* Don't worry about sign mismatch as earlier mapping - * logic would have thrown a bad address error. */ - ret = MIN(strlen(real), arg3); - /* We cannot NUL terminate the string. */ - memcpy(p2, real, ret); - } - } else { - ret = get_errno(readlink(path(p), p2, arg3)); - } + ret = get_errno(do_guest_readlink(p, p2, arg3)); unlock_user(p2, arg2, ret); unlock_user(p, arg1, 0); } @@ -10404,11 +10959,17 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, p2 = lock_user(VERIFY_WRITE, arg3, arg4, 0); if (!p || !p2) { ret = -TARGET_EFAULT; + } else if (!arg4) { + /* Short circuit this for the magic exe check. */ + ret = -TARGET_EINVAL; } else if (is_proc_myself((const char *)p, "exe")) { - char real[PATH_MAX], *temp; - temp = realpath(exec_path, real); - ret = temp == NULL ? get_errno(-1) : strlen(real) ; - snprintf((char *)p2, arg4, "%s", real); + /* + * Don't worry about sign mismatch as earlier mapping + * logic would have thrown a bad address error. + */ + ret = MIN(strlen(exec_path), arg4); + /* We cannot NUL terminate the string. */ + memcpy(p2, exec_path, ret); } else { ret = get_errno(readlinkat(arg1, path(p), p2, arg4)); } @@ -10456,28 +11017,20 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, v5 = tswapal(v[4]); v6 = tswapal(v[5]); unlock_user(v, arg1, 0); - ret = get_errno(target_mmap(v1, v2, v3, - target_to_host_bitmask(v4, mmap_flags_tbl), - v5, v6)); + return do_mmap(v1, v2, v3, v4, v5, v6); } #else /* mmap pointers are always untagged */ - ret = get_errno(target_mmap(arg1, arg2, arg3, - target_to_host_bitmask(arg4, mmap_flags_tbl), - arg5, - arg6)); + return do_mmap(arg1, arg2, arg3, arg4, arg5, arg6); #endif - return ret; #endif #ifdef TARGET_NR_mmap2 case TARGET_NR_mmap2: #ifndef MMAP_SHIFT #define MMAP_SHIFT 12 #endif - ret = target_mmap(arg1, arg2, arg3, - target_to_host_bitmask(arg4, mmap_flags_tbl), - arg5, arg6 << MMAP_SHIFT); - return get_errno(ret); + return do_mmap(arg1, arg2, arg3, arg4, arg5, + (off_t)(abi_ulong)arg6 << MMAP_SHIFT); #endif case TARGET_NR_munmap: arg1 = cpu_untagged_addr(cpu, arg1); @@ -10485,7 +11038,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, case TARGET_NR_mprotect: arg1 = cpu_untagged_addr(cpu, arg1); { - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); /* Special hack to detect libc making the stack executable. */ if ((arg3 & PROT_GROWSDOWN) && arg1 >= ts->info->stack_limit @@ -10505,7 +11058,8 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, /* ??? msync/mlock/munlock are broken for softmmu. */ #ifdef TARGET_NR_msync case TARGET_NR_msync: - return get_errno(msync(g2h(cpu, arg1), arg2, arg3)); + return get_errno(msync(g2h(cpu, arg1), arg2, + target_to_host_msync_arg(arg3))); #endif #ifdef TARGET_NR_mlock case TARGET_NR_mlock: @@ -10987,11 +11541,11 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_shmat case TARGET_NR_shmat: - return do_shmat(cpu_env, arg1, arg2, arg3); + return target_shmat(cpu_env, arg1, arg2, arg3); #endif #ifdef TARGET_NR_shmdt case TARGET_NR_shmdt: - return do_shmdt(arg1); + return target_shmdt(arg1); #endif case TARGET_NR_fsync: return get_errno(fsync(arg1)); @@ -11074,16 +11628,14 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #if defined(TARGET_NR_clock_adjtime) && defined(CONFIG_CLOCK_ADJTIME) case TARGET_NR_clock_adjtime: { - struct timex htx, *phtx = &htx; + struct timex htx; - if (target_to_host_timex(phtx, arg2) != 0) { + if (target_to_host_timex(&htx, arg2) != 0) { return -TARGET_EFAULT; } - ret = get_errno(clock_adjtime(arg1, phtx)); - if (!is_error(ret) && phtx) { - if (host_to_target_timex(arg2, phtx) != 0) { - return -TARGET_EFAULT; - } + ret = get_errno(clock_adjtime(arg1, &htx)); + if (!is_error(ret) && host_to_target_timex(arg2, &htx)) { + return -TARGET_EFAULT; } } return ret; @@ -11281,14 +11833,14 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, } case TARGET_NR_getcpu: { - unsigned cpu, node; - ret = get_errno(sys_getcpu(arg1 ? &cpu : NULL, + unsigned cpuid, node; + ret = get_errno(sys_getcpu(arg1 ? &cpuid : NULL, arg2 ? &node : NULL, NULL)); if (is_error(ret)) { return ret; } - if (arg1 && put_user_u32(cpu, arg1)) { + if (arg1 && put_user_u32(cpuid, arg1)) { return -TARGET_EFAULT; } if (arg2 && put_user_u32(node, arg2)) { @@ -11809,42 +12361,61 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, case TARGET_NR_setregid: return get_errno(setregid(low2highgid(arg1), low2highgid(arg2))); case TARGET_NR_getgroups: - { + { /* the same code as for TARGET_NR_getgroups32 */ int gidsetsize = arg1; target_id *target_grouplist; - gid_t *grouplist; + g_autofree gid_t *grouplist = NULL; int i; - grouplist = alloca(gidsetsize * sizeof(gid_t)); - ret = get_errno(getgroups(gidsetsize, grouplist)); - if (gidsetsize == 0) - return ret; - if (!is_error(ret)) { - target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * sizeof(target_id), 0); - if (!target_grouplist) - return -TARGET_EFAULT; - for(i = 0;i < ret; i++) - target_grouplist[i] = tswapid(high2lowgid(grouplist[i])); - unlock_user(target_grouplist, arg2, gidsetsize * sizeof(target_id)); + if (gidsetsize > NGROUPS_MAX || gidsetsize < 0) { + return -TARGET_EINVAL; } + if (gidsetsize > 0) { + grouplist = g_try_new(gid_t, gidsetsize); + if (!grouplist) { + return -TARGET_ENOMEM; + } + } + ret = get_errno(getgroups(gidsetsize, grouplist)); + if (!is_error(ret) && gidsetsize > 0) { + target_grouplist = lock_user(VERIFY_WRITE, arg2, + gidsetsize * sizeof(target_id), 0); + if (!target_grouplist) { + return -TARGET_EFAULT; + } + for (i = 0; i < ret; i++) { + target_grouplist[i] = tswapid(high2lowgid(grouplist[i])); + } + unlock_user(target_grouplist, arg2, + gidsetsize * sizeof(target_id)); + } + return ret; } - return ret; case TARGET_NR_setgroups: - { + { /* the same code as for TARGET_NR_setgroups32 */ int gidsetsize = arg1; target_id *target_grouplist; - gid_t *grouplist = NULL; + g_autofree gid_t *grouplist = NULL; int i; - if (gidsetsize) { - grouplist = alloca(gidsetsize * sizeof(gid_t)); - target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * sizeof(target_id), 1); + + if (gidsetsize > NGROUPS_MAX || gidsetsize < 0) { + return -TARGET_EINVAL; + } + if (gidsetsize > 0) { + grouplist = g_try_new(gid_t, gidsetsize); + if (!grouplist) { + return -TARGET_ENOMEM; + } + target_grouplist = lock_user(VERIFY_READ, arg2, + gidsetsize * sizeof(target_id), 1); if (!target_grouplist) { return -TARGET_EFAULT; } for (i = 0; i < gidsetsize; i++) { grouplist[i] = low2highgid(tswapid(target_grouplist[i])); } - unlock_user(target_grouplist, arg2, 0); + unlock_user(target_grouplist, arg2, + gidsetsize * sizeof(target_id)); } return get_errno(setgroups(gidsetsize, grouplist)); } @@ -12126,44 +12697,62 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_getgroups32 case TARGET_NR_getgroups32: - { + { /* the same code as for TARGET_NR_getgroups */ int gidsetsize = arg1; uint32_t *target_grouplist; - gid_t *grouplist; + g_autofree gid_t *grouplist = NULL; int i; - grouplist = alloca(gidsetsize * sizeof(gid_t)); + if (gidsetsize > NGROUPS_MAX || gidsetsize < 0) { + return -TARGET_EINVAL; + } + if (gidsetsize > 0) { + grouplist = g_try_new(gid_t, gidsetsize); + if (!grouplist) { + return -TARGET_ENOMEM; + } + } ret = get_errno(getgroups(gidsetsize, grouplist)); - if (gidsetsize == 0) - return ret; - if (!is_error(ret)) { - target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * 4, 0); + if (!is_error(ret) && gidsetsize > 0) { + target_grouplist = lock_user(VERIFY_WRITE, arg2, + gidsetsize * 4, 0); if (!target_grouplist) { return -TARGET_EFAULT; } - for(i = 0;i < ret; i++) + for (i = 0; i < ret; i++) { target_grouplist[i] = tswap32(grouplist[i]); + } unlock_user(target_grouplist, arg2, gidsetsize * 4); } + return ret; } - return ret; #endif #ifdef TARGET_NR_setgroups32 case TARGET_NR_setgroups32: - { + { /* the same code as for TARGET_NR_setgroups */ int gidsetsize = arg1; uint32_t *target_grouplist; - gid_t *grouplist; + g_autofree gid_t *grouplist = NULL; int i; - grouplist = alloca(gidsetsize * sizeof(gid_t)); - target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * 4, 1); - if (!target_grouplist) { - return -TARGET_EFAULT; + if (gidsetsize > NGROUPS_MAX || gidsetsize < 0) { + return -TARGET_EINVAL; + } + if (gidsetsize > 0) { + grouplist = g_try_new(gid_t, gidsetsize); + if (!grouplist) { + return -TARGET_ENOMEM; + } + target_grouplist = lock_user(VERIFY_READ, arg2, + gidsetsize * 4, 1); + if (!target_grouplist) { + return -TARGET_EFAULT; + } + for (i = 0; i < gidsetsize; i++) { + grouplist[i] = tswap32(target_grouplist[i]); + } + unlock_user(target_grouplist, arg2, 0); } - for(i = 0;i < gidsetsize; i++) - grouplist[i] = tswap32(target_grouplist[i]); - unlock_user(target_grouplist, arg2, 0); return get_errno(setgroups(gidsetsize, grouplist)); } #endif @@ -12234,7 +12823,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_mincore case TARGET_NR_mincore: { - void *a = lock_user(VERIFY_READ, arg1, arg2, 0); + void *a = lock_user(VERIFY_NONE, arg1, arg2, 0); if (!a) { return -TARGET_ENOMEM; } @@ -12262,7 +12851,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return -host_to_target_errno(ret); #endif -#if TARGET_ABI_BITS == 32 +#if TARGET_ABI_BITS == 32 && !defined(TARGET_ABI_MIPSN32) #ifdef TARGET_NR_fadvise64_64 case TARGET_NR_fadvise64_64: @@ -12327,11 +12916,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_madvise case TARGET_NR_madvise: - /* A straight passthrough may not be safe because qemu sometimes - turns private file-backed mappings into anonymous mappings. - This will break MADV_DONTNEED. - This is a hint, so ignoring and returning success is ok. */ - return 0; + return target_madvise(arg1, arg2, arg3); #endif #ifdef TARGET_NR_fcntl64 case TARGET_NR_fcntl64: @@ -12393,7 +12978,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return get_errno(sys_gettid()); #ifdef TARGET_NR_readahead case TARGET_NR_readahead: -#if TARGET_ABI_BITS == 32 +#if TARGET_ABI_BITS == 32 && !defined(TARGET_ABI_MIPSN32) if (regpairs_aligned(cpu_env, num)) { arg2 = arg3; arg3 = arg4; @@ -12439,7 +13024,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, case TARGET_NR_listxattr: case TARGET_NR_llistxattr: { - void *p, *b = 0; + void *b = 0; if (arg2) { b = lock_user(VERIFY_WRITE, arg2, arg3, 0); if (!b) { @@ -12476,7 +13061,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, case TARGET_NR_setxattr: case TARGET_NR_lsetxattr: { - void *p, *n, *v = 0; + void *n, *v = 0; if (arg3) { v = lock_user(VERIFY_READ, arg3, arg4, 1); if (!v) { @@ -12521,7 +13106,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, case TARGET_NR_getxattr: case TARGET_NR_lgetxattr: { - void *p, *n, *v = 0; + void *n, *v = 0; if (arg3) { v = lock_user(VERIFY_WRITE, arg3, arg4, 0); if (!v) { @@ -12566,7 +13151,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, case TARGET_NR_removexattr: case TARGET_NR_lremovexattr: { - void *p, *n; + void *n; p = lock_user_string(arg1); n = lock_user_string(arg2); if (p && n) { @@ -12613,7 +13198,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return do_set_thread_area(cpu_env, arg1); #elif defined(TARGET_M68K) { - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); ts->tp_value = arg1; return 0; } @@ -12627,7 +13212,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return do_get_thread_area(cpu_env, arg1); #elif defined(TARGET_M68K) { - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); return ts->tp_value; } #else @@ -12749,9 +13334,14 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, } #endif -#if defined(TARGET_NR_set_tid_address) && defined(__NR_set_tid_address) +#if defined(TARGET_NR_set_tid_address) case TARGET_NR_set_tid_address: - return get_errno(set_tid_address((int *)g2h(cpu, arg1))); + { + TaskState *ts = get_task_state(cpu); + ts->child_tidptr = arg1; + /* do not call host set_tid_address() syscall, instead return tid() */ + return get_errno(sys_gettid()); + } #endif case TARGET_NR_tkill: @@ -12838,11 +13428,11 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_futex case TARGET_NR_futex: - return do_futex(cpu, arg1, arg2, arg3, arg4, arg5, arg6); + return do_futex(cpu, false, arg1, arg2, arg3, arg4, arg5, arg6); #endif #ifdef TARGET_NR_futex_time64 case TARGET_NR_futex_time64: - return do_futex_time64(cpu, arg1, arg2, arg3, arg4, arg5, arg6); + return do_futex(cpu, true, arg1, arg2, arg3, arg4, arg5, arg6); #endif #ifdef CONFIG_INOTIFY #if defined(TARGET_NR_inotify_init) @@ -13109,7 +13699,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #endif /* CONFIG_EVENTFD */ #if defined(CONFIG_FALLOCATE) && defined(TARGET_NR_fallocate) case TARGET_NR_fallocate: -#if TARGET_ABI_BITS == 32 +#if TARGET_ABI_BITS == 32 && !defined(TARGET_ABI_MIPSN32) ret = get_errno(fallocate(arg1, arg2, target_offset64(arg3, arg4), target_offset64(arg5, arg6))); #else @@ -13120,7 +13710,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #if defined(CONFIG_SYNC_FILE_RANGE) #if defined(TARGET_NR_sync_file_range) case TARGET_NR_sync_file_range: -#if TARGET_ABI_BITS == 32 +#if TARGET_ABI_BITS == 32 && !defined(TARGET_ABI_MIPSN32) #if defined(TARGET_MIPS) ret = get_errno(sync_file_range(arg1, target_offset64(arg3, arg4), target_offset64(arg5, arg6), arg7)); @@ -13142,7 +13732,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, case TARGET_NR_arm_sync_file_range: #endif /* This is like sync_file_range but the arguments are reordered */ -#if TARGET_ABI_BITS == 32 +#if TARGET_ABI_BITS == 32 && !defined(TARGET_ABI_MIPSN32) ret = get_errno(sync_file_range(arg1, target_offset64(arg3, arg4), target_offset64(arg5, arg6), arg2)); #else @@ -13290,8 +13880,8 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, if (!lock_user_struct(VERIFY_READ, target_rnew, arg3, 1)) { return -TARGET_EFAULT; } - rnew.rlim_cur = tswap64(target_rnew->rlim_cur); - rnew.rlim_max = tswap64(target_rnew->rlim_max); + __get_user(rnew.rlim_cur, &target_rnew->rlim_cur); + __get_user(rnew.rlim_max, &target_rnew->rlim_max); unlock_user_struct(target_rnew, arg3, 0); rnewp = &rnew; } @@ -13301,8 +13891,8 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, if (!lock_user_struct(VERIFY_WRITE, target_rold, arg4, 1)) { return -TARGET_EFAULT; } - target_rold->rlim_cur = tswap64(rold.rlim_cur); - target_rold->rlim_max = tswap64(rold.rlim_max); + __put_user(rold.rlim_cur, &target_rold->rlim_cur); + __put_user(rold.rlim_max, &target_rold->rlim_max); unlock_user_struct(target_rold, arg4, 1); } return ret; @@ -13367,15 +13957,18 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, phost_sevp = &host_sevp; ret = target_to_host_sigevent(phost_sevp, arg2); if (ret != 0) { + free_host_timer_slot(timer_index); return ret; } } ret = get_errno(timer_create(clkid, phost_sevp, phtimer)); if (ret) { - phtimer = NULL; + free_host_timer_slot(timer_index); } else { if (put_user(TIMER_MAGIC | timer_index, arg3, target_timer_t)) { + timer_delete(*phtimer); + free_host_timer_slot(timer_index); return -TARGET_EFAULT; } } @@ -13511,7 +14104,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, } else { timer_t htimer = g_posix_timers[timerid]; ret = get_errno(timer_delete(htimer)); - g_posix_timers[timerid] = 0; + free_host_timer_slot(timerid); } return ret; } @@ -13519,8 +14112,12 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #if defined(TARGET_NR_timerfd_create) && defined(CONFIG_TIMERFD) case TARGET_NR_timerfd_create: - return get_errno(timerfd_create(arg1, - target_to_host_bitmask(arg2, fcntl_flags_tbl))); + ret = get_errno(timerfd_create(arg1, + target_to_host_bitmask(arg2, fcntl_flags_tbl))); + if (ret >= 0) { + fd_trans_register(ret, &target_timerfd_trans); + } + return ret; #endif #if defined(TARGET_NR_timerfd_gettime) && defined(CONFIG_TIMERFD) @@ -13694,6 +14291,11 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return ret; #endif +#if defined(TARGET_NR_riscv_hwprobe) + case TARGET_NR_riscv_hwprobe: + return do_riscv_hwprobe(cpu_env, arg1, arg2, arg3, arg4, arg5); +#endif + default: qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num); return -TARGET_ENOSYS; diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index f051db9709..59558c5bc9 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -33,18 +33,18 @@ #define TARGET_SYS_SENDMMSG 20 /* sendmmsg() */ #define IPCOP_CALL(VERSION, OP) ((VERSION) << 16 | (OP)) -#define IPCOP_semop 1 -#define IPCOP_semget 2 -#define IPCOP_semctl 3 -#define IPCOP_semtimedop 4 -#define IPCOP_msgsnd 11 -#define IPCOP_msgrcv 12 -#define IPCOP_msgget 13 -#define IPCOP_msgctl 14 -#define IPCOP_shmat 21 -#define IPCOP_shmdt 22 -#define IPCOP_shmget 23 -#define IPCOP_shmctl 24 +#define IPCOP_semop 1 +#define IPCOP_semget 2 +#define IPCOP_semctl 3 +#define IPCOP_semtimedop 4 +#define IPCOP_msgsnd 11 +#define IPCOP_msgrcv 12 +#define IPCOP_msgget 13 +#define IPCOP_msgctl 14 +#define IPCOP_shmat 21 +#define IPCOP_shmdt 22 +#define IPCOP_shmget 23 +#define IPCOP_shmctl 24 #define TARGET_SEMOPM 500 @@ -56,42 +56,43 @@ * this explicit here. Please be sure to use the decoding macros * below from now on. */ -#define TARGET_IOC_NRBITS 8 -#define TARGET_IOC_TYPEBITS 8 +#define TARGET_IOC_NRBITS 8 +#define TARGET_IOC_TYPEBITS 8 -#if (defined(TARGET_I386) && defined(TARGET_ABI32)) \ - || (defined(TARGET_ARM) && defined(TARGET_ABI32)) \ - || defined(TARGET_SPARC) \ +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) \ + || (defined(TARGET_ARM) && defined(TARGET_ABI32)) \ + || (defined(TARGET_SPARC) && defined(TARGET_ABI32)) \ || defined(TARGET_M68K) || defined(TARGET_SH4) || defined(TARGET_CRIS) - /* 16 bit uid wrappers emulation */ +/* 16 bit uid wrappers emulation */ #define USE_UID16 #define target_id uint16_t #else -#define target_id uint32_t +#define target_id abi_uint #endif -#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SH4) \ - || defined(TARGET_M68K) || defined(TARGET_CRIS) \ - || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \ - || defined(TARGET_NIOS2) || defined(TARGET_RISCV) \ - || defined(TARGET_XTENSA) || defined(TARGET_E2K) +#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SH4) \ + || defined(TARGET_M68K) || defined(TARGET_CRIS) \ + || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \ + || defined(TARGET_NIOS2) || defined(TARGET_RISCV) \ + || defined(TARGET_XTENSA) || defined(TARGET_LOONGARCH64) \ + || defined(TARGET_E2K) -#define TARGET_IOC_SIZEBITS 14 -#define TARGET_IOC_DIRBITS 2 +#define TARGET_IOC_SIZEBITS 14 +#define TARGET_IOC_DIRBITS 2 -#define TARGET_IOC_NONE 0U +#define TARGET_IOC_NONE 0U #define TARGET_IOC_WRITE 1U -#define TARGET_IOC_READ 2U +#define TARGET_IOC_READ 2U -#elif defined(TARGET_PPC) || defined(TARGET_ALPHA) || \ - defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) || \ - defined(TARGET_MIPS) +#elif defined(TARGET_PPC) || defined(TARGET_ALPHA) || \ + defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) || \ + defined(TARGET_MIPS) -#define TARGET_IOC_SIZEBITS 13 -#define TARGET_IOC_DIRBITS 3 +#define TARGET_IOC_SIZEBITS 13 +#define TARGET_IOC_DIRBITS 3 -#define TARGET_IOC_NONE 1U -#define TARGET_IOC_READ 2U +#define TARGET_IOC_NONE 1U +#define TARGET_IOC_READ 2U #define TARGET_IOC_WRITE 4U #elif defined(TARGET_HPPA) @@ -115,32 +116,32 @@ #error unsupported CPU #endif -#define TARGET_IOC_NRMASK ((1 << TARGET_IOC_NRBITS)-1) -#define TARGET_IOC_TYPEMASK ((1 << TARGET_IOC_TYPEBITS)-1) -#define TARGET_IOC_SIZEMASK ((1 << TARGET_IOC_SIZEBITS)-1) -#define TARGET_IOC_DIRMASK ((1 << TARGET_IOC_DIRBITS)-1) +#define TARGET_IOC_NRMASK ((1 << TARGET_IOC_NRBITS)-1) +#define TARGET_IOC_TYPEMASK ((1 << TARGET_IOC_TYPEBITS)-1) +#define TARGET_IOC_SIZEMASK ((1 << TARGET_IOC_SIZEBITS)-1) +#define TARGET_IOC_DIRMASK ((1 << TARGET_IOC_DIRBITS)-1) -#define TARGET_IOC_NRSHIFT 0 -#define TARGET_IOC_TYPESHIFT (TARGET_IOC_NRSHIFT+TARGET_IOC_NRBITS) -#define TARGET_IOC_SIZESHIFT (TARGET_IOC_TYPESHIFT+TARGET_IOC_TYPEBITS) -#define TARGET_IOC_DIRSHIFT (TARGET_IOC_SIZESHIFT+TARGET_IOC_SIZEBITS) +#define TARGET_IOC_NRSHIFT 0 +#define TARGET_IOC_TYPESHIFT (TARGET_IOC_NRSHIFT+TARGET_IOC_NRBITS) +#define TARGET_IOC_SIZESHIFT (TARGET_IOC_TYPESHIFT+TARGET_IOC_TYPEBITS) +#define TARGET_IOC_DIRSHIFT (TARGET_IOC_SIZESHIFT+TARGET_IOC_SIZEBITS) -#define TARGET_IOC(dir,type,nr,size) \ - (((dir) << TARGET_IOC_DIRSHIFT) | \ - ((type) << TARGET_IOC_TYPESHIFT) | \ - ((nr) << TARGET_IOC_NRSHIFT) | \ - ((size) << TARGET_IOC_SIZESHIFT)) +#define TARGET_IOC(dir,type,nr,size) \ + (((dir) << TARGET_IOC_DIRSHIFT) | \ + ((type) << TARGET_IOC_TYPESHIFT) | \ + ((nr) << TARGET_IOC_NRSHIFT) | \ + ((size) << TARGET_IOC_SIZESHIFT)) /* used to create numbers */ -#define TARGET_IO(type,nr) TARGET_IOC(TARGET_IOC_NONE,(type),(nr),0) -#define TARGET_IOR(type,nr,size) TARGET_IOC(TARGET_IOC_READ,(type),(nr),sizeof(size)) -#define TARGET_IOW(type,nr,size) TARGET_IOC(TARGET_IOC_WRITE,(type),(nr),sizeof(size)) -#define TARGET_IOWR(type,nr,size) TARGET_IOC(TARGET_IOC_READ|TARGET_IOC_WRITE,(type),(nr),sizeof(size)) +#define TARGET_IO(type,nr) TARGET_IOC(TARGET_IOC_NONE,(type),(nr),0) +#define TARGET_IOR(type,nr,size) TARGET_IOC(TARGET_IOC_READ,(type),(nr),sizeof(size)) +#define TARGET_IOW(type,nr,size) TARGET_IOC(TARGET_IOC_WRITE,(type),(nr),sizeof(size)) +#define TARGET_IOWR(type,nr,size) TARGET_IOC(TARGET_IOC_READ|TARGET_IOC_WRITE,(type),(nr),sizeof(size)) /* the size is automatically computed for these defines */ -#define TARGET_IORU(type,nr) TARGET_IOC(TARGET_IOC_READ,(type),(nr),TARGET_IOC_SIZEMASK) -#define TARGET_IOWU(type,nr) TARGET_IOC(TARGET_IOC_WRITE,(type),(nr),TARGET_IOC_SIZEMASK) -#define TARGET_IOWRU(type,nr) TARGET_IOC(TARGET_IOC_READ|TARGET_IOC_WRITE,(type),(nr),TARGET_IOC_SIZEMASK) +#define TARGET_IORU(type,nr) TARGET_IOC(TARGET_IOC_READ,(type),(nr),TARGET_IOC_SIZEMASK) +#define TARGET_IOWU(type,nr) TARGET_IOC(TARGET_IOC_WRITE,(type),(nr),TARGET_IOC_SIZEMASK) +#define TARGET_IOWRU(type,nr) TARGET_IOC(TARGET_IOC_READ|TARGET_IOC_WRITE,(type),(nr),TARGET_IOC_SIZEMASK) struct target_sockaddr { abi_ushort sa_family; @@ -174,12 +175,12 @@ struct target_in_addr { }; struct target_sockaddr_in { - abi_ushort sin_family; - abi_short sin_port; /* big endian */ - struct target_in_addr sin_addr; - uint8_t __pad[sizeof(struct target_sockaddr) - - sizeof(abi_ushort) - sizeof(abi_short) - - sizeof(struct target_in_addr)]; + abi_ushort sin_family; + abi_short sin_port; /* big endian */ + struct target_in_addr sin_addr; + uint8_t __pad[sizeof(struct target_sockaddr) - + sizeof(abi_ushort) - sizeof(abi_short) - + sizeof(struct target_in_addr)]; }; struct target_sockaddr_in6 { @@ -215,9 +216,9 @@ struct target_ip_mreqn { struct target_ip_mreq_source { /* big endian */ - uint32_t imr_multiaddr; - uint32_t imr_interface; - uint32_t imr_sourceaddr; + abi_uint imr_multiaddr; + abi_uint imr_interface; + abi_uint imr_sourceaddr; }; struct target_linger { @@ -360,26 +361,26 @@ struct target_iovec { }; struct target_msghdr { - abi_long msg_name; /* Socket name */ - int msg_namelen; /* Length of name */ - abi_long msg_iov; /* Data blocks */ - abi_long msg_iovlen; /* Number of blocks */ - abi_long msg_control; /* Per protocol magic (eg BSD file descriptor passing) */ - abi_long msg_controllen; /* Length of cmsg list */ - unsigned int msg_flags; + abi_long msg_name; /* Socket name */ + abi_int msg_namelen; /* Length of name */ + abi_long msg_iov; /* Data blocks */ + abi_long msg_iovlen; /* Number of blocks */ + abi_long msg_control; /* Per protocol magic (eg BSD file descriptor passing) */ + abi_long msg_controllen; /* Length of cmsg list */ + abi_uint msg_flags; }; struct target_cmsghdr { abi_long cmsg_len; - int cmsg_level; - int cmsg_type; + abi_int cmsg_level; + abi_int cmsg_type; }; #define TARGET_CMSG_DATA(cmsg) ((unsigned char *) ((struct target_cmsghdr *) (cmsg) + 1)) -#define TARGET_CMSG_NXTHDR(mhdr, cmsg, cmsg_start) \ - __target_cmsg_nxthdr(mhdr, cmsg, cmsg_start) -#define TARGET_CMSG_ALIGN(len) (((len) + sizeof (abi_long) - 1) \ - & (size_t) ~(sizeof (abi_long) - 1)) +#define TARGET_CMSG_NXTHDR(mhdr, cmsg, cmsg_start) \ + __target_cmsg_nxthdr(mhdr, cmsg, cmsg_start) +#define TARGET_CMSG_ALIGN(len) (((len) + sizeof (abi_long) - 1) \ + & (size_t) ~(sizeof (abi_long) - 1)) #define TARGET_CMSG_SPACE(len) (sizeof(struct target_cmsghdr) + \ TARGET_CMSG_ALIGN(len)) #define TARGET_CMSG_LEN(len) (sizeof(struct target_cmsghdr) + (len)) @@ -389,73 +390,73 @@ __target_cmsg_nxthdr(struct target_msghdr *__mhdr, struct target_cmsghdr *__cmsg, struct target_cmsghdr *__cmsg_start) { - struct target_cmsghdr *__ptr; + struct target_cmsghdr *__ptr; - __ptr = (struct target_cmsghdr *)((unsigned char *) __cmsg - + TARGET_CMSG_ALIGN (tswapal(__cmsg->cmsg_len))); - if ((unsigned long)((char *)(__ptr+1) - (char *)__cmsg_start) - > tswapal(__mhdr->msg_controllen)) { - /* No more entries. */ - return (struct target_cmsghdr *)0; - } - return __ptr; + __ptr = (struct target_cmsghdr *)((unsigned char *) __cmsg + + TARGET_CMSG_ALIGN (tswapal(__cmsg->cmsg_len))); + if ((unsigned long)((char *)(__ptr+1) - (char *)__cmsg_start) + > tswapal(__mhdr->msg_controllen)) { + /* No more entries. */ + return (struct target_cmsghdr *)0; + } + return __ptr; } struct target_mmsghdr { struct target_msghdr msg_hdr; /* Message header */ - unsigned int msg_len; /* Number of bytes transmitted */ + abi_uint msg_len; /* Number of bytes transmitted */ }; struct target_rusage { - struct target_timeval ru_utime; /* user time used */ - struct target_timeval ru_stime; /* system time used */ - abi_long ru_maxrss; /* maximum resident set size */ - abi_long ru_ixrss; /* integral shared memory size */ - abi_long ru_idrss; /* integral unshared data size */ - abi_long ru_isrss; /* integral unshared stack size */ - abi_long ru_minflt; /* page reclaims */ - abi_long ru_majflt; /* page faults */ - abi_long ru_nswap; /* swaps */ - abi_long ru_inblock; /* block input operations */ - abi_long ru_oublock; /* block output operations */ - abi_long ru_msgsnd; /* messages sent */ - abi_long ru_msgrcv; /* messages received */ - abi_long ru_nsignals; /* signals received */ - abi_long ru_nvcsw; /* voluntary context switches */ - abi_long ru_nivcsw; /* involuntary " */ + struct target_timeval ru_utime; /* user time used */ + struct target_timeval ru_stime; /* system time used */ + abi_long ru_maxrss; /* maximum resident set size */ + abi_long ru_ixrss; /* integral shared memory size */ + abi_long ru_idrss; /* integral unshared data size */ + abi_long ru_isrss; /* integral unshared stack size */ + abi_long ru_minflt; /* page reclaims */ + abi_long ru_majflt; /* page faults */ + abi_long ru_nswap; /* swaps */ + abi_long ru_inblock; /* block input operations */ + abi_long ru_oublock; /* block output operations */ + abi_long ru_msgsnd; /* messages sent */ + abi_long ru_msgrcv; /* messages received */ + abi_long ru_nsignals; /* signals received */ + abi_long ru_nvcsw; /* voluntary context switches */ + abi_long ru_nivcsw; /* involuntary " */ }; typedef struct { - int val[2]; + abi_int val[2]; } kernel_fsid_t; struct target_dirent { - abi_long d_ino; - abi_long d_off; - unsigned short d_reclen; - char d_name[]; + abi_long d_ino; + abi_long d_off; + abi_ushort d_reclen; + char d_name[]; }; struct target_dirent64 { - abi_ullong d_ino; - abi_llong d_off; - abi_ushort d_reclen; - unsigned char d_type; - char d_name[]; + abi_ullong d_ino; + abi_llong d_off; + abi_ushort d_reclen; + unsigned char d_type; + char d_name[]; }; /* mostly generic signal stuff */ -#define TARGET_SIG_DFL ((abi_long)0) /* default signal handling */ -#define TARGET_SIG_IGN ((abi_long)1) /* ignore signal */ -#define TARGET_SIG_ERR ((abi_long)-1) /* error return from signal */ +#define TARGET_SIG_DFL ((abi_long)0) /* default signal handling */ +#define TARGET_SIG_IGN ((abi_long)1) /* ignore signal */ +#define TARGET_SIG_ERR ((abi_long)-1) /* error return from signal */ #ifdef TARGET_MIPS -#define TARGET_NSIG 128 +#define TARGET_NSIG 128 #else -#define TARGET_NSIG 64 +#define TARGET_NSIG 64 #endif -#define TARGET_NSIG_BPW TARGET_ABI_BITS +#define TARGET_NSIG_BPW TARGET_ABI_BITS #define TARGET_NSIG_WORDS (TARGET_NSIG / TARGET_NSIG_BPW) typedef struct { @@ -501,78 +502,54 @@ int do_sigaction(int sig, const struct target_sigaction *act, #endif #if defined(TARGET_ALPHA) -typedef int32_t target_old_sa_flags; +typedef abi_int target_old_sa_flags; #else typedef abi_ulong target_old_sa_flags; #endif #if defined(TARGET_MIPS) struct target_sigaction { - uint32_t sa_flags; + abi_uint sa_flags; #if defined(TARGET_ABI_MIPSN32) - uint32_t _sa_handler; + abi_uint _sa_handler; #else - abi_ulong _sa_handler; + abi_ulong _sa_handler; #endif - target_sigset_t sa_mask; + target_sigset_t sa_mask; #ifdef TARGET_ARCH_HAS_SA_RESTORER - /* ??? This is always present, but ignored unless O32. */ - abi_ulong sa_restorer; + /* ??? This is always present, but ignored unless O32. */ + abi_ulong sa_restorer; #endif }; #else struct target_old_sigaction { - abi_ulong _sa_handler; - abi_ulong sa_mask; - target_old_sa_flags sa_flags; + abi_ulong _sa_handler; + abi_ulong sa_mask; + target_old_sa_flags sa_flags; #ifdef TARGET_ARCH_HAS_SA_RESTORER - abi_ulong sa_restorer; + abi_ulong sa_restorer; #endif }; struct target_sigaction { - abi_ulong _sa_handler; - abi_ulong sa_flags; + abi_ulong _sa_handler; + abi_ulong sa_flags; #ifdef TARGET_ARCH_HAS_SA_RESTORER - abi_ulong sa_restorer; + abi_ulong sa_restorer; #endif - target_sigset_t sa_mask; + target_sigset_t sa_mask; #ifdef TARGET_ARCH_HAS_KA_RESTORER - abi_ulong ka_restorer; + abi_ulong ka_restorer; #endif }; #endif typedef union target_sigval { - int sival_int; - abi_ulong sival_ptr; + abi_int sival_int; + abi_ulong sival_ptr; } target_sigval_t; -#if 0 -#if defined (TARGET_SPARC) -typedef struct { - struct { - abi_ulong psr; - abi_ulong pc; - abi_ulong npc; - abi_ulong y; - abi_ulong u_regs[16]; /* globals and ins */ - } si_regs; - int si_mask; -} __siginfo_t; -typedef struct { - unsigned long si_float_regs [32]; - unsigned long si_fsr; - unsigned long si_fpqdepth; - struct { - unsigned long *insn_addr; - unsigned long insn; - } si_fpqueue [16]; -} __siginfo_fpu_t; -#endif -#endif - -#define TARGET_SI_MAX_SIZE 128 +#define TARGET_SI_MAX_SIZE 128 #if TARGET_ABI_BITS == 32 #define TARGET_SI_PREAMBLE_SIZE (3 * sizeof(int)) @@ -599,82 +576,82 @@ typedef struct { typedef struct target_siginfo { #ifdef TARGET_MIPS - int si_signo; - int si_code; - int si_errno; + abi_int si_signo; + abi_int si_code; + abi_int si_errno; #else - int si_signo; - int si_errno; - int si_code; + abi_int si_signo; + abi_int si_errno; + abi_int si_code; #endif - union { - int _pad[TARGET_SI_PAD_SIZE]; + union { + abi_int _pad[TARGET_SI_PAD_SIZE]; - /* kill() */ - struct { - pid_t _pid; /* sender's pid */ - uid_t _uid; /* sender's uid */ - } _kill; + /* kill() */ + struct { + pid_t _pid; /* sender's pid */ + uid_t _uid; /* sender's uid */ + } _kill; - /* POSIX.1b timers */ - struct { - unsigned int _timer1; - unsigned int _timer2; - } _timer; + /* POSIX.1b timers */ + struct { + abi_uint _timer1; + abi_uint _timer2; + } _timer; - /* POSIX.1b signals */ - struct { - pid_t _pid; /* sender's pid */ - uid_t _uid; /* sender's uid */ - target_sigval_t _sigval; - } _rt; + /* POSIX.1b signals */ + struct { + pid_t _pid; /* sender's pid */ + uid_t _uid; /* sender's uid */ + target_sigval_t _sigval; + } _rt; - /* SIGCHLD */ - struct { - pid_t _pid; /* which child */ - uid_t _uid; /* sender's uid */ - int _status; /* exit code */ - target_clock_t _utime; - target_clock_t _stime; - } _sigchld; + /* SIGCHLD */ + struct { + pid_t _pid; /* which child */ + uid_t _uid; /* sender's uid */ + abi_int _status; /* exit code */ + target_clock_t _utime; + target_clock_t _stime; + } _sigchld; - /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ - struct { - abi_ulong _addr; /* faulting insn/memory ref. */ - } _sigfault; + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + abi_ulong _addr; /* faulting insn/memory ref. */ + } _sigfault; - /* SIGPOLL */ - struct { - int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ - int _fd; - } _sigpoll; - } _sifields; + /* SIGPOLL */ + struct { + abi_int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + abi_int _fd; + } _sigpoll; + } _sifields; } target_siginfo_t; /* * si_code values * Digital reserves positive values for kernel-generated signals. */ -#define TARGET_SI_USER 0 /* sent by kill, sigsend, raise */ -#define TARGET_SI_KERNEL 0x80 /* sent by the kernel from somewhere */ -#define TARGET_SI_QUEUE -1 /* sent by sigqueue */ +#define TARGET_SI_USER 0 /* sent by kill, sigsend, raise */ +#define TARGET_SI_KERNEL 0x80 /* sent by the kernel from somewhere */ +#define TARGET_SI_QUEUE -1 /* sent by sigqueue */ #define TARGET_SI_TIMER -2 /* sent by timer expiration */ -#define TARGET_SI_MESGQ -3 /* sent by real time mesq state change */ -#define TARGET_SI_ASYNCIO -4 /* sent by AIO completion */ -#define TARGET_SI_SIGIO -5 /* sent by queued SIGIO */ +#define TARGET_SI_MESGQ -3 /* sent by real time mesq state change */ +#define TARGET_SI_ASYNCIO -4 /* sent by AIO completion */ +#define TARGET_SI_SIGIO -5 /* sent by queued SIGIO */ /* * SIGILL si_codes */ -#define TARGET_ILL_ILLOPC (1) /* illegal opcode */ -#define TARGET_ILL_ILLOPN (2) /* illegal operand */ -#define TARGET_ILL_ILLADR (3) /* illegal addressing mode */ -#define TARGET_ILL_ILLTRP (4) /* illegal trap */ -#define TARGET_ILL_PRVOPC (5) /* privileged opcode */ -#define TARGET_ILL_PRVREG (6) /* privileged register */ -#define TARGET_ILL_COPROC (7) /* coprocessor error */ -#define TARGET_ILL_BADSTK (8) /* internal stack error */ +#define TARGET_ILL_ILLOPC (1) /* illegal opcode */ +#define TARGET_ILL_ILLOPN (2) /* illegal operand */ +#define TARGET_ILL_ILLADR (3) /* illegal addressing mode */ +#define TARGET_ILL_ILLTRP (4) /* illegal trap */ +#define TARGET_ILL_PRVOPC (5) /* privileged opcode */ +#define TARGET_ILL_PRVREG (6) /* privileged register */ +#define TARGET_ILL_COPROC (7) /* coprocessor error */ +#define TARGET_ILL_BADSTK (8) /* internal stack error */ /* * SIGFPE si_codes @@ -700,9 +677,9 @@ typedef struct target_siginfo { /* * SIGBUS si_codes */ -#define TARGET_BUS_ADRALN (1) /* invalid address alignment */ -#define TARGET_BUS_ADRERR (2) /* non-existent physical address */ -#define TARGET_BUS_OBJERR (3) /* object specific hardware error */ +#define TARGET_BUS_ADRALN (1) /* invalid address alignment */ +#define TARGET_BUS_ADRERR (2) /* non-existent physical address */ +#define TARGET_BUS_OBJERR (3) /* object specific hardware error */ /* hardware memory error consumed on a machine check: action required */ #define TARGET_BUS_MCEERR_AR (4) /* hardware memory error detected in process but not consumed: action optional*/ @@ -711,42 +688,47 @@ typedef struct target_siginfo { /* * SIGTRAP si_codes */ -#define TARGET_TRAP_BRKPT (1) /* process breakpoint */ -#define TARGET_TRAP_TRACE (2) /* process trace trap */ +#define TARGET_TRAP_BRKPT (1) /* process breakpoint */ +#define TARGET_TRAP_TRACE (2) /* process trace trap */ #define TARGET_TRAP_BRANCH (3) /* process taken branch trap */ #define TARGET_TRAP_HWBKPT (4) /* hardware breakpoint/watchpoint */ #define TARGET_TRAP_UNK (5) /* undiagnosed trap */ +/* + * SIGEMT si_codes + */ +#define TARGET_EMT_TAGOVF 1 /* tag overflow */ + #include "target_resource.h" struct target_pollfd { - int fd; /* file descriptor */ - short events; /* requested events */ - short revents; /* returned events */ + abi_int fd; /* file descriptor */ + abi_short events; /* requested events */ + abi_short revents; /* returned events */ }; /* virtual terminal ioctls */ -#define TARGET_KIOCSOUND 0x4B2F /* start sound generation (0 for off) */ -#define TARGET_KDMKTONE 0x4B30 /* generate tone */ +#define TARGET_KIOCSOUND 0x4B2F /* start sound generation (0 for off) */ +#define TARGET_KDMKTONE 0x4B30 /* generate tone */ #define TARGET_KDGKBTYPE 0x4b33 #define TARGET_KDSETMODE 0x4b3a #define TARGET_KDGKBMODE 0x4b44 #define TARGET_KDSKBMODE 0x4b45 -#define TARGET_KDGKBENT 0x4B46 /* gets one entry in translation table */ -#define TARGET_KDGKBSENT 0x4B48 /* gets one function key string entry */ -#define TARGET_KDGKBLED 0x4B64 /* get led flags (not lights) */ -#define TARGET_KDSKBLED 0x4B65 /* set led flags (not lights) */ -#define TARGET_KDGETLED 0x4B31 /* return current led state */ -#define TARGET_KDSETLED 0x4B32 /* set led state [lights, not flags] */ +#define TARGET_KDGKBENT 0x4B46 /* gets one entry in translation table */ +#define TARGET_KDGKBSENT 0x4B48 /* gets one function key string entry */ +#define TARGET_KDGKBLED 0x4B64 /* get led flags (not lights) */ +#define TARGET_KDSKBLED 0x4B65 /* set led flags (not lights) */ +#define TARGET_KDGETLED 0x4B31 /* return current led state */ +#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_int pll_ctrl; + abi_int pll_value; + abi_int pll_max; + abi_int pll_min; + abi_int pll_posmult; + abi_int pll_negmult; abi_long pll_clock; }; @@ -769,18 +751,18 @@ struct target_rtc_pll_info { #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, \ +#define TARGET_RTC_PLL_GET TARGET_IOR('p', 0x11, \ struct target_rtc_pll_info) -#define TARGET_RTC_PLL_SET TARGET_IOW('p', 0x12, \ +#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_READ TARGET_IOR('p', 0x13, abi_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) -#define TARGET_FIOSETOWN TARGET_IOW('f', 124, int) -#define TARGET_SIOCATMARK TARGET_IOR('s', 7, int) +#if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_SH4) || \ + defined(TARGET_XTENSA) +#define TARGET_FIOGETOWN TARGET_IOR('f', 123, abi_int) +#define TARGET_FIOSETOWN TARGET_IOW('f', 124, abi_int) +#define TARGET_SIOCATMARK TARGET_IOR('s', 7, abi_int) #define TARGET_SIOCSPGRP TARGET_IOW('s', 8, pid_t) #define TARGET_SIOCGPGRP TARGET_IOR('s', 9, pid_t) #else @@ -870,40 +852,40 @@ struct target_rtc_pll_info { /* From */ -#define TARGET_TUNSETDEBUG TARGET_IOW('T', 201, int) -#define TARGET_TUNSETIFF TARGET_IOW('T', 202, int) -#define TARGET_TUNSETPERSIST TARGET_IOW('T', 203, int) -#define TARGET_TUNSETOWNER TARGET_IOW('T', 204, int) -#define TARGET_TUNSETLINK TARGET_IOW('T', 205, int) -#define TARGET_TUNSETGROUP TARGET_IOW('T', 206, int) -#define TARGET_TUNGETFEATURES TARGET_IOR('T', 207, unsigned int) -#define TARGET_TUNSETOFFLOAD TARGET_IOW('T', 208, unsigned int) -#define TARGET_TUNSETTXFILTER TARGET_IOW('T', 209, unsigned int) -#define TARGET_TUNGETIFF TARGET_IOR('T', 210, unsigned int) -#define TARGET_TUNGETSNDBUF TARGET_IOR('T', 211, int) -#define TARGET_TUNSETSNDBUF TARGET_IOW('T', 212, int) +#define TARGET_TUNSETDEBUG TARGET_IOW('T', 201, abi_int) +#define TARGET_TUNSETIFF TARGET_IOW('T', 202, abi_int) +#define TARGET_TUNSETPERSIST TARGET_IOW('T', 203, abi_int) +#define TARGET_TUNSETOWNER TARGET_IOW('T', 204, abi_int) +#define TARGET_TUNSETLINK TARGET_IOW('T', 205, abi_int) +#define TARGET_TUNSETGROUP TARGET_IOW('T', 206, abi_int) +#define TARGET_TUNGETFEATURES TARGET_IOR('T', 207, abi_uint) +#define TARGET_TUNSETOFFLOAD TARGET_IOW('T', 208, abi_uint) +#define TARGET_TUNSETTXFILTER TARGET_IOW('T', 209, abi_uint) +#define TARGET_TUNGETIFF TARGET_IOR('T', 210, abi_uint) +#define TARGET_TUNGETSNDBUF TARGET_IOR('T', 211, abi_int) +#define TARGET_TUNSETSNDBUF TARGET_IOW('T', 212, abi_int) /* * TUNATTACHFILTER and TUNDETACHFILTER are not supported. Linux kernel keeps a * user pointer in TUNATTACHFILTER, which we are not able to correctly handle. */ -#define TARGET_TUNGETVNETHDRSZ TARGET_IOR('T', 215, int) -#define TARGET_TUNSETVNETHDRSZ TARGET_IOW('T', 216, int) -#define TARGET_TUNSETQUEUE TARGET_IOW('T', 217, int) -#define TARGET_TUNSETIFINDEX TARGET_IOW('T', 218, unsigned int) +#define TARGET_TUNGETVNETHDRSZ TARGET_IOR('T', 215, abi_int) +#define TARGET_TUNSETVNETHDRSZ TARGET_IOW('T', 216, abi_int) +#define TARGET_TUNSETQUEUE TARGET_IOW('T', 217, abi_int) +#define TARGET_TUNSETIFINDEX TARGET_IOW('T', 218, abi_uint) /* TUNGETFILTER is not supported: see TUNATTACHFILTER. */ -#define TARGET_TUNSETVNETLE TARGET_IOW('T', 220, int) -#define TARGET_TUNGETVNETLE TARGET_IOR('T', 221, int) -#define TARGET_TUNSETVNETBE TARGET_IOW('T', 222, int) -#define TARGET_TUNGETVNETBE TARGET_IOR('T', 223, int) -#define TARGET_TUNSETSTEERINGEBPF TARGET_IOR('T', 224, int) -#define TARGET_TUNSETFILTEREBPF TARGET_IOR('T', 225, int) -#define TARGET_TUNSETCARRIER TARGET_IOW('T', 226, int) +#define TARGET_TUNSETVNETLE TARGET_IOW('T', 220, abi_int) +#define TARGET_TUNGETVNETLE TARGET_IOR('T', 221, abi_int) +#define TARGET_TUNSETVNETBE TARGET_IOW('T', 222, abi_int) +#define TARGET_TUNGETVNETBE TARGET_IOR('T', 223, abi_int) +#define TARGET_TUNSETSTEERINGEBPF TARGET_IOR('T', 224, abi_int) +#define TARGET_TUNSETFILTEREBPF TARGET_IOR('T', 225, abi_int) +#define TARGET_TUNSETCARRIER TARGET_IOW('T', 226, abi_int) #define TARGET_TUNGETDEVNETNS TARGET_IO('T', 227) /* From */ -#define TARGET_RNDGETENTCNT TARGET_IOR('R', 0x00, int) -#define TARGET_RNDADDTOENTCNT TARGET_IOW('R', 0x01, int) +#define TARGET_RNDGETENTCNT TARGET_IOR('R', 0x00, abi_int) +#define TARGET_RNDADDTOENTCNT TARGET_IOW('R', 0x01, abi_int) #define TARGET_RNDZAPENTCNT TARGET_IO('R', 0x04) #define TARGET_RNDCLEARPOOL TARGET_IO('R', 0x06) #define TARGET_RNDRESEEDCRNG TARGET_IO('R', 0x07) @@ -927,8 +909,8 @@ struct target_rtc_pll_info { #define TARGET_BLKBSZGET TARGET_IOR(0x12, 112, abi_ulong) #define TARGET_BLKBSZSET TARGET_IOW(0x12, 113, abi_ulong) #define TARGET_BLKGETSIZE64 TARGET_IOR(0x12,114,abi_ulong) - /* return device size in bytes - (u64 *arg) */ +/* return device size in bytes + (u64 *arg) */ #define TARGET_BLKDISCARD TARGET_IO(0x12, 119) #define TARGET_BLKIOMIN TARGET_IO(0x12, 120) @@ -959,9 +941,12 @@ struct target_rtc_pll_info { #define TARGET_FIBMAP TARGET_IO(0x00,1) /* bmap access */ #define TARGET_FIGETBSZ TARGET_IO(0x00,2) /* get the block size used for bmap */ -#define TARGET_FICLONE TARGET_IOW(0x94, 9, int) +#define TARGET_FICLONE TARGET_IOW(0x94, 9, abi_int) #define TARGET_FICLONERANGE TARGET_IOW(0x94, 13, struct file_clone_range) +#define TARGET_FIFREEZE TARGET_IOWR('X', 119, abi_int) +#define TARGET_FITHAW TARGET_IOWR('X', 120, abi_int) + /* * Note that the ioctl numbers for FS_IOC_ * claim type "long" but the actual type used by the kernel is "int". @@ -971,10 +956,10 @@ struct target_rtc_pll_info { #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) +#define TARGET_FS_IOC32_GETFLAGS TARGET_IOR('f', 1, abi_int) +#define TARGET_FS_IOC32_SETFLAGS TARGET_IOW('f', 2, abi_int) +#define TARGET_FS_IOC32_GETVERSION TARGET_IOR('v', 1, abi_int) +#define TARGET_FS_IOC32_SETVERSION TARGET_IOW('v', 2, abi_int) /* btrfs ioctls */ #ifdef HAVE_BTRFS_H @@ -986,11 +971,11 @@ struct target_rtc_pll_info { #define TARGET_BTRFS_IOC_SUBVOL_CREATE TARGET_IOWU(BTRFS_IOCTL_MAGIC, 14) #define TARGET_BTRFS_IOC_SNAP_DESTROY TARGET_IOWU(BTRFS_IOCTL_MAGIC, 15) #define TARGET_BTRFS_IOC_INO_LOOKUP TARGET_IOWRU(BTRFS_IOCTL_MAGIC, 18) -#define TARGET_BTRFS_IOC_DEFAULT_SUBVOL TARGET_IOW(BTRFS_IOCTL_MAGIC, 19,\ +#define TARGET_BTRFS_IOC_DEFAULT_SUBVOL TARGET_IOW(BTRFS_IOCTL_MAGIC, 19, \ abi_ullong) -#define TARGET_BTRFS_IOC_SUBVOL_GETFLAGS TARGET_IOR(BTRFS_IOCTL_MAGIC, 25,\ +#define TARGET_BTRFS_IOC_SUBVOL_GETFLAGS TARGET_IOR(BTRFS_IOCTL_MAGIC, 25, \ abi_ullong) -#define TARGET_BTRFS_IOC_SUBVOL_SETFLAGS TARGET_IOW(BTRFS_IOCTL_MAGIC, 26,\ +#define TARGET_BTRFS_IOC_SUBVOL_SETFLAGS TARGET_IOW(BTRFS_IOCTL_MAGIC, 26, \ abi_ullong) #define TARGET_BTRFS_IOC_SCRUB TARGET_IOWRU(BTRFS_IOCTL_MAGIC, 27) #define TARGET_BTRFS_IOC_SCRUB_CANCEL TARGET_IO(BTRFS_IOCTL_MAGIC, 28) @@ -1044,56 +1029,56 @@ struct target_rtc_pll_info { #define TARGET_USBDEVFS_GET_SPEED TARGET_IO('U', 31) /* cdrom commands */ -#define TARGET_CDROMPAUSE 0x5301 /* Pause Audio Operation */ -#define TARGET_CDROMRESUME 0x5302 /* Resume paused Audio Operation */ -#define TARGET_CDROMPLAYMSF 0x5303 /* Play Audio MSF (struct cdrom_msf) */ -#define TARGET_CDROMPLAYTRKIND 0x5304 /* Play Audio Track/index - (struct cdrom_ti) */ -#define TARGET_CDROMREADTOCHDR 0x5305 /* Read TOC header - (struct cdrom_tochdr) */ -#define TARGET_CDROMREADTOCENTRY 0x5306 /* Read TOC entry - (struct cdrom_tocentry) */ -#define TARGET_CDROMSTOP 0x5307 /* Stop the cdrom drive */ -#define TARGET_CDROMSTART 0x5308 /* Start the cdrom drive */ -#define TARGET_CDROMEJECT 0x5309 /* Ejects the cdrom media */ -#define TARGET_CDROMVOLCTRL 0x530a /* Control output volume - (struct cdrom_volctrl) */ -#define TARGET_CDROMSUBCHNL 0x530b /* Read subchannel data - (struct cdrom_subchnl) */ -#define TARGET_CDROMREADMODE2 0x530c /* Read TARGET_CDROM mode 2 data (2336 Bytes) - (struct cdrom_read) */ -#define TARGET_CDROMREADMODE1 0x530d /* Read TARGET_CDROM mode 1 data (2048 Bytes) - (struct cdrom_read) */ -#define TARGET_CDROMREADAUDIO 0x530e /* (struct cdrom_read_audio) */ -#define TARGET_CDROMEJECT_SW 0x530f /* enable(1)/disable(0) auto-ejecting */ -#define TARGET_CDROMMULTISESSION 0x5310 /* Obtain the start-of-last-session - address of multi session disks - (struct cdrom_multisession) */ -#define TARGET_CDROM_GET_MCN 0x5311 /* Obtain the "Universal Product Code" - if available (struct cdrom_mcn) */ -#define TARGET_CDROM_GET_UPC TARGET_CDROM_GET_MCN /* This one is deprecated, - but here anyway for compatibility */ -#define TARGET_CDROMRESET 0x5312 /* hard-reset the drive */ -#define TARGET_CDROMVOLREAD 0x5313 /* Get the drive's volume setting - (struct cdrom_volctrl) */ -#define TARGET_CDROMREADRAW 0x5314 /* read data in raw mode (2352 Bytes) - (struct cdrom_read) */ +#define TARGET_CDROMPAUSE 0x5301 /* Pause Audio Operation */ +#define TARGET_CDROMRESUME 0x5302 /* Resume paused Audio Operation */ +#define TARGET_CDROMPLAYMSF 0x5303 /* Play Audio MSF (struct cdrom_msf) */ +#define TARGET_CDROMPLAYTRKIND 0x5304 /* Play Audio Track/index + (struct cdrom_ti) */ +#define TARGET_CDROMREADTOCHDR 0x5305 /* Read TOC header + (struct cdrom_tochdr) */ +#define TARGET_CDROMREADTOCENTRY 0x5306 /* Read TOC entry + (struct cdrom_tocentry) */ +#define TARGET_CDROMSTOP 0x5307 /* Stop the cdrom drive */ +#define TARGET_CDROMSTART 0x5308 /* Start the cdrom drive */ +#define TARGET_CDROMEJECT 0x5309 /* Ejects the cdrom media */ +#define TARGET_CDROMVOLCTRL 0x530a /* Control output volume + (struct cdrom_volctrl) */ +#define TARGET_CDROMSUBCHNL 0x530b /* Read subchannel data + (struct cdrom_subchnl) */ +#define TARGET_CDROMREADMODE2 0x530c /* Read TARGET_CDROM mode 2 data (2336 Bytes) + (struct cdrom_read) */ +#define TARGET_CDROMREADMODE1 0x530d /* Read TARGET_CDROM mode 1 data (2048 Bytes) + (struct cdrom_read) */ +#define TARGET_CDROMREADAUDIO 0x530e /* (struct cdrom_read_audio) */ +#define TARGET_CDROMEJECT_SW 0x530f /* enable(1)/disable(0) auto-ejecting */ +#define TARGET_CDROMMULTISESSION 0x5310 /* Obtain the start-of-last-session + address of multi session disks + (struct cdrom_multisession) */ +#define TARGET_CDROM_GET_MCN 0x5311 /* Obtain the "Universal Product Code" + if available (struct cdrom_mcn) */ +#define TARGET_CDROM_GET_UPC TARGET_CDROM_GET_MCN /* This one is deprecated, + but here anyway for compatibility */ +#define TARGET_CDROMRESET 0x5312 /* hard-reset the drive */ +#define TARGET_CDROMVOLREAD 0x5313 /* Get the drive's volume setting + (struct cdrom_volctrl) */ +#define TARGET_CDROMREADRAW 0x5314 /* read data in raw mode (2352 Bytes) + (struct cdrom_read) */ /* * These ioctls are used only used in aztcd.c and optcd.c */ -#define TARGET_CDROMREADCOOKED 0x5315 /* read data in cooked mode */ -#define TARGET_CDROMSEEK 0x5316 /* seek msf address */ +#define TARGET_CDROMREADCOOKED 0x5315 /* read data in cooked mode */ +#define TARGET_CDROMSEEK 0x5316 /* seek msf address */ /* * This ioctl is only used by the scsi-cd driver. - It is for playing audio in logical block addressing mode. - */ -#define TARGET_CDROMPLAYBLK 0x5317 /* (struct cdrom_blk) */ + It is for playing audio in logical block addressing mode. +*/ +#define TARGET_CDROMPLAYBLK 0x5317 /* (struct cdrom_blk) */ /* * These ioctls are only used in optcd.c */ -#define TARGET_CDROMREADALL 0x5318 /* read all 2646 bytes */ +#define TARGET_CDROMREADALL 0x5318 /* read all 2646 bytes */ /* * These ioctls are (now) only in ide-cd.c for controlling @@ -1110,35 +1095,35 @@ struct target_rtc_pll_info { * They _will_ be adopted by all CD-ROM drivers, when all the CD-ROM * drivers are eventually ported to the uniform CD-ROM driver interface. */ -#define TARGET_CDROMCLOSETRAY 0x5319 /* pendant of CDROMEJECT */ -#define TARGET_CDROM_SET_OPTIONS 0x5320 /* Set behavior options */ -#define TARGET_CDROM_CLEAR_OPTIONS 0x5321 /* Clear behavior options */ -#define TARGET_CDROM_SELECT_SPEED 0x5322 /* Set the CD-ROM speed */ -#define TARGET_CDROM_SELECT_DISC 0x5323 /* Select disc (for juke-boxes) */ -#define TARGET_CDROM_MEDIA_CHANGED 0x5325 /* Check is media changed */ -#define TARGET_CDROM_DRIVE_STATUS 0x5326 /* Get tray position, etc. */ -#define TARGET_CDROM_DISC_STATUS 0x5327 /* Get disc type, etc. */ +#define TARGET_CDROMCLOSETRAY 0x5319 /* pendant of CDROMEJECT */ +#define TARGET_CDROM_SET_OPTIONS 0x5320 /* Set behavior options */ +#define TARGET_CDROM_CLEAR_OPTIONS 0x5321 /* Clear behavior options */ +#define TARGET_CDROM_SELECT_SPEED 0x5322 /* Set the CD-ROM speed */ +#define TARGET_CDROM_SELECT_DISC 0x5323 /* Select disc (for juke-boxes) */ +#define TARGET_CDROM_MEDIA_CHANGED 0x5325 /* Check is media changed */ +#define TARGET_CDROM_DRIVE_STATUS 0x5326 /* Get tray position, etc. */ +#define TARGET_CDROM_DISC_STATUS 0x5327 /* Get disc type, etc. */ #define TARGET_CDROM_CHANGER_NSLOTS 0x5328 /* Get number of slots */ -#define TARGET_CDROM_LOCKDOOR 0x5329 /* lock or unlock door */ -#define TARGET_CDROM_DEBUG 0x5330 /* Turn debug messages on/off */ -#define TARGET_CDROM_GET_CAPABILITY 0x5331 /* get capabilities */ +#define TARGET_CDROM_LOCKDOOR 0x5329 /* lock or unlock door */ +#define TARGET_CDROM_DEBUG 0x5330 /* Turn debug messages on/off */ +#define TARGET_CDROM_GET_CAPABILITY 0x5331 /* get capabilities */ /* Note that scsi/scsi_ioctl.h also uses 0x5382 - 0x5386. * Future CDROM ioctls should be kept below 0x537F */ /* This ioctl is only used by sbpcd at the moment */ -#define TARGET_CDROMAUDIOBUFSIZ 0x5382 /* set the audio buffer size */ - /* conflict with SCSI_IOCTL_GET_IDLUN */ +#define TARGET_CDROMAUDIOBUFSIZ 0x5382 /* set the audio buffer size */ +/* conflict with SCSI_IOCTL_GET_IDLUN */ /* DVD-ROM Specific ioctls */ -#define TARGET_DVD_READ_STRUCT 0x5390 /* Read structure */ -#define TARGET_DVD_WRITE_STRUCT 0x5391 /* Write structure */ -#define TARGET_DVD_AUTH 0x5392 /* Authentication */ +#define TARGET_DVD_READ_STRUCT 0x5390 /* Read structure */ +#define TARGET_DVD_WRITE_STRUCT 0x5391 /* Write structure */ +#define TARGET_DVD_AUTH 0x5392 /* Authentication */ -#define TARGET_CDROM_SEND_PACKET 0x5393 /* send a packet to the drive */ -#define TARGET_CDROM_NEXT_WRITABLE 0x5394 /* get next writable block */ -#define TARGET_CDROM_LAST_WRITTEN 0x5395 /* get last block written on disc */ +#define TARGET_CDROM_SEND_PACKET 0x5393 /* send a packet to the drive */ +#define TARGET_CDROM_NEXT_WRITABLE 0x5394 /* get next writable block */ +#define TARGET_CDROM_LAST_WRITTEN 0x5395 /* get last block written on disc */ /* HD commands */ @@ -1229,163 +1214,50 @@ struct target_rtc_pll_info { #define TARGET_NCC 8 struct target_termio { - unsigned short c_iflag; /* input mode flags */ - unsigned short c_oflag; /* output mode flags */ - unsigned short c_cflag; /* control mode flags */ - unsigned short c_lflag; /* local mode flags */ - unsigned char c_line; /* line discipline */ - unsigned char c_cc[TARGET_NCC]; /* control characters */ + abi_ushort c_iflag; /* input mode flags */ + abi_ushort c_oflag; /* output mode flags */ + abi_ushort c_cflag; /* control mode flags */ + abi_ushort c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[TARGET_NCC]; /* control characters */ }; struct target_winsize { - unsigned short ws_row; - unsigned short ws_col; - unsigned short ws_xpixel; - unsigned short ws_ypixel; + abi_ushort ws_row; + abi_ushort ws_col; + abi_ushort ws_xpixel; + abi_ushort ws_ypixel; }; #include "termbits.h" -#if defined(TARGET_MIPS) -#define TARGET_PROT_SEM 0x10 -#else -#define TARGET_PROT_SEM 0x08 -#endif +#include "target_mman.h" -#ifdef TARGET_AARCH64 -#define TARGET_PROT_BTI 0x10 -#define TARGET_PROT_MTE 0x20 -#endif - -/* Common */ -#define TARGET_MAP_SHARED 0x01 /* Share changes */ -#define TARGET_MAP_PRIVATE 0x02 /* Changes are private */ -#if defined(TARGET_HPPA) -#define TARGET_MAP_TYPE 0x03 /* Mask for type of mapping */ -#else -#define TARGET_MAP_TYPE 0x0f /* Mask for type of mapping */ -#endif - -/* Target specific */ -#if defined(TARGET_MIPS) -#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */ -#define TARGET_MAP_ANONYMOUS 0x0800 /* don't use a file */ -#define TARGET_MAP_GROWSDOWN 0x1000 /* stack-like segment */ -#define TARGET_MAP_DENYWRITE 0x2000 /* ETXTBSY */ -#define TARGET_MAP_EXECUTABLE 0x4000 /* mark it as an executable */ -#define TARGET_MAP_LOCKED 0x8000 /* pages are locked */ -#define TARGET_MAP_NORESERVE 0x0400 /* don't check for reservations */ -#define TARGET_MAP_POPULATE 0x10000 /* populate (prefault) pagetables */ -#define TARGET_MAP_NONBLOCK 0x20000 /* do not block on IO */ -#define TARGET_MAP_STACK 0x40000 /* ignored */ -#define TARGET_MAP_HUGETLB 0x80000 /* create a huge page mapping */ -#elif defined(TARGET_PPC) -#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */ -#define TARGET_MAP_ANONYMOUS 0x20 /* don't use a file */ -#define TARGET_MAP_GROWSDOWN 0x0100 /* stack-like segment */ -#define TARGET_MAP_DENYWRITE 0x0800 /* ETXTBSY */ -#define TARGET_MAP_EXECUTABLE 0x1000 /* mark it as an executable */ -#define TARGET_MAP_LOCKED 0x0080 /* pages are locked */ -#define TARGET_MAP_NORESERVE 0x0040 /* don't check for reservations */ -#define TARGET_MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ -#define TARGET_MAP_NONBLOCK 0x10000 /* do not block on IO */ -#define TARGET_MAP_STACK 0x20000 /* ignored */ -#define TARGET_MAP_HUGETLB 0x40000 /* create a huge page mapping */ -#elif defined(TARGET_ALPHA) -#define TARGET_MAP_ANONYMOUS 0x10 /* don't use a file */ -#define TARGET_MAP_FIXED 0x100 /* Interpret addr exactly */ -#define TARGET_MAP_GROWSDOWN 0x01000 /* stack-like segment */ -#define TARGET_MAP_DENYWRITE 0x02000 /* ETXTBSY */ -#define TARGET_MAP_EXECUTABLE 0x04000 /* mark it as an executable */ -#define TARGET_MAP_LOCKED 0x08000 /* lock the mapping */ -#define TARGET_MAP_NORESERVE 0x10000 /* no check for reservations */ -#define TARGET_MAP_POPULATE 0x20000 /* pop (prefault) pagetables */ -#define TARGET_MAP_NONBLOCK 0x40000 /* do not block on IO */ -#define TARGET_MAP_STACK 0x80000 /* ignored */ -#define TARGET_MAP_HUGETLB 0x100000 /* create a huge page mapping */ -#elif defined(TARGET_HPPA) -#define TARGET_MAP_ANONYMOUS 0x10 /* don't use a file */ -#define TARGET_MAP_FIXED 0x04 /* Interpret addr exactly */ -#define TARGET_MAP_GROWSDOWN 0x08000 /* stack-like segment */ -#define TARGET_MAP_DENYWRITE 0x00800 /* ETXTBSY */ -#define TARGET_MAP_EXECUTABLE 0x01000 /* mark it as an executable */ -#define TARGET_MAP_LOCKED 0x02000 /* lock the mapping */ -#define TARGET_MAP_NORESERVE 0x04000 /* no check for reservations */ -#define TARGET_MAP_POPULATE 0x10000 /* pop (prefault) pagetables */ -#define TARGET_MAP_NONBLOCK 0x20000 /* do not block on IO */ -#define TARGET_MAP_STACK 0x40000 /* ignored */ -#define TARGET_MAP_HUGETLB 0x80000 /* create a huge page mapping */ -#elif defined(TARGET_XTENSA) -#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */ -#define TARGET_MAP_ANONYMOUS 0x0800 /* don't use a file */ -#define TARGET_MAP_GROWSDOWN 0x1000 /* stack-like segment */ -#define TARGET_MAP_DENYWRITE 0x2000 /* ETXTBSY */ -#define TARGET_MAP_EXECUTABLE 0x4000 /* mark it as an executable */ -#define TARGET_MAP_LOCKED 0x8000 /* pages are locked */ -#define TARGET_MAP_NORESERVE 0x0400 /* don't check for reservations */ -#define TARGET_MAP_POPULATE 0x10000 /* populate (prefault) pagetables */ -#define TARGET_MAP_NONBLOCK 0x20000 /* do not block on IO */ -#define TARGET_MAP_STACK 0x40000 -#define TARGET_MAP_HUGETLB 0x80000 /* create a huge page mapping */ -#elif defined(TARGET_E2K) -#define TARGET_MAP_ANONYMOUS 0x000010 /* don't use a file */ -#define TARGET_MAP_FIXED 0x000100 /* Interpret addr exactly */ -#define TARGET_MAP_DENYWRITE 0x000800 /* ETXTBSY */ -#define TARGET_MAP_GROWSDOWN 0x001000 /* stack-like segment */ -#define TARGET_MAP_GROWSUP 0x002000 /* register stack-like segment */ -#define TARGET_MAP_EXECUTABLE 0x004000 /* mark it as an executable */ -#define TARGET_MAP_LOCKED 0x008000 /* pages are locked */ -#define TARGET_MAP_NORESERVE 0x010000 /* don't check for reservations */ -#define TARGET_MAP_POPULATE 0x020000 /* populate (prefault) pagetables */ -#define TARGET_MAP_NONBLOCK 0x040000 /* do not block on IO */ -#define TARGET_MAP_FIRST32 0x080000 /* in protected mode map in */ - /* first 2 ** 32 area */ -#define TARGET_MAP_WRITECOMBINED 0x100000 /* Write combine */ -#define TARGET_MAP_HUGETLB 0x200000 /* create a huge page mapping */ -#define TARGET_MAP_FIXED_NOREPLACE 0x400000 /* MAP_FIXED which doesn't unmap */ - /* underlying mapping */ -#define TARGET_MAP_STACK TARGET_MAP_GROWSDOWN - -#else -#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */ -#define TARGET_MAP_ANONYMOUS 0x20 /* don't use a file */ -#define TARGET_MAP_GROWSDOWN 0x0100 /* stack-like segment */ -#define TARGET_MAP_DENYWRITE 0x0800 /* ETXTBSY */ -#define TARGET_MAP_EXECUTABLE 0x1000 /* mark it as an executable */ -#define TARGET_MAP_LOCKED 0x2000 /* pages are locked */ -#define TARGET_MAP_NORESERVE 0x4000 /* don't check for reservations */ -#define TARGET_MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ -#define TARGET_MAP_NONBLOCK 0x10000 /* do not block on IO */ -#define TARGET_MAP_STACK 0x20000 /* ignored */ -#define TARGET_MAP_HUGETLB 0x40000 /* create a huge page mapping */ -#define TARGET_MAP_UNINITIALIZED 0x4000000 /* for anonymous mmap, memory could be uninitialized */ -#endif - -#if (defined(TARGET_I386) && defined(TARGET_ABI32)) \ - || (defined(TARGET_ARM) && defined(TARGET_ABI32)) \ +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) \ + || (defined(TARGET_ARM) && defined(TARGET_ABI32)) \ || defined(TARGET_CRIS) #define TARGET_STAT_HAVE_NSEC struct target_stat { - unsigned short st_dev; - unsigned short __pad1; - abi_ulong st_ino; - unsigned short st_mode; - unsigned short st_nlink; - unsigned short st_uid; - unsigned short st_gid; - unsigned short st_rdev; - unsigned short __pad2; - abi_ulong st_size; - abi_ulong st_blksize; - abi_ulong st_blocks; - abi_ulong target_st_atime; - abi_ulong target_st_atime_nsec; - abi_ulong target_st_mtime; - abi_ulong target_st_mtime_nsec; - abi_ulong target_st_ctime; - abi_ulong target_st_ctime_nsec; - abi_ulong __unused4; - abi_ulong __unused5; + abi_ushort st_dev; + abi_ushort __pad1; + abi_ulong st_ino; + abi_ushort st_mode; + abi_ushort st_nlink; + abi_ushort st_uid; + abi_ushort st_gid; + abi_ushort st_rdev; + abi_ushort __pad2; + abi_ulong st_size; + abi_ulong st_blksize; + abi_ulong st_blocks; + abi_ulong target_st_atime; + abi_ulong target_st_atime_nsec; + abi_ulong target_st_mtime; + abi_ulong target_st_mtime_nsec; + abi_ulong target_st_ctime; + abi_ulong target_st_ctime_nsec; + abi_ulong __unused4; + abi_ulong __unused5; }; /* This matches struct stat64 in glibc2.1, hence the absolutely @@ -1393,182 +1265,182 @@ struct target_stat { */ #define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { - unsigned short st_dev; - unsigned char __pad0[10]; + abi_ushort st_dev; + unsigned char __pad0[10]; -#define TARGET_STAT64_HAS_BROKEN_ST_INO 1 - abi_ulong __st_ino; +#define TARGET_STAT64_HAS_BROKEN_ST_INO 1 + abi_ulong __st_ino; - unsigned int st_mode; - unsigned int st_nlink; + abi_uint st_mode; + abi_uint st_nlink; - abi_ulong st_uid; - abi_ulong st_gid; + abi_ulong st_uid; + abi_ulong st_gid; - unsigned short st_rdev; - unsigned char __pad3[10]; + abi_ushort st_rdev; + unsigned char __pad3[10]; - long long st_size; - abi_ulong st_blksize; + abi_llong st_size; + abi_ulong st_blksize; - abi_ulong st_blocks; /* Number 512-byte blocks allocated. */ - abi_ulong __pad4; /* future possible st_blocks high bits */ + abi_ulong st_blocks; /* Number 512-byte blocks allocated. */ + abi_ulong __pad4; /* future possible st_blocks high bits */ - abi_ulong target_st_atime; - abi_ulong target_st_atime_nsec; + abi_ulong target_st_atime; + abi_ulong target_st_atime_nsec; - abi_ulong target_st_mtime; - abi_ulong target_st_mtime_nsec; + abi_ulong target_st_mtime; + abi_ulong target_st_mtime_nsec; - abi_ulong target_st_ctime; - abi_ulong target_st_ctime_nsec; + abi_ulong target_st_ctime; + abi_ulong target_st_ctime_nsec; - unsigned long long st_ino; + abi_ullong st_ino; } QEMU_PACKED; #ifdef TARGET_ARM #define TARGET_HAS_STRUCT_STAT64 struct target_eabi_stat64 { - unsigned long long st_dev; - unsigned int __pad1; - abi_ulong __st_ino; - unsigned int st_mode; - unsigned int st_nlink; + abi_ullong st_dev; + abi_uint __pad1; + abi_ulong __st_ino; + abi_uint st_mode; + abi_uint st_nlink; - abi_ulong st_uid; - abi_ulong st_gid; + abi_ulong st_uid; + abi_ulong st_gid; - unsigned long long st_rdev; - unsigned int __pad2[2]; + abi_ullong st_rdev; + abi_uint __pad2[2]; - long long st_size; - abi_ulong st_blksize; - unsigned int __pad3; - unsigned long long st_blocks; + abi_llong st_size; + abi_ulong st_blksize; + abi_uint __pad3; + abi_ullong st_blocks; - abi_ulong target_st_atime; - abi_ulong target_st_atime_nsec; + abi_ulong target_st_atime; + abi_ulong target_st_atime_nsec; - abi_ulong target_st_mtime; - abi_ulong target_st_mtime_nsec; + abi_ulong target_st_mtime; + abi_ulong target_st_mtime_nsec; - abi_ulong target_st_ctime; - abi_ulong target_st_ctime_nsec; + abi_ulong target_st_ctime; + abi_ulong target_st_ctime_nsec; - unsigned long long st_ino; + abi_ullong st_ino; } QEMU_PACKED; #endif #elif defined(TARGET_SPARC64) && !defined(TARGET_ABI32) struct target_stat { - unsigned int st_dev; - abi_ulong st_ino; - unsigned int st_mode; - unsigned int st_nlink; - unsigned int st_uid; - unsigned int st_gid; - unsigned int st_rdev; - abi_long st_size; - abi_long target_st_atime; - abi_long target_st_mtime; - abi_long target_st_ctime; - abi_long st_blksize; - abi_long st_blocks; - abi_ulong __unused4[2]; + abi_uint st_dev; + abi_ulong st_ino; + abi_uint st_mode; + abi_uint st_nlink; + abi_uint st_uid; + abi_uint st_gid; + abi_uint st_rdev; + abi_long st_size; + abi_long target_st_atime; + abi_long target_st_mtime; + abi_long target_st_ctime; + abi_long st_blksize; + abi_long st_blocks; + abi_ulong __unused4[2]; }; #define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { - unsigned char __pad0[6]; - unsigned short st_dev; + unsigned char __pad0[6]; + abi_ushort st_dev; - uint64_t st_ino; - uint64_t st_nlink; + abi_ullong st_ino; + abi_ullong st_nlink; - unsigned int st_mode; + abi_uint st_mode; - unsigned int st_uid; - unsigned int st_gid; + abi_uint st_uid; + abi_uint st_gid; - unsigned char __pad2[6]; - unsigned short st_rdev; + unsigned char __pad2[6]; + abi_ushort st_rdev; - int64_t st_size; - int64_t st_blksize; + abi_llong st_size; + abi_llong st_blksize; - unsigned char __pad4[4]; - unsigned int st_blocks; + unsigned char __pad4[4]; + abi_uint st_blocks; - abi_ulong target_st_atime; - abi_ulong target_st_atime_nsec; + abi_ulong target_st_atime; + abi_ulong target_st_atime_nsec; - abi_ulong target_st_mtime; - abi_ulong target_st_mtime_nsec; + abi_ulong target_st_mtime; + abi_ulong target_st_mtime_nsec; - abi_ulong target_st_ctime; - abi_ulong target_st_ctime_nsec; + abi_ulong target_st_ctime; + abi_ulong target_st_ctime_nsec; - abi_ulong __unused4[3]; + abi_ulong __unused4[3]; }; #elif defined(TARGET_SPARC) #define TARGET_STAT_HAVE_NSEC struct target_stat { - unsigned short st_dev; - abi_ulong st_ino; - unsigned short st_mode; - short st_nlink; - unsigned short st_uid; - unsigned short st_gid; - unsigned short st_rdev; - abi_long st_size; - abi_long target_st_atime; - abi_ulong target_st_atime_nsec; - abi_long target_st_mtime; - abi_ulong target_st_mtime_nsec; - abi_long target_st_ctime; - abi_ulong target_st_ctime_nsec; - abi_long st_blksize; - abi_long st_blocks; - abi_ulong __unused1[2]; + abi_ushort st_dev; + abi_ulong st_ino; + abi_ushort st_mode; + abi_short st_nlink; + abi_ushort st_uid; + abi_ushort st_gid; + abi_ushort st_rdev; + abi_long st_size; + abi_long target_st_atime; + abi_ulong target_st_atime_nsec; + abi_long target_st_mtime; + abi_ulong target_st_mtime_nsec; + abi_long target_st_ctime; + abi_ulong target_st_ctime_nsec; + abi_long st_blksize; + abi_long st_blocks; + abi_ulong __unused1[2]; }; #define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { - unsigned char __pad0[6]; - unsigned short st_dev; + unsigned char __pad0[6]; + abi_ushort st_dev; - uint64_t st_ino; + abi_ullong st_ino; - unsigned int st_mode; - unsigned int st_nlink; + abi_uint st_mode; + abi_uint st_nlink; - unsigned int st_uid; - unsigned int st_gid; + abi_uint st_uid; + abi_uint st_gid; - unsigned char __pad2[6]; - unsigned short st_rdev; + unsigned char __pad2[6]; + abi_ushort st_rdev; - unsigned char __pad3[8]; + unsigned char __pad3[8]; - int64_t st_size; - unsigned int st_blksize; + abi_llong st_size; + abi_uint st_blksize; - unsigned char __pad4[8]; - unsigned int st_blocks; + unsigned char __pad4[8]; + abi_uint st_blocks; - unsigned int target_st_atime; - unsigned int target_st_atime_nsec; + abi_uint target_st_atime; + abi_uint target_st_atime_nsec; - unsigned int target_st_mtime; - unsigned int target_st_mtime_nsec; + abi_uint target_st_mtime; + abi_uint target_st_mtime_nsec; - unsigned int target_st_ctime; - unsigned int target_st_ctime_nsec; + abi_uint target_st_ctime; + abi_uint target_st_ctime_nsec; - unsigned int __unused1; - unsigned int __unused2; + abi_uint __unused1; + abi_uint __unused2; }; #elif defined(TARGET_E2K) @@ -1626,57 +1498,57 @@ struct target_stat64 { #define TARGET_STAT_HAVE_NSEC struct target_stat { - abi_ulong st_dev; - abi_ulong st_ino; + abi_ulong st_dev; + abi_ulong st_ino; #if defined(TARGET_PPC64) - abi_ulong st_nlink; - unsigned int st_mode; + abi_ulong st_nlink; + abi_uint st_mode; #else - unsigned int st_mode; - unsigned short st_nlink; + abi_uint st_mode; + abi_ushort st_nlink; #endif - unsigned int st_uid; - unsigned int st_gid; - abi_ulong st_rdev; - abi_ulong st_size; - abi_ulong st_blksize; - abi_ulong st_blocks; - abi_ulong target_st_atime; - abi_ulong target_st_atime_nsec; - abi_ulong target_st_mtime; - abi_ulong target_st_mtime_nsec; - abi_ulong target_st_ctime; - abi_ulong target_st_ctime_nsec; - abi_ulong __unused4; - abi_ulong __unused5; + abi_uint st_uid; + abi_uint st_gid; + abi_ulong st_rdev; + abi_ulong st_size; + abi_ulong st_blksize; + abi_ulong st_blocks; + abi_ulong target_st_atime; + abi_ulong target_st_atime_nsec; + abi_ulong target_st_mtime; + abi_ulong target_st_mtime_nsec; + abi_ulong target_st_ctime; + abi_ulong target_st_ctime_nsec; + abi_ulong __unused4; + abi_ulong __unused5; #if defined(TARGET_PPC64) - abi_ulong __unused6; + abi_ulong __unused6; #endif }; #if !defined(TARGET_PPC64) #define TARGET_HAS_STRUCT_STAT64 struct QEMU_PACKED target_stat64 { - unsigned long long st_dev; - unsigned long long st_ino; - unsigned int st_mode; - unsigned int st_nlink; - unsigned int st_uid; - unsigned int st_gid; - unsigned long long st_rdev; - unsigned long long __pad0; - long long st_size; - int st_blksize; - unsigned int __pad1; - long long st_blocks; /* Number 512-byte blocks allocated. */ - int target_st_atime; - unsigned int target_st_atime_nsec; - int target_st_mtime; - unsigned int target_st_mtime_nsec; - int target_st_ctime; - unsigned int target_st_ctime_nsec; - unsigned int __unused4; - unsigned int __unused5; + abi_ullong st_dev; + abi_ullong st_ino; + abi_uint st_mode; + abi_uint st_nlink; + abi_uint st_uid; + abi_uint st_gid; + abi_ullong st_rdev; + abi_ullong __pad0; + abi_llong st_size; + abi_int st_blksize; + abi_uint __pad1; + abi_llong st_blocks; /* Number 512-byte blocks allocated. */ + abi_int target_st_atime; + abi_uint target_st_atime_nsec; + abi_int target_st_mtime; + abi_uint target_st_mtime_nsec; + abi_int target_st_ctime; + abi_uint target_st_ctime_nsec; + abi_uint __unused4; + abi_uint __unused5; }; #endif @@ -1684,78 +1556,78 @@ struct QEMU_PACKED target_stat64 { #define TARGET_STAT_HAVE_NSEC struct target_stat { - abi_ulong st_dev; - abi_ulong st_ino; - unsigned int st_mode; - unsigned short st_nlink; - unsigned int st_uid; - unsigned int st_gid; - abi_ulong st_rdev; - abi_ulong st_size; - abi_ulong st_blksize; - abi_ulong st_blocks; - abi_ulong target_st_atime; - abi_ulong target_st_atime_nsec; - abi_ulong target_st_mtime; - abi_ulong target_st_mtime_nsec; - abi_ulong target_st_ctime; - abi_ulong target_st_ctime_nsec; - abi_ulong __unused4; - abi_ulong __unused5; + abi_ulong st_dev; + abi_ulong st_ino; + abi_uint st_mode; + abi_ushort st_nlink; + abi_uint st_uid; + abi_uint st_gid; + abi_ulong st_rdev; + abi_ulong st_size; + abi_ulong st_blksize; + abi_ulong st_blocks; + abi_ulong target_st_atime; + abi_ulong target_st_atime_nsec; + abi_ulong target_st_mtime; + abi_ulong target_st_mtime_nsec; + abi_ulong target_st_ctime; + abi_ulong target_st_ctime_nsec; + abi_ulong __unused4; + abi_ulong __unused5; }; /* FIXME: Microblaze no-mmu user-space has a difference stat64 layout... */ #define TARGET_HAS_STRUCT_STAT64 struct QEMU_PACKED target_stat64 { - uint64_t st_dev; + abi_ullong st_dev; #define TARGET_STAT64_HAS_BROKEN_ST_INO 1 - uint32_t pad0; - uint32_t __st_ino; + abi_uint pad0; + abi_uint __st_ino; - uint32_t st_mode; - uint32_t st_nlink; - uint32_t st_uid; - uint32_t st_gid; - uint64_t st_rdev; - uint64_t __pad1; + abi_uint st_mode; + abi_uint st_nlink; + abi_uint st_uid; + abi_uint st_gid; + abi_ullong st_rdev; + abi_ullong __pad1; - int64_t st_size; - int32_t st_blksize; - uint32_t __pad2; - int64_t st_blocks; /* Number 512-byte blocks allocated. */ + abi_llong st_size; + abi_int st_blksize; + abi_uint __pad2; + abi_llong st_blocks; - int target_st_atime; - unsigned int target_st_atime_nsec; - int target_st_mtime; - unsigned int target_st_mtime_nsec; - int target_st_ctime; - unsigned int target_st_ctime_nsec; - uint64_t st_ino; + abi_int target_st_atime; + abi_uint target_st_atime_nsec; + abi_int target_st_mtime; + abi_uint target_st_mtime_nsec; + abi_int target_st_ctime; + abi_uint target_st_ctime_nsec; + abi_ullong st_ino; }; #elif defined(TARGET_M68K) struct target_stat { - unsigned short st_dev; - unsigned short __pad1; - abi_ulong st_ino; - unsigned short st_mode; - unsigned short st_nlink; - unsigned short st_uid; - unsigned short st_gid; - unsigned short st_rdev; - unsigned short __pad2; - abi_ulong st_size; - abi_ulong st_blksize; - abi_ulong st_blocks; - abi_ulong target_st_atime; - abi_ulong __unused1; - abi_ulong target_st_mtime; - abi_ulong __unused2; - abi_ulong target_st_ctime; - abi_ulong __unused3; - abi_ulong __unused4; - abi_ulong __unused5; + abi_ushort st_dev; + abi_ushort __pad1; + abi_ulong st_ino; + abi_ushort st_mode; + abi_ushort st_nlink; + abi_ushort st_uid; + abi_ushort st_gid; + abi_ushort st_rdev; + abi_ushort __pad2; + abi_ulong st_size; + abi_ulong st_blksize; + abi_ulong st_blocks; + abi_ulong target_st_atime; + abi_ulong __unused1; + abi_ulong target_st_mtime; + abi_ulong __unused2; + abi_ulong target_st_ctime; + abi_ulong __unused3; + abi_ulong __unused4; + abi_ulong __unused5; }; /* This matches struct stat64 in glibc2.1, hence the absolutely @@ -1763,37 +1635,37 @@ struct target_stat { */ #define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { - unsigned long long st_dev; - unsigned char __pad1[2]; + abi_ullong st_dev; + unsigned char __pad1[2]; -#define TARGET_STAT64_HAS_BROKEN_ST_INO 1 - abi_ulong __st_ino; +#define TARGET_STAT64_HAS_BROKEN_ST_INO 1 + abi_ulong __st_ino; - unsigned int st_mode; - unsigned int st_nlink; + abi_uint st_mode; + abi_uint st_nlink; - abi_ulong st_uid; - abi_ulong st_gid; + abi_ulong st_uid; + abi_ulong st_gid; - unsigned long long st_rdev; - unsigned char __pad3[2]; + abi_ullong st_rdev; + unsigned char __pad3[2]; - long long st_size; - abi_ulong st_blksize; + abi_llong st_size; + abi_ulong st_blksize; - abi_ulong __pad4; /* future possible st_blocks high bits */ - abi_ulong st_blocks; /* Number 512-byte blocks allocated. */ + abi_ulong __pad4; /* future possible st_blocks high bits */ + abi_ulong st_blocks; /* Number 512-byte blocks allocated. */ - abi_ulong target_st_atime; - abi_ulong target_st_atime_nsec; + abi_ulong target_st_atime; + abi_ulong target_st_atime_nsec; - abi_ulong target_st_mtime; - abi_ulong target_st_mtime_nsec; + abi_ulong target_st_mtime; + abi_ulong target_st_mtime_nsec; - abi_ulong target_st_ctime; - abi_ulong target_st_ctime_nsec; + abi_ulong target_st_ctime; + abi_ulong target_st_ctime_nsec; - unsigned long long st_ino; + abi_ullong st_ino; } QEMU_PACKED; #elif defined(TARGET_ABI_MIPSN64) @@ -1801,94 +1673,94 @@ struct target_stat64 { #define TARGET_STAT_HAVE_NSEC /* The memory layout is the same as of struct stat64 of the 32-bit kernel. */ struct target_stat { - unsigned int st_dev; - unsigned int st_pad0[3]; /* Reserved for st_dev expansion */ + abi_uint st_dev; + abi_uint st_pad0[3]; /* Reserved for st_dev expansion */ - abi_ulong st_ino; + abi_ulong st_ino; - unsigned int st_mode; - unsigned int st_nlink; + abi_uint st_mode; + abi_uint st_nlink; - int st_uid; - int st_gid; + abi_int st_uid; + abi_int st_gid; - unsigned int st_rdev; - unsigned int st_pad1[3]; /* Reserved for st_rdev expansion */ + abi_uint st_rdev; + abi_uint st_pad1[3]; /* Reserved for st_rdev expansion */ - abi_ulong st_size; + abi_ulong st_size; - /* - * Actually this should be timestruc_t st_atime, st_mtime and st_ctime - * but we don't have it under Linux. - */ - unsigned int target_st_atime; - unsigned int target_st_atime_nsec; + /* + * Actually this should be timestruc_t st_atime, st_mtime and st_ctime + * but we don't have it under Linux. + */ + abi_uint target_st_atime; + abi_uint target_st_atime_nsec; - unsigned int target_st_mtime; - unsigned int target_st_mtime_nsec; + abi_uint target_st_mtime; + abi_uint target_st_mtime_nsec; - unsigned int target_st_ctime; - unsigned int target_st_ctime_nsec; + abi_uint target_st_ctime; + abi_uint target_st_ctime_nsec; - unsigned int st_blksize; - unsigned int st_pad2; + abi_uint st_blksize; + abi_uint st_pad2; - abi_ulong st_blocks; + abi_ulong st_blocks; }; #elif defined(TARGET_ABI_MIPSN32) #define TARGET_STAT_HAVE_NSEC struct target_stat { - abi_ulong st_dev; - abi_ulong st_pad0[3]; /* Reserved for st_dev expansion */ - uint64_t st_ino; - unsigned int st_mode; - unsigned int st_nlink; - int st_uid; - int st_gid; - abi_ulong st_rdev; - abi_ulong st_pad1[3]; /* Reserved for st_rdev expansion */ - int64_t st_size; - abi_long target_st_atime; - abi_ulong target_st_atime_nsec; /* Reserved for st_atime expansion */ - abi_long target_st_mtime; - abi_ulong target_st_mtime_nsec; /* Reserved for st_mtime expansion */ - abi_long target_st_ctime; - abi_ulong target_st_ctime_nsec; /* Reserved for st_ctime expansion */ - abi_ulong st_blksize; - abi_ulong st_pad2; - int64_t st_blocks; + abi_ulong st_dev; + abi_ulong st_pad0[3]; /* Reserved for st_dev expansion */ + abi_ullong st_ino; + abi_uint st_mode; + abi_uint st_nlink; + abi_int st_uid; + abi_int st_gid; + abi_ulong st_rdev; + abi_ulong st_pad1[3]; /* Reserved for st_rdev expansion */ + abi_llong st_size; + abi_long target_st_atime; + abi_ulong target_st_atime_nsec; /* Reserved for st_atime expansion */ + abi_long target_st_mtime; + abi_ulong target_st_mtime_nsec; /* Reserved for st_mtime expansion */ + abi_long target_st_ctime; + abi_ulong target_st_ctime_nsec; /* Reserved for st_ctime expansion */ + abi_ulong st_blksize; + abi_ulong st_pad2; + abi_llong st_blocks; }; #elif defined(TARGET_ABI_MIPSO32) #define TARGET_STAT_HAVE_NSEC struct target_stat { - unsigned st_dev; - abi_long st_pad1[3]; /* Reserved for network id */ - abi_ulong st_ino; - unsigned int st_mode; - unsigned int st_nlink; - int st_uid; - int st_gid; - unsigned st_rdev; - abi_long st_pad2[2]; - abi_long st_size; - abi_long st_pad3; - /* - * Actually this should be timestruc_t st_atime, st_mtime and st_ctime - * but we don't have it under Linux. - */ - abi_long target_st_atime; - abi_long target_st_atime_nsec; - abi_long target_st_mtime; - abi_long target_st_mtime_nsec; - abi_long target_st_ctime; - abi_long target_st_ctime_nsec; - abi_long st_blksize; - abi_long st_blocks; - abi_long st_pad4[14]; + abi_uint st_dev; + abi_long st_pad1[3]; /* Reserved for network id */ + abi_ulong st_ino; + abi_uint st_mode; + abi_uint st_nlink; + abi_int st_uid; + abi_int st_gid; + abi_uint st_rdev; + abi_long st_pad2[2]; + abi_long st_size; + abi_long st_pad3; + /* + * Actually this should be timestruc_t st_atime, st_mtime and st_ctime + * but we don't have it under Linux. + */ + abi_long target_st_atime; + abi_long target_st_atime_nsec; + abi_long target_st_mtime; + abi_long target_st_mtime_nsec; + abi_long target_st_ctime; + abi_long target_st_ctime_nsec; + abi_long st_blksize; + abi_long st_blocks; + abi_long st_pad4[14]; }; /* @@ -1899,107 +1771,107 @@ struct target_stat { #define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { - abi_ulong st_dev; - abi_ulong st_pad0[3]; /* Reserved for st_dev expansion */ + abi_ulong st_dev; + abi_ulong st_pad0[3]; /* Reserved for st_dev expansion */ - uint64_t st_ino; + abi_ullong st_ino; - unsigned int st_mode; - unsigned int st_nlink; + abi_uint st_mode; + abi_uint st_nlink; - int st_uid; - int st_gid; + abi_int st_uid; + abi_int st_gid; - abi_ulong st_rdev; - abi_ulong st_pad1[3]; /* Reserved for st_rdev expansion */ + abi_ulong st_rdev; + abi_ulong st_pad1[3]; /* Reserved for st_rdev expansion */ - int64_t st_size; + abi_llong st_size; - /* - * Actually this should be timestruc_t st_atime, st_mtime and st_ctime - * but we don't have it under Linux. - */ - abi_long target_st_atime; - abi_ulong target_st_atime_nsec; /* Reserved for st_atime expansion */ + /* + * Actually this should be timestruc_t st_atime, st_mtime and st_ctime + * but we don't have it under Linux. + */ + abi_long target_st_atime; + abi_ulong target_st_atime_nsec; /* Reserved for st_atime expansion */ - abi_long target_st_mtime; - abi_ulong target_st_mtime_nsec; /* Reserved for st_mtime expansion */ + abi_long target_st_mtime; + abi_ulong target_st_mtime_nsec; /* Reserved for st_mtime expansion */ - abi_long target_st_ctime; - abi_ulong target_st_ctime_nsec; /* Reserved for st_ctime expansion */ + abi_long target_st_ctime; + abi_ulong target_st_ctime_nsec; /* Reserved for st_ctime expansion */ - abi_ulong st_blksize; - abi_ulong st_pad2; + abi_ulong st_blksize; + abi_ulong st_pad2; - int64_t st_blocks; + abi_llong st_blocks; }; #elif defined(TARGET_ALPHA) struct target_stat { - unsigned int st_dev; - unsigned int st_ino; - unsigned int st_mode; - unsigned int st_nlink; - unsigned int st_uid; - unsigned int st_gid; - unsigned int st_rdev; - abi_long st_size; - abi_ulong target_st_atime; - abi_ulong target_st_mtime; - abi_ulong target_st_ctime; - unsigned int st_blksize; - unsigned int st_blocks; - unsigned int st_flags; - unsigned int st_gen; + abi_uint st_dev; + abi_uint st_ino; + abi_uint st_mode; + abi_uint st_nlink; + abi_uint st_uid; + abi_uint st_gid; + abi_uint st_rdev; + abi_long st_size; + abi_ulong target_st_atime; + abi_ulong target_st_mtime; + abi_ulong target_st_ctime; + abi_uint st_blksize; + abi_uint st_blocks; + abi_uint st_flags; + abi_uint st_gen; }; #define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { - abi_ulong st_dev; - abi_ulong st_ino; - abi_ulong st_rdev; - abi_long st_size; - abi_ulong st_blocks; + abi_ulong st_dev; + abi_ulong st_ino; + abi_ulong st_rdev; + abi_long st_size; + abi_ulong st_blocks; - unsigned int st_mode; - unsigned int st_uid; - unsigned int st_gid; - unsigned int st_blksize; - unsigned int st_nlink; - unsigned int __pad0; + abi_uint st_mode; + abi_uint st_uid; + abi_uint st_gid; + abi_uint st_blksize; + abi_uint st_nlink; + abi_uint __pad0; - abi_ulong target_st_atime; - abi_ulong target_st_atime_nsec; - abi_ulong target_st_mtime; - abi_ulong target_st_mtime_nsec; - abi_ulong target_st_ctime; - abi_ulong target_st_ctime_nsec; - abi_long __unused[3]; + abi_ulong target_st_atime; + abi_ulong target_st_atime_nsec; + abi_ulong target_st_mtime; + abi_ulong target_st_mtime_nsec; + abi_ulong target_st_ctime; + abi_ulong target_st_ctime_nsec; + abi_long __unused[3]; }; #elif defined(TARGET_SH4) #define TARGET_STAT_HAVE_NSEC struct target_stat { - abi_ulong st_dev; - abi_ulong st_ino; - unsigned short st_mode; - unsigned short st_nlink; - unsigned short st_uid; - unsigned short st_gid; - abi_ulong st_rdev; - abi_ulong st_size; - abi_ulong st_blksize; - abi_ulong st_blocks; - abi_ulong target_st_atime; - abi_ulong target_st_atime_nsec; - abi_ulong target_st_mtime; - abi_ulong target_st_mtime_nsec; - abi_ulong target_st_ctime; - abi_ulong target_st_ctime_nsec; - abi_ulong __unused4; - abi_ulong __unused5; + abi_ulong st_dev; + abi_ulong st_ino; + abi_ushort st_mode; + abi_ushort st_nlink; + abi_ushort st_uid; + abi_ushort st_gid; + abi_ulong st_rdev; + abi_ulong st_size; + abi_ulong st_blksize; + abi_ulong st_blocks; + abi_ulong target_st_atime; + abi_ulong target_st_atime_nsec; + abi_ulong target_st_mtime; + abi_ulong target_st_mtime_nsec; + abi_ulong target_st_ctime; + abi_ulong target_st_ctime_nsec; + abi_ulong __unused4; + abi_ulong __unused5; }; /* This matches struct stat64 in glibc2.1, hence the absolutely @@ -2007,72 +1879,72 @@ struct target_stat { */ #define TARGET_HAS_STRUCT_STAT64 struct QEMU_PACKED target_stat64 { - unsigned long long st_dev; - unsigned char __pad0[4]; + abi_ullong st_dev; + unsigned char __pad0[4]; -#define TARGET_STAT64_HAS_BROKEN_ST_INO 1 - abi_ulong __st_ino; +#define TARGET_STAT64_HAS_BROKEN_ST_INO 1 + abi_ulong __st_ino; - unsigned int st_mode; - unsigned int st_nlink; + abi_uint st_mode; + abi_uint st_nlink; - abi_ulong st_uid; - abi_ulong st_gid; + abi_ulong st_uid; + abi_ulong st_gid; - unsigned long long st_rdev; - unsigned char __pad3[4]; + abi_ullong st_rdev; + unsigned char __pad3[4]; - long long st_size; - abi_ulong st_blksize; + abi_llong st_size; + abi_ulong st_blksize; - unsigned long long st_blocks; /* Number 512-byte blocks allocated. */ + abi_ullong st_blocks; /* Number 512-byte blocks allocated. */ - abi_ulong target_st_atime; - abi_ulong target_st_atime_nsec; + abi_ulong target_st_atime; + abi_ulong target_st_atime_nsec; - abi_ulong target_st_mtime; - abi_ulong target_st_mtime_nsec; + abi_ulong target_st_mtime; + abi_ulong target_st_mtime_nsec; - abi_ulong target_st_ctime; - abi_ulong target_st_ctime_nsec; + abi_ulong target_st_ctime; + abi_ulong target_st_ctime_nsec; - unsigned long long st_ino; + abi_ullong st_ino; }; #elif defined(TARGET_I386) && !defined(TARGET_ABI32) #define TARGET_STAT_HAVE_NSEC struct target_stat { - abi_ulong st_dev; - abi_ulong st_ino; - abi_ulong st_nlink; + abi_ulong st_dev; + abi_ulong st_ino; + abi_ulong st_nlink; - unsigned int st_mode; - unsigned int st_uid; - unsigned int st_gid; - unsigned int __pad0; - abi_ulong st_rdev; - abi_long st_size; - abi_long st_blksize; - abi_long st_blocks; /* Number 512-byte blocks allocated. */ + abi_uint st_mode; + abi_uint st_uid; + abi_uint st_gid; + abi_uint __pad0; + abi_ulong st_rdev; + abi_long st_size; + abi_long st_blksize; + abi_long st_blocks; /* Number 512-byte blocks allocated. */ - abi_ulong target_st_atime; - abi_ulong target_st_atime_nsec; - abi_ulong target_st_mtime; - abi_ulong target_st_mtime_nsec; - abi_ulong target_st_ctime; - abi_ulong target_st_ctime_nsec; + abi_ulong target_st_atime; + abi_ulong target_st_atime_nsec; + abi_ulong target_st_mtime; + abi_ulong target_st_mtime_nsec; + abi_ulong target_st_ctime; + abi_ulong target_st_ctime_nsec; - abi_long __unused[3]; + abi_long __unused[3]; }; #elif defined(TARGET_S390X) struct target_stat { abi_ulong st_dev; abi_ulong st_ino; abi_ulong st_nlink; - unsigned int st_mode; - unsigned int st_uid; - unsigned int st_gid; - unsigned int __pad1; + abi_uint st_mode; + abi_uint st_uid; + abi_uint st_gid; + abi_uint __pad1; abi_ulong st_rdev; abi_ulong st_size; abi_ulong target_st_atime; @@ -2090,15 +1962,15 @@ struct target_stat { struct target_stat { abi_ulong st_dev; abi_ulong st_ino; - unsigned int st_mode; - unsigned int st_nlink; - unsigned int st_uid; - unsigned int st_gid; + abi_uint st_mode; + abi_uint st_nlink; + abi_uint st_uid; + abi_uint st_gid; abi_ulong st_rdev; abi_ulong _pad1; abi_long st_size; - int st_blksize; - int __pad2; + abi_int st_blksize; + abi_int __pad2; abi_long st_blocks; abi_long target_st_atime; abi_ulong target_st_atime_nsec; @@ -2106,17 +1978,17 @@ struct target_stat { abi_ulong target_st_mtime_nsec; abi_long target_st_ctime; abi_ulong target_st_ctime_nsec; - unsigned int __unused[2]; + abi_uint __unused[2]; }; #elif defined(TARGET_XTENSA) #define TARGET_STAT_HAVE_NSEC struct target_stat { abi_ulong st_dev; abi_ulong st_ino; - unsigned int st_mode; - unsigned int st_nlink; - unsigned int st_uid; - unsigned int st_gid; + abi_uint st_mode; + abi_uint st_nlink; + abi_uint st_uid; + abi_uint st_gid; abi_ulong st_rdev; abi_long st_size; abi_ulong st_blksize; @@ -2133,17 +2005,17 @@ struct target_stat { #define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { - uint64_t st_dev; /* Device */ - uint64_t st_ino; /* File serial number */ - unsigned int st_mode; /* File mode. */ - unsigned int st_nlink; /* Link count. */ - unsigned int st_uid; /* User ID of the file's owner. */ - unsigned int st_gid; /* Group ID of the file's group. */ - uint64_t st_rdev; /* Device number, if device. */ - int64_t st_size; /* Size of file, in bytes. */ + abi_ullong st_dev; /* Device */ + abi_ullong st_ino; /* File serial number */ + abi_uint st_mode; /* File mode. */ + abi_uint st_nlink; /* Link count. */ + abi_uint st_uid; /* User ID of the file's owner. */ + abi_uint st_gid; /* Group ID of the file's group. */ + abi_ullong st_rdev; /* Device number, if device. */ + abi_llong st_size; /* Size of file, in bytes. */ abi_ulong st_blksize; /* Optimal block size for I/O. */ abi_ulong __unused2; - uint64_t st_blocks; /* Number 512-byte blocks allocated. */ + abi_ullong st_blocks; /* Number 512-byte blocks allocated. */ abi_ulong target_st_atime; /* Time of last access. */ abi_ulong target_st_atime_nsec; abi_ulong target_st_mtime; /* Time of last modification. */ @@ -2155,7 +2027,7 @@ struct target_stat64 { }; #elif defined(TARGET_OPENRISC) || defined(TARGET_NIOS2) \ - || defined(TARGET_RISCV) || defined(TARGET_HEXAGON) + || defined(TARGET_RISCV) || defined(TARGET_HEXAGON) /* These are the asm-generic versions of the stat and stat64 structures */ @@ -2163,15 +2035,15 @@ struct target_stat64 { struct target_stat { abi_ulong st_dev; abi_ulong st_ino; - unsigned int st_mode; - unsigned int st_nlink; - unsigned int st_uid; - unsigned int st_gid; + abi_uint st_mode; + abi_uint st_nlink; + abi_uint st_uid; + abi_uint st_gid; abi_ulong st_rdev; abi_ulong __pad1; abi_long st_size; - int st_blksize; - int __pad2; + abi_int st_blksize; + abi_int __pad2; abi_long st_blocks; abi_long target_st_atime; abi_ulong target_st_atime_nsec; @@ -2179,33 +2051,33 @@ struct target_stat { abi_ulong target_st_mtime_nsec; abi_long target_st_ctime; abi_ulong target_st_ctime_nsec; - unsigned int __unused4; - unsigned int __unused5; + abi_uint __unused4; + abi_uint __unused5; }; #if !defined(TARGET_RISCV64) #define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { - uint64_t st_dev; - uint64_t st_ino; - unsigned int st_mode; - unsigned int st_nlink; - unsigned int st_uid; - unsigned int st_gid; - uint64_t st_rdev; - uint64_t __pad1; - int64_t st_size; - int st_blksize; - int __pad2; - int64_t st_blocks; - int target_st_atime; - unsigned int target_st_atime_nsec; - int target_st_mtime; - unsigned int target_st_mtime_nsec; - int target_st_ctime; - unsigned int target_st_ctime_nsec; - unsigned int __unused4; - unsigned int __unused5; + abi_ullong st_dev; + abi_ullong st_ino; + abi_uint st_mode; + abi_uint st_nlink; + abi_uint st_uid; + abi_uint st_gid; + abi_ullong st_rdev; + abi_ullong __pad1; + abi_llong st_size; + abi_int st_blksize; + abi_int __pad2; + abi_llong st_blocks; + abi_int target_st_atime; + abi_uint target_st_atime_nsec; + abi_int target_st_mtime; + abi_uint target_st_mtime_nsec; + abi_int target_st_ctime; + abi_uint target_st_ctime_nsec; + abi_uint __unused4; + abi_uint __unused5; }; #endif @@ -2245,149 +2117,154 @@ struct target_stat { #define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { - uint64_t st_dev; + abi_ullong st_dev; abi_uint _pad1; abi_uint _res1; abi_uint st_mode; abi_uint st_nlink; abi_uint st_uid; abi_uint st_gid; - uint64_t st_rdev; + abi_ullong st_rdev; abi_uint _pad2; - int64_t st_size; + abi_llong st_size; abi_int st_blksize; - int64_t st_blocks; + abi_llong st_blocks; abi_int target_st_atime; abi_uint target_st_atime_nsec; abi_int target_st_mtime; abi_uint target_st_mtime_nsec; abi_int target_st_ctime; abi_uint target_st_ctime_nsec; - uint64_t st_ino; + abi_ullong st_ino; }; +#elif defined(TARGET_LOONGARCH64) + +/* LoongArch no newfstatat/fstat syscall. */ + #else #error unsupported CPU #endif typedef struct { - int val[2]; + abi_int val[2]; } target_fsid_t; #ifdef TARGET_MIPS #ifdef TARGET_ABI_MIPSN32 struct target_statfs { - int32_t f_type; - int32_t f_bsize; - int32_t f_frsize; /* Fragment size - unsupported */ - int32_t f_blocks; - int32_t f_bfree; - int32_t f_files; - int32_t f_ffree; - int32_t f_bavail; + abi_int f_type; + abi_int f_bsize; + abi_int f_frsize; /* Fragment size - unsupported */ + abi_int f_blocks; + abi_int f_bfree; + abi_int f_files; + abi_int f_ffree; + abi_int f_bavail; - /* Linux specials */ - target_fsid_t f_fsid; - int32_t f_namelen; - int32_t f_flags; - int32_t f_spare[5]; + /* Linux specials */ + target_fsid_t f_fsid; + abi_int f_namelen; + abi_int f_flags; + abi_int f_spare[5]; }; #else struct target_statfs { - abi_long f_type; - abi_long f_bsize; - abi_long f_frsize; /* Fragment size - unsupported */ - abi_long f_blocks; - abi_long f_bfree; - abi_long f_files; - abi_long f_ffree; - abi_long f_bavail; + abi_long f_type; + abi_long f_bsize; + abi_long f_frsize; /* Fragment size - unsupported */ + abi_long f_blocks; + abi_long f_bfree; + abi_long f_files; + abi_long f_ffree; + abi_long f_bavail; - /* Linux specials */ - target_fsid_t f_fsid; - abi_long f_namelen; - abi_long f_flags; - abi_long f_spare[5]; + /* Linux specials */ + target_fsid_t f_fsid; + abi_long f_namelen; + abi_long f_flags; + abi_long f_spare[5]; }; #endif struct target_statfs64 { - uint32_t f_type; - uint32_t f_bsize; - uint32_t f_frsize; /* Fragment size - unsupported */ - uint32_t __pad; - uint64_t f_blocks; - uint64_t f_bfree; - uint64_t f_files; - uint64_t f_ffree; - uint64_t f_bavail; - target_fsid_t f_fsid; - uint32_t f_namelen; - uint32_t f_flags; - uint32_t f_spare[5]; + abi_uint f_type; + abi_uint f_bsize; + abi_uint f_frsize; /* Fragment size - unsupported */ + abi_uint __pad; + abi_ullong f_blocks; + abi_ullong f_bfree; + abi_ullong f_files; + abi_ullong f_ffree; + abi_ullong f_bavail; + target_fsid_t f_fsid; + abi_uint f_namelen; + abi_uint f_flags; + abi_uint f_spare[5]; }; -#elif (defined(TARGET_PPC64) || defined(TARGET_X86_64) || \ - defined(TARGET_SPARC64) || defined(TARGET_AARCH64) || \ - defined(TARGET_RISCV)) && !defined(TARGET_ABI32) +#elif (defined(TARGET_PPC64) || defined(TARGET_X86_64) || \ + defined(TARGET_SPARC64) || defined(TARGET_AARCH64) || \ + defined(TARGET_RISCV) || defined(TARGET_LOONGARCH64)) && \ + !defined(TARGET_ABI32) struct target_statfs { - abi_long f_type; - abi_long f_bsize; - abi_long f_blocks; - abi_long f_bfree; - abi_long f_bavail; - abi_long f_files; - abi_long f_ffree; - target_fsid_t f_fsid; - abi_long f_namelen; - abi_long f_frsize; - abi_long f_flags; - abi_long f_spare[4]; + abi_long f_type; + abi_long f_bsize; + abi_long f_blocks; + abi_long f_bfree; + abi_long f_bavail; + abi_long f_files; + abi_long f_ffree; + target_fsid_t f_fsid; + abi_long f_namelen; + abi_long f_frsize; + abi_long f_flags; + abi_long f_spare[4]; }; struct target_statfs64 { - abi_long f_type; - abi_long f_bsize; - abi_long f_blocks; - abi_long f_bfree; - abi_long f_bavail; - abi_long f_files; - abi_long f_ffree; - target_fsid_t f_fsid; - abi_long f_namelen; - abi_long f_frsize; - abi_long f_flags; - abi_long f_spare[4]; + abi_long f_type; + abi_long f_bsize; + abi_long f_blocks; + abi_long f_bfree; + abi_long f_bavail; + abi_long f_files; + abi_long f_ffree; + target_fsid_t f_fsid; + abi_long f_namelen; + abi_long f_frsize; + abi_long f_flags; + abi_long f_spare[4]; }; #elif defined(TARGET_S390X) struct target_statfs { - int32_t f_type; - int32_t f_bsize; + abi_int f_type; + abi_int f_bsize; abi_long f_blocks; abi_long f_bfree; abi_long f_bavail; abi_long f_files; abi_long f_ffree; kernel_fsid_t f_fsid; - int32_t f_namelen; - int32_t f_frsize; - int32_t f_flags; - int32_t f_spare[4]; + abi_int f_namelen; + abi_int f_frsize; + abi_int f_flags; + abi_int f_spare[4]; }; struct target_statfs64 { - int32_t f_type; - int32_t f_bsize; + abi_int f_type; + abi_int f_bsize; abi_long f_blocks; abi_long f_bfree; abi_long f_bavail; abi_long f_files; abi_long f_ffree; kernel_fsid_t f_fsid; - int32_t f_namelen; - int32_t f_frsize; - int32_t f_flags; - int32_t f_spare[4]; + abi_int f_namelen; + abi_int f_frsize; + abi_int f_flags; + abi_int f_spare[4]; }; #elif defined(TARGET_E2K) struct target_statfs { @@ -2421,33 +2298,33 @@ struct target_statfs64 { }; #else struct target_statfs { - uint32_t f_type; - uint32_t f_bsize; - uint32_t f_blocks; - uint32_t f_bfree; - uint32_t f_bavail; - uint32_t f_files; - uint32_t f_ffree; - target_fsid_t f_fsid; - uint32_t f_namelen; - uint32_t f_frsize; - uint32_t f_flags; - uint32_t f_spare[4]; + abi_uint f_type; + abi_uint f_bsize; + abi_uint f_blocks; + abi_uint f_bfree; + abi_uint f_bavail; + abi_uint f_files; + abi_uint f_ffree; + target_fsid_t f_fsid; + abi_uint f_namelen; + abi_uint f_frsize; + abi_uint f_flags; + abi_uint f_spare[4]; }; struct target_statfs64 { - uint32_t f_type; - uint32_t f_bsize; - uint64_t f_blocks; - uint64_t f_bfree; - uint64_t f_bavail; - uint64_t f_files; - uint64_t f_ffree; - target_fsid_t f_fsid; - uint32_t f_namelen; - uint32_t f_frsize; - uint32_t f_flags; - uint32_t f_spare[4]; + abi_uint f_type; + abi_uint f_bsize; + abi_ullong f_blocks; + abi_ullong f_bfree; + abi_ullong f_bavail; + abi_ullong f_files; + abi_ullong f_ffree; + target_fsid_t f_fsid; + abi_uint f_namelen; + abi_uint f_frsize; + abi_uint f_flags; + abi_uint f_spare[4]; }; #endif @@ -2465,7 +2342,7 @@ struct target_statfs64 { /* soundcard defines */ /* XXX: convert them all to arch independent entries */ -#define TARGET_SNDCTL_COPR_HALT TARGET_IOWR('C', 7, int); +#define TARGET_SNDCTL_COPR_HALT TARGET_IOWR('C', 7, abi_int); #define TARGET_SNDCTL_COPR_LOAD 0xcfb04301 #define TARGET_SNDCTL_COPR_RCODE 0xc0144303 #define TARGET_SNDCTL_COPR_RCVMSG 0x8fa44309 @@ -2477,20 +2354,20 @@ struct target_statfs64 { #define TARGET_SNDCTL_COPR_WDATA 0x40144304 #define TARGET_SNDCTL_DSP_RESET TARGET_IO('P', 0) #define TARGET_SNDCTL_DSP_SYNC TARGET_IO('P', 1) -#define TARGET_SNDCTL_DSP_SPEED TARGET_IOWR('P', 2, int) -#define TARGET_SNDCTL_DSP_STEREO TARGET_IOWR('P', 3, int) -#define TARGET_SNDCTL_DSP_GETBLKSIZE TARGET_IOWR('P', 4, int) -#define TARGET_SNDCTL_DSP_SETFMT TARGET_IOWR('P', 5, int) -#define TARGET_SNDCTL_DSP_CHANNELS TARGET_IOWR('P', 6, int) -#define TARGET_SOUND_PCM_WRITE_FILTER TARGET_IOWR('P', 7, int) +#define TARGET_SNDCTL_DSP_SPEED TARGET_IOWR('P', 2, abi_int) +#define TARGET_SNDCTL_DSP_STEREO TARGET_IOWR('P', 3, abi_int) +#define TARGET_SNDCTL_DSP_GETBLKSIZE TARGET_IOWR('P', 4, abi_int) +#define TARGET_SNDCTL_DSP_SETFMT TARGET_IOWR('P', 5, abi_int) +#define TARGET_SNDCTL_DSP_CHANNELS TARGET_IOWR('P', 6, abi_int) +#define TARGET_SOUND_PCM_WRITE_FILTER TARGET_IOWR('P', 7, abi_int) #define TARGET_SNDCTL_DSP_POST TARGET_IO('P', 8) -#define TARGET_SNDCTL_DSP_SUBDIVIDE TARGET_IOWR('P', 9, int) -#define TARGET_SNDCTL_DSP_SETFRAGMENT TARGET_IOWR('P',10, int) -#define TARGET_SNDCTL_DSP_GETFMTS TARGET_IOR('P', 11, int) +#define TARGET_SNDCTL_DSP_SUBDIVIDE TARGET_IOWR('P', 9, abi_int) +#define TARGET_SNDCTL_DSP_SETFRAGMENT TARGET_IOWR('P',10, abi_int) +#define TARGET_SNDCTL_DSP_GETFMTS TARGET_IOR('P', 11, abi_int) #define TARGET_SNDCTL_DSP_GETOSPACE TARGET_IORU('P',12) #define TARGET_SNDCTL_DSP_GETISPACE TARGET_IORU('P',13) -#define TARGET_SNDCTL_DSP_GETCAPS TARGET_IOR('P', 15, int) -#define TARGET_SNDCTL_DSP_GETTRIGGER TARGET_IOR('P',16, int) +#define TARGET_SNDCTL_DSP_GETCAPS TARGET_IOR('P', 15, abi_int) +#define TARGET_SNDCTL_DSP_GETTRIGGER TARGET_IOR('P',16, abi_int) #define TARGET_SNDCTL_DSP_GETIPTR TARGET_IORU('P',17) #define TARGET_SNDCTL_DSP_GETOPTR TARGET_IORU('P',18) #define TARGET_SNDCTL_DSP_MAPINBUF TARGET_IORU('P', 19) @@ -2538,89 +2415,89 @@ struct target_statfs64 { #define TARGET_SOUND_PCM_READ_FILTER 0x80045007 #define TARGET_SOUND_MIXER_INFO TARGET_IOR ('M', 101, mixer_info) #define TARGET_SOUND_MIXER_ACCESS 0xc0804d66 -#define TARGET_SOUND_MIXER_PRIVATE1 TARGET_IOWR('M', 111, int) -#define TARGET_SOUND_MIXER_PRIVATE2 TARGET_IOWR('M', 112, int) -#define TARGET_SOUND_MIXER_PRIVATE3 TARGET_IOWR('M', 113, int) -#define TARGET_SOUND_MIXER_PRIVATE4 TARGET_IOWR('M', 114, int) -#define TARGET_SOUND_MIXER_PRIVATE5 TARGET_IOWR('M', 115, int) +#define TARGET_SOUND_MIXER_PRIVATE1 TARGET_IOWR('M', 111, abi_int) +#define TARGET_SOUND_MIXER_PRIVATE2 TARGET_IOWR('M', 112, abi_int) +#define TARGET_SOUND_MIXER_PRIVATE3 TARGET_IOWR('M', 113, abi_int) +#define TARGET_SOUND_MIXER_PRIVATE4 TARGET_IOWR('M', 114, abi_int) +#define TARGET_SOUND_MIXER_PRIVATE5 TARGET_IOWR('M', 115, abi_int) -#define TARGET_MIXER_READ(dev) TARGET_IOR('M', dev, int) +#define TARGET_MIXER_READ(dev) TARGET_IOR('M', dev, abi_int) -#define TARGET_SOUND_MIXER_READ_VOLUME TARGET_MIXER_READ(SOUND_MIXER_VOLUME) -#define TARGET_SOUND_MIXER_READ_BASS TARGET_MIXER_READ(SOUND_MIXER_BASS) -#define TARGET_SOUND_MIXER_READ_TREBLE TARGET_MIXER_READ(SOUND_MIXER_TREBLE) -#define TARGET_SOUND_MIXER_READ_SYNTH TARGET_MIXER_READ(SOUND_MIXER_SYNTH) -#define TARGET_SOUND_MIXER_READ_PCM TARGET_MIXER_READ(SOUND_MIXER_PCM) -#define TARGET_SOUND_MIXER_READ_SPEAKER TARGET_MIXER_READ(SOUND_MIXER_SPEAKER) -#define TARGET_SOUND_MIXER_READ_LINE TARGET_MIXER_READ(SOUND_MIXER_LINE) -#define TARGET_SOUND_MIXER_READ_MIC TARGET_MIXER_READ(SOUND_MIXER_MIC) -#define TARGET_SOUND_MIXER_READ_CD TARGET_MIXER_READ(SOUND_MIXER_CD) -#define TARGET_SOUND_MIXER_READ_IMIX TARGET_MIXER_READ(SOUND_MIXER_IMIX) -#define TARGET_SOUND_MIXER_READ_ALTPCM TARGET_MIXER_READ(SOUND_MIXER_ALTPCM) -#define TARGET_SOUND_MIXER_READ_RECLEV TARGET_MIXER_READ(SOUND_MIXER_RECLEV) -#define TARGET_SOUND_MIXER_READ_IGAIN TARGET_MIXER_READ(SOUND_MIXER_IGAIN) -#define TARGET_SOUND_MIXER_READ_OGAIN TARGET_MIXER_READ(SOUND_MIXER_OGAIN) -#define TARGET_SOUND_MIXER_READ_LINE1 TARGET_MIXER_READ(SOUND_MIXER_LINE1) -#define TARGET_SOUND_MIXER_READ_LINE2 TARGET_MIXER_READ(SOUND_MIXER_LINE2) -#define TARGET_SOUND_MIXER_READ_LINE3 TARGET_MIXER_READ(SOUND_MIXER_LINE3) +#define TARGET_SOUND_MIXER_READ_VOLUME TARGET_MIXER_READ(SOUND_MIXER_VOLUME) +#define TARGET_SOUND_MIXER_READ_BASS TARGET_MIXER_READ(SOUND_MIXER_BASS) +#define TARGET_SOUND_MIXER_READ_TREBLE TARGET_MIXER_READ(SOUND_MIXER_TREBLE) +#define TARGET_SOUND_MIXER_READ_SYNTH TARGET_MIXER_READ(SOUND_MIXER_SYNTH) +#define TARGET_SOUND_MIXER_READ_PCM TARGET_MIXER_READ(SOUND_MIXER_PCM) +#define TARGET_SOUND_MIXER_READ_SPEAKER TARGET_MIXER_READ(SOUND_MIXER_SPEAKER) +#define TARGET_SOUND_MIXER_READ_LINE TARGET_MIXER_READ(SOUND_MIXER_LINE) +#define TARGET_SOUND_MIXER_READ_MIC TARGET_MIXER_READ(SOUND_MIXER_MIC) +#define TARGET_SOUND_MIXER_READ_CD TARGET_MIXER_READ(SOUND_MIXER_CD) +#define TARGET_SOUND_MIXER_READ_IMIX TARGET_MIXER_READ(SOUND_MIXER_IMIX) +#define TARGET_SOUND_MIXER_READ_ALTPCM TARGET_MIXER_READ(SOUND_MIXER_ALTPCM) +#define TARGET_SOUND_MIXER_READ_RECLEV TARGET_MIXER_READ(SOUND_MIXER_RECLEV) +#define TARGET_SOUND_MIXER_READ_IGAIN TARGET_MIXER_READ(SOUND_MIXER_IGAIN) +#define TARGET_SOUND_MIXER_READ_OGAIN TARGET_MIXER_READ(SOUND_MIXER_OGAIN) +#define TARGET_SOUND_MIXER_READ_LINE1 TARGET_MIXER_READ(SOUND_MIXER_LINE1) +#define TARGET_SOUND_MIXER_READ_LINE2 TARGET_MIXER_READ(SOUND_MIXER_LINE2) +#define TARGET_SOUND_MIXER_READ_LINE3 TARGET_MIXER_READ(SOUND_MIXER_LINE3) /* Obsolete macros */ -#define TARGET_SOUND_MIXER_READ_MUTE TARGET_MIXER_READ(SOUND_MIXER_MUTE) -#define TARGET_SOUND_MIXER_READ_ENHANCE TARGET_MIXER_READ(SOUND_MIXER_ENHANCE) -#define TARGET_SOUND_MIXER_READ_LOUD TARGET_MIXER_READ(SOUND_MIXER_LOUD) +#define TARGET_SOUND_MIXER_READ_MUTE TARGET_MIXER_READ(SOUND_MIXER_MUTE) +#define TARGET_SOUND_MIXER_READ_ENHANCE TARGET_MIXER_READ(SOUND_MIXER_ENHANCE) +#define TARGET_SOUND_MIXER_READ_LOUD TARGET_MIXER_READ(SOUND_MIXER_LOUD) -#define TARGET_SOUND_MIXER_READ_RECSRC TARGET_MIXER_READ(SOUND_MIXER_RECSRC) -#define TARGET_SOUND_MIXER_READ_DEVMASK TARGET_MIXER_READ(SOUND_MIXER_DEVMASK) -#define TARGET_SOUND_MIXER_READ_RECMASK TARGET_MIXER_READ(SOUND_MIXER_RECMASK) -#define TARGET_SOUND_MIXER_READ_STEREODEVS TARGET_MIXER_READ(SOUND_MIXER_STEREODEVS) -#define TARGET_SOUND_MIXER_READ_CAPS TARGET_MIXER_READ(SOUND_MIXER_CAPS) +#define TARGET_SOUND_MIXER_READ_RECSRC TARGET_MIXER_READ(SOUND_MIXER_RECSRC) +#define TARGET_SOUND_MIXER_READ_DEVMASK TARGET_MIXER_READ(SOUND_MIXER_DEVMASK) +#define TARGET_SOUND_MIXER_READ_RECMASK TARGET_MIXER_READ(SOUND_MIXER_RECMASK) +#define TARGET_SOUND_MIXER_READ_STEREODEVS TARGET_MIXER_READ(SOUND_MIXER_STEREODEVS) +#define TARGET_SOUND_MIXER_READ_CAPS TARGET_MIXER_READ(SOUND_MIXER_CAPS) -#define TARGET_MIXER_WRITE(dev) TARGET_IOWR('M', dev, int) +#define TARGET_MIXER_WRITE(dev) TARGET_IOWR('M', dev, abi_int) -#define TARGET_SOUND_MIXER_WRITE_VOLUME TARGET_MIXER_WRITE(SOUND_MIXER_VOLUME) -#define TARGET_SOUND_MIXER_WRITE_BASS TARGET_MIXER_WRITE(SOUND_MIXER_BASS) -#define TARGET_SOUND_MIXER_WRITE_TREBLE TARGET_MIXER_WRITE(SOUND_MIXER_TREBLE) -#define TARGET_SOUND_MIXER_WRITE_SYNTH TARGET_MIXER_WRITE(SOUND_MIXER_SYNTH) -#define TARGET_SOUND_MIXER_WRITE_PCM TARGET_MIXER_WRITE(SOUND_MIXER_PCM) -#define TARGET_SOUND_MIXER_WRITE_SPEAKER TARGET_MIXER_WRITE(SOUND_MIXER_SPEAKER) -#define TARGET_SOUND_MIXER_WRITE_LINE TARGET_MIXER_WRITE(SOUND_MIXER_LINE) -#define TARGET_SOUND_MIXER_WRITE_MIC TARGET_MIXER_WRITE(SOUND_MIXER_MIC) -#define TARGET_SOUND_MIXER_WRITE_CD TARGET_MIXER_WRITE(SOUND_MIXER_CD) -#define TARGET_SOUND_MIXER_WRITE_IMIX TARGET_MIXER_WRITE(SOUND_MIXER_IMIX) -#define TARGET_SOUND_MIXER_WRITE_ALTPCM TARGET_MIXER_WRITE(SOUND_MIXER_ALTPCM) -#define TARGET_SOUND_MIXER_WRITE_RECLEV TARGET_MIXER_WRITE(SOUND_MIXER_RECLEV) -#define TARGET_SOUND_MIXER_WRITE_IGAIN TARGET_MIXER_WRITE(SOUND_MIXER_IGAIN) -#define TARGET_SOUND_MIXER_WRITE_OGAIN TARGET_MIXER_WRITE(SOUND_MIXER_OGAIN) -#define TARGET_SOUND_MIXER_WRITE_LINE1 TARGET_MIXER_WRITE(SOUND_MIXER_LINE1) -#define TARGET_SOUND_MIXER_WRITE_LINE2 TARGET_MIXER_WRITE(SOUND_MIXER_LINE2) -#define TARGET_SOUND_MIXER_WRITE_LINE3 TARGET_MIXER_WRITE(SOUND_MIXER_LINE3) +#define TARGET_SOUND_MIXER_WRITE_VOLUME TARGET_MIXER_WRITE(SOUND_MIXER_VOLUME) +#define TARGET_SOUND_MIXER_WRITE_BASS TARGET_MIXER_WRITE(SOUND_MIXER_BASS) +#define TARGET_SOUND_MIXER_WRITE_TREBLE TARGET_MIXER_WRITE(SOUND_MIXER_TREBLE) +#define TARGET_SOUND_MIXER_WRITE_SYNTH TARGET_MIXER_WRITE(SOUND_MIXER_SYNTH) +#define TARGET_SOUND_MIXER_WRITE_PCM TARGET_MIXER_WRITE(SOUND_MIXER_PCM) +#define TARGET_SOUND_MIXER_WRITE_SPEAKER TARGET_MIXER_WRITE(SOUND_MIXER_SPEAKER) +#define TARGET_SOUND_MIXER_WRITE_LINE TARGET_MIXER_WRITE(SOUND_MIXER_LINE) +#define TARGET_SOUND_MIXER_WRITE_MIC TARGET_MIXER_WRITE(SOUND_MIXER_MIC) +#define TARGET_SOUND_MIXER_WRITE_CD TARGET_MIXER_WRITE(SOUND_MIXER_CD) +#define TARGET_SOUND_MIXER_WRITE_IMIX TARGET_MIXER_WRITE(SOUND_MIXER_IMIX) +#define TARGET_SOUND_MIXER_WRITE_ALTPCM TARGET_MIXER_WRITE(SOUND_MIXER_ALTPCM) +#define TARGET_SOUND_MIXER_WRITE_RECLEV TARGET_MIXER_WRITE(SOUND_MIXER_RECLEV) +#define TARGET_SOUND_MIXER_WRITE_IGAIN TARGET_MIXER_WRITE(SOUND_MIXER_IGAIN) +#define TARGET_SOUND_MIXER_WRITE_OGAIN TARGET_MIXER_WRITE(SOUND_MIXER_OGAIN) +#define TARGET_SOUND_MIXER_WRITE_LINE1 TARGET_MIXER_WRITE(SOUND_MIXER_LINE1) +#define TARGET_SOUND_MIXER_WRITE_LINE2 TARGET_MIXER_WRITE(SOUND_MIXER_LINE2) +#define TARGET_SOUND_MIXER_WRITE_LINE3 TARGET_MIXER_WRITE(SOUND_MIXER_LINE3) /* Obsolete macros */ -#define TARGET_SOUND_MIXER_WRITE_MUTE TARGET_MIXER_WRITE(SOUND_MIXER_MUTE) -#define TARGET_SOUND_MIXER_WRITE_ENHANCE TARGET_MIXER_WRITE(SOUND_MIXER_ENHANCE) -#define TARGET_SOUND_MIXER_WRITE_LOUD TARGET_MIXER_WRITE(SOUND_MIXER_LOUD) +#define TARGET_SOUND_MIXER_WRITE_MUTE TARGET_MIXER_WRITE(SOUND_MIXER_MUTE) +#define TARGET_SOUND_MIXER_WRITE_ENHANCE TARGET_MIXER_WRITE(SOUND_MIXER_ENHANCE) +#define TARGET_SOUND_MIXER_WRITE_LOUD TARGET_MIXER_WRITE(SOUND_MIXER_LOUD) -#define TARGET_SOUND_MIXER_WRITE_RECSRC TARGET_MIXER_WRITE(SOUND_MIXER_RECSRC) +#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; + abi_int dev_class; + abi_int dev_sclass; + abi_int card; + abi_int device; + abi_int subdevice; }; struct target_snd_timer_ginfo { struct target_snd_timer_id tid; - unsigned int flags; - int card; + abi_uint flags; + abi_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; + abi_uint clients; unsigned char reserved[32]; }; @@ -2645,8 +2522,8 @@ struct target_snd_timer_select { }; struct target_snd_timer_info { - unsigned int flags; - int card; + abi_uint flags; + abi_int card; unsigned char id[64]; unsigned char name[80]; abi_ulong reserved0; @@ -2656,31 +2533,31 @@ struct target_snd_timer_info { struct target_snd_timer_status { struct target_timespec tstamp; - unsigned int resolution; - unsigned int lost; - unsigned int overrun; - unsigned int queue; + abi_uint resolution; + abi_uint lost; + abi_uint overrun; + abi_uint 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_PVERSION TARGET_IOR('T', 0x00, abi_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) @@ -2733,11 +2610,11 @@ struct target_sysinfo { abi_ulong bufferram; /* Memory used by buffers */ abi_ulong totalswap; /* Total swap space size */ abi_ulong freeswap; /* swap space still available */ - unsigned short procs; /* Number of current processes */ - unsigned short pad; /* explicit padding for m68k */ + abi_ushort procs; /* Number of current processes */ + abi_ushort pad; /* explicit padding for m68k */ abi_ulong totalhigh; /* Total high memory size */ abi_ulong freehigh; /* Available high memory size */ - unsigned int mem_unit; /* Memory unit size in bytes */ + abi_uint mem_unit; /* Memory unit size in bytes */ char _f[20-2*sizeof(abi_long)-sizeof(int)]; /* Padding: libc5 uses this.. */ }; @@ -2764,9 +2641,9 @@ struct target_mq_attr { }; struct target_drm_version { - int version_major; - int version_minor; - int version_patchlevel; + abi_int version_major; + abi_int version_minor; + abi_int version_patchlevel; abi_ulong name_len; abi_ulong name; abi_ulong date_len; @@ -2776,7 +2653,7 @@ struct target_drm_version { }; struct target_drm_i915_getparam { - int param; + abi_int param; abi_ulong value; }; @@ -2795,6 +2672,9 @@ struct target_drm_i915_getparam { #define FUTEX_TRYLOCK_PI 8 #define FUTEX_WAIT_BITSET 9 #define FUTEX_WAKE_BITSET 10 +#define FUTEX_WAIT_REQUEUE_PI 11 +#define FUTEX_CMP_REQUEUE_PI 12 +#define FUTEX_LOCK_PI2 13 #define FUTEX_PRIVATE_FLAG 128 #define FUTEX_CLOCK_REALTIME 256 @@ -2824,26 +2704,26 @@ struct target_epoll_event { #endif struct target_ucred { - uint32_t pid; - uint32_t uid; - uint32_t gid; + abi_uint pid; + abi_uint uid; + abi_uint gid; }; -typedef int32_t target_timer_t; +typedef abi_int target_timer_t; #define TARGET_SIGEV_MAX_SIZE 64 /* This is architecture-specific but most architectures use the default */ #ifdef TARGET_MIPS -#define TARGET_SIGEV_PREAMBLE_SIZE (sizeof(int32_t) * 2 + sizeof(abi_long)) +#define TARGET_SIGEV_PREAMBLE_SIZE (sizeof(abi_int) * 2 + sizeof(abi_long)) #else -#define TARGET_SIGEV_PREAMBLE_SIZE (sizeof(int32_t) * 2 \ +#define TARGET_SIGEV_PREAMBLE_SIZE (sizeof(abi_int) * 2 \ + sizeof(target_sigval_t)) #endif -#define TARGET_SIGEV_PAD_SIZE ((TARGET_SIGEV_MAX_SIZE \ - - TARGET_SIGEV_PREAMBLE_SIZE) \ - / sizeof(int32_t)) +#define TARGET_SIGEV_PAD_SIZE ((TARGET_SIGEV_MAX_SIZE \ + - TARGET_SIGEV_PREAMBLE_SIZE) \ + / sizeof(abi_int)) struct target_sigevent { target_sigval_t sigev_value; @@ -2865,14 +2745,14 @@ struct target_sigevent { }; struct target_user_cap_header { - uint32_t version; - int pid; + abi_uint version; + abi_int pid; }; struct target_user_cap_data { - uint32_t effective; - uint32_t permitted; - uint32_t inheritable; + abi_uint effective; + abi_uint permitted; + abi_uint inheritable; }; /* from kernel's include/linux/syslog.h */ @@ -2901,40 +2781,40 @@ struct target_user_cap_data { #define TARGET_SYSLOG_ACTION_SIZE_BUFFER 10 struct target_statx_timestamp { - int64_t tv_sec; - uint32_t tv_nsec; - int32_t __reserved; + abi_llong tv_sec; + abi_uint tv_nsec; + abi_int __reserved; }; struct target_statx { - /* 0x00 */ - uint32_t stx_mask; /* What results were written [uncond] */ - uint32_t stx_blksize; /* Preferred general I/O size [uncond] */ - uint64_t stx_attributes; /* Flags conveying information about the file */ - /* 0x10 */ - uint32_t stx_nlink; /* Number of hard links */ - uint32_t stx_uid; /* User ID of owner */ - uint32_t stx_gid; /* Group ID of owner */ - uint16_t stx_mode; /* File mode */ - uint16_t __spare0[1]; - /* 0x20 */ - uint64_t stx_ino; /* Inode number */ - uint64_t stx_size; /* File size */ - uint64_t stx_blocks; /* Number of 512-byte blocks allocated */ - uint64_t stx_attributes_mask; /* Mask to show what is supported */ - /* 0x40 */ - struct target_statx_timestamp stx_atime; /* Last access time */ - struct target_statx_timestamp stx_btime; /* File creation time */ - struct target_statx_timestamp stx_ctime; /* Last attribute change time */ - struct target_statx_timestamp stx_mtime; /* Last data modification time */ - /* 0x80 */ - uint32_t stx_rdev_major; /* Device ID of special file [if bdev/cdev] */ - uint32_t stx_rdev_minor; - uint32_t stx_dev_major; /* ID of device containing file [uncond] */ - uint32_t stx_dev_minor; - /* 0x90 */ - uint64_t __spare2[14]; /* Spare space for future expansion */ - /* 0x100 */ + /* 0x00 */ + abi_uint stx_mask; /* What results were written [uncond] */ + abi_uint stx_blksize; /* Preferred general I/O size [uncond] */ + abi_ullong stx_attributes; /* Flags conveying information about the file */ + /* 0x10 */ + abi_uint stx_nlink; /* Number of hard links */ + abi_uint stx_uid; /* User ID of owner */ + abi_uint stx_gid; /* Group ID of owner */ + uint16_t stx_mode; /* File mode */ + uint16_t __spare0[1]; + /* 0x20 */ + abi_ullong stx_ino; /* Inode number */ + abi_ullong stx_size; /* File size */ + abi_ullong stx_blocks; /* Number of 512-byte blocks allocated */ + abi_ullong stx_attributes_mask; /* Mask to show what is supported */ + /* 0x40 */ + struct target_statx_timestamp stx_atime; /* Last access time */ + struct target_statx_timestamp stx_btime; /* File creation time */ + struct target_statx_timestamp stx_ctime; /* Last attribute change time */ + struct target_statx_timestamp stx_mtime; /* Last data modification time */ + /* 0x80 */ + abi_uint stx_rdev_major; /* Device ID of special file [if bdev/cdev] */ + abi_uint stx_rdev_minor; + abi_uint stx_dev_major; /* ID of device containing file [uncond] */ + abi_uint stx_dev_minor; + /* 0x90 */ + abi_ullong __spare2[14]; /* Spare space for future expansion */ + /* 0x100 */ }; /* from kernel's include/linux/sched/types.h */ diff --git a/linux-user/thunk.c b/linux-user/thunk.c index dac4bf11c6..071aad4b5f 100644 --- a/linux-user/thunk.c +++ b/linux-user/thunk.c @@ -436,29 +436,29 @@ const argtype *thunk_print(void *arg, const argtype *type_ptr) /* Utility function: Table-driven functions to translate bitmasks * between host and target formats */ -unsigned int target_to_host_bitmask(unsigned int target_mask, - const bitmask_transtbl * trans_tbl) +unsigned int target_to_host_bitmask_len(unsigned int target_mask, + const bitmask_transtbl *tbl, + size_t len) { - const bitmask_transtbl *btp; unsigned int host_mask = 0; - for (btp = trans_tbl; btp->target_mask && btp->host_mask; btp++) { - if ((target_mask & btp->target_mask) == btp->target_bits) { - host_mask |= btp->host_bits; + for (size_t i = 0; i < len; ++i) { + if ((target_mask & tbl[i].target_mask) == tbl[i].target_bits) { + host_mask |= tbl[i].host_bits; } } return host_mask; } -unsigned int host_to_target_bitmask(unsigned int host_mask, - const bitmask_transtbl * trans_tbl) +unsigned int host_to_target_bitmask_len(unsigned int host_mask, + const bitmask_transtbl *tbl, + size_t len) { - const bitmask_transtbl *btp; unsigned int target_mask = 0; - for (btp = trans_tbl; btp->target_mask && btp->host_mask; btp++) { - if ((host_mask & btp->host_mask) == btp->host_bits) { - target_mask |= btp->target_bits; + for (size_t i = 0; i < len; ++i) { + if ((host_mask & tbl[i].host_mask) == tbl[i].host_bits) { + target_mask |= tbl[i].target_bits; } } return target_mask; diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index 6175ce53db..ce11d9e21c 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -20,6 +20,7 @@ #include "exec/user/thunk.h" #include "exec/exec-all.h" +#include "exec/tb-flush.h" #include "qemu/log.h" extern char *exec_path; @@ -65,28 +66,29 @@ abi_long do_syscall(CPUArchState *cpu_env, int num, abi_long arg1, abi_long arg8); extern __thread CPUState *thread_cpu; G_NORETURN void cpu_loop(CPUArchState *env); +abi_long get_errno(abi_long ret); const char *target_strerror(int err); int get_osversion(void); void init_qemu_uname_release(void); void fork_start(void); -void fork_end(int child); +void fork_end(pid_t pid); /** * probe_guest_base: * @image_name: the executable being loaded - * @loaddr: the lowest fixed address in the executable - * @hiaddr: the highest fixed address in the executable + * @loaddr: the lowest fixed address within the executable + * @hiaddr: the highest fixed address within the executable * * Creates the initial guest address space in the host memory space. * - * If @loaddr == 0, then no address in the executable is fixed, - * i.e. it is fully relocatable. In that case @hiaddr is the size - * of the executable. + * If @loaddr == 0, then no address in the executable is fixed, i.e. + * it is fully relocatable. In that case @hiaddr is the size of the + * executable minus one. * * This function will not return if a valid value for guest_base * cannot be chosen. On return, the executable loader can expect * - * target_mmap(loaddr, hiaddr - loaddr, ...) + * target_mmap(loaddr, hiaddr - loaddr + 1, ...) * * to succeed. */ @@ -134,7 +136,7 @@ void print_termios(void *arg); #ifdef TARGET_ARM static inline int regpairs_aligned(CPUArchState *cpu_env, int num) { - return cpu_env->eabi == 1; + return cpu_env->eabi; } #elif defined(TARGET_MIPS) && defined(TARGET_ABI_MIPSO32) static inline int regpairs_aligned(CPUArchState *cpu_env, int num) { return 1; } diff --git a/linux-user/user-mmap.h b/linux-user/user-mmap.h index 86342d3bce..6fa949309d 100644 --- a/linux-user/user-mmap.h +++ b/linux-user/user-mmap.h @@ -18,15 +18,42 @@ #ifndef LINUX_USER_USER_MMAP_H #define LINUX_USER_USER_MMAP_H +/* + * Guest parameters for the ADDR_COMPAT_LAYOUT personality + * (at present this is the only layout supported by QEMU). + * + * TASK_UNMAPPED_BASE: For mmap without hint (addr != 0), the search + * for unused virtual memory begins at TASK_UNMAPPED_BASE. + * + * ELF_ET_DYN_BASE: When the executable is ET_DYN (i.e. PIE), and requires + * an interpreter (i.e. not -static-pie), use ELF_ET_DYN_BASE instead of + * TASK_UNMAPPED_BASE for selecting the address of the executable. + * This provides some distance between the executable and the interpreter, + * which allows the initial brk to be placed immediately after the + * executable and also have room to grow. + * + * task_unmapped_base, elf_et_dyn_base: When the guest address space is + * limited via -R, the values of TASK_UNMAPPED_BASE and ELF_ET_DYN_BASE + * must be adjusted to fit. + */ +extern abi_ulong task_unmapped_base; +extern abi_ulong elf_et_dyn_base; + +/* + * mmap_next_start: The base address for the next mmap without hint, + * increased after each successful map, starting at task_unmapped_base. + * This is an optimization within QEMU and not part of ADDR_COMPAT_LAYOUT. + */ +extern abi_ulong mmap_next_start; + int target_mprotect(abi_ulong start, abi_ulong len, int prot); abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, - int flags, int fd, abi_ulong offset); + int flags, int fd, off_t offset); int target_munmap(abi_ulong start, abi_ulong len); abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, abi_ulong new_size, unsigned long flags, abi_ulong new_addr); -extern unsigned long last_brk; -extern abi_ulong mmap_next_start; +abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice); abi_ulong mmap_find_vma(abi_ulong, abi_ulong, abi_ulong); void mmap_fork_start(void); void mmap_fork_end(int child); @@ -35,4 +62,8 @@ void mmap_fork_end(int child); abi_ulong e2k_mmap(abi_ulong size); #endif +abi_ulong target_shmat(CPUArchState *cpu_env, int shmid, + abi_ulong shmaddr, int shmflg); +abi_long target_shmdt(abi_ulong shmaddr); + #endif /* LINUX_USER_USER_MMAP_H */ diff --git a/linux-user/vm86.c b/linux-user/vm86.c index c2facf3fc2..9f512a2242 100644 --- a/linux-user/vm86.c +++ b/linux-user/vm86.c @@ -74,7 +74,7 @@ static inline unsigned int vm_getl(CPUX86State *env, void save_v86_state(CPUX86State *env) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); struct target_vm86plus_struct * target_v86; if (!lock_user_struct(VERIFY_WRITE, target_v86, ts->target_v86, 0)) @@ -134,7 +134,7 @@ static inline void return_to_32bit(CPUX86State *env, int retval) static inline int set_IF(CPUX86State *env) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); ts->v86flags |= VIF_MASK; if (ts->v86flags & VIP_MASK) { @@ -147,7 +147,7 @@ static inline int set_IF(CPUX86State *env) static inline void clear_IF(CPUX86State *env) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); ts->v86flags &= ~VIF_MASK; } @@ -165,7 +165,7 @@ static inline void clear_AC(CPUX86State *env) static inline int set_vflags_long(unsigned long eflags, CPUX86State *env) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); set_flags(ts->v86flags, eflags, ts->v86mask); set_flags(env->eflags, eflags, SAFE_MASK); @@ -179,7 +179,7 @@ static inline int set_vflags_long(unsigned long eflags, CPUX86State *env) static inline int set_vflags_short(unsigned short flags, CPUX86State *env) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); set_flags(ts->v86flags, flags, ts->v86mask & 0xffff); set_flags(env->eflags, flags, SAFE_MASK); @@ -193,7 +193,7 @@ static inline int set_vflags_short(unsigned short flags, CPUX86State *env) static inline unsigned int get_vflags(CPUX86State *env) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); unsigned int flags; flags = env->eflags & RETURN_MASK; @@ -210,7 +210,7 @@ static inline unsigned int get_vflags(CPUX86State *env) static void do_int(CPUX86State *env, int intno) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); uint32_t int_addr, segoffs, ssp; unsigned int sp; @@ -269,7 +269,7 @@ void handle_vm86_trap(CPUX86State *env, int trapno) void handle_vm86_fault(CPUX86State *env) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); uint32_t csp, ssp; unsigned int ip, sp, newflags, newip, newcs, opcode, intno; int data32, pref_done; @@ -394,7 +394,7 @@ void handle_vm86_fault(CPUX86State *env) int do_vm86(CPUX86State *env, long subfunction, abi_ulong vm86_addr) { CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); struct target_vm86plus_struct * target_v86; int ret; diff --git a/linux-user/x86_64/Makefile.vdso b/linux-user/x86_64/Makefile.vdso new file mode 100644 index 0000000000..26552b66db --- /dev/null +++ b/linux-user/x86_64/Makefile.vdso @@ -0,0 +1,11 @@ +include $(BUILD_DIR)/tests/tcg/x86_64-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/x86_64 +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso.so + +$(SUBDIR)/vdso.so: vdso.S vdso.ld + $(CC) -o $@ -nostdlib -shared -Wl,-h,linux-vdso.so.1 \ + -Wl,--build-id=sha1 -Wl,--hash-style=both \ + -Wl,-T,$(SUBDIR)/vdso.ld $< diff --git a/linux-user/x86_64/meson.build b/linux-user/x86_64/meson.build index 203af9a60c..8c60da7a60 100644 --- a/linux-user/x86_64/meson.build +++ b/linux-user/x86_64/meson.build @@ -3,3 +3,7 @@ syscall_nr_generators += { arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], output: '@BASENAME@_nr.h') } + +vdso_inc = gen_vdso.process('vdso.so') + +linux_user_ss.add(when: 'TARGET_X86_64', if_true: vdso_inc) diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index 7b76a90de8..3f628f8d66 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -9,6 +9,6 @@ #define X86_64_TARGET_ELF_H static inline const char *cpu_get_model(uint32_t eflags) { - return "qemu64"; + return "max"; } #endif diff --git a/linux-user/x86_64/target_mman.h b/linux-user/x86_64/target_mman.h new file mode 100644 index 0000000000..48fbf20b42 --- /dev/null +++ b/linux-user/x86_64/target_mman.h @@ -0,0 +1,16 @@ +/* + * arch/x86/include/asm/processor.h: + * TASK_UNMAPPED_BASE __TASK_UNMAPPED_BASE(TASK_SIZE_LOW) + * __TASK_UNMAPPED_BASE(S) PAGE_ALIGN(S / 3) + * + * arch/x86/include/asm/page_64_types.h: + * TASK_SIZE_LOW DEFAULT_MAP_WINDOW + * DEFAULT_MAP_WINDOW ((1UL << 47) - PAGE_SIZE) + */ +#define TASK_UNMAPPED_BASE \ + TARGET_PAGE_ALIGN((1ull << TARGET_VIRT_ADDR_SPACE_BITS) / 3) + +/* arch/x86/include/asm/elf.h */ +#define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE * 2) + +#include "../generic/target_mman.h" diff --git a/linux-user/x86_64/target_proc.h b/linux-user/x86_64/target_proc.h new file mode 100644 index 0000000000..43fe29ca72 --- /dev/null +++ b/linux-user/x86_64/target_proc.h @@ -0,0 +1 @@ +/* No target-specific /proc support */ diff --git a/linux-user/x86_64/vdso.S b/linux-user/x86_64/vdso.S new file mode 100644 index 0000000000..47d16c00ab --- /dev/null +++ b/linux-user/x86_64/vdso.S @@ -0,0 +1,78 @@ +/* + * x86-64 linux replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +.macro endf name + .globl \name + .type \name, @function + .size \name, . - \name +.endm + +.macro weakalias name +\name = __vdso_\name + .weak \name +.endm + +.macro vdso_syscall name, nr +__vdso_\name: + mov $\nr, %eax + syscall + ret +endf __vdso_\name +weakalias \name +.endm + + .cfi_startproc + +vdso_syscall clock_gettime, __NR_clock_gettime +vdso_syscall clock_getres, __NR_clock_getres +vdso_syscall gettimeofday, __NR_gettimeofday +vdso_syscall time, __NR_time + +__vdso_getcpu: + /* + * There is no syscall number for this allocated on x64. + * We can handle this several ways: + * + * (1) Invent a syscall number for use within qemu. + * It should be easy enough to pick a number that + * is well out of the way of the kernel numbers. + * + * (2) Force the emulated cpu to support the rdtscp insn, + * and initialize the TSC_AUX value the appropriate value. + * + * (3) Pretend that we're always running on cpu 0. + * + * This last is the one that's implemented here, with the + * tiny bit of extra code to support rdtscp in place. + */ + xor %ecx, %ecx /* rdtscp w/ tsc_aux = 0 */ + + /* if (cpu != NULL) *cpu = (ecx & 0xfff); */ + test %rdi, %rdi + jz 1f + mov %ecx, %eax + and $0xfff, %eax + mov %eax, (%rdi) + + /* if (node != NULL) *node = (ecx >> 12); */ +1: test %rsi, %rsi + jz 2f + shr $12, %ecx + mov %ecx, (%rsi) + +2: xor %eax, %eax + ret +endf __vdso_getcpu + +weakalias getcpu + + .cfi_endproc + +/* TODO: Add elf note for LINUX_VERSION_CODE */ diff --git a/linux-user/x86_64/vdso.ld b/linux-user/x86_64/vdso.ld new file mode 100644 index 0000000000..ca6001cc3c --- /dev/null +++ b/linux-user/x86_64/vdso.ld @@ -0,0 +1,73 @@ +/* + * Linker script for linux x86-64 replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_2.6 { + global: + clock_gettime; + __vdso_clock_gettime; + gettimeofday; + __vdso_gettimeofday; + getcpu; + __vdso_getcpu; + time; + __vdso_time; + clock_getres; + __vdso_clock_getres; + + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; /* FLAGS=RWX */ + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + .data : { + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load =0x90909090 +} diff --git a/linux-user/x86_64/vdso.so b/linux-user/x86_64/vdso.so new file mode 100755 index 0000000000..c873d6ea58 Binary files /dev/null and b/linux-user/x86_64/vdso.so differ diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c index f5fb8b5cbe..6514b8dd57 100644 --- a/linux-user/xtensa/signal.c +++ b/linux-user/xtensa/signal.c @@ -157,6 +157,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, { abi_ulong frame_addr; struct target_rt_sigframe *frame; + int is_fdpic = info_is_fdpic(get_task_state(thread_cpu)->info); + abi_ulong handler = 0; + abi_ulong handler_fdpic_GOT = 0; uint32_t ra; bool abi_call0; unsigned base; @@ -165,12 +168,23 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, frame_addr = get_sigframe(ka, env, sizeof(*frame)); trace_user_setup_rt_frame(env, frame_addr); + if (is_fdpic) { + abi_ulong funcdesc_ptr = ka->_sa_handler; + + if (get_user_ual(handler, funcdesc_ptr) + || get_user_ual(handler_fdpic_GOT, funcdesc_ptr + 4)) { + goto give_sigsegv; + } + } else { + handler = ka->_sa_handler; + } + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { goto give_sigsegv; } if (ka->sa_flags & SA_SIGINFO) { - tswap_siginfo(&frame->info, info); + frame->info = *info; } __put_user(0, &frame->uc.tuc_flags); @@ -185,14 +199,21 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, } if (ka->sa_flags & TARGET_SA_RESTORER) { - ra = ka->sa_restorer; + if (is_fdpic) { + if (get_user_ual(ra, ka->sa_restorer)) { + unlock_user_struct(frame, frame_addr, 0); + goto give_sigsegv; + } + } else { + ra = ka->sa_restorer; + } } else { /* Not used, but retain for ABI compatibility. */ install_sigtramp(frame->retcode); ra = default_rt_sigreturn; } memset(env->regs, 0, sizeof(env->regs)); - env->pc = ka->_sa_handler; + env->pc = handler; env->regs[1] = frame_addr; env->sregs[WINDOW_BASE] = 0; env->sregs[WINDOW_START] = 1; @@ -212,6 +233,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, env->regs[base + 3] = frame_addr + offsetof(struct target_rt_sigframe, info); env->regs[base + 4] = frame_addr + offsetof(struct target_rt_sigframe, uc); + if (is_fdpic) { + env->regs[base + 11] = handler_fdpic_GOT; + } unlock_user_struct(frame, frame_addr, 1); return; diff --git a/linux-user/xtensa/target_mman.h b/linux-user/xtensa/target_mman.h new file mode 100644 index 0000000000..8fa6337a97 --- /dev/null +++ b/linux-user/xtensa/target_mman.h @@ -0,0 +1,29 @@ +#ifndef XTENSA_TARGET_MMAN_H +#define XTENSA_TARGET_MMAN_H + +#define TARGET_PROT_SEM 0x10 + +#define TARGET_MAP_NORESERVE 0x0400 +#define TARGET_MAP_ANONYMOUS 0x0800 +#define TARGET_MAP_GROWSDOWN 0x1000 +#define TARGET_MAP_DENYWRITE 0x2000 +#define TARGET_MAP_EXECUTABLE 0x4000 +#define TARGET_MAP_LOCKED 0x8000 +#define TARGET_MAP_POPULATE 0x10000 +#define TARGET_MAP_NONBLOCK 0x20000 +#define TARGET_MAP_STACK 0x40000 +#define TARGET_MAP_HUGETLB 0x80000 + +/* + * arch/xtensa/include/asm/processor.h: + * TASK_UNMAPPED_BASE (TASK_SIZE / 2) + */ +#define TASK_UNMAPPED_BASE (1u << (TARGET_VIRT_ADDR_SPACE_BITS - 1)) + +/* arch/xtensa/include/asm/elf.h */ +#define ELF_ET_DYN_BASE \ + TARGET_PAGE_ALIGN((1u << TARGET_VIRT_ADDR_SPACE_BITS) / 3) + +#include "../generic/target_mman.h" + +#endif diff --git a/linux-user/xtensa/target_proc.h b/linux-user/xtensa/target_proc.h new file mode 100644 index 0000000000..43fe29ca72 --- /dev/null +++ b/linux-user/xtensa/target_proc.h @@ -0,0 +1 @@ +/* No target-specific /proc support */ diff --git a/meson b/meson deleted file mode 160000 index 12f9f04ba0..0000000000 --- a/meson +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 12f9f04ba0decfda425dbbf9a501084c153a2d18 diff --git a/meson.build b/meson.build index d6c27fcd6a..e758414b32 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('qemu', ['c'], meson_version: '>=0.59.3', +project('qemu', ['c'], meson_version: '>=0.63.0', default_options: ['warning_level=1', 'c_std=gnu11', 'cpp_std=gnu++11', 'b_colorout=auto', 'b_staticpic=false', 'stdsplit=false', 'optimization=2', 'b_pie=true'], version: files('VERSION')) @@ -7,19 +7,19 @@ add_test_setup('quick', exclude_suites: ['slow', 'thorough'], is_default: true) add_test_setup('slow', exclude_suites: ['thorough'], env: ['G_TEST_SLOW=1', 'SPEED=slow']) add_test_setup('thorough', env: ['G_TEST_SLOW=1', 'SPEED=thorough']) +meson.add_postconf_script(find_program('scripts/symlink-install-tree.py')) + +#################### +# Global variables # +#################### + not_found = dependency('', required: false) keyval = import('keyval') ss = import('sourceset') fs = import('fs') -sh = find_program('sh') -cc = meson.get_compiler('c') +host_os = host_machine.system() config_host = keyval.load(meson.current_build_dir() / 'config-host.mak') -enable_modules = 'CONFIG_MODULES' in config_host -enable_static = 'CONFIG_STATIC' in config_host - -# Allow both shared and static libraries unless --enable-static -static_kwargs = enable_static ? {'static': true} : {} # Temporary directory used for files created while # configure runs. Since it is in the build directory @@ -40,117 +40,37 @@ qemu_moddir = get_option('libdir') / get_option('qemu_suffix') qemu_desktopdir = get_option('datadir') / 'applications' qemu_icondir = get_option('datadir') / 'icons' -config_host_data = configuration_data() genh = [] qapi_trace_events = [] bsd_oses = ['gnu/kfreebsd', 'freebsd', 'netbsd', 'openbsd', 'dragonfly', 'darwin'] supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux'] -supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv', 'x86', 'x86_64', - 'arm', 'aarch64', 'loongarch64', 'mips', 'mips64', 'sparc', 'sparc64'] +supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv32', 'riscv64', 'x86', 'x86_64', + 'arm', 'aarch64', 'loongarch64', 'mips', 'mips64', 'sparc64'] cpu = host_machine.cpu_family() -# Unify riscv* to a single family. -if cpu in ['riscv32', 'riscv64'] - cpu = 'riscv' -endif - -targetos = host_machine.system() - target_dirs = config_host['TARGET_DIRS'].split() -have_linux_user = false -have_bsd_user = false -have_system = false -foreach target : target_dirs - have_linux_user = have_linux_user or target.endswith('linux-user') - have_bsd_user = have_bsd_user or target.endswith('bsd-user') - have_system = have_system or target.endswith('-softmmu') -endforeach -have_user = have_linux_user or have_bsd_user -have_tools = get_option('tools') \ - .disable_auto_if(not have_system) \ - .allowed() -have_ga = get_option('guest_agent') \ - .disable_auto_if(not have_system and not have_tools) \ - .require(targetos in ['sunos', 'linux', 'windows'], - error_message: 'unsupported OS for QEMU guest agent') \ - .allowed() -have_block = have_system or have_tools +############ +# Programs # +############ + +sh = find_program('sh') python = import('python').find_installation() -if cpu not in supported_cpus - host_arch = 'unknown' -elif cpu == 'x86' - host_arch = 'i386' -elif cpu == 'mips64' - host_arch = 'mips' -else - host_arch = cpu +cc = meson.get_compiler('c') +all_languages = ['c'] +if host_os == 'windows' and add_languages('cpp', required: false, native: false) + all_languages += ['cpp'] + cxx = meson.get_compiler('cpp') endif - -if cpu in ['x86', 'x86_64'] - kvm_targets = ['i386-softmmu', 'x86_64-softmmu'] -elif cpu == 'aarch64' - kvm_targets = ['aarch64-softmmu'] -elif cpu == 's390x' - kvm_targets = ['s390x-softmmu'] -elif cpu in ['ppc', 'ppc64'] - kvm_targets = ['ppc-softmmu', 'ppc64-softmmu'] -elif cpu in ['mips', 'mips64'] - kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu'] -elif cpu in ['riscv'] - kvm_targets = ['riscv32-softmmu', 'riscv64-softmmu'] -else - kvm_targets = [] +if host_os == 'darwin' and \ + add_languages('objc', required: true, native: false) + all_languages += ['objc'] + objc = meson.get_compiler('objc') endif -kvm_targets_c = '""' -if get_option('kvm').allowed() and targetos == 'linux' - kvm_targets_c = '"' + '" ,"'.join(kvm_targets) + '"' -endif -config_host_data.set('CONFIG_KVM_TARGETS', kvm_targets_c) - -accelerator_targets = { 'CONFIG_KVM': kvm_targets } - -if cpu in ['aarch64'] - accelerator_targets += { - 'CONFIG_HVF': ['aarch64-softmmu'] - } -endif - -if cpu in ['x86', 'x86_64', 'arm', 'aarch64'] - # i386 emulator provides xenpv machine type for multiple architectures - accelerator_targets += { - 'CONFIG_XEN': ['i386-softmmu', 'x86_64-softmmu'], - } -endif -if cpu in ['x86', 'x86_64'] - accelerator_targets += { - 'CONFIG_HAX': ['i386-softmmu', 'x86_64-softmmu'], - 'CONFIG_HVF': ['x86_64-softmmu'], - 'CONFIG_NVMM': ['i386-softmmu', 'x86_64-softmmu'], - 'CONFIG_WHPX': ['i386-softmmu', 'x86_64-softmmu'], - } -endif - -modular_tcg = [] -# Darwin does not support references to thread-local variables in modules -if targetos != 'darwin' - modular_tcg = ['i386-softmmu', 'x86_64-softmmu'] -endif - -edk2_targets = [ 'arm-softmmu', 'aarch64-softmmu', 'i386-softmmu', 'x86_64-softmmu' ] -unpack_edk2_blobs = false -foreach target : edk2_targets - if target in target_dirs - bzip2 = find_program('bzip2', required: get_option('install_blobs')) - unpack_edk2_blobs = bzip2.found() - break - endif -endforeach - dtrace = not_found stap = not_found if 'dtrace' in get_option('trace_backends') @@ -163,7 +83,7 @@ if 'dtrace' in get_option('trace_backends') # semaphores are linked into the main binary and not the module's shared # object. add_global_arguments('-DSTAP_SDT_V2', - native: false, language: ['c', 'cpp', 'objc']) + native: false, language: all_languages) endif endif @@ -173,106 +93,19 @@ else iasl = find_program(get_option('iasl'), required: true) endif -################## -# Compiler flags # -################## - -qemu_cflags = config_host['QEMU_CFLAGS'].split() -qemu_cxxflags = config_host['QEMU_CXXFLAGS'].split() -qemu_objcflags = config_host['QEMU_OBJCFLAGS'].split() -qemu_ldflags = config_host['QEMU_LDFLAGS'].split() - -if targetos == 'windows' - qemu_ldflags += cc.get_supported_link_arguments('-Wl,--no-seh', '-Wl,--nxcompat') - # Disable ASLR for debug builds to allow debugging with gdb - if get_option('optimization') == '0' - qemu_ldflags += cc.get_supported_link_arguments('-Wl,--dynamicbase') +edk2_targets = [ 'arm-softmmu', 'aarch64-softmmu', 'i386-softmmu', 'x86_64-softmmu' ] +unpack_edk2_blobs = false +foreach target : edk2_targets + if target in target_dirs + bzip2 = find_program('bzip2', required: get_option('install_blobs')) + unpack_edk2_blobs = bzip2.found() + break endif -endif +endforeach -if get_option('gprof') - qemu_cflags += ['-p'] - qemu_cxxflags += ['-p'] - qemu_objcflags += ['-p'] - qemu_ldflags += ['-p'] -endif - -# Specify linker-script with add_project_link_arguments so that it is not placed -# within a linker --start-group/--end-group pair -if get_option('fuzzing') - add_project_link_arguments(['-Wl,-T,', - (meson.current_source_dir() / 'tests/qtest/fuzz/fork_fuzz.ld')], - native: false, language: ['c', 'cpp', 'objc']) - - # Specify a filter to only instrument code that is directly related to - # virtual-devices. - configure_file(output: 'instrumentation-filter', - input: 'scripts/oss-fuzz/instrumentation-filter-template', - copy: true) - add_global_arguments( - cc.get_supported_arguments('-fsanitize-coverage-allowlist=instrumentation-filter'), - native: false, language: ['c', 'cpp', 'objc']) - - if get_option('fuzzing_engine') == '' - # Add CFLAGS to tell clang to add fuzzer-related instrumentation to all the - # compiled code. To build non-fuzzer binaries with --enable-fuzzing, link - # everything with fsanitize=fuzzer-no-link. Otherwise, the linker will be - # unable to bind the fuzzer-related callbacks added by instrumentation. - add_global_arguments('-fsanitize=fuzzer-no-link', - native: false, language: ['c', 'cpp', 'objc']) - add_global_link_arguments('-fsanitize=fuzzer-no-link', - native: false, language: ['c', 'cpp', 'objc']) - # For the actual fuzzer binaries, we need to link against the libfuzzer - # library. They need to be configurable, to support OSS-Fuzz - fuzz_exe_ldflags = ['-fsanitize=fuzzer'] - else - # LIB_FUZZING_ENGINE was set; assume we are running on OSS-Fuzz, and - # the needed CFLAGS have already been provided - fuzz_exe_ldflags = get_option('fuzzing_engine').split() - endif -endif - -add_global_arguments(qemu_cflags, native: false, language: ['c']) -add_global_arguments(qemu_cxxflags, native: false, language: ['cpp']) -add_global_arguments(qemu_objcflags, native: false, language: ['objc']) -add_global_link_arguments(qemu_ldflags, native: false, language: ['c', 'cpp', 'objc']) - -if targetos == 'linux' - add_project_arguments('-isystem', meson.current_source_dir() / 'linux-headers', - '-isystem', 'linux-headers', - language: ['c', 'cpp']) -endif - -add_project_arguments('-iquote', '.', - '-iquote', meson.current_source_dir(), - '-iquote', meson.current_source_dir() / 'include', - '-iquote', meson.current_source_dir() / 'disas/libvixl', - language: ['c', 'cpp', 'objc']) - -link_language = meson.get_external_property('link_language', 'cpp') -if link_language == 'cpp' - add_languages('cpp', required: true, native: false) - cxx = meson.get_compiler('cpp') - linker = cxx -else - linker = cc -endif -if host_machine.system() == 'darwin' - add_languages('objc', required: false, native: false) -endif - -sparse = find_program('cgcc', required: get_option('sparse')) -if sparse.found() - run_target('sparse', - command: [find_program('scripts/check_sparse.py'), - 'compile_commands.json', sparse.full_path(), '-Wbitwise', - '-Wno-transparent-union', '-Wno-old-initializer', - '-Wno-non-pointer-null']) -endif - -########################################### -# Target-specific checks and dependencies # -########################################### +##################### +# Option validation # +##################### # Fuzzing if get_option('fuzzing') and get_option('fuzzing_engine') == '' and \ @@ -287,7 +120,7 @@ if get_option('fuzzing') and get_option('fuzzing_engine') == '' and \ endif # Tracing backends -if 'ftrace' in get_option('trace_backends') and targetos != 'linux' +if 'ftrace' in get_option('trace_backends') and host_os != 'linux' error('ftrace is supported only on Linux') endif if 'syslog' in get_option('trace_backends') and not cc.compiles(''' @@ -302,26 +135,30 @@ endif # Miscellaneous Linux-only features get_option('mpath') \ - .require(targetos == 'linux', error_message: 'Multipath is supported only on Linux') + .require(host_os == 'linux', error_message: 'Multipath is supported only on Linux') multiprocess_allowed = get_option('multiprocess') \ - .require(targetos == 'linux', error_message: 'Multiprocess QEMU is supported only on Linux') \ + .require(host_os == 'linux', error_message: 'Multiprocess QEMU is supported only on Linux') \ + .allowed() + +vfio_user_server_allowed = get_option('vfio_user_server') \ + .require(host_os == 'linux', error_message: 'vfio-user server is supported only on Linux') \ .allowed() have_tpm = get_option('tpm') \ - .require(targetos != 'windows', error_message: 'TPM emulation only available on POSIX systems') \ + .require(host_os != 'windows', error_message: 'TPM emulation only available on POSIX systems') \ .allowed() # vhost have_vhost_user = get_option('vhost_user') \ - .disable_auto_if(targetos != 'linux') \ - .require(targetos != 'windows', + .disable_auto_if(host_os != 'linux') \ + .require(host_os != 'windows', error_message: 'vhost-user is not available on Windows').allowed() have_vhost_vdpa = get_option('vhost_vdpa') \ - .require(targetos == 'linux', + .require(host_os == 'linux', error_message: 'vhost-vdpa is only available on Linux').allowed() have_vhost_kernel = get_option('vhost_kernel') \ - .require(targetos == 'linux', + .require(host_os == 'linux', error_message: 'vhost-kernel is only available on Linux').allowed() have_vhost_user_crypto = get_option('vhost_crypto') \ .require(have_vhost_user, @@ -334,7 +171,501 @@ have_vhost_net_vdpa = have_vhost_vdpa and get_option('vhost_net').allowed() have_vhost_net_kernel = have_vhost_kernel and get_option('vhost_net').allowed() have_vhost_net = have_vhost_net_kernel or have_vhost_net_user or have_vhost_net_vdpa -# Target-specific libraries and flags +# type of binaries to build +have_linux_user = false +have_bsd_user = false +have_system = false +foreach target : target_dirs + have_linux_user = have_linux_user or target.endswith('linux-user') + have_bsd_user = have_bsd_user or target.endswith('bsd-user') + have_system = have_system or target.endswith('-softmmu') +endforeach +have_user = have_linux_user or have_bsd_user + +have_tools = get_option('tools') \ + .disable_auto_if(not have_system) \ + .allowed() +have_ga = get_option('guest_agent') \ + .disable_auto_if(not have_system and not have_tools) \ + .require(host_os in ['sunos', 'linux', 'windows', 'freebsd', 'netbsd', 'openbsd'], + error_message: 'unsupported OS for QEMU guest agent') \ + .allowed() +have_block = have_system or have_tools + +enable_modules = get_option('modules') \ + .require(host_os != 'windows', + error_message: 'Modules are not available for Windows') \ + .require(not get_option('prefer_static'), + error_message: 'Modules are incompatible with static linking') \ + .allowed() + +####################################### +# Variables for host and accelerators # +####################################### + +if cpu not in supported_cpus + host_arch = 'unknown' +elif cpu == 'x86' + host_arch = 'i386' +elif cpu == 'mips64' + host_arch = 'mips' +elif cpu in ['riscv32', 'riscv64'] + host_arch = 'riscv' +else + host_arch = cpu +endif + +if cpu in ['x86', 'x86_64'] + kvm_targets = ['i386-softmmu', 'x86_64-softmmu'] +elif cpu == 'aarch64' + kvm_targets = ['aarch64-softmmu'] +elif cpu == 's390x' + kvm_targets = ['s390x-softmmu'] +elif cpu in ['ppc', 'ppc64'] + kvm_targets = ['ppc-softmmu', 'ppc64-softmmu'] +elif cpu in ['mips', 'mips64'] + kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu'] +elif cpu in ['riscv32'] + kvm_targets = ['riscv32-softmmu'] +elif cpu in ['riscv64'] + kvm_targets = ['riscv64-softmmu'] +elif cpu in ['loongarch64'] + kvm_targets = ['loongarch64-softmmu'] +else + kvm_targets = [] +endif +accelerator_targets = { 'CONFIG_KVM': kvm_targets } + +if cpu in ['x86', 'x86_64'] + xen_targets = ['i386-softmmu', 'x86_64-softmmu'] +elif cpu in ['arm', 'aarch64'] + # i386 emulator provides xenpv machine type for multiple architectures + xen_targets = ['i386-softmmu', 'x86_64-softmmu', 'aarch64-softmmu'] +else + xen_targets = [] +endif +accelerator_targets += { 'CONFIG_XEN': xen_targets } + +if cpu in ['aarch64'] + accelerator_targets += { + 'CONFIG_HVF': ['aarch64-softmmu'] + } +endif + +if cpu in ['x86', 'x86_64'] + accelerator_targets += { + 'CONFIG_HVF': ['x86_64-softmmu'], + 'CONFIG_NVMM': ['i386-softmmu', 'x86_64-softmmu'], + 'CONFIG_WHPX': ['i386-softmmu', 'x86_64-softmmu'], + } +endif + +modular_tcg = [] +# Darwin does not support references to thread-local variables in modules +if host_os != 'darwin' + modular_tcg = ['i386-softmmu', 'x86_64-softmmu'] +endif + +################## +# Compiler flags # +################## + +foreach lang : all_languages + compiler = meson.get_compiler(lang) + if compiler.get_id() == 'gcc' and compiler.version().version_compare('>=7.4') + # ok + elif compiler.get_id() == 'clang' and compiler.compiles(''' + #ifdef __apple_build_version__ + # if __clang_major__ < 12 || (__clang_major__ == 12 && __clang_minor__ < 0) + # error You need at least XCode Clang v12.0 to compile QEMU + # endif + #else + # if __clang_major__ < 10 || (__clang_major__ == 10 && __clang_minor__ < 0) + # error You need at least Clang v10.0 to compile QEMU + # endif + #endif''') + # ok + else + error('You either need GCC v7.4 or Clang v10.0 (or XCode Clang v12.0) to compile QEMU') + endif +endforeach + +# default flags for all hosts +# We use -fwrapv to tell the compiler that we require a C dialect where +# left shift of signed integers is well defined and has the expected +# 2s-complement style results. (Both clang and gcc agree that it +# provides these semantics.) + +qemu_common_flags = [ + '-D_GNU_SOURCE', '-D_FILE_OFFSET_BITS=64', '-D_LARGEFILE_SOURCE', + '-fno-strict-aliasing', '-fno-common', '-fwrapv' ] +qemu_cflags = [] +qemu_ldflags = [] + +if host_os == 'darwin' + # 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. + if compiler.get_id() == 'gcc' + qemu_common_flags += '-DOS_OBJECT_USE_OBJC=0' + endif +elif host_os == 'sunos' + # needed for CMSG_ macros in sys/socket.h + qemu_common_flags += '-D_XOPEN_SOURCE=600' + # needed for TIOCWIN* defines in termios.h + qemu_common_flags += '-D__EXTENSIONS__' +elif host_os == 'haiku' + qemu_common_flags += ['-DB_USE_POSITIVE_POSIX_ERRORS', '-D_BSD_SOURCE', '-fPIC'] +endif + +# __sync_fetch_and_and requires at least -march=i486. Many toolchains +# use i686 as default anyway, but for those that don't, an explicit +# specification is necessary +if host_arch == 'i386' and not cc.links(''' + static int sfaa(int *ptr) + { + return __sync_fetch_and_and(ptr, 0); + } + + int main(void) + { + int val = 42; + val = __sync_val_compare_and_swap(&val, 0, 1); + sfaa(&val); + return val; + }''') + qemu_common_flags = ['-march=i486'] + qemu_common_flags +endif + +if get_option('prefer_static') + qemu_ldflags += get_option('b_pie') ? '-static-pie' : '-static' +endif + +# Meson currently only handles pie as a boolean for now, so if the user +# has explicitly disabled PIE we need to extend our cflags. +# +# -no-pie is supposedly a linker flag that has no effect on the compiler +# command line, but some distros, that didn't quite know what they were +# doing, made local changes to gcc's specs file that turned it into +# a compiler command-line flag. +# +# What about linker flags? For a static build, no PIE is implied by -static +# which we added above (and if it's not because of the same specs patching, +# there's nothing we can do: compilation will fail, report a bug to your +# distro and do not use --disable-pie in the meanwhile). For dynamic linking, +# instead, we can't add -no-pie because it overrides -shared: the linker then +# tries to build an executable instead of a shared library and fails. So +# don't add -no-pie anywhere and cross fingers. :( +if not get_option('b_pie') + qemu_common_flags += cc.get_supported_arguments('-fno-pie', '-no-pie') +endif + +if not get_option('stack_protector').disabled() + stack_protector_probe = ''' + int main(int argc, char *argv[]) + { + char arr[64], *p = arr, *c = argv[argc - 1]; + while (*c) { + *p++ = *c++; + } + return 0; + }''' + have_stack_protector = false + foreach arg : ['-fstack-protector-strong', '-fstack-protector-all'] + # We need to check both a compile and a link, since some compiler + # setups fail only on a .c->.o compile and some only at link time + if cc.compiles(stack_protector_probe, args: ['-Werror', arg]) and \ + cc.links(stack_protector_probe, args: ['-Werror', arg]) + have_stack_protector = true + qemu_cflags += arg + qemu_ldflags += arg + break + endif + endforeach + get_option('stack_protector') \ + .require(have_stack_protector, error_message: 'Stack protector not supported') +endif + +coroutine_backend = get_option('coroutine_backend') +ucontext_probe = ''' + #include + #ifdef __stub_makecontext + #error Ignoring glibc stub makecontext which will always fail + #endif + int main(void) { makecontext(0, 0, 0); return 0; }''' + +# On Windows the only valid backend is the Windows specific one. +# For POSIX prefer ucontext, but it's not always possible. The fallback +# is sigcontext. +supported_backends = [] +if host_os == 'windows' + supported_backends += ['windows'] +else + if host_os != 'darwin' and cc.links(ucontext_probe) + supported_backends += ['ucontext'] + endif + supported_backends += ['sigaltstack'] +endif + +if coroutine_backend == 'auto' + coroutine_backend = supported_backends[0] +elif coroutine_backend not in supported_backends + error('"@0@" backend requested but not available. Available backends: @1@' \ + .format(coroutine_backend, ', '.join(supported_backends))) +endif + +# Compiles if SafeStack *not* enabled +safe_stack_probe = ''' + int main(void) + { + #if defined(__has_feature) + #if __has_feature(safe_stack) + #error SafeStack Enabled + #endif + #endif + return 0; + }''' +if get_option('safe_stack') != not cc.compiles(safe_stack_probe) + safe_stack_arg = get_option('safe_stack') ? '-fsanitize=safe-stack' : '-fno-sanitize=safe-stack' + if get_option('safe_stack') != not cc.compiles(safe_stack_probe, args: safe_stack_arg) + error(get_option('safe_stack') \ + ? 'SafeStack not supported by your compiler' \ + : 'Cannot disable SafeStack') + endif + qemu_cflags += safe_stack_arg + qemu_ldflags += safe_stack_arg +endif +if get_option('safe_stack') and coroutine_backend != 'ucontext' + error('SafeStack is only supported with the ucontext coroutine backend') +endif + +if get_option('sanitizers') + if cc.has_argument('-fsanitize=address') + qemu_cflags = ['-fsanitize=address'] + qemu_cflags + qemu_ldflags = ['-fsanitize=address'] + qemu_ldflags + endif + + # Detect static linking issue with ubsan - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84285 + if cc.links('int main(int argc, char **argv) { return argc + 1; }', + args: [qemu_ldflags, '-fsanitize=undefined']) + qemu_cflags = ['-fsanitize=undefined'] + qemu_cflags + qemu_ldflags = ['-fsanitize=undefined'] + qemu_ldflags + endif +endif + +# Thread sanitizer is, for now, much noisier than the other sanitizers; +# keep it separate until that is not the case. +if get_option('tsan') + if get_option('sanitizers') + error('TSAN is not supported with other sanitizers') + endif + if not cc.has_function('__tsan_create_fiber', + args: '-fsanitize=thread', + prefix: '#include ') + error('Cannot enable TSAN due to missing fiber annotation interface') + endif + qemu_cflags = ['-fsanitize=thread'] + qemu_cflags + qemu_ldflags = ['-fsanitize=thread'] + qemu_ldflags +endif + +# Detect support for PT_GNU_RELRO + DT_BIND_NOW. +# The combination is known as "full relro", because .got.plt is read-only too. +qemu_ldflags += cc.get_supported_link_arguments('-Wl,-z,relro', '-Wl,-z,now') + +if host_os == 'windows' + qemu_ldflags += cc.get_supported_link_arguments('-Wl,--no-seh', '-Wl,--nxcompat') + qemu_ldflags += cc.get_supported_link_arguments('-Wl,--dynamicbase', '-Wl,--high-entropy-va') +endif + +if get_option('fuzzing') + # Specify a filter to only instrument code that is directly related to + # virtual-devices. + configure_file(output: 'instrumentation-filter', + input: 'scripts/oss-fuzz/instrumentation-filter-template', + copy: true) + + if cc.compiles('int main () { return 0; }', + name: '-fsanitize-coverage-allowlist=/dev/null', + args: ['-fsanitize-coverage-allowlist=/dev/null', + '-fsanitize-coverage=trace-pc'] ) + qemu_common_flags += ['-fsanitize-coverage-allowlist=instrumentation-filter'] + endif + + if get_option('fuzzing_engine') == '' + # Add CFLAGS to tell clang to add fuzzer-related instrumentation to all the + # compiled code. To build non-fuzzer binaries with --enable-fuzzing, link + # everything with fsanitize=fuzzer-no-link. Otherwise, the linker will be + # unable to bind the fuzzer-related callbacks added by instrumentation. + qemu_common_flags += ['-fsanitize=fuzzer-no-link'] + qemu_ldflags += ['-fsanitize=fuzzer-no-link'] + # For the actual fuzzer binaries, we need to link against the libfuzzer + # library. They need to be configurable, to support OSS-Fuzz + fuzz_exe_ldflags = ['-fsanitize=fuzzer'] + else + # LIB_FUZZING_ENGINE was set; assume we are running on OSS-Fuzz, and + # the needed CFLAGS have already been provided + fuzz_exe_ldflags = get_option('fuzzing_engine').split() + endif +endif + +if get_option('cfi') + cfi_flags=[] + # Check for dependency on LTO + if not get_option('b_lto') + error('Selected Control-Flow Integrity but LTO is disabled') + endif + if enable_modules + error('Selected Control-Flow Integrity is not compatible with modules') + endif + # Check for cfi flags. CFI requires LTO so we can't use + # get_supported_arguments, but need a more complex "compiles" which allows + # custom arguments + if cc.compiles('int main () { return 0; }', name: '-fsanitize=cfi-icall', + args: ['-flto', '-fsanitize=cfi-icall'] ) + cfi_flags += '-fsanitize=cfi-icall' + else + error('-fsanitize=cfi-icall is not supported by the compiler') + endif + if cc.compiles('int main () { return 0; }', + name: '-fsanitize-cfi-icall-generalize-pointers', + args: ['-flto', '-fsanitize=cfi-icall', + '-fsanitize-cfi-icall-generalize-pointers'] ) + cfi_flags += '-fsanitize-cfi-icall-generalize-pointers' + else + error('-fsanitize-cfi-icall-generalize-pointers is not supported by the compiler') + endif + if get_option('cfi_debug') + if cc.compiles('int main () { return 0; }', + name: '-fno-sanitize-trap=cfi-icall', + args: ['-flto', '-fsanitize=cfi-icall', + '-fno-sanitize-trap=cfi-icall'] ) + cfi_flags += '-fno-sanitize-trap=cfi-icall' + else + error('-fno-sanitize-trap=cfi-icall is not supported by the compiler') + endif + endif + add_global_arguments(cfi_flags, native: false, language: all_languages) + add_global_link_arguments(cfi_flags, native: false, language: all_languages) +endif + +# Check further flags that make QEMU more robust against malicious parties + +hardening_flags = [ + # Initialize all stack variables to zero. This makes + # it harder to take advantage of uninitialized stack + # data to drive exploits + '-ftrivial-auto-var-init=zero', +] + +# Zero out registers used during a function call +# upon its return. This makes it harder to assemble +# ROP gadgets into something usable +# +# NB: Clang 17 is broken and SEGVs +# https://github.com/llvm/llvm-project/issues/75168 +# +# NB2: This clashes with the "retguard" extension of OpenBSD's Clang +# https://gitlab.com/qemu-project/qemu/-/issues/2278 +if host_os != 'openbsd' and \ + cc.compiles('extern struct { void (*cb)(void); } s; void f(void) { s.cb(); }', + name: '-fzero-call-used-regs=used-gpr', + args: ['-O2', '-fzero-call-used-regs=used-gpr']) + hardening_flags += '-fzero-call-used-regs=used-gpr' +endif + +qemu_common_flags += cc.get_supported_arguments(hardening_flags) + +add_global_arguments(qemu_common_flags, native: false, language: all_languages) +add_global_link_arguments(qemu_ldflags, native: false, language: all_languages) + +# Collect warning flags we want to set, sorted alphabetically +warn_flags = [ + # First enable interesting warnings + '-Wempty-body', + '-Wendif-labels', + '-Wexpansion-to-defined', + '-Wformat-security', + '-Wformat-y2k', + '-Wignored-qualifiers', + '-Wimplicit-fallthrough=2', + '-Winit-self', + '-Wmissing-format-attribute', + '-Wmissing-prototypes', + '-Wnested-externs', + '-Wold-style-declaration', + '-Wold-style-definition', + '-Wredundant-decls', + '-Wshadow=local', + '-Wstrict-prototypes', + '-Wtype-limits', + '-Wundef', + '-Wvla', + '-Wwrite-strings', + + # Then disable some undesirable warnings + '-Wno-gnu-variable-sized-type-not-at-end', + '-Wno-initializer-overrides', + '-Wno-missing-include-dirs', + '-Wno-psabi', + '-Wno-shift-negative-value', + '-Wno-string-plus-int', + '-Wno-tautological-type-limit-compare', + '-Wno-typedef-redefinition', +] + +if host_os != 'darwin' + warn_flags += ['-Wthread-safety'] +endif + +# Set up C++ compiler flags +qemu_cxxflags = [] +if 'cpp' in all_languages + qemu_cxxflags = ['-D__STDC_LIMIT_MACROS', '-D__STDC_CONSTANT_MACROS', '-D__STDC_FORMAT_MACROS'] + qemu_cflags +endif + +add_project_arguments(qemu_cflags, native: false, language: 'c') +add_project_arguments(cc.get_supported_arguments(warn_flags), native: false, language: 'c') +if 'cpp' in all_languages + add_project_arguments(qemu_cxxflags, native: false, language: 'cpp') + add_project_arguments(cxx.get_supported_arguments(warn_flags), native: false, language: 'cpp') +endif +if 'objc' in all_languages + # Note sanitizer flags are not applied to Objective-C sources! + add_project_arguments(objc.get_supported_arguments(warn_flags), native: false, language: 'objc') +endif +if host_os == 'linux' + add_project_arguments('-isystem', meson.current_source_dir() / 'linux-headers', + '-isystem', 'linux-headers', + language: all_languages) +endif + +add_project_arguments('-iquote', '.', + '-iquote', meson.current_source_dir(), + '-iquote', meson.current_source_dir() / 'include', + language: all_languages) + +# If a host-specific include directory exists, list that first... +host_include = meson.current_source_dir() / 'host/include/' +if fs.is_dir(host_include / host_arch) + add_project_arguments('-iquote', host_include / host_arch, + language: all_languages) +endif +# ... followed by the generic fallback. +add_project_arguments('-iquote', host_include / 'generic', + language: all_languages) + +sparse = find_program('cgcc', required: get_option('sparse')) +if sparse.found() + run_target('sparse', + command: [find_program('scripts/check_sparse.py'), + 'compile_commands.json', sparse.full_path(), '-Wbitwise', + '-Wno-transparent-union', '-Wno-old-initializer', + '-Wno-non-pointer-null']) +endif + +##################################### +# Host-specific libraries and flags # +##################################### + libm = cc.find_library('m', required: false) threads = dependency('threads') util = cc.find_library('util', required: false) @@ -344,14 +675,14 @@ version_res = [] coref = [] iokit = [] emulator_link_args = [] -nvmm =not_found -hvf = not_found midl = not_found widl = not_found +pathcch = not_found host_dsosuf = '.so' -if targetos == 'windows' +if host_os == 'windows' midl = find_program('midl', required: false) widl = find_program('widl', required: false) + pathcch = cc.find_library('pathcch') socket = cc.find_library('ws2_32') winmm = cc.find_library('winmm') @@ -360,38 +691,43 @@ if targetos == 'windows' depend_files: files('pc-bios/qemu-nsis.ico'), include_directories: include_directories('.')) host_dsosuf = '.dll' -elif targetos == 'darwin' +elif host_os == 'darwin' coref = dependency('appleframeworks', modules: 'CoreFoundation') iokit = dependency('appleframeworks', modules: 'IOKit', required: false) host_dsosuf = '.dylib' -elif targetos == 'sunos' +elif host_os == 'sunos' socket = [cc.find_library('socket'), cc.find_library('nsl'), cc.find_library('resolv')] -elif targetos == 'haiku' +elif host_os == 'haiku' socket = [cc.find_library('posix_error_mapper'), cc.find_library('network'), cc.find_library('bsd')] -elif targetos == 'openbsd' +elif host_os == 'openbsd' if get_option('tcg').allowed() and target_dirs.length() > 0 # Disable OpenBSD W^X if available emulator_link_args = cc.get_supported_link_arguments('-Wl,-z,wxneeded') endif endif -# Target-specific configuration of accelerators +############################################### +# Host-specific configuration of accelerators # +############################################### + accelerators = [] -if get_option('kvm').allowed() and targetos == 'linux' +if get_option('kvm').allowed() and host_os == 'linux' accelerators += 'CONFIG_KVM' endif -if get_option('whpx').allowed() and targetos == 'windows' +if get_option('whpx').allowed() and host_os == 'windows' if get_option('whpx').enabled() and host_machine.cpu() != 'x86_64' error('WHPX requires 64-bit host') - elif cc.has_header('WinHvPlatform.h', required: get_option('whpx')) and \ - cc.has_header('WinHvEmulation.h', required: get_option('whpx')) + elif cc.has_header('winhvplatform.h', required: get_option('whpx')) and \ + cc.has_header('winhvemulation.h', required: get_option('whpx')) accelerators += 'CONFIG_WHPX' endif endif + +hvf = not_found if get_option('hvf').allowed() hvf = dependency('appleframeworks', modules: 'Hypervisor', required: get_option('hvf')) @@ -399,12 +735,9 @@ if get_option('hvf').allowed() accelerators += 'CONFIG_HVF' endif endif -if get_option('hax').allowed() - if get_option('hax').enabled() or targetos in ['windows', 'darwin', 'netbsd'] - accelerators += 'CONFIG_HAX' - endif -endif -if targetos == 'netbsd' + +nvmm = not_found +if host_os == 'netbsd' nvmm = cc.find_library('nvmm', required: get_option('nvmm')) if nvmm.found() accelerators += 'CONFIG_NVMM' @@ -414,9 +747,7 @@ endif tcg_arch = host_arch if get_option('tcg').allowed() if host_arch == 'unknown' - if get_option('tcg_interpreter') - warning('Unsupported CPU @0@, will use TCG with TCI (slow)'.format(cpu)) - else + if not get_option('tcg_interpreter') error('Unsupported CPU @0@, try --enable-tcg-interpreter'.format(cpu)) endif elif get_option('tcg_interpreter') @@ -429,18 +760,15 @@ if get_option('tcg').allowed() endif if get_option('tcg_interpreter') tcg_arch = 'tci' - elif host_arch == 'sparc64' - tcg_arch = 'sparc' elif host_arch == 'x86_64' tcg_arch = 'i386' elif host_arch == 'ppc64' tcg_arch = 'ppc' endif add_project_arguments('-iquote', meson.current_source_dir() / 'tcg' / tcg_arch, - language: ['c', 'cpp', 'objc']) + language: all_languages) accelerators += 'CONFIG_TCG' - config_host += { 'CONFIG_TCG': 'y' } endif if 'CONFIG_KVM' not in accelerators and get_option('kvm').enabled() @@ -456,907 +784,45 @@ if 'CONFIG_WHPX' not in accelerators and get_option('whpx').enabled() error('WHPX not available on this platform') endif -################ -# Dependencies # -################ - -# The path to glib.h is added to all compilation commands. This was -# grandfathered in from the QEMU Makefiles. -add_project_arguments(config_host['GLIB_CFLAGS'].split(), - native: false, language: ['c', 'cpp', 'objc']) -glib = declare_dependency(compile_args: config_host['GLIB_CFLAGS'].split(), - link_args: config_host['GLIB_LIBS'].split(), - version: config_host['GLIB_VERSION'], - variables: { - 'bindir': config_host['GLIB_BINDIR'], - }) -# override glib dep with the configure results (for subprojects) -meson.override_dependency('glib-2.0', glib) - -gio = not_found -gdbus_codegen = not_found -if not get_option('gio').auto() or have_system - gio = dependency('gio-2.0', required: get_option('gio'), - method: 'pkg-config', kwargs: static_kwargs) - if gio.found() and not cc.links(''' - #include - int main(void) - { - g_dbus_proxy_new_sync(0, 0, 0, 0, 0, 0, 0, 0); - return 0; - }''', dependencies: [glib, gio]) - if get_option('gio').enabled() - error('The installed libgio is broken for static linking') - endif - gio = not_found - endif - if gio.found() - gdbus_codegen = find_program(gio.get_variable('gdbus_codegen'), - required: get_option('gio')) - gio_unix = dependency('gio-unix-2.0', required: get_option('gio'), - method: 'pkg-config', kwargs: static_kwargs) - gio = declare_dependency(dependencies: [gio, gio_unix], - version: gio.version()) - endif -endif - -lttng = not_found -if 'ust' in get_option('trace_backends') - lttng = dependency('lttng-ust', required: true, version: '>= 2.1', - method: 'pkg-config', kwargs: static_kwargs) -endif -pixman = not_found -if have_system or have_tools - pixman = dependency('pixman-1', required: have_system, version:'>=0.21.8', - method: 'pkg-config', kwargs: static_kwargs) -endif -zlib = dependency('zlib', required: true, kwargs: static_kwargs) - -libaio = not_found -if not get_option('linux_aio').auto() or have_block - libaio = cc.find_library('aio', has_headers: ['libaio.h'], - required: get_option('linux_aio'), - kwargs: static_kwargs) -endif - -linux_io_uring_test = ''' - #include - #include - - int main(void) { return 0; }''' - -linux_io_uring = not_found -if not get_option('linux_io_uring').auto() or have_block - linux_io_uring = dependency('liburing', version: '>=0.3', - required: get_option('linux_io_uring'), - method: 'pkg-config', kwargs: static_kwargs) - if not cc.links(linux_io_uring_test) - linux_io_uring = not_found - endif -endif - -libnfs = not_found -if not get_option('libnfs').auto() or have_block - libnfs = dependency('libnfs', version: '>=1.9.3', - required: get_option('libnfs'), - method: 'pkg-config', kwargs: static_kwargs) -endif - -libattr_test = ''' - #include - #include - #ifdef CONFIG_LIBATTR - #include - #else - #include - #endif - int main(void) { getxattr(NULL, NULL, NULL, 0); setxattr(NULL, NULL, NULL, 0, 0); return 0; }''' - -libattr = not_found -have_old_libattr = false -if get_option('attr').allowed() - if cc.links(libattr_test) - libattr = declare_dependency() - else - libattr = cc.find_library('attr', has_headers: ['attr/xattr.h'], - required: get_option('attr'), - kwargs: static_kwargs) - if libattr.found() and not \ - cc.links(libattr_test, dependencies: libattr, args: '-DCONFIG_LIBATTR') - libattr = not_found - if get_option('attr').enabled() - error('could not link libattr') - else - warning('could not link libattr, disabling') - endif - else - have_old_libattr = libattr.found() - endif - endif -endif - -cocoa = dependency('appleframeworks', modules: 'Cocoa', required: get_option('cocoa')) -if cocoa.found() and get_option('sdl').enabled() - error('Cocoa and SDL cannot be enabled at the same time') -endif -if cocoa.found() and get_option('gtk').enabled() - error('Cocoa and GTK+ cannot be enabled at the same time') -endif - -vmnet = dependency('appleframeworks', modules: 'vmnet', required: get_option('vmnet')) -if vmnet.found() and not cc.has_header_symbol('vmnet/vmnet.h', - 'VMNET_BRIDGED_MODE', - dependencies: vmnet) - vmnet = not_found - if get_option('vmnet').enabled() - error('vmnet.framework API is outdated') - else - warning('vmnet.framework API is outdated, disabling') - endif -endif - -seccomp = not_found -if not get_option('seccomp').auto() or have_system or have_tools - seccomp = dependency('libseccomp', version: '>=2.3.0', - required: get_option('seccomp'), - method: 'pkg-config', kwargs: static_kwargs) -endif - -libcap_ng = not_found -if not get_option('cap_ng').auto() or have_system or have_tools - libcap_ng = cc.find_library('cap-ng', has_headers: ['cap-ng.h'], - required: get_option('cap_ng'), - kwargs: static_kwargs) -endif -if libcap_ng.found() and not cc.links(''' - #include - int main(void) - { - capng_capability_to_name(CAPNG_EFFECTIVE); - return 0; - }''', dependencies: libcap_ng) - libcap_ng = not_found - if get_option('cap_ng').enabled() - error('could not link libcap-ng') - else - warning('could not link libcap-ng, disabling') - endif -endif - -if get_option('xkbcommon').auto() and not have_system and not have_tools - xkbcommon = not_found -else - xkbcommon = dependency('xkbcommon', required: get_option('xkbcommon'), - method: 'pkg-config', kwargs: static_kwargs) -endif - -vde = not_found -if not get_option('vde').auto() or have_system or have_tools - vde = cc.find_library('vdeplug', has_headers: ['libvdeplug.h'], - required: get_option('vde'), - kwargs: static_kwargs) -endif -if vde.found() and not cc.links(''' - #include - int main(void) - { - struct vde_open_args a = {0, 0, 0}; - char s[] = ""; - vde_open(s, s, &a); - return 0; - }''', dependencies: vde) - vde = not_found - if get_option('cap_ng').enabled() - error('could not link libvdeplug') - else - warning('could not link libvdeplug, disabling') - endif -endif - -pulse = not_found -if not get_option('pa').auto() or (targetos == 'linux' and have_system) - pulse = dependency('libpulse', required: get_option('pa'), - method: 'pkg-config', kwargs: static_kwargs) -endif -alsa = not_found -if not get_option('alsa').auto() or (targetos == 'linux' and have_system) - alsa = dependency('alsa', required: get_option('alsa'), - method: 'pkg-config', kwargs: static_kwargs) -endif -jack = not_found -if not get_option('jack').auto() or have_system - jack = dependency('jack', required: get_option('jack'), - method: 'pkg-config', kwargs: static_kwargs) -endif - -spice_protocol = not_found -if not get_option('spice_protocol').auto() or have_system - spice_protocol = dependency('spice-protocol', version: '>=0.12.3', - required: get_option('spice_protocol'), - method: 'pkg-config', kwargs: static_kwargs) -endif -spice = not_found -if not get_option('spice').auto() or have_system - spice = dependency('spice-server', version: '>=0.12.5', - required: get_option('spice'), - method: 'pkg-config', kwargs: static_kwargs) -endif -spice_headers = spice.partial_dependency(compile_args: true, includes: true) - -rt = cc.find_library('rt', required: false) - -libiscsi = not_found -if not get_option('libiscsi').auto() or have_block - libiscsi = dependency('libiscsi', version: '>=1.9.0', - required: get_option('libiscsi'), - method: 'pkg-config', kwargs: static_kwargs) -endif -zstd = not_found -if not get_option('zstd').auto() or have_block - zstd = dependency('libzstd', version: '>=1.4.0', - required: get_option('zstd'), - method: 'pkg-config', kwargs: static_kwargs) -endif -virgl = not_found - -have_vhost_user_gpu = have_tools and targetos == 'linux' and pixman.found() -if not get_option('virglrenderer').auto() or have_system or have_vhost_user_gpu - virgl = dependency('virglrenderer', - method: 'pkg-config', - required: get_option('virglrenderer'), - kwargs: static_kwargs) -endif -curl = not_found -if not get_option('curl').auto() or have_block - curl = dependency('libcurl', version: '>=7.29.0', - method: 'pkg-config', - required: get_option('curl'), - kwargs: static_kwargs) -endif -libudev = not_found -if targetos == 'linux' and (have_system or have_tools) - libudev = dependency('libudev', - method: 'pkg-config', - required: get_option('libudev'), - kwargs: static_kwargs) -endif - -mpathlibs = [libudev] -mpathpersist = not_found -mpathpersist_new_api = false -if targetos == 'linux' and have_tools and get_option('mpath').allowed() - mpath_test_source_new = ''' - #include - #include - unsigned mpath_mx_alloc_len = 1024; - int logsink; - static struct config *multipath_conf; - extern struct udev *udev; - extern struct config *get_multipath_config(void); - extern void put_multipath_config(struct config *conf); - struct udev *udev; - struct config *get_multipath_config(void) { return multipath_conf; } - void put_multipath_config(struct config *conf) { } - int main(void) { - udev = udev_new(); - multipath_conf = mpath_lib_init(); - return 0; - }''' - mpath_test_source_old = ''' - #include - #include - unsigned mpath_mx_alloc_len = 1024; - int logsink; - int main(void) { - struct udev *udev = udev_new(); - mpath_lib_init(udev); - return 0; - }''' - libmpathpersist = cc.find_library('mpathpersist', - required: get_option('mpath'), - kwargs: static_kwargs) - if libmpathpersist.found() - mpathlibs += libmpathpersist - if enable_static - mpathlibs += cc.find_library('devmapper', - required: get_option('mpath'), - kwargs: static_kwargs) - endif - mpathlibs += cc.find_library('multipath', - required: get_option('mpath'), - kwargs: static_kwargs) - foreach lib: mpathlibs - if not lib.found() - mpathlibs = [] - break - endif - endforeach - if mpathlibs.length() == 0 - msg = 'Dependencies missing for libmpathpersist' - elif cc.links(mpath_test_source_new, dependencies: mpathlibs) - mpathpersist = declare_dependency(dependencies: mpathlibs) - mpathpersist_new_api = true - elif cc.links(mpath_test_source_old, dependencies: mpathlibs) - mpathpersist = declare_dependency(dependencies: mpathlibs) - else - msg = 'Cannot detect libmpathpersist API' - endif - if not mpathpersist.found() - if get_option('mpath').enabled() - error(msg) - else - warning(msg + ', disabling') - endif - endif - endif -endif - -iconv = not_found -curses = not_found -if have_system and get_option('curses').allowed() - curses_test = ''' - #if defined(__APPLE__) || defined(__OpenBSD__) - #define _XOPEN_SOURCE_EXTENDED 1 - #endif - #include - #include - #include - int main(void) { - wchar_t wch = L'w'; - setlocale(LC_ALL, ""); - resize_term(0, 0); - addwstr(L"wide chars\n"); - addnwstr(&wch, 1); - add_wch(WACS_DEGREE); - return 0; - }''' - - curses_dep_list = targetos == 'windows' ? ['ncurses', 'ncursesw'] : ['ncursesw'] - foreach curses_dep : curses_dep_list - if not curses.found() - curses = dependency(curses_dep, - required: false, - method: 'pkg-config', - kwargs: static_kwargs) - endif - endforeach - msg = get_option('curses').enabled() ? 'curses library not found' : '' - curses_compile_args = ['-DNCURSES_WIDECHAR=1'] - if curses.found() - if cc.links(curses_test, args: curses_compile_args, dependencies: [curses]) - curses = declare_dependency(compile_args: curses_compile_args, dependencies: [curses]) - else - msg = 'curses package not usable' - curses = not_found - endif - endif - if not curses.found() - has_curses_h = cc.has_header('curses.h', args: curses_compile_args) - if targetos != 'windows' and not has_curses_h - message('Trying with /usr/include/ncursesw') - curses_compile_args += ['-I/usr/include/ncursesw'] - has_curses_h = cc.has_header('curses.h', args: curses_compile_args) - endif - if has_curses_h - curses_libname_list = (targetos == 'windows' ? ['pdcurses'] : ['ncursesw', 'cursesw']) - foreach curses_libname : curses_libname_list - libcurses = cc.find_library(curses_libname, - required: false, - kwargs: static_kwargs) - if libcurses.found() - if cc.links(curses_test, args: curses_compile_args, dependencies: libcurses) - curses = declare_dependency(compile_args: curses_compile_args, - dependencies: [libcurses]) - break - else - msg = 'curses library not usable' - endif - endif - endforeach - endif - endif - if get_option('iconv').allowed() - foreach link_args : [ ['-liconv'], [] ] - # Programs will be linked with glib and this will bring in libiconv on FreeBSD. - # We need to use libiconv if available because mixing libiconv's headers with - # the system libc does not work. - # However, without adding glib to the dependencies -L/usr/local/lib will not be - # included in the command line and libiconv will not be found. - if cc.links(''' - #include - int main(void) { - iconv_t conv = iconv_open("WCHAR_T", "UCS-2"); - return conv != (iconv_t) -1; - }''', args: config_host['GLIB_CFLAGS'].split() + config_host['GLIB_LIBS'].split() + link_args) - iconv = declare_dependency(link_args: link_args, dependencies: glib) - break - endif - endforeach - endif - if curses.found() and not iconv.found() - if get_option('iconv').enabled() - error('iconv not available') - endif - msg = 'iconv required for curses UI but not available' - curses = not_found - endif - if not curses.found() and msg != '' - if get_option('curses').enabled() - error(msg) - else - warning(msg + ', disabling') - endif - endif -endif - -brlapi = not_found -if not get_option('brlapi').auto() or have_system - brlapi = cc.find_library('brlapi', has_headers: ['brlapi.h'], - required: get_option('brlapi'), - kwargs: static_kwargs) - if brlapi.found() and not cc.links(''' - #include - #include - int main(void) { return brlapi__openConnection (NULL, NULL, NULL); }''', dependencies: brlapi) - brlapi = not_found - if get_option('brlapi').enabled() - error('could not link brlapi') - else - warning('could not link brlapi, disabling') - endif - endif -endif - -sdl = not_found -if not get_option('sdl').auto() or (have_system and not cocoa.found()) - sdl = dependency('sdl2', required: get_option('sdl'), kwargs: static_kwargs) - sdl_image = not_found -endif -if sdl.found() - # work around 2.0.8 bug - sdl = declare_dependency(compile_args: '-Wno-undef', - dependencies: sdl) - sdl_image = dependency('SDL2_image', required: get_option('sdl_image'), - method: 'pkg-config', kwargs: static_kwargs) -else - if get_option('sdl_image').enabled() - error('sdl-image required, but SDL was @0@'.format( - get_option('sdl').disabled() ? 'disabled' : 'not found')) - endif - sdl_image = not_found -endif - -rbd = not_found -if not get_option('rbd').auto() or have_block - librados = cc.find_library('rados', required: get_option('rbd'), - kwargs: static_kwargs) - librbd = cc.find_library('rbd', has_headers: ['rbd/librbd.h'], - required: get_option('rbd'), - kwargs: static_kwargs) - if librados.found() and librbd.found() - if cc.links(''' - #include - #include - int main(void) { - rados_t cluster; - rados_create(&cluster, NULL); - #if LIBRBD_VERSION_CODE < LIBRBD_VERSION(1, 12, 0) - #error - #endif - return 0; - }''', dependencies: [librbd, librados]) - rbd = declare_dependency(dependencies: [librbd, librados]) - elif get_option('rbd').enabled() - error('librbd >= 1.12.0 required') - else - warning('librbd >= 1.12.0 not found, disabling') - endif - endif -endif - -glusterfs = not_found -glusterfs_ftruncate_has_stat = false -glusterfs_iocb_has_stat = false -if not get_option('glusterfs').auto() or have_block - glusterfs = dependency('glusterfs-api', version: '>=3', - required: get_option('glusterfs'), - method: 'pkg-config', kwargs: static_kwargs) - if glusterfs.found() - glusterfs_ftruncate_has_stat = cc.links(''' - #include - - int - main(void) - { - /* new glfs_ftruncate() passes two additional args */ - return glfs_ftruncate(NULL, 0, NULL, NULL); - } - ''', dependencies: glusterfs) - glusterfs_iocb_has_stat = cc.links(''' - #include - - /* new glfs_io_cbk() passes two additional glfs_stat structs */ - static void - glusterfs_iocb(glfs_fd_t *fd, ssize_t ret, struct glfs_stat *prestat, struct glfs_stat *poststat, void *data) - {} - - int - main(void) - { - glfs_io_cbk iocb = &glusterfs_iocb; - iocb(NULL, 0 , NULL, NULL, NULL); - return 0; - } - ''', dependencies: glusterfs) - endif -endif - -libssh = not_found -if not get_option('libssh').auto() or have_block - libssh = dependency('libssh', version: '>=0.8.7', - method: 'pkg-config', - required: get_option('libssh'), - kwargs: static_kwargs) -endif - -libbzip2 = not_found -if not get_option('bzip2').auto() or have_block - libbzip2 = cc.find_library('bz2', has_headers: ['bzlib.h'], - required: get_option('bzip2'), - kwargs: static_kwargs) - if libbzip2.found() and not cc.links(''' - #include - int main(void) { BZ2_bzlibVersion(); return 0; }''', dependencies: libbzip2) - libbzip2 = not_found - if get_option('bzip2').enabled() - error('could not link libbzip2') - else - warning('could not link libbzip2, disabling') - endif - endif -endif - -liblzfse = not_found -if not get_option('lzfse').auto() or have_block - liblzfse = cc.find_library('lzfse', has_headers: ['lzfse.h'], - required: get_option('lzfse'), - kwargs: static_kwargs) -endif -if liblzfse.found() and not cc.links(''' - #include - int main(void) { lzfse_decode_scratch_size(); return 0; }''', dependencies: liblzfse) - liblzfse = not_found - if get_option('lzfse').enabled() - error('could not link liblzfse') - else - warning('could not link liblzfse, disabling') - endif -endif - -oss = not_found -if get_option('oss').allowed() and have_system - if not cc.has_header('sys/soundcard.h') - # not found - elif targetos == 'netbsd' - oss = cc.find_library('ossaudio', required: get_option('oss'), - kwargs: static_kwargs) - else - oss = declare_dependency() - endif - - if not oss.found() - if get_option('oss').enabled() - error('OSS not found') - endif - endif -endif -dsound = not_found -if not get_option('dsound').auto() or (targetos == 'windows' and have_system) - if cc.has_header('dsound.h') - dsound = declare_dependency(link_args: ['-lole32', '-ldxguid']) - endif - - if not dsound.found() - if get_option('dsound').enabled() - error('DirectSound not found') - endif - endif -endif - -coreaudio = not_found -if not get_option('coreaudio').auto() or (targetos == 'darwin' and have_system) - coreaudio = dependency('appleframeworks', modules: 'CoreAudio', - required: get_option('coreaudio')) -endif - -opengl = not_found -if not get_option('opengl').auto() or have_system or have_vhost_user_gpu - epoxy = dependency('epoxy', method: 'pkg-config', - required: get_option('opengl'), kwargs: static_kwargs) - if cc.has_header('epoxy/egl.h', dependencies: epoxy) - opengl = epoxy - elif get_option('opengl').enabled() - error('epoxy/egl.h not found') - endif -endif -gbm = not_found -if (have_system or have_tools) and (virgl.found() or opengl.found()) - gbm = dependency('gbm', method: 'pkg-config', required: false, - kwargs: static_kwargs) -endif -have_vhost_user_gpu = have_vhost_user_gpu and virgl.found() and gbm.found() - -gnutls = not_found -gnutls_crypto = not_found -if get_option('gnutls').enabled() or (get_option('gnutls').auto() and have_system) - # For general TLS support our min gnutls matches - # that implied by our platform support matrix - # - # For the crypto backends, we look for a newer - # gnutls: - # - # Version 3.6.8 is needed to get XTS - # Version 3.6.13 is needed to get PBKDF - # Version 3.6.14 is needed to get HW accelerated XTS - # - # If newer enough gnutls isn't available, we can - # still use a different crypto backend to satisfy - # the platform support requirements - gnutls_crypto = dependency('gnutls', version: '>=3.6.14', - method: 'pkg-config', - required: false, - kwargs: static_kwargs) - if gnutls_crypto.found() - gnutls = gnutls_crypto - else - # Our min version if all we need is TLS - gnutls = dependency('gnutls', version: '>=3.5.18', - method: 'pkg-config', - required: get_option('gnutls'), - kwargs: static_kwargs) - endif -endif - -# We prefer use of gnutls for crypto, unless the options -# explicitly asked for nettle or gcrypt. -# -# If gnutls isn't available for crypto, then we'll prefer -# gcrypt over nettle for performance reasons. -gcrypt = not_found -nettle = not_found -hogweed = not_found -xts = 'none' - -if get_option('nettle').enabled() and get_option('gcrypt').enabled() - error('Only one of gcrypt & nettle can be enabled') -endif - -# Explicit nettle/gcrypt request, so ignore gnutls for crypto -if get_option('nettle').enabled() or get_option('gcrypt').enabled() - gnutls_crypto = not_found -endif - -if not gnutls_crypto.found() - if (not get_option('gcrypt').auto() or have_system) and not get_option('nettle').enabled() - gcrypt = dependency('libgcrypt', version: '>=1.8', - method: 'config-tool', - required: get_option('gcrypt'), - kwargs: static_kwargs) - # Debian has removed -lgpg-error from libgcrypt-config - # as it "spreads unnecessary dependencies" which in - # turn breaks static builds... - if gcrypt.found() and enable_static - gcrypt = declare_dependency(dependencies: [ - gcrypt, - cc.find_library('gpg-error', required: true, kwargs: static_kwargs)]) - endif - endif - if (not get_option('nettle').auto() or have_system) and not gcrypt.found() - nettle = dependency('nettle', version: '>=3.4', - method: 'pkg-config', - required: get_option('nettle'), - kwargs: static_kwargs) - if nettle.found() and not cc.has_header('nettle/xts.h', dependencies: nettle) - xts = 'private' - endif - endif -endif - -gmp = dependency('gmp', required: false, method: 'pkg-config', kwargs: static_kwargs) -if nettle.found() and gmp.found() - hogweed = dependency('hogweed', version: '>=3.4', - method: 'pkg-config', - required: get_option('nettle'), - kwargs: static_kwargs) -endif - - -gtk = not_found -gtkx11 = not_found -vte = not_found -if not get_option('gtk').auto() or (have_system and not cocoa.found()) - gtk = dependency('gtk+-3.0', version: '>=3.22.0', - method: 'pkg-config', - required: get_option('gtk'), - kwargs: static_kwargs) - if gtk.found() - gtkx11 = dependency('gtk+-x11-3.0', version: '>=3.22.0', - method: 'pkg-config', - required: false, - kwargs: static_kwargs) - gtk = declare_dependency(dependencies: [gtk, gtkx11]) - - if not get_option('vte').auto() or have_system - vte = dependency('vte-2.91', - method: 'pkg-config', - required: get_option('vte'), - kwargs: static_kwargs) - endif - endif -endif - -x11 = not_found -if gtkx11.found() - x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found(), - kwargs: static_kwargs) -endif -png = not_found -if get_option('png').allowed() and have_system - png = dependency('libpng', required: get_option('png'), - method: 'pkg-config', kwargs: static_kwargs) -endif -vnc = not_found -jpeg = not_found -sasl = not_found -if get_option('vnc').allowed() and have_system - vnc = declare_dependency() # dummy dependency - jpeg = dependency('libjpeg', required: get_option('vnc_jpeg'), - method: 'pkg-config', kwargs: static_kwargs) - sasl = cc.find_library('sasl2', has_headers: ['sasl/sasl.h'], - required: get_option('vnc_sasl'), - kwargs: static_kwargs) - if sasl.found() - sasl = declare_dependency(dependencies: sasl, - compile_args: '-DSTRUCT_IOVEC_DEFINED') - endif -endif - -pam = not_found -if not get_option('auth_pam').auto() or have_system - pam = cc.find_library('pam', has_headers: ['security/pam_appl.h'], - required: get_option('auth_pam'), - kwargs: static_kwargs) -endif -if pam.found() and not cc.links(''' - #include - #include - int main(void) { - const char *service_name = "qemu"; - const char *user = "frank"; - const struct pam_conv pam_conv = { 0 }; - pam_handle_t *pamh = NULL; - pam_start(service_name, user, &pam_conv, &pamh); - return 0; - }''', dependencies: pam) - pam = not_found - if get_option('auth_pam').enabled() - error('could not link libpam') - else - warning('could not link libpam, disabling') - endif -endif - -snappy = not_found -if not get_option('snappy').auto() or have_system - snappy = cc.find_library('snappy', has_headers: ['snappy-c.h'], - required: get_option('snappy'), - kwargs: static_kwargs) -endif -if snappy.found() and not linker.links(''' - #include - int main(void) { snappy_max_compressed_length(4096); return 0; }''', dependencies: snappy) - snappy = not_found - if get_option('snappy').enabled() - error('could not link libsnappy') - else - warning('could not link libsnappy, disabling') - endif -endif - -lzo = not_found -if not get_option('lzo').auto() or have_system - lzo = cc.find_library('lzo2', has_headers: ['lzo/lzo1x.h'], - required: get_option('lzo'), - kwargs: static_kwargs) -endif -if lzo.found() and not cc.links(''' - #include - int main(void) { lzo_version(); return 0; }''', dependencies: lzo) - lzo = not_found - if get_option('lzo').enabled() - error('could not link liblzo2') - else - warning('could not link liblzo2, disabling') - endif -endif - -numa = not_found -if not get_option('numa').auto() or have_system or have_tools - numa = cc.find_library('numa', has_headers: ['numa.h'], - required: get_option('numa'), - kwargs: static_kwargs) -endif -if numa.found() and not cc.links(''' - #include - int main(void) { return numa_available(); } - ''', dependencies: numa) - numa = not_found - if get_option('numa').enabled() - error('could not link numa') - else - warning('could not link numa, disabling') - endif -endif - -rdma = not_found -if not get_option('rdma').auto() or have_system - libumad = cc.find_library('ibumad', required: get_option('rdma')) - rdma_libs = [cc.find_library('rdmacm', has_headers: ['rdma/rdma_cma.h'], - required: get_option('rdma'), - kwargs: static_kwargs), - cc.find_library('ibverbs', required: get_option('rdma'), - kwargs: static_kwargs), - libumad] - rdma = declare_dependency(dependencies: rdma_libs) - foreach lib: rdma_libs - if not lib.found() - rdma = not_found - endif - endforeach -endif - xen = not_found if get_option('xen').enabled() or (get_option('xen').auto() and have_system) xencontrol = dependency('xencontrol', required: false, - method: 'pkg-config', kwargs: static_kwargs) + method: 'pkg-config') if xencontrol.found() xen_pc = declare_dependency(version: xencontrol.version(), dependencies: [ xencontrol, # disabler: true makes xen_pc.found() return false if any is not found dependency('xenstore', required: false, - method: 'pkg-config', kwargs: static_kwargs, + method: 'pkg-config', disabler: true), dependency('xenforeignmemory', required: false, - method: 'pkg-config', kwargs: static_kwargs, + method: 'pkg-config', disabler: true), dependency('xengnttab', required: false, - method: 'pkg-config', kwargs: static_kwargs, + method: 'pkg-config', disabler: true), dependency('xenevtchn', required: false, - method: 'pkg-config', kwargs: static_kwargs, + method: 'pkg-config', disabler: true), dependency('xendevicemodel', required: false, - method: 'pkg-config', kwargs: static_kwargs, + method: 'pkg-config', disabler: true), # optional, no "disabler: true" dependency('xentoolcore', required: false, - method: 'pkg-config', kwargs: static_kwargs)]) + method: 'pkg-config')]) if xen_pc.found() xen = xen_pc endif endif if not xen.found() - xen_tests = [ '4.11.0', '4.10.0', '4.9.0', '4.8.0', '4.7.1', '4.6.0', '4.5.0', '4.2.0' ] + xen_tests = [ '4.11.0', '4.10.0', '4.9.0', '4.8.0', '4.7.1' ] xen_libs = { '4.11.0': [ 'xenstore', 'xenctrl', 'xendevicemodel', 'xenforeignmemory', 'xengnttab', 'xenevtchn', 'xentoolcore' ], '4.10.0': [ 'xenstore', 'xenctrl', 'xendevicemodel', 'xenforeignmemory', 'xengnttab', 'xenevtchn', 'xentoolcore' ], '4.9.0': [ 'xenstore', 'xenctrl', 'xendevicemodel', 'xenforeignmemory', 'xengnttab', 'xenevtchn' ], '4.8.0': [ 'xenstore', 'xenctrl', 'xenforeignmemory', 'xengnttab', 'xenevtchn' ], '4.7.1': [ 'xenstore', 'xenctrl', 'xenforeignmemory', 'xengnttab', 'xenevtchn' ], - '4.6.0': [ 'xenstore', 'xenctrl' ], - '4.5.0': [ 'xenstore', 'xenctrl' ], - '4.2.0': [ 'xenstore', 'xenctrl' ], } xen_deps = {} foreach ver: xen_tests @@ -1391,62 +857,1075 @@ endif have_xen_pci_passthrough = get_option('xen_pci_passthrough') \ .require(xen.found(), error_message: 'Xen PCI passthrough requested but Xen not enabled') \ - .require(targetos == 'linux', + .require(host_os == 'linux', + error_message: 'Xen PCI passthrough not available on this platform') \ + .require(cpu == 'x86' or cpu == 'x86_64', error_message: 'Xen PCI passthrough not available on this platform') \ .allowed() +################ +# Dependencies # +################ + +# When bumping glib minimum version, please check also whether to increase +# the _WIN32_WINNT setting in osdep.h according to the value from glib +glib_req_ver = '>=2.56.0' +glib_pc = dependency('glib-2.0', version: glib_req_ver, required: true, + method: 'pkg-config') +glib_cflags = [] +if enable_modules + gmodule = dependency('gmodule-export-2.0', version: glib_req_ver, required: true, + method: 'pkg-config') +elif get_option('plugins') + gmodule = dependency('gmodule-no-export-2.0', version: glib_req_ver, required: true, + method: 'pkg-config') +else + gmodule = not_found +endif + +# This workaround is required due to a bug in pkg-config file for glib as it +# doesn't define GLIB_STATIC_COMPILATION for pkg-config --static +if host_os == 'windows' and get_option('prefer_static') + glib_cflags += ['-DGLIB_STATIC_COMPILATION'] +endif + +# 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 +# 32-bit QEMU while pointing at 64-bit glib headers + +if not cc.compiles(''' + #include + #include + + #define QEMU_BUILD_BUG_ON(x) \ + typedef char qemu_build_bug_on[(x)?-1:1] __attribute__((unused)); + + int main(void) { + QEMU_BUILD_BUG_ON(sizeof(size_t) != GLIB_SIZEOF_SIZE_T); + return 0; + }''', dependencies: glib_pc, args: glib_cflags) + error('''sizeof(size_t) doesn't match GLIB_SIZEOF_SIZE_T. + You probably need to set PKG_CONFIG_LIBDIR" to point + to the right pkg-config files for your build target.''') +endif + +# Silence clang warnings triggered by glib < 2.57.2 +if not cc.compiles(''' + #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; }''', dependencies: glib_pc, args: ['-Wunused-function', '-Werror']) + glib_cflags += cc.get_supported_arguments('-Wno-unused-function') +endif +glib = declare_dependency(dependencies: [glib_pc, gmodule], + compile_args: glib_cflags, + version: glib_pc.version()) + +# Check whether glib has gslice, which we have to avoid for correctness. +# TODO: remove this check and the corresponding workaround (qtree) when +# the minimum supported glib is >= 2.75.3 +glib_has_gslice = glib.version().version_compare('<2.75.3') + +# override glib dep to include the above refinements +meson.override_dependency('glib-2.0', glib) + +# The path to glib.h is added to all compilation commands. +add_project_dependencies(glib.partial_dependency(compile_args: true, includes: true), + native: false, language: all_languages) + +gio = not_found +gdbus_codegen = not_found +gdbus_codegen_error = '@0@ requires gdbus-codegen, please install libgio' +if not get_option('gio').auto() or have_system + gio = dependency('gio-2.0', required: get_option('gio'), + method: 'pkg-config') + if gio.found() and not cc.links(''' + #include + int main(void) + { + g_dbus_proxy_new_sync(0, 0, 0, 0, 0, 0, 0, 0); + return 0; + }''', dependencies: [glib, gio]) + if get_option('gio').enabled() + error('The installed libgio is broken for static linking') + endif + gio = not_found + endif + if gio.found() + gdbus_codegen = find_program(gio.get_variable('gdbus_codegen'), + required: get_option('gio')) + gio_unix = dependency('gio-unix-2.0', required: get_option('gio'), + method: 'pkg-config') + gio = declare_dependency(dependencies: [gio, gio_unix], + version: gio.version()) + endif +endif +if gdbus_codegen.found() and get_option('cfi') + gdbus_codegen = not_found + gdbus_codegen_error = '@0@ uses gdbus-codegen, which does not support control flow integrity' +endif + +xml_pp = find_program('scripts/xml-preprocess.py') + +lttng = not_found +if 'ust' in get_option('trace_backends') + lttng = dependency('lttng-ust', required: true, version: '>= 2.1', + method: 'pkg-config') +endif +pixman = not_found +if not get_option('pixman').auto() or have_system or have_tools + pixman = dependency('pixman-1', required: get_option('pixman'), version:'>=0.21.8', + method: 'pkg-config') +endif + +zlib = dependency('zlib', required: true) + +libaio = not_found +if not get_option('linux_aio').auto() or have_block + libaio = cc.find_library('aio', has_headers: ['libaio.h'], + required: get_option('linux_aio')) +endif + +linux_io_uring_test = ''' + #include + #include + + int main(void) { return 0; }''' + +linux_io_uring = not_found +if not get_option('linux_io_uring').auto() or have_block + linux_io_uring = dependency('liburing', version: '>=0.3', + required: get_option('linux_io_uring'), + method: 'pkg-config') + if not cc.links(linux_io_uring_test) + linux_io_uring = not_found + endif +endif + +libnfs = not_found +if not get_option('libnfs').auto() or have_block + libnfs = dependency('libnfs', version: '>=1.9.3', + required: get_option('libnfs'), + method: 'pkg-config') +endif + +libattr_test = ''' + #include + #include + #ifdef CONFIG_LIBATTR + #include + #else + #include + #endif + int main(void) { getxattr(NULL, NULL, NULL, 0); setxattr(NULL, NULL, NULL, 0, 0); return 0; }''' + +libattr = not_found +have_old_libattr = false +if get_option('attr').allowed() + if cc.links(libattr_test) + libattr = declare_dependency() + else + libattr = cc.find_library('attr', has_headers: ['attr/xattr.h'], + required: get_option('attr')) + if libattr.found() and not \ + cc.links(libattr_test, dependencies: libattr, args: '-DCONFIG_LIBATTR') + libattr = not_found + if get_option('attr').enabled() + error('could not link libattr') + else + warning('could not link libattr, disabling') + endif + else + have_old_libattr = libattr.found() + endif + endif +endif + +cocoa = dependency('appleframeworks', modules: ['Cocoa', 'CoreVideo'], + required: get_option('cocoa')) + +vmnet = dependency('appleframeworks', modules: 'vmnet', required: get_option('vmnet')) +if vmnet.found() and not cc.has_header_symbol('vmnet/vmnet.h', + 'VMNET_BRIDGED_MODE', + dependencies: vmnet) + vmnet = not_found + if get_option('vmnet').enabled() + error('vmnet.framework API is outdated') + else + warning('vmnet.framework API is outdated, disabling') + endif +endif + +seccomp = not_found +seccomp_has_sysrawrc = false +if not get_option('seccomp').auto() or have_system or have_tools + seccomp = dependency('libseccomp', version: '>=2.3.0', + required: get_option('seccomp'), + method: 'pkg-config') + if seccomp.found() + seccomp_has_sysrawrc = cc.has_header_symbol('seccomp.h', + 'SCMP_FLTATR_API_SYSRAWRC', + dependencies: seccomp) + endif +endif + +libcap_ng = not_found +if not get_option('cap_ng').auto() or have_system or have_tools + libcap_ng = cc.find_library('cap-ng', has_headers: ['cap-ng.h'], + required: get_option('cap_ng')) +endif +if libcap_ng.found() and not cc.links(''' + #include + int main(void) + { + capng_capability_to_name(CAPNG_EFFECTIVE); + return 0; + }''', dependencies: libcap_ng) + libcap_ng = not_found + if get_option('cap_ng').enabled() + error('could not link libcap-ng') + else + warning('could not link libcap-ng, disabling') + endif +endif + +if get_option('xkbcommon').auto() and not have_system and not have_tools + xkbcommon = not_found +else + xkbcommon = dependency('xkbcommon', required: get_option('xkbcommon'), + method: 'pkg-config') +endif + +slirp = not_found +if not get_option('slirp').auto() or have_system + slirp = dependency('slirp', required: get_option('slirp'), + method: 'pkg-config') + # slirp < 4.7 is incompatible with CFI support in QEMU. This is because + # it passes function pointers within libslirp as callbacks for timers. + # When using a system-wide shared libslirp, the type information for the + # callback is missing and the timer call produces a false positive with CFI. + # Do not use the "version" keyword argument to produce a better error. + # with control-flow integrity. + if get_option('cfi') and slirp.found() and slirp.version().version_compare('<4.7') + if get_option('slirp').enabled() + error('Control-Flow Integrity requires libslirp 4.7.') + else + warning('Cannot use libslirp since Control-Flow Integrity requires libslirp >= 4.7.') + slirp = not_found + endif + endif +endif + +vde = not_found +if not get_option('vde').auto() or have_system or have_tools + vde = cc.find_library('vdeplug', has_headers: ['libvdeplug.h'], + required: get_option('vde')) +endif +if vde.found() and not cc.links(''' + #include + int main(void) + { + struct vde_open_args a = {0, 0, 0}; + char s[] = ""; + vde_open(s, s, &a); + return 0; + }''', dependencies: vde) + vde = not_found + if get_option('cap_ng').enabled() + error('could not link libvdeplug') + else + warning('could not link libvdeplug, disabling') + endif +endif + +pulse = not_found +if not get_option('pa').auto() or (host_os == 'linux' and have_system) + pulse = dependency('libpulse', required: get_option('pa'), + method: 'pkg-config') +endif +alsa = not_found +if not get_option('alsa').auto() or (host_os == 'linux' and have_system) + alsa = dependency('alsa', required: get_option('alsa'), + method: 'pkg-config') +endif +jack = not_found +if not get_option('jack').auto() or have_system + jack = dependency('jack', required: get_option('jack'), + method: 'pkg-config') +endif +pipewire = not_found +if not get_option('pipewire').auto() or (host_os == 'linux' and have_system) + pipewire = dependency('libpipewire-0.3', version: '>=0.3.60', + required: get_option('pipewire'), + method: 'pkg-config') +endif +sndio = not_found +if not get_option('sndio').auto() or have_system + sndio = dependency('sndio', required: get_option('sndio'), + method: 'pkg-config') +endif + +spice_protocol = not_found +if not get_option('spice_protocol').auto() or have_system + spice_protocol = dependency('spice-protocol', version: '>=0.14.0', + required: get_option('spice_protocol'), + method: 'pkg-config') +endif +spice = not_found +if get_option('spice') \ + .disable_auto_if(not have_system) \ + .require(pixman.found(), + error_message: 'cannot enable SPICE if pixman is not available') \ + .allowed() + spice = dependency('spice-server', version: '>=0.14.0', + required: get_option('spice'), + method: 'pkg-config') +endif +spice_headers = spice.partial_dependency(compile_args: true, includes: true) + +rt = cc.find_library('rt', required: false) + +libiscsi = not_found +if not get_option('libiscsi').auto() or have_block + libiscsi = dependency('libiscsi', version: '>=1.9.0', + required: get_option('libiscsi'), + method: 'pkg-config') +endif +zstd = not_found +if not get_option('zstd').auto() or have_block + zstd = dependency('libzstd', version: '>=1.4.0', + required: get_option('zstd'), + method: 'pkg-config') +endif +virgl = not_found + +have_vhost_user_gpu = have_tools and host_os == 'linux' and pixman.found() +if not get_option('virglrenderer').auto() or have_system or have_vhost_user_gpu + virgl = dependency('virglrenderer', + method: 'pkg-config', + required: get_option('virglrenderer')) +endif +rutabaga = not_found +if not get_option('rutabaga_gfx').auto() or have_system or have_vhost_user_gpu + rutabaga = dependency('rutabaga_gfx_ffi', + method: 'pkg-config', + required: get_option('rutabaga_gfx')) +endif +blkio = not_found +if not get_option('blkio').auto() or have_block + blkio = dependency('blkio', + method: 'pkg-config', + required: get_option('blkio')) +endif +curl = not_found +if not get_option('curl').auto() or have_block + curl = dependency('libcurl', version: '>=7.29.0', + method: 'pkg-config', + required: get_option('curl')) +endif +libudev = not_found +if host_os == 'linux' and (have_system or have_tools) + libudev = dependency('libudev', + method: 'pkg-config', + required: get_option('libudev')) +endif + +mpathlibs = [libudev] +mpathpersist = not_found +if host_os == 'linux' and have_tools and get_option('mpath').allowed() + mpath_test_source = ''' + #include + #include + unsigned mpath_mx_alloc_len = 1024; + int logsink; + static struct config *multipath_conf; + extern struct udev *udev; + extern struct config *get_multipath_config(void); + extern void put_multipath_config(struct config *conf); + struct udev *udev; + struct config *get_multipath_config(void) { return multipath_conf; } + void put_multipath_config(struct config *conf) { } + int main(void) { + udev = udev_new(); + multipath_conf = mpath_lib_init(); + return 0; + }''' + libmpathpersist = cc.find_library('mpathpersist', + required: get_option('mpath')) + if libmpathpersist.found() + mpathlibs += libmpathpersist + if get_option('prefer_static') + mpathlibs += cc.find_library('devmapper', + required: get_option('mpath')) + endif + mpathlibs += cc.find_library('multipath', + required: get_option('mpath')) + foreach lib: mpathlibs + if not lib.found() + mpathlibs = [] + break + endif + endforeach + if mpathlibs.length() == 0 + msg = 'Dependencies missing for libmpathpersist' + elif cc.links(mpath_test_source, dependencies: mpathlibs) + mpathpersist = declare_dependency(dependencies: mpathlibs) + else + msg = 'Cannot detect libmpathpersist API' + endif + if not mpathpersist.found() + if get_option('mpath').enabled() + error(msg) + else + warning(msg + ', disabling') + endif + endif + endif +endif + +iconv = not_found +curses = not_found +if have_system and get_option('curses').allowed() + curses_test = ''' + #if defined(__APPLE__) || defined(__OpenBSD__) + #define _XOPEN_SOURCE_EXTENDED 1 + #endif + #include + #include + #include + int main(void) { + wchar_t wch = L'w'; + setlocale(LC_ALL, ""); + resize_term(0, 0); + addwstr(L"wide chars\n"); + addnwstr(&wch, 1); + add_wch(WACS_DEGREE); + return 0; + }''' + + curses_dep_list = host_os == 'windows' ? ['ncurses', 'ncursesw'] : ['ncursesw'] + curses = dependency(curses_dep_list, + required: false, + method: 'pkg-config') + msg = get_option('curses').enabled() ? 'curses library not found' : '' + curses_compile_args = ['-DNCURSES_WIDECHAR=1'] + if curses.found() + if cc.links(curses_test, args: curses_compile_args, dependencies: [curses]) + curses = declare_dependency(compile_args: curses_compile_args, dependencies: [curses], + version: curses.version()) + else + msg = 'curses package not usable' + curses = not_found + endif + endif + if not curses.found() + has_curses_h = cc.has_header('curses.h', args: curses_compile_args) + if host_os != 'windows' and not has_curses_h + message('Trying with /usr/include/ncursesw') + curses_compile_args += ['-I/usr/include/ncursesw'] + has_curses_h = cc.has_header('curses.h', args: curses_compile_args) + endif + if has_curses_h + curses_libname_list = (host_os == 'windows' ? ['pdcurses'] : ['ncursesw', 'cursesw']) + foreach curses_libname : curses_libname_list + libcurses = cc.find_library(curses_libname, + required: false) + if libcurses.found() + if cc.links(curses_test, args: curses_compile_args, dependencies: libcurses) + curses = declare_dependency(compile_args: curses_compile_args, + dependencies: [libcurses]) + break + else + msg = 'curses library not usable' + endif + endif + endforeach + endif + endif + if get_option('iconv').allowed() + foreach link_args : [ ['-liconv'], [] ] + # Programs will be linked with glib and this will bring in libiconv on FreeBSD. + # We need to use libiconv if available because mixing libiconv's headers with + # the system libc does not work. + # However, without adding glib to the dependencies -L/usr/local/lib will not be + # included in the command line and libiconv will not be found. + if cc.links(''' + #include + int main(void) { + iconv_t conv = iconv_open("WCHAR_T", "UCS-2"); + return conv != (iconv_t) -1; + }''', args: link_args, dependencies: glib) + iconv = declare_dependency(link_args: link_args, dependencies: glib) + break + endif + endforeach + endif + if curses.found() and not iconv.found() + if get_option('iconv').enabled() + error('iconv not available') + endif + msg = 'iconv required for curses UI but not available' + curses = not_found + endif + if not curses.found() and msg != '' + if get_option('curses').enabled() + error(msg) + else + warning(msg + ', disabling') + endif + endif +endif + +brlapi = not_found +if not get_option('brlapi').auto() or have_system + brlapi = cc.find_library('brlapi', has_headers: ['brlapi.h'], + required: get_option('brlapi')) + if brlapi.found() and not cc.links(''' + #include + #include + int main(void) { return brlapi__openConnection (NULL, NULL, NULL); }''', dependencies: brlapi) + brlapi = not_found + if get_option('brlapi').enabled() + error('could not link brlapi') + else + warning('could not link brlapi, disabling') + endif + endif +endif + +sdl = not_found +if not get_option('sdl').auto() or have_system + sdl = dependency('sdl2', required: get_option('sdl')) + sdl_image = not_found +endif +if sdl.found() + # Some versions of SDL have problems with -Wundef + if not cc.compiles(''' + #include + #include + int main(int argc, char *argv[]) { return 0; } + ''', dependencies: sdl, args: '-Werror=undef') + sdl = declare_dependency(compile_args: '-Wno-undef', + dependencies: sdl, + version: sdl.version()) + endif + sdl_image = dependency('SDL2_image', required: get_option('sdl_image'), + method: 'pkg-config') +else + if get_option('sdl_image').enabled() + error('sdl-image required, but SDL was @0@'.format( + get_option('sdl').disabled() ? 'disabled' : 'not found')) + endif + sdl_image = not_found +endif + +rbd = not_found +if not get_option('rbd').auto() or have_block + librados = cc.find_library('rados', required: get_option('rbd')) + librbd = cc.find_library('rbd', has_headers: ['rbd/librbd.h'], + required: get_option('rbd')) + if librados.found() and librbd.found() + if cc.links(''' + #include + #include + int main(void) { + rados_t cluster; + rados_create(&cluster, NULL); + #if LIBRBD_VERSION_CODE < LIBRBD_VERSION(1, 12, 0) + #error + #endif + return 0; + }''', dependencies: [librbd, librados]) + rbd = declare_dependency(dependencies: [librbd, librados]) + elif get_option('rbd').enabled() + error('librbd >= 1.12.0 required') + else + warning('librbd >= 1.12.0 not found, disabling') + endif + endif +endif + +glusterfs = not_found +glusterfs_ftruncate_has_stat = false +glusterfs_iocb_has_stat = false +if not get_option('glusterfs').auto() or have_block + glusterfs = dependency('glusterfs-api', version: '>=3', + required: get_option('glusterfs'), + method: 'pkg-config') + if glusterfs.found() + glusterfs_ftruncate_has_stat = cc.links(''' + #include + + int + main(void) + { + /* new glfs_ftruncate() passes two additional args */ + return glfs_ftruncate(NULL, 0, NULL, NULL); + } + ''', dependencies: glusterfs) + glusterfs_iocb_has_stat = cc.links(''' + #include + + /* new glfs_io_cbk() passes two additional glfs_stat structs */ + static void + glusterfs_iocb(glfs_fd_t *fd, ssize_t ret, struct glfs_stat *prestat, struct glfs_stat *poststat, void *data) + {} + + int + main(void) + { + glfs_io_cbk iocb = &glusterfs_iocb; + iocb(NULL, 0 , NULL, NULL, NULL); + return 0; + } + ''', dependencies: glusterfs) + endif +endif + +hv_balloon = false +if get_option('hv_balloon').allowed() and have_system + if cc.links(''' + #include + #include + int main(void) { + GTree *tree; + + tree = g_tree_new((GCompareFunc)strcmp); + (void)g_tree_node_first(tree); + g_tree_destroy(tree); + return 0; + } + ''', dependencies: glib) + hv_balloon = true + else + if get_option('hv_balloon').enabled() + error('could not enable hv-balloon, update your glib') + else + warning('could not find glib support for hv-balloon, disabling') + endif + endif +endif + +libssh = not_found +if not get_option('libssh').auto() or have_block + libssh = dependency('libssh', version: '>=0.8.7', + method: 'pkg-config', + required: get_option('libssh')) +endif + +libbzip2 = not_found +if not get_option('bzip2').auto() or have_block + libbzip2 = cc.find_library('bz2', has_headers: ['bzlib.h'], + required: get_option('bzip2')) + if libbzip2.found() and not cc.links(''' + #include + int main(void) { BZ2_bzlibVersion(); return 0; }''', dependencies: libbzip2) + libbzip2 = not_found + if get_option('bzip2').enabled() + error('could not link libbzip2') + else + warning('could not link libbzip2, disabling') + endif + endif +endif + +liblzfse = not_found +if not get_option('lzfse').auto() or have_block + liblzfse = cc.find_library('lzfse', has_headers: ['lzfse.h'], + required: get_option('lzfse')) +endif +if liblzfse.found() and not cc.links(''' + #include + int main(void) { lzfse_decode_scratch_size(); return 0; }''', dependencies: liblzfse) + liblzfse = not_found + if get_option('lzfse').enabled() + error('could not link liblzfse') + else + warning('could not link liblzfse, disabling') + endif +endif + +oss = not_found +if get_option('oss').allowed() and have_system + if not cc.has_header('sys/soundcard.h') + # not found + elif host_os == 'netbsd' + oss = cc.find_library('ossaudio', required: get_option('oss')) + else + oss = declare_dependency() + endif + + if not oss.found() + if get_option('oss').enabled() + error('OSS not found') + endif + endif +endif +dsound = not_found +if not get_option('dsound').auto() or (host_os == 'windows' and have_system) + if cc.has_header('dsound.h') + dsound = declare_dependency(link_args: ['-lole32', '-ldxguid']) + endif + + if not dsound.found() + if get_option('dsound').enabled() + error('DirectSound not found') + endif + endif +endif + +coreaudio = not_found +if not get_option('coreaudio').auto() or (host_os == 'darwin' and have_system) + coreaudio = dependency('appleframeworks', modules: 'CoreAudio', + required: get_option('coreaudio')) +endif + +opengl = not_found +if not get_option('opengl').auto() or have_system or have_vhost_user_gpu + epoxy = dependency('epoxy', method: 'pkg-config', + required: get_option('opengl')) + if cc.has_header('epoxy/egl.h', dependencies: epoxy) + opengl = epoxy + elif get_option('opengl').enabled() + error('epoxy/egl.h not found') + endif +endif +gbm = not_found +if (have_system or have_tools) and (virgl.found() or opengl.found()) + gbm = dependency('gbm', method: 'pkg-config', required: false) +endif +have_vhost_user_gpu = have_vhost_user_gpu and virgl.found() and opengl.found() and gbm.found() + +gnutls = not_found +gnutls_crypto = not_found +if get_option('gnutls').enabled() or (get_option('gnutls').auto() and have_system) + # For general TLS support our min gnutls matches + # that implied by our platform support matrix + # + # For the crypto backends, we look for a newer + # gnutls: + # + # Version 3.6.8 is needed to get XTS + # Version 3.6.13 is needed to get PBKDF + # Version 3.6.14 is needed to get HW accelerated XTS + # + # If newer enough gnutls isn't available, we can + # still use a different crypto backend to satisfy + # the platform support requirements + gnutls_crypto = dependency('gnutls', version: '>=3.6.14', + method: 'pkg-config', + required: false) + if gnutls_crypto.found() + gnutls = gnutls_crypto + else + # Our min version if all we need is TLS + gnutls = dependency('gnutls', version: '>=3.5.18', + method: 'pkg-config', + required: get_option('gnutls')) + endif +endif + +# We prefer use of gnutls for crypto, unless the options +# explicitly asked for nettle or gcrypt. +# +# If gnutls isn't available for crypto, then we'll prefer +# gcrypt over nettle for performance reasons. +gcrypt = not_found +nettle = not_found +hogweed = not_found +crypto_sm4 = not_found +xts = 'none' + +if get_option('nettle').enabled() and get_option('gcrypt').enabled() + error('Only one of gcrypt & nettle can be enabled') +endif + +# Explicit nettle/gcrypt request, so ignore gnutls for crypto +if get_option('nettle').enabled() or get_option('gcrypt').enabled() + gnutls_crypto = not_found +endif + +if not gnutls_crypto.found() + if (not get_option('gcrypt').auto() or have_system) and not get_option('nettle').enabled() + gcrypt = dependency('libgcrypt', version: '>=1.8', + method: 'config-tool', + required: get_option('gcrypt')) + # Debian has removed -lgpg-error from libgcrypt-config + # as it "spreads unnecessary dependencies" which in + # turn breaks static builds... + if gcrypt.found() and get_option('prefer_static') + gcrypt = declare_dependency(dependencies: + [gcrypt, + cc.find_library('gpg-error', required: true)], + version: gcrypt.version()) + endif + crypto_sm4 = gcrypt + # SM4 ALG is available in libgcrypt >= 1.9 + if gcrypt.found() and not cc.links(''' + #include + int main(void) { + gcry_cipher_hd_t handler; + gcry_cipher_open(&handler, GCRY_CIPHER_SM4, GCRY_CIPHER_MODE_ECB, 0); + return 0; + }''', dependencies: gcrypt) + crypto_sm4 = not_found + endif + endif + if (not get_option('nettle').auto() or have_system) and not gcrypt.found() + nettle = dependency('nettle', version: '>=3.4', + method: 'pkg-config', + required: get_option('nettle')) + if nettle.found() and not cc.has_header('nettle/xts.h', dependencies: nettle) + xts = 'private' + endif + crypto_sm4 = nettle + # SM4 ALG is available in nettle >= 3.9 + if nettle.found() and not cc.links(''' + #include + int main(void) { + struct sm4_ctx ctx; + unsigned char key[16] = {0}; + sm4_set_encrypt_key(&ctx, key); + return 0; + }''', dependencies: nettle) + crypto_sm4 = not_found + endif + endif +endif + +capstone = not_found +if not get_option('capstone').auto() or have_system or have_user + capstone = dependency('capstone', version: '>=3.0.5', + method: 'pkg-config', + required: get_option('capstone')) + + # Some versions of capstone have broken pkg-config file + # that reports a wrong -I path, causing the #include to + # fail later. If the system has such a broken version + # do not use it. + if capstone.found() and not cc.compiles('#include ', + dependencies: [capstone]) + capstone = not_found + if get_option('capstone').enabled() + error('capstone requested, but it does not appear to work') + endif + endif +endif + +gmp = dependency('gmp', required: false, method: 'pkg-config') +if nettle.found() and gmp.found() + hogweed = dependency('hogweed', version: '>=3.4', + method: 'pkg-config', + required: get_option('nettle')) +endif + + +gtk = not_found +gtkx11 = not_found +vte = not_found +have_gtk_clipboard = get_option('gtk_clipboard').enabled() + +if get_option('gtk') \ + .disable_auto_if(not have_system) \ + .require(pixman.found(), + error_message: 'cannot enable GTK if pixman is not available') \ + .allowed() + gtk = dependency('gtk+-3.0', version: '>=3.22.0', + method: 'pkg-config', + required: get_option('gtk')) + if gtk.found() + gtkx11 = dependency('gtk+-x11-3.0', version: '>=3.22.0', + method: 'pkg-config', + required: false) + gtk = declare_dependency(dependencies: [gtk, gtkx11], + version: gtk.version()) + + if not get_option('vte').auto() or have_system + vte = dependency('vte-2.91', + method: 'pkg-config', + required: get_option('vte')) + endif + elif have_gtk_clipboard + error('GTK clipboard requested, but GTK not found') + endif +endif + +x11 = not_found +if gtkx11.found() + x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found()) +endif +png = not_found +if get_option('png').allowed() and have_system + png = dependency('libpng', version: '>=1.6.34', required: get_option('png'), + method: 'pkg-config') +endif +vnc = not_found +jpeg = not_found +sasl = not_found +if get_option('vnc') \ + .disable_auto_if(not have_system) \ + .require(pixman.found(), + error_message: 'cannot enable VNC if pixman is not available') \ + .allowed() + vnc = declare_dependency() # dummy dependency + jpeg = dependency('libjpeg', required: get_option('vnc_jpeg'), + method: 'pkg-config') + sasl = cc.find_library('sasl2', has_headers: ['sasl/sasl.h'], + required: get_option('vnc_sasl')) + if sasl.found() + sasl = declare_dependency(dependencies: sasl, + compile_args: '-DSTRUCT_IOVEC_DEFINED') + endif +endif + +pam = not_found +if not get_option('auth_pam').auto() or have_system + pam = cc.find_library('pam', has_headers: ['security/pam_appl.h'], + required: get_option('auth_pam')) +endif +if pam.found() and not cc.links(''' + #include + #include + int main(void) { + const char *service_name = "qemu"; + const char *user = "frank"; + const struct pam_conv pam_conv = { 0 }; + pam_handle_t *pamh = NULL; + pam_start(service_name, user, &pam_conv, &pamh); + return 0; + }''', dependencies: pam) + pam = not_found + if get_option('auth_pam').enabled() + error('could not link libpam') + else + warning('could not link libpam, disabling') + endif +endif + +snappy = not_found +if not get_option('snappy').auto() or have_system + snappy = cc.find_library('snappy', has_headers: ['snappy-c.h'], + required: get_option('snappy')) +endif +if snappy.found() and not cc.links(''' + #include + int main(void) { snappy_max_compressed_length(4096); return 0; }''', dependencies: snappy) + snappy = not_found + if get_option('snappy').enabled() + error('could not link libsnappy') + else + warning('could not link libsnappy, disabling') + endif +endif + +lzo = not_found +if not get_option('lzo').auto() or have_system + lzo = cc.find_library('lzo2', has_headers: ['lzo/lzo1x.h'], + required: get_option('lzo')) +endif +if lzo.found() and not cc.links(''' + #include + int main(void) { lzo_version(); return 0; }''', dependencies: lzo) + lzo = not_found + if get_option('lzo').enabled() + error('could not link liblzo2') + else + warning('could not link liblzo2, disabling') + endif +endif + +numa = not_found +if not get_option('numa').auto() or have_system or have_tools + numa = cc.find_library('numa', has_headers: ['numa.h'], + required: get_option('numa')) +endif +if numa.found() and not cc.links(''' + #include + int main(void) { return numa_available(); } + ''', dependencies: numa) + numa = not_found + if get_option('numa').enabled() + error('could not link numa') + else + warning('could not link numa, disabling') + endif +endif + +rdma = not_found +if not get_option('rdma').auto() or have_system + libumad = cc.find_library('ibumad', required: get_option('rdma')) + rdma_libs = [cc.find_library('rdmacm', has_headers: ['rdma/rdma_cma.h'], + required: get_option('rdma')), + cc.find_library('ibverbs', required: get_option('rdma')), + libumad] + rdma = declare_dependency(dependencies: rdma_libs) + foreach lib: rdma_libs + if not lib.found() + rdma = not_found + endif + endforeach +endif cacard = not_found if not get_option('smartcard').auto() or have_system cacard = dependency('libcacard', required: get_option('smartcard'), - version: '>=2.5.1', method: 'pkg-config', - kwargs: static_kwargs) + version: '>=2.5.1', method: 'pkg-config') endif u2f = not_found -if have_system +if not get_option('u2f').auto() or have_system u2f = dependency('u2f-emu', required: get_option('u2f'), - method: 'pkg-config', - kwargs: static_kwargs) + method: 'pkg-config') +endif +canokey = not_found +if not get_option('canokey').auto() or have_system + canokey = dependency('canokey-qemu', required: get_option('canokey'), + method: 'pkg-config') endif usbredir = not_found if not get_option('usb_redir').auto() or have_system usbredir = dependency('libusbredirparser-0.5', required: get_option('usb_redir'), - version: '>=0.6', method: 'pkg-config', - kwargs: static_kwargs) + version: '>=0.6', method: 'pkg-config') endif libusb = not_found if not get_option('libusb').auto() or have_system libusb = dependency('libusb-1.0', required: get_option('libusb'), - version: '>=1.0.13', method: 'pkg-config', - kwargs: static_kwargs) + version: '>=1.0.13', method: 'pkg-config') endif libpmem = not_found if not get_option('libpmem').auto() or have_system libpmem = dependency('libpmem', required: get_option('libpmem'), - method: 'pkg-config', kwargs: static_kwargs) + method: 'pkg-config') endif libdaxctl = not_found if not get_option('libdaxctl').auto() or have_system libdaxctl = dependency('libdaxctl', required: get_option('libdaxctl'), - version: '>=57', method: 'pkg-config', - kwargs: static_kwargs) + version: '>=57', method: 'pkg-config') endif tasn1 = not_found if gnutls.found() tasn1 = dependency('libtasn1', - method: 'pkg-config', - kwargs: static_kwargs) + method: 'pkg-config') +endif +keyutils = not_found +if not get_option('libkeyutils').auto() or have_block + keyutils = dependency('libkeyutils', required: get_option('libkeyutils'), + method: 'pkg-config') endif -keyutils = dependency('libkeyutils', required: false, - method: 'pkg-config', kwargs: static_kwargs) has_gettid = cc.has_function('gettid') # libselinux selinux = dependency('libselinux', required: get_option('selinux'), - method: 'pkg-config', kwargs: static_kwargs) + method: 'pkg-config') # Malloc tests @@ -1454,8 +1933,7 @@ malloc = [] if get_option('malloc') == 'system' has_malloc_trim = \ get_option('malloc_trim').allowed() and \ - cc.links('''#include - int main(void) { malloc_trim(0); return 0; }''') + cc.has_function('malloc_trim', prefix: '#include ') else has_malloc_trim = false malloc = cc.find_library(get_option('malloc'), required: true) @@ -1468,41 +1946,26 @@ if not has_malloc_trim and get_option('malloc_trim').enabled() endif endif -# Check whether the glibc provides statx() - gnu_source_prefix = ''' #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif ''' -statx_test = gnu_source_prefix + ''' - #include - int main(void) { - struct statx statxbuf; - statx(0, "", 0, STATX_BASIC_STATS, &statxbuf); - return 0; - }''' -has_statx = cc.links(statx_test) +# Check whether the glibc provides STATX_BASIC_STATS + +has_statx = cc.has_header_symbol('sys/stat.h', 'STATX_BASIC_STATS', prefix: gnu_source_prefix) # Check whether statx() provides mount ID information -statx_mnt_id_test = gnu_source_prefix + ''' - #include - int main(void) { - struct statx statxbuf; - statx(0, "", 0, STATX_BASIC_STATS | STATX_MNT_ID, &statxbuf); - return statxbuf.stx_mnt_id; - }''' - -has_statx_mnt_id = cc.links(statx_mnt_id_test) +has_statx_mnt_id = cc.has_header_symbol('sys/stat.h', 'STATX_MNT_ID', prefix: gnu_source_prefix) have_vhost_user_blk_server = get_option('vhost_user_blk_server') \ - .require(targetos == 'linux', + .require(host_os == 'linux', error_message: 'vhost_user_blk_server requires linux') \ .require(have_vhost_user, error_message: 'vhost_user_blk_server requires vhost-user support') \ - .disable_auto_if(not have_system) \ + .disable_auto_if(not have_tools and not have_system) \ .allowed() if get_option('fuse').disabled() and get_option('fuse_lseek').enabled() @@ -1510,8 +1973,7 @@ if get_option('fuse').disabled() and get_option('fuse_lseek').enabled() endif fuse = dependency('fuse3', required: get_option('fuse'), - version: '>=3.1', method: 'pkg-config', - kwargs: static_kwargs) + version: '>=3.1', method: 'pkg-config') fuse_lseek = not_found if get_option('fuse_lseek').allowed() @@ -1527,27 +1989,69 @@ if get_option('fuse_lseek').allowed() endif endif +have_libvduse = (host_os == 'linux') +if get_option('libvduse').enabled() + if host_os != 'linux' + error('libvduse requires linux') + endif +elif get_option('libvduse').disabled() + have_libvduse = false +endif + +have_vduse_blk_export = (have_libvduse and host_os == 'linux') +if get_option('vduse_blk_export').enabled() + if host_os != 'linux' + error('vduse_blk_export requires linux') + elif not have_libvduse + error('vduse_blk_export requires libvduse support') + endif +elif get_option('vduse_blk_export').disabled() + have_vduse_blk_export = false +endif + # libbpf -libbpf = dependency('libbpf', required: get_option('bpf'), method: 'pkg-config') +bpf_version = '1.1.0' +libbpf = dependency('libbpf', version: '>=' + bpf_version, required: get_option('bpf'), method: 'pkg-config') if libbpf.found() and not cc.links(''' #include + #include int main(void) { + // check flag availability + int flag = BPF_F_MMAPABLE; bpf_object__destroy_skeleton(NULL); return 0; }''', dependencies: libbpf) libbpf = not_found if get_option('bpf').enabled() - error('libbpf skeleton test failed') + error('libbpf skeleton/mmaping test failed') else - warning('libbpf skeleton test failed, disabling') + warning('libbpf skeleton/mmaping test failed, disabling') endif endif +# libxdp +libxdp = not_found +if not get_option('af_xdp').auto() or have_system + libxdp = dependency('libxdp', required: get_option('af_xdp'), + version: '>=1.4.0', method: 'pkg-config') +endif + +# libdw +libdw = not_found +if not get_option('libdw').auto() or \ + (not get_option('prefer_static') and (have_system or have_user)) + libdw = dependency('libdw', + method: 'pkg-config', + required: get_option('libdw')) +endif + ################# # config-host.h # ################# +config_host_data = configuration_data() + audio_drivers_selected = [] if have_system audio_drivers_available = { @@ -1557,7 +2061,9 @@ if have_system 'jack': jack.found(), 'oss': oss.found(), 'pa': pulse.found(), + 'pipewire': pipewire.found(), 'sdl': sdl.found(), + 'sndio': sndio.found(), } foreach k, v: audio_drivers_available config_host_data.set('CONFIG_AUDIO_' + k.to_upper(), v) @@ -1565,8 +2071,8 @@ if have_system # Default to native drivers first, OSS second, SDL third audio_drivers_priority = \ - [ 'pa', 'coreaudio', 'dsound', 'oss' ] + \ - (targetos == 'linux' ? [] : [ 'sdl' ]) + [ 'pa', 'coreaudio', 'dsound', 'sndio', 'oss' ] + \ + (host_os == 'linux' ? [] : [ 'sdl' ]) audio_drivers_default = [] foreach k: audio_drivers_priority if audio_drivers_available[k] @@ -1587,70 +2093,32 @@ endif config_host_data.set('CONFIG_AUDIO_DRIVERS', '"' + '", "'.join(audio_drivers_selected) + '", ') -if get_option('cfi') - cfi_flags=[] - # Check for dependency on LTO - if not get_option('b_lto') - error('Selected Control-Flow Integrity but LTO is disabled') - endif - if config_host.has_key('CONFIG_MODULES') - error('Selected Control-Flow Integrity is not compatible with modules') - endif - # Check for cfi flags. CFI requires LTO so we can't use - # get_supported_arguments, but need a more complex "compiles" which allows - # custom arguments - if cc.compiles('int main () { return 0; }', name: '-fsanitize=cfi-icall', - args: ['-flto', '-fsanitize=cfi-icall'] ) - cfi_flags += '-fsanitize=cfi-icall' - else - error('-fsanitize=cfi-icall is not supported by the compiler') - endif - if cc.compiles('int main () { return 0; }', - name: '-fsanitize-cfi-icall-generalize-pointers', - args: ['-flto', '-fsanitize=cfi-icall', - '-fsanitize-cfi-icall-generalize-pointers'] ) - cfi_flags += '-fsanitize-cfi-icall-generalize-pointers' - else - error('-fsanitize-cfi-icall-generalize-pointers is not supported by the compiler') - endif - if get_option('cfi_debug') - if cc.compiles('int main () { return 0; }', - name: '-fno-sanitize-trap=cfi-icall', - args: ['-flto', '-fsanitize=cfi-icall', - '-fno-sanitize-trap=cfi-icall'] ) - cfi_flags += '-fno-sanitize-trap=cfi-icall' - else - error('-fno-sanitize-trap=cfi-icall is not supported by the compiler') - endif - endif - add_global_arguments(cfi_flags, native: false, language: ['c', 'cpp', 'objc']) - add_global_link_arguments(cfi_flags, native: false, language: ['c', 'cpp', 'objc']) -endif - -have_host_block_device = (targetos != 'darwin' or +have_host_block_device = (host_os != 'darwin' or cc.has_header('IOKit/storage/IOMedia.h')) -# FIXME enable_modules shouldn't be necessary, but: https://github.com/mesonbuild/meson/issues/8333 dbus_display = get_option('dbus_display') \ .require(gio.version().version_compare('>=2.64'), error_message: '-display dbus requires glib>=2.64') \ - .require(enable_modules, - error_message: '-display dbus requires --enable-modules') \ .require(gdbus_codegen.found(), - error_message: '-display dbus requires gdbus-codegen') \ + error_message: gdbus_codegen_error.format('-display dbus')) \ .allowed() have_virtfs = get_option('virtfs') \ - .require(targetos == 'linux' or targetos == 'darwin', + .require(host_os == 'linux' or host_os == 'darwin', error_message: 'virtio-9p (virtfs) requires Linux or macOS') \ - .require(targetos == 'linux' or cc.has_function('pthread_fchdir_np'), + .require(host_os == 'linux' or cc.has_function('pthread_fchdir_np'), error_message: 'virtio-9p (virtfs) on macOS requires the presence of pthread_fchdir_np') \ - .require(targetos == 'darwin' or (libattr.found() and libcap_ng.found()), - error_message: 'virtio-9p (virtfs) on Linux requires libcap-ng-devel and libattr-devel') \ + .require(host_os == 'darwin' or libattr.found(), + error_message: 'virtio-9p (virtfs) on Linux requires libattr-devel') \ .disable_auto_if(not have_tools and not have_system) \ .allowed() -have_virtfs_proxy_helper = targetos != 'darwin' and have_virtfs and have_tools +have_virtfs_proxy_helper = get_option('virtfs_proxy_helper') \ + .require(host_os != 'darwin', error_message: 'the virtfs proxy helper is incompatible with macOS') \ + .require(have_virtfs, error_message: 'the virtfs proxy helper requires that virtfs is enabled') \ + .disable_auto_if(not have_tools) \ + .require(libcap_ng.found(), error_message: 'the virtfs proxy helper requires libcap-ng') \ + .allowed() if get_option('block_drv_ro_whitelist') == '' config_host_data.set('CONFIG_BDRV_RO_WHITELIST', '') @@ -1678,7 +2146,13 @@ config_host_data.set_quoted('CONFIG_PREFIX', get_option('prefix')) config_host_data.set_quoted('CONFIG_QEMU_CONFDIR', get_option('prefix') / qemu_confdir) config_host_data.set_quoted('CONFIG_QEMU_DATADIR', get_option('prefix') / qemu_datadir) config_host_data.set_quoted('CONFIG_QEMU_DESKTOPDIR', get_option('prefix') / qemu_desktopdir) -config_host_data.set_quoted('CONFIG_QEMU_FIRMWAREPATH', get_option('prefix') / get_option('qemu_firmwarepath')) + +qemu_firmwarepath = '' +foreach k : get_option('qemu_firmwarepath') + qemu_firmwarepath += '"' + get_option('prefix') / k + '", ' +endforeach +config_host_data.set('CONFIG_QEMU_FIRMWAREPATH', qemu_firmwarepath) + config_host_data.set_quoted('CONFIG_QEMU_HELPERDIR', get_option('prefix') / get_option('libexecdir')) config_host_data.set_quoted('CONFIG_QEMU_ICONDIR', get_option('prefix') / qemu_icondir) config_host_data.set_quoted('CONFIG_QEMU_LOCALEDIR', get_option('prefix') / get_option('localedir')) @@ -1686,7 +2160,7 @@ config_host_data.set_quoted('CONFIG_QEMU_LOCALSTATEDIR', get_option('prefix') / config_host_data.set_quoted('CONFIG_QEMU_MODDIR', get_option('prefix') / qemu_moddir) config_host_data.set_quoted('CONFIG_SYSCONFDIR', get_option('prefix') / get_option('sysconfdir')) -if config_host.has_key('CONFIG_MODULES') +if enable_modules config_host_data.set('CONFIG_STAMP', run_command( meson.current_source_dir() / 'scripts/qemu-stamp.py', meson.project_version(), get_option('pkgversion'), '--', @@ -1695,18 +2169,24 @@ if config_host.has_key('CONFIG_MODULES') endif have_slirp_smbd = get_option('slirp_smbd') \ - .require(targetos != 'windows', error_message: 'Host smbd not supported on this platform.') \ + .require(host_os != 'windows', error_message: 'Host smbd not supported on this platform.') \ .allowed() if have_slirp_smbd smbd_path = get_option('smbd') if smbd_path == '' - smbd_path = (targetos == 'solaris' ? '/usr/sfw/sbin/smbd' : '/usr/sbin/smbd') + smbd_path = (host_os == 'sunos' ? '/usr/sfw/sbin/smbd' : '/usr/sbin/smbd') endif config_host_data.set_quoted('CONFIG_SMBD_COMMAND', smbd_path) endif config_host_data.set('HOST_' + host_arch.to_upper(), 1) +kvm_targets_c = '""' +if get_option('kvm').allowed() and host_os == 'linux' + kvm_targets_c = '"' + '" ,"'.join(kvm_targets) + '"' +endif +config_host_data.set('CONFIG_KVM_TARGETS', kvm_targets_c) + if get_option('module_upgrades') and not enable_modules error('Cannot enable module-upgrades as modules are not enabled') endif @@ -1715,13 +2195,23 @@ config_host_data.set('CONFIG_MODULE_UPGRADES', get_option('module_upgrades')) config_host_data.set('CONFIG_ATTR', libattr.found()) config_host_data.set('CONFIG_BDRV_WHITELIST_TOOLS', get_option('block_drv_whitelist_in_tools')) config_host_data.set('CONFIG_BRLAPI', brlapi.found()) +config_host_data.set('CONFIG_BSD', host_os in bsd_oses) +config_host_data.set('CONFIG_CAPSTONE', capstone.found()) config_host_data.set('CONFIG_COCOA', cocoa.found()) +config_host_data.set('CONFIG_DARWIN', host_os == 'darwin') config_host_data.set('CONFIG_FUZZ', get_option('fuzzing')) config_host_data.set('CONFIG_GCOV', get_option('b_coverage')) config_host_data.set('CONFIG_LIBUDEV', libudev.found()) +config_host_data.set('CONFIG_LINUX', host_os == 'linux') +config_host_data.set('CONFIG_POSIX', host_os != 'windows') +config_host_data.set('CONFIG_WIN32', host_os == 'windows') config_host_data.set('CONFIG_LZO', lzo.found()) config_host_data.set('CONFIG_MPATH', mpathpersist.found()) -config_host_data.set('CONFIG_MPATH_NEW_API', mpathpersist_new_api) +config_host_data.set('CONFIG_BLKIO', blkio.found()) +if blkio.found() + config_host_data.set('CONFIG_BLKIO_VHOST_VDPA_FD', + blkio.version().version_compare('>=1.3.0')) +endif config_host_data.set('CONFIG_CURL', curl.found()) config_host_data.set('CONFIG_CURSES', curses.found()) config_host_data.set('CONFIG_GBM', gbm.found()) @@ -1737,9 +2227,12 @@ if glusterfs.found() endif config_host_data.set('CONFIG_GTK', gtk.found()) config_host_data.set('CONFIG_VTE', vte.found()) +config_host_data.set('CONFIG_GTK_CLIPBOARD', have_gtk_clipboard) +config_host_data.set('CONFIG_HEXAGON_IDEF_PARSER', get_option('hexagon_idef_parser')) config_host_data.set('CONFIG_LIBATTR', have_old_libattr) config_host_data.set('CONFIG_LIBCAP_NG', libcap_ng.found()) config_host_data.set('CONFIG_EBPF', libbpf.found()) +config_host_data.set('CONFIG_AF_XDP', libxdp.found()) config_host_data.set('CONFIG_LIBDAXCTL', libdaxctl.found()) config_host_data.set('CONFIG_LIBISCSI', libiscsi.found()) config_host_data.set('CONFIG_LIBNFS', libnfs.found()) @@ -1747,18 +2240,38 @@ config_host_data.set('CONFIG_LIBSSH', libssh.found()) config_host_data.set('CONFIG_LINUX_AIO', libaio.found()) config_host_data.set('CONFIG_LINUX_IO_URING', linux_io_uring.found()) config_host_data.set('CONFIG_LIBPMEM', libpmem.found()) +config_host_data.set('CONFIG_MODULES', enable_modules) config_host_data.set('CONFIG_NUMA', numa.found()) +if numa.found() + config_host_data.set('HAVE_NUMA_HAS_PREFERRED_MANY', + cc.has_function('numa_has_preferred_many', + dependencies: numa)) +endif config_host_data.set('CONFIG_OPENGL', opengl.found()) -config_host_data.set('CONFIG_PROFILER', get_option('profiler')) +config_host_data.set('CONFIG_PLUGIN', get_option('plugins')) config_host_data.set('CONFIG_RBD', rbd.found()) config_host_data.set('CONFIG_RDMA', rdma.found()) +config_host_data.set('CONFIG_RELOCATABLE', get_option('relocatable')) +config_host_data.set('CONFIG_SAFESTACK', get_option('safe_stack')) config_host_data.set('CONFIG_SDL', sdl.found()) config_host_data.set('CONFIG_SDL_IMAGE', sdl_image.found()) config_host_data.set('CONFIG_SECCOMP', seccomp.found()) +if seccomp.found() + config_host_data.set('CONFIG_SECCOMP_SYSRAWRC', seccomp_has_sysrawrc) +endif +config_host_data.set('CONFIG_PIXMAN', pixman.found()) +config_host_data.set('CONFIG_SLIRP', slirp.found()) config_host_data.set('CONFIG_SNAPPY', snappy.found()) +config_host_data.set('CONFIG_SOLARIS', host_os == 'sunos') +if get_option('tcg').allowed() + config_host_data.set('CONFIG_TCG', 1) + config_host_data.set('CONFIG_TCG_INTERPRETER', tcg_arch == 'tci') +endif config_host_data.set('CONFIG_TPM', have_tpm) +config_host_data.set('CONFIG_TSAN', get_option('tsan')) config_host_data.set('CONFIG_USB_LIBUSB', libusb.found()) config_host_data.set('CONFIG_VDE', vde.found()) +config_host_data.set('CONFIG_VHOST', have_vhost) config_host_data.set('CONFIG_VHOST_NET', have_vhost_net) config_host_data.set('CONFIG_VHOST_NET_USER', have_vhost_net_user) config_host_data.set('CONFIG_VHOST_NET_VDPA', have_vhost_net_vdpa) @@ -1768,10 +2281,17 @@ config_host_data.set('CONFIG_VHOST_CRYPTO', have_vhost_user_crypto) config_host_data.set('CONFIG_VHOST_VDPA', have_vhost_vdpa) config_host_data.set('CONFIG_VMNET', vmnet.found()) config_host_data.set('CONFIG_VHOST_USER_BLK_SERVER', have_vhost_user_blk_server) +config_host_data.set('CONFIG_VDUSE_BLK_EXPORT', have_vduse_blk_export) config_host_data.set('CONFIG_PNG', png.found()) config_host_data.set('CONFIG_VNC', vnc.found()) config_host_data.set('CONFIG_VNC_JPEG', jpeg.found()) config_host_data.set('CONFIG_VNC_SASL', sasl.found()) +if virgl.found() + config_host_data.set('HAVE_VIRGL_D3D_INFO_EXT', + cc.has_member('struct virgl_renderer_resource_info_ext', 'd3d_tex2d', + prefix: '#include ', + dependencies: virgl)) +endif config_host_data.set('CONFIG_VIRTFS', have_virtfs) config_host_data.set('CONFIG_VTE', vte.found()) config_host_data.set('CONFIG_XKBCOMMON', xkbcommon.found()) @@ -1782,6 +2302,7 @@ config_host_data.set('CONFIG_GNUTLS_CRYPTO', gnutls_crypto.found()) config_host_data.set('CONFIG_TASN1', tasn1.found()) config_host_data.set('CONFIG_GCRYPT', gcrypt.found()) config_host_data.set('CONFIG_NETTLE', nettle.found()) +config_host_data.set('CONFIG_CRYPTO_SM4', crypto_sm4.found()) config_host_data.set('CONFIG_HOGWEED', hogweed.found()) config_host_data.set('CONFIG_QEMU_PRIVATE_XTS', xts == 'private') config_host_data.set('CONFIG_MALLOC_TRIM', has_malloc_trim) @@ -1802,6 +2323,7 @@ config_host_data.set('CONFIG_DBUS_DISPLAY', dbus_display) config_host_data.set('CONFIG_CFI', get_option('cfi')) config_host_data.set('CONFIG_SELINUX', selinux.found()) config_host_data.set('CONFIG_XEN_BACKEND', xen.found()) +config_host_data.set('CONFIG_LIBDW', libdw.found()) if xen.found() # protect from xen.version() having less than three components xen_version = xen.version().split('.') + ['0', '0'] @@ -1823,13 +2345,14 @@ if get_option('debug_stack_usage') and have_coroutine_pool message('Disabling coroutine pool to measure stack usage') have_coroutine_pool = false endif -config_host_data.set10('CONFIG_COROUTINE_POOL', have_coroutine_pool) +config_host_data.set('CONFIG_COROUTINE_POOL', have_coroutine_pool) +config_host_data.set('CONFIG_DEBUG_GRAPH_LOCK', get_option('debug_graph_lock')) config_host_data.set('CONFIG_DEBUG_MUTEX', get_option('debug_mutex')) config_host_data.set('CONFIG_DEBUG_STACK_USAGE', get_option('debug_stack_usage')) -config_host_data.set('CONFIG_GPROF', get_option('gprof')) +config_host_data.set('CONFIG_DEBUG_TCG', get_option('debug_tcg')) config_host_data.set('CONFIG_LIVE_BLOCK_MIGRATION', get_option('live_block_migration').allowed()) config_host_data.set('CONFIG_QOM_CAST_DEBUG', get_option('qom_cast_debug')) -config_host_data.set('CONFIG_REPLICATION', get_option('live_block_migration').allowed()) +config_host_data.set('CONFIG_REPLICATION', get_option('replication').allowed()) # has_header config_host_data.set('CONFIG_EPOLL', cc.has_header('sys/epoll.h')) @@ -1841,13 +2364,19 @@ config_host_data.set('HAVE_PTY_H', cc.has_header('pty.h')) config_host_data.set('HAVE_SYS_DISK_H', cc.has_header('sys/disk.h')) config_host_data.set('HAVE_SYS_IOCCOM_H', cc.has_header('sys/ioccom.h')) config_host_data.set('HAVE_SYS_KCOV_H', cc.has_header('sys/kcov.h')) +if host_os == 'windows' + config_host_data.set('HAVE_AFUNIX_H', cc.has_header('afunix.h')) +endif # has_function +config_host_data.set('CONFIG_CLOSE_RANGE', cc.has_function('close_range')) config_host_data.set('CONFIG_ACCEPT4', cc.has_function('accept4')) config_host_data.set('CONFIG_CLOCK_ADJTIME', cc.has_function('clock_adjtime')) config_host_data.set('CONFIG_DUP3', cc.has_function('dup3')) config_host_data.set('CONFIG_FALLOCATE', cc.has_function('fallocate')) config_host_data.set('CONFIG_POSIX_FALLOCATE', cc.has_function('posix_fallocate')) +config_host_data.set('CONFIG_GETCPU', cc.has_function('getcpu', prefix: gnu_source_prefix)) +config_host_data.set('CONFIG_SCHED_GETCPU', cc.has_function('sched_getcpu', prefix: '#include ')) # Note that we need to specify prefix: here to avoid incorrectly # thinking that Windows has posix_memalign() config_host_data.set('CONFIG_POSIX_MEMALIGN', cc.has_function('posix_memalign', prefix: '#include ')) @@ -1864,9 +2393,16 @@ config_host_data.set('CONFIG_SYNC_FILE_RANGE', cc.has_function('sync_file_range' config_host_data.set('CONFIG_TIMERFD', cc.has_function('timerfd_create')) config_host_data.set('HAVE_COPY_FILE_RANGE', cc.has_function('copy_file_range')) config_host_data.set('HAVE_GETIFADDRS', cc.has_function('getifaddrs')) +config_host_data.set('HAVE_GLIB_WITH_SLICE_ALLOCATOR', glib_has_gslice) config_host_data.set('HAVE_OPENPTY', cc.has_function('openpty', dependencies: util)) config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul')) config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include ')) +if rbd.found() + config_host_data.set('HAVE_RBD_NAMESPACE_EXISTS', + cc.has_function('rbd_namespace_exists', + dependencies: rbd, + prefix: '#include ')) +endif if rdma.found() config_host_data.set('HAVE_IBV_ADVISE_MR', cc.has_function('ibv_advise_mr', @@ -1874,9 +2410,37 @@ if rdma.found() prefix: '#include ')) endif +have_asan_fiber = false +if get_option('sanitizers') and \ + not cc.has_function('__sanitizer_start_switch_fiber', + args: '-fsanitize=address', + prefix: '#include ') + warning('Missing ASAN due to missing fiber annotation interface') + warning('Without code annotation, the report may be inferior.') +else + have_asan_fiber = true +endif +config_host_data.set('CONFIG_ASAN_IFACE_FIBER', have_asan_fiber) + +have_inotify_init = cc.has_header_symbol('sys/inotify.h', 'inotify_init') +have_inotify_init1 = cc.has_header_symbol('sys/inotify.h', 'inotify_init1') +inotify = not_found +if (have_inotify_init or have_inotify_init1) and host_os == 'freebsd' + # libinotify-kqueue + inotify = cc.find_library('inotify') + if have_inotify_init + have_inotify_init = inotify.found() + endif + if have_inotify_init1 + have_inotify_init1 = inotify.found() + endif +endif +config_host_data.set('CONFIG_INOTIFY', have_inotify_init) +config_host_data.set('CONFIG_INOTIFY1', have_inotify_init1) + # has_header_symbol -config_host_data.set('CONFIG_BYTESWAP_H', - cc.has_header_symbol('byteswap.h', 'bswap_32')) +config_host_data.set('CONFIG_BLKZONED', + cc.has_header_symbol('linux/blkzoned.h', 'BLKOPENZONE')) config_host_data.set('CONFIG_EPOLL_CREATE1', cc.has_header_symbol('sys/epoll.h', 'epoll_create1')) config_host_data.set('CONFIG_FALLOCATE_PUNCH_HOLE', @@ -1890,14 +2454,6 @@ config_host_data.set('CONFIG_FIEMAP', config_host_data.set('CONFIG_GETRANDOM', cc.has_function('getrandom') and cc.has_header_symbol('sys/random.h', 'GRND_NONBLOCK')) -config_host_data.set('CONFIG_INOTIFY', - cc.has_header_symbol('sys/inotify.h', 'inotify_init')) -config_host_data.set('CONFIG_INOTIFY1', - cc.has_header_symbol('sys/inotify.h', 'inotify_init1')) -config_host_data.set('CONFIG_MACHINE_BSWAP_H', - cc.has_header_symbol('machine/bswap.h', 'bswap32', - prefix: '''#include - #include ''')) config_host_data.set('CONFIG_PRCTL_PR_SET_TIMERSLACK', cc.has_header_symbol('sys/prctl.h', 'PR_SET_TIMERSLACK')) config_host_data.set('CONFIG_RTNETLINK', @@ -1916,6 +2472,9 @@ config_host_data.set('HAVE_SIGEV_NOTIFY_THREAD_ID', config_host_data.set('HAVE_STRUCT_STAT_ST_ATIM', cc.has_member('struct stat', 'st_atim', prefix: '#include ')) +config_host_data.set('HAVE_BLK_ZONE_REP_CAPACITY', + cc.has_member('struct blk_zone', 'capacity', + prefix: '#include ')) # has_type config_host_data.set('CONFIG_IOVEC', @@ -1971,15 +2530,6 @@ config_host_data.set('CONFIG_OPEN_BY_HANDLE', cc.links(gnu_source_prefix + ''' #else int main(void) { struct file_handle fh; return open_by_handle_at(0, &fh, 0); } #endif''')) -config_host_data.set('CONFIG_PIPE2', cc.links(gnu_source_prefix + ''' - #include - #include - - int main(void) - { - int pipefd[2]; - return pipe2(pipefd, O_CLOEXEC); - }''')) config_host_data.set('CONFIG_POSIX_MADVISE', cc.links(gnu_source_prefix + ''' #include #include @@ -2006,6 +2556,18 @@ config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_WO_TID', cc.links(gnu_source_pre pthread_create(&thread, 0, f, 0); return 0; }''', dependencies: threads)) +config_host_data.set('CONFIG_PTHREAD_SET_NAME_NP', cc.links(gnu_source_prefix + ''' + #include + #include + + static void *f(void *p) { return NULL; } + int main(void) + { + pthread_t thread; + pthread_create(&thread, 0, f, 0); + pthread_set_name_np(thread, "QEMU"); + return 0; + }''', dependencies: threads)) config_host_data.set('CONFIG_PTHREAD_CONDATTR_SETCLOCK', cc.links(gnu_source_prefix + ''' #include #include @@ -2017,7 +2579,23 @@ config_host_data.set('CONFIG_PTHREAD_CONDATTR_SETCLOCK', cc.links(gnu_source_pre pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); return 0; }''', dependencies: threads)) +config_host_data.set('CONFIG_PTHREAD_AFFINITY_NP', cc.links(gnu_source_prefix + ''' + #include + static void *f(void *p) { return NULL; } + int main(void) + { + int setsize = CPU_ALLOC_SIZE(64); + pthread_t thread; + cpu_set_t *cpuset; + pthread_create(&thread, 0, f, 0); + cpuset = CPU_ALLOC(64); + CPU_ZERO_S(setsize, cpuset); + pthread_setaffinity_np(thread, setsize, cpuset); + pthread_getaffinity_np(thread, setsize, cpuset); + CPU_FREE(cpuset); + return 0; + }''', dependencies: threads)) config_host_data.set('CONFIG_SIGNALFD', cc.links(gnu_source_prefix + ''' #include #include @@ -2037,7 +2615,7 @@ config_host_data.set('CONFIG_SPLICE', cc.links(gnu_source_prefix + ''' config_host_data.set('HAVE_MLOCKALL', cc.links(gnu_source_prefix + ''' #include - int main(int argc, char *argv[]) { + int main(void) { return mlockall(MCL_FUTURE); }''')) @@ -2082,29 +2660,33 @@ config_host_data.set('HAVE_FSXATTR', cc.links(''' config_host_data.set('HAVE_BROKEN_SIZE_MAX', not cc.compiles(''' #include #include - int main(int argc, char *argv[]) { + int main(void) { return printf("%zu", SIZE_MAX); }''', args: ['-Werror'])) -atomic_test = ''' +# See if 64-bit atomic operations are supported. +# Note that without __atomic builtins, we can only +# assume atomic loads/stores max at pointer size. +config_host_data.set('CONFIG_ATOMIC64', cc.links(''' #include int main(void) { - @0@ x = 0, y = 0; + uint64_t x = 0, y = 0; y = __atomic_load_n(&x, __ATOMIC_RELAXED); __atomic_store_n(&x, y, __ATOMIC_RELAXED); __atomic_compare_exchange_n(&x, &y, x, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED); __atomic_exchange_n(&x, y, __ATOMIC_RELAXED); __atomic_fetch_add(&x, y, __ATOMIC_RELAXED); return 0; - }''' + }''')) -# See if 64-bit atomic operations are supported. -# Note that without __atomic builtins, we can only -# assume atomic loads/stores max at pointer size. -config_host_data.set('CONFIG_ATOMIC64', cc.links(atomic_test.format('uint64_t'))) +has_int128_type = cc.compiles(''' + __int128_t a; + __uint128_t b; + int main(void) { b = a; }''') +config_host_data.set('CONFIG_INT128_TYPE', has_int128_type) -has_int128 = cc.links(''' +has_int128 = has_int128_type and cc.links(''' __int128_t a; __uint128_t b; int main (void) { @@ -2113,28 +2695,45 @@ has_int128 = cc.links(''' a = a * a; return 0; }''') - config_host_data.set('CONFIG_INT128', has_int128) -if has_int128 +if has_int128_type # "do we have 128-bit atomics which are handled inline and specifically not # via libatomic". The reason we can't use libatomic is documented in the # comment starting "GCC is a house divided" in include/qemu/atomic128.h. - has_atomic128 = cc.links(atomic_test.format('unsigned __int128')) + # We only care about these operations on 16-byte aligned pointers, so + # force 16-byte alignment of the pointer, which may be greater than + # __alignof(unsigned __int128) for the host. + atomic_test_128 = ''' + int main(int ac, char **av) { + __uint128_t *p = __builtin_assume_aligned(av[ac - 1], 16); + p[1] = __atomic_load_n(&p[0], __ATOMIC_RELAXED); + __atomic_store_n(&p[2], p[3], __ATOMIC_RELAXED); + __atomic_compare_exchange_n(&p[4], &p[5], p[6], 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED); + return 0; + }''' + has_atomic128 = cc.links(atomic_test_128) config_host_data.set('CONFIG_ATOMIC128', has_atomic128) if not has_atomic128 - has_cmpxchg128 = cc.links(''' - int main(void) - { - unsigned __int128 x = 0, y = 0; - __sync_val_compare_and_swap_16(&x, y, x); - return 0; - } - ''') + # Even with __builtin_assume_aligned, the above test may have failed + # without optimization enabled. Try again with optimizations locally + # enabled for the function. See + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107389 + has_atomic128_opt = cc.links('__attribute__((optimize("O1")))' + atomic_test_128) + config_host_data.set('CONFIG_ATOMIC128_OPT', has_atomic128_opt) - config_host_data.set('CONFIG_CMPXCHG128', has_cmpxchg128) + if not has_atomic128_opt + config_host_data.set('CONFIG_CMPXCHG128', cc.links(''' + int main(void) + { + __uint128_t x = 0, y = 0; + __sync_val_compare_and_swap_16(&x, y, x); + return 0; + } + ''')) + endif endif endif @@ -2158,7 +2757,7 @@ config_host_data.set('CONFIG_USBFS', have_linux_user and cc.compiles(''' int main(void) { return 0; }''')) have_keyring = get_option('keyring') \ - .require(targetos == 'linux', error_message: 'keyring is only available on Linux') \ + .require(host_os == 'linux', error_message: 'keyring is only available on Linux') \ .require(cc.compiles(''' #include #include @@ -2191,31 +2790,49 @@ config_host_data.set('CONFIG_CPUID_H', have_cpuid_h) config_host_data.set('CONFIG_AVX2_OPT', get_option('avx2') \ .require(have_cpuid_h, error_message: 'cpuid.h not available, cannot enable AVX2') \ .require(cc.links(''' - #pragma GCC push_options - #pragma GCC target("avx2") #include #include - static int bar(void *a) { + static int __attribute__((target("avx2"))) bar(void *a) { __m256i x = *(__m256i *)a; return _mm256_testz_si256(x, x); } - int main(int argc, char *argv[]) { return bar(argv[0]); } + int main(int argc, char *argv[]) { return bar(argv[argc - 1]); } '''), error_message: 'AVX2 not available').allowed()) config_host_data.set('CONFIG_AVX512F_OPT', get_option('avx512f') \ .require(have_cpuid_h, error_message: 'cpuid.h not available, cannot enable AVX512F') \ .require(cc.links(''' - #pragma GCC push_options - #pragma GCC target("avx512f") #include #include - static int bar(void *a) { + static int __attribute__((target("avx512f"))) bar(void *a) { __m512i x = *(__m512i *)a; return _mm512_test_epi64_mask(x, x); } - int main(int argc, char *argv[]) { return bar(argv[0]); } + int main(int argc, char *argv[]) { return bar(argv[argc - 1]); } '''), error_message: 'AVX512F not available').allowed()) +config_host_data.set('CONFIG_AVX512BW_OPT', get_option('avx512bw') \ + .require(have_cpuid_h, error_message: 'cpuid.h not available, cannot enable AVX512BW') \ + .require(cc.links(''' + #include + #include + static int __attribute__((target("avx512bw"))) bar(void *a) { + __m512i *x = a; + __m512i res= _mm512_abs_epi8(*x); + return res[1]; + } + int main(int argc, char *argv[]) { return bar(argv[0]); } + '''), error_message: 'AVX512BW not available').allowed()) + +# For both AArch64 and AArch32, detect if builtins are available. +config_host_data.set('CONFIG_ARM_AES_BUILTIN', cc.compiles(''' + #include + #ifndef __ARM_FEATURE_AES + __attribute__((target("+crypto"))) + #endif + void foo(uint8x16_t *p) { *p = vaesmcq_u8(*p); } + ''')) + have_pvrdma = get_option('pvrdma') \ .require(rdma.found(), error_message: 'PVRDMA requires OpenFabrics libraries') \ .require(cc.compiles(gnu_source_prefix + ''' @@ -2249,9 +2866,9 @@ endif if get_option('membarrier').disabled() have_membarrier = false -elif targetos == 'windows' +elif host_os == 'windows' have_membarrier = true -elif targetos == 'linux' +elif host_os == 'linux' have_membarrier = cc.compiles(''' #include #include @@ -2288,7 +2905,7 @@ config_host_data.set('CONFIG_AF_VSOCK', cc.has_header_symbol( have_vss = false have_vss_sdk = false # old xp/2003 SDK -if targetos == 'windows' and link_language == 'cpp' +if host_os == 'windows' and 'cpp' in all_languages have_vss = cxx.compiles(''' #define __MIDL_user_allocate_free_DEFINED__ #include @@ -2297,15 +2914,9 @@ if targetos == 'windows' and link_language == 'cpp' endif config_host_data.set('HAVE_VSS_SDK', have_vss_sdk) -foreach k, v: config_host - if k.startswith('CONFIG_') - config_host_data.set(k, v == 'y' ? 1 : v) - endif -endforeach - # Older versions of MinGW do not import _lock_file and _unlock_file properly. # This was fixed for v6.0.0 with commit b48e3ac8969d. -if targetos == 'windows' +if host_os == 'windows' config_host_data.set('HAVE__LOCK_FILE', cc.links(''' #include int main(void) { @@ -2315,14 +2926,35 @@ if targetos == 'windows' }''', name: '_lock_file and _unlock_file')) endif +if host_os == 'windows' + mingw_has_setjmp_longjmp = cc.links(''' + #include + int main(void) { + /* + * These functions are not available in setjmp header, but may be + * available at link time, from libmingwex.a. + */ + extern int __mingw_setjmp(jmp_buf); + extern void __attribute__((noreturn)) __mingw_longjmp(jmp_buf, int); + jmp_buf env; + __mingw_setjmp(env); + __mingw_longjmp(env, 0); + } + ''', name: 'mingw setjmp and longjmp') + + if cpu == 'aarch64' and not mingw_has_setjmp_longjmp + error('mingw must provide setjmp/longjmp for windows-arm64') + endif +endif + ######################## # Target configuration # ######################## minikconf = find_program('scripts/minikconf.py') -config_all = {} + +config_all_accel = {} config_all_devices = {} -config_all_disas = {} config_devices_mak_list = [] config_devices_h = {} config_target_h = {} @@ -2330,7 +2962,6 @@ config_target_mak = {} disassemblers = { 'alpha' : ['CONFIG_ALPHA_DIS'], - 'arm' : ['CONFIG_ARM_DIS'], 'avr' : ['CONFIG_AVR_DIS'], 'cris' : ['CONFIG_CRIS_DIS'], 'hexagon' : ['CONFIG_HEXAGON_DIS'], @@ -2352,18 +2983,12 @@ disassemblers = { 'xtensa' : ['CONFIG_XTENSA_DIS'], 'loongarch' : ['CONFIG_LOONGARCH_DIS'], } -if link_language == 'cpp' - disassemblers += { - 'aarch64' : [ 'CONFIG_ARM_A64_DIS'], - 'arm' : [ 'CONFIG_ARM_DIS', 'CONFIG_ARM_A64_DIS'], - 'mips' : [ 'CONFIG_MIPS_DIS', 'CONFIG_NANOMIPS_DIS'], - } -endif have_ivshmem = config_host_data.get('CONFIG_EVENTFD') host_kconfig = \ (get_option('fuzzing') ? ['CONFIG_FUZZ=y'] : []) + \ (have_tpm ? ['CONFIG_TPM=y'] : []) + \ + (pixman.found() ? ['CONFIG_PIXMAN=y'] : []) + \ (spice.found() ? ['CONFIG_SPICE=y'] : []) + \ (have_ivshmem ? ['CONFIG_IVSHMEM=y'] : []) + \ (opengl.found() ? ['CONFIG_OPENGL=y'] : []) + \ @@ -2372,9 +2997,11 @@ host_kconfig = \ (have_vhost_vdpa ? ['CONFIG_VHOST_VDPA=y'] : []) + \ (have_vhost_kernel ? ['CONFIG_VHOST_KERNEL=y'] : []) + \ (have_virtfs ? ['CONFIG_VIRTFS=y'] : []) + \ - ('CONFIG_LINUX' in config_host ? ['CONFIG_LINUX=y'] : []) + \ + (host_os == 'linux' ? ['CONFIG_LINUX=y'] : []) + \ (have_pvrdma ? ['CONFIG_PVRDMA=y'] : []) + \ - (multiprocess_allowed ? ['CONFIG_MULTIPROCESS_ALLOWED=y'] : []) + (multiprocess_allowed ? ['CONFIG_MULTIPROCESS_ALLOWED=y'] : []) + \ + (vfio_user_server_allowed ? ['CONFIG_VFIO_USER_SERVER_ALLOWED=y'] : []) + \ + (hv_balloon ? ['CONFIG_HV_BALLOON_POSSIBLE=y'] : []) ignored = [ 'TARGET_XML_FILES', 'TARGET_ABI_DIR', 'TARGET_ARCH' ] @@ -2384,7 +3011,7 @@ fdt_required = [] foreach target : target_dirs config_target = { 'TARGET_NAME': target.split('-')[0] } if target.endswith('linux-user') - if targetos != 'linux' + if host_os != 'linux' if default_targets continue endif @@ -2392,7 +3019,7 @@ foreach target : target_dirs endif config_target += { 'CONFIG_LINUX_USER': 'y' } elif target.endswith('bsd-user') - if 'CONFIG_BSD' not in config_host + if host_os not in bsd_oses if default_targets continue endif @@ -2400,6 +3027,7 @@ foreach target : target_dirs endif config_target += { 'CONFIG_BSD_USER': 'y' } elif target.endswith('softmmu') + config_target += { 'CONFIG_SYSTEM_ONLY': 'y' } config_target += { 'CONFIG_SOFTMMU': 'y' } endif if target.endswith('-user') @@ -2414,10 +3042,7 @@ foreach target : target_dirs foreach sym: accelerators if sym == 'CONFIG_TCG' or target in accelerator_targets.get(sym, []) config_target += { sym: 'y' } - config_all += { sym: 'y' } - if sym == 'CONFIG_TCG' and tcg_arch == 'tci' - config_target += { 'CONFIG_TCG_INTERPRETER': 'y' } - endif + config_all_accel += { sym: 'y' } if target in modular_tcg config_target += { 'CONFIG_TCG_MODULAR': 'y' } else @@ -2456,7 +3081,6 @@ foreach target : target_dirs if host_arch.startswith(k) or config_target['TARGET_BASE_ARCH'].startswith(k) foreach sym: v config_target += { sym: 'y' } - config_all_disas += { sym: 'y' } endforeach endif endforeach @@ -2515,25 +3139,6 @@ foreach target : target_dirs endforeach target_dirs = actual_target_dirs -# This configuration is used to build files that are shared by -# multiple binaries, and then extracted out of the "common" -# static_library target. -# -# We do not use all_sources()/all_dependencies(), because it would -# build literally all source files, including devices only used by -# targets that are not built for this compilation. The CONFIG_ALL -# pseudo symbol replaces it. - -config_all += config_all_devices -config_all += config_host -config_all += config_all_disas -config_all += { - 'CONFIG_XEN': xen.found(), - 'CONFIG_SOFTMMU': have_system, - 'CONFIG_USER_ONLY': have_user, - 'CONFIG_ALL': true, -} - target_configs_h = [] foreach target: target_dirs target_configs_h += config_target_h[target] @@ -2546,134 +3151,28 @@ genh += custom_target('config-poison.h', command: [find_program('scripts/make-config-poison.sh'), target_configs_h]) -############## -# Submodules # -############## +############### +# Subprojects # +############### -capstone = not_found -if not get_option('capstone').auto() or have_system or have_user - capstone = dependency('capstone', version: '>=3.0.5', - kwargs: static_kwargs, method: 'pkg-config', - required: get_option('capstone')) - - # Some versions of capstone have broken pkg-config file - # that reports a wrong -I path, causing the #include to - # fail later. If the system has such a broken version - # do not use it. - if capstone.found() and not cc.compiles('#include ', - dependencies: [capstone]) - capstone = not_found - if get_option('capstone').enabled() - error('capstone requested, but it does not appear to work') - endif - endif -endif - -slirp = not_found -slirp_opt = 'disabled' -if have_system - slirp_opt = get_option('slirp') - if slirp_opt in ['enabled', 'auto', 'system'] - have_internal = fs.exists(meson.current_source_dir() / 'slirp/meson.build') - slirp_dep_required = (slirp_opt == 'system' or - slirp_opt == 'enabled' and not have_internal) - slirp = dependency('slirp', kwargs: static_kwargs, - method: 'pkg-config', version: '>=4.1.0', - required: slirp_dep_required) - # slirp <4.7 is incompatible with CFI support in QEMU. This is because - # it passes function pointers within libslirp as callbacks for timers. - # When using a system-wide shared libslirp, the type information for the - # callback is missing and the timer call produces a false positive with CFI. - # Do not use the "version" keyword argument to produce a better error. - # with control-flow integrity. - if get_option('cfi') and slirp.found() and slirp.version().version_compare('<4.7') - if slirp_dep_required - error('Control-Flow Integrity requires libslirp 4.7.') - else - warning('Control-Flow Integrity requires libslirp 4.7, not using system-wide libslirp.') - slirp = not_found - endif - endif - if slirp.found() - slirp_opt = 'system' - elif have_internal - slirp_opt = 'internal' - else - slirp_opt = 'disabled' - endif - endif - if slirp_opt == 'internal' - slirp_deps = [] - if targetos == 'windows' - slirp_deps = cc.find_library('iphlpapi') - elif targetos == 'darwin' - slirp_deps = cc.find_library('resolv') - endif - slirp_conf = configuration_data() - slirp_conf.set('SLIRP_MAJOR_VERSION', meson.project_version().split('.')[0]) - slirp_conf.set('SLIRP_MINOR_VERSION', meson.project_version().split('.')[1]) - slirp_conf.set('SLIRP_MICRO_VERSION', meson.project_version().split('.')[2]) - slirp_conf.set_quoted('SLIRP_VERSION_STRING', meson.project_version()) - slirp_cargs = ['-DG_LOG_DOMAIN="Slirp"'] - slirp_files = [ - 'slirp/src/arp_table.c', - 'slirp/src/bootp.c', - 'slirp/src/cksum.c', - 'slirp/src/dhcpv6.c', - 'slirp/src/dnssearch.c', - 'slirp/src/if.c', - 'slirp/src/ip6_icmp.c', - 'slirp/src/ip6_input.c', - 'slirp/src/ip6_output.c', - 'slirp/src/ip_icmp.c', - 'slirp/src/ip_input.c', - 'slirp/src/ip_output.c', - 'slirp/src/mbuf.c', - 'slirp/src/misc.c', - 'slirp/src/ncsi.c', - 'slirp/src/ndp_table.c', - 'slirp/src/sbuf.c', - 'slirp/src/slirp.c', - 'slirp/src/socket.c', - 'slirp/src/state.c', - 'slirp/src/stream.c', - 'slirp/src/tcp_input.c', - 'slirp/src/tcp_output.c', - 'slirp/src/tcp_subr.c', - 'slirp/src/tcp_timer.c', - 'slirp/src/tftp.c', - 'slirp/src/udp.c', - 'slirp/src/udp6.c', - 'slirp/src/util.c', - 'slirp/src/version.c', - 'slirp/src/vmstate.c', - ] - - configure_file( - input : 'slirp/src/libslirp-version.h.in', - output : 'libslirp-version.h', - configuration: slirp_conf) - - slirp_inc = include_directories('slirp', 'slirp/src') - libslirp = static_library('slirp', - build_by_default: false, - sources: slirp_files, - c_args: slirp_cargs, - include_directories: slirp_inc) - slirp = declare_dependency(link_with: libslirp, - dependencies: slirp_deps, - include_directories: slirp_inc) - endif +libvfio_user_dep = not_found +if have_system and vfio_user_server_allowed + libvfio_user_proj = subproject('libvfio-user', required: true) + libvfio_user_dep = libvfio_user_proj.get_variable('libvfio_user_dep') endif fdt = not_found -if have_system - fdt_opt = get_option('fdt') +fdt_opt = get_option('fdt') +if fdt_required.length() > 0 or fdt_opt == 'enabled' + if fdt_opt == 'disabled' + error('fdt disabled but required by targets ' + ', '.join(fdt_required)) + endif + if fdt_opt in ['enabled', 'auto', 'system'] - have_internal = fs.exists(meson.current_source_dir() / 'dtc/libfdt/Makefile.libfdt') - fdt = cc.find_library('fdt', kwargs: static_kwargs, - required: fdt_opt == 'system' or - fdt_opt == 'enabled' and not have_internal) + if get_option('wrap_mode') == 'nodownload' + fdt_opt = 'system' + endif + fdt = cc.find_library('fdt', required: fdt_opt == 'system') if fdt.found() and cc.links(''' #include #include @@ -2682,45 +3181,35 @@ if have_system fdt_opt = 'system' elif fdt_opt == 'system' error('system libfdt requested, but it is too old (1.5.1 or newer required)') - elif have_internal - fdt_opt = 'internal' else - fdt_opt = 'disabled' + fdt_opt = 'internal' fdt = not_found endif endif - if fdt_opt == 'internal' - fdt_files = files( - 'dtc/libfdt/fdt.c', - 'dtc/libfdt/fdt_ro.c', - 'dtc/libfdt/fdt_wip.c', - 'dtc/libfdt/fdt_sw.c', - 'dtc/libfdt/fdt_rw.c', - 'dtc/libfdt/fdt_strerror.c', - 'dtc/libfdt/fdt_empty_tree.c', - 'dtc/libfdt/fdt_addresses.c', - 'dtc/libfdt/fdt_overlay.c', - 'dtc/libfdt/fdt_check.c', - ) - - fdt_inc = include_directories('dtc/libfdt') - libfdt = static_library('fdt', - build_by_default: false, - sources: fdt_files, - include_directories: fdt_inc) - fdt = declare_dependency(link_with: libfdt, - include_directories: fdt_inc) + if not fdt.found() + assert(fdt_opt == 'internal') + libfdt_proj = subproject('dtc', required: true, + default_options: ['tools=false', 'yaml=disabled', + 'python=disabled', 'default_library=static']) + fdt = libfdt_proj.get_variable('libfdt_dep') endif else fdt_opt = 'disabled' endif -if not fdt.found() and fdt_required.length() > 0 - error('fdt not available but required by targets ' + ', '.join(fdt_required)) + +config_host_data.set('CONFIG_FDT', fdt.found()) + +vhost_user = not_found +if host_os == 'linux' and have_vhost_user + libvhost_user = subproject('libvhost-user') + vhost_user = libvhost_user.get_variable('vhost_user_dep') endif -config_host_data.set('CONFIG_CAPSTONE', capstone.found()) -config_host_data.set('CONFIG_FDT', fdt.found()) -config_host_data.set('CONFIG_SLIRP', slirp.found()) +libvduse = not_found +if have_libvduse + libvduse_proj = subproject('libvduse') + libvduse = libvduse_proj.get_variable('libvduse_dep') +endif ##################### # Generated sources # @@ -2729,7 +3218,7 @@ config_host_data.set('CONFIG_SLIRP', slirp.found()) genh += configure_file(output: 'config-host.h', configuration: config_host_data) hxtool = find_program('scripts/hxtool') -shaderinclude = find_program('scripts/shaderinclude.pl') +shaderinclude = find_program('scripts/shaderinclude.py') qapi_gen = find_program('scripts/qapi-gen.py') qapi_gen_depends = [ meson.current_source_dir() / 'scripts/qapi/__init__.py', meson.current_source_dir() / 'scripts/qapi/commands.py', @@ -2739,12 +3228,12 @@ qapi_gen_depends = [ meson.current_source_dir() / 'scripts/qapi/__init__.py', meson.current_source_dir() / 'scripts/qapi/expr.py', meson.current_source_dir() / 'scripts/qapi/gen.py', meson.current_source_dir() / 'scripts/qapi/introspect.py', + meson.current_source_dir() / 'scripts/qapi/main.py', meson.current_source_dir() / 'scripts/qapi/parser.py', meson.current_source_dir() / 'scripts/qapi/schema.py', meson.current_source_dir() / 'scripts/qapi/source.py', meson.current_source_dir() / 'scripts/qapi/types.py', meson.current_source_dir() / 'scripts/qapi/visit.py', - meson.current_source_dir() / 'scripts/qapi/common.py', meson.current_source_dir() / 'scripts/qapi-gen.py' ] @@ -2770,7 +3259,6 @@ tracetool_depends = files( 'scripts/tracetool/format/log_stap.py', 'scripts/tracetool/format/stap.py', 'scripts/tracetool/__init__.py', - 'scripts/tracetool/transform.py', 'scripts/tracetool/vcpu.py' ) @@ -2801,44 +3289,10 @@ foreach d : hx_headers input: files(d[0]), output: d[1], capture: true, - build_by_default: true, # to be removed when added to a target command: [hxtool, '-h', '@INPUT0@']) endforeach genh += hxdep -################### -# Collect sources # -################### - -authz_ss = ss.source_set() -blockdev_ss = ss.source_set() -block_ss = ss.source_set() -chardev_ss = ss.source_set() -common_ss = ss.source_set() -crypto_ss = ss.source_set() -hwcore_ss = ss.source_set() -io_ss = ss.source_set() -qmp_ss = ss.source_set() -qom_ss = ss.source_set() -softmmu_ss = ss.source_set() -specific_fuzz_ss = ss.source_set() -specific_ss = ss.source_set() -stub_ss = ss.source_set() -trace_ss = ss.source_set() -user_ss = ss.source_set() -util_ss = ss.source_set() - -# accel modules -qtest_module_ss = ss.source_set() -tcg_module_ss = ss.source_set() - -modules = {} -target_modules = {} -hw_arch = {} -target_arch = {} -target_softmmu_arch = {} -target_user_arch = {} - ############### # Trace files # ############### @@ -2851,6 +3305,7 @@ trace_events_subdirs = [ 'qom', 'monitor', 'util', + 'gdbstub', ] if have_linux_user trace_events_subdirs += [ 'linux-user' ] @@ -2882,14 +3337,15 @@ if have_system 'hw/arm', 'hw/audio', 'hw/block', - 'hw/block/dataplane', 'hw/char', 'hw/display', 'hw/dma', + 'hw/fsi', 'hw/hyperv', 'hw/i2c', 'hw/i386', 'hw/i386/xen', + 'hw/i386/kvm', 'hw/ide', 'hw/input', 'hw/intc', @@ -2918,6 +3374,7 @@ if have_system 'hw/ssi', 'hw/timer', 'hw/tpm', + 'hw/ufs', 'hw/usb', 'hw/vfio', 'hw/virtio', @@ -2926,7 +3383,7 @@ if have_system 'hw/gpio', 'migration', 'net', - 'softmmu', + 'system', 'ui', 'hw/remote', ] @@ -2940,6 +3397,7 @@ if have_system or have_user 'target/hppa', 'target/i386', 'target/i386/kvm', + 'target/loongarch', 'target/mips/tcg', 'target/nios2', 'target/ppc', @@ -2950,11 +3408,38 @@ if have_system or have_user ] endif -vhost_user = not_found -if targetos == 'linux' and have_vhost_user - libvhost_user = subproject('libvhost-user') - vhost_user = libvhost_user.get_variable('vhost_user_dep') -endif +################### +# Collect sources # +################### + +authz_ss = ss.source_set() +blockdev_ss = ss.source_set() +block_ss = ss.source_set() +chardev_ss = ss.source_set() +common_ss = ss.source_set() +crypto_ss = ss.source_set() +hwcore_ss = ss.source_set() +io_ss = ss.source_set() +qmp_ss = ss.source_set() +qom_ss = ss.source_set() +system_ss = ss.source_set() +specific_fuzz_ss = ss.source_set() +specific_ss = ss.source_set() +stub_ss = ss.source_set() +trace_ss = ss.source_set() +user_ss = ss.source_set() +util_ss = ss.source_set() + +# accel modules +qtest_module_ss = ss.source_set() +tcg_module_ss = ss.source_set() + +modules = {} +target_modules = {} +hw_arch = {} +target_arch = {} +target_system_arch = {} +target_user_arch = {} # NOTE: the trace/ subdirectory needs the qapi_trace_events variable # that is filled in by qapi/. @@ -2968,30 +3453,34 @@ subdir('authz') subdir('crypto') subdir('ui') subdir('hw') - +subdir('gdbstub') if enable_modules libmodulecommon = static_library('module-common', files('module-common.c') + genh, pic: true, c_args: '-DBUILD_DSO') modulecommon = declare_dependency(link_whole: libmodulecommon, compile_args: '-DBUILD_DSO') endif -qom_ss = qom_ss.apply(config_host, strict: false) +qom_ss = qom_ss.apply({}) libqom = static_library('qom', qom_ss.sources() + genh, dependencies: [qom_ss.dependencies()], - name_suffix: 'fa') + name_suffix: 'fa', + build_by_default: false) qom = declare_dependency(link_whole: libqom) event_loop_base = files('event-loop-base.c') -event_loop_base = static_library('event-loop-base', sources: event_loop_base + genh, - build_by_default: true) +event_loop_base = static_library('event-loop-base', + sources: event_loop_base + genh, + name_suffix: 'fa', + build_by_default: false) event_loop_base = declare_dependency(link_whole: event_loop_base, dependencies: [qom]) -stub_ss = stub_ss.apply(config_all, strict: false) +stub_ss = stub_ss.apply({}) util_ss.add_all(trace_ss) -util_ss = util_ss.apply(config_all, strict: false) +util_ss = util_ss.apply({}) libqemuutil = static_library('qemuutil', + build_by_default: false, sources: util_ss.sources() + stub_ss.sources() + genh, dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc, pixman]) qemuutil = declare_dependency(link_with: libqemuutil, @@ -3036,16 +3525,17 @@ if have_block # os-posix.c contains POSIX-specific functions used by qemu-storage-daemon, # os-win32.c does not - blockdev_ss.add(when: 'CONFIG_POSIX', if_true: files('os-posix.c')) - softmmu_ss.add(when: 'CONFIG_WIN32', if_true: [files('os-win32.c')]) + if host_os == 'windows' + system_ss.add(files('os-win32.c')) + else + blockdev_ss.add(files('os-posix.c')) + endif endif -common_ss.add(files('cpus-common.c')) +common_ss.add(files('cpu-common.c')) +specific_ss.add(files('cpu-target.c')) -subdir('softmmu') - -common_ss.add(capstone) -specific_ss.add(files('cpu.c', 'disas.c', 'gdbstub.c'), capstone) +subdir('system') # Work around a gcc bug/misfeature wherein constant propagation looks # through an alias: @@ -3065,7 +3555,7 @@ if get_option('b_lto') pagevary = declare_dependency(link_with: pagevary) endif common_ss.add(pagevary) -specific_ss.add(files('page-vary.c')) +specific_ss.add(files('page-vary-target.c')) subdir('backends') subdir('disas') @@ -3074,6 +3564,7 @@ subdir('monitor') subdir('net') subdir('replay') subdir('semihosting') +subdir('stats') subdir('tcg') subdir('fpu') subdir('accel') @@ -3097,26 +3588,30 @@ specific_ss.add_all(when: 'CONFIG_TCG_BUILTIN', if_true: tcg_module_ss) target_modules += { 'accel' : { 'qtest': qtest_module_ss, 'tcg': tcg_real_module_ss }} -######################## -# Library dependencies # -######################## +############################################## +# Internal static_libraries and dependencies # +############################################## modinfo_collect = find_program('scripts/modinfo-collect.py') modinfo_generate = find_program('scripts/modinfo-generate.py') modinfo_files = [] block_mods = [] -softmmu_mods = [] +system_mods = [] foreach d, list : modules + if not (d == 'block' ? have_block : have_system) + continue + endif + foreach m, module_ss : list - if enable_modules and targetos != 'windows' - module_ss = module_ss.apply(config_all, strict: false) + if enable_modules + module_ss = module_ss.apply(config_all_devices, strict: false) sl = static_library(d + '-' + m, [genh, module_ss.sources()], dependencies: [modulecommon, module_ss.dependencies()], pic: true) if d == 'block' block_mods += sl else - softmmu_mods += sl + system_mods += sl endif if module_ss.sources() != [] # FIXME: Should use sl.extract_all_objects(recursive: true) as @@ -3134,7 +3629,7 @@ foreach d, list : modules if d == 'block' block_ss.add_all(module_ss) else - softmmu_ss.add_all(module_ss) + system_ss.add_all(module_ss) endif endif endforeach @@ -3142,11 +3637,10 @@ endforeach foreach d, list : target_modules foreach m, module_ss : list - if enable_modules and targetos != 'windows' + if enable_modules foreach target : target_dirs if target.endswith('-softmmu') config_target = config_target_mak[target] - config_target += config_host target_inc = [include_directories('target' / config_target['TARGET_BASE_ARCH'])] c_args = ['-DNEED_CPU_H', '-DCONFIG_TARGET="@0@-config-target.h"'.format(target), @@ -3160,7 +3654,7 @@ foreach d, list : target_modules include_directories: target_inc, c_args: c_args, pic: true) - softmmu_mods += sl + system_mods += sl # FIXME: Should use sl.extract_all_objects(recursive: true) too. modinfo_files += custom_target(module_name + '.modinfo', output: module_name + '.modinfo', @@ -3203,11 +3697,11 @@ block_syms = custom_target('block.syms', output: 'block.syms', capture: true, command: [undefsym, nm, '@INPUT@']) qemu_syms = custom_target('qemu.syms', output: 'qemu.syms', - input: [libqemuutil, softmmu_mods], + input: [libqemuutil, system_mods], capture: true, command: [undefsym, nm, '@INPUT@']) -authz_ss = authz_ss.apply(config_host, strict: false) +authz_ss = authz_ss.apply({}) libauthz = static_library('authz', authz_ss.sources() + genh, dependencies: [authz_ss.dependencies()], name_suffix: 'fa', @@ -3216,7 +3710,7 @@ libauthz = static_library('authz', authz_ss.sources() + genh, authz = declare_dependency(link_whole: libauthz, dependencies: qom) -crypto_ss = crypto_ss.apply(config_host, strict: false) +crypto_ss = crypto_ss.apply({}) libcrypto = static_library('crypto', crypto_ss.sources() + genh, dependencies: [crypto_ss.dependencies()], name_suffix: 'fa', @@ -3225,7 +3719,7 @@ libcrypto = static_library('crypto', crypto_ss.sources() + genh, crypto = declare_dependency(link_whole: libcrypto, dependencies: [authz, qom]) -io_ss = io_ss.apply(config_host, strict: false) +io_ss = io_ss.apply({}) libio = static_library('io', io_ss.sources() + genh, dependencies: [io_ss.dependencies()], link_with: libqemuutil, @@ -3239,9 +3733,9 @@ libmigration = static_library('migration', sources: migration_files + genh, build_by_default: false) migration = declare_dependency(link_with: libmigration, dependencies: [zlib, qom, io]) -softmmu_ss.add(migration) +system_ss.add(migration) -block_ss = block_ss.apply(config_host, strict: false) +block_ss = block_ss.apply({}) libblock = static_library('block', block_ss.sources() + genh, dependencies: block_ss.dependencies(), link_depends: block_syms, @@ -3252,7 +3746,7 @@ block = declare_dependency(link_whole: [libblock], link_args: '@block.syms', dependencies: [crypto, io]) -blockdev_ss = blockdev_ss.apply(config_host, strict: false) +blockdev_ss = blockdev_ss.apply({}) libblockdev = static_library('blockdev', blockdev_ss.sources() + genh, dependencies: blockdev_ss.dependencies(), name_suffix: 'fa', @@ -3261,7 +3755,7 @@ libblockdev = static_library('blockdev', blockdev_ss.sources() + genh, blockdev = declare_dependency(link_whole: [libblockdev], dependencies: [block, event_loop_base]) -qmp_ss = qmp_ss.apply(config_host, strict: false) +qmp_ss = qmp_ss.apply({}) libqmp = static_library('qmp', qmp_ss.sources() + genh, dependencies: qmp_ss.dependencies(), name_suffix: 'fa', @@ -3276,7 +3770,7 @@ libchardev = static_library('chardev', chardev_ss.sources() + genh, chardev = declare_dependency(link_whole: libchardev) -hwcore_ss = hwcore_ss.apply(config_host, strict: false) +hwcore_ss = hwcore_ss.apply({}) libhwcore = static_library('hwcore', sources: hwcore_ss.sources() + genh, name_suffix: 'fa', build_by_default: false) @@ -3288,7 +3782,7 @@ common_ss.add(hwcore) ########### emulator_modules = [] -foreach m : block_mods + softmmu_mods +foreach m : block_mods + system_mods emulator_modules += shared_module(m.name(), build_by_default: true, name_prefix: '', @@ -3300,27 +3794,30 @@ if emulator_modules.length() > 0 alias_target('modules', emulator_modules) endif -softmmu_ss.add(authz, blockdev, chardev, crypto, io, qmp) +system_ss.add(authz, blockdev, chardev, crypto, io, qmp) common_ss.add(qom, qemuutil) -common_ss.add_all(when: 'CONFIG_SOFTMMU', if_true: [softmmu_ss]) +common_ss.add_all(when: 'CONFIG_SYSTEM_ONLY', if_true: [system_ss]) common_ss.add_all(when: 'CONFIG_USER_ONLY', if_true: user_ss) -common_all = common_ss.apply(config_all, strict: false) +# Note that this library is never used directly (only through extract_objects) +# and is not built by default; therefore, source files not used by the build +# configuration will be in build.ninja, but are never built by default. common_all = static_library('common', build_by_default: false, - sources: common_all.sources() + genh, + sources: common_ss.all_sources() + genh, include_directories: common_user_inc, implicit_include_directories: false, - dependencies: common_all.dependencies(), + dependencies: common_ss.all_dependencies(), name_suffix: 'fa') -feature_to_c = find_program('scripts/feature_to_c.sh') +feature_to_c = find_program('scripts/feature_to_c.py') -if targetos == 'darwin' +if host_os == 'darwin' entitlement = find_program('scripts/entitlement.sh') endif +traceable = [] emulators = {} foreach target : target_dirs config_target = config_target_mak[target] @@ -3333,22 +3830,22 @@ foreach target : target_dirs '-DCONFIG_DEVICES="@0@-config-devices.h"'.format(target)] link_args = emulator_link_args - config_target += config_host target_inc = [include_directories('target' / config_target['TARGET_BASE_ARCH'])] - if targetos == 'linux' + if host_os == 'linux' target_inc += include_directories('linux-headers', is_system: true) endif if target.endswith('-softmmu') - qemu_target_name = 'qemu-system-' + target_name target_type='system' - t = target_softmmu_arch[target_base_arch].apply(config_target, strict: false) + t = target_system_arch[target_base_arch].apply(config_target, strict: false) arch_srcs += t.sources() arch_deps += t.dependencies() hw_dir = target_name == 'sparc64' ? 'sparc64' : target_base_arch - hw = hw_arch[hw_dir].apply(config_target, strict: false) - arch_srcs += hw.sources() - arch_deps += hw.dependencies() + if hw_arch.has_key(hw_dir) + hw = hw_arch[hw_dir].apply(config_target, strict: false) + arch_srcs += hw.sources() + arch_deps += hw.dependencies() + endif arch_srcs += config_devices_h[target] link_args += ['@block.syms', '@qemu.syms'] @@ -3356,7 +3853,6 @@ foreach target : target_dirs abi = config_target['TARGET_ABI_DIR'] target_type='user' target_inc += common_user_inc - qemu_target_name = 'qemu-' + target_name if target_base_arch in target_user_arch t = target_user_arch[target_base_arch].apply(config_target, strict: false) arch_srcs += t.sources() @@ -3367,7 +3863,7 @@ foreach target : target_dirs endif if 'CONFIG_BSD_USER' in config_target base_dir = 'bsd-user' - target_inc += include_directories('bsd-user/' / targetos) + target_inc += include_directories('bsd-user/' / host_os) target_inc += include_directories('bsd-user/host/' / host_arch) dir = base_dir / abi arch_srcs += files(dir / 'signal.c', dir / 'target_arch_cpu.c') @@ -3421,14 +3917,14 @@ foreach target : target_dirs execs = [{ 'name': 'qemu-system-' + target_name, 'win_subsystem': 'console', - 'sources': files('softmmu/main.c'), + 'sources': files('system/main.c'), 'dependencies': [] }] - if targetos == 'windows' and (sdl.found() or gtk.found()) + if host_os == 'windows' and (sdl.found() or gtk.found()) execs += [{ 'name': 'qemu-system-' + target_name + 'w', 'win_subsystem': 'windows', - 'sources': files('softmmu/main.c'), + 'sources': files('system/main.c'), 'dependencies': [] }] endif @@ -3451,7 +3947,7 @@ foreach target : target_dirs endif foreach exe: execs exe_name = exe['name'] - if targetos == 'darwin' + if host_os == 'darwin' exe_name += '-unsigned' endif @@ -3460,12 +3956,11 @@ foreach target : target_dirs c_args: c_args, dependencies: arch_deps + deps + exe['dependencies'], objects: lib.extract_all_objects(recursive: true), - link_language: link_language, - link_depends: [block_syms, qemu_syms] + exe.get('link_depends', []), + link_depends: [block_syms, qemu_syms], link_args: link_args, win_subsystem: exe['win_subsystem']) - if targetos == 'darwin' + if host_os == 'darwin' icon = 'pc-bios/qemu.rsrc' build_input = [emulator, files(icon)] install_input = [ @@ -3491,36 +3986,23 @@ foreach target : target_dirs emulators += {exe['name']: emulator} endif - if stap.found() - foreach stp: [ - {'ext': '.stp-build', 'fmt': 'stap', 'bin': meson.current_build_dir() / exe['name'], 'install': false}, - {'ext': '.stp', 'fmt': 'stap', 'bin': get_option('prefix') / get_option('bindir') / exe['name'], 'install': true}, - {'ext': '-simpletrace.stp', 'fmt': 'simpletrace-stap', 'bin': '', 'install': true}, - {'ext': '-log.stp', 'fmt': 'log-stap', 'bin': '', 'install': true}, - ] - custom_target(exe['name'] + stp['ext'], - input: trace_events_all, - output: exe['name'] + stp['ext'], - install: stp['install'], - install_dir: get_option('datadir') / 'systemtap/tapset', - command: [ - tracetool, '--group=all', '--format=' + stp['fmt'], - '--binary=' + stp['bin'], - '--target-name=' + target_name, - '--target-type=' + target_type, - '--probe-prefix=qemu.' + target_type + '.' + target_name, - '@INPUT@', '@OUTPUT@' - ], - depend_files: tracetool_depends) - endforeach - endif + traceable += [{ + 'exe': exe['name'], + 'probe-prefix': 'qemu.' + target_type + '.' + target_name, + }] + endforeach endforeach # Other build targets -if 'CONFIG_PLUGIN' in config_host +if get_option('plugins') install_headers('include/qemu/qemu-plugin.h') + if host_os == 'windows' + # On windows, we want to deliver the qemu_plugin_api.lib file in the qemu installer, + # so that plugin authors can compile against it. + install_data(win32_qemu_plugin_api_lib, install_dir: 'lib') + endif endif subdir('qga') @@ -3543,6 +4025,14 @@ if have_tools install: true) subdir('storage-daemon') + + foreach exe: [ 'qemu-img', 'qemu-io', 'qemu-nbd', 'qemu-storage-daemon'] + traceable += [{ + 'exe': exe, + 'probe-prefix': 'qemu.' + exe.substring(5).replace('-', '_') + }] + endforeach + subdir('contrib/rdmacm-mux') subdir('contrib/elf2dmp') @@ -3557,7 +4047,7 @@ if have_tools subdir('contrib/vhost-user-scsi') endif - if targetos == 'linux' + if host_os == 'linux' executable('qemu-bridge-helper', files('qemu-bridge-helper.c'), dependencies: [qemuutil, libcap_ng], install: true, @@ -3575,6 +4065,32 @@ if have_tools endif endif +if stap.found() + foreach t: traceable + foreach stp: [ + {'ext': '.stp-build', 'fmt': 'stap', 'bin': meson.current_build_dir() / t['exe'], 'install': false}, + {'ext': '.stp', 'fmt': 'stap', 'bin': get_option('prefix') / get_option('bindir') / t['exe'], 'install': true}, + {'ext': '-simpletrace.stp', 'fmt': 'simpletrace-stap', 'bin': '', 'install': true}, + {'ext': '-log.stp', 'fmt': 'log-stap', 'bin': '', 'install': true}, + ] + cmd = [ + tracetool, '--group=all', '--format=' + stp['fmt'], + '--binary=' + stp['bin'], + '--probe-prefix=' + t['probe-prefix'], + '@INPUT@', '@OUTPUT@' + ] + + custom_target(t['exe'] + stp['ext'], + input: trace_events_all, + output: t['exe'] + stp['ext'], + install: stp['install'], + install_dir: get_option('datadir') / 'systemtap/tapset', + command: cmd, + depend_files: tracetool_depends) + endforeach + endforeach +endif + subdir('scripts') subdir('tools') subdir('pc-bios') @@ -3590,6 +4106,7 @@ if host_machine.system() == 'windows' '@OUTPUT@', get_option('prefix'), meson.current_source_dir(), + glib_pc.get_variable('bindir'), host_machine.cpu(), '--', '-DDISPLAYVERSION=' + meson.project_version(), @@ -3613,44 +4130,51 @@ endif # Configuration summary # ######################### -# Directories +# Build environment summary_info = {} +summary_info += {'Build directory': meson.current_build_dir()} +summary_info += {'Source path': meson.current_source_dir()} +summary_info += {'Download dependencies': get_option('wrap_mode') != 'nodownload'} +summary(summary_info, bool_yn: true, section: 'Build environment') + +# Directories summary_info += {'Install prefix': get_option('prefix')} summary_info += {'BIOS directory': qemu_datadir} -summary_info += {'firmware path': get_option('prefix') / get_option('qemu_firmwarepath')} +pathsep = host_os == 'windows' ? ';' : ':' +summary_info += {'firmware path': pathsep.join(get_option('qemu_firmwarepath'))} summary_info += {'binary directory': get_option('prefix') / get_option('bindir')} summary_info += {'library directory': get_option('prefix') / get_option('libdir')} summary_info += {'module directory': qemu_moddir} summary_info += {'libexec directory': get_option('prefix') / get_option('libexecdir')} summary_info += {'include directory': get_option('prefix') / get_option('includedir')} summary_info += {'config directory': get_option('prefix') / get_option('sysconfdir')} -if targetos != 'windows' +if host_os != 'windows' summary_info += {'local state directory': get_option('prefix') / get_option('localstatedir')} summary_info += {'Manual directory': get_option('prefix') / get_option('mandir')} else summary_info += {'local state directory': 'queried at runtime'} endif summary_info += {'Doc directory': get_option('prefix') / get_option('docdir')} -summary_info += {'Build directory': meson.current_build_dir()} -summary_info += {'Source path': meson.current_source_dir()} -summary_info += {'GIT submodules': config_host['GIT_SUBMODULES']} summary(summary_info, bool_yn: true, section: 'Directories') # Host binaries summary_info = {} -summary_info += {'git': config_host['GIT']} -summary_info += {'make': config_host['MAKE']} summary_info += {'python': '@0@ (version: @1@)'.format(python.full_path(), python.language_version())} summary_info += {'sphinx-build': sphinx_build} -if config_host.has_key('HAVE_GDB_BIN') - summary_info += {'gdb': config_host['HAVE_GDB_BIN']} + +# FIXME: the [binaries] section of machine files, which can be probed +# with find_program(), would be great for passing gdb and genisoimage +# paths from configure to Meson. However, there seems to be no way to +# hide a program (for example if gdb is too old). +if config_host.has_key('GDB') + summary_info += {'gdb': config_host['GDB']} endif summary_info += {'iasl': iasl} summary_info += {'genisoimage': config_host['GENISOIMAGE']} -if targetos == 'windows' and have_ga +if host_os == 'windows' and have_ga summary_info += {'wixl': wixl} endif -if slirp_opt != 'disabled' and have_system +if slirp.found() and have_system summary_info += {'smbd': have_slirp_smbd ? smbd_path : false} endif summary(summary_info, bool_yn: true, section: 'Host binaries') @@ -3662,8 +4186,8 @@ summary_info += {'system-mode emulation': have_system} summary_info += {'user-mode emulation': have_user} summary_info += {'block layer': have_block} summary_info += {'Install blobs': get_option('install_blobs')} -summary_info += {'module support': config_host.has_key('CONFIG_MODULES')} -if config_host.has_key('CONFIG_MODULES') +summary_info += {'module support': enable_modules} +if enable_modules summary_info += {'alternative module path': get_option('module_upgrades')} endif summary_info += {'fuzzing support': get_option('fuzzing')} @@ -3676,6 +4200,7 @@ if 'simple' in get_option('trace_backends') endif summary_info += {'D-Bus display': dbus_display} summary_info += {'QOM debugging': get_option('qom_cast_debug')} +summary_info += {'Relocatable install': get_option('relocatable')} summary_info += {'vhost-kernel support': have_vhost_kernel} summary_info += {'vhost-net support': have_vhost_net} summary_info += {'vhost-user support': have_vhost_user} @@ -3691,112 +4216,120 @@ summary_info += {'host CPU': cpu} summary_info += {'host endianness': build_machine.endian()} summary_info += {'C compiler': ' '.join(meson.get_compiler('c').cmd_array())} summary_info += {'Host C compiler': ' '.join(meson.get_compiler('c', native: true).cmd_array())} -if link_language == 'cpp' +if 'cpp' in all_languages summary_info += {'C++ compiler': ' '.join(meson.get_compiler('cpp').cmd_array())} else summary_info += {'C++ compiler': false} endif -if targetos == 'darwin' +if 'objc' in all_languages summary_info += {'Objective-C compiler': ' '.join(meson.get_compiler('objc').cmd_array())} +else + summary_info += {'Objective-C compiler': false} endif -summary_info += {'CFLAGS': ' '.join(get_option('c_args') - + ['-O' + get_option('optimization')] - + (get_option('debug') ? ['-g'] : []))} -if link_language == 'cpp' - summary_info += {'CXXFLAGS': ' '.join(get_option('cpp_args') - + ['-O' + get_option('optimization')] - + (get_option('debug') ? ['-g'] : []))} +option_cflags = (get_option('debug') ? ['-g'] : []) +if get_option('optimization') != 'plain' + option_cflags += ['-O' + get_option('optimization')] endif -if targetos == 'darwin' - summary_info += {'OBJCFLAGS': ' '.join(get_option('objc_args') - + ['-O' + get_option('optimization')] - + (get_option('debug') ? ['-g'] : []))} +summary_info += {'CFLAGS': ' '.join(get_option('c_args') + option_cflags)} +if 'cpp' in all_languages + summary_info += {'CXXFLAGS': ' '.join(get_option('cpp_args') + option_cflags)} endif -link_args = get_option(link_language + '_link_args') +if 'objc' in all_languages + summary_info += {'OBJCFLAGS': ' '.join(get_option('objc_args') + option_cflags)} +endif +link_args = get_option('c_link_args') if link_args.length() > 0 summary_info += {'LDFLAGS': ' '.join(link_args)} endif -summary_info += {'QEMU_CFLAGS': ' '.join(qemu_cflags)} -summary_info += {'QEMU_CXXFLAGS': ' '.join(qemu_cxxflags)} -summary_info += {'QEMU_OBJCFLAGS': ' '.join(qemu_objcflags)} +summary_info += {'QEMU_CFLAGS': ' '.join(qemu_common_flags + qemu_cflags)} +if 'cpp' in all_languages + summary_info += {'QEMU_CXXFLAGS': ' '.join(qemu_common_flags + qemu_cxxflags)} +endif +if 'objc' in all_languages + summary_info += {'QEMU_OBJCFLAGS': ' '.join(qemu_common_flags)} +endif summary_info += {'QEMU_LDFLAGS': ' '.join(qemu_ldflags)} -summary_info += {'profiler': get_option('profiler')} summary_info += {'link-time optimization (LTO)': get_option('b_lto')} summary_info += {'PIE': get_option('b_pie')} -summary_info += {'static build': config_host.has_key('CONFIG_STATIC')} +summary_info += {'static build': get_option('prefer_static')} summary_info += {'malloc trim support': has_malloc_trim} summary_info += {'membarrier': have_membarrier} +summary_info += {'debug graph lock': get_option('debug_graph_lock')} summary_info += {'debug stack usage': get_option('debug_stack_usage')} summary_info += {'mutex debugging': get_option('debug_mutex')} summary_info += {'memory allocator': get_option('malloc')} summary_info += {'avx2 optimization': config_host_data.get('CONFIG_AVX2_OPT')} +summary_info += {'avx512bw optimization': config_host_data.get('CONFIG_AVX512BW_OPT')} summary_info += {'avx512f optimization': config_host_data.get('CONFIG_AVX512F_OPT')} -summary_info += {'gprof enabled': get_option('gprof')} summary_info += {'gcov': get_option('b_coverage')} -summary_info += {'thread sanitizer': config_host.has_key('CONFIG_TSAN')} +summary_info += {'thread sanitizer': get_option('tsan')} summary_info += {'CFI support': get_option('cfi')} if get_option('cfi') summary_info += {'CFI debug support': get_option('cfi_debug')} endif summary_info += {'strip binaries': get_option('strip')} summary_info += {'sparse': sparse} -summary_info += {'mingw32 support': targetos == 'windows'} +summary_info += {'mingw32 support': host_os == 'windows'} +summary(summary_info, bool_yn: true, section: 'Compilation') # snarf the cross-compilation information for tests +summary_info = {} +have_cross = false foreach target: target_dirs - tcg_mak = meson.current_build_dir() / 'tests/tcg' / 'config-' + target + '.mak' + tcg_mak = meson.current_build_dir() / 'tests/tcg' / target / 'config-target.mak' if fs.exists(tcg_mak) config_cross_tcg = keyval.load(tcg_mak) - target = config_cross_tcg['TARGET_NAME'] - compiler = '' if 'CC' in config_cross_tcg - summary_info += {target + ' tests': config_cross_tcg['CC']} + summary_info += {config_cross_tcg['TARGET_NAME']: config_cross_tcg['CC']} + have_cross = true endif - endif + endif endforeach - -summary(summary_info, bool_yn: true, section: 'Compilation') +if have_cross + summary(summary_info, bool_yn: true, section: 'Cross compilers') +endif # Targets and accelerators summary_info = {} if have_system - summary_info += {'KVM support': config_all.has_key('CONFIG_KVM')} - summary_info += {'HAX support': config_all.has_key('CONFIG_HAX')} - summary_info += {'HVF support': config_all.has_key('CONFIG_HVF')} - summary_info += {'WHPX support': config_all.has_key('CONFIG_WHPX')} - summary_info += {'NVMM support': config_all.has_key('CONFIG_NVMM')} + summary_info += {'KVM support': config_all_accel.has_key('CONFIG_KVM')} + summary_info += {'HVF support': config_all_accel.has_key('CONFIG_HVF')} + summary_info += {'WHPX support': config_all_accel.has_key('CONFIG_WHPX')} + summary_info += {'NVMM support': config_all_accel.has_key('CONFIG_NVMM')} summary_info += {'Xen support': xen.found()} if xen.found() summary_info += {'xen ctrl version': xen.version()} endif + summary_info += {'Xen emulation': config_all_devices.has_key('CONFIG_XEN_EMU')} endif -summary_info += {'TCG support': config_all.has_key('CONFIG_TCG')} -if config_all.has_key('CONFIG_TCG') +summary_info += {'TCG support': config_all_accel.has_key('CONFIG_TCG')} +if config_all_accel.has_key('CONFIG_TCG') if get_option('tcg_interpreter') summary_info += {'TCG backend': 'TCI (TCG with bytecode interpreter, slow)'} else summary_info += {'TCG backend': 'native (@0@)'.format(cpu)} endif - summary_info += {'TCG plugins': config_host.has_key('CONFIG_PLUGIN')} - summary_info += {'TCG debug enabled': config_host.has_key('CONFIG_DEBUG_TCG')} + summary_info += {'TCG plugins': get_option('plugins')} + summary_info += {'TCG debug enabled': get_option('debug_tcg')} endif summary_info += {'target list': ' '.join(target_dirs)} if have_system summary_info += {'default devices': get_option('default_devices')} summary_info += {'out of process emulation': multiprocess_allowed} + summary_info += {'vfio-user server': vfio_user_server_allowed} endif summary(summary_info, bool_yn: true, section: 'Targets and accelerators') # Block layer summary_info = {} -summary_info += {'coroutine backend': config_host['CONFIG_COROUTINE_BACKEND']} +summary_info += {'coroutine backend': coroutine_backend} summary_info += {'coroutine pool': have_coroutine_pool} if have_block summary_info += {'Block whitelist (rw)': get_option('block_drv_rw_whitelist')} summary_info += {'Block whitelist (ro)': get_option('block_drv_ro_whitelist')} summary_info += {'Use block whitelist in tools': get_option('block_drv_whitelist_in_tools')} - summary_info += {'VirtFS support': have_virtfs} - summary_info += {'build virtiofs daemon': have_virtiofsd} + summary_info += {'VirtFS (9P) support': have_virtfs} + summary_info += {'VirtFS (9P) Proxy Helper support (deprecated)': have_virtfs_proxy_helper} summary_info += {'Live block migration': config_host_data.get('CONFIG_LIVE_BLOCK_MIGRATION')} summary_info += {'replication support': config_host_data.get('CONFIG_REPLICATION')} summary_info += {'bochs support': get_option('bochs').allowed()} @@ -3804,10 +4337,14 @@ if have_block summary_info += {'dmg support': get_option('dmg').allowed()} summary_info += {'qcow v1 support': get_option('qcow1').allowed()} summary_info += {'vdi support': get_option('vdi').allowed()} + summary_info += {'vhdx support': get_option('vhdx').allowed()} + summary_info += {'vmdk support': get_option('vmdk').allowed()} + summary_info += {'vpc support': get_option('vpc').allowed()} summary_info += {'vvfat support': get_option('vvfat').allowed()} summary_info += {'qed support': get_option('qed').allowed()} summary_info += {'parallels support': get_option('parallels').allowed()} summary_info += {'FUSE exports': fuse} + summary_info += {'VDUSE block exports': have_vduse_blk_export} endif summary(summary_info, bool_yn: true, section: 'Block layer support') @@ -3823,52 +4360,81 @@ summary_info += {'nettle': nettle} if nettle.found() summary_info += {' XTS': xts != 'private'} endif +summary_info += {'SM4 ALG support': crypto_sm4} summary_info += {'AF_ALG support': have_afalg} summary_info += {'rng-none': get_option('rng_none')} summary_info += {'Linux keyring': have_keyring} +summary_info += {'Linux keyutils': keyutils} summary(summary_info, bool_yn: true, section: 'Crypto') -# Libraries +# UI summary_info = {} -if targetos == 'darwin' +if host_os == 'darwin' summary_info += {'Cocoa support': cocoa} - summary_info += {'vmnet.framework support': vmnet} endif summary_info += {'SDL support': sdl} summary_info += {'SDL image support': sdl_image} summary_info += {'GTK support': gtk} summary_info += {'pixman': pixman} summary_info += {'VTE support': vte} -summary_info += {'slirp support': slirp_opt == 'internal' ? slirp_opt : slirp} -summary_info += {'libtasn1': tasn1} -summary_info += {'PAM': pam} -summary_info += {'iconv support': iconv} -summary_info += {'curses support': curses} -summary_info += {'virgl support': virgl} -summary_info += {'curl support': curl} -summary_info += {'Multipath support': mpathpersist} summary_info += {'PNG support': png} summary_info += {'VNC support': vnc} if vnc.found() summary_info += {'VNC SASL support': sasl} summary_info += {'VNC JPEG support': jpeg} endif -if targetos not in ['darwin', 'haiku', 'windows'] +summary_info += {'spice protocol support': spice_protocol} +if spice_protocol.found() + summary_info += {' spice server support': spice} +endif +summary_info += {'curses support': curses} +summary_info += {'brlapi support': brlapi} +summary(summary_info, bool_yn: true, section: 'User interface') + +# Graphics backends +summary_info = {} +summary_info += {'VirGL support': virgl} +summary_info += {'Rutabaga support': rutabaga} +summary(summary_info, bool_yn: true, section: 'Graphics backends') + +# Audio backends +summary_info = {} +if host_os not in ['darwin', 'haiku', 'windows'] summary_info += {'OSS support': oss} -elif targetos == 'darwin' + summary_info += {'sndio support': sndio} +elif host_os == 'darwin' summary_info += {'CoreAudio support': coreaudio} -elif targetos == 'windows' +elif host_os == 'windows' summary_info += {'DirectSound support': dsound} endif -if targetos == 'linux' +if host_os == 'linux' summary_info += {'ALSA support': alsa} summary_info += {'PulseAudio support': pulse} endif +summary_info += {'PipeWire support': pipewire} summary_info += {'JACK support': jack} -summary_info += {'brlapi support': brlapi} +summary(summary_info, bool_yn: true, section: 'Audio backends') + +# Network backends +summary_info = {} +if host_os == 'darwin' + summary_info += {'vmnet.framework support': vmnet} +endif +summary_info += {'AF_XDP support': libxdp} +summary_info += {'slirp support': slirp} summary_info += {'vde support': vde} summary_info += {'netmap support': have_netmap} summary_info += {'l2tpv3 support': have_l2tpv3} +summary(summary_info, bool_yn: true, section: 'Network backends') + +# Libraries +summary_info = {} +summary_info += {'libtasn1': tasn1} +summary_info += {'PAM': pam} +summary_info += {'iconv support': iconv} +summary_info += {'blkio support': blkio} +summary_info += {'curl support': curl} +summary_info += {'Multipath support': mpathpersist} summary_info += {'Linux AIO support': libaio} summary_info += {'Linux io_uring support': linux_io_uring} summary_info += {'ATTR/XATTR support': libattr} @@ -3877,10 +4443,6 @@ summary_info += {'PVRDMA support': have_pvrdma} summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt} summary_info += {'libcap-ng support': libcap_ng} summary_info += {'bpf support': libbpf} -summary_info += {'spice protocol support': spice_protocol} -if spice_protocol.found() - summary_info += {' spice server support': spice} -endif summary_info += {'rbd support': rbd} summary_info += {'smartcard support': cacard} summary_info += {'U2F support': u2f} @@ -3890,13 +4452,14 @@ summary_info += {'OpenGL support (epoxy)': opengl} summary_info += {'GBM': gbm} summary_info += {'libiscsi support': libiscsi} summary_info += {'libnfs support': libnfs} -if targetos == 'windows' +if host_os == 'windows' if have_ga summary_info += {'QGA VSS support': have_qga_vss} endif endif summary_info += {'seccomp support': seccomp} summary_info += {'GlusterFS support': glusterfs} +summary_info += {'hv-balloon support': hv_balloon} summary_info += {'TPM support': have_tpm} summary_info += {'libssh support': libssh} summary_info += {'lzo support': lzo} @@ -3912,30 +4475,61 @@ summary_info += {'libudev': libudev} # Dummy dependency, keep .found() summary_info += {'FUSE lseek': fuse_lseek.found()} summary_info += {'selinux': selinux} +summary_info += {'libdw': libdw} +if host_os == 'freebsd' + summary_info += {'libinotify-kqueue': inotify} +endif summary(summary_info, bool_yn: true, section: 'Dependencies') -if not supported_cpus.contains(cpu) +if host_arch == 'unknown' message() - warning('SUPPORT FOR THIS HOST CPU WILL GO AWAY IN FUTURE RELEASES!') + warning('UNSUPPORTED HOST CPU') message() - message('CPU host architecture ' + cpu + ' support is not currently maintained.') - message('The QEMU project intends to remove support for this host CPU in') - message('a future release if nobody volunteers to maintain it and to') - message('provide a build host for our continuous integration setup.') - message('configure has succeeded and you can continue to build, but') - message('if you care about QEMU on this platform you should contact') - message('us upstream at qemu-devel@nongnu.org.') + message('Support for CPU host architecture ' + cpu + ' is not currently') + message('maintained. The QEMU project does not guarantee that QEMU will') + message('compile or work on this host CPU. You can help by volunteering') + message('to maintain it and providing a build host for our continuous') + message('integration setup.') + if get_option('tcg').allowed() and target_dirs.length() > 0 + message() + message('configure has succeeded and you can continue to build, but') + message('QEMU will use a slow interpreter to emulate the target CPU.') + endif endif -if not supported_oses.contains(targetos) +if not supported_oses.contains(host_os) message() - warning('WARNING: SUPPORT FOR THIS HOST OS WILL GO AWAY IN FUTURE RELEASES!') + warning('UNSUPPORTED HOST OS') message() - message('Host OS ' + targetos + 'support is not currently maintained.') - message('The QEMU project intends to remove support for this host OS in') - message('a future release if nobody volunteers to maintain it and to') - message('provide a build host for our continuous integration setup.') + message('Support for host OS ' + host_os + 'is not currently maintained.') message('configure has succeeded and you can continue to build, but') - message('if you care about QEMU on this platform you should contact') - message('us upstream at qemu-devel@nongnu.org.') + message('the QEMU project does not guarantee that QEMU will compile or') + message('work on this operating system. You can help by volunteering') + message('to maintain it and providing a build host for our continuous') + message('integration setup. This will ensure that future versions of QEMU') + message('will keep working on ' + host_os + '.') +endif + +if host_arch == 'unknown' or not supported_oses.contains(host_os) + message() + message('If you want to help supporting QEMU on this platform, please') + message('contact the developers at qemu-devel@nongnu.org.') +endif + +actually_reloc = get_option('relocatable') +# check if get_relocated_path() is actually able to relocate paths +if get_option('relocatable') and \ + not (get_option('prefix') / get_option('bindir')).startswith(get_option('prefix') / '') + message() + warning('bindir not included within prefix, the installation will not be relocatable.') + actually_reloc = false +endif +if not actually_reloc and (host_os == 'windows' or get_option('relocatable')) + if host_os == 'windows' + message() + warning('Windows installs should usually be relocatable.') + endif + message() + message('QEMU will have to be installed under ' + get_option('prefix') + '.') + message('Use --disable-relocatable to remove this warning.') endif diff --git a/meson_options.txt b/meson_options.txt index 2de94af037..0a99a059ec 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -6,14 +6,12 @@ option('qemu_suffix', type : 'string', value: 'qemu', description: 'Suffix for QEMU data/modules/config directories (can be empty)') option('docdir', type : 'string', value : 'share/doc', description: 'Base directory for documentation installation (can be empty)') -option('qemu_firmwarepath', type : 'string', value : 'qemu-firmware', +option('qemu_firmwarepath', type : 'array', value : ['share/qemu-firmware'], description: 'search PATH for firmware files') option('pkgversion', type : 'string', value : '', description: 'use specified string as sub-version of the package') option('smbd', type : 'string', value : '', description: 'Path to smbd for slirp networking') -option('sphinx_build', type : 'string', value : '', - description: 'Use specified sphinx-build for building document') option('iasl', type : 'string', value : '', description: 'Path to ACPI disassembler') option('tls_priority', type : 'string', value : 'NORMAL', @@ -21,7 +19,7 @@ option('tls_priority', type : 'string', value : 'NORMAL', option('default_devices', type : 'boolean', value : true, description: 'Include a default selection of devices in emulators') option('audio_drv_list', type: 'array', value: ['default'], - choices: ['alsa', 'coreaudio', 'default', 'dsound', 'jack', 'oss', 'pa', 'sdl'], + choices: ['alsa', 'coreaudio', 'default', 'dsound', 'jack', 'oss', 'pa', 'pipewire', 'sdl', 'sndio'], description: 'Set audio driver list') option('block_drv_rw_whitelist', type : 'string', value : '', description: 'set block driver read-write whitelist (by default affects only QEMU, not tools like qemu-img)') @@ -33,6 +31,9 @@ option('fuzzing_engine', type : 'string', value : '', description: 'fuzzing engine library for OSS-Fuzz') option('trace_file', type: 'string', value: 'trace', description: 'Trace file prefix for simple backend') +option('coroutine_backend', type: 'combo', + choices: ['ucontext', 'sigaltstack', 'windows', 'auto'], + value: 'auto', description: 'coroutine backend to use') # Everything else can be set via --enable/--disable-* option # on the configure script command line. After adding an option @@ -44,6 +45,8 @@ option('fuzzing', type : 'boolean', value: false, description: 'build fuzzing targets') option('gettext', type : 'feature', value : 'auto', description: 'Localization of the GTK+ user interface') +option('modules', type : 'feature', value : 'disabled', + description: 'modules support (non Windows)') option('module_upgrades', type : 'boolean', value : false, description: 'try to load modules from alternate paths for upgrades') option('install_blobs', type : 'boolean', value : true, @@ -66,8 +69,6 @@ option('malloc', type : 'combo', choices : ['system', 'tcmalloc', 'jemalloc'], option('kvm', type: 'feature', value: 'auto', description: 'KVM acceleration support') -option('hax', type: 'feature', value: 'auto', - description: 'HAX acceleration support') option('whpx', type: 'feature', value: 'auto', description: 'WHPX acceleration support') option('hvf', type: 'feature', value: 'auto', @@ -80,14 +81,30 @@ option('xen_pci_passthrough', type: 'feature', value: 'auto', description: 'Xen PCI passthrough support') option('tcg', type: 'feature', value: 'enabled', description: 'TCG support') +option('plugins', type: 'boolean', value: false, + description: 'TCG plugins via shared library loading') +option('debug_tcg', type: 'boolean', value: false, + description: 'TCG debugging') option('tcg_interpreter', type: 'boolean', value: false, description: 'TCG with bytecode interpreter (slow)') -option('cfi', type: 'boolean', value: 'false', +option('safe_stack', type: 'boolean', value: false, + description: 'SafeStack Stack Smash Protection (requires clang/llvm and coroutine backend ucontext)') +option('sanitizers', type: 'boolean', value: false, + description: 'enable default sanitizers') +option('tsan', type: 'boolean', value: false, + description: 'enable thread sanitizer') +option('stack_protector', type: 'feature', value: 'auto', + description: 'compiler-provided stack protection') +option('cfi', type: 'boolean', value: false, description: 'Control-Flow Integrity (CFI)') -option('cfi_debug', type: 'boolean', value: 'false', +option('cfi_debug', type: 'boolean', value: false, description: 'Verbose errors in case of CFI violation') option('multiprocess', type: 'feature', value: 'auto', description: 'Out of process device emulation support') +option('relocatable', type : 'boolean', value : true, + description: 'toggle relocatable install') +option('vfio_user_server', type: 'feature', value: 'disabled', + description: 'vfio-user server support') option('dbus_display', type: 'feature', value: 'auto', description: '-display dbus support') option('tpm', type : 'feature', value : 'auto', @@ -102,9 +119,15 @@ option('avx2', type: 'feature', value: 'auto', description: 'AVX2 optimizations') option('avx512f', type: 'feature', value: 'disabled', description: 'AVX512F optimizations') +option('avx512bw', type: 'feature', value: 'auto', + description: 'AVX512BW optimizations') option('keyring', type: 'feature', value: 'auto', description: 'Linux keyring support') +option('libkeyutils', type: 'feature', value: 'auto', + description: 'Linux keyutils support') +option('af_xdp', type : 'feature', value : 'auto', + description: 'AF_XDP network backend support') option('attr', type : 'feature', value : 'auto', description: 'attr/xattr support') option('auth_pam', type : 'feature', value : 'auto', @@ -115,6 +138,8 @@ option('bzip2', type : 'feature', value : 'auto', description: 'bzip2 support for DMG images') option('cap_ng', type : 'feature', value : 'auto', description: 'cap_ng support') +option('blkio', type : 'feature', value : 'auto', + description: 'libblkio block device driver') option('bpf', type : 'feature', value : 'auto', description: 'eBPF support') option('cocoa', type : 'feature', value : 'auto', @@ -125,6 +150,10 @@ option('gio', type : 'feature', value : 'auto', description: 'use libgio for D-Bus support') option('glusterfs', type : 'feature', value : 'auto', description: 'Glusterfs block device driver') +option('hv_balloon', type : 'feature', value : 'auto', + description: 'hv-balloon driver (requires Glib 2.68+ GTree API)') +option('libdw', type : 'feature', value : 'auto', + description: 'debuginfo support') option('libiscsi', type : 'feature', value : 'auto', description: 'libiscsi userspace initiator') option('libnfs', type : 'feature', value : 'auto', @@ -189,18 +218,26 @@ option('spice_protocol', type : 'feature', value : 'auto', description: 'Spice protocol support') option('u2f', type : 'feature', value : 'auto', description: 'U2F emulation support') +option('canokey', type : 'feature', value : 'auto', + description: 'CanoKey support') option('usb_redir', type : 'feature', value : 'auto', description: 'libusbredir support') option('l2tpv3', type : 'feature', value : 'auto', description: 'l2tpv3 network backend support') option('netmap', type : 'feature', value : 'auto', description: 'netmap network backend support') +option('pixman', type : 'feature', value : 'auto', + description: 'pixman support') +option('slirp', type: 'feature', value: 'auto', + description: 'libslirp user mode network backend support') option('vde', type : 'feature', value : 'auto', description: 'vde network backend support') option('vmnet', type : 'feature', value : 'auto', description: 'vmnet.framework network backend support') option('virglrenderer', type : 'feature', value : 'auto', description: 'virgl rendering support') +option('rutabaga_gfx', type : 'feature', value : 'auto', + description: 'rutabaga_gfx support') option('png', type : 'feature', value : 'auto', description: 'PNG support with libpng') option('vnc', type : 'feature', value : 'auto', @@ -211,6 +248,13 @@ option('vnc_sasl', type : 'feature', value : 'auto', description: 'SASL authentication for VNC server') option('vte', type : 'feature', value : 'auto', description: 'vte support for the gtk UI') + +# GTK Clipboard implementation is disabled by default, since it may cause hangs +# of the guest VCPUs. See gitlab issue 1150: +# https://gitlab.com/qemu-project/qemu/-/issues/1150 + +option('gtk_clipboard', type: 'feature', value : 'disabled', + description: 'clipboard support for the gtk UI (EXPERIMENTAL, MAY HANG)') option('xkbcommon', type : 'feature', value : 'auto', description: 'xkbcommon support') option('zstd', type : 'feature', value : 'auto', @@ -236,6 +280,10 @@ option('oss', type: 'feature', value: 'auto', description: 'OSS sound support') option('pa', type: 'feature', value: 'auto', description: 'PulseAudio sound support') +option('pipewire', type: 'feature', value: 'auto', + description: 'PipeWire sound support') +option('sndio', type: 'feature', value: 'auto', + description: 'sndio sound support') option('vhost_kernel', type: 'feature', value: 'auto', description: 'vhost kernel backend support') @@ -251,14 +299,15 @@ option('vhost_user_blk_server', type: 'feature', value: 'auto', description: 'build vhost-user-blk server') option('virtfs', type: 'feature', value: 'auto', description: 'virtio-9p support') -option('virtiofsd', type: 'feature', value: 'auto', - description: 'build virtiofs daemon (virtiofsd)') +option('virtfs_proxy_helper', type: 'feature', value: 'auto', + description: 'virtio-9p proxy helper support') +option('libvduse', type: 'feature', value: 'auto', + description: 'build VDUSE Library') +option('vduse_blk_export', type: 'feature', value: 'auto', + description: 'VDUSE block export support') option('capstone', type: 'feature', value: 'auto', description: 'Whether and how to find the capstone library') -option('slirp', type: 'combo', value: 'auto', - choices: ['disabled', 'enabled', 'auto', 'system', 'internal'], - description: 'Whether and how to find the slirp library') option('fdt', type: 'combo', value: 'auto', choices: ['disabled', 'enabled', 'auto', 'system', 'internal'], description: 'Whether and how to find the libfdt library') @@ -269,6 +318,8 @@ option('live_block_migration', type: 'feature', value: 'auto', description: 'block migration in the main migration stream') option('replication', type: 'feature', value: 'auto', description: 'replication support') +option('colo_proxy', type: 'feature', value: 'auto', + description: 'colo-proxy support') option('bochs', type: 'feature', value: 'auto', description: 'bochs image format support') option('cloop', type: 'feature', value: 'auto', @@ -279,6 +330,12 @@ option('qcow1', type: 'feature', value: 'auto', description: 'qcow1 image format support') option('vdi', type: 'feature', value: 'auto', description: 'vdi image format support') +option('vhdx', type: 'feature', value: 'auto', + description: 'vhdx image format support') +option('vmdk', type: 'feature', value: 'auto', + description: 'vmdk image format support') +option('vpc', type: 'feature', value: 'auto', + description: 'vpc image format support') option('vvfat', type: 'feature', value: 'auto', description: 'vvfat image format support') option('qed', type: 'feature', value: 'auto', @@ -291,15 +348,23 @@ option('rng_none', type: 'boolean', value: false, description: 'dummy RNG, avoid using /dev/(u)random and getrandom()') option('coroutine_pool', type: 'boolean', value: true, description: 'coroutine freelist (better performance)') +option('debug_graph_lock', type: 'boolean', value: false, + description: 'graph lock debugging support') option('debug_mutex', type: 'boolean', value: false, description: 'mutex debugging support') option('debug_stack_usage', type: 'boolean', value: false, description: 'measure coroutine stack usage') -option('qom_cast_debug', type: 'boolean', value: false, +option('qom_cast_debug', type: 'boolean', value: true, description: 'cast debugging support') -option('gprof', type: 'boolean', value: false, - description: 'QEMU profiling with gprof') -option('profiler', type: 'boolean', value: false, - description: 'profiler support') option('slirp_smbd', type : 'feature', value : 'auto', description: 'use smbd (at path --smbd=*) in slirp networking') + +option('qemu_ga_manufacturer', type: 'string', value: 'QEMU', + description: '"manufacturer" name for qemu-ga registry entries') +option('qemu_ga_distro', type: 'string', value: 'Linux', + description: 'second path element in qemu-ga registry entries') +option('qemu_ga_version', type: 'string', value: '', + description: 'version number for qemu-ga installer') + +option('hexagon_idef_parser', type : 'boolean', value : true, + description: 'use idef-parser to automatically generate TCG code for the Hexagon frontend') diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index 9aba7d9c22..2708abf3d7 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -61,6 +61,7 @@ #include "qemu/osdep.h" #include "block/block.h" #include "block/block_int.h" +#include "block/dirty-bitmap.h" #include "sysemu/block-backend.h" #include "sysemu/runstate.h" #include "qemu/main-loop.h" @@ -78,6 +79,7 @@ #include "qapi/qapi-visit-migration.h" #include "qapi/clone-visitor.h" #include "trace.h" +#include "options.h" #define CHUNK_SIZE (1 << 10) @@ -462,7 +464,7 @@ static void send_bitmap_bits(QEMUFile *f, DBMSaveState *s, g_free(buf); } -/* Called with iothread lock taken. */ +/* Called with the BQL taken. */ static void dirty_bitmap_do_save_cleanup(DBMSaveState *s) { SaveBitmapState *dbms; @@ -477,7 +479,7 @@ static void dirty_bitmap_do_save_cleanup(DBMSaveState *s) } } -/* Called with iothread lock taken. */ +/* Called with the BQL taken. */ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, const char *bs_name, GHashTable *alias_map) { @@ -551,7 +553,7 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, } bitmap_alias = bmap_inner->alias; - if (bmap_inner->has_transform) { + if (bmap_inner->transform) { bitmap_transform = bmap_inner->transform; } } else { @@ -596,18 +598,21 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, return 0; } -/* Called with iothread lock taken. */ +/* Called with the BQL taken. */ static int init_dirty_bitmap_migration(DBMSaveState *s) { BlockDriverState *bs; SaveBitmapState *dbms; GHashTable *handled_by_blk = g_hash_table_new(NULL, NULL); BlockBackend *blk; - const MigrationParameters *mig_params = &migrate_get_current()->parameters; GHashTable *alias_map = NULL; - if (mig_params->has_block_bitmap_mapping) { - alias_map = construct_alias_map(mig_params->block_bitmap_mapping, true, + /* Runs in the migration thread, but holds the BQL */ + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + if (migrate_has_block_bitmap_mapping()) { + alias_map = construct_alias_map(migrate_block_bitmap_mapping(), true, &error_abort); } @@ -705,7 +710,7 @@ static void bulk_phase(QEMUFile *f, DBMSaveState *s, bool limit) QSIMPLEQ_FOREACH(dbms, &s->dbms_list, entry) { while (!dbms->bulk_completed) { bulk_phase_send_chunk(f, s, dbms); - if (limit && qemu_file_rate_limit(f)) { + if (limit && migration_rate_exceeded(f)) { return; } } @@ -737,7 +742,7 @@ static int dirty_bitmap_save_iterate(QEMUFile *f, void *opaque) return s->bulk_completed; } -/* Called with iothread lock taken. */ +/* Called with the BQL taken. */ static int dirty_bitmap_save_complete(QEMUFile *f, void *opaque) { @@ -761,17 +766,15 @@ static int dirty_bitmap_save_complete(QEMUFile *f, void *opaque) return 0; } -static void dirty_bitmap_save_pending(QEMUFile *f, void *opaque, - uint64_t max_size, - uint64_t *res_precopy_only, - uint64_t *res_compatible, - uint64_t *res_postcopy_only) +static void dirty_bitmap_state_pending(void *opaque, + uint64_t *must_precopy, + uint64_t *can_postcopy) { DBMSaveState *s = &((DBMState *)opaque)->save; SaveBitmapState *dbms; uint64_t pending = 0; - qemu_mutex_lock_iothread(); + bql_lock(); QSIMPLEQ_FOREACH(dbms, &s->dbms_list, entry) { uint64_t gran = bdrv_dirty_bitmap_granularity(dbms->bitmap); @@ -781,11 +784,11 @@ static void dirty_bitmap_save_pending(QEMUFile *f, void *opaque, pending += DIV_ROUND_UP(sectors * BDRV_SECTOR_SIZE, gran); } - qemu_mutex_unlock_iothread(); + bql_unlock(); - trace_dirty_bitmap_save_pending(pending, max_size); + trace_dirty_bitmap_state_pending(pending); - *res_postcopy_only += pending; + *can_postcopy += pending; } /* First occurrence of this bitmap. It should be created if doesn't exist */ @@ -821,7 +824,7 @@ static int dirty_bitmap_load_start(QEMUFile *f, DBMLoadState *s) } if (s->bmap_inner && - s->bmap_inner->has_transform && + s->bmap_inner->transform && s->bmap_inner->transform->has_persistent) { persistent = s->bmap_inner->transform->persistent; } else { @@ -1158,7 +1161,6 @@ static int dirty_bitmap_load_header(QEMUFile *f, DBMLoadState *s, static int dirty_bitmap_load(QEMUFile *f, void *opaque, int version_id) { GHashTable *alias_map = NULL; - const MigrationParameters *mig_params = &migrate_get_current()->parameters; DBMLoadState *s = &((DBMState *)opaque)->load; int ret = 0; @@ -1170,9 +1172,9 @@ static int dirty_bitmap_load(QEMUFile *f, void *opaque, int version_id) return -EINVAL; } - if (mig_params->has_block_bitmap_mapping) { - alias_map = construct_alias_map(mig_params->block_bitmap_mapping, - false, &error_abort); + if (migrate_has_block_bitmap_mapping()) { + alias_map = construct_alias_map(migrate_block_bitmap_mapping(), false, + &error_abort); } do { @@ -1216,9 +1218,7 @@ static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque) DBMSaveState *s = &((DBMState *)opaque)->save; SaveBitmapState *dbms = NULL; - qemu_mutex_lock_iothread(); if (init_dirty_bitmap_migration(s) < 0) { - qemu_mutex_unlock_iothread(); return -1; } @@ -1226,7 +1226,6 @@ static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque) send_bitmap_start(f, s, dbms); } qemu_put_bitmap_flags(f, DIRTY_BITMAP_MIG_FLAG_EOS); - qemu_mutex_unlock_iothread(); return 0; } @@ -1252,7 +1251,8 @@ static SaveVMHandlers savevm_dirty_bitmap_handlers = { .save_live_complete_postcopy = dirty_bitmap_save_complete, .save_live_complete_precopy = dirty_bitmap_save_complete, .has_postcopy = dirty_bitmap_has_postcopy, - .save_live_pending = dirty_bitmap_save_pending, + .state_pending_exact = dirty_bitmap_state_pending, + .state_pending_estimate = dirty_bitmap_state_pending, .save_live_iterate = dirty_bitmap_save_iterate, .is_active_iterate = dirty_bitmap_is_active_iterate, .load_state = dirty_bitmap_load, diff --git a/migration/block.c b/migration/block.c index 077a413325..2b9054889a 100644 --- a/migration/block.c +++ b/migration/block.c @@ -20,15 +20,18 @@ #include "qemu/cutils.h" #include "qemu/queue.h" #include "block.h" +#include "block/dirty-bitmap.h" #include "migration/misc.h" #include "migration.h" +#include "migration-stats.h" #include "migration/register.h" #include "qemu-file.h" #include "migration/vmstate.h" #include "sysemu/block-backend.h" #include "trace.h" +#include "options.h" -#define BLK_MIG_BLOCK_SIZE (1 << 20) +#define BLK_MIG_BLOCK_SIZE (1ULL << 20) #define BDRV_SECTORS_PER_DIRTY_CHUNK (BLK_MIG_BLOCK_SIZE >> BDRV_SECTOR_BITS) #define BLK_MIG_FLAG_DEVICE_BLOCK 0x01 @@ -41,16 +44,6 @@ #define MAX_IO_BUFFERS 512 #define MAX_PARALLEL_IO 16 -/* #define DEBUG_BLK_MIGRATION */ - -#ifdef DEBUG_BLK_MIGRATION -#define DPRINTF(fmt, ...) \ - do { printf("blk_migration: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - typedef struct BlkMigDevState { /* Written during setup phase. Can be read without a lock. */ BlockBackend *blk; @@ -73,7 +66,7 @@ typedef struct BlkMigDevState { /* Protected by block migration lock. */ int64_t completed_sectors; - /* During migration this is protected by iothread lock / AioContext. + /* During migration this is protected by bdrv_dirty_bitmap_lock(). * Allocation and free happen during setup and cleanup respectively. */ BdrvDirtyBitmap *dirty_bitmap; @@ -108,7 +101,7 @@ typedef struct BlkMigState { int prev_progress; int bulk_completed; - /* Lock must be taken _inside_ the iothread lock and any AioContexts. */ + /* Lock must be taken _inside_ the BQL. */ QemuMutex lock; } BlkMigState; @@ -124,7 +117,7 @@ static void blk_mig_unlock(void) qemu_mutex_unlock(&block_mig_state.lock); } -/* Must run outside of the iothread lock during the bulk phase, +/* Must run outside of the BQL during the bulk phase, * or the VM will stall. */ @@ -204,7 +197,7 @@ static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector) { int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK; - if (sector < blk_nb_sectors(bmds->blk)) { + if (sector < bmds->total_sectors) { return !!(bmds->aio_bitmap[chunk / (sizeof(unsigned long) * 8)] & (1UL << (chunk % (sizeof(unsigned long) * 8)))); } else { @@ -238,10 +231,9 @@ static void bmds_set_aio_inflight(BlkMigDevState *bmds, int64_t sector_num, static void alloc_aio_bitmap(BlkMigDevState *bmds) { - BlockBackend *bb = bmds->blk; int64_t bitmap_size; - bitmap_size = blk_nb_sectors(bb) + BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1; + bitmap_size = bmds->total_sectors + BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1; bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8; bmds->aio_bitmap = g_malloc0(bitmap_size); @@ -277,8 +269,7 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) int64_t count; if (bmds->shared_base) { - qemu_mutex_lock_iothread(); - aio_context_acquire(blk_get_aio_context(bb)); + bql_lock(); /* Skip unallocated sectors; intentionally treats failure or * partial sector as an allocated sector */ while (cur_sector < total_sectors && @@ -289,8 +280,7 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) } cur_sector += count >> BDRV_SECTOR_BITS; } - aio_context_release(blk_get_aio_context(bb)); - qemu_mutex_unlock_iothread(); + bql_unlock(); } if (cur_sector >= total_sectors) { @@ -321,28 +311,23 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) block_mig_state.submitted++; blk_mig_unlock(); - /* We do not know if bs is under the main thread (and thus does - * not acquire the AioContext when doing AIO) or rather under - * dataplane. Thus acquire both the iothread mutex and the - * AioContext. - * - * This is ugly and will disappear when we make bdrv_* thread-safe, - * without the need to acquire the AioContext. + /* + * The migration thread does not have an AioContext. Lock the BQL so that + * I/O runs in the main loop AioContext (see + * qemu_get_current_aio_context()). */ - qemu_mutex_lock_iothread(); - aio_context_acquire(blk_get_aio_context(bmds->blk)); + bql_lock(); bdrv_reset_dirty_bitmap(bmds->dirty_bitmap, cur_sector * BDRV_SECTOR_SIZE, nr_sectors * BDRV_SECTOR_SIZE); blk->aiocb = blk_aio_preadv(bb, cur_sector * BDRV_SECTOR_SIZE, &blk->qiov, 0, blk_mig_read_cb, blk); - aio_context_release(blk_get_aio_context(bmds->blk)); - qemu_mutex_unlock_iothread(); + bql_unlock(); bmds->cur_sector = cur_sector + nr_sectors; return (bmds->cur_sector >= total_sectors); } -/* Called with iothread lock taken. */ +/* Called with the BQL taken. */ static int set_dirty_tracking(void) { @@ -369,14 +354,16 @@ fail: return ret; } -/* Called with iothread lock taken. */ +/* Called with the BQL taken. */ static void unset_dirty_tracking(void) { BlkMigDevState *bmds; QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - bdrv_release_dirty_bitmap(bmds->dirty_bitmap); + if (bmds->dirty_bitmap) { + bdrv_release_dirty_bitmap(bmds->dirty_bitmap); + } } } @@ -394,6 +381,8 @@ static int init_blk_migration(QEMUFile *f) Error *local_err = NULL; int ret; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + block_mig_state.submitted = 0; block_mig_state.read_done = 0; block_mig_state.transferred = 0; @@ -413,7 +402,10 @@ static int init_blk_migration(QEMUFile *f) } sectors = bdrv_nb_sectors(bs); - if (sectors <= 0) { + if (sectors == 0) { + continue; + } + if (sectors < 0) { ret = sectors; bdrv_next_cleanup(&it); goto out; @@ -426,7 +418,7 @@ static int init_blk_migration(QEMUFile *f) bmds->bulk_completed = 0; bmds->total_sectors = sectors; bmds->completed_sectors = 0; - bmds->shared_base = migrate_use_block_incremental(); + bmds->shared_base = migrate_block_incremental(); assert(i < num_bs); bmds_bs[i].bmds = bmds; @@ -446,8 +438,8 @@ static int init_blk_migration(QEMUFile *f) /* Can only insert new BDSes now because doing so while iterating block * devices may end up in a deadlock (iterating the new BDSes, too). */ for (i = 0; i < num_bs; i++) { - BlkMigDevState *bmds = bmds_bs[i].bmds; - BlockDriverState *bs = bmds_bs[i].bs; + bmds = bmds_bs[i].bmds; + bs = bmds_bs[i].bs; if (bmds) { ret = blk_insert_bs(bmds->blk, bs, &local_err); @@ -501,7 +493,7 @@ static int blk_mig_save_bulked_block(QEMUFile *f) block_mig_state.prev_progress = progress; qemu_put_be64(f, (progress << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS); - DPRINTF("Completed %d %%\r", progress); + trace_migration_block_progression(progress); } return ret; @@ -516,7 +508,7 @@ static void blk_mig_reset_dirty_cursor(void) } } -/* Called with iothread lock and AioContext taken. */ +/* Called with the BQL taken. */ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, int is_async) @@ -568,8 +560,8 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, bmds_set_aio_inflight(bmds, sector, nr_sectors, 1); blk_mig_unlock(); } else { - ret = blk_pread(bmds->blk, sector * BDRV_SECTOR_SIZE, blk->buf, - nr_sectors * BDRV_SECTOR_SIZE); + ret = blk_pread(bmds->blk, sector * BDRV_SECTOR_SIZE, + nr_sectors * BDRV_SECTOR_SIZE, blk->buf, 0); if (ret < 0) { goto error; } @@ -598,7 +590,7 @@ error: return ret; } -/* Called with iothread lock taken. +/* Called with the BQL taken. * * return value: * 0: too much data for max_downtime @@ -610,9 +602,7 @@ static int blk_mig_save_dirty_block(QEMUFile *f, int is_async) int ret = 1; QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - aio_context_acquire(blk_get_aio_context(bmds->blk)); ret = mig_save_device_dirty(f, bmds, is_async); - aio_context_release(blk_get_aio_context(bmds->blk)); if (ret <= 0) { break; } @@ -634,7 +624,7 @@ static int flush_blks(QEMUFile *f) blk_mig_lock(); while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) { - if (qemu_file_rate_limit(f)) { + if (migration_rate_exceeded(f)) { break; } if (blk->ret < 0) { @@ -662,7 +652,7 @@ static int flush_blks(QEMUFile *f) return ret; } -/* Called with iothread lock taken. */ +/* Called with the BQL taken. */ static int64_t get_remaining_dirty(void) { @@ -670,9 +660,9 @@ static int64_t get_remaining_dirty(void) int64_t dirty = 0; QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - aio_context_acquire(blk_get_aio_context(bmds->blk)); + bdrv_dirty_bitmap_lock(bmds->dirty_bitmap); dirty += bdrv_get_dirty_count(bmds->dirty_bitmap); - aio_context_release(blk_get_aio_context(bmds->blk)); + bdrv_dirty_bitmap_unlock(bmds->dirty_bitmap); } return dirty; @@ -680,32 +670,30 @@ static int64_t get_remaining_dirty(void) -/* Called with iothread lock taken. */ +/* Called with the BQL taken. */ static void block_migration_cleanup_bmds(void) { BlkMigDevState *bmds; - AioContext *ctx; + BlockDriverState *bs; unset_dirty_tracking(); while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) { QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry); - bdrv_op_unblock_all(blk_bs(bmds->blk), bmds->blocker); + + bs = blk_bs(bmds->blk); + if (bs) { + bdrv_op_unblock_all(bs, bmds->blocker); + } error_free(bmds->blocker); - - /* Save ctx, because bmds->blk can disappear during blk_unref. */ - ctx = blk_get_aio_context(bmds->blk); - aio_context_acquire(ctx); blk_unref(bmds->blk); - aio_context_release(ctx); - g_free(bmds->blk_name); g_free(bmds->aio_bitmap); g_free(bmds); } } -/* Called with iothread lock taken. */ +/* Called with the BQL taken. */ static void block_migration_cleanup(void *opaque) { BlkMigBlock *blk; @@ -730,18 +718,16 @@ static int block_save_setup(QEMUFile *f, void *opaque) trace_migration_block_save("setup", block_mig_state.submitted, block_mig_state.transferred); - qemu_mutex_lock_iothread(); + warn_report("block migration is deprecated;" + " use blockdev-mirror with NBD instead"); + ret = init_blk_migration(f); if (ret < 0) { - qemu_mutex_unlock_iothread(); return ret; } /* start track dirty blocks */ ret = set_dirty_tracking(); - - qemu_mutex_unlock_iothread(); - if (ret) { return ret; } @@ -756,8 +742,7 @@ static int block_save_setup(QEMUFile *f, void *opaque) static int block_save_iterate(QEMUFile *f, void *opaque) { int ret; - int64_t last_ftell = qemu_ftell(f); - int64_t delta_ftell; + uint64_t last_bytes = qemu_file_transferred(f); trace_migration_block_save("iterate", block_mig_state.submitted, block_mig_state.transferred); @@ -772,7 +757,7 @@ static int block_save_iterate(QEMUFile *f, void *opaque) /* control the rate of transfer */ blk_mig_lock(); while (block_mig_state.read_done * BLK_MIG_BLOCK_SIZE < - qemu_file_get_rate_limit(f) && + migration_rate_get() && block_mig_state.submitted < MAX_PARALLEL_IO && (block_mig_state.submitted + block_mig_state.read_done) < MAX_IO_BUFFERS) { @@ -785,12 +770,12 @@ static int block_save_iterate(QEMUFile *f, void *opaque) } ret = 0; } else { - /* Always called with iothread lock taken for + /* Always called with the BQL taken for * simplicity, block_save_complete also calls it. */ - qemu_mutex_lock_iothread(); + bql_lock(); ret = blk_mig_save_dirty_block(f, 1); - qemu_mutex_unlock_iothread(); + bql_unlock(); } if (ret < 0) { return ret; @@ -809,17 +794,11 @@ static int block_save_iterate(QEMUFile *f, void *opaque) } qemu_put_be64(f, BLK_MIG_FLAG_EOS); - delta_ftell = qemu_ftell(f) - last_ftell; - if (delta_ftell > 0) { - return 1; - } else if (delta_ftell < 0) { - return -1; - } else { - return 0; - } + uint64_t delta_bytes = qemu_file_transferred(f) - last_bytes; + return (delta_bytes > 0); } -/* Called with iothread lock taken. */ +/* Called with the BQL taken. */ static int block_save_complete(QEMUFile *f, void *opaque) { @@ -862,17 +841,15 @@ static int block_save_complete(QEMUFile *f, void *opaque) return 0; } -static void block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, - uint64_t *res_precopy_only, - uint64_t *res_compatible, - uint64_t *res_postcopy_only) +static void block_state_pending(void *opaque, uint64_t *must_precopy, + uint64_t *can_postcopy) { /* Estimate pending number of bytes to send */ uint64_t pending; - qemu_mutex_lock_iothread(); + bql_lock(); pending = get_remaining_dirty(); - qemu_mutex_unlock_iothread(); + bql_unlock(); blk_mig_lock(); pending += block_mig_state.submitted * BLK_MIG_BLOCK_SIZE + @@ -880,13 +857,13 @@ static void block_save_pending(QEMUFile *f, void *opaque, uint64_t max_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 + BLK_MIG_BLOCK_SIZE; + if (!pending && !block_mig_state.bulk_completed) { + pending = BLK_MIG_BLOCK_SIZE; } - trace_migration_block_save_pending(pending); + trace_migration_block_state_pending(pending); /* We don't do postcopy */ - *res_precopy_only += pending; + *must_precopy += pending; } static int block_load(QEMUFile *f, void *opaque, int version_id) @@ -976,8 +953,8 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) cluster_size, BDRV_REQ_MAY_UNMAP); } else { - ret = blk_pwrite(blk, cur_addr, cur_buf, - cluster_size, 0); + ret = blk_pwrite(blk, cur_addr, cluster_size, cur_buf, + 0); } if (ret < 0) { break; @@ -1012,14 +989,15 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) static bool block_is_active(void *opaque) { - return migrate_use_block(); + return migrate_block(); } static SaveVMHandlers savevm_block_handlers = { .save_setup = block_save_setup, .save_live_iterate = block_save_iterate, .save_live_complete_precopy = block_save_complete, - .save_live_pending = block_save_pending, + .state_pending_exact = block_state_pending, + .state_pending_estimate = block_state_pending, .load_state = block_load, .save_cleanup = block_migration_cleanup, .is_active = block_is_active, diff --git a/migration/channel-block.c b/migration/channel-block.c new file mode 100644 index 0000000000..fff8d87094 --- /dev/null +++ b/migration/channel-block.c @@ -0,0 +1,199 @@ +/* + * QEMU I/O channels block driver + * + * Copyright (c) 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "migration/channel-block.h" +#include "qapi/error.h" +#include "block/block.h" +#include "trace.h" + +QIOChannelBlock * +qio_channel_block_new(BlockDriverState *bs) +{ + QIOChannelBlock *ioc; + + ioc = QIO_CHANNEL_BLOCK(object_new(TYPE_QIO_CHANNEL_BLOCK)); + + bdrv_ref(bs); + ioc->bs = bs; + + return ioc; +} + + +static void +qio_channel_block_finalize(Object *obj) +{ + QIOChannelBlock *ioc = QIO_CHANNEL_BLOCK(obj); + + g_clear_pointer(&ioc->bs, bdrv_unref); +} + + +static ssize_t +qio_channel_block_readv(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int **fds, + size_t *nfds, + int flags, + Error **errp) +{ + QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc); + QEMUIOVector qiov; + int ret; + + qemu_iovec_init_external(&qiov, (struct iovec *)iov, niov); + ret = bdrv_readv_vmstate(bioc->bs, &qiov, bioc->offset); + if (ret < 0) { + error_setg_errno(errp, -ret, "bdrv_readv_vmstate failed"); + return -1; + } + + bioc->offset += qiov.size; + return qiov.size; +} + + +static ssize_t +qio_channel_block_writev(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + int *fds, + size_t nfds, + int flags, + Error **errp) +{ + QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc); + QEMUIOVector qiov; + int ret; + + qemu_iovec_init_external(&qiov, (struct iovec *)iov, niov); + ret = bdrv_writev_vmstate(bioc->bs, &qiov, bioc->offset); + if (ret < 0) { + error_setg_errno(errp, -ret, "bdrv_writev_vmstate failed"); + return -1; + } + + bioc->offset += qiov.size; + return qiov.size; +} + + +static int +qio_channel_block_set_blocking(QIOChannel *ioc, + bool enabled, + Error **errp) +{ + if (!enabled) { + error_setg(errp, "Non-blocking mode not supported for block devices"); + return -1; + } + return 0; +} + + +static off_t +qio_channel_block_seek(QIOChannel *ioc, + off_t offset, + int whence, + Error **errp) +{ + QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc); + + switch (whence) { + case SEEK_SET: + bioc->offset = offset; + break; + case SEEK_CUR: + bioc->offset += whence; + break; + case SEEK_END: + error_setg(errp, "Size of VMstate region is unknown"); + return (off_t)-1; + default: + g_assert_not_reached(); + } + + return bioc->offset; +} + + +static int +qio_channel_block_close(QIOChannel *ioc, + Error **errp) +{ + QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc); + int rv = bdrv_flush(bioc->bs); + + if (rv < 0) { + error_setg_errno(errp, -rv, + "Unable to flush VMState"); + return -1; + } + + g_clear_pointer(&bioc->bs, bdrv_unref); + bioc->offset = 0; + + return 0; +} + + +static void +qio_channel_block_set_aio_fd_handler(QIOChannel *ioc, + AioContext *read_ctx, + IOHandler *io_read, + AioContext *write_ctx, + IOHandler *io_write, + void *opaque) +{ + /* XXX anything we can do here ? */ +} + + +static void +qio_channel_block_class_init(ObjectClass *klass, + void *class_data G_GNUC_UNUSED) +{ + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); + + ioc_klass->io_writev = qio_channel_block_writev; + ioc_klass->io_readv = qio_channel_block_readv; + ioc_klass->io_set_blocking = qio_channel_block_set_blocking; + ioc_klass->io_seek = qio_channel_block_seek; + ioc_klass->io_close = qio_channel_block_close; + ioc_klass->io_set_aio_fd_handler = qio_channel_block_set_aio_fd_handler; +} + +static const TypeInfo qio_channel_block_info = { + .parent = TYPE_QIO_CHANNEL, + .name = TYPE_QIO_CHANNEL_BLOCK, + .instance_size = sizeof(QIOChannelBlock), + .instance_finalize = qio_channel_block_finalize, + .class_init = qio_channel_block_class_init, +}; + +static void +qio_channel_block_register_types(void) +{ + type_register_static(&qio_channel_block_info); +} + +type_init(qio_channel_block_register_types); diff --git a/migration/channel-block.h b/migration/channel-block.h new file mode 100644 index 0000000000..31673824e6 --- /dev/null +++ b/migration/channel-block.h @@ -0,0 +1,59 @@ +/* + * QEMU I/O channels block driver + * + * Copyright (c) 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 QIO_CHANNEL_BLOCK_H +#define QIO_CHANNEL_BLOCK_H + +#include "io/channel.h" +#include "qom/object.h" + +#define TYPE_QIO_CHANNEL_BLOCK "qio-channel-block" +OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelBlock, QIO_CHANNEL_BLOCK) + + +/** + * QIOChannelBlock: + * + * The QIOChannelBlock object provides a channel implementation + * that is able to perform I/O on the BlockDriverState objects + * to the VMState region. + */ + +struct QIOChannelBlock { + QIOChannel parent; + BlockDriverState *bs; + off_t offset; +}; + + +/** + * qio_channel_block_new: + * @bs: the block driver state + * + * Create a new IO channel object that can perform + * I/O on a BlockDriverState object to the VMState + * region + * + * Returns: the new channel object + */ +QIOChannelBlock * +qio_channel_block_new(BlockDriverState *bs); + +#endif /* QIO_CHANNEL_BLOCK_H */ diff --git a/migration/channel.c b/migration/channel.c index a162d00fea..f9de064f3b 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -14,7 +14,7 @@ #include "channel.h" #include "tls.h" #include "migration.h" -#include "qemu-file-channel.h" +#include "qemu-file.h" #include "trace.h" #include "qapi/error.h" #include "io/channel-tls.h" @@ -38,9 +38,7 @@ void migration_channel_process_incoming(QIOChannel *ioc) trace_migration_set_incoming_channel( ioc, object_get_typename(OBJECT(ioc))); - if (migrate_use_tls() && - !object_dynamic_cast(OBJECT(ioc), - TYPE_QIO_CHANNEL_TLS)) { + if (migrate_channel_requires_tls_upgrade(ioc)) { migration_tls_channel_process_incoming(s, ioc, &local_err); } else { migration_ioc_register_yank(ioc); @@ -70,10 +68,7 @@ void migration_channel_connect(MigrationState *s, ioc, object_get_typename(OBJECT(ioc)), hostname, error); if (!error) { - if (s->parameters.tls_creds && - *s->parameters.tls_creds && - !object_dynamic_cast(OBJECT(ioc), - TYPE_QIO_CHANNEL_TLS)) { + if (migrate_channel_requires_tls_upgrade(ioc)) { migration_tls_channel_connect(s, ioc, hostname, &error); if (!error) { @@ -85,7 +80,7 @@ void migration_channel_connect(MigrationState *s, return; } } else { - QEMUFile *f = qemu_fopen_channel_output(ioc); + QEMUFile *f = qemu_file_new_output(ioc); migration_ioc_register_yank(ioc); @@ -97,3 +92,51 @@ void migration_channel_connect(MigrationState *s, migrate_fd_connect(s, error); error_free(error); } + + +/** + * @migration_channel_read_peek - Peek at migration channel, without + * actually removing it from channel buffer. + * + * @ioc: the channel object + * @buf: the memory region to read data into + * @buflen: the number of bytes to read in @buf + * @errp: pointer to a NULL-initialized error object + * + * Returns 0 if successful, returns -1 and sets @errp if fails. + */ +int migration_channel_read_peek(QIOChannel *ioc, + const char *buf, + const size_t buflen, + Error **errp) +{ + ssize_t len = 0; + struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen }; + + while (true) { + len = qio_channel_readv_full(ioc, &iov, 1, NULL, NULL, + QIO_CHANNEL_READ_FLAG_MSG_PEEK, errp); + + if (len < 0 && len != QIO_CHANNEL_ERR_BLOCK) { + return -1; + } + + if (len == 0) { + error_setg(errp, "Failed to peek at channel"); + return -1; + } + + if (len == buflen) { + break; + } + + /* 1ms sleep. */ + if (qemu_in_coroutine()) { + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000); + } else { + g_usleep(1000); + } + } + + return 0; +} diff --git a/migration/channel.h b/migration/channel.h index 67a461c28a..5bdb8208a7 100644 --- a/migration/channel.h +++ b/migration/channel.h @@ -24,4 +24,9 @@ void migration_channel_connect(MigrationState *s, QIOChannel *ioc, const char *hostname, Error *error_in); + +int migration_channel_read_peek(QIOChannel *ioc, + const char *buf, + const size_t buflen, + Error **errp); #endif diff --git a/migration/colo-failover.c b/migration/colo-failover.c index 42453481c4..6cb6f90357 100644 --- a/migration/colo-failover.c +++ b/migration/colo-failover.c @@ -17,7 +17,6 @@ #include "migration.h" #include "qapi/error.h" #include "qapi/qapi-commands-migration.h" -#include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "trace.h" @@ -78,7 +77,7 @@ FailoverStatus failover_get_state(void) void qmp_x_colo_lost_heartbeat(Error **errp) { if (get_colo_mode() == COLO_MODE_NONE) { - error_setg(errp, QERR_FEATURE_DISABLED, "colo"); + error_setg(errp, "VM is not in COLO mode"); return; } diff --git a/migration/colo.c b/migration/colo.c index 5f7071b3cd..84632a603e 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -14,7 +14,6 @@ #include "sysemu/sysemu.h" #include "qapi/error.h" #include "qapi/qapi-commands-migration.h" -#include "qemu-file-channel.h" #include "migration.h" #include "qemu-file.h" #include "savevm.h" @@ -27,17 +26,15 @@ #include "qemu/rcu.h" #include "migration/failover.h" #include "migration/ram.h" -#ifdef CONFIG_REPLICATION #include "block/replication.h" -#endif #include "net/colo-compare.h" #include "net/colo.h" #include "block/block.h" #include "qapi/qapi-events-migration.h" -#include "qapi/qmp/qerror.h" #include "sysemu/cpus.h" #include "sysemu/runstate.h" #include "net/filter.h" +#include "options.h" static bool vmstate_loading; static Notifier packets_compare_notifier; @@ -66,10 +63,32 @@ static bool colo_runstate_is_stopped(void) return runstate_check(RUN_STATE_COLO) || !runstate_is_running(); } +static void colo_checkpoint_notify(void) +{ + MigrationState *s = migrate_get_current(); + int64_t next_notify_time; + + qemu_event_set(&s->colo_checkpoint_event); + s->colo_checkpoint_time = qemu_clock_get_ms(QEMU_CLOCK_HOST); + next_notify_time = s->colo_checkpoint_time + migrate_checkpoint_delay(); + timer_mod(s->colo_delay_timer, next_notify_time); +} + +static void colo_checkpoint_notify_timer(void *opaque) +{ + colo_checkpoint_notify(); +} + +void colo_checkpoint_delay_set(void) +{ + if (migration_in_colo_state()) { + colo_checkpoint_notify(); + } +} + static void secondary_vm_do_failover(void) { /* COLO needs enable block-replication */ -#ifdef CONFIG_REPLICATION int old_state; MigrationIncomingState *mis = migration_incoming_get_current(); Error *local_err = NULL; @@ -131,17 +150,13 @@ static void secondary_vm_do_failover(void) qemu_sem_post(&mis->colo_incoming_sem); /* For Secondary VM, jump to incoming co */ - if (mis->migration_incoming_co) { - qemu_coroutine_enter(mis->migration_incoming_co); + if (mis->colo_incoming_co) { + qemu_coroutine_enter(mis->colo_incoming_co); } -#else - abort(); -#endif } static void primary_vm_do_failover(void) { -#ifdef CONFIG_REPLICATION MigrationState *s = migrate_get_current(); int old_state; Error *local_err = NULL; @@ -152,7 +167,7 @@ static void primary_vm_do_failover(void) * kick COLO thread which might wait at * qemu_sem_wait(&s->colo_checkpoint_sem). */ - colo_checkpoint_notify(s); + colo_checkpoint_notify(); /* * Wake up COLO thread which may blocked in recv() or send(), @@ -182,9 +197,6 @@ static void primary_vm_do_failover(void) /* Notify COLO thread that failover work is finished */ qemu_sem_post(&s->colo_exit_sem); -#else - abort(); -#endif } COLOMode get_colo_mode(void) @@ -218,7 +230,6 @@ void colo_do_failover(void) } } -#ifdef CONFIG_REPLICATION void qmp_xen_set_replication(bool enable, bool primary, bool has_failover, bool failover, Error **errp) @@ -251,7 +262,6 @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp) replication_get_error_all(&err); if (err) { s->error = true; - s->has_desc = true; s->desc = g_strdup(error_get_pretty(err)); } else { s->error = false; @@ -273,7 +283,6 @@ void qmp_xen_colo_do_checkpoint(Error **errp) /* Notify all filters of all NIC to do checkpoint */ colo_notify_filters_event(COLO_EVENT_CHECKPOINT, errp); } -#endif COLOStatus *qmp_query_colo_status(Error **errp) { @@ -310,9 +319,7 @@ static void colo_send_message(QEMUFile *f, COLOMessage msg, return; } qemu_put_be32(f, msg); - qemu_fflush(f); - - ret = qemu_file_get_error(f); + ret = qemu_fflush(f); if (ret < 0) { error_setg_errno(errp, -ret, "Can't send COLO message"); } @@ -331,9 +338,7 @@ static void colo_send_message_value(QEMUFile *f, COLOMessage msg, return; } qemu_put_be64(f, value); - qemu_fflush(f); - - ret = qemu_file_get_error(f); + ret = qemu_fflush(f); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to send value for message:%s", COLOMessage_str(msg)); @@ -420,13 +425,13 @@ static int colo_do_checkpoint_transaction(MigrationState *s, qio_channel_io_seek(QIO_CHANNEL(bioc), 0, 0, NULL); bioc->usage = 0; - qemu_mutex_lock_iothread(); + bql_lock(); if (failover_get_state() != FAILOVER_STATUS_NONE) { - qemu_mutex_unlock_iothread(); + bql_unlock(); goto out; } vm_stop_force_state(RUN_STATE_COLO); - qemu_mutex_unlock_iothread(); + bql_unlock(); trace_colo_vm_state_change("run", "stop"); /* * Failover request bh could be called after vm_stop_force_state(), @@ -435,27 +440,23 @@ static int colo_do_checkpoint_transaction(MigrationState *s, if (failover_get_state() != FAILOVER_STATUS_NONE) { goto out; } - qemu_mutex_lock_iothread(); + bql_lock(); -#ifdef CONFIG_REPLICATION replication_do_checkpoint_all(&local_err); if (local_err) { - qemu_mutex_unlock_iothread(); + bql_unlock(); goto out; } -#else - abort(); -#endif colo_send_message(s->to_dst_file, COLO_MESSAGE_VMSTATE_SEND, &local_err); if (local_err) { - qemu_mutex_unlock_iothread(); + bql_unlock(); goto out; } /* Note: device state is saved into buffer */ ret = qemu_save_device_state(fb); - qemu_mutex_unlock_iothread(); + bql_unlock(); if (ret < 0) { goto out; } @@ -483,8 +484,7 @@ static int colo_do_checkpoint_transaction(MigrationState *s, } qemu_put_buffer(s->to_dst_file, bioc->data, bioc->usage); - qemu_fflush(s->to_dst_file); - ret = qemu_file_get_error(s->to_dst_file); + ret = qemu_fflush(s->to_dst_file); if (ret < 0) { goto out; } @@ -509,9 +509,9 @@ static int colo_do_checkpoint_transaction(MigrationState *s, ret = 0; - qemu_mutex_lock_iothread(); + bql_lock(); vm_start(); - qemu_mutex_unlock_iothread(); + bql_unlock(); trace_colo_vm_state_change("stop", "run"); out: @@ -523,7 +523,7 @@ out: static void colo_compare_notify_checkpoint(Notifier *notifier, void *data) { - colo_checkpoint_notify(data); + colo_checkpoint_notify(); } static void colo_process_checkpoint(MigrationState *s) @@ -559,26 +559,22 @@ static void colo_process_checkpoint(MigrationState *s) goto out; } bioc = qio_channel_buffer_new(COLO_BUFFER_BASE_SIZE); - fb = qemu_fopen_channel_output(QIO_CHANNEL(bioc)); + fb = qemu_file_new_output(QIO_CHANNEL(bioc)); object_unref(OBJECT(bioc)); - qemu_mutex_lock_iothread(); -#ifdef CONFIG_REPLICATION + bql_lock(); replication_start_all(REPLICATION_MODE_PRIMARY, &local_err); if (local_err) { - qemu_mutex_unlock_iothread(); + bql_unlock(); goto out; } -#else - abort(); -#endif vm_start(); - qemu_mutex_unlock_iothread(); + bql_unlock(); trace_colo_vm_state_change("stop", "run"); timer_mod(s->colo_delay_timer, qemu_clock_get_ms(QEMU_CLOCK_HOST) + - s->parameters.x_checkpoint_delay); + migrate_checkpoint_delay()); while (s->state == MIGRATION_STATUS_COLO) { if (failover_get_state() != FAILOVER_STATUS_NONE) { @@ -646,28 +642,16 @@ out: } } -void colo_checkpoint_notify(void *opaque) -{ - MigrationState *s = opaque; - int64_t next_notify_time; - - qemu_event_set(&s->colo_checkpoint_event); - s->colo_checkpoint_time = qemu_clock_get_ms(QEMU_CLOCK_HOST); - next_notify_time = s->colo_checkpoint_time + - s->parameters.x_checkpoint_delay; - timer_mod(s->colo_delay_timer, next_notify_time); -} - void migrate_start_colo_process(MigrationState *s) { - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_event_init(&s->colo_checkpoint_event, false); s->colo_delay_timer = timer_new_ms(QEMU_CLOCK_HOST, - colo_checkpoint_notify, s); + colo_checkpoint_notify_timer, NULL); qemu_sem_init(&s->colo_exit_sem, 0); colo_process_checkpoint(s); - qemu_mutex_lock_iothread(); + bql_lock(); } static void colo_incoming_process_checkpoint(MigrationIncomingState *mis, @@ -678,9 +662,9 @@ static void colo_incoming_process_checkpoint(MigrationIncomingState *mis, Error *local_err = NULL; int ret; - qemu_mutex_lock_iothread(); + bql_lock(); vm_stop_force_state(RUN_STATE_COLO); - qemu_mutex_unlock_iothread(); + bql_unlock(); trace_colo_vm_state_change("run", "stop"); /* FIXME: This is unnecessary for periodic checkpoint mode */ @@ -698,10 +682,10 @@ static void colo_incoming_process_checkpoint(MigrationIncomingState *mis, return; } - qemu_mutex_lock_iothread(); + bql_lock(); cpu_synchronize_all_states(); ret = qemu_loadvm_state_main(mis->from_src_file, mis); - qemu_mutex_unlock_iothread(); + bql_unlock(); if (ret < 0) { error_setg(errp, "Load VM's live state (ram) error"); @@ -740,23 +724,22 @@ static void colo_incoming_process_checkpoint(MigrationIncomingState *mis, return; } - qemu_mutex_lock_iothread(); + bql_lock(); vmstate_loading = true; colo_flush_ram_cache(); ret = qemu_load_device_state(fb); if (ret < 0) { error_setg(errp, "COLO: load device state failed"); vmstate_loading = false; - qemu_mutex_unlock_iothread(); + bql_unlock(); return; } -#ifdef CONFIG_REPLICATION replication_get_error_all(&local_err); if (local_err) { error_propagate(errp, local_err); vmstate_loading = false; - qemu_mutex_unlock_iothread(); + bql_unlock(); return; } @@ -765,25 +748,22 @@ static void colo_incoming_process_checkpoint(MigrationIncomingState *mis, if (local_err) { error_propagate(errp, local_err); vmstate_loading = false; - qemu_mutex_unlock_iothread(); + bql_unlock(); 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); vmstate_loading = false; - qemu_mutex_unlock_iothread(); + bql_unlock(); return; } vmstate_loading = false; vm_start(); - qemu_mutex_unlock_iothread(); + bql_unlock(); trace_colo_vm_state_change("stop", "run"); if (failover_get_state() == FAILOVER_STATUS_RELAUNCH) { @@ -837,7 +817,7 @@ void colo_shutdown(void) } } -void *colo_process_incoming_thread(void *opaque) +static void *colo_process_incoming_thread(void *opaque) { MigrationIncomingState *mis = opaque; QEMUFile *fb = NULL; @@ -873,21 +853,17 @@ void *colo_process_incoming_thread(void *opaque) colo_incoming_start_dirty_log(); bioc = qio_channel_buffer_new(COLO_BUFFER_BASE_SIZE); - fb = qemu_fopen_channel_input(QIO_CHANNEL(bioc)); + fb = qemu_file_new_input(QIO_CHANNEL(bioc)); object_unref(OBJECT(bioc)); - qemu_mutex_lock_iothread(); -#ifdef CONFIG_REPLICATION + bql_lock(); replication_start_all(REPLICATION_MODE_SECONDARY, &local_err); if (local_err) { - qemu_mutex_unlock_iothread(); + bql_unlock(); goto out; } -#else - abort(); -#endif vm_start(); - qemu_mutex_unlock_iothread(); + bql_unlock(); trace_colo_vm_state_change("stop", "run"); colo_send_message(mis->to_src_file, COLO_MESSAGE_CHECKPOINT_READY, @@ -942,3 +918,40 @@ out: rcu_unregister_thread(); return NULL; } + +int coroutine_fn colo_incoming_co(void) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + Error *local_err = NULL; + QemuThread th; + + assert(bql_locked()); + + if (!migration_incoming_colo_enabled()) { + return 0; + } + + /* Make sure all file formats throw away their mutable metadata */ + bdrv_activate_all(&local_err); + if (local_err) { + error_report_err(local_err); + return -EINVAL; + } + + qemu_thread_create(&th, "COLO incoming", colo_process_incoming_thread, + mis, QEMU_THREAD_JOINABLE); + + mis->colo_incoming_co = qemu_coroutine_self(); + qemu_coroutine_yield(); + mis->colo_incoming_co = NULL; + + bql_unlock(); + /* Wait checkpoint incoming thread exit before free resource */ + qemu_thread_join(&th); + bql_lock(); + + /* We hold the global BQL, so it is safe here */ + colo_release_ram_cache(); + + return 0; +} diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index aace12a787..1d2e85746f 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -11,11 +11,12 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include +#include "hw/core/cpu.h" #include "qapi/error.h" -#include "cpu.h" #include "exec/ramblock.h" -#include "exec/ram_addr.h" +#include "exec/target_page.h" #include "qemu/rcu_queue.h" #include "qemu/main-loop.h" #include "qapi/qapi-commands-migration.h" @@ -28,6 +29,7 @@ #include "sysemu/kvm.h" #include "sysemu/runstate.h" #include "exec/memory.h" +#include "qemu/xxhash.h" /* * total_dirty_pages is procted by BQL and is used @@ -46,7 +48,7 @@ static struct DirtyRateStat DirtyStat; static DirtyRateMeasureMode dirtyrate_mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING; -static int64_t set_sample_page_period(int64_t msec, int64_t initial_time) +static int64_t dirty_stat_wait(int64_t msec, int64_t initial_time) { int64_t current_time; @@ -55,15 +57,140 @@ static int64_t set_sample_page_period(int64_t msec, int64_t initial_time) msec = current_time - initial_time; } else { g_usleep((msec + initial_time - current_time) * 1000); + /* g_usleep may overshoot */ + msec = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - initial_time; } return msec; } -static bool is_sample_period_valid(int64_t sec) +static inline void record_dirtypages(DirtyPageRecord *dirty_pages, + CPUState *cpu, bool start) { - if (sec < MIN_FETCH_DIRTYRATE_TIME_SEC || - sec > MAX_FETCH_DIRTYRATE_TIME_SEC) { + if (start) { + dirty_pages[cpu->cpu_index].start_pages = cpu->dirty_pages; + } else { + dirty_pages[cpu->cpu_index].end_pages = cpu->dirty_pages; + } +} + +static int64_t do_calculate_dirtyrate(DirtyPageRecord dirty_pages, + int64_t calc_time_ms) +{ + uint64_t increased_dirty_pages = + dirty_pages.end_pages - dirty_pages.start_pages; + + /* + * multiply by 1000ms/s _before_ converting down to megabytes + * to avoid losing precision + */ + return qemu_target_pages_to_MiB(increased_dirty_pages * 1000) / + calc_time_ms; +} + +void global_dirty_log_change(unsigned int flag, bool start) +{ + bql_lock(); + if (start) { + memory_global_dirty_log_start(flag); + } else { + memory_global_dirty_log_stop(flag); + } + bql_unlock(); +} + +/* + * global_dirty_log_sync + * 1. sync dirty log from kvm + * 2. stop dirty tracking if needed. + */ +static void global_dirty_log_sync(unsigned int flag, bool one_shot) +{ + bql_lock(); + memory_global_dirty_log_sync(false); + if (one_shot) { + memory_global_dirty_log_stop(flag); + } + bql_unlock(); +} + +static DirtyPageRecord *vcpu_dirty_stat_alloc(VcpuStat *stat) +{ + CPUState *cpu; + int nvcpu = 0; + + CPU_FOREACH(cpu) { + nvcpu++; + } + + stat->nvcpu = nvcpu; + stat->rates = g_new0(DirtyRateVcpu, nvcpu); + + return g_new0(DirtyPageRecord, nvcpu); +} + +static void vcpu_dirty_stat_collect(DirtyPageRecord *records, + bool start) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + record_dirtypages(records, cpu, start); + } +} + +int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms, + VcpuStat *stat, + unsigned int flag, + bool one_shot) +{ + DirtyPageRecord *records; + int64_t init_time_ms; + int64_t duration; + int64_t dirtyrate; + int i = 0; + unsigned int gen_id; + +retry: + init_time_ms = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + + WITH_QEMU_LOCK_GUARD(&qemu_cpu_list_lock) { + gen_id = cpu_list_generation_id_get(); + records = vcpu_dirty_stat_alloc(stat); + vcpu_dirty_stat_collect(records, true); + } + + duration = dirty_stat_wait(calc_time_ms, init_time_ms); + + global_dirty_log_sync(flag, one_shot); + + WITH_QEMU_LOCK_GUARD(&qemu_cpu_list_lock) { + if (gen_id != cpu_list_generation_id_get()) { + g_free(records); + g_free(stat->rates); + cpu_list_unlock(); + goto retry; + } + vcpu_dirty_stat_collect(records, false); + } + + for (i = 0; i < stat->nvcpu; i++) { + dirtyrate = do_calculate_dirtyrate(records[i], duration); + + stat->rates[i].id = i; + stat->rates[i].dirty_rate = dirtyrate; + + trace_dirtyrate_do_calculate_vcpu(i, dirtyrate); + } + + g_free(records); + + return duration; +} + +static bool is_calc_time_valid(int64_t msec) +{ + if ((msec < MIN_CALC_TIME_MS) || (msec > MAX_CALC_TIME_MS)) { return false; } @@ -87,7 +214,39 @@ static int dirtyrate_set_state(int *state, int old_state, int new_state) } } -static struct DirtyRateInfo *query_dirty_rate_info(void) +/* Decimal power of given time unit relative to one second */ +static int time_unit_to_power(TimeUnit time_unit) +{ + switch (time_unit) { + case TIME_UNIT_SECOND: + return 0; + case TIME_UNIT_MILLISECOND: + return -3; + default: + assert(false); /* unreachable */ + return 0; + } +} + +static int64_t convert_time_unit(int64_t value, TimeUnit unit_from, + TimeUnit unit_to) +{ + int power = time_unit_to_power(unit_from) - + time_unit_to_power(unit_to); + while (power < 0) { + value /= 10; + power += 1; + } + while (power > 0) { + value *= 10; + power -= 1; + } + return value; +} + + +static struct DirtyRateInfo * +query_dirty_rate_info(TimeUnit calc_time_unit) { int i; int64_t dirty_rate = DirtyStat.dirty_rate; @@ -96,7 +255,10 @@ static struct DirtyRateInfo *query_dirty_rate_info(void) info->status = CalculatingState; info->start_time = DirtyStat.start_time; - info->calc_time = DirtyStat.calc_time; + info->calc_time = convert_time_unit(DirtyStat.calc_time_ms, + TIME_UNIT_MILLISECOND, + calc_time_unit); + info->calc_time_unit = calc_time_unit; info->sample_pages = DirtyStat.sample_pages; info->mode = dirtyrate_mode; @@ -130,12 +292,11 @@ static struct DirtyRateInfo *query_dirty_rate_info(void) return info; } -static void init_dirtyrate_stat(int64_t start_time, - struct DirtyRateConfig config) +static void init_dirtyrate_stat(struct DirtyRateConfig config) { DirtyStat.dirty_rate = -1; - DirtyStat.start_time = start_time; - DirtyStat.calc_time = config.sample_period_seconds; + DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000; + DirtyStat.calc_time_ms = config.calc_time_ms; DirtyStat.sample_pages = config.sample_pages_per_gigabytes; switch (config.mode) { @@ -167,8 +328,8 @@ static void update_dirtyrate_stat(struct RamblockDirtyInfo *info) DirtyStat.page_sampling.total_dirty_samples += info->sample_dirty_count; DirtyStat.page_sampling.total_sample_count += info->sample_pages_count; /* size of total pages in MB */ - DirtyStat.page_sampling.total_block_mem_MB += (info->ramblock_pages * - TARGET_PAGE_SIZE) >> 20; + DirtyStat.page_sampling.total_block_mem_MB += + qemu_target_pages_to_MiB(info->ramblock_pages); } static void update_dirtyrate(uint64_t msec) @@ -184,6 +345,34 @@ static void update_dirtyrate(uint64_t msec) DirtyStat.dirty_rate = dirtyrate; } +/* + * Compute hash of a single page of size TARGET_PAGE_SIZE. + */ +static uint32_t compute_page_hash(void *ptr) +{ + size_t page_size = qemu_target_page_size(); + uint32_t i; + uint64_t v1, v2, v3, v4; + uint64_t res; + const uint64_t *p = ptr; + + v1 = QEMU_XXHASH_SEED + XXH_PRIME64_1 + XXH_PRIME64_2; + v2 = QEMU_XXHASH_SEED + XXH_PRIME64_2; + v3 = QEMU_XXHASH_SEED + 0; + v4 = QEMU_XXHASH_SEED - XXH_PRIME64_1; + for (i = 0; i < page_size / 8; i += 4) { + v1 = XXH64_round(v1, p[i + 0]); + v2 = XXH64_round(v2, p[i + 1]); + v3 = XXH64_round(v3, p[i + 2]); + v4 = XXH64_round(v4, p[i + 3]); + } + res = XXH64_mergerounds(v1, v2, v3, v4); + res += page_size; + res = XXH64_avalanche(res); + return (uint32_t)(res & UINT32_MAX); +} + + /* * get hash result for the sampled memory with length of TARGET_PAGE_SIZE * in ramblock, which starts from ramblock base address. @@ -191,13 +380,13 @@ static void update_dirtyrate(uint64_t msec) static uint32_t get_ramblock_vfn_hash(struct RamblockDirtyInfo *info, uint64_t vfn) { - uint32_t crc; + uint32_t hash; - crc = crc32(0, (info->ramblock_addr + - vfn * TARGET_PAGE_SIZE), TARGET_PAGE_SIZE); + hash = compute_page_hash(info->ramblock_addr + + vfn * qemu_target_page_size()); - trace_get_ramblock_vfn_hash(info->idstr, vfn, crc); - return crc; + trace_get_ramblock_vfn_hash(info->idstr, vfn, hash); + return hash; } static bool save_ramblock_hash(struct RamblockDirtyInfo *info) @@ -249,7 +438,7 @@ static void get_ramblock_dirty_info(RAMBlock *block, sample_pages_per_gigabytes) >> 30; /* Right shift TARGET_PAGE_BITS to calc page count */ info->ramblock_pages = qemu_ram_get_used_length(block) >> - TARGET_PAGE_BITS; + qemu_target_page_bits(); info->ramblock_addr = qemu_ram_get_host_addr(block); strcpy(info->idstr, qemu_ram_get_idstr(block)); } @@ -330,13 +519,13 @@ out: static void calc_page_dirty_rate(struct RamblockDirtyInfo *info) { - uint32_t crc; + uint32_t hash; int i; for (i = 0; i < info->sample_pages_count; i++) { - crc = get_ramblock_vfn_hash(info, info->sample_page_vfn[i]); - if (crc != info->hash_result[i]) { - trace_calc_page_dirty_rate(info->idstr, crc, info->hash_result[i]); + hash = get_ramblock_vfn_hash(info, info->sample_page_vfn[i]); + if (hash != info->hash_result[i]) { + trace_calc_page_dirty_rate(info->idstr, hash, info->hash_result[i]); info->sample_dirty_count++; } } @@ -347,7 +536,6 @@ find_block_matched(RAMBlock *block, int count, struct RamblockDirtyInfo *infos) { int i; - struct RamblockDirtyInfo *matched; for (i = 0; i < count; i++) { if (!strcmp(infos[i].idstr, qemu_ram_get_idstr(block))) { @@ -361,14 +549,12 @@ find_block_matched(RAMBlock *block, int count, if (infos[i].ramblock_addr != qemu_ram_get_host_addr(block) || infos[i].ramblock_pages != - (qemu_ram_get_used_length(block) >> TARGET_PAGE_BITS)) { + (qemu_ram_get_used_length(block) >> qemu_target_page_bits())) { trace_find_page_matched(block->idstr); return NULL; } - matched = &infos[i]; - - return matched; + return &infos[i]; } static bool compare_page_hash_info(struct RamblockDirtyInfo *info, @@ -396,44 +582,6 @@ static bool compare_page_hash_info(struct RamblockDirtyInfo *info, return true; } -static inline void record_dirtypages(DirtyPageRecord *dirty_pages, - CPUState *cpu, bool start) -{ - if (start) { - dirty_pages[cpu->cpu_index].start_pages = cpu->dirty_pages; - } else { - dirty_pages[cpu->cpu_index].end_pages = cpu->dirty_pages; - } -} - -static void dirtyrate_global_dirty_log_start(void) -{ - qemu_mutex_lock_iothread(); - memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE); - qemu_mutex_unlock_iothread(); -} - -static void dirtyrate_global_dirty_log_stop(void) -{ - qemu_mutex_lock_iothread(); - memory_global_dirty_log_sync(); - memory_global_dirty_log_stop(GLOBAL_DIRTY_DIRTY_RATE); - qemu_mutex_unlock_iothread(); -} - -static int64_t do_calculate_dirtyrate_vcpu(DirtyPageRecord dirty_pages) -{ - uint64_t memory_size_MB; - int64_t time_s; - uint64_t increased_dirty_pages = - dirty_pages.end_pages - dirty_pages.start_pages; - - memory_size_MB = (increased_dirty_pages * TARGET_PAGE_SIZE) >> 20; - time_s = DirtyStat.calc_time; - - return memory_size_MB / time_s; -} - static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages, bool start) { @@ -444,11 +592,6 @@ static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages, } } -static void do_calculate_dirtyrate_bitmap(DirtyPageRecord dirty_pages) -{ - DirtyStat.dirty_rate = do_calculate_dirtyrate_vcpu(dirty_pages); -} - static inline void dirtyrate_manual_reset_protect(void) { RAMBlock *block = NULL; @@ -463,11 +606,10 @@ static inline void dirtyrate_manual_reset_protect(void) static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config) { - int64_t msec = 0; int64_t start_time; DirtyPageRecord dirty_pages; - qemu_mutex_lock_iothread(); + bql_lock(); memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE); /* @@ -476,7 +618,7 @@ static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config) * skip it unconditionally and start dirty tracking * from 2'round of log sync */ - memory_global_dirty_log_sync(); + memory_global_dirty_log_sync(false); /* * reset page protect manually and unconditionally. @@ -484,106 +626,78 @@ static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config) * KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE cap is enabled. */ dirtyrate_manual_reset_protect(); - qemu_mutex_unlock_iothread(); + bql_unlock(); record_dirtypages_bitmap(&dirty_pages, true); start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - DirtyStat.start_time = start_time / 1000; + DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000; - msec = config.sample_period_seconds * 1000; - msec = set_sample_page_period(msec, start_time); - DirtyStat.calc_time = msec / 1000; + DirtyStat.calc_time_ms = dirty_stat_wait(config.calc_time_ms, start_time); /* - * dirtyrate_global_dirty_log_stop do two things. + * do two things. * 1. fetch dirty bitmap from kvm * 2. stop dirty tracking */ - dirtyrate_global_dirty_log_stop(); + global_dirty_log_sync(GLOBAL_DIRTY_DIRTY_RATE, true); record_dirtypages_bitmap(&dirty_pages, false); - do_calculate_dirtyrate_bitmap(dirty_pages); + DirtyStat.dirty_rate = do_calculate_dirtyrate(dirty_pages, + DirtyStat.calc_time_ms); } static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config) { - CPUState *cpu; - int64_t msec = 0; - int64_t start_time; uint64_t dirtyrate = 0; uint64_t dirtyrate_sum = 0; - DirtyPageRecord *dirty_pages; - int nvcpu = 0; int i = 0; - CPU_FOREACH(cpu) { - nvcpu++; - } + /* start log sync */ + global_dirty_log_change(GLOBAL_DIRTY_DIRTY_RATE, true); - dirty_pages = malloc(sizeof(*dirty_pages) * nvcpu); + DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000; - DirtyStat.dirty_ring.nvcpu = nvcpu; - DirtyStat.dirty_ring.rates = malloc(sizeof(DirtyRateVcpu) * nvcpu); - - dirtyrate_global_dirty_log_start(); - - CPU_FOREACH(cpu) { - record_dirtypages(dirty_pages, cpu, true); - } - - start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - DirtyStat.start_time = start_time / 1000; - - msec = config.sample_period_seconds * 1000; - msec = set_sample_page_period(msec, start_time); - DirtyStat.calc_time = msec / 1000; - - dirtyrate_global_dirty_log_stop(); - - CPU_FOREACH(cpu) { - record_dirtypages(dirty_pages, cpu, false); - } + /* calculate vcpu dirtyrate */ + DirtyStat.calc_time_ms = vcpu_calculate_dirtyrate(config.calc_time_ms, + &DirtyStat.dirty_ring, + GLOBAL_DIRTY_DIRTY_RATE, + true); + /* calculate vm dirtyrate */ for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) { - dirtyrate = do_calculate_dirtyrate_vcpu(dirty_pages[i]); - trace_dirtyrate_do_calculate_vcpu(i, dirtyrate); - - DirtyStat.dirty_ring.rates[i].id = i; + dirtyrate = DirtyStat.dirty_ring.rates[i].dirty_rate; DirtyStat.dirty_ring.rates[i].dirty_rate = dirtyrate; dirtyrate_sum += dirtyrate; } DirtyStat.dirty_rate = dirtyrate_sum; - free(dirty_pages); } static void calculate_dirtyrate_sample_vm(struct DirtyRateConfig config) { struct RamblockDirtyInfo *block_dinfo = NULL; int block_count = 0; - int64_t msec = 0; int64_t initial_time; rcu_read_lock(); initial_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000; if (!record_ramblock_hash_info(&block_dinfo, config, &block_count)) { goto out; } rcu_read_unlock(); - msec = config.sample_period_seconds * 1000; - msec = set_sample_page_period(msec, initial_time); - DirtyStat.start_time = initial_time / 1000; - DirtyStat.calc_time = msec / 1000; + DirtyStat.calc_time_ms = dirty_stat_wait(config.calc_time_ms, + initial_time); rcu_read_lock(); if (!compare_page_hash_info(block_dinfo, block_count)) { goto out; } - update_dirtyrate(msec); + update_dirtyrate(DirtyStat.calc_time_ms); out: rcu_read_unlock(); @@ -629,6 +743,8 @@ void *get_dirtyrate_thread(void *arg) } void qmp_calc_dirty_rate(int64_t calc_time, + bool has_calc_time_unit, + TimeUnit calc_time_unit, bool has_sample_pages, int64_t sample_pages, bool has_mode, @@ -638,7 +754,6 @@ void qmp_calc_dirty_rate(int64_t calc_time, static struct DirtyRateConfig config; QemuThread thread; int ret; - int64_t start_time; /* * If the dirty rate is already being measured, don't attempt to start. @@ -648,10 +763,15 @@ void qmp_calc_dirty_rate(int64_t calc_time, return; } - if (!is_sample_period_valid(calc_time)) { - error_setg(errp, "calc-time is out of range[%d, %d].", - MIN_FETCH_DIRTYRATE_TIME_SEC, - MAX_FETCH_DIRTYRATE_TIME_SEC); + int64_t calc_time_ms = convert_time_unit( + calc_time, + has_calc_time_unit ? calc_time_unit : TIME_UNIT_SECOND, + TIME_UNIT_MILLISECOND + ); + + if (!is_calc_time_valid(calc_time_ms)) { + error_setg(errp, "Calculation time is out of range [%dms, %dms].", + MIN_CALC_TIME_MS, MAX_CALC_TIME_MS); return; } @@ -659,8 +779,8 @@ void qmp_calc_dirty_rate(int64_t calc_time, mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING; } - if (has_sample_pages && mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) { - error_setg(errp, "either sample-pages or dirty-ring can be specified."); + if (has_sample_pages && mode != DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING) { + error_setg(errp, "sample-pages is used only in page-sampling mode"); return; } @@ -698,7 +818,7 @@ void qmp_calc_dirty_rate(int64_t calc_time, return; } - config.sample_period_seconds = calc_time; + config.calc_time_ms = calc_time_ms; config.sample_pages_per_gigabytes = sample_pages; config.mode = mode; @@ -710,28 +830,33 @@ void qmp_calc_dirty_rate(int64_t calc_time, **/ dirtyrate_mode = mode; - start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000; - init_dirtyrate_stat(start_time, config); + init_dirtyrate_stat(config); qemu_thread_create(&thread, "get_dirtyrate", get_dirtyrate_thread, (void *)&config, QEMU_THREAD_DETACHED); } -struct DirtyRateInfo *qmp_query_dirty_rate(Error **errp) + +struct DirtyRateInfo *qmp_query_dirty_rate(bool has_calc_time_unit, + TimeUnit calc_time_unit, + Error **errp) { - return query_dirty_rate_info(); + return query_dirty_rate_info( + has_calc_time_unit ? calc_time_unit : TIME_UNIT_SECOND); } void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict) { - DirtyRateInfo *info = query_dirty_rate_info(); + DirtyRateInfo *info = query_dirty_rate_info(TIME_UNIT_SECOND); monitor_printf(mon, "Status: %s\n", DirtyRateStatus_str(info->status)); monitor_printf(mon, "Start Time: %"PRIi64" (ms)\n", info->start_time); - monitor_printf(mon, "Sample Pages: %"PRIu64" (per GB)\n", - info->sample_pages); + if (info->mode == DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING) { + monitor_printf(mon, "Sample Pages: %"PRIu64" (per GB)\n", + info->sample_pages); + } monitor_printf(mon, "Period: %"PRIi64" (sec)\n", info->calc_time); monitor_printf(mon, "Mode: %s\n", @@ -782,8 +907,11 @@ void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict) mode = DIRTY_RATE_MEASURE_MODE_DIRTY_RING; } - qmp_calc_dirty_rate(sec, has_sample_pages, sample_pages, true, - mode, &err); + qmp_calc_dirty_rate(sec, /* calc-time */ + false, TIME_UNIT_SECOND, /* calc-time-unit */ + has_sample_pages, sample_pages, + true, mode, + &err); if (err) { hmp_handle_error(mon, err); return; diff --git a/migration/dirtyrate.h b/migration/dirtyrate.h index 69d4c5b865..869c060941 100644 --- a/migration/dirtyrate.h +++ b/migration/dirtyrate.h @@ -13,6 +13,8 @@ #ifndef QEMU_MIGRATION_DIRTYRATE_H #define QEMU_MIGRATION_DIRTYRATE_H +#include "sysemu/dirtyrate.h" + /* * Sample 512 pages per GB as default. */ @@ -29,10 +31,12 @@ #define MIN_RAMBLOCK_SIZE 128 /* - * Take 1s as minimum time for calculation duration + * Allowed range for dirty page rate calculation (in milliseconds). + * Lower limit relates to the smallest realistic downtime it + * makes sense to impose on migration. */ -#define MIN_FETCH_DIRTYRATE_TIME_SEC 1 -#define MAX_FETCH_DIRTYRATE_TIME_SEC 60 +#define MIN_CALC_TIME_MS 50 +#define MAX_CALC_TIME_MS 60000 /* * Take 1/16 pages in 1G as the maxmum sample page count @@ -42,7 +46,7 @@ struct DirtyRateConfig { uint64_t sample_pages_per_gigabytes; /* sample pages per GB */ - int64_t sample_period_seconds; /* time duration between two sampling */ + int64_t calc_time_ms; /* desired calculation time (in milliseconds) */ DirtyRateMeasureMode mode; /* mode of dirtyrate measurement */ }; @@ -65,18 +69,13 @@ typedef struct SampleVMStat { uint64_t total_block_mem_MB; /* size of total sampled pages in MB */ } SampleVMStat; -typedef struct VcpuStat { - int nvcpu; /* number of vcpu */ - DirtyRateVcpu *rates; /* array of dirty rate for each vcpu */ -} VcpuStat; - /* * Store calculation statistics for each measure. */ struct DirtyRateStat { int64_t dirty_rate; /* dirty rate in MB/s */ int64_t start_time; /* calculation start time in units of second */ - int64_t calc_time; /* time duration of two sampling in units of second */ + int64_t calc_time_ms; /* actual calculation time (in milliseconds) */ uint64_t sample_pages; /* sample pages per GB */ union { SampleVMStat page_sampling; diff --git a/migration/exec.c b/migration/exec.c index 375d2e1b54..20e6cccf8c 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -18,22 +18,38 @@ */ #include "qemu/osdep.h" +#include "qapi/type-helpers.h" +#include "qemu/error-report.h" #include "channel.h" #include "exec.h" #include "migration.h" #include "io/channel-command.h" #include "trace.h" +#include "qemu/cutils.h" - -void exec_start_outgoing_migration(MigrationState *s, const char *command, Error **errp) +#ifdef WIN32 +const char *exec_get_cmd_path(void) { - QIOChannel *ioc; - const char *argv[] = { "/bin/sh", "-c", command, NULL }; + g_autofree char *detected_path = g_new(char, MAX_PATH); + if (GetSystemDirectoryA(detected_path, MAX_PATH) == 0) { + warn_report("Could not detect cmd.exe path, using default."); + return "C:\\Windows\\System32\\cmd.exe"; + } + pstrcat(detected_path, MAX_PATH, "\\cmd.exe"); + return g_steal_pointer(&detected_path); +} +#endif - trace_migration_exec_outgoing(command); - ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv, - O_RDWR, - errp)); +void exec_start_outgoing_migration(MigrationState *s, strList *command, + Error **errp) +{ + QIOChannel *ioc = NULL; + g_auto(GStrv) argv = strv_from_str_list(command); + const char * const *args = (const char * const *) argv; + g_autofree char *new_command = g_strjoinv(" ", (char **)argv); + + trace_migration_exec_outgoing(new_command); + ioc = QIO_CHANNEL(qio_channel_command_new_spawn(args, O_RDWR, errp)); if (!ioc) { return; } @@ -52,15 +68,15 @@ static gboolean exec_accept_incoming_migration(QIOChannel *ioc, return G_SOURCE_REMOVE; } -void exec_start_incoming_migration(const char *command, Error **errp) +void exec_start_incoming_migration(strList *command, Error **errp) { QIOChannel *ioc; - const char *argv[] = { "/bin/sh", "-c", command, NULL }; + g_auto(GStrv) argv = strv_from_str_list(command); + const char * const *args = (const char * const *) argv; + g_autofree char *new_command = g_strjoinv(" ", (char **)argv); - trace_migration_exec_incoming(command); - ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv, - O_RDWR, - errp)); + trace_migration_exec_incoming(new_command); + ioc = QIO_CHANNEL(qio_channel_command_new_spawn(args, O_RDWR, errp)); if (!ioc) { return; } diff --git a/migration/exec.h b/migration/exec.h index b210ffde7a..3107f205e3 100644 --- a/migration/exec.h +++ b/migration/exec.h @@ -19,8 +19,12 @@ #ifndef QEMU_MIGRATION_EXEC_H #define QEMU_MIGRATION_EXEC_H -void exec_start_incoming_migration(const char *host_port, Error **errp); -void exec_start_outgoing_migration(MigrationState *s, const char *host_port, +#ifdef WIN32 +const char *exec_get_cmd_path(void); +#endif +void exec_start_incoming_migration(strList *host_port, Error **errp); + +void exec_start_outgoing_migration(MigrationState *s, strList *host_port, Error **errp); #endif diff --git a/migration/fd.c b/migration/fd.c index 6f2f50475f..449adaa2de 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "channel.h" #include "fd.h" +#include "file.h" #include "migration.h" #include "monitor/monitor.h" #include "io/channel-util.h" @@ -38,7 +39,7 @@ void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error ** return; } - qio_channel_set_name(QIO_CHANNEL(ioc), "migration-fd-outgoing"); + qio_channel_set_name(ioc, "migration-fd-outgoing"); migration_channel_connect(s, ioc, NULL, NULL); object_unref(OBJECT(ioc)); } @@ -68,7 +69,7 @@ void fd_start_incoming_migration(const char *fdname, Error **errp) return; } - qio_channel_set_name(QIO_CHANNEL(ioc), "migration-fd-incoming"); + qio_channel_set_name(ioc, "migration-fd-incoming"); qio_channel_add_watch_full(ioc, G_IO_IN, fd_accept_incoming_migration, NULL, NULL, diff --git a/migration/file.c b/migration/file.c new file mode 100644 index 0000000000..ab18ba505a --- /dev/null +++ b/migration/file.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2021-2023 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "exec/ramblock.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "channel.h" +#include "file.h" +#include "migration.h" +#include "io/channel-file.h" +#include "io/channel-socket.h" +#include "io/channel-util.h" +#include "options.h" +#include "trace.h" + +#define OFFSET_OPTION ",offset=" + +static struct FileOutgoingArgs { + char *fname; +} outgoing_args; + +/* Remove the offset option from @filespec and return it in @offsetp. */ + +int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp) +{ + char *option = strstr(filespec, OFFSET_OPTION); + int ret; + + if (option) { + *option = 0; + option += sizeof(OFFSET_OPTION) - 1; + ret = qemu_strtosz(option, NULL, offsetp); + if (ret) { + error_setg_errno(errp, -ret, "file URI has bad offset %s", option); + return -1; + } + } + return 0; +} + +void file_cleanup_outgoing_migration(void) +{ + g_free(outgoing_args.fname); + outgoing_args.fname = NULL; +} + +bool file_send_channel_create(gpointer opaque, Error **errp) +{ + QIOChannelFile *ioc; + int flags = O_WRONLY; + bool ret = true; + + ioc = qio_channel_file_new_path(outgoing_args.fname, flags, 0, errp); + if (!ioc) { + ret = false; + goto out; + } + + multifd_channel_connect(opaque, QIO_CHANNEL(ioc)); + +out: + /* + * File channel creation is synchronous. However posting this + * semaphore here is simpler than adding a special case. + */ + multifd_send_channel_created(); + + return ret; +} + +void file_start_outgoing_migration(MigrationState *s, + FileMigrationArgs *file_args, Error **errp) +{ + g_autoptr(QIOChannelFile) fioc = NULL; + g_autofree char *filename = g_strdup(file_args->filename); + uint64_t offset = file_args->offset; + QIOChannel *ioc; + + trace_migration_file_outgoing(filename); + + fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC, + 0600, errp); + if (!fioc) { + return; + } + + outgoing_args.fname = g_strdup(filename); + + ioc = QIO_CHANNEL(fioc); + if (offset && qio_channel_io_seek(ioc, offset, SEEK_SET, errp) < 0) { + return; + } + qio_channel_set_name(ioc, "migration-file-outgoing"); + migration_channel_connect(s, ioc, NULL, NULL); +} + +static gboolean file_accept_incoming_migration(QIOChannel *ioc, + GIOCondition condition, + gpointer opaque) +{ + migration_channel_process_incoming(ioc); + object_unref(OBJECT(ioc)); + return G_SOURCE_REMOVE; +} + +void file_create_incoming_channels(QIOChannel *ioc, Error **errp) +{ + int i, fd, channels = 1; + g_autofree QIOChannel **iocs = NULL; + + if (migrate_multifd()) { + channels += migrate_multifd_channels(); + } + + iocs = g_new0(QIOChannel *, channels); + fd = QIO_CHANNEL_FILE(ioc)->fd; + iocs[0] = ioc; + + for (i = 1; i < channels; i++) { + QIOChannelFile *fioc = qio_channel_file_new_dupfd(fd, errp); + + if (!fioc) { + while (i) { + object_unref(iocs[--i]); + } + return; + } + + iocs[i] = QIO_CHANNEL(fioc); + } + + for (i = 0; i < channels; i++) { + qio_channel_set_name(iocs[i], "migration-file-incoming"); + qio_channel_add_watch_full(iocs[i], G_IO_IN, + file_accept_incoming_migration, + NULL, NULL, + g_main_context_get_thread_default()); + } +} + +void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp) +{ + g_autofree char *filename = g_strdup(file_args->filename); + QIOChannelFile *fioc = NULL; + uint64_t offset = file_args->offset; + + trace_migration_file_incoming(filename); + + fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp); + if (!fioc) { + return; + } + + if (offset && + qio_channel_io_seek(QIO_CHANNEL(fioc), offset, SEEK_SET, errp) < 0) { + object_unref(OBJECT(fioc)); + return; + } + + file_create_incoming_channels(QIO_CHANNEL(fioc), errp); +} + +int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, + int niov, RAMBlock *block, Error **errp) +{ + ssize_t ret = 0; + int i, slice_idx, slice_num; + uintptr_t base, next, offset; + size_t len; + + slice_idx = 0; + slice_num = 1; + + /* + * If the iov array doesn't have contiguous elements, we need to + * split it in slices because we only have one file offset for the + * whole iov. Do this here so callers don't need to break the iov + * array themselves. + */ + for (i = 0; i < niov; i++, slice_num++) { + base = (uintptr_t) iov[i].iov_base; + + if (i != niov - 1) { + len = iov[i].iov_len; + next = (uintptr_t) iov[i + 1].iov_base; + + if (base + len == next) { + continue; + } + } + + /* + * Use the offset of the first element of the segment that + * we're sending. + */ + offset = (uintptr_t) iov[slice_idx].iov_base - (uintptr_t) block->host; + if (offset >= block->used_length) { + error_setg(errp, "offset %" PRIxPTR + "outside of ramblock %s range", offset, block->idstr); + ret = -1; + break; + } + + ret = qio_channel_pwritev(ioc, &iov[slice_idx], slice_num, + block->pages_offset + offset, errp); + if (ret < 0) { + break; + } + + slice_idx += slice_num; + slice_num = 0; + } + + return (ret < 0) ? ret : 0; +} + +int multifd_file_recv_data(MultiFDRecvParams *p, Error **errp) +{ + MultiFDRecvData *data = p->data; + size_t ret; + + ret = qio_channel_pread(p->c, (char *) data->opaque, + data->size, data->file_offset, errp); + if (ret != data->size) { + error_prepend(errp, + "multifd recv (%u): read 0x%zx, expected 0x%zx", + p->id, ret, data->size); + return -1; + } + + return 0; +} diff --git a/migration/file.h b/migration/file.h new file mode 100644 index 0000000000..7699c04677 --- /dev/null +++ b/migration/file.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021-2023 Oracle and/or its affiliates. + * + * 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_FILE_H +#define QEMU_MIGRATION_FILE_H + +#include "qapi/qapi-types-migration.h" +#include "io/task.h" +#include "channel.h" +#include "multifd.h" + +void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp); + +void file_start_outgoing_migration(MigrationState *s, + FileMigrationArgs *file_args, Error **errp); +int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp); +void file_cleanup_outgoing_migration(void); +bool file_send_channel_create(gpointer opaque, Error **errp); +void file_create_incoming_channels(QIOChannel *ioc, Error **errp); +int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, + int niov, RAMBlock *block, Error **errp); +int multifd_file_recv_data(MultiFDRecvParams *p, Error **errp); +#endif diff --git a/migration/global_state.c b/migration/global_state.c index a33947ca32..3a9796cae2 100644 --- a/migration/global_state.c +++ b/migration/global_state.c @@ -22,30 +22,42 @@ typedef struct { uint32_t size; - uint8_t runstate[100]; + + /* + * runstate was 100 bytes, zero padded, but we trimmed it to add a + * few fields and maintain backwards compatibility. + */ + uint8_t runstate[32]; + uint8_t has_vm_was_suspended; + uint8_t vm_was_suspended; + uint8_t unused[66]; + RunState state; bool received; } GlobalState; static GlobalState global_state; -int global_state_store(void) +static void global_state_do_store(RunState state) { - if (!runstate_store((char *)global_state.runstate, - sizeof(global_state.runstate))) { - error_report("runstate name too big: %s", global_state.runstate); - trace_migrate_state_too_big(); - return -EINVAL; - } - return 0; + const char *state_str = RunState_str(state); + assert(strlen(state_str) < sizeof(global_state.runstate)); + strpadcpy((char *)global_state.runstate, sizeof(global_state.runstate), + state_str, '\0'); + global_state.has_vm_was_suspended = true; + global_state.vm_was_suspended = vm_get_suspended(); + + memset(global_state.unused, 0, sizeof(global_state.unused)); +} + +void global_state_store(void) +{ + global_state_do_store(runstate_get()); } void global_state_store_running(void) { - const char *state = RunState_str(RUN_STATE_RUNNING); - assert(strlen(state) < sizeof(global_state.runstate)); - strpadcpy((char *)global_state.runstate, sizeof(global_state.runstate), - state, '\0'); + global_state_do_store(RUN_STATE_RUNNING); } bool global_state_received(void) @@ -60,24 +72,7 @@ RunState global_state_get_runstate(void) static bool global_state_needed(void *opaque) { - GlobalState *s = opaque; - char *runstate = (char *)s->runstate; - - /* If it is not optional, it is mandatory */ - - if (migrate_get_current()->store_global_state) { - return true; - } - - /* If state is running or paused, it is not needed */ - - if (strcmp(runstate, "running") == 0 || - strcmp(runstate, "paused") == 0) { - return false; - } - - /* for any other state it is needed */ - return true; + return migrate_get_current()->store_global_state; } static int global_state_post_load(void *opaque, int version_id) @@ -94,7 +89,7 @@ static int global_state_post_load(void *opaque, int version_id) sizeof(s->runstate)) == sizeof(s->runstate)) { /* * This condition should never happen during migration, because - * all runstate names are shorter than 100 bytes (the size of + * all runstate names are shorter than 32 bytes (the size of * s->runstate). However, a malicious stream could overflow * the qapi_enum_parse() call, so we force the last character * to a NUL byte. @@ -111,6 +106,14 @@ static int global_state_post_load(void *opaque, int version_id) } s->state = r; + /* + * global_state is saved on the outgoing side before forcing a stopped + * state, so it may have saved state=suspended and vm_was_suspended=0. + * Now we are in a paused state, and when we later call vm_start, it must + * restore the suspended state, so we must set vm_was_suspended=1 here. + */ + vm_set_suspended(s->vm_was_suspended || r == RUN_STATE_SUSPENDED); + return 0; } @@ -132,9 +135,12 @@ static const VMStateDescription vmstate_globalstate = { .post_load = global_state_post_load, .pre_save = global_state_pre_save, .needed = global_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(size, GlobalState), VMSTATE_BUFFER(runstate, GlobalState), + VMSTATE_UINT8(has_vm_was_suspended, GlobalState), + VMSTATE_UINT8(vm_was_suspended, GlobalState), + VMSTATE_BUFFER(unused, GlobalState), VMSTATE_END_OF_LIST() }, }; diff --git a/migration/meson.build b/migration/meson.build index 6880b61b10..1eeb915ff6 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -1,37 +1,47 @@ # Files needed by unit tests migration_files = files( + 'migration-stats.c', 'page_cache.c', 'xbzrle.c', 'vmstate-types.c', 'vmstate.c', - 'qemu-file-channel.c', 'qemu-file.c', 'yank_functions.c', ) -softmmu_ss.add(migration_files) -softmmu_ss.add(files( +system_ss.add(files( 'block-dirty-bitmap.c', 'channel.c', - 'colo-failover.c', - 'colo.c', + 'channel-block.c', + 'dirtyrate.c', 'exec.c', 'fd.c', + 'file.c', 'global_state.c', + 'migration-hmp-cmds.c', 'migration.c', 'multifd.c', 'multifd-zlib.c', + 'multifd-zero-page.c', + 'ram-compress.c', + 'options.c', 'postcopy-ram.c', 'savevm.c', 'socket.c', 'tls.c', + 'threadinfo.c', ), gnutls) -softmmu_ss.add(when: rdma, if_true: files('rdma.c')) -if get_option('live_block_migration').allowed() - softmmu_ss.add(files('block.c')) +if get_option('replication').allowed() + system_ss.add(files('colo-failover.c', 'colo.c')) endif -softmmu_ss.add(when: zstd, if_true: files('multifd-zstd.c')) -specific_ss.add(when: 'CONFIG_SOFTMMU', - if_true: files('dirtyrate.c', 'ram.c', 'target.c')) +system_ss.add(when: rdma, if_true: files('rdma.c')) +if get_option('live_block_migration').allowed() + system_ss.add(files('block.c')) +endif +system_ss.add(when: zstd, if_true: files('multifd-zstd.c')) + +specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', + if_true: files('ram.c', + 'target.c')) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c new file mode 100644 index 0000000000..7e96ae6ffd --- /dev/null +++ b/migration/migration-hmp-cmds.c @@ -0,0 +1,896 @@ +/* + * HMP commands related to migration + * + * 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. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "block/qapi.h" +#include "migration/snapshot.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-migration.h" +#include "qapi/qapi-visit-migration.h" +#include "qapi/qmp/qdict.h" +#include "qapi/string-input-visitor.h" +#include "qapi/string-output-visitor.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qemu/sockets.h" +#include "sysemu/runstate.h" +#include "ui/qemu-spice.h" +#include "sysemu/sysemu.h" +#include "options.h" +#include "migration.h" + +static void migration_global_dump(Monitor *mon) +{ + MigrationState *ms = migrate_get_current(); + + monitor_printf(mon, "globals:\n"); + monitor_printf(mon, "store-global-state: %s\n", + ms->store_global_state ? "on" : "off"); + monitor_printf(mon, "only-migratable: %s\n", + only_migratable ? "on" : "off"); + monitor_printf(mon, "send-configuration: %s\n", + ms->send_configuration ? "on" : "off"); + monitor_printf(mon, "send-section-footer: %s\n", + ms->send_section_footer ? "on" : "off"); + monitor_printf(mon, "decompress-error-check: %s\n", + ms->decompress_error_check ? "on" : "off"); + monitor_printf(mon, "clear-bitmap-shift: %u\n", + ms->clear_bitmap_shift); +} + +void hmp_info_migrate(Monitor *mon, const QDict *qdict) +{ + MigrationInfo *info; + + info = qmp_query_migrate(NULL); + + migration_global_dump(mon); + + if (info->blocked_reasons) { + strList *reasons = info->blocked_reasons; + monitor_printf(mon, "Outgoing migration blocked:\n"); + while (reasons) { + monitor_printf(mon, " %s\n", reasons->value); + reasons = reasons->next; + } + } + + if (info->has_status) { + monitor_printf(mon, "Migration status: %s", + MigrationStatus_str(info->status)); + if (info->status == MIGRATION_STATUS_FAILED && info->error_desc) { + monitor_printf(mon, " (%s)\n", info->error_desc); + } else { + monitor_printf(mon, "\n"); + } + + monitor_printf(mon, "total time: %" PRIu64 " ms\n", + info->total_time); + if (info->has_expected_downtime) { + monitor_printf(mon, "expected downtime: %" PRIu64 " ms\n", + info->expected_downtime); + } + if (info->has_downtime) { + monitor_printf(mon, "downtime: %" PRIu64 " ms\n", + info->downtime); + } + if (info->has_setup_time) { + monitor_printf(mon, "setup: %" PRIu64 " ms\n", + info->setup_time); + } + } + + if (info->ram) { + monitor_printf(mon, "transferred ram: %" PRIu64 " kbytes\n", + info->ram->transferred >> 10); + monitor_printf(mon, "throughput: %0.2f mbps\n", + info->ram->mbps); + monitor_printf(mon, "remaining ram: %" PRIu64 " kbytes\n", + info->ram->remaining >> 10); + monitor_printf(mon, "total ram: %" PRIu64 " kbytes\n", + info->ram->total >> 10); + monitor_printf(mon, "duplicate: %" PRIu64 " pages\n", + info->ram->duplicate); + monitor_printf(mon, "skipped: %" PRIu64 " pages\n", + info->ram->skipped); + monitor_printf(mon, "normal: %" PRIu64 " pages\n", + info->ram->normal); + monitor_printf(mon, "normal bytes: %" PRIu64 " kbytes\n", + info->ram->normal_bytes >> 10); + monitor_printf(mon, "dirty sync count: %" PRIu64 "\n", + info->ram->dirty_sync_count); + monitor_printf(mon, "page size: %" PRIu64 " kbytes\n", + info->ram->page_size >> 10); + monitor_printf(mon, "multifd bytes: %" PRIu64 " kbytes\n", + info->ram->multifd_bytes >> 10); + monitor_printf(mon, "pages-per-second: %" PRIu64 "\n", + info->ram->pages_per_second); + + if (info->ram->dirty_pages_rate) { + monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n", + info->ram->dirty_pages_rate); + } + if (info->ram->postcopy_requests) { + monitor_printf(mon, "postcopy request count: %" PRIu64 "\n", + info->ram->postcopy_requests); + } + if (info->ram->precopy_bytes) { + monitor_printf(mon, "precopy ram: %" PRIu64 " kbytes\n", + info->ram->precopy_bytes >> 10); + } + if (info->ram->downtime_bytes) { + monitor_printf(mon, "downtime ram: %" PRIu64 " kbytes\n", + info->ram->downtime_bytes >> 10); + } + if (info->ram->postcopy_bytes) { + monitor_printf(mon, "postcopy ram: %" PRIu64 " kbytes\n", + info->ram->postcopy_bytes >> 10); + } + if (info->ram->dirty_sync_missed_zero_copy) { + monitor_printf(mon, + "Zero-copy-send fallbacks happened: %" PRIu64 " times\n", + info->ram->dirty_sync_missed_zero_copy); + } + } + + if (info->disk) { + monitor_printf(mon, "transferred disk: %" PRIu64 " kbytes\n", + info->disk->transferred >> 10); + monitor_printf(mon, "remaining disk: %" PRIu64 " kbytes\n", + info->disk->remaining >> 10); + monitor_printf(mon, "total disk: %" PRIu64 " kbytes\n", + info->disk->total >> 10); + } + + if (info->xbzrle_cache) { + monitor_printf(mon, "cache size: %" PRIu64 " bytes\n", + info->xbzrle_cache->cache_size); + monitor_printf(mon, "xbzrle transferred: %" PRIu64 " kbytes\n", + info->xbzrle_cache->bytes >> 10); + monitor_printf(mon, "xbzrle pages: %" PRIu64 " pages\n", + info->xbzrle_cache->pages); + monitor_printf(mon, "xbzrle cache miss: %" PRIu64 " pages\n", + info->xbzrle_cache->cache_miss); + monitor_printf(mon, "xbzrle cache miss rate: %0.2f\n", + info->xbzrle_cache->cache_miss_rate); + monitor_printf(mon, "xbzrle encoding rate: %0.2f\n", + info->xbzrle_cache->encoding_rate); + monitor_printf(mon, "xbzrle overflow: %" PRIu64 "\n", + info->xbzrle_cache->overflow); + } + + if (info->compression) { + monitor_printf(mon, "compression pages: %" PRIu64 " pages\n", + info->compression->pages); + monitor_printf(mon, "compression busy: %" PRIu64 "\n", + info->compression->busy); + monitor_printf(mon, "compression busy rate: %0.2f\n", + info->compression->busy_rate); + monitor_printf(mon, "compressed size: %" PRIu64 " kbytes\n", + info->compression->compressed_size >> 10); + monitor_printf(mon, "compression rate: %0.2f\n", + info->compression->compression_rate); + } + + if (info->has_cpu_throttle_percentage) { + monitor_printf(mon, "cpu throttle percentage: %" PRIu64 "\n", + info->cpu_throttle_percentage); + } + + if (info->has_dirty_limit_throttle_time_per_round) { + monitor_printf(mon, "dirty-limit throttle time: %" PRIu64 " us\n", + info->dirty_limit_throttle_time_per_round); + } + + if (info->has_dirty_limit_ring_full_time) { + monitor_printf(mon, "dirty-limit ring full time: %" PRIu64 " us\n", + info->dirty_limit_ring_full_time); + } + + if (info->has_postcopy_blocktime) { + monitor_printf(mon, "postcopy blocktime: %u\n", + info->postcopy_blocktime); + } + + if (info->has_postcopy_vcpu_blocktime) { + Visitor *v; + char *str; + v = string_output_visitor_new(false, &str); + visit_type_uint32List(v, NULL, &info->postcopy_vcpu_blocktime, + &error_abort); + visit_complete(v, &str); + monitor_printf(mon, "postcopy vcpu blocktime: %s\n", str); + g_free(str); + visit_free(v); + } + if (info->has_socket_address) { + SocketAddressList *addr; + + monitor_printf(mon, "socket address: [\n"); + + for (addr = info->socket_address; addr; addr = addr->next) { + char *s = socket_uri(addr->value); + monitor_printf(mon, "\t%s\n", s); + g_free(s); + } + monitor_printf(mon, "]\n"); + } + + if (info->vfio) { + monitor_printf(mon, "vfio device transferred: %" PRIu64 " kbytes\n", + info->vfio->transferred >> 10); + } + + qapi_free_MigrationInfo(info); +} + +void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict) +{ + MigrationCapabilityStatusList *caps, *cap; + + caps = qmp_query_migrate_capabilities(NULL); + + if (caps) { + for (cap = caps; cap; cap = cap->next) { + monitor_printf(mon, "%s: %s\n", + MigrationCapability_str(cap->value->capability), + cap->value->state ? "on" : "off"); + } + } + + qapi_free_MigrationCapabilityStatusList(caps); +} + +void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) +{ + MigrationParameters *params; + + params = qmp_query_migrate_parameters(NULL); + + if (params) { + monitor_printf(mon, "%s: %" PRIu64 " ms\n", + MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_INITIAL), + params->announce_initial); + monitor_printf(mon, "%s: %" PRIu64 " ms\n", + MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_MAX), + params->announce_max); + monitor_printf(mon, "%s: %" PRIu64 "\n", + MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_ROUNDS), + params->announce_rounds); + monitor_printf(mon, "%s: %" PRIu64 " ms\n", + MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_STEP), + params->announce_step); + assert(params->has_compress_level); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_LEVEL), + params->compress_level); + assert(params->has_compress_threads); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_THREADS), + params->compress_threads); + assert(params->has_compress_wait_thread); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD), + params->compress_wait_thread ? "on" : "off"); + assert(params->has_decompress_threads); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_DECOMPRESS_THREADS), + params->decompress_threads); + assert(params->has_throttle_trigger_threshold); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD), + params->throttle_trigger_threshold); + assert(params->has_cpu_throttle_initial); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL), + params->cpu_throttle_initial); + assert(params->has_cpu_throttle_increment); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT), + params->cpu_throttle_increment); + assert(params->has_cpu_throttle_tailslow); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW), + params->cpu_throttle_tailslow ? "on" : "off"); + assert(params->has_max_cpu_throttle); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_MAX_CPU_THROTTLE), + params->max_cpu_throttle); + assert(params->tls_creds); + monitor_printf(mon, "%s: '%s'\n", + MigrationParameter_str(MIGRATION_PARAMETER_TLS_CREDS), + params->tls_creds); + assert(params->tls_hostname); + monitor_printf(mon, "%s: '%s'\n", + MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME), + params->tls_hostname); + assert(params->has_max_bandwidth); + monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n", + MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH), + params->max_bandwidth); + assert(params->has_avail_switchover_bandwidth); + monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n", + MigrationParameter_str(MIGRATION_PARAMETER_AVAIL_SWITCHOVER_BANDWIDTH), + params->avail_switchover_bandwidth); + assert(params->has_downtime_limit); + monitor_printf(mon, "%s: %" PRIu64 " ms\n", + MigrationParameter_str(MIGRATION_PARAMETER_DOWNTIME_LIMIT), + params->downtime_limit); + assert(params->has_x_checkpoint_delay); + monitor_printf(mon, "%s: %u ms\n", + MigrationParameter_str(MIGRATION_PARAMETER_X_CHECKPOINT_DELAY), + params->x_checkpoint_delay); + assert(params->has_block_incremental); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_BLOCK_INCREMENTAL), + params->block_incremental ? "on" : "off"); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_CHANNELS), + params->multifd_channels); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_COMPRESSION), + MultiFDCompression_str(params->multifd_compression)); + assert(params->has_zero_page_detection); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_ZERO_PAGE_DETECTION), + qapi_enum_lookup(&ZeroPageDetection_lookup, + params->zero_page_detection)); + monitor_printf(mon, "%s: %" PRIu64 " bytes\n", + MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE), + params->xbzrle_cache_size); + monitor_printf(mon, "%s: %" PRIu64 "\n", + MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH), + params->max_postcopy_bandwidth); + monitor_printf(mon, "%s: '%s'\n", + MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ), + params->tls_authz); + + if (params->has_block_bitmap_mapping) { + const BitmapMigrationNodeAliasList *bmnal; + + monitor_printf(mon, "%s:\n", + MigrationParameter_str( + MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING)); + + for (bmnal = params->block_bitmap_mapping; + bmnal; + bmnal = bmnal->next) + { + const BitmapMigrationNodeAlias *bmna = bmnal->value; + const BitmapMigrationBitmapAliasList *bmbal; + + monitor_printf(mon, " '%s' -> '%s'\n", + bmna->node_name, bmna->alias); + + for (bmbal = bmna->bitmaps; bmbal; bmbal = bmbal->next) { + const BitmapMigrationBitmapAlias *bmba = bmbal->value; + + monitor_printf(mon, " '%s' -> '%s'\n", + bmba->name, bmba->alias); + } + } + } + + monitor_printf(mon, "%s: %" PRIu64 " ms\n", + MigrationParameter_str(MIGRATION_PARAMETER_X_VCPU_DIRTY_LIMIT_PERIOD), + params->x_vcpu_dirty_limit_period); + + monitor_printf(mon, "%s: %" PRIu64 " MB/s\n", + MigrationParameter_str(MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT), + params->vcpu_dirty_limit); + + assert(params->has_mode); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_MODE), + qapi_enum_lookup(&MigMode_lookup, params->mode)); + } + + qapi_free_MigrationParameters(params); +} + +void hmp_loadvm(Monitor *mon, const QDict *qdict) +{ + RunState saved_state = runstate_get(); + + const char *name = qdict_get_str(qdict, "name"); + Error *err = NULL; + + vm_stop(RUN_STATE_RESTORE_VM); + + if (load_snapshot(name, NULL, false, NULL, &err)) { + load_snapshot_resume(saved_state); + } + + hmp_handle_error(mon, err); +} + +void hmp_savevm(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + save_snapshot(qdict_get_try_str(qdict, "name"), + true, NULL, false, NULL, &err); + hmp_handle_error(mon, err); +} + +void hmp_delvm(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *name = qdict_get_str(qdict, "name"); + + delete_snapshot(name, false, NULL, &err); + hmp_handle_error(mon, err); +} + +void hmp_migrate_cancel(Monitor *mon, const QDict *qdict) +{ + qmp_migrate_cancel(NULL); +} + +void hmp_migrate_continue(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *state = qdict_get_str(qdict, "state"); + int val = qapi_enum_parse(&MigrationStatus_lookup, state, -1, &err); + + if (val >= 0) { + qmp_migrate_continue(val, &err); + } + + hmp_handle_error(mon, err); +} + +void hmp_migrate_incoming(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *uri = qdict_get_str(qdict, "uri"); + MigrationChannelList *caps = NULL; + g_autoptr(MigrationChannel) channel = NULL; + + if (!migrate_uri_parse(uri, &channel, &err)) { + goto end; + } + QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel)); + + qmp_migrate_incoming(NULL, true, caps, &err); + qapi_free_MigrationChannelList(caps); + +end: + hmp_handle_error(mon, err); +} + +void hmp_migrate_recover(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *uri = qdict_get_str(qdict, "uri"); + + qmp_migrate_recover(uri, &err); + + hmp_handle_error(mon, err); +} + +void hmp_migrate_pause(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qmp_migrate_pause(&err); + + hmp_handle_error(mon, err); +} + + +void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict) +{ + const char *cap = qdict_get_str(qdict, "capability"); + bool state = qdict_get_bool(qdict, "state"); + Error *err = NULL; + MigrationCapabilityStatusList *caps = NULL; + MigrationCapabilityStatus *value; + int val; + + val = qapi_enum_parse(&MigrationCapability_lookup, cap, -1, &err); + if (val < 0) { + goto end; + } + + value = g_malloc0(sizeof(*value)); + value->capability = val; + value->state = state; + QAPI_LIST_PREPEND(caps, value); + qmp_migrate_set_capabilities(caps, &err); + qapi_free_MigrationCapabilityStatusList(caps); + +end: + hmp_handle_error(mon, err); +} + +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) +{ + const char *param = qdict_get_str(qdict, "parameter"); + const char *valuestr = qdict_get_str(qdict, "value"); + Visitor *v = string_input_visitor_new(valuestr); + MigrateSetParameters *p = g_new0(MigrateSetParameters, 1); + uint64_t valuebw = 0; + uint64_t cache_size; + Error *err = NULL; + int val, ret; + + val = qapi_enum_parse(&MigrationParameter_lookup, param, -1, &err); + if (val < 0) { + goto cleanup; + } + + switch (val) { + case MIGRATION_PARAMETER_COMPRESS_LEVEL: + p->has_compress_level = true; + visit_type_uint8(v, param, &p->compress_level, &err); + break; + case MIGRATION_PARAMETER_COMPRESS_THREADS: + p->has_compress_threads = true; + visit_type_uint8(v, param, &p->compress_threads, &err); + break; + case MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD: + p->has_compress_wait_thread = true; + visit_type_bool(v, param, &p->compress_wait_thread, &err); + break; + case MIGRATION_PARAMETER_DECOMPRESS_THREADS: + p->has_decompress_threads = true; + visit_type_uint8(v, param, &p->decompress_threads, &err); + break; + case MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD: + p->has_throttle_trigger_threshold = true; + visit_type_uint8(v, param, &p->throttle_trigger_threshold, &err); + break; + case MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL: + p->has_cpu_throttle_initial = true; + visit_type_uint8(v, param, &p->cpu_throttle_initial, &err); + break; + case MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT: + p->has_cpu_throttle_increment = true; + visit_type_uint8(v, param, &p->cpu_throttle_increment, &err); + break; + case MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW: + p->has_cpu_throttle_tailslow = true; + visit_type_bool(v, param, &p->cpu_throttle_tailslow, &err); + break; + case MIGRATION_PARAMETER_MAX_CPU_THROTTLE: + p->has_max_cpu_throttle = true; + visit_type_uint8(v, param, &p->max_cpu_throttle, &err); + break; + case MIGRATION_PARAMETER_TLS_CREDS: + p->tls_creds = g_new0(StrOrNull, 1); + p->tls_creds->type = QTYPE_QSTRING; + visit_type_str(v, param, &p->tls_creds->u.s, &err); + break; + case MIGRATION_PARAMETER_TLS_HOSTNAME: + p->tls_hostname = g_new0(StrOrNull, 1); + p->tls_hostname->type = QTYPE_QSTRING; + visit_type_str(v, param, &p->tls_hostname->u.s, &err); + break; + case MIGRATION_PARAMETER_TLS_AUTHZ: + p->tls_authz = g_new0(StrOrNull, 1); + p->tls_authz->type = QTYPE_QSTRING; + visit_type_str(v, param, &p->tls_authz->u.s, &err); + break; + case MIGRATION_PARAMETER_MAX_BANDWIDTH: + p->has_max_bandwidth = true; + /* + * Can't use visit_type_size() here, because it + * defaults to Bytes rather than Mebibytes. + */ + ret = qemu_strtosz_MiB(valuestr, NULL, &valuebw); + if (ret < 0 || valuebw > INT64_MAX + || (size_t)valuebw != valuebw) { + error_setg(&err, "Invalid size %s", valuestr); + break; + } + p->max_bandwidth = valuebw; + break; + case MIGRATION_PARAMETER_AVAIL_SWITCHOVER_BANDWIDTH: + p->has_avail_switchover_bandwidth = true; + ret = qemu_strtosz_MiB(valuestr, NULL, &valuebw); + if (ret < 0 || valuebw > INT64_MAX + || (size_t)valuebw != valuebw) { + error_setg(&err, "Invalid size %s", valuestr); + break; + } + p->avail_switchover_bandwidth = valuebw; + break; + case MIGRATION_PARAMETER_DOWNTIME_LIMIT: + p->has_downtime_limit = true; + visit_type_size(v, param, &p->downtime_limit, &err); + break; + case MIGRATION_PARAMETER_X_CHECKPOINT_DELAY: + p->has_x_checkpoint_delay = true; + visit_type_uint32(v, param, &p->x_checkpoint_delay, &err); + break; + case MIGRATION_PARAMETER_BLOCK_INCREMENTAL: + p->has_block_incremental = true; + visit_type_bool(v, param, &p->block_incremental, &err); + break; + case MIGRATION_PARAMETER_MULTIFD_CHANNELS: + p->has_multifd_channels = true; + visit_type_uint8(v, param, &p->multifd_channels, &err); + break; + case MIGRATION_PARAMETER_MULTIFD_COMPRESSION: + p->has_multifd_compression = true; + visit_type_MultiFDCompression(v, param, &p->multifd_compression, + &err); + break; + case MIGRATION_PARAMETER_MULTIFD_ZLIB_LEVEL: + p->has_multifd_zlib_level = true; + visit_type_uint8(v, param, &p->multifd_zlib_level, &err); + break; + case MIGRATION_PARAMETER_MULTIFD_ZSTD_LEVEL: + p->has_multifd_zstd_level = true; + visit_type_uint8(v, param, &p->multifd_zstd_level, &err); + break; + case MIGRATION_PARAMETER_ZERO_PAGE_DETECTION: + p->has_zero_page_detection = true; + visit_type_ZeroPageDetection(v, param, &p->zero_page_detection, &err); + break; + case MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE: + p->has_xbzrle_cache_size = true; + if (!visit_type_size(v, param, &cache_size, &err)) { + break; + } + if (cache_size > INT64_MAX || (size_t)cache_size != cache_size) { + error_setg(&err, "Invalid size %s", valuestr); + break; + } + p->xbzrle_cache_size = cache_size; + break; + case MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH: + p->has_max_postcopy_bandwidth = true; + visit_type_size(v, param, &p->max_postcopy_bandwidth, &err); + break; + case MIGRATION_PARAMETER_ANNOUNCE_INITIAL: + p->has_announce_initial = true; + visit_type_size(v, param, &p->announce_initial, &err); + break; + case MIGRATION_PARAMETER_ANNOUNCE_MAX: + p->has_announce_max = true; + visit_type_size(v, param, &p->announce_max, &err); + break; + case MIGRATION_PARAMETER_ANNOUNCE_ROUNDS: + p->has_announce_rounds = true; + visit_type_size(v, param, &p->announce_rounds, &err); + break; + case MIGRATION_PARAMETER_ANNOUNCE_STEP: + p->has_announce_step = true; + visit_type_size(v, param, &p->announce_step, &err); + break; + case MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING: + error_setg(&err, "The block-bitmap-mapping parameter can only be set " + "through QMP"); + break; + case MIGRATION_PARAMETER_X_VCPU_DIRTY_LIMIT_PERIOD: + p->has_x_vcpu_dirty_limit_period = true; + visit_type_size(v, param, &p->x_vcpu_dirty_limit_period, &err); + break; + case MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT: + p->has_vcpu_dirty_limit = true; + visit_type_size(v, param, &p->vcpu_dirty_limit, &err); + break; + case MIGRATION_PARAMETER_MODE: + p->has_mode = true; + visit_type_MigMode(v, param, &p->mode, &err); + break; + default: + assert(0); + } + + if (err) { + goto cleanup; + } + + qmp_migrate_set_parameters(p, &err); + + cleanup: + qapi_free_MigrateSetParameters(p); + visit_free(v); + hmp_handle_error(mon, err); +} + +void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + qmp_migrate_start_postcopy(&err); + hmp_handle_error(mon, err); +} + +#ifdef CONFIG_REPLICATION +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); +} +#endif + +typedef struct HMPMigrationStatus { + QEMUTimer *timer; + Monitor *mon; +} HMPMigrationStatus; + +static void hmp_migrate_status_cb(void *opaque) +{ + HMPMigrationStatus *status = opaque; + MigrationInfo *info; + + info = qmp_query_migrate(NULL); + if (!info->has_status || info->status == MIGRATION_STATUS_ACTIVE || + info->status == MIGRATION_STATUS_SETUP) { + if (info->disk) { + int progress; + + if (info->disk->remaining) { + progress = info->disk->transferred * 100 / info->disk->total; + } else { + progress = 100; + } + + monitor_printf(status->mon, "Completed %d %%\r", progress); + monitor_flush(status->mon); + } + + timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000); + } else { + if (migrate_block()) { + monitor_printf(status->mon, "\n"); + } + if (info->error_desc) { + error_report("%s", info->error_desc); + } + monitor_resume(status->mon); + timer_free(status->timer); + g_free(status); + } + + qapi_free_MigrationInfo(info); +} + +void hmp_migrate(Monitor *mon, const QDict *qdict) +{ + bool detach = qdict_get_try_bool(qdict, "detach", false); + bool blk = qdict_get_try_bool(qdict, "blk", false); + bool inc = qdict_get_try_bool(qdict, "inc", false); + bool resume = qdict_get_try_bool(qdict, "resume", false); + const char *uri = qdict_get_str(qdict, "uri"); + Error *err = NULL; + g_autoptr(MigrationChannelList) caps = NULL; + g_autoptr(MigrationChannel) channel = NULL; + + if (inc) { + warn_report("option '-i' is deprecated;" + " use blockdev-mirror with NBD instead"); + } + + if (blk) { + warn_report("option '-b' is deprecated;" + " use blockdev-mirror with NBD instead"); + } + + if (!migrate_uri_parse(uri, &channel, &err)) { + hmp_handle_error(mon, err); + return; + } + QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel)); + + qmp_migrate(NULL, true, caps, !!blk, blk, !!inc, inc, + false, false, true, resume, &err); + if (hmp_handle_error(mon, err)) { + return; + } + + if (!detach) { + HMPMigrationStatus *status; + + if (monitor_suspend(mon) < 0) { + monitor_printf(mon, "terminal does not allow synchronous " + "migration, continuing detached\n"); + return; + } + + status = g_malloc0(sizeof(*status)); + status->mon = mon; + status->timer = timer_new_ms(QEMU_CLOCK_REALTIME, hmp_migrate_status_cb, + status); + timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); + } +} + +void migrate_set_capability_completion(ReadLineState *rs, int nb_args, + const char *str) +{ + size_t len; + + len = strlen(str); + readline_set_completion_index(rs, len); + if (nb_args == 2) { + int i; + for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { + readline_add_completion_of(rs, str, MigrationCapability_str(i)); + } + } else if (nb_args == 3) { + readline_add_completion_of(rs, str, "on"); + readline_add_completion_of(rs, str, "off"); + } +} + +void migrate_set_parameter_completion(ReadLineState *rs, int nb_args, + const char *str) +{ + size_t len; + + len = strlen(str); + readline_set_completion_index(rs, len); + if (nb_args == 2) { + int i; + for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) { + readline_add_completion_of(rs, str, MigrationParameter_str(i)); + } + } +} + +static void vm_completion(ReadLineState *rs, const char *str) +{ + size_t len; + BlockDriverState *bs; + BdrvNextIterator it; + + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + len = strlen(str); + readline_set_completion_index(rs, len); + + for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + SnapshotInfoList *snapshots, *snapshot; + bool ok = false; + + if (bdrv_can_snapshot(bs)) { + ok = bdrv_query_snapshot_info_list(bs, &snapshots, NULL) == 0; + } + if (!ok) { + continue; + } + + snapshot = snapshots; + while (snapshot) { + readline_add_completion_of(rs, str, snapshot->value->name); + readline_add_completion_of(rs, str, snapshot->value->id); + snapshot = snapshot->next; + } + qapi_free_SnapshotInfoList(snapshots); + } + +} + +void delvm_completion(ReadLineState *rs, int nb_args, const char *str) +{ + if (nb_args == 2) { + vm_completion(rs, str); + } +} + +void loadvm_completion(ReadLineState *rs, int nb_args, const char *str) +{ + if (nb_args == 2) { + vm_completion(rs, str); + } +} diff --git a/migration/migration-stats.c b/migration/migration-stats.c new file mode 100644 index 0000000000..f690b98a03 --- /dev/null +++ b/migration/migration-stats.c @@ -0,0 +1,70 @@ +/* + * Migration stats + * + * Copyright (c) 2012-2023 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/stats64.h" +#include "qemu-file.h" +#include "trace.h" +#include "migration-stats.h" + +MigrationAtomicStats mig_stats; + +bool migration_rate_exceeded(QEMUFile *f) +{ + if (qemu_file_get_error(f)) { + return true; + } + + uint64_t rate_limit_max = migration_rate_get(); + if (rate_limit_max == RATE_LIMIT_DISABLED) { + return false; + } + + uint64_t rate_limit_start = stat64_get(&mig_stats.rate_limit_start); + uint64_t rate_limit_current = migration_transferred_bytes(); + uint64_t rate_limit_used = rate_limit_current - rate_limit_start; + + if (rate_limit_max > 0 && rate_limit_used > rate_limit_max) { + return true; + } + return false; +} + +uint64_t migration_rate_get(void) +{ + return stat64_get(&mig_stats.rate_limit_max); +} + +#define XFER_LIMIT_RATIO (1000 / BUFFER_DELAY) + +void migration_rate_set(uint64_t limit) +{ + /* + * 'limit' is per second. But we check it each BUFFER_DELAY milliseconds. + */ + stat64_set(&mig_stats.rate_limit_max, limit / XFER_LIMIT_RATIO); +} + +void migration_rate_reset(void) +{ + stat64_set(&mig_stats.rate_limit_start, migration_transferred_bytes()); +} + +uint64_t migration_transferred_bytes(void) +{ + uint64_t multifd = stat64_get(&mig_stats.multifd_bytes); + uint64_t rdma = stat64_get(&mig_stats.rdma_bytes); + uint64_t qemu_file = stat64_get(&mig_stats.qemu_file_transferred); + + trace_migration_transferred_bytes(qemu_file, multifd, rdma); + return qemu_file + multifd + rdma; +} diff --git a/migration/migration-stats.h b/migration/migration-stats.h new file mode 100644 index 0000000000..05290ade76 --- /dev/null +++ b/migration/migration-stats.h @@ -0,0 +1,139 @@ +/* + * Migration stats + * + * Copyright (c) 2012-2023 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_STATS_H +#define QEMU_MIGRATION_STATS_H + +#include "qemu/stats64.h" + +/* + * Amount of time to allocate to each "chunk" of bandwidth-throttled + * data. + */ +#define BUFFER_DELAY 100 + +/* + * If rate_limit_max is 0, there is special code to remove the rate + * limit. + */ +#define RATE_LIMIT_DISABLED 0 + +/* + * These are the ram migration statistic counters. It is loosely + * based on MigrationStats. We change to Stat64 any counter that + * needs to be updated using atomic ops (can be accessed by more than + * one thread). + */ +typedef struct { + /* + * Number of bytes that were dirty last time that we synced with + * the guest memory. We use that to calculate the downtime. As + * the remaining dirty amounts to what we know that is still dirty + * since last iteration, not counting what the guest has dirtied + * since we synchronized bitmaps. + */ + Stat64 dirty_bytes_last_sync; + /* + * Number of pages dirtied per second. + */ + Stat64 dirty_pages_rate; + /* + * Number of times we have synchronized guest bitmaps. + */ + Stat64 dirty_sync_count; + /* + * Number of times zero copy failed to send any page using zero + * copy. + */ + Stat64 dirty_sync_missed_zero_copy; + /* + * Number of bytes sent at migration completion stage while the + * guest is stopped. + */ + Stat64 downtime_bytes; + /* + * Number of bytes sent through multifd channels. + */ + Stat64 multifd_bytes; + /* + * Number of pages transferred that were not full of zeros. + */ + Stat64 normal_pages; + /* + * Number of bytes sent during postcopy. + */ + Stat64 postcopy_bytes; + /* + * Number of postcopy page faults that we have handled during + * postcopy stage. + */ + Stat64 postcopy_requests; + /* + * Number of bytes sent during precopy stage. + */ + Stat64 precopy_bytes; + /* + * Number of bytes transferred with QEMUFile. + */ + Stat64 qemu_file_transferred; + /* + * Amount of transferred data at the start of current cycle. + */ + Stat64 rate_limit_start; + /* + * Maximum amount of data we can send in a cycle. + */ + Stat64 rate_limit_max; + /* + * Number of bytes sent through RDMA. + */ + Stat64 rdma_bytes; + /* + * Number of pages transferred that were full of zeros. + */ + Stat64 zero_pages; +} MigrationAtomicStats; + +extern MigrationAtomicStats mig_stats; + +/** + * migration_rate_get: Get the maximum amount that can be transferred. + * + * Returns the maximum number of bytes that can be transferred in a cycle. + */ +uint64_t migration_rate_get(void); + +/** + * migration_rate_reset: Reset the rate limit counter. + * + * This is called when we know we start a new transfer cycle. + */ +void migration_rate_reset(void); + +/** + * migration_rate_set: Set the maximum amount that can be transferred. + * + * Sets the maximum amount of bytes that can be transferred in one cycle. + * + * @new_rate: new maximum amount + */ +void migration_rate_set(uint64_t new_rate); + +/** + * migration_transferred_bytes: Return number of bytes transferred + * + * Returns how many bytes have we transferred since the beginning of + * the migration. It accounts for bytes sent through any migration + * channel, multifd, qemu_file, rdma, .... + */ +uint64_t migration_transferred_bytes(void); +#endif diff --git a/migration/migration.c b/migration/migration.c index 31739b2af9..86bf76e925 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -20,18 +20,21 @@ #include "migration/blocker.h" #include "exec.h" #include "fd.h" +#include "file.h" #include "socket.h" #include "sysemu/runstate.h" #include "sysemu/sysemu.h" #include "sysemu/cpu-throttle.h" #include "rdma.h" #include "ram.h" +#include "ram-compress.h" #include "migration/global_state.h" #include "migration/misc.h" #include "migration.h" +#include "migration-stats.h" #include "savevm.h" -#include "qemu-file-channel.h" #include "qemu-file.h" +#include "channel.h" #include "migration/vmstate.h" #include "block/block.h" #include "qapi/error.h" @@ -49,75 +52,30 @@ #include "trace.h" #include "exec/target_page.h" #include "io/channel-buffer.h" +#include "io/channel-tls.h" #include "migration/colo.h" #include "hw/boards.h" -#include "hw/qdev-properties.h" -#include "hw/qdev-properties-system.h" #include "monitor/monitor.h" #include "net/announce.h" #include "qemu/queue.h" #include "multifd.h" +#include "threadinfo.h" #include "qemu/yank.h" #include "sysemu/cpus.h" #include "yank_functions.h" #include "sysemu/qtest.h" +#include "options.h" +#include "sysemu/dirtylimit.h" +#include "qemu/sockets.h" +#include "sysemu/kvm.h" -#define MAX_THROTTLE (128 << 20) /* Migration transfer speed throttling */ +#define NOTIFIER_ELEM_INIT(array, elem) \ + [elem] = NOTIFIER_WITH_RETURN_LIST_INITIALIZER((array)[elem]) -/* Amount of time to allocate to each "chunk" of bandwidth-throttled - * data. */ -#define BUFFER_DELAY 100 -#define XFER_LIMIT_RATIO (1000 / BUFFER_DELAY) - -/* Time in milliseconds we are allowed to stop the source, - * for sending the last part */ -#define DEFAULT_MIGRATE_SET_DOWNTIME 300 - -/* Maximum migrate downtime set to 2000 seconds */ -#define MAX_MIGRATE_DOWNTIME_SECONDS 2000 -#define MAX_MIGRATE_DOWNTIME (MAX_MIGRATE_DOWNTIME_SECONDS * 1000) - -/* Default compression thread count */ -#define DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT 8 -/* Default decompression thread count, usually decompression is at - * least 4 times as fast as compression.*/ -#define DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT 2 -/*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 - -/* Migration XBZRLE default cache size */ -#define DEFAULT_MIGRATE_XBZRLE_CACHE_SIZE (64 * 1024 * 1024) - -/* 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. - */ -#define DEFAULT_MIGRATE_MAX_POSTCOPY_BANDWIDTH 0 - -/* - * Parameters for self_announce_delay giving a stream of RARP/ARP - * packets after migration. - */ -#define DEFAULT_MIGRATE_ANNOUNCE_INITIAL 50 -#define DEFAULT_MIGRATE_ANNOUNCE_MAX 550 -#define DEFAULT_MIGRATE_ANNOUNCE_ROUNDS 5 -#define DEFAULT_MIGRATE_ANNOUNCE_STEP 100 - -static NotifierList migration_state_notifiers = - NOTIFIER_LIST_INITIALIZER(migration_state_notifiers); +static NotifierWithReturnList migration_state_notifiers[] = { + NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_NORMAL), + NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_CPR_REBOOT), +}; /* Messages sent on the return path from destination to source */ enum mig_rp_message_type { @@ -129,42 +87,11 @@ enum mig_rp_message_type { MIG_RP_MSG_REQ_PAGES, /* data (start: be64, len: be32) */ MIG_RP_MSG_RECV_BITMAP, /* send recved_bitmap back to source */ MIG_RP_MSG_RESUME_ACK, /* tell source that we are ready to resume */ + MIG_RP_MSG_SWITCHOVER_ACK, /* Tell source it's OK to do switchover */ MIG_RP_MSG_MAX }; -/* Migration capabilities set */ -struct MigrateCapsSet { - int size; /* Capability set size */ - MigrationCapability caps[]; /* Variadic array of capabilities */ -}; -typedef struct MigrateCapsSet MigrateCapsSet; - -/* Define and initialize MigrateCapsSet */ -#define INITIALIZE_MIGRATE_CAPS_SET(_name, ...) \ - MigrateCapsSet _name = { \ - .size = sizeof((int []) { __VA_ARGS__ }) / sizeof(int), \ - .caps = { __VA_ARGS__ } \ - } - -/* Background-snapshot compatibility check list */ -static const -INITIALIZE_MIGRATE_CAPS_SET(check_caps_background_snapshot, - MIGRATION_CAPABILITY_POSTCOPY_RAM, - MIGRATION_CAPABILITY_DIRTY_BITMAPS, - MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME, - MIGRATION_CAPABILITY_LATE_BLOCK_ACTIVATE, - MIGRATION_CAPABILITY_RETURN_PATH, - MIGRATION_CAPABILITY_MULTIFD, - MIGRATION_CAPABILITY_PAUSE_BEFORE_SWITCHOVER, - MIGRATION_CAPABILITY_AUTO_CONVERGE, - MIGRATION_CAPABILITY_RELEASE_RAM, - MIGRATION_CAPABILITY_RDMA_PIN_ALL, - MIGRATION_CAPABILITY_COMPRESS, - MIGRATION_CAPABILITY_XBZRLE, - MIGRATION_CAPABILITY_X_COLO, - MIGRATION_CAPABILITY_VALIDATE_UUID); - /* When we add fault tolerance, we could have several migrations at once. For now we don't need to add dynamic creation of migration */ @@ -172,24 +99,88 @@ INITIALIZE_MIGRATE_CAPS_SET(check_caps_background_snapshot, static MigrationState *current_migration; static MigrationIncomingState *current_incoming; -static GSList *migration_blockers; +static GSList *migration_blockers[MIG_MODE__MAX]; static bool migration_object_check(MigrationState *ms, Error **errp); static int migration_maybe_pause(MigrationState *s, int *current_active_state, int new_state); static void migrate_fd_cancel(MigrationState *s); +static bool close_return_path_on_source(MigrationState *s); +static void migration_completion_end(MigrationState *s); -static bool migrate_allow_multi_channels = true; - -void migrate_protocol_allow_multi_channels(bool allow) +static void migration_downtime_start(MigrationState *s) { - migrate_allow_multi_channels = allow; + trace_vmstate_downtime_checkpoint("src-downtime-start"); + s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); } -bool migrate_multi_channels_is_allowed(void) +static void migration_downtime_end(MigrationState *s) { - return migrate_allow_multi_channels; + int64_t now = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + + /* + * If downtime already set, should mean that postcopy already set it, + * then that should be the real downtime already. + */ + if (!s->downtime) { + s->downtime = now - s->downtime_start; + } + + trace_vmstate_downtime_checkpoint("src-downtime-end"); +} + +static bool migration_needs_multiple_sockets(void) +{ + return migrate_multifd() || migrate_postcopy_preempt(); +} + +static bool transport_supports_multi_channels(MigrationAddress *addr) +{ + if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) { + SocketAddress *saddr = &addr->u.socket; + + return (saddr->type == SOCKET_ADDRESS_TYPE_INET || + saddr->type == SOCKET_ADDRESS_TYPE_UNIX || + saddr->type == SOCKET_ADDRESS_TYPE_VSOCK); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) { + return migrate_mapped_ram(); + } else { + return false; + } +} + +static bool migration_needs_seekable_channel(void) +{ + return migrate_mapped_ram(); +} + +static bool transport_supports_seeking(MigrationAddress *addr) +{ + if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) { + return true; + } + + return false; +} + +static bool +migration_channels_and_transport_compatible(MigrationAddress *addr, + Error **errp) +{ + if (migration_needs_seekable_channel() && + !transport_supports_seeking(addr)) { + error_setg(errp, "Migration requires seekable transport (e.g. file)"); + return false; + } + + if (migration_needs_multiple_sockets() && + !transport_supports_multi_channels(addr)) { + error_setg(errp, "Migration requires multi-channel URIs (e.g. tcp)"); + return false; + } + + return true; } static gint page_request_addr_cmp(gconstpointer ap, gconstpointer bp) @@ -199,6 +190,23 @@ static gint page_request_addr_cmp(gconstpointer ap, gconstpointer bp) return (a > b) - (a < b); } +static int migration_stop_vm(MigrationState *s, RunState state) +{ + int ret; + + migration_downtime_start(s); + + s->vm_old_state = runstate_get(); + global_state_store(); + + ret = vm_stop_force_state(state); + + trace_vmstate_downtime_checkpoint("src-vm-stopped"); + trace_migration_completion_vm_stop(ret); + + return ret; +} + void migration_object_init(void) { /* This can only be called once. */ @@ -215,10 +223,15 @@ void migration_object_init(void) current_incoming->postcopy_remote_fds = g_array_new(FALSE, TRUE, sizeof(struct PostCopyFD)); qemu_mutex_init(¤t_incoming->rp_mutex); + qemu_mutex_init(¤t_incoming->postcopy_prio_thread_mutex); qemu_event_init(¤t_incoming->main_thread_load_event, false); qemu_sem_init(¤t_incoming->postcopy_pause_sem_dst, 0); qemu_sem_init(¤t_incoming->postcopy_pause_sem_fault, 0); + qemu_sem_init(¤t_incoming->postcopy_pause_sem_fast_load, 0); + qemu_sem_init(¤t_incoming->postcopy_qemufile_dst_done, 0); + qemu_mutex_init(¤t_incoming->page_request_mutex); + qemu_cond_init(¤t_incoming->page_request_cond); current_incoming->page_requested = g_tree_new(page_request_addr_cmp); migration_object_check(current_migration, &error_fatal); @@ -228,11 +241,55 @@ void migration_object_init(void) dirty_bitmap_mig_init(); } +typedef struct { + QEMUBH *bh; + QEMUBHFunc *cb; + void *opaque; +} MigrationBH; + +static void migration_bh_dispatch_bh(void *opaque) +{ + MigrationState *s = migrate_get_current(); + MigrationBH *migbh = opaque; + + /* cleanup this BH */ + qemu_bh_delete(migbh->bh); + migbh->bh = NULL; + + /* dispatch the other one */ + migbh->cb(migbh->opaque); + object_unref(OBJECT(s)); + + g_free(migbh); +} + +void migration_bh_schedule(QEMUBHFunc *cb, void *opaque) +{ + MigrationState *s = migrate_get_current(); + MigrationBH *migbh = g_new0(MigrationBH, 1); + QEMUBH *bh = qemu_bh_new(migration_bh_dispatch_bh, migbh); + + /* Store these to dispatch when the BH runs */ + migbh->bh = bh; + migbh->cb = cb; + migbh->opaque = opaque; + + /* + * Ref the state for bh, because it may be called when + * there're already no other refs + */ + object_ref(OBJECT(s)); + qemu_bh_schedule(bh); +} + void migration_cancel(const Error *error) { if (error) { migrate_set_error(current_migration, error); } + if (migrate_dirty_limit()) { + qmp_cancel_vcpu_dirty_limit(false, -1, NULL); + } migrate_fd_cancel(current_migration); } @@ -296,6 +353,9 @@ void migration_incoming_state_destroy(void) { struct MigrationIncomingState *mis = migration_incoming_get_current(); + multifd_recv_cleanup(); + compress_threads_load_cleanup(); + if (mis->to_src_file) { /* Tell source that we are done */ migrate_send_rp_shut(mis, qemu_file_get_error(mis->from_src_file) != 0); @@ -321,26 +381,22 @@ void migration_incoming_state_destroy(void) mis->page_requested = NULL; } + if (mis->postcopy_qemufile_dst) { + migration_ioc_unregister_yank_from_file(mis->postcopy_qemufile_dst); + qemu_fclose(mis->postcopy_qemufile_dst); + mis->postcopy_qemufile_dst = NULL; + } + yank_unregister_instance(MIGRATION_YANK_INSTANCE); } static void migrate_generate_event(int new_state) { - if (migrate_use_events()) { + if (migrate_events()) { qapi_event_send_migration(new_state); } } -static bool migrate_late_block_activate(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[ - MIGRATION_CAPABILITY_LATE_BLOCK_ACTIVATE]; -} - /* * Send a message on the return channel back to the source * of the migration. @@ -366,12 +422,7 @@ static int migrate_send_rp_message(MigrationIncomingState *mis, qemu_put_be16(mis->to_src_file, (unsigned int)message_type); qemu_put_be16(mis->to_src_file, len); qemu_put_buffer(mis->to_src_file, data, len); - qemu_fflush(mis->to_src_file); - - /* It's possible that qemu file got error during sending */ - ret = qemu_file_get_error(mis->to_src_file); - - return ret; + return qemu_fflush(mis->to_src_file); } /* Request one page from the source VM at the given start address. @@ -431,7 +482,7 @@ int migrate_send_rp_req_pages(MigrationIncomingState *mis, * things like g_tree_lookup() will return TRUE (1) when found. */ g_tree_insert(mis->page_requested, aligned, (gpointer)1); - mis->page_requested_count++; + qatomic_inc(&mis->page_requested_count); trace_postcopy_page_req_add(aligned, mis->page_requested_count); } } @@ -461,6 +512,18 @@ void migration_incoming_disable_colo(void) int migration_incoming_enable_colo(void) { +#ifndef CONFIG_REPLICATION + error_report("ENABLE_COLO command come in migration stream, but COLO " + "module is not built in"); + return -ENOTSUP; +#endif + + if (!migrate_colo()) { + error_report("ENABLE_COLO command come in migration stream, but c-colo " + "capability is not set"); + return -EINVAL; + } + if (ram_block_discard_disable(true)) { error_report("COLO: cannot disable RAM discard"); return -EBUSY; @@ -477,25 +540,131 @@ void migrate_add_address(SocketAddress *address) QAPI_CLONE(SocketAddress, address)); } -static void qemu_start_incoming_migration(const char *uri, Error **errp) +bool migrate_uri_parse(const char *uri, MigrationChannel **channel, + Error **errp) { - const char *p = NULL; + g_autoptr(MigrationChannel) val = g_new0(MigrationChannel, 1); + g_autoptr(MigrationAddress) addr = g_new0(MigrationAddress, 1); + InetSocketAddress *isock = &addr->u.rdma; + strList **tail = &addr->u.exec.args; - migrate_protocol_allow_multi_channels(false); /* reset it anyway */ - qapi_event_send_migration(MIGRATION_STATUS_SETUP); - if (strstart(uri, "tcp:", &p) || - strstart(uri, "unix:", NULL) || - strstart(uri, "vsock:", NULL)) { - migrate_protocol_allow_multi_channels(true); - socket_start_incoming_migration(p ? p : uri, errp); -#ifdef CONFIG_RDMA - } else if (strstart(uri, "rdma:", &p)) { - rdma_start_incoming_migration(p, errp); + if (strstart(uri, "exec:", NULL)) { + addr->transport = MIGRATION_ADDRESS_TYPE_EXEC; +#ifdef WIN32 + QAPI_LIST_APPEND(tail, g_strdup(exec_get_cmd_path())); + QAPI_LIST_APPEND(tail, g_strdup("/c")); +#else + QAPI_LIST_APPEND(tail, g_strdup("/bin/sh")); + QAPI_LIST_APPEND(tail, g_strdup("-c")); #endif - } else if (strstart(uri, "exec:", &p)) { - exec_start_incoming_migration(p, errp); - } else if (strstart(uri, "fd:", &p)) { - fd_start_incoming_migration(p, errp); + QAPI_LIST_APPEND(tail, g_strdup(uri + strlen("exec:"))); + } else if (strstart(uri, "rdma:", NULL)) { + if (inet_parse(isock, uri + strlen("rdma:"), errp)) { + qapi_free_InetSocketAddress(isock); + return false; + } + addr->transport = MIGRATION_ADDRESS_TYPE_RDMA; + } else if (strstart(uri, "tcp:", NULL) || + strstart(uri, "unix:", NULL) || + strstart(uri, "vsock:", NULL) || + strstart(uri, "fd:", NULL)) { + addr->transport = MIGRATION_ADDRESS_TYPE_SOCKET; + SocketAddress *saddr = socket_parse(uri, errp); + if (!saddr) { + return false; + } + addr->u.socket.type = saddr->type; + addr->u.socket.u = saddr->u; + /* Don't free the objects inside; their ownership moved to "addr" */ + g_free(saddr); + } else if (strstart(uri, "file:", NULL)) { + addr->transport = MIGRATION_ADDRESS_TYPE_FILE; + addr->u.file.filename = g_strdup(uri + strlen("file:")); + if (file_parse_offset(addr->u.file.filename, &addr->u.file.offset, + errp)) { + return false; + } + } else { + error_setg(errp, "unknown migration protocol: %s", uri); + return false; + } + + val->channel_type = MIGRATION_CHANNEL_TYPE_MAIN; + val->addr = g_steal_pointer(&addr); + *channel = g_steal_pointer(&val); + return true; +} + +static void qemu_start_incoming_migration(const char *uri, bool has_channels, + MigrationChannelList *channels, + Error **errp) +{ + g_autoptr(MigrationChannel) channel = NULL; + MigrationAddress *addr = NULL; + MigrationIncomingState *mis = migration_incoming_get_current(); + + /* + * Having preliminary checks for uri and channel + */ + if (!uri == !channels) { + error_setg(errp, "need either 'uri' or 'channels' argument"); + return; + } + + if (channels) { + /* To verify that Migrate channel list has only item */ + if (channels->next) { + error_setg(errp, "Channel list has more than one entries"); + return; + } + addr = channels->value->addr; + } + + if (uri) { + /* caller uses the old URI syntax */ + if (!migrate_uri_parse(uri, &channel, errp)) { + return; + } + addr = channel->addr; + } + + /* transport mechanism not suitable for migration? */ + if (!migration_channels_and_transport_compatible(addr, errp)) { + return; + } + + migrate_set_state(&mis->state, MIGRATION_STATUS_NONE, + MIGRATION_STATUS_SETUP); + + if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) { + SocketAddress *saddr = &addr->u.socket; + if (saddr->type == SOCKET_ADDRESS_TYPE_INET || + saddr->type == SOCKET_ADDRESS_TYPE_UNIX || + saddr->type == SOCKET_ADDRESS_TYPE_VSOCK) { + socket_start_incoming_migration(saddr, errp); + } else if (saddr->type == SOCKET_ADDRESS_TYPE_FD) { + fd_start_incoming_migration(saddr->u.fd.str, errp); + } +#ifdef CONFIG_RDMA + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) { + if (migrate_compress()) { + error_setg(errp, "RDMA and compression can't be used together"); + return; + } + if (migrate_xbzrle()) { + error_setg(errp, "RDMA and XBZRLE can't be used together"); + return; + } + if (migrate_multifd()) { + error_setg(errp, "RDMA and multifd can't be used together"); + return; + } + rdma_start_incoming_migration(&addr->u.rdma, errp); +#endif + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_EXEC) { + exec_start_incoming_migration(addr->u.exec.args, errp); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) { + file_start_incoming_migration(&addr->u.file, errp); } else { error_setg(errp, "unknown migration protocol: %s", uri); } @@ -506,6 +675,8 @@ static void process_incoming_migration_bh(void *opaque) Error *local_err = NULL; MigrationIncomingState *mis = opaque; + trace_vmstate_downtime_checkpoint("dst-precopy-bh-enter"); + /* If capability late_block_activate is set: * Only fire up the block code now if we're going to restart the * VM, else 'cont' will do it. @@ -514,7 +685,7 @@ static void process_incoming_migration_bh(void *opaque) */ if (!migrate_late_block_activate() || (autostart && (!global_state_received() || - global_state_get_runstate() == RUN_STATE_RUNNING))) { + runstate_is_live(global_state_get_runstate())))) { /* Make sure all file formats throw away their mutable metadata. * If we get an error here, just don't restart the VM yet. */ bdrv_activate_all(&local_err); @@ -531,18 +702,14 @@ static void process_incoming_migration_bh(void *opaque) */ qemu_announce_self(&mis->announce_timer, migrate_announce_params()); - if (multifd_load_cleanup(&local_err) != 0) { - error_report_err(local_err); - autostart = false; - } - /* If global state section was not received or we are in running - state, we need to obey autostart. Any other state is set with - runstate_set. */ + trace_vmstate_downtime_checkpoint("dst-precopy-bh-announced"); + + multifd_recv_shutdown(); dirty_bitmap_mig_before_vm_start(); if (!global_state_received() || - global_state_get_runstate() == RUN_STATE_RUNNING) { + runstate_is_live(global_state_get_runstate())) { if (autostart) { vm_start(); } else { @@ -554,6 +721,7 @@ static void process_incoming_migration_bh(void *opaque) } else { runstate_set(global_state_get_runstate()); } + trace_vmstate_downtime_checkpoint("dst-precopy-bh-vm-started"); /* * This must happen after any state changes since as soon as an external * observer sees this event they might start to prod at the VM assuming @@ -561,24 +729,33 @@ static void process_incoming_migration_bh(void *opaque) */ migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_COMPLETED); - qemu_bh_delete(mis->bh); migration_incoming_state_destroy(); } -static void process_incoming_migration_co(void *opaque) +static void coroutine_fn +process_incoming_migration_co(void *opaque) { MigrationIncomingState *mis = migration_incoming_get_current(); PostcopyState ps; int ret; - Error *local_err = NULL; assert(mis->from_src_file); - mis->migration_incoming_co = qemu_coroutine_self(); + + if (compress_threads_load_setup(mis->from_src_file)) { + error_report("Failed to setup decompress threads"); + goto fail; + } + mis->largest_page_size = qemu_ram_pagesize_largest(); postcopy_state_set(POSTCOPY_INCOMING_NONE); - migrate_set_state(&mis->state, MIGRATION_STATUS_NONE, + migrate_set_state(&mis->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE); + + mis->loadvm_co = qemu_coroutine_self(); ret = qemu_loadvm_state(mis->from_src_file); + mis->loadvm_co = NULL; + + trace_vmstate_downtime_checkpoint("dst-precopy-loadvm-completed"); ps = postcopy_state_get(); trace_process_incoming_migration_co_end(ret, ps); @@ -601,67 +778,47 @@ static void process_incoming_migration_co(void *opaque) /* Else if something went wrong then just fall out of the normal exit */ } - /* we get COLO info, and know if we are in COLO mode */ - if (!ret && migration_incoming_colo_enabled()) { - /* Make sure all file formats throw away their mutable metadata */ - bdrv_activate_all(&local_err); - if (local_err) { - error_report_err(local_err); - goto fail; - } - - qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming", - colo_process_incoming_thread, mis, QEMU_THREAD_JOINABLE); - mis->have_colo_incoming_thread = true; - qemu_coroutine_yield(); - - qemu_mutex_unlock_iothread(); - /* Wait checkpoint incoming thread exit before free resource */ - qemu_thread_join(&mis->colo_incoming_thread); - qemu_mutex_lock_iothread(); - /* We hold the global iothread lock, so it is safe here */ - colo_release_ram_cache(); - } - if (ret < 0) { + MigrationState *s = migrate_get_current(); + + if (migrate_has_error(s)) { + WITH_QEMU_LOCK_GUARD(&s->error_mutex) { + error_report_err(s->error); + } + } error_report("load of migration failed: %s", strerror(-ret)); goto fail; } - mis->bh = qemu_bh_new(process_incoming_migration_bh, mis); - qemu_bh_schedule(mis->bh); - mis->migration_incoming_co = NULL; + + if (colo_incoming_co() < 0) { + goto fail; + } + + migration_bh_schedule(process_incoming_migration_bh, mis); return; fail: - local_err = NULL; migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_FAILED); qemu_fclose(mis->from_src_file); - if (multifd_load_cleanup(&local_err) != 0) { - error_report_err(local_err); - } + + multifd_recv_cleanup(); + compress_threads_load_cleanup(); + exit(EXIT_FAILURE); } /** * migration_incoming_setup: Setup incoming migration * @f: file for main migration channel - * @errp: where to put errors - * - * Returns: %true on success, %false on error. */ -static bool migration_incoming_setup(QEMUFile *f, Error **errp) +static void migration_incoming_setup(QEMUFile *f) { MigrationIncomingState *mis = migration_incoming_get_current(); - if (multifd_load_setup(errp) != 0) { - return false; - } - if (!mis->from_src_file) { mis->from_src_file = f; } qemu_file_set_blocking(f, false); - return true; } void migration_incoming_process(void) @@ -691,9 +848,9 @@ static bool postcopy_try_recover(void) /* * Here, we only wake up the main loading thread (while the - * fault thread will still be waiting), so that we can receive + * rest threads will still be waiting), so that we can receive * commands from source now, and answer it if needed. The - * fault thread will be woken up afterwards until we are sure + * rest threads will be woken up afterwards until we are sure * that source is ready to reply to page requests. */ qemu_sem_post(&mis->postcopy_pause_sem_dst); @@ -703,47 +860,98 @@ static bool postcopy_try_recover(void) return false; } -void migration_fd_process_incoming(QEMUFile *f, Error **errp) +void migration_fd_process_incoming(QEMUFile *f) { - if (!migration_incoming_setup(f, errp)) { - return; - } + migration_incoming_setup(f); if (postcopy_try_recover()) { return; } migration_incoming_process(); } +/* + * Returns true when we want to start a new incoming migration process, + * false otherwise. + */ +static bool migration_should_start_incoming(bool main_channel) +{ + /* Multifd doesn't start unless all channels are established */ + if (migrate_multifd()) { + return migration_has_all_channels(); + } + + /* Preempt channel only starts when the main channel is created */ + if (migrate_postcopy_preempt()) { + return main_channel; + } + + /* + * For all the rest types of migration, we should only reach here when + * it's the main channel that's being created, and we should always + * proceed with this channel. + */ + assert(main_channel); + return true; +} + void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) { MigrationIncomingState *mis = migration_incoming_get_current(); Error *local_err = NULL; - bool start_migration; + QEMUFile *f; + bool default_channel = true; + uint32_t channel_magic = 0; + int ret = 0; - if (!mis->from_src_file) { - /* The first connection (multifd may have multiple) */ - QEMUFile *f = qemu_fopen_channel_input(ioc); + if (migrate_multifd() && !migrate_mapped_ram() && + !migrate_postcopy_ram() && + qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_READ_MSG_PEEK)) { + /* + * With multiple channels, it is possible that we receive channels + * out of order on destination side, causing incorrect mapping of + * source channels on destination side. Check channel MAGIC to + * decide type of channel. Please note this is best effort, postcopy + * preempt channel does not send any magic number so avoid it for + * postcopy live migration. Also tls live migration already does + * tls handshake while initializing main channel so with tls this + * issue is not possible. + */ + ret = migration_channel_read_peek(ioc, (void *)&channel_magic, + sizeof(channel_magic), errp); - if (!migration_incoming_setup(f, errp)) { + if (ret != 0) { return; } - /* - * Common migration only needs one channel, so we can start - * right now. Multifd needs more than one channel, we wait. - */ - start_migration = !migrate_use_multifd(); + default_channel = (channel_magic == cpu_to_be32(QEMU_VM_FILE_MAGIC)); + } else { + default_channel = !mis->from_src_file; + } + + if (multifd_recv_setup(errp) != 0) { + return; + } + + if (default_channel) { + f = qemu_file_new_input(ioc); + migration_incoming_setup(f); } else { /* Multiple connections */ - assert(migrate_use_multifd()); - start_migration = multifd_recv_new_channel(ioc, &local_err); + assert(migration_needs_multiple_sockets()); + if (migrate_multifd()) { + multifd_recv_new_channel(ioc, &local_err); + } else { + assert(migrate_postcopy_preempt()); + f = qemu_file_new_input(ioc); + postcopy_preempt_new_channel(mis, f); + } if (local_err) { error_propagate(errp, local_err); return; } } - if (start_migration) { + if (migration_should_start_incoming(default_channel)) { /* If it's a recovery, we're done */ if (postcopy_try_recover()) { return; @@ -761,11 +969,25 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) bool migration_has_all_channels(void) { MigrationIncomingState *mis = migration_incoming_get_current(); - bool all_channels; - all_channels = multifd_recv_all_channels_created(); + if (!mis->from_src_file) { + return false; + } - return all_channels && mis->from_src_file != NULL; + if (migrate_multifd()) { + return multifd_recv_all_channels_created(); + } + + if (migrate_postcopy_preempt()) { + return mis->postcopy_qemufile_dst != NULL; + } + + return true; +} + +int migrate_send_rp_switchover_ack(MigrationIncomingState *mis) +{ + return migrate_send_rp_message(mis, MIG_RP_MSG_SWITCHOVER_ACK, 0, NULL); } /* @@ -842,124 +1064,15 @@ void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value) migrate_send_rp_message(mis, MIG_RP_MSG_RESUME_ACK, sizeof(buf), &buf); } -MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) -{ - MigrationCapabilityStatusList *head = NULL, **tail = &head; - MigrationCapabilityStatus *caps; - MigrationState *s = migrate_get_current(); - int i; - - for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { -#ifndef CONFIG_LIVE_BLOCK_MIGRATION - if (i == MIGRATION_CAPABILITY_BLOCK) { - continue; - } -#endif - caps = g_malloc0(sizeof(*caps)); - caps->capability = i; - caps->state = s->enabled_capabilities[i]; - QAPI_LIST_APPEND(tail, caps); - } - - return head; -} - -MigrationParameters *qmp_query_migrate_parameters(Error **errp) -{ - MigrationParameters *params; - MigrationState *s = migrate_get_current(); - - /* TODO use QAPI_CLONE() instead of duplicating it inline */ - params = g_malloc0(sizeof(*params)); - params->has_compress_level = true; - params->compress_level = s->parameters.compress_level; - params->has_compress_threads = true; - params->compress_threads = s->parameters.compress_threads; - params->has_compress_wait_thread = true; - 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; - params->cpu_throttle_increment = s->parameters.cpu_throttle_increment; - params->has_cpu_throttle_tailslow = true; - params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow; - params->has_tls_creds = true; - params->tls_creds = g_strdup(s->parameters.tls_creds); - 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 ? - s->parameters.tls_authz : ""); - params->has_max_bandwidth = true; - params->max_bandwidth = s->parameters.max_bandwidth; - params->has_downtime_limit = true; - params->downtime_limit = s->parameters.downtime_limit; - params->has_x_checkpoint_delay = true; - params->x_checkpoint_delay = s->parameters.x_checkpoint_delay; - params->has_block_incremental = true; - 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; -#ifdef CONFIG_LINUX - params->has_zero_copy_send = true; - params->zero_copy_send = s->parameters.zero_copy_send; -#endif - params->has_xbzrle_cache_size = true; - params->xbzrle_cache_size = s->parameters.xbzrle_cache_size; - params->has_max_postcopy_bandwidth = true; - params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth; - params->has_max_cpu_throttle = true; - params->max_cpu_throttle = s->parameters.max_cpu_throttle; - params->has_announce_initial = true; - params->announce_initial = s->parameters.announce_initial; - params->has_announce_max = true; - params->announce_max = s->parameters.announce_max; - params->has_announce_rounds = true; - params->announce_rounds = s->parameters.announce_rounds; - params->has_announce_step = true; - params->announce_step = s->parameters.announce_step; - - if (s->parameters.has_block_bitmap_mapping) { - params->has_block_bitmap_mapping = true; - params->block_bitmap_mapping = - QAPI_CLONE(BitmapMigrationNodeAliasList, - s->parameters.block_bitmap_mapping); - } - - return params; -} - -AnnounceParameters *migrate_announce_params(void) -{ - static AnnounceParameters ap; - - MigrationState *s = migrate_get_current(); - - ap.initial = s->parameters.announce_initial; - ap.max = s->parameters.announce_max; - ap.rounds = s->parameters.announce_rounds; - ap.step = s->parameters.announce_step; - - return ≈ -} - /* * Return true if we're already in the middle of a migration * (i.e. any of the active or setup states) */ -bool migration_is_setup_or_active(int state) +bool migration_is_setup_or_active(void) { - switch (state) { + MigrationState *s = current_migration; + + switch (s->state) { case MIGRATION_STATUS_ACTIVE: case MIGRATION_STATUS_POSTCOPY_ACTIVE: case MIGRATION_STATUS_POSTCOPY_PAUSED: @@ -977,9 +1090,11 @@ bool migration_is_setup_or_active(int state) } } -bool migration_is_running(int state) +bool migration_is_running(void) { - switch (state) { + MigrationState *s = current_migration; + + switch (s->state) { case MIGRATION_STATUS_ACTIVE: case MIGRATION_STATUS_POSTCOPY_ACTIVE: case MIGRATION_STATUS_POSTCOPY_PAUSED: @@ -997,20 +1112,30 @@ bool migration_is_running(int state) } } +static bool migrate_show_downtime(MigrationState *s) +{ + return (s->state == MIGRATION_STATUS_COMPLETED) || migration_in_postcopy(); +} + static void populate_time_info(MigrationInfo *info, MigrationState *s) { info->has_status = true; info->has_setup_time = true; info->setup_time = s->setup_time; + if (s->state == MIGRATION_STATUS_COMPLETED) { info->has_total_time = true; info->total_time = s->total_time; - info->has_downtime = true; - info->downtime = s->downtime; } else { info->has_total_time = true; info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - s->start_time; + } + + if (migrate_show_downtime(s)) { + info->has_downtime = true; + info->downtime = s->downtime; + } else { info->has_expected_downtime = true; info->expected_downtime = s->expected_downtime; } @@ -1020,27 +1145,29 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) { size_t page_size = qemu_target_page_size(); - info->has_ram = true; info->ram = g_malloc0(sizeof(*info->ram)); - info->ram->transferred = ram_counters.transferred; + info->ram->transferred = migration_transferred_bytes(); info->ram->total = ram_bytes_total(); - info->ram->duplicate = ram_counters.duplicate; + info->ram->duplicate = stat64_get(&mig_stats.zero_pages); /* legacy value. It is not used anymore */ info->ram->skipped = 0; - info->ram->normal = ram_counters.normal; - info->ram->normal_bytes = ram_counters.normal * page_size; + info->ram->normal = stat64_get(&mig_stats.normal_pages); + info->ram->normal_bytes = info->ram->normal * page_size; info->ram->mbps = s->mbps; - info->ram->dirty_sync_count = ram_counters.dirty_sync_count; - info->ram->postcopy_requests = ram_counters.postcopy_requests; + info->ram->dirty_sync_count = + stat64_get(&mig_stats.dirty_sync_count); + info->ram->dirty_sync_missed_zero_copy = + stat64_get(&mig_stats.dirty_sync_missed_zero_copy); + info->ram->postcopy_requests = + stat64_get(&mig_stats.postcopy_requests); info->ram->page_size = page_size; - info->ram->multifd_bytes = ram_counters.multifd_bytes; + info->ram->multifd_bytes = stat64_get(&mig_stats.multifd_bytes); info->ram->pages_per_second = s->pages_per_second; - info->ram->precopy_bytes = ram_counters.precopy_bytes; - info->ram->downtime_bytes = ram_counters.downtime_bytes; - info->ram->postcopy_bytes = ram_counters.postcopy_bytes; + info->ram->precopy_bytes = stat64_get(&mig_stats.precopy_bytes); + info->ram->downtime_bytes = stat64_get(&mig_stats.downtime_bytes); + info->ram->postcopy_bytes = stat64_get(&mig_stats.postcopy_bytes); - if (migrate_use_xbzrle()) { - info->has_xbzrle_cache = true; + if (migrate_xbzrle()) { info->xbzrle_cache = g_malloc0(sizeof(*info->xbzrle_cache)); info->xbzrle_cache->cache_size = migrate_xbzrle_cache_size(); info->xbzrle_cache->bytes = xbzrle_counters.bytes; @@ -1051,17 +1178,7 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) info->xbzrle_cache->overflow = xbzrle_counters.overflow; } - if (migrate_use_compression()) { - info->has_compression = true; - info->compression = g_malloc0(sizeof(*info->compression)); - info->compression->pages = compression_counters.pages; - info->compression->busy = compression_counters.busy; - info->compression->busy_rate = compression_counters.busy_rate; - info->compression->compressed_size = - compression_counters.compressed_size; - info->compression->compression_rate = - compression_counters.compression_rate; - } + populate_compress(info); if (cpu_throttle_active()) { info->has_cpu_throttle_percentage = true; @@ -1070,14 +1187,23 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) if (s->state != MIGRATION_STATUS_COMPLETED) { info->ram->remaining = ram_bytes_remaining(); - info->ram->dirty_pages_rate = ram_counters.dirty_pages_rate; + info->ram->dirty_pages_rate = + stat64_get(&mig_stats.dirty_pages_rate); + } + + if (migrate_dirty_limit() && dirtylimit_in_service()) { + info->has_dirty_limit_throttle_time_per_round = true; + info->dirty_limit_throttle_time_per_round = + dirtylimit_throttle_time_per_round(); + + info->has_dirty_limit_ring_full_time = true; + info->dirty_limit_ring_full_time = dirtylimit_ring_full_time(); } } static void populate_disk_info(MigrationInfo *info) { if (blk_mig_active()) { - info->has_disk = true; info->disk = g_malloc0(sizeof(*info->disk)); info->disk->transferred = blk_mig_bytes_transferred(); info->disk->remaining = blk_mig_bytes_remaining(); @@ -1089,7 +1215,7 @@ static void fill_source_migration_info(MigrationInfo *info) { MigrationState *s = migrate_get_current(); int state = qatomic_read(&s->state); - GSList *cur_blocker = migration_blockers; + GSList *cur_blocker = migration_blockers[migrate_mode()]; info->blocked_reasons = NULL; @@ -1128,7 +1254,7 @@ static void fill_source_migration_info(MigrationInfo *info) populate_time_info(info, s); populate_ram_info(info, s); populate_disk_info(info); - populate_vfio_info(info); + migration_populate_vfio_info(info); break; case MIGRATION_STATUS_COLO: info->has_status = true; @@ -1137,14 +1263,10 @@ static void fill_source_migration_info(MigrationInfo *info) case MIGRATION_STATUS_COMPLETED: populate_time_info(info, s); populate_ram_info(info, s); - populate_vfio_info(info); + migration_populate_vfio_info(info); break; case MIGRATION_STATUS_FAILED: info->has_status = true; - if (s->error) { - info->has_error_desc = true; - info->error_desc = g_strdup(error_get_pretty(s->error)); - } break; case MIGRATION_STATUS_CANCELLED: info->has_status = true; @@ -1154,136 +1276,11 @@ static void fill_source_migration_info(MigrationInfo *info) break; } info->status = state; -} -typedef enum WriteTrackingSupport { - WT_SUPPORT_UNKNOWN = 0, - WT_SUPPORT_ABSENT, - WT_SUPPORT_AVAILABLE, - WT_SUPPORT_COMPATIBLE -} WriteTrackingSupport; - -static -WriteTrackingSupport migrate_query_write_tracking(void) -{ - /* Check if kernel supports required UFFD features */ - if (!ram_write_tracking_available()) { - return WT_SUPPORT_ABSENT; + QEMU_LOCK_GUARD(&s->error_mutex); + if (s->error) { + info->error_desc = g_strdup(error_get_pretty(s->error)); } - /* - * Check if current memory configuration is - * compatible with required UFFD features. - */ - if (!ram_write_tracking_compatible()) { - return WT_SUPPORT_AVAILABLE; - } - - return WT_SUPPORT_COMPATIBLE; -} - -/** - * @migration_caps_check - check capability validity - * - * @cap_list: old capability list, array of bool - * @params: new capabilities to be applied soon - * @errp: set *errp if the check failed, with reason - * - * Returns true if check passed, otherwise false. - */ -static bool migrate_caps_check(bool *cap_list, - MigrationCapabilityStatusList *params, - Error **errp) -{ - MigrationCapabilityStatusList *cap; - bool old_postcopy_cap; - MigrationIncomingState *mis = migration_incoming_get_current(); - - old_postcopy_cap = cap_list[MIGRATION_CAPABILITY_POSTCOPY_RAM]; - - for (cap = params; cap; cap = cap->next) { - cap_list[cap->value->capability] = cap->value->state; - } - -#ifndef CONFIG_LIVE_BLOCK_MIGRATION - if (cap_list[MIGRATION_CAPABILITY_BLOCK]) { - error_setg(errp, "QEMU compiled without old-style (blk/-b, inc/-i) " - "block migration"); - error_append_hint(errp, "Use drive_mirror+NBD instead.\n"); - return false; - } -#endif - -#ifndef CONFIG_REPLICATION - if (cap_list[MIGRATION_CAPABILITY_X_COLO]) { - error_setg(errp, "QEMU compiled without replication module" - " can't enable COLO"); - error_append_hint(errp, "Please enable replication before COLO.\n"); - return false; - } -#endif - - if (cap_list[MIGRATION_CAPABILITY_POSTCOPY_RAM]) { - /* 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. - */ - if (!old_postcopy_cap && runstate_check(RUN_STATE_INMIGRATE) && - !postcopy_ram_supported_by_host(mis)) { - /* postcopy_ram_supported_by_host will have emitted a more - * detailed message - */ - error_setg(errp, "Postcopy is not supported"); - return false; - } - - if (cap_list[MIGRATION_CAPABILITY_X_IGNORE_SHARED]) { - error_setg(errp, "Postcopy is not compatible with ignore-shared"); - return false; - } - } - - if (cap_list[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT]) { - WriteTrackingSupport wt_support; - int idx; - /* - * Check if 'background-snapshot' capability is supported by - * host kernel and compatible with guest memory configuration. - */ - wt_support = migrate_query_write_tracking(); - if (wt_support < WT_SUPPORT_AVAILABLE) { - error_setg(errp, "Background-snapshot is not supported by host kernel"); - return false; - } - if (wt_support < WT_SUPPORT_COMPATIBLE) { - error_setg(errp, "Background-snapshot is not compatible " - "with guest memory configuration"); - return false; - } - - /* - * Check if there are any migration capabilities - * incompatible with 'background-snapshot'. - */ - for (idx = 0; idx < check_caps_background_snapshot.size; idx++) { - int incomp_cap = check_caps_background_snapshot.caps[idx]; - if (cap_list[incomp_cap]) { - error_setg(errp, - "Background-snapshot is not compatible with %s", - MigrationCapability_str(incomp_cap)); - return false; - } - } - } - - /* incoming side only */ - if (runstate_check(RUN_STATE_INMIGRATE) && - !migrate_multi_channels_is_allowed() && - cap_list[MIGRATION_CAPABILITY_MULTIFD]) { - error_setg(errp, "multifd is not supported by current protocol"); - return false; - } - - return true; } static void fill_destination_migration_info(MigrationInfo *info) @@ -1328,448 +1325,6 @@ MigrationInfo *qmp_query_migrate(Error **errp) return info; } -void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, - Error **errp) -{ - MigrationState *s = migrate_get_current(); - MigrationCapabilityStatusList *cap; - bool cap_list[MIGRATION_CAPABILITY__MAX]; - - if (migration_is_running(s->state)) { - error_setg(errp, QERR_MIGRATION_ACTIVE); - return; - } - - memcpy(cap_list, s->enabled_capabilities, sizeof(cap_list)); - if (!migrate_caps_check(cap_list, params, errp)) { - return; - } - - for (cap = params; cap; cap = cap->next) { - s->enabled_capabilities[cap->value->capability] = cap->value->state; - } -} - -/* - * Check whether the parameters are valid. Error will be put into errp - * (if provided). Return true if valid, otherwise false. - */ -static bool migrate_params_check(MigrationParameters *params, Error **errp) -{ - if (params->has_compress_level && - (params->compress_level > 9)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level", - "a value between 0 and 9"); - return false; - } - - if (params->has_compress_threads && (params->compress_threads < 1)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "compress_threads", - "a value between 1 and 255"); - return false; - } - - if (params->has_decompress_threads && (params->decompress_threads < 1)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "decompress_threads", - "a value between 1 and 255"); - 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)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "cpu_throttle_initial", - "an integer in the range of 1 to 99"); - return false; - } - - if (params->has_cpu_throttle_increment && - (params->cpu_throttle_increment < 1 || - params->cpu_throttle_increment > 99)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "cpu_throttle_increment", - "an integer in the range of 1 to 99"); - return false; - } - - if (params->has_max_bandwidth && (params->max_bandwidth > SIZE_MAX)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "max_bandwidth", - "an integer in the range of 0 to "stringify(SIZE_MAX) - " bytes/second"); - return false; - } - - if (params->has_downtime_limit && - (params->downtime_limit > MAX_MIGRATE_DOWNTIME)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "downtime_limit", - "an integer in the range of 0 to " - stringify(MAX_MIGRATE_DOWNTIME)" ms"); - return false; - } - - /* x_checkpoint_delay is now always positive */ - - if (params->has_multifd_channels && (params->multifd_channels < 1)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "multifd_channels", - "a value between 1 and 255"); - return false; - } - - if (params->has_multifd_zlib_level && - (params->multifd_zlib_level > 9)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_zlib_level", - "a value between 0 and 9"); - return false; - } - - if (params->has_multifd_zstd_level && - (params->multifd_zstd_level > 20)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_zstd_level", - "a value between 0 and 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", - "a power of two no less than the target page size"); - return false; - } - - if (params->has_max_cpu_throttle && - (params->max_cpu_throttle < params->cpu_throttle_initial || - params->max_cpu_throttle > 99)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "max_cpu_throttle", - "an integer in the range of cpu_throttle_initial to 99"); - return false; - } - - if (params->has_announce_initial && - params->announce_initial > 100000) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "announce_initial", - "a value between 0 and 100000"); - return false; - } - if (params->has_announce_max && - params->announce_max > 100000) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "announce_max", - "a value between 0 and 100000"); - return false; - } - if (params->has_announce_rounds && - params->announce_rounds > 1000) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "announce_rounds", - "a value between 0 and 1000"); - return false; - } - if (params->has_announce_step && - (params->announce_step < 1 || - params->announce_step > 10000)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "announce_step", - "a value between 0 and 10000"); - return false; - } - - if (params->has_block_bitmap_mapping && - !check_dirty_bitmap_mig_alias_map(params->block_bitmap_mapping, errp)) { - error_prepend(errp, "Invalid mapping given for block-bitmap-mapping: "); - return false; - } -#ifdef CONFIG_LINUX - if (params->zero_copy_send && - (!migrate_use_multifd() || - params->multifd_compression != MULTIFD_COMPRESSION_NONE || - (params->tls_creds && *params->tls_creds))) { - error_setg(errp, - "Zero copy only available for non-compressed non-TLS multifd migration"); - return false; - } -#endif - return true; -} - -static void migrate_params_test_apply(MigrateSetParameters *params, - MigrationParameters *dest) -{ - *dest = migrate_get_current()->parameters; - - /* TODO use QAPI_CLONE() instead of duplicating it inline */ - - if (params->has_compress_level) { - dest->compress_level = params->compress_level; - } - - if (params->has_compress_threads) { - dest->compress_threads = params->compress_threads; - } - - if (params->has_compress_wait_thread) { - dest->compress_wait_thread = params->compress_wait_thread; - } - - if (params->has_decompress_threads) { - 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; - } - - if (params->has_cpu_throttle_increment) { - dest->cpu_throttle_increment = params->cpu_throttle_increment; - } - - if (params->has_cpu_throttle_tailslow) { - dest->cpu_throttle_tailslow = params->cpu_throttle_tailslow; - } - - if (params->has_tls_creds) { - assert(params->tls_creds->type == QTYPE_QSTRING); - dest->tls_creds = params->tls_creds->u.s; - } - - if (params->has_tls_hostname) { - assert(params->tls_hostname->type == QTYPE_QSTRING); - dest->tls_hostname = params->tls_hostname->u.s; - } - - if (params->has_max_bandwidth) { - dest->max_bandwidth = params->max_bandwidth; - } - - if (params->has_downtime_limit) { - dest->downtime_limit = params->downtime_limit; - } - - if (params->has_x_checkpoint_delay) { - dest->x_checkpoint_delay = params->x_checkpoint_delay; - } - - if (params->has_block_incremental) { - dest->block_incremental = params->block_incremental; - } - if (params->has_multifd_channels) { - dest->multifd_channels = params->multifd_channels; - } - if (params->has_multifd_compression) { - dest->multifd_compression = params->multifd_compression; - } -#ifdef CONFIG_LINUX - if (params->has_zero_copy_send) { - dest->zero_copy_send = params->zero_copy_send; - } -#endif - if (params->has_xbzrle_cache_size) { - dest->xbzrle_cache_size = params->xbzrle_cache_size; - } - if (params->has_max_postcopy_bandwidth) { - dest->max_postcopy_bandwidth = params->max_postcopy_bandwidth; - } - if (params->has_max_cpu_throttle) { - dest->max_cpu_throttle = params->max_cpu_throttle; - } - if (params->has_announce_initial) { - dest->announce_initial = params->announce_initial; - } - if (params->has_announce_max) { - dest->announce_max = params->announce_max; - } - if (params->has_announce_rounds) { - dest->announce_rounds = params->announce_rounds; - } - if (params->has_announce_step) { - dest->announce_step = params->announce_step; - } - - if (params->has_block_bitmap_mapping) { - dest->has_block_bitmap_mapping = true; - dest->block_bitmap_mapping = params->block_bitmap_mapping; - } -} - -static void migrate_params_apply(MigrateSetParameters *params, Error **errp) -{ - MigrationState *s = migrate_get_current(); - - /* TODO use QAPI_CLONE() instead of duplicating it inline */ - - if (params->has_compress_level) { - s->parameters.compress_level = params->compress_level; - } - - if (params->has_compress_threads) { - s->parameters.compress_threads = params->compress_threads; - } - - if (params->has_compress_wait_thread) { - s->parameters.compress_wait_thread = params->compress_wait_thread; - } - - if (params->has_decompress_threads) { - 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; - } - - if (params->has_cpu_throttle_increment) { - s->parameters.cpu_throttle_increment = params->cpu_throttle_increment; - } - - if (params->has_cpu_throttle_tailslow) { - s->parameters.cpu_throttle_tailslow = params->cpu_throttle_tailslow; - } - - if (params->has_tls_creds) { - g_free(s->parameters.tls_creds); - assert(params->tls_creds->type == QTYPE_QSTRING); - s->parameters.tls_creds = g_strdup(params->tls_creds->u.s); - } - - if (params->has_tls_hostname) { - g_free(s->parameters.tls_hostname); - assert(params->tls_hostname->type == QTYPE_QSTRING); - s->parameters.tls_hostname = g_strdup(params->tls_hostname->u.s); - } - - if (params->has_tls_authz) { - g_free(s->parameters.tls_authz); - assert(params->tls_authz->type == QTYPE_QSTRING); - s->parameters.tls_authz = g_strdup(params->tls_authz->u.s); - } - - if (params->has_max_bandwidth) { - s->parameters.max_bandwidth = params->max_bandwidth; - if (s->to_dst_file && !migration_in_postcopy()) { - qemu_file_set_rate_limit(s->to_dst_file, - s->parameters.max_bandwidth / XFER_LIMIT_RATIO); - } - } - - if (params->has_downtime_limit) { - s->parameters.downtime_limit = params->downtime_limit; - } - - if (params->has_x_checkpoint_delay) { - s->parameters.x_checkpoint_delay = params->x_checkpoint_delay; - if (migration_in_colo_state()) { - colo_checkpoint_notify(s); - } - } - - if (params->has_block_incremental) { - s->parameters.block_incremental = params->block_incremental; - } - if (params->has_multifd_channels) { - s->parameters.multifd_channels = params->multifd_channels; - } - if (params->has_multifd_compression) { - s->parameters.multifd_compression = params->multifd_compression; - } -#ifdef CONFIG_LINUX - if (params->has_zero_copy_send) { - s->parameters.zero_copy_send = params->zero_copy_send; - } -#endif - if (params->has_xbzrle_cache_size) { - s->parameters.xbzrle_cache_size = params->xbzrle_cache_size; - xbzrle_cache_resize(params->xbzrle_cache_size, errp); - } - if (params->has_max_postcopy_bandwidth) { - s->parameters.max_postcopy_bandwidth = params->max_postcopy_bandwidth; - if (s->to_dst_file && migration_in_postcopy()) { - qemu_file_set_rate_limit(s->to_dst_file, - s->parameters.max_postcopy_bandwidth / XFER_LIMIT_RATIO); - } - } - if (params->has_max_cpu_throttle) { - s->parameters.max_cpu_throttle = params->max_cpu_throttle; - } - if (params->has_announce_initial) { - s->parameters.announce_initial = params->announce_initial; - } - if (params->has_announce_max) { - s->parameters.announce_max = params->announce_max; - } - if (params->has_announce_rounds) { - s->parameters.announce_rounds = params->announce_rounds; - } - if (params->has_announce_step) { - s->parameters.announce_step = params->announce_step; - } - - if (params->has_block_bitmap_mapping) { - qapi_free_BitmapMigrationNodeAliasList( - s->parameters.block_bitmap_mapping); - - s->parameters.has_block_bitmap_mapping = true; - s->parameters.block_bitmap_mapping = - QAPI_CLONE(BitmapMigrationNodeAliasList, - params->block_bitmap_mapping); - } -} - -void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) -{ - MigrationParameters tmp; - - /* TODO Rewrite "" to null instead */ - if (params->has_tls_creds - && params->tls_creds->type == QTYPE_QNULL) { - qobject_unref(params->tls_creds->u.n); - params->tls_creds->type = QTYPE_QSTRING; - params->tls_creds->u.s = strdup(""); - } - /* TODO Rewrite "" to null instead */ - if (params->has_tls_hostname - && params->tls_hostname->type == QTYPE_QNULL) { - qobject_unref(params->tls_hostname->u.n); - params->tls_hostname->type = QTYPE_QSTRING; - params->tls_hostname->u.s = strdup(""); - } - - migrate_params_test_apply(params, &tmp); - - if (!migrate_params_check(&tmp, errp)) { - /* Invalid parameter */ - return; - } - - migrate_params_apply(params, errp); -} - - void qmp_migrate_start_postcopy(Error **errp) { MigrationState *s = migrate_get_current(); @@ -1803,64 +1358,31 @@ void migrate_set_state(int *state, int old_state, int new_state) } } -static MigrationCapabilityStatus *migrate_cap_add(MigrationCapability index, - bool state) -{ - MigrationCapabilityStatus *cap; - - cap = g_new0(MigrationCapabilityStatus, 1); - cap->capability = index; - cap->state = state; - - return cap; -} - -void migrate_set_block_enabled(bool value, Error **errp) -{ - MigrationCapabilityStatusList *cap = NULL; - - QAPI_LIST_PREPEND(cap, migrate_cap_add(MIGRATION_CAPABILITY_BLOCK, value)); - qmp_migrate_set_capabilities(cap, errp); - qapi_free_MigrationCapabilityStatusList(cap); -} - -static void migrate_set_block_incremental(MigrationState *s, bool value) -{ - s->parameters.block_incremental = value; -} - -static void block_cleanup_parameters(MigrationState *s) -{ - if (s->must_remove_block_options) { - /* setting to false can never fail */ - migrate_set_block_enabled(false, &error_abort); - migrate_set_block_incremental(s, false); - s->must_remove_block_options = false; - } -} - static void migrate_fd_cleanup(MigrationState *s) { - qemu_bh_delete(s->cleanup_bh); - s->cleanup_bh = NULL; + MigrationEventType type; g_free(s->hostname); s->hostname = NULL; + json_writer_free(s->vmdesc); + s->vmdesc = NULL; qemu_savevm_state_cleanup(); + close_return_path_on_source(s); + if (s->to_dst_file) { QEMUFile *tmp; trace_migrate_fd_cleanup(); - qemu_mutex_unlock_iothread(); + bql_unlock(); if (s->migration_thread_running) { qemu_thread_join(&s->thread); s->migration_thread_running = false; } - qemu_mutex_lock_iothread(); + bql_lock(); - multifd_save_cleanup(); + multifd_send_shutdown(); qemu_mutex_lock(&s->qemu_file_lock); tmp = s->to_dst_file; s->to_dst_file = NULL; @@ -1873,7 +1395,7 @@ static void migrate_fd_cleanup(MigrationState *s) qemu_fclose(tmp); } - assert(!migration_is_active(s)); + assert(!migration_is_active()); if (s->state == MIGRATION_STATUS_CANCELLING) { migrate_set_state(&s->state, MIGRATION_STATUS_CANCELLING, @@ -1884,26 +1406,16 @@ static void migrate_fd_cleanup(MigrationState *s) /* It is used on info migrate. We can't free it */ error_report_err(error_copy(s->error)); } - notifier_list_notify(&migration_state_notifiers, s); - block_cleanup_parameters(s); + type = migration_has_failed(s) ? MIG_EVENT_PRECOPY_FAILED : + MIG_EVENT_PRECOPY_DONE; + migration_call_notifiers(s, type, NULL); + block_cleanup_parameters(); yank_unregister_instance(MIGRATION_YANK_INSTANCE); } -static void migrate_fd_cleanup_schedule(MigrationState *s) -{ - /* - * Ref the state for bh, because it may be called when - * there're already no other refs - */ - object_ref(OBJECT(s)); - qemu_bh_schedule(s->cleanup_bh); -} - static void migrate_fd_cleanup_bh(void *opaque) { - MigrationState *s = opaque; - migrate_fd_cleanup(s); - object_unref(OBJECT(s)); + migrate_fd_cleanup(opaque); } void migrate_set_error(MigrationState *s, const Error *error) @@ -1914,6 +1426,13 @@ void migrate_set_error(MigrationState *s, const Error *error) } } +bool migrate_has_error(MigrationState *s) +{ + /* The lock is not helpful here, but still follow the rule */ + QEMU_LOCK_GUARD(&s->error_mutex); + return qatomic_read(&s->error); +} + static void migrate_error_free(MigrationState *s) { QEMU_LOCK_GUARD(&s->error_mutex); @@ -1923,7 +1442,7 @@ static void migrate_error_free(MigrationState *s) } } -void migrate_fd_error(MigrationState *s, const Error *error) +static void migrate_fd_error(MigrationState *s, const Error *error) { trace_migrate_fd_error(error_get_pretty(error)); assert(s->to_dst_file == NULL); @@ -1935,7 +1454,7 @@ void migrate_fd_error(MigrationState *s, const Error *error) static void migrate_fd_cancel(MigrationState *s) { int old_state ; - QEMUFile *f = migrate_get_current()->to_dst_file; + trace_migrate_fd_cancel(); WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) { @@ -1947,7 +1466,7 @@ static void migrate_fd_cancel(MigrationState *s) do { old_state = s->state; - if (!migration_is_running(old_state)) { + if (!migration_is_running()) { break; } /* If the migration is paused, kick it out of the pause */ @@ -1961,11 +1480,13 @@ static void migrate_fd_cancel(MigrationState *s) * If we're unlucky the migration code might be stuck somewhere in a * send/write while the network has failed and is waiting to timeout; * if we've got shutdown(2) available then we can force it to quit. - * The outgoing qemu file gets closed in migrate_fd_cleanup that is - * called in a bh, so there is no race against this cancel. */ - if (s->state == MIGRATION_STATUS_CANCELLING && f) { - qemu_file_shutdown(f); + if (s->state == MIGRATION_STATUS_CANCELLING) { + WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) { + if (s->to_dst_file) { + qemu_file_shutdown(s->to_dst_file); + } + } } if (s->state == MIGRATION_STATUS_CANCELLING && s->block_inactive) { Error *local_err = NULL; @@ -1979,24 +1500,39 @@ static void migrate_fd_cancel(MigrationState *s) } } -void add_migration_state_change_notifier(Notifier *notify) +void migration_add_notifier_mode(NotifierWithReturn *notify, + MigrationNotifyFunc func, MigMode mode) { - notifier_list_add(&migration_state_notifiers, notify); + notify->notify = (NotifierWithReturnFunc)func; + notifier_with_return_list_add(&migration_state_notifiers[mode], notify); } -void remove_migration_state_change_notifier(Notifier *notify) +void migration_add_notifier(NotifierWithReturn *notify, + MigrationNotifyFunc func) { - notifier_remove(notify); + migration_add_notifier_mode(notify, func, MIG_MODE_NORMAL); } -bool migration_in_setup(MigrationState *s) +void migration_remove_notifier(NotifierWithReturn *notify) { - return s->state == MIGRATION_STATUS_SETUP; + if (notify->notify) { + notifier_with_return_remove(notify); + notify->notify = NULL; + } } -bool migration_has_finished(MigrationState *s) +int migration_call_notifiers(MigrationState *s, MigrationEventType type, + Error **errp) { - return s->state == MIGRATION_STATUS_COMPLETED; + MigMode mode = s->parameters.mode; + MigrationEvent e; + int ret; + + e.type = type; + ret = notifier_with_return_list_notify(&migration_state_notifiers[mode], + &e, errp); + assert(!ret || type == MIG_EVENT_PRECOPY_SETUP); + return ret; } bool migration_has_failed(MigrationState *s) @@ -2019,9 +1555,15 @@ bool migration_in_postcopy(void) } } -bool migration_in_postcopy_after_devices(MigrationState *s) +bool migration_postcopy_is_alive(int state) { - return migration_in_postcopy() && s->postcopy_after_devices; + switch (state) { + case MIGRATION_STATUS_POSTCOPY_ACTIVE: + case MIGRATION_STATUS_POSTCOPY_RECOVER: + return true; + default: + return false; + } } bool migration_in_incoming_postcopy(void) @@ -2031,12 +1573,17 @@ bool migration_in_incoming_postcopy(void) return ps >= POSTCOPY_INCOMING_DISCARD && ps < POSTCOPY_INCOMING_END; } +bool migration_incoming_postcopy_advised(void) +{ + PostcopyState ps = postcopy_state_get(); + + return ps >= POSTCOPY_INCOMING_ADVISE && ps < POSTCOPY_INCOMING_END; +} + bool migration_in_bg_snapshot(void) { - MigrationState *s = migrate_get_current(); - return migrate_background_snapshot() && - migration_is_setup_or_active(s->state); + migration_is_setup_or_active(); } bool migration_is_idle(void) @@ -2069,78 +1616,185 @@ bool migration_is_idle(void) return false; } -bool migration_is_active(MigrationState *s) +bool migration_is_active(void) { + MigrationState *s = current_migration; + return (s->state == MIGRATION_STATUS_ACTIVE || s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); } -void migrate_init(MigrationState *s) +bool migration_is_device(void) { + MigrationState *s = current_migration; + + return s->state == MIGRATION_STATUS_DEVICE; +} + +bool migration_thread_is_self(void) +{ + MigrationState *s = current_migration; + + return qemu_thread_is_self(&s->thread); +} + +bool migrate_mode_is_cpr(MigrationState *s) +{ + return s->parameters.mode == MIG_MODE_CPR_REBOOT; +} + +int migrate_init(MigrationState *s, Error **errp) +{ + int ret; + + ret = qemu_savevm_state_prepare(errp); + if (ret) { + return ret; + } + /* * Reinitialise all migration state, except * parameters/capabilities that the user set, and * locks. */ - s->cleanup_bh = 0; - s->vm_start_bh = 0; s->to_dst_file = NULL; s->state = MIGRATION_STATUS_NONE; s->rp_state.from_dst_file = NULL; - s->rp_state.error = false; s->mbps = 0.0; s->pages_per_second = 0.0; s->downtime = 0; s->expected_downtime = 0; s->setup_time = 0; s->start_postcopy = false; - s->postcopy_after_devices = false; s->migration_thread_running = false; error_free(s->error); s->error = NULL; - s->hostname = NULL; + s->vmdesc = NULL; migrate_set_state(&s->state, MIGRATION_STATUS_NONE, MIGRATION_STATUS_SETUP); s->start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); s->total_time = 0; - s->vm_was_running = false; + s->vm_old_state = -1; s->iteration_initial_bytes = 0; s->threshold_size = 0; -} + s->switchover_acked = false; + s->rdma_migration = false; + /* + * set mig_stats memory to zero for a new migration + */ + memset(&mig_stats, 0, sizeof(mig_stats)); + migration_reset_vfio_bytes_transferred(); -int migrate_add_blocker_internal(Error *reason, Error **errp) -{ - /* Snapshots are similar to migrations, so check RUN_STATE_SAVE_VM too. */ - if (runstate_check(RUN_STATE_SAVE_VM) || !migration_is_idle()) { - error_propagate_prepend(errp, error_copy(reason), - "disallowing migration blocker " - "(migration/snapshot in progress) for: "); - return -EBUSY; - } - - migration_blockers = g_slist_prepend(migration_blockers, reason); return 0; } -int migrate_add_blocker(Error *reason, Error **errp) +static bool is_busy(Error **reasonp, Error **errp) { - if (only_migratable) { - error_propagate_prepend(errp, error_copy(reason), + ERRP_GUARD(); + + /* Snapshots are similar to migrations, so check RUN_STATE_SAVE_VM too. */ + if (runstate_check(RUN_STATE_SAVE_VM) || !migration_is_idle()) { + error_propagate_prepend(errp, *reasonp, + "disallowing migration blocker " + "(migration/snapshot in progress) for: "); + *reasonp = NULL; + return true; + } + return false; +} + +static bool is_only_migratable(Error **reasonp, Error **errp, int modes) +{ + ERRP_GUARD(); + + if (only_migratable && (modes & BIT(MIG_MODE_NORMAL))) { + error_propagate_prepend(errp, *reasonp, "disallowing migration blocker " "(--only-migratable) for: "); - return -EACCES; + *reasonp = NULL; + return true; } - - return migrate_add_blocker_internal(reason, errp); + return false; } -void migrate_del_blocker(Error *reason) +static int get_modes(MigMode mode, va_list ap) { - migration_blockers = g_slist_remove(migration_blockers, reason); + int modes = 0; + + while (mode != -1 && mode != MIG_MODE_ALL) { + assert(mode >= MIG_MODE_NORMAL && mode < MIG_MODE__MAX); + modes |= BIT(mode); + mode = va_arg(ap, MigMode); + } + if (mode == MIG_MODE_ALL) { + modes = BIT(MIG_MODE__MAX) - 1; + } + return modes; } -void qmp_migrate_incoming(const char *uri, Error **errp) +static int add_blockers(Error **reasonp, Error **errp, int modes) +{ + for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) { + if (modes & BIT(mode)) { + migration_blockers[mode] = g_slist_prepend(migration_blockers[mode], + *reasonp); + } + } + return 0; +} + +int migrate_add_blocker(Error **reasonp, Error **errp) +{ + return migrate_add_blocker_modes(reasonp, errp, MIG_MODE_ALL); +} + +int migrate_add_blocker_normal(Error **reasonp, Error **errp) +{ + return migrate_add_blocker_modes(reasonp, errp, MIG_MODE_NORMAL, -1); +} + +int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...) +{ + int modes; + va_list ap; + + va_start(ap, mode); + modes = get_modes(mode, ap); + va_end(ap); + + if (is_only_migratable(reasonp, errp, modes)) { + return -EACCES; + } else if (is_busy(reasonp, errp)) { + return -EBUSY; + } + return add_blockers(reasonp, errp, modes); +} + +int migrate_add_blocker_internal(Error **reasonp, Error **errp) +{ + int modes = BIT(MIG_MODE__MAX) - 1; + + if (is_busy(reasonp, errp)) { + return -EBUSY; + } + return add_blockers(reasonp, errp, modes); +} + +void migrate_del_blocker(Error **reasonp) +{ + if (*reasonp) { + for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) { + migration_blockers[mode] = g_slist_remove(migration_blockers[mode], + *reasonp); + } + error_free(*reasonp); + *reasonp = NULL; + } +} + +void qmp_migrate_incoming(const char *uri, bool has_channels, + MigrationChannelList *channels, Error **errp) { Error *local_err = NULL; static bool once = true; @@ -2158,7 +1812,7 @@ void qmp_migrate_incoming(const char *uri, Error **errp) return; } - qemu_start_incoming_migration(uri, &local_err); + qemu_start_incoming_migration(uri, has_channels, channels, &local_err); if (local_err) { yank_unregister_instance(MIGRATION_YANK_INSTANCE); @@ -2194,27 +1848,43 @@ void qmp_migrate_recover(const char *uri, Error **errp) * only re-setup the migration stream and poke existing migration * to continue using that newly established channel. */ - qemu_start_incoming_migration(uri, errp); + qemu_start_incoming_migration(uri, false, NULL, errp); } void qmp_migrate_pause(Error **errp) { MigrationState *ms = migrate_get_current(); MigrationIncomingState *mis = migration_incoming_get_current(); - int ret; + int ret = 0; - if (ms->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + if (migration_postcopy_is_alive(ms->state)) { /* Source side, during postcopy */ + Error *error = NULL; + + /* Tell the core migration that we're pausing */ + error_setg(&error, "Postcopy migration is paused by the user"); + migrate_set_error(ms, error); + error_free(error); + qemu_mutex_lock(&ms->qemu_file_lock); - ret = qemu_file_shutdown(ms->to_dst_file); + if (ms->to_dst_file) { + ret = qemu_file_shutdown(ms->to_dst_file); + } qemu_mutex_unlock(&ms->qemu_file_lock); if (ret) { error_setg(errp, "Failed to pause source migration"); } + + /* + * Kick the migration thread out of any waiting windows (on behalf + * of the rp thread). + */ + migration_rp_kick(ms); + return; } - if (mis->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + if (migration_postcopy_is_alive(mis->state)) { ret = qemu_file_shutdown(mis->from_src_file); if (ret) { error_setg(errp, "Failed to pause destination migration"); @@ -2223,17 +1893,19 @@ void qmp_migrate_pause(Error **errp) } error_setg(errp, "migrate-pause is currently only supported " - "during postcopy-active state"); + "during postcopy-active or postcopy-recover state"); } bool migration_is_blocked(Error **errp) { + GSList *blockers = migration_blockers[migrate_mode()]; + if (qemu_savevm_state_blocked(errp)) { return true; } - if (migration_blockers) { - error_propagate(errp, error_copy(migration_blockers->data)); + if (blockers) { + error_propagate(errp, error_copy(blockers->data)); return true; } @@ -2244,7 +1916,15 @@ bool migration_is_blocked(Error **errp) static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, bool resume, Error **errp) { - Error *local_err = NULL; + if (blk_inc) { + warn_report("parameter 'inc' is deprecated;" + " use blockdev-mirror with NBD instead"); + } + + if (blk) { + warn_report("parameter 'blk' is deprecated;" + " use blockdev-mirror with NBD instead"); + } if (resume) { if (s->state != MIGRATION_STATUS_POSTCOPY_PAUSED) { @@ -2275,7 +1955,7 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, return true; } - if (migration_is_running(s->state)) { + if (migration_is_running()) { error_setg(errp, QERR_MIGRATION_ACTIVE); return false; } @@ -2291,91 +1971,153 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, return false; } + if (kvm_hwpoisoned_mem()) { + error_setg(errp, "Can't migrate this vm with hardware poisoned memory, " + "please reboot the vm and try again"); + return false; + } + if (migration_is_blocked(errp)) { return false; } + if (migrate_mapped_ram()) { + if (migrate_tls()) { + error_setg(errp, "Cannot use TLS with mapped-ram"); + return false; + } + + if (migrate_multifd_compression()) { + error_setg(errp, "Cannot use compression with mapped-ram"); + return false; + } + } + + if (migrate_mode_is_cpr(s)) { + const char *conflict = NULL; + + if (migrate_postcopy()) { + conflict = "postcopy"; + } else if (migrate_background_snapshot()) { + conflict = "background snapshot"; + } else if (migrate_colo()) { + conflict = "COLO"; + } + + if (conflict) { + error_setg(errp, "Cannot use %s with CPR", conflict); + return false; + } + } + if (blk || blk_inc) { - if (migrate_colo_enabled()) { + if (migrate_colo()) { error_setg(errp, "No disk migration is required in COLO mode"); return false; } - if (migrate_use_block() || migrate_use_block_incremental()) { + if (migrate_block() || migrate_block_incremental()) { error_setg(errp, "Command options are incompatible with " "current migration capabilities"); return false; } - migrate_set_block_enabled(true, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!migrate_cap_set(MIGRATION_CAPABILITY_BLOCK, true, errp)) { return false; } s->must_remove_block_options = true; } if (blk_inc) { - migrate_set_block_incremental(s, true); + migrate_set_block_incremental(true); } - migrate_init(s); - /* - * set ram_counters compression_counters memory to zero for a - * new migration - */ - memset(&ram_counters, 0, sizeof(ram_counters)); - memset(&compression_counters, 0, sizeof(compression_counters)); + if (migrate_init(s, errp)) { + return false; + } return true; } -void qmp_migrate(const char *uri, bool has_blk, bool blk, +void qmp_migrate(const char *uri, bool has_channels, + MigrationChannelList *channels, bool has_blk, bool blk, bool has_inc, bool inc, bool has_detach, bool detach, bool has_resume, bool resume, Error **errp) { + bool resume_requested; Error *local_err = NULL; MigrationState *s = migrate_get_current(); - const char *p = NULL; + g_autoptr(MigrationChannel) channel = NULL; + MigrationAddress *addr = NULL; + /* + * Having preliminary checks for uri and channel + */ + if (!uri == !channels) { + error_setg(errp, "need either 'uri' or 'channels' argument"); + return; + } + + if (channels) { + /* To verify that Migrate channel list has only item */ + if (channels->next) { + error_setg(errp, "Channel list has more than one entries"); + return; + } + addr = channels->value->addr; + } + + if (uri) { + /* caller uses the old URI syntax */ + if (!migrate_uri_parse(uri, &channel, errp)) { + return; + } + addr = channel->addr; + } + + /* transport mechanism not suitable for migration? */ + if (!migration_channels_and_transport_compatible(addr, errp)) { + return; + } + + resume_requested = has_resume && resume; if (!migrate_prepare(s, has_blk && blk, has_inc && inc, - has_resume && resume, errp)) { + resume_requested, errp)) { /* Error detected, put into errp */ return; } - if (!(has_resume && resume)) { + if (!resume_requested) { if (!yank_register_instance(MIGRATION_YANK_INSTANCE, errp)) { return; } } - migrate_protocol_allow_multi_channels(false); - if (strstart(uri, "tcp:", &p) || - strstart(uri, "unix:", NULL) || - strstart(uri, "vsock:", NULL)) { - migrate_protocol_allow_multi_channels(true); - socket_start_outgoing_migration(s, p ? p : uri, &local_err); -#ifdef CONFIG_RDMA - } else if (strstart(uri, "rdma:", &p)) { - rdma_start_outgoing_migration(s, p, &local_err); -#endif - } else if (strstart(uri, "exec:", &p)) { - exec_start_outgoing_migration(s, p, &local_err); - } else if (strstart(uri, "fd:", &p)) { - fd_start_outgoing_migration(s, p, &local_err); - } else { - if (!(has_resume && resume)) { - yank_unregister_instance(MIGRATION_YANK_INSTANCE); + if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) { + SocketAddress *saddr = &addr->u.socket; + if (saddr->type == SOCKET_ADDRESS_TYPE_INET || + saddr->type == SOCKET_ADDRESS_TYPE_UNIX || + saddr->type == SOCKET_ADDRESS_TYPE_VSOCK) { + socket_start_outgoing_migration(s, saddr, &local_err); + } else if (saddr->type == SOCKET_ADDRESS_TYPE_FD) { + fd_start_outgoing_migration(s, saddr->u.fd.str, &local_err); } - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "uri", +#ifdef CONFIG_RDMA + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) { + rdma_start_outgoing_migration(s, &addr->u.rdma, &local_err); +#endif + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_EXEC) { + exec_start_outgoing_migration(s, addr->u.exec.args, &local_err); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) { + file_start_outgoing_migration(s, &addr->u.file, &local_err); + } else { + error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE, "uri", "a valid migration protocol"); migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_FAILED); - block_cleanup_parameters(s); - return; + block_cleanup_parameters(); } if (local_err) { - if (!(has_resume && resume)) { + if (!resume_requested) { yank_unregister_instance(MIGRATION_YANK_INSTANCE); } migrate_fd_error(s, local_err); @@ -2400,283 +2142,26 @@ void qmp_migrate_continue(MigrationStatus state, Error **errp) qemu_sem_post(&s->pause_sem); } -bool migrate_release_ram(void) +int migration_rp_wait(MigrationState *s) { - MigrationState *s; + /* If migration has failure already, ignore the wait */ + if (migrate_has_error(s)) { + return -1; + } - s = migrate_get_current(); + qemu_sem_wait(&s->rp_state.rp_sem); - return s->enabled_capabilities[MIGRATION_CAPABILITY_RELEASE_RAM]; + /* After wait, double check that there's no failure */ + if (migrate_has_error(s)) { + return -1; + } + + return 0; } -bool migrate_postcopy_ram(void) +void migration_rp_kick(MigrationState *s) { - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_RAM]; -} - -bool migrate_postcopy(void) -{ - return migrate_postcopy_ram() || migrate_dirty_bitmaps(); -} - -bool migrate_auto_converge(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_AUTO_CONVERGE]; -} - -bool migrate_zero_blocks(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_ZERO_BLOCKS]; -} - -bool migrate_postcopy_blocktime(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME]; -} - -bool migrate_use_compression(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_COMPRESS]; -} - -int migrate_compress_level(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->parameters.compress_level; -} - -int migrate_compress_threads(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->parameters.compress_threads; -} - -int migrate_compress_wait_thread(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->parameters.compress_wait_thread; -} - -int migrate_decompress_threads(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->parameters.decompress_threads; -} - -bool migrate_dirty_bitmaps(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_DIRTY_BITMAPS]; -} - -bool migrate_ignore_shared(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_X_IGNORE_SHARED]; -} - -bool migrate_validate_uuid(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_VALIDATE_UUID]; -} - -bool migrate_use_events(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_EVENTS]; -} - -bool migrate_use_multifd(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_MULTIFD]; -} - -bool migrate_pause_before_switchover(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[ - MIGRATION_CAPABILITY_PAUSE_BEFORE_SWITCHOVER]; -} - -int migrate_multifd_channels(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - 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; -} - -#ifdef CONFIG_LINUX -bool migrate_use_zero_copy_send(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->parameters.zero_copy_send; -} -#endif - -int migrate_use_tls(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->parameters.tls_creds && *s->parameters.tls_creds; -} - -int migrate_use_xbzrle(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_XBZRLE]; -} - -uint64_t migrate_xbzrle_cache_size(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->parameters.xbzrle_cache_size; -} - -static int64_t migrate_max_postcopy_bandwidth(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->parameters.max_postcopy_bandwidth; -} - -bool migrate_use_block(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_BLOCK]; -} - -bool migrate_use_return_path(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_RETURN_PATH]; -} - -bool migrate_use_block_incremental(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->parameters.block_incremental; -} - -bool migrate_background_snapshot(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT]; -} - -/* migration thread support */ -/* - * Something bad happened to the RP stream, mark an error - * The caller shall print or trace something to indicate why - */ -static void mark_source_rp_bad(MigrationState *s) -{ - s->rp_state.error = true; + qemu_sem_post(&s->rp_state.rp_sem); } static struct rp_cmd_args { @@ -2690,6 +2175,7 @@ static struct rp_cmd_args { [MIG_RP_MSG_REQ_PAGES_ID] = { .len = -1, .name = "REQ_PAGES_ID" }, [MIG_RP_MSG_RECV_BITMAP] = { .len = -1, .name = "RECV_BITMAP" }, [MIG_RP_MSG_RESUME_ACK] = { .len = 4, .name = "RESUME_ACK" }, + [MIG_RP_MSG_SWITCHOVER_ACK] = { .len = 0, .name = "SWITCHOVER_ACK" }, [MIG_RP_MSG_MAX] = { .len = -1, .name = "MAX" }, }; @@ -2698,8 +2184,9 @@ static struct rp_cmd_args { * We're allowed to send more than requested (e.g. to round to our page size) * and we don't need to send pages that have already been sent. */ -static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, - ram_addr_t start, size_t len) +static void +migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, + ram_addr_t start, size_t len, Error **errp) { long our_host_ps = qemu_real_host_page_size(); @@ -2711,50 +2198,37 @@ static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, */ if (!QEMU_IS_ALIGNED(start, our_host_ps) || !QEMU_IS_ALIGNED(len, our_host_ps)) { - error_report("%s: Misaligned page request, start: " RAM_ADDR_FMT - " len: %zd", __func__, start, len); - mark_source_rp_bad(ms); + error_setg(errp, "MIG_RP_MSG_REQ_PAGES: Misaligned page request, start:" + RAM_ADDR_FMT " len: %zd", start, len); return; } - if (ram_save_queue_pages(rbname, start, len)) { - mark_source_rp_bad(ms); - } + ram_save_queue_pages(rbname, start, len, errp); } -/* Return true to retry, false to quit */ -static bool postcopy_pause_return_path_thread(MigrationState *s) -{ - trace_postcopy_pause_return_path(); - - qemu_sem_wait(&s->postcopy_pause_rp_sem); - - trace_postcopy_pause_return_path_continued(); - - return true; -} - -static int migrate_handle_rp_recv_bitmap(MigrationState *s, char *block_name) +static bool migrate_handle_rp_recv_bitmap(MigrationState *s, char *block_name, + Error **errp) { RAMBlock *block = qemu_ram_block_by_name(block_name); if (!block) { - error_report("%s: invalid block name '%s'", __func__, block_name); - return -EINVAL; + error_setg(errp, "MIG_RP_MSG_RECV_BITMAP has invalid block name '%s'", + block_name); + return false; } /* Fetch the received bitmap and refresh the dirty bitmap */ - return ram_dirty_bitmap_reload(s, block); + return ram_dirty_bitmap_reload(s, block, errp); } -static int migrate_handle_rp_resume_ack(MigrationState *s, uint32_t value) +static bool migrate_handle_rp_resume_ack(MigrationState *s, + uint32_t value, Error **errp) { trace_source_return_path_thread_resume_ack(value); if (value != MIGRATION_RESUME_ACK_VALUE) { - error_report("%s: illegal resume_ack value %"PRIu32, - __func__, value); - return -1; + error_setg(errp, "illegal resume_ack value %"PRIu32, value); + return false; } /* Now both sides are active. */ @@ -2762,13 +2236,16 @@ static int migrate_handle_rp_resume_ack(MigrationState *s, uint32_t value) MIGRATION_STATUS_POSTCOPY_ACTIVE); /* Notify send thread that time to continue send pages */ - qemu_sem_post(&s->rp_state.rp_sem); + migration_rp_kick(s); - return 0; + return true; } -/* Release ms->rp_state.from_dst_file in a safe way */ -static void migration_release_from_dst_file(MigrationState *ms) +/* + * Release ms->rp_state.from_dst_file (and postcopy_qemufile_src if + * existed) in a safe way. + */ +static void migration_release_dst_files(MigrationState *ms) { QEMUFile *file; @@ -2781,6 +2258,18 @@ static void migration_release_from_dst_file(MigrationState *ms) ms->rp_state.from_dst_file = NULL; } + /* + * Do the same to postcopy fast path socket too if there is. No + * locking needed because this qemufile should only be managed by + * return path thread. + */ + if (ms->postcopy_qemufile_src) { + migration_ioc_unregister_yank_from_file(ms->postcopy_qemufile_src); + qemu_file_shutdown(ms->postcopy_qemufile_src); + qemu_fclose(ms->postcopy_qemufile_src); + ms->postcopy_qemufile_src = NULL; + } + qemu_fclose(file); } @@ -2797,49 +2286,46 @@ static void *source_return_path_thread(void *opaque) uint32_t tmp32, sibling_error; ram_addr_t start = 0; /* =0 to silence warning */ size_t len = 0, expected_len; + Error *err = NULL; int res; trace_source_return_path_thread_entry(); rcu_register_thread(); -retry: - while (!ms->rp_state.error && !qemu_file_get_error(rp) && - migration_is_setup_or_active(ms->state)) { + while (migration_is_setup_or_active()) { trace_source_return_path_thread_loop_top(); + header_type = qemu_get_be16(rp); header_len = qemu_get_be16(rp); if (qemu_file_get_error(rp)) { - mark_source_rp_bad(ms); + qemu_file_get_error_obj(rp, &err); goto out; } if (header_type >= MIG_RP_MSG_MAX || header_type == MIG_RP_MSG_INVALID) { - error_report("RP: Received invalid message 0x%04x length 0x%04x", - header_type, header_len); - mark_source_rp_bad(ms); + error_setg(&err, "Received invalid message 0x%04x length 0x%04x", + header_type, header_len); goto out; } if ((rp_cmd_args[header_type].len != -1 && header_len != rp_cmd_args[header_type].len) || header_len > sizeof(buf)) { - error_report("RP: Received '%s' message (0x%04x) with" - "incorrect length %d expecting %zu", - rp_cmd_args[header_type].name, header_type, header_len, - (size_t)rp_cmd_args[header_type].len); - mark_source_rp_bad(ms); + error_setg(&err, "Received '%s' message (0x%04x) with" + "incorrect length %d expecting %zu", + rp_cmd_args[header_type].name, header_type, header_len, + (size_t)rp_cmd_args[header_type].len); goto out; } /* We know we've got a valid header by this point */ res = qemu_get_buffer(rp, buf, header_len); if (res != header_len) { - error_report("RP: Failed reading data for message 0x%04x" - " read %d expected %d", - header_type, res, header_len); - mark_source_rp_bad(ms); + error_setg(&err, "Failed reading data for message 0x%04x" + " read %d expected %d", + header_type, res, header_len); goto out; } @@ -2849,8 +2335,7 @@ retry: sibling_error = ldl_be_p(buf); trace_source_return_path_thread_shut(sibling_error); if (sibling_error) { - error_report("RP: Sibling indicated error %d", sibling_error); - mark_source_rp_bad(ms); + error_setg(&err, "Sibling indicated error %d", sibling_error); } /* * We'll let the main thread deal with closing the RP @@ -2862,12 +2347,16 @@ retry: case MIG_RP_MSG_PONG: tmp32 = ldl_be_p(buf); trace_source_return_path_thread_pong(tmp32); + qemu_sem_post(&ms->rp_state.rp_pong_acks); break; case MIG_RP_MSG_REQ_PAGES: start = ldq_be_p(buf); len = ldl_be_p(buf + 8); - migrate_handle_rp_req_pages(ms, NULL, start, len); + migrate_handle_rp_req_pages(ms, NULL, start, len, &err); + if (err) { + goto out; + } break; case MIG_RP_MSG_REQ_PAGES_ID: @@ -2882,74 +2371,74 @@ retry: expected_len += tmp32; } if (header_len != expected_len) { - error_report("RP: Req_Page_id with length %d expecting %zd", - header_len, expected_len); - mark_source_rp_bad(ms); + error_setg(&err, "Req_Page_id with length %d expecting %zd", + header_len, expected_len); + goto out; + } + migrate_handle_rp_req_pages(ms, (char *)&buf[13], start, len, + &err); + if (err) { goto out; } - migrate_handle_rp_req_pages(ms, (char *)&buf[13], start, len); break; case MIG_RP_MSG_RECV_BITMAP: if (header_len < 1) { - error_report("%s: missing block name", __func__); - mark_source_rp_bad(ms); + error_setg(&err, "MIG_RP_MSG_RECV_BITMAP missing block name"); goto out; } /* Format: len (1B) + idstr (<255B). This ends the idstr. */ buf[buf[0] + 1] = '\0'; - if (migrate_handle_rp_recv_bitmap(ms, (char *)(buf + 1))) { - mark_source_rp_bad(ms); + if (!migrate_handle_rp_recv_bitmap(ms, (char *)(buf + 1), &err)) { goto out; } break; case MIG_RP_MSG_RESUME_ACK: tmp32 = ldl_be_p(buf); - if (migrate_handle_rp_resume_ack(ms, tmp32)) { - mark_source_rp_bad(ms); + if (!migrate_handle_rp_resume_ack(ms, tmp32, &err)) { goto out; } break; + case MIG_RP_MSG_SWITCHOVER_ACK: + ms->switchover_acked = true; + trace_source_return_path_thread_switchover_acked(); + break; + default: break; } } out: - res = qemu_file_get_error(rp); - if (res) { - if (res && migration_in_postcopy()) { - /* - * Maybe there is something we can do: it looks like a - * network down issue, and we pause for a recovery. - */ - migration_release_from_dst_file(ms); - rp = NULL; - if (postcopy_pause_return_path_thread(ms)) { - /* - * Reload rp, reset the rest. Referencing it is safe since - * it's reset only by us above, or when migration completes - */ - rp = ms->rp_state.from_dst_file; - ms->rp_state.error = false; - goto retry; - } - } - + if (err) { + migrate_set_error(ms, err); + error_free(err); trace_source_return_path_thread_bad_end(); - mark_source_rp_bad(ms); + } + + if (ms->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { + /* + * this will be extremely unlikely: that we got yet another network + * issue during recovering of the 1st network failure.. during this + * period the main migration thread can be waiting on rp_sem for + * this thread to sync with the other side. + * + * When this happens, explicitly kick the migration thread out of + * RECOVER stage and back to PAUSED, so the admin can try + * everything again. + */ + migration_rp_kick(ms); } trace_source_return_path_thread_end(); - migration_release_from_dst_file(ms); rcu_unregister_thread(); + return NULL; } -static int open_return_path_on_source(MigrationState *ms, - bool create_thread) +static int open_return_path_on_source(MigrationState *ms) { ms->rp_state.from_dst_file = qemu_file_get_return_path(ms->to_dst_file); if (!ms->rp_state.from_dst_file) { @@ -2958,11 +2447,6 @@ static int open_return_path_on_source(MigrationState *ms, trace_open_return_path_on_source(); - if (!create_thread) { - /* We're done */ - return 0; - } - qemu_thread_create(&ms->rp_state.rp_thread, "return path", source_return_path_thread, ms, QEMU_THREAD_JOINABLE); ms->rp_state.rp_thread_created = true; @@ -2972,66 +2456,93 @@ static int open_return_path_on_source(MigrationState *ms, return 0; } -/* Returns 0 if the RP was ok, otherwise there was an error on the RP */ -static int await_return_path_close_on_source(MigrationState *ms) +/* Return true if error detected, or false otherwise */ +static bool close_return_path_on_source(MigrationState *ms) { - /* - * If this is a normal exit then the destination will send a SHUT and the - * rp_thread will exit, however if there's an error we need to cause - * it to exit. - */ - if (qemu_file_get_error(ms->to_dst_file) && ms->rp_state.from_dst_file) { - /* - * shutdown(2), if we have it, will cause it to unblock if it's stuck - * waiting for the destination. - */ - qemu_file_shutdown(ms->rp_state.from_dst_file); - mark_source_rp_bad(ms); + if (!ms->rp_state.rp_thread_created) { + return false; } - trace_await_return_path_close_on_source_joining(); + + trace_migration_return_path_end_before(); + + /* + * If this is a normal exit then the destination will send a SHUT + * and the rp_thread will exit, however if there's an error we + * need to cause it to exit. shutdown(2), if we have it, will + * cause it to unblock if it's stuck waiting for the destination. + */ + WITH_QEMU_LOCK_GUARD(&ms->qemu_file_lock) { + if (migrate_has_error(ms) && ms->rp_state.from_dst_file) { + qemu_file_shutdown(ms->rp_state.from_dst_file); + } + } + qemu_thread_join(&ms->rp_state.rp_thread); ms->rp_state.rp_thread_created = false; - trace_await_return_path_close_on_source_close(); - return ms->rp_state.error; + migration_release_dst_files(ms); + trace_migration_return_path_end_after(); + + /* Return path will persist the error in MigrationState when quit */ + return migrate_has_error(ms); +} + +static inline void +migration_wait_main_channel(MigrationState *ms) +{ + /* Wait until one PONG message received */ + qemu_sem_wait(&ms->rp_state.rp_pong_acks); } /* * Switch from normal iteration to postcopy * Returns non-0 on error */ -static int postcopy_start(MigrationState *ms) +static int postcopy_start(MigrationState *ms, Error **errp) { int ret; QIOChannelBuffer *bioc; QEMUFile *fb; - int64_t time_at_stop = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - int64_t bandwidth = migrate_max_postcopy_bandwidth(); + uint64_t bandwidth = migrate_max_postcopy_bandwidth(); bool restart_block = false; int cur_state = MIGRATION_STATUS_ACTIVE; + + if (migrate_postcopy_preempt()) { + migration_wait_main_channel(ms); + if (postcopy_preempt_establish_channel(ms)) { + migrate_set_state(&ms->state, ms->state, MIGRATION_STATUS_FAILED); + error_setg(errp, "%s: Failed to establish preempt channel", + __func__); + return -1; + } + } + if (!migrate_pause_before_switchover()) { migrate_set_state(&ms->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_POSTCOPY_ACTIVE); } trace_postcopy_start(); - qemu_mutex_lock_iothread(); + bql_lock(); trace_postcopy_start_set_run(); - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); - global_state_store(); - ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); + ret = migration_stop_vm(ms, RUN_STATE_FINISH_MIGRATE); if (ret < 0) { + error_setg_errno(errp, -ret, "%s: Failed to stop the VM", __func__); goto fail; } ret = migration_maybe_pause(ms, &cur_state, MIGRATION_STATUS_POSTCOPY_ACTIVE); if (ret < 0) { + error_setg_errno(errp, -ret, "%s: Failed in migration_maybe_pause()", + __func__); goto fail; } ret = bdrv_inactivate_all(); if (ret < 0) { + error_setg_errno(errp, -ret, "%s: Failed in bdrv_inactivate_all()", + __func__); goto fail; } restart_block = true; @@ -3057,12 +2568,7 @@ static int postcopy_start(MigrationState *ms) * will notice we're in POSTCOPY_ACTIVE and not actually * wrap their state up here */ - /* 0 max-postcopy-bandwidth means unlimited */ - if (!bandwidth) { - qemu_file_set_rate_limit(ms->to_dst_file, INT64_MAX); - } else { - qemu_file_set_rate_limit(ms->to_dst_file, bandwidth / XFER_LIMIT_RATIO); - } + migration_rate_set(bandwidth); if (migrate_postcopy_ram()) { /* Ping just for debugging, helps line traces up */ qemu_savevm_send_ping(ms->to_dst_file, 2); @@ -3081,7 +2587,7 @@ static int postcopy_start(MigrationState *ms) */ bioc = qio_channel_buffer_new(4096); qio_channel_set_name(QIO_CHANNEL(bioc), "migration-postcopy-buffer"); - fb = qemu_fopen_channel_output(QIO_CHANNEL(bioc)); + fb = qemu_file_new_output(QIO_CHANNEL(bioc)); object_unref(OBJECT(bioc)); /* @@ -3105,7 +2611,7 @@ static int postcopy_start(MigrationState *ms) */ ret = qemu_file_get_error(ms->to_dst_file); if (ret) { - error_report("postcopy_start: Migration stream errored (pre package)"); + error_setg(errp, "postcopy_start: Migration stream errored (pre package)"); goto fail_closefb; } @@ -3113,6 +2619,7 @@ static int postcopy_start(MigrationState *ms) /* Now send that blob */ if (qemu_savevm_send_packaged(ms->to_dst_file, bioc->data, bioc->usage)) { + error_setg(errp, "%s: Failed to send packaged data", __func__); goto fail_closefb; } qemu_fclose(fb); @@ -3121,12 +2628,11 @@ static int postcopy_start(MigrationState *ms) * at the transition to postcopy and after the device state; in particular * spice needs to trigger a transition now */ - ms->postcopy_after_devices = true; - notifier_list_notify(&migration_state_notifiers, ms); + migration_call_notifiers(ms, MIG_EVENT_PRECOPY_DONE, NULL); - ms->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - time_at_stop; + migration_downtime_end(ms); - qemu_mutex_unlock_iothread(); + bql_unlock(); if (migrate_postcopy_ram()) { /* @@ -3142,10 +2648,11 @@ static int postcopy_start(MigrationState *ms) ret = qemu_file_get_error(ms->to_dst_file); if (ret) { - error_report("postcopy_start: Migration stream errored"); - migrate_set_state(&ms->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, - MIGRATION_STATUS_FAILED); + error_setg_errno(errp, -ret, "postcopy_start: Migration stream error"); + bql_lock(); + goto fail; } + trace_postcopy_preempt_enabled(migrate_postcopy_preempt()); return ret; @@ -3165,13 +2672,14 @@ fail: error_report_err(local_err); } } - qemu_mutex_unlock_iothread(); + migration_call_notifiers(ms, MIG_EVENT_PRECOPY_FAILED, NULL); + bql_unlock(); return -1; } /** * migration_maybe_pause: Pause if required to by - * migrate_pause_before_switchover called with the iothread locked + * migrate_pause_before_switchover called with the BQL locked * Returns: 0 on success */ static int migration_maybe_pause(MigrationState *s, @@ -3199,19 +2707,96 @@ static int migration_maybe_pause(MigrationState *s, * wait for the 'pause_sem' semaphore. */ if (s->state != MIGRATION_STATUS_CANCELLING) { - qemu_mutex_unlock_iothread(); + bql_unlock(); 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(); + bql_lock(); } return s->state == new_state ? 0 : -EINVAL; } +static int migration_completion_precopy(MigrationState *s, + int *current_active_state) +{ + int ret; + + bql_lock(); + + if (!migrate_mode_is_cpr(s)) { + ret = migration_stop_vm(s, RUN_STATE_FINISH_MIGRATE); + if (ret < 0) { + goto out_unlock; + } + } + + ret = migration_maybe_pause(s, current_active_state, + MIGRATION_STATUS_DEVICE); + if (ret < 0) { + goto out_unlock; + } + + /* + * Inactivate disks except in COLO, and track that we have done so in order + * to remember to reactivate them if migration fails or is cancelled. + */ + s->block_inactive = !migrate_colo(); + migration_rate_set(RATE_LIMIT_DISABLED); + ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false, + s->block_inactive); +out_unlock: + bql_unlock(); + return ret; +} + +static void migration_completion_postcopy(MigrationState *s) +{ + trace_migration_completion_postcopy_end(); + + bql_lock(); + qemu_savevm_state_complete_postcopy(s->to_dst_file); + bql_unlock(); + + /* + * Shutdown the postcopy fast path thread. This is only needed when dest + * QEMU binary is old (7.1/7.2). QEMU 8.0+ doesn't need this. + */ + if (migrate_postcopy_preempt() && s->preempt_pre_7_2) { + postcopy_preempt_shutdown_file(s); + } + + trace_migration_completion_postcopy_end_after_complete(); +} + +static void migration_completion_failed(MigrationState *s, + int current_active_state) +{ + if (s->block_inactive && (s->state == MIGRATION_STATUS_ACTIVE || + s->state == MIGRATION_STATUS_DEVICE)) { + /* + * If not doing postcopy, vm_start() will be called: let's + * regain control on images. + */ + Error *local_err = NULL; + + bql_lock(); + bdrv_activate_all(&local_err); + if (local_err) { + error_report_err(local_err); + } else { + s->block_inactive = false; + } + bql_unlock(); + } + + migrate_set_state(&s->state, current_active_state, + MIGRATION_STATUS_FAILED); +} + /** * migration_completion: Used by migration_thread when there's not much left. * The caller 'breaks' the loop when this returns. @@ -3220,103 +2805,52 @@ static int migration_maybe_pause(MigrationState *s, */ static void migration_completion(MigrationState *s) { - int ret; + int ret = 0; int current_active_state = s->state; + Error *local_err = NULL; if (s->state == MIGRATION_STATUS_ACTIVE) { - qemu_mutex_lock_iothread(); - s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); - s->vm_was_running = runstate_is_running(); - ret = global_state_store(); - - if (!ret) { - bool inactivate = !migrate_colo_enabled(); - ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); - trace_migration_completion_vm_stop(ret); - if (ret >= 0) { - ret = migration_maybe_pause(s, ¤t_active_state, - MIGRATION_STATUS_DEVICE); - } - if (ret >= 0) { - qemu_file_set_rate_limit(s->to_dst_file, INT64_MAX); - ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false, - inactivate); - } - if (inactivate && ret >= 0) { - s->block_inactive = true; - } - } - qemu_mutex_unlock_iothread(); - - if (ret < 0) { - goto fail; - } + ret = migration_completion_precopy(s, ¤t_active_state); } else if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { - trace_migration_completion_postcopy_end(); - - qemu_mutex_lock_iothread(); - qemu_savevm_state_complete_postcopy(s->to_dst_file); - qemu_mutex_unlock_iothread(); - - trace_migration_completion_postcopy_end_after_complete(); + migration_completion_postcopy(s); } else { + ret = -1; + } + + if (ret < 0) { goto fail; } - /* - * If rp was opened we must clean up the thread before - * cleaning everything else up (since if there are no failures - * it will wait for the destination to send it's status in - * a SHUT command). - */ - if (s->rp_state.rp_thread_created) { - int rp_error; - trace_migration_return_path_end_before(); - rp_error = await_return_path_close_on_source(s); - trace_migration_return_path_end_after(rp_error); - if (rp_error) { - goto fail_invalidate; - } + if (close_return_path_on_source(s)) { + goto fail; } if (qemu_file_get_error(s->to_dst_file)) { trace_migration_completion_file_err(); - goto fail_invalidate; + goto fail; } - if (migrate_colo_enabled() && s->state == MIGRATION_STATUS_ACTIVE) { + if (migrate_colo() && s->state == MIGRATION_STATUS_ACTIVE) { /* COLO does not support postcopy */ migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_COLO); } else { - migrate_set_state(&s->state, current_active_state, - MIGRATION_STATUS_COMPLETED); + migration_completion_end(s); } return; -fail_invalidate: - /* If not doing postcopy, vm_start() will be called: let's regain - * control on images. - */ - if (s->state == MIGRATION_STATUS_ACTIVE || - s->state == MIGRATION_STATUS_DEVICE) { - Error *local_err = NULL; - - qemu_mutex_lock_iothread(); - bdrv_activate_all(&local_err); - if (local_err) { - error_report_err(local_err); - } else { - s->block_inactive = false; - } - qemu_mutex_unlock_iothread(); +fail: + if (qemu_file_get_error_obj(s->to_dst_file, &local_err)) { + migrate_set_error(s, local_err); + error_free(local_err); + } else if (ret) { + error_setg_errno(&local_err, -ret, "Error in migration completion"); + migrate_set_error(s, local_err); + error_free(local_err); } -fail: - migrate_set_state(&s->state, current_active_state, - MIGRATION_STATUS_FAILED); + migration_completion_failed(s, current_active_state); } /** @@ -3329,13 +2863,6 @@ static void bg_migration_completion(MigrationState *s) { int current_active_state = s->state; - /* - * Stop tracking RAM writes - un-protect memory, un-register UFFD - * memory ranges, flush kernel wait queues and wake up threads - * waiting for write fault to be resolved. - */ - ram_write_tracking_stop(); - if (s->state == MIGRATION_STATUS_ACTIVE) { /* * By this moment we have RAM content saved into the migration stream. @@ -3354,8 +2881,7 @@ static void bg_migration_completion(MigrationState *s) goto fail; } - migrate_set_state(&s->state, current_active_state, - MIGRATION_STATUS_COMPLETED); + migration_completion_end(s); return; fail: @@ -3363,12 +2889,6 @@ fail: MIGRATION_STATUS_FAILED); } -bool migrate_colo_enabled(void) -{ - MigrationState *s = migrate_get_current(); - return s->enabled_capabilities[MIGRATION_CAPABILITY_X_COLO]; -} - typedef enum MigThrError { /* No error detected */ MIG_THR_ERR_NONE = 0, @@ -3383,7 +2903,9 @@ static int postcopy_resume_handshake(MigrationState *s) qemu_savevm_send_postcopy_resume(s->to_dst_file); while (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { - qemu_sem_wait(&s->rp_state.rp_sem); + if (migration_rp_wait(s)) { + return -1; + } } if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { @@ -3409,6 +2931,20 @@ static int postcopy_do_resume(MigrationState *s) return ret; } + /* + * If preempt is enabled, re-establish the preempt channel. Note that + * we do it after resume prepare to make sure the main channel will be + * created before the preempt channel. E.g. with weak network, the + * dest QEMU may get messed up with the preempt and main channels on + * the order of connection setup. This guarantees the correct order. + */ + ret = postcopy_preempt_establish_channel(s); + if (ret) { + error_report("%s: postcopy_preempt_establish_channel(): %d", + __func__, ret); + return ret; + } + /* * Last handshake with destination on the resume (destination will * switch to postcopy-active afterwards) @@ -3434,6 +2970,13 @@ static MigThrError postcopy_pause(MigrationState *s) while (true) { QEMUFile *file; + /* + * We're already pausing, so ignore any errors on the return + * path and just wait for the thread to finish. It will be + * re-created when we resume. + */ + close_return_path_on_source(s); + /* * Current channel is possibly broken. Release it. Note that this is * guaranteed even without lock because to_dst_file should only be @@ -3470,12 +3013,6 @@ static MigThrError postcopy_pause(MigrationState *s) if (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { /* Woken up by a recover procedure. Give it a shot */ - /* - * Firstly, let's wake up the return path now, with a new - * return path channel. - */ - qemu_sem_post(&s->postcopy_pause_rp_sem); - /* Do the resume logic */ if (postcopy_do_resume(s) == 0) { /* Let's continue! */ @@ -3496,6 +3033,17 @@ static MigThrError postcopy_pause(MigrationState *s) } } +void migration_file_set_error(int err) +{ + MigrationState *s = current_migration; + + WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) { + if (s->to_dst_file) { + qemu_file_set_error(s->to_dst_file, err); + } + } +} + static MigThrError migration_detect_error(MigrationState *s) { int ret; @@ -3508,8 +3056,13 @@ static MigThrError migration_detect_error(MigrationState *s) return MIG_THR_ERR_FATAL; } - /* Try to detect any file errors */ - ret = qemu_file_get_error_obj(s->to_dst_file, &local_error); + /* + * Try to detect any file errors. Note that postcopy_qemufile_src will + * be NULL when postcopy preempt is not enabled. + */ + ret = qemu_file_get_error_obj_any(s->to_dst_file, + s->postcopy_qemufile_src, + &local_error); if (!ret) { /* Everything is fine */ assert(!local_error); @@ -3541,31 +3094,28 @@ static MigThrError migration_detect_error(MigrationState *s) } } -/* How many bytes have we transferred since the beginning of the migration */ -static uint64_t migration_total_bytes(MigrationState *s) +static void migration_completion_end(MigrationState *s) { - return qemu_ftell(s->to_dst_file) + ram_counters.multifd_bytes; -} - -static void migration_calculate_complete(MigrationState *s) -{ - uint64_t bytes = migration_total_bytes(s); + uint64_t bytes = migration_transferred_bytes(); int64_t end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); int64_t transfer_time; + /* + * Take the BQL here so that query-migrate on the QMP thread sees: + * - atomic update of s->total_time and s->mbps; + * - correct ordering of s->mbps update vs. s->state; + */ + bql_lock(); + migration_downtime_end(s); s->total_time = end_time - s->start_time; - if (!s->downtime) { - /* - * It's still not set, so we are precopy migration. For - * postcopy, downtime is calculated during postcopy_start(). - */ - s->downtime = end_time - s->downtime_start; - } - transfer_time = s->total_time - s->setup_time; if (transfer_time) { s->mbps = ((double) bytes * 8.0) / transfer_time / 1000; } + + migrate_set_state(&s->state, s->state, + MIGRATION_STATUS_COMPLETED); + bql_unlock(); } static void update_iteration_initial_status(MigrationState *s) @@ -3575,7 +3125,7 @@ static void update_iteration_initial_status(MigrationState *s) * wrong speed calculation. */ s->iteration_start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - s->iteration_initial_bytes = migration_total_bytes(s); + s->iteration_initial_bytes = migration_transferred_bytes(); s->iteration_initial_pages = ram_get_total_transferred_pages(); } @@ -3584,17 +3134,33 @@ static void migration_update_counters(MigrationState *s, { uint64_t transferred, transferred_pages, time_spent; uint64_t current_bytes; /* bytes transferred since the beginning */ + uint64_t switchover_bw; + /* Expected bandwidth when switching over to destination QEMU */ + double expected_bw_per_ms; double bandwidth; if (current_time < s->iteration_start_time + BUFFER_DELAY) { return; } - current_bytes = migration_total_bytes(s); + switchover_bw = migrate_avail_switchover_bandwidth(); + current_bytes = migration_transferred_bytes(); transferred = current_bytes - s->iteration_initial_bytes; time_spent = current_time - s->iteration_start_time; bandwidth = (double)transferred / time_spent; - s->threshold_size = bandwidth * s->parameters.downtime_limit; + + if (switchover_bw) { + /* + * If the user specified a switchover bandwidth, let's trust the + * user so that can be more accurate than what we estimated. + */ + expected_bw_per_ms = switchover_bw / 1000; + } else { + /* If the user doesn't specify bandwidth, we use the estimated */ + expected_bw_per_ms = bandwidth; + } + + s->threshold_size = expected_bw_per_ms * migrate_downtime_limit(); s->mbps = (((double) transferred * 8.0) / ((double) time_spent / 1000.0)) / 1000.0 / 1000.0; @@ -3608,16 +3174,34 @@ static void migration_update_counters(MigrationState *s, * if we haven't sent anything, we don't want to * recalculate. 10000 is a small enough number for our purposes */ - if (ram_counters.dirty_pages_rate && transferred > 10000) { - s->expected_downtime = ram_counters.remaining / bandwidth; + if (stat64_get(&mig_stats.dirty_pages_rate) && + transferred > 10000) { + s->expected_downtime = + stat64_get(&mig_stats.dirty_bytes_last_sync) / expected_bw_per_ms; } - qemu_file_reset_rate_limit(s->to_dst_file); + migration_rate_reset(); update_iteration_initial_status(s); trace_migrate_transferred(transferred, time_spent, - bandwidth, s->threshold_size); + /* Both in unit bytes/ms */ + bandwidth, switchover_bw / 1000, + s->threshold_size); +} + +static bool migration_can_switchover(MigrationState *s) +{ + if (!migrate_switchover_ack()) { + return true; + } + + /* No reason to wait for switchover ACK if VM is stopped */ + if (!runstate_is_running()) { + return true; + } + + return s->switchover_acked; } /* Migration thread iteration status */ @@ -3633,33 +3217,39 @@ typedef enum { */ static MigIterateState migration_iteration_run(MigrationState *s) { - uint64_t pending_size, pend_pre, pend_compat, pend_post; + uint64_t must_precopy, can_postcopy, pending_size; + Error *local_err = NULL; bool in_postcopy = s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE; + bool can_switchover = migration_can_switchover(s); - qemu_savevm_state_pending(s->to_dst_file, s->threshold_size, &pend_pre, - &pend_compat, &pend_post); - pending_size = pend_pre + pend_compat + pend_post; + qemu_savevm_state_pending_estimate(&must_precopy, &can_postcopy); + pending_size = must_precopy + can_postcopy; + trace_migrate_pending_estimate(pending_size, must_precopy, can_postcopy); - trace_migrate_pending(pending_size, s->threshold_size, - pend_pre, pend_compat, pend_post); + if (pending_size < s->threshold_size) { + qemu_savevm_state_pending_exact(&must_precopy, &can_postcopy); + pending_size = must_precopy + can_postcopy; + trace_migrate_pending_exact(pending_size, must_precopy, can_postcopy); + } - if (pending_size && pending_size >= s->threshold_size) { - /* Still a significant amount to transfer */ - if (!in_postcopy && pend_pre <= s->threshold_size && - qatomic_read(&s->start_postcopy)) { - if (postcopy_start(s)) { - error_report("%s: postcopy failed to start", __func__); - } - return MIG_ITERATE_SKIP; - } - /* Just another iteration step */ - qemu_savevm_state_iterate(s->to_dst_file, in_postcopy); - } else { + if ((!pending_size || pending_size < s->threshold_size) && can_switchover) { trace_migration_thread_low_pending(pending_size); migration_completion(s); return MIG_ITERATE_BREAK; } + /* Still a significant amount to transfer */ + if (!in_postcopy && must_precopy <= s->threshold_size && can_switchover && + qatomic_read(&s->start_postcopy)) { + if (postcopy_start(s, &local_err)) { + migrate_set_error(s, local_err); + error_report_err(local_err); + } + return MIG_ITERATE_SKIP; + } + + /* Just another iteration step */ + qemu_savevm_state_iterate(s->to_dst_file, in_postcopy); return MIG_ITERATE_RESUME; } @@ -3668,30 +3258,26 @@ static void migration_iteration_finish(MigrationState *s) /* If we enabled cpu throttling for auto-converge, turn it off. */ cpu_throttle_stop(); - qemu_mutex_lock_iothread(); + bql_lock(); switch (s->state) { case MIGRATION_STATUS_COMPLETED: - migration_calculate_complete(s); runstate_set(RUN_STATE_POSTMIGRATE); break; case MIGRATION_STATUS_COLO: - if (!migrate_colo_enabled()) { - error_report("%s: critical error: calling COLO code without " - "COLO enabled", __func__); - } + assert(migrate_colo()); migrate_start_colo_process(s); - s->vm_was_running = true; + s->vm_old_state = RUN_STATE_RUNNING; /* Fallthrough */ case MIGRATION_STATUS_FAILED: case MIGRATION_STATUS_CANCELLED: case MIGRATION_STATUS_CANCELLING: - if (s->vm_was_running) { + if (runstate_is_live(s->vm_old_state)) { if (!runstate_check(RUN_STATE_SHUTDOWN)) { vm_start(); } } else { if (runstate_check(RUN_STATE_FINISH_MIGRATE)) { - runstate_set(RUN_STATE_POSTMIGRATE); + runstate_set(s->vm_old_state); } } break; @@ -3701,18 +3287,23 @@ static void migration_iteration_finish(MigrationState *s) error_report("%s: Unknown ending state %d", __func__, s->state); break; } - migrate_fd_cleanup_schedule(s); - qemu_mutex_unlock_iothread(); + + migration_bh_schedule(migrate_fd_cleanup_bh, s); + bql_unlock(); } static void bg_migration_iteration_finish(MigrationState *s) { - qemu_mutex_lock_iothread(); + /* + * Stop tracking RAM writes - un-protect memory, un-register UFFD + * memory ranges, flush kernel wait queues and wake up threads + * waiting for write fault to be resolved. + */ + ram_write_tracking_stop(); + + bql_lock(); switch (s->state) { case MIGRATION_STATUS_COMPLETED: - migration_calculate_complete(s); - break; - case MIGRATION_STATUS_ACTIVE: case MIGRATION_STATUS_FAILED: case MIGRATION_STATUS_CANCELLED: @@ -3725,8 +3316,8 @@ static void bg_migration_iteration_finish(MigrationState *s) break; } - migrate_fd_cleanup_schedule(s); - qemu_mutex_unlock_iothread(); + migration_bh_schedule(migrate_fd_cleanup_bh, s); + bql_unlock(); } /* @@ -3764,7 +3355,7 @@ bool migration_rate_limit(void) bool urgent = false; migration_update_counters(s, now); - if (qemu_file_rate_limit(s->to_dst_file)) { + if (migration_rate_exceeded(s->to_dst_file)) { if (qemu_file_get_error(s->to_dst_file)) { return false; @@ -3836,16 +3427,25 @@ static void qemu_savevm_wait_unplug(MigrationState *s, int old_state, static void *migration_thread(void *opaque) { MigrationState *s = opaque; + MigrationThread *thread = NULL; int64_t setup_start = qemu_clock_get_ms(QEMU_CLOCK_HOST); MigThrError thr_error; bool urgent = false; + thread = migration_threads_add("live_migration", qemu_get_thread_id()); + rcu_register_thread(); object_ref(OBJECT(s)); update_iteration_initial_status(s); + if (!multifd_send_setup()) { + goto out; + } + + bql_lock(); qemu_savevm_state_header(s->to_dst_file); + bql_unlock(); /* * If we opened the return path, we need to make sure dst has it @@ -3868,12 +3468,14 @@ static void *migration_thread(void *opaque) qemu_savevm_send_postcopy_advise(s->to_dst_file); } - if (migrate_colo_enabled()) { + if (migrate_colo()) { /* Notify migration destination that we enable COLO */ qemu_savevm_send_colo_enable(s->to_dst_file); } + bql_lock(); qemu_savevm_state_setup(s->to_dst_file); + bql_unlock(); qemu_savevm_wait_unplug(s, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE); @@ -3882,8 +3484,8 @@ static void *migration_thread(void *opaque) trace_migration_thread_setup_complete(); - while (migration_is_active(s)) { - if (urgent || !qemu_file_rate_limit(s->to_dst_file)) { + while (migration_is_active()) { + if (urgent || !migration_rate_exceeded(s->to_dst_file)) { MigIterateState iter_state = migration_iteration_run(s); if (iter_state == MIG_ITERATE_SKIP) { continue; @@ -3912,10 +3514,12 @@ static void *migration_thread(void *opaque) urgent = migration_rate_limit(); } +out: trace_migration_thread_after_loop(); migration_iteration_finish(s); object_unref(OBJECT(s)); rcu_unregister_thread(); + migration_threads_remove(thread); return NULL; } @@ -3923,11 +3527,8 @@ static void bg_migration_vm_start_bh(void *opaque) { MigrationState *s = opaque; - qemu_bh_delete(s->vm_start_bh); - s->vm_start_bh = NULL; - - vm_start(); - s->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - s->downtime_start; + vm_resume(s->vm_old_state); + migration_downtime_end(s); } /** @@ -3956,7 +3557,7 @@ static void *bg_migration_thread(void *opaque) rcu_register_thread(); object_ref(OBJECT(s)); - qemu_file_set_rate_limit(s->to_dst_file, INT64_MAX); + migration_rate_set(RATE_LIMIT_DISABLED); setup_start = qemu_clock_get_ms(QEMU_CLOCK_HOST); /* @@ -3970,7 +3571,7 @@ static void *bg_migration_thread(void *opaque) */ s->bioc = qio_channel_buffer_new(512 * 1024); qio_channel_set_name(QIO_CHANNEL(s->bioc), "vmstate-buffer"); - fb = qemu_fopen_channel_output(QIO_CHANNEL(s->bioc)); + fb = qemu_file_new_output(QIO_CHANNEL(s->bioc)); object_unref(OBJECT(s->bioc)); update_iteration_initial_status(s); @@ -3983,8 +3584,10 @@ static void *bg_migration_thread(void *opaque) ram_write_tracking_prepare(); #endif + bql_lock(); qemu_savevm_state_header(s->to_dst_file); qemu_savevm_state_setup(s->to_dst_file); + bql_unlock(); qemu_savevm_wait_unplug(s, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE); @@ -3992,22 +3595,10 @@ static void *bg_migration_thread(void *opaque) s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; trace_migration_thread_setup_complete(); - s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - qemu_mutex_lock_iothread(); + bql_lock(); - /* - * If VM is currently in suspended state, then, to make a valid runstate - * transition in vm_stop_force_state() we need to wakeup it up. - */ - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); - s->vm_was_running = runstate_is_running(); - - if (global_state_store()) { - goto fail; - } - /* Forcibly stop VM before saving state of vCPUs and devices */ - if (vm_stop_force_state(RUN_STATE_PAUSED)) { + if (migration_stop_vm(s, RUN_STATE_PAUSED)) { goto fail; } /* @@ -4036,12 +3627,10 @@ static void *bg_migration_thread(void *opaque) * calling VM state change notifiers from vm_start() would initiate * writes to virtio VQs memory which is in write-protected region. */ - s->vm_start_bh = qemu_bh_new(bg_migration_vm_start_bh, s); - qemu_bh_schedule(s->vm_start_bh); + migration_bh_schedule(bg_migration_vm_start_bh, s); + bql_unlock(); - qemu_mutex_unlock_iothread(); - - while (migration_is_active(s)) { + while (migration_is_active()) { MigIterateState iter_state = bg_migration_iteration_run(s); if (iter_state == MIG_ITERATE_SKIP) { continue; @@ -4068,7 +3657,7 @@ fail: if (early_fail) { migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_FAILED); - qemu_mutex_unlock_iothread(); + bql_unlock(); } bg_migration_iteration_finish(s); @@ -4083,8 +3672,9 @@ fail: void migrate_fd_connect(MigrationState *s, Error *error_in) { Error *local_err = NULL; - int64_t rate_limit; + uint64_t rate_limit; bool resume = s->state == MIGRATION_STATUS_POSTCOPY_PAUSED; + int ret; /* * If there's a previous error, free it and prepare for another one. @@ -4093,13 +3683,7 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) */ migrate_error_free(s); - s->expected_downtime = s->parameters.downtime_limit; - if (resume) { - assert(s->cleanup_bh); - } else { - assert(!s->cleanup_bh); - s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup_bh, s); - } + s->expected_downtime = migrate_downtime_limit(); if (error_in) { migrate_fd_error(s, error_in); if (resume) { @@ -4119,17 +3703,18 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) if (resume) { /* This is a resumed migration */ - rate_limit = s->parameters.max_postcopy_bandwidth / - XFER_LIMIT_RATIO; + rate_limit = migrate_max_postcopy_bandwidth(); } else { /* This is a fresh new migration */ - rate_limit = s->parameters.max_bandwidth / XFER_LIMIT_RATIO; + rate_limit = migrate_max_bandwidth(); /* Notify before starting migration thread */ - notifier_list_notify(&migration_state_notifiers, s); + if (migration_call_notifiers(s, MIG_EVENT_PRECOPY_SETUP, &local_err)) { + goto fail; + } } - qemu_file_set_rate_limit(s->to_dst_file, rate_limit); + migration_rate_set(rate_limit); qemu_file_set_blocking(s->to_dst_file, true); /* @@ -4137,15 +3722,22 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) * precopy, only if user specified "return-path" capability would * QEMU uses the return path. */ - if (migrate_postcopy_ram() || migrate_use_return_path()) { - if (open_return_path_on_source(s, !resume)) { - error_report("Unable to open return-path for postcopy"); - migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); - migrate_fd_cleanup(s); - return; + if (migrate_postcopy_ram() || migrate_return_path()) { + if (open_return_path_on_source(s)) { + error_setg(&local_err, "Unable to open return-path for postcopy"); + goto fail; } } + /* + * This needs to be done before resuming a postcopy. Note: for newer + * QEMUs we will delay the channel creation until postcopy_start(), to + * avoid disorder of channel creations. + */ + if (migrate_postcopy_preempt() && s->preempt_pre_7_2) { + postcopy_preempt_setup(s); + } + if (resume) { /* Wakeup the main migration thread to do the recovery */ migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_PAUSED, @@ -4154,12 +3746,12 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) return; } - 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); - return; + if (migrate_mode_is_cpr(s)) { + ret = migration_stop_vm(s, RUN_STATE_FINISH_MIGRATE); + if (ret < 0) { + error_setg(&local_err, "migration_stop_vm failed, error %d", -ret); + goto fail; + } } if (migrate_background_snapshot()) { @@ -4170,130 +3762,15 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) migration_thread, s, QEMU_THREAD_JOINABLE); } s->migration_thread_running = true; + return; + +fail: + migrate_set_error(s, local_err); + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); + error_report_err(local_err); + migrate_fd_cleanup(s); } -void migration_global_dump(Monitor *mon) -{ - MigrationState *ms = migrate_get_current(); - - monitor_printf(mon, "globals:\n"); - monitor_printf(mon, "store-global-state: %s\n", - ms->store_global_state ? "on" : "off"); - monitor_printf(mon, "only-migratable: %s\n", - only_migratable ? "on" : "off"); - monitor_printf(mon, "send-configuration: %s\n", - ms->send_configuration ? "on" : "off"); - monitor_printf(mon, "send-section-footer: %s\n", - ms->send_section_footer ? "on" : "off"); - monitor_printf(mon, "decompress-error-check: %s\n", - ms->decompress_error_check ? "on" : "off"); - monitor_printf(mon, "clear-bitmap-shift: %u\n", - ms->clear_bitmap_shift); -} - -#define DEFINE_PROP_MIG_CAP(name, x) \ - DEFINE_PROP_BOOL(name, MigrationState, enabled_capabilities[x], false) - -static Property migration_properties[] = { - DEFINE_PROP_BOOL("store-global-state", MigrationState, - store_global_state, true), - DEFINE_PROP_BOOL("send-configuration", MigrationState, - send_configuration, true), - DEFINE_PROP_BOOL("send-section-footer", MigrationState, - send_section_footer, true), - DEFINE_PROP_BOOL("decompress-error-check", MigrationState, - decompress_error_check, true), - DEFINE_PROP_UINT8("x-clear-bitmap-shift", MigrationState, - clear_bitmap_shift, CLEAR_BITMAP_SHIFT_DEFAULT), - - /* Migration parameters */ - DEFINE_PROP_UINT8("x-compress-level", MigrationState, - parameters.compress_level, - DEFAULT_MIGRATE_COMPRESS_LEVEL), - DEFINE_PROP_UINT8("x-compress-threads", MigrationState, - parameters.compress_threads, - DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT), - DEFINE_PROP_BOOL("x-compress-wait-thread", MigrationState, - parameters.compress_wait_thread, true), - 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), - DEFINE_PROP_UINT8("x-cpu-throttle-increment", MigrationState, - parameters.cpu_throttle_increment, - DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT), - DEFINE_PROP_BOOL("x-cpu-throttle-tailslow", MigrationState, - parameters.cpu_throttle_tailslow, false), - DEFINE_PROP_SIZE("x-max-bandwidth", MigrationState, - parameters.max_bandwidth, MAX_THROTTLE), - DEFINE_PROP_UINT64("x-downtime-limit", MigrationState, - parameters.downtime_limit, - DEFAULT_MIGRATE_SET_DOWNTIME), - DEFINE_PROP_UINT32("x-checkpoint-delay", MigrationState, - parameters.x_checkpoint_delay, - DEFAULT_MIGRATE_X_CHECKPOINT_DELAY), - 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), -#ifdef CONFIG_LINUX - DEFINE_PROP_BOOL("zero_copy_send", MigrationState, - parameters.zero_copy_send, false), -#endif - DEFINE_PROP_SIZE("xbzrle-cache-size", MigrationState, - parameters.xbzrle_cache_size, - DEFAULT_MIGRATE_XBZRLE_CACHE_SIZE), - DEFINE_PROP_SIZE("max-postcopy-bandwidth", MigrationState, - parameters.max_postcopy_bandwidth, - DEFAULT_MIGRATE_MAX_POSTCOPY_BANDWIDTH), - DEFINE_PROP_UINT8("max-cpu-throttle", MigrationState, - parameters.max_cpu_throttle, - DEFAULT_MIGRATE_MAX_CPU_THROTTLE), - DEFINE_PROP_SIZE("announce-initial", MigrationState, - parameters.announce_initial, - DEFAULT_MIGRATE_ANNOUNCE_INITIAL), - DEFINE_PROP_SIZE("announce-max", MigrationState, - parameters.announce_max, - DEFAULT_MIGRATE_ANNOUNCE_MAX), - DEFINE_PROP_SIZE("announce-rounds", MigrationState, - parameters.announce_rounds, - DEFAULT_MIGRATE_ANNOUNCE_ROUNDS), - DEFINE_PROP_SIZE("announce-step", MigrationState, - parameters.announce_step, - DEFAULT_MIGRATE_ANNOUNCE_STEP), - - /* Migration capabilities */ - DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE), - DEFINE_PROP_MIG_CAP("x-rdma-pin-all", MIGRATION_CAPABILITY_RDMA_PIN_ALL), - DEFINE_PROP_MIG_CAP("x-auto-converge", MIGRATION_CAPABILITY_AUTO_CONVERGE), - DEFINE_PROP_MIG_CAP("x-zero-blocks", MIGRATION_CAPABILITY_ZERO_BLOCKS), - DEFINE_PROP_MIG_CAP("x-compress", MIGRATION_CAPABILITY_COMPRESS), - DEFINE_PROP_MIG_CAP("x-events", MIGRATION_CAPABILITY_EVENTS), - DEFINE_PROP_MIG_CAP("x-postcopy-ram", MIGRATION_CAPABILITY_POSTCOPY_RAM), - DEFINE_PROP_MIG_CAP("x-colo", MIGRATION_CAPABILITY_X_COLO), - DEFINE_PROP_MIG_CAP("x-release-ram", MIGRATION_CAPABILITY_RELEASE_RAM), - DEFINE_PROP_MIG_CAP("x-block", MIGRATION_CAPABILITY_BLOCK), - DEFINE_PROP_MIG_CAP("x-return-path", MIGRATION_CAPABILITY_RETURN_PATH), - DEFINE_PROP_MIG_CAP("x-multifd", MIGRATION_CAPABILITY_MULTIFD), - DEFINE_PROP_MIG_CAP("x-background-snapshot", - MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT), - - DEFINE_PROP_END_OF_LIST(), -}; - static void migration_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -4305,25 +3782,22 @@ static void migration_class_init(ObjectClass *klass, void *data) static void migration_instance_finalize(Object *obj) { MigrationState *ms = MIGRATION_OBJ(obj); - MigrationParameters *params = &ms->parameters; qemu_mutex_destroy(&ms->error_mutex); qemu_mutex_destroy(&ms->qemu_file_lock); - g_free(params->tls_hostname); - g_free(params->tls_creds); qemu_sem_destroy(&ms->wait_unplug_sem); qemu_sem_destroy(&ms->rate_limit_sem); qemu_sem_destroy(&ms->pause_sem); qemu_sem_destroy(&ms->postcopy_pause_sem); - qemu_sem_destroy(&ms->postcopy_pause_rp_sem); qemu_sem_destroy(&ms->rp_state.rp_sem); + qemu_sem_destroy(&ms->rp_state.rp_pong_acks); + qemu_sem_destroy(&ms->postcopy_qemufile_src_sem); error_free(ms->error); } static void migration_instance_init(Object *obj) { MigrationState *ms = MIGRATION_OBJ(obj); - MigrationParameters *params = &ms->parameters; ms->state = MIGRATION_STATUS_NONE; ms->mbps = -1; @@ -4331,41 +3805,14 @@ static void migration_instance_init(Object *obj) qemu_sem_init(&ms->pause_sem, 0); qemu_mutex_init(&ms->error_mutex); - params->tls_hostname = g_strdup(""); - params->tls_creds = g_strdup(""); - - /* Set has_* up only for parameter checks */ - 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_cpu_throttle_tailslow = true; - params->has_max_bandwidth = true; - params->has_downtime_limit = true; - 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; -#ifdef CONFIG_LINUX - params->has_zero_copy_send = true; -#endif - params->has_xbzrle_cache_size = true; - params->has_max_postcopy_bandwidth = true; - params->has_max_cpu_throttle = true; - params->has_announce_initial = true; - params->has_announce_max = true; - params->has_announce_rounds = true; - params->has_announce_step = true; + migrate_params_init(&ms->parameters); qemu_sem_init(&ms->postcopy_pause_sem, 0); - qemu_sem_init(&ms->postcopy_pause_rp_sem, 0); qemu_sem_init(&ms->rp_state.rp_sem, 0); + qemu_sem_init(&ms->rp_state.rp_pong_acks, 0); qemu_sem_init(&ms->rate_limit_sem, 0); qemu_sem_init(&ms->wait_unplug_sem, 0); + qemu_sem_init(&ms->postcopy_qemufile_src_sem, 0); qemu_mutex_init(&ms->qemu_file_lock); } @@ -4375,27 +3822,14 @@ static void migration_instance_init(Object *obj) */ static bool migration_object_check(MigrationState *ms, Error **errp) { - MigrationCapabilityStatusList *head = NULL; /* Assuming all off */ - bool cap_list[MIGRATION_CAPABILITY__MAX] = { 0 }, ret; - int i; + bool old_caps[MIGRATION_CAPABILITY__MAX] = { 0 }; if (!migrate_params_check(&ms->parameters, errp)) { return false; } - for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { - if (ms->enabled_capabilities[i]) { - QAPI_LIST_PREPEND(head, migrate_cap_add(i, true)); - } - } - - ret = migrate_caps_check(cap_list, head, errp); - - /* It works with head == NULL */ - qapi_free_MigrationCapabilityStatusList(head); - - return ret; + return migrate_caps_check(old_caps, ms->capabilities, errp); } static const TypeInfo migration_type = { diff --git a/migration/migration.h b/migration/migration.h index 485d58b95f..8045e39c26 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -17,12 +17,16 @@ #include "exec/cpu-common.h" #include "hw/qdev-core.h" #include "qapi/qapi-types-migration.h" +#include "qapi/qmp/json-writer.h" #include "qemu/thread.h" #include "qemu/coroutine_int.h" #include "io/channel.h" #include "io/channel-buffer.h" #include "net/announce.h" #include "qom/object.h" +#include "postcopy-ram.h" +#include "sysemu/runstate.h" +#include "migration/misc.h" struct PostcopyBlocktimeContext; @@ -63,11 +67,17 @@ typedef struct { bool all_zero; } PostcopyTmpPage; +typedef enum { + PREEMPT_THREAD_NONE = 0, + PREEMPT_THREAD_CREATED, + PREEMPT_THREAD_QUIT, +} PreemptThreadStatus; + /* State for the incoming migration */ struct MigrationIncomingState { QEMUFile *from_src_file; /* Previously received RAM's RAMBlock pointer */ - RAMBlock *last_recv_block; + RAMBlock *last_recv_block[RAM_CHANNEL_MAX]; /* A hook to allow cleanup at the end of incoming migration */ void *transport_data; void (*transport_cleanup)(void *data); @@ -112,6 +122,34 @@ struct MigrationIncomingState { * enabled. */ unsigned int postcopy_channels; + /* QEMUFile for postcopy only; it'll be handled by a separate thread */ + QEMUFile *postcopy_qemufile_dst; + /* + * When postcopy_qemufile_dst is properly setup, this sem is posted. + * One can wait on this semaphore to wait until the preempt channel is + * properly setup. + */ + QemuSemaphore postcopy_qemufile_dst_done; + /* Postcopy priority thread is used to receive postcopy requested pages */ + QemuThread postcopy_prio_thread; + /* + * Always set by the main vm load thread only, but can be read by the + * postcopy preempt thread. "volatile" makes sure all reads will be + * up-to-date across cores. + */ + volatile PreemptThreadStatus preempt_thread_status; + /* + * Used to sync between the ram load main thread and the fast ram load + * thread. It protects postcopy_qemufile_dst, which is the postcopy + * fast channel. + * + * The ram fast load thread will take it mostly for the whole lifecycle + * because it needs to continuously read data from the channel, and + * it'll only release this mutex if postcopy is interrupted, so that + * the ram load main thread will take this mutex over and properly + * release the broken channel. + */ + QemuMutex postcopy_prio_thread_mutex; /* * An array of temp host huge pages to be used, one for each postcopy * channel. @@ -122,14 +160,17 @@ struct MigrationIncomingState { /* PostCopyFD's for external userfaultfds & handlers of shared memory */ GArray *postcopy_remote_fds; - QEMUBH *bh; - int state; - bool have_colo_incoming_thread; - QemuThread colo_incoming_thread; + /* + * The incoming migration coroutine, non-NULL during qemu_loadvm_state(). + * Used to wake the migration incoming coroutine from rdma code. How much is + * it safe - it's a question. + */ + Coroutine *loadvm_co; + /* The coroutine we should enter (back) after failover */ - Coroutine *migration_incoming_co; + Coroutine *colo_incoming_co; QemuSemaphore colo_incoming_sem; /* @@ -141,13 +182,23 @@ struct MigrationIncomingState { /* notify PAUSED postcopy incoming migrations to try to continue */ QemuSemaphore postcopy_pause_sem_dst; QemuSemaphore postcopy_pause_sem_fault; + /* + * This semaphore is used to allow the ram fast load thread (only when + * postcopy preempt is enabled) fall into sleep when there's network + * interruption detected. When the recovery is done, the main load + * thread will kick the fast ram load thread using this semaphore. + */ + QemuSemaphore postcopy_pause_sem_fast_load; /* List of listening socket addresses */ SocketAddressList *socket_address_list; /* A tree of pages that we requested to the source VM */ GTree *page_requested; - /* For debugging purpose only, but would be nice to keep */ + /* + * For postcopy only, count the number of requested page faults that + * still haven't been resolved. + */ int page_requested_count; /* * The mutex helps to maintain the requested pages that we sent to the @@ -161,6 +212,21 @@ struct MigrationIncomingState { * contains valid information. */ QemuMutex page_request_mutex; + /* + * If postcopy preempt is enabled, there is a chance that the main + * thread finished loading its data before the preempt channel has + * finished loading the urgent pages. If that happens, the two threads + * will use this condvar to synchronize, so the main thread will always + * wait until all pages received. + */ + QemuCond page_request_cond; + + /* + * Number of devices that have yet to approve switchover. When this reaches + * zero an ACK that it's OK to do switchover is sent to the source. No lock + * is needed as this field is updated serially. + */ + unsigned int switchover_ack_pending_num; }; MigrationIncomingState *migration_incoming_get_current(void); @@ -188,10 +254,17 @@ struct MigrationState { /*< public >*/ QemuThread thread; - QEMUBH *vm_start_bh; - QEMUBH *cleanup_bh; /* Protected by qemu_file_lock */ QEMUFile *to_dst_file; + /* Postcopy specific transfer channel */ + QEMUFile *postcopy_qemufile_src; + /* + * It is posted when the preempt channel is established. Note: this is + * used for both the start or recover of a postcopy migration. We'll + * post to this sem every time a new preempt channel is created in the + * main thread, and we keep post() and wait() in pair. + */ + QemuSemaphore postcopy_qemufile_src_sem; QIOChannelBuffer *bioc; /* * Protects to_dst_file/from_dst_file pointers. We need to make sure we @@ -218,9 +291,9 @@ struct MigrationState { /* * The final stage happens when the remaining data is smaller than * this threshold; it's calculated from the requested downtime and - * measured bandwidth + * measured bandwidth, or avail-switchover-bandwidth if specified. */ - int64_t threshold_size; + uint64_t threshold_size; /* params from 'migrate-set-parameters' */ MigrationParameters parameters; @@ -232,7 +305,6 @@ struct MigrationState { /* Protected by qemu_file_lock */ QEMUFile *from_dst_file; QemuThread rp_thread; - bool error; /* * We can also check non-zero of rp_thread, but there's no "official" * way to do this, so this bool makes it slightly more elegant. @@ -240,7 +312,19 @@ struct MigrationState { * be cleared in the rp_thread! */ bool rp_thread_created; + /* + * Used to synchronize between migration main thread and return + * path thread. The migration thread can wait() on this sem, while + * other threads (e.g., return path thread) can kick it using a + * post(). + */ QemuSemaphore rp_sem; + /* + * We post to this when we got one PONG from dest. So far it's an + * easy way to know the main channel has successfully established + * on dest QEMU. + */ + QemuSemaphore rp_pong_acks; } rp_state; double mbps; @@ -252,19 +336,19 @@ struct MigrationState { int64_t downtime_start; int64_t downtime; int64_t expected_downtime; - bool enabled_capabilities[MIGRATION_CAPABILITY__MAX]; + bool capabilities[MIGRATION_CAPABILITY__MAX]; int64_t setup_time; + /* - * Whether guest was running when we enter the completion stage. + * State before stopping the vm by vm_stop_force_state(). * If migration is interrupted by any reason, we need to continue - * running the guest on source. + * running the guest on source if it was running or restore its stopped + * state. */ - bool vm_was_running; + RunState vm_old_state; /* Flag set once the migration has been asked to enter postcopy */ bool start_postcopy; - /* Flag set after postcopy has sent the device state */ - bool postcopy_after_devices; /* Flag set once the migration thread is running (and needs joining) */ bool migration_thread_running; @@ -309,7 +393,6 @@ struct MigrationState { /* Needed by postcopy-pause state */ QemuSemaphore postcopy_pause_sem; - QemuSemaphore postcopy_pause_rp_sem; /* * Whether we abort the migration if decompression errors are * detected at the destination. It is left at false for qemu @@ -317,7 +400,46 @@ struct MigrationState { * do not trigger spurious decompression errors. */ bool decompress_error_check; + /* + * This variable only affects behavior when postcopy preempt mode is + * enabled. + * + * When set: + * + * - postcopy preempt src QEMU instance will generate an EOS message at + * the end of migration to shut the preempt channel on dest side. + * + * - postcopy preempt channel will be created at the setup phase on src + QEMU. + * + * When clear: + * + * - postcopy preempt src QEMU instance will _not_ generate an EOS + * message at the end of migration, the dest qemu will shutdown the + * channel itself. + * + * - postcopy preempt channel will be created at the switching phase + * from precopy -> postcopy (to avoid race condition of misordered + * creation of channels). + * + * NOTE: See message-id on qemu-devel + * mailing list for more information on the possible race. Everyone + * should probably just keep this value untouched after set by the + * machine type (or the default). + */ + bool preempt_pre_7_2; + /* + * flush every channel after each section sent. + * + * This assures that we can't mix pages from one iteration through + * ram pages with pages for the following iteration. We really + * only need to do this flush after we have go through all the + * dirty pages. For historical reasons, we do that after each + * section. This is suboptimal (we flush too many times). + * Default value is false. (since 8.1) + */ + bool multifd_flush_after_each_section; /* * This decides the size of guest memory chunk that will be used * to track dirty bitmap clearing. The size of memory chunk will @@ -332,75 +454,46 @@ struct MigrationState { * This save hostname when out-going migration starts */ char *hostname; + + /* QEMU_VM_VMDESCRIPTION content filled for all non-iterable devices. */ + JSONWriter *vmdesc; + + /* + * Indicates whether an ACK from the destination that it's OK to do + * switchover has been received. + */ + bool switchover_acked; + /* Is this a rdma migration */ + bool rdma_migration; }; void migrate_set_state(int *state, int old_state, int new_state); -void migration_fd_process_incoming(QEMUFile *f, Error **errp); +void migration_fd_process_incoming(QEMUFile *f); void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp); void migration_incoming_process(void); bool migration_has_all_channels(void); -uint64_t migrate_max_downtime(void); - void migrate_set_error(MigrationState *s, const Error *error); -void migrate_fd_error(MigrationState *s, const Error *error); +bool migrate_has_error(MigrationState *s); void migrate_fd_connect(MigrationState *s, Error *error_in); -bool migration_is_setup_or_active(int state); -bool migration_is_running(int state); +int migration_call_notifiers(MigrationState *s, MigrationEventType type, + Error **errp); -void migrate_init(MigrationState *s); +int migrate_init(MigrationState *s, Error **errp); bool migration_is_blocked(Error **errp); /* True if outgoing migration has entered postcopy phase */ bool migration_in_postcopy(void); +bool migration_postcopy_is_alive(int state); MigrationState *migrate_get_current(void); - -bool migrate_postcopy(void); - -bool migrate_release_ram(void); -bool migrate_postcopy_ram(void); -bool migrate_zero_blocks(void); -bool migrate_dirty_bitmaps(void); -bool migrate_ignore_shared(void); -bool migrate_validate_uuid(void); - -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); - -#ifdef CONFIG_LINUX -bool migrate_use_zero_copy_send(void); -#else -#define migrate_use_zero_copy_send() (false) -#endif -int migrate_use_tls(void); -int migrate_use_xbzrle(void); -uint64_t migrate_xbzrle_cache_size(void); -bool migrate_colo_enabled(void); - -bool migrate_use_block(void); -bool migrate_use_block_incremental(void); -int migrate_max_cpu_throttle(void); -bool migrate_use_return_path(void); +bool migration_has_failed(MigrationState *); +bool migrate_mode_is_cpr(MigrationState *); uint64_t ram_get_total_transferred_pages(void); -bool migrate_use_compression(void); -int migrate_compress_level(void); -int migrate_compress_threads(void); -int migrate_compress_wait_thread(void); -int migrate_decompress_threads(void); -bool migrate_use_events(void); -bool migrate_postcopy_blocktime(void); -bool migrate_background_snapshot(void); - /* Sending on the return path - generic and then for each message type */ void migrate_send_rp_shut(MigrationIncomingState *mis, uint32_t value); @@ -413,6 +506,7 @@ int migrate_send_rp_message_req_pages(MigrationIncomingState *mis, void migrate_send_rp_recv_bitmap(MigrationIncomingState *mis, char *block_name); void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value); +int migrate_send_rp_switchover_ack(MigrationIncomingState *mis); void dirty_bitmap_mig_before_vm_start(void); void dirty_bitmap_mig_cancel_outgoing(void); @@ -421,7 +515,8 @@ bool check_dirty_bitmap_mig_alias_map(const BitmapMigrationNodeAliasList *bbm, Error **errp); void migrate_add_address(SocketAddress *address); - +bool migrate_uri_parse(const char *uri, MigrationChannel **channel, + Error **errp); int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque); #define qemu_ram_foreach_block \ @@ -430,12 +525,23 @@ 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); +void migration_bh_schedule(QEMUBHFunc *cb, void *opaque); void migration_cancel(const Error *error); -void populate_vfio_info(MigrationInfo *info); +void migration_populate_vfio_info(MigrationInfo *info); +void migration_reset_vfio_bytes_transferred(void); void postcopy_temp_page_reset(PostcopyTmpPage *tmp_page); -bool migrate_multi_channels_is_allowed(void); -void migrate_protocol_allow_multi_channels(bool allow); +/* + * Migration thread waiting for return path thread. Return non-zero if an + * error is detected. + */ +int migration_rp_wait(MigrationState *s); +/* + * Kick the migration thread waiting for return path messages. NOTE: the + * name can be slightly confusing (when read as "kick the rp thread"), just + * to remember the target is always the migration thread. + */ +void migration_rp_kick(MigrationState *s); #endif diff --git a/migration/multifd-zero-page.c b/migration/multifd-zero-page.c new file mode 100644 index 0000000000..1ba38be636 --- /dev/null +++ b/migration/multifd-zero-page.c @@ -0,0 +1,87 @@ +/* + * Multifd zero page detection implementation. + * + * Copyright (c) 2024 Bytedance Inc + * + * Authors: + * Hao Xiang + * + * 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/cutils.h" +#include "exec/ramblock.h" +#include "migration.h" +#include "multifd.h" +#include "options.h" +#include "ram.h" + +static bool multifd_zero_page_enabled(void) +{ + return migrate_zero_page_detection() == ZERO_PAGE_DETECTION_MULTIFD; +} + +static void swap_page_offset(ram_addr_t *pages_offset, int a, int b) +{ + ram_addr_t temp; + + if (a == b) { + return; + } + + temp = pages_offset[a]; + pages_offset[a] = pages_offset[b]; + pages_offset[b] = temp; +} + +/** + * multifd_send_zero_page_detect: Perform zero page detection on all pages. + * + * Sorts normal pages before zero pages in p->pages->offset and updates + * p->pages->normal_num. + * + * @param p A pointer to the send params. + */ +void multifd_send_zero_page_detect(MultiFDSendParams *p) +{ + MultiFDPages_t *pages = p->pages; + RAMBlock *rb = pages->block; + int i = 0; + int j = pages->num - 1; + + if (!multifd_zero_page_enabled()) { + pages->normal_num = pages->num; + return; + } + + /* + * Sort the page offset array by moving all normal pages to + * the left and all zero pages to the right of the array. + */ + while (i <= j) { + uint64_t offset = pages->offset[i]; + + if (!buffer_is_zero(rb->host + offset, p->page_size)) { + i++; + continue; + } + + swap_page_offset(pages->offset, i, j); + ram_release_page(rb->idstr, offset); + j--; + } + + pages->normal_num = i; +} + +void multifd_recv_zero_page_process(MultiFDRecvParams *p) +{ + for (int i = 0; i < p->zero_num; i++) { + void *page = p->host + p->zero[i]; + if (!buffer_is_zero(page, p->page_size)) { + memset(page, 0, p->page_size); + } + } +} diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index 3a7ae44485..99821cd4d5 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -18,6 +18,7 @@ #include "qapi/error.h" #include "migration.h" #include "trace.h" +#include "options.h" #include "multifd.h" struct zlib_data { @@ -27,6 +28,8 @@ struct zlib_data { uint8_t *zbuff; /* size of compressed buffer */ uint32_t zbuff_len; + /* uncompressed buffer of size qemu_target_page_size() */ + uint8_t *buf; }; /* Multifd zlib compression */ @@ -45,26 +48,38 @@ static int zlib_send_setup(MultiFDSendParams *p, Error **errp) { struct zlib_data *z = g_new0(struct zlib_data, 1); z_stream *zs = &z->zs; + const char *err_msg; 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 %u: deflate init failed", p->id); - return -1; + err_msg = "deflate init failed"; + goto err_free_z; } - /* This is the maxium size of the compressed buffer */ + /* This is the maximum size of the compressed buffer */ z->zbuff_len = compressBound(MULTIFD_PACKET_SIZE); z->zbuff = g_try_malloc(z->zbuff_len); if (!z->zbuff) { - deflateEnd(&z->zs); - g_free(z); - error_setg(errp, "multifd %u: out of memory for zbuff", p->id); - return -1; + err_msg = "out of memory for zbuff"; + goto err_deflate_end; } - p->data = z; + z->buf = g_try_malloc(qemu_target_page_size()); + if (!z->buf) { + err_msg = "out of memory for buf"; + goto err_free_zbuff; + } + p->compress_data = z; return 0; + +err_free_zbuff: + g_free(z->zbuff); +err_deflate_end: + deflateEnd(zs); +err_free_z: + g_free(z); + error_setg(errp, "multifd %u: %s", p->id, err_msg); + return -1; } /** @@ -77,13 +92,15 @@ static int zlib_send_setup(MultiFDSendParams *p, Error **errp) */ static void zlib_send_cleanup(MultiFDSendParams *p, Error **errp) { - struct zlib_data *z = p->data; + struct zlib_data *z = p->compress_data; deflateEnd(&z->zs); g_free(z->zbuff); z->zbuff = NULL; - g_free(p->data); - p->data = NULL; + g_free(z->buf); + z->buf = NULL; + g_free(p->compress_data); + p->compress_data = NULL; } /** @@ -99,23 +116,33 @@ static void zlib_send_cleanup(MultiFDSendParams *p, Error **errp) */ static int zlib_send_prepare(MultiFDSendParams *p, Error **errp) { - struct zlib_data *z = p->data; - size_t page_size = qemu_target_page_size(); + MultiFDPages_t *pages = p->pages; + struct zlib_data *z = p->compress_data; z_stream *zs = &z->zs; uint32_t out_size = 0; int ret; uint32_t i; - for (i = 0; i < p->normal_num; i++) { + if (!multifd_send_prepare_common(p)) { + goto out; + } + + for (i = 0; i < pages->normal_num; i++) { uint32_t available = z->zbuff_len - out_size; int flush = Z_NO_FLUSH; - if (i == p->normal_num - 1) { + if (i == pages->normal_num - 1) { flush = Z_SYNC_FLUSH; } - zs->avail_in = page_size; - zs->next_in = p->pages->block->host + p->normal[i]; + /* + * Since the VM might be running, the page may be changing concurrently + * with compression. zlib does not guarantee that this is safe, + * therefore copy the page before calling deflate(). + */ + memcpy(z->buf, p->pages->block->host + pages->offset[i], p->page_size); + zs->avail_in = p->page_size; + zs->next_in = z->buf; zs->avail_out = available; zs->next_out = z->zbuff + out_size; @@ -147,8 +174,10 @@ static int zlib_send_prepare(MultiFDSendParams *p, Error **errp) p->iov[p->iovs_num].iov_len = out_size; p->iovs_num++; p->next_packet_size = out_size; - p->flags |= MULTIFD_FLAG_ZLIB; +out: + p->flags |= MULTIFD_FLAG_ZLIB; + multifd_send_fill_packet(p); return 0; } @@ -167,7 +196,7 @@ static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp) struct zlib_data *z = g_new0(struct zlib_data, 1); z_stream *zs = &z->zs; - p->data = z; + p->compress_data = z; zs->zalloc = Z_NULL; zs->zfree = Z_NULL; zs->opaque = Z_NULL; @@ -197,17 +226,17 @@ static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp) */ static void zlib_recv_cleanup(MultiFDRecvParams *p) { - struct zlib_data *z = p->data; + struct zlib_data *z = p->compress_data; inflateEnd(&z->zs); g_free(z->zbuff); z->zbuff = NULL; - g_free(p->data); - p->data = NULL; + g_free(p->compress_data); + p->compress_data = NULL; } /** - * zlib_recv_pages: read the data from the channel into actual pages + * zlib_recv: read the data from the channel into actual pages * * Read the compressed buffer, and uncompress it into the actual * pages. @@ -217,15 +246,14 @@ static void zlib_recv_cleanup(MultiFDRecvParams *p) * @p: Params for the channel that we are using * @errp: pointer to an error */ -static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp) +static int zlib_recv(MultiFDRecvParams *p, Error **errp) { - struct zlib_data *z = p->data; - size_t page_size = qemu_target_page_size(); + struct zlib_data *z = p->compress_data; z_stream *zs = &z->zs; uint32_t in_size = p->next_packet_size; /* we measure the change of total_out */ uint32_t out_size = zs->total_out; - uint32_t expected_size = p->normal_num * page_size; + uint32_t expected_size = p->normal_num * p->page_size; uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; int ret; int i; @@ -235,6 +263,14 @@ static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp) p->id, flags, MULTIFD_FLAG_ZLIB); return -1; } + + multifd_recv_zero_page_process(p); + + if (!p->normal_num) { + assert(in_size == 0); + return 0; + } + ret = qio_channel_read_all(p->c, (void *)z->zbuff, in_size, errp); if (ret != 0) { @@ -252,7 +288,7 @@ static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp) flush = Z_SYNC_FLUSH; } - zs->avail_out = page_size; + zs->avail_out = p->page_size; zs->next_out = p->host + p->normal[i]; /* @@ -266,8 +302,8 @@ static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp) do { ret = inflate(zs, flush); } while (ret == Z_OK && zs->avail_in - && (zs->total_out - start) < page_size); - if (ret == Z_OK && (zs->total_out - start) < page_size) { + && (zs->total_out - start) < p->page_size); + if (ret == Z_OK && (zs->total_out - start) < p->page_size) { error_setg(errp, "multifd %u: inflate generated too few output", p->id); return -1; @@ -284,6 +320,7 @@ static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp) p->id, out_size, expected_size); return -1; } + return 0; } @@ -293,7 +330,7 @@ static MultiFDMethods multifd_zlib_ops = { .send_prepare = zlib_send_prepare, .recv_setup = zlib_recv_setup, .recv_cleanup = zlib_recv_cleanup, - .recv_pages = zlib_recv_pages + .recv = zlib_recv }; static void multifd_zlib_register(void) diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index d788d309f2..02112255ad 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -18,6 +18,7 @@ #include "qapi/error.h" #include "migration.h" #include "trace.h" +#include "options.h" #include "multifd.h" struct zstd_data { @@ -51,7 +52,7 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp) struct zstd_data *z = g_new0(struct zstd_data, 1); int res; - p->data = z; + p->compress_data = z; z->zcs = ZSTD_createCStream(); if (!z->zcs) { g_free(z); @@ -67,7 +68,7 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp) p->id, ZSTD_getErrorName(res)); return -1; } - /* This is the maxium size of the compressed buffer */ + /* This is the maximum size of the compressed buffer */ z->zbuff_len = ZSTD_compressBound(MULTIFD_PACKET_SIZE); z->zbuff = g_try_malloc(z->zbuff_len); if (!z->zbuff) { @@ -89,14 +90,14 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp) */ static void zstd_send_cleanup(MultiFDSendParams *p, Error **errp) { - struct zstd_data *z = p->data; + struct zstd_data *z = p->compress_data; ZSTD_freeCStream(z->zcs); z->zcs = NULL; g_free(z->zbuff); z->zbuff = NULL; - g_free(p->data); - p->data = NULL; + g_free(p->compress_data); + p->compress_data = NULL; } /** @@ -112,23 +113,27 @@ static void zstd_send_cleanup(MultiFDSendParams *p, Error **errp) */ static int zstd_send_prepare(MultiFDSendParams *p, Error **errp) { - struct zstd_data *z = p->data; - size_t page_size = qemu_target_page_size(); + MultiFDPages_t *pages = p->pages; + struct zstd_data *z = p->compress_data; int ret; uint32_t i; + if (!multifd_send_prepare_common(p)) { + goto out; + } + z->out.dst = z->zbuff; z->out.size = z->zbuff_len; z->out.pos = 0; - for (i = 0; i < p->normal_num; i++) { + for (i = 0; i < pages->normal_num; i++) { ZSTD_EndDirective flush = ZSTD_e_continue; - if (i == p->normal_num - 1) { + if (i == pages->normal_num - 1) { flush = ZSTD_e_flush; } - z->in.src = p->pages->block->host + p->normal[i]; - z->in.size = page_size; + z->in.src = p->pages->block->host + pages->offset[i]; + z->in.size = p->page_size; z->in.pos = 0; /* @@ -158,8 +163,10 @@ static int zstd_send_prepare(MultiFDSendParams *p, Error **errp) p->iov[p->iovs_num].iov_len = z->out.pos; p->iovs_num++; p->next_packet_size = z->out.pos; - p->flags |= MULTIFD_FLAG_ZSTD; +out: + p->flags |= MULTIFD_FLAG_ZSTD; + multifd_send_fill_packet(p); return 0; } @@ -178,7 +185,7 @@ static int zstd_recv_setup(MultiFDRecvParams *p, Error **errp) struct zstd_data *z = g_new0(struct zstd_data, 1); int ret; - p->data = z; + p->compress_data = z; z->zds = ZSTD_createDStream(); if (!z->zds) { g_free(z); @@ -216,18 +223,18 @@ static int zstd_recv_setup(MultiFDRecvParams *p, Error **errp) */ static void zstd_recv_cleanup(MultiFDRecvParams *p) { - struct zstd_data *z = p->data; + struct zstd_data *z = p->compress_data; ZSTD_freeDStream(z->zds); z->zds = NULL; g_free(z->zbuff); z->zbuff = NULL; - g_free(p->data); - p->data = NULL; + g_free(p->compress_data); + p->compress_data = NULL; } /** - * zstd_recv_pages: read the data from the channel into actual pages + * zstd_recv: read the data from the channel into actual pages * * Read the compressed buffer, and uncompress it into the actual * pages. @@ -237,14 +244,13 @@ static void zstd_recv_cleanup(MultiFDRecvParams *p) * @p: Params for the channel that we are using * @errp: pointer to an error */ -static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp) +static int zstd_recv(MultiFDRecvParams *p, Error **errp) { uint32_t in_size = p->next_packet_size; uint32_t out_size = 0; - size_t page_size = qemu_target_page_size(); - uint32_t expected_size = p->normal_num * page_size; + uint32_t expected_size = p->normal_num * p->page_size; uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; - struct zstd_data *z = p->data; + struct zstd_data *z = p->compress_data; int ret; int i; @@ -253,6 +259,14 @@ static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp) p->id, flags, MULTIFD_FLAG_ZSTD); return -1; } + + multifd_recv_zero_page_process(p); + + if (!p->normal_num) { + assert(in_size == 0); + return 0; + } + ret = qio_channel_read_all(p->c, (void *)z->zbuff, in_size, errp); if (ret != 0) { @@ -265,7 +279,7 @@ static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp) for (i = 0; i < p->normal_num; i++) { z->out.dst = p->host + p->normal[i]; - z->out.size = page_size; + z->out.size = p->page_size; z->out.pos = 0; /* @@ -279,8 +293,8 @@ static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp) do { ret = ZSTD_decompressStream(z->zds, &z->out, &z->in); } while (ret > 0 && (z->in.size - z->in.pos > 0) - && (z->out.pos < page_size)); - if (ret > 0 && (z->out.pos < page_size)) { + && (z->out.pos < p->page_size)); + if (ret > 0 && (z->out.pos < p->page_size)) { error_setg(errp, "multifd %u: decompressStream buffer too small", p->id); return -1; @@ -306,7 +320,7 @@ static MultiFDMethods multifd_zstd_ops = { .send_prepare = zstd_send_prepare, .recv_setup = zstd_recv_setup, .recv_cleanup = zstd_recv_cleanup, - .recv_pages = zstd_recv_pages + .recv = zstd_recv }; static void multifd_zstd_register(void) diff --git a/migration/multifd.c b/migration/multifd.c index 9282ab6aa4..2802afe79d 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -11,21 +11,25 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.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 "file.h" #include "migration.h" +#include "migration-stats.h" #include "socket.h" #include "tls.h" #include "qemu-file.h" #include "trace.h" #include "multifd.h" - +#include "threadinfo.h" +#include "options.h" #include "qemu/yank.h" +#include "io/channel-file.h" #include "io/channel-socket.h" #include "yank_functions.h" @@ -43,20 +47,96 @@ typedef struct { uint64_t unused2[4]; /* Reserved for future use */ } __attribute__((packed)) MultiFDInit_t; +struct { + MultiFDSendParams *params; + /* array of pages to sent */ + MultiFDPages_t *pages; + /* + * Global number of generated multifd packets. + * + * Note that we used 'uintptr_t' because it'll naturally support atomic + * operations on both 32bit / 64 bits hosts. It means on 32bit systems + * multifd will overflow the packet_num easier, but that should be + * fine. + * + * Another option is to use QEMU's Stat64 then it'll be 64 bits on all + * hosts, however so far it does not support atomic fetch_add() yet. + * Make it easy for now. + */ + uintptr_t packet_num; + /* + * Synchronization point past which no more channels will be + * created. + */ + QemuSemaphore channels_created; + /* 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; + +struct { + MultiFDRecvParams *params; + MultiFDRecvData *data; + /* number of created threads */ + int count; + /* + * This is always posted by the recv threads, the migration thread + * uses it to wait for recv threads to finish assigned tasks. + */ + QemuSemaphore sem_sync; + /* global number of generated multifd packets */ + uint64_t packet_num; + int exiting; + /* multifd ops */ + MultiFDMethods *ops; +} *multifd_recv_state; + +static bool multifd_use_packets(void) +{ + return !migrate_mapped_ram(); +} + +void multifd_send_channel_created(void) +{ + qemu_sem_post(&multifd_send_state->channels_created); +} + +static void multifd_set_file_bitmap(MultiFDSendParams *p) +{ + MultiFDPages_t *pages = p->pages; + + assert(pages->block); + + for (int i = 0; i < p->pages->normal_num; i++) { + ramblock_set_file_bmap_atomic(pages->block, pages->offset[i], true); + } + + for (int i = p->pages->normal_num; i < p->pages->num; i++) { + ramblock_set_file_bmap_atomic(pages->block, pages->offset[i], false); + } +} + /* Multifd without compression */ /** * nocomp_send_setup: setup send side * - * 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) { + if (migrate_zero_copy_send()) { + p->write_flags |= QIO_CHANNEL_WRITE_FLAG_ZERO_COPY; + } + return 0; } @@ -73,6 +153,19 @@ static void nocomp_send_cleanup(MultiFDSendParams *p, Error **errp) return; } +static void multifd_send_prepare_iovs(MultiFDSendParams *p) +{ + MultiFDPages_t *pages = p->pages; + + for (int i = 0; i < pages->normal_num; i++) { + p->iov[p->iovs_num].iov_base = pages->block->host + pages->offset[i]; + p->iov[p->iovs_num].iov_len = p->page_size; + p->iovs_num++; + } + + p->next_packet_size = pages->normal_num * p->page_size; +} + /** * nocomp_send_prepare: prepare date to be able to send * @@ -86,17 +179,40 @@ static void nocomp_send_cleanup(MultiFDSendParams *p, Error **errp) */ static int nocomp_send_prepare(MultiFDSendParams *p, Error **errp) { - MultiFDPages_t *pages = p->pages; - size_t page_size = qemu_target_page_size(); + bool use_zero_copy_send = migrate_zero_copy_send(); + int ret; - for (int i = 0; i < p->normal_num; i++) { - p->iov[p->iovs_num].iov_base = pages->block->host + p->normal[i]; - p->iov[p->iovs_num].iov_len = page_size; - p->iovs_num++; + multifd_send_zero_page_detect(p); + + if (!multifd_use_packets()) { + multifd_send_prepare_iovs(p); + multifd_set_file_bitmap(p); + + return 0; } - p->next_packet_size = p->normal_num * page_size; + if (!use_zero_copy_send) { + /* + * Only !zerocopy needs the header in IOV; zerocopy will + * send it separately. + */ + multifd_send_prepare_header(p); + } + + multifd_send_prepare_iovs(p); p->flags |= MULTIFD_FLAG_NOCOMP; + + multifd_send_fill_packet(p); + + if (use_zero_copy_send) { + /* Send header first, without zerocopy */ + ret = qio_channel_write_all(p->c, (void *)p->packet, + p->packet_len, errp); + if (ret != 0) { + return -1; + } + } + return 0; } @@ -127,7 +243,7 @@ static void nocomp_recv_cleanup(MultiFDRecvParams *p) } /** - * nocomp_recv_pages: read the data from the channel into actual pages + * nocomp_recv: read the data from the channel * * For no compression we just need to read things into the correct place. * @@ -136,19 +252,31 @@ static void nocomp_recv_cleanup(MultiFDRecvParams *p) * @p: Params for the channel that we are using * @errp: pointer to an error */ -static int nocomp_recv_pages(MultiFDRecvParams *p, Error **errp) +static int nocomp_recv(MultiFDRecvParams *p, Error **errp) { - uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; - size_t page_size = qemu_target_page_size(); + uint32_t flags; + + if (!multifd_use_packets()) { + return multifd_file_recv_data(p, errp); + } + + flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; if (flags != MULTIFD_FLAG_NOCOMP) { error_setg(errp, "multifd %u: flags received %x flags expected %x", p->id, flags, MULTIFD_FLAG_NOCOMP); return -1; } + + multifd_recv_zero_page_process(p); + + if (!p->normal_num) { + return 0; + } + for (int i = 0; i < p->normal_num; i++) { p->iov[i].iov_base = p->host + p->normal[i]; - p->iov[i].iov_len = page_size; + p->iov[i].iov_len = p->page_size; } return qio_channel_readv_all(p->c, p->iov, p->normal_num, errp); } @@ -159,7 +287,7 @@ static MultiFDMethods multifd_nocomp_ops = { .send_prepare = nocomp_send_prepare, .recv_setup = nocomp_recv_setup, .recv_cleanup = nocomp_recv_cleanup, - .recv_pages = nocomp_recv_pages + .recv = nocomp_recv }; static MultiFDMethods *multifd_ops[MULTIFD_COMPRESSION__MAX] = { @@ -172,9 +300,22 @@ void multifd_register_ops(int method, MultiFDMethods *ops) multifd_ops[method] = ops; } +/* Reset a MultiFDPages_t* object for the next use */ +static void multifd_pages_reset(MultiFDPages_t *pages) +{ + /* + * We don't need to touch offset[] array, because it will be + * overwritten later when reused. + */ + pages->num = 0; + pages->normal_num = 0; + pages->block = NULL; +} + static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp) { MultiFDInit_t msg = {}; + size_t size = sizeof(msg); int ret; msg.magic = cpu_to_be32(MULTIFD_MAGIC); @@ -182,10 +323,11 @@ static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp) 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); + ret = qio_channel_write_all(p->c, (char *)&msg, size, errp); if (ret != 0) { return -1; } + stat64_add(&mig_stats.multifd_bytes, size); return 0; } @@ -226,64 +368,72 @@ static int multifd_recv_initial_packet(QIOChannel *c, Error **errp) } if (msg.id > migrate_multifd_channels()) { - error_setg(errp, "multifd: received channel version %u " - "expected %u", msg.version, MULTIFD_VERSION); + error_setg(errp, "multifd: received channel id %u is greater than " + "number of channels %u", msg.id, migrate_multifd_channels()); return -1; } return msg.id; } -static MultiFDPages_t *multifd_pages_init(size_t size) +static MultiFDPages_t *multifd_pages_init(uint32_t n) { MultiFDPages_t *pages = g_new0(MultiFDPages_t, 1); - pages->allocated = size; - pages->offset = g_new0(ram_addr_t, size); + pages->allocated = n; + pages->offset = g_new0(ram_addr_t, n); return pages; } static void multifd_pages_clear(MultiFDPages_t *pages) { - pages->num = 0; + multifd_pages_reset(pages); pages->allocated = 0; - pages->packet_num = 0; - pages->block = NULL; g_free(pages->offset); pages->offset = NULL; g_free(pages); } -static void multifd_send_fill_packet(MultiFDSendParams *p) +void multifd_send_fill_packet(MultiFDSendParams *p) { MultiFDPacket_t *packet = p->packet; + MultiFDPages_t *pages = p->pages; + uint64_t packet_num; + uint32_t zero_num = pages->num - pages->normal_num; int i; packet->flags = cpu_to_be32(p->flags); packet->pages_alloc = cpu_to_be32(p->pages->allocated); - packet->normal_pages = cpu_to_be32(p->normal_num); + packet->normal_pages = cpu_to_be32(pages->normal_num); + packet->zero_pages = cpu_to_be32(zero_num); packet->next_packet_size = cpu_to_be32(p->next_packet_size); - packet->packet_num = cpu_to_be64(p->packet_num); - if (p->pages->block) { - strncpy(packet->ramblock, p->pages->block->idstr, 256); + packet_num = qatomic_fetch_inc(&multifd_send_state->packet_num); + packet->packet_num = cpu_to_be64(packet_num); + + if (pages->block) { + strncpy(packet->ramblock, pages->block->idstr, 256); } - for (i = 0; i < p->normal_num; i++) { + for (i = 0; i < pages->num; i++) { /* there are architectures where ram_addr_t is 32 bit */ - uint64_t temp = p->normal[i]; + uint64_t temp = pages->offset[i]; packet->offset[i] = cpu_to_be64(temp); } + + p->packets_sent++; + p->total_normal_pages += pages->normal_num; + p->total_zero_pages += zero_num; + + trace_multifd_send(p->id, packet_num, pages->normal_num, zero_num, + p->flags, p->next_packet_size); } static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) { MultiFDPacket_t *packet = p->packet; - size_t page_size = qemu_target_page_size(); - uint32_t page_count = MULTIFD_PACKET_SIZE / page_size; - RAMBlock *block; int i; packet->magic = be32_to_cpu(packet->magic); @@ -309,70 +459,100 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) * If we received a packet that is 100 times bigger than expected * just stop migration. It is a magic number. */ - if (packet->pages_alloc > page_count) { + if (packet->pages_alloc > p->page_count) { error_setg(errp, "multifd: received packet " "with size %u and expected a size of %u", - packet->pages_alloc, page_count) ; + packet->pages_alloc, p->page_count) ; return -1; } p->normal_num = be32_to_cpu(packet->normal_pages); if (p->normal_num > packet->pages_alloc) { error_setg(errp, "multifd: received packet " - "with %u pages and expected maximum pages are %u", + "with %u normal pages and expected maximum pages are %u", p->normal_num, packet->pages_alloc) ; return -1; } + p->zero_num = be32_to_cpu(packet->zero_pages); + if (p->zero_num > packet->pages_alloc - p->normal_num) { + error_setg(errp, "multifd: received packet " + "with %u zero pages and expected maximum zero pages are %u", + p->zero_num, packet->pages_alloc - p->normal_num) ; + return -1; + } + p->next_packet_size = be32_to_cpu(packet->next_packet_size); p->packet_num = be64_to_cpu(packet->packet_num); + p->packets_recved++; + p->total_normal_pages += p->normal_num; + p->total_zero_pages += p->zero_num; - if (p->normal_num == 0) { + trace_multifd_recv(p->id, p->packet_num, p->normal_num, p->zero_num, + p->flags, p->next_packet_size); + + if (p->normal_num == 0 && p->zero_num == 0) { return 0; } /* make sure that ramblock is 0 terminated */ packet->ramblock[255] = 0; - block = qemu_ram_block_by_name(packet->ramblock); - if (!block) { + p->block = qemu_ram_block_by_name(packet->ramblock); + if (!p->block) { error_setg(errp, "multifd: unknown ram block %s", packet->ramblock); return -1; } - p->host = block->host; + p->host = p->block->host; for (i = 0; i < p->normal_num; i++) { uint64_t offset = be64_to_cpu(packet->offset[i]); - if (offset > (block->used_length - page_size)) { + if (offset > (p->block->used_length - p->page_size)) { error_setg(errp, "multifd: offset too long %" PRIu64 " (max " RAM_ADDR_FMT ")", - offset, block->used_length); + offset, p->block->used_length); return -1; } p->normal[i] = offset; } + for (i = 0; i < p->zero_num; i++) { + uint64_t offset = be64_to_cpu(packet->offset[p->normal_num + i]); + + if (offset > (p->block->used_length - p->page_size)) { + error_setg(errp, "multifd: offset too long %" PRIu64 + " (max " RAM_ADDR_FMT ")", + offset, p->block->used_length); + return -1; + } + p->zero[i] = offset; + } + return 0; } -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; +static bool multifd_send_should_exit(void) +{ + return qatomic_read(&multifd_send_state->exiting); +} + +static bool multifd_recv_should_exit(void) +{ + return qatomic_read(&multifd_recv_state->exiting); +} + +/* + * The migration thread can wait on either of the two semaphores. This + * function can be used to kick the main thread out of waiting on either of + * them. Should mostly only be called when something wrong happened with + * the current multifd send thread. + */ +static void multifd_send_kick_main(MultiFDSendParams *p) +{ + qemu_sem_post(&p->sem_sync); + qemu_sem_post(&multifd_send_state->channels_ready); +} /* * How we use multifd_send_state->pages and channel->pages? @@ -390,21 +570,23 @@ struct { * 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. + * + * Returns true if succeed, false otherwise. */ - -static int multifd_send_pages(QEMUFile *f) +static bool multifd_send_pages(void) { int i; static int next_channel; MultiFDSendParams *p = NULL; /* make happy gcc */ MultiFDPages_t *pages = multifd_send_state->pages; - uint64_t transferred; - if (qatomic_read(&multifd_send_state->exiting)) { - return -1; + if (multifd_send_should_exit()) { + return false; } + /* We wait here, until at least one channel is ready */ qemu_sem_wait(&multifd_send_state->channels_ready); + /* * next_channel can remain from a previous migration that was * using more channels, so ensure it doesn't overflow if the @@ -412,71 +594,100 @@ static int multifd_send_pages(QEMUFile *f) */ next_channel %= migrate_multifd_channels(); 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 (multifd_send_should_exit()) { + return false; } - if (!p->pending_job) { - p->pending_job++; + p = &multifd_send_state->params[i]; + /* + * Lockless read to p->pending_job is safe, because only multifd + * sender thread can clear it. + */ + if (qatomic_read(&p->pending_job) == false) { next_channel = (i + 1) % migrate_multifd_channels(); break; } - qemu_mutex_unlock(&p->mutex); } - assert(!p->pages->num); - assert(!p->pages->block); - p->packet_num = multifd_send_state->packet_num++; + /* + * Make sure we read p->pending_job before all the rest. Pairs with + * qatomic_store_release() in multifd_send_thread(). + */ + smp_mb_acquire(); + assert(!p->pages->num); multifd_send_state->pages = p->pages; p->pages = pages; - transferred = ((uint64_t) pages->num) * 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); + /* + * Making sure p->pages is setup before marking pending_job=true. Pairs + * with the qatomic_load_acquire() in multifd_send_thread(). + */ + qatomic_store_release(&p->pending_job, true); qemu_sem_post(&p->sem); - return 1; + return true; } -int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset) +static inline bool multifd_queue_empty(MultiFDPages_t *pages) { - MultiFDPages_t *pages = multifd_send_state->pages; + return pages->num == 0; +} - if (!pages->block) { +static inline bool multifd_queue_full(MultiFDPages_t *pages) +{ + return pages->num == pages->allocated; +} + +static inline void multifd_enqueue(MultiFDPages_t *pages, ram_addr_t offset) +{ + pages->offset[pages->num++] = offset; +} + +/* Returns true if enqueue successful, false otherwise */ +bool multifd_queue_page(RAMBlock *block, ram_addr_t offset) +{ + MultiFDPages_t *pages; + +retry: + pages = multifd_send_state->pages; + + /* If the queue is empty, we can already enqueue now */ + if (multifd_queue_empty(pages)) { pages->block = block; + multifd_enqueue(pages, offset); + return true; } - if (pages->block == block) { - pages->offset[pages->num] = offset; - pages->num++; - - if (pages->num < pages->allocated) { - return 1; + /* + * Not empty, meanwhile we need a flush. It can because of either: + * + * (1) The page is not on the same ramblock of previous ones, or, + * (2) The queue is full. + * + * After flush, always retry. + */ + if (pages->block != block || multifd_queue_full(pages)) { + if (!multifd_send_pages()) { + return false; } + goto retry; } - if (multifd_send_pages(f) < 0) { - return -1; - } - - if (pages->block != block) { - return multifd_queue_page(f, block, offset); - } - - return 1; + /* Not empty, and we still have space, do it! */ + multifd_enqueue(pages, offset); + return true; } -static void multifd_send_terminate_threads(Error *err) +/* Multifd send side hit an error; remember it and prepare to quit */ +static void multifd_send_set_error(Error *err) { - int i; - - trace_multifd_send_terminate_threads(err != NULL); + /* + * 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 (qatomic_xchg(&multifd_send_state->exiting, 1)) { + return; + } if (err) { MigrationState *s = migrate_get_current(); @@ -489,74 +700,100 @@ static void multifd_send_terminate_threads(Error *err) MIGRATION_STATUS_FAILED); } } +} + +static void multifd_send_terminate_threads(void) +{ + int i; + + trace_multifd_send_terminate_threads(); /* - * 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. + * Tell everyone we're quitting. No xchg() needed here; we simply + * always set it. */ - if (qatomic_xchg(&multifd_send_state->exiting, 1)) { - return; - } + qatomic_set(&multifd_send_state->exiting, 1); + /* + * Firstly, kick all threads out; no matter whether they are just idle, + * or blocked in an IO system call. + */ 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); if (p->c) { qio_channel_shutdown(p->c, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); } - qemu_mutex_unlock(&p->mutex); } -} -void multifd_save_cleanup(void) -{ - int i; - - if (!migrate_use_multifd() || !migrate_multi_channels_is_allowed()) { - return; - } - multifd_send_terminate_threads(NULL); + /* + * Finally recycle all the threads. + */ for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; - if (p->running) { + if (p->tls_thread_created) { + qemu_thread_join(&p->tls_thread); + } + + if (p->thread_created) { qemu_thread_join(&p->thread); } } - for (i = 0; i < migrate_multifd_channels(); i++) { - MultiFDSendParams *p = &multifd_send_state->params[i]; - Error *local_err = NULL; +} - if (p->registered_yank) { - migration_ioc_unregister_yank(p->c); - } - socket_send_channel_destroy(p->c); +static bool multifd_send_cleanup_channel(MultiFDSendParams *p, Error **errp) +{ + if (p->c) { + migration_ioc_unregister_yank(p->c); + /* + * The object_unref() cannot guarantee the fd will always be + * released because finalize() of the iochannel is only + * triggered on the last reference and it's not guaranteed + * that we always hold the last refcount when reaching here. + * + * Closing the fd explicitly has the benefit that if there is any + * registered I/O handler callbacks on such fd, that will get a + * POLLNVAL event and will further trigger the cleanup to finally + * release the IOC. + * + * FIXME: It should logically be guaranteed that all multifd + * channels have no I/O handler callback registered when reaching + * here, because migration thread will wait for all multifd channel + * establishments to complete during setup. Since + * migrate_fd_cleanup() will be scheduled in main thread too, all + * previous callbacks should guarantee to be completed when + * reaching here. See multifd_send_state.channels_created and its + * usage. In the future, we could replace this with an assert + * making sure we're the last reference, or simply drop it if above + * is more clear to be justified. + */ + qio_channel_close(p->c, &error_abort); + object_unref(OBJECT(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; - g_free(p->iov); - p->iov = NULL; - g_free(p->normal); - p->normal = NULL; - multifd_send_state->ops->send_cleanup(p, &local_err); - if (local_err) { - migrate_set_error(migrate_get_current(), local_err); - error_free(local_err); - } } + 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; + g_free(p->iov); + p->iov = NULL; + multifd_send_state->ops->send_cleanup(p, errp); + + return *errp == NULL; +} + +static void multifd_send_cleanup_state(void) +{ + file_cleanup_outgoing_migration(); + socket_cleanup_outgoing_migration(); + qemu_sem_destroy(&multifd_send_state->channels_created); qemu_sem_destroy(&multifd_send_state->channels_ready); g_free(multifd_send_state->params); multifd_send_state->params = NULL; @@ -566,72 +803,94 @@ void multifd_save_cleanup(void) multifd_send_state = NULL; } -int multifd_send_sync_main(QEMUFile *f) +void multifd_send_shutdown(void) +{ + int i; + + if (!migrate_multifd()) { + return; + } + + multifd_send_terminate_threads(); + + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDSendParams *p = &multifd_send_state->params[i]; + Error *local_err = NULL; + + if (!multifd_send_cleanup_channel(p, &local_err)) { + migrate_set_error(migrate_get_current(), local_err); + error_free(local_err); + } + } + + multifd_send_cleanup_state(); +} + +static int multifd_zero_copy_flush(QIOChannel *c) +{ + int ret; + Error *err = NULL; + + ret = qio_channel_flush(c, &err); + if (ret < 0) { + error_report_err(err); + return -1; + } + if (ret == 1) { + stat64_add(&mig_stats.dirty_sync_missed_zero_copy, 1); + } + + return ret; +} + +int multifd_send_sync_main(void) { int i; bool flush_zero_copy; - if (!migrate_use_multifd()) { + if (!migrate_multifd()) { return 0; } if (multifd_send_state->pages->num) { - if (multifd_send_pages(f) < 0) { + if (!multifd_send_pages()) { error_report("%s: multifd_send_pages fail", __func__); return -1; } } - /* - * When using zero-copy, it's necessary to flush the pages before any of - * the pages can be sent again, so we'll make sure the new version of the - * pages will always arrive _later_ than the old pages. - * - * Currently we achieve this by flushing the zero-page requested writes - * per ram iteration, but in the future we could potentially optimize it - * to be less frequent, e.g. only after we finished one whole scanning of - * all the dirty bitmaps. - */ - - flush_zero_copy = migrate_use_zero_copy_send(); + flush_zero_copy = migrate_zero_copy_send(); 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); + if (multifd_send_should_exit()) { return -1; } - 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); + trace_multifd_send_sync_main_signal(p->id); + + /* + * We should be the only user so far, so not possible to be set by + * others concurrently. + */ + assert(qatomic_read(&p->pending_sync) == false); + qatomic_set(&p->pending_sync, true); qemu_sem_post(&p->sem); - - if (flush_zero_copy && p->c) { - int ret; - Error *err = NULL; - - ret = qio_channel_flush(p->c, &err); - if (ret < 0) { - error_report_err(err); - return -1; - } - } } for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; + if (multifd_send_should_exit()) { + return -1; + } + + qemu_sem_wait(&multifd_send_state->channels_ready); trace_multifd_send_sync_main_wait(p->id); qemu_sem_wait(&p->sem_sync); + + if (flush_zero_copy && p->c && (multifd_zero_copy_flush(p->c) < 0)) { + return -1; + } } trace_multifd_send_sync_main(multifd_send_state->packet_num); @@ -641,281 +900,266 @@ int multifd_send_sync_main(QEMUFile *f) static void *multifd_send_thread(void *opaque) { MultiFDSendParams *p = opaque; + MigrationThread *thread = NULL; Error *local_err = NULL; int ret = 0; - bool use_zero_copy_send = migrate_use_zero_copy_send(); + bool use_packets = multifd_use_packets(); + + thread = migration_threads_add(p->name, qemu_get_thread_id()); trace_multifd_send_thread_start(p->id); rcu_register_thread(); - if (multifd_send_initial_packet(p, &local_err) < 0) { - ret = -1; - goto out; + if (use_packets) { + if (multifd_send_initial_packet(p, &local_err) < 0) { + ret = -1; + goto out; + } } - /* initial packet */ - p->num_packets = 1; while (true) { + qemu_sem_post(&multifd_send_state->channels_ready); qemu_sem_wait(&p->sem); - if (qatomic_read(&multifd_send_state->exiting)) { + if (multifd_send_should_exit()) { break; } - qemu_mutex_lock(&p->mutex); - if (p->pending_job) { - uint64_t packet_num = p->packet_num; - uint32_t flags = p->flags; - p->normal_num = 0; + /* + * Read pending_job flag before p->pages. Pairs with the + * qatomic_store_release() in multifd_send_pages(). + */ + if (qatomic_load_acquire(&p->pending_job)) { + MultiFDPages_t *pages = p->pages; - if (use_zero_copy_send) { - p->iovs_num = 0; + p->iovs_num = 0; + assert(pages->num); + + ret = multifd_send_state->ops->send_prepare(p, &local_err); + if (ret != 0) { + break; + } + + if (migrate_mapped_ram()) { + ret = file_write_ramblock_iov(p->c, p->iov, p->iovs_num, + p->pages->block, &local_err); } else { - p->iovs_num = 1; + ret = qio_channel_writev_full_all(p->c, p->iov, p->iovs_num, + NULL, 0, p->write_flags, + &local_err); } - for (int i = 0; i < p->pages->num; i++) { - p->normal[p->normal_num] = p->pages->offset[i]; - p->normal_num++; + if (ret != 0) { + break; } - if (p->normal_num) { - ret = multifd_send_state->ops->send_prepare(p, &local_err); - if (ret != 0) { - qemu_mutex_unlock(&p->mutex); - break; - } - } - multifd_send_fill_packet(p); - p->flags = 0; - p->num_packets++; - p->total_normal_pages += p->normal_num; - p->pages->num = 0; - p->pages->block = NULL; - qemu_mutex_unlock(&p->mutex); + stat64_add(&mig_stats.multifd_bytes, + p->next_packet_size + p->packet_len); + stat64_add(&mig_stats.normal_pages, pages->normal_num); + stat64_add(&mig_stats.zero_pages, pages->num - pages->normal_num); - trace_multifd_send(p->id, packet_num, p->normal_num, flags, - p->next_packet_size); + multifd_pages_reset(p->pages); + p->next_packet_size = 0; - if (use_zero_copy_send) { - /* Send header first, without zerocopy */ + /* + * Making sure p->pages is published before saying "we're + * free". Pairs with the smp_mb_acquire() in + * multifd_send_pages(). + */ + qatomic_store_release(&p->pending_job, false); + } else { + /* + * If not a normal job, must be a sync request. Note that + * pending_sync is a standalone flag (unlike pending_job), so + * it doesn't require explicit memory barriers. + */ + assert(qatomic_read(&p->pending_sync)); + + if (use_packets) { + p->flags = MULTIFD_FLAG_SYNC; + multifd_send_fill_packet(p); ret = qio_channel_write_all(p->c, (void *)p->packet, p->packet_len, &local_err); if (ret != 0) { break; } - } else { - /* Send header using the same writev call */ - p->iov[0].iov_len = p->packet_len; - p->iov[0].iov_base = p->packet; + /* p->next_packet_size will always be zero for a SYNC packet */ + stat64_add(&mig_stats.multifd_bytes, p->packet_len); + p->flags = 0; } - ret = qio_channel_writev_full_all(p->c, p->iov, p->iovs_num, NULL, - 0, p->write_flags, &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 */ + qatomic_set(&p->pending_sync, false); + qemu_sem_post(&p->sem_sync); } } out: - if (local_err) { + if (ret) { + assert(local_err); trace_multifd_send_error(p->id); - multifd_send_terminate_threads(local_err); + multifd_send_set_error(local_err); + multifd_send_kick_main(p); error_free(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->total_normal_pages); + migration_threads_remove(thread); + trace_multifd_send_thread_end(p->id, p->packets_sent, p->total_normal_pages, + p->total_zero_pages); return NULL; } -static bool multifd_channel_connect(MultiFDSendParams *p, - QIOChannel *ioc, - Error *error); +static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque); -static void multifd_tls_outgoing_handshake(QIOTask *task, - gpointer opaque) -{ - MultiFDSendParams *p = opaque; - QIOChannel *ioc = QIO_CHANNEL(qio_task_get_source(task)); - Error *err = NULL; - - if (qio_task_propagate_error(task, &err)) { - trace_multifd_tls_outgoing_handshake_error(ioc, error_get_pretty(err)); - } else { - trace_multifd_tls_outgoing_handshake_complete(ioc); - } - - if (!multifd_channel_connect(p, ioc, err)) { - /* - * Error happen, mark multifd_send_thread status as 'quit' although it - * is not created, and then tell who pay attention to me. - */ - p->quit = true; - qemu_sem_post(&multifd_send_state->channels_ready); - qemu_sem_post(&p->sem_sync); - } -} +typedef struct { + MultiFDSendParams *p; + QIOChannelTLS *tioc; +} MultiFDTLSThreadArgs; static void *multifd_tls_handshake_thread(void *opaque) { - MultiFDSendParams *p = opaque; - QIOChannelTLS *tioc = QIO_CHANNEL_TLS(p->c); + MultiFDTLSThreadArgs *args = opaque; - qio_channel_tls_handshake(tioc, - multifd_tls_outgoing_handshake, - p, + qio_channel_tls_handshake(args->tioc, + multifd_new_send_channel_async, + args->p, NULL, NULL); + g_free(args); + return NULL; } -static void multifd_tls_channel_connect(MultiFDSendParams *p, +static bool multifd_tls_channel_connect(MultiFDSendParams *p, QIOChannel *ioc, Error **errp) { MigrationState *s = migrate_get_current(); const char *hostname = s->hostname; + MultiFDTLSThreadArgs *args; QIOChannelTLS *tioc; - tioc = migration_tls_client_create(s, ioc, hostname, errp); + tioc = migration_tls_client_create(ioc, hostname, errp); if (!tioc) { - return; + return false; } + /* + * Ownership of the socket channel now transfers to the newly + * created TLS channel, which has already taken a reference. + */ object_unref(OBJECT(ioc)); trace_multifd_tls_outgoing_handshake_start(ioc, tioc, hostname); qio_channel_set_name(QIO_CHANNEL(tioc), "multifd-tls-outgoing"); - p->c = QIO_CHANNEL(tioc); - qemu_thread_create(&p->thread, "multifd-tls-handshake-worker", - multifd_tls_handshake_thread, p, + + args = g_new0(MultiFDTLSThreadArgs, 1); + args->tioc = tioc; + args->p = p; + + p->tls_thread_created = true; + qemu_thread_create(&p->tls_thread, "multifd-tls-handshake-worker", + multifd_tls_handshake_thread, args, + QEMU_THREAD_JOINABLE); + return true; +} + +void multifd_channel_connect(MultiFDSendParams *p, QIOChannel *ioc) +{ + qio_channel_set_delay(ioc, false); + + migration_ioc_register_yank(ioc); + /* Setup p->c only if the channel is completely setup */ + p->c = ioc; + + p->thread_created = true; + qemu_thread_create(&p->thread, p->name, multifd_send_thread, p, QEMU_THREAD_JOINABLE); } -static bool multifd_channel_connect(MultiFDSendParams *p, - QIOChannel *ioc, - Error *error) -{ - trace_multifd_set_outgoing_channel( - ioc, object_get_typename(OBJECT(ioc)), - migrate_get_current()->hostname, error); - - if (!error) { - if (migrate_use_tls() && - !object_dynamic_cast(OBJECT(ioc), - TYPE_QIO_CHANNEL_TLS)) { - multifd_tls_channel_connect(p, ioc, &error); - if (!error) { - /* - * tls_channel_connect will call back to this - * function after the TLS handshake, - * so we mustn't call multifd_send_thread until then - */ - return true; - } else { - return false; - } - } else { - migration_ioc_register_yank(ioc); - p->registered_yank = true; - p->c = ioc; - qemu_thread_create(&p->thread, p->name, multifd_send_thread, p, - QEMU_THREAD_JOINABLE); - } - return true; - } - - return false; -} - -static void multifd_new_send_channel_cleanup(MultiFDSendParams *p, - QIOChannel *ioc, Error *err) -{ - migrate_set_error(migrate_get_current(), 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; - object_unref(OBJECT(ioc)); - error_free(err); -} - +/* + * When TLS is enabled this function is called once to establish the + * TLS connection and a second time after the TLS handshake to create + * the multifd channel. Without TLS it goes straight into the channel + * creation. + */ static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque) { MultiFDSendParams *p = opaque; - QIOChannel *sioc = QIO_CHANNEL(qio_task_get_source(task)); + QIOChannel *ioc = QIO_CHANNEL(qio_task_get_source(task)); Error *local_err = NULL; + bool ret; trace_multifd_new_send_channel_async(p->id); + if (qio_task_propagate_error(task, &local_err)) { - goto cleanup; - } else { - p->c = QIO_CHANNEL(sioc); - qio_channel_set_delay(p->c, false); - p->running = true; - if (!multifd_channel_connect(p, sioc, local_err)) { - goto cleanup; + ret = false; + goto out; + } + + trace_multifd_set_outgoing_channel(ioc, object_get_typename(OBJECT(ioc)), + migrate_get_current()->hostname); + + if (migrate_channel_requires_tls_upgrade(ioc)) { + ret = multifd_tls_channel_connect(p, ioc, &local_err); + if (ret) { + return; } + } else { + multifd_channel_connect(p, ioc); + ret = true; + } + +out: + /* + * Here we're not interested whether creation succeeded, only that + * it happened at all. + */ + multifd_send_channel_created(); + + if (ret) { return; } -cleanup: - multifd_new_send_channel_cleanup(p, sioc, local_err); + trace_multifd_new_send_channel_async_error(p->id, local_err); + multifd_send_set_error(local_err); + /* + * For error cases (TLS or non-TLS), IO channel is always freed here + * rather than when cleanup multifd: since p->c is not set, multifd + * cleanup code doesn't even know its existence. + */ + object_unref(OBJECT(ioc)); + error_free(local_err); } -int multifd_save_setup(Error **errp) +static bool multifd_new_send_channel_create(gpointer opaque, Error **errp) { - int thread_count; + if (!multifd_use_packets()) { + return file_send_channel_create(opaque, errp); + } + + socket_send_channel_create(multifd_new_send_channel_async, opaque); + return true; +} + +bool multifd_send_setup(void) +{ + MigrationState *s = migrate_get_current(); + Error *local_err = NULL; + int thread_count, ret = 0; uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); + bool use_packets = multifd_use_packets(); uint8_t i; - if (!migrate_use_multifd()) { - return 0; - } - if (!migrate_multi_channels_is_allowed()) { - error_setg(errp, "multifd is not supported by current protocol"); - return -1; + if (!migrate_multifd()) { + return true; } thread_count = migrate_multifd_channels(); multifd_send_state = g_malloc0(sizeof(*multifd_send_state)); multifd_send_state->params = g_new0(MultiFDSendParams, thread_count); multifd_send_state->pages = multifd_pages_init(page_count); + qemu_sem_init(&multifd_send_state->channels_created, 0); qemu_sem_init(&multifd_send_state->channels_ready, 0); qatomic_set(&multifd_send_state->exiting, 0); multifd_send_state->ops = multifd_ops[migrate_multifd_compression()]; @@ -923,57 +1167,112 @@ int multifd_save_setup(Error **errp) 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); - /* We need one extra place for the packet header */ - p->iov = g_new0(struct iovec, page_count + 1); - p->normal = g_new0(ram_addr_t, page_count); - if (migrate_use_zero_copy_send()) { - p->write_flags = QIO_CHANNEL_WRITE_FLAG_ZERO_COPY; + if (use_packets) { + p->packet_len = sizeof(MultiFDPacket_t) + + sizeof(uint64_t) * page_count; + p->packet = g_malloc0(p->packet_len); + p->packet->magic = cpu_to_be32(MULTIFD_MAGIC); + p->packet->version = cpu_to_be32(MULTIFD_VERSION); + + /* We need one extra place for the packet header */ + p->iov = g_new0(struct iovec, page_count + 1); } else { - p->write_flags = 0; + p->iov = g_new0(struct iovec, page_count); } + p->name = g_strdup_printf("multifdsend_%d", i); + p->page_size = qemu_target_page_size(); + p->page_count = page_count; + p->write_flags = 0; - socket_send_channel_create(multifd_new_send_channel_async, p); + if (!multifd_new_send_channel_create(p, &local_err)) { + return false; + } + } + + /* + * Wait until channel creation has started for all channels. The + * creation can still fail, but no more channels will be created + * past this point. + */ + for (i = 0; i < thread_count; i++) { + qemu_sem_wait(&multifd_send_state->channels_created); } 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; + break; } } - return 0; + + if (ret) { + migrate_set_error(s, local_err); + error_report_err(local_err); + migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, + MIGRATION_STATUS_FAILED); + return false; + } + + return true; } -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; +bool multifd_recv(void) +{ + int i; + static int next_recv_channel; + MultiFDRecvParams *p = NULL; + MultiFDRecvData *data = multifd_recv_state->data; + + /* + * next_channel can remain from a previous migration that was + * using more channels, so ensure it doesn't overflow if the + * limit is lower now. + */ + next_recv_channel %= migrate_multifd_channels(); + for (i = next_recv_channel;; i = (i + 1) % migrate_multifd_channels()) { + if (multifd_recv_should_exit()) { + return false; + } + + p = &multifd_recv_state->params[i]; + + if (qatomic_read(&p->pending_job) == false) { + next_recv_channel = (i + 1) % migrate_multifd_channels(); + break; + } + } + + /* + * Order pending_job read before manipulating p->data below. Pairs + * with qatomic_store_release() at multifd_recv_thread(). + */ + smp_mb_acquire(); + + assert(!p->data->size); + multifd_recv_state->data = p->data; + p->data = data; + + /* + * Order p->data update before setting pending_job. Pairs with + * qatomic_load_acquire() at multifd_recv_thread(). + */ + qatomic_store_release(&p->pending_job, true); + qemu_sem_post(&p->sem); + + return true; +} + +MultiFDRecvData *multifd_get_recv_data(void) +{ + return multifd_recv_state->data; +} static void multifd_recv_terminate_threads(Error *err) { @@ -981,6 +1280,10 @@ static void multifd_recv_terminate_threads(Error *err) trace_multifd_recv_terminate_threads(err != NULL); + if (qatomic_xchg(&multifd_recv_state->exiting, 1)) { + return; + } + if (err) { MigrationState *s = migrate_get_current(); migrate_set_error(s, err); @@ -994,8 +1297,29 @@ static void multifd_recv_terminate_threads(Error *err) for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; - qemu_mutex_lock(&p->mutex); - p->quit = true; + /* + * The migration thread and channels interact differently + * depending on the presence of packets. + */ + if (multifd_use_packets()) { + /* + * The channel receives as long as there are packets. When + * packets end (i.e. MULTIFD_FLAG_SYNC is reached), the + * channel waits for the migration thread to sync. If the + * sync never happens, do it here. + */ + qemu_sem_post(&p->sem_sync); + } else { + /* + * The channel waits for the migration thread to give it + * work. When the migration thread runs out of work, it + * releases the channel and waits for any pending work to + * finish. If we reach here (e.g. due to error) before the + * work runs out, release the channel. + */ + qemu_sem_post(&p->sem); + } + /* * We could arrive here for two reasons: * - normal quit, i.e. everything went fine, just finished @@ -1005,73 +1329,119 @@ static void multifd_recv_terminate_threads(Error *err) if (p->c) { qio_channel_shutdown(p->c, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); } - qemu_mutex_unlock(&p->mutex); } } -int multifd_load_cleanup(Error **errp) +void multifd_recv_shutdown(void) +{ + if (migrate_multifd()) { + multifd_recv_terminate_threads(NULL); + } +} + +static void multifd_recv_cleanup_channel(MultiFDRecvParams *p) +{ + migration_ioc_unregister_yank(p->c); + object_unref(OBJECT(p->c)); + p->c = NULL; + qemu_mutex_destroy(&p->mutex); + qemu_sem_destroy(&p->sem_sync); + qemu_sem_destroy(&p->sem); + g_free(p->name); + p->name = NULL; + p->packet_len = 0; + g_free(p->packet); + p->packet = NULL; + g_free(p->iov); + p->iov = NULL; + g_free(p->normal); + p->normal = NULL; + g_free(p->zero); + p->zero = NULL; + multifd_recv_state->ops->recv_cleanup(p); +} + +static void multifd_recv_cleanup_state(void) +{ + qemu_sem_destroy(&multifd_recv_state->sem_sync); + g_free(multifd_recv_state->params); + multifd_recv_state->params = NULL; + g_free(multifd_recv_state->data); + multifd_recv_state->data = NULL; + g_free(multifd_recv_state); + multifd_recv_state = NULL; +} + +void multifd_recv_cleanup(void) { int i; - if (!migrate_use_multifd() || !migrate_multi_channels_is_allowed()) { - return 0; + if (!migrate_multifd()) { + return; } 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); + if (p->thread_created) { qemu_thread_join(&p->thread); } } for (i = 0; i < migrate_multifd_channels(); i++) { - MultiFDRecvParams *p = &multifd_recv_state->params[i]; - - migration_ioc_unregister_yank(p->c); - 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; - p->packet_len = 0; - g_free(p->packet); - p->packet = NULL; - g_free(p->iov); - p->iov = NULL; - g_free(p->normal); - p->normal = NULL; - multifd_recv_state->ops->recv_cleanup(p); + multifd_recv_cleanup_channel(&multifd_recv_state->params[i]); } - 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; + multifd_recv_cleanup_state(); } void multifd_recv_sync_main(void) { + int thread_count = migrate_multifd_channels(); + bool file_based = !multifd_use_packets(); int i; - if (!migrate_use_multifd()) { + if (!migrate_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); + /* + * File-based channels don't use packets and therefore need to + * wait for more work. Release them to start the sync. + */ + if (file_based) { + for (i = 0; i < thread_count; i++) { + MultiFDRecvParams *p = &multifd_recv_state->params[i]; + + trace_multifd_recv_sync_main_signal(p->id); + qemu_sem_post(&p->sem); + } + } + + /* + * Initiate the synchronization by waiting for all channels. + * + * For socket-based migration this means each channel has received + * the SYNC packet on the stream. + * + * For file-based migration this means each channel is done with + * the work (pending_job=false). + */ + for (i = 0; i < thread_count; i++) { + trace_multifd_recv_sync_main_wait(i); qemu_sem_wait(&multifd_recv_state->sem_sync); } - for (i = 0; i < migrate_multifd_channels(); i++) { + + if (file_based) { + /* + * For file-based loading is done in one iteration. We're + * done. + */ + return; + } + + /* + * Sync done. Release the channels for the next iteration. + */ + for (i = 0; i < thread_count; i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; WITH_QEMU_LOCK_GUARD(&p->mutex) { @@ -1089,53 +1459,87 @@ static void *multifd_recv_thread(void *opaque) { MultiFDRecvParams *p = opaque; Error *local_err = NULL; + bool use_packets = multifd_use_packets(); int ret; trace_multifd_recv_thread_start(p->id); rcu_register_thread(); while (true) { - uint32_t flags; + uint32_t flags = 0; + bool has_data = false; + p->normal_num = 0; - if (p->quit) { - break; - } + if (use_packets) { + if (multifd_recv_should_exit()) { + 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; - } + ret = qio_channel_read_all_eof(p->c, (void *)p->packet, + p->packet_len, &local_err); + if (ret == 0 || ret == -1) { /* 0: EOF -1: Error */ + break; + } - qemu_mutex_lock(&p->mutex); - ret = multifd_recv_unfill_packet(p, &local_err); - if (ret) { + qemu_mutex_lock(&p->mutex); + ret = multifd_recv_unfill_packet(p, &local_err); + if (ret) { + qemu_mutex_unlock(&p->mutex); + break; + } + + flags = p->flags; + /* recv methods don't know how to handle the SYNC flag */ + p->flags &= ~MULTIFD_FLAG_SYNC; + has_data = p->normal_num || p->zero_num; qemu_mutex_unlock(&p->mutex); - break; + } else { + /* + * No packets, so we need to wait for the vmstate code to + * give us work. + */ + qemu_sem_wait(&p->sem); + + if (multifd_recv_should_exit()) { + break; + } + + /* pairs with qatomic_store_release() at multifd_recv() */ + if (!qatomic_load_acquire(&p->pending_job)) { + /* + * Migration thread did not send work, this is + * equivalent to pending_sync on the sending + * side. Post sem_sync to notify we reached this + * point. + */ + qemu_sem_post(&multifd_recv_state->sem_sync); + continue; + } + + has_data = !!p->data->size; } - 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, p->normal_num, flags, - p->next_packet_size); - p->num_packets++; - p->total_normal_pages += p->normal_num; - qemu_mutex_unlock(&p->mutex); - - if (p->normal_num) { - ret = multifd_recv_state->ops->recv_pages(p, &local_err); + if (has_data) { + ret = multifd_recv_state->ops->recv(p, &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 (use_packets) { + if (flags & MULTIFD_FLAG_SYNC) { + qemu_sem_post(&multifd_recv_state->sem_sync); + qemu_sem_wait(&p->sem_sync); + } + } else { + p->total_normal_pages += p->data->size / qemu_target_page_size(); + p->data->size = 0; + /* + * Order data->size update before clearing + * pending_job. Pairs with smp_mb_acquire() at + * multifd_recv(). + */ + qatomic_store_release(&p->pending_job, false); } } @@ -1143,33 +1547,39 @@ static void *multifd_recv_thread(void *opaque) multifd_recv_terminate_threads(local_err); error_free(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->total_normal_pages); + trace_multifd_recv_thread_end(p->id, p->packets_recved, + p->total_normal_pages, + p->total_zero_pages); return NULL; } -int multifd_load_setup(Error **errp) +int multifd_recv_setup(Error **errp) { int thread_count; uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); + bool use_packets = multifd_use_packets(); uint8_t i; - if (!migrate_use_multifd()) { + /* + * Return successfully if multiFD recv state is already initialised + * or multiFD is not enabled. + */ + if (multifd_recv_state || !migrate_multifd()) { return 0; } - if (!migrate_multi_channels_is_allowed()) { - error_setg(errp, "multifd is not supported by current protocol"); - return -1; - } + thread_count = migrate_multifd_channels(); multifd_recv_state = g_malloc0(sizeof(*multifd_recv_state)); multifd_recv_state->params = g_new0(MultiFDRecvParams, thread_count); + + multifd_recv_state->data = g_new0(MultiFDRecvData, 1); + multifd_recv_state->data->size = 0; + qatomic_set(&multifd_recv_state->count, 0); + qatomic_set(&multifd_recv_state->exiting, 0); qemu_sem_init(&multifd_recv_state->sem_sync, 0); multifd_recv_state->ops = multifd_ops[migrate_multifd_compression()]; @@ -1178,24 +1588,32 @@ int multifd_load_setup(Error **errp) qemu_mutex_init(&p->mutex); qemu_sem_init(&p->sem_sync, 0); - p->quit = false; + qemu_sem_init(&p->sem, 0); + p->pending_job = false; p->id = i; - p->packet_len = sizeof(MultiFDPacket_t) - + sizeof(uint64_t) * page_count; - p->packet = g_malloc0(p->packet_len); + + p->data = g_new0(MultiFDRecvData, 1); + p->data->size = 0; + + if (use_packets) { + 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); p->iov = g_new0(struct iovec, page_count); p->normal = g_new0(ram_addr_t, page_count); + p->zero = g_new0(ram_addr_t, page_count); + p->page_count = page_count; + p->page_size = qemu_target_page_size(); } for (i = 0; i < thread_count; i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; - Error *local_err = NULL; int ret; - ret = multifd_recv_state->ops->recv_setup(p, &local_err); + ret = multifd_recv_state->ops->recv_setup(p, errp); if (ret) { - error_propagate(errp, local_err); return ret; } } @@ -1206,7 +1624,7 @@ bool multifd_recv_all_channels_created(void) { int thread_count = migrate_multifd_channels(); - if (!migrate_use_multifd()) { + if (!migrate_multifd()) { return true; } @@ -1220,26 +1638,29 @@ bool multifd_recv_all_channels_created(void) /* * Try to receive all multifd channels to get ready for the migration. - * - Return true and do not set @errp when correctly receiving 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. + * Sets @errp when failing to receive the current channel. */ -bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp) +void multifd_recv_new_channel(QIOChannel *ioc, Error **errp) { MultiFDRecvParams *p; Error *local_err = NULL; + bool use_packets = multifd_use_packets(); 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: ", - qatomic_read(&multifd_recv_state->count)); - return false; + if (use_packets) { + 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: ", + qatomic_read(&multifd_recv_state->count)); + return; + } + trace_multifd_recv_new_channel(id); + } else { + id = qatomic_read(&multifd_recv_state->count); } - trace_multifd_recv_new_channel(id); p = &multifd_recv_state->params[id]; if (p->c != NULL) { @@ -1247,17 +1668,27 @@ bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp) id); multifd_recv_terminate_threads(local_err); error_propagate(errp, local_err); - return false; + return; } p->c = ioc; object_ref(OBJECT(ioc)); - /* initial packet */ - p->num_packets = 1; - p->running = true; + p->thread_created = true; qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p, QEMU_THREAD_JOINABLE); qatomic_inc(&multifd_recv_state->count); - return qatomic_read(&multifd_recv_state->count) == - migrate_multifd_channels(); +} + +bool multifd_send_prepare_common(MultiFDSendParams *p) +{ + multifd_send_zero_page_detect(p); + + if (!p->pages->normal_num) { + p->next_packet_size = 0; + return false; + } + + multifd_send_prepare_header(p); + + return true; } diff --git a/migration/multifd.h b/migration/multifd.h index 4d8d89e5e5..c9d9b09239 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -13,15 +13,23 @@ #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); +#include "ram.h" + +typedef struct MultiFDRecvData MultiFDRecvData; + +bool multifd_send_setup(void); +void multifd_send_shutdown(void); +void multifd_send_channel_created(void); +int multifd_recv_setup(Error **errp); +void multifd_recv_cleanup(void); +void multifd_recv_shutdown(void); bool multifd_recv_all_channels_created(void); -bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp); +void multifd_recv_new_channel(QIOChannel *ioc, Error **errp); void multifd_recv_sync_main(void); -int multifd_send_sync_main(QEMUFile *f); -int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset); +int multifd_send_sync_main(void); +bool multifd_queue_page(RAMBlock *block, ram_addr_t offset); +bool multifd_recv(void); +MultiFDRecvData *multifd_get_recv_data(void); /* Multifd Compression flags */ #define MULTIFD_FLAG_SYNC (1 << 0) @@ -47,121 +55,171 @@ typedef struct { /* 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 */ + /* zero pages */ + uint32_t zero_pages; + uint32_t unused32[1]; /* Reserved for future use */ + uint64_t unused64[3]; /* Reserved for future use */ char ramblock[256]; + /* + * This array contains the pointers to: + * - normal pages (initial normal_pages entries) + * - zero pages (following zero_pages entries) + */ uint64_t offset[]; } __attribute__((packed)) MultiFDPacket_t; typedef struct { /* number of used pages */ uint32_t num; + /* number of normal pages */ + uint32_t normal_num; /* 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; RAMBlock *block; } MultiFDPages_t; +struct MultiFDRecvData { + void *opaque; + size_t size; + /* for preadv */ + off_t file_offset; +}; + typedef struct { - /* this fields are not changed once the thread is created */ + /* Fields are only written at creating/deletion time */ + /* No lock required for them, they are read only */ + /* channel number */ uint8_t id; /* channel thread name */ char *name; /* channel thread id */ QemuThread thread; + bool thread_created; + QemuThread tls_thread; + bool tls_thread_created; /* 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; - /* is the yank function registered */ - bool registered_yank; - /* 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; + /* guest page size */ + uint32_t page_size; + /* number of pages in a full packet */ + uint32_t page_count; /* multifd flags for sending ram */ int write_flags; - /* 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; - /* non zero pages sent through this channel */ - uint64_t total_normal_pages; + + /* sem where to wait for more work */ + QemuSemaphore sem; /* syncs main thread and channels */ QemuSemaphore sem_sync; + + /* multifd flags for each packet */ + uint32_t flags; + /* + * The sender thread has work to do if either of below boolean is set. + * + * @pending_job: a job is pending + * @pending_sync: a sync request is pending + * + * For both of these fields, they're only set by the requesters, and + * cleared by the multifd sender threads. + */ + bool pending_job; + bool pending_sync; + /* array of pages to sent. + * The owner of 'pages' depends of 'pending_job' value: + * pending_job == 0 -> migration_thread can use it. + * pending_job != 0 -> multifd_channel can use it. + */ + MultiFDPages_t *pages; + + /* thread local variables. No locking required */ + + /* pointer to the packet */ + MultiFDPacket_t *packet; + /* size of the next packet that contains pages */ + uint32_t next_packet_size; + /* packets sent through this channel */ + uint64_t packets_sent; + /* non zero pages sent through this channel */ + uint64_t total_normal_pages; + /* zero pages sent through this channel */ + uint64_t total_zero_pages; /* buffers to send */ struct iovec *iov; /* number of iovs used */ uint32_t iovs_num; - /* Pages that are not zero */ - ram_addr_t *normal; - /* num of non zero pages */ - uint32_t normal_num; /* used for compression methods */ - void *data; + void *compress_data; } MultiFDSendParams; typedef struct { - /* this fields are not changed once the thread is created */ + /* Fields are only written at creating/deletion time */ + /* No lock required for them, they are read only */ + /* channel number */ uint8_t id; /* channel thread name */ char *name; /* channel thread id */ QemuThread thread; + bool thread_created; /* 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; - /* ramblock host address */ - uint8_t *host; /* packet allocated len */ uint32_t packet_len; - /* pointer to the packet */ - MultiFDPacket_t *packet; + /* guest page size */ + uint32_t page_size; + /* number of pages in a full packet */ + uint32_t page_count; + + /* syncs main thread and channels */ + QemuSemaphore sem_sync; + /* sem where to wait for more work */ + QemuSemaphore sem; + + /* this mutex protects the following parameters */ + QemuMutex mutex; + /* should this thread finish */ + bool quit; /* multifd flags for each packet */ uint32_t flags; /* global number of generated multifd packets */ uint64_t packet_num; - /* thread local variables */ + int pending_job; + MultiFDRecvData *data; + + /* thread local variables. No locking required */ + + /* pointer to the packet */ + MultiFDPacket_t *packet; /* size of the next packet that contains pages */ uint32_t next_packet_size; - /* packets sent through this channel */ - uint64_t num_packets; + /* packets received through this channel */ + uint64_t packets_recved; + /* ramblock */ + RAMBlock *block; + /* ramblock host address */ + uint8_t *host; /* non zero pages recv through this channel */ uint64_t total_normal_pages; - /* syncs main thread and channels */ - QemuSemaphore sem_sync; + /* zero pages recv through this channel */ + uint64_t total_zero_pages; /* buffers to recv */ struct iovec *iov; /* Pages that are not zero */ ram_addr_t *normal; /* num of non zero pages */ uint32_t normal_num; + /* Pages that are zero */ + ram_addr_t *zero; + /* num of zero pages */ + uint32_t zero_num; /* used for de-compression methods */ - void *data; + void *compress_data; } MultiFDRecvParams; typedef struct { @@ -175,11 +233,23 @@ typedef struct { int (*recv_setup)(MultiFDRecvParams *p, Error **errp); /* Cleanup for receiving side */ void (*recv_cleanup)(MultiFDRecvParams *p); - /* Read all pages */ - int (*recv_pages)(MultiFDRecvParams *p, Error **errp); + /* Read all data */ + int (*recv)(MultiFDRecvParams *p, Error **errp); } MultiFDMethods; void multifd_register_ops(int method, MultiFDMethods *ops); +void multifd_send_fill_packet(MultiFDSendParams *p); +bool multifd_send_prepare_common(MultiFDSendParams *p); +void multifd_send_zero_page_detect(MultiFDSendParams *p); +void multifd_recv_zero_page_process(MultiFDRecvParams *p); + +static inline void multifd_send_prepare_header(MultiFDSendParams *p) +{ + p->iov[0].iov_len = p->packet_len; + p->iov[0].iov_base = p->packet; + p->iovs_num++; +} + +void multifd_channel_connect(MultiFDSendParams *p, QIOChannel *ioc); #endif - diff --git a/migration/options.c b/migration/options.c new file mode 100644 index 0000000000..bfd7753b69 --- /dev/null +++ b/migration/options.c @@ -0,0 +1,1608 @@ +/* + * QEMU migration capabilities + * + * Copyright (c) 2012-2023 Red Hat Inc + * + * Authors: + * Orit Wasserman + * 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/error-report.h" +#include "exec/target_page.h" +#include "qapi/clone-visitor.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-migration.h" +#include "qapi/qapi-visit-migration.h" +#include "qapi/qmp/qerror.h" +#include "qapi/qmp/qnull.h" +#include "sysemu/runstate.h" +#include "migration/colo.h" +#include "migration/misc.h" +#include "migration.h" +#include "migration-stats.h" +#include "qemu-file.h" +#include "ram.h" +#include "options.h" +#include "sysemu/kvm.h" + +/* Maximum migrate downtime set to 2000 seconds */ +#define MAX_MIGRATE_DOWNTIME_SECONDS 2000 +#define MAX_MIGRATE_DOWNTIME (MAX_MIGRATE_DOWNTIME_SECONDS * 1000) + +#define MAX_THROTTLE (128 << 20) /* Migration transfer speed throttling */ + +/* Time in milliseconds we are allowed to stop the source, + * for sending the last part */ +#define DEFAULT_MIGRATE_SET_DOWNTIME 300 + +/* Default compression thread count */ +#define DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT 8 +/* Default decompression thread count, usually decompression is at + * least 4 times as fast as compression.*/ +#define DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT 2 +/*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 + +/* Migration XBZRLE default cache size */ +#define DEFAULT_MIGRATE_XBZRLE_CACHE_SIZE (64 * 1024 * 1024) + +/* 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. + */ +#define DEFAULT_MIGRATE_MAX_POSTCOPY_BANDWIDTH 0 + +/* + * Parameters for self_announce_delay giving a stream of RARP/ARP + * packets after migration. + */ +#define DEFAULT_MIGRATE_ANNOUNCE_INITIAL 50 +#define DEFAULT_MIGRATE_ANNOUNCE_MAX 550 +#define DEFAULT_MIGRATE_ANNOUNCE_ROUNDS 5 +#define DEFAULT_MIGRATE_ANNOUNCE_STEP 100 + +#define DEFINE_PROP_MIG_CAP(name, x) \ + DEFINE_PROP_BOOL(name, MigrationState, capabilities[x], false) + +#define DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT_PERIOD 1000 /* milliseconds */ +#define DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT 1 /* MB/s */ + +Property migration_properties[] = { + DEFINE_PROP_BOOL("store-global-state", MigrationState, + store_global_state, true), + DEFINE_PROP_BOOL("send-configuration", MigrationState, + send_configuration, true), + DEFINE_PROP_BOOL("send-section-footer", MigrationState, + send_section_footer, true), + DEFINE_PROP_BOOL("decompress-error-check", MigrationState, + decompress_error_check, true), + DEFINE_PROP_BOOL("multifd-flush-after-each-section", MigrationState, + multifd_flush_after_each_section, false), + DEFINE_PROP_UINT8("x-clear-bitmap-shift", MigrationState, + clear_bitmap_shift, CLEAR_BITMAP_SHIFT_DEFAULT), + DEFINE_PROP_BOOL("x-preempt-pre-7-2", MigrationState, + preempt_pre_7_2, false), + + /* Migration parameters */ + DEFINE_PROP_UINT8("x-compress-level", MigrationState, + parameters.compress_level, + DEFAULT_MIGRATE_COMPRESS_LEVEL), + DEFINE_PROP_UINT8("x-compress-threads", MigrationState, + parameters.compress_threads, + DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT), + DEFINE_PROP_BOOL("x-compress-wait-thread", MigrationState, + parameters.compress_wait_thread, true), + 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), + DEFINE_PROP_UINT8("x-cpu-throttle-increment", MigrationState, + parameters.cpu_throttle_increment, + DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT), + DEFINE_PROP_BOOL("x-cpu-throttle-tailslow", MigrationState, + parameters.cpu_throttle_tailslow, false), + DEFINE_PROP_SIZE("x-max-bandwidth", MigrationState, + parameters.max_bandwidth, MAX_THROTTLE), + DEFINE_PROP_SIZE("avail-switchover-bandwidth", MigrationState, + parameters.avail_switchover_bandwidth, 0), + DEFINE_PROP_UINT64("x-downtime-limit", MigrationState, + parameters.downtime_limit, + DEFAULT_MIGRATE_SET_DOWNTIME), + DEFINE_PROP_UINT32("x-checkpoint-delay", MigrationState, + parameters.x_checkpoint_delay, + DEFAULT_MIGRATE_X_CHECKPOINT_DELAY), + 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), + DEFINE_PROP_SIZE("max-postcopy-bandwidth", MigrationState, + parameters.max_postcopy_bandwidth, + DEFAULT_MIGRATE_MAX_POSTCOPY_BANDWIDTH), + DEFINE_PROP_UINT8("max-cpu-throttle", MigrationState, + parameters.max_cpu_throttle, + DEFAULT_MIGRATE_MAX_CPU_THROTTLE), + DEFINE_PROP_SIZE("announce-initial", MigrationState, + parameters.announce_initial, + DEFAULT_MIGRATE_ANNOUNCE_INITIAL), + DEFINE_PROP_SIZE("announce-max", MigrationState, + parameters.announce_max, + DEFAULT_MIGRATE_ANNOUNCE_MAX), + DEFINE_PROP_SIZE("announce-rounds", MigrationState, + parameters.announce_rounds, + DEFAULT_MIGRATE_ANNOUNCE_ROUNDS), + DEFINE_PROP_SIZE("announce-step", MigrationState, + parameters.announce_step, + DEFAULT_MIGRATE_ANNOUNCE_STEP), + DEFINE_PROP_STRING("tls-creds", MigrationState, parameters.tls_creds), + DEFINE_PROP_STRING("tls-hostname", MigrationState, parameters.tls_hostname), + DEFINE_PROP_STRING("tls-authz", MigrationState, parameters.tls_authz), + DEFINE_PROP_UINT64("x-vcpu-dirty-limit-period", MigrationState, + parameters.x_vcpu_dirty_limit_period, + DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT_PERIOD), + DEFINE_PROP_UINT64("vcpu-dirty-limit", MigrationState, + parameters.vcpu_dirty_limit, + DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT), + DEFINE_PROP_MIG_MODE("mode", MigrationState, + parameters.mode, + MIG_MODE_NORMAL), + DEFINE_PROP_ZERO_PAGE_DETECTION("zero-page-detection", MigrationState, + parameters.zero_page_detection, + ZERO_PAGE_DETECTION_MULTIFD), + + /* Migration capabilities */ + DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE), + DEFINE_PROP_MIG_CAP("x-rdma-pin-all", MIGRATION_CAPABILITY_RDMA_PIN_ALL), + DEFINE_PROP_MIG_CAP("x-auto-converge", MIGRATION_CAPABILITY_AUTO_CONVERGE), + DEFINE_PROP_MIG_CAP("x-zero-blocks", MIGRATION_CAPABILITY_ZERO_BLOCKS), + DEFINE_PROP_MIG_CAP("x-compress", MIGRATION_CAPABILITY_COMPRESS), + DEFINE_PROP_MIG_CAP("x-events", MIGRATION_CAPABILITY_EVENTS), + DEFINE_PROP_MIG_CAP("x-postcopy-ram", MIGRATION_CAPABILITY_POSTCOPY_RAM), + DEFINE_PROP_MIG_CAP("x-postcopy-preempt", + MIGRATION_CAPABILITY_POSTCOPY_PREEMPT), + DEFINE_PROP_MIG_CAP("x-colo", MIGRATION_CAPABILITY_X_COLO), + DEFINE_PROP_MIG_CAP("x-release-ram", MIGRATION_CAPABILITY_RELEASE_RAM), + DEFINE_PROP_MIG_CAP("x-block", MIGRATION_CAPABILITY_BLOCK), + DEFINE_PROP_MIG_CAP("x-return-path", MIGRATION_CAPABILITY_RETURN_PATH), + DEFINE_PROP_MIG_CAP("x-multifd", MIGRATION_CAPABILITY_MULTIFD), + DEFINE_PROP_MIG_CAP("x-background-snapshot", + MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT), +#ifdef CONFIG_LINUX + DEFINE_PROP_MIG_CAP("x-zero-copy-send", + MIGRATION_CAPABILITY_ZERO_COPY_SEND), +#endif + DEFINE_PROP_MIG_CAP("x-switchover-ack", + MIGRATION_CAPABILITY_SWITCHOVER_ACK), + DEFINE_PROP_MIG_CAP("x-dirty-limit", MIGRATION_CAPABILITY_DIRTY_LIMIT), + DEFINE_PROP_MIG_CAP("mapped-ram", MIGRATION_CAPABILITY_MAPPED_RAM), + DEFINE_PROP_END_OF_LIST(), +}; + +bool migrate_auto_converge(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_AUTO_CONVERGE]; +} + +bool migrate_background_snapshot(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT]; +} + +bool migrate_block(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_BLOCK]; +} + +bool migrate_colo(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_X_COLO]; +} + +bool migrate_compress(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_COMPRESS]; +} + +bool migrate_dirty_bitmaps(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_DIRTY_BITMAPS]; +} + +bool migrate_dirty_limit(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_DIRTY_LIMIT]; +} + +bool migrate_events(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_EVENTS]; +} + +bool migrate_mapped_ram(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_MAPPED_RAM]; +} + +bool migrate_ignore_shared(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_X_IGNORE_SHARED]; +} + +bool migrate_late_block_activate(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_LATE_BLOCK_ACTIVATE]; +} + +bool migrate_multifd(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_MULTIFD]; +} + +bool migrate_pause_before_switchover(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_PAUSE_BEFORE_SWITCHOVER]; +} + +bool migrate_postcopy_blocktime(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME]; +} + +bool migrate_postcopy_preempt(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT]; +} + +bool migrate_postcopy_ram(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_POSTCOPY_RAM]; +} + +bool migrate_rdma_pin_all(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_RDMA_PIN_ALL]; +} + +bool migrate_release_ram(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_RELEASE_RAM]; +} + +bool migrate_return_path(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_RETURN_PATH]; +} + +bool migrate_switchover_ack(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_SWITCHOVER_ACK]; +} + +bool migrate_validate_uuid(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_VALIDATE_UUID]; +} + +bool migrate_xbzrle(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_XBZRLE]; +} + +bool migrate_zero_blocks(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_ZERO_BLOCKS]; +} + +bool migrate_zero_copy_send(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_ZERO_COPY_SEND]; +} + +/* pseudo capabilities */ + +bool migrate_multifd_flush_after_each_section(void) +{ + MigrationState *s = migrate_get_current(); + + return s->multifd_flush_after_each_section; +} + +bool migrate_postcopy(void) +{ + return migrate_postcopy_ram() || migrate_dirty_bitmaps(); +} + +bool migrate_rdma(void) +{ + MigrationState *s = migrate_get_current(); + + return s->rdma_migration; +} + +bool migrate_tls(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.tls_creds && *s->parameters.tls_creds; +} + +typedef enum WriteTrackingSupport { + WT_SUPPORT_UNKNOWN = 0, + WT_SUPPORT_ABSENT, + WT_SUPPORT_AVAILABLE, + WT_SUPPORT_COMPATIBLE +} WriteTrackingSupport; + +static +WriteTrackingSupport migrate_query_write_tracking(void) +{ + /* Check if kernel supports required UFFD features */ + if (!ram_write_tracking_available()) { + return WT_SUPPORT_ABSENT; + } + /* + * Check if current memory configuration is + * compatible with required UFFD features. + */ + if (!ram_write_tracking_compatible()) { + return WT_SUPPORT_AVAILABLE; + } + + return WT_SUPPORT_COMPATIBLE; +} + +/* Migration capabilities set */ +struct MigrateCapsSet { + int size; /* Capability set size */ + MigrationCapability caps[]; /* Variadic array of capabilities */ +}; +typedef struct MigrateCapsSet MigrateCapsSet; + +/* Define and initialize MigrateCapsSet */ +#define INITIALIZE_MIGRATE_CAPS_SET(_name, ...) \ + MigrateCapsSet _name = { \ + .size = sizeof((int []) { __VA_ARGS__ }) / sizeof(int), \ + .caps = { __VA_ARGS__ } \ + } + +/* Background-snapshot compatibility check list */ +static const +INITIALIZE_MIGRATE_CAPS_SET(check_caps_background_snapshot, + MIGRATION_CAPABILITY_POSTCOPY_RAM, + MIGRATION_CAPABILITY_DIRTY_BITMAPS, + MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME, + MIGRATION_CAPABILITY_LATE_BLOCK_ACTIVATE, + MIGRATION_CAPABILITY_RETURN_PATH, + MIGRATION_CAPABILITY_MULTIFD, + MIGRATION_CAPABILITY_PAUSE_BEFORE_SWITCHOVER, + MIGRATION_CAPABILITY_AUTO_CONVERGE, + MIGRATION_CAPABILITY_RELEASE_RAM, + MIGRATION_CAPABILITY_RDMA_PIN_ALL, + MIGRATION_CAPABILITY_COMPRESS, + MIGRATION_CAPABILITY_XBZRLE, + MIGRATION_CAPABILITY_X_COLO, + MIGRATION_CAPABILITY_VALIDATE_UUID, + MIGRATION_CAPABILITY_ZERO_COPY_SEND); + +static bool migrate_incoming_started(void) +{ + return !!migration_incoming_get_current()->transport_data; +} + +/** + * @migration_caps_check - check capability compatibility + * + * @old_caps: old capability list + * @new_caps: new capability list + * @errp: set *errp if the check failed, with reason + * + * Returns true if check passed, otherwise false. + */ +bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) +{ + ERRP_GUARD(); + MigrationIncomingState *mis = migration_incoming_get_current(); + +#ifndef CONFIG_LIVE_BLOCK_MIGRATION + if (new_caps[MIGRATION_CAPABILITY_BLOCK]) { + error_setg(errp, "QEMU compiled without old-style (blk/-b, inc/-i) " + "block migration"); + error_append_hint(errp, "Use blockdev-mirror with NBD instead.\n"); + return false; + } +#endif + if (new_caps[MIGRATION_CAPABILITY_BLOCK]) { + warn_report("block migration is deprecated;" + " use blockdev-mirror with NBD instead"); + } + + if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { + warn_report("old compression method is deprecated;" + " use multifd compression methods instead"); + } + +#ifndef CONFIG_REPLICATION + if (new_caps[MIGRATION_CAPABILITY_X_COLO]) { + error_setg(errp, "QEMU compiled without replication module" + " can't enable COLO"); + error_append_hint(errp, "Please enable replication before COLO.\n"); + return false; + } +#endif + + if (new_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) { + /* 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. + */ + if (!old_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] && + runstate_check(RUN_STATE_INMIGRATE) && + !postcopy_ram_supported_by_host(mis, errp)) { + error_prepend(errp, "Postcopy is not supported: "); + return false; + } + + if (new_caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED]) { + error_setg(errp, "Postcopy is not compatible with ignore-shared"); + return false; + } + + if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) { + error_setg(errp, "Postcopy is not yet compatible with multifd"); + return false; + } + } + + if (new_caps[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT]) { + WriteTrackingSupport wt_support; + int idx; + /* + * Check if 'background-snapshot' capability is supported by + * host kernel and compatible with guest memory configuration. + */ + wt_support = migrate_query_write_tracking(); + if (wt_support < WT_SUPPORT_AVAILABLE) { + error_setg(errp, "Background-snapshot is not supported by host kernel"); + return false; + } + if (wt_support < WT_SUPPORT_COMPATIBLE) { + error_setg(errp, "Background-snapshot is not compatible " + "with guest memory configuration"); + return false; + } + + /* + * Check if there are any migration capabilities + * incompatible with 'background-snapshot'. + */ + for (idx = 0; idx < check_caps_background_snapshot.size; idx++) { + int incomp_cap = check_caps_background_snapshot.caps[idx]; + if (new_caps[incomp_cap]) { + error_setg(errp, + "Background-snapshot is not compatible with %s", + MigrationCapability_str(incomp_cap)); + return false; + } + } + } + +#ifdef CONFIG_LINUX + if (new_caps[MIGRATION_CAPABILITY_ZERO_COPY_SEND] && + (!new_caps[MIGRATION_CAPABILITY_MULTIFD] || + new_caps[MIGRATION_CAPABILITY_COMPRESS] || + new_caps[MIGRATION_CAPABILITY_XBZRLE] || + migrate_multifd_compression() || + migrate_tls())) { + error_setg(errp, + "Zero copy only available for non-compressed non-TLS multifd migration"); + return false; + } +#else + if (new_caps[MIGRATION_CAPABILITY_ZERO_COPY_SEND]) { + error_setg(errp, + "Zero copy currently only available on Linux"); + return false; + } +#endif + + if (new_caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT]) { + if (!new_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) { + error_setg(errp, "Postcopy preempt requires postcopy-ram"); + return false; + } + + /* + * Preempt mode requires urgent pages to be sent in separate + * channel, OTOH compression logic will disorder all pages into + * different compression channels, which is not compatible with the + * preempt assumptions on channel assignments. + */ + if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { + error_setg(errp, "Postcopy preempt not compatible with compress"); + return false; + } + + if (migrate_incoming_started()) { + error_setg(errp, + "Postcopy preempt must be set before incoming starts"); + return false; + } + } + + if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) { + if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { + error_setg(errp, "Multifd is not compatible with compress"); + return false; + } + if (migrate_incoming_started()) { + error_setg(errp, "Multifd must be set before incoming starts"); + return false; + } + } + + if (new_caps[MIGRATION_CAPABILITY_SWITCHOVER_ACK]) { + if (!new_caps[MIGRATION_CAPABILITY_RETURN_PATH]) { + error_setg(errp, "Capability 'switchover-ack' requires capability " + "'return-path'"); + return false; + } + } + if (new_caps[MIGRATION_CAPABILITY_DIRTY_LIMIT]) { + if (new_caps[MIGRATION_CAPABILITY_AUTO_CONVERGE]) { + error_setg(errp, "dirty-limit conflicts with auto-converge" + " either of then available currently"); + return false; + } + + if (!kvm_enabled() || !kvm_dirty_ring_enabled()) { + error_setg(errp, "dirty-limit requires KVM with accelerator" + " property 'dirty-ring-size' set"); + return false; + } + } + + if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) { + if (new_caps[MIGRATION_CAPABILITY_XBZRLE]) { + error_setg(errp, "Multifd is not compatible with xbzrle"); + return false; + } + } + + if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { + if (new_caps[MIGRATION_CAPABILITY_XBZRLE]) { + error_setg(errp, "Compression is not compatible with xbzrle"); + return false; + } + } + + if (new_caps[MIGRATION_CAPABILITY_MAPPED_RAM]) { + if (new_caps[MIGRATION_CAPABILITY_XBZRLE]) { + error_setg(errp, + "Mapped-ram migration is incompatible with xbzrle"); + return false; + } + + if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { + error_setg(errp, + "Mapped-ram migration is incompatible with compression"); + return false; + } + + if (new_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) { + error_setg(errp, + "Mapped-ram migration is incompatible with postcopy"); + return false; + } + } + + return true; +} + +bool migrate_cap_set(int cap, bool value, Error **errp) +{ + MigrationState *s = migrate_get_current(); + bool new_caps[MIGRATION_CAPABILITY__MAX]; + + if (migration_is_running()) { + error_setg(errp, QERR_MIGRATION_ACTIVE); + return false; + } + + memcpy(new_caps, s->capabilities, sizeof(new_caps)); + new_caps[cap] = value; + + if (!migrate_caps_check(s->capabilities, new_caps, errp)) { + return false; + } + s->capabilities[cap] = value; + return true; +} + +MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) +{ + MigrationCapabilityStatusList *head = NULL, **tail = &head; + MigrationCapabilityStatus *caps; + MigrationState *s = migrate_get_current(); + int i; + + for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { +#ifndef CONFIG_LIVE_BLOCK_MIGRATION + if (i == MIGRATION_CAPABILITY_BLOCK) { + continue; + } +#endif + caps = g_malloc0(sizeof(*caps)); + caps->capability = i; + caps->state = s->capabilities[i]; + QAPI_LIST_APPEND(tail, caps); + } + + return head; +} + +void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, + Error **errp) +{ + MigrationState *s = migrate_get_current(); + MigrationCapabilityStatusList *cap; + bool new_caps[MIGRATION_CAPABILITY__MAX]; + + if (migration_is_running() || migration_in_colo_state()) { + error_setg(errp, QERR_MIGRATION_ACTIVE); + return; + } + + memcpy(new_caps, s->capabilities, sizeof(new_caps)); + for (cap = params; cap; cap = cap->next) { + new_caps[cap->value->capability] = cap->value->state; + } + + if (!migrate_caps_check(s->capabilities, new_caps, errp)) { + return; + } + + for (cap = params; cap; cap = cap->next) { + s->capabilities[cap->value->capability] = cap->value->state; + } +} + +/* parameters */ + +const BitmapMigrationNodeAliasList *migrate_block_bitmap_mapping(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.block_bitmap_mapping; +} + +bool migrate_has_block_bitmap_mapping(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.has_block_bitmap_mapping; +} + +bool migrate_block_incremental(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.block_incremental; +} + +uint32_t migrate_checkpoint_delay(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.x_checkpoint_delay; +} + +int migrate_compress_level(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.compress_level; +} + +int migrate_compress_threads(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.compress_threads; +} + +int migrate_compress_wait_thread(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.compress_wait_thread; +} + +uint8_t migrate_cpu_throttle_increment(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.cpu_throttle_increment; +} + +uint8_t migrate_cpu_throttle_initial(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.cpu_throttle_initial; +} + +bool migrate_cpu_throttle_tailslow(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.cpu_throttle_tailslow; +} + +int migrate_decompress_threads(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.decompress_threads; +} + +uint64_t migrate_downtime_limit(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.downtime_limit; +} + +uint8_t migrate_max_cpu_throttle(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.max_cpu_throttle; +} + +uint64_t migrate_max_bandwidth(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.max_bandwidth; +} + +uint64_t migrate_avail_switchover_bandwidth(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.avail_switchover_bandwidth; +} + +uint64_t migrate_max_postcopy_bandwidth(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.max_postcopy_bandwidth; +} + +MigMode migrate_mode(void) +{ + MigrationState *s = migrate_get_current(); + MigMode mode = s->parameters.mode; + + assert(mode >= 0 && mode < MIG_MODE__MAX); + return mode; +} + +int migrate_multifd_channels(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.multifd_channels; +} + +MultiFDCompression migrate_multifd_compression(void) +{ + MigrationState *s = migrate_get_current(); + + assert(s->parameters.multifd_compression < MULTIFD_COMPRESSION__MAX); + return s->parameters.multifd_compression; +} + +int migrate_multifd_zlib_level(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.multifd_zlib_level; +} + +int migrate_multifd_zstd_level(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.multifd_zstd_level; +} + +uint8_t migrate_throttle_trigger_threshold(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.throttle_trigger_threshold; +} + +const char *migrate_tls_authz(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.tls_authz; +} + +const char *migrate_tls_creds(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.tls_creds; +} + +const char *migrate_tls_hostname(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.tls_hostname; +} + +uint64_t migrate_vcpu_dirty_limit_period(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.x_vcpu_dirty_limit_period; +} + +uint64_t migrate_xbzrle_cache_size(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.xbzrle_cache_size; +} + +ZeroPageDetection migrate_zero_page_detection(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.zero_page_detection; +} + +/* parameter setters */ + +void migrate_set_block_incremental(bool value) +{ + MigrationState *s = migrate_get_current(); + + s->parameters.block_incremental = value; +} + +/* parameters helpers */ + +void block_cleanup_parameters(void) +{ + MigrationState *s = migrate_get_current(); + + if (s->must_remove_block_options) { + /* setting to false can never fail */ + migrate_cap_set(MIGRATION_CAPABILITY_BLOCK, false, &error_abort); + migrate_set_block_incremental(false); + s->must_remove_block_options = false; + } +} + +AnnounceParameters *migrate_announce_params(void) +{ + static AnnounceParameters ap; + + MigrationState *s = migrate_get_current(); + + ap.initial = s->parameters.announce_initial; + ap.max = s->parameters.announce_max; + ap.rounds = s->parameters.announce_rounds; + ap.step = s->parameters.announce_step; + + return ≈ +} + +MigrationParameters *qmp_query_migrate_parameters(Error **errp) +{ + MigrationParameters *params; + MigrationState *s = migrate_get_current(); + + /* TODO use QAPI_CLONE() instead of duplicating it inline */ + params = g_malloc0(sizeof(*params)); + params->has_compress_level = true; + params->compress_level = s->parameters.compress_level; + params->has_compress_threads = true; + params->compress_threads = s->parameters.compress_threads; + params->has_compress_wait_thread = true; + 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; + params->cpu_throttle_increment = s->parameters.cpu_throttle_increment; + params->has_cpu_throttle_tailslow = true; + params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow; + params->tls_creds = g_strdup(s->parameters.tls_creds); + params->tls_hostname = g_strdup(s->parameters.tls_hostname); + 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_avail_switchover_bandwidth = true; + params->avail_switchover_bandwidth = s->parameters.avail_switchover_bandwidth; + params->has_downtime_limit = true; + params->downtime_limit = s->parameters.downtime_limit; + params->has_x_checkpoint_delay = true; + params->x_checkpoint_delay = s->parameters.x_checkpoint_delay; + params->has_block_incremental = true; + 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; + params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth; + params->has_max_cpu_throttle = true; + params->max_cpu_throttle = s->parameters.max_cpu_throttle; + params->has_announce_initial = true; + params->announce_initial = s->parameters.announce_initial; + params->has_announce_max = true; + params->announce_max = s->parameters.announce_max; + params->has_announce_rounds = true; + params->announce_rounds = s->parameters.announce_rounds; + params->has_announce_step = true; + params->announce_step = s->parameters.announce_step; + + if (s->parameters.has_block_bitmap_mapping) { + params->has_block_bitmap_mapping = true; + params->block_bitmap_mapping = + QAPI_CLONE(BitmapMigrationNodeAliasList, + s->parameters.block_bitmap_mapping); + } + + params->has_x_vcpu_dirty_limit_period = true; + params->x_vcpu_dirty_limit_period = s->parameters.x_vcpu_dirty_limit_period; + params->has_vcpu_dirty_limit = true; + params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit; + params->has_mode = true; + params->mode = s->parameters.mode; + params->has_zero_page_detection = true; + params->zero_page_detection = s->parameters.zero_page_detection; + + return params; +} + +void migrate_params_init(MigrationParameters *params) +{ + params->tls_hostname = g_strdup(""); + params->tls_creds = g_strdup(""); + + /* Set has_* up only for parameter checks */ + params->has_compress_level = true; + params->has_compress_threads = true; + params->has_compress_wait_thread = 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_cpu_throttle_tailslow = true; + params->has_max_bandwidth = true; + params->has_downtime_limit = true; + 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; + params->has_announce_initial = true; + params->has_announce_max = true; + params->has_announce_rounds = true; + params->has_announce_step = true; + params->has_x_vcpu_dirty_limit_period = true; + params->has_vcpu_dirty_limit = true; + params->has_mode = true; + params->has_zero_page_detection = true; +} + +/* + * Check whether the parameters are valid. Error will be put into errp + * (if provided). Return true if valid, otherwise false. + */ +bool migrate_params_check(MigrationParameters *params, Error **errp) +{ + ERRP_GUARD(); + + if (params->has_compress_level && + (params->compress_level > 9)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level", + "a value between 0 and 9"); + return false; + } + + if (params->has_compress_threads && (params->compress_threads < 1)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "compress_threads", + "a value between 1 and 255"); + return false; + } + + if (params->has_decompress_threads && (params->decompress_threads < 1)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "decompress_threads", + "a value between 1 and 255"); + 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)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "cpu_throttle_initial", + "an integer in the range of 1 to 99"); + return false; + } + + if (params->has_cpu_throttle_increment && + (params->cpu_throttle_increment < 1 || + params->cpu_throttle_increment > 99)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "cpu_throttle_increment", + "an integer in the range of 1 to 99"); + return false; + } + + if (params->has_max_bandwidth && (params->max_bandwidth > SIZE_MAX)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "max_bandwidth", + "an integer in the range of 0 to "stringify(SIZE_MAX) + " bytes/second"); + return false; + } + + if (params->has_avail_switchover_bandwidth && + (params->avail_switchover_bandwidth > SIZE_MAX)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "avail_switchover_bandwidth", + "an integer in the range of 0 to "stringify(SIZE_MAX) + " bytes/second"); + return false; + } + + if (params->has_downtime_limit && + (params->downtime_limit > MAX_MIGRATE_DOWNTIME)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "downtime_limit", + "an integer in the range of 0 to " + stringify(MAX_MIGRATE_DOWNTIME)" ms"); + return false; + } + + /* x_checkpoint_delay is now always positive */ + + if (params->has_multifd_channels && (params->multifd_channels < 1)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "multifd_channels", + "a value between 1 and 255"); + return false; + } + + if (params->has_multifd_zlib_level && + (params->multifd_zlib_level > 9)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_zlib_level", + "a value between 0 and 9"); + return false; + } + + if (params->has_multifd_zstd_level && + (params->multifd_zstd_level > 20)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_zstd_level", + "a value between 0 and 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", + "a power of two no less than the target page size"); + return false; + } + + if (params->has_max_cpu_throttle && + (params->max_cpu_throttle < params->cpu_throttle_initial || + params->max_cpu_throttle > 99)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "max_cpu_throttle", + "an integer in the range of cpu_throttle_initial to 99"); + return false; + } + + if (params->has_announce_initial && + params->announce_initial > 100000) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "announce_initial", + "a value between 0 and 100000"); + return false; + } + if (params->has_announce_max && + params->announce_max > 100000) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "announce_max", + "a value between 0 and 100000"); + return false; + } + if (params->has_announce_rounds && + params->announce_rounds > 1000) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "announce_rounds", + "a value between 0 and 1000"); + return false; + } + if (params->has_announce_step && + (params->announce_step < 1 || + params->announce_step > 10000)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "announce_step", + "a value between 0 and 10000"); + return false; + } + + if (params->has_block_bitmap_mapping && + !check_dirty_bitmap_mig_alias_map(params->block_bitmap_mapping, errp)) { + error_prepend(errp, "Invalid mapping given for block-bitmap-mapping: "); + return false; + } + +#ifdef CONFIG_LINUX + if (migrate_zero_copy_send() && + ((params->has_multifd_compression && params->multifd_compression) || + (params->tls_creds && *params->tls_creds))) { + error_setg(errp, + "Zero copy only available for non-compressed non-TLS multifd migration"); + return false; + } +#endif + + if (migrate_mapped_ram() && + (migrate_multifd_compression() || migrate_tls())) { + error_setg(errp, + "Mapped-ram only available for non-compressed non-TLS multifd migration"); + return false; + } + + if (params->has_x_vcpu_dirty_limit_period && + (params->x_vcpu_dirty_limit_period < 1 || + params->x_vcpu_dirty_limit_period > 1000)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "x-vcpu-dirty-limit-period", + "a value between 1 and 1000"); + return false; + } + + if (params->has_vcpu_dirty_limit && + (params->vcpu_dirty_limit < 1)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "vcpu_dirty_limit", + "is invalid, it must greater then 1 MB/s"); + return false; + } + + return true; +} + +static void migrate_params_test_apply(MigrateSetParameters *params, + MigrationParameters *dest) +{ + *dest = migrate_get_current()->parameters; + + /* TODO use QAPI_CLONE() instead of duplicating it inline */ + + if (params->has_compress_level) { + dest->compress_level = params->compress_level; + } + + if (params->has_compress_threads) { + dest->compress_threads = params->compress_threads; + } + + if (params->has_compress_wait_thread) { + dest->compress_wait_thread = params->compress_wait_thread; + } + + if (params->has_decompress_threads) { + 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; + } + + if (params->has_cpu_throttle_increment) { + dest->cpu_throttle_increment = params->cpu_throttle_increment; + } + + if (params->has_cpu_throttle_tailslow) { + dest->cpu_throttle_tailslow = params->cpu_throttle_tailslow; + } + + if (params->tls_creds) { + assert(params->tls_creds->type == QTYPE_QSTRING); + dest->tls_creds = params->tls_creds->u.s; + } + + if (params->tls_hostname) { + assert(params->tls_hostname->type == QTYPE_QSTRING); + dest->tls_hostname = params->tls_hostname->u.s; + } + + if (params->has_max_bandwidth) { + dest->max_bandwidth = params->max_bandwidth; + } + + if (params->has_avail_switchover_bandwidth) { + dest->avail_switchover_bandwidth = params->avail_switchover_bandwidth; + } + + if (params->has_downtime_limit) { + dest->downtime_limit = params->downtime_limit; + } + + if (params->has_x_checkpoint_delay) { + dest->x_checkpoint_delay = params->x_checkpoint_delay; + } + + if (params->has_block_incremental) { + dest->block_incremental = params->block_incremental; + } + 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_multifd_zlib_level) { + dest->multifd_zlib_level = params->multifd_zlib_level; + } + if (params->has_multifd_zstd_level) { + dest->multifd_zstd_level = params->multifd_zstd_level; + } + if (params->has_xbzrle_cache_size) { + dest->xbzrle_cache_size = params->xbzrle_cache_size; + } + if (params->has_max_postcopy_bandwidth) { + dest->max_postcopy_bandwidth = params->max_postcopy_bandwidth; + } + if (params->has_max_cpu_throttle) { + dest->max_cpu_throttle = params->max_cpu_throttle; + } + if (params->has_announce_initial) { + dest->announce_initial = params->announce_initial; + } + if (params->has_announce_max) { + dest->announce_max = params->announce_max; + } + if (params->has_announce_rounds) { + dest->announce_rounds = params->announce_rounds; + } + if (params->has_announce_step) { + dest->announce_step = params->announce_step; + } + + if (params->has_block_bitmap_mapping) { + dest->has_block_bitmap_mapping = true; + dest->block_bitmap_mapping = params->block_bitmap_mapping; + } + + if (params->has_x_vcpu_dirty_limit_period) { + dest->x_vcpu_dirty_limit_period = + params->x_vcpu_dirty_limit_period; + } + if (params->has_vcpu_dirty_limit) { + dest->vcpu_dirty_limit = params->vcpu_dirty_limit; + } + + if (params->has_mode) { + dest->mode = params->mode; + } + + if (params->has_zero_page_detection) { + dest->zero_page_detection = params->zero_page_detection; + } +} + +static void migrate_params_apply(MigrateSetParameters *params, Error **errp) +{ + MigrationState *s = migrate_get_current(); + + /* TODO use QAPI_CLONE() instead of duplicating it inline */ + + if (params->has_compress_level) { + warn_report("old compression is deprecated;" + " use multifd compression methods instead"); + s->parameters.compress_level = params->compress_level; + } + + if (params->has_compress_threads) { + warn_report("old compression is deprecated;" + " use multifd compression methods instead"); + s->parameters.compress_threads = params->compress_threads; + } + + if (params->has_compress_wait_thread) { + warn_report("old compression is deprecated;" + " use multifd compression methods instead"); + s->parameters.compress_wait_thread = params->compress_wait_thread; + } + + if (params->has_decompress_threads) { + warn_report("old compression is deprecated;" + " use multifd compression methods instead"); + 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; + } + + if (params->has_cpu_throttle_increment) { + s->parameters.cpu_throttle_increment = params->cpu_throttle_increment; + } + + if (params->has_cpu_throttle_tailslow) { + s->parameters.cpu_throttle_tailslow = params->cpu_throttle_tailslow; + } + + if (params->tls_creds) { + g_free(s->parameters.tls_creds); + assert(params->tls_creds->type == QTYPE_QSTRING); + s->parameters.tls_creds = g_strdup(params->tls_creds->u.s); + } + + if (params->tls_hostname) { + g_free(s->parameters.tls_hostname); + assert(params->tls_hostname->type == QTYPE_QSTRING); + s->parameters.tls_hostname = g_strdup(params->tls_hostname->u.s); + } + + if (params->tls_authz) { + g_free(s->parameters.tls_authz); + assert(params->tls_authz->type == QTYPE_QSTRING); + s->parameters.tls_authz = g_strdup(params->tls_authz->u.s); + } + + if (params->has_max_bandwidth) { + s->parameters.max_bandwidth = params->max_bandwidth; + if (s->to_dst_file && !migration_in_postcopy()) { + migration_rate_set(s->parameters.max_bandwidth); + } + } + + if (params->has_avail_switchover_bandwidth) { + s->parameters.avail_switchover_bandwidth = params->avail_switchover_bandwidth; + } + + if (params->has_downtime_limit) { + s->parameters.downtime_limit = params->downtime_limit; + } + + if (params->has_x_checkpoint_delay) { + s->parameters.x_checkpoint_delay = params->x_checkpoint_delay; + colo_checkpoint_delay_set(); + } + + if (params->has_block_incremental) { + warn_report("block migration is deprecated;" + " use blockdev-mirror with NBD instead"); + s->parameters.block_incremental = params->block_incremental; + } + 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_multifd_zlib_level) { + s->parameters.multifd_zlib_level = params->multifd_zlib_level; + } + if (params->has_multifd_zstd_level) { + s->parameters.multifd_zstd_level = params->multifd_zstd_level; + } + if (params->has_xbzrle_cache_size) { + s->parameters.xbzrle_cache_size = params->xbzrle_cache_size; + xbzrle_cache_resize(params->xbzrle_cache_size, errp); + } + if (params->has_max_postcopy_bandwidth) { + s->parameters.max_postcopy_bandwidth = params->max_postcopy_bandwidth; + if (s->to_dst_file && migration_in_postcopy()) { + migration_rate_set(s->parameters.max_postcopy_bandwidth); + } + } + if (params->has_max_cpu_throttle) { + s->parameters.max_cpu_throttle = params->max_cpu_throttle; + } + if (params->has_announce_initial) { + s->parameters.announce_initial = params->announce_initial; + } + if (params->has_announce_max) { + s->parameters.announce_max = params->announce_max; + } + if (params->has_announce_rounds) { + s->parameters.announce_rounds = params->announce_rounds; + } + if (params->has_announce_step) { + s->parameters.announce_step = params->announce_step; + } + + if (params->has_block_bitmap_mapping) { + qapi_free_BitmapMigrationNodeAliasList( + s->parameters.block_bitmap_mapping); + + s->parameters.has_block_bitmap_mapping = true; + s->parameters.block_bitmap_mapping = + QAPI_CLONE(BitmapMigrationNodeAliasList, + params->block_bitmap_mapping); + } + + if (params->has_x_vcpu_dirty_limit_period) { + s->parameters.x_vcpu_dirty_limit_period = + params->x_vcpu_dirty_limit_period; + } + if (params->has_vcpu_dirty_limit) { + s->parameters.vcpu_dirty_limit = params->vcpu_dirty_limit; + } + + if (params->has_mode) { + s->parameters.mode = params->mode; + } + + if (params->has_zero_page_detection) { + s->parameters.zero_page_detection = params->zero_page_detection; + } +} + +void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) +{ + MigrationParameters tmp; + + /* TODO Rewrite "" to null instead for all three tls_* parameters */ + if (params->tls_creds + && params->tls_creds->type == QTYPE_QNULL) { + qobject_unref(params->tls_creds->u.n); + params->tls_creds->type = QTYPE_QSTRING; + params->tls_creds->u.s = strdup(""); + } + if (params->tls_hostname + && params->tls_hostname->type == QTYPE_QNULL) { + qobject_unref(params->tls_hostname->u.n); + params->tls_hostname->type = QTYPE_QSTRING; + params->tls_hostname->u.s = strdup(""); + } + if (params->tls_authz + && params->tls_authz->type == QTYPE_QNULL) { + qobject_unref(params->tls_authz->u.n); + params->tls_authz->type = QTYPE_QSTRING; + params->tls_authz->u.s = strdup(""); + } + + migrate_params_test_apply(params, &tmp); + + if (!migrate_params_check(&tmp, errp)) { + /* Invalid parameter */ + return; + } + + migrate_params_apply(params, errp); +} diff --git a/migration/options.h b/migration/options.h new file mode 100644 index 0000000000..ab8199e207 --- /dev/null +++ b/migration/options.h @@ -0,0 +1,105 @@ +/* + * QEMU migration capabilities + * + * Copyright (c) 2012-2023 Red Hat Inc + * + * Authors: + * Orit Wasserman + * 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_OPTIONS_H +#define QEMU_MIGRATION_OPTIONS_H + +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "migration/client-options.h" + +/* migration properties */ + +extern Property migration_properties[]; + +/* capabilities */ + +bool migrate_auto_converge(void); +bool migrate_block(void); +bool migrate_colo(void); +bool migrate_compress(void); +bool migrate_dirty_bitmaps(void); +bool migrate_events(void); +bool migrate_mapped_ram(void); +bool migrate_ignore_shared(void); +bool migrate_late_block_activate(void); +bool migrate_multifd(void); +bool migrate_pause_before_switchover(void); +bool migrate_postcopy_blocktime(void); +bool migrate_postcopy_preempt(void); +bool migrate_rdma_pin_all(void); +bool migrate_release_ram(void); +bool migrate_return_path(void); +bool migrate_validate_uuid(void); +bool migrate_xbzrle(void); +bool migrate_zero_blocks(void); +bool migrate_zero_copy_send(void); + +/* + * pseudo capabilities + * + * These are functions that are used in a similar way to capabilities + * check, but they are not a capability. + */ + +bool migrate_multifd_flush_after_each_section(void); +bool migrate_postcopy(void); +bool migrate_rdma(void); +bool migrate_tls(void); + +/* capabilities helpers */ + +bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp); +bool migrate_cap_set(int cap, bool value, Error **errp); + +/* parameters */ + +const BitmapMigrationNodeAliasList *migrate_block_bitmap_mapping(void); +bool migrate_has_block_bitmap_mapping(void); + +bool migrate_block_incremental(void); +uint32_t migrate_checkpoint_delay(void); +int migrate_compress_level(void); +int migrate_compress_threads(void); +int migrate_compress_wait_thread(void); +uint8_t migrate_cpu_throttle_increment(void); +uint8_t migrate_cpu_throttle_initial(void); +bool migrate_cpu_throttle_tailslow(void); +int migrate_decompress_threads(void); +uint64_t migrate_downtime_limit(void); +uint8_t migrate_max_cpu_throttle(void); +uint64_t migrate_max_bandwidth(void); +uint64_t migrate_avail_switchover_bandwidth(void); +uint64_t migrate_max_postcopy_bandwidth(void); +int migrate_multifd_channels(void); +MultiFDCompression migrate_multifd_compression(void); +int migrate_multifd_zlib_level(void); +int migrate_multifd_zstd_level(void); +uint8_t migrate_throttle_trigger_threshold(void); +const char *migrate_tls_authz(void); +const char *migrate_tls_creds(void); +const char *migrate_tls_hostname(void); +uint64_t migrate_xbzrle_cache_size(void); +ZeroPageDetection migrate_zero_page_detection(void); + +/* parameters setters */ + +void migrate_set_block_incremental(bool value); + +/* parameters helpers */ + +bool migrate_params_check(MigrationParameters *params, Error **errp); +void migrate_params_init(MigrationParameters *params); +void block_cleanup_parameters(void); + +#endif diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index a66dd536d9..eccff499cb 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -17,7 +17,6 @@ */ #include "qemu/osdep.h" -#include "qemu/rcu.h" #include "qemu/madvise.h" #include "exec/target_page.h" #include "migration.h" @@ -33,6 +32,12 @@ #include "trace.h" #include "hw/boards.h" #include "exec/ramblock.h" +#include "socket.h" +#include "yank_functions.h" +#include "tls.h" +#include "qemu/userfaultfd.h" +#include "qemu/mmap-alloc.h" +#include "options.h" /* Arbitrary limit on size of each discard command, * keeps them around ~200 bytes @@ -72,10 +77,9 @@ int postcopy_notify(enum PostcopyNotifyReason reason, Error **errp) { struct PostcopyNotifyData pnd; pnd.reason = reason; - pnd.errp = errp; return notifier_with_return_list_notify(&postcopy_notifier_list, - &pnd); + &pnd, errp); } /* @@ -97,11 +101,9 @@ void postcopy_thread_create(MigrationIncomingState *mis, * are target OS specific. */ #if defined(__linux__) - #include #include #include -#include /* for __u64 */ #endif #if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) @@ -222,11 +224,9 @@ static bool receive_ufd_features(uint64_t *features) int ufd; bool ret = true; - /* if we are here __NR_userfaultfd should exists */ - ufd = syscall(__NR_userfaultfd, O_CLOEXEC); + ufd = uffd_open(O_CLOEXEC); if (ufd == -1) { - error_report("%s: syscall __NR_userfaultfd failed: %s", __func__, - strerror(errno)); + error_report("%s: uffd_open() failed: %s", __func__, strerror(errno)); return false; } @@ -269,8 +269,8 @@ static bool request_ufd_features(int ufd, uint64_t features) return false; } - ioctl_mask = (__u64)1 << _UFFDIO_REGISTER | - (__u64)1 << _UFFDIO_UNREGISTER; + ioctl_mask = 1ULL << _UFFDIO_REGISTER | + 1ULL << _UFFDIO_UNREGISTER; if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) { error_report("Missing userfault features: %" PRIx64, (uint64_t)(~api_struct.ioctls & ioctl_mask)); @@ -280,8 +280,10 @@ static bool request_ufd_features(int ufd, uint64_t features) return true; } -static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis) +static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis, + Error **errp) { + ERRP_GUARD(); uint64_t asked_features = 0; static uint64_t supported_features; @@ -292,7 +294,7 @@ static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis) */ if (!supported_features) { if (!receive_ufd_features(&supported_features)) { - error_report("%s failed", __func__); + error_setg(errp, "Userfault feature detection failed"); return false; } } @@ -314,8 +316,7 @@ static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis) * userfault file descriptor */ if (!request_ufd_features(ufd, asked_features)) { - error_report("%s failed: features %" PRIu64, __func__, - asked_features); + error_setg(errp, "Failed features %" PRIu64, asked_features); return false; } @@ -326,7 +327,8 @@ static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis) have_hp = supported_features & UFFD_FEATURE_MISSING_HUGETLBFS; #endif if (!have_hp) { - error_report("Userfault on this host does not support huge pages"); + error_setg(errp, + "Userfault on this host does not support huge pages"); return false; } } @@ -335,18 +337,30 @@ static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis) /* Callback from postcopy_ram_supported_by_host block iterator. */ -static int test_ramblock_postcopiable(RAMBlock *rb, void *opaque) +static int test_ramblock_postcopiable(RAMBlock *rb, Error **errp) { const char *block_name = qemu_ram_get_idstr(rb); ram_addr_t length = qemu_ram_get_used_length(rb); size_t pagesize = qemu_ram_pagesize(rb); + QemuFsType fs; if (length % pagesize) { - error_report("Postcopy requires RAM blocks to be a page size multiple," - " block %s is 0x" RAM_ADDR_FMT " bytes with a " - "page size of 0x%zx", block_name, length, pagesize); + error_setg(errp, + "Postcopy requires RAM blocks to be a page size multiple," + " block %s is 0x" RAM_ADDR_FMT " bytes with a " + "page size of 0x%zx", block_name, length, pagesize); return 1; } + + if (rb->fd >= 0) { + fs = qemu_fd_getfs(rb->fd); + if (fs != QEMU_FS_TYPE_TMPFS && fs != QEMU_FS_TYPE_HUGETLBFS) { + error_setg(errp, + "Host backend files need to be TMPFS or HUGETLBFS only"); + return 1; + } + } + return 0; } @@ -355,8 +369,9 @@ static int test_ramblock_postcopiable(RAMBlock *rb, void *opaque) * normally fine since if the postcopy succeeds it gets turned back on at the * end. */ -bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) +bool postcopy_ram_supported_by_host(MigrationIncomingState *mis, Error **errp) { + ERRP_GUARD(); long pagesize = qemu_real_host_page_size(); int ufd = -1; bool ret = false; /* Error unless we change it */ @@ -364,34 +379,46 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) struct uffdio_register reg_struct; struct uffdio_range range_struct; uint64_t feature_mask; - Error *local_err = NULL; + RAMBlock *block; if (qemu_target_page_size() > pagesize) { - error_report("Target page size bigger than host page size"); + error_setg(errp, "Target page size bigger than host page size"); goto out; } - ufd = syscall(__NR_userfaultfd, O_CLOEXEC); + ufd = uffd_open(O_CLOEXEC); if (ufd == -1) { - error_report("%s: userfaultfd not available: %s", __func__, - strerror(errno)); + error_setg(errp, "Userfaultfd not available: %s", strerror(errno)); goto out; } /* Give devices a chance to object */ - if (postcopy_notify(POSTCOPY_NOTIFY_PROBE, &local_err)) { - error_report_err(local_err); + if (postcopy_notify(POSTCOPY_NOTIFY_PROBE, errp)) { goto out; } /* Version and features check */ - if (!ufd_check_and_apply(ufd, mis)) { + if (!ufd_check_and_apply(ufd, mis, errp)) { goto out; } - /* We don't support postcopy with shared RAM yet */ - if (foreach_not_ignored_block(test_ramblock_postcopiable, NULL)) { - goto out; + /* + * We don't support postcopy with some type of ramblocks. + * + * NOTE: we explicitly ignored migrate_ram_is_ignored() instead we checked + * all possible ramblocks. This is because this function can be called + * when creating the migration object, during the phase RAM_MIGRATABLE + * is not even properly set for all the ramblocks. + * + * A side effect of this is we'll also check against RAM_SHARED + * ramblocks even if migrate_ignore_shared() is set (in which case + * we'll never migrate RAM_SHARED at all), but normally this shouldn't + * affect in reality, or we can revisit. + */ + RAMBLOCK_FOREACH(block) { + if (test_ramblock_postcopiable(block, errp)) { + goto out; + } } /* @@ -399,7 +426,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) * it was enabled. */ if (munlockall()) { - error_report("%s: munlockall: %s", __func__, strerror(errno)); + error_setg(errp, "munlockall() failed: %s", strerror(errno)); goto out; } @@ -411,8 +438,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) testarea = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (testarea == MAP_FAILED) { - error_report("%s: Failed to map test area: %s", __func__, - strerror(errno)); + error_setg(errp, "Failed to map test area: %s", strerror(errno)); goto out; } g_assert(QEMU_PTR_IS_ALIGNED(testarea, pagesize)); @@ -422,23 +448,23 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) reg_struct.mode = UFFDIO_REGISTER_MODE_MISSING; if (ioctl(ufd, UFFDIO_REGISTER, ®_struct)) { - error_report("%s userfault register: %s", __func__, strerror(errno)); + error_setg(errp, "UFFDIO_REGISTER failed: %s", strerror(errno)); goto out; } range_struct.start = (uintptr_t)testarea; range_struct.len = pagesize; if (ioctl(ufd, UFFDIO_UNREGISTER, &range_struct)) { - error_report("%s userfault unregister: %s", __func__, strerror(errno)); + error_setg(errp, "UFFDIO_UNREGISTER failed: %s", strerror(errno)); goto out; } - feature_mask = (__u64)1 << _UFFDIO_WAKE | - (__u64)1 << _UFFDIO_COPY | - (__u64)1 << _UFFDIO_ZEROPAGE; + feature_mask = 1ULL << _UFFDIO_WAKE | + 1ULL << _UFFDIO_COPY | + 1ULL << _UFFDIO_ZEROPAGE; if ((reg_struct.ioctls & feature_mask) != feature_mask) { - error_report("Missing userfault map features: %" PRIx64, - (uint64_t)(~reg_struct.ioctls & feature_mask)); + error_setg(errp, "Missing userfault map features: %" PRIx64, + (uint64_t)(~reg_struct.ioctls & feature_mask)); goto out; } @@ -567,6 +593,40 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) { trace_postcopy_ram_incoming_cleanup_entry(); + if (mis->preempt_thread_status == PREEMPT_THREAD_CREATED) { + /* Notify the fast load thread to quit */ + mis->preempt_thread_status = PREEMPT_THREAD_QUIT; + /* + * Update preempt_thread_status before reading count. Note: mutex + * lock only provide ACQUIRE semantic, and it doesn't stops this + * write to be reordered after reading the count. + */ + smp_mb(); + /* + * It's possible that the preempt thread is still handling the last + * pages to arrive which were requested by guest page faults. + * Making sure nothing is left behind by waiting on the condvar if + * that unlikely case happened. + */ + WITH_QEMU_LOCK_GUARD(&mis->page_request_mutex) { + if (qatomic_read(&mis->page_requested_count)) { + /* + * It is guaranteed to receive a signal later, because the + * count>0 now, so it's destined to be decreased to zero + * very soon by the preempt thread. + */ + qemu_cond_wait(&mis->page_request_cond, + &mis->page_request_mutex); + } + } + /* Notify the fast load thread to quit */ + if (mis->postcopy_qemufile_dst) { + qemu_file_shutdown(mis->postcopy_qemufile_dst); + } + qemu_thread_join(&mis->postcopy_prio_thread); + mis->preempt_thread_status = PREEMPT_THREAD_NONE; + } + if (mis->have_fault_thread) { Error *local_err = NULL; @@ -670,11 +730,11 @@ static int ram_block_enable_notify(RAMBlock *rb, void *opaque) error_report("%s userfault register: %s", __func__, strerror(errno)); return -1; } - if (!(reg_struct.ioctls & ((__u64)1 << _UFFDIO_COPY))) { + if (!(reg_struct.ioctls & (1ULL << _UFFDIO_COPY))) { error_report("%s userfault: Region doesn't support COPY", __func__); return -1; } - if (reg_struct.ioctls & ((__u64)1 << _UFFDIO_ZEROPAGE)) { + if (reg_struct.ioctls & (1ULL << _UFFDIO_ZEROPAGE)) { qemu_ram_set_uf_zeroable(rb); } @@ -1102,8 +1162,13 @@ static int postcopy_temp_pages_setup(MigrationIncomingState *mis) int err, i, channels; void *temp_page; - /* TODO: will be boosted when enable postcopy preemption */ - mis->postcopy_channels = 1; + if (migrate_postcopy_preempt()) { + /* If preemption enabled, need extra channel for urgent requests */ + mis->postcopy_channels = RAM_CHANNEL_MAX; + } else { + /* Both precopy/postcopy on the same channel */ + mis->postcopy_channels = 1; + } channels = mis->postcopy_channels; mis->postcopy_tmp_pages = g_malloc0_n(sizeof(PostcopyTmpPage), channels); @@ -1145,8 +1210,10 @@ static int postcopy_temp_pages_setup(MigrationIncomingState *mis) int postcopy_ram_incoming_setup(MigrationIncomingState *mis) { + Error *local_err = NULL; + /* Open the fd for the kernel to give us userfaults */ - mis->userfault_fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); + mis->userfault_fd = uffd_open(O_CLOEXEC | O_NONBLOCK); if (mis->userfault_fd == -1) { error_report("%s: Failed to open userfault fd: %s", __func__, strerror(errno)); @@ -1157,7 +1224,8 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis) * Although the host check already tested the API, we need to * do the check again as an ABI handshake on the new fd. */ - if (!ufd_check_and_apply(mis->userfault_fd, mis)) { + if (!ufd_check_and_apply(mis->userfault_fd, mis, &local_err)) { + error_report_err(local_err); return -1; } @@ -1170,7 +1238,7 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis) return -1; } - postcopy_thread_create(mis, &mis->fault_thread, "postcopy/fault", + postcopy_thread_create(mis, &mis->fault_thread, "fault-default", postcopy_ram_fault_thread, QEMU_THREAD_JOINABLE); mis->have_fault_thread = true; @@ -1185,6 +1253,16 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis) return -1; } + if (migrate_postcopy_preempt()) { + /* + * This thread needs to be created after the temp pages because + * it'll fetch RAM_CHANNEL_POSTCOPY PostcopyTmpPage immediately. + */ + postcopy_thread_create(mis, &mis->postcopy_prio_thread, "fault-fast", + postcopy_preempt_thread, QEMU_THREAD_JOINABLE); + mis->preempt_thread_status = PREEMPT_THREAD_CREATED; + } + trace_postcopy_ram_enable_notify(); return 0; @@ -1220,8 +1298,20 @@ static int qemu_ufd_copy_ioctl(MigrationIncomingState *mis, void *host_addr, */ if (g_tree_lookup(mis->page_requested, host_addr)) { g_tree_remove(mis->page_requested, host_addr); - mis->page_requested_count--; + int left_pages = qatomic_dec_fetch(&mis->page_requested_count); + trace_postcopy_page_req_del(host_addr, mis->page_requested_count); + /* Order the update of count and read of preempt status */ + smp_mb(); + if (mis->preempt_thread_status == PREEMPT_THREAD_QUIT && + left_pages == 0) { + /* + * This probably means the main thread is waiting for us. + * Notify that we've finished receiving the last requested + * page. + */ + qemu_cond_signal(&mis->page_request_cond); + } } qemu_mutex_unlock(&mis->page_request_mutex); mark_postcopy_blocktime_end((uintptr_t)host_addr); @@ -1307,7 +1397,7 @@ void fill_destination_postcopy_migration_info(MigrationInfo *info) { } -bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) +bool postcopy_ram_supported_by_host(MigrationIncomingState *mis, Error **errp) { error_report("%s: No OS support", __func__); return false; @@ -1474,7 +1564,7 @@ static PostcopyState incoming_postcopy_state; PostcopyState postcopy_state_get(void) { - return qatomic_mb_read(&incoming_postcopy_state); + return qatomic_load_acquire(&incoming_postcopy_state); } /* Set the state and return the old state */ @@ -1514,3 +1604,169 @@ void postcopy_unregister_shared_ufd(struct PostCopyFD *pcfd) } } } + +void postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file) +{ + /* + * The new loading channel has its own threads, so it needs to be + * blocked too. It's by default true, just be explicit. + */ + qemu_file_set_blocking(file, true); + mis->postcopy_qemufile_dst = file; + qemu_sem_post(&mis->postcopy_qemufile_dst_done); + trace_postcopy_preempt_new_channel(); +} + +/* + * Setup the postcopy preempt channel with the IOC. If ERROR is specified, + * setup the error instead. This helper will free the ERROR if specified. + */ +static void +postcopy_preempt_send_channel_done(MigrationState *s, + QIOChannel *ioc, Error *local_err) +{ + if (local_err) { + migrate_set_error(s, local_err); + error_free(local_err); + } else { + migration_ioc_register_yank(ioc); + s->postcopy_qemufile_src = qemu_file_new_output(ioc); + trace_postcopy_preempt_new_channel(); + } + + /* + * Kick the waiter in all cases. The waiter should check upon + * postcopy_qemufile_src to know whether it failed or not. + */ + qemu_sem_post(&s->postcopy_qemufile_src_sem); +} + +static void +postcopy_preempt_tls_handshake(QIOTask *task, gpointer opaque) +{ + g_autoptr(QIOChannel) ioc = QIO_CHANNEL(qio_task_get_source(task)); + MigrationState *s = opaque; + Error *local_err = NULL; + + qio_task_propagate_error(task, &local_err); + postcopy_preempt_send_channel_done(s, ioc, local_err); +} + +static void +postcopy_preempt_send_channel_new(QIOTask *task, gpointer opaque) +{ + g_autoptr(QIOChannel) ioc = QIO_CHANNEL(qio_task_get_source(task)); + MigrationState *s = opaque; + QIOChannelTLS *tioc; + Error *local_err = NULL; + + if (qio_task_propagate_error(task, &local_err)) { + goto out; + } + + if (migrate_channel_requires_tls_upgrade(ioc)) { + tioc = migration_tls_client_create(ioc, s->hostname, &local_err); + if (!tioc) { + goto out; + } + trace_postcopy_preempt_tls_handshake(); + qio_channel_set_name(QIO_CHANNEL(tioc), "migration-tls-preempt"); + qio_channel_tls_handshake(tioc, postcopy_preempt_tls_handshake, + s, NULL, NULL); + /* Setup the channel until TLS handshake finished */ + return; + } + +out: + /* This handles both good and error cases */ + postcopy_preempt_send_channel_done(s, ioc, local_err); +} + +/* + * This function will kick off an async task to establish the preempt + * channel, and wait until the connection setup completed. Returns 0 if + * channel established, -1 for error. + */ +int postcopy_preempt_establish_channel(MigrationState *s) +{ + /* If preempt not enabled, no need to wait */ + if (!migrate_postcopy_preempt()) { + return 0; + } + + /* + * Kick off async task to establish preempt channel. Only do so with + * 8.0+ machines, because 7.1/7.2 require the channel to be created in + * setup phase of migration (even if racy in an unreliable network). + */ + if (!s->preempt_pre_7_2) { + postcopy_preempt_setup(s); + } + + /* + * We need the postcopy preempt channel to be established before + * starting doing anything. + */ + qemu_sem_wait(&s->postcopy_qemufile_src_sem); + + return s->postcopy_qemufile_src ? 0 : -1; +} + +void postcopy_preempt_setup(MigrationState *s) +{ + /* Kick an async task to connect */ + socket_send_channel_create(postcopy_preempt_send_channel_new, s); +} + +static void postcopy_pause_ram_fast_load(MigrationIncomingState *mis) +{ + trace_postcopy_pause_fast_load(); + qemu_mutex_unlock(&mis->postcopy_prio_thread_mutex); + qemu_sem_wait(&mis->postcopy_pause_sem_fast_load); + qemu_mutex_lock(&mis->postcopy_prio_thread_mutex); + trace_postcopy_pause_fast_load_continued(); +} + +static bool preempt_thread_should_run(MigrationIncomingState *mis) +{ + return mis->preempt_thread_status != PREEMPT_THREAD_QUIT; +} + +void *postcopy_preempt_thread(void *opaque) +{ + MigrationIncomingState *mis = opaque; + int ret; + + trace_postcopy_preempt_thread_entry(); + + rcu_register_thread(); + + qemu_sem_post(&mis->thread_sync_sem); + + /* + * The preempt channel is established in asynchronous way. Wait + * for its completion. + */ + qemu_sem_wait(&mis->postcopy_qemufile_dst_done); + + /* Sending RAM_SAVE_FLAG_EOS to terminate this thread */ + qemu_mutex_lock(&mis->postcopy_prio_thread_mutex); + while (preempt_thread_should_run(mis)) { + ret = ram_load_postcopy(mis->postcopy_qemufile_dst, + RAM_CHANNEL_POSTCOPY); + /* If error happened, go into recovery routine */ + if (ret && preempt_thread_should_run(mis)) { + postcopy_pause_ram_fast_load(mis); + } else { + /* We're done */ + break; + } + } + qemu_mutex_unlock(&mis->postcopy_prio_thread_mutex); + + rcu_unregister_thread(); + + trace_postcopy_preempt_thread_exit(); + + return NULL; +} diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h index 07684c0e1d..ecae941211 100644 --- a/migration/postcopy-ram.h +++ b/migration/postcopy-ram.h @@ -14,7 +14,8 @@ #define QEMU_POSTCOPY_RAM_H /* Return true if the host supports everything we need to do postcopy-ram */ -bool postcopy_ram_supported_by_host(MigrationIncomingState *mis); +bool postcopy_ram_supported_by_host(MigrationIncomingState *mis, + Error **errp); /* * Make all of RAM sensitive to accesses to areas that haven't yet been written @@ -127,7 +128,6 @@ enum PostcopyNotifyReason { struct PostcopyNotifyData { enum PostcopyNotifyReason reason; - Error **errp; }; void postcopy_add_notifier(NotifierWithReturn *nn); @@ -183,4 +183,15 @@ int postcopy_wake_shared(struct PostCopyFD *pcfd, uint64_t client_addr, int postcopy_request_shared_page(struct PostCopyFD *pcfd, RAMBlock *rb, uint64_t client_addr, uint64_t offset); +/* Hard-code channels for now for postcopy preemption */ +enum PostcopyChannels { + RAM_CHANNEL_PRECOPY = 0, + RAM_CHANNEL_POSTCOPY = 1, + RAM_CHANNEL_MAX, +}; + +void postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file); +void postcopy_preempt_setup(MigrationState *s); +int postcopy_preempt_establish_channel(MigrationState *s); + #endif diff --git a/migration/qemu-file-channel.c b/migration/qemu-file-channel.c deleted file mode 100644 index bb5a5752df..0000000000 --- a/migration/qemu-file-channel.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * QEMUFile backend for QIOChannel objects - * - * Copyright (c) 2015-2016 Red Hat, Inc - * - * 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-file-channel.h" -#include "qemu-file.h" -#include "io/channel-socket.h" -#include "io/channel-tls.h" -#include "qemu/iov.h" -#include "qemu/yank.h" -#include "yank_functions.h" - - -static ssize_t channel_writev_buffer(void *opaque, - struct iovec *iov, - int iovcnt, - int64_t pos, - Error **errp) -{ - QIOChannel *ioc = QIO_CHANNEL(opaque); - ssize_t done = 0; - struct iovec *local_iov = g_new(struct iovec, iovcnt); - struct iovec *local_iov_head = local_iov; - unsigned int nlocal_iov = iovcnt; - - nlocal_iov = iov_copy(local_iov, nlocal_iov, - iov, iovcnt, - 0, iov_size(iov, iovcnt)); - - while (nlocal_iov > 0) { - ssize_t len; - len = qio_channel_writev(ioc, local_iov, nlocal_iov, errp); - if (len == QIO_CHANNEL_ERR_BLOCK) { - if (qemu_in_coroutine()) { - qio_channel_yield(ioc, G_IO_OUT); - } else { - qio_channel_wait(ioc, G_IO_OUT); - } - continue; - } - if (len < 0) { - done = -EIO; - goto cleanup; - } - - iov_discard_front(&local_iov, &nlocal_iov, len); - done += len; - } - - cleanup: - g_free(local_iov_head); - return done; -} - - -static ssize_t channel_get_buffer(void *opaque, - uint8_t *buf, - int64_t pos, - size_t size, - Error **errp) -{ - QIOChannel *ioc = QIO_CHANNEL(opaque); - ssize_t ret; - - do { - ret = qio_channel_read(ioc, (char *)buf, size, errp); - if (ret < 0) { - if (ret == QIO_CHANNEL_ERR_BLOCK) { - if (qemu_in_coroutine()) { - qio_channel_yield(ioc, G_IO_IN); - } else { - qio_channel_wait(ioc, G_IO_IN); - } - } else { - return -EIO; - } - } - } while (ret == QIO_CHANNEL_ERR_BLOCK); - - return ret; -} - - -static int channel_close(void *opaque, Error **errp) -{ - int ret; - QIOChannel *ioc = QIO_CHANNEL(opaque); - ret = qio_channel_close(ioc, errp); - object_unref(OBJECT(ioc)); - return ret; -} - - -static int channel_shutdown(void *opaque, - bool rd, - bool wr, - Error **errp) -{ - QIOChannel *ioc = QIO_CHANNEL(opaque); - - if (qio_channel_has_feature(ioc, - QIO_CHANNEL_FEATURE_SHUTDOWN)) { - QIOChannelShutdown mode; - if (rd && wr) { - mode = QIO_CHANNEL_SHUTDOWN_BOTH; - } else if (rd) { - mode = QIO_CHANNEL_SHUTDOWN_READ; - } else { - mode = QIO_CHANNEL_SHUTDOWN_WRITE; - } - if (qio_channel_shutdown(ioc, mode, errp) < 0) { - return -EIO; - } - } - return 0; -} - - -static int channel_set_blocking(void *opaque, - bool enabled, - Error **errp) -{ - QIOChannel *ioc = QIO_CHANNEL(opaque); - - if (qio_channel_set_blocking(ioc, enabled, errp) < 0) { - return -1; - } - return 0; -} - -static QEMUFile *channel_get_input_return_path(void *opaque) -{ - QIOChannel *ioc = QIO_CHANNEL(opaque); - - return qemu_fopen_channel_output(ioc); -} - -static QEMUFile *channel_get_output_return_path(void *opaque) -{ - QIOChannel *ioc = QIO_CHANNEL(opaque); - - return qemu_fopen_channel_input(ioc); -} - -static const QEMUFileOps channel_input_ops = { - .get_buffer = channel_get_buffer, - .close = channel_close, - .shut_down = channel_shutdown, - .set_blocking = channel_set_blocking, - .get_return_path = channel_get_input_return_path, -}; - - -static const QEMUFileOps channel_output_ops = { - .writev_buffer = channel_writev_buffer, - .close = channel_close, - .shut_down = channel_shutdown, - .set_blocking = channel_set_blocking, - .get_return_path = channel_get_output_return_path, -}; - - -QEMUFile *qemu_fopen_channel_input(QIOChannel *ioc) -{ - object_ref(OBJECT(ioc)); - return qemu_fopen_ops(ioc, &channel_input_ops, true); -} - -QEMUFile *qemu_fopen_channel_output(QIOChannel *ioc) -{ - object_ref(OBJECT(ioc)); - return qemu_fopen_ops(ioc, &channel_output_ops, true); -} diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 1479cddad9..a10882d47f 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -27,23 +27,21 @@ #include "qemu/error-report.h" #include "qemu/iov.h" #include "migration.h" +#include "migration-stats.h" #include "qemu-file.h" #include "trace.h" +#include "options.h" #include "qapi/error.h" +#include "rdma.h" +#include "io/channel-file.h" #define IO_BUF_SIZE 32768 #define MAX_IOV_SIZE MIN_CONST(IOV_MAX, 64) struct QEMUFile { - const QEMUFileOps *ops; - const QEMUFileHooks *hooks; - void *opaque; + QIOChannel *ioc; + bool is_writable; - int64_t bytes_xfer; - int64_t xfer_limit; - - int64_t pos; /* start of buffer when writing, end of buffer - when reading */ int buf_index; int buf_size; /* 0 when writing */ uint8_t buf[IO_BUF_SIZE]; @@ -54,30 +52,66 @@ struct QEMUFile { int last_error; Error *last_error_obj; - /* has the file has been shutdown */ - bool shutdown; - /* Whether opaque points to a QIOChannel */ - bool has_ioc; }; /* * Stop a file from being read/written - not all backing files can do this * typically only sockets can. + * + * TODO: convert to propagate Error objects instead of squashing + * to a fixed errno value */ int qemu_file_shutdown(QEMUFile *f) { - int ret; - - f->shutdown = true; - if (!f->ops->shut_down) { - return -ENOSYS; - } - ret = f->ops->shut_down(f->opaque, true, true, NULL); + Error *err = NULL; + /* + * We must set qemufile error before the real shutdown(), otherwise + * there can be a race window where we thought IO all went though + * (because last_error==NULL) but actually IO has already stopped. + * + * If without correct ordering, the race can happen like this: + * + * page receiver other thread + * ------------- ------------ + * qemu_get_buffer() + * do shutdown() + * returns 0 (buffer all zero) + * (we didn't check this retcode) + * try to detect IO error + * last_error==NULL, IO okay + * install ALL-ZERO page + * set last_error + * --> guest crash! + */ if (!f->last_error) { qemu_file_set_error(f, -EIO); } - return ret; + + if (!qio_channel_has_feature(f->ioc, + QIO_CHANNEL_FEATURE_SHUTDOWN)) { + return -ENOSYS; + } + + if (qio_channel_shutdown(f->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, &err) < 0) { + error_report_err(err); + return -EIO; + } + + return 0; +} + +static QEMUFile *qemu_file_new_impl(QIOChannel *ioc, bool is_writable) +{ + QEMUFile *f; + + f = g_new0(QEMUFile, 1); + + object_ref(ioc); + f->ioc = ioc; + f->is_writable = is_writable; + + return f; } /* @@ -86,40 +120,17 @@ int qemu_file_shutdown(QEMUFile *f) */ QEMUFile *qemu_file_get_return_path(QEMUFile *f) { - if (!f->ops->get_return_path) { - return NULL; - } - return f->ops->get_return_path(f->opaque); + return qemu_file_new_impl(f->ioc, !f->is_writable); } -bool qemu_file_mode_is_not_valid(const char *mode) +QEMUFile *qemu_file_new_output(QIOChannel *ioc) { - if (mode == NULL || - (mode[0] != 'r' && mode[0] != 'w') || - mode[1] != 'b' || mode[2] != 0) { - fprintf(stderr, "qemu_fopen: Argument validity check failed\n"); - return true; - } - - return false; + return qemu_file_new_impl(ioc, true); } -QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops, bool has_ioc) +QEMUFile *qemu_file_new_input(QIOChannel *ioc) { - QEMUFile *f; - - f = g_new0(QEMUFile, 1); - - f->opaque = opaque; - f->ops = ops; - f->has_ioc = has_ioc; - return f; -} - - -void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks) -{ - f->hooks = hooks; + return qemu_file_new_impl(ioc, false); } /* @@ -127,18 +138,54 @@ void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks) * * Return negative error value if there has been an error on previous * operations, return 0 if no error happened. - * Optional, it returns Error* in errp, but it may be NULL even if return value - * is not 0. * + * If errp is specified, a verbose error message will be copied over. */ int qemu_file_get_error_obj(QEMUFile *f, Error **errp) { - if (errp) { - *errp = f->last_error_obj ? error_copy(f->last_error_obj) : NULL; + if (!f->last_error) { + return 0; } + + /* There is an error */ + if (errp) { + if (f->last_error_obj) { + *errp = error_copy(f->last_error_obj); + } else { + error_setg_errno(errp, -f->last_error, "Channel error"); + } + } + return f->last_error; } +/* + * Get last error for either stream f1 or f2 with optional Error*. + * The error returned (non-zero) can be either from f1 or f2. + * + * If any of the qemufile* is NULL, then skip the check on that file. + * + * When there is no error on both qemufile, zero is returned. + */ +int qemu_file_get_error_obj_any(QEMUFile *f1, QEMUFile *f2, Error **errp) +{ + int ret = 0; + + if (f1) { + ret = qemu_file_get_error_obj(f1, errp); + /* If there's already error detected, return */ + if (ret) { + return ret; + } + } + + if (f2) { + ret = qemu_file_get_error_obj(f2, errp); + } + + return ret; +} + /* * Set the last error for stream f with optional Error* */ @@ -161,7 +208,7 @@ void qemu_file_set_error_obj(QEMUFile *f, int ret, Error *err) */ int qemu_file_get_error(QEMUFile *f) { - return qemu_file_get_error_obj(f, NULL); + return f->last_error; } /* @@ -172,9 +219,9 @@ void qemu_file_set_error(QEMUFile *f, int ret) qemu_file_set_error_obj(f, ret, NULL); } -bool qemu_file_is_writable(QEMUFile *f) +static bool qemu_file_is_writable(QEMUFile *f) { - return f->ops->writev_buffer; + return f->is_writable; } static void qemu_iovec_release_ram(QEMUFile *f) @@ -212,114 +259,43 @@ static void qemu_iovec_release_ram(QEMUFile *f) memset(f->may_free, 0, sizeof(f->may_free)); } +bool qemu_file_is_seekable(QEMUFile *f) +{ + return qio_channel_has_feature(f->ioc, QIO_CHANNEL_FEATURE_SEEKABLE); +} + /** * Flushes QEMUFile buffer * * This will flush all pending data. If data was only partially flushed, it * will set an error state. */ -void qemu_fflush(QEMUFile *f) +int qemu_fflush(QEMUFile *f) { - ssize_t ret = 0; - ssize_t expect = 0; - Error *local_error = NULL; - if (!qemu_file_is_writable(f)) { - return; + return f->last_error; } - if (f->shutdown) { - return; + if (f->last_error) { + return f->last_error; } if (f->iovcnt > 0) { - expect = iov_size(f->iov, f->iovcnt); - ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos, - &local_error); + Error *local_error = NULL; + if (qio_channel_writev_all(f->ioc, + f->iov, f->iovcnt, + &local_error) < 0) { + qemu_file_set_error_obj(f, -EIO, local_error); + } else { + uint64_t size = iov_size(f->iov, f->iovcnt); + stat64_add(&mig_stats.qemu_file_transferred, size); + } qemu_iovec_release_ram(f); } - if (ret >= 0) { - f->pos += ret; - } - /* We expect the QEMUFile write impl to send the full - * data set we requested, so sanity check that. - */ - if (ret != expect) { - qemu_file_set_error_obj(f, ret < 0 ? ret : -EIO, local_error); - } f->buf_index = 0; f->iovcnt = 0; -} - -void ram_control_before_iterate(QEMUFile *f, uint64_t flags) -{ - int ret = 0; - - if (f->hooks && f->hooks->before_ram_iterate) { - ret = f->hooks->before_ram_iterate(f, f->opaque, flags, NULL); - if (ret < 0) { - qemu_file_set_error(f, ret); - } - } -} - -void ram_control_after_iterate(QEMUFile *f, uint64_t flags) -{ - int ret = 0; - - if (f->hooks && f->hooks->after_ram_iterate) { - ret = f->hooks->after_ram_iterate(f, f->opaque, flags, NULL); - if (ret < 0) { - qemu_file_set_error(f, ret); - } - } -} - -void ram_control_load_hook(QEMUFile *f, uint64_t flags, void *data) -{ - int ret = -EINVAL; - - if (f->hooks && f->hooks->hook_ram_load) { - ret = f->hooks->hook_ram_load(f, f->opaque, flags, data); - if (ret < 0) { - qemu_file_set_error(f, ret); - } - } else { - /* - * Hook is a hook specifically requested by the source sending a flag - * that expects there to be a hook on the destination. - */ - if (flags == RAM_CONTROL_HOOK) { - qemu_file_set_error(f, ret); - } - } -} - -size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, - ram_addr_t offset, size_t size, - uint64_t *bytes_sent) -{ - if (f->hooks && f->hooks->save_page) { - int ret = f->hooks->save_page(f, f->opaque, block_offset, - offset, size, bytes_sent); - if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { - f->bytes_xfer += size; - } - - if (ret != RAM_SAVE_CONTROL_DELAYED && - ret != RAM_SAVE_CONTROL_NOT_SUPP) { - if (bytes_sent && *bytes_sent > 0) { - qemu_update_position(f, *bytes_sent); - } else if (ret < 0) { - qemu_file_set_error(f, ret); - } - } - - return ret; - } - - return RAM_SAVE_CONTROL_NOT_SUPP; + return f->last_error; } /* @@ -330,7 +306,7 @@ size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, * case if the underlying file descriptor gives a short read, and that can * happen even on a blocking fd. */ -static ssize_t qemu_fill_buffer(QEMUFile *f) +static ssize_t coroutine_mixed_fn qemu_fill_buffer(QEMUFile *f) { int len; int pending; @@ -345,31 +321,37 @@ static ssize_t qemu_fill_buffer(QEMUFile *f) f->buf_index = 0; f->buf_size = pending; - if (f->shutdown) { + if (qemu_file_get_error(f)) { return 0; } - len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos, - IO_BUF_SIZE - pending, &local_error); + do { + len = qio_channel_read(f->ioc, + (char *)f->buf + pending, + IO_BUF_SIZE - pending, + &local_error); + if (len == QIO_CHANNEL_ERR_BLOCK) { + if (qemu_in_coroutine()) { + qio_channel_yield(f->ioc, G_IO_IN); + } else { + qio_channel_wait(f->ioc, G_IO_IN); + } + } else if (len < 0) { + len = -EIO; + } + } while (len == QIO_CHANNEL_ERR_BLOCK); + if (len > 0) { f->buf_size += len; - f->pos += len; } else if (len == 0) { qemu_file_set_error_obj(f, -EIO, local_error); - } else if (len != -EAGAIN) { - qemu_file_set_error_obj(f, len, local_error); } else { - error_free(local_error); + qemu_file_set_error_obj(f, len, local_error); } return len; } -void qemu_update_position(QEMUFile *f, size_t size) -{ - f->pos += size; -} - /** Closes the file * * Returns negative error value if any error happened on previous operations or @@ -380,22 +362,12 @@ void qemu_update_position(QEMUFile *f, size_t size) */ int qemu_fclose(QEMUFile *f) { - int ret; - qemu_fflush(f); - ret = qemu_file_get_error(f); - - if (f->ops->close) { - int ret2 = f->ops->close(f->opaque, NULL); - if (ret >= 0) { - ret = ret2; - } - } - /* If any error was spotted before closing, we should report it - * instead of the close() return value. - */ - if (f->last_error) { - ret = f->last_error; + int ret = qemu_fflush(f); + int ret2 = qio_channel_close(f->ioc, NULL); + if (ret >= 0) { + ret = ret2; } + g_clear_pointer(&f->ioc, object_unref); error_free(f->last_error_obj); g_free(f); trace_qemu_file_fclose(); @@ -422,7 +394,7 @@ static int add_to_iovec(QEMUFile *f, const uint8_t *buf, size_t size, } else { if (f->iovcnt >= MAX_IOV_SIZE) { /* Should only happen if a previous fflush failed */ - assert(f->shutdown || !qemu_file_is_writable(f)); + assert(qemu_file_get_error(f) || !qemu_file_is_writable(f)); return 1; } if (may_free) { @@ -457,7 +429,6 @@ void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size, return; } - f->bytes_xfer += size; add_to_iovec(f, buf, size, may_free); } @@ -475,7 +446,6 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, size_t size) l = size; } memcpy(f->buf + f->buf_index, buf, l); - f->bytes_xfer += l; add_buf_to_iovec(f, l); if (qemu_file_get_error(f)) { break; @@ -485,6 +455,107 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, size_t size) } } +void qemu_put_buffer_at(QEMUFile *f, const uint8_t *buf, size_t buflen, + off_t pos) +{ + Error *err = NULL; + size_t ret; + + if (f->last_error) { + return; + } + + qemu_fflush(f); + ret = qio_channel_pwrite(f->ioc, (char *)buf, buflen, pos, &err); + + if (err) { + qemu_file_set_error_obj(f, -EIO, err); + return; + } + + if ((ssize_t)ret == QIO_CHANNEL_ERR_BLOCK) { + qemu_file_set_error_obj(f, -EAGAIN, NULL); + return; + } + + if (ret != buflen) { + error_setg(&err, "Partial write of size %zu, expected %zu", ret, + buflen); + qemu_file_set_error_obj(f, -EIO, err); + return; + } + + stat64_add(&mig_stats.qemu_file_transferred, buflen); + + return; +} + + +size_t qemu_get_buffer_at(QEMUFile *f, const uint8_t *buf, size_t buflen, + off_t pos) +{ + Error *err = NULL; + size_t ret; + + if (f->last_error) { + return 0; + } + + ret = qio_channel_pread(f->ioc, (char *)buf, buflen, pos, &err); + + if ((ssize_t)ret == -1 || err) { + qemu_file_set_error_obj(f, -EIO, err); + return 0; + } + + if ((ssize_t)ret == QIO_CHANNEL_ERR_BLOCK) { + qemu_file_set_error_obj(f, -EAGAIN, NULL); + return 0; + } + + if (ret != buflen) { + error_setg(&err, "Partial read of size %zu, expected %zu", ret, buflen); + qemu_file_set_error_obj(f, -EIO, err); + return 0; + } + + return ret; +} + +void qemu_set_offset(QEMUFile *f, off_t off, int whence) +{ + Error *err = NULL; + off_t ret; + + if (qemu_file_is_writable(f)) { + qemu_fflush(f); + } else { + /* Drop all cached buffers if existed; will trigger a re-fill later */ + f->buf_index = 0; + f->buf_size = 0; + } + + ret = qio_channel_io_seek(f->ioc, off, whence, &err); + if (ret == (off_t)-1) { + qemu_file_set_error_obj(f, -EIO, err); + } +} + +off_t qemu_get_offset(QEMUFile *f) +{ + Error *err = NULL; + off_t ret; + + qemu_fflush(f); + + ret = qio_channel_io_seek(f->ioc, 0, SEEK_CUR, &err); + if (ret == (off_t)-1) { + qemu_file_set_error_obj(f, -EIO, err); + } + return ret; +} + + void qemu_put_byte(QEMUFile *f, int v) { if (f->last_error) { @@ -492,7 +563,6 @@ void qemu_put_byte(QEMUFile *f, int v) } f->buf[f->buf_index] = v; - f->bytes_xfer++; add_buf_to_iovec(f, 1); } @@ -511,7 +581,7 @@ void qemu_file_skip(QEMUFile *f, int size) * return as many as it managed to read (assuming blocking fd's which * all current QEMUFile are) */ -size_t qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset) +size_t coroutine_mixed_fn qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset) { ssize_t pending; size_t index; @@ -559,7 +629,7 @@ size_t qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset) * return as many as it managed to read (assuming blocking fd's which * all current QEMUFile are) */ -size_t qemu_get_buffer(QEMUFile *f, uint8_t *buf, size_t size) +size_t coroutine_mixed_fn qemu_get_buffer(QEMUFile *f, uint8_t *buf, size_t size) { size_t pending = size; size_t done = 0; @@ -600,7 +670,7 @@ size_t qemu_get_buffer(QEMUFile *f, uint8_t *buf, size_t size) * Note: Since **buf may get changed, the caller should take care to * keep a pointer to the original buffer if it needs to deallocate it. */ -size_t qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size) +size_t coroutine_mixed_fn qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size) { if (size < IO_BUF_SIZE) { size_t res; @@ -622,7 +692,7 @@ size_t qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size) * Peeks a single byte from the buffer; this isn't guaranteed to work if * offset leaves a gap after the previous read/peeked data. */ -int qemu_peek_byte(QEMUFile *f, int offset) +int coroutine_mixed_fn qemu_peek_byte(QEMUFile *f, int offset) { int index = f->buf_index + offset; @@ -639,7 +709,7 @@ int qemu_peek_byte(QEMUFile *f, int offset) return f->buf[index]; } -int qemu_get_byte(QEMUFile *f) +int coroutine_mixed_fn qemu_get_byte(QEMUFile *f) { int result; @@ -648,11 +718,13 @@ int qemu_get_byte(QEMUFile *f) return result; } -int64_t qemu_ftell_fast(QEMUFile *f) +uint64_t qemu_file_transferred(QEMUFile *f) { - int64_t ret = f->pos; + uint64_t ret = stat64_get(&mig_stats.qemu_file_transferred); int i; + g_assert(qemu_file_is_writable(f)); + for (i = 0; i < f->iovcnt; i++) { ret += f->iov[i].iov_len; } @@ -660,46 +732,6 @@ int64_t qemu_ftell_fast(QEMUFile *f) return ret; } -int64_t qemu_ftell(QEMUFile *f) -{ - qemu_fflush(f); - return f->pos; -} - -int qemu_file_rate_limit(QEMUFile *f) -{ - if (f->shutdown) { - return 1; - } - if (qemu_file_get_error(f)) { - return 1; - } - if (f->xfer_limit > 0 && f->bytes_xfer > f->xfer_limit) { - return 1; - } - return 0; -} - -int64_t qemu_file_get_rate_limit(QEMUFile *f) -{ - return f->xfer_limit; -} - -void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit) -{ - f->xfer_limit = limit; -} - -void qemu_file_reset_rate_limit(QEMUFile *f) -{ - f->bytes_xfer = 0; -} - -void qemu_file_update_transfer(QEMUFile *f, int64_t len) -{ - f->bytes_xfer += len; -} - void qemu_put_be16(QEMUFile *f, unsigned int v) { qemu_put_byte(f, v >> 8); @@ -813,6 +845,17 @@ int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src) return len; } +/* + * Check if the writable buffer is empty + */ + +bool qemu_file_buffer_empty(QEMUFile *file) +{ + assert(qemu_file_is_writable(file)); + + return !file->iovcnt; +} + /* * Get a string whose length is determined by a single preceding byte * A preallocated 256 byte buffer must be passed in. @@ -820,7 +863,7 @@ int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src) * else 0 * (Note a 0 length string will return 0 either way) */ -size_t qemu_get_counted_string(QEMUFile *f, char buf[256]) +size_t coroutine_fn qemu_get_counted_string(QEMUFile *f, char buf[256]) { size_t len = qemu_get_byte(f); size_t res = qemu_get_buffer(f, (uint8_t *)buf, len); @@ -851,19 +894,52 @@ void qemu_put_counted_string(QEMUFile *f, const char *str) */ void qemu_file_set_blocking(QEMUFile *f, bool block) { - if (f->ops->set_blocking) { - f->ops->set_blocking(f->opaque, block, NULL); - } + qio_channel_set_blocking(f->ioc, block, NULL); } /* - * Return the ioc object if it's a migration channel. Note: it can return NULL - * for callers passing in a non-migration qemufile. E.g. see qemu_fopen_bdrv() - * and its usage in e.g. load_snapshot(). So we need to check against NULL - * before using it. If without the check, migration_incoming_state_destroy() - * could fail for load_snapshot(). + * qemu_file_get_ioc: + * + * Get the ioc object for the file, without incrementing + * the reference count. + * + * Returns: the ioc object */ QIOChannel *qemu_file_get_ioc(QEMUFile *file) { - return file->has_ioc ? QIO_CHANNEL(file->opaque) : NULL; + return file->ioc; +} + +/* + * Read size bytes from QEMUFile f and write them to fd. + */ +int qemu_file_get_to_fd(QEMUFile *f, int fd, size_t size) +{ + while (size) { + size_t pending = f->buf_size - f->buf_index; + ssize_t rc; + + if (!pending) { + rc = qemu_fill_buffer(f); + if (rc < 0) { + return rc; + } + if (rc == 0) { + return -EIO; + } + continue; + } + + rc = write(fd, f->buf + f->buf_index, MIN(pending, size)); + if (rc < 0) { + return -errno; + } + if (rc == 0) { + return -EIO; + } + f->buf_index += rc; + size -= rc; + } + + return 0; } diff --git a/migration/qemu-file.h b/migration/qemu-file.h index 3f36d4dc8c..32fd4a34fd 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -29,157 +29,59 @@ #include "exec/cpu-common.h" #include "io/channel.h" -/* Read a chunk of data from a file at the given position. The pos argument - * can be ignored if the file is only be used for streaming. The number of - * bytes actually read should be returned. - */ -typedef ssize_t (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf, - int64_t pos, size_t size, - Error **errp); - -/* Close a file - * - * Return negative error number on error, 0 or positive value on success. - * - * The meaning of return value on success depends on the specific back-end being - * used. - */ -typedef int (QEMUFileCloseFunc)(void *opaque, Error **errp); - -/* Called to return the OS file descriptor associated to the QEMUFile. - */ -typedef int (QEMUFileGetFD)(void *opaque); - -/* Called to change the blocking mode of the file - */ -typedef int (QEMUFileSetBlocking)(void *opaque, bool enabled, Error **errp); - -/* - * This function writes an iovec to file. The handler must write all - * of the data or return a negative errno value. - */ -typedef ssize_t (QEMUFileWritevBufferFunc)(void *opaque, struct iovec *iov, - int iovcnt, int64_t pos, - Error **errp); - -/* - * This function provides hooks around different - * stages of RAM migration. - * 'opaque' is the backend specific data in QEMUFile - * 'data' is call specific data associated with the 'flags' value - */ -typedef int (QEMURamHookFunc)(QEMUFile *f, void *opaque, uint64_t flags, - void *data); - -/* - * Constants used by ram_control_* hooks - */ -#define RAM_CONTROL_SETUP 0 -#define RAM_CONTROL_ROUND 1 -#define RAM_CONTROL_HOOK 2 -#define RAM_CONTROL_FINISH 3 -#define RAM_CONTROL_BLOCK_REG 4 - -/* - * This function allows override of where the RAM page - * is saved (such as RDMA, for example.) - */ -typedef size_t (QEMURamSaveFunc)(QEMUFile *f, void *opaque, - ram_addr_t block_offset, - ram_addr_t offset, - size_t size, - uint64_t *bytes_sent); - -/* - * Return a QEMUFile for comms in the opposite direction - */ -typedef QEMUFile *(QEMURetPathFunc)(void *opaque); - -/* - * Stop any read or write (depending on flags) on the underlying - * transport on the QEMUFile. - * Existing blocking reads/writes must be woken - * Returns 0 on success, -err on error - */ -typedef int (QEMUFileShutdownFunc)(void *opaque, bool rd, bool wr, - Error **errp); - -typedef struct QEMUFileOps { - QEMUFileGetBufferFunc *get_buffer; - QEMUFileCloseFunc *close; - QEMUFileSetBlocking *set_blocking; - QEMUFileWritevBufferFunc *writev_buffer; - QEMURetPathFunc *get_return_path; - QEMUFileShutdownFunc *shut_down; -} QEMUFileOps; - -typedef struct QEMUFileHooks { - QEMURamHookFunc *before_ram_iterate; - QEMURamHookFunc *after_ram_iterate; - QEMURamHookFunc *hook_ram_load; - QEMURamSaveFunc *save_page; -} QEMUFileHooks; - -QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops, bool has_ioc); -void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks); -int qemu_get_fd(QEMUFile *f); +QEMUFile *qemu_file_new_input(QIOChannel *ioc); +QEMUFile *qemu_file_new_output(QIOChannel *ioc); int qemu_fclose(QEMUFile *f); -int64_t qemu_ftell(QEMUFile *f); -int64_t qemu_ftell_fast(QEMUFile *f); + +/* + * qemu_file_transferred: + * + * No flush is performed and the reported amount will include the size + * of any queued buffers, on top of the amount actually transferred. + * + * Returns: the total bytes transferred and queued + */ +uint64_t qemu_file_transferred(QEMUFile *f); + /* * put_buffer without copying the buffer. * The buffer should be available till it is sent asynchronously. */ void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size, bool may_free); -bool qemu_file_mode_is_not_valid(const char *mode); -bool qemu_file_is_writable(QEMUFile *f); #include "migration/qemu-file-types.h" -size_t qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset); -size_t qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size); +size_t coroutine_mixed_fn qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset); +size_t coroutine_mixed_fn qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size); ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, const uint8_t *p, size_t size); int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src); +bool qemu_file_buffer_empty(QEMUFile *file); /* * Note that you can only peek continuous bytes from where the current pointer * is; you aren't guaranteed to be able to peak to +n bytes unless you've * previously peeked +n-1. */ -int qemu_peek_byte(QEMUFile *f, int offset); +int coroutine_mixed_fn qemu_peek_byte(QEMUFile *f, int offset); void qemu_file_skip(QEMUFile *f, int size); -void qemu_update_position(QEMUFile *f, size_t size); -void qemu_file_reset_rate_limit(QEMUFile *f); -void qemu_file_update_transfer(QEMUFile *f, int64_t len); -void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate); -int64_t qemu_file_get_rate_limit(QEMUFile *f); -int qemu_file_get_error_obj(QEMUFile *f, Error **errp); +int qemu_file_get_error_obj_any(QEMUFile *f1, QEMUFile *f2, Error **errp); void qemu_file_set_error_obj(QEMUFile *f, int ret, Error *err); +int qemu_file_get_error_obj(QEMUFile *f, Error **errp); void qemu_file_set_error(QEMUFile *f, int ret); int qemu_file_shutdown(QEMUFile *f); QEMUFile *qemu_file_get_return_path(QEMUFile *f); -void qemu_fflush(QEMUFile *f); +int qemu_fflush(QEMUFile *f); void qemu_file_set_blocking(QEMUFile *f, bool block); +int qemu_file_get_to_fd(QEMUFile *f, int fd, size_t size); +void qemu_set_offset(QEMUFile *f, off_t off, int whence); +off_t qemu_get_offset(QEMUFile *f); +void qemu_put_buffer_at(QEMUFile *f, const uint8_t *buf, size_t buflen, + off_t pos); +size_t qemu_get_buffer_at(QEMUFile *f, const uint8_t *buf, size_t buflen, + off_t pos); -void ram_control_before_iterate(QEMUFile *f, uint64_t flags); -void ram_control_after_iterate(QEMUFile *f, uint64_t flags); -void ram_control_load_hook(QEMUFile *f, uint64_t flags, void *data); - -/* Whenever this is found in the data stream, the flags - * will be passed to ram_control_load_hook in the incoming-migration - * side. This lets before_ram_iterate/after_ram_iterate add - * transport-specific sections to the RAM migration data. - */ -#define RAM_SAVE_FLAG_HOOK 0x80 - -#define RAM_SAVE_CONTROL_NOT_SUPP -1000 -#define RAM_SAVE_CONTROL_DELAYED -2000 - -size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, - ram_addr_t offset, size_t size, - uint64_t *bytes_sent); QIOChannel *qemu_file_get_ioc(QEMUFile *file); #endif diff --git a/migration/ram-compress.c b/migration/ram-compress.c new file mode 100644 index 0000000000..fa4388f6a6 --- /dev/null +++ b/migration/ram-compress.c @@ -0,0 +1,564 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2011-2015 Red Hat Inc + * + * Authors: + * Juan Quintela + * + * 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/cutils.h" + +#include "ram-compress.h" + +#include "qemu/error-report.h" +#include "qemu/stats64.h" +#include "migration.h" +#include "options.h" +#include "io/channel-null.h" +#include "exec/target_page.h" +#include "exec/ramblock.h" +#include "ram.h" +#include "migration-stats.h" + +static struct { + int64_t pages; + int64_t busy; + double busy_rate; + int64_t compressed_size; + double compression_rate; + /* compression statistics since the beginning of the period */ + /* amount of count that no free thread to compress data */ + uint64_t compress_thread_busy_prev; + /* amount bytes after compression */ + uint64_t compressed_size_prev; + /* amount of compressed pages */ + uint64_t compress_pages_prev; +} compression_counters; + +static CompressParam *comp_param; +static QemuThread *compress_threads; +/* comp_done_cond is used to wake up the migration thread when + * one of the compression threads has finished the compression. + * comp_done_lock is used to co-work with comp_done_cond. + */ +static QemuMutex comp_done_lock; +static QemuCond comp_done_cond; + +struct DecompressParam { + bool done; + bool quit; + QemuMutex mutex; + QemuCond cond; + void *des; + uint8_t *compbuf; + int len; + z_stream stream; +}; +typedef struct DecompressParam DecompressParam; + +static QEMUFile *decomp_file; +static DecompressParam *decomp_param; +static QemuThread *decompress_threads; +static QemuMutex decomp_done_lock; +static QemuCond decomp_done_cond; + +static CompressResult do_compress_ram_page(QEMUFile *f, z_stream *stream, + RAMBlock *block, ram_addr_t offset, + uint8_t *source_buf); + +static void *do_data_compress(void *opaque) +{ + CompressParam *param = opaque; + RAMBlock *block; + ram_addr_t offset; + CompressResult result; + + qemu_mutex_lock(¶m->mutex); + while (!param->quit) { + if (param->trigger) { + block = param->block; + offset = param->offset; + param->trigger = false; + qemu_mutex_unlock(¶m->mutex); + + result = do_compress_ram_page(param->file, ¶m->stream, + block, offset, param->originbuf); + + qemu_mutex_lock(&comp_done_lock); + param->done = true; + param->result = result; + qemu_cond_signal(&comp_done_cond); + qemu_mutex_unlock(&comp_done_lock); + + qemu_mutex_lock(¶m->mutex); + } else { + qemu_cond_wait(¶m->cond, ¶m->mutex); + } + } + qemu_mutex_unlock(¶m->mutex); + + return NULL; +} + +void compress_threads_save_cleanup(void) +{ + int i, thread_count; + + if (!migrate_compress() || !comp_param) { + return; + } + + thread_count = migrate_compress_threads(); + for (i = 0; i < thread_count; i++) { + /* + * we use it as a indicator which shows if the thread is + * properly init'd or not + */ + if (!comp_param[i].file) { + break; + } + + qemu_mutex_lock(&comp_param[i].mutex); + comp_param[i].quit = true; + qemu_cond_signal(&comp_param[i].cond); + qemu_mutex_unlock(&comp_param[i].mutex); + + qemu_thread_join(compress_threads + i); + qemu_mutex_destroy(&comp_param[i].mutex); + qemu_cond_destroy(&comp_param[i].cond); + deflateEnd(&comp_param[i].stream); + g_free(comp_param[i].originbuf); + qemu_fclose(comp_param[i].file); + comp_param[i].file = NULL; + } + qemu_mutex_destroy(&comp_done_lock); + qemu_cond_destroy(&comp_done_cond); + g_free(compress_threads); + g_free(comp_param); + compress_threads = NULL; + comp_param = NULL; +} + +int compress_threads_save_setup(void) +{ + int i, thread_count; + + if (!migrate_compress()) { + return 0; + } + thread_count = migrate_compress_threads(); + compress_threads = g_new0(QemuThread, thread_count); + comp_param = g_new0(CompressParam, thread_count); + qemu_cond_init(&comp_done_cond); + qemu_mutex_init(&comp_done_lock); + for (i = 0; i < thread_count; i++) { + comp_param[i].originbuf = g_try_malloc(qemu_target_page_size()); + if (!comp_param[i].originbuf) { + goto exit; + } + + if (deflateInit(&comp_param[i].stream, + migrate_compress_level()) != Z_OK) { + g_free(comp_param[i].originbuf); + goto exit; + } + + /* comp_param[i].file is just used as a dummy buffer to save data, + * set its ops to empty. + */ + comp_param[i].file = qemu_file_new_output( + QIO_CHANNEL(qio_channel_null_new())); + comp_param[i].done = true; + comp_param[i].quit = false; + qemu_mutex_init(&comp_param[i].mutex); + qemu_cond_init(&comp_param[i].cond); + qemu_thread_create(compress_threads + i, "compress", + do_data_compress, comp_param + i, + QEMU_THREAD_JOINABLE); + } + return 0; + +exit: + compress_threads_save_cleanup(); + return -1; +} + +static CompressResult do_compress_ram_page(QEMUFile *f, z_stream *stream, + RAMBlock *block, ram_addr_t offset, + uint8_t *source_buf) +{ + uint8_t *p = block->host + offset; + size_t page_size = qemu_target_page_size(); + int ret; + + assert(qemu_file_buffer_empty(f)); + + if (buffer_is_zero(p, page_size)) { + return RES_ZEROPAGE; + } + + /* + * copy it to a internal buffer to avoid it being modified by VM + * so that we can catch up the error during compression and + * decompression + */ + memcpy(source_buf, p, page_size); + ret = qemu_put_compression_data(f, stream, source_buf, page_size); + if (ret < 0) { + qemu_file_set_error(migrate_get_current()->to_dst_file, ret); + error_report("compressed data failed!"); + qemu_fflush(f); + return RES_NONE; + } + return RES_COMPRESS; +} + +static inline void compress_reset_result(CompressParam *param) +{ + param->result = RES_NONE; + param->block = NULL; + param->offset = 0; +} + +void compress_flush_data(void) +{ + int thread_count = migrate_compress_threads(); + + if (!migrate_compress()) { + return; + } + + qemu_mutex_lock(&comp_done_lock); + for (int i = 0; i < thread_count; i++) { + while (!comp_param[i].done) { + qemu_cond_wait(&comp_done_cond, &comp_done_lock); + } + } + qemu_mutex_unlock(&comp_done_lock); + + for (int i = 0; i < thread_count; i++) { + qemu_mutex_lock(&comp_param[i].mutex); + if (!comp_param[i].quit) { + CompressParam *param = &comp_param[i]; + compress_send_queued_data(param); + assert(qemu_file_buffer_empty(param->file)); + compress_reset_result(param); + } + qemu_mutex_unlock(&comp_param[i].mutex); + } +} + +static inline void set_compress_params(CompressParam *param, RAMBlock *block, + ram_addr_t offset) +{ + param->block = block; + param->offset = offset; + param->trigger = true; +} + +/* + * Return true when it compress a page + */ +bool compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset, + int (send_queued_data(CompressParam *))) +{ + int thread_count; + bool wait = migrate_compress_wait_thread(); + + thread_count = migrate_compress_threads(); + qemu_mutex_lock(&comp_done_lock); + + while (true) { + for (int i = 0; i < thread_count; i++) { + if (comp_param[i].done) { + CompressParam *param = &comp_param[i]; + qemu_mutex_lock(¶m->mutex); + param->done = false; + send_queued_data(param); + assert(qemu_file_buffer_empty(param->file)); + compress_reset_result(param); + set_compress_params(param, block, offset); + + qemu_cond_signal(¶m->cond); + qemu_mutex_unlock(¶m->mutex); + qemu_mutex_unlock(&comp_done_lock); + return true; + } + } + if (!wait) { + qemu_mutex_unlock(&comp_done_lock); + compression_counters.busy++; + return false; + } + /* + * wait for a free thread if the user specifies + * 'compress-wait-thread', otherwise we will post the page out + * in the main thread as normal page. + */ + qemu_cond_wait(&comp_done_cond, &comp_done_lock); + } +} + +/* return the size after decompression, or negative value on error */ +static int +qemu_uncompress_data(z_stream *stream, uint8_t *dest, size_t dest_len, + const uint8_t *source, size_t source_len) +{ + int err; + + err = inflateReset(stream); + if (err != Z_OK) { + return -1; + } + + stream->avail_in = source_len; + stream->next_in = (uint8_t *)source; + stream->avail_out = dest_len; + stream->next_out = dest; + + err = inflate(stream, Z_NO_FLUSH); + if (err != Z_STREAM_END) { + return -1; + } + + return stream->total_out; +} + +static void *do_data_decompress(void *opaque) +{ + DecompressParam *param = opaque; + unsigned long pagesize; + uint8_t *des; + int len, ret; + + qemu_mutex_lock(¶m->mutex); + while (!param->quit) { + if (param->des) { + des = param->des; + len = param->len; + param->des = 0; + qemu_mutex_unlock(¶m->mutex); + + pagesize = qemu_target_page_size(); + + ret = qemu_uncompress_data(¶m->stream, des, pagesize, + param->compbuf, len); + if (ret < 0 && migrate_get_current()->decompress_error_check) { + error_report("decompress data failed"); + qemu_file_set_error(decomp_file, ret); + } + + qemu_mutex_lock(&decomp_done_lock); + param->done = true; + qemu_cond_signal(&decomp_done_cond); + qemu_mutex_unlock(&decomp_done_lock); + + qemu_mutex_lock(¶m->mutex); + } else { + qemu_cond_wait(¶m->cond, ¶m->mutex); + } + } + qemu_mutex_unlock(¶m->mutex); + + return NULL; +} + +int wait_for_decompress_done(void) +{ + if (!migrate_compress()) { + return 0; + } + + int thread_count = migrate_decompress_threads(); + qemu_mutex_lock(&decomp_done_lock); + for (int i = 0; i < thread_count; i++) { + while (!decomp_param[i].done) { + qemu_cond_wait(&decomp_done_cond, &decomp_done_lock); + } + } + qemu_mutex_unlock(&decomp_done_lock); + return qemu_file_get_error(decomp_file); +} + +void compress_threads_load_cleanup(void) +{ + int i, thread_count; + + if (!migrate_compress()) { + return; + } + thread_count = migrate_decompress_threads(); + for (i = 0; i < thread_count; i++) { + /* + * we use it as a indicator which shows if the thread is + * properly init'd or not + */ + if (!decomp_param[i].compbuf) { + break; + } + + qemu_mutex_lock(&decomp_param[i].mutex); + decomp_param[i].quit = true; + qemu_cond_signal(&decomp_param[i].cond); + qemu_mutex_unlock(&decomp_param[i].mutex); + } + for (i = 0; i < thread_count; i++) { + if (!decomp_param[i].compbuf) { + break; + } + + qemu_thread_join(decompress_threads + i); + qemu_mutex_destroy(&decomp_param[i].mutex); + qemu_cond_destroy(&decomp_param[i].cond); + inflateEnd(&decomp_param[i].stream); + g_free(decomp_param[i].compbuf); + decomp_param[i].compbuf = NULL; + } + g_free(decompress_threads); + g_free(decomp_param); + decompress_threads = NULL; + decomp_param = NULL; + decomp_file = NULL; +} + +int compress_threads_load_setup(QEMUFile *f) +{ + int i, thread_count; + + if (!migrate_compress()) { + return 0; + } + + /* + * set compression_counters memory to zero for a new migration + */ + memset(&compression_counters, 0, sizeof(compression_counters)); + + thread_count = migrate_decompress_threads(); + decompress_threads = g_new0(QemuThread, thread_count); + decomp_param = g_new0(DecompressParam, thread_count); + qemu_mutex_init(&decomp_done_lock); + qemu_cond_init(&decomp_done_cond); + decomp_file = f; + for (i = 0; i < thread_count; i++) { + if (inflateInit(&decomp_param[i].stream) != Z_OK) { + goto exit; + } + + size_t compbuf_size = compressBound(qemu_target_page_size()); + decomp_param[i].compbuf = g_malloc0(compbuf_size); + qemu_mutex_init(&decomp_param[i].mutex); + qemu_cond_init(&decomp_param[i].cond); + decomp_param[i].done = true; + decomp_param[i].quit = false; + qemu_thread_create(decompress_threads + i, "decompress", + do_data_decompress, decomp_param + i, + QEMU_THREAD_JOINABLE); + } + return 0; +exit: + compress_threads_load_cleanup(); + return -1; +} + +void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len) +{ + int thread_count = migrate_decompress_threads(); + QEMU_LOCK_GUARD(&decomp_done_lock); + while (true) { + for (int i = 0; i < thread_count; i++) { + if (decomp_param[i].done) { + decomp_param[i].done = false; + qemu_mutex_lock(&decomp_param[i].mutex); + qemu_get_buffer(f, decomp_param[i].compbuf, len); + decomp_param[i].des = host; + decomp_param[i].len = len; + qemu_cond_signal(&decomp_param[i].cond); + qemu_mutex_unlock(&decomp_param[i].mutex); + return; + } + } + qemu_cond_wait(&decomp_done_cond, &decomp_done_lock); + } +} + +void populate_compress(MigrationInfo *info) +{ + if (!migrate_compress()) { + return; + } + info->compression = g_malloc0(sizeof(*info->compression)); + info->compression->pages = compression_counters.pages; + info->compression->busy = compression_counters.busy; + info->compression->busy_rate = compression_counters.busy_rate; + info->compression->compressed_size = compression_counters.compressed_size; + info->compression->compression_rate = compression_counters.compression_rate; +} + +uint64_t compress_ram_pages(void) +{ + return compression_counters.pages; +} + +void update_compress_thread_counts(const CompressParam *param, int bytes_xmit) +{ + ram_transferred_add(bytes_xmit); + + if (param->result == RES_ZEROPAGE) { + stat64_add(&mig_stats.zero_pages, 1); + return; + } + + /* 8 means a header with RAM_SAVE_FLAG_CONTINUE. */ + compression_counters.compressed_size += bytes_xmit - 8; + compression_counters.pages++; +} + +void compress_update_rates(uint64_t page_count) +{ + if (!migrate_compress()) { + return; + } + compression_counters.busy_rate = (double)(compression_counters.busy - + compression_counters.compress_thread_busy_prev) / page_count; + compression_counters.compress_thread_busy_prev = + compression_counters.busy; + + double compressed_size = compression_counters.compressed_size - + compression_counters.compressed_size_prev; + if (compressed_size) { + double uncompressed_size = (compression_counters.pages - + compression_counters.compress_pages_prev) * + qemu_target_page_size(); + + /* Compression-Ratio = Uncompressed-size / Compressed-size */ + compression_counters.compression_rate = + uncompressed_size / compressed_size; + + compression_counters.compress_pages_prev = + compression_counters.pages; + compression_counters.compressed_size_prev = + compression_counters.compressed_size; + } +} diff --git a/migration/ram-compress.h b/migration/ram-compress.h new file mode 100644 index 0000000000..0d89a2f55e --- /dev/null +++ b/migration/ram-compress.h @@ -0,0 +1,77 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2011-2015 Red Hat Inc + * + * Authors: + * Juan Quintela + * + * 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_MIGRATION_COMPRESS_H +#define QEMU_MIGRATION_COMPRESS_H + +#include "qemu-file.h" +#include "qapi/qapi-types-migration.h" + +enum CompressResult { + RES_NONE = 0, + RES_ZEROPAGE = 1, + RES_COMPRESS = 2 +}; +typedef enum CompressResult CompressResult; + +struct CompressParam { + bool done; + bool quit; + bool trigger; + CompressResult result; + QEMUFile *file; + QemuMutex mutex; + QemuCond cond; + RAMBlock *block; + ram_addr_t offset; + + /* internally used fields */ + z_stream stream; + uint8_t *originbuf; +}; +typedef struct CompressParam CompressParam; + +void compress_threads_save_cleanup(void); +int compress_threads_save_setup(void); + +bool compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset, + int (send_queued_data(CompressParam *))); + +int wait_for_decompress_done(void); +void compress_threads_load_cleanup(void); +int compress_threads_load_setup(QEMUFile *f); +void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len); + +void populate_compress(MigrationInfo *info); +uint64_t compress_ram_pages(void); +void update_compress_thread_counts(const CompressParam *param, int bytes_xmit); +void compress_update_rates(uint64_t page_count); +int compress_send_queued_data(CompressParam *param); +void compress_flush_data(void); + +#endif diff --git a/migration/ram.c b/migration/ram.c index 5f5e37f64d..8deb84984f 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -33,8 +33,10 @@ #include "qemu/madvise.h" #include "qemu/main-loop.h" #include "xbzrle.h" +#include "ram-compress.h" #include "ram.h" #include "migration.h" +#include "migration-stats.h" #include "migration/register.h" #include "migration/misc.h" #include "qemu-file.h" @@ -44,6 +46,7 @@ #include "qapi/error.h" #include "qapi/qapi-types-migration.h" #include "qapi/qapi-events-migration.h" +#include "qapi/qapi-commands-migration.h" #include "qapi/qmp/qerror.h" #include "trace.h" #include "exec/ram_addr.h" @@ -56,6 +59,10 @@ #include "qemu/iov.h" #include "multifd.h" #include "sysemu/runstate.h" +#include "rdma.h" +#include "options.h" +#include "sysemu/dirtylimit.h" +#include "sysemu/kvm.h" #include "hw/boards.h" /* for machine_dump_guest_core() */ @@ -66,24 +73,67 @@ /***********************************************************/ /* ram save/restore */ -/* RAM_SAVE_FLAG_ZERO used to be named RAM_SAVE_FLAG_COMPRESS, it - * worked for pages that where filled with the same char. We switched +/* + * RAM_SAVE_FLAG_ZERO used to be named RAM_SAVE_FLAG_COMPRESS, it + * worked for pages that were filled with the same char. We switched * it to only search for the zero value. And to avoid confusion with - * RAM_SSAVE_FLAG_COMPRESS_PAGE just rename it. + * RAM_SAVE_FLAG_COMPRESS_PAGE just rename it. */ - -#define RAM_SAVE_FLAG_FULL 0x01 /* Obsolete, not used anymore */ +/* + * RAM_SAVE_FLAG_FULL was obsoleted in 2009, it can be reused now + */ +#define RAM_SAVE_FLAG_FULL 0x01 #define RAM_SAVE_FLAG_ZERO 0x02 #define RAM_SAVE_FLAG_MEM_SIZE 0x04 #define RAM_SAVE_FLAG_PAGE 0x08 #define RAM_SAVE_FLAG_EOS 0x10 #define RAM_SAVE_FLAG_CONTINUE 0x20 #define RAM_SAVE_FLAG_XBZRLE 0x40 -/* 0x80 is reserved in migration.h start with 0x100 next */ +/* 0x80 is reserved in rdma.h for RAM_SAVE_FLAG_HOOK */ #define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100 +#define RAM_SAVE_FLAG_MULTIFD_FLUSH 0x200 +/* We can't use any flag that is bigger than 0x200 */ + +/* + * mapped-ram migration supports O_DIRECT, so we need to make sure the + * userspace buffer, the IO operation size and the file offset are + * aligned according to the underlying device's block size. The first + * two are already aligned to page size, but we need to add padding to + * the file to align the offset. We cannot read the block size + * dynamically because the migration file can be moved between + * different systems, so use 1M to cover most block sizes and to keep + * the file offset aligned at page size as well. + */ +#define MAPPED_RAM_FILE_OFFSET_ALIGNMENT 0x100000 + +/* + * When doing mapped-ram migration, this is the amount we read from + * the pages region in the migration file at a time. + */ +#define MAPPED_RAM_LOAD_BUF_SIZE 0x100000 XBZRLECacheStats xbzrle_counters; +/* used by the search for pages to send */ +struct PageSearchStatus { + /* The migration channel used for a specific host page */ + QEMUFile *pss_channel; + /* Last block from where we have sent data */ + RAMBlock *last_sent_block; + /* Current block being searched */ + RAMBlock *block; + /* Current page to search from */ + unsigned long page; + /* Set once we wrap around */ + bool complete_round; + /* Whether we're sending a host page */ + bool host_page_sending; + /* The start/end of current host page. Invalid if host_page_sending==false */ + unsigned long host_page_start; + unsigned long host_page_end; +}; +typedef struct PageSearchStatus PageSearchStatus; + /* struct contains XBZRLE cache and a static page used by the compression */ static struct { @@ -102,14 +152,14 @@ static struct { static void XBZRLE_cache_lock(void) { - if (migrate_use_xbzrle()) { + if (migrate_xbzrle()) { qemu_mutex_lock(&XBZRLE.lock); } } static void XBZRLE_cache_unlock(void) { - if (migrate_use_xbzrle()) { + if (migrate_xbzrle()) { qemu_mutex_unlock(&XBZRLE.lock); } } @@ -161,10 +211,16 @@ out: return ret; } -bool ramblock_is_ignored(RAMBlock *block) +static bool postcopy_preempt_active(void) +{ + return migrate_postcopy_preempt() && migration_in_postcopy(); +} + +bool migrate_ram_is_ignored(RAMBlock *block) { return !qemu_ram_is_migratable(block) || - (migrate_ignore_shared() && qemu_ram_is_shared(block)); + (migrate_ignore_shared() && qemu_ram_is_shared(block) + && qemu_ram_is_named_file(block)); } #undef RAMBLOCK_FOREACH @@ -267,17 +323,15 @@ int64_t ramblock_recv_bitmap_send(QEMUFile *file, qemu_put_be64(file, size); qemu_put_buffer(file, (const uint8_t *)le_bitmap, size); + g_free(le_bitmap); /* * Mark as an end, in case the middle part is screwed up due to * some "mysterious" reason. */ qemu_put_be64(file, RAMBLOCK_RECV_BITMAP_ENDING); - qemu_fflush(file); - - g_free(le_bitmap); - - if (qemu_file_get_error(file)) { - return qemu_file_get_error(file); + int ret = qemu_fflush(file); + if (ret) { + return ret; } return size + sizeof(size); @@ -297,14 +351,17 @@ struct RAMSrcPageRequest { /* State of RAM for migration */ struct RAMState { - /* QEMUFile used for this migration */ - QEMUFile *f; + /* + * PageSearchStatus structures for the channels when send pages. + * Protected by the bitmap_mutex. + */ + PageSearchStatus pss[RAM_CHANNEL_MAX]; /* UFFD file descriptor, used in 'write-tracking' migration */ int uffdio_fd; + /* total ram size in bytes */ + uint64_t ram_bytes_total; /* Last block that we have visited searching for dirty pages */ RAMBlock *last_seen_block; - /* Last block from where we have sent data */ - RAMBlock *last_sent_block; /* Last dirty target page we have sent */ ram_addr_t last_page; /* last ram version we have seen */ @@ -324,17 +381,10 @@ struct RAMState { uint64_t xbzrle_pages_prev; /* Amount of xbzrle encoded bytes since the beginning of the period */ uint64_t xbzrle_bytes_prev; - /* Start using XBZRLE (e.g., after the first round). */ - bool xbzrle_enabled; + /* Are we really using XBZRLE (e.g., after the first round). */ + bool xbzrle_started; /* Are we on the last stage of migration */ bool last_stage; - /* compression statistics since the beginning of the period */ - /* amount of count that no free thread to compress data */ - uint64_t compress_thread_busy_prev; - /* amount bytes after compression */ - uint64_t compressed_size_prev; - /* amount of compressed pages */ - uint64_t compress_pages_prev; /* total handled target pages at the beginning of period */ uint64_t target_page_count_prev; @@ -342,13 +392,26 @@ struct RAMState { uint64_t target_page_count; /* number of dirty bits in the bitmap */ uint64_t migration_dirty_pages; - /* Protects modification of the bitmap and migration dirty pages */ + /* + * Protects: + * - dirty/clear bitmap + * - migration_dirty_pages + * - pss structures + */ QemuMutex bitmap_mutex; /* The RAMBlock used in the last src_page_requests */ RAMBlock *last_req_rb; /* Queue of outstanding page requests from the destination */ QemuMutex src_page_req_mutex; QSIMPLEQ_HEAD(, RAMSrcPageRequest) src_page_requests; + + /* + * This is only used when postcopy is in recovery phase, to communicate + * between the migration thread and the return path thread on dirty + * bitmap synchronizations. This field is unused in other stages of + * RAM migration. + */ + unsigned int postcopy_bmap_sync_requested; }; typedef struct RAMState RAMState; @@ -381,9 +444,8 @@ int precopy_notify(PrecopyNotifyReason reason, Error **errp) { PrecopyNotifyData pnd; pnd.reason = reason; - pnd.errp = errp; - return notifier_with_return_list_notify(&precopy_notifier_list, &pnd); + return notifier_with_return_list_notify(&precopy_notifier_list, &pnd, errp); } uint64_t ram_bytes_remaining(void) @@ -392,197 +454,42 @@ uint64_t ram_bytes_remaining(void) 0; } -MigrationStats ram_counters; - -static void ram_transferred_add(uint64_t bytes) +void ram_transferred_add(uint64_t bytes) { if (runstate_is_running()) { - ram_counters.precopy_bytes += bytes; + stat64_add(&mig_stats.precopy_bytes, bytes); } else if (migration_in_postcopy()) { - ram_counters.postcopy_bytes += bytes; + stat64_add(&mig_stats.postcopy_bytes, bytes); } else { - ram_counters.downtime_bytes += bytes; + stat64_add(&mig_stats.downtime_bytes, bytes); } - ram_counters.transferred += bytes; } -/* used by the search for pages to send */ -struct PageSearchStatus { - /* Current block being searched */ - RAMBlock *block; - /* Current page to search from */ - unsigned long page; - /* Set once we wrap around */ - bool complete_round; - /* Whether current page is explicitly requested by postcopy */ - bool postcopy_requested; +struct MigrationOps { + int (*ram_save_target_page)(RAMState *rs, PageSearchStatus *pss); }; -typedef struct PageSearchStatus PageSearchStatus; +typedef struct MigrationOps MigrationOps; -CompressionStats compression_counters; +MigrationOps *migration_ops; -struct CompressParam { - bool done; - bool quit; - bool zero_page; - QEMUFile *file; - QemuMutex mutex; - QemuCond cond; - RAMBlock *block; - ram_addr_t offset; +static int ram_save_host_page_urgent(PageSearchStatus *pss); - /* internally used fields */ - z_stream stream; - uint8_t *originbuf; -}; -typedef struct CompressParam CompressParam; +/* NOTE: page is the PFN not real ram_addr_t. */ +static void pss_init(PageSearchStatus *pss, RAMBlock *rb, ram_addr_t page) +{ + pss->block = rb; + pss->page = page; + pss->complete_round = false; +} -struct DecompressParam { - bool done; - bool quit; - QemuMutex mutex; - QemuCond cond; - void *des; - uint8_t *compbuf; - int len; - z_stream stream; -}; -typedef struct DecompressParam DecompressParam; - -static CompressParam *comp_param; -static QemuThread *compress_threads; -/* comp_done_cond is used to wake up the migration thread when - * one of the compression threads has finished the compression. - * comp_done_lock is used to co-work with comp_done_cond. +/* + * Check whether two PSSs are actively sending the same page. Return true + * if it is, false otherwise. */ -static QemuMutex comp_done_lock; -static QemuCond comp_done_cond; -/* The empty QEMUFileOps will be used by file in CompressParam */ -static const QEMUFileOps empty_ops = { }; - -static QEMUFile *decomp_file; -static DecompressParam *decomp_param; -static QemuThread *decompress_threads; -static QemuMutex decomp_done_lock; -static QemuCond decomp_done_cond; - -static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, - ram_addr_t offset, uint8_t *source_buf); - -static void *do_data_compress(void *opaque) +static bool pss_overlap(PageSearchStatus *pss1, PageSearchStatus *pss2) { - CompressParam *param = opaque; - RAMBlock *block; - ram_addr_t offset; - bool zero_page; - - qemu_mutex_lock(¶m->mutex); - while (!param->quit) { - if (param->block) { - block = param->block; - offset = param->offset; - param->block = NULL; - qemu_mutex_unlock(¶m->mutex); - - zero_page = do_compress_ram_page(param->file, ¶m->stream, - block, offset, param->originbuf); - - qemu_mutex_lock(&comp_done_lock); - param->done = true; - param->zero_page = zero_page; - qemu_cond_signal(&comp_done_cond); - qemu_mutex_unlock(&comp_done_lock); - - qemu_mutex_lock(¶m->mutex); - } else { - qemu_cond_wait(¶m->cond, ¶m->mutex); - } - } - qemu_mutex_unlock(¶m->mutex); - - return NULL; -} - -static void compress_threads_save_cleanup(void) -{ - int i, thread_count; - - if (!migrate_use_compression() || !comp_param) { - return; - } - - thread_count = migrate_compress_threads(); - for (i = 0; i < thread_count; i++) { - /* - * we use it as a indicator which shows if the thread is - * properly init'd or not - */ - if (!comp_param[i].file) { - break; - } - - qemu_mutex_lock(&comp_param[i].mutex); - comp_param[i].quit = true; - qemu_cond_signal(&comp_param[i].cond); - qemu_mutex_unlock(&comp_param[i].mutex); - - qemu_thread_join(compress_threads + i); - qemu_mutex_destroy(&comp_param[i].mutex); - qemu_cond_destroy(&comp_param[i].cond); - deflateEnd(&comp_param[i].stream); - g_free(comp_param[i].originbuf); - qemu_fclose(comp_param[i].file); - comp_param[i].file = NULL; - } - qemu_mutex_destroy(&comp_done_lock); - qemu_cond_destroy(&comp_done_cond); - g_free(compress_threads); - g_free(comp_param); - compress_threads = NULL; - comp_param = NULL; -} - -static int compress_threads_save_setup(void) -{ - int i, thread_count; - - if (!migrate_use_compression()) { - return 0; - } - thread_count = migrate_compress_threads(); - compress_threads = g_new0(QemuThread, thread_count); - comp_param = g_new0(CompressParam, thread_count); - qemu_cond_init(&comp_done_cond); - qemu_mutex_init(&comp_done_lock); - for (i = 0; i < thread_count; i++) { - comp_param[i].originbuf = g_try_malloc(TARGET_PAGE_SIZE); - if (!comp_param[i].originbuf) { - goto exit; - } - - if (deflateInit(&comp_param[i].stream, - migrate_compress_level()) != Z_OK) { - g_free(comp_param[i].originbuf); - goto exit; - } - - /* comp_param[i].file is just used as a dummy buffer to save data, - * set its ops to empty. - */ - comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops, false); - comp_param[i].done = true; - comp_param[i].quit = false; - qemu_mutex_init(&comp_param[i].mutex); - qemu_cond_init(&comp_param[i].cond); - qemu_thread_create(compress_threads + i, "compress", - do_data_compress, comp_param + i, - QEMU_THREAD_JOINABLE); - } - return 0; - -exit: - compress_threads_save_cleanup(); - return -1; + return pss1->host_page_sending && pss2->host_page_sending && + (pss1->host_page_start == pss2->host_page_start); } /** @@ -592,28 +499,29 @@ exit: * * Returns the number of bytes written * - * @f: QEMUFile where to send the data + * @pss: current PSS channel status * @block: block that contains the page we want to send * @offset: offset inside the block for the page * in the lower bits, it contains flags */ -static size_t save_page_header(RAMState *rs, QEMUFile *f, RAMBlock *block, - ram_addr_t offset) +static size_t save_page_header(PageSearchStatus *pss, QEMUFile *f, + RAMBlock *block, ram_addr_t offset) { size_t size, len; + bool same_block = (block == pss->last_sent_block); - if (block == rs->last_sent_block) { + if (same_block) { offset |= RAM_SAVE_FLAG_CONTINUE; } qemu_put_be64(f, offset); size = 8; - if (!(offset & RAM_SAVE_FLAG_CONTINUE)) { + if (!same_block) { len = strlen(block->idstr); qemu_put_byte(f, len); qemu_put_buffer(f, (uint8_t *)block->idstr, len); size += 1 + len; - rs->last_sent_block = block; + pss->last_sent_block = block; } return size; } @@ -630,11 +538,10 @@ static size_t save_page_header(RAMState *rs, QEMUFile *f, RAMBlock *block, static void mig_throttle_guest_down(uint64_t bytes_dirty_period, uint64_t bytes_dirty_threshold) { - MigrationState *s = migrate_get_current(); - uint64_t pct_initial = s->parameters.cpu_throttle_initial; - uint64_t pct_increment = s->parameters.cpu_throttle_increment; - bool pct_tailslow = s->parameters.cpu_throttle_tailslow; - int pct_max = s->parameters.max_cpu_throttle; + uint64_t pct_initial = migrate_cpu_throttle_initial(); + uint64_t pct_increment = migrate_cpu_throttle_increment(); + bool pct_tailslow = migrate_cpu_throttle_tailslow(); + int pct_max = migrate_max_cpu_throttle(); uint64_t throttle_now = cpu_throttle_get_percentage(); uint64_t cpu_now, cpu_ideal, throttle_inc; @@ -664,13 +571,12 @@ void mig_throttle_counter_reset(void) rs->time_last_bitmap_sync = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); rs->num_dirty_pages_period = 0; - rs->bytes_xfer_prev = ram_counters.transferred; + rs->bytes_xfer_prev = migration_transferred_bytes(); } /** * xbzrle_cache_zero_page: insert a zero page in the XBZRLE cache * - * @rs: current RAM state * @current_addr: address for the zero page * * Update the xbzrle cache to reflect a page that's been sent as all 0. @@ -679,16 +585,12 @@ void mig_throttle_counter_reset(void) * As a bonus, if the page wasn't in the cache it gets added so that * when a small write is made into the 0'd page it gets XBZRLE sent. */ -static void xbzrle_cache_zero_page(RAMState *rs, ram_addr_t current_addr) +static void xbzrle_cache_zero_page(ram_addr_t current_addr) { - if (!rs->xbzrle_enabled) { - return; - } - /* We don't care if this fails to allocate a new cache page * as long as it updated an old one */ cache_insert(XBZRLE.cache, current_addr, XBZRLE.zero_target_page, - ram_counters.dirty_sync_count); + stat64_get(&mig_stats.dirty_sync_count)); } #define ENCODING_FLAG_XBZRLE 0x1 @@ -701,24 +603,26 @@ static void xbzrle_cache_zero_page(RAMState *rs, ram_addr_t current_addr) * -1 means that xbzrle would be longer than normal * * @rs: current RAM state + * @pss: current PSS channel * @current_data: pointer to the address of the page contents * @current_addr: addr of the page * @block: block that contains the page we want to send * @offset: offset inside the block for the page */ -static int save_xbzrle_page(RAMState *rs, uint8_t **current_data, - ram_addr_t current_addr, RAMBlock *block, - ram_addr_t offset) +static int save_xbzrle_page(RAMState *rs, PageSearchStatus *pss, + uint8_t **current_data, ram_addr_t current_addr, + RAMBlock *block, ram_addr_t offset) { int encoded_len = 0, bytes_xbzrle; uint8_t *prev_cached_page; + QEMUFile *file = pss->pss_channel; + uint64_t generation = stat64_get(&mig_stats.dirty_sync_count); - if (!cache_is_cached(XBZRLE.cache, current_addr, - ram_counters.dirty_sync_count)) { + if (!cache_is_cached(XBZRLE.cache, current_addr, generation)) { xbzrle_counters.cache_miss++; if (!rs->last_stage) { if (cache_insert(XBZRLE.cache, current_addr, *current_data, - ram_counters.dirty_sync_count) == -1) { + generation) == -1) { return -1; } else { /* update *current_data when the page has been @@ -776,11 +680,11 @@ static int save_xbzrle_page(RAMState *rs, uint8_t **current_data, } /* Send XBZRLE based compressed page */ - bytes_xbzrle = save_page_header(rs, rs->f, block, + bytes_xbzrle = save_page_header(pss, pss->pss_channel, block, offset | RAM_SAVE_FLAG_XBZRLE); - qemu_put_byte(rs->f, ENCODING_FLAG_XBZRLE); - qemu_put_be16(rs->f, encoded_len); - qemu_put_buffer(rs->f, XBZRLE.encoded_buf, encoded_len); + qemu_put_byte(file, ENCODING_FLAG_XBZRLE); + qemu_put_be16(file, encoded_len); + qemu_put_buffer(file, XBZRLE.encoded_buf, encoded_len); bytes_xbzrle += encoded_len + 1 + 2; /* * Like compressed_size (please see update_compress_thread_counts), @@ -794,26 +698,38 @@ static int save_xbzrle_page(RAMState *rs, uint8_t **current_data, } /** - * migration_bitmap_find_dirty: find the next dirty page from start + * pss_find_next_dirty: find the next dirty page of current ramblock * - * Returns the page offset within memory region of the start of a dirty page + * This function updates pss->page to point to the next dirty page index + * within the ramblock to migrate, or the end of ramblock when nothing + * found. Note that when pss->host_page_sending==true it means we're + * during sending a host page, so we won't look for dirty page that is + * outside the host page boundary. * - * @rs: current RAM state - * @rb: RAMBlock where to search for dirty pages - * @start: page where we start the search + * @pss: the current page search status */ -static inline -unsigned long migration_bitmap_find_dirty(RAMState *rs, RAMBlock *rb, - unsigned long start) +static void pss_find_next_dirty(PageSearchStatus *pss) { + RAMBlock *rb = pss->block; unsigned long size = rb->used_length >> TARGET_PAGE_BITS; unsigned long *bitmap = rb->bmap; - if (ramblock_is_ignored(rb)) { - return size; + if (migrate_ram_is_ignored(rb)) { + /* Points directly to the end, so we know no dirty page */ + pss->page = size; + return; } - return find_next_bit(bitmap, size, start); + /* + * If during sending a host page, only look for dirty pages within the + * current host page being send. + */ + if (pss->host_page_sending) { + assert(pss->host_page_end); + size = MIN(size, pss->host_page_end); + } + + pss->page = find_next_bit(bitmap, size, pss->page); } static void migration_clear_memory_region_dirty_bitmap(RAMBlock *rb, @@ -882,7 +798,7 @@ unsigned long colo_bitmap_find_dirty(RAMState *rs, RAMBlock *rb, *num = 0; - if (ramblock_is_ignored(rb)) { + if (migrate_ram_is_ignored(rb)) { return size; } @@ -1028,24 +944,25 @@ uint64_t ram_pagesize_summary(void) uint64_t ram_get_total_transferred_pages(void) { - return ram_counters.normal + ram_counters.duplicate + - compression_counters.pages + xbzrle_counters.pages; + return stat64_get(&mig_stats.normal_pages) + + stat64_get(&mig_stats.zero_pages) + + compress_ram_pages() + xbzrle_counters.pages; } static void migration_update_rates(RAMState *rs, int64_t end_time) { uint64_t page_count = rs->target_page_count - rs->target_page_count_prev; - double compressed_size; /* calculate period counters */ - ram_counters.dirty_pages_rate = rs->num_dirty_pages_period * 1000 - / (end_time - rs->time_last_bitmap_sync); + stat64_set(&mig_stats.dirty_pages_rate, + rs->num_dirty_pages_period * 1000 / + (end_time - rs->time_last_bitmap_sync)); if (!page_count) { return; } - if (migrate_use_xbzrle()) { + if (migrate_xbzrle()) { double encoded_size, unencoded_size; xbzrle_counters.cache_miss_rate = (double)(xbzrle_counters.cache_miss - @@ -1062,77 +979,95 @@ static void migration_update_rates(RAMState *rs, int64_t end_time) rs->xbzrle_pages_prev = xbzrle_counters.pages; rs->xbzrle_bytes_prev = xbzrle_counters.bytes; } + compress_update_rates(page_count); +} - if (migrate_use_compression()) { - compression_counters.busy_rate = (double)(compression_counters.busy - - rs->compress_thread_busy_prev) / page_count; - rs->compress_thread_busy_prev = compression_counters.busy; +/* + * Enable dirty-limit to throttle down the guest + */ +static void migration_dirty_limit_guest(void) +{ + /* + * dirty page rate quota for all vCPUs fetched from + * migration parameter 'vcpu_dirty_limit' + */ + static int64_t quota_dirtyrate; + MigrationState *s = migrate_get_current(); - compressed_size = compression_counters.compressed_size - - rs->compressed_size_prev; - if (compressed_size) { - double uncompressed_size = (compression_counters.pages - - rs->compress_pages_prev) * TARGET_PAGE_SIZE; - - /* Compression-Ratio = Uncompressed-size / Compressed-size */ - compression_counters.compression_rate = - uncompressed_size / compressed_size; - - rs->compress_pages_prev = compression_counters.pages; - rs->compressed_size_prev = compression_counters.compressed_size; - } + /* + * If dirty limit already enabled and migration parameter + * vcpu-dirty-limit untouched. + */ + if (dirtylimit_in_service() && + quota_dirtyrate == s->parameters.vcpu_dirty_limit) { + return; } + + quota_dirtyrate = s->parameters.vcpu_dirty_limit; + + /* + * Set all vCPU a quota dirtyrate, note that the second + * parameter will be ignored if setting all vCPU for the vm + */ + qmp_set_vcpu_dirty_limit(false, -1, quota_dirtyrate, NULL); + trace_migration_dirty_limit_guest(quota_dirtyrate); } 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 threshold = migrate_throttle_trigger_threshold(); + uint64_t bytes_xfer_period = + migration_transferred_bytes() - 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 (blk_mig_bulk_active()) { + return; + } - if ((bytes_dirty_period > bytes_dirty_threshold) && - (++rs->dirty_rate_high_cnt >= 2)) { + /* + * 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)) { + rs->dirty_rate_high_cnt = 0; + if (migrate_auto_converge()) { trace_migration_throttle(); - rs->dirty_rate_high_cnt = 0; mig_throttle_guest_down(bytes_dirty_period, bytes_dirty_threshold); + } else if (migrate_dirty_limit()) { + migration_dirty_limit_guest(); } } } -static void migration_bitmap_sync(RAMState *rs) +static void migration_bitmap_sync(RAMState *rs, bool last_stage) { RAMBlock *block; int64_t end_time; - ram_counters.dirty_sync_count++; + stat64_add(&mig_stats.dirty_sync_count, 1); if (!rs->time_last_bitmap_sync) { rs->time_last_bitmap_sync = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); } trace_migration_bitmap_sync_start(); - memory_global_dirty_log_sync(); + memory_global_dirty_log_sync(last_stage); qemu_mutex_lock(&rs->bitmap_mutex); WITH_RCU_READ_LOCK_GUARD() { RAMBLOCK_FOREACH_NOT_IGNORED(block) { ramblock_sync_dirty_bitmap(rs, block); } - ram_counters.remaining = ram_bytes_remaining(); + stat64_set(&mig_stats.dirty_bytes_last_sync, ram_bytes_remaining()); } qemu_mutex_unlock(&rs->bitmap_mutex); @@ -1152,14 +1087,15 @@ 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 = ram_counters.transferred; + rs->bytes_xfer_prev = migration_transferred_bytes(); } - if (migrate_use_events()) { - qapi_event_send_migration_pass(ram_counters.dirty_sync_count); + if (migrate_events()) { + uint64_t generation = stat64_get(&mig_stats.dirty_sync_count); + qapi_event_send_migration_pass(generation); } } -static void migration_bitmap_sync_precopy(RAMState *rs) +static void migration_bitmap_sync_precopy(RAMState *rs, bool last_stage) { Error *local_err = NULL; @@ -1172,14 +1108,14 @@ static void migration_bitmap_sync_precopy(RAMState *rs) local_err = NULL; } - migration_bitmap_sync(rs); + migration_bitmap_sync(rs, last_stage); if (precopy_notify(PRECOPY_NOTIFY_AFTER_BITMAP_SYNC, &local_err)) { error_report_err(local_err); } } -static void ram_release_page(const char *rbname, uint64_t offset) +void ram_release_page(const char *rbname, uint64_t offset) { if (!migrate_release_ram() || !migration_in_postcopy()) { return; @@ -1188,51 +1124,55 @@ static void ram_release_page(const char *rbname, uint64_t offset) ram_discard_range(rbname, offset, TARGET_PAGE_SIZE); } -/** - * save_zero_page_to_file: send the zero page to the file - * - * Returns the size of data written to the file, 0 means the page is not - * a zero page - * - * @rs: current RAM state - * @file: the file where the data is saved - * @block: block that contains the page we want to send - * @offset: offset inside the block for the page - */ -static int save_zero_page_to_file(RAMState *rs, QEMUFile *file, - RAMBlock *block, ram_addr_t offset) -{ - uint8_t *p = block->host + offset; - int len = 0; - - if (buffer_is_zero(p, TARGET_PAGE_SIZE)) { - len += save_page_header(rs, file, block, offset | RAM_SAVE_FLAG_ZERO); - qemu_put_byte(file, 0); - len += 1; - ram_release_page(block->idstr, offset); - } - return len; -} - /** * save_zero_page: send the zero page to the stream * * Returns the number of pages written. * * @rs: current RAM state - * @block: block that contains the page we want to send + * @pss: current PSS channel * @offset: offset inside the block for the page */ -static int save_zero_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) +static int save_zero_page(RAMState *rs, PageSearchStatus *pss, + ram_addr_t offset) { - int len = save_zero_page_to_file(rs, rs->f, block, offset); + uint8_t *p = pss->block->host + offset; + QEMUFile *file = pss->pss_channel; + int len = 0; - if (len) { - ram_counters.duplicate++; - ram_transferred_add(len); + if (migrate_zero_page_detection() == ZERO_PAGE_DETECTION_NONE) { + return 0; + } + + if (!buffer_is_zero(p, TARGET_PAGE_SIZE)) { + return 0; + } + + stat64_add(&mig_stats.zero_pages, 1); + + if (migrate_mapped_ram()) { + /* zero pages are not transferred with mapped-ram */ + clear_bit_atomic(offset >> TARGET_PAGE_BITS, pss->block->file_bmap); return 1; } - return -1; + + len += save_page_header(pss, file, pss->block, offset | RAM_SAVE_FLAG_ZERO); + qemu_put_byte(file, 0); + len += 1; + ram_release_page(pss->block->idstr, offset); + ram_transferred_add(len); + + /* + * Must let xbzrle know, otherwise a previous (now 0'd) cached + * page would be stale. + */ + if (rs->xbzrle_started) { + XBZRLE_cache_lock(); + xbzrle_cache_zero_page(pss->block->offset + offset); + XBZRLE_cache_unlock(); + } + + return len; } /* @@ -1242,34 +1182,22 @@ static int save_zero_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) * * Return true if the pages has been saved, otherwise false is returned. */ -static bool control_save_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, - int *pages) +static bool control_save_page(PageSearchStatus *pss, + ram_addr_t offset, int *pages) { - uint64_t bytes_xmit = 0; int ret; - *pages = -1; - ret = ram_control_save_page(rs->f, block->offset, offset, TARGET_PAGE_SIZE, - &bytes_xmit); + ret = rdma_control_save_page(pss->pss_channel, pss->block->offset, offset, + TARGET_PAGE_SIZE); if (ret == RAM_SAVE_CONTROL_NOT_SUPP) { return false; } - if (bytes_xmit) { - ram_transferred_add(bytes_xmit); - *pages = 1; - } - if (ret == RAM_SAVE_CONTROL_DELAYED) { + *pages = 1; return true; } - - if (bytes_xmit > 0) { - ram_counters.normal++; - } else if (bytes_xmit == 0) { - ram_counters.duplicate++; - } - + *pages = ret; return true; } @@ -1278,26 +1206,34 @@ static bool control_save_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, * * Returns the number of pages written. * - * @rs: current RAM state + * @pss: current PSS channel * @block: block that contains the page we want to send * @offset: offset inside the block for the page * @buf: the page to be sent * @async: send to page asyncly */ -static int save_normal_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, - uint8_t *buf, bool async) +static int save_normal_page(PageSearchStatus *pss, RAMBlock *block, + ram_addr_t offset, uint8_t *buf, bool async) { - ram_transferred_add(save_page_header(rs, rs->f, block, - offset | RAM_SAVE_FLAG_PAGE)); - if (async) { - qemu_put_buffer_async(rs->f, buf, TARGET_PAGE_SIZE, - migrate_release_ram() && - migration_in_postcopy()); + QEMUFile *file = pss->pss_channel; + + if (migrate_mapped_ram()) { + qemu_put_buffer_at(file, buf, TARGET_PAGE_SIZE, + block->pages_offset + offset); + set_bit(offset >> TARGET_PAGE_BITS, block->file_bmap); } else { - qemu_put_buffer(rs->f, buf, TARGET_PAGE_SIZE); + ram_transferred_add(save_page_header(pss, pss->pss_channel, block, + offset | RAM_SAVE_FLAG_PAGE)); + if (async) { + qemu_put_buffer_async(file, buf, TARGET_PAGE_SIZE, + migrate_release_ram() && + migration_in_postcopy()); + } else { + qemu_put_buffer(file, buf, TARGET_PAGE_SIZE); + } } ram_transferred_add(TARGET_PAGE_SIZE); - ram_counters.normal++; + stat64_add(&mig_stats.normal_pages, 1); return 1; } @@ -1326,9 +1262,9 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss) trace_ram_save_page(block->idstr, (uint64_t)offset, p); XBZRLE_cache_lock(); - if (rs->xbzrle_enabled && !migration_in_postcopy()) { - pages = save_xbzrle_page(rs, &p, current_addr, block, - offset); + if (rs->xbzrle_started && !migration_in_postcopy()) { + pages = save_xbzrle_page(rs, pss, &p, current_addr, + block, offset); if (!rs->last_stage) { /* Can't send this cached data async, since the cache page * might get updated before it gets to the wire @@ -1339,7 +1275,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss) /* XBZRLE overflow or normal page */ if (pages == -1) { - pages = save_normal_page(rs, block, offset, p, send_async); + pages = save_normal_page(pss, block, offset, p, send_async); } XBZRLE_cache_unlock(); @@ -1347,160 +1283,80 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss) return pages; } -static int ram_save_multifd_page(RAMState *rs, RAMBlock *block, - ram_addr_t offset) +static int ram_save_multifd_page(RAMBlock *block, ram_addr_t offset) { - if (multifd_queue_page(rs->f, block, offset) < 0) { + if (!multifd_queue_page(block, offset)) { return -1; } - ram_counters.normal++; return 1; } -static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, - ram_addr_t offset, uint8_t *source_buf) +int compress_send_queued_data(CompressParam *param) { - RAMState *rs = ram_state; - uint8_t *p = block->host + offset; - int ret; + PageSearchStatus *pss = &ram_state->pss[RAM_CHANNEL_PRECOPY]; + MigrationState *ms = migrate_get_current(); + QEMUFile *file = ms->to_dst_file; + int len = 0; - if (save_zero_page_to_file(rs, f, block, offset)) { - return true; + RAMBlock *block = param->block; + ram_addr_t offset = param->offset; + + if (param->result == RES_NONE) { + return 0; } - save_page_header(rs, f, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE); + assert(block == pss->last_sent_block); - /* - * copy it to a internal buffer to avoid it being modified by VM - * so that we can catch up the error during compression and - * decompression - */ - memcpy(source_buf, p, TARGET_PAGE_SIZE); - ret = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE); - if (ret < 0) { - qemu_file_set_error(migrate_get_current()->to_dst_file, ret); - error_report("compressed data failed!"); + if (param->result == RES_ZEROPAGE) { + assert(qemu_file_buffer_empty(param->file)); + len += save_page_header(pss, file, block, offset | RAM_SAVE_FLAG_ZERO); + qemu_put_byte(file, 0); + len += 1; + ram_release_page(block->idstr, offset); + } else if (param->result == RES_COMPRESS) { + assert(!qemu_file_buffer_empty(param->file)); + len += save_page_header(pss, file, block, + offset | RAM_SAVE_FLAG_COMPRESS_PAGE); + len += qemu_put_qemu_file(file, param->file); + } else { + abort(); } - return false; -} - -static void -update_compress_thread_counts(const CompressParam *param, int bytes_xmit) -{ - ram_transferred_add(bytes_xmit); - - if (param->zero_page) { - ram_counters.duplicate++; - return; - } - - /* 8 means a header with RAM_SAVE_FLAG_CONTINUE. */ - compression_counters.compressed_size += bytes_xmit - 8; - compression_counters.pages++; -} - -static bool save_page_use_compression(RAMState *rs); - -static void flush_compressed_data(RAMState *rs) -{ - int idx, len, thread_count; - - if (!save_page_use_compression(rs)) { - return; - } - thread_count = migrate_compress_threads(); - - qemu_mutex_lock(&comp_done_lock); - for (idx = 0; idx < thread_count; idx++) { - while (!comp_param[idx].done) { - qemu_cond_wait(&comp_done_cond, &comp_done_lock); - } - } - qemu_mutex_unlock(&comp_done_lock); - - for (idx = 0; idx < thread_count; idx++) { - qemu_mutex_lock(&comp_param[idx].mutex); - if (!comp_param[idx].quit) { - len = qemu_put_qemu_file(rs->f, comp_param[idx].file); - /* - * it's safe to fetch zero_page without holding comp_done_lock - * as there is no further request submitted to the thread, - * i.e, the thread should be waiting for a request at this point. - */ - update_compress_thread_counts(&comp_param[idx], len); - } - qemu_mutex_unlock(&comp_param[idx].mutex); - } -} - -static inline void set_compress_params(CompressParam *param, RAMBlock *block, - ram_addr_t offset) -{ - param->block = block; - param->offset = offset; -} - -static int compress_page_with_multi_thread(RAMState *rs, RAMBlock *block, - ram_addr_t offset) -{ - int idx, thread_count, bytes_xmit = -1, pages = -1; - bool wait = migrate_compress_wait_thread(); - - thread_count = migrate_compress_threads(); - qemu_mutex_lock(&comp_done_lock); -retry: - for (idx = 0; idx < thread_count; idx++) { - if (comp_param[idx].done) { - comp_param[idx].done = false; - bytes_xmit = qemu_put_qemu_file(rs->f, comp_param[idx].file); - qemu_mutex_lock(&comp_param[idx].mutex); - set_compress_params(&comp_param[idx], block, offset); - qemu_cond_signal(&comp_param[idx].cond); - qemu_mutex_unlock(&comp_param[idx].mutex); - pages = 1; - update_compress_thread_counts(&comp_param[idx], bytes_xmit); - break; - } - } - - /* - * wait for the free thread if the user specifies 'compress-wait-thread', - * otherwise we will post the page out in the main thread as normal page. - */ - if (pages < 0 && wait) { - qemu_cond_wait(&comp_done_cond, &comp_done_lock); - goto retry; - } - qemu_mutex_unlock(&comp_done_lock); - - return pages; + + update_compress_thread_counts(param, len); + + return len; } +#define PAGE_ALL_CLEAN 0 +#define PAGE_TRY_AGAIN 1 +#define PAGE_DIRTY_FOUND 2 /** * find_dirty_block: find the next dirty page and update any state * associated with the search process. * - * Returns true if a page is found + * Returns: + * <0: An error happened + * PAGE_ALL_CLEAN: no dirty page found, give up + * PAGE_TRY_AGAIN: no dirty page found, retry for next block + * PAGE_DIRTY_FOUND: dirty page found * * @rs: current RAM state * @pss: data about the state of the current dirty page scan * @again: set to false if the search has scanned the whole of RAM */ -static bool find_dirty_block(RAMState *rs, PageSearchStatus *pss, bool *again) +static int find_dirty_block(RAMState *rs, PageSearchStatus *pss) { - /* This is not a postcopy requested page */ - pss->postcopy_requested = false; + /* Update pss->page for the next dirty bit in ramblock */ + pss_find_next_dirty(pss); - pss->page = migration_bitmap_find_dirty(rs, pss->block, pss->page); if (pss->complete_round && pss->block == rs->last_seen_block && pss->page >= rs->last_page) { /* * We've been once around the RAM and haven't found anything. * Give up. */ - *again = false; - return false; + return PAGE_ALL_CLEAN; } if (!offset_in_ramblock(pss->block, ((ram_addr_t)pss->page) << TARGET_PAGE_BITS)) { @@ -1508,6 +1364,20 @@ static bool find_dirty_block(RAMState *rs, PageSearchStatus *pss, bool *again) pss->page = 0; pss->block = QLIST_NEXT_RCU(pss->block, next); if (!pss->block) { + if (migrate_multifd() && + (!migrate_multifd_flush_after_each_section() || + migrate_mapped_ram())) { + QEMUFile *f = rs->pss[RAM_CHANNEL_PRECOPY].pss_channel; + int ret = multifd_send_sync_main(); + if (ret < 0) { + return ret; + } + + if (!migrate_mapped_ram()) { + qemu_put_be64(f, RAM_SAVE_FLAG_MULTIFD_FLUSH); + qemu_fflush(f); + } + } /* * If memory migration starts over, we will meet a dirtied page * which may still exists in compression threads's ring, so we @@ -1517,25 +1387,22 @@ static bool find_dirty_block(RAMState *rs, PageSearchStatus *pss, bool *again) * Also If xbzrle is on, stop using the data compression at this * point. In theory, xbzrle can do better than compression. */ - flush_compressed_data(rs); + compress_flush_data(); /* Hit the end of the list */ pss->block = QLIST_FIRST_RCU(&ram_list.blocks); /* Flag that we've looped */ pss->complete_round = true; /* After the first round, enable XBZRLE. */ - if (migrate_use_xbzrle()) { - rs->xbzrle_enabled = true; + if (migrate_xbzrle()) { + rs->xbzrle_started = true; } } /* Didn't find anything this time, but try again on the new block */ - *again = true; - return false; + return PAGE_TRY_AGAIN; } else { - /* Can go around again, but... */ - *again = true; - /* We've found something so probably don't need to */ - return true; + /* We've found something */ + return PAGE_DIRTY_FOUND; } } @@ -1553,7 +1420,6 @@ static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset) { struct RAMSrcPageRequest *entry; RAMBlock *block = NULL; - size_t page_size; if (!postcopy_has_request(rs)) { return NULL; @@ -1570,13 +1436,10 @@ static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset) entry = QSIMPLEQ_FIRST(&rs->src_page_requests); block = entry->rb; *offset = entry->offset; - page_size = qemu_ram_pagesize(block); - /* Each page request should only be multiple page size of the ramblock */ - assert((entry->len % page_size) == 0); - if (entry->len > page_size) { - entry->len -= page_size; - entry->offset += page_size; + if (entry->len > TARGET_PAGE_SIZE) { + entry->len -= TARGET_PAGE_SIZE; + entry->offset += TARGET_PAGE_SIZE; } else { memory_region_unref(block->mr); QSIMPLEQ_REMOVE_HEAD(&rs->src_page_requests, next_req); @@ -1584,9 +1447,6 @@ static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset) migration_consume_urgent_request(); } - trace_unqueue_page(block->idstr, *offset, - test_bit((*offset >> TARGET_PAGE_BITS), block->bmap)); - return block; } @@ -1644,7 +1504,7 @@ static int ram_save_release_protection(RAMState *rs, PageSearchStatus *pss, uint64_t run_length = (pss->page - start_page) << TARGET_PAGE_BITS; /* Flush async buffers before un-protect. */ - qemu_fflush(rs->f); + qemu_fflush(pss->pss_channel); /* Un-protect memory range. */ res = uffd_change_protection(rs->uffdio_fd, page_address, run_length, false, false); @@ -1713,13 +1573,15 @@ out: static inline void populate_read_range(RAMBlock *block, ram_addr_t offset, ram_addr_t size) { + const ram_addr_t end = offset + size; + /* * We read one byte of each page; this will preallocate page tables if * required and populate the shared zeropage on MAP_PRIVATE anonymous memory * where no page was populated yet. This might require adaption when * supporting other mappings, like shmem. */ - for (; offset < size; offset += block->page_size) { + for (; offset < end; offset += block->page_size) { char tmp = *((char *)block->host + offset); /* Don't optimize the read out */ @@ -1802,6 +1664,39 @@ void ram_write_tracking_prepare(void) } } +static inline int uffd_protect_section(MemoryRegionSection *section, + void *opaque) +{ + const hwaddr size = int128_get64(section->size); + const hwaddr offset = section->offset_within_region; + RAMBlock *rb = section->mr->ram_block; + int uffd_fd = (uintptr_t)opaque; + + return uffd_change_protection(uffd_fd, rb->host + offset, size, true, + false); +} + +static int ram_block_uffd_protect(RAMBlock *rb, int uffd_fd) +{ + assert(rb->flags & RAM_UF_WRITEPROTECT); + + /* See ram_block_populate_read() */ + if (rb->mr && memory_region_has_ram_discard_manager(rb->mr)) { + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(rb->mr); + MemoryRegionSection section = { + .mr = rb->mr, + .offset_within_region = 0, + .size = rb->mr->size, + }; + + return ram_discard_manager_replay_populated(rdm, §ion, + uffd_protect_section, + (void *)(uintptr_t)uffd_fd); + } + return uffd_change_protection(uffd_fd, rb->host, + rb->used_length, true, false); +} + /* * ram_write_tracking_start: start UFFD-WP memory tracking * @@ -1833,14 +1728,14 @@ int ram_write_tracking_start(void) block->max_length, UFFDIO_REGISTER_MODE_WP, NULL)) { goto fail; } - /* Apply UFFD write protection to the block memory range */ - if (uffd_change_protection(rs->uffdio_fd, block->host, - block->max_length, true, false)) { - goto fail; - } block->flags |= RAM_UF_WRITEPROTECT; memory_region_ref(block->mr); + /* Apply UFFD write protection to the block memory range */ + if (ram_block_uffd_protect(block, uffd_fd)) { + goto fail; + } + trace_ram_write_tracking_ramblock_start(block->idstr, block->page_size, block->host, block->max_length); } @@ -1854,12 +1749,6 @@ fail: if ((block->flags & RAM_UF_WRITEPROTECT) == 0) { continue; } - /* - * In case some memory block failed to be write-protected - * remove protection and unregister all succeeded RAM blocks - */ - uffd_change_protection(rs->uffdio_fd, block->host, block->max_length, - false, false); uffd_unregister_memory(rs->uffdio_fd, block->host, block->max_length); /* Cleanup flags and remove reference */ block->flags &= ~RAM_UF_WRITEPROTECT; @@ -1885,9 +1774,6 @@ void ram_write_tracking_stop(void) if ((block->flags & RAM_UF_WRITEPROTECT) == 0) { continue; } - /* Remove protection and unregister all affected RAM blocks */ - uffd_change_protection(rs->uffdio_fd, block->host, block->max_length, - false, false); uffd_unregister_memory(rs->uffdio_fd, block->host, block->max_length); trace_ram_write_tracking_ramblock_stop(block->idstr, block->page_size, @@ -1961,8 +1847,30 @@ static bool get_queued_page(RAMState *rs, PageSearchStatus *pss) { RAMBlock *block; ram_addr_t offset; + bool dirty; - block = unqueue_page(rs, &offset); + do { + block = unqueue_page(rs, &offset); + /* + * We're sending this page, and since it's postcopy nothing else + * will dirty it, and we must make sure it doesn't get sent again + * even if this queue request was received after the background + * search already sent it. + */ + if (block) { + unsigned long page; + + page = offset >> TARGET_PAGE_BITS; + dirty = test_bit(page, block->bmap); + if (!dirty) { + trace_get_queued_page_not_dirty(block->idstr, (uint64_t)offset, + page); + } else { + trace_get_queued_page(block->idstr, (uint64_t)offset, page); + } + } + + } while (block && !dirty); if (!block) { /* @@ -1986,7 +1894,6 @@ static bool get_queued_page(RAMState *rs, PageSearchStatus *pss) * really rare. */ pss->complete_round = false; - pss->postcopy_requested = true; } return !!block; @@ -2026,12 +1933,13 @@ static void migration_page_queue_free(RAMState *rs) * @start: starting address from the start of the RAMBlock * @len: length (in bytes) to send */ -int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) +int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len, + Error **errp) { RAMBlock *ramblock; RAMState *rs = ram_state; - ram_counters.postcopy_requests++; + stat64_add(&mig_stats.postcopy_requests, 1); RCU_READ_LOCK_GUARD(); if (!rbname) { @@ -2043,7 +1951,7 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) * Shouldn't happen, we can't reuse the last RAMBlock if * it's the 1st request. */ - error_report("ram_save_queue_pages no previous block"); + error_setg(errp, "MIG_RP_MSG_REQ_PAGES has no previous block"); return -1; } } else { @@ -2051,19 +1959,70 @@ 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); + error_setg(errp, "MIG_RP_MSG_REQ_PAGES has no block '%s'", rbname); return -1; } rs->last_req_rb = ramblock; } trace_ram_save_queue_pages(ramblock->idstr, start, len); if (!offset_in_ramblock(ramblock, start + len - 1)) { - error_report("%s request overrun start=" RAM_ADDR_FMT " len=" - RAM_ADDR_FMT " blocklen=" RAM_ADDR_FMT, - __func__, start, len, ramblock->used_length); + error_setg(errp, "MIG_RP_MSG_REQ_PAGES request overrun, " + "start=" RAM_ADDR_FMT " len=" + RAM_ADDR_FMT " blocklen=" RAM_ADDR_FMT, + start, len, ramblock->used_length); return -1; } + /* + * When with postcopy preempt, we send back the page directly in the + * rp-return thread. + */ + if (postcopy_preempt_active()) { + ram_addr_t page_start = start >> TARGET_PAGE_BITS; + size_t page_size = qemu_ram_pagesize(ramblock); + PageSearchStatus *pss = &ram_state->pss[RAM_CHANNEL_POSTCOPY]; + int ret = 0; + + qemu_mutex_lock(&rs->bitmap_mutex); + + pss_init(pss, ramblock, page_start); + /* + * Always use the preempt channel, and make sure it's there. It's + * safe to access without lock, because when rp-thread is running + * we should be the only one who operates on the qemufile + */ + pss->pss_channel = migrate_get_current()->postcopy_qemufile_src; + assert(pss->pss_channel); + + /* + * It must be either one or multiple of host page size. Just + * assert; if something wrong we're mostly split brain anyway. + */ + assert(len % page_size == 0); + while (len) { + if (ram_save_host_page_urgent(pss)) { + error_setg(errp, "ram_save_host_page_urgent() failed: " + "ramblock=%s, start_addr=0x"RAM_ADDR_FMT, + ramblock->idstr, start); + ret = -1; + break; + } + /* + * NOTE: after ram_save_host_page_urgent() succeeded, pss->page + * will automatically be moved and point to the next host page + * we're going to send, so no need to update here. + * + * Normally QEMU never sends >1 host page in requests, so + * logically we don't even need that as the loop should only + * run once, but just to be consistent. + */ + len -= page_size; + }; + qemu_mutex_unlock(&rs->bitmap_mutex); + + return ret; + } + struct RAMSrcPageRequest *new_entry = g_new0(struct RAMSrcPageRequest, 1); new_entry->rb = ramblock; @@ -2079,32 +2038,15 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) return 0; } -static bool save_page_use_compression(RAMState *rs) -{ - if (!migrate_use_compression()) { - return false; - } - - /* - * If xbzrle is enabled (e.g., after first round of migration), stop - * using the data compression. In theory, xbzrle can do better than - * compression. - */ - if (rs->xbzrle_enabled) { - return false; - } - - return true; -} - /* * try to compress the page before posting it out, return true if the page * has been properly handled by compression, otherwise needs other * paths to handle it */ -static bool save_compress_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) +static bool save_compress_page(RAMState *rs, PageSearchStatus *pss, + ram_addr_t offset) { - if (!save_page_use_compression(rs)) { + if (!migrate_compress()) { return false; } @@ -2118,68 +2060,173 @@ static bool save_compress_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) * We post the fist page as normal page as compression will take * much CPU resource. */ - if (block != rs->last_sent_block) { - flush_compressed_data(rs); + if (pss->block != pss->last_sent_block) { + compress_flush_data(); return false; } - if (compress_page_with_multi_thread(rs, block, offset) > 0) { - return true; - } - - compression_counters.busy++; - return false; + return compress_page_with_multi_thread(pss->block, offset, + compress_send_queued_data); } /** - * ram_save_target_page: save one target page + * ram_save_target_page_legacy: save one target page * * Returns the number of pages written * * @rs: current RAM state * @pss: data about the page we want to send */ -static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss) +static int ram_save_target_page_legacy(RAMState *rs, PageSearchStatus *pss) { - RAMBlock *block = pss->block; ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS; int res; - if (control_save_page(rs, block, offset, &res)) { + if (control_save_page(pss, offset, &res)) { return res; } - if (save_compress_page(rs, block, offset)) { + if (save_compress_page(rs, pss, offset)) { return 1; } - res = save_zero_page(rs, block, offset); - if (res > 0) { - /* Must let xbzrle know, otherwise a previous (now 0'd) cached - * page would be stale - */ - if (!save_page_use_compression(rs)) { - XBZRLE_cache_lock(); - xbzrle_cache_zero_page(rs, block->offset + offset); - XBZRLE_cache_unlock(); - } - return res; - } - - /* - * 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() - && !migration_in_postcopy()) { - return ram_save_multifd_page(rs, block, offset); + if (save_zero_page(rs, pss, offset)) { + return 1; } return ram_save_page(rs, pss); } +/** + * ram_save_target_page_multifd: send one target page to multifd workers + * + * Returns 1 if the page was queued, -1 otherwise. + * + * @rs: current RAM state + * @pss: data about the page we want to send + */ +static int ram_save_target_page_multifd(RAMState *rs, PageSearchStatus *pss) +{ + RAMBlock *block = pss->block; + ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS; + + /* + * While using multifd live migration, we still need to handle zero + * page checking on the migration main thread. + */ + if (migrate_zero_page_detection() == ZERO_PAGE_DETECTION_LEGACY) { + if (save_zero_page(rs, pss, offset)) { + return 1; + } + } + + return ram_save_multifd_page(block, offset); +} + +/* Should be called before sending a host page */ +static void pss_host_page_prepare(PageSearchStatus *pss) +{ + /* How many guest pages are there in one host page? */ + size_t guest_pfns = qemu_ram_pagesize(pss->block) >> TARGET_PAGE_BITS; + + pss->host_page_sending = true; + if (guest_pfns <= 1) { + /* + * This covers both when guest psize == host psize, or when guest + * has larger psize than the host (guest_pfns==0). + * + * For the latter, we always send one whole guest page per + * iteration of the host page (example: an Alpha VM on x86 host + * will have guest psize 8K while host psize 4K). + */ + pss->host_page_start = pss->page; + pss->host_page_end = pss->page + 1; + } else { + /* + * The host page spans over multiple guest pages, we send them + * within the same host page iteration. + */ + pss->host_page_start = ROUND_DOWN(pss->page, guest_pfns); + pss->host_page_end = ROUND_UP(pss->page + 1, guest_pfns); + } +} + +/* + * Whether the page pointed by PSS is within the host page being sent. + * Must be called after a previous pss_host_page_prepare(). + */ +static bool pss_within_range(PageSearchStatus *pss) +{ + ram_addr_t ram_addr; + + assert(pss->host_page_sending); + + /* Over host-page boundary? */ + if (pss->page >= pss->host_page_end) { + return false; + } + + ram_addr = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS; + + return offset_in_ramblock(pss->block, ram_addr); +} + +static void pss_host_page_finish(PageSearchStatus *pss) +{ + pss->host_page_sending = false; + /* This is not needed, but just to reset it */ + pss->host_page_start = pss->host_page_end = 0; +} + +/* + * Send an urgent host page specified by `pss'. Need to be called with + * bitmap_mutex held. + * + * Returns 0 if save host page succeeded, false otherwise. + */ +static int ram_save_host_page_urgent(PageSearchStatus *pss) +{ + bool page_dirty, sent = false; + RAMState *rs = ram_state; + int ret = 0; + + trace_postcopy_preempt_send_host_page(pss->block->idstr, pss->page); + pss_host_page_prepare(pss); + + /* + * If precopy is sending the same page, let it be done in precopy, or + * we could send the same page in two channels and none of them will + * receive the whole page. + */ + if (pss_overlap(pss, &ram_state->pss[RAM_CHANNEL_PRECOPY])) { + trace_postcopy_preempt_hit(pss->block->idstr, + pss->page << TARGET_PAGE_BITS); + return 0; + } + + do { + page_dirty = migration_bitmap_clear_dirty(rs, pss->block, pss->page); + + if (page_dirty) { + /* Be strict to return code; it must be 1, or what else? */ + if (migration_ops->ram_save_target_page(rs, pss) != 1) { + error_report_once("%s: ram_save_target_page failed", __func__); + ret = -1; + goto out; + } + sent = true; + } + pss_find_next_dirty(pss); + } while (pss_within_range(pss)); +out: + pss_host_page_finish(pss); + /* For urgent requests, flush immediately if sent */ + if (sent) { + qemu_fflush(pss->pss_channel); + } + return ret; +} + /** * ram_save_host_page: save a whole host page * @@ -2188,9 +2235,14 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss) * a host page in which case the remainder of the hostpage is sent. * Only dirty target pages are sent. Note that the host page size may * be a huge page for this block. + * * The saving stops at the boundary of the used_length of the block * if the RAMBlock isn't a multiple of the host page size. * + * The caller must be with ram_state.bitmap_mutex held to call this + * function. Note that this function can temporarily release the lock, but + * when the function is returned it'll make sure the lock is still held. + * * Returns the number of pages written or negative on error * * @rs: current RAM state @@ -2198,42 +2250,61 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss) */ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) { + bool page_dirty, preempt_active = postcopy_preempt_active(); int tmppages, pages = 0; size_t pagesize_bits = qemu_ram_pagesize(pss->block) >> TARGET_PAGE_BITS; - unsigned long hostpage_boundary = - QEMU_ALIGN_UP(pss->page + 1, pagesize_bits); unsigned long start_page = pss->page; int res; - if (ramblock_is_ignored(pss->block)) { + if (migrate_ram_is_ignored(pss->block)) { error_report("block %s should not be migrated !", pss->block->idstr); return 0; } - do { - /* Check the pages is dirty and if it is send it */ - if (migration_bitmap_clear_dirty(rs, pss->block, pss->page)) { - tmppages = ram_save_target_page(rs, pss); - if (tmppages < 0) { - return tmppages; - } + /* Update host page boundary information */ + pss_host_page_prepare(pss); - pages += tmppages; + do { + page_dirty = migration_bitmap_clear_dirty(rs, pss->block, pss->page); + + /* Check the pages is dirty and if it is send it */ + if (page_dirty) { /* - * Allow rate limiting to happen in the middle of huge pages if - * something is sent in the current iteration. + * Properly yield the lock only in postcopy preempt mode + * because both migration thread and rp-return thread can + * operate on the bitmaps. */ - if (pagesize_bits > 1 && tmppages > 0) { - migration_rate_limit(); + if (preempt_active) { + qemu_mutex_unlock(&rs->bitmap_mutex); } + tmppages = migration_ops->ram_save_target_page(rs, pss); + if (tmppages >= 0) { + pages += tmppages; + /* + * Allow rate limiting to happen in the middle of huge pages if + * something is sent in the current iteration. + */ + if (pagesize_bits > 1 && tmppages > 0) { + migration_rate_limit(); + } + } + if (preempt_active) { + qemu_mutex_lock(&rs->bitmap_mutex); + } + } else { + tmppages = 0; } - pss->page = migration_bitmap_find_dirty(rs, pss->block, pss->page); - } while ((pss->page < hostpage_boundary) && - offset_in_ramblock(pss->block, - ((ram_addr_t)pss->page) << TARGET_PAGE_BITS)); - /* The offset we leave with is the min boundary of host page and block */ - pss->page = MIN(pss->page, hostpage_boundary); + + if (tmppages < 0) { + pss_host_page_finish(pss); + return tmppages; + } + + pss_find_next_dirty(pss); + } while (pss_within_range(pss)); + + pss_host_page_finish(pss); res = ram_save_release_protection(rs, pss, start_page); return (res < 0 ? res : pages); @@ -2254,78 +2325,79 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) */ static int ram_find_and_save_block(RAMState *rs) { - PageSearchStatus pss; + PageSearchStatus *pss = &rs->pss[RAM_CHANNEL_PRECOPY]; int pages = 0; - bool again, found; /* No dirty page as there is zero RAM */ - if (!ram_bytes_total()) { + if (!rs->ram_bytes_total) { return pages; } - pss.block = rs->last_seen_block; - pss.page = rs->last_page; - pss.complete_round = false; - - if (!pss.block) { - pss.block = QLIST_FIRST_RCU(&ram_list.blocks); + /* + * Always keep last_seen_block/last_page valid during this procedure, + * because find_dirty_block() relies on these values (e.g., we compare + * last_seen_block with pss.block to see whether we searched all the + * ramblocks) to detect the completion of migration. Having NULL value + * of last_seen_block can conditionally cause below loop to run forever. + */ + if (!rs->last_seen_block) { + rs->last_seen_block = QLIST_FIRST_RCU(&ram_list.blocks); + rs->last_page = 0; } - do { - again = true; - found = get_queued_page(rs, &pss); + pss_init(pss, rs->last_seen_block, rs->last_page); - if (!found) { + while (true){ + if (!get_queued_page(rs, pss)) { /* priority queue empty, so just search for something dirty */ - found = find_dirty_block(rs, &pss, &again); + int res = find_dirty_block(rs, pss); + if (res != PAGE_DIRTY_FOUND) { + if (res == PAGE_ALL_CLEAN) { + break; + } else if (res == PAGE_TRY_AGAIN) { + continue; + } else if (res < 0) { + pages = res; + break; + } + } } - - if (found) { - pages = ram_save_host_page(rs, &pss); + pages = ram_save_host_page(rs, pss); + if (pages) { + break; } - } while (!pages && again); + } - rs->last_seen_block = pss.block; - rs->last_page = pss.page; + rs->last_seen_block = pss->block; + rs->last_page = pss->page; return pages; } -void acct_update_position(QEMUFile *f, size_t size, bool zero) -{ - uint64_t pages = size / TARGET_PAGE_SIZE; - - if (zero) { - ram_counters.duplicate += pages; - } else { - ram_counters.normal += pages; - ram_transferred_add(size); - qemu_update_position(f, size); - } -} - -static uint64_t ram_bytes_total_common(bool count_ignored) +static uint64_t ram_bytes_total_with_ignored(void) { RAMBlock *block; uint64_t total = 0; RCU_READ_LOCK_GUARD(); - if (count_ignored) { - RAMBLOCK_FOREACH_MIGRATABLE(block) { - total += block->used_length; - } - } else { - RAMBLOCK_FOREACH_NOT_IGNORED(block) { - total += block->used_length; - } + RAMBLOCK_FOREACH_MIGRATABLE(block) { + total += block->used_length; } return total; } uint64_t ram_bytes_total(void) { - return ram_bytes_total_common(false); + RAMBlock *block; + uint64_t total = 0; + + RCU_READ_LOCK_GUARD(); + + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + total += block->used_length; + } + return total; } static void xbzrle_load_setup(void) @@ -2373,7 +2445,7 @@ static void ram_save_cleanup(void *opaque) /* We don't use dirty log with background snapshots */ if (!migrate_background_snapshot()) { - /* caller have hold iothread lock or is in a bh, so there is + /* caller have hold BQL or is in a bh, so there is * no writing race against the migration bitmap */ if (global_dirty_tracking & GLOBAL_DIRTY_MIGRATION) { @@ -2396,15 +2468,22 @@ static void ram_save_cleanup(void *opaque) xbzrle_cleanup(); compress_threads_save_cleanup(); ram_state_cleanup(rsp); + g_free(migration_ops); + migration_ops = NULL; } static void ram_state_reset(RAMState *rs) { + int i; + + for (i = 0; i < RAM_CHANNEL_MAX; i++) { + rs->pss[i].last_sent_block = NULL; + } + rs->last_seen_block = NULL; - rs->last_sent_block = NULL; rs->last_page = 0; rs->last_version = ram_list.version; - rs->xbzrle_enabled = false; + rs->xbzrle_started = false; } #define MAX_WAIT 50 /* ms, half buffered_file limit */ @@ -2590,11 +2669,11 @@ void ram_postcopy_send_discard_bitmap(MigrationState *ms) RCU_READ_LOCK_GUARD(); /* This should be our last sync, the src is now paused */ - migration_bitmap_sync(rs); + migration_bitmap_sync(rs, false); /* Easiest way to make sure we don't resume in the middle of a host-page */ + rs->pss[RAM_CHANNEL_PRECOPY].last_sent_block = NULL; rs->last_seen_block = NULL; - rs->last_sent_block = NULL; rs->last_page = 0; postcopy_each_ram_send_discard(ms); @@ -2644,7 +2723,7 @@ static int xbzrle_init(void) { Error *local_err = NULL; - if (!migrate_use_xbzrle()) { + if (!migrate_xbzrle()) { return 0; } @@ -2705,13 +2784,14 @@ static int ram_state_init(RAMState **rsp) qemu_mutex_init(&(*rsp)->bitmap_mutex); qemu_mutex_init(&(*rsp)->src_page_req_mutex); QSIMPLEQ_INIT(&(*rsp)->src_page_requests); + (*rsp)->ram_bytes_total = ram_bytes_total(); /* * Count the total number of pages used by ram blocks not including any * gaps due to alignment or unplugs. * This must match with the initial values of dirty bitmap. */ - (*rsp)->migration_dirty_pages = ram_bytes_total() >> TARGET_PAGE_BITS; + (*rsp)->migration_dirty_pages = (*rsp)->ram_bytes_total >> TARGET_PAGE_BITS; ram_state_reset(*rsp); return 0; @@ -2750,6 +2830,9 @@ static void ram_list_init_bitmaps(void) */ block->bmap = bitmap_new(pages); bitmap_set(block->bmap, 0, pages); + if (migrate_mapped_ram()) { + block->file_bmap = bitmap_new(pages); + } block->clear_bmap_shift = shift; block->clear_bmap = bitmap_new(clear_bmap_size(pages, shift)); } @@ -2771,8 +2854,6 @@ static void migration_bitmap_clear_discarded_pages(RAMState *rs) static void ram_init_bitmaps(RAMState *rs) { - /* For memory_global_dirty_log_start below. */ - qemu_mutex_lock_iothread(); qemu_mutex_lock_ramlist(); WITH_RCU_READ_LOCK_GUARD() { @@ -2780,11 +2861,10 @@ static void ram_init_bitmaps(RAMState *rs) /* We don't use dirty log with background snapshots */ if (!migrate_background_snapshot()) { memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION); - migration_bitmap_sync_precopy(rs); + migration_bitmap_sync_precopy(rs, false); } } qemu_mutex_unlock_ramlist(); - qemu_mutex_unlock_iothread(); /* * After an eventual first bitmap sync, fixup the initial bitmap @@ -2831,7 +2911,7 @@ static void ram_state_resume_prepare(RAMState *rs, QEMUFile *out) ram_state_reset(rs); /* Update RAMState cache of output QEMUFile */ - rs->f = out; + rs->pss[RAM_CHANNEL_PRECOPY].pss_channel = out; trace_ram_state_resume_prepare(pages); } @@ -2847,10 +2927,9 @@ void qemu_guest_free_page_hint(void *addr, size_t len) RAMBlock *block; ram_addr_t offset; size_t used_len, start, npages; - MigrationState *s = migrate_get_current(); /* This function is currently expected to be used during live migration */ - if (!migration_is_setup_or_active(s->state)) { + if (!migration_is_setup_or_active()) { return; } @@ -2890,6 +2969,89 @@ void qemu_guest_free_page_hint(void *addr, size_t len) } } +#define MAPPED_RAM_HDR_VERSION 1 +struct MappedRamHeader { + uint32_t version; + /* + * The target's page size, so we know how many pages are in the + * bitmap. + */ + uint64_t page_size; + /* + * The offset in the migration file where the pages bitmap is + * stored. + */ + uint64_t bitmap_offset; + /* + * The offset in the migration file where the actual pages (data) + * are stored. + */ + uint64_t pages_offset; +} QEMU_PACKED; +typedef struct MappedRamHeader MappedRamHeader; + +static void mapped_ram_setup_ramblock(QEMUFile *file, RAMBlock *block) +{ + g_autofree MappedRamHeader *header = NULL; + size_t header_size, bitmap_size; + long num_pages; + + header = g_new0(MappedRamHeader, 1); + header_size = sizeof(MappedRamHeader); + + num_pages = block->used_length >> TARGET_PAGE_BITS; + bitmap_size = BITS_TO_LONGS(num_pages) * sizeof(unsigned long); + + /* + * Save the file offsets of where the bitmap and the pages should + * go as they are written at the end of migration and during the + * iterative phase, respectively. + */ + block->bitmap_offset = qemu_get_offset(file) + header_size; + block->pages_offset = ROUND_UP(block->bitmap_offset + + bitmap_size, + MAPPED_RAM_FILE_OFFSET_ALIGNMENT); + + header->version = cpu_to_be32(MAPPED_RAM_HDR_VERSION); + header->page_size = cpu_to_be64(TARGET_PAGE_SIZE); + header->bitmap_offset = cpu_to_be64(block->bitmap_offset); + header->pages_offset = cpu_to_be64(block->pages_offset); + + qemu_put_buffer(file, (uint8_t *) header, header_size); + + /* prepare offset for next ramblock */ + qemu_set_offset(file, block->pages_offset + block->used_length, SEEK_SET); +} + +static bool mapped_ram_read_header(QEMUFile *file, MappedRamHeader *header, + Error **errp) +{ + size_t ret, header_size = sizeof(MappedRamHeader); + + ret = qemu_get_buffer(file, (uint8_t *)header, header_size); + if (ret != header_size) { + error_setg(errp, "Could not read whole mapped-ram migration header " + "(expected %zd, got %zd bytes)", header_size, ret); + return false; + } + + /* migration stream is big-endian */ + header->version = be32_to_cpu(header->version); + + if (header->version > MAPPED_RAM_HDR_VERSION) { + error_setg(errp, "Migration mapped-ram capability version not " + "supported (expected <= %d, got %d)", MAPPED_RAM_HDR_VERSION, + header->version); + return false; + } + + header->page_size = be64_to_cpu(header->page_size); + header->bitmap_offset = be64_to_cpu(header->bitmap_offset); + header->pages_offset = be64_to_cpu(header->pages_offset); + + return true; +} + /* * Each of ram_save_setup, ram_save_iterate and ram_save_complete has * long-running RCU critical section. When rcu-reclaims in the code @@ -2909,7 +3071,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) { RAMState **rsp = opaque; RAMBlock *block; - int ret; + int ret, max_hg_page_size; if (compress_threads_save_setup()) { return -1; @@ -2922,37 +3084,101 @@ static int ram_save_setup(QEMUFile *f, void *opaque) return -1; } } - (*rsp)->f = f; + (*rsp)->pss[RAM_CHANNEL_PRECOPY].pss_channel = f; + + /* + * ??? Mirrors the previous value of qemu_host_page_size, + * but is this really what was intended for the migration? + */ + max_hg_page_size = MAX(qemu_real_host_page_size(), TARGET_PAGE_SIZE); WITH_RCU_READ_LOCK_GUARD() { - qemu_put_be64(f, ram_bytes_total_common(true) | RAM_SAVE_FLAG_MEM_SIZE); + qemu_put_be64(f, ram_bytes_total_with_ignored() + | RAM_SAVE_FLAG_MEM_SIZE); RAMBLOCK_FOREACH_MIGRATABLE(block) { qemu_put_byte(f, strlen(block->idstr)); qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); qemu_put_be64(f, block->used_length); - if (migrate_postcopy_ram() && block->page_size != - qemu_host_page_size) { + if (migrate_postcopy_ram() && + block->page_size != max_hg_page_size) { qemu_put_be64(f, block->page_size); } if (migrate_ignore_shared()) { qemu_put_be64(f, block->mr->addr); } + + if (migrate_mapped_ram()) { + mapped_ram_setup_ramblock(f, block); + } } } - ram_control_before_iterate(f, RAM_CONTROL_SETUP); - ram_control_after_iterate(f, RAM_CONTROL_SETUP); + ret = rdma_registration_start(f, RAM_CONTROL_SETUP); + if (ret < 0) { + qemu_file_set_error(f, ret); + return ret; + } - ret = multifd_send_sync_main(f); + ret = rdma_registration_stop(f, RAM_CONTROL_SETUP); + if (ret < 0) { + qemu_file_set_error(f, ret); + return ret; + } + + migration_ops = g_malloc0(sizeof(MigrationOps)); + + if (migrate_multifd()) { + migration_ops->ram_save_target_page = ram_save_target_page_multifd; + } else { + migration_ops->ram_save_target_page = ram_save_target_page_legacy; + } + + bql_unlock(); + ret = multifd_send_sync_main(); + bql_lock(); if (ret < 0) { return ret; } - qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - qemu_fflush(f); + if (migrate_multifd() && !migrate_multifd_flush_after_each_section() + && !migrate_mapped_ram()) { + qemu_put_be64(f, RAM_SAVE_FLAG_MULTIFD_FLUSH); + } - return 0; + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + return qemu_fflush(f); +} + +static void ram_save_file_bmap(QEMUFile *f) +{ + RAMBlock *block; + + RAMBLOCK_FOREACH_MIGRATABLE(block) { + long num_pages = block->used_length >> TARGET_PAGE_BITS; + long bitmap_size = BITS_TO_LONGS(num_pages) * sizeof(unsigned long); + + qemu_put_buffer_at(f, (uint8_t *)block->file_bmap, bitmap_size, + block->bitmap_offset); + ram_transferred_add(bitmap_size); + + /* + * Free the bitmap here to catch any synchronization issues + * with multifd channels. No channels should be sending pages + * after we've written the bitmap to file. + */ + g_free(block->file_bmap); + block->file_bmap = NULL; + } +} + +void ramblock_set_file_bmap_atomic(RAMBlock *block, ram_addr_t offset, bool set) +{ + if (set) { + set_bit_atomic(offset >> TARGET_PAGE_BITS, block->file_bmap); + } else { + clear_bit_atomic(offset >> TARGET_PAGE_BITS, block->file_bmap); + } } /** @@ -2986,87 +3212,95 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) * MAX_WAIT (if curious, further see commit 4508bd9ed8053ce) below, which * guarantees that we'll at least released it in a regular basis. */ - qemu_mutex_lock(&rs->bitmap_mutex); - WITH_RCU_READ_LOCK_GUARD() { - if (ram_list.version != rs->last_version) { - ram_state_reset(rs); - } - - /* Read version before ram_list.blocks */ - smp_rmb(); - - ram_control_before_iterate(f, RAM_CONTROL_ROUND); - - t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - i = 0; - while ((ret = qemu_file_rate_limit(f)) == 0 || - postcopy_has_request(rs)) { - int pages; - - if (qemu_file_get_error(f)) { - break; + WITH_QEMU_LOCK_GUARD(&rs->bitmap_mutex) { + WITH_RCU_READ_LOCK_GUARD() { + if (ram_list.version != rs->last_version) { + ram_state_reset(rs); } - pages = ram_find_and_save_block(rs); - /* no more pages to sent */ - if (pages == 0) { - done = 1; - break; + /* Read version before ram_list.blocks */ + smp_rmb(); + + ret = rdma_registration_start(f, RAM_CONTROL_ROUND); + if (ret < 0) { + qemu_file_set_error(f, ret); + goto out; } - if (pages < 0) { - qemu_file_set_error(f, pages); - break; - } + t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + i = 0; + while ((ret = migration_rate_exceeded(f)) == 0 || + postcopy_has_request(rs)) { + int pages; - 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. - * qemu_clock_get_ns() is a bit expensive, so we only check each - * some iterations - */ - if ((i & 63) == 0) { - uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) / - 1000000; - if (t1 > MAX_WAIT) { - trace_ram_save_iterate_big_wait(t1, i); + if (qemu_file_get_error(f)) { break; } + + pages = ram_find_and_save_block(rs); + /* no more pages to sent */ + if (pages == 0) { + done = 1; + break; + } + + if (pages < 0) { + qemu_file_set_error(f, pages); + break; + } + + 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()) { + compress_flush_data(); + } + + /* + * we want to check in the 1st loop, just in case it was the 1st + * time and we had to sync the dirty bitmap. + * qemu_clock_get_ns() is a bit expensive, so we only check each + * some iterations + */ + if ((i & 63) == 0) { + uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) / + 1000000; + if (t1 > MAX_WAIT) { + trace_ram_save_iterate_big_wait(t1, i); + break; + } + } + i++; } - i++; } } - qemu_mutex_unlock(&rs->bitmap_mutex); /* * Must occur before EOS (or any QEMUFile operation) * because of RDMA protocol. */ - ram_control_after_iterate(f, RAM_CONTROL_ROUND); + ret = rdma_registration_stop(f, RAM_CONTROL_ROUND); + if (ret < 0) { + qemu_file_set_error(f, ret); + } out: if (ret >= 0 - && migration_is_setup_or_active(migrate_get_current()->state)) { - ret = multifd_send_sync_main(rs->f); - if (ret < 0) { - return ret; + && migration_is_setup_or_active()) { + if (migrate_multifd() && migrate_multifd_flush_after_each_section() && + !migrate_mapped_ram()) { + ret = multifd_send_sync_main(); + if (ret < 0) { + return ret; + } } qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - qemu_fflush(f); ram_transferred_add(8); - - ret = qemu_file_get_error(f); + ret = qemu_fflush(f); } if (ret < 0) { return ret; @@ -3080,7 +3314,7 @@ out: * * Returns zero to indicate success or negative on error * - * Called with iothread lock + * Called with the BQL * * @f: QEMUFile where to send the data * @opaque: RAMState pointer @@ -3095,14 +3329,19 @@ static int ram_save_complete(QEMUFile *f, void *opaque) WITH_RCU_READ_LOCK_GUARD() { if (!migration_in_postcopy()) { - migration_bitmap_sync_precopy(rs); + migration_bitmap_sync_precopy(rs, true); } - ram_control_before_iterate(f, RAM_CONTROL_FINISH); + ret = rdma_registration_start(f, RAM_CONTROL_FINISH); + if (ret < 0) { + qemu_file_set_error(f, ret); + return ret; + } /* try transferring iterative blocks of memory */ /* flush all remaining blocks regardless of rate limiting */ + qemu_mutex_lock(&rs->bitmap_mutex); while (true) { int pages; @@ -3112,56 +3351,84 @@ static int ram_save_complete(QEMUFile *f, void *opaque) break; } if (pages < 0) { - ret = pages; - break; + qemu_mutex_unlock(&rs->bitmap_mutex); + return pages; } } + qemu_mutex_unlock(&rs->bitmap_mutex); - flush_compressed_data(rs); - ram_control_after_iterate(f, RAM_CONTROL_FINISH); + compress_flush_data(); + + ret = rdma_registration_stop(f, RAM_CONTROL_FINISH); + if (ret < 0) { + qemu_file_set_error(f, ret); + return ret; + } } + ret = multifd_send_sync_main(); if (ret < 0) { return ret; } - ret = multifd_send_sync_main(rs->f); - if (ret < 0) { - return ret; + if (migrate_mapped_ram()) { + ram_save_file_bmap(f); + + if (qemu_file_get_error(f)) { + Error *local_err = NULL; + int err = qemu_file_get_error_obj(f, &local_err); + + error_reportf_err(local_err, "Failed to write bitmap to file: "); + return -err; + } } + if (migrate_multifd() && !migrate_multifd_flush_after_each_section() && + !migrate_mapped_ram()) { + qemu_put_be64(f, RAM_SAVE_FLAG_MULTIFD_FLUSH); + } qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - qemu_fflush(f); - - return 0; + return qemu_fflush(f); } -static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, - uint64_t *res_precopy_only, - uint64_t *res_compatible, - uint64_t *res_postcopy_only) +static void ram_state_pending_estimate(void *opaque, uint64_t *must_precopy, + uint64_t *can_postcopy) +{ + RAMState **temp = opaque; + RAMState *rs = *temp; + + uint64_t remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE; + + if (migrate_postcopy_ram()) { + /* We can do postcopy, and all the data is postcopiable */ + *can_postcopy += remaining_size; + } else { + *must_precopy += remaining_size; + } +} + +static void ram_state_pending_exact(void *opaque, uint64_t *must_precopy, + uint64_t *can_postcopy) { RAMState **temp = opaque; RAMState *rs = *temp; uint64_t remaining_size; - remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE; - - if (!migration_in_postcopy() && - remaining_size < max_size) { - qemu_mutex_lock_iothread(); + if (!migration_in_postcopy()) { + bql_lock(); WITH_RCU_READ_LOCK_GUARD() { - migration_bitmap_sync_precopy(rs); + migration_bitmap_sync_precopy(rs, false); } - qemu_mutex_unlock_iothread(); - remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE; + bql_unlock(); } + remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE; + if (migrate_postcopy_ram()) { /* We can do postcopy, and all the data is postcopiable */ - *res_compatible += remaining_size; + *can_postcopy += remaining_size; } else { - *res_precopy_only += remaining_size; + *must_precopy += remaining_size; } } @@ -3209,11 +3476,13 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host) * @mis: the migration incoming state pointer * @f: QEMUFile where to read the data from * @flags: Page flags (mostly to see if it's a continuation of previous block) + * @channel: the channel we're using */ static inline RAMBlock *ram_block_from_stream(MigrationIncomingState *mis, - QEMUFile *f, int flags) + QEMUFile *f, int flags, + int channel) { - RAMBlock *block = mis->last_recv_block; + RAMBlock *block = mis->last_recv_block[channel]; char id[256]; uint8_t len; @@ -3235,12 +3504,12 @@ static inline RAMBlock *ram_block_from_stream(MigrationIncomingState *mis, return NULL; } - if (ramblock_is_ignored(block)) { + if (migrate_ram_is_ignored(block)) { error_report("block %s should not be migrated !", id); return NULL; } - mis->last_recv_block = block; + mis->last_recv_block[channel] = block; return block; } @@ -3269,6 +3538,18 @@ static ram_addr_t host_page_offset_from_ram_block_offset(RAMBlock *block, return ((uintptr_t)block->host + offset) & (block->page_size - 1); } +void colo_record_bitmap(RAMBlock *block, ram_addr_t *normal, uint32_t pages) +{ + qemu_mutex_lock(&ram_state->bitmap_mutex); + for (int i = 0; i < pages; i++) { + ram_addr_t offset = normal[i]; + ram_state->migration_dirty_pages += !test_and_set_bit( + offset >> TARGET_PAGE_BITS, + block->bmap); + } + qemu_mutex_unlock(&ram_state->bitmap_mutex); +} + static inline void *colo_cache_from_block_offset(RAMBlock *block, ram_addr_t offset, bool record_bitmap) { @@ -3286,15 +3567,14 @@ 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 (record_bitmap && - !test_and_set_bit(offset >> TARGET_PAGE_BITS, block->bmap)) { - ram_state->migration_dirty_pages++; + if (record_bitmap) { + colo_record_bitmap(block, &offset, 1); } return block->colo_cache + offset; } /** - * ram_handle_compressed: handle the zero page case + * ram_handle_zero: handle the zero page case * * If a page (or a whole RDMA chunk) has been * determined to be zero, then zap it. @@ -3303,196 +3583,10 @@ static inline void *colo_cache_from_block_offset(RAMBlock *block, * @ch: what the page is filled from. We only support zero * @size: size of the zero page */ -void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) +void ram_handle_zero(void *host, uint64_t size) { - if (ch != 0 || !buffer_is_zero(host, size)) { - memset(host, ch, size); - } -} - -/* return the size after decompression, or negative value on error */ -static int -qemu_uncompress_data(z_stream *stream, uint8_t *dest, size_t dest_len, - const uint8_t *source, size_t source_len) -{ - int err; - - err = inflateReset(stream); - if (err != Z_OK) { - return -1; - } - - stream->avail_in = source_len; - stream->next_in = (uint8_t *)source; - stream->avail_out = dest_len; - stream->next_out = dest; - - err = inflate(stream, Z_NO_FLUSH); - if (err != Z_STREAM_END) { - return -1; - } - - return stream->total_out; -} - -static void *do_data_decompress(void *opaque) -{ - DecompressParam *param = opaque; - unsigned long pagesize; - uint8_t *des; - int len, ret; - - qemu_mutex_lock(¶m->mutex); - while (!param->quit) { - if (param->des) { - des = param->des; - len = param->len; - param->des = 0; - qemu_mutex_unlock(¶m->mutex); - - pagesize = TARGET_PAGE_SIZE; - - ret = qemu_uncompress_data(¶m->stream, des, pagesize, - param->compbuf, len); - if (ret < 0 && migrate_get_current()->decompress_error_check) { - error_report("decompress data failed"); - qemu_file_set_error(decomp_file, ret); - } - - qemu_mutex_lock(&decomp_done_lock); - param->done = true; - qemu_cond_signal(&decomp_done_cond); - qemu_mutex_unlock(&decomp_done_lock); - - qemu_mutex_lock(¶m->mutex); - } else { - qemu_cond_wait(¶m->cond, ¶m->mutex); - } - } - qemu_mutex_unlock(¶m->mutex); - - return NULL; -} - -static int wait_for_decompress_done(void) -{ - int idx, thread_count; - - if (!migrate_use_compression()) { - return 0; - } - - thread_count = migrate_decompress_threads(); - qemu_mutex_lock(&decomp_done_lock); - for (idx = 0; idx < thread_count; idx++) { - while (!decomp_param[idx].done) { - qemu_cond_wait(&decomp_done_cond, &decomp_done_lock); - } - } - qemu_mutex_unlock(&decomp_done_lock); - return qemu_file_get_error(decomp_file); -} - -static void compress_threads_load_cleanup(void) -{ - int i, thread_count; - - if (!migrate_use_compression()) { - return; - } - thread_count = migrate_decompress_threads(); - for (i = 0; i < thread_count; i++) { - /* - * we use it as a indicator which shows if the thread is - * properly init'd or not - */ - if (!decomp_param[i].compbuf) { - break; - } - - qemu_mutex_lock(&decomp_param[i].mutex); - decomp_param[i].quit = true; - qemu_cond_signal(&decomp_param[i].cond); - qemu_mutex_unlock(&decomp_param[i].mutex); - } - for (i = 0; i < thread_count; i++) { - if (!decomp_param[i].compbuf) { - break; - } - - qemu_thread_join(decompress_threads + i); - qemu_mutex_destroy(&decomp_param[i].mutex); - qemu_cond_destroy(&decomp_param[i].cond); - inflateEnd(&decomp_param[i].stream); - g_free(decomp_param[i].compbuf); - decomp_param[i].compbuf = NULL; - } - g_free(decompress_threads); - g_free(decomp_param); - decompress_threads = NULL; - decomp_param = NULL; - decomp_file = NULL; -} - -static int compress_threads_load_setup(QEMUFile *f) -{ - int i, thread_count; - - if (!migrate_use_compression()) { - return 0; - } - - thread_count = migrate_decompress_threads(); - decompress_threads = g_new0(QemuThread, thread_count); - decomp_param = g_new0(DecompressParam, thread_count); - qemu_mutex_init(&decomp_done_lock); - qemu_cond_init(&decomp_done_cond); - decomp_file = f; - for (i = 0; i < thread_count; i++) { - if (inflateInit(&decomp_param[i].stream) != Z_OK) { - goto exit; - } - - decomp_param[i].compbuf = g_malloc0(compressBound(TARGET_PAGE_SIZE)); - qemu_mutex_init(&decomp_param[i].mutex); - qemu_cond_init(&decomp_param[i].cond); - decomp_param[i].done = true; - decomp_param[i].quit = false; - qemu_thread_create(decompress_threads + i, "decompress", - do_data_decompress, decomp_param + i, - QEMU_THREAD_JOINABLE); - } - return 0; -exit: - compress_threads_load_cleanup(); - return -1; -} - -static void decompress_data_with_multi_threads(QEMUFile *f, - void *host, int len) -{ - int idx, thread_count; - - thread_count = migrate_decompress_threads(); - QEMU_LOCK_GUARD(&decomp_done_lock); - while (true) { - for (idx = 0; idx < thread_count; idx++) { - if (decomp_param[idx].done) { - decomp_param[idx].done = false; - qemu_mutex_lock(&decomp_param[idx].mutex); - qemu_get_buffer(f, decomp_param[idx].compbuf, len); - decomp_param[idx].des = host; - decomp_param[idx].len = len; - qemu_cond_signal(&decomp_param[idx].cond); - qemu_mutex_unlock(&decomp_param[idx].mutex); - break; - } - } - if (idx < thread_count) { - break; - } else { - qemu_cond_wait(&decomp_done_cond, &decomp_done_lock); - } + if (!buffer_is_zero(host, size)) { + memset(host, 0, size); } } @@ -3539,8 +3633,6 @@ int colo_init_ram_cache(void) * we use the same name 'ram_bitmap' as for migration. */ if (ram_bytes_total()) { - RAMBlock *block; - RAMBLOCK_FOREACH_NOT_IGNORED(block) { unsigned long pages = block->max_length >> TARGET_PAGE_BITS; block->bmap = bitmap_new(pages); @@ -3556,10 +3648,10 @@ void colo_incoming_start_dirty_log(void) { RAMBlock *block = NULL; /* For memory_global_dirty_log_start below. */ - qemu_mutex_lock_iothread(); + bql_lock(); qemu_mutex_lock_ramlist(); - memory_global_dirty_log_sync(); + memory_global_dirty_log_sync(false); WITH_RCU_READ_LOCK_GUARD() { RAMBLOCK_FOREACH_NOT_IGNORED(block) { ramblock_sync_dirty_bitmap(ram_state, block); @@ -3570,7 +3662,7 @@ void colo_incoming_start_dirty_log(void) } ram_state->migration_dirty_pages = 0; qemu_mutex_unlock_ramlist(); - qemu_mutex_unlock_iothread(); + bql_unlock(); } /* It is need to hold the global lock to call this helper */ @@ -3605,10 +3697,6 @@ void colo_release_ram_cache(void) */ static int ram_load_setup(QEMUFile *f, void *opaque) { - if (compress_threads_load_setup(f)) { - return -1; - } - xbzrle_load_setup(); ramblock_recv_map_init(); @@ -3624,7 +3712,6 @@ static int ram_load_cleanup(void *opaque) } xbzrle_load_cleanup(); - compress_threads_load_cleanup(); RAMBLOCK_FOREACH_NOT_IGNORED(rb) { g_free(rb->receivedmap); @@ -3659,15 +3746,15 @@ int ram_postcopy_incoming_init(MigrationIncomingState *mis) * rcu_read_lock is taken prior to this being called. * * @f: QEMUFile where to send the data + * @channel: the channel to use for loading */ -int ram_load_postcopy(QEMUFile *f) +int ram_load_postcopy(QEMUFile *f, int channel) { int flags = 0, ret = 0; bool place_needed = false; bool matches_target_page_size = false; MigrationIncomingState *mis = migration_incoming_get_current(); - /* Currently we only use channel 0. TODO: use all the channels */ - PostcopyTmpPage *tmp_page = &mis->postcopy_tmp_pages[0]; + PostcopyTmpPage *tmp_page = &mis->postcopy_tmp_pages[channel]; while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { ram_addr_t addr; @@ -3691,10 +3778,10 @@ int ram_load_postcopy(QEMUFile *f) flags = addr & ~TARGET_PAGE_MASK; addr &= TARGET_PAGE_MASK; - trace_ram_load_postcopy_loop((uint64_t)addr, flags); + trace_ram_load_postcopy_loop(channel, (uint64_t)addr, flags); if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE | RAM_SAVE_FLAG_COMPRESS_PAGE)) { - block = ram_block_from_stream(mis, f, flags); + block = ram_block_from_stream(mis, f, flags, channel); if (!block) { ret = -EINVAL; break; @@ -3732,10 +3819,10 @@ int ram_load_postcopy(QEMUFile *f) } else if (tmp_page->host_addr != host_page_from_ram_block_offset(block, addr)) { /* not the 1st TP within the HP */ - error_report("Non-same host page detected. " + error_report("Non-same host page detected on channel %d: " "Target host page %p, received host page %p " "(rb %s offset 0x"RAM_ADDR_FMT" target_pages %d)", - tmp_page->host_addr, + channel, tmp_page->host_addr, host_page_from_ram_block_offset(block, addr), block->idstr, addr, tmp_page->target_pages); ret = -EINVAL; @@ -3756,16 +3843,18 @@ int ram_load_postcopy(QEMUFile *f) switch (flags & ~RAM_SAVE_FLAG_CONTINUE) { case RAM_SAVE_FLAG_ZERO: ch = qemu_get_byte(f); + if (ch != 0) { + error_report("Found a zero page with value %d", ch); + ret = -EINVAL; + break; + } /* * 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) { + if (!matches_target_page_size) { memset(page_buffer, ch, TARGET_PAGE_SIZE); } - if (ch) { - tmp_page->all_zero = false; - } break; case RAM_SAVE_FLAG_PAGE: @@ -3796,10 +3885,15 @@ int ram_load_postcopy(QEMUFile *f) } decompress_data_with_multi_threads(f, page_buffer, len); break; - + case RAM_SAVE_FLAG_MULTIFD_FLUSH: + multifd_recv_sync_main(); + break; case RAM_SAVE_FLAG_EOS: /* normal exit */ - multifd_recv_sync_main(); + if (migrate_multifd() && + migrate_multifd_flush_after_each_section()) { + multifd_recv_sync_main(); + } break; default: error_report("Unknown combination of migration flags: 0x%x" @@ -3833,12 +3927,6 @@ int ram_load_postcopy(QEMUFile *f) return ret; } -static bool postcopy_is_advised(void) -{ - PostcopyState ps = postcopy_state_get(); - return ps >= POSTCOPY_INCOMING_ADVISE && ps < POSTCOPY_INCOMING_END; -} - static bool postcopy_is_running(void) { PostcopyState ps = postcopy_state_get(); @@ -3856,7 +3944,8 @@ void colo_flush_ram_cache(void) void *src_host; unsigned long offset = 0; - memory_global_dirty_log_sync(); + memory_global_dirty_log_sync(false); + qemu_mutex_lock(&ram_state->bitmap_mutex); WITH_RCU_READ_LOCK_GUARD() { RAMBLOCK_FOREACH_NOT_IGNORED(block) { ramblock_sync_dirty_bitmap(ram_state, block); @@ -3891,9 +3980,224 @@ void colo_flush_ram_cache(void) } } } + qemu_mutex_unlock(&ram_state->bitmap_mutex); trace_colo_flush_ram_cache_end(); } +static size_t ram_load_multifd_pages(void *host_addr, size_t size, + uint64_t offset) +{ + MultiFDRecvData *data = multifd_get_recv_data(); + + data->opaque = host_addr; + data->file_offset = offset; + data->size = size; + + if (!multifd_recv()) { + return 0; + } + + return size; +} + +static bool read_ramblock_mapped_ram(QEMUFile *f, RAMBlock *block, + long num_pages, unsigned long *bitmap, + Error **errp) +{ + ERRP_GUARD(); + unsigned long set_bit_idx, clear_bit_idx; + ram_addr_t offset; + void *host; + size_t read, unread, size; + + for (set_bit_idx = find_first_bit(bitmap, num_pages); + set_bit_idx < num_pages; + set_bit_idx = find_next_bit(bitmap, num_pages, clear_bit_idx + 1)) { + + clear_bit_idx = find_next_zero_bit(bitmap, num_pages, set_bit_idx + 1); + + unread = TARGET_PAGE_SIZE * (clear_bit_idx - set_bit_idx); + offset = set_bit_idx << TARGET_PAGE_BITS; + + while (unread > 0) { + host = host_from_ram_block_offset(block, offset); + if (!host) { + error_setg(errp, "page outside of ramblock %s range", + block->idstr); + return false; + } + + size = MIN(unread, MAPPED_RAM_LOAD_BUF_SIZE); + + if (migrate_multifd()) { + read = ram_load_multifd_pages(host, size, + block->pages_offset + offset); + } else { + read = qemu_get_buffer_at(f, host, size, + block->pages_offset + offset); + } + + if (!read) { + goto err; + } + offset += read; + unread -= read; + } + } + + return true; + +err: + qemu_file_get_error_obj(f, errp); + error_prepend(errp, "(%s) failed to read page " RAM_ADDR_FMT + "from file offset %" PRIx64 ": ", block->idstr, offset, + block->pages_offset + offset); + return false; +} + +static void parse_ramblock_mapped_ram(QEMUFile *f, RAMBlock *block, + ram_addr_t length, Error **errp) +{ + g_autofree unsigned long *bitmap = NULL; + MappedRamHeader header; + size_t bitmap_size; + long num_pages; + + if (!mapped_ram_read_header(f, &header, errp)) { + return; + } + + block->pages_offset = header.pages_offset; + + /* + * Check the alignment of the file region that contains pages. We + * don't enforce MAPPED_RAM_FILE_OFFSET_ALIGNMENT to allow that + * value to change in the future. Do only a sanity check with page + * size alignment. + */ + if (!QEMU_IS_ALIGNED(block->pages_offset, TARGET_PAGE_SIZE)) { + error_setg(errp, + "Error reading ramblock %s pages, region has bad alignment", + block->idstr); + return; + } + + num_pages = length / header.page_size; + bitmap_size = BITS_TO_LONGS(num_pages) * sizeof(unsigned long); + + bitmap = g_malloc0(bitmap_size); + if (qemu_get_buffer_at(f, (uint8_t *)bitmap, bitmap_size, + header.bitmap_offset) != bitmap_size) { + error_setg(errp, "Error reading dirty bitmap"); + return; + } + + if (!read_ramblock_mapped_ram(f, block, num_pages, bitmap, errp)) { + return; + } + + /* Skip pages array */ + qemu_set_offset(f, block->pages_offset + length, SEEK_SET); + + return; +} + +static int parse_ramblock(QEMUFile *f, RAMBlock *block, ram_addr_t length) +{ + int ret = 0; + /* ADVISE is earlier, it shows the source has the postcopy capability on */ + bool postcopy_advised = migration_incoming_postcopy_advised(); + int max_hg_page_size; + Error *local_err = NULL; + + assert(block); + + if (migrate_mapped_ram()) { + parse_ramblock_mapped_ram(f, block, length, &local_err); + if (local_err) { + error_report_err(local_err); + return -EINVAL; + } + return 0; + } + + if (!qemu_ram_is_migratable(block)) { + error_report("block %s should not be migrated !", block->idstr); + return -EINVAL; + } + + if (length != block->used_length) { + ret = qemu_ram_resize(block, length, &local_err); + if (local_err) { + error_report_err(local_err); + return ret; + } + } + + /* + * ??? Mirrors the previous value of qemu_host_page_size, + * but is this really what was intended for the migration? + */ + max_hg_page_size = MAX(qemu_real_host_page_size(), TARGET_PAGE_SIZE); + + /* For postcopy we need to check hugepage sizes match */ + if (postcopy_advised && migrate_postcopy_ram() && + block->page_size != max_hg_page_size) { + uint64_t remote_page_size = qemu_get_be64(f); + if (remote_page_size != block->page_size) { + error_report("Mismatched RAM page size %s " + "(local) %zd != %" PRId64, block->idstr, + block->page_size, remote_page_size); + return -EINVAL; + } + } + if (migrate_ignore_shared()) { + hwaddr addr = qemu_get_be64(f); + if (migrate_ram_is_ignored(block) && + block->mr->addr != addr) { + error_report("Mismatched GPAs for block %s " + "%" PRId64 "!= %" PRId64, block->idstr, + (uint64_t)addr, (uint64_t)block->mr->addr); + return -EINVAL; + } + } + ret = rdma_block_notification_handle(f, block->idstr); + if (ret < 0) { + qemu_file_set_error(f, ret); + } + + return ret; +} + +static int parse_ramblocks(QEMUFile *f, ram_addr_t total_ram_bytes) +{ + int ret = 0; + + /* Synchronize RAM block list */ + while (!ret && total_ram_bytes) { + RAMBlock *block; + char id[256]; + ram_addr_t length; + int len = qemu_get_byte(f); + + qemu_get_buffer(f, (uint8_t *)id, len); + id[len] = 0; + length = qemu_get_be64(f); + + block = qemu_ram_block_by_name(id); + if (block) { + ret = parse_ramblock(f, block, length); + } else { + error_report("Unknown ramblock \"%s\", cannot accept " + "migration", id); + ret = -EINVAL; + } + total_ram_bytes -= length; + } + + return ret; +} + /** * ram_load_precopy: load pages in precopy case * @@ -3908,14 +4212,19 @@ static int ram_load_precopy(QEMUFile *f) { MigrationIncomingState *mis = migration_incoming_get_current(); int flags = 0, ret = 0, invalid_flags = 0, len = 0, i = 0; - /* ADVISE is earlier, it shows the source has the postcopy capability on */ - bool postcopy_advised = postcopy_is_advised(); - if (!migrate_use_compression()) { + + if (!migrate_compress()) { invalid_flags |= RAM_SAVE_FLAG_COMPRESS_PAGE; } + if (migrate_mapped_ram()) { + invalid_flags |= (RAM_SAVE_FLAG_HOOK | RAM_SAVE_FLAG_MULTIFD_FLUSH | + RAM_SAVE_FLAG_PAGE | RAM_SAVE_FLAG_XBZRLE | + RAM_SAVE_FLAG_ZERO); + } + while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { - ram_addr_t addr, total_ram_bytes; + ram_addr_t addr; void *host = NULL, *host_bak = NULL; uint8_t ch; @@ -3931,10 +4240,18 @@ static int ram_load_precopy(QEMUFile *f) i++; addr = qemu_get_be64(f); + ret = qemu_file_get_error(f); + if (ret) { + error_report("Getting RAM address failed"); + break; + } + flags = addr & ~TARGET_PAGE_MASK; addr &= TARGET_PAGE_MASK; if (flags & invalid_flags) { + error_report("Unexpected RAM flags: %d", flags & invalid_flags); + if (flags & invalid_flags & RAM_SAVE_FLAG_COMPRESS_PAGE) { error_report("Received an unexpected compressed page"); } @@ -3945,7 +4262,8 @@ static int ram_load_precopy(QEMUFile *f) if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE | RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE)) { - RAMBlock *block = ram_block_from_stream(mis, f, flags); + RAMBlock *block = ram_block_from_stream(mis, f, flags, + RAM_CHANNEL_PRECOPY); host = host_from_ram_block_offset(block, addr); /* @@ -3985,70 +4303,27 @@ static int ram_load_precopy(QEMUFile *f) switch (flags & ~RAM_SAVE_FLAG_CONTINUE) { case RAM_SAVE_FLAG_MEM_SIZE: - /* Synchronize RAM block list */ - total_ram_bytes = addr; - while (!ret && total_ram_bytes) { - RAMBlock *block; - char id[256]; - ram_addr_t length; - - len = qemu_get_byte(f); - qemu_get_buffer(f, (uint8_t *)id, len); - id[len] = 0; - length = qemu_get_be64(f); - - block = qemu_ram_block_by_name(id); - if (block && !qemu_ram_is_migratable(block)) { - error_report("block %s should not be migrated !", id); - ret = -EINVAL; - } else if (block) { - if (length != block->used_length) { - Error *local_err = NULL; - - ret = qemu_ram_resize(block, length, - &local_err); - if (local_err) { - error_report_err(local_err); - } - } - /* For postcopy we need to check hugepage sizes match */ - if (postcopy_advised && migrate_postcopy_ram() && - block->page_size != qemu_host_page_size) { - uint64_t remote_page_size = qemu_get_be64(f); - if (remote_page_size != block->page_size) { - error_report("Mismatched RAM page size %s " - "(local) %zd != %" PRId64, - id, block->page_size, - remote_page_size); - ret = -EINVAL; - } - } - if (migrate_ignore_shared()) { - hwaddr addr = qemu_get_be64(f); - if (ramblock_is_ignored(block) && - block->mr->addr != addr) { - error_report("Mismatched GPAs for block %s " - "%" PRId64 "!= %" PRId64, - id, (uint64_t)addr, - (uint64_t)block->mr->addr); - ret = -EINVAL; - } - } - ram_control_load_hook(f, RAM_CONTROL_BLOCK_REG, - block->idstr); - } else { - error_report("Unknown ramblock \"%s\", cannot " - "accept migration", id); - ret = -EINVAL; - } - - total_ram_bytes -= length; + ret = parse_ramblocks(f, addr); + /* + * For mapped-ram migration (to a file) using multifd, we sync + * once and for all here to make sure all tasks we queued to + * multifd threads are completed, so that all the ramblocks + * (including all the guest memory pages within) are fully + * loaded after this sync returns. + */ + if (migrate_mapped_ram()) { + multifd_recv_sync_main(); } break; case RAM_SAVE_FLAG_ZERO: ch = qemu_get_byte(f); - ram_handle_compressed(host, ch, TARGET_PAGE_SIZE); + if (ch != 0) { + error_report("Found a zero page with value %d", ch); + ret = -EINVAL; + break; + } + ram_handle_zero(host, TARGET_PAGE_SIZE); break; case RAM_SAVE_FLAG_PAGE: @@ -4073,18 +4348,30 @@ static int ram_load_precopy(QEMUFile *f) break; } break; - case RAM_SAVE_FLAG_EOS: - /* normal exit */ + case RAM_SAVE_FLAG_MULTIFD_FLUSH: multifd_recv_sync_main(); break; - default: - if (flags & RAM_SAVE_FLAG_HOOK) { - ram_control_load_hook(f, RAM_CONTROL_HOOK, NULL); - } else { - error_report("Unknown combination of migration flags: 0x%x", - flags); - ret = -EINVAL; + case RAM_SAVE_FLAG_EOS: + /* normal exit */ + if (migrate_multifd() && + migrate_multifd_flush_after_each_section() && + /* + * Mapped-ram migration flushes once and for all after + * parsing ramblocks. Always ignore EOS for it. + */ + !migrate_mapped_ram()) { + multifd_recv_sync_main(); } + break; + case RAM_SAVE_FLAG_HOOK: + ret = rdma_registration_handle(f); + if (ret < 0) { + qemu_file_set_error(f, ret); + } + break; + default: + error_report("Unknown combination of migration flags: 0x%x", flags); + ret = -EINVAL; } if (!ret) { ret = qemu_file_get_error(f); @@ -4122,7 +4409,12 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) */ WITH_RCU_READ_LOCK_GUARD() { if (postcopy_running) { - ret = ram_load_postcopy(f); + /* + * Note! Here RAM_CHANNEL_PRECOPY is the precopy channel of + * postcopy migration, we have another RAM_CHANNEL_POSTCOPY to + * service fast page faults. + */ + ret = ram_load_postcopy(f, RAM_CHANNEL_PRECOPY); } else { ret = ram_load_precopy(f); } @@ -4151,21 +4443,23 @@ static int ram_dirty_bitmap_sync_all(MigrationState *s, RAMState *rs) { RAMBlock *block; QEMUFile *file = s->to_dst_file; - int ramblock_count = 0; trace_ram_dirty_bitmap_sync_start(); + qatomic_set(&rs->postcopy_bmap_sync_requested, 0); RAMBLOCK_FOREACH_NOT_IGNORED(block) { qemu_savevm_send_recv_bitmap(file, block->idstr); trace_ram_dirty_bitmap_request(block->idstr); - ramblock_count++; + qatomic_inc(&rs->postcopy_bmap_sync_requested); } trace_ram_dirty_bitmap_sync_wait(); /* Wait until all the ramblocks' dirty bitmap synced */ - while (ramblock_count--) { - qemu_sem_wait(&s->rp_state.rp_sem); + while (qatomic_read(&rs->postcopy_bmap_sync_requested)) { + if (migration_rp_wait(s)) { + return -1; + } } trace_ram_dirty_bitmap_sync_complete(); @@ -4173,31 +4467,29 @@ static int ram_dirty_bitmap_sync_all(MigrationState *s, RAMState *rs) return 0; } -static void ram_dirty_bitmap_reload_notify(MigrationState *s) -{ - qemu_sem_post(&s->rp_state.rp_sem); -} - /* * Read the received bitmap, revert it as the initial dirty bitmap. * This is only used when the postcopy migration is paused but wants * to resume from a middle point. + * + * Returns true if succeeded, false for errors. */ -int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) +bool ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block, Error **errp) { - int ret = -EINVAL; /* from_dst_file is always valid because we're within rp_thread */ QEMUFile *file = s->rp_state.from_dst_file; - unsigned long *le_bitmap, nbits = block->used_length >> TARGET_PAGE_BITS; + g_autofree unsigned long *le_bitmap = NULL; + unsigned long nbits = block->used_length >> TARGET_PAGE_BITS; uint64_t local_size = DIV_ROUND_UP(nbits, 8); uint64_t size, end_mark; + RAMState *rs = ram_state; trace_ram_dirty_bitmap_reload_begin(block->idstr); if (s->state != MIGRATION_STATUS_POSTCOPY_RECOVER) { - error_report("%s: incorrect state %s", __func__, - MigrationStatus_str(s->state)); - return -EINVAL; + error_setg(errp, "Reload bitmap in incorrect state %s", + MigrationStatus_str(s->state)); + return false; } /* @@ -4213,30 +4505,25 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) /* The size of the bitmap should match with our ramblock */ if (size != local_size) { - error_report("%s: ramblock '%s' bitmap size mismatch " - "(0x%"PRIx64" != 0x%"PRIx64")", __func__, - block->idstr, size, local_size); - ret = -EINVAL; - goto out; + error_setg(errp, "ramblock '%s' bitmap size mismatch (0x%"PRIx64 + " != 0x%"PRIx64")", block->idstr, size, local_size); + return false; } size = qemu_get_buffer(file, (uint8_t *)le_bitmap, local_size); end_mark = qemu_get_be64(file); - ret = qemu_file_get_error(file); - if (ret || size != local_size) { - error_report("%s: read bitmap failed for ramblock '%s': %d" - " (size 0x%"PRIx64", got: 0x%"PRIx64")", - __func__, block->idstr, ret, local_size, size); - ret = -EIO; - goto out; + if (qemu_file_get_error(file) || size != local_size) { + error_setg(errp, "read bitmap failed for ramblock '%s': " + "(size 0x%"PRIx64", got: 0x%"PRIx64")", + block->idstr, local_size, size); + return false; } if (end_mark != RAMBLOCK_RECV_BITMAP_ENDING) { - error_report("%s: ramblock '%s' end mark incorrect: 0x%"PRIx64, - __func__, block->idstr, end_mark); - ret = -EINVAL; - goto out; + error_setg(errp, "ramblock '%s' end mark incorrect: 0x%"PRIx64, + block->idstr, end_mark); + return false; } /* @@ -4257,16 +4544,18 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) /* We'll recalculate migration_dirty_pages in ram_state_resume_prepare(). */ trace_ram_dirty_bitmap_reload_complete(block->idstr); - /* - * We succeeded to sync bitmap for current ramblock. If this is - * the last one to sync, we need to notify the main send thread. - */ - ram_dirty_bitmap_reload_notify(s); + qatomic_dec(&rs->postcopy_bmap_sync_requested); - ret = 0; -out: - g_free(le_bitmap); - return ret; + /* + * We succeeded to sync bitmap for current ramblock. Always kick the + * migration thread to check whether all requested bitmaps are + * reloaded. NOTE: it's racy to only kick when requested==0, because + * we don't know whether the migration thread may still be increasing + * it. + */ + migration_rp_kick(s); + + return true; } static int ram_resume_prepare(MigrationState *s, void *opaque) @@ -4284,13 +4573,20 @@ static int ram_resume_prepare(MigrationState *s, void *opaque) return 0; } +void postcopy_preempt_shutdown_file(MigrationState *s) +{ + qemu_put_be64(s->postcopy_qemufile_src, RAM_SAVE_FLAG_EOS); + qemu_fflush(s->postcopy_qemufile_src); +} + static SaveVMHandlers savevm_ram_handlers = { .save_setup = ram_save_setup, .save_live_iterate = ram_save_iterate, .save_live_complete_postcopy = ram_save_complete, .save_live_complete_precopy = ram_save_complete, .has_postcopy = ram_has_postcopy, - .save_live_pending = ram_save_pending, + .state_pending_exact = ram_state_pending_exact, + .state_pending_estimate = ram_state_pending_estimate, .load_state = ram_load, .save_cleanup = ram_save_cleanup, .load_setup = ram_load_setup, @@ -4306,7 +4602,12 @@ static void ram_mig_ram_block_resized(RAMBlockNotifier *n, void *host, RAMBlock *rb = qemu_ram_block_from_host(host, false, &offset); Error *err = NULL; - if (ramblock_is_ignored(rb)) { + if (!rb) { + error_report("RAM block not found"); + return; + } + + if (migrate_ram_is_ignored(rb)) { return; } diff --git a/migration/ram.h b/migration/ram.h index ded0a3a086..08feecaf51 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -33,15 +33,12 @@ #include "exec/cpu-common.h" #include "io/channel.h" -extern MigrationStats ram_counters; extern XBZRLECacheStats xbzrle_counters; -extern CompressionStats compression_counters; -bool ramblock_is_ignored(RAMBlock *block); /* Should be holding either ram_list.mutex, or the RCU lock. */ #define RAMBLOCK_FOREACH_NOT_IGNORED(block) \ INTERNAL_RAMBLOCK_FOREACH(block) \ - if (ramblock_is_ignored(block)) {} else + if (migrate_ram_is_ignored(block)) {} else #define RAMBLOCK_FOREACH_MIGRATABLE(block) \ INTERNAL_RAMBLOCK_FOREACH(block) \ @@ -53,17 +50,20 @@ uint64_t ram_bytes_total(void); void mig_throttle_counter_reset(void); 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); +int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len, + Error **errp); void ram_postcopy_migrated_memory_release(MigrationState *ms); /* For outgoing discard bitmap */ void ram_postcopy_send_discard_bitmap(MigrationState *ms); /* For incoming postcopy discard */ int ram_discard_range(const char *block_name, uint64_t start, size_t length); int ram_postcopy_incoming_init(MigrationIncomingState *mis); -int ram_load_postcopy(QEMUFile *f); +int ram_load_postcopy(QEMUFile *f, int channel); -void ram_handle_compressed(void *host, uint8_t ch, uint64_t size); +void ram_handle_zero(void *host, uint64_t size); + +void ram_transferred_add(uint64_t bytes); +void ram_release_page(const char *rbname, uint64_t offset); int ramblock_recv_bitmap_test(RAMBlock *rb, void *host_addr); bool ramblock_recv_bitmap_test_byte_offset(RAMBlock *rb, uint64_t byte_offset); @@ -71,14 +71,19 @@ void ramblock_recv_bitmap_set(RAMBlock *rb, void *host_addr); void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, size_t nr); int64_t ramblock_recv_bitmap_send(QEMUFile *file, const char *block_name); -int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb); +bool ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb, Error **errp); bool ramblock_page_is_discarded(RAMBlock *rb, ram_addr_t start); +void postcopy_preempt_shutdown_file(MigrationState *s); +void *postcopy_preempt_thread(void *opaque); +void ramblock_set_file_bmap_atomic(RAMBlock *block, ram_addr_t offset, + bool set); /* ram cache */ int colo_init_ram_cache(void); void colo_flush_ram_cache(void); void colo_release_ram_cache(void); void colo_incoming_start_dirty_log(void); +void colo_record_bitmap(RAMBlock *block, ram_addr_t *normal, uint32_t pages); /* Background snapshot */ bool ram_write_tracking_available(void); diff --git a/migration/rdma.c b/migration/rdma.c index 672d1958a9..855753c671 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -17,11 +17,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/cutils.h" +#include "exec/target_page.h" #include "rdma.h" #include "migration.h" +#include "migration-stats.h" #include "qemu-file.h" #include "ram.h" -#include "qemu-file-channel.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" @@ -36,19 +37,9 @@ #include #include "trace.h" #include "qom/object.h" +#include "options.h" #include -/* - * Print and error on both the Monitor and the Log file. - */ -#define ERROR(errp, fmt, ...) \ - do { \ - fprintf(stderr, "RDMA ERROR: " fmt "\n", ## __VA_ARGS__); \ - if (errp && (*(errp) == NULL)) { \ - error_setg(errp, "RDMA ERROR: " fmt, ## __VA_ARGS__); \ - } \ - } while (0) - #define RDMA_RESOLVE_TIMEOUT_MS 10000 /* Do not merge data if larger than this. */ @@ -83,18 +74,6 @@ */ static uint32_t known_capabilities = RDMA_CAPABILITY_PIN_ALL; -#define CHECK_ERROR_STATE() \ - do { \ - if (rdma->error_state) { \ - if (!rdma->error_reported) { \ - error_report("RDMA is in an error state waiting migration" \ - " to abort!"); \ - rdma->error_reported = 1; \ - } \ - return rdma->error_state; \ - } \ - } while (0) - /* * A work request ID is 64-bits and we split up these bits * into 3 parts: @@ -131,13 +110,6 @@ enum { RDMA_WRID_RECV_CONTROL = 4000, }; -static const char *wrid_desc[] = { - [RDMA_WRID_NONE] = "NONE", - [RDMA_WRID_RDMA_WRITE] = "WRITE RDMA", - [RDMA_WRID_SEND_CONTROL] = "CONTROL SEND", - [RDMA_WRID_RECV_CONTROL] = "CONTROL RECV", -}; - /* * Work request IDs for IB SEND messages only (not RDMA writes). * This is used by the migration protocol to transmit @@ -266,6 +238,7 @@ static const char *control_desc(unsigned int rdma_control) return strs[rdma_control]; } +#if !defined(htonll) static uint64_t htonll(uint64_t v) { union { uint32_t lv[2]; uint64_t llv; } u; @@ -273,13 +246,16 @@ static uint64_t htonll(uint64_t v) u.lv[1] = htonl(v & 0xFFFFFFFFULL); return u.llv; } +#endif +#if !defined(ntohll) static uint64_t ntohll(uint64_t v) { union { uint32_t lv[2]; uint64_t llv; } u; u.llv = v; return ((uint64_t)ntohl(u.lv[0]) << 32) | (uint64_t) ntohl(u.lv[1]); } +#endif static void dest_block_to_network(RDMADestBlock *db) { @@ -317,7 +293,6 @@ typedef struct RDMALocalBlocks { typedef struct RDMAContext { char *host; int port; - char *host_port; RDMAWorkRequestData wr_data[RDMA_WRID_MAX]; @@ -369,9 +344,9 @@ typedef struct RDMAContext { * memory registration, then do not attempt any future work * and remember the error state. */ - int error_state; - int error_reported; - int received_error; + bool errored; + bool error_reported; + bool received_error; /* * Description of ram blocks used throughout the code. @@ -456,6 +431,16 @@ typedef struct QEMU_PACKED { uint64_t chunks; /* how many sequential chunks to register */ } RDMARegister; +static bool rdma_errored(RDMAContext *rdma) +{ + if (rdma->errored && !rdma->error_reported) { + error_report("RDMA is in an error state waiting migration" + " to abort!"); + rdma->error_reported = true; + } + return rdma->errored; +} + static void register_to_network(RDMAContext *rdma, RDMARegister *reg) { RDMALocalBlock *local_block; @@ -533,11 +518,12 @@ static void network_to_result(RDMARegisterResult *result) result->host_addr = ntohll(result->host_addr); }; -const char *print_wrid(int wrid); static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head, uint8_t *data, RDMAControlHeader *resp, int *resp_idx, - int (*callback)(RDMAContext *rdma)); + int (*callback)(RDMAContext *rdma, + Error **errp), + Error **errp); static inline uint64_t ram_chunk_index(const uint8_t *start, const uint8_t *host) @@ -565,9 +551,9 @@ static inline uint8_t *ram_chunk_end(const RDMALocalBlock *rdma_ram_block, return result; } -static int rdma_add_block(RDMAContext *rdma, const char *block_name, - void *host_addr, - ram_addr_t block_offset, uint64_t length) +static void rdma_add_block(RDMAContext *rdma, const char *block_name, + void *host_addr, + ram_addr_t block_offset, uint64_t length) { RDMALocalBlocks *local = &rdma->local_ram_blocks; RDMALocalBlock *block; @@ -576,10 +562,8 @@ static int rdma_add_block(RDMAContext *rdma, const char *block_name, local->block = g_new0(RDMALocalBlock, local->nb_blocks + 1); if (local->nb_blocks) { - int x; - if (rdma->blockmap) { - for (x = 0; x < local->nb_blocks; x++) { + for (int x = 0; x < local->nb_blocks; x++) { g_hash_table_remove(rdma->blockmap, (void *)(uintptr_t)old[x].offset); g_hash_table_insert(rdma->blockmap, @@ -621,8 +605,6 @@ static int rdma_add_block(RDMAContext *rdma, const char *block_name, block->nb_chunks); local->nb_blocks++; - - return 0; } /* @@ -636,7 +618,8 @@ static int qemu_rdma_init_one_block(RAMBlock *rb, void *opaque) void *host_addr = qemu_ram_get_host_addr(rb); ram_addr_t block_offset = qemu_ram_get_offset(rb); ram_addr_t length = qemu_ram_get_used_length(rb); - return rdma_add_block(opaque, block_name, host_addr, block_offset, length); + rdma_add_block(opaque, block_name, host_addr, block_offset, length); + return 0; } /* @@ -644,7 +627,7 @@ static int qemu_rdma_init_one_block(RAMBlock *rb, void *opaque) * identify chunk boundaries inside each RAMBlock and also be referenced * during dynamic page registration. */ -static int qemu_rdma_init_ram_blocks(RDMAContext *rdma) +static void qemu_rdma_init_ram_blocks(RDMAContext *rdma) { RDMALocalBlocks *local = &rdma->local_ram_blocks; int ret; @@ -652,33 +635,27 @@ static int qemu_rdma_init_ram_blocks(RDMAContext *rdma) assert(rdma->blockmap == NULL); memset(local, 0, sizeof *local); ret = foreach_not_ignored_block(qemu_rdma_init_one_block, rdma); - if (ret) { - return ret; - } + assert(!ret); trace_qemu_rdma_init_ram_blocks(local->nb_blocks); rdma->dest_blocks = g_new0(RDMADestBlock, rdma->local_ram_blocks.nb_blocks); local->init = true; - return 0; } /* * Note: If used outside of cleanup, the caller must ensure that the destination * block structures are also updated */ -static int rdma_delete_block(RDMAContext *rdma, RDMALocalBlock *block) +static void rdma_delete_block(RDMAContext *rdma, RDMALocalBlock *block) { RDMALocalBlocks *local = &rdma->local_ram_blocks; RDMALocalBlock *old = local->block; - int x; if (rdma->blockmap) { g_hash_table_remove(rdma->blockmap, (void *)(uintptr_t)block->offset); } if (block->pmr) { - int j; - - for (j = 0; j < block->nb_chunks; j++) { + for (int j = 0; j < block->nb_chunks; j++) { if (!block->pmr[j]) { continue; } @@ -708,7 +685,7 @@ static int rdma_delete_block(RDMAContext *rdma, RDMALocalBlock *block) block->block_name = NULL; if (rdma->blockmap) { - for (x = 0; x < local->nb_blocks; x++) { + for (int x = 0; x < local->nb_blocks; x++) { g_hash_table_remove(rdma->blockmap, (void *)(uintptr_t)old[x].offset); } @@ -726,7 +703,7 @@ static int rdma_delete_block(RDMAContext *rdma, RDMALocalBlock *block) memcpy(local->block + block->index, old + (block->index + 1), sizeof(RDMALocalBlock) * (local->nb_blocks - (block->index + 1))); - for (x = block->index; x < local->nb_blocks - 1; x++) { + for (int x = block->index; x < local->nb_blocks - 1; x++) { local->block[x].index--; } } @@ -746,49 +723,40 @@ static int rdma_delete_block(RDMAContext *rdma, RDMALocalBlock *block) local->nb_blocks--; if (local->nb_blocks && rdma->blockmap) { - for (x = 0; x < local->nb_blocks; x++) { + for (int x = 0; x < local->nb_blocks; x++) { g_hash_table_insert(rdma->blockmap, (void *)(uintptr_t)local->block[x].offset, &local->block[x]); } } - - return 0; } /* - * Put in the log file which RDMA device was opened and the details - * associated with that device. + * Trace RDMA device open, with device details. */ static void qemu_rdma_dump_id(const char *who, struct ibv_context *verbs) { struct ibv_port_attr port; if (ibv_query_port(verbs, 1, &port)) { - error_report("Failed to query port information"); + trace_qemu_rdma_dump_id_failed(who); return; } - printf("%s RDMA Device opened: kernel name %s " - "uverbs device name %s, " - "infiniband_verbs class device path %s, " - "infiniband class device path %s, " - "transport: (%d) %s\n", - who, + trace_qemu_rdma_dump_id(who, verbs->device->name, verbs->device->dev_name, verbs->device->dev_path, verbs->device->ibdev_path, port.link_layer, - (port.link_layer == IBV_LINK_LAYER_INFINIBAND) ? "Infiniband" : - ((port.link_layer == IBV_LINK_LAYER_ETHERNET) - ? "Ethernet" : "Unknown")); + port.link_layer == IBV_LINK_LAYER_INFINIBAND ? "Infiniband" + : port.link_layer == IBV_LINK_LAYER_ETHERNET ? "Ethernet" + : "Unknown"); } /* - * Put in the log file the RDMA gid addressing information, - * useful for folks who have trouble understanding the - * RDMA device hierarchy in the kernel. + * Trace RDMA gid addressing information. + * Useful for understanding the RDMA device hierarchy in the kernel. */ static void qemu_rdma_dump_gid(const char *who, struct rdma_cm_id *id) { @@ -858,25 +826,34 @@ static int qemu_rdma_broken_ipv6_kernel(struct ibv_context *verbs, Error **errp) * Otherwise, there are no guarantees until the bug is fixed in linux. */ if (!verbs) { - int num_devices, x; + int num_devices; struct ibv_device **dev_list = ibv_get_device_list(&num_devices); bool roce_found = false; bool ib_found = false; - for (x = 0; x < num_devices; x++) { + for (int x = 0; x < num_devices; x++) { verbs = ibv_open_device(dev_list[x]); + /* + * ibv_open_device() is not documented to set errno. If + * it does, it's somebody else's doc bug. If it doesn't, + * the use of errno below is wrong. + * TODO Find out whether ibv_open_device() sets errno. + */ if (!verbs) { if (errno == EPERM) { continue; } else { - return -EINVAL; + error_setg_errno(errp, errno, + "could not open RDMA device context"); + return -1; } } if (ibv_query_port(verbs, 1, &port_attr)) { ibv_close_device(verbs); - ERROR(errp, "Could not query initial IB port"); - return -EINVAL; + error_setg(errp, + "RDMA ERROR: Could not query initial IB port"); + return -1; } if (port_attr.link_layer == IBV_LINK_LAYER_INFINIBAND) { @@ -891,17 +868,18 @@ static int qemu_rdma_broken_ipv6_kernel(struct ibv_context *verbs, Error **errp) if (roce_found) { if (ib_found) { - fprintf(stderr, "WARN: migrations may fail:" - " IPv6 over RoCE / iWARP in linux" - " is broken. But since you appear to have a" - " mixed RoCE / IB environment, be sure to only" - " migrate over the IB fabric until the kernel " - " fixes the bug.\n"); + warn_report("migrations may fail:" + " IPv6 over RoCE / iWARP in linux" + " is broken. But since you appear to have a" + " mixed RoCE / IB environment, be sure to only" + " migrate over the IB fabric until the kernel " + " fixes the bug."); } else { - ERROR(errp, "You only have RoCE / iWARP devices in your systems" - " and your management software has specified '[::]'" - ", but IPv6 over RoCE / iWARP is not supported in Linux."); - return -ENONET; + error_setg(errp, "RDMA ERROR: " + "You only have RoCE / iWARP devices in your systems" + " and your management software has specified '[::]'" + ", but IPv6 over RoCE / iWARP is not supported in Linux."); + return -1; } } @@ -916,14 +894,15 @@ static int qemu_rdma_broken_ipv6_kernel(struct ibv_context *verbs, Error **errp) /* IB ports start with 1, not 0 */ if (ibv_query_port(verbs, 1, &port_attr)) { - ERROR(errp, "Could not query initial IB port"); - return -EINVAL; + error_setg(errp, "RDMA ERROR: Could not query initial IB port"); + return -1; } if (port_attr.link_layer == IBV_LINK_LAYER_ETHERNET) { - ERROR(errp, "Linux kernel's RoCE / iWARP does not support IPv6 " - "(but patches on linux-rdma in progress)"); - return -ENONET; + error_setg(errp, "RDMA ERROR: " + "Linux kernel's RoCE / iWARP does not support IPv6 " + "(but patches on linux-rdma in progress)"); + return -1; } #endif @@ -938,29 +917,29 @@ static int qemu_rdma_broken_ipv6_kernel(struct ibv_context *verbs, Error **errp) */ static int qemu_rdma_resolve_host(RDMAContext *rdma, Error **errp) { + Error *err = NULL; int ret; struct rdma_addrinfo *res; char port_str[16]; struct rdma_cm_event *cm_event; char ip[40] = "unknown"; - struct rdma_addrinfo *e; if (rdma->host == NULL || !strcmp(rdma->host, "")) { - ERROR(errp, "RDMA hostname has not been set"); - return -EINVAL; + error_setg(errp, "RDMA ERROR: RDMA hostname has not been set"); + return -1; } /* create CM channel */ rdma->channel = rdma_create_event_channel(); if (!rdma->channel) { - ERROR(errp, "could not create CM channel"); - return -EINVAL; + error_setg(errp, "RDMA ERROR: could not create CM channel"); + return -1; } /* create CM id */ ret = rdma_create_id(rdma->channel, &rdma->cm_id, NULL, RDMA_PS_TCP); - if (ret) { - ERROR(errp, "could not create channel id"); + if (ret < 0) { + error_setg(errp, "RDMA ERROR: could not create channel id"); goto err_resolve_create_id; } @@ -968,31 +947,42 @@ static int qemu_rdma_resolve_host(RDMAContext *rdma, Error **errp) port_str[15] = '\0'; ret = rdma_getaddrinfo(rdma->host, port_str, NULL, &res); - if (ret < 0) { - ERROR(errp, "could not rdma_getaddrinfo address %s", rdma->host); + if (ret) { + error_setg(errp, "RDMA ERROR: could not rdma_getaddrinfo address %s", + rdma->host); goto err_resolve_get_addr; } - for (e = res; e != NULL; e = e->ai_next) { + /* Try all addresses, saving the first error in @err */ + for (struct rdma_addrinfo *e = res; e != NULL; e = e->ai_next) { + Error **local_errp = err ? NULL : &err; + inet_ntop(e->ai_family, &((struct sockaddr_in *) e->ai_dst_addr)->sin_addr, ip, sizeof ip); trace_qemu_rdma_resolve_host_trying(rdma->host, ip); ret = rdma_resolve_addr(rdma->cm_id, NULL, e->ai_dst_addr, RDMA_RESOLVE_TIMEOUT_MS); - if (!ret) { + if (ret >= 0) { if (e->ai_family == AF_INET6) { - ret = qemu_rdma_broken_ipv6_kernel(rdma->cm_id->verbs, errp); - if (ret) { + ret = qemu_rdma_broken_ipv6_kernel(rdma->cm_id->verbs, + local_errp); + if (ret < 0) { continue; } } + error_free(err); goto route; } } rdma_freeaddrinfo(res); - ERROR(errp, "could not resolve address %s", rdma->host); + if (err) { + error_propagate(errp, err); + } else { + error_setg(errp, "RDMA ERROR: could not resolve address %s", + rdma->host); + } goto err_resolve_get_addr; route: @@ -1000,38 +990,37 @@ route: qemu_rdma_dump_gid("source_resolve_addr", rdma->cm_id); ret = rdma_get_cm_event(rdma->channel, &cm_event); - if (ret) { - ERROR(errp, "could not perform event_addr_resolved"); + if (ret < 0) { + error_setg(errp, "RDMA ERROR: could not perform event_addr_resolved"); goto err_resolve_get_addr; } if (cm_event->event != RDMA_CM_EVENT_ADDR_RESOLVED) { - ERROR(errp, "result not equal to event_addr_resolved %s", - rdma_event_str(cm_event->event)); - error_report("rdma_resolve_addr"); + error_setg(errp, + "RDMA ERROR: result not equal to event_addr_resolved %s", + rdma_event_str(cm_event->event)); rdma_ack_cm_event(cm_event); - ret = -EINVAL; goto err_resolve_get_addr; } rdma_ack_cm_event(cm_event); /* resolve route */ ret = rdma_resolve_route(rdma->cm_id, RDMA_RESOLVE_TIMEOUT_MS); - if (ret) { - ERROR(errp, "could not resolve rdma route"); + if (ret < 0) { + error_setg(errp, "RDMA ERROR: could not resolve rdma route"); goto err_resolve_get_addr; } ret = rdma_get_cm_event(rdma->channel, &cm_event); - if (ret) { - ERROR(errp, "could not perform event_route_resolved"); + if (ret < 0) { + error_setg(errp, "RDMA ERROR: could not perform event_route_resolved"); goto err_resolve_get_addr; } if (cm_event->event != RDMA_CM_EVENT_ROUTE_RESOLVED) { - ERROR(errp, "result not equal to event_route_resolved: %s", - rdma_event_str(cm_event->event)); + error_setg(errp, "RDMA ERROR: " + "result not equal to event_route_resolved: %s", + rdma_event_str(cm_event->event)); rdma_ack_cm_event(cm_event); - ret = -EINVAL; goto err_resolve_get_addr; } rdma_ack_cm_event(cm_event); @@ -1046,25 +1035,25 @@ err_resolve_get_addr: err_resolve_create_id: rdma_destroy_event_channel(rdma->channel); rdma->channel = NULL; - return ret; + return -1; } /* * Create protection domain and completion queues */ -static int qemu_rdma_alloc_pd_cq(RDMAContext *rdma) +static int qemu_rdma_alloc_pd_cq(RDMAContext *rdma, Error **errp) { /* allocate pd */ rdma->pd = ibv_alloc_pd(rdma->verbs); if (!rdma->pd) { - error_report("failed to allocate protection domain"); + error_setg(errp, "failed to allocate protection domain"); return -1; } /* create receive completion channel */ rdma->recv_comp_channel = ibv_create_comp_channel(rdma->verbs); if (!rdma->recv_comp_channel) { - error_report("failed to allocate receive completion channel"); + error_setg(errp, "failed to allocate receive completion channel"); goto err_alloc_pd_cq; } @@ -1074,21 +1063,21 @@ static int qemu_rdma_alloc_pd_cq(RDMAContext *rdma) rdma->recv_cq = ibv_create_cq(rdma->verbs, (RDMA_SIGNALED_SEND_MAX * 3), NULL, rdma->recv_comp_channel, 0); if (!rdma->recv_cq) { - error_report("failed to allocate receive completion queue"); + error_setg(errp, "failed to allocate receive completion queue"); goto err_alloc_pd_cq; } /* create send completion channel */ rdma->send_comp_channel = ibv_create_comp_channel(rdma->verbs); if (!rdma->send_comp_channel) { - error_report("failed to allocate send completion channel"); + error_setg(errp, "failed to allocate send completion channel"); goto err_alloc_pd_cq; } rdma->send_cq = ibv_create_cq(rdma->verbs, (RDMA_SIGNALED_SEND_MAX * 3), NULL, rdma->send_comp_channel, 0); if (!rdma->send_cq) { - error_report("failed to allocate send completion queue"); + error_setg(errp, "failed to allocate send completion queue"); goto err_alloc_pd_cq; } @@ -1121,7 +1110,6 @@ err_alloc_pd_cq: static int qemu_rdma_alloc_qp(RDMAContext *rdma) { struct ibv_qp_init_attr attr = { 0 }; - int ret; attr.cap.max_send_wr = RDMA_SIGNALED_SEND_MAX; attr.cap.max_recv_wr = 3; @@ -1131,8 +1119,7 @@ static int qemu_rdma_alloc_qp(RDMAContext *rdma) attr.recv_cq = rdma->recv_cq; attr.qp_type = IBV_QPT_RC; - ret = rdma_create_qp(rdma->cm_id, rdma->pd, &attr); - if (ret) { + if (rdma_create_qp(rdma->cm_id, rdma->pd, &attr) < 0) { return -1; } @@ -1144,8 +1131,8 @@ static int qemu_rdma_alloc_qp(RDMAContext *rdma) static bool rdma_support_odp(struct ibv_context *dev) { struct ibv_device_attr_ex attr = {0}; - int ret = ibv_query_device_ex(dev, NULL, &attr); - if (ret) { + + if (ibv_query_device_ex(dev, NULL, &attr)) { return false; } @@ -1174,15 +1161,11 @@ static void qemu_rdma_advise_prefetch_mr(struct ibv_pd *pd, uint64_t addr, ret = ibv_advise_mr(pd, advice, IBV_ADVISE_MR_FLAG_FLUSH, &sg_list, 1); /* ignore the error */ - if (ret) { - trace_qemu_rdma_advise_mr(name, len, addr, strerror(errno)); - } else { - trace_qemu_rdma_advise_mr(name, len, addr, "successed"); - } + trace_qemu_rdma_advise_mr(name, len, addr, strerror(ret)); #endif } -static int qemu_rdma_reg_whole_ram_blocks(RDMAContext *rdma) +static int qemu_rdma_reg_whole_ram_blocks(RDMAContext *rdma, Error **errp) { int i; RDMALocalBlocks *local = &rdma->local_ram_blocks; @@ -1195,7 +1178,12 @@ static int qemu_rdma_reg_whole_ram_blocks(RDMAContext *rdma) local->block[i].local_host_addr, local->block[i].length, access ); - + /* + * ibv_reg_mr() is not documented to set errno. If it does, + * it's somebody else's doc bug. If it doesn't, the use of + * errno below is wrong. + * TODO Find out whether ibv_reg_mr() sets errno. + */ if (!local->block[i].mr && errno == ENOTSUP && rdma_support_odp(rdma->verbs)) { access |= IBV_ACCESS_ON_DEMAND; @@ -1217,16 +1205,16 @@ static int qemu_rdma_reg_whole_ram_blocks(RDMAContext *rdma) } if (!local->block[i].mr) { - perror("Failed to register local dest ram block!"); - break; + error_setg_errno(errp, errno, + "Failed to register local dest ram block!"); + goto err; } rdma->total_registrations++; } - if (i >= local->nb_blocks) { - return 0; - } + return 0; +err: for (i--; i >= 0; i--) { ibv_dereg_mr(local->block[i].mr); local->block[i].mr = NULL; @@ -1243,15 +1231,13 @@ static int qemu_rdma_reg_whole_ram_blocks(RDMAContext *rdma) * * Once the block is found, also identify which 'chunk' within that * block that the page belongs to. - * - * This search cannot fail or the migration will fail. */ -static int qemu_rdma_search_ram_block(RDMAContext *rdma, - uintptr_t block_offset, - uint64_t offset, - uint64_t length, - uint64_t *block_index, - uint64_t *chunk_index) +static void qemu_rdma_search_ram_block(RDMAContext *rdma, + uintptr_t block_offset, + uint64_t offset, + uint64_t length, + uint64_t *block_index, + uint64_t *chunk_index) { uint64_t current_addr = block_offset + offset; RDMALocalBlock *block = g_hash_table_lookup(rdma->blockmap, @@ -1263,8 +1249,6 @@ static int qemu_rdma_search_ram_block(RDMAContext *rdma, *block_index = block->index; *chunk_index = ram_chunk_index(block->local_host_addr, block->local_host_addr + (current_addr - block->offset)); - - return 0; } /* @@ -1307,6 +1291,12 @@ static int qemu_rdma_register_and_get_keys(RDMAContext *rdma, trace_qemu_rdma_register_and_get_keys(len, chunk_start); block->pmr[chunk] = ibv_reg_mr(rdma->pd, chunk_start, len, access); + /* + * ibv_reg_mr() is not documented to set errno. If it does, + * it's somebody else's doc bug. If it doesn't, the use of + * errno below is wrong. + * TODO Find out whether ibv_reg_mr() sets errno. + */ if (!block->pmr[chunk] && errno == ENOTSUP && rdma_support_odp(rdma->verbs)) { access |= IBV_ACCESS_ON_DEMAND; @@ -1323,15 +1313,6 @@ static int qemu_rdma_register_and_get_keys(RDMAContext *rdma, } } if (!block->pmr[chunk]) { - perror("Failed to register chunk!"); - fprintf(stderr, "Chunk details: block: %d chunk index %d" - " start %" PRIuPTR " end %" PRIuPTR - " host %" PRIuPTR - " local %" PRIuPTR " registrations: %d\n", - block->index, chunk, (uintptr_t)chunk_start, - (uintptr_t)chunk_end, host_addr, - (uintptr_t)block->local_host_addr, - rdma->total_registrations); return -1; } rdma->total_registrations++; @@ -1358,42 +1339,9 @@ static int qemu_rdma_reg_control(RDMAContext *rdma, int idx) rdma->total_registrations++; return 0; } - error_report("qemu_rdma_reg_control failed"); return -1; } -const char *print_wrid(int wrid) -{ - if (wrid >= RDMA_WRID_RECV_CONTROL) { - return wrid_desc[RDMA_WRID_RECV_CONTROL]; - } - return wrid_desc[wrid]; -} - -/* - * RDMA requires memory registration (mlock/pinning), but this is not good for - * overcommitment. - * - * In preparation for the future where LRU information or workload-specific - * writable writable working set memory access behavior is available to QEMU - * it would be nice to have in place the ability to UN-register/UN-pin - * particular memory regions from the RDMA hardware when it is determine that - * those regions of memory will likely not be accessed again in the near future. - * - * While we do not yet have such information right now, the following - * compile-time option allows us to perform a non-optimized version of this - * behavior. - * - * By uncommenting this option, you will cause *all* RDMA transfers to be - * unregistered immediately after the transfer completes on both sides of the - * connection. This has no effect in 'rdma-pin-all' mode, only regular mode. - * - * This will have a terrible impact on migration performance, so until future - * workload information or LRU information is available, do not attempt to use - * this feature except for basic testing. - */ -/* #define RDMA_UNREGISTRATION_EXAMPLE */ - /* * Perform a non-optimized memory unregistration after every transfer * for demonstration purposes, only if pin-all is not requested. @@ -1407,6 +1355,8 @@ const char *print_wrid(int wrid) */ static int qemu_rdma_unregister_waiting(RDMAContext *rdma) { + Error *err = NULL; + while (rdma->unregistrations[rdma->unregister_current]) { int ret; uint64_t wr_id = rdma->unregistrations[rdma->unregister_current]; @@ -1456,17 +1406,19 @@ static int qemu_rdma_unregister_waiting(RDMAContext *rdma) block->remote_keys[chunk] = 0; if (ret != 0) { - perror("unregistration chunk failed"); - return -ret; + error_report("unregistration chunk failed: %s", + strerror(ret)); + return -1; } rdma->total_registrations--; reg.key.chunk = chunk; register_to_network(rdma, ®); ret = qemu_rdma_exchange_send(rdma, &head, (uint8_t *) ®, - &resp, NULL, NULL); + &resp, NULL, NULL, &err); if (ret < 0) { - return ret; + error_report_err(err); + return -1; } trace_qemu_rdma_unregister_waiting_complete(chunk); @@ -1486,41 +1438,13 @@ static uint64_t qemu_rdma_make_wrid(uint64_t wr_id, uint64_t index, return result; } -/* - * Set bit for unregistration in the next iteration. - * We cannot transmit right here, but will unpin later. - */ -static void qemu_rdma_signal_unregister(RDMAContext *rdma, uint64_t index, - uint64_t chunk, uint64_t wr_id) -{ - if (rdma->unregistrations[rdma->unregister_next] != 0) { - error_report("rdma migration: queue is full"); - } else { - RDMALocalBlock *block = &(rdma->local_ram_blocks.block[index]); - - if (!test_and_set_bit(chunk, block->unregister_bitmap)) { - trace_qemu_rdma_signal_unregister_append(chunk, - rdma->unregister_next); - - rdma->unregistrations[rdma->unregister_next++] = - qemu_rdma_make_wrid(wr_id, index, chunk); - - if (rdma->unregister_next == RDMA_SIGNALED_SEND_MAX) { - rdma->unregister_next = 0; - } - } else { - trace_qemu_rdma_signal_unregister_already(chunk); - } - } -} - /* * Consult the connection manager to see a work request * (of any kind) has completed. * Return the work request ID that completed. */ -static uint64_t qemu_rdma_poll(RDMAContext *rdma, struct ibv_cq *cq, - uint64_t *wr_id_out, uint32_t *byte_len) +static int qemu_rdma_poll(RDMAContext *rdma, struct ibv_cq *cq, + uint64_t *wr_id_out, uint32_t *byte_len) { int ret; struct ibv_wc wc; @@ -1534,24 +1458,19 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, struct ibv_cq *cq, } if (ret < 0) { - error_report("ibv_poll_cq return %d", ret); - return ret; + return -1; } wr_id = wc.wr_id & RDMA_WRID_TYPE_MASK; if (wc.status != IBV_WC_SUCCESS) { - fprintf(stderr, "ibv_poll_cq wc.status=%d %s!\n", - wc.status, ibv_wc_status_str(wc.status)); - fprintf(stderr, "ibv_poll_cq wrid=%s!\n", wrid_desc[wr_id]); - return -1; } if (rdma->control_ready_expected && (wr_id >= RDMA_WRID_RECV_CONTROL)) { - trace_qemu_rdma_poll_recv(wrid_desc[RDMA_WRID_RECV_CONTROL], - wr_id - RDMA_WRID_RECV_CONTROL, wr_id, rdma->nb_sent); + trace_qemu_rdma_poll_recv(wr_id - RDMA_WRID_RECV_CONTROL, wr_id, + rdma->nb_sent); rdma->control_ready_expected = 0; } @@ -1562,7 +1481,7 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, struct ibv_cq *cq, (wc.wr_id & RDMA_WRID_BLOCK_MASK) >> RDMA_WRID_BLOCK_SHIFT; RDMALocalBlock *block = &(rdma->local_ram_blocks.block[index]); - trace_qemu_rdma_poll_write(print_wrid(wr_id), wr_id, rdma->nb_sent, + trace_qemu_rdma_poll_write(wr_id, rdma->nb_sent, index, chunk, block->local_host_addr, (void *)(uintptr_t)block->remote_host_addr); @@ -1571,20 +1490,8 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, struct ibv_cq *cq, if (rdma->nb_sent > 0) { rdma->nb_sent--; } - - if (!rdma->pin_all) { - /* - * FYI: If one wanted to signal a specific chunk to be unregistered - * using LRU or workload-specific information, this is the function - * you would call to do so. That chunk would then get asynchronously - * unregistered later. - */ -#ifdef RDMA_UNREGISTRATION_EXAMPLE - qemu_rdma_signal_unregister(rdma, index, chunk, wc.wr_id); -#endif - } } else { - trace_qemu_rdma_poll_other(print_wrid(wr_id), wr_id, rdma->nb_sent); + trace_qemu_rdma_poll_other(wr_id, rdma->nb_sent); } *wr_id_out = wc.wr_id; @@ -1602,7 +1509,6 @@ static int qemu_rdma_wait_comp_channel(RDMAContext *rdma, struct ibv_comp_channel *comp_channel) { struct rdma_cm_event *cm_event; - int ret = -1; /* * Coroutine doesn't start until migration_fd_process_incoming() @@ -1619,7 +1525,7 @@ static int qemu_rdma_wait_comp_channel(RDMAContext *rdma, * But we need to be able to handle 'cancel' or an error * without hanging forever. */ - while (!rdma->error_state && !rdma->received_error) { + while (!rdma->errored && !rdma->received_error) { GPollFD pfds[2]; pfds[0].fd = comp_channel->fd; pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR; @@ -1638,19 +1544,14 @@ static int qemu_rdma_wait_comp_channel(RDMAContext *rdma, } if (pfds[1].revents) { - ret = rdma_get_cm_event(rdma->channel, &cm_event); - if (ret) { - error_report("failed to get cm event while wait " - "completion channel"); - return -EPIPE; + if (rdma_get_cm_event(rdma->channel, &cm_event) < 0) { + return -1; } - error_report("receive cm event while wait comp channel," - "cm event is %d", cm_event->event); if (cm_event->event == RDMA_CM_EVENT_DISCONNECTED || cm_event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) { rdma_ack_cm_event(cm_event); - return -EPIPE; + return -1; } rdma_ack_cm_event(cm_event); } @@ -1662,30 +1563,29 @@ static int qemu_rdma_wait_comp_channel(RDMAContext *rdma, default: /* Error of some type - * I don't trust errno from qemu_poll_ns */ - error_report("%s: poll failed", __func__); - return -EPIPE; + return -1; } if (migrate_get_current()->state == MIGRATION_STATUS_CANCELLING) { /* Bail out and let the cancellation happen */ - return -EPIPE; + return -1; } } } if (rdma->received_error) { - return -EPIPE; + return -1; } - return rdma->error_state; + return -rdma->errored; } -static struct ibv_comp_channel *to_channel(RDMAContext *rdma, int wrid) +static struct ibv_comp_channel *to_channel(RDMAContext *rdma, uint64_t wrid) { return wrid < RDMA_WRID_RECV_CONTROL ? rdma->send_comp_channel : rdma->recv_comp_channel; } -static struct ibv_cq *to_cq(RDMAContext *rdma, int wrid) +static struct ibv_cq *to_cq(RDMAContext *rdma, uint64_t wrid) { return wrid < RDMA_WRID_RECV_CONTROL ? rdma->send_cq : rdma->recv_cq; } @@ -1703,10 +1603,11 @@ static struct ibv_cq *to_cq(RDMAContext *rdma, int wrid) * completions only need to be recorded, but do not actually * need further processing. */ -static int qemu_rdma_block_for_wrid(RDMAContext *rdma, int wrid_requested, +static int qemu_rdma_block_for_wrid(RDMAContext *rdma, + uint64_t wrid_requested, uint32_t *byte_len) { - int num_cq_events = 0, ret = 0; + int num_cq_events = 0, ret; struct ibv_cq *cq; void *cq_ctx; uint64_t wr_id = RDMA_WRID_NONE, wr_id_in; @@ -1720,7 +1621,7 @@ static int qemu_rdma_block_for_wrid(RDMAContext *rdma, int wrid_requested, while (wr_id != wrid_requested) { ret = qemu_rdma_poll(rdma, poll_cq, &wr_id_in, byte_len); if (ret < 0) { - return ret; + return -1; } wr_id = wr_id_in & RDMA_WRID_TYPE_MASK; @@ -1729,8 +1630,7 @@ static int qemu_rdma_block_for_wrid(RDMAContext *rdma, int wrid_requested, break; } if (wr_id != wrid_requested) { - trace_qemu_rdma_block_for_wrid_miss(print_wrid(wrid_requested), - wrid_requested, print_wrid(wr_id), wr_id); + trace_qemu_rdma_block_for_wrid_miss(wrid_requested, wr_id); } } @@ -1740,20 +1640,18 @@ static int qemu_rdma_block_for_wrid(RDMAContext *rdma, int wrid_requested, while (1) { ret = qemu_rdma_wait_comp_channel(rdma, ch); - if (ret) { + if (ret < 0) { goto err_block_for_wrid; } ret = ibv_get_cq_event(ch, &cq, &cq_ctx); - if (ret) { - perror("ibv_get_cq_event"); + if (ret < 0) { goto err_block_for_wrid; } num_cq_events++; - ret = -ibv_req_notify_cq(cq, 0); - if (ret) { + if (ibv_req_notify_cq(cq, 0)) { goto err_block_for_wrid; } @@ -1769,8 +1667,7 @@ static int qemu_rdma_block_for_wrid(RDMAContext *rdma, int wrid_requested, break; } if (wr_id != wrid_requested) { - trace_qemu_rdma_block_for_wrid_miss(print_wrid(wrid_requested), - wrid_requested, print_wrid(wr_id), wr_id); + trace_qemu_rdma_block_for_wrid_miss(wrid_requested, wr_id); } } @@ -1790,8 +1687,8 @@ err_block_for_wrid: ibv_ack_cq_events(cq, num_cq_events); } - rdma->error_state = ret; - return ret; + rdma->errored = true; + return -1; } /* @@ -1799,9 +1696,10 @@ err_block_for_wrid: * containing some data and block until the post completes. */ static int qemu_rdma_post_send_control(RDMAContext *rdma, uint8_t *buf, - RDMAControlHeader *head) + RDMAControlHeader *head, + Error **errp) { - int ret = 0; + int ret; RDMAWorkRequestData *wr = &rdma->wr_data[RDMA_WRID_CONTROL]; struct ibv_send_wr *bad_wr; struct ibv_sge sge = { @@ -1839,23 +1737,25 @@ static int qemu_rdma_post_send_control(RDMAContext *rdma, uint8_t *buf, ret = ibv_post_send(rdma->qp, &send_wr, &bad_wr); if (ret > 0) { - error_report("Failed to use post IB SEND for control"); - return -ret; + error_setg(errp, "Failed to use post IB SEND for control"); + return -1; } ret = qemu_rdma_block_for_wrid(rdma, RDMA_WRID_SEND_CONTROL, NULL); if (ret < 0) { - error_report("rdma migration: send polling control error"); + error_setg(errp, "rdma migration: send polling control error"); + return -1; } - return ret; + return 0; } /* * Post a RECV work request in anticipation of some future receipt * of data on the control channel. */ -static int qemu_rdma_post_recv_control(RDMAContext *rdma, int idx) +static int qemu_rdma_post_recv_control(RDMAContext *rdma, int idx, + Error **errp) { struct ibv_recv_wr *bad_wr; struct ibv_sge sge = { @@ -1872,6 +1772,7 @@ static int qemu_rdma_post_recv_control(RDMAContext *rdma, int idx) if (ibv_post_recv(rdma->qp, &recv_wr, &bad_wr)) { + error_setg(errp, "error posting control recv"); return -1; } @@ -1882,15 +1783,16 @@ static int qemu_rdma_post_recv_control(RDMAContext *rdma, int idx) * Block and wait for a RECV control channel message to arrive. */ static int qemu_rdma_exchange_get_response(RDMAContext *rdma, - RDMAControlHeader *head, int expecting, int idx) + RDMAControlHeader *head, uint32_t expecting, int idx, + Error **errp) { uint32_t byte_len; int ret = qemu_rdma_block_for_wrid(rdma, RDMA_WRID_RECV_CONTROL + idx, &byte_len); if (ret < 0) { - error_report("rdma migration: recv polling control error!"); - return ret; + error_setg(errp, "rdma migration: recv polling control error!"); + return -1; } network_to_control((void *) rdma->wr_data[idx].control); @@ -1902,22 +1804,23 @@ static int qemu_rdma_exchange_get_response(RDMAContext *rdma, trace_qemu_rdma_exchange_get_response_none(control_desc(head->type), head->type); } else if (head->type != expecting || head->type == RDMA_CONTROL_ERROR) { - error_report("Was expecting a %s (%d) control message" + error_setg(errp, "Was expecting a %s (%d) control message" ", but got: %s (%d), length: %d", control_desc(expecting), expecting, control_desc(head->type), head->type, head->len); if (head->type == RDMA_CONTROL_ERROR) { rdma->received_error = true; } - return -EIO; + return -1; } if (head->len > RDMA_CONTROL_MAX_BUFFER - sizeof(*head)) { - error_report("too long length: %d", head->len); - return -EINVAL; + error_setg(errp, "too long length: %d", head->len); + return -1; } if (sizeof(*head) + head->len != byte_len) { - error_report("Malformed length: %d byte_len %d", head->len, byte_len); - return -EINVAL; + error_setg(errp, "Malformed length: %d byte_len %d", + head->len, byte_len); + return -1; } return 0; @@ -1955,20 +1858,24 @@ static void qemu_rdma_move_header(RDMAContext *rdma, int idx, static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head, uint8_t *data, RDMAControlHeader *resp, int *resp_idx, - int (*callback)(RDMAContext *rdma)) + int (*callback)(RDMAContext *rdma, + Error **errp), + Error **errp) { - int ret = 0; + int ret; /* * Wait until the dest is ready before attempting to deliver the message * by waiting for a READY message. */ if (rdma->control_ready_expected) { - RDMAControlHeader resp; - ret = qemu_rdma_exchange_get_response(rdma, - &resp, RDMA_CONTROL_READY, RDMA_WRID_READY); + RDMAControlHeader resp_ignored; + + ret = qemu_rdma_exchange_get_response(rdma, &resp_ignored, + RDMA_CONTROL_READY, + RDMA_WRID_READY, errp); if (ret < 0) { - return ret; + return -1; } } @@ -1976,31 +1883,27 @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head, * If the user is expecting a response, post a WR in anticipation of it. */ if (resp) { - ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_DATA); - if (ret) { - error_report("rdma migration: error posting" - " extra control recv for anticipated result!"); - return ret; + ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_DATA, errp); + if (ret < 0) { + return -1; } } /* * Post a WR to replace the one we just consumed for the READY message. */ - ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY); - if (ret) { - error_report("rdma migration: error posting first control recv!"); - return ret; + ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY, errp); + if (ret < 0) { + return -1; } /* * Deliver the control message that was requested. */ - ret = qemu_rdma_post_send_control(rdma, data, head); + ret = qemu_rdma_post_send_control(rdma, data, head, errp); if (ret < 0) { - error_report("Failed to send control buffer!"); - return ret; + return -1; } /* @@ -2009,18 +1912,19 @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head, if (resp) { if (callback) { trace_qemu_rdma_exchange_send_issue_callback(); - ret = callback(rdma); + ret = callback(rdma, errp); if (ret < 0) { - return ret; + return -1; } } trace_qemu_rdma_exchange_send_waiting(control_desc(resp->type)); ret = qemu_rdma_exchange_get_response(rdma, resp, - resp->type, RDMA_WRID_DATA); + resp->type, RDMA_WRID_DATA, + errp); if (ret < 0) { - return ret; + return -1; } qemu_rdma_move_header(rdma, RDMA_WRID_DATA, resp); @@ -2040,7 +1944,7 @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head, * control-channel message. */ static int qemu_rdma_exchange_recv(RDMAContext *rdma, RDMAControlHeader *head, - int expecting) + uint32_t expecting, Error **errp) { RDMAControlHeader ready = { .len = 0, @@ -2052,21 +1956,20 @@ static int qemu_rdma_exchange_recv(RDMAContext *rdma, RDMAControlHeader *head, /* * Inform the source that we're ready to receive a message. */ - ret = qemu_rdma_post_send_control(rdma, NULL, &ready); + ret = qemu_rdma_post_send_control(rdma, NULL, &ready, errp); if (ret < 0) { - error_report("Failed to send control buffer!"); - return ret; + return -1; } /* * Block and wait for the message. */ ret = qemu_rdma_exchange_get_response(rdma, head, - expecting, RDMA_WRID_READY); + expecting, RDMA_WRID_READY, errp); if (ret < 0) { - return ret; + return -1; } qemu_rdma_move_header(rdma, RDMA_WRID_READY, head); @@ -2074,10 +1977,9 @@ static int qemu_rdma_exchange_recv(RDMAContext *rdma, RDMAControlHeader *head, /* * Post a new RECV work request to replace the one we just consumed. */ - ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY); - if (ret) { - error_report("rdma migration: error posting second control recv!"); - return ret; + ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY, errp); + if (ret < 0) { + return -1; } return 0; @@ -2089,9 +1991,9 @@ static int qemu_rdma_exchange_recv(RDMAContext *rdma, RDMAControlHeader *head, * If we're using dynamic registration on the dest-side, we have to * send a registration command first. */ -static int qemu_rdma_write_one(QEMUFile *f, RDMAContext *rdma, +static int qemu_rdma_write_one(RDMAContext *rdma, int current_index, uint64_t current_addr, - uint64_t length) + uint64_t length, Error **errp) { struct ibv_sge sge; struct ibv_send_wr send_wr = { 0 }; @@ -2137,11 +2039,6 @@ retry: chunk_end = ram_chunk_end(block, chunk + chunks); - if (!rdma->pin_all) { -#ifdef RDMA_UNREGISTRATION_EXAMPLE - qemu_rdma_unregister_waiting(rdma); -#endif - } while (test_bit(chunk, block->transit_bitmap)) { (void)count; @@ -2151,11 +2048,11 @@ retry: ret = qemu_rdma_block_for_wrid(rdma, RDMA_WRID_RDMA_WRITE, NULL); if (ret < 0) { - error_report("Failed to Wait for previous write to complete " + error_setg(errp, "Failed to Wait for previous write to complete " "block %d chunk %" PRIu64 " current %" PRIu64 " len %" PRIu64 " %d", current_index, chunk, sge.addr, length, rdma->nb_sent); - return ret; + return -1; } } @@ -2183,14 +2080,24 @@ retry: compress_to_network(rdma, &comp); ret = qemu_rdma_exchange_send(rdma, &head, - (uint8_t *) &comp, NULL, NULL, NULL); + (uint8_t *) &comp, NULL, NULL, NULL, errp); if (ret < 0) { - return -EIO; + return -1; } - acct_update_position(f, sge.length, true); - + /* + * TODO: Here we are sending something, but we are not + * accounting for anything transferred. The following is wrong: + * + * stat64_add(&mig_stats.rdma_bytes, sge.length); + * + * because we are using some kind of compression. I + * would think that head.len would be the more similar + * thing to a correct value. + */ + stat64_add(&mig_stats.zero_pages, + sge.length / qemu_target_page_size()); return 1; } @@ -2210,17 +2117,17 @@ retry: register_to_network(rdma, ®); ret = qemu_rdma_exchange_send(rdma, &head, (uint8_t *) ®, - &resp, ®_result_idx, NULL); + &resp, ®_result_idx, NULL, errp); if (ret < 0) { - return ret; + return -1; } /* try to overlap this single registration with the one we sent. */ if (qemu_rdma_register_and_get_keys(rdma, block, sge.addr, &sge.lkey, NULL, chunk, chunk_start, chunk_end)) { - error_report("cannot get lkey"); - return -EINVAL; + error_setg(errp, "cannot get lkey"); + return -1; } reg_result = (RDMARegisterResult *) @@ -2238,8 +2145,8 @@ retry: if (qemu_rdma_register_and_get_keys(rdma, block, sge.addr, &sge.lkey, NULL, chunk, chunk_start, chunk_end)) { - error_report("cannot get lkey!"); - return -EINVAL; + error_setg(errp, "cannot get lkey!"); + return -1; } } @@ -2250,8 +2157,8 @@ retry: if (qemu_rdma_register_and_get_keys(rdma, block, sge.addr, &sge.lkey, NULL, chunk, chunk_start, chunk_end)) { - error_report("cannot get lkey!"); - return -EINVAL; + error_setg(errp, "cannot get lkey!"); + return -1; } } @@ -2284,20 +2191,32 @@ retry: trace_qemu_rdma_write_one_queue_full(); ret = qemu_rdma_block_for_wrid(rdma, RDMA_WRID_RDMA_WRITE, NULL); if (ret < 0) { - error_report("rdma migration: failed to make " - "room in full send queue! %d", ret); - return ret; + error_setg(errp, "rdma migration: failed to make " + "room in full send queue!"); + return -1; } goto retry; } else if (ret > 0) { - perror("rdma migration: post rdma write failed"); - return -ret; + error_setg_errno(errp, ret, + "rdma migration: post rdma write failed"); + return -1; } set_bit(chunk, block->transit_bitmap); - acct_update_position(f, sge.length, false); + stat64_add(&mig_stats.normal_pages, sge.length / qemu_target_page_size()); + /* + * We are adding to transferred the amount of data written, but no + * overhead at all. I will assume that RDMA is magicaly and don't + * need to transfer (at least) the addresses where it wants to + * write the pages. Here it looks like it should be something + * like: + * sizeof(send_wr) + sge.length + * but this being RDMA, who knows. + */ + stat64_add(&mig_stats.rdma_bytes, sge.length); + ram_transferred_add(sge.length); rdma->total_writes++; return 0; @@ -2309,7 +2228,7 @@ retry: * We support sending out multiple chunks at the same time. * Not all of them need to get signaled in the completion queue. */ -static int qemu_rdma_write_flush(QEMUFile *f, RDMAContext *rdma) +static int qemu_rdma_write_flush(RDMAContext *rdma, Error **errp) { int ret; @@ -2317,11 +2236,11 @@ static int qemu_rdma_write_flush(QEMUFile *f, RDMAContext *rdma) return 0; } - ret = qemu_rdma_write_one(f, rdma, - rdma->current_index, rdma->current_addr, rdma->current_length); + ret = qemu_rdma_write_one(rdma, rdma->current_index, rdma->current_addr, + rdma->current_length, errp); if (ret < 0) { - return ret; + return -1; } if (ret == 0) { @@ -2335,7 +2254,7 @@ static int qemu_rdma_write_flush(QEMUFile *f, RDMAContext *rdma) return 0; } -static inline int qemu_rdma_buffer_mergable(RDMAContext *rdma, +static inline bool qemu_rdma_buffer_mergeable(RDMAContext *rdma, uint64_t offset, uint64_t len) { RDMALocalBlock *block; @@ -2343,11 +2262,11 @@ static inline int qemu_rdma_buffer_mergable(RDMAContext *rdma, uint8_t *chunk_end; if (rdma->current_index < 0) { - return 0; + return false; } if (rdma->current_chunk < 0) { - return 0; + return false; } block = &(rdma->local_ram_blocks.block[rdma->current_index]); @@ -2355,29 +2274,29 @@ static inline int qemu_rdma_buffer_mergable(RDMAContext *rdma, chunk_end = ram_chunk_end(block, rdma->current_chunk); if (rdma->current_length == 0) { - return 0; + return false; } /* * Only merge into chunk sequentially. */ if (offset != (rdma->current_addr + rdma->current_length)) { - return 0; + return false; } if (offset < block->offset) { - return 0; + return false; } if ((offset + len) > (block->offset + block->length)) { - return 0; + return false; } if ((host_addr + len) > chunk_end) { - return 0; + return false; } - return 1; + return true; } /* @@ -2390,30 +2309,24 @@ static inline int qemu_rdma_buffer_mergable(RDMAContext *rdma, * and only require that a batch gets acknowledged in the completion * queue instead of each individual chunk. */ -static int qemu_rdma_write(QEMUFile *f, RDMAContext *rdma, +static int qemu_rdma_write(RDMAContext *rdma, uint64_t block_offset, uint64_t offset, - uint64_t len) + uint64_t len, Error **errp) { uint64_t current_addr = block_offset + offset; uint64_t index = rdma->current_index; uint64_t chunk = rdma->current_chunk; - int ret; /* If we cannot merge it, we flush the current buffer first. */ - if (!qemu_rdma_buffer_mergable(rdma, current_addr, len)) { - ret = qemu_rdma_write_flush(f, rdma); - if (ret) { - return ret; + if (!qemu_rdma_buffer_mergeable(rdma, current_addr, len)) { + if (qemu_rdma_write_flush(rdma, errp) < 0) { + return -1; } rdma->current_length = 0; rdma->current_addr = current_addr; - ret = qemu_rdma_search_ram_block(rdma, block_offset, - offset, len, &index, &chunk); - if (ret) { - error_report("ram block search failed"); - return ret; - } + qemu_rdma_search_ram_block(rdma, block_offset, + offset, len, &index, &chunk); rdma->current_index = index; rdma->current_chunk = chunk; } @@ -2423,7 +2336,7 @@ static int qemu_rdma_write(QEMUFile *f, RDMAContext *rdma, /* flush it if buffer is too large */ if (rdma->current_length >= RDMA_MERGE_MAX) { - return qemu_rdma_write_flush(f, rdma); + return qemu_rdma_write_flush(rdma, errp); } return 0; @@ -2431,18 +2344,20 @@ static int qemu_rdma_write(QEMUFile *f, RDMAContext *rdma, static void qemu_rdma_cleanup(RDMAContext *rdma) { - int idx; + Error *err = NULL; if (rdma->cm_id && rdma->connected) { - if ((rdma->error_state || + if ((rdma->errored || migrate_get_current()->state == MIGRATION_STATUS_CANCELLING) && !rdma->received_error) { RDMAControlHeader head = { .len = 0, .type = RDMA_CONTROL_ERROR, .repeat = 1, }; - error_report("Early error. Sending error."); - qemu_rdma_post_send_control(rdma, NULL, &head); + warn_report("Early error. Sending error."); + if (qemu_rdma_post_send_control(rdma, NULL, &head, &err) < 0) { + warn_report_err(err); + } } rdma_disconnect(rdma->cm_id); @@ -2456,12 +2371,12 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) g_free(rdma->dest_blocks); rdma->dest_blocks = NULL; - for (idx = 0; idx < RDMA_WRID_MAX; idx++) { - if (rdma->wr_data[idx].control_mr) { + for (int i = 0; i < RDMA_WRID_MAX; i++) { + if (rdma->wr_data[i].control_mr) { rdma->total_registrations--; - ibv_dereg_mr(rdma->wr_data[idx].control_mr); + ibv_dereg_mr(rdma->wr_data[i].control_mr); } - rdma->wr_data[idx].control_mr = NULL; + rdma->wr_data[i].control_mr = NULL; } if (rdma->local_ram_blocks.block) { @@ -2519,16 +2434,13 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) rdma->channel = NULL; } g_free(rdma->host); - g_free(rdma->host_port); rdma->host = NULL; - rdma->host_port = NULL; } static int qemu_rdma_source_init(RDMAContext *rdma, bool pin_all, Error **errp) { - int ret, idx; - Error *local_err = NULL, **temp = &local_err; + int ret; /* * Will be validated against destination's actual capabilities @@ -2536,44 +2448,37 @@ static int qemu_rdma_source_init(RDMAContext *rdma, bool pin_all, Error **errp) */ rdma->pin_all = pin_all; - ret = qemu_rdma_resolve_host(rdma, temp); - if (ret) { + ret = qemu_rdma_resolve_host(rdma, errp); + if (ret < 0) { goto err_rdma_source_init; } - ret = qemu_rdma_alloc_pd_cq(rdma); - if (ret) { - ERROR(temp, "rdma migration: error allocating pd and cq! Your mlock()" - " limits may be too low. Please check $ ulimit -a # and " - "search for 'ulimit -l' in the output"); + ret = qemu_rdma_alloc_pd_cq(rdma, errp); + if (ret < 0) { goto err_rdma_source_init; } ret = qemu_rdma_alloc_qp(rdma); - if (ret) { - ERROR(temp, "rdma migration: error allocating qp!"); + if (ret < 0) { + error_setg(errp, "RDMA ERROR: rdma migration: error allocating qp!"); goto err_rdma_source_init; } - ret = qemu_rdma_init_ram_blocks(rdma); - if (ret) { - ERROR(temp, "rdma migration: error initializing ram blocks!"); - goto err_rdma_source_init; - } + qemu_rdma_init_ram_blocks(rdma); /* Build the hash that maps from offset to RAMBlock */ rdma->blockmap = g_hash_table_new(g_direct_hash, g_direct_equal); - for (idx = 0; idx < rdma->local_ram_blocks.nb_blocks; idx++) { + for (int i = 0; i < rdma->local_ram_blocks.nb_blocks; i++) { g_hash_table_insert(rdma->blockmap, - (void *)(uintptr_t)rdma->local_ram_blocks.block[idx].offset, - &rdma->local_ram_blocks.block[idx]); + (void *)(uintptr_t)rdma->local_ram_blocks.block[i].offset, + &rdma->local_ram_blocks.block[i]); } - for (idx = 0; idx < RDMA_WRID_MAX; idx++) { - ret = qemu_rdma_reg_control(rdma, idx); - if (ret) { - ERROR(temp, "rdma migration: error registering %d control!", - idx); + for (int i = 0; i < RDMA_WRID_MAX; i++) { + ret = qemu_rdma_reg_control(rdma, i); + if (ret < 0) { + error_setg(errp, "RDMA ERROR: rdma migration: error " + "registering %d control!", i); goto err_rdma_source_init; } } @@ -2581,7 +2486,6 @@ static int qemu_rdma_source_init(RDMAContext *rdma, bool pin_all, Error **errp) return 0; err_rdma_source_init: - error_propagate(errp, local_err); qemu_rdma_cleanup(rdma); return -1; } @@ -2602,20 +2506,27 @@ static int qemu_get_cm_event_timeout(RDMAContext *rdma, } while (ret < 0 && errno == EINTR); if (ret == 0) { - ERROR(errp, "poll cm event timeout"); + error_setg(errp, "RDMA ERROR: poll cm event timeout"); return -1; } else if (ret < 0) { - ERROR(errp, "failed to poll cm event, errno=%i", errno); + error_setg(errp, "RDMA ERROR: failed to poll cm event, errno=%i", + errno); return -1; } else if (poll_fd.revents & POLLIN) { - return rdma_get_cm_event(rdma->channel, cm_event); + if (rdma_get_cm_event(rdma->channel, cm_event) < 0) { + error_setg(errp, "RDMA ERROR: failed to get cm event"); + return -1; + } + return 0; } else { - ERROR(errp, "no POLLIN event, revent=%x", poll_fd.revents); + error_setg(errp, "RDMA ERROR: no POLLIN event, revent=%x", + poll_fd.revents); return -1; } } -static int qemu_rdma_connect(RDMAContext *rdma, Error **errp, bool return_path) +static int qemu_rdma_connect(RDMAContext *rdma, bool return_path, + Error **errp) { RDMACapabilities cap = { .version = RDMA_CONTROL_VERSION_CURRENT, @@ -2640,16 +2551,15 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp, bool return_path) caps_to_network(&cap); - ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY); - if (ret) { - ERROR(errp, "posting second control recv"); + ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY, errp); + if (ret < 0) { goto err_rdma_source_connect; } ret = rdma_connect(rdma->cm_id, &conn_param); - if (ret) { - perror("rdma_connect"); - ERROR(errp, "connecting to destination!"); + if (ret < 0) { + error_setg_errno(errp, errno, + "RDMA ERROR: connecting to destination!"); goto err_rdma_source_connect; } @@ -2657,16 +2567,17 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp, bool return_path) ret = qemu_get_cm_event_timeout(rdma, &cm_event, 5000, errp); } else { ret = rdma_get_cm_event(rdma->channel, &cm_event); + if (ret < 0) { + error_setg_errno(errp, errno, + "RDMA ERROR: failed to get cm event"); + } } - if (ret) { - perror("rdma_get_cm_event after rdma_connect"); - ERROR(errp, "connecting to destination!"); + if (ret < 0) { goto err_rdma_source_connect; } if (cm_event->event != RDMA_CM_EVENT_ESTABLISHED) { - error_report("rdma_get_cm_event != EVENT_ESTABLISHED after rdma_connect"); - ERROR(errp, "connecting to destination!"); + error_setg(errp, "RDMA ERROR: connecting to destination!"); rdma_ack_cm_event(cm_event); goto err_rdma_source_connect; } @@ -2680,8 +2591,8 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp, bool return_path) * and disable them otherwise. */ if (rdma->pin_all && !(cap.flags & RDMA_CAPABILITY_PIN_ALL)) { - ERROR(errp, "Server cannot support pinning all memory. " - "Will register memory dynamically."); + warn_report("RDMA: Server cannot support pinning all memory. " + "Will register memory dynamically."); rdma->pin_all = false; } @@ -2700,35 +2611,36 @@ err_rdma_source_connect: static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp) { - int ret, idx; + Error *err = NULL; + int ret; struct rdma_cm_id *listen_id; char ip[40] = "unknown"; struct rdma_addrinfo *res, *e; char port_str[16]; int reuse = 1; - for (idx = 0; idx < RDMA_WRID_MAX; idx++) { - rdma->wr_data[idx].control_len = 0; - rdma->wr_data[idx].control_curr = NULL; + for (int i = 0; i < RDMA_WRID_MAX; i++) { + rdma->wr_data[i].control_len = 0; + rdma->wr_data[i].control_curr = NULL; } if (!rdma->host || !rdma->host[0]) { - ERROR(errp, "RDMA host is not set!"); - rdma->error_state = -EINVAL; + error_setg(errp, "RDMA ERROR: RDMA host is not set!"); + rdma->errored = true; return -1; } /* create CM channel */ rdma->channel = rdma_create_event_channel(); if (!rdma->channel) { - ERROR(errp, "could not create rdma event channel"); - rdma->error_state = -EINVAL; + error_setg(errp, "RDMA ERROR: could not create rdma event channel"); + rdma->errored = true; return -1; } /* create CM id */ ret = rdma_create_id(rdma->channel, &listen_id, NULL, RDMA_PS_TCP); - if (ret) { - ERROR(errp, "could not create cm_id!"); + if (ret < 0) { + error_setg(errp, "RDMA ERROR: could not create cm_id!"); goto err_dest_init_create_listen_id; } @@ -2736,37 +2648,48 @@ static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp) port_str[15] = '\0'; ret = rdma_getaddrinfo(rdma->host, port_str, NULL, &res); - if (ret < 0) { - ERROR(errp, "could not rdma_getaddrinfo address %s", rdma->host); + if (ret) { + error_setg(errp, "RDMA ERROR: could not rdma_getaddrinfo address %s", + rdma->host); goto err_dest_init_bind_addr; } ret = rdma_set_option(listen_id, RDMA_OPTION_ID, RDMA_OPTION_ID_REUSEADDR, &reuse, sizeof reuse); - if (ret) { - ERROR(errp, "Error: could not set REUSEADDR option"); + if (ret < 0) { + error_setg(errp, "RDMA ERROR: Error: could not set REUSEADDR option"); goto err_dest_init_bind_addr; } + + /* Try all addresses, saving the first error in @err */ for (e = res; e != NULL; e = e->ai_next) { + Error **local_errp = err ? NULL : &err; + inet_ntop(e->ai_family, &((struct sockaddr_in *) e->ai_dst_addr)->sin_addr, ip, sizeof ip); trace_qemu_rdma_dest_init_trying(rdma->host, ip); ret = rdma_bind_addr(listen_id, e->ai_dst_addr); - if (ret) { + if (ret < 0) { continue; } if (e->ai_family == AF_INET6) { - ret = qemu_rdma_broken_ipv6_kernel(listen_id->verbs, errp); - if (ret) { + ret = qemu_rdma_broken_ipv6_kernel(listen_id->verbs, + local_errp); + if (ret < 0) { continue; } } + error_free(err); break; } rdma_freeaddrinfo(res); if (!e) { - ERROR(errp, "Error: could not rdma_bind_addr!"); + if (err) { + error_propagate(errp, err); + } else { + error_setg(errp, "RDMA ERROR: Error: could not rdma_bind_addr!"); + } goto err_dest_init_bind_addr; } @@ -2779,19 +2702,17 @@ err_dest_init_bind_addr: err_dest_init_create_listen_id: rdma_destroy_event_channel(rdma->channel); rdma->channel = NULL; - rdma->error_state = ret; - return ret; + rdma->errored = true; + return -1; } static void qemu_rdma_return_path_dest_init(RDMAContext *rdma_return_path, RDMAContext *rdma) { - int idx; - - for (idx = 0; idx < RDMA_WRID_MAX; idx++) { - rdma_return_path->wr_data[idx].control_len = 0; - rdma_return_path->wr_data[idx].control_curr = NULL; + for (int i = 0; i < RDMA_WRID_MAX; i++) { + rdma_return_path->wr_data[i].control_len = 0; + rdma_return_path->wr_data[i].control_curr = NULL; } /*the CM channel and CM id is shared*/ @@ -2803,30 +2724,16 @@ static void qemu_rdma_return_path_dest_init(RDMAContext *rdma_return_path, rdma_return_path->is_return_path = true; } -static void *qemu_rdma_data_init(const char *host_port, Error **errp) +static RDMAContext *qemu_rdma_data_init(InetSocketAddress *saddr, Error **errp) { RDMAContext *rdma = NULL; - InetSocketAddress *addr; - if (host_port) { - rdma = g_new0(RDMAContext, 1); - rdma->current_index = -1; - rdma->current_chunk = -1; - - addr = g_new(InetSocketAddress, 1); - if (!inet_parse(addr, host_port, NULL)) { - rdma->port = atoi(addr->port); - rdma->host = g_strdup(addr->host); - rdma->host_port = g_strdup(host_port); - } else { - ERROR(errp, "bad RDMA migration address '%s'", host_port); - g_free(rdma); - rdma = NULL; - } - - qapi_free_InetSocketAddress(addr); - } + rdma = g_new0(RDMAContext, 1); + rdma->current_index = -1; + rdma->current_chunk = -1; + rdma->host = g_strdup(saddr->host); + rdma->port = atoi(saddr->port); return rdma; } @@ -2844,37 +2751,40 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, Error **errp) { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); - QEMUFile *f = rioc->file; RDMAContext *rdma; int ret; ssize_t done = 0; - size_t i; - size_t len = 0; + size_t len; RCU_READ_LOCK_GUARD(); rdma = qatomic_rcu_read(&rioc->rdmaout); if (!rdma) { - return -EIO; + error_setg(errp, "RDMA control channel output is not set"); + return -1; } - CHECK_ERROR_STATE(); + if (rdma->errored) { + error_setg(errp, + "RDMA is in an error state waiting migration to abort!"); + return -1; + } /* * Push out any writes that * we're queued up for VM's ram. */ - ret = qemu_rdma_write_flush(f, rdma); + ret = qemu_rdma_write_flush(rdma, errp); if (ret < 0) { - rdma->error_state = ret; - return ret; + rdma->errored = true; + return -1; } - for (i = 0; i < niov; i++) { + for (int i = 0; i < niov; i++) { size_t remaining = iov[i].iov_len; uint8_t * data = (void *)iov[i].iov_base; while (remaining) { - RDMAControlHeader head; + RDMAControlHeader head = {}; len = MIN(remaining, RDMA_SEND_INCREMENT); remaining -= len; @@ -2882,11 +2792,12 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, head.len = len; head.type = RDMA_CONTROL_QEMU_FILE; - ret = qemu_rdma_exchange_send(rdma, &head, data, NULL, NULL, NULL); + ret = qemu_rdma_exchange_send(rdma, &head, + data, NULL, NULL, NULL, errp); if (ret < 0) { - rdma->error_state = ret; - return ret; + rdma->errored = true; + return -1; } data += len; @@ -2924,25 +2835,31 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp) { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); RDMAContext *rdma; RDMAControlHeader head; - int ret = 0; - ssize_t i; - size_t done = 0; + int ret; + ssize_t done = 0; + size_t len; RCU_READ_LOCK_GUARD(); rdma = qatomic_rcu_read(&rioc->rdmain); if (!rdma) { - return -EIO; + error_setg(errp, "RDMA control channel input is not set"); + return -1; } - CHECK_ERROR_STATE(); + if (rdma->errored) { + error_setg(errp, + "RDMA is in an error state waiting migration to abort!"); + return -1; + } - for (i = 0; i < niov; i++) { + for (int i = 0; i < niov; i++) { size_t want = iov[i].iov_len; uint8_t *data = (void *)iov[i].iov_base; @@ -2951,9 +2868,9 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, * were given and dish out the bytes until we run * out of bytes. */ - ret = qemu_rdma_fill(rdma, data, want, 0); - done += ret; - want -= ret; + len = qemu_rdma_fill(rdma, data, want, 0); + done += len; + want -= len; /* Got what we needed, so go to next iovec */ if (want == 0) { continue; @@ -2969,19 +2886,20 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, /* We've got nothing at all, so lets wait for * more to arrive */ - ret = qemu_rdma_exchange_recv(rdma, &head, RDMA_CONTROL_QEMU_FILE); + ret = qemu_rdma_exchange_recv(rdma, &head, RDMA_CONTROL_QEMU_FILE, + errp); if (ret < 0) { - rdma->error_state = ret; - return ret; + rdma->errored = true; + return -1; } /* * SEND was received with new bytes, now try again. */ - ret = qemu_rdma_fill(rdma, data, want, 0); - done += ret; - want -= ret; + len = qemu_rdma_fill(rdma, data, want, 0); + done += len; + want -= len; /* Still didn't get enough, so lets just return */ if (want) { @@ -2998,19 +2916,19 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, /* * Block until all the outstanding chunks have been delivered by the hardware. */ -static int qemu_rdma_drain_cq(QEMUFile *f, RDMAContext *rdma) +static int qemu_rdma_drain_cq(RDMAContext *rdma) { - int ret; + Error *err = NULL; - if (qemu_rdma_write_flush(f, rdma) < 0) { - return -EIO; + if (qemu_rdma_write_flush(rdma, &err) < 0) { + error_report_err(err); + return -1; } while (rdma->nb_sent) { - ret = qemu_rdma_block_for_wrid(rdma, RDMA_WRID_RDMA_WRITE, NULL); - if (ret < 0) { + if (qemu_rdma_block_for_wrid(rdma, RDMA_WRID_RDMA_WRITE, NULL) < 0) { error_report("rdma migration: complete polling error!"); - return -EIO; + return -1; } } @@ -3134,7 +3052,7 @@ qio_channel_rdma_source_finalize(GSource *source) object_unref(OBJECT(ssource->rioc)); } -GSourceFuncs qio_channel_rdma_source_funcs = { +static GSourceFuncs qio_channel_rdma_source_funcs = { qio_channel_rdma_source_prepare, qio_channel_rdma_source_check, qio_channel_rdma_source_dispatch, @@ -3161,22 +3079,23 @@ static GSource *qio_channel_rdma_create_watch(QIOChannel *ioc, } static void qio_channel_rdma_set_aio_fd_handler(QIOChannel *ioc, - AioContext *ctx, - IOHandler *io_read, - IOHandler *io_write, - void *opaque) + AioContext *read_ctx, + IOHandler *io_read, + AioContext *write_ctx, + IOHandler *io_write, + void *opaque) { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); if (io_read) { - aio_set_fd_handler(ctx, rioc->rdmain->recv_comp_channel->fd, - false, io_read, io_write, NULL, NULL, opaque); - aio_set_fd_handler(ctx, rioc->rdmain->send_comp_channel->fd, - false, io_read, io_write, NULL, NULL, opaque); + aio_set_fd_handler(read_ctx, rioc->rdmain->recv_comp_channel->fd, + io_read, io_write, NULL, NULL, opaque); + aio_set_fd_handler(read_ctx, rioc->rdmain->send_comp_channel->fd, + io_read, io_write, NULL, NULL, opaque); } else { - aio_set_fd_handler(ctx, rioc->rdmaout->recv_comp_channel->fd, - false, io_read, io_write, NULL, NULL, opaque); - aio_set_fd_handler(ctx, rioc->rdmaout->send_comp_channel->fd, - false, io_read, io_write, NULL, NULL, opaque); + aio_set_fd_handler(write_ctx, rioc->rdmaout->recv_comp_channel->fd, + io_read, io_write, NULL, NULL, opaque); + aio_set_fd_handler(write_ctx, rioc->rdmaout->send_comp_channel->fd, + io_read, io_write, NULL, NULL, opaque); } } @@ -3244,21 +3163,21 @@ qio_channel_rdma_shutdown(QIOChannel *ioc, switch (how) { case QIO_CHANNEL_SHUTDOWN_READ: if (rdmain) { - rdmain->error_state = -1; + rdmain->errored = true; } break; case QIO_CHANNEL_SHUTDOWN_WRITE: if (rdmaout) { - rdmaout->error_state = -1; + rdmaout->errored = true; } break; case QIO_CHANNEL_SHUTDOWN_BOTH: default: if (rdmain) { - rdmain->error_state = -1; + rdmain->errored = true; } if (rdmaout) { - rdmaout->error_state = -1; + rdmaout->errored = true; } break; } @@ -3278,33 +3197,17 @@ qio_channel_rdma_shutdown(QIOChannel *ioc, * Offset is an offset to be added to block_offset and used * to also lookup the corresponding RAMBlock. * - * @size > 0 : - * Initiate an transfer this size. + * @size : Number of bytes to transfer * - * @size == 0 : - * A 'hint' or 'advice' that means that we wish to speculatively - * and asynchronously unregister this memory. In this case, there is no - * guarantee that the unregister will actually happen, for example, - * if the memory is being actively transmitted. Additionally, the memory - * may be re-registered at any future time if a write within the same - * chunk was requested again, even if you attempted to unregister it - * here. - * - * @size < 0 : TODO, not yet supported - * Unregister the memory NOW. This means that the caller does not - * expect there to be any future RDMA transfers and we just want to clean - * things up. This is used in case the upper layer owns the memory and - * cannot wait for qemu_fclose() to occur. - * - * @bytes_sent : User-specificed pointer to indicate how many bytes were + * @pages_sent : User-specificed pointer to indicate how many pages were * sent. Usually, this will not be more than a few bytes of * the protocol because most transfers are sent asynchronously. */ -static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, - ram_addr_t block_offset, ram_addr_t offset, - size_t size, uint64_t *bytes_sent) +static int qemu_rdma_save_page(QEMUFile *f, ram_addr_t block_offset, + ram_addr_t offset, size_t size) { - QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); + QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); + Error *err = NULL; RDMAContext *rdma; int ret; @@ -3312,72 +3215,24 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, rdma = qatomic_rcu_read(&rioc->rdmaout); if (!rdma) { - return -EIO; + return -1; } - CHECK_ERROR_STATE(); - - if (migration_in_postcopy()) { - return RAM_SAVE_CONTROL_NOT_SUPP; + if (rdma_errored(rdma)) { + return -1; } qemu_fflush(f); - if (size > 0) { - /* - * Add this page to the current 'chunk'. If the chunk - * is full, or the page doesn't belong to the current chunk, - * an actual RDMA write will occur and a new chunk will be formed. - */ - ret = qemu_rdma_write(f, rdma, block_offset, offset, size); - if (ret < 0) { - error_report("rdma migration: write error! %d", ret); - goto err; - } - - /* - * We always return 1 bytes because the RDMA - * protocol is completely asynchronous. We do not yet know - * whether an identified chunk is zero or not because we're - * waiting for other pages to potentially be merged with - * the current chunk. So, we have to call qemu_update_position() - * later on when the actual write occurs. - */ - if (bytes_sent) { - *bytes_sent = 1; - } - } else { - uint64_t index, chunk; - - /* TODO: Change QEMUFileOps prototype to be signed: size_t => long - if (size < 0) { - ret = qemu_rdma_drain_cq(f, rdma); - if (ret < 0) { - fprintf(stderr, "rdma: failed to synchronously drain" - " completion queue before unregistration.\n"); - goto err; - } - } - */ - - ret = qemu_rdma_search_ram_block(rdma, block_offset, - offset, size, &index, &chunk); - - if (ret) { - error_report("ram block search failed"); - goto err; - } - - qemu_rdma_signal_unregister(rdma, index, chunk, 0); - - /* - * TODO: Synchronous, guaranteed unregistration (should not occur during - * fast-path). Otherwise, unregisters will process on the next call to - * qemu_rdma_drain_cq() - if (size < 0) { - qemu_rdma_unregister_waiting(rdma); - } - */ + /* + * Add this page to the current 'chunk'. If the chunk + * is full, or the page doesn't belong to the current chunk, + * an actual RDMA write will occur and a new chunk will be formed. + */ + ret = qemu_rdma_write(rdma, block_offset, offset, size, &err); + if (ret < 0) { + error_report_err(err); + goto err; } /* @@ -3389,9 +3244,10 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, */ while (1) { uint64_t wr_id, wr_id_in; - int ret = qemu_rdma_poll(rdma, rdma->recv_cq, &wr_id_in, NULL); + ret = qemu_rdma_poll(rdma, rdma->recv_cq, &wr_id_in, NULL); + if (ret < 0) { - error_report("rdma migration: polling error! %d", ret); + error_report("rdma migration: polling error"); goto err; } @@ -3404,9 +3260,10 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, while (1) { uint64_t wr_id, wr_id_in; - int ret = qemu_rdma_poll(rdma, rdma->send_cq, &wr_id_in, NULL); + ret = qemu_rdma_poll(rdma, rdma->send_cq, &wr_id_in, NULL); + if (ret < 0) { - error_report("rdma migration: polling error! %d", ret); + error_report("rdma migration: polling error"); goto err; } @@ -3418,8 +3275,27 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, } return RAM_SAVE_CONTROL_DELAYED; + err: - rdma->error_state = ret; + rdma->errored = true; + return -1; +} + +int rdma_control_save_page(QEMUFile *f, ram_addr_t block_offset, + ram_addr_t offset, size_t size) +{ + if (!migrate_rdma() || migration_in_postcopy()) { + return RAM_SAVE_CONTROL_NOT_SUPP; + } + + int ret = qemu_rdma_save_page(f, block_offset, offset, size); + + if (ret != RAM_SAVE_CONTROL_DELAYED && + ret != RAM_SAVE_CONTROL_NOT_SUPP) { + if (ret < 0) { + qemu_file_set_error(f, ret); + } + } return ret; } @@ -3428,31 +3304,28 @@ static void rdma_accept_incoming_migration(void *opaque); static void rdma_cm_poll_handler(void *opaque) { RDMAContext *rdma = opaque; - int ret; struct rdma_cm_event *cm_event; MigrationIncomingState *mis = migration_incoming_get_current(); - ret = rdma_get_cm_event(rdma->channel, &cm_event); - if (ret) { + if (rdma_get_cm_event(rdma->channel, &cm_event) < 0) { error_report("get_cm_event failed %d", errno); return; } if (cm_event->event == RDMA_CM_EVENT_DISCONNECTED || cm_event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) { - if (!rdma->error_state && + if (!rdma->errored && migration_incoming_get_current()->state != MIGRATION_STATUS_COMPLETED) { error_report("receive cm event, cm event is %d", cm_event->event); - rdma->error_state = -EPIPE; + rdma->errored = true; if (rdma->return_path) { - rdma->return_path->error_state = -EPIPE; + rdma->return_path->errored = true; } } rdma_ack_cm_event(cm_event); - - if (mis->migration_incoming_co) { - qemu_coroutine_enter(mis->migration_incoming_co); + if (mis->loadvm_co) { + qemu_coroutine_enter(mis->loadvm_co); } return; } @@ -3461,6 +3334,7 @@ static void rdma_cm_poll_handler(void *opaque) static int qemu_rdma_accept(RDMAContext *rdma) { + Error *err = NULL; RDMACapabilities cap; struct rdma_conn_param conn_param = { .responder_resources = 2, @@ -3468,13 +3342,13 @@ static int qemu_rdma_accept(RDMAContext *rdma) .private_data_len = sizeof(cap), }; RDMAContext *rdma_return_path = NULL; + g_autoptr(InetSocketAddress) isock = g_new0(InetSocketAddress, 1); struct rdma_cm_event *cm_event; struct ibv_context *verbs; - int ret = -EINVAL; - int idx; + int ret; ret = rdma_get_cm_event(rdma->channel, &cm_event); - if (ret) { + if (ret < 0) { goto err_rdma_dest_wait; } @@ -3483,12 +3357,16 @@ static int qemu_rdma_accept(RDMAContext *rdma) goto err_rdma_dest_wait; } + isock->host = g_strdup(rdma->host); + isock->port = g_strdup_printf("%d", rdma->port); + /* * initialize the RDMAContext for return path for postcopy after first * connection request reached. */ - if (migrate_postcopy() && !rdma->is_return_path) { - rdma_return_path = qemu_rdma_data_init(rdma->host_port, NULL); + if ((migrate_postcopy() || migrate_return_path()) + && !rdma->is_return_path) { + rdma_return_path = qemu_rdma_data_init(isock, NULL); if (rdma_return_path == NULL) { rdma_ack_cm_event(cm_event); goto err_rdma_dest_wait; @@ -3502,10 +3380,10 @@ static int qemu_rdma_accept(RDMAContext *rdma) network_to_caps(&cap); if (cap.version < 1 || cap.version > RDMA_CONTROL_VERSION_CURRENT) { - error_report("Unknown source RDMA version: %d, bailing...", - cap.version); - rdma_ack_cm_event(cm_event); - goto err_rdma_dest_wait; + error_report("Unknown source RDMA version: %d, bailing...", + cap.version); + rdma_ack_cm_event(cm_event); + goto err_rdma_dest_wait; } /* @@ -3535,41 +3413,38 @@ static int qemu_rdma_accept(RDMAContext *rdma) if (!rdma->verbs) { rdma->verbs = verbs; } else if (rdma->verbs != verbs) { - error_report("ibv context not matching %p, %p!", rdma->verbs, - verbs); - goto err_rdma_dest_wait; + error_report("ibv context not matching %p, %p!", rdma->verbs, + verbs); + goto err_rdma_dest_wait; } qemu_rdma_dump_id("dest_init", verbs); - ret = qemu_rdma_alloc_pd_cq(rdma); - if (ret) { - error_report("rdma migration: error allocating pd and cq!"); + ret = qemu_rdma_alloc_pd_cq(rdma, &err); + if (ret < 0) { + error_report_err(err); goto err_rdma_dest_wait; } ret = qemu_rdma_alloc_qp(rdma); - if (ret) { + if (ret < 0) { error_report("rdma migration: error allocating qp!"); goto err_rdma_dest_wait; } - ret = qemu_rdma_init_ram_blocks(rdma); - if (ret) { - error_report("rdma migration: error initializing ram blocks!"); - goto err_rdma_dest_wait; - } + qemu_rdma_init_ram_blocks(rdma); - for (idx = 0; idx < RDMA_WRID_MAX; idx++) { - ret = qemu_rdma_reg_control(rdma, idx); - if (ret) { - error_report("rdma: error registering %d control", idx); + for (int i = 0; i < RDMA_WRID_MAX; i++) { + ret = qemu_rdma_reg_control(rdma, i); + if (ret < 0) { + error_report("rdma: error registering %d control", i); goto err_rdma_dest_wait; } } /* Accept the second connection request for return path */ - if (migrate_postcopy() && !rdma->is_return_path) { + if ((migrate_postcopy() || migrate_return_path()) + && !rdma->is_return_path) { qemu_set_fd_handler(rdma->channel->fd, rdma_accept_incoming_migration, NULL, (void *)(intptr_t)rdma->return_path); @@ -3579,14 +3454,14 @@ static int qemu_rdma_accept(RDMAContext *rdma) } ret = rdma_accept(rdma->cm_id, &conn_param); - if (ret) { - error_report("rdma_accept returns %d", ret); + if (ret < 0) { + error_report("rdma_accept failed"); goto err_rdma_dest_wait; } ret = rdma_get_cm_event(rdma->channel, &cm_event); - if (ret) { - error_report("rdma_accept get_cm_event failed %d", ret); + if (ret < 0) { + error_report("rdma_accept get_cm_event failed"); goto err_rdma_dest_wait; } @@ -3599,9 +3474,9 @@ static int qemu_rdma_accept(RDMAContext *rdma) rdma_ack_cm_event(cm_event); rdma->connected = true; - ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY); - if (ret) { - error_report("rdma migration: error posting second control recv"); + ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY, &err); + if (ret < 0) { + error_report_err(err); goto err_rdma_dest_wait; } @@ -3610,10 +3485,10 @@ static int qemu_rdma_accept(RDMAContext *rdma) return 0; err_rdma_dest_wait: - rdma->error_state = ret; + rdma->errored = true; qemu_rdma_cleanup(rdma); g_free(rdma_return_path); - return ret; + return -1; } static int dest_ram_sort_func(const void *a, const void *b) @@ -3633,7 +3508,7 @@ static int dest_ram_sort_func(const void *a, const void *b) * * Keep doing this until the source tells us to stop. */ -static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) +int rdma_registration_handle(QEMUFile *f) { RDMAControlHeader reg_resp = { .len = sizeof(RDMARegisterResult), .type = RDMA_CONTROL_REGISTER_RESULT, @@ -3645,7 +3520,8 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) }; RDMAControlHeader blocks = { .type = RDMA_CONTROL_RAM_BLOCKS_RESULT, .repeat = 1 }; - QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); + QIOChannelRDMA *rioc; + Error *err = NULL; RDMAContext *rdma; RDMALocalBlocks *local; RDMAControlHeader head; @@ -3655,34 +3531,39 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) static RDMARegisterResult results[RDMA_CONTROL_MAX_COMMANDS_PER_MESSAGE]; RDMALocalBlock *block; void *host_addr; - int ret = 0; + int ret; int idx = 0; - int count = 0; - int i = 0; + + if (!migrate_rdma()) { + return 0; + } RCU_READ_LOCK_GUARD(); + rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); rdma = qatomic_rcu_read(&rioc->rdmain); if (!rdma) { - return -EIO; + return -1; } - CHECK_ERROR_STATE(); + if (rdma_errored(rdma)) { + return -1; + } local = &rdma->local_ram_blocks; do { - trace_qemu_rdma_registration_handle_wait(); + trace_rdma_registration_handle_wait(); - ret = qemu_rdma_exchange_recv(rdma, &head, RDMA_CONTROL_NONE); + ret = qemu_rdma_exchange_recv(rdma, &head, RDMA_CONTROL_NONE, &err); if (ret < 0) { + error_report_err(err); break; } if (head.repeat > RDMA_CONTROL_MAX_COMMANDS_PER_MESSAGE) { error_report("rdma: Too many requests in this message (%d)." "Bailing.", head.repeat); - ret = -EIO; break; } @@ -3691,30 +3572,33 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) comp = (RDMACompress *) rdma->wr_data[idx].control_curr; network_to_compress(comp); - trace_qemu_rdma_registration_handle_compress(comp->length, - comp->block_idx, - comp->offset); + trace_rdma_registration_handle_compress(comp->length, + comp->block_idx, + comp->offset); if (comp->block_idx >= rdma->local_ram_blocks.nb_blocks) { error_report("rdma: 'compress' bad block index %u (vs %d)", (unsigned int)comp->block_idx, rdma->local_ram_blocks.nb_blocks); - ret = -EIO; - goto out; + goto err; } block = &(rdma->local_ram_blocks.block[comp->block_idx]); host_addr = block->local_host_addr + (comp->offset - block->offset); - - ram_handle_compressed(host_addr, comp->value, comp->length); + if (comp->value) { + error_report("rdma: Zero page with non-zero (%d) value", + comp->value); + goto err; + } + ram_handle_zero(host_addr, comp->length); break; case RDMA_CONTROL_REGISTER_FINISHED: - trace_qemu_rdma_registration_handle_finished(); - goto out; + trace_rdma_registration_handle_finished(); + return 0; case RDMA_CONTROL_RAM_BLOCKS_REQUEST: - trace_qemu_rdma_registration_handle_ram_blocks(); + trace_rdma_registration_handle_ram_blocks(); /* Sort our local RAM Block list so it's the same as the source, * we can do this since we've filled in a src_index in the list @@ -3723,16 +3607,15 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) qsort(rdma->local_ram_blocks.block, rdma->local_ram_blocks.nb_blocks, sizeof(RDMALocalBlock), dest_ram_sort_func); - for (i = 0; i < local->nb_blocks; i++) { + for (int i = 0; i < local->nb_blocks; i++) { local->block[i].index = i; } if (rdma->pin_all) { - ret = qemu_rdma_reg_whole_ram_blocks(rdma); - if (ret) { - error_report("rdma migration: error dest " - "registering ram blocks"); - goto out; + ret = qemu_rdma_reg_whole_ram_blocks(rdma, &err); + if (ret < 0) { + error_report_err(err); + goto err; } } @@ -3742,7 +3625,7 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) * Both sides use the "remote" structure to communicate and update * their "local" descriptions with what was sent. */ - for (i = 0; i < local->nb_blocks; i++) { + for (int i = 0; i < local->nb_blocks; i++) { rdma->dest_blocks[i].remote_host_addr = (uintptr_t)(local->block[i].local_host_addr); @@ -3754,7 +3637,7 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) rdma->dest_blocks[i].length = local->block[i].length; dest_block_to_network(&rdma->dest_blocks[i]); - trace_qemu_rdma_registration_handle_ram_blocks_loop( + trace_rdma_registration_handle_ram_blocks_loop( local->block[i].block_name, local->block[i].offset, local->block[i].length, @@ -3767,21 +3650,22 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) ret = qemu_rdma_post_send_control(rdma, - (uint8_t *) rdma->dest_blocks, &blocks); + (uint8_t *) rdma->dest_blocks, &blocks, + &err); if (ret < 0) { - error_report("rdma migration: error sending remote info"); - goto out; + error_report_err(err); + goto err; } break; case RDMA_CONTROL_REGISTER_REQUEST: - trace_qemu_rdma_registration_handle_register(head.repeat); + trace_rdma_registration_handle_register(head.repeat); reg_resp.repeat = head.repeat; registers = (RDMARegister *) rdma->wr_data[idx].control_curr; - for (count = 0; count < head.repeat; count++) { + for (int count = 0; count < head.repeat; count++) { uint64_t chunk; uint8_t *chunk_start, *chunk_end; @@ -3790,15 +3674,14 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) reg_result = &results[count]; - trace_qemu_rdma_registration_handle_register_loop(count, + trace_rdma_registration_handle_register_loop(count, reg->current_index, reg->key.current_addr, reg->chunks); if (reg->current_index >= rdma->local_ram_blocks.nb_blocks) { error_report("rdma: 'register' bad block index %u (vs %d)", (unsigned int)reg->current_index, rdma->local_ram_blocks.nb_blocks); - ret = -ENOENT; - goto out; + goto err; } block = &(rdma->local_ram_blocks.block[reg->current_index]); if (block->is_ram_block) { @@ -3807,8 +3690,7 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) " offset: %" PRIx64 " current_addr: %" PRIx64, block->block_name, block->offset, reg->key.current_addr); - ret = -ERANGE; - goto out; + goto err; } host_addr = (block->local_host_addr + (reg->key.current_addr - block->offset)); @@ -3823,8 +3705,7 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) error_report("rdma: bad chunk for block %s" " chunk: %" PRIx64, block->block_name, reg->key.chunk); - ret = -ERANGE; - goto out; + goto err; } } chunk_start = ram_chunk_start(block, chunk); @@ -3835,37 +3716,35 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) (uintptr_t)host_addr, NULL, &tmp_rkey, chunk, chunk_start, chunk_end)) { error_report("cannot get rkey"); - ret = -EINVAL; - goto out; + goto err; } reg_result->rkey = tmp_rkey; reg_result->host_addr = (uintptr_t)block->local_host_addr; - trace_qemu_rdma_registration_handle_register_rkey( - reg_result->rkey); + trace_rdma_registration_handle_register_rkey(reg_result->rkey); result_to_network(reg_result); } ret = qemu_rdma_post_send_control(rdma, - (uint8_t *) results, ®_resp); + (uint8_t *) results, ®_resp, &err); if (ret < 0) { - error_report("Failed to send control buffer"); - goto out; + error_report_err(err); + goto err; } break; case RDMA_CONTROL_UNREGISTER_REQUEST: - trace_qemu_rdma_registration_handle_unregister(head.repeat); + trace_rdma_registration_handle_unregister(head.repeat); unreg_resp.repeat = head.repeat; registers = (RDMARegister *) rdma->wr_data[idx].control_curr; - for (count = 0; count < head.repeat; count++) { + for (int count = 0; count < head.repeat; count++) { reg = ®isters[count]; network_to_register(reg); - trace_qemu_rdma_registration_handle_unregister_loop(count, + trace_rdma_registration_handle_unregister_loop(count, reg->current_index, reg->key.chunk); block = &(rdma->local_ram_blocks.block[reg->current_index]); @@ -3874,60 +3753,58 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) block->pmr[reg->key.chunk] = NULL; if (ret != 0) { - perror("rdma unregistration chunk failed"); - ret = -ret; - goto out; + error_report("rdma unregistration chunk failed: %s", + strerror(errno)); + goto err; } rdma->total_registrations--; - trace_qemu_rdma_registration_handle_unregister_success( - reg->key.chunk); + trace_rdma_registration_handle_unregister_success(reg->key.chunk); } - ret = qemu_rdma_post_send_control(rdma, NULL, &unreg_resp); + ret = qemu_rdma_post_send_control(rdma, NULL, &unreg_resp, &err); if (ret < 0) { - error_report("Failed to send control buffer"); - goto out; + error_report_err(err); + goto err; } break; case RDMA_CONTROL_REGISTER_RESULT: error_report("Invalid RESULT message at dest."); - ret = -EIO; - goto out; + goto err; default: error_report("Unknown control message %s", control_desc(head.type)); - ret = -EIO; - goto out; + goto err; } } while (1); -out: - if (ret < 0) { - rdma->error_state = ret; - } - return ret; + +err: + rdma->errored = true; + return -1; } /* Destination: - * Called via a ram_control_load_hook during the initial RAM load section which - * lists the RAMBlocks by name. This lets us know the order of the RAMBlocks - * on the source. - * We've already built our local RAMBlock list, but not yet sent the list to - * the source. + * Called during the initial RAM load section which lists the + * RAMBlocks by name. This lets us know the order of the RAMBlocks on + * the source. We've already built our local RAMBlock list, but not + * yet sent the list to the source. */ -static int -rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name) +int rdma_block_notification_handle(QEMUFile *f, const char *name) { - RDMAContext *rdma; int curr; int found = -1; + if (!migrate_rdma()) { + return 0; + } + RCU_READ_LOCK_GUARD(); - rdma = qatomic_rcu_read(&rioc->rdmain); + QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); + RDMAContext *rdma = qatomic_rcu_read(&rioc->rdmain); if (!rdma) { - return -EIO; + return -1; } /* Find the matching RAMBlock in our local list */ @@ -3940,7 +3817,7 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name) if (found == -1) { error_report("RAMBlock '%s' not found on destination", name); - return -ENOENT; + return -1; } rdma->local_ram_blocks.block[curr].src_index = rdma->next_src_index; @@ -3950,72 +3827,57 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name) return 0; } -static int rdma_load_hook(QEMUFile *f, void *opaque, uint64_t flags, void *data) +int rdma_registration_start(QEMUFile *f, uint64_t flags) { - switch (flags) { - case RAM_CONTROL_BLOCK_REG: - return rdma_block_notification_handle(opaque, data); - - case RAM_CONTROL_HOOK: - return qemu_rdma_registration_handle(f, opaque); - - default: - /* Shouldn't be called with any other values */ - abort(); - } -} - -static int qemu_rdma_registration_start(QEMUFile *f, void *opaque, - uint64_t flags, void *data) -{ - QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); - RDMAContext *rdma; - - RCU_READ_LOCK_GUARD(); - rdma = qatomic_rcu_read(&rioc->rdmaout); - if (!rdma) { - return -EIO; - } - - CHECK_ERROR_STATE(); - - if (migration_in_postcopy()) { + if (!migrate_rdma() || migration_in_postcopy()) { return 0; } - trace_qemu_rdma_registration_start(flags); - qemu_put_be64(f, RAM_SAVE_FLAG_HOOK); - qemu_fflush(f); + QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); + RCU_READ_LOCK_GUARD(); + RDMAContext *rdma = qatomic_rcu_read(&rioc->rdmaout); + if (!rdma) { + return -1; + } - return 0; + if (rdma_errored(rdma)) { + return -1; + } + + trace_rdma_registration_start(flags); + qemu_put_be64(f, RAM_SAVE_FLAG_HOOK); + return qemu_fflush(f); } /* * Inform dest that dynamic registrations are done for now. * First, flush writes, if any. */ -static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, - uint64_t flags, void *data) +int rdma_registration_stop(QEMUFile *f, uint64_t flags) { - QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); + QIOChannelRDMA *rioc; + Error *err = NULL; RDMAContext *rdma; RDMAControlHeader head = { .len = 0, .repeat = 1 }; - int ret = 0; + int ret; - RCU_READ_LOCK_GUARD(); - rdma = qatomic_rcu_read(&rioc->rdmaout); - if (!rdma) { - return -EIO; - } - - CHECK_ERROR_STATE(); - - if (migration_in_postcopy()) { + if (!migrate_rdma() || migration_in_postcopy()) { return 0; } + RCU_READ_LOCK_GUARD(); + rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); + rdma = qatomic_rcu_read(&rioc->rdmaout); + if (!rdma) { + return -1; + } + + if (rdma_errored(rdma)) { + return -1; + } + qemu_fflush(f); - ret = qemu_rdma_drain_cq(f, rdma); + ret = qemu_rdma_drain_cq(rdma); if (ret < 0) { goto err; @@ -4024,10 +3886,10 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, if (flags == RAM_CONTROL_SETUP) { RDMAControlHeader resp = {.type = RDMA_CONTROL_RAM_BLOCKS_RESULT }; RDMALocalBlocks *local = &rdma->local_ram_blocks; - int reg_result_idx, i, nb_dest_blocks; + int reg_result_idx, nb_dest_blocks; head.type = RDMA_CONTROL_RAM_BLOCKS_REQUEST; - trace_qemu_rdma_registration_stop_ram(); + trace_rdma_registration_stop_ram(); /* * Make sure that we parallelize the pinning on both sides. @@ -4039,10 +3901,11 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, */ ret = qemu_rdma_exchange_send(rdma, &head, NULL, &resp, ®_result_idx, rdma->pin_all ? - qemu_rdma_reg_whole_ram_blocks : NULL); + qemu_rdma_reg_whole_ram_blocks : NULL, + &err); if (ret < 0) { - fprintf(stderr, "receiving remote info!"); - return ret; + error_report_err(err); + return -1; } nb_dest_blocks = resp.len / sizeof(RDMADestBlock); @@ -4060,28 +3923,29 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, */ if (local->nb_blocks != nb_dest_blocks) { - fprintf(stderr, "ram blocks mismatch (Number of blocks %d vs %d) " - "Your QEMU command line parameters are probably " - "not identical on both the source and destination.", - local->nb_blocks, nb_dest_blocks); - rdma->error_state = -EINVAL; - return -EINVAL; + error_report("ram blocks mismatch (Number of blocks %d vs %d)", + local->nb_blocks, nb_dest_blocks); + error_printf("Your QEMU command line parameters are probably " + "not identical on both the source and destination."); + rdma->errored = true; + return -1; } qemu_rdma_move_header(rdma, reg_result_idx, &resp); memcpy(rdma->dest_blocks, rdma->wr_data[reg_result_idx].control_curr, resp.len); - for (i = 0; i < nb_dest_blocks; i++) { + for (int i = 0; i < nb_dest_blocks; i++) { network_to_dest_block(&rdma->dest_blocks[i]); /* We require that the blocks are in the same order */ if (rdma->dest_blocks[i].length != local->block[i].length) { - fprintf(stderr, "Block %s/%d has a different length %" PRIu64 - "vs %" PRIu64, local->block[i].block_name, i, - local->block[i].length, - rdma->dest_blocks[i].length); - rdma->error_state = -EINVAL; - return -EINVAL; + error_report("Block %s/%d has a different length %" PRIu64 + "vs %" PRIu64, + local->block[i].block_name, i, + local->block[i].length, + rdma->dest_blocks[i].length); + rdma->errored = true; + return -1; } local->block[i].remote_host_addr = rdma->dest_blocks[i].remote_host_addr; @@ -4089,32 +3953,22 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, } } - trace_qemu_rdma_registration_stop(flags); + trace_rdma_registration_stop(flags); head.type = RDMA_CONTROL_REGISTER_FINISHED; - ret = qemu_rdma_exchange_send(rdma, &head, NULL, NULL, NULL, NULL); + ret = qemu_rdma_exchange_send(rdma, &head, NULL, NULL, NULL, NULL, &err); if (ret < 0) { + error_report_err(err); goto err; } return 0; err: - rdma->error_state = ret; - return ret; + rdma->errored = true; + return -1; } -static const QEMUFileHooks rdma_read_hooks = { - .hook_ram_load = rdma_load_hook, -}; - -static const QEMUFileHooks rdma_write_hooks = { - .before_ram_iterate = qemu_rdma_registration_start, - .after_ram_iterate = qemu_rdma_registration_stop, - .save_page = qemu_rdma_save_page, -}; - - static void qio_channel_rdma_finalize(Object *obj) { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(obj); @@ -4159,27 +4013,24 @@ static void qio_channel_rdma_register_types(void) type_init(qio_channel_rdma_register_types); -static QEMUFile *qemu_fopen_rdma(RDMAContext *rdma, const char *mode) +static QEMUFile *rdma_new_input(RDMAContext *rdma) { - QIOChannelRDMA *rioc; + QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(object_new(TYPE_QIO_CHANNEL_RDMA)); - if (qemu_file_mode_is_not_valid(mode)) { - return NULL; - } + rioc->file = qemu_file_new_input(QIO_CHANNEL(rioc)); + rioc->rdmain = rdma; + rioc->rdmaout = rdma->return_path; - rioc = QIO_CHANNEL_RDMA(object_new(TYPE_QIO_CHANNEL_RDMA)); + return rioc->file; +} - if (mode[0] == 'w') { - rioc->file = qemu_fopen_channel_output(QIO_CHANNEL(rioc)); - rioc->rdmaout = rdma; - rioc->rdmain = rdma->return_path; - qemu_file_set_hooks(rioc->file, &rdma_write_hooks); - } else { - rioc->file = qemu_fopen_channel_input(QIO_CHANNEL(rioc)); - rioc->rdmain = rdma; - rioc->rdmaout = rdma->return_path; - qemu_file_set_hooks(rioc->file, &rdma_read_hooks); - } +static QEMUFile *rdma_new_output(RDMAContext *rdma) +{ + QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(object_new(TYPE_QIO_CHANNEL_RDMA)); + + rioc->file = qemu_file_new_output(QIO_CHANNEL(rioc)); + rioc->rdmaout = rdma; + rioc->rdmain = rdma->return_path; return rioc->file; } @@ -4187,15 +4038,11 @@ static QEMUFile *qemu_fopen_rdma(RDMAContext *rdma, const char *mode) static void rdma_accept_incoming_migration(void *opaque) { RDMAContext *rdma = opaque; - int ret; QEMUFile *f; - Error *local_err = NULL; trace_qemu_rdma_accept_incoming_migration(); - ret = qemu_rdma_accept(rdma); - - if (ret) { - fprintf(stderr, "RDMA ERROR: Migration initialization failed\n"); + if (qemu_rdma_accept(rdma) < 0) { + error_report("RDMA ERROR: Migration initialization failed"); return; } @@ -4205,25 +4052,23 @@ static void rdma_accept_incoming_migration(void *opaque) return; } - f = qemu_fopen_rdma(rdma, "rb"); + f = rdma_new_input(rdma); if (f == NULL) { - fprintf(stderr, "RDMA ERROR: could not qemu_fopen_rdma\n"); + error_report("RDMA ERROR: could not open RDMA for input"); qemu_rdma_cleanup(rdma); return; } rdma->migration_started_on_destination = 1; - migration_fd_process_incoming(f, &local_err); - if (local_err) { - error_reportf_err(local_err, "RDMA ERROR:"); - } + migration_fd_process_incoming(f); } -void rdma_start_incoming_migration(const char *host_port, Error **errp) +void rdma_start_incoming_migration(InetSocketAddress *host_port, + Error **errp) { + MigrationState *s = migrate_get_current(); int ret; - RDMAContext *rdma, *rdma_return_path = NULL; - Error *local_err = NULL; + RDMAContext *rdma; trace_rdma_start_incoming_migration(); @@ -4233,79 +4078,74 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp) return; } - rdma = qemu_rdma_data_init(host_port, &local_err); - if (rdma == NULL) { - goto err; - } - - ret = qemu_rdma_dest_init(rdma, &local_err); - - if (ret) { - goto err; - } - - trace_rdma_start_incoming_migration_after_dest_init(); - - ret = rdma_listen(rdma->listen_id, 5); - - if (ret) { - ERROR(errp, "listening on socket!"); - goto cleanup_rdma; - } - - trace_rdma_start_incoming_migration_after_rdma_listen(); - - qemu_set_fd_handler(rdma->channel->fd, rdma_accept_incoming_migration, - NULL, (void *)(intptr_t)rdma); - return; - -cleanup_rdma: - qemu_rdma_cleanup(rdma); -err: - error_propagate(errp, local_err); - if (rdma) { - g_free(rdma->host); - g_free(rdma->host_port); - } - g_free(rdma); - g_free(rdma_return_path); -} - -void rdma_start_outgoing_migration(void *opaque, - const char *host_port, Error **errp) -{ - MigrationState *s = opaque; - RDMAContext *rdma_return_path = NULL; - RDMAContext *rdma; - int ret = 0; - - /* Avoid ram_block_discard_disable(), cannot change during migration. */ - if (ram_block_discard_is_required()) { - error_setg(errp, "RDMA: cannot disable RAM discard"); - return; - } - rdma = qemu_rdma_data_init(host_port, errp); if (rdma == NULL) { goto err; } - ret = qemu_rdma_source_init(rdma, - s->enabled_capabilities[MIGRATION_CAPABILITY_RDMA_PIN_ALL], errp); + ret = qemu_rdma_dest_init(rdma, errp); + if (ret < 0) { + goto err; + } - if (ret) { + trace_rdma_start_incoming_migration_after_dest_init(); + + ret = rdma_listen(rdma->listen_id, 5); + + if (ret < 0) { + error_setg(errp, "RDMA ERROR: listening on socket!"); + goto cleanup_rdma; + } + + trace_rdma_start_incoming_migration_after_rdma_listen(); + s->rdma_migration = true; + qemu_set_fd_handler(rdma->channel->fd, rdma_accept_incoming_migration, + NULL, (void *)(intptr_t)rdma); + return; + +cleanup_rdma: + qemu_rdma_cleanup(rdma); +err: + if (rdma) { + g_free(rdma->host); + } + g_free(rdma); +} + +void rdma_start_outgoing_migration(void *opaque, + InetSocketAddress *host_port, Error **errp) +{ + MigrationState *s = opaque; + RDMAContext *rdma_return_path = NULL; + RDMAContext *rdma; + int ret; + + /* Avoid ram_block_discard_disable(), cannot change during migration. */ + if (ram_block_discard_is_required()) { + error_setg(errp, "RDMA: cannot disable RAM discard"); + return; + } + + rdma = qemu_rdma_data_init(host_port, errp); + if (rdma == NULL) { + goto err; + } + + ret = qemu_rdma_source_init(rdma, migrate_rdma_pin_all(), errp); + + if (ret < 0) { goto err; } trace_rdma_start_outgoing_migration_after_rdma_source_init(); - ret = qemu_rdma_connect(rdma, errp, false); + ret = qemu_rdma_connect(rdma, false, errp); - if (ret) { + if (ret < 0) { goto err; } /* RDMA postcopy need a separate queue pair for return path */ - if (migrate_postcopy()) { + if (migrate_postcopy() || migrate_return_path()) { rdma_return_path = qemu_rdma_data_init(host_port, errp); if (rdma_return_path == NULL) { @@ -4313,15 +4153,15 @@ void rdma_start_outgoing_migration(void *opaque, } ret = qemu_rdma_source_init(rdma_return_path, - s->enabled_capabilities[MIGRATION_CAPABILITY_RDMA_PIN_ALL], errp); + migrate_rdma_pin_all(), errp); - if (ret) { + if (ret < 0) { goto return_path_err; } - ret = qemu_rdma_connect(rdma_return_path, errp, true); + ret = qemu_rdma_connect(rdma_return_path, true, errp); - if (ret) { + if (ret < 0) { goto return_path_err; } @@ -4332,7 +4172,8 @@ void rdma_start_outgoing_migration(void *opaque, trace_rdma_start_outgoing_migration_after_rdma_connect(); - s->to_dst_file = qemu_fopen_rdma(rdma, "wb"); + s->to_dst_file = rdma_new_output(rdma); + s->rdma_migration = true; migrate_fd_connect(s, NULL); return; return_path_err: diff --git a/migration/rdma.h b/migration/rdma.h index de2ba09dc5..a8d27f33b8 100644 --- a/migration/rdma.h +++ b/migration/rdma.h @@ -14,12 +14,56 @@ * */ +#include "qemu/sockets.h" + #ifndef QEMU_MIGRATION_RDMA_H #define QEMU_MIGRATION_RDMA_H -void rdma_start_outgoing_migration(void *opaque, const char *host_port, +#include "exec/memory.h" + +void rdma_start_outgoing_migration(void *opaque, InetSocketAddress *host_port, Error **errp); -void rdma_start_incoming_migration(const char *host_port, Error **errp); +void rdma_start_incoming_migration(InetSocketAddress *host_port, Error **errp); +/* + * Constants used by rdma return codes + */ +#define RAM_CONTROL_SETUP 0 +#define RAM_CONTROL_ROUND 1 +#define RAM_CONTROL_FINISH 3 + +/* + * Whenever this is found in the data stream, the flags + * will be passed to rdma functions in the incoming-migration + * side. + */ +#define RAM_SAVE_FLAG_HOOK 0x80 + +#define RAM_SAVE_CONTROL_NOT_SUPP -1000 +#define RAM_SAVE_CONTROL_DELAYED -2000 + +#ifdef CONFIG_RDMA +int rdma_registration_handle(QEMUFile *f); +int rdma_registration_start(QEMUFile *f, uint64_t flags); +int rdma_registration_stop(QEMUFile *f, uint64_t flags); +int rdma_block_notification_handle(QEMUFile *f, const char *name); +int rdma_control_save_page(QEMUFile *f, ram_addr_t block_offset, + ram_addr_t offset, size_t size); +#else +static inline +int rdma_registration_handle(QEMUFile *f) { return 0; } +static inline +int rdma_registration_start(QEMUFile *f, uint64_t flags) { return 0; } +static inline +int rdma_registration_stop(QEMUFile *f, uint64_t flags) { return 0; } +static inline +int rdma_block_notification_handle(QEMUFile *f, const char *name) { return 0; } +static inline +int rdma_control_save_page(QEMUFile *f, ram_addr_t block_offset, + ram_addr_t offset, size_t size) +{ + return RAM_SAVE_CONTROL_NOT_SUPP; +} +#endif #endif diff --git a/migration/savevm.c b/migration/savevm.c index d9076897b8..e7c1215671 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -31,18 +31,18 @@ #include "net/net.h" #include "migration.h" #include "migration/snapshot.h" +#include "migration-stats.h" #include "migration/vmstate.h" #include "migration/misc.h" #include "migration/register.h" #include "migration/global_state.h" +#include "migration/channel-block.h" #include "ram.h" -#include "qemu-file-channel.h" #include "qemu-file.h" #include "savevm.h" #include "postcopy-ram.h" #include "qapi/error.h" #include "qapi/qapi-commands-migration.h" -#include "qapi/qmp/json-writer.h" #include "qapi/clone-visitor.h" #include "qapi/qapi-builtin-visit.h" #include "qapi/qmp/qerror.h" @@ -52,6 +52,7 @@ #include "exec/target_page.h" #include "trace.h" #include "qemu/iov.h" +#include "qemu/job.h" #include "qemu/main-loop.h" #include "block/snapshot.h" #include "qemu/cutils.h" @@ -66,6 +67,8 @@ #include "net/announce.h" #include "qemu/yank.h" #include "yank_functions.h" +#include "sysemu/qtest.h" +#include "options.h" const unsigned int postcopy_ram_discard_version; @@ -114,7 +117,7 @@ static struct mig_cmd_args { * The format of arguments is depending on postcopy mode: * - postcopy RAM only * uint64_t host page size - * uint64_t taget page size + * uint64_t target page size * * - postcopy RAM and postcopy dirty bitmaps * format is the same as for postcopy RAM only @@ -130,48 +133,13 @@ static struct mig_cmd_args { /***********************************************************/ /* savevm/loadvm support */ -static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, - int64_t pos, Error **errp) -{ - int ret; - QEMUIOVector qiov; - - qemu_iovec_init_external(&qiov, iov, iovcnt); - ret = bdrv_writev_vmstate(opaque, &qiov, pos); - if (ret < 0) { - return ret; - } - - return qiov.size; -} - -static ssize_t block_get_buffer(void *opaque, uint8_t *buf, int64_t pos, - size_t size, Error **errp) -{ - return bdrv_load_vmstate(opaque, buf, pos, size); -} - -static int bdrv_fclose(void *opaque, Error **errp) -{ - return bdrv_flush(opaque); -} - -static const QEMUFileOps bdrv_read_ops = { - .get_buffer = block_get_buffer, - .close = bdrv_fclose -}; - -static const QEMUFileOps bdrv_write_ops = { - .writev_buffer = block_writev_buffer, - .close = bdrv_fclose -}; - static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable) { if (is_writable) { - return qemu_fopen_ops(bs, &bdrv_write_ops, false); + return qemu_file_new_output(QIO_CHANNEL(qio_channel_block_new(bs))); + } else { + return qemu_file_new_input(QIO_CHANNEL(qio_channel_block_new(bs))); } - return qemu_fopen_ops(bs, &bdrv_read_ops, false); } @@ -269,12 +237,15 @@ static SaveState savevm_state = { .global_section_id = 0, }; +static SaveStateEntry *find_se(const char *idstr, uint32_t instance_id); + static bool should_validate_capability(int capability) { assert(capability >= 0 && capability < MIGRATION_CAPABILITY__MAX); /* Validate only new capabilities to keep compatibility. */ switch (capability) { case MIGRATION_CAPABILITY_X_IGNORE_SHARED: + case MIGRATION_CAPABILITY_MAPPED_RAM: return true; default: return false; @@ -287,7 +258,7 @@ static uint32_t get_validatable_capabilities_count(void) uint32_t result = 0; int i; for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { - if (should_validate_capability(i) && s->enabled_capabilities[i]) { + if (should_validate_capability(i) && s->capabilities[i]) { result++; } } @@ -309,7 +280,7 @@ static int configuration_pre_save(void *opaque) state->capabilities = g_renew(MigrationCapability, state->capabilities, state->caps_count); for (i = j = 0; i < MIGRATION_CAPABILITY__MAX; i++) { - if (should_validate_capability(i) && s->enabled_capabilities[i]) { + if (should_validate_capability(i) && s->capabilities[i]) { state->capabilities[j++] = i; } } @@ -359,7 +330,7 @@ static bool configuration_validate_capabilities(SaveState *state) continue; } source_state = test_bit(i, source_caps_bm); - target_state = s->enabled_capabilities[i]; + target_state = s->capabilities[i]; if (source_state != target_state) { error_report("Capability %s is %s, but received capability is %s", MigrationCapability_str(i), @@ -468,7 +439,7 @@ static const VMStateDescription vmstate_target_page_bits = { .version_id = 1, .minimum_version_id = 1, .needed = vmstate_target_page_bits_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(target_page_bits, SaveState), VMSTATE_END_OF_LIST() } @@ -484,7 +455,7 @@ static const VMStateDescription vmstate_capabilites = { .version_id = 1, .minimum_version_id = 1, .needed = vmstate_capabilites_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_V(caps_count, SaveState, 1), VMSTATE_VARRAY_UINT32_ALLOC(capabilities, SaveState, caps_count, 1, vmstate_info_capability, @@ -501,8 +472,8 @@ static bool vmstate_uuid_needed(void *opaque) static int vmstate_uuid_post_load(void *opaque, int version_id) { SaveState *state = opaque; - char uuid_src[UUID_FMT_LEN + 1]; - char uuid_dst[UUID_FMT_LEN + 1]; + char uuid_src[UUID_STR_LEN]; + char uuid_dst[UUID_STR_LEN]; if (!qemu_uuid_set) { /* @@ -529,7 +500,7 @@ static const VMStateDescription vmstate_uuid = { .minimum_version_id = 1, .needed = vmstate_uuid_needed, .post_load = vmstate_uuid_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY_V(uuid.data, SaveState, sizeof(QemuUUID), 1), VMSTATE_END_OF_LIST() } @@ -542,12 +513,12 @@ static const VMStateDescription vmstate_configuration = { .post_load = configuration_post_load, .pre_save = configuration_pre_save, .post_save = configuration_post_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(len, SaveState), VMSTATE_VBUFFER_ALLOC_UINT32(name, SaveState, 0, NULL, len), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription *[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_target_page_bits, &vmstate_capabilites, &vmstate_uuid, @@ -569,6 +540,9 @@ static void dump_vmstate_vmsf(FILE *out_file, const VMStateField *field, field->version_id); fprintf(out_file, "%*s\"field_exists\": %s,\n", indent, "", field->field_exists ? "true" : "false"); + if (field->flags & VMS_ARRAY) { + fprintf(out_file, "%*s\"num\": %d,\n", indent, "", field->num); + } fprintf(out_file, "%*s\"size\": %zu", indent, "", field->size); if (field->vmsd != NULL) { fprintf(out_file, ",\n"); @@ -578,11 +552,11 @@ static void dump_vmstate_vmsf(FILE *out_file, const VMStateField *field, } static void dump_vmstate_vmss(FILE *out_file, - const VMStateDescription **subsection, + const VMStateDescription *subsection, int indent) { - if (*subsection != NULL) { - dump_vmstate_vmsd(out_file, *subsection, indent, true); + if (subsection != NULL) { + dump_vmstate_vmsd(out_file, subsection, indent, true); } } @@ -620,10 +594,11 @@ static void dump_vmstate_vmsd(FILE *out_file, field++; first = false; } + assert(field->flags == VMS_END); fprintf(out_file, "\n%*s]", indent, ""); } if (vmsd->subsections != NULL) { - const VMStateDescription **subsection = vmsd->subsections; + const VMStateDescription * const *subsection = vmsd->subsections; bool first; fprintf(out_file, ",\n%*s\"Subsections\": [\n", indent, ""); @@ -632,7 +607,7 @@ static void dump_vmstate_vmsd(FILE *out_file, if (!first) { fprintf(out_file, ",\n"); } - dump_vmstate_vmss(out_file, subsection, indent + 2); + dump_vmstate_vmss(out_file, *subsection, indent + 2); subsection++; first = false; } @@ -744,6 +719,18 @@ static void savevm_state_handler_insert(SaveStateEntry *nse) assert(priority <= MIG_PRI_MAX); + /* + * This should never happen otherwise migration will probably fail + * silently somewhere because we can be wrongly applying one + * object properties upon another one. Bail out ASAP. + */ + if (find_se(nse->idstr, nse->instance_id)) { + error_report("%s: Detected duplicate SaveStateEntry: " + "id=%s, instance_id=0x%"PRIx32, __func__, + nse->idstr, nse->instance_id); + exit(EXIT_FAILURE); + } + for (i = priority - 1; i >= 0; i--) { se = savevm_state.handler_pri_head[i]; if (se != NULL) { @@ -838,6 +825,60 @@ void unregister_savevm(VMStateIf *obj, const char *idstr, void *opaque) } } +/* + * Perform some basic checks on vmsd's at registration + * time. + */ +static void vmstate_check(const VMStateDescription *vmsd) +{ + const VMStateField *field = vmsd->fields; + const VMStateDescription * const *subsection = vmsd->subsections; + + if (field) { + while (field->name) { + if (field->flags & (VMS_STRUCT | VMS_VSTRUCT)) { + /* Recurse to sub structures */ + vmstate_check(field->vmsd); + } + /* Carry on */ + field++; + } + /* Check for the end of field list canary */ + if (field->flags != VMS_END) { + error_report("VMSTATE not ending with VMS_END: %s", vmsd->name); + g_assert_not_reached(); + } + } + + while (subsection && *subsection) { + /* + * The name of a subsection should start with the name of the + * current object. + */ + assert(!strncmp(vmsd->name, (*subsection)->name, strlen(vmsd->name))); + vmstate_check(*subsection); + subsection++; + } +} + +/* + * See comment in hw/intc/xics.c:icp_realize() + * + * This function can be removed when + * pre_2_10_vmstate_register_dummy_icp() is removed. + */ +int vmstate_replace_hack_for_ppc(VMStateIf *obj, int instance_id, + const VMStateDescription *vmsd, + void *opaque) +{ + SaveStateEntry *se = find_se(vmsd->name, instance_id); + + if (se) { + savevm_state_handler_remove(se); + } + return vmstate_register(obj, instance_id, vmsd, opaque); +} + int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id, const VMStateDescription *vmsd, void *opaque, int alias_id, @@ -883,6 +924,11 @@ int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id, } else { se->instance_id = instance_id; } + + /* Perform a recursive sanity check during the test runs */ + if (qtest_enabled()) { + vmstate_check(vmsd); + } assert(!se->compat || se->instance_id == 0); savevm_state_handler_insert(se); return 0; @@ -914,11 +960,9 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry *se) static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc) { - int64_t old_offset, size; - - old_offset = qemu_ftell_fast(f); + uint64_t old_offset = qemu_file_transferred(f); se->ops->save_state(f, se->opaque); - size = qemu_ftell_fast(f) - old_offset; + uint64_t size = qemu_file_transferred(f) - old_offset; if (vmdesc) { json_writer_int64(vmdesc, "size", size); @@ -932,17 +976,6 @@ static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se, } } -static int vmstate_save(QEMUFile *f, SaveStateEntry *se, - JSONWriter *vmdesc) -{ - trace_vmstate_save(se->idstr, se->vmsd ? se->vmsd->name : "(old)"); - if (!se->vmsd) { - vmstate_save_old_style(f, se, vmdesc); - return 0; - } - return vmstate_save_state(f, se->vmsd, se->opaque, vmdesc); -} - /* * Write the header for device section (QEMU_VM_SECTION START/END/PART/FULL) */ @@ -976,6 +1009,47 @@ static void save_section_footer(QEMUFile *f, SaveStateEntry *se) } } +static int vmstate_save(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc) +{ + int ret; + Error *local_err = NULL; + MigrationState *s = migrate_get_current(); + + if ((!se->ops || !se->ops->save_state) && !se->vmsd) { + return 0; + } + if (se->vmsd && !vmstate_section_needed(se->vmsd, se->opaque)) { + trace_savevm_section_skip(se->idstr, se->section_id); + return 0; + } + + trace_savevm_section_start(se->idstr, se->section_id); + save_section_header(f, se, QEMU_VM_SECTION_FULL); + if (vmdesc) { + json_writer_start_object(vmdesc, NULL); + json_writer_str(vmdesc, "name", se->idstr); + json_writer_int64(vmdesc, "instance_id", se->instance_id); + } + + trace_vmstate_save(se->idstr, se->vmsd ? se->vmsd->name : "(old)"); + if (!se->vmsd) { + vmstate_save_old_style(f, se, vmdesc); + } else { + ret = vmstate_save_state_with_err(f, se->vmsd, se->opaque, vmdesc, &local_err); + if (ret) { + migrate_set_error(s, local_err); + error_report_err(local_err); + return ret; + } + } + + trace_savevm_section_end(se->idstr, se->section_id, 0); + save_section_footer(f, se); + if (vmdesc) { + json_writer_end_object(vmdesc); + } + return 0; +} /** * qemu_savevm_command_send: Send a 'QEMU_VM_COMMAND' type element with the * command and associated data. @@ -1031,10 +1105,14 @@ void qemu_savevm_send_open_return_path(QEMUFile *f) int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len) { uint32_t tmp; + MigrationState *ms = migrate_get_current(); + Error *local_err = NULL; if (len > MAX_VM_CMD_PACKAGED_SIZE) { - error_report("%s: Unreasonably large packaged state: %zu", + error_setg(&local_err, "%s: Unreasonably large packaged state: %zu", __func__, len); + migrate_set_error(ms, local_err); + error_report_err(local_err); return -1; } @@ -1172,13 +1250,27 @@ void qemu_savevm_non_migratable_list(strList **reasons) void qemu_savevm_state_header(QEMUFile *f) { + MigrationState *s = migrate_get_current(); + + s->vmdesc = json_writer_new(false); + trace_savevm_state_header(); qemu_put_be32(f, QEMU_VM_FILE_MAGIC); qemu_put_be32(f, QEMU_VM_FILE_VERSION); - if (migrate_get_current()->send_configuration) { + if (s->send_configuration) { qemu_put_byte(f, QEMU_VM_CONFIGURATION); - vmstate_save_state(f, &vmstate_configuration, &savevm_state, 0); + + /* + * This starts the main json object and is paired with the + * json_writer_end_object in + * qemu_savevm_state_complete_precopy_non_iterable + */ + json_writer_start_object(s->vmdesc, NULL); + + json_writer_start_object(s->vmdesc, "configuration"); + vmstate_save_state(f, &vmstate_configuration, &savevm_state, s->vmdesc); + json_writer_end_object(s->vmdesc); } } @@ -1196,14 +1288,51 @@ bool qemu_savevm_state_guest_unplug_pending(void) return false; } -void qemu_savevm_state_setup(QEMUFile *f) +int qemu_savevm_state_prepare(Error **errp) { SaveStateEntry *se; - Error *local_err = NULL; int ret; + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + if (!se->ops || !se->ops->save_prepare) { + continue; + } + if (se->ops->is_active) { + if (!se->ops->is_active(se->opaque)) { + continue; + } + } + + ret = se->ops->save_prepare(se->opaque, errp); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +void qemu_savevm_state_setup(QEMUFile *f) +{ + MigrationState *ms = migrate_get_current(); + SaveStateEntry *se; + Error *local_err = NULL; + int ret = 0; + + json_writer_int64(ms->vmdesc, "page_size", qemu_target_page_size()); + json_writer_start_array(ms->vmdesc, "devices"); + trace_savevm_state_setup(); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + if (se->vmsd && se->vmsd->early_setup) { + ret = vmstate_save(f, se, ms->vmdesc); + if (ret) { + qemu_file_set_error(f, ret); + break; + } + continue; + } + if (!se->ops || !se->ops->save_setup) { continue; } @@ -1222,6 +1351,10 @@ void qemu_savevm_state_setup(QEMUFile *f) } } + if (ret) { + return; + } + if (precopy_notify(PRECOPY_NOTIFY_SETUP, &local_err)) { error_report_err(local_err); } @@ -1261,7 +1394,8 @@ int qemu_savevm_state_resume_prepare(MigrationState *s) int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy) { SaveStateEntry *se; - int ret = 1; + bool all_finished = true; + int ret; trace_savevm_state_iterate(); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { @@ -1286,7 +1420,7 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy) !(se->ops->has_postcopy && se->ops->has_postcopy(se->opaque))) { continue; } - if (qemu_file_rate_limit(f)) { + if (migration_rate_exceeded(f)) { return 0; } trace_savevm_section_start(se->idstr, se->section_id); @@ -1302,16 +1436,12 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy) "%d(%s): %d", se->section_id, se->idstr, ret); qemu_file_set_error(f, ret); - } - if (ret <= 0) { - /* Do not proceed to the next vmstate before this one reported - completion of the current stage. This serializes the migration - and reduces the probability that a faster changing state is - synchronized over and over again. */ - break; + return ret; + } else if (!ret) { + all_finished = false; } } - return ret; + return all_finished; } static bool should_send_vmdesc(void) @@ -1363,6 +1493,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f) static int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) { + int64_t start_ts_each, end_ts_each; SaveStateEntry *se; int ret; @@ -1379,6 +1510,8 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) continue; } } + + start_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); trace_savevm_section_start(se->idstr, se->section_id); save_section_header(f, se, QEMU_VM_SECTION_END); @@ -1390,8 +1523,13 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) qemu_file_set_error(f, ret); return -1; } + end_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + trace_vmstate_downtime_save("iterable", se->idstr, se->instance_id, + end_ts_each - start_ts_each); } + trace_vmstate_downtime_checkpoint("src-iterable-saved"); + return 0; } @@ -1399,41 +1537,30 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, bool in_postcopy, bool inactivate_disks) { - g_autoptr(JSONWriter) vmdesc = NULL; + MigrationState *ms = migrate_get_current(); + int64_t start_ts_each, end_ts_each; + JSONWriter *vmdesc = ms->vmdesc; int vmdesc_len; SaveStateEntry *se; int ret; - vmdesc = json_writer_new(false); - json_writer_start_object(vmdesc, NULL); - json_writer_int64(vmdesc, "page_size", qemu_target_page_size()); - json_writer_start_array(vmdesc, "devices"); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - - if ((!se->ops || !se->ops->save_state) && !se->vmsd) { - continue; - } - if (se->vmsd && !vmstate_save_needed(se->vmsd, se->opaque)) { - trace_savevm_section_skip(se->idstr, se->section_id); + if (se->vmsd && se->vmsd->early_setup) { + /* Already saved during qemu_savevm_state_setup(). */ continue; } - trace_savevm_section_start(se->idstr, se->section_id); + start_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); - json_writer_start_object(vmdesc, NULL); - json_writer_str(vmdesc, "name", se->idstr); - json_writer_int64(vmdesc, "instance_id", se->instance_id); - - save_section_header(f, se, QEMU_VM_SECTION_FULL); ret = vmstate_save(f, se, vmdesc); if (ret) { qemu_file_set_error(f, ret); return ret; } - trace_savevm_section_end(se->idstr, se->section_id, 0); - save_section_footer(f, se); - json_writer_end_object(vmdesc); + end_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + trace_vmstate_downtime_save("non-iterable", se->idstr, se->instance_id, + end_ts_each - start_ts_each); } if (inactivate_disks) { @@ -1441,8 +1568,11 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, * bdrv_activate_all() on the other end won't fail. */ ret = bdrv_inactivate_all(); if (ret) { - error_report("%s: bdrv_inactivate_all() failed (%d)", - __func__, ret); + Error *local_err = NULL; + error_setg(&local_err, "%s: bdrv_inactivate_all() failed (%d)", + __func__, ret); + migrate_set_error(ms, local_err); + error_report_err(local_err); qemu_file_set_error(f, ret); return ret; } @@ -1462,6 +1592,12 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, qemu_put_buffer(f, (uint8_t *)json_writer_get(vmdesc), vmdesc_len); } + /* Free it now to detect any inconsistencies. */ + json_writer_free(vmdesc); + ms->vmdesc = NULL; + + trace_vmstate_downtime_checkpoint("src-non-iterable-saved"); + return 0; } @@ -1498,28 +1634,23 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, } flush: - qemu_fflush(f); - return 0; + return qemu_fflush(f); } /* Give an estimate of the amount left to be transferred, * the result is split into the amount for units that can and * for units that can't do postcopy. */ -void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size, - uint64_t *res_precopy_only, - uint64_t *res_compatible, - uint64_t *res_postcopy_only) +void qemu_savevm_state_pending_estimate(uint64_t *must_precopy, + uint64_t *can_postcopy) { SaveStateEntry *se; - *res_precopy_only = 0; - *res_compatible = 0; - *res_postcopy_only = 0; - + *must_precopy = 0; + *can_postcopy = 0; QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - if (!se->ops || !se->ops->save_live_pending) { + if (!se->ops || !se->ops->state_pending_estimate) { continue; } if (se->ops->is_active) { @@ -1527,9 +1658,28 @@ void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size, continue; } } - se->ops->save_live_pending(f, se->opaque, threshold_size, - res_precopy_only, res_compatible, - res_postcopy_only); + se->ops->state_pending_estimate(se->opaque, must_precopy, can_postcopy); + } +} + +void qemu_savevm_state_pending_exact(uint64_t *must_precopy, + uint64_t *can_postcopy) +{ + SaveStateEntry *se; + + *must_precopy = 0; + *can_postcopy = 0; + + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + if (!se->ops || !se->ops->state_pending_exact) { + continue; + } + if (se->ops->is_active) { + if (!se->ops->is_active(se->opaque)) { + continue; + } + } + se->ops->state_pending_exact(se->opaque, must_precopy, can_postcopy); } } @@ -1556,25 +1706,24 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) MigrationState *ms = migrate_get_current(); MigrationStatus status; - if (migration_is_running(ms->state)) { + if (migration_is_running()) { error_setg(errp, QERR_MIGRATION_ACTIVE); return -EINVAL; } - if (migrate_use_block()) { + if (migrate_block()) { error_setg(errp, "Block migration and snapshots are incompatible"); return -EINVAL; } - migrate_init(ms); - memset(&ram_counters, 0, sizeof(ram_counters)); - memset(&compression_counters, 0, sizeof(compression_counters)); + ret = migrate_init(ms, errp); + if (ret) { + return ret; + } ms->to_dst_file = f; - qemu_mutex_unlock_iothread(); qemu_savevm_state_header(f); qemu_savevm_state_setup(f); - qemu_mutex_lock_iothread(); while (qemu_file_get_error(f) == 0) { if (qemu_savevm_state_iterate(f, false) > 0) { @@ -1629,21 +1778,10 @@ int qemu_save_device_state(QEMUFile *f) if (se->is_ram) { continue; } - if ((!se->ops || !se->ops->save_state) && !se->vmsd) { - continue; - } - if (se->vmsd && !vmstate_save_needed(se->vmsd, se->opaque)) { - continue; - } - - save_section_header(f, se, QEMU_VM_SECTION_FULL); - ret = vmstate_save(f, se, NULL); if (ret) { return ret; } - - save_section_footer(f, se); } qemu_put_byte(f, QEMU_VM_EOF); @@ -1713,7 +1851,8 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis, return -EINVAL; } - if (!postcopy_ram_supported_by_host(mis)) { + if (!postcopy_ram_supported_by_host(mis, &local_err)) { + error_report_err(local_err); postcopy_state_set(POSTCOPY_INCOMING_NONE); return -1; } @@ -2000,18 +2139,18 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) Error *local_err = NULL; MigrationIncomingState *mis = opaque; - trace_loadvm_postcopy_handle_run_bh("enter"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-enter"); /* TODO we should move all of this lot into postcopy_ram.c or a shared code * in migration.c */ cpu_synchronize_all_post_init(); - trace_loadvm_postcopy_handle_run_bh("after cpu sync"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cpu-synced"); qemu_announce_self(&mis->announce_timer, migrate_announce_params()); - trace_loadvm_postcopy_handle_run_bh("after announce"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-announced"); /* Make sure all file formats throw away their mutable metadata. * If we get an error here, just don't restart the VM yet. */ @@ -2022,7 +2161,7 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) autostart = false; } - trace_loadvm_postcopy_handle_run_bh("after invalidate cache"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cache-invalidated"); dirty_bitmap_mig_before_vm_start(); @@ -2034,9 +2173,7 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) runstate_set(RUN_STATE_PAUSED); } - qemu_bh_delete(mis->bh); - - trace_loadvm_postcopy_handle_run_bh("return"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-vm-started"); } /* After all discards we can start running and asking for pages */ @@ -2051,8 +2188,7 @@ static int loadvm_postcopy_handle_run(MigrationIncomingState *mis) } postcopy_state_set(POSTCOPY_INCOMING_RUNNING); - mis->bh = qemu_bh_new(loadvm_postcopy_handle_run_bh, mis); - qemu_bh_schedule(mis->bh); + migration_bh_schedule(loadvm_postcopy_handle_run_bh, mis); /* We need to finish reading the stream from the package * and also stop reading anything more from the stream that loaded the @@ -2152,6 +2288,17 @@ static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis) */ qemu_sem_post(&mis->postcopy_pause_sem_fault); + if (migrate_postcopy_preempt()) { + /* + * The preempt channel will be created in async manner, now let's + * wait for it and make sure it's created. + */ + qemu_sem_wait(&mis->postcopy_qemufile_dst_done); + assert(mis->postcopy_qemufile_dst); + /* Kick the fast ram load thread too */ + qemu_sem_post(&mis->postcopy_pause_sem_fast_load); + } + return 0; } @@ -2193,7 +2340,28 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis) bioc->usage += length; trace_loadvm_handle_cmd_packaged_received(ret); - QEMUFile *packf = qemu_fopen_channel_input(QIO_CHANNEL(bioc)); + QEMUFile *packf = qemu_file_new_input(QIO_CHANNEL(bioc)); + + /* + * Before loading the guest states, ensure that the preempt channel has + * been ready to use, as some of the states (e.g. via virtio_load) might + * trigger page faults that will be handled through the preempt channel. + * So yield to the main thread in the case that the channel create event + * hasn't been dispatched. + * + * TODO: if we can move migration loadvm out of main thread, then we + * won't block main thread from polling the accept() fds. We can drop + * this as a whole when that is done. + */ + do { + if (!migrate_postcopy_preempt() || !qemu_in_coroutine() || + mis->postcopy_qemufile_dst) { + break; + } + + aio_co_schedule(qemu_get_current_aio_context(), qemu_coroutine_self()); + qemu_coroutine_yield(); + } while (1); ret = qemu_loadvm_state_main(packf, mis); trace_loadvm_handle_cmd_packaged_main(ret); @@ -2306,6 +2474,21 @@ static int loadvm_process_command(QEMUFile *f) error_report("CMD_OPEN_RETURN_PATH failed"); return -1; } + + /* + * Switchover ack is enabled but no device uses it, so send an ACK to + * source that it's OK to switchover. Do it here, after return path has + * been created. + */ + if (migrate_switchover_ack() && !mis->switchover_ack_pending_num) { + int ret = migrate_send_rp_switchover_ack(mis); + if (ret) { + error_report( + "Could not send switchover ack RP MSG, err %d (%s)", ret, + strerror(-ret)); + return ret; + } + } break; case MIG_CMD_PING: @@ -2391,9 +2574,12 @@ static bool check_section_footer(QEMUFile *f, SaveStateEntry *se) } static int -qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) +qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis, + uint8_t type) { + bool trace_downtime = (type == QEMU_VM_SECTION_FULL); uint32_t instance_id, version_id, section_id; + int64_t start_ts, end_ts; SaveStateEntry *se; char idstr[256]; int ret; @@ -2442,12 +2628,23 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) return -EINVAL; } + if (trace_downtime) { + start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + } + ret = vmstate_load(f, se); if (ret < 0) { error_report("error while loading state for instance 0x%"PRIx32" of" " device '%s'", instance_id, idstr); return ret; } + + if (trace_downtime) { + end_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + trace_vmstate_downtime_load("non-iterable", se->idstr, + se->instance_id, end_ts - start_ts); + } + if (!check_section_footer(f, se)) { return -EINVAL; } @@ -2456,8 +2653,11 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) } static int -qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis) +qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis, + uint8_t type) { + bool trace_downtime = (type == QEMU_VM_SECTION_END); + int64_t start_ts, end_ts; uint32_t section_id; SaveStateEntry *se; int ret; @@ -2482,12 +2682,23 @@ qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis) return -EINVAL; } + if (trace_downtime) { + start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + } + ret = vmstate_load(f, se); if (ret < 0) { error_report("error while loading state section id %d(%s)", section_id, se->idstr); return ret; } + + if (trace_downtime) { + end_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + trace_vmstate_downtime_load("iterable", se->idstr, + se->instance_id, end_ts - start_ts); + } + if (!check_section_footer(f, se)) { return -EINVAL; } @@ -2532,6 +2743,23 @@ static int qemu_loadvm_state_header(QEMUFile *f) return 0; } +static void qemu_loadvm_state_switchover_ack_needed(MigrationIncomingState *mis) +{ + SaveStateEntry *se; + + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + if (!se->ops || !se->ops->switchover_ack_needed) { + continue; + } + + if (se->ops->switchover_ack_needed(se->opaque)) { + mis->switchover_ack_pending_num++; + } + } + + trace_loadvm_state_switchover_ack_needed(mis->switchover_ack_pending_num); +} + static int qemu_loadvm_state_setup(QEMUFile *f) { SaveStateEntry *se; @@ -2575,16 +2803,6 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis) { int i; - /* - * If network is interrupted, any temp page we received will be useless - * because we didn't mark them as "received" in receivedmap. After a - * proper recovery later (which will sync src dirty bitmap with receivedmap - * on dest) these cached small pages will be resent again. - */ - for (i = 0; i < mis->postcopy_channels; i++) { - postcopy_temp_page_reset(&mis->postcopy_tmp_pages[i]); - } - trace_postcopy_pause_incoming(); assert(migrate_postcopy_ram()); @@ -2607,12 +2825,38 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis) mis->to_src_file = NULL; qemu_mutex_unlock(&mis->rp_mutex); - migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, + /* + * NOTE: this must happen before reset the PostcopyTmpPages below, + * otherwise it's racy to reset those fields when the fast load thread + * can be accessing it in parallel. + */ + if (mis->postcopy_qemufile_dst) { + qemu_file_shutdown(mis->postcopy_qemufile_dst); + /* Take the mutex to make sure the fast ram load thread halted */ + qemu_mutex_lock(&mis->postcopy_prio_thread_mutex); + migration_ioc_unregister_yank_from_file(mis->postcopy_qemufile_dst); + qemu_fclose(mis->postcopy_qemufile_dst); + mis->postcopy_qemufile_dst = NULL; + qemu_mutex_unlock(&mis->postcopy_prio_thread_mutex); + } + + /* Current state can be either ACTIVE or RECOVER */ + migrate_set_state(&mis->state, mis->state, MIGRATION_STATUS_POSTCOPY_PAUSED); /* Notify the fault thread for the invalidated file handle */ postcopy_fault_thread_notify(mis); + /* + * If network is interrupted, any temp page we received will be useless + * because we didn't mark them as "received" in receivedmap. After a + * proper recovery later (which will sync src dirty bitmap with receivedmap + * on dest) these cached small pages will be resent again. + */ + for (i = 0; i < mis->postcopy_channels; i++) { + postcopy_temp_page_reset(&mis->postcopy_tmp_pages[i]); + } + error_report("Detected IO failure for postcopy. " "Migration paused."); @@ -2634,8 +2878,8 @@ retry: while (true) { section_type = qemu_get_byte(f); - if (qemu_file_get_error(f)) { - ret = qemu_file_get_error(f); + ret = qemu_file_get_error_obj_any(f, mis->postcopy_qemufile_dst, NULL); + if (ret) { break; } @@ -2643,14 +2887,14 @@ retry: switch (section_type) { case QEMU_VM_SECTION_START: case QEMU_VM_SECTION_FULL: - ret = qemu_loadvm_section_start_full(f, mis); + ret = qemu_loadvm_section_start_full(f, mis, section_type); if (ret < 0) { goto out; } break; case QEMU_VM_SECTION_PART: case QEMU_VM_SECTION_END: - ret = qemu_loadvm_section_part_end(f, mis); + ret = qemu_loadvm_section_part_end(f, mis, section_type); if (ret < 0) { goto out; } @@ -2720,6 +2964,10 @@ int qemu_loadvm_state(QEMUFile *f) return -EINVAL; } + if (migrate_switchover_ack()) { + qemu_loadvm_state_switchover_ack_needed(mis); + } + cpu_synchronize_all_pre_loadvm(); ret = qemu_loadvm_state_main(f, mis); @@ -2793,6 +3041,24 @@ int qemu_load_device_state(QEMUFile *f) return 0; } +int qemu_loadvm_approve_switchover(void) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + + if (!mis->switchover_ack_pending_num) { + return -EINVAL; + } + + mis->switchover_ack_pending_num--; + trace_loadvm_approve_switchover(mis->switchover_ack_pending_num); + + if (mis->switchover_ack_pending_num) { + return 0; + } + + return migrate_send_rp_switchover_ack(mis); +} + bool save_snapshot(const char *name, bool overwrite, const char *vmstate, bool has_devices, strList *devices, Error **errp) { @@ -2800,10 +3066,9 @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate, QEMUSnapshotInfo sn1, *sn = &sn1; int ret = -1, ret2; QEMUFile *f; - int saved_vm_running; + RunState saved_state = runstate_get(); uint64_t vm_state_size; g_autoptr(GDateTime) now = g_date_time_new_now_local(); - AioContext *aio_context; GLOBAL_STATE_CODE(); @@ -2846,21 +3111,12 @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate, if (bs == NULL) { return false; } - aio_context = bdrv_get_aio_context(bs); - saved_vm_running = runstate_is_running(); - - ret = global_state_store(); - if (ret) { - error_setg(errp, "Error saving global state"); - return false; - } + global_state_store(); vm_stop(RUN_STATE_SAVE_VM); bdrv_drain_all_begin(); - aio_context_acquire(aio_context); - memset(sn, 0, sizeof(*sn)); /* fill auxiliary fields */ @@ -2887,7 +3143,7 @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate, goto the_end; } ret = qemu_savevm_state(f, errp); - vm_state_size = qemu_ftell(f); + vm_state_size = qemu_file_transferred(f); ret2 = qemu_fclose(f); if (ret < 0) { goto the_end; @@ -2897,14 +3153,6 @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate, goto the_end; } - /* The bdrv_all_create_snapshot() call that follows acquires the AioContext - * for itself. BDRV_POLL_WHILE() does not support nested locking because - * it only releases the lock once. Therefore synchronous I/O will deadlock - * unless we release the AioContext before bdrv_all_create_snapshot(). - */ - aio_context_release(aio_context); - aio_context = NULL; - ret = bdrv_all_create_snapshot(sn, bs, vm_state_size, has_devices, devices, errp); if (ret < 0) { @@ -2915,15 +3163,9 @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate, ret = 0; the_end: - if (aio_context) { - aio_context_release(aio_context); - } - bdrv_drain_all_end(); - if (saved_vm_running) { - vm_start(); - } + vm_resume(saved_state); return ret == 0; } @@ -2951,7 +3193,7 @@ void qmp_xen_save_devices_state(const char *filename, bool has_live, bool live, goto the_end; } qio_channel_set_name(QIO_CHANNEL(ioc), "migration-xen-save-state"); - f = qemu_fopen_channel_output(QIO_CHANNEL(ioc)); + f = qemu_file_new_output(QIO_CHANNEL(ioc)); object_unref(OBJECT(ioc)); ret = qemu_save_device_state(f); if (ret < 0 || qemu_fclose(f) < 0) { @@ -2998,7 +3240,7 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp) return; } qio_channel_set_name(QIO_CHANNEL(ioc), "migration-xen-load-state"); - f = qemu_fopen_channel_input(QIO_CHANNEL(ioc)); + f = qemu_file_new_input(QIO_CHANNEL(ioc)); object_unref(OBJECT(ioc)); ret = qemu_loadvm_state(f); @@ -3016,7 +3258,6 @@ bool load_snapshot(const char *name, const char *vmstate, QEMUSnapshotInfo sn; QEMUFile *f; int ret; - AioContext *aio_context; MigrationIncomingState *mis = migration_incoming_get_current(); if (!bdrv_all_can_snapshot(has_devices, devices, errp)) { @@ -3036,12 +3277,9 @@ bool load_snapshot(const char *name, const char *vmstate, if (!bs_vm_state) { return false; } - aio_context = bdrv_get_aio_context(bs_vm_state); /* Don't even try to load empty VM states */ - aio_context_acquire(aio_context); ret = bdrv_snapshot_find(bs_vm_state, &sn, name); - aio_context_release(aio_context); if (ret < 0) { return false; } else if (sn.vm_state_size == 0) { @@ -3071,17 +3309,15 @@ bool load_snapshot(const char *name, const char *vmstate, goto err_drain; } - qemu_system_reset(SHUTDOWN_CAUSE_NONE); + qemu_system_reset(SHUTDOWN_CAUSE_SNAPSHOT_LOAD); mis->from_src_file = f; if (!yank_register_instance(MIGRATION_YANK_INSTANCE, errp)) { ret = -EINVAL; goto err_drain; } - aio_context_acquire(aio_context); ret = qemu_loadvm_state(f); migration_incoming_state_destroy(); - aio_context_release(aio_context); bdrv_drain_all_end(); @@ -3097,6 +3333,14 @@ err_drain: return false; } +void load_snapshot_resume(RunState state) +{ + vm_resume(state); + if (state == RUN_STATE_RUNNING && runstate_get() == RUN_STATE_SUSPENDED) { + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, &error_abort); + } +} + bool delete_snapshot(const char *name, bool has_devices, strList *devices, Error **errp) { @@ -3161,16 +3405,15 @@ static void snapshot_load_job_bh(void *opaque) { Job *job = opaque; SnapshotJob *s = container_of(job, SnapshotJob, common); - int orig_vm_running; + RunState orig_state = runstate_get(); job_progress_set_remaining(&s->common, 1); - orig_vm_running = runstate_is_running(); vm_stop(RUN_STATE_RESTORE_VM); s->ret = load_snapshot(s->tag, s->vmstate, true, s->devices, s->errp); - if (s->ret && orig_vm_running) { - vm_start(); + if (s->ret) { + load_snapshot_resume(orig_state); } job_progress_update(&s->common, 1); diff --git a/migration/savevm.h b/migration/savevm.h index 6461342cb4..74669733dd 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -31,6 +31,7 @@ bool qemu_savevm_state_blocked(Error **errp); void qemu_savevm_non_migratable_list(strList **reasons); +int qemu_savevm_state_prepare(Error **errp); void qemu_savevm_state_setup(QEMUFile *f); bool qemu_savevm_state_guest_unplug_pending(void); int qemu_savevm_state_resume_prepare(MigrationState *s); @@ -40,10 +41,10 @@ void qemu_savevm_state_cleanup(void); void qemu_savevm_state_complete_postcopy(QEMUFile *f); int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, bool inactivate_disks); -void qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size, - uint64_t *res_precopy_only, - uint64_t *res_compatible, - uint64_t *res_postcopy_only); +void qemu_savevm_state_pending_exact(uint64_t *must_precopy, + uint64_t *can_postcopy); +void qemu_savevm_state_pending_estimate(uint64_t *must_precopy, + uint64_t *can_postcopy); void qemu_savevm_send_ping(QEMUFile *f, uint32_t value); void qemu_savevm_send_open_return_path(QEMUFile *f); int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len); @@ -65,6 +66,7 @@ int qemu_loadvm_state(QEMUFile *f); void qemu_loadvm_state_cleanup(void); int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis); int qemu_load_device_state(QEMUFile *f); +int qemu_loadvm_approve_switchover(void); int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, bool in_postcopy, bool inactivate_disks); diff --git a/migration/socket.c b/migration/socket.c index 4fd5e85f50..9ab89b1e08 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -26,7 +26,10 @@ #include "io/channel-socket.h" #include "io/net-listener.h" #include "trace.h" - +#include "postcopy-ram.h" +#include "options.h" +#include "qapi/clone-visitor.h" +#include "qapi/qapi-visit-sockets.h" struct SocketOutgoingArgs { SocketAddress *saddr; @@ -39,15 +42,22 @@ void socket_send_channel_create(QIOTaskFunc f, void *data) f, data, NULL, NULL); } -int socket_send_channel_destroy(QIOChannel *send) +QIOChannel *socket_send_channel_create_sync(Error **errp) { - /* Remove channel */ - object_unref(OBJECT(send)); - if (outgoing_args.saddr) { - qapi_free_SocketAddress(outgoing_args.saddr); - outgoing_args.saddr = NULL; + QIOChannelSocket *sioc = qio_channel_socket_new(); + + if (!outgoing_args.saddr) { + object_unref(OBJECT(sioc)); + error_setg(errp, "Initial sock address not set!"); + return NULL; } - return 0; + + if (qio_channel_socket_connect_sync(sioc, outgoing_args.saddr, errp) < 0) { + object_unref(OBJECT(sioc)); + return NULL; + } + + return QIO_CHANNEL(sioc); } struct SocketConnectData { @@ -79,7 +89,7 @@ static void socket_outgoing_migration(QIOTask *task, trace_migration_socket_outgoing_connected(data->hostname); - if (migrate_use_zero_copy_send() && + if (migrate_zero_copy_send() && !qio_channel_has_feature(sioc, QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY)) { error_setg(&err, "Zero copy send feature not detected in host kernel"); } @@ -89,19 +99,19 @@ out: object_unref(OBJECT(sioc)); } -static void -socket_start_outgoing_migration_internal(MigrationState *s, - SocketAddress *saddr, - Error **errp) +void socket_start_outgoing_migration(MigrationState *s, + SocketAddress *saddr, + Error **errp) { QIOChannelSocket *sioc = qio_channel_socket_new(); struct SocketConnectData *data = g_new0(struct SocketConnectData, 1); + SocketAddress *addr = QAPI_CLONE(SocketAddress, saddr); data->s = s; /* in case previous migration leaked it */ qapi_free_SocketAddress(outgoing_args.saddr); - outgoing_args.saddr = saddr; + outgoing_args.saddr = addr; if (saddr->type == SOCKET_ADDRESS_TYPE_INET) { data->hostname = g_strdup(saddr->u.inet.host); @@ -116,16 +126,12 @@ socket_start_outgoing_migration_internal(MigrationState *s, NULL); } -void socket_start_outgoing_migration(MigrationState *s, - const char *str, - Error **errp) +void socket_cleanup_outgoing_migration(void) { - Error *err = NULL; - SocketAddress *saddr = socket_parse(str, &err); - if (!err) { - socket_start_outgoing_migration_internal(s, saddr, &err); + if (outgoing_args.saddr) { + qapi_free_SocketAddress(outgoing_args.saddr); + outgoing_args.saddr = NULL; } - error_propagate(errp, err); } static void socket_accept_incoming_migration(QIONetListener *listener, @@ -153,9 +159,8 @@ socket_incoming_migration_end(void *opaque) object_unref(OBJECT(listener)); } -static void -socket_start_incoming_migration_internal(SocketAddress *saddr, - Error **errp) +void socket_start_incoming_migration(SocketAddress *saddr, + Error **errp) { QIONetListener *listener = qio_net_listener_new(); MigrationIncomingState *mis = migration_incoming_get_current(); @@ -164,8 +169,10 @@ socket_start_incoming_migration_internal(SocketAddress *saddr, qio_net_listener_set_name(listener, "migration-socket-listener"); - if (migrate_use_multifd()) { + if (migrate_multifd()) { num = migrate_multifd_channels(); + } else if (migrate_postcopy_preempt()) { + num = RAM_CHANNEL_MAX; } if (qio_net_listener_open_sync(listener, saddr, num, errp) < 0) { @@ -192,13 +199,3 @@ socket_start_incoming_migration_internal(SocketAddress *saddr, } } -void socket_start_incoming_migration(const char *str, Error **errp) -{ - Error *err = NULL; - SocketAddress *saddr = socket_parse(str, &err); - if (!err) { - socket_start_incoming_migration_internal(saddr, &err); - } - qapi_free_SocketAddress(saddr); - error_propagate(errp, err); -} diff --git a/migration/socket.h b/migration/socket.h index 891dbccceb..46c233ecd2 100644 --- a/migration/socket.h +++ b/migration/socket.h @@ -19,12 +19,15 @@ #include "io/channel.h" #include "io/task.h" +#include "qemu/sockets.h" void socket_send_channel_create(QIOTaskFunc f, void *data); -int socket_send_channel_destroy(QIOChannel *send); +QIOChannel *socket_send_channel_create_sync(Error **errp); -void socket_start_incoming_migration(const char *str, Error **errp); +void socket_start_incoming_migration(SocketAddress *saddr, Error **errp); + +void socket_start_outgoing_migration(MigrationState *s, + SocketAddress *saddr, Error **errp); +void socket_cleanup_outgoing_migration(void); -void socket_start_outgoing_migration(MigrationState *s, const char *str, - Error **errp); #endif diff --git a/migration/target.c b/migration/target.c index 907ebf0a0a..a6ffa9a5ce 100644 --- a/migration/target.c +++ b/migration/target.c @@ -8,18 +8,31 @@ #include "qemu/osdep.h" #include "qapi/qapi-types-migration.h" #include "migration.h" +#include CONFIG_DEVICES #ifdef CONFIG_VFIO #include "hw/vfio/vfio-common.h" #endif -void populate_vfio_info(MigrationInfo *info) -{ #ifdef CONFIG_VFIO +void migration_populate_vfio_info(MigrationInfo *info) +{ if (vfio_mig_active()) { - info->has_vfio = true; info->vfio = g_malloc0(sizeof(*info->vfio)); info->vfio->transferred = vfio_mig_bytes_transferred(); } -#endif } + +void migration_reset_vfio_bytes_transferred(void) +{ + vfio_reset_bytes_transferred(); +} +#else +void migration_populate_vfio_info(MigrationInfo *info) +{ +} + +void migration_reset_vfio_bytes_transferred(void) +{ +} +#endif diff --git a/migration/threadinfo.c b/migration/threadinfo.c new file mode 100644 index 0000000000..262990dd75 --- /dev/null +++ b/migration/threadinfo.c @@ -0,0 +1,64 @@ +/* + * Migration Threads info + * + * Copyright (c) 2022 HUAWEI TECHNOLOGIES CO., LTD. + * + * Authors: + * Jiang Jiacheng + * + * 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/queue.h" +#include "qemu/lockable.h" +#include "threadinfo.h" + +QemuMutex migration_threads_lock; +static QLIST_HEAD(, MigrationThread) migration_threads; + +static void __attribute__((constructor)) migration_threads_init(void) +{ + qemu_mutex_init(&migration_threads_lock); +} + +MigrationThread *migration_threads_add(const char *name, int thread_id) +{ + MigrationThread *thread = g_new0(MigrationThread, 1); + thread->name = name; + thread->thread_id = thread_id; + + WITH_QEMU_LOCK_GUARD(&migration_threads_lock) { + QLIST_INSERT_HEAD(&migration_threads, thread, node); + } + + return thread; +} + +void migration_threads_remove(MigrationThread *thread) +{ + QEMU_LOCK_GUARD(&migration_threads_lock); + if (thread) { + QLIST_REMOVE(thread, node); + g_free(thread); + } +} + +MigrationThreadInfoList *qmp_query_migrationthreads(Error **errp) +{ + MigrationThreadInfoList *head = NULL; + MigrationThreadInfoList **tail = &head; + MigrationThread *thread = NULL; + + QEMU_LOCK_GUARD(&migration_threads_lock); + QLIST_FOREACH(thread, &migration_threads, node) { + MigrationThreadInfo *info = g_new0(MigrationThreadInfo, 1); + info->name = g_strdup(thread->name); + info->thread_id = thread->thread_id; + + QAPI_LIST_APPEND(tail, info); + } + + return head; +} diff --git a/migration/threadinfo.h b/migration/threadinfo.h new file mode 100644 index 0000000000..2f356ff312 --- /dev/null +++ b/migration/threadinfo.h @@ -0,0 +1,25 @@ +/* + * Migration Threads info + * + * Copyright (c) 2022 HUAWEI TECHNOLOGIES CO., LTD. + * + * Authors: + * Jiang Jiacheng + * + * 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 "qapi/error.h" +#include "qapi/qapi-commands-migration.h" + +typedef struct MigrationThread MigrationThread; + +struct MigrationThread { + const char *name; /* the name of migration thread */ + int thread_id; /* ID of the underlying host thread */ + QLIST_ENTRY(MigrationThread) node; +}; + +MigrationThread *migration_threads_add(const char *name, int thread_id); +void migration_threads_remove(MigrationThread *info); diff --git a/migration/tls.c b/migration/tls.c index 32c384a8b6..fa03d9136c 100644 --- a/migration/tls.c +++ b/migration/tls.c @@ -22,31 +22,29 @@ #include "channel.h" #include "migration.h" #include "tls.h" +#include "options.h" #include "crypto/tlscreds.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "trace.h" static QCryptoTLSCreds * -migration_tls_get_creds(MigrationState *s, - QCryptoTLSCredsEndpoint endpoint, - Error **errp) +migration_tls_get_creds(QCryptoTLSCredsEndpoint endpoint, Error **errp) { Object *creds; + const char *tls_creds = migrate_tls_creds(); QCryptoTLSCreds *ret; - creds = object_resolve_path_component( - object_get_objects_root(), s->parameters.tls_creds); + creds = object_resolve_path_component(object_get_objects_root(), tls_creds); if (!creds) { - error_setg(errp, "No TLS credentials with id '%s'", - s->parameters.tls_creds); + error_setg(errp, "No TLS credentials with id '%s'", tls_creds); return NULL; } ret = (QCryptoTLSCreds *)object_dynamic_cast( creds, TYPE_QCRYPTO_TLS_CREDS); if (!ret) { error_setg(errp, "Object with id '%s' is not TLS credentials", - s->parameters.tls_creds); + tls_creds); return NULL; } if (!qcrypto_tls_creds_check_endpoint(ret, endpoint, errp)) { @@ -80,16 +78,12 @@ void migration_tls_channel_process_incoming(MigrationState *s, QCryptoTLSCreds *creds; QIOChannelTLS *tioc; - creds = migration_tls_get_creds( - s, QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, errp); + creds = migration_tls_get_creds(QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, errp); if (!creds) { return; } - tioc = qio_channel_tls_new_server( - ioc, creds, - s->parameters.tls_authz, - errp); + tioc = qio_channel_tls_new_server(ioc, creds, migrate_tls_authz(), errp); if (!tioc) { return; } @@ -120,28 +114,23 @@ static void migration_tls_outgoing_handshake(QIOTask *task, object_unref(OBJECT(ioc)); } -QIOChannelTLS *migration_tls_client_create(MigrationState *s, - QIOChannel *ioc, +QIOChannelTLS *migration_tls_client_create(QIOChannel *ioc, const char *hostname, Error **errp) { QCryptoTLSCreds *creds; - QIOChannelTLS *tioc; - creds = migration_tls_get_creds( - s, QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, errp); + creds = migration_tls_get_creds(QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, errp); if (!creds) { return NULL; } - if (s->parameters.tls_hostname && *s->parameters.tls_hostname) { - hostname = s->parameters.tls_hostname; + const char *tls_hostname = migrate_tls_hostname(); + if (tls_hostname && *tls_hostname) { + hostname = tls_hostname; } - tioc = qio_channel_tls_new_client( - ioc, creds, hostname, errp); - - return tioc; + return qio_channel_tls_new_client(ioc, creds, hostname, errp); } void migration_tls_channel_connect(MigrationState *s, @@ -151,7 +140,7 @@ void migration_tls_channel_connect(MigrationState *s, { QIOChannelTLS *tioc; - tioc = migration_tls_client_create(s, ioc, hostname, errp); + tioc = migration_tls_client_create(ioc, hostname, errp); if (!tioc) { return; } @@ -166,3 +155,12 @@ void migration_tls_channel_connect(MigrationState *s, NULL, NULL); } + +bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc) +{ + if (!migrate_tls()) { + return false; + } + + return !object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_TLS); +} diff --git a/migration/tls.h b/migration/tls.h index de4fe2cafd..5797d153cb 100644 --- a/migration/tls.h +++ b/migration/tls.h @@ -28,8 +28,7 @@ void migration_tls_channel_process_incoming(MigrationState *s, QIOChannel *ioc, Error **errp); -QIOChannelTLS *migration_tls_client_create(MigrationState *s, - QIOChannel *ioc, +QIOChannelTLS *migration_tls_client_create(QIOChannel *ioc, const char *hostname, Error **errp); @@ -37,4 +36,8 @@ void migration_tls_channel_connect(MigrationState *s, QIOChannel *ioc, const char *hostname, Error **errp); + +/* Whether the QIO channel requires further TLS handshake? */ +bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc); + #endif diff --git a/migration/trace-events b/migration/trace-events index 1aec580e92..f0e1cb80c7 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -7,6 +7,7 @@ qemu_loadvm_state_section_partend(uint32_t section_id) "%u" qemu_loadvm_state_post_main(int ret) "%d" qemu_loadvm_state_section_startfull(uint32_t section_id, const char *idstr, uint32_t instance_id, uint32_t version_id) "%u(%s) %u %u" qemu_savevm_send_packaged(void) "" +loadvm_state_switchover_ack_needed(unsigned int switchover_ack_pending_num) "Switchover ack pending num=%u" loadvm_state_setup(void) "" loadvm_state_cleanup(void) "" loadvm_handle_cmd_packaged(unsigned int length) "%u" @@ -16,13 +17,13 @@ loadvm_handle_recv_bitmap(char *s) "%s" loadvm_postcopy_handle_advise(void) "" loadvm_postcopy_handle_listen(const char *str) "%s" loadvm_postcopy_handle_run(void) "" -loadvm_postcopy_handle_run_bh(const char *str) "%s" loadvm_postcopy_handle_resume(void) "" loadvm_postcopy_ram_handle_discard(void) "" loadvm_postcopy_ram_handle_discard_end(void) "" loadvm_postcopy_ram_handle_discard_header(const char *ramid, uint16_t len) "%s: %ud" loadvm_process_command(const char *s, uint16_t len) "com=%s len=%d" loadvm_process_command_ping(uint32_t val) "0x%x" +loadvm_approve_switchover(unsigned int switchover_ack_pending_num) "Switchover ack pending num=%u" postcopy_ram_listen_thread_exit(void) "" postcopy_ram_listen_thread_start(void) "" qemu_savevm_send_postcopy_advise(void) "" @@ -46,6 +47,9 @@ savevm_state_cleanup(void) "" savevm_state_complete_precopy(void) "" vmstate_save(const char *idstr, const char *vmsd_name) "%s, %s" vmstate_load(const char *idstr, const char *vmsd_name) "%s, %s" +vmstate_downtime_save(const char *type, const char *idstr, uint32_t instance_id, int64_t downtime) "type=%s idstr=%s instance_id=%d downtime=%"PRIi64 +vmstate_downtime_load(const char *type, const char *idstr, uint32_t instance_id, int64_t downtime) "type=%s idstr=%s instance_id=%d downtime=%"PRIi64 +vmstate_downtime_checkpoint(const char *checkpoint) "%s" postcopy_pause_incoming(void) "" postcopy_pause_incoming_continued(void) "" postcopy_page_req_sync(void *host_addr) "sync page req %p" @@ -64,6 +68,7 @@ vmstate_save_state_loop(const char *name, const char *field, int n_elems) "%s/%s vmstate_save_state_top(const char *idstr) "%s" vmstate_subsection_save_loop(const char *name, const char *sub) "%s/%s" vmstate_subsection_save_top(const char *idstr) "%s" +vmstate_field_exists(const char *vmsd, const char *name, int field_version, int version, int result) "%s:%s field_version %d version %d result %d" # vmstate-types.c get_qtailq(const char *name, int version_id) "%s v%d" @@ -85,13 +90,16 @@ put_qlist_end(const char *field_name, const char *vmsd_name) "%s(%s)" qemu_file_fclose(void) "" # ram.c +get_queued_page(const char *block_name, uint64_t tmp_offset, unsigned long page_abs) "%s/0x%" PRIx64 " page_abs=0x%lx" +get_queued_page_not_dirty(const char *block_name, uint64_t tmp_offset, unsigned long page_abs) "%s/0x%" PRIx64 " page_abs=0x%lx" migration_bitmap_sync_start(void) "" migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64 migration_bitmap_clear_dirty(char *str, uint64_t start, uint64_t size, unsigned long page) "rb %s start 0x%"PRIx64" size 0x%"PRIx64" page 0x%lx" migration_throttle(void) "" +migration_dirty_limit_guest(int64_t dirtyrate) "guest dirty page rate limit %" PRIi64 " MB/s" ram_discard_range(const char *rbname, uint64_t start, size_t len) "%s: start: %" PRIx64 " %zx" ram_load_loop(const char *rbname, uint64_t addr, int flags, void *host) "%s: addr: 0x%" PRIx64 " flags: 0x%x host: %p" -ram_load_postcopy_loop(uint64_t addr, int flags) "@%" PRIx64 " %x" +ram_load_postcopy_loop(int channel, uint64_t addr, int flags) "chan=%d addr=0x%" PRIx64 " flags=0x%x" ram_postcopy_send_discard_bitmap(void) "" ram_save_page(const char *rbname, uint64_t offset, void *host) "%s: offset: 0x%" PRIx64 " host: %p" ram_save_queue_pages(const char *rbname, size_t start, size_t len) "%s: start: 0x%zx len: 0x%zx" @@ -110,40 +118,45 @@ ram_save_iterate_big_wait(uint64_t milliconds, int iterations) "big wait: %" PRI ram_load_complete(int ret, uint64_t seq_iter) "exit_code %d seq iteration %" PRIu64 ram_write_tracking_ramblock_start(const char *block_id, size_t page_size, void *addr, size_t length) "%s: page_size: %zu addr: %p length: %zu" ram_write_tracking_ramblock_stop(const char *block_id, size_t page_size, void *addr, size_t length) "%s: page_size: %zu addr: %p length: %zu" -unqueue_page(char *block, uint64_t offset, bool dirty) "ramblock '%s' offset 0x%"PRIx64" dirty %d" +postcopy_preempt_triggered(char *str, unsigned long page) "during sending ramblock %s offset 0x%lx" +postcopy_preempt_restored(char *str, unsigned long page) "ramblock %s offset 0x%lx" +postcopy_preempt_hit(char *str, uint64_t offset) "ramblock %s offset 0x%"PRIx64 +postcopy_preempt_send_host_page(char *str, uint64_t offset) "ramblock %s offset 0x%"PRIx64 +postcopy_preempt_switch_channel(int channel) "%d" +postcopy_preempt_reset_channel(void) "" # multifd.c multifd_new_send_channel_async(uint8_t id) "channel %u" -multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " pages %u flags 0x%x next packet size %u" +multifd_new_send_channel_async_error(uint8_t id, void *err) "channel=%u err=%p" +multifd_recv(uint8_t id, uint64_t packet_num, uint32_t normal, uint32_t zero, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " normal pages %u zero pages %u flags 0x%x next packet size %u" multifd_recv_new_channel(uint8_t id) "channel %u" multifd_recv_sync_main(long packet_num) "packet num %ld" multifd_recv_sync_main_signal(uint8_t id) "channel %u" -multifd_recv_sync_main_wait(uint8_t id) "channel %u" +multifd_recv_sync_main_wait(uint8_t id) "iter %u" multifd_recv_terminate_threads(bool error) "error %d" -multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %u packets %" PRIu64 " pages %" PRIu64 +multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t normal_pages, uint64_t zero_pages) "channel %u packets %" PRIu64 " normal pages %" PRIu64 " zero pages %" PRIu64 multifd_recv_thread_start(uint8_t id) "%u" -multifd_send(uint8_t id, uint64_t packet_num, uint32_t normal, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " normal pages %u flags 0x%x next packet size %u" +multifd_send(uint8_t id, uint64_t packet_num, uint32_t normal_pages, uint32_t zero_pages, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " normal pages %u zero pages %u flags 0x%x next packet size %u" multifd_send_error(uint8_t id) "channel %u" multifd_send_sync_main(long packet_num) "packet num %ld" multifd_send_sync_main_signal(uint8_t id) "channel %u" multifd_send_sync_main_wait(uint8_t id) "channel %u" -multifd_send_terminate_threads(bool error) "error %d" -multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t normal_pages) "channel %u packets %" PRIu64 " normal pages %" PRIu64 +multifd_send_terminate_threads(void) "" +multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t normal_pages, uint64_t zero_pages) "channel %u packets %" PRIu64 " normal pages %" PRIu64 " zero pages %" PRIu64 multifd_send_thread_start(uint8_t id) "%u" multifd_tls_outgoing_handshake_start(void *ioc, void *tioc, const char *hostname) "ioc=%p tioc=%p hostname=%s" multifd_tls_outgoing_handshake_error(void *ioc, const char *err) "ioc=%p err=%s" multifd_tls_outgoing_handshake_complete(void *ioc) "ioc=%p" -multifd_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname, void *err) "ioc=%p ioctype=%s hostname=%s err=%p" +multifd_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname) "ioc=%p ioctype=%s hostname=%s" # migration.c -await_return_path_close_on_source_close(void) "" -await_return_path_close_on_source_joining(void) "" migrate_set_state(const char *new_state) "new state %s" migrate_fd_cleanup(void) "" migrate_fd_error(const char *error_desc) "error=%s" migrate_fd_cancel(void) "" migrate_handle_rp_req_pages(const char *rbname, size_t start, size_t len) "in %s at 0x%zx len 0x%zx" -migrate_pending(uint64_t size, uint64_t max, uint64_t pre, uint64_t compat, uint64_t post) "pending size %" PRIu64 " max %" PRIu64 " (pre = %" PRIu64 " compat=%" PRIu64 " post=%" PRIu64 ")" +migrate_pending_exact(uint64_t size, uint64_t pre, uint64_t post) "exact pending size %" PRIu64 " (pre = %" PRIu64 " post=%" PRIu64 ")" +migrate_pending_estimate(uint64_t size, uint64_t pre, uint64_t post) "estimate pending size %" PRIu64 " (pre = %" PRIu64 " post=%" PRIu64 ")" migrate_send_rp_message(int msg_type, uint16_t len) "%d: len %d" migrate_send_rp_recv_bitmap(char *name, int64_t size) "block '%s' size 0x%"PRIi64 migration_completion_file_err(void) "" @@ -153,7 +166,7 @@ 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_return_path_end_after(void) "" migration_thread_after_loop(void) "" migration_thread_file_err(void) "" migration_thread_setup_complete(void) "" @@ -172,10 +185,15 @@ source_return_path_thread_loop_top(void) "" source_return_path_thread_pong(uint32_t val) "0x%x" source_return_path_thread_shut(uint32_t val) "0x%x" source_return_path_thread_resume_ack(uint32_t v) "%"PRIu32 +source_return_path_thread_switchover_acked(void) "" migration_thread_low_pending(uint64_t pending) "%" PRIu64 -migrate_transferred(uint64_t tranferred, uint64_t time_spent, uint64_t bandwidth, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %" PRIu64 " max_size %" PRId64 +migrate_transferred(uint64_t transferred, uint64_t time_spent, uint64_t bandwidth, uint64_t avail_bw, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %" PRIu64 " switchover_bw %" PRIu64 " max_size %" PRId64 process_incoming_migration_co_end(int ret, int ps) "ret=%d postcopy-state=%d" process_incoming_migration_co_postcopy_end_main(void) "" +postcopy_preempt_enabled(bool value) "%d" + +# migration-stats +migration_transferred_bytes(uint64_t qemu_file, uint64_t multifd, uint64_t rdma) "qemu_file %" PRIu64 " multifd %" PRIu64 " RDMA %" PRIu64 # channel.c migration_set_incoming_channel(void *ioc, const char *ioctype) "ioc=%p ioctype=%s" @@ -191,12 +209,14 @@ qemu_rdma_accept_incoming_migration(void) "" qemu_rdma_accept_incoming_migration_accepted(void) "" qemu_rdma_accept_pin_state(bool pin) "%d" qemu_rdma_accept_pin_verbsc(void *verbs) "Verbs context after listen: %p" -qemu_rdma_block_for_wrid_miss(const char *wcompstr, int wcomp, const char *gcompstr, uint64_t req) "A Wanted wrid %s (%d) but got %s (%" PRIu64 ")" +qemu_rdma_block_for_wrid_miss(uint64_t wcomp, uint64_t req) "A Wanted wrid %" PRIu64 " but got %" PRIu64 qemu_rdma_cleanup_disconnect(void) "" qemu_rdma_close(void) "" qemu_rdma_connect_pin_all_requested(void) "" qemu_rdma_connect_pin_all_outcome(bool pin) "%d" qemu_rdma_dest_init_trying(const char *host, const char *ip) "%s => %s" +qemu_rdma_dump_id_failed(const char *who) "%s RDMA Device opened, but can't query port information" +qemu_rdma_dump_id(const char *who, const char *name, const char *dev_name, const char *dev_path, const char *ibdev_path, int transport, const char *transport_name) "%s RDMA Device opened: kernel name %s uverbs device name %s, infiniband_verbs class device path %s, infiniband class device path %s, transport: (%d) %s" qemu_rdma_dump_gid(const char *who, const char *src, const char *dst) "%s Source GID: %s, Dest GID: %s" qemu_rdma_exchange_get_response_start(const char *desc) "CONTROL: %s receiving..." qemu_rdma_exchange_get_response_none(const char *desc, int type) "Surprise: got %s (%d)" @@ -205,27 +225,13 @@ qemu_rdma_exchange_send_waiting(const char *desc) "Waiting for response %s" qemu_rdma_exchange_send_received(const char *desc) "Response %s received." qemu_rdma_fill(size_t control_len, size_t size) "RDMA %zd of %zd bytes already in buffer" qemu_rdma_init_ram_blocks(int blocks) "Allocated %d local ram block structures" -qemu_rdma_poll_recv(const char *compstr, int64_t comp, int64_t id, int sent) "completion %s #%" PRId64 " received (%" PRId64 ") left %d" -qemu_rdma_poll_write(const char *compstr, int64_t comp, int left, uint64_t block, uint64_t chunk, void *local, void *remote) "completions %s (%" PRId64 ") left %d, block %" PRIu64 ", chunk: %" PRIu64 " %p %p" -qemu_rdma_poll_other(const char *compstr, int64_t comp, int left) "other completion %s (%" PRId64 ") received left %d" +qemu_rdma_poll_recv(uint64_t comp, int64_t id, int sent) "completion %" PRIu64 " received (%" PRId64 ") left %d" +qemu_rdma_poll_write(uint64_t comp, int left, uint64_t block, uint64_t chunk, void *local, void *remote) "completions %" PRIu64 " left %d, block %" PRIu64 ", chunk: %" PRIu64 " %p %p" +qemu_rdma_poll_other(uint64_t comp, int left) "other completion %" PRIu64 " received left %d" qemu_rdma_post_send_control(const char *desc) "CONTROL: sending %s.." qemu_rdma_register_and_get_keys(uint64_t len, void *start) "Registering %" PRIu64 " bytes @ %p" qemu_rdma_register_odp_mr(const char *name) "Try to register On-Demand Paging memory region: %s" qemu_rdma_advise_mr(const char *name, uint32_t len, uint64_t addr, const char *res) "Try to advise block %s prefetch at %" PRIu32 "@0x%" PRIx64 ": %s" -qemu_rdma_registration_handle_compress(int64_t length, int index, int64_t offset) "Zapping zero chunk: %" PRId64 " bytes, index %d, offset %" PRId64 -qemu_rdma_registration_handle_finished(void) "" -qemu_rdma_registration_handle_ram_blocks(void) "" -qemu_rdma_registration_handle_ram_blocks_loop(const char *name, uint64_t offset, uint64_t length, void *local_host_addr, unsigned int src_index) "%s: @0x%" PRIx64 "/%" PRIu64 " host:@%p src_index: %u" -qemu_rdma_registration_handle_register(int requests) "%d requests" -qemu_rdma_registration_handle_register_loop(int req, int index, uint64_t addr, uint64_t chunks) "Registration request (%d): index %d, current_addr %" PRIu64 " chunks: %" PRIu64 -qemu_rdma_registration_handle_register_rkey(int rkey) "0x%x" -qemu_rdma_registration_handle_unregister(int requests) "%d requests" -qemu_rdma_registration_handle_unregister_loop(int count, int index, uint64_t chunk) "Unregistration request (%d): index %d, chunk %" PRIu64 -qemu_rdma_registration_handle_unregister_success(uint64_t chunk) "%" PRIu64 -qemu_rdma_registration_handle_wait(void) "" -qemu_rdma_registration_start(uint64_t flags) "%" PRIu64 -qemu_rdma_registration_stop(uint64_t flags) "%" PRIu64 -qemu_rdma_registration_stop_ram(void) "" qemu_rdma_resolve_host_trying(const char *host, const char *ip) "Trying %s => %s" qemu_rdma_signal_unregister_append(uint64_t chunk, int pos) "Appending unregister chunk %" PRIu64 " at position %d" qemu_rdma_signal_unregister_already(uint64_t chunk) "Unregister chunk %" PRIu64 " already in queue" @@ -244,6 +250,20 @@ qemu_rdma_write_one_zero(uint64_t chunk, int len, int index, int64_t offset) "En rdma_add_block(const char *block_name, int block, uint64_t addr, uint64_t offset, uint64_t len, uint64_t end, uint64_t bits, int chunks) "Added Block: '%s':%d, addr: %" PRIu64 ", offset: %" PRIu64 " length: %" PRIu64 " end: %" PRIu64 " bits %" PRIu64 " chunks %d" rdma_block_notification_handle(const char *name, int index) "%s at %d" rdma_delete_block(void *block, uint64_t addr, uint64_t offset, uint64_t len, uint64_t end, uint64_t bits, int chunks) "Deleted Block: %p, addr: %" PRIu64 ", offset: %" PRIu64 " length: %" PRIu64 " end: %" PRIu64 " bits %" PRIu64 " chunks %d" +rdma_registration_handle_compress(int64_t length, int index, int64_t offset) "Zapping zero chunk: %" PRId64 " bytes, index %d, offset %" PRId64 +rdma_registration_handle_finished(void) "" +rdma_registration_handle_ram_blocks(void) "" +rdma_registration_handle_ram_blocks_loop(const char *name, uint64_t offset, uint64_t length, void *local_host_addr, unsigned int src_index) "%s: @0x%" PRIx64 "/%" PRIu64 " host:@%p src_index: %u" +rdma_registration_handle_register(int requests) "%d requests" +rdma_registration_handle_register_loop(int req, int index, uint64_t addr, uint64_t chunks) "Registration request (%d): index %d, current_addr %" PRIu64 " chunks: %" PRIu64 +rdma_registration_handle_register_rkey(int rkey) "0x%x" +rdma_registration_handle_unregister(int requests) "%d requests" +rdma_registration_handle_unregister_loop(int count, int index, uint64_t chunk) "Unregistration request (%d): index %d, chunk %" PRIu64 +rdma_registration_handle_unregister_success(uint64_t chunk) "%" PRIu64 +rdma_registration_handle_wait(void) "" +rdma_registration_start(uint64_t flags) "%" PRIu64 +rdma_registration_stop(uint64_t flags) "%" PRIu64 +rdma_registration_stop_ram(void) "" rdma_start_incoming_migration(void) "" rdma_start_incoming_migration_after_dest_init(void) "" rdma_start_incoming_migration_after_rdma_listen(void) "" @@ -263,6 +283,8 @@ mark_postcopy_blocktime_begin(uint64_t addr, void *dd, uint32_t time, int cpu, i mark_postcopy_blocktime_end(uint64_t addr, void *dd, uint32_t time, int affected_cpu) "addr: 0x%" PRIx64 ", dd: %p, time: %u, affected_cpu: %d" postcopy_pause_fault_thread(void) "" postcopy_pause_fault_thread_continued(void) "" +postcopy_pause_fast_load(void) "" +postcopy_pause_fast_load_continued(void) "" postcopy_ram_fault_thread_entry(void) "" postcopy_ram_fault_thread_exit(void) "" postcopy_ram_fault_thread_fds_core(int baseufd, int quitfd) "ufd: %d quitfd: %d" @@ -278,6 +300,10 @@ postcopy_request_shared_page(const char *sharer, const char *rb, uint64_t rb_off postcopy_request_shared_page_present(const char *sharer, const char *rb, uint64_t rb_offset) "%s already %s offset 0x%"PRIx64 postcopy_wake_shared(uint64_t client_addr, const char *rb) "at 0x%"PRIx64" in %s" postcopy_page_req_del(void *addr, int count) "resolved page req %p total %d" +postcopy_preempt_tls_handshake(void) "" +postcopy_preempt_new_channel(void) "" +postcopy_preempt_thread_entry(void) "" +postcopy_preempt_thread_exit(void) "" get_mem_fault_cpu_index(int cpu, uint32_t pid) "cpu: %d, pid: %u" @@ -289,6 +315,10 @@ migration_exec_incoming(const char *cmd) "cmd=%s" migration_fd_outgoing(int fd) "fd=%d" migration_fd_incoming(int fd) "fd=%d" +# file.c +migration_file_outgoing(const char *filename) "filename=%s" +migration_file_incoming(const char *filename) "filename=%s" + # socket.c migration_socket_incoming_accepted(void) "" migration_socket_outgoing_connected(const char *hostname) "hostname=%s" @@ -316,7 +346,7 @@ send_bitmap_bits(uint32_t flags, uint64_t start_sector, uint32_t nr_sectors, uin dirty_bitmap_save_iterate(int in_postcopy) "in postcopy: %d" dirty_bitmap_save_complete_enter(void) "" dirty_bitmap_save_complete_finish(void) "" -dirty_bitmap_save_pending(uint64_t pending, uint64_t max_size) "pending %" PRIu64 " max: %" PRIu64 +dirty_bitmap_state_pending(uint64_t pending) "pending %" PRIu64 dirty_bitmap_load_complete(void) "" dirty_bitmap_load_bits_enter(uint64_t first_sector, uint32_t nr_sectors) "chunk: %" PRIu64 " %" PRIu32 dirty_bitmap_load_bits_zeroes(void) "" @@ -327,8 +357,8 @@ dirty_bitmap_load_success(void) "" # dirtyrate.c dirtyrate_set_state(const char *new_state) "new state %s" query_dirty_rate_info(const char *new_state) "current state %s" -get_ramblock_vfn_hash(const char *idstr, uint64_t vfn, uint32_t crc) "ramblock name: %s, vfn: %"PRIu64 ", crc: %" PRIu32 -calc_page_dirty_rate(const char *idstr, uint32_t new_crc, uint32_t old_crc) "ramblock name: %s, new crc: %" PRIu32 ", old crc: %" PRIu32 +get_ramblock_vfn_hash(const char *idstr, uint64_t vfn, uint32_t hash) "ramblock name: %s, vfn: %"PRIu64 ", hash: %" PRIu32 +calc_page_dirty_rate(const char *idstr, uint32_t new_hash, uint32_t old_hash) "ramblock name: %s, new hash: %" PRIu32 ", old hash: %" PRIu32 skip_sample_ramblock(const char *idstr, uint64_t ramblock_size) "ramblock name: %s, ramblock size: %" PRIu64 find_page_matched(const char *idstr) "ramblock %s addr or size changed" dirtyrate_calculate(int64_t dirtyrate) "dirty rate: %" PRIi64 " MB/s" @@ -341,7 +371,8 @@ migration_block_save_device_dirty(int64_t sector) "Error reading sector %" PRId6 migration_block_flush_blks(const char *action, int submitted, int read_done, int transferred) "%s submitted %d read_done %d transferred %d" migration_block_save(const char *mig_stage, int submitted, int transferred) "Enter save live %s submitted %d transferred %d" migration_block_save_complete(void) "Block migration completed" -migration_block_save_pending(uint64_t pending) "Enter save live pending %" PRIu64 +migration_block_state_pending(uint64_t pending) "Enter save live pending %" PRIu64 +migration_block_progression(unsigned percent) "Completed %u%%" # page_cache.c migration_pagecache_init(int64_t max_num_items) "Setting cache buckets to %" PRId64 diff --git a/migration/vmstate.c b/migration/vmstate.c index 36ae8b9e19..ef26f26ccd 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -14,6 +14,7 @@ #include "migration.h" #include "migration/vmstate.h" #include "savevm.h" +#include "qapi/error.h" #include "qapi/qmp/json-writer.h" #include "qemu-file.h" #include "qemu/bitops.h" @@ -25,6 +26,30 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, void *opaque); +/* Whether this field should exist for either save or load the VM? */ +static bool +vmstate_field_exists(const VMStateDescription *vmsd, const VMStateField *field, + void *opaque, int version_id) +{ + bool result; + + if (field->field_exists) { + /* If there's the function checker, that's the solo truth */ + result = field->field_exists(opaque, version_id); + trace_vmstate_field_exists(vmsd->name, field->name, field->version_id, + version_id, result); + } else { + /* + * Otherwise, we only save/load if field version is same or older. + * For example, when loading from an old binary with old version, + * we ignore new fields with newer version_ids. + */ + result = field->version_id <= version_id; + } + + return result; +} + static int vmstate_n_elems(void *opaque, const VMStateField *field) { int n_elems = 1; @@ -97,17 +122,14 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, return -EINVAL; } if (vmsd->pre_load) { - int ret = vmsd->pre_load(opaque); + ret = vmsd->pre_load(opaque); if (ret) { return ret; } } while (field->name) { trace_vmstate_load_state_field(vmsd->name, field->name); - if ((field->field_exists && - field->field_exists(opaque, version_id)) || - (!field->field_exists && - field->version_id <= version_id)) { + if (vmstate_field_exists(vmsd, field, opaque, version_id)) { void *first_elem = opaque + field->offset; int i, n_elems = vmstate_n_elems(opaque, field); int size = vmstate_size(opaque, field); @@ -154,8 +176,10 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, } field++; } + assert(field->flags == VMS_END); ret = vmstate_subsection_load(f, vmsd, opaque); if (ret != 0) { + qemu_file_set_error(f, ret); return ret; } if (vmsd->post_load) { @@ -301,7 +325,7 @@ static void vmsd_desc_field_end(const VMStateDescription *vmsd, } -bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque) +bool vmstate_section_needed(const VMStateDescription *vmsd, void *opaque) { if (vmsd->needed && !vmsd->needed(opaque)) { /* optional section not needed */ @@ -314,11 +338,17 @@ bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque) int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, JSONWriter *vmdesc_id) { - return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id); + return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id, NULL); +} + +int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque, JSONWriter *vmdesc_id, Error **errp) +{ + return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id, errp); } int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque, JSONWriter *vmdesc, int version_id) + void *opaque, JSONWriter *vmdesc, int version_id, Error **errp) { int ret = 0; const VMStateField *field = vmsd->fields; @@ -329,7 +359,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, ret = vmsd->pre_save(opaque); trace_vmstate_save_state_pre_save_res(vmsd->name, ret); if (ret) { - error_report("pre-save failed: %s", vmsd->name); + error_setg(errp, "pre-save failed: %s", vmsd->name); return ret; } } @@ -341,14 +371,11 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, } while (field->name) { - if ((field->field_exists && - field->field_exists(opaque, version_id)) || - (!field->field_exists && - field->version_id <= version_id)) { + if (vmstate_field_exists(vmsd, field, opaque, version_id)) { void *first_elem = opaque + field->offset; int i, n_elems = vmstate_n_elems(opaque, field); int size = vmstate_size(opaque, field); - int64_t old_offset, written_bytes; + uint64_t old_offset, written_bytes; JSONWriter *vmdesc_loop = vmdesc; trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems); @@ -360,7 +387,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, void *curr_elem = first_elem + size * i; vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems); - old_offset = qemu_ftell_fast(f); + old_offset = qemu_file_transferred(f); if (field->flags & VMS_ARRAY_OF_POINTER) { assert(curr_elem); curr_elem = *(void **)curr_elem; @@ -376,21 +403,21 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, } else if (field->flags & VMS_VSTRUCT) { ret = vmstate_save_state_v(f, field->vmsd, curr_elem, vmdesc_loop, - field->struct_version_id); + field->struct_version_id, errp); } else { ret = field->info->put(f, curr_elem, size, field, vmdesc_loop); } if (ret) { - error_report("Save of field %s/%s failed", - vmsd->name, field->name); + error_setg(errp, "Save of field %s/%s failed", + vmsd->name, field->name); if (vmsd->post_save) { vmsd->post_save(opaque); } return ret; } - written_bytes = qemu_ftell_fast(f) - old_offset; + written_bytes = qemu_file_transferred(f) - old_offset; vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes, i); /* Compressed arrays only care about the first element */ @@ -407,6 +434,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, } field++; } + assert(field->flags == VMS_END); if (vmdesc) { json_writer_end_array(vmdesc); @@ -424,13 +452,15 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, } static const VMStateDescription * -vmstate_get_subsection(const VMStateDescription **sub, char *idstr) +vmstate_get_subsection(const VMStateDescription * const *sub, + const char *idstr) { - while (sub && *sub) { - if (strcmp(idstr, (*sub)->name) == 0) { - return *sub; + if (sub) { + for (const VMStateDescription *s = *sub; s ; s = *++sub) { + if (strcmp(idstr, s->name) == 0) { + return s; + } } - sub++; } return NULL; } @@ -489,13 +519,13 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, JSONWriter *vmdesc) { - const VMStateDescription **sub = vmsd->subsections; + const VMStateDescription * const *sub = vmsd->subsections; bool vmdesc_has_subsections = false; int ret = 0; trace_vmstate_subsection_save_top(vmsd->name); while (sub && *sub) { - if (vmstate_save_needed(*sub, opaque)) { + if (vmstate_section_needed(*sub, opaque)) { const VMStateDescription *vmsdsub = *sub; uint8_t len; diff --git a/migration/xbzrle.c b/migration/xbzrle.c index 1ba482ded9..3eddcf249b 100644 --- a/migration/xbzrle.c +++ b/migration/xbzrle.c @@ -12,8 +12,155 @@ */ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/host-utils.h" #include "xbzrle.h" +#if defined(CONFIG_AVX512BW_OPT) +#include +#include "host/cpuinfo.h" + +static int __attribute__((target("avx512bw"))) +xbzrle_encode_buffer_avx512(uint8_t *old_buf, uint8_t *new_buf, int slen, + uint8_t *dst, int dlen) +{ + uint32_t zrun_len = 0, nzrun_len = 0; + int d = 0, i = 0, num = 0; + uint8_t *nzrun_start = NULL; + /* add 1 to include residual part in main loop */ + uint32_t count512s = (slen >> 6) + 1; + /* countResidual is tail of data, i.e., countResidual = slen % 64 */ + uint32_t count_residual = slen & 0b111111; + bool never_same = true; + uint64_t mask_residual = 1; + mask_residual <<= count_residual; + mask_residual -= 1; + __m512i r = _mm512_set1_epi32(0); + + while (count512s) { + int bytes_to_check = 64; + uint64_t mask = 0xffffffffffffffff; + if (count512s == 1) { + bytes_to_check = count_residual; + mask = mask_residual; + } + __m512i old_data = _mm512_mask_loadu_epi8(r, + mask, old_buf + i); + __m512i new_data = _mm512_mask_loadu_epi8(r, + mask, new_buf + i); + uint64_t comp = _mm512_cmpeq_epi8_mask(old_data, new_data); + count512s--; + + bool is_same = (comp & 0x1); + while (bytes_to_check) { + if (d + 2 > dlen) { + return -1; + } + if (is_same) { + if (nzrun_len) { + d += uleb128_encode_small(dst + d, nzrun_len); + if (d + nzrun_len > dlen) { + return -1; + } + nzrun_start = new_buf + i - nzrun_len; + memcpy(dst + d, nzrun_start, nzrun_len); + d += nzrun_len; + nzrun_len = 0; + } + /* 64 data at a time for speed */ + if (count512s && (comp == 0xffffffffffffffff)) { + i += 64; + zrun_len += 64; + break; + } + never_same = false; + num = ctz64(~comp); + num = (num < bytes_to_check) ? num : bytes_to_check; + zrun_len += num; + bytes_to_check -= num; + comp >>= num; + i += num; + if (bytes_to_check) { + /* still has different data after same data */ + d += uleb128_encode_small(dst + d, zrun_len); + zrun_len = 0; + } else { + break; + } + } + if (never_same || zrun_len) { + /* + * never_same only acts if + * data begins with diff in first count512s + */ + d += uleb128_encode_small(dst + d, zrun_len); + zrun_len = 0; + never_same = false; + } + /* has diff, 64 data at a time for speed */ + if ((bytes_to_check == 64) && (comp == 0x0)) { + i += 64; + nzrun_len += 64; + break; + } + num = ctz64(comp); + num = (num < bytes_to_check) ? num : bytes_to_check; + nzrun_len += num; + bytes_to_check -= num; + comp >>= num; + i += num; + if (bytes_to_check) { + /* mask like 111000 */ + d += uleb128_encode_small(dst + d, nzrun_len); + /* overflow */ + if (d + nzrun_len > dlen) { + return -1; + } + nzrun_start = new_buf + i - nzrun_len; + memcpy(dst + d, nzrun_start, nzrun_len); + d += nzrun_len; + nzrun_len = 0; + is_same = true; + } + } + } + + if (nzrun_len != 0) { + d += uleb128_encode_small(dst + d, nzrun_len); + /* overflow */ + if (d + nzrun_len > dlen) { + return -1; + } + nzrun_start = new_buf + i - nzrun_len; + memcpy(dst + d, nzrun_start, nzrun_len); + d += nzrun_len; + } + return d; +} + +static int xbzrle_encode_buffer_int(uint8_t *old_buf, uint8_t *new_buf, + int slen, uint8_t *dst, int dlen); + +static int (*accel_func)(uint8_t *, uint8_t *, int, uint8_t *, int); + +static void __attribute__((constructor)) init_accel(void) +{ + unsigned info = cpuinfo_init(); + if (info & CPUINFO_AVX512BW) { + accel_func = xbzrle_encode_buffer_avx512; + } else { + accel_func = xbzrle_encode_buffer_int; + } +} + +int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen, + uint8_t *dst, int dlen) +{ + return accel_func(old_buf, new_buf, slen, dst, dlen); +} + +#define xbzrle_encode_buffer xbzrle_encode_buffer_int +#endif + /* page = zrun nzrun | zrun nzrun page diff --git a/migration/xbzrle.h b/migration/xbzrle.h index a0db507b9c..39e651b9ec 100644 --- a/migration/xbzrle.h +++ b/migration/xbzrle.h @@ -18,4 +18,5 @@ int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen, uint8_t *dst, int dlen); int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen); + #endif diff --git a/migration/yank_functions.c b/migration/yank_functions.c index 8c08aef14a..979e60c762 100644 --- a/migration/yank_functions.c +++ b/migration/yank_functions.c @@ -8,12 +8,9 @@ */ #include "qemu/osdep.h" -#include "qapi/error.h" #include "io/channel.h" #include "yank_functions.h" #include "qemu/yank.h" -#include "io/channel-socket.h" -#include "io/channel-tls.h" #include "qemu-file.h" void migration_yank_iochannel(void *opaque) @@ -26,8 +23,7 @@ void migration_yank_iochannel(void *opaque) /* Return whether yank is supported on this ioc */ static bool migration_ioc_yank_supported(QIOChannel *ioc) { - return object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_SOCKET) || - object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_TLS); + return qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); } void migration_ioc_register_yank(QIOChannel *ioc) @@ -35,7 +31,7 @@ void migration_ioc_register_yank(QIOChannel *ioc) if (migration_ioc_yank_supported(ioc)) { yank_register_function(MIGRATION_YANK_INSTANCE, migration_yank_iochannel, - QIO_CHANNEL(ioc)); + ioc); } } @@ -44,7 +40,7 @@ void migration_ioc_unregister_yank(QIOChannel *ioc) if (migration_ioc_yank_supported(ioc)) { yank_unregister_function(MIGRATION_YANK_INSTANCE, migration_yank_iochannel, - QIO_CHANNEL(ioc)); + ioc); } } diff --git a/monitor/fds.c b/monitor/fds.c new file mode 100644 index 0000000000..d86c2c674c --- /dev/null +++ b/monitor/fds.c @@ -0,0 +1,517 @@ +/* + * QEMU monitor file descriptor passing + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "monitor-internal.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc.h" +#include "qapi/qmp/qerror.h" +#include "qemu/ctype.h" +#include "qemu/cutils.h" +#include "sysemu/runstate.h" + +/* file descriptors passed via SCM_RIGHTS */ +typedef struct mon_fd_t mon_fd_t; +struct mon_fd_t { + char *name; + int fd; + QLIST_ENTRY(mon_fd_t) next; +}; + +/* file descriptor associated with a file descriptor set */ +typedef struct MonFdsetFd MonFdsetFd; +struct MonFdsetFd { + int fd; + bool removed; + char *opaque; + QLIST_ENTRY(MonFdsetFd) next; +}; + +/* file descriptor set containing fds passed via SCM_RIGHTS */ +typedef struct MonFdset MonFdset; +struct MonFdset { + int64_t id; + QLIST_HEAD(, MonFdsetFd) fds; + QLIST_HEAD(, MonFdsetFd) dup_fds; + QLIST_ENTRY(MonFdset) next; +}; + +/* Protects mon_fdsets */ +static QemuMutex mon_fdsets_lock; +static QLIST_HEAD(, MonFdset) mon_fdsets; + +static bool monitor_add_fd(Monitor *mon, int fd, const char *fdname, Error **errp) +{ + mon_fd_t *monfd; + + if (qemu_isdigit(fdname[0])) { + close(fd); + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdname", + "a name not starting with a digit"); + return false; + } + + /* See close() call below. */ + qemu_mutex_lock(&mon->mon_lock); + QLIST_FOREACH(monfd, &mon->fds, next) { + int tmp_fd; + + if (strcmp(monfd->name, fdname) != 0) { + continue; + } + + tmp_fd = monfd->fd; + monfd->fd = fd; + qemu_mutex_unlock(&mon->mon_lock); + /* Make sure close() is outside critical section */ + close(tmp_fd); + return true; + } + + monfd = g_new0(mon_fd_t, 1); + monfd->name = g_strdup(fdname); + monfd->fd = fd; + + QLIST_INSERT_HEAD(&mon->fds, monfd, next); + qemu_mutex_unlock(&mon->mon_lock); + return true; +} + +#ifdef CONFIG_POSIX +void qmp_getfd(const char *fdname, Error **errp) +{ + Monitor *cur_mon = monitor_cur(); + int fd; + + fd = qemu_chr_fe_get_msgfd(&cur_mon->chr); + if (fd == -1) { + error_setg(errp, "No file descriptor supplied via SCM_RIGHTS"); + return; + } + + monitor_add_fd(cur_mon, fd, fdname, errp); +} +#endif + +void qmp_closefd(const char *fdname, Error **errp) +{ + Monitor *cur_mon = monitor_cur(); + mon_fd_t *monfd; + int tmp_fd; + + qemu_mutex_lock(&cur_mon->mon_lock); + QLIST_FOREACH(monfd, &cur_mon->fds, next) { + if (strcmp(monfd->name, fdname) != 0) { + continue; + } + + QLIST_REMOVE(monfd, next); + tmp_fd = monfd->fd; + g_free(monfd->name); + g_free(monfd); + qemu_mutex_unlock(&cur_mon->mon_lock); + /* Make sure close() is outside critical section */ + close(tmp_fd); + return; + } + + qemu_mutex_unlock(&cur_mon->mon_lock); + error_setg(errp, "File descriptor named '%s' not found", fdname); +} + +int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) +{ + mon_fd_t *monfd; + + QEMU_LOCK_GUARD(&mon->mon_lock); + QLIST_FOREACH(monfd, &mon->fds, next) { + int fd; + + if (strcmp(monfd->name, fdname) != 0) { + continue; + } + + fd = monfd->fd; + assert(fd >= 0); + + /* caller takes ownership of fd */ + QLIST_REMOVE(monfd, next); + g_free(monfd->name); + g_free(monfd); + + return fd; + } + + error_setg(errp, "File descriptor named '%s' has not been found", fdname); + return -1; +} + +static void monitor_fdset_cleanup(MonFdset *mon_fdset) +{ + MonFdsetFd *mon_fdset_fd; + MonFdsetFd *mon_fdset_fd_next; + + QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, mon_fdset_fd_next) { + if ((mon_fdset_fd->removed || + (QLIST_EMPTY(&mon_fdset->dup_fds) && mon_refcount == 0)) && + runstate_is_running()) { + close(mon_fdset_fd->fd); + g_free(mon_fdset_fd->opaque); + QLIST_REMOVE(mon_fdset_fd, next); + g_free(mon_fdset_fd); + } + } + + if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) { + QLIST_REMOVE(mon_fdset, next); + g_free(mon_fdset); + } +} + +void monitor_fdsets_cleanup(void) +{ + MonFdset *mon_fdset; + MonFdset *mon_fdset_next; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) { + monitor_fdset_cleanup(mon_fdset); + } +} + +AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, + const char *opaque, Error **errp) +{ + int fd; + Monitor *mon = monitor_cur(); + AddfdInfo *fdinfo; + + fd = qemu_chr_fe_get_msgfd(&mon->chr); + if (fd == -1) { + error_setg(errp, "No file descriptor supplied via SCM_RIGHTS"); + goto error; + } + + fdinfo = monitor_fdset_add_fd(fd, has_fdset_id, fdset_id, opaque, errp); + if (fdinfo) { + return fdinfo; + } + +error: + if (fd != -1) { + close(fd); + } + return NULL; +} + +#ifdef WIN32 +void qmp_get_win32_socket(const char *infos, const char *fdname, Error **errp) +{ + g_autofree WSAPROTOCOL_INFOW *info = NULL; + gsize len; + SOCKET sk; + int fd; + + info = (void *)g_base64_decode(infos, &len); + if (len != sizeof(*info)) { + error_setg(errp, "Invalid WSAPROTOCOL_INFOW value"); + return; + } + + sk = WSASocketW(FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + info, 0, 0); + if (sk == INVALID_SOCKET) { + error_setg_win32(errp, WSAGetLastError(), "Couldn't import socket"); + return; + } + + fd = _open_osfhandle(sk, _O_BINARY); + if (fd < 0) { + error_setg_errno(errp, errno, "Failed to associate a FD with the SOCKET"); + closesocket(sk); + return; + } + + monitor_add_fd(monitor_cur(), fd, fdname, errp); +} +#endif + + +void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp) +{ + MonFdset *mon_fdset; + MonFdsetFd *mon_fdset_fd; + char fd_str[60]; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + if (mon_fdset->id != fdset_id) { + continue; + } + QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { + if (has_fd) { + if (mon_fdset_fd->fd != fd) { + continue; + } + mon_fdset_fd->removed = true; + break; + } else { + mon_fdset_fd->removed = true; + } + } + if (has_fd && !mon_fdset_fd) { + goto error; + } + monitor_fdset_cleanup(mon_fdset); + return; + } + +error: + if (has_fd) { + snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64 ", fd:%" PRId64, + fdset_id, fd); + } else { + snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64, fdset_id); + } + error_setg(errp, "File descriptor named '%s' not found", fd_str); +} + +FdsetInfoList *qmp_query_fdsets(Error **errp) +{ + MonFdset *mon_fdset; + MonFdsetFd *mon_fdset_fd; + FdsetInfoList *fdset_list = NULL; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + FdsetInfo *fdset_info = g_malloc0(sizeof(*fdset_info)); + + fdset_info->fdset_id = mon_fdset->id; + + QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { + FdsetFdInfo *fdsetfd_info; + + fdsetfd_info = g_malloc0(sizeof(*fdsetfd_info)); + fdsetfd_info->fd = mon_fdset_fd->fd; + fdsetfd_info->opaque = g_strdup(mon_fdset_fd->opaque); + + QAPI_LIST_PREPEND(fdset_info->fds, fdsetfd_info); + } + + QAPI_LIST_PREPEND(fdset_list, fdset_info); + } + + return fdset_list; +} + +AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, + const char *opaque, Error **errp) +{ + MonFdset *mon_fdset = NULL; + MonFdsetFd *mon_fdset_fd; + AddfdInfo *fdinfo; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + if (has_fdset_id) { + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + /* Break if match found or match impossible due to ordering by ID */ + if (fdset_id <= mon_fdset->id) { + if (fdset_id < mon_fdset->id) { + mon_fdset = NULL; + } + break; + } + } + } + + if (mon_fdset == NULL) { + int64_t fdset_id_prev = -1; + MonFdset *mon_fdset_cur = QLIST_FIRST(&mon_fdsets); + + if (has_fdset_id) { + if (fdset_id < 0) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id", + "a non-negative value"); + return NULL; + } + /* Use specified fdset ID */ + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + mon_fdset_cur = mon_fdset; + if (fdset_id < mon_fdset_cur->id) { + break; + } + } + } else { + /* Use first available fdset ID */ + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + mon_fdset_cur = mon_fdset; + if (fdset_id_prev == mon_fdset_cur->id - 1) { + fdset_id_prev = mon_fdset_cur->id; + continue; + } + break; + } + } + + mon_fdset = g_malloc0(sizeof(*mon_fdset)); + if (has_fdset_id) { + mon_fdset->id = fdset_id; + } else { + mon_fdset->id = fdset_id_prev + 1; + } + + /* The fdset list is ordered by fdset ID */ + if (!mon_fdset_cur) { + QLIST_INSERT_HEAD(&mon_fdsets, mon_fdset, next); + } else if (mon_fdset->id < mon_fdset_cur->id) { + QLIST_INSERT_BEFORE(mon_fdset_cur, mon_fdset, next); + } else { + QLIST_INSERT_AFTER(mon_fdset_cur, mon_fdset, next); + } + } + + mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd)); + mon_fdset_fd->fd = fd; + mon_fdset_fd->removed = false; + mon_fdset_fd->opaque = g_strdup(opaque); + QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next); + + fdinfo = g_malloc0(sizeof(*fdinfo)); + fdinfo->fdset_id = mon_fdset->id; + fdinfo->fd = mon_fdset_fd->fd; + + return fdinfo; +} + +int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) +{ +#ifdef _WIN32 + return -ENOENT; +#else + MonFdset *mon_fdset; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + MonFdsetFd *mon_fdset_fd; + MonFdsetFd *mon_fdset_fd_dup; + int fd = -1; + int dup_fd; + int mon_fd_flags; + + if (mon_fdset->id != fdset_id) { + continue; + } + + QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { + mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL); + if (mon_fd_flags == -1) { + return -1; + } + + if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) { + fd = mon_fdset_fd->fd; + break; + } + } + + if (fd == -1) { + errno = EACCES; + return -1; + } + + dup_fd = qemu_dup_flags(fd, flags); + if (dup_fd == -1) { + return -1; + } + + mon_fdset_fd_dup = g_malloc0(sizeof(*mon_fdset_fd_dup)); + mon_fdset_fd_dup->fd = dup_fd; + QLIST_INSERT_HEAD(&mon_fdset->dup_fds, mon_fdset_fd_dup, next); + return dup_fd; + } + + errno = ENOENT; + return -1; +#endif +} + +static int64_t monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove) +{ + MonFdset *mon_fdset; + MonFdsetFd *mon_fdset_fd_dup; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) { + if (mon_fdset_fd_dup->fd == dup_fd) { + if (remove) { + QLIST_REMOVE(mon_fdset_fd_dup, next); + g_free(mon_fdset_fd_dup); + if (QLIST_EMPTY(&mon_fdset->dup_fds)) { + monitor_fdset_cleanup(mon_fdset); + } + return -1; + } else { + return mon_fdset->id; + } + } + } + } + + return -1; +} + +int64_t monitor_fdset_dup_fd_find(int dup_fd) +{ + return monitor_fdset_dup_fd_find_remove(dup_fd, false); +} + +void monitor_fdset_dup_fd_remove(int dup_fd) +{ + monitor_fdset_dup_fd_find_remove(dup_fd, true); +} + +int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp) +{ + int fd; + + if (!qemu_isdigit(fdname[0]) && mon) { + fd = monitor_get_fd(mon, fdname, errp); + } else { + fd = qemu_parse_fd(fdname); + if (fd < 0) { + error_setg(errp, "Invalid file descriptor number '%s'", + fdname); + } + } + + return fd; +} + +static void __attribute__((__constructor__)) monitor_fds_init(void) +{ + qemu_mutex_init(&mon_fdsets_lock); +} diff --git a/monitor/hmp-cmds-target.c b/monitor/hmp-cmds-target.c new file mode 100644 index 0000000000..ff01cf9d8d --- /dev/null +++ b/monitor/hmp-cmds-target.c @@ -0,0 +1,381 @@ +/* + * Miscellaneous target-dependent HMP commands + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "disas/disas.h" +#include "exec/address-spaces.h" +#include "exec/memory.h" +#include "monitor/hmp-target.h" +#include "monitor/monitor-internal.h" +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "sysemu/hw_accel.h" + +/* Set the current CPU defined by the user. Callers must hold BQL. */ +int monitor_set_cpu(Monitor *mon, int cpu_index) +{ + CPUState *cpu; + + cpu = qemu_get_cpu(cpu_index); + if (cpu == NULL) { + return -1; + } + g_free(mon->mon_cpu_path); + mon->mon_cpu_path = object_get_canonical_path(OBJECT(cpu)); + return 0; +} + +/* Callers must hold BQL. */ +static CPUState *mon_get_cpu_sync(Monitor *mon, bool synchronize) +{ + CPUState *cpu = NULL; + + if (mon->mon_cpu_path) { + cpu = (CPUState *) object_resolve_path_type(mon->mon_cpu_path, + TYPE_CPU, NULL); + if (!cpu) { + g_free(mon->mon_cpu_path); + mon->mon_cpu_path = NULL; + } + } + if (!mon->mon_cpu_path) { + if (!first_cpu) { + return NULL; + } + monitor_set_cpu(mon, first_cpu->cpu_index); + cpu = first_cpu; + } + assert(cpu != NULL); + if (synchronize) { + cpu_synchronize_state(cpu); + } + return cpu; +} + +CPUState *mon_get_cpu(Monitor *mon) +{ + return mon_get_cpu_sync(mon, true); +} + +CPUArchState *mon_get_cpu_env(Monitor *mon) +{ + CPUState *cs = mon_get_cpu(mon); + + return cs ? cpu_env(cs) : NULL; +} + +int monitor_get_cpu_index(Monitor *mon) +{ + CPUState *cs = mon_get_cpu_sync(mon, false); + + return cs ? cs->cpu_index : UNASSIGNED_CPU_INDEX; +} + +void hmp_info_registers(Monitor *mon, const QDict *qdict) +{ + bool all_cpus = qdict_get_try_bool(qdict, "cpustate_all", false); + int vcpu = qdict_get_try_int(qdict, "vcpu", -1); + CPUState *cs; + + if (all_cpus) { + CPU_FOREACH(cs) { + monitor_printf(mon, "\nCPU#%d\n", cs->cpu_index); + cpu_dump_state(cs, NULL, CPU_DUMP_FPU); + } + } else { + cs = vcpu >= 0 ? qemu_get_cpu(vcpu) : mon_get_cpu(mon); + + if (!cs) { + if (vcpu >= 0) { + monitor_printf(mon, "CPU#%d not available\n", vcpu); + } else { + monitor_printf(mon, "No CPU available\n"); + } + return; + } + + monitor_printf(mon, "\nCPU#%d\n", cs->cpu_index); + cpu_dump_state(cs, NULL, CPU_DUMP_FPU); + } +} + +static void memory_dump(Monitor *mon, int count, int format, int wsize, + hwaddr addr, int is_physical) +{ + int l, line_size, i, max_digits, len; + uint8_t buf[16]; + uint64_t v; + CPUState *cs = mon_get_cpu(mon); + + if (!cs && (format == 'i' || !is_physical)) { + monitor_printf(mon, "Can not dump without CPU\n"); + return; + } + + if (format == 'i') { + monitor_disas(mon, cs, addr, count, is_physical); + return; + } + + len = wsize * count; + if (wsize == 1) { + line_size = 8; + } else { + line_size = 16; + } + max_digits = 0; + + switch(format) { + case 'o': + max_digits = DIV_ROUND_UP(wsize * 8, 3); + break; + default: + case 'x': + max_digits = (wsize * 8) / 4; + break; + case 'u': + case 'd': + max_digits = DIV_ROUND_UP(wsize * 8 * 10, 33); + break; + case 'c': + wsize = 1; + break; + } + + while (len > 0) { + if (is_physical) { + monitor_printf(mon, HWADDR_FMT_plx ":", addr); + } else { + monitor_printf(mon, TARGET_FMT_lx ":", (target_ulong)addr); + } + l = len; + if (l > line_size) + l = line_size; + if (is_physical) { + AddressSpace *as = cs ? cs->as : &address_space_memory; + MemTxResult r = address_space_read(as, addr, + MEMTXATTRS_UNSPECIFIED, buf, l); + if (r != MEMTX_OK) { + monitor_printf(mon, " Cannot access memory\n"); + break; + } + } else { + if (cpu_memory_rw_debug(cs, addr, buf, l, 0) < 0) { + monitor_printf(mon, " Cannot access memory\n"); + break; + } + } + i = 0; + while (i < l) { + switch(wsize) { + default: + case 1: + v = ldub_p(buf + i); + break; + case 2: + v = lduw_p(buf + i); + break; + case 4: + v = (uint32_t)ldl_p(buf + i); + break; + case 8: + v = ldq_p(buf + i); + break; + } + monitor_printf(mon, " "); + switch(format) { + case 'o': + monitor_printf(mon, "%#*" PRIo64, max_digits, v); + break; + case 'x': + monitor_printf(mon, "0x%0*" PRIx64, max_digits, v); + break; + case 'u': + monitor_printf(mon, "%*" PRIu64, max_digits, v); + break; + case 'd': + monitor_printf(mon, "%*" PRId64, max_digits, v); + break; + case 'c': + monitor_printc(mon, v); + break; + } + i += wsize; + } + monitor_printf(mon, "\n"); + addr += l; + len -= l; + } +} + +void hmp_memory_dump(Monitor *mon, const QDict *qdict) +{ + int count = qdict_get_int(qdict, "count"); + int format = qdict_get_int(qdict, "format"); + int size = qdict_get_int(qdict, "size"); + target_long addr = qdict_get_int(qdict, "addr"); + + memory_dump(mon, count, format, size, addr, 0); +} + +void hmp_physical_memory_dump(Monitor *mon, const QDict *qdict) +{ + int count = qdict_get_int(qdict, "count"); + int format = qdict_get_int(qdict, "format"); + int size = qdict_get_int(qdict, "size"); + hwaddr addr = qdict_get_int(qdict, "addr"); + + memory_dump(mon, count, format, size, addr, 1); +} + +void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, uint64_t size, Error **errp) +{ + Int128 gpa_region_size; + MemoryRegionSection mrs = memory_region_find(get_system_memory(), + addr, size); + + if (!mrs.mr) { + error_setg(errp, "No memory is mapped at address 0x%" HWADDR_PRIx, addr); + return NULL; + } + + if (!memory_region_is_ram(mrs.mr) && !memory_region_is_romd(mrs.mr)) { + error_setg(errp, "Memory at address 0x%" HWADDR_PRIx " is not RAM", addr); + memory_region_unref(mrs.mr); + return NULL; + } + + gpa_region_size = int128_make64(size); + if (int128_lt(mrs.size, gpa_region_size)) { + error_setg(errp, "Size of memory region at 0x%" HWADDR_PRIx + " exceeded.", addr); + memory_region_unref(mrs.mr); + return NULL; + } + + *p_mr = mrs.mr; + return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region); +} + +void hmp_gpa2hva(Monitor *mon, const QDict *qdict) +{ + hwaddr addr = qdict_get_int(qdict, "addr"); + Error *local_err = NULL; + MemoryRegion *mr = NULL; + void *ptr; + + ptr = gpa2hva(&mr, addr, 1, &local_err); + if (local_err) { + error_report_err(local_err); + return; + } + + monitor_printf(mon, "Host virtual address for 0x%" HWADDR_PRIx + " (%s) is %p\n", + addr, mr->name, ptr); + + memory_region_unref(mr); +} + +void hmp_gva2gpa(Monitor *mon, const QDict *qdict) +{ + target_ulong addr = qdict_get_int(qdict, "addr"); + MemTxAttrs attrs; + CPUState *cs = mon_get_cpu(mon); + hwaddr gpa; + + if (!cs) { + monitor_printf(mon, "No cpu\n"); + return; + } + + gpa = cpu_get_phys_page_attrs_debug(cs, addr & TARGET_PAGE_MASK, &attrs); + if (gpa == -1) { + monitor_printf(mon, "Unmapped\n"); + } else { + monitor_printf(mon, "gpa: %#" HWADDR_PRIx "\n", + gpa + (addr & ~TARGET_PAGE_MASK)); + } +} + +#ifdef CONFIG_LINUX +static uint64_t vtop(void *ptr, Error **errp) +{ + uint64_t pinfo; + uint64_t ret = -1; + uintptr_t addr = (uintptr_t) ptr; + uintptr_t pagesize = qemu_real_host_page_size(); + off_t offset = addr / pagesize * sizeof(pinfo); + int fd; + + fd = open("/proc/self/pagemap", O_RDONLY); + if (fd == -1) { + error_setg_errno(errp, errno, "Cannot open /proc/self/pagemap"); + return -1; + } + + /* Force copy-on-write if necessary. */ + qatomic_add((uint8_t *)ptr, 0); + + if (pread(fd, &pinfo, sizeof(pinfo), offset) != sizeof(pinfo)) { + error_setg_errno(errp, errno, "Cannot read pagemap"); + goto out; + } + if ((pinfo & (1ull << 63)) == 0) { + error_setg(errp, "Page not present"); + goto out; + } + ret = ((pinfo & 0x007fffffffffffffull) * pagesize) | (addr & (pagesize - 1)); + +out: + close(fd); + return ret; +} + +void hmp_gpa2hpa(Monitor *mon, const QDict *qdict) +{ + hwaddr addr = qdict_get_int(qdict, "addr"); + Error *local_err = NULL; + MemoryRegion *mr = NULL; + void *ptr; + uint64_t physaddr; + + ptr = gpa2hva(&mr, addr, 1, &local_err); + if (local_err) { + error_report_err(local_err); + return; + } + + physaddr = vtop(ptr, &local_err); + if (local_err) { + error_report_err(local_err); + } else { + monitor_printf(mon, "Host physical address for 0x%" HWADDR_PRIx + " (%s) is 0x%" PRIx64 "\n", + addr, mr->name, (uint64_t) physaddr); + } + + memory_region_unref(mr); +} +#endif diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 622c783c32..871898ac46 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -14,51 +14,20 @@ */ #include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "exec/gdbstub.h" +#include "exec/ioport.h" #include "monitor/hmp.h" -#include "net/net.h" -#include "net/eth.h" -#include "chardev/char.h" -#include "sysemu/block-backend.h" -#include "sysemu/runstate.h" -#include "qemu/config-file.h" -#include "qemu/option.h" -#include "qemu/timer.h" -#include "qemu/sockets.h" #include "qemu/help_option.h" #include "monitor/monitor-internal.h" #include "qapi/error.h" -#include "qapi/clone-visitor.h" -#include "qapi/opts-visitor.h" -#include "qapi/qapi-builtin-visit.h" -#include "qapi/qapi-commands-block.h" -#include "qapi/qapi-commands-char.h" #include "qapi/qapi-commands-control.h" -#include "qapi/qapi-commands-machine.h" -#include "qapi/qapi-commands-migration.h" #include "qapi/qapi-commands-misc.h" -#include "qapi/qapi-commands-net.h" -#include "qapi/qapi-commands-pci.h" -#include "qapi/qapi-commands-rocker.h" -#include "qapi/qapi-commands-run-state.h" -#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 "qemu/cutils.h" -#include "qemu/error-report.h" #include "hw/intc/intc.h" -#include "migration/snapshot.h" -#include "migration/misc.h" - -#ifdef CONFIG_SPICE -#include -#endif +#include "qemu/log.h" +#include "sysemu/sysemu.h" bool hmp_handle_error(Monitor *mon, Error *err) { @@ -70,28 +39,21 @@ bool hmp_handle_error(Monitor *mon, Error *err) } /* - * Produce a strList from a comma separated list. - * A NULL or empty input string return NULL. + * Split @str at comma. + * A null @str defaults to "". */ -static strList *strList_from_comma_list(const char *in) +strList *hmp_split_at_comma(const char *str) { + char **split = g_strsplit(str ?: "", ",", -1); strList *res = NULL; strList **tail = &res; + int i; - while (in && in[0]) { - char *comma = strchr(in, ','); - char *value; - - if (comma) { - value = g_strndup(in, comma - in); - in = comma + 1; /* skip the , */ - } else { - value = g_strdup(in); - in = NULL; - } - QAPI_LIST_APPEND(tail, value); + for (i = 0; split[i]; i++) { + QAPI_LIST_APPEND(tail, split[i]); } + g_free(split); return res; } @@ -100,7 +62,7 @@ void hmp_info_name(Monitor *mon, const QDict *qdict) NameInfo *info; info = qmp_query_name(NULL); - if (info->has_name) { + if (info->name) { monitor_printf(mon, "%s\n", info->name); } qapi_free_NameInfo(info); @@ -119,683 +81,6 @@ void hmp_info_version(Monitor *mon, const QDict *qdict) qapi_free_VersionInfo(info); } -void hmp_info_kvm(Monitor *mon, const QDict *qdict) -{ - KvmInfo *info; - - info = qmp_query_kvm(NULL); - monitor_printf(mon, "kvm support: "); - if (info->present) { - monitor_printf(mon, "%s\n", info->enabled ? "enabled" : "disabled"); - } else { - monitor_printf(mon, "not compiled\n"); - } - - qapi_free_KvmInfo(info); -} - -void hmp_info_status(Monitor *mon, const QDict *qdict) -{ - StatusInfo *info; - - info = qmp_query_status(NULL); - - monitor_printf(mon, "VM status: %s%s", - info->running ? "running" : "paused", - info->singlestep ? " (single step mode)" : ""); - - if (!info->running && info->status != RUN_STATE_PAUSED) { - monitor_printf(mon, " (%s)", RunState_str(info->status)); - } - - monitor_printf(mon, "\n"); - - qapi_free_StatusInfo(info); -} - -void hmp_info_uuid(Monitor *mon, const QDict *qdict) -{ - UuidInfo *info; - - info = qmp_query_uuid(NULL); - monitor_printf(mon, "%s\n", info->UUID); - qapi_free_UuidInfo(info); -} - -void hmp_info_chardev(Monitor *mon, const QDict *qdict) -{ - ChardevInfoList *char_info, *info; - - char_info = qmp_query_chardev(NULL); - for (info = char_info; info; info = info->next) { - monitor_printf(mon, "%s: filename=%s\n", info->value->label, - info->value->filename); - } - - qapi_free_ChardevInfoList(char_info); -} - -void hmp_info_mice(Monitor *mon, const QDict *qdict) -{ - MouseInfoList *mice_list, *mouse; - - mice_list = qmp_query_mice(NULL); - if (!mice_list) { - monitor_printf(mon, "No mouse devices connected\n"); - return; - } - - for (mouse = mice_list; mouse; mouse = mouse->next) { - monitor_printf(mon, "%c Mouse #%" PRId64 ": %s%s\n", - mouse->value->current ? '*' : ' ', - mouse->value->index, mouse->value->name, - mouse->value->absolute ? " (absolute)" : ""); - } - - qapi_free_MouseInfoList(mice_list); -} - -static char *SocketAddress_to_str(SocketAddress *addr) -{ - switch (addr->type) { - case SOCKET_ADDRESS_TYPE_INET: - return g_strdup_printf("tcp:%s:%s", - addr->u.inet.host, - addr->u.inet.port); - case SOCKET_ADDRESS_TYPE_UNIX: - return g_strdup_printf("unix:%s", - addr->u.q_unix.path); - case SOCKET_ADDRESS_TYPE_FD: - return g_strdup_printf("fd:%s", addr->u.fd.str); - case SOCKET_ADDRESS_TYPE_VSOCK: - return g_strdup_printf("tcp:%s:%s", - addr->u.vsock.cid, - addr->u.vsock.port); - default: - return g_strdup("unknown address type"); - } -} - -void hmp_info_migrate(Monitor *mon, const QDict *qdict) -{ - MigrationInfo *info; - - info = qmp_query_migrate(NULL); - - migration_global_dump(mon); - - if (info->blocked_reasons) { - strList *reasons = info->blocked_reasons; - monitor_printf(mon, "Outgoing migration blocked:\n"); - while (reasons) { - monitor_printf(mon, " %s\n", reasons->value); - reasons = reasons->next; - } - } - - if (info->has_status) { - monitor_printf(mon, "Migration status: %s", - MigrationStatus_str(info->status)); - if (info->status == MIGRATION_STATUS_FAILED && - info->has_error_desc) { - monitor_printf(mon, " (%s)\n", info->error_desc); - } else { - monitor_printf(mon, "\n"); - } - - monitor_printf(mon, "total time: %" PRIu64 " ms\n", - info->total_time); - if (info->has_expected_downtime) { - monitor_printf(mon, "expected downtime: %" PRIu64 " ms\n", - info->expected_downtime); - } - if (info->has_downtime) { - monitor_printf(mon, "downtime: %" PRIu64 " ms\n", - info->downtime); - } - if (info->has_setup_time) { - monitor_printf(mon, "setup: %" PRIu64 " ms\n", - info->setup_time); - } - } - - if (info->has_ram) { - monitor_printf(mon, "transferred ram: %" PRIu64 " kbytes\n", - info->ram->transferred >> 10); - monitor_printf(mon, "throughput: %0.2f mbps\n", - info->ram->mbps); - monitor_printf(mon, "remaining ram: %" PRIu64 " kbytes\n", - info->ram->remaining >> 10); - monitor_printf(mon, "total ram: %" PRIu64 " kbytes\n", - info->ram->total >> 10); - monitor_printf(mon, "duplicate: %" PRIu64 " pages\n", - info->ram->duplicate); - monitor_printf(mon, "skipped: %" PRIu64 " pages\n", - info->ram->skipped); - monitor_printf(mon, "normal: %" PRIu64 " pages\n", - info->ram->normal); - monitor_printf(mon, "normal bytes: %" PRIu64 " kbytes\n", - info->ram->normal_bytes >> 10); - monitor_printf(mon, "dirty sync count: %" PRIu64 "\n", - info->ram->dirty_sync_count); - monitor_printf(mon, "page size: %" PRIu64 " kbytes\n", - info->ram->page_size >> 10); - monitor_printf(mon, "multifd bytes: %" PRIu64 " kbytes\n", - info->ram->multifd_bytes >> 10); - monitor_printf(mon, "pages-per-second: %" PRIu64 "\n", - info->ram->pages_per_second); - - if (info->ram->dirty_pages_rate) { - monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n", - info->ram->dirty_pages_rate); - } - if (info->ram->postcopy_requests) { - monitor_printf(mon, "postcopy request count: %" PRIu64 "\n", - info->ram->postcopy_requests); - } - if (info->ram->precopy_bytes) { - monitor_printf(mon, "precopy ram: %" PRIu64 " kbytes\n", - info->ram->precopy_bytes >> 10); - } - if (info->ram->downtime_bytes) { - monitor_printf(mon, "downtime ram: %" PRIu64 " kbytes\n", - info->ram->downtime_bytes >> 10); - } - if (info->ram->postcopy_bytes) { - monitor_printf(mon, "postcopy ram: %" PRIu64 " kbytes\n", - info->ram->postcopy_bytes >> 10); - } - } - - if (info->has_disk) { - monitor_printf(mon, "transferred disk: %" PRIu64 " kbytes\n", - info->disk->transferred >> 10); - monitor_printf(mon, "remaining disk: %" PRIu64 " kbytes\n", - info->disk->remaining >> 10); - monitor_printf(mon, "total disk: %" PRIu64 " kbytes\n", - info->disk->total >> 10); - } - - if (info->has_xbzrle_cache) { - monitor_printf(mon, "cache size: %" PRIu64 " bytes\n", - info->xbzrle_cache->cache_size); - monitor_printf(mon, "xbzrle transferred: %" PRIu64 " kbytes\n", - info->xbzrle_cache->bytes >> 10); - monitor_printf(mon, "xbzrle pages: %" PRIu64 " pages\n", - info->xbzrle_cache->pages); - monitor_printf(mon, "xbzrle cache miss: %" PRIu64 " pages\n", - info->xbzrle_cache->cache_miss); - monitor_printf(mon, "xbzrle cache miss rate: %0.2f\n", - info->xbzrle_cache->cache_miss_rate); - monitor_printf(mon, "xbzrle encoding rate: %0.2f\n", - info->xbzrle_cache->encoding_rate); - monitor_printf(mon, "xbzrle overflow: %" PRIu64 "\n", - info->xbzrle_cache->overflow); - } - - if (info->has_compression) { - monitor_printf(mon, "compression pages: %" PRIu64 " pages\n", - info->compression->pages); - monitor_printf(mon, "compression busy: %" PRIu64 "\n", - info->compression->busy); - monitor_printf(mon, "compression busy rate: %0.2f\n", - info->compression->busy_rate); - monitor_printf(mon, "compressed size: %" PRIu64 " kbytes\n", - info->compression->compressed_size >> 10); - monitor_printf(mon, "compression rate: %0.2f\n", - info->compression->compression_rate); - } - - if (info->has_cpu_throttle_percentage) { - monitor_printf(mon, "cpu throttle percentage: %" PRIu64 "\n", - info->cpu_throttle_percentage); - } - - if (info->has_postcopy_blocktime) { - monitor_printf(mon, "postcopy blocktime: %u\n", - info->postcopy_blocktime); - } - - if (info->has_postcopy_vcpu_blocktime) { - Visitor *v; - char *str; - v = string_output_visitor_new(false, &str); - visit_type_uint32List(v, NULL, &info->postcopy_vcpu_blocktime, - &error_abort); - visit_complete(v, &str); - monitor_printf(mon, "postcopy vcpu blocktime: %s\n", str); - g_free(str); - visit_free(v); - } - if (info->has_socket_address) { - SocketAddressList *addr; - - monitor_printf(mon, "socket address: [\n"); - - for (addr = info->socket_address; addr; addr = addr->next) { - char *s = SocketAddress_to_str(addr->value); - monitor_printf(mon, "\t%s\n", s); - g_free(s); - } - monitor_printf(mon, "]\n"); - } - - if (info->has_vfio) { - monitor_printf(mon, "vfio device transferred: %" PRIu64 " kbytes\n", - info->vfio->transferred >> 10); - } - - qapi_free_MigrationInfo(info); -} - -void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict) -{ - MigrationCapabilityStatusList *caps, *cap; - - caps = qmp_query_migrate_capabilities(NULL); - - if (caps) { - for (cap = caps; cap; cap = cap->next) { - monitor_printf(mon, "%s: %s\n", - MigrationCapability_str(cap->value->capability), - cap->value->state ? "on" : "off"); - } - } - - qapi_free_MigrationCapabilityStatusList(caps); -} - -void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) -{ - MigrationParameters *params; - - params = qmp_query_migrate_parameters(NULL); - - if (params) { - monitor_printf(mon, "%s: %" PRIu64 " ms\n", - MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_INITIAL), - params->announce_initial); - monitor_printf(mon, "%s: %" PRIu64 " ms\n", - MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_MAX), - params->announce_max); - monitor_printf(mon, "%s: %" PRIu64 "\n", - MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_ROUNDS), - params->announce_rounds); - monitor_printf(mon, "%s: %" PRIu64 " ms\n", - MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_STEP), - params->announce_step); - assert(params->has_compress_level); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_LEVEL), - params->compress_level); - assert(params->has_compress_threads); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_THREADS), - params->compress_threads); - assert(params->has_compress_wait_thread); - monitor_printf(mon, "%s: %s\n", - MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD), - params->compress_wait_thread ? "on" : "off"); - assert(params->has_decompress_threads); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_DECOMPRESS_THREADS), - params->decompress_threads); - assert(params->has_throttle_trigger_threshold); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD), - params->throttle_trigger_threshold); - assert(params->has_cpu_throttle_initial); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL), - params->cpu_throttle_initial); - assert(params->has_cpu_throttle_increment); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT), - params->cpu_throttle_increment); - assert(params->has_cpu_throttle_tailslow); - monitor_printf(mon, "%s: %s\n", - MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW), - params->cpu_throttle_tailslow ? "on" : "off"); - assert(params->has_max_cpu_throttle); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_MAX_CPU_THROTTLE), - params->max_cpu_throttle); - assert(params->has_tls_creds); - monitor_printf(mon, "%s: '%s'\n", - MigrationParameter_str(MIGRATION_PARAMETER_TLS_CREDS), - params->tls_creds); - assert(params->has_tls_hostname); - monitor_printf(mon, "%s: '%s'\n", - MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME), - params->tls_hostname); - assert(params->has_max_bandwidth); - monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n", - MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH), - params->max_bandwidth); - assert(params->has_downtime_limit); - monitor_printf(mon, "%s: %" PRIu64 " ms\n", - MigrationParameter_str(MIGRATION_PARAMETER_DOWNTIME_LIMIT), - params->downtime_limit); - assert(params->has_x_checkpoint_delay); - monitor_printf(mon, "%s: %u ms\n", - MigrationParameter_str(MIGRATION_PARAMETER_X_CHECKPOINT_DELAY), - params->x_checkpoint_delay); - assert(params->has_block_incremental); - monitor_printf(mon, "%s: %s\n", - MigrationParameter_str(MIGRATION_PARAMETER_BLOCK_INCREMENTAL), - params->block_incremental ? "on" : "off"); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_CHANNELS), - params->multifd_channels); - monitor_printf(mon, "%s: %s\n", - MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_COMPRESSION), - MultiFDCompression_str(params->multifd_compression)); - monitor_printf(mon, "%s: %" PRIu64 " bytes\n", - MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE), - params->xbzrle_cache_size); - monitor_printf(mon, "%s: %" PRIu64 "\n", - MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH), - params->max_postcopy_bandwidth); - monitor_printf(mon, "%s: '%s'\n", - MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ), - params->tls_authz); - - if (params->has_block_bitmap_mapping) { - const BitmapMigrationNodeAliasList *bmnal; - - monitor_printf(mon, "%s:\n", - MigrationParameter_str( - MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING)); - - for (bmnal = params->block_bitmap_mapping; - bmnal; - bmnal = bmnal->next) - { - const BitmapMigrationNodeAlias *bmna = bmnal->value; - const BitmapMigrationBitmapAliasList *bmbal; - - monitor_printf(mon, " '%s' -> '%s'\n", - bmna->node_name, bmna->alias); - - for (bmbal = bmna->bitmaps; bmbal; bmbal = bmbal->next) { - const BitmapMigrationBitmapAlias *bmba = bmbal->value; - - monitor_printf(mon, " '%s' -> '%s'\n", - bmba->name, bmba->alias); - } - } - } - } - - qapi_free_MigrationParameters(params); -} - - -#ifdef CONFIG_VNC -/* Helper for hmp_info_vnc_clients, _servers */ -static void hmp_info_VncBasicInfo(Monitor *mon, VncBasicInfo *info, - const char *name) -{ - monitor_printf(mon, " %s: %s:%s (%s%s)\n", - name, - info->host, - info->service, - NetworkAddressFamily_str(info->family), - info->websocket ? " (Websocket)" : ""); -} - -/* Helper displaying and auth and crypt info */ -static void hmp_info_vnc_authcrypt(Monitor *mon, const char *indent, - VncPrimaryAuth auth, - VncVencryptSubAuth *vencrypt) -{ - monitor_printf(mon, "%sAuth: %s (Sub: %s)\n", indent, - VncPrimaryAuth_str(auth), - vencrypt ? VncVencryptSubAuth_str(*vencrypt) : "none"); -} - -static void hmp_info_vnc_clients(Monitor *mon, VncClientInfoList *client) -{ - while (client) { - VncClientInfo *cinfo = client->value; - - hmp_info_VncBasicInfo(mon, qapi_VncClientInfo_base(cinfo), "Client"); - monitor_printf(mon, " x509_dname: %s\n", - cinfo->has_x509_dname ? - cinfo->x509_dname : "none"); - monitor_printf(mon, " sasl_username: %s\n", - cinfo->has_sasl_username ? - cinfo->sasl_username : "none"); - - client = client->next; - } -} - -static void hmp_info_vnc_servers(Monitor *mon, VncServerInfo2List *server) -{ - while (server) { - VncServerInfo2 *sinfo = server->value; - hmp_info_VncBasicInfo(mon, qapi_VncServerInfo2_base(sinfo), "Server"); - hmp_info_vnc_authcrypt(mon, " ", sinfo->auth, - sinfo->has_vencrypt ? &sinfo->vencrypt : NULL); - server = server->next; - } -} - -void hmp_info_vnc(Monitor *mon, const QDict *qdict) -{ - VncInfo2List *info2l, *info2l_head; - Error *err = NULL; - - info2l = qmp_query_vnc_servers(&err); - info2l_head = info2l; - if (hmp_handle_error(mon, err)) { - return; - } - if (!info2l) { - monitor_printf(mon, "None\n"); - return; - } - - while (info2l) { - VncInfo2 *info = info2l->value; - monitor_printf(mon, "%s:\n", info->id); - hmp_info_vnc_servers(mon, info->server); - hmp_info_vnc_clients(mon, info->clients); - if (!info->server) { - /* The server entry displays its auth, we only - * need to display in the case of 'reverse' connections - * where there's no server. - */ - hmp_info_vnc_authcrypt(mon, " ", info->auth, - info->has_vencrypt ? &info->vencrypt : NULL); - } - if (info->has_display) { - monitor_printf(mon, " Display: %s\n", info->display); - } - info2l = info2l->next; - } - - qapi_free_VncInfo2List(info2l_head); - -} -#endif - -#ifdef CONFIG_SPICE -void hmp_info_spice(Monitor *mon, const QDict *qdict) -{ - SpiceChannelList *chan; - SpiceInfo *info; - const char *channel_name; - const char * const channel_names[] = { - [SPICE_CHANNEL_MAIN] = "main", - [SPICE_CHANNEL_DISPLAY] = "display", - [SPICE_CHANNEL_INPUTS] = "inputs", - [SPICE_CHANNEL_CURSOR] = "cursor", - [SPICE_CHANNEL_PLAYBACK] = "playback", - [SPICE_CHANNEL_RECORD] = "record", - [SPICE_CHANNEL_TUNNEL] = "tunnel", - [SPICE_CHANNEL_SMARTCARD] = "smartcard", - [SPICE_CHANNEL_USBREDIR] = "usbredir", - [SPICE_CHANNEL_PORT] = "port", -#if 0 - /* minimum spice-protocol is 0.12.3, webdav was added in 0.12.7, - * no easy way to #ifdef (SPICE_CHANNEL_* is a enum). Disable - * as quick fix for build failures with older versions. */ - [SPICE_CHANNEL_WEBDAV] = "webdav", -#endif - }; - - info = qmp_query_spice(NULL); - - if (!info->enabled) { - monitor_printf(mon, "Server: disabled\n"); - goto out; - } - - monitor_printf(mon, "Server:\n"); - if (info->has_port) { - monitor_printf(mon, " address: %s:%" PRId64 "\n", - info->host, info->port); - } - if (info->has_tls_port) { - monitor_printf(mon, " address: %s:%" PRId64 " [tls]\n", - info->host, info->tls_port); - } - monitor_printf(mon, " migrated: %s\n", - info->migrated ? "true" : "false"); - monitor_printf(mon, " auth: %s\n", info->auth); - monitor_printf(mon, " compiled: %s\n", info->compiled_version); - monitor_printf(mon, " mouse-mode: %s\n", - SpiceQueryMouseMode_str(info->mouse_mode)); - - if (!info->has_channels || info->channels == NULL) { - monitor_printf(mon, "Channels: none\n"); - } else { - for (chan = info->channels; chan; chan = chan->next) { - monitor_printf(mon, "Channel:\n"); - monitor_printf(mon, " address: %s:%s%s\n", - chan->value->host, chan->value->port, - chan->value->tls ? " [tls]" : ""); - monitor_printf(mon, " session: %" PRId64 "\n", - chan->value->connection_id); - monitor_printf(mon, " channel: %" PRId64 ":%" PRId64 "\n", - chan->value->channel_type, chan->value->channel_id); - - channel_name = "unknown"; - if (chan->value->channel_type > 0 && - chan->value->channel_type < ARRAY_SIZE(channel_names) && - channel_names[chan->value->channel_type]) { - channel_name = channel_names[chan->value->channel_type]; - } - - monitor_printf(mon, " channel name: %s\n", channel_name); - } - } - -out: - qapi_free_SpiceInfo(info); -} -#endif - -void hmp_info_balloon(Monitor *mon, const QDict *qdict) -{ - BalloonInfo *info; - Error *err = NULL; - - info = qmp_query_balloon(&err); - if (hmp_handle_error(mon, err)) { - return; - } - - monitor_printf(mon, "balloon: actual=%" PRId64 "\n", info->actual >> 20); - - qapi_free_BalloonInfo(info); -} - -static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev) -{ - PciMemoryRegionList *region; - - monitor_printf(mon, " Bus %2" PRId64 ", ", dev->bus); - monitor_printf(mon, "device %3" PRId64 ", function %" PRId64 ":\n", - dev->slot, dev->function); - monitor_printf(mon, " "); - - if (dev->class_info->has_desc) { - monitor_printf(mon, "%s", dev->class_info->desc); - } else { - monitor_printf(mon, "Class %04" PRId64, dev->class_info->q_class); - } - - monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n", - dev->id->vendor, dev->id->device); - if (dev->id->has_subsystem_vendor && dev->id->has_subsystem) { - monitor_printf(mon, " PCI subsystem %04" PRIx64 ":%04" PRIx64 "\n", - dev->id->subsystem_vendor, dev->id->subsystem); - } - - if (dev->has_irq) { - monitor_printf(mon, " IRQ %" PRId64 ", pin %c\n", - dev->irq, (char)('A' + dev->irq_pin - 1)); - } - - if (dev->has_pci_bridge) { - monitor_printf(mon, " BUS %" PRId64 ".\n", - dev->pci_bridge->bus->number); - monitor_printf(mon, " secondary bus %" PRId64 ".\n", - dev->pci_bridge->bus->secondary); - monitor_printf(mon, " subordinate bus %" PRId64 ".\n", - dev->pci_bridge->bus->subordinate); - - monitor_printf(mon, " IO range [0x%04"PRIx64", 0x%04"PRIx64"]\n", - dev->pci_bridge->bus->io_range->base, - dev->pci_bridge->bus->io_range->limit); - - monitor_printf(mon, - " memory range [0x%08"PRIx64", 0x%08"PRIx64"]\n", - dev->pci_bridge->bus->memory_range->base, - dev->pci_bridge->bus->memory_range->limit); - - monitor_printf(mon, " prefetchable memory range " - "[0x%08"PRIx64", 0x%08"PRIx64"]\n", - dev->pci_bridge->bus->prefetchable_range->base, - dev->pci_bridge->bus->prefetchable_range->limit); - } - - for (region = dev->regions; region; region = region->next) { - uint64_t addr, size; - - addr = region->value->address; - size = region->value->size; - - monitor_printf(mon, " BAR%" PRId64 ": ", region->value->bar); - - if (!strcmp(region->value->type, "io")) { - monitor_printf(mon, "I/O at 0x%04" PRIx64 - " [0x%04" PRIx64 "].\n", - addr, addr + size - 1); - } else { - monitor_printf(mon, "%d bit%s memory at 0x%08" PRIx64 - " [0x%08" PRIx64 "].\n", - region->value->mem_type_64 ? 64 : 32, - region->value->prefetch ? " prefetchable" : "", - addr, addr + size - 1); - } - } - - monitor_printf(mon, " id \"%s\"\n", dev->qdev_id); - - if (dev->has_pci_bridge) { - if (dev->pci_bridge->has_devices) { - PciDeviceInfoList *cdev; - for (cdev = dev->pci_bridge->devices; cdev; cdev = cdev->next) { - hmp_info_pci_device(mon, cdev->value); - } - } - } -} - static int hmp_info_pic_foreach(Object *obj, void *opaque) { InterruptStatsProvider *intc; @@ -822,82 +107,6 @@ void hmp_info_pic(Monitor *mon, const QDict *qdict) hmp_info_pic_foreach, mon); } -void hmp_info_pci(Monitor *mon, const QDict *qdict) -{ - PciInfoList *info_list, *info; - Error *err = NULL; - - info_list = qmp_query_pci(&err); - if (err) { - monitor_printf(mon, "PCI devices not supported\n"); - error_free(err); - return; - } - - for (info = info_list; info; info = info->next) { - PciDeviceInfoList *dev; - - for (dev = info->value->devices; dev; dev = dev->next) { - hmp_info_pci_device(mon, dev->value); - } - } - - qapi_free_PciInfoList(info_list); -} - -void hmp_info_tpm(Monitor *mon, const QDict *qdict) -{ -#ifdef CONFIG_TPM - TPMInfoList *info_list, *info; - Error *err = NULL; - unsigned int c = 0; - TPMPassthroughOptions *tpo; - TPMEmulatorOptions *teo; - - info_list = qmp_query_tpm(&err); - if (err) { - monitor_printf(mon, "TPM device not supported\n"); - error_free(err); - return; - } - - if (info_list) { - monitor_printf(mon, "TPM device:\n"); - } - - for (info = info_list; info; info = info->next) { - TPMInfo *ti = info->value; - monitor_printf(mon, " tpm%d: model=%s\n", - c, TpmModel_str(ti->model)); - - monitor_printf(mon, " \\ %s: type=%s", - ti->id, TpmType_str(ti->options->type)); - - switch (ti->options->type) { - case TPM_TYPE_PASSTHROUGH: - tpo = ti->options->u.passthrough.data; - monitor_printf(mon, "%s%s%s%s", - tpo->has_path ? ",path=" : "", - tpo->has_path ? tpo->path : "", - tpo->has_cancel_path ? ",cancel-path=" : "", - tpo->has_cancel_path ? tpo->cancel_path : ""); - break; - case TPM_TYPE_EMULATOR: - teo = ti->options->u.emulator.data; - monitor_printf(mon, ",chardev=%s", teo->chardev); - break; - case TPM_TYPE__MAX: - break; - } - monitor_printf(mon, "\n"); - c++; - } - qapi_free_TPMInfoList(info_list); -#else - monitor_printf(mon, "TPM device not supported\n"); -#endif /* CONFIG_TPM */ -} - void hmp_quit(Monitor *mon, const QDict *qdict) { monitor_suspend(mon); @@ -928,21 +137,12 @@ void hmp_sync_profile(Monitor *mon, const QDict *qdict) } else { Error *err = NULL; - error_setg(&err, QERR_INVALID_PARAMETER, op); + error_setg(&err, "invalid parameter '%s'," + " expecting 'on', 'off', or 'reset'", op); hmp_handle_error(mon, err); } } -void hmp_system_reset(Monitor *mon, const QDict *qdict) -{ - qmp_system_reset(NULL); -} - -void hmp_system_powerdown(Monitor *mon, const QDict *qdict) -{ - qmp_system_powerdown(NULL); -} - void hmp_exit_preconfig(Monitor *mon, const QDict *qdict) { Error *err = NULL; @@ -963,74 +163,6 @@ void hmp_cpu(Monitor *mon, const QDict *qdict) } } -void hmp_memsave(Monitor *mon, const QDict *qdict) -{ - uint32_t size = qdict_get_int(qdict, "size"); - const char *filename = qdict_get_str(qdict, "filename"); - uint64_t addr = qdict_get_int(qdict, "val"); - Error *err = NULL; - int cpu_index = monitor_get_cpu_index(mon); - - if (cpu_index < 0) { - monitor_printf(mon, "No CPU available\n"); - return; - } - - qmp_memsave(addr, size, filename, true, cpu_index, &err); - hmp_handle_error(mon, err); -} - -void hmp_pmemsave(Monitor *mon, const QDict *qdict) -{ - uint32_t size = qdict_get_int(qdict, "size"); - const char *filename = qdict_get_str(qdict, "filename"); - uint64_t addr = qdict_get_int(qdict, "val"); - Error *err = NULL; - - qmp_pmemsave(addr, size, filename, &err); - hmp_handle_error(mon, err); -} - -void hmp_ringbuf_write(Monitor *mon, const QDict *qdict) -{ - const char *chardev = qdict_get_str(qdict, "device"); - const char *data = qdict_get_str(qdict, "data"); - Error *err = NULL; - - qmp_ringbuf_write(chardev, data, false, 0, &err); - - hmp_handle_error(mon, err); -} - -void hmp_ringbuf_read(Monitor *mon, const QDict *qdict) -{ - uint32_t size = qdict_get_int(qdict, "size"); - const char *chardev = qdict_get_str(qdict, "device"); - char *data; - Error *err = NULL; - int i; - - data = qmp_ringbuf_read(chardev, size, false, 0, &err); - if (hmp_handle_error(mon, err)) { - return; - } - - for (i = 0; data[i]; i++) { - unsigned char ch = data[i]; - - if (ch == '\\') { - monitor_printf(mon, "\\\\"); - } else if ((ch < 0x20 && ch != '\n' && ch != '\t') || ch == 0x7F) { - monitor_printf(mon, "\\u%04X", ch); - } else { - monitor_printf(mon, "%c", ch); - } - - } - monitor_printf(mon, "\n"); - g_free(data); -} - void hmp_cont(Monitor *mon, const QDict *qdict) { Error *err = NULL; @@ -1039,439 +171,6 @@ void hmp_cont(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, err); } -void hmp_system_wakeup(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - - qmp_system_wakeup(&err); - hmp_handle_error(mon, err); -} - -void hmp_nmi(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - - qmp_inject_nmi(&err); - hmp_handle_error(mon, err); -} - -void hmp_set_link(Monitor *mon, const QDict *qdict) -{ - const char *name = qdict_get_str(qdict, "name"); - bool up = qdict_get_bool(qdict, "up"); - Error *err = NULL; - - qmp_set_link(name, up, &err); - hmp_handle_error(mon, err); -} - -void hmp_balloon(Monitor *mon, const QDict *qdict) -{ - int64_t value = qdict_get_int(qdict, "value"); - Error *err = NULL; - - qmp_balloon(value, &err); - hmp_handle_error(mon, err); -} - -void hmp_loadvm(Monitor *mon, const QDict *qdict) -{ - int saved_vm_running = runstate_is_running(); - const char *name = qdict_get_str(qdict, "name"); - Error *err = NULL; - - vm_stop(RUN_STATE_RESTORE_VM); - - if (load_snapshot(name, NULL, false, NULL, &err) && saved_vm_running) { - vm_start(); - } - hmp_handle_error(mon, err); -} - -void hmp_savevm(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - - save_snapshot(qdict_get_try_str(qdict, "name"), - true, NULL, false, NULL, &err); - hmp_handle_error(mon, err); -} - -void hmp_delvm(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - const char *name = qdict_get_str(qdict, "name"); - - delete_snapshot(name, false, NULL, &err); - hmp_handle_error(mon, err); -} - -void hmp_announce_self(Monitor *mon, const QDict *qdict) -{ - const char *interfaces_str = qdict_get_try_str(qdict, "interfaces"); - const char *id = qdict_get_try_str(qdict, "id"); - AnnounceParameters *params = QAPI_CLONE(AnnounceParameters, - migrate_announce_params()); - - qapi_free_strList(params->interfaces); - params->interfaces = strList_from_comma_list(interfaces_str); - params->has_interfaces = params->interfaces != NULL; - params->id = g_strdup(id); - params->has_id = !!params->id; - qmp_announce_self(params, NULL); - qapi_free_AnnounceParameters(params); -} - -void hmp_migrate_cancel(Monitor *mon, const QDict *qdict) -{ - qmp_migrate_cancel(NULL); -} - -void hmp_migrate_continue(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - const char *state = qdict_get_str(qdict, "state"); - int val = qapi_enum_parse(&MigrationStatus_lookup, state, -1, &err); - - if (val >= 0) { - qmp_migrate_continue(val, &err); - } - - hmp_handle_error(mon, err); -} - -void hmp_migrate_incoming(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - const char *uri = qdict_get_str(qdict, "uri"); - - qmp_migrate_incoming(uri, &err); - - hmp_handle_error(mon, err); -} - -void hmp_migrate_recover(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - const char *uri = qdict_get_str(qdict, "uri"); - - qmp_migrate_recover(uri, &err); - - hmp_handle_error(mon, err); -} - -void hmp_migrate_pause(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - - qmp_migrate_pause(&err); - - hmp_handle_error(mon, err); -} - - -void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict) -{ - const char *cap = qdict_get_str(qdict, "capability"); - bool state = qdict_get_bool(qdict, "state"); - Error *err = NULL; - MigrationCapabilityStatusList *caps = NULL; - MigrationCapabilityStatus *value; - int val; - - val = qapi_enum_parse(&MigrationCapability_lookup, cap, -1, &err); - if (val < 0) { - goto end; - } - - value = g_malloc0(sizeof(*value)); - value->capability = val; - value->state = state; - QAPI_LIST_PREPEND(caps, value); - qmp_migrate_set_capabilities(caps, &err); - qapi_free_MigrationCapabilityStatusList(caps); - -end: - hmp_handle_error(mon, err); -} - -void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) -{ - const char *param = qdict_get_str(qdict, "parameter"); - const char *valuestr = qdict_get_str(qdict, "value"); - Visitor *v = string_input_visitor_new(valuestr); - MigrateSetParameters *p = g_new0(MigrateSetParameters, 1); - uint64_t valuebw = 0; - uint64_t cache_size; - Error *err = NULL; - int val, ret; - - val = qapi_enum_parse(&MigrationParameter_lookup, param, -1, &err); - if (val < 0) { - goto cleanup; - } - - switch (val) { - case MIGRATION_PARAMETER_COMPRESS_LEVEL: - p->has_compress_level = true; - visit_type_uint8(v, param, &p->compress_level, &err); - break; - case MIGRATION_PARAMETER_COMPRESS_THREADS: - p->has_compress_threads = true; - visit_type_uint8(v, param, &p->compress_threads, &err); - break; - case MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD: - p->has_compress_wait_thread = true; - visit_type_bool(v, param, &p->compress_wait_thread, &err); - break; - case MIGRATION_PARAMETER_DECOMPRESS_THREADS: - p->has_decompress_threads = true; - visit_type_uint8(v, param, &p->decompress_threads, &err); - break; - case MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD: - p->has_throttle_trigger_threshold = true; - visit_type_uint8(v, param, &p->throttle_trigger_threshold, &err); - break; - case MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL: - p->has_cpu_throttle_initial = true; - visit_type_uint8(v, param, &p->cpu_throttle_initial, &err); - break; - case MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT: - p->has_cpu_throttle_increment = true; - visit_type_uint8(v, param, &p->cpu_throttle_increment, &err); - break; - case MIGRATION_PARAMETER_CPU_THROTTLE_TAILSLOW: - p->has_cpu_throttle_tailslow = true; - visit_type_bool(v, param, &p->cpu_throttle_tailslow, &err); - break; - case MIGRATION_PARAMETER_MAX_CPU_THROTTLE: - p->has_max_cpu_throttle = true; - visit_type_uint8(v, param, &p->max_cpu_throttle, &err); - break; - case MIGRATION_PARAMETER_TLS_CREDS: - p->has_tls_creds = true; - p->tls_creds = g_new0(StrOrNull, 1); - p->tls_creds->type = QTYPE_QSTRING; - visit_type_str(v, param, &p->tls_creds->u.s, &err); - break; - case MIGRATION_PARAMETER_TLS_HOSTNAME: - p->has_tls_hostname = true; - p->tls_hostname = g_new0(StrOrNull, 1); - p->tls_hostname->type = QTYPE_QSTRING; - visit_type_str(v, param, &p->tls_hostname->u.s, &err); - break; - case MIGRATION_PARAMETER_TLS_AUTHZ: - p->has_tls_authz = true; - p->tls_authz = g_new0(StrOrNull, 1); - p->tls_authz->type = QTYPE_QSTRING; - visit_type_str(v, param, &p->tls_authz->u.s, &err); - break; - case MIGRATION_PARAMETER_MAX_BANDWIDTH: - p->has_max_bandwidth = true; - /* - * Can't use visit_type_size() here, because it - * defaults to Bytes rather than Mebibytes. - */ - ret = qemu_strtosz_MiB(valuestr, NULL, &valuebw); - if (ret < 0 || valuebw > INT64_MAX - || (size_t)valuebw != valuebw) { - error_setg(&err, "Invalid size %s", valuestr); - break; - } - p->max_bandwidth = valuebw; - break; - case MIGRATION_PARAMETER_DOWNTIME_LIMIT: - p->has_downtime_limit = true; - visit_type_size(v, param, &p->downtime_limit, &err); - break; - case MIGRATION_PARAMETER_X_CHECKPOINT_DELAY: - p->has_x_checkpoint_delay = true; - visit_type_uint32(v, param, &p->x_checkpoint_delay, &err); - break; - case MIGRATION_PARAMETER_BLOCK_INCREMENTAL: - p->has_block_incremental = true; - visit_type_bool(v, param, &p->block_incremental, &err); - break; - case MIGRATION_PARAMETER_MULTIFD_CHANNELS: - p->has_multifd_channels = true; - visit_type_uint8(v, param, &p->multifd_channels, &err); - break; - case MIGRATION_PARAMETER_MULTIFD_COMPRESSION: - p->has_multifd_compression = true; - visit_type_MultiFDCompression(v, param, &p->multifd_compression, - &err); - break; - case MIGRATION_PARAMETER_MULTIFD_ZLIB_LEVEL: - p->has_multifd_zlib_level = true; - visit_type_uint8(v, param, &p->multifd_zlib_level, &err); - break; - case MIGRATION_PARAMETER_MULTIFD_ZSTD_LEVEL: - p->has_multifd_zstd_level = true; - visit_type_uint8(v, param, &p->multifd_zstd_level, &err); - break; -#ifdef CONFIG_LINUX - case MIGRATION_PARAMETER_ZERO_COPY_SEND: - p->has_zero_copy_send = true; - visit_type_bool(v, param, &p->zero_copy_send, &err); - break; -#endif - case MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE: - p->has_xbzrle_cache_size = true; - if (!visit_type_size(v, param, &cache_size, &err)) { - break; - } - if (cache_size > INT64_MAX || (size_t)cache_size != cache_size) { - error_setg(&err, "Invalid size %s", valuestr); - break; - } - p->xbzrle_cache_size = cache_size; - break; - case MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH: - p->has_max_postcopy_bandwidth = true; - visit_type_size(v, param, &p->max_postcopy_bandwidth, &err); - break; - case MIGRATION_PARAMETER_ANNOUNCE_INITIAL: - p->has_announce_initial = true; - visit_type_size(v, param, &p->announce_initial, &err); - break; - case MIGRATION_PARAMETER_ANNOUNCE_MAX: - p->has_announce_max = true; - visit_type_size(v, param, &p->announce_max, &err); - break; - case MIGRATION_PARAMETER_ANNOUNCE_ROUNDS: - p->has_announce_rounds = true; - visit_type_size(v, param, &p->announce_rounds, &err); - break; - case MIGRATION_PARAMETER_ANNOUNCE_STEP: - p->has_announce_step = true; - visit_type_size(v, param, &p->announce_step, &err); - break; - case MIGRATION_PARAMETER_BLOCK_BITMAP_MAPPING: - error_setg(&err, "The block-bitmap-mapping parameter can only be set " - "through QMP"); - break; - default: - assert(0); - } - - if (err) { - goto cleanup; - } - - qmp_migrate_set_parameters(p, &err); - - cleanup: - qapi_free_MigrateSetParameters(p); - visit_free(v); - hmp_handle_error(mon, err); -} - -void hmp_client_migrate_info(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - const char *protocol = qdict_get_str(qdict, "protocol"); - const char *hostname = qdict_get_str(qdict, "hostname"); - bool has_port = qdict_haskey(qdict, "port"); - int port = qdict_get_try_int(qdict, "port", -1); - bool has_tls_port = qdict_haskey(qdict, "tls-port"); - int tls_port = qdict_get_try_int(qdict, "tls-port", -1); - const char *cert_subject = qdict_get_try_str(qdict, "cert-subject"); - - qmp_client_migrate_info(protocol, hostname, - has_port, port, has_tls_port, tls_port, - !!cert_subject, cert_subject, &err); - hmp_handle_error(mon, err); -} - -void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - qmp_migrate_start_postcopy(&err); - hmp_handle_error(mon, err); -} - -void hmp_x_colo_lost_heartbeat(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - - qmp_x_colo_lost_heartbeat(&err); - hmp_handle_error(mon, err); -} - -void hmp_set_password(Monitor *mon, const QDict *qdict) -{ - const char *protocol = qdict_get_str(qdict, "protocol"); - const char *password = qdict_get_str(qdict, "password"); - const char *display = qdict_get_try_str(qdict, "display"); - const char *connected = qdict_get_try_str(qdict, "connected"); - Error *err = NULL; - - SetPasswordOptions opts = { - .password = (char *)password, - .has_connected = !!connected, - }; - - opts.connected = qapi_enum_parse(&SetPasswordAction_lookup, connected, - SET_PASSWORD_ACTION_KEEP, &err); - if (err) { - goto out; - } - - opts.protocol = qapi_enum_parse(&DisplayProtocol_lookup, protocol, - DISPLAY_PROTOCOL_VNC, &err); - if (err) { - goto out; - } - - if (opts.protocol == DISPLAY_PROTOCOL_VNC) { - opts.u.vnc.has_display = !!display; - opts.u.vnc.display = (char *)display; - } - - qmp_set_password(&opts, &err); - -out: - hmp_handle_error(mon, err); -} - -void hmp_expire_password(Monitor *mon, const QDict *qdict) -{ - const char *protocol = qdict_get_str(qdict, "protocol"); - const char *whenstr = qdict_get_str(qdict, "time"); - const char *display = qdict_get_try_str(qdict, "display"); - Error *err = NULL; - - ExpirePasswordOptions opts = { - .time = (char *)whenstr, - }; - - opts.protocol = qapi_enum_parse(&DisplayProtocol_lookup, protocol, - DISPLAY_PROTOCOL_VNC, &err); - if (err) { - goto out; - } - - if (opts.protocol == DISPLAY_PROTOCOL_VNC) { - opts.u.vnc.has_display = !!display; - opts.u.vnc.display = (char *)display; - } - - qmp_expire_password(&opts, &err); - -out: - hmp_handle_error(mon, err); -} - - -#ifdef CONFIG_VNC -static void hmp_change_read_arg(void *opaque, const char *password, - void *readline_opaque) -{ - qmp_change_vnc_password(password, NULL); - monitor_read_command(opaque, 1); -} -#endif - void hmp_change(Monitor *mon, const QDict *qdict) { const char *device = qdict_get_str(qdict, "device"); @@ -1479,169 +178,21 @@ void hmp_change(Monitor *mon, const QDict *qdict) const char *arg = qdict_get_try_str(qdict, "arg"); const char *read_only = qdict_get_try_str(qdict, "read-only-mode"); bool force = qdict_get_try_bool(qdict, "force", false); - BlockdevChangeReadOnlyMode read_only_mode = 0; Error *err = NULL; #ifdef CONFIG_VNC if (strcmp(device, "vnc") == 0) { - if (read_only) { - monitor_printf(mon, - "Parameter 'read-only-mode' is invalid for VNC\n"); - return; - } - if (strcmp(target, "passwd") == 0 || - strcmp(target, "password") == 0) { - if (!arg) { - MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common); - monitor_read_password(hmp_mon, hmp_change_read_arg, NULL); - return; - } else { - qmp_change_vnc_password(arg, &err); - } - } else { - monitor_printf(mon, "Expected 'password' after 'vnc'\n"); - } + hmp_change_vnc(mon, device, target, arg, read_only, force, &err); } else #endif { - if (read_only) { - read_only_mode = - qapi_enum_parse(&BlockdevChangeReadOnlyMode_lookup, - read_only, - BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN, &err); - if (err) { - goto end; - } - } - - qmp_blockdev_change_medium(true, device, false, NULL, target, - !!arg, arg, true, force, - !!read_only, read_only_mode, - &err); + hmp_change_medium(mon, device, target, arg, read_only, force, &err); } -end: - hmp_handle_error(mon, err); -} - -typedef struct HMPMigrationStatus { - QEMUTimer *timer; - Monitor *mon; - bool is_block_migration; -} HMPMigrationStatus; - -static void hmp_migrate_status_cb(void *opaque) -{ - HMPMigrationStatus *status = opaque; - MigrationInfo *info; - - info = qmp_query_migrate(NULL); - if (!info->has_status || info->status == MIGRATION_STATUS_ACTIVE || - info->status == MIGRATION_STATUS_SETUP) { - if (info->has_disk) { - int progress; - - if (info->disk->remaining) { - progress = info->disk->transferred * 100 / info->disk->total; - } else { - progress = 100; - } - - monitor_printf(status->mon, "Completed %d %%\r", progress); - monitor_flush(status->mon); - } - - timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000); - } else { - if (status->is_block_migration) { - monitor_printf(status->mon, "\n"); - } - if (info->has_error_desc) { - error_report("%s", info->error_desc); - } - monitor_resume(status->mon); - timer_free(status->timer); - g_free(status); - } - - qapi_free_MigrationInfo(info); -} - -void hmp_migrate(Monitor *mon, const QDict *qdict) -{ - bool detach = qdict_get_try_bool(qdict, "detach", false); - bool blk = qdict_get_try_bool(qdict, "blk", false); - bool inc = qdict_get_try_bool(qdict, "inc", false); - bool resume = qdict_get_try_bool(qdict, "resume", false); - const char *uri = qdict_get_str(qdict, "uri"); - Error *err = NULL; - - qmp_migrate(uri, !!blk, blk, !!inc, inc, - false, false, true, resume, &err); - if (hmp_handle_error(mon, err)) { - return; - } - - if (!detach) { - HMPMigrationStatus *status; - - if (monitor_suspend(mon) < 0) { - monitor_printf(mon, "terminal does not allow synchronous " - "migration, continuing detached\n"); - return; - } - - status = g_malloc0(sizeof(*status)); - status->mon = mon; - status->is_block_migration = blk || inc; - status->timer = timer_new_ms(QEMU_CLOCK_REALTIME, hmp_migrate_status_cb, - status); - timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); - } -} - -void hmp_netdev_add(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - QemuOpts *opts; - const char *type = qdict_get_try_str(qdict, "type"); - - if (type && is_help_option(type)) { - show_netdevs(); - return; - } - opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict, &err); - if (err) { - goto out; - } - - netdev_add(opts, &err); - if (err) { - qemu_opts_del(opts); - } - -out: - hmp_handle_error(mon, err); -} - -void hmp_netdev_del(Monitor *mon, const QDict *qdict) -{ - const char *id = qdict_get_str(qdict, "id"); - Error *err = NULL; - - qmp_netdev_del(id, &err); - hmp_handle_error(mon, err); -} - -void hmp_object_add(Monitor *mon, const QDict *qdict) -{ - const char *options = qdict_get_str(qdict, "object"); - Error *err = NULL; - - user_creatable_add_from_str(options, &err); hmp_handle_error(mon, err); } +#ifdef CONFIG_POSIX void hmp_getfd(Monitor *mon, const QDict *qdict) { const char *fdname = qdict_get_str(qdict, "fdname"); @@ -1650,6 +201,7 @@ void hmp_getfd(Monitor *mon, const QDict *qdict) qmp_getfd(fdname, &err); hmp_handle_error(mon, err); } +#endif void hmp_closefd(Monitor *mon, const QDict *qdict) { @@ -1660,242 +212,6 @@ void hmp_closefd(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, err); } -void hmp_sendkey(Monitor *mon, const QDict *qdict) -{ - const char *keys = qdict_get_str(qdict, "keys"); - KeyValue *v = NULL; - KeyValueList *head = NULL, **tail = &head; - int has_hold_time = qdict_haskey(qdict, "hold-time"); - int hold_time = qdict_get_try_int(qdict, "hold-time", -1); - Error *err = NULL; - const char *separator; - int keyname_len; - - while (1) { - separator = qemu_strchrnul(keys, '-'); - keyname_len = separator - keys; - - /* Be compatible with old interface, convert user inputted "<" */ - if (keys[0] == '<' && keyname_len == 1) { - keys = "less"; - keyname_len = 4; - } - - v = g_malloc0(sizeof(*v)); - - if (strstart(keys, "0x", NULL)) { - char *endp; - int value = strtoul(keys, &endp, 0); - assert(endp <= keys + keyname_len); - if (endp != keys + keyname_len) { - goto err_out; - } - v->type = KEY_VALUE_KIND_NUMBER; - v->u.number.data = value; - } else { - int idx = index_from_key(keys, keyname_len); - if (idx == Q_KEY_CODE__MAX) { - goto err_out; - } - v->type = KEY_VALUE_KIND_QCODE; - v->u.qcode.data = idx; - } - QAPI_LIST_APPEND(tail, v); - v = NULL; - - if (!*separator) { - break; - } - keys = separator + 1; - } - - qmp_send_key(head, has_hold_time, hold_time, &err); - hmp_handle_error(mon, err); - -out: - qapi_free_KeyValue(v); - qapi_free_KeyValueList(head); - return; - -err_out: - monitor_printf(mon, "invalid parameter: %.*s\n", keyname_len, keys); - goto out; -} - -void coroutine_fn -hmp_screendump(Monitor *mon, const QDict *qdict) -{ - const char *filename = qdict_get_str(qdict, "filename"); - const char *id = qdict_get_try_str(qdict, "device"); - int64_t head = qdict_get_try_int(qdict, "head", 0); - const char *input_format = qdict_get_try_str(qdict, "format"); - Error *err = NULL; - ImageFormat format; - - format = qapi_enum_parse(&ImageFormat_lookup, input_format, - IMAGE_FORMAT_PPM, &err); - if (err) { - goto end; - } - - qmp_screendump(filename, id != NULL, id, id != NULL, head, - input_format != NULL, format, &err); -end: - hmp_handle_error(mon, err); -} - -void hmp_chardev_add(Monitor *mon, const QDict *qdict) -{ - const char *args = qdict_get_str(qdict, "args"); - Error *err = NULL; - QemuOpts *opts; - - opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), args, true); - if (opts == NULL) { - error_setg(&err, "Parsing chardev args failed"); - } else { - qemu_chr_new_from_opts(opts, NULL, &err); - qemu_opts_del(opts); - } - hmp_handle_error(mon, err); -} - -void hmp_chardev_change(Monitor *mon, const QDict *qdict) -{ - const char *args = qdict_get_str(qdict, "args"); - const char *id; - Error *err = NULL; - ChardevBackend *backend = NULL; - ChardevReturn *ret = NULL; - QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), args, - true); - if (!opts) { - error_setg(&err, "Parsing chardev args failed"); - goto end; - } - - id = qdict_get_str(qdict, "id"); - if (qemu_opts_id(opts)) { - error_setg(&err, "Unexpected 'id' parameter"); - goto end; - } - - backend = qemu_chr_parse_opts(opts, &err); - if (!backend) { - goto end; - } - - ret = qmp_chardev_change(id, backend, &err); - -end: - qapi_free_ChardevReturn(ret); - qapi_free_ChardevBackend(backend); - qemu_opts_del(opts); - hmp_handle_error(mon, err); -} - -void hmp_chardev_remove(Monitor *mon, const QDict *qdict) -{ - Error *local_err = NULL; - - qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err); - hmp_handle_error(mon, local_err); -} - -void hmp_chardev_send_break(Monitor *mon, const QDict *qdict) -{ - Error *local_err = NULL; - - qmp_chardev_send_break(qdict_get_str(qdict, "id"), &local_err); - hmp_handle_error(mon, local_err); -} - -void hmp_object_del(Monitor *mon, const QDict *qdict) -{ - const char *id = qdict_get_str(qdict, "id"); - Error *err = NULL; - - user_creatable_del(id, &err); - hmp_handle_error(mon, err); -} - -void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - MemoryDeviceInfoList *info_list = qmp_query_memory_devices(&err); - MemoryDeviceInfoList *info; - VirtioPMEMDeviceInfo *vpi; - VirtioMEMDeviceInfo *vmi; - MemoryDeviceInfo *value; - PCDIMMDeviceInfo *di; - SgxEPCDeviceInfo *se; - - for (info = info_list; info; info = info->next) { - value = info->value; - - if (value) { - switch (value->type) { - case MEMORY_DEVICE_INFO_KIND_DIMM: - case MEMORY_DEVICE_INFO_KIND_NVDIMM: - di = value->type == MEMORY_DEVICE_INFO_KIND_DIMM ? - value->u.dimm.data : value->u.nvdimm.data; - monitor_printf(mon, "Memory device [%s]: \"%s\"\n", - MemoryDeviceInfoKind_str(value->type), - di->id ? di->id : ""); - monitor_printf(mon, " addr: 0x%" PRIx64 "\n", di->addr); - monitor_printf(mon, " slot: %" PRId64 "\n", di->slot); - monitor_printf(mon, " node: %" PRId64 "\n", di->node); - monitor_printf(mon, " size: %" PRIu64 "\n", di->size); - monitor_printf(mon, " memdev: %s\n", di->memdev); - monitor_printf(mon, " hotplugged: %s\n", - di->hotplugged ? "true" : "false"); - monitor_printf(mon, " hotpluggable: %s\n", - di->hotpluggable ? "true" : "false"); - break; - case MEMORY_DEVICE_INFO_KIND_VIRTIO_PMEM: - vpi = value->u.virtio_pmem.data; - monitor_printf(mon, "Memory device [%s]: \"%s\"\n", - MemoryDeviceInfoKind_str(value->type), - vpi->id ? vpi->id : ""); - monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", vpi->memaddr); - monitor_printf(mon, " size: %" PRIu64 "\n", vpi->size); - monitor_printf(mon, " memdev: %s\n", vpi->memdev); - break; - case MEMORY_DEVICE_INFO_KIND_VIRTIO_MEM: - vmi = value->u.virtio_mem.data; - monitor_printf(mon, "Memory device [%s]: \"%s\"\n", - MemoryDeviceInfoKind_str(value->type), - vmi->id ? vmi->id : ""); - monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", vmi->memaddr); - monitor_printf(mon, " node: %" PRId64 "\n", vmi->node); - monitor_printf(mon, " requested-size: %" PRIu64 "\n", - vmi->requested_size); - monitor_printf(mon, " size: %" PRIu64 "\n", vmi->size); - monitor_printf(mon, " max-size: %" PRIu64 "\n", vmi->max_size); - monitor_printf(mon, " block-size: %" PRIu64 "\n", - vmi->block_size); - monitor_printf(mon, " memdev: %s\n", vmi->memdev); - break; - case MEMORY_DEVICE_INFO_KIND_SGX_EPC: - se = value->u.sgx_epc.data; - monitor_printf(mon, "Memory device [%s]: \"%s\"\n", - MemoryDeviceInfoKind_str(value->type), - se->id ? se->id : ""); - monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", se->memaddr); - monitor_printf(mon, " size: %" PRIu64 "\n", se->size); - monitor_printf(mon, " node: %" PRId64 "\n", se->node); - monitor_printf(mon, " memdev: %s\n", se->memdev); - break; - default: - g_assert_not_reached(); - } - } - } - - qapi_free_MemoryDeviceInfoList(info_list); - hmp_handle_error(mon, err); -} - void hmp_info_iothreads(Monitor *mon, const QDict *qdict) { IOThreadInfoList *info_list = qmp_query_iothreads(NULL); @@ -1916,326 +232,214 @@ void hmp_info_iothreads(Monitor *mon, const QDict *qdict) qapi_free_IOThreadInfoList(info_list); } -void hmp_rocker(Monitor *mon, const QDict *qdict) +void hmp_help(Monitor *mon, const QDict *qdict) { - const char *name = qdict_get_str(qdict, "name"); - RockerSwitch *rocker; - Error *err = NULL; + hmp_help_cmd(mon, qdict_get_try_str(qdict, "name")); +} - rocker = qmp_query_rocker(name, &err); - if (hmp_handle_error(mon, err)) { +void hmp_info_help(Monitor *mon, const QDict *qdict) +{ + hmp_help_cmd(mon, "info"); +} + +void hmp_info_sync_profile(Monitor *mon, const QDict *qdict) +{ + int64_t max = qdict_get_try_int(qdict, "max", 10); + bool mean = qdict_get_try_bool(qdict, "mean", false); + bool coalesce = !qdict_get_try_bool(qdict, "no_coalesce", false); + enum QSPSortBy sort_by; + + sort_by = mean ? QSP_SORT_BY_AVG_WAIT_TIME : QSP_SORT_BY_TOTAL_WAIT_TIME; + qsp_report(max, sort_by, coalesce); +} + +void hmp_info_history(Monitor *mon, const QDict *qdict) +{ + MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common); + int i; + const char *str; + + if (!hmp_mon->rs) { return; } - - monitor_printf(mon, "name: %s\n", rocker->name); - monitor_printf(mon, "id: 0x%" PRIx64 "\n", rocker->id); - monitor_printf(mon, "ports: %d\n", rocker->ports); - - qapi_free_RockerSwitch(rocker); + i = 0; + for(;;) { + str = readline_get_history(hmp_mon->rs, i); + if (!str) { + break; + } + monitor_printf(mon, "%d: '%s'\n", i, str); + i++; + } } -void hmp_rocker_ports(Monitor *mon, const QDict *qdict) -{ - RockerPortList *list, *port; - const char *name = qdict_get_str(qdict, "name"); - Error *err = NULL; - - list = qmp_query_rocker_ports(name, &err); - if (hmp_handle_error(mon, err)) { - return; - } - - monitor_printf(mon, " ena/ speed/ auto\n"); - monitor_printf(mon, " port link duplex neg?\n"); - - for (port = list; port; port = port->next) { - monitor_printf(mon, "%10s %-4s %-3s %2s %s\n", - port->value->name, - port->value->enabled ? port->value->link_up ? - "up" : "down" : "!ena", - port->value->speed == 10000 ? "10G" : "??", - port->value->duplex ? "FD" : "HD", - port->value->autoneg ? "Yes" : "No"); - } - - qapi_free_RockerPortList(list); -} - -void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict) -{ - RockerOfDpaFlowList *list, *info; - const char *name = qdict_get_str(qdict, "name"); - uint32_t tbl_id = qdict_get_try_int(qdict, "tbl_id", -1); - Error *err = NULL; - - list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &err); - if (hmp_handle_error(mon, err)) { - return; - } - - monitor_printf(mon, "prio tbl hits key(mask) --> actions\n"); - - for (info = list; info; info = info->next) { - RockerOfDpaFlow *flow = info->value; - RockerOfDpaFlowKey *key = flow->key; - RockerOfDpaFlowMask *mask = flow->mask; - RockerOfDpaFlowAction *action = flow->action; - - if (flow->hits) { - monitor_printf(mon, "%-4d %-3d %-4" PRIu64, - key->priority, key->tbl_id, flow->hits); - } else { - monitor_printf(mon, "%-4d %-3d ", - key->priority, key->tbl_id); - } - - if (key->has_in_pport) { - monitor_printf(mon, " pport %d", key->in_pport); - if (mask->has_in_pport) { - monitor_printf(mon, "(0x%x)", mask->in_pport); - } - } - - if (key->has_vlan_id) { - monitor_printf(mon, " vlan %d", - key->vlan_id & VLAN_VID_MASK); - if (mask->has_vlan_id) { - monitor_printf(mon, "(0x%x)", mask->vlan_id); - } - } - - if (key->has_tunnel_id) { - monitor_printf(mon, " tunnel %d", key->tunnel_id); - if (mask->has_tunnel_id) { - monitor_printf(mon, "(0x%x)", mask->tunnel_id); - } - } - - if (key->has_eth_type) { - switch (key->eth_type) { - case 0x0806: - monitor_printf(mon, " ARP"); - break; - case 0x0800: - monitor_printf(mon, " IP"); - break; - case 0x86dd: - monitor_printf(mon, " IPv6"); - break; - case 0x8809: - monitor_printf(mon, " LACP"); - break; - case 0x88cc: - monitor_printf(mon, " LLDP"); - break; - default: - monitor_printf(mon, " eth type 0x%04x", key->eth_type); - break; - } - } - - if (key->has_eth_src) { - if ((strcmp(key->eth_src, "01:00:00:00:00:00") == 0) && - (mask->has_eth_src) && - (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) { - monitor_printf(mon, " src "); - } else if ((strcmp(key->eth_src, "00:00:00:00:00:00") == 0) && - (mask->has_eth_src) && - (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) { - monitor_printf(mon, " src "); - } else { - monitor_printf(mon, " src %s", key->eth_src); - if (mask->has_eth_src) { - monitor_printf(mon, "(%s)", mask->eth_src); - } - } - } - - if (key->has_eth_dst) { - if ((strcmp(key->eth_dst, "01:00:00:00:00:00") == 0) && - (mask->has_eth_dst) && - (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) { - monitor_printf(mon, " dst "); - } else if ((strcmp(key->eth_dst, "00:00:00:00:00:00") == 0) && - (mask->has_eth_dst) && - (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) { - monitor_printf(mon, " dst "); - } else { - monitor_printf(mon, " dst %s", key->eth_dst); - if (mask->has_eth_dst) { - monitor_printf(mon, "(%s)", mask->eth_dst); - } - } - } - - if (key->has_ip_proto) { - monitor_printf(mon, " proto %d", key->ip_proto); - if (mask->has_ip_proto) { - monitor_printf(mon, "(0x%x)", mask->ip_proto); - } - } - - if (key->has_ip_tos) { - monitor_printf(mon, " TOS %d", key->ip_tos); - if (mask->has_ip_tos) { - monitor_printf(mon, "(0x%x)", mask->ip_tos); - } - } - - if (key->has_ip_dst) { - monitor_printf(mon, " dst %s", key->ip_dst); - } - - if (action->has_goto_tbl || action->has_group_id || - action->has_new_vlan_id) { - monitor_printf(mon, " -->"); - } - - if (action->has_new_vlan_id) { - monitor_printf(mon, " apply new vlan %d", - ntohs(action->new_vlan_id)); - } - - if (action->has_group_id) { - monitor_printf(mon, " write group 0x%08x", action->group_id); - } - - if (action->has_goto_tbl) { - monitor_printf(mon, " goto tbl %d", action->goto_tbl); - } - - monitor_printf(mon, "\n"); - } - - qapi_free_RockerOfDpaFlowList(list); -} - -void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict) -{ - RockerOfDpaGroupList *list, *g; - const char *name = qdict_get_str(qdict, "name"); - uint8_t type = qdict_get_try_int(qdict, "type", 9); - Error *err = NULL; - - list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &err); - if (hmp_handle_error(mon, err)) { - return; - } - - monitor_printf(mon, "id (decode) --> buckets\n"); - - for (g = list; g; g = g->next) { - RockerOfDpaGroup *group = g->value; - bool set = false; - - monitor_printf(mon, "0x%08x", group->id); - - monitor_printf(mon, " (type %s", group->type == 0 ? "L2 interface" : - group->type == 1 ? "L2 rewrite" : - group->type == 2 ? "L3 unicast" : - group->type == 3 ? "L2 multicast" : - group->type == 4 ? "L2 flood" : - group->type == 5 ? "L3 interface" : - group->type == 6 ? "L3 multicast" : - group->type == 7 ? "L3 ECMP" : - group->type == 8 ? "L2 overlay" : - "unknown"); - - if (group->has_vlan_id) { - monitor_printf(mon, " vlan %d", group->vlan_id); - } - - if (group->has_pport) { - monitor_printf(mon, " pport %d", group->pport); - } - - if (group->has_index) { - monitor_printf(mon, " index %d", group->index); - } - - monitor_printf(mon, ") -->"); - - if (group->has_set_vlan_id && group->set_vlan_id) { - set = true; - monitor_printf(mon, " set vlan %d", - group->set_vlan_id & VLAN_VID_MASK); - } - - if (group->has_set_eth_src) { - if (!set) { - set = true; - monitor_printf(mon, " set"); - } - monitor_printf(mon, " src %s", group->set_eth_src); - } - - if (group->has_set_eth_dst) { - if (!set) { - monitor_printf(mon, " set"); - } - monitor_printf(mon, " dst %s", group->set_eth_dst); - } - - if (group->has_ttl_check && group->ttl_check) { - monitor_printf(mon, " check TTL"); - } - - if (group->has_group_id && group->group_id) { - monitor_printf(mon, " group id 0x%08x", group->group_id); - } - - if (group->has_pop_vlan && group->pop_vlan) { - monitor_printf(mon, " pop vlan"); - } - - if (group->has_out_pport) { - monitor_printf(mon, " out pport %d", group->out_pport); - } - - if (group->has_group_ids) { - struct uint32List *id; - - monitor_printf(mon, " groups ["); - for (id = group->group_ids; id; id = id->next) { - monitor_printf(mon, "0x%08x", id->value); - if (id->next) { - monitor_printf(mon, ","); - } - } - monitor_printf(mon, "]"); - } - - monitor_printf(mon, "\n"); - } - - qapi_free_RockerOfDpaGroupList(list); -} - -void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict) +void hmp_logfile(Monitor *mon, const QDict *qdict) { Error *err = NULL; - GuidInfo *info = qmp_query_vm_generation_id(&err); - if (info) { - monitor_printf(mon, "%s\n", info->guid); + + if (!qemu_set_log_filename(qdict_get_str(qdict, "filename"), &err)) { + error_report_err(err); } - hmp_handle_error(mon, err); - qapi_free_GuidInfo(info); } -void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict) +void hmp_log(Monitor *mon, const QDict *qdict) { + int mask; + const char *items = qdict_get_str(qdict, "items"); Error *err = NULL; - MemoryInfo *info = qmp_query_memory_size_summary(&err); - if (info) { - monitor_printf(mon, "base memory: %" PRIu64 "\n", - info->base_memory); - if (info->has_plugged_memory) { - monitor_printf(mon, "plugged memory: %" PRIu64 "\n", - info->plugged_memory); + if (!strcmp(items, "none")) { + mask = 0; + } else { + mask = qemu_str_to_log_mask(items); + if (!mask) { + hmp_help_cmd(mon, "log"); + return; } - - qapi_free_MemoryInfo(info); } - hmp_handle_error(mon, err); + + if (!qemu_set_log(mask, &err)) { + error_report_err(err); + } +} + +void hmp_gdbserver(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_try_str(qdict, "device"); + if (!device) { + device = "tcp::" DEFAULT_GDBSTUB_PORT; + } + + if (gdbserver_start(device) < 0) { + monitor_printf(mon, "Could not open gdbserver on device '%s'\n", + device); + } else if (strcmp(device, "none") == 0) { + monitor_printf(mon, "Disabled gdbserver\n"); + } else { + monitor_printf(mon, "Waiting for gdb connection on device '%s'\n", + device); + } +} + +void hmp_print(Monitor *mon, const QDict *qdict) +{ + int format = qdict_get_int(qdict, "format"); + hwaddr val = qdict_get_int(qdict, "val"); + + switch(format) { + case 'o': + monitor_printf(mon, "%#" HWADDR_PRIo, val); + break; + case 'x': + monitor_printf(mon, "%#" HWADDR_PRIx, val); + break; + case 'u': + monitor_printf(mon, "%" HWADDR_PRIu, val); + break; + default: + case 'd': + monitor_printf(mon, "%" HWADDR_PRId, val); + break; + case 'c': + monitor_printc(mon, val); + break; + } + monitor_printf(mon, "\n"); +} + +void hmp_sum(Monitor *mon, const QDict *qdict) +{ + uint32_t addr; + uint16_t sum; + uint32_t start = qdict_get_int(qdict, "start"); + uint32_t size = qdict_get_int(qdict, "size"); + + sum = 0; + for(addr = start; addr < (start + size); addr++) { + uint8_t val = address_space_ldub(&address_space_memory, addr, + MEMTXATTRS_UNSPECIFIED, NULL); + /* BSD sum algorithm ('sum' Unix command) */ + sum = (sum >> 1) | (sum << 15); + sum += val; + } + monitor_printf(mon, "%05d\n", sum); +} + +void hmp_ioport_read(Monitor *mon, const QDict *qdict) +{ + int size = qdict_get_int(qdict, "size"); + int addr = qdict_get_int(qdict, "addr"); + int has_index = qdict_haskey(qdict, "index"); + uint32_t val; + int suffix; + + if (has_index) { + int index = qdict_get_int(qdict, "index"); + cpu_outb(addr & IOPORTS_MASK, index & 0xff); + addr++; + } + addr &= 0xffff; + + switch(size) { + default: + case 1: + val = cpu_inb(addr); + suffix = 'b'; + break; + case 2: + val = cpu_inw(addr); + suffix = 'w'; + break; + case 4: + val = cpu_inl(addr); + suffix = 'l'; + break; + } + monitor_printf(mon, "port%c[0x%04x] = 0x%0*x\n", + suffix, addr, size * 2, val); +} + +void hmp_ioport_write(Monitor *mon, const QDict *qdict) +{ + int size = qdict_get_int(qdict, "size"); + int addr = qdict_get_int(qdict, "addr"); + int val = qdict_get_int(qdict, "val"); + + addr &= IOPORTS_MASK; + + switch (size) { + default: + case 1: + cpu_outb(addr, val); + break; + case 2: + cpu_outw(addr, val); + break; + case 4: + cpu_outl(addr, val); + break; + } +} + +void hmp_boot_set(Monitor *mon, const QDict *qdict) +{ + Error *local_err = NULL; + const char *bootdevice = qdict_get_str(qdict, "bootdevice"); + + qemu_boot_set(bootdevice, &local_err); + if (local_err) { + error_report_err(local_err); + } else { + monitor_printf(mon, "boot device list now set to %s\n", bootdevice); + } +} + +void hmp_info_mtree(Monitor *mon, const QDict *qdict) +{ + bool flatview = qdict_get_try_bool(qdict, "flatview", false); + bool dispatch_tree = qdict_get_try_bool(qdict, "dispatch_tree", false); + bool owner = qdict_get_try_bool(qdict, "owner", false); + bool disabled = qdict_get_try_bool(qdict, "disabled", false); + + mtree_info(flatview, dispatch_tree, owner, disabled); } diff --git a/monitor/hmp-target.c b/monitor/hmp-target.c new file mode 100644 index 0000000000..1eb72ac1bf --- /dev/null +++ b/monitor/hmp-target.c @@ -0,0 +1,178 @@ +/* + * QEMU monitor, target-dependent part + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "monitor-internal.h" +#include "monitor/qdev.h" +#include "net/slirp.h" +#include "sysemu/device_tree.h" +#include "monitor/hmp-target.h" +#include "monitor/hmp.h" +#include "block/block-hmp-cmds.h" +#include "qapi/qapi-commands-control.h" +#include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-commands-machine.h" +#include "qapi/error.h" +#include "qemu/cutils.h" + +#if defined(TARGET_S390X) +#include "hw/s390x/storage-keys.h" +#include "hw/s390x/storage-attributes.h" +#endif + +/* Make devices configuration available for use in hmp-commands*.hx templates */ +#include CONFIG_DEVICES + +static HMPCommand hmp_info_cmds[]; + +/** + * Is @name in the '|' separated list of names @list? + */ +int hmp_compare_cmd(const char *name, const char *list) +{ + const char *p, *pstart; + int len; + len = strlen(name); + p = list; + for (;;) { + pstart = p; + p = qemu_strchrnul(p, '|'); + if ((p - pstart) == len && !memcmp(pstart, name, len)) { + return 1; + } + if (*p == '\0') { + break; + } + p++; + } + return 0; +} + +/* Please update hmp-commands.hx when adding or changing commands */ +static HMPCommand hmp_info_cmds[] = { +#include "hmp-commands-info.h" + { NULL, NULL, }, +}; + +/* hmp_cmds and hmp_info_cmds would be sorted at runtime */ +HMPCommand hmp_cmds[] = { +#include "hmp-commands.h" + { NULL, NULL, }, +}; + +/* + * Set @pval to the value in the register identified by @name. + * return 0 if OK, -1 if not found + */ +int get_monitor_def(Monitor *mon, int64_t *pval, const char *name) +{ + const MonitorDef *md = target_monitor_defs(); + CPUState *cs = mon_get_cpu(mon); + void *ptr; + uint64_t tmp = 0; + int ret; + + if (cs == NULL || md == NULL) { + return -1; + } + + for(; md->name != NULL; md++) { + if (hmp_compare_cmd(name, md->name)) { + if (md->get_value) { + *pval = md->get_value(mon, md, md->offset); + } else { + CPUArchState *env = mon_get_cpu_env(mon); + ptr = (uint8_t *)env + md->offset; + switch(md->type) { + case MD_I32: + *pval = *(int32_t *)ptr; + break; + case MD_TLONG: + *pval = *(target_long *)ptr; + break; + default: + *pval = 0; + break; + } + } + return 0; + } + } + + ret = target_get_monitor_def(cs, name, &tmp); + if (!ret) { + *pval = (target_long) tmp; + } + + return ret; +} + +static int +compare_mon_cmd(const void *a, const void *b) +{ + return strcmp(((const HMPCommand *)a)->name, + ((const HMPCommand *)b)->name); +} + +static void __attribute__((__constructor__)) sortcmdlist(void) +{ + qsort(hmp_cmds, ARRAY_SIZE(hmp_cmds) - 1, + sizeof(*hmp_cmds), + compare_mon_cmd); + qsort(hmp_info_cmds, ARRAY_SIZE(hmp_info_cmds) - 1, + sizeof(*hmp_info_cmds), + compare_mon_cmd); +} + +void monitor_register_hmp(const char *name, bool info, + void (*cmd)(Monitor *mon, const QDict *qdict)) +{ + HMPCommand *table = info ? hmp_info_cmds : hmp_cmds; + + while (table->name != NULL) { + if (strcmp(table->name, name) == 0) { + g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL); + table->cmd = cmd; + return; + } + table++; + } + g_assert_not_reached(); +} + +void monitor_register_hmp_info_hrt(const char *name, + HumanReadableText *(*handler)(Error **errp)) +{ + HMPCommand *table = hmp_info_cmds; + + while (table->name != NULL) { + if (strcmp(table->name, name) == 0) { + g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL); + table->cmd_info_hrt = handler; + return; + } + table++; + } + g_assert_not_reached(); +} diff --git a/monitor/hmp.c b/monitor/hmp.c index 15ca04735c..69c1b7e98a 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -27,7 +27,6 @@ #include "hw/qdev-core.h" #include "monitor-internal.h" #include "monitor/hmp.h" -#include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qnum.h" #include "qemu/config-file.h" @@ -37,7 +36,6 @@ #include "qemu/option.h" #include "qemu/units.h" #include "sysemu/block-backend.h" -#include "sysemu/runstate.h" #include "trace.h" static void monitor_command_cb(void *opaque, const char *cmdline, @@ -274,7 +272,7 @@ static void help_cmd_dump(Monitor *mon, const HMPCommand *cmds, } } -void help_cmd(Monitor *mon, const char *name) +void hmp_help_cmd(Monitor *mon, const char *name) { char *args[MAX_ARGS]; int nb_args = 0; @@ -285,10 +283,15 @@ void help_cmd(Monitor *mon, const char *name) if (!strcmp(name, "log")) { const QEMULogItem *item; monitor_printf(mon, "Log items (comma separated):\n"); - monitor_printf(mon, "%-10s %s\n", "none", "remove all logs"); + monitor_printf(mon, "%-15s %s\n", "none", "remove all logs"); for (item = qemu_log_items; item->mask != 0; item++) { - monitor_printf(mon, "%-10s %s\n", item->name, item->help); + monitor_printf(mon, "%-15s %s\n", item->name, item->help); } +#ifdef CONFIG_TRACE_LOG + monitor_printf(mon, "trace:PATTERN enable trace events\n"); + monitor_printf(mon, "\nUse \"log trace:help\" to get a list of " + "trace events.\n\n"); +#endif return; } @@ -1089,7 +1092,7 @@ static void hmp_info_human_readable_text(Monitor *mon, return; } - monitor_printf(mon, "%s", info->human_readable_text); + monitor_puts(mon, info->human_readable_text); } static void handle_hmp_command_exec(Monitor *mon, @@ -1164,7 +1167,7 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline) Coroutine *co = qemu_coroutine_create(handle_hmp_command_co, &data); monitor_set_cur(co, &mon->common); aio_co_enter(qemu_get_aio_context(), co); - AIO_WAIT_WHILE(qemu_get_aio_context(), !data.done); + AIO_WAIT_WHILE_UNLOCKED(NULL, !data.done); } qobject_unref(qdict); @@ -1186,9 +1189,7 @@ static void cmd_completion(MonitorHMP *mon, const char *name, const char *list) } memcpy(cmd, pstart, len); cmd[len] = '\0'; - if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) { - readline_add_completion(mon->rs, cmd); - } + readline_add_completion_of(mon->rs, name, cmd); if (*p == '\0') { break; } @@ -1267,7 +1268,7 @@ static void monitor_find_completion_by_table(MonitorHMP *mon, { const char *cmdname; int i; - const char *ptype, *old_ptype, *str, *name; + const char *ptype, *old_ptype, *str; const HMPCommand *cmd; BlockBackend *blk = NULL; @@ -1332,11 +1333,7 @@ static void monitor_find_completion_by_table(MonitorHMP *mon, /* block device name completion */ readline_set_completion_index(mon->rs, strlen(str)); while ((blk = blk_next(blk)) != NULL) { - name = blk_name(blk); - if (str[0] == '\0' || - !strncmp(name, str, strlen(str))) { - readline_add_completion(mon->rs, name); - } + readline_add_completion_of(mon->rs, str, blk_name(blk)); } break; case 's': @@ -1404,45 +1401,42 @@ static void monitor_read(void *opaque, const uint8_t *buf, int size) static void monitor_event(void *opaque, QEMUChrEvent event) { Monitor *mon = opaque; - MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common); switch (event) { case CHR_EVENT_MUX_IN: qemu_mutex_lock(&mon->mon_lock); - mon->mux_out = 0; - qemu_mutex_unlock(&mon->mon_lock); - if (mon->reset_seen) { - readline_restart(hmp_mon->rs); + if (mon->mux_out) { + mon->mux_out = 0; monitor_resume(mon); - monitor_flush(mon); - } else { - qatomic_mb_set(&mon->suspend_cnt, 0); } + qemu_mutex_unlock(&mon->mon_lock); break; case CHR_EVENT_MUX_OUT: - if (mon->reset_seen) { - if (qatomic_mb_read(&mon->suspend_cnt) == 0) { - monitor_printf(mon, "\n"); - } - monitor_flush(mon); - monitor_suspend(mon); - } else { - qatomic_inc(&mon->suspend_cnt); - } qemu_mutex_lock(&mon->mon_lock); - mon->mux_out = 1; + if (!mon->mux_out) { + if (mon->reset_seen && !mon->suspend_cnt) { + monitor_puts_locked(mon, "\n"); + } else { + monitor_flush_locked(mon); + } + monitor_suspend(mon); + mon->mux_out = 1; + } qemu_mutex_unlock(&mon->mon_lock); break; case CHR_EVENT_OPENED: monitor_printf(mon, "QEMU %s monitor - type 'help' for more " "information\n", QEMU_VERSION); - if (!mon->mux_out) { - readline_restart(hmp_mon->rs); - readline_show_prompt(hmp_mon->rs); - } + qemu_mutex_lock(&mon->mon_lock); mon->reset_seen = 1; + if (!mon->mux_out) { + /* Suspend-resume forces the prompt to be printed. */ + monitor_suspend(mon); + monitor_resume(mon); + } + qemu_mutex_unlock(&mon->mon_lock); mon_refcount++; break; diff --git a/monitor/meson.build b/monitor/meson.build index 6d00985ace..5269492cf0 100644 --- a/monitor/meson.build +++ b/monitor/meson.build @@ -1,9 +1,11 @@ qmp_ss.add(files('monitor.c', 'qmp.c', 'qmp-cmds-control.c')) -softmmu_ss.add(files( +system_ss.add(files( + 'fds.c', 'hmp-cmds.c', 'hmp.c', )) -softmmu_ss.add([spice_headers, files('qmp-cmds.c')]) +system_ss.add([spice_headers, files('qmp-cmds.c')]) -specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files('misc.c'), spice]) +specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', + if_true: [files( 'hmp-cmds-target.c', 'hmp-target.c'), spice]) diff --git a/monitor/misc.c b/monitor/misc.c deleted file mode 100644 index 3d2312ba8d..0000000000 --- a/monitor/misc.c +++ /dev/null @@ -1,1987 +0,0 @@ -/* - * QEMU monitor - * - * Copyright (c) 2003-2004 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "monitor-internal.h" -#include "monitor/qdev.h" -#include "hw/usb.h" -#include "hw/pci/pci.h" -#include "sysemu/watchdog.h" -#include "hw/loader.h" -#include "exec/gdbstub.h" -#include "net/net.h" -#include "net/slirp.h" -#include "ui/qemu-spice.h" -#include "qemu/config-file.h" -#include "qemu/ctype.h" -#include "ui/console.h" -#include "ui/input.h" -#include "audio/audio.h" -#include "disas/disas.h" -#include "sysemu/balloon.h" -#include "qemu/timer.h" -#include "qemu/log.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" -#include "authz/list.h" -#include "qapi/util.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" -#include "sysemu/tpm.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" -#include "qapi/qmp/qstring.h" -#include "qom/object_interfaces.h" -#include "trace/control.h" -#include "monitor/hmp-target.h" -#include "monitor/hmp.h" -#ifdef CONFIG_TRACE_SIMPLE -#include "trace/simple.h" -#endif -#include "exec/memory.h" -#include "exec/exec-all.h" -#include "qemu/option.h" -#include "qemu/thread.h" -#include "block/qapi.h" -#include "block/block-hmp-cmds.h" -#include "qapi/qapi-commands-char.h" -#include "qapi/qapi-commands-control.h" -#include "qapi/qapi-commands-migration.h" -#include "qapi/qapi-commands-misc.h" -#include "qapi/qapi-commands-qom.h" -#include "qapi/qapi-commands-run-state.h" -#include "qapi/qapi-commands-trace.h" -#include "qapi/qapi-commands-machine.h" -#include "qapi/qapi-init-commands.h" -#include "qapi/error.h" -#include "qapi/qmp-event.h" -#include "sysemu/cpus.h" -#include "qemu/cutils.h" - -#if defined(TARGET_S390X) -#include "hw/s390x/storage-keys.h" -#include "hw/s390x/storage-attributes.h" -#endif - -/* Make devices configuration available for use in hmp-commands*.hx templates */ -#include CONFIG_DEVICES - -/* file descriptors passed via SCM_RIGHTS */ -typedef struct mon_fd_t mon_fd_t; -struct mon_fd_t { - char *name; - int fd; - QLIST_ENTRY(mon_fd_t) next; -}; - -/* file descriptor associated with a file descriptor set */ -typedef struct MonFdsetFd MonFdsetFd; -struct MonFdsetFd { - int fd; - bool removed; - char *opaque; - QLIST_ENTRY(MonFdsetFd) next; -}; - -/* file descriptor set containing fds passed via SCM_RIGHTS */ -typedef struct MonFdset MonFdset; -struct MonFdset { - int64_t id; - QLIST_HEAD(, MonFdsetFd) fds; - QLIST_HEAD(, MonFdsetFd) dup_fds; - QLIST_ENTRY(MonFdset) next; -}; - -/* Protects mon_fdsets */ -static QemuMutex mon_fdsets_lock; -static QLIST_HEAD(, MonFdset) mon_fdsets; - -static HMPCommand hmp_info_cmds[]; - -char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, - int64_t cpu_index, Error **errp) -{ - char *output = NULL; - MonitorHMP hmp = {}; - - monitor_data_init(&hmp.common, false, true, false); - - if (has_cpu_index) { - int ret = monitor_set_cpu(&hmp.common, cpu_index); - if (ret < 0) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", - "a CPU number"); - goto out; - } - } - - handle_hmp_command(&hmp, command_line); - - WITH_QEMU_LOCK_GUARD(&hmp.common.mon_lock) { - output = g_strdup(hmp.common.outbuf->str); - } - -out: - monitor_data_destroy(&hmp.common); - return output; -} - -/** - * Is @name in the '|' separated list of names @list? - */ -int hmp_compare_cmd(const char *name, const char *list) -{ - const char *p, *pstart; - int len; - len = strlen(name); - p = list; - for (;;) { - pstart = p; - p = qemu_strchrnul(p, '|'); - if ((p - pstart) == len && !memcmp(pstart, name, len)) { - return 1; - } - if (*p == '\0') { - break; - } - p++; - } - return 0; -} - -static void do_help_cmd(Monitor *mon, const QDict *qdict) -{ - help_cmd(mon, qdict_get_try_str(qdict, "name")); -} - -static void hmp_trace_event(Monitor *mon, const QDict *qdict) -{ - const char *tp_name = qdict_get_str(qdict, "name"); - bool new_state = qdict_get_bool(qdict, "option"); - bool has_vcpu = qdict_haskey(qdict, "vcpu"); - int vcpu = qdict_get_try_int(qdict, "vcpu", 0); - Error *local_err = NULL; - - if (vcpu < 0) { - monitor_printf(mon, "argument vcpu must be positive"); - return; - } - - qmp_trace_event_set_state(tp_name, new_state, true, true, has_vcpu, vcpu, &local_err); - if (local_err) { - error_report_err(local_err); - } -} - -#ifdef CONFIG_TRACE_SIMPLE -static void hmp_trace_file(Monitor *mon, const QDict *qdict) -{ - const char *op = qdict_get_try_str(qdict, "op"); - const char *arg = qdict_get_try_str(qdict, "arg"); - - if (!op) { - st_print_trace_file_status(); - } else if (!strcmp(op, "on")) { - st_set_trace_file_enabled(true); - } else if (!strcmp(op, "off")) { - st_set_trace_file_enabled(false); - } else if (!strcmp(op, "flush")) { - st_flush_trace_buffer(); - } else if (!strcmp(op, "set")) { - if (arg) { - st_set_trace_file(arg); - } - } else { - monitor_printf(mon, "unexpected argument \"%s\"\n", op); - help_cmd(mon, "trace-file"); - } -} -#endif - -static void hmp_info_help(Monitor *mon, const QDict *qdict) -{ - help_cmd(mon, "info"); -} - -static void monitor_init_qmp_commands(void) -{ - /* - * Two command lists: - * - qmp_commands contains all QMP commands - * - qmp_cap_negotiation_commands contains just - * "qmp_capabilities", to enforce capability negotiation - */ - - qmp_init_marshal(&qmp_commands); - - qmp_register_command(&qmp_commands, "device_add", - qmp_device_add, 0, 0); - - QTAILQ_INIT(&qmp_cap_negotiation_commands); - qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", - qmp_marshal_qmp_capabilities, - QCO_ALLOW_PRECONFIG, 0); -} - -/* Set the current CPU defined by the user. Callers must hold BQL. */ -int monitor_set_cpu(Monitor *mon, int cpu_index) -{ - CPUState *cpu; - - cpu = qemu_get_cpu(cpu_index); - if (cpu == NULL) { - return -1; - } - g_free(mon->mon_cpu_path); - mon->mon_cpu_path = object_get_canonical_path(OBJECT(cpu)); - return 0; -} - -/* Callers must hold BQL. */ -static CPUState *mon_get_cpu_sync(Monitor *mon, bool synchronize) -{ - CPUState *cpu = NULL; - - if (mon->mon_cpu_path) { - cpu = (CPUState *) object_resolve_path_type(mon->mon_cpu_path, - TYPE_CPU, NULL); - if (!cpu) { - g_free(mon->mon_cpu_path); - mon->mon_cpu_path = NULL; - } - } - if (!mon->mon_cpu_path) { - if (!first_cpu) { - return NULL; - } - monitor_set_cpu(mon, first_cpu->cpu_index); - cpu = first_cpu; - } - assert(cpu != NULL); - if (synchronize) { - cpu_synchronize_state(cpu); - } - return cpu; -} - -CPUState *mon_get_cpu(Monitor *mon) -{ - return mon_get_cpu_sync(mon, true); -} - -CPUArchState *mon_get_cpu_env(Monitor *mon) -{ - CPUState *cs = mon_get_cpu(mon); - - return cs ? cs->env_ptr : NULL; -} - -int monitor_get_cpu_index(Monitor *mon) -{ - CPUState *cs = mon_get_cpu_sync(mon, false); - - return cs ? cs->cpu_index : UNASSIGNED_CPU_INDEX; -} - -static void hmp_info_registers(Monitor *mon, const QDict *qdict) -{ - bool all_cpus = qdict_get_try_bool(qdict, "cpustate_all", false); - CPUState *cs; - - if (all_cpus) { - CPU_FOREACH(cs) { - monitor_printf(mon, "\nCPU#%d\n", cs->cpu_index); - cpu_dump_state(cs, NULL, CPU_DUMP_FPU); - } - } else { - cs = mon_get_cpu(mon); - - if (!cs) { - monitor_printf(mon, "No CPU available\n"); - return; - } - - cpu_dump_state(cs, NULL, CPU_DUMP_FPU); - } -} - -static void hmp_info_sync_profile(Monitor *mon, const QDict *qdict) -{ - int64_t max = qdict_get_try_int(qdict, "max", 10); - bool mean = qdict_get_try_bool(qdict, "mean", false); - bool coalesce = !qdict_get_try_bool(qdict, "no_coalesce", false); - enum QSPSortBy sort_by; - - sort_by = mean ? QSP_SORT_BY_AVG_WAIT_TIME : QSP_SORT_BY_TOTAL_WAIT_TIME; - qsp_report(max, sort_by, coalesce); -} - -static void hmp_info_history(Monitor *mon, const QDict *qdict) -{ - MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common); - int i; - const char *str; - - if (!hmp_mon->rs) { - return; - } - i = 0; - for(;;) { - str = readline_get_history(hmp_mon->rs, i); - if (!str) { - break; - } - monitor_printf(mon, "%d: '%s'\n", i, str); - i++; - } -} - -static void hmp_info_trace_events(Monitor *mon, const QDict *qdict) -{ - const char *name = qdict_get_try_str(qdict, "name"); - bool has_vcpu = qdict_haskey(qdict, "vcpu"); - int vcpu = qdict_get_try_int(qdict, "vcpu", 0); - TraceEventInfoList *events; - TraceEventInfoList *elem; - Error *local_err = NULL; - - if (name == NULL) { - name = "*"; - } - if (vcpu < 0) { - monitor_printf(mon, "argument vcpu must be positive"); - return; - } - - events = qmp_trace_event_get_state(name, has_vcpu, vcpu, &local_err); - if (local_err) { - error_report_err(local_err); - return; - } - - for (elem = events; elem != NULL; elem = elem->next) { - monitor_printf(mon, "%s : state %u\n", - elem->value->name, - elem->value->state == TRACE_EVENT_STATE_ENABLED ? 1 : 0); - } - qapi_free_TraceEventInfoList(events); -} - -void qmp_client_migrate_info(const char *protocol, const char *hostname, - bool has_port, int64_t port, - bool has_tls_port, int64_t tls_port, - bool has_cert_subject, const char *cert_subject, - Error **errp) -{ - if (strcmp(protocol, "spice") == 0) { - if (!qemu_using_spice(errp)) { - return; - } - - if (!has_port && !has_tls_port) { - error_setg(errp, QERR_MISSING_PARAMETER, "port/tls-port"); - return; - } - - if (qemu_spice.migrate_info(hostname, - has_port ? port : -1, - has_tls_port ? tls_port : -1, - cert_subject)) { - error_setg(errp, "Could not set up display for migration"); - return; - } - return; - } - - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "'spice'"); -} - -static void hmp_logfile(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - - if (!qemu_set_log_filename(qdict_get_str(qdict, "filename"), &err)) { - error_report_err(err); - } -} - -static void hmp_log(Monitor *mon, const QDict *qdict) -{ - int mask; - const char *items = qdict_get_str(qdict, "items"); - Error *err = NULL; - - if (!strcmp(items, "none")) { - mask = 0; - } else { - mask = qemu_str_to_log_mask(items); - if (!mask) { - help_cmd(mon, "log"); - return; - } - } - - if (!qemu_set_log(mask, &err)) { - error_report_err(err); - } -} - -static void hmp_singlestep(Monitor *mon, const QDict *qdict) -{ - const char *option = qdict_get_try_str(qdict, "option"); - if (!option || !strcmp(option, "on")) { - singlestep = 1; - } else if (!strcmp(option, "off")) { - singlestep = 0; - } else { - monitor_printf(mon, "unexpected option %s\n", option); - } -} - -static void hmp_gdbserver(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_try_str(qdict, "device"); - if (!device) { - device = "tcp::" DEFAULT_GDBSTUB_PORT; - } - - if (gdbserver_start(device) < 0) { - monitor_printf(mon, "Could not open gdbserver on device '%s'\n", - device); - } else if (strcmp(device, "none") == 0) { - monitor_printf(mon, "Disabled gdbserver\n"); - } else { - monitor_printf(mon, "Waiting for gdb connection on device '%s'\n", - device); - } -} - -static void hmp_watchdog_action(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - WatchdogAction action; - char *qapi_value; - - qapi_value = g_ascii_strdown(qdict_get_str(qdict, "action"), -1); - action = qapi_enum_parse(&WatchdogAction_lookup, qapi_value, -1, &err); - g_free(qapi_value); - if (err) { - hmp_handle_error(mon, err); - return; - } - qmp_watchdog_set_action(action, &error_abort); -} - -static void monitor_printc(Monitor *mon, int c) -{ - monitor_printf(mon, "'"); - switch(c) { - case '\'': - monitor_printf(mon, "\\'"); - break; - case '\\': - monitor_printf(mon, "\\\\"); - break; - case '\n': - monitor_printf(mon, "\\n"); - break; - case '\r': - monitor_printf(mon, "\\r"); - break; - default: - if (c >= 32 && c <= 126) { - monitor_printf(mon, "%c", c); - } else { - monitor_printf(mon, "\\x%02x", c); - } - break; - } - monitor_printf(mon, "'"); -} - -static void memory_dump(Monitor *mon, int count, int format, int wsize, - hwaddr addr, int is_physical) -{ - int l, line_size, i, max_digits, len; - uint8_t buf[16]; - uint64_t v; - CPUState *cs = mon_get_cpu(mon); - - if (!cs && (format == 'i' || !is_physical)) { - monitor_printf(mon, "Can not dump without CPU\n"); - return; - } - - if (format == 'i') { - monitor_disas(mon, cs, addr, count, is_physical); - return; - } - - len = wsize * count; - if (wsize == 1) { - line_size = 8; - } else { - line_size = 16; - } - max_digits = 0; - - switch(format) { - case 'o': - max_digits = DIV_ROUND_UP(wsize * 8, 3); - break; - default: - case 'x': - max_digits = (wsize * 8) / 4; - break; - case 'u': - case 'd': - max_digits = DIV_ROUND_UP(wsize * 8 * 10, 33); - break; - case 'c': - wsize = 1; - break; - } - - while (len > 0) { - if (is_physical) { - monitor_printf(mon, TARGET_FMT_plx ":", addr); - } else { - monitor_printf(mon, TARGET_FMT_lx ":", (target_ulong)addr); - } - l = len; - if (l > line_size) - l = line_size; - if (is_physical) { - AddressSpace *as = cs ? cs->as : &address_space_memory; - MemTxResult r = address_space_read(as, addr, - MEMTXATTRS_UNSPECIFIED, buf, l); - if (r != MEMTX_OK) { - monitor_printf(mon, " Cannot access memory\n"); - break; - } - } else { - if (cpu_memory_rw_debug(cs, addr, buf, l, 0) < 0) { - monitor_printf(mon, " Cannot access memory\n"); - break; - } - } - i = 0; - while (i < l) { - switch(wsize) { - default: - case 1: - v = ldub_p(buf + i); - break; - case 2: - v = lduw_p(buf + i); - break; - case 4: - v = (uint32_t)ldl_p(buf + i); - break; - case 8: - v = ldq_p(buf + i); - break; - } - monitor_printf(mon, " "); - switch(format) { - case 'o': - monitor_printf(mon, "%#*" PRIo64, max_digits, v); - break; - case 'x': - monitor_printf(mon, "0x%0*" PRIx64, max_digits, v); - break; - case 'u': - monitor_printf(mon, "%*" PRIu64, max_digits, v); - break; - case 'd': - monitor_printf(mon, "%*" PRId64, max_digits, v); - break; - case 'c': - monitor_printc(mon, v); - break; - } - i += wsize; - } - monitor_printf(mon, "\n"); - addr += l; - len -= l; - } -} - -static void hmp_memory_dump(Monitor *mon, const QDict *qdict) -{ - int count = qdict_get_int(qdict, "count"); - int format = qdict_get_int(qdict, "format"); - int size = qdict_get_int(qdict, "size"); - target_long addr = qdict_get_int(qdict, "addr"); - - memory_dump(mon, count, format, size, addr, 0); -} - -static void hmp_physical_memory_dump(Monitor *mon, const QDict *qdict) -{ - int count = qdict_get_int(qdict, "count"); - int format = qdict_get_int(qdict, "format"); - int size = qdict_get_int(qdict, "size"); - hwaddr addr = qdict_get_int(qdict, "addr"); - - memory_dump(mon, count, format, size, addr, 1); -} - -void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, uint64_t size, Error **errp) -{ - Int128 gpa_region_size; - MemoryRegionSection mrs = memory_region_find(get_system_memory(), - addr, size); - - if (!mrs.mr) { - error_setg(errp, "No memory is mapped at address 0x%" HWADDR_PRIx, addr); - return NULL; - } - - if (!memory_region_is_ram(mrs.mr) && !memory_region_is_romd(mrs.mr)) { - error_setg(errp, "Memory at address 0x%" HWADDR_PRIx "is not RAM", addr); - memory_region_unref(mrs.mr); - return NULL; - } - - gpa_region_size = int128_make64(size); - if (int128_lt(mrs.size, gpa_region_size)) { - error_setg(errp, "Size of memory region at 0x%" HWADDR_PRIx - " exceeded.", addr); - memory_region_unref(mrs.mr); - return NULL; - } - - *p_mr = mrs.mr; - return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region); -} - -static void hmp_gpa2hva(Monitor *mon, const QDict *qdict) -{ - hwaddr addr = qdict_get_int(qdict, "addr"); - Error *local_err = NULL; - MemoryRegion *mr = NULL; - void *ptr; - - ptr = gpa2hva(&mr, addr, 1, &local_err); - if (local_err) { - error_report_err(local_err); - return; - } - - monitor_printf(mon, "Host virtual address for 0x%" HWADDR_PRIx - " (%s) is %p\n", - addr, mr->name, ptr); - - memory_region_unref(mr); -} - -static void hmp_gva2gpa(Monitor *mon, const QDict *qdict) -{ - target_ulong addr = qdict_get_int(qdict, "addr"); - MemTxAttrs attrs; - CPUState *cs = mon_get_cpu(mon); - hwaddr gpa; - - if (!cs) { - monitor_printf(mon, "No cpu\n"); - return; - } - - gpa = cpu_get_phys_page_attrs_debug(cs, addr & TARGET_PAGE_MASK, &attrs); - if (gpa == -1) { - monitor_printf(mon, "Unmapped\n"); - } else { - monitor_printf(mon, "gpa: %#" HWADDR_PRIx "\n", - gpa + (addr & ~TARGET_PAGE_MASK)); - } -} - -#ifdef CONFIG_LINUX -static uint64_t vtop(void *ptr, Error **errp) -{ - uint64_t pinfo; - uint64_t ret = -1; - uintptr_t addr = (uintptr_t) ptr; - uintptr_t pagesize = qemu_real_host_page_size(); - off_t offset = addr / pagesize * sizeof(pinfo); - int fd; - - fd = open("/proc/self/pagemap", O_RDONLY); - if (fd == -1) { - error_setg_errno(errp, errno, "Cannot open /proc/self/pagemap"); - return -1; - } - - /* Force copy-on-write if necessary. */ - qatomic_add((uint8_t *)ptr, 0); - - if (pread(fd, &pinfo, sizeof(pinfo), offset) != sizeof(pinfo)) { - error_setg_errno(errp, errno, "Cannot read pagemap"); - goto out; - } - if ((pinfo & (1ull << 63)) == 0) { - error_setg(errp, "Page not present"); - goto out; - } - ret = ((pinfo & 0x007fffffffffffffull) * pagesize) | (addr & (pagesize - 1)); - -out: - close(fd); - return ret; -} - -static void hmp_gpa2hpa(Monitor *mon, const QDict *qdict) -{ - hwaddr addr = qdict_get_int(qdict, "addr"); - Error *local_err = NULL; - MemoryRegion *mr = NULL; - void *ptr; - uint64_t physaddr; - - ptr = gpa2hva(&mr, addr, 1, &local_err); - if (local_err) { - error_report_err(local_err); - return; - } - - physaddr = vtop(ptr, &local_err); - if (local_err) { - error_report_err(local_err); - } else { - monitor_printf(mon, "Host physical address for 0x%" HWADDR_PRIx - " (%s) is 0x%" PRIx64 "\n", - addr, mr->name, (uint64_t) physaddr); - } - - memory_region_unref(mr); -} -#endif - -static void do_print(Monitor *mon, const QDict *qdict) -{ - int format = qdict_get_int(qdict, "format"); - hwaddr val = qdict_get_int(qdict, "val"); - - switch(format) { - case 'o': - monitor_printf(mon, "%#" HWADDR_PRIo, val); - break; - case 'x': - monitor_printf(mon, "%#" HWADDR_PRIx, val); - break; - case 'u': - monitor_printf(mon, "%" HWADDR_PRIu, val); - break; - default: - case 'd': - monitor_printf(mon, "%" HWADDR_PRId, val); - break; - case 'c': - monitor_printc(mon, val); - break; - } - monitor_printf(mon, "\n"); -} - -static void hmp_sum(Monitor *mon, const QDict *qdict) -{ - uint32_t addr; - uint16_t sum; - uint32_t start = qdict_get_int(qdict, "start"); - uint32_t size = qdict_get_int(qdict, "size"); - - sum = 0; - for(addr = start; addr < (start + size); addr++) { - uint8_t val = address_space_ldub(&address_space_memory, addr, - MEMTXATTRS_UNSPECIFIED, NULL); - /* BSD sum algorithm ('sum' Unix command) */ - sum = (sum >> 1) | (sum << 15); - sum += val; - } - monitor_printf(mon, "%05d\n", sum); -} - -static int mouse_button_state; - -static void hmp_mouse_move(Monitor *mon, const QDict *qdict) -{ - int dx, dy, dz, button; - const char *dx_str = qdict_get_str(qdict, "dx_str"); - const char *dy_str = qdict_get_str(qdict, "dy_str"); - const char *dz_str = qdict_get_try_str(qdict, "dz_str"); - - dx = strtol(dx_str, NULL, 0); - dy = strtol(dy_str, NULL, 0); - qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx); - qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy); - - if (dz_str) { - dz = strtol(dz_str, NULL, 0); - if (dz != 0) { - button = (dz > 0) ? INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN; - qemu_input_queue_btn(NULL, button, true); - qemu_input_event_sync(); - qemu_input_queue_btn(NULL, button, false); - } - } - qemu_input_event_sync(); -} - -static void hmp_mouse_button(Monitor *mon, const QDict *qdict) -{ - static uint32_t bmap[INPUT_BUTTON__MAX] = { - [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, - [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, - [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, - }; - int button_state = qdict_get_int(qdict, "button_state"); - - if (mouse_button_state == button_state) { - return; - } - qemu_input_update_buttons(NULL, bmap, mouse_button_state, button_state); - qemu_input_event_sync(); - mouse_button_state = button_state; -} - -static void hmp_ioport_read(Monitor *mon, const QDict *qdict) -{ - int size = qdict_get_int(qdict, "size"); - int addr = qdict_get_int(qdict, "addr"); - int has_index = qdict_haskey(qdict, "index"); - uint32_t val; - int suffix; - - if (has_index) { - int index = qdict_get_int(qdict, "index"); - cpu_outb(addr & IOPORTS_MASK, index & 0xff); - addr++; - } - addr &= 0xffff; - - switch(size) { - default: - case 1: - val = cpu_inb(addr); - suffix = 'b'; - break; - case 2: - val = cpu_inw(addr); - suffix = 'w'; - break; - case 4: - val = cpu_inl(addr); - suffix = 'l'; - break; - } - monitor_printf(mon, "port%c[0x%04x] = 0x%0*x\n", - suffix, addr, size * 2, val); -} - -static void hmp_ioport_write(Monitor *mon, const QDict *qdict) -{ - int size = qdict_get_int(qdict, "size"); - int addr = qdict_get_int(qdict, "addr"); - int val = qdict_get_int(qdict, "val"); - - addr &= IOPORTS_MASK; - - switch (size) { - default: - case 1: - cpu_outb(addr, val); - break; - case 2: - cpu_outw(addr, val); - break; - case 4: - cpu_outl(addr, val); - break; - } -} - -static void hmp_boot_set(Monitor *mon, const QDict *qdict) -{ - Error *local_err = NULL; - const char *bootdevice = qdict_get_str(qdict, "bootdevice"); - - qemu_boot_set(bootdevice, &local_err); - if (local_err) { - error_report_err(local_err); - } else { - monitor_printf(mon, "boot device list now set to %s\n", bootdevice); - } -} - -static void hmp_info_mtree(Monitor *mon, const QDict *qdict) -{ - bool flatview = qdict_get_try_bool(qdict, "flatview", false); - bool dispatch_tree = qdict_get_try_bool(qdict, "dispatch_tree", false); - bool owner = qdict_get_try_bool(qdict, "owner", false); - bool disabled = qdict_get_try_bool(qdict, "disabled", false); - - mtree_info(flatview, dispatch_tree, owner, disabled); -} - -/* Capture support */ -static QLIST_HEAD (capture_list_head, CaptureState) capture_head; - -static void hmp_info_capture(Monitor *mon, const QDict *qdict) -{ - int i; - CaptureState *s; - - for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) { - monitor_printf(mon, "[%d]: ", i); - s->ops.info (s->opaque); - } -} - -static void hmp_stopcapture(Monitor *mon, const QDict *qdict) -{ - int i; - int n = qdict_get_int(qdict, "n"); - CaptureState *s; - - for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) { - if (i == n) { - s->ops.destroy (s->opaque); - QLIST_REMOVE (s, entries); - g_free (s); - return; - } - } -} - -static void hmp_wavcapture(Monitor *mon, const QDict *qdict) -{ - const char *path = qdict_get_str(qdict, "path"); - int freq = qdict_get_try_int(qdict, "freq", 44100); - int bits = qdict_get_try_int(qdict, "bits", 16); - int nchannels = qdict_get_try_int(qdict, "nchannels", 2); - const char *audiodev = qdict_get_str(qdict, "audiodev"); - CaptureState *s; - AudioState *as = audio_state_by_name(audiodev); - - if (!as) { - monitor_printf(mon, "Audiodev '%s' not found\n", audiodev); - return; - } - - s = g_malloc0 (sizeof (*s)); - - if (wav_start_capture(as, s, path, freq, bits, nchannels)) { - monitor_printf(mon, "Failed to add wave capture\n"); - g_free (s); - return; - } - QLIST_INSERT_HEAD (&capture_head, s, entries); -} - -void qmp_getfd(const char *fdname, Error **errp) -{ - Monitor *cur_mon = monitor_cur(); - mon_fd_t *monfd; - int fd, tmp_fd; - - fd = qemu_chr_fe_get_msgfd(&cur_mon->chr); - if (fd == -1) { - error_setg(errp, "No file descriptor supplied via SCM_RIGHTS"); - return; - } - - if (qemu_isdigit(fdname[0])) { - close(fd); - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdname", - "a name not starting with a digit"); - return; - } - - QEMU_LOCK_GUARD(&cur_mon->mon_lock); - QLIST_FOREACH(monfd, &cur_mon->fds, next) { - if (strcmp(monfd->name, fdname) != 0) { - continue; - } - - tmp_fd = monfd->fd; - monfd->fd = fd; - /* Make sure close() is outside critical section */ - close(tmp_fd); - return; - } - - monfd = g_new0(mon_fd_t, 1); - monfd->name = g_strdup(fdname); - monfd->fd = fd; - - QLIST_INSERT_HEAD(&cur_mon->fds, monfd, next); -} - -void qmp_closefd(const char *fdname, Error **errp) -{ - Monitor *cur_mon = monitor_cur(); - mon_fd_t *monfd; - int tmp_fd; - - qemu_mutex_lock(&cur_mon->mon_lock); - QLIST_FOREACH(monfd, &cur_mon->fds, next) { - if (strcmp(monfd->name, fdname) != 0) { - continue; - } - - QLIST_REMOVE(monfd, next); - tmp_fd = monfd->fd; - g_free(monfd->name); - g_free(monfd); - qemu_mutex_unlock(&cur_mon->mon_lock); - /* Make sure close() is outside critical section */ - close(tmp_fd); - return; - } - - qemu_mutex_unlock(&cur_mon->mon_lock); - error_setg(errp, "File descriptor named '%s' not found", fdname); -} - -int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) -{ - mon_fd_t *monfd; - - QEMU_LOCK_GUARD(&mon->mon_lock); - QLIST_FOREACH(monfd, &mon->fds, next) { - int fd; - - if (strcmp(monfd->name, fdname) != 0) { - continue; - } - - fd = monfd->fd; - - /* caller takes ownership of fd */ - QLIST_REMOVE(monfd, next); - g_free(monfd->name); - g_free(monfd); - - return fd; - } - - error_setg(errp, "File descriptor named '%s' has not been found", fdname); - return -1; -} - -static void monitor_fdset_cleanup(MonFdset *mon_fdset) -{ - MonFdsetFd *mon_fdset_fd; - MonFdsetFd *mon_fdset_fd_next; - - QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, mon_fdset_fd_next) { - if ((mon_fdset_fd->removed || - (QLIST_EMPTY(&mon_fdset->dup_fds) && mon_refcount == 0)) && - runstate_is_running()) { - close(mon_fdset_fd->fd); - g_free(mon_fdset_fd->opaque); - QLIST_REMOVE(mon_fdset_fd, next); - g_free(mon_fdset_fd); - } - } - - if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) { - QLIST_REMOVE(mon_fdset, next); - g_free(mon_fdset); - } -} - -void monitor_fdsets_cleanup(void) -{ - MonFdset *mon_fdset; - MonFdset *mon_fdset_next; - - QEMU_LOCK_GUARD(&mon_fdsets_lock); - QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) { - monitor_fdset_cleanup(mon_fdset); - } -} - -AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, bool has_opaque, - const char *opaque, Error **errp) -{ - int fd; - Monitor *mon = monitor_cur(); - AddfdInfo *fdinfo; - - fd = qemu_chr_fe_get_msgfd(&mon->chr); - if (fd == -1) { - error_setg(errp, "No file descriptor supplied via SCM_RIGHTS"); - goto error; - } - - fdinfo = monitor_fdset_add_fd(fd, has_fdset_id, fdset_id, - has_opaque, opaque, errp); - if (fdinfo) { - return fdinfo; - } - -error: - if (fd != -1) { - close(fd); - } - return NULL; -} - -void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp) -{ - MonFdset *mon_fdset; - MonFdsetFd *mon_fdset_fd; - char fd_str[60]; - - QEMU_LOCK_GUARD(&mon_fdsets_lock); - QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { - if (mon_fdset->id != fdset_id) { - continue; - } - QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { - if (has_fd) { - if (mon_fdset_fd->fd != fd) { - continue; - } - mon_fdset_fd->removed = true; - break; - } else { - mon_fdset_fd->removed = true; - } - } - if (has_fd && !mon_fdset_fd) { - goto error; - } - monitor_fdset_cleanup(mon_fdset); - return; - } - -error: - if (has_fd) { - snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64 ", fd:%" PRId64, - fdset_id, fd); - } else { - snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64, fdset_id); - } - error_setg(errp, "File descriptor named '%s' not found", fd_str); -} - -FdsetInfoList *qmp_query_fdsets(Error **errp) -{ - MonFdset *mon_fdset; - MonFdsetFd *mon_fdset_fd; - FdsetInfoList *fdset_list = NULL; - - QEMU_LOCK_GUARD(&mon_fdsets_lock); - QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { - FdsetInfo *fdset_info = g_malloc0(sizeof(*fdset_info)); - - fdset_info->fdset_id = mon_fdset->id; - - QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { - FdsetFdInfo *fdsetfd_info; - - fdsetfd_info = g_malloc0(sizeof(*fdsetfd_info)); - fdsetfd_info->fd = mon_fdset_fd->fd; - if (mon_fdset_fd->opaque) { - fdsetfd_info->has_opaque = true; - fdsetfd_info->opaque = g_strdup(mon_fdset_fd->opaque); - } else { - fdsetfd_info->has_opaque = false; - } - - QAPI_LIST_PREPEND(fdset_info->fds, fdsetfd_info); - } - - QAPI_LIST_PREPEND(fdset_list, fdset_info); - } - - return fdset_list; -} - -AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, - bool has_opaque, const char *opaque, - Error **errp) -{ - MonFdset *mon_fdset = NULL; - MonFdsetFd *mon_fdset_fd; - AddfdInfo *fdinfo; - - QEMU_LOCK_GUARD(&mon_fdsets_lock); - if (has_fdset_id) { - QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { - /* Break if match found or match impossible due to ordering by ID */ - if (fdset_id <= mon_fdset->id) { - if (fdset_id < mon_fdset->id) { - mon_fdset = NULL; - } - break; - } - } - } - - if (mon_fdset == NULL) { - int64_t fdset_id_prev = -1; - MonFdset *mon_fdset_cur = QLIST_FIRST(&mon_fdsets); - - if (has_fdset_id) { - if (fdset_id < 0) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id", - "a non-negative value"); - return NULL; - } - /* Use specified fdset ID */ - QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { - mon_fdset_cur = mon_fdset; - if (fdset_id < mon_fdset_cur->id) { - break; - } - } - } else { - /* Use first available fdset ID */ - QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { - mon_fdset_cur = mon_fdset; - if (fdset_id_prev == mon_fdset_cur->id - 1) { - fdset_id_prev = mon_fdset_cur->id; - continue; - } - break; - } - } - - mon_fdset = g_malloc0(sizeof(*mon_fdset)); - if (has_fdset_id) { - mon_fdset->id = fdset_id; - } else { - mon_fdset->id = fdset_id_prev + 1; - } - - /* The fdset list is ordered by fdset ID */ - if (!mon_fdset_cur) { - QLIST_INSERT_HEAD(&mon_fdsets, mon_fdset, next); - } else if (mon_fdset->id < mon_fdset_cur->id) { - QLIST_INSERT_BEFORE(mon_fdset_cur, mon_fdset, next); - } else { - QLIST_INSERT_AFTER(mon_fdset_cur, mon_fdset, next); - } - } - - mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd)); - mon_fdset_fd->fd = fd; - mon_fdset_fd->removed = false; - if (has_opaque) { - mon_fdset_fd->opaque = g_strdup(opaque); - } - QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next); - - fdinfo = g_malloc0(sizeof(*fdinfo)); - fdinfo->fdset_id = mon_fdset->id; - fdinfo->fd = mon_fdset_fd->fd; - - return fdinfo; -} - -int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) -{ -#ifdef _WIN32 - return -ENOENT; -#else - MonFdset *mon_fdset; - - QEMU_LOCK_GUARD(&mon_fdsets_lock); - QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { - MonFdsetFd *mon_fdset_fd; - MonFdsetFd *mon_fdset_fd_dup; - int fd = -1; - int dup_fd; - int mon_fd_flags; - - if (mon_fdset->id != fdset_id) { - continue; - } - - QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { - mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL); - if (mon_fd_flags == -1) { - return -1; - } - - if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) { - fd = mon_fdset_fd->fd; - break; - } - } - - if (fd == -1) { - errno = EACCES; - return -1; - } - - dup_fd = qemu_dup_flags(fd, flags); - if (dup_fd == -1) { - return -1; - } - - mon_fdset_fd_dup = g_malloc0(sizeof(*mon_fdset_fd_dup)); - mon_fdset_fd_dup->fd = dup_fd; - QLIST_INSERT_HEAD(&mon_fdset->dup_fds, mon_fdset_fd_dup, next); - return dup_fd; - } - - errno = ENOENT; - return -1; -#endif -} - -static int64_t monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove) -{ - MonFdset *mon_fdset; - MonFdsetFd *mon_fdset_fd_dup; - - QEMU_LOCK_GUARD(&mon_fdsets_lock); - QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { - QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) { - if (mon_fdset_fd_dup->fd == dup_fd) { - if (remove) { - QLIST_REMOVE(mon_fdset_fd_dup, next); - g_free(mon_fdset_fd_dup); - if (QLIST_EMPTY(&mon_fdset->dup_fds)) { - monitor_fdset_cleanup(mon_fdset); - } - return -1; - } else { - return mon_fdset->id; - } - } - } - } - - return -1; -} - -int64_t monitor_fdset_dup_fd_find(int dup_fd) -{ - return monitor_fdset_dup_fd_find_remove(dup_fd, false); -} - -void monitor_fdset_dup_fd_remove(int dup_fd) -{ - monitor_fdset_dup_fd_find_remove(dup_fd, true); -} - -int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp) -{ - int fd; - Error *local_err = NULL; - - if (!qemu_isdigit(fdname[0]) && mon) { - fd = monitor_get_fd(mon, fdname, &local_err); - } else { - fd = qemu_parse_fd(fdname); - if (fd == -1) { - error_setg(&local_err, "Invalid file descriptor number '%s'", - fdname); - } - } - if (local_err) { - error_propagate(errp, local_err); - assert(fd == -1); - } else { - assert(fd != -1); - } - - return fd; -} - -/* Please update hmp-commands.hx when adding or changing commands */ -static HMPCommand hmp_info_cmds[] = { -#include "hmp-commands-info.h" - { NULL, NULL, }, -}; - -/* hmp_cmds and hmp_info_cmds would be sorted at runtime */ -HMPCommand hmp_cmds[] = { -#include "hmp-commands.h" - { NULL, NULL, }, -}; - -/* - * Set @pval to the value in the register identified by @name. - * return 0 if OK, -1 if not found - */ -int get_monitor_def(Monitor *mon, int64_t *pval, const char *name) -{ - const MonitorDef *md = target_monitor_defs(); - CPUState *cs = mon_get_cpu(mon); - void *ptr; - uint64_t tmp = 0; - int ret; - - if (cs == NULL || md == NULL) { - return -1; - } - - for(; md->name != NULL; md++) { - if (hmp_compare_cmd(name, md->name)) { - if (md->get_value) { - *pval = md->get_value(mon, md, md->offset); - } else { - CPUArchState *env = mon_get_cpu_env(mon); - ptr = (uint8_t *)env + md->offset; - switch(md->type) { - case MD_I32: - *pval = *(int32_t *)ptr; - break; - case MD_TLONG: - *pval = *(target_long *)ptr; - break; - default: - *pval = 0; - break; - } - } - return 0; - } - } - - ret = target_get_monitor_def(cs, name, &tmp); - if (!ret) { - *pval = (target_long) tmp; - } - - return ret; -} - -static void add_completion_option(ReadLineState *rs, const char *str, - const char *option) -{ - if (!str || !option) { - return; - } - if (!strncmp(option, str, strlen(str))) { - readline_add_completion(rs, option); - } -} - -void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str) -{ - size_t len; - ChardevBackendInfoList *list, *start; - - if (nb_args != 2) { - return; - } - len = strlen(str); - readline_set_completion_index(rs, len); - - start = list = qmp_query_chardev_backends(NULL); - while (list) { - const char *chr_name = list->value->name; - - if (!strncmp(chr_name, str, len)) { - readline_add_completion(rs, chr_name); - } - list = list->next; - } - qapi_free_ChardevBackendInfoList(start); -} - -void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str) -{ - size_t len; - int i; - - if (nb_args != 2) { - return; - } - len = strlen(str); - readline_set_completion_index(rs, len); - for (i = 0; i < NET_CLIENT_DRIVER__MAX; i++) { - add_completion_option(rs, str, NetClientDriver_str(i)); - } -} - -void device_add_completion(ReadLineState *rs, int nb_args, const char *str) -{ - GSList *list, *elt; - size_t len; - - if (nb_args != 2) { - return; - } - - len = strlen(str); - readline_set_completion_index(rs, len); - list = elt = object_class_get_list(TYPE_DEVICE, false); - while (elt) { - const char *name; - DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, elt->data, - TYPE_DEVICE); - name = object_class_get_name(OBJECT_CLASS(dc)); - - if (dc->user_creatable - && !strncmp(name, str, len)) { - readline_add_completion(rs, name); - } - elt = elt->next; - } - g_slist_free(list); -} - -void object_add_completion(ReadLineState *rs, int nb_args, const char *str) -{ - GSList *list, *elt; - size_t len; - - if (nb_args != 2) { - return; - } - - len = strlen(str); - readline_set_completion_index(rs, len); - list = elt = object_class_get_list(TYPE_USER_CREATABLE, false); - while (elt) { - const char *name; - - name = object_class_get_name(OBJECT_CLASS(elt->data)); - if (!strncmp(name, str, len) && strcmp(name, TYPE_USER_CREATABLE)) { - readline_add_completion(rs, name); - } - elt = elt->next; - } - g_slist_free(list); -} - -static int qdev_add_hotpluggable_device(Object *obj, void *opaque) -{ - GSList **list = opaque; - DeviceState *dev = (DeviceState *)object_dynamic_cast(obj, TYPE_DEVICE); - - if (dev == NULL) { - return 0; - } - - if (dev->realized && object_property_get_bool(obj, "hotpluggable", NULL)) { - *list = g_slist_append(*list, dev); - } - - return 0; -} - -static GSList *qdev_build_hotpluggable_device_list(Object *peripheral) -{ - GSList *list = NULL; - - object_child_foreach(peripheral, qdev_add_hotpluggable_device, &list); - - return list; -} - -static void peripheral_device_del_completion(ReadLineState *rs, - const char *str, size_t len) -{ - Object *peripheral = container_get(qdev_get_machine(), "/peripheral"); - GSList *list, *item; - - list = qdev_build_hotpluggable_device_list(peripheral); - if (!list) { - return; - } - - for (item = list; item; item = g_slist_next(item)) { - DeviceState *dev = item->data; - - if (dev->id && !strncmp(str, dev->id, len)) { - readline_add_completion(rs, dev->id); - } - } - - g_slist_free(list); -} - -void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str) -{ - size_t len; - ChardevInfoList *list, *start; - - if (nb_args != 2) { - return; - } - len = strlen(str); - readline_set_completion_index(rs, len); - - start = list = qmp_query_chardev(NULL); - while (list) { - ChardevInfo *chr = list->value; - - if (!strncmp(chr->label, str, len)) { - readline_add_completion(rs, chr->label); - } - list = list->next; - } - qapi_free_ChardevInfoList(start); -} - -static void ringbuf_completion(ReadLineState *rs, const char *str) -{ - size_t len; - ChardevInfoList *list, *start; - - len = strlen(str); - readline_set_completion_index(rs, len); - - start = list = qmp_query_chardev(NULL); - while (list) { - ChardevInfo *chr_info = list->value; - - if (!strncmp(chr_info->label, str, len)) { - Chardev *chr = qemu_chr_find(chr_info->label); - if (chr && CHARDEV_IS_RINGBUF(chr)) { - readline_add_completion(rs, chr_info->label); - } - } - list = list->next; - } - qapi_free_ChardevInfoList(start); -} - -void ringbuf_write_completion(ReadLineState *rs, int nb_args, const char *str) -{ - if (nb_args != 2) { - return; - } - ringbuf_completion(rs, str); -} - -void device_del_completion(ReadLineState *rs, int nb_args, const char *str) -{ - size_t len; - - if (nb_args != 2) { - return; - } - - len = strlen(str); - readline_set_completion_index(rs, len); - peripheral_device_del_completion(rs, str, len); -} - -void object_del_completion(ReadLineState *rs, int nb_args, const char *str) -{ - ObjectPropertyInfoList *list, *start; - size_t len; - - if (nb_args != 2) { - return; - } - len = strlen(str); - readline_set_completion_index(rs, len); - - start = list = qmp_qom_list("/objects", NULL); - while (list) { - ObjectPropertyInfo *info = list->value; - - if (!strncmp(info->type, "child<", 5) - && !strncmp(info->name, str, len)) { - readline_add_completion(rs, info->name); - } - list = list->next; - } - qapi_free_ObjectPropertyInfoList(start); -} - -void sendkey_completion(ReadLineState *rs, int nb_args, const char *str) -{ - int i; - char *sep; - size_t len; - - if (nb_args != 2) { - return; - } - sep = strrchr(str, '-'); - if (sep) { - str = sep + 1; - } - len = strlen(str); - readline_set_completion_index(rs, len); - for (i = 0; i < Q_KEY_CODE__MAX; i++) { - if (!strncmp(str, QKeyCode_str(i), len)) { - readline_add_completion(rs, QKeyCode_str(i)); - } - } -} - -void set_link_completion(ReadLineState *rs, int nb_args, const char *str) -{ - size_t len; - - len = strlen(str); - readline_set_completion_index(rs, len); - if (nb_args == 2) { - NetClientState *ncs[MAX_QUEUE_NUM]; - int count, i; - count = qemu_find_net_clients_except(NULL, ncs, - NET_CLIENT_DRIVER_NONE, - MAX_QUEUE_NUM); - for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { - const char *name = ncs[i]->name; - if (!strncmp(str, name, len)) { - readline_add_completion(rs, name); - } - } - } else if (nb_args == 3) { - add_completion_option(rs, str, "on"); - add_completion_option(rs, str, "off"); - } -} - -void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str) -{ - int len, count, i; - NetClientState *ncs[MAX_QUEUE_NUM]; - - if (nb_args != 2) { - return; - } - - len = strlen(str); - readline_set_completion_index(rs, len); - count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_DRIVER_NIC, - MAX_QUEUE_NUM); - for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { - const char *name = ncs[i]->name; - if (strncmp(str, name, len)) { - continue; - } - if (ncs[i]->is_netdev) { - readline_add_completion(rs, name); - } - } -} - -void info_trace_events_completion(ReadLineState *rs, int nb_args, const char *str) -{ - size_t len; - - len = strlen(str); - readline_set_completion_index(rs, len); - if (nb_args == 2) { - TraceEventIter iter; - TraceEvent *ev; - char *pattern = g_strdup_printf("%s*", str); - trace_event_iter_init_pattern(&iter, pattern); - while ((ev = trace_event_iter_next(&iter)) != NULL) { - readline_add_completion(rs, trace_event_get_name(ev)); - } - g_free(pattern); - } -} - -void trace_event_completion(ReadLineState *rs, int nb_args, const char *str) -{ - size_t len; - - len = strlen(str); - readline_set_completion_index(rs, len); - if (nb_args == 2) { - TraceEventIter iter; - TraceEvent *ev; - char *pattern = g_strdup_printf("%s*", str); - trace_event_iter_init_pattern(&iter, pattern); - while ((ev = trace_event_iter_next(&iter)) != NULL) { - readline_add_completion(rs, trace_event_get_name(ev)); - } - g_free(pattern); - } else if (nb_args == 3) { - add_completion_option(rs, str, "on"); - add_completion_option(rs, str, "off"); - } -} - -void watchdog_action_completion(ReadLineState *rs, int nb_args, const char *str) -{ - int i; - - if (nb_args != 2) { - return; - } - readline_set_completion_index(rs, strlen(str)); - for (i = 0; i < WATCHDOG_ACTION__MAX; i++) { - add_completion_option(rs, str, WatchdogAction_str(i)); - } -} - -void migrate_set_capability_completion(ReadLineState *rs, int nb_args, - const char *str) -{ - size_t len; - - len = strlen(str); - readline_set_completion_index(rs, len); - if (nb_args == 2) { - int i; - for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { - const char *name = MigrationCapability_str(i); - if (!strncmp(str, name, len)) { - readline_add_completion(rs, name); - } - } - } else if (nb_args == 3) { - add_completion_option(rs, str, "on"); - add_completion_option(rs, str, "off"); - } -} - -void migrate_set_parameter_completion(ReadLineState *rs, int nb_args, - const char *str) -{ - size_t len; - - len = strlen(str); - readline_set_completion_index(rs, len); - if (nb_args == 2) { - int i; - for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) { - const char *name = MigrationParameter_str(i); - if (!strncmp(str, name, len)) { - readline_add_completion(rs, name); - } - } - } -} - -static void vm_completion(ReadLineState *rs, const char *str) -{ - size_t len; - BlockDriverState *bs; - BdrvNextIterator it; - - len = strlen(str); - readline_set_completion_index(rs, len); - - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { - SnapshotInfoList *snapshots, *snapshot; - AioContext *ctx = bdrv_get_aio_context(bs); - bool ok = false; - - aio_context_acquire(ctx); - if (bdrv_can_snapshot(bs)) { - ok = bdrv_query_snapshot_info_list(bs, &snapshots, NULL) == 0; - } - aio_context_release(ctx); - if (!ok) { - continue; - } - - snapshot = snapshots; - while (snapshot) { - char *completion = snapshot->value->name; - if (!strncmp(str, completion, len)) { - readline_add_completion(rs, completion); - } - completion = snapshot->value->id; - if (!strncmp(str, completion, len)) { - readline_add_completion(rs, completion); - } - snapshot = snapshot->next; - } - qapi_free_SnapshotInfoList(snapshots); - } - -} - -void delvm_completion(ReadLineState *rs, int nb_args, const char *str) -{ - if (nb_args == 2) { - vm_completion(rs, str); - } -} - -void loadvm_completion(ReadLineState *rs, int nb_args, const char *str) -{ - if (nb_args == 2) { - vm_completion(rs, str); - } -} - -static int -compare_mon_cmd(const void *a, const void *b) -{ - return strcmp(((const HMPCommand *)a)->name, - ((const HMPCommand *)b)->name); -} - -static void sortcmdlist(void) -{ - qsort(hmp_cmds, ARRAY_SIZE(hmp_cmds) - 1, - sizeof(*hmp_cmds), - compare_mon_cmd); - qsort(hmp_info_cmds, ARRAY_SIZE(hmp_info_cmds) - 1, - sizeof(*hmp_info_cmds), - compare_mon_cmd); -} - -void monitor_register_hmp(const char *name, bool info, - void (*cmd)(Monitor *mon, const QDict *qdict)) -{ - HMPCommand *table = info ? hmp_info_cmds : hmp_cmds; - - while (table->name != NULL) { - if (strcmp(table->name, name) == 0) { - g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL); - table->cmd = cmd; - return; - } - table++; - } - g_assert_not_reached(); -} - -void monitor_register_hmp_info_hrt(const char *name, - HumanReadableText *(*handler)(Error **errp)) -{ - HMPCommand *table = hmp_info_cmds; - - while (table->name != NULL) { - if (strcmp(table->name, name) == 0) { - g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL); - table->cmd_info_hrt = handler; - return; - } - table++; - } - g_assert_not_reached(); -} - -void monitor_init_globals(void) -{ - monitor_init_globals_core(); - monitor_init_qmp_commands(); - sortcmdlist(); - qemu_mutex_init(&mon_fdsets_lock); -} diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index caa2e90ef2..252de85681 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -94,7 +94,6 @@ typedef struct HMPCommand { struct Monitor { CharBackend chr; - int reset_seen; int suspend_cnt; /* Needs to be accessed atomically */ bool is_qmp; bool skip_flush; @@ -115,8 +114,8 @@ struct Monitor { QLIST_HEAD(, mon_fd_t) fds; GString *outbuf; guint out_watch; - /* Read under either BQL or mon_lock, written with BQL+mon_lock. */ int mux_out; + int reset_seen; }; struct MonitorHMP { @@ -166,7 +165,6 @@ typedef QTAILQ_HEAD(MonitorList, Monitor) MonitorList; extern IOThread *mon_iothread; extern Coroutine *qmp_dispatcher_co; extern bool qmp_dispatcher_co_shutdown; -extern bool qmp_dispatcher_co_busy; extern QmpCommandList qmp_commands, qmp_cap_negotiation_commands; extern QemuMutex monitor_lock; extern MonitorList mon_list; @@ -174,7 +172,6 @@ extern int mon_refcount; extern HMPCommand hmp_cmds[]; -int monitor_puts(Monitor *mon, const char *str); void monitor_data_init(Monitor *mon, bool is_qmp, bool skip_flush, bool use_io_thread); void monitor_data_destroy(Monitor *mon); @@ -185,9 +182,9 @@ void monitor_fdsets_cleanup(void); void qmp_send_response(MonitorQMP *mon, const QDict *rsp); void monitor_data_destroy_qmp(MonitorQMP *mon); void coroutine_fn monitor_qmp_dispatcher_co(void *data); +void qmp_dispatcher_co_wake(void); int get_monitor_def(Monitor *mon, int64_t *pval, const char *name); -void help_cmd(Monitor *mon, const char *name); void handle_hmp_command(MonitorHMP *mon, const char *cmdline); int hmp_compare_cmd(const char *name, const char *list); diff --git a/monitor/monitor.c b/monitor/monitor.c index 86949024f6..01ede1babd 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -56,29 +56,11 @@ IOThread *mon_iothread; /* Coroutine to dispatch the requests received from I/O thread */ Coroutine *qmp_dispatcher_co; -/* Set to true when the dispatcher coroutine should terminate */ -bool qmp_dispatcher_co_shutdown; - /* - * qmp_dispatcher_co_busy is used for synchronisation between the - * monitor thread and the main thread to ensure that the dispatcher - * coroutine never gets scheduled a second time when it's already - * scheduled (scheduling the same coroutine twice is forbidden). - * - * It is true if the coroutine is active and processing requests. - * Additional requests may then be pushed onto mon->qmp_requests, - * and @qmp_dispatcher_co_shutdown may be set without further ado. - * @qmp_dispatcher_co_busy must not be woken up in this case. - * - * If false, you also have to set @qmp_dispatcher_co_busy to true and - * wake up @qmp_dispatcher_co after pushing the new requests. - * - * The coroutine will automatically change this variable back to false - * before it yields. Nobody else may set the variable to false. - * - * Access must be atomic for thread safety. + * Set to true when the dispatcher coroutine should terminate. Protected + * by monitor_lock. */ -bool qmp_dispatcher_co_busy; +bool qmp_dispatcher_co_shutdown; /* * Protects mon_list, monitor_qapi_event_state, coroutine_mon, @@ -154,22 +136,19 @@ static inline bool monitor_is_hmp_non_interactive(const Monitor *mon) return !monitor_uses_readline(container_of(mon, MonitorHMP, common)); } -static void monitor_flush_locked(Monitor *mon); - static gboolean monitor_unblocked(void *do_not_use, GIOCondition cond, void *opaque) { Monitor *mon = opaque; - qemu_mutex_lock(&mon->mon_lock); + QEMU_LOCK_GUARD(&mon->mon_lock); mon->out_watch = 0; monitor_flush_locked(mon); - qemu_mutex_unlock(&mon->mon_lock); - return FALSE; + return G_SOURCE_REMOVE; } /* Caller must hold mon->mon_lock */ -static void monitor_flush_locked(Monitor *mon) +void monitor_flush_locked(Monitor *mon) { int rc; size_t len; @@ -203,18 +182,16 @@ static void monitor_flush_locked(Monitor *mon) void monitor_flush(Monitor *mon) { - qemu_mutex_lock(&mon->mon_lock); + QEMU_LOCK_GUARD(&mon->mon_lock); monitor_flush_locked(mon); - qemu_mutex_unlock(&mon->mon_lock); } /* flush at every end of line */ -int monitor_puts(Monitor *mon, const char *str) +int monitor_puts_locked(Monitor *mon, const char *str) { int i; char c; - qemu_mutex_lock(&mon->mon_lock); for (i = 0; str[i]; i++) { c = str[i]; if (c == '\n') { @@ -225,11 +202,16 @@ int monitor_puts(Monitor *mon, const char *str) monitor_flush_locked(mon); } } - qemu_mutex_unlock(&mon->mon_lock); return i; } +int monitor_puts(Monitor *mon, const char *str) +{ + QEMU_LOCK_GUARD(&mon->mon_lock); + return monitor_puts_locked(mon, str); +} + int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { char *buf; @@ -260,6 +242,33 @@ int monitor_printf(Monitor *mon, const char *fmt, ...) return ret; } +void monitor_printc(Monitor *mon, int c) +{ + monitor_printf(mon, "'"); + switch(c) { + case '\'': + monitor_printf(mon, "\\'"); + break; + case '\\': + monitor_printf(mon, "\\\\"); + break; + case '\n': + monitor_printf(mon, "\\n"); + break; + case '\r': + monitor_printf(mon, "\\r"); + break; + default: + if (c >= 32 && c <= 126) { + monitor_printf(mon, "%c", c); + } else { + monitor_printf(mon, "\\x%02x", c); + } + break; + } + monitor_printf(mon, "'"); +} + /* * Print to current monitor if we have one, else to stderr. */ @@ -306,6 +315,7 @@ static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = { [QAPI_EVENT_QUORUM_FAILURE] = { 1000 * SCALE_MS }, [QAPI_EVENT_VSERPORT_CHANGE] = { 1000 * SCALE_MS }, [QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE] = { 1000 * SCALE_MS }, + [QAPI_EVENT_HV_BALLOON_STATUS_REPORT] = { 1000 * SCALE_MS }, }; /* @@ -542,6 +552,17 @@ static void monitor_accept_input(void *opaque) { Monitor *mon = opaque; + qemu_mutex_lock(&mon->mon_lock); + if (!monitor_is_qmp(mon) && mon->reset_seen) { + MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common); + assert(hmp_mon->rs); + readline_restart(hmp_mon->rs); + qemu_mutex_unlock(&mon->mon_lock); + readline_show_prompt(hmp_mon->rs); + } else { + qemu_mutex_unlock(&mon->mon_lock); + } + qemu_chr_fe_accept_input(&mon->chr); } @@ -560,12 +581,6 @@ void monitor_resume(Monitor *mon) ctx = qemu_get_aio_context(); } - if (!monitor_is_qmp(mon)) { - MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common); - assert(hmp_mon->rs); - readline_show_prompt(hmp_mon->rs); - } - aio_bh_schedule_oneshot(ctx, monitor_accept_input, mon); } @@ -576,7 +591,7 @@ int monitor_can_read(void *opaque) { Monitor *mon = opaque; - return !qatomic_mb_read(&mon->suspend_cnt); + return !qatomic_read(&mon->suspend_cnt); } void monitor_list_append(Monitor *mon) @@ -639,7 +654,7 @@ void monitor_cleanup(void) * We need to poll both qemu_aio_context and iohandler_ctx to make * sure that the dispatcher coroutine keeps making progress and * eventually terminates. qemu_aio_context is automatically - * polled by calling AIO_WAIT_WHILE on it, but we must poll + * polled by calling AIO_WAIT_WHILE_UNLOCKED on it, but we must poll * iohandler_ctx manually. * * Letting the iothread continue while shutting down the dispatcher @@ -647,14 +662,14 @@ void monitor_cleanup(void) * we'll just leave them in the queue without sending a response * and monitor_data_destroy() will free them. */ - qmp_dispatcher_co_shutdown = true; - if (!qatomic_xchg(&qmp_dispatcher_co_busy, true)) { - aio_co_wake(qmp_dispatcher_co); + WITH_QEMU_LOCK_GUARD(&monitor_lock) { + qmp_dispatcher_co_shutdown = true; } + qmp_dispatcher_co_wake(); - AIO_WAIT_WHILE(qemu_get_aio_context(), + AIO_WAIT_WHILE_UNLOCKED(NULL, (aio_poll(iohandler_get_aio_context(), false), - qatomic_mb_read(&qmp_dispatcher_co_busy))); + qatomic_read(&qmp_dispatcher_co))); /* * We need to explicitly stop the I/O thread (but not destroy it), @@ -693,7 +708,7 @@ static void monitor_qapi_event_init(void) qapi_event_throttle_equal); } -void monitor_init_globals_core(void) +void monitor_init_globals(void) { monitor_qapi_event_init(); qemu_mutex_init(&monitor_lock); @@ -705,14 +720,13 @@ void monitor_init_globals_core(void) * rid of those assumptions. */ qmp_dispatcher_co = qemu_coroutine_create(monitor_qmp_dispatcher_co, NULL); - qatomic_mb_set(&qmp_dispatcher_co_busy, true); aio_co_schedule(iohandler_get_aio_context(), qmp_dispatcher_co); } int monitor_init(MonitorOptions *opts, bool allow_hmp, Error **errp) { + ERRP_GUARD(); Chardev *chr; - Error *local_err = NULL; chr = qemu_chr_find(opts->chardev); if (chr == NULL) { @@ -726,7 +740,7 @@ int monitor_init(MonitorOptions *opts, bool allow_hmp, Error **errp) switch (opts->mode) { case MONITOR_MODE_CONTROL: - monitor_init_qmp(chr, opts->pretty, &local_err); + monitor_init_qmp(chr, opts->pretty, errp); break; case MONITOR_MODE_READLINE: if (!allow_hmp) { @@ -737,17 +751,13 @@ int monitor_init(MonitorOptions *opts, bool allow_hmp, Error **errp) error_setg(errp, "'pretty' is not compatible with HMP monitors"); return -1; } - monitor_init_hmp(chr, true, &local_err); + monitor_init_hmp(chr, true, errp); break; default: g_assert_not_reached(); } - if (local_err) { - error_propagate(errp, local_err); - return -1; - } - return 0; + return *errp ? -1 : 0; } int monitor_init_opts(QemuOpts *opts, Error **errp) diff --git a/monitor/qmp-cmds-control.c b/monitor/qmp-cmds-control.c index 6e581713a3..f21506efa5 100644 --- a/monitor/qmp-cmds-control.c +++ b/monitor/qmp-cmds-control.c @@ -30,7 +30,6 @@ #include "qapi/error.h" #include "qapi/qapi-commands-control.h" #include "qapi/qapi-commands-introspect.h" -#include "qapi/qapi-emit-events.h" #include "qapi/qapi-introspect.h" #include "qapi/qapi-visit-introspect.h" #include "qapi/qobject-input-visitor.h" diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 1ebb89f46c..b0f948d337 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -14,33 +14,22 @@ */ #include "qemu/osdep.h" -#include "qemu/cutils.h" -#include "qemu/option.h" -#include "monitor/monitor.h" +#include "qemu/sockets.h" +#include "monitor-internal.h" +#include "monitor/qdev.h" +#include "monitor/qmp-helpers.h" #include "sysemu/sysemu.h" -#include "qemu/config-file.h" -#include "qemu/uuid.h" -#include "chardev/char.h" -#include "ui/qemu-spice.h" -#include "ui/console.h" -#include "ui/dbus-display.h" #include "sysemu/kvm.h" #include "sysemu/runstate.h" #include "sysemu/runstate-action.h" -#include "sysemu/blockdev.h" #include "sysemu/block-backend.h" #include "qapi/error.h" -#include "qapi/qapi-commands-acpi.h" -#include "qapi/qapi-commands-block.h" +#include "qapi/qapi-init-commands.h" #include "qapi/qapi-commands-control.h" -#include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" -#include "qapi/qapi-commands-ui.h" -#include "qapi/type-helpers.h" #include "qapi/qmp/qerror.h" -#include "exec/ramlist.h" +#include "qapi/type-helpers.h" #include "hw/mem/memory-device.h" -#include "hw/acpi/acpi_dev_interface.h" #include "hw/intc/intc.h" #include "hw/rdma/rdma.h" @@ -48,29 +37,7 @@ NameInfo *qmp_query_name(Error **errp) { NameInfo *info = g_malloc0(sizeof(*info)); - if (qemu_name) { - info->has_name = true; - info->name = g_strdup(qemu_name); - } - - return info; -} - -KvmInfo *qmp_query_kvm(Error **errp) -{ - KvmInfo *info = g_malloc0(sizeof(*info)); - - info->enabled = kvm_enabled(); - info->present = accel_find("kvm"); - - return info; -} - -UuidInfo *qmp_query_uuid(Error **errp) -{ - UuidInfo *info = g_malloc0(sizeof(*info)); - - info->UUID = qemu_uuid_unparse_strdup(&qemu_uuid); + info->name = g_strdup(qemu_name); return info; } @@ -96,16 +63,6 @@ void qmp_stop(Error **errp) } } -void qmp_system_reset(Error **errp) -{ - qemu_system_reset_request(SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET); -} - -void qmp_system_powerdown(Error **errp) -{ - qemu_system_powerdown_request(); -} - void qmp_cont(Error **errp) { BlockBackend *blk; @@ -133,8 +90,11 @@ void qmp_cont(Error **errp) blk_iostatus_reset(blk); } - for (job = block_job_next(NULL); job; job = block_job_next(job)) { - block_job_iostatus_reset(job); + WITH_JOB_LOCK_GUARD() { + for (job = block_job_next_locked(NULL); job; + job = block_job_next_locked(job)) { + block_job_iostatus_reset_locked(job); + } } /* Continuing after completed migration. Images have been inactivated to @@ -156,288 +116,96 @@ void qmp_cont(Error **errp) } } -void qmp_system_wakeup(Error **errp) -{ - if (!qemu_wakeup_suspend_enabled()) { - error_setg(errp, - "wake-up from suspend is not supported by this guest"); - return; - } - - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, errp); -} - -void qmp_set_password(SetPasswordOptions *opts, Error **errp) -{ - int rc; - - if (opts->protocol == DISPLAY_PROTOCOL_SPICE) { - if (!qemu_using_spice(errp)) { - return; - } - rc = qemu_spice.set_passwd(opts->password, - opts->connected == SET_PASSWORD_ACTION_FAIL, - opts->connected == SET_PASSWORD_ACTION_DISCONNECT); - } else { - assert(opts->protocol == DISPLAY_PROTOCOL_VNC); - if (opts->connected != SET_PASSWORD_ACTION_KEEP) { - /* vnc supports "connected=keep" only */ - error_setg(errp, QERR_INVALID_PARAMETER, "connected"); - return; - } - /* Note that setting an empty password will not disable login through - * this interface. */ - rc = vnc_display_password(opts->u.vnc.display, opts->password); - } - - if (rc != 0) { - error_setg(errp, "Could not set password"); - } -} - -void qmp_expire_password(ExpirePasswordOptions *opts, Error **errp) -{ - time_t when; - int rc; - const char *whenstr = opts->time; - - if (strcmp(whenstr, "now") == 0) { - when = 0; - } else if (strcmp(whenstr, "never") == 0) { - when = TIME_MAX; - } else if (whenstr[0] == '+') { - when = time(NULL) + strtoull(whenstr+1, NULL, 10); - } else { - when = strtoull(whenstr, NULL, 10); - } - - if (opts->protocol == DISPLAY_PROTOCOL_SPICE) { - if (!qemu_using_spice(errp)) { - return; - } - rc = qemu_spice.set_pw_expire(when); - } else { - assert(opts->protocol == DISPLAY_PROTOCOL_VNC); - rc = vnc_display_pw_expire(opts->u.vnc.display, when); - } - - if (rc != 0) { - error_setg(errp, "Could not set password expire time"); - } -} - -#ifdef CONFIG_VNC -void qmp_change_vnc_password(const char *password, Error **errp) -{ - if (vnc_display_password(NULL, password) < 0) { - error_setg(errp, "Could not set password"); - } -} -#endif - void qmp_add_client(const char *protocol, const char *fdname, bool has_skipauth, bool skipauth, bool has_tls, bool tls, Error **errp) { - Chardev *s; - int fd; + static const struct { + const char *name; + bool (*add_client)(int fd, bool has_skipauth, bool skipauth, + bool has_tls, bool tls, Error **errp); + } protocol_table[] = { + { "spice", qmp_add_client_spice }, +#ifdef CONFIG_VNC + { "vnc", qmp_add_client_vnc }, +#endif +#ifdef CONFIG_DBUS_DISPLAY + { "@dbus-display", qmp_add_client_dbus_display }, +#endif + }; + int fd, i; fd = monitor_get_fd(monitor_cur(), fdname, errp); if (fd < 0) { return; } - if (strcmp(protocol, "spice") == 0) { - if (!qemu_using_spice(errp)) { - close(fd); - return; - } - skipauth = has_skipauth ? skipauth : false; - tls = has_tls ? tls : false; - if (qemu_spice.display_add_client(fd, skipauth, tls) < 0) { - error_setg(errp, "spice failed to add client"); - close(fd); - } - return; -#ifdef CONFIG_VNC - } else if (strcmp(protocol, "vnc") == 0) { - skipauth = has_skipauth ? skipauth : false; - vnc_display_add_client(NULL, fd, skipauth); - return; -#endif -#ifdef CONFIG_DBUS_DISPLAY - } else if (strcmp(protocol, "@dbus-display") == 0) { - if (!qemu_using_dbus_display(errp)) { - close(fd); - return; - } - if (!qemu_dbus_display.add_client(fd, errp)) { - close(fd); - return; - } - return; -#endif - } else if ((s = qemu_chr_find(protocol)) != NULL) { - if (qemu_chr_add_client(s, fd) < 0) { - error_setg(errp, "failed to add client"); - close(fd); - return; - } + if (!fd_is_socket(fd)) { + error_setg(errp, "parameter @fdname must name a socket"); + close(fd); return; } - error_setg(errp, "protocol '%s' is invalid", protocol); - close(fd); -} - - -MemoryDeviceInfoList *qmp_query_memory_devices(Error **errp) -{ - return qmp_memory_device_list(); -} - -ACPIOSTInfoList *qmp_query_acpi_ospm_status(Error **errp) -{ - bool ambig; - ACPIOSTInfoList *head = NULL; - ACPIOSTInfoList **prev = &head; - Object *obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, &ambig); - - if (obj) { - AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj); - AcpiDeviceIf *adev = ACPI_DEVICE_IF(obj); - - adevc->ospm_status(adev, &prev); - } else { - error_setg(errp, "command is not supported, missing ACPI device"); - } - - return head; -} - -MemoryInfo *qmp_query_memory_size_summary(Error **errp) -{ - MemoryInfo *mem_info = g_new0(MemoryInfo, 1); - MachineState *ms = MACHINE(qdev_get_machine()); - - mem_info->base_memory = ms->ram_size; - - mem_info->plugged_memory = get_plugged_memory_size(); - mem_info->has_plugged_memory = - mem_info->plugged_memory != (uint64_t)-1; - - return mem_info; -} - -void qmp_display_reload(DisplayReloadOptions *arg, Error **errp) -{ - switch (arg->type) { - case DISPLAY_RELOAD_TYPE_VNC: -#ifdef CONFIG_VNC - if (arg->u.vnc.has_tls_certs && arg->u.vnc.tls_certs) { - vnc_display_reload_certs(NULL, errp); - } -#else - error_setg(errp, "vnc is invalid, missing 'CONFIG_VNC'"); -#endif - break; - default: - abort(); - } -} - -void qmp_display_update(DisplayUpdateOptions *arg, Error **errp) -{ - switch (arg->type) { - case DISPLAY_UPDATE_TYPE_VNC: -#ifdef CONFIG_VNC - vnc_display_update(&arg->u.vnc, errp); -#else - error_setg(errp, "vnc is invalid, missing 'CONFIG_VNC'"); -#endif - break; - default: - abort(); - } -} - -static int qmp_x_query_rdma_foreach(Object *obj, void *opaque) -{ - RdmaProvider *rdma; - RdmaProviderClass *k; - GString *buf = opaque; - - if (object_dynamic_cast(obj, INTERFACE_RDMA_PROVIDER)) { - rdma = RDMA_PROVIDER(obj); - k = RDMA_PROVIDER_GET_CLASS(obj); - if (k->format_statistics) { - k->format_statistics(rdma, buf); - } else { - g_string_append_printf(buf, - "RDMA statistics not available for %s.\n", - object_get_typename(obj)); - } - } - - return 0; -} - -HumanReadableText *qmp_x_query_rdma(Error **errp) -{ - g_autoptr(GString) buf = g_string_new(""); - - object_child_foreach_recursive(object_get_root(), - qmp_x_query_rdma_foreach, buf); - - return human_readable_text_from_str(buf); -} - -HumanReadableText *qmp_x_query_ramblock(Error **errp) -{ - g_autoptr(GString) buf = ram_block_format(); - - return human_readable_text_from_str(buf); -} - -static int qmp_x_query_irq_foreach(Object *obj, void *opaque) -{ - InterruptStatsProvider *intc; - InterruptStatsProviderClass *k; - GString *buf = opaque; - - if (object_dynamic_cast(obj, TYPE_INTERRUPT_STATS_PROVIDER)) { - intc = INTERRUPT_STATS_PROVIDER(obj); - k = INTERRUPT_STATS_PROVIDER_GET_CLASS(obj); - uint64_t *irq_counts; - unsigned int nb_irqs, i; - if (k->get_statistics && - k->get_statistics(intc, &irq_counts, &nb_irqs)) { - if (nb_irqs > 0) { - g_string_append_printf(buf, "IRQ statistics for %s:\n", - object_get_typename(obj)); - for (i = 0; i < nb_irqs; i++) { - if (irq_counts[i] > 0) { - g_string_append_printf(buf, "%2d: %" PRId64 "\n", i, - irq_counts[i]); - } - } + for (i = 0; i < ARRAY_SIZE(protocol_table); i++) { + if (!strcmp(protocol, protocol_table[i].name)) { + if (!protocol_table[i].add_client(fd, has_skipauth, skipauth, + has_tls, tls, errp)) { + close(fd); } - } else { - g_string_append_printf(buf, - "IRQ statistics not available for %s.\n", - object_get_typename(obj)); + return; } } - return 0; + if (!qmp_add_client_char(fd, has_skipauth, skipauth, has_tls, tls, + protocol, errp)) { + close(fd); + } } -HumanReadableText *qmp_x_query_irq(Error **errp) +char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, + int64_t cpu_index, Error **errp) { - g_autoptr(GString) buf = g_string_new(""); + char *output = NULL; + MonitorHMP hmp = {}; - object_child_foreach_recursive(object_get_root(), - qmp_x_query_irq_foreach, buf); + monitor_data_init(&hmp.common, false, true, false); - return human_readable_text_from_str(buf); + if (has_cpu_index) { + int ret = monitor_set_cpu(&hmp.common, cpu_index); + if (ret < 0) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", + "a CPU number"); + goto out; + } + } + + handle_hmp_command(&hmp, command_line); + + WITH_QEMU_LOCK_GUARD(&hmp.common.mon_lock) { + output = g_strdup(hmp.common.outbuf->str); + } + +out: + monitor_data_destroy(&hmp.common); + return output; +} + +static void __attribute__((__constructor__)) monitor_init_qmp_commands(void) +{ + /* + * Two command lists: + * - qmp_commands contains all QMP commands + * - qmp_cap_negotiation_commands contains just + * "qmp_capabilities", to enforce capability negotiation + */ + + qmp_init_marshal(&qmp_commands); + + qmp_register_command(&qmp_commands, "device_add", + qmp_device_add, 0, 0); + + QTAILQ_INIT(&qmp_cap_negotiation_commands); + qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", + qmp_marshal_qmp_capabilities, + QCO_ALLOW_PRECONFIG, 0); } diff --git a/monitor/qmp.c b/monitor/qmp.c index 092c527b6f..a239945e8d 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -33,6 +33,30 @@ #include "qapi/qmp/qlist.h" #include "trace.h" +/* + * qmp_dispatcher_co_busy is used for synchronisation between the + * monitor thread and the main thread to ensure that the dispatcher + * coroutine never gets scheduled a second time when it's already + * scheduled (scheduling the same coroutine twice is forbidden). + * + * It is true if the coroutine will process at least one more request + * before going to sleep. Either it has been kicked already, or it + * is active and processing requests. Additional requests may therefore + * be pushed onto mon->qmp_requests, and @qmp_dispatcher_co_shutdown may + * be set without further ado. @qmp_dispatcher_co must not be woken up + * in this case. + * + * If false, you have to wake up @qmp_dispatcher_co after pushing new + * requests. You also have to set @qmp_dispatcher_co_busy to true + * before waking up the coroutine. + * + * The coroutine will automatically change this variable back to false + * before it yields. Nobody else may set the variable to false. + * + * Access must be atomic for thread safety. + */ +static bool qmp_dispatcher_co_busy = true; + struct QMPRequest { /* Owner of the request */ MonitorQMP *mon; @@ -178,8 +202,6 @@ static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void) Monitor *mon; MonitorQMP *qmp_mon; - QEMU_LOCK_GUARD(&monitor_lock); - QTAILQ_FOREACH(mon, &mon_list, entry) { if (!monitor_is_qmp(mon)) { continue; @@ -207,53 +229,56 @@ static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void) return req_obj; } -void coroutine_fn monitor_qmp_dispatcher_co(void *data) +static QMPRequest *monitor_qmp_dispatcher_pop_any(void) { - QMPRequest *req_obj = NULL; - QDict *rsp; - bool oob_enabled; - MonitorQMP *mon; - while (true) { - assert(qatomic_mb_read(&qmp_dispatcher_co_busy) == true); + /* + * To avoid double scheduling, busy is true on entry to + * monitor_qmp_dispatcher_co(), and must be set again before + * aio_co_wake()-ing it. + */ + assert(qatomic_read(&qmp_dispatcher_co_busy) == true); /* * Mark the dispatcher as not busy already here so that we * don't miss any new requests coming in the middle of our * processing. + * + * Clear qmp_dispatcher_co_busy before reading request. */ - qatomic_mb_set(&qmp_dispatcher_co_busy, false); + qatomic_set_mb(&qmp_dispatcher_co_busy, false); - /* On shutdown, don't take any more requests from the queue */ - if (qmp_dispatcher_co_shutdown) { - return; - } + WITH_QEMU_LOCK_GUARD(&monitor_lock) { + QMPRequest *req_obj; - while (!(req_obj = monitor_qmp_requests_pop_any_with_lock())) { - /* - * No more requests to process. Wait to be reentered from - * handle_qmp_command() when it pushes more requests, or - * from monitor_cleanup() when it requests shutdown. - */ - if (!qmp_dispatcher_co_shutdown) { - qemu_coroutine_yield(); - - /* - * busy must be set to true again by whoever - * rescheduled us to avoid double scheduling - */ - assert(qatomic_xchg(&qmp_dispatcher_co_busy, false) == true); - } - - /* - * qmp_dispatcher_co_shutdown may have changed if we - * yielded and were reentered from monitor_cleanup() - */ + /* On shutdown, don't take any more requests from the queue */ if (qmp_dispatcher_co_shutdown) { - return; + return NULL; + } + + req_obj = monitor_qmp_requests_pop_any_with_lock(); + if (req_obj) { + return req_obj; } } + /* + * No more requests to process. Wait to be reentered from + * handle_qmp_command() when it pushes more requests, or + * from monitor_cleanup() when it requests shutdown. + */ + qemu_coroutine_yield(); + } +} + +void coroutine_fn monitor_qmp_dispatcher_co(void *data) +{ + QMPRequest *req_obj; + QDict *rsp; + bool oob_enabled; + MonitorQMP *mon; + + while ((req_obj = monitor_qmp_dispatcher_pop_any()) != NULL) { trace_monitor_qmp_in_band_dequeue(req_obj, req_obj->mon->qmp_requests->length); @@ -296,14 +321,6 @@ void coroutine_fn monitor_qmp_dispatcher_co(void *data) qemu_coroutine_yield(); } - /* - * Move the coroutine from iohandler_ctx to qemu_aio_context for - * executing the command handler so that it can make progress if it - * involves an AIO_WAIT_WHILE(). - */ - aio_co_schedule(qemu_get_aio_context(), qmp_dispatcher_co); - qemu_coroutine_yield(); - /* Process request */ if (req_obj->req) { if (trace_event_get_state(TRACE_MONITOR_QMP_CMD_IN_BAND)) { @@ -330,15 +347,17 @@ void coroutine_fn monitor_qmp_dispatcher_co(void *data) } qmp_request_free(req_obj); + } + qatomic_set(&qmp_dispatcher_co, NULL); +} - /* - * Yield and reschedule so the main loop stays responsive. - * - * Move back to iohandler_ctx so that nested event loops for - * qemu_aio_context don't start new monitor commands. - */ - aio_co_schedule(iohandler_get_aio_context(), qmp_dispatcher_co); - qemu_coroutine_yield(); +void qmp_dispatcher_co_wake(void) +{ + /* Write request before reading qmp_dispatcher_co_busy. */ + smp_mb__before_rmw(); + + if (!qatomic_xchg(&qmp_dispatcher_co_busy, true)) { + aio_co_wake(qmp_dispatcher_co); } } @@ -403,9 +422,7 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err) } /* Kick the dispatcher routine */ - if (!qatomic_xchg(&qmp_dispatcher_co_busy, true)) { - aio_co_wake(qmp_dispatcher_co); - } + qmp_dispatcher_co_wake(); } static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size) diff --git a/nbd/client-connection.c b/nbd/client-connection.c index 2a632931c3..f9da67c87e 100644 --- a/nbd/client-connection.c +++ b/nbd/client-connection.c @@ -1,5 +1,5 @@ /* - * QEMU Block driver for NBD + * QEMU Block driver for NBD * * Copyright (c) 2021 Virtuozzo International GmbH. * @@ -23,11 +23,13 @@ */ #include "qemu/osdep.h" +#include "trace.h" #include "block/nbd.h" #include "qapi/qapi-visit-sockets.h" #include "qapi/clone-visitor.h" +#include "qemu/coroutine.h" struct NBDClientConnection { /* Initialization constants, never change */ @@ -91,7 +93,7 @@ NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr, .do_negotiation = do_negotiation, .initial_info.request_sizes = true, - .initial_info.structured_reply = true, + .initial_info.mode = NBD_MODE_EXTENDED, .initial_info.base_allocation = true, .initial_info.x_dirty_bitmap = g_strdup(x_dirty_bitmap), .initial_info.name = g_strdup(export_name ?: "") @@ -144,8 +146,7 @@ static int nbd_connect(QIOChannelSocket *sioc, SocketAddress *addr, return 0; } - ret = nbd_receive_negotiate(NULL, QIO_CHANNEL(sioc), tlscreds, - tlshostname, + ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), tlscreds, tlshostname, outioc, info, errp); if (ret < 0) { /* @@ -154,7 +155,7 @@ static int nbd_connect(QIOChannelSocket *sioc, SocketAddress *addr, * channel. */ if (outioc && *outioc) { - qio_channel_close(QIO_CHANNEL(*outioc), NULL); + qio_channel_close(*outioc, NULL); object_unref(OBJECT(*outioc)); *outioc = NULL; } else { @@ -195,7 +196,7 @@ static void *connect_thread_func(void *opaque) * conn->updated_info will finally be returned to the user. Clear the * pointers to our internally allocated strings, which are IN parameters * of nbd_receive_negotiate() and therefore nbd_connect(). Caller - * shoudn't be interested in these fields. + * shouldn't be interested in these fields. */ conn->updated_info.x_dirty_bitmap = NULL; conn->updated_info.name = NULL; @@ -210,6 +211,7 @@ static void *connect_thread_func(void *opaque) object_unref(OBJECT(conn->sioc)); conn->sioc = NULL; if (conn->do_retry && !conn->detached) { + trace_nbd_connect_thread_sleep(timeout); qemu_mutex_unlock(&conn->mutex); sleep(timeout); diff --git a/nbd/client.c b/nbd/client.c index 30d5383cb1..29ffc609a4 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2019 Red Hat, Inc. + * Copyright Red Hat * Copyright (C) 2005 Anthony Liguori * * Network Block Device Client Side @@ -650,19 +650,20 @@ static int nbd_send_meta_query(QIOChannel *ioc, uint32_t opt, Error **errp) { int ret; - uint32_t export_len = strlen(export); + uint32_t export_len; uint32_t queries = !!query; uint32_t query_len = 0; uint32_t data_len; char *data; char *p; + assert(strnlen(export, NBD_MAX_STRING_SIZE + 1) <= NBD_MAX_STRING_SIZE); + export_len = strlen(export); data_len = sizeof(export_len) + export_len + sizeof(queries); - assert(export_len <= NBD_MAX_STRING_SIZE); if (query) { + assert(strnlen(query, NBD_MAX_STRING_SIZE + 1) <= NBD_MAX_STRING_SIZE); query_len = strlen(query); data_len += sizeof(query_len) + query_len; - assert(query_len <= NBD_MAX_STRING_SIZE); } else { assert(opt == NBD_OPT_LIST_META_CONTEXT); } @@ -874,15 +875,11 @@ static int nbd_list_meta_contexts(QIOChannel *ioc, * Start the handshake to the server. After a positive return, the server * is ready to accept additional NBD_OPT requests. * Returns: negative errno: failure talking to server - * 0: server is oldstyle, must call nbd_negotiate_finish_oldstyle - * 1: server is newstyle, but can only accept EXPORT_NAME - * 2: server is newstyle, but lacks structured replies - * 3: server is newstyle and set up for structured replies + * non-negative: enum NBDMode describing server abilities */ -static int nbd_start_negotiate(AioContext *aio_context, QIOChannel *ioc, - QCryptoTLSCreds *tlscreds, +static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, const char *hostname, QIOChannel **outioc, - bool structured_reply, bool *zeroes, + NBDMode max_mode, bool *zeroes, Error **errp) { ERRP_GUARD(); @@ -948,10 +945,6 @@ static int nbd_start_negotiate(AioContext *aio_context, QIOChannel *ioc, return -EINVAL; } ioc = *outioc; - if (aio_context) { - qio_channel_set_blocking(ioc, false, NULL); - qio_channel_attach_aio_context(ioc, aio_context); - } } else { error_setg(errp, "Server does not support STARTTLS"); return -EINVAL; @@ -960,24 +953,32 @@ static int nbd_start_negotiate(AioContext *aio_context, QIOChannel *ioc, if (fixedNewStyle) { int result = 0; - if (structured_reply) { + if (max_mode >= NBD_MODE_EXTENDED) { + result = nbd_request_simple_option(ioc, + NBD_OPT_EXTENDED_HEADERS, + false, errp); + if (result) { + return result < 0 ? -EINVAL : NBD_MODE_EXTENDED; + } + } + if (max_mode >= NBD_MODE_STRUCTURED) { result = nbd_request_simple_option(ioc, NBD_OPT_STRUCTURED_REPLY, false, errp); - if (result < 0) { - return -EINVAL; + if (result) { + return result < 0 ? -EINVAL : NBD_MODE_STRUCTURED; } } - return 2 + result; + return NBD_MODE_SIMPLE; } else { - return 1; + return NBD_MODE_EXPORT_NAME; } } else if (magic == NBD_CLIENT_MAGIC) { if (tlscreds) { error_setg(errp, "Server does not support STARTTLS"); return -EINVAL; } - return 0; + return NBD_MODE_OLDSTYLE; } else { error_setg(errp, "Bad server magic received: 0x%" PRIx64, magic); return -EINVAL; @@ -1016,8 +1017,7 @@ static int nbd_negotiate_finish_oldstyle(QIOChannel *ioc, NBDExportInfo *info, * Returns: negative errno: failure talking to server * 0: server is connected */ -int nbd_receive_negotiate(AioContext *aio_context, QIOChannel *ioc, - QCryptoTLSCreds *tlscreds, +int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, const char *hostname, QIOChannel **outioc, NBDExportInfo *info, Error **errp) { @@ -1029,18 +1029,21 @@ int nbd_receive_negotiate(AioContext *aio_context, QIOChannel *ioc, assert(info->name && strlen(info->name) <= NBD_MAX_STRING_SIZE); trace_nbd_receive_negotiate_name(info->name); - result = nbd_start_negotiate(aio_context, ioc, tlscreds, hostname, outioc, - info->structured_reply, &zeroes, errp); + result = nbd_start_negotiate(ioc, tlscreds, hostname, outioc, + info->mode, &zeroes, errp); + if (result < 0) { + return result; + } - info->structured_reply = false; + info->mode = result; info->base_allocation = false; if (tlscreds && *outioc) { ioc = *outioc; } - switch (result) { - case 3: /* newstyle, with structured replies */ - info->structured_reply = true; + switch (info->mode) { + case NBD_MODE_EXTENDED: + case NBD_MODE_STRUCTURED: if (base_allocation) { result = nbd_negotiate_simple_meta_context(ioc, info, errp); if (result < 0) { @@ -1049,7 +1052,7 @@ int nbd_receive_negotiate(AioContext *aio_context, QIOChannel *ioc, info->base_allocation = result == 1; } /* fall through */ - case 2: /* newstyle, try OPT_GO */ + case NBD_MODE_SIMPLE: /* Try NBD_OPT_GO first - if it works, we are done (it * also gives us a good message if the server requires * TLS). If it is not available, fall back to @@ -1072,7 +1075,7 @@ int nbd_receive_negotiate(AioContext *aio_context, QIOChannel *ioc, return -EINVAL; } /* fall through */ - case 1: /* newstyle, but limited to EXPORT_NAME */ + case NBD_MODE_EXPORT_NAME: /* write the export name request */ if (nbd_send_option_request(ioc, NBD_OPT_EXPORT_NAME, -1, info->name, errp) < 0) { @@ -1088,7 +1091,7 @@ int nbd_receive_negotiate(AioContext *aio_context, QIOChannel *ioc, return -EINVAL; } break; - case 0: /* oldstyle, parse length and flags */ + case NBD_MODE_OLDSTYLE: if (*info->name) { error_setg(errp, "Server does not support non-empty export names"); return -EINVAL; @@ -1098,7 +1101,7 @@ int nbd_receive_negotiate(AioContext *aio_context, QIOChannel *ioc, } break; default: - return result; + g_assert_not_reached(); } trace_nbd_receive_negotiate_size_flags(info->size, info->flags); @@ -1149,15 +1152,19 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, QIOChannel *sioc = NULL; *info = NULL; - result = nbd_start_negotiate(NULL, ioc, tlscreds, hostname, &sioc, true, - NULL, errp); + result = nbd_start_negotiate(ioc, tlscreds, hostname, &sioc, + NBD_MODE_EXTENDED, NULL, errp); if (tlscreds && sioc) { ioc = sioc; } + if (result < 0) { + goto out; + } - switch (result) { - case 2: - case 3: + switch ((NBDMode)result) { + case NBD_MODE_SIMPLE: + case NBD_MODE_STRUCTURED: + case NBD_MODE_EXTENDED: /* newstyle - use NBD_OPT_LIST to populate array, then try * NBD_OPT_INFO on each array member. If structured replies * are enabled, also try NBD_OPT_LIST_META_CONTEXT. */ @@ -1178,7 +1185,7 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, memset(&array[count - 1], 0, sizeof(*array)); array[count - 1].name = name; array[count - 1].description = desc; - array[count - 1].structured_reply = result == 3; + array[count - 1].mode = result; } for (i = 0; i < count; i++) { @@ -1194,7 +1201,7 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, break; } - if (result == 3 && + if (result >= NBD_MODE_STRUCTURED && nbd_list_meta_contexts(ioc, &array[i], errp) < 0) { goto out; } @@ -1203,13 +1210,15 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, /* Send NBD_OPT_ABORT as a courtesy before hanging up */ nbd_send_opt_abort(ioc); break; - case 1: /* newstyle, but limited to EXPORT_NAME */ + case NBD_MODE_EXPORT_NAME: error_setg(errp, "Server does not support export lists"); /* We can't even send NBD_OPT_ABORT, so merely hang up */ goto out; - case 0: /* oldstyle, parse length and flags */ + case NBD_MODE_OLDSTYLE: + /* Lone export name is implied, but we can parse length and flags */ array = g_new0(NBDExportInfo, 1); array->name = g_strdup(""); + array->mode = NBD_MODE_OLDSTYLE; count = 1; if (nbd_negotiate_finish_oldstyle(ioc, array, errp) < 0) { @@ -1219,13 +1228,13 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, /* Send NBD_CMD_DISC as a courtesy to the server, but ignore all * errors now that we have the information we wanted. */ if (nbd_drop(ioc, 124, NULL) == 0) { - NBDRequest request = { .type = NBD_CMD_DISC }; + NBDRequest request = { .type = NBD_CMD_DISC, .mode = result }; nbd_send_request(ioc, &request); } break; default: - goto out; + g_assert_not_reached(); } *info = array; @@ -1347,20 +1356,29 @@ int nbd_disconnect(int fd) int nbd_send_request(QIOChannel *ioc, NBDRequest *request) { - uint8_t buf[NBD_REQUEST_SIZE]; + uint8_t buf[NBD_EXTENDED_REQUEST_SIZE]; + size_t len; - trace_nbd_send_request(request->from, request->len, request->handle, + trace_nbd_send_request(request->from, request->len, request->cookie, request->flags, request->type, nbd_cmd_lookup(request->type)); - stl_be_p(buf, NBD_REQUEST_MAGIC); stw_be_p(buf + 4, request->flags); stw_be_p(buf + 6, request->type); - stq_be_p(buf + 8, request->handle); + stq_be_p(buf + 8, request->cookie); stq_be_p(buf + 16, request->from); - stl_be_p(buf + 24, request->len); + if (request->mode >= NBD_MODE_EXTENDED) { + stl_be_p(buf, NBD_EXTENDED_REQUEST_MAGIC); + stq_be_p(buf + 24, request->len); + len = NBD_EXTENDED_REQUEST_SIZE; + } else { + assert(request->len <= UINT32_MAX); + stl_be_p(buf, NBD_REQUEST_MAGIC); + stl_be_p(buf + 24, request->len); + len = NBD_REQUEST_SIZE; + } - return nbd_write(ioc, buf, sizeof(buf), NULL); + return nbd_write(ioc, buf, len, NULL); } /* nbd_receive_simple_reply @@ -1382,35 +1400,62 @@ static int nbd_receive_simple_reply(QIOChannel *ioc, NBDSimpleReply *reply, } reply->error = be32_to_cpu(reply->error); - reply->handle = be64_to_cpu(reply->handle); + reply->cookie = be64_to_cpu(reply->cookie); return 0; } -/* nbd_receive_structured_reply_chunk +/* nbd_receive_reply_chunk_header * Read structured reply chunk except magic field (which should be already - * read). + * read). Normalize into the compact form. * Payload is not read. */ -static int nbd_receive_structured_reply_chunk(QIOChannel *ioc, - NBDStructuredReplyChunk *chunk, - Error **errp) +static int nbd_receive_reply_chunk_header(QIOChannel *ioc, NBDReply *chunk, + Error **errp) { int ret; + size_t len; + uint64_t payload_len; - assert(chunk->magic == NBD_STRUCTURED_REPLY_MAGIC); + if (chunk->magic == NBD_STRUCTURED_REPLY_MAGIC) { + len = sizeof(chunk->structured); + } else { + assert(chunk->magic == NBD_EXTENDED_REPLY_MAGIC); + len = sizeof(chunk->extended); + } ret = nbd_read(ioc, (uint8_t *)chunk + sizeof(chunk->magic), - sizeof(*chunk) - sizeof(chunk->magic), "structured chunk", + len - sizeof(chunk->magic), "structured chunk", errp); if (ret < 0) { return ret; } - chunk->flags = be16_to_cpu(chunk->flags); - chunk->type = be16_to_cpu(chunk->type); - chunk->handle = be64_to_cpu(chunk->handle); - chunk->length = be32_to_cpu(chunk->length); + /* flags, type, and cookie occupy same space between forms */ + chunk->structured.flags = be16_to_cpu(chunk->structured.flags); + chunk->structured.type = be16_to_cpu(chunk->structured.type); + chunk->structured.cookie = be64_to_cpu(chunk->structured.cookie); + + /* + * Because we use BLOCK_STATUS with REQ_ONE, and cap READ requests + * at 32M, no valid server should send us payload larger than + * this. Even if we stopped using REQ_ONE, sane servers will cap + * the number of extents they return for block status. + */ + if (chunk->magic == NBD_STRUCTURED_REPLY_MAGIC) { + payload_len = be32_to_cpu(chunk->structured.length); + } else { + /* For now, we are ignoring the extended header offset. */ + payload_len = be64_to_cpu(chunk->extended.length); + chunk->magic = NBD_STRUCTURED_REPLY_MAGIC; + } + if (payload_len > NBD_MAX_BUFFER_SIZE + sizeof(NBDStructuredReadData)) { + error_setg(errp, "server chunk %" PRIu32 " (%s) payload is too long", + chunk->structured.type, + nbd_rep_lookup(chunk->structured.type)); + return -EINVAL; + } + chunk->structured.length = payload_len; return 0; } @@ -1457,19 +1502,21 @@ nbd_read_eof(BlockDriverState *bs, QIOChannel *ioc, void *buffer, size_t size, /* nbd_receive_reply * - * Decreases bs->in_flight while waiting for a new reply. This yield is where - * we wait indefinitely and the coroutine must be able to be safely reentered - * for nbd_client_attach_aio_context(). + * Wait for a new reply. If this yields, the coroutine must be able to be + * safely reentered for nbd_client_attach_aio_context(). @mode determines + * which reply magic we are expecting, although this normalizes the result + * so that the caller only has to work with compact headers. * * Returns 1 on success - * 0 on eof, when no data was read (errp is not set) - * negative errno on failure (errp is set) + * 0 on eof, when no data was read + * negative errno on failure */ int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc, - NBDReply *reply, Error **errp) + NBDReply *reply, NBDMode mode, Error **errp) { int ret; const char *type; + uint32_t expected; ret = nbd_read_eof(bs, ioc, &reply->magic, sizeof(reply->magic), errp); if (ret <= 0) { @@ -1478,34 +1525,44 @@ int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc, reply->magic = be32_to_cpu(reply->magic); + /* Diagnose but accept wrong-width header */ switch (reply->magic) { case NBD_SIMPLE_REPLY_MAGIC: + if (mode >= NBD_MODE_EXTENDED) { + trace_nbd_receive_wrong_header(reply->magic, + nbd_mode_lookup(mode)); + } ret = nbd_receive_simple_reply(ioc, &reply->simple, errp); if (ret < 0) { - break; + return ret; } trace_nbd_receive_simple_reply(reply->simple.error, nbd_err_lookup(reply->simple.error), - reply->handle); + reply->cookie); break; case NBD_STRUCTURED_REPLY_MAGIC: - ret = nbd_receive_structured_reply_chunk(ioc, &reply->structured, errp); + case NBD_EXTENDED_REPLY_MAGIC: + expected = mode >= NBD_MODE_EXTENDED ? NBD_EXTENDED_REPLY_MAGIC + : NBD_STRUCTURED_REPLY_MAGIC; + if (reply->magic != expected) { + trace_nbd_receive_wrong_header(reply->magic, + nbd_mode_lookup(mode)); + } + ret = nbd_receive_reply_chunk_header(ioc, reply, errp); if (ret < 0) { - break; + return ret; } type = nbd_reply_type_lookup(reply->structured.type); - trace_nbd_receive_structured_reply_chunk(reply->structured.flags, - reply->structured.type, type, - reply->structured.handle, - reply->structured.length); + trace_nbd_receive_reply_chunk_header(reply->structured.flags, + reply->structured.type, type, + reply->structured.cookie, + reply->structured.length); break; default: + trace_nbd_receive_wrong_header(reply->magic, nbd_mode_lookup(mode)); error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", reply->magic); return -EINVAL; } - if (ret < 0) { - return ret; - } return 1; } diff --git a/nbd/common.c b/nbd/common.c index ddfe7d1183..3247c1d618 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -79,6 +79,8 @@ const char *nbd_opt_lookup(uint32_t opt) return "list meta context"; case NBD_OPT_SET_META_CONTEXT: return "set meta context"; + case NBD_OPT_EXTENDED_HEADERS: + return "extended headers"; default: return ""; } @@ -112,6 +114,10 @@ const char *nbd_rep_lookup(uint32_t rep) return "server shutting down"; case NBD_REP_ERR_BLOCK_SIZE_REQD: return "block size required"; + case NBD_REP_ERR_TOO_BIG: + return "option payload too big"; + case NBD_REP_ERR_EXT_HEADER_REQD: + return "extended headers required"; default: return ""; } @@ -170,7 +176,9 @@ const char *nbd_reply_type_lookup(uint16_t type) case NBD_REPLY_TYPE_OFFSET_HOLE: return "hole"; case NBD_REPLY_TYPE_BLOCK_STATUS: - return "block status"; + return "block status (32-bit)"; + case NBD_REPLY_TYPE_BLOCK_STATUS_EXT: + return "block status (64-bit)"; case NBD_REPLY_TYPE_ERROR: return "generic error"; case NBD_REPLY_TYPE_ERROR_OFFSET: @@ -248,3 +256,22 @@ int nbd_errno_to_system_errno(int err) } return ret; } + + +const char *nbd_mode_lookup(NBDMode mode) +{ + switch (mode) { + case NBD_MODE_OLDSTYLE: + return "oldstyle"; + case NBD_MODE_EXPORT_NAME: + return "export name only"; + case NBD_MODE_SIMPLE: + return "simple headers"; + case NBD_MODE_STRUCTURED: + return "structured replies"; + case NBD_MODE_EXTENDED: + return "extended headers"; + default: + return ""; + } +} diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h index 1b2141ab4b..dfa02f77ee 100644 --- a/nbd/nbd-internal.h +++ b/nbd/nbd-internal.h @@ -1,7 +1,7 @@ /* * NBD Internal Declarations * - * Copyright (C) 2016 Red Hat, Inc. + * Copyright Red Hat * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -13,7 +13,6 @@ #include "sysemu/block-backend.h" #include "io/channel-tls.h" -#include "qemu/coroutine.h" #include "qemu/iov.h" #ifndef _WIN32 @@ -35,8 +34,11 @@ * https://github.com/yoe/nbd/blob/master/doc/proto.md */ -/* Size of all NBD_OPT_*, without payload */ +/* Size of all compact NBD_CMD_*, without payload */ #define NBD_REQUEST_SIZE (4 + 2 + 2 + 8 + 8 + 4) +/* Size of all extended NBD_CMD_*, without payload */ +#define NBD_EXTENDED_REQUEST_SIZE (4 + 2 + 2 + 8 + 8 + 8) + /* Size of all NBD_REP_* sent in answer to most NBD_OPT_*, without payload */ #define NBD_REPLY_SIZE (4 + 4 + 8) /* Size of reply to NBD_OPT_EXPORT_NAME */ @@ -45,7 +47,6 @@ #define NBD_OLDSTYLE_NEGOTIATE_SIZE (8 + 8 + 8 + 4 + 124) #define NBD_INIT_MAGIC 0x4e42444d41474943LL /* ASCII "NBDMAGIC" */ -#define NBD_REQUEST_MAGIC 0x25609513 #define NBD_OPTS_MAGIC 0x49484156454F5054LL /* ASCII "IHAVEOPT" */ #define NBD_CLIENT_MAGIC 0x0000420281861253LL #define NBD_REP_MAGIC 0x0003e889045565a9LL diff --git a/nbd/server.c b/nbd/server.c index 213e00e761..c3484cc1eb 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2022 Red Hat, Inc. + * Copyright Red Hat * Copyright (C) 2005 Anthony Liguori * * Network Block Device Server Side @@ -19,7 +19,9 @@ #include "qemu/osdep.h" +#include "block/block_int.h" #include "block/export.h" +#include "block/dirty-bitmap.h" #include "qapi/error.h" #include "qemu/queue.h" #include "trace.h" @@ -103,11 +105,13 @@ struct NBDExport { static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); -/* NBDExportMetaContexts represents a list of contexts to be exported, +/* + * NBDMetaContexts represents a list of meta contexts in use, * as selected by NBD_OPT_SET_META_CONTEXT. Also used for - * NBD_OPT_LIST_META_CONTEXT. */ -typedef struct NBDExportMetaContexts { - NBDExport *exp; + * NBD_OPT_LIST_META_CONTEXT. + */ +struct NBDMetaContexts { + const NBDExport *exp; /* associated export */ size_t count; /* number of negotiated contexts */ bool base_allocation; /* export base:allocation context (block status) */ bool allocation_depth; /* export qemu:allocation-depth */ @@ -115,34 +119,36 @@ typedef struct NBDExportMetaContexts { * export qemu:dirty-bitmap:, * sized by exp->nr_export_bitmaps */ -} NBDExportMetaContexts; +}; struct NBDClient { - int refcount; + int refcount; /* atomic */ void (*close_fn)(NBDClient *client, bool negotiated); + QemuMutex lock; + NBDExport *exp; QCryptoTLSCreds *tlscreds; char *tlsauthz; QIOChannelSocket *sioc; /* The underlying data channel */ QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ - Coroutine *recv_coroutine; + Coroutine *recv_coroutine; /* protected by lock */ CoMutex send_lock; Coroutine *send_coroutine; - bool read_yielding; - bool quiescing; + bool read_yielding; /* protected by lock */ + bool quiescing; /* protected by lock */ QTAILQ_ENTRY(NBDClient) next; - int nb_requests; - bool closing; + int nb_requests; /* protected by lock */ + bool closing; /* protected by lock */ uint32_t check_align; /* If non-zero, check for aligned client requests */ - bool structured_reply; - NBDExportMetaContexts export_meta; + NBDMode mode; + NBDMetaContexts contexts; /* Negotiated meta contexts */ uint32_t opt; /* Current option being negotiated */ uint32_t optlen; /* remaining length of data in ioc for the option being @@ -453,10 +459,10 @@ static int nbd_negotiate_handle_list(NBDClient *client, Error **errp) return nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); } -static void nbd_check_meta_export(NBDClient *client) +static void nbd_check_meta_export(NBDClient *client, NBDExport *exp) { - if (client->exp != client->export_meta.exp) { - client->export_meta.count = 0; + if (exp != client->contexts.exp) { + client->contexts.count = 0; } } @@ -480,6 +486,10 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, bool no_zeroes, [10 .. 133] reserved (0) [unless no_zeroes] */ trace_nbd_negotiate_handle_export_name(); + if (client->mode >= NBD_MODE_EXTENDED) { + error_setg(errp, "Extended headers already negotiated"); + return -EINVAL; + } if (client->optlen > NBD_MAX_STRING_SIZE) { error_setg(errp, "Bad length received"); return -EINVAL; @@ -498,11 +508,15 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, bool no_zeroes, error_setg(errp, "export not found"); return -EINVAL; } + nbd_check_meta_export(client, client->exp); myflags = client->exp->nbdflags; - if (client->structured_reply) { + if (client->mode >= NBD_MODE_STRUCTURED) { myflags |= NBD_FLAG_SEND_DF; } + if (client->mode >= NBD_MODE_EXTENDED && client->contexts.count) { + myflags |= NBD_FLAG_BLOCK_STAT_PAYLOAD; + } trace_nbd_negotiate_new_style_size_flags(client->exp->size, myflags); stq_be_p(buf, client->exp->size); stw_be_p(buf + 8, myflags); @@ -515,7 +529,6 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, bool no_zeroes, QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); blk_exp_ref(&client->exp->common); - nbd_check_meta_export(client); return 0; } @@ -635,6 +648,9 @@ static int nbd_negotiate_handle_info(NBDClient *client, Error **errp) errp, "export '%s' not present", sane_name); } + if (client->opt == NBD_OPT_GO) { + nbd_check_meta_export(client, exp); + } /* Don't bother sending NBD_INFO_NAME unless client requested it */ if (sendname) { @@ -685,9 +701,13 @@ static int nbd_negotiate_handle_info(NBDClient *client, Error **errp) /* Send NBD_INFO_EXPORT always */ myflags = exp->nbdflags; - if (client->structured_reply) { + if (client->mode >= NBD_MODE_STRUCTURED) { myflags |= NBD_FLAG_SEND_DF; } + if (client->mode >= NBD_MODE_EXTENDED && + (client->contexts.count || client->opt == NBD_OPT_INFO)) { + myflags |= NBD_FLAG_BLOCK_STAT_PAYLOAD; + } trace_nbd_negotiate_new_style_size_flags(exp->size, myflags); stq_be_p(buf, exp->size); stw_be_p(buf + 8, myflags); @@ -723,7 +743,6 @@ static int nbd_negotiate_handle_info(NBDClient *client, Error **errp) client->check_align = check_align; QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); blk_exp_ref(&client->exp->common); - nbd_check_meta_export(client); rc = 1; } return rc; @@ -846,7 +865,7 @@ static bool nbd_strshift(const char **str, const char *prefix) * Handle queries to 'base' namespace. For now, only the base:allocation * context is available. Return true if @query has been handled. */ -static bool nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, +static bool nbd_meta_base_query(NBDClient *client, NBDMetaContexts *meta, const char *query) { if (!nbd_strshift(&query, "base:")) { @@ -866,7 +885,7 @@ static bool nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, * and qemu:allocation-depth contexts are available. Return true if @query * has been handled. */ -static bool nbd_meta_qemu_query(NBDClient *client, NBDExportMetaContexts *meta, +static bool nbd_meta_qemu_query(NBDClient *client, NBDMetaContexts *meta, const char *query) { size_t i; @@ -932,7 +951,7 @@ static bool nbd_meta_qemu_query(NBDClient *client, NBDExportMetaContexts *meta, * Return -errno on I/O error, 0 if option was completely handled by * sending a reply about inconsistent lengths, or 1 on success. */ static int nbd_negotiate_meta_query(NBDClient *client, - NBDExportMetaContexts *meta, Error **errp) + NBDMetaContexts *meta, Error **errp) { int ret; g_autofree char *query = NULL; @@ -971,19 +990,20 @@ static int nbd_negotiate_meta_query(NBDClient *client, * Handle NBD_OPT_LIST_META_CONTEXT and NBD_OPT_SET_META_CONTEXT * * Return -errno on I/O error, or 0 if option was completely handled. */ -static int nbd_negotiate_meta_queries(NBDClient *client, - NBDExportMetaContexts *meta, Error **errp) +static int nbd_negotiate_meta_queries(NBDClient *client, Error **errp) { int ret; g_autofree char *export_name = NULL; /* Mark unused to work around https://bugs.llvm.org/show_bug.cgi?id=3888 */ g_autofree G_GNUC_UNUSED bool *bitmaps = NULL; - NBDExportMetaContexts local_meta = {0}; + NBDMetaContexts local_meta = {0}; + NBDMetaContexts *meta; uint32_t nb_queries; size_t i; size_t count = 0; - if (client->opt == NBD_OPT_SET_META_CONTEXT && !client->structured_reply) { + if (client->opt == NBD_OPT_SET_META_CONTEXT && + client->mode < NBD_MODE_STRUCTURED) { return nbd_opt_invalid(client, errp, "request option '%s' when structured reply " "is not negotiated", @@ -993,6 +1013,8 @@ static int nbd_negotiate_meta_queries(NBDClient *client, if (client->opt == NBD_OPT_LIST_META_CONTEXT) { /* Only change the caller's meta on SET. */ meta = &local_meta; + } else { + meta = &client->contexts; } g_free(meta->bitmaps); @@ -1120,10 +1142,12 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) if (nbd_read32(client->ioc, &flags, "flags", errp) < 0) { return -EIO; } + client->mode = NBD_MODE_EXPORT_NAME; trace_nbd_negotiate_options_flags(flags); if (flags & NBD_FLAG_C_FIXED_NEWSTYLE) { fixedNewstyle = true; flags &= ~NBD_FLAG_C_FIXED_NEWSTYLE; + client->mode = NBD_MODE_SIMPLE; } if (flags & NBD_FLAG_C_NO_ZEROES) { no_zeroes = true; @@ -1160,7 +1184,7 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) client->optlen = length; if (length > NBD_MAX_BUFFER_SIZE) { - error_setg(errp, "len (%" PRIu32" ) is larger than max len (%u)", + error_setg(errp, "len (%" PRIu32 ") is larger than max len (%u)", length, NBD_MAX_BUFFER_SIZE); return -EINVAL; } @@ -1187,7 +1211,7 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) } ret = 0; object_unref(OBJECT(client->ioc)); - client->ioc = QIO_CHANNEL(tioc); + client->ioc = tioc; break; case NBD_OPT_EXPORT_NAME: @@ -1259,20 +1283,36 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) case NBD_OPT_STRUCTURED_REPLY: if (length) { ret = nbd_reject_length(client, false, errp); - } else if (client->structured_reply) { + } else if (client->mode >= NBD_MODE_EXTENDED) { + ret = nbd_negotiate_send_rep_err( + client, NBD_REP_ERR_EXT_HEADER_REQD, errp, + "extended headers already negotiated"); + } else if (client->mode >= NBD_MODE_STRUCTURED) { ret = nbd_negotiate_send_rep_err( client, NBD_REP_ERR_INVALID, errp, "structured reply already negotiated"); } else { ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); - client->structured_reply = true; + client->mode = NBD_MODE_STRUCTURED; } break; case NBD_OPT_LIST_META_CONTEXT: case NBD_OPT_SET_META_CONTEXT: - ret = nbd_negotiate_meta_queries(client, &client->export_meta, - errp); + ret = nbd_negotiate_meta_queries(client, errp); + break; + + case NBD_OPT_EXTENDED_HEADERS: + if (length) { + ret = nbd_reject_length(client, false, errp); + } else if (client->mode >= NBD_MODE_EXTENDED) { + ret = nbd_negotiate_send_rep_err( + client, NBD_REP_ERR_INVALID, errp, + "extended headers already negotiated"); + } else { + ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); + client->mode = NBD_MODE_EXTENDED; + } break; default: @@ -1331,6 +1371,7 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) */ qio_channel_set_blocking(client->ioc, false, NULL); + qio_channel_set_follow_coroutine_ctx(client->ioc, true); trace_nbd_negotiate_begin(); memcpy(buf, "NBDMAGIC", 8); @@ -1350,11 +1391,6 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) return ret; } - /* Attach the channel to the same AioContext as the export */ - if (client->exp && client->exp->common.ctx) { - qio_channel_attach_aio_context(client->ioc, client->exp->common.ctx); - } - assert(!client->optlen); trace_nbd_negotiate_success(); @@ -1381,11 +1417,18 @@ nbd_read_eof(NBDClient *client, void *buffer, size_t size, Error **errp) len = qio_channel_readv(client->ioc, &iov, 1, errp); if (len == QIO_CHANNEL_ERR_BLOCK) { - client->read_yielding = true; + WITH_QEMU_LOCK_GUARD(&client->lock) { + client->read_yielding = true; + + /* Prompt main loop thread to re-run nbd_drained_poll() */ + aio_wait_kick(); + } qio_channel_yield(client->ioc, G_IO_IN); - client->read_yielding = false; - if (client->quiescing) { - return -EAGAIN; + WITH_QEMU_LOCK_GUARD(&client->lock) { + client->read_yielding = false; + if (client->quiescing) { + return -EAGAIN; + } } continue; } else if (len < 0) { @@ -1407,14 +1450,16 @@ nbd_read_eof(NBDClient *client, void *buffer, size_t size, Error **errp) return 1; } -static int nbd_receive_request(NBDClient *client, NBDRequest *request, - Error **errp) +static int coroutine_fn nbd_receive_request(NBDClient *client, NBDRequest *request, + Error **errp) { - uint8_t buf[NBD_REQUEST_SIZE]; - uint32_t magic; + uint8_t buf[NBD_EXTENDED_REQUEST_SIZE]; + uint32_t magic, expect; int ret; + size_t size = client->mode >= NBD_MODE_EXTENDED ? + NBD_EXTENDED_REQUEST_SIZE : NBD_REQUEST_SIZE; - ret = nbd_read_eof(client, buf, sizeof(buf), errp); + ret = nbd_read_eof(client, buf, size, errp); if (ret < 0) { return ret; } @@ -1422,27 +1467,42 @@ static int nbd_receive_request(NBDClient *client, NBDRequest *request, return -EIO; } - /* Request - [ 0 .. 3] magic (NBD_REQUEST_MAGIC) - [ 4 .. 5] flags (NBD_CMD_FLAG_FUA, ...) - [ 6 .. 7] type (NBD_CMD_READ, ...) - [ 8 .. 15] handle - [16 .. 23] from - [24 .. 27] len + /* + * Compact request + * [ 0 .. 3] magic (NBD_REQUEST_MAGIC) + * [ 4 .. 5] flags (NBD_CMD_FLAG_FUA, ...) + * [ 6 .. 7] type (NBD_CMD_READ, ...) + * [ 8 .. 15] cookie + * [16 .. 23] from + * [24 .. 27] len + * Extended request + * [ 0 .. 3] magic (NBD_EXTENDED_REQUEST_MAGIC) + * [ 4 .. 5] flags (NBD_CMD_FLAG_FUA, NBD_CMD_FLAG_PAYLOAD_LEN, ...) + * [ 6 .. 7] type (NBD_CMD_READ, ...) + * [ 8 .. 15] cookie + * [16 .. 23] from + * [24 .. 31] len */ magic = ldl_be_p(buf); request->flags = lduw_be_p(buf + 4); request->type = lduw_be_p(buf + 6); - request->handle = ldq_be_p(buf + 8); + request->cookie = ldq_be_p(buf + 8); request->from = ldq_be_p(buf + 16); - request->len = ldl_be_p(buf + 24); + if (client->mode >= NBD_MODE_EXTENDED) { + request->len = ldq_be_p(buf + 24); + expect = NBD_EXTENDED_REQUEST_MAGIC; + } else { + request->len = (uint32_t)ldl_be_p(buf + 24); /* widen 32 to 64 bits */ + expect = NBD_REQUEST_MAGIC; + } trace_nbd_receive_request(magic, request->flags, request->type, request->from, request->len); - if (magic != NBD_REQUEST_MAGIC) { - error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", magic); + if (magic != expect) { + error_setg(errp, "invalid magic (got 0x%" PRIx32 ", expected 0x%" + PRIx32 ")", magic, expect); return -EINVAL; } return 0; @@ -1450,20 +1510,22 @@ static int nbd_receive_request(NBDClient *client, NBDRequest *request, #define MAX_NBD_REQUESTS 16 +/* Runs in export AioContext and main loop thread */ void nbd_client_get(NBDClient *client) { - client->refcount++; + qatomic_inc(&client->refcount); } void nbd_client_put(NBDClient *client) { - if (--client->refcount == 0) { + assert(qemu_in_main_thread()); + + if (qatomic_fetch_dec(&client->refcount) == 1) { /* The last reference should be dropped by client->close, * which is called by client_close. */ assert(client->closing); - qio_channel_detach_aio_context(client->ioc); object_unref(OBJECT(client->sioc)); object_unref(OBJECT(client->ioc)); if (client->tlscreds) { @@ -1474,18 +1536,48 @@ void nbd_client_put(NBDClient *client) QTAILQ_REMOVE(&client->exp->clients, client, next); blk_exp_unref(&client->exp->common); } - g_free(client->export_meta.bitmaps); + g_free(client->contexts.bitmaps); + qemu_mutex_destroy(&client->lock); g_free(client); } } +/* + * Tries to release the reference to @client, but only if other references + * remain. This is an optimization for the common case where we want to avoid + * the expense of scheduling nbd_client_put() in the main loop thread. + * + * Returns true upon success or false if the reference was not released because + * it is the last reference. + */ +static bool nbd_client_put_nonzero(NBDClient *client) +{ + int old = qatomic_read(&client->refcount); + int expected; + + do { + if (old == 1) { + return false; + } + + expected = old; + old = qatomic_cmpxchg(&client->refcount, expected, expected - 1); + } while (old != expected); + + return true; +} + static void client_close(NBDClient *client, bool negotiated) { - if (client->closing) { - return; - } + assert(qemu_in_main_thread()); - client->closing = true; + WITH_QEMU_LOCK_GUARD(&client->lock) { + if (client->closing) { + return; + } + + client->closing = true; + } /* Force requests to finish. They will drop their own references, * then we'll close the socket and free the NBDClient. @@ -1499,6 +1591,7 @@ static void client_close(NBDClient *client, bool negotiated) } } +/* Runs in export AioContext with client->lock held */ static NBDRequestData *nbd_request_get(NBDClient *client) { NBDRequestData *req; @@ -1507,11 +1600,11 @@ static NBDRequestData *nbd_request_get(NBDClient *client) client->nb_requests++; req = g_new0(NBDRequestData, 1); - nbd_client_get(client); req->client = client; return req; } +/* Runs in export AioContext with client->lock held */ static void nbd_request_put(NBDRequestData *req) { NBDClient *client = req->client; @@ -1528,8 +1621,6 @@ static void nbd_request_put(NBDRequestData *req) } nbd_client_receive_next_request(client); - - nbd_client_put(client); } static void blk_aio_attached(AioContext *ctx, void *opaque) @@ -1537,30 +1628,29 @@ static void blk_aio_attached(AioContext *ctx, void *opaque) NBDExport *exp = opaque; NBDClient *client; + assert(qemu_in_main_thread()); + trace_nbd_blk_aio_attached(exp->name, ctx); exp->common.ctx = ctx; QTAILQ_FOREACH(client, &exp->clients, next) { - qio_channel_attach_aio_context(client->ioc, ctx); - - assert(client->nb_requests == 0); - assert(client->recv_coroutine == NULL); - assert(client->send_coroutine == NULL); + WITH_QEMU_LOCK_GUARD(&client->lock) { + assert(client->nb_requests == 0); + assert(client->recv_coroutine == NULL); + assert(client->send_coroutine == NULL); + } } } static void blk_aio_detach(void *opaque) { NBDExport *exp = opaque; - NBDClient *client; + + assert(qemu_in_main_thread()); trace_nbd_blk_aio_detach(exp->name, exp->common.ctx); - QTAILQ_FOREACH(client, &exp->clients, next) { - qio_channel_detach_aio_context(client->ioc); - } - exp->common.ctx = NULL; } @@ -1569,8 +1659,12 @@ static void nbd_drained_begin(void *opaque) NBDExport *exp = opaque; NBDClient *client; + assert(qemu_in_main_thread()); + QTAILQ_FOREACH(client, &exp->clients, next) { - client->quiescing = true; + WITH_QEMU_LOCK_GUARD(&client->lock) { + client->quiescing = true; + } } } @@ -1579,29 +1673,48 @@ static void nbd_drained_end(void *opaque) NBDExport *exp = opaque; NBDClient *client; + assert(qemu_in_main_thread()); + QTAILQ_FOREACH(client, &exp->clients, next) { - client->quiescing = false; - nbd_client_receive_next_request(client); + WITH_QEMU_LOCK_GUARD(&client->lock) { + client->quiescing = false; + nbd_client_receive_next_request(client); + } } } +/* Runs in export AioContext */ +static void nbd_wake_read_bh(void *opaque) +{ + NBDClient *client = opaque; + qio_channel_wake_read(client->ioc); +} + static bool nbd_drained_poll(void *opaque) { NBDExport *exp = opaque; NBDClient *client; - QTAILQ_FOREACH(client, &exp->clients, next) { - if (client->nb_requests != 0) { - /* - * If there's a coroutine waiting for a request on nbd_read_eof() - * enter it here so we don't depend on the client to wake it up. - */ - if (client->recv_coroutine != NULL && client->read_yielding) { - qemu_aio_coroutine_enter(exp->common.ctx, - client->recv_coroutine); - } + assert(qemu_in_main_thread()); - return true; + QTAILQ_FOREACH(client, &exp->clients, next) { + WITH_QEMU_LOCK_GUARD(&client->lock) { + if (client->nb_requests != 0) { + /* + * If there's a coroutine waiting for a request on nbd_read_eof() + * enter it here so we don't depend on the client to wake it up. + * + * Schedule a BH in the export AioContext to avoid missing the + * wake up due to the race between qio_channel_wake_read() and + * qio_channel_yield(). + */ + if (client->recv_coroutine != NULL && client->read_yielding) { + aio_bh_schedule_oneshot(nbd_export_aio_context(client->exp), + nbd_wake_read_bh, client); + } + + return true; + } } } @@ -1612,6 +1725,8 @@ static void nbd_eject_notifier(Notifier *n, void *data) { NBDExport *exp = container_of(n, NBDExport, eject_notifier); + assert(qemu_in_main_thread()); + blk_exp_request_shutdown(&exp->common); } @@ -1638,6 +1753,7 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args, { NBDExport *exp = container_of(blk_exp, NBDExport, common); BlockExportOptionsNbd *arg = &exp_args->u.nbd; + const char *name = arg->name ?: exp_args->node_name; BlockBackend *blk = blk_exp->blk; int64_t size; uint64_t perm, shared_perm; @@ -1646,6 +1762,7 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args, size_t i; int ret; + GLOBAL_STATE_CODE(); assert(exp_args->type == BLOCK_EXPORT_TYPE_NBD); if (!nbd_server_is_running()) { @@ -1653,12 +1770,8 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args, return -EINVAL; } - if (!arg->has_name) { - arg->name = exp_args->node_name; - } - - if (strlen(arg->name) > NBD_MAX_STRING_SIZE) { - error_setg(errp, "export name '%s' too long", arg->name); + if (strlen(name) > NBD_MAX_STRING_SIZE) { + error_setg(errp, "export name '%s' too long", name); return -EINVAL; } @@ -1667,8 +1780,8 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args, return -EINVAL; } - if (nbd_export_find(arg->name)) { - error_setg(errp, "NBD server already has export named '%s'", arg->name); + if (nbd_export_find(name)) { + error_setg(errp, "NBD server already has export named '%s'", name); return -EEXIST; } @@ -1688,7 +1801,7 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args, } QTAILQ_INIT(&exp->clients); - exp->name = g_strdup(arg->name); + exp->name = g_strdup(name); exp->description = g_strdup(arg->description); exp->nbdflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA | NBD_FLAG_SEND_CACHE); @@ -1704,6 +1817,8 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args, } exp->size = QEMU_ALIGN_DOWN(size, BDRV_SECTOR_SIZE); + bdrv_graph_rdlock_main_loop(); + for (bitmaps = arg->bitmaps; bitmaps; bitmaps = bitmaps->next) { exp->nr_export_bitmaps++; } @@ -1786,9 +1901,12 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args, QTAILQ_INSERT_TAIL(&exports, exp, next); + bdrv_graph_rdunlock_main_loop(); + return 0; fail: + bdrv_graph_rdunlock_main_loop(); g_free(exp->export_bitmaps); g_free(exp->name); g_free(exp->description); @@ -1848,15 +1966,13 @@ static void nbd_export_delete(BlockExport *blk_exp) g_free(exp->description); exp->description = NULL; - if (exp->common.blk) { - if (exp->eject_notifier_blk) { - notifier_remove(&exp->eject_notifier); - blk_unref(exp->eject_notifier_blk); - } - blk_remove_aio_context_notifier(exp->common.blk, blk_aio_attached, - blk_aio_detach, exp); - blk_set_disable_request_queuing(exp->common.blk, false); + if (exp->eject_notifier_blk) { + notifier_remove(&exp->eject_notifier); + blk_unref(exp->eject_notifier_blk); } + blk_remove_aio_context_notifier(exp->common.blk, blk_aio_attached, + blk_aio_detach, exp); + blk_set_disable_request_queuing(exp->common.blk, false); for (i = 0; i < exp->nr_export_bitmaps; i++) { bdrv_dirty_bitmap_set_busy(exp->export_bitmaps[i], false); @@ -1889,19 +2005,19 @@ static int coroutine_fn nbd_co_send_iov(NBDClient *client, struct iovec *iov, } static inline void set_be_simple_reply(NBDSimpleReply *reply, uint64_t error, - uint64_t handle) + uint64_t cookie) { stl_be_p(&reply->magic, NBD_SIMPLE_REPLY_MAGIC); stl_be_p(&reply->error, error); - stq_be_p(&reply->handle, handle); + stq_be_p(&reply->cookie, cookie); } -static int nbd_co_send_simple_reply(NBDClient *client, - uint64_t handle, - uint32_t error, - void *data, - size_t len, - Error **errp) +static int coroutine_fn nbd_co_send_simple_reply(NBDClient *client, + NBDRequest *request, + uint32_t error, + void *data, + uint64_t len, + Error **errp) { NBDSimpleReply reply; int nbd_err = system_errno_to_nbd_errno(error); @@ -1910,144 +2026,184 @@ static int nbd_co_send_simple_reply(NBDClient *client, {.iov_base = data, .iov_len = len} }; - trace_nbd_co_send_simple_reply(handle, nbd_err, nbd_err_lookup(nbd_err), - len); - set_be_simple_reply(&reply, nbd_err, handle); - - return nbd_co_send_iov(client, iov, len ? 2 : 1, errp); -} - -static inline void set_be_chunk(NBDStructuredReplyChunk *chunk, uint16_t flags, - uint16_t type, uint64_t handle, uint32_t length) -{ - stl_be_p(&chunk->magic, NBD_STRUCTURED_REPLY_MAGIC); - stw_be_p(&chunk->flags, flags); - stw_be_p(&chunk->type, type); - stq_be_p(&chunk->handle, handle); - stl_be_p(&chunk->length, length); -} - -static int coroutine_fn nbd_co_send_structured_done(NBDClient *client, - uint64_t handle, - Error **errp) -{ - NBDStructuredReplyChunk chunk; - struct iovec iov[] = { - {.iov_base = &chunk, .iov_len = sizeof(chunk)}, - }; - - trace_nbd_co_send_structured_done(handle); - set_be_chunk(&chunk, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_NONE, handle, 0); - - return nbd_co_send_iov(client, iov, 1, errp); -} - -static int coroutine_fn nbd_co_send_structured_read(NBDClient *client, - uint64_t handle, - uint64_t offset, - void *data, - size_t size, - bool final, - Error **errp) -{ - NBDStructuredReadData chunk; - struct iovec iov[] = { - {.iov_base = &chunk, .iov_len = sizeof(chunk)}, - {.iov_base = data, .iov_len = size} - }; - - assert(size); - trace_nbd_co_send_structured_read(handle, offset, data, size); - set_be_chunk(&chunk.h, final ? NBD_REPLY_FLAG_DONE : 0, - NBD_REPLY_TYPE_OFFSET_DATA, handle, - sizeof(chunk) - sizeof(chunk.h) + size); - stq_be_p(&chunk.offset, offset); + assert(!len || !nbd_err); + assert(len <= NBD_MAX_BUFFER_SIZE); + assert(client->mode < NBD_MODE_STRUCTURED || + (client->mode == NBD_MODE_STRUCTURED && + request->type != NBD_CMD_READ)); + trace_nbd_co_send_simple_reply(request->cookie, nbd_err, + nbd_err_lookup(nbd_err), len); + set_be_simple_reply(&reply, nbd_err, request->cookie); return nbd_co_send_iov(client, iov, 2, errp); } -static int coroutine_fn nbd_co_send_structured_error(NBDClient *client, - uint64_t handle, - uint32_t error, - const char *msg, - Error **errp) +/* + * Prepare the header of a reply chunk for network transmission. + * + * On input, @iov is partially initialized: iov[0].iov_base must point + * to an uninitialized NBDReply, while the remaining @niov elements + * (if any) must be ready for transmission. This function then + * populates iov[0] for transmission. + */ +static inline void set_be_chunk(NBDClient *client, struct iovec *iov, + size_t niov, uint16_t flags, uint16_t type, + NBDRequest *request) { + size_t i, length = 0; + + for (i = 1; i < niov; i++) { + length += iov[i].iov_len; + } + assert(length <= NBD_MAX_BUFFER_SIZE + sizeof(NBDStructuredReadData)); + + if (client->mode >= NBD_MODE_EXTENDED) { + NBDExtendedReplyChunk *chunk = iov->iov_base; + + iov[0].iov_len = sizeof(*chunk); + stl_be_p(&chunk->magic, NBD_EXTENDED_REPLY_MAGIC); + stw_be_p(&chunk->flags, flags); + stw_be_p(&chunk->type, type); + stq_be_p(&chunk->cookie, request->cookie); + stq_be_p(&chunk->offset, request->from); + stq_be_p(&chunk->length, length); + } else { + NBDStructuredReplyChunk *chunk = iov->iov_base; + + iov[0].iov_len = sizeof(*chunk); + stl_be_p(&chunk->magic, NBD_STRUCTURED_REPLY_MAGIC); + stw_be_p(&chunk->flags, flags); + stw_be_p(&chunk->type, type); + stq_be_p(&chunk->cookie, request->cookie); + stl_be_p(&chunk->length, length); + } +} + +static int coroutine_fn nbd_co_send_chunk_done(NBDClient *client, + NBDRequest *request, + Error **errp) +{ + NBDReply hdr; + struct iovec iov[] = { + {.iov_base = &hdr}, + }; + + trace_nbd_co_send_chunk_done(request->cookie); + set_be_chunk(client, iov, 1, NBD_REPLY_FLAG_DONE, + NBD_REPLY_TYPE_NONE, request); + return nbd_co_send_iov(client, iov, 1, errp); +} + +static int coroutine_fn nbd_co_send_chunk_read(NBDClient *client, + NBDRequest *request, + uint64_t offset, + void *data, + uint64_t size, + bool final, + Error **errp) +{ + NBDReply hdr; + NBDStructuredReadData chunk; + struct iovec iov[] = { + {.iov_base = &hdr}, + {.iov_base = &chunk, .iov_len = sizeof(chunk)}, + {.iov_base = data, .iov_len = size} + }; + + assert(size && size <= NBD_MAX_BUFFER_SIZE); + trace_nbd_co_send_chunk_read(request->cookie, offset, data, size); + set_be_chunk(client, iov, 3, final ? NBD_REPLY_FLAG_DONE : 0, + NBD_REPLY_TYPE_OFFSET_DATA, request); + stq_be_p(&chunk.offset, offset); + + return nbd_co_send_iov(client, iov, 3, errp); +} + +static int coroutine_fn nbd_co_send_chunk_error(NBDClient *client, + NBDRequest *request, + uint32_t error, + const char *msg, + Error **errp) +{ + NBDReply hdr; NBDStructuredError chunk; int nbd_err = system_errno_to_nbd_errno(error); struct iovec iov[] = { + {.iov_base = &hdr}, {.iov_base = &chunk, .iov_len = sizeof(chunk)}, {.iov_base = (char *)msg, .iov_len = msg ? strlen(msg) : 0}, }; assert(nbd_err); - trace_nbd_co_send_structured_error(handle, nbd_err, - nbd_err_lookup(nbd_err), msg ? msg : ""); - set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_ERROR, handle, - sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len); + trace_nbd_co_send_chunk_error(request->cookie, nbd_err, + nbd_err_lookup(nbd_err), msg ? msg : ""); + set_be_chunk(client, iov, 3, NBD_REPLY_FLAG_DONE, + NBD_REPLY_TYPE_ERROR, request); stl_be_p(&chunk.error, nbd_err); - stw_be_p(&chunk.message_length, iov[1].iov_len); + stw_be_p(&chunk.message_length, iov[2].iov_len); - return nbd_co_send_iov(client, iov, 1 + !!iov[1].iov_len, errp); + return nbd_co_send_iov(client, iov, 3, errp); } /* Do a sparse read and send the structured reply to the client. - * Returns -errno if sending fails. bdrv_block_status_above() failure is + * Returns -errno if sending fails. blk_co_block_status_above() failure is * reported to the client, at which point this function succeeds. */ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, - uint64_t handle, + NBDRequest *request, uint64_t offset, uint8_t *data, - size_t size, + uint64_t size, Error **errp) { int ret = 0; NBDExport *exp = client->exp; size_t progress = 0; + assert(size <= NBD_MAX_BUFFER_SIZE); while (progress < size) { int64_t pnum; - int status = bdrv_block_status_above(blk_bs(exp->common.blk), NULL, - offset + progress, - size - progress, &pnum, NULL, - NULL); + int status = blk_co_block_status_above(exp->common.blk, NULL, + offset + progress, + size - progress, &pnum, NULL, + NULL); bool final; if (status < 0) { char *msg = g_strdup_printf("unable to check for holes: %s", strerror(-status)); - ret = nbd_co_send_structured_error(client, handle, -status, msg, - errp); + ret = nbd_co_send_chunk_error(client, request, -status, msg, errp); g_free(msg); return ret; } assert(pnum && pnum <= size - progress); final = progress + pnum == size; if (status & BDRV_BLOCK_ZERO) { + NBDReply hdr; NBDStructuredReadHole chunk; struct iovec iov[] = { + {.iov_base = &hdr}, {.iov_base = &chunk, .iov_len = sizeof(chunk)}, }; - trace_nbd_co_send_structured_read_hole(handle, offset + progress, - pnum); - set_be_chunk(&chunk.h, final ? NBD_REPLY_FLAG_DONE : 0, - NBD_REPLY_TYPE_OFFSET_HOLE, - handle, sizeof(chunk) - sizeof(chunk.h)); + trace_nbd_co_send_chunk_read_hole(request->cookie, + offset + progress, pnum); + set_be_chunk(client, iov, 2, + final ? NBD_REPLY_FLAG_DONE : 0, + NBD_REPLY_TYPE_OFFSET_HOLE, request); stq_be_p(&chunk.offset, offset + progress); stl_be_p(&chunk.length, pnum); - ret = nbd_co_send_iov(client, iov, 1, errp); + ret = nbd_co_send_iov(client, iov, 2, errp); } else { - ret = blk_pread(exp->common.blk, offset + progress, - data + progress, pnum); + ret = blk_co_pread(exp->common.blk, offset + progress, pnum, + data + progress, 0); if (ret < 0) { error_setg_errno(errp, -ret, "reading from file failed"); break; } - ret = nbd_co_send_structured_read(client, handle, offset + progress, - data + progress, pnum, final, - errp); + ret = nbd_co_send_chunk_read(client, request, offset + progress, + data + progress, pnum, final, errp); } if (ret < 0) { @@ -2059,20 +2215,24 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, } typedef struct NBDExtentArray { - NBDExtent *extents; + NBDExtent64 *extents; unsigned int nb_alloc; unsigned int count; uint64_t total_length; + bool extended; bool can_add; bool converted_to_be; } NBDExtentArray; -static NBDExtentArray *nbd_extent_array_new(unsigned int nb_alloc) +static NBDExtentArray *nbd_extent_array_new(unsigned int nb_alloc, + NBDMode mode) { NBDExtentArray *ea = g_new0(NBDExtentArray, 1); + assert(mode >= NBD_MODE_STRUCTURED); ea->nb_alloc = nb_alloc; - ea->extents = g_new(NBDExtent, nb_alloc); + ea->extents = g_new(NBDExtent64, nb_alloc); + ea->extended = mode >= NBD_MODE_EXTENDED; ea->can_add = true; return ea; @@ -2091,15 +2251,36 @@ static void nbd_extent_array_convert_to_be(NBDExtentArray *ea) int i; assert(!ea->converted_to_be); + assert(ea->extended); 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); + ea->extents[i].length = cpu_to_be64(ea->extents[i].length); + ea->extents[i].flags = cpu_to_be64(ea->extents[i].flags); } } +/* Further modifications of the array after conversion are abandoned */ +static NBDExtent32 *nbd_extent_array_convert_to_narrow(NBDExtentArray *ea) +{ + int i; + NBDExtent32 *extents = g_new(NBDExtent32, ea->count); + + assert(!ea->converted_to_be); + assert(!ea->extended); + ea->can_add = false; + ea->converted_to_be = true; + + for (i = 0; i < ea->count; i++) { + assert((ea->extents[i].length | ea->extents[i].flags) <= UINT32_MAX); + extents[i].length = cpu_to_be32(ea->extents[i].length); + extents[i].flags = cpu_to_be32(ea->extents[i].flags); + } + + return extents; +} + /* * Add extent to NBDExtentArray. If extent can't be added (no available space), * return -1. @@ -2110,19 +2291,27 @@ static void nbd_extent_array_convert_to_be(NBDExtentArray *ea) * would result in an incorrect range reported to the client) */ static int nbd_extent_array_add(NBDExtentArray *ea, - uint32_t length, uint32_t flags) + uint64_t length, uint32_t flags) { assert(ea->can_add); if (!length) { return 0; } + if (!ea->extended) { + assert(length <= UINT32_MAX); + } /* 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; + uint64_t sum = length + ea->extents[ea->count - 1].length; - if (sum <= UINT32_MAX) { + /* + * sum cannot overflow: the block layer bounds image size at + * 2^63, and ea->extents[].length comes from the block layer. + */ + assert(sum >= length); + if (sum <= UINT32_MAX || ea->extended) { ea->extents[ea->count - 1].length = sum; ea->total_length += length; return 0; @@ -2135,20 +2324,21 @@ static int nbd_extent_array_add(NBDExtentArray *ea, } ea->total_length += length; - ea->extents[ea->count] = (NBDExtent) {.length = length, .flags = flags}; + ea->extents[ea->count] = (NBDExtent64) {.length = length, .flags = flags}; ea->count++; return 0; } -static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, NBDExtentArray *ea) +static int coroutine_fn blockstatus_to_extents(BlockBackend *blk, + 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, bytes, &num, - NULL, NULL); + int ret = blk_co_block_status_above(blk, NULL, offset, bytes, &num, + NULL, NULL); if (ret < 0) { return ret; @@ -2168,13 +2358,14 @@ static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, return 0; } -static int blockalloc_to_extents(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, NBDExtentArray *ea) +static int coroutine_fn blockalloc_to_extents(BlockBackend *blk, + uint64_t offset, uint64_t bytes, + NBDExtentArray *ea) { while (bytes) { int64_t num; - int ret = bdrv_is_allocated_above(bs, NULL, false, offset, bytes, - &num); + int ret = blk_co_is_allocated_above(blk, NULL, false, offset, bytes, + &num); if (ret < 0) { return ret; @@ -2197,50 +2388,72 @@ static int blockalloc_to_extents(BlockDriverState *bs, uint64_t offset, * @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, - NBDExtentArray *ea, - bool last, uint32_t context_id, Error **errp) +static int coroutine_fn +nbd_co_send_extents(NBDClient *client, NBDRequest *request, NBDExtentArray *ea, + bool last, uint32_t context_id, Error **errp) { - NBDStructuredMeta chunk; - struct iovec iov[] = { - {.iov_base = &chunk, .iov_len = sizeof(chunk)}, - {.iov_base = ea->extents, .iov_len = ea->count * sizeof(ea->extents[0])} - }; + NBDReply hdr; + NBDStructuredMeta meta; + NBDExtendedMeta meta_ext; + g_autofree NBDExtent32 *extents = NULL; + uint16_t type; + struct iovec iov[] = { {.iov_base = &hdr}, {0}, {0} }; - nbd_extent_array_convert_to_be(ea); + if (client->mode >= NBD_MODE_EXTENDED) { + type = NBD_REPLY_TYPE_BLOCK_STATUS_EXT; - 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); - stl_be_p(&chunk.context_id, context_id); + iov[1].iov_base = &meta_ext; + iov[1].iov_len = sizeof(meta_ext); + stl_be_p(&meta_ext.context_id, context_id); + stl_be_p(&meta_ext.count, ea->count); - return nbd_co_send_iov(client, iov, 2, errp); + nbd_extent_array_convert_to_be(ea); + iov[2].iov_base = ea->extents; + iov[2].iov_len = ea->count * sizeof(ea->extents[0]); + } else { + type = NBD_REPLY_TYPE_BLOCK_STATUS; + + iov[1].iov_base = &meta; + iov[1].iov_len = sizeof(meta); + stl_be_p(&meta.context_id, context_id); + + extents = nbd_extent_array_convert_to_narrow(ea); + iov[2].iov_base = extents; + iov[2].iov_len = ea->count * sizeof(extents[0]); + } + + trace_nbd_co_send_extents(request->cookie, ea->count, context_id, + ea->total_length, last); + set_be_chunk(client, iov, 3, last ? NBD_REPLY_FLAG_DONE : 0, type, + request); + + return nbd_co_send_iov(client, iov, 3, errp); } /* Get block status from the exported device and send it to the client */ -static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, - BlockDriverState *bs, uint64_t offset, - uint32_t length, bool dont_fragment, - bool last, uint32_t context_id, - Error **errp) +static int +coroutine_fn nbd_co_send_block_status(NBDClient *client, NBDRequest *request, + BlockBackend *blk, uint64_t offset, + uint64_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; - g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents); + g_autoptr(NBDExtentArray) ea = + nbd_extent_array_new(nb_extents, client->mode); if (context_id == NBD_META_ID_BASE_ALLOCATION) { - ret = blockstatus_to_extents(bs, offset, length, ea); + ret = blockstatus_to_extents(blk, offset, length, ea); } else { - ret = blockalloc_to_extents(bs, offset, length, ea); + ret = blockalloc_to_extents(blk, offset, length, ea); } if (ret < 0) { - return nbd_co_send_structured_error( - client, handle, -ret, "can't get block status", errp); + return nbd_co_send_chunk_error(client, request, -ret, + "can't get block status", errp); } - return nbd_co_send_extents(client, handle, ea, last, context_id, errp); + return nbd_co_send_extents(client, request, ea, last, context_id, errp); } /* Populate @ea from a dirty bitmap. */ @@ -2251,11 +2464,12 @@ static void bitmap_to_extents(BdrvDirtyBitmap *bitmap, int64_t start, dirty_start, dirty_count; int64_t end = offset + length; bool full = false; + int64_t bound = es->extended ? INT64_MAX : INT32_MAX; bdrv_dirty_bitmap_lock(bitmap); for (start = offset; - bdrv_dirty_bitmap_next_dirty_area(bitmap, start, end, INT32_MAX, + bdrv_dirty_bitmap_next_dirty_area(bitmap, start, end, bound, &dirty_start, &dirty_count); start = dirty_start + dirty_count) { @@ -2275,17 +2489,105 @@ static void bitmap_to_extents(BdrvDirtyBitmap *bitmap, bdrv_dirty_bitmap_unlock(bitmap); } -static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle, - BdrvDirtyBitmap *bitmap, uint64_t offset, - uint32_t length, bool dont_fragment, bool last, - uint32_t context_id, Error **errp) +static int coroutine_fn nbd_co_send_bitmap(NBDClient *client, + NBDRequest *request, + BdrvDirtyBitmap *bitmap, + uint64_t offset, + uint64_t length, bool dont_fragment, + bool last, uint32_t context_id, + Error **errp) { unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS; - g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents); + g_autoptr(NBDExtentArray) ea = + nbd_extent_array_new(nb_extents, client->mode); bitmap_to_extents(bitmap, offset, length, ea); - return nbd_co_send_extents(client, handle, ea, last, context_id, errp); + return nbd_co_send_extents(client, request, ea, last, context_id, errp); +} + +/* + * nbd_co_block_status_payload_read + * Called when a client wants a subset of negotiated contexts via a + * BLOCK_STATUS payload. Check the payload for valid length and + * contents. On success, return 0 with request updated to effective + * length. If request was invalid but all payload consumed, return 0 + * with request->len and request->contexts->count set to 0 (which will + * trigger an appropriate NBD_EINVAL response later on). Return + * negative errno if the payload was not fully consumed. + */ +static int +nbd_co_block_status_payload_read(NBDClient *client, NBDRequest *request, + Error **errp) +{ + uint64_t payload_len = request->len; + g_autofree char *buf = NULL; + size_t count, i, nr_bitmaps; + uint32_t id; + + if (payload_len > NBD_MAX_BUFFER_SIZE) { + error_setg(errp, "len (%" PRIu64 ") is larger than max len (%u)", + request->len, NBD_MAX_BUFFER_SIZE); + return -EINVAL; + } + + assert(client->contexts.exp == client->exp); + nr_bitmaps = client->exp->nr_export_bitmaps; + request->contexts = g_new0(NBDMetaContexts, 1); + request->contexts->exp = client->exp; + + if (payload_len % sizeof(uint32_t) || + payload_len < sizeof(NBDBlockStatusPayload) || + payload_len > (sizeof(NBDBlockStatusPayload) + + sizeof(id) * client->contexts.count)) { + goto skip; + } + + buf = g_malloc(payload_len); + if (nbd_read(client->ioc, buf, payload_len, + "CMD_BLOCK_STATUS data", errp) < 0) { + return -EIO; + } + trace_nbd_co_receive_request_payload_received(request->cookie, + payload_len); + request->contexts->bitmaps = g_new0(bool, nr_bitmaps); + count = (payload_len - sizeof(NBDBlockStatusPayload)) / sizeof(id); + payload_len = 0; + + for (i = 0; i < count; i++) { + id = ldl_be_p(buf + sizeof(NBDBlockStatusPayload) + sizeof(id) * i); + if (id == NBD_META_ID_BASE_ALLOCATION) { + if (!client->contexts.base_allocation || + request->contexts->base_allocation) { + goto skip; + } + request->contexts->base_allocation = true; + } else if (id == NBD_META_ID_ALLOCATION_DEPTH) { + if (!client->contexts.allocation_depth || + request->contexts->allocation_depth) { + goto skip; + } + request->contexts->allocation_depth = true; + } else { + unsigned idx = id - NBD_META_ID_DIRTY_BITMAP; + + if (idx >= nr_bitmaps || !client->contexts.bitmaps[idx] || + request->contexts->bitmaps[idx]) { + goto skip; + } + request->contexts->bitmaps[idx] = true; + } + } + + request->len = ldq_be_p(buf); + request->contexts->count = count; + return 0; + + skip: + trace_nbd_co_receive_block_status_payload_compliance(request->from, + request->len); + request->len = request->contexts->count = 0; + return nbd_drop(client->ioc, payload_len, errp); } /* nbd_co_receive_request @@ -2295,76 +2597,158 @@ static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle, * to the client (although the caller may still need to disconnect after * reporting the error). */ -static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, - Error **errp) +static int coroutine_fn nbd_co_receive_request(NBDRequestData *req, + NBDRequest *request, + Error **errp) { NBDClient *client = req->client; - int valid_flags; + bool extended_with_payload; + bool check_length = false; + bool check_rofs = false; + bool allocate_buffer = false; + bool payload_okay = false; + uint64_t payload_len = 0; + int valid_flags = NBD_CMD_FLAG_FUA; int ret; g_assert(qemu_in_coroutine()); - assert(client->recv_coroutine == qemu_coroutine_self()); ret = nbd_receive_request(client, request, errp); if (ret < 0) { return ret; } - trace_nbd_co_receive_request_decode_type(request->handle, request->type, + trace_nbd_co_receive_request_decode_type(request->cookie, request->type, nbd_cmd_lookup(request->type)); - - if (request->type != NBD_CMD_WRITE) { - /* No payload, we are ready to read the next request. */ - req->complete = true; + extended_with_payload = client->mode >= NBD_MODE_EXTENDED && + request->flags & NBD_CMD_FLAG_PAYLOAD_LEN; + if (extended_with_payload) { + payload_len = request->len; + check_length = true; } - if (request->type == NBD_CMD_DISC) { + switch (request->type) { + case NBD_CMD_DISC: /* Special case: we're going to disconnect without a reply, * whether or not flags, from, or len are bogus */ + req->complete = true; return -EIO; - } - if (request->type == NBD_CMD_READ || request->type == NBD_CMD_WRITE || - request->type == NBD_CMD_CACHE) - { - if (request->len > NBD_MAX_BUFFER_SIZE) { - error_setg(errp, "len (%" PRIu32" ) is larger than max len (%u)", - request->len, NBD_MAX_BUFFER_SIZE); - return -EINVAL; + case NBD_CMD_READ: + if (client->mode >= NBD_MODE_STRUCTURED) { + valid_flags |= NBD_CMD_FLAG_DF; } + check_length = true; + allocate_buffer = true; + break; - if (request->type != NBD_CMD_CACHE) { - req->data = blk_try_blockalign(client->exp->common.blk, - request->len); - if (req->data == NULL) { - error_setg(errp, "No memory"); - return -ENOMEM; + case NBD_CMD_WRITE: + if (client->mode >= NBD_MODE_EXTENDED) { + if (!extended_with_payload) { + /* The client is noncompliant. Trace it, but proceed. */ + trace_nbd_co_receive_ext_payload_compliance(request->from, + request->len); } + valid_flags |= NBD_CMD_FLAG_PAYLOAD_LEN; } + payload_okay = true; + payload_len = request->len; + check_length = true; + allocate_buffer = true; + check_rofs = true; + break; + + case NBD_CMD_FLUSH: + break; + + case NBD_CMD_TRIM: + check_rofs = true; + break; + + case NBD_CMD_CACHE: + check_length = true; + break; + + case NBD_CMD_WRITE_ZEROES: + valid_flags |= NBD_CMD_FLAG_NO_HOLE | NBD_CMD_FLAG_FAST_ZERO; + check_rofs = true; + break; + + case NBD_CMD_BLOCK_STATUS: + if (extended_with_payload) { + ret = nbd_co_block_status_payload_read(client, request, errp); + if (ret < 0) { + return ret; + } + /* payload now consumed */ + check_length = false; + payload_len = 0; + valid_flags |= NBD_CMD_FLAG_PAYLOAD_LEN; + } else { + request->contexts = &client->contexts; + } + valid_flags |= NBD_CMD_FLAG_REQ_ONE; + break; + + default: + /* Unrecognized, will fail later */ + ; } - if (request->type == NBD_CMD_WRITE) { - if (nbd_read(client->ioc, req->data, request->len, "CMD_WRITE data", - errp) < 0) - { + /* Payload and buffer handling. */ + if (!payload_len) { + req->complete = true; + } + if (check_length && request->len > NBD_MAX_BUFFER_SIZE) { + /* READ, WRITE, CACHE */ + error_setg(errp, "len (%" PRIu64 ") is larger than max len (%u)", + request->len, NBD_MAX_BUFFER_SIZE); + return -EINVAL; + } + if (payload_len && !payload_okay) { + /* + * For now, we don't support payloads on other commands; but + * we can keep the connection alive by ignoring the payload. + * We will fail the command later with NBD_EINVAL for the use + * of an unsupported flag (and not for access beyond bounds). + */ + assert(request->type != NBD_CMD_WRITE); + request->len = 0; + } + if (allocate_buffer) { + /* READ, WRITE */ + req->data = blk_try_blockalign(client->exp->common.blk, + request->len); + if (req->data == NULL) { + error_setg(errp, "No memory"); + return -ENOMEM; + } + } + if (payload_len) { + if (payload_okay) { + /* WRITE */ + assert(req->data); + ret = nbd_read(client->ioc, req->data, payload_len, + "CMD_WRITE data", errp); + } else { + ret = nbd_drop(client->ioc, payload_len, errp); + } + if (ret < 0) { return -EIO; } req->complete = true; - - trace_nbd_co_receive_request_payload_received(request->handle, - request->len); + trace_nbd_co_receive_request_payload_received(request->cookie, + payload_len); } /* Sanity checks. */ - if (client->exp->nbdflags & NBD_FLAG_READ_ONLY && - (request->type == NBD_CMD_WRITE || - request->type == NBD_CMD_WRITE_ZEROES || - request->type == NBD_CMD_TRIM)) { + if (client->exp->nbdflags & NBD_FLAG_READ_ONLY && check_rofs) { + /* WRITE, TRIM, WRITE_ZEROES */ error_setg(errp, "Export is read-only"); return -EROFS; } if (request->from > client->exp->size || request->len > client->exp->size - request->from) { - error_setg(errp, "operation past EOF; From: %" PRIu64 ", Len: %" PRIu32 + error_setg(errp, "operation past EOF; From: %" PRIu64 ", Len: %" PRIu64 ", Size: %" PRIu64, request->from, request->len, client->exp->size); return (request->type == NBD_CMD_WRITE || @@ -2381,14 +2765,6 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, request->len, client->check_align); } - valid_flags = NBD_CMD_FLAG_FUA; - if (request->type == NBD_CMD_READ && client->structured_reply) { - valid_flags |= NBD_CMD_FLAG_DF; - } else if (request->type == NBD_CMD_WRITE_ZEROES) { - valid_flags |= NBD_CMD_FLAG_NO_HOLE | NBD_CMD_FLAG_FAST_ZERO; - } else if (request->type == NBD_CMD_BLOCK_STATUS) { - valid_flags |= NBD_CMD_FLAG_REQ_ONE; - } if (request->flags & ~valid_flags) { error_setg(errp, "unsupported flags for command %s (got 0x%x)", nbd_cmd_lookup(request->type), request->flags); @@ -2403,16 +2779,17 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, * Returns 0 if connection is still live, -errno on failure to talk to client */ static coroutine_fn int nbd_send_generic_reply(NBDClient *client, - uint64_t handle, + NBDRequest *request, int ret, const char *error_msg, Error **errp) { - if (client->structured_reply && ret < 0) { - return nbd_co_send_structured_error(client, handle, -ret, error_msg, - errp); + if (client->mode >= NBD_MODE_STRUCTURED && ret < 0) { + return nbd_co_send_chunk_error(client, request, -ret, error_msg, errp); + } else if (client->mode >= NBD_MODE_EXTENDED) { + return nbd_co_send_chunk_done(client, request, errp); } else { - return nbd_co_send_simple_reply(client, handle, ret < 0 ? -ret : 0, + return nbd_co_send_simple_reply(client, request, ret < 0 ? -ret : 0, NULL, 0, errp); } } @@ -2427,39 +2804,39 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, NBDExport *exp = client->exp; assert(request->type == NBD_CMD_READ); + assert(request->len <= NBD_MAX_BUFFER_SIZE); /* XXX: NBD Protocol only documents use of FUA with WRITE */ if (request->flags & NBD_CMD_FLAG_FUA) { ret = blk_co_flush(exp->common.blk); if (ret < 0) { - return nbd_send_generic_reply(client, request->handle, ret, + return nbd_send_generic_reply(client, request, ret, "flush failed", errp); } } - if (client->structured_reply && !(request->flags & NBD_CMD_FLAG_DF) && - request->len) + if (client->mode >= NBD_MODE_STRUCTURED && + !(request->flags & NBD_CMD_FLAG_DF) && request->len) { - return nbd_co_send_sparse_read(client, request->handle, request->from, + return nbd_co_send_sparse_read(client, request, request->from, data, request->len, errp); } - ret = blk_pread(exp->common.blk, request->from, data, request->len); + ret = blk_co_pread(exp->common.blk, request->from, request->len, data, 0); if (ret < 0) { - return nbd_send_generic_reply(client, request->handle, ret, + return nbd_send_generic_reply(client, request, ret, "reading from file failed", errp); } - if (client->structured_reply) { + if (client->mode >= NBD_MODE_STRUCTURED) { if (request->len) { - return nbd_co_send_structured_read(client, request->handle, - request->from, data, - request->len, true, errp); + return nbd_co_send_chunk_read(client, request, request->from, data, + request->len, true, errp); } else { - return nbd_co_send_structured_done(client, request->handle, errp); + return nbd_co_send_chunk_done(client, request, errp); } } else { - return nbd_co_send_simple_reply(client, request->handle, 0, + return nbd_co_send_simple_reply(client, request, 0, data, request->len, errp); } } @@ -2478,11 +2855,12 @@ static coroutine_fn int nbd_do_cmd_cache(NBDClient *client, NBDRequest *request, NBDExport *exp = client->exp; assert(request->type == NBD_CMD_CACHE); + assert(request->len <= NBD_MAX_BUFFER_SIZE); ret = blk_co_preadv(exp->common.blk, request->from, request->len, NULL, BDRV_REQ_COPY_ON_READ | BDRV_REQ_PREFETCH); - return nbd_send_generic_reply(client, request->handle, ret, + return nbd_send_generic_reply(client, request, ret, "caching data failed", errp); } @@ -2511,9 +2889,10 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, if (request->flags & NBD_CMD_FLAG_FUA) { flags |= BDRV_REQ_FUA; } - ret = blk_pwrite(exp->common.blk, request->from, data, request->len, - flags); - return nbd_send_generic_reply(client, request->handle, ret, + assert(request->len <= NBD_MAX_BUFFER_SIZE); + ret = blk_co_pwrite(exp->common.blk, request->from, request->len, data, + flags); + return nbd_send_generic_reply(client, request, ret, "writing to file failed", errp); case NBD_CMD_WRITE_ZEROES: @@ -2527,9 +2906,9 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, if (request->flags & NBD_CMD_FLAG_FAST_ZERO) { flags |= BDRV_REQ_NO_FALLBACK; } - ret = blk_pwrite_zeroes(exp->common.blk, request->from, request->len, - flags); - return nbd_send_generic_reply(client, request->handle, ret, + ret = blk_co_pwrite_zeroes(exp->common.blk, request->from, request->len, + flags); + return nbd_send_generic_reply(client, request, ret, "writing to file failed", errp); case NBD_CMD_DISC: @@ -2538,7 +2917,7 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, case NBD_CMD_FLUSH: ret = blk_co_flush(exp->common.blk); - return nbd_send_generic_reply(client, request->handle, ret, + return nbd_send_generic_reply(client, request, ret, "flush failed", errp); case NBD_CMD_TRIM: @@ -2546,21 +2925,24 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, if (ret >= 0 && request->flags & NBD_CMD_FLAG_FUA) { ret = blk_co_flush(exp->common.blk); } - return nbd_send_generic_reply(client, request->handle, ret, + return nbd_send_generic_reply(client, request, ret, "discard failed", errp); case NBD_CMD_BLOCK_STATUS: - if (!request->len) { - return nbd_send_generic_reply(client, request->handle, -EINVAL, - "need non-zero length", errp); - } - if (client->export_meta.count) { + assert(request->contexts); + assert(client->mode >= NBD_MODE_EXTENDED || + request->len <= UINT32_MAX); + if (request->contexts->count) { bool dont_fragment = request->flags & NBD_CMD_FLAG_REQ_ONE; - int contexts_remaining = client->export_meta.count; + int contexts_remaining = request->contexts->count; - if (client->export_meta.base_allocation) { - ret = nbd_co_send_block_status(client, request->handle, - blk_bs(exp->common.blk), + if (!request->len) { + return nbd_send_generic_reply(client, request, -EINVAL, + "need non-zero length", errp); + } + if (request->contexts->base_allocation) { + ret = nbd_co_send_block_status(client, request, + exp->common.blk, request->from, request->len, dont_fragment, !--contexts_remaining, @@ -2571,9 +2953,9 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, } } - if (client->export_meta.allocation_depth) { - ret = nbd_co_send_block_status(client, request->handle, - blk_bs(exp->common.blk), + if (request->contexts->allocation_depth) { + ret = nbd_co_send_block_status(client, request, + exp->common.blk, request->from, request->len, dont_fragment, !--contexts_remaining, @@ -2584,11 +2966,12 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, } } + assert(request->contexts->exp == client->exp); for (i = 0; i < client->exp->nr_export_bitmaps; i++) { - if (!client->export_meta.bitmaps[i]) { + if (!request->contexts->bitmaps[i]) { continue; } - ret = nbd_co_send_bitmap(client, request->handle, + ret = nbd_co_send_bitmap(client, request, client->exp->export_bitmaps[i], request->from, request->len, dont_fragment, !--contexts_remaining, @@ -2601,8 +2984,12 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, assert(!contexts_remaining); return 0; + } else if (client->contexts.count) { + return nbd_send_generic_reply(client, request, -EINVAL, + "CMD_BLOCK_STATUS payload not valid", + errp); } else { - return nbd_send_generic_reply(client, request->handle, -EINVAL, + return nbd_send_generic_reply(client, request, -EINVAL, "CMD_BLOCK_STATUS not negotiated", errp); } @@ -2610,7 +2997,7 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, default: msg = g_strdup_printf("invalid request type (%" PRIu32 ") received", request->type); - ret = nbd_send_generic_reply(client, request->handle, -EINVAL, msg, + ret = nbd_send_generic_reply(client, request, -EINVAL, msg, errp); g_free(msg); return ret; @@ -2620,16 +3007,24 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, /* Owns a reference to the NBDClient passed as opaque. */ static coroutine_fn void nbd_trip(void *opaque) { - NBDClient *client = opaque; - NBDRequestData *req; + NBDRequestData *req = opaque; + NBDClient *client = req->client; NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */ int ret; Error *local_err = NULL; + /* + * Note that nbd_client_put() and client_close() must be called from the + * main loop thread. Use aio_co_reschedule_self() to switch AioContext + * before calling these functions. + */ + trace_nbd_trip(); + + qemu_mutex_lock(&client->lock); + if (client->closing) { - nbd_client_put(client); - return; + goto done; } if (client->quiescing) { @@ -2637,14 +3032,25 @@ static coroutine_fn void nbd_trip(void *opaque) * We're switching between AIO contexts. Don't attempt to receive a new * request and kick the main context which may be waiting for us. */ - nbd_client_put(client); client->recv_coroutine = NULL; aio_wait_kick(); - return; + goto done; } - req = nbd_request_get(client); - ret = nbd_co_receive_request(req, &request, &local_err); + /* + * nbd_co_receive_request() returns -EAGAIN when nbd_drained_begin() has + * set client->quiescing but by the time we get back nbd_drained_end() may + * have already cleared client->quiescing. In that case we try again + * because nothing else will spawn an nbd_trip() coroutine until we set + * client->recv_coroutine = NULL further down. + */ + do { + assert(client->recv_coroutine == qemu_coroutine_self()); + qemu_mutex_unlock(&client->lock); + ret = nbd_co_receive_request(req, &request, &local_err); + qemu_mutex_lock(&client->lock); + } while (ret == -EAGAIN && !client->quiescing); + client->recv_coroutine = NULL; if (client->closing) { @@ -2656,34 +3062,47 @@ static coroutine_fn void nbd_trip(void *opaque) } if (ret == -EAGAIN) { - assert(client->quiescing); goto done; } nbd_client_receive_next_request(client); + if (ret == -EIO) { goto disconnect; } + qemu_mutex_unlock(&client->lock); + qio_channel_set_cork(client->ioc, true); + if (ret < 0) { /* It wasn't -EIO, so, according to nbd_co_receive_request() * semantics, we should return the error to the client. */ Error *export_err = local_err; local_err = NULL; - ret = nbd_send_generic_reply(client, request.handle, -EINVAL, + ret = nbd_send_generic_reply(client, &request, -EINVAL, error_get_pretty(export_err), &local_err); error_free(export_err); } else { ret = nbd_handle_request(client, &request, req->data, &local_err); } + if (request.contexts && request.contexts != &client->contexts) { + assert(request.type == NBD_CMD_BLOCK_STATUS); + g_free(request.contexts->bitmaps); + g_free(request.contexts); + } + + qio_channel_set_cork(client->ioc, false); + qemu_mutex_lock(&client->lock); + if (ret < 0) { error_prepend(&local_err, "Failed to send reply: "); goto disconnect; } - /* We must disconnect after NBD_CMD_WRITE if we did not - * read the payload. + /* + * We must disconnect after NBD_CMD_WRITE or BLOCK_STATUS with + * payload if we did not read the payload. */ if (!req->complete) { error_setg(&local_err, "Request handling failed in intermediate state"); @@ -2692,24 +3111,41 @@ static coroutine_fn void nbd_trip(void *opaque) done: nbd_request_put(req); - nbd_client_put(client); + + qemu_mutex_unlock(&client->lock); + + if (!nbd_client_put_nonzero(client)) { + aio_co_reschedule_self(qemu_get_aio_context()); + nbd_client_put(client); + } return; disconnect: if (local_err) { error_reportf_err(local_err, "Disconnect client, due to: "); } + nbd_request_put(req); + qemu_mutex_unlock(&client->lock); + + aio_co_reschedule_self(qemu_get_aio_context()); client_close(client, true); nbd_client_put(client); } +/* + * Runs in export AioContext and main loop thread. Caller must hold + * client->lock. + */ static void nbd_client_receive_next_request(NBDClient *client) { + NBDRequestData *req; + if (!client->recv_coroutine && client->nb_requests < MAX_NBD_REQUESTS && !client->quiescing) { nbd_client_get(client); - client->recv_coroutine = qemu_coroutine_create(nbd_trip, client); + req = nbd_request_get(client); + client->recv_coroutine = qemu_coroutine_create(nbd_trip, req); aio_co_schedule(client->exp->common.ctx, client->recv_coroutine); } } @@ -2729,7 +3165,9 @@ static coroutine_fn void nbd_co_client_start(void *opaque) return; } - nbd_client_receive_next_request(client); + WITH_QEMU_LOCK_GUARD(&client->lock) { + nbd_client_receive_next_request(client); + } } /* @@ -2746,6 +3184,7 @@ void nbd_client_new(QIOChannelSocket *sioc, Coroutine *co; client = g_new0(NBDClient, 1); + qemu_mutex_init(&client->lock); client->refcount = 1; client->tlscreds = tlscreds; if (tlscreds) { @@ -2753,6 +3192,7 @@ void nbd_client_new(QIOChannelSocket *sioc, } client->tlsauthz = g_strdup(tlsauthz); client->sioc = sioc; + qio_channel_set_delay(QIO_CHANNEL(sioc), false); object_ref(OBJECT(client->sioc)); client->ioc = QIO_CHANNEL(sioc); object_ref(OBJECT(client->ioc)); diff --git a/nbd/trace-events b/nbd/trace-events index c4919a2dd5..00ae3216a1 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -31,9 +31,10 @@ nbd_client_loop(void) "Doing NBD loop" nbd_client_loop_ret(int ret, const char *error) "NBD loop returned %d: %s" nbd_client_clear_queue(void) "Clearing NBD queue" nbd_client_clear_socket(void) "Clearing NBD socket" -nbd_send_request(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name) "Sending request to server: { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) }" -nbd_receive_simple_reply(int32_t error, const char *errname, uint64_t handle) "Got simple reply: { .error = %" PRId32 " (%s), handle = %" PRIu64" }" -nbd_receive_structured_reply_chunk(uint16_t flags, uint16_t type, const char *name, uint64_t handle, uint32_t length) "Got structured reply chunk: { flags = 0x%" PRIx16 ", type = %d (%s), handle = %" PRIu64 ", length = %" PRIu32 " }" +nbd_send_request(uint64_t from, uint64_t len, uint64_t cookie, uint16_t flags, uint16_t type, const char *name) "Sending request to server: { .from = %" PRIu64", .len = %" PRIu64 ", .cookie = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) }" +nbd_receive_simple_reply(int32_t error, const char *errname, uint64_t cookie) "Got simple reply: { .error = %" PRId32 " (%s), cookie = %" PRIu64" }" +nbd_receive_reply_chunk_header(uint16_t flags, uint16_t type, const char *name, uint64_t cookie, uint32_t length) "Got reply chunk header: { flags = 0x%" PRIx16 ", type = %" PRIu16 " (%s), cookie = %" PRIu64 ", length = %" PRIu32 " }" +nbd_receive_wrong_header(uint32_t magic, const char *mode) "Server sent unexpected magic 0x%" PRIx32 " for negotiated mode %s" # common.c nbd_unknown_error(int err) "Squashing unexpected error %d to EINVAL" @@ -60,16 +61,21 @@ nbd_negotiate_options_check_option(uint32_t option, const char *name) "Checking nbd_negotiate_begin(void) "Beginning negotiation" nbd_negotiate_new_style_size_flags(uint64_t size, unsigned flags) "advertising size %" PRIu64 " and flags 0x%x" nbd_negotiate_success(void) "Negotiation succeeded" -nbd_receive_request(uint32_t magic, uint16_t flags, uint16_t type, uint64_t from, uint32_t len) "Got request: { magic = 0x%" PRIx32 ", .flags = 0x%" PRIx16 ", .type = 0x%" PRIx16 ", from = %" PRIu64 ", len = %" PRIu32 " }" +nbd_receive_request(uint32_t magic, uint16_t flags, uint16_t type, uint64_t from, uint64_t len) "Got request: { magic = 0x%" PRIx32 ", .flags = 0x%" PRIx16 ", .type = 0x%" PRIx16 ", from = %" PRIu64 ", len = %" PRIu64 " }" nbd_blk_aio_attached(const char *name, void *ctx) "Export %s: Attaching clients to AIO context %p" nbd_blk_aio_detach(const char *name, void *ctx) "Export %s: Detaching clients from AIO context %p" -nbd_co_send_simple_reply(uint64_t handle, uint32_t error, const char *errname, int len) "Send simple reply: handle = %" PRIu64 ", error = %" PRIu32 " (%s), len = %d" -nbd_co_send_structured_done(uint64_t handle) "Send structured reply done: handle = %" PRIu64 -nbd_co_send_structured_read(uint64_t handle, uint64_t offset, void *data, size_t size) "Send structured read data reply: handle = %" PRIu64 ", offset = %" PRIu64 ", data = %p, len = %zu" -nbd_co_send_structured_read_hole(uint64_t handle, uint64_t offset, size_t size) "Send structured read hole reply: handle = %" PRIu64 ", offset = %" PRIu64 ", len = %zu" -nbd_co_send_extents(uint64_t handle, unsigned int extents, uint32_t id, uint64_t length, int last) "Send block status reply: handle = %" PRIu64 ", extents = %u, context = %d (extents cover %" PRIu64 " bytes, last chunk = %d)" -nbd_co_send_structured_error(uint64_t handle, int err, const char *errname, const char *msg) "Send structured error reply: handle = %" PRIu64 ", error = %d (%s), msg = '%s'" -nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *name) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 " (%s)" -nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32 -nbd_co_receive_align_compliance(const char *op, uint64_t from, uint32_t len, uint32_t align) "client sent non-compliant unaligned %s request: from=0x%" PRIx64 ", len=0x%" PRIx32 ", align=0x%" PRIx32 +nbd_co_send_simple_reply(uint64_t cookie, uint32_t error, const char *errname, uint64_t len) "Send simple reply: cookie = %" PRIu64 ", error = %" PRIu32 " (%s), len = %" PRIu64 +nbd_co_send_chunk_done(uint64_t cookie) "Send structured reply done: cookie = %" PRIu64 +nbd_co_send_chunk_read(uint64_t cookie, uint64_t offset, void *data, uint64_t size) "Send structured read data reply: cookie = %" PRIu64 ", offset = %" PRIu64 ", data = %p, len = %" PRIu64 +nbd_co_send_chunk_read_hole(uint64_t cookie, uint64_t offset, uint64_t size) "Send structured read hole reply: cookie = %" PRIu64 ", offset = %" PRIu64 ", len = %" PRIu64 +nbd_co_send_extents(uint64_t cookie, unsigned int extents, uint32_t id, uint64_t length, int last) "Send block status reply: cookie = %" PRIu64 ", extents = %u, context = %d (extents cover %" PRIu64 " bytes, last chunk = %d)" +nbd_co_send_chunk_error(uint64_t cookie, int err, const char *errname, const char *msg) "Send structured error reply: cookie = %" PRIu64 ", error = %d (%s), msg = '%s'" +nbd_co_receive_block_status_payload_compliance(uint64_t from, uint64_t len) "client sent unusable block status payload: from=0x%" PRIx64 ", len=0x%" PRIx64 +nbd_co_receive_request_decode_type(uint64_t cookie, uint16_t type, const char *name) "Decoding type: cookie = %" PRIu64 ", type = %" PRIu16 " (%s)" +nbd_co_receive_request_payload_received(uint64_t cookie, uint64_t len) "Payload received: cookie = %" PRIu64 ", len = %" PRIu64 +nbd_co_receive_ext_payload_compliance(uint64_t from, uint64_t len) "client sent non-compliant write without payload flag: from=0x%" PRIx64 ", len=0x%" PRIx64 +nbd_co_receive_align_compliance(const char *op, uint64_t from, uint64_t len, uint32_t align) "client sent non-compliant unaligned %s request: from=0x%" PRIx64 ", len=0x%" PRIx64 ", align=0x%" PRIx32 nbd_trip(void) "Reading request" + +# client-connection.c +nbd_connect_thread_sleep(uint64_t timeout) "timeout %" PRIu64 diff --git a/net/af-xdp.c b/net/af-xdp.c new file mode 100644 index 0000000000..01c5fb914e --- /dev/null +++ b/net/af-xdp.c @@ -0,0 +1,524 @@ +/* + * AF_XDP network backend. + * + * Copyright (c) 2023 Red Hat, Inc. + * + * Authors: + * Ilya Maximets + * + * 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 +#include +#include +#include + +#include "clients.h" +#include "monitor/monitor.h" +#include "net/net.h" +#include "qapi/error.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qemu/iov.h" +#include "qemu/main-loop.h" +#include "qemu/memalign.h" + + +typedef struct AFXDPState { + NetClientState nc; + + struct xsk_socket *xsk; + struct xsk_ring_cons rx; + struct xsk_ring_prod tx; + struct xsk_ring_cons cq; + struct xsk_ring_prod fq; + + char ifname[IFNAMSIZ]; + int ifindex; + bool read_poll; + bool write_poll; + uint32_t outstanding_tx; + + uint64_t *pool; + uint32_t n_pool; + char *buffer; + struct xsk_umem *umem; + + uint32_t n_queues; + uint32_t xdp_flags; + bool inhibit; +} AFXDPState; + +#define AF_XDP_BATCH_SIZE 64 + +static void af_xdp_send(void *opaque); +static void af_xdp_writable(void *opaque); + +/* Set the event-loop handlers for the af-xdp backend. */ +static void af_xdp_update_fd_handler(AFXDPState *s) +{ + qemu_set_fd_handler(xsk_socket__fd(s->xsk), + s->read_poll ? af_xdp_send : NULL, + s->write_poll ? af_xdp_writable : NULL, + s); +} + +/* Update the read handler. */ +static void af_xdp_read_poll(AFXDPState *s, bool enable) +{ + if (s->read_poll != enable) { + s->read_poll = enable; + af_xdp_update_fd_handler(s); + } +} + +/* Update the write handler. */ +static void af_xdp_write_poll(AFXDPState *s, bool enable) +{ + if (s->write_poll != enable) { + s->write_poll = enable; + af_xdp_update_fd_handler(s); + } +} + +static void af_xdp_poll(NetClientState *nc, bool enable) +{ + AFXDPState *s = DO_UPCAST(AFXDPState, nc, nc); + + if (s->read_poll != enable || s->write_poll != enable) { + s->write_poll = enable; + s->read_poll = enable; + af_xdp_update_fd_handler(s); + } +} + +static void af_xdp_complete_tx(AFXDPState *s) +{ + uint32_t idx = 0; + uint32_t done, i; + uint64_t *addr; + + done = xsk_ring_cons__peek(&s->cq, XSK_RING_CONS__DEFAULT_NUM_DESCS, &idx); + + for (i = 0; i < done; i++) { + addr = (void *) xsk_ring_cons__comp_addr(&s->cq, idx++); + s->pool[s->n_pool++] = *addr; + s->outstanding_tx--; + } + + if (done) { + xsk_ring_cons__release(&s->cq, done); + } +} + +/* + * The fd_write() callback, invoked if the fd is marked as writable + * after a poll. + */ +static void af_xdp_writable(void *opaque) +{ + AFXDPState *s = opaque; + + /* Try to recover buffers that are already sent. */ + af_xdp_complete_tx(s); + + /* + * Unregister the handler, unless we still have packets to transmit + * and kernel needs a wake up. + */ + if (!s->outstanding_tx || !xsk_ring_prod__needs_wakeup(&s->tx)) { + af_xdp_write_poll(s, false); + } + + /* Flush any buffered packets. */ + qemu_flush_queued_packets(&s->nc); +} + +static ssize_t af_xdp_receive(NetClientState *nc, + const uint8_t *buf, size_t size) +{ + AFXDPState *s = DO_UPCAST(AFXDPState, nc, nc); + struct xdp_desc *desc; + uint32_t idx; + void *data; + + /* Try to recover buffers that are already sent. */ + af_xdp_complete_tx(s); + + if (size > XSK_UMEM__DEFAULT_FRAME_SIZE) { + /* We can't transmit packet this size... */ + return size; + } + + if (!s->n_pool || !xsk_ring_prod__reserve(&s->tx, 1, &idx)) { + /* + * Out of buffers or space in tx ring. Poll until we can write. + * This will also kick the Tx, if it was waiting on CQ. + */ + af_xdp_write_poll(s, true); + return 0; + } + + desc = xsk_ring_prod__tx_desc(&s->tx, idx); + desc->addr = s->pool[--s->n_pool]; + desc->len = size; + + data = xsk_umem__get_data(s->buffer, desc->addr); + memcpy(data, buf, size); + + xsk_ring_prod__submit(&s->tx, 1); + s->outstanding_tx++; + + if (xsk_ring_prod__needs_wakeup(&s->tx)) { + af_xdp_write_poll(s, true); + } + + return size; +} + +/* + * Complete a previous send (backend --> guest) and enable the + * fd_read callback. + */ +static void af_xdp_send_completed(NetClientState *nc, ssize_t len) +{ + AFXDPState *s = DO_UPCAST(AFXDPState, nc, nc); + + af_xdp_read_poll(s, true); +} + +static void af_xdp_fq_refill(AFXDPState *s, uint32_t n) +{ + uint32_t i, idx = 0; + + /* Leave one packet for Tx, just in case. */ + if (s->n_pool < n + 1) { + n = s->n_pool; + } + + if (!n || !xsk_ring_prod__reserve(&s->fq, n, &idx)) { + return; + } + + for (i = 0; i < n; i++) { + *xsk_ring_prod__fill_addr(&s->fq, idx++) = s->pool[--s->n_pool]; + } + xsk_ring_prod__submit(&s->fq, n); + + if (xsk_ring_prod__needs_wakeup(&s->fq)) { + /* Receive was blocked by not having enough buffers. Wake it up. */ + af_xdp_read_poll(s, true); + } +} + +static void af_xdp_send(void *opaque) +{ + uint32_t i, n_rx, idx = 0; + AFXDPState *s = opaque; + + n_rx = xsk_ring_cons__peek(&s->rx, AF_XDP_BATCH_SIZE, &idx); + if (!n_rx) { + return; + } + + for (i = 0; i < n_rx; i++) { + const struct xdp_desc *desc; + struct iovec iov; + + desc = xsk_ring_cons__rx_desc(&s->rx, idx++); + + iov.iov_base = xsk_umem__get_data(s->buffer, desc->addr); + iov.iov_len = desc->len; + + s->pool[s->n_pool++] = desc->addr; + + if (!qemu_sendv_packet_async(&s->nc, &iov, 1, + af_xdp_send_completed)) { + /* + * The peer does not receive anymore. Packet is queued, stop + * reading from the backend until af_xdp_send_completed(). + */ + af_xdp_read_poll(s, false); + + /* Return unused descriptors to not break the ring cache. */ + xsk_ring_cons__cancel(&s->rx, n_rx - i - 1); + n_rx = i + 1; + break; + } + } + + /* Release actually sent descriptors and try to re-fill. */ + xsk_ring_cons__release(&s->rx, n_rx); + af_xdp_fq_refill(s, AF_XDP_BATCH_SIZE); +} + +/* Flush and close. */ +static void af_xdp_cleanup(NetClientState *nc) +{ + AFXDPState *s = DO_UPCAST(AFXDPState, nc, nc); + + qemu_purge_queued_packets(nc); + + af_xdp_poll(nc, false); + + xsk_socket__delete(s->xsk); + s->xsk = NULL; + g_free(s->pool); + s->pool = NULL; + xsk_umem__delete(s->umem); + s->umem = NULL; + qemu_vfree(s->buffer); + s->buffer = NULL; + + /* Remove the program if it's the last open queue. */ + if (!s->inhibit && nc->queue_index == s->n_queues - 1 && s->xdp_flags + && bpf_xdp_detach(s->ifindex, s->xdp_flags, NULL) != 0) { + fprintf(stderr, + "af-xdp: unable to remove XDP program from '%s', ifindex: %d\n", + s->ifname, s->ifindex); + } +} + +static int af_xdp_umem_create(AFXDPState *s, int sock_fd, Error **errp) +{ + struct xsk_umem_config config = { + .fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS, + .comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS, + .frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE, + .frame_headroom = 0, + }; + uint64_t n_descs; + uint64_t size; + int64_t i; + int ret; + + /* Number of descriptors if all 4 queues (rx, tx, cq, fq) are full. */ + n_descs = (XSK_RING_PROD__DEFAULT_NUM_DESCS + + XSK_RING_CONS__DEFAULT_NUM_DESCS) * 2; + size = n_descs * XSK_UMEM__DEFAULT_FRAME_SIZE; + + s->buffer = qemu_memalign(qemu_real_host_page_size(), size); + memset(s->buffer, 0, size); + + if (sock_fd < 0) { + ret = xsk_umem__create(&s->umem, s->buffer, size, + &s->fq, &s->cq, &config); + } else { + ret = xsk_umem__create_with_fd(&s->umem, sock_fd, s->buffer, size, + &s->fq, &s->cq, &config); + } + + if (ret) { + qemu_vfree(s->buffer); + error_setg_errno(errp, errno, + "failed to create umem for %s queue_index: %d", + s->ifname, s->nc.queue_index); + return -1; + } + + s->pool = g_new(uint64_t, n_descs); + /* Fill the pool in the opposite order, because it's a LIFO queue. */ + for (i = n_descs; i >= 0; i--) { + s->pool[i] = i * XSK_UMEM__DEFAULT_FRAME_SIZE; + } + s->n_pool = n_descs; + + af_xdp_fq_refill(s, XSK_RING_PROD__DEFAULT_NUM_DESCS); + + return 0; +} + +static int af_xdp_socket_create(AFXDPState *s, + const NetdevAFXDPOptions *opts, Error **errp) +{ + struct xsk_socket_config cfg = { + .rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS, + .tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS, + .libxdp_flags = 0, + .bind_flags = XDP_USE_NEED_WAKEUP, + .xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST, + }; + int queue_id, error = 0; + + s->inhibit = opts->has_inhibit && opts->inhibit; + if (s->inhibit) { + cfg.libxdp_flags |= XSK_LIBXDP_FLAGS__INHIBIT_PROG_LOAD; + } + + if (opts->has_force_copy && opts->force_copy) { + cfg.bind_flags |= XDP_COPY; + } + + queue_id = s->nc.queue_index; + if (opts->has_start_queue && opts->start_queue > 0) { + queue_id += opts->start_queue; + } + + if (opts->has_mode) { + /* Specific mode requested. */ + cfg.xdp_flags |= (opts->mode == AFXDP_MODE_NATIVE) + ? XDP_FLAGS_DRV_MODE : XDP_FLAGS_SKB_MODE; + if (xsk_socket__create(&s->xsk, s->ifname, queue_id, + s->umem, &s->rx, &s->tx, &cfg)) { + error = errno; + } + } else { + /* No mode requested, try native first. */ + cfg.xdp_flags |= XDP_FLAGS_DRV_MODE; + + if (xsk_socket__create(&s->xsk, s->ifname, queue_id, + s->umem, &s->rx, &s->tx, &cfg)) { + /* Can't use native mode, try skb. */ + cfg.xdp_flags &= ~XDP_FLAGS_DRV_MODE; + cfg.xdp_flags |= XDP_FLAGS_SKB_MODE; + + if (xsk_socket__create(&s->xsk, s->ifname, queue_id, + s->umem, &s->rx, &s->tx, &cfg)) { + error = errno; + } + } + } + + if (error) { + error_setg_errno(errp, error, + "failed to create AF_XDP socket for %s queue_id: %d", + s->ifname, queue_id); + return -1; + } + + s->xdp_flags = cfg.xdp_flags; + + return 0; +} + +/* NetClientInfo methods. */ +static NetClientInfo net_af_xdp_info = { + .type = NET_CLIENT_DRIVER_AF_XDP, + .size = sizeof(AFXDPState), + .receive = af_xdp_receive, + .poll = af_xdp_poll, + .cleanup = af_xdp_cleanup, +}; + +static int *parse_socket_fds(const char *sock_fds_str, + int64_t n_expected, Error **errp) +{ + gchar **substrings = g_strsplit(sock_fds_str, ":", -1); + int64_t i, n_sock_fds = g_strv_length(substrings); + int *sock_fds = NULL; + + if (n_sock_fds != n_expected) { + error_setg(errp, "expected %"PRIi64" socket fds, got %"PRIi64, + n_expected, n_sock_fds); + goto exit; + } + + sock_fds = g_new(int, n_sock_fds); + + for (i = 0; i < n_sock_fds; i++) { + sock_fds[i] = monitor_fd_param(monitor_cur(), substrings[i], errp); + if (sock_fds[i] < 0) { + g_free(sock_fds); + sock_fds = NULL; + goto exit; + } + } + +exit: + g_strfreev(substrings); + return sock_fds; +} + +/* + * The exported init function. + * + * ... -netdev af-xdp,ifname="..." + */ +int net_init_af_xdp(const Netdev *netdev, + const char *name, NetClientState *peer, Error **errp) +{ + const NetdevAFXDPOptions *opts = &netdev->u.af_xdp; + NetClientState *nc, *nc0 = NULL; + unsigned int ifindex; + uint32_t prog_id = 0; + g_autofree int *sock_fds = NULL; + int64_t i, queues; + Error *err = NULL; + AFXDPState *s; + + ifindex = if_nametoindex(opts->ifname); + if (!ifindex) { + error_setg_errno(errp, errno, "failed to get ifindex for '%s'", + opts->ifname); + return -1; + } + + queues = opts->has_queues ? opts->queues : 1; + if (queues < 1) { + error_setg(errp, "invalid number of queues (%" PRIi64 ") for '%s'", + queues, opts->ifname); + return -1; + } + + if ((opts->has_inhibit && opts->inhibit) != !!opts->sock_fds) { + error_setg(errp, "'inhibit=on' requires 'sock-fds' and vice versa"); + return -1; + } + + if (opts->sock_fds) { + sock_fds = parse_socket_fds(opts->sock_fds, queues, errp); + if (!sock_fds) { + return -1; + } + } + + for (i = 0; i < queues; i++) { + nc = qemu_new_net_client(&net_af_xdp_info, peer, "af-xdp", name); + qemu_set_info_str(nc, "af-xdp%"PRIi64" to %s", i, opts->ifname); + nc->queue_index = i; + + if (!nc0) { + nc0 = nc; + } + + s = DO_UPCAST(AFXDPState, nc, nc); + + pstrcpy(s->ifname, sizeof(s->ifname), opts->ifname); + s->ifindex = ifindex; + s->n_queues = queues; + + if (af_xdp_umem_create(s, sock_fds ? sock_fds[i] : -1, errp) + || af_xdp_socket_create(s, opts, errp)) { + /* Make sure the XDP program will be removed. */ + s->n_queues = i; + error_propagate(errp, err); + goto err; + } + } + + if (nc0) { + s = DO_UPCAST(AFXDPState, nc, nc0); + if (bpf_xdp_query_id(s->ifindex, s->xdp_flags, &prog_id) || !prog_id) { + error_setg_errno(errp, errno, + "no XDP program loaded on '%s', ifindex: %d", + s->ifname, s->ifindex); + goto err; + } + } + + af_xdp_read_poll(s, true); /* Initially only poll for reads. */ + + return 0; + +err: + if (nc0) { + qemu_del_net_client(nc0); + } + + return -1; +} diff --git a/net/announce.c b/net/announce.c index 62c60192a3..9e99044422 100644 --- a/net/announce.c +++ b/net/announce.c @@ -46,7 +46,7 @@ void qemu_announce_timer_del(AnnounceTimer *timer, bool free_named) } qapi_free_strList(timer->params.interfaces); timer->params.interfaces = NULL; - if (free_named && timer->params.has_id) { + if (free_named && timer->params.id) { AnnounceTimer *list_timer; /* * Sanity check: There should only be one timer on the list with @@ -157,7 +157,7 @@ static void qemu_announce_self_iter(NICState *nic, void *opaque) skip = false; } - trace_qemu_announce_self_iter(timer->params.has_id ? timer->params.id : "_", + trace_qemu_announce_self_iter(timer->params.id ?: "_", nic->ncs->name, qemu_ether_ntoa(&nic->conf->macaddr), skip); @@ -199,9 +199,9 @@ void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params) void qmp_announce_self(AnnounceParameters *params, Error **errp) { AnnounceTimer *named_timer; - if (!params->has_id) { + + if (!params->id) { params->id = g_strdup(""); - params->has_id = true; } named_timer = g_datalist_get_data(&named_timers, params->id); diff --git a/net/can/meson.build b/net/can/meson.build index f53d9ec54f..af3b27921c 100644 --- a/net/can/meson.build +++ b/net/can/meson.build @@ -1,5 +1,7 @@ can_ss = ss.source_set() can_ss.add(files('can_core.c', 'can_host.c')) -can_ss.add(when: 'CONFIG_LINUX', if_true: files('can_socketcan.c')) +if host_os == 'linux' + can_ss.add(files('can_socketcan.c')) +endif -softmmu_ss.add_all(when: 'CONFIG_CAN_BUS', if_true: can_ss) +system_ss.add_all(when: 'CONFIG_CAN_BUS', if_true: can_ss) diff --git a/net/checksum.c b/net/checksum.c index 68245fd748..1a957e4c0b 100644 --- a/net/checksum.c +++ b/net/checksum.c @@ -74,7 +74,7 @@ void net_checksum_calculate(uint8_t *data, int length, int csum_flag) return; } - /* Handle the optionnal VLAN headers */ + /* Handle the optional VLAN headers */ switch (lduw_be_p(&PKT_GET_ETH_HDR(data)->h_proto)) { case ETH_P_VLAN: mac_hdr_len = sizeof(struct eth_header) + @@ -96,7 +96,7 @@ void net_checksum_calculate(uint8_t *data, int length, int csum_flag) length -= mac_hdr_len; - /* Now check we have an IP header (with an optionnal VLAN header) */ + /* Now check we have an IP header (with an optional VLAN header) */ if (length < sizeof(struct ip_header)) { return; } diff --git a/net/clients.h b/net/clients.h index c9157789f2..be53794582 100644 --- a/net/clients.h +++ b/net/clients.h @@ -40,6 +40,12 @@ int net_init_hubport(const Netdev *netdev, const char *name, int net_init_socket(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp); +int net_init_stream(const Netdev *netdev, const char *name, + NetClientState *peer, Error **errp); + +int net_init_dgram(const Netdev *netdev, const char *name, + NetClientState *peer, Error **errp); + int net_init_tap(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp); @@ -58,6 +64,11 @@ int net_init_netmap(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp); #endif +#ifdef CONFIG_AF_XDP +int net_init_af_xdp(const Netdev *netdev, const char *name, + NetClientState *peer, Error **errp); +#endif + int net_init_vhost_user(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp); diff --git a/net/colo-compare.c b/net/colo-compare.c index d5d0965805..c4ad0ab71f 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -28,7 +28,6 @@ #include "sysemu/iothread.h" #include "net/colo-compare.h" #include "migration/colo.h" -#include "migration/migration.h" #include "util.h" #include "block/aio-wait.h" @@ -189,7 +188,7 @@ static void colo_compare_inconsistency_notify(CompareState *s) notify_remote_frame(s); } else { notifier_list_notify(&colo_compare_notifiers, - migrate_get_current()); + NULL); } } @@ -1135,22 +1134,17 @@ static void set_max_queue_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Error *local_err = NULL; uint64_t value; - visit_type_uint64(v, name, &value, &local_err); - if (local_err) { - goto out; + if (!visit_type_uint64(v, name, &value, errp)) { + return; } if (!value) { - error_setg(&local_err, "Property '%s.%s' requires a positive value", + error_setg(errp, "Property '%s.%s' requires a positive value", object_get_typename(obj), name); - goto out; + return; } max_queue_size = value; - -out: - error_propagate(errp, local_err); } static void compare_pri_rs_finalize(SocketReadState *pri_rs) @@ -1323,7 +1317,7 @@ static void colo_compare_complete(UserCreatable *uc, Error **errp) s->connection_track_table = g_hash_table_new_full(connection_key_hash, connection_key_equal, g_free, - connection_destroy); + NULL); colo_compare_iothread(s); @@ -1444,12 +1438,10 @@ static void colo_compare_finalize(Object *obj) qemu_bh_delete(s->event_bh); AioContext *ctx = iothread_get_aio_context(s->iothread); - aio_context_acquire(ctx); AIO_WAIT_WHILE(ctx, !s->out_sendco.done); if (s->notify_dev) { AIO_WAIT_WHILE(ctx, !s->notify_sendco.done); } - aio_context_release(ctx); /* Release all unhandled packets after compare thead exited */ g_queue_foreach(&s->conn_list, colo_flush_packets, s); diff --git a/net/colo.c b/net/colo.c index 1f8162f59f..fb2c36a026 100644 --- a/net/colo.c +++ b/net/colo.c @@ -44,14 +44,28 @@ int parse_packet_early(Packet *pkt) { int network_length; static const uint8_t vlan[] = {0x81, 0x00}; - uint8_t *data = pkt->data + pkt->vnet_hdr_len; + uint8_t *data = pkt->data; uint16_t l3_proto; - ssize_t l2hdr_len = eth_get_l2_hdr_length(data); + ssize_t l2hdr_len; - if (pkt->size < ETH_HLEN + pkt->vnet_hdr_len) { - trace_colo_proxy_main("pkt->size < ETH_HLEN"); + assert(data); + + /* Check the received vnet_hdr_len then add the offset */ + if ((pkt->vnet_hdr_len > sizeof(struct virtio_net_hdr_v1_hash)) || + (pkt->size < sizeof(struct eth_header) + sizeof(struct vlan_header) + + pkt->vnet_hdr_len)) { + /* + * The received remote packet maybe misconfiguration here, + * Please enable/disable filter module's the vnet_hdr flag at + * the same time. + */ + trace_colo_proxy_main_vnet_info("This received packet load wrong ", + pkt->vnet_hdr_len, pkt->size); return 1; } + data += pkt->vnet_hdr_len; + + l2hdr_len = eth_get_l2_hdr_length(data); /* * TODO: support vlan. @@ -218,7 +232,7 @@ Connection *connection_get(GHashTable *connection_track_table, /* * clear the conn_list */ - while (!g_queue_is_empty(conn_list)) { + while (conn_list && !g_queue_is_empty(conn_list)) { connection_destroy(g_queue_pop_head(conn_list)); } } diff --git a/net/colo.h b/net/colo.h index 8b3e8d5a83..22fc3031f7 100644 --- a/net/colo.h +++ b/net/colo.h @@ -18,6 +18,7 @@ #include "qemu/jhash.h" #include "qemu/timer.h" #include "net/eth.h" +#include "standard-headers/linux/virtio_net.h" #define HASHTABLE_MAX_SIZE 16384 diff --git a/net/dgram.c b/net/dgram.c new file mode 100644 index 0000000000..48f653bceb --- /dev/null +++ b/net/dgram.c @@ -0,0 +1,623 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2022 Red Hat, Inc. + * + * 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 "net/net.h" +#include "clients.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/option.h" +#include "qemu/sockets.h" +#include "qemu/iov.h" +#include "qemu/main-loop.h" +#include "qemu/cutils.h" + +typedef struct NetDgramState { + NetClientState nc; + int fd; + SocketReadState rs; + bool read_poll; /* waiting to receive data? */ + bool write_poll; /* waiting to transmit data? */ + /* contains destination iff connectionless */ + struct sockaddr *dest_addr; + socklen_t dest_len; +} NetDgramState; + +static void net_dgram_send(void *opaque); +static void net_dgram_writable(void *opaque); + +static void net_dgram_update_fd_handler(NetDgramState *s) +{ + qemu_set_fd_handler(s->fd, + s->read_poll ? net_dgram_send : NULL, + s->write_poll ? net_dgram_writable : NULL, + s); +} + +static void net_dgram_read_poll(NetDgramState *s, bool enable) +{ + s->read_poll = enable; + net_dgram_update_fd_handler(s); +} + +static void net_dgram_write_poll(NetDgramState *s, bool enable) +{ + s->write_poll = enable; + net_dgram_update_fd_handler(s); +} + +static void net_dgram_writable(void *opaque) +{ + NetDgramState *s = opaque; + + net_dgram_write_poll(s, false); + + qemu_flush_queued_packets(&s->nc); +} + +static ssize_t net_dgram_receive(NetClientState *nc, + const uint8_t *buf, size_t size) +{ + NetDgramState *s = DO_UPCAST(NetDgramState, nc, nc); + ssize_t ret; + + do { + if (s->dest_addr) { + ret = sendto(s->fd, buf, size, 0, s->dest_addr, s->dest_len); + } else { + ret = send(s->fd, buf, size, 0); + } + } while (ret == -1 && errno == EINTR); + + if (ret == -1 && errno == EAGAIN) { + net_dgram_write_poll(s, true); + return 0; + } + return ret; +} + +static void net_dgram_send_completed(NetClientState *nc, ssize_t len) +{ + NetDgramState *s = DO_UPCAST(NetDgramState, nc, nc); + + if (!s->read_poll) { + net_dgram_read_poll(s, true); + } +} + +static void net_dgram_rs_finalize(SocketReadState *rs) +{ + NetDgramState *s = container_of(rs, NetDgramState, rs); + + if (qemu_send_packet_async(&s->nc, rs->buf, + rs->packet_len, + net_dgram_send_completed) == 0) { + net_dgram_read_poll(s, false); + } +} + +static void net_dgram_send(void *opaque) +{ + NetDgramState *s = opaque; + int size; + + size = recv(s->fd, s->rs.buf, sizeof(s->rs.buf), 0); + if (size < 0) { + return; + } + if (size == 0) { + /* end of connection */ + net_dgram_read_poll(s, false); + net_dgram_write_poll(s, false); + return; + } + if (qemu_send_packet_async(&s->nc, s->rs.buf, size, + net_dgram_send_completed) == 0) { + net_dgram_read_poll(s, false); + } +} + +static int net_dgram_mcast_create(struct sockaddr_in *mcastaddr, + struct in_addr *localaddr, + Error **errp) +{ + struct ip_mreq imr; + int fd; + int val, ret; +#ifdef __OpenBSD__ + unsigned char loop; +#else + int loop; +#endif + + if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) { + error_setg(errp, "specified mcastaddr %s (0x%08x) " + "does not contain a multicast address", + inet_ntoa(mcastaddr->sin_addr), + (int)ntohl(mcastaddr->sin_addr.s_addr)); + return -1; + } + + fd = qemu_socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + error_setg_errno(errp, errno, "can't create datagram socket"); + return -1; + } + + /* + * Allow multiple sockets to bind the same multicast ip and port by setting + * SO_REUSEADDR. This is the only situation where SO_REUSEADDR should be set + * on windows. Use socket_set_fast_reuse otherwise as it sets SO_REUSEADDR + * only on posix systems. + */ + val = 1; + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + if (ret < 0) { + error_setg_errno(errp, errno, "can't set socket option SO_REUSEADDR"); + goto fail; + } + + ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr)); + if (ret < 0) { + error_setg_errno(errp, errno, "can't bind ip=%s to socket", + inet_ntoa(mcastaddr->sin_addr)); + goto fail; + } + + /* Add host to multicast group */ + imr.imr_multiaddr = mcastaddr->sin_addr; + if (localaddr) { + imr.imr_interface = *localaddr; + } else { + imr.imr_interface.s_addr = htonl(INADDR_ANY); + } + + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &imr, sizeof(struct ip_mreq)); + if (ret < 0) { + error_setg_errno(errp, errno, + "can't add socket to multicast group %s", + inet_ntoa(imr.imr_multiaddr)); + goto fail; + } + + /* Force mcast msgs to loopback (eg. several QEMUs in same host */ + loop = 1; + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + &loop, sizeof(loop)); + if (ret < 0) { + error_setg_errno(errp, errno, + "can't force multicast message to loopback"); + goto fail; + } + + /* If a bind address is given, only send packets from that address */ + if (localaddr != NULL) { + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, + localaddr, sizeof(*localaddr)); + if (ret < 0) { + error_setg_errno(errp, errno, + "can't set the default network send interface"); + goto fail; + } + } + + qemu_socket_set_nonblock(fd); + return fd; +fail: + if (fd >= 0) { + close(fd); + } + return -1; +} + +static void net_dgram_cleanup(NetClientState *nc) +{ + NetDgramState *s = DO_UPCAST(NetDgramState, nc, nc); + if (s->fd != -1) { + net_dgram_read_poll(s, false); + net_dgram_write_poll(s, false); + close(s->fd); + s->fd = -1; + } + g_free(s->dest_addr); + s->dest_addr = NULL; + s->dest_len = 0; +} + +static NetClientInfo net_dgram_socket_info = { + .type = NET_CLIENT_DRIVER_DGRAM, + .size = sizeof(NetDgramState), + .receive = net_dgram_receive, + .cleanup = net_dgram_cleanup, +}; + +static NetDgramState *net_dgram_fd_init(NetClientState *peer, + const char *model, + const char *name, + int fd, + Error **errp) +{ + NetClientState *nc; + NetDgramState *s; + + nc = qemu_new_net_client(&net_dgram_socket_info, peer, model, name); + + s = DO_UPCAST(NetDgramState, nc, nc); + + s->fd = fd; + net_socket_rs_init(&s->rs, net_dgram_rs_finalize, false); + net_dgram_read_poll(s, true); + + return s; +} + +static int net_dgram_mcast_init(NetClientState *peer, + const char *model, + const char *name, + SocketAddress *remote, + SocketAddress *local, + Error **errp) +{ + NetDgramState *s; + int fd, ret; + struct sockaddr_in *saddr; + + if (remote->type != SOCKET_ADDRESS_TYPE_INET) { + error_setg(errp, "multicast only support inet type"); + return -1; + } + + saddr = g_new(struct sockaddr_in, 1); + if (convert_host_port(saddr, remote->u.inet.host, remote->u.inet.port, + errp) < 0) { + g_free(saddr); + return -1; + } + + if (!local) { + fd = net_dgram_mcast_create(saddr, NULL, errp); + if (fd < 0) { + g_free(saddr); + return -1; + } + } else { + switch (local->type) { + case SOCKET_ADDRESS_TYPE_INET: { + struct in_addr localaddr; + + if (inet_aton(local->u.inet.host, &localaddr) == 0) { + g_free(saddr); + error_setg(errp, "localaddr '%s' is not a valid IPv4 address", + local->u.inet.host); + return -1; + } + + fd = net_dgram_mcast_create(saddr, &localaddr, errp); + if (fd < 0) { + g_free(saddr); + return -1; + } + break; + } + case SOCKET_ADDRESS_TYPE_FD: { + int newfd; + + fd = monitor_fd_param(monitor_cur(), local->u.fd.str, errp); + if (fd == -1) { + g_free(saddr); + return -1; + } + ret = qemu_socket_try_set_nonblock(fd); + if (ret < 0) { + g_free(saddr); + error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", + name, fd); + return -1; + } + + /* + * fd passed: multicast: "learn" dest_addr address from bound + * address and save it. Because this may be "shared" socket from a + * "master" process, datagrams would be recv() by ONLY ONE process: + * we must "clone" this dgram socket --jjo + */ + + saddr = g_new(struct sockaddr_in, 1); + + if (convert_host_port(saddr, local->u.inet.host, local->u.inet.port, + errp) < 0) { + g_free(saddr); + close(fd); + return -1; + } + + /* must be bound */ + if (saddr->sin_addr.s_addr == 0) { + error_setg(errp, "can't setup multicast destination address"); + g_free(saddr); + close(fd); + return -1; + } + /* clone dgram socket */ + newfd = net_dgram_mcast_create(saddr, NULL, errp); + if (newfd < 0) { + g_free(saddr); + close(fd); + return -1; + } + /* clone newfd to fd, close newfd */ + dup2(newfd, fd); + close(newfd); + break; + } + default: + g_free(saddr); + error_setg(errp, "only support inet or fd type for local"); + return -1; + } + } + + s = net_dgram_fd_init(peer, model, name, fd, errp); + if (!s) { + g_free(saddr); + return -1; + } + + g_assert(s->dest_addr == NULL); + s->dest_addr = (struct sockaddr *)saddr; + s->dest_len = sizeof(*saddr); + + if (!local) { + qemu_set_info_str(&s->nc, "mcast=%s:%d", + inet_ntoa(saddr->sin_addr), + ntohs(saddr->sin_port)); + } else { + switch (local->type) { + case SOCKET_ADDRESS_TYPE_INET: + qemu_set_info_str(&s->nc, "mcast=%s:%d", + inet_ntoa(saddr->sin_addr), + ntohs(saddr->sin_port)); + break; + case SOCKET_ADDRESS_TYPE_FD: + qemu_set_info_str(&s->nc, "fd=%d (cloned mcast=%s:%d)", + fd, inet_ntoa(saddr->sin_addr), + ntohs(saddr->sin_port)); + break; + default: + g_assert_not_reached(); + } + } + + return 0; + +} + + +int net_init_dgram(const Netdev *netdev, const char *name, + NetClientState *peer, Error **errp) +{ + NetDgramState *s; + int fd, ret; + SocketAddress *remote, *local; + struct sockaddr *dest_addr; + struct sockaddr_in laddr_in, raddr_in; + struct sockaddr_un laddr_un, raddr_un; + socklen_t dest_len; + + assert(netdev->type == NET_CLIENT_DRIVER_DGRAM); + + remote = netdev->u.dgram.remote; + local = netdev->u.dgram.local; + + /* detect multicast address */ + if (remote && remote->type == SOCKET_ADDRESS_TYPE_INET) { + struct sockaddr_in mcastaddr; + + if (convert_host_port(&mcastaddr, remote->u.inet.host, + remote->u.inet.port, errp) < 0) { + return -1; + } + + if (IN_MULTICAST(ntohl(mcastaddr.sin_addr.s_addr))) { + return net_dgram_mcast_init(peer, "dram", name, remote, local, + errp); + } + } + + /* unicast address */ + if (!local) { + error_setg(errp, "dgram requires local= parameter"); + return -1; + } + + if (remote) { + if (local->type == SOCKET_ADDRESS_TYPE_FD) { + error_setg(errp, "don't set remote with local.fd"); + return -1; + } + if (remote->type != local->type) { + error_setg(errp, "remote and local types must be the same"); + return -1; + } + } else { + if (local->type != SOCKET_ADDRESS_TYPE_FD) { + error_setg(errp, + "type=inet or type=unix requires remote parameter"); + return -1; + } + } + + switch (local->type) { + case SOCKET_ADDRESS_TYPE_INET: + if (convert_host_port(&laddr_in, local->u.inet.host, local->u.inet.port, + errp) < 0) { + return -1; + } + + if (convert_host_port(&raddr_in, remote->u.inet.host, + remote->u.inet.port, errp) < 0) { + return -1; + } + + fd = qemu_socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + error_setg_errno(errp, errno, "can't create datagram socket"); + return -1; + } + + ret = socket_set_fast_reuse(fd); + if (ret < 0) { + error_setg_errno(errp, errno, + "can't set socket option SO_REUSEADDR"); + close(fd); + return -1; + } + ret = bind(fd, (struct sockaddr *)&laddr_in, sizeof(laddr_in)); + if (ret < 0) { + error_setg_errno(errp, errno, "can't bind ip=%s to socket", + inet_ntoa(laddr_in.sin_addr)); + close(fd); + return -1; + } + qemu_socket_set_nonblock(fd); + + dest_len = sizeof(raddr_in); + dest_addr = g_malloc(dest_len); + memcpy(dest_addr, &raddr_in, dest_len); + break; + case SOCKET_ADDRESS_TYPE_UNIX: + ret = unlink(local->u.q_unix.path); + if (ret < 0 && errno != ENOENT) { + error_setg_errno(errp, errno, "failed to unlink socket %s", + local->u.q_unix.path); + return -1; + } + + laddr_un.sun_family = PF_UNIX; + ret = snprintf(laddr_un.sun_path, sizeof(laddr_un.sun_path), "%s", + local->u.q_unix.path); + if (ret < 0 || ret >= sizeof(laddr_un.sun_path)) { + error_setg(errp, "UNIX socket path '%s' is too long", + local->u.q_unix.path); + error_append_hint(errp, "Path must be less than %zu bytes\n", + sizeof(laddr_un.sun_path)); + } + + raddr_un.sun_family = PF_UNIX; + ret = snprintf(raddr_un.sun_path, sizeof(raddr_un.sun_path), "%s", + remote->u.q_unix.path); + if (ret < 0 || ret >= sizeof(raddr_un.sun_path)) { + error_setg(errp, "UNIX socket path '%s' is too long", + remote->u.q_unix.path); + error_append_hint(errp, "Path must be less than %zu bytes\n", + sizeof(raddr_un.sun_path)); + } + + fd = qemu_socket(PF_UNIX, SOCK_DGRAM, 0); + if (fd < 0) { + error_setg_errno(errp, errno, "can't create datagram socket"); + return -1; + } + + ret = bind(fd, (struct sockaddr *)&laddr_un, sizeof(laddr_un)); + if (ret < 0) { + error_setg_errno(errp, errno, "can't bind unix=%s to socket", + laddr_un.sun_path); + close(fd); + return -1; + } + qemu_socket_set_nonblock(fd); + + dest_len = sizeof(raddr_un); + dest_addr = g_malloc(dest_len); + memcpy(dest_addr, &raddr_un, dest_len); + break; + case SOCKET_ADDRESS_TYPE_FD: + fd = monitor_fd_param(monitor_cur(), local->u.fd.str, errp); + if (fd == -1) { + return -1; + } + ret = qemu_socket_try_set_nonblock(fd); + if (ret < 0) { + error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", + name, fd); + return -1; + } + dest_addr = NULL; + dest_len = 0; + break; + default: + error_setg(errp, "only support inet or fd type for local"); + return -1; + } + + s = net_dgram_fd_init(peer, "dgram", name, fd, errp); + if (!s) { + return -1; + } + + if (remote) { + g_assert(s->dest_addr == NULL); + s->dest_addr = dest_addr; + s->dest_len = dest_len; + } + + switch (local->type) { + case SOCKET_ADDRESS_TYPE_INET: + qemu_set_info_str(&s->nc, "udp=%s:%d/%s:%d", + inet_ntoa(laddr_in.sin_addr), + ntohs(laddr_in.sin_port), + inet_ntoa(raddr_in.sin_addr), + ntohs(raddr_in.sin_port)); + break; + case SOCKET_ADDRESS_TYPE_UNIX: + qemu_set_info_str(&s->nc, "udp=%s:%s", + laddr_un.sun_path, raddr_un.sun_path); + break; + case SOCKET_ADDRESS_TYPE_FD: { + SocketAddress *sa; + SocketAddressType sa_type; + + sa = socket_local_address(fd, errp); + if (sa) { + sa_type = sa->type; + qapi_free_SocketAddress(sa); + + qemu_set_info_str(&s->nc, "fd=%d %s", fd, + SocketAddressType_str(sa_type)); + } else { + qemu_set_info_str(&s->nc, "fd=%d", fd); + } + break; + } + default: + g_assert_not_reached(); + } + + return 0; +} diff --git a/net/dump.c b/net/dump.c index 6a63b15359..16073f2458 100644 --- a/net/dump.c +++ b/net/dump.c @@ -61,13 +61,14 @@ struct pcap_sf_pkthdr { uint32_t len; }; -static ssize_t dump_receive_iov(DumpState *s, const struct iovec *iov, int cnt) +static ssize_t dump_receive_iov(DumpState *s, const struct iovec *iov, int cnt, + int offset) { struct pcap_sf_pkthdr hdr; int64_t ts; int caplen; - size_t size = iov_size(iov, cnt); - struct iovec dumpiov[cnt + 1]; + size_t size = iov_size(iov, cnt) - offset; + g_autofree struct iovec *dumpiov = g_new(struct iovec, cnt + 1); /* Early return in case of previous error. */ if (s->fd < 0) { @@ -84,7 +85,7 @@ static ssize_t dump_receive_iov(DumpState *s, const struct iovec *iov, int cnt) dumpiov[0].iov_base = &hdr; dumpiov[0].iov_len = sizeof(hdr); - cnt = iov_copy(&dumpiov[1], cnt, iov, cnt, 0, caplen); + cnt = iov_copy(&dumpiov[1], cnt, iov, cnt, offset, caplen); if (writev(s->fd, dumpiov, cnt + 1) != sizeof(hdr) + caplen) { error_report("network dump write error - stopping dump"); @@ -153,8 +154,10 @@ static ssize_t filter_dump_receive_iov(NetFilterState *nf, NetClientState *sndr, int iovcnt, NetPacketSent *sent_cb) { NetFilterDumpState *nfds = FILTER_DUMP(nf); + int offset = qemu_get_using_vnet_hdr(nf->netdev) ? + qemu_get_vnet_hdr_len(nf->netdev) : 0; - dump_receive_iov(&nfds->ds, iov, iovcnt); + dump_receive_iov(&nfds->ds, iov, iovcnt, offset); return 0; } diff --git a/net/eth.c b/net/eth.c index f074b2f9f3..3f680cc033 100644 --- a/net/eth.c +++ b/net/eth.c @@ -21,26 +21,16 @@ #include "net/checksum.h" #include "net/tap.h" -void eth_setup_vlan_headers_ex(struct eth_header *ehdr, uint16_t vlan_tag, - uint16_t vlan_ethtype, bool *is_new) +void eth_setup_vlan_headers(struct eth_header *ehdr, size_t *ehdr_size, + uint16_t vlan_tag, uint16_t vlan_ethtype) { struct vlan_header *vhdr = PKT_GET_VLAN_HDR(ehdr); - switch (be16_to_cpu(ehdr->h_proto)) { - case ETH_P_VLAN: - case ETH_P_DVLAN: - /* vlan hdr exists */ - *is_new = false; - break; - - default: - /* No VLAN header, put a new one */ - vhdr->h_proto = ehdr->h_proto; - ehdr->h_proto = cpu_to_be16(vlan_ethtype); - *is_new = true; - break; - } + memmove(vhdr + 1, vhdr, *ehdr_size - ETH_HLEN); vhdr->h_tci = cpu_to_be16(vlan_tag); + vhdr->h_proto = ehdr->h_proto; + ehdr->h_proto = cpu_to_be16(vlan_ethtype); + *ehdr_size += sizeof(*vhdr); } uint8_t @@ -136,9 +126,8 @@ _eth_tcp_has_data(bool is_ip4, return l4len > TCP_HEADER_DATA_OFFSET(tcp); } -void eth_get_protocols(const struct iovec *iov, int iovcnt, - bool *isip4, bool *isip6, - bool *isudp, bool *istcp, +void eth_get_protocols(const struct iovec *iov, size_t iovcnt, size_t iovoff, + bool *hasip4, bool *hasip6, size_t *l3hdr_off, size_t *l4hdr_off, size_t *l5hdr_off, @@ -148,96 +137,94 @@ void eth_get_protocols(const struct iovec *iov, int iovcnt, { int proto; bool fragment = false; - size_t l2hdr_len = eth_get_l2_hdr_length_iov(iov, iovcnt); size_t input_size = iov_size(iov, iovcnt); size_t copied; + uint8_t ip_p; - *isip4 = *isip6 = *isudp = *istcp = false; + *hasip4 = *hasip6 = false; + *l3hdr_off = iovoff + eth_get_l2_hdr_length_iov(iov, iovcnt, iovoff); + l4hdr_info->proto = ETH_L4_HDR_PROTO_INVALID; - proto = eth_get_l3_proto(iov, iovcnt, l2hdr_len); - - *l3hdr_off = l2hdr_len; + proto = eth_get_l3_proto(iov, iovcnt, *l3hdr_off); if (proto == ETH_P_IP) { struct ip_header *iphdr = &ip4hdr_info->ip4_hdr; - if (input_size < l2hdr_len) { + if (input_size < *l3hdr_off) { return; } - copied = iov_to_buf(iov, iovcnt, l2hdr_len, iphdr, sizeof(*iphdr)); - - *isip4 = true; - - if (copied < sizeof(*iphdr)) { + copied = iov_to_buf(iov, iovcnt, *l3hdr_off, iphdr, sizeof(*iphdr)); + if (copied < sizeof(*iphdr) || + IP_HEADER_VERSION(iphdr) != IP_HEADER_VERSION_4) { return; } - if (IP_HEADER_VERSION(iphdr) == IP_HEADER_VERSION_4) { - if (iphdr->ip_p == IP_PROTO_TCP) { - *istcp = true; - } else if (iphdr->ip_p == IP_PROTO_UDP) { - *isudp = true; - } - } - + *hasip4 = true; + ip_p = iphdr->ip_p; ip4hdr_info->fragment = IP4_IS_FRAGMENT(iphdr); - *l4hdr_off = l2hdr_len + IP_HDR_GET_LEN(iphdr); + *l4hdr_off = *l3hdr_off + IP_HDR_GET_LEN(iphdr); fragment = ip4hdr_info->fragment; } else if (proto == ETH_P_IPV6) { - - *isip6 = true; - if (eth_parse_ipv6_hdr(iov, iovcnt, l2hdr_len, - ip6hdr_info)) { - if (ip6hdr_info->l4proto == IP_PROTO_TCP) { - *istcp = true; - } else if (ip6hdr_info->l4proto == IP_PROTO_UDP) { - *isudp = true; - } - } else { + if (!eth_parse_ipv6_hdr(iov, iovcnt, *l3hdr_off, ip6hdr_info)) { return; } - *l4hdr_off = l2hdr_len + ip6hdr_info->full_hdr_len; + *hasip6 = true; + ip_p = ip6hdr_info->l4proto; + *l4hdr_off = *l3hdr_off + ip6hdr_info->full_hdr_len; fragment = ip6hdr_info->fragment; + } else { + return; } - if (!fragment) { - if (*istcp) { - *istcp = _eth_copy_chunk(input_size, - iov, iovcnt, - *l4hdr_off, sizeof(l4hdr_info->hdr.tcp), - &l4hdr_info->hdr.tcp); + if (fragment) { + return; + } - if (*istcp) { - *l5hdr_off = *l4hdr_off + - TCP_HEADER_DATA_OFFSET(&l4hdr_info->hdr.tcp); + switch (ip_p) { + case IP_PROTO_TCP: + if (_eth_copy_chunk(input_size, + iov, iovcnt, + *l4hdr_off, sizeof(l4hdr_info->hdr.tcp), + &l4hdr_info->hdr.tcp)) { + l4hdr_info->proto = ETH_L4_HDR_PROTO_TCP; + *l5hdr_off = *l4hdr_off + + TCP_HEADER_DATA_OFFSET(&l4hdr_info->hdr.tcp); - l4hdr_info->has_tcp_data = - _eth_tcp_has_data(proto == ETH_P_IP, - &ip4hdr_info->ip4_hdr, - &ip6hdr_info->ip6_hdr, - *l4hdr_off - *l3hdr_off, - &l4hdr_info->hdr.tcp); - } - } else if (*isudp) { - *isudp = _eth_copy_chunk(input_size, - iov, iovcnt, - *l4hdr_off, sizeof(l4hdr_info->hdr.udp), - &l4hdr_info->hdr.udp); + l4hdr_info->has_tcp_data = + _eth_tcp_has_data(proto == ETH_P_IP, + &ip4hdr_info->ip4_hdr, + &ip6hdr_info->ip6_hdr, + *l4hdr_off - *l3hdr_off, + &l4hdr_info->hdr.tcp); + } + break; + + case IP_PROTO_UDP: + if (_eth_copy_chunk(input_size, + iov, iovcnt, + *l4hdr_off, sizeof(l4hdr_info->hdr.udp), + &l4hdr_info->hdr.udp)) { + l4hdr_info->proto = ETH_L4_HDR_PROTO_UDP; *l5hdr_off = *l4hdr_off + sizeof(l4hdr_info->hdr.udp); } + break; + + case IP_PROTO_SCTP: + l4hdr_info->proto = ETH_L4_HDR_PROTO_SCTP; + break; } } size_t eth_strip_vlan(const struct iovec *iov, int iovcnt, size_t iovoff, - uint8_t *new_ehdr_buf, + void *new_ehdr_buf, uint16_t *payload_offset, uint16_t *tci) { struct vlan_header vlan_hdr; - struct eth_header *new_ehdr = (struct eth_header *) new_ehdr_buf; + struct eth_header *new_ehdr = new_ehdr_buf; size_t copied = iov_to_buf(iov, iovcnt, iovoff, new_ehdr, sizeof(*new_ehdr)); @@ -282,63 +269,50 @@ eth_strip_vlan(const struct iovec *iov, int iovcnt, size_t iovoff, } size_t -eth_strip_vlan_ex(const struct iovec *iov, int iovcnt, size_t iovoff, - uint16_t vet, uint8_t *new_ehdr_buf, +eth_strip_vlan_ex(const struct iovec *iov, int iovcnt, size_t iovoff, int index, + uint16_t vet, uint16_t vet_ext, void *new_ehdr_buf, uint16_t *payload_offset, uint16_t *tci) { struct vlan_header vlan_hdr; - struct eth_header *new_ehdr = (struct eth_header *) new_ehdr_buf; + uint16_t *new_ehdr_proto; + size_t new_ehdr_size; + size_t copied; - size_t copied = iov_to_buf(iov, iovcnt, iovoff, - new_ehdr, sizeof(*new_ehdr)); + switch (index) { + case 0: + new_ehdr_proto = &PKT_GET_ETH_HDR(new_ehdr_buf)->h_proto; + new_ehdr_size = sizeof(struct eth_header); + copied = iov_to_buf(iov, iovcnt, iovoff, new_ehdr_buf, new_ehdr_size); + break; - if (copied < sizeof(*new_ehdr)) { + case 1: + new_ehdr_proto = &PKT_GET_VLAN_HDR(new_ehdr_buf)->h_proto; + new_ehdr_size = sizeof(struct eth_header) + sizeof(struct vlan_header); + copied = iov_to_buf(iov, iovcnt, iovoff, new_ehdr_buf, new_ehdr_size); + if (be16_to_cpu(PKT_GET_ETH_HDR(new_ehdr_buf)->h_proto) != vet_ext) { + return 0; + } + break; + + default: return 0; } - if (be16_to_cpu(new_ehdr->h_proto) == vet) { - copied = iov_to_buf(iov, iovcnt, iovoff + sizeof(*new_ehdr), - &vlan_hdr, sizeof(vlan_hdr)); - - if (copied < sizeof(vlan_hdr)) { - return 0; - } - - new_ehdr->h_proto = vlan_hdr.h_proto; - - *tci = be16_to_cpu(vlan_hdr.h_tci); - *payload_offset = iovoff + sizeof(*new_ehdr) + sizeof(vlan_hdr); - return sizeof(struct eth_header); + if (copied < new_ehdr_size || be16_to_cpu(*new_ehdr_proto) != vet) { + return 0; } - return 0; -} - -void -eth_setup_ip4_fragmentation(const void *l2hdr, size_t l2hdr_len, - void *l3hdr, size_t l3hdr_len, - size_t l3payload_len, - size_t frag_offset, bool more_frags) -{ - const struct iovec l2vec = { - .iov_base = (void *) l2hdr, - .iov_len = l2hdr_len - }; - - if (eth_get_l3_proto(&l2vec, 1, l2hdr_len) == ETH_P_IP) { - uint16_t orig_flags; - struct ip_header *iphdr = (struct ip_header *) l3hdr; - uint16_t frag_off_units = frag_offset / IP_FRAG_UNIT_SIZE; - uint16_t new_ip_off; - - assert(frag_offset % IP_FRAG_UNIT_SIZE == 0); - assert((frag_off_units & ~IP_OFFMASK) == 0); - - orig_flags = be16_to_cpu(iphdr->ip_off) & ~(IP_OFFMASK|IP_MF); - new_ip_off = frag_off_units | orig_flags | (more_frags ? IP_MF : 0); - iphdr->ip_off = cpu_to_be16(new_ip_off); - iphdr->ip_len = cpu_to_be16(l3payload_len + l3hdr_len); + copied = iov_to_buf(iov, iovcnt, iovoff + new_ehdr_size, + &vlan_hdr, sizeof(vlan_hdr)); + if (copied < sizeof(vlan_hdr)) { + return 0; } + + *new_ehdr_proto = vlan_hdr.h_proto; + *payload_offset = iovoff + new_ehdr_size + sizeof(vlan_hdr); + *tci = be16_to_cpu(vlan_hdr.h_tci); + + return new_ehdr_size; } void @@ -458,8 +432,6 @@ _eth_get_rss_ex_src_addr(const struct iovec *pkt, int pkt_frags, } if (opthdr.type == IP6_OPT_HOME) { - size_t input_size = iov_size(pkt, pkt_frags); - if (input_size < opt_offset + sizeof(opthdr)) { return false; } diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c index bf05023dc3..c18c4c2019 100644 --- a/net/filter-rewriter.c +++ b/net/filter-rewriter.c @@ -383,7 +383,7 @@ static void colo_rewriter_setup(NetFilterState *nf, Error **errp) s->connection_track_table = g_hash_table_new_full(connection_key_hash, connection_key_equal, g_free, - connection_destroy); + NULL); s->incoming_queue = qemu_new_net_queue(qemu_netfilter_pass_to_next, nf); } diff --git a/net/filter.c b/net/filter.c index 3fe88fa43f..3335908771 100644 --- a/net/filter.c +++ b/net/filter.c @@ -91,8 +91,8 @@ ssize_t qemu_netfilter_pass_to_next(NetClientState *sender, next = netfilter_next(nf, direction); while (next) { /* - * if qemu_netfilter_pass_to_next been called, means that - * the packet has been hold by filter and has already retured size + * if qemu_netfilter_pass_to_next has been called, it means that + * the packet was held by a filter and has already returned size * to the sender, so sent_cb shouldn't be called later, just * pass NULL to next. */ @@ -106,7 +106,7 @@ ssize_t qemu_netfilter_pass_to_next(NetClientState *sender, /* * We have gone through all filters, pass it to receiver. - * Do the valid check again incase sender or receiver been + * Do the valid check again in case sender or receiver been * deleted while we go through filters. */ if (sender && sender->peer) { diff --git a/net/hub.c b/net/hub.c index 1375738bf1..4c8a469a50 100644 --- a/net/hub.c +++ b/net/hub.c @@ -274,7 +274,7 @@ int net_init_hubport(const Netdev *netdev, const char *name, assert(!peer); hubport = &netdev->u.hubport; - if (hubport->has_netdev) { + if (hubport->netdev) { hubpeer = qemu_find_netdev(hubport->netdev); if (!hubpeer) { error_setg(errp, "netdev '%s' not found", hubport->netdev); @@ -313,6 +313,8 @@ void net_hub_check_clients(void) case NET_CLIENT_DRIVER_USER: case NET_CLIENT_DRIVER_TAP: case NET_CLIENT_DRIVER_SOCKET: + case NET_CLIENT_DRIVER_STREAM: + case NET_CLIENT_DRIVER_DGRAM: case NET_CLIENT_DRIVER_VDE: case NET_CLIENT_DRIVER_VHOST_USER: has_host_dev = 1; diff --git a/net/l2tpv3.c b/net/l2tpv3.c index af373e5c30..b5547cb917 100644 --- a/net/l2tpv3.c +++ b/net/l2tpv3.c @@ -42,7 +42,7 @@ */ #define BUFFER_ALIGN sysconf(_SC_PAGESIZE) -#define BUFFER_SIZE 2048 +#define BUFFER_SIZE 16384 #define IOVSIZE 2 #define MAX_L2TPV3_MSGCNT 64 #define MAX_L2TPV3_IOVCNT (MAX_L2TPV3_MSGCNT * IOVSIZE) @@ -240,9 +240,7 @@ static ssize_t net_l2tpv3_receive_dgram_iov(NetClientState *nc, message.msg_control = NULL; message.msg_controllen = 0; message.msg_flags = 0; - do { - ret = sendmsg(s->fd, &message, 0); - } while ((ret == -1) && (errno == EINTR)); + ret = RETRY_ON_EINTR(sendmsg(s->fd, &message, 0)); if (ret > 0) { ret -= s->offset; } else if (ret == 0) { @@ -285,9 +283,7 @@ static ssize_t net_l2tpv3_receive_dgram(NetClientState *nc, message.msg_control = NULL; message.msg_controllen = 0; message.msg_flags = 0; - do { - ret = sendmsg(s->fd, &message, 0); - } while ((ret == -1) && (errno == EINTR)); + ret = RETRY_ON_EINTR(sendmsg(s->fd, &message, 0)); if (ret > 0) { ret -= s->offset; } else if (ret == 0) { @@ -434,12 +430,9 @@ static void net_l2tpv3_send(void *opaque) msgvec = s->msgvec + s->queue_head; if (target_count > 0) { - do { - count = recvmmsg( - s->fd, - msgvec, - target_count, MSG_DONTWAIT, NULL); - } while ((count == -1) && (errno == EINTR)); + count = RETRY_ON_EINTR( + recvmmsg(s->fd, msgvec, target_count, MSG_DONTWAIT, NULL) + ); if (count < 0) { /* Recv error - we still need to flush packets here, * (re)set queue head to current position @@ -578,7 +571,7 @@ int net_init_l2tpv3(const Netdev *netdev, if (l2tpv3->has_udp && l2tpv3->udp) { s->udp = true; - if (!(l2tpv3->has_srcport && l2tpv3->has_dstport)) { + if (!(l2tpv3->srcport && l2tpv3->dstport)) { error_setg(errp, "need both src and dst port for udp"); goto outerr; } else { @@ -723,8 +716,7 @@ int net_init_l2tpv3(const Netdev *netdev, l2tpv3_read_poll(s, true); - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "l2tpv3: connected"); + qemu_set_info_str(&s->nc, "l2tpv3: connected"); return 0; outerr: qemu_del_net_client(nc); diff --git a/net/meson.build b/net/meson.build index 754e2d1d40..9432a588e4 100644 --- a/net/meson.build +++ b/net/meson.build @@ -1,47 +1,61 @@ -softmmu_ss.add(files( +system_ss.add(files( 'announce.c', 'checksum.c', - 'colo-compare.c', - 'colo.c', 'dump.c', 'eth.c', 'filter-buffer.c', 'filter-mirror.c', - 'filter-rewriter.c', 'filter.c', 'hub.c', + 'net-hmp-cmds.c', 'net.c', 'queue.c', 'socket.c', + 'stream.c', + 'dgram.c', 'util.c', )) -softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('filter-replay.c')) +if get_option('replication').allowed() or \ + get_option('colo_proxy').allowed() + system_ss.add(files('colo-compare.c')) + system_ss.add(files('colo.c')) +endif + +if get_option('colo_proxy').allowed() + system_ss.add(files('filter-rewriter.c')) +endif + +system_ss.add(when: 'CONFIG_TCG', if_true: files('filter-replay.c')) if have_l2tpv3 - softmmu_ss.add(files('l2tpv3.c')) + system_ss.add(files('l2tpv3.c')) endif -softmmu_ss.add(when: slirp, if_true: files('slirp.c')) -softmmu_ss.add(when: vde, if_true: files('vde.c')) +system_ss.add(when: slirp, if_true: files('slirp.c')) +system_ss.add(when: vde, if_true: files('vde.c')) if have_netmap - softmmu_ss.add(files('netmap.c')) -endif -if have_vhost_net_user - softmmu_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost-user.c'), if_false: files('vhost-user-stub.c')) - softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-user-stub.c')) + system_ss.add(files('netmap.c')) endif -softmmu_ss.add(when: 'CONFIG_LINUX', if_true: files('tap-linux.c')) -softmmu_ss.add(when: 'CONFIG_BSD', if_true: files('tap-bsd.c')) -softmmu_ss.add(when: 'CONFIG_SOLARIS', if_true: files('tap-solaris.c')) -tap_posix = ['tap.c'] -if not config_host.has_key('CONFIG_LINUX') and not config_host.has_key('CONFIG_BSD') and not config_host.has_key('CONFIG_SOLARIS') - tap_posix += 'tap-stub.c' +system_ss.add(when: libxdp, if_true: files('af-xdp.c')) + +if have_vhost_net_user + system_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost-user.c'), if_false: files('vhost-user-stub.c')) +endif + +if host_os == 'windows' + system_ss.add(files('tap-win32.c')) +elif host_os == 'linux' + system_ss.add(files('tap.c', 'tap-linux.c')) +elif host_os in bsd_oses + system_ss.add(files('tap.c', 'tap-bsd.c')) +elif host_os == 'sunos' + system_ss.add(files('tap.c', 'tap-solaris.c')) +else + system_ss.add(files('tap.c', 'tap-stub.c')) endif -softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files(tap_posix)) -softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('tap-win32.c')) if have_vhost_net_vdpa - softmmu_ss.add(files('vhost-vdpa.c')) + system_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost-vdpa.c'), if_false: files('vhost-vdpa-stub.c')) endif vmnet_files = files( @@ -50,5 +64,5 @@ vmnet_files = files( 'vmnet-host.c', 'vmnet-shared.c' ) -softmmu_ss.add(when: vmnet, if_true: vmnet_files) +system_ss.add(when: vmnet, if_true: vmnet_files) subdir('can') diff --git a/net/net-hmp-cmds.c b/net/net-hmp-cmds.c new file mode 100644 index 0000000000..41d326bf5f --- /dev/null +++ b/net/net-hmp-cmds.c @@ -0,0 +1,170 @@ +/* + * Human Monitor Interface commands + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "migration/misc.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "net/net.h" +#include "net/hub.h" +#include "qapi/clone-visitor.h" +#include "qapi/qapi-commands-net.h" +#include "qapi/qapi-visit-net.h" +#include "qapi/qmp/qdict.h" +#include "qemu/config-file.h" +#include "qemu/help_option.h" +#include "qemu/option.h" + +void hmp_info_network(Monitor *mon, const QDict *qdict) +{ + NetClientState *nc, *peer; + NetClientDriver type; + + net_hub_info(mon); + + QTAILQ_FOREACH(nc, &net_clients, next) { + peer = nc->peer; + type = nc->info->type; + + /* Skip if already printed in hub info */ + if (net_hub_id_for_client(nc, NULL) == 0) { + continue; + } + + if (!peer || type == NET_CLIENT_DRIVER_NIC) { + print_net_client(mon, nc); + } /* else it's a netdev connected to a NIC, printed with the NIC */ + if (peer && type == NET_CLIENT_DRIVER_NIC) { + monitor_printf(mon, " \\ "); + print_net_client(mon, peer); + } + } +} + +void hmp_set_link(Monitor *mon, const QDict *qdict) +{ + const char *name = qdict_get_str(qdict, "name"); + bool up = qdict_get_bool(qdict, "up"); + Error *err = NULL; + + qmp_set_link(name, up, &err); + hmp_handle_error(mon, err); +} + + +void hmp_announce_self(Monitor *mon, const QDict *qdict) +{ + const char *interfaces_str = qdict_get_try_str(qdict, "interfaces"); + const char *id = qdict_get_try_str(qdict, "id"); + AnnounceParameters *params = QAPI_CLONE(AnnounceParameters, + migrate_announce_params()); + + qapi_free_strList(params->interfaces); + params->interfaces = hmp_split_at_comma(interfaces_str); + params->has_interfaces = params->interfaces != NULL; + params->id = g_strdup(id); + qmp_announce_self(params, NULL); + qapi_free_AnnounceParameters(params); +} + +void hmp_netdev_add(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + QemuOpts *opts; + const char *type = qdict_get_try_str(qdict, "type"); + + if (type && is_help_option(type)) { + show_netdevs(); + return; + } + opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict, &err); + if (err) { + goto out; + } + + netdev_add(opts, &err); + if (err) { + qemu_opts_del(opts); + } + +out: + hmp_handle_error(mon, err); +} + +void hmp_netdev_del(Monitor *mon, const QDict *qdict) +{ + const char *id = qdict_get_str(qdict, "id"); + Error *err = NULL; + + qmp_netdev_del(id, &err); + hmp_handle_error(mon, err); +} + + +void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + int i; + + if (nb_args != 2) { + return; + } + len = strlen(str); + readline_set_completion_index(rs, len); + for (i = 0; i < NET_CLIENT_DRIVER__MAX; i++) { + readline_add_completion_of(rs, str, NetClientDriver_str(i)); + } +} + +void set_link_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + + len = strlen(str); + readline_set_completion_index(rs, len); + if (nb_args == 2) { + NetClientState *ncs[MAX_QUEUE_NUM]; + int count, i; + count = qemu_find_net_clients_except(NULL, ncs, + NET_CLIENT_DRIVER_NONE, + MAX_QUEUE_NUM); + for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { + readline_add_completion_of(rs, str, ncs[i]->name); + } + } else if (nb_args == 3) { + readline_add_completion_of(rs, str, "on"); + readline_add_completion_of(rs, str, "off"); + } +} + +void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str) +{ + int len, count, i; + NetClientState *ncs[MAX_QUEUE_NUM]; + + if (nb_args != 2) { + return; + } + + len = strlen(str); + readline_set_completion_index(rs, len); + count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_DRIVER_NIC, + MAX_QUEUE_NUM); + for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { + if (ncs[i]->is_netdev) { + readline_add_completion_of(rs, str, ncs[i]->name); + } + } +} diff --git a/net/net.c b/net/net.c index 2db160e063..a2f0c828bb 100644 --- a/net/net.c +++ b/net/net.c @@ -48,12 +48,14 @@ #include "qemu/qemu-print.h" #include "qemu/main-loop.h" #include "qemu/option.h" +#include "qemu/keyval.h" #include "qapi/error.h" #include "qapi/opts-visitor.h" #include "sysemu/runstate.h" #include "net/colo-compare.h" #include "net/filter.h" #include "qapi/string-output-visitor.h" +#include "qapi/qobject-input-visitor.h" /* Net bridge is currently not supported for W32. */ #if !defined(_WIN32) @@ -61,20 +63,67 @@ #endif static VMChangeStateEntry *net_change_state_entry; -static QTAILQ_HEAD(, NetClientState) net_clients; +NetClientStateList net_clients; + +typedef struct NetdevQueueEntry { + Netdev *nd; + Location loc; + QSIMPLEQ_ENTRY(NetdevQueueEntry) entry; +} NetdevQueueEntry; + +typedef QSIMPLEQ_HEAD(, NetdevQueueEntry) NetdevQueue; + +static NetdevQueue nd_queue = QSIMPLEQ_HEAD_INITIALIZER(nd_queue); + +static GHashTable *nic_model_help; + +static int nb_nics; +static NICInfo nd_table[MAX_NICS]; /***********************************************************/ /* network device redirectors */ +int convert_host_port(struct sockaddr_in *saddr, const char *host, + const char *port, Error **errp) +{ + struct hostent *he; + const char *r; + long p; + + memset(saddr, 0, sizeof(*saddr)); + + saddr->sin_family = AF_INET; + if (host[0] == '\0') { + saddr->sin_addr.s_addr = 0; + } else { + if (qemu_isdigit(host[0])) { + if (!inet_aton(host, &saddr->sin_addr)) { + error_setg(errp, "host address '%s' is not a valid " + "IPv4 address", host); + return -1; + } + } else { + he = gethostbyname(host); + if (he == NULL) { + error_setg(errp, "can't resolve host address '%s'", host); + return -1; + } + saddr->sin_addr = *(struct in_addr *)he->h_addr; + } + } + if (qemu_strtol(port, &r, 0, &p) != 0) { + error_setg(errp, "port number '%s' is invalid", port); + return -1; + } + saddr->sin_port = htons(p); + return 0; +} + int parse_host_port(struct sockaddr_in *saddr, const char *str, Error **errp) { gchar **substrings; - struct hostent *he; - const char *addr, *p, *r; - int port, ret = 0; - - memset(saddr, 0, sizeof(*saddr)); + int ret; substrings = g_strsplit(str, ":", 2); if (!substrings || !substrings[0] || !substrings[1]) { @@ -84,37 +133,7 @@ int parse_host_port(struct sockaddr_in *saddr, const char *str, goto out; } - addr = substrings[0]; - p = substrings[1]; - - saddr->sin_family = AF_INET; - if (addr[0] == '\0') { - saddr->sin_addr.s_addr = 0; - } else { - if (qemu_isdigit(addr[0])) { - if (!inet_aton(addr, &saddr->sin_addr)) { - error_setg(errp, "host address '%s' is not a valid " - "IPv4 address", addr); - ret = -1; - goto out; - } - } else { - he = gethostbyname(addr); - if (he == NULL) { - error_setg(errp, "can't resolve host address '%s'", addr); - ret = -1; - goto out; - } - saddr->sin_addr = *(struct in_addr *)he->h_addr; - } - } - port = strtol(p, (char **)&r, 0); - if (r == p) { - error_setg(errp, "port number '%s' is invalid", p); - ret = -1; - goto out; - } - saddr->sin_port = htons(port); + ret = convert_host_port(saddr, substrings[0], substrings[1], errp); out: g_strfreev(substrings); @@ -128,13 +147,20 @@ char *qemu_mac_strdup_printf(const uint8_t *macaddr) macaddr[3], macaddr[4], macaddr[5]); } +void qemu_set_info_str(NetClientState *nc, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsnprintf(nc->info_str, sizeof(nc->info_str), fmt, ap); + va_end(ap); +} + void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6]) { - snprintf(nc->info_str, sizeof(nc->info_str), - "model=%s,macaddr=%02x:%02x:%02x:%02x:%02x:%02x", - nc->model, - macaddr[0], macaddr[1], macaddr[2], - macaddr[3], macaddr[4], macaddr[5]); + qemu_set_info_str(nc, "model=%s,macaddr=%02x:%02x:%02x:%02x:%02x:%02x", + nc->model, macaddr[0], macaddr[1], macaddr[2], + macaddr[3], macaddr[4], macaddr[5]); } static int mac_table[256] = {0}; @@ -298,6 +324,7 @@ NICState *qemu_new_nic(NetClientInfo *info, NICConf *conf, const char *model, const char *name, + MemReentrancyGuard *reentrancy_guard, void *opaque) { NetClientState **peers = conf->peers.ncs; @@ -310,6 +337,7 @@ NICState *qemu_new_nic(NetClientInfo *info, nic = g_malloc0(info->size + sizeof(NetClientState) * queues); nic->ncs = (void *)nic + info->size; nic->conf = conf; + nic->reentrancy_guard = reentrancy_guard, nic->opaque = opaque; for (i = 0; i < queues; i++) { @@ -474,6 +502,15 @@ bool qemu_has_ufo(NetClientState *nc) return nc->info->has_ufo(nc); } +bool qemu_has_uso(NetClientState *nc) +{ + if (!nc || !nc->info->has_uso) { + return false; + } + + return nc->info->has_uso(nc); +} + bool qemu_has_vnet_hdr(NetClientState *nc) { if (!nc || !nc->info->has_vnet_hdr) { @@ -492,6 +529,15 @@ bool qemu_has_vnet_hdr_len(NetClientState *nc, int len) return nc->info->has_vnet_hdr_len(nc, len); } +bool qemu_get_using_vnet_hdr(NetClientState *nc) +{ + if (!nc || !nc->info->get_using_vnet_hdr) { + return false; + } + + return nc->info->get_using_vnet_hdr(nc); +} + void qemu_using_vnet_hdr(NetClientState *nc, bool enable) { if (!nc || !nc->info->using_vnet_hdr) { @@ -502,13 +548,22 @@ void qemu_using_vnet_hdr(NetClientState *nc, bool enable) } void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6, - int ecn, int ufo) + int ecn, int ufo, int uso4, int uso6) { if (!nc || !nc->info->set_offload) { return; } - nc->info->set_offload(nc, csum, tso4, tso6, ecn, ufo); + nc->info->set_offload(nc, csum, tso4, tso6, ecn, ufo, uso4, uso6); +} + +int qemu_get_vnet_hdr_len(NetClientState *nc) +{ + if (!nc || !nc->info->get_vnet_hdr_len) { + return 0; + } + + return nc->info->get_vnet_hdr_len(nc); } void qemu_set_vnet_hdr_len(NetClientState *nc, int len) @@ -765,6 +820,7 @@ static ssize_t qemu_deliver_packet_iov(NetClientState *sender, int iovcnt, void *opaque) { + MemReentrancyGuard *owned_reentrancy_guard; NetClientState *nc = opaque; int ret; @@ -777,12 +833,24 @@ static ssize_t qemu_deliver_packet_iov(NetClientState *sender, return 0; } + if (nc->info->type != NET_CLIENT_DRIVER_NIC || + qemu_get_nic(nc)->reentrancy_guard->engaged_in_io) { + owned_reentrancy_guard = NULL; + } else { + owned_reentrancy_guard = qemu_get_nic(nc)->reentrancy_guard; + owned_reentrancy_guard->engaged_in_io = true; + } + if (nc->info->receive_iov && !(flags & QEMU_NET_PACKET_FLAG_RAW)) { ret = nc->info->receive_iov(nc, iov, iovcnt); } else { ret = nc_sendv_compat(nc, iov, iovcnt, flags); } + if (owned_reentrancy_guard) { + owned_reentrancy_guard->engaged_in_io = false; + } + if (ret == 0) { nc->receive_disabled = 1; } @@ -878,49 +946,38 @@ static int nic_get_free_idx(void) return -1; } -int qemu_show_nic_models(const char *arg, const char *const *models) +GPtrArray *qemu_get_nic_models(const char *device_type) { - int i; + GPtrArray *nic_models = g_ptr_array_new(); + GSList *list = object_class_get_list_sorted(device_type, false); - if (!arg || !is_help_option(arg)) { - return 0; + while (list) { + DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, list->data, + TYPE_DEVICE); + GSList *next; + if (test_bit(DEVICE_CATEGORY_NETWORK, dc->categories) && + dc->user_creatable) { + const char *name = object_class_get_name(list->data); + /* + * A network device might also be something else than a NIC, see + * e.g. the "rocker" device. Thus we have to look for the "netdev" + * property, too. Unfortunately, some devices like virtio-net only + * create this property during instance_init, so we have to create + * a temporary instance here to be able to check it. + */ + Object *obj = object_new_with_class(OBJECT_CLASS(dc)); + if (object_property_find(obj, "netdev")) { + g_ptr_array_add(nic_models, (gpointer)name); + } + object_unref(obj); + } + next = list->next; + g_slist_free_1(list); + list = next; } + g_ptr_array_add(nic_models, NULL); - printf("Supported NIC models:\n"); - for (i = 0 ; models[i]; i++) { - printf("%s\n", models[i]); - } - return 1; -} - -void qemu_check_nic_model(NICInfo *nd, const char *model) -{ - const char *models[2]; - - models[0] = model; - models[1] = NULL; - - if (qemu_show_nic_models(nd->model, models)) - exit(0); - if (qemu_find_nic_model(nd, models, model) < 0) - exit(1); -} - -int qemu_find_nic_model(NICInfo *nd, const char * const *models, - const char *default_model) -{ - int i; - - if (!nd->model) - nd->model = g_strdup(default_model); - - for (i = 0 ; models[i]; i++) { - if (strcmp(nd->model, models[i]) == 0) - return i; - } - - error_report("Unsupported NIC model: %s", nd->model); - return -1; + return nic_models; } static int net_init_nic(const Netdev *netdev, const char *name, @@ -943,7 +1000,7 @@ static int net_init_nic(const Netdev *netdev, const char *name, memset(nd, 0, sizeof(*nd)); - if (nic->has_netdev) { + if (nic->netdev) { nd->netdev = qemu_find_netdev(nic->netdev); if (!nd->netdev) { error_setg(errp, "netdev '%s' not found", nic->netdev); @@ -954,19 +1011,19 @@ static int net_init_nic(const Netdev *netdev, const char *name, nd->netdev = peer; } nd->name = g_strdup(name); - if (nic->has_model) { + if (nic->model) { nd->model = g_strdup(nic->model); } - if (nic->has_addr) { + if (nic->addr) { nd->devaddr = g_strdup(nic->addr); } - if (nic->has_macaddr && + if (nic->macaddr && net_parse_macaddr(nd->macaddr.a, nic->macaddr) < 0) { error_setg(errp, "invalid syntax for ethernet address"); return -1; } - if (nic->has_macaddr && + if (nic->macaddr && is_multicast_ether_addr(nd->macaddr.a)) { error_setg(errp, "NIC cannot have multicast MAC address (odd 1st byte)"); @@ -990,6 +1047,192 @@ static int net_init_nic(const Netdev *netdev, const char *name, return idx; } +static gboolean add_nic_result(gpointer key, gpointer value, gpointer user_data) +{ + GPtrArray *results = user_data; + GPtrArray *alias_list = value; + const char *model = key; + char *result; + + if (!alias_list) { + result = g_strdup(model); + } else { + GString *result_str = g_string_new(model); + int i; + + g_string_append(result_str, " (aka "); + for (i = 0; i < alias_list->len; i++) { + if (i) { + g_string_append(result_str, ", "); + } + g_string_append(result_str, alias_list->pdata[i]); + } + g_string_append(result_str, ")"); + result = result_str->str; + g_string_free(result_str, false); + g_ptr_array_unref(alias_list); + } + g_ptr_array_add(results, result); + return true; +} + +static int model_cmp(char **a, char **b) +{ + return strcmp(*a, *b); +} + +static void show_nic_models(void) +{ + GPtrArray *results = g_ptr_array_new(); + int i; + + g_hash_table_foreach_remove(nic_model_help, add_nic_result, results); + g_ptr_array_sort(results, (GCompareFunc)model_cmp); + + printf("Available NIC models for this configuration:\n"); + for (i = 0 ; i < results->len; i++) { + printf("%s\n", (char *)results->pdata[i]); + } + g_hash_table_unref(nic_model_help); + nic_model_help = NULL; +} + +static void add_nic_model_help(const char *model, const char *alias) +{ + GPtrArray *alias_list = NULL; + + if (g_hash_table_lookup_extended(nic_model_help, model, NULL, + (gpointer *)&alias_list)) { + /* Already exists, no alias to add: return */ + if (!alias) { + return; + } + if (alias_list) { + /* Check if this alias is already in the list. Add if not. */ + if (!g_ptr_array_find_with_equal_func(alias_list, alias, + g_str_equal, NULL)) { + g_ptr_array_add(alias_list, g_strdup(alias)); + } + return; + } + } + /* Either this model wasn't in the list already, or a first alias added */ + if (alias) { + alias_list = g_ptr_array_new(); + g_ptr_array_set_free_func(alias_list, g_free); + g_ptr_array_add(alias_list, g_strdup(alias)); + } + g_hash_table_replace(nic_model_help, g_strdup(model), alias_list); +} + +NICInfo *qemu_find_nic_info(const char *typename, bool match_default, + const char *alias) +{ + NICInfo *nd; + int i; + + if (nic_model_help) { + add_nic_model_help(typename, alias); + } + + for (i = 0; i < nb_nics; i++) { + nd = &nd_table[i]; + + if (!nd->used || nd->instantiated) { + continue; + } + + if ((match_default && !nd->model) || !g_strcmp0(nd->model, typename) + || (alias && !g_strcmp0(nd->model, alias))) { + return nd; + } + } + return NULL; +} + + +/* "I have created a device. Please configure it if you can" */ +bool qemu_configure_nic_device(DeviceState *dev, bool match_default, + const char *alias) +{ + NICInfo *nd = qemu_find_nic_info(object_get_typename(OBJECT(dev)), + match_default, alias); + + if (nd) { + qdev_set_nic_properties(dev, nd); + return true; + } + return false; +} + +/* "Please create a device, if you have a configuration for it" */ +DeviceState *qemu_create_nic_device(const char *typename, bool match_default, + const char *alias) +{ + NICInfo *nd = qemu_find_nic_info(typename, match_default, alias); + DeviceState *dev; + + if (!nd) { + return NULL; + } + + dev = qdev_new(typename); + qdev_set_nic_properties(dev, nd); + return dev; +} + +void qemu_create_nic_bus_devices(BusState *bus, const char *parent_type, + const char *default_model, + const char *alias, const char *alias_target) +{ + GPtrArray *nic_models = qemu_get_nic_models(parent_type); + const char *model; + DeviceState *dev; + NICInfo *nd; + int i; + + if (nic_model_help) { + if (alias_target) { + add_nic_model_help(alias_target, alias); + } + for (i = 0; i < nic_models->len - 1; i++) { + add_nic_model_help(nic_models->pdata[i], NULL); + } + } + + /* Drop the NULL terminator which would make g_str_equal() unhappy */ + nic_models->len--; + + for (i = 0; i < nb_nics; i++) { + nd = &nd_table[i]; + + if (!nd->used || nd->instantiated) { + continue; + } + + model = nd->model ? nd->model : default_model; + if (!model) { + continue; + } + + /* Each bus type is allowed *one* substitution */ + if (g_str_equal(model, alias)) { + model = alias_target; + } + + if (!g_ptr_array_find_with_equal_func(nic_models, model, + g_str_equal, NULL)) { + /* This NIC does not live on this bus. */ + continue; + } + + dev = qdev_new(model); + qdev_set_nic_properties(dev, nd); + qdev_realize_and_unref(dev, bus, &error_fatal); + } + + g_ptr_array_free(nic_models, true); +} static int (* const net_client_init_fun[NET_CLIENT_DRIVER__MAX])( const Netdev *netdev, @@ -1001,12 +1244,17 @@ static int (* const net_client_init_fun[NET_CLIENT_DRIVER__MAX])( #endif [NET_CLIENT_DRIVER_TAP] = net_init_tap, [NET_CLIENT_DRIVER_SOCKET] = net_init_socket, + [NET_CLIENT_DRIVER_STREAM] = net_init_stream, + [NET_CLIENT_DRIVER_DGRAM] = net_init_dgram, #ifdef CONFIG_VDE [NET_CLIENT_DRIVER_VDE] = net_init_vde, #endif #ifdef CONFIG_NETMAP [NET_CLIENT_DRIVER_NETMAP] = net_init_netmap, #endif +#ifdef CONFIG_AF_XDP + [NET_CLIENT_DRIVER_AF_XDP] = net_init_af_xdp, +#endif #ifdef CONFIG_NET_BRIDGE [NET_CLIENT_DRIVER_BRIDGE] = net_init_bridge, #endif @@ -1036,25 +1284,29 @@ static int net_client_init1(const Netdev *netdev, bool is_netdev, Error **errp) if (is_netdev) { if (netdev->type == NET_CLIENT_DRIVER_NIC || !net_client_init_fun[netdev->type]) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type", - "a netdev backend type"); + error_setg(errp, "network backend '%s' is not compiled into this binary", + NetClientDriver_str(netdev->type)); return -1; } } else { if (netdev->type == NET_CLIENT_DRIVER_NONE) { return 0; /* nothing to do */ } - if (netdev->type == NET_CLIENT_DRIVER_HUBPORT || - !net_client_init_fun[netdev->type]) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type", - "a net backend type (maybe it is not compiled " - "into this binary)"); + if (netdev->type == NET_CLIENT_DRIVER_HUBPORT) { + error_setg(errp, "network backend '%s' is only supported with -netdev/-nic", + NetClientDriver_str(netdev->type)); + return -1; + } + + if (!net_client_init_fun[netdev->type]) { + error_setg(errp, "network backend '%s' is not compiled into this binary", + NetClientDriver_str(netdev->type)); return -1; } /* Do not add to a hub if it's a nic with a netdev= parameter. */ if (netdev->type != NET_CLIENT_DRIVER_NIC || - !netdev->u.nic.has_netdev) { + !netdev->u.nic.netdev) { peer = net_hub_add_port(0, NULL, NULL); } } @@ -1088,6 +1340,8 @@ void show_netdevs(void) int idx; const char *available_netdevs[] = { "socket", + "stream", + "dgram", "hubport", "tap", #ifdef CONFIG_SLIRP @@ -1105,6 +1359,9 @@ void show_netdevs(void) #ifdef CONFIG_NETMAP "netmap", #endif +#ifdef CONFIG_AF_XDP + "af-xdp", +#endif #ifdef CONFIG_POSIX "vhost-user", #endif @@ -1131,7 +1388,7 @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp) int ret = -1; Visitor *v = opts_visitor_new(opts); - /* Parse convenience option format ip6-net=fec0::0[/64] */ + /* Parse convenience option format ipv6-net=fec0::0[/64] */ const char *ip6_net = qemu_opt_get(opts, "ipv6-net"); if (ip6_net) { @@ -1151,8 +1408,8 @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp) if (substrings[1] && qemu_strtoul(substrings[1], NULL, 10, &prefix_len)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "ipv6-prefixlen", "a number"); + error_setg(errp, + "parameter 'ipv6-net' expects a number after '/'"); goto out; } @@ -1266,8 +1523,7 @@ void print_net_client(Monitor *mon, NetClientState *nc) } } -RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name, - Error **errp) +RxFilterInfoList *qmp_query_rx_filter(const char *name, Error **errp) { NetClientState *nc; RxFilterInfoList *filter_list = NULL, **tail = &filter_list; @@ -1275,13 +1531,13 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name, QTAILQ_FOREACH(nc, &net_clients, next) { RxFilterInfo *info; - if (has_name && strcmp(nc->name, name) != 0) { + if (name && strcmp(nc->name, name) != 0) { continue; } /* only query rx-filter information of NIC */ if (nc->info->type != NET_CLIENT_DRIVER_NIC) { - if (has_name) { + if (name) { error_setg(errp, "net client(%s) isn't a NIC", name); assert(!filter_list); return NULL; @@ -1298,51 +1554,25 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name, if (nc->info->query_rx_filter) { info = nc->info->query_rx_filter(nc); QAPI_LIST_APPEND(tail, info); - } else if (has_name) { + } else if (name) { error_setg(errp, "net client(%s) doesn't support" " rx-filter querying", name); assert(!filter_list); return NULL; } - if (has_name) { + if (name) { break; } } - if (filter_list == NULL && has_name) { + if (filter_list == NULL && name) { error_setg(errp, "invalid net client name: %s", name); } return filter_list; } -void hmp_info_network(Monitor *mon, const QDict *qdict) -{ - NetClientState *nc, *peer; - NetClientDriver type; - - net_hub_info(mon); - - QTAILQ_FOREACH(nc, &net_clients, next) { - peer = nc->peer; - type = nc->info->type; - - /* Skip if already printed in hub info */ - if (net_hub_id_for_client(nc, NULL) == 0) { - continue; - } - - if (!peer || type == NET_CLIENT_DRIVER_NIC) { - print_net_client(mon, nc); - } /* else it's a netdev connected to a NIC, printed with the NIC */ - if (peer && type == NET_CLIENT_DRIVER_NIC) { - monitor_printf(mon, " \\ "); - print_net_client(mon, peer); - } - } -} - void colo_notify_filters_event(int event, Error **errp) { NetClientState *nc; @@ -1430,18 +1660,34 @@ static void net_vm_change_state_handler(void *opaque, bool running, void net_cleanup(void) { - NetClientState *nc; + NetClientState *nc, **p = &QTAILQ_FIRST(&net_clients); /*cleanup colo compare module for COLO*/ colo_compare_cleanup(); - /* We may del multiple entries during qemu_del_net_client(), - * so QTAILQ_FOREACH_SAFE() is also not safe here. + /* + * Walk the net_clients list and remove the netdevs but *not* any + * NET_CLIENT_DRIVER_NIC entries. The latter are owned by the device + * model which created them, and in some cases (e.g. xen-net-device) + * the device itself may do cleanup at exit and will be upset if we + * just delete its NIC from underneath it. + * + * Since qemu_del_net_client() may delete multiple entries, using + * QTAILQ_FOREACH_SAFE() is not safe here. The only safe pointer + * to keep as a bookmark is a NET_CLIENT_DRIVER_NIC entry, so keep + * 'p' pointing to either the head of the list, or the 'next' field + * of the latest NET_CLIENT_DRIVER_NIC, and operate on *p as we walk + * the list. + * + * The 'nc' variable isn't part of the list traversal; it's purely + * for convenience as too much '(*p)->' has a tendency to make the + * readers' eyes bleed. */ - while (!QTAILQ_EMPTY(&net_clients)) { - nc = QTAILQ_FIRST(&net_clients); + while (*p) { + nc = *p; if (nc->info->type == NET_CLIENT_DRIVER_NIC) { - qemu_del_nic(qemu_get_nic(nc)); + /* Skip NET_CLIENT_DRIVER_NIC entries */ + p = &QTAILQ_NEXT(nc, next); } else { qemu_del_net_client(nc); } @@ -1455,6 +1701,10 @@ void net_check_clients(void) NetClientState *nc; int i; + if (nic_model_help) { + show_nic_models(); + exit(0); + } net_hub_check_clients(); QTAILQ_FOREACH(nc, &net_clients, next) { @@ -1506,8 +1756,23 @@ static int net_param_nic(void *dummy, QemuOpts *opts, Error **errp) const char *type; type = qemu_opt_get(opts, "type"); - if (type && g_str_equal(type, "none")) { - return 0; /* Nothing to do, default_net is cleared in vl.c */ + if (type) { + if (g_str_equal(type, "none")) { + return 0; /* Nothing to do, default_net is cleared in vl.c */ + } + if (is_help_option(type)) { + GPtrArray *nic_models = qemu_get_nic_models(TYPE_DEVICE); + int i; + show_netdevs(); + printf("\n"); + printf("Available NIC models " + "(use -nic model=help for a filtered list):\n"); + for (i = 0 ; nic_models->pdata[i]; i++) { + printf("%s\n", (char *)nic_models->pdata[i]); + } + g_ptr_array_free(nic_models, true); + exit(0); + } } idx = nic_get_free_idx(); @@ -1524,6 +1789,12 @@ static int net_param_nic(void *dummy, QemuOpts *opts, Error **errp) memset(ni, 0, sizeof(*ni)); ni->model = qemu_opt_get_del(opts, "model"); + if (!nic_model_help && !g_strcmp0(ni->model, "help")) { + nic_model_help = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); + return 0; + } + /* Create an ID if the user did not specify one */ nd_id = g_strdup(qemu_opts_id(opts)); if (!nd_id) { @@ -1560,36 +1831,97 @@ out: return ret; } -int net_init_clients(Error **errp) +static void netdev_init_modern(void) +{ + while (!QSIMPLEQ_EMPTY(&nd_queue)) { + NetdevQueueEntry *nd = QSIMPLEQ_FIRST(&nd_queue); + + QSIMPLEQ_REMOVE_HEAD(&nd_queue, entry); + loc_push_restore(&nd->loc); + net_client_init1(nd->nd, true, &error_fatal); + loc_pop(&nd->loc); + qapi_free_Netdev(nd->nd); + g_free(nd); + } +} + +void net_init_clients(void) { net_change_state_entry = qemu_add_vm_change_state_handler(net_vm_change_state_handler, NULL); QTAILQ_INIT(&net_clients); - if (qemu_opts_foreach(qemu_find_opts("netdev"), - net_init_netdev, NULL, errp)) { - return -1; - } + netdev_init_modern(); - if (qemu_opts_foreach(qemu_find_opts("nic"), net_param_nic, NULL, errp)) { - return -1; - } + qemu_opts_foreach(qemu_find_opts("netdev"), net_init_netdev, NULL, + &error_fatal); - if (qemu_opts_foreach(qemu_find_opts("net"), net_init_client, NULL, errp)) { - return -1; - } + qemu_opts_foreach(qemu_find_opts("nic"), net_param_nic, NULL, + &error_fatal); - return 0; + qemu_opts_foreach(qemu_find_opts("net"), net_init_client, NULL, + &error_fatal); } -int net_client_parse(QemuOptsList *opts_list, const char *optarg) +/* + * Does this -netdev argument use modern rather than traditional syntax? + * Modern syntax is to be parsed with netdev_parse_modern(). + * Traditional syntax is to be parsed with net_client_parse(). + */ +bool netdev_is_modern(const char *optstr) { - if (!qemu_opts_parse_noisily(opts_list, optarg, true)) { - return -1; + QemuOpts *opts; + bool is_modern; + const char *type; + static QemuOptsList dummy_opts = { + .name = "netdev", + .implied_opt_name = "type", + .head = QTAILQ_HEAD_INITIALIZER(dummy_opts.head), + .desc = { { } }, + }; + + if (optstr[0] == '{') { + /* This is JSON, which means it's modern syntax */ + return true; } - return 0; + opts = qemu_opts_create(&dummy_opts, NULL, false, &error_abort); + qemu_opts_do_parse(opts, optstr, dummy_opts.implied_opt_name, + &error_abort); + type = qemu_opt_get(opts, "type"); + is_modern = !g_strcmp0(type, "stream") || !g_strcmp0(type, "dgram"); + + qemu_opts_reset(&dummy_opts); + + return is_modern; +} + +/* + * netdev_parse_modern() uses modern, more expressive syntax than + * net_client_parse(), but supports only the -netdev option. + * netdev_parse_modern() appends to @nd_queue, whereas net_client_parse() + * appends to @qemu_netdev_opts. + */ +void netdev_parse_modern(const char *optstr) +{ + Visitor *v; + NetdevQueueEntry *nd; + + v = qobject_input_visitor_new_str(optstr, "type", &error_fatal); + nd = g_new(NetdevQueueEntry, 1); + visit_type_Netdev(v, NULL, &nd->nd, &error_fatal); + visit_free(v); + loc_save(&nd->loc); + + QSIMPLEQ_INSERT_TAIL(&nd_queue, nd, entry); +} + +void net_client_parse(QemuOptsList *opts_list, const char *optstr) +{ + if (!qemu_opts_parse_noisily(opts_list, optstr, true)) { + exit(1); + } } /* From FreeBSD */ diff --git a/net/netmap.c b/net/netmap.c index 9e0cec58d3..241b27c8e9 100644 --- a/net/netmap.c +++ b/net/netmap.c @@ -371,7 +371,7 @@ static void netmap_set_vnet_hdr_len(NetClientState *nc, int len) } static void netmap_set_offload(NetClientState *nc, int csum, int tso4, int tso6, - int ecn, int ufo) + int ecn, int ufo, int uso4, int uso6) { NetmapState *s = DO_UPCAST(NetmapState, nc, nc); diff --git a/net/slirp.c b/net/slirp.c index 8679be6444..25b49c4526 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -46,6 +46,7 @@ #include "qapi/qmp/qdict.h" #include "util.h" #include "migration/register.h" +#include "migration/vmstate.h" #include "migration/qemu-file-types.h" static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) @@ -248,12 +249,24 @@ static void net_slirp_timer_mod(void *timer, int64_t expire_timer, static void net_slirp_register_poll_fd(int fd, void *opaque) { - qemu_fd_register(fd); +#ifdef WIN32 + AioContext *ctxt = qemu_get_aio_context(); + + if (WSAEventSelect(fd, event_notifier_get_handle(&ctxt->notifier), + FD_READ | FD_ACCEPT | FD_CLOSE | + FD_CONNECT | FD_WRITE | FD_OOB) != 0) { + error_setg_win32(&error_warn, WSAGetLastError(), "failed to WSAEventSelect()"); + } +#endif } static void net_slirp_unregister_poll_fd(int fd, void *opaque) { - /* no qemu_fd_unregister */ +#ifdef WIN32 + if (WSAEventSelect(fd, NULL, 0) != 0) { + error_setg_win32(&error_warn, WSAGetLastError(), "failed to WSAEventSelect()"); + } +#endif } static void net_slirp_notify(void *opaque) @@ -611,9 +624,8 @@ static int net_slirp_init(NetClientState *peer, const char *model, nc = qemu_new_net_client(&net_slirp_info, peer, model, name); - snprintf(nc->info_str, sizeof(nc->info_str), - "net=%s,restrict=%s", inet_ntoa(net), - restricted ? "on" : "off"); + qemu_set_info_str(nc, "net=%s,restrict=%s", inet_ntoa(net), + restricted ? "on" : "off"); s = DO_UPCAST(SlirpState, nc, nc); @@ -648,8 +660,8 @@ static int net_slirp_init(NetClientState *peer, const char *model, * specific version? */ g_assert(slirp_state_version() == 4); - register_savevm_live("slirp", 0, slirp_state_version(), - &savevm_slirp_state, s->slirp); + register_savevm_live("slirp", VMSTATE_INSTANCE_ID_ANY, + slirp_state_version(), &savevm_slirp_state, s->slirp); s->poll_notifier.notify = net_slirp_poll_notify; main_loop_poll_add_notifier(&s->poll_notifier); @@ -1154,8 +1166,8 @@ int net_init_slirp(const Netdev *netdev, const char *name, ipv6 = 0; } - vnet = user->has_net ? g_strdup(user->net) : - user->has_ip ? g_strdup_printf("%s/24", user->ip) : + vnet = user->net ? g_strdup(user->net) : + user->ip ? g_strdup_printf("%s/24", user->ip) : NULL; dnssearch = slirp_dnssearch(user->dnssearch); diff --git a/net/socket.c b/net/socket.c index bfd8596250..8e3702e1f3 100644 --- a/net/socket.c +++ b/net/socket.c @@ -117,15 +117,13 @@ static ssize_t net_socket_receive_dgram(NetClientState *nc, const uint8_t *buf, NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc); ssize_t ret; - do { - if (s->dgram_dst.sin_family != AF_UNIX) { - ret = sendto(s->fd, buf, size, 0, - (struct sockaddr *)&s->dgram_dst, - sizeof(s->dgram_dst)); - } else { - ret = send(s->fd, buf, size, 0); - } - } while (ret == -1 && errno == EINTR); + ret = RETRY_ON_EINTR( + s->dgram_dst.sin_family != AF_UNIX ? + sendto(s->fd, buf, size, 0, + (struct sockaddr *)&s->dgram_dst, + sizeof(s->dgram_dst)) : + send(s->fd, buf, size, 0) + ); if (ret == -1 && errno == EAGAIN) { net_socket_write_poll(s, true); @@ -174,12 +172,12 @@ static void net_socket_send(void *opaque) if (s->listen_fd != -1) { qemu_set_fd_handler(s->listen_fd, net_socket_accept, NULL, s); } - closesocket(s->fd); + close(s->fd); s->fd = -1; net_socket_rs_init(&s->rs, net_socket_rs_finalize, false); s->nc.link_down = true; - memset(s->nc.info_str, 0, sizeof(s->nc.info_str)); + qemu_set_info_str(&s->nc, "%s", ""); return; } @@ -301,7 +299,7 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, return fd; fail: if (fd >= 0) - closesocket(fd); + close(fd); return -1; } @@ -316,7 +314,7 @@ static void net_socket_cleanup(NetClientState *nc) } if (s->listen_fd != -1) { qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); - closesocket(s->listen_fd); + close(s->listen_fd); s->listen_fd = -1; } } @@ -387,22 +385,21 @@ static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer, /* mcast: save bound address as dst */ if (is_connected && mcast != NULL) { s->dgram_dst = saddr; - snprintf(nc->info_str, sizeof(nc->info_str), - "socket: fd=%d (cloned mcast=%s:%d)", - fd, inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + qemu_set_info_str(nc, "socket: fd=%d (cloned mcast=%s:%d)", fd, + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); } else { if (sa_type == SOCKET_ADDRESS_TYPE_UNIX) { s->dgram_dst.sin_family = AF_UNIX; } - snprintf(nc->info_str, sizeof(nc->info_str), - "socket: fd=%d %s", fd, SocketAddressType_str(sa_type)); + qemu_set_info_str(nc, "socket: fd=%d %s", fd, + SocketAddressType_str(sa_type)); } return s; err: - closesocket(fd); + close(fd); return NULL; } @@ -430,7 +427,7 @@ static NetSocketState *net_socket_fd_init_stream(NetClientState *peer, nc = qemu_new_net_client(&net_socket_info, peer, model, name); - snprintf(nc->info_str, sizeof(nc->info_str), "socket: fd=%d", fd); + qemu_set_info_str(nc, "socket: fd=%d", fd); s = DO_UPCAST(NetSocketState, nc, nc); @@ -449,31 +446,21 @@ static NetSocketState *net_socket_fd_init_stream(NetClientState *peer, return s; } -static NetSocketState *net_socket_fd_init(NetClientState *peer, - const char *model, const char *name, - int fd, int is_connected, - const char *mc, Error **errp) +static int net_socket_fd_check(int fd, Error **errp) { - int so_type = -1, optlen=sizeof(so_type); + int so_type, optlen = sizeof(so_type); - if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type, - (socklen_t *)&optlen)< 0) { + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type, + (socklen_t *)&optlen) < 0) { error_setg(errp, "can't get socket option SO_TYPE"); - closesocket(fd); - return NULL; + return -1; } - switch(so_type) { - case SOCK_DGRAM: - return net_socket_fd_init_dgram(peer, model, name, fd, is_connected, - mc, errp); - case SOCK_STREAM: - return net_socket_fd_init_stream(peer, model, name, fd, is_connected); - default: + if (so_type != SOCK_DGRAM && so_type != SOCK_STREAM) { error_setg(errp, "socket type=%d for fd=%d must be either" " SOCK_DGRAM or SOCK_STREAM", so_type, fd); - closesocket(fd); + return -1; } - return NULL; + return so_type; } static void net_socket_accept(void *opaque) @@ -497,9 +484,8 @@ static void net_socket_accept(void *opaque) s->fd = fd; s->nc.link_down = false; net_socket_connect(s); - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "socket: connection from %s:%d", - inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + qemu_set_info_str(&s->nc, "socket: connection from %s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); } static int net_socket_listen_init(NetClientState *peer, @@ -530,13 +516,13 @@ static int net_socket_listen_init(NetClientState *peer, if (ret < 0) { error_setg_errno(errp, errno, "can't bind ip=%s to socket", inet_ntoa(saddr.sin_addr)); - closesocket(fd); + close(fd); return -1; } ret = listen(fd, 0); if (ret < 0) { error_setg_errno(errp, errno, "can't listen on socket"); - closesocket(fd); + close(fd); return -1; } @@ -579,12 +565,11 @@ static int net_socket_connect_init(NetClientState *peer, if (errno == EINTR || errno == EWOULDBLOCK) { /* continue */ } else if (errno == EINPROGRESS || - errno == EALREADY || - errno == EINVAL) { + errno == EALREADY) { break; } else { error_setg_errno(errp, errno, "can't connect socket"); - closesocket(fd); + close(fd); return -1; } } else { @@ -592,14 +577,13 @@ static int net_socket_connect_init(NetClientState *peer, break; } } - s = net_socket_fd_init(peer, model, name, fd, connected, NULL, errp); + s = net_socket_fd_init_stream(peer, model, name, fd, connected); if (!s) { return -1; } - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "socket: connect to %s:%d", - inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + qemu_set_info_str(&s->nc, "socket: connect to %s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); return 0; } @@ -635,16 +619,15 @@ static int net_socket_mcast_init(NetClientState *peer, return -1; } - s = net_socket_fd_init(peer, model, name, fd, 0, NULL, errp); + s = net_socket_fd_init_dgram(peer, model, name, fd, 0, NULL, errp); if (!s) { return -1; } s->dgram_dst = saddr; - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "socket: mcast=%s:%d", - inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + qemu_set_info_str(&s->nc, "socket: mcast=%s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); return 0; } @@ -678,28 +661,27 @@ static int net_socket_udp_init(NetClientState *peer, if (ret < 0) { error_setg_errno(errp, errno, "can't set socket option SO_REUSEADDR"); - closesocket(fd); + close(fd); return -1; } ret = bind(fd, (struct sockaddr *)&laddr, sizeof(laddr)); if (ret < 0) { error_setg_errno(errp, errno, "can't bind ip=%s to socket", inet_ntoa(laddr.sin_addr)); - closesocket(fd); + close(fd); return -1; } qemu_socket_set_nonblock(fd); - s = net_socket_fd_init(peer, model, name, fd, 0, NULL, errp); + s = net_socket_fd_init_dgram(peer, model, name, fd, 0, NULL, errp); if (!s) { return -1; } s->dgram_dst = raddr; - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "socket: udp=%s:%d", - inet_ntoa(raddr.sin_addr), ntohs(raddr.sin_port)); + qemu_set_info_str(&s->nc, "socket: udp=%s:%d", inet_ntoa(raddr.sin_addr), + ntohs(raddr.sin_port)); return 0; } @@ -711,39 +693,52 @@ int net_init_socket(const Netdev *netdev, const char *name, assert(netdev->type == NET_CLIENT_DRIVER_SOCKET); sock = &netdev->u.socket; - if (sock->has_fd + sock->has_listen + sock->has_connect + sock->has_mcast + - sock->has_udp != 1) { + if (!!sock->fd + !!sock->listen + !!sock->connect + !!sock->mcast + + !!sock->udp != 1) { error_setg(errp, "exactly one of listen=, connect=, mcast= or udp=" " is required"); return -1; } - if (sock->has_localaddr && !sock->has_mcast && !sock->has_udp) { + if (sock->localaddr && !sock->mcast && !sock->udp) { error_setg(errp, "localaddr= is only valid with mcast= or udp="); return -1; } - if (sock->has_fd) { - int fd, ret; + if (sock->fd) { + int fd, ret, so_type; fd = monitor_fd_param(monitor_cur(), sock->fd, errp); if (fd == -1) { return -1; } + so_type = net_socket_fd_check(fd, errp); + if (so_type < 0) { + return -1; + } ret = qemu_socket_try_set_nonblock(fd); if (ret < 0) { error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d", name, fd); return -1; } - if (!net_socket_fd_init(peer, "socket", name, fd, 1, sock->mcast, - errp)) { - return -1; + switch (so_type) { + case SOCK_DGRAM: + if (!net_socket_fd_init_dgram(peer, "socket", name, fd, 1, + sock->mcast, errp)) { + return -1; + } + break; + case SOCK_STREAM: + if (!net_socket_fd_init_stream(peer, "socket", name, fd, 1)) { + return -1; + } + break; } return 0; } - if (sock->has_listen) { + if (sock->listen) { if (net_socket_listen_init(peer, "socket", name, sock->listen, errp) < 0) { return -1; @@ -751,7 +746,7 @@ int net_init_socket(const Netdev *netdev, const char *name, return 0; } - if (sock->has_connect) { + if (sock->connect) { if (net_socket_connect_init(peer, "socket", name, sock->connect, errp) < 0) { return -1; @@ -759,7 +754,7 @@ int net_init_socket(const Netdev *netdev, const char *name, return 0; } - if (sock->has_mcast) { + if (sock->mcast) { /* if sock->localaddr is missing, it has been initialized to "all bits * zero" */ if (net_socket_mcast_init(peer, "socket", name, sock->mcast, @@ -769,8 +764,8 @@ int net_init_socket(const Netdev *netdev, const char *name, return 0; } - assert(sock->has_udp); - if (!sock->has_localaddr) { + assert(sock->udp); + if (!sock->localaddr) { error_setg(errp, "localaddr= is mandatory with udp="); return -1; } diff --git a/net/stream.c b/net/stream.c new file mode 100644 index 0000000000..97e6ec6679 --- /dev/null +++ b/net/stream.c @@ -0,0 +1,445 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2022 Red Hat, Inc. + * + * 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 "net/net.h" +#include "clients.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/option.h" +#include "qemu/sockets.h" +#include "qemu/iov.h" +#include "qemu/main-loop.h" +#include "qemu/cutils.h" +#include "io/channel.h" +#include "io/channel-socket.h" +#include "io/net-listener.h" +#include "qapi/qapi-events-net.h" +#include "qapi/qapi-visit-sockets.h" +#include "qapi/clone-visitor.h" + +typedef struct NetStreamState { + NetClientState nc; + QIOChannel *listen_ioc; + QIONetListener *listener; + QIOChannel *ioc; + guint ioc_read_tag; + guint ioc_write_tag; + SocketReadState rs; + unsigned int send_index; /* number of bytes sent*/ + uint32_t reconnect; + guint timer_tag; + SocketAddress *addr; +} NetStreamState; + +static void net_stream_listen(QIONetListener *listener, + QIOChannelSocket *cioc, + void *opaque); +static void net_stream_arm_reconnect(NetStreamState *s); + +static gboolean net_stream_writable(QIOChannel *ioc, + GIOCondition condition, + gpointer data) +{ + NetStreamState *s = data; + + s->ioc_write_tag = 0; + + qemu_flush_queued_packets(&s->nc); + + return G_SOURCE_REMOVE; +} + +static ssize_t net_stream_receive(NetClientState *nc, const uint8_t *buf, + size_t size) +{ + NetStreamState *s = DO_UPCAST(NetStreamState, nc, nc); + uint32_t len = htonl(size); + struct iovec iov[] = { + { + .iov_base = &len, + .iov_len = sizeof(len), + }, { + .iov_base = (void *)buf, + .iov_len = size, + }, + }; + struct iovec local_iov[2]; + unsigned int nlocal_iov; + size_t remaining; + ssize_t ret; + + remaining = iov_size(iov, 2) - s->send_index; + nlocal_iov = iov_copy(local_iov, 2, iov, 2, s->send_index, remaining); + ret = qio_channel_writev(s->ioc, local_iov, nlocal_iov, NULL); + if (ret == QIO_CHANNEL_ERR_BLOCK) { + ret = 0; /* handled further down */ + } + if (ret == -1) { + s->send_index = 0; + return -errno; + } + if (ret < (ssize_t)remaining) { + s->send_index += ret; + s->ioc_write_tag = qio_channel_add_watch(s->ioc, G_IO_OUT, + net_stream_writable, s, NULL); + return 0; + } + s->send_index = 0; + return size; +} + +static gboolean net_stream_send(QIOChannel *ioc, + GIOCondition condition, + gpointer data); + +static void net_stream_send_completed(NetClientState *nc, ssize_t len) +{ + NetStreamState *s = DO_UPCAST(NetStreamState, nc, nc); + + if (!s->ioc_read_tag) { + s->ioc_read_tag = qio_channel_add_watch(s->ioc, G_IO_IN, + net_stream_send, s, NULL); + } +} + +static void net_stream_rs_finalize(SocketReadState *rs) +{ + NetStreamState *s = container_of(rs, NetStreamState, rs); + + if (qemu_send_packet_async(&s->nc, rs->buf, + rs->packet_len, + net_stream_send_completed) == 0) { + if (s->ioc_read_tag) { + g_source_remove(s->ioc_read_tag); + s->ioc_read_tag = 0; + } + } +} + +static gboolean net_stream_send(QIOChannel *ioc, + GIOCondition condition, + gpointer data) +{ + NetStreamState *s = data; + int size; + int ret; + char buf1[NET_BUFSIZE]; + const char *buf; + + size = qio_channel_read(s->ioc, buf1, sizeof(buf1), NULL); + if (size < 0) { + if (errno != EWOULDBLOCK) { + goto eoc; + } + } else if (size == 0) { + /* end of connection */ + eoc: + s->ioc_read_tag = 0; + if (s->ioc_write_tag) { + g_source_remove(s->ioc_write_tag); + s->ioc_write_tag = 0; + } + if (s->listener) { + qemu_set_info_str(&s->nc, "listening"); + qio_net_listener_set_client_func(s->listener, net_stream_listen, + s, NULL); + } + object_unref(OBJECT(s->ioc)); + s->ioc = NULL; + + net_socket_rs_init(&s->rs, net_stream_rs_finalize, false); + s->nc.link_down = true; + + qapi_event_send_netdev_stream_disconnected(s->nc.name); + net_stream_arm_reconnect(s); + + return G_SOURCE_REMOVE; + } + buf = buf1; + + ret = net_fill_rstate(&s->rs, (const uint8_t *)buf, size); + + if (ret == -1) { + goto eoc; + } + + return G_SOURCE_CONTINUE; +} + +static void net_stream_cleanup(NetClientState *nc) +{ + NetStreamState *s = DO_UPCAST(NetStreamState, nc, nc); + if (s->timer_tag) { + g_source_remove(s->timer_tag); + s->timer_tag = 0; + } + if (s->addr) { + qapi_free_SocketAddress(s->addr); + s->addr = NULL; + } + if (s->ioc) { + if (QIO_CHANNEL_SOCKET(s->ioc)->fd != -1) { + if (s->ioc_read_tag) { + g_source_remove(s->ioc_read_tag); + s->ioc_read_tag = 0; + } + if (s->ioc_write_tag) { + g_source_remove(s->ioc_write_tag); + s->ioc_write_tag = 0; + } + } + object_unref(OBJECT(s->ioc)); + s->ioc = NULL; + } + if (s->listen_ioc) { + if (s->listener) { + qio_net_listener_disconnect(s->listener); + object_unref(OBJECT(s->listener)); + s->listener = NULL; + } + object_unref(OBJECT(s->listen_ioc)); + s->listen_ioc = NULL; + } +} + +static NetClientInfo net_stream_info = { + .type = NET_CLIENT_DRIVER_STREAM, + .size = sizeof(NetStreamState), + .receive = net_stream_receive, + .cleanup = net_stream_cleanup, +}; + +static void net_stream_listen(QIONetListener *listener, + QIOChannelSocket *cioc, + void *opaque) +{ + NetStreamState *s = opaque; + SocketAddress *addr; + char *uri; + + object_ref(OBJECT(cioc)); + + qio_net_listener_set_client_func(s->listener, NULL, s, NULL); + + s->ioc = QIO_CHANNEL(cioc); + qio_channel_set_name(s->ioc, "stream-server"); + s->nc.link_down = false; + + s->ioc_read_tag = qio_channel_add_watch(s->ioc, G_IO_IN, net_stream_send, + s, NULL); + + if (cioc->localAddr.ss_family == AF_UNIX) { + addr = qio_channel_socket_get_local_address(cioc, NULL); + } else { + addr = qio_channel_socket_get_remote_address(cioc, NULL); + } + g_assert(addr != NULL); + uri = socket_uri(addr); + qemu_set_info_str(&s->nc, "%s", uri); + g_free(uri); + qapi_event_send_netdev_stream_connected(s->nc.name, addr); + qapi_free_SocketAddress(addr); +} + +static void net_stream_server_listening(QIOTask *task, gpointer opaque) +{ + NetStreamState *s = opaque; + QIOChannelSocket *listen_sioc = QIO_CHANNEL_SOCKET(s->listen_ioc); + SocketAddress *addr; + int ret; + Error *err = NULL; + + if (qio_task_propagate_error(task, &err)) { + qemu_set_info_str(&s->nc, "error: %s", error_get_pretty(err)); + error_free(err); + return; + } + + addr = qio_channel_socket_get_local_address(listen_sioc, NULL); + g_assert(addr != NULL); + ret = qemu_socket_try_set_nonblock(listen_sioc->fd); + if (addr->type == SOCKET_ADDRESS_TYPE_FD && ret < 0) { + qemu_set_info_str(&s->nc, "can't use file descriptor %s (errno %d)", + addr->u.fd.str, -ret); + return; + } + g_assert(ret == 0); + qapi_free_SocketAddress(addr); + + s->nc.link_down = true; + s->listener = qio_net_listener_new(); + + qemu_set_info_str(&s->nc, "listening"); + net_socket_rs_init(&s->rs, net_stream_rs_finalize, false); + qio_net_listener_set_client_func(s->listener, net_stream_listen, s, NULL); + qio_net_listener_add(s->listener, listen_sioc); +} + +static int net_stream_server_init(NetClientState *peer, + const char *model, + const char *name, + SocketAddress *addr, + Error **errp) +{ + NetClientState *nc; + NetStreamState *s; + QIOChannelSocket *listen_sioc = qio_channel_socket_new(); + + nc = qemu_new_net_client(&net_stream_info, peer, model, name); + s = DO_UPCAST(NetStreamState, nc, nc); + qemu_set_info_str(&s->nc, "initializing"); + + s->listen_ioc = QIO_CHANNEL(listen_sioc); + qio_channel_socket_listen_async(listen_sioc, addr, 0, + net_stream_server_listening, s, + NULL, NULL); + + return 0; +} + +static void net_stream_client_connected(QIOTask *task, gpointer opaque) +{ + NetStreamState *s = opaque; + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(s->ioc); + SocketAddress *addr; + gchar *uri; + int ret; + Error *err = NULL; + + if (qio_task_propagate_error(task, &err)) { + qemu_set_info_str(&s->nc, "error: %s", error_get_pretty(err)); + error_free(err); + goto error; + } + + addr = qio_channel_socket_get_remote_address(sioc, NULL); + g_assert(addr != NULL); + uri = socket_uri(addr); + qemu_set_info_str(&s->nc, "%s", uri); + g_free(uri); + + ret = qemu_socket_try_set_nonblock(sioc->fd); + if (addr->type == SOCKET_ADDRESS_TYPE_FD && ret < 0) { + qemu_set_info_str(&s->nc, "can't use file descriptor %s (errno %d)", + addr->u.fd.str, -ret); + qapi_free_SocketAddress(addr); + goto error; + } + g_assert(ret == 0); + + net_socket_rs_init(&s->rs, net_stream_rs_finalize, false); + + /* Disable Nagle algorithm on TCP sockets to reduce latency */ + qio_channel_set_delay(s->ioc, false); + + s->ioc_read_tag = qio_channel_add_watch(s->ioc, G_IO_IN, net_stream_send, + s, NULL); + s->nc.link_down = false; + qapi_event_send_netdev_stream_connected(s->nc.name, addr); + qapi_free_SocketAddress(addr); + + return; +error: + object_unref(OBJECT(s->ioc)); + s->ioc = NULL; + net_stream_arm_reconnect(s); +} + +static gboolean net_stream_reconnect(gpointer data) +{ + NetStreamState *s = data; + QIOChannelSocket *sioc; + + s->timer_tag = 0; + + sioc = qio_channel_socket_new(); + s->ioc = QIO_CHANNEL(sioc); + qio_channel_socket_connect_async(sioc, s->addr, + net_stream_client_connected, s, + NULL, NULL); + return G_SOURCE_REMOVE; +} + +static void net_stream_arm_reconnect(NetStreamState *s) +{ + if (s->reconnect && s->timer_tag == 0) { + qemu_set_info_str(&s->nc, "connecting"); + s->timer_tag = g_timeout_add_seconds(s->reconnect, + net_stream_reconnect, s); + } +} + +static int net_stream_client_init(NetClientState *peer, + const char *model, + const char *name, + SocketAddress *addr, + uint32_t reconnect, + Error **errp) +{ + NetStreamState *s; + NetClientState *nc; + QIOChannelSocket *sioc = qio_channel_socket_new(); + + nc = qemu_new_net_client(&net_stream_info, peer, model, name); + s = DO_UPCAST(NetStreamState, nc, nc); + qemu_set_info_str(&s->nc, "connecting"); + + s->ioc = QIO_CHANNEL(sioc); + s->nc.link_down = true; + + s->reconnect = reconnect; + if (reconnect) { + s->addr = QAPI_CLONE(SocketAddress, addr); + } + qio_channel_socket_connect_async(sioc, addr, + net_stream_client_connected, s, + NULL, NULL); + + return 0; +} + +int net_init_stream(const Netdev *netdev, const char *name, + NetClientState *peer, Error **errp) +{ + const NetdevStreamOptions *sock; + + assert(netdev->type == NET_CLIENT_DRIVER_STREAM); + sock = &netdev->u.stream; + + if (!sock->has_server || !sock->server) { + return net_stream_client_init(peer, "stream", name, sock->addr, + sock->has_reconnect ? sock->reconnect : 0, + errp); + } + if (sock->has_reconnect) { + error_setg(errp, "'reconnect' option is incompatible with " + "socket in server mode"); + return -1; + } + return net_stream_server_init(peer, "stream", name, sock->addr, errp); +} diff --git a/net/tap-bsd.c b/net/tap-bsd.c index 005ce05c6e..274ea7bd2c 100644 --- a/net/tap-bsd.c +++ b/net/tap-bsd.c @@ -56,7 +56,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, } else { snprintf(dname, sizeof dname, "/dev/tap%d", i); } - TFR(fd = open(dname, O_RDWR)); + fd = RETRY_ON_EINTR(open(dname, O_RDWR)); if (fd >= 0) { break; } @@ -111,7 +111,7 @@ static int tap_open_clone(char *ifname, int ifname_size, Error **errp) int fd, s, ret; struct ifreq ifr; - TFR(fd = open(PATH_NET_TAP, O_RDWR)); + fd = RETRY_ON_EINTR(open(PATH_NET_TAP, O_RDWR)); if (fd < 0) { error_setg_errno(errp, errno, "could not open %s", PATH_NET_TAP); return -1; @@ -159,7 +159,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, if (ifname[0] != '\0') { char dname[100]; snprintf(dname, sizeof dname, "/dev/%s", ifname); - TFR(fd = open(dname, O_RDWR)); + fd = RETRY_ON_EINTR(open(dname, O_RDWR)); if (fd < 0 && errno != ENOENT) { error_setg_errno(errp, errno, "could not open %s", dname); return -1; @@ -212,6 +212,11 @@ int tap_probe_has_ufo(int fd) return 0; } +int tap_probe_has_uso(int fd) +{ + return 0; +} + int tap_probe_vnet_hdr_len(int fd, int len) { return 0; @@ -232,7 +237,7 @@ int tap_fd_set_vnet_be(int fd, int is_be) } void tap_fd_set_offload(int fd, int csum, int tso4, - int tso6, int ecn, int ufo) + int tso6, int ecn, int ufo, int uso4, int uso6) { } diff --git a/net/tap-linux.c b/net/tap-linux.c index 304ff45071..c7e514ecb0 100644 --- a/net/tap-linux.c +++ b/net/tap-linux.c @@ -45,7 +45,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int len = sizeof(struct virtio_net_hdr); unsigned int features; - TFR(fd = open(PATH_NET_TUN, O_RDWR)); + fd = RETRY_ON_EINTR(open(PATH_NET_TUN, O_RDWR)); if (fd < 0) { error_setg_errno(errp, errno, "could not open %s", PATH_NET_TUN); return -1; @@ -173,6 +173,18 @@ int tap_probe_has_ufo(int fd) return 1; } +int tap_probe_has_uso(int fd) +{ + unsigned offload; + + offload = TUN_F_CSUM | TUN_F_USO4 | TUN_F_USO6; + + if (ioctl(fd, TUNSETOFFLOAD, offload) < 0) { + return 0; + } + return 1; +} + /* Verify that we can assign given length */ int tap_probe_vnet_hdr_len(int fd, int len) { @@ -237,7 +249,7 @@ int tap_fd_set_vnet_be(int fd, int is_be) } void tap_fd_set_offload(int fd, int csum, int tso4, - int tso6, int ecn, int ufo) + int tso6, int ecn, int ufo, int uso4, int uso6) { unsigned int offload = 0; @@ -256,13 +268,22 @@ void tap_fd_set_offload(int fd, int csum, int tso4, offload |= TUN_F_TSO_ECN; if (ufo) offload |= TUN_F_UFO; + if (uso4) { + offload |= TUN_F_USO4; + } + if (uso6) { + offload |= TUN_F_USO6; + } } if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { - offload &= ~TUN_F_UFO; + offload &= ~(TUN_F_USO4 | TUN_F_USO6); if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { - fprintf(stderr, "TUNSETOFFLOAD ioctl() failed: %s\n", + offload &= ~TUN_F_UFO; + if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { + fprintf(stderr, "TUNSETOFFLOAD ioctl() failed: %s\n", strerror(errno)); + } } } } diff --git a/net/tap-linux.h b/net/tap-linux.h index 1d06fe0de6..9a58cecb7f 100644 --- a/net/tap-linux.h +++ b/net/tap-linux.h @@ -45,10 +45,12 @@ #define IFF_DETACH_QUEUE 0x0400 /* Features for GSO (TUNSETOFFLOAD). */ -#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */ -#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */ -#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */ -#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */ -#define TUN_F_UFO 0x10 /* I can handle UFO packets */ +#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */ +#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */ +#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */ +#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */ +#define TUN_F_UFO 0x10 /* I can handle UFO packets */ +#define TUN_F_USO4 0x20 /* I can handle USO for IPv4 packets */ +#define TUN_F_USO6 0x40 /* I can handle USO for IPv6 packets */ #endif /* QEMU_TAP_LINUX_H */ diff --git a/net/tap-solaris.c b/net/tap-solaris.c index a44f8805c2..08b13af512 100644 --- a/net/tap-solaris.c +++ b/net/tap-solaris.c @@ -84,13 +84,13 @@ static int tap_alloc(char *dev, size_t dev_size, Error **errp) if( ip_fd ) close(ip_fd); - TFR(ip_fd = open("/dev/udp", O_RDWR, 0)); + ip_fd = RETRY_ON_EINTR(open("/dev/udp", O_RDWR, 0)); if (ip_fd < 0) { error_setg(errp, "Can't open /dev/ip (actually /dev/udp)"); return -1; } - TFR(tap_fd = open("/dev/tap", O_RDWR, 0)); + tap_fd = RETRY_ON_EINTR(open("/dev/tap", O_RDWR, 0)); if (tap_fd < 0) { error_setg(errp, "Can't open /dev/tap"); return -1; @@ -104,7 +104,7 @@ static int tap_alloc(char *dev, size_t dev_size, Error **errp) if ((ppa = ioctl (tap_fd, I_STR, &strioc_ppa)) < 0) error_report("Can't assign new interface"); - TFR(if_fd = open("/dev/tap", O_RDWR, 0)); + if_fd = RETRY_ON_EINTR(open("/dev/tap", O_RDWR, 0)); if (if_fd < 0) { error_setg(errp, "Can't open /dev/tap (2)"); return -1; @@ -137,7 +137,7 @@ static int tap_alloc(char *dev, size_t dev_size, Error **errp) if (ioctl (ip_fd, I_PUSH, "arp") < 0) error_report("Can't push ARP module (3)"); /* Open arp_fd */ - TFR(arp_fd = open ("/dev/tap", O_RDWR, 0)); + arp_fd = RETRY_ON_EINTR(open("/dev/tap", O_RDWR, 0)); if (arp_fd < 0) error_report("Can't open %s", "/dev/tap"); @@ -216,6 +216,11 @@ int tap_probe_has_ufo(int fd) return 0; } +int tap_probe_has_uso(int fd) +{ + return 0; +} + int tap_probe_vnet_hdr_len(int fd, int len) { return 0; @@ -236,7 +241,7 @@ int tap_fd_set_vnet_be(int fd, int is_be) } void tap_fd_set_offload(int fd, int csum, int tso4, - int tso6, int ecn, int ufo) + int tso6, int ecn, int ufo, int uso4, int uso6) { } diff --git a/net/tap-stub.c b/net/tap-stub.c index a0fa25804b..4b24f61e3a 100644 --- a/net/tap-stub.c +++ b/net/tap-stub.c @@ -47,6 +47,11 @@ int tap_probe_has_ufo(int fd) return 0; } +int tap_probe_has_uso(int fd) +{ + return 0; +} + int tap_probe_vnet_hdr_len(int fd, int len) { return 0; @@ -67,7 +72,7 @@ int tap_fd_set_vnet_be(int fd, int is_be) } void tap_fd_set_offload(int fd, int csum, int tso4, - int tso6, int ecn, int ufo) + int tso6, int ecn, int ufo, int uso4, int uso6) { } diff --git a/net/tap-win32.c b/net/tap-win32.c index 7466f22e77..7edbd71633 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -707,70 +707,16 @@ static void tap_win32_send(void *opaque) } } -static bool tap_has_ufo(NetClientState *nc) -{ - return false; -} - -static bool tap_has_vnet_hdr(NetClientState *nc) -{ - return false; -} - -int tap_probe_vnet_hdr_len(int fd, int len) -{ - return 0; -} - -void tap_fd_set_vnet_hdr_len(int fd, int len) -{ -} - -int tap_fd_set_vnet_le(int fd, int is_le) -{ - return -EINVAL; -} - -int tap_fd_set_vnet_be(int fd, int is_be) -{ - return -EINVAL; -} - -static void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) -{ -} - -static void tap_set_offload(NetClientState *nc, int csum, int tso4, - int tso6, int ecn, int ufo) -{ -} - struct vhost_net *tap_get_vhost_net(NetClientState *nc) { return NULL; } -static bool tap_has_vnet_hdr_len(NetClientState *nc, int len) -{ - return false; -} - -static void tap_set_vnet_hdr_len(NetClientState *nc, int len) -{ - abort(); -} - static NetClientInfo net_tap_win32_info = { .type = NET_CLIENT_DRIVER_TAP, .size = sizeof(TAPState), .receive = tap_receive, .cleanup = tap_cleanup, - .has_ufo = tap_has_ufo, - .has_vnet_hdr = tap_has_vnet_hdr, - .has_vnet_hdr_len = tap_has_vnet_hdr_len, - .using_vnet_hdr = tap_using_vnet_hdr, - .set_offload = tap_set_offload, - .set_vnet_hdr_len = tap_set_vnet_hdr_len, }; static int tap_win32_init(NetClientState *peer, const char *model, @@ -789,8 +735,7 @@ static int tap_win32_init(NetClientState *peer, const char *model, s = DO_UPCAST(TAPState, nc, nc); - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "tap: ifname=%s", ifname); + qemu_set_info_str(&s->nc, "tap: ifname=%s", ifname); s->handle = handle; @@ -808,7 +753,7 @@ int net_init_tap(const Netdev *netdev, const char *name, assert(netdev->type == NET_CLIENT_DRIVER_TAP); tap = &netdev->u.tap; - if (!tap->has_ifname) { + if (!tap->ifname) { error_report("tap: no interface name"); return -1; } diff --git a/net/tap.c b/net/tap.c index b3ddfd4a74..baaa2f7a9a 100644 --- a/net/tap.c +++ b/net/tap.c @@ -57,6 +57,7 @@ typedef struct TAPState { bool write_poll; bool using_vnet_hdr; bool has_ufo; + bool has_uso; bool enabled; VHostNetState *vhost_net; unsigned host_vnet_hdr_len; @@ -102,9 +103,7 @@ static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt { ssize_t len; - do { - len = writev(s->fd, iov, iovcnt); - } while (len == -1 && errno == EINTR); + len = RETRY_ON_EINTR(writev(s->fd, iov, iovcnt)); if (len == -1 && errno == EAGAIN) { tap_write_poll(s, true); @@ -119,10 +118,11 @@ static ssize_t tap_receive_iov(NetClientState *nc, const struct iovec *iov, { TAPState *s = DO_UPCAST(TAPState, nc, nc); const struct iovec *iovp = iov; - struct iovec iov_copy[iovcnt + 1]; + g_autofree struct iovec *iov_copy = NULL; struct virtio_net_hdr_mrg_rxbuf hdr = { }; if (s->host_vnet_hdr_len && !s->using_vnet_hdr) { + iov_copy = g_new(struct iovec, iovcnt + 1); iov_copy[0].iov_base = &hdr; iov_copy[0].iov_len = s->host_vnet_hdr_len; memcpy(&iov_copy[1], iov, iovcnt * sizeof(*iov)); @@ -219,7 +219,7 @@ static void tap_send(void *opaque) /* * When the host keeps receiving more packets while tap_send() is - * running we can hog the QEMU global mutex. Limit the number of + * running we can hog the BQL. Limit the number of * packets that are processed per tap_send() callback to prevent * stalling the guest. */ @@ -239,6 +239,15 @@ static bool tap_has_ufo(NetClientState *nc) return s->has_ufo; } +static bool tap_has_uso(NetClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + assert(nc->info->type == NET_CLIENT_DRIVER_TAP); + + return s->has_uso; +} + static bool tap_has_vnet_hdr(NetClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); @@ -257,6 +266,13 @@ static bool tap_has_vnet_hdr_len(NetClientState *nc, int len) return !!tap_probe_vnet_hdr_len(s->fd, len); } +static int tap_get_vnet_hdr_len(NetClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + return s->host_vnet_hdr_len; +} + static void tap_set_vnet_hdr_len(NetClientState *nc, int len) { TAPState *s = DO_UPCAST(TAPState, nc, nc); @@ -270,6 +286,13 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len) s->host_vnet_hdr_len = len; } +static bool tap_get_using_vnet_hdr(NetClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + return s->using_vnet_hdr; +} + static void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) { TAPState *s = DO_UPCAST(TAPState, nc, nc); @@ -295,14 +318,14 @@ static int tap_set_vnet_be(NetClientState *nc, bool is_be) } static void tap_set_offload(NetClientState *nc, int csum, int tso4, - int tso6, int ecn, int ufo) + int tso6, int ecn, int ufo, int uso4, int uso6) { TAPState *s = DO_UPCAST(TAPState, nc, nc); if (s->fd < 0) { return; } - tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo); + tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo, uso4, uso6); } static void tap_exit_notify(Notifier *notifier, void *data) @@ -372,10 +395,13 @@ static NetClientInfo net_tap_info = { .poll = tap_poll, .cleanup = tap_cleanup, .has_ufo = tap_has_ufo, + .has_uso = tap_has_uso, .has_vnet_hdr = tap_has_vnet_hdr, .has_vnet_hdr_len = tap_has_vnet_hdr_len, + .get_using_vnet_hdr = tap_get_using_vnet_hdr, .using_vnet_hdr = tap_using_vnet_hdr, .set_offload = tap_set_offload, + .get_vnet_hdr_len = tap_get_vnet_hdr_len, .set_vnet_hdr_len = tap_set_vnet_hdr_len, .set_vnet_le = tap_set_vnet_le, .set_vnet_be = tap_set_vnet_be, @@ -399,8 +425,9 @@ static TAPState *net_tap_fd_init(NetClientState *peer, s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; s->using_vnet_hdr = false; s->has_ufo = tap_probe_has_ufo(s->fd); + s->has_uso = tap_probe_has_uso(s->fd); s->enabled = true; - tap_set_offload(&s->nc, 0, 0, 0, 0, 0); + tap_set_offload(&s->nc, 0, 0, 0, 0, 0, 0, 0); /* * Make sure host header length is set correctly in tap: * it might have been modified by another instance of qemu. @@ -577,9 +604,7 @@ static int net_bridge_run_helper(const char *helper, const char *bridge, close(sv[1]); - do { - fd = recv_fd(sv[0]); - } while (fd == -1 && errno == EINTR); + fd = RETRY_ON_EINTR(recv_fd(sv[0])); saved_errno = errno; close(sv[0]); @@ -611,8 +636,8 @@ int net_init_bridge(const Netdev *netdev, const char *name, assert(netdev->type == NET_CLIENT_DRIVER_BRIDGE); bridge = &netdev->u.bridge; - helper = bridge->has_helper ? bridge->helper : NULL; - br = bridge->has_br ? bridge->br : DEFAULT_BRIDGE_INTERFACE; + helper = bridge->helper; + br = bridge->br ?: DEFAULT_BRIDGE_INTERFACE; fd = net_bridge_run_helper(helper, br, errp); if (fd == -1) { @@ -630,8 +655,7 @@ int net_init_bridge(const Netdev *netdev, const char *name, } s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr); - snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s,br=%s", helper, - br); + qemu_set_info_str(&s->nc, "helper=%s,br=%s", helper, br); return 0; } @@ -651,7 +675,7 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr, vnet_hdr_required = 0; } - TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required, + fd = RETRY_ON_EINTR(tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required, mq_required, errp)); if (fd < 0) { return -1; @@ -686,18 +710,16 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, tap_set_sndbuf(s->fd, tap, &err); if (err) { error_propagate(errp, err); - return; + goto failed; } - if (tap->has_fd || tap->has_fds) { - snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd); - } else if (tap->has_helper) { - snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s", - tap->helper); + if (tap->fd || tap->fds) { + qemu_set_info_str(&s->nc, "fd=%d", fd); + } else if (tap->helper) { + qemu_set_info_str(&s->nc, "helper=%s", tap->helper); } else { - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "ifname=%s,script=%s,downscript=%s", ifname, script, - downscript); + qemu_set_info_str(&s->nc, "ifname=%s,script=%s,downscript=%s", ifname, + script, downscript); if (strcmp(downscript, "no") != 0) { snprintf(s->down_script, sizeof(s->down_script), "%s", downscript); @@ -721,33 +743,24 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, if (vhostfdname) { vhostfd = monitor_fd_param(monitor_cur(), vhostfdname, &err); if (vhostfd == -1) { - if (tap->has_vhostforce && tap->vhostforce) { - error_propagate(errp, err); - } else { - warn_report_err(err); - } - return; + error_propagate(errp, err); + goto failed; } if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { error_setg_errno(errp, errno, "%s: Can't use file descriptor %d", name, fd); - return; + goto failed; } } else { vhostfd = open("/dev/vhost-net", O_RDWR); if (vhostfd < 0) { - if (tap->has_vhostforce && tap->vhostforce) { - error_setg_errno(errp, errno, - "tap: open vhost char device failed"); - } else { - warn_report("tap: open vhost char device failed: %s", - strerror(errno)); - } - return; + error_setg_errno(errp, errno, + "tap: open vhost char device failed"); + goto failed; } if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { error_setg_errno(errp, errno, "Failed to set FD nonblocking"); - return; + goto failed; } } options.opaque = (void *)(uintptr_t)vhostfd; @@ -755,16 +768,19 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, s->vhost_net = vhost_net_init(&options); if (!s->vhost_net) { - if (tap->has_vhostforce && tap->vhostforce) { - error_setg(errp, VHOST_NET_INIT_FAILED); - } else { - warn_report(VHOST_NET_INIT_FAILED); - } - return; + error_setg(errp, + "vhost-net requested but could not be initialized"); + goto failed; } } else if (vhostfdname) { error_setg(errp, "vhostfd(s)= is not valid without vhost"); + goto failed; } + + return; + +failed: + qemu_del_net_client(&s->nc); } static int get_fds(char *str, char *fds[], int max) @@ -809,21 +825,21 @@ int net_init_tap(const Netdev *netdev, const char *name, assert(netdev->type == NET_CLIENT_DRIVER_TAP); tap = &netdev->u.tap; queues = tap->has_queues ? tap->queues : 1; - vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL; - script = tap->has_script ? tap->script : NULL; - downscript = tap->has_downscript ? tap->downscript : NULL; + vhostfdname = tap->vhostfd; + script = tap->script; + downscript = tap->downscript; /* QEMU hubs do not support multiqueue tap, in this case peer is set. * For -netdev, peer is always NULL. */ - if (peer && (tap->has_queues || tap->has_fds || tap->has_vhostfds)) { + if (peer && (tap->has_queues || tap->fds || tap->vhostfds)) { error_setg(errp, "Multiqueue tap cannot be used with hubs"); return -1; } - if (tap->has_fd) { - if (tap->has_ifname || tap->has_script || tap->has_downscript || - tap->has_vnet_hdr || tap->has_helper || tap->has_queues || - tap->has_fds || tap->has_vhostfds) { + if (tap->fd) { + if (tap->ifname || tap->script || tap->downscript || + tap->has_vnet_hdr || tap->helper || tap->has_queues || + tap->fds || tap->vhostfds) { error_setg(errp, "ifname=, script=, downscript=, vnet_hdr=, " "helper=, queues=, fds=, and vhostfds= " "are invalid with fd="); @@ -856,14 +872,14 @@ int net_init_tap(const Netdev *netdev, const char *name, close(fd); return -1; } - } else if (tap->has_fds) { + } else if (tap->fds) { char **fds; char **vhost_fds; int nfds = 0, nvhosts = 0; - if (tap->has_ifname || tap->has_script || tap->has_downscript || - tap->has_vnet_hdr || tap->has_helper || tap->has_queues || - tap->has_vhostfd) { + if (tap->ifname || tap->script || tap->downscript || + tap->has_vnet_hdr || tap->helper || tap->has_queues || + tap->vhostfd) { error_setg(errp, "ifname=, script=, downscript=, vnet_hdr=, " "helper=, queues=, and vhostfd= " "are invalid with fds="); @@ -874,7 +890,7 @@ int net_init_tap(const Netdev *netdev, const char *name, vhost_fds = g_new0(char *, MAX_TAP_QUEUES); nfds = get_fds(tap->fds, fds, MAX_TAP_QUEUES); - if (tap->has_vhostfds) { + if (tap->vhostfds) { nvhosts = get_fds(tap->vhostfds, vhost_fds, MAX_TAP_QUEUES); if (nfds != nvhosts) { error_setg(errp, "The number of fds passed does not match " @@ -913,7 +929,7 @@ int net_init_tap(const Netdev *netdev, const char *name, net_init_tap_one(tap, peer, "tap", name, ifname, script, downscript, - tap->has_vhostfds ? vhost_fds[i] : NULL, + tap->vhostfds ? vhost_fds[i] : NULL, vnet_hdr, fd, &err); if (err) { error_propagate(errp, err); @@ -932,17 +948,16 @@ free_fail: g_free(fds); g_free(vhost_fds); return ret; - } else if (tap->has_helper) { - if (tap->has_ifname || tap->has_script || tap->has_downscript || - tap->has_vnet_hdr || tap->has_queues || tap->has_vhostfds) { + } else if (tap->helper) { + if (tap->ifname || tap->script || tap->downscript || + tap->has_vnet_hdr || tap->has_queues || tap->vhostfds) { error_setg(errp, "ifname=, script=, downscript=, vnet_hdr=, " "queues=, and vhostfds= are invalid with helper="); return -1; } fd = net_bridge_run_helper(tap->helper, - tap->has_br ? - tap->br : DEFAULT_BRIDGE_INTERFACE, + tap->br ?: DEFAULT_BRIDGE_INTERFACE, errp); if (fd == -1) { return -1; @@ -969,7 +984,7 @@ free_fail: } else { g_autofree char *default_script = NULL; g_autofree char *default_downscript = NULL; - if (tap->has_vhostfds) { + if (tap->vhostfds) { error_setg(errp, "vhostfds= is invalid if fds= wasn't specified"); return -1; } @@ -982,7 +997,7 @@ free_fail: get_relocated_path(DEFAULT_NETWORK_DOWN_SCRIPT); } - if (tap->has_ifname) { + if (tap->ifname) { pstrcpy(ifname, sizeof ifname, tap->ifname); } else { ifname[0] = '\0'; @@ -995,7 +1010,7 @@ free_fail: return -1; } - if (queues > 1 && i == 0 && !tap->has_ifname) { + if (queues > 1 && i == 0 && !tap->ifname) { if (tap_fd_get_ifname(fd, ifname)) { error_setg(errp, "Fail to get ifname"); close(fd); diff --git a/net/tap_int.h b/net/tap_int.h index 547f8a5a28..9a2175655b 100644 --- a/net/tap_int.h +++ b/net/tap_int.h @@ -37,7 +37,9 @@ void tap_set_sndbuf(int fd, const NetdevTapOptions *tap, Error **errp); int tap_probe_vnet_hdr(int fd, Error **errp); int tap_probe_vnet_hdr_len(int fd, int len); int tap_probe_has_ufo(int fd); -void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo); +int tap_probe_has_uso(int fd); +void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo, + int uso4, int uso6); void tap_fd_set_vnet_hdr_len(int fd, int len); int tap_fd_set_vnet_le(int fd, int vnet_is_le); int tap_fd_set_vnet_be(int fd, int vnet_is_be); diff --git a/net/trace-events b/net/trace-events index d7a17256cc..cda960f42b 100644 --- a/net/trace-events +++ b/net/trace-events @@ -9,6 +9,7 @@ vhost_user_event(const char *chr, int event) "chr: %s got event: %d" # colo.c colo_proxy_main(const char *chr) ": %s" +colo_proxy_main_vnet_info(const char *sta, uint32_t vnet_hdr, int size) ": %s pkt->vnet_hdr_len = %u, pkt->size = %d" # colo-compare.c colo_compare_main(const char *chr) ": %s" @@ -22,3 +23,9 @@ colo_compare_tcp_info(const char *pkt, uint32_t seq, uint32_t ack, int hdlen, in # filter-rewriter.c colo_filter_rewriter_pkt_info(const char *func, const char *src, const char *dst, uint32_t seq, uint32_t ack, uint32_t flag) "%s: src/dst: %s/%s p: seq/ack=%u/%u flags=0x%x" colo_filter_rewriter_conn_offset(uint32_t offset) ": offset=%u" + +# vhost-vdpa.c +vhost_vdpa_set_address_space_id(void *v, unsigned vq_group, unsigned asid_num) "vhost_vdpa: %p vq_group: %u asid: %u" +vhost_vdpa_net_load_cmd(void *s, uint8_t class, uint8_t cmd, int data_num, int data_size) "vdpa state: %p class: %u cmd: %u sg_num: %d size: %d" +vhost_vdpa_net_load_cmd_retval(void *s, uint8_t class, uint8_t cmd, int r) "vdpa state: %p class: %u cmd: %u retval: %d" +vhost_vdpa_net_load_mq(void *s, int ncurqps) "vdpa state: %p current_qpairs: %d" diff --git a/net/vde.c b/net/vde.c index 1083916bcf..c0a08662cc 100644 --- a/net/vde.c +++ b/net/vde.c @@ -98,8 +98,7 @@ static int net_vde_init(NetClientState *peer, const char *model, nc = qemu_new_net_client(&net_vde_info, peer, model, name); - snprintf(nc->info_str, sizeof(nc->info_str), "sock=%s,fd=%d", - sock, vde_datafd(vde)); + qemu_set_info_str(nc, "sock=%s,fd=%d", sock, vde_datafd(vde)); s = DO_UPCAST(VDEState, nc, nc); diff --git a/net/vhost-user.c b/net/vhost-user.c index b1a0247b59..12555518e8 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -45,10 +45,23 @@ uint64_t vhost_user_get_acked_features(NetClientState *nc) return s->acked_features; } -static void vhost_user_stop(int queues, NetClientState *ncs[]) +void vhost_user_save_acked_features(NetClientState *nc) { NetVhostUserState *s; + + s = DO_UPCAST(NetVhostUserState, nc, nc); + if (s->vhost_net) { + uint64_t features = vhost_net_get_acked_features(s->vhost_net); + if (features) { + s->acked_features = features; + } + } +} + +static void vhost_user_stop(int queues, NetClientState *ncs[]) +{ int i; + NetVhostUserState *s; for (i = 0; i < queues; i++) { assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER); @@ -56,11 +69,7 @@ static void vhost_user_stop(int queues, NetClientState *ncs[]) s = DO_UPCAST(NetVhostUserState, nc, ncs[i]); if (s->vhost_net) { - /* save acked features */ - uint64_t features = vhost_net_get_acked_features(s->vhost_net); - if (features) { - s->acked_features = features; - } + vhost_user_save_acked_features(ncs[i]); vhost_net_cleanup(s->vhost_net); } } @@ -230,7 +239,7 @@ static gboolean net_vhost_user_watch(void *do_not_use, GIOCondition cond, qemu_chr_fe_disconnect(&s->chr); - return TRUE; + return G_SOURCE_CONTINUE; } static void net_vhost_user_event(void *opaque, QEMUChrEvent event); @@ -251,11 +260,7 @@ static void chr_closed_bh(void *opaque) s = DO_UPCAST(NetVhostUserState, nc, ncs[0]); for (i = queues -1; i >= 0; i--) { - s = DO_UPCAST(NetVhostUserState, nc, ncs[i]); - - if (s->vhost_net) { - s->acked_features = vhost_net_get_acked_features(s->vhost_net); - } + vhost_user_save_acked_features(ncs[i]); } qmp_set_link(name, false, &err); @@ -341,8 +346,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, user = g_new0(struct VhostUserState, 1); for (i = 0; i < queues; i++) { nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name); - snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s", - i, chr->label); + qemu_set_info_str(nc, "vhost-user%d to %s", i, chr->label); nc->queue_index = i; if (!nc0) { nc0 = nc; diff --git a/net/vhost-vdpa-stub.c b/net/vhost-vdpa-stub.c new file mode 100644 index 0000000000..1732ed2443 --- /dev/null +++ b/net/vhost-vdpa-stub.c @@ -0,0 +1,21 @@ +/* + * vhost-vdpa-stub.c + * + * Copyright (c) 2022 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 "clients.h" +#include "net/vhost-vdpa.h" +#include "qapi/error.h" + +int net_init_vhost_vdpa(const Netdev *netdev, const char *name, + NetClientState *peer, Error **errp) +{ + error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*"); + return -1; +} diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index df1e69ee72..85e73dd6a7 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -11,11 +11,14 @@ #include "qemu/osdep.h" #include "clients.h" +#include "hw/virtio/virtio-net.h" #include "net/vhost_net.h" #include "net/vhost-vdpa.h" #include "hw/virtio/vhost-vdpa.h" #include "qemu/config-file.h" #include "qemu/error-report.h" +#include "qemu/log.h" +#include "qemu/memalign.h" #include "qemu/option.h" #include "qapi/error.h" #include @@ -23,52 +26,109 @@ #include #include "standard-headers/linux/virtio_net.h" #include "monitor/monitor.h" +#include "migration/misc.h" #include "hw/virtio/vhost.h" +#include "trace.h" /* Todo:need to add the multiqueue support here */ typedef struct VhostVDPAState { NetClientState nc; struct vhost_vdpa vhost_vdpa; + NotifierWithReturn migration_state; VHostNetState *vhost_net; + + /* Control commands shadow buffers */ + void *cvq_cmd_out_buffer; + virtio_net_ctrl_ack *status; + + /* The device always have SVQ enabled */ + bool always_svq; + + /* The device can isolate CVQ in its own ASID */ + bool cvq_isolated; + bool started; } VhostVDPAState; +/* + * The array is sorted alphabetically in ascending order, + * with the exception of VHOST_INVALID_FEATURE_BIT, + * which should always be the last entry. + */ const int vdpa_feature_bits[] = { - VIRTIO_F_NOTIFY_ON_EMPTY, - VIRTIO_RING_F_INDIRECT_DESC, - VIRTIO_RING_F_EVENT_IDX, VIRTIO_F_ANY_LAYOUT, + VIRTIO_F_IOMMU_PLATFORM, + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_F_RING_PACKED, + VIRTIO_F_RING_RESET, VIRTIO_F_VERSION_1, VIRTIO_NET_F_CSUM, - VIRTIO_NET_F_GUEST_CSUM, - VIRTIO_NET_F_GSO, - VIRTIO_NET_F_GUEST_TSO4, - VIRTIO_NET_F_GUEST_TSO6, - VIRTIO_NET_F_GUEST_ECN, - VIRTIO_NET_F_GUEST_UFO, - VIRTIO_NET_F_HOST_TSO4, - VIRTIO_NET_F_HOST_TSO6, - VIRTIO_NET_F_HOST_ECN, - VIRTIO_NET_F_HOST_UFO, - VIRTIO_NET_F_MRG_RXBUF, - VIRTIO_NET_F_MTU, + VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, + VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_RX_EXTRA, VIRTIO_NET_F_CTRL_VLAN, - VIRTIO_NET_F_GUEST_ANNOUNCE, - VIRTIO_NET_F_CTRL_MAC_ADDR, - VIRTIO_NET_F_RSS, - VIRTIO_NET_F_MQ, VIRTIO_NET_F_CTRL_VQ, - VIRTIO_F_IOMMU_PLATFORM, - VIRTIO_F_RING_PACKED, - VIRTIO_NET_F_RSS, + VIRTIO_NET_F_GSO, + VIRTIO_NET_F_GUEST_CSUM, + VIRTIO_NET_F_GUEST_ECN, + VIRTIO_NET_F_GUEST_TSO4, + VIRTIO_NET_F_GUEST_TSO6, + VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_GUEST_USO4, + VIRTIO_NET_F_GUEST_USO6, VIRTIO_NET_F_HASH_REPORT, - VIRTIO_NET_F_GUEST_ANNOUNCE, + VIRTIO_NET_F_HOST_ECN, + VIRTIO_NET_F_HOST_TSO4, + VIRTIO_NET_F_HOST_TSO6, + VIRTIO_NET_F_HOST_UFO, + VIRTIO_NET_F_HOST_USO, + VIRTIO_NET_F_MQ, + VIRTIO_NET_F_MRG_RXBUF, + VIRTIO_NET_F_MTU, + VIRTIO_NET_F_RSS, VIRTIO_NET_F_STATUS, + VIRTIO_RING_F_EVENT_IDX, + VIRTIO_RING_F_INDIRECT_DESC, + + /* VHOST_INVALID_FEATURE_BIT should always be the last entry */ VHOST_INVALID_FEATURE_BIT }; +/** Supported device specific feature bits with SVQ */ +static const uint64_t vdpa_svq_device_features = + BIT_ULL(VIRTIO_NET_F_CSUM) | + BIT_ULL(VIRTIO_NET_F_GUEST_CSUM) | + BIT_ULL(VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) | + BIT_ULL(VIRTIO_NET_F_MTU) | + BIT_ULL(VIRTIO_NET_F_MAC) | + BIT_ULL(VIRTIO_NET_F_GUEST_TSO4) | + BIT_ULL(VIRTIO_NET_F_GUEST_TSO6) | + BIT_ULL(VIRTIO_NET_F_GUEST_ECN) | + BIT_ULL(VIRTIO_NET_F_GUEST_UFO) | + BIT_ULL(VIRTIO_NET_F_HOST_TSO4) | + BIT_ULL(VIRTIO_NET_F_HOST_TSO6) | + BIT_ULL(VIRTIO_NET_F_HOST_ECN) | + BIT_ULL(VIRTIO_NET_F_HOST_UFO) | + BIT_ULL(VIRTIO_NET_F_MRG_RXBUF) | + BIT_ULL(VIRTIO_NET_F_STATUS) | + BIT_ULL(VIRTIO_NET_F_CTRL_VQ) | + BIT_ULL(VIRTIO_NET_F_CTRL_RX) | + BIT_ULL(VIRTIO_NET_F_CTRL_VLAN) | + BIT_ULL(VIRTIO_NET_F_CTRL_RX_EXTRA) | + BIT_ULL(VIRTIO_NET_F_MQ) | + BIT_ULL(VIRTIO_F_ANY_LAYOUT) | + BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR) | + /* VHOST_F_LOG_ALL is exposed by SVQ */ + BIT_ULL(VHOST_F_LOG_ALL) | + BIT_ULL(VIRTIO_NET_F_HASH_REPORT) | + BIT_ULL(VIRTIO_NET_F_RSS) | + BIT_ULL(VIRTIO_NET_F_RSC_EXT) | + BIT_ULL(VIRTIO_NET_F_STANDBY) | + BIT_ULL(VIRTIO_NET_F_SPEED_DUPLEX); + +#define VHOST_VDPA_NET_CVQ_ASID 1 + VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc) { VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); @@ -76,6 +136,39 @@ VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc) return s->vhost_net; } +static size_t vhost_vdpa_net_cvq_cmd_len(void) +{ + /* + * MAC_TABLE_SET is the ctrl command that produces the longer out buffer. + * In buffer is always 1 byte, so it should fit here + */ + return sizeof(struct virtio_net_ctrl_hdr) + + 2 * sizeof(struct virtio_net_ctrl_mac) + + MAC_TABLE_ENTRIES * ETH_ALEN; +} + +static size_t vhost_vdpa_net_cvq_cmd_page_len(void) +{ + return ROUND_UP(vhost_vdpa_net_cvq_cmd_len(), qemu_real_host_page_size()); +} + +static bool vhost_vdpa_net_valid_svq_features(uint64_t features, Error **errp) +{ + uint64_t invalid_dev_features = + features & ~vdpa_svq_device_features & + /* Transport are all accepted at this point */ + ~MAKE_64BIT_MASK(VIRTIO_TRANSPORT_F_START, + VIRTIO_TRANSPORT_F_END - VIRTIO_TRANSPORT_F_START); + + if (invalid_dev_features) { + error_setg(errp, "vdpa svq does not work with features 0x%" PRIx64, + invalid_dev_features); + return false; + } + + return vhost_svq_valid_features(features, errp); +} + static int vhost_vdpa_net_check_device_id(struct vhost_net *net) { uint32_t device_id; @@ -128,15 +221,32 @@ static void vhost_vdpa_cleanup(NetClientState *nc) { VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); + /* + * If a peer NIC is attached, do not cleanup anything. + * Cleanup will happen as a part of qemu_cleanup() -> net_cleanup() + * when the guest is shutting down. + */ + if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_NIC) { + return; + } + munmap(s->cvq_cmd_out_buffer, vhost_vdpa_net_cvq_cmd_page_len()); + munmap(s->status, vhost_vdpa_net_cvq_cmd_page_len()); if (s->vhost_net) { vhost_net_cleanup(s->vhost_net); g_free(s->vhost_net); s->vhost_net = NULL; } - if (s->vhost_vdpa.device_fd >= 0) { - qemu_close(s->vhost_vdpa.device_fd); - s->vhost_vdpa.device_fd = -1; + if (s->vhost_vdpa.index != 0) { + return; } + qemu_close(s->vhost_vdpa.shared->device_fd); + g_free(s->vhost_vdpa.shared); +} + +/** Dummy SetSteeringEBPF to support RSS for vhost-vdpa backend */ +static bool vhost_vdpa_set_steering_ebpf(NetClientState *nc, int prog_fd) +{ + return true; } static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc) @@ -174,65 +284,1469 @@ static bool vhost_vdpa_check_peer_type(NetClientState *nc, ObjectClass *oc, static ssize_t vhost_vdpa_receive(NetClientState *nc, const uint8_t *buf, size_t size) { + return size; +} + + +/** From any vdpa net client, get the netclient of the i-th queue pair */ +static VhostVDPAState *vhost_vdpa_net_get_nc_vdpa(VhostVDPAState *s, int i) +{ + NICState *nic = qemu_get_nic(s->nc.peer); + NetClientState *nc_i = qemu_get_peer(nic->ncs, i); + + return DO_UPCAST(VhostVDPAState, nc, nc_i); +} + +static VhostVDPAState *vhost_vdpa_net_first_nc_vdpa(VhostVDPAState *s) +{ + return vhost_vdpa_net_get_nc_vdpa(s, 0); +} + +static void vhost_vdpa_net_log_global_enable(VhostVDPAState *s, bool enable) +{ + struct vhost_vdpa *v = &s->vhost_vdpa; + VirtIONet *n; + VirtIODevice *vdev; + int data_queue_pairs, cvq, r; + + /* We are only called on the first data vqs and only if x-svq is not set */ + if (s->vhost_vdpa.shadow_vqs_enabled == enable) { + return; + } + + vdev = v->dev->vdev; + n = VIRTIO_NET(vdev); + if (!n->vhost_started) { + return; + } + + data_queue_pairs = n->multiqueue ? n->max_queue_pairs : 1; + cvq = virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ) ? + n->max_ncs - n->max_queue_pairs : 0; + v->shared->svq_switching = enable ? + SVQ_TSTATE_ENABLING : SVQ_TSTATE_DISABLING; + /* + * TODO: vhost_net_stop does suspend, get_base and reset. We can be smarter + * in the future and resume the device if read-only operations between + * suspend and reset goes wrong. + */ + vhost_net_stop(vdev, n->nic->ncs, data_queue_pairs, cvq); + + /* Start will check migration setup_or_active to configure or not SVQ */ + r = vhost_net_start(vdev, n->nic->ncs, data_queue_pairs, cvq); + if (unlikely(r < 0)) { + error_report("unable to start vhost net: %s(%d)", g_strerror(-r), -r); + } + v->shared->svq_switching = SVQ_TSTATE_DONE; +} + +static int vdpa_net_migration_state_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, Error **errp) +{ + VhostVDPAState *s = container_of(notifier, VhostVDPAState, migration_state); + + if (e->type == MIG_EVENT_PRECOPY_SETUP) { + vhost_vdpa_net_log_global_enable(s, true); + } else if (e->type == MIG_EVENT_PRECOPY_FAILED) { + vhost_vdpa_net_log_global_enable(s, false); + } return 0; } +static void vhost_vdpa_net_data_start_first(VhostVDPAState *s) +{ + struct vhost_vdpa *v = &s->vhost_vdpa; + + migration_add_notifier(&s->migration_state, + vdpa_net_migration_state_notifier); + if (v->shadow_vqs_enabled) { + v->shared->iova_tree = vhost_iova_tree_new(v->shared->iova_range.first, + v->shared->iova_range.last); + } +} + +static int vhost_vdpa_net_data_start(NetClientState *nc) +{ + VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); + struct vhost_vdpa *v = &s->vhost_vdpa; + + assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); + + if (s->always_svq || + migration_is_setup_or_active()) { + v->shadow_vqs_enabled = true; + } else { + v->shadow_vqs_enabled = false; + } + + if (v->index == 0) { + v->shared->shadow_data = v->shadow_vqs_enabled; + vhost_vdpa_net_data_start_first(s); + return 0; + } + + return 0; +} + +static int vhost_vdpa_net_data_load(NetClientState *nc) +{ + VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); + struct vhost_vdpa *v = &s->vhost_vdpa; + bool has_cvq = v->dev->vq_index_end % 2; + + if (has_cvq) { + return 0; + } + + for (int i = 0; i < v->dev->nvqs; ++i) { + vhost_vdpa_set_vring_ready(v, i + v->dev->vq_index); + } + return 0; +} + +static void vhost_vdpa_net_client_stop(NetClientState *nc) +{ + VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); + struct vhost_dev *dev; + + assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); + + if (s->vhost_vdpa.index == 0) { + migration_remove_notifier(&s->migration_state); + } + + dev = s->vhost_vdpa.dev; + if (dev->vq_index + dev->nvqs == dev->vq_index_end) { + g_clear_pointer(&s->vhost_vdpa.shared->iova_tree, + vhost_iova_tree_delete); + } +} + static NetClientInfo net_vhost_vdpa_info = { .type = NET_CLIENT_DRIVER_VHOST_VDPA, .size = sizeof(VhostVDPAState), .receive = vhost_vdpa_receive, + .start = vhost_vdpa_net_data_start, + .load = vhost_vdpa_net_data_load, + .stop = vhost_vdpa_net_client_stop, .cleanup = vhost_vdpa_cleanup, .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, .has_ufo = vhost_vdpa_has_ufo, .check_peer_type = vhost_vdpa_check_peer_type, + .set_steering_ebpf = vhost_vdpa_set_steering_ebpf, }; +static int64_t vhost_vdpa_get_vring_group(int device_fd, unsigned vq_index, + Error **errp) +{ + struct vhost_vring_state state = { + .index = vq_index, + }; + int r = ioctl(device_fd, VHOST_VDPA_GET_VRING_GROUP, &state); + + if (unlikely(r < 0)) { + r = -errno; + error_setg_errno(errp, errno, "Cannot get VQ %u group", vq_index); + return r; + } + + return state.num; +} + +static int vhost_vdpa_set_address_space_id(struct vhost_vdpa *v, + unsigned vq_group, + unsigned asid_num) +{ + struct vhost_vring_state asid = { + .index = vq_group, + .num = asid_num, + }; + int r; + + trace_vhost_vdpa_set_address_space_id(v, vq_group, asid_num); + + r = ioctl(v->shared->device_fd, VHOST_VDPA_SET_GROUP_ASID, &asid); + if (unlikely(r < 0)) { + error_report("Can't set vq group %u asid %u, errno=%d (%s)", + asid.index, asid.num, errno, g_strerror(errno)); + } + return r; +} + +static void vhost_vdpa_cvq_unmap_buf(struct vhost_vdpa *v, void *addr) +{ + VhostIOVATree *tree = v->shared->iova_tree; + DMAMap needle = { + /* + * No need to specify size or to look for more translations since + * this contiguous chunk was allocated by us. + */ + .translated_addr = (hwaddr)(uintptr_t)addr, + }; + const DMAMap *map = vhost_iova_tree_find_iova(tree, &needle); + int r; + + if (unlikely(!map)) { + error_report("Cannot locate expected map"); + return; + } + + r = vhost_vdpa_dma_unmap(v->shared, v->address_space_id, map->iova, + map->size + 1); + if (unlikely(r != 0)) { + error_report("Device cannot unmap: %s(%d)", g_strerror(r), r); + } + + vhost_iova_tree_remove(tree, *map); +} + +/** Map CVQ buffer. */ +static int vhost_vdpa_cvq_map_buf(struct vhost_vdpa *v, void *buf, size_t size, + bool write) +{ + DMAMap map = {}; + int r; + + map.translated_addr = (hwaddr)(uintptr_t)buf; + map.size = size - 1; + map.perm = write ? IOMMU_RW : IOMMU_RO, + r = vhost_iova_tree_map_alloc(v->shared->iova_tree, &map); + if (unlikely(r != IOVA_OK)) { + error_report("Cannot map injected element"); + return r; + } + + r = vhost_vdpa_dma_map(v->shared, v->address_space_id, map.iova, + vhost_vdpa_net_cvq_cmd_page_len(), buf, !write); + if (unlikely(r < 0)) { + goto dma_map_err; + } + + return 0; + +dma_map_err: + vhost_iova_tree_remove(v->shared->iova_tree, map); + return r; +} + +static int vhost_vdpa_net_cvq_start(NetClientState *nc) +{ + VhostVDPAState *s, *s0; + struct vhost_vdpa *v; + int64_t cvq_group; + int r; + Error *err = NULL; + + assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); + + s = DO_UPCAST(VhostVDPAState, nc, nc); + v = &s->vhost_vdpa; + + s0 = vhost_vdpa_net_first_nc_vdpa(s); + v->shadow_vqs_enabled = s0->vhost_vdpa.shadow_vqs_enabled; + s->vhost_vdpa.address_space_id = VHOST_VDPA_GUEST_PA_ASID; + + if (v->shared->shadow_data) { + /* SVQ is already configured for all virtqueues */ + goto out; + } + + /* + * If we early return in these cases SVQ will not be enabled. The migration + * will be blocked as long as vhost-vdpa backends will not offer _F_LOG. + */ + if (!vhost_vdpa_net_valid_svq_features(v->dev->features, NULL)) { + return 0; + } + + if (!s->cvq_isolated) { + return 0; + } + + cvq_group = vhost_vdpa_get_vring_group(v->shared->device_fd, + v->dev->vq_index_end - 1, + &err); + if (unlikely(cvq_group < 0)) { + error_report_err(err); + return cvq_group; + } + + r = vhost_vdpa_set_address_space_id(v, cvq_group, VHOST_VDPA_NET_CVQ_ASID); + if (unlikely(r < 0)) { + return r; + } + + v->shadow_vqs_enabled = true; + s->vhost_vdpa.address_space_id = VHOST_VDPA_NET_CVQ_ASID; + +out: + if (!s->vhost_vdpa.shadow_vqs_enabled) { + return 0; + } + + /* + * If other vhost_vdpa already have an iova_tree, reuse it for simplicity, + * whether CVQ shares ASID with guest or not, because: + * - Memory listener need access to guest's memory addresses allocated in + * the IOVA tree. + * - There should be plenty of IOVA address space for both ASID not to + * worry about collisions between them. Guest's translations are still + * validated with virtio virtqueue_pop so there is no risk for the guest + * to access memory that it shouldn't. + * + * To allocate a iova tree per ASID is doable but it complicates the code + * and it is not worth it for the moment. + */ + if (!v->shared->iova_tree) { + v->shared->iova_tree = vhost_iova_tree_new(v->shared->iova_range.first, + v->shared->iova_range.last); + } + + r = vhost_vdpa_cvq_map_buf(&s->vhost_vdpa, s->cvq_cmd_out_buffer, + vhost_vdpa_net_cvq_cmd_page_len(), false); + if (unlikely(r < 0)) { + return r; + } + + r = vhost_vdpa_cvq_map_buf(&s->vhost_vdpa, s->status, + vhost_vdpa_net_cvq_cmd_page_len(), true); + if (unlikely(r < 0)) { + vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, s->cvq_cmd_out_buffer); + } + + return r; +} + +static void vhost_vdpa_net_cvq_stop(NetClientState *nc) +{ + VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); + + assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); + + if (s->vhost_vdpa.shadow_vqs_enabled) { + vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, s->cvq_cmd_out_buffer); + vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, s->status); + } + + vhost_vdpa_net_client_stop(nc); +} + +static ssize_t vhost_vdpa_net_cvq_add(VhostVDPAState *s, + const struct iovec *out_sg, size_t out_num, + const struct iovec *in_sg, size_t in_num) +{ + VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0); + int r; + + r = vhost_svq_add(svq, out_sg, out_num, in_sg, in_num, NULL); + if (unlikely(r != 0)) { + if (unlikely(r == -ENOSPC)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n", + __func__); + } + } + + return r; +} + +/* + * Convenience wrapper to poll SVQ for multiple control commands. + * + * Caller should hold the BQL when invoking this function, and should take + * the answer before SVQ pulls by itself when BQL is released. + */ +static ssize_t vhost_vdpa_net_svq_poll(VhostVDPAState *s, size_t cmds_in_flight) +{ + VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0); + return vhost_svq_poll(svq, cmds_in_flight); +} + +static void vhost_vdpa_net_load_cursor_reset(VhostVDPAState *s, + struct iovec *out_cursor, + struct iovec *in_cursor) +{ + /* reset the cursor of the output buffer for the device */ + out_cursor->iov_base = s->cvq_cmd_out_buffer; + out_cursor->iov_len = vhost_vdpa_net_cvq_cmd_page_len(); + + /* reset the cursor of the in buffer for the device */ + in_cursor->iov_base = s->status; + in_cursor->iov_len = vhost_vdpa_net_cvq_cmd_page_len(); +} + +/* + * Poll SVQ for multiple pending control commands and check the device's ack. + * + * Caller should hold the BQL when invoking this function. + * + * @s: The VhostVDPAState + * @len: The length of the pending status shadow buffer + */ +static ssize_t vhost_vdpa_net_svq_flush(VhostVDPAState *s, size_t len) +{ + /* device uses a one-byte length ack for each control command */ + ssize_t dev_written = vhost_vdpa_net_svq_poll(s, len); + if (unlikely(dev_written != len)) { + return -EIO; + } + + /* check the device's ack */ + for (int i = 0; i < len; ++i) { + if (s->status[i] != VIRTIO_NET_OK) { + return -EIO; + } + } + return 0; +} + +static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, + struct iovec *out_cursor, + struct iovec *in_cursor, uint8_t class, + uint8_t cmd, const struct iovec *data_sg, + size_t data_num) +{ + const struct virtio_net_ctrl_hdr ctrl = { + .class = class, + .cmd = cmd, + }; + size_t data_size = iov_size(data_sg, data_num), cmd_size; + struct iovec out, in; + ssize_t r; + unsigned dummy_cursor_iov_cnt; + VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0); + + assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl)); + cmd_size = sizeof(ctrl) + data_size; + trace_vhost_vdpa_net_load_cmd(s, class, cmd, data_num, data_size); + if (vhost_svq_available_slots(svq) < 2 || + iov_size(out_cursor, 1) < cmd_size) { + /* + * It is time to flush all pending control commands if SVQ is full + * or control commands shadow buffers are full. + * + * We can poll here since we've had BQL from the time + * we sent the descriptor. + */ + r = vhost_vdpa_net_svq_flush(s, in_cursor->iov_base - + (void *)s->status); + if (unlikely(r < 0)) { + return r; + } + + vhost_vdpa_net_load_cursor_reset(s, out_cursor, in_cursor); + } + + /* pack the CVQ command header */ + iov_from_buf(out_cursor, 1, 0, &ctrl, sizeof(ctrl)); + /* pack the CVQ command command-specific-data */ + iov_to_buf(data_sg, data_num, 0, + out_cursor->iov_base + sizeof(ctrl), data_size); + + /* extract the required buffer from the cursor for output */ + iov_copy(&out, 1, out_cursor, 1, 0, cmd_size); + /* extract the required buffer from the cursor for input */ + iov_copy(&in, 1, in_cursor, 1, 0, sizeof(*s->status)); + + r = vhost_vdpa_net_cvq_add(s, &out, 1, &in, 1); + if (unlikely(r < 0)) { + trace_vhost_vdpa_net_load_cmd_retval(s, class, cmd, r); + return r; + } + + /* iterate the cursors */ + dummy_cursor_iov_cnt = 1; + iov_discard_front(&out_cursor, &dummy_cursor_iov_cnt, cmd_size); + dummy_cursor_iov_cnt = 1; + iov_discard_front(&in_cursor, &dummy_cursor_iov_cnt, sizeof(*s->status)); + + return 0; +} + +static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n, + struct iovec *out_cursor, + struct iovec *in_cursor) +{ + if (virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_CTRL_MAC_ADDR)) { + const struct iovec data = { + .iov_base = (void *)n->mac, + .iov_len = sizeof(n->mac), + }; + ssize_t r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_MAC, + VIRTIO_NET_CTRL_MAC_ADDR_SET, + &data, 1); + if (unlikely(r < 0)) { + return r; + } + } + + /* + * According to VirtIO standard, "The device MUST have an + * empty MAC filtering table on reset.". + * + * Therefore, there is no need to send this CVQ command if the + * driver also sets an empty MAC filter table, which aligns with + * the device's defaults. + * + * Note that the device's defaults can mismatch the driver's + * configuration only at live migration. + */ + if (!virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_CTRL_RX) || + n->mac_table.in_use == 0) { + return 0; + } + + uint32_t uni_entries = n->mac_table.first_multi, + uni_macs_size = uni_entries * ETH_ALEN, + mul_entries = n->mac_table.in_use - uni_entries, + mul_macs_size = mul_entries * ETH_ALEN; + struct virtio_net_ctrl_mac uni = { + .entries = cpu_to_le32(uni_entries), + }; + struct virtio_net_ctrl_mac mul = { + .entries = cpu_to_le32(mul_entries), + }; + const struct iovec data[] = { + { + .iov_base = &uni, + .iov_len = sizeof(uni), + }, { + .iov_base = n->mac_table.macs, + .iov_len = uni_macs_size, + }, { + .iov_base = &mul, + .iov_len = sizeof(mul), + }, { + .iov_base = &n->mac_table.macs[uni_macs_size], + .iov_len = mul_macs_size, + }, + }; + ssize_t r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_MAC, + VIRTIO_NET_CTRL_MAC_TABLE_SET, + data, ARRAY_SIZE(data)); + if (unlikely(r < 0)) { + return r; + } + + return 0; +} + +static int vhost_vdpa_net_load_rss(VhostVDPAState *s, const VirtIONet *n, + struct iovec *out_cursor, + struct iovec *in_cursor, bool do_rss) +{ + struct virtio_net_rss_config cfg = {}; + ssize_t r; + g_autofree uint16_t *table = NULL; + + /* + * According to VirtIO standard, "Initially the device has all hash + * types disabled and reports only VIRTIO_NET_HASH_REPORT_NONE.". + * + * Therefore, there is no need to send this CVQ command if the + * driver disables the all hash types, which aligns with + * the device's defaults. + * + * Note that the device's defaults can mismatch the driver's + * configuration only at live migration. + */ + if (!n->rss_data.enabled || + n->rss_data.hash_types == VIRTIO_NET_HASH_REPORT_NONE) { + return 0; + } + + table = g_malloc_n(n->rss_data.indirections_len, + sizeof(n->rss_data.indirections_table[0])); + cfg.hash_types = cpu_to_le32(n->rss_data.hash_types); + + if (do_rss) { + /* + * According to VirtIO standard, "Number of entries in indirection_table + * is (indirection_table_mask + 1)". + */ + cfg.indirection_table_mask = cpu_to_le16(n->rss_data.indirections_len - + 1); + cfg.unclassified_queue = cpu_to_le16(n->rss_data.default_queue); + for (int i = 0; i < n->rss_data.indirections_len; ++i) { + table[i] = cpu_to_le16(n->rss_data.indirections_table[i]); + } + cfg.max_tx_vq = cpu_to_le16(n->curr_queue_pairs); + } else { + /* + * According to VirtIO standard, "Field reserved MUST contain zeroes. + * It is defined to make the structure to match the layout of + * virtio_net_rss_config structure, defined in 5.1.6.5.7.". + * + * Therefore, we need to zero the fields in + * struct virtio_net_rss_config, which corresponds to the + * `reserved` field in struct virtio_net_hash_config. + * + * Note that all other fields are zeroed at their definitions, + * except for the `indirection_table` field, where the actual data + * is stored in the `table` variable to ensure compatibility + * with RSS case. Therefore, we need to zero the `table` variable here. + */ + table[0] = 0; + } + + /* + * Considering that virtio_net_handle_rss() currently does not restore + * the hash key length parsed from the CVQ command sent from the guest + * into n->rss_data and uses the maximum key length in other code, so + * we also employ the maximum key length here. + */ + cfg.hash_key_length = sizeof(n->rss_data.key); + + const struct iovec data[] = { + { + .iov_base = &cfg, + .iov_len = offsetof(struct virtio_net_rss_config, + indirection_table), + }, { + .iov_base = table, + .iov_len = n->rss_data.indirections_len * + sizeof(n->rss_data.indirections_table[0]), + }, { + .iov_base = &cfg.max_tx_vq, + .iov_len = offsetof(struct virtio_net_rss_config, hash_key_data) - + offsetof(struct virtio_net_rss_config, max_tx_vq), + }, { + .iov_base = (void *)n->rss_data.key, + .iov_len = sizeof(n->rss_data.key), + } + }; + + r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_MQ, + do_rss ? VIRTIO_NET_CTRL_MQ_RSS_CONFIG : + VIRTIO_NET_CTRL_MQ_HASH_CONFIG, + data, ARRAY_SIZE(data)); + if (unlikely(r < 0)) { + return r; + } + + return 0; +} + +static int vhost_vdpa_net_load_mq(VhostVDPAState *s, + const VirtIONet *n, + struct iovec *out_cursor, + struct iovec *in_cursor) +{ + struct virtio_net_ctrl_mq mq; + ssize_t r; + + if (!virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_MQ)) { + return 0; + } + + trace_vhost_vdpa_net_load_mq(s, n->curr_queue_pairs); + + mq.virtqueue_pairs = cpu_to_le16(n->curr_queue_pairs); + const struct iovec data = { + .iov_base = &mq, + .iov_len = sizeof(mq), + }; + r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_MQ, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, + &data, 1); + if (unlikely(r < 0)) { + return r; + } + + if (virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_RSS)) { + /* load the receive-side scaling state */ + r = vhost_vdpa_net_load_rss(s, n, out_cursor, in_cursor, true); + if (unlikely(r < 0)) { + return r; + } + } else if (virtio_vdev_has_feature(&n->parent_obj, + VIRTIO_NET_F_HASH_REPORT)) { + /* load the hash calculation state */ + r = vhost_vdpa_net_load_rss(s, n, out_cursor, in_cursor, false); + if (unlikely(r < 0)) { + return r; + } + } + + return 0; +} + +static int vhost_vdpa_net_load_offloads(VhostVDPAState *s, + const VirtIONet *n, + struct iovec *out_cursor, + struct iovec *in_cursor) +{ + uint64_t offloads; + ssize_t r; + + if (!virtio_vdev_has_feature(&n->parent_obj, + VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { + return 0; + } + + if (n->curr_guest_offloads == virtio_net_supported_guest_offloads(n)) { + /* + * According to VirtIO standard, "Upon feature negotiation + * corresponding offload gets enabled to preserve + * backward compatibility.". + * + * Therefore, there is no need to send this CVQ command if the + * driver also enables all supported offloads, which aligns with + * the device's defaults. + * + * Note that the device's defaults can mismatch the driver's + * configuration only at live migration. + */ + return 0; + } + + offloads = cpu_to_le64(n->curr_guest_offloads); + const struct iovec data = { + .iov_base = &offloads, + .iov_len = sizeof(offloads), + }; + r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_GUEST_OFFLOADS, + VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, + &data, 1); + if (unlikely(r < 0)) { + return r; + } + + return 0; +} + +static int vhost_vdpa_net_load_rx_mode(VhostVDPAState *s, + struct iovec *out_cursor, + struct iovec *in_cursor, + uint8_t cmd, + uint8_t on) +{ + const struct iovec data = { + .iov_base = &on, + .iov_len = sizeof(on), + }; + ssize_t r; + + r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_RX, cmd, &data, 1); + if (unlikely(r < 0)) { + return r; + } + + return 0; +} + +static int vhost_vdpa_net_load_rx(VhostVDPAState *s, + const VirtIONet *n, + struct iovec *out_cursor, + struct iovec *in_cursor) +{ + ssize_t r; + + if (!virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_CTRL_RX)) { + return 0; + } + + /* + * According to virtio_net_reset(), device turns promiscuous mode + * on by default. + * + * Additionally, according to VirtIO standard, "Since there are + * no guarantees, it can use a hash filter or silently switch to + * allmulti or promiscuous mode if it is given too many addresses.". + * QEMU marks `n->mac_table.uni_overflow` if guest sets too many + * non-multicast MAC addresses, indicating that promiscuous mode + * should be enabled. + * + * Therefore, QEMU should only send this CVQ command if the + * `n->mac_table.uni_overflow` is not marked and `n->promisc` is off, + * which sets promiscuous mode on, different from the device's defaults. + * + * Note that the device's defaults can mismatch the driver's + * configuration only at live migration. + */ + if (!n->mac_table.uni_overflow && !n->promisc) { + r = vhost_vdpa_net_load_rx_mode(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_RX_PROMISC, 0); + if (unlikely(r < 0)) { + return r; + } + } + + /* + * According to virtio_net_reset(), device turns all-multicast mode + * off by default. + * + * According to VirtIO standard, "Since there are no guarantees, + * it can use a hash filter or silently switch to allmulti or + * promiscuous mode if it is given too many addresses.". QEMU marks + * `n->mac_table.multi_overflow` if guest sets too many + * non-multicast MAC addresses. + * + * Therefore, QEMU should only send this CVQ command if the + * `n->mac_table.multi_overflow` is marked or `n->allmulti` is on, + * which sets all-multicast mode on, different from the device's defaults. + * + * Note that the device's defaults can mismatch the driver's + * configuration only at live migration. + */ + if (n->mac_table.multi_overflow || n->allmulti) { + r = vhost_vdpa_net_load_rx_mode(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_RX_ALLMULTI, 1); + if (unlikely(r < 0)) { + return r; + } + } + + if (!virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_CTRL_RX_EXTRA)) { + return 0; + } + + /* + * According to virtio_net_reset(), device turns all-unicast mode + * off by default. + * + * Therefore, QEMU should only send this CVQ command if the driver + * sets all-unicast mode on, different from the device's defaults. + * + * Note that the device's defaults can mismatch the driver's + * configuration only at live migration. + */ + if (n->alluni) { + r = vhost_vdpa_net_load_rx_mode(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_RX_ALLUNI, 1); + if (r < 0) { + return r; + } + } + + /* + * According to virtio_net_reset(), device turns non-multicast mode + * off by default. + * + * Therefore, QEMU should only send this CVQ command if the driver + * sets non-multicast mode on, different from the device's defaults. + * + * Note that the device's defaults can mismatch the driver's + * configuration only at live migration. + */ + if (n->nomulti) { + r = vhost_vdpa_net_load_rx_mode(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_RX_NOMULTI, 1); + if (r < 0) { + return r; + } + } + + /* + * According to virtio_net_reset(), device turns non-unicast mode + * off by default. + * + * Therefore, QEMU should only send this CVQ command if the driver + * sets non-unicast mode on, different from the device's defaults. + * + * Note that the device's defaults can mismatch the driver's + * configuration only at live migration. + */ + if (n->nouni) { + r = vhost_vdpa_net_load_rx_mode(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_RX_NOUNI, 1); + if (r < 0) { + return r; + } + } + + /* + * According to virtio_net_reset(), device turns non-broadcast mode + * off by default. + * + * Therefore, QEMU should only send this CVQ command if the driver + * sets non-broadcast mode on, different from the device's defaults. + * + * Note that the device's defaults can mismatch the driver's + * configuration only at live migration. + */ + if (n->nobcast) { + r = vhost_vdpa_net_load_rx_mode(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_RX_NOBCAST, 1); + if (r < 0) { + return r; + } + } + + return 0; +} + +static int vhost_vdpa_net_load_single_vlan(VhostVDPAState *s, + const VirtIONet *n, + struct iovec *out_cursor, + struct iovec *in_cursor, + uint16_t vid) +{ + const struct iovec data = { + .iov_base = &vid, + .iov_len = sizeof(vid), + }; + ssize_t r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_VLAN, + VIRTIO_NET_CTRL_VLAN_ADD, + &data, 1); + if (unlikely(r < 0)) { + return r; + } + + return 0; +} + +static int vhost_vdpa_net_load_vlan(VhostVDPAState *s, + const VirtIONet *n, + struct iovec *out_cursor, + struct iovec *in_cursor) +{ + int r; + + if (!virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_CTRL_VLAN)) { + return 0; + } + + for (int i = 0; i < MAX_VLAN >> 5; i++) { + for (int j = 0; n->vlans[i] && j <= 0x1f; j++) { + if (n->vlans[i] & (1U << j)) { + r = vhost_vdpa_net_load_single_vlan(s, n, out_cursor, + in_cursor, (i << 5) + j); + if (unlikely(r != 0)) { + return r; + } + } + } + } + + return 0; +} + +static int vhost_vdpa_net_cvq_load(NetClientState *nc) +{ + VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); + struct vhost_vdpa *v = &s->vhost_vdpa; + const VirtIONet *n; + int r; + struct iovec out_cursor, in_cursor; + + assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); + + vhost_vdpa_set_vring_ready(v, v->dev->vq_index); + + if (v->shadow_vqs_enabled) { + n = VIRTIO_NET(v->dev->vdev); + vhost_vdpa_net_load_cursor_reset(s, &out_cursor, &in_cursor); + r = vhost_vdpa_net_load_mac(s, n, &out_cursor, &in_cursor); + if (unlikely(r < 0)) { + return r; + } + r = vhost_vdpa_net_load_mq(s, n, &out_cursor, &in_cursor); + if (unlikely(r)) { + return r; + } + r = vhost_vdpa_net_load_offloads(s, n, &out_cursor, &in_cursor); + if (unlikely(r)) { + return r; + } + r = vhost_vdpa_net_load_rx(s, n, &out_cursor, &in_cursor); + if (unlikely(r)) { + return r; + } + r = vhost_vdpa_net_load_vlan(s, n, &out_cursor, &in_cursor); + if (unlikely(r)) { + return r; + } + + /* + * We need to poll and check all pending device's used buffers. + * + * We can poll here since we've had BQL from the time + * we sent the descriptor. + */ + r = vhost_vdpa_net_svq_flush(s, in_cursor.iov_base - (void *)s->status); + if (unlikely(r)) { + return r; + } + } + + for (int i = 0; i < v->dev->vq_index; ++i) { + vhost_vdpa_set_vring_ready(v, i); + } + + return 0; +} + +static NetClientInfo net_vhost_vdpa_cvq_info = { + .type = NET_CLIENT_DRIVER_VHOST_VDPA, + .size = sizeof(VhostVDPAState), + .receive = vhost_vdpa_receive, + .start = vhost_vdpa_net_cvq_start, + .load = vhost_vdpa_net_cvq_load, + .stop = vhost_vdpa_net_cvq_stop, + .cleanup = vhost_vdpa_cleanup, + .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, + .has_ufo = vhost_vdpa_has_ufo, + .check_peer_type = vhost_vdpa_check_peer_type, + .set_steering_ebpf = vhost_vdpa_set_steering_ebpf, +}; + +/* + * Forward the excessive VIRTIO_NET_CTRL_MAC_TABLE_SET CVQ command to + * vdpa device. + * + * Considering that QEMU cannot send the entire filter table to the + * vdpa device, it should send the VIRTIO_NET_CTRL_RX_PROMISC CVQ + * command to enable promiscuous mode to receive all packets, + * according to VirtIO standard, "Since there are no guarantees, + * it can use a hash filter or silently switch to allmulti or + * promiscuous mode if it is given too many addresses.". + * + * Since QEMU ignores MAC addresses beyond `MAC_TABLE_ENTRIES` and + * marks `n->mac_table.x_overflow` accordingly, it should have + * the same effect on the device model to receive + * (`MAC_TABLE_ENTRIES` + 1) or more non-multicast MAC addresses. + * The same applies to multicast MAC addresses. + * + * Therefore, QEMU can provide the device model with a fake + * VIRTIO_NET_CTRL_MAC_TABLE_SET command with (`MAC_TABLE_ENTRIES` + 1) + * non-multicast MAC addresses and (`MAC_TABLE_ENTRIES` + 1) multicast + * MAC addresses. This ensures that the device model marks + * `n->mac_table.uni_overflow` and `n->mac_table.multi_overflow`, + * allowing all packets to be received, which aligns with the + * state of the vdpa device. + */ +static int vhost_vdpa_net_excessive_mac_filter_cvq_add(VhostVDPAState *s, + VirtQueueElement *elem, + struct iovec *out, + const struct iovec *in) +{ + struct virtio_net_ctrl_mac mac_data, *mac_ptr; + struct virtio_net_ctrl_hdr *hdr_ptr; + uint32_t cursor; + ssize_t r; + uint8_t on = 1; + + /* parse the non-multicast MAC address entries from CVQ command */ + cursor = sizeof(*hdr_ptr); + r = iov_to_buf(elem->out_sg, elem->out_num, cursor, + &mac_data, sizeof(mac_data)); + if (unlikely(r != sizeof(mac_data))) { + /* + * If the CVQ command is invalid, we should simulate the vdpa device + * to reject the VIRTIO_NET_CTRL_MAC_TABLE_SET CVQ command + */ + *s->status = VIRTIO_NET_ERR; + return sizeof(*s->status); + } + cursor += sizeof(mac_data) + le32_to_cpu(mac_data.entries) * ETH_ALEN; + + /* parse the multicast MAC address entries from CVQ command */ + r = iov_to_buf(elem->out_sg, elem->out_num, cursor, + &mac_data, sizeof(mac_data)); + if (r != sizeof(mac_data)) { + /* + * If the CVQ command is invalid, we should simulate the vdpa device + * to reject the VIRTIO_NET_CTRL_MAC_TABLE_SET CVQ command + */ + *s->status = VIRTIO_NET_ERR; + return sizeof(*s->status); + } + cursor += sizeof(mac_data) + le32_to_cpu(mac_data.entries) * ETH_ALEN; + + /* validate the CVQ command */ + if (iov_size(elem->out_sg, elem->out_num) != cursor) { + /* + * If the CVQ command is invalid, we should simulate the vdpa device + * to reject the VIRTIO_NET_CTRL_MAC_TABLE_SET CVQ command + */ + *s->status = VIRTIO_NET_ERR; + return sizeof(*s->status); + } + + /* + * According to VirtIO standard, "Since there are no guarantees, + * it can use a hash filter or silently switch to allmulti or + * promiscuous mode if it is given too many addresses.". + * + * Therefore, considering that QEMU is unable to send the entire + * filter table to the vdpa device, it should send the + * VIRTIO_NET_CTRL_RX_PROMISC CVQ command to enable promiscuous mode + */ + hdr_ptr = out->iov_base; + out->iov_len = sizeof(*hdr_ptr) + sizeof(on); + + hdr_ptr->class = VIRTIO_NET_CTRL_RX; + hdr_ptr->cmd = VIRTIO_NET_CTRL_RX_PROMISC; + iov_from_buf(out, 1, sizeof(*hdr_ptr), &on, sizeof(on)); + r = vhost_vdpa_net_cvq_add(s, out, 1, in, 1); + if (unlikely(r < 0)) { + return r; + } + + /* + * We can poll here since we've had BQL from the time + * we sent the descriptor. + */ + r = vhost_vdpa_net_svq_poll(s, 1); + if (unlikely(r < sizeof(*s->status))) { + return r; + } + if (*s->status != VIRTIO_NET_OK) { + return sizeof(*s->status); + } + + /* + * QEMU should also send a fake VIRTIO_NET_CTRL_MAC_TABLE_SET CVQ + * command to the device model, including (`MAC_TABLE_ENTRIES` + 1) + * non-multicast MAC addresses and (`MAC_TABLE_ENTRIES` + 1) + * multicast MAC addresses. + * + * By doing so, the device model can mark `n->mac_table.uni_overflow` + * and `n->mac_table.multi_overflow`, enabling all packets to be + * received, which aligns with the state of the vdpa device. + */ + cursor = 0; + uint32_t fake_uni_entries = MAC_TABLE_ENTRIES + 1, + fake_mul_entries = MAC_TABLE_ENTRIES + 1, + fake_cvq_size = sizeof(struct virtio_net_ctrl_hdr) + + sizeof(mac_data) + fake_uni_entries * ETH_ALEN + + sizeof(mac_data) + fake_mul_entries * ETH_ALEN; + + assert(fake_cvq_size < vhost_vdpa_net_cvq_cmd_page_len()); + out->iov_len = fake_cvq_size; + + /* pack the header for fake CVQ command */ + hdr_ptr = out->iov_base + cursor; + hdr_ptr->class = VIRTIO_NET_CTRL_MAC; + hdr_ptr->cmd = VIRTIO_NET_CTRL_MAC_TABLE_SET; + cursor += sizeof(*hdr_ptr); + + /* + * Pack the non-multicast MAC addresses part for fake CVQ command. + * + * According to virtio_net_handle_mac(), QEMU doesn't verify the MAC + * addresses provided in CVQ command. Therefore, only the entries + * field need to be prepared in the CVQ command. + */ + mac_ptr = out->iov_base + cursor; + mac_ptr->entries = cpu_to_le32(fake_uni_entries); + cursor += sizeof(*mac_ptr) + fake_uni_entries * ETH_ALEN; + + /* + * Pack the multicast MAC addresses part for fake CVQ command. + * + * According to virtio_net_handle_mac(), QEMU doesn't verify the MAC + * addresses provided in CVQ command. Therefore, only the entries + * field need to be prepared in the CVQ command. + */ + mac_ptr = out->iov_base + cursor; + mac_ptr->entries = cpu_to_le32(fake_mul_entries); + + /* + * Simulating QEMU poll a vdpa device used buffer + * for VIRTIO_NET_CTRL_MAC_TABLE_SET CVQ command + */ + return sizeof(*s->status); +} + +/** + * Validate and copy control virtqueue commands. + * + * Following QEMU guidelines, we offer a copy of the buffers to the device to + * prevent TOCTOU bugs. + */ +static int vhost_vdpa_net_handle_ctrl_avail(VhostShadowVirtqueue *svq, + VirtQueueElement *elem, + void *opaque) +{ + VhostVDPAState *s = opaque; + size_t in_len; + const struct virtio_net_ctrl_hdr *ctrl; + virtio_net_ctrl_ack status = VIRTIO_NET_ERR; + /* Out buffer sent to both the vdpa device and the device model */ + struct iovec out = { + .iov_base = s->cvq_cmd_out_buffer, + }; + /* in buffer used for device model */ + const struct iovec model_in = { + .iov_base = &status, + .iov_len = sizeof(status), + }; + /* in buffer used for vdpa device */ + const struct iovec vdpa_in = { + .iov_base = s->status, + .iov_len = sizeof(*s->status), + }; + ssize_t dev_written = -EINVAL; + + out.iov_len = iov_to_buf(elem->out_sg, elem->out_num, 0, + s->cvq_cmd_out_buffer, + vhost_vdpa_net_cvq_cmd_page_len()); + + ctrl = s->cvq_cmd_out_buffer; + if (ctrl->class == VIRTIO_NET_CTRL_ANNOUNCE) { + /* + * Guest announce capability is emulated by qemu, so don't forward to + * the device. + */ + dev_written = sizeof(status); + *s->status = VIRTIO_NET_OK; + } else if (unlikely(ctrl->class == VIRTIO_NET_CTRL_MAC && + ctrl->cmd == VIRTIO_NET_CTRL_MAC_TABLE_SET && + iov_size(elem->out_sg, elem->out_num) > out.iov_len)) { + /* + * Due to the size limitation of the out buffer sent to the vdpa device, + * which is determined by vhost_vdpa_net_cvq_cmd_page_len(), excessive + * MAC addresses set by the driver for the filter table can cause + * truncation of the CVQ command in QEMU. As a result, the vdpa device + * rejects the flawed CVQ command. + * + * Therefore, QEMU must handle this situation instead of sending + * the CVQ command directly. + */ + dev_written = vhost_vdpa_net_excessive_mac_filter_cvq_add(s, elem, + &out, &vdpa_in); + if (unlikely(dev_written < 0)) { + goto out; + } + } else { + ssize_t r; + r = vhost_vdpa_net_cvq_add(s, &out, 1, &vdpa_in, 1); + if (unlikely(r < 0)) { + dev_written = r; + goto out; + } + + /* + * We can poll here since we've had BQL from the time + * we sent the descriptor. + */ + dev_written = vhost_vdpa_net_svq_poll(s, 1); + } + + if (unlikely(dev_written < sizeof(status))) { + error_report("Insufficient written data (%zu)", dev_written); + goto out; + } + + if (*s->status != VIRTIO_NET_OK) { + goto out; + } + + status = VIRTIO_NET_ERR; + virtio_net_handle_ctrl_iov(svq->vdev, &model_in, 1, &out, 1); + if (status != VIRTIO_NET_OK) { + error_report("Bad CVQ processing in model"); + } + +out: + in_len = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, + sizeof(status)); + if (unlikely(in_len < sizeof(status))) { + error_report("Bad device CVQ written length"); + } + vhost_svq_push_elem(svq, elem, MIN(in_len, sizeof(status))); + /* + * `elem` belongs to vhost_vdpa_net_handle_ctrl_avail() only when + * the function successfully forwards the CVQ command, indicated + * by a non-negative value of `dev_written`. Otherwise, it still + * belongs to SVQ. + * This function should only free the `elem` when it owns. + */ + if (dev_written >= 0) { + g_free(elem); + } + return dev_written < 0 ? dev_written : 0; +} + +static const VhostShadowVirtqueueOps vhost_vdpa_net_svq_ops = { + .avail_handler = vhost_vdpa_net_handle_ctrl_avail, +}; + +/** + * Probe if CVQ is isolated + * + * @device_fd The vdpa device fd + * @features Features offered by the device. + * @cvq_index The control vq pair index + * + * Returns <0 in case of failure, 0 if false and 1 if true. + */ +static int vhost_vdpa_probe_cvq_isolation(int device_fd, uint64_t features, + int cvq_index, Error **errp) +{ + ERRP_GUARD(); + uint64_t backend_features; + int64_t cvq_group; + uint8_t status = VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER; + int r; + + r = ioctl(device_fd, VHOST_GET_BACKEND_FEATURES, &backend_features); + if (unlikely(r < 0)) { + error_setg_errno(errp, errno, "Cannot get vdpa backend_features"); + return r; + } + + if (!(backend_features & BIT_ULL(VHOST_BACKEND_F_IOTLB_ASID))) { + return 0; + } + + r = ioctl(device_fd, VHOST_VDPA_SET_STATUS, &status); + if (unlikely(r)) { + error_setg_errno(errp, -r, "Cannot set device status"); + goto out; + } + + r = ioctl(device_fd, VHOST_SET_FEATURES, &features); + if (unlikely(r)) { + error_setg_errno(errp, -r, "Cannot set features"); + goto out; + } + + status |= VIRTIO_CONFIG_S_FEATURES_OK; + r = ioctl(device_fd, VHOST_VDPA_SET_STATUS, &status); + if (unlikely(r)) { + error_setg_errno(errp, -r, "Cannot set device status"); + goto out; + } + + cvq_group = vhost_vdpa_get_vring_group(device_fd, cvq_index, errp); + if (unlikely(cvq_group < 0)) { + if (cvq_group != -ENOTSUP) { + r = cvq_group; + goto out; + } + + /* + * The kernel report VHOST_BACKEND_F_IOTLB_ASID if the vdpa frontend + * support ASID even if the parent driver does not. The CVQ cannot be + * isolated in this case. + */ + error_free(*errp); + *errp = NULL; + r = 0; + goto out; + } + + for (int i = 0; i < cvq_index; ++i) { + int64_t group = vhost_vdpa_get_vring_group(device_fd, i, errp); + if (unlikely(group < 0)) { + r = group; + goto out; + } + + if (group == (int64_t)cvq_group) { + r = 0; + goto out; + } + } + + r = 1; + +out: + status = 0; + ioctl(device_fd, VHOST_VDPA_SET_STATUS, &status); + return r; +} + static NetClientState *net_vhost_vdpa_init(NetClientState *peer, - const char *device, - const char *name, - int vdpa_device_fd, - int queue_pair_index, - int nvqs, - bool is_datapath) + const char *device, + const char *name, + int vdpa_device_fd, + int queue_pair_index, + int nvqs, + bool is_datapath, + bool svq, + struct vhost_vdpa_iova_range iova_range, + uint64_t features, + VhostVDPAShared *shared, + Error **errp) { NetClientState *nc = NULL; VhostVDPAState *s; int ret = 0; assert(name); + int cvq_isolated = 0; + if (is_datapath) { nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name); } else { - nc = qemu_new_net_control_client(&net_vhost_vdpa_info, peer, + cvq_isolated = vhost_vdpa_probe_cvq_isolation(vdpa_device_fd, features, + queue_pair_index * 2, + errp); + if (unlikely(cvq_isolated < 0)) { + return NULL; + } + + nc = qemu_new_net_control_client(&net_vhost_vdpa_cvq_info, peer, device, name); } - snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA); + qemu_set_info_str(nc, TYPE_VHOST_VDPA); s = DO_UPCAST(VhostVDPAState, nc, nc); - s->vhost_vdpa.device_fd = vdpa_device_fd; s->vhost_vdpa.index = queue_pair_index; + s->always_svq = svq; + s->migration_state.notify = NULL; + s->vhost_vdpa.shadow_vqs_enabled = svq; + if (queue_pair_index == 0) { + vhost_vdpa_net_valid_svq_features(features, + &s->vhost_vdpa.migration_blocker); + s->vhost_vdpa.shared = g_new0(VhostVDPAShared, 1); + s->vhost_vdpa.shared->device_fd = vdpa_device_fd; + s->vhost_vdpa.shared->iova_range = iova_range; + s->vhost_vdpa.shared->shadow_data = svq; + } else if (!is_datapath) { + s->cvq_cmd_out_buffer = mmap(NULL, vhost_vdpa_net_cvq_cmd_page_len(), + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + s->status = mmap(NULL, vhost_vdpa_net_cvq_cmd_page_len(), + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, + -1, 0); + + s->vhost_vdpa.shadow_vq_ops = &vhost_vdpa_net_svq_ops; + s->vhost_vdpa.shadow_vq_ops_opaque = s; + s->cvq_isolated = cvq_isolated; + } + if (queue_pair_index != 0) { + s->vhost_vdpa.shared = shared; + } + ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa, queue_pair_index, nvqs); if (ret) { qemu_del_net_client(nc); return NULL; } + return nc; } -static int vhost_vdpa_get_max_queue_pairs(int fd, int *has_cvq, Error **errp) +static int vhost_vdpa_get_features(int fd, uint64_t *features, Error **errp) +{ + int ret = ioctl(fd, VHOST_GET_FEATURES, features); + if (unlikely(ret < 0)) { + error_setg_errno(errp, errno, + "Fail to query features from vhost-vDPA device"); + } + return ret; +} + +static int vhost_vdpa_get_max_queue_pairs(int fd, uint64_t features, + int *has_cvq, Error **errp) { unsigned long config_size = offsetof(struct vhost_vdpa_config, buf); g_autofree struct vhost_vdpa_config *config = NULL; __virtio16 *max_queue_pairs; - uint64_t features; int ret; - ret = ioctl(fd, VHOST_GET_FEATURES, &features); - if (ret) { - error_setg(errp, "Fail to query features from vhost-vDPA device"); - return ret; - } - if (features & (1 << VIRTIO_NET_F_CTRL_VQ)) { *has_cvq = 1; } else { @@ -261,43 +1775,89 @@ static int vhost_vdpa_get_max_queue_pairs(int fd, int *has_cvq, Error **errp) int net_init_vhost_vdpa(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp) { + ERRP_GUARD(); const NetdevVhostVDPAOptions *opts; + uint64_t features; int vdpa_device_fd; g_autofree NetClientState **ncs = NULL; + struct vhost_vdpa_iova_range iova_range; NetClientState *nc; - int queue_pairs, i, has_cvq = 0; + int queue_pairs, r, i = 0, has_cvq = 0; assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); opts = &netdev->u.vhost_vdpa; - if (!opts->vhostdev) { - error_setg(errp, "vdpa character device not specified with vhostdev"); + if (!opts->vhostdev && !opts->vhostfd) { + error_setg(errp, + "vhost-vdpa: neither vhostdev= nor vhostfd= was specified"); return -1; } - vdpa_device_fd = qemu_open(opts->vhostdev, O_RDWR, errp); - if (vdpa_device_fd == -1) { - return -errno; + if (opts->vhostdev && opts->vhostfd) { + error_setg(errp, + "vhost-vdpa: vhostdev= and vhostfd= are mutually exclusive"); + return -1; } - queue_pairs = vhost_vdpa_get_max_queue_pairs(vdpa_device_fd, + if (opts->vhostdev) { + vdpa_device_fd = qemu_open(opts->vhostdev, O_RDWR, errp); + if (vdpa_device_fd == -1) { + return -errno; + } + } else { + /* has_vhostfd */ + vdpa_device_fd = monitor_fd_param(monitor_cur(), opts->vhostfd, errp); + if (vdpa_device_fd == -1) { + error_prepend(errp, "vhost-vdpa: unable to parse vhostfd: "); + return -1; + } + } + + r = vhost_vdpa_get_features(vdpa_device_fd, &features, errp); + if (unlikely(r < 0)) { + goto err; + } + + queue_pairs = vhost_vdpa_get_max_queue_pairs(vdpa_device_fd, features, &has_cvq, errp); if (queue_pairs < 0) { qemu_close(vdpa_device_fd); return queue_pairs; } + r = vhost_vdpa_get_iova_range(vdpa_device_fd, &iova_range); + if (unlikely(r < 0)) { + error_setg(errp, "vhost-vdpa: get iova range failed: %s", + strerror(-r)); + goto err; + } + + if (opts->x_svq && !vhost_vdpa_net_valid_svq_features(features, errp)) { + goto err; + } + ncs = g_malloc0(sizeof(*ncs) * queue_pairs); for (i = 0; i < queue_pairs; i++) { + VhostVDPAShared *shared = NULL; + + if (i) { + shared = DO_UPCAST(VhostVDPAState, nc, ncs[0])->vhost_vdpa.shared; + } ncs[i] = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, - vdpa_device_fd, i, 2, true); + vdpa_device_fd, i, 2, true, opts->x_svq, + iova_range, features, shared, errp); if (!ncs[i]) goto err; } if (has_cvq) { + VhostVDPAState *s0 = DO_UPCAST(VhostVDPAState, nc, ncs[0]); + VhostVDPAShared *shared = s0->vhost_vdpa.shared; + nc = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, - vdpa_device_fd, i, 1, false); + vdpa_device_fd, i, 1, false, + opts->x_svq, iova_range, features, shared, + errp); if (!nc) goto err; } @@ -310,6 +1870,7 @@ err: qemu_del_net_client(ncs[i]); } } + qemu_close(vdpa_device_fd); return -1; diff --git a/net/vmnet-bridged.m b/net/vmnet-bridged.m index 46d2282863..76a28abe79 100644 --- a/net/vmnet-bridged.m +++ b/net/vmnet-bridged.m @@ -37,7 +37,7 @@ done: } -static char* get_valid_ifnames() +static char* get_valid_ifnames(void) { xpc_object_t shared_if_list = vmnet_copy_shared_interface_list(); __block char *if_list = NULL; diff --git a/net/vmnet-common.m b/net/vmnet-common.m index 2cb60b9ddd..2958283485 100644 --- a/net/vmnet-common.m +++ b/net/vmnet-common.m @@ -17,6 +17,7 @@ #include "clients.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "sysemu/runstate.h" #include #include @@ -242,6 +243,35 @@ static void vmnet_bufs_init(VmnetState *s) } } +/** + * Called on state change to un-register/re-register handlers + */ +static void vmnet_vm_state_change_cb(void *opaque, bool running, RunState state) +{ + VmnetState *s = opaque; + + if (running) { + vmnet_interface_set_event_callback( + s->vmnet_if, + VMNET_INTERFACE_PACKETS_AVAILABLE, + s->if_queue, + ^(interface_event_t event_id, xpc_object_t event) { + assert(event_id == VMNET_INTERFACE_PACKETS_AVAILABLE); + /* + * This function is being called from a non qemu thread, so + * we only schedule a BH, and do the rest of the io completion + * handling from vmnet_send_bh() which runs in a qemu context. + */ + qemu_bh_schedule(s->send_bh); + }); + } else { + vmnet_interface_set_event_callback( + s->vmnet_if, + VMNET_INTERFACE_PACKETS_AVAILABLE, + NULL, + NULL); + } +} int vmnet_if_create(NetClientState *nc, xpc_object_t if_desc, @@ -329,19 +359,9 @@ int vmnet_if_create(NetClientState *nc, s->packets_send_current_pos = 0; s->packets_send_end_pos = 0; - vmnet_interface_set_event_callback( - s->vmnet_if, - VMNET_INTERFACE_PACKETS_AVAILABLE, - s->if_queue, - ^(interface_event_t event_id, xpc_object_t event) { - assert(event_id == VMNET_INTERFACE_PACKETS_AVAILABLE); - /* - * This function is being called from a non qemu thread, so - * we only schedule a BH, and do the rest of the io completion - * handling from vmnet_send_bh() which runs in a qemu context. - */ - qemu_bh_schedule(s->send_bh); - }); + vmnet_vm_state_change_cb(s, 1, RUN_STATE_RUNNING); + + s->change = qemu_add_vm_change_state_handler(vmnet_vm_state_change_cb, s); return 0; } @@ -356,6 +376,8 @@ void vmnet_cleanup_common(NetClientState *nc) return; } + vmnet_vm_state_change_cb(s, 0, RUN_STATE_SHUTDOWN); + qemu_del_vm_change_state_handler(s->change); if_stopped_sem = dispatch_semaphore_create(0); vmnet_stop_interface( s->vmnet_if, diff --git a/net/vmnet-host.c b/net/vmnet-host.c index 05f8d78864..1f95f7343a 100644 --- a/net/vmnet-host.c +++ b/net/vmnet-host.c @@ -26,7 +26,7 @@ static bool validate_options(const Netdev *netdev, Error **errp) MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 QemuUUID net_uuid; - if (options->has_net_uuid && + if (options->net_uuid && qemu_uuid_parse(options->net_uuid, &net_uuid) < 0) { error_setg(errp, "Invalid UUID provided in 'net-uuid'"); return false; @@ -39,7 +39,7 @@ static bool validate_options(const Netdev *netdev, Error **errp) return false; } - if (options->has_net_uuid) { + if (options->net_uuid) { error_setg(errp, "vmnet-host.net-uuid feature is " "unavailable: outdated vmnet.framework API"); @@ -47,12 +47,12 @@ static bool validate_options(const Netdev *netdev, Error **errp) } #endif - if ((options->has_start_address || - options->has_end_address || - options->has_subnet_mask) && - !(options->has_start_address && - options->has_end_address && - options->has_subnet_mask)) { + if ((options->start_address || + options->end_address || + options->subnet_mask) && + !(options->start_address && + options->end_address && + options->subnet_mask)) { error_setg(errp, "'start-address', 'end-address', 'subnet-mask' " "should be provided together"); @@ -79,7 +79,7 @@ static xpc_object_t build_if_desc(const Netdev *netdev) options->isolated); QemuUUID net_uuid; - if (options->has_net_uuid) { + if (options->net_uuid) { qemu_uuid_parse(options->net_uuid, &net_uuid); xpc_dictionary_set_uuid(if_desc, vmnet_network_identifier_key, @@ -87,7 +87,7 @@ static xpc_object_t build_if_desc(const Netdev *netdev) } #endif - if (options->has_start_address) { + if (options->start_address) { xpc_dictionary_set_string(if_desc, vmnet_start_address_key, options->start_address); diff --git a/net/vmnet-shared.c b/net/vmnet-shared.c index 18cadc72bd..40c7306a75 100644 --- a/net/vmnet-shared.c +++ b/net/vmnet-shared.c @@ -31,12 +31,12 @@ static bool validate_options(const Netdev *netdev, Error **errp) } #endif - if ((options->has_start_address || - options->has_end_address || - options->has_subnet_mask) && - !(options->has_start_address && - options->has_end_address && - options->has_subnet_mask)) { + if ((options->start_address || + options->end_address || + options->subnet_mask) && + !(options->start_address && + options->end_address && + options->subnet_mask)) { error_setg(errp, "'start-address', 'end-address', 'subnet-mask' " "should be provided together" @@ -58,13 +58,13 @@ static xpc_object_t build_if_desc(const Netdev *netdev) VMNET_SHARED_MODE ); - if (options->has_nat66_prefix) { + if (options->nat66_prefix) { xpc_dictionary_set_string(if_desc, vmnet_nat66_prefix_key, options->nat66_prefix); } - if (options->has_start_address) { + if (options->start_address) { xpc_dictionary_set_string(if_desc, vmnet_start_address_key, options->start_address); diff --git a/net/vmnet_int.h b/net/vmnet_int.h index adf6e8c20d..a8a033dc96 100644 --- a/net/vmnet_int.h +++ b/net/vmnet_int.h @@ -10,7 +10,6 @@ #ifndef VMNET_INT_H #define VMNET_INT_H -#include "qemu/osdep.h" #include "vmnet_int.h" #include "clients.h" @@ -46,6 +45,8 @@ typedef struct VmnetState { int packets_send_end_pos; struct iovec iov_buf[VMNET_PACKETS_LIMIT]; + + VMChangeStateEntry *change; } VmnetState; const char *vmnet_status_map_str(vmnet_return_t status); diff --git a/os-posix.c b/os-posix.c index 321fc4bd13..a4284e2c07 100644 --- a/os-posix.c +++ b/os-posix.c @@ -24,14 +24,12 @@ */ #include "qemu/osdep.h" +#include #include #include #include #include -/* Needed early for CONFIG_BSD etc. */ -#include "net/slirp.h" -#include "qemu/qemu-options.h" #include "qemu/error-report.h" #include "qemu/log.h" #include "sysemu/runstate.h" @@ -41,17 +39,6 @@ #include #endif -/* - * Must set all three of these at once. - * Legal combinations are unset by name by uid - */ -static struct passwd *user_pwd; /* NULL non-NULL NULL */ -static uid_t user_uid = (uid_t)-1; /* -1 -1 >=0 */ -static gid_t user_gid = (gid_t)-1; /* -1 -1 >=0 */ - -static const char *chroot_dir; -static int daemonize; -static int daemon_pipe; void os_setup_early_signal_handling(void) { @@ -99,7 +86,22 @@ void os_set_proc_name(const char *s) } -static bool os_parse_runas_uid_gid(const char *optarg) +/* + * Must set all three of these at once. + * Legal combinations are unset by name by uid + */ +static struct passwd *user_pwd; /* NULL non-NULL NULL */ +static uid_t user_uid = (uid_t)-1; /* -1 -1 >=0 */ +static gid_t user_gid = (gid_t)-1; /* -1 -1 >=0 */ + +/* + * Prepare to change user ID. user_id can be one of 3 forms: + * - a username, in which case user ID will be changed to its uid, + * with primary and supplementary groups set up too; + * - a numeric uid, in which case only the uid will be set; + * - a pair of numeric uid:gid. + */ +bool os_set_runas(const char *user_id) { unsigned long lv; const char *ep; @@ -107,7 +109,14 @@ static bool os_parse_runas_uid_gid(const char *optarg) gid_t got_gid; int rc; - rc = qemu_strtoul(optarg, &ep, 0, &lv); + user_pwd = getpwnam(user_id); + if (user_pwd) { + user_uid = -1; + user_gid = -1; + return true; + } + + rc = qemu_strtoul(user_id, &ep, 0, &lv); got_uid = lv; /* overflow here is ID in C99 */ if (rc || *ep != ':' || got_uid != lv || got_uid == (uid_t)-1) { return false; @@ -125,38 +134,6 @@ static bool os_parse_runas_uid_gid(const char *optarg) return true; } -/* - * Parse OS specific command line options. - * return 0 if option handled, -1 otherwise - */ -int os_parse_cmd_args(int index, const char *optarg) -{ - switch (index) { - case QEMU_OPTION_runas: - user_pwd = getpwnam(optarg); - if (user_pwd) { - user_uid = -1; - user_gid = -1; - } else if (!os_parse_runas_uid_gid(optarg)) { - error_report("User \"%s\" doesn't exist" - " (and is not :)", - optarg); - exit(1); - } - break; - case QEMU_OPTION_chroot: - chroot_dir = optarg; - break; - case QEMU_OPTION_daemonize: - daemonize = 1; - break; - default: - return -1; - } - - return 0; -} - static void change_process_uid(void) { assert((user_uid == (uid_t)-1) || user_pwd == NULL); @@ -194,6 +171,14 @@ static void change_process_uid(void) } } + +static const char *chroot_dir; + +void os_set_chroot(const char *path) +{ + chroot_dir = path; +} + static void change_root(void) { if (chroot_dir) { @@ -209,6 +194,21 @@ static void change_root(void) } + +static int daemonize; +static int daemon_pipe; + +bool is_daemonized(void) +{ + return daemonize; +} + +int os_set_daemonize(bool d) +{ + daemonize = d; + return 0; +} + void os_daemonize(void) { if (daemonize) { @@ -257,6 +257,27 @@ void os_daemonize(void) } } +void os_setup_limits(void) +{ + struct rlimit nofile; + + if (getrlimit(RLIMIT_NOFILE, &nofile) < 0) { + warn_report("unable to query NOFILE limit: %s", strerror(errno)); + return; + } + + if (nofile.rlim_cur == nofile.rlim_max) { + return; + } + + nofile.rlim_cur = nofile.rlim_max; + + if (setrlimit(RLIMIT_NOFILE, &nofile) < 0) { + warn_report("unable to set NOFILE limit: %s", strerror(errno)); + return; + } +} + void os_setup_post(void) { int fd = 0; @@ -266,7 +287,7 @@ void os_setup_post(void) error_report("not able to chdir to /: %s", strerror(errno)); exit(1); } - TFR(fd = qemu_open_old("/dev/null", O_RDWR)); + fd = RETRY_ON_EINTR(qemu_open_old("/dev/null", O_RDWR)); if (fd == -1) { exit(1); } @@ -302,17 +323,6 @@ void os_set_line_buffering(void) setvbuf(stdout, NULL, _IOLBF, 0); } -bool is_daemonized(void) -{ - return daemonize; -} - -int os_set_daemonize(bool d) -{ - daemonize = d; - return 0; -} - int os_mlock(void) { #ifdef HAVE_MLOCKALL diff --git a/page-vary.c b/page-vary-target.c similarity index 100% rename from page-vary.c rename to page-vary-target.c diff --git a/pc-bios/Makefile b/pc-bios/Makefile deleted file mode 100644 index 315288df84..0000000000 --- a/pc-bios/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# -# NOTE: only compilable with x86 cross compile tools -# -include ../config-host.mak - -DEFINES= - -TARGETS= - -all: $(TARGETS) - -%.o: %.S - $(CC) $(DEFINES) -c -o $@ $< - -%.dtb: %.dts - dtc -I dts -O dtb -o $@ $< - -clean: - rm -f $(TARGETS) *.o *~ diff --git a/pc-bios/README b/pc-bios/README index ba6c15e769..7ffb2f43a4 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -14,18 +14,12 @@ - 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-20220110. + built from git tag qemu-slof-20230918. - VOF (Virtual Open Firmware) is a minimalistic firmware to work with -machine pseries,x-vof=on. When enabled, the firmware acts as a slim shim and QEMU implements parts of the IEEE 1275 Open Firmware interface. -- sgabios (the Serial Graphics Adapter option ROM) provides a means for - legacy x86 software to communicate with an attached serial console as - if a video card were attached. The master sources reside in a subversion - repository at http://sgabios.googlecode.com/svn/trunk. A git mirror is - available at https://gitlab.com/qemu-project/sgabios.git. - - The PXE roms come from the iPXE project. Built with BANNER_TIME 0. Sources available at http://ipxe.org. Vendor:Device ID -> ROM mapping: @@ -56,8 +50,8 @@ variable store templates built from the TianoCore community's EFI Development Kit II project . The images - were built at git tag "edk2-stable202008". The firmware binaries bundle parts - of the OpenSSL project, at git tag "OpenSSL_1_1_1g" (the OpenSSL tag is a + were built at git tag "edk2-stable202302". The firmware binaries bundle parts + of the OpenSSL project, at git tag "OpenSSL_1_1_1s" (the OpenSSL tag is a function of the edk2 tag). Parts of the Berkeley SoftFloat library are bundled as well, at Release 3e plus a subsequent typo fix (commit b64af41c3276f97f0e181920400ee056b9c88037), as an OpenSSL dependency on 32-bit @@ -73,7 +67,7 @@ and enable the use of well-known bootloaders such as U-Boot. OpenSBI is distributed under the terms of the BSD 2-clause license ("Simplified BSD License" or "FreeBSD License", SPDX: BSD-2-Clause). OpenSBI - source code also contains code reused from other projects desribed here: + source code also contains code reused from other projects described here: https://github.com/riscv/opensbi/blob/master/ThirdPartyNotices.md. - npcm7xx_bootrom.bin is a simplified, free (Apache 2.0) boot ROM for Nuvoton @@ -81,3 +75,9 @@ initialize and run boot images stored in SPI flash, but may grow more features over time as needed. The source code is available at: https://github.com/google/vbootrom + +- hppa-firmware.img (32-bit) and hppa-firmware64.img (64-bit) are firmware + files for the HP-PARISC (hppa) architecture. + They are built form the SeaBIOS-hppa sources, which is a fork of SeaBIOS + adapted for hppa. + SeaBIOS-hppa is available at https://github.com/hdeller/seabios-hppa diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin index 6163fb8149..48c3707d55 100644 Binary files a/pc-bios/bios-256k.bin and b/pc-bios/bios-256k.bin differ diff --git a/pc-bios/bios-microvm.bin b/pc-bios/bios-microvm.bin index 97fbd3192a..c98351e734 100644 Binary files a/pc-bios/bios-microvm.bin and b/pc-bios/bios-microvm.bin differ diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin index 68f65ff2fd..7e2d062af6 100644 Binary files a/pc-bios/bios.bin and b/pc-bios/bios.bin differ diff --git a/pc-bios/edk2-aarch64-code.fd.bz2 b/pc-bios/edk2-aarch64-code.fd.bz2 index 0262f5bd8f..e763982db4 100644 Binary files a/pc-bios/edk2-aarch64-code.fd.bz2 and b/pc-bios/edk2-aarch64-code.fd.bz2 differ diff --git a/pc-bios/edk2-arm-code.fd.bz2 b/pc-bios/edk2-arm-code.fd.bz2 index 4ca97b43ea..329646dafa 100644 Binary files a/pc-bios/edk2-arm-code.fd.bz2 and b/pc-bios/edk2-arm-code.fd.bz2 differ diff --git a/pc-bios/edk2-i386-code.fd.bz2 b/pc-bios/edk2-i386-code.fd.bz2 index 6e02c9b995..271ce659b6 100644 Binary files a/pc-bios/edk2-i386-code.fd.bz2 and b/pc-bios/edk2-i386-code.fd.bz2 differ diff --git a/pc-bios/edk2-i386-secure-code.fd.bz2 b/pc-bios/edk2-i386-secure-code.fd.bz2 index a4b1cc92bd..00335cde4b 100644 Binary files a/pc-bios/edk2-i386-secure-code.fd.bz2 and b/pc-bios/edk2-i386-secure-code.fd.bz2 differ diff --git a/pc-bios/edk2-riscv-code.fd.bz2 b/pc-bios/edk2-riscv-code.fd.bz2 new file mode 100644 index 0000000000..f3a98d6ed8 Binary files /dev/null and b/pc-bios/edk2-riscv-code.fd.bz2 differ diff --git a/pc-bios/edk2-riscv-vars.fd.bz2 b/pc-bios/edk2-riscv-vars.fd.bz2 new file mode 100644 index 0000000000..36435a1046 Binary files /dev/null and b/pc-bios/edk2-riscv-vars.fd.bz2 differ diff --git a/pc-bios/edk2-x86_64-code.fd.bz2 b/pc-bios/edk2-x86_64-code.fd.bz2 index 37bfb0dbed..a1a8c05a1b 100644 Binary files a/pc-bios/edk2-x86_64-code.fd.bz2 and b/pc-bios/edk2-x86_64-code.fd.bz2 differ diff --git a/pc-bios/edk2-x86_64-microvm.fd.bz2 b/pc-bios/edk2-x86_64-microvm.fd.bz2 index 1d65c61ded..6b7cd544a4 100644 Binary files a/pc-bios/edk2-x86_64-microvm.fd.bz2 and b/pc-bios/edk2-x86_64-microvm.fd.bz2 differ diff --git a/pc-bios/edk2-x86_64-secure-code.fd.bz2 b/pc-bios/edk2-x86_64-secure-code.fd.bz2 index 76dc6d5aad..ef40a8bfd6 100644 Binary files a/pc-bios/edk2-x86_64-secure-code.fd.bz2 and b/pc-bios/edk2-x86_64-secure-code.fd.bz2 differ diff --git a/pc-bios/hppa-firmware.img b/pc-bios/hppa-firmware.img old mode 100644 new mode 100755 index b2cbb71ee0..e065e48ded Binary files a/pc-bios/hppa-firmware.img and b/pc-bios/hppa-firmware.img differ diff --git a/pc-bios/hppa-firmware64.img b/pc-bios/hppa-firmware64.img new file mode 100755 index 0000000000..7f6d837f0d Binary files /dev/null and b/pc-bios/hppa-firmware64.img differ diff --git a/pc-bios/keymaps/meson.build b/pc-bios/keymaps/meson.build index 44247a12b5..0bd8ce0077 100644 --- a/pc-bios/keymaps/meson.build +++ b/pc-bios/keymaps/meson.build @@ -1,5 +1,5 @@ keymaps = { - 'ar': '-l ar', + 'ar': '-l ara', 'bepo': '-l fr -v dvorak', 'cz': '-l cz', 'da': '-l dk', @@ -33,37 +33,29 @@ keymaps = { 'tr': '-l tr', } -if meson.is_cross_build() or 'CONFIG_XKBCOMMON' not in config_host +if meson.is_cross_build() or not xkbcommon.found() native_qemu_keymap = find_program('qemu-keymap', required: false, disabler: true) else native_qemu_keymap = qemu_keymap endif -cp = find_program('cp') -t = [] -foreach km, args: keymaps - if native_qemu_keymap.found() +if native_qemu_keymap.found() + t = [] + foreach km, args: keymaps # generate with qemu-kvm t += custom_target(km, build_by_default: true, output: km, command: [native_qemu_keymap, '-f', '@OUTPUT@', args.split()], - install: true, + install: have_system, install_dir: qemu_datadir / 'keymaps') - else - # copy from source tree - t += custom_target(km, - build_by_default: true, - input: km, - output: km, - command: [cp, '@INPUT@', '@OUTPUT@'], - install: true, - install_dir: qemu_datadir / 'keymaps') - endif -endforeach + endforeach -if native_qemu_keymap.found() alias_target('update-keymaps', t) +else + install_data(keymaps.keys(), install_dir: qemu_datadir / 'keymaps') endif -install_data(['sl', 'sv'], install_dir: qemu_datadir / 'keymaps') +if have_system + install_data(['sl', 'sv'], install_dir: qemu_datadir / 'keymaps') +endif diff --git a/pc-bios/meson.build b/pc-bios/meson.build index 41ba1c0ec7..0760612bea 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -28,7 +28,6 @@ blobs = [ 'bios-256k.bin', 'bios-microvm.bin', 'qboot.rom', - 'sgabios.bin', 'vgabios.bin', 'vgabios-cirrus.bin', 'vgabios-stdvga.bin', @@ -58,10 +57,6 @@ blobs = [ 'efi-e1000e.rom', 'efi-vmxnet3.rom', 'qemu-nsis.bmp', - 'bamboo.dtb', - 'canyonlands.dtb', - 'petalogix-s3adsp1800.dtb', - 'petalogix-ml605.dtb', 'multiboot.bin', 'multiboot_dma.bin', 'linuxboot.bin', @@ -78,6 +73,7 @@ blobs = [ 'qemu_vga.ndrv', 'edk2-licenses.txt', 'hppa-firmware.img', + 'hppa-firmware64.img', 'opensbi-riscv32-generic-fw_dynamic.bin', 'opensbi-riscv64-generic-fw_dynamic.bin', 'npcm7xx_bootrom.bin', @@ -85,16 +81,30 @@ blobs = [ 'vof-nvram.bin', ] -ln_s = [find_program('ln', required: true), '-sf'] -foreach f : blobs - roms += custom_target(f, - build_by_default: have_system, - output: f, - input: files('meson.build'), # dummy input - install: get_option('install_blobs'), - install_dir: qemu_datadir, - command: [ ln_s, meson.project_source_root() / 'pc-bios' / f, '@OUTPUT@' ]) +dtc = find_program('dtc', required: false) +foreach f : [ + 'bamboo.dts', + 'canyonlands.dts', + 'petalogix-s3adsp1800.dts', + 'petalogix-ml605.dts', +] + out = fs.replace_suffix(f, '.dtb') + if dtc.found() + custom_target(f, + build_by_default: have_system, + input: files(f), + output: out, + install: get_option('install_blobs'), + install_dir: qemu_datadir, + command: [ dtc, '-I', 'dts', '-O', 'dtb', '-o', '@OUTPUT@', '@INPUT0@' ]) + else + blobs += out + endif endforeach +if get_option('install_blobs') + install_data(blobs, install_dir: qemu_datadir) +endif + subdir('descriptors') subdir('keymaps') diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc index d8203a5074..4af60026b4 100644 Binary files a/pc-bios/openbios-ppc and b/pc-bios/openbios-ppc differ diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 index 118b1cc1c0..41b6a607fb 100644 Binary files a/pc-bios/openbios-sparc32 and b/pc-bios/openbios-sparc32 differ diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 index 846cb4854c..902b4b3508 100644 Binary files a/pc-bios/openbios-sparc64 and b/pc-bios/openbios-sparc64 differ diff --git a/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin index dba8e8655f..60ca1165c8 100644 Binary files a/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin and b/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin differ diff --git a/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin index f223e56991..bae158d457 100644 Binary files a/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin and b/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin differ diff --git a/pc-bios/optionrom/Makefile b/pc-bios/optionrom/Makefile index f639915b4f..30d07026c7 100644 --- a/pc-bios/optionrom/Makefile +++ b/pc-bios/optionrom/Makefile @@ -8,66 +8,62 @@ all: multiboot.bin multiboot_dma.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bi CFLAGS = -O2 -g -quiet-command = $(if $(V),$1,$(if $(2),@printf " %-7s %s\n" $2 $3 && $1, @$1)) -cc-option = $(if $(shell $(CC) $1 -c -o /dev/null -xc /dev/null >/dev/null 2>&1 && echo OK), $1, $2) +NULL := +SPACE := $(NULL) # +TARGET_PREFIX := $(patsubst %/,%:$(SPACE),$(TARGET_DIR)) -override CFLAGS += -march=i486 -Wall - -# If -fcf-protection is enabled in flags or compiler defaults that will -# conflict with -march=i486 -override CFLAGS += $(call cc-option, -fcf-protection=none) +quiet-@ = $(if $(V),,@$(if $1,printf "%s\n" "$(TARGET_PREFIX)$1" && )) +quiet-command = $(call quiet-@,$2 $@)$1 # Flags for dependency generation override CPPFLAGS += -MMD -MP -MT $@ -MF $(@D)/$(*F).d -override CFLAGS += $(filter -W%, $(QEMU_CFLAGS)) -override CFLAGS += $(call cc-option, -fno-pie) +override CFLAGS += -march=i486 -Wall $(EXTRA_CFLAGS) -m16 override CFLAGS += -ffreestanding -I$(TOPSRC_DIR)/include -override CFLAGS += $(call cc-option, -fno-stack-protector) -override CFLAGS += $(call cc-option, -m16) -override CFLAGS += $(call cc-option, -Wno-array-bounds) -ifeq ($(filter -m16, $(CFLAGS)),) -# Attempt to work around compilers that lack -m16 (GCC <= 4.8, clang <= ??) -# On GCC we add -fno-toplevel-reorder to keep the order of asm blocks with -# respect to the rest of the code. clang does not have -fno-toplevel-reorder, -# but it places all asm blocks at the beginning and we're relying on it for -# the option ROM header. So just force clang not to use the integrated -# assembler, which doesn't support .code16gcc. -override CFLAGS += $(call cc-option, -fno-toplevel-reorder) -override CFLAGS += $(call cc-option, -no-integrated-as) -override CFLAGS += -m32 -include $(SRC_DIR)/code16gcc.h -endif +cc-test = $(CC) -Werror $1 -c -o /dev/null -xc /dev/null >/dev/null 2>/dev/null +cc-option = if $(call cc-test, $1); then \ + echo "$(TARGET_PREFIX)$1 detected" && echo "override CFLAGS += $1" >&3; else \ + echo "$(TARGET_PREFIX)$1 not detected" $(if $2,&& echo "override CFLAGS += $2" >&3); fi -Wa = -Wa, -override ASFLAGS += -32 -override CFLAGS += $(call cc-option, $(Wa)-32) +# If -fcf-protection is enabled in flags or compiler defaults that will +# conflict with -march=i486 +config-cc.mak: Makefile + $(quiet-@)($(call cc-option,-fcf-protection=none); \ + $(call cc-option,-fno-pie); \ + $(call cc-option,-no-pie); \ + $(call cc-option,-fno-stack-protector); \ + $(call cc-option,-Wno-array-bounds)) 3> config-cc.mak +-include config-cc.mak -override LDFLAGS = -m $(LD_I386_EMULATION) -T $(SRC_DIR)/flat.lds +override LDFLAGS = -nostdlib -Wl,--build-id=none,-T,$(SRC_DIR)/flat.lds pvh.img: pvh.o pvh_main.o %.o: %.S - $(call quiet-command,$(CC) $(CPPFLAGS) -E -o - $< | $(AS) $(ASFLAGS) -o $@,"AS","$@") + $(call quiet-command,$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<,Assembling) %.o: %.c - $(call quiet-command,$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@,"CC","$@") + $(call quiet-command,$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@,Compiling) %.img: %.o - $(call quiet-command,$(LD) $(LDFLAGS) -s -o $@ $^,"BUILD","$@") + $(call quiet-command,$(CC) $(CFLAGS) $(LDFLAGS) -s -o $@ $^,Linking) %.raw: %.img - $(call quiet-command,$(OBJCOPY) -O binary -j .text $< $@,"BUILD","$@") + $(call quiet-command,$(OBJCOPY) -O binary -j .text $< $@,Extracting raw object) %.bin: %.raw - $(call quiet-command,$(PYTHON) $(TOPSRC_DIR)/scripts/signrom.py $< $@,"SIGN","$@") + $(call quiet-command,$(PYTHON) $(TOPSRC_DIR)/scripts/signrom.py $< $@,Computing checksum into) include $(wildcard *.d) clean: rm -f *.o *.d *.raw *.img *.bin *~ +distclean: + rm -f config-cc.mak + # suppress auto-removal of intermediate files .SECONDARY: -.PHONY: all clean +.PHONY: all clean distclean diff --git a/pc-bios/optionrom/code16gcc.h b/pc-bios/optionrom/code16gcc.h deleted file mode 100644 index 9c8d25d508..0000000000 --- a/pc-bios/optionrom/code16gcc.h +++ /dev/null @@ -1,3 +0,0 @@ -asm( -".code16gcc\n" -); diff --git a/pc-bios/optionrom/optionrom.h b/pc-bios/optionrom/optionrom.h index 8d74c0ddf3..7bcdf0eeb2 100644 --- a/pc-bios/optionrom/optionrom.h +++ b/pc-bios/optionrom/optionrom.h @@ -34,8 +34,8 @@ #define FW_CFG_SETUP_SIZE 0x17 #define FW_CFG_SETUP_DATA 0x18 -#define BIOS_CFG_IOPORT_CFG 0x510 -#define BIOS_CFG_IOPORT_DATA 0x511 +#define BIOS_CFG_IOPORT_CFG 0x510 +#define BIOS_CFG_IOPORT_DATA 0x511 #define FW_CFG_DMA_CTL_ERROR 0x01 #define FW_CFG_DMA_CTL_READ 0x02 @@ -49,65 +49,65 @@ #define BIOS_CFG_DMA_ADDR_LOW 0x518 /* Break the translation block flow so -d cpu shows us values */ -#define DEBUG_HERE \ - jmp 1f; \ - 1: - +#define DEBUG_HERE \ + jmp 1f; \ + 1: + /* * Read a variable from the fw_cfg device. - * Clobbers: %edx - * Out: %eax + * Clobbers: %edx + * Out: %eax */ .macro read_fw VAR - mov $\VAR, %ax - mov $BIOS_CFG_IOPORT_CFG, %dx - outw %ax, (%dx) - mov $BIOS_CFG_IOPORT_DATA, %dx - inb (%dx), %al - shl $8, %eax - inb (%dx), %al - shl $8, %eax - inb (%dx), %al - shl $8, %eax - inb (%dx), %al - bswap %eax + mov $\VAR, %ax + mov $BIOS_CFG_IOPORT_CFG, %dx + outw %ax, (%dx) + mov $BIOS_CFG_IOPORT_DATA, %dx + inb (%dx), %al + shl $8, %eax + inb (%dx), %al + shl $8, %eax + inb (%dx), %al + shl $8, %eax + inb (%dx), %al + bswap %eax .endm /* * Read data from the fw_cfg device using DMA. - * Clobbers: %edx, %eax, ADDR, SIZE, memory[%esp-16] to memory[%esp] + * Clobbers: %edx, %eax, ADDR, SIZE, memory[%esp-16] to memory[%esp] */ .macro read_fw_dma VAR, SIZE, ADDR /* Address */ - bswapl \ADDR - pushl \ADDR + bswapl \ADDR + pushl \ADDR - /* We only support 32 bit target addresses */ - xorl %eax, %eax - pushl %eax - mov $BIOS_CFG_DMA_ADDR_HIGH, %dx - outl %eax, (%dx) + /* We only support 32 bit target addresses */ + xorl %eax, %eax + pushl %eax + mov $BIOS_CFG_DMA_ADDR_HIGH, %dx + outl %eax, (%dx) - /* Size */ - bswapl \SIZE - pushl \SIZE + /* Size */ + bswapl \SIZE + pushl \SIZE /* Control */ - movl $(\VAR << 16) | (FW_CFG_DMA_CTL_READ | FW_CFG_DMA_CTL_SELECT), %eax - bswapl %eax - pushl %eax + movl $(\VAR << 16) | (FW_CFG_DMA_CTL_READ | FW_CFG_DMA_CTL_SELECT), %eax + bswapl %eax + pushl %eax - movl %esp, %eax /* Address of the struct we generated */ - bswapl %eax - mov $BIOS_CFG_DMA_ADDR_LOW, %dx - outl %eax, (%dx) /* Initiate DMA */ + movl %esp, %eax /* Address of the struct we generated */ + bswapl %eax + mov $BIOS_CFG_DMA_ADDR_LOW, %dx + outl %eax, (%dx) /* Initiate DMA */ -1: mov (%esp), %eax /* Wait for completion */ - bswapl %eax - testl $~FW_CFG_DMA_CTL_ERROR, %eax - jnz 1b - addl $16, %esp +1: mov (%esp), %eax /* Wait for completion */ + bswapl %eax + testl $~FW_CFG_DMA_CTL_ERROR, %eax + jnz 1b + addl $16, %esp .endm @@ -115,116 +115,116 @@ * Read a blob from the fw_cfg device using DMA * Requires _ADDR, _SIZE and _DATA values for the parameter. * - * Clobbers: %eax, %edx, %es, %ecx, %edi and adresses %esp-20 to %esp + * Clobbers: %eax, %edx, %es, %ecx, %edi and adresses %esp-20 to %esp */ #ifdef USE_FW_CFG_DMA -#define read_fw_blob_dma(var) \ - read_fw var ## _SIZE; \ - mov %eax, %ecx; \ - read_fw var ## _ADDR; \ - mov %eax, %edi ;\ - read_fw_dma var ## _DATA, %ecx, %edi +#define read_fw_blob_dma(var) \ + read_fw var ## _SIZE; \ + mov %eax, %ecx; \ + read_fw var ## _ADDR; \ + mov %eax, %edi ; \ + read_fw_dma var ## _DATA, %ecx, %edi #else #define read_fw_blob_dma(var) read_fw_blob(var) #endif -#define read_fw_blob_pre(var) \ - read_fw var ## _SIZE; \ - mov %eax, %ecx; \ - mov $var ## _DATA, %ax; \ - mov $BIOS_CFG_IOPORT_CFG, %edx; \ - outw %ax, (%dx); \ - mov $BIOS_CFG_IOPORT_DATA, %dx; \ - cld +#define read_fw_blob_pre(var) \ + read_fw var ## _SIZE; \ + mov %eax, %ecx; \ + mov $var ## _DATA, %ax; \ + mov $BIOS_CFG_IOPORT_CFG, %edx; \ + outw %ax, (%dx); \ + mov $BIOS_CFG_IOPORT_DATA, %dx; \ + cld /* * Read a blob from the fw_cfg device. * Requires _ADDR, _SIZE and _DATA values for the parameter. * - * Clobbers: %eax, %edx, %es, %ecx, %edi + * Clobbers: %eax, %edx, %es, %ecx, %edi */ -#define read_fw_blob(var) \ - read_fw var ## _ADDR; \ - mov %eax, %edi; \ - read_fw_blob_pre(var); \ - /* old as(1) doesn't like this insn so emit the bytes instead: \ - rep insb (%dx), %es:(%edi); \ - */ \ - .dc.b 0xf3,0x6c +#define read_fw_blob(var) \ + read_fw var ## _ADDR; \ + mov %eax, %edi; \ + read_fw_blob_pre(var); \ + /* old as(1) doesn't like this insn so emit the bytes instead: \ + rep insb (%dx), %es:(%edi); \ + */ \ + .dc.b 0xf3,0x6c /* * Read a blob from the fw_cfg device in forced addr32 mode. * Requires _ADDR, _SIZE and _DATA values for the parameter. * - * Clobbers: %eax, %edx, %es, %ecx, %edi + * Clobbers: %eax, %edx, %es, %ecx, %edi */ -#define read_fw_blob_addr32(var) \ - read_fw var ## _ADDR; \ - mov %eax, %edi; \ - read_fw_blob_pre(var); \ - /* old as(1) doesn't like this insn so emit the bytes instead: \ - addr32 rep insb (%dx), %es:(%edi); \ - */ \ - .dc.b 0x67,0xf3,0x6c +#define read_fw_blob_addr32(var) \ + read_fw var ## _ADDR; \ + mov %eax, %edi; \ + read_fw_blob_pre(var); \ + /* old as(1) doesn't like this insn so emit the bytes instead: \ + addr32 rep insb (%dx), %es:(%edi); \ + */ \ + .dc.b 0x67,0xf3,0x6c /* * Read a blob from the fw_cfg device in forced addr32 mode, address is in %edi. * Requires _SIZE and _DATA values for the parameter. * - * Clobbers: %eax, %edx, %edi, %es, %ecx + * Clobbers: %eax, %edx, %edi, %es, %ecx */ -#define read_fw_blob_addr32_edi(var) \ - read_fw_blob_pre(var); \ - /* old as(1) doesn't like this insn so emit the bytes instead: \ - addr32 rep insb (%dx), %es:(%edi); \ - */ \ - .dc.b 0x67,0xf3,0x6c +#define read_fw_blob_addr32_edi(var) \ + read_fw_blob_pre(var); \ + /* old as(1) doesn't like this insn so emit the bytes instead: \ + addr32 rep insb (%dx), %es:(%edi); \ + */ \ + .dc.b 0x67,0xf3,0x6c -#define OPTION_ROM_START \ - .code16; \ - .text; \ - .global _start; \ - _start:; \ - .short 0xaa55; \ - .byte (_end - _start) / 512; +#define OPTION_ROM_START \ + .code16; \ + .text; \ + .global _start; \ + _start:; \ + .short 0xaa55; \ + .byte (_end - _start) / 512; -#define BOOT_ROM_START \ - OPTION_ROM_START \ - lret; \ - .org 0x18; \ - .short 0; \ - .short _pnph; \ - _pnph: \ - .ascii "$PnP"; \ - .byte 0x01; \ - .byte ( _pnph_len / 16 ); \ - .short 0x0000; \ - .byte 0x00; \ - .byte 0x00; \ - .long 0x00000000; \ - .short _manufacturer; \ - .short _product; \ - .long 0x00000000; \ - .short 0x0000; \ - .short 0x0000; \ - .short _bev; \ - .short 0x0000; \ - .short 0x0000; \ - .equ _pnph_len, . - _pnph; \ - _bev:; \ - /* DS = CS */ \ - movw %cs, %ax; \ - movw %ax, %ds; +#define BOOT_ROM_START \ + OPTION_ROM_START \ + lret; \ + .org 0x18; \ + .short 0; \ + .short _pnph; \ + _pnph: \ + .ascii "$PnP"; \ + .byte 0x01; \ + .byte ( _pnph_len / 16 ); \ + .short 0x0000; \ + .byte 0x00; \ + .byte 0x00; \ + .long 0x00000000; \ + .short _manufacturer; \ + .short _product; \ + .long 0x00000000; \ + .short 0x0000; \ + .short 0x0000; \ + .short _bev; \ + .short 0x0000; \ + .short 0x0000; \ + .equ _pnph_len, . - _pnph; \ + _bev:; \ + /* DS = CS */ \ + movw %cs, %ax; \ + movw %ax, %ds; -#define OPTION_ROM_END \ - .byte 0; \ - .align 512, 0; \ +#define OPTION_ROM_END \ + .byte 0; \ + .align 512, 0; \ _end: -#define BOOT_ROM_END \ - _manufacturer:; \ - .asciz "QEMU"; \ - _product:; \ - .asciz BOOT_ROM_PRODUCT; \ - OPTION_ROM_END +#define BOOT_ROM_END \ + _manufacturer:; \ + .asciz "QEMU"; \ + _product:; \ + .asciz BOOT_ROM_PRODUCT; \ + OPTION_ROM_END diff --git a/pc-bios/qboot.rom b/pc-bios/qboot.rom old mode 100644 new mode 100755 index 7634106a07..684000f57a Binary files a/pc-bios/qboot.rom and b/pc-bios/qboot.rom differ diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index a560c1272f..f0d9ef6d4d 100644 Binary files a/pc-bios/s390-ccw.img and b/pc-bios/s390-ccw.img differ diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index 6eb713bf37..acfcd1e71a 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -6,9 +6,14 @@ include config-host.mak CFLAGS = -O2 -g MAKEFLAGS += -rR -quiet-command = $(if $(V),$1,$(if $(2),@printf " %-7s %s\n" $2 $3 && $1, @$1)) -cc-option = $(if $(shell $(CC) $1 $2 -S -o /dev/null -xc /dev/null \ - >/dev/null 2>&1 && echo OK),$2,$3) +GIT_SUBMODULES = roms/SLOF + +NULL := +SPACE := $(NULL) # +TARGET_PREFIX := $(patsubst %/,%:$(SPACE),$(TARGET_DIR)) + +quiet-@ = $(if $(V),,@$(if $1,printf "%s\n" "$(TARGET_PREFIX)$1" && )) +quiet-command = $(call quiet-@,$2 $@)$1 VPATH_SUFFIXES = %.c %.h %.S %.m %.mak %.sh %.rc Kconfig% %.json.in set-vpath = $(if $1,$(foreach PATTERN,$(VPATH_SUFFIXES),$(eval vpath $(PATTERN) $1))) @@ -18,47 +23,66 @@ $(call set-vpath, $(SRC_PATH)) QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d %.o: %.c - $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) \ - -c -o $@ $<,"CC","$(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(EXTRA_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) \ + -c -o $@ $<,Compiling) %.o: %.S - $(call quiet-command,$(CCAS) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) \ - -c -o $@ $<,"CCAS","$(TARGET_DIR)$@") + $(call quiet-command,$(CCAS) $(EXTRA_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) \ + -c -o $@ $<,Assembling) -.PHONY : all clean build-all +.PHONY : all clean build-all distclean OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o \ virtio.o virtio-scsi.o virtio-blkdev.o libc.o cio.o dasd-ipl.o -QEMU_CFLAGS := -Wall $(filter -W%, $(QEMU_CFLAGS)) -QEMU_CFLAGS += $(call cc-option,-Werror $(QEMU_CFLAGS),-Wno-stringop-overflow) -QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -fno-common -fPIE -QEMU_CFLAGS += -fwrapv -fno-strict-aliasing -fno-asynchronous-unwind-tables -QEMU_CFLAGS += $(call cc-option, $(QEMU_CFLAGS), -fno-stack-protector) -QEMU_CFLAGS += -msoft-float -QEMU_CFLAGS += $(call cc-option, $(QEMU_CFLAGS),-march=z900,-march=z10) -QEMU_CFLAGS += -std=gnu99 +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -fno-common -fPIE +EXTRA_CFLAGS += -fwrapv -fno-strict-aliasing -fno-asynchronous-unwind-tables +EXTRA_CFLAGS += -msoft-float +EXTRA_CFLAGS += -std=gnu99 LDFLAGS += -Wl,-pie -nostdlib +cc-test = $(CC) -Werror $1 -c -o /dev/null -xc /dev/null >/dev/null 2>/dev/null +cc-option = if $(call cc-test, $1); then \ + echo "$(TARGET_PREFIX)$1 detected" && echo "EXTRA_CFLAGS += $1" >&3; else \ + echo "$(TARGET_PREFIX)$1 not detected" $(if $2,&& echo "EXTRA_CFLAGS += $2" >&3); fi + +config-cc.mak: Makefile + $(quiet-@)($(call cc-option,-Wno-stringop-overflow); \ + $(call cc-option,-fno-stack-protector); \ + $(call cc-option,-Wno-array-bounds); \ + $(call cc-option,-Wno-gnu); \ + $(call cc-option,-march=z900,-march=z10)) 3> config-cc.mak +-include config-cc.mak + +LDFLAGS += -Wl,-pie -nostdlib -z noexecstack + build-all: s390-ccw.img s390-netboot.img s390-ccw.elf: $(OBJECTS) - $(call quiet-command,$(CC) $(LDFLAGS) -o $@ $(OBJECTS),"BUILD","$(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(LDFLAGS) -o $@ $(OBJECTS),Linking) s390-ccw.img: s390-ccw.elf - $(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,"STRIP","$(TARGET_DIR)$@") + $(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,Stripping $< into) $(OBJECTS): Makefile -ifneq ($(wildcard $(SRC_PATH)/../../roms/SLOF/lib/libnet),) include $(SRC_PATH)/netboot.mak -else -s390-netboot.img: - @echo "s390-netboot.img not built since roms/SLOF/ is not available." -endif ALL_OBJS = $(sort $(OBJECTS) $(NETOBJS) $(LIBCOBJS) $(LIBNETOBJS)) -include $(ALL_OBJS:%.o=%.d) clean: rm -f *.o *.d *.img *.elf *~ *.a + +distclean: + rm -f config-cc.mak + +.PHONY: git-submodule-update +$(SRC_PATH)/../../.git-submodule-status: git-submodule-update config-host.mak +Makefile: $(SRC_PATH)/../../.git-submodule-status + +git-submodule-update: +ifneq ($(GIT_SUBMODULES_ACTION),ignore) + $(quiet-@)GIT=git "$(SRC_PATH)/../../scripts/git-submodule.sh" $(GIT_SUBMODULES_ACTION) $(GIT_SUBMODULES) +endif diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 56411ab3b6..a2137449dc 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -72,42 +72,74 @@ static inline void verify_boot_info(BootInfo *bip) "Bad block size in zIPL section of the 1st record."); } -static block_number_t eckd_block_num(EckdCHS *chs) +static void eckd_format_chs(ExtEckdBlockPtr *ptr, bool ldipl, + uint64_t *c, + uint64_t *h, + uint64_t *s) +{ + if (ldipl) { + *c = ptr->ldptr.chs.cylinder; + *h = ptr->ldptr.chs.head; + *s = ptr->ldptr.chs.sector; + } else { + *c = ptr->bptr.chs.cylinder; + *h = ptr->bptr.chs.head; + *s = ptr->bptr.chs.sector; + } +} + +static block_number_t eckd_chs_to_block(uint64_t c, uint64_t h, uint64_t s) { const uint64_t sectors = virtio_get_sectors(); const uint64_t heads = virtio_get_heads(); - const uint64_t cylinder = chs->cylinder - + ((chs->head & 0xfff0) << 12); - const uint64_t head = chs->head & 0x000f; + const uint64_t cylinder = c + ((h & 0xfff0) << 12); + const uint64_t head = h & 0x000f; const block_number_t block = sectors * heads * cylinder + sectors * head - + chs->sector - - 1; /* block nr starts with zero */ + + s - 1; /* block nr starts with zero */ return block; } -static bool eckd_valid_address(BootMapPointer *p) +static block_number_t eckd_block_num(EckdCHS *chs) { - const uint64_t head = p->eckd.chs.head & 0x000f; + return eckd_chs_to_block(chs->cylinder, chs->head, chs->sector); +} +static block_number_t gen_eckd_block_num(ExtEckdBlockPtr *ptr, bool ldipl) +{ + uint64_t cyl, head, sec; + eckd_format_chs(ptr, ldipl, &cyl, &head, &sec); + return eckd_chs_to_block(cyl, head, sec); +} + +static bool eckd_valid_chs(uint64_t cyl, uint64_t head, uint64_t sector) +{ if (head >= virtio_get_heads() - || p->eckd.chs.sector > virtio_get_sectors() - || p->eckd.chs.sector <= 0) { + || sector > virtio_get_sectors() + || sector <= 0) { return false; } if (!virtio_guessed_disk_nature() && - eckd_block_num(&p->eckd.chs) >= virtio_get_blocks()) { + eckd_chs_to_block(cyl, head, sector) >= virtio_get_blocks()) { return false; } return true; } -static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address) +static bool eckd_valid_address(ExtEckdBlockPtr *ptr, bool ldipl) +{ + uint64_t cyl, head, sec; + eckd_format_chs(ptr, ldipl, &cyl, &head, &sec); + return eckd_valid_chs(cyl, head, sec); +} + +static block_number_t load_eckd_segments(block_number_t blk, bool ldipl, + uint64_t *address) { block_number_t block_nr; - int j, rc; + int j, rc, count; BootMapPointer *bprs = (void *)_bprs; bool more_data; @@ -117,7 +149,7 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address) do { more_data = false; for (j = 0;; j++) { - block_nr = eckd_block_num(&bprs[j].xeckd.bptr.chs); + block_nr = gen_eckd_block_num(&bprs[j].xeckd, ldipl); if (is_null_block_number(block_nr)) { /* end of chunk */ break; } @@ -129,11 +161,26 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address) break; } - IPL_assert(block_size_ok(bprs[j].xeckd.bptr.size), + /* List directed pointer does not store block size */ + IPL_assert(ldipl || block_size_ok(bprs[j].xeckd.bptr.size), "bad chunk block size"); - IPL_assert(eckd_valid_address(&bprs[j]), "bad chunk ECKD addr"); - if ((bprs[j].xeckd.bptr.count == 0) && unused_space(&(bprs[j+1]), + if (!eckd_valid_address(&bprs[j].xeckd, ldipl)) { + /* + * If an invalid address is found during LD-IPL then break and + * retry as CCW + */ + IPL_assert(ldipl, "bad chunk ECKD addr"); + break; + } + + if (ldipl) { + count = bprs[j].xeckd.ldptr.count; + } else { + count = bprs[j].xeckd.bptr.count; + } + + if (count == 0 && unused_space(&bprs[j + 1], sizeof(EckdBlockPtr))) { /* This is a "continue" pointer. * This ptr should be the last one in the current @@ -149,11 +196,10 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address) /* Load (count+1) blocks of code at (block_nr) * to memory (address). */ - rc = virtio_read_many(block_nr, (void *)(*address), - bprs[j].xeckd.bptr.count+1); + rc = virtio_read_many(block_nr, (void *)(*address), count + 1); IPL_assert(rc == 0, "code chunk read failed"); - *address += (bprs[j].xeckd.bptr.count+1) * virtio_get_block_size(); + *address += (count + 1) * virtio_get_block_size(); } } while (more_data); return block_nr; @@ -237,8 +283,10 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr, uint64_t address; BootMapTable *bmt = (void *)sec; BootMapScript *bms = (void *)sec; + /* The S1B block number is NULL_BLOCK_NR if and only if it's an LD-IPL */ + bool ldipl = (s1b_block_nr == NULL_BLOCK_NR); - if (menu_is_enabled_zipl()) { + if (menu_is_enabled_zipl() && !ldipl) { loadparm = eckd_get_boot_menu_index(s1b_block_nr); } @@ -249,7 +297,7 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr, memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(bmt_block_nr, sec, "Cannot read Boot Map Table"); - block_nr = eckd_block_num(&bmt->entry[loadparm].xeckd.bptr.chs); + block_nr = gen_eckd_block_num(&bmt->entry[loadparm].xeckd, ldipl); IPL_assert(block_nr != -1, "Cannot find Boot Map Table Entry"); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); @@ -264,13 +312,18 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr, } address = bms->entry[i].address.load_address; - block_nr = eckd_block_num(&bms->entry[i].blkptr.xeckd.bptr.chs); + block_nr = gen_eckd_block_num(&bms->entry[i].blkptr.xeckd, ldipl); do { - block_nr = load_eckd_segments(block_nr, &address); + block_nr = load_eckd_segments(block_nr, ldipl, &address); } while (block_nr != -1); } + if (ldipl && bms->entry[i].type != BOOT_SCRIPT_EXEC) { + /* Abort LD-IPL and retry as CCW-IPL */ + return; + } + IPL_assert(bms->entry[i].type == BOOT_SCRIPT_EXEC, "Unknown script entry type"); write_reset_psw(bms->entry[i].address.load_address); /* no return */ @@ -380,6 +433,23 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) /* no return */ } +static block_number_t eckd_find_bmt(ExtEckdBlockPtr *ptr) +{ + block_number_t blockno; + uint8_t tmp_sec[MAX_SECTOR_SIZE]; + BootRecord *br; + + blockno = gen_eckd_block_num(ptr, 0); + read_block(blockno, tmp_sec, "Cannot read boot record"); + br = (BootRecord *)tmp_sec; + if (!magic_match(br->magic, ZIPL_MAGIC)) { + /* If the boot record is invalid, return and try CCW-IPL instead */ + return NULL_BLOCK_NR; + } + + return gen_eckd_block_num(&br->pgt.xeckd, 1); +} + static void print_eckd_msg(void) { char msg[] = "Using ECKD scheme (block size *****), "; @@ -401,28 +471,43 @@ static void print_eckd_msg(void) static void ipl_eckd(void) { - XEckdMbr *mbr = (void *)sec; - LDL_VTOC *vlbl = (void *)sec; + IplVolumeLabel *vlbl = (void *)sec; + LDL_VTOC *vtoc = (void *)sec; + block_number_t ldipl_bmt; /* Boot Map Table for List-Directed IPL */ print_eckd_msg(); - /* Grab the MBR again */ - memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(0, mbr, "Cannot read block 0 on DASD"); - - if (magic_match(mbr->magic, IPL1_MAGIC)) { - ipl_eckd_cdl(); /* only returns in case of error */ - return; - } - - /* LDL/CMS? */ + /* Block 2 can contain either the CDL VOL1 label or the LDL VTOC */ memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(2, vlbl, "Cannot read block 2"); - if (magic_match(vlbl->magic, CMS1_MAGIC)) { + /* + * First check for a list-directed-format pointer which would + * supersede the CCW pointer. + */ + if (eckd_valid_address((ExtEckdBlockPtr *)&vlbl->f.br, 0)) { + ldipl_bmt = eckd_find_bmt((ExtEckdBlockPtr *)&vlbl->f.br); + if (ldipl_bmt) { + sclp_print("List-Directed\n"); + /* LD-IPL does not use the S1B bock, just make it NULL */ + run_eckd_boot_script(ldipl_bmt, NULL_BLOCK_NR); + /* Only return in error, retry as CCW-IPL */ + sclp_print("Retrying IPL "); + print_eckd_msg(); + } + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); + read_block(2, vtoc, "Cannot read block 2"); + } + + /* Not list-directed */ + if (magic_match(vtoc->magic, VOL1_MAGIC)) { + ipl_eckd_cdl(); /* may return in error */ + } + + if (magic_match(vtoc->magic, CMS1_MAGIC)) { ipl_eckd_ldl(ECKD_CMS); /* no return */ } - if (magic_match(vlbl->magic, LNX1_MAGIC)) { + if (magic_match(vtoc->magic, LNX1_MAGIC)) { ipl_eckd_ldl(ECKD_LDL); /* no return */ } @@ -780,18 +865,37 @@ static void ipl_iso_el_torito(void) } } +/** + * Detect whether we're trying to boot from an .ISO image. + * These always have a signature string "CD001" at offset 0x8001. + */ +static bool has_iso_signature(void) +{ + int blksize = virtio_get_block_size(); + + if (!blksize || virtio_read(0x8000 / blksize, sec)) { + return false; + } + + return !memcmp("CD001", &sec[1], 5); +} + /*********************************************************************** * Bus specific IPL sequences */ static void zipl_load_vblk(void) { - if (virtio_guessed_disk_nature()) { - virtio_assume_iso9660(); - } - ipl_iso_el_torito(); + int blksize = virtio_get_block_size(); - if (virtio_guessed_disk_nature()) { + if (blksize == VIRTIO_ISO_BLOCK_SIZE || has_iso_signature()) { + if (blksize != VIRTIO_ISO_BLOCK_SIZE) { + virtio_assume_iso9660(); + } + ipl_iso_el_torito(); + } + + if (blksize != VIRTIO_DASD_DEFAULT_BLOCK_SIZE) { sclp_print("Using guessed DASD geometry.\n"); virtio_assume_eckd(); } diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index 3946aa3f8d..d4690a88c2 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -45,9 +45,23 @@ typedef struct EckdBlockPtr { * it's 0 for TablePtr, ScriptPtr, and SectionPtr */ } __attribute__ ((packed)) EckdBlockPtr; -typedef struct ExtEckdBlockPtr { +typedef struct LdEckdCHS { + uint32_t cylinder; + uint8_t head; + uint8_t sector; +} __attribute__ ((packed)) LdEckdCHS; + +typedef struct LdEckdBlockPtr { + LdEckdCHS chs; /* cylinder/head/sector is an address of the block */ + uint8_t reserved[4]; + uint16_t count; + uint32_t pad; +} __attribute__ ((packed)) LdEckdBlockPtr; + +/* bptr is used for CCW type IPL, while ldptr is for list-directed IPL */ +typedef union ExtEckdBlockPtr { EckdBlockPtr bptr; - uint8_t reserved[8]; + LdEckdBlockPtr ldptr; } __attribute__ ((packed)) ExtEckdBlockPtr; typedef union BootMapPointer { @@ -57,6 +71,15 @@ typedef union BootMapPointer { ExtEckdBlockPtr xeckd; } __attribute__ ((packed)) BootMapPointer; +typedef struct BootRecord { + uint8_t magic[4]; + uint32_t version; + uint64_t res1; + BootMapPointer pgt; + uint8_t reserved[510 - 32]; + uint16_t os_id; +} __attribute__ ((packed)) BootRecord; + /* aka Program Table */ typedef struct BootMapTable { uint8_t magic[4]; @@ -292,7 +315,8 @@ typedef struct IplVolumeLabel { struct { unsigned char key[4]; /* == "VOL1" */ unsigned char volser[6]; - unsigned char reserved[6]; + unsigned char reserved[64]; + EckdCHS br; /* Location of Boot Record for list-directed IPL */ } f; }; } __attribute__((packed)) IplVolumeLabel; diff --git a/pc-bios/s390-ccw/cio.h b/pc-bios/s390-ccw/cio.h index 1e5d4e92e1..8b18153deb 100644 --- a/pc-bios/s390-ccw/cio.h +++ b/pc-bios/s390-ccw/cio.h @@ -17,32 +17,32 @@ * path management control word */ struct pmcw { - __u32 intparm; /* interruption parameter */ - __u32 qf:1; /* qdio facility */ - __u32 w:1; - __u32 isc:3; /* interruption sublass */ - __u32 res5:3; /* reserved zeros */ - __u32 ena:1; /* enabled */ - __u32 lm:2; /* limit mode */ - __u32 mme:2; /* measurement-mode enable */ - __u32 mp:1; /* multipath mode */ - __u32 tf:1; /* timing facility */ - __u32 dnv:1; /* device number valid */ - __u32 dev:16; /* device number */ - __u8 lpm; /* logical path mask */ - __u8 pnom; /* path not operational mask */ - __u8 lpum; /* last path used mask */ - __u8 pim; /* path installed mask */ - __u16 mbi; /* measurement-block index */ - __u8 pom; /* path operational mask */ - __u8 pam; /* path available mask */ - __u8 chpid[8]; /* CHPID 0-7 (if available) */ - __u32 unused1:8; /* reserved zeros */ - __u32 st:3; /* subchannel type */ - __u32 unused2:18; /* reserved zeros */ - __u32 mbfc:1; /* measurement block format control */ - __u32 xmwme:1; /* extended measurement word mode enable */ - __u32 csense:1; /* concurrent sense; can be enabled ...*/ + u32 intparm; /* interruption parameter */ + u32 qf:1; /* qdio facility */ + u32 w:1; + u32 isc:3; /* interruption subclass */ + u32 res5:3; /* reserved zeros */ + u32 ena:1; /* enabled */ + u32 lm:2; /* limit mode */ + u32 mme:2; /* measurement-mode enable */ + u32 mp:1; /* multipath mode */ + u32 tf:1; /* timing facility */ + u32 dnv:1; /* device number valid */ + u32 dev:16; /* device number */ + u8 lpm; /* logical path mask */ + u8 pnom; /* path not operational mask */ + u8 lpum; /* last path used mask */ + u8 pim; /* path installed mask */ + u16 mbi; /* measurement-block index */ + u8 pom; /* path operational mask */ + u8 pam; /* path available mask */ + u8 chpid[8]; /* CHPID 0-7 (if available) */ + u32 unused1:8; /* reserved zeros */ + u32 st:3; /* subchannel type */ + u32 unused2:18; /* reserved zeros */ + u32 mbfc:1; /* measurement block format control */ + u32 xmwme:1; /* extended measurement word mode enable */ + u32 csense:1; /* concurrent sense; can be enabled ...*/ /* ... per MSCH, however, if facility */ /* ... is not installed, this results */ /* ... in an operand exception. */ @@ -50,24 +50,24 @@ struct pmcw { /* Target SCHIB configuration. */ struct schib_config { - __u64 mba; - __u32 intparm; - __u16 mbi; - __u32 isc:3; - __u32 ena:1; - __u32 mme:2; - __u32 mp:1; - __u32 csense:1; - __u32 mbfc:1; + u64 mba; + u32 intparm; + u16 mbi; + u32 isc:3; + u32 ena:1; + u32 mme:2; + u32 mp:1; + u32 csense:1; + u32 mbfc:1; } __attribute__ ((packed)); struct scsw { - __u16 flags; - __u16 ctrl; - __u32 cpa; - __u8 dstat; - __u8 cstat; - __u16 count; + u16 flags; + u16 ctrl; + u32 cpa; + u8 dstat; + u8 cstat; + u16 count; } __attribute__ ((packed)); /* Function Control */ @@ -117,42 +117,42 @@ struct scsw { typedef struct schib { struct pmcw pmcw; /* path management control word */ struct scsw scsw; /* subchannel status word */ - __u64 mba; /* measurement block address */ - __u8 mda[4]; /* model dependent area */ + u64 mba; /* measurement block address */ + u8 mda[4]; /* model dependent area */ } __attribute__ ((packed, aligned(4))) Schib; typedef struct subchannel_id { union { struct { - __u16 cssid:8; - __u16 reserved:4; - __u16 m:1; - __u16 ssid:2; - __u16 one:1; + u16 cssid:8; + u16 reserved:4; + u16 m:1; + u16 ssid:2; + u16 one:1; }; - __u16 sch_id; + u16 sch_id; }; - __u16 sch_no; + u16 sch_no; } __attribute__ ((packed, aligned(4))) SubChannelId; struct chsc_header { - __u16 length; - __u16 code; + u16 length; + u16 code; } __attribute__((packed)); typedef struct chsc_area_sda { struct chsc_header request; - __u8 reserved1:4; - __u8 format:4; - __u8 reserved2; - __u16 operation_code; - __u32 reserved3; - __u32 reserved4; - __u32 operation_data_area[252]; + u8 reserved1:4; + u8 format:4; + u8 reserved2; + u16 operation_code; + u32 reserved3; + u32 reserved4; + u32 operation_data_area[252]; struct chsc_header response; - __u32 reserved5:4; - __u32 format2:4; - __u32 reserved6:24; + u32 reserved5:4; + u32 format2:4; + u32 reserved6:24; } __attribute__((packed)) ChscAreaSda; /* @@ -160,37 +160,37 @@ typedef struct chsc_area_sda { */ struct tpi_info { struct subchannel_id schid; - __u32 intparm; /* interruption parameter */ - __u32 adapter_IO:1; - __u32 reserved2:1; - __u32 isc:3; - __u32 reserved3:12; - __u32 int_type:3; - __u32 reserved4:12; + u32 intparm; /* interruption parameter */ + u32 adapter_IO:1; + u32 reserved2:1; + u32 isc:3; + u32 reserved3:12; + u32 int_type:3; + u32 reserved4:12; } __attribute__ ((packed, aligned(4))); /* channel command word (format 0) */ typedef struct ccw0 { - __u8 cmd_code; - __u32 cda:24; - __u32 chainData:1; - __u32 chain:1; - __u32 sli:1; - __u32 skip:1; - __u32 pci:1; - __u32 ida:1; - __u32 suspend:1; - __u32 mida:1; - __u8 reserved; - __u16 count; + u8 cmd_code; + u32 cda:24; + u32 chainData:1; + u32 chain:1; + u32 sli:1; + u32 skip:1; + u32 pci:1; + u32 ida:1; + u32 suspend:1; + u32 mida:1; + u8 reserved; + u16 count; } __attribute__ ((packed, aligned(8))) Ccw0; /* channel command word (format 1) */ typedef struct ccw1 { - __u8 cmd_code; - __u8 flags; - __u16 count; - __u32 cda; + u8 cmd_code; + u8 flags; + u16 count; + u32 cda; } __attribute__ ((packed, aligned(8))) Ccw1; /* do_cio() CCW formats */ @@ -234,31 +234,31 @@ typedef struct ccw1 { * Command-mode operation request block */ typedef struct cmd_orb { - __u32 intparm; /* interruption parameter */ - __u32 key:4; /* flags, like key, suspend control, etc. */ - __u32 spnd:1; /* suspend control */ - __u32 res1:1; /* reserved */ - __u32 mod:1; /* modification control */ - __u32 sync:1; /* synchronize control */ - __u32 fmt:1; /* format control */ - __u32 pfch:1; /* prefetch control */ - __u32 isic:1; /* initial-status-interruption control */ - __u32 alcc:1; /* address-limit-checking control */ - __u32 ssic:1; /* suppress-suspended-interr. control */ - __u32 res2:1; /* reserved */ - __u32 c64:1; /* IDAW/QDIO 64 bit control */ - __u32 i2k:1; /* IDAW 2/4kB block size control */ - __u32 lpm:8; /* logical path mask */ - __u32 ils:1; /* incorrect length */ - __u32 zero:6; /* reserved zeros */ - __u32 orbx:1; /* ORB extension control */ - __u32 cpa; /* channel program address */ + u32 intparm; /* interruption parameter */ + u32 key:4; /* flags, like key, suspend control, etc. */ + u32 spnd:1; /* suspend control */ + u32 res1:1; /* reserved */ + u32 mod:1; /* modification control */ + u32 sync:1; /* synchronize control */ + u32 fmt:1; /* format control */ + u32 pfch:1; /* prefetch control */ + u32 isic:1; /* initial-status-interruption control */ + u32 alcc:1; /* address-limit-checking control */ + u32 ssic:1; /* suppress-suspended-interr. control */ + u32 res2:1; /* reserved */ + u32 c64:1; /* IDAW/QDIO 64 bit control */ + u32 i2k:1; /* IDAW 2/4kB block size control */ + u32 lpm:8; /* logical path mask */ + u32 ils:1; /* incorrect length */ + u32 zero:6; /* reserved zeros */ + u32 orbx:1; /* ORB extension control */ + u32 cpa; /* channel program address */ } __attribute__ ((packed, aligned(4))) CmdOrb; struct ciw { - __u8 type; - __u8 command; - __u16 count; + u8 type; + u8 command; + u16 count; }; #define CU_TYPE_UNKNOWN 0x0000 @@ -271,12 +271,12 @@ struct ciw { */ typedef struct senseid { /* common part */ - __u8 reserved; /* always 0x'FF' */ - __u16 cu_type; /* control unit type */ - __u8 cu_model; /* control unit model */ - __u16 dev_type; /* device type */ - __u8 dev_model; /* device model */ - __u8 unused; /* padding byte */ + u8 reserved; /* always 0x'FF' */ + u16 cu_type; /* control unit type */ + u8 cu_model; /* control unit model */ + u16 dev_type; /* device type */ + u8 dev_model; /* device model */ + u8 unused; /* padding byte */ /* extended part */ struct ciw ciw[62]; } __attribute__ ((packed, aligned(4))) SenseId; @@ -342,9 +342,9 @@ typedef struct SenseDataEckdDasd { /* interruption response block */ typedef struct irb { struct scsw scsw; - __u32 esw[5]; - __u32 ecw[8]; - __u32 emw[8]; + u32 esw[5]; + u32 ecw[8]; + u32 emw[8]; } __attribute__ ((packed, aligned(4))) Irb; /* Used for SEEK ccw commands */ diff --git a/pc-bios/s390-ccw/helper.h b/pc-bios/s390-ccw/helper.h index 3d0731c4c6..8e3dfcb6d6 100644 --- a/pc-bios/s390-ccw/helper.h +++ b/pc-bios/s390-ccw/helper.h @@ -38,7 +38,7 @@ static inline void yield(void) static inline void sleep(unsigned int seconds) { - ulong target = get_time_seconds() + seconds; + unsigned long target = get_time_seconds() + seconds; while (get_time_seconds() < target) { yield(); diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h index 772d5c57c9..cb6ac8a880 100644 --- a/pc-bios/s390-ccw/iplb.h +++ b/pc-bios/s390-ccw/iplb.h @@ -81,7 +81,7 @@ extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); #define QIPL_FLAG_BM_OPTS_ZIPL 0x40 /* - * This definition must be kept in sync with the defininition + * This definition must be kept in sync with the definition * in hw/s390x/ipl.h */ struct QemuIplParameters { diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 5d2b7ba94d..5506798098 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -14,9 +14,9 @@ #include "s390-ccw.h" #include "cio.h" #include "virtio.h" +#include "virtio-scsi.h" #include "dasd-ipl.h" -char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); static SubChannelId blk_schid = { .one = 1 }; static char loadparm_str[LOADPARM_LEN + 1]; QemuIplParameters qipl; @@ -218,6 +218,7 @@ static int virtio_setup(void) { VDev *vdev = virtio_get_device(); QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS; + int ret; memcpy(&qipl, early_qipl, sizeof(QemuIplParameters)); @@ -225,18 +226,26 @@ static int virtio_setup(void) menu_setup(); } - if (virtio_get_device_type() == VIRTIO_ID_NET) { + switch (vdev->senseid.cu_model) { + case VIRTIO_ID_NET: sclp_print("Network boot device detected\n"); vdev->netboot_start_addr = qipl.netboot_start_addr; - } else { - int ret = virtio_blk_setup_device(blk_schid); - if (ret) { - return ret; - } + return 0; + case VIRTIO_ID_BLOCK: + ret = virtio_blk_setup_device(blk_schid); + break; + case VIRTIO_ID_SCSI: + ret = virtio_scsi_setup_device(blk_schid); + break; + default: + panic("\n! No IPL device available !\n"); + } + + if (!ret) { IPL_assert(virtio_ipl_disk_is_valid(), "No valid IPL device detected"); } - return 0; + return ret; } static void ipl_boot_device(void) @@ -281,7 +290,7 @@ static void probe_boot_device(void) sclp_print("Could not find a suitable boot device (none specified)\n"); } -int main(void) +void main(void) { sclp_setup(); css_setup(); @@ -294,5 +303,4 @@ int main(void) } panic("Failed to load OS from hard disk\n"); - return 0; /* make compiler happy */ } diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak index 1a06befa4b..046aa35587 100644 --- a/pc-bios/s390-ccw/netboot.mak +++ b/pc-bios/s390-ccw/netboot.mak @@ -8,55 +8,55 @@ LIBNET_INC := -I$(SLOF_DIR)/lib/libnet NETLDFLAGS := $(LDFLAGS) -Wl,-Ttext=0x7800000 -$(NETOBJS): QEMU_CFLAGS += $(LIBC_INC) $(LIBNET_INC) +$(NETOBJS): EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC) s390-netboot.elf: $(NETOBJS) libnet.a libc.a - $(call quiet-command,$(CC) $(NETLDFLAGS) -o $@ $^,"BUILD","$(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(NETLDFLAGS) -o $@ $^,Linking) s390-netboot.img: s390-netboot.elf - $(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,"STRIP","$(TARGET_DIR)$@") + $(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,Stripping $< into) # libc files: -LIBC_CFLAGS = $(QEMU_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \ +LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \ -MMD -MP -MT $@ -MF $(@:%.o=%.d) CTYPE_OBJS = isdigit.o isxdigit.o toupper.o %.o : $(SLOF_DIR)/lib/libc/ctype/%.c - $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \ strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \ memset.o memcpy.o memmove.o memcmp.o %.o : $(SLOF_DIR)/lib/libc/string/%.c - $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o %.o : $(SLOF_DIR)/lib/libc/stdlib/%.c - $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \ printf.o putc.o puts.o putchar.o stdchnls.o fileno.o %.o : $(SLOF_DIR)/lib/libc/stdio/%.c - $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) sbrk.o: $(SLOF_DIR)/slof/sbrk.c - $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o libc.a: $(LIBCOBJS) - $(call quiet-command,$(AR) -rc $@ $^,"AR","$(TARGET_DIR)$@") + $(call quiet-command,$(AR) -rc $@ $^,Creating static library) # libnet files: LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \ dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o -LIBNETCFLAGS = $(QEMU_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \ +LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \ -DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d) %.o : $(SLOF_DIR)/lib/libnet/%.c - $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,Compiling) libnet.a: $(LIBNETOBJS) - $(call quiet-command,$(AR) -rc $@ $^,"AR","$(TARGET_DIR)$@") + $(call quiet-command,$(AR) -rc $@ $^,Creating static library) diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index 056e93a818..5cd619b2d6 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -50,7 +50,6 @@ void write_iplb_location(void) {} /* STSI 3.2.2 offset of first vmdb + offset of uuid inside vmdb */ #define STSI322_VMDB_UUID_OFFSET ((8 + 12) * 4) -char stack[PAGE_SIZE * 8] __attribute__((aligned(PAGE_SIZE))); IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE))); static char cfgbuf[2048]; diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 79db69ff54..c977a52b50 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -17,11 +17,6 @@ typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; -typedef unsigned long ulong; -typedef unsigned char __u8; -typedef unsigned short __u16; -typedef unsigned int __u32; -typedef unsigned long long __u64; #define true 1 #define false 0 @@ -55,8 +50,8 @@ void consume_io_int(void); /* main.c */ 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); +void main(void); /* sclp.c */ void sclp_print(const char *string); @@ -66,11 +61,11 @@ void sclp_get_loadparm_ascii(char *loadparm); int sclp_read(char *str, size_t count); /* virtio.c */ -unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, - ulong subchan_id, void *load_addr); +unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2, + unsigned long subchan_id, void *load_addr); bool virtio_is_supported(SubChannelId schid); int virtio_blk_setup_device(SubChannelId schid); -int virtio_read(ulong sector, void *load_addr); +int virtio_read(unsigned long sector, void *load_addr); /* bootmap.c */ void zipl_load(void); diff --git a/pc-bios/s390-ccw/start.S b/pc-bios/s390-ccw/start.S index 4d5ad21653..061b06591c 100644 --- a/pc-bios/s390-ccw/start.S +++ b/pc-bios/s390-ccw/start.S @@ -10,49 +10,52 @@ * directory. */ - .globl _start +#define STACK_SIZE 0x8000 +#define STACK_FRAME_SIZE 160 + + .globl _start _start: - larl %r15, stack + 0x8000 /* Set up stack */ + larl %r15,stack + STACK_SIZE - STACK_FRAME_SIZE /* Set up stack */ - /* clear bss */ - larl %r2, __bss_start - larl %r3, _end - slgr %r3, %r2 /* get sizeof bss */ - ltgr %r3,%r3 /* bss emtpy? */ - jz done - aghi %r3,-1 - srlg %r4,%r3,8 /* how many 256 byte chunks? */ - ltgr %r4,%r4 - lgr %r1,%r2 - jz remainder + /* clear bss */ + larl %r2,bss_start_literal /* __bss_start might be unaligned ... */ + lg %r2,0(%r2) /* ... so load it indirectly */ + larl %r3,_end + slgr %r3,%r2 /* get sizeof bss */ + ltgr %r3,%r3 /* bss empty? */ + jz done + aghi %r3,-1 + srlg %r4,%r3,8 /* how many 256 byte chunks? */ + ltgr %r4,%r4 + lgr %r1,%r2 + jz remainder loop: - xc 0(256,%r1),0(%r1) - la %r1,256(%r1) - brctg %r4,loop + xc 0(256,%r1),0(%r1) + la %r1,256(%r1) + brctg %r4,loop remainder: - larl %r2,memsetxc - ex %r3,0(%r2) + larl %r2,memsetxc + ex %r3,0(%r2) done: - /* set up a pgm exception disabled wait psw */ - larl %r2, disabled_wait_psw - mvc 0x01d0(16), 0(%r2) - j main /* And call C */ + /* set up a pgm exception disabled wait psw */ + larl %r2,disabled_wait_psw + mvc 0x01d0(16),0(%r2) + j main /* And call C */ memsetxc: - xc 0(1,%r1),0(%r1) - + xc 0(1,%r1),0(%r1) /* * void disabled_wait(void) * * stops the current guest cpu. */ - .globl disabled_wait + .globl disabled_wait disabled_wait: - larl %r1,disabled_wait_psw - lpswe 0(%r1) -1: j 1b + larl %r1,disabled_wait_psw + lpswe 0(%r1) +1: j 1b /* @@ -60,61 +63,69 @@ disabled_wait: * * eats one sclp interrupt */ - .globl consume_sclp_int + .globl consume_sclp_int consume_sclp_int: - /* enable service interrupts in cr0 */ - stctg %c0,%c0,0(%r15) - oi 6(%r15),0x2 - lctlg %c0,%c0,0(%r15) - /* prepare external call handler */ - larl %r1, external_new_code - stg %r1, 0x1b8 - larl %r1, external_new_mask - mvc 0x1b0(8),0(%r1) - /* load enabled wait PSW */ - larl %r1, enabled_wait_psw - lpswe 0(%r1) + /* enable service interrupts in cr0 */ + stctg %c0,%c0,0(%r15) + oi 6(%r15),0x2 + lctlg %c0,%c0,0(%r15) + /* prepare external call handler */ + larl %r1,external_new_code + stg %r1,0x1b8 + larl %r1,external_new_mask + mvc 0x1b0(8),0(%r1) + /* load enabled wait PSW */ + larl %r1,enabled_wait_psw + lpswe 0(%r1) /* * void consume_io_int(void) * * eats one I/O interrupt */ - .globl consume_io_int + .globl consume_io_int consume_io_int: - /* enable I/O interrupts in cr6 */ - stctg %c6,%c6,0(%r15) - oi 4(%r15), 0xff - lctlg %c6,%c6,0(%r15) - /* prepare i/o call handler */ - larl %r1, io_new_code - stg %r1, 0x1f8 - larl %r1, io_new_mask - mvc 0x1f0(8),0(%r1) - /* load enabled wait PSW */ - larl %r1, enabled_wait_psw - lpswe 0(%r1) + /* enable I/O interrupts in cr6 */ + stctg %c6,%c6,0(%r15) + oi 4(%r15), 0xff + lctlg %c6,%c6,0(%r15) + /* prepare i/o call handler */ + larl %r1,io_new_code + stg %r1,0x1f8 + larl %r1,io_new_mask + mvc 0x1f0(8),0(%r1) + /* load enabled wait PSW */ + larl %r1,enabled_wait_psw + lpswe 0(%r1) external_new_code: - /* disable service interrupts in cr0 */ - stctg %c0,%c0,0(%r15) - ni 6(%r15),0xfd - lctlg %c0,%c0,0(%r15) - br %r14 + /* disable service interrupts in cr0 */ + stctg %c0,%c0,0(%r15) + ni 6(%r15),0xfd + lctlg %c0,%c0,0(%r15) + br %r14 io_new_code: - /* disable I/O interrupts in cr6 */ - stctg %c6,%c6,0(%r15) - ni 4(%r15), 0x00 - lctlg %c6,%c6,0(%r15) - br %r14 + /* disable I/O interrupts in cr6 */ + stctg %c6,%c6,0(%r15) + ni 4(%r15),0x00 + lctlg %c6,%c6,0(%r15) + br %r14 - .align 8 + .align 8 +bss_start_literal: + .quad __bss_start disabled_wait_psw: - .quad 0x0002000180000000,0x0000000000000000 + .quad 0x0002000180000000,0x0000000000000000 enabled_wait_psw: - .quad 0x0302000180000000,0x0000000000000000 + .quad 0x0302000180000000,0x0000000000000000 external_new_mask: - .quad 0x0000000180000000 + .quad 0x0000000180000000 io_new_mask: - .quad 0x0000000180000000 + .quad 0x0000000180000000 + +.bss + .align 8 +stack: + .space STACK_SIZE + .size stack,STACK_SIZE diff --git a/pc-bios/s390-ccw/virtio-blkdev.c b/pc-bios/s390-ccw/virtio-blkdev.c index 7d35050292..a81207b52e 100644 --- a/pc-bios/s390-ccw/virtio-blkdev.c +++ b/pc-bios/s390-ccw/virtio-blkdev.c @@ -13,7 +13,10 @@ #include "virtio.h" #include "virtio-scsi.h" -static int virtio_blk_read_many(VDev *vdev, ulong sector, void *load_addr, +#define VIRTIO_BLK_F_GEOMETRY (1 << 4) +#define VIRTIO_BLK_F_BLK_SIZE (1 << 6) + +static int virtio_blk_read_many(VDev *vdev, unsigned long sector, void *load_addr, int sec_num) { VirtioBlkOuthdr out_hdr; @@ -46,7 +49,7 @@ static int virtio_blk_read_many(VDev *vdev, ulong sector, void *load_addr, return status; } -int virtio_read_many(ulong sector, void *load_addr, int sec_num) +int virtio_read_many(unsigned long sector, void *load_addr, int sec_num) { VDev *vdev = virtio_get_device(); @@ -60,14 +63,14 @@ int virtio_read_many(ulong sector, void *load_addr, int sec_num) return -1; } -unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, - ulong subchan_id, void *load_addr) +unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2, + unsigned long subchan_id, void *load_addr) { u8 status; int sec = rec_list1; int sec_num = ((rec_list2 >> 32) & 0xffff) + 1; int sec_len = rec_list2 >> 48; - ulong addr = (ulong)load_addr; + unsigned long addr = (unsigned long)load_addr; if (sec_len != virtio_get_block_size()) { return -1; @@ -83,7 +86,7 @@ unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, return addr; } -int virtio_read(ulong sector, void *load_addr) +int virtio_read(unsigned long sector, void *load_addr) { return virtio_read_many(sector, load_addr, 1); } @@ -112,23 +115,6 @@ VirtioGDN virtio_guessed_disk_nature(void) return virtio_get_device()->guessed_disk_nature; } -void virtio_assume_scsi(void) -{ - VDev *vdev = virtio_get_device(); - - switch (vdev->senseid.cu_model) { - case VIRTIO_ID_BLOCK: - vdev->guessed_disk_nature = VIRTIO_GDN_SCSI; - vdev->config.blk.blk_size = VIRTIO_SCSI_BLOCK_SIZE; - vdev->config.blk.physical_block_exp = 0; - vdev->blk_factor = 1; - break; - case VIRTIO_ID_SCSI: - vdev->scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE; - break; - } -} - void virtio_assume_iso9660(void) { VDev *vdev = virtio_get_device(); @@ -155,7 +141,7 @@ void virtio_assume_eckd(void) vdev->config.blk.physical_block_exp = 0; switch (vdev->senseid.cu_model) { case VIRTIO_ID_BLOCK: - vdev->config.blk.blk_size = 4096; + vdev->config.blk.blk_size = VIRTIO_DASD_DEFAULT_BLOCK_SIZE; break; case VIRTIO_ID_SCSI: vdev->config.blk.blk_size = vdev->scsi_block_size; @@ -166,46 +152,19 @@ void virtio_assume_eckd(void) virtio_eckd_sectors_for_block_size(vdev->config.blk.blk_size); } -bool virtio_disk_is_scsi(void) -{ - VDev *vdev = virtio_get_device(); - - if (vdev->guessed_disk_nature == VIRTIO_GDN_SCSI) { - return true; - } - switch (vdev->senseid.cu_model) { - case VIRTIO_ID_BLOCK: - return (vdev->config.blk.geometry.heads == 255) - && (vdev->config.blk.geometry.sectors == 63) - && (virtio_get_block_size() == VIRTIO_SCSI_BLOCK_SIZE); - case VIRTIO_ID_SCSI: - return true; - } - return false; -} - -bool virtio_disk_is_eckd(void) -{ - VDev *vdev = virtio_get_device(); - const int block_size = virtio_get_block_size(); - - if (vdev->guessed_disk_nature == VIRTIO_GDN_DASD) { - return true; - } - switch (vdev->senseid.cu_model) { - case VIRTIO_ID_BLOCK: - return (vdev->config.blk.geometry.heads == 15) - && (vdev->config.blk.geometry.sectors == - virtio_eckd_sectors_for_block_size(block_size)); - case VIRTIO_ID_SCSI: - return false; - } - return false; -} - bool virtio_ipl_disk_is_valid(void) { - return virtio_disk_is_scsi() || virtio_disk_is_eckd(); + int blksize = virtio_get_block_size(); + VDev *vdev = virtio_get_device(); + + if (vdev->guessed_disk_nature == VIRTIO_GDN_SCSI || + vdev->guessed_disk_nature == VIRTIO_GDN_DASD) { + return true; + } + + return (vdev->senseid.cu_model == VIRTIO_ID_BLOCK || + vdev->senseid.cu_model == VIRTIO_ID_SCSI) && + blksize >= 512 && blksize <= 4096; } int virtio_get_block_size(void) @@ -214,7 +173,7 @@ int virtio_get_block_size(void) switch (vdev->senseid.cu_model) { case VIRTIO_ID_BLOCK: - return vdev->config.blk.blk_size << vdev->config.blk.physical_block_exp; + return vdev->config.blk.blk_size; case VIRTIO_ID_SCSI: return vdev->scsi_block_size; } @@ -266,34 +225,12 @@ uint64_t virtio_get_blocks(void) int virtio_blk_setup_device(SubChannelId schid) { VDev *vdev = virtio_get_device(); - int ret = 0; + vdev->guest_features[0] = VIRTIO_BLK_F_GEOMETRY | VIRTIO_BLK_F_BLK_SIZE; vdev->schid = schid; virtio_setup_ccw(vdev); - switch (vdev->senseid.cu_model) { - case VIRTIO_ID_BLOCK: - sclp_print("Using virtio-blk.\n"); - if (!virtio_ipl_disk_is_valid()) { - /* make sure all getters but blocksize return 0 for - * invalid IPL disk - */ - memset(&vdev->config.blk, 0, sizeof(vdev->config.blk)); - virtio_assume_scsi(); - } - break; - case VIRTIO_ID_SCSI: - IPL_assert(vdev->config.scsi.sense_size == VIRTIO_SCSI_SENSE_SIZE, - "Config: sense size mismatch"); - IPL_assert(vdev->config.scsi.cdb_size == VIRTIO_SCSI_CDB_SIZE, - "Config: CDB size mismatch"); + sclp_print("Using virtio-blk.\n"); - sclp_print("Using virtio-scsi.\n"); - ret = virtio_scsi_setup(vdev); - break; - default: - panic("\n! No IPL device available !\n"); - } - - return ret; + return 0; } diff --git a/pc-bios/s390-ccw/virtio-scsi.c b/pc-bios/s390-ccw/virtio-scsi.c index 2c8d0f3097..d1a84b937c 100644 --- a/pc-bios/s390-ccw/virtio-scsi.c +++ b/pc-bios/s390-ccw/virtio-scsi.c @@ -150,7 +150,7 @@ static bool scsi_report_luns(VDev *vdev, void *data, uint32_t data_size) } static bool scsi_read_10(VDev *vdev, - ulong sector, int sectors, void *data, + unsigned long sector, int sectors, void *data, unsigned int data_size) { ScsiCdbRead10 cdb = { @@ -195,7 +195,7 @@ static bool scsi_read_capacity(VDev *vdev, /* virtio-scsi routines */ /* - * Tries to locate a SCSI device and and adds the information for the found + * Tries to locate a SCSI device and adds the information for the found * device to the vdev->scsi_device structure. * Returns 0 if SCSI device could be located, or a error code < 0 otherwise */ @@ -269,7 +269,7 @@ static int virtio_scsi_locate_device(VDev *vdev) } int virtio_scsi_read_many(VDev *vdev, - ulong sector, void *load_addr, int sec_num) + unsigned long sector, void *load_addr, int sec_num) { int sector_count; int f = vdev->blk_factor; @@ -329,7 +329,7 @@ static void scsi_parse_capacity_report(void *data, } } -int virtio_scsi_setup(VDev *vdev) +static int virtio_scsi_setup(VDev *vdev) { int retry_test_unit_ready = 3; uint8_t data[256]; @@ -430,3 +430,20 @@ int virtio_scsi_setup(VDev *vdev) return 0; } + +int virtio_scsi_setup_device(SubChannelId schid) +{ + VDev *vdev = virtio_get_device(); + + vdev->schid = schid; + virtio_setup_ccw(vdev); + + IPL_assert(vdev->config.scsi.sense_size == VIRTIO_SCSI_SENSE_SIZE, + "Config: sense size mismatch"); + IPL_assert(vdev->config.scsi.cdb_size == VIRTIO_SCSI_CDB_SIZE, + "Config: CDB size mismatch"); + + sclp_print("Using virtio-scsi.\n"); + + return virtio_scsi_setup(vdev); +} diff --git a/pc-bios/s390-ccw/virtio-scsi.h b/pc-bios/s390-ccw/virtio-scsi.h index 4b14c2c2f9..c5612e16a2 100644 --- a/pc-bios/s390-ccw/virtio-scsi.h +++ b/pc-bios/s390-ccw/virtio-scsi.h @@ -67,8 +67,8 @@ static inline bool virtio_scsi_response_ok(const VirtioScsiCmdResp *r) return r->response == VIRTIO_SCSI_S_OK && r->status == CDB_STATUS_GOOD; } -int virtio_scsi_setup(VDev *vdev); int virtio_scsi_read_many(VDev *vdev, - ulong sector, void *load_addr, int sec_num); + unsigned long sector, void *load_addr, int sec_num); +int virtio_scsi_setup_device(SubChannelId schid); #endif /* VIRTIO_SCSI_H */ diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index 5d2c6e3381..5edd058d88 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -48,10 +48,10 @@ VirtioDevType virtio_get_device_type(void) static long kvm_hypercall(unsigned long nr, unsigned long param1, unsigned long param2, unsigned long param3) { - register ulong r_nr asm("1") = nr; - register ulong r_param1 asm("2") = param1; - register ulong r_param2 asm("3") = param2; - register ulong r_param3 asm("4") = param3; + register unsigned long r_nr asm("1") = nr; + register unsigned long r_param1 asm("2") = param1; + register unsigned long r_param2 asm("3") = param2; + register unsigned long r_param3 asm("4") = param3; register long retval asm("2"); asm volatile ("diag %%r2,%%r4,0x500" @@ -145,7 +145,7 @@ void vring_send_buf(VRing *vr, void *p, int len, int flags) vr->avail->ring[vr->avail->idx % vr->num] = vr->next_idx; } - vr->desc[vr->next_idx].addr = (ulong)p; + vr->desc[vr->next_idx].addr = (unsigned long)p; vr->desc[vr->next_idx].len = len; vr->desc[vr->next_idx].flags = flags & ~VRING_HIDDEN_IS_CHAIN; vr->desc[vr->next_idx].next = vr->next_idx; @@ -182,7 +182,7 @@ int vr_poll(VRing *vr) */ int vring_wait_reply(void) { - ulong target_second = get_time_seconds() + vdev.wait_reply_timeout; + unsigned long target_second = get_time_seconds() + vdev.wait_reply_timeout; /* Wait for any queue to be updated by the host */ do { @@ -220,7 +220,7 @@ int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd) void virtio_setup_ccw(VDev *vdev) { int i, rc, cfg_size = 0; - unsigned char status = VIRTIO_CONFIG_S_DRIVER_OK; + uint8_t status; struct VirtioFeatureDesc { uint32_t features; uint8_t index; @@ -234,6 +234,10 @@ void virtio_setup_ccw(VDev *vdev) run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false); + status = VIRTIO_CONFIG_S_ACKNOWLEDGE; + rc = run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false); + IPL_assert(rc == 0, "Could not write ACKNOWLEDGE status to host"); + switch (vdev->senseid.cu_model) { case VIRTIO_ID_NET: vdev->nr_vqs = 2; @@ -253,9 +257,10 @@ void virtio_setup_ccw(VDev *vdev) default: panic("Unsupported virtio device\n"); } - IPL_assert( - run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false) == 0, - "Could not get block device configuration"); + + status |= VIRTIO_CONFIG_S_DRIVER; + rc = run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false); + IPL_assert(rc == 0, "Could not write DRIVER status to host"); /* Feature negotiation */ for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) { @@ -269,6 +274,9 @@ void virtio_setup_ccw(VDev *vdev) IPL_assert(rc == 0, "Could not set features bits"); } + rc = run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false); + IPL_assert(rc == 0, "Could not get virtio device configuration"); + for (i = 0; i < vdev->nr_vqs; i++) { VqInfo info = { .queue = (unsigned long long) ring_area + (i * VIRTIO_RING_SIZE), @@ -281,9 +289,8 @@ void virtio_setup_ccw(VDev *vdev) .num = 0, }; - IPL_assert( - run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config), false) == 0, - "Could not get block device VQ configuration"); + rc = run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config), false); + IPL_assert(rc == 0, "Could not get virtio device VQ configuration"); info.num = config.num; vring_init(&vdev->vrings[i], &info); vdev->vrings[i].schid = vdev->schid; @@ -291,9 +298,10 @@ void virtio_setup_ccw(VDev *vdev) run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false) == 0, "Cannot set VQ info"); } - IPL_assert( - run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false) == 0, - "Could not write status to host"); + + status |= VIRTIO_CONFIG_S_DRIVER_OK; + rc = run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false); + IPL_assert(rc == 0, "Could not write DRIVER_OK status to host"); } bool virtio_is_supported(SubChannelId schid) diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h index 19fceb6495..85bd9d1695 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -182,24 +182,22 @@ enum guessed_disk_nature_type { typedef enum guessed_disk_nature_type VirtioGDN; VirtioGDN virtio_guessed_disk_nature(void); -void virtio_assume_scsi(void); void virtio_assume_eckd(void); void virtio_assume_iso9660(void); -extern bool virtio_disk_is_scsi(void); -extern bool virtio_disk_is_eckd(void); -extern bool virtio_ipl_disk_is_valid(void); -extern int virtio_get_block_size(void); -extern uint8_t virtio_get_heads(void); -extern uint8_t virtio_get_sectors(void); -extern uint64_t virtio_get_blocks(void); -extern int virtio_read_many(ulong sector, void *load_addr, int sec_num); +bool virtio_ipl_disk_is_valid(void); +int virtio_get_block_size(void); +uint8_t virtio_get_heads(void); +uint8_t virtio_get_sectors(void); +uint64_t virtio_get_blocks(void); +int virtio_read_many(unsigned long sector, void *load_addr, int sec_num); #define VIRTIO_SECTOR_SIZE 512 #define VIRTIO_ISO_BLOCK_SIZE 2048 #define VIRTIO_SCSI_BLOCK_SIZE 512 +#define VIRTIO_DASD_DEFAULT_BLOCK_SIZE 4096 -static inline ulong virtio_sector_adjust(ulong sector) +static inline unsigned long virtio_sector_adjust(unsigned long sector) { return sector * (virtio_get_block_size() / VIRTIO_SECTOR_SIZE); } diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img index bc34af8a28..6908e49f06 100644 Binary files a/pc-bios/s390-netboot.img and b/pc-bios/s390-netboot.img differ diff --git a/pc-bios/sgabios.bin b/pc-bios/sgabios.bin deleted file mode 100644 index 6308f2e2d7..0000000000 Binary files a/pc-bios/sgabios.bin and /dev/null differ diff --git a/pc-bios/skiboot.lid b/pc-bios/skiboot.lid index 58ec5ec38e..906bd51271 100644 Binary files a/pc-bios/skiboot.lid and b/pc-bios/skiboot.lid differ diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin index cbbe23e910..27fed09f49 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 4533d0d063..e10cd263b8 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 3ecf92de01..416036d986 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 9b4ffdf45f..4ffaa43d6f 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 8a27dac557..1b7a3831ef 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 ec9541cfb4..dba6cb8e32 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 55390c45c9..0d541c5530 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 2334733a75..2ce3557624 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 b668ac04a6..b7cab15593 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 a924891ea5..ee748f6599 100644 Binary files a/pc-bios/vgabios.bin and b/pc-bios/vgabios.bin differ diff --git a/pc-bios/vof/Makefile b/pc-bios/vof/Makefile index 391ac0d600..d1eb6ced7e 100644 --- a/pc-bios/vof/Makefile +++ b/pc-bios/vof/Makefile @@ -2,23 +2,30 @@ include config.mak VPATH=$(SRC_DIR) all: vof.bin -CC ?= $(CROSS)gcc -LD ?= $(CROSS)ld -OBJCOPY ?= $(CROSS)objcopy +NULL := +SPACE := $(NULL) # +TARGET_PREFIX := $(patsubst %/,%:$(SPACE),$(TARGET_DIR)) + +quiet-@ = $(if $(V),,@$(if $1,,printf "%s\n" "$(TARGET_PREFIX)$1" && )) +quiet-command = $(call quiet-@,$2 $@)$1 + +EXTRA_CFLAGS += -mcpu=power4 %.o: %.S - $(CC) -m32 -mbig-endian -mcpu=power4 -c -o $@ $< + $(call quiet-command, $(CC) $(EXTRA_CFLAGS) -c -o $@ $<,Assembling) %.o: %.c - $(CC) -m32 -mbig-endian -mcpu=power4 -c -fno-stack-protector -o $@ $< + $(call quiet-command, $(CC) $(EXTRA_CFLAGS) -c -fno-stack-protector -o $@ $<,Compiling) vof.elf: entry.o main.o ci.o bootmem.o libc.o - $(LD) -nostdlib -e_start -T$(SRC_DIR)/vof.lds -EB -o $@ $^ + $(call quiet-command, $(LD) -nostdlib -e_start -T$(SRC_DIR)/vof.lds -EB -o $@ $^,Linking) %.bin: %.elf - $(OBJCOPY) -O binary -j .text -j .data -j .toc -j .got2 $^ $@ + $(call quiet-command, $(OBJCOPY) -O binary -j .text -j .data -j .toc -j .got2 $^ $@,Extracting raw object) clean: rm -f *.o vof.bin vof.elf *~ -.PHONY: all clean +distclean: clean + +.PHONY: all clean distclean diff --git a/plugins/api.c b/plugins/api.c index 7bf71b189d..8fa5a600ac 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -8,6 +8,7 @@ * * qemu_plugin_tb * qemu_plugin_insn + * qemu_plugin_register * * Which can then be passed back into the API to do additional things. * As such all the public functions in here are exported in @@ -35,10 +36,12 @@ */ #include "qemu/osdep.h" +#include "qemu/main-loop.h" #include "qemu/plugin.h" #include "qemu/log.h" #include "tcg/tcg.h" #include "exec/exec-all.h" +#include "exec/gdbstub.h" #include "exec/ram_addr.h" #include "disas/disas.h" #include "plugin.h" @@ -89,17 +92,24 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb, void *udata) { if (!tb->mem_only) { - plugin_register_dyn_cb__udata(&tb->cbs[PLUGIN_CB_REGULAR], + int index = flags == QEMU_PLUGIN_CB_R_REGS || + flags == QEMU_PLUGIN_CB_RW_REGS ? + PLUGIN_CB_REGULAR_R : PLUGIN_CB_REGULAR; + + plugin_register_dyn_cb__udata(&tb->cbs[index], cb, flags, udata); } } -void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb, - enum qemu_plugin_op op, - void *ptr, uint64_t imm) +void qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + struct qemu_plugin_tb *tb, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm) { if (!tb->mem_only) { - plugin_register_inline_op(&tb->cbs[PLUGIN_CB_INLINE], 0, op, ptr, imm); + plugin_register_inline_op_on_entry( + &tb->cbs[PLUGIN_CB_INLINE], 0, op, entry, imm); } } @@ -109,18 +119,24 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, void *udata) { if (!insn->mem_only) { - plugin_register_dyn_cb__udata(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], + int index = flags == QEMU_PLUGIN_CB_R_REGS || + flags == QEMU_PLUGIN_CB_RW_REGS ? + PLUGIN_CB_REGULAR_R : PLUGIN_CB_REGULAR; + + plugin_register_dyn_cb__udata(&insn->cbs[PLUGIN_CB_INSN][index], cb, flags, udata); } } -void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn, - enum qemu_plugin_op op, - void *ptr, uint64_t imm) +void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + struct qemu_plugin_insn *insn, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm) { if (!insn->mem_only) { - plugin_register_inline_op(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], - 0, op, ptr, imm); + plugin_register_inline_op_on_entry( + &insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], 0, op, entry, imm); } } @@ -136,16 +152,18 @@ void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, void *udata) { plugin_register_vcpu_mem_cb(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], - cb, flags, rw, udata); + cb, flags, rw, udata); } -void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn, - enum qemu_plugin_mem_rw rw, - enum qemu_plugin_op op, void *ptr, - uint64_t imm) +void qemu_plugin_register_vcpu_mem_inline_per_vcpu( + struct qemu_plugin_insn *insn, + enum qemu_plugin_mem_rw rw, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm) { - plugin_register_inline_op(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE], - rw, op, ptr, imm); + plugin_register_inline_op_on_entry( + &insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE], rw, op, entry, imm); } void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, @@ -289,6 +307,8 @@ struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); hwaddr_info.is_store = (rw & QEMU_PLUGIN_MEM_W) != 0; + assert(mmu_idx < NB_MMU_MODES); + if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx, hwaddr_info.is_store, &hwaddr_info)) { error_report("invalid use of qemu_plugin_get_hwaddr"); @@ -314,22 +334,7 @@ uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr) { #ifdef CONFIG_SOFTMMU if (haddr) { - if (!haddr->is_io) { - RAMBlock *block; - ram_addr_t offset; - void *hostaddr = haddr->v.ram.hostaddr; - - block = qemu_ram_block_from_host(hostaddr, false, &offset); - if (!block) { - error_report("Bad host ram pointer %p", haddr->v.ram.hostaddr); - abort(); - } - - return block->offset + offset + block->mr->addr; - } else { - MemoryRegionSection *mrs = haddr->v.io.section; - return mrs->offset_within_address_space + haddr->v.io.offset; - } + return haddr->phys_addr; } #endif return 0; @@ -339,13 +344,13 @@ const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h) { #ifdef CONFIG_SOFTMMU if (h && h->is_io) { - MemoryRegionSection *mrs = h->v.io.section; - if (!mrs->mr->name) { - unsigned long maddr = 0xffffffff & (uintptr_t) mrs->mr; - g_autofree char *temp = g_strdup_printf("anon%08lx", maddr); + MemoryRegion *mr = h->mr; + if (!mr->name) { + unsigned maddr = (uintptr_t)mr; + g_autofree char *temp = g_strdup_printf("anon%08x", maddr); return g_intern_string(temp); } else { - return g_intern_string(mrs->mr->name); + return g_intern_string(mr->name); } } else { return g_intern_static_string("RAM"); @@ -355,34 +360,9 @@ const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h) #endif } -/* - * Queries to the number and potential maximum number of vCPUs there - * will be. This helps the plugin dimension per-vcpu arrays. - */ - -#ifndef CONFIG_USER_ONLY -static MachineState * get_ms(void) +int qemu_plugin_num_vcpus(void) { - return MACHINE(qdev_get_machine()); -} -#endif - -int qemu_plugin_n_vcpus(void) -{ -#ifdef CONFIG_USER_ONLY - return -1; -#else - return get_ms()->smp.cpus; -#endif -} - -int qemu_plugin_n_max_vcpus(void) -{ -#ifdef CONFIG_USER_ONLY - return -1; -#else - return get_ms()->smp.max_cpus; -#endif + return plugin_num_vcpus(); } /* @@ -405,7 +385,7 @@ const char *qemu_plugin_path_to_binary(void) { char *path = NULL; #ifdef CONFIG_USER_ONLY - TaskState *ts = (TaskState *) current_cpu->opaque; + TaskState *ts = get_task_state(current_cpu); path = g_strdup(ts->bprm->filename); #endif return path; @@ -415,7 +395,7 @@ uint64_t qemu_plugin_start_code(void) { uint64_t start = 0; #ifdef CONFIG_USER_ONLY - TaskState *ts = (TaskState *) current_cpu->opaque; + TaskState *ts = get_task_state(current_cpu); start = ts->info->start_code; #endif return start; @@ -425,7 +405,7 @@ uint64_t qemu_plugin_end_code(void) { uint64_t end = 0; #ifdef CONFIG_USER_ONLY - TaskState *ts = (TaskState *) current_cpu->opaque; + TaskState *ts = get_task_state(current_cpu); end = ts->info->end_code; #endif return end; @@ -435,8 +415,113 @@ uint64_t qemu_plugin_entry_code(void) { uint64_t entry = 0; #ifdef CONFIG_USER_ONLY - TaskState *ts = (TaskState *) current_cpu->opaque; + TaskState *ts = get_task_state(current_cpu); entry = ts->info->entry; #endif return entry; } + +/* + * Create register handles. + * + * We need to create a handle for each register so the plugin + * infrastructure can call gdbstub to read a register. They are + * currently just a pointer encapsulation of the gdb_reg but in + * future may hold internal plugin state so its important plugin + * authors are not tempted to treat them as numbers. + * + * We also construct a result array with those handles and some + * ancillary data the plugin might find useful. + */ + +static GArray *create_register_handles(GArray *gdbstub_regs) +{ + GArray *find_data = g_array_new(true, true, + sizeof(qemu_plugin_reg_descriptor)); + + for (int i = 0; i < gdbstub_regs->len; i++) { + GDBRegDesc *grd = &g_array_index(gdbstub_regs, GDBRegDesc, i); + qemu_plugin_reg_descriptor desc; + + /* skip "un-named" regs */ + if (!grd->name) { + continue; + } + + /* Create a record for the plugin */ + desc.handle = GINT_TO_POINTER(grd->gdb_reg); + desc.name = g_intern_string(grd->name); + desc.feature = g_intern_string(grd->feature_name); + g_array_append_val(find_data, desc); + } + + return find_data; +} + +GArray *qemu_plugin_get_registers(void) +{ + g_assert(current_cpu); + + g_autoptr(GArray) regs = gdb_get_register_list(current_cpu); + return create_register_handles(regs); +} + +int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) +{ + g_assert(current_cpu); + + return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg)); +} + +struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size) +{ + return plugin_scoreboard_new(element_size); +} + +void qemu_plugin_scoreboard_free(struct qemu_plugin_scoreboard *score) +{ + plugin_scoreboard_free(score); +} + +void *qemu_plugin_scoreboard_find(struct qemu_plugin_scoreboard *score, + unsigned int vcpu_index) +{ + g_assert(vcpu_index < qemu_plugin_num_vcpus()); + /* we can't use g_array_index since entry size is not statically known */ + char *base_ptr = score->data->data; + return base_ptr + vcpu_index * g_array_get_element_size(score->data); +} + +static uint64_t *plugin_u64_address(qemu_plugin_u64 entry, + unsigned int vcpu_index) +{ + char *ptr = qemu_plugin_scoreboard_find(entry.score, vcpu_index); + return (uint64_t *)(ptr + entry.offset); +} + +void qemu_plugin_u64_add(qemu_plugin_u64 entry, unsigned int vcpu_index, + uint64_t added) +{ + *plugin_u64_address(entry, vcpu_index) += added; +} + +uint64_t qemu_plugin_u64_get(qemu_plugin_u64 entry, + unsigned int vcpu_index) +{ + return *plugin_u64_address(entry, vcpu_index); +} + +void qemu_plugin_u64_set(qemu_plugin_u64 entry, unsigned int vcpu_index, + uint64_t val) +{ + *plugin_u64_address(entry, vcpu_index) = val; +} + +uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry) +{ + uint64_t total = 0; + for (int i = 0, n = qemu_plugin_num_vcpus(); i < n; ++i) { + total += qemu_plugin_u64_get(entry, i); + } + return total; +} diff --git a/plugins/core.c b/plugins/core.c index 792262da08..11ca20e626 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -17,18 +17,18 @@ #include "qapi/error.h" #include "qemu/lockable.h" #include "qemu/option.h" +#include "qemu/plugin.h" +#include "qemu/queue.h" #include "qemu/rcu_queue.h" #include "qemu/xxhash.h" #include "qemu/rcu.h" #include "hw/core/cpu.h" -#include "exec/cpu-common.h" #include "exec/exec-all.h" -#include "exec/helper-proto.h" +#include "exec/tb-flush.h" #include "tcg/tcg.h" #include "tcg/tcg-op.h" #include "plugin.h" -#include "qemu/compiler.h" struct qemu_plugin_cb { struct qemu_plugin_ctx *ctx; @@ -55,8 +55,9 @@ struct qemu_plugin_ctx *plugin_id_to_ctx_locked(qemu_plugin_id_t id) static void plugin_cpu_update__async(CPUState *cpu, run_on_cpu_data data) { - bitmap_copy(cpu->plugin_mask, &data.host_ulong, QEMU_PLUGIN_EV_MAX); - cpu_tb_jmp_cache_clear(cpu); + bitmap_copy(cpu->plugin_state->event_mask, + &data.host_ulong, QEMU_PLUGIN_EV_MAX); + tcg_flush_jmp_cache(cpu); } static void plugin_cpu_update__locked(gpointer k, gpointer v, gpointer udata) @@ -64,7 +65,7 @@ static void plugin_cpu_update__locked(gpointer k, gpointer v, gpointer udata) CPUState *cpu = container_of(k, CPUState, cpu_index); run_on_cpu_data mask = RUN_ON_CPU_HOST_ULONG(*plugin.mask); - if (cpu->created) { + if (DEVICE(cpu)->realized) { async_run_on_cpu(cpu, plugin_cpu_update__async, mask); } else { plugin_cpu_update__async(cpu, mask); @@ -210,15 +211,51 @@ plugin_register_cb_udata(qemu_plugin_id_t id, enum qemu_plugin_event ev, do_plugin_register_cb(id, ev, func, udata); } +CPUPluginState *qemu_plugin_create_vcpu_state(void) +{ + return g_new0(CPUPluginState, 1); +} + +static void plugin_grow_scoreboards__locked(CPUState *cpu) +{ + if (cpu->cpu_index < plugin.scoreboard_alloc_size) { + return; + } + + bool need_realloc = FALSE; + while (cpu->cpu_index >= plugin.scoreboard_alloc_size) { + plugin.scoreboard_alloc_size *= 2; + need_realloc = TRUE; + } + + + if (!need_realloc || QLIST_EMPTY(&plugin.scoreboards)) { + /* nothing to do, we just updated sizes for future scoreboards */ + return; + } + + /* cpus must be stopped, as tb might still use an existing scoreboard. */ + start_exclusive(); + struct qemu_plugin_scoreboard *score; + QLIST_FOREACH(score, &plugin.scoreboards, entry) { + g_array_set_size(score->data, plugin.scoreboard_alloc_size); + } + /* force all tb to be flushed, as scoreboard pointers were changed. */ + tb_flush(cpu); + end_exclusive(); +} + void qemu_plugin_vcpu_init_hook(CPUState *cpu) { bool success; qemu_rec_mutex_lock(&plugin.lock); + plugin.num_vcpus = MAX(plugin.num_vcpus, cpu->cpu_index + 1); plugin_cpu_update__locked(&cpu->cpu_index, NULL, NULL); success = g_hash_table_insert(plugin.cpu_ht, &cpu->cpu_index, &cpu->cpu_index); g_assert(success); + plugin_grow_scoreboards__locked(cpu); qemu_rec_mutex_unlock(&plugin.lock); plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT); @@ -279,17 +316,19 @@ static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray **arr) return &g_array_index(cbs, struct qemu_plugin_dyn_cb, cbs->len - 1); } -void plugin_register_inline_op(GArray **arr, - enum qemu_plugin_mem_rw rw, - enum qemu_plugin_op op, void *ptr, - uint64_t imm) +void plugin_register_inline_op_on_entry(GArray **arr, + enum qemu_plugin_mem_rw rw, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm) { struct qemu_plugin_dyn_cb *dyn_cb; dyn_cb = plugin_get_dyn_cb(arr); - dyn_cb->userp = ptr; + dyn_cb->userp = NULL; dyn_cb->type = PLUGIN_CB_INLINE; dyn_cb->rw = rw; + dyn_cb->inline_insn.entry = entry; dyn_cb->inline_insn.op = op; dyn_cb->inline_insn.imm = imm; } @@ -357,7 +396,7 @@ qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2, struct qemu_plugin_cb *cb, *next; enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_SYSCALL; - if (!test_bit(ev, cpu->plugin_mask)) { + if (!test_bit(ev, cpu->plugin_state->event_mask)) { return; } @@ -379,7 +418,7 @@ void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret) struct qemu_plugin_cb *cb, *next; enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_SYSCALL_RET; - if (!test_bit(ev, cpu->plugin_mask)) { + if (!test_bit(ev, cpu->plugin_state->event_mask)) { return; } @@ -392,12 +431,17 @@ void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret) void qemu_plugin_vcpu_idle_cb(CPUState *cpu) { - plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_IDLE); + /* idle and resume cb may be called before init, ignore in this case */ + if (cpu->cpu_index < plugin.num_vcpus) { + plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_IDLE); + } } void qemu_plugin_vcpu_resume_cb(CPUState *cpu) { - plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_RESUME); + if (cpu->cpu_index < plugin.num_vcpus) { + plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_RESUME); + } } void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id, @@ -432,9 +476,13 @@ void qemu_plugin_flush_cb(void) plugin_cb__simple(QEMU_PLUGIN_EV_FLUSH); } -void exec_inline_op(struct qemu_plugin_dyn_cb *cb) +void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index) { - uint64_t *val = cb->userp; + char *ptr = cb->inline_insn.entry.score->data->data; + size_t elem_size = g_array_get_element_size( + cb->inline_insn.entry.score->data); + size_t offset = cb->inline_insn.entry.offset; + uint64_t *val = (uint64_t *)(ptr + offset + cpu_index * elem_size); switch (cb->inline_insn.op) { case QEMU_PLUGIN_INLINE_ADD_U64: @@ -467,7 +515,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, vaddr, cb->userp); break; case PLUGIN_CB_INLINE: - exec_inline_op(cb); + exec_inline_op(cb, cpu->cpu_index); break; default: g_assert_not_reached(); @@ -500,26 +548,33 @@ void qemu_plugin_user_exit(void) enum qemu_plugin_event ev; CPUState *cpu; - QEMU_LOCK_GUARD(&plugin.lock); - + /* + * Locking order: we must acquire locks in an order that is consistent + * with the one in fork_start(). That is: + * - start_exclusive(), which acquires qemu_cpu_list_lock, + * must be called before acquiring plugin.lock. + * - tb_flush(), which acquires mmap_lock(), must be called + * while plugin.lock is not held. + */ start_exclusive(); + qemu_rec_mutex_lock(&plugin.lock); /* un-register all callbacks except the final AT_EXIT one */ for (ev = 0; ev < QEMU_PLUGIN_EV_MAX; ev++) { if (ev != QEMU_PLUGIN_EV_ATEXIT) { - struct qemu_plugin_ctx *ctx; - QTAILQ_FOREACH(ctx, &plugin.ctxs, entry) { - plugin_unregister_cb__locked(ctx, ev); + struct qemu_plugin_cb *cb, *next; + + QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { + plugin_unregister_cb__locked(cb->ctx, ev); } } } - - tb_flush(current_cpu); - CPU_FOREACH(cpu) { qemu_plugin_disable_mem_helpers(cpu); } + qemu_rec_mutex_unlock(&plugin.lock); + tb_flush(current_cpu); end_exclusive(); /* now it's safe to handle the exit case */ @@ -527,13 +582,22 @@ void qemu_plugin_user_exit(void) } /* - * Call this function after longjmp'ing to the main loop. It's possible that the - * last instruction of a TB might have used helpers, and therefore the - * "disable" instruction will never execute because it ended up as dead code. + * Helpers for *-user to ensure locks are sane across fork() events. */ -void qemu_plugin_disable_mem_helpers(CPUState *cpu) + +void qemu_plugin_user_prefork_lock(void) { - cpu->plugin_mem_cbs = NULL; + qemu_rec_mutex_lock(&plugin.lock); +} + +void qemu_plugin_user_postfork(bool is_child) +{ + if (is_child) { + /* should we just reset via plugin_init? */ + qemu_rec_mutex_init(&plugin.lock); + } else { + qemu_rec_mutex_unlock(&plugin.lock); + } } static bool plugin_dyn_cb_arr_cmp(const void *ap, const void *bp) @@ -551,8 +615,39 @@ static void __attribute__((__constructor__)) plugin_init(void) qemu_rec_mutex_init(&plugin.lock); plugin.id_ht = g_hash_table_new(g_int64_hash, g_int64_equal); plugin.cpu_ht = g_hash_table_new(g_int_hash, g_int_equal); + QLIST_INIT(&plugin.scoreboards); + plugin.scoreboard_alloc_size = 16; /* avoid frequent reallocation */ QTAILQ_INIT(&plugin.ctxs); qht_init(&plugin.dyn_cb_arr_ht, plugin_dyn_cb_arr_cmp, 16, QHT_MODE_AUTO_RESIZE); atexit(qemu_plugin_atexit_cb); } + +int plugin_num_vcpus(void) +{ + return plugin.num_vcpus; +} + +struct qemu_plugin_scoreboard *plugin_scoreboard_new(size_t element_size) +{ + struct qemu_plugin_scoreboard *score = + g_malloc0(sizeof(struct qemu_plugin_scoreboard)); + score->data = g_array_new(FALSE, TRUE, element_size); + g_array_set_size(score->data, plugin.scoreboard_alloc_size); + + qemu_rec_mutex_lock(&plugin.lock); + QLIST_INSERT_HEAD(&plugin.scoreboards, score, entry); + qemu_rec_mutex_unlock(&plugin.lock); + + return score; +} + +void plugin_scoreboard_free(struct qemu_plugin_scoreboard *score) +{ + qemu_rec_mutex_lock(&plugin.lock); + QLIST_REMOVE(score, entry); + qemu_rec_mutex_unlock(&plugin.lock); + + g_array_free(score->data, TRUE); + g_free(score); +} diff --git a/plugins/loader.c b/plugins/loader.c index 88c30bde2d..513a429c57 100644 --- a/plugins/loader.c +++ b/plugins/loader.c @@ -29,11 +29,10 @@ #include "qemu/plugin.h" #include "qemu/memalign.h" #include "hw/core/cpu.h" -#include "exec/exec-all.h" +#include "exec/tb-flush.h" #ifndef CONFIG_USER_ONLY #include "hw/boards.h" #endif -#include "qemu/compiler.h" #include "plugin.h" @@ -140,12 +139,12 @@ static int plugin_add(void *opaque, const char *name, const char *value, return 0; } -void qemu_plugin_opt_parse(const char *optarg, QemuPluginList *head) +void qemu_plugin_opt_parse(const char *optstr, QemuPluginList *head) { struct qemu_plugin_parse_arg arg; QemuOpts *opts; - opts = qemu_opts_parse_noisily(qemu_find_opts("plugin"), optarg, true); + opts = qemu_opts_parse_noisily(qemu_find_opts("plugin"), optstr, true); if (opts == NULL) { exit(1); } @@ -391,7 +390,7 @@ void plugin_reset_uninstall(qemu_plugin_id_t id, bool reset) { struct qemu_plugin_reset_data *data; - struct qemu_plugin_ctx *ctx; + struct qemu_plugin_ctx *ctx = NULL; WITH_QEMU_LOCK_GUARD(&plugin.lock) { ctx = plugin_id_to_ctx_locked(id); diff --git a/plugins/meson.build b/plugins/meson.build index fa12047327..51b4350c2a 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -1,8 +1,8 @@ plugin_ldflags = [] # Modules need more symbols than just those in plugins/qemu-plugins.symbols if not enable_modules - if targetos == 'darwin' - qemu_plugins_symbols_list = configure_file( + if host_os == 'darwin' + configure_file( input: files('qemu-plugins.symbols'), output: 'qemu-plugins-ld64.symbols', capture: true, @@ -13,8 +13,29 @@ if not enable_modules endif endif -specific_ss.add(when: 'CONFIG_PLUGIN', if_true: [files( - 'loader.c', - 'core.c', - 'api.c', -), declare_dependency(link_args: plugin_ldflags)]) +if get_option('plugins') + if host_os == 'windows' + dlltool = find_program('dlltool', required: true) + + # Generate a .lib file for plugins to link against. + # First, create a .def file listing all the symbols a plugin should expect to have + # available in qemu + win32_plugin_def = configure_file( + input: files('qemu-plugins.symbols'), + output: 'qemu_plugin_api.def', + capture: true, + command: ['sed', '-e', '0,/^/s//EXPORTS/; s/[{};]//g', '@INPUT@']) + # then use dlltool to assemble a delaylib. + win32_qemu_plugin_api_lib = configure_file( + input: win32_plugin_def, + output: 'libqemu_plugin_api.a', + command: [dlltool, '--input-def', '@INPUT@', + '--output-delaylib', '@OUTPUT@', '--dllname', 'qemu.exe'] + ) + endif + specific_ss.add(files( + 'loader.c', + 'core.c', + 'api.c', + ), declare_dependency(link_args: plugin_ldflags)) +endif diff --git a/plugins/plugin.h b/plugins/plugin.h index 5eb2fdbc85..7c34f23cfc 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -15,7 +15,7 @@ #include #include "qemu/qht.h" -#define QEMU_PLUGIN_MIN_VERSION 0 +#define QEMU_PLUGIN_MIN_VERSION 2 /* global state */ struct qemu_plugin_state { @@ -31,6 +31,8 @@ struct qemu_plugin_state { * but with the HT we avoid adding a field to CPUState. */ GHashTable *cpu_ht; + QLIST_HEAD(, qemu_plugin_scoreboard) scoreboards; + size_t scoreboard_alloc_size; DECLARE_BITMAP(mask, QEMU_PLUGIN_EV_MAX); /* * @lock protects the struct as well as ctx->uninstalling. @@ -44,6 +46,8 @@ struct qemu_plugin_state { * the code cache is flushed. */ struct qht dyn_cb_arr_ht; + /* How many vcpus were started */ + int num_vcpus; }; @@ -64,10 +68,11 @@ struct qemu_plugin_ctx { struct qemu_plugin_ctx *plugin_id_to_ctx_locked(qemu_plugin_id_t id); -void plugin_register_inline_op(GArray **arr, - enum qemu_plugin_mem_rw rw, - enum qemu_plugin_op op, void *ptr, - uint64_t imm); +void plugin_register_inline_op_on_entry(GArray **arr, + enum qemu_plugin_mem_rw rw, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm); void plugin_reset_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb, @@ -95,6 +100,12 @@ void plugin_register_vcpu_mem_cb(GArray **arr, enum qemu_plugin_mem_rw rw, void *udata); -void exec_inline_op(struct qemu_plugin_dyn_cb *cb); +void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index); + +int plugin_num_vcpus(void); + +struct qemu_plugin_scoreboard *plugin_scoreboard_new(size_t element_size); + +void plugin_scoreboard_free(struct qemu_plugin_scoreboard *score); #endif /* PLUGIN_H */ diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols index 71f6c90549..a9fac056c7 100644 --- a/plugins/qemu-plugins.symbols +++ b/plugins/qemu-plugins.symbols @@ -3,6 +3,7 @@ qemu_plugin_end_code; qemu_plugin_entry_code; qemu_plugin_get_hwaddr; + qemu_plugin_get_registers; qemu_plugin_hwaddr_device_name; qemu_plugin_hwaddr_is_io; qemu_plugin_hwaddr_phys_addr; @@ -16,30 +17,37 @@ qemu_plugin_mem_is_sign_extended; qemu_plugin_mem_is_store; qemu_plugin_mem_size_shift; - qemu_plugin_n_max_vcpus; - qemu_plugin_n_vcpus; + qemu_plugin_num_vcpus; qemu_plugin_outs; qemu_plugin_path_to_binary; + qemu_plugin_read_register; qemu_plugin_register_atexit_cb; qemu_plugin_register_flush_cb; qemu_plugin_register_vcpu_exit_cb; qemu_plugin_register_vcpu_idle_cb; qemu_plugin_register_vcpu_init_cb; qemu_plugin_register_vcpu_insn_exec_cb; - qemu_plugin_register_vcpu_insn_exec_inline; + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu; qemu_plugin_register_vcpu_mem_cb; - qemu_plugin_register_vcpu_mem_inline; + qemu_plugin_register_vcpu_mem_inline_per_vcpu; qemu_plugin_register_vcpu_resume_cb; qemu_plugin_register_vcpu_syscall_cb; qemu_plugin_register_vcpu_syscall_ret_cb; qemu_plugin_register_vcpu_tb_exec_cb; - qemu_plugin_register_vcpu_tb_exec_inline; + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu; qemu_plugin_register_vcpu_tb_trans_cb; qemu_plugin_reset; + qemu_plugin_scoreboard_free; + qemu_plugin_scoreboard_find; + qemu_plugin_scoreboard_new; qemu_plugin_start_code; qemu_plugin_tb_get_insn; qemu_plugin_tb_n_insns; qemu_plugin_tb_vaddr; + qemu_plugin_u64_add; + qemu_plugin_u64_get; + qemu_plugin_u64_set; + qemu_plugin_u64_sum; qemu_plugin_uninstall; qemu_plugin_vcpu_for_each; }; diff --git a/po/LINGUAS b/po/LINGUAS index cc4b5c3b36..9b33a3659f 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -5,4 +5,5 @@ hu it sv tr +uk zh_CN diff --git a/po/uk.po b/po/uk.po new file mode 100644 index 0000000000..ff037808bf --- /dev/null +++ b/po/uk.po @@ -0,0 +1,75 @@ +# Ukrainian translation for QEMU. +# This file is put in the public domain. +# Andrij Mizyk , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: QEMU 1.4.50\n" +"Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" +"PO-Revision-Date: 2022-06-13 01:33+0300\n" +"Last-Translator: Andrij Mizyk \n" +"Language-Team: Ukrainian\n" +"Language: uk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Gtranslator 2.91.6\n" + +msgid " - Press Ctrl+Alt+G to release grab" +msgstr " - Натисніть Ctrl+Alt+G, щоб відпустити захоплення" + +msgid " [Paused]" +msgstr " [Призупинено]" + +msgid "_Pause" +msgstr "_Призупинити" + +msgid "_Reset" +msgstr "_Скинути" + +msgid "Power _Down" +msgstr "Вимкнути _живлення" + +msgid "_Quit" +msgstr "_Вийти" + +msgid "_Fullscreen" +msgstr "Повний _екран" + +msgid "_Copy" +msgstr "_Копіювати" + +msgid "Zoom _In" +msgstr "_Збільшити" + +msgid "Zoom _Out" +msgstr "З_меншити" + +msgid "Best _Fit" +msgstr "Найкращий _розмір" + +msgid "Zoom To _Fit" +msgstr "Збільшити до _розміру" + +msgid "Grab On _Hover" +msgstr "Захопити при _наведенні" + +msgid "_Grab Input" +msgstr "Захопити _введення" + +msgid "Show _Tabs" +msgstr "Показувати _вкладки" + +msgid "Detach Tab" +msgstr "Відʼєднати вкладку" + +msgid "Show Menubar" +msgstr "Показувати рядок меню" + +msgid "_Machine" +msgstr "_Машина" + +msgid "_View" +msgstr "_Вигляд" diff --git a/python/.gitignore b/python/.gitignore index 904f324bb1..c3ceb1ca0a 100644 --- a/python/.gitignore +++ b/python/.gitignore @@ -11,8 +11,8 @@ qemu.egg-info/ .idea/ .vscode/ -# virtual environments (pipenv et al) -.venv/ +# virtual environments +.min-venv/ .tox/ .dev-venv/ diff --git a/python/Makefile b/python/Makefile index 3334311362..1fa4ba2498 100644 --- a/python/Makefile +++ b/python/Makefile @@ -1,21 +1,22 @@ QEMU_VENV_DIR=.dev-venv +QEMU_MINVENV_DIR=.min-venv QEMU_TOX_EXTRA_ARGS ?= .PHONY: help help: @echo "python packaging help:" @echo "" - @echo "make check-pipenv:" - @echo " Run tests in pipenv's virtual environment." + @echo "make check-minreqs:" + @echo " Run tests in the minreqs virtual environment." @echo " These tests use the oldest dependencies." - @echo " Requires: Python 3.6 and pipenv." - @echo " Hint (Fedora): 'sudo dnf install python3.6 pipenv'" + @echo " Requires: Python 3.8" + @echo " Hint (Fedora): 'sudo dnf install python3.8'" @echo "" @echo "make check-tox:" @echo " Run tests against multiple python versions." @echo " These tests use the newest dependencies." - @echo " Requires: Python 3.6 - 3.10, and tox." - @echo " Hint (Fedora): 'sudo dnf install python3-tox python3.10'" + @echo " Requires: Python 3.8 - 3.11, and tox." + @echo " Hint (Fedora): 'sudo dnf install python3-tox python3.11'" @echo " The variable QEMU_TOX_EXTRA_ARGS can be use to pass extra" @echo " arguments to tox". @echo "" @@ -29,12 +30,12 @@ help: @echo " Performs no environment setup of any kind." @echo "" @echo "make develop:" - @echo " Install deps needed for for 'make check'," + @echo " Install deps needed for 'make check'," @echo " and install the qemu package in editable mode." @echo " (Can be used in or outside of a venv.)" @echo "" - @echo "make pipenv" - @echo " Creates pipenv's virtual environment (.venv)" + @echo "make min-venv" + @echo " Creates the minreqs virtual environment ($(QEMU_MINVENV_DIR))" @echo "" @echo "make dev-venv" @echo " Creates a simple venv for check-dev. ($(QEMU_VENV_DIR))" @@ -43,21 +44,41 @@ help: @echo " Remove package build output." @echo "" @echo "make distclean:" - @echo " remove pipenv/venv files, qemu package forwarder," + @echo " remove venv files, qemu package forwarder," @echo " built distribution files, and everything from 'make clean'." @echo "" @echo -e "Have a nice day ^_^\n" -.PHONY: pipenv -pipenv: .venv -.venv: Pipfile.lock - @PIPENV_VENV_IN_PROJECT=1 pipenv sync --dev --keep-outdated - rm -f pyproject.toml - @touch .venv +.PHONY: pipenv check-pipenv +pipenv check-pipenv: + @echo "pipenv was dropped; try 'make check-minreqs' or 'make min-venv'" + @exit 1 -.PHONY: check-pipenv -check-pipenv: pipenv - @pipenv run make check +PIP_INSTALL = pip install --disable-pip-version-check +.PHONY: min-venv +min-venv: $(QEMU_MINVENV_DIR) $(QEMU_MINVENV_DIR)/bin/activate +$(QEMU_MINVENV_DIR) $(QEMU_MINVENV_DIR)/bin/activate: setup.cfg tests/minreqs.txt + @echo "VENV $(QEMU_MINVENV_DIR)" + @python3.8 -m venv $(QEMU_MINVENV_DIR) + @( \ + echo "ACTIVATE $(QEMU_MINVENV_DIR)"; \ + . $(QEMU_MINVENV_DIR)/bin/activate; \ + echo "INSTALL wheel $(QEMU_MINVENV_DIR)"; \ + $(PIP_INSTALL) wheel 1>/dev/null; \ + echo "INSTALL -r tests/minreqs.txt $(QEMU_MINVENV_DIR)";\ + $(PIP_INSTALL) -r tests/minreqs.txt 1>/dev/null; \ + echo "INSTALL -e qemu $(QEMU_MINVENV_DIR)"; \ + $(PIP_INSTALL) -e . 1>/dev/null; \ + ) + @touch $(QEMU_MINVENV_DIR) + +.PHONY: check-minreqs +check-minreqs: min-venv + @( \ + echo "ACTIVATE $(QEMU_MINVENV_DIR)"; \ + . $(QEMU_MINVENV_DIR)/bin/activate; \ + make check; \ + ) .PHONY: dev-venv dev-venv: $(QEMU_VENV_DIR) $(QEMU_VENV_DIR)/bin/activate @@ -82,7 +103,7 @@ check-dev: dev-venv .PHONY: develop develop: - pip3 install --disable-pip-version-check -e .[devel] + $(PIP_INSTALL) -e .[devel] .PHONY: check check: @@ -106,6 +127,7 @@ clean: .PHONY: distclean distclean: clean - rm -rf qemu.egg-info/ .venv/ .tox/ $(QEMU_VENV_DIR) dist/ + rm -rf qemu.egg-info/ .eggs/ dist/ + rm -rf $(QEMU_VENV_DIR) $(QEMU_MINVENV_DIR) .tox/ rm -f .coverage .coverage.* rm -rf htmlcov/ diff --git a/python/Pipfile b/python/Pipfile deleted file mode 100644 index e7acb8cefa..0000000000 --- a/python/Pipfile +++ /dev/null @@ -1,13 +0,0 @@ -[[source]] -name = "pypi" -url = "https://pypi.org/simple" -verify_ssl = true - -[dev-packages] -qemu = {editable = true, extras = ["devel"], path = "."} - -[packages] -qemu = {editable = true,path = "."} - -[requires] -python_version = "3.6" diff --git a/python/Pipfile.lock b/python/Pipfile.lock deleted file mode 100644 index ce46404ce0..0000000000 --- a/python/Pipfile.lock +++ /dev/null @@ -1,347 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "f1a25654d884a5b450e38d78b1f2e3ebb9073e421cc4358d4bbb83ac251a5670" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.6" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "qemu": { - "editable": true, - "path": "." - } - }, - "develop": { - "appdirs": { - "hashes": [ - "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", - "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128" - ], - "version": "==1.4.4" - }, - "astroid": { - "hashes": [ - "sha256:09bdb456e02564731f8b5957cdd0c98a7f01d2db5e90eb1d794c353c28bfd705", - "sha256:6a8a51f64dae307f6e0c9db752b66a7951e282389d8362cc1d39a56f3feeb31d" - ], - "index": "pypi", - "version": "==2.6.0" - }, - "avocado-framework": { - "hashes": [ - "sha256:244cb569f8eb4e50a22ac82e1a2b2bba2458999f4281efbe2651bd415d59c65b", - "sha256:6f15998b67ecd0e7dde790c4de4dd249d6df52dfe6d5cc4e2dd6596df51c3583" - ], - "index": "pypi", - "version": "==90.0" - }, - "distlib": { - "hashes": [ - "sha256:106fef6dc37dd8c0e2c0a60d3fca3e77460a48907f335fa28420463a6f799736", - "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c" - ], - "index": "pypi", - "version": "==0.3.2" - }, - "filelock": { - "hashes": [ - "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", - "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836" - ], - "index": "pypi", - "version": "==3.0.12" - }, - "flake8": { - "hashes": [ - "sha256:6a35f5b8761f45c5513e3405f110a86bea57982c3b75b766ce7b65217abe1670", - "sha256:c01f8a3963b3571a8e6bd7a4063359aff90749e160778e03817cd9b71c9e07d2" - ], - "index": "pypi", - "version": "==3.6.0" - }, - "fusepy": { - "hashes": [ - "sha256:10f5c7f5414241bffecdc333c4d3a725f1d6605cae6b4eaf86a838ff49cdaf6c", - "sha256:a9f3a3699080ddcf0919fd1eb2cf743e1f5859ca54c2018632f939bdfac269ee" - ], - "index": "pypi", - "version": "==2.0.4" - }, - "importlib-metadata": { - "hashes": [ - "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83", - "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070" - ], - "markers": "python_version < '3.8'", - "version": "==1.7.0" - }, - "importlib-resources": { - "hashes": [ - "sha256:54161657e8ffc76596c4ede7080ca68cb02962a2e074a2586b695a93a925d36e", - "sha256:e962bff7440364183203d179d7ae9ad90cb1f2b74dcb84300e88ecc42dca3351" - ], - "index": "pypi", - "version": "==5.1.4" - }, - "isort": { - "hashes": [ - "sha256:408e4d75d84f51b64d0824894afee44469eba34a4caee621dc53799f80d71ccc", - "sha256:64022dea6a06badfa09b300b4dfe8ba968114a737919e8ed50aea1c288f078aa" - ], - "index": "pypi", - "version": "==5.1.2" - }, - "lazy-object-proxy": { - "hashes": [ - "sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653", - "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61", - "sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2", - "sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837", - "sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3", - "sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43", - "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726", - "sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3", - "sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587", - "sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8", - "sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a", - "sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd", - "sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f", - "sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad", - "sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4", - "sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b", - "sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf", - "sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981", - "sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741", - "sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e", - "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93", - "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b" - ], - "index": "pypi", - "version": "==1.6.0" - }, - "mccabe": { - "hashes": [ - "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", - "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" - ], - "version": "==0.6.1" - }, - "mypy": { - "hashes": [ - "sha256:00cb1964a7476e871d6108341ac9c1a857d6bd20bf5877f4773ac5e9d92cd3cd", - "sha256:127de5a9b817a03a98c5ae8a0c46a20dc44442af6dcfa2ae7f96cb519b312efa", - "sha256:1f3976a945ad7f0a0727aafdc5651c2d3278e3c88dee94e2bf75cd3386b7b2f4", - "sha256:2f8c098f12b402c19b735aec724cc9105cc1a9eea405d08814eb4b14a6fb1a41", - "sha256:4ef13b619a289aa025f2273e05e755f8049bb4eaba6d703a425de37d495d178d", - "sha256:5d142f219bf8c7894dfa79ebfb7d352c4c63a325e75f10dfb4c3db9417dcd135", - "sha256:62eb5dd4ea86bda8ce386f26684f7f26e4bfe6283c9f2b6ca6d17faf704dcfad", - "sha256:64c36eb0936d0bfb7d8da49f92c18e312ad2e3ed46e5548ae4ca997b0d33bd59", - "sha256:75eed74d2faf2759f79c5f56f17388defd2fc994222312ec54ee921e37b31ad4", - "sha256:974bebe3699b9b46278a7f076635d219183da26e1a675c1f8243a69221758273", - "sha256:a5e5bb12b7982b179af513dddb06fca12285f0316d74f3964078acbfcf4c68f2", - "sha256:d31291df31bafb997952dc0a17ebb2737f802c754aed31dd155a8bfe75112c57", - "sha256:d3b4941de44341227ece1caaf5b08b23e42ad4eeb8b603219afb11e9d4cfb437", - "sha256:eadb865126da4e3c4c95bdb47fe1bb087a3e3ea14d39a3b13224b8a4d9f9a102" - ], - "index": "pypi", - "version": "==0.780" - }, - "mypy-extensions": { - "hashes": [ - "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", - "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" - ], - "version": "==0.4.3" - }, - "packaging": { - "hashes": [ - "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5", - "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a" - ], - "index": "pypi", - "version": "==20.9" - }, - "pluggy": { - "hashes": [ - "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", - "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" - ], - "index": "pypi", - "version": "==0.13.1" - }, - "py": { - "hashes": [ - "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3", - "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a" - ], - "index": "pypi", - "version": "==1.10.0" - }, - "pycodestyle": { - "hashes": [ - "sha256:74abc4e221d393ea5ce1f129ea6903209940c1ecd29e002e8c6933c2b21026e0", - "sha256:cbc619d09254895b0d12c2c691e237b2e91e9b2ecf5e84c26b35400f93dcfb83", - "sha256:cbfca99bd594a10f674d0cd97a3d802a1fdef635d4361e1a2658de47ed261e3a" - ], - "version": "==2.4.0" - }, - "pyflakes": { - "hashes": [ - "sha256:9a7662ec724d0120012f6e29d6248ae3727d821bba522a0e6b356eff19126a49", - "sha256:f661252913bc1dbe7fcfcbf0af0db3f42ab65aabd1a6ca68fe5d466bace94dae" - ], - "version": "==2.0.0" - }, - "pygments": { - "hashes": [ - "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f", - "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e" - ], - "index": "pypi", - "version": "==2.9.0" - }, - "pylint": { - "hashes": [ - "sha256:082a6d461b54f90eea49ca90fff4ee8b6e45e8029e5dbd72f6107ef84f3779c0", - "sha256:a01cd675eccf6e25b3bdb42be184eb46aaf89187d612ba0fb5f93328ed6b0fd5" - ], - "index": "pypi", - "version": "==2.8.0" - }, - "pyparsing": { - "hashes": [ - "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", - "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" - ], - "index": "pypi", - "version": "==2.4.7" - }, - "qemu": { - "editable": true, - "path": "." - }, - "setuptools": { - "hashes": [ - "sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373", - "sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e" - ], - "markers": "python_version >= '3.6'", - "version": "==59.6.0" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.2" - }, - "tox": { - "hashes": [ - "sha256:c60692d92fe759f46c610ac04c03cf0169432d1ff8e981e8ae63e068d0954fc3", - "sha256:f179cb4043d7dc1339425dd49ab1dd8c916246b0d9173143c1b0af7498a03ab0" - ], - "index": "pypi", - "version": "==3.18.0" - }, - "typed-ast": { - "hashes": [ - "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace", - "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff", - "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266", - "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528", - "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6", - "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808", - "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4", - "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363", - "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341", - "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04", - "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41", - "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e", - "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3", - "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899", - "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805", - "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c", - "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c", - "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39", - "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a", - "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3", - "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7", - "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f", - "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075", - "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0", - "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40", - "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428", - "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927", - "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3", - "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f", - "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65" - ], - "markers": "python_version < '3.8' and implementation_name == 'cpython'", - "version": "==1.4.3" - }, - "typing-extensions": { - "hashes": [ - "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", - "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", - "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" - ], - "index": "pypi", - "version": "==3.10.0.0" - }, - "urwid": { - "hashes": [ - "sha256:588bee9c1cb208d0906a9f73c613d2bd32c3ed3702012f51efe318a3f2127eae" - ], - "index": "pypi", - "version": "==2.1.2" - }, - "urwid-readline": { - "hashes": [ - "sha256:018020cbc864bb5ed87be17dc26b069eae2755cb29f3a9c569aac3bded1efaf4" - ], - "index": "pypi", - "version": "==0.13" - }, - "virtualenv": { - "hashes": [ - "sha256:14fdf849f80dbb29a4eb6caa9875d476ee2a5cf76a5f5415fa2f1606010ab467", - "sha256:2b0126166ea7c9c3661f5b8e06773d28f83322de7a3ff7d06f0aed18c9de6a76" - ], - "index": "pypi", - "version": "==20.4.7" - }, - "wrapt": { - "hashes": [ - "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7" - ], - "version": "==1.12.1" - }, - "zipp": { - "hashes": [ - "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76", - "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098" - ], - "index": "pypi", - "version": "==3.4.1" - } - } -} diff --git a/python/README.rst b/python/README.rst index 9c1fceaee7..d62e71528d 100644 --- a/python/README.rst +++ b/python/README.rst @@ -77,9 +77,6 @@ Files in this directory - ``MANIFEST.in`` is read by python setuptools, it specifies additional files that should be included by a source distribution. - ``PACKAGE.rst`` is used as the README file that is visible on PyPI.org. -- ``Pipfile`` is used by Pipenv to generate ``Pipfile.lock``. -- ``Pipfile.lock`` is a set of pinned package dependencies that this package - is tested under in our CI suite. It is used by ``make check-pipenv``. - ``README.rst`` you are here! - ``VERSION`` contains the PEP-440 compliant version used to describe this package; it is referenced by ``setup.cfg``. diff --git a/python/qemu/machine/console_socket.py b/python/qemu/machine/console_socket.py index 8c4ff598ad..0a4e09ffc7 100644 --- a/python/qemu/machine/console_socket.py +++ b/python/qemu/machine/console_socket.py @@ -24,19 +24,32 @@ class ConsoleSocket(socket.socket): """ ConsoleSocket represents a socket attached to a char device. - Optionally (if drain==True), drains the socket and places the bytes - into an in memory buffer for later processing. - - Optionally a file path can be passed in and we will also - dump the characters to this file for debugging purposes. + :param address: An AF_UNIX path or address. + :param sock_fd: Optionally, an existing socket file descriptor. + One of address or sock_fd must be specified. + :param file: Optionally, a filename to log to. + :param drain: Optionally, drains the socket and places the bytes + into an in memory buffer for later processing. """ - def __init__(self, address: str, file: Optional[str] = None, + def __init__(self, + address: Optional[str] = None, + sock_fd: Optional[int] = None, + file: Optional[str] = None, drain: bool = False): + if address is None and sock_fd is None: + raise ValueError("one of 'address' or 'sock_fd' must be specified") + if address is not None and sock_fd is not None: + raise ValueError("can't specify both 'address' and 'sock_fd'") + self._recv_timeout_sec = 300.0 self._sleep_time = 0.5 self._buffer: Deque[int] = deque() - socket.socket.__init__(self, socket.AF_UNIX, socket.SOCK_STREAM) - self.connect(address) + if address is not None: + socket.socket.__init__(self, socket.AF_UNIX, socket.SOCK_STREAM) + self.connect(address) + else: + assert sock_fd is not None + socket.socket.__init__(self, fileno=sock_fd) self._logfile = None if file: # pylint: disable=consider-using-with @@ -68,7 +81,7 @@ class ConsoleSocket(socket.socket): """Kick off a thread to drain the socket.""" # Configure socket to not block and timeout. # This allows our drain thread to not block - # on recieve and exit smoothly. + # on receive and exit smoothly. socket.socket.setblocking(self, False) socket.socket.settimeout(self, 1) drain_thread = threading.Thread(target=self._drain_fn) diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index 37191f433b..31cb9d617d 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -127,11 +127,10 @@ class QEMUMachine: name: Optional[str] = None, base_temp_dir: str = "/var/tmp", monitor_address: Optional[SocketAddrT] = None, - sock_dir: Optional[str] = None, drain_console: bool = False, console_log: Optional[str] = None, log_dir: Optional[str] = None, - qmp_timer: Optional[float] = None): + qmp_timer: Optional[float] = 30): ''' Initialize a QEMUMachine @@ -141,7 +140,6 @@ class QEMUMachine: @param name: prefix for socket and log file names (default: qemu-PID) @param base_temp_dir: default location where temp files are created @param monitor_address: address for QMP monitor - @param sock_dir: where to create socket (defaults to base_temp_dir) @param drain_console: (optional) True to drain console socket to buffer @param console_log: (optional) path to console log file @param log_dir: where to create and keep log files @@ -157,18 +155,15 @@ class QEMUMachine: self._wrapper = wrapper self._qmp_timer = qmp_timer - self._name = name or f"qemu-{os.getpid()}-{id(self):02x}" + self._name = name or f"{id(self):x}" + self._sock_pair: Optional[Tuple[socket.socket, socket.socket]] = None + self._cons_sock_pair: Optional[ + Tuple[socket.socket, socket.socket]] = None self._temp_dir: Optional[str] = None self._base_temp_dir = base_temp_dir - self._sock_dir = sock_dir self._log_dir = log_dir - if monitor_address is not None: - self._monitor_address = monitor_address - else: - self._monitor_address = os.path.join( - self.sock_dir, f"{self._name}-monitor.sock" - ) + self._monitor_address = monitor_address self._console_log_path = console_log if self._console_log_path: @@ -191,10 +186,8 @@ class QEMUMachine: self._console_index = 0 self._console_set = False self._console_device_type: Optional[str] = None - self._console_address = os.path.join( - self.sock_dir, f"{self._name}-console.sock" - ) self._console_socket: Optional[socket.socket] = None + self._console_file: Optional[socket.SocketIO] = None self._remove_files: List[str] = [] self._user_killed = False self._quit_issued = False @@ -303,7 +296,9 @@ class QEMUMachine: args = ['-display', 'none', '-vga', 'none'] if self._qmp_set: - if isinstance(self._monitor_address, tuple): + if self._sock_pair: + moncdev = f"socket,id=mon,fd={self._sock_pair[0].fileno()}" + elif isinstance(self._monitor_address, tuple): moncdev = "socket,id=mon,host={},port={}".format( *self._monitor_address ) @@ -317,8 +312,9 @@ class QEMUMachine: for _ in range(self._console_index): args.extend(['-serial', 'null']) if self._console_set: - chardev = ('socket,id=console,path=%s,server=on,wait=off' % - self._console_address) + assert self._cons_sock_pair is not None + fd = self._cons_sock_pair[0].fileno() + chardev = f"socket,id=console,fd={fd}" args.extend(['-chardev', chardev]) if self._console_device_type is None: args.extend(['-serial', 'chardev:console']) @@ -333,18 +329,27 @@ class QEMUMachine: return self._args def _pre_launch(self) -> None: - if self._console_set: - self._remove_files.append(self._console_address) - if self._qmp_set: + if self._monitor_address is None: + self._sock_pair = socket.socketpair() + os.set_inheritable(self._sock_pair[0].fileno(), True) + sock = self._sock_pair[1] if isinstance(self._monitor_address, str): self._remove_files.append(self._monitor_address) + + sock_or_addr = self._monitor_address or sock + assert sock_or_addr is not None + self._qmp_connection = QEMUMonitorProtocol( - self._monitor_address, - server=True, + sock_or_addr, + server=bool(self._monitor_address), nickname=self._name ) + if self._console_set: + self._cons_sock_pair = socket.socketpair() + os.set_inheritable(self._cons_sock_pair[0].fileno(), True) + # NOTE: Make sure any opened resources are *definitely* freed in # _post_shutdown()! # pylint: disable=consider-using-with @@ -360,8 +365,16 @@ class QEMUMachine: )) def _post_launch(self) -> None: + if self._sock_pair: + self._sock_pair[0].close() + if self._cons_sock_pair: + self._cons_sock_pair[0].close() + if self._qmp_connection: - self._qmp.accept(self._qmp_timer) + if self._sock_pair: + self._qmp.connect() + else: + self._qmp.accept(self._qmp_timer) def _close_qemu_log_file(self) -> None: if self._qemu_log_file is not None: @@ -373,6 +386,7 @@ class QEMUMachine: Called to cleanup the VM instance after the process has exited. May also be called after a failed launch. """ + LOG.debug("Cleaning up after VM process") try: self._close_qmp_connection() except Exception as err: # pylint: disable=broad-except @@ -383,6 +397,11 @@ class QEMUMachine: finally: assert self._qmp_connection is None + if self._sock_pair: + self._sock_pair[0].close() + self._sock_pair[1].close() + self._sock_pair = None + self._close_qemu_log_file() self._load_io_log() @@ -496,10 +515,21 @@ class QEMUMachine: # If we keep the console socket open, we may deadlock waiting # for QEMU to exit, while QEMU is waiting for the socket to # become writable. + if self._console_file is not None: + LOG.debug("Closing console file") + self._console_file.close() + self._console_file = None + if self._console_socket is not None: + LOG.debug("Closing console socket") self._console_socket.close() self._console_socket = None + if self._cons_sock_pair: + self._cons_sock_pair[0].close() + self._cons_sock_pair[1].close() + self._cons_sock_pair = None + def _hard_shutdown(self) -> None: """ Perform early cleanup, kill the VM, and wait for it to terminate. @@ -507,6 +537,7 @@ class QEMUMachine: :raise subprocess.Timeout: When timeout is exceeds 60 seconds waiting for the QEMU process to terminate. """ + LOG.debug("Performing hard shutdown") self._early_cleanup() self._subp.kill() self._subp.wait(timeout=60) @@ -523,8 +554,18 @@ class QEMUMachine: :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for the QEMU process to terminate. """ + LOG.debug("Attempting graceful termination") + self._early_cleanup() + if self._quit_issued: + LOG.debug( + "Anticipating QEMU termination due to prior 'quit' command, " + "or explicit call to wait()" + ) + else: + LOG.debug("Politely asking QEMU to terminate") + if self._qmp_connection: try: if not self._quit_issued: @@ -534,8 +575,18 @@ class QEMUMachine: finally: # Regardless, we want to quiesce the connection. self._close_qmp_connection() + elif not self._quit_issued: + LOG.debug( + "Not anticipating QEMU quit and no QMP connection present, " + "issuing SIGTERM" + ) + self._subp.terminate() # May raise subprocess.TimeoutExpired + LOG.debug( + "Waiting (timeout=%s) for QEMU process (pid=%s) to terminate", + timeout, self._subp.pid + ) self._subp.wait(timeout=timeout) def _do_shutdown(self, timeout: Optional[int]) -> None: @@ -553,6 +604,10 @@ class QEMUMachine: try: self._soft_shutdown(timeout) except Exception as exc: + if isinstance(exc, subprocess.TimeoutExpired): + LOG.debug("Timed out waiting for QEMU process to exit") + LOG.debug("Graceful shutdown failed", exc_info=True) + LOG.debug("Falling back to hard shutdown") self._hard_shutdown() raise AbnormalShutdown("Could not perform graceful shutdown") \ from exc @@ -575,6 +630,10 @@ class QEMUMachine: if not self._launched: return + LOG.debug("Shutting down VM appliance; timeout=%s", timeout) + if hard: + LOG.debug("Caller requests immediate termination of QEMU process.") + try: if hard: self._user_killed = True @@ -643,21 +702,31 @@ class QEMUMachine: conv_keys = True qmp_args = self._qmp_args(conv_keys, args) - ret = self._qmp.cmd(cmd, args=qmp_args) + ret = self._qmp.cmd_raw(cmd, args=qmp_args) if cmd == 'quit' and 'error' not in ret and 'return' in ret: self._quit_issued = True return ret - def command(self, cmd: str, - conv_keys: bool = True, - **args: Any) -> QMPReturnValue: + def cmd(self, cmd: str, + args_dict: Optional[Dict[str, object]] = None, + conv_keys: Optional[bool] = None, + **args: Any) -> QMPReturnValue: """ Invoke a QMP command. On success return the response dict. On failure raise an exception. """ + if args_dict is not None: + assert not args + assert conv_keys is None + args = args_dict + conv_keys = False + + if conv_keys is None: + conv_keys = True + qmp_args = self._qmp_args(conv_keys, args) - ret = self._qmp.command(cmd, **qmp_args) + ret = self._qmp.cmd(cmd, **qmp_args) if cmd == 'quit': self._quit_issued = True return ret @@ -831,12 +900,34 @@ class QEMUMachine: Returns a socket connected to the console """ if self._console_socket is None: + LOG.debug("Opening console socket") + if not self._console_set: + raise QEMUMachineError( + "Attempt to access console socket with no connection") + assert self._cons_sock_pair is not None + # os.dup() is used here for sock_fd because otherwise we'd + # have two rich python socket objects that would each try to + # close the same underlying fd when either one gets garbage + # collected. self._console_socket = console_socket.ConsoleSocket( - self._console_address, + sock_fd=os.dup(self._cons_sock_pair[1].fileno()), file=self._console_log_path, drain=self._drain_console) + self._cons_sock_pair[1].close() return self._console_socket + @property + def console_file(self) -> socket.SocketIO: + """ + Returns a file associated with the console socket + """ + if self._console_file is None: + LOG.debug("Opening console file") + self._console_file = self.console_socket.makefile(mode='rb', + buffering=0, + encoding='utf-8') + return self._console_file + @property def temp_dir(self) -> str: """ @@ -847,15 +938,6 @@ class QEMUMachine: dir=self._base_temp_dir) return self._temp_dir - @property - def sock_dir(self) -> str: - """ - Returns the directory used for sockfiles by this machine. - """ - if self._sock_dir: - return self._sock_dir - return self.temp_dir - @property def log_dir(self) -> str: """ diff --git a/python/qemu/machine/qtest.py b/python/qemu/machine/qtest.py index 1a1fc6c9b0..4f5ede85b2 100644 --- a/python/qemu/machine/qtest.py +++ b/python/qemu/machine/qtest.py @@ -24,6 +24,7 @@ from typing import ( Optional, Sequence, TextIO, + Tuple, ) from qemu.qmp import SocketAddrT @@ -38,23 +39,41 @@ class QEMUQtestProtocol: :param address: QEMU address, can be either a unix socket path (string) or a tuple in the form ( address, port ) for a TCP connection - :param server: server mode, listens on the socket (bool) + :param sock: An existing socket can be provided as an alternative to + an address. One of address or sock must be provided. + :param server: server mode, listens on the socket. Only meaningful + in conjunction with an address and not an existing + socket. + :raise socket.error: on socket connection errors .. note:: - No conection is estabalished by __init__(), this is done + No connection is established by __init__(), this is done by the connect() or accept() methods. """ - def __init__(self, address: SocketAddrT, + def __init__(self, + address: Optional[SocketAddrT] = None, + sock: Optional[socket.socket] = None, server: bool = False): + if address is None and sock is None: + raise ValueError("Either 'address' or 'sock' must be specified") + if address is not None and sock is not None: + raise ValueError( + "Either 'address' or 'sock' must be specified, but not both") + if sock is not None and server: + raise ValueError("server=True is meaningless when passing socket") + self._address = address - self._sock = self._get_sock() + self._sock = sock or self._get_sock() self._sockfile: Optional[TextIO] = None + if server: + assert self._address is not None self._sock.bind(self._address) self._sock.listen(1) def _get_sock(self) -> socket.socket: + assert self._address is not None if isinstance(self._address, tuple): family = socket.AF_INET else: @@ -67,7 +86,8 @@ class QEMUQtestProtocol: @raise socket.error on socket connection errors """ - self._sock.connect(self._address) + if self._address is not None: + self._sock.connect(self._address) self._sockfile = self._sock.makefile(mode='r') def accept(self) -> None: @@ -115,41 +135,49 @@ class QEMUQtestMachine(QEMUMachine): wrapper: Sequence[str] = (), name: Optional[str] = None, base_temp_dir: str = "/var/tmp", - sock_dir: Optional[str] = None, qmp_timer: Optional[float] = None): # pylint: disable=too-many-arguments if name is None: name = "qemu-%d" % os.getpid() - if sock_dir is None: - sock_dir = base_temp_dir super().__init__(binary, args, wrapper=wrapper, name=name, base_temp_dir=base_temp_dir, - sock_dir=sock_dir, qmp_timer=qmp_timer) + qmp_timer=qmp_timer) self._qtest: Optional[QEMUQtestProtocol] = None - self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock") + self._qtest_sock_pair: Optional[ + Tuple[socket.socket, socket.socket]] = None @property def _base_args(self) -> List[str]: args = super()._base_args + assert self._qtest_sock_pair is not None + fd = self._qtest_sock_pair[0].fileno() args.extend([ - '-qtest', f"unix:path={self._qtest_path}", + '-chardev', f"socket,id=qtest,fd={fd}", + '-qtest', 'chardev:qtest', '-accel', 'qtest' ]) return args def _pre_launch(self) -> None: + self._qtest_sock_pair = socket.socketpair() + os.set_inheritable(self._qtest_sock_pair[0].fileno(), True) super()._pre_launch() - self._qtest = QEMUQtestProtocol(self._qtest_path, server=True) + self._qtest = QEMUQtestProtocol(sock=self._qtest_sock_pair[1]) def _post_launch(self) -> None: assert self._qtest is not None super()._post_launch() - self._qtest.accept() + if self._qtest_sock_pair: + self._qtest_sock_pair[0].close() + self._qtest.connect() def _post_shutdown(self) -> None: + if self._qtest_sock_pair: + self._qtest_sock_pair[0].close() + self._qtest_sock_pair[1].close() + self._qtest_sock_pair = None super()._post_shutdown() - self._remove_if_exists(self._qtest_path) def qtest(self, cmd: str) -> str: """ diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py index 03b5574618..22a2b5616e 100644 --- a/python/qemu/qmp/legacy.py +++ b/python/qemu/qmp/legacy.py @@ -22,6 +22,7 @@ old interface. # import asyncio +import socket from types import TracebackType from typing import ( Any, @@ -50,7 +51,7 @@ QMPObject = Dict[str, object] # QMPMessage can be outgoing commands or incoming events/returns. # QMPReturnValue is usually a dict/json object, but due to QAPI's -# 'returns-whitelist', it can actually be anything. +# 'command-returns-exceptions', it can actually be anything. # # {'return': {}} is a QMPMessage, # {} is the QMPReturnValue. @@ -67,23 +68,30 @@ class QEMUMonitorProtocol: Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP) and then allow to handle commands and events. - :param address: QEMU address, can be either a unix socket path (string) - or a tuple in the form ( address, port ) for a TCP - connection + :param address: QEMU address, can be a unix socket path (string), a tuple + in the form ( address, port ) for a TCP connection, or an + existing `socket.socket` object. :param server: Act as the socket server. (See 'accept') + Not applicable when passing a socket directly. :param nickname: Optional nickname used for logging. """ - def __init__(self, address: SocketAddrT, + def __init__(self, + address: Union[SocketAddrT, socket.socket], server: bool = False, nickname: Optional[str] = None): + if server and isinstance(address, socket.socket): + raise ValueError( + "server argument should be False when passing a socket") + self._qmp = QMPClient(nickname) self._aloop = asyncio.get_event_loop() self._address = address self._timeout: Optional[float] = None if server: + assert not isinstance(self._address, socket.socket) self._sync(self._qmp.start_server(self._address)) _T = TypeVar('_T') @@ -186,24 +194,20 @@ class QEMUMonitorProtocol: ) ) - def cmd(self, name: str, - args: Optional[Dict[str, object]] = None, - cmd_id: Optional[object] = None) -> QMPMessage: + def cmd_raw(self, name: str, + args: Optional[Dict[str, object]] = None) -> QMPMessage: """ Build a QMP command and send it to the QMP Monitor. :param name: command name (string) :param args: command arguments (dict) - :param cmd_id: command id (dict, list, string or int) """ qmp_cmd: QMPMessage = {'execute': name} if args: qmp_cmd['arguments'] = args - if cmd_id: - qmp_cmd['id'] = cmd_id return self.cmd_obj(qmp_cmd) - def command(self, cmd: str, **kwds: object) -> QMPReturnValue: + def cmd(self, cmd: str, **kwds: object) -> QMPReturnValue: """ Build and send a QMP command to the monitor, report errors if any """ diff --git a/python/qemu/qmp/models.py b/python/qemu/qmp/models.py index de87f87804..da52848d5a 100644 --- a/python/qemu/qmp/models.py +++ b/python/qemu/qmp/models.py @@ -54,7 +54,7 @@ class Model: class Greeting(Model): """ - Defined in qmp-spec.txt, section 2.2, "Server Greeting". + Defined in qmp-spec.rst, section "Server Greeting". :param raw: The raw Greeting object. :raise KeyError: If any required fields are absent. @@ -82,7 +82,7 @@ class Greeting(Model): class QMPGreeting(Model): """ - Defined in qmp-spec.txt, section 2.2, "Server Greeting". + Defined in qmp-spec.rst, section "Server Greeting". :param raw: The raw QMPGreeting object. :raise KeyError: If any required fields are absent. @@ -104,7 +104,7 @@ class QMPGreeting(Model): class ErrorResponse(Model): """ - Defined in qmp-spec.txt, section 2.4.2, "error". + Defined in qmp-spec.rst, section "Error". :param raw: The raw ErrorResponse object. :raise KeyError: If any required fields are absent. @@ -126,7 +126,7 @@ class ErrorResponse(Model): class ErrorInfo(Model): """ - Defined in qmp-spec.txt, section 2.4.2, "error". + Defined in qmp-spec.rst, section "Error". :param raw: The raw ErrorInfo object. :raise KeyError: If any required fields are absent. diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index 6ea86650ad..a4ffdfad51 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -18,6 +18,7 @@ from asyncio import StreamReader, StreamWriter from enum import Enum from functools import wraps import logging +import socket from ssl import SSLContext from typing import ( Any, @@ -206,7 +207,7 @@ class AsyncProtocol(Generic[T]): logger = logging.getLogger(__name__) # Maximum allowable size of read buffer - _limit = (64 * 1024) + _limit = 64 * 1024 # ------------------------- # Section: Public interface @@ -355,7 +356,7 @@ class AsyncProtocol(Generic[T]): @upper_half @require(Runstate.IDLE) - async def connect(self, address: SocketAddrT, + async def connect(self, address: Union[SocketAddrT, socket.socket], ssl: Optional[SSLContext] = None) -> None: """ Connect to the server and begin processing message queues. @@ -494,7 +495,6 @@ class AsyncProtocol(Generic[T]): try: self.logger.debug("Stopping server.") self._server.close() - await self._server.wait_closed() self.logger.debug("Server stopped.") finally: self._server = None @@ -600,7 +600,7 @@ class AsyncProtocol(Generic[T]): self.logger.debug("Connection accepted.") @upper_half - async def _do_connect(self, address: SocketAddrT, + async def _do_connect(self, address: Union[SocketAddrT, socket.socket], ssl: Optional[SSLContext] = None) -> None: """ Acting as the transport client, initiate a connection to a server. @@ -619,9 +619,17 @@ class AsyncProtocol(Generic[T]): # otherwise yield. await asyncio.sleep(0) - self.logger.debug("Connecting to %s ...", address) - - if isinstance(address, tuple): + if isinstance(address, socket.socket): + self.logger.debug("Connecting with existing socket: " + "fd=%d, family=%r, type=%r", + address.fileno(), address.family, address.type) + connect = asyncio.open_connection( + limit=self._limit, + ssl=ssl, + sock=address, + ) + elif isinstance(address, tuple): + self.logger.debug("Connecting to %s ...", address) connect = asyncio.open_connection( address[0], address[1], @@ -629,13 +637,14 @@ class AsyncProtocol(Generic[T]): limit=self._limit, ) else: + self.logger.debug("Connecting to file://%s ...", address) connect = asyncio.open_unix_connection( path=address, ssl=ssl, limit=self._limit, ) - self._reader, self._writer = await connect + self._reader, self._writer = await connect self.logger.debug("Connected.") @upper_half @@ -812,7 +821,7 @@ class AsyncProtocol(Generic[T]): @bottom_half async def _bh_close_stream(self, error_pathway: bool = False) -> None: - # NB: Closing the writer also implcitly closes the reader. + # NB: Closing the writer also implicitly closes the reader. if not self._writer: return diff --git a/python/qemu/qmp/qmp_client.py b/python/qemu/qmp/qmp_client.py index 5dcda04a75..2a817f9db3 100644 --- a/python/qemu/qmp/qmp_client.py +++ b/python/qemu/qmp/qmp_client.py @@ -197,8 +197,8 @@ class QMPClient(AsyncProtocol[Message], Events): #: Logger object used for debugging messages. logger = logging.getLogger(__name__) - # Read buffer limit; large enough to accept query-qmp-schema - _limit = (256 * 1024) + # Read buffer limit; 10MB like libvirt default + _limit = 10 * 1024 * 1024 # Type alias for pending execute() result items _PendingT = Union[Message, ExecInterruptedError] @@ -369,7 +369,7 @@ class QMPClient(AsyncProtocol[Message], Events): # This is very likely a server parsing error. # It doesn't inherently belong to any pending execution. # Instead of performing clever recovery, just terminate. - # See "NOTE" in qmp-spec.txt, section 2.4.2 + # See "NOTE" in qmp-spec.rst, section "Error". raise ServerParseError( ("Server sent an error response without an ID, " "but there are no ID-less executions pending. " @@ -377,7 +377,7 @@ class QMPClient(AsyncProtocol[Message], Events): msg ) - # qmp-spec.txt, section 2.4: + # qmp-spec.rst, section "Commands Responses": # 'Clients should drop all the responses # that have an unknown "id" field.' self.logger.log( diff --git a/python/qemu/qmp/qmp_shell.py b/python/qemu/qmp/qmp_shell.py index 619ab42ced..98e684e9e8 100644 --- a/python/qemu/qmp/qmp_shell.py +++ b/python/qemu/qmp/qmp_shell.py @@ -91,14 +91,21 @@ from subprocess import Popen import sys from typing import ( IO, + Dict, Iterator, List, NoReturn, Optional, Sequence, + cast, ) -from qemu.qmp import ConnectError, QMPError, SocketAddrT +from qemu.qmp import ( + ConnectError, + ExecuteError, + QMPError, + SocketAddrT, +) from qemu.qmp.legacy import ( QEMUMonitorProtocol, QMPBadPortError, @@ -194,11 +201,12 @@ class QMPShell(QEMUMonitorProtocol): super().close() def _fill_completion(self) -> None: - cmds = self.cmd('query-commands') - if 'error' in cmds: - return - for cmd in cmds['return']: - self._completer.append(cmd['name']) + try: + cmds = cast(List[Dict[str, str]], self.cmd('query-commands')) + for cmd in cmds: + self._completer.append(cmd['name']) + except ExecuteError: + pass def _completer_setup(self) -> None: self._completer = QMPCompleter() diff --git a/python/qemu/qmp/qmp_tui.py b/python/qemu/qmp/qmp_tui.py index ce239d8979..2d9ebbd20b 100644 --- a/python/qemu/qmp/qmp_tui.py +++ b/python/qemu/qmp/qmp_tui.py @@ -71,7 +71,7 @@ def format_json(msg: str) -> str: due to an decoding error then a simple string manipulation is done to achieve a single line JSON string. - Converting into single line is more asthetically pleasing when looking + Converting into single line is more aesthetically pleasing when looking along with error messages. Eg: @@ -91,7 +91,7 @@ def format_json(msg: str) -> str: [1, true, 3]: QMP message is not a JSON object. - The single line mode is more asthetically pleasing. + The single line mode is more aesthetically pleasing. :param msg: The message to formatted into single line. @@ -346,7 +346,10 @@ class App(QMPClient): self._set_status('[Disconnected]') await self.disconnect() # check if a retry is needed - if self.runstate == Runstate.IDLE: + # mypy 1.4.0 doesn't believe runstate can change after + # disconnect(), hence the cast. + state = cast(Runstate, self.runstate) + if state == Runstate.IDLE: continue await self.runstate_changed() @@ -498,7 +501,7 @@ class EditorWidget(urwid.Filler): class HistoryBox(urwid.ListBox): """ This widget is modelled using the ListBox widget, contains the list of - all messages both QMP messages and log messsages to be shown in the TUI. + all messages both QMP messages and log messages to be shown in the TUI. The messages are urwid.Text widgets. On every append of a message, the focus is shifted to the last appended message. diff --git a/python/qemu/utils/__init__.py b/python/qemu/utils/__init__.py index 9fb273b13d..017cfdcda7 100644 --- a/python/qemu/utils/__init__.py +++ b/python/qemu/utils/__init__.py @@ -79,7 +79,7 @@ def add_visual_margin( :param content: The text to wrap and decorate. :param width: The number of columns to use, including for the decoration - itself. The default (None) uses the the available width of the + itself. The default (None) uses the available width of the current terminal, or a fallback of 72 lines. A negative number subtracts a fixed-width from the default size. The default obeys the COLUMNS environment variable, if set. diff --git a/python/qemu/utils/qemu_ga_client.py b/python/qemu/utils/qemu_ga_client.py index 8c38a7ac9c..9a665e6e99 100644 --- a/python/qemu/utils/qemu_ga_client.py +++ b/python/qemu/utils/qemu_ga_client.py @@ -64,7 +64,7 @@ from qemu.qmp.legacy import QEMUMonitorProtocol class QemuGuestAgent(QEMUMonitorProtocol): def __getattr__(self, name: str) -> Callable[..., Any]: def wrapper(**kwds: object) -> object: - return self.command('guest-' + name.replace('_', '-'), **kwds) + return self.cmd('guest-' + name.replace('_', '-'), **kwds) return wrapper @@ -155,7 +155,7 @@ class QemuGuestAgentClient: def fsfreeze(self, cmd: str) -> object: if cmd not in ['status', 'freeze', 'thaw']: - raise Exception('Invalid command: ' + cmd) + raise ValueError('Invalid command: ' + cmd) # Can be int (freeze, thaw) or GuestFsfreezeStatus (status) return getattr(self.qga, 'fsfreeze' + '_' + cmd)() @@ -167,7 +167,7 @@ class QemuGuestAgentClient: def suspend(self, mode: str) -> None: if mode not in ['disk', 'ram', 'hybrid']: - raise Exception('Invalid mode: ' + mode) + raise ValueError('Invalid mode: ' + mode) try: getattr(self.qga, 'suspend' + '_' + mode)() @@ -178,7 +178,7 @@ class QemuGuestAgentClient: def shutdown(self, mode: str = 'powerdown') -> None: if mode not in ['powerdown', 'halt', 'reboot']: - raise Exception('Invalid mode: ' + mode) + raise ValueError('Invalid mode: ' + mode) try: self.qga.shutdown(mode=mode) diff --git a/python/qemu/utils/qom.py b/python/qemu/utils/qom.py index bcf192f477..426a0f245f 100644 --- a/python/qemu/utils/qom.py +++ b/python/qemu/utils/qom.py @@ -84,7 +84,7 @@ class QOMSet(QOMCommand): self.value = args.value def run(self) -> int: - rsp = self.qmp.command( + rsp = self.qmp.cmd( 'qom-set', path=self.path, property=self.prop, @@ -129,7 +129,7 @@ class QOMGet(QOMCommand): self.prop = tmp[1] def run(self) -> int: - rsp = self.qmp.command( + rsp = self.qmp.cmd( 'qom-get', path=self.path, property=self.prop @@ -231,8 +231,8 @@ class QOMTree(QOMCommand): if item.child: continue try: - rsp = self.qmp.command('qom-get', path=path, - property=item.name) + rsp = self.qmp.cmd('qom-get', path=path, + property=item.name) print(f" {item.name}: {rsp} ({item.type})") except ExecuteError as err: print(f" {item.name}: ({item.type})") diff --git a/python/qemu/utils/qom_common.py b/python/qemu/utils/qom_common.py index 80da1b2304..dd2c8b1908 100644 --- a/python/qemu/utils/qom_common.py +++ b/python/qemu/utils/qom_common.py @@ -140,7 +140,7 @@ class QOMCommand: """ :return: a strongly typed list from the 'qom-list' command. """ - rsp = self.qmp.command('qom-list', path=path) + rsp = self.qmp.cmd('qom-list', path=path) # qom-list returns List[ObjectPropertyInfo] assert isinstance(rsp, list) return [ObjectPropertyInfo.make(x) for x in rsp] diff --git a/python/qemu/utils/qom_fuse.py b/python/qemu/utils/qom_fuse.py index 8dcd59fcde..cf7e344bd5 100644 --- a/python/qemu/utils/qom_fuse.py +++ b/python/qemu/utils/qom_fuse.py @@ -137,7 +137,7 @@ class QOMFuse(QOMCommand, Operations): if path == '': path = '/' try: - data = str(self.qmp.command('qom-get', path=path, property=prop)) + data = str(self.qmp.cmd('qom-get', path=path, property=prop)) data += '\n' # make values shell friendly except ExecuteError as err: raise FuseOSError(EPERM) from err @@ -152,8 +152,8 @@ class QOMFuse(QOMCommand, Operations): return False path, prop = path.rsplit('/', 1) prefix = '/'.join(['..'] * (len(path.split('/')) - 1)) - return prefix + str(self.qmp.command('qom-get', path=path, - property=prop)) + return prefix + str(self.qmp.cmd('qom-get', path=path, + property=prop)) def getattr(self, path: str, fh: Optional[IO[bytes]] = None) -> Mapping[str, object]: diff --git a/python/scripts/mkvenv.py b/python/scripts/mkvenv.py new file mode 100644 index 0000000000..d0b9c215ca --- /dev/null +++ b/python/scripts/mkvenv.py @@ -0,0 +1,988 @@ +""" +mkvenv - QEMU pyvenv bootstrapping utility + +usage: mkvenv [-h] command ... + +QEMU pyvenv bootstrapping utility + +options: + -h, --help show this help message and exit + +Commands: + command Description + create create a venv + post_init + post-venv initialization + ensure Ensure that the specified package is installed. + ensuregroup + Ensure that the specified package group is installed. + +-------------------------------------------------- + +usage: mkvenv create [-h] target + +positional arguments: + target Target directory to install virtual environment into. + +options: + -h, --help show this help message and exit + +-------------------------------------------------- + +usage: mkvenv post_init [-h] + +options: + -h, --help show this help message and exit + +-------------------------------------------------- + +usage: mkvenv ensure [-h] [--online] [--dir DIR] dep_spec... + +positional arguments: + dep_spec PEP 508 Dependency specification, e.g. 'meson>=0.61.5' + +options: + -h, --help show this help message and exit + --online Install packages from PyPI, if necessary. + --dir DIR Path to vendored packages where we may install from. + +-------------------------------------------------- + +usage: mkvenv ensuregroup [-h] [--online] [--dir DIR] file group... + +positional arguments: + file pointer to a TOML file + group section name in the TOML file + +options: + -h, --help show this help message and exit + --online Install packages from PyPI, if necessary. + --dir DIR Path to vendored packages where we may install from. + +""" + +# Copyright (C) 2022-2023 Red Hat, Inc. +# +# Authors: +# John Snow +# Paolo Bonzini +# +# 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 argparse +from importlib.metadata import ( + Distribution, + EntryPoint, + PackageNotFoundError, + distribution, + version, +) +from importlib.util import find_spec +import logging +import os +from pathlib import Path +import re +import shutil +import site +import subprocess +import sys +import sysconfig +from types import SimpleNamespace +from typing import ( + Any, + Dict, + Iterator, + Optional, + Sequence, + Tuple, + Union, +) +import venv + + +# Try to load distlib, with a fallback to pip's vendored version. +# HAVE_DISTLIB is checked below, just-in-time, so that mkvenv does not fail +# outside the venv or before a potential call to ensurepip in checkpip(). +HAVE_DISTLIB = True +try: + import distlib.scripts + import distlib.version +except ImportError: + try: + # Reach into pip's cookie jar. pylint and flake8 don't understand + # that these imports will be used via distlib.xxx. + from pip._vendor import distlib + import pip._vendor.distlib.scripts # noqa, pylint: disable=unused-import + import pip._vendor.distlib.version # noqa, pylint: disable=unused-import + except ImportError: + HAVE_DISTLIB = False + +# Try to load tomllib, with a fallback to tomli. +# HAVE_TOMLLIB is checked below, just-in-time, so that mkvenv does not fail +# outside the venv or before a potential call to ensurepip in checkpip(). +HAVE_TOMLLIB = True +try: + import tomllib +except ImportError: + try: + import tomli as tomllib + except ImportError: + HAVE_TOMLLIB = False + +# Do not add any mandatory dependencies from outside the stdlib: +# This script *must* be usable standalone! + +DirType = Union[str, bytes, "os.PathLike[str]", "os.PathLike[bytes]"] +logger = logging.getLogger("mkvenv") + + +def inside_a_venv() -> bool: + """Returns True if it is executed inside of a virtual environment.""" + return sys.prefix != sys.base_prefix + + +class Ouch(RuntimeError): + """An Exception class we can't confuse with a builtin.""" + + +class QemuEnvBuilder(venv.EnvBuilder): + """ + An extension of venv.EnvBuilder for building QEMU's configure-time venv. + + The primary difference is that it emulates a "nested" virtual + environment when invoked from inside of an existing virtual + environment by including packages from the parent. Also, + "ensurepip" is replaced if possible with just recreating pip's + console_scripts inside the virtual environment. + + Parameters for base class init: + - system_site_packages: bool = False + - clear: bool = False + - symlinks: bool = False + - upgrade: bool = False + - with_pip: bool = False + - prompt: Optional[str] = None + - upgrade_deps: bool = False (Since 3.9) + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + logger.debug("QemuEnvBuilder.__init__(...)") + + # For nested venv emulation: + self.use_parent_packages = False + if inside_a_venv(): + # Include parent packages only if we're in a venv and + # system_site_packages was True. + self.use_parent_packages = kwargs.pop( + "system_site_packages", False + ) + # Include system_site_packages only when the parent, + # The venv we are currently in, also does so. + kwargs["system_site_packages"] = sys.base_prefix in site.PREFIXES + + # ensurepip is slow: venv creation can be very fast for cases where + # we allow the use of system_site_packages. Therefore, ensurepip is + # replaced with our own script generation once the virtual environment + # is setup. + self.want_pip = kwargs.get("with_pip", False) + if self.want_pip: + if ( + kwargs.get("system_site_packages", False) + and not need_ensurepip() + ): + kwargs["with_pip"] = False + else: + check_ensurepip() + + super().__init__(*args, **kwargs) + + # Make the context available post-creation: + self._context: Optional[SimpleNamespace] = None + + def get_parent_libpath(self) -> Optional[str]: + """Return the libpath of the parent venv, if applicable.""" + if self.use_parent_packages: + return sysconfig.get_path("purelib") + return None + + @staticmethod + def compute_venv_libpath(context: SimpleNamespace) -> str: + """ + Compatibility wrapper for context.lib_path for Python < 3.12 + """ + # Python 3.12+, not strictly necessary because it's documented + # to be the same as 3.10 code below: + if sys.version_info >= (3, 12): + return context.lib_path + + # Python 3.10+ + if "venv" in sysconfig.get_scheme_names(): + lib_path = sysconfig.get_path( + "purelib", scheme="venv", vars={"base": context.env_dir} + ) + assert lib_path is not None + return lib_path + + # For Python <= 3.9 we need to hardcode this. Fortunately the + # code below was the same in Python 3.6-3.10, so there is only + # one case. + if sys.platform == "win32": + return os.path.join(context.env_dir, "Lib", "site-packages") + return os.path.join( + context.env_dir, + "lib", + "python%d.%d" % sys.version_info[:2], + "site-packages", + ) + + def ensure_directories(self, env_dir: DirType) -> SimpleNamespace: + logger.debug("ensure_directories(env_dir=%s)", env_dir) + self._context = super().ensure_directories(env_dir) + return self._context + + def create(self, env_dir: DirType) -> None: + logger.debug("create(env_dir=%s)", env_dir) + super().create(env_dir) + assert self._context is not None + self.post_post_setup(self._context) + + def post_post_setup(self, context: SimpleNamespace) -> None: + """ + The final, final hook. Enter the venv and run commands inside of it. + """ + if self.use_parent_packages: + # We're inside of a venv and we want to include the parent + # venv's packages. + parent_libpath = self.get_parent_libpath() + assert parent_libpath is not None + logger.debug("parent_libpath: %s", parent_libpath) + + our_libpath = self.compute_venv_libpath(context) + logger.debug("our_libpath: %s", our_libpath) + + pth_file = os.path.join(our_libpath, "nested.pth") + with open(pth_file, "w", encoding="UTF-8") as file: + file.write(parent_libpath + os.linesep) + + if self.want_pip: + args = [ + context.env_exe, + __file__, + "post_init", + ] + subprocess.run(args, check=True) + + def get_value(self, field: str) -> str: + """ + Get a string value from the context namespace after a call to build. + + For valid field names, see: + https://docs.python.org/3/library/venv.html#venv.EnvBuilder.ensure_directories + """ + ret = getattr(self._context, field) + assert isinstance(ret, str) + return ret + + +def need_ensurepip() -> bool: + """ + Tests for the presence of setuptools and pip. + + :return: `True` if we do not detect both packages. + """ + # Don't try to actually import them, it's fraught with danger: + # https://github.com/pypa/setuptools/issues/2993 + if find_spec("setuptools") and find_spec("pip"): + return False + return True + + +def check_ensurepip() -> None: + """ + Check that we have ensurepip. + + Raise a fatal exception with a helpful hint if it isn't available. + """ + if not find_spec("ensurepip"): + msg = ( + "Python's ensurepip module is not found.\n" + "It's normally part of the Python standard library, " + "maybe your distribution packages it separately?\n" + "Either install ensurepip, or alleviate the need for it in the " + "first place by installing pip and setuptools for " + f"'{sys.executable}'.\n" + "(Hint: Debian puts ensurepip in its python3-venv package.)" + ) + raise Ouch(msg) + + # ensurepip uses pyexpat, which can also go missing on us: + if not find_spec("pyexpat"): + msg = ( + "Python's pyexpat module is not found.\n" + "It's normally part of the Python standard library, " + "maybe your distribution packages it separately?\n" + "Either install pyexpat, or alleviate the need for it in the " + "first place by installing pip and setuptools for " + f"'{sys.executable}'.\n\n" + "(Hint: NetBSD's pkgsrc debundles this to e.g. 'py310-expat'.)" + ) + raise Ouch(msg) + + +def make_venv( # pylint: disable=too-many-arguments + env_dir: Union[str, Path], + system_site_packages: bool = False, + clear: bool = True, + symlinks: Optional[bool] = None, + with_pip: bool = True, +) -> None: + """ + Create a venv using `QemuEnvBuilder`. + + This is analogous to the `venv.create` module-level convenience + function that is part of the Python stdblib, except it uses + `QemuEnvBuilder` instead. + + :param env_dir: The directory to create/install to. + :param system_site_packages: + Allow inheriting packages from the system installation. + :param clear: When True, fully remove any prior venv and files. + :param symlinks: + Whether to use symlinks to the target interpreter or not. If + left unspecified, it will use symlinks except on Windows to + match behavior with the "venv" CLI tool. + :param with_pip: + Whether to install "pip" binaries or not. + """ + logger.debug( + "%s: make_venv(env_dir=%s, system_site_packages=%s, " + "clear=%s, symlinks=%s, with_pip=%s)", + __file__, + str(env_dir), + system_site_packages, + clear, + symlinks, + with_pip, + ) + + if symlinks is None: + # Default behavior of standard venv CLI + symlinks = os.name != "nt" + + builder = QemuEnvBuilder( + system_site_packages=system_site_packages, + clear=clear, + symlinks=symlinks, + with_pip=with_pip, + ) + + style = "non-isolated" if builder.system_site_packages else "isolated" + nested = "" + if builder.use_parent_packages: + nested = f"(with packages from '{builder.get_parent_libpath()}') " + print( + f"mkvenv: Creating {style} virtual environment" + f" {nested}at '{str(env_dir)}'", + file=sys.stderr, + ) + + try: + logger.debug("Invoking builder.create()") + try: + builder.create(str(env_dir)) + except SystemExit as exc: + # Some versions of the venv module raise SystemExit; *nasty*! + # We want the exception that prompted it. It might be a subprocess + # error that has output we *really* want to see. + logger.debug("Intercepted SystemExit from EnvBuilder.create()") + raise exc.__cause__ or exc.__context__ or exc + logger.debug("builder.create() finished") + except subprocess.CalledProcessError as exc: + logger.error("mkvenv subprocess failed:") + logger.error("cmd: %s", exc.cmd) + logger.error("returncode: %d", exc.returncode) + + def _stringify(data: Union[str, bytes]) -> str: + if isinstance(data, bytes): + return data.decode() + return data + + lines = [] + if exc.stdout: + lines.append("========== stdout ==========") + lines.append(_stringify(exc.stdout)) + lines.append("============================") + if exc.stderr: + lines.append("========== stderr ==========") + lines.append(_stringify(exc.stderr)) + lines.append("============================") + if lines: + logger.error(os.linesep.join(lines)) + + raise Ouch("VENV creation subprocess failed.") from exc + + # print the python executable to stdout for configure. + print(builder.get_value("env_exe")) + + +def _get_entry_points(packages: Sequence[str]) -> Iterator[str]: + + def _generator() -> Iterator[str]: + for package in packages: + try: + entry_points: Iterator[EntryPoint] = \ + iter(distribution(package).entry_points) + except PackageNotFoundError: + continue + + # The EntryPoints type is only available in 3.10+, + # treat this as a vanilla list and filter it ourselves. + entry_points = filter( + lambda ep: ep.group == "console_scripts", entry_points + ) + + for entry_point in entry_points: + yield f"{entry_point.name} = {entry_point.value}" + + return _generator() + + +def generate_console_scripts( + packages: Sequence[str], + python_path: Optional[str] = None, + bin_path: Optional[str] = None, +) -> None: + """ + Generate script shims for console_script entry points in @packages. + """ + if python_path is None: + python_path = sys.executable + if bin_path is None: + bin_path = sysconfig.get_path("scripts") + assert bin_path is not None + + logger.debug( + "generate_console_scripts(packages=%s, python_path=%s, bin_path=%s)", + packages, + python_path, + bin_path, + ) + + if not packages: + return + + maker = distlib.scripts.ScriptMaker(None, bin_path) + maker.variants = {""} + maker.clobber = False + + for entry_point in _get_entry_points(packages): + for filename in maker.make(entry_point): + logger.debug("wrote console_script '%s'", filename) + + +def pkgname_from_depspec(dep_spec: str) -> str: + """ + Parse package name out of a PEP-508 depspec. + + See https://peps.python.org/pep-0508/#names + """ + match = re.match( + r"^([A-Z0-9]([A-Z0-9._-]*[A-Z0-9])?)", dep_spec, re.IGNORECASE + ) + if not match: + raise ValueError( + f"dep_spec '{dep_spec}'" + " does not appear to contain a valid package name" + ) + return match.group(0) + + +def _path_is_prefix(prefix: Optional[str], path: str) -> bool: + try: + return ( + prefix is not None and os.path.commonpath([prefix, path]) == prefix + ) + except ValueError: + return False + + +def _is_system_package(dist: Distribution) -> bool: + path = str(dist.locate_file(".")) + return not ( + _path_is_prefix(sysconfig.get_path("purelib"), path) + or _path_is_prefix(sysconfig.get_path("platlib"), path) + ) + + +def diagnose( + dep_spec: str, + online: bool, + wheels_dir: Optional[Union[str, Path]], + prog: Optional[str], +) -> Tuple[str, bool]: + """ + Offer a summary to the user as to why a package failed to be installed. + + :param dep_spec: The package we tried to ensure, e.g. 'meson>=0.61.5' + :param online: Did we allow PyPI access? + :param prog: + Optionally, a shell program name that can be used as a + bellwether to detect if this program is installed elsewhere on + the system. This is used to offer advice when a program is + detected for a different python version. + :param wheels_dir: + Optionally, a directory that was searched for vendored packages. + """ + # pylint: disable=too-many-branches + + # Some errors are not particularly serious + bad = False + + pkg_name = pkgname_from_depspec(dep_spec) + pkg_version: Optional[str] = None + try: + pkg_version = version(pkg_name) + except PackageNotFoundError: + pass + + lines = [] + + if pkg_version: + lines.append( + f"Python package '{pkg_name}' version '{pkg_version}' was found," + " but isn't suitable." + ) + else: + lines.append( + f"Python package '{pkg_name}' was not found nor installed." + ) + + if wheels_dir: + lines.append( + "No suitable version found in, or failed to install from" + f" '{wheels_dir}'." + ) + bad = True + + if online: + lines.append("A suitable version could not be obtained from PyPI.") + bad = True + else: + lines.append( + "mkvenv was configured to operate offline and did not check PyPI." + ) + + if prog and not pkg_version: + which = shutil.which(prog) + if which: + if sys.base_prefix in site.PREFIXES: + pypath = Path(sys.executable).resolve() + lines.append( + f"'{prog}' was detected on your system at '{which}', " + f"but the Python package '{pkg_name}' was not found by " + f"this Python interpreter ('{pypath}'). " + f"Typically this means that '{prog}' has been installed " + "against a different Python interpreter on your system." + ) + else: + lines.append( + f"'{prog}' was detected on your system at '{which}', " + "but the build is using an isolated virtual environment." + ) + bad = True + + lines = [f" • {line}" for line in lines] + if bad: + lines.insert(0, f"Could not provide build dependency '{dep_spec}':") + else: + lines.insert(0, f"'{dep_spec}' not found:") + return os.linesep.join(lines), bad + + +def pip_install( + args: Sequence[str], + online: bool = False, + wheels_dir: Optional[Union[str, Path]] = None, +) -> None: + """ + Use pip to install a package or package(s) as specified in @args. + """ + loud = bool( + os.environ.get("DEBUG") + or os.environ.get("GITLAB_CI") + or os.environ.get("V") + ) + + full_args = [ + sys.executable, + "-m", + "pip", + "install", + "--disable-pip-version-check", + "-v" if loud else "-q", + ] + if not online: + full_args += ["--no-index"] + if wheels_dir: + full_args += ["--find-links", f"file://{str(wheels_dir)}"] + full_args += list(args) + subprocess.run( + full_args, + check=True, + ) + + +def _make_version_constraint(info: Dict[str, str], install: bool) -> str: + """ + Construct the version constraint part of a PEP 508 dependency + specification (for example '>=0.61.5') from the accepted and + installed keys of the provided dictionary. + + :param info: A dictionary corresponding to a TOML key-value list. + :param install: True generates install constraints, False generates + presence constraints + """ + if install and "installed" in info: + return "==" + info["installed"] + + dep_spec = info.get("accepted", "") + dep_spec = dep_spec.strip() + # Double check that they didn't just use a version number + if dep_spec and dep_spec[0] not in "!~><=(": + raise Ouch( + "invalid dependency specifier " + dep_spec + " in dependency file" + ) + + return dep_spec + + +def _do_ensure( + group: Dict[str, Dict[str, str]], + online: bool = False, + wheels_dir: Optional[Union[str, Path]] = None, +) -> Optional[Tuple[str, bool]]: + """ + Use pip to ensure we have the packages specified in @group. + + If the packages are already installed, do nothing. If online and + wheels_dir are both provided, prefer packages found in wheels_dir + first before connecting to PyPI. + + :param group: A dictionary of dictionaries, corresponding to a + section in a pythondeps.toml file. + :param online: If True, fall back to PyPI. + :param wheels_dir: If specified, search this path for packages. + """ + absent = [] + present = [] + canary = None + for name, info in group.items(): + constraint = _make_version_constraint(info, False) + matcher = distlib.version.LegacyMatcher(name + constraint) + print(f"mkvenv: checking for {matcher}", file=sys.stderr) + + dist: Optional[Distribution] = None + try: + dist = distribution(matcher.name) + except PackageNotFoundError: + pass + + if ( + dist is None + # Always pass installed package to pip, so that they can be + # updated if the requested version changes + or not _is_system_package(dist) + or not matcher.match(distlib.version.LegacyVersion(dist.version)) + ): + absent.append(name + _make_version_constraint(info, True)) + if len(absent) == 1: + canary = info.get("canary", None) + else: + logger.info("found %s %s", name, dist.version) + present.append(name) + + if present: + generate_console_scripts(present) + + if absent: + if online or wheels_dir: + # Some packages are missing or aren't a suitable version, + # install a suitable (possibly vendored) package. + print(f"mkvenv: installing {', '.join(absent)}", file=sys.stderr) + try: + pip_install(args=absent, online=online, wheels_dir=wheels_dir) + return None + except subprocess.CalledProcessError: + pass + + return diagnose( + absent[0], + online, + wheels_dir, + canary, + ) + + return None + + +def ensure( + dep_specs: Sequence[str], + online: bool = False, + wheels_dir: Optional[Union[str, Path]] = None, + prog: Optional[str] = None, +) -> None: + """ + Use pip to ensure we have the package specified by @dep_specs. + + If the package is already installed, do nothing. If online and + wheels_dir are both provided, prefer packages found in wheels_dir + first before connecting to PyPI. + + :param dep_specs: + PEP 508 dependency specifications. e.g. ['meson>=0.61.5']. + :param online: If True, fall back to PyPI. + :param wheels_dir: If specified, search this path for packages. + :param prog: + If specified, use this program name for error diagnostics that will + be presented to the user. e.g., 'sphinx-build' can be used as a + bellwether for the presence of 'sphinx'. + """ + + if not HAVE_DISTLIB: + raise Ouch("a usable distlib could not be found, please install it") + + # Convert the depspecs to a dictionary, as if they came + # from a section in a pythondeps.toml file + group: Dict[str, Dict[str, str]] = {} + for spec in dep_specs: + name = distlib.version.LegacyMatcher(spec).name + group[name] = {} + + spec = spec.strip() + pos = len(name) + ver = spec[pos:].strip() + if ver: + group[name]["accepted"] = ver + + if prog: + group[name]["canary"] = prog + prog = None + + result = _do_ensure(group, online, wheels_dir) + if result: + # Well, that's not good. + if result[1]: + raise Ouch(result[0]) + raise SystemExit(f"\n{result[0]}\n\n") + + +def _parse_groups(file: str) -> Dict[str, Dict[str, Any]]: + if not HAVE_TOMLLIB: + if sys.version_info < (3, 11): + raise Ouch("found no usable tomli, please install it") + + raise Ouch( + "Python >=3.11 does not have tomllib... what have you done!?" + ) + + # Use loads() to support both tomli v1.2.x (Ubuntu 22.04, + # Debian bullseye-backports) and v2.0.x + with open(file, "r", encoding="ascii") as depfile: + contents = depfile.read() + return tomllib.loads(contents) # type: ignore + + +def ensure_group( + file: str, + groups: Sequence[str], + online: bool = False, + wheels_dir: Optional[Union[str, Path]] = None, +) -> None: + """ + Use pip to ensure we have the package specified by @dep_specs. + + If the package is already installed, do nothing. If online and + wheels_dir are both provided, prefer packages found in wheels_dir + first before connecting to PyPI. + + :param dep_specs: + PEP 508 dependency specifications. e.g. ['meson>=0.61.5']. + :param online: If True, fall back to PyPI. + :param wheels_dir: If specified, search this path for packages. + """ + + if not HAVE_DISTLIB: + raise Ouch("found no usable distlib, please install it") + + parsed_deps = _parse_groups(file) + + to_install: Dict[str, Dict[str, str]] = {} + for group in groups: + try: + to_install.update(parsed_deps[group]) + except KeyError as exc: + raise Ouch(f"group {group} not defined") from exc + + result = _do_ensure(to_install, online, wheels_dir) + if result: + # Well, that's not good. + if result[1]: + raise Ouch(result[0]) + raise SystemExit(f"\n{result[0]}\n\n") + + +def post_venv_setup() -> None: + """ + This is intended to be run *inside the venv* after it is created. + """ + logger.debug("post_venv_setup()") + # Generate a 'pip' script so the venv is usable in a normal + # way from the CLI. This only happens when we inherited pip from a + # parent/system-site and haven't run ensurepip in some way. + generate_console_scripts(["pip"]) + + +def _add_create_subcommand(subparsers: Any) -> None: + subparser = subparsers.add_parser("create", help="create a venv") + subparser.add_argument( + "target", + type=str, + action="store", + help="Target directory to install virtual environment into.", + ) + + +def _add_post_init_subcommand(subparsers: Any) -> None: + subparsers.add_parser("post_init", help="post-venv initialization") + + +def _add_ensuregroup_subcommand(subparsers: Any) -> None: + subparser = subparsers.add_parser( + "ensuregroup", + help="Ensure that the specified package group is installed.", + ) + subparser.add_argument( + "--online", + action="store_true", + help="Install packages from PyPI, if necessary.", + ) + subparser.add_argument( + "--dir", + type=str, + action="store", + help="Path to vendored packages where we may install from.", + ) + subparser.add_argument( + "file", + type=str, + action="store", + help=("Path to a TOML file describing package groups"), + ) + subparser.add_argument( + "group", + type=str, + action="store", + help="One or more package group names", + nargs="+", + ) + + +def _add_ensure_subcommand(subparsers: Any) -> None: + subparser = subparsers.add_parser( + "ensure", help="Ensure that the specified package is installed." + ) + subparser.add_argument( + "--online", + action="store_true", + help="Install packages from PyPI, if necessary.", + ) + subparser.add_argument( + "--dir", + type=str, + action="store", + help="Path to vendored packages where we may install from.", + ) + subparser.add_argument( + "--diagnose", + type=str, + action="store", + help=( + "Name of a shell utility to use for " + "diagnostics if this command fails." + ), + ) + subparser.add_argument( + "dep_specs", + type=str, + action="store", + help="PEP 508 Dependency specification, e.g. 'meson>=0.61.5'", + nargs="+", + ) + + +def main() -> int: + """CLI interface to make_qemu_venv. See module docstring.""" + if os.environ.get("DEBUG") or os.environ.get("GITLAB_CI"): + # You're welcome. + logging.basicConfig(level=logging.DEBUG) + else: + if os.environ.get("V"): + logging.basicConfig(level=logging.INFO) + + parser = argparse.ArgumentParser( + prog="mkvenv", + description="QEMU pyvenv bootstrapping utility", + ) + subparsers = parser.add_subparsers( + title="Commands", + dest="command", + required=True, + metavar="command", + help="Description", + ) + + _add_create_subcommand(subparsers) + _add_post_init_subcommand(subparsers) + _add_ensure_subcommand(subparsers) + _add_ensuregroup_subcommand(subparsers) + + args = parser.parse_args() + try: + if args.command == "create": + make_venv( + args.target, + system_site_packages=True, + clear=True, + ) + if args.command == "post_init": + post_venv_setup() + if args.command == "ensure": + ensure( + dep_specs=args.dep_specs, + online=args.online, + wheels_dir=args.dir, + prog=args.diagnose, + ) + if args.command == "ensuregroup": + ensure_group( + file=args.file, + groups=args.group, + online=args.online, + wheels_dir=args.dir, + ) + logger.debug("mkvenv.py %s: exiting", args.command) + except Ouch as exc: + print("\n*** Ouch! ***\n", file=sys.stderr) + print(str(exc), "\n\n", file=sys.stderr) + return 1 + except SystemExit: + raise + except: # pylint: disable=bare-except + logger.exception("mkvenv did not complete successfully:") + return 2 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/python/scripts/vendor.py b/python/scripts/vendor.py new file mode 100755 index 0000000000..1038b14ae0 --- /dev/null +++ b/python/scripts/vendor.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +""" +vendor - QEMU python vendoring utility + +usage: vendor [-h] + +QEMU python vendoring utility + +options: + -h, --help show this help message and exit +""" + +# Copyright (C) 2023 Red Hat, Inc. +# +# Authors: +# John Snow +# +# 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 argparse +import os +from pathlib import Path +import subprocess +import sys +import tempfile + + +def main() -> int: + """Run the vendoring utility. See module-level docstring.""" + loud = False + if os.environ.get("DEBUG") or os.environ.get("V"): + loud = True + + # No options or anything for now, but I guess + # you'll figure that out when you run --help. + parser = argparse.ArgumentParser( + prog="vendor", + description="QEMU python vendoring utility", + ) + parser.parse_args() + + packages = { + "meson==1.2.3": + "4533a43c34548edd1f63a276a42690fce15bde9409bcf20c4b8fa3d7e4d7cac1", + + "tomli==2.0.1": + "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + } + + vendor_dir = Path(__file__, "..", "..", "wheels").resolve() + + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as file: + for dep_spec, checksum in packages.items(): + print(f"{dep_spec} --hash=sha256:{checksum}", file=file) + file.flush() + + cli_args = [ + "pip", + "download", + "--dest", + str(vendor_dir), + "--require-hashes", + "-r", + file.name, + ] + if loud: + cli_args.append("-v") + + print(" ".join(cli_args)) + subprocess.run(cli_args, check=True) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/python/setup.cfg b/python/setup.cfg index c2c61c7519..48668609d3 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -14,15 +14,15 @@ classifiers = Natural Language :: English Operating System :: OS Independent Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 Typing :: Typed [options] -python_requires = >= 3.6 +python_requires = >= 3.8 packages = qemu.qmp qemu.machine @@ -32,16 +32,15 @@ packages = * = py.typed [options.extras_require] -# For the devel group, When adding new dependencies or bumping the minimum -# version, use e.g. "pipenv install --dev pylint==3.0.0". -# Subsequently, edit 'Pipfile' to remove e.g. 'pylint = "==3.0.0'. +# Remember to update tests/minreqs.txt if changing anything below: devel = avocado-framework >= 90.0 - flake8 >= 3.6.0 + distlib >= 0.3.6 + flake8 >= 5.0.4 fusepy >= 2.0.4 isort >= 5.1.2 - mypy >= 0.780 - pylint >= 2.8.0 + mypy >= 1.4.0 + pylint >= 2.17.3 tox >= 3.18.0 urwid >= 2.1.2 urwid-readline >= 0.13 @@ -71,12 +70,13 @@ console_scripts = qmp-tui = qemu.qmp.qmp_tui:main [tui] [flake8] -extend-ignore = E722 # Prefer pylint's bare-except checks to flake8's +# Prefer pylint's bare-except checks to flake8's +extend-ignore = E722 exclude = __pycache__, [mypy] strict = True -python_version = 3.6 +python_version = 3.8 warn_unused_configs = True namespace_packages = True warn_unused_ignores = False @@ -94,6 +94,12 @@ allow_subclassing_any = True [mypy-fuse] ignore_missing_imports = True +[mypy-tomli] +ignore_missing_imports = True + +[mypy-tomllib] +ignore_missing_imports = True + [mypy-urwid] ignore_missing_imports = True @@ -103,6 +109,24 @@ ignore_missing_imports = True [mypy-pygments] ignore_missing_imports = True +[mypy-distlib] +ignore_missing_imports = True + +[mypy-distlib.scripts] +ignore_missing_imports = True + +[mypy-distlib.version] +ignore_missing_imports = True + +[mypy-pip._vendor.distlib] +ignore_missing_imports = True + +[mypy-pip._vendor.distlib.scripts] +ignore_missing_imports = True + +[mypy-pip._vendor.distlib.version] +ignore_missing_imports = True + [pylint.messages control] # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this @@ -132,6 +156,7 @@ good-names=i, fd, # fd = os.open(...) c, # for c in string: ... T, # for TypeVars. See pylint#3401 + SocketAddrT, # Not sure why this is invalid. [pylint.similarities] # Ignore imports when computing similarities. @@ -158,7 +183,7 @@ multi_line_output=3 # of python available on your system to run this test. [tox:tox] -envlist = py36, py37, py38, py39, py310 +envlist = py38, py39, py310, py311, py312 skip_missing_interpreters = true [testenv] diff --git a/python/tests/flake8.sh b/python/tests/flake8.sh index 1cd7d40fad..e013699645 100755 --- a/python/tests/flake8.sh +++ b/python/tests/flake8.sh @@ -1,2 +1,3 @@ #!/bin/sh -e python3 -m flake8 qemu/ +python3 -m flake8 scripts/ diff --git a/python/tests/isort.sh b/python/tests/isort.sh index 4480405bfb..66c2f7df0f 100755 --- a/python/tests/isort.sh +++ b/python/tests/isort.sh @@ -1,2 +1,3 @@ #!/bin/sh -e python3 -m isort -c qemu/ +python3 -m isort -c scripts/ diff --git a/python/tests/minreqs.txt b/python/tests/minreqs.txt new file mode 100644 index 0000000000..a3f423efd8 --- /dev/null +++ b/python/tests/minreqs.txt @@ -0,0 +1,47 @@ +# This file lists the ***oldest possible dependencies*** needed to run +# "make check" successfully under ***Python 3.8***. It is used primarily +# by GitLab CI to ensure that our stated minimum versions in setup.cfg +# are truthful and regularly validated. +# +# This file should not contain any dependencies that are not expressed +# by the [devel] section of setup.cfg, except for transitive +# dependencies which must be enumerated here explicitly to eliminate +# dependency resolution ambiguity. +# +# When adding new dependencies, pin the very oldest non-yanked version +# on PyPI that allows the test suite to pass. + +# Dependencies for the TUI addon (Required for successful linting) +urwid==2.1.2 +urwid-readline==0.13 +Pygments==2.9.0 + +# Dependencies for mkvenv +distlib==0.3.6 + +# Dependencies for FUSE support for qom-fuse +fusepy==2.0.4 + +# Test-runners, utilities, etc. +avocado-framework==90.0 + +# Linters +flake8==5.0.4 +isort==5.1.2 +mypy==1.4.0 +pylint==2.17.3 + +# Transitive flake8 dependencies +mccabe==0.7.0 +pycodestyle==2.9.1 +pyflakes==2.5.0 + +# Transitive mypy dependencies +mypy-extensions==1.0.0 +typing-extensions==4.7.1 + +# Transitive pylint dependencies +astroid==2.15.4 +lazy-object-proxy==1.4.0 +toml==0.10.0 +wrapt==1.14.0 diff --git a/python/tests/mypy.sh b/python/tests/mypy.sh index 5f980f563b..a33a3f58ab 100755 --- a/python/tests/mypy.sh +++ b/python/tests/mypy.sh @@ -1,2 +1,3 @@ #!/bin/sh -e python3 -m mypy -p qemu +python3 -m mypy scripts/ diff --git a/python/tests/pylint.sh b/python/tests/pylint.sh index 03d64705a1..2b68da90df 100755 --- a/python/tests/pylint.sh +++ b/python/tests/pylint.sh @@ -1,3 +1,4 @@ #!/bin/sh -e # See commit message for environment variable explainer. SETUPTOOLS_USE_DISTUTILS=stdlib python3 -m pylint qemu/ +SETUPTOOLS_USE_DISTUTILS=stdlib python3 -m pylint scripts/ diff --git a/python/wheels/meson-1.2.3-py3-none-any.whl b/python/wheels/meson-1.2.3-py3-none-any.whl new file mode 100644 index 0000000000..a8b84e5f11 Binary files /dev/null and b/python/wheels/meson-1.2.3-py3-none-any.whl differ diff --git a/python/wheels/tomli-2.0.1-py3-none-any.whl b/python/wheels/tomli-2.0.1-py3-none-any.whl new file mode 100644 index 0000000000..29670b98d1 Binary files /dev/null and b/python/wheels/tomli-2.0.1-py3-none-any.whl differ diff --git a/pythondeps.toml b/pythondeps.toml new file mode 100644 index 0000000000..0e88415999 --- /dev/null +++ b/pythondeps.toml @@ -0,0 +1,33 @@ +# This file describes Python package requirements to be +# installed in the pyvenv Python virtual environment. +# +# Packages are placed in groups, which are installed using +# the ensuregroup subcommand of python/scripts/mkvenv.py. +# Each group forms a TOML section and each entry in the +# section is a TOML key-value list describing a package. +# All fields are optional; valid fields are: +# +# - accepted: accepted versions when using a system package +# - installed: fixed version to install in the virtual environment +# if a system package is not found; if not specified, +# defaults to the same as "accepted" or, if also missing, +# to the newest version available on PyPI. +# - canary: if specified, use this program name to present more +# precise error diagnostics to the user. For example, +# 'sphinx-build' can be used as a bellwether for the +# presence of 'sphinx' in the system. + +[meson] +# The install key should match the version in python/wheels/ +meson = { accepted = ">=0.63.0", installed = "1.2.3", canary = "meson" } + +[docs] +sphinx = { accepted = ">=1.6", installed = "5.3.0", canary = "sphinx-build" } +sphinx_rtd_theme = { accepted = ">=0.5", installed = "1.1.1" } + +[avocado] +# Note that qemu.git/python/ is always implicitly installed. +# Prefer an LTS version when updating the accepted versions of +# avocado-framework, for example right now the limit is 92.x. +avocado-framework = { accepted = "(>=88.1, <93.0)", installed = "88.1", canary = "avocado" } +pycdlib = { accepted = ">=1.11.0" } diff --git a/qapi/acpi.json b/qapi/acpi.json index d148f6db9f..aa4dbe5794 100644 --- a/qapi/acpi.json +++ b/qapi/acpi.json @@ -14,18 +14,19 @@ # # Specify an ACPI table on the command line to load. # -# At most one of @file and @data can be specified. The list of files specified -# by any one of them is loaded and concatenated in order. If both are omitted, -# @data is implied. +# At most one of @file and @data can be specified. The list of files +# specified by any one of them is loaded and concatenated in order. +# If both are omitted, @data is implied. # -# Other fields / optargs can be used to override fields of the generic ACPI -# table header; refer to the ACPI specification 5.0, section 5.2.6 System -# Description Table Header. If a header field is not overridden, then the -# corresponding value from the concatenated blob is used (in case of @file), or -# it is filled in with a hard-coded value (in case of @data). +# Other fields / optargs can be used to override fields of the generic +# ACPI table header; refer to the ACPI specification 5.0, section +# 5.2.6 System Description Table Header. If a header field is not +# overridden, then the corresponding value from the concatenated blob +# is used (in case of @file), or it is filled in with a hard-coded +# value (in case of @data). # -# String fields are copied into the matching ACPI member from lowest address -# upwards, and silently truncated / NUL-padded to length. +# String fields are copied into the matching ACPI member from lowest +# address upwards, and silently truncated / NUL-padded to length. # # @sig: table signature / identifier (4 bytes) # @@ -38,20 +39,20 @@ # @oem_rev: OEM-supplied revision number (4 bytes) # # @asl_compiler_id: identifier of the utility that created the table -# (4 bytes) +# (4 bytes) # # @asl_compiler_rev: revision number of the utility that created the -# table (4 bytes) +# table (4 bytes) # -# @file: colon (:) separated list of pathnames to load and -# concatenate as table data. The resultant binary blob is expected to -# have an ACPI table header. At least one file is required. This field -# excludes @data. +# @file: colon (:) separated list of pathnames to load and concatenate +# as table data. The resultant binary blob is expected to have an +# ACPI table header. At least one file is required. This field +# excludes @data. # -# @data: colon (:) separated list of pathnames to load and -# concatenate as table data. The resultant binary blob must not have an -# ACPI table header. At least one file is required. This field excludes -# @file. +# @data: colon (:) separated list of pathnames to load and concatenate +# as table data. The resultant binary blob must not have an ACPI +# table header. At least one file is required. This field +# excludes @file. # # Since: 1.5 ## @@ -71,6 +72,7 @@ # @ACPISlotType: # # @DIMM: memory slot +# # @CPU: logical CPU slot (since 2.7) ## { 'enum': 'ACPISlotType', 'data': [ 'DIMM', 'CPU' ] } @@ -78,9 +80,9 @@ ## # @ACPIOSTInfo: # -# OSPM Status Indication for a device -# For description of possible values of @source and @status fields -# see "_OST (OSPM Status Indication)" chapter of ACPI5.0 spec. +# OSPM Status Indication for a device For description of possible +# values of @source and @status fields see "_OST (OSPM Status +# Indication)" chapter of ACPI5.0 spec. # # @device: device ID associated with slot # @@ -111,13 +113,12 @@ # # Example: # -# -> { "execute": "query-acpi-ospm-status" } -# <- { "return": [ { "device": "d1", "slot": "0", "slot-type": "DIMM", "source": 1, "status": 0}, -# { "slot": "1", "slot-type": "DIMM", "source": 0, "status": 0}, -# { "slot": "2", "slot-type": "DIMM", "source": 0, "status": 0}, -# { "slot": "3", "slot-type": "DIMM", "source": 0, "status": 0} -# ]} -# +# -> { "execute": "query-acpi-ospm-status" } +# <- { "return": [ { "device": "d1", "slot": "0", "slot-type": "DIMM", "source": 1, "status": 0}, +# { "slot": "1", "slot-type": "DIMM", "source": 0, "status": 0}, +# { "slot": "2", "slot-type": "DIMM", "source": 0, "status": 0}, +# { "slot": "3", "slot-type": "DIMM", "source": 0, "status": 0} +# ]} ## { 'command': 'query-acpi-ospm-status', 'returns': ['ACPIOSTInfo'] } @@ -132,11 +133,10 @@ # # Example: # -# <- { "event": "ACPI_DEVICE_OST", -# "data": { "info": { "device": "d1", "slot": "0", -# "slot-type": "DIMM", "source": 1, "status": 0 } }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } -# +# <- { "event": "ACPI_DEVICE_OST", +# "data": { "info": { "device": "d1", "slot": "0", +# "slot-type": "DIMM", "source": 1, "status": 0 } }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'ACPI_DEVICE_OST', 'data': { 'info': 'ACPIOSTInfo' } } diff --git a/qapi/audio.json b/qapi/audio.json index 8099e3d7f1..519697c0cd 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -16,24 +16,24 @@ # General audio backend options that are used for both playback and # recording. # -# @mixing-engine: use QEMU's mixing engine to mix all streams inside QEMU and -# convert audio formats when not supported by the backend. When -# set to off, fixed-settings must be also off (default on, -# since 4.2) +# @mixing-engine: use QEMU's mixing engine to mix all streams inside +# QEMU and convert audio formats when not supported by the +# backend. When set to off, fixed-settings must be also off +# (default on, since 4.2) # -# @fixed-settings: use fixed settings for host input/output. When off, -# frequency, channels and format must not be -# specified (default true) +# @fixed-settings: use fixed settings for host input/output. When +# off, frequency, channels and format must not be specified +# (default true) # -# @frequency: frequency to use when using fixed settings -# (default 44100) +# @frequency: frequency to use when using fixed settings (default +# 44100) # # @channels: number of channels when using fixed settings (default 2) # # @voices: number of voices to use (default 1) # -# @format: sample format to use when using fixed settings -# (default s16) +# @format: sample format to use when using fixed settings (default +# s16) # # @buffer-length: the buffer length in microseconds # @@ -76,7 +76,7 @@ # @period-length: the period length in microseconds # # @try-poll: attempt to use poll mode, falling back to non-polling -# access on failure (default true) +# access on failure (default true) # # Since: 4.0 ## @@ -106,11 +106,33 @@ '*out': 'AudiodevAlsaPerDirectionOptions', '*threshold': 'uint32' } } +## +# @AudiodevSndioOptions: +# +# Options of the sndio audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# @dev: the name of the sndio device to use (default 'default') +# +# @latency: play buffer size (in microseconds) +# +# Since: 7.2 +## +{ 'struct': 'AudiodevSndioOptions', + 'data': { + '*in': 'AudiodevPerDirectionOptions', + '*out': 'AudiodevPerDirectionOptions', + '*dev': 'str', + '*latency': 'uint32'} } + ## # @AudiodevCoreaudioPerDirectionOptions: # -# Options of the Core Audio backend that are used for both playback and -# recording. +# Options of the Core Audio backend that are used for both playback +# and recording. # # @buffer-count: number of buffers # @@ -146,8 +168,8 @@ # # @out: options of the playback stream # -# @latency: add extra latency to playback in microseconds -# (default 10000) +# @latency: add extra latency to playback in microseconds (default +# 10000) # # Since: 4.0 ## @@ -163,21 +185,22 @@ # Options of the JACK backend that are used for both playback and # recording. # -# @server-name: select from among several possible concurrent server instances -# (default: environment variable $JACK_DEFAULT_SERVER if set, else "default") +# @server-name: select from among several possible concurrent server +# instances (default: environment variable $JACK_DEFAULT_SERVER if +# set, else "default") # -# @client-name: the client name to use. The server will modify this name to -# create a unique variant, if needed unless @exact-name is true (default: the -# guest's name) +# @client-name: the client name to use. The server will modify this +# name to create a unique variant, if needed unless @exact-name is +# true (default: the guest's name) # -# @connect-ports: if set, a regular expression of JACK client port name(s) to -# monitor for and automatically connect to +# @connect-ports: if set, a regular expression of JACK client port +# name(s) to monitor for and automatically connect to # -# @start-server: start a jack server process if one is not already present -# (default: false) +# @start-server: start a jack server process if one is not already +# present (default: false) # -# @exact-name: use the exact name requested otherwise JACK automatically -# generates a unique one, if needed (default: false) +# @exact-name: use the exact name requested otherwise JACK +# automatically generates a unique one, if needed (default: false) # # Since: 5.1 ## @@ -217,7 +240,7 @@ # @buffer-count: number of buffers # # @try-poll: attempt to use poll mode, falling back to non-polling -# access on failure (default true) +# access on failure (default true) # # Since: 4.0 ## @@ -238,15 +261,15 @@ # @out: options of the playback stream # # @try-mmap: try using memory-mapped access, falling back to -# non-memory-mapped access on failure (default true) +# non-memory-mapped access on failure (default true) # -# @exclusive: open device in exclusive mode (vmix won't work) -# (default false) +# @exclusive: open device in exclusive mode (vmix won't work) (default +# false) # # @dsp-policy: set the timing policy of the device (between 0 and 10, -# where smaller number means smaller latency but higher -# CPU usage) or -1 to use fragment mode (option ignored -# on some platforms) (default 5) +# where smaller number means smaller latency but higher CPU usage) +# or -1 to use fragment mode (option ignored on some platforms) +# (default 5) # # Since: 4.0 ## @@ -261,18 +284,18 @@ ## # @AudiodevPaPerDirectionOptions: # -# Options of the Pulseaudio backend that are used for both playback and -# recording. +# Options of the Pulseaudio backend that are used for both playback +# and recording. # # @name: name of the sink/source to use # # @stream-name: name of the PulseAudio stream created by qemu. Can be -# used to identify the stream in PulseAudio when you -# create multiple PulseAudio devices or run multiple qemu -# instances (default: audiodev's id, since 4.2) +# used to identify the stream in PulseAudio when you create +# multiple PulseAudio devices or run multiple qemu instances +# (default: audiodev's id, since 4.2) # # @latency: latency you want PulseAudio to achieve in microseconds -# (default 15000) +# (default 15000) # # Since: 4.0 ## @@ -302,6 +325,47 @@ '*out': 'AudiodevPaPerDirectionOptions', '*server': 'str' } } +## +# @AudiodevPipewirePerDirectionOptions: +# +# Options of the PipeWire backend that are used for both playback and +# recording. +# +# @name: name of the sink/source to use +# +# @stream-name: name of the PipeWire stream created by qemu. Can be +# used to identify the stream in PipeWire when you create multiple +# PipeWire devices or run multiple qemu instances (default: +# audiodev's id) +# +# @latency: latency you want PipeWire to achieve in microseconds +# (default 46000) +# +# Since: 8.1 +## +{ 'struct': 'AudiodevPipewirePerDirectionOptions', + 'base': 'AudiodevPerDirectionOptions', + 'data': { + '*name': 'str', + '*stream-name': 'str', + '*latency': 'uint32' } } + +## +# @AudiodevPipewireOptions: +# +# Options of the PipeWire audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# Since: 8.1 +## +{ 'struct': 'AudiodevPipewireOptions', + 'data': { + '*in': 'AudiodevPipewirePerDirectionOptions', + '*out': 'AudiodevPipewirePerDirectionOptions' } } + ## # @AudiodevSdlPerDirectionOptions: # @@ -386,8 +450,19 @@ # Since: 4.0 ## { 'enum': 'AudiodevDriver', - 'data': [ 'none', 'alsa', 'coreaudio', 'dbus', 'dsound', 'jack', 'oss', 'pa', - 'sdl', 'spice', 'wav' ] } + 'data': [ 'none', + { 'name': 'alsa', 'if': 'CONFIG_AUDIO_ALSA' }, + { 'name': 'coreaudio', 'if': 'CONFIG_AUDIO_COREAUDIO' }, + { 'name': 'dbus', 'if': 'CONFIG_DBUS_DISPLAY' }, + { 'name': 'dsound', 'if': 'CONFIG_AUDIO_DSOUND' }, + { 'name': 'jack', 'if': 'CONFIG_AUDIO_JACK' }, + { 'name': 'oss', 'if': 'CONFIG_AUDIO_OSS' }, + { 'name': 'pa', 'if': 'CONFIG_AUDIO_PA' }, + { 'name': 'pipewire', 'if': 'CONFIG_AUDIO_PIPEWIRE' }, + { 'name': 'sdl', 'if': 'CONFIG_AUDIO_SDL' }, + { 'name': 'sndio', 'if': 'CONFIG_AUDIO_SNDIO' }, + { 'name': 'spice', 'if': 'CONFIG_SPICE' }, + 'wav' ] } ## # @Audiodev: @@ -398,7 +473,8 @@ # # @driver: the backend driver to use # -# @timer-period: timer period (in microseconds, 0: use lowest possible) +# @timer-period: timer period (in microseconds, 0: use lowest +# possible) # # Since: 4.0 ## @@ -410,13 +486,38 @@ 'discriminator': 'driver', 'data': { 'none': 'AudiodevGenericOptions', - 'alsa': 'AudiodevAlsaOptions', - 'coreaudio': 'AudiodevCoreaudioOptions', - 'dbus': 'AudiodevGenericOptions', - 'dsound': 'AudiodevDsoundOptions', - 'jack': 'AudiodevJackOptions', - 'oss': 'AudiodevOssOptions', - 'pa': 'AudiodevPaOptions', - 'sdl': 'AudiodevSdlOptions', - 'spice': 'AudiodevGenericOptions', + 'alsa': { 'type': 'AudiodevAlsaOptions', + 'if': 'CONFIG_AUDIO_ALSA' }, + 'coreaudio': { 'type': 'AudiodevCoreaudioOptions', + 'if': 'CONFIG_AUDIO_COREAUDIO' }, + 'dbus': { 'type': 'AudiodevGenericOptions', + 'if': 'CONFIG_DBUS_DISPLAY' }, + 'dsound': { 'type': 'AudiodevDsoundOptions', + 'if': 'CONFIG_AUDIO_DSOUND' }, + 'jack': { 'type': 'AudiodevJackOptions', + 'if': 'CONFIG_AUDIO_JACK' }, + 'oss': { 'type': 'AudiodevOssOptions', + 'if': 'CONFIG_AUDIO_OSS' }, + 'pa': { 'type': 'AudiodevPaOptions', + 'if': 'CONFIG_AUDIO_PA' }, + 'pipewire': { 'type': 'AudiodevPipewireOptions', + 'if': 'CONFIG_AUDIO_PIPEWIRE' }, + 'sdl': { 'type': 'AudiodevSdlOptions', + 'if': 'CONFIG_AUDIO_SDL' }, + 'sndio': { 'type': 'AudiodevSndioOptions', + 'if': 'CONFIG_AUDIO_SNDIO' }, + 'spice': { 'type': 'AudiodevGenericOptions', + 'if': 'CONFIG_SPICE' }, 'wav': 'AudiodevWavOptions' } } + +## +# @query-audiodevs: +# +# Returns information about audiodev configuration +# +# Returns: array of @Audiodev +# +# Since: 8.0 +## +{ 'command': 'query-audiodevs', + 'returns': ['Audiodev'] } diff --git a/qapi/authz.json b/qapi/authz.json index 51845e37cc..7fc6e3032e 100644 --- a/qapi/authz.json +++ b/qapi/authz.json @@ -11,6 +11,7 @@ # The authorization policy result # # @deny: deny access +# # @allow: allow access # # Since: 4.0 @@ -25,6 +26,7 @@ # The authorization policy match format # # @exact: an exact string match +# # @glob: string with ? and * shell wildcard support # # Since: 4.0 @@ -39,7 +41,9 @@ # A single authorization rule. # # @match: a string or glob to match against a user identity +# # @policy: the result to return if @match evaluates to true +# # @format: the format of the @match rule (default 'exact') # # Since: 4.0 @@ -54,7 +58,8 @@ # # Properties for authz-list objects. # -# @policy: Default policy to apply when no rule matches (default: deny) +# @policy: Default policy to apply when no rule matches (default: +# deny) # # @rules: Authorization rules based on matching user # @@ -69,14 +74,14 @@ # # Properties for authz-listfile objects. # -# @filename: File name to load the configuration from. The file must -# contain valid JSON for AuthZListProperties. +# @filename: File name to load the configuration from. The file must +# contain valid JSON for AuthZListProperties. # -# @refresh: If true, inotify is used to monitor the file, automatically -# reloading changes. If an error occurs during reloading, all -# authorizations will fail until the file is next successfully -# loaded. (default: true if the binary was built with -# CONFIG_INOTIFY1, false otherwise) +# @refresh: If true, inotify is used to monitor the file, +# automatically reloading changes. If an error occurs during +# reloading, all authorizations will fail until the file is next +# successfully loaded. (default: true if the binary was built +# with CONFIG_INOTIFY1, false otherwise) # # Since: 4.0 ## @@ -101,10 +106,10 @@ # # Properties for authz-simple objects. # -# @identity: Identifies the allowed user. Its format depends on the network -# service that authorization object is associated with. For -# authorizing based on TLS x509 certificates, the identity must be -# the x509 distinguished name. +# @identity: Identifies the allowed user. Its format depends on the +# network service that authorization object is associated with. +# For authorizing based on TLS x509 certificates, the identity +# must be the x509 distinguished name. # # Since: 4.0 ## diff --git a/qapi/block-core.json b/qapi/block-core.json index f0383c7925..746d1694c2 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -25,15 +25,16 @@ # # @vm-clock-sec: VM clock relative to boot in seconds # -# @vm-clock-nsec: fractional part in nano seconds to be used with vm-clock-sec +# @vm-clock-nsec: fractional part in nano seconds to be used with +# vm-clock-sec # -# @icount: Current instruction count. Appears when execution record/replay -# is enabled. Used for "time-traveling" to match the moment -# in the recorded execution with the snapshots. This counter may -# be obtained through @query-replay command (since 5.2) +# @icount: Current instruction count. Appears when execution +# record/replay is enabled. Used for "time-traveling" to match +# the moment in the recorded execution with the snapshots. This +# counter may be obtained through @query-replay command (since +# 5.2) # # Since: 1.3 -# ## { 'struct': 'SnapshotInfo', 'data': { 'id': 'str', 'name': 'str', 'vm-state-size': 'int', @@ -66,25 +67,26 @@ # # @compat: compatibility level # -# @data-file: the filename of the external data file that is stored in the -# image and used as a default for opening the image (since: 4.0) +# @data-file: the filename of the external data file that is stored in +# the image and used as a default for opening the image +# (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 (since: 4.0) +# standalone (read-only) raw image without looking at qcow2 +# metadata (since: 4.0) # -# @extended-l2: true if the image has extended L2 entries; only valid for -# compat >= 1.1 (since 5.2) +# @extended-l2: true if the image has extended L2 entries; only valid +# for compat >= 1.1 (since 5.2) # # @lazy-refcounts: on or off; only valid for compat >= 1.1 # # @corrupt: true if the image has been marked corrupt; only valid for -# compat >= 1.1 (since 2.2) +# compat >= 1.1 (since 2.2) # # @refcount-bits: width of a refcount entry in bits (since 2.3) # -# @encrypt: details about encryption parameters; only set if image -# is encrypted (since 2.10) +# @encrypt: details about encryption parameters; only set if image is +# encrypted (since 2.10) # # @bitmaps: A list of qcow2 bitmap details (since 4.0) # @@ -124,7 +126,33 @@ 'create-type': 'str', 'cid': 'int', 'parent-cid': 'int', - 'extents': ['ImageInfo'] + 'extents': ['VmdkExtentInfo'] + } } + +## +# @VmdkExtentInfo: +# +# Information about a VMDK extent file +# +# @filename: Name of the extent file +# +# @format: Extent type (e.g. FLAT or SPARSE) +# +# @virtual-size: Number of bytes covered by this extent +# +# @cluster-size: Cluster size in bytes (for non-flat extents) +# +# @compressed: Whether this extent contains compressed data +# +# Since: 8.0 +## +{ 'struct': 'VmdkExtentInfo', + 'data': { + 'filename': 'str', + 'format': 'str', + 'virtual-size': 'int', + '*cluster-size': 'int', + '*compressed': 'bool' } } ## @@ -139,20 +167,37 @@ '*encryption-format': 'RbdImageEncryptionFormat' } } +## +# @ImageInfoSpecificFile: +# +# @extent-size-hint: Extent size hint (if available) +# +# Since: 8.0 +## +{ 'struct': 'ImageInfoSpecificFile', + 'data': { + '*extent-size-hint': 'size' + } } + ## # @ImageInfoSpecificKind: # # @luks: Since 2.7 +# # @rbd: Since 6.1 # +# @file: Since 8.0 +# # Since: 1.7 ## { 'enum': 'ImageInfoSpecificKind', - 'data': [ 'qcow2', 'vmdk', 'luks', 'rbd' ] } + 'data': [ 'qcow2', 'vmdk', 'luks', 'rbd', 'file' ] } ## # @ImageInfoSpecificQCow2Wrapper: # +# @data: image information specific to QCOW2 +# # Since: 1.7 ## { 'struct': 'ImageInfoSpecificQCow2Wrapper', @@ -161,6 +206,8 @@ ## # @ImageInfoSpecificVmdkWrapper: # +# @data: image information specific to VMDK +# # Since: 6.1 ## { 'struct': 'ImageInfoSpecificVmdkWrapper', @@ -169,6 +216,8 @@ ## # @ImageInfoSpecificLUKSWrapper: # +# @data: image information specific to LUKS +# # Since: 2.7 ## { 'struct': 'ImageInfoSpecificLUKSWrapper', @@ -180,15 +229,30 @@ ## # @ImageInfoSpecificRbdWrapper: # +# @data: image information specific to RBD +# # Since: 6.1 ## { 'struct': 'ImageInfoSpecificRbdWrapper', 'data': { 'data': 'ImageInfoSpecificRbd' } } +## +# @ImageInfoSpecificFileWrapper: +# +# @data: image information specific to files +# +# Since: 8.0 +## +{ 'struct': 'ImageInfoSpecificFileWrapper', + 'data': { 'data': 'ImageInfoSpecificFile' } } + ## # @ImageInfoSpecific: # -# A discriminated record of image format specific information structures. +# A discriminated record of image format specific information +# structures. +# +# @type: block driver name # # Since: 1.7 ## @@ -199,11 +263,12 @@ 'qcow2': 'ImageInfoSpecificQCow2Wrapper', 'vmdk': 'ImageInfoSpecificVmdkWrapper', 'luks': 'ImageInfoSpecificLUKSWrapper', - 'rbd': 'ImageInfoSpecificRbdWrapper' + 'rbd': 'ImageInfoSpecificRbdWrapper', + 'file': 'ImageInfoSpecificFileWrapper' } } ## -# @ImageInfo: +# @BlockNodeInfo: # # Information about a QEMU image file # @@ -231,22 +296,70 @@ # # @snapshots: list of VM snapshots # -# @backing-image: info of the backing image (since 1.6) -# # @format-specific: structure supplying additional format-specific -# information (since 1.7) +# information (since 1.7) # -# Since: 1.3 +# Since: 8.0 ## -{ 'struct': 'ImageInfo', +{ 'struct': 'BlockNodeInfo', 'data': {'filename': 'str', 'format': 'str', '*dirty-flag': 'bool', '*actual-size': 'int', 'virtual-size': 'int', '*cluster-size': 'int', '*encrypted': 'bool', '*compressed': 'bool', '*backing-filename': 'str', '*full-backing-filename': 'str', '*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'], - '*backing-image': 'ImageInfo', '*format-specific': 'ImageInfoSpecific' } } +## +# @ImageInfo: +# +# Information about a QEMU image file, and potentially its backing +# image +# +# @backing-image: info of the backing image +# +# Since: 1.3 +## +{ 'struct': 'ImageInfo', + 'base': 'BlockNodeInfo', + 'data': { + '*backing-image': 'ImageInfo' + } } + +## +# @BlockChildInfo: +# +# Information about all nodes in the block graph starting at some +# node, annotated with information about that node in relation to its +# parent. +# +# @name: Child name of the root node in the BlockGraphInfo struct, in +# its role as the child of some undescribed parent node +# +# @info: Block graph information starting at this node +# +# Since: 8.0 +## +{ 'struct': 'BlockChildInfo', + 'data': { + 'name': 'str', + 'info': 'BlockGraphInfo' + } } + +## +# @BlockGraphInfo: +# +# Information about all nodes in a block (sub)graph in the form of +# BlockNodeInfo data. The base BlockNodeInfo struct contains the +# information for the (sub)graph's root node. +# +# @children: Array of links to this node's child nodes' information +# +# Since: 8.0 +## +{ 'struct': 'BlockGraphInfo', + 'base': 'BlockNodeInfo', + 'data': { 'children': ['BlockChildInfo'] } } + ## # @ImageCheck: # @@ -259,32 +372,28 @@ # @check-errors: number of unexpected errors occurred during check # # @image-end-offset: offset (in bytes) where the image ends, this -# field is present if the driver for the image format -# supports it +# field is present if the driver for the image format supports it # # @corruptions: number of corruptions found during the check if any # # @leaks: number of leaks found during the check if any # -# @corruptions-fixed: number of corruptions fixed during the check -# if any +# @corruptions-fixed: number of corruptions fixed during the check if +# any # # @leaks-fixed: number of leaks fixed during the check if any # -# @total-clusters: total number of clusters, this field is present -# if the driver for the image format supports it +# @total-clusters: total number of clusters, this field is present if +# the driver for the image format supports it # -# @allocated-clusters: total number of allocated clusters, this -# field is present if the driver for the image format -# supports it +# @allocated-clusters: total number of allocated clusters, this field +# is present if the driver for the image format supports it # # @fragmented-clusters: total number of fragmented clusters, this -# field is present if the driver for the image format -# supports it +# field is present if the driver for the image format supports it # # @compressed-clusters: total number of compressed clusters, this -# field is present if the driver for the image format -# supports it +# field is present if the driver for the image format supports it # # Since: 1.4 ## @@ -301,27 +410,29 @@ # Mapping information from a virtual block range to a host file range # # @start: virtual (guest) offset of the first byte described by this -# entry +# entry # # @length: the number of bytes of the mapped virtual range # # @data: reading the image will actually read data from a file (in -# particular, if @offset is present this means that the sectors -# are not simply preallocated, but contain actual data in raw -# format) +# particular, if @offset is present this means that the sectors +# are not simply preallocated, but contain actual data in raw +# format) # # @zero: whether the virtual blocks read as zeroes # -# @depth: number of layers (0 = top image, 1 = top image's backing -# file, ..., n - 1 = bottom image (where n is the number of -# images in the chain)) before reaching one for which the -# range is allocated +# @compressed: true if the data is stored compressed (since 8.2) # -# @present: true if this layer provides the data, false if adding a backing -# layer could impact this region (since 6.1) +# @depth: number of layers (0 = top image, 1 = top image's backing +# file, ..., n - 1 = bottom image (where n is the number of images +# in the chain)) before reaching one for which the range is +# allocated +# +# @present: true if this layer provides the data, false if adding a +# backing layer could impact this region (since 6.1) # # @offset: if present, the image file stores the data for this range -# in raw format at the given (host) offset +# in raw format at the given (host) offset # # @filename: filename that is referred to by @offset # @@ -329,8 +440,8 @@ ## { 'struct': 'MapEntry', 'data': {'start': 'int', 'length': 'int', 'data': 'bool', - 'zero': 'bool', 'depth': 'int', 'present': 'bool', - '*offset': 'int', '*filename': 'str' } } + 'zero': 'bool', 'compressed': 'bool', 'depth': 'int', + 'present': 'bool', '*offset': 'int', '*filename': 'str' } } ## # @BlockdevCacheInfo: @@ -338,7 +449,9 @@ # Cache mode information for a block device # # @writeback: true if writeback mode is enabled +# # @direct: true if the host page cache is bypassed (O_DIRECT) +# # @no-flush: true if flush requests are ignored for the device # # Since: 2.3 @@ -359,21 +472,19 @@ # # @ro: true if the backing device was open read-only # -# @drv: the name of the block format used to open the backing device. As of -# 0.14 this can be: 'blkdebug', 'bochs', 'cloop', 'cow', 'dmg', -# 'file', 'file', 'ftp', 'ftps', 'host_cdrom', 'host_device', -# 'http', 'https', 'luks', 'nbd', 'parallels', 'qcow', -# 'qcow2', 'raw', 'vdi', 'vmdk', 'vpc', 'vvfat' -# 2.2: 'archipelago' added, 'cow' dropped -# 2.3: 'host_floppy' deprecated -# 2.5: 'host_floppy' dropped -# 2.6: 'luks' added -# 2.8: 'replication' added, 'tftp' dropped -# 2.9: 'archipelago' dropped +# @drv: the name of the block format used to open the backing device. +# As of 0.14 this can be: 'blkdebug', 'bochs', 'cloop', 'cow', +# 'dmg', 'file', 'file', 'ftp', 'ftps', 'host_cdrom', +# 'host_device', 'http', 'https', 'luks', 'nbd', 'parallels', +# 'qcow', 'qcow2', 'raw', 'vdi', 'vmdk', 'vpc', 'vvfat' 2.2: +# 'archipelago' added, 'cow' dropped 2.3: 'host_floppy' deprecated +# 2.5: 'host_floppy' dropped 2.6: 'luks' added 2.8: 'replication' +# added, 'tftp' dropped 2.9: 'archipelago' dropped # # @backing_file: the name of the backing file (for copy-on-write) # -# @backing_file_depth: number of files in the backing file chain (since: 1.2) +# @backing_file_depth: number of files in the backing file chain +# (since: 1.2) # # @encrypted: true if the backing device is encrypted # @@ -393,41 +504,40 @@ # # @image: the info of image used (since: 1.6) # -# @bps_max: total throughput limit during bursts, -# in bytes (Since 1.7) +# @bps_max: total throughput limit during bursts, in bytes (Since 1.7) # -# @bps_rd_max: read throughput limit during bursts, -# in bytes (Since 1.7) +# @bps_rd_max: read throughput limit during bursts, in bytes (Since +# 1.7) # -# @bps_wr_max: write throughput limit during bursts, -# in bytes (Since 1.7) +# @bps_wr_max: write throughput limit during bursts, in bytes (Since +# 1.7) # -# @iops_max: total I/O operations per second during bursts, -# in bytes (Since 1.7) +# @iops_max: total I/O operations per second during bursts, in bytes +# (Since 1.7) # -# @iops_rd_max: read I/O operations per second during bursts, -# in bytes (Since 1.7) +# @iops_rd_max: read I/O operations per second during bursts, in bytes +# (Since 1.7) # -# @iops_wr_max: write I/O operations per second during bursts, -# in bytes (Since 1.7) +# @iops_wr_max: write I/O operations per second during bursts, in +# bytes (Since 1.7) # -# @bps_max_length: maximum length of the @bps_max burst -# period, in seconds. (Since 2.6) +# @bps_max_length: maximum length of the @bps_max burst period, in +# seconds. (Since 2.6) # -# @bps_rd_max_length: maximum length of the @bps_rd_max -# burst period, in seconds. (Since 2.6) +# @bps_rd_max_length: maximum length of the @bps_rd_max burst period, +# in seconds. (Since 2.6) # -# @bps_wr_max_length: maximum length of the @bps_wr_max -# burst period, in seconds. (Since 2.6) +# @bps_wr_max_length: maximum length of the @bps_wr_max burst period, +# in seconds. (Since 2.6) # -# @iops_max_length: maximum length of the @iops burst -# period, in seconds. (Since 2.6) +# @iops_max_length: maximum length of the @iops burst period, in +# seconds. (Since 2.6) # -# @iops_rd_max_length: maximum length of the @iops_rd_max -# burst period, in seconds. (Since 2.6) +# @iops_rd_max_length: maximum length of the @iops_rd_max burst +# period, in seconds. (Since 2.6) # -# @iops_wr_max_length: maximum length of the @iops_wr_max -# burst period, in seconds. (Since 2.6) +# @iops_wr_max_length: maximum length of the @iops_wr_max burst +# period, in seconds. (Since 2.6) # # @iops_size: an I/O size in bytes (Since 1.7) # @@ -435,11 +545,11 @@ # # @cache: the cache mode used for the block device (since: 2.3) # -# @write_threshold: configured write threshold for the device. -# 0 if disabled. (Since 2.3) +# @write_threshold: configured write threshold for the device. 0 if +# disabled. (Since 2.3) # -# @dirty-bitmaps: dirty bitmaps information (only present if node -# has one or more dirty bitmaps) (Since 4.2) +# @dirty-bitmaps: dirty bitmaps information (only present if node has +# one or more dirty bitmaps) (Since 4.2) # # Since: 0.14 ## @@ -469,7 +579,8 @@ # # @failed: The last I/O operation has failed # -# @nospace: The last I/O operation has failed due to a no-space condition +# @nospace: The last I/O operation has failed due to a no-space +# condition # # Since: 1.0 ## @@ -486,20 +597,20 @@ # # @granularity: granularity of the dirty bitmap in bytes (since 1.4) # -# @recording: true if the bitmap is recording new writes from the guest. -# Replaces ``active`` and ``disabled`` statuses. (since 4.0) +# @recording: true if the bitmap is recording new writes from the +# guest. (since 4.0) # # @busy: true if the bitmap is in-use by some operation (NBD or jobs) -# and cannot be modified via QMP or used by another operation. -# Replaces ``locked`` and ``frozen`` statuses. (since 4.0) +# and cannot be modified via QMP or used by another operation. +# (since 4.0) # -# @persistent: true if the bitmap was stored on disk, is scheduled to be stored -# on disk, or both. (since 4.0) +# @persistent: true if the bitmap was stored on disk, is scheduled to +# be stored on disk, or both. (since 4.0) # -# @inconsistent: true if this is a persistent bitmap that was improperly -# stored. Implies @persistent to be true; @recording and -# @busy to be false. This bitmap cannot be used. To remove -# it, use @block-dirty-bitmap-remove. (Since 4.0) +# @inconsistent: true if this is a persistent bitmap that was +# improperly stored. Implies @persistent to be true; @recording +# and @busy to be false. This bitmap cannot be used. To remove +# it, use @block-dirty-bitmap-remove. (Since 4.0) # # Since: 1.3 ## @@ -513,14 +624,14 @@ # # An enumeration of flags that a bitmap can report to the user. # -# @in-use: This flag is set by any process actively modifying the qcow2 file, -# and cleared when the updated bitmap is flushed to the qcow2 image. -# The presence of this flag in an offline image means that the bitmap -# was not saved correctly after its last usage, and may contain -# inconsistent data. +# @in-use: This flag is set by any process actively modifying the +# qcow2 file, and cleared when the updated bitmap is flushed to +# the qcow2 image. The presence of this flag in an offline image +# means that the bitmap was not saved correctly after its last +# usage, and may contain inconsistent data. # -# @auto: The bitmap must reflect all changes of the virtual disk by any -# application that would write to this qcow2 file. +# @auto: The bitmap must reflect all changes of the virtual disk by +# any application that would write to this qcow2 file. # # Since: 4.0 ## @@ -549,17 +660,15 @@ # # Block latency histogram. # -# @boundaries: list of interval boundary values in nanoseconds, all greater -# than zero and in ascending order. -# For example, the list [10, 50, 100] produces the following -# histogram intervals: [0, 10), [10, 50), [50, 100), [100, +inf). +# @boundaries: list of interval boundary values in nanoseconds, all +# greater than zero and in ascending order. For example, the list +# [10, 50, 100] produces the following histogram intervals: [0, +# 10), [10, 50), [50, 100), [100, +inf). # -# @bins: list of io request counts corresponding to histogram intervals. -# len(@bins) = len(@boundaries) + 1 -# For the example above, @bins may be something like [3, 1, 5, 2], -# and corresponding histogram looks like: -# -# :: +# @bins: list of io request counts corresponding to histogram +# intervals, one more element than @boundaries has. For the +# example above, @bins may be something like [3, 1, 5, 2], and +# corresponding histogram looks like:: # # 5| * # 4| * @@ -577,32 +686,32 @@ ## # @BlockInfo: # -# Block device information. This structure describes a virtual device and -# the backing device associated with it. +# Block device information. This structure describes a virtual device +# and the backing device associated with it. # # @device: The device name associated with the virtual device. # -# @qdev: The qdev ID, or if no ID is assigned, the QOM path of the block -# device. (since 2.10) +# @qdev: The qdev ID, or if no ID is assigned, the QOM path of the +# block device. (since 2.10) # -# @type: This field is returned only for compatibility reasons, it should -# not be used (always returns 'unknown') +# @type: This field is returned only for compatibility reasons, it +# should not be used (always returns 'unknown') # # @removable: True if the device supports removable media. # -# @locked: True if the guest has locked this device from having its media -# removed +# @locked: True if the guest has locked this device from having its +# media removed # -# @tray_open: True if the device's tray is open -# (only present if it has a tray) +# @tray_open: True if the device's tray is open (only present if it +# has a tray) # -# @io-status: @BlockDeviceIoStatus. Only present if the device -# supports it and the VM is configured to stop on errors -# (supported device models: virtio-blk, IDE, SCSI except -# scsi-generic) +# @io-status: @BlockDeviceIoStatus. Only present if the device +# supports it and the VM is configured to stop on errors +# (supported device models: virtio-blk, IDE, SCSI except +# scsi-generic) # # @inserted: @BlockDeviceInfo describing the device if media is -# present +# present # # Since: 0.14 ## @@ -614,28 +723,31 @@ ## # @BlockMeasureInfo: # -# Image file size calculation information. This structure describes the size -# requirements for creating a new image file. +# Image file size calculation information. This structure describes +# the size requirements for creating a new image file. # -# The size requirements depend on the new image file format. File size always -# equals virtual disk size for the 'raw' format, even for sparse POSIX files. -# Compact formats such as 'qcow2' represent unallocated and zero regions -# efficiently so file size may be smaller than virtual disk size. +# The size requirements depend on the new image file format. File +# size always equals virtual disk size for the 'raw' format, even for +# sparse POSIX files. Compact formats such as 'qcow2' represent +# unallocated and zero regions efficiently so file size may be smaller +# than virtual disk size. # -# The values are upper bounds that are guaranteed to fit the new image file. -# Subsequent modification, such as internal snapshot or further bitmap -# creation, may require additional space and is not covered here. +# The values are upper bounds that are guaranteed to fit the new image +# file. Subsequent modification, such as internal snapshot or further +# bitmap creation, may require additional space and is not covered +# here. # -# @required: Size required for a new image file, in bytes, when copying just -# allocated guest-visible contents. +# @required: Size required for a new image file, in bytes, when +# copying just allocated guest-visible contents. # -# @fully-allocated: Image file size, in bytes, once data has been written -# to all sectors, when copying just guest-visible contents. +# @fully-allocated: Image file size, in bytes, once data has been +# written to all sectors, when copying just guest-visible +# contents. # -# @bitmaps: Additional size required if all the top-level bitmap metadata -# in the source image were to be copied to the destination, -# present only when source and destination both support -# persistent bitmaps. (since 5.1) +# @bitmaps: Additional size required if all the top-level bitmap +# metadata in the source image were to be copied to the +# destination, present only when source and destination both +# support persistent bitmaps. (since 5.1) # # Since: 2.10 ## @@ -647,138 +759,150 @@ # # 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. +# Returns: a list of @BlockInfo describing each virtual block device. +# Filter nodes that were created implicitly are skipped over. # # Since: 0.14 # # Example: # -# -> { "execute": "query-block" } -# <- { -# "return":[ -# { -# "io-status": "ok", -# "device":"ide0-hd0", -# "locked":false, -# "removable":false, -# "inserted":{ -# "ro":false, -# "drv":"qcow2", -# "encrypted":false, -# "file":"disks/test.qcow2", -# "backing_file_depth":1, -# "bps":1000000, -# "bps_rd":0, -# "bps_wr":0, -# "iops":1000000, -# "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, -# "iops_size": 0, -# "detect_zeroes": "on", -# "write_threshold": 0, -# "image":{ -# "filename":"disks/test.qcow2", -# "format":"qcow2", -# "virtual-size":2048000, -# "backing_file":"base.qcow2", -# "full-backing-filename":"disks/base.qcow2", -# "backing-filename-format":"qcow2", -# "snapshots":[ -# { -# "id": "1", -# "name": "snapshot1", -# "vm-state-size": 0, -# "date-sec": 10000200, -# "date-nsec": 12, -# "vm-clock-sec": 206, -# "vm-clock-nsec": 30 -# } -# ], -# "backing-image":{ -# "filename":"disks/base.qcow2", +# -> { "execute": "query-block" } +# <- { +# "return":[ +# { +# "io-status": "ok", +# "device":"ide0-hd0", +# "locked":false, +# "removable":false, +# "inserted":{ +# "ro":false, +# "drv":"qcow2", +# "encrypted":false, +# "file":"disks/test.qcow2", +# "backing_file_depth":1, +# "bps":1000000, +# "bps_rd":0, +# "bps_wr":0, +# "iops":1000000, +# "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, +# "iops_size": 0, +# "detect_zeroes": "on", +# "write_threshold": 0, +# "image":{ +# "filename":"disks/test.qcow2", # "format":"qcow2", -# "virtual-size":2048000 -# } -# } -# }, -# "qdev": "ide_disk", -# "type":"unknown" -# }, -# { -# "io-status": "ok", -# "device":"ide1-cd0", -# "locked":false, -# "removable":true, -# "qdev": "/machine/unattached/device[23]", -# "tray_open": false, -# "type":"unknown" -# }, -# { -# "device":"floppy0", -# "locked":false, -# "removable":true, -# "qdev": "/machine/unattached/device[20]", -# "type":"unknown" -# }, -# { -# "device":"sd0", -# "locked":false, -# "removable":true, -# "type":"unknown" -# } -# ] -# } -# +# "virtual-size":2048000, +# "backing_file":"base.qcow2", +# "full-backing-filename":"disks/base.qcow2", +# "backing-filename-format":"qcow2", +# "snapshots":[ +# { +# "id": "1", +# "name": "snapshot1", +# "vm-state-size": 0, +# "date-sec": 10000200, +# "date-nsec": 12, +# "vm-clock-sec": 206, +# "vm-clock-nsec": 30 +# } +# ], +# "backing-image":{ +# "filename":"disks/base.qcow2", +# "format":"qcow2", +# "virtual-size":2048000 +# } +# } +# }, +# "qdev": "ide_disk", +# "type":"unknown" +# }, +# { +# "io-status": "ok", +# "device":"ide1-cd0", +# "locked":false, +# "removable":true, +# "qdev": "/machine/unattached/device[23]", +# "tray_open": false, +# "type":"unknown" +# }, +# { +# "device":"floppy0", +# "locked":false, +# "removable":true, +# "qdev": "/machine/unattached/device[20]", +# "type":"unknown" +# }, +# { +# "device":"sd0", +# "locked":false, +# "removable":true, +# "type":"unknown" +# } +# ] +# } ## -{ 'command': 'query-block', 'returns': ['BlockInfo'] } +{ 'command': 'query-block', 'returns': ['BlockInfo'], + 'allow-preconfig': true } ## # @BlockDeviceTimedStats: # # Statistics of a block device during a given interval of time. # -# @interval_length: Interval used for calculating the statistics, -# in seconds. +# @interval_length: Interval used for calculating the statistics, in +# seconds. # # @min_rd_latency_ns: Minimum latency of read operations in the -# defined interval, in nanoseconds. +# defined interval, in nanoseconds. # # @min_wr_latency_ns: Minimum latency of write operations in the -# defined interval, in nanoseconds. +# defined interval, in nanoseconds. +# +# @min_zone_append_latency_ns: Minimum latency of zone append +# operations in the defined interval, in nanoseconds (since 8.1) # # @min_flush_latency_ns: Minimum latency of flush operations in the -# defined interval, in nanoseconds. +# defined interval, in nanoseconds. # # @max_rd_latency_ns: Maximum latency of read operations in the -# defined interval, in nanoseconds. +# defined interval, in nanoseconds. # # @max_wr_latency_ns: Maximum latency of write operations in the -# defined interval, in nanoseconds. +# defined interval, in nanoseconds. +# +# @max_zone_append_latency_ns: Maximum latency of zone append +# operations in the defined interval, in nanoseconds (since 8.1) # # @max_flush_latency_ns: Maximum latency of flush operations in the -# defined interval, in nanoseconds. +# defined interval, in nanoseconds. # # @avg_rd_latency_ns: Average latency of read operations in the -# defined interval, in nanoseconds. +# defined interval, in nanoseconds. # # @avg_wr_latency_ns: Average latency of write operations in the -# defined interval, in nanoseconds. +# defined interval, in nanoseconds. +# +# @avg_zone_append_latency_ns: Average latency of zone append +# operations in the defined interval, in nanoseconds (since 8.1) # # @avg_flush_latency_ns: Average latency of flush operations in the -# defined interval, in nanoseconds. +# defined interval, in nanoseconds. # -# @avg_rd_queue_depth: Average number of pending read operations -# in the defined interval. +# @avg_rd_queue_depth: Average number of pending read operations in +# the defined interval. # -# @avg_wr_queue_depth: Average number of pending write operations -# in the defined interval. +# @avg_wr_queue_depth: Average number of pending write operations in +# the defined interval. +# +# @avg_zone_append_queue_depth: Average number of pending zone append +# operations in the defined interval (since 8.1). # # Since: 2.5 ## @@ -786,9 +910,13 @@ 'data': { 'interval_length': 'int', 'min_rd_latency_ns': 'int', 'max_rd_latency_ns': 'int', 'avg_rd_latency_ns': 'int', 'min_wr_latency_ns': 'int', 'max_wr_latency_ns': 'int', - 'avg_wr_latency_ns': 'int', 'min_flush_latency_ns': 'int', - 'max_flush_latency_ns': 'int', 'avg_flush_latency_ns': 'int', - 'avg_rd_queue_depth': 'number', 'avg_wr_queue_depth': 'number' } } + 'avg_wr_latency_ns': 'int', 'min_zone_append_latency_ns': 'int', + 'max_zone_append_latency_ns': 'int', + 'avg_zone_append_latency_ns': 'int', + 'min_flush_latency_ns': 'int', 'max_flush_latency_ns': 'int', + 'avg_flush_latency_ns': 'int', 'avg_rd_queue_depth': 'number', + 'avg_wr_queue_depth': 'number', + 'avg_zone_append_queue_depth': 'number' } } ## # @BlockDeviceStats: @@ -799,104 +927,134 @@ # # @wr_bytes: The number of bytes written by the device. # +# @zone_append_bytes: The number of bytes appended by the zoned +# devices (since 8.1) +# # @unmap_bytes: The number of bytes unmapped by the device (Since 4.2) # -# @rd_operations: The number of read operations performed by the device. +# @rd_operations: The number of read operations performed by the +# device. # -# @wr_operations: The number of write operations performed by the device. +# @wr_operations: The number of write operations performed by the +# device. # -# @flush_operations: The number of cache flush operations performed by the -# device (since 0.15) +# @zone_append_operations: The number of zone append operations +# performed by the zoned devices (since 8.1) # -# @unmap_operations: The number of unmap operations performed by the device -# (Since 4.2) +# @flush_operations: The number of cache flush operations performed by +# the device (since 0.15) # -# @rd_total_time_ns: Total time spent on reads in nanoseconds (since 0.15). +# @unmap_operations: The number of unmap operations performed by the +# device (Since 4.2) # -# @wr_total_time_ns: Total time spent on writes in nanoseconds (since 0.15). +# @rd_total_time_ns: Total time spent on reads in nanoseconds (since +# 0.15). # -# @flush_total_time_ns: Total time spent on cache flushes in nanoseconds -# (since 0.15). +# @wr_total_time_ns: Total time spent on writes in nanoseconds (since +# 0.15). # -# @unmap_total_time_ns: Total time spent on unmap operations in nanoseconds -# (Since 4.2) +# @zone_append_total_time_ns: Total time spent on zone append writes +# in nanoseconds (since 8.1) # -# @wr_highest_offset: The offset after the greatest byte written to the -# device. The intended use of this information is for -# growable sparse files (like qcow2) that are used on top -# of a physical device. +# @flush_total_time_ns: Total time spent on cache flushes in +# nanoseconds (since 0.15). # -# @rd_merged: Number of read requests that have been merged into another -# request (Since 2.3). +# @unmap_total_time_ns: Total time spent on unmap operations in +# nanoseconds (Since 4.2) # -# @wr_merged: Number of write requests that have been merged into another -# request (Since 2.3). +# @wr_highest_offset: The offset after the greatest byte written to +# the device. The intended use of this information is for +# growable sparse files (like qcow2) that are used on top of a +# physical device. # -# @unmap_merged: Number of unmap requests that have been merged into another -# request (Since 4.2) +# @rd_merged: Number of read requests that have been merged into +# another request (Since 2.3). # -# @idle_time_ns: Time since the last I/O operation, in -# nanoseconds. If the field is absent it means that -# there haven't been any operations yet (Since 2.5). +# @wr_merged: Number of write requests that have been merged into +# another request (Since 2.3). +# +# @zone_append_merged: Number of zone append requests that have been +# merged into another request (since 8.1) +# +# @unmap_merged: Number of unmap requests that have been merged into +# another request (Since 4.2) +# +# @idle_time_ns: Time since the last I/O operation, in nanoseconds. +# If the field is absent it means that there haven't been any +# operations yet (Since 2.5). # # @failed_rd_operations: The number of failed read operations -# performed by the device (Since 2.5) +# performed by the device (Since 2.5) # # @failed_wr_operations: The number of failed write operations -# performed by the device (Since 2.5) +# performed by the device (Since 2.5) +# +# @failed_zone_append_operations: The number of failed zone append +# write operations performed by the zoned devices (since 8.1) # # @failed_flush_operations: The number of failed flush operations -# performed by the device (Since 2.5) +# performed by the device (Since 2.5) # -# @failed_unmap_operations: The number of failed unmap operations performed -# by the device (Since 4.2) +# @failed_unmap_operations: The number of failed unmap operations +# performed by the device (Since 4.2) # # @invalid_rd_operations: The number of invalid read operations -# performed by the device (Since 2.5) +# performed by the device (Since 2.5) # # @invalid_wr_operations: The number of invalid write operations -# performed by the device (Since 2.5) +# performed by the device (Since 2.5) +# +# @invalid_zone_append_operations: The number of invalid zone append +# operations performed by the zoned device (since 8.1) # # @invalid_flush_operations: The number of invalid flush operations -# performed by the device (Since 2.5) +# performed by the device (Since 2.5) # -# @invalid_unmap_operations: The number of invalid unmap operations performed -# by the device (Since 4.2) +# @invalid_unmap_operations: The number of invalid unmap operations +# performed by the device (Since 4.2) # # @account_invalid: Whether invalid operations are included in the -# last access statistics (Since 2.5) +# last access statistics (Since 2.5) # # @account_failed: Whether failed operations are included in the -# latency and last access statistics (Since 2.5) +# latency and last access statistics (Since 2.5) # # @timed_stats: Statistics specific to the set of previously defined -# intervals of time (Since 2.5) +# intervals of time (Since 2.5) # -# @rd_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0) +# @rd_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0) # -# @wr_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0) +# @wr_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0) # -# @flush_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0) +# @zone_append_latency_histogram: @BlockLatencyHistogramInfo. +# (since 8.1) +# +# @flush_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0) # # Since: 0.14 ## { 'struct': 'BlockDeviceStats', - 'data': {'rd_bytes': 'int', 'wr_bytes': 'int', 'unmap_bytes' : 'int', - 'rd_operations': 'int', 'wr_operations': 'int', + 'data': {'rd_bytes': 'int', 'wr_bytes': 'int', 'zone_append_bytes': 'int', + 'unmap_bytes' : 'int', 'rd_operations': 'int', + 'wr_operations': 'int', 'zone_append_operations': 'int', 'flush_operations': 'int', 'unmap_operations': 'int', 'rd_total_time_ns': 'int', 'wr_total_time_ns': 'int', - 'flush_total_time_ns': 'int', 'unmap_total_time_ns': 'int', - 'wr_highest_offset': 'int', - 'rd_merged': 'int', 'wr_merged': 'int', 'unmap_merged': 'int', - '*idle_time_ns': 'int', + 'zone_append_total_time_ns': 'int', 'flush_total_time_ns': 'int', + 'unmap_total_time_ns': 'int', 'wr_highest_offset': 'int', + 'rd_merged': 'int', 'wr_merged': 'int', 'zone_append_merged': 'int', + 'unmap_merged': 'int', '*idle_time_ns': 'int', 'failed_rd_operations': 'int', 'failed_wr_operations': 'int', - 'failed_flush_operations': 'int', 'failed_unmap_operations': 'int', - 'invalid_rd_operations': 'int', 'invalid_wr_operations': 'int', + 'failed_zone_append_operations': 'int', + 'failed_flush_operations': 'int', + 'failed_unmap_operations': 'int', 'invalid_rd_operations': 'int', + 'invalid_wr_operations': 'int', + 'invalid_zone_append_operations': 'int', 'invalid_flush_operations': 'int', 'invalid_unmap_operations': 'int', 'account_invalid': 'bool', 'account_failed': 'bool', 'timed_stats': ['BlockDeviceTimedStats'], '*rd_latency_histogram': 'BlockLatencyHistogramInfo', '*wr_latency_histogram': 'BlockLatencyHistogramInfo', + '*zone_append_latency_histogram': 'BlockLatencyHistogramInfo', '*flush_latency_histogram': 'BlockLatencyHistogramInfo' } } ## @@ -904,11 +1062,11 @@ # # File driver statistics # -# @discard-nb-ok: The number of successful discard operations performed by -# the driver. +# @discard-nb-ok: The number of successful discard operations +# performed by the driver. # -# @discard-nb-failed: The number of failed discard operations performed by -# the driver. +# @discard-nb-failed: The number of failed discard operations +# performed by the driver. # # @discard-bytes-ok: The number of bytes discarded by the driver. # @@ -927,11 +1085,11 @@ # # @completion-errors: The number of completion errors. # -# @aligned-accesses: The number of aligned accesses performed by -# the driver. +# @aligned-accesses: The number of aligned accesses performed by the +# driver. # # @unaligned-accesses: The number of unaligned accesses performed by -# the driver. +# the driver. # # Since: 5.2 ## @@ -946,6 +1104,8 @@ # # Block driver specific statistics # +# @driver: block driver name +# # Since: 4.2 ## { 'union': 'BlockStatsSpecific', @@ -963,24 +1123,24 @@ # Statistics of a virtual block device or a block backing device. # # @device: If the stats are for a virtual block device, the name -# corresponding to the virtual block device. +# corresponding to the virtual block device. # -# @node-name: The node name of the device. (Since 2.3) +# @node-name: The node name of the device. (Since 2.3) # -# @qdev: The qdev ID, or if no ID is assigned, the QOM path of the block -# device. (since 3.0) +# @qdev: The qdev ID, or if no ID is assigned, the QOM path of the +# block device. (since 3.0) # # @stats: A @BlockDeviceStats for the device. # -# @driver-specific: Optional driver-specific stats. (Since 4.2) +# @driver-specific: Optional driver-specific stats. (Since 4.2) # # @parent: This describes the file block device if it has one. -# Contains recursively the statistics of the underlying -# protocol (e.g. the host file for a qcow2 image). If there is -# no underlying protocol, this field is omitted +# Contains recursively the statistics of the underlying protocol +# (e.g. the host file for a qcow2 image). If there is no +# underlying protocol, this field is omitted # # @backing: This describes the backing block device if it has one. -# (Since 2.0) +# (Since 2.0) # # Since: 0.14 ## @@ -997,12 +1157,12 @@ # Query the @BlockStats for all virtual block devices. # # @query-nodes: If true, the command will query all the block nodes -# that have a node name, in a list which will include "parent" -# information, but not "backing". -# If false or omitted, the behavior is as before - query all the -# device backends, recursively including their "parent" and -# "backing". Filter nodes that were created implicitly are -# skipped over in this mode. (Since 2.3) +# that have a node name, in a list which will include "parent" +# information, but not "backing". If false or omitted, the +# behavior is as before - query all the device backends, +# recursively including their "parent" and "backing". Filter nodes +# that were created implicitly are skipped over in this mode. +# (Since 2.3) # # Returns: A list of @BlockStats for each virtual block devices. # @@ -1010,131 +1170,131 @@ # # Example: # -# -> { "execute": "query-blockstats" } -# <- { -# "return":[ -# { -# "device":"ide0-hd0", -# "parent":{ -# "stats":{ -# "wr_highest_offset":3686448128, -# "wr_bytes":9786368, -# "wr_operations":751, -# "rd_bytes":122567168, -# "rd_operations":36772 -# "wr_total_times_ns":313253456 -# "rd_total_times_ns":3465673657 -# "flush_total_times_ns":49653 -# "flush_operations":61, -# "rd_merged":0, -# "wr_merged":0, -# "idle_time_ns":2953431879, -# "account_invalid":true, -# "account_failed":false -# } -# }, -# "stats":{ -# "wr_highest_offset":2821110784, -# "wr_bytes":9786368, -# "wr_operations":692, -# "rd_bytes":122739200, -# "rd_operations":36604 -# "flush_operations":51, -# "wr_total_times_ns":313253456 -# "rd_total_times_ns":3465673657 -# "flush_total_times_ns":49653, -# "rd_merged":0, -# "wr_merged":0, -# "idle_time_ns":2953431879, -# "account_invalid":true, -# "account_failed":false -# }, -# "qdev": "/machine/unattached/device[23]" -# }, -# { -# "device":"ide1-cd0", -# "stats":{ -# "wr_highest_offset":0, -# "wr_bytes":0, -# "wr_operations":0, -# "rd_bytes":0, -# "rd_operations":0 -# "flush_operations":0, -# "wr_total_times_ns":0 -# "rd_total_times_ns":0 -# "flush_total_times_ns":0, -# "rd_merged":0, -# "wr_merged":0, -# "account_invalid":false, -# "account_failed":false -# }, -# "qdev": "/machine/unattached/device[24]" -# }, -# { -# "device":"floppy0", -# "stats":{ -# "wr_highest_offset":0, -# "wr_bytes":0, -# "wr_operations":0, -# "rd_bytes":0, -# "rd_operations":0 -# "flush_operations":0, -# "wr_total_times_ns":0 -# "rd_total_times_ns":0 -# "flush_total_times_ns":0, -# "rd_merged":0, -# "wr_merged":0, -# "account_invalid":false, -# "account_failed":false -# }, -# "qdev": "/machine/unattached/device[16]" -# }, -# { -# "device":"sd0", -# "stats":{ -# "wr_highest_offset":0, -# "wr_bytes":0, -# "wr_operations":0, -# "rd_bytes":0, -# "rd_operations":0 -# "flush_operations":0, -# "wr_total_times_ns":0 -# "rd_total_times_ns":0 -# "flush_total_times_ns":0, -# "rd_merged":0, -# "wr_merged":0, -# "account_invalid":false, -# "account_failed":false -# } -# } -# ] -# } -# +# -> { "execute": "query-blockstats" } +# <- { +# "return":[ +# { +# "device":"ide0-hd0", +# "parent":{ +# "stats":{ +# "wr_highest_offset":3686448128, +# "wr_bytes":9786368, +# "wr_operations":751, +# "rd_bytes":122567168, +# "rd_operations":36772 +# "wr_total_times_ns":313253456 +# "rd_total_times_ns":3465673657 +# "flush_total_times_ns":49653 +# "flush_operations":61, +# "rd_merged":0, +# "wr_merged":0, +# "idle_time_ns":2953431879, +# "account_invalid":true, +# "account_failed":false +# } +# }, +# "stats":{ +# "wr_highest_offset":2821110784, +# "wr_bytes":9786368, +# "wr_operations":692, +# "rd_bytes":122739200, +# "rd_operations":36604 +# "flush_operations":51, +# "wr_total_times_ns":313253456 +# "rd_total_times_ns":3465673657 +# "flush_total_times_ns":49653, +# "rd_merged":0, +# "wr_merged":0, +# "idle_time_ns":2953431879, +# "account_invalid":true, +# "account_failed":false +# }, +# "qdev": "/machine/unattached/device[23]" +# }, +# { +# "device":"ide1-cd0", +# "stats":{ +# "wr_highest_offset":0, +# "wr_bytes":0, +# "wr_operations":0, +# "rd_bytes":0, +# "rd_operations":0 +# "flush_operations":0, +# "wr_total_times_ns":0 +# "rd_total_times_ns":0 +# "flush_total_times_ns":0, +# "rd_merged":0, +# "wr_merged":0, +# "account_invalid":false, +# "account_failed":false +# }, +# "qdev": "/machine/unattached/device[24]" +# }, +# { +# "device":"floppy0", +# "stats":{ +# "wr_highest_offset":0, +# "wr_bytes":0, +# "wr_operations":0, +# "rd_bytes":0, +# "rd_operations":0 +# "flush_operations":0, +# "wr_total_times_ns":0 +# "rd_total_times_ns":0 +# "flush_total_times_ns":0, +# "rd_merged":0, +# "wr_merged":0, +# "account_invalid":false, +# "account_failed":false +# }, +# "qdev": "/machine/unattached/device[16]" +# }, +# { +# "device":"sd0", +# "stats":{ +# "wr_highest_offset":0, +# "wr_bytes":0, +# "wr_operations":0, +# "rd_bytes":0, +# "rd_operations":0 +# "flush_operations":0, +# "wr_total_times_ns":0 +# "rd_total_times_ns":0 +# "flush_total_times_ns":0, +# "rd_merged":0, +# "wr_merged":0, +# "account_invalid":false, +# "account_failed":false +# } +# } +# ] +# } ## { 'command': 'query-blockstats', 'data': { '*query-nodes': 'bool' }, - 'returns': ['BlockStats'] } + 'returns': ['BlockStats'], + 'allow-preconfig': true } ## # @BlockdevOnError: # # An enumeration of possible behaviors for errors on I/O operations. -# The exact meaning depends on whether the I/O was initiated by a guest -# or by a block job +# The exact meaning depends on whether the I/O was initiated by a +# guest or by a block job # -# @report: for guest operations, report the error to the guest; -# for jobs, cancel the job +# @report: for guest operations, report the error to the guest; for +# jobs, cancel the job # # @ignore: ignore the error, only report a QMP event (BLOCK_IO_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. +# 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. # -# @stop: for guest operations, stop the virtual machine; -# for jobs, pause the job +# @stop: for guest operations, stop the virtual machine; for jobs, +# pause the job # # @auto: inherit the error handling policy of the backend (since: 2.7) # @@ -1155,10 +1315,11 @@ # # @none: only copy data written from now on # -# @incremental: only copy data described by the dirty bitmap. (since: 2.4) +# @incremental: only copy data described by the dirty bitmap. +# (since: 2.4) # -# @bitmap: only copy data described by the dirty bitmap. (since: 4.2) -# Behavior on completion is determined by the BitmapSyncMode. +# @bitmap: only copy data described by the dirty bitmap. (since: 4.2) +# Behavior on completion is determined by the BitmapSyncMode. # # Since: 1.3 ## @@ -1168,17 +1329,18 @@ ## # @BitmapSyncMode: # -# An enumeration of possible behaviors for the synchronization of a bitmap -# when used for data copy operations. +# An enumeration of possible behaviors for the synchronization of a +# bitmap when used for data copy operations. # -# @on-success: The bitmap is only synced when the operation is successful. -# This is the behavior always used for 'INCREMENTAL' backups. +# @on-success: The bitmap is only synced when the operation is +# successful. This is the behavior always used for 'INCREMENTAL' +# backups. # # @never: The bitmap is never synchronized with the operation, and is -# treated solely as a read-only manifest of blocks to copy. +# treated solely as a read-only manifest of blocks to copy. # # @always: The bitmap is always synchronized with the operation, -# regardless of whether or not the operation was successful. +# regardless of whether or not the operation was successful. # # Since: 4.2 ## @@ -1194,15 +1356,28 @@ # @background: copy data in background only. # # @write-blocking: when data is written to the source, write it -# (synchronously) to the target as well. In -# addition, data is copied in background just like in -# @background mode. +# (synchronously) to the target as well. In addition, data is +# copied in background just like in @background mode. # # Since: 3.0 ## { 'enum': 'MirrorCopyMode', 'data': ['background', 'write-blocking'] } +## +# @BlockJobInfoMirror: +# +# Information specific to mirror block jobs. +# +# @actively-synced: Whether the source is actively synced to the +# target, i.e. same data and new writes are done synchronously to +# both. +# +# Since: 8.2 +## +{ 'struct': 'BlockJobInfoMirror', + 'data': { 'actively-synced': 'bool' } } + ## # @BlockJobInfo: # @@ -1210,21 +1385,22 @@ # # @type: the job type ('stream' for image streaming) # -# @device: The job identifier. Originally the device name but other -# values are allowed since QEMU 2.7 +# @device: The job identifier. Originally the device name but other +# values are allowed since QEMU 2.7 # -# @len: Estimated @offset value at the completion of the job. This value can -# arbitrarily change while the job is running, in both directions. +# @len: Estimated @offset value at the completion of the job. This +# value can arbitrarily change while the job is running, in both +# directions. # -# @offset: Progress made until now. The unit is arbitrary and the value can -# only meaningfully be used for the ratio of @offset to @len. The -# value is monotonically increasing. +# @offset: Progress made until now. The unit is arbitrary and the +# value can only meaningfully be used for the ratio of @offset to +# @len. The value is monotonically increasing. # -# @busy: false if the job is known to be in a quiescent state, with -# no pending I/O. Since 1.3. +# @busy: false if the job is known to be in a quiescent state, with no +# pending I/O. (Since 1.3) # -# @paused: whether the job is paused or, if @busy is true, will -# pause itself as soon as possible. Since 1.3. +# @paused: whether the job is paused or, if @busy is true, will pause +# itself as soon as possible. (Since 1.3) # # @speed: the rate limit, bytes per second # @@ -1234,24 +1410,26 @@ # # @status: Current job state/status (since 2.12) # -# @auto-finalize: Job will finalize itself when PENDING, moving to -# the CONCLUDED state. (since 2.12) +# @auto-finalize: Job will finalize itself when PENDING, moving to the +# CONCLUDED state. (since 2.12) # -# @auto-dismiss: Job will dismiss itself when CONCLUDED, moving to the NULL -# state and disappearing from the query list. (since 2.12) +# @auto-dismiss: Job will dismiss itself when CONCLUDED, moving to the +# NULL state and disappearing from the query list. (since 2.12) # # @error: Error information if the job did not complete successfully. -# Not set if the job completed successfully. (since 2.12.1) +# Not set if the job completed successfully. (since 2.12.1) # # Since: 1.1 ## -{ 'struct': 'BlockJobInfo', - 'data': {'type': 'str', 'device': 'str', 'len': 'int', +{ 'union': 'BlockJobInfo', + 'base': {'type': 'JobType', 'device': 'str', 'len': 'int', 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int', 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool', 'status': 'JobStatus', 'auto-finalize': 'bool', 'auto-dismiss': 'bool', - '*error': 'str' } } + '*error': 'str' }, + 'discriminator': 'type', + 'data': { 'mirror': 'BlockJobInfoMirror' } } ## # @query-block-jobs: @@ -1262,7 +1440,8 @@ # # Since: 1.1 ## -{ 'command': 'query-block-jobs', 'returns': ['BlockJobInfo'] } +{ 'command': 'query-block-jobs', 'returns': ['BlockJobInfo'], + 'allow-preconfig': true } ## # @block_resize: @@ -1277,35 +1456,35 @@ # # @size: new image size in bytes # -# Returns: - nothing on success -# - If @device is not a valid block device, DeviceNotFound +# Errors: +# - If @device is not a valid block device, DeviceNotFound # # Since: 0.14 # # Example: # -# -> { "execute": "block_resize", -# "arguments": { "device": "scratch", "size": 1073741824 } } -# <- { "return": {} } -# +# -> { "execute": "block_resize", +# "arguments": { "device": "scratch", "size": 1073741824 } } +# <- { "return": {} } ## { 'command': 'block_resize', 'data': { '*device': 'str', '*node-name': 'str', 'size': 'int' }, - 'coroutine': true } + 'coroutine': true, + 'allow-preconfig': true } ## # @NewImageMode: # -# An enumeration that tells QEMU how to set the backing file path in -# a new image file. +# An enumeration that tells QEMU how to set the backing file path in a +# new image file. # # @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 ## @@ -1319,18 +1498,20 @@ # # @device: the name of the device to take a snapshot of. # -# @node-name: graph node name to generate the snapshot from (Since 2.0) +# @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. +# @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. # -# @snapshot-node-name: the graph node name of the new image (Since 2.0) +# @snapshot-node-name: the graph node name of the new image (Since +# 2.0) # # @format: the format of the overlay image, default is 'qcow2'. # # @mode: whether and how QEMU should create a new image, default is -# 'absolute-paths'. +# 'absolute-paths'. ## { 'struct': 'BlockdevSnapshotSync', 'data': { '*device': 'str', '*node-name': 'str', @@ -1343,9 +1524,9 @@ # @node: device or node name that will have a snapshot taken. # # @overlay: reference to the existing block device that will become -# the overlay of @node, as part of taking the snapshot. -# It must not have a current backing file (this can be -# achieved by passing "backing": null to blockdev-add). +# the overlay of @node, as part of taking the snapshot. It must +# not have a current backing file (this can be achieved by passing +# "backing": null to blockdev-add). # # Since: 2.5 ## @@ -1355,20 +1536,20 @@ ## # @BackupPerf: # -# Optional parameters for backup. These parameters don't affect +# Optional parameters for backup. These parameters don't affect # functionality, but may significantly affect performance. # -# @use-copy-range: Use copy offloading. Default false. +# @use-copy-range: Use copy offloading. Default false. # -# @max-workers: Maximum number of parallel requests for the sustained background -# copying process. Doesn't influence copy-before-write operations. -# Default 64. +# @max-workers: Maximum number of parallel requests for the sustained +# background copying process. Doesn't influence copy-before-write +# operations. Default 64. # -# @max-chunk: Maximum request length for the sustained background copying -# process. Doesn't influence copy-before-write operations. -# 0 means unlimited. If max-chunk is non-zero then it should not be -# less than job cluster size which is calculated as maximum of -# target image cluster size and 64k. Default 0. +# @max-chunk: Maximum request length for the sustained background +# copying process. Doesn't influence copy-before-write +# operations. 0 means unlimited. If max-chunk is non-zero then +# it should not be less than job cluster size which is calculated +# as maximum of target image cluster size and 64k. Default 0. # # Since: 6.0 ## @@ -1379,66 +1560,65 @@ ## # @BackupCommon: # -# @job-id: identifier for the newly-created block job. If -# omitted, the device name will be used. (Since 2.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 which should be copied. +# @device: the device name or node-name of a root node which should be +# copied. # -# @sync: what parts of the disk image should be copied to the destination -# (all the disk, only the sectors allocated in the topmost image, from a -# dirty bitmap, or only new I/O). +# @sync: what parts of the disk image should be copied to the +# destination (all the disk, only the sectors allocated in the +# topmost image, from a dirty bitmap, or only new I/O). # -# @speed: the maximum speed, in bytes per second. The default is 0, -# for unlimited. +# @speed: the maximum speed, in bytes per second. The default is 0, +# for unlimited. # -# @bitmap: The name of a dirty bitmap to use. -# Must be present if sync is "bitmap" or "incremental". -# Can be present if sync is "full" or "top". -# Must not be present otherwise. -# (Since 2.4 (drive-backup), 3.1 (blockdev-backup)) +# @bitmap: The name of a dirty bitmap to use. Must be present if sync +# is "bitmap" or "incremental". Can be present if sync is "full" +# or "top". Must not be present otherwise. +# (Since 2.4 (drive-backup), 3.1 (blockdev-backup)) # -# @bitmap-mode: Specifies the type of data the bitmap should contain after -# the operation concludes. -# Must be present if a bitmap was provided, -# Must NOT be present otherwise. (Since 4.2) +# @bitmap-mode: Specifies the type of data the bitmap should contain +# after the operation concludes. Must be present if a bitmap was +# provided, Must NOT be present otherwise. (Since 4.2) # # @compress: true to compress data, if the target format supports it. -# (default: false) (since 2.8) +# (default: false) (since 2.8) # # @on-source-error: the action to take on an error on the source, -# default 'report'. 'stop' and 'enospc' can only be used -# if the block device supports io-status (see BlockInfo). +# default 'report'. 'stop' and 'enospc' can only be used if the +# block device supports io-status (see BlockInfo). # # @on-target-error: the action to take on an error on the target, -# default 'report' (no limitations, since this applies to -# a different block device than @device). +# default 'report' (no limitations, since this applies to a +# different block device than @device). # -# @auto-finalize: When false, this job will wait in a PENDING state after it has -# finished its work, waiting for @block-job-finalize before -# making any block graph changes. -# When true, this job will automatically -# perform its abort or commit actions. -# Defaults to true. (Since 2.12) +# @auto-finalize: When false, this job will wait in a PENDING state +# after it has finished its work, waiting for @block-job-finalize +# before making any block graph changes. When true, this job will +# automatically perform its abort or commit actions. Defaults to +# true. (Since 2.12) # -# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it -# has completely ceased all work, and awaits @block-job-dismiss. -# When true, this job will automatically disappear from the query -# list without user intervention. -# Defaults to true. (Since 2.12) +# @auto-dismiss: When false, this job will wait in a CONCLUDED state +# after it has completely ceased all work, and awaits +# @block-job-dismiss. When true, this job will automatically +# disappear from the query list without user intervention. +# Defaults to true. (Since 2.12) # # @filter-node-name: the node name that should be assigned to the -# filter driver that the backup job inserts into the graph -# above node specified by @drive. If this option is not given, -# a node name is autogenerated. (Since: 4.2) +# filter driver that the backup job inserts into the graph above +# node specified by @drive. If this option is not given, a node +# name is autogenerated. (Since: 4.2) # -# @x-perf: Performance options. (Since 6.0) +# @x-perf: Performance options. (Since 6.0) # # Features: +# # @unstable: Member @x-perf is experimental. # # 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 ## @@ -1457,15 +1637,15 @@ ## # @DriveBackup: # -# @target: the target of the new image. If the file exists, or if it -# is a device, the existing file/device will be used as the new -# destination. If it does not exist, a new file will be created. +# @target: the target of the new image. If the file exists, or if it +# is a device, the existing file/device will be used as the new +# destination. If it does not exist, a new file will be created. # -# @format: the format of the new destination, default is to -# probe if @mode is 'existing', else the format of the source +# @format: the format of the new destination, default is to probe if +# @mode is 'existing', else the format of the source # # @mode: whether and how QEMU should create a new image, default is -# 'absolute-paths'. +# 'absolute-paths'. # # Since: 1.6 ## @@ -1493,23 +1673,23 @@ # # For the arguments, see the documentation of BlockdevSnapshotSync. # -# Returns: - nothing on success -# - If @device is not a valid block device, DeviceNotFound +# Errors: +# - If @device is not a valid block device, DeviceNotFound # # Since: 0.14 # # Example: # -# -> { "execute": "blockdev-snapshot-sync", -# "arguments": { "device": "ide-hd0", -# "snapshot-file": -# "/some/place/my-image", -# "format": "qcow2" } } -# <- { "return": {} } -# +# -> { "execute": "blockdev-snapshot-sync", +# "arguments": { "device": "ide-hd0", +# "snapshot-file": +# "/some/place/my-image", +# "format": "qcow2" } } +# <- { "return": {} } ## { 'command': 'blockdev-snapshot-sync', - 'data': 'BlockdevSnapshotSync' } + 'data': 'BlockdevSnapshotSync', + 'allow-preconfig': true } ## # @blockdev-snapshot: @@ -1518,83 +1698,82 @@ # # Take a snapshot, by installing 'node' as the backing image of # 'overlay'. Additionally, if 'node' is associated with a block -# device, the block device changes to using 'overlay' as its new active -# image. +# device, the block device changes to using 'overlay' as its new +# active image. # # 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) +# +# @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: # -# -> { "execute": "blockdev-add", -# "arguments": { "driver": "qcow2", -# "node-name": "node1534", -# "file": { "driver": "file", -# "filename": "hd1.qcow2" }, -# "backing": null } } +# -> { "execute": "blockdev-add", +# "arguments": { "driver": "qcow2", +# "node-name": "node1534", +# "file": { "driver": "file", +# "filename": "hd1.qcow2" }, +# "backing": null } } # -# <- { "return": {} } -# -# -> { "execute": "blockdev-snapshot", -# "arguments": { "node": "ide-hd0", -# "overlay": "node1534" } } -# <- { "return": {} } +# <- { "return": {} } # +# -> { "execute": "blockdev-snapshot", +# "arguments": { "node": "ide-hd0", +# "overlay": "node1534" } } +# <- { "return": {} } ## { 'command': 'blockdev-snapshot', 'data': 'BlockdevSnapshot', - 'features': [ 'allow-write-only-overlay' ] } + 'features': [ 'allow-write-only-overlay' ], + 'allow-preconfig': true } ## # @change-backing-file: # # Change the backing file in the image file metadata. This does not # cause QEMU to reopen the image file to reparse the backing filename -# (it may, however, perform a reopen to change permissions from -# r/o -> r/w -> r/o, if needed). The new backing file string is written -# into the image file metadata, and the QEMU internal strings are -# updated. +# (it may, however, perform a reopen to change permissions from r/o -> +# r/w -> r/o, if needed). The new backing file string is written into +# the image file metadata, and the QEMU internal strings are updated. # # @image-node-name: The name of the block driver state node of the -# image to modify. The "device" argument is used -# to verify "image-node-name" is in the chain -# described by "device". +# image to modify. The "device" argument is used 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. +# 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 +# Errors: +# - If "device" does not exist or cannot be determined, +# DeviceNotFound # # Since: 2.1 ## { 'command': 'change-backing-file', 'data': { 'device': 'str', 'image-node-name': 'str', - 'backing-file': 'str' } } + 'backing-file': 'str' }, + 'allow-preconfig': true } ## # @block-commit: # -# Live commit of data from overlay image nodes into backing nodes - i.e., -# writes data between 'top' and 'base' into 'base'. +# Live commit of data from overlay image nodes into backing nodes - +# i.e., writes data between 'top' and 'base' into 'base'. # -# If top == base, that is an error. -# If top has no overlays on top of it, or if it is in use by a writer, -# the job will not be completed by itself. The 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 has no overlays on top of +# it, or if it is in use by a writer, the job will not be completed by +# itself. The 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 @@ -1602,160 +1781,165 @@ # size to match the size of the smaller top, you can safely truncate # it yourself once the commit operation successfully completes. # -# @job-id: identifier for the newly-created block job. If -# omitted, the device name will be used. (Since 2.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 # # @base-node: The node name of the backing image to write data into. -# If not specified, this is the deepest backing image. -# (since: 3.1) +# If not specified, this is the deepest backing image. +# (since: 3.1) # -# @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 +# @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 # # @top-node: The node name of the backing image within the image chain -# which contains the topmost data to be committed down. If -# not specified, this is the active layer. (since: 3.1) +# which contains the topmost data to be committed down. If not +# specified, this is the active layer. (since: 3.1) # -# @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 +# @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 # # @backing-file: The backing file string to write into the overlay -# image of 'top'. If 'top' does not have an overlay -# image, or if 'top' is in use by a writer, specifying -# a backing file string is an error. +# image of 'top'. If 'top' does not have an overlay image, or if +# 'top' is in use by a writer, 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. +# 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 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) +# +# @backing-mask-protocol: If true, replace any protocol mentioned in +# the 'backing file format' with 'raw', rather than storing the +# protocol name as the backing format. Can be used even when no +# image header will be updated (default false; since 9.0). # # @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) +# @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 -# above @top. If this option is not given, a node name is -# autogenerated. (Since: 2.9) +# filter driver that the commit job inserts into the graph above +# @top. If this option is not given, a node name is +# autogenerated. (Since: 2.9) # -# @auto-finalize: When false, this job will wait in a PENDING state after it has -# finished its work, waiting for @block-job-finalize before -# making any block graph changes. -# When true, this job will automatically -# perform its abort or commit actions. -# Defaults to true. (Since 3.1) +# @auto-finalize: When false, this job will wait in a PENDING state +# after it has finished its work, waiting for @block-job-finalize +# before making any block graph changes. When true, this job will +# automatically perform its abort or commit actions. Defaults to +# true. (Since 3.1) # -# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it -# has completely ceased all work, and awaits @block-job-dismiss. -# When true, this job will automatically disappear from the query -# list without user intervention. -# Defaults to true. (Since 3.1) +# @auto-dismiss: When false, this job will wait in a CONCLUDED state +# after it has completely ceased all work, and awaits +# @block-job-dismiss. When true, this job will automatically +# disappear from the query list without user intervention. +# Defaults to true. (Since 3.1) # # 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. +# @deprecated: Members @base and @top are deprecated. Use @base-node +# and @top-node instead. +# +# Errors: +# - If @device does not exist, DeviceNotFound +# - Any other error returns a GenericError. # # Since: 1.3 # # Example: # -# -> { "execute": "block-commit", -# "arguments": { "device": "virtio0", -# "top": "/tmp/snap1.qcow2" } } -# <- { "return": {} } -# +# -> { "execute": "block-commit", +# "arguments": { "device": "virtio0", +# "top": "/tmp/snap1.qcow2" } } +# <- { "return": {} } ## { 'command': 'block-commit', 'data': { '*job-id': 'str', 'device': 'str', '*base-node': 'str', '*base': { 'type': 'str', 'features': [ 'deprecated' ] }, '*top-node': 'str', '*top': { 'type': 'str', 'features': [ 'deprecated' ] }, - '*backing-file': 'str', '*speed': 'int', + '*backing-file': 'str', '*backing-mask-protocol': 'bool', + '*speed': 'int', '*on-error': 'BlockdevOnError', '*filter-node-name': 'str', - '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' }, + 'allow-preconfig': true } ## # @drive-backup: # -# Start a point-in-time copy of a block device to a new destination. The -# status of ongoing drive-backup operations can be checked with -# query-block-jobs where the BlockJobInfo.type field has the value 'backup'. -# The operation can be stopped before it has completed using the -# block-job-cancel command. +# Start a point-in-time copy of a block device to a new destination. +# The status of ongoing drive-backup operations can be checked with +# query-block-jobs where the BlockJobInfo.type field has the value +# 'backup'. The operation can be stopped before it has completed using +# the block-job-cancel command. # # Features: -# @deprecated: This command is deprecated. Use @blockdev-backup instead. # -# Returns: - nothing on success -# - If @device is not a valid block device, GenericError +# @deprecated: This command is deprecated. Use @blockdev-backup +# instead. +# +# Errors: +# - If @device is not a valid block device, GenericError # # Since: 1.6 # # Example: # -# -> { "execute": "drive-backup", -# "arguments": { "device": "drive0", -# "sync": "full", -# "target": "backup.img" } } -# <- { "return": {} } -# +# -> { "execute": "drive-backup", +# "arguments": { "device": "drive0", +# "sync": "full", +# "target": "backup.img" } } +# <- { "return": {} } ## { 'command': 'drive-backup', 'boxed': true, - 'data': 'DriveBackup', 'features': ['deprecated'] } + 'data': 'DriveBackup', 'features': ['deprecated'], + 'allow-preconfig': true } ## # @blockdev-backup: # -# Start a point-in-time copy of a block device to a new destination. The -# status of ongoing blockdev-backup operations can be checked with -# query-block-jobs where the BlockJobInfo.type field has the value 'backup'. -# The operation can be stopped before it has completed using the -# block-job-cancel command. +# Start a point-in-time copy of a block device to a new destination. +# The status of ongoing blockdev-backup operations can be checked with +# query-block-jobs where the BlockJobInfo.type field has the value +# 'backup'. 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 +# Errors: +# - If @device is not a valid block device, DeviceNotFound # # Since: 2.3 # # Example: # -# -> { "execute": "blockdev-backup", -# "arguments": { "device": "src-id", -# "sync": "full", -# "target": "tgt-id" } } -# <- { "return": {} } -# +# -> { "execute": "blockdev-backup", +# "arguments": { "device": "src-id", +# "sync": "full", +# "target": "tgt-id" } } +# <- { "return": {} } ## { 'command': 'blockdev-backup', 'boxed': true, - 'data': 'BlockdevBackup' } + 'data': 'BlockdevBackup', + 'allow-preconfig': true } ## # @query-named-block-nodes: # # 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) +# @flat: Omit the nested data about backing image ("backing-image" +# key) if true. Default is false (Since 5.0) # # Returns: the list of BlockDeviceInfo # @@ -1763,57 +1947,57 @@ # # Example: # -# -> { "execute": "query-named-block-nodes" } -# <- { "return": [ { "ro":false, -# "drv":"qcow2", -# "encrypted":false, -# "file":"disks/test.qcow2", -# "node-name": "my-node", -# "backing_file_depth":1, -# "detect_zeroes":"off", -# "bps":1000000, -# "bps_rd":0, -# "bps_wr":0, -# "iops":1000000, -# "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, -# "iops_size": 0, -# "write_threshold": 0, -# "image":{ -# "filename":"disks/test.qcow2", -# "format":"qcow2", -# "virtual-size":2048000, -# "backing_file":"base.qcow2", -# "full-backing-filename":"disks/base.qcow2", -# "backing-filename-format":"qcow2", -# "snapshots":[ -# { -# "id": "1", -# "name": "snapshot1", -# "vm-state-size": 0, -# "date-sec": 10000200, -# "date-nsec": 12, -# "vm-clock-sec": 206, -# "vm-clock-nsec": 30 -# } -# ], -# "backing-image":{ -# "filename":"disks/base.qcow2", +# -> { "execute": "query-named-block-nodes" } +# <- { "return": [ { "ro":false, +# "drv":"qcow2", +# "encrypted":false, +# "file":"disks/test.qcow2", +# "node-name": "my-node", +# "backing_file_depth":1, +# "detect_zeroes":"off", +# "bps":1000000, +# "bps_rd":0, +# "bps_wr":0, +# "iops":1000000, +# "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, +# "iops_size": 0, +# "write_threshold": 0, +# "image":{ +# "filename":"disks/test.qcow2", # "format":"qcow2", -# "virtual-size":2048000 -# } -# } } ] } -# +# "virtual-size":2048000, +# "backing_file":"base.qcow2", +# "full-backing-filename":"disks/base.qcow2", +# "backing-filename-format":"qcow2", +# "snapshots":[ +# { +# "id": "1", +# "name": "snapshot1", +# "vm-state-size": 0, +# "date-sec": 10000200, +# "date-nsec": 12, +# "vm-clock-sec": 206, +# "vm-clock-nsec": 30 +# } +# ], +# "backing-image":{ +# "filename":"disks/base.qcow2", +# "format":"qcow2", +# "virtual-size":2048000 +# } +# } } ] } ## { 'command': 'query-named-block-nodes', 'returns': [ 'BlockDeviceInfo' ], - 'data': { '*flat': 'bool' } } + 'data': { '*flat': 'bool' }, + 'allow-preconfig': true } ## # @XDbgBlockGraphNodeType: @@ -1832,16 +2016,16 @@ ## # @XDbgBlockGraphNode: # -# @id: Block graph node identifier. This @id is generated only for -# x-debug-query-block-graph and does not relate to any other identifiers in -# Qemu. +# @id: Block graph node identifier. This @id is generated only for +# x-debug-query-block-graph and does not relate to any other +# identifiers in Qemu. # -# @type: Type of graph node. Can be one of block-backend, block-job or -# block-driver-state. +# @type: Type of graph node. Can be one of block-backend, block-job +# or block-driver-state. # -# @name: Human readable name of the node. Corresponds to node-name for -# block-driver-state nodes; is not guaranteed to be unique in the whole -# graph (with block-jobs and block-backends). +# @name: Human readable name of the node. Corresponds to node-name +# for block-driver-state nodes; is not guaranteed to be unique in +# the whole graph (with block-jobs and block-backends). # # Since: 4.0 ## @@ -1853,25 +2037,26 @@ # # Enum of base block permissions. # -# @consistent-read: A user that has the "permission" of consistent reads is -# guaranteed that their view of the contents of the block -# device is complete and self-consistent, representing the -# contents of a disk at a specific point. -# For most block devices (including their backing files) this -# is true, but the property cannot be maintained in a few -# situations like for intermediate nodes of a commit block -# job. +# @consistent-read: A user that has the "permission" of consistent +# reads is guaranteed that their view of the contents of the block +# device is complete and self-consistent, representing the +# contents of a disk at a specific point. For most block devices +# (including their backing files) this is true, but the property +# cannot be maintained in a few situations like for intermediate +# nodes of a commit block job. # -# @write: This permission is required to change the visible disk contents. +# @write: This permission is required to change the visible disk +# contents. # -# @write-unchanged: This permission (which is weaker than BLK_PERM_WRITE) is -# both enough and required for writes to the block node when -# the caller promises that the visible disk content doesn't -# change. -# As the BLK_PERM_WRITE permission is strictly stronger, -# either is sufficient to perform an unchanging write. +# @write-unchanged: This permission (which is weaker than +# BLK_PERM_WRITE) is both enough and required for writes to the +# block node when the caller promises that the visible disk +# content doesn't change. As the BLK_PERM_WRITE permission is +# strictly stronger, either is sufficient to perform an unchanging +# write. # -# @resize: This permission is required to change the size of a block node. +# @resize: This permission is required to change the size of a block +# node. # # Since: 4.0 ## @@ -1891,8 +2076,8 @@ # # @perm: granted permissions for the parent operating on the child # -# @shared-perm: permissions that can still be granted to other users of the -# child while it is still attached to this parent +# @shared-perm: permissions that can still be granted to other users +# of the child while it is still attached to this parent # # Since: 4.0 ## @@ -1917,113 +2102,114 @@ # Get the block graph. # # Features: +# # @unstable: This command is meant for debugging. # # Since: 4.0 ## { 'command': 'x-debug-query-block-graph', 'returns': 'XDbgBlockGraph', - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @drive-mirror: # -# Start mirroring a block device's writes to a new destination. target -# specifies the target of the new image. If the file exists, or if it -# is a device, it will be used as the new destination for writes. If -# it does not exist, a new file will be created. format specifies the -# format of the mirror image, default is to probe if mode='existing', -# else the format of the source. +# Start mirroring a block device's writes to a new destination. +# target specifies the target of the new image. If the file exists, +# or if it is a device, it will be used as the new destination for +# writes. If it does not exist, a new file will be created. @format +# specifies the 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 +# Errors: +# - If @device is not a valid block device, GenericError # # Since: 1.3 # # Example: # -# -> { "execute": "drive-mirror", -# "arguments": { "device": "ide-hd0", -# "target": "/some/place/my-image", -# "sync": "full", -# "format": "qcow2" } } -# <- { "return": {} } -# +# -> { "execute": "drive-mirror", +# "arguments": { "device": "ide-hd0", +# "target": "/some/place/my-image", +# "sync": "full", +# "format": "qcow2" } } +# <- { "return": {} } ## { 'command': 'drive-mirror', 'boxed': true, - 'data': 'DriveMirror' } + 'data': 'DriveMirror', + 'allow-preconfig': true } ## # @DriveMirror: # # A set of parameters describing drive mirror setup. # -# @job-id: identifier for the newly-created block job. If -# omitted, the device name will be used. (Since 2.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 whose writes should be -# mirrored. +# @device: the device name or node-name of a root node whose writes +# should be mirrored. # -# @target: the target of the new image. If the file exists, or if it -# is a device, the existing file/device will be used as the new -# destination. If it does not exist, a new file will be created. +# @target: the target of the new image. If the file exists, or if it +# is a device, the existing file/device will be used as the new +# destination. If it does not exist, a new file will be created. # -# @format: the format of the new destination, default is to -# probe if @mode is 'existing', else the format of the source +# @format: the format of the new destination, default is to probe if +# @mode is 'existing', else the format of the source # -# @node-name: the new block driver state node name in the graph -# (Since 2.1) +# @node-name: the new block driver state node name in the graph (Since +# 2.1) # # @replaces: with sync=full graph node name to be replaced by the new -# image when a whole image copy is done. This can be used to repair -# broken Quorum files. By default, @device is replaced, although -# implicitly created filters on it are kept. (Since 2.1) +# image when a whole image copy is done. This can be used to +# repair broken Quorum files. By default, @device is replaced, +# although implicitly created filters on it are kept. (Since 2.1) # # @mode: whether and how QEMU should create a new image, default is -# 'absolute-paths'. +# 'absolute-paths'. # # @speed: the maximum speed, in bytes per second # -# @sync: what parts of the disk image should be copied to the destination -# (all the disk, only the sectors allocated in the topmost image, or -# only new I/O). +# @sync: what parts of the disk image should be copied to the +# destination (all the disk, only the sectors allocated in the +# topmost image, or only new I/O). # -# @granularity: granularity of the dirty bitmap, default is 64K -# if the image format doesn't have clusters, 4K if the clusters -# are smaller than that, else the cluster size. Must be a -# power of 2 between 512 and 64M (since 1.4). +# @granularity: granularity of the dirty bitmap, default is 64K if the +# image format doesn't have clusters, 4K if the clusters are +# smaller than that, else the cluster size. Must be a power of 2 +# between 512 and 64M (since 1.4). # -# @buf-size: maximum amount of data in flight from source to -# target (since 1.4). +# @buf-size: maximum amount of data in flight from source to target +# (since 1.4). # # @on-source-error: the action to take on an error on the source, -# default 'report'. 'stop' and 'enospc' can only be used -# if the block device supports io-status (see BlockInfo). +# default 'report'. 'stop' and 'enospc' can only be used if the +# block device supports io-status (see BlockInfo). # # @on-target-error: the action to take on an error on the target, -# default 'report' (no limitations, since this applies to -# a different block device than @device). +# default 'report' (no limitations, since this applies to a +# different block device than @device). # -# @unmap: Whether to try to unmap target sectors where source has -# only zero. If true, and target unallocated sectors will read as zero, -# target image sectors will be unmapped; otherwise, zeroes will be -# written. Both will result in identical contents. -# Default is true. (Since 2.4) +# @unmap: Whether to try to unmap target sectors where source has only +# zero. If true, and target unallocated sectors will read as +# zero, target image sectors will be unmapped; otherwise, zeroes +# will be written. Both will result in identical contents. +# Default is true. (Since 2.4) # -# @copy-mode: when to copy data to the destination; defaults to 'background' -# (Since: 3.0) +# @copy-mode: when to copy data to the destination; defaults to +# 'background' (Since: 3.0) # -# @auto-finalize: When false, this job will wait in a PENDING state after it has -# finished its work, waiting for @block-job-finalize before -# making any block graph changes. -# When true, this job will automatically -# perform its abort or commit actions. -# Defaults to true. (Since 3.1) +# @auto-finalize: When false, this job will wait in a PENDING state +# after it has finished its work, waiting for @block-job-finalize +# before making any block graph changes. When true, this job will +# automatically perform its abort or commit actions. Defaults to +# true. (Since 3.1) # -# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it -# has completely ceased all work, and awaits @block-job-dismiss. -# When true, this job will automatically disappear from the query -# list without user intervention. -# Defaults to true. (Since 3.1) +# @auto-dismiss: When false, this job will wait in a CONCLUDED state +# after it has completely ceased all work, and awaits +# @block-job-dismiss. When true, this job will automatically +# disappear from the query list without user intervention. +# Defaults to true. (Since 3.1) # # Since: 1.3 ## @@ -2057,16 +2243,16 @@ # @name: name of the dirty bitmap (must be less than 1024 bytes) # # @granularity: the bitmap granularity, default is 64k for -# block-dirty-bitmap-add +# block-dirty-bitmap-add # # @persistent: the bitmap is persistent, i.e. it will be saved to the -# corresponding block device image file on its close. For now only -# Qcow2 disks support persistent bitmaps. Default is false for -# block-dirty-bitmap-add. (Since: 2.10) +# corresponding block device image file on its close. For now +# only Qcow2 disks support persistent bitmaps. Default is false +# for block-dirty-bitmap-add. (Since: 2.10) # -# @disabled: the bitmap is created in the disabled state, which means that -# it will not track drive changes. The bitmap may be enabled with -# block-dirty-bitmap-enable. Default is false. (Since: 4.0) +# @disabled: the bitmap is created in the disabled state, which means +# that it will not track drive changes. The bitmap may be enabled +# with block-dirty-bitmap-enable. Default is false. (Since: 4.0) # # Since: 2.4 ## @@ -2077,7 +2263,8 @@ ## # @BlockDirtyBitmapOrStr: # -# @local: name of the bitmap, attached to the same node as target bitmap. +# @local: name of the bitmap, attached to the same node as target +# bitmap. # # @external: bitmap with specified node # @@ -2094,9 +2281,9 @@ # # @target: name of the destination dirty bitmap # -# @bitmaps: name(s) of the source dirty bitmap(s) at @node and/or fully -# specified BlockDirtyBitmap elements. The latter are supported -# since 4.1. +# @bitmaps: name(s) of the source dirty bitmap(s) at @node and/or +# fully specified BlockDirtyBitmap elements. The latter are +# supported since 4.1. # # Since: 4.0 ## @@ -2107,47 +2294,48 @@ ## # @block-dirty-bitmap-add: # -# Create a dirty bitmap with a name on the node, and start tracking the writes. +# 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 +# Errors: +# - If @node is not a valid block device or node, DeviceNotFound +# - If @name is already taken, GenericError with an explanation # # Since: 2.4 # # Example: # -# -> { "execute": "block-dirty-bitmap-add", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } -# +# -> { "execute": "block-dirty-bitmap-add", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-add', - 'data': 'BlockDirtyBitmapAdd' } + 'data': 'BlockDirtyBitmapAdd', + 'allow-preconfig': true } ## # @block-dirty-bitmap-remove: # # Stop write tracking and remove the dirty bitmap that was created -# with block-dirty-bitmap-add. If the bitmap is persistent, remove it from its -# storage too. +# 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 +# Errors: +# - 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 # # Example: # -# -> { "execute": "block-dirty-bitmap-remove", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } -# +# -> { "execute": "block-dirty-bitmap-remove", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-remove', - 'data': 'BlockDirtyBitmap' } + 'data': 'BlockDirtyBitmap', + 'allow-preconfig': true } ## # @block-dirty-bitmap-clear: @@ -2156,95 +2344,96 @@ # 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 +# Errors: +# - If @node is not a valid block device, DeviceNotFound +# - If @name is not found, GenericError with an explanation # # Since: 2.4 # # Example: # -# -> { "execute": "block-dirty-bitmap-clear", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } -# +# -> { "execute": "block-dirty-bitmap-clear", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-clear', - 'data': 'BlockDirtyBitmap' } + 'data': 'BlockDirtyBitmap', + 'allow-preconfig': true } ## # @block-dirty-bitmap-enable: # # 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 +# Errors: +# - If @node is not a valid block device, DeviceNotFound +# - If @name is not found, GenericError with an explanation # # Since: 4.0 # # Example: # -# -> { "execute": "block-dirty-bitmap-enable", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } -# +# -> { "execute": "block-dirty-bitmap-enable", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-enable', - 'data': 'BlockDirtyBitmap' } + 'data': 'BlockDirtyBitmap', + 'allow-preconfig': true } ## # @block-dirty-bitmap-disable: # # 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 +# Errors: +# - If @node is not a valid block device, DeviceNotFound +# - If @name is not found, GenericError with an explanation # # Since: 4.0 # # Example: # -# -> { "execute": "block-dirty-bitmap-disable", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } -# +# -> { "execute": "block-dirty-bitmap-disable", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-disable', - 'data': 'BlockDirtyBitmap' } + 'data': 'BlockDirtyBitmap', + 'allow-preconfig': true } ## # @block-dirty-bitmap-merge: # # Merge dirty bitmaps listed in @bitmaps to the @target dirty bitmap. -# Dirty bitmaps in @bitmaps will be unchanged, except if it also appears -# as the @target bitmap. Any bits already set in @target will still be -# set after the merge, i.e., this operation does not clear the target. -# On error, @target is unchanged. +# Dirty bitmaps in @bitmaps will be unchanged, except if it also +# appears as the @target bitmap. Any bits already set in @target will +# still be set after the merge, i.e., this operation does not clear +# the target. On error, @target is unchanged. # -# The resulting bitmap will count as dirty any clusters that were dirty in any -# of the source bitmaps. This can be used to achieve backup checkpoints, or in -# simpler usages, to copy bitmaps. +# The resulting bitmap will count as dirty any clusters that were +# dirty in any 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 +# Errors: +# - 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 # # Example: # -# -> { "execute": "block-dirty-bitmap-merge", -# "arguments": { "node": "drive0", "target": "bitmap0", -# "bitmaps": ["bitmap1"] } } -# <- { "return": {} } -# +# -> { "execute": "block-dirty-bitmap-merge", +# "arguments": { "node": "drive0", "target": "bitmap0", +# "bitmaps": ["bitmap1"] } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-merge', - 'data': 'BlockDirtyBitmapMerge' } + 'data': 'BlockDirtyBitmapMerge', + 'allow-preconfig': true } ## # @BlockDirtyBitmapSha256: @@ -2264,93 +2453,93 @@ # Get bitmap SHA256. # # Features: +# # @unstable: This command is meant for debugging. # -# 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 +# +# Errors: +# - 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 ## { 'command': 'x-debug-block-dirty-bitmap-sha256', 'data': 'BlockDirtyBitmap', 'returns': 'BlockDirtyBitmapSha256', - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @blockdev-mirror: # # Start mirroring a block device's writes to a new destination. # -# @job-id: identifier for the newly-created block job. If -# omitted, the device name will be used. (Since 2.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 whose writes should be -# mirrored. +# @device: The device name or node-name of a root node whose writes +# should be mirrored. # -# @target: the id or node-name of the block device to mirror to. This mustn't be -# attached to guest. +# @target: the id or node-name of the block device to mirror to. This +# mustn't be attached to guest. # # @replaces: with sync=full graph node name to be replaced by the new -# image when a whole image copy is done. This can be used to repair -# broken Quorum files. By default, @device is replaced, although -# implicitly created filters on it are kept. +# image when a whole image copy is done. This can be used to +# repair broken Quorum files. By default, @device is replaced, +# although implicitly created filters on it are kept. # # @speed: the maximum speed, in bytes per second # -# @sync: what parts of the disk image should be copied to the destination -# (all the disk, only the sectors allocated in the topmost image, or -# only new I/O). +# @sync: what parts of the disk image should be copied to the +# destination (all the disk, only the sectors allocated in the +# topmost image, or only new I/O). # -# @granularity: granularity of the dirty bitmap, default is 64K -# if the image format doesn't have clusters, 4K if the clusters -# are smaller than that, else the cluster size. Must be a -# power of 2 between 512 and 64M +# @granularity: granularity of the dirty bitmap, default is 64K if the +# image format doesn't have clusters, 4K if the clusters are +# smaller than that, else the cluster size. Must be a power of 2 +# between 512 and 64M # -# @buf-size: maximum amount of data in flight from source to -# target +# @buf-size: maximum amount of data in flight from source to target # # @on-source-error: the action to take on an error on the source, -# default 'report'. 'stop' and 'enospc' can only be used -# if the block device supports io-status (see BlockInfo). +# default 'report'. 'stop' and 'enospc' can only be used if the +# block device supports io-status (see BlockInfo). # # @on-target-error: the action to take on an error on the target, -# default 'report' (no limitations, since this applies to -# a different block device than @device). +# default 'report' (no limitations, since this applies to a +# different block device than @device). # # @filter-node-name: the node name that should be assigned to the -# filter driver that the mirror job inserts into the graph -# above @device. If this option is not given, a node name is -# autogenerated. (Since: 2.9) +# filter driver that the mirror job inserts into the graph above +# @device. If this option is not given, a node name is +# autogenerated. (Since: 2.9) # -# @copy-mode: when to copy data to the destination; defaults to 'background' -# (Since: 3.0) +# @copy-mode: when to copy data to the destination; defaults to +# 'background' (Since: 3.0) # -# @auto-finalize: When false, this job will wait in a PENDING state after it has -# finished its work, waiting for @block-job-finalize before -# making any block graph changes. -# When true, this job will automatically -# perform its abort or commit actions. -# Defaults to true. (Since 3.1) +# @auto-finalize: When false, this job will wait in a PENDING state +# after it has finished its work, waiting for @block-job-finalize +# before making any block graph changes. When true, this job will +# automatically perform its abort or commit actions. Defaults to +# true. (Since 3.1) # -# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it -# has completely ceased all work, and awaits @block-job-dismiss. -# When true, this job will automatically disappear from the query -# list without user intervention. -# Defaults to true. (Since 3.1) -# -# Returns: nothing on success. +# @auto-dismiss: When false, this job will wait in a CONCLUDED state +# after it has completely ceased all work, and awaits +# @block-job-dismiss. When true, this job will automatically +# disappear from the query list without user intervention. +# Defaults to true. (Since 3.1) # # Since: 2.6 # # Example: # -# -> { "execute": "blockdev-mirror", -# "arguments": { "device": "ide-hd0", -# "target": "target0", -# "sync": "full" } } -# <- { "return": {} } -# +# -> { "execute": "blockdev-mirror", +# "arguments": { "device": "ide-hd0", +# "target": "target0", +# "sync": "full" } } +# <- { "return": {} } ## { 'command': 'blockdev-mirror', 'data': { '*job-id': 'str', 'device': 'str', 'target': 'str', @@ -2361,7 +2550,8 @@ '*on-target-error': 'BlockdevOnError', '*filter-node-name': 'str', '*copy-mode': 'MirrorCopyMode', - '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' }, + 'allow-preconfig': true } ## # @BlockIOThrottle: @@ -2384,59 +2574,53 @@ # # @iops_wr: write I/O operations per second # -# @bps_max: total throughput limit during bursts, -# in bytes (Since 1.7) +# @bps_max: total throughput limit during bursts, in bytes (Since 1.7) # -# @bps_rd_max: read throughput limit during bursts, -# in bytes (Since 1.7) +# @bps_rd_max: read throughput limit during bursts, in bytes (Since +# 1.7) # -# @bps_wr_max: write throughput limit during bursts, -# in bytes (Since 1.7) +# @bps_wr_max: write throughput limit during bursts, in bytes (Since +# 1.7) # -# @iops_max: total I/O operations per second during bursts, -# in bytes (Since 1.7) +# @iops_max: total I/O operations per second during bursts, in bytes +# (Since 1.7) # -# @iops_rd_max: read I/O operations per second during bursts, -# in bytes (Since 1.7) +# @iops_rd_max: read I/O operations per second during bursts, in bytes +# (Since 1.7) # -# @iops_wr_max: write I/O operations per second during bursts, -# in bytes (Since 1.7) +# @iops_wr_max: write I/O operations per second during bursts, 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) +# @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) # -# @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) +# @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) # -# @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) +# @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) # -# @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) +# @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) # -# @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) +# @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) # -# @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) +# @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) # # @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 @@ -2456,35 +2640,55 @@ ## # @ThrottleLimits: # -# Limit parameters for throttling. -# Since some limit combinations are illegal, limits should always be set in one -# transaction. All fields are optional. When setting limits, if a field is -# missing the current value is not changed. +# Limit parameters for throttling. Since some limit combinations are +# illegal, limits should always be set in one 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-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-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. +# +# @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-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-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. +# +# @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 @@ -2509,11 +2713,11 @@ # @limits: limits to apply for this throttle group # # Features: +# # @unstable: All members starting with x- are aliases for the same key -# without x- in the @limits object. This is not a stable -# interface and may be removed or changed incompatibly in -# the future. Use @limits for a supported stable -# interface. +# without x- in the @limits object. This is not a stable +# interface and may be removed or changed incompatibly in the +# future. Use @limits for a supported stable interface. # # Since: 2.11 ## @@ -2563,107 +2767,113 @@ # # Copy data from a backing file into a block device. # -# The block streaming operation is performed in the background until the entire -# backing file has been copied. This command returns immediately once streaming -# has started. The status of ongoing block streaming operations can be checked -# with query-block-jobs. The operation can be stopped before it has completed -# using the block-job-cancel command. +# The block streaming operation is performed in the background until +# the entire backing file has been copied. This command returns +# immediately once streaming has started. The status of ongoing block +# streaming operations can be checked with query-block-jobs. The +# operation can be stopped before it has completed using the +# block-job-cancel command. # -# The node that receives the data is called the top image, can be located in -# any part of the chain (but always above the base image; see below) and can be -# specified using its device or node name. Earlier qemu versions only allowed -# 'device' to name the top level node; presence of the 'base-node' parameter -# during introspection can be used as a witness of the enhanced semantics -# of 'device'. +# The node that receives the data is called the top image, can be +# located in any part of the chain (but always above the base image; +# see below) and can be specified using its device or node name. +# Earlier qemu versions only allowed 'device' to name the top level +# node; presence of the 'base-node' parameter during introspection can +# be used as a witness of the enhanced semantics of 'device'. # -# If a base file is specified then sectors are not copied from that base file and -# its backing chain. This can be used to stream a subset of the backing file -# chain instead of flattening the entire image. -# When streaming completes the image file will have the base file as its backing -# file, unless that node was changed while the job was running. In that case, -# base's parent's backing (or filtered, whichever exists) child (i.e., base at -# the beginning of the job) will be the new backing file. +# If a base file is specified then sectors are not copied from that +# base file and its backing chain. This can be used to stream a +# subset of the backing file chain instead of flattening the entire +# image. When streaming completes the image file will have the base +# file as its backing file, unless that node was changed while the job +# was running. In that case, base's parent's backing (or filtered, +# whichever exists) child (i.e., base at the beginning of the job) +# will be the new backing file. # -# On successful completion the image file is updated to drop the backing file -# and the BLOCK_JOB_COMPLETED event is emitted. +# On successful completion the image file is updated to drop the +# backing file and the BLOCK_JOB_COMPLETED event is emitted. # -# In case @device is a filter node, block-stream modifies the first non-filter -# overlay node below it to point to the new backing node instead of modifying -# @device itself. +# In case @device is a filter node, block-stream modifies the first +# non-filter overlay node below it to point to the new backing node +# instead of modifying @device itself. # -# @job-id: identifier for the newly-created block job. If -# omitted, the device name will be used. (Since 2.7) +# @job-id: identifier for the newly-created block job. If omitted, +# the device name will be used. (Since 2.7) # # @device: the device or node name of the top image # -# @base: the common backing file name. -# It cannot be set if @base-node or @bottom is also set. +# @base: the common backing file name. It cannot be set if @base-node +# or @bottom is also set. # -# @base-node: the node name of the backing file. -# It cannot be set if @base or @bottom is also set. (Since 2.8) +# @base-node: the node name of the backing file. It cannot be set if +# @base or @bottom is also set. (Since 2.8) # # @bottom: the last node in the chain that should be streamed into -# top. It cannot be set if @base or @base-node is also set. -# It cannot be filter node. (Since 6.0) +# top. It cannot be set if @base or @base-node is also set. It +# cannot be filter node. (Since 6.0) # -# @backing-file: The backing file string to write into the top -# image. This filename is not validated. +# @backing-file: The backing file string to write into the top 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) +# +# @backing-mask-protocol: If true, replace any protocol mentioned in +# the 'backing file format' with 'raw', rather than storing the +# protocol name as the backing format. Can be used even when no +# image header will be updated (default false; since 9.0). # # @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 -# supports io-status (see BlockInfo). Since 1.3. +# @on-error: the action to take on an error (default report). 'stop' +# and 'enospc' can only be used if the block device supports +# io-status (see BlockInfo). (Since 1.3) # # @filter-node-name: the node name that should be assigned to the -# filter driver that the stream job inserts into the graph -# above @device. If this option is not given, a node name is -# autogenerated. (Since: 6.0) +# filter driver that the stream job inserts into the graph above +# @device. If this option is not given, a node name is +# autogenerated. (Since: 6.0) # -# @auto-finalize: When false, this job will wait in a PENDING state after it has -# finished its work, waiting for @block-job-finalize before -# making any block graph changes. -# When true, this job will automatically -# perform its abort or commit actions. -# Defaults to true. (Since 3.1) +# @auto-finalize: When false, this job will wait in a PENDING state +# after it has finished its work, waiting for @block-job-finalize +# before making any block graph changes. When true, this job will +# automatically perform its abort or commit actions. Defaults to +# true. (Since 3.1) # -# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it -# has completely ceased all work, and awaits @block-job-dismiss. -# When true, this job will automatically disappear from the query -# list without user intervention. -# Defaults to true. (Since 3.1) +# @auto-dismiss: When false, this job will wait in a CONCLUDED state +# after it has completely ceased all work, and awaits +# @block-job-dismiss. When true, this job will automatically +# disappear from the query list without user intervention. +# Defaults to true. (Since 3.1) # -# Returns: - Nothing on success. -# - If @device does not exist, DeviceNotFound. +# Errors: +# - If @device does not exist, DeviceNotFound. # # Since: 1.1 # # Example: # -# -> { "execute": "block-stream", -# "arguments": { "device": "virtio0", -# "base": "/tmp/master.qcow2" } } -# <- { "return": {} } -# +# -> { "execute": "block-stream", +# "arguments": { "device": "virtio0", +# "base": "/tmp/master.qcow2" } } +# <- { "return": {} } ## { 'command': 'block-stream', 'data': { '*job-id': 'str', 'device': 'str', '*base': 'str', - '*base-node': 'str', '*backing-file': 'str', '*bottom': 'str', + '*base-node': 'str', '*backing-file': 'str', + '*backing-mask-protocol': 'bool', + '*bottom': 'str', '*speed': 'int', '*on-error': 'BlockdevOnError', '*filter-node-name': 'str', - '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' }, + 'allow-preconfig': true } ## # @block-job-set-speed: @@ -2674,171 +2884,220 @@ # # Throttling can be disabled by setting the speed to 0. # -# @device: The job identifier. This used to be a device name (hence -# the name of the parameter), but since QEMU 2.7 it can have -# other values. +# @device: The job identifier. This used to be a device name (hence +# 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. +# Defaults to 0. # -# Returns: - Nothing on success -# - If no background operation is active on this device, DeviceNotActive +# Errors: +# - If no background operation is active on this device, +# DeviceNotActive # # Since: 1.1 ## { 'command': 'block-job-set-speed', - 'data': { 'device': 'str', 'speed': 'int' } } + 'data': { 'device': 'str', 'speed': 'int' }, + 'allow-preconfig': true } ## # @block-job-cancel: # # Stop an active background block operation. # -# This command returns immediately after marking the active background block -# operation for cancellation. It is an error to call this command if no -# operation is in progress. +# This command returns immediately after marking the active background +# block operation for cancellation. It is an error to call this +# command if no operation is in progress. # # The operation will cancel as soon as possible and then emit the -# BLOCK_JOB_CANCELLED event. Before that happens the job is still visible when -# enumerated using query-block-jobs. +# BLOCK_JOB_CANCELLED event. Before that happens the job is still +# visible when enumerated using query-block-jobs. # -# Note that if you issue 'block-job-cancel' after 'drive-mirror' has indicated -# (via the event BLOCK_JOB_READY) that the source and destination are -# synchronized, then the event triggered by this command changes to -# BLOCK_JOB_COMPLETED, to indicate that the mirroring has ended and the -# destination now has a point-in-time copy tied to the time of the cancellation. +# Note that if you issue 'block-job-cancel' after 'drive-mirror' has +# indicated (via the event BLOCK_JOB_READY) that the source and +# destination are synchronized, then the event triggered by this +# command changes to BLOCK_JOB_COMPLETED, to indicate that the +# mirroring has ended and the destination now has a point-in-time copy +# tied to the time of the cancellation. # -# For streaming, the image file retains its backing file unless the streaming -# operation happens to complete just as it is being cancelled. A new streaming -# operation can be started at a later time to finish copying all data from the -# backing file. +# For streaming, the image file retains its backing file unless the +# streaming operation happens to complete just as it is being +# cancelled. A new streaming operation can be started at a later time +# to finish copying all data from the backing file. # -# @device: The job identifier. This used to be a device name (hence -# the name of the parameter), but since QEMU 2.7 it can have -# other values. +# @device: The job identifier. This used to be a device name (hence +# the name of the parameter), but since QEMU 2.7 it can have other +# values. # -# @force: If true, and the job has already emitted the event BLOCK_JOB_READY, -# abandon the job immediately (even if it is paused) instead of waiting -# for the destination to complete its final synchronization (since 1.3) +# @force: If true, and the job has already emitted the event +# BLOCK_JOB_READY, 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 +# Errors: +# - If no background operation is active on this device, +# DeviceNotActive # # Since: 1.1 ## -{ 'command': 'block-job-cancel', 'data': { 'device': 'str', '*force': 'bool' } } +{ 'command': 'block-job-cancel', 'data': { 'device': 'str', '*force': 'bool' }, + 'allow-preconfig': true } ## # @block-job-pause: # # Pause an active background block operation. # -# This command returns immediately after marking the active background block -# operation for pausing. It is an error to call this command if no -# operation is in progress or if the job is already paused. +# This command returns immediately after marking the active background +# block operation for pausing. It is an error to call this command if +# no operation is in progress or if the job is already paused. # -# The operation will pause as soon as possible. No event is emitted when -# the operation is actually paused. Cancelling a paused job automatically -# resumes it. +# The operation will pause as soon as possible. No event is emitted +# when the operation is actually paused. Cancelling a paused job +# automatically resumes it. # -# @device: The job identifier. This used to be a device name (hence -# the name of the parameter), but since QEMU 2.7 it can have -# other values. +# @device: The job identifier. This used to be a device name (hence +# 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 +# Errors: +# - If no background operation is active on this device, +# DeviceNotActive # # Since: 1.3 ## -{ 'command': 'block-job-pause', 'data': { 'device': 'str' } } +{ 'command': 'block-job-pause', 'data': { 'device': 'str' }, + 'allow-preconfig': true } ## # @block-job-resume: # # Resume an active background block operation. # -# This command returns immediately after resuming a paused background block -# operation. It is an error to call this command if no operation is in -# progress or if the job is not paused. +# This command returns immediately after resuming a paused background +# block operation. It is an error to call this command if no +# operation is in progress or if the job is not paused. # # This command also clears the error status of the job. # -# @device: The job identifier. This used to be a device name (hence -# the name of the parameter), but since QEMU 2.7 it can have -# other values. +# @device: The job identifier. This used to be a device name (hence +# 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 +# Errors: +# - If no background operation is active on this device, +# DeviceNotActive # # Since: 1.3 ## -{ 'command': 'block-job-resume', 'data': { 'device': 'str' } } +{ 'command': 'block-job-resume', 'data': { 'device': 'str' }, + 'allow-preconfig': true } ## # @block-job-complete: # -# Manually trigger completion of an active background block operation. This -# is supported for drive mirroring, where it also switches the device to -# write to the target path only. The ability to complete is signaled with -# a BLOCK_JOB_READY event. +# Manually trigger completion of an active background block operation. +# This is supported for drive mirroring, where it also switches the +# device to write to the target path only. The ability to complete is +# signaled with a BLOCK_JOB_READY event. # -# This command completes an active background block operation synchronously. -# The ordering of this command's return with the BLOCK_JOB_COMPLETED event -# is not defined. Note that if an I/O error occurs during the processing of -# this command: 1) the command itself will fail; 2) the error will be processed -# according to the rerror/werror arguments that were specified when starting -# the operation. +# This command completes an active background block operation +# synchronously. The ordering of this command's return with the +# BLOCK_JOB_COMPLETED event is not defined. Note that if an I/O error +# occurs during the processing of this command: 1) the command itself +# will fail; 2) the error will be processed according to the +# rerror/werror arguments that were specified when starting the +# operation. # # A cancelled or paused job cannot be completed. # -# @device: The job identifier. This used to be a device name (hence -# the name of the parameter), but since QEMU 2.7 it can have -# other values. +# @device: The job identifier. This used to be a device name (hence +# 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 +# Errors: +# - If no background operation is active on this device, +# DeviceNotActive # # Since: 1.3 ## -{ 'command': 'block-job-complete', 'data': { 'device': 'str' } } +{ 'command': 'block-job-complete', 'data': { 'device': 'str' }, + 'allow-preconfig': true } ## # @block-job-dismiss: # -# For jobs that have already concluded, remove them from the block-job-query -# list. This command only needs to be run for jobs which were started with -# QEMU 2.12+ job lifetime management semantics. +# For jobs that have already concluded, remove them from the +# block-job-query list. This command only needs to be run for jobs +# which were started with QEMU 2.12+ job lifetime management +# semantics. # -# This command will refuse to operate on any job that has not yet reached -# its terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of the -# BLOCK_JOB_READY event, block-job-cancel or block-job-complete will still need -# to be used as appropriate. +# This command will refuse to operate on any job that has not yet +# reached its terminal state, JOB_STATUS_CONCLUDED. For jobs that make +# use of the BLOCK_JOB_READY event, block-job-cancel or +# block-job-complete will still need to be used as appropriate. # # @id: The job identifier. # -# Returns: Nothing on success -# # Since: 2.12 ## -{ 'command': 'block-job-dismiss', 'data': { 'id': 'str' } } +{ 'command': 'block-job-dismiss', 'data': { 'id': 'str' }, + 'allow-preconfig': true } ## # @block-job-finalize: # # Once a job that has manual=true reaches the pending state, it can be -# instructed to finalize any graph changes and do any necessary cleanup -# via this command. -# For jobs in a transaction, instructing one job to finalize will force -# ALL jobs in the transaction to finalize, so it is only necessary to instruct -# a single member job to finalize. +# instructed to finalize any graph changes and do any necessary +# cleanup via this command. For jobs in a transaction, instructing +# one job to finalize will force ALL jobs in the transaction to +# finalize, so it is only necessary to instruct a single member job to +# finalize. # # @id: The job identifier. # -# Returns: Nothing on success -# # Since: 2.12 ## -{ 'command': 'block-job-finalize', 'data': { 'id': 'str' } } +{ 'command': 'block-job-finalize', 'data': { 'id': 'str' }, + 'allow-preconfig': true } + +## +# @BlockJobChangeOptionsMirror: +# +# @copy-mode: Switch to this copy mode. Currently, only the switch +# from 'background' to 'write-blocking' is implemented. +# +# Since: 8.2 +## +{ 'struct': 'BlockJobChangeOptionsMirror', + 'data': { 'copy-mode' : 'MirrorCopyMode' } } + +## +# @BlockJobChangeOptions: +# +# Block job options that can be changed after job creation. +# +# @id: The job identifier +# +# @type: The job type +# +# Since: 8.2 +## +{ 'union': 'BlockJobChangeOptions', + 'base': { 'id': 'str', 'type': 'JobType' }, + 'discriminator': 'type', + 'data': { 'mirror': 'BlockJobChangeOptionsMirror' } } + +## +# @block-job-change: +# +# Change the block job's options. +# +# Since: 8.2 +## +{ 'command': 'block-job-change', + 'data': 'BlockJobChangeOptions', 'boxed': true } ## # @BlockdevDiscardOptions: @@ -2846,6 +3105,7 @@ # Determines how to handle discard requests. # # @ignore: Ignore the request +# # @unmap: Forward as an unmap request # # Since: 2.9 @@ -2857,12 +3117,16 @@ # @BlockdevDetectZeroesOptions: # # Describes the operation mode for the automatic conversion of plain -# zero writes by the OS to driver specific optimized zero write commands. +# 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. +# +# @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 ## @@ -2875,7 +3139,9 @@ # Selects the AIO backend to handle I/O requests # # @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 @@ -2890,9 +3156,9 @@ # 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) +# default: false) +# +# @no-flush: ignore any flush requests for the device (default: false) # # Since: 2.9 ## @@ -2906,12 +3172,19 @@ # Drivers that are supported in block device operations. # # @throttle: Since 2.11 +# # @nvme: Since 2.12 +# # @copy-on-read: Since 3.0 +# # @blklogwrites: Since 3.0 +# # @blkreplay: Since 4.2 +# # @compress: Since 5.0 +# # @copy-before-write: Since 6.2 +# # @snapshot-access: Since 7.0 # # Since: 2.9 @@ -2922,11 +3195,19 @@ 'file', 'snapshot-access', 'ftp', 'ftps', 'gluster', {'name': 'host_cdrom', 'if': 'HAVE_HOST_BLOCK_DEVICE' }, {'name': 'host_device', 'if': 'HAVE_HOST_BLOCK_DEVICE' }, - 'http', 'https', 'iscsi', - 'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', - 'preallocate', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd', + 'http', 'https', + { 'name': 'io_uring', 'if': 'CONFIG_BLKIO' }, + 'iscsi', + 'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', + { 'name': 'nvme-io_uring', 'if': 'CONFIG_BLKIO' }, + 'parallels', 'preallocate', 'qcow', 'qcow2', 'qed', 'quorum', + 'raw', 'rbd', { 'name': 'replication', 'if': 'CONFIG_REPLICATION' }, - 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] } + 'ssh', 'throttle', 'vdi', 'vhdx', + { 'name': 'virtio-blk-vfio-pci', 'if': 'CONFIG_BLKIO' }, + { 'name': 'virtio-blk-vhost-user', 'if': 'CONFIG_BLKIO' }, + { 'name': 'virtio-blk-vhost-vdpa', 'if': 'CONFIG_BLKIO' }, + 'vmdk', 'vpc', 'vvfat' ] } ## # @BlockdevOptionsFile: @@ -2934,36 +3215,43 @@ # 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) +# +# @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) -# @aio-max-batch: maximum number of requests to batch together into a single -# submission in the AIO backend. The smallest value between -# this and the aio-max-batch value of the IOThread object is -# chosen. -# 0 means that the AIO backend will handle it automatically. -# (default: 0, since 6.2) -# @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. -# (default: off) (since: 3.0) +# +# @aio-max-batch: maximum number of requests to batch together into a +# single submission in the AIO backend. The smallest value +# between this and the aio-max-batch value of the IOThread object +# is chosen. 0 means that the AIO backend will handle it +# automatically. (default: 0, since 6.2) +# +# @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. (default: off) +# (since: 3.0) # # Features: -# @dynamic-auto-read-only: If present, enabled auto-read-only means that the -# driver will open the image read-only at first, -# dynamically reopen the image file read-write when -# the first writer is attached to the node and reopen -# read-only when the last writer is detached. This -# allows giving QEMU write permissions only on demand -# when an operation actually needs write access. +# +# @dynamic-auto-read-only: If present, enabled auto-read-only means +# that the driver will open the image read-only at first, +# dynamically reopen the image file read-write when the first +# writer is attached to the node and reopen read-only when the +# last writer is detached. This allows giving QEMU write +# permissions only on demand when an operation actually needs +# write access. +# # @unstable: Member x-check-cache-dropped is meant for debugging. # # Since: 2.9 @@ -2987,11 +3275,14 @@ # Driver specific block device options for the null backend. # # @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) -# @read-zeroes: if true, reads from the device produce zeroes; if false, the -# buffer is left unchanged. (default: false; since: 4.1) +# requests. Default to zero which completes requests immediately. +# (Since 2.4) +# +# @read-zeroes: if true, reads from the device produce zeroes; if +# false, the buffer is left unchanged. +# (default: false; since: 4.1) # # Since: 2.9 ## @@ -3003,8 +3294,9 @@ # # Driver specific block device options for the NVMe backend. # -# @device: PCI controller address of the NVMe device in -# format hhhh:bb:ss.f (host:bus:slot.function) +# @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 @@ -3021,13 +3313,17 @@ # 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) +# +# @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 @@ -3039,8 +3335,8 @@ ## # @BlockdevOptionsGenericFormat: # -# Driver specific block device options for image format that have no option -# besides their data source. +# 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 # @@ -3054,25 +3350,28 @@ # # Driver specific block device options for LUKS. # -# @key-secret: the ID of a QCryptoSecret object providing -# the decryption key (since 2.6). Mandatory except when -# doing a metadata-only probe of the image. +# @key-secret: the ID of a QCryptoSecret object providing the +# decryption key (since 2.6). Mandatory except when doing a +# metadata-only probe of the image. +# +# @header: block device holding a detached LUKS header. (since 9.0) # # Since: 2.9 ## { 'struct': 'BlockdevOptionsLUKS', 'base': 'BlockdevOptionsGenericFormat', - 'data': { '*key-secret': 'str' } } + 'data': { '*key-secret': 'str', + '*header': 'BlockdevRef'} } ## # @BlockdevOptionsGenericCOWFormat: # -# Driver specific block device options for image format that have no option -# besides their data source and an optional backing file. +# 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. +# device, null disables the backing file entirely. Defaults to +# the backing file stored the image file. # # Since: 2.9 ## @@ -3087,11 +3386,11 @@ # # @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 # @@ -3103,14 +3402,32 @@ ## # @Qcow2OverlapCheckFlags: # -# Structure of flags for each metadata structure. Setting a field to 'true' -# makes qemu guard that structure against unintended overwriting. The default -# value is chosen according to the template given. +# Structure of flags for each metadata structure. Setting a field to +# 'true' makes QEMU guard that Qcow2 format structure against +# unintended overwriting. See Qcow2 format specification for detailed +# information on these structures. The default value is chosen +# according to the template given. # -# @template: Specifies a template mode which can be adjusted using the other -# flags, defaults to 'cached' +# @template: Specifies a template mode which can be adjusted using the +# other flags, defaults to 'cached' # -# @bitmap-directory: since 3.0 +# @main-header: Qcow2 format header +# +# @active-l1: Qcow2 active L1 table +# +# @active-l2: Qcow2 active L2 table +# +# @refcount-table: Qcow2 refcount table +# +# @refcount-block: Qcow2 refcount blocks +# +# @snapshot-table: Qcow2 snapshot table +# +# @inactive-l1: Qcow2 inactive L1 tables +# +# @inactive-l2: Qcow2 inactive L2 tables +# +# @bitmap-directory: Qcow2 bitmap directory (since 3.0) # # Since: 2.9 ## @@ -3129,11 +3446,11 @@ ## # @Qcow2OverlapChecks: # -# Specifies which metadata structures should be guarded against unintended -# overwriting. +# 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 # @@ -3156,6 +3473,8 @@ ## # @BlockdevQcowEncryption: # +# @format: encryption format +# # Since: 2.10 ## { 'union': 'BlockdevQcowEncryption', @@ -3168,9 +3487,8 @@ # # 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 ## @@ -3191,6 +3509,8 @@ ## # @BlockdevQcow2Encryption: # +# @format: encryption format +# # Since: 2.10 ## { 'union': 'BlockdevQcow2Encryption', @@ -3202,11 +3522,11 @@ ## # @BlockdevOptionsPreallocate: # -# Filter driver intended to be inserted between format and protocol node -# and do preallocation in protocol node on write. +# Filter driver intended to be inserted between format and protocol +# node and do preallocation in protocol node on write. # # @prealloc-align: on preallocation, align file length to this number, -# default 1048576 (1M) +# default 1048576 (1M) # # @prealloc-size: how much to preallocate, default 134217728 (128M) # @@ -3221,51 +3541,63 @@ # # 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 +# 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 +# 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) +# @discard-no-unref: when enabled, data clusters will remain +# preallocated when they are no longer used, e.g. because they are +# discarded or converted to zero clusters. As usual, whether the +# old data is discarded or kept on the protocol level (i.e. in the +# image file) depends on the setting of the pass-discard-request +# option. Keeping the clusters preallocated prevents qcow2 +# fragmentation that would otherwise be caused by freeing and +# re-allocating them later. Besides potential performance +# degradation, such fragmentation can lead to increased allocation +# of clusters past the end of the image file, resulting in image +# files whose file length can grow much larger than their guest +# disk size would suggest. If image file length is of concern +# (e.g. when storing qcow2 images directly on block devices), you +# should consider enabling this option. (since 8.1) # -# @cache-size: the maximum total size of the L2 table and -# refcount block caches in bytes (since 2.2) +# @overlap-check: which overlap checks to perform for writes to the +# image, defaults to 'cached' (since 2.2) # -# @l2-cache-size: the maximum size of the L2 table cache 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-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) +# 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) +# 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) +# 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) +# 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 ## @@ -3275,6 +3607,7 @@ '*pass-discard-request': 'bool', '*pass-discard-snapshot': 'bool', '*pass-discard-other': 'bool', + '*discard-no-unref': 'bool', '*overlap-check': 'Qcow2OverlapChecks', '*cache-size': 'int', '*l2-cache-size': 'int', @@ -3288,7 +3621,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 # # Since: 2.12 @@ -3300,7 +3635,9 @@ # @SshHostKeyCheckHashType: # # @md5: The given hash is an md5 hash +# # @sha1: The given hash is an sha1 hash +# # @sha256: The given hash is an sha256 hash # # Since: 2.12 @@ -3312,6 +3649,7 @@ # @SshHostKeyHash: # # @type: The hash algorithm used for the hash +# # @hash: The expected hash value # # Since: 2.12 @@ -3323,6 +3661,8 @@ ## # @SshHostKeyCheck: # +# @mode: How to check the host key +# # Since: 2.12 ## { 'union': 'SshHostKeyCheck', @@ -3340,7 +3680,7 @@ # @user: user as which to connect, defaults to current local user name # # @host-key-check: Defines how and what to check the host key against -# (default: known_hosts) +# (default: known_hosts) # # Since: 2.9 ## @@ -3356,13 +3696,14 @@ # Trigger events supported by blkdebug. # # @l1_shrink_write_table: write zeros to the l1 table to shrink image. -# (since 2.11) +# (since 2.11) # -# @l1_shrink_free_l2_clusters: discard the l2 tables. (since 2.11) +# @l1_shrink_free_l2_clusters: discard the l2 tables. (since 2.11) # # @cor_write: a write due to copy-on-read (since 2.11) # -# @cluster_alloc_space: an allocation of file space for a cluster (since 4.1) +# @cluster_alloc_space: an allocation of file space for a cluster +# (since 4.1) # # @none: triggers once at creation of the blkdebug node (since 4.1) # @@ -3416,23 +3757,20 @@ # # @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 # @@ -3455,10 +3793,10 @@ # @event: trigger event # # @state: the current state identifier blkdebug needs to be in; -# defaults to "any" +# defaults to "any" # # @new_state: the state identifier blkdebug is supposed to assume if -# this event is triggered +# this event is triggered # # Since: 2.9 ## @@ -3476,47 +3814,45 @@ # # @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) +# 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) +# 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 # # @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) +# 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) +# to what cannot be shared anyway (which depends on how the +# blkdebug node is used). Defaults to none. (since 5.0) # # Since: 2.9 ## @@ -3540,13 +3876,14 @@ # # @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-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-super-update-interval: interval of write requests after which the log -# super block is updated to disk (default: 4096) +# @log-super-update-interval: interval of write requests after which +# the log super block is updated to disk (default: 4096) # # Since: 3.0 ## @@ -3602,18 +3939,18 @@ # # Driver specific block device options for Quorum # -# @blkverify: true if the driver must print content mismatch -# set to false by default +# @blkverify: true if the driver must print content mismatch set to +# false by default # # @children: the children block devices to use # # @vote-threshold: the vote limit under which a read will fail # # @rewrite-corrupted: rewrite corrupted data when quorum is reached -# (Since 2.1) +# (Since 2.1) # # @read-pattern: choose read pattern and set to quorum by default -# (Since 2.2) +# (Since 2.2) # # Since: 2.9 ## @@ -3635,8 +3972,7 @@ # # @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) # @@ -3649,6 +3985,82 @@ '*debug': 'int', '*logfile': 'str' } } +## +# @BlockdevOptionsIoUring: +# +# Driver specific block device options for the io_uring backend. +# +# @filename: path to the image file +# +# Since: 7.2 +## +{ 'struct': 'BlockdevOptionsIoUring', + 'data': { 'filename': 'str' }, + 'if': 'CONFIG_BLKIO' } + +## +# @BlockdevOptionsNvmeIoUring: +# +# Driver specific block device options for the nvme-io_uring backend. +# +# @path: path to the NVMe namespace's character device (e.g. +# /dev/ng0n1). +# +# Since: 7.2 +## +{ 'struct': 'BlockdevOptionsNvmeIoUring', + 'data': { 'path': 'str' }, + 'if': 'CONFIG_BLKIO' } + +## +# @BlockdevOptionsVirtioBlkVfioPci: +# +# Driver specific block device options for the virtio-blk-vfio-pci +# backend. +# +# @path: path to the PCI device's sysfs directory (e.g. +# /sys/bus/pci/devices/0000:00:01.0). +# +# Since: 7.2 +## +{ 'struct': 'BlockdevOptionsVirtioBlkVfioPci', + 'data': { 'path': 'str' }, + 'if': 'CONFIG_BLKIO' } + +## +# @BlockdevOptionsVirtioBlkVhostUser: +# +# Driver specific block device options for the virtio-blk-vhost-user +# backend. +# +# @path: path to the vhost-user UNIX domain socket. +# +# Since: 7.2 +## +{ 'struct': 'BlockdevOptionsVirtioBlkVhostUser', + 'data': { 'path': 'str' }, + 'if': 'CONFIG_BLKIO' } + +## +# @BlockdevOptionsVirtioBlkVhostVdpa: +# +# Driver specific block device options for the virtio-blk-vhost-vdpa +# backend. +# +# @path: path to the vhost-vdpa character device. +# +# Features: +# @fdset: Member @path supports the special "/dev/fdset/N" path +# (since 8.1) +# +# Since: 7.2 +## +{ 'struct': 'BlockdevOptionsVirtioBlkVhostVdpa', + 'data': { 'path': 'str' }, + 'features': [ { 'name' :'fdset', + 'if': 'CONFIG_BLKIO_VHOST_VDPA_FD' } ], + 'if': 'CONFIG_BLKIO' } + ## # @IscsiTransport: # @@ -3673,32 +4085,31 @@ ## # @BlockdevOptionsIscsi: # +# Driver specific block device options for iscsi +# # @transport: The iscsi transport type # # @portal: The address of the iscsi portal # # @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. +# @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. -# -# Driver specific block device options for iscsi +# @timeout: Timeout in seconds after which a request will timeout. 0 +# means no timeout and is the default. # # Since: 2.9 ## @@ -3724,16 +4135,18 @@ ## # @RbdImageEncryptionFormat: # +# @luks-any: Used for opening either luks or luks2 (Since 8.0) +# # Since: 6.1 ## { 'enum': 'RbdImageEncryptionFormat', - 'data': [ 'luks', 'luks2' ] } + 'data': [ 'luks', 'luks2', 'luks-any' ] } ## # @RbdEncryptionOptionsLUKSBase: # -# @key-secret: ID of a QCryptoSecret object providing a passphrase -# for unlocking the encryption +# @key-secret: ID of a QCryptoSecret object providing a passphrase for +# unlocking the encryption # # Since: 6.1 ## @@ -3769,6 +4182,15 @@ 'base': 'RbdEncryptionOptionsLUKSBase', 'data': { } } +## +# @RbdEncryptionOptionsLUKSAny: +# +# Since: 8.0 +## +{ 'struct': 'RbdEncryptionOptionsLUKSAny', + 'base': 'RbdEncryptionOptionsLUKSBase', + 'data': { } } + ## # @RbdEncryptionCreateOptionsLUKS: # @@ -3790,17 +4212,28 @@ ## # @RbdEncryptionOptions: # +# @format: Encryption format. +# +# @parent: Parent image encryption options (for cloned images). Can +# be left unspecified if this cloned image is encrypted using the +# same format and secret as its parent image (i.e. not explicitly +# formatted) or if its parent image is not encrypted. (Since 8.0) +# # Since: 6.1 ## { 'union': 'RbdEncryptionOptions', - 'base': { 'format': 'RbdImageEncryptionFormat' }, + 'base': { 'format': 'RbdImageEncryptionFormat', + '*parent': 'RbdEncryptionOptions' }, 'discriminator': 'format', 'data': { 'luks': 'RbdEncryptionOptionsLUKS', - 'luks2': 'RbdEncryptionOptionsLUKS2' } } + 'luks2': 'RbdEncryptionOptionsLUKS2', + 'luks-any': 'RbdEncryptionOptionsLUKSAny'} } ## # @RbdEncryptionCreateOptions: # +# @format: Encryption format. +# # Since: 6.1 ## { 'union': 'RbdEncryptionCreateOptions', @@ -3814,31 +4247,29 @@ # # @pool: Ceph pool name. # -# @namespace: Rados namespace name in the Ceph pool. (Since 5.0) +# @namespace: Rados namespace name in the Ceph pool. (Since 5.0) # # @image: Image name in the Ceph pool. # -# @conf: path to Ceph configuration file. Values -# in the configuration file will be overridden by -# options specified via QAPI. +# @conf: path to Ceph configuration file. Values in the configuration +# file will be overridden by options specified via QAPI. # # @snapshot: Ceph snapshot name. # -# @encrypt: Image encryption options. (Since 6.1) +# @encrypt: Image encryption options. (Since 6.1) # # @user: Ceph id name. # -# @auth-client-required: Acceptable authentication modes. -# This maps to Ceph configuration option -# "auth_client_required". (Since 3.0) +# @auth-client-required: Acceptable authentication modes. 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 ## @@ -3859,9 +4290,11 @@ # # An enumeration of replication modes. # -# @primary: Primary mode, the vm's state will be sent to secondary QEMU. +# @primary: Primary mode, the vm's state will be sent to secondary +# QEMU. # -# @secondary: Secondary mode, receive the vm's state from primary QEMU. +# @secondary: Secondary mode, receive the vm's state from primary +# QEMU. # # Since: 2.9 ## @@ -3875,9 +4308,9 @@ # # @mode: the replication mode # -# @top-id: In secondary mode, node name or device ID of the root -# node who owns the replication node chain. Must not be given in -# primary mode. +# @top-id: In secondary mode, node name or device ID of the root node +# who owns the replication node chain. Must not be given in +# primary mode. # # Since: 2.9 ## @@ -3923,25 +4356,22 @@ # # @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 ## @@ -3958,25 +4388,26 @@ ## # @BlockdevOptionsCurlBase: # -# Driver specific block device options shared by all protocols supported by the -# curl backend. +# Driver specific block device options shared by all protocols +# supported by the curl backend. # # @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) # # @username: Username for authentication (defaults to none) # # @password-secret: ID of a QCryptoSecret object providing a password -# for authentication (defaults to no 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 ## @@ -3992,15 +4423,15 @@ ## # @BlockdevOptionsCurlHttp: # -# Driver specific block device options for HTTP connections over the curl -# backend. URLs must start with "http://". +# 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) +# @cookie-secret: ID of a QCryptoSecret object providing the cookie +# data in a secure way. See @cookie for the format. (since 2.10) # # Since: 2.9 ## @@ -4012,18 +4443,18 @@ ## # @BlockdevOptionsCurlHttps: # -# Driver specific block device options for HTTPS connections over the curl -# backend. URLs must start with "https://". +# 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) +# @cookie-secret: ID of a QCryptoSecret object providing the cookie +# data in a secure way. See @cookie for the format. (since 2.10) # # Since: 2.9 ## @@ -4036,8 +4467,8 @@ ## # @BlockdevOptionsCurlFtp: # -# Driver specific block device options for FTP connections over the curl -# backend. URLs must start with "ftp://". +# Driver specific block device options for FTP connections over the +# curl backend. URLs must start with "ftp://". # # Since: 2.9 ## @@ -4048,11 +4479,11 @@ ## # @BlockdevOptionsCurlFtps: # -# Driver specific block device options for FTPS connections over the curl -# backend. URLs must start with "ftps://". +# 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 ## @@ -4071,30 +4502,31 @@ # # @tls-creds: TLS credentials ID # -# @tls-hostname: TLS hostname override for certificate validation (Since 7.0) +# @tls-hostname: TLS hostname override for certificate validation +# (Since 7.0) # -# @x-dirty-bitmap: A metadata context name such as "qemu:dirty-bitmap:NAME" -# or "qemu:allocation-depth" to query in place of the -# traditional "base:allocation" block status (see -# NBD_OPT_LIST_META_CONTEXT in the NBD protocol; and -# yes, naming this option x-context would have made -# more sense) (since 3.0) +# @x-dirty-bitmap: A metadata context name such as +# "qemu:dirty-bitmap:NAME" or "qemu:allocation-depth" to query in +# place of the traditional "base:allocation" block status (see +# NBD_OPT_LIST_META_CONTEXT in the NBD protocol; and yes, naming +# this option x-context would have made more sense) (since 3.0) # -# @reconnect-delay: On an unexpected disconnect, the nbd client tries to -# connect again until succeeding or encountering a serious -# error. During the first @reconnect-delay seconds, all -# requests are paused and will be rerun on a successful -# reconnect. After that time, any delayed requests and all -# future requests before a successful reconnect will -# immediately fail. Default 0 (Since 4.2) +# @reconnect-delay: On an unexpected disconnect, the nbd client tries +# to connect again until succeeding or encountering a serious +# error. During the first @reconnect-delay seconds, all requests +# are paused and will be rerun on a successful reconnect. After +# that time, any delayed requests and all future requests before a +# successful reconnect will immediately fail. Default 0 (Since +# 4.2) # -# @open-timeout: In seconds. If zero, the nbd driver tries the connection -# only once, and fails to open if the connection fails. -# If non-zero, the nbd driver will repeat connection attempts -# until successful or until @open-timeout seconds have elapsed. -# Default 0 (Since 7.0) +# @open-timeout: In seconds. If zero, the nbd driver tries the +# connection only once, and fails to open if the connection fails. +# If non-zero, the nbd driver will repeat connection attempts +# until successful or until @open-timeout seconds have elapsed. +# Default 0 (Since 7.0) # # Features: +# # @unstable: Member @x-dirty-bitmap is experimental. # # Since: 2.9 @@ -4114,6 +4546,7 @@ # Driver specific block device options for the raw driver. # # @offset: position where the block device starts +# # @size: the assumed size of the device # # Since: 2.9 @@ -4127,8 +4560,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. +# @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 @@ -4143,11 +4577,11 @@ # # Driver specific block device options for the copy-on-read driver. # -# @bottom: The name of a non-filter node (allocation-bearing layer) that -# limits the COR operations in the backing chain (inclusive), so -# that no data below this node will be copied by this filter. -# If option is absent, the limit is not applied, so that data -# from all backing layers may be copied. +# @bottom: The name of a non-filter node (allocation-bearing layer) +# that limits the COR operations in the backing chain (inclusive), +# so that no data below this node will be copied by this filter. +# If option is absent, the limit is not applied, so that data from +# all backing layers may be copied. # # Since: 6.0 ## @@ -4155,64 +4589,98 @@ 'base': 'BlockdevOptionsGenericFormat', 'data': { '*bottom': 'str' } } +## +# @OnCbwError: +# +# An enumeration of possible behaviors for copy-before-write operation +# failures. +# +# @break-guest-write: report the error to the guest. This way, the +# guest will not be able to overwrite areas that cannot be backed +# up, so the backup process remains valid. +# +# @break-snapshot: continue guest write. Doing so will make the +# provided snapshot state invalid and any backup or export process +# based on it will finally fail. +# +# Since: 7.1 +## +{ 'enum': 'OnCbwError', + 'data': [ 'break-guest-write', 'break-snapshot' ] } + ## # @BlockdevOptionsCbw: # -# Driver specific block device options for the copy-before-write driver, -# which does so called copy-before-write operations: when data is -# written to the filter, the filter first reads corresponding blocks -# from its file child and copies them to @target child. After successfully -# copying, the write request is propagated to file child. If copying -# fails, the original write request is failed too and no data is written -# to file child. +# Driver specific block device options for the copy-before-write +# driver, which does so called copy-before-write operations: when data +# is written to the filter, the filter first reads corresponding +# blocks from its file child and copies them to @target child. After +# successfully copying, the write request is propagated to file child. +# If copying fails, the original write request is failed too and no +# data is written to file child. # # @target: The target for copy-before-write operations. # # @bitmap: If specified, copy-before-write filter will do -# copy-before-write operations only for dirty regions of the -# bitmap. Bitmap size must be equal to length of file and -# target child of the filter. Note also, that bitmap is used -# only to initialize internal bitmap of the process, so further -# modifications (or removing) of specified bitmap doesn't -# influence the filter. (Since 7.0) +# copy-before-write operations only for dirty regions of the +# bitmap. Bitmap size must be equal to length of file and target +# child of the filter. Note also, that bitmap is used only to +# initialize internal bitmap of the process, so further +# modifications (or removing) of specified bitmap doesn't +# influence the filter. (Since 7.0) +# +# @on-cbw-error: Behavior on failure of copy-before-write operation. +# Default is @break-guest-write. (Since 7.1) +# +# @cbw-timeout: Zero means no limit. Non-zero sets the timeout in +# seconds for copy-before-write operation. When a timeout occurs, +# the respective copy-before-write operation will fail, and the +# @on-cbw-error parameter will decide how this failure is handled. +# Default 0. (Since 7.1) # # Since: 6.2 ## { 'struct': 'BlockdevOptionsCbw', 'base': 'BlockdevOptionsGenericFormat', - 'data': { 'target': 'BlockdevRef', '*bitmap': 'BlockDirtyBitmap' } } + 'data': { 'target': 'BlockdevRef', '*bitmap': 'BlockDirtyBitmap', + '*on-cbw-error': 'OnCbwError', '*cbw-timeout': 'uint32' } } ## # @BlockdevOptions: # -# Options for creating a block device. Many options are available for all -# block devices, independent of the block driver: +# 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. -# @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 -# later), e.g. depending on whether the image file is writable -# or whether a writing user is attached to the node -# (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) # -# Remaining options are determined by the block driver. +# @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 later), e.g. depending on whether the image +# file is writable or whether a writing user is attached to the +# node (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) # # Since: 2.9 ## @@ -4247,6 +4715,8 @@ 'if': 'HAVE_HOST_BLOCK_DEVICE' }, 'http': 'BlockdevOptionsCurlHttp', 'https': 'BlockdevOptionsCurlHttps', + 'io_uring': { 'type': 'BlockdevOptionsIoUring', + 'if': 'CONFIG_BLKIO' }, 'iscsi': 'BlockdevOptionsIscsi', 'luks': 'BlockdevOptionsLUKS', 'nbd': 'BlockdevOptionsNbd', @@ -4254,6 +4724,8 @@ 'null-aio': 'BlockdevOptionsNull', 'null-co': 'BlockdevOptionsNull', 'nvme': 'BlockdevOptionsNVMe', + 'nvme-io_uring': { 'type': 'BlockdevOptionsNvmeIoUring', + 'if': 'CONFIG_BLKIO' }, 'parallels': 'BlockdevOptionsGenericFormat', 'preallocate':'BlockdevOptionsPreallocate', 'qcow2': 'BlockdevOptionsQcow2', @@ -4269,6 +4741,15 @@ 'throttle': 'BlockdevOptionsThrottle', 'vdi': 'BlockdevOptionsGenericFormat', 'vhdx': 'BlockdevOptionsGenericFormat', + 'virtio-blk-vfio-pci': + { 'type': 'BlockdevOptionsVirtioBlkVfioPci', + 'if': 'CONFIG_BLKIO' }, + 'virtio-blk-vhost-user': + { 'type': 'BlockdevOptionsVirtioBlkVhostUser', + 'if': 'CONFIG_BLKIO' }, + 'virtio-blk-vhost-vdpa': + { 'type': 'BlockdevOptionsVirtioBlkVhostVdpa', + 'if': 'CONFIG_BLKIO' }, 'vmdk': 'BlockdevOptionsGenericCOWFormat', 'vpc': 'BlockdevOptionsGenericFormat', 'vvfat': 'BlockdevOptionsVVFAT' @@ -4280,6 +4761,7 @@ # Reference to a block device. # # @definition: defines a new block device inline +# # @reference: references the ID of an existing block device # # Since: 2.9 @@ -4294,9 +4776,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. +# +# @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 @@ -4313,65 +4797,64 @@ # # Since: 2.9 # -# Example: +# Examples: # -# 1. -# -> { "execute": "blockdev-add", -# "arguments": { -# "driver": "qcow2", -# "node-name": "test1", -# "file": { -# "driver": "file", -# "filename": "test.qcow2" -# } -# } -# } -# <- { "return": {} } -# -# 2. -# -> { "execute": "blockdev-add", -# "arguments": { -# "driver": "qcow2", -# "node-name": "node0", -# "discard": "unmap", -# "cache": { -# "direct": true -# }, -# "file": { -# "driver": "file", -# "filename": "/tmp/test.qcow2" -# }, -# "backing": { -# "driver": "raw", +# -> { "execute": "blockdev-add", +# "arguments": { +# "driver": "qcow2", +# "node-name": "test1", # "file": { +# "driver": "file", +# "filename": "test.qcow2" +# } +# } +# } +# <- { "return": {} } +# +# -> { "execute": "blockdev-add", +# "arguments": { +# "driver": "qcow2", +# "node-name": "node0", +# "discard": "unmap", +# "cache": { +# "direct": true +# }, +# "file": { # "driver": "file", -# "filename": "/dev/fdset/4" +# "filename": "/tmp/test.qcow2" +# }, +# "backing": { +# "driver": "raw", +# "file": { +# "driver": "file", +# "filename": "/dev/fdset/4" +# } # } # } -# } -# } -# -# <- { "return": {} } +# } # +# <- { "return": {} } ## -{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true } +{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true, + 'allow-preconfig': true } ## # @blockdev-reopen: # # Reopens one or more block devices using the given set of options. -# Any option not specified will be reset to its default value regardless -# of its previous status. If an option cannot be changed or a particular -# driver does not support reopening then the command will return an -# error. All devices in the list are reopened in one transaction, so -# if one of them fails then the whole transaction is cancelled. +# Any option not specified will be reset to its default value +# regardless of its previous status. If an option cannot be changed +# or a particular driver does not support reopening then the command +# will return an error. All devices in the list are reopened in one +# transaction, so if one of them fails then the whole transaction is +# cancelled. # -# The command receives a list of block devices to reopen. For each one -# of them, the top-level @node-name option (from BlockdevOptions) must be -# specified and is used to select the block device to be reopened. -# Other @node-name options must be either omitted or set to the -# current name of the appropriate node. This command won't change any -# node name and any attempt to do it will result in an error. +# The command receives a list of block devices to reopen. For each +# one of them, the top-level @node-name option (from BlockdevOptions) +# must be specified and is used to select the block device to be +# reopened. Other @node-name options must be either omitted or set to +# the current name of the appropriate node. This command won't change +# any node name and any attempt to do it will result in an error. # # In the case of options that refer to child nodes, the behavior of # this command depends on the value: @@ -4387,7 +4870,7 @@ # # 4) NULL: the current child (if any) is detached. # -# Options (1) and (2) are supported in all cases. Option (3) is +# Options (1) and (2) are supported in all cases. Option (3) is # supported for @file and @backing, and option (4) for @backing only. # # Unlike with blockdev-add, the @backing option must always be present @@ -4398,13 +4881,14 @@ # Since: 6.1 ## { 'command': 'blockdev-reopen', - 'data': { 'options': ['BlockdevOptions'] } } + 'data': { 'options': ['BlockdevOptions'] }, + 'allow-preconfig': true } ## # @blockdev-del: # -# Deletes a block device that has been added using blockdev-add. -# The command will fail if the node is attached to a device or is +# Deletes a block device that has been added using blockdev-add. The +# command will fail if the node is attached to a device or is # otherwise being used. # # @node-name: Name of the graph node to delete. @@ -4413,25 +4897,25 @@ # # Example: # -# -> { "execute": "blockdev-add", -# "arguments": { -# "driver": "qcow2", -# "node-name": "node0", -# "file": { -# "driver": "file", -# "filename": "test.qcow2" -# } -# } -# } -# <- { "return": {} } -# -# -> { "execute": "blockdev-del", -# "arguments": { "node-name": "node0" } -# } -# <- { "return": {} } +# -> { "execute": "blockdev-add", +# "arguments": { +# "driver": "qcow2", +# "node-name": "node0", +# "file": { +# "driver": "file", +# "filename": "test.qcow2" +# } +# } +# } +# <- { "return": {} } # +# -> { "execute": "blockdev-del", +# "arguments": { "node-name": "node0" } +# } +# <- { "return": {} } ## -{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' } } +{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' }, + 'allow-preconfig': true } ## # @BlockdevCreateOptionsFile: @@ -4439,14 +4923,17 @@ # 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 CONFIG_POSIX_FALLOCATE), -# full (if CONFIG_POSIX)) +# allowed values: off, falloc (if CONFIG_POSIX_FALLOCATE), full +# (if CONFIG_POSIX)) +# # @nocow: Turn off copy-on-write (valid only on btrfs; default: off) -# @extent-size-hint: Extent size hint to add to the image file; 0 for not -# adding an extent size hint (default: 1 MB, since 5.1) +# +# @extent-size-hint: Extent size hint to add to the image file; 0 for +# not adding an extent size hint (default: 1 MB, since 5.1) # # Since: 2.12 ## @@ -4463,11 +4950,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 CONFIG_GLUSTERFS_FALLOCATE), -# full (if CONFIG_GLUSTERFS_ZEROFILL)) +# allowed values: off, falloc (if CONFIG_GLUSTERFS_FALLOCATE), +# full (if CONFIG_GLUSTERFS_ZEROFILL)) # # Since: 2.12 ## @@ -4481,17 +4969,22 @@ # # Driver specific image creation options for LUKS. # -# @file: Node to create the image format on +# @file: Node to create the image format on, mandatory except when +# 'preallocation' is not requested +# +# @header: Block device holding a detached LUKS header. (since 9.0) +# # @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) +# +# @preallocation: Preallocation mode for the new image (since: 4.2) +# (default: off; allowed values: off, metadata, falloc, full) # # Since: 2.12 ## { 'struct': 'BlockdevCreateOptionsLUKS', 'base': 'QCryptoBlockCreateOptionsLUKS', - 'data': { 'file': 'BlockdevRef', + 'data': { '*file': 'BlockdevRef', + '*header': 'BlockdevRef', 'size': 'size', '*preallocation': 'PreallocMode' } } @@ -4501,6 +4994,7 @@ # Driver specific image creation options for NFS. # # @location: Where to store the new image file +# # @size: Size of the virtual disk in bytes # # Since: 2.12 @@ -4515,7 +5009,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) # # Since: 2.12 @@ -4531,9 +5027,12 @@ # 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 +# should be used +# # @encrypt: Encryption options if the image should be encrypted # # Since: 2.12 @@ -4547,7 +5046,9 @@ ## # @BlockdevQcow2Version: # -# @v2: The original QCOW2 format as introduced in qemu 0.10 (version 2) +# @v2: The original QCOW2 format as introduced in qemu 0.10 (version +# 2) +# # @v3: The extended QCOW2 format as introduced in qemu 1.1 (version 3) # # Since: 2.12 @@ -4561,6 +5062,7 @@ # Compression type used in qcow2 image file # # @zlib: zlib compression, see +# # @zstd: zstd compression, see # # Since: 5.1 @@ -4574,27 +5076,41 @@ # 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 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) +# standalone (read-only) raw image without looking at qcow2 +# metadata (default: false; since: 4.0) +# # @extended-l2: True to make the image have extended L2 entries -# (default: false; since 5.2) +# (default: false; since 5.2) +# # @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 +# 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) +# 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) +# # @compression-type: The image cluster compression method -# (default: zlib, since 5.1) +# (default: zlib, since 5.1) # # Since: 2.12 ## @@ -4620,11 +5136,16 @@ # 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 +# 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 @@ -4642,11 +5163,14 @@ # # Driver specific image creation options for rbd/Ceph. # -# @location: Where to store the new image file. This location cannot -# point to a snapshot. +# @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 -# @encrypt: Image encryption options. (Since 6.1) +# +# @encrypt: Image encryption options. (Since 6.1) # # Since: 2.12 ## @@ -4665,14 +5189,14 @@ # # @monolithicFlat: Single flat data image and a descriptor file # -# @twoGbMaxExtentSparse: Data is split into 2GB (per virtual LBA) sparse extent -# files, in addition to a descriptor file +# @twoGbMaxExtentSparse: Data is split into 2GB (per virtual LBA) +# sparse extent files, in addition to a descriptor file # -# @twoGbMaxExtentFlat: Data is split into 2GB (per virtual LBA) flat extent -# files, in addition to a descriptor file +# @twoGbMaxExtentFlat: Data is split into 2GB (per virtual LBA) flat +# extent files, in addition to a descriptor file # -# @streamOptimized: Single file image sparse cluster allocation, optimized -# for streaming over network. +# @streamOptimized: Single file image sparse cluster allocation, +# optimized for streaming over network. # # Since: 4.0 ## @@ -4695,25 +5219,36 @@ # # 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. +# @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". -# @toolsversion: VMware guest tools version. -# Default: "2147483647" (Since 6.2) -# @zeroed-grain: Whether to enable zeroed-grain feature for sparse subformats. -# Default: false. +# +# @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". +# +# @toolsversion: VMware guest tools version. Default: "2147483647" +# (Since 6.2) +# +# @zeroed-grain: Whether to enable zeroed-grain feature for sparse +# subformats. Default: false. # # Since: 4.0 ## @@ -4734,6 +5269,7 @@ # Driver specific image creation options for SSH. # # @location: Where to store the new image file +# # @size: Size of the virtual disk in bytes # # Since: 2.12 @@ -4748,9 +5284,11 @@ # 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) +# allowed values: off, metadata) # # Since: 2.12 ## @@ -4763,6 +5301,7 @@ # @BlockdevVhdxSubformat: # # @dynamic: Growing image file +# # @fixed: Preallocated fixed-size image file # # Since: 2.12 @@ -4776,16 +5315,21 @@ # 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) +# +# @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) +# 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. +# +# @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 ## @@ -4801,6 +5345,7 @@ # @BlockdevVpcSubformat: # # @dynamic: Growing image file +# # @fixed: Preallocated fixed-size image file # # Since: 2.12 @@ -4814,11 +5359,14 @@ # 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) +# +# @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 ## @@ -4861,7 +5409,7 @@ ## # @blockdev-create: # -# Starts a job to create an image format on a given node. The job is +# Starts a job to create an image format on a given node. The job is # automatically finalized, but a manual job-dismiss is required. # # @job-id: Identifier for the newly created job. @@ -4872,7 +5420,8 @@ ## { 'command': 'blockdev-create', 'data': { 'job-id': 'str', - 'options': 'BlockdevCreateOptions' } } + 'options': 'BlockdevCreateOptions' }, + 'allow-preconfig': true } ## # @BlockdevAmendOptionsLUKS: @@ -4889,10 +5438,10 @@ ## # @BlockdevAmendOptionsQcow2: # -# Driver specific image amend options for qcow2. -# For now, only encryption options can be amended +# Driver specific image amend options for qcow2. For now, only +# encryption options can be amended # -# @encrypt Encryption options to be amended +# @encrypt: Encryption options to be amended # # Since: 5.1 ## @@ -4919,8 +5468,9 @@ ## # @x-blockdev-amend: # -# Starts a job to amend format specific options of an existing open block device -# The job is automatically finalized, but a manual job-dismiss is required. +# Starts a job to amend format specific options of an existing open +# block device The job is automatically finalized, but a manual +# job-dismiss is required. # # @job-id: Identifier for the newly created job. # @@ -4928,13 +5478,13 @@ # # @options: Options (driver specific) # -# @force: Allow unsafe operations, format specific -# For luks that allows erase of the last active keyslot -# (permanent loss of data), -# and replacement of an active keyslot -# (possible loss of data if IO error happens) +# @force: Allow unsafe operations, format specific For luks that +# allows erase of the last active keyslot (permanent loss of +# data), and replacement of an active keyslot (possible loss of +# data if IO error happens) # # Features: +# # @unstable: This command is experimental. # # Since: 5.1 @@ -4944,7 +5494,8 @@ 'node-name': 'str', 'options': 'BlockdevAmendOptions', '*force': 'bool' }, - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @BlockErrorAction: @@ -4965,40 +5516,40 @@ ## # @BLOCK_IMAGE_CORRUPTED: # -# Emitted when a disk image is being marked corrupt. The image can be -# identified by its device or node name. The 'device' field is always +# Emitted when a disk image is being marked corrupt. The image can be +# identified by its device or node name. The 'device' field is always # present for compatibility reasons, but it can be empty ("") if the # image does not have a device name associated. # -# @device: device name. This is always present for compatibility -# reasons, but it can be empty ("") if the image does not -# have a device name associated. +# @device: device name. This is always present for compatibility +# reasons, but it can be empty ("") if the image does not have a +# device name associated. # # @node-name: node name (Since: 2.4) # # @msg: informative message for human consumption, such as the kind of -# corruption being detected. It should not be parsed by machine as it is -# not guaranteed to be stable +# corruption being detected. It should not be parsed by machine +# as it is not guaranteed to be stable # # @offset: if the corruption resulted from an image access, this is -# the host's access offset into the image +# the host's access offset into the image # -# @size: if the corruption resulted from an image access, this is -# the access size +# @size: if the corruption resulted from an image access, this is 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) +# @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) # # Note: If action is "stop", a STOP event will eventually follow the -# BLOCK_IO_ERROR event. +# BLOCK_IO_ERROR event. # # Example: # -# <- { "event": "BLOCK_IMAGE_CORRUPTED", -# "data": { "device": "", "node-name": "drive", "fatal": false, -# "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)" }, -# "timestamp": { "seconds": 1648243240, "microseconds": 906060 } } +# <- { "event": "BLOCK_IMAGE_CORRUPTED", +# "data": { "device": "", "node-name": "drive", "fatal": false, +# "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)" }, +# "timestamp": { "seconds": 1648243240, "microseconds": 906060 } } # # Since: 1.7 ## @@ -5015,43 +5566,42 @@ # # Emitted when a disk I/O error occurs # -# @device: device name. This is always present for compatibility -# reasons, but it can be empty ("") if the image does not -# have a device name associated. +# @device: device name. This is always present for compatibility +# reasons, but it can be empty ("") if the image does not have a +# device name associated. # -# @node-name: node name. Note that errors may be reported for the root node -# that is directly attached to a guest device rather than for the -# node where the error occurred. The node name is not present if -# the drive is empty. (Since: 2.8) +# @node-name: node name. Note that errors may be reported for the +# root node that is directly attached to a guest device rather +# than for the node where the error occurred. The node name is +# not present if the drive is empty. (Since: 2.8) # # @operation: I/O operation # # @action: action that has been taken # -# @nospace: true if I/O error was caused due to a no-space -# condition. This key is only present if query-block's -# io-status is present, please see query-block documentation -# for more information (since: 2.2) +# @nospace: true if I/O error was caused due to a no-space condition. +# This key is only present if query-block's io-status is present, +# please see query-block documentation for more information +# (since: 2.2) # -# @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) +# @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) # # Note: If action is "stop", a STOP event will eventually follow the -# BLOCK_IO_ERROR event +# BLOCK_IO_ERROR event # # Since: 0.13 # # Example: # -# <- { "event": "BLOCK_IO_ERROR", -# "data": { "device": "ide0-hd1", -# "node-name": "#block212", -# "operation": "write", -# "action": "stop", -# "reason": "No space left on device" }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } -# +# <- { "event": "BLOCK_IO_ERROR", +# "data": { "device": "ide0-hd1", +# "node-name": "#block212", +# "operation": "write", +# "action": "stop", +# "reason": "No space left on device" }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'BLOCK_IO_ERROR', 'data': { 'device': 'str', '*node-name': 'str', @@ -5066,31 +5616,30 @@ # # @type: job type # -# @device: The job identifier. Originally the device name but other -# values are allowed since QEMU 2.7 +# @device: The job identifier. Originally the device name but other +# values are allowed since QEMU 2.7 # # @len: maximum progress value # -# @offset: current progress value. On success this is equal to len. -# On failure this is less than len +# @offset: current progress value. On success this is equal to len. +# On failure this is less than len # # @speed: rate limit, bytes per second # -# @error: error message. Only present on failure. This field -# contains a human-readable error message. There are no semantics -# other than that streaming has failed and clients should not try to -# interpret the error string +# @error: error message. Only present on failure. This field +# contains a human-readable error message. There are no semantics +# other than that streaming has failed and clients should not try +# to interpret the error string # # Since: 1.1 # # Example: # -# <- { "event": "BLOCK_JOB_COMPLETED", -# "data": { "type": "stream", "device": "virtio-disk0", -# "len": 10737418240, "offset": 10737418240, -# "speed": 0 }, -# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } -# +# <- { "event": "BLOCK_JOB_COMPLETED", +# "data": { "type": "stream", "device": "virtio-disk0", +# "len": 10737418240, "offset": 10737418240, +# "speed": 0 }, +# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } ## { 'event': 'BLOCK_JOB_COMPLETED', 'data': { 'type' : 'JobType', @@ -5107,13 +5656,13 @@ # # @type: job type # -# @device: The job identifier. Originally the device name but other -# values are allowed since QEMU 2.7 +# @device: The job identifier. Originally the device name but other +# values are allowed since QEMU 2.7 # # @len: maximum progress value # -# @offset: current progress value. On success this is equal to len. -# On failure this is less than len +# @offset: current progress value. On success this is equal to len. +# On failure this is less than len # # @speed: rate limit, bytes per second # @@ -5121,12 +5670,11 @@ # # Example: # -# <- { "event": "BLOCK_JOB_CANCELLED", -# "data": { "type": "stream", "device": "virtio-disk0", -# "len": 10737418240, "offset": 134217728, -# "speed": 0 }, -# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } -# +# <- { "event": "BLOCK_JOB_CANCELLED", +# "data": { "type": "stream", "device": "virtio-disk0", +# "len": 10737418240, "offset": 134217728, +# "speed": 0 }, +# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } ## { 'event': 'BLOCK_JOB_CANCELLED', 'data': { 'type' : 'JobType', @@ -5140,8 +5688,8 @@ # # Emitted when a block job encounters an error # -# @device: The job identifier. Originally the device name but other -# values are allowed since QEMU 2.7 +# @device: The job identifier. Originally the device name but other +# values are allowed since QEMU 2.7 # # @operation: I/O operation # @@ -5151,12 +5699,11 @@ # # Example: # -# <- { "event": "BLOCK_JOB_ERROR", -# "data": { "device": "ide0-hd1", -# "operation": "write", -# "action": "stop" }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } -# +# <- { "event": "BLOCK_JOB_ERROR", +# "data": { "device": "ide0-hd1", +# "operation": "write", +# "action": "stop" }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'BLOCK_JOB_ERROR', 'data': { 'device' : 'str', @@ -5170,28 +5717,27 @@ # # @type: job type # -# @device: The job identifier. Originally the device name but other -# values are allowed since QEMU 2.7 +# @device: The job identifier. Originally the device name but other +# values are allowed since QEMU 2.7 # # @len: maximum progress value # -# @offset: current progress value. On success this is equal to len. -# On failure this is less than len +# @offset: current progress value. On success this is equal to len. +# On failure this is less than len # # @speed: rate limit, bytes per second # -# Note: The "ready to complete" status is always reset by a @BLOCK_JOB_ERROR -# event +# Note: The "ready to complete" status is always reset by a +# @BLOCK_JOB_ERROR event # # Since: 1.3 # # Example: # -# <- { "event": "BLOCK_JOB_READY", -# "data": { "device": "drive0", "type": "mirror", "speed": 0, -# "len": 2097152, "offset": 2097152 } -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } -# +# <- { "event": "BLOCK_JOB_READY", +# "data": { "device": "drive0", "type": "mirror", "speed": 0, +# "len": 2097152, "offset": 2097152 }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'BLOCK_JOB_READY', 'data': { 'type' : 'JobType', @@ -5203,9 +5749,10 @@ ## # @BLOCK_JOB_PENDING: # -# Emitted when a block job is awaiting explicit authorization to finalize graph -# changes via @block-job-finalize. If this job is part of a transaction, it will -# not emit this event until the transaction has converged first. +# Emitted when a block job is awaiting explicit authorization to +# finalize graph changes via @block-job-finalize. If this job is part +# of a transaction, it will not emit this event until the transaction +# has converged first. # # @type: job type # @@ -5215,10 +5762,9 @@ # # Example: # -# <- { "event": "BLOCK_JOB_PENDING", -# "data": { "type": "mirror", "id": "backup_1" }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } -# +# <- { "event": "BLOCK_JOB_PENDING", +# "data": { "type": "mirror", "id": "backup_1" }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'BLOCK_JOB_PENDING', 'data': { 'type' : 'JobType', @@ -5230,13 +5776,16 @@ # Preallocation mode of QEMU image file # # @off: no preallocation +# # @metadata: preallocate only for metadata +# # @falloc: like @full preallocation but allocate disk space by -# posix_fallocate() rather than writing data. +# posix_fallocate() rather than writing data. +# # @full: preallocate all data by writing it to the device to ensure -# disk space is really available. This data may or may not be -# zero, depending on the image format and storage. -# @full preallocation also sets up metadata correctly. +# disk space is really available. This data may or may not be +# zero, depending on the image format and storage. @full +# preallocation also sets up metadata correctly. # # Since: 2.2 ## @@ -5247,15 +5796,15 @@ # @BLOCK_WRITE_THRESHOLD: # # Emitted when writes on block device reaches or exceeds the -# configured write threshold. For thin-provisioned devices, this -# means the device should be extended to avoid pausing for -# disk exhaustion. -# The event is one shot. Once triggered, it needs to be +# configured write threshold. For thin-provisioned devices, this +# means the device should be extended to avoid pausing for disk +# exhaustion. The event is one shot. Once triggered, it needs to be # re-registered with another block-set-write-threshold command. # # @node-name: graph node name on which the threshold was exceeded. # -# @amount-exceeded: amount of data which exceeded the threshold, in bytes. +# @amount-exceeded: amount of data which exceeded the threshold, in +# bytes. # # @write-threshold: last configured threshold, in bytes. # @@ -5269,43 +5818,43 @@ ## # @block-set-write-threshold: # -# Change the write threshold for a block drive. An event will be +# Change the write threshold for a block drive. An event will be # delivered if a write to this block drive crosses the configured -# threshold. The threshold is an offset, thus must be -# non-negative. Default is no write threshold. Setting the threshold -# to zero disables it. +# threshold. The threshold is an offset, thus must be non-negative. +# Default is no write threshold. Setting the threshold to zero +# disables it. # -# This is useful to transparently resize thin-provisioned drives without -# the guest OS noticing. +# This is useful to transparently resize thin-provisioned drives +# without the guest OS noticing. # # @node-name: graph node name on which the threshold must be set. # # @write-threshold: configured threshold for the block device, bytes. -# Use 0 to disable the threshold. +# Use 0 to disable the threshold. # # Since: 2.3 # # Example: # -# -> { "execute": "block-set-write-threshold", -# "arguments": { "node-name": "mydev", -# "write-threshold": 17179869184 } } -# <- { "return": {} } -# +# -> { "execute": "block-set-write-threshold", +# "arguments": { "node-name": "mydev", +# "write-threshold": 17179869184 } } +# <- { "return": {} } ## { 'command': 'block-set-write-threshold', - 'data': { 'node-name': 'str', 'write-threshold': 'uint64' } } + 'data': { 'node-name': 'str', 'write-threshold': 'uint64' }, + 'allow-preconfig': true } ## # @x-blockdev-change: # -# Dynamically reconfigure the block driver state graph. It can be used -# to add, remove, insert or replace a graph node. Currently only the -# Quorum driver implements this feature to add or remove its child. This -# is useful to fix a broken quorum child. +# Dynamically reconfigure the block driver state graph. It can be +# used to add, remove, insert or replace a graph node. Currently only +# the Quorum driver implements this feature to add or remove its +# child. This is useful to fix a broken quorum child. # -# If @node is specified, it will be inserted under @parent. @child -# may not be specified in this case. If both @parent and @child are +# If @node is specified, it will be inserted under @parent. @child +# may not be specified in this case. If both @parent and @child are # specified but @node is not, @child will be detached from @parent. # # @parent: the id or name of the parent node. @@ -5315,53 +5864,56 @@ # @node: the name of the node that will be added. # # Features: -# @unstable: 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. # -# 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(). +# @unstable: 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. # -# Warning: The data in a new quorum child MUST be consistent -# with that of the rest of the array. +# 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. # # Since: 2.7 # -# Example: +# Examples: # -# 1. Add a new node to a quorum -# -> { "execute": "blockdev-add", -# "arguments": { -# "driver": "raw", -# "node-name": "new_node", -# "file": { "driver": "file", -# "filename": "test.raw" } } } -# <- { "return": {} } -# -> { "execute": "x-blockdev-change", -# "arguments": { "parent": "disk1", -# "node": "new_node" } } -# <- { "return": {} } +# 1. Add a new node to a quorum # -# 2. Delete a quorum's node -# -> { "execute": "x-blockdev-change", -# "arguments": { "parent": "disk1", -# "child": "children.1" } } -# <- { "return": {} } +# -> { "execute": "blockdev-add", +# "arguments": { +# "driver": "raw", +# "node-name": "new_node", +# "file": { "driver": "file", +# "filename": "test.raw" } } } +# <- { "return": {} } +# -> { "execute": "x-blockdev-change", +# "arguments": { "parent": "disk1", +# "node": "new_node" } } +# <- { "return": {} } # +# 2. Delete a quorum's node +# +# -> { "execute": "x-blockdev-change", +# "arguments": { "parent": "disk1", +# "child": "children.1" } } +# <- { "return": {} } ## { 'command': 'x-blockdev-change', 'data' : { 'parent': 'str', '*child': 'str', '*node': 'str' }, - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @x-blockdev-set-iothread: # -# Move @node and its children into the @iothread. If @iothread is null then -# move @node and its children into the main loop. +# Move @node and its children into the @iothread. If @iothread is +# null then move @node and its children into the main loop. # # The node must not be attached to a BlockBackend. # @@ -5369,35 +5921,38 @@ # # @iothread: the name of the IOThread object or null for the main loop # -# @force: true if the node and its children should be moved when a BlockBackend -# is already attached +# @force: true if the node and its children should be moved when a +# BlockBackend is already attached # # Features: -# @unstable: This command is experimental and intended for test cases that -# need control over IOThreads only. +# +# @unstable: This command is experimental and intended for test cases +# that need control over IOThreads only. # # Since: 2.12 # -# Example: +# Examples: # -# 1. Move a node into an IOThread -# -> { "execute": "x-blockdev-set-iothread", -# "arguments": { "node-name": "disk1", -# "iothread": "iothread0" } } -# <- { "return": {} } +# 1. Move a node into an IOThread # -# 2. Move a node into the main loop -# -> { "execute": "x-blockdev-set-iothread", -# "arguments": { "node-name": "disk1", -# "iothread": null } } -# <- { "return": {} } +# -> { "execute": "x-blockdev-set-iothread", +# "arguments": { "node-name": "disk1", +# "iothread": "iothread0" } } +# <- { "return": {} } # +# 2. Move a node into the main loop +# +# -> { "execute": "x-blockdev-set-iothread", +# "arguments": { "node-name": "disk1", +# "iothread": null } } +# <- { "return": {} } ## { 'command': 'x-blockdev-set-iothread', 'data' : { 'node-name': 'str', 'iothread': 'StrOrNull', '*force': 'bool' }, - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @QuorumOpType: @@ -5432,10 +5987,9 @@ # # Example: # -# <- { "event": "QUORUM_FAILURE", -# "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 }, -# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } -# +# <- { "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' } } @@ -5447,10 +6001,10 @@ # # @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. +# @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 # @@ -5462,22 +6016,21 @@ # # Since: 2.0 # -# Example: +# Examples: # -# 1. Read operation +# 1. Read operation # -# { "event": "QUORUM_REPORT_BAD", -# "data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5, -# "type": "read" }, -# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } +# <- { "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 } } +# 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', @@ -5486,14 +6039,14 @@ ## # @BlockdevSnapshotInternal: # -# @device: the device name or node-name of a root node to generate the snapshot -# from +# @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, and rbd. +# 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, and rbd. # # Since: 1.7 ## @@ -5504,76 +6057,90 @@ # @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 +# 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. +# 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 +# Errors: +# - 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, +# GenericError # # Since: 1.7 # # Example: # -# -> { "execute": "blockdev-snapshot-internal-sync", -# "arguments": { "device": "ide-hd0", -# "name": "snapshot0" } -# } -# <- { "return": {} } -# +# -> { "execute": "blockdev-snapshot-internal-sync", +# "arguments": { "device": "ide-hd0", +# "name": "snapshot0" } +# } +# <- { "return": {} } ## { 'command': 'blockdev-snapshot-internal-sync', - 'data': 'BlockdevSnapshotInternal' } + 'data': 'BlockdevSnapshotInternal', + 'allow-preconfig': true } ## # @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. +# 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 +# @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 +# Returns: +# SnapshotInfo +# +# Errors: +# - 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, +# GenericError +# - 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, -# "icount": 220414 -# } -# } -# +# -> { "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, +# "icount": 220414 +# } +# } ## { 'command': 'blockdev-snapshot-delete-internal-sync', 'data': { 'device': 'str', '*id': 'str', '*name': 'str'}, - 'returns': 'SnapshotInfo' } + 'returns': 'SnapshotInfo', + 'allow-preconfig': true } + +## +# @DummyBlockCoreForceArrays: +# +# Not used by QMP; hack to let us use BlockGraphInfoList internally +# +# Since: 8.0 +## +{ 'struct': 'DummyBlockCoreForceArrays', + 'data': { 'unused-block-graph-info': ['BlockGraphInfo'] } } diff --git a/qapi/block-export.json b/qapi/block-export.json index 0685cb8b9a..3919a2d5b9 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -11,20 +11,24 @@ ## # @NbdServerOptions: # -# Keep this type consistent with the nbd-server-start arguments. The only -# intended difference is using SocketAddress instead of SocketAddressLegacy. +# Keep this type consistent with the nbd-server-start arguments. The +# only intended difference is using SocketAddress instead of +# SocketAddressLegacy. # # @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). -# @max-connections: The maximum number of connections to allow at the same -# time, 0 for unlimited. Setting this to 1 also stops -# the server from advertising multiple client support -# (since 5.2; default: 0) +# 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). +# +# @max-connections: The maximum number of connections to allow at the +# same time, 0 for unlimited. Setting this to 1 also stops the +# server from advertising multiple client support (since 5.2; +# default: 0) # # Since: 4.2 ## @@ -38,26 +42,31 @@ # @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". +# 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". # -# Keep this type consistent with the NbdServerOptions type. The only intended -# difference is using SocketAddressLegacy instead of SocketAddress. +# Keep this type consistent with the NbdServerOptions type. The only +# intended difference is using SocketAddressLegacy instead of +# SocketAddress. # # @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). -# @max-connections: The maximum number of connections to allow at the same -# time, 0 for unlimited. Setting this to 1 also stops -# the server from advertising multiple client support -# (since 5.2; default: 0). # -# Returns: error if the server is already running. +# @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). +# +# @max-connections: The maximum number of connections to allow at the +# same time, 0 for unlimited. Setting this to 1 also stops the +# server from advertising multiple client support (since 5.2; +# default: 0). +# +# Errors: +# - if the server is already running # # Since: 1.3 ## @@ -65,19 +74,20 @@ 'data': { 'addr': 'SocketAddressLegacy', '*tls-creds': 'str', '*tls-authz': 'str', - '*max-connections': 'uint32' } } + '*max-connections': 'uint32' }, + 'allow-preconfig': true } ## # @BlockExportOptionsNbdBase: # -# An NBD block export (common options shared between nbd-server-add and -# the NBD branch of block-export-add). +# An NBD block export (common options shared between nbd-server-add +# and the NBD branch of block-export-add). # -# @name: Export name. If unspecified, the @device parameter is used as the -# export name. (Since 2.12) +# @name: Export name. If unspecified, the @device parameter is used +# as the export name. (Since 2.12) # # @description: Free-form description of the export, up to 4096 bytes. -# (Since 5.0) +# (Since 5.0) # # Since: 5.0 ## @@ -91,15 +101,15 @@ # block-export-add). # # @bitmaps: Also export each of the named dirty bitmaps reachable from -# @device, so the NBD client can use NBD_OPT_SET_META_CONTEXT with -# the metadata context name "qemu:dirty-bitmap:BITMAP" to inspect -# each bitmap. -# Since 7.1 bitmap may be specified by node/name pair. +# @device, so the NBD client can use NBD_OPT_SET_META_CONTEXT with +# the metadata context name "qemu:dirty-bitmap:BITMAP" to inspect +# each bitmap. Since 7.1 bitmap may be specified by node/name +# pair. # -# @allocation-depth: Also export the allocation depth map for @device, so -# the NBD client can use NBD_OPT_SET_META_CONTEXT with -# the metadata context name "qemu:allocation-depth" to -# inspect allocation details. (since 5.2) +# @allocation-depth: Also export the allocation depth map for @device, +# so the NBD client can use NBD_OPT_SET_META_CONTEXT with the +# metadata context name "qemu:allocation-depth" to inspect +# allocation details. (since 5.2) # # Since: 5.2 ## @@ -113,12 +123,15 @@ # # A vhost-user-blk block export. # -# @addr: The vhost-user socket on which to listen. Both 'unix' and 'fd' -# SocketAddress types are supported. Passed fds must be UNIX domain -# sockets. -# @logical-block-size: Logical block size in bytes. Defaults to 512 bytes. -# @num-queues: Number of request virtqueues. Must be greater than 0. Defaults -# to 1. +# @addr: The vhost-user socket on which to listen. Both 'unix' and +# 'fd' SocketAddress types are supported. Passed fds must be UNIX +# domain sockets. +# +# @logical-block-size: Logical block size in bytes. Defaults to 512 +# bytes. +# +# @num-queues: Number of request virtqueues. Must be greater than 0. +# Defaults to 1. # # Since: 5.2 ## @@ -137,7 +150,7 @@ # @on: Pass allow_other as a mount option. # # @auto: Try mounting with allow_other first, and if that fails, retry -# without allow_other. +# without allow_other. # # Since: 6.1 ## @@ -150,24 +163,21 @@ # Options for exporting a block graph node on some (file) mountpoint # as a raw image. # -# @mountpoint: Path on which to export the block device via FUSE. -# This must point to an existing regular file. +# @mountpoint: Path on which to export the block device via FUSE. This +# must point to an existing regular file. # # @growable: Whether writes beyond the EOF should grow the block node -# accordingly. (default: false) +# accordingly. (default: false) # # @allow-other: If this is off, only qemu's user is allowed access to -# this export. That cannot be changed even with chmod or -# chown. -# Enabling this option will allow other users access to -# the export with the FUSE mount option "allow_other". -# Note that using allow_other as a non-root user requires -# user_allow_other to be enabled in the global fuse.conf -# configuration file. -# In auto mode (the default), the FUSE export driver will -# first attempt to mount the export with allow_other, and -# if that fails, try again without. -# (since 6.1; default: auto) +# this export. That cannot be changed even with chmod or chown. +# Enabling this option will allow other users access to the export +# with the FUSE mount option "allow_other". Note that using +# allow_other as a non-root user requires user_allow_other to be +# enabled in the global fuse.conf configuration file. In auto +# mode (the default), the FUSE export driver will first attempt to +# mount the export with allow_other, and if that fails, try again +# without. (since 6.1; default: auto) # # Since: 6.0 ## @@ -177,6 +187,32 @@ '*allow-other': 'FuseExportAllowOther' }, 'if': 'CONFIG_FUSE' } +## +# @BlockExportOptionsVduseBlk: +# +# A vduse-blk block export. +# +# @name: the name of VDUSE device (must be unique across the host). +# +# @num-queues: the number of virtqueues. Defaults to 1. +# +# @queue-size: the size of virtqueue. Defaults to 256. +# +# @logical-block-size: Logical block size in bytes. Range [512, +# PAGE_SIZE] and must be power of 2. Defaults to 512 bytes. +# +# @serial: the serial number of virtio block device. Defaults to +# empty string. +# +# Since: 7.1 +## +{ 'struct': 'BlockExportOptionsVduseBlk', + 'data': { 'name': 'str', + '*num-queues': 'uint16', + '*queue-size': 'uint16', + '*logical-block-size': 'size', + '*serial': 'str' } } + ## # @NbdServerAddOptions: # @@ -184,13 +220,13 @@ # # @device: The device name or node name of the node to be exported # -# @writable: Whether clients should be able to write to the device via the -# NBD connection (default false). +# @writable: Whether clients should be able to write to the device via +# the NBD connection (default false). # -# @bitmap: Also export a single dirty bitmap reachable from @device, so the -# NBD client can use NBD_OPT_SET_META_CONTEXT with the metadata -# context name "qemu:dirty-bitmap:BITMAP" to inspect the bitmap -# (since 4.0). +# @bitmap: Also export a single dirty bitmap reachable from @device, +# so the NBD client can use NBD_OPT_SET_META_CONTEXT with the +# metadata context name "qemu:dirty-bitmap:BITMAP" to inspect the +# bitmap (since 4.0). # # Since: 5.0 ## @@ -204,35 +240,42 @@ # # Export a block node to QEMU's embedded NBD server. # -# The export name will be used as the id for the resulting block export. +# The export name will be used as the id for the resulting block +# export. # # Features: -# @deprecated: This command is deprecated. Use @block-export-add instead. # -# Returns: error if the server is not running, or export with the same name -# already exists. +# @deprecated: This command is deprecated. Use @block-export-add +# instead. +# +# Errors: +# - if the server is not running +# - if an export with the same name already exists # # Since: 1.3 ## { 'command': 'nbd-server-add', - 'data': 'NbdServerAddOptions', 'boxed': true, 'features': ['deprecated'] } + 'data': 'NbdServerAddOptions', 'boxed': true, 'features': ['deprecated'], + 'allow-preconfig': true } ## # @BlockExportRemoveMode: # # Mode for removing a block export. # -# @safe: Remove export if there are no existing connections, fail otherwise. +# @safe: Remove export if there are no existing connections, fail +# otherwise. # # @hard: Drop all connections immediately and remove export. # # TODO: Potential additional modes to be added in the future: # -# hide: Just hide export from new clients, leave existing connections as is. -# Remove export after all clients are disconnected. +# - hide: Just hide export from new clients, leave existing +# connections as is. Remove export after all clients are +# disconnected. # -# soft: Hide export from new clients, answer with ESHUTDOWN for all further -# requests from existing clients. +# - soft: Hide export from new clients, answer with ESHUTDOWN for +# all further requests from existing clients. # # Since: 2.12 ## @@ -245,32 +288,36 @@ # # @name: Block export id. # -# @mode: Mode of command operation. See @BlockExportRemoveMode description. -# Default is 'safe'. +# @mode: Mode of command operation. See @BlockExportRemoveMode +# description. Default is 'safe'. # # Features: -# @deprecated: This command is deprecated. Use @block-export-del instead. # -# Returns: error if -# - the server is not running -# - export is not found -# - mode is 'safe' and there are existing connections +# @deprecated: This command is deprecated. Use @block-export-del +# instead. +# +# Errors: +# - if the server is not running +# - if export is not found +# - if mode is 'safe' and there are existing connections # # Since: 2.12 ## { 'command': 'nbd-server-remove', 'data': {'name': 'str', '*mode': 'BlockExportRemoveMode'}, - 'features': ['deprecated'] } + 'features': ['deprecated'], + 'allow-preconfig': true } ## # @nbd-server-stop: # -# Stop QEMU's embedded NBD server, and unregister all devices previously -# added via @nbd-server-add. +# Stop QEMU's embedded NBD server, and unregister all devices +# previously added via @nbd-server-add. # # Since: 1.3 ## -{ 'command': 'nbd-server-stop' } +{ 'command': 'nbd-server-stop', + 'allow-preconfig': true } ## # @BlockExportType: @@ -278,42 +325,52 @@ # An enumeration of block export types # # @nbd: NBD export +# # @vhost-user-blk: vhost-user-blk export (since 5.2) +# # @fuse: FUSE export (since: 6.0) # +# @vduse-blk: vduse-blk export (since 7.1) +# # Since: 4.2 ## { 'enum': 'BlockExportType', 'data': [ 'nbd', { 'name': 'vhost-user-blk', 'if': 'CONFIG_VHOST_USER_BLK_SERVER' }, - { 'name': 'fuse', 'if': 'CONFIG_FUSE' } ] } + { 'name': 'fuse', 'if': 'CONFIG_FUSE' }, + { 'name': 'vduse-blk', 'if': 'CONFIG_VDUSE_BLK_EXPORT' } ] } ## # @BlockExportOptions: # -# Describes a block export, i.e. how single node should be exported on an -# external interface. +# Describes a block export, i.e. how single node should be exported on +# an external interface. # -# @id: A unique identifier for the block export (across all export types) +# @type: Block export type # -# @node-name: The node name of the block node to be exported (since: 5.2) +# @id: A unique identifier for the block export (across all export +# types) +# +# @node-name: The node name of the block node to be exported +# (since: 5.2) # # @writable: True if clients should be able to write to the export -# (default false) +# (default false) # -# @writethrough: If true, caches are flushed after every write request to the -# export before completion is signalled. (since: 5.2; -# default: false) +# @writethrough: If true, caches are flushed after every write request +# to the export before completion is signalled. (since: 5.2; +# default: false) # -# @iothread: The name of the iothread object where the export will run. The -# default is to use the thread currently associated with the -# block node. (since: 5.2) +# @iothread: The name of the iothread object where the export will +# run. The default is to use the thread currently associated with +# the block node. (since: 5.2) # -# @fixed-iothread: True prevents the block node from being moved to another -# thread while the export is active. If true and @iothread is -# given, export creation fails if the block node cannot be -# moved to the iothread. The default is false. (since: 5.2) +# @fixed-iothread: True prevents the block node from being moved to +# another thread while the export is active. If true and +# @iothread is given, export creation fails if the block node +# cannot be moved to the iothread. The default is false. +# (since: 5.2) # # Since: 4.2 ## @@ -331,7 +388,9 @@ 'vhost-user-blk': { 'type': 'BlockExportOptionsVhostUserBlk', 'if': 'CONFIG_VHOST_USER_BLK_SERVER' }, 'fuse': { 'type': 'BlockExportOptionsFuse', - 'if': 'CONFIG_FUSE' } + 'if': 'CONFIG_FUSE' }, + 'vduse-blk': { 'type': 'BlockExportOptionsVduseBlk', + 'if': 'CONFIG_VDUSE_BLK_EXPORT' } } } ## @@ -342,27 +401,31 @@ # Since: 5.2 ## { 'command': 'block-export-add', - 'data': 'BlockExportOptions', 'boxed': true } + 'data': 'BlockExportOptions', 'boxed': true, + 'allow-preconfig': true } ## # @block-export-del: # -# Request to remove a block export. This drops the user's reference to the -# export, but the export may still stay around after this command returns until -# the shutdown of the export has completed. +# Request to remove a block export. This drops the user's reference +# to the export, but the export may still stay around after this +# command returns until the shutdown of the export has completed. # # @id: Block export id. # -# @mode: Mode of command operation. See @BlockExportRemoveMode description. -# Default is 'safe'. +# @mode: Mode of command operation. See @BlockExportRemoveMode +# description. Default is 'safe'. # -# Returns: Error if the export is not found or @mode is 'safe' and the export -# is still in use (e.g. by existing client connections) +# Errors: +# - if the export is not found +# - if @mode is 'safe' and the export is still in use (e.g. by +# existing client connections) # # Since: 5.2 ## { 'command': 'block-export-del', - 'data': { 'id': 'str', '*mode': 'BlockExportRemoveMode' } } + 'data': { 'id': 'str', '*mode': 'BlockExportRemoveMode' }, + 'allow-preconfig': true } ## # @BLOCK_EXPORT_DELETED: @@ -388,8 +451,7 @@ # @node-name: The node name of the block node that is exported # # @shutting-down: True if the export is shutting down (e.g. after a -# block-export-del command, but before the shutdown has -# completed) +# block-export-del command, but before the shutdown has completed) # # Since: 5.2 ## @@ -406,4 +468,5 @@ # # Since: 5.2 ## -{ 'command': 'query-block-exports', 'returns': ['BlockExportInfo'] } +{ 'command': 'query-block-exports', 'returns': ['BlockExportInfo'], + 'allow-preconfig': true } diff --git a/qapi/block.json b/qapi/block.json index 19326641ac..5de99fe09d 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -19,26 +19,26 @@ # translate logical CHS to physical; instead, they will use logical # block addressing. # -# @auto: If cylinder/heads/sizes are passed, choose between none and LBA -# depending on the size of the disk. If they are not passed, -# choose none if QEMU can guess that the disk had 16 or fewer -# heads, large if QEMU can guess that the disk had 131072 or -# fewer tracks across all heads (i.e. cylinders*heads<131072), -# otherwise LBA. +# @auto: If cylinder/heads/sizes are passed, choose between none and +# LBA depending on the size of the disk. If they are not passed, +# choose none if QEMU can guess that the disk had 16 or fewer +# heads, large if QEMU can guess that the disk had 131072 or fewer +# tracks across all heads (i.e. cylinders*heads<131072), otherwise +# LBA. # # @none: The physical disk geometry is equal to the logical geometry. # # @lba: Assume 63 sectors per track and one of 16, 32, 64, 128 or 255 -# heads (if fewer than 255 are enough to cover the whole disk -# with 1024 cylinders/head). The number of cylinders/head is -# then computed based on the number of sectors and heads. +# heads (if fewer than 255 are enough to cover the whole disk with +# 1024 cylinders/head). The number of cylinders/head is then +# computed based on the number of sectors and heads. # -# @large: The number of cylinders per head is scaled down to 1024 -# by correspondingly scaling up the number of heads. +# @large: The number of cylinders per head is scaled down to 1024 by +# correspondingly scaling up the number of heads. # # @rechs: Same as @large, but first convert a 16-head geometry to -# 15-head, by proportionally scaling up the number of -# cylinders/head. +# 15-head, by proportionally scaling up the number of +# cylinders/head. # # Since: 2.0 ## @@ -51,9 +51,13 @@ # Type of Floppy drive to be emulated by the Floppy Disk Controller. # # @144: 1.44MB 3.5" drive +# # @288: 2.88MB 3.5" drive +# # @120: 1.2MB 5.25" drive +# # @none: No drive connected +# # @auto: Automatically determined by inserted media at boot # # Since: 2.6 @@ -68,8 +72,8 @@ # # @id: the identifier of the persistent reservation manager # -# @connected: true if the persistent reservation manager is connected to -# the underlying storage or helper +# @connected: true if the persistent reservation manager is connected +# to the underlying storage or helper # # Since: 3.0 ## @@ -79,9 +83,11 @@ ## # @query-pr-managers: # -# Returns a list of information about each persistent reservation manager. +# Returns a list of information about each persistent reservation +# manager. # -# Returns: a list of @PRManagerInfo for each persistent reservation manager +# Returns: a list of @PRManagerInfo for each persistent reservation +# manager # # Since: 3.0 ## @@ -98,13 +104,14 @@ # @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. +# If not specified, the default value is false. # # Features: +# # @deprecated: Member @device is deprecated. Use @id instead. # -# Returns: - Nothing on success -# - If @device is not a valid block device, DeviceNotFound +# Errors: +# - If @device is not a valid block device, DeviceNotFound # # Notes: Ejecting a device with no media results in success # @@ -112,8 +119,8 @@ # # Example: # -# -> { "execute": "eject", "arguments": { "id": "ide1-0-1" } } -# <- { "return": {} } +# -> { "execute": "eject", "arguments": { "id": "ide1-0-1" } } +# <- { "return": {} } ## { 'command': 'eject', 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, @@ -123,50 +130,50 @@ ## # @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). +# 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: +# 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 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 +# @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" } } +# -> { "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": {} } +# <- { "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': { 'type': 'str', 'features': [ 'deprecated' ] }, @@ -176,9 +183,9 @@ ## # @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. +# 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. # @@ -187,24 +194,24 @@ # @id: The name or QOM path of the guest device (since: 2.8) # # Features: +# # @deprecated: Member @device is deprecated. Use @id instead. # # Since: 2.5 # # Example: # -# -> { "execute": "blockdev-close-tray", -# "arguments": { "id": "ide0-1-0" } } +# -> { "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": {} } +# <- { "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': { 'type': 'str', 'features': [ 'deprecated' ] }, @@ -213,11 +220,12 @@ ## # @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). +# 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. +# 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 # @@ -225,29 +233,28 @@ # # Example: # -# -> { "execute": "blockdev-remove-medium", -# "arguments": { "id": "ide0-1-0" } } +# -> { "execute": "blockdev-remove-medium", +# "arguments": { "id": "ide0-1-0" } } # -# <- { "error": { "class": "GenericError", -# "desc": "Tray of device 'ide0-1-0' is not open" } } +# <- { "error": { "class": "GenericError", +# "desc": "Tray of device 'ide0-1-0' is not open" } } # -# -> { "execute": "blockdev-open-tray", -# "arguments": { "id": "ide0-1-0" } } +# -> { "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 } } +# <- { "timestamp": { "seconds": 1418751627, +# "microseconds": 549958 }, +# "event": "DEVICE_TRAY_MOVED", +# "data": { "device": "ide1-cd0", +# "id": "ide0-1-0", +# "tray-open": true } } # -# <- { "return": {} } +# <- { "return": {} } # -# -> { "execute": "blockdev-remove-medium", -# "arguments": { "id": "ide0-1-0" } } -# -# <- { "return": {} } +# -> { "execute": "blockdev-remove-medium", +# "arguments": { "id": "ide0-1-0" } } # +# <- { "return": {} } ## { 'command': 'blockdev-remove-medium', 'data': { 'id': 'str' } } @@ -255,9 +262,9 @@ ## # @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. +# 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 # @@ -267,20 +274,19 @@ # # Example: # -# -> { "execute": "blockdev-add", -# "arguments": { -# "node-name": "node0", -# "driver": "raw", -# "file": { "driver": "file", -# "filename": "fedora.iso" } } } -# <- { "return": {} } +# -> { "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": {} } +# -> { "execute": "blockdev-insert-medium", +# "arguments": { "id": "ide0-1-0", +# "node-name": "node0" } } # +# <- { "return": {} } ## { 'command': 'blockdev-insert-medium', 'data': { 'id': 'str', @@ -306,64 +312,65 @@ ## # @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). +# 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) +# @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) +# @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' +# to 'retain' # -# @force: if false (the default), an eject request through blockdev-open-tray -# will be sent to the guest if it has locked the tray (and the tray -# will not be opened immediately); if true, the tray will be opened -# regardless of whether it is locked. (since 7.1) +# @force: if false (the default), an eject request through +# blockdev-open-tray will be sent to the guest if it has locked +# the tray (and the tray will not be opened immediately); if true, +# the tray will be opened regardless of whether it is locked. +# (since 7.1) # # Features: +# # @deprecated: Member @device is deprecated. Use @id instead. # # Since: 2.5 # # Examples: # -# 1. Change a removable medium +# 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": {} } +# -> { "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 +# 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" } } +# -> { "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" } } +# <- { "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": {} } +# -> { "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' ] }, @@ -376,28 +383,28 @@ ## # @DEVICE_TRAY_MOVED: # -# Emitted whenever the tray of a removable device is moved by the guest or by -# HMP/QMP commands +# Emitted whenever the tray of a removable device is moved by the +# guest or by HMP/QMP commands # -# @device: Block device name. This is always present for compatibility -# reasons, but it can be empty ("") if the image does not -# have a device name associated. +# @device: Block device name. This is always present for +# compatibility reasons, but it can be empty ("") if the image +# does not have a device name associated. # # @id: The name or QOM path of the guest device (since 2.8) # -# @tray-open: true if the tray has been opened or false if it has been closed +# @tray-open: true if the tray has been opened or false if it has been +# closed # # Since: 1.1 # # Example: # -# <- { "event": "DEVICE_TRAY_MOVED", -# "data": { "device": "ide1-cd0", -# "id": "/machine/unattached/device[22]", -# "tray-open": true -# }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } -# +# <- { "event": "DEVICE_TRAY_MOVED", +# "data": { "device": "ide1-cd0", +# "id": "/machine/unattached/device[22]", +# "tray-open": true +# }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'DEVICE_TRAY_MOVED', 'data': { 'device': 'str', 'id': 'str', 'tray-open': 'bool' } } @@ -416,12 +423,11 @@ # # Example: # -# <- { "event": "PR_MANAGER_STATUS_CHANGED", -# "data": { "id": "pr-helper0", -# "connected": true -# }, -# "timestamp": { "seconds": 1519840375, "microseconds": 450486 } } -# +# <- { "event": "PR_MANAGER_STATUS_CHANGED", +# "data": { "id": "pr-helper0", +# "connected": true +# }, +# "timestamp": { "seconds": 1519840375, "microseconds": 450486 } } ## { 'event': 'PR_MANAGER_STATUS_CHANGED', 'data': { 'id': 'str', 'connected': 'bool' } } @@ -436,140 +442,152 @@ # # 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 +# 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 +# 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. +# 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. +# members will not be affected. The 'group' parameter is ignored. # -# Returns: - Nothing on success -# - If @device is not a valid block device, DeviceNotFound +# Errors: +# - If @device is not a valid block device, DeviceNotFound # # Since: 1.1 # -# Example: +# Examples: # -# -> { "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": "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": {} } +# -> { "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' } + 'data': 'BlockIOThrottle', + 'allow-preconfig': true } ## # @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. +# 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). +# 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. +# 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-write: list of interval boundary values for write +# latency histogram. # -# @boundaries-flush: list of interval boundary values for flush latency -# histogram. +# @boundaries-zap: list of interval boundary values for zone append +# write latency histogram. # -# Returns: error if device is not found or any boundary arrays are invalid. +# @boundaries-flush: list of interval boundary values for flush +# latency histogram. +# +# Errors: +# - 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": {} } +# 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": {} } +# 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": {} } +# 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": {} } +# 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'] } } + '*boundaries-zap': ['uint64'], + '*boundaries-flush': ['uint64'] }, + 'allow-preconfig': true } diff --git a/qapi/char.json b/qapi/char.json index 923dc5056d..777dde55d9 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -17,12 +17,12 @@ # # @filename: the filename of the character device # -# @frontend-open: shows whether the frontend device attached to this backend -# (eg. with the chardev=... option) is in open or closed state -# (since 2.1) +# @frontend-open: shows whether the frontend device attached to this +# backend (e.g. with the chardev=... option) is in open or closed +# state (since 2.1) # -# Notes: @filename is encoded using the QEMU command line character device -# encoding. See the QEMU man page for details. +# Notes: @filename is encoded using the QEMU command line character +# device encoding. See the QEMU man page for details. # # Since: 0.14 ## @@ -42,27 +42,26 @@ # # Example: # -# -> { "execute": "query-chardev" } -# <- { -# "return": [ -# { -# "label": "charchannel0", -# "filename": "unix:/var/lib/libvirt/qemu/seabios.rhel6.agent,server=on", -# "frontend-open": false -# }, -# { -# "label": "charmonitor", -# "filename": "unix:/var/lib/libvirt/qemu/seabios.rhel6.monitor,server=on", -# "frontend-open": true -# }, -# { -# "label": "charserial0", -# "filename": "pty:/dev/pts/2", -# "frontend-open": true -# } -# ] -# } -# +# -> { "execute": "query-chardev" } +# <- { +# "return": [ +# { +# "label": "charchannel0", +# "filename": "unix:/var/lib/libvirt/qemu/seabios.rhel6.agent,server=on", +# "frontend-open": false +# }, +# { +# "label": "charmonitor", +# "filename": "unix:/var/lib/libvirt/qemu/seabios.rhel6.monitor,server=on", +# "frontend-open": true +# }, +# { +# "label": "charserial0", +# "filename": "pty:/dev/pts/2", +# "frontend-open": true +# } +# ] +# } ## { 'command': 'query-chardev', 'returns': ['ChardevInfo'], 'allow-preconfig': true } @@ -89,24 +88,23 @@ # # Example: # -# -> { "execute": "query-chardev-backends" } -# <- { -# "return":[ -# { -# "name":"udp" -# }, -# { -# "name":"tcp" -# }, -# { -# "name":"unix" -# }, -# { -# "name":"spiceport" -# } -# ] -# } -# +# -> { "execute": "query-chardev-backends" } +# <- { +# "return":[ +# { +# "name":"udp" +# }, +# { +# "name":"tcp" +# }, +# { +# "name":"unix" +# }, +# { +# "name":"spiceport" +# } +# ] +# } ## { 'command': 'query-chardev-backends', 'returns': ['ChardevBackendInfo'] } @@ -135,24 +133,21 @@ # # @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 -# - data itself is always Unicode regardless of format, like -# any other string. -# -# Returns: Nothing on success +# - base64: data must be base64 encoded text. Its binary decoding +# gets written. +# - utf8: data's UTF-8 encoding is written +# - data itself is always Unicode regardless of format, like any +# other string. # # Since: 1.4 # # Example: # -# -> { "execute": "ringbuf-write", -# "arguments": { "device": "foo", -# "data": "abcdefgh", -# "format": "utf8" } } -# <- { "return": {} } -# +# -> { "execute": "ringbuf-write", +# "arguments": { "device": "foo", +# "data": "abcdefgh", +# "format": "utf8" } } +# <- { "return": {} } ## { 'command': 'ringbuf-write', 'data': { 'device': 'str', @@ -170,14 +165,13 @@ # # @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 -# sequences, NUL characters, after the ring buffer lost -# data, and when reading stops because the size limit is -# reached. -# - The return value is always Unicode regardless of format, -# like any other string. +# - 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 +# sequences, NUL characters, after the ring buffer lost data, +# and when reading stops because the size limit is reached. +# - The return value is always Unicode regardless of format, like +# any other string. # # Returns: data read from the device # @@ -185,12 +179,11 @@ # # Example: # -# -> { "execute": "ringbuf-read", -# "arguments": { "device": "foo", -# "size": 1000, -# "format": "utf8" } } -# <- { "return": "abcdefgh" } -# +# -> { "execute": "ringbuf-read", +# "arguments": { "device": "foo", +# "size": 1000, +# "format": "utf8" } } +# <- { "return": "abcdefgh" } ## { 'command': 'ringbuf-read', 'data': {'device': 'str', 'size': 'int', '*format': 'DataFormat'}, @@ -202,8 +195,9 @@ # Configuration shared across all chardev backends # # @logfile: The name of a logfile to save output -# @logappend: true to append instead of truncate -# (default to false to truncate) +# +# @logappend: true to append instead of truncate (default to false to +# truncate) # # Since: 2.6 ## @@ -217,9 +211,11 @@ # Configuration info for file chardevs. # # @in: The name of the input file +# # @out: The name of the output file -# @append: Open the file in append mode (default false to -# truncate) (Since 2.6) +# +# @append: Open the file in append mode (default false to truncate) +# (Since 2.6) # # Since: 1.4 ## @@ -234,8 +230,8 @@ # # Configuration info for device and pipe chardevs. # -# @device: The name of the special file for the device, -# i.e. /dev/ttyS0 on Unix or COM1: on Windows +# @device: The name of the special file for the device, i.e. +# /dev/ttyS0 on Unix or COM1: on Windows # # Since: 1.4 ## @@ -248,29 +244,36 @@ # # Configuration info for (stream) socket chardevs. # -# @addr: socket address to listen on (server=true) -# or connect to (server=false) +# @addr: socket address to listen on (server=true) or connect to +# (server=false) +# # @tls-creds: the ID of the TLS credentials object (since 2.6) +# # @tls-authz: 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 (since 4.0) +# 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 (since 4.0) +# # @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. +# +# @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) -# @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) +# +# @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) +# +# @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) # # Since: 1.4 ## @@ -293,6 +296,7 @@ # Configuration info for datagram socket chardevs. # # @remote: remote address +# # @local: local address # # Since: 1.5 @@ -320,8 +324,8 @@ # # Configuration info for stdio chardevs. # -# @signal: Allow signals (such as SIGINT triggered by ^C) -# be delivered to qemu. Default: true. +# @signal: Allow signals (such as SIGINT triggered by ^C) be delivered +# to qemu. Default: true. # # Since: 1.5 ## @@ -377,10 +381,17 @@ # Configuration info for virtual console chardevs. # # @width: console width, in pixels +# # @height: console height, in pixels +# # @cols: console width, in chars +# # @rows: console height, in chars # +# Note: the options are only effective when the VNC or SDL graphical +# display backend is active. They are ignored with the GTK, +# Spice, VNC and D-Bus display backends. +# # Since: 1.5 ## { 'struct': 'ChardevVC', @@ -409,6 +420,7 @@ # Configuration info for qemu vdagent implementation. # # @mouse: enable/disable mouse, default is enabled. +# # @clipboard: enable/disable clipboard, default is disabled. # # Since: 6.1 @@ -423,28 +435,47 @@ # @ChardevBackendKind: # # @pipe: Since 1.5 +# # @udp: Since 1.5 +# # @mux: Since 1.5 +# # @msmouse: Since 1.5 +# # @wctablet: Since 2.9 +# # @braille: Since 1.5 +# # @testdev: Since 2.2 +# # @stdio: Since 1.5 +# # @console: Since 1.5 +# # @spicevmc: Since 1.5 +# # @spiceport: Since 1.5 +# # @qemu-vdagent: Since 6.1 +# # @dbus: Since 7.0 +# # @vc: v1.5 +# # @ringbuf: Since 1.6 +# # @memory: Since 1.5 # +# Features: +# +# @deprecated: Member @memory is deprecated. Use @ringbuf instead. +# # Since: 1.4 ## { 'enum': 'ChardevBackendKind', 'data': [ 'file', - 'serial', - 'parallel', + { 'name': 'serial', 'if': 'HAVE_CHARDEV_SERIAL' }, + { 'name': 'parallel', 'if': 'HAVE_CHARDEV_PARALLEL' }, 'pipe', 'socket', 'udp', @@ -453,22 +484,23 @@ 'mux', 'msmouse', 'wctablet', - 'braille', + { 'name': 'braille', 'if': 'CONFIG_BRLAPI' }, 'testdev', 'stdio', - 'console', + { 'name': 'console', 'if': 'CONFIG_WIN32' }, { 'name': 'spicevmc', 'if': 'CONFIG_SPICE' }, { 'name': 'spiceport', 'if': 'CONFIG_SPICE' }, { 'name': 'qemu-vdagent', 'if': 'CONFIG_SPICE_PROTOCOL' }, { 'name': 'dbus', 'if': 'CONFIG_DBUS_DISPLAY' }, 'vc', 'ringbuf', - # next one is just for compatibility - 'memory' ] } + { 'name': 'memory', 'features': [ 'deprecated' ] } ] } ## # @ChardevFileWrapper: # +# @data: Configuration info for file chardevs +# # Since: 1.4 ## { 'struct': 'ChardevFileWrapper', @@ -477,6 +509,8 @@ ## # @ChardevHostdevWrapper: # +# @data: Configuration info for device and pipe chardevs +# # Since: 1.4 ## { 'struct': 'ChardevHostdevWrapper', @@ -485,6 +519,8 @@ ## # @ChardevSocketWrapper: # +# @data: Configuration info for (stream) socket chardevs +# # Since: 1.4 ## { 'struct': 'ChardevSocketWrapper', @@ -493,6 +529,8 @@ ## # @ChardevUdpWrapper: # +# @data: Configuration info for datagram socket chardevs +# # Since: 1.5 ## { 'struct': 'ChardevUdpWrapper', @@ -501,6 +539,8 @@ ## # @ChardevCommonWrapper: # +# @data: Configuration shared across all chardev backends +# # Since: 2.6 ## { 'struct': 'ChardevCommonWrapper', @@ -509,6 +549,8 @@ ## # @ChardevMuxWrapper: # +# @data: Configuration info for mux chardevs +# # Since: 1.5 ## { 'struct': 'ChardevMuxWrapper', @@ -517,6 +559,8 @@ ## # @ChardevStdioWrapper: # +# @data: Configuration info for stdio chardevs +# # Since: 1.5 ## { 'struct': 'ChardevStdioWrapper', @@ -525,6 +569,8 @@ ## # @ChardevSpiceChannelWrapper: # +# @data: Configuration info for spice vm channel chardevs +# # Since: 1.5 ## { 'struct': 'ChardevSpiceChannelWrapper', @@ -534,6 +580,8 @@ ## # @ChardevSpicePortWrapper: # +# @data: Configuration info for spice port chardevs +# # Since: 1.5 ## { 'struct': 'ChardevSpicePortWrapper', @@ -543,6 +591,8 @@ ## # @ChardevQemuVDAgentWrapper: # +# @data: Configuration info for qemu vdagent implementation +# # Since: 6.1 ## { 'struct': 'ChardevQemuVDAgentWrapper', @@ -552,6 +602,8 @@ ## # @ChardevDBusWrapper: # +# @data: Configuration info for DBus chardevs +# # Since: 7.0 ## { 'struct': 'ChardevDBusWrapper', @@ -561,6 +613,8 @@ ## # @ChardevVCWrapper: # +# @data: Configuration info for virtual console chardevs +# # Since: 1.5 ## { 'struct': 'ChardevVCWrapper', @@ -569,6 +623,8 @@ ## # @ChardevRingbufWrapper: # +# @data: Configuration info for ring buffer chardevs +# # Since: 1.5 ## { 'struct': 'ChardevRingbufWrapper', @@ -579,14 +635,18 @@ # # Configuration info for the new chardev backend. # +# @type: backend type +# # Since: 1.4 ## { 'union': 'ChardevBackend', 'base': { 'type': 'ChardevBackendKind' }, 'discriminator': 'type', 'data': { 'file': 'ChardevFileWrapper', - 'serial': 'ChardevHostdevWrapper', - 'parallel': 'ChardevHostdevWrapper', + 'serial': { 'type': 'ChardevHostdevWrapper', + 'if': 'HAVE_CHARDEV_SERIAL' }, + 'parallel': { 'type': 'ChardevHostdevWrapper', + 'if': 'HAVE_CHARDEV_PARALLEL' }, 'pipe': 'ChardevHostdevWrapper', 'socket': 'ChardevSocketWrapper', 'udp': 'ChardevUdpWrapper', @@ -595,10 +655,12 @@ 'mux': 'ChardevMuxWrapper', 'msmouse': 'ChardevCommonWrapper', 'wctablet': 'ChardevCommonWrapper', - 'braille': 'ChardevCommonWrapper', + 'braille': { 'type': 'ChardevCommonWrapper', + 'if': 'CONFIG_BRLAPI' }, 'testdev': 'ChardevCommonWrapper', 'stdio': 'ChardevStdioWrapper', - 'console': 'ChardevCommonWrapper', + 'console': { 'type': 'ChardevCommonWrapper', + 'if': 'CONFIG_WIN32' }, 'spicevmc': { 'type': 'ChardevSpiceChannelWrapper', 'if': 'CONFIG_SPICE' }, 'spiceport': { 'type': 'ChardevSpicePortWrapper', @@ -609,7 +671,6 @@ 'if': 'CONFIG_DBUS_DISPLAY' }, 'vc': 'ChardevVCWrapper', 'ringbuf': 'ChardevRingbufWrapper', - # next one is just for compatibility 'memory': 'ChardevRingbufWrapper' } } ## @@ -617,8 +678,8 @@ # # Return info about the chardev backend just created. # -# @pty: name of the slave pseudoterminal device, present if -# and only if a chardev of type 'pty' was created +# @pty: name of the slave pseudoterminal device, present if and only +# if a chardev of type 'pty' was created # # Since: 1.4 ## @@ -631,30 +692,30 @@ # Add a character device backend # # @id: the chardev's ID, must be unique +# # @backend: backend type and parameters # # Returns: ChardevReturn. # # Since: 1.4 # -# Example: +# Examples: # -# -> { "execute" : "chardev-add", -# "arguments" : { "id" : "foo", -# "backend" : { "type" : "null", "data" : {} } } } -# <- { "return": {} } +# -> { "execute" : "chardev-add", +# "arguments" : { "id" : "foo", +# "backend" : { "type" : "null", "data" : {} } } } +# <- { "return": {} } # -# -> { "execute" : "chardev-add", -# "arguments" : { "id" : "bar", -# "backend" : { "type" : "file", -# "data" : { "out" : "/tmp/bar.log" } } } } -# <- { "return": {} } -# -# -> { "execute" : "chardev-add", -# "arguments" : { "id" : "baz", -# "backend" : { "type" : "pty", "data" : {} } } } -# <- { "return": { "pty" : "/dev/pty/42" } } +# -> { "execute" : "chardev-add", +# "arguments" : { "id" : "bar", +# "backend" : { "type" : "file", +# "data" : { "out" : "/tmp/bar.log" } } } } +# <- { "return": {} } # +# -> { "execute" : "chardev-add", +# "arguments" : { "id" : "baz", +# "backend" : { "type" : "pty", "data" : {} } } } +# <- { "return": { "pty" : "/dev/pty/42" } } ## { 'command': 'chardev-add', 'data': { 'id': 'str', @@ -667,35 +728,35 @@ # Change a character device backend # # @id: the chardev's ID, must exist +# # @backend: new backend type and parameters # # Returns: ChardevReturn. # # Since: 2.10 # -# Example: +# Examples: # -# -> { "execute" : "chardev-change", -# "arguments" : { "id" : "baz", -# "backend" : { "type" : "pty", "data" : {} } } } -# <- { "return": { "pty" : "/dev/pty/42" } } -# -# -> {"execute" : "chardev-change", -# "arguments" : { -# "id" : "charchannel2", -# "backend" : { -# "type" : "socket", -# "data" : { -# "addr" : { -# "type" : "unix" , -# "data" : { -# "path" : "/tmp/charchannel2.socket" -# } -# }, -# "server" : true, -# "wait" : false }}}} -# <- {"return": {}} +# -> { "execute" : "chardev-change", +# "arguments" : { "id" : "baz", +# "backend" : { "type" : "pty", "data" : {} } } } +# <- { "return": { "pty" : "/dev/pty/42" } } # +# -> {"execute" : "chardev-change", +# "arguments" : { +# "id" : "charchannel2", +# "backend" : { +# "type" : "socket", +# "data" : { +# "addr" : { +# "type" : "unix" , +# "data" : { +# "path" : "/tmp/charchannel2.socket" +# } +# }, +# "server" : true, +# "wait" : false }}}} +# <- {"return": {}} ## { 'command': 'chardev-change', 'data': { 'id': 'str', @@ -709,15 +770,12 @@ # # @id: the chardev's ID, must exist and not be in use # -# Returns: Nothing on success -# # Since: 1.4 # # Example: # -# -> { "execute": "chardev-remove", "arguments": { "id" : "foo" } } -# <- { "return": {} } -# +# -> { "execute": "chardev-remove", "arguments": { "id" : "foo" } } +# <- { "return": {} } ## { 'command': 'chardev-remove', 'data': { 'id': 'str' } } @@ -729,15 +787,12 @@ # # @id: the chardev's ID, must exist # -# Returns: Nothing on success -# # Since: 2.10 # # Example: # -# -> { "execute": "chardev-send-break", "arguments": { "id" : "foo" } } -# <- { "return": {} } -# +# -> { "execute": "chardev-send-break", "arguments": { "id" : "foo" } } +# <- { "return": {} } ## { 'command': 'chardev-send-break', 'data': { 'id': 'str' } } @@ -757,10 +812,9 @@ # # Example: # -# <- { "event": "VSERPORT_CHANGE", -# "data": { "id": "channel0", "open": true }, -# "timestamp": { "seconds": 1401385907, "microseconds": 422329 } } -# +# <- { "event": "VSERPORT_CHANGE", +# "data": { "id": "channel0", "open": true }, +# "timestamp": { "seconds": 1401385907, "microseconds": 422329 } } ## { 'event': 'VSERPORT_CHANGE', 'data': { 'id': 'str', diff --git a/qapi/common.json b/qapi/common.json index 356db3f670..7558ce5430 100644 --- a/qapi/common.json +++ b/qapi/common.json @@ -51,17 +51,6 @@ { 'enum': 'OnOffSplit', 'data': [ 'on', 'off', 'split' ] } -## -# @String: -# -# A fat type wrapping 'str', to be embedded in lists. -# -# Since: 1.2 -## -{ 'struct': 'String', - 'data': { - 'str': 'str' } } - ## # @StrOrNull: # @@ -70,6 +59,7 @@ # has a different meaning. # # @s: the string value +# # @n: no string value # # Since: 2.10 @@ -117,10 +107,14 @@ # # @16: 16.0GT/s # +# @32: 32.0GT/s (since 9.0) +# +# @64: 64.0GT/s (since 9.0) +# # Since: 4.0 ## { 'enum': 'PCIELinkSpeed', - 'data': [ '2_5', '5', '8', '16' ] } + 'data': [ '2_5', '5', '8', '16', '32', '64' ] } ## # @PCIELinkWidth: @@ -155,11 +149,11 @@ # # @preferred: set the preferred host nodes for allocation # -# @bind: a strict policy that restricts memory allocation to the -# host nodes specified +# @bind: a strict policy that restricts memory allocation to the host +# nodes specified # -# @interleave: memory allocations are interleaved across the set -# of host nodes specified +# @interleave: memory allocations are interleaved across the set of +# host nodes specified # # Since: 2.1 ## @@ -169,17 +163,17 @@ ## # @NetFilterDirection: # -# Indicates whether a netfilter is attached to a netdev's transmit queue or -# receive queue or both. +# Indicates whether a netfilter is attached to a netdev's transmit +# queue or receive queue or both. # # @all: the filter is attached both to the receive and the transmit -# queue of the netdev (default). +# 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. +# 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. +# where it will receive packets sent by the netdev. # # Since: 2.5 ## diff --git a/qapi/compat.json b/qapi/compat.json index 39b52872d5..42034d9368 100644 --- a/qapi/compat.json +++ b/qapi/compat.json @@ -11,7 +11,9 @@ # Policy for handling "funny" input. # # @accept: Accept silently +# # @reject: Reject with an error +# # @crash: abort() the process # # Since: 6.0 @@ -25,6 +27,7 @@ # Policy for handling "funny" output. # # @accept: Pass on unchanged +# # @hide: Filter out # # Since: 6.0 @@ -40,18 +43,22 @@ # This is intended for testing users of the management interfaces. # # Limitation: covers only syntactic aspects of QMP, i.e. stuff tagged -# with feature 'deprecated'. We may want to extend it to cover -# semantic aspects and CLI. +# with feature 'deprecated' or 'unstable'. We may want to extend it +# to cover semantic aspects and CLI. # # Limitation: deprecated-output policy @hide is not implemented for # enumeration values. They behave the same as with policy @accept. # # @deprecated-input: how to handle deprecated input (default 'accept') -# @deprecated-output: how to handle deprecated output (default 'accept') +# +# @deprecated-output: how to handle deprecated output (default +# 'accept') +# # @unstable-input: how to handle unstable input (default 'accept') -# (since 6.2) +# (since 6.2) +# # @unstable-output: how to handle unstable output (default 'accept') -# (since 6.2) +# (since 6.2) # # Since: 6.0 ## diff --git a/qapi/control.json b/qapi/control.json index afca2043af..6bdbf077c2 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -11,26 +11,25 @@ # # 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) +# 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": {} } +# -> { "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) +# 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.rst) # -# The QMP client needs to explicitly enable QMP capabilities, otherwise -# all the QMP capabilities will be turned off by default. +# The QMP client needs to explicitly enable QMP capabilities, +# otherwise all the QMP capabilities will be turned off by +# default. # # Since: 0.13 ## @@ -44,8 +43,8 @@ # 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) +# @oob: QMP ability to support out-of-band requests. (Please refer to +# qmp-spec.rst for more information on OOB) # # Since: 2.12 ## @@ -73,16 +72,16 @@ # # 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. +# @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. +# @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 ## @@ -94,24 +93,24 @@ # # Returns the current version of QEMU. # -# Returns: A @VersionInfo object describing the current version of QEMU. +# Returns: A @VersionInfo object describing the current version of +# QEMU. # # Since: 0.14 # # Example: # -# -> { "execute": "query-version" } -# <- { -# "return":{ -# "qemu":{ -# "major":0, -# "minor":11, -# "micro":5 -# }, -# "package":"" -# } -# } -# +# -> { "execute": "query-version" } +# <- { +# "return":{ +# "qemu":{ +# "major":0, +# "minor":11, +# "micro":5 +# }, +# "package":"" +# } +# } ## { 'command': 'query-version', 'returns': 'VersionInfo', 'allow-preconfig': true } @@ -138,20 +137,20 @@ # # Example: # -# -> { "execute": "query-commands" } -# <- { -# "return":[ -# { -# "name":"query-balloon" -# }, -# { -# "name":"system_powerdown" -# } -# ] -# } -# -# Note: This example has been shortened as the real response is too long. +# -> { "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 } @@ -159,17 +158,17 @@ ## # @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. +# 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 # # Example: # -# -> { "execute": "quit" } -# <- { "return": {} } +# -> { "execute": "quit" } +# <- { "return": {} } ## { 'command': 'quit', 'allow-preconfig': true } @@ -195,7 +194,7 @@ # @id: Name of the monitor # # @mode: Selects the monitor mode (default: readline in the system -# emulator, control in qemu-storage-daemon) +# emulator, control in qemu-storage-daemon) # # @pretty: Enables pretty printing (QMP only) # diff --git a/qapi/crypto.json b/qapi/crypto.json index 653e6e3f3d..e102be337b 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -11,8 +11,7 @@ # # The type of network endpoint that will be using the credentials. # Most types of credential require different setup / structures -# depending on whether they will be used in a server versus a -# client. +# depending on whether they will be used in a server versus a client. # # @client: the network endpoint is acting as the client # @@ -29,7 +28,9 @@ # # The data format that the secret is provided in # -# @raw: raw bytes. When encoded in JSON only valid UTF-8 sequences can be used +# @raw: raw bytes. When encoded in JSON only valid UTF-8 sequences +# can be used +# # @base64: arbitrary base64 encoded binary data # # Since: 2.6 @@ -44,12 +45,18 @@ # The supported algorithms for computing content digests # # @md5: MD5. Should not be used in any new code, legacy compat only +# # @sha1: SHA-1. Should not be used in any new code, legacy compat only -# @sha224: SHA-224. (since 2.7) +# +# @sha224: SHA-224. (since 2.7) +# # @sha256: SHA-256. Current recommended strong hash. -# @sha384: SHA-384. (since 2.7) -# @sha512: SHA-512. (since 2.7) -# @ripemd160: RIPEMD-160. (since 2.7) +# +# @sha384: SHA-384. (since 2.7) +# +# @sha512: SHA-512. (since 2.7) +# +# @ripemd160: RIPEMD-160. (since 2.7) # # Since: 2.6 ## @@ -63,18 +70,32 @@ # The supported algorithms for content encryption ciphers # # @aes-128: AES with 128 bit / 16 byte keys +# # @aes-192: AES with 192 bit / 24 byte keys +# # @aes-256: AES with 256 bit / 32 byte keys -# @des: DES with 56 bit / 8 byte keys. Do not use except in VNC. (since 6.1) +# +# @des: DES with 56 bit / 8 byte keys. Do not use except in VNC. +# (since 6.1) +# # @3des: 3DES(EDE) with 192 bit / 24 byte keys (since 2.9) +# # @cast5-128: Cast5 with 128 bit / 16 byte keys +# # @serpent-128: Serpent with 128 bit / 16 byte keys +# # @serpent-192: Serpent with 192 bit / 24 byte keys +# # @serpent-256: Serpent with 256 bit / 32 byte keys +# # @twofish-128: Twofish with 128 bit / 16 byte keys +# # @twofish-192: Twofish with 192 bit / 24 byte keys +# # @twofish-256: Twofish with 256 bit / 32 byte keys # +# @sm4: SM4 with 128 bit / 16 byte keys (since 9.0) +# # Since: 2.6 ## { 'enum': 'QCryptoCipherAlgorithm', @@ -83,7 +104,8 @@ 'des', '3des', 'cast5-128', 'serpent-128', 'serpent-192', 'serpent-256', - 'twofish-128', 'twofish-192', 'twofish-256']} + 'twofish-128', 'twofish-192', 'twofish-256', + 'sm4']} ## # @QCryptoCipherMode: @@ -91,8 +113,11 @@ # The supported modes for content encryption ciphers # # @ecb: Electronic Code Book +# # @cbc: Cipher Block Chaining +# # @xts: XEX with tweaked code book and ciphertext stealing +# # @ctr: Counter (Since 2.8) # # Since: 2.6 @@ -104,15 +129,17 @@ ## # @QCryptoIVGenAlgorithm: # -# The supported algorithms for generating initialization -# vectors for full disk encryption. The 'plain' generator -# should not be used for disks with sector numbers larger -# than 2^32, except where compatibility with pre-existing -# Linux dm-crypt volumes is required. +# The supported algorithms for generating initialization vectors for +# full disk encryption. The 'plain' generator should not be used for +# disks with sector numbers larger than 2^32, except where +# compatibility with pre-existing Linux dm-crypt volumes is required. # # @plain: 64-bit sector number truncated to 32-bits +# # @plain64: 64-bit sector number -# @essiv: 64-bit sector number encrypted with a hash of the encryption key +# +# @essiv: 64-bit sector number encrypted with a hash of the encryption +# key # # Since: 2.6 ## @@ -125,9 +152,10 @@ # # The supported full disk encryption formats # -# @qcow: QCow/QCow2 built-in AES-CBC encryption. Use only -# for liberating data from old images. -# @luks: LUKS encryption format. Recommended for new images +# @qcow: QCow/QCow2 built-in AES-CBC encryption. Use only for +# liberating data from old images. +# +# @luks: LUKS encryption format. Recommended for new images # # Since: 2.6 ## @@ -138,8 +166,7 @@ ## # @QCryptoBlockOptionsBase: # -# The common options that apply to all full disk -# encryption formats +# The common options that apply to all full disk encryption formats # # @format: the encryption format # @@ -154,8 +181,8 @@ # The options that apply to QCow/QCow2 AES-CBC encryption format # # @key-secret: the ID of a QCryptoSecret object providing the -# decryption key. Mandatory except when probing image for -# metadata only. +# decryption key. Mandatory except when probing image for +# metadata only. # # Since: 2.6 ## @@ -168,8 +195,8 @@ # The options that apply to LUKS encryption format # # @key-secret: the ID of a QCryptoSecret object providing the -# decryption key. Mandatory except when probing image for -# metadata only. +# decryption key. Mandatory except when probing image for +# metadata only. # # Since: 2.6 ## @@ -181,19 +208,25 @@ # # The options that apply to LUKS encryption format initialization # -# @cipher-alg: the cipher algorithm for data encryption -# Currently defaults to 'aes-256'. -# @cipher-mode: the cipher mode for data encryption -# Currently defaults to 'xts' -# @ivgen-alg: the initialization vector generator -# Currently defaults to 'plain64' -# @ivgen-hash-alg: the initialization vector generator hash -# Currently defaults to 'sha256' -# @hash-alg: the master key hash algorithm -# Currently defaults to 'sha256' -# @iter-time: number of milliseconds to spend in -# PBKDF passphrase processing. Currently defaults -# to 2000. (since 2.8) +# @cipher-alg: the cipher algorithm for data encryption Currently +# defaults to 'aes-256'. +# +# @cipher-mode: the cipher mode for data encryption Currently defaults +# to 'xts' +# +# @ivgen-alg: the initialization vector generator Currently defaults +# to 'plain64' +# +# @ivgen-hash-alg: the initialization vector generator hash Currently +# defaults to 'sha256' +# +# @hash-alg: the master key hash algorithm Currently defaults to +# 'sha256' +# +# @iter-time: number of milliseconds to spend in PBKDF passphrase +# processing. Currently defaults to 2000. (since 2.8) +# +# @detached-header: create a detached LUKS header. (since 9.0) # # Since: 2.6 ## @@ -204,13 +237,14 @@ '*ivgen-alg': 'QCryptoIVGenAlgorithm', '*ivgen-hash-alg': 'QCryptoHashAlgorithm', '*hash-alg': 'QCryptoHashAlgorithm', - '*iter-time': 'int'}} + '*iter-time': 'int', + '*detached-header': 'bool'}} ## # @QCryptoBlockOpenOptions: # -# The options that are available for all encryption formats -# when opening an existing volume +# The options that are available for all encryption formats when +# opening an existing volume # # Since: 2.6 ## @@ -223,8 +257,8 @@ ## # @QCryptoBlockCreateOptions: # -# The options that are available for all encryption formats -# when initializing a new volume +# The options that are available for all encryption formats when +# initializing a new volume # # Since: 2.6 ## @@ -237,8 +271,8 @@ ## # @QCryptoBlockInfoBase: # -# The common information that applies to all full disk -# encryption formats +# The common information that applies to all full disk encryption +# formats # # @format: the encryption format # @@ -250,12 +284,14 @@ ## # @QCryptoBlockInfoLUKSSlot: # -# Information about the LUKS block encryption key -# slot options +# Information about the LUKS block encryption key slot options # # @active: whether the key slot is currently in use +# # @key-offset: offset to the key material in bytes +# # @iters: number of PBKDF2 iterations for key material +# # @stripes: number of stripes for splitting key material # # Since: 2.7 @@ -272,13 +308,23 @@ # Information about the LUKS block encryption options # # @cipher-alg: the cipher algorithm for data encryption +# # @cipher-mode: the cipher mode for data encryption +# # @ivgen-alg: the initialization vector generator +# # @ivgen-hash-alg: the initialization vector generator hash +# # @hash-alg: the master key hash algorithm +# +# @detached-header: whether the LUKS header is detached (Since 9.0) +# # @payload-offset: offset to the payload data in bytes +# # @master-key-iters: number of PBKDF2 iterations for key material +# # @uuid: unique identifier for the volume +# # @slots: information about each key slot # # Since: 2.7 @@ -289,6 +335,7 @@ 'ivgen-alg': 'QCryptoIVGenAlgorithm', '*ivgen-hash-alg': 'QCryptoHashAlgorithm', 'hash-alg': 'QCryptoHashAlgorithm', + 'detached-header': 'bool', 'payload-offset': 'int', 'master-key-iters': 'int', 'uuid': 'str', @@ -312,7 +359,9 @@ # Defines state of keyslots that are affected by the update # # @active: The slots contain the given password and marked as active -# @inactive: The slots are erased (contain garbage) and marked as inactive +# +# @inactive: The slots are erased (contain garbage) and marked as +# inactive # # Since: 5.1 ## @@ -322,35 +371,34 @@ ## # @QCryptoBlockAmendOptionsLUKS: # -# This struct defines the update parameters that activate/de-activate set -# of keyslots +# This struct defines the update parameters that activate/de-activate +# set of keyslots # # @state: the desired state of the keyslots # -# @new-secret: The ID of a QCryptoSecret object providing the password to be -# written into added active keyslots +# @new-secret: The ID of a QCryptoSecret object providing the password +# to be written into added active keyslots # -# @old-secret: Optional (for deactivation only) -# If given will deactivate all keyslots that -# match password located in QCryptoSecret with this ID +# @old-secret: Optional (for deactivation only) If given will +# deactivate all keyslots that match password located in +# QCryptoSecret with this ID # -# @iter-time: Optional (for activation only) -# Number of milliseconds to spend in -# PBKDF passphrase processing for the newly activated keyslot. -# Currently defaults to 2000. +# @iter-time: Optional (for activation only) Number of milliseconds to +# spend in PBKDF passphrase processing for the newly activated +# keyslot. Currently defaults to 2000. # -# @keyslot: Optional. ID of the keyslot to activate/deactivate. -# For keyslot activation, keyslot should not be active already -# (this is unsafe to update an active keyslot), -# but possible if 'force' parameter is given. -# If keyslot is not given, first free keyslot will be written. +# @keyslot: Optional. ID of the keyslot to activate/deactivate. For +# keyslot activation, keyslot should not be active already (this +# is unsafe to update an active keyslot), but possible if 'force' +# parameter is given. If keyslot is not given, first free keyslot +# will be written. # -# For keyslot deactivation, this parameter specifies the exact -# keyslot to deactivate +# For keyslot deactivation, this parameter specifies the exact +# keyslot to deactivate # -# @secret: Optional. The ID of a QCryptoSecret object providing the -# password to use to retrieve current master key. -# Defaults to the same secret that was used to open the image +# @secret: Optional. The ID of a QCryptoSecret object providing the +# password to use to retrieve current master key. Defaults to the +# same secret that was used to open the image # # Since: 5.1 ## @@ -365,8 +413,8 @@ ## # @QCryptoBlockAmendOptions: # -# The options that are available for all encryption formats -# when amending encryption settings +# The options that are available for all encryption formats when +# amending encryption settings # # Since: 5.1 ## @@ -381,22 +429,27 @@ # # Properties for objects of classes derived from secret-common. # -# @loaded: if true, the secret is loaded immediately when applying this option -# and will probably fail when processing the next option. Don't use; -# only provided for compatibility. (default: false) +# @loaded: if true, the secret is loaded immediately when applying +# this option and will probably fail when processing the next +# option. Don't use; only provided for compatibility. +# (default: false) # -# @format: the data format that the secret is provided in (default: raw) +# @format: the data format that the secret is provided in +# (default: raw) # -# @keyid: the name of another secret that should be used to decrypt the -# provided data. If not present, the data is assumed to be unencrypted. +# @keyid: the name of another secret that should be used to decrypt +# the provided data. If not present, the data is assumed to be +# unencrypted. # -# @iv: the random initialization vector used for encryption of this particular -# secret. Should be a base64 encrypted string of the 16-byte IV. Mandatory -# if @keyid is given. Ignored if @keyid is absent. +# @iv: the random initialization vector used for encryption of this +# particular secret. Should be a base64 encrypted string of the +# 16-byte IV. Mandatory if @keyid is given. Ignored if @keyid is +# absent. # # Features: -# @deprecated: Member @loaded is deprecated. Setting true doesn't make sense, -# and false is already the default. +# +# @deprecated: Member @loaded is deprecated. Setting true doesn't +# make sense, and false is already the default. # # Since: 2.6 ## @@ -443,16 +496,17 @@ # Properties for objects of classes derived from tls-creds. # # @verify-peer: if true the peer credentials will be verified once the -# handshake is completed. This is a no-op for anonymous -# credentials. (default: true) +# handshake is completed. This is a no-op for anonymous +# credentials. (default: true) # # @dir: the path of the directory that contains the credential files # -# @endpoint: whether the QEMU network backend that uses the credentials will be -# acting as a client or as a server (default: client) +# @endpoint: whether the QEMU network backend that uses the +# credentials will be acting as a client or as a server +# (default: client) # # @priority: a gnutls priority string as described at -# https://gnutls.org/manual/html_node/Priority-Strings.html +# https://gnutls.org/manual/html_node/Priority-Strings.html # # Since: 2.5 ## @@ -467,13 +521,15 @@ # # Properties for tls-creds-anon objects. # -# @loaded: if true, the credentials are loaded immediately when applying this -# option and will ignore options that are processed later. Don't use; -# only provided for compatibility. (default: false) +# @loaded: if true, the credentials are loaded immediately when +# applying this option and will ignore options that are processed +# later. Don't use; only provided for compatibility. +# (default: false) # # Features: -# @deprecated: Member @loaded is deprecated. Setting true doesn't make sense, -# and false is already the default. +# +# @deprecated: Member @loaded is deprecated. Setting true doesn't +# make sense, and false is already the default. # # Since: 2.5 ## @@ -486,17 +542,19 @@ # # Properties for tls-creds-psk objects. # -# @loaded: if true, the credentials are loaded immediately when applying this -# option and will ignore options that are processed later. Don't use; -# only provided for compatibility. (default: false) +# @loaded: if true, the credentials are loaded immediately when +# applying this option and will ignore options that are processed +# later. Don't use; only provided for compatibility. +# (default: false) # -# @username: the username which will be sent to the server. For clients only. -# If absent, "qemu" is sent and the property will read back as an -# empty string. +# @username: the username which will be sent to the server. For +# clients only. If absent, "qemu" is sent and the property will +# read back as an empty string. # # Features: -# @deprecated: Member @loaded is deprecated. Setting true doesn't make sense, -# and false is already the default. +# +# @deprecated: Member @loaded is deprecated. Setting true doesn't +# make sense, and false is already the default. # # Since: 3.0 ## @@ -510,22 +568,24 @@ # # Properties for tls-creds-x509 objects. # -# @loaded: if true, the credentials are loaded immediately when applying this -# option and will ignore options that are processed later. Don't use; -# only provided for compatibility. (default: false) +# @loaded: if true, the credentials are loaded immediately when +# applying this option and will ignore options that are processed +# later. Don't use; only provided for compatibility. +# (default: false) # # @sanity-check: if true, perform some sanity checks before using the -# credentials (default: true) +# credentials (default: true) # -# @passwordid: 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. +# @passwordid: 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. # # Features: -# @deprecated: Member @loaded is deprecated. Setting true doesn't make sense, -# and false is already the default. +# +# @deprecated: Member @loaded is deprecated. Setting true doesn't +# make sense, and false is already the default. # # Since: 2.5 ## @@ -564,6 +624,7 @@ # The padding algorithm for RSA. # # @raw: no padding used +# # @pkcs1: pkcs1#v1.5 # # Since: 7.1 @@ -578,6 +639,7 @@ # Specific parameters for RSA algorithm. # # @hash-alg: QCryptoHashAlgorithm +# # @padding-alg: QCryptoRSAPaddingAlgorithm # # Since: 7.1 @@ -592,6 +654,8 @@ # The options that are available for all asymmetric key algorithms # when creating a new QCryptoAkCipher. # +# @alg: encryption cipher algorithm +# # Since: 7.1 ## { 'union': 'QCryptoAkCipherOptions', diff --git a/qapi/cryptodev.json b/qapi/cryptodev.json new file mode 100644 index 0000000000..68289f4984 --- /dev/null +++ b/qapi/cryptodev.json @@ -0,0 +1,96 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. + +## +# = Cryptography devices +## + +## +# @QCryptodevBackendAlgType: +# +# The supported algorithm types of a crypto device. +# +# @sym: symmetric encryption +# +# @asym: asymmetric Encryption +# +# Since: 8.0 +## +{ 'enum': 'QCryptodevBackendAlgType', + 'prefix': 'QCRYPTODEV_BACKEND_ALG', + 'data': ['sym', 'asym']} + +## +# @QCryptodevBackendServiceType: +# +# The supported service types of a crypto device. +# +# Since: 8.0 +## +{ 'enum': 'QCryptodevBackendServiceType', + 'prefix': 'QCRYPTODEV_BACKEND_SERVICE', + 'data': ['cipher', 'hash', 'mac', 'aead', 'akcipher']} + +## +# @QCryptodevBackendType: +# +# The crypto device backend type +# +# @builtin: the QEMU builtin support +# +# @vhost-user: vhost-user +# +# @lkcf: Linux kernel cryptographic framework +# +# Since: 8.0 +## +{ 'enum': 'QCryptodevBackendType', + 'prefix': 'QCRYPTODEV_BACKEND_TYPE', + 'data': ['builtin', 'vhost-user', 'lkcf']} + +## +# @QCryptodevBackendClient: +# +# Information about a queue of crypto device. +# +# @queue: the queue index of the crypto device +# +# @type: the type of the crypto device +# +# Since: 8.0 +## +{ 'struct': 'QCryptodevBackendClient', + 'data': { 'queue': 'uint32', + 'type': 'QCryptodevBackendType' } } + +## +# @QCryptodevInfo: +# +# Information about a crypto device. +# +# @id: the id of the crypto device +# +# @service: supported service types of a crypto device +# +# @client: the additional information of the crypto device +# +# Since: 8.0 +## +{ 'struct': 'QCryptodevInfo', + 'data': { 'id': 'str', + 'service': ['QCryptodevBackendServiceType'], + 'client': ['QCryptodevBackendClient'] } } + +## +# @query-cryptodev: +# +# Returns information about current crypto devices. +# +# Returns: a list of @QCryptodevInfo +# +# Since: 8.0 +## +{ 'command': 'query-cryptodev', 'returns': ['QCryptodevInfo']} diff --git a/qapi/cxl.json b/qapi/cxl.json new file mode 100644 index 0000000000..4281726dec --- /dev/null +++ b/qapi/cxl.json @@ -0,0 +1,363 @@ +# -*- Mode: Python -*- +# vim: filetype=python + +## +# = CXL devices +## + +## +# @CxlEventLog: +# +# CXL has a number of separate event logs for different types of +# events. Each such event log is handled and signaled independently. +# +# @informational: Information Event Log +# +# @warning: Warning Event Log +# +# @failure: Failure Event Log +# +# @fatal: Fatal Event Log +# +# Since: 8.1 +## +{ 'enum': 'CxlEventLog', + 'data': ['informational', + 'warning', + 'failure', + 'fatal'] + } + +## +# @cxl-inject-general-media-event: +# +# Inject an event record for a General Media Event (CXL r3.0 +# 8.2.9.2.1.1). This event type is reported via one of the event logs +# specified via the log parameter. +# +# @path: CXL type 3 device canonical QOM path +# +# @log: event log to add the event to +# +# @flags: Event Record Flags. See CXL r3.0 Table 8-42 Common Event +# Record Format, Event Record Flags for subfield definitions. +# +# @dpa: Device Physical Address (relative to @path device). Note +# lower bits include some flags. See CXL r3.0 Table 8-43 General +# Media Event Record, Physical Address. +# +# @descriptor: Memory Event Descriptor with additional memory event +# information. See CXL r3.0 Table 8-43 General Media Event +# Record, Memory Event Descriptor for bit definitions. +# +# @type: Type of memory event that occurred. See CXL r3.0 Table 8-43 +# General Media Event Record, Memory Event Type for possible +# values. +# +# @transaction-type: Type of first transaction that caused the event +# to occur. See CXL r3.0 Table 8-43 General Media Event Record, +# Transaction Type for possible values. +# +# @channel: The channel of the memory event location. A channel is an +# interface that can be independently accessed for a transaction. +# +# @rank: The rank of the memory event location. A rank is a set of +# memory devices on a channel that together execute a transaction. +# +# @device: Bitmask that represents all devices in the rank associated +# with the memory event location. +# +# @component-id: Device specific component identifier for the event. +# May describe a field replaceable sub-component of the device. +# +# Since: 8.1 +## +{ 'command': 'cxl-inject-general-media-event', + 'data': { 'path': 'str', 'log': 'CxlEventLog', 'flags': 'uint8', + 'dpa': 'uint64', 'descriptor': 'uint8', + 'type': 'uint8', 'transaction-type': 'uint8', + '*channel': 'uint8', '*rank': 'uint8', + '*device': 'uint32', '*component-id': 'str' } } + +## +# @cxl-inject-dram-event: +# +# Inject an event record for a DRAM Event (CXL r3.0 8.2.9.2.1.2). +# This event type is reported via one of the event logs specified via +# the log parameter. +# +# @path: CXL type 3 device canonical QOM path +# +# @log: Event log to add the event to +# +# @flags: Event Record Flags. See CXL r3.0 Table 8-42 Common Event +# Record Format, Event Record Flags for subfield definitions. +# +# @dpa: Device Physical Address (relative to @path device). Note +# lower bits include some flags. See CXL r3.0 Table 8-44 DRAM +# Event Record, Physical Address. +# +# @descriptor: Memory Event Descriptor with additional memory event +# information. See CXL r3.0 Table 8-44 DRAM Event Record, Memory +# Event Descriptor for bit definitions. +# +# @type: Type of memory event that occurred. See CXL r3.0 Table 8-44 +# DRAM Event Record, Memory Event Type for possible values. +# +# @transaction-type: Type of first transaction that caused the event +# to occur. See CXL r3.0 Table 8-44 DRAM Event Record, +# Transaction Type for possible values. +# +# @channel: The channel of the memory event location. A channel is an +# interface that can be independently accessed for a transaction. +# +# @rank: The rank of the memory event location. A rank is a set of +# memory devices on a channel that together execute a transaction. +# +# @nibble-mask: Identifies one or more nibbles that the error affects +# +# @bank-group: Bank group of the memory event location, incorporating +# a number of Banks. +# +# @bank: Bank of the memory event location. A single bank is accessed +# per read or write of the memory. +# +# @row: Row address within the DRAM. +# +# @column: Column address within the DRAM. +# +# @correction-mask: Bits within each nibble. Used in order of bits +# set in the nibble-mask. Up to 4 nibbles may be covered. +# +# Since: 8.1 +## +{ 'command': 'cxl-inject-dram-event', + 'data': { 'path': 'str', 'log': 'CxlEventLog', 'flags': 'uint8', + 'dpa': 'uint64', 'descriptor': 'uint8', + 'type': 'uint8', 'transaction-type': 'uint8', + '*channel': 'uint8', '*rank': 'uint8', '*nibble-mask': 'uint32', + '*bank-group': 'uint8', '*bank': 'uint8', '*row': 'uint32', + '*column': 'uint16', '*correction-mask': [ 'uint64' ] + }} + +## +# @cxl-inject-memory-module-event: +# +# Inject an event record for a Memory Module Event (CXL r3.0 +# 8.2.9.2.1.3). This event includes a copy of the Device Health info +# at the time of the event. +# +# @path: CXL type 3 device canonical QOM path +# +# @log: Event Log to add the event to +# +# @flags: Event Record Flags. See CXL r3.0 Table 8-42 Common Event +# Record Format, Event Record Flags for subfield definitions. +# +# @type: Device Event Type. See CXL r3.0 Table 8-45 Memory Module +# Event Record for bit definitions for bit definiions. +# +# @health-status: Overall health summary bitmap. See CXL r3.0 Table +# 8-100 Get Health Info Output Payload, Health Status for bit +# definitions. +# +# @media-status: Overall media health summary. See CXL r3.0 Table +# 8-100 Get Health Info Output Payload, Media Status for bit +# definitions. +# +# @additional-status: See CXL r3.0 Table 8-100 Get Health Info Output +# Payload, Additional Status for subfield definitions. +# +# @life-used: Percentage (0-100) of factory expected life span. +# +# @temperature: Device temperature in degrees Celsius. +# +# @dirty-shutdown-count: Number of times the device has been unable to +# determine whether data loss may have occurred. +# +# @corrected-volatile-error-count: Total number of correctable errors +# in volatile memory. +# +# @corrected-persistent-error-count: Total number of correctable +# errors in persistent memory +# +# Since: 8.1 +## +{ 'command': 'cxl-inject-memory-module-event', + 'data': { 'path': 'str', 'log': 'CxlEventLog', 'flags' : 'uint8', + 'type': 'uint8', 'health-status': 'uint8', + 'media-status': 'uint8', 'additional-status': 'uint8', + 'life-used': 'uint8', 'temperature' : 'int16', + 'dirty-shutdown-count': 'uint32', + 'corrected-volatile-error-count': 'uint32', + 'corrected-persistent-error-count': 'uint32' + }} + +## +# @cxl-inject-poison: +# +# Poison records indicate that a CXL memory device knows that a +# particular memory region may be corrupted. This may be because of +# locally detected errors (e.g. ECC failure) or poisoned writes +# received from other components in the system. This injection +# mechanism enables testing of the OS handling of poison records which +# may be queried via the CXL mailbox. +# +# @path: CXL type 3 device canonical QOM path +# +# @start: Start address; must be 64 byte aligned. +# +# @length: Length of poison to inject; must be a multiple of 64 bytes. +# +# Since: 8.1 +## +{ 'command': 'cxl-inject-poison', + 'data': { 'path': 'str', 'start': 'uint64', 'length': 'size' }} + +## +# @CxlUncorErrorType: +# +# Type of uncorrectable CXL error to inject. These errors are +# reported via an AER uncorrectable internal error with additional +# information logged at the CXL device. +# +# @cache-data-parity: Data error such as data parity or data ECC error +# CXL.cache +# +# @cache-address-parity: Address parity or other errors associated +# with the address field on CXL.cache +# +# @cache-be-parity: Byte enable parity or other byte enable errors on +# CXL.cache +# +# @cache-data-ecc: ECC error on CXL.cache +# +# @mem-data-parity: Data error such as data parity or data ECC error +# on CXL.mem +# +# @mem-address-parity: Address parity or other errors associated with +# the address field on CXL.mem +# +# @mem-be-parity: Byte enable parity or other byte enable errors on +# CXL.mem. +# +# @mem-data-ecc: Data ECC error on CXL.mem. +# +# @reinit-threshold: REINIT threshold hit. +# +# @rsvd-encoding: Received unrecognized encoding. +# +# @poison-received: Received poison from the peer. +# +# @receiver-overflow: Buffer overflows (first 3 bits of header log +# indicate which) +# +# @internal: Component specific error +# +# @cxl-ide-tx: Integrity and data encryption tx error. +# +# @cxl-ide-rx: Integrity and data encryption rx error. +# +# Since: 8.0 +## + +{ 'enum': 'CxlUncorErrorType', + 'data': ['cache-data-parity', + 'cache-address-parity', + 'cache-be-parity', + 'cache-data-ecc', + 'mem-data-parity', + 'mem-address-parity', + 'mem-be-parity', + 'mem-data-ecc', + 'reinit-threshold', + 'rsvd-encoding', + 'poison-received', + 'receiver-overflow', + 'internal', + 'cxl-ide-tx', + 'cxl-ide-rx' + ] + } + +## +# @CXLUncorErrorRecord: +# +# Record of a single error including header log. +# +# @type: Type of error +# +# @header: 16 DWORD of header. +# +# Since: 8.0 +## +{ 'struct': 'CXLUncorErrorRecord', + 'data': { + 'type': 'CxlUncorErrorType', + 'header': [ 'uint32' ] + } +} + +## +# @cxl-inject-uncorrectable-errors: +# +# Command to allow injection of multiple errors in one go. This +# allows testing of multiple header log handling in the OS. +# +# @path: CXL Type 3 device canonical QOM path +# +# @errors: Errors to inject +# +# Since: 8.0 +## +{ 'command': 'cxl-inject-uncorrectable-errors', + 'data': { 'path': 'str', + 'errors': [ 'CXLUncorErrorRecord' ] }} + +## +# @CxlCorErrorType: +# +# Type of CXL correctable error to inject +# +# @cache-data-ecc: Data ECC error on CXL.cache +# +# @mem-data-ecc: Data ECC error on CXL.mem +# +# @crc-threshold: Component specific and applicable to 68 byte Flit +# mode only. +# +# @cache-poison-received: Received poison from a peer on CXL.cache. +# +# @mem-poison-received: Received poison from a peer on CXL.mem +# +# @physical: Received error indication from the physical layer. +# +# Since: 8.0 +## +{ 'enum': 'CxlCorErrorType', + 'data': ['cache-data-ecc', + 'mem-data-ecc', + 'crc-threshold', + 'retry-threshold', + 'cache-poison-received', + 'mem-poison-received', + 'physical'] +} + +## +# @cxl-inject-correctable-error: +# +# Command to inject a single correctable error. Multiple error +# injection of this error type is not interesting as there is no +# associated header log. These errors are reported via AER as a +# correctable internal error, with additional detail available from +# the CXL device. +# +# @path: CXL Type 3 device canonical QOM path +# +# @type: Type of error. +# +# Since: 8.0 +## +{'command': 'cxl-inject-correctable-error', + 'data': {'path': 'str', 'type': 'CxlCorErrorType'}} diff --git a/qapi/dump.json b/qapi/dump.json index 90859c5483..2fa9504d86 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -15,77 +15,90 @@ # # @elf: elf format # -# @kdump-zlib: kdump-compressed format with zlib-compressed +# @kdump-zlib: makedumpfile flattened, kdump-compressed format with +# zlib compression # -# @kdump-lzo: kdump-compressed format with lzo-compressed +# @kdump-lzo: makedumpfile flattened, kdump-compressed format with lzo +# compression # -# @kdump-snappy: kdump-compressed format with snappy-compressed +# @kdump-snappy: makedumpfile flattened, kdump-compressed format with +# snappy compression # -# @win-dmp: Windows full crashdump format, -# can be used instead of ELF converting (since 2.13) +# @kdump-raw-zlib: raw assembled kdump-compressed format with zlib +# compression (since 8.2) +# +# @kdump-raw-lzo: raw assembled kdump-compressed format with lzo +# compression (since 8.2) +# +# @kdump-raw-snappy: raw assembled kdump-compressed format with snappy +# compression (since 8.2) +# +# @win-dmp: Windows full crashdump format, can be used instead of ELF +# converting (since 2.13) # # Since: 2.0 ## { 'enum': 'DumpGuestMemoryFormat', - 'data': [ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy', 'win-dmp' ] } + 'data': [ + 'elf', + 'kdump-zlib', 'kdump-lzo', 'kdump-snappy', + 'kdump-raw-zlib', 'kdump-raw-lzo', 'kdump-raw-snappy', + 'win-dmp' ] } ## # @dump-guest-memory: # -# Dump guest's memory to vmcore. It is a synchronous operation that can take -# very long depending on the amount of guest memory. +# Dump guest's memory to vmcore. It is a synchronous operation that +# can take very long depending on the amount of guest memory. # -# @paging: if true, do paging to get guest's memory mapping. This allows -# using gdb to process the core file. +# @paging: if true, do paging to get guest's memory mapping. This +# allows 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. +# 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. # -# Also, paging=true has the following limitations: +# Also, paging=true has the following limitations: # -# 1. The guest may be in a catastrophic state or can have corrupted -# memory, which cannot be trusted -# 2. The guest can be in real-mode even if paging is enabled. For -# example, the guest uses ACPI to sleep, and ACPI sleep state -# goes in real-mode -# 3. Currently only supported on i386 and x86_64. +# 1. The guest may be in a catastrophic state or can have +# corrupted memory, which cannot be trusted +# 2. The guest can be in real-mode even if paging is enabled. For +# example, the guest uses ACPI to sleep, and ACPI sleep state +# goes in real-mode +# 3. Currently only supported on i386 and x86_64. # -# @protocol: the filename or file descriptor of the vmcore. The supported -# protocols are: +# @protocol: the filename or file descriptor of the vmcore. The +# supported protocols are: # -# 1. file: the protocol starts with "file:", and the following -# string is the file's path. -# 2. fd: the protocol starts with "fd:", and the following string -# is the fd's name. +# 1. file: the protocol starts with "file:", and the following +# string is the file's path. +# 2. fd: the protocol starts with "fd:", and the following string +# is the fd's name. # -# @detach: if true, QMP will return immediately rather than -# waiting for the dump to finish. The user can track progress -# using "query-dump". (since 2.6). +# @detach: if true, QMP will return immediately rather than waiting +# for the dump to finish. The user can track progress using +# "query-dump". (since 2.6). # # @begin: if specified, the starting physical address. # -# @length: if specified, the memory size, in bytes. If you don't -# want to dump all guest's memory, please specify the start @begin -# and @length +# @length: if specified, the memory size, in bytes. If you don't want +# to dump all guest's memory, please specify the start @begin and +# @length # -# @format: if specified, the format of guest memory dump. But non-elf -# format is conflict with paging and filter, ie. @paging, @begin and -# @length is not allowed to be specified with non-elf @format at the -# same time (since 2.0) +# @format: if specified, the format of guest memory dump. But non-elf +# format is conflict with paging and filter, ie. @paging, @begin +# and @length is not allowed to be specified with non-elf @format +# at the same time (since 2.0) # # Note: All boolean arguments default to false # -# Returns: nothing on success -# # Since: 1.2 # # Example: # -# -> { "execute": "dump-guest-memory", -# "arguments": { "paging": false, "protocol": "fd:dump" } } -# <- { "return": {} } -# +# -> { "execute": "dump-guest-memory", +# "arguments": { "paging": false, "protocol": "fd:dump" } } +# <- { "return": {} } ## { 'command': 'dump-guest-memory', 'data': { 'paging': 'bool', 'protocol': 'str', '*detach': 'bool', @@ -139,10 +152,9 @@ # # Example: # -# -> { "execute": "query-dump" } -# <- { "return": { "status": "active", "completed": 1024000, -# "total": 2048000 } } -# +# -> { "execute": "query-dump" } +# <- { "return": { "status": "active", "completed": 1024000, +# "total": 2048000 } } ## { 'command': 'query-dump', 'returns': 'DumpQueryResult' } @@ -153,19 +165,18 @@ # # @result: final dump status # -# @error: human-readable error string that provides -# hint on why dump failed. Only presents on failure. The -# user should not try to interpret the error string. +# @error: human-readable error string that provides hint on why dump +# failed. Only presents on failure. The user should not try to +# interpret the error string. # # Since: 2.6 # # Example: # -# <- { "event": "DUMP_COMPLETED", -# "data": { "result": { "total": 1090650112, "status": "completed", -# "completed": 1090650112 } }, -# "timestamp": { "seconds": 1648244171, "microseconds": 950316 } } -# +# <- { "event": "DUMP_COMPLETED", +# "data": { "result": { "total": 1090650112, "status": "completed", +# "completed": 1090650112 } }, +# "timestamp": { "seconds": 1648244171, "microseconds": 950316 } } ## { 'event': 'DUMP_COMPLETED' , 'data': { 'result': 'DumpQueryResult', '*error': 'str' } } @@ -173,7 +184,7 @@ ## # @DumpGuestMemoryCapability: # -# A list of the available formats for dump-guest-memory +# @formats: the available formats for dump-guest-memory # # Since: 2.0 ## @@ -186,17 +197,16 @@ # # Returns the available formats for dump-guest-memory # -# Returns: A @DumpGuestMemoryCapability object listing available formats for -# dump-guest-memory +# Returns: A @DumpGuestMemoryCapability object listing available +# formats for dump-guest-memory # # Since: 2.0 # # Example: # -# -> { "execute": "query-dump-guest-memory-capability" } -# <- { "return": { "formats": -# ["elf", "kdump-zlib", "kdump-lzo", "kdump-snappy"] } -# +# -> { "execute": "query-dump-guest-memory-capability" } +# <- { "return": { "formats": +# ["elf", "kdump-zlib", "kdump-lzo", "kdump-snappy"] } } ## { 'command': 'query-dump-guest-memory-capability', 'returns': 'DumpGuestMemoryCapability' } diff --git a/qapi/ebpf.json b/qapi/ebpf.json new file mode 100644 index 0000000000..e500b5a744 --- /dev/null +++ b/qapi/ebpf.json @@ -0,0 +1,64 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. + +## +# = eBPF Objects +# +# eBPF object is an ELF binary that contains the eBPF program and eBPF +# map description(BTF). Overall, eBPF object should contain the +# program and enough metadata to create/load eBPF with libbpf. As the +# eBPF maps/program should correspond to QEMU, the eBPF can't be used +# from different QEMU build. +# +# Currently, there is a possible eBPF for receive-side scaling (RSS). +## + +## +# @EbpfObject: +# +# An eBPF ELF object. +# +# @object: the eBPF object encoded in base64 +# +# Since: 9.0 +## +{ 'struct': 'EbpfObject', + 'data': {'object': 'str'}, + 'if': 'CONFIG_EBPF' } + +## +# @EbpfProgramID: +# +# The eBPF programs that can be gotten with request-ebpf. +# +# @rss: Receive side scaling, technology that allows steering traffic +# between queues by calculation hash. Users may set up +# indirection table and hash/packet types configurations. Used +# with virtio-net. +# +# Since: 9.0 +## +{ 'enum': 'EbpfProgramID', + 'if': 'CONFIG_EBPF', + 'data': [ { 'name': 'rss' } ] } + +## +# @request-ebpf: +# +# Retrieve an eBPF object that can be loaded with libbpf. Management +# applications (e.g. libvirt) may load it and pass file descriptors to +# QEMU, so they can run running QEMU without BPF capabilities. +# +# @id: The ID of the program to return. +# +# Returns: eBPF object encoded in base64. +# +# Since: 9.0 +## +{ 'command': 'request-ebpf', + 'data': { 'id': 'EbpfProgramID' }, + 'returns': 'EbpfObject', + 'if': 'CONFIG_EBPF' } diff --git a/qapi/error.json b/qapi/error.json index 94a6502de9..135c1e8231 100644 --- a/qapi/error.json +++ b/qapi/error.json @@ -10,8 +10,8 @@ # # QEMU error classes # -# @GenericError: this is used for errors that don't require a specific error -# class. This should be the default case for most errors +# @GenericError: this is used for errors that don't require a specific +# error class. This should be the default case for most errors # # @CommandNotFound: the requested command has not been found # @@ -20,7 +20,7 @@ # @DeviceNotFound: the requested device has not been found # # @KVMMissingCap: the requested operation can't be fulfilled because a -# required KVM capability is missing +# required KVM capability is missing # # Since: 1.2 ## diff --git a/qapi/introspect.json b/qapi/introspect.json index 183148b2e9..b041b02ba8 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -35,15 +35,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 ## @@ -80,22 +80,18 @@ ## # @SchemaInfo: # -# @name: the entity's name, inherited from @base. -# The SchemaInfo is always referenced by this name. -# Commands and events have the name defined in the QAPI schema. -# Unlike command and event names, type names are not part of -# the wire ABI. Consequently, type names are meaningless -# strings here, although they are still guaranteed unique -# regardless of @meta-type. +# @name: the entity's name, inherited from @base. The SchemaInfo is +# always referenced by this name. Commands and events have the +# name defined in the QAPI schema. Unlike command and event +# names, type names are not part of the wire ABI. Consequently, +# type names are meaningless strings here, although they are still +# guaranteed unique regardless of @meta-type. # # @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. +# particular order. (since 4.1 for object types, 4.2 for +# commands, 5.0 for the rest) # # Since: 2.5 ## @@ -142,13 +138,15 @@ # # Additional SchemaInfo members for meta-type 'enum'. # -# @members: the enum type's members, in no particular order -# (since 6.2). +# @members: the enum type's members, in no particular order (since +# 6.2). # -# @values: the enumeration type's member names, in no particular order. -# Redundant with @members. Just for backward compatibility. +# @values: the enumeration type's member names, in no particular +# order. Redundant with @members. Just for backward +# compatibility. # # Features: +# # @deprecated: Member @values is deprecated. Use @members instead. # # Values of this type are JSON string on the wire. @@ -168,7 +166,7 @@ # @name: the member's name, as defined in the QAPI schema. # # @features: names of features associated with the member, in no -# particular order. +# particular order. # # Since: 6.2 ## @@ -194,16 +192,16 @@ # # Additional SchemaInfo members for meta-type 'object'. # -# @members: the object type's (non-variant) members, in no particular order. +# @members: the object type's (non-variant) members, in no particular +# order. # -# @tag: the name of the member serving as type tag. -# An element of @members with this name must exist. +# @tag: the name of the member serving as type tag. An element of +# @members with this name must exist. # -# @variants: variant members, i.e. additional members that -# depend on the type tag's value. Present exactly when -# @tag is present. The variants are in no particular order, -# and may even differ from the order of the values of the -# enum type of the @tag. +# @variants: variant members, i.e. additional members that depend on +# the type tag's value. Present exactly when @tag is present. +# The variants are in no particular order, and may even differ +# from the order of the values of the enum type of the @tag. # # Values of this type are JSON object on the wire. # @@ -223,16 +221,14 @@ # # @type: the name of the member's type. # -# @default: default when used as command parameter. -# If absent, the parameter is mandatory. -# If present, the value must be null. The parameter is -# optional, and behavior when it's missing is not specified -# here. -# Future extension: if present and non-null, the parameter -# is optional, and defaults to this value. +# @default: default when used as command parameter. If absent, the +# parameter is mandatory. If present, the value must be null. +# The parameter is optional, and behavior when it's missing is not +# specified here. 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) +# particular order. (since 5.0) # # Since: 2.5 ## @@ -249,7 +245,7 @@ # @case: a value of the type tag. # # @type: the name of the object type that provides the variant members -# when the type tag has value @case. +# when the type tag has value @case. # # Since: 2.5 ## @@ -261,9 +257,9 @@ # # Additional SchemaInfo members for meta-type 'alternate'. # -# @members: the alternate type's members, in no particular order. -# The members' wire encoding is distinct, see -# docs/devel/qapi-code-gen.txt section Alternate types. +# @members: the alternate type's members, in no particular order. The +# members' wire encoding is distinct, see +# :doc:`/devel/qapi-code-gen` section Alternate types. # # On the wire, this can be any of the members. # @@ -290,14 +286,15 @@ # Additional SchemaInfo members for meta-type 'command'. # # @arg-type: the name of the object type that provides the command's -# parameters. +# parameters. # # @ret-type: the name of the command's result type. # # @allow-oob: whether the command allows out-of-band execution, -# defaults to false (Since: 2.12) +# defaults to false (Since: 2.12) # -# TODO: @success-response (currently irrelevant, because it's QGA, not QMP) +# TODO: @success-response (currently irrelevant, because it's QGA, not +# QMP) # # Since: 2.5 ## @@ -311,7 +308,7 @@ # Additional SchemaInfo members for meta-type 'event'. # # @arg-type: the name of the object type that provides the event's -# parameters. +# parameters. # # Since: 2.5 ## diff --git a/qapi/job.json b/qapi/job.json index d5f84e9615..b3957207a4 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -2,7 +2,7 @@ # vim: filetype=python ## -# == Background jobs +# = Background jobs ## ## @@ -20,13 +20,17 @@ # # @create: image creation job type, see "blockdev-create" (since 3.0) # -# @amend: image options amend job type, see "x-blockdev-amend" (since 5.1) +# @amend: image options amend job type, see "x-blockdev-amend" (since +# 5.1) # -# @snapshot-load: snapshot load job type, see "snapshot-load" (since 6.0) +# @snapshot-load: snapshot load job type, see "snapshot-load" (since +# 6.0) # -# @snapshot-save: snapshot save job type, see "snapshot-save" (since 6.0) +# @snapshot-save: snapshot save job type, see "snapshot-save" (since +# 6.0) # -# @snapshot-delete: snapshot delete job type, see "snapshot-delete" (since 6.0) +# @snapshot-delete: snapshot delete job type, see "snapshot-delete" +# (since 6.0) # # Since: 1.7 ## @@ -39,41 +43,42 @@ # # Indicates the present state of a given job in its lifetime. # -# @undefined: Erroneous, default state. Should not ever be visible. +# @undefined: Erroneous, default state. Should not ever be visible. # # @created: The job has been created, but not yet started. # # @running: The job is currently running. # -# @paused: The job is running, but paused. The pause may be requested by -# either the QMP user or by internal processes. +# @paused: The job is running, but paused. The pause may be requested +# by either the QMP user or by internal processes. # -# @ready: The job is running, but is ready for the user to signal completion. -# This is used for long-running jobs like mirror that are designed to -# run indefinitely. +# @ready: The job is running, but is ready for the user to signal +# completion. This is used for long-running jobs like mirror that +# are designed to run indefinitely. # -# @standby: The job is ready, but paused. This is nearly identical to @paused. -# The job may return to @ready or otherwise be canceled. +# @standby: The job is ready, but paused. This is nearly identical to +# @paused. The job may return to @ready or otherwise be canceled. # -# @waiting: The job is waiting for other jobs in the transaction to converge -# to the waiting state. This status will likely not be visible for -# the last job in a transaction. +# @waiting: The job is waiting for other jobs in the transaction to +# converge to the waiting state. This status will likely not be +# visible for the last job in a transaction. # -# @pending: The job has finished its work, but has finalization steps that it -# needs to make prior to completing. These changes will require -# manual intervention via @job-finalize if auto-finalize was set to -# false. These pending changes may still fail. +# @pending: The job has finished its work, but has finalization steps +# that it needs to make prior to completing. These changes will +# require manual intervention via @job-finalize if auto-finalize +# was set to false. These pending changes may still fail. # -# @aborting: The job is in the process of being aborted, and will finish with -# an error. The job will afterwards report that it is @concluded. -# This status may not be visible to the management process. +# @aborting: The job is in the process of being aborted, and will +# finish with an error. The job will afterwards report that it is +# @concluded. This status may not be visible to the management +# process. # -# @concluded: The job has finished all work. If auto-dismiss was set to false, -# the job will remain in the query list until it is dismissed via -# @job-dismiss. +# @concluded: The job has finished all work. If auto-dismiss was set +# to false, the job will remain in the query list until it is +# dismissed via @job-dismiss. # -# @null: The job is in the process of being dismantled. This state should not -# ever be visible externally. +# @null: The job is in the process of being dismantled. This state +# should not ever be visible externally. # # Since: 2.12 ## @@ -100,11 +105,13 @@ # # @finalize: see @job-finalize # +# @change: see @block-job-change (since 8.2) +# # Since: 2.12 ## { 'enum': 'JobVerb', 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', - 'finalize' ] } + 'finalize', 'change' ] } ## # @JOB_STATUS_CHANGE: @@ -112,6 +119,7 @@ # Emitted when a job transitions to a different status. # # @id: The job identifier +# # @status: The new job status # # Since: 3.0 @@ -125,12 +133,12 @@ # # Pause an active job. # -# This command returns immediately after marking the active job for pausing. -# Pausing an already paused job is an error. +# This command returns immediately after marking the active job for +# pausing. Pausing an already paused job is an error. # -# The job will pause as soon as possible, which means transitioning into the -# PAUSED state if it was RUNNING, or into STANDBY if it was READY. The -# corresponding JOB_STATUS_CHANGE event will be emitted. +# The job will pause as soon as possible, which means transitioning +# into the PAUSED state if it was RUNNING, or into STANDBY if it was +# READY. The corresponding JOB_STATUS_CHANGE event will be emitted. # # Cancelling a paused job automatically resumes it. # @@ -145,10 +153,10 @@ # # Resume a paused job. # -# This command returns immediately after resuming a paused job. Resuming an -# already running job is an error. +# This command returns immediately after resuming a paused job. +# Resuming an already running job is an error. # -# @id : The job identifier. +# @id: The job identifier. # # Since: 3.0 ## @@ -161,11 +169,11 @@ # This command returns immediately after marking the active job for # cancellation. # -# The job will cancel as soon as possible and then emit a JOB_STATUS_CHANGE -# event. Usually, the status will change to ABORTING, but it is possible that -# a job successfully completes (e.g. because it was almost done and there was -# no opportunity to cancel earlier than completing the job) and transitions to -# PENDING instead. +# The job will cancel as soon as possible and then emit a +# JOB_STATUS_CHANGE event. Usually, the status will change to +# ABORTING, but it is possible that a job successfully completes (e.g. +# because it was almost done and there was no opportunity to cancel +# earlier than completing the job) and transitions to PENDING instead. # # @id: The job identifier. # @@ -187,12 +195,14 @@ ## # @job-dismiss: # -# Deletes a job that is in the CONCLUDED state. This command only needs to be -# run explicitly for jobs that don't have automatic dismiss enabled. +# Deletes a job that is in the CONCLUDED state. This command only +# needs to be run explicitly for jobs that don't have automatic +# dismiss enabled. # -# This command will refuse to operate on any job that has not yet reached its -# terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of JOB_READY -# event, job-cancel or job-complete will still need to be used as appropriate. +# This command will refuse to operate on any job that has not yet +# reached its terminal state, JOB_STATUS_CONCLUDED. For jobs that make +# use of JOB_READY event, job-cancel or job-complete will still need +# to be used as appropriate. # # @id: The job identifier. # @@ -203,16 +213,17 @@ ## # @job-finalize: # -# Instructs all jobs in a transaction (or a single job if it is not part of any -# transaction) to finalize any graph changes and do any necessary cleanup. This -# command requires that all involved jobs are in the PENDING state. +# Instructs all jobs in a transaction (or a single job if it is not +# part of any transaction) to finalize any graph changes and do any +# necessary cleanup. This command requires that all involved jobs are +# in the PENDING state. # -# For jobs in a transaction, instructing one job to finalize will force -# ALL jobs in the transaction to finalize, so it is only necessary to instruct -# a single member job to finalize. +# For jobs in a transaction, instructing one job to finalize will +# force ALL jobs in the transaction to finalize, so it is only +# necessary to instruct a single member job to finalize. # -# @id: The identifier of any job in the transaction, or of a job that is not -# part of any transaction. +# @id: The identifier of any job in the transaction, or of a job that +# is not part of any transaction. # # Since: 3.0 ## @@ -229,22 +240,22 @@ # # @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-common.json b/qapi/machine-common.json new file mode 100644 index 0000000000..fa6bd71d12 --- /dev/null +++ b/qapi/machine-common.json @@ -0,0 +1,21 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. + +## +# = Machines S390 data types +## + +## +# @CpuS390Entitlement: +# +# An enumeration of CPU entitlements that can be assumed by a virtual +# S390 CPU +# +# Since: 8.2 +## +{ 'enum': 'CpuS390Entitlement', + 'prefix': 'S390_CPU_ENTITLEMENT', + 'data': [ 'auto', 'low', 'medium', 'high' ] } diff --git a/qapi/machine-target.json b/qapi/machine-target.json index 2e267fa458..29e695aa06 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -4,17 +4,20 @@ # 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': 'machine-common.json' } + ## # @CpuModelInfo: # # Virtual CPU model. # -# A CPU model consists of the name of a CPU definition, to which -# delta changes are applied (e.g. features added/removed). Most magic values +# A CPU model consists of the name of a CPU definition, to which delta +# changes are applied (e.g. features added/removed). Most magic values # that an architecture might require should be hidden behind the name. # However, if required, architectures can expose relevant properties. # # @name: the name of the CPU definition the model is based on +# # @props: a dictionary of QOM properties to be applied # # Since: 2.8 @@ -28,26 +31,28 @@ # # An enumeration of CPU model expansion types. # -# @static: Expand to a static CPU model, a combination of a static base -# model name and property delta changes. As the static base model will -# never change, the expanded CPU model will be the same, independent of -# QEMU version, machine type, machine options, and accelerator options. -# Therefore, the resulting model can be used by tooling without having -# to specify a compatibility machine - e.g. when displaying the "host" -# model. The @static CPU models are migration-safe. - -# @full: Expand all properties. The produced model is not guaranteed to be -# migration-safe, but allows tooling to get an insight and work with -# model details. +# @static: Expand to a static CPU model, a combination of a static +# base model name and property delta changes. As the static base +# model will never change, the expanded CPU model will be the +# same, independent of QEMU version, machine type, machine +# options, and accelerator options. Therefore, the resulting +# model can be used by tooling without having to specify a +# compatibility machine - e.g. when displaying the "host" model. +# The @static CPU models are migration-safe. # -# 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). +# @full: Expand all properties. The produced model is not guaranteed +# to be migration-safe, but allows tooling to get an insight and +# work with 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). # # Since: 2.8 ## @@ -57,20 +62,22 @@ ## # @CpuModelCompareResult: # -# An enumeration of CPU model comparison results. The result is usually -# calculated using e.g. CPU features or CPU generations. +# An enumeration of CPU model comparison results. The result is +# usually calculated using e.g. CPU features or CPU generations. # # @incompatible: If model A is incompatible to model B, model A is not -# guaranteed to run where model B runs and the other way around. +# guaranteed to run where model B runs and the other way around. # -# @identical: If model A is identical to model B, model A is guaranteed to run -# where model B runs and the other way around. +# @identical: If model A is identical to model B, model A is +# guaranteed to run where model B runs and the other way around. # -# @superset: If model A is a superset of model B, model B is guaranteed to run -# where model A runs. There are no guarantees about the other way. +# @superset: If model A is a superset of model B, model B is +# guaranteed to run where model A runs. There are no guarantees +# about the other way. # -# @subset: If model A is a subset of model B, model A is guaranteed to run -# where model B runs. There are no guarantees about the other way. +# @subset: If model A is a subset of model B, model A is guaranteed to +# run where model B runs. There are no guarantees about the other +# way. # # Since: 2.8 ## @@ -96,15 +103,16 @@ # The result of a CPU model comparison. # # @result: The result of the compare operation. -# @responsible-properties: List of properties that led to the comparison result -# not being identical. +# +# @responsible-properties: List of properties that led to the +# comparison result not being identical. # # @responsible-properties is a list of QOM property names that led to -# both CPUs not being detected as identical. For identical models, this -# list is empty. -# If a QOM property is read-only, that means there's no known way to make the -# CPU models identical. If the special property name "type" is included, the -# models are by definition not identical and cannot be made identical. +# both CPUs not being detected as identical. For identical models, +# this list is empty. If a QOM property is read-only, that means +# there's no known way to make the CPU models identical. If the +# special property name "type" is included, the models are by +# definition not identical and cannot be made identical. # # Since: 2.8 ## @@ -116,39 +124,54 @@ ## # @query-cpu-model-comparison: # -# Compares two CPU models, returning how they compare in a specific -# configuration. The results indicates how both models compare regarding -# runnability. This result can be used by tooling to make decisions if a -# certain CPU model will run in a certain configuration or if a compatible -# CPU model has to be created by baselining. +# Compares two CPU models, @modela and @modelb, returning how they +# compare in a specific configuration. The results indicates how +# both models compare regarding runnability. This result can be +# used by tooling to make decisions if a certain CPU model will +# run in a certain configuration or if a compatible CPU model has +# to be created by baselining. # -# Usually, a CPU model is compared against the maximum possible CPU model -# of a certain configuration (e.g. the "host" model for KVM). If that CPU -# model is identical or a subset, it will run in that configuration. +# Usually, a CPU model is compared against the maximum possible CPU +# model of a certain configuration (e.g. the "host" model for KVM). +# If that CPU model is identical or a subset, it will run in that +# configuration. # # The result returned by this command may be affected by: # -# * QEMU version: CPU models may look different depending on the QEMU version. -# (Except for CPU models reported as "static" in query-cpu-definitions.) -# * machine-type: CPU model may look different depending on the machine-type. -# (Except for CPU models reported as "static" in query-cpu-definitions.) -# * machine options (including accelerator): in some architectures, CPU models -# may look different depending on machine and accelerator options. (Except for -# CPU models reported as "static" in query-cpu-definitions.) -# * "-cpu" arguments and global properties: arguments to the -cpu option and -# global properties may affect expansion of CPU models. Using -# query-cpu-model-expansion while using these is not advised. +# * QEMU version: CPU models may look different depending on the QEMU +# version. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * machine-type: CPU model may look different depending on the +# machine-type. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * machine options (including accelerator): in some architectures, +# CPU models may look different depending on machine and accelerator +# options. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * "-cpu" arguments and global properties: arguments to the -cpu +# option and global properties may affect expansion of CPU models. +# Using query-cpu-model-expansion while using these is not advised. # -# Some architectures may not support comparing CPU models. s390x supports -# comparing CPU models. +# Some architectures may not support comparing CPU models. s390x +# supports comparing CPU models. # -# Returns: a CpuModelBaselineInfo. Returns an error if comparing CPU models is -# not supported, if a model cannot be used, if a model contains -# an unknown cpu definition name, unknown properties or properties -# with wrong types. +# @modela: description of the first CPU model to compare, referred to as +# "model A" in CpuModelCompareResult +# +# @modelb: description of the second CPU model to compare, referred to as +# "model B" in CpuModelCompareResult +# +# Returns: a CpuModelCompareInfo describing how both CPU models +# compare +# +# Errors: +# - if comparing CPU models is not supported +# - if a model cannot be used +# - if a model contains an unknown cpu definition name, unknown +# properties or properties 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 ## @@ -160,38 +183,49 @@ ## # @query-cpu-model-baseline: # -# Baseline two CPU models, creating a compatible third model. The created -# model will always be a static, migration-safe CPU model (see "static" -# CPU model expansion for details). +# Baseline two CPU models, @modela and @modelb, creating a compatible +# third model. The created model will always be a static, +# migration-safe CPU model (see "static" CPU model expansion for details). # -# This interface can be used by tooling to create a compatible CPU model out -# two CPU models. The created CPU model will be identical to or a subset of -# both CPU models when comparing them. Therefore, the created CPU model is -# guaranteed to run where the given CPU models run. +# This interface can be used by tooling to create a compatible CPU +# model out two CPU models. The created CPU model will be identical +# to or a subset of both CPU models when comparing them. Therefore, +# the created CPU model is guaranteed to run where the given CPU +# models run. # # The result returned by this command may be affected by: # -# * QEMU version: CPU models may look different depending on the QEMU version. -# (Except for CPU models reported as "static" in query-cpu-definitions.) -# * machine-type: CPU model may look different depending on the machine-type. -# (Except for CPU models reported as "static" in query-cpu-definitions.) -# * machine options (including accelerator): in some architectures, CPU models -# may look different depending on machine and accelerator options. (Except for -# CPU models reported as "static" in query-cpu-definitions.) -# * "-cpu" arguments and global properties: arguments to the -cpu option and -# global properties may affect expansion of CPU models. Using -# query-cpu-model-expansion while using these is not advised. +# * QEMU version: CPU models may look different depending on the QEMU +# version. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * machine-type: CPU model may look different depending on the +# machine-type. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * machine options (including accelerator): in some architectures, +# CPU models may look different depending on machine and accelerator +# options. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * "-cpu" arguments and global properties: arguments to the -cpu +# option and global properties may affect expansion of CPU models. +# Using query-cpu-model-expansion while using these is not advised. # -# Some architectures may not support baselining CPU models. s390x supports -# baselining CPU models. +# Some architectures may not support baselining CPU models. s390x +# supports baselining CPU models. # -# Returns: a CpuModelBaselineInfo. Returns an error if baselining CPU models is -# not supported, if a model cannot be used, if a model contains -# an unknown cpu definition name, unknown properties or properties -# with wrong types. +# @modela: description of the first CPU model to baseline +# +# @modelb: description of the second CPU model to baseline +# +# Returns: a CpuModelBaselineInfo describing the baselined CPU model +# +# Errors: +# - if baselining CPU models is not supported +# - if a model cannot be used +# - if a model contains an unknown cpu definition name, unknown +# properties or properties 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 ## @@ -214,38 +248,51 @@ 'data': { 'model': 'CpuModelInfo' }, 'if': { 'any': [ 'TARGET_S390X', 'TARGET_I386', - 'TARGET_ARM' ] } } + 'TARGET_ARM', + 'TARGET_LOONGARCH64', + 'TARGET_RISCV' ] } } ## # @query-cpu-model-expansion: # -# Expands a given CPU model (or a combination of CPU model + additional options) -# to different granularities, allowing tooling to get an understanding what a -# specific CPU model looks like in QEMU under a certain configuration. +# Expands a given CPU model, @model, (or a combination of CPU model + +# additional options) to different granularities, specified by +# @type, allowing tooling to get an understanding what a specific +# CPU model looks like in QEMU under a certain configuration. # # This interface can be used to query the "host" CPU model. # # The data returned by this command may be affected by: # -# * QEMU version: CPU models may look different depending on the QEMU version. -# (Except for CPU models reported as "static" in query-cpu-definitions.) -# * machine-type: CPU model may look different depending on the machine-type. -# (Except for CPU models reported as "static" in query-cpu-definitions.) -# * machine options (including accelerator): in some architectures, CPU models -# may look different depending on machine and accelerator options. (Except for -# CPU models reported as "static" in query-cpu-definitions.) -# * "-cpu" arguments and global properties: arguments to the -cpu option and -# global properties may affect expansion of CPU models. Using -# query-cpu-model-expansion while using these is not advised. +# * QEMU version: CPU models may look different depending on the QEMU +# version. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * machine-type: CPU model may look different depending on the +# machine-type. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * machine options (including accelerator): in some architectures, +# CPU models may look different depending on machine and accelerator +# options. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * "-cpu" arguments and global properties: arguments to the -cpu +# option and global properties may affect expansion of CPU models. +# Using query-cpu-model-expansion while using these is not advised. # -# Some architectures may not support all expansion types. s390x supports -# "full" and "static". Arm only supports "full". +# Some architectures may not support all expansion types. s390x +# supports "full" and "static". Arm only supports "full". # -# Returns: a CpuModelExpansionInfo. Returns an error if expanding CPU models is -# not supported, if the model cannot be expanded, if the model contains -# an unknown CPU definition name, unknown properties or properties -# with a wrong type. Also returns an error if an expansion type is -# not supported. +# @model: description of the CPU model to expand +# +# @type: expansion type, specifying how to expand the CPU model +# +# Returns: a CpuModelExpansionInfo describing the expanded CPU model +# +# Errors: +# - if expanding CPU models is not supported +# - if the model cannot be expanded +# - if the model contains an unknown CPU definition name, unknown +# properties or properties with a wrong type +# - if an expansion type is not supported # # Since: 2.8 ## @@ -255,7 +302,9 @@ 'returns': 'CpuModelExpansionInfo', 'if': { 'any': [ 'TARGET_S390X', 'TARGET_I386', - 'TARGET_ARM' ] } } + 'TARGET_ARM', + 'TARGET_LOONGARCH64', + 'TARGET_RISCV' ] } } ## # @CpuDefinitionInfo: @@ -265,49 +314,48 @@ # @name: the name of the CPU definition # # @migration-safe: whether a CPU definition can be safely used for -# migration in combination with a QEMU compatibility machine -# when migrating between different QEMU versions and between -# hosts with different sets of (hardware or software) -# capabilities. If not provided, information is not available -# and callers should not assume the CPU definition to be -# migration-safe. (since 2.8) +# migration in combination with a QEMU compatibility machine when +# migrating between different QEMU versions and between hosts with +# different sets of (hardware or software) capabilities. If not +# provided, information is not available and callers should not +# assume the CPU definition to be migration-safe. (since 2.8) # -# @static: whether a CPU definition is static and will not change depending on -# QEMU version, machine type, machine options and accelerator options. -# A static model is always migration-safe. (since 2.8) +# @static: whether a CPU definition is static and will not change +# depending on QEMU version, machine type, machine options and +# accelerator options. A static model is always migration-safe. +# (since 2.8) # -# @unavailable-features: List of properties that prevent -# the CPU model from running in the current -# host. (since 2.8) -# @typename: Type name that can be used as argument to @device-list-properties, -# to introspect properties configurable using -cpu or -global. -# (since 2.9) +# @unavailable-features: List of properties that prevent the CPU model +# from running in the current host. (since 2.8) # -# @alias-of: Name of CPU model this model is an alias for. The target of the -# CPU model alias may change depending on the machine type. -# Management software is supposed to translate CPU model aliases -# in the VM configuration, because aliases may stop being -# migration-safe in the future (since 4.1) +# @typename: Type name that can be used as argument to +# @device-list-properties, to introspect properties configurable +# using -cpu or -global. (since 2.9) # -# @deprecated: If true, this CPU model is deprecated and may be removed in -# in some future version of QEMU according to the QEMU deprecation -# policy. (since 5.2) +# @alias-of: Name of CPU model this model is an alias for. The target +# of the CPU model alias may change depending on the machine type. +# Management software is supposed to translate CPU model aliases +# in the VM configuration, because aliases may stop being +# migration-safe in the future (since 4.1) # -# @unavailable-features is a list of QOM property names that -# represent CPU model attributes that prevent the CPU from running. -# If the QOM property is read-only, that means there's no known -# way to make the CPU model run in the current host. Implementations -# that choose not to provide specific information return the -# property name "type". -# If the property is read-write, it means that it MAY be possible -# to run the CPU model in the current host if that property is -# changed. Management software can use it as hints to suggest or -# choose an alternative for the user, or just to generate meaningful -# error messages explaining why the CPU model can't be used. -# If @unavailable-features is an empty list, the CPU model is -# runnable using the current host and machine-type. -# If @unavailable-features is not present, runnability -# information for the CPU is not available. +# @deprecated: If true, this CPU model is deprecated and may be +# removed in in some future version of QEMU according to the QEMU +# deprecation policy. (since 5.2) +# +# @unavailable-features is a list of QOM property names that represent +# CPU model attributes that prevent the CPU from running. If the QOM +# property is read-only, that means there's no known way to make the +# CPU model run in the current host. Implementations that choose not +# to provide specific information return the property name "type". If +# the property is read-write, it means that it MAY be possible to run +# the CPU model in the current host if that property is changed. +# Management software can use it as hints to suggest or choose an +# alternative for the user, or just to generate meaningful error +# messages explaining why the CPU model can't be used. If +# @unavailable-features is an empty list, the CPU model is runnable +# using the current host and machine-type. If @unavailable-features +# is not present, runnability information for the CPU is not +# available. # # Since: 1.2 ## @@ -324,14 +372,15 @@ 'TARGET_I386', 'TARGET_S390X', 'TARGET_MIPS', - 'TARGET_LOONGARCH64' ] } } + 'TARGET_LOONGARCH64', + 'TARGET_RISCV' ] } } ## # @query-cpu-definitions: # # Return a list of supported virtual CPU definitions # -# Returns: a list of CpuDefInfo +# Returns: a list of CpuDefinitionInfo # # Since: 1.2 ## @@ -341,4 +390,124 @@ 'TARGET_I386', 'TARGET_S390X', 'TARGET_MIPS', - 'TARGET_LOONGARCH64' ] } } + 'TARGET_LOONGARCH64', + 'TARGET_RISCV' ] } } + +## +# @CpuS390Polarization: +# +# An enumeration of CPU polarization that can be assumed by a virtual +# S390 CPU +# +# Since: 8.2 +## +{ 'enum': 'CpuS390Polarization', + 'prefix': 'S390_CPU_POLARIZATION', + 'data': [ 'horizontal', 'vertical' ], + 'if': 'TARGET_S390X' +} + +## +# @set-cpu-topology: +# +# Modify the topology by moving the CPU inside the topology tree, or +# by changing a modifier attribute of a CPU. Absent values will not +# be modified. +# +# @core-id: the vCPU ID to be moved +# +# @socket-id: destination socket to move the vCPU to +# +# @book-id: destination book to move the vCPU to +# +# @drawer-id: destination drawer to move the vCPU to +# +# @entitlement: entitlement to set +# +# @dedicated: whether the provisioning of real to virtual CPU is +# dedicated +# +# Features: +# +# @unstable: This command is experimental. +# +# Since: 8.2 +## +{ 'command': 'set-cpu-topology', + 'data': { + 'core-id': 'uint16', + '*socket-id': 'uint16', + '*book-id': 'uint16', + '*drawer-id': 'uint16', + '*entitlement': 'CpuS390Entitlement', + '*dedicated': 'bool' + }, + 'features': [ 'unstable' ], + 'if': { 'all': [ 'TARGET_S390X' , 'CONFIG_KVM' ] } +} + +## +# @CPU_POLARIZATION_CHANGE: +# +# Emitted when the guest asks to change the polarization. +# +# The guest can tell the host (via the PTF instruction) whether the +# CPUs should be provisioned using horizontal or vertical +# polarization. +# +# On horizontal polarization the host is expected to provision all +# vCPUs equally. +# +# On vertical polarization the host can provision each vCPU +# differently. The guest will get information on the details of the +# provisioning the next time it uses the STSI(15) instruction. +# +# @polarization: polarization specified by the guest +# +# Features: +# +# @unstable: This event is experimental. +# +# Since: 8.2 +# +# Example: +# +# <- { "event": "CPU_POLARIZATION_CHANGE", +# "data": { "polarization": "horizontal" }, +# "timestamp": { "seconds": 1401385907, "microseconds": 422329 } } +## +{ 'event': 'CPU_POLARIZATION_CHANGE', + 'data': { 'polarization': 'CpuS390Polarization' }, + 'features': [ 'unstable' ], + 'if': { 'all': [ 'TARGET_S390X', 'CONFIG_KVM' ] } +} + +## +# @CpuPolarizationInfo: +# +# The result of a CPU polarization query. +# +# @polarization: the CPU polarization +# +# Since: 8.2 +## +{ 'struct': 'CpuPolarizationInfo', + 'data': { 'polarization': 'CpuS390Polarization' }, + 'if': { 'all': [ 'TARGET_S390X', 'CONFIG_KVM' ] } +} + +## +# @query-s390x-cpu-polarization: +# +# Features: +# +# @unstable: This command is experimental. +# +# Returns: the machine's CPU polarization +# +# Since: 8.2 +## +{ 'command': 'query-s390x-cpu-polarization', 'returns': 'CpuPolarizationInfo', + 'features': [ 'unstable' ], + 'if': { 'all': [ 'TARGET_S390X', 'CONFIG_KVM' ] } +} diff --git a/qapi/machine.json b/qapi/machine.json index f750a16396..e8b60641f2 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -9,22 +9,24 @@ ## { 'include': 'common.json' } +{ 'include': 'machine-common.json' } ## # @SysEmuTarget: # # The comprehensive enumeration of QEMU system emulation ("softmmu") -# targets. Run "./configure --help" in the project root directory, and -# look for the \*-softmmu targets near the "--target-list" option. The -# individual target constants are not documented here, for the time -# being. +# targets. Run "./configure --help" in the project root directory, +# and look for the \*-softmmu targets near the "--target-list" option. +# The individual target constants are not documented here, for the +# time being. # # @rx: since 5.0 +# # @avr: since 5.1 # -# 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". +# 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". # # Since: 3.0 ## @@ -39,8 +41,8 @@ ## # @CpuS390State: # -# An enumeration of cpu states that can be assumed by a virtual -# S390 CPU +# An enumeration of cpu states that can be assumed by a virtual S390 +# CPU # # Since: 2.12 ## @@ -55,9 +57,16 @@ # # @cpu-state: the virtual CPU's state # +# @dedicated: the virtual CPU's dedication (since 8.2) +# +# @entitlement: the virtual CPU's entitlement (since 8.2) +# # Since: 2.12 ## -{ 'struct': 'CpuInfoS390', 'data': { 'cpu-state': 'CpuS390State' } } +{ 'struct': 'CpuInfoS390', + 'data': { 'cpu-state': 'CpuS390State', + '*dedicated': 'bool', + '*entitlement': 'CpuS390Entitlement' } } ## # @CpuInfoFast: @@ -70,11 +79,10 @@ # # @thread-id: ID of the underlying host thread # -# @props: properties describing to which node/socket/core/thread -# virtual CPU belongs to, provided if supported by board +# @props: properties associated with a virtual CPU, e.g. the socket id # # @target: the QEMU system emulation target, which determines which -# additional fields will be listed (since 3.0) +# additional fields will be listed (since 3.0) # # Since: 2.12 ## @@ -98,32 +106,32 @@ # # Example: # -# -> { "execute": "query-cpus-fast" } -# <- { "return": [ -# { -# "thread-id": 25627, -# "props": { -# "core-id": 0, -# "thread-id": 0, -# "socket-id": 0 +# -> { "execute": "query-cpus-fast" } +# <- { "return": [ +# { +# "thread-id": 25627, +# "props": { +# "core-id": 0, +# "thread-id": 0, +# "socket-id": 0 +# }, +# "qom-path": "/machine/unattached/device[0]", +# "target":"x86_64", +# "cpu-index": 0 # }, -# "qom-path": "/machine/unattached/device[0]", -# "target":"x86_64", -# "cpu-index": 0 -# }, -# { -# "thread-id": 25628, -# "props": { -# "core-id": 0, -# "thread-id": 0, -# "socket-id": 1 -# }, -# "qom-path": "/machine/unattached/device[2]", -# "target":"x86_64", -# "cpu-index": 1 -# } -# ] -# } +# { +# "thread-id": 25628, +# "props": { +# "core-id": 0, +# "thread-id": 0, +# "socket-id": 1 +# }, +# "qom-path": "/machine/unattached/device[2]", +# "target":"x86_64", +# "cpu-index": 1 +# } +# ] +# } ## { 'command': 'query-cpus-fast', 'returns': [ 'CpuInfoFast' ] } @@ -139,21 +147,24 @@ # @is-default: whether the machine is default # # @cpu-max: maximum number of CPUs supported by the machine type -# (since 1.5) +# (since 1.5) # # @hotpluggable-cpus: cpu hotplug via -device is supported (since 2.7) # # @numa-mem-supported: true if '-numa node,mem' option is supported by -# the machine type and false otherwise (since 4.1) +# the machine type and false otherwise (since 4.1) # -# @deprecated: if true, the machine type is deprecated and may be removed -# in future versions of QEMU according to the QEMU deprecation -# policy (since 4.1) +# @deprecated: if true, the machine type is deprecated and may be +# removed in future versions of QEMU according to the QEMU +# deprecation policy (since 4.1) # -# @default-cpu-type: default CPU model typename if none is requested via -# the -cpu argument. (since 4.2) +# @default-cpu-type: default CPU model typename if none is requested +# via the -cpu argument. (since 4.2) # -# @default-ram-id: the default ID of initial RAM memory backend (since 5.2) +# @default-ram-id: the default ID of initial RAM memory backend (since +# 5.2) +# +# @acpi: machine type supports ACPI (since 8.0) # # Since: 1.2 ## @@ -162,7 +173,7 @@ '*is-default': 'bool', 'cpu-max': 'int', 'hotpluggable-cpus': 'bool', 'numa-mem-supported': 'bool', 'deprecated': 'bool', '*default-cpu-type': 'str', - '*default-ram-id': 'str' } } + '*default-ram-id': 'str', 'acpi': 'bool' } } ## # @query-machines: @@ -181,7 +192,7 @@ # Information describing the running machine parameters. # # @wakeup-suspend-support: true if the machine supports wake up from -# suspend +# suspend # # Since: 4.0 ## @@ -231,7 +242,8 @@ # # Since: 0.14 # -# Notes: If no UUID was specified for the guest, a null UUID is returned. +# Notes: If no UUID was specified for the guest, a null UUID is +# returned. ## { 'struct': 'UuidInfo', 'data': {'UUID': 'str'} } @@ -246,9 +258,8 @@ # # Example: # -# -> { "execute": "query-uuid" } -# <- { "return": { "UUID": "550e8400-e29b-41d4-a716-446655440000" } } -# +# -> { "execute": "query-uuid" } +# <- { "return": { "UUID": "550e8400-e29b-41d4-a716-446655440000" } } ## { 'command': 'query-uuid', 'returns': 'UuidInfo', 'allow-preconfig': true } @@ -281,9 +292,8 @@ # # Example: # -# -> { "execute": "system_reset" } -# <- { "return": {} } -# +# -> { "execute": "system_reset" } +# <- { "return": {} } ## { 'command': 'system_reset' } @@ -295,67 +305,63 @@ # Since: 0.14 # # Notes: A guest may or may not respond to this command. This command -# returning does not indicate that a guest has accepted the request or -# that it has shut down. Many guests will respond to this command by -# prompting the user in some way. +# returning does not indicate that a guest has accepted the +# request or that it has shut down. Many guests will respond to +# this command by prompting the user in some way. # # Example: # -# -> { "execute": "system_powerdown" } -# <- { "return": {} } -# +# -> { "execute": "system_powerdown" } +# <- { "return": {} } ## { 'command': 'system_powerdown' } ## # @system_wakeup: # -# Wake up guest from suspend. If the guest has wake-up from suspend +# Wake up guest from suspend. If the guest has wake-up from suspend # support enabled (wakeup-suspend-support flag from # query-current-machine), wake-up guest from suspend if the guest is -# in SUSPENDED state. Return an error otherwise. +# in SUSPENDED state. Return an error otherwise. # # Since: 1.1 # -# Returns: nothing. -# # Note: prior to 4.0, this command does nothing in case the guest -# isn't suspended. +# isn't suspended. # # Example: # -# -> { "execute": "system_wakeup" } -# <- { "return": {} } -# +# -> { "execute": "system_wakeup" } +# <- { "return": {} } ## { 'command': 'system_wakeup' } ## # @LostTickPolicy: # -# Policy for handling lost ticks in timer devices. Ticks end up getting -# lost when, for example, the guest is paused. +# 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 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. +# @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. 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. +# @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 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. +# @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 ## @@ -365,20 +371,19 @@ ## # @inject-nmi: # -# Injects a Non-Maskable Interrupt into the default CPU (x86/s390) or all CPUs (ppc64). -# The command fails when the guest doesn't support injecting. -# -# Returns: If successful, nothing +# Injects a Non-Maskable Interrupt into the default CPU (x86/s390) or +# all CPUs (ppc64). The command fails when the guest doesn't support +# injecting. # # Since: 0.14 # -# Note: prior to 2.1, this command was only supported for x86 and s390 VMs +# Note: prior to 2.1, this command was only supported for x86 and s390 +# VMs # # Example: # -# -> { "execute": "inject-nmi" } -# <- { "return": {} } -# +# -> { "execute": "inject-nmi" } +# <- { "return": {} } ## { 'command': 'inject-nmi' } @@ -406,9 +411,8 @@ # # Example: # -# -> { "execute": "query-kvm" } -# <- { "return": { "enabled": true, "present": true } } -# +# -> { "execute": "query-kvm" } +# <- { "return": { "enabled": true, "present": true } } ## { 'command': 'query-kvm', 'returns': 'KvmInfo' } @@ -433,7 +437,9 @@ ## # @NumaOptions: # -# A discriminated record of NUMA options. (for OptsVisitor) +# A discriminated record of NUMA options. (for OptsVisitor) +# +# @type: NUMA option type # # Since: 2.1 ## @@ -450,26 +456,25 @@ ## # @NumaNodeOptions: # -# Create a guest NUMA node. (for OptsVisitor) +# Create a guest NUMA node. (for OptsVisitor) # # @nodeid: NUMA node ID (increase by 1 from 0 if omitted) # -# @cpus: VCPUs belonging to this node (assign VCPUS round-robin -# if omitted) +# @cpus: VCPUs belonging to this node (assign VCPUS round-robin if +# omitted) # # @mem: memory size of this node; mutually exclusive with @memdev. -# Equally divide total memory among nodes if both @mem and @memdev are -# omitted. +# Equally divide total memory among nodes if both @mem and @memdev +# are omitted. # -# @memdev: memory backend object. If specified for one node, -# it must be specified for all nodes. +# @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) +# @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 ## @@ -490,9 +495,9 @@ # # @dst: destination NUMA node. # -# @val: NUMA distance from source node to destination node. -# When a node is unreachable from another node, set the distance -# between them to 255. +# @val: NUMA distance from source node to destination node. When a +# node is unreachable from another node, set the distance between +# them to 255. # # Since: 2.10 ## @@ -507,15 +512,17 @@ # # Create a CXL Fixed Memory Window # -# @size: Size of the Fixed Memory Window in bytes. Must be a multiple -# of 256MiB. -# @interleave-granularity: Number of contiguous bytes for which -# accesses will go to a given interleave target. -# Accepted values [256, 512, 1k, 2k, 4k, 8k, 16k] -# @targets: Target root bridge IDs from -device ...,id= for each root -# bridge. +# @size: Size of the Fixed Memory Window in bytes. Must be a multiple +# of 256MiB. # -# Since 7.1 +# @interleave-granularity: Number of contiguous bytes for which +# accesses will go to a given interleave target. Accepted values +# [256, 512, 1k, 2k, 4k, 8k, 16k] +# +# @targets: Target root bridge IDs from -device ...,id= for each +# root bridge. +# +# Since: 7.1 ## { 'struct': 'CXLFixedMemoryWindowOptions', 'data': { @@ -523,6 +530,19 @@ '*interleave-granularity': 'size', 'targets': ['str'] }} +## +# @CXLFMWProperties: +# +# List of CXL Fixed Memory Windows. +# +# @cxl-fmw: List of CXLFixedMemoryWindowOptions +# +# Since: 7.1 +## +{ 'struct' : 'CXLFMWProperties', + 'data': { 'cxl-fmw': ['CXLFixedMemoryWindowOptions'] } +} + ## # @X86CPURegister32: # @@ -538,10 +558,11 @@ # # Information about a X86 CPU feature word # -# @cpuid-input-eax: Input EAX value for CPUID instruction for that feature word +# @cpuid-input-eax: Input EAX value for CPUID instruction for that +# feature word # # @cpuid-input-ecx: Input ECX value for CPUID instruction for that -# feature word +# feature word # # @cpuid-register: Output register containing the feature bits # @@ -558,7 +579,8 @@ ## # @DummyForceArrays: # -# Not used by QMP; hack to let us use X86CPUFeatureWordInfoList internally +# Not used by QMP; hack to let us use X86CPUFeatureWordInfoList +# internally # # Since: 2.5 ## @@ -568,8 +590,8 @@ ## # @NumaCpuOptions: # -# Option "-numa cpu" overrides default cpu to node mapping. -# It accepts the same set of cpu properties as returned by +# Option "-numa cpu" overrides default cpu to node mapping. It +# accepts the same set of cpu properties as returned by # query-hotpluggable-cpus[].props, where node-id could be used to # override default node mapping. # @@ -604,11 +626,11 @@ ## # @HmatLBDataType: # -# Data type in the System Locality Latency and Bandwidth -# Information Structure of HMAT (Heterogeneous Memory Attribute Table) +# 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. +# 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) # @@ -631,28 +653,27 @@ ## # @NumaHmatLBOptions: # -# Set the system locality latency and bandwidth information -# between Initiator and Target proximity Domains. +# 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. +# 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. +# @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. +# @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)". +# @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". +# proximity domain, the bandwidth unit is "Bytes per second". # # Since: 5.0 ## @@ -674,8 +695,8 @@ # 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) +# @none: None (no memory side cache in this proximity domain, or cache +# associativity unknown) # # @direct: Direct Mapped # @@ -689,14 +710,14 @@ ## # @HmatCacheWritePolicy: # -# Cache write policy in the Memory Side Cache Information Structure -# of HMAT +# 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. +# 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) +# @none: None (no memory side cache in this proximity domain, or cache +# write policy unknown) # # @write-back: Write Back (WB) # @@ -712,8 +733,8 @@ # # 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. +# 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. # @@ -722,7 +743,7 @@ # @level: the cache level described in this structure. # # @associativity: the cache associativity, -# none/direct-mapped/complex(complex cache indexing). +# none/direct-mapped/complex(complex cache indexing). # # @policy: the write policy, none/write-back/write-through. # @@ -751,9 +772,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) -# -# Returns: Nothing on success +# virtual address (defaults to CPU 0) # # Since: 0.14 # @@ -761,12 +780,11 @@ # # Example: # -# -> { "execute": "memsave", -# "arguments": { "val": 10, -# "size": 100, -# "filename": "/tmp/virtual-mem-dump" } } -# <- { "return": {} } -# +# -> { "execute": "memsave", +# "arguments": { "val": 10, +# "size": 100, +# "filename": "/tmp/virtual-mem-dump" } } +# <- { "return": {} } ## { 'command': 'memsave', 'data': {'val': 'int', 'size': 'int', 'filename': 'str', '*cpu-index': 'int'} } @@ -782,20 +800,17 @@ # # @filename: the file to save the memory to as binary data # -# Returns: Nothing on success -# # Since: 0.14 # # Notes: Errors were not reliably returned until 1.1 # # Example: # -# -> { "execute": "pmemsave", -# "arguments": { "val": 10, -# "size": 100, -# "filename": "/tmp/physical-mem-dump" } } -# <- { "return": {} } -# +# -> { "execute": "pmemsave", +# "arguments": { "val": 10, +# "size": 100, +# "filename": "/tmp/physical-mem-dump" } } +# <- { "return": {} } ## { 'command': 'pmemsave', 'data': {'val': 'int', 'size': 'int', 'filename': 'str'} } @@ -817,11 +832,11 @@ # # @share: whether memory is private to QEMU or shared (since 6.1) # -# @reserve: whether swap space (or huge pages) was reserved if applicable. -# This corresponds to the user configuration and not the actual -# behavior implemented in the OS to perform the reservation. -# For example, Linux will never reserve swap space for shared -# file mappings. (since 6.1) +# @reserve: whether swap space (or huge pages) was reserved if +# applicable. This corresponds to the user configuration and not +# the actual behavior implemented in the OS to perform the +# reservation. For example, Linux will never reserve swap space +# for shared file mappings. (since 6.1) # # @host-nodes: host nodes for its memory policy # @@ -852,57 +867,78 @@ # # Example: # -# -> { "execute": "query-memdev" } -# <- { "return": [ -# { -# "id": "mem1", -# "size": 536870912, -# "merge": false, -# "dump": true, -# "prealloc": false, -# "share": false, -# "host-nodes": [0, 1], -# "policy": "bind" -# }, -# { -# "size": 536870912, -# "merge": false, -# "dump": true, -# "prealloc": true, -# "share": false, -# "host-nodes": [2, 3], -# "policy": "preferred" +# -> { "execute": "query-memdev" } +# <- { "return": [ +# { +# "id": "mem1", +# "size": 536870912, +# "merge": false, +# "dump": true, +# "prealloc": false, +# "share": false, +# "host-nodes": [0, 1], +# "policy": "bind" +# }, +# { +# "size": 536870912, +# "merge": false, +# "dump": true, +# "prealloc": true, +# "share": false, +# "host-nodes": [2, 3], +# "policy": "preferred" +# } +# ] # } -# ] -# } -# ## { 'command': 'query-memdev', 'returns': ['Memdev'], 'allow-preconfig': true } ## # @CpuInstanceProperties: # -# List of properties to be used for hotplugging a CPU instance, -# it should be passed by management with device_add command when -# a CPU is being hotplugged. +# List of properties to be used for hotplugging a CPU instance, it +# should be passed by management with device_add command when a CPU is +# being hotplugged. +# +# Which members are optional and which mandatory depends on the +# architecture and board. +# +# For s390x see :ref:`cpu-topology-s390x`. +# +# The ids other than the node-id specify the position of the CPU +# within the CPU topology (as defined by the machine property "smp", +# thus see also type @SMPConfiguration) # # @node-id: NUMA node ID the CPU belongs to -# @socket-id: socket number within node/board the CPU belongs to -# @die-id: die number within socket the CPU belongs to (since 4.1) -# @cluster-id: cluster number within die the CPU belongs to (since 7.1) -# @core-id: core number within cluster the CPU belongs to -# @thread-id: thread number within core the CPU belongs to # -# Note: currently there are 6 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. +# @drawer-id: drawer number within CPU topology the CPU belongs to +# (since 8.2) +# +# @book-id: book number within parent container the CPU belongs to +# (since 8.2) +# +# @socket-id: socket number within parent container the CPU belongs to +# +# @die-id: die number within the parent container the CPU belongs to +# (since 4.1) +# +# @cluster-id: cluster number within the parent container the CPU +# belongs to (since 7.1) +# +# @core-id: core number within the parent container the CPU belongs to +# +# @thread-id: thread number within the core the CPU belongs to +# +# Note: management should be prepared to pass through additional +# properties with device_add. # # Since: 2.7 ## { 'struct': 'CpuInstanceProperties', + # Keep these in sync with the properties device_add accepts 'data': { '*node-id': 'int', + '*drawer-id': 'int', + '*book-id': 'int', '*socket-id': 'int', '*die-id': 'int', '*cluster-id': 'int', @@ -915,10 +951,14 @@ # @HotpluggableCPU: # # @type: CPU object type for usage with device_add command +# # @props: list of properties to be used for hotplugging CPU -# @vcpus-count: number of logical VCPU threads @HotpluggableCPU provides -# @qom-path: link to existing CPU object if CPU is present or -# omitted if CPU is not present. +# +# @vcpus-count: number of logical VCPU threads @HotpluggableCPU +# provides +# +# @qom-path: link to existing CPU object if CPU is present or omitted +# if CPU is not present. # # Since: 2.7 ## @@ -939,49 +979,49 @@ # # Since: 2.7 # -# Example: +# Examples: # -# For pseries machine type started with -smp 2,cores=2,maxcpus=4 -cpu POWER8: +# For pseries machine type started with -smp 2,cores=2,maxcpus=4 +# -cpu POWER8: # -# -> { "execute": "query-hotpluggable-cpus" } -# <- {"return": [ -# { "props": { "core": 8 }, "type": "POWER8-spapr-cpu-core", -# "vcpus-count": 1 }, -# { "props": { "core": 0 }, "type": "POWER8-spapr-cpu-core", -# "vcpus-count": 1, "qom-path": "/machine/unattached/device[0]"} -# ]}' +# -> { "execute": "query-hotpluggable-cpus" } +# <- {"return": [ +# { "props": { "core-id": 8 }, "type": "POWER8-spapr-cpu-core", +# "vcpus-count": 1 }, +# { "props": { "core-id": 0 }, "type": "POWER8-spapr-cpu-core", +# "vcpus-count": 1, "qom-path": "/machine/unattached/device[0]"} +# ]}' # -# For pc machine type started with -smp 1,maxcpus=2: +# For pc machine type started with -smp 1,maxcpus=2: # -# -> { "execute": "query-hotpluggable-cpus" } -# <- {"return": [ -# { -# "type": "qemu64-x86_64-cpu", "vcpus-count": 1, -# "props": {"core-id": 0, "socket-id": 1, "thread-id": 0} -# }, -# { -# "qom-path": "/machine/unattached/device[0]", -# "type": "qemu64-x86_64-cpu", "vcpus-count": 1, -# "props": {"core-id": 0, "socket-id": 0, "thread-id": 0} -# } -# ]} +# -> { "execute": "query-hotpluggable-cpus" } +# <- {"return": [ +# { +# "type": "qemu64-x86_64-cpu", "vcpus-count": 1, +# "props": {"core-id": 0, "socket-id": 1, "thread-id": 0} +# }, +# { +# "qom-path": "/machine/unattached/device[0]", +# "type": "qemu64-x86_64-cpu", "vcpus-count": 1, +# "props": {"core-id": 0, "socket-id": 0, "thread-id": 0} +# } +# ]} # -# For s390x-virtio-ccw machine type started with -smp 1,maxcpus=2 -cpu qemu -# (Since: 2.11): -# -# -> { "execute": "query-hotpluggable-cpus" } -# <- {"return": [ -# { -# "type": "qemu-s390x-cpu", "vcpus-count": 1, -# "props": { "core-id": 1 } -# }, -# { -# "qom-path": "/machine/unattached/device[0]", -# "type": "qemu-s390x-cpu", "vcpus-count": 1, -# "props": { "core-id": 0 } -# } -# ]} +# For s390x-virtio-ccw machine type started with -smp 1,maxcpus=2 +# -cpu qemu (Since: 2.11): # +# -> { "execute": "query-hotpluggable-cpus" } +# <- {"return": [ +# { +# "type": "qemu-s390x-cpu", "vcpus-count": 1, +# "props": { "core-id": 1 } +# }, +# { +# "qom-path": "/machine/unattached/device[0]", +# "type": "qemu-s390x-cpu", "vcpus-count": 1, +# "props": { "core-id": 0 } +# } +# ]} ## { 'command': 'query-hotpluggable-cpus', 'returns': ['HotpluggableCPU'], 'allow-preconfig': true } @@ -989,9 +1029,8 @@ ## # @set-numa-node: # -# Runtime equivalent of '-numa' CLI option, available at -# preconfigure stage to configure numa mapping before initializing -# machine. +# Runtime equivalent of '-numa' CLI option, available at preconfigure +# stage to configure numa mapping before initializing machine. # # Since: 3.0 ## @@ -1005,31 +1044,30 @@ # # Request the balloon driver to change its balloon size. # -# @value: the target logical size of the VM in bytes. -# We can deduce the size of the balloon using this formula: +# @value: the target logical size of the VM in bytes. We can deduce +# the size of the balloon using this formula: # -# logical_vm_size = vm_ram_size - balloon_size +# logical_vm_size = vm_ram_size - balloon_size # -# From it we have: balloon_size = vm_ram_size - @value +# From it we have: balloon_size = vm_ram_size - @value # -# 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 +# Errors: +# - 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 # -# 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 -# size independent of this command. +# 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 size independent of this command. # # Since: 0.14 # # Example: # -# -> { "execute": "balloon", "arguments": { "value": 536870912 } } -# <- { "return": {} } -# -# With a 2.5GiB guest this command inflated the ballon to 3GiB. +# -> { "execute": "balloon", "arguments": { "value": 536870912 } } +# <- { "return": {} } # +# With a 2.5GiB guest this command inflated the ballon to 3GiB. ## { 'command': 'balloon', 'data': {'value': 'int'} } @@ -1038,8 +1076,8 @@ # # Information about the guest balloon device. # -# @actual: the logical size of the VM in bytes -# Formula used: logical_vm_size = vm_ram_size - balloon_size +# @actual: the logical size of the VM in bytes Formula used: +# logical_vm_size = vm_ram_size - balloon_size # # Since: 0.14 ## @@ -1050,32 +1088,35 @@ # # 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 +# +# Errors: +# - 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 # # Example: # -# -> { "execute": "query-balloon" } -# <- { "return": { -# "actual": 1073741824, -# } -# } -# +# -> { "execute": "query-balloon" } +# <- { "return": { +# "actual": 1073741824 +# } +# } ## { 'command': 'query-balloon', 'returns': 'BalloonInfo' } ## # @BALLOON_CHANGE: # -# Emitted when the guest changes the actual BALLOON level. This value is -# equivalent to the @actual field return by the 'query-balloon' command +# Emitted when the guest changes the actual BALLOON level. This value +# is equivalent to the @actual field return by the 'query-balloon' +# command # -# @actual: the logical size of the VM in bytes -# Formula used: logical_vm_size = vm_ram_size - balloon_size +# @actual: the logical size of the VM in bytes Formula used: +# logical_vm_size = vm_ram_size - balloon_size # # Note: this event is rate-limited. # @@ -1083,25 +1124,87 @@ # # Example: # -# <- { "event": "BALLOON_CHANGE", -# "data": { "actual": 944766976 }, -# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } -# +# <- { "event": "BALLOON_CHANGE", +# "data": { "actual": 944766976 }, +# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } ## { 'event': 'BALLOON_CHANGE', 'data': { 'actual': 'int' } } +## +# @HvBalloonInfo: +# +# hv-balloon guest-provided memory status information. +# +# @committed: the amount of memory in use inside the guest plus the +# amount of the memory unusable inside the guest (ballooned out, +# offline, etc.) +# +# @available: the amount of the memory inside the guest available for +# new allocations ("free") +# +# Since: 8.2 +## +{ 'struct': 'HvBalloonInfo', + 'data': { 'committed': 'size', 'available': 'size' } } + +## +# @query-hv-balloon-status-report: +# +# Returns the hv-balloon driver data contained in the last received +# "STATUS" message from the guest. +# +# Returns: +# @HvBalloonInfo +# +# Errors: +# - If no hv-balloon device is present, guest memory status +# reporting is not enabled or no guest memory status report +# received yet, GenericError +# +# Since: 8.2 +# +# Example: +# +# -> { "execute": "query-hv-balloon-status-report" } +# <- { "return": { +# "committed": 816640000, +# "available": 3333054464 +# } +# } +## +{ 'command': 'query-hv-balloon-status-report', 'returns': 'HvBalloonInfo' } + +## +# @HV_BALLOON_STATUS_REPORT: +# +# Emitted when the hv-balloon driver receives a "STATUS" message from +# the guest. +# +# Note: this event is rate-limited. +# +# Since: 8.2 +# +# Example: +# +# <- { "event": "HV_BALLOON_STATUS_REPORT", +# "data": { "committed": 816640000, "available": 3333054464 }, +# "timestamp": { "seconds": 1600295492, "microseconds": 661044 } } +## +{ 'event': 'HV_BALLOON_STATUS_REPORT', + 'data': 'HvBalloonInfo' } + ## # @MemoryInfo: # # Actual memory information in bytes. # # @base-memory: size of "base" memory specified with command line -# option -m. +# option -m. # -# @plugged-memory: size of memory that can be hot-unplugged. This field -# is omitted if target doesn't support memory hotplug -# (i.e. CONFIG_MEM_DEVICE not defined at build time). +# @plugged-memory: size of memory that can be hot-unplugged. This +# field is omitted if target doesn't support memory hotplug (i.e. +# CONFIG_MEM_DEVICE not defined at build time). # # Since: 2.11 ## @@ -1111,13 +1214,13 @@ ## # @query-memory-size-summary: # -# Return the amount of initially allocated and present hotpluggable (if -# enabled) memory in bytes. +# Return the amount of initially allocated and present hotpluggable +# (if enabled) memory in bytes. # # Example: # -# -> { "execute": "query-memory-size-summary" } -# <- { "return": { "base-memory": 4294967296, "plugged-memory": 0 } } +# -> { "execute": "query-memory-size-summary" } +# <- { "return": { "base-memory": 4294967296, "plugged-memory": 0 } } # # Since: 2.11 ## @@ -1142,7 +1245,8 @@ # # @hotplugged: true if device was hotplugged # -# @hotpluggable: true if device if could be added/removed while machine is running +# @hotpluggable: true if device if could be added/removed while +# machine is running # # Since: 2.1 ## @@ -1242,17 +1346,53 @@ } } +## +# @HvBalloonDeviceInfo: +# +# hv-balloon provided memory state information +# +# @id: device's ID +# +# @memaddr: physical address in memory, where device is mapped +# +# @max-size: the maximum size of memory that the device can provide +# +# @memdev: memory backend linked with device +# +# Since: 8.2 +## +{ 'struct': 'HvBalloonDeviceInfo', + 'data': { '*id': 'str', + '*memaddr': 'size', + 'max-size': 'size', + '*memdev': 'str' + } +} + ## # @MemoryDeviceInfoKind: # +# @nvdimm: since 2.12 +# +# @virtio-pmem: since 4.1 +# +# @virtio-mem: since 5.1 +# +# @sgx-epc: since 6.2. +# +# @hv-balloon: since 8.2. +# # Since: 2.1 ## { 'enum': 'MemoryDeviceInfoKind', - 'data': [ 'dimm', 'nvdimm', 'virtio-pmem', 'virtio-mem', 'sgx-epc' ] } + 'data': [ 'dimm', 'nvdimm', 'virtio-pmem', 'virtio-mem', 'sgx-epc', + 'hv-balloon' ] } ## # @PCDIMMDeviceInfoWrapper: # +# @data: PCDIMMDevice state information +# # Since: 2.1 ## { 'struct': 'PCDIMMDeviceInfoWrapper', @@ -1261,6 +1401,8 @@ ## # @VirtioPMEMDeviceInfoWrapper: # +# @data: VirtioPMEM state information +# # Since: 2.1 ## { 'struct': 'VirtioPMEMDeviceInfoWrapper', @@ -1269,6 +1411,8 @@ ## # @VirtioMEMDeviceInfoWrapper: # +# @data: VirtioMEMDevice state information +# # Since: 2.1 ## { 'struct': 'VirtioMEMDeviceInfoWrapper', @@ -1277,18 +1421,29 @@ ## # @SgxEPCDeviceInfoWrapper: # +# @data: Sgx EPC state information +# # Since: 6.2 ## { 'struct': 'SgxEPCDeviceInfoWrapper', 'data': { 'data': 'SgxEPCDeviceInfo' } } +## +# @HvBalloonDeviceInfoWrapper: +# +# @data: hv-balloon provided memory state information +# +# Since: 8.2 +## +{ 'struct': 'HvBalloonDeviceInfoWrapper', + 'data': { 'data': 'HvBalloonDeviceInfo' } } + ## # @MemoryDeviceInfo: # # Union containing information about a memory device # -# nvdimm is included since 2.12. virtio-pmem is included since 4.1. -# virtio-mem is included since 5.1. sgx-epc is included since 6.2. +# @type: memory device type # # Since: 2.1 ## @@ -1299,7 +1454,8 @@ 'nvdimm': 'PCDIMMDeviceInfoWrapper', 'virtio-pmem': 'VirtioPMEMDeviceInfoWrapper', 'virtio-mem': 'VirtioMEMDeviceInfoWrapper', - 'sgx-epc': 'SgxEPCDeviceInfoWrapper' + 'sgx-epc': 'SgxEPCDeviceInfoWrapper', + 'hv-balloon': 'HvBalloonDeviceInfoWrapper' } } @@ -1342,28 +1498,27 @@ # # Example: # -# -> { "execute": "query-memory-devices" } -# <- { "return": [ { "data": -# { "addr": 5368709120, -# "hotpluggable": true, -# "hotplugged": true, -# "id": "d1", -# "memdev": "/objects/memX", -# "node": 0, -# "size": 1073741824, -# "slot": 0}, -# "type": "dimm" -# } ] } -# +# -> { "execute": "query-memory-devices" } +# <- { "return": [ { "data": +# { "addr": 5368709120, +# "hotpluggable": true, +# "hotplugged": true, +# "id": "d1", +# "memdev": "/objects/memX", +# "node": 0, +# "size": 1073741824, +# "slot": 0}, +# "type": "dimm" +# } ] } ## { 'command': 'query-memory-devices', 'returns': ['MemoryDeviceInfo'] } ## # @MEMORY_DEVICE_SIZE_CHANGE: # -# Emitted when the size of a memory device changes. Only emitted for memory -# devices that can actually change the size (e.g., virtio-mem due to guest -# action). +# Emitted when the size of a memory device changes. Only emitted for +# memory devices that can actually change the size (e.g., virtio-mem +# due to guest action). # # @id: device's ID # @@ -1377,11 +1532,10 @@ # # Example: # -# <- { "event": "MEMORY_DEVICE_SIZE_CHANGE", -# "data": { "id": "vm0", "size": 1073741824, -# "qom-path": "/machine/unattached/device[2]" }, -# "timestamp": { "seconds": 1588168529, "microseconds": 201316 } } -# +# <- { "event": "MEMORY_DEVICE_SIZE_CHANGE", +# "data": { "id": "vm0", "size": 1073741824, +# "qom-path": "/machine/unattached/device[2]" }, +# "timestamp": { "seconds": 1588168529, "microseconds": 201316 } } ## { 'event': 'MEMORY_DEVICE_SIZE_CHANGE', 'data': { '*id': 'str', 'size': 'size', 'qom-path' : 'str'} } @@ -1396,19 +1550,19 @@ # @msg: Informative message # # Features: -# @deprecated: This event is deprecated. Use @DEVICE_UNPLUG_GUEST_ERROR -# instead. +# +# @deprecated: This event is deprecated. Use +# @DEVICE_UNPLUG_GUEST_ERROR instead. # # Since: 2.4 # # Example: # -# <- { "event": "MEM_UNPLUG_ERROR" -# "data": { "device": "dimm1", -# "msg": "acpi: device unplug for unsupported device" -# }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } -# +# <- { "event": "MEM_UNPLUG_ERROR", +# "data": { "device": "dimm1", +# "msg": "acpi: device unplug for unsupported device" +# }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'MEM_UNPLUG_ERROR', 'data': { 'device': 'str', 'msg': 'str' }, @@ -1425,13 +1579,15 @@ # # @menu: Whether to show a boot menu # -# @splash: The name of the file to be passed to the firmware as logo picture, if @menu is true. +# @splash: The name of the file to be passed to the firmware as logo +# picture, if @menu is true. # # @splash-time: How long to show the logo picture, in milliseconds # # @reboot-timeout: Timeout before guest reboots after boot fails # -# @strict: Whether to attempt booting from devices not included in the boot order +# @strict: Whether to attempt booting from devices not included in the +# boot order # # Since: 7.1 ## @@ -1447,27 +1603,46 @@ ## # @SMPConfiguration: # -# Schema for CPU topology configuration. A missing value lets -# QEMU figure out a suitable value based on the ones that are provided. +# Schema for CPU topology configuration. A missing value lets QEMU +# figure out a suitable value based on the ones that are provided. +# +# The members other than @cpus and @maxcpus define a topology of +# containers. +# +# The ordering from highest/coarsest to lowest/finest is: +# @drawers, @books, @sockets, @dies, @clusters, @cores, @threads. +# +# Different architectures support different subsets of topology +# containers. +# +# For example, s390x does not have clusters and dies, and the socket +# is the parent container of cores. # # @cpus: number of virtual CPUs in the virtual machine # -# @sockets: number of sockets in the CPU topology +# @maxcpus: maximum number of hotpluggable virtual CPUs in the virtual +# machine # -# @dies: number of dies per socket in the CPU topology +# @drawers: number of drawers in the CPU topology (since 8.2) # -# @clusters: number of clusters per die in the CPU topology (since 7.0) +# @books: number of books in the CPU topology (since 8.2) # -# @cores: number of cores per cluster in the CPU topology +# @sockets: number of sockets per parent container # -# @threads: number of threads per core in the CPU topology +# @dies: number of dies per parent container # -# @maxcpus: maximum number of hotpluggable virtual CPUs in the virtual machine +# @clusters: number of clusters per parent container (since 7.0) +# +# @cores: number of cores per parent container +# +# @threads: number of threads per core # # Since: 6.1 ## { 'struct': 'SMPConfiguration', 'data': { '*cpus': 'int', + '*drawers': 'int', + '*books': 'int', '*sockets': 'int', '*dies': 'int', '*clusters': 'int', @@ -1481,6 +1656,7 @@ # Query interrupt statistics # # Features: +# # @unstable: This command is meant for debugging. # # Returns: interrupt statistics @@ -1497,6 +1673,7 @@ # Query TCG compiler statistics # # Features: +# # @unstable: This command is meant for debugging. # # Returns: TCG compiler statistics @@ -1514,6 +1691,7 @@ # Query NUMA topology information # # Features: +# # @unstable: This command is meant for debugging. # # Returns: topology information @@ -1530,6 +1708,7 @@ # Query TCG opcode counters # # Features: +# # @unstable: This command is meant for debugging. # # Returns: TCG opcode counters @@ -1541,29 +1720,13 @@ 'if': 'CONFIG_TCG', 'features': [ 'unstable' ] } -## -# @x-query-profile: -# -# Query TCG profiling information -# -# Features: -# @unstable: This command is meant for debugging. -# -# Returns: profile information -# -# Since: 6.2 -## -{ 'command': 'x-query-profile', - 'returns': 'HumanReadableText', - 'if': 'CONFIG_TCG', - 'features': [ 'unstable' ] } - ## # @x-query-ramblock: # # Query system ramblock information # # Features: +# # @unstable: This command is meant for debugging. # # Returns: system ramblock information @@ -1580,6 +1743,7 @@ # Query RDMA state # # Features: +# # @unstable: This command is meant for debugging. # # Returns: RDMA state @@ -1596,6 +1760,7 @@ # Query information on the registered ROMS # # Features: +# # @unstable: This command is meant for debugging. # # Returns: registered ROMs @@ -1612,6 +1777,7 @@ # Query information on the USB devices # # Features: +# # @unstable: This command is meant for debugging. # # Returns: USB device information @@ -1629,10 +1795,13 @@ # # @64: SMBIOS version 3.0 (64-bit) Entry Point # +# @auto: Either 2.x or 3.x SMBIOS version, 2.x if configuration can be +# described by it and 3.x otherwise (since: 9.0) +# # Since: 7.0 ## { 'enum': 'SmbiosEntryPointType', - 'data': [ '32', '64' ] } + 'data': [ '32', '64', 'auto' ] } ## # @MemorySizeConfiguration: @@ -1651,3 +1820,22 @@ '*size': 'size', '*max-size': 'size', '*slots': 'uint64' } } + +## +# @dumpdtb: +# +# Save the FDT in dtb format. +# +# @filename: name of the dtb file to be created +# +# Since: 7.2 +# +# Example: +# +# -> { "execute": "dumpdtb" } +# "arguments": { "filename": "fdt.dtb" } } +# <- { "return": {} } +## +{ 'command': 'dumpdtb', + 'data': { 'filename': 'str' }, + 'if': 'CONFIG_FDT' } diff --git a/qapi/meson.build b/qapi/meson.build index 656ef0e039..375d564277 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -13,7 +13,7 @@ util_ss.add(files( if have_system util_ss.add(files('qapi-type-helpers.c')) endif -if have_system or have_tools +if have_system or have_tools or have_ga util_ss.add(files( 'qmp-dispatch.c', 'qmp-event.c', @@ -31,10 +31,13 @@ qapi_all_modules = [ 'compat', 'control', 'crypto', + 'cxl', 'dump', + 'ebpf', 'error', 'introspect', 'job', + 'machine-common', 'machine', 'machine-target', 'migration', @@ -46,14 +49,17 @@ qapi_all_modules = [ 'replay', 'run-state', 'sockets', + 'stats', 'trace', 'transaction', + 'virtio', 'yank', ] if have_system qapi_all_modules += [ 'acpi', 'audio', + 'cryptodev', 'qdev', 'pci', 'rdma', @@ -67,21 +73,6 @@ if have_system or have_tools ] endif -qapi_storage_daemon_modules = [ - 'block-core', - 'block-export', - 'char', - 'common', - 'control', - 'crypto', - 'introspect', - 'job', - 'qom', - 'sockets', - 'pragma', - 'transaction', -] - qapi_nonmodule_outputs = [ 'qapi-introspect.c', 'qapi-introspect.h', 'qapi-types.c', 'qapi-types.h', @@ -152,6 +143,6 @@ foreach output : qapi_specific_outputs + qapi_nonmodule_outputs if output.endswith('.trace-events') qapi_trace_events += qapi_files[i] endif - specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: qapi_files[i]) + specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: qapi_files[i]) i = i + 1 endforeach diff --git a/qapi/migration.json b/qapi/migration.json index 6130cd9fae..8c65b90328 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -16,56 +16,71 @@ # # @transferred: amount of bytes already transferred to the target VM # -# @remaining: amount of bytes remaining to be transferred to the target VM +# @remaining: amount of bytes remaining to be transferred to the +# target VM # # @total: total amount of bytes involved in the migration process # # @duplicate: number of duplicate (zero) pages (since 1.2) # -# @skipped: number of skipped zero pages (since 1.5) +# @skipped: number of skipped zero pages. Always zero, only provided +# for compatibility (since 1.5) # # @normal: number of normal pages (since 1.2) # # @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) +# @dirty-pages-rate: number of pages dirtied by second by the guest +# (since 1.3) # -# @mbps: throughput in megabits/sec. (since 1.6) +# @mbps: throughput in megabits/sec. (since 1.6) # -# @dirty-sync-count: number of times that dirty ram was synchronized (since 2.1) +# @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) +# @postcopy-requests: The number of page requests received from the +# destination (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) # # @precopy-bytes: The number of bytes sent in the pre-copy phase -# (since 7.0). +# (since 7.0). # # @downtime-bytes: The number of bytes sent while the guest is paused -# (since 7.0). +# (since 7.0). # # @postcopy-bytes: The number of bytes sent during the post-copy phase -# (since 7.0). +# (since 7.0). +# +# @dirty-sync-missed-zero-copy: Number of times dirty RAM +# synchronization could not avoid copying dirty pages. This is +# between 0 and @dirty-sync-count * @multifd-channels. (since +# 7.1) +# +# Features: +# +# @deprecated: Member @skipped is always zero since 1.5.3 # # Since: 0.14 ## { 'struct': 'MigrationStats', 'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' , - 'duplicate': 'int', 'skipped': 'int', 'normal': 'int', - 'normal-bytes': 'int', 'dirty-pages-rate' : 'int', - 'mbps' : 'number', 'dirty-sync-count' : 'int', - 'postcopy-requests' : 'int', 'page-size' : 'int', - 'multifd-bytes' : 'uint64', 'pages-per-second' : 'uint64', - 'precopy-bytes' : 'uint64', 'downtime-bytes' : 'uint64', - 'postcopy-bytes' : 'uint64' } } + 'duplicate': 'int', + 'skipped': { 'type': 'int', 'features': [ 'deprecated' ] }, + 'normal': 'int', + 'normal-bytes': 'int', 'dirty-pages-rate': 'int', + 'mbps': 'number', 'dirty-sync-count': 'int', + 'postcopy-requests': 'int', 'page-size': 'int', + 'multifd-bytes': 'uint64', 'pages-per-second': 'uint64', + 'precopy-bytes': 'uint64', 'downtime-bytes': 'uint64', + 'postcopy-bytes': 'uint64', + 'dirty-sync-missed-zero-copy': 'uint64' } } ## # @XBZRLECacheStats: @@ -100,7 +115,8 @@ # # @pages: amount of pages compressed and transferred to the target VM # -# @busy: count of times that no free thread was available to compress data +# @busy: count of times that no free thread was available to compress +# data # # @busy-rate: rate of thread busy # @@ -129,26 +145,29 @@ # # @active: in the process of doing migration. # -# @postcopy-active: like active, but now in postcopy mode. (since 2.5) +# @postcopy-active: like active, but now in postcopy mode. (since +# 2.5) # -# @postcopy-paused: during postcopy but paused. (since 3.0) +# @postcopy-paused: during postcopy but paused. (since 3.0) # -# @postcopy-recover: trying to recover from a paused postcopy. (since 3.0) +# @postcopy-recover: trying to recover from a paused postcopy. (since +# 3.0) # # @completed: migration is finished. # # @failed: some error occurred during migration process. # -# @colo: VM is in the process of fault tolerance, VM can not get into this -# state unless colo capability is enabled for migration. (since 2.8) +# @colo: VM is in the process of fault tolerance, VM can not get into +# this state unless colo capability is enabled for migration. +# (since 2.8) # -# @pre-switchover: Paused before device serialisation. (since 2.11) +# @pre-switchover: Paused before device serialisation. (since 2.11) # -# @device: During device serialisation when pause-before-switchover is enabled -# (since 2.11) +# @device: During device serialisation when pause-before-switchover is +# enabled (since 2.11) # -# @wait-unplug: wait for device unplug request by guest OS to be completed. -# (since 4.2) +# @wait-unplug: wait for device unplug request by guest OS to be +# completed. (since 4.2) # # Since: 2.3 ## @@ -162,7 +181,8 @@ # # Detailed VFIO devices migration statistics # -# @transferred: amount of bytes transferred to the target VM by VFIO devices +# @transferred: amount of bytes transferred to the target VM by VFIO +# devices # # Since: 5.2 ## @@ -175,73 +195,92 @@ # Information about current migration process. # # @status: @MigrationStatus describing the current migration status. -# If this field is not returned, no migration process -# has been initiated +# If this field is not returned, no migration process has been +# initiated # -# @ram: @MigrationStats containing detailed migration -# status, only returned if status is 'active' or -# 'completed'(since 1.2) +# @ram: @MigrationStats containing detailed migration status, only +# returned if status is 'active' or 'completed'(since 1.2) # -# @disk: @MigrationStats containing detailed disk migration -# status, only returned if status is 'active' and it is a block -# migration +# @disk: @MigrationStats containing detailed disk migration status, +# only returned if status is 'active' and it is a block migration # # @xbzrle-cache: @XBZRLECacheStats containing detailed XBZRLE -# migration statistics, only returned if XBZRLE feature is on and -# status is 'active' or 'completed' (since 1.2) +# migration statistics, only returned if XBZRLE feature is on and +# 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) +# @downtime: only present when migration finishes correctly 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: only present while migration is active 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) +# 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) +# @error-desc: the human readable error description string. 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) +# @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) # -# @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) +# @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) # -# @compression: migration compression statistics, only returned if compression -# feature is on and status is 'active' or 'completed' (Since 3.1) +# @compression: migration compression statistics, only returned if +# compression 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) +# @socket-address: Only used for tcp, to know what the real port is +# (Since 4.0) # -# @vfio: @VfioStats containing detailed VFIO devices migration statistics, -# only returned if VFIO device is present, migration is supported by all -# VFIO devices and status is 'active' or 'completed' (since 5.2) +# @vfio: @VfioStats containing detailed VFIO devices migration +# statistics, only returned if VFIO device is present, migration +# is supported by all VFIO devices and status is 'active' or +# 'completed' (since 5.2) # -# @blocked-reasons: A list of reasons an outgoing migration is blocked. -# Present and non-empty when migration is blocked. -# (since 6.0) +# @blocked-reasons: A list of reasons an outgoing migration is +# blocked. Present and non-empty when migration is blocked. +# (since 6.0) +# +# @dirty-limit-throttle-time-per-round: Maximum throttle time +# (in microseconds) of virtual CPUs each dirty ring full round, +# which shows how MigrationCapability dirty-limit affects the +# guest during live migration. (Since 8.1) +# +# @dirty-limit-ring-full-time: Estimated average dirty ring full time +# (in microseconds) for each dirty ring full round. The value +# equals the dirty ring memory size divided by the average dirty +# page rate of the virtual CPU, which can be used to observe the +# average memory load of the virtual CPU indirectly. Note that +# zero means guest doesn't dirty memory. (Since 8.1) +# +# Features: +# +# @deprecated: Member @disk is deprecated because block migration is. +# Member @compression is deprecated because it is unreliable and +# untested. It is recommended to use multifd migration, which +# offers an alternative compression implementation that is +# reliable and tested. # # Since: 0.14 ## { 'struct': 'MigrationInfo', 'data': {'*status': 'MigrationStatus', '*ram': 'MigrationStats', - '*disk': 'MigrationStats', + '*disk': { 'type': 'MigrationStats', 'features': [ 'deprecated' ] }, '*vfio': 'VfioStats', '*xbzrle-cache': 'XBZRLECacheStats', '*total-time': 'int', @@ -251,15 +290,17 @@ '*cpu-throttle-percentage': 'int', '*error-desc': 'str', '*blocked-reasons': ['str'], - '*postcopy-blocktime' : 'uint32', + '*postcopy-blocktime': 'uint32', '*postcopy-vcpu-blocktime': ['uint32'], - '*compression': 'CompressionStats', - '*socket-address': ['SocketAddress'] } } + '*compression': { 'type': 'CompressionStats', 'features': [ 'deprecated' ] }, + '*socket-address': ['SocketAddress'], + '*dirty-limit-throttle-time-per-round': 'uint64', + '*dirty-limit-ring-full-time': 'uint64'} } ## # @query-migrate: # -# Returns information about current migration process. If migration +# Returns information about current migration process. If migration # is active there will be another json-object with RAM migration # status and if block migration is active another one with block # migration status. @@ -268,115 +309,114 @@ # # Since: 0.14 # -# Example: +# Examples: # -# 1. Before the first migration +# 1. Before the first migration # -# -> { "execute": "query-migrate" } -# <- { "return": {} } +# -> { "execute": "query-migrate" } +# <- { "return": {} } # -# 2. Migration is done and has succeeded +# 2. Migration is done and has succeeded # -# -> { "execute": "query-migrate" } -# <- { "return": { -# "status": "completed", -# "total-time":12345, -# "setup-time":12345, -# "downtime":12345, -# "ram":{ -# "transferred":123, -# "remaining":123, -# "total":246, -# "duplicate":123, -# "normal":123, -# "normal-bytes":123456, -# "dirty-sync-count":15 -# } -# } -# } -# -# 3. Migration is done and has failed -# -# -> { "execute": "query-migrate" } -# <- { "return": { "status": "failed" } } -# -# 4. Migration is being performed and is not a block migration: -# -# -> { "execute": "query-migrate" } -# <- { -# "return":{ -# "status":"active", -# "total-time":12345, -# "setup-time":12345, -# "expected-downtime":12345, -# "ram":{ -# "transferred":123, -# "remaining":123, -# "total":246, -# "duplicate":123, -# "normal":123, -# "normal-bytes":123456, -# "dirty-sync-count":15 +# -> { "execute": "query-migrate" } +# <- { "return": { +# "status": "completed", +# "total-time":12345, +# "setup-time":12345, +# "downtime":12345, +# "ram":{ +# "transferred":123, +# "remaining":123, +# "total":246, +# "duplicate":123, +# "normal":123, +# "normal-bytes":123456, +# "dirty-sync-count":15 +# } # } -# } -# } +# } # -# 5. Migration is being performed and is a block migration: +# 3. Migration is done and has failed # -# -> { "execute": "query-migrate" } -# <- { -# "return":{ -# "status":"active", -# "total-time":12345, -# "setup-time":12345, -# "expected-downtime":12345, -# "ram":{ -# "total":1057024, -# "remaining":1053304, -# "transferred":3720, -# "duplicate":123, -# "normal":123, -# "normal-bytes":123456, -# "dirty-sync-count":15 -# }, -# "disk":{ -# "total":20971520, -# "remaining":20880384, -# "transferred":91136 -# } -# } -# } +# -> { "execute": "query-migrate" } +# <- { "return": { "status": "failed" } } # -# 6. Migration is being performed and XBZRLE is active: +# 4. Migration is being performed and is not a block migration: # -# -> { "execute": "query-migrate" } -# <- { -# "return":{ -# "status":"active", -# "total-time":12345, -# "setup-time":12345, -# "expected-downtime":12345, -# "ram":{ -# "total":1057024, -# "remaining":1053304, -# "transferred":3720, -# "duplicate":10, -# "normal":3333, -# "normal-bytes":3412992, -# "dirty-sync-count":15 -# }, -# "xbzrle-cache":{ -# "cache-size":67108864, -# "bytes":20971520, -# "pages":2444343, -# "cache-miss":2244, -# "cache-miss-rate":0.123, -# "encoding-rate":80.1, -# "overflow":34434 -# } -# } -# } +# -> { "execute": "query-migrate" } +# <- { +# "return":{ +# "status":"active", +# "total-time":12345, +# "setup-time":12345, +# "expected-downtime":12345, +# "ram":{ +# "transferred":123, +# "remaining":123, +# "total":246, +# "duplicate":123, +# "normal":123, +# "normal-bytes":123456, +# "dirty-sync-count":15 +# } +# } +# } # +# 5. Migration is being performed and is a block migration: +# +# -> { "execute": "query-migrate" } +# <- { +# "return":{ +# "status":"active", +# "total-time":12345, +# "setup-time":12345, +# "expected-downtime":12345, +# "ram":{ +# "total":1057024, +# "remaining":1053304, +# "transferred":3720, +# "duplicate":123, +# "normal":123, +# "normal-bytes":123456, +# "dirty-sync-count":15 +# }, +# "disk":{ +# "total":20971520, +# "remaining":20880384, +# "transferred":91136 +# } +# } +# } +# +# 6. Migration is being performed and XBZRLE is active: +# +# -> { "execute": "query-migrate" } +# <- { +# "return":{ +# "status":"active", +# "total-time":12345, +# "setup-time":12345, +# "expected-downtime":12345, +# "ram":{ +# "total":1057024, +# "remaining":1053304, +# "transferred":3720, +# "duplicate":10, +# "normal":3333, +# "normal-bytes":3412992, +# "dirty-sync-count":15 +# }, +# "xbzrle-cache":{ +# "cache-size":67108864, +# "bytes":20971520, +# "pages":2444343, +# "cache-miss":2244, +# "cache-miss-rate":0.123, +# "encoding-rate":80.1, +# "overflow":34434 +# } +# } +# } ## { 'command': 'query-migrate', 'returns': 'MigrationInfo' } @@ -385,96 +425,140 @@ # # Migration capabilities enumeration # -# @xbzrle: Migration supports xbzrle (Xor Based Zero Run Length Encoding). -# This feature allows us to minimize migration traffic for certain work -# loads, by sending compressed difference of the pages +# @xbzrle: Migration supports xbzrle (Xor Based Zero Run Length +# Encoding). This feature allows us to minimize migration traffic +# for certain work 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) +# @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) # -# @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) +# @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) # -# @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 ) +# @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) # -# @events: generate events for each migration state change -# (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) +# @auto-converge: If enabled, QEMU will automatically throttle down +# the guest 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) +# @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) # -# @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) +# @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) # -# @release-ram: if enabled, qemu will free the migrated ram pages on the source -# during postcopy-ram migration. (since 2.9) +# @release-ram: if enabled, qemu will free the migrated ram pages on +# the source 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) +# for precopy. (since 2.10) # -# @pause-before-switchover: Pause outgoing migration before serialising device -# state and before disabling block IO (since 2.11) +# @pause-before-switchover: Pause outgoing migration before +# serialising device state and before disabling block IO (since +# 2.11) # # @multifd: Use more than one fd for migration (since 4.0) # # @dirty-bitmaps: If enabled, QEMU will migrate named dirty bitmaps. -# (since 2.12) +# (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) +# @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) # -# @x-ignore-shared: If enabled, QEMU will not migrate shared memory (since 4.0) +# @x-ignore-shared: If enabled, QEMU will not migrate shared memory +# that is accessible on the destination machine. (since 4.0) # # @validate-uuid: Send the UUID of the source to allow the destination -# to ensure it is the same. (since 4.2) +# to ensure it is the same. (since 4.2) # -# @background-snapshot: If enabled, the migration stream will be a snapshot -# of the VM exactly at the point when the migration -# procedure starts. The VM RAM is saved with running VM. -# (since 6.0) +# @background-snapshot: If enabled, the migration stream will be a +# snapshot of the VM exactly at the point when the migration +# procedure starts. The VM RAM is saved with running VM. +# (since 6.0) +# +# @zero-copy-send: Controls behavior on sending memory pages on +# migration. When true, enables a zero-copy mechanism for sending +# memory pages, if host supports it. Requires that QEMU be +# permitted to use locked memory for guest RAM pages. (since 7.1) +# +# @postcopy-preempt: If enabled, the migration process will allow +# postcopy requests to preempt precopy stream, so postcopy +# requests will be handled faster. This is a performance feature +# and should not affect the correctness of postcopy migration. +# (since 7.1) +# +# @switchover-ack: If enabled, migration will not stop the source VM +# and complete the migration until an ACK is received from the +# destination that it's OK to do so. Exactly when this ACK is +# sent depends on the migrated devices that use this feature. For +# example, a device can use it to make sure some of its data is +# sent and loaded in the destination before doing switchover. +# This can reduce downtime if devices that support this capability +# are present. 'return-path' capability must be enabled to use +# it. (since 8.1) +# +# @dirty-limit: If enabled, migration will throttle vCPUs as needed to +# keep their dirty page rate within @vcpu-dirty-limit. This can +# improve responsiveness of large guests during live migration, +# and can result in more stable read performance. Requires KVM +# with accelerator property "dirty-ring-size" set. (Since 8.1) +# +# @mapped-ram: Migrate using fixed offsets in the migration file for +# each RAM page. Requires a migration URI that supports seeking, +# such as a file. (since 9.0) # # Features: +# +# @deprecated: Member @block is deprecated. Use blockdev-mirror with +# NBD instead. Member @compress is deprecated because it is +# unreliable and untested. It is recommended to use multifd +# migration, which offers an alternative compression +# implementation that is reliable and tested. +# # @unstable: Members @x-colo and @x-ignore-shared are experimental. # # Since: 1.2 ## { 'enum': 'MigrationCapability', 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks', - 'compress', 'events', 'postcopy-ram', + { 'name': 'compress', 'features': [ 'deprecated' ] }, + 'events', 'postcopy-ram', { 'name': 'x-colo', 'features': [ 'unstable' ] }, 'release-ram', - 'block', 'return-path', 'pause-before-switchover', 'multifd', + { 'name': 'block', 'features': [ 'deprecated' ] }, + 'return-path', 'pause-before-switchover', 'multifd', 'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate', { 'name': 'x-ignore-shared', 'features': [ 'unstable' ] }, - 'validate-uuid', 'background-snapshot'] } + 'validate-uuid', 'background-snapshot', + 'zero-copy-send', 'postcopy-preempt', 'switchover-ack', + 'dirty-limit', 'mapped-ram'] } ## # @MigrationCapabilityStatus: @@ -488,7 +572,7 @@ # Since: 1.2 ## { 'struct': 'MigrationCapabilityStatus', - 'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } } + 'data': { 'capability': 'MigrationCapability', 'state': 'bool' } } ## # @migrate-set-capabilities: @@ -501,9 +585,9 @@ # # Example: # -# -> { "execute": "migrate-set-capabilities" , "arguments": -# { "capabilities": [ { "capability": "xbzrle", "state": true } ] } } -# +# -> { "execute": "migrate-set-capabilities" , "arguments": +# { "capabilities": [ { "capability": "xbzrle", "state": true } ] } } +# <- { "return": {} } ## { 'command': 'migrate-set-capabilities', 'data': { 'capabilities': ['MigrationCapabilityStatus'] } } @@ -513,24 +597,23 @@ # # Returns information about the current migration capabilities status # -# Returns: @MigrationCapabilitiesStatus +# Returns: @MigrationCapabilityStatus # # Since: 1.2 # # Example: # -# -> { "execute": "query-migrate-capabilities" } -# <- { "return": [ -# {"state": false, "capability": "xbzrle"}, -# {"state": false, "capability": "rdma-pin-all"}, -# {"state": false, "capability": "auto-converge"}, -# {"state": false, "capability": "zero-blocks"}, -# {"state": false, "capability": "compress"}, -# {"state": true, "capability": "events"}, -# {"state": false, "capability": "postcopy-ram"}, -# {"state": false, "capability": "x-colo"} -# ]} -# +# -> { "execute": "query-migrate-capabilities" } +# <- { "return": [ +# {"state": false, "capability": "xbzrle"}, +# {"state": false, "capability": "rdma-pin-all"}, +# {"state": false, "capability": "auto-converge"}, +# {"state": false, "capability": "zero-blocks"}, +# {"state": false, "capability": "compress"}, +# {"state": true, "capability": "events"}, +# {"state": false, "capability": "postcopy-ram"}, +# {"state": false, "capability": "x-colo"} +# ]} ## { 'command': 'query-migrate-capabilities', 'returns': ['MigrationCapabilityStatus']} @@ -540,7 +623,9 @@ # An enumeration of multifd compression methods. # # @none: no compression. +# # @zlib: use zlib compression method. +# # @zstd: use zstd compression method. # # Since: 5.0 @@ -549,11 +634,62 @@ 'data': [ 'none', 'zlib', { 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] } +## +# @MigMode: +# +# @normal: the original form of migration. (since 8.2) +# +# @cpr-reboot: The migrate command stops the VM and saves state to the +# URI. After quitting QEMU, the user resumes by running QEMU +# -incoming. +# +# This mode allows the user to quit QEMU, optionally update and +# reboot the OS, and restart QEMU. If the user reboots, the URI +# must persist across the reboot, such as by using a file. +# +# Unlike normal mode, the use of certain local storage options +# does not block the migration, but the user must not modify the +# contents of guest block devices between the quit and restart. +# +# This mode supports VFIO devices provided the user first puts the +# guest in the suspended runstate, such as by issuing +# guest-suspend-ram to the QEMU guest agent. +# +# Best performance is achieved when the memory backend is shared +# and the @x-ignore-shared migration capability is set, but this +# is not required. Further, if the user reboots before restarting +# such a configuration, the shared memory must persist across the +# reboot, such as by backing it with a dax device. +# +# @cpr-reboot may not be used with postcopy, background-snapshot, +# or COLO. +# +# (since 8.2) +## +{ 'enum': 'MigMode', + 'data': [ 'normal', 'cpr-reboot' ] } + +## +# @ZeroPageDetection: +# +# @none: Do not perform zero page checking. +# +# @legacy: Perform zero page checking in main migration thread. +# +# @multifd: Perform zero page checking in multifd sender thread if +# multifd migration is enabled, else in the main migration thread +# as for @legacy. +# +# Since: 9.0 +## +{ 'enum': 'ZeroPageDetection', + 'data': [ 'none', 'legacy', 'multifd' ] } + ## # @BitmapMigrationBitmapAliasTransform: # -# @persistent: If present, the bitmap will be made persistent -# or transient depending on this parameter. +# @persistent: If present, the bitmap will be made persistent or +# transient depending on this parameter. # # Since: 6.0 ## @@ -568,10 +704,10 @@ # @name: The name of the bitmap. # # @alias: An alias name for migration (for example the bitmap name on -# the opposite site). +# the opposite site). # -# @transform: Allows the modification of the migrated bitmap. -# (since 6.0) +# @transform: Allows the modification of the migrated bitmap. (since +# 6.0) # # Since: 5.2 ## @@ -590,8 +726,8 @@ # # @node-name: A block node name. # -# @alias: An alias block node name for migration (for example the -# node name on the opposite site). +# @alias: An alias block node name for migration (for example the node +# name on the opposite site). # # @bitmaps: Mappings for the bitmaps on this node. # @@ -609,350 +745,411 @@ # # Migration parameters enumeration # -# @announce-initial: Initial delay (in milliseconds) before sending the first -# announce (Since 4.0) +# @announce-initial: Initial delay (in milliseconds) before sending +# the first announce (Since 4.0) # -# @announce-max: Maximum delay (in milliseconds) between packets in the -# announcement (Since 4.0) +# @announce-max: Maximum delay (in milliseconds) between packets in +# the announcement (Since 4.0) # -# @announce-rounds: Number of self-announce packets sent after migration -# (Since 4.0) +# @announce-rounds: Number of self-announce packets sent after +# migration (Since 4.0) # -# @announce-step: Increase in delay (in milliseconds) between subsequent -# packets in the announcement (Since 4.0) +# @announce-step: Increase in delay (in milliseconds) between +# subsequent 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. +# @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. # -# @compress-threads: Set compression thread count to be used in live migration, -# the compression thread count is an integer between 1 and 255. +# @compress-threads: Set compression thread count to be used in live +# migration, 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 -# compression thread to become available; otherwise, -# send the page uncompressed. (Since 3.1) +# @compress-wait-thread: Controls behavior when all compression +# threads are currently busy. If true (default), wait for a free +# compression thread to become available; otherwise, 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. +# @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. # -# @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) +# @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) +# @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) # # @cpu-throttle-increment: throttle percentage increase each time -# auto-converge detects that migration is not making -# progress. The default value is 10. (Since 2.7) +# auto-converge detects that migration is not making progress. +# The default value is 10. (Since 2.7) # -# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage -# At the tail stage of throttling, the Guest is very -# sensitive to CPU percentage while the @cpu-throttle -# -increment is excessive usually at tail stage. -# If this parameter is true, we will compute the ideal -# CPU percentage used by the Guest, which may exactly make -# the dirty rate match the dirty rate threshold. Then we -# will choose a smaller throttle increment between the -# one specified by @cpu-throttle-increment and the one -# generated by ideal CPU percentage. -# Therefore, it is compatible to traditional throttling, -# meanwhile the throttle increment won't be excessive -# at tail stage. -# The default value is false. (Since 5.1) +# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage At +# the tail stage of throttling, the Guest is very sensitive to CPU +# percentage while the @cpu-throttle -increment is excessive +# usually at tail stage. If this parameter is true, we will +# compute the ideal CPU percentage used by the Guest, which may +# exactly make the dirty rate match the dirty rate threshold. +# Then we will choose a smaller throttle increment between the one +# specified by @cpu-throttle-increment and the one generated by +# ideal CPU percentage. Therefore, it is compatible to +# traditional throttling, meanwhile the throttle increment won't +# be excessive at tail stage. The default value is false. (Since +# 5.1) # -# @tls-creds: ID of the 'tls-creds' object that provides credentials for -# establishing a TLS connection over the migration data channel. -# On the outgoing side of the migration, the credentials must -# be for a 'client' endpoint, while for the incoming side the -# credentials must be for a 'server' endpoint. Setting this -# will enable TLS for all migrations. The default is unset, -# resulting in unsecured migration at the QEMU level. (Since 2.7) +# @tls-creds: ID of the 'tls-creds' object that provides credentials +# for establishing a TLS connection over the migration data +# channel. On the outgoing side of the migration, the credentials +# must be for a 'client' endpoint, while for the incoming side the +# credentials must be for a 'server' endpoint. Setting this to a +# non-empty string enables TLS for all migrations. An empty +# string means that QEMU will use plain text mode for migration, +# rather than TLS. (Since 2.7) # -# @tls-hostname: hostname of the target host for the migration. This is -# required when using x509 based TLS credentials and the -# migration URI does not already include a hostname. For -# example if using fd: or exec: based migration, the -# hostname must be provided so that the server's x509 -# certificate identity can be validated. (Since 2.7) +# @tls-hostname: migration target's hostname for validating the +# server's x509 certificate identity. If empty, QEMU will use the +# hostname from the migration URI, if any. A non-empty value is +# required when using x509 based TLS credentials and the migration +# URI does not include a hostname, such as fd: or exec: based +# migration. (Since 2.7) # -# @tls-authz: ID of the 'authz' object subclass that provides access control -# checking of the TLS x509 certificate distinguished name. -# This object is only resolved at time of use, so can be deleted -# and recreated on the fly while the migration server is active. -# If missing, it will default to denying access (Since 4.0) +# Note: empty value works only since 2.9. # -# @max-bandwidth: to set maximum speed for migration. maximum speed in -# bytes per second. (Since 2.8) +# @tls-authz: ID of the 'authz' object subclass that provides access +# control checking of the TLS x509 certificate distinguished name. +# This object is only resolved at time of use, so can be deleted +# and recreated on the fly while the migration server is active. +# If missing, it will default to denying access (Since 4.0) # -# @downtime-limit: set maximum tolerated downtime for migration. maximum -# downtime in milliseconds (Since 2.8) +# @max-bandwidth: maximum speed for migration, in bytes per second. +# (Since 2.8) # -# @x-checkpoint-delay: The delay time (in ms) between two COLO checkpoints in -# periodic mode. (Since 2.8) +# @avail-switchover-bandwidth: to set the available bandwidth that +# migration can use during switchover phase. NOTE! This does not +# limit the bandwidth during switchover, but only for calculations +# when making decisions to switchover. By default, this value is +# zero, which means QEMU will estimate the bandwidth +# automatically. This can be set when the estimated value is not +# accurate, while the user is able to guarantee such bandwidth is +# available when switching over. When specified correctly, this +# can make the switchover decision much more accurate. +# (Since 8.2) +# +# @downtime-limit: set maximum tolerated downtime for migration. +# maximum downtime in milliseconds (Since 2.8) +# +# @x-checkpoint-delay: The delay time (in ms) between two COLO +# checkpoints in 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 -# number of sockets used for migration. The -# default value is 2 (since 4.0) +# parallel. This is the same number that the number of sockets +# used for migration. The default value is 2 (since 4.0) # # @xbzrle-cache-size: cache size to be used by XBZRLE migration. It -# needs to be a multiple of the target page size -# and a power of 2 -# (Since 2.11) +# needs to be a multiple of the target page size and a power of 2 +# (Since 2.11) # -# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. -# Defaults to 0 (unlimited). In bytes per second. -# (Since 3.0) +# @max-postcopy-bandwidth: Background transfer bandwidth during +# postcopy. Defaults to 0 (unlimited). In bytes per second. +# (Since 3.0) # -# @max-cpu-throttle: maximum cpu throttle percentage. -# Defaults to 99. (Since 3.1) +# @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-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) +# 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) -# -# @zero-copy-send: Controls behavior on sending memory pages on migration. -# When true, enables a zero-copy mechanism for sending -# memory pages, if host supports it. -# Requires that QEMU be permitted to use locked memory -# for guest RAM pages. -# Defaults to false. (Since 7.1) +# 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) # # @block-bitmap-mapping: Maps block nodes and bitmaps on them to -# aliases for the purpose of dirty bitmap migration. Such -# aliases may for example be the corresponding names on the -# opposite site. -# The mapping must be one-to-one, but not necessarily -# complete: On the source, unmapped bitmaps and all bitmaps -# on unmapped nodes will be ignored. On the destination, -# encountering an unmapped alias in the incoming migration -# stream will result in a report, and all further bitmap -# migration data will then be discarded. -# Note that the destination does not know about bitmaps it -# does not receive, so there is no limitation or requirement -# regarding the number of bitmaps received, or how they are -# named, or on which nodes they are placed. -# By default (when this parameter has never been set), bitmap -# names are mapped to themselves. Nodes are mapped to their -# block device name if there is one, and to their node name -# otherwise. (Since 5.2) +# aliases for the purpose of dirty bitmap migration. Such aliases +# may for example be the corresponding names on the opposite site. +# The mapping must be one-to-one, but not necessarily complete: On +# the source, unmapped bitmaps and all bitmaps on unmapped nodes +# will be ignored. On the destination, encountering an unmapped +# alias in the incoming migration stream will result in a report, +# and all further bitmap migration data will then be discarded. +# Note that the destination does not know about bitmaps it does +# not receive, so there is no limitation or requirement regarding +# the number of bitmaps received, or how they are named, or on +# which nodes they are placed. By default (when this parameter +# has never been set), bitmap names are mapped to themselves. +# Nodes are mapped to their block device name if there is one, and +# to their node name otherwise. (Since 5.2) +# +# @x-vcpu-dirty-limit-period: Periodic time (in milliseconds) of dirty +# limit during live migration. Should be in the range 1 to +# 1000ms. Defaults to 1000ms. (Since 8.1) +# +# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration. +# Defaults to 1. (Since 8.1) +# +# @mode: Migration mode. See description in @MigMode. Default is +# 'normal'. (Since 8.2) +# +# @zero-page-detection: Whether and how to detect zero pages. +# See description in @ZeroPageDetection. Default is 'multifd'. +# (since 9.0) # # Features: -# @unstable: Member @x-checkpoint-delay is experimental. +# +# @deprecated: Member @block-incremental is deprecated. Use +# blockdev-mirror with NBD instead. Members @compress-level, +# @compress-threads, @decompress-threads and @compress-wait-thread +# are deprecated because @compression is deprecated. +# +# @unstable: Members @x-checkpoint-delay and +# @x-vcpu-dirty-limit-period are experimental. # # Since: 2.4 ## { 'enum': 'MigrationParameter', 'data': ['announce-initial', 'announce-max', 'announce-rounds', 'announce-step', - 'compress-level', 'compress-threads', 'decompress-threads', - 'compress-wait-thread', 'throttle-trigger-threshold', + { 'name': 'compress-level', 'features': [ 'deprecated' ] }, + { 'name': 'compress-threads', 'features': [ 'deprecated' ] }, + { 'name': 'decompress-threads', 'features': [ 'deprecated' ] }, + { 'name': 'compress-wait-thread', 'features': [ 'deprecated' ] }, + 'throttle-trigger-threshold', 'cpu-throttle-initial', 'cpu-throttle-increment', 'cpu-throttle-tailslow', 'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth', - 'downtime-limit', + 'avail-switchover-bandwidth', 'downtime-limit', { 'name': 'x-checkpoint-delay', 'features': [ 'unstable' ] }, - 'block-incremental', + { 'name': 'block-incremental', 'features': [ 'deprecated' ] }, 'multifd-channels', 'xbzrle-cache-size', 'max-postcopy-bandwidth', 'max-cpu-throttle', 'multifd-compression', - 'multifd-zlib-level' ,'multifd-zstd-level', - { 'name': 'zero-copy-send', 'if' : 'CONFIG_LINUX'}, - 'block-bitmap-mapping' ] } + 'multifd-zlib-level', 'multifd-zstd-level', + 'block-bitmap-mapping', + { 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] }, + 'vcpu-dirty-limit', + 'mode', + 'zero-page-detection'] } ## # @MigrateSetParameters: # -# @announce-initial: Initial delay (in milliseconds) before sending the first -# announce (Since 4.0) +# @announce-initial: Initial delay (in milliseconds) before sending +# the first announce (Since 4.0) # -# @announce-max: Maximum delay (in milliseconds) between packets in the -# announcement (Since 4.0) +# @announce-max: Maximum delay (in milliseconds) between packets in +# the announcement (Since 4.0) # -# @announce-rounds: Number of self-announce packets sent after migration -# (Since 4.0) +# @announce-rounds: Number of self-announce packets sent after +# migration (Since 4.0) # -# @announce-step: Increase in delay (in milliseconds) between subsequent -# packets in the announcement (Since 4.0) +# @announce-step: Increase in delay (in milliseconds) between +# subsequent packets in the announcement (Since 4.0) # -# @compress-level: compression level +# @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. # -# @compress-threads: compression thread count +# @compress-threads: Set compression thread count to be used in live +# migration, 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 -# compression thread to become available; otherwise, -# send the page uncompressed. (Since 3.1) +# @compress-wait-thread: Controls behavior when all compression +# threads are currently busy. If true (default), wait for a free +# compression thread to become available; otherwise, send the page +# uncompressed. (Since 3.1) # -# @decompress-threads: decompression thread count +# @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. # -# @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) +# @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) +# throttled when migration auto-converge is activated. The +# default value is 20. (Since 2.7) # # @cpu-throttle-increment: throttle percentage increase each time -# auto-converge detects that migration is not making -# progress. The default value is 10. (Since 2.7) +# auto-converge detects that migration is not making progress. +# The default value is 10. (Since 2.7) # -# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage -# At the tail stage of throttling, the Guest is very -# sensitive to CPU percentage while the @cpu-throttle -# -increment is excessive usually at tail stage. -# If this parameter is true, we will compute the ideal -# CPU percentage used by the Guest, which may exactly make -# the dirty rate match the dirty rate threshold. Then we -# will choose a smaller throttle increment between the -# one specified by @cpu-throttle-increment and the one -# generated by ideal CPU percentage. -# Therefore, it is compatible to traditional throttling, -# meanwhile the throttle increment won't be excessive -# at tail stage. -# The default value is false. (Since 5.1) +# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage At +# the tail stage of throttling, the Guest is very sensitive to CPU +# percentage while the @cpu-throttle -increment is excessive +# usually at tail stage. If this parameter is true, we will +# compute the ideal CPU percentage used by the Guest, which may +# exactly make the dirty rate match the dirty rate threshold. +# Then we will choose a smaller throttle increment between the one +# specified by @cpu-throttle-increment and the one generated by +# ideal CPU percentage. Therefore, it is compatible to +# traditional throttling, meanwhile the throttle increment won't +# be excessive at tail stage. The default value is false. (Since +# 5.1) # # @tls-creds: ID of the 'tls-creds' object that provides credentials -# for establishing a TLS connection over the migration data -# channel. On the outgoing side of the migration, the credentials -# must be for a 'client' endpoint, while for the incoming side the -# credentials must be for a 'server' endpoint. Setting this -# to a non-empty string enables TLS for all migrations. -# An empty string means that QEMU will use plain text mode for -# migration, rather than TLS (Since 2.9) -# Previously (since 2.7), this was reported by omitting -# tls-creds instead. +# for establishing a TLS connection over the migration data +# channel. On the outgoing side of the migration, the credentials +# must be for a 'client' endpoint, while for the incoming side the +# credentials must be for a 'server' endpoint. Setting this to a +# non-empty string enables TLS for all migrations. An empty +# string means that QEMU will use plain text mode for migration, +# rather than TLS. This is the default. (Since 2.7) # -# @tls-hostname: hostname of the target host for the migration. This -# is required when using x509 based TLS credentials and the -# migration URI does not already include a hostname. For -# example if using fd: or exec: based migration, the -# hostname must be provided so that the server's x509 -# certificate identity can be validated. (Since 2.7) -# An empty string means that QEMU will use the hostname -# associated with the migration URI, if any. (Since 2.9) -# Previously (since 2.7), this was reported by omitting -# tls-hostname instead. +# @tls-hostname: migration target's hostname for validating the +# server's x509 certificate identity. If empty, QEMU will use the +# hostname from the migration URI, if any. A non-empty value is +# required when using x509 based TLS credentials and the migration +# URI does not include a hostname, such as fd: or exec: based +# migration. (Since 2.7) # -# @max-bandwidth: to set maximum speed for migration. maximum speed in -# bytes per second. (Since 2.8) +# Note: empty value works only since 2.9. # -# @downtime-limit: set maximum tolerated downtime for migration. maximum -# downtime in milliseconds (Since 2.8) +# @tls-authz: ID of the 'authz' object subclass that provides access +# control checking of the TLS x509 certificate distinguished name. +# This object is only resolved at time of use, so can be deleted +# and recreated on the fly while the migration server is active. +# If missing, it will default to denying access (Since 4.0) # -# @x-checkpoint-delay: the delay time between two COLO checkpoints. (Since 2.8) +# @max-bandwidth: maximum speed for migration, in bytes per second. +# (Since 2.8) +# +# @avail-switchover-bandwidth: to set the available bandwidth that +# migration can use during switchover phase. NOTE! This does not +# limit the bandwidth during switchover, but only for calculations +# when making decisions to switchover. By default, this value is +# zero, which means QEMU will estimate the bandwidth +# automatically. This can be set when the estimated value is not +# accurate, while the user is able to guarantee such bandwidth is +# available when switching over. When specified correctly, this +# can make the switchover decision much more accurate. +# (Since 8.2) +# +# @downtime-limit: set maximum tolerated downtime for migration. +# maximum downtime in milliseconds (Since 2.8) +# +# @x-checkpoint-delay: The delay time (in ms) between two COLO +# checkpoints in 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 -# number of sockets used for migration. The -# default value is 2 (since 4.0) +# parallel. This is the same number that the number of sockets +# used for migration. The default value is 2 (since 4.0) # # @xbzrle-cache-size: cache size to be used by XBZRLE migration. It -# needs to be a multiple of the target page size -# and a power of 2 -# (Since 2.11) +# needs to be a multiple of the target page size and a power of 2 +# (Since 2.11) # -# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. -# Defaults to 0 (unlimited). In bytes per second. -# (Since 3.0) +# @max-postcopy-bandwidth: Background transfer bandwidth during +# postcopy. 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) +# @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-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) +# 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) -# -# @zero-copy-send: Controls behavior on sending memory pages on migration. -# When true, enables a zero-copy mechanism for sending -# memory pages, if host supports it. -# Requires that QEMU be permitted to use locked memory -# for guest RAM pages. -# Defaults to false. (Since 7.1) +# 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) # # @block-bitmap-mapping: Maps block nodes and bitmaps on them to -# aliases for the purpose of dirty bitmap migration. Such -# aliases may for example be the corresponding names on the -# opposite site. -# The mapping must be one-to-one, but not necessarily -# complete: On the source, unmapped bitmaps and all bitmaps -# on unmapped nodes will be ignored. On the destination, -# encountering an unmapped alias in the incoming migration -# stream will result in a report, and all further bitmap -# migration data will then be discarded. -# Note that the destination does not know about bitmaps it -# does not receive, so there is no limitation or requirement -# regarding the number of bitmaps received, or how they are -# named, or on which nodes they are placed. -# By default (when this parameter has never been set), bitmap -# names are mapped to themselves. Nodes are mapped to their -# block device name if there is one, and to their node name -# otherwise. (Since 5.2) +# aliases for the purpose of dirty bitmap migration. Such aliases +# may for example be the corresponding names on the opposite site. +# The mapping must be one-to-one, but not necessarily complete: On +# the source, unmapped bitmaps and all bitmaps on unmapped nodes +# will be ignored. On the destination, encountering an unmapped +# alias in the incoming migration stream will result in a report, +# and all further bitmap migration data will then be discarded. +# Note that the destination does not know about bitmaps it does +# not receive, so there is no limitation or requirement regarding +# the number of bitmaps received, or how they are named, or on +# which nodes they are placed. By default (when this parameter +# has never been set), bitmap names are mapped to themselves. +# Nodes are mapped to their block device name if there is one, and +# to their node name otherwise. (Since 5.2) +# +# @x-vcpu-dirty-limit-period: Periodic time (in milliseconds) of dirty +# limit during live migration. Should be in the range 1 to +# 1000ms. Defaults to 1000ms. (Since 8.1) +# +# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration. +# Defaults to 1. (Since 8.1) +# +# @mode: Migration mode. See description in @MigMode. Default is +# 'normal'. (Since 8.2) +# +# @zero-page-detection: Whether and how to detect zero pages. +# See description in @ZeroPageDetection. Default is 'multifd'. +# (since 9.0) # # Features: -# @unstable: Member @x-checkpoint-delay is experimental. +# +# @deprecated: Member @block-incremental is deprecated. Use +# blockdev-mirror with NBD instead. Members @compress-level, +# @compress-threads, @decompress-threads and @compress-wait-thread +# are deprecated because @compression is deprecated. +# +# @unstable: Members @x-checkpoint-delay and +# @x-vcpu-dirty-limit-period are experimental. +# +# TODO: either fuse back into MigrationParameters, or make +# MigrationParameters members mandatory # # Since: 2.4 ## -# TODO either fuse back into MigrationParameters, or make -# MigrationParameters members mandatory { 'struct': 'MigrateSetParameters', 'data': { '*announce-initial': 'size', '*announce-max': 'size', '*announce-rounds': 'size', '*announce-step': 'size', - '*compress-level': 'uint8', - '*compress-threads': 'uint8', - '*compress-wait-thread': 'bool', - '*decompress-threads': 'uint8', + '*compress-level': { 'type': 'uint8', + 'features': [ 'deprecated' ] }, + '*compress-threads': { 'type': 'uint8', + 'features': [ 'deprecated' ] }, + '*compress-wait-thread': { 'type': 'bool', + 'features': [ 'deprecated' ] }, + '*decompress-threads': { 'type': 'uint8', + 'features': [ 'deprecated' ] }, '*throttle-trigger-threshold': 'uint8', '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', @@ -961,10 +1158,12 @@ '*tls-hostname': 'StrOrNull', '*tls-authz': 'StrOrNull', '*max-bandwidth': 'size', + '*avail-switchover-bandwidth': 'size', '*downtime-limit': 'uint64', '*x-checkpoint-delay': { 'type': 'uint32', 'features': [ 'unstable' ] }, - '*block-incremental': 'bool', + '*block-incremental': { 'type': 'bool', + 'features': [ 'deprecated' ] }, '*multifd-channels': 'uint8', '*xbzrle-cache-size': 'size', '*max-postcopy-bandwidth': 'size', @@ -972,8 +1171,12 @@ '*multifd-compression': 'MultiFDCompression', '*multifd-zlib-level': 'uint8', '*multifd-zstd-level': 'uint8', - '*zero-copy-send': { 'type': 'bool', 'if': 'CONFIG_LINUX' }, - '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ] } } + '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ], + '*x-vcpu-dirty-limit-period': { 'type': 'uint64', + 'features': [ 'unstable' ] }, + '*vcpu-dirty-limit': 'uint64', + '*mode': 'MigMode', + '*zero-page-detection': 'ZeroPageDetection'} } ## # @migrate-set-parameters: @@ -984,9 +1187,9 @@ # # Example: # -# -> { "execute": "migrate-set-parameters" , -# "arguments": { "compress-level": 1 } } -# +# -> { "execute": "migrate-set-parameters" , +# "arguments": { "multifd-channels": 5 } } +# <- { "return": {} } ## { 'command': 'migrate-set-parameters', 'boxed': true, 'data': 'MigrateSetParameters' } @@ -996,157 +1199,170 @@ # # The optional members aren't actually optional. # -# @announce-initial: Initial delay (in milliseconds) before sending the -# first announce (Since 4.0) +# @announce-initial: Initial delay (in milliseconds) before sending +# the first announce (Since 4.0) # -# @announce-max: Maximum delay (in milliseconds) between packets in the -# announcement (Since 4.0) +# @announce-max: Maximum delay (in milliseconds) between packets in +# the announcement (Since 4.0) # -# @announce-rounds: Number of self-announce packets sent after migration -# (Since 4.0) +# @announce-rounds: Number of self-announce packets sent after +# migration (Since 4.0) # -# @announce-step: Increase in delay (in milliseconds) between subsequent -# packets in the announcement (Since 4.0) +# @announce-step: Increase in delay (in milliseconds) between +# subsequent packets in the announcement (Since 4.0) # # @compress-level: compression level # # @compress-threads: compression thread count # -# @compress-wait-thread: Controls behavior when all compression threads are -# currently busy. If true (default), wait for a free -# compression thread to become available; otherwise, -# send the page uncompressed. (Since 3.1) +# @compress-wait-thread: Controls behavior when all compression +# threads are currently busy. If true (default), wait for a free +# compression thread to become available; otherwise, send the page +# uncompressed. (Since 3.1) # # @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) +# @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) +# throttled when migration auto-converge is activated. (Since +# 2.7) # # @cpu-throttle-increment: throttle percentage increase each time -# auto-converge detects that migration is not making -# progress. (Since 2.7) +# auto-converge detects that migration is not making progress. +# (Since 2.7) # -# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage -# At the tail stage of throttling, the Guest is very -# sensitive to CPU percentage while the @cpu-throttle -# -increment is excessive usually at tail stage. -# If this parameter is true, we will compute the ideal -# CPU percentage used by the Guest, which may exactly make -# the dirty rate match the dirty rate threshold. Then we -# will choose a smaller throttle increment between the -# one specified by @cpu-throttle-increment and the one -# generated by ideal CPU percentage. -# Therefore, it is compatible to traditional throttling, -# meanwhile the throttle increment won't be excessive -# at tail stage. -# The default value is false. (Since 5.1) +# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage At +# the tail stage of throttling, the Guest is very sensitive to CPU +# percentage while the @cpu-throttle -increment is excessive +# usually at tail stage. If this parameter is true, we will +# compute the ideal CPU percentage used by the Guest, which may +# exactly make the dirty rate match the dirty rate threshold. +# Then we will choose a smaller throttle increment between the one +# specified by @cpu-throttle-increment and the one generated by +# ideal CPU percentage. Therefore, it is compatible to +# traditional throttling, meanwhile the throttle increment won't +# be excessive at tail stage. The default value is false. (Since +# 5.1) # # @tls-creds: ID of the 'tls-creds' object that provides credentials -# for establishing a TLS connection over the migration data -# channel. On the outgoing side of the migration, the credentials -# must be for a 'client' endpoint, while for the incoming side the -# credentials must be for a 'server' endpoint. -# An empty string means that QEMU will use plain text mode for -# migration, rather than TLS (Since 2.7) -# Note: 2.8 reports this by omitting tls-creds instead. +# for establishing a TLS connection over the migration data +# channel. On the outgoing side of the migration, the credentials +# must be for a 'client' endpoint, while for the incoming side the +# credentials must be for a 'server' endpoint. An empty string +# means that QEMU will use plain text mode for migration, rather +# than TLS. (Since 2.7) # -# @tls-hostname: hostname of the target host for the migration. This -# is required when using x509 based TLS credentials and the -# migration URI does not already include a hostname. For -# example if using fd: or exec: based migration, the -# hostname must be provided so that the server's x509 -# certificate identity can be validated. (Since 2.7) -# An empty string means that QEMU will use the hostname -# associated with the migration URI, if any. (Since 2.9) -# Note: 2.8 reports this by omitting tls-hostname instead. +# Note: 2.8 omits empty @tls-creds instead. # -# @tls-authz: ID of the 'authz' object subclass that provides access control -# checking of the TLS x509 certificate distinguished name. (Since -# 4.0) +# @tls-hostname: migration target's hostname for validating the +# server's x509 certificate identity. If empty, QEMU will use the +# hostname from the migration URI, if any. (Since 2.7) # -# @max-bandwidth: to set maximum speed for migration. maximum speed in -# bytes per second. (Since 2.8) +# Note: 2.8 omits empty @tls-hostname instead. # -# @downtime-limit: set maximum tolerated downtime for migration. maximum -# downtime in milliseconds (Since 2.8) +# @tls-authz: ID of the 'authz' object subclass that provides access +# control checking of the TLS x509 certificate distinguished name. +# (Since 4.0) # -# @x-checkpoint-delay: the delay time between two COLO checkpoints. (Since 2.8) +# @max-bandwidth: maximum speed for migration, in bytes per second. +# (Since 2.8) +# +# @avail-switchover-bandwidth: to set the available bandwidth that +# migration can use during switchover phase. NOTE! This does not +# limit the bandwidth during switchover, but only for calculations +# when making decisions to switchover. By default, this value is +# zero, which means QEMU will estimate the bandwidth +# automatically. This can be set when the estimated value is not +# accurate, while the user is able to guarantee such bandwidth is +# available when switching over. When specified correctly, this +# can make the switchover decision much more accurate. +# (Since 8.2) +# +# @downtime-limit: set maximum tolerated downtime for migration. +# maximum downtime in milliseconds (Since 2.8) +# +# @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 -# number of sockets used for migration. -# The default value is 2 (since 4.0) +# parallel. This is the same number that the number of sockets +# used for migration. The default value is 2 (since 4.0) # # @xbzrle-cache-size: cache size to be used by XBZRLE migration. It -# needs to be a multiple of the target page size -# and a power of 2 -# (Since 2.11) +# needs to be a multiple of the target page size and a power of 2 +# (Since 2.11) # -# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. -# Defaults to 0 (unlimited). In bytes per second. -# (Since 3.0) +# @max-postcopy-bandwidth: Background transfer bandwidth during +# postcopy. Defaults to 0 (unlimited). In bytes per second. +# (Since 3.0) # -# @max-cpu-throttle: maximum cpu throttle percentage. -# Defaults to 99. -# (Since 3.1) +# @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-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) +# 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) -# -# @zero-copy-send: Controls behavior on sending memory pages on migration. -# When true, enables a zero-copy mechanism for sending -# memory pages, if host supports it. -# Requires that QEMU be permitted to use locked memory -# for guest RAM pages. -# Defaults to false. (Since 7.1) +# 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) # # @block-bitmap-mapping: Maps block nodes and bitmaps on them to -# aliases for the purpose of dirty bitmap migration. Such -# aliases may for example be the corresponding names on the -# opposite site. -# The mapping must be one-to-one, but not necessarily -# complete: On the source, unmapped bitmaps and all bitmaps -# on unmapped nodes will be ignored. On the destination, -# encountering an unmapped alias in the incoming migration -# stream will result in a report, and all further bitmap -# migration data will then be discarded. -# Note that the destination does not know about bitmaps it -# does not receive, so there is no limitation or requirement -# regarding the number of bitmaps received, or how they are -# named, or on which nodes they are placed. -# By default (when this parameter has never been set), bitmap -# names are mapped to themselves. Nodes are mapped to their -# block device name if there is one, and to their node name -# otherwise. (Since 5.2) +# aliases for the purpose of dirty bitmap migration. Such aliases +# may for example be the corresponding names on the opposite site. +# The mapping must be one-to-one, but not necessarily complete: On +# the source, unmapped bitmaps and all bitmaps on unmapped nodes +# will be ignored. On the destination, encountering an unmapped +# alias in the incoming migration stream will result in a report, +# and all further bitmap migration data will then be discarded. +# Note that the destination does not know about bitmaps it does +# not receive, so there is no limitation or requirement regarding +# the number of bitmaps received, or how they are named, or on +# which nodes they are placed. By default (when this parameter +# has never been set), bitmap names are mapped to themselves. +# Nodes are mapped to their block device name if there is one, and +# to their node name otherwise. (Since 5.2) +# +# @x-vcpu-dirty-limit-period: Periodic time (in milliseconds) of dirty +# limit during live migration. Should be in the range 1 to +# 1000ms. Defaults to 1000ms. (Since 8.1) +# +# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration. +# Defaults to 1. (Since 8.1) +# +# @mode: Migration mode. See description in @MigMode. Default is +# 'normal'. (Since 8.2) +# +# @zero-page-detection: Whether and how to detect zero pages. +# See description in @ZeroPageDetection. Default is 'multifd'. +# (since 9.0) # # Features: -# @unstable: Member @x-checkpoint-delay is experimental. +# +# @deprecated: Member @block-incremental is deprecated. Use +# blockdev-mirror with NBD instead. Members @compress-level, +# @compress-threads, @decompress-threads and @compress-wait-thread +# are deprecated because @compression is deprecated. +# +# @unstable: Members @x-checkpoint-delay and +# @x-vcpu-dirty-limit-period are experimental. # # Since: 2.4 ## @@ -1155,10 +1371,14 @@ '*announce-max': 'size', '*announce-rounds': 'size', '*announce-step': 'size', - '*compress-level': 'uint8', - '*compress-threads': 'uint8', - '*compress-wait-thread': 'bool', - '*decompress-threads': 'uint8', + '*compress-level': { 'type': 'uint8', + 'features': [ 'deprecated' ] }, + '*compress-threads': { 'type': 'uint8', + 'features': [ 'deprecated' ] }, + '*compress-wait-thread': { 'type': 'bool', + 'features': [ 'deprecated' ] }, + '*decompress-threads': { 'type': 'uint8', + 'features': [ 'deprecated' ] }, '*throttle-trigger-threshold': 'uint8', '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', @@ -1167,10 +1387,12 @@ '*tls-hostname': 'str', '*tls-authz': 'str', '*max-bandwidth': 'size', + '*avail-switchover-bandwidth': 'size', '*downtime-limit': 'uint64', '*x-checkpoint-delay': { 'type': 'uint32', 'features': [ 'unstable' ] }, - '*block-incremental': 'bool', + '*block-incremental': { 'type': 'bool', + 'features': [ 'deprecated' ] }, '*multifd-channels': 'uint8', '*xbzrle-cache-size': 'size', '*max-postcopy-bandwidth': 'size', @@ -1178,8 +1400,12 @@ '*multifd-compression': 'MultiFDCompression', '*multifd-zlib-level': 'uint8', '*multifd-zstd-level': 'uint8', - '*zero-copy-send': { 'type': 'bool', 'if': 'CONFIG_LINUX' }, - '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ] } } + '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ], + '*x-vcpu-dirty-limit-period': { 'type': 'uint64', + 'features': [ 'unstable' ] }, + '*vcpu-dirty-limit': 'uint64', + '*mode': 'MigMode', + '*zero-page-detection': 'ZeroPageDetection'} } ## # @query-migrate-parameters: @@ -1192,64 +1418,32 @@ # # Example: # -# -> { "execute": "query-migrate-parameters" } -# <- { "return": { -# "decompress-threads": 2, -# "cpu-throttle-increment": 10, -# "compress-threads": 8, -# "compress-level": 1, -# "cpu-throttle-initial": 20, -# "max-bandwidth": 33554432, -# "downtime-limit": 300 -# } -# } -# +# -> { "execute": "query-migrate-parameters" } +# <- { "return": { +# "multifd-channels": 2, +# "cpu-throttle-increment": 10, +# "cpu-throttle-initial": 20, +# "max-bandwidth": 33554432, +# "downtime-limit": 300 +# } +# } ## { 'command': 'query-migrate-parameters', 'returns': 'MigrationParameters' } -## -# @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. -# -# @protocol: must be "spice" -# @hostname: migration target hostname -# @port: spice tcp port for plaintext channels -# @tls-port: spice tcp port for tls-secured channels -# @cert-subject: server certificate subject -# -# Since: 0.14 -# -# Example: -# -# -> { "execute": "client_migrate_info", -# "arguments": { "protocol": "spice", -# "hostname": "virt42.lab.kraxel.org", -# "port": 1234 } } -# <- { "return": {} } -# -## -{ 'command': 'client_migrate_info', - 'data': { 'protocol': 'str', 'hostname': 'str', '*port': 'int', - '*tls-port': 'int', '*cert-subject': 'str' } } - ## # @migrate-start-postcopy: # -# Followup to a migration command to switch the migration to postcopy mode. -# The postcopy-ram capability must be set on both source and destination -# before the original migration command. +# Followup to a migration command to switch the migration to postcopy +# mode. The postcopy-ram capability must be set on both source and +# destination before the original migration command. # # Since: 2.5 # # Example: # -# -> { "execute": "migrate-start-postcopy" } -# <- { "return": {} } -# +# -> { "execute": "migrate-start-postcopy" } +# <- { "return": {} } ## { 'command': 'migrate-start-postcopy' } @@ -1264,10 +1458,9 @@ # # Example: # -# <- {"timestamp": {"seconds": 1432121972, "microseconds": 744001}, -# "event": "MIGRATION", -# "data": {"status": "completed"} } -# +# <- {"timestamp": {"seconds": 1432121972, "microseconds": 744001}, +# "event": "MIGRATION", +# "data": {"status": "completed"} } ## { 'event': 'MIGRATION', 'data': {'status': 'MigrationStatus'}} @@ -1275,8 +1468,8 @@ ## # @MIGRATION_PASS: # -# Emitted from the source side of a migration at the start of each pass -# (when it syncs the dirty bitmap) +# Emitted from the source side of a migration at the start of each +# pass (when it syncs the dirty bitmap) # # @pass: An incrementing count (starting at 1 on the first pass) # @@ -1284,9 +1477,8 @@ # # Example: # -# { "timestamp": {"seconds": 1449669631, "microseconds": 239225}, -# "event": "MIGRATION_PASS", "data": {"pass": 2} } -# +# <- { "timestamp": {"seconds": 1449669631, "microseconds": 239225}, +# "event": "MIGRATION_PASS", "data": {"pass": 2} } ## { 'event': 'MIGRATION_PASS', 'data': { 'pass': 'int' } } @@ -1298,7 +1490,8 @@ # # @checkpoint-ready: Secondary VM (SVM) is ready for checkpointing # -# @checkpoint-request: Primary VM (PVM) tells SVM to prepare for checkpointing +# @checkpoint-request: Primary VM (PVM) tells SVM to prepare for +# checkpointing # # @checkpoint-reply: SVM gets PVM's checkpoint request # @@ -1346,7 +1539,8 @@ # # @completed: finish the process of failover # -# @relaunch: restart the failover process, from 'none' -> 'completed' (Since 2.9) +# @relaunch: restart the failover process, from 'none' -> 'completed' +# (Since 2.9) # # Since: 2.8 ## @@ -1367,9 +1561,8 @@ # # Example: # -# <- { "timestamp": {"seconds": 2032141960, "microseconds": 417172}, -# "event": "COLO_EXIT", "data": {"mode": "primary", "reason": "request" } } -# +# <- { "timestamp": {"seconds": 2032141960, "microseconds": 417172}, +# "event": "COLO_EXIT", "data": {"mode": "primary", "reason": "request" } } ## { 'event': 'COLO_EXIT', 'data': {'mode': 'COLOMode', 'reason': 'COLOExitReason' } } @@ -1379,9 +1572,9 @@ # # 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. +# @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. # # @request: COLO exit is due to an external request. # @@ -1397,41 +1590,41 @@ ## # @x-colo-lost-heartbeat: # -# Tell qemu that heartbeat is lost, request it to do takeover procedures. -# If this command is sent to the PVM, the Primary side will exit COLO mode. -# If sent to the Secondary, the Secondary side will run failover work, -# then takes over server operation to become the service VM. +# Tell qemu that heartbeat is lost, request it to do takeover +# procedures. If this command is sent to the PVM, the Primary side +# will exit COLO mode. If sent to the Secondary, the Secondary side +# will run failover work, then takes over server operation to become +# the service VM. # # Features: +# # @unstable: This command is experimental. # # Since: 2.8 # # Example: # -# -> { "execute": "x-colo-lost-heartbeat" } -# <- { "return": {} } -# +# -> { "execute": "x-colo-lost-heartbeat" } +# <- { "return": {} } ## { 'command': 'x-colo-lost-heartbeat', - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'if': 'CONFIG_REPLICATION' } ## # @migrate_cancel: # # Cancel the current executing migration process. # -# Returns: nothing on success -# -# Notes: This command succeeds even if there is no migration process running. +# Notes: This command succeeds even if there is no migration process +# running. # # Since: 0.14 # # Example: # -# -> { "execute": "migrate_cancel" } -# <- { "return": {} } -# +# -> { "execute": "migrate_cancel" } +# <- { "return": {} } ## { 'command': 'migrate_cancel' } @@ -1442,18 +1635,103 @@ # # @state: The state the migration is currently expected to be in # -# Returns: nothing on success -# # Since: 2.11 # # Example: # -# -> { "execute": "migrate-continue" , "arguments": -# { "state": "pre-switchover" } } -# <- { "return": {} } +# -> { "execute": "migrate-continue" , "arguments": +# { "state": "pre-switchover" } } +# <- { "return": {} } ## { 'command': 'migrate-continue', 'data': {'state': 'MigrationStatus'} } +## +# @MigrationAddressType: +# +# The migration stream transport mechanisms. +# +# @socket: Migrate via socket. +# +# @exec: Direct the migration stream to another process. +# +# @rdma: Migrate via RDMA. +# +# @file: Direct the migration stream to a file. +# +# Since: 8.2 +## +{ 'enum': 'MigrationAddressType', + 'data': [ 'socket', 'exec', 'rdma', 'file' ] } + +## +# @FileMigrationArgs: +# +# @filename: The file to receive the migration stream +# +# @offset: The file offset where the migration stream will start +# +# Since: 8.2 +## +{ 'struct': 'FileMigrationArgs', + 'data': { 'filename': 'str', + 'offset': 'uint64' } } + +## +# @MigrationExecCommand: +# +# @args: command (list head) and arguments to execute. +# +# Since: 8.2 +## +{ 'struct': 'MigrationExecCommand', + 'data': {'args': [ 'str' ] } } + +## +# @MigrationAddress: +# +# Migration endpoint configuration. +# +# @transport: The migration stream transport mechanism +# +# Since: 8.2 +## +{ 'union': 'MigrationAddress', + 'base': { 'transport' : 'MigrationAddressType'}, + 'discriminator': 'transport', + 'data': { + 'socket': 'SocketAddress', + 'exec': 'MigrationExecCommand', + 'rdma': 'InetSocketAddress', + 'file': 'FileMigrationArgs' } } + +## +# @MigrationChannelType: +# +# The migration channel-type request options. +# +# @main: Main outbound migration channel. +# +# Since: 8.1 +## +{ 'enum': 'MigrationChannelType', + 'data': [ 'main' ] } + +## +# @MigrationChannel: +# +# Migration stream channel parameters. +# +# @channel-type: Channel type for transferring packet information. +# +# @addr: Migration endpoint configuration on destination interface. +# +# Since: 8.1 +## +{ 'struct': 'MigrationChannel', + 'data': { + 'channel-type': 'MigrationChannelType', + 'addr': 'MigrationAddress' } } + ## # @migrate: # @@ -1461,95 +1739,178 @@ # # @uri: the Uniform Resource Identifier of the destination VM # +# @channels: list of migration stream channels with each stream in the +# list connected to a destination interface endpoint. +# # @blk: do block migration (full disk copy) # # @inc: incremental disk copy migration # -# @detach: this argument exists only for compatibility reasons and -# is ignored by QEMU +# @detach: this argument exists only for compatibility reasons and is +# ignored by QEMU # -# @resume: resume one paused migration, default "off". (since 3.0) +# @resume: resume one paused migration, default "off". (since 3.0) # -# Returns: nothing on success +# Features: +# +# @deprecated: Members @inc and @blk are deprecated. Use +# blockdev-mirror with NBD instead. # # Since: 0.14 # # Notes: # -# 1. The 'query-migrate' command should be used to check migration's progress -# and final result (this information is provided by the 'status' member) +# 1. The 'query-migrate' command should be used to check +# migration's progress and final result (this information is +# provided by the 'status' member) # -# 2. All boolean arguments default to false +# 2. All boolean arguments default to false # -# 3. The user Monitor's "detach" argument is invalid in QMP and should not -# be used +# 3. The user Monitor's "detach" argument is invalid in QMP and +# should not be used +# +# 4. The uri argument should have the Uniform Resource Identifier +# of default destination VM. This connection will be bound to +# default network. +# +# 5. For now, number of migration streams is restricted to one, +# i.e. number of items in 'channels' list is just 1. +# +# 6. The 'uri' and 'channels' arguments are mutually exclusive; +# exactly one of the two should be present. # # Example: # -# -> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } } -# <- { "return": {} } +# -> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } } +# <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "socket", +# "type": "inet", +# "host": "10.12.34.9", +# "port": "1050" } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "exec", +# "args": [ "/bin/nc", "-p", "6000", +# "/some/sock" ] } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "rdma", +# "host": "10.12.34.9", +# "port": "1050" } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "file", +# "filename": "/tmp/migfile", +# "offset": "0x1000" } } ] } } +# <- { "return": {} } # ## { 'command': 'migrate', - 'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', + 'data': {'*uri': 'str', + '*channels': [ 'MigrationChannel' ], + '*blk': { 'type': 'bool', 'features': [ 'deprecated' ] }, + '*inc': { 'type': 'bool', 'features': [ 'deprecated' ] }, '*detach': 'bool', '*resume': 'bool' } } ## # @migrate-incoming: # -# Start an incoming migration, the qemu must have been started -# with -incoming defer +# Start an incoming migration, the qemu must have been started with +# -incoming defer # # @uri: The Uniform Resource Identifier identifying the source or -# address to listen on +# address to listen on # -# Returns: nothing on success +# @channels: list of migration stream channels with each stream in the +# list connected to a destination interface endpoint. # # Since: 2.3 # # Notes: # -# 1. It's a bad idea to use a string for the uri, but it needs to stay -# compatible with -incoming and the format of the uri is already exposed -# above libvirt. +# 1. It's a bad idea to use a string for the uri, but it needs to +# stay compatible with -incoming and the format of the uri is +# already exposed above libvirt. # -# 2. QEMU must be started with -incoming defer to allow migrate-incoming to -# be used. +# 2. QEMU must be started with -incoming defer to allow +# migrate-incoming to be used. # -# 3. The uri format is the same as for -incoming +# 3. The uri format is the same as for -incoming +# +# 4. For now, number of migration streams is restricted to one, +# i.e. number of items in 'channels' list is just 1. +# +# 5. The 'uri' and 'channels' arguments are mutually exclusive; +# exactly one of the two should be present. # # Example: # -# -> { "execute": "migrate-incoming", -# "arguments": { "uri": "tcp::4446" } } -# <- { "return": {} } +# -> { "execute": "migrate-incoming", +# "arguments": { "uri": "tcp:0:4446" } } +# <- { "return": {} } # +# -> { "execute": "migrate-incoming", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "socket", +# "type": "inet", +# "host": "10.12.34.9", +# "port": "1050" } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate-incoming", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "exec", +# "args": [ "/bin/nc", "-p", "6000", +# "/some/sock" ] } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate-incoming", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "rdma", +# "host": "10.12.34.9", +# "port": "1050" } } ] } } +# <- { "return": {} } ## -{ 'command': 'migrate-incoming', 'data': {'uri': 'str' } } +{ 'command': 'migrate-incoming', + 'data': {'*uri': 'str', + '*channels': [ 'MigrationChannel' ] } } ## # @xen-save-devices-state: # -# Save the state of all devices to file. The RAM and the block devices -# of the VM are not saved by this command. +# Save the state of all devices to file. The RAM and the block +# devices 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) -# -# Returns: Nothing on success +# @live: Optional argument to ask QEMU to treat this command as part +# of a live migration. Default to true. (since 2.11) # # Since: 1.1 # # Example: # -# -> { "execute": "xen-save-devices-state", -# "arguments": { "filename": "/tmp/save" } } -# <- { "return": {} } -# +# -> { "execute": "xen-save-devices-state", +# "arguments": { "filename": "/tmp/save" } } +# <- { "return": {} } ## { 'command': 'xen-save-devices-state', 'data': {'filename': 'str', '*live':'bool' } } @@ -1561,37 +1922,33 @@ # # @enable: true to enable, false to disable. # -# Returns: nothing -# # Since: 1.3 # # Example: # -# -> { "execute": "xen-set-global-dirty-log", -# "arguments": { "enable": true } } -# <- { "return": {} } -# +# -> { "execute": "xen-set-global-dirty-log", +# "arguments": { "enable": true } } +# <- { "return": {} } ## { 'command': 'xen-set-global-dirty-log', 'data': { 'enable': 'bool' } } ## # @xen-load-devices-state: # -# Load the state of all devices from file. The RAM and the block devices -# of the VM are not loaded by this command. +# Load the state of all devices from file. The RAM and the block +# devices 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 # # Example: # -# -> { "execute": "xen-load-devices-state", -# "arguments": { "filename": "/tmp/resume" } } -# <- { "return": {} } -# +# -> { "execute": "xen-load-devices-state", +# "arguments": { "filename": "/tmp/resume" } } +# <- { "return": {} } ## { 'command': 'xen-load-devices-state', 'data': {'filename': 'str'} } @@ -1604,21 +1961,19 @@ # # @primary: true for primary or false for secondary. # -# @failover: true to do failover, false to stop. but cannot be -# specified if 'enable' is true. default value is false. -# -# Returns: nothing. +# @failover: true to do failover, false to stop. Cannot be specified +# if 'enable' is true. Default value is false. # # Example: # -# -> { "execute": "xen-set-replication", -# "arguments": {"enable": true, "primary": false} } -# <- { "return": {} } +# -> { "execute": "xen-set-replication", +# "arguments": {"enable": true, "primary": false} } +# <- { "return": {} } # # Since: 2.9 ## { 'command': 'xen-set-replication', - 'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' }, + 'data': { 'enable': 'bool', 'primary': 'bool', '*failover': 'bool' }, 'if': 'CONFIG_REPLICATION' } ## @@ -1628,8 +1983,8 @@ # # @error: true if an error happened, false if replication is normal. # -# @desc: the human readable error description string, when -# @error is 'true'. +# @desc: the human readable error description string, when @error is +# 'true'. # # Since: 2.9 ## @@ -1646,8 +2001,8 @@ # # Example: # -# -> { "execute": "query-xen-replication-status" } -# <- { "return": { "error": false } } +# -> { "execute": "query-xen-replication-status" } +# <- { "return": { "error": false } } # # Since: 2.9 ## @@ -1660,12 +2015,10 @@ # # Xen uses this command to notify replication to trigger a checkpoint. # -# Returns: nothing. -# # Example: # -# -> { "execute": "xen-colo-do-checkpoint" } -# <- { "return": {} } +# -> { "execute": "xen-colo-do-checkpoint" } +# <- { "return": {} } # # Since: 2.9 ## @@ -1677,12 +2030,12 @@ # # The result format for 'query-colo-status'. # -# @mode: COLO running mode. If COLO is running, this field will return -# 'primary' or 'secondary'. +# @mode: COLO running mode. If COLO is running, this field will +# return 'primary' or 'secondary'. # -# @last-mode: COLO last running mode. If COLO is running, this field -# will return same like mode field, after failover we can -# use this field to get last colo mode. (since 4.0) +# @last-mode: COLO last running mode. If COLO is running, this field +# will return same like mode field, after failover we can use this +# field to get last colo mode. (since 4.0) # # @reason: describes the reason for the COLO exit. # @@ -1690,7 +2043,8 @@ ## { 'struct': 'COLOStatus', 'data': { 'mode': 'COLOMode', 'last-mode': 'COLOMode', - 'reason': 'COLOExitReason' } } + 'reason': 'COLOExitReason' }, + 'if': 'CONFIG_REPLICATION' } ## # @query-colo-status: @@ -1701,13 +2055,14 @@ # # Example: # -# -> { "execute": "query-colo-status" } -# <- { "return": { "mode": "primary", "last-mode": "none", "reason": "request" } } +# -> { "execute": "query-colo-status" } +# <- { "return": { "mode": "primary", "last-mode": "none", "reason": "request" } } # # Since: 3.1 ## { 'command': 'query-colo-status', - 'returns': 'COLOStatus' } + 'returns': 'COLOStatus', + 'if': 'CONFIG_REPLICATION' } ## # @migrate-recover: @@ -1716,13 +2071,11 @@ # # @uri: the URI to be used for the recovery of migration stream. # -# Returns: nothing. -# # Example: # -# -> { "execute": "migrate-recover", -# "arguments": { "uri": "tcp:192.168.1.200:12345" } } -# <- { "return": {} } +# -> { "execute": "migrate-recover", +# "arguments": { "uri": "tcp:192.168.1.200:12345" } } +# <- { "return": {} } # # Since: 3.0 ## @@ -1735,12 +2088,10 @@ # # Pause a migration. Currently it only supports postcopy. # -# Returns: nothing. -# # Example: # -# -> { "execute": "migrate-pause" } -# <- { "return": {} } +# -> { "execute": "migrate-pause" } +# <- { "return": {} } # # Since: 3.0 ## @@ -1750,9 +2101,9 @@ # @UNPLUG_PRIMARY: # # Emitted from source side of a migration when migration state is -# WAIT_UNPLUG. Device was unplugged by guest operating system. -# Device resources in QEMU are kept on standby to be able to re-plug it in case -# of migration failure. +# WAIT_UNPLUG. Device was unplugged by guest operating system. Device +# resources in QEMU are kept on standby to be able to re-plug it in +# case of migration failure. # # @device-id: QEMU device id of the unplugged device # @@ -1760,10 +2111,9 @@ # # Example: # -# <- { "event": "UNPLUG_PRIMARY", -# "data": { "device-id": "hostdev0" }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } -# +# <- { "event": "UNPLUG_PRIMARY", +# "data": { "device-id": "hostdev0" }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'UNPLUG_PRIMARY', 'data': { 'device-id': 'str' } } @@ -1785,13 +2135,13 @@ ## # @DirtyRateStatus: # -# An enumeration of dirtyrate status. +# Dirty page rate measurement status. # -# @unstarted: the dirtyrate thread has not been started. +# @unstarted: measuring thread has not been started yet # -# @measuring: the dirtyrate thread is measuring. +# @measuring: measuring thread is running # -# @measured: the dirtyrate thread has measured and results are available. +# @measured: dirty page rate is measured and the results are available # # Since: 5.2 ## @@ -1801,42 +2151,58 @@ ## # @DirtyRateMeasureMode: # -# An enumeration of mode of measuring dirtyrate. +# Method used to measure dirty page rate. Differences between +# available methods are explained in @calc-dirty-rate. # -# @page-sampling: calculate dirtyrate by sampling pages. +# @page-sampling: use page sampling # -# @dirty-ring: calculate dirtyrate by dirty ring. +# @dirty-ring: use dirty ring # -# @dirty-bitmap: calculate dirtyrate by dirty bitmap. +# @dirty-bitmap: use dirty bitmap # # Since: 6.2 ## { 'enum': 'DirtyRateMeasureMode', 'data': ['page-sampling', 'dirty-ring', 'dirty-bitmap'] } +## +# @TimeUnit: +# +# Specifies unit in which time-related value is specified. +# +# @second: value is in seconds +# +# @millisecond: value is in milliseconds +# +# Since: 8.2 +## +{ 'enum': 'TimeUnit', + 'data': ['second', 'millisecond'] } + ## # @DirtyRateInfo: # -# Information about current dirty page rate of vm. +# Information about measured dirty page rate. # -# @dirty-rate: an estimate of the dirty page rate of the VM in units of -# MB/s, present only when estimating the rate has completed. +# @dirty-rate: an estimate of the dirty page rate of the VM in units +# of MiB/s. Value is present only when @status is 'measured'. # -# @status: status containing dirtyrate query status includes -# 'unstarted' or 'measuring' or 'measured' +# @status: current status of dirty page rate measurements # # @start-time: start time in units of second for calculation # -# @calc-time: time in units of second for sample dirty pages +# @calc-time: time period for which dirty page rate was measured, +# expressed and rounded down to @calc-time-unit. # -# @sample-pages: page count per GB for sample dirty pages -# the default value is 512 (since 6.1) +# @calc-time-unit: time unit of @calc-time (Since 8.2) # -# @mode: mode containing method of calculate dirtyrate includes -# 'page-sampling' and 'dirty-ring' (Since 6.2) +# @sample-pages: number of sampled pages per GiB of guest memory. +# Valid only in page-sampling mode (Since 6.1) # -# @vcpu-dirty-rate: dirtyrate for each vcpu if dirty-ring -# mode specified (Since 6.2) +# @mode: mode that was used to measure dirty page rate (Since 6.2) +# +# @vcpu-dirty-rate: dirty rate for each vCPU if dirty-ring mode was +# specified (Since 6.2) # # Since: 5.2 ## @@ -1845,6 +2211,7 @@ 'status': 'DirtyRateStatus', 'start-time': 'int64', 'calc-time': 'int64', + 'calc-time-unit': 'TimeUnit', 'sample-pages': 'uint64', 'mode': 'DirtyRateMeasureMode', '*vcpu-dirty-rate': [ 'DirtyRateVcpu' ] } } @@ -1852,36 +2219,212 @@ ## # @calc-dirty-rate: # -# start calculating dirty page rate for vm +# Start measuring dirty page rate of the VM. Results can be retrieved +# with @query-dirty-rate after measurements are completed. # -# @calc-time: time in units of second for sample dirty pages +# Dirty page rate is the number of pages changed in a given time +# period expressed in MiB/s. The following methods of calculation are +# available: # -# @sample-pages: page count per GB for sample dirty pages -# the default value is 512 (since 6.1) +# 1. In page sampling mode, a random subset of pages are selected and +# hashed twice: once at the beginning of measurement time period, +# and once again at the end. If two hashes for some page are +# different, the page is counted as changed. Since this method +# relies on sampling and hashing, calculated dirty page rate is +# only an estimate of its true value. Increasing @sample-pages +# improves estimation quality at the cost of higher computational +# overhead. # -# @mode: mechanism of calculating dirtyrate includes -# 'page-sampling' and 'dirty-ring' (Since 6.1) +# 2. Dirty bitmap mode captures writes to memory (for example by +# temporarily revoking write access to all pages) and counting page +# faults. Information about modified pages is collected into a +# bitmap, where each bit corresponds to one guest page. This mode +# requires that KVM accelerator property "dirty-ring-size" is *not* +# set. +# +# 3. Dirty ring mode is similar to dirty bitmap mode, but the +# information about modified pages is collected into ring buffer. +# This mode tracks page modification per each vCPU separately. It +# requires that KVM accelerator property "dirty-ring-size" is set. +# +# @calc-time: time period for which dirty page rate is calculated. +# By default it is specified in seconds, but the unit can be set +# explicitly with @calc-time-unit. Note that larger @calc-time +# values will typically result in smaller dirty page rates because +# page dirtying is a one-time event. Once some page is counted +# as dirty during @calc-time period, further writes to this page +# will not increase dirty page rate anymore. +# +# @calc-time-unit: time unit in which @calc-time is specified. +# By default it is seconds. (Since 8.2) +# +# @sample-pages: number of sampled pages per each GiB of guest memory. +# Default value is 512. For 4KiB guest pages this corresponds to +# sampling ratio of 0.2%. This argument is used only in page +# sampling mode. (Since 6.1) +# +# @mode: mechanism for tracking dirty pages. Default value is +# 'page-sampling'. Others are 'dirty-bitmap' and 'dirty-ring'. +# (Since 6.1) # # Since: 5.2 # # Example: # -# {"execute": "calc-dirty-rate", "arguments": {"calc-time": 1, -# 'sample-pages': 512} } +# -> {"execute": "calc-dirty-rate", "arguments": {"calc-time": 1, +# 'sample-pages': 512} } +# <- { "return": {} } # +# Measure dirty rate using dirty bitmap for 500 milliseconds: +# +# -> {"execute": "calc-dirty-rate", "arguments": {"calc-time": 500, +# "calc-time-unit": "millisecond", "mode": "dirty-bitmap"} } +# +# <- { "return": {} } ## { 'command': 'calc-dirty-rate', 'data': {'calc-time': 'int64', + '*calc-time-unit': 'TimeUnit', '*sample-pages': 'int', '*mode': 'DirtyRateMeasureMode'} } ## # @query-dirty-rate: # -# query dirty page rate in units of MB/s for vm +# Query results of the most recent invocation of @calc-dirty-rate. +# +# @calc-time-unit: time unit in which to report calculation time. +# By default it is reported in seconds. (Since 8.2) # # Since: 5.2 +# +# Examples: +# +# 1. Measurement is in progress: +# +# <- {"status": "measuring", "sample-pages": 512, +# "mode": "page-sampling", "start-time": 1693900454, "calc-time": 10, +# "calc-time-unit": "second"} +# +# 2. Measurement has been completed: +# +# <- {"status": "measured", "sample-pages": 512, "dirty-rate": 108, +# "mode": "page-sampling", "start-time": 1693900454, "calc-time": 10, +# "calc-time-unit": "second"} ## -{ 'command': 'query-dirty-rate', 'returns': 'DirtyRateInfo' } +{ 'command': 'query-dirty-rate', 'data': {'*calc-time-unit': 'TimeUnit' }, + 'returns': 'DirtyRateInfo' } + +## +# @DirtyLimitInfo: +# +# Dirty page rate limit information of a virtual CPU. +# +# @cpu-index: index of a virtual CPU. +# +# @limit-rate: upper limit of dirty page rate (MB/s) for a virtual +# CPU, 0 means unlimited. +# +# @current-rate: current dirty page rate (MB/s) for a virtual CPU. +# +# Since: 7.1 +## +{ 'struct': 'DirtyLimitInfo', + 'data': { 'cpu-index': 'int', + 'limit-rate': 'uint64', + 'current-rate': 'uint64' } } + +## +# @set-vcpu-dirty-limit: +# +# Set the upper limit of dirty page rate for virtual CPUs. +# +# Requires KVM with accelerator property "dirty-ring-size" set. A +# virtual CPU's dirty page rate is a measure of its memory load. To +# observe dirty page rates, use @calc-dirty-rate. +# +# @cpu-index: index of a virtual CPU, default is all. +# +# @dirty-rate: upper limit of dirty page rate (MB/s) for virtual CPUs. +# +# Since: 7.1 +# +# Example: +# +# -> {"execute": "set-vcpu-dirty-limit"} +# "arguments": { "dirty-rate": 200, +# "cpu-index": 1 } } +# <- { "return": {} } +## +{ 'command': 'set-vcpu-dirty-limit', + 'data': { '*cpu-index': 'int', + 'dirty-rate': 'uint64' } } + +## +# @cancel-vcpu-dirty-limit: +# +# Cancel the upper limit of dirty page rate for virtual CPUs. +# +# Cancel the dirty page limit for the vCPU which has been set with +# set-vcpu-dirty-limit command. Note that this command requires +# support from dirty ring, same as the "set-vcpu-dirty-limit". +# +# @cpu-index: index of a virtual CPU, default is all. +# +# Since: 7.1 +# +# Example: +# +# -> {"execute": "cancel-vcpu-dirty-limit"}, +# "arguments": { "cpu-index": 1 } } +# <- { "return": {} } +## +{ 'command': 'cancel-vcpu-dirty-limit', + 'data': { '*cpu-index': 'int'} } + +## +# @query-vcpu-dirty-limit: +# +# Returns information about virtual CPU dirty page rate limits, if +# any. +# +# Since: 7.1 +# +# Example: +# +# -> {"execute": "query-vcpu-dirty-limit"} +# <- {"return": [ +# { "limit-rate": 60, "current-rate": 3, "cpu-index": 0}, +# { "limit-rate": 60, "current-rate": 3, "cpu-index": 1}]} +## +{ 'command': 'query-vcpu-dirty-limit', + 'returns': [ 'DirtyLimitInfo' ] } + +## +# @MigrationThreadInfo: +# +# Information about migrationthreads +# +# @name: the name of migration thread +# +# @thread-id: ID of the underlying host thread +# +# Since: 7.2 +## +{ 'struct': 'MigrationThreadInfo', + 'data': {'name': 'str', + 'thread-id': 'int'} } + +## +# @query-migrationthreads: +# +# Returns information of migration threads +# +# Returns: @MigrationThreadInfo +# +# Since: 7.2 +## +{ 'command': 'query-migrationthreads', + 'returns': ['MigrationThreadInfo'] } ## # @snapshot-save: @@ -1889,54 +2432,63 @@ # Save a VM snapshot # # @job-id: identifier for the newly created job +# # @tag: name of the snapshot to create +# # @vmstate: block device node name to save vmstate to +# # @devices: list of block device node names to save a snapshot to # # Applications should not assume that the snapshot save is complete -# when this command returns. The job commands / events must be used -# to determine completion and to fetch details of any errors that arise. +# when this command returns. The job commands / events must be used +# to determine completion and to fetch details of any errors that +# arise. # -# Note that execution of the guest CPUs may be stopped during the -# time it takes to save the snapshot. A future version of QEMU -# may ensure CPUs are executing continuously. +# Note that execution of the guest CPUs may be stopped during the time +# it takes to save the snapshot. A future version of QEMU may ensure +# CPUs are executing continuously. # -# It is strongly recommended that @devices contain all writable -# block device nodes if a consistent snapshot is required. +# It is strongly recommended that @devices contain all writable block +# device nodes if a consistent snapshot is required. # # If @tag already exists, an error will be reported # -# Returns: nothing -# # Example: # -# -> { "execute": "snapshot-save", -# "arguments": { -# "job-id": "snapsave0", -# "tag": "my-snap", -# "vmstate": "disk0", -# "devices": ["disk0", "disk1"] -# } -# } -# <- { "return": { } } -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "created", "id": "snapsave0"}} -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "running", "id": "snapsave0"}} -# <- {"event": "STOP"} -# <- {"event": "RESUME"} -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "waiting", "id": "snapsave0"}} -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "pending", "id": "snapsave0"}} -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "concluded", "id": "snapsave0"}} -# -> {"execute": "query-jobs"} -# <- {"return": [{"current-progress": 1, -# "status": "concluded", -# "total-progress": 1, -# "type": "snapshot-save", -# "id": "snapsave0"}]} +# -> { "execute": "snapshot-save", +# "arguments": { +# "job-id": "snapsave0", +# "tag": "my-snap", +# "vmstate": "disk0", +# "devices": ["disk0", "disk1"] +# } +# } +# <- { "return": { } } +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1432121972, "microseconds": 744001}, +# "data": {"status": "created", "id": "snapsave0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1432122172, "microseconds": 744001}, +# "data": {"status": "running", "id": "snapsave0"}} +# <- {"event": "STOP", +# "timestamp": {"seconds": 1432122372, "microseconds": 744001} } +# <- {"event": "RESUME", +# "timestamp": {"seconds": 1432122572, "microseconds": 744001} } +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1432122772, "microseconds": 744001}, +# "data": {"status": "waiting", "id": "snapsave0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1432122972, "microseconds": 744001}, +# "data": {"status": "pending", "id": "snapsave0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1432123172, "microseconds": 744001}, +# "data": {"status": "concluded", "id": "snapsave0"}} +# -> {"execute": "query-jobs"} +# <- {"return": [{"current-progress": 1, +# "status": "concluded", +# "total-progress": 1, +# "type": "snapshot-save", +# "id": "snapsave0"}]} # # Since: 6.0 ## @@ -1952,52 +2504,61 @@ # Load a VM snapshot # # @job-id: identifier for the newly created job +# # @tag: name of the snapshot to load. +# # @vmstate: block device node name to load vmstate from +# # @devices: list of block device node names to load a snapshot from # # Applications should not assume that the snapshot load is complete -# when this command returns. The job commands / events must be used -# to determine completion and to fetch details of any errors that arise. +# when this command returns. The job commands / events must be used +# to determine completion and to fetch details of any errors that +# arise. # # Note that execution of the guest CPUs will be stopped during the # time it takes to load the snapshot. # -# It is strongly recommended that @devices contain all writable -# block device nodes that can have changed since the original -# @snapshot-save command execution. -# -# Returns: nothing +# It is strongly recommended that @devices contain all writable block +# device nodes that can have changed since the original @snapshot-save +# command execution. # # Example: # -# -> { "execute": "snapshot-load", -# "arguments": { -# "job-id": "snapload0", -# "tag": "my-snap", -# "vmstate": "disk0", -# "devices": ["disk0", "disk1"] -# } -# } -# <- { "return": { } } -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "created", "id": "snapload0"}} -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "running", "id": "snapload0"}} -# <- {"event": "STOP"} -# <- {"event": "RESUME"} -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "waiting", "id": "snapload0"}} -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "pending", "id": "snapload0"}} -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "concluded", "id": "snapload0"}} -# -> {"execute": "query-jobs"} -# <- {"return": [{"current-progress": 1, -# "status": "concluded", -# "total-progress": 1, -# "type": "snapshot-load", -# "id": "snapload0"}]} +# -> { "execute": "snapshot-load", +# "arguments": { +# "job-id": "snapload0", +# "tag": "my-snap", +# "vmstate": "disk0", +# "devices": ["disk0", "disk1"] +# } +# } +# <- { "return": { } } +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1472124172, "microseconds": 744001}, +# "data": {"status": "created", "id": "snapload0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1472125172, "microseconds": 744001}, +# "data": {"status": "running", "id": "snapload0"}} +# <- {"event": "STOP", +# "timestamp": {"seconds": 1472125472, "microseconds": 744001} } +# <- {"event": "RESUME", +# "timestamp": {"seconds": 1472125872, "microseconds": 744001} } +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1472126172, "microseconds": 744001}, +# "data": {"status": "waiting", "id": "snapload0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1472127172, "microseconds": 744001}, +# "data": {"status": "pending", "id": "snapload0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1472128172, "microseconds": 744001}, +# "data": {"status": "concluded", "id": "snapload0"}} +# -> {"execute": "query-jobs"} +# <- {"return": [{"current-progress": 1, +# "status": "concluded", +# "total-progress": 1, +# "type": "snapshot-load", +# "id": "snapload0"}]} # # Since: 6.0 ## @@ -2013,41 +2574,47 @@ # Delete a VM snapshot # # @job-id: identifier for the newly created job +# # @tag: name of the snapshot to delete. +# # @devices: list of block device node names to delete a snapshot from # # Applications should not assume that the snapshot delete is complete -# when this command returns. The job commands / events must be used -# to determine completion and to fetch details of any errors that arise. -# -# Returns: nothing +# when this command returns. The job commands / events must be used +# to determine completion and to fetch details of any errors that +# arise. # # Example: # -# -> { "execute": "snapshot-delete", -# "arguments": { -# "job-id": "snapdelete0", -# "tag": "my-snap", -# "devices": ["disk0", "disk1"] -# } -# } -# <- { "return": { } } -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "created", "id": "snapdelete0"}} -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "running", "id": "snapdelete0"}} -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "waiting", "id": "snapdelete0"}} -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "pending", "id": "snapdelete0"}} -# <- {"event": "JOB_STATUS_CHANGE", -# "data": {"status": "concluded", "id": "snapdelete0"}} -# -> {"execute": "query-jobs"} -# <- {"return": [{"current-progress": 1, -# "status": "concluded", -# "total-progress": 1, -# "type": "snapshot-delete", -# "id": "snapdelete0"}]} +# -> { "execute": "snapshot-delete", +# "arguments": { +# "job-id": "snapdelete0", +# "tag": "my-snap", +# "devices": ["disk0", "disk1"] +# } +# } +# <- { "return": { } } +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1442124172, "microseconds": 744001}, +# "data": {"status": "created", "id": "snapdelete0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1442125172, "microseconds": 744001}, +# "data": {"status": "running", "id": "snapdelete0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1442126172, "microseconds": 744001}, +# "data": {"status": "waiting", "id": "snapdelete0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1442127172, "microseconds": 744001}, +# "data": {"status": "pending", "id": "snapdelete0"}} +# <- {"event": "JOB_STATUS_CHANGE", +# "timestamp": {"seconds": 1442128172, "microseconds": 744001}, +# "data": {"status": "concluded", "id": "snapdelete0"}} +# -> {"execute": "query-jobs"} +# <- {"return": [{"current-progress": 1, +# "status": "concluded", +# "total-progress": 1, +# "type": "snapshot-delete", +# "id": "snapdelete0"}]} # # Since: 6.0 ## diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 4944c0528f..4e0a6492a9 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -5,18 +5,16 @@ ## # @rtc-reset-reinjection: # -# This command will reset the RTC interrupt reinjection backlog. -# Can be used if another mechanism to synchronize guest time -# is in effect, for example QEMU guest agent's guest-set-time -# command. +# This command will reset the RTC interrupt reinjection backlog. Can +# be used if another mechanism to synchronize guest time is in effect, +# for example QEMU guest agent's guest-set-time command. # # Since: 2.1 # # Example: # -# -> { "execute": "rtc-reset-reinjection" } -# <- { "return": {} } -# +# -> { "execute": "rtc-reset-reinjection" } +# <- { "return": {} } ## { 'command': 'rtc-reset-reinjection', 'if': 'TARGET_I386' } @@ -28,17 +26,19 @@ # # @uninit: The guest is uninitialized. # -# @launch-update: The guest is currently being launched; plaintext data and -# register state is being imported. +# @launch-update: The guest is currently being launched; plaintext +# data and register state is being imported. # -# @launch-secret: The guest is currently being launched; ciphertext data -# is being imported. +# @launch-secret: The guest is currently being launched; ciphertext +# data is being imported. # # @running: The guest is fully launched or migrated in. # -# @send-update: The guest is currently being migrated out to another machine. +# @send-update: The guest is currently being migrated out to another +# machine. # -# @receive-update: The guest is currently being migrated from another machine. +# @receive-update: The guest is currently being migrated from another +# machine. # # Since: 2.12 ## @@ -91,11 +91,10 @@ # # Example: # -# -> { "execute": "query-sev" } -# <- { "return": { "enabled": true, "api-major" : 0, "api-minor" : 0, -# "build-id" : 0, "policy" : 0, "state" : "running", -# "handle" : 1 } } -# +# -> { "execute": "query-sev" } +# <- { "return": { "enabled": true, "api-major" : 0, "api-minor" : 0, +# "build-id" : 0, "policy" : 0, "state" : "running", +# "handle" : 1 } } ## { 'command': 'query-sev', 'returns': 'SevInfo', 'if': 'TARGET_I386' } @@ -123,9 +122,8 @@ # # Example: # -# -> { "execute": "query-sev-launch-measure" } -# <- { "return": { "data": "4l8LXeNlSPUDlXPJG5966/8%YZ" } } -# +# -> { "execute": "query-sev-launch-measure" } +# <- { "return": { "data": "4l8LXeNlSPUDlXPJG5966/8%YZ" } } ## { 'command': 'query-sev-launch-measure', 'returns': 'SevLaunchMeasureInfo', 'if': 'TARGET_I386' } @@ -133,8 +131,8 @@ ## # @SevCapability: # -# The struct describes capability for a Secure Encrypted Virtualization -# feature. +# The struct describes capability for a Secure Encrypted +# Virtualization feature. # # @pdh: Platform Diffie-Hellman key (base64 encoded) # @@ -144,8 +142,8 @@ # # @cbitpos: C-bit location in page table entry # -# @reduced-phys-bits: Number of physical Address bit reduction when SEV is -# enabled +# @reduced-phys-bits: Number of physical Address bit reduction when +# SEV is enabled # # Since: 2.12 ## @@ -160,8 +158,8 @@ ## # @query-sev-capabilities: # -# This command is used to get the SEV capabilities, and is supported on AMD -# X86 platforms only. +# This command is used to get the SEV capabilities, and is supported +# on AMD X86 platforms only. # # Returns: SevCapability objects. # @@ -169,11 +167,10 @@ # # Example: # -# -> { "execute": "query-sev-capabilities" } -# <- { "return": { "pdh": "8CCDD8DDD", "cert-chain": "888CCCDDDEE", -# "cpu0-id": "2lvmGwo+...61iEinw==", -# "cbitpos": 47, "reduced-phys-bits": 5}} -# +# -> { "execute": "query-sev-capabilities" } +# <- { "return": { "pdh": "8CCDD8DDD", "cert-chain": "888CCCDDDEE", +# "cpu0-id": "2lvmGwo+...61iEinw==", +# "cbitpos": 47, "reduced-phys-bits": 1}} ## { 'command': 'query-sev-capabilities', 'returns': 'SevCapability', 'if': 'TARGET_I386' } @@ -216,7 +213,7 @@ # supported on AMD X86 platforms only. # # @mnonce: a random 16 bytes value encoded in base64 (it will be -# included in report) +# included in report) # # Returns: SevAttestationReport objects. # @@ -224,10 +221,9 @@ # # Example: # -# -> { "execute" : "query-sev-attestation-report", -# "arguments": { "mnonce": "aaaaaaa" } } -# <- { "return" : { "data": "aaaaaaaabbbddddd"} } -# +# -> { "execute" : "query-sev-attestation-report", +# "arguments": { "mnonce": "aaaaaaa" } } +# <- { "return" : { "data": "aaaaaaaabbbddddd"} } ## { 'command': 'query-sev-attestation-report', 'data': { 'mnonce': 'str' }, @@ -241,16 +237,13 @@ # # @filename: the path to the file to dump to # -# This command is only supported on s390 architecture. -# # Since: 2.5 # # Example: # -# -> { "execute": "dump-skeys", -# "arguments": { "filename": "/tmp/skeys" } } -# <- { "return": {} } -# +# -> { "execute": "dump-skeys", +# "arguments": { "filename": "/tmp/skeys" } } +# <- { "return": {} } ## { 'command': 'dump-skeys', 'data': { 'filename': 'str' }, @@ -260,18 +253,18 @@ # @GICCapability: # # The struct describes capability for a specific GIC (Generic -# Interrupt Controller) version. These bits are not only decided by -# QEMU/KVM software version, but also decided by the hardware that -# the program is running upon. +# Interrupt Controller) version. These bits are not only decided by +# 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. +# 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 ## @@ -284,7 +277,7 @@ ## # @query-gic-capabilities: # -# This command is ARM-only. It will return a list of GICCapability +# This command is ARM-only. It will return a list of GICCapability # objects that describe its capability bits. # # Returns: a list of GICCapability objects. @@ -293,10 +286,9 @@ # # Example: # -# -> { "execute": "query-gic-capabilities" } -# <- { "return": [{ "version": 2, "emulated": true, "kernel": false }, -# { "version": 3, "emulated": false, "kernel": true } ] } -# +# -> { "execute": "query-gic-capabilities" } +# <- { "return": [{ "version": 2, "emulated": true, "kernel": false }, +# { "version": 3, "emulated": false, "kernel": true } ] } ## { 'command': 'query-gic-capabilities', 'returns': ['GICCapability'], 'if': 'TARGET_ARM' } @@ -329,14 +321,8 @@ # # @flc: true if FLC is supported # -# @section-size: The EPC section size for guest -# Redundant with @sections. Just for backward compatibility. -# # @sections: The EPC sections info for guest (Since: 7.0) # -# Features: -# @deprecated: Member @section-size is deprecated. Use @sections instead. -# # Since: 6.2 ## { 'struct': 'SGXInfo', @@ -344,8 +330,6 @@ 'sgx1': 'bool', 'sgx2': 'bool', 'flc': 'bool', - 'section-size': { 'type': 'uint64', - 'features': [ 'deprecated' ] }, 'sections': ['SGXEPCSection']}, 'if': 'TARGET_I386' } @@ -360,12 +344,11 @@ # # Example: # -# -> { "execute": "query-sgx" } -# <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, -# "flc": true, "section-size" : 96468992, -# "sections": [{"node": 0, "size": 67108864}, -# {"node": 1, "size": 29360128}]} } -# +# -> { "execute": "query-sgx" } +# <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, +# "flc": true, +# "sections": [{"node": 0, "size": 67108864}, +# {"node": 1, "size": 29360128}]} } ## { 'command': 'query-sgx', 'returns': 'SGXInfo', 'if': 'TARGET_I386' } @@ -380,11 +363,122 @@ # # Example: # -# -> { "execute": "query-sgx-capabilities" } -# <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, -# "flc": true, "section-size" : 96468992, -# "section" : [{"node": 0, "size": 67108864}, -# {"node": 1, "size": 29360128}]} } -# +# -> { "execute": "query-sgx-capabilities" } +# <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, +# "flc": true, +# "section" : [{"node": 0, "size": 67108864}, +# {"node": 1, "size": 29360128}]} } ## { 'command': 'query-sgx-capabilities', 'returns': 'SGXInfo', 'if': 'TARGET_I386' } + + +## +# @EvtchnPortType: +# +# An enumeration of Xen event channel port types. +# +# @closed: The port is unused. +# +# @unbound: The port is allocated and ready to be bound. +# +# @interdomain: The port is connected as an interdomain interrupt. +# +# @pirq: The port is bound to a physical IRQ (PIRQ). +# +# @virq: The port is bound to a virtual IRQ (VIRQ). +# +# @ipi: The post is an inter-processor interrupt (IPI). +# +# Since: 8.0 +## +{ 'enum': 'EvtchnPortType', + 'data': ['closed', 'unbound', 'interdomain', 'pirq', 'virq', 'ipi'], + 'if': 'TARGET_I386' } + +## +# @EvtchnInfo: +# +# Information about a Xen event channel port +# +# @port: the port number +# +# @vcpu: target vCPU for this port +# +# @type: the port type +# +# @remote-domain: remote domain for interdomain ports +# +# @target: remote port ID, or virq/pirq number +# +# @pending: port is currently active pending delivery +# +# @masked: port is masked +# +# Since: 8.0 +## +{ 'struct': 'EvtchnInfo', + 'data': {'port': 'uint16', + 'vcpu': 'uint32', + 'type': 'EvtchnPortType', + 'remote-domain': 'str', + 'target': 'uint16', + 'pending': 'bool', + 'masked': 'bool'}, + 'if': 'TARGET_I386' } + + +## +# @xen-event-list: +# +# Query the Xen event channels opened by the guest. +# +# Returns: list of open event channel ports. +# +# Since: 8.0 +# +# Example: +# +# -> { "execute": "xen-event-list" } +# <- { "return": [ +# { +# "pending": false, +# "port": 1, +# "vcpu": 1, +# "remote-domain": "qemu", +# "masked": false, +# "type": "interdomain", +# "target": 1 +# }, +# { +# "pending": false, +# "port": 2, +# "vcpu": 0, +# "remote-domain": "", +# "masked": false, +# "type": "virq", +# "target": 0 +# } +# ] +# } +## +{ 'command': 'xen-event-list', + 'returns': ['EvtchnInfo'], + 'if': 'TARGET_I386' } + +## +# @xen-event-inject: +# +# Inject a Xen event channel port (interrupt) to the guest. +# +# @port: The port number +# +# Since: 8.0 +# +# Example: +# +# -> { "execute": "xen-event-inject", "arguments": { "port": 1 } } +# <- { "return": { } } +## +{ 'command': 'xen-event-inject', + 'data': { 'port': 'uint32' }, + 'if': 'TARGET_I386' } diff --git a/qapi/misc.json b/qapi/misc.json index 45344483cd..ec30e5c570 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -11,30 +11,30 @@ ## # @add_client: # -# Allow client connections for VNC, Spice and socket based -# character devices to be passed in to QEMU via SCM_RIGHTS. +# Allow client connections for VNC, Spice and socket based character +# devices to be passed in to QEMU via SCM_RIGHTS. # -# @protocol: protocol name. Valid names are "vnc", "spice", "@dbus-display" or -# the name of a character device (eg. from -chardev id=XXXX) +# If the FD associated with @fdname is not a socket, the command will +# fail and the FD will be closed. +# +# @protocol: protocol name. Valid names are "vnc", "spice", +# "@dbus-display" or the name of a character device (e.g. from +# -chardev id=XXXX) # # @fdname: file descriptor name previously passed via 'getfd' command # -# @skipauth: whether to skip authentication. Only applies -# to "vnc" and "spice" protocols +# @skipauth: whether to skip authentication. Only applies to "vnc" +# and "spice" protocols # -# @tls: whether to perform TLS. Only applies to the "spice" -# protocol -# -# Returns: nothing on success. +# @tls: whether to perform TLS. Only applies to the "spice" protocol # # Since: 0.14 # # Example: # -# -> { "execute": "add_client", "arguments": { "protocol": "vnc", -# "fdname": "myclient" } } -# <- { "return": {} } -# +# -> { "execute": "add_client", "arguments": { "protocol": "vnc", +# "fdname": "myclient" } } +# <- { "return": {} } ## { 'command': 'add_client', 'data': { 'protocol': 'str', 'fdname': 'str', '*skipauth': 'bool', @@ -62,9 +62,8 @@ # # Example: # -# -> { "execute": "query-name" } -# <- { "return": { "name": "qemu-name" } } -# +# -> { "execute": "query-name" } +# <- { "return": { "name": "qemu-name" } } ## { 'command': 'query-name', 'returns': 'NameInfo', 'allow-preconfig': true } @@ -77,17 +76,17 @@ # # @thread-id: ID of the underlying host thread # -# @poll-max-ns: maximum polling time in ns, 0 means polling is disabled -# (since 2.9) +# @poll-max-ns: maximum polling time in ns, 0 means polling is +# disabled (since 2.9) # -# @poll-grow: how many ns will be added to polling time, 0 means that it's not -# configured (since 2.9) +# @poll-grow: how many ns will be added to polling time, 0 means that +# it's not configured (since 2.9) # -# @poll-shrink: how many ns will be removed from polling time, 0 means that -# it's not configured (since 2.9) +# @poll-shrink: how many ns will be removed from polling time, 0 means +# that it's not configured (since 2.9) # -# @aio-max-batch: maximum number of requests in a batch for the AIO engine, -# 0 means that the engine will use its default (since 6.1) +# @aio-max-batch: maximum number of requests in a batch for the AIO +# engine, 0 means that the engine will use its default (since 6.1) # # Since: 2.0 ## @@ -104,9 +103,9 @@ # # 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. +# 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. # # Returns: a list of @IOThreadInfo for each iothread # @@ -114,19 +113,18 @@ # # Example: # -# -> { "execute": "query-iothreads" } -# <- { "return": [ -# { -# "id":"iothread0", -# "thread-id":3134 -# }, -# { -# "id":"iothread1", -# "thread-id":3135 -# } -# ] -# } -# +# -> { "execute": "query-iothreads" } +# <- { "return": [ +# { +# "id":"iothread0", +# "thread-id":3134 +# }, +# { +# "id":"iothread1", +# "thread-id":3135 +# } +# ] +# } ## { 'command': 'query-iothreads', 'returns': ['IOThreadInfo'], 'allow-preconfig': true } @@ -134,43 +132,46 @@ ## # @stop: # -# Stop all guest VCPU execution. +# Stop guest VM execution. # # Since: 0.14 # -# 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. +# +# In the "suspended" state, it will completely stop the VM and +# cause a transition to the "paused" state. (Since 9.0) # # Example: # -# -> { "execute": "stop" } -# <- { "return": {} } -# +# -> { "execute": "stop" } +# <- { "return": {} } ## { 'command': 'stop' } ## # @cont: # -# Resume guest VCPU execution. +# Resume guest VM execution. # # Since: 0.14 # -# 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. +# If the VM was previously suspended, and not been reset or woken, +# this command will transition back to the "suspended" state. +# (Since 9.0) # # Example: # -# -> { "execute": "cont" } -# <- { "return": {} } -# +# -> { "execute": "cont" } +# <- { "return": {} } ## { 'command': 'cont' } @@ -179,24 +180,22 @@ # # Exit from "preconfig" state # -# 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). +# 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). # # Features: +# # @unstable: This command is experimental. # # Since: 3.0 # -# Returns: nothing -# # Example: # -# -> { "execute": "x-exit-preconfig" } -# <- { "return": {} } -# +# -> { "execute": "x-exit-preconfig" } +# <- { "return": {} } ## { 'command': 'x-exit-preconfig', 'allow-preconfig': true, 'features': [ 'unstable' ] } @@ -211,35 +210,33 @@ # @cpu-index: The CPU to use for commands that require an implicit CPU # # Features: +# # @savevm-monitor-nodes: If present, HMP command savevm only snapshots -# monitor-owned nodes if they have no parents. -# This allows the use of 'savevm' with -# -blockdev. (since 4.2) +# monitor-owned nodes if they have no parents. This allows the +# use of 'savevm' with -blockdev. (since 4.2) # # Returns: the output of the command as a string # # Since: 0.14 # # Notes: This command only exists as a stop-gap. Its use is highly -# discouraged. The semantics of this command are not -# guaranteed: this means that command names, arguments and -# responses can change or be removed at ANY time. Applications -# that rely on long term stability guarantees should NOT -# use this command. +# discouraged. The semantics of this command are not guaranteed: +# this means that command names, arguments and responses can +# change or be removed at ANY time. Applications that rely on +# long term stability guarantees should NOT use this command. # -# Known limitations: +# Known limitations: # -# * This command is stateless, this means that commands that depend -# on state information (such as getfd) might not work +# * This command is stateless, this means that commands that +# depend on state information (such as getfd) might not work # -# * Commands that prompt the user for data don't currently work +# * Commands that prompt the user for data don't currently work # # Example: # -# -> { "execute": "human-monitor-command", -# "arguments": { "command-line": "info kvm" } } -# <- { "return": "kvm support: enabled\r\n" } -# +# -> { "execute": "human-monitor-command", +# "arguments": { "command-line": "info kvm" } } +# <- { "return": "kvm support: enabled\r\n" } ## { 'command': 'human-monitor-command', 'data': {'command-line': 'str', '*cpu-index': 'int'}, @@ -253,24 +250,47 @@ # # @fdname: file descriptor name # -# Returns: Nothing on success -# # Since: 0.14 # -# Notes: If @fdname already exists, the file descriptor assigned to -# it will be closed and replaced by the received file -# descriptor. +# Notes: If @fdname already exists, the file descriptor assigned to it +# will be closed and replaced by the received file descriptor. # -# The 'closefd' command can be used to explicitly close the -# file descriptor when it is no longer needed. +# The 'closefd' command can be used to explicitly close the file +# descriptor when it is no longer needed. # # Example: # -# -> { "execute": "getfd", "arguments": { "fdname": "fd1" } } -# <- { "return": {} } -# +# -> { "execute": "getfd", "arguments": { "fdname": "fd1" } } +# <- { "return": {} } ## -{ 'command': 'getfd', 'data': {'fdname': 'str'} } +{ 'command': 'getfd', 'data': {'fdname': 'str'}, 'if': 'CONFIG_POSIX' } + +## +# @get-win32-socket: +# +# Add a socket that was duplicated to QEMU process with +# WSADuplicateSocketW() via WSASocket() & WSAPROTOCOL_INFOW structure +# and assign it a name (the SOCKET is associated with a CRT file +# descriptor) +# +# @info: the WSAPROTOCOL_INFOW structure (encoded in base64) +# +# @fdname: file descriptor name +# +# Since: 8.0 +# +# Notes: If @fdname already exists, the file descriptor assigned to it +# will be closed and replaced by the received file descriptor. +# +# The 'closefd' command can be used to explicitly close the file +# descriptor when it is no longer needed. +# +# Example: +# +# -> { "execute": "get-win32-socket", "arguments": { "info": "abcd123..", fdname": "skclient" } } +# <- { "return": {} } +## +{ 'command': 'get-win32-socket', 'data': {'info': 'str', 'fdname': 'str'}, 'if': 'CONFIG_WIN32' } ## # @closefd: @@ -279,15 +299,12 @@ # # @fdname: file descriptor name # -# Returns: Nothing on success -# # Since: 0.14 # # Example: # -# -> { "execute": "closefd", "arguments": { "fdname": "fd1" } } -# <- { "return": {} } -# +# -> { "execute": "closefd", "arguments": { "fdname": "fd1" } } +# <- { "return": {} } ## { 'command': 'closefd', 'data': {'fdname': 'str'} } @@ -298,8 +315,8 @@ # # @fdset-id: The ID of the fd set that @fd was added to. # -# @fd: The file descriptor that was received via SCM rights and -# added to the fd set. +# @fd: The file descriptor that was received via SCM rights and added +# to the fd set. # # Since: 1.2 ## @@ -314,21 +331,24 @@ # # @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 # -# Notes: The list of fd sets is shared by all monitor connections. +# Errors: +# - If file descriptor was not received, GenericError +# - If @fdset-id is a negative value, GenericError # -# If @fdset-id is not specified, a new fd set will be created. +# Notes: +# The list of fd sets is shared by all monitor connections. +# +# If @fdset-id is not specified, a new fd set will be created. # # Since: 1.2 # # Example: # -# -> { "execute": "add-fd", "arguments": { "fdset-id": 1 } } -# <- { "return": { "fdset-id": 1, "fd": 3 } } -# +# -> { "execute": "add-fd", "arguments": { "fdset-id": 1 } } +# <- { "return": { "fdset-id": 1, "fd": 3 } } ## { 'command': 'add-fd', 'data': { '*fdset-id': 'int', @@ -344,21 +364,21 @@ # # @fd: The file descriptor that is to be removed. # -# Returns: - Nothing on success -# - If @fdset-id or @fd is not found, FdNotFound +# Errors: +# - If @fdset-id or @fd is not found, GenericError # # Since: 1.2 # -# Notes: The list of fd sets is shared by all monitor connections. +# Notes: +# The list of fd sets is shared by all monitor connections. # -# If @fd is not specified, all file descriptors in @fdset-id -# will be removed. +# If @fd is not specified, all file descriptors in @fdset-id will +# be removed. # # Example: # -# -> { "execute": "remove-fd", "arguments": { "fdset-id": 1, "fd": 3 } } -# <- { "return": {} } -# +# -> { "execute": "remove-fd", "arguments": { "fdset-id": 1, "fd": 3 } } +# <- { "return": {} } ## { 'command': 'remove-fd', 'data': {'fdset-id': 'int', '*fd': 'int'} } @@ -403,35 +423,34 @@ # # Example: # -# -> { "execute": "query-fdsets" } -# <- { "return": [ -# { -# "fds": [ +# -> { "execute": "query-fdsets" } +# <- { "return": [ # { -# "fd": 30, -# "opaque": "rdonly:/path/to/file" +# "fds": [ +# { +# "fd": 30, +# "opaque": "rdonly:/path/to/file" +# }, +# { +# "fd": 24, +# "opaque": "rdwr:/path/to/file" +# } +# ], +# "fdset-id": 1 # }, # { -# "fd": 24, -# "opaque": "rdwr:/path/to/file" +# "fds": [ +# { +# "fd": 28 +# }, +# { +# "fd": 29 +# } +# ], +# "fdset-id": 0 # } -# ], -# "fdset-id": 1 -# }, -# { -# "fds": [ -# { -# "fd": 28 -# }, -# { -# "fd": 29 -# } -# ], -# "fdset-id": 0 +# ] # } -# ] -# } -# ## { 'command': 'query-fdsets', 'returns': ['FdsetInfo'] } @@ -447,7 +466,7 @@ # @number: accepts a number # # @size: accepts a number followed by an optional suffix (K)ilo, -# (M)ega, (G)iga, (T)era +# (M)ega, (G)iga, (T)era # # Since: 1.5 ## @@ -478,7 +497,8 @@ ## # @CommandLineOptionInfo: # -# Details about a command line option, including its list of parameter details +# Details about a command line option, including its list of parameter +# details # # @option: option name # @@ -496,60 +516,92 @@ # # @option: option name # -# Returns: list of @CommandLineOptionInfo for all options (or for the given -# @option). Returns an error if the given @option doesn't exist. +# Returns: list of @CommandLineOptionInfo for all options (or for the +# given @option). +# +# Errors: +# - if the given @option doesn't exist # # Since: 1.5 # # Example: # -# -> { "execute": "query-command-line-options", -# "arguments": { "option": "option-rom" } } -# <- { "return": [ -# { -# "parameters": [ -# { -# "name": "romfile", -# "type": "string" -# }, -# { -# "name": "bootindex", -# "type": "number" -# } -# ], -# "option": "option-rom" -# } -# ] -# } -# +# -> { "execute": "query-command-line-options", +# "arguments": { "option": "option-rom" } } +# <- { "return": [ +# { +# "parameters": [ +# { +# "name": "romfile", +# "type": "string" +# }, +# { +# "name": "bootindex", +# "type": "number" +# } +# ], +# "option": "option-rom" +# } +# ] +# } ## {'command': 'query-command-line-options', - 'data': { '*option': 'str' }, + 'data': {'*option': 'str'}, 'returns': ['CommandLineOptionInfo'], - 'allow-preconfig': true } + 'allow-preconfig': true} ## # @RTC_CHANGE: # # Emitted when the guest changes the RTC time. # -# @offset: offset in seconds between base RTC clock (as specified -# by -rtc base), and new RTC clock value +# @offset: offset in seconds between base RTC clock (as specified by +# -rtc base), and new RTC clock value # # @qom-path: path to the RTC object in the QOM tree # -# Note: This event is rate-limited. -# It is not guaranteed that the RTC in the system implements -# this event, or even that the system has an RTC at all. +# Note: This event is rate-limited. It is not guaranteed that the RTC +# in the system implements this event, or even that the system has +# an RTC at all. # # Since: 0.13 # # Example: # -# <- { "event": "RTC_CHANGE", -# "data": { "offset": 78 }, -# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } -# +# <- { "event": "RTC_CHANGE", +# "data": { "offset": 78 }, +# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } ## { 'event': 'RTC_CHANGE', 'data': { 'offset': 'int', 'qom-path': 'str' } } + +## +# @VFU_CLIENT_HANGUP: +# +# Emitted when the client of a TYPE_VFIO_USER_SERVER closes the +# communication channel +# +# @vfu-id: ID of the TYPE_VFIO_USER_SERVER object. It is the last +# component of @vfu-qom-path referenced below +# +# @vfu-qom-path: path to the TYPE_VFIO_USER_SERVER object in the QOM +# tree +# +# @dev-id: ID of attached PCI device +# +# @dev-qom-path: path to attached PCI device in the QOM tree +# +# Since: 7.1 +# +# Example: +# +# <- { "event": "VFU_CLIENT_HANGUP", +# "data": { "vfu-id": "vfu1", +# "vfu-qom-path": "/objects/vfu1", +# "dev-id": "sas1", +# "dev-qom-path": "/machine/peripheral/sas1" }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +## +{ 'event': 'VFU_CLIENT_HANGUP', + 'data': { 'vfu-id': 'str', 'vfu-qom-path': 'str', + 'dev-id': 'str', 'dev-qom-path': 'str' } } diff --git a/qapi/net.json b/qapi/net.json index d6f7cfd4d6..0f5a259475 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -6,7 +6,7 @@ # = Net devices ## -{ 'include': 'common.json' } +{ 'include': 'sockets.json' } ## # @set_link: @@ -17,21 +17,20 @@ # # @up: true to set the link status to be up # -# Returns: Nothing on success -# If @name is not a valid network device, DeviceNotFound +# Errors: +# - If @name is not a valid network device, DeviceNotFound # # Since: 0.14 # -# Notes: Not all network adapters support setting link status. This command -# will succeed even if the network adapter does not support link status -# notification. +# Notes: Not all network adapters support setting link status. This +# command will succeed even if the network adapter does not +# support link status notification. # # Example: # -# -> { "execute": "set_link", -# "arguments": { "name": "e1000.0", "up": false } } -# <- { "return": {} } -# +# -> { "execute": "set_link", +# "arguments": { "name": "e1000.0", "up": false } } +# <- { "return": {} } ## { 'command': 'set_link', 'data': {'name': 'str', 'up': 'bool'} } @@ -44,16 +43,15 @@ # # Since: 0.14 # -# Returns: Nothing on success -# If @type is not a valid network backend, DeviceNotFound +# Errors: +# - If @type is not a valid network backend, DeviceNotFound # # Example: # -# -> { "execute": "netdev_add", -# "arguments": { "type": "user", "id": "netdev1", -# "dnssearch": [ { "str": "example.org" } ] } } -# <- { "return": {} } -# +# -> { "execute": "netdev_add", +# "arguments": { "type": "user", "id": "netdev1", +# "dnssearch": [ { "str": "example.org" } ] } } +# <- { "return": {} } ## { 'command': 'netdev_add', 'data': 'Netdev', 'boxed': true, 'allow-preconfig': true } @@ -65,16 +63,15 @@ # # @id: the name of the network backend to remove # -# Returns: Nothing on success -# If @id is not a valid network backend, DeviceNotFound +# Errors: +# - If @id is not a valid network backend, DeviceNotFound # # Since: 0.14 # # Example: # -# -> { "execute": "netdev_del", "arguments": { "id": "netdev1" } } -# <- { "return": {} } -# +# -> { "execute": "netdev_del", "arguments": { "id": "netdev1" } } +# <- { "return": {} } ## { 'command': 'netdev_del', 'data': {'id': 'str'}, 'allow-preconfig': true } @@ -104,28 +101,37 @@ '*addr': 'str', '*vectors': 'uint32' } } +## +# @String: +# +# A fat type wrapping 'str', to be embedded in lists. +# +# Since: 1.2 +## +{ 'struct': 'String', + 'data': { + 'str': 'str' } } + ## # @NetdevUserOptions: # -# Use the user mode network stack which requires no administrator privilege to -# run. +# Use the user mode network stack which requires no administrator +# privilege to run. # # @hostname: client hostname reported by the builtin DHCP server # # @restrict: isolate the guest from the host # -# @ipv4: whether to support IPv4, default true for enabled -# (since 2.6) +# @ipv4: whether to support IPv4, default true for enabled (since 2.6) # -# @ipv6: whether to support IPv6, default true for enabled -# (since 2.6) +# @ipv6: whether to support IPv6, default true for enabled (since 2.6) # # @ip: legacy parameter, use net= instead # -# @net: IP network address that the guest will see, in the -# form addr[/netmask] The netmask is optional, and can be -# either in the form a.b.c.d or as a number of valid top-most -# bits. Default is 10.0.2.0/24. +# @net: IP network address that the guest will see, in the form +# addr[/netmask] The netmask is optional, and can be either in the +# form a.b.c.d or as a number of valid top-most bits. Default is +# 10.0.2.0/24. # # @host: guest-visible address of the host # @@ -134,34 +140,34 @@ # @bootfile: BOOTP filename, for use with tftp= # # @dhcpstart: the first of the 16 IPs the built-in DHCP server can -# assign +# assign # # @dns: guest-visible address of the virtual nameserver # -# @dnssearch: list of DNS suffixes to search, passed as DHCP option -# to the guest +# @dnssearch: list of DNS suffixes to search, passed as DHCP option to +# the guest # # @domainname: guest-visible domain name of the virtual nameserver -# (since 3.0) +# (since 3.0) # -# @ipv6-prefix: IPv6 network prefix (default is fec0::) (since -# 2.6). The network prefix is given in the usual -# hexadecimal IPv6 address notation. +# @ipv6-prefix: IPv6 network prefix (default is fec0::) (since 2.6). +# The network prefix is given in the usual hexadecimal IPv6 +# address notation. # -# @ipv6-prefixlen: IPv6 network prefix length (default is 64) -# (since 2.6) +# @ipv6-prefixlen: IPv6 network prefix length (default is 64) (since +# 2.6) # # @ipv6-host: guest-visible IPv6 address of the host (since 2.6) # -# @ipv6-dns: guest-visible IPv6 address of the virtual -# nameserver (since 2.6) +# @ipv6-dns: guest-visible IPv6 address of the virtual nameserver +# (since 2.6) # # @smb: root directory of the built-in SMB server # # @smbserver: IP address of the built-in SMB server # # @hostfwd: redirect incoming TCP or UDP host connections to guest -# endpoints +# endpoints # # @guestfwd: forward guest TCP connections # @@ -204,7 +210,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 # @@ -214,7 +220,7 @@ # # @helper: command to execute to configure bridge # -# @sndbuf: send buffer limit. Understands [TGMKkb] suffixes. +# @sndbuf: send buffer limit. Understands [TGMKkb] suffixes. # # @vnet_hdr: enable the IFF_VNET_HDR flag on the tap interface # @@ -223,14 +229,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) +# @poll-us: maximum number of microseconds that could be spent on busy +# polling for tap (since 2.7) # # Since: 1.2 ## @@ -298,13 +304,12 @@ # # @udp: use the udp version of l2tpv3 encapsulation # -# @cookie64: use 64 bit coookies +# @cookie64: use 64 bit cookies # # @counter: have sequence counter # -# @pincounter: pin sequence counter to zero - -# workaround for buggy implementations or -# networks with packet reorder +# @pincounter: pin sequence counter to zero - workaround for buggy +# implementations or networks with packet reorder # # @txcookie: 32 or 64 bit transmit cookie # @@ -312,11 +317,11 @@ # # @txsession: 32 bit transmit session # -# @rxsession: 32 bit receive session - if not specified -# set to the same value as transmit +# @rxsession: 32 bit receive session - if not specified set to the +# same value as transmit # -# @offset: additional offset - allows the insertion of -# additional application-specific data before the packet payload +# @offset: additional offset - allows the insertion of additional +# application-specific data before the packet payload # # Since: 2.1 ## @@ -381,7 +386,9 @@ # Connect two or more net clients through a software hub. # # @hubid: hub identifier number -# @netdev: used to connect hub to a netdev instead of a device (since 2.12) +# +# @netdev: used to connect hub to a netdev instead of a device (since +# 2.12) # # Since: 1.2 ## @@ -395,12 +402,12 @@ # # Connect a client to a netmap-enabled NIC or to a VALE switch port # -# @ifname: Either the name of an existing network interface supported by -# netmap, or the name of a VALE port (created on the fly). -# A VALE port name is in the form 'valeXXX:YYY', where XXX and -# YYY are non-negative integers. XXX identifies a switch and -# YYY identifies a port of the switch. VALE ports having the -# same XXX are therefore connected to the same switch. +# @ifname: Either the name of an existing network interface supported +# by netmap, or the name of a VALE port (created on the fly). A +# VALE port name is in the form 'valeXXX:YYY', where XXX and YYY +# are non-negative integers. XXX identifies a switch and YYY +# identifies a port of the switch. VALE ports having the same XXX +# are therefore connected to the same switch. # # @devname: path of the netmap device (default: '/dev/netmap'). # @@ -411,6 +418,63 @@ 'ifname': 'str', '*devname': 'str' } } +## +# @AFXDPMode: +# +# Attach mode for a default XDP program +# +# @skb: generic mode, no driver support necessary +# +# @native: DRV mode, program is attached to a driver, packets are +# passed to the socket without allocation of skb. +# +# Since: 8.2 +## +{ 'enum': 'AFXDPMode', + 'data': [ 'native', 'skb' ], + 'if': 'CONFIG_AF_XDP' } + +## +# @NetdevAFXDPOptions: +# +# AF_XDP network backend +# +# @ifname: The name of an existing network interface. +# +# @mode: Attach mode for a default XDP program. If not specified, +# then 'native' will be tried first, then 'skb'. +# +# @force-copy: Force XDP copy mode even if device supports zero-copy. +# (default: false) +# +# @queues: number of queues to be used for multiqueue interfaces +# (default: 1). +# +# @start-queue: Use @queues starting from this queue number +# (default: 0). +# +# @inhibit: Don't load a default XDP program, use one already loaded +# to the interface (default: false). Requires @sock-fds. +# +# @sock-fds: A colon (:) separated list of file descriptors for +# already open but not bound AF_XDP sockets in the queue order. +# One fd per queue. These descriptors should already be added +# into XDP socket map for corresponding queues. Requires +# @inhibit. +# +# Since: 8.2 +## +{ 'struct': 'NetdevAFXDPOptions', + 'data': { + 'ifname': 'str', + '*mode': 'AFXDPMode', + '*force-copy': 'bool', + '*queues': 'int', + '*start-queue': 'int', + '*inhibit': 'bool', + '*sock-fds': 'str' }, + 'if': 'CONFIG_AF_XDP' } + ## # @NetdevVhostUserOptions: # @@ -421,7 +485,7 @@ # @vhostforce: vhost on for non-MSIX virtio guests (default: false). # # @queues: number of queues to be created for multiqueue vhost-user -# (default: 1) (Since 2.5) +# (default: 1) (Since 2.5) # # Since: 2.1 ## @@ -436,21 +500,31 @@ # # Vhost-vdpa network backend # -# vDPA device is a device that uses a datapath which complies with the virtio -# specifications with a vendor specific control path. +# vDPA device is a device that uses a datapath which complies with the +# virtio specifications with a vendor specific control path. # -# @vhostdev: path of vhost-vdpa device -# (default:'/dev/vhost-vdpa-0') +# @vhostdev: path of vhost-vdpa device (default:'/dev/vhost-vdpa-0') +# +# @vhostfd: file descriptor of an already opened vhost vdpa device # # @queues: number of queues to be created for multiqueue vhost-vdpa -# (default: 1) +# (default: 1) +# +# @x-svq: Start device with (experimental) shadow virtqueue. (Since +# 7.1) (default: false) +# +# Features: +# +# @unstable: Member @x-svq is experimental. # # Since: 5.1 ## { 'struct': 'NetdevVhostVDPAOptions', 'data': { '*vhostdev': 'str', - '*queues': 'int' } } + '*vhostfd': 'str', + '*queues': 'int', + '*x-svq': {'type': 'bool', 'features' : [ 'unstable'] } } } ## # @NetdevVmnetHostOptions: @@ -461,31 +535,28 @@ # interfaces that are in host mode and also with the host. # # @start-address: The starting IPv4 address to use for the interface. -# Must be in the private IP range (RFC 1918). Must be -# specified along with @end-address and @subnet-mask. -# This address is used as the gateway address. The -# subsequent address up to and including end-address are -# placed in the DHCP pool. +# Must be in the private IP range (RFC 1918). Must be specified +# along with @end-address and @subnet-mask. This address is used +# as the gateway address. The subsequent address up to and +# including end-address are placed in the DHCP pool. # # @end-address: The DHCP IPv4 range end address to use for the -# interface. Must be in the private IP range (RFC 1918). -# Must be specified along with @start-address and -# @subnet-mask. +# interface. Must be in the private IP range (RFC 1918). Must be +# specified along with @start-address and @subnet-mask. # -# @subnet-mask: The IPv4 subnet mask to use on the interface. Must -# be specified along with @start-address and @subnet-mask. +# @subnet-mask: The IPv4 subnet mask to use on the interface. Must be +# specified along with @start-address and @subnet-mask. # -# @isolated: Enable isolation for this interface. Interface isolation -# ensures that vmnet interface is not able to communicate -# with any other vmnet interfaces. Only communication with -# host is allowed. Requires at least macOS Big Sur 11.0. +# @isolated: Enable isolation for this interface. Interface isolation +# ensures that vmnet interface is not able to communicate with any +# other vmnet interfaces. Only communication with host is +# allowed. Requires at least macOS Big Sur 11.0. # # @net-uuid: The identifier (UUID) to uniquely identify the isolated -# network vmnet interface should be added to. If -# set, no DHCP service is provided for this interface and -# network communication is allowed only with other interfaces -# added to this network identified by the UUID. Requires -# at least macOS Big Sur 11.0. +# network vmnet interface should be added to. If set, no DHCP +# service is provided for this interface and network communication +# is allowed only with other interfaces added to this network +# identified by the UUID. Requires at least macOS Big Sur 11.0. # # Since: 7.1 ## @@ -504,34 +575,33 @@ # vmnet (shared mode) network backend. # # Allows traffic originating from the vmnet interface to reach the -# Internet through a network address translator (NAT). -# The vmnet interface can communicate with the host and with -# other shared mode interfaces on the same subnet. If no DHCP -# settings, subnet mask and IPv6 prefix specified, the interface can -# communicate with any of other interfaces in shared mode. +# Internet through a network address translator (NAT). The vmnet +# interface can communicate with the host and with other shared mode +# interfaces on the same subnet. If no DHCP settings, subnet mask and +# IPv6 prefix specified, the interface can communicate with any of +# other interfaces in shared mode. # # @start-address: The starting IPv4 address to use for the interface. -# Must be in the private IP range (RFC 1918). Must be -# specified along with @end-address and @subnet-mask. -# This address is used as the gateway address. The -# subsequent address up to and including end-address are -# placed in the DHCP pool. +# Must be in the private IP range (RFC 1918). Must be specified +# along with @end-address and @subnet-mask. This address is used +# as the gateway address. The subsequent address up to and +# including end-address are placed in the DHCP pool. # # @end-address: The DHCP IPv4 range end address to use for the -# interface. Must be in the private IP range (RFC 1918). -# Must be specified along with @start-address and @subnet-mask. +# interface. Must be in the private IP range (RFC 1918). Must be +# specified along with @start-address and @subnet-mask. # -# @subnet-mask: The IPv4 subnet mask to use on the interface. Must -# be specified along with @start-address and @subnet-mask. +# @subnet-mask: The IPv4 subnet mask to use on the interface. Must be +# specified along with @start-address and @subnet-mask. # -# @isolated: Enable isolation for this interface. Interface isolation -# ensures that vmnet interface is not able to communicate -# with any other vmnet interfaces. Only communication with -# host is allowed. Requires at least macOS Big Sur 11.0. +# @isolated: Enable isolation for this interface. Interface isolation +# ensures that vmnet interface is not able to communicate with any +# other vmnet interfaces. Only communication with host is +# allowed. Requires at least macOS Big Sur 11.0. # -# @nat66-prefix: The IPv6 prefix to use into guest network. Must be a -# unique local address i.e. start with fd00::/8 and have -# length of 64. +# @nat66-prefix: The IPv6 prefix to use into guest network. Must be a +# unique local address i.e. start with fd00::/8 and have length of +# 64. # # Since: 7.1 ## @@ -553,10 +623,10 @@ # # @ifname: The name of the physical interface to be bridged. # -# @isolated: Enable isolation for this interface. Interface isolation -# ensures that vmnet interface is not able to communicate -# with any other vmnet interfaces. Only communication with -# host is allowed. Requires at least macOS Big Sur 11.0. +# @isolated: Enable isolation for this interface. Interface isolation +# ensures that vmnet interface is not able to communicate with any +# other vmnet interfaces. Only communication with host is +# allowed. Requires at least macOS Big Sur 11.0. # # Since: 7.1 ## @@ -566,21 +636,88 @@ '*isolated': 'bool' }, 'if': 'CONFIG_VMNET' } +## +# @NetdevStreamOptions: +# +# Configuration info for stream socket netdev +# +# @addr: socket address to listen on (server=true) or connect to +# (server=false) +# +# @server: create server socket (default: false) +# +# @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 8.0) +# +# Only SocketAddress types 'unix', 'inet' and 'fd' are supported. +# +# Since: 7.2 +## +{ 'struct': 'NetdevStreamOptions', + 'data': { + 'addr': 'SocketAddress', + '*server': 'bool', + '*reconnect': 'uint32' } } + +## +# @NetdevDgramOptions: +# +# Configuration info for datagram socket netdev. +# +# @remote: remote address +# +# @local: local address +# +# Only SocketAddress types 'unix', 'inet' and 'fd' are supported. +# +# If remote address is present and it's a multicast address, local +# address is optional. Otherwise local address is required and remote +# address is optional. +# +# .. table:: Valid parameters combination table +# :widths: auto +# +# ============= ======== ===== +# remote local okay? +# ============= ======== ===== +# absent absent no +# absent not fd no +# absent fd yes +# multicast absent yes +# multicast present yes +# not multicast absent no +# not multicast present yes +# ============= ======== ===== +# +# Since: 7.2 +## +{ 'struct': 'NetdevDgramOptions', + 'data': { + '*local': 'SocketAddress', + '*remote': 'SocketAddress' } } + ## # @NetClientDriver: # # Available netdev drivers. # -# Since: 2.7 +# @l2tpv3: since 2.1 +# @vhost-vdpa: since 5.1 +# @vmnet-host: since 7.1 +# @vmnet-shared: since 7.1 +# @vmnet-bridged: since 7.1 +# @stream: since 7.2 +# @dgram: since 7.2 +# @af-xdp: since 8.2 # -# @vhost-vdpa since 5.1 -# @vmnet-host since 7.1 -# @vmnet-shared since 7.1 -# @vmnet-bridged since 7.1 +# Since: 2.7 ## { 'enum': 'NetClientDriver', - 'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'vde', - 'bridge', 'hubport', 'netmap', 'vhost-user', 'vhost-vdpa', + 'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'stream', + 'dgram', 'vde', 'bridge', 'hubport', 'netmap', 'vhost-user', + 'vhost-vdpa', + { 'name': 'af-xdp', 'if': 'CONFIG_AF_XDP' }, { 'name': 'vmnet-host', 'if': 'CONFIG_VMNET' }, { 'name': 'vmnet-shared', 'if': 'CONFIG_VMNET' }, { 'name': 'vmnet-bridged', 'if': 'CONFIG_VMNET' }] } @@ -595,11 +732,6 @@ # @type: Specify the driver used for interpreting remaining arguments. # # Since: 1.2 -# -# 'l2tpv3' - since 2.1 -# 'vmnet-host' - since 7.1 -# 'vmnet-shared' - since 7.1 -# 'vmnet-bridged' - since 7.1 ## { 'union': 'Netdev', 'base': { 'id': 'str', 'type': 'NetClientDriver' }, @@ -610,10 +742,14 @@ 'tap': 'NetdevTapOptions', 'l2tpv3': 'NetdevL2TPv3Options', 'socket': 'NetdevSocketOptions', + 'stream': 'NetdevStreamOptions', + 'dgram': 'NetdevDgramOptions', 'vde': 'NetdevVdeOptions', 'bridge': 'NetdevBridgeOptions', 'hubport': 'NetdevHubPortOptions', 'netmap': 'NetdevNetmapOptions', + 'af-xdp': { 'type': 'NetdevAFXDPOptions', + 'if': 'CONFIG_AF_XDP' }, 'vhost-user': 'NetdevVhostUserOptions', 'vhost-vdpa': 'NetdevVhostVDPAOptions', 'vmnet-host': { 'type': 'NetdevVmnetHostOptions', @@ -692,41 +828,42 @@ # @name: net client name # # Returns: list of @RxFilterInfo for all NICs (or for the given NIC). -# Returns an error if the given @name doesn't exist, or given -# NIC doesn't support rx-filter querying, or given net client -# isn't a NIC. +# +# Errors: +# - if the given @name doesn't exist +# - if the given NIC doesn't support rx-filter querying +# - if the given net client isn't a NIC # # Since: 1.6 # # Example: # -# -> { "execute": "query-rx-filter", "arguments": { "name": "vnet0" } } -# <- { "return": [ -# { -# "promiscuous": true, -# "name": "vnet0", -# "main-mac": "52:54:00:12:34:56", -# "unicast": "normal", -# "vlan": "normal", -# "vlan-table": [ -# 4, -# 0 -# ], -# "unicast-table": [ -# ], -# "multicast": "normal", -# "multicast-overflow": false, -# "unicast-overflow": false, -# "multicast-table": [ -# "01:00:5e:00:00:01", -# "33:33:00:00:00:01", -# "33:33:ff:12:34:56" -# ], -# "broadcast-allowed": false -# } -# ] -# } -# +# -> { "execute": "query-rx-filter", "arguments": { "name": "vnet0" } } +# <- { "return": [ +# { +# "promiscuous": true, +# "name": "vnet0", +# "main-mac": "52:54:00:12:34:56", +# "unicast": "normal", +# "vlan": "normal", +# "vlan-table": [ +# 4, +# 0 +# ], +# "unicast-table": [ +# ], +# "multicast": "normal", +# "multicast-overflow": false, +# "unicast-overflow": false, +# "multicast-table": [ +# "01:00:5e:00:00:01", +# "33:33:00:00:00:01", +# "33:33:ff:12:34:56" +# ], +# "broadcast-allowed": false +# } +# ] +# } ## { 'command': 'query-rx-filter', 'data': { '*name': 'str' }, @@ -735,8 +872,8 @@ ## # @NIC_RX_FILTER_CHANGED: # -# Emitted once until the 'query-rx-filter' command is executed, the first event -# will always be emitted +# Emitted once until the 'query-rx-filter' command is executed, the +# first event will always be emitted # # @name: net client name # @@ -746,12 +883,10 @@ # # Example: # -# <- { "event": "NIC_RX_FILTER_CHANGED", -# "data": { "name": "vnet0", -# "path": "/machine/peripheral/vnet0/virtio-backend" }, -# "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } -# } -# +# <- { "event": "NIC_RX_FILTER_CHANGED", +# "data": { "name": "vnet0", +# "path": "/machine/peripheral/vnet0/virtio-backend" }, +# "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } ## { 'event': 'NIC_RX_FILTER_CHANGED', 'data': { '*name': 'str', 'path': 'str' } } @@ -762,7 +897,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 # @@ -770,12 +905,12 @@ # # @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) +# @interfaces: An optional list of interface names, which restricts +# the 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 ## @@ -791,16 +926,17 @@ ## # @announce-self: # -# Trigger generation of broadcast RARP frames to update network switches. -# This can be useful when network bonds fail-over the active slave. +# Trigger generation of broadcast RARP frames to update network +# switches. This can be useful when network bonds fail-over the +# active slave. # # Example: # -# -> { "execute": "announce-self", -# "arguments": { -# "initial": 50, "max": 550, "rounds": 10, "step": 50, -# "interfaces": ["vn2", "vn3"], "id": "bob" } } -# <- { "return": {} } +# -> { "execute": "announce-self", +# "arguments": { +# "initial": 50, "max": 550, "rounds": 10, "step": 50, +# "interfaces": ["vn2", "vn3"], "id": "bob" } } +# <- { "return": {} } # # Since: 4.0 ## @@ -810,9 +946,10 @@ ## # @FAILOVER_NEGOTIATED: # -# Emitted when VIRTIO_NET_F_STANDBY was enabled during feature negotiation. -# Failover primary devices which were hidden (not hotplugged when requested) -# before will now be hotplugged by the virtio-net standby device. +# Emitted when VIRTIO_NET_F_STANDBY was enabled during feature +# negotiation. Failover primary devices which were hidden (not +# hotplugged when requested) before will now be hotplugged by the +# virtio-net standby device. # # @device-id: QEMU device id of the unplugged device # @@ -820,10 +957,55 @@ # # Example: # -# <- { "event": "FAILOVER_NEGOTIATED", -# "data": { "device-id": "net1" }, -# "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } -# +# <- { "event": "FAILOVER_NEGOTIATED", +# "data": { "device-id": "net1" }, +# "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } ## { 'event': 'FAILOVER_NEGOTIATED', 'data': {'device-id': 'str'} } + +## +# @NETDEV_STREAM_CONNECTED: +# +# Emitted when the netdev stream backend is connected +# +# @netdev-id: QEMU netdev id that is connected +# +# @addr: The destination address +# +# Since: 7.2 +# +# Examples: +# +# <- { "event": "NETDEV_STREAM_CONNECTED", +# "data": { "netdev-id": "netdev0", +# "addr": { "port": "47666", "ipv6": true, +# "host": "::1", "type": "inet" } }, +# "timestamp": { "seconds": 1666269863, "microseconds": 311222 } } +# +# <- { "event": "NETDEV_STREAM_CONNECTED", +# "data": { "netdev-id": "netdev0", +# "addr": { "path": "/tmp/qemu0", "type": "unix" } }, +# "timestamp": { "seconds": 1666269706, "microseconds": 413651 } } +## +{ 'event': 'NETDEV_STREAM_CONNECTED', + 'data': { 'netdev-id': 'str', + 'addr': 'SocketAddress' } } + +## +# @NETDEV_STREAM_DISCONNECTED: +# +# Emitted when the netdev stream backend is disconnected +# +# @netdev-id: QEMU netdev id that is disconnected +# +# Since: 7.2 +# +# Example: +# +# <- { 'event': 'NETDEV_STREAM_DISCONNECTED', +# 'data': {'netdev-id': 'netdev0'}, +# 'timestamp': {'seconds': 1663330937, 'microseconds': 526695} } +## +{ 'event': 'NETDEV_STREAM_DISCONNECTED', + 'data': { 'netdev-id': 'str' } } diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index 587f31baf6..8f1efab8b9 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -454,8 +454,8 @@ opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) OptsVisitor *ov = to_ov(v); const QemuOpt *opt; const char *str; - unsigned long long val; - char *endptr; + uint64_t val; + const char *endptr; if (ov->list_mode == LM_UNSIGNED_INTERVAL) { *obj = ov->range_next.u; @@ -471,18 +471,18 @@ opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) /* we've gotten past lookup_scalar() */ assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); - if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) { + if (parse_uint(str, &endptr, 0, &val) == 0) { if (*endptr == '\0') { *obj = val; processed(ov, name); return true; } if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { - unsigned long long val2; + uint64_t val2; str = endptr + 1; - if (parse_uint_full(str, &val2, 0) == 0 && - val2 <= UINT64_MAX && val <= val2 && + if (parse_uint_full(str, 0, &val2) == 0 && + val <= val2 && val2 - val < OPTS_VISITOR_RANGE_MAX) { ov->range_next.u = val; ov->range_limit.u = val2; diff --git a/qapi/pci.json b/qapi/pci.json index ee7c659fec..08bf695863 100644 --- a/qapi/pci.json +++ b/qapi/pci.json @@ -29,8 +29,9 @@ # # @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 # @@ -49,21 +50,21 @@ # # Information about a bus of a PCI Bridge device # -# @number: primary bus interface number. This should be the number of the -# bus the device resides on. +# @number: primary bus interface number. This should be the number of +# the bus the device resides on. # -# @secondary: secondary bus interface number. This is the number of the -# main bus for the bridge +# @secondary: secondary bus interface number. This is the number of +# the main bus for the bridge # # @subordinate: This is the highest number bus that resides below the -# bridge. +# bridge. # # @io_range: The PIO range for all devices on this bridge # # @memory_range: The MMIO range for all devices on this bridge # -# @prefetchable_range: The range of prefetchable MMIO for all devices on -# this bridge +# @prefetchable_range: The range of prefetchable MMIO for all devices +# on this bridge # # Since: 2.4 ## @@ -145,8 +146,8 @@ # # @regions: a list of the PCI I/O regions associated with the device # -# Notes: the contents of @class_info.desc are not stable and should only be -# treated as informational. +# Notes: the contents of @class_info.desc are not stable and should +# only be treated as informational. # # Since: 0.14 ## @@ -174,143 +175,143 @@ # # 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. +# 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. # # Since: 0.14 # # Example: # -# -> { "execute": "query-pci" } -# <- { "return": [ -# { -# "bus": 0, -# "devices": [ -# { -# "bus": 0, -# "qdev_id": "", -# "slot": 0, -# "class_info": { -# "class": 1536, -# "desc": "Host bridge" -# }, -# "id": { -# "device": 32902, -# "vendor": 4663 -# }, -# "function": 0, -# "regions": [ -# ] -# }, -# { -# "bus": 0, -# "qdev_id": "", -# "slot": 1, -# "class_info": { -# "class": 1537, -# "desc": "ISA bridge" -# }, -# "id": { -# "device": 32902, -# "vendor": 28672 -# }, -# "function": 0, -# "regions": [ -# ] -# }, -# { -# "bus": 0, -# "qdev_id": "", -# "slot": 1, -# "class_info": { -# "class": 257, -# "desc": "IDE controller" -# }, -# "id": { -# "device": 32902, -# "vendor": 28688 -# }, -# "function": 1, -# "regions": [ -# { -# "bar": 4, -# "size": 16, -# "address": 49152, -# "type": "io" -# } -# ] -# }, -# { -# "bus": 0, -# "qdev_id": "", -# "slot": 2, -# "class_info": { -# "class": 768, -# "desc": "VGA controller" -# }, -# "id": { -# "device": 4115, -# "vendor": 184 -# }, -# "function": 0, -# "regions": [ -# { -# "prefetch": true, -# "mem_type_64": false, -# "bar": 0, -# "size": 33554432, -# "address": 4026531840, -# "type": "memory" -# }, -# { -# "prefetch": false, -# "mem_type_64": false, -# "bar": 1, -# "size": 4096, -# "address": 4060086272, -# "type": "memory" -# }, -# { -# "prefetch": false, -# "mem_type_64": false, -# "bar": 6, -# "size": 65536, -# "address": -1, -# "type": "memory" -# } -# ] -# }, -# { -# "bus": 0, -# "qdev_id": "", -# "irq": 11, -# "slot": 4, -# "class_info": { -# "class": 1280, -# "desc": "RAM controller" -# }, -# "id": { -# "device": 6900, -# "vendor": 4098 -# }, -# "function": 0, -# "regions": [ -# { -# "bar": 0, -# "size": 32, -# "address": 49280, -# "type": "io" -# } -# ] -# } -# ] -# } -# ] -# } -# -# Note: This example has been shortened as the real response is too long. +# -> { "execute": "query-pci" } +# <- { "return": [ +# { +# "bus": 0, +# "devices": [ +# { +# "bus": 0, +# "qdev_id": "", +# "slot": 0, +# "class_info": { +# "class": 1536, +# "desc": "Host bridge" +# }, +# "id": { +# "device": 32902, +# "vendor": 4663 +# }, +# "function": 0, +# "regions": [ +# ] +# }, +# { +# "bus": 0, +# "qdev_id": "", +# "slot": 1, +# "class_info": { +# "class": 1537, +# "desc": "ISA bridge" +# }, +# "id": { +# "device": 32902, +# "vendor": 28672 +# }, +# "function": 0, +# "regions": [ +# ] +# }, +# { +# "bus": 0, +# "qdev_id": "", +# "slot": 1, +# "class_info": { +# "class": 257, +# "desc": "IDE controller" +# }, +# "id": { +# "device": 32902, +# "vendor": 28688 +# }, +# "function": 1, +# "regions": [ +# { +# "bar": 4, +# "size": 16, +# "address": 49152, +# "type": "io" +# } +# ] +# }, +# { +# "bus": 0, +# "qdev_id": "", +# "slot": 2, +# "class_info": { +# "class": 768, +# "desc": "VGA controller" +# }, +# "id": { +# "device": 4115, +# "vendor": 184 +# }, +# "function": 0, +# "regions": [ +# { +# "prefetch": true, +# "mem_type_64": false, +# "bar": 0, +# "size": 33554432, +# "address": 4026531840, +# "type": "memory" +# }, +# { +# "prefetch": false, +# "mem_type_64": false, +# "bar": 1, +# "size": 4096, +# "address": 4060086272, +# "type": "memory" +# }, +# { +# "prefetch": false, +# "mem_type_64": false, +# "bar": 6, +# "size": 65536, +# "address": -1, +# "type": "memory" +# } +# ] +# }, +# { +# "bus": 0, +# "qdev_id": "", +# "irq": 11, +# "slot": 4, +# "class_info": { +# "class": 1280, +# "desc": "RAM controller" +# }, +# "id": { +# "device": 6900, +# "vendor": 4098 +# }, +# "function": 0, +# "regions": [ +# { +# "bar": 0, +# "size": 32, +# "address": 49280, +# "type": "io" +# } +# ] +# } +# ] +# } +# ] +# } # +# Note: This example has been shortened as the real response is too +# long. ## { 'command': 'query-pci', 'returns': ['PciInfo'] } diff --git a/qapi/pragma.json b/qapi/pragma.json index 7f810b0e97..59fbe74b8c 100644 --- a/qapi/pragma.json +++ b/qapi/pragma.json @@ -3,8 +3,8 @@ { 'pragma': { 'doc-required': true } } -# Whitelists to permit QAPI rule violations; think twice before you -# add to them! +# Entries in these lists are allowed to violate the QAPI rules (for +# historical reasons); think twice before you add to them! { 'pragma': { # Command names containing '_' 'command-name-exceptions': [ @@ -31,6 +31,61 @@ 'query-tpm-models', 'query-tpm-types', 'ringbuf-read' ], + # Types, commands, and events with undocumented members / arguments: + 'documentation-exceptions': [ + 'AbortWrapper', + 'AudiodevDriver', + 'BlkdebugEvent', + 'BlockDirtyBitmapAddWrapper', + 'BlockDirtyBitmapMergeWrapper', + 'BlockDirtyBitmapWrapper', + 'BlockdevBackupWrapper', + 'BlockdevDriver', + 'BlockdevQcow2EncryptionFormat', + 'BlockdevSnapshotInternalWrapper', + 'BlockdevSnapshotSyncWrapper', + 'BlockdevSnapshotWrapper', + 'BlockdevVmdkAdapterType', + 'ChardevBackendKind', + 'CpuS390Entitlement', + 'CpuS390Polarization', + 'CpuS390State', + 'CxlCorErrorType', + 'DisplayProtocol', + 'DriveBackupWrapper', + 'DummyBlockCoreForceArrays', + 'DummyForceArrays', + 'DummyVirtioForceArrays', + 'GrabToggleKeys', + 'HotKeyMod', + 'ImageInfoSpecificKind', + 'InputAxis', + 'InputButton', + 'IscsiHeaderDigest', + 'IscsiTransport', + 'JSONType', + 'KeyValueKind', + 'MemoryDeviceInfoKind', + 'NetClientDriver', + 'ObjectType', + 'PciMemoryRegion', + 'QCryptoAkCipherKeyType', + 'QCryptodevBackendServiceType', + 'QKeyCode', + 'RbdAuthMode', + 'RbdImageEncryptionFormat', + 'String', + 'StringWrapper', + 'SysEmuTarget', + 'ThrottleGroupProperties', + 'VncPrimaryAuth', + 'VncVencryptSubAuth', + 'X86CPURegister32', + 'XDbgBlockGraph', + 'YankInstanceType', + 'blockdev-reopen', + 'query-rocker', + 'query-rocker-ports' ], # Externally visible types whose member names may use uppercase 'member-name-exceptions': [ # visible in: 'ACPISlotType', # query-acpi-ospm-status diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 4912b9744e..8304d45625 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -5,17 +5,20 @@ # # This document describes all commands currently supported by QMP. # -# Most of the time their usage is exactly the same as in the user Monitor, this -# means that any other document which also describe commands (the manpage, -# QEMU's manual, etc) can and should be consulted. +# Most of the time their usage is exactly the same as in the user +# Monitor, this means that any other document which also describe +# commands (the manpage, QEMU's manual, etc) can and should be +# consulted. # -# QMP has two types of commands: regular and query commands. Regular commands -# usually change the Virtual Machine's state someway, while query commands just -# return information. The sections below are divided accordingly. +# QMP has two types of commands: regular and query commands. Regular +# commands usually change the Virtual Machine's state someway, while +# query commands just return information. The sections below are +# divided accordingly. # -# It's important to observe that all communication examples are formatted in -# a reader-friendly way, so that they're easier to understand. However, in real -# protocol usage, they're emitted as a single line. +# It's important to observe that all communication examples are +# formatted in a reader-friendly way, so that they're easier to +# understand. However, in real protocol usage, they're emitted as a +# single line. # # Also, the following notation is used to denote data flow: # @@ -26,30 +29,9 @@ # -> data issued by the Client # <- Server data response # -# Please, refer to the QMP specification (docs/interop/qmp-spec.txt) for -# detailed information on the Server command and response formats. -# -# = Stability Considerations -# -# The current QMP command set (described in this file) may be useful for a -# number of use cases, however it's limited and several commands have bad -# defined semantics, specially with regard to command completion. -# -# These problems are going to be solved incrementally in the next QEMU releases -# and we're going to establish a deprecation policy for badly defined commands. -# -# If you're planning to adopt QMP, please observe the following: -# -# 1. The deprecation policy will take effect and be documented soon, please -# check the documentation of each used command as soon as a new release of -# QEMU is available -# -# 2. DO NOT rely on anything which is not explicit documented -# -# 3. Errors, in special, are not documented. Applications should NOT check -# for specific errors classes or data (it's strongly recommended to only -# check for the "error" key) -# +# Please refer to the +# :doc:`QEMU Machine Protocol Specification ` +# for detailed information on the Server command and response formats. ## { 'include': 'pragma.json' } @@ -65,12 +47,13 @@ { 'include': 'sockets.json' } { 'include': 'run-state.json' } { 'include': 'crypto.json' } +{ 'include': 'job.json' } { 'include': 'block.json' } { 'include': 'block-export.json' } { 'include': 'char.json' } { 'include': 'dump.json' } -{ 'include': 'job.json' } { 'include': 'net.json' } +{ 'include': 'ebpf.json' } { 'include': 'rdma.json' } { 'include': 'rocker.json' } { 'include': 'tpm.json' } @@ -84,6 +67,7 @@ { 'include': 'introspect.json' } { 'include': 'qom.json' } { 'include': 'qdev.json' } +{ 'include': 'machine-common.json' } { 'include': 'machine.json' } { 'include': 'machine-target.json' } { 'include': 'replay.json' } @@ -93,3 +77,7 @@ { 'include': 'audio.json' } { 'include': 'acpi.json' } { 'include': 'pci.json' } +{ 'include': 'stats.json' } +{ 'include': 'virtio.json' } +{ 'include': 'cryptodev.json' } +{ 'include': 'cxl.json' } diff --git a/qapi/qapi-type-helpers.c b/qapi/qapi-type-helpers.c index f76b34f647..266da013ad 100644 --- a/qapi/qapi-type-helpers.c +++ b/qapi/qapi-type-helpers.c @@ -21,3 +21,17 @@ HumanReadableText *human_readable_text_from_str(GString *str) return ret; } + +char **strv_from_str_list(const strList *list) +{ + const strList *tail; + int i = 0; + char **strv = g_new(char *, QAPI_LIST_LENGTH(list) + 1); + + for (tail = list; tail != NULL; tail = tail->next) { + strv[i++] = g_strdup(tail->value); + } + strv[i] = NULL; + + return strv; +} diff --git a/qapi/qapi-util.c b/qapi/qapi-util.c index 63596e11c5..65a7d18437 100644 --- a/qapi/qapi-util.c +++ b/qapi/qapi-util.c @@ -112,7 +112,7 @@ bool qapi_bool_parse(const char *name, const char *value, bool *obj, Error **err * It may be prefixed by __RFQDN_ (downstream extension), where RFQDN * may contain only letters, digits, hyphen and period. * The special exception for enumeration names is not implemented. - * See docs/devel/qapi-code-gen.txt for more on QAPI naming rules. + * See docs/devel/qapi-code-gen.rst for more on QAPI naming rules. * Keep this consistent with scripts/qapi-gen.py! * If @complete, the parse fails unless it consumes @str completely. * Return its length on success, -1 on failure. diff --git a/qapi/qdev.json b/qapi/qdev.json index 26cd10106b..facaa0bc6a 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -17,11 +17,12 @@ # # @typename: the type name of a device # -# Returns: a list of ObjectPropertyInfo describing a devices properties +# 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. +# 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. # # Since: 1.2 ## @@ -41,36 +42,38 @@ # @id: the device's ID, must be unique # # Features: -# @json-cli: If present, the "-device" command line option supports JSON -# syntax with a structure identical to the arguments of this -# command. -# @json-cli-hotplug: If present, the "-device" command line option supports JSON -# syntax without the reference counting leak that broke -# hot-unplug +# +# @json-cli: If present, the "-device" command line option supports +# JSON syntax with a structure identical to the arguments of this +# command. +# +# @json-cli-hotplug: If present, the "-device" command line option +# supports JSON syntax without the reference counting leak that +# broke hot-unplug # # Notes: # -# Additional arguments depend on the type. +# 1. Additional arguments depend on the type. # -# 1. For detailed information about this command, please refer to the -# 'docs/qdev-device-use.txt' file. +# 2. For detailed information about this command, please refer to +# the 'docs/qdev-device-use.txt' file. # -# 2. It's possible to list device properties by running QEMU with the -# "-device DEVICE,help" command-line argument, where DEVICE is the -# device's name +# 3. It's possible to list device properties by running QEMU with +# the "-device DEVICE,help" command-line argument, where DEVICE +# is the device's name # # Example: # -# -> { "execute": "device_add", -# "arguments": { "driver": "e1000", "id": "net1", -# "bus": "pci.0", -# "mac": "52:54:00:12:34:56" } } -# <- { "return": {} } +# -> { "execute": "device_add", +# "arguments": { "driver": "e1000", "id": "net1", +# "bus": "pci.0", +# "mac": "52:54:00:12:34:56" } } +# <- { "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 ## @@ -86,39 +89,40 @@ # # @id: the device's ID or QOM path # -# Returns: Nothing on success -# If @id is not a valid device, DeviceNotFound +# Errors: +# - If @id is not a valid device, DeviceNotFound # -# Notes: When this command completes, the device may not be removed from the -# guest. Hot removal is an operation that requires guest cooperation. -# This command merely requests that the guest begin the hot removal -# process. Completion of the device removal process is signaled with a -# DEVICE_DELETED event. Guest reset will automatically complete removal -# for all devices. If a guest-side error in the hot removal process is -# detected, the device will not be removed and a DEVICE_UNPLUG_GUEST_ERROR -# event is sent. Some errors cannot be detected. +# Notes: When this command completes, the device may not be removed +# from the guest. Hot removal is an operation that requires guest +# cooperation. This command merely requests that the guest begin +# the hot removal process. Completion of the device removal +# process is signaled with a DEVICE_DELETED event. Guest reset +# will automatically complete removal for all devices. If a +# guest-side error in the hot removal process is detected, the +# device will not be removed and a DEVICE_UNPLUG_GUEST_ERROR event +# is sent. Some errors cannot be detected. # # Since: 0.14 # -# Example: +# Examples: # -# -> { "execute": "device_del", -# "arguments": { "id": "net1" } } -# <- { "return": {} } -# -# -> { "execute": "device_del", -# "arguments": { "id": "/machine/peripheral-anon/device[0]" } } -# <- { "return": {} } +# -> { "execute": "device_del", +# "arguments": { "id": "net1" } } +# <- { "return": {} } # +# -> { "execute": "device_del", +# "arguments": { "id": "/machine/peripheral-anon/device[0]" } } +# <- { "return": {} } ## { 'command': 'device_del', 'data': {'id': 'str'} } ## # @DEVICE_DELETED: # -# Emitted whenever the device removal completion is acknowledged by the guest. -# At this point, it's safe to reuse the specified device ID. Device removal can -# be initiated by the guest or by HMP/QMP commands. +# Emitted whenever the device removal completion is acknowledged by +# the guest. At this point, it's safe to reuse the specified device +# ID. Device removal can be initiated by the guest or by HMP/QMP +# commands. # # @device: the device's ID if it has one # @@ -128,11 +132,10 @@ # # Example: # -# <- { "event": "DEVICE_DELETED", -# "data": { "device": "virtio-net-pci-0", -# "path": "/machine/peripheral/virtio-net-pci-0" }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } -# +# <- { "event": "DEVICE_DELETED", +# "data": { "device": "virtio-net-pci-0", +# "path": "/machine/peripheral/virtio-net-pci-0" }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'DEVICE_DELETED', 'data': { '*device': 'str', 'path': 'str' } } @@ -140,7 +143,8 @@ ## # @DEVICE_UNPLUG_GUEST_ERROR: # -# Emitted when a device hot unplug fails due to a guest reported error. +# Emitted when a device hot unplug fails due to a guest reported +# error. # # @device: the device's ID if it has one # @@ -150,12 +154,10 @@ # # Example: # -# <- { "event": "DEVICE_UNPLUG_GUEST_ERROR" -# "data": { "device": "core1", -# "path": "/machine/peripheral/core1" }, -# }, -# "timestamp": { "seconds": 1615570772, "microseconds": 202844 } } -# +# <- { "event": "DEVICE_UNPLUG_GUEST_ERROR", +# "data": { "device": "core1", +# "path": "/machine/peripheral/core1" }, +# "timestamp": { "seconds": 1615570772, "microseconds": 202844 } } ## { 'event': 'DEVICE_UNPLUG_GUEST_ERROR', 'data': { '*device': 'str', 'path': 'str' } } diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 0990873ec8..f3488afeef 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -134,8 +134,8 @@ static void do_qmp_dispatch_bh(void *opaque) * Runs outside of coroutine context for OOB commands, but in coroutine * context for everything else. */ -QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, - bool allow_oob, Monitor *cur_mon) +QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *request, + bool allow_oob, Monitor *cur_mon) { Error *err = NULL; bool oob; @@ -206,9 +206,28 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, assert(!(oob && qemu_in_coroutine())); assert(monitor_cur() == NULL); if (!!(cmd->options & QCO_COROUTINE) == qemu_in_coroutine()) { + if (qemu_in_coroutine()) { + /* + * Move the coroutine from iohandler_ctx to qemu_aio_context for + * executing the command handler so that it can make progress if it + * involves an AIO_WAIT_WHILE(). + */ + aio_co_reschedule_self(qemu_get_aio_context()); + } + monitor_set_cur(qemu_coroutine_self(), cur_mon); cmd->fn(args, &ret, &err); monitor_set_cur(qemu_coroutine_self(), NULL); + + if (qemu_in_coroutine()) { + /* + * Yield and reschedule so the main loop stays responsive. + * + * Move back to iohandler_ctx so that nested event loops for + * qemu_aio_context don't start new monitor commands. + */ + aio_co_reschedule_self(iohandler_get_aio_context()); + } } else { /* * Actual context doesn't match the one the command needs. @@ -232,7 +251,7 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, .errp = &err, .co = qemu_coroutine_self(), }; - aio_bh_schedule_oneshot(qemu_get_aio_context(), do_qmp_dispatch_bh, + aio_bh_schedule_oneshot(iohandler_get_aio_context(), do_qmp_dispatch_bh, &data); qemu_coroutine_yield(); } diff --git a/qapi/qom.json b/qapi/qom.json index 6a653c6636..85e6b4f84a 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -18,17 +18,20 @@ # # @name: the name of the property # -# @type: the type of the property. This will typically come in one of four -# forms: +# @type: the type of the property. This will typically come in one of +# four forms: # -# 1) A primitive type such as 'u8', 'u16', 'bool', 'str', or 'double'. -# These types are mapped to the appropriate JSON type. +# 1) A primitive type such as 'u8', 'u16', 'bool', 'str', or +# 'double'. These types are mapped to the appropriate JSON +# type. # -# 2) A child type in the form 'child' where subtype is a qdev -# device type name. Child properties create the composition tree. +# 2) A child type in the form 'child' where subtype is a +# qdev device type name. Child properties create the +# composition tree. # -# 3) A link type in the form 'link' where subtype is a qdev -# device type name. Link properties form the device model graph. +# 3) A link type in the form 'link' where subtype is a +# qdev device type name. Link properties form the device model +# graph. # # @description: if specified, the description of the property. # @@ -45,26 +48,25 @@ ## # @qom-list: # -# This command will list any properties of a object given a path in the object -# model. +# This command will list any properties of a object given a path in +# the object model. # -# @path: the path within the object model. See @qom-get for a description of -# this parameter. +# @path: the path within the object model. See @qom-get for a +# description of this parameter. # -# Returns: a list of @ObjectPropertyInfo that describe the properties of the -# object. +# Returns: a list of @ObjectPropertyInfo that describe the properties +# of the object. # # Since: 1.2 # # Example: # -# -> { "execute": "qom-list", -# "arguments": { "path": "/chardevs" } } -# <- { "return": [ { "name": "type", "type": "string" }, -# { "name": "parallel0", "type": "child" }, -# { "name": "serial0", "type": "child" }, -# { "name": "mon0", "type": "child" } ] } -# +# -> { "execute": "qom-list", +# "arguments": { "path": "/chardevs" } } +# <- { "return": [ { "name": "type", "type": "string" }, +# { "name": "parallel0", "type": "child" }, +# { "name": "serial0", "type": "child" }, +# { "name": "mon0", "type": "child" } ] } ## { 'command': 'qom-list', 'data': { 'path': 'str' }, @@ -74,51 +76,49 @@ ## # @qom-get: # -# This command will get a property from a object model path and return the -# value. +# This command will get a property from a object model path and return +# the value. # -# @path: The path within the object model. There are two forms of supported -# paths--absolute and partial paths. +# @path: The path within the object model. There are two forms of +# supported paths--absolute and partial paths. # -# Absolute paths are derived from the root object and can follow child<> -# or link<> properties. Since they can follow link<> properties, they -# can be arbitrarily long. Absolute paths look like absolute filenames -# and are prefixed with a leading slash. +# Absolute paths are derived from the root object and can follow +# child<> or link<> properties. Since they can follow link<> +# properties, they can be arbitrarily long. Absolute paths look +# like absolute filenames and are prefixed with a leading slash. # -# Partial paths look like relative filenames. They do not begin -# with a prefix. The matching rules for partial paths are subtle but -# designed to make specifying objects easy. At each level of the -# composition tree, the partial path is matched as an absolute path. -# The first match is not returned. At least two matches are searched -# for. A successful result is only returned if only one match is -# found. If more than one match is found, a flag is return to -# indicate that the match was ambiguous. +# Partial paths look like relative filenames. They do not begin +# with a prefix. The matching rules for partial paths are subtle +# but designed to make specifying objects easy. At each level of +# the composition tree, the partial path is matched as an absolute +# path. The first match is not returned. At least two matches +# are searched for. A successful result is only returned if only +# one match is found. If more than one match is found, a flag is +# return to indicate that the match was ambiguous. # # @property: The property name to read # -# Returns: The property value. The type depends on the property -# type. child<> and link<> properties are returned as #str -# pathnames. All integer property types (u8, u16, etc) are -# returned as #int. +# Returns: The property value. The type depends on the property type. +# child<> and link<> properties are returned as #str pathnames. +# All integer property types (u8, u16, etc) are returned as #int. # # Since: 1.2 # -# Example: +# Examples: # -# 1. Use absolute path +# 1. Use absolute path # -# -> { "execute": "qom-get", -# "arguments": { "path": "/machine/unattached/device[0]", -# "property": "hotplugged" } } -# <- { "return": false } +# -> { "execute": "qom-get", +# "arguments": { "path": "/machine/unattached/device[0]", +# "property": "hotplugged" } } +# <- { "return": false } # -# 2. Use partial path -# -# -> { "execute": "qom-get", -# "arguments": { "path": "unattached/sysbus", -# "property": "type" } } -# <- { "return": "System" } +# 2. Use partial path # +# -> { "execute": "qom-get", +# "arguments": { "path": "unattached/sysbus", +# "property": "type" } } +# <- { "return": "System" } ## { 'command': 'qom-get', 'data': { 'path': 'str', 'property': 'str' }, @@ -134,19 +134,18 @@ # # @property: the property name to set # -# @value: a value who's type is appropriate for the property type. See @qom-get -# for a description of type mapping. +# @value: a value who's type is appropriate for the property type. +# See @qom-get for a description of type mapping. # # Since: 1.2 # # Example: # -# -> { "execute": "qom-set", -# "arguments": { "path": "/machine", -# "property": "graphics", -# "value": false } } -# <- { "return": {} } -# +# -> { "execute": "qom-set", +# "arguments": { "path": "/machine", +# "property": "graphics", +# "value": false } } +# <- { "return": {} } ## { 'command': 'qom-set', 'data': { 'path': 'str', 'property': 'str', 'value': 'any' }, @@ -160,7 +159,7 @@ # @name: the type name found in the search # # @abstract: the type is abstract and can't be directly instantiated. -# Omitted if false. (since 2.10) +# Omitted if false. (since 2.10) # # @parent: Name of parent type, if any (since 2.10) # @@ -174,11 +173,13 @@ # # This command will return a list of types given search parameters # -# @implements: if specified, only return types that implement this type name +# @implements: if specified, only return types that implement this +# type name # # @abstract: if true, include abstract types in the results # -# Returns: a list of @ObjectTypeInfo or an empty list if no results are found +# Returns: a list of @ObjectTypeInfo or an empty list if no results +# are found # # Since: 1.1 ## @@ -194,9 +195,9 @@ # # @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. +# 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. # # Returns: a list of ObjectPropertyInfo describing object properties # @@ -214,7 +215,8 @@ # # @if: interface name of the host system CAN bus to connect to # -# @canbus: object ID of the can-bus object to connect to the host interface +# @canbus: object ID of the can-bus object to connect to the host +# interface # # Since: 2.12 ## @@ -227,34 +229,35 @@ # # Properties for colo-compare objects. # -# @primary_in: name of the character device backend to use for the primary -# input (incoming packets are redirected to @outdev) +# @primary_in: name of the character device backend to use for the +# primary input (incoming packets are redirected to @outdev) # -# @secondary_in: name of the character device backend to use for secondary -# input (incoming packets are only compared to the input on -# @primary_in and then dropped) +# @secondary_in: name of the character device backend to use for +# secondary input (incoming packets are only compared to the input +# on @primary_in and then dropped) # # @outdev: name of the character device backend to use for output # # @iothread: name of the iothread to run in # -# @notify_dev: name of the character device backend to be used to communicate -# with the remote colo-frame (only for Xen COLO) +# @notify_dev: name of the character device backend to be used to +# communicate with the remote colo-frame (only for Xen COLO) # -# @compare_timeout: the maximum time to hold a packet from @primary_in for -# comparison with an incoming packet on @secondary_in in -# milliseconds (default: 3000) +# @compare_timeout: the maximum time to hold a packet from @primary_in +# for comparison with an incoming packet on @secondary_in in +# milliseconds (default: 3000) # -# @expired_scan_cycle: the interval at which colo-compare checks whether -# packets from @primary have timed out, in milliseconds -# (default: 3000) +# @expired_scan_cycle: the interval at which colo-compare checks +# whether packets from @primary have timed out, in milliseconds +# (default: 3000) # -# @max_queue_size: the maximum number of packets to keep in the queue for -# comparing with incoming packets from @secondary_in. If the -# queue is full and additional packets are received, the -# additional packets are dropped. (default: 1024) +# @max_queue_size: the maximum number of packets to keep in the queue +# for comparing with incoming packets from @secondary_in. If the +# queue is full and additional packets are received, the +# additional packets are dropped. (default: 1024) # -# @vnet_hdr_support: if true, vnet header support is enabled (default: false) +# @vnet_hdr_support: if true, vnet header support is enabled +# (default: false) # # Since: 2.8 ## @@ -272,24 +275,31 @@ ## # @CryptodevBackendProperties: # -# Properties for cryptodev-backend and cryptodev-backend-builtin objects. +# Properties for cryptodev-backend and cryptodev-backend-builtin +# objects. # -# @queues: the number of queues for the cryptodev backend. Ignored for -# cryptodev-backend and must be 1 for cryptodev-backend-builtin. -# (default: 1) +# @queues: the number of queues for the cryptodev backend. Ignored +# for cryptodev-backend and must be 1 for +# cryptodev-backend-builtin. (default: 1) +# +# @throttle-bps: limit total bytes per second (Since 8.0) +# +# @throttle-ops: limit total operations per second (Since 8.0) # # Since: 2.8 ## { 'struct': 'CryptodevBackendProperties', - 'data': { '*queues': 'uint32' } } + 'data': { '*queues': 'uint32', + '*throttle-bps': 'uint64', + '*throttle-ops': 'uint64' } } ## # @CryptodevVhostUserProperties: # # Properties for cryptodev-vhost-user objects. # -# @chardev: the name of a Unix domain socket character device that connects to -# the vhost-user server +# @chardev: the name of a Unix domain socket character device that +# connects to the vhost-user server # # Since: 2.12 ## @@ -304,8 +314,8 @@ # # @addr: the name of the DBus bus to connect to # -# @id-list: a comma separated list of DBus IDs of helpers whose data should be -# included in the VM state on migration +# @id-list: a comma separated list of DBus IDs of helpers whose data +# should be included in the VM state on migration # # Since: 5.0 ## @@ -316,7 +326,8 @@ ## # @NetfilterInsert: # -# Indicates where to insert a netfilter relative to a given other filter. +# Indicates where to insert a netfilter relative to a given other +# filter. # # @before: insert before the specified filter # @@ -336,20 +347,20 @@ # # @queue: indicates which queue(s) to filter (default: all) # -# @status: indicates whether the filter is enabled ("on") or disabled ("off") -# (default: "on") +# @status: indicates whether the filter is enabled ("on") or disabled +# ("off") (default: "on") # -# @position: specifies where the filter should be inserted in the filter list. -# "head" means the filter is inserted at the head of the filter list, -# before any existing filters. -# "tail" means the filter is inserted at the tail of the filter list, -# behind any existing filters (default). -# "id=" means the filter is inserted before or behind the filter -# specified by , depending on the @insert property. -# (default: "tail") +# @position: specifies where the filter should be inserted in the +# filter list. "head" means the filter is inserted at the head of +# the filter list, before any existing filters. "tail" means the +# filter is inserted at the tail of the filter list, behind any +# existing filters (default). "id=" means the filter is +# inserted before or behind the filter specified by , +# depending on the @insert property. (default: "tail") # -# @insert: where to insert the filter relative to the filter given in @position. -# Ignored if @position is "head" or "tail". (default: behind) +# @insert: where to insert the filter relative to the filter given in +# @position. Ignored if @position is "head" or "tail". +# (default: behind) # # Since: 2.5 ## @@ -365,8 +376,9 @@ # # Properties for filter-buffer objects. # -# @interval: a non-zero interval in microseconds. All packets arriving in the -# given interval are delayed until the end of the interval. +# @interval: a non-zero interval in microseconds. All packets +# arriving in the given interval are delayed until the end of the +# interval. # # Since: 2.5 ## @@ -381,7 +393,8 @@ # # @file: the filename where the dumped packets should be stored # -# @maxlen: maximum number of bytes in a packet that are stored (default: 65536) +# @maxlen: maximum number of bytes in a packet that are stored +# (default: 65536) # # Since: 2.5 ## @@ -395,10 +408,11 @@ # # Properties for filter-mirror objects. # -# @outdev: the name of a character device backend to which all incoming packets -# are mirrored +# @outdev: the name of a character device backend to which all +# incoming packets are mirrored # -# @vnet_hdr_support: if true, vnet header support is enabled (default: false) +# @vnet_hdr_support: if true, vnet header support is enabled +# (default: false) # # Since: 2.6 ## @@ -412,16 +426,17 @@ # # Properties for filter-redirector objects. # -# At least one of @indev or @outdev must be present. If both are present, they -# must not refer to the same character device backend. +# At least one of @indev or @outdev must be present. If both are +# present, they must not refer to the same character device backend. # -# @indev: the name of a character device backend from which packets are -# received and redirected to the filtered network device +# @indev: the name of a character device backend from which packets +# are received and redirected to the filtered network device # -# @outdev: the name of a character device backend to which all incoming packets -# are redirected +# @outdev: the name of a character device backend to which all +# incoming packets are redirected # -# @vnet_hdr_support: if true, vnet header support is enabled (default: false) +# @vnet_hdr_support: if true, vnet header support is enabled +# (default: false) # # Since: 2.6 ## @@ -436,7 +451,8 @@ # # Properties for filter-rewriter objects. # -# @vnet_hdr_support: if true, vnet header support is enabled (default: false) +# @vnet_hdr_support: if true, vnet header support is enabled +# (default: false) # # Since: 2.8 ## @@ -449,17 +465,18 @@ # # Properties for input-barrier objects. # -# @name: the screen name as declared in the screens section of barrier.conf +# @name: the screen name as declared in the screens section of +# barrier.conf # # @server: hostname of the Barrier server (default: "localhost") # # @port: TCP port of the Barrier server (default: "24800") # # @x-origin: x coordinate of the leftmost pixel on the guest screen -# (default: "0") +# (default: "0") # # @y-origin: y coordinate of the topmost pixel on the guest screen -# (default: "0") +# (default: "0") # # @width: the width of secondary screen in pixels (default: "1920") # @@ -483,13 +500,13 @@ # # @evdev: the path of the host evdev device to use # -# @grab_all: if true, grab is toggled for all devices (e.g. both keyboard and -# mouse) instead of just one device (default: false) +# @grab_all: if true, grab is toggled for all devices (e.g. both +# keyboard and mouse) instead of just one device (default: false) # # @repeat: enables auto-repeat events (default: false) # # @grab-toggle: the key or key combination that toggles device grab -# (default: ctrl-ctrl) +# (default: ctrl-ctrl) # # Since: 2.6 ## @@ -504,15 +521,15 @@ # # Common properties for event loops # -# @aio-max-batch: maximum number of requests in a batch for the AIO engine, -# 0 means that the engine will use its default. -# (default: 0) +# @aio-max-batch: maximum number of requests in a batch for the AIO +# engine, 0 means that the engine will use its default. +# (default: 0) # -# @thread-pool-min: minimum number of threads reserved in the thread pool -# (default:0) +# @thread-pool-min: minimum number of threads reserved in the thread +# pool (default:0) # -# @thread-pool-max: maximum number of threads the thread pool can contain -# (default:64) +# @thread-pool-max: maximum number of threads the thread pool can +# contain (default:64) # # Since: 7.1 ## @@ -526,17 +543,17 @@ # # Properties for iothread objects. # -# @poll-max-ns: the maximum number of nanoseconds to busy wait for events. -# 0 means polling is disabled (default: 32768 on POSIX hosts, -# 0 otherwise) +# @poll-max-ns: the maximum number of nanoseconds to busy wait for +# events. 0 means polling is disabled (default: 32768 on POSIX +# hosts, 0 otherwise) # -# @poll-grow: the multiplier used to increase the polling time when the -# algorithm detects it is missing events due to not polling long -# enough. 0 selects a default behaviour (default: 0) +# @poll-grow: the multiplier used to increase the polling time when +# the algorithm detects it is missing events due to not polling +# long enough. 0 selects a default behaviour (default: 0) # # @poll-shrink: the divisor used to decrease the polling time when the -# algorithm detects it is spending too long polling without -# encountering events. 0 selects a default behaviour (default: 0) +# algorithm detects it is spending too long polling without +# encountering events. 0 selects a default behaviour (default: 0) # # The @aio-max-batch option is available since 6.1. # @@ -564,11 +581,11 @@ # # Properties for objects of classes derived from memory-backend. # -# @merge: if true, mark the memory as mergeable (default depends on the machine -# type) +# @merge: if true, mark the memory as mergeable (default depends on +# the machine type) # -# @dump: if true, include the memory in core dumps (default depends on the -# machine type) +# @dump: if true, include the memory in core dumps (default depends on +# the machine type) # # @host-nodes: the list of NUMA host nodes to bind the memory to # @@ -576,28 +593,31 @@ # # @prealloc: if true, preallocate memory (default: false) # -# @prealloc-threads: number of CPU threads to use for prealloc (default: 1) +# @prealloc-threads: number of CPU threads to use for prealloc +# (default: 1) # -# @share: if false, the memory is private to QEMU; if true, it is shared -# (default: false) +# @prealloc-context: thread context to use for creation of +# preallocation threads (default: none) (since 7.2) +# +# @share: if false, the memory is private to QEMU; if true, it is +# shared (default: false) # # @reserve: if true, reserve swap space (or huge pages) if applicable -# (default: true) (since 6.1) +# (default: true) (since 6.1) # # @size: size of the memory region in bytes # -# @x-use-canonical-path-for-ramblock-id: if true, the canoncial path is used -# for ramblock-id. Disable this for 4.0 -# machine types or older to allow -# migration with newer QEMU versions. -# (default: false generally, -# but true for machine types <= 4.0) +# @x-use-canonical-path-for-ramblock-id: if true, the canonical path +# is used for ramblock-id. Disable this for 4.0 machine types or +# older to allow migration with newer QEMU versions. +# (default: false generally, but true for machine types <= 4.0) # -# Note: prealloc=true and reserve=false cannot be set at the same time. With -# reserve=true, the behavior depends on the operating system: for example, -# Linux will not reserve swap space for shared file mappings -- -# "not applicable". In contrast, reserve=false will bail out if it cannot -# be configured accordingly. +# Note: prealloc=true and reserve=false cannot be set at the same +# time. With reserve=true, the behavior depends on the operating +# system: for example, Linux will not reserve swap space for +# shared file mappings -- "not applicable". In contrast, +# reserve=false will bail out if it cannot be configured +# accordingly. # # Since: 2.1 ## @@ -608,6 +628,7 @@ '*policy': 'HostMemPolicy', '*prealloc': 'bool', '*prealloc-threads': 'uint32', + '*prealloc-context': 'str', '*share': 'bool', '*reserve': 'bool', 'size': 'size', @@ -618,37 +639,61 @@ # # Properties for memory-backend-file objects. # -# @align: the base address alignment when QEMU mmap(2)s @mem-path. Some -# backend stores specified by @mem-path require an alignment different -# than the default one used by QEMU, e.g. the device DAX /dev/dax0.0 -# requires 2M alignment rather than 4K. In such cases, users can -# specify the required alignment via this option. -# 0 selects a default alignment (currently the page size). (default: 0) +# @align: the base address alignment when QEMU mmap(2)s @mem-path. +# Some backend stores specified by @mem-path require an alignment +# different than the default one used by QEMU, e.g. the device DAX +# /dev/dax0.0 requires 2M alignment rather than 4K. In such cases, +# users can specify the required alignment via this option. 0 +# selects a default alignment (currently the page size). +# (default: 0) # -# @discard-data: if true, the 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. (default: false) +# @offset: the offset into the target file that the region starts at. +# You can use this option to back multiple regions with a single +# file. Must be a multiple of the page size. +# (default: 0) (since 8.1) # -# @mem-path: the path to either a shared memory or huge page filesystem mount +# @discard-data: if true, the 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. (default: false) # -# @pmem: 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). +# @mem-path: the path to either a shared memory or huge page +# filesystem mount # -# @readonly: if true, the backing file is opened read-only; if false, it is -# opened read-write. (default: false) +# @pmem: 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). +# +# @readonly: if true, the backing file is opened read-only; if false, +# it is opened read-write. (default: false) +# +# @rom: whether to create Read Only Memory (ROM) that cannot be +# modified by the VM. Any write attempts to such ROM will be +# denied. Most use cases want writable RAM instead of ROM. +# However, selected use cases, like R/O NVDIMMs, can benefit from +# ROM. If set to 'on', create ROM; if set to 'off', create +# writable RAM; if set to 'auto', the value of the @readonly +# property is used. This property is primarily helpful when we +# want to have proper RAM in configurations that would +# traditionally create ROM before this property was introduced: VM +# templating, where we want to open a file readonly (@readonly set +# to true) and mark the memory to be private for QEMU (@share set +# to false). For this use case, we need writable RAM instead of +# ROM, and want to set this property to 'off'. (default: auto, +# since 8.2) # # Since: 2.1 ## { 'struct': 'MemoryBackendFileProperties', 'base': 'MemoryBackendProperties', 'data': { '*align': 'size', + '*offset': 'size', '*discard-data': 'bool', 'mem-path': 'str', '*pmem': { 'type': 'bool', 'if': 'CONFIG_LIBPMEM' }, - '*readonly': 'bool' } } + '*readonly': 'bool', + '*rom': 'OnOffAuto' } } ## # @MemoryBackendMemfdProperties: @@ -657,16 +702,16 @@ # # The @share boolean option is true by default with memfd. # -# @hugetlb: if true, the file to be created resides in the hugetlbfs filesystem -# (default: false) +# @hugetlb: if true, the file to be created resides in the hugetlbfs +# filesystem (default: false) # -# @hugetlbsize: the hugetlb page size on systems that support multiple hugetlb -# page sizes (it must be a power of 2 value supported by the -# system). 0 selects a default page size. This option is ignored -# if @hugetlb is false. (default: 0) +# @hugetlbsize: the hugetlb page size on systems that support multiple +# hugetlb page sizes (it must be a power of 2 value supported by +# the system). 0 selects a default page size. This option is +# ignored if @hugetlb is false. (default: 0) # -# @seal: if true, create a sealed-file, which will block further resizing of -# the memory (default: true) +# @seal: if true, create a sealed-file, which will block further +# resizing of the memory (default: true) # # Since: 2.12 ## @@ -698,7 +743,8 @@ # # Properties for pr-manager-helper objects. # -# @path: the path to a Unix domain socket for connecting to the external helper +# @path: the path to a Unix domain socket for connecting to the +# external helper # # Since: 2.11 ## @@ -727,25 +773,73 @@ # # @fd: file descriptor name previously passed via 'getfd' command # -# @devid: the id of the device to be associated with the file descriptor +# @devid: the id of the device to be associated with the file +# descriptor # # Since: 6.0 ## { 'struct': 'RemoteObjectProperties', 'data': { 'fd': 'str', 'devid': 'str' } } +## +# @VfioUserServerProperties: +# +# Properties for x-vfio-user-server objects. +# +# @socket: socket to be used by the libvfio-user library +# +# @device: the ID of the device to be emulated at the server +# +# Since: 7.1 +## +{ 'struct': 'VfioUserServerProperties', + 'data': { 'socket': 'SocketAddress', 'device': 'str' } } + +## +# @IOMMUFDProperties: +# +# Properties for iommufd objects. +# +# @fd: file descriptor name previously passed via 'getfd' command, +# which represents a pre-opened /dev/iommu. This allows the +# iommufd object to be shared across several subsystems (VFIO, +# VDPA, ...), and the file descriptor to be shared with other +# process, e.g. DPDK. (default: QEMU opens /dev/iommu by itself) +# +# Since: 9.0 +## +{ 'struct': 'IOMMUFDProperties', + 'data': { '*fd': 'str' } } + +## +# @AcpiGenericInitiatorProperties: +# +# Properties for acpi-generic-initiator objects. +# +# @pci-dev: PCI device ID to be associated with the node +# +# @node: NUMA node associated with the PCI device +# +# Since: 9.0 +## +{ 'struct': 'AcpiGenericInitiatorProperties', + 'data': { 'pci-dev': 'str', + 'node': 'uint32' } } + ## # @RngProperties: # # Properties for objects of classes derived from rng. # -# @opened: if true, the device is opened immediately when applying this option -# and will probably fail when processing the next option. Don't use; -# only provided for compatibility. (default: false) +# @opened: if true, the device is opened immediately when applying +# this option and will probably fail when processing the next +# option. Don't use; only provided for compatibility. +# (default: false) # # Features: -# @deprecated: Member @opened is deprecated. Setting true doesn't make sense, -# and false is already the default. +# +# @deprecated: Member @opened is deprecated. Setting true doesn't +# make sense, and false is already the default. # # Since: 1.3 ## @@ -757,8 +851,8 @@ # # Properties for rng-egd objects. # -# @chardev: the name of a character device backend that provides the connection -# to the RNG daemon +# @chardev: the name of a character device backend that provides the +# connection to the RNG daemon # # Since: 1.3 ## @@ -771,8 +865,8 @@ # # Properties for rng-random objects. # -# @filename: the filename of the device on the host to obtain entropy from -# (default: "/dev/urandom") +# @filename: the filename of the device on the host to obtain entropy +# from (default: "/dev/urandom") # # Since: 1.3 ## @@ -798,11 +892,11 @@ # @cbitpos: C-bit location in page table entry (default: 0) # # @reduced-phys-bits: number of bits in physical addresses that become -# unavailable when SEV is enabled +# unavailable when SEV is enabled # # @kernel-hashes: if true, add hashes of kernel/initrd/cmdline to a -# designated guest firmware page for measured boot -# with -kernel (default: false) (since 6.2) +# designated guest firmware page for measured boot with -kernel +# (default: false) (since 6.2) # # Since: 2.12 ## @@ -816,16 +910,40 @@ 'reduced-phys-bits': 'uint32', '*kernel-hashes': 'bool' } } +## +# @ThreadContextProperties: +# +# Properties for thread context objects. +# +# @cpu-affinity: the list of host CPU numbers used as CPU affinity for +# all threads created in the thread context (default: QEMU main +# thread CPU affinity) +# +# @node-affinity: the list of host node numbers that will be resolved +# to a list of host CPU numbers used as CPU affinity. This is a +# shortcut for specifying the list of host CPU numbers belonging +# to the host nodes manually by setting @cpu-affinity. +# (default: QEMU main thread affinity) +# +# Since: 7.2 +## +{ 'struct': 'ThreadContextProperties', + 'data': { '*cpu-affinity': ['uint16'], + '*node-affinity': ['uint16'] } } + + ## # @ObjectType: # # Features: +# # @unstable: Member @x-remote-object is experimental. # # Since: 6.0 ## { 'enum': 'ObjectType', 'data': [ + 'acpi-generic-initiator', 'authz-list', 'authz-listfile', 'authz-pam', @@ -836,6 +954,7 @@ 'colo-compare', 'cryptodev-backend', 'cryptodev-backend-builtin', + 'cryptodev-backend-lkcf', { 'name': 'cryptodev-vhost-user', 'if': 'CONFIG_VHOST_CRYPTO' }, 'dbus-vmstate', @@ -848,6 +967,7 @@ 'input-barrier', { 'name': 'input-linux', 'if': 'CONFIG_LINUX' }, + 'iommufd', 'iothread', 'main-loop', { 'name': 'memory-backend-epc', @@ -868,13 +988,15 @@ { 'name': 'secret_keyring', 'if': 'CONFIG_SECRET_KEYRING' }, 'sev-guest', + 'thread-context', 's390-pv-guest', 'throttle-group', 'tls-creds-anon', 'tls-creds-psk', 'tls-creds-x509', 'tls-cipher-suites', - { 'name': 'x-remote-object', 'features': [ 'unstable' ] } + { 'name': 'x-remote-object', 'features': [ 'unstable' ] }, + { 'name': 'x-vfio-user-server', 'features': [ 'unstable' ] } ] } ## @@ -893,6 +1015,7 @@ 'id': 'str' }, 'discriminator': 'qom-type', 'data': { + 'acpi-generic-initiator': 'AcpiGenericInitiatorProperties', 'authz-list': 'AuthZListProperties', 'authz-listfile': 'AuthZListFileProperties', 'authz-pam': 'AuthZPAMProperties', @@ -902,6 +1025,7 @@ 'colo-compare': 'ColoCompareProperties', 'cryptodev-backend': 'CryptodevBackendProperties', 'cryptodev-backend-builtin': 'CryptodevBackendProperties', + 'cryptodev-backend-lkcf': 'CryptodevBackendProperties', 'cryptodev-vhost-user': { 'type': 'CryptodevVhostUserProperties', 'if': 'CONFIG_VHOST_CRYPTO' }, 'dbus-vmstate': 'DBusVMStateProperties', @@ -914,6 +1038,7 @@ 'input-barrier': 'InputBarrierProperties', 'input-linux': { 'type': 'InputLinuxProperties', 'if': 'CONFIG_LINUX' }, + 'iommufd': 'IOMMUFDProperties', 'iothread': 'IothreadProperties', 'main-loop': 'MainLoopProperties', 'memory-backend-epc': { 'type': 'MemoryBackendEpcProperties', @@ -933,12 +1058,14 @@ 'secret_keyring': { 'type': 'SecretKeyringProperties', 'if': 'CONFIG_SECRET_KEYRING' }, 'sev-guest': 'SevGuestProperties', + 'thread-context': 'ThreadContextProperties', 'throttle-group': 'ThrottleGroupProperties', 'tls-creds-anon': 'TlsCredsAnonProperties', 'tls-creds-psk': 'TlsCredsPskProperties', 'tls-creds-x509': 'TlsCredsX509Properties', 'tls-cipher-suites': 'TlsCredsProperties', - 'x-remote-object': 'RemoteObjectProperties' + 'x-remote-object': 'RemoteObjectProperties', + 'x-vfio-user-server': 'VfioUserServerProperties' } } ## @@ -946,18 +1073,17 @@ # # Create a QOM object. # -# Returns: Nothing on success -# Error if @qom-type is not a valid class name +# Errors: +# - Error if @qom-type is not a valid class name # # Since: 2.0 # # Example: # -# -> { "execute": "object-add", -# "arguments": { "qom-type": "rng-random", "id": "rng1", -# "filename": "/dev/hwrng" } } -# <- { "return": {} } -# +# -> { "execute": "object-add", +# "arguments": { "qom-type": "rng-random", "id": "rng1", +# "filename": "/dev/hwrng" } } +# <- { "return": {} } ## { 'command': 'object-add', 'data': 'ObjectOptions', 'boxed': true, 'allow-preconfig': true } @@ -969,16 +1095,15 @@ # # @id: the name of the QOM object to remove # -# Returns: Nothing on success -# Error if @id is not a valid id for a QOM object +# Errors: +# - Error if @id is not a valid id for a QOM object # # Since: 2.0 # # Example: # -# -> { "execute": "object-del", "arguments": { "id": "rng1" } } -# <- { "return": {} } -# +# -> { "execute": "object-del", "arguments": { "id": "rng1" } } +# <- { "return": {} } ## { 'command': 'object-del', 'data': {'id': 'str'}, 'allow-preconfig': true } diff --git a/qapi/rdma.json b/qapi/rdma.json index a1d2175a8b..195c001850 100644 --- a/qapi/rdma.json +++ b/qapi/rdma.json @@ -17,20 +17,19 @@ # # @subnet-prefix: Subnet Prefix # -# @interface-id : Interface ID +# @interface-id: Interface ID # # Since: 4.0 # # Example: # -# <- {"timestamp": {"seconds": 1541579657, "microseconds": 986760}, -# "event": "RDMA_GID_STATUS_CHANGED", -# "data": -# {"netdev": "bridge0", -# "interface-id": 15880512517475447892, -# "gid-status": true, -# "subnet-prefix": 33022}} -# +# <- {"timestamp": {"seconds": 1541579657, "microseconds": 986760}, +# "event": "RDMA_GID_STATUS_CHANGED", +# "data": +# {"netdev": "bridge0", +# "interface-id": 15880512517475447892, +# "gid-status": true, +# "subnet-prefix": 33022}} ## { 'event': 'RDMA_GID_STATUS_CHANGED', 'data': { 'netdev' : 'str', diff --git a/qapi/replay.json b/qapi/replay.json index 729470300d..d3559f9c8f 100644 --- a/qapi/replay.json +++ b/qapi/replay.json @@ -13,13 +13,13 @@ # # Mode of the replay subsystem. # -# @none: normal execution mode. Replay or record are not enabled. +# @none: normal execution mode. Replay or record are not enabled. # -# @record: record mode. All non-deterministic data is written into the -# replay log. +# @record: record mode. All non-deterministic data is written into +# the replay log. # -# @play: replay mode. Non-deterministic data required for system execution -# is read from the log. +# @play: replay mode. Non-deterministic data required for system +# execution is read from the log. # # Since: 2.5 ## @@ -33,9 +33,8 @@ # # @mode: current mode. # -# @filename: name of the record/replay log file. -# It is present only in record or replay modes, when the log -# is recorded or replayed. +# @filename: name of the record/replay log file. It is present only +# in record or replay modes, when the log is recorded or replayed. # # @icount: current number of executed instructions. # @@ -47,9 +46,9 @@ ## # @query-replay: # -# Retrieve the record/replay information. -# It includes current instruction count which may be used for -# @replay-break and @replay-seek commands. +# Retrieve the record/replay information. It includes current +# instruction count which may be used for @replay-break and +# @replay-seek commands. # # Returns: record/replay information. # @@ -57,9 +56,8 @@ # # Example: # -# -> { "execute": "query-replay" } -# <- { "return": { "mode": "play", "filename": "log.rr", "icount": 220414 } } -# +# -> { "execute": "query-replay" } +# <- { "return": { "mode": "play", "filename": "log.rr", "icount": 220414 } } ## { 'command': 'query-replay', 'returns': 'ReplayInfo' } @@ -67,12 +65,12 @@ ## # @replay-break: # -# Set replay breakpoint at instruction count @icount. -# Execution stops when the specified instruction is reached. -# There can be at most one breakpoint. When breakpoint is set, any prior -# one is removed. The breakpoint may be set only in replay mode and only -# "in the future", i.e. at instruction counts greater than the current one. -# The current instruction count can be observed with @query-replay. +# Set replay breakpoint at instruction count @icount. Execution stops +# when the specified instruction is reached. There can be at most one +# breakpoint. When breakpoint is set, any prior one is removed. The +# breakpoint may be set only in replay mode and only "in the future", +# i.e. at instruction counts greater than the current one. The +# current instruction count can be observed with @query-replay. # # @icount: instruction count to stop at # @@ -80,23 +78,23 @@ # # Example: # -# -> { "execute": "replay-break", "arguments": { "icount": 220414 } } -# +# -> { "execute": "replay-break", "arguments": { "icount": 220414 } } +# <- { "return": {} } ## { 'command': 'replay-break', 'data': { 'icount': 'int' } } ## # @replay-delete-break: # -# Remove replay breakpoint which was set with @replay-break. -# The command is ignored when there are no replay breakpoints. +# Remove replay breakpoint which was set with @replay-break. The +# command is ignored when there are no replay breakpoints. # # Since: 5.2 # # Example: # -# -> { "execute": "replay-delete-break" } -# +# -> { "execute": "replay-delete-break" } +# <- { "return": {} } ## { 'command': 'replay-delete-break' } @@ -104,11 +102,11 @@ # @replay-seek: # # Automatically proceed to the instruction count @icount, when -# replaying the execution. The command automatically loads nearest +# replaying the execution. The command automatically loads nearest # snapshot and replays the execution to find the desired instruction. -# When there is no preceding snapshot or the execution is not replayed, -# then the command fails. -# icount for the reference may be obtained with @query-replay command. +# When there is no preceding snapshot or the execution is not +# replayed, then the command fails. Instruction count can be obtained +# with the @query-replay command. # # @icount: target instruction count # @@ -116,6 +114,7 @@ # # Example: # -# -> { "execute": "replay-seek", "arguments": { "icount": 220414 } } +# -> { "execute": "replay-seek", "arguments": { "icount": 220414 } } +# <- { "return": {} } ## { 'command': 'replay-seek', 'data': { 'icount': 'int' } } diff --git a/qapi/rocker.json b/qapi/rocker.json index b48e49a89b..5635cf174f 100644 --- a/qapi/rocker.json +++ b/qapi/rocker.json @@ -32,9 +32,8 @@ # # Example: # -# -> { "execute": "query-rocker", "arguments": { "name": "sw1" } } -# <- { "return": {"name": "sw1", "ports": 2, "id": 1327446905938}} -# +# -> { "execute": "query-rocker", "arguments": { "name": "sw1" } } +# <- { "return": {"name": "sw1", "ports": 2, "id": 1327446905938}} ## { 'command': 'query-rocker', 'data': { 'name': 'str' }, @@ -101,13 +100,12 @@ # # Example: # -# -> { "execute": "query-rocker-ports", "arguments": { "name": "sw1" } } -# <- { "return": [ {"duplex": "full", "enabled": true, "name": "sw1.1", -# "autoneg": "off", "link-up": true, "speed": 10000}, -# {"duplex": "full", "enabled": true, "name": "sw1.2", -# "autoneg": "off", "link-up": true, "speed": 10000} -# ]} -# +# -> { "execute": "query-rocker-ports", "arguments": { "name": "sw1" } } +# <- { "return": [ {"duplex": "full", "enabled": true, "name": "sw1.1", +# "autoneg": "off", "link-up": true, "speed": 10000}, +# {"duplex": "full", "enabled": true, "name": "sw1.2", +# "autoneg": "off", "link-up": true, "speed": 10000} +# ]} ## { 'command': 'query-rocker-ports', 'data': { 'name': 'str' }, @@ -141,7 +139,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 ## @@ -171,7 +169,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 ## @@ -198,7 +196,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,8 +233,8 @@ # # @name: switch name # -# @tbl-id: flow table ID. If tbl-id is not specified, returns -# flow information for all tables. +# @tbl-id: flow table ID. If tbl-id is not specified, returns flow +# information for all tables. # # Returns: rocker OF-DPA flow information # @@ -244,17 +242,16 @@ # # Example: # -# -> { "execute": "query-rocker-of-dpa-flows", -# "arguments": { "name": "sw1" } } -# <- { "return": [ {"key": {"in-pport": 0, "priority": 1, "tbl-id": 0}, -# "hits": 138, -# "cookie": 0, -# "action": {"goto-tbl": 10}, -# "mask": {"in-pport": 4294901760} -# }, -# {...more...}, -# ]} -# +# -> { "execute": "query-rocker-of-dpa-flows", +# "arguments": { "name": "sw1" } } +# <- { "return": [ {"key": {"in-pport": 0, "priority": 1, "tbl-id": 0}, +# "hits": 138, +# "cookie": 0, +# "action": {"goto-tbl": 10}, +# "mask": {"in-pport": 4294901760} +# }, +# {...more...}, +# ]} ## { 'command': 'query-rocker-of-dpa-flows', 'data': { 'name': 'str', '*tbl-id': 'uint32' }, @@ -292,7 +289,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,8 +308,8 @@ # # @name: switch name # -# @type: group type. If type is not specified, returns -# group information for all group types. +# @type: group type. If type is not specified, returns group +# information for all group types. # # Returns: rocker OF-DPA group information # @@ -320,22 +317,21 @@ # # Example: # -# -> { "execute": "query-rocker-of-dpa-groups", -# "arguments": { "name": "sw1" } } -# <- { "return": [ {"type": 0, "out-pport": 2, -# "pport": 2, "vlan-id": 3841, -# "pop-vlan": 1, "id": 251723778}, -# {"type": 0, "out-pport": 0, -# "pport": 0, "vlan-id": 3841, -# "pop-vlan": 1, "id": 251723776}, -# {"type": 0, "out-pport": 1, -# "pport": 1, "vlan-id": 3840, -# "pop-vlan": 1, "id": 251658241}, -# {"type": 0, "out-pport": 0, -# "pport": 0, "vlan-id": 3840, -# "pop-vlan": 1, "id": 251658240} -# ]} -# +# -> { "execute": "query-rocker-of-dpa-groups", +# "arguments": { "name": "sw1" } } +# <- { "return": [ {"type": 0, "out-pport": 2, +# "pport": 2, "vlan-id": 3841, +# "pop-vlan": 1, "id": 251723778}, +# {"type": 0, "out-pport": 0, +# "pport": 0, "vlan-id": 3841, +# "pop-vlan": 1, "id": 251723776}, +# {"type": 0, "out-pport": 1, +# "pport": 1, "vlan-id": 3840, +# "pop-vlan": 1, "id": 251658241}, +# {"type": 0, "out-pport": 0, +# "pport": 0, "vlan-id": 3840, +# "pop-vlan": 1, "id": 251658240} +# ]} ## { 'command': 'query-rocker-of-dpa-groups', 'data': { 'name': 'str', '*type': 'uint8' }, diff --git a/qapi/run-state.json b/qapi/run-state.json index 6e2162d7b3..f8773f23b2 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -16,16 +16,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 +# @internal-error: An internal error that prevents further guest +# execution has occurred # -# @io-error: the last IOP has failed and the device is configured to pause -# on I/O errors +# @io-error: the last IOP has failed and the device is configured to +# pause on I/O errors # # @paused: guest has been paused via the 'stop' command # @@ -43,13 +43,15 @@ # # @suspended: guest is suspended (ACPI S3) # -# @watchdog: the watchdog action is configured to pause and has been triggered +# @watchdog: the watchdog action is configured to pause and has been +# triggered # -# @guest-panicked: guest has been panicked as a result of guest OS panic +# @guest-panicked: guest has been panicked as a result of guest OS +# panic # -# @colo: guest is paused to save/restore VM state under colo checkpoint, -# VM can not get into this state unless colo capability is enabled -# for migration. (since 2.8) +# @colo: guest is paused to save/restore VM state under colo +# checkpoint, VM can not get into this state unless colo +# capability is enabled for migration. (since 2.8) ## { 'enum': 'RunState', 'data': [ 'debug', 'inmigrate', 'internal-error', 'io-error', 'paused', @@ -75,58 +77,57 @@ # @host-ui: Reaction to a UI event, like window close # # @guest-shutdown: Guest shutdown/suspend request, via ACPI or other -# hardware-specific means +# hardware-specific means # # @guest-reset: Guest reset request, and command line turns that into -# a shutdown +# a shutdown # -# @guest-panic: Guest panicked, and command line turns that into a shutdown +# @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 +# @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 # +# @snapshot-load: A snapshot is being loaded by the record & replay +# subsystem. This value is used only within QEMU. It doesn't +# occur in QMP. (since 7.2) ## { 'enum': 'ShutdownCause', # Beware, shutdown_caused_by_guest() depends on enumeration order 'data': [ 'none', 'host-error', 'host-qmp-quit', 'host-qmp-system-reset', 'host-signal', 'host-ui', 'guest-shutdown', 'guest-reset', - 'guest-panic', 'subsystem-reset'] } + 'guest-panic', 'subsystem-reset', 'snapshot-load'] } ## # @StatusInfo: # -# Information about VCPU run state +# Information about VM run state # # @running: true if all VCPUs are runnable, false if not runnable # -# @singlestep: true if VCPUs are in single-step mode -# # @status: the virtual machine @RunState # # Since: 0.14 -# -# Notes: @singlestep is enabled through the GDB stub ## { 'struct': 'StatusInfo', - 'data': {'running': 'bool', 'singlestep': 'bool', 'status': 'RunState'} } + 'data': {'running': 'bool', + 'status': 'RunState'} } ## # @query-status: # -# Query the run status of all VCPUs +# Query the run status of the VM # -# Returns: @StatusInfo reflecting all VCPUs +# Returns: @StatusInfo reflecting the VM # # Since: 0.14 # # Example: # -# -> { "execute": "query-status" } -# <- { "return": { "running": true, -# "singlestep": false, -# "status": "running" } } -# +# -> { "execute": "query-status" } +# <- { "return": { "running": true, +# "status": "running" } } ## { 'command': 'query-status', 'returns': 'StatusInfo', 'allow-preconfig': true } @@ -134,42 +135,43 @@ ## # @SHUTDOWN: # -# Emitted when the virtual machine has shut down, indicating that qemu is -# about to exit. +# Emitted when the virtual machine has shut down, indicating that qemu +# is 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) +# @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) # -# @reason: The @ShutdownCause which resulted in the SHUTDOWN. (since 4.0) +# @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 +# 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 # # Since: 0.12 # # Example: # -# <- { "event": "SHUTDOWN", -# "data": { "guest": true, "reason": "guest-shutdown" }, -# "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } -# +# <- { "event": "SHUTDOWN", +# "data": { "guest": true, "reason": "guest-shutdown" }, +# "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } ## { 'event': 'SHUTDOWN', 'data': { 'guest': 'bool', 'reason': 'ShutdownCause' } } ## # @POWERDOWN: # -# Emitted when the virtual machine is powered down through the power control -# system, such as via ACPI. +# Emitted when the virtual machine is powered down through the power +# control system, such as via ACPI. # # Since: 0.12 # # Example: # -# <- { "event": "POWERDOWN", -# "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } -# +# <- { "event": "POWERDOWN", +# "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } ## { 'event': 'POWERDOWN' } @@ -179,20 +181,19 @@ # 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) +# @reason: The @ShutdownCause of the RESET. (since 4.0) # # Since: 0.12 # # Example: # -# <- { "event": "RESET", -# "data": { "guest": false, "reason": "guest-reset" }, -# "timestamp": { "seconds": 1267041653, "microseconds": 9518 } } -# +# <- { "event": "RESET", +# "data": { "guest": false, "reason": "guest-reset" }, +# "timestamp": { "seconds": 1267041653, "microseconds": 9518 } } ## { 'event': 'RESET', 'data': { 'guest': 'bool', 'reason': 'ShutdownCause' } } @@ -205,9 +206,8 @@ # # Example: # -# <- { "event": "STOP", -# "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } -# +# <- { "event": "STOP", +# "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } ## { 'event': 'STOP' } @@ -220,58 +220,57 @@ # # Example: # -# <- { "event": "RESUME", -# "timestamp": { "seconds": 1271770767, "microseconds": 582542 } } -# +# <- { "event": "RESUME", +# "timestamp": { "seconds": 1271770767, "microseconds": 582542 } } ## { 'event': 'RESUME' } ## # @SUSPEND: # -# Emitted when guest enters a hardware suspension state, for example, S3 state, -# which is sometimes called standby state +# Emitted when guest enters a hardware suspension state, for example, +# S3 state, which is sometimes called standby state # # Since: 1.1 # # Example: # -# <- { "event": "SUSPEND", -# "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } -# +# <- { "event": "SUSPEND", +# "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } ## { 'event': 'SUSPEND' } ## # @SUSPEND_DISK: # -# Emitted when guest enters a hardware suspension state with data saved on -# disk, for example, S4 state, which is sometimes called hibernate state +# Emitted when guest enters a hardware suspension state with data +# saved on disk, for example, S4 state, which is sometimes called +# hibernate state # -# Note: QEMU shuts down (similar to event @SHUTDOWN) when entering this state +# Note: QEMU shuts down (similar to event @SHUTDOWN) when entering +# this state # # Since: 1.2 # # Example: # -# <- { "event": "SUSPEND_DISK", -# "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } -# +# <- { "event": "SUSPEND_DISK", +# "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } ## { 'event': 'SUSPEND_DISK' } ## # @WAKEUP: # -# Emitted when the guest has woken up from suspend state and is running +# Emitted when the guest has woken up from suspend state and is +# running # # Since: 1.1 # # Example: # -# <- { "event": "WAKEUP", -# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } -# +# <- { "event": "WAKEUP", +# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } ## { 'event': 'WAKEUP' } @@ -282,8 +281,9 @@ # # @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 +# Note: If action is "reset", "shutdown", or "pause" the WATCHDOG +# event is followed respectively by the RESET, SHUTDOWN, or STOP +# events # # Note: This event is rate-limited. # @@ -291,10 +291,9 @@ # # Example: # -# <- { "event": "WATCHDOG", -# "data": { "action": "reset" }, -# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } -# +# <- { "event": "WATCHDOG", +# "data": { "action": "reset" }, +# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } ## { 'event': 'WATCHDOG', 'data': { 'action': 'WatchdogAction' } } @@ -302,13 +301,13 @@ ## # @WatchdogAction: # -# An enumeration of the actions taken when the watchdog device's timer is -# expired +# An enumeration of the actions taken when the watchdog device's timer +# is expired # # @reset: system resets # -# @shutdown: system shutdown, note that it is similar to @powerdown, which -# tries to set to system status and notify guest +# @shutdown: system shutdown, note that it is similar to @powerdown, +# which tries to set to system status and notify guest # # @poweroff: system poweroff, the emulator program exits # @@ -318,8 +317,8 @@ # # @none: nothing is done # -# @inject-nmi: a non-maskable interrupt is injected into the first VCPU (all -# VCPUS on x86) (since 2.4) +# @inject-nmi: a non-maskable interrupt is injected into the first +# VCPU (all VCPUS on x86) (since 2.4) # # Since: 2.1 ## @@ -334,7 +333,8 @@ # # @reset: Reset the VM # -# @shutdown: Shutdown the VM and exit, according to the shutdown action +# @shutdown: Shutdown the VM and exit, according to the shutdown +# action # # Since: 6.0 ## @@ -362,27 +362,39 @@ # # @pause: Pause the VM # -# @shutdown: Shutdown the VM and exit, according to the shutdown action +# @shutdown: Shutdown the VM and exit, according to the shutdown +# action +# +# @exit-failure: Shutdown the VM and exit with nonzero status (since +# 7.1) # # Since: 6.0 ## { 'enum': 'PanicAction', - 'data': [ 'pause', 'shutdown', 'none' ] } + 'data': [ 'pause', 'shutdown', 'exit-failure', 'none' ] } ## # @watchdog-set-action: # -# Set watchdog action +# Set watchdog action. +# +# @action: @WatchdogAction action taken when watchdog timer expires. # # Since: 2.11 +# +# Example: +# +# -> { "execute": "watchdog-set-action", +# "arguments": { "action": "inject-nmi" } } +# <- { "return": {} } ## { 'command': 'watchdog-set-action', 'data' : {'action': 'WatchdogAction'} } ## # @set-action: # -# Set the actions that will be taken by the emulator in response to guest -# events. +# Set the actions that will be taken by the emulator in response to +# guest events. # # @reboot: @RebootAction action taken on guest reboot. # @@ -390,20 +402,18 @@ # # @panic: @PanicAction action taken on guest panic. # -# @watchdog: @WatchdogAction action taken when watchdog timer expires . -# -# Returns: Nothing on success. +# @watchdog: @WatchdogAction action taken when watchdog timer expires. # # Since: 6.0 # # Example: # -# -> { "execute": "set-action", -# "arguments": { "reboot": "shutdown", -# "shutdown" : "pause", -# "panic": "pause", -# "watchdog": "inject-nmi" } } -# <- { "return": {} } +# -> { "execute": "set-action", +# "arguments": { "reboot": "shutdown", +# "shutdown" : "pause", +# "panic": "pause", +# "watchdog": "inject-nmi" } } +# <- { "return": {} } ## { 'command': 'set-action', 'data': { '*reboot': 'RebootAction', @@ -425,10 +435,9 @@ # # Example: # -# <- { "event": "GUEST_PANICKED", -# "data": { "action": "pause" }, -# "timestamp": { "seconds": 1648245231, "microseconds": 900001 } } -# +# <- { "event": "GUEST_PANICKED", +# "data": { "action": "pause" }, +# "timestamp": { "seconds": 1648245231, "microseconds": 900001 } } ## { 'event': 'GUEST_PANICKED', 'data': { 'action': 'GuestPanicAction', '*info': 'GuestPanicInformation' } } @@ -446,10 +455,9 @@ # # Example: # -# <- { "event": "GUEST_CRASHLOADED", -# "data": { "action": "run" }, -# "timestamp": { "seconds": 1648245259, "microseconds": 893771 } } -# +# <- { "event": "GUEST_CRASHLOADED", +# "data": { "action": "run" }, +# "timestamp": { "seconds": 1648245259, "microseconds": 893771 } } ## { 'event': 'GUEST_CRASHLOADED', 'data': { 'action': 'GuestPanicAction', '*info': 'GuestPanicInformation' } } @@ -461,7 +469,11 @@ # # @pause: system pauses # -# Since: 2.1 (poweroff since 2.8, run since 5.0) +# @poweroff: system powers off (since 2.8) +# +# @run: system continues to run (since 5.0) +# +# Since: 2.1 ## { 'enum': 'GuestPanicAction', 'data': [ 'pause', 'poweroff', 'run' ] } @@ -492,22 +504,38 @@ {'union': 'GuestPanicInformation', 'base': {'type': 'GuestPanicInformationType'}, 'discriminator': 'type', - 'data': { 'hyper-v': 'GuestPanicInformationHyperV', - 's390': 'GuestPanicInformationS390' } } + 'data': {'hyper-v': 'GuestPanicInformationHyperV', + 's390': 'GuestPanicInformationS390'}} ## # @GuestPanicInformationHyperV: # # Hyper-V specific guest panic information (HV crash MSRs) # +# @arg1: for Windows, STOP code for the guest crash. For Linux, +# an error code. +# +# @arg2: for Windows, first argument of the STOP. For Linux, the +# guest OS ID, which has the kernel version in bits 16-47 +# and 0x8100 in bits 48-63. +# +# @arg3: for Windows, second argument of the STOP. For Linux, the +# program counter of the guest. +# +# @arg4: for Windows, third argument of the STOP. For Linux, the +# RAX register (x86) or the stack pointer (aarch64) of the guest. +# +# @arg5: for Windows, fourth argument of the STOP. For x86 Linux, the +# stack pointer of the guest. +# # Since: 2.9 ## {'struct': 'GuestPanicInformationHyperV', - 'data': { 'arg1': 'uint64', - 'arg2': 'uint64', - 'arg3': 'uint64', - 'arg4': 'uint64', - 'arg5': 'uint64' } } + 'data': {'arg1': 'uint64', + 'arg2': 'uint64', + 'arg3': 'uint64', + 'arg4': 'uint64', + 'arg5': 'uint64'}} ## # @S390CrashReason: @@ -518,13 +546,13 @@ # # @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 +# @extint-loop: clock comparator or cpu timer interrupt with new PSW +# enabled 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 +# @opint-loop: operation exception interrupt with invalid code at the +# program interrupt new PSW # # Since: 2.12 ## @@ -541,17 +569,20 @@ # S390 specific guest panic information (PSW) # # @core: core id of the CPU that crashed +# # @psw-mask: control fields of guest PSW +# # @psw-addr: guest instruction address +# # @reason: guest crash reason # # Since: 2.12 ## {'struct': 'GuestPanicInformationS390', - 'data': { 'core': 'uint32', - 'psw-mask': 'uint64', - 'psw-addr': 'uint64', - 'reason': 'S390CrashReason' } } + 'data': {'core': 'uint32', + 'psw-mask': 'uint64', + 'psw-addr': 'uint64', + 'reason': 'S390CrashReason'}} ## # @MEMORY_FAILURE: @@ -560,21 +591,20 @@ # # @recipient: recipient is defined as @MemoryFailureRecipient. # -# @action: action that has been taken. action is defined as @MemoryFailureAction. +# @action: action that has been taken. # -# @flags: flags for MemoryFailureAction. action is defined as @MemoryFailureFlags. +# @flags: flags for MemoryFailureAction. # # Since: 5.2 # # Example: # -# <- { "event": "MEMORY_FAILURE", -# "data": { "recipient": "hypervisor", -# "action": "fatal", -# "flags": { "action-required": false, -# "recursive": false } }, -# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } -# +# <- { "event": "MEMORY_FAILURE", +# "data": { "recipient": "hypervisor", +# "action": "fatal", +# "flags": { "action-required": false, +# "recursive": false } }, +# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } ## { 'event': 'MEMORY_FAILURE', 'data': { 'recipient': 'MemoryFailureRecipient', @@ -586,8 +616,8 @@ # # Hardware memory failure occurs, handled by recipient. # -# @hypervisor: memory failure at QEMU process address space. -# (none guest memory, but used by QEMU itself). +# @hypervisor: memory failure at QEMU process address space. (none +# guest memory, but used by QEMU itself). # # @guest: memory failure at guest memory, # @@ -602,19 +632,20 @@ # # Actions taken by QEMU in response to a hardware memory failure. # -# @ignore: the memory failure could be ignored. This will only be the case -# for action-optional failures. +# @ignore: the memory failure could be ignored. This will only be the +# case for action-optional failures. # -# @inject: memory failure occurred in guest memory, the guest enabled MCE -# handling mechanism, and QEMU could inject the MCE into the guest -# successfully. +# @inject: memory failure occurred in guest memory, the guest enabled +# MCE handling mechanism, and QEMU could inject the MCE into the +# guest successfully. # -# @fatal: the failure is unrecoverable. This occurs for action-required -# failures if the recipient is the hypervisor; QEMU will exit. +# @fatal: the failure is unrecoverable. This occurs for +# action-required failures if the recipient is the hypervisor; +# QEMU will exit. # -# @reset: the failure is unrecoverable but confined to the guest. This -# occurs if the recipient is a guest guest which is not ready -# to handle memory failures. +# @reset: the failure is unrecoverable but confined to the guest. +# This occurs if the recipient is a guest guest which is not ready +# to handle memory failures. # # Since: 5.2 ## @@ -630,13 +661,31 @@ # Additional information on memory failures. # # @action-required: whether a memory failure event is action-required -# or action-optional (e.g. a failure during memory scrub). +# or action-optional (e.g. a failure during memory scrub). # -# @recursive: whether the failure occurred while the previous -# failure was still in progress. +# @recursive: whether the failure occurred while the previous failure +# was still in progress. # # Since: 5.2 ## { 'struct': 'MemoryFailureFlags', 'data': { 'action-required': 'bool', 'recursive': 'bool'} } + +## +# @NotifyVmexitOption: +# +# An enumeration of the options specified when enabling notify VM exit +# +# @run: enable the feature, do nothing and continue if the notify VM +# exit happens. +# +# @internal-error: enable the feature, raise a internal error if the +# notify VM exit happens. +# +# @disable: disable the feature. +# +# Since: 7.2 +## +{ 'enum': 'NotifyVmexitOption', + 'data': [ 'run', 'internal-error', 'disable' ] } diff --git a/qapi/sockets.json b/qapi/sockets.json index bad74e34d3..aa97c89768 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -5,8 +5,6 @@ # = Socket data types ## -{ 'include': 'common.json' } - ## # @NetworkAddressFamily: # @@ -41,23 +39,26 @@ ## # @InetSocketAddress: # -# Captures a socket address or address range in the Internet namespace. +# Captures a socket address or address range in the Internet +# namespace. # -# @numeric: true if the host/port are guaranteed to be numeric, -# false if name resolution should be attempted. Defaults to false. -# (Since 2.9) +# @numeric: true if the host/port are guaranteed to be numeric, false +# if name resolution should be attempted. Defaults to false. +# (Since 2.9) # # @to: If present, this is range of possible addresses, with port -# between @port and @to. +# between @port and @to. # -# @ipv4: whether to accept IPv4 addresses, default try both IPv4 and IPv6 +# @ipv4: whether to accept IPv4 addresses, default try both IPv4 and +# IPv6 # -# @ipv6: whether to accept IPv6 addresses, default try both IPv4 and IPv6 +# @ipv6: whether to accept IPv6 addresses, default try both IPv4 and +# IPv6 # -# @keep-alive: enable keep-alive when connecting to this socket. Not supported -# for passive sockets. (Since 4.2) +# @keep-alive: enable keep-alive when connecting to this socket. Not +# supported for passive sockets. (Since 4.2) # -# @mptcp: enable multi-path TCP. (Since 6.1) +# @mptcp: enable multi-path TCP. (Since 6.1) # # Since: 1.3 ## @@ -77,12 +78,14 @@ # Captures a socket address in the local ("Unix socket") namespace. # # @path: filesystem path to use +# # @abstract: if true, this is a Linux abstract socket address. @path -# will be prefixed by a null byte, and optionally padded -# with null bytes. Defaults to false. (Since 5.1) +# will be prefixed by a null byte, and optionally padded with null +# bytes. Defaults to false. (Since 5.1) +# # @tight: if false, pad an abstract socket address with enough null -# bytes to make it fill struct sockaddr_un member sun_path. -# Defaults to true. (Since 5.1) +# bytes to make it fill struct sockaddr_un member sun_path. +# Defaults to true. (Since 5.1) # # Since: 1.3 ## @@ -98,10 +101,11 @@ # Captures a socket address in the vsock namespace. # # @cid: unique host identifier +# # @port: port # # Note: string types are used to allow for possible future hostname or -# service resolution support. +# service resolution support. # # Since: 2.8 ## @@ -110,9 +114,28 @@ 'cid': 'str', 'port': 'str' } } +## +# @FdSocketAddress: +# +# A file descriptor name or number. +# +# @str: decimal is for file descriptor number, otherwise it's a file +# descriptor name. Named file descriptors are permitted in +# monitor commands, in combination with the 'getfd' command. +# Decimal file descriptors are permitted at startup or other +# contexts where no monitor context is active. +# +# Since: 1.2 +## +{ 'struct': 'FdSocketAddress', + 'data': { + 'str': 'str' } } + ## # @InetSocketAddressWrapper: # +# @data: internet domain socket address +# # Since: 1.3 ## { 'struct': 'InetSocketAddressWrapper', @@ -121,6 +144,8 @@ ## # @UnixSocketAddressWrapper: # +# @data: UNIX domain socket address +# # Since: 1.3 ## { 'struct': 'UnixSocketAddressWrapper', @@ -129,27 +154,34 @@ ## # @VsockSocketAddressWrapper: # +# @data: VSOCK domain socket address +# # Since: 2.8 ## { 'struct': 'VsockSocketAddressWrapper', 'data': { 'data': 'VsockSocketAddress' } } ## -# @StringWrapper: +# @FdSocketAddressWrapper: +# +# @data: file descriptor name or number # # Since: 1.3 ## -{ 'struct': 'StringWrapper', - 'data': { 'data': 'String' } } +{ 'struct': 'FdSocketAddressWrapper', + 'data': { 'data': 'FdSocketAddress' } } ## # @SocketAddressLegacy: # -# Captures the address of a socket, which could also be a named file descriptor +# Captures the address of a socket, which could also be a named file +# descriptor +# +# @type: Transport type # # Note: This type is deprecated in favor of SocketAddress. The -# difference between SocketAddressLegacy and SocketAddress is that the -# latter has fewer {} on the wire. +# difference between SocketAddressLegacy and SocketAddress is that +# the latter has fewer {} on the wire. # # Since: 1.3 ## @@ -160,7 +192,7 @@ 'inet': 'InetSocketAddressWrapper', 'unix': 'UnixSocketAddressWrapper', 'vsock': 'VsockSocketAddressWrapper', - 'fd': 'StringWrapper' } } + 'fd': 'FdSocketAddressWrapper' } } ## # @SocketAddressType: @@ -173,10 +205,7 @@ # # @vsock: VMCI address # -# @fd: decimal is for file descriptor number, otherwise a file descriptor name. -# Named file descriptors are permitted in monitor commands, in combination -# with the 'getfd' command. Decimal file descriptors are permitted at -# startup or other contexts where no monitor context is active. +# @fd: Socket file descriptor # # Since: 2.9 ## @@ -186,7 +215,7 @@ ## # @SocketAddress: # -# Captures the address of a socket, which could also be a named file +# Captures the address of a socket, which could also be a socket file # descriptor # # @type: Transport type @@ -199,4 +228,4 @@ 'data': { 'inet': 'InetSocketAddress', 'unix': 'UnixSocketAddress', 'vsock': 'VsockSocketAddress', - 'fd': 'String' } } + 'fd': 'FdSocketAddress' } } diff --git a/qapi/stats.json b/qapi/stats.json new file mode 100644 index 0000000000..578b52c7ef --- /dev/null +++ b/qapi/stats.json @@ -0,0 +1,277 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# +# Copyright (c) 2022 Oracle and/or its affiliates. +# +# 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 + +## +# = Statistics +## + +## +# @StatsType: +# +# Enumeration of statistics types +# +# @cumulative: stat is cumulative; value can only increase. +# +# @instant: stat is instantaneous; value can increase or decrease. +# +# @peak: stat is the peak value; value can only increase. +# +# @linear-histogram: stat is a linear histogram. +# +# @log2-histogram: stat is a logarithmic histogram, with one bucket +# for each power of two. +# +# Since: 7.1 +## +{ 'enum' : 'StatsType', + 'data' : [ 'cumulative', 'instant', 'peak', 'linear-histogram', + 'log2-histogram' ] } + +## +# @StatsUnit: +# +# Enumeration of unit of measurement for statistics +# +# @bytes: stat reported in bytes. +# +# @seconds: stat reported in seconds. +# +# @cycles: stat reported in clock cycles. +# +# @boolean: stat is a boolean value. +# +# Since: 7.1 +## +{ 'enum' : 'StatsUnit', + 'data' : [ 'bytes', 'seconds', 'cycles', 'boolean' ] } + +## +# @StatsProvider: +# +# Enumeration of statistics providers. +# +# @kvm: since 7.1 +# +# @cryptodev: since 8.0 +# +# Since: 7.1 +## +{ 'enum': 'StatsProvider', + 'data': [ 'kvm', 'cryptodev' ] } + +## +# @StatsTarget: +# +# The kinds of objects on which one can request statistics. +# +# @vm: statistics that apply to the entire virtual machine or the +# entire QEMU process. +# +# @vcpu: statistics that apply to a single virtual CPU. +# +# @cryptodev: statistics that apply to a crypto device (since 8.0) +# +# Since: 7.1 +## +{ 'enum': 'StatsTarget', + 'data': [ 'vm', 'vcpu', 'cryptodev' ] } + +## +# @StatsRequest: +# +# Indicates a set of statistics that should be returned by +# query-stats. +# +# @provider: provider for which to return statistics. +# +# @names: statistics to be returned (all if omitted). +# +# Since: 7.1 +## +{ 'struct': 'StatsRequest', + 'data': { 'provider': 'StatsProvider', + '*names': [ 'str' ] } } + +## +# @StatsVCPUFilter: +# +# @vcpus: list of QOM paths for the desired vCPU objects. +# +# Since: 7.1 +## +{ 'struct': 'StatsVCPUFilter', + 'data': { '*vcpus': [ 'str' ] } } + +## +# @StatsFilter: +# +# The arguments to the query-stats command; specifies a target for +# which to request statistics and optionally the required subset of +# information for that target. +# +# @target: the kind of objects to query. Note that each possible +# target may enable additional filtering options +# +# @providers: which providers to request statistics from, and optionally +# which named values to return within each provider +# +# Since: 7.1 +## +{ 'union': 'StatsFilter', + 'base': { + 'target': 'StatsTarget', + '*providers': [ 'StatsRequest' ] }, + 'discriminator': 'target', + 'data': { 'vcpu': 'StatsVCPUFilter' } } + +## +# @StatsValue: +# +# @scalar: single unsigned 64-bit integers. +# +# @boolean: single boolean value. +# +# @list: list of unsigned 64-bit integers (used for histograms). +# +# Since: 7.1 +## +{ 'alternate': 'StatsValue', + 'data': { 'scalar': 'uint64', + 'boolean': 'bool', + 'list': [ 'uint64' ] } } + +## +# @Stats: +# +# @name: name of stat. +# +# @value: stat value. +# +# Since: 7.1 +## +{ 'struct': 'Stats', + 'data': { 'name': 'str', + 'value' : 'StatsValue' } } + +## +# @StatsResult: +# +# @provider: provider for this set of statistics. +# +# @qom-path: Path to the object for which the statistics are returned, +# if the object is exposed in the QOM tree +# +# @stats: list of statistics. +# +# Since: 7.1 +## +{ 'struct': 'StatsResult', + 'data': { 'provider': 'StatsProvider', + '*qom-path': 'str', + 'stats': [ 'Stats' ] } } + +## +# @query-stats: +# +# Return runtime-collected statistics for objects such as the VM or +# its vCPUs. +# +# The arguments are a StatsFilter and specify the provider and objects +# to return statistics about. +# +# Returns: a list of StatsResult, one for each provider and object +# (e.g., for each vCPU). +# +# Since: 7.1 +## +{ 'command': 'query-stats', + 'data': 'StatsFilter', + 'boxed': true, + 'returns': [ 'StatsResult' ] } + +## +# @StatsSchemaValue: +# +# Schema for a single statistic. +# +# @name: name of the statistic; each element of the schema is uniquely +# identified by a target, a provider (both available in +# @StatsSchema) and the name. +# +# @type: kind of statistic. +# +# @unit: basic unit of measure for the statistic; if missing, the +# statistic is a simple number or counter. +# +# @base: base for the multiple of @unit in which the statistic is +# measured. Only present if @exponent is non-zero; @base and +# @exponent together form a SI prefix (e.g., _nano-_ for +# ``base=10`` and ``exponent=-9``) or IEC binary prefix (e.g. +# _kibi-_ for ``base=2`` and ``exponent=10``) +# +# @exponent: exponent for the multiple of @unit in which the statistic +# is expressed, or 0 for the basic unit +# +# @bucket-size: Present when @type is "linear-histogram", contains the +# width of each bucket of the histogram. +# +# Since: 7.1 +## +{ 'struct': 'StatsSchemaValue', + 'data': { 'name': 'str', + 'type': 'StatsType', + '*unit': 'StatsUnit', + '*base': 'int8', + 'exponent': 'int16', + '*bucket-size': 'uint32' } } + +## +# @StatsSchema: +# +# Schema for all available statistics for a provider and target. +# +# @provider: provider for this set of statistics. +# +# @target: the kind of object that can be queried through the +# provider. +# +# @stats: list of statistics. +# +# Since: 7.1 +## +{ 'struct': 'StatsSchema', + 'data': { 'provider': 'StatsProvider', + 'target': 'StatsTarget', + 'stats': [ 'StatsSchemaValue' ] } } + +## +# @query-stats-schemas: +# +# Return the schema for all available runtime-collected statistics. +# +# @provider: a provider to restrict the query to. +# +# Note: runtime-collected statistics and their names fall outside +# QEMU's usual deprecation policies. QEMU will try to keep the +# set of available data stable, together with their names, but +# will not guarantee stability at all costs; the same is true of +# providers that source statistics externally, e.g. from Linux. +# For example, if the same value is being tracked with different +# names on different architectures or by different providers, one +# of them might be renamed. A statistic might go away if an +# algorithm is changed or some code is removed; changing a default +# might cause previously useful statistics to always report 0. +# Such changes, however, are expected to be rare. +# +# Since: 7.1 +## +{ 'command': 'query-stats-schemas', + 'data': { '*provider': 'StatsProvider' }, + 'returns': [ 'StatsSchema' ] } diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c index 71ddc92b7b..5115536b15 100644 --- a/qapi/string-output-visitor.c +++ b/qapi/string-output-visitor.c @@ -65,6 +65,7 @@ struct StringOutputVisitor } range_start, range_end; GList *ranges; void *list; /* Only needed for sanity checking the caller */ + unsigned int struct_nesting; }; static StringOutputVisitor *to_sov(Visitor *v) @@ -74,11 +75,27 @@ static StringOutputVisitor *to_sov(Visitor *v) static void string_output_set(StringOutputVisitor *sov, char *string) { - if (sov->string) { - g_string_free(sov->string, true); + switch (sov->list_mode) { + case LM_STARTED: + sov->list_mode = LM_IN_PROGRESS; + /* fall through */ + case LM_NONE: + if (sov->string) { + g_string_free(sov->string, true); + } + sov->string = g_string_new(string); + g_free(string); + break; + + case LM_IN_PROGRESS: + case LM_END: + g_string_append(sov->string, ", "); + g_string_append(sov->string, string); + break; + + default: + abort(); } - sov->string = g_string_new(string); - g_free(string); } static void string_output_append(StringOutputVisitor *sov, int64_t a) @@ -128,6 +145,10 @@ static bool print_type_int64(Visitor *v, const char *name, int64_t *obj, StringOutputVisitor *sov = to_sov(v); GList *l; + if (sov->struct_nesting) { + return true; + } + switch (sov->list_mode) { case LM_NONE: string_output_append(sov, *obj); @@ -215,6 +236,10 @@ static bool print_type_size(Visitor *v, const char *name, uint64_t *obj, uint64_t val; char *out, *psize; + if (sov->struct_nesting) { + return true; + } + if (!sov->human) { out = g_strdup_printf("%"PRIu64, *obj); string_output_set(sov, out); @@ -234,6 +259,11 @@ static bool print_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) { StringOutputVisitor *sov = to_sov(v); + + if (sov->struct_nesting) { + return true; + } + string_output_set(sov, g_strdup(*obj ? "true" : "false")); return true; } @@ -244,6 +274,10 @@ static bool print_type_str(Visitor *v, const char *name, char **obj, StringOutputVisitor *sov = to_sov(v); char *out; + if (sov->struct_nesting) { + return true; + } + if (sov->human) { out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup(""); } else { @@ -257,6 +291,11 @@ static bool print_type_number(Visitor *v, const char *name, double *obj, Error **errp) { StringOutputVisitor *sov = to_sov(v); + + if (sov->struct_nesting) { + return true; + } + string_output_set(sov, g_strdup_printf("%.17g", *obj)); return true; } @@ -267,6 +306,10 @@ static bool print_type_null(Visitor *v, const char *name, QNull **obj, StringOutputVisitor *sov = to_sov(v); char *out; + if (sov->struct_nesting) { + return true; + } + if (sov->human) { out = g_strdup(""); } else { @@ -276,12 +319,37 @@ static bool print_type_null(Visitor *v, const char *name, QNull **obj, return true; } +static bool start_struct(Visitor *v, const char *name, void **obj, + size_t size, Error **errp) +{ + StringOutputVisitor *sov = to_sov(v); + + sov->struct_nesting++; + return true; +} + +static void end_struct(Visitor *v, void **obj) +{ + StringOutputVisitor *sov = to_sov(v); + + if (--sov->struct_nesting) { + return; + } + + /* TODO actually print struct fields */ + string_output_set(sov, g_strdup("")); +} + static bool start_list(Visitor *v, const char *name, GenericList **list, size_t size, Error **errp) { StringOutputVisitor *sov = to_sov(v); + if (sov->struct_nesting) { + return true; + } + /* we can't traverse a list in a list */ assert(sov->list_mode == LM_NONE); /* We don't support visits without a list */ @@ -299,6 +367,10 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) StringOutputVisitor *sov = to_sov(v); GenericList *ret = tail->next; + if (sov->struct_nesting) { + return ret; + } + if (ret && !ret->next) { sov->list_mode = LM_END; } @@ -309,6 +381,10 @@ static void end_list(Visitor *v, void **obj) { StringOutputVisitor *sov = to_sov(v); + if (sov->struct_nesting) { + return; + } + assert(sov->list == obj); assert(sov->list_mode == LM_STARTED || sov->list_mode == LM_END || @@ -363,6 +439,8 @@ Visitor *string_output_visitor_new(bool human, char **result) v->visitor.type_str = print_type_str; v->visitor.type_number = print_type_number; v->visitor.type_null = print_type_null; + v->visitor.start_struct = start_struct; + v->visitor.end_struct = end_struct; v->visitor.start_list = start_list; v->visitor.next_list = next_list; v->visitor.end_list = end_list; diff --git a/qapi/tpm.json b/qapi/tpm.json index 4e2ea9756a..1577b5c259 100644 --- a/qapi/tpm.json +++ b/qapi/tpm.json @@ -12,7 +12,9 @@ # An enumeration of TPM models # # @tpm-tis: TPM TIS model +# # @tpm-crb: TPM CRB model (since 2.12) +# # @tpm-spapr: TPM SPAPR model (since 5.0) # # Since: 1.5 @@ -31,9 +33,8 @@ # # Example: # -# -> { "execute": "query-tpm-models" } -# <- { "return": [ "tpm-tis", "tpm-crb", "tpm-spapr" ] } -# +# -> { "execute": "query-tpm-models" } +# <- { "return": [ "tpm-tis", "tpm-crb", "tpm-spapr" ] } ## { 'command': 'query-tpm-models', 'returns': ['TpmModel'], 'if': 'CONFIG_TPM' } @@ -44,8 +45,8 @@ # An enumeration of TPM types # # @passthrough: TPM passthrough type -# @emulator: Software Emulator TPM type -# Since: 2.11 +# +# @emulator: Software Emulator TPM type (since 2.11) # # Since: 1.5 ## @@ -63,9 +64,8 @@ # # Example: # -# -> { "execute": "query-tpm-types" } -# <- { "return": [ "passthrough", "emulator" ] } -# +# -> { "execute": "query-tpm-types" } +# <- { "return": [ "passthrough", "emulator" ] } ## { 'command': 'query-tpm-types', 'returns': ['TpmType'], 'if': 'CONFIG_TPM' } @@ -77,8 +77,8 @@ # # @path: string describing the path used for accessing the TPM device # -# @cancel-path: string showing the TPM's sysfs cancel file -# for cancellation of TPM commands while they are executing +# @cancel-path: string showing the TPM's sysfs cancel file for +# cancellation of TPM commands while they are executing # # Since: 1.5 ## @@ -102,6 +102,8 @@ ## # @TPMPassthroughOptionsWrapper: # +# @data: Information about the TPM passthrough type +# # Since: 1.5 ## { 'struct': 'TPMPassthroughOptionsWrapper', @@ -111,6 +113,8 @@ ## # @TPMEmulatorOptionsWrapper: # +# @data: Information about the TPM emulator type +# # Since: 2.11 ## { 'struct': 'TPMEmulatorOptionsWrapper', @@ -120,10 +124,14 @@ ## # @TpmTypeOptions: # -# A union referencing different TPM backend types' configuration options +# 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 ## @@ -158,28 +166,25 @@ # # Return information about the TPM device # -# Returns: @TPMInfo on success -# # Since: 1.5 # # Example: # -# -> { "execute": "query-tpm" } -# <- { "return": -# [ -# { "model": "tpm-tis", -# "options": -# { "type": "passthrough", -# "data": -# { "cancel-path": "/sys/class/misc/tpm0/device/cancel", -# "path": "/dev/tpm0" -# } -# }, -# "id": "tpm0" +# -> { "execute": "query-tpm" } +# <- { "return": +# [ +# { "model": "tpm-tis", +# "options": +# { "type": "passthrough", +# "data": +# { "cancel-path": "/sys/class/misc/tpm0/device/cancel", +# "path": "/dev/tpm0" +# } +# }, +# "id": "tpm0" +# } +# ] # } -# ] -# } -# ## { 'command': 'query-tpm', 'returns': ['TPMInfo'], 'if': 'CONFIG_TPM' } diff --git a/qapi/trace.json b/qapi/trace.json index 6c6982a587..043d12f83e 100644 --- a/qapi/trace.json +++ b/qapi/trace.json @@ -32,16 +32,20 @@ # Information of a tracing event. # # @name: Event name. +# # @state: Tracing state. +# # @vcpu: Whether this is a per-vCPU event (since 2.7). # -# An event is per-vCPU if it has the "vcpu" property in the "trace-events" -# files. +# Features: +# +# @deprecated: Member @vcpu is deprecated, and always ignored. # # Since: 2.2 ## { 'struct': 'TraceEventInfo', - 'data': {'name': 'str', 'state': 'TraceEventState', 'vcpu': 'bool'} } + 'data': {'name': 'str', 'state': 'TraceEventState', + 'vcpu': { 'type': 'bool', 'features': ['deprecated'] } } } ## # @trace-event-get-state: @@ -49,31 +53,26 @@ # Query the state of events. # # @name: Event name pattern (case-sensitive glob). -# @vcpu: The vCPU to query (any by default; since 2.7). +# +# @vcpu: The vCPU to query (since 2.7). +# +# Features: +# +# @deprecated: Member @vcpu is deprecated, and always ignored. # # 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. -# -# 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 # # Example: # -# -> { "execute": "trace-event-get-state", -# "arguments": { "name": "qemu_memalign" } } -# <- { "return": [ { "name": "qemu_memalign", "state": "disabled", "vcpu": false } ] } -# +# -> { "execute": "trace-event-get-state", +# "arguments": { "name": "qemu_memalign" } } +# <- { "return": [ { "name": "qemu_memalign", "state": "disabled", "vcpu": false } ] } ## { 'command': 'trace-event-get-state', - 'data': {'name': 'str', '*vcpu': 'int'}, + 'data': {'name': 'str', + '*vcpu': {'type': 'int', 'features': ['deprecated'] } }, 'returns': ['TraceEventInfo'] } ## @@ -82,28 +81,25 @@ # Set the dynamic tracing state of events. # # @name: Event name pattern (case-sensitive glob). +# # @enable: Whether to enable tracing. +# # @ignore-unavailable: Do not match unavailable events with @name. +# # @vcpu: The vCPU to act upon (all by default; since 2.7). # -# An event's state is modified if: -# - its name matches the @name pattern, and -# - if @vcpu is given, the event has the "vcpu" property. +# Features: # -# Therefore, if @vcpu is given, the operation will only match per-vCPU events, -# setting 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. +# @deprecated: Member @vcpu is deprecated, and always ignored. # # Since: 2.2 # # Example: # -# -> { "execute": "trace-event-set-state", -# "arguments": { "name": "qemu_memalign", "enable": true } } -# <- { "return": {} } -# +# -> { "execute": "trace-event-set-state", +# "arguments": { "name": "qemu_memalign", "enable": true } } +# <- { "return": {} } ## { 'command': 'trace-event-set-state', 'data': {'name': 'str', 'enable': 'bool', '*ignore-unavailable': 'bool', - '*vcpu': 'int'} } + '*vcpu': {'type': 'int', 'features': ['deprecated'] } } } diff --git a/qapi/transaction.json b/qapi/transaction.json index 381a2df782..5749c133d4 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -23,15 +23,15 @@ # # An enumeration of Transactional completion modes. # -# @individual: Do not attempt to cancel any other Actions if any Actions fail -# after the Transaction request succeeds. All Actions that -# can complete successfully will do so without waiting on others. -# This is the default. +# @individual: Do not attempt to cancel any other Actions if any +# Actions fail after the Transaction request succeeds. All +# Actions that can complete successfully will do so without +# waiting on others. This is the default. # -# @grouped: If any Action fails after the Transaction succeeds, cancel all -# Actions. Actions do not complete until all Actions are ready to -# complete. May be rejected by Actions that do not support this -# completion mode. +# @grouped: If any Action fails after the Transaction succeeds, cancel +# all Actions. Actions do not complete until all Actions are +# ready to complete. May be rejected by Actions that do not +# support this completion mode. # # Since: 2.5 ## @@ -42,21 +42,33 @@ # @TransactionActionKind: # # @abort: Since 1.6 +# # @block-dirty-bitmap-add: Since 2.5 +# # @block-dirty-bitmap-remove: Since 4.2 +# # @block-dirty-bitmap-clear: Since 2.5 +# # @block-dirty-bitmap-enable: Since 4.0 +# # @block-dirty-bitmap-disable: Since 4.0 +# # @block-dirty-bitmap-merge: Since 4.0 +# # @blockdev-backup: Since 2.3 +# # @blockdev-snapshot: Since 2.5 +# # @blockdev-snapshot-internal-sync: Since 1.7 +# # @blockdev-snapshot-sync: since 1.1 +# # @drive-backup: Since 1.6 # # Features: +# # @deprecated: Member @drive-backup is deprecated. Use member -# @blockdev-backup instead. +# @blockdev-backup instead. # # Since: 1.1 ## @@ -146,6 +158,8 @@ # A discriminated record of operations that can be performed with # @transaction. # +# @type: the operation to be performed +# # Since: 1.1 ## { 'union': 'TransactionAction', @@ -172,8 +186,8 @@ # Optional arguments to modify the behavior of a Transaction. # # @completion-mode: Controls how jobs launched asynchronously by -# Actions will complete or fail as a group. -# See @ActionCompletionMode for details. +# Actions will complete or fail as a group. See +# @ActionCompletionMode for details. # # Since: 2.5 ## @@ -186,70 +200,70 @@ ## # @transaction: # -# Executes a number of transactionable QMP commands atomically. If any -# operation fails, then the entire set of actions will be abandoned and the -# appropriate error returned. +# Executes a number of transactionable QMP commands atomically. If +# any operation fails, then the entire set of actions will be +# abandoned and the appropriate error returned. # -# For external snapshots, the dictionary contains the device, the file to use for -# the new snapshot, and the format. The default format, if not specified, is -# qcow2. +# For external snapshots, the dictionary contains the device, the file +# to use for the new snapshot, and the format. The default format, if +# not specified, is qcow2. # # Each new snapshot defaults to being created by QEMU (wiping any -# contents if the file already exists), but it is also possible to reuse -# an externally-created file. In the latter case, you should ensure that -# the new image file has the same contents as the current one; QEMU cannot -# perform any meaningful check. Typically this is achieved by using the -# current image file as the backing file for the new image. +# contents if the file already exists), but it is also possible to +# reuse an externally-created file. In the latter case, you should +# ensure that the new image file has the same contents as the current +# one; QEMU cannot perform any meaningful check. Typically this is +# achieved by using the current image file as the backing file for the +# new image. # # On failure, the original disks pre-snapshot attempt will be used. # # For internal snapshots, the dictionary contains the device and the -# snapshot's name. If an internal snapshot matching name already exists, -# the request will be rejected. Only some image formats support it, for -# example, qcow2, and rbd, +# snapshot's name. If an internal snapshot matching name already +# exists, the request will be rejected. Only some image formats +# support it, for example, qcow2, and rbd, # -# On failure, qemu will try delete the newly created internal snapshot in the -# transaction. When an I/O error occurs during deletion, the user needs to fix -# it later with qemu-img or other command. +# On failure, qemu will try delete the newly created internal snapshot +# in the transaction. When an I/O error occurs during deletion, the +# user needs to fix it later with qemu-img or other command. # -# @actions: List of @TransactionAction; -# information needed for the respective operations. +# @actions: List of @TransactionAction; information needed for the +# respective operations. # # @properties: structure of additional options to control the -# execution of the transaction. See @TransactionProperties -# for additional detail. +# execution of the transaction. See @TransactionProperties for +# additional detail. # -# Returns: nothing on success +# Errors: +# Any errors from commands in the transaction # -# 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. +# 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. # # Since: 1.1 # # Example: # -# -> { "execute": "transaction", -# "arguments": { "actions": [ -# { "type": "blockdev-snapshot-sync", "data" : { "device": "ide-hd0", -# "snapshot-file": "/some/place/my-image", -# "format": "qcow2" } }, -# { "type": "blockdev-snapshot-sync", "data" : { "node-name": "myfile", -# "snapshot-file": "/some/place/my-image2", -# "snapshot-node-name": "node3432", -# "mode": "existing", -# "format": "qcow2" } }, -# { "type": "blockdev-snapshot-sync", "data" : { "device": "ide-hd1", -# "snapshot-file": "/some/place/my-image2", -# "mode": "existing", -# "format": "qcow2" } }, -# { "type": "blockdev-snapshot-internal-sync", "data" : { -# "device": "ide-hd2", -# "name": "snapshot0" } } ] } } -# <- { "return": {} } -# +# -> { "execute": "transaction", +# "arguments": { "actions": [ +# { "type": "blockdev-snapshot-sync", "data" : { "device": "ide-hd0", +# "snapshot-file": "/some/place/my-image", +# "format": "qcow2" } }, +# { "type": "blockdev-snapshot-sync", "data" : { "node-name": "myfile", +# "snapshot-file": "/some/place/my-image2", +# "snapshot-node-name": "node3432", +# "mode": "existing", +# "format": "qcow2" } }, +# { "type": "blockdev-snapshot-sync", "data" : { "device": "ide-hd1", +# "snapshot-file": "/some/place/my-image2", +# "mode": "existing", +# "format": "qcow2" } }, +# { "type": "blockdev-snapshot-internal-sync", "data" : { +# "device": "ide-hd2", +# "name": "snapshot0" } } ] } } +# <- { "return": {} } ## { 'command': 'transaction', 'data': { 'actions': [ 'TransactionAction' ], diff --git a/qapi/ui.json b/qapi/ui.json index 413371d5e8..f610bce118 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -22,7 +22,8 @@ ## # @SetPasswordAction: # -# An action to take on changing a password on a connection with active clients. +# An action to take on changing a password on a connection with active +# clients. # # @keep: maintain existing clients # @@ -40,14 +41,15 @@ # # Options for set_password. # -# @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'. -# For VNC, only 'keep' is currently implemented. +# password. If nothing is specified, defaults to 'keep'. For VNC, +# only 'keep' is currently implemented. # # Since: 7.0 ## @@ -61,10 +63,10 @@ ## # @SetPasswordOptionsVnc: # -# Options for set_password specific to the VNC procotol. +# Options for set_password specific to the VNC protocol. # -# @display: The id of the display where the password should be changed. -# Defaults to the first. +# @display: The id of the display where the password should be +# changed. Defaults to the first. # # Since: 7.0 ## @@ -76,17 +78,16 @@ # # Set the password of a remote display server. # -# Returns: - Nothing on success -# - If Spice is not enabled, DeviceNotFound +# Errors: +# - If Spice is not enabled, DeviceNotFound # # Since: 0.14 # # Example: # -# -> { "execute": "set_password", "arguments": { "protocol": "vnc", -# "password": "secret" } } -# <- { "return": {} } -# +# -> { "execute": "set_password", "arguments": { "protocol": "vnc", +# "password": "secret" } } +# <- { "return": {} } ## { 'command': 'set_password', 'boxed': true, 'data': 'SetPasswordOptions' } @@ -95,20 +96,22 @@ # # General options for expire_password. # -# @protocol: - 'vnc' to modify the VNC server expiration -# - 'spice' to modify the Spice server expiration +# @protocol: +# - 'vnc' to modify the VNC server expiration +# - 'spice' to modify the Spice server expiration # # @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 +# - '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 # -# Notes: Time is relative to the server and currently there is no way to -# coordinate server time with client time. It is not recommended to -# use the absolute time version of the @time parameter unless you're -# sure you are on the same machine as the QEMU instance. +# Notes: Time is relative to the server and currently there is no way +# to coordinate server time with client time. It is not +# recommended to use the absolute time version of the @time +# parameter unless you're sure you are on the same machine as the +# QEMU instance. # # Since: 7.0 ## @@ -121,10 +124,10 @@ ## # @ExpirePasswordOptionsVnc: # -# Options for expire_password specific to the VNC procotol. +# Options for expire_password specific to the VNC protocol. # -# @display: The id of the display where the expiration should be changed. -# Defaults to the first. +# @display: The id of the display where the expiration should be +# changed. Defaults to the first. # # Since: 7.0 ## @@ -136,17 +139,17 @@ # # Expire the password of a remote display server. # -# Returns: - Nothing on success -# - If @protocol is 'spice' and Spice is not active, DeviceNotFound +# Errors: +# - If @protocol is 'spice' and Spice is not active, +# DeviceNotFound # # Since: 0.14 # # Example: # -# -> { "execute": "expire_password", "arguments": { "protocol": "vnc", -# "time": "+60" } } -# <- { "return": {} } -# +# -> { "execute": "expire_password", "arguments": { "protocol": "vnc", +# "time": "+60" } } +# <- { "return": {} } ## { 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' } @@ -171,30 +174,30 @@ # # @filename: the path of a new file to store the image # -# @device: ID of the display device that should be dumped. If this parameter -# is missing, the primary display will be used. (Since 2.12) +# @device: ID of the display device that should be dumped. If this +# parameter is missing, the primary display will be used. (Since +# 2.12) # -# @head: head to use in case the device supports multiple heads. If this -# parameter is missing, head #0 will be used. Also note that the head -# can only be specified in conjunction with the device ID. (Since 2.12) +# @head: head to use in case the device supports multiple heads. If +# this parameter is missing, head #0 will be used. Also note that +# the head can only be specified in conjunction with the device +# ID. (Since 2.12) # -# @format: image format for screendump. (default: ppm) (Since 7.1) -# -# Returns: Nothing on success +# @format: image format for screendump. (default: ppm) (Since 7.1) # # Since: 0.14 # # Example: # -# -> { "execute": "screendump", -# "arguments": { "filename": "/tmp/image" } } -# <- { "return": {} } -# +# -> { "execute": "screendump", +# "arguments": { "filename": "/tmp/image" } } +# <- { "return": {} } ## { 'command': 'screendump', 'data': {'filename': 'str', '*device': 'str', '*head': 'int', '*format': 'ImageFormat'}, - 'coroutine': true } + 'coroutine': true, + 'if': 'CONFIG_PIXMAN' } ## # == Spice @@ -238,16 +241,16 @@ # # Information about a SPICE client channel. # -# @connection-id: SPICE connection id number. All channels with the same id -# belong to the same SPICE session. +# @connection-id: SPICE connection id number. All channels with the +# same id belong to the same SPICE session. # # @channel-type: SPICE channel type number. "1" is the main control -# channel, filter for this one if you want to track spice -# sessions only +# channel, filter for this one if you want to track spice sessions +# only # -# @channel-id: SPICE channel ID number. Usually "0", might be different when -# multiple channels of the same type exist, such as multiple -# display channels in a multihead setup +# @channel-id: SPICE channel ID number. Usually "0", might be +# different when multiple channels of the same type exist, such as +# multiple display channels in a multihead setup # # @tls: true if the channel is encrypted, false otherwise. # @@ -268,8 +271,8 @@ # # @server: Mouse cursor position is determined by the server. # -# @unknown: No information is available about mouse mode used by -# the spice server. +# @unknown: No information is available about mouse mode used by the +# spice server. # # Note: spice/enums.h has a SpiceMouseMode already, hence the name. # @@ -287,10 +290,10 @@ # @enabled: true if the SPICE server is enabled, false otherwise # # @migrated: true if the last guest migration completed and spice -# migration had completed as well. false otherwise. (since 1.4) +# migration had completed as well, false otherwise (since 1.4) # # @host: The hostname the SPICE server is bound to. This depends on -# the name resolution on the host and may be an IP address. +# the name resolution on the host and may be an IP address. # # @port: The SPICE server's port number. # @@ -300,13 +303,14 @@ # # @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 -# server doesn't provide this information. (since: 1.1) +# @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 server doesn't provide this information. +# (since: 1.1) # # @channels: a list of @SpiceChannel for each active spice channel # @@ -329,39 +333,38 @@ # # Example: # -# -> { "execute": "query-spice" } -# <- { "return": { -# "enabled": true, -# "auth": "spice", -# "port": 5920, -# "migrated":false, -# "tls-port": 5921, -# "host": "0.0.0.0", -# "mouse-mode":"client", -# "channels": [ -# { -# "port": "54924", -# "family": "ipv4", -# "channel-type": 1, -# "connection-id": 1804289383, -# "host": "127.0.0.1", -# "channel-id": 0, -# "tls": true -# }, -# { -# "port": "36710", -# "family": "ipv4", -# "channel-type": 4, -# "connection-id": 1804289383, -# "host": "127.0.0.1", -# "channel-id": 0, -# "tls": false -# }, -# [ ... more channels follow ... ] -# ] -# } -# } -# +# -> { "execute": "query-spice" } +# <- { "return": { +# "enabled": true, +# "auth": "spice", +# "port": 5920, +# "migrated":false, +# "tls-port": 5921, +# "host": "0.0.0.0", +# "mouse-mode":"client", +# "channels": [ +# { +# "port": "54924", +# "family": "ipv4", +# "channel-type": 1, +# "connection-id": 1804289383, +# "host": "127.0.0.1", +# "channel-id": 0, +# "tls": true +# }, +# { +# "port": "36710", +# "family": "ipv4", +# "channel-type": 4, +# "connection-id": 1804289383, +# "host": "127.0.0.1", +# "channel-id": 0, +# "tls": false +# }, +# [ ... more channels follow ... ] +# ] +# } +# } ## { 'command': 'query-spice', 'returns': 'SpiceInfo', 'if': 'CONFIG_SPICE' } @@ -379,13 +382,12 @@ # # Example: # -# <- { "timestamp": {"seconds": 1290688046, "microseconds": 388707}, -# "event": "SPICE_CONNECTED", -# "data": { -# "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, -# "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} -# }} -# +# <- { "timestamp": {"seconds": 1290688046, "microseconds": 388707}, +# "event": "SPICE_CONNECTED", +# "data": { +# "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, +# "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} +# }} ## { 'event': 'SPICE_CONNECTED', 'data': { 'server': 'SpiceBasicInfo', @@ -395,8 +397,8 @@ ## # @SPICE_INITIALIZED: # -# Emitted after initial handshake and authentication takes place (if any) -# and the SPICE channel is up and running +# Emitted after initial handshake and authentication takes place (if +# any) and the SPICE channel is up and running # # @server: server information # @@ -406,15 +408,14 @@ # # Example: # -# <- { "timestamp": {"seconds": 1290688046, "microseconds": 417172}, -# "event": "SPICE_INITIALIZED", -# "data": {"server": {"auth": "spice", "port": "5921", -# "family": "ipv4", "host": "127.0.0.1"}, -# "client": {"port": "49004", "family": "ipv4", "channel-type": 3, -# "connection-id": 1804289383, "host": "127.0.0.1", -# "channel-id": 0, "tls": true} -# }} -# +# <- { "timestamp": {"seconds": 1290688046, "microseconds": 417172}, +# "event": "SPICE_INITIALIZED", +# "data": {"server": {"auth": "spice", "port": "5921", +# "family": "ipv4", "host": "127.0.0.1"}, +# "client": {"port": "49004", "family": "ipv4", "channel-type": 3, +# "connection-id": 1804289383, "host": "127.0.0.1", +# "channel-id": 0, "tls": true} +# }} ## { 'event': 'SPICE_INITIALIZED', 'data': { 'server': 'SpiceServerInfo', @@ -434,13 +435,12 @@ # # Example: # -# <- { "timestamp": {"seconds": 1290688046, "microseconds": 388707}, -# "event": "SPICE_DISCONNECTED", -# "data": { -# "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, -# "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} -# }} -# +# <- { "timestamp": {"seconds": 1290688046, "microseconds": 388707}, +# "event": "SPICE_DISCONNECTED", +# "data": { +# "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, +# "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} +# }} ## { 'event': 'SPICE_DISCONNECTED', 'data': { 'server': 'SpiceBasicInfo', @@ -456,9 +456,8 @@ # # Example: # -# <- { "timestamp": {"seconds": 1290688046, "microseconds": 417172}, -# "event": "SPICE_MIGRATE_COMPLETED" } -# +# <- { "timestamp": {"seconds": 1290688046, "microseconds": 417172}, +# "event": "SPICE_MIGRATE_COMPLETED" } ## { 'event': 'SPICE_MIGRATE_COMPLETED', 'if': 'CONFIG_SPICE' } @@ -474,9 +473,9 @@ # # @host: IP address # -# @service: The service name of the vnc port. This may depend on the host -# system's service database so symbolic names should not be relied -# on. +# @service: The service name of the vnc port. This may depend on the +# host system's service database so symbolic names should not be +# relied on. # # @family: address family # @@ -496,8 +495,8 @@ # # The network connection information for server # -# @auth: authentication method used for -# the plain (non-websocket) VNC server +# @auth: authentication method used for the plain (non-websocket) VNC +# server # # Since: 2.1 ## @@ -512,10 +511,10 @@ # Information about a connected VNC client. # # @x509_dname: If x509 authentication is in use, the Distinguished -# Name of the client. +# Name of the client. # # @sasl_username: If SASL authentication is in use, the SASL username -# used for authentication. +# used for authentication. # # Since: 0.14 ## @@ -531,33 +530,41 @@ # # @enabled: true if the VNC server is enabled, false otherwise # -# @host: The hostname the VNC server is bound to. This depends on -# the name resolution on the host and may be an IP address. +# @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. +# 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 +# @clients: a list of @VncClientInfo of all currently connected +# clients # # Since: 0.14 ## @@ -601,8 +608,8 @@ # # @auth: The current authentication type used by the servers # -# @vencrypt: The vencrypt sub authentication type used by the -# servers, only specified in case auth == vencrypt. +# @vencrypt: The vencrypt sub authentication type used by the servers, +# only specified in case auth == vencrypt. # # Since: 2.9 ## @@ -620,17 +627,18 @@ # @id: vnc server name. # # @server: A list of @VncBasincInfo describing all listening sockets. -# The list can be empty (in case the vnc server is disabled). -# It also may have multiple entries: normal + websocket, -# possibly also ipv4 + ipv6 in the future. +# The list can be empty (in case the vnc server is disabled). It +# also may have multiple entries: normal + websocket, possibly +# also ipv4 + ipv6 in the future. # -# @clients: A list of @VncClientInfo of all currently connected clients. -# The list can be empty, for obvious reasons. +# @clients: A list of @VncClientInfo of all currently connected +# clients. The list can be empty, for obvious reasons. # -# @auth: The current authentication type used by the non-websockets servers +# @auth: The current authentication type used by the non-websockets +# servers # # @vencrypt: The vencrypt authentication type used by the servers, -# only specified in case auth == vencrypt. +# only specified in case auth == vencrypt. # # @display: The display device the vnc server is linked to. # @@ -656,24 +664,23 @@ # # Example: # -# -> { "execute": "query-vnc" } -# <- { "return": { -# "enabled":true, -# "host":"0.0.0.0", -# "service":"50402", -# "auth":"vnc", -# "family":"ipv4", -# "clients":[ -# { -# "host":"127.0.0.1", -# "service":"50401", -# "family":"ipv4" -# "websocket":false, -# } -# ] -# } -# } -# +# -> { "execute": "query-vnc" } +# <- { "return": { +# "enabled":true, +# "host":"0.0.0.0", +# "service":"50402", +# "auth":"vnc", +# "family":"ipv4", +# "clients":[ +# { +# "host":"127.0.0.1", +# "service":"50401", +# "family":"ipv4", +# "websocket":false +# } +# ] +# } +# } ## { 'command': 'query-vnc', 'returns': 'VncInfo', 'if': 'CONFIG_VNC' } @@ -698,8 +705,9 @@ # # 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' }, @@ -714,21 +722,20 @@ # # @client: client information # -# Note: This event is emitted before any authentication takes place, thus -# the authentication ID is not provided +# Note: This event is emitted before any authentication takes place, +# thus the authentication ID is not provided # # Since: 0.13 # # Example: # -# <- { "event": "VNC_CONNECTED", -# "data": { -# "server": { "auth": "sasl", "family": "ipv4", "websocket": false, -# "service": "5901", "host": "0.0.0.0" }, -# "client": { "family": "ipv4", "service": "58425", -# "host": "127.0.0.1", "websocket": false } }, -# "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } -# +# <- { "event": "VNC_CONNECTED", +# "data": { +# "server": { "auth": "sasl", "family": "ipv4", "websocket": false, +# "service": "5901", "host": "0.0.0.0" }, +# "client": { "family": "ipv4", "service": "58425", +# "host": "127.0.0.1", "websocket": false } }, +# "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } ## { 'event': 'VNC_CONNECTED', 'data': { 'server': 'VncServerInfo', @@ -738,8 +745,8 @@ ## # @VNC_INITIALIZED: # -# Emitted after authentication takes place (if any) and the VNC session is -# made active +# Emitted after authentication takes place (if any) and the VNC +# session is made active # # @server: server information # @@ -749,14 +756,13 @@ # # Example: # -# <- { "event": "VNC_INITIALIZED", -# "data": { -# "server": { "auth": "sasl", "family": "ipv4", "websocket": false, -# "service": "5901", "host": "0.0.0.0"}, -# "client": { "family": "ipv4", "service": "46089", "websocket": false, -# "host": "127.0.0.1", "sasl_username": "luiz" } }, -# "timestamp": { "seconds": 1263475302, "microseconds": 150772 } } -# +# <- { "event": "VNC_INITIALIZED", +# "data": { +# "server": { "auth": "sasl", "family": "ipv4", "websocket": false, +# "service": "5901", "host": "0.0.0.0"}, +# "client": { "family": "ipv4", "service": "46089", "websocket": false, +# "host": "127.0.0.1", "sasl_username": "luiz" } }, +# "timestamp": { "seconds": 1263475302, "microseconds": 150772 } } ## { 'event': 'VNC_INITIALIZED', 'data': { 'server': 'VncServerInfo', @@ -776,14 +782,13 @@ # # Example: # -# <- { "event": "VNC_DISCONNECTED", -# "data": { -# "server": { "auth": "sasl", "family": "ipv4", "websocket": false, -# "service": "5901", "host": "0.0.0.0" }, -# "client": { "family": "ipv4", "service": "58425", "websocket": false, -# "host": "127.0.0.1", "sasl_username": "luiz" } }, -# "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } -# +# <- { "event": "VNC_DISCONNECTED", +# "data": { +# "server": { "auth": "sasl", "family": "ipv4", "websocket": false, +# "service": "5901", "host": "0.0.0.0" }, +# "client": { "family": "ipv4", "service": "58425", "websocket": false, +# "host": "127.0.0.1", "sasl_username": "luiz" } }, +# "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } ## { 'event': 'VNC_DISCONNECTED', 'data': { 'server': 'VncServerInfo', @@ -805,7 +810,8 @@ # # @current: true if this device is currently receiving mouse events # -# @absolute: true if this device supports absolute coordinates as input +# @absolute: true if this device supports absolute coordinates as +# input # # Since: 0.14 ## @@ -824,23 +830,22 @@ # # Example: # -# -> { "execute": "query-mice" } -# <- { "return": [ -# { -# "name":"QEMU Microsoft Mouse", -# "index":0, -# "current":false, -# "absolute":false -# }, -# { -# "name":"QEMU PS/2 Mouse", -# "index":1, -# "current":true, -# "absolute":true -# } -# ] -# } -# +# -> { "execute": "query-mice" } +# <- { "return": [ +# { +# "name":"QEMU Microsoft Mouse", +# "index":0, +# "current":false, +# "absolute":false +# }, +# { +# "name":"QEMU PS/2 Mouse", +# "index":1, +# "current":true, +# "absolute":true +# } +# ] +# } ## { 'command': 'query-mice', 'returns': ['MouseInfo'] } @@ -852,46 +857,96 @@ # This is used by the @send-key command. # # @unmapped: since 2.0 +# # @pause: since 2.0 +# # @ro: since 2.4 +# # @kp_comma: since 2.4 +# # @kp_equals: since 2.6 +# # @power: since 2.6 +# # @hiragana: since 2.9 +# # @henkan: since 2.9 +# # @yen: since 2.9 # # @sleep: since 2.10 +# # @wake: since 2.10 +# # @audionext: since 2.10 +# # @audioprev: since 2.10 +# # @audiostop: since 2.10 +# # @audioplay: since 2.10 +# # @audiomute: since 2.10 +# # @volumeup: since 2.10 +# # @volumedown: since 2.10 +# # @mediaselect: since 2.10 +# # @mail: since 2.10 +# # @calculator: since 2.10 +# # @computer: since 2.10 +# # @ac_home: since 2.10 +# # @ac_back: since 2.10 +# # @ac_forward: since 2.10 +# # @ac_refresh: since 2.10 +# # @ac_bookmarks: since 2.10 # # @muhenkan: since 2.12 +# # @katakanahiragana: since 2.12 # # @lang1: since 6.1 +# # @lang2: since 6.1 # -# 'sysrq' was mistakenly added to hack around the fact that -# the ps2 driver was not generating correct scancodes sequences -# when 'alt+print' was pressed. This flaw is now fixed and the -# 'sysrq' key serves no further purpose. Any further use of -# 'sysrq' will be transparently changed to 'print', so they -# are effectively synonyms. +# @f13: since 8.0 +# +# @f14: since 8.0 +# +# @f15: since 8.0 +# +# @f16: since 8.0 +# +# @f17: since 8.0 +# +# @f18: since 8.0 +# +# @f19: since 8.0 +# +# @f20: since 8.0 +# +# @f21: since 8.0 +# +# @f22: since 8.0 +# +# @f23: since 8.0 +# +# @f24: since 8.0 +# +# 'sysrq' was mistakenly added to hack around the fact that the ps2 +# driver was not generating correct scancodes sequences when +# 'alt+print' was pressed. This flaw is now fixed and the 'sysrq' key +# serves no further purpose. Any further use of 'sysrq' will be +# transparently changed to 'print', so they are effectively synonyms. # # Since: 1.3 ## @@ -918,7 +973,7 @@ 'volumeup', 'volumedown', 'mediaselect', 'mail', 'calculator', 'computer', 'ac_home', 'ac_back', 'ac_forward', 'ac_refresh', 'ac_bookmarks', - 'lang1', 'lang2' ] } + 'lang1', 'lang2','f13','f14','f15','f16','f17','f18','f19','f20','f21','f22','f23','f24' ] } ## # @KeyValueKind: @@ -931,6 +986,8 @@ ## # @IntWrapper: # +# @data: a numeric key code +# # Since: 1.3 ## { 'struct': 'IntWrapper', @@ -939,6 +996,8 @@ ## # @QKeyCodeWrapper: # +# @data: An enumeration of key name +# # Since: 1.3 ## { 'struct': 'QKeyCodeWrapper', @@ -949,6 +1008,8 @@ # # Represents a keyboard key. # +# @type: key encoding +# # Since: 1.3 ## { 'union': 'KeyValue', @@ -963,27 +1024,26 @@ # # Send keys to guest. # -# @keys: An array of @KeyValue elements. All @KeyValues in this array are -# simultaneously sent to the guest. A @KeyValue.number value is sent -# directly to the guest, while @KeyValue.qcode must be a valid -# @QKeyCode value +# @keys: An array of @KeyValue elements. All @KeyValues in this array +# are simultaneously sent to the guest. A @KeyValue.number value +# is sent directly to the guest, while @KeyValue.qcode must be a +# valid @QKeyCode value # -# @hold-time: time to delay key up events, milliseconds. Defaults -# to 100 +# @hold-time: time to delay key up events, milliseconds. Defaults to +# 100 # -# Returns: - Nothing on success -# - If key is unknown or redundant, InvalidParameter +# Errors: +# - If key is unknown or redundant, GenericError # # Since: 1.3 # # Example: # -# -> { "execute": "send-key", -# "arguments": { "keys": [ { "type": "qcode", "data": "ctrl" }, -# { "type": "qcode", "data": "alt" }, -# { "type": "qcode", "data": "delete" } ] } } -# <- { "return": {} } -# +# -> { "execute": "send-key", +# "arguments": { "keys": [ { "type": "qcode", "data": "ctrl" }, +# { "type": "qcode", "data": "alt" }, +# { "type": "qcode", "data": "delete" } ] } } +# <- { "return": {} } ## { 'command': 'send-key', 'data': { 'keys': ['KeyValue'], '*hold-time': 'int' } } @@ -997,11 +1057,13 @@ # # @extra: rear side button of a 5-button mouse (since 2.9) # +# @touch: screen contact on a multi-touch device (since 8.1) +# # Since: 2.0 ## { 'enum' : 'InputButton', 'data' : [ 'left', 'middle', 'right', 'wheel-up', 'wheel-down', 'side', - 'extra', 'wheel-left', 'wheel-right' ] } + 'extra', 'wheel-left', 'wheel-right', 'touch' ] } ## # @InputAxis: @@ -1013,12 +1075,34 @@ { 'enum' : 'InputAxis', 'data' : [ 'x', 'y' ] } +## +# @InputMultiTouchType: +# +# Type of a multi-touch event. +# +# @begin: A new touch event sequence has just started. +# +# @update: A touch event sequence has been updated. +# +# @end: A touch event sequence has finished. +# +# @cancel: A touch event sequence has been canceled. +# +# @data: Absolute position data. +# +# Since: 8.1 +## +{ 'enum' : 'InputMultiTouchType', + 'data' : [ 'begin', 'update', 'end', 'cancel', 'data' ] } + + ## # @InputKeyEvent: # # Keyboard input event. # # @key: Which key this event is for. +# # @down: True for key-down and false for key-up events. # # Since: 2.0 @@ -1033,6 +1117,7 @@ # Pointer button input event. # # @button: Which button this event is for. +# # @down: True for key-down and false for key-up events. # # Since: 2.0 @@ -1047,8 +1132,9 @@ # Pointer motion input event. # # @axis: Which axis is referenced by @value. -# @value: Pointer position. For absolute coordinates the -# valid range is 0 -> 0x7ffff +# +# @value: Pointer position. For absolute coordinates the valid range +# is 0 -> 0x7ffff # # Since: 2.0 ## @@ -1056,17 +1142,54 @@ 'data' : { 'axis' : 'InputAxis', 'value' : 'int' } } +## +# @InputMultiTouchEvent: +# +# MultiTouch input event. +# +# @type: The type of multi-touch event. +# +# @slot: Which slot has generated the event. +# +# @tracking-id: ID to correlate this event with previously generated +# events. +# +# @axis: Which axis is referenced by @value. +# +# @value: Contact position. +# +# Since: 8.1 +## +{ 'struct' : 'InputMultiTouchEvent', + 'data' : { 'type' : 'InputMultiTouchType', + 'slot' : 'int', + 'tracking-id': 'int', + 'axis' : 'InputAxis', + 'value' : 'int' } } + ## # @InputEventKind: # +# @key: a keyboard input event +# +# @btn: a pointer button input event +# +# @rel: a relative pointer motion input event +# +# @abs: an absolute pointer motion input event +# +# @mtt: a multi-touch input event +# # Since: 2.0 ## { 'enum': 'InputEventKind', - 'data': [ 'key', 'btn', 'rel', 'abs' ] } + 'data': [ 'key', 'btn', 'rel', 'abs', 'mtt' ] } ## # @InputKeyEventWrapper: # +# @data: Keyboard input event +# # Since: 2.0 ## { 'struct': 'InputKeyEventWrapper', @@ -1075,6 +1198,8 @@ ## # @InputBtnEventWrapper: # +# @data: Pointer button input event +# # Since: 2.0 ## { 'struct': 'InputBtnEventWrapper', @@ -1083,22 +1208,29 @@ ## # @InputMoveEventWrapper: # +# @data: Pointer motion input event +# # Since: 2.0 ## { 'struct': 'InputMoveEventWrapper', 'data': { 'data': 'InputMoveEvent' } } +## +# @InputMultiTouchEventWrapper: +# +# @data: MultiTouch input event +# +# Since: 8.1 +## +{ 'struct': 'InputMultiTouchEventWrapper', + 'data': { 'data': 'InputMultiTouchEvent' } } + ## # @InputEvent: # # 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 +# @type: the type of input event # # Since: 2.0 ## @@ -1108,7 +1240,8 @@ 'data' : { 'key' : 'InputKeyEventWrapper', 'btn' : 'InputBtnEventWrapper', 'rel' : 'InputMoveEventWrapper', - 'abs' : 'InputMoveEventWrapper' } } + 'abs' : 'InputMoveEventWrapper', + 'mtt' : 'InputMultiTouchEventWrapper' } } ## # @input-send-event: @@ -1127,55 +1260,54 @@ # 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. +# @head: head to send event(s) to, in case the display device supports +# multiple scanouts. +# +# @events: List of InputEvent union. # # 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: +# Examples: # -# 1. Press left mouse button. +# 1. Press left mouse button. # -# -> { "execute": "input-send-event", -# "arguments": { "device": "video0", -# "events": [ { "type": "btn", -# "data" : { "down": true, "button": "left" } } ] } } -# <- { "return": {} } +# -> { "execute": "input-send-event", +# "arguments": { "device": "video0", +# "events": [ { "type": "btn", +# "data" : { "down": true, "button": "left" } } ] } } +# <- { "return": {} } # -# -> { "execute": "input-send-event", -# "arguments": { "device": "video0", -# "events": [ { "type": "btn", -# "data" : { "down": false, "button": "left" } } ] } } -# <- { "return": {} } +# -> { "execute": "input-send-event", +# "arguments": { "device": "video0", +# "events": [ { "type": "btn", +# "data" : { "down": false, "button": "left" } } ] } } +# <- { "return": {} } # -# 2. Press ctrl-alt-del. +# 2. Press ctrl-alt-del. # -# -> { "execute": "input-send-event", -# "arguments": { "events": [ -# { "type": "key", "data" : { "down": true, -# "key": {"type": "qcode", "data": "ctrl" } } }, -# { "type": "key", "data" : { "down": true, -# "key": {"type": "qcode", "data": "alt" } } }, -# { "type": "key", "data" : { "down": true, -# "key": {"type": "qcode", "data": "delete" } } } ] } } -# <- { "return": {} } +# -> { "execute": "input-send-event", +# "arguments": { "events": [ +# { "type": "key", "data" : { "down": true, +# "key": {"type": "qcode", "data": "ctrl" } } }, +# { "type": "key", "data" : { "down": true, +# "key": {"type": "qcode", "data": "alt" } } }, +# { "type": "key", "data" : { "down": true, +# "key": {"type": "qcode", "data": "delete" } } } ] } } +# <- { "return": {} } # -# 3. Move mouse pointer to absolute coordinates (20000, 400). -# -# -> { "execute": "input-send-event" , -# "arguments": { "events": [ -# { "type": "abs", "data" : { "axis": "x", "value" : 20000 } }, -# { "type": "abs", "data" : { "axis": "y", "value" : 400 } } ] } } -# <- { "return": {} } +# 3. Move mouse pointer to absolute coordinates (20000, 400). # +# -> { "execute": "input-send-event" , +# "arguments": { "events": [ +# { "type": "abs", "data" : { "axis": "x", "value" : 20000 } }, +# { "type": "abs", "data" : { "axis": "y", "value" : 400 } } ] } } +# <- { "return": {} } ## { 'command': 'input-send-event', 'data': { '*device': 'str', @@ -1188,27 +1320,36 @@ # GTK display options. # # @grab-on-hover: Grab keyboard input on mouse hover. +# # @zoom-to-fit: Zoom guest display to fit into the host window. When -# turned off the host window will be resized instead. -# In case the display device can notify the guest on -# window resizes (virtio-gpu) this will default to "on", -# assuming the guest will resize the display to match -# the window size then. Otherwise it defaults to "off". -# Since 3.1 +# turned off the host window will be resized instead. In case the +# display device can notify the guest on window resizes +# (virtio-gpu) this will default to "on", assuming the guest will +# resize the display to match the window size then. Otherwise it +# defaults to "off". (Since 3.1) +# +# @show-tabs: Display the tab bar for switching between the various +# graphical interfaces (e.g. VGA and virtual console character +# devices) by default. (Since 7.1) +# +# @show-menubar: Display the main window menubar. Defaults to "on". +# (Since 8.0) # # Since: 2.12 ## { 'struct' : 'DisplayGTK', 'data' : { '*grab-on-hover' : 'bool', - '*zoom-to-fit' : 'bool' } } + '*zoom-to-fit' : 'bool', + '*show-tabs' : 'bool', + '*show-menubar' : 'bool' } } ## # @DisplayEGLHeadless: # # EGL headless display options. # -# @rendernode: Which DRM render node should be used. Default is the first -# available node on the host. +# @rendernode: Which DRM render node should be used. Default is the +# first available node on the host. # # Since: 3.1 ## @@ -1222,11 +1363,11 @@ # # @addr: The D-Bus bus address (default to the session bus). # -# @rendernode: Which DRM render node should be used. Default is the first -# available node on the host. +# @rendernode: Which DRM render node should be used. Default is the +# first available node on the host. # # @p2p: Whether to use peer-to-peer connections (accepted through -# ``add_client``). +# @add_client). # # @audiodev: Use the specified DBus audiodev to export audio. # @@ -1244,10 +1385,13 @@ # Display OpenGL mode. # # @off: Disable OpenGL (default). -# @on: Use OpenGL, pick context type automatically. -# Would better be named 'auto' but is called 'on' for backward -# compatibility with bool type. +# +# @on: Use OpenGL, pick context type automatically. Would better be +# named 'auto' but is called 'on' for backward compatibility with +# bool type. +# # @core: Use OpenGL with Core (desktop) Context. +# # @es: Use OpenGL with ES (embedded systems) Context. # # Since: 3.0 @@ -1273,18 +1417,24 @@ # Cocoa display options. # # @left-command-key: Enable/disable forwarding of left command key to -# guest. Allows command-tab window switching on the -# host without sending this key to the guest when -# "off". Defaults to "on" +# guest. Allows command-tab window switching on the host without +# sending this key to the guest when "off". Defaults to "on" # -# @full-grab: Capture all key presses, including system combos. This -# requires accessibility permissions, since it performs -# a global grab on key events. (default: off) -# See https://support.apple.com/en-in/guide/mac-help/mh32356/mac +# @full-grab: Capture all key presses, including system combos. This +# requires accessibility permissions, since it performs a global +# grab on key events. (default: off) See +# https://support.apple.com/en-in/guide/mac-help/mh32356/mac # -# @swap-opt-cmd: Swap the Option and Command keys so that their key codes match -# their position on non-Mac keyboards and you can use Meta/Super -# and Alt where you expect them. (default: off) +# @swap-opt-cmd: Swap the Option and Command keys so that their key +# codes match their position on non-Mac keyboards and you can use +# Meta/Super and Alt where you expect them. (default: off) +# +# @zoom-to-fit: Zoom guest display to fit into the host window. When +# turned off the host window will be resized instead. Defaults to +# "off". (Since 8.2) +# +# @zoom-interpolation: Apply interpolation to smooth output when +# zoom-to-fit is enabled. Defaults to "off". (Since 9.0) # # Since: 7.0 ## @@ -1292,7 +1442,9 @@ 'data': { '*left-command-key': 'bool', '*full-grab': 'bool', - '*swap-opt-cmd': 'bool' + '*swap-opt-cmd': 'bool', + '*zoom-to-fit': 'bool', + '*zoom-interpolation': 'bool' } } ## @@ -1310,8 +1462,8 @@ # # SDL2 display options. # -# @grab-mod: Modifier keys that should be pressed together with the -# "G" key to release the mouse grab. +# @grab-mod: Modifier keys that should be pressed together with the +# "G" key to release the mouse grab. # # Since: 7.1 ## @@ -1323,36 +1475,35 @@ # # Display (user interface) type. # -# @default: The default user interface, selecting from the first available -# of gtk, sdl, cocoa, and vnc. +# @default: The default user interface, selecting from the first +# available of gtk, sdl, cocoa, and vnc. # -# @none: No user interface or video output display. The guest will -# still see an emulated graphics card, but its output will not -# be displayed to the QEMU user. +# @none: No user interface or video output display. The guest will +# still see an emulated graphics card, but its output will not be +# displayed to the QEMU user. # # @gtk: The GTK user interface. # # @sdl: The SDL user interface. # # @egl-headless: No user interface, offload GL operations to a local -# DRI device. Graphical display need to be paired with -# VNC or Spice. (Since 3.1) +# DRI device. Graphical display need to be paired with VNC or +# Spice. (Since 3.1) # # @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. +# 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. # # @cocoa: The Cocoa user interface. # # @spice-app: Set up a Spice server and run the default associated -# application to connect to it. The server will redirect -# the serial console and QEMU monitors. (Since 4.0) +# application to connect to it. The server will redirect the +# serial console and QEMU monitors. (Since 4.0) # -# @dbus: Start a D-Bus service for the display. (Since 7.0) +# @dbus: Start a D-Bus service for the display. (Since 7.0) # # Since: 2.12 ## @@ -1362,8 +1513,7 @@ { 'name': 'none' }, { 'name': 'gtk', 'if': 'CONFIG_GTK' }, { 'name': 'sdl', 'if': 'CONFIG_SDL' }, - { 'name': 'egl-headless', - 'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } }, + { 'name': 'egl-headless', 'if': 'CONFIG_OPENGL' }, { 'name': 'curses', 'if': 'CONFIG_CURSES' }, { 'name': 'cocoa', 'if': 'CONFIG_COCOA' }, { 'name': 'spice-app', 'if': 'CONFIG_SPICE' }, @@ -1377,9 +1527,16 @@ # Display (user interface) options. # # @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) +# +# @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 @@ -1396,7 +1553,7 @@ 'cocoa': { 'type': 'DisplayCocoa', 'if': 'CONFIG_COCOA' }, 'curses': { 'type': 'DisplayCurses', 'if': 'CONFIG_CURSES' }, 'egl-headless': { 'type': 'DisplayEGLHeadless', - 'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } }, + 'if': 'CONFIG_OPENGL' }, 'dbus': { 'type': 'DisplayDBus', 'if': 'CONFIG_DBUS_DISPLAY' }, 'sdl': { 'type': 'DisplaySDL', 'if': 'CONFIG_SDL' } } @@ -1457,16 +1614,13 @@ # # Reload display configuration. # -# Returns: Nothing on success. -# # Since: 6.0 # # Example: # -# -> { "execute": "display-reload", -# "arguments": { "type": "vnc", "tls-certs": true } } -# <- { "return": {} } -# +# -> { "execute": "display-reload", +# "arguments": { "type": "vnc", "tls-certs": true } } +# <- { "return": {} } ## { 'command': 'display-reload', 'data': 'DisplayReloadOptions', @@ -1489,9 +1643,9 @@ # # Specify the VNC reload options. # -# @addresses: If specified, change set of addresses -# to listen for connections. Addresses configured -# for websockets are not touched. +# @addresses: If specified, change set of addresses to listen for +# connections. Addresses configured for websockets are not +# touched. # # Since: 7.1 ## @@ -1517,19 +1671,47 @@ # # Update display configuration. # -# Returns: Nothing on success. -# # Since: 7.1 # # Example: # -# -> { "execute": "display-update", -# "arguments": { "type": "vnc", "addresses": -# [ { "type": "inet", "host": "0.0.0.0", -# "port": "5901" } ] } } -# <- { "return": {} } -# +# -> { "execute": "display-update", +# "arguments": { "type": "vnc", "addresses": +# [ { "type": "inet", "host": "0.0.0.0", +# "port": "5901" } ] } } +# <- { "return": {} } ## { 'command': 'display-update', 'data': 'DisplayUpdateOptions', 'boxed' : true } + +## +# @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. +# +# @protocol: must be "spice" +# +# @hostname: migration target hostname +# +# @port: spice tcp port for plaintext channels +# +# @tls-port: spice tcp port for tls-secured channels +# +# @cert-subject: server certificate subject +# +# Since: 0.14 +# +# Example: +# +# -> { "execute": "client_migrate_info", +# "arguments": { "protocol": "spice", +# "hostname": "virt42.lab.kraxel.org", +# "port": 1234 } } +# <- { "return": {} } +## +{ 'command': 'client_migrate_info', + 'data': { 'protocol': 'str', 'hostname': 'str', '*port': 'int', + '*tls-port': 'int', '*cert-subject': 'str' } } diff --git a/qapi/virtio.json b/qapi/virtio.json new file mode 100644 index 0000000000..74fc27c702 --- /dev/null +++ b/qapi/virtio.json @@ -0,0 +1,981 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# + +## +# = Virtio devices +## + +## +# @VirtioInfo: +# +# Basic information about a given VirtIODevice +# +# @path: The VirtIODevice's canonical QOM path +# +# @name: Name of the VirtIODevice +# +# Since: 7.2 +## +{ 'struct': 'VirtioInfo', + 'data': { 'path': 'str', + 'name': 'str' } } + +## +# @x-query-virtio: +# +# Returns a list of all realized VirtIODevices +# +# Features: +# +# @unstable: This command is meant for debugging. +# +# Returns: List of gathered VirtIODevices +# +# Since: 7.2 +# +# Example: +# +# -> { "execute": "x-query-virtio" } +# <- { "return": [ +# { +# "name": "virtio-input", +# "path": "/machine/peripheral-anon/device[4]/virtio-backend" +# }, +# { +# "name": "virtio-crypto", +# "path": "/machine/peripheral/crypto0/virtio-backend" +# }, +# { +# "name": "virtio-scsi", +# "path": "/machine/peripheral-anon/device[2]/virtio-backend" +# }, +# { +# "name": "virtio-net", +# "path": "/machine/peripheral-anon/device[1]/virtio-backend" +# }, +# { +# "name": "virtio-serial", +# "path": "/machine/peripheral-anon/device[0]/virtio-backend" +# } +# ] +# } +## +{ 'command': 'x-query-virtio', + 'returns': [ 'VirtioInfo' ], + 'features': [ 'unstable' ] } + +## +# @VhostStatus: +# +# Information about a vhost device. This information will only be +# displayed if the vhost device is active. +# +# @n-mem-sections: vhost_dev n_mem_sections +# +# @n-tmp-sections: vhost_dev n_tmp_sections +# +# @nvqs: vhost_dev nvqs (number of virtqueues being used) +# +# @vq-index: vhost_dev vq_index +# +# @features: vhost_dev features +# +# @acked-features: vhost_dev acked_features +# +# @backend-features: vhost_dev backend_features +# +# @protocol-features: vhost_dev protocol_features +# +# @max-queues: vhost_dev max_queues +# +# @backend-cap: vhost_dev backend_cap +# +# @log-enabled: vhost_dev log_enabled flag +# +# @log-size: vhost_dev log_size +# +# Since: 7.2 +## +{ 'struct': 'VhostStatus', + 'data': { 'n-mem-sections': 'int', + 'n-tmp-sections': 'int', + 'nvqs': 'uint32', + 'vq-index': 'int', + 'features': 'VirtioDeviceFeatures', + 'acked-features': 'VirtioDeviceFeatures', + 'backend-features': 'VirtioDeviceFeatures', + 'protocol-features': 'VhostDeviceProtocols', + 'max-queues': 'uint64', + 'backend-cap': 'uint64', + 'log-enabled': 'bool', + 'log-size': 'uint64' } } + +## +# @VirtioStatus: +# +# Full status of the virtio device with most VirtIODevice members. +# Also includes the full status of the corresponding vhost device if +# the vhost device is active. +# +# @name: VirtIODevice name +# +# @device-id: VirtIODevice ID +# +# @vhost-started: VirtIODevice vhost_started flag +# +# @guest-features: VirtIODevice guest_features +# +# @host-features: VirtIODevice host_features +# +# @backend-features: VirtIODevice backend_features +# +# @device-endian: VirtIODevice device_endian +# +# @num-vqs: VirtIODevice virtqueue count. This is the number of +# active virtqueues being used by the VirtIODevice. +# +# @status: VirtIODevice configuration status (VirtioDeviceStatus) +# +# @isr: VirtIODevice ISR +# +# @queue-sel: VirtIODevice queue_sel +# +# @vm-running: VirtIODevice vm_running flag +# +# @broken: VirtIODevice broken flag +# +# @disabled: VirtIODevice disabled flag +# +# @use-started: VirtIODevice use_started flag +# +# @started: VirtIODevice started flag +# +# @start-on-kick: VirtIODevice start_on_kick flag +# +# @disable-legacy-check: VirtIODevice disabled_legacy_check flag +# +# @bus-name: VirtIODevice bus_name +# +# @use-guest-notifier-mask: VirtIODevice use_guest_notifier_mask flag +# +# @vhost-dev: Corresponding vhost device info for a given +# VirtIODevice. Present if the given VirtIODevice has an active +# vhost device. +# +# Since: 7.2 +## +{ 'struct': 'VirtioStatus', + 'data': { 'name': 'str', + 'device-id': 'uint16', + 'vhost-started': 'bool', + 'device-endian': 'str', + 'guest-features': 'VirtioDeviceFeatures', + 'host-features': 'VirtioDeviceFeatures', + 'backend-features': 'VirtioDeviceFeatures', + 'num-vqs': 'int', + 'status': 'VirtioDeviceStatus', + 'isr': 'uint8', + 'queue-sel': 'uint16', + 'vm-running': 'bool', + 'broken': 'bool', + 'disabled': 'bool', + 'use-started': 'bool', + 'started': 'bool', + 'start-on-kick': 'bool', + 'disable-legacy-check': 'bool', + 'bus-name': 'str', + 'use-guest-notifier-mask': 'bool', + '*vhost-dev': 'VhostStatus' } } + +## +# @x-query-virtio-status: +# +# Poll for a comprehensive status of a given virtio device +# +# @path: Canonical QOM path of the VirtIODevice +# +# Features: +# +# @unstable: This command is meant for debugging. +# +# Returns: VirtioStatus of the virtio device +# +# Since: 7.2 +# +# Examples: +# +# 1. Poll for the status of virtio-crypto (no vhost-crypto active) +# +# -> { "execute": "x-query-virtio-status", +# "arguments": { "path": "/machine/peripheral/crypto0/virtio-backend" } +# } +# <- { "return": { +# "device-endian": "little", +# "bus-name": "", +# "disable-legacy-check": false, +# "name": "virtio-crypto", +# "started": true, +# "device-id": 20, +# "backend-features": { +# "transports": [], +# "dev-features": [] +# }, +# "start-on-kick": false, +# "isr": 1, +# "broken": false, +# "status": { +# "statuses": [ +# "VIRTIO_CONFIG_S_ACKNOWLEDGE: Valid virtio device found", +# "VIRTIO_CONFIG_S_DRIVER: Guest OS compatible with device", +# "VIRTIO_CONFIG_S_FEATURES_OK: Feature negotiation complete", +# "VIRTIO_CONFIG_S_DRIVER_OK: Driver setup and ready" +# ] +# }, +# "num-vqs": 2, +# "guest-features": { +# "dev-features": [], +# "transports": [ +# "VIRTIO_RING_F_EVENT_IDX: Used & avail. event fields enabled", +# "VIRTIO_RING_F_INDIRECT_DESC: Indirect descriptors supported", +# "VIRTIO_F_VERSION_1: Device compliant for v1 spec (legacy)" +# ] +# }, +# "host-features": { +# "unknown-dev-features": 1073741824, +# "dev-features": [], +# "transports": [ +# "VIRTIO_RING_F_EVENT_IDX: Used & avail. event fields enabled", +# "VIRTIO_RING_F_INDIRECT_DESC: Indirect descriptors supported", +# "VIRTIO_F_VERSION_1: Device compliant for v1 spec (legacy)", +# "VIRTIO_F_ANY_LAYOUT: Device accepts arbitrary desc. layouts", +# "VIRTIO_F_NOTIFY_ON_EMPTY: Notify when device runs out of avail. descs. on VQ" +# ] +# }, +# "use-guest-notifier-mask": true, +# "vm-running": true, +# "queue-sel": 1, +# "disabled": false, +# "vhost-started": false, +# "use-started": true +# } +# } +# +# 2. Poll for the status of virtio-net (vhost-net is active) +# +# -> { "execute": "x-query-virtio-status", +# "arguments": { "path": "/machine/peripheral-anon/device[1]/virtio-backend" } +# } +# <- { "return": { +# "device-endian": "little", +# "bus-name": "", +# "disabled-legacy-check": false, +# "name": "virtio-net", +# "started": true, +# "device-id": 1, +# "vhost-dev": { +# "n-tmp-sections": 4, +# "n-mem-sections": 4, +# "max-queues": 1, +# "backend-cap": 2, +# "log-size": 0, +# "backend-features": { +# "dev-features": [], +# "transports": [] +# }, +# "nvqs": 2, +# "protocol-features": { +# "protocols": [] +# }, +# "vq-index": 0, +# "log-enabled": false, +# "acked-features": { +# "dev-features": [ +# "VIRTIO_NET_F_MRG_RXBUF: Driver can merge receive buffers" +# ], +# "transports": [ +# "VIRTIO_RING_F_EVENT_IDX: Used & avail. event fields enabled", +# "VIRTIO_RING_F_INDIRECT_DESC: Indirect descriptors supported", +# "VIRTIO_F_VERSION_1: Device compliant for v1 spec (legacy)" +# ] +# }, +# "features": { +# "dev-features": [ +# "VHOST_F_LOG_ALL: Logging write descriptors supported", +# "VIRTIO_NET_F_MRG_RXBUF: Driver can merge receive buffers" +# ], +# "transports": [ +# "VIRTIO_RING_F_EVENT_IDX: Used & avail. event fields enabled", +# "VIRTIO_RING_F_INDIRECT_DESC: Indirect descriptors supported", +# "VIRTIO_F_IOMMU_PLATFORM: Device can be used on IOMMU platform", +# "VIRTIO_F_VERSION_1: Device compliant for v1 spec (legacy)", +# "VIRTIO_F_ANY_LAYOUT: Device accepts arbitrary desc. layouts", +# "VIRTIO_F_NOTIFY_ON_EMPTY: Notify when device runs out of avail. descs. on VQ" +# ] +# } +# }, +# "backend-features": { +# "dev-features": [ +# "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features negotiation supported", +# "VIRTIO_NET_F_GSO: Handling GSO-type packets supported", +# "VIRTIO_NET_F_CTRL_MAC_ADDR: MAC address set through control channel", +# "VIRTIO_NET_F_GUEST_ANNOUNCE: Driver sending gratuitous packets supported", +# "VIRTIO_NET_F_CTRL_RX_EXTRA: Extra RX mode control supported", +# "VIRTIO_NET_F_CTRL_VLAN: Control channel VLAN filtering supported", +# "VIRTIO_NET_F_CTRL_RX: Control channel RX mode supported", +# "VIRTIO_NET_F_CTRL_VQ: Control channel available", +# "VIRTIO_NET_F_STATUS: Configuration status field available", +# "VIRTIO_NET_F_MRG_RXBUF: Driver can merge receive buffers", +# "VIRTIO_NET_F_HOST_UFO: Device can receive UFO", +# "VIRTIO_NET_F_HOST_ECN: Device can receive TSO with ECN", +# "VIRTIO_NET_F_HOST_TSO6: Device can receive TSOv6", +# "VIRTIO_NET_F_HOST_TSO4: Device can receive TSOv4", +# "VIRTIO_NET_F_GUEST_UFO: Driver can receive UFO", +# "VIRTIO_NET_F_GUEST_ECN: Driver can receive TSO with ECN", +# "VIRTIO_NET_F_GUEST_TSO6: Driver can receive TSOv6", +# "VIRTIO_NET_F_GUEST_TSO4: Driver can receive TSOv4", +# "VIRTIO_NET_F_MAC: Device has given MAC address", +# "VIRTIO_NET_F_CTRL_GUEST_OFFLOADS: Control channel offloading reconfig. supported", +# "VIRTIO_NET_F_GUEST_CSUM: Driver handling packets with partial checksum supported", +# "VIRTIO_NET_F_CSUM: Device handling packets with partial checksum supported" +# ], +# "transports": [ +# "VIRTIO_RING_F_EVENT_IDX: Used & avail. event fields enabled", +# "VIRTIO_RING_F_INDIRECT_DESC: Indirect descriptors supported", +# "VIRTIO_F_VERSION_1: Device compliant for v1 spec (legacy)", +# "VIRTIO_F_ANY_LAYOUT: Device accepts arbitrary desc. layouts", +# "VIRTIO_F_NOTIFY_ON_EMPTY: Notify when device runs out of avail. descs. on VQ" +# ] +# }, +# "start-on-kick": false, +# "isr": 1, +# "broken": false, +# "status": { +# "statuses": [ +# "VIRTIO_CONFIG_S_ACKNOWLEDGE: Valid virtio device found", +# "VIRTIO_CONFIG_S_DRIVER: Guest OS compatible with device", +# "VIRTIO_CONFIG_S_FEATURES_OK: Feature negotiation complete", +# "VIRTIO_CONFIG_S_DRIVER_OK: Driver setup and ready" +# ] +# }, +# "num-vqs": 3, +# "guest-features": { +# "dev-features": [ +# "VIRTIO_NET_F_CTRL_MAC_ADDR: MAC address set through control channel", +# "VIRTIO_NET_F_GUEST_ANNOUNCE: Driver sending gratuitous packets supported", +# "VIRTIO_NET_F_CTRL_VLAN: Control channel VLAN filtering supported", +# "VIRTIO_NET_F_CTRL_RX: Control channel RX mode supported", +# "VIRTIO_NET_F_CTRL_VQ: Control channel available", +# "VIRTIO_NET_F_STATUS: Configuration status field available", +# "VIRTIO_NET_F_MRG_RXBUF: Driver can merge receive buffers", +# "VIRTIO_NET_F_HOST_UFO: Device can receive UFO", +# "VIRTIO_NET_F_HOST_ECN: Device can receive TSO with ECN", +# "VIRTIO_NET_F_HOST_TSO6: Device can receive TSOv6", +# "VIRTIO_NET_F_HOST_TSO4: Device can receive TSOv4", +# "VIRTIO_NET_F_GUEST_UFO: Driver can receive UFO", +# "VIRTIO_NET_F_GUEST_ECN: Driver can receive TSO with ECN", +# "VIRTIO_NET_F_GUEST_TSO6: Driver can receive TSOv6", +# "VIRTIO_NET_F_GUEST_TSO4: Driver can receive TSOv4", +# "VIRTIO_NET_F_MAC: Device has given MAC address", +# "VIRTIO_NET_F_CTRL_GUEST_OFFLOADS: Control channel offloading reconfig. supported", +# "VIRTIO_NET_F_GUEST_CSUM: Driver handling packets with partial checksum supported", +# "VIRTIO_NET_F_CSUM: Device handling packets with partial checksum supported" +# ], +# "transports": [ +# "VIRTIO_RING_F_EVENT_IDX: Used & avail. event fields enabled", +# "VIRTIO_RING_F_INDIRECT_DESC: Indirect descriptors supported", +# "VIRTIO_F_VERSION_1: Device compliant for v1 spec (legacy)" +# ] +# }, +# "host-features": { +# "dev-features": [ +# "VHOST_USER_F_PROTOCOL_FEATURES: Vhost-user protocol features negotiation supported", +# "VIRTIO_NET_F_GSO: Handling GSO-type packets supported", +# "VIRTIO_NET_F_CTRL_MAC_ADDR: MAC address set through control channel", +# "VIRTIO_NET_F_GUEST_ANNOUNCE: Driver sending gratuitous packets supported", +# "VIRTIO_NET_F_CTRL_RX_EXTRA: Extra RX mode control supported", +# "VIRTIO_NET_F_CTRL_VLAN: Control channel VLAN filtering supported", +# "VIRTIO_NET_F_CTRL_RX: Control channel RX mode supported", +# "VIRTIO_NET_F_CTRL_VQ: Control channel available", +# "VIRTIO_NET_F_STATUS: Configuration status field available", +# "VIRTIO_NET_F_MRG_RXBUF: Driver can merge receive buffers", +# "VIRTIO_NET_F_HOST_UFO: Device can receive UFO", +# "VIRTIO_NET_F_HOST_ECN: Device can receive TSO with ECN", +# "VIRTIO_NET_F_HOST_TSO6: Device can receive TSOv6", +# "VIRTIO_NET_F_HOST_TSO4: Device can receive TSOv4", +# "VIRTIO_NET_F_GUEST_UFO: Driver can receive UFO", +# "VIRTIO_NET_F_GUEST_ECN: Driver can receive TSO with ECN", +# "VIRTIO_NET_F_GUEST_TSO6: Driver can receive TSOv6", +# "VIRTIO_NET_F_GUEST_TSO4: Driver can receive TSOv4", +# "VIRTIO_NET_F_MAC: Device has given MAC address", +# "VIRTIO_NET_F_CTRL_GUEST_OFFLOADS: Control channel offloading reconfig. supported", +# "VIRTIO_NET_F_GUEST_CSUM: Driver handling packets with partial checksum supported", +# "VIRTIO_NET_F_CSUM: Device handling packets with partial checksum supported" +# ], +# "transports": [ +# "VIRTIO_RING_F_EVENT_IDX: Used & avail. event fields enabled", +# "VIRTIO_RING_F_INDIRECT_DESC: Indirect descriptors supported", +# "VIRTIO_F_VERSION_1: Device compliant for v1 spec (legacy)", +# "VIRTIO_F_ANY_LAYOUT: Device accepts arbitrary desc. layouts", +# "VIRTIO_F_NOTIFY_ON_EMPTY: Notify when device runs out of avail. descs. on VQ" +# ] +# }, +# "use-guest-notifier-mask": true, +# "vm-running": true, +# "queue-sel": 2, +# "disabled": false, +# "vhost-started": true, +# "use-started": true +# } +# } +## +{ 'command': 'x-query-virtio-status', + 'data': { 'path': 'str' }, + 'returns': 'VirtioStatus', + 'features': [ 'unstable' ] } + +## +# @VirtioDeviceStatus: +# +# A structure defined to list the configuration statuses of a virtio +# device +# +# @statuses: List of decoded configuration statuses of the virtio +# device +# +# @unknown-statuses: Virtio device statuses bitmap that have not been +# decoded +# +# Since: 7.2 +## +{ 'struct': 'VirtioDeviceStatus', + 'data': { 'statuses': [ 'str' ], + '*unknown-statuses': 'uint8' } } + +## +# @VhostDeviceProtocols: +# +# A structure defined to list the vhost user protocol features of a +# Vhost User device +# +# @protocols: List of decoded vhost user protocol features of a vhost +# user device +# +# @unknown-protocols: Vhost user device protocol features bitmap that +# have not been decoded +# +# Since: 7.2 +## +{ 'struct': 'VhostDeviceProtocols', + 'data': { 'protocols': [ 'str' ], + '*unknown-protocols': 'uint64' } } + +## +# @VirtioDeviceFeatures: +# +# The common fields that apply to most Virtio devices. Some devices +# may not have their own device-specific features (e.g. virtio-rng). +# +# @transports: List of transport features of the virtio device +# +# @dev-features: List of device-specific features (if the device has +# unique features) +# +# @unknown-dev-features: Virtio device features bitmap that have not +# been decoded +# +# Since: 7.2 +## +{ 'struct': 'VirtioDeviceFeatures', + 'data': { 'transports': [ 'str' ], + '*dev-features': [ 'str' ], + '*unknown-dev-features': 'uint64' } } + +## +# @VirtQueueStatus: +# +# Information of a VirtIODevice VirtQueue, including most members of +# the VirtQueue data structure. +# +# @name: Name of the VirtIODevice that uses this VirtQueue +# +# @queue-index: VirtQueue queue_index +# +# @inuse: VirtQueue inuse +# +# @vring-num: VirtQueue vring.num +# +# @vring-num-default: VirtQueue vring.num_default +# +# @vring-align: VirtQueue vring.align +# +# @vring-desc: VirtQueue vring.desc (descriptor area) +# +# @vring-avail: VirtQueue vring.avail (driver area) +# +# @vring-used: VirtQueue vring.used (device area) +# +# @last-avail-idx: VirtQueue last_avail_idx or return of vhost_dev +# vhost_get_vring_base (if vhost active) +# +# @shadow-avail-idx: VirtQueue shadow_avail_idx +# +# @used-idx: VirtQueue used_idx +# +# @signalled-used: VirtQueue signalled_used +# +# @signalled-used-valid: VirtQueue signalled_used_valid flag +# +# Since: 7.2 +## +{ 'struct': 'VirtQueueStatus', + 'data': { 'name': 'str', + 'queue-index': 'uint16', + 'inuse': 'uint32', + 'vring-num': 'uint32', + 'vring-num-default': 'uint32', + 'vring-align': 'uint32', + 'vring-desc': 'uint64', + 'vring-avail': 'uint64', + 'vring-used': 'uint64', + '*last-avail-idx': 'uint16', + '*shadow-avail-idx': 'uint16', + 'used-idx': 'uint16', + 'signalled-used': 'uint16', + 'signalled-used-valid': 'bool' } } + +## +# @x-query-virtio-queue-status: +# +# Return the status of a given VirtIODevice's VirtQueue +# +# @path: VirtIODevice canonical QOM path +# +# @queue: VirtQueue index to examine +# +# Features: +# +# @unstable: This command is meant for debugging. +# +# Returns: VirtQueueStatus of the VirtQueue +# +# Notes: last_avail_idx will not be displayed in the case where the +# selected VirtIODevice has a running vhost device and the +# VirtIODevice VirtQueue index (queue) does not exist for the +# corresponding vhost device vhost_virtqueue. Also, +# shadow_avail_idx will not be displayed in the case where the +# selected VirtIODevice has a running vhost device. +# +# Since: 7.2 +# +# Examples: +# +# 1. Get VirtQueueStatus for virtio-vsock (vhost-vsock running) +# +# -> { "execute": "x-query-virtio-queue-status", +# "arguments": { "path": "/machine/peripheral/vsock0/virtio-backend", +# "queue": 1 } +# } +# <- { "return": { +# "signalled-used": 0, +# "inuse": 0, +# "name": "vhost-vsock", +# "vring-align": 4096, +# "vring-desc": 5217370112, +# "signalled-used-valid": false, +# "vring-num-default": 128, +# "vring-avail": 5217372160, +# "queue-index": 1, +# "last-avail-idx": 0, +# "vring-used": 5217372480, +# "used-idx": 0, +# "vring-num": 128 +# } +# } +# +# 2. Get VirtQueueStatus for virtio-serial (no vhost) +# +# -> { "execute": "x-query-virtio-queue-status", +# "arguments": { "path": "/machine/peripheral-anon/device[0]/virtio-backend", +# "queue": 20 } +# } +# <- { "return": { +# "signalled-used": 0, +# "inuse": 0, +# "name": "virtio-serial", +# "vring-align": 4096, +# "vring-desc": 5182074880, +# "signalled-used-valid": false, +# "vring-num-default": 128, +# "vring-avail": 5182076928, +# "queue-index": 20, +# "last-avail-idx": 0, +# "vring-used": 5182077248, +# "used-idx": 0, +# "shadow-avail-idx": 0, +# "vring-num": 128 +# } +# } +## +{ 'command': 'x-query-virtio-queue-status', + 'data': { 'path': 'str', 'queue': 'uint16' }, + 'returns': 'VirtQueueStatus', + 'features': [ 'unstable' ] } + +## +# @VirtVhostQueueStatus: +# +# Information of a vhost device's vhost_virtqueue, including most +# members of the vhost_dev vhost_virtqueue data structure. +# +# @name: Name of the VirtIODevice that uses this vhost_virtqueue +# +# @kick: vhost_virtqueue kick +# +# @call: vhost_virtqueue call +# +# @desc: vhost_virtqueue desc +# +# @avail: vhost_virtqueue avail +# +# @used: vhost_virtqueue used +# +# @num: vhost_virtqueue num +# +# @desc-phys: vhost_virtqueue desc_phys (descriptor area physical +# address) +# +# @desc-size: vhost_virtqueue desc_size +# +# @avail-phys: vhost_virtqueue avail_phys (driver area physical +# address) +# +# @avail-size: vhost_virtqueue avail_size +# +# @used-phys: vhost_virtqueue used_phys (device area physical address) +# +# @used-size: vhost_virtqueue used_size +# +# Since: 7.2 +## +{ 'struct': 'VirtVhostQueueStatus', + 'data': { 'name': 'str', + 'kick': 'int', + 'call': 'int', + 'desc': 'uint64', + 'avail': 'uint64', + 'used': 'uint64', + 'num': 'int', + 'desc-phys': 'uint64', + 'desc-size': 'uint32', + 'avail-phys': 'uint64', + 'avail-size': 'uint32', + 'used-phys': 'uint64', + 'used-size': 'uint32' } } + +## +# @x-query-virtio-vhost-queue-status: +# +# Return information of a given vhost device's vhost_virtqueue +# +# @path: VirtIODevice canonical QOM path +# +# @queue: vhost_virtqueue index to examine +# +# Features: +# +# @unstable: This command is meant for debugging. +# +# Returns: VirtVhostQueueStatus of the vhost_virtqueue +# +# Since: 7.2 +# +# Examples: +# +# 1. Get vhost_virtqueue status for vhost-crypto +# +# -> { "execute": "x-query-virtio-vhost-queue-status", +# "arguments": { "path": "/machine/peripheral/crypto0/virtio-backend", +# "queue": 0 } +# } +# <- { "return": { +# "avail-phys": 5216124928, +# "name": "virtio-crypto", +# "used-phys": 5216127040, +# "avail-size": 2054, +# "desc-size": 16384, +# "used-size": 8198, +# "desc": 140141447430144, +# "num": 1024, +# "call": 0, +# "avail": 140141447446528, +# "desc-phys": 5216108544, +# "used": 140141447448640, +# "kick": 0 +# } +# } +# +# 2. Get vhost_virtqueue status for vhost-vsock +# +# -> { "execute": "x-query-virtio-vhost-queue-status", +# "arguments": { "path": "/machine/peripheral/vsock0/virtio-backend", +# "queue": 0 } +# } +# <- { "return": { +# "avail-phys": 5182261248, +# "name": "vhost-vsock", +# "used-phys": 5182261568, +# "avail-size": 262, +# "desc-size": 2048, +# "used-size": 1030, +# "desc": 140141413580800, +# "num": 128, +# "call": 0, +# "avail": 140141413582848, +# "desc-phys": 5182259200, +# "used": 140141413583168, +# "kick": 0 +# } +# } +## +{ 'command': 'x-query-virtio-vhost-queue-status', + 'data': { 'path': 'str', 'queue': 'uint16' }, + 'returns': 'VirtVhostQueueStatus', + 'features': [ 'unstable' ] } + +## +# @VirtioRingDesc: +# +# Information regarding the vring descriptor area +# +# @addr: Guest physical address of the descriptor area +# +# @len: Length of the descriptor area +# +# @flags: List of descriptor flags +# +# Since: 7.2 +## +{ 'struct': 'VirtioRingDesc', + 'data': { 'addr': 'uint64', + 'len': 'uint32', + 'flags': [ 'str' ] } } + +## +# @VirtioRingAvail: +# +# Information regarding the avail vring (a.k.a. driver area) +# +# @flags: VRingAvail flags +# +# @idx: VRingAvail index +# +# @ring: VRingAvail ring[] entry at provided index +# +# Since: 7.2 +## +{ 'struct': 'VirtioRingAvail', + 'data': { 'flags': 'uint16', + 'idx': 'uint16', + 'ring': 'uint16' } } + +## +# @VirtioRingUsed: +# +# Information regarding the used vring (a.k.a. device area) +# +# @flags: VRingUsed flags +# +# @idx: VRingUsed index +# +# Since: 7.2 +## +{ 'struct': 'VirtioRingUsed', + 'data': { 'flags': 'uint16', + 'idx': 'uint16' } } + +## +# @VirtioQueueElement: +# +# Information regarding a VirtQueue's VirtQueueElement including +# descriptor, driver, and device areas +# +# @name: Name of the VirtIODevice that uses this VirtQueue +# +# @index: Index of the element in the queue +# +# @descs: List of descriptors (VirtioRingDesc) +# +# @avail: VRingAvail info +# +# @used: VRingUsed info +# +# Since: 7.2 +## +{ 'struct': 'VirtioQueueElement', + 'data': { 'name': 'str', + 'index': 'uint32', + 'descs': [ 'VirtioRingDesc' ], + 'avail': 'VirtioRingAvail', + 'used': 'VirtioRingUsed' } } + +## +# @x-query-virtio-queue-element: +# +# Return the information about a VirtQueue's VirtQueueElement +# +# @path: VirtIODevice canonical QOM path +# +# @queue: VirtQueue index to examine +# +# @index: Index of the element in the queue (default: head of the +# queue) +# +# Features: +# +# @unstable: This command is meant for debugging. +# +# Returns: VirtioQueueElement information +# +# Since: 7.2 +# +# Examples: +# +# 1. Introspect on virtio-net's VirtQueue 0 at index 5 +# +# -> { "execute": "x-query-virtio-queue-element", +# "arguments": { "path": "/machine/peripheral-anon/device[1]/virtio-backend", +# "queue": 0, +# "index": 5 } +# } +# <- { "return": { +# "index": 5, +# "name": "virtio-net", +# "descs": [ +# { +# "flags": ["write"], +# "len": 1536, +# "addr": 5257305600 +# } +# ], +# "avail": { +# "idx": 256, +# "flags": 0, +# "ring": 5 +# }, +# "used": { +# "idx": 13, +# "flags": 0 +# } +# } +# } +# +# 2. Introspect on virtio-crypto's VirtQueue 1 at head +# +# -> { "execute": "x-query-virtio-queue-element", +# "arguments": { "path": "/machine/peripheral/crypto0/virtio-backend", +# "queue": 1 } +# } +# <- { "return": { +# "index": 0, +# "name": "virtio-crypto", +# "descs": [ +# { +# "flags": [], +# "len": 0, +# "addr": 8080268923184214134 +# } +# ], +# "avail": { +# "idx": 280, +# "flags": 0, +# "ring": 0 +# }, +# "used": { +# "idx": 280, +# "flags": 0 +# } +# } +# } +# +# 3. Introspect on virtio-scsi's VirtQueue 2 at head +# +# -> { "execute": "x-query-virtio-queue-element", +# "arguments": { "path": "/machine/peripheral-anon/device[2]/virtio-backend", +# "queue": 2 } +# } +# <- { "return": { +# "index": 19, +# "name": "virtio-scsi", +# "descs": [ +# { +# "flags": ["used", "indirect", "write"], +# "len": 4099327944, +# "addr": 12055409292258155293 +# } +# ], +# "avail": { +# "idx": 1147, +# "flags": 0, +# "ring": 19 +# }, +# "used": { +# "idx": 280, +# "flags": 0 +# } +# } +# } +## +{ 'command': 'x-query-virtio-queue-element', + 'data': { 'path': 'str', 'queue': 'uint16', '*index': 'uint16' }, + 'returns': 'VirtioQueueElement', + 'features': [ 'unstable' ] } + +## +# @IOThreadVirtQueueMapping: +# +# Describes the subset of virtqueues assigned to an IOThread. +# +# @iothread: the id of IOThread object +# +# @vqs: an optional array of virtqueue indices that will be handled by +# this IOThread. When absent, virtqueues are assigned round-robin +# across all IOThreadVirtQueueMappings provided. Either all +# IOThreadVirtQueueMappings must have @vqs or none of them must +# have it. +# +# Since: 9.0 +## + +{ 'struct': 'IOThreadVirtQueueMapping', + 'data': { 'iothread': 'str', '*vqs': ['uint16'] } } + +## +# @DummyVirtioForceArrays: +# +# Not used by QMP; hack to let us use IOThreadVirtQueueMappingList +# internally +# +# Since: 9.0 +## + +{ 'struct': 'DummyVirtioForceArrays', + 'data': { 'unused-iothread-vq-mapping': ['IOThreadVirtQueueMapping'] } } + +## +# @GranuleMode: +# +# @4k: granule page size of 4KiB +# +# @8k: granule page size of 8KiB +# +# @16k: granule page size of 16KiB +# +# @64k: granule page size of 64KiB +# +# @host: granule matches the host page size +# +# Since: 9.0 +## +{ 'enum': 'GranuleMode', + 'data': [ '4k', '8k', '16k', '64k', 'host' ] } diff --git a/qapi/yank.json b/qapi/yank.json index 167a775594..89f2f4d199 100644 --- a/qapi/yank.json +++ b/qapi/yank.json @@ -9,7 +9,7 @@ ## # @YankInstanceType: # -# An enumeration of yank instance types. See @YankInstance for more +# An enumeration of yank instance types. See @YankInstance for more # information. # # Since: 6.0 @@ -20,8 +20,8 @@ ## # @YankInstanceBlockNode: # -# Specifies which block graph node to yank. See @YankInstance for more -# information. +# Specifies which block graph node to yank. See @YankInstance for +# more information. # # @node-name: the name of the block graph node # @@ -33,8 +33,8 @@ ## # @YankInstanceChardev: # -# Specifies which character device to yank. See @YankInstance for more -# information. +# Specifies which character device to yank. See @YankInstance for +# more information. # # @id: the chardev's ID # @@ -46,20 +46,20 @@ ## # @YankInstance: # -# A yank instance can be yanked with the @yank qmp command to recover from a -# hanging QEMU. +# A yank instance can be yanked with the @yank qmp command to recover +# from a hanging QEMU. +# +# @type: yank instance type # # Currently implemented yank instances: -# - nbd block device: -# Yanking it will shut down the connection to the nbd server without -# attempting to reconnect. -# - socket chardev: -# Yanking it will shut down the connected socket. -# - migration: -# Yanking it will shut down all migration connections. Unlike -# @migrate_cancel, it will not notify the migration process, so migration -# will go into @failed state, instead of @cancelled state. @yank should be -# used to recover from hangs. +# +# - nbd block device: Yanking it will shut down the connection to the +# nbd server without attempting to reconnect. +# - socket chardev: Yanking it will shut down the connected socket. +# - migration: Yanking it will shut down all migration connections. +# Unlike @migrate_cancel, it will not notify the migration process, +# so migration will go into @failed state, instead of @cancelled +# state. @yank should be used to recover from hangs. # # Since: 6.0 ## @@ -73,23 +73,23 @@ ## # @yank: # -# Try to recover from hanging QEMU by yanking the specified instances. See -# @YankInstance for more information. +# Try to recover from hanging QEMU by yanking the specified instances. +# See @YankInstance for more information. # -# Takes a list of @YankInstance as argument. +# @instances: the instances to be yanked # -# Returns: - Nothing on success -# - @DeviceNotFound error, if any of the YankInstances doesn't exist +# Errors: +# - If any of the YankInstances doesn't exist, DeviceNotFound # # Example: # -# -> { "execute": "yank", -# "arguments": { -# "instances": [ -# { "type": "block-node", -# "node-name": "nbd0" } -# ] } } -# <- { "return": {} } +# -> { "execute": "yank", +# "arguments": { +# "instances": [ +# { "type": "block-node", +# "node-name": "nbd0" } +# ] } } +# <- { "return": {} } # # Since: 6.0 ## @@ -100,17 +100,17 @@ ## # @query-yank: # -# Query yank instances. See @YankInstance for more information. +# Query yank instances. See @YankInstance for more information. # # Returns: list of @YankInstance # # Example: # -# -> { "execute": "query-yank" } -# <- { "return": [ -# { "type": "block-node", -# "node-name": "nbd0" } -# ] } +# -> { "execute": "query-yank" } +# <- { "return": [ +# { "type": "block-node", +# "node-name": "nbd0" } +# ] } # # Since: 6.0 ## diff --git a/qemu-edid.c b/qemu-edid.c index 20c958d9c7..92e1a660a7 100644 --- a/qemu-edid.c +++ b/qemu-edid.c @@ -92,6 +92,10 @@ int main(int argc, char *argv[]) fprintf(stderr, "not a number: %s\n", optarg); exit(1); } + if (dpi == 0) { + fprintf(stderr, "cannot be zero: %s\n", optarg); + exit(1); + } break; case 'v': info.vendor = optarg; diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 1b1dab5b17..c9dd70a892 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -1,3 +1,5 @@ +HXCOMM See docs/devel/docs.rst for the format of this file. +HXCOMM HXCOMM Keep the list of subcommands sorted by name. HXCOMM Use DEFHEADING() to define headings in both help text and rST HXCOMM Text between SRST and ERST are copied to rST version and @@ -88,9 +90,9 @@ SRST 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") + "rebase [--object objectdef] [--image-opts] [-U] [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] [-c] -b backing_file [-F backing_fmt] filename") 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 +.. option:: rebase [--object OBJECTDEF] [--image-opts] [-U] [-q] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-p] [-u] [-c] -b BACKING_FILE [-F BACKING_FMT] FILENAME ERST DEF("resize", img_resize, diff --git a/qemu-img.c b/qemu-img.c index 4cf4d2423d..7668f86769 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -48,6 +48,7 @@ #include "sysemu/block-backend.h" #include "block/block_int.h" #include "block/blockjob.h" +#include "block/dirty-bitmap.h" #include "block/qapi.h" #include "crypto/init.h" #include "trace/control.h" @@ -164,8 +165,8 @@ void help(void) " 'output_filename' is the destination disk image filename\n" " 'output_fmt' is the destination format\n" " 'options' is a comma separated list of format specific options in a\n" - " name=value format. Use -o ? for an overview of the options supported by the\n" - " used format\n" + " name=value format. Use -o help for an overview of the options supported by\n" + " the used format\n" " 'snapshot_param' is param used for internal snapshot, format\n" " is 'snapshot.id=[ID],snapshot.name=[NAME]', or\n" " '[ID_OR_NAME]'\n" @@ -234,25 +235,25 @@ void help(void) } /* - * Is @optarg safe for accumulate_options()? + * Is @list safe for accumulate_options()? * It is when multiple of them can be joined together separated by ','. - * To make that work, @optarg must not start with ',' (or else a + * To make that work, @list must not start with ',' (or else a * separating ',' preceding it gets escaped), and it must not end with * an odd number of ',' (or else a separating ',' following it gets * escaped), or be empty (or else a separating ',' preceding it can * escape a separating ',' following it). * */ -static bool is_valid_option_list(const char *optarg) +static bool is_valid_option_list(const char *list) { - size_t len = strlen(optarg); + size_t len = strlen(list); size_t i; - if (!optarg[0] || optarg[0] == ',') { + if (!list[0] || list[0] == ',') { return false; } - for (i = len; i > 0 && optarg[i - 1] == ','; i--) { + for (i = len; i > 0 && list[i - 1] == ','; i--) { } if ((len - i) % 2) { return false; @@ -261,19 +262,19 @@ static bool is_valid_option_list(const char *optarg) return true; } -static int accumulate_options(char **options, char *optarg) +static int accumulate_options(char **options, char *list) { char *new_options; - if (!is_valid_option_list(optarg)) { - error_report("Invalid option list: %s", optarg); + if (!is_valid_option_list(list)) { + error_report("Invalid option list: %s", list); return -1; } if (!*options) { - *options = g_strdup(optarg); + *options = g_strdup(list); } else { - new_options = g_strdup_printf("%s,%s", *options, optarg); + new_options = g_strdup_printf("%s,%s", *options, list); g_free(*options); *options = new_options; } @@ -449,6 +450,11 @@ static BlockBackend *img_open(bool image_opts, blk = img_open_file(filename, NULL, fmt, flags, writethrough, quiet, force_share); } + + if (blk) { + blk_set_force_allow_inactivate(blk); + } + return blk; } @@ -911,10 +917,11 @@ static void run_block_job(BlockJob *job, Error **errp) AioContext *aio_context = block_job_get_aio_context(job); int ret = 0; - aio_context_acquire(aio_context); - job_ref(&job->job); + job_lock(); + job_ref_locked(&job->job); do { float progress = 0.0f; + job_unlock(); aio_poll(aio_context, true); progress_get_snapshot(&job->job.progress, &progress_current, @@ -923,15 +930,17 @@ static void run_block_job(BlockJob *job, Error **errp) progress = (float)progress_current / progress_total * 100.f; } qemu_progress_print(progress, 0); - } while (!job_is_ready(&job->job) && !job_is_completed(&job->job)); + job_lock(); + } while (!job_is_ready_locked(&job->job) && + !job_is_completed_locked(&job->job)); - if (!job_is_completed(&job->job)) { - ret = job_complete_sync(&job->job, errp); + if (!job_is_completed_locked(&job->job)) { + ret = job_complete_sync_locked(&job->job, errp); } else { ret = job->job.ret; } - job_unref(&job->job); - aio_context_release(aio_context); + job_unref_locked(&job->job); + job_unlock(); /* publish completion progress only when success */ if (!ret) { @@ -951,7 +960,6 @@ static int img_commit(int argc, char **argv) Error *local_err = NULL; CommonBlockJobCBInfo cbi; bool image_opts = false; - AioContext *aio_context; int64_t rate_limit = 0; fmt = NULL; @@ -1041,12 +1049,14 @@ static int img_commit(int argc, char **argv) qemu_progress_init(progress, 1.f); qemu_progress_print(0.f, 100); + bdrv_graph_rdlock_main_loop(); if (base) { base_bs = bdrv_find_backing_image(bs, base); if (!base_bs) { error_setg(&local_err, "Did not find '%s' in the backing chain of '%s'", base, filename); + bdrv_graph_rdunlock_main_loop(); goto done; } } else { @@ -1056,21 +1066,20 @@ static int img_commit(int argc, char **argv) base_bs = bdrv_backing_chain_next(bs); if (!base_bs) { error_setg(&local_err, "Image does not have a backing file"); + bdrv_graph_rdunlock_main_loop(); goto done; } } + bdrv_graph_rdunlock_main_loop(); cbi = (CommonBlockJobCBInfo){ .errp = &local_err, .bs = bs, }; - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); commit_active_start("commit", bs, base_bs, JOB_DEFAULT, rate_limit, BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb, &cbi, false, &local_err); - aio_context_release(aio_context); if (local_err) { goto done; } @@ -1116,6 +1125,14 @@ unref_backing: done: qemu_progress_end(); + /* + * Manually inactivate the image first because this way we can know whether + * an error occurred. blk_unref() doesn't tell us about failures. + */ + ret = bdrv_inactivate_all(); + if (ret < 0 && !local_err) { + error_setg_errno(&local_err, -ret, "Error while closing the image"); + } blk_unref(blk); if (local_err) { @@ -1257,23 +1274,29 @@ static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum, } /* - * Compares two buffers sector by sector. Returns 0 if the first - * sector of each buffer matches, non-zero otherwise. + * Compares two buffers chunk by chunk, where @chsize is the chunk size. + * If @chsize is 0, default chunk size of BDRV_SECTOR_SIZE is used. + * Returns 0 if the first chunk of each buffer matches, non-zero otherwise. * - * pnum is set to the sector-aligned size of the buffer prefix that - * has the same matching status as the first sector. + * @pnum is set to the size of the buffer prefix aligned to @chsize that + * has the same matching status as the first chunk. */ static int compare_buffers(const uint8_t *buf1, const uint8_t *buf2, - int64_t bytes, int64_t *pnum) + int64_t bytes, uint64_t chsize, int64_t *pnum) { bool res; - int64_t i = MIN(bytes, BDRV_SECTOR_SIZE); + int64_t i; assert(bytes > 0); + if (!chsize) { + chsize = BDRV_SECTOR_SIZE; + } + i = MIN(bytes, chsize); + res = !!memcmp(buf1, buf2, i); while (i < bytes) { - int64_t len = MIN(bytes - i, BDRV_SECTOR_SIZE); + int64_t len = MIN(bytes - i, chsize); if (!!memcmp(buf1 + i, buf2 + i, len) != res) { break; @@ -1309,7 +1332,7 @@ static int check_empty_sectors(BlockBackend *blk, int64_t offset, int ret = 0; int64_t idx; - ret = blk_pread(blk, offset, buffer, bytes); + ret = blk_pread(blk, offset, bytes, buffer, 0); if (ret < 0) { error_report("Error while reading offset %" PRId64 " of %s: %s", offset, filename, strerror(-ret)); @@ -1526,7 +1549,7 @@ static int img_compare(int argc, char **argv) int64_t pnum; chunk = MIN(chunk, IO_BUF_SIZE); - ret = blk_pread(blk1, offset, buf1, chunk); + ret = blk_pread(blk1, offset, chunk, buf1, 0); if (ret < 0) { error_report("Error while reading offset %" PRId64 " of %s: %s", @@ -1534,7 +1557,7 @@ static int img_compare(int argc, char **argv) ret = 4; goto out; } - ret = blk_pread(blk2, offset, buf2, chunk); + ret = blk_pread(blk2, offset, chunk, buf2, 0); if (ret < 0) { error_report("Error while reading offset %" PRId64 " of %s: %s", @@ -1542,7 +1565,7 @@ static int img_compare(int argc, char **argv) ret = 4; goto out; } - ret = compare_buffers(buf1, buf2, chunk, &pnum); + ret = compare_buffers(buf1, buf2, chunk, 0, &pnum); if (ret || pnum != chunk) { qprintf(quiet, "Content mismatch at offset %" PRId64 "!\n", offset + (ret ? 0 : pnum)); @@ -1690,7 +1713,8 @@ static void convert_select_part(ImgConvertState *s, int64_t sector_num, } } -static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) +static int coroutine_mixed_fn GRAPH_RDLOCK +convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) { int64_t src_cur_offset; int ret, n, src_cur; @@ -1974,7 +1998,9 @@ static void coroutine_fn convert_co_do_copy(void *opaque) qemu_co_mutex_unlock(&s->lock); break; } - n = convert_iteration_sectors(s, s->sector_num); + WITH_GRAPH_RDLOCK_GUARD() { + n = convert_iteration_sectors(s, s->sector_num); + } if (n < 0) { qemu_co_mutex_unlock(&s->lock); s->ret = n; @@ -2022,7 +2048,9 @@ retry: if (s->ret == -EINPROGRESS) { if (copy_range) { - ret = convert_co_copy_range(s, sector_num, n); + WITH_GRAPH_RDLOCK_GUARD() { + ret = convert_co_copy_range(s, sector_num, n); + } if (ret) { s->copy_range = false; goto retry; @@ -2072,7 +2100,9 @@ static int convert_do_copy(ImgConvertState *s) /* Check whether we have zero initialisation or can get it efficiently */ if (!s->has_zero_init && s->target_is_new && s->min_sparse && !s->target_has_backing) { + bdrv_graph_rdlock_main_loop(); s->has_zero_init = bdrv_has_zero_init(blk_bs(s->target)); + bdrv_graph_rdunlock_main_loop(); } /* Allocate buffer for copied data. For compressed images, only one cluster @@ -2086,7 +2116,9 @@ static int convert_do_copy(ImgConvertState *s) } while (sector_num < s->total_sectors) { + bdrv_graph_rdlock_main_loop(); n = convert_iteration_sectors(s, sector_num); + bdrv_graph_rdunlock_main_loop(); if (n < 0) { return n; } @@ -2114,7 +2146,7 @@ static int convert_do_copy(ImgConvertState *s) if (s->compressed && !s->ret) { /* signal EOF to align */ - ret = blk_pwrite_compressed(s->target, 0, NULL, 0); + ret = blk_pwrite_compressed(s->target, 0, 0, NULL); if (ret < 0) { return ret; } @@ -2728,8 +2760,10 @@ static int img_convert(int argc, char **argv) * s.target_backing_sectors has to be negative, which it will * be automatically). The backing file length is used only * for optimizations, so such a case is not fatal. */ + bdrv_graph_rdlock_main_loop(); s.target_backing_sectors = bdrv_nb_sectors(bdrv_backing_chain_next(out_bs)); + bdrv_graph_rdunlock_main_loop(); } else { s.target_backing_sectors = -1; } @@ -2800,13 +2834,13 @@ static void dump_snapshots(BlockDriverState *bs) g_free(sn_tab); } -static void dump_json_image_info_list(ImageInfoList *list) +static void dump_json_block_graph_info_list(BlockGraphInfoList *list) { GString *str; QObject *obj; Visitor *v = qobject_output_visitor_new(&obj); - visit_type_ImageInfoList(v, NULL, &list, &error_abort); + visit_type_BlockGraphInfoList(v, NULL, &list, &error_abort); visit_complete(v, &obj); str = qobject_to_json_pretty(obj, true); assert(str != NULL); @@ -2816,13 +2850,13 @@ static void dump_json_image_info_list(ImageInfoList *list) g_string_free(str, true); } -static void dump_json_image_info(ImageInfo *info) +static void dump_json_block_graph_info(BlockGraphInfo *info) { GString *str; QObject *obj; Visitor *v = qobject_output_visitor_new(&obj); - visit_type_ImageInfo(v, NULL, &info, &error_abort); + visit_type_BlockGraphInfo(v, NULL, &info, &error_abort); visit_complete(v, &obj); str = qobject_to_json_pretty(obj, true); assert(str != NULL); @@ -2832,9 +2866,30 @@ static void dump_json_image_info(ImageInfo *info) g_string_free(str, true); } -static void dump_human_image_info_list(ImageInfoList *list) +static void dump_human_image_info(BlockGraphInfo *info, int indentation, + const char *path) { - ImageInfoList *elem; + BlockChildInfoList *children_list; + + bdrv_node_info_dump(qapi_BlockGraphInfo_base(info), indentation, + info->children == NULL); + + for (children_list = info->children; children_list; + children_list = children_list->next) + { + BlockChildInfo *child = children_list->value; + g_autofree char *child_path = NULL; + + printf("%*sChild node '%s%s':\n", + indentation * 4, "", path, child->name); + child_path = g_strdup_printf("%s%s/", path, child->name); + dump_human_image_info(child->info, indentation + 1, child_path); + } +} + +static void dump_human_image_info_list(BlockGraphInfoList *list) +{ + BlockGraphInfoList *elem; bool delim = false; for (elem = list; elem; elem = elem->next) { @@ -2843,7 +2898,7 @@ static void dump_human_image_info_list(ImageInfoList *list) } delim = true; - bdrv_image_info_dump(elem->value); + dump_human_image_info(elem->value, 0, "/"); } } @@ -2853,24 +2908,24 @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b) } /** - * Open an image file chain and return an ImageInfoList + * Open an image file chain and return an BlockGraphInfoList * * @filename: topmost image filename * @fmt: topmost image format (may be NULL to autodetect) * @chain: true - enumerate entire backing file chain * false - only topmost image file * - * Returns a list of ImageInfo objects or NULL if there was an error opening an - * image file. If there was an error a message will have been printed to - * stderr. + * Returns a list of BlockNodeInfo objects or NULL if there was an error + * opening an image file. If there was an error a message will have been + * printed to stderr. */ -static ImageInfoList *collect_image_info_list(bool image_opts, - const char *filename, - const char *fmt, - bool chain, bool force_share) +static BlockGraphInfoList *collect_image_info_list(bool image_opts, + const char *filename, + const char *fmt, + bool chain, bool force_share) { - ImageInfoList *head = NULL; - ImageInfoList **tail = &head; + BlockGraphInfoList *head = NULL; + BlockGraphInfoList **tail = &head; GHashTable *filenames; Error *err = NULL; @@ -2879,7 +2934,7 @@ static ImageInfoList *collect_image_info_list(bool image_opts, while (filename) { BlockBackend *blk; BlockDriverState *bs; - ImageInfo *info; + BlockGraphInfo *info; if (g_hash_table_lookup_extended(filenames, filename, NULL, NULL)) { error_report("Backing file '%s' creates an infinite loop.", @@ -2896,7 +2951,17 @@ static ImageInfoList *collect_image_info_list(bool image_opts, } bs = blk_bs(blk); - bdrv_query_image_info(bs, &info, &err); + /* + * Note that the returned BlockGraphInfo object will not have + * information about this image's backing node, because we have opened + * it with BDRV_O_NO_BACKING. Printing this object will therefore not + * duplicate the backing chain information that we obtain by walking + * the chain manually here. + */ + bdrv_graph_rdlock_main_loop(); + bdrv_query_block_graph_info(bs, &info, &err); + bdrv_graph_rdunlock_main_loop(); + if (err) { error_report_err(err); blk_unref(blk); @@ -2912,15 +2977,15 @@ static ImageInfoList *collect_image_info_list(bool image_opts, image_opts = false; if (chain) { - if (info->has_full_backing_filename) { + if (info->full_backing_filename) { filename = info->full_backing_filename; - } else if (info->has_backing_filename) { + } else if (info->backing_filename) { error_report("Could not determine absolute backing filename," " but backing filename '%s' present", info->backing_filename); goto err; } - if (info->has_backing_filename_format) { + if (info->backing_filename_format) { fmt = info->backing_filename_format; } } @@ -2929,7 +2994,7 @@ static ImageInfoList *collect_image_info_list(bool image_opts, return head; err: - qapi_free_ImageInfoList(head); + qapi_free_BlockGraphInfoList(head); g_hash_table_destroy(filenames); return NULL; } @@ -2940,7 +3005,7 @@ static int img_info(int argc, char **argv) OutputFormat output_format = OFORMAT_HUMAN; bool chain = false; const char *filename, *fmt, *output; - ImageInfoList *list; + BlockGraphInfoList *list; bool image_opts = false; bool force_share = false; @@ -3019,14 +3084,14 @@ static int img_info(int argc, char **argv) break; case OFORMAT_JSON: if (chain) { - dump_json_image_info_list(list); + dump_json_block_graph_info_list(list); } else { - dump_json_image_info(list->value); + dump_json_block_graph_info(list->value); } break; } - qapi_free_ImageInfoList(list); + qapi_free_BlockGraphInfoList(list); return 0; } @@ -3043,7 +3108,7 @@ static int dump_map_entry(OutputFormat output_format, MapEntry *e, printf("%#-16"PRIx64"%#-16"PRIx64"%#-16"PRIx64"%s\n", e->start, e->length, e->has_offset ? e->offset : 0, - e->has_filename ? e->filename : ""); + e->filename ?: ""); } /* This format ignores the distinction between 0, ZERO and ZERO|DATA. * Modify the flags here to allow more coalescing. @@ -3056,10 +3121,12 @@ static int dump_map_entry(OutputFormat output_format, MapEntry *e, case OFORMAT_JSON: printf("{ \"start\": %"PRId64", \"length\": %"PRId64"," " \"depth\": %"PRId64", \"present\": %s, \"zero\": %s," - " \"data\": %s", e->start, e->length, e->depth, + " \"data\": %s, \"compressed\": %s", + e->start, e->length, e->depth, e->present ? "true" : "false", e->zero ? "true" : "false", - e->data ? "true" : "false"); + e->data ? "true" : "false", + e->compressed ? "true" : "false"); if (e->has_offset) { printf(", \"offset\": %"PRId64"", e->offset); } @@ -3083,6 +3150,9 @@ static int get_block_status(BlockDriverState *bs, int64_t offset, int64_t map; char *filename = NULL; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + /* As an optimization, we could cache the current range of unallocated * clusters in each file of the chain, and avoid querying the same * range repeatedly. @@ -3120,11 +3190,11 @@ static int get_block_status(BlockDriverState *bs, int64_t offset, .length = bytes, .data = !!(ret & BDRV_BLOCK_DATA), .zero = !!(ret & BDRV_BLOCK_ZERO), + .compressed = !!(ret & BDRV_BLOCK_COMPRESSED), .offset = map, .has_offset = has_offset, .depth = depth, .present = !!(ret & BDRV_BLOCK_ALLOCATED), - .has_filename = filename, .filename = filename, }; @@ -3138,13 +3208,14 @@ static inline bool entry_mergeable(const MapEntry *curr, const MapEntry *next) } if (curr->zero != next->zero || curr->data != next->data || + curr->compressed != next->compressed || curr->depth != next->depth || curr->present != next->present || - curr->has_filename != next->has_filename || + !curr->filename != !next->filename || curr->has_offset != next->has_offset) { return false; } - if (curr->has_filename && strcmp(curr->filename, next->filename)) { + if (curr->filename && strcmp(curr->filename, next->filename)) { return false; } if (curr->has_offset && curr->offset + curr->length != next->offset) { @@ -3415,10 +3486,13 @@ static int img_snapshot(int argc, char **argv) sn.date_sec = rt / G_USEC_PER_SEC; sn.date_nsec = (rt % G_USEC_PER_SEC) * 1000; + bdrv_graph_rdlock_main_loop(); ret = bdrv_snapshot_create(bs, &sn); + bdrv_graph_rdunlock_main_loop(); + if (ret) { - error_report("Could not create snapshot '%s': %d (%s)", - snapshot_name, ret, strerror(-ret)); + error_report("Could not create snapshot '%s': %s", + snapshot_name, strerror(-ret)); } break; @@ -3431,6 +3505,7 @@ static int img_snapshot(int argc, char **argv) break; case SNAPSHOT_DELETE: + bdrv_graph_rdlock_main_loop(); ret = bdrv_snapshot_find(bs, &sn, snapshot_name); if (ret < 0) { error_report("Could not delete snapshot '%s': snapshot not " @@ -3444,6 +3519,7 @@ static int img_snapshot(int argc, char **argv) ret = 1; } } + bdrv_graph_rdunlock_main_loop(); break; } @@ -3461,17 +3537,21 @@ static int img_rebase(int argc, char **argv) uint8_t *buf_old = NULL; uint8_t *buf_new = NULL; BlockDriverState *bs = NULL, *prefix_chain_bs = NULL; - BlockDriverState *unfiltered_bs; + BlockDriverState *unfiltered_bs, *unfiltered_bs_cow; + BlockDriverInfo bdi = {0}; char *filename; const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg; int c, flags, src_flags, ret; + BdrvRequestFlags write_flags = 0; bool writethrough, src_writethrough; int unsafe = 0; bool force_share = false; int progress = 0; bool quiet = false; + bool compress = false; Error *local_err = NULL; bool image_opts = false; + int64_t write_align; /* Parse commandline parameters */ fmt = NULL; @@ -3485,9 +3565,10 @@ static int img_rebase(int argc, char **argv) {"object", required_argument, 0, OPTION_OBJECT}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {"force-share", no_argument, 0, 'U'}, + {"compress", no_argument, 0, 'c'}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":hf:F:b:upt:T:qU", + c = getopt_long(argc, argv, ":hf:F:b:upt:T:qUc", long_options, NULL); if (c == -1) { break; @@ -3535,6 +3616,9 @@ static int img_rebase(int argc, char **argv) case 'U': force_share = true; break; + case 'c': + compress = true; + break; } } @@ -3585,7 +3669,18 @@ static int img_rebase(int argc, char **argv) } bs = blk_bs(blk); + bdrv_graph_rdlock_main_loop(); unfiltered_bs = bdrv_skip_filters(bs); + unfiltered_bs_cow = bdrv_cow_bs(unfiltered_bs); + bdrv_graph_rdunlock_main_loop(); + + if (compress && !block_driver_can_compress(unfiltered_bs->drv)) { + error_report("Compression not supported for this file format"); + ret = -1; + goto out; + } else if (compress) { + write_flags |= BDRV_REQ_WRITE_COMPRESSED; + } if (out_basefmt != NULL) { if (bdrv_find_format(out_basefmt) == NULL) { @@ -3595,10 +3690,28 @@ static int img_rebase(int argc, char **argv) } } + /* + * We need overlay subcluster size (or cluster size in case writes are + * compressed) to make sure write requests are aligned. + */ + ret = bdrv_get_info(unfiltered_bs, &bdi); + if (ret < 0) { + error_report("could not get block driver info"); + goto out; + } else if (bdi.subcluster_size == 0) { + bdi.cluster_size = bdi.subcluster_size = 1; + } + + write_align = compress ? bdi.cluster_size : bdi.subcluster_size; + /* For safe rebasing we need to compare old and new backing file */ if (!unsafe) { QDict *options = NULL; - BlockDriverState *base_bs = bdrv_cow_bs(unfiltered_bs); + BlockDriverState *base_bs; + + bdrv_graph_rdlock_main_loop(); + base_bs = bdrv_cow_bs(unfiltered_bs); + bdrv_graph_rdunlock_main_loop(); if (base_bs) { blk_old_backing = blk_new(qemu_get_aio_context(), @@ -3628,7 +3741,9 @@ static int img_rebase(int argc, char **argv) qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); } + bdrv_graph_rdlock_main_loop(); bdrv_refresh_filename(bs); + bdrv_graph_rdunlock_main_loop(); overlay_filename = bs->exact_filename[0] ? bs->exact_filename : bs->filename; out_real_path = @@ -3692,11 +3807,16 @@ static int img_rebase(int argc, char **argv) int64_t old_backing_size = 0; int64_t new_backing_size = 0; uint64_t offset; - int64_t n; + int64_t n, n_old = 0, n_new = 0; float local_progress = 0; - buf_old = blk_blockalign(blk, IO_BUF_SIZE); - buf_new = blk_blockalign(blk, IO_BUF_SIZE); + if (blk_old_backing && bdrv_opt_mem_align(blk_bs(blk_old_backing)) > + bdrv_opt_mem_align(blk_bs(blk))) { + buf_old = blk_blockalign(blk_old_backing, IO_BUF_SIZE); + } else { + buf_old = blk_blockalign(blk, IO_BUF_SIZE); + } + buf_new = blk_blockalign(blk_new_backing, IO_BUF_SIZE); size = blk_getlength(blk); if (size < 0) { @@ -3733,7 +3853,8 @@ static int img_rebase(int argc, char **argv) } for (offset = 0; offset < size; offset += n) { - bool buf_old_is_zero = false; + bool old_backing_eof = false; + int64_t n_alloc; /* How many bytes can we handle with the next read? */ n = MIN(IO_BUF_SIZE, size - offset); @@ -3750,11 +3871,13 @@ static int img_rebase(int argc, char **argv) } if (prefix_chain_bs) { + uint64_t bytes = n; + /* * If cluster wasn't changed since prefix_chain, we don't need * to take action */ - ret = bdrv_is_allocated_above(bdrv_cow_bs(unfiltered_bs), + ret = bdrv_is_allocated_above(unfiltered_bs_cow, prefix_chain_bs, false, offset, n, &n); if (ret < 0) { @@ -3762,38 +3885,60 @@ static int img_rebase(int argc, char **argv) strerror(-ret)); goto out; } - if (!ret) { + if (!ret && n) { continue; } + if (!n) { + /* + * If we've reached EOF of the old backing, it means that + * offsets beyond the old backing size were read as zeroes. + * Now we will need to explicitly zero the cluster in + * order to preserve that state after the rebase. + */ + n = bytes; + } } + /* + * At this point we know that the region [offset; offset + n) + * is unallocated within the target image. This region might be + * unaligned to the target image's (sub)cluster boundaries, as + * old backing may have smaller clusters (or have subclusters). + * We extend it to the aligned boundaries to avoid CoW on + * partial writes in blk_pwrite(), + */ + n += offset - QEMU_ALIGN_DOWN(offset, write_align); + offset = QEMU_ALIGN_DOWN(offset, write_align); + n += QEMU_ALIGN_UP(offset + n, write_align) - (offset + n); + n = MIN(n, size - offset); + assert(!bdrv_is_allocated(unfiltered_bs, offset, n, &n_alloc) && + n_alloc == n); + + /* + * Much like with the target image, we'll try to read as much + * of the old and new backings as we can. + */ + n_old = MIN(n, MAX(0, old_backing_size - (int64_t) offset)); + n_new = MIN(n, MAX(0, new_backing_size - (int64_t) offset)); + /* * Read old and new backing file and take into consideration that * backing files may be smaller than the COW image. */ - if (offset >= old_backing_size) { - memset(buf_old, 0, n); - buf_old_is_zero = true; + memset(buf_old + n_old, 0, n - n_old); + if (!n_old) { + old_backing_eof = true; } else { - if (offset + n > old_backing_size) { - n = old_backing_size - offset; - } - - ret = blk_pread(blk_old_backing, offset, buf_old, n); + ret = blk_pread(blk_old_backing, offset, n_old, buf_old, 0); if (ret < 0) { error_report("error while reading from old backing file"); goto out; } } - if (offset >= new_backing_size || !blk_new_backing) { - memset(buf_new, 0, n); - } else { - if (offset + n > new_backing_size) { - n = new_backing_size - offset; - } - - ret = blk_pread(blk_new_backing, offset, buf_new, n); + memset(buf_new + n_new, 0, n - n_new); + if (n_new) { + ret = blk_pread(blk_new_backing, offset, n_new, buf_new, 0); if (ret < 0) { error_report("error while reading from new backing file"); goto out; @@ -3807,13 +3952,14 @@ static int img_rebase(int argc, char **argv) int64_t pnum; if (compare_buffers(buf_old + written, buf_new + written, - n - written, &pnum)) + n - written, write_align, &pnum)) { - if (buf_old_is_zero) { + if (old_backing_eof) { ret = blk_pwrite_zeroes(blk, offset + written, pnum, 0); } else { - ret = blk_pwrite(blk, offset + written, - buf_old + written, pnum, 0); + assert(written + pnum <= IO_BUF_SIZE); + ret = blk_pwrite(blk, offset + written, pnum, + buf_old + written, write_flags); } if (ret < 0) { error_report("Error while writing to COW image: %s", @@ -3823,6 +3969,9 @@ static int img_rebase(int argc, char **argv) } written += pnum; + if (offset + written >= old_backing_size) { + old_backing_eof = true; + } } qemu_progress_print(local_progress, 100); } @@ -4065,6 +4214,8 @@ static int print_amend_option_help(const char *format) { BlockDriver *drv; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + /* Find driver and parse its options */ drv = bdrv_find_format(format); if (!drv) { @@ -4203,9 +4354,11 @@ static int img_amend(int argc, char **argv) goto out; } + bdrv_graph_rdlock_main_loop(); if (!bs->drv->bdrv_amend_options) { error_report("Format driver '%s' does not support option amendment", fmt); + bdrv_graph_rdunlock_main_loop(); ret = -1; goto out; } @@ -4225,6 +4378,7 @@ static int img_amend(int argc, char **argv) "This option is only supported for image creation\n"); } + bdrv_graph_rdunlock_main_loop(); error_report_err(err); ret = -1; goto out; @@ -4234,6 +4388,8 @@ static int img_amend(int argc, char **argv) qemu_progress_print(0.f, 0); ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL, force, &err); qemu_progress_print(100.f, 0); + bdrv_graph_rdunlock_main_loop(); + if (ret < 0) { error_report_err(err); goto out; @@ -4368,7 +4524,7 @@ static int img_bench(int argc, char **argv) struct timeval t1, t2; int i; bool force_share = false; - size_t buf_size; + size_t buf_size = 0; for (;;) { static const struct option long_options[] = { @@ -4567,7 +4723,7 @@ static int img_bench(int argc, char **argv) data.buf = blk_blockalign(blk, buf_size); memset(data.buf, pattern, data.nrreq * data.bufsize); - blk_register_buf(blk, data.buf, buf_size); + blk_register_buf(blk, data.buf, buf_size, &error_fatal); data.qiov = g_new(QEMUIOVector, data.nrreq); for (i = 0; i < data.nrreq; i++) { @@ -4590,7 +4746,7 @@ static int img_bench(int argc, char **argv) out: if (data.buf) { - blk_unregister_buf(blk, data.buf); + blk_unregister_buf(blk, data.buf, buf_size); } qemu_vfree(data.buf); blk_unref(blk); @@ -4630,6 +4786,7 @@ static int img_bitmap(int argc, char **argv) QSIMPLEQ_HEAD(, ImgBitmapAction) actions; ImgBitmapAction *act, *act_next; const char *op; + int inactivate_ret; QSIMPLEQ_INIT(&actions); @@ -4814,6 +4971,16 @@ static int img_bitmap(int argc, char **argv) ret = 0; out: + /* + * Manually inactivate the images first because this way we can know whether + * an error occurred. blk_unref() doesn't tell us about failures. + */ + inactivate_ret = bdrv_inactivate_all(); + if (inactivate_ret < 0) { + error_report("Error while closing the image: %s", strerror(-inactivate_ret)); + ret = 1; + } + blk_unref(src); blk_unref(blk); qemu_opts_del(opts); @@ -4919,7 +5086,7 @@ static int img_dd(int argc, char **argv) const char *out_fmt = "raw"; const char *fmt = NULL; int64_t size = 0; - int64_t block_count = 0, out_pos, in_pos; + int64_t out_pos, in_pos; bool force_share = false; struct DdInfo dd = { .flags = 0, @@ -5119,31 +5286,24 @@ static int img_dd(int argc, char **argv) in.buf = g_new(uint8_t, in.bsz); - for (out_pos = 0; in_pos < size; block_count++) { - int in_ret, out_ret; + for (out_pos = 0; in_pos < size; ) { + int bytes = (in_pos + in.bsz > size) ? size - in_pos : in.bsz; - if (in_pos + in.bsz > size) { - in_ret = blk_pread(blk1, in_pos, in.buf, size - in_pos); - } else { - in_ret = blk_pread(blk1, in_pos, in.buf, in.bsz); - } - if (in_ret < 0) { + ret = blk_pread(blk1, in_pos, bytes, in.buf, 0); + if (ret < 0) { error_report("error while reading from input image file: %s", - strerror(-in_ret)); - ret = -1; + strerror(-ret)); goto out; } - in_pos += in_ret; + in_pos += bytes; - out_ret = blk_pwrite(blk2, out_pos, in.buf, in_ret, 0); - - if (out_ret < 0) { + ret = blk_pwrite(blk2, out_pos, bytes, in.buf, 0); + if (ret < 0) { error_report("error while writing to output image file: %s", - strerror(-out_ret)); - ret = -1; + strerror(-ret)); goto out; } - out_pos += out_ret; + out_pos += bytes; } out: diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 2f0d8ac25a..f5d7202a13 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -338,7 +338,8 @@ static int parse_pattern(const char *arg) */ #define MISALIGN_OFFSET 16 -static void *qemu_io_alloc(BlockBackend *blk, size_t len, int pattern) +static void *qemu_io_alloc(BlockBackend *blk, size_t len, int pattern, + bool register_buf) { void *buf; @@ -347,16 +348,24 @@ static void *qemu_io_alloc(BlockBackend *blk, size_t len, int pattern) } buf = blk_blockalign(blk, len); memset(buf, pattern, len); + if (register_buf) { + blk_register_buf(blk, buf, len, &error_abort); + } if (qemuio_misalign) { buf += MISALIGN_OFFSET; } return buf; } -static void qemu_io_free(void *p) +static void qemu_io_free(BlockBackend *blk, void *p, size_t len, + bool unregister_buf) { if (qemuio_misalign) { p -= MISALIGN_OFFSET; + len += MISALIGN_OFFSET; + } + if (unregister_buf) { + blk_unregister_buf(blk, p, len); } qemu_vfree(p); } @@ -371,14 +380,16 @@ static void qemu_io_free(void *p) * @blk - the block backend where the buffer content is going to be written to * @len - the buffer length * @file_name - the file to read the content from + * @register_buf - call blk_register_buf() * * Returns: the buffer pointer on success * NULL on error */ static void *qemu_io_alloc_from_file(BlockBackend *blk, size_t len, - const char *file_name) + const char *file_name, bool register_buf) { - char *buf, *buf_origin; + size_t alloc_len = len + (qemuio_misalign ? MISALIGN_OFFSET : 0); + char *alloc_buf, *buf, *end; FILE *f = fopen(file_name, "r"); int pattern_len; @@ -387,19 +398,13 @@ static void *qemu_io_alloc_from_file(BlockBackend *blk, size_t len, return NULL; } - if (qemuio_misalign) { - len += MISALIGN_OFFSET; - } - - buf_origin = buf = blk_blockalign(blk, len); + alloc_buf = buf = blk_blockalign(blk, alloc_len); if (qemuio_misalign) { - buf_origin += MISALIGN_OFFSET; buf += MISALIGN_OFFSET; - len -= MISALIGN_OFFSET; } - pattern_len = fread(buf_origin, 1, len, f); + pattern_len = fread(buf, 1, len, f); if (ferror(f)) { perror(file_name); @@ -414,24 +419,23 @@ static void *qemu_io_alloc_from_file(BlockBackend *blk, size_t len, fclose(f); f = NULL; - if (len > pattern_len) { - len -= pattern_len; - buf += pattern_len; - - while (len > 0) { - size_t len_to_copy = MIN(pattern_len, len); - - memcpy(buf, buf_origin, len_to_copy); - - len -= len_to_copy; - buf += len_to_copy; - } + if (register_buf) { + blk_register_buf(blk, alloc_buf, alloc_len, &error_abort); } - return buf_origin; + end = buf + len; + for (char *p = buf + pattern_len; p < end; p += pattern_len) { + memcpy(p, buf, MIN(pattern_len, end - p)); + } + + return buf; error: - qemu_io_free(buf_origin); + /* + * This code path is only taken before blk_register_buf() is called, so + * hardcode the qemu_io_free() unregister_buf argument to false. + */ + qemu_io_free(blk, alloc_buf, alloc_len, false); if (f) { fclose(f); } @@ -490,7 +494,7 @@ static void print_report(const char *op, struct timespec *t, int64_t offset, */ static void * create_iovec(BlockBackend *blk, QEMUIOVector *qiov, char **argv, int nr_iov, - int pattern) + int pattern, bool register_buf) { size_t *sizes = g_new0(size_t, nr_iov); size_t count = 0; @@ -526,7 +530,7 @@ create_iovec(BlockBackend *blk, QEMUIOVector *qiov, char **argv, int nr_iov, qemu_iovec_init(qiov, nr_iov); - buf = p = qemu_io_alloc(blk, count, pattern); + buf = p = qemu_io_alloc(blk, count, pattern, register_buf); for (i = 0; i < nr_iov; i++) { qemu_iovec_add(qiov, p, sizes[i]); @@ -539,81 +543,51 @@ fail: } static int do_pread(BlockBackend *blk, char *buf, int64_t offset, - int64_t bytes, int64_t *total) + int64_t bytes, BdrvRequestFlags flags, int64_t *total) { + int ret; + if (bytes > INT_MAX) { return -ERANGE; } - *total = blk_pread(blk, offset, (uint8_t *)buf, bytes); - if (*total < 0) { - return *total; + ret = blk_pread(blk, offset, bytes, (uint8_t *)buf, flags); + if (ret < 0) { + return ret; } + *total = bytes; return 1; } static int do_pwrite(BlockBackend *blk, char *buf, int64_t offset, - int64_t bytes, int flags, int64_t *total) + int64_t bytes, BdrvRequestFlags flags, int64_t *total) { + int ret; + if (bytes > INT_MAX) { return -ERANGE; } - *total = blk_pwrite(blk, offset, (uint8_t *)buf, bytes, flags); - if (*total < 0) { - return *total; + ret = blk_pwrite(blk, offset, bytes, (uint8_t *)buf, flags); + if (ret < 0) { + return ret; } + *total = bytes; return 1; } -typedef struct { - BlockBackend *blk; - int64_t offset; - int64_t bytes; - int64_t *total; - int flags; - int ret; - bool done; -} CoWriteZeroes; - -static void coroutine_fn co_pwrite_zeroes_entry(void *opaque) +static int do_pwrite_zeroes(BlockBackend *blk, int64_t offset, + int64_t bytes, BdrvRequestFlags flags, + int64_t *total) { - CoWriteZeroes *data = opaque; + int ret = blk_pwrite_zeroes(blk, offset, bytes, + flags | BDRV_REQ_ZERO_WRITE); - data->ret = blk_co_pwrite_zeroes(data->blk, data->offset, data->bytes, - data->flags); - data->done = true; - if (data->ret < 0) { - *data->total = data->ret; - return; - } - - *data->total = data->bytes; -} - -static int do_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int64_t bytes, int flags, int64_t *total) -{ - Coroutine *co; - CoWriteZeroes data = { - .blk = blk, - .offset = offset, - .bytes = bytes, - .total = total, - .flags = flags, - .done = false, - }; - - co = qemu_coroutine_create(co_pwrite_zeroes_entry, &data); - bdrv_coroutine_enter(blk_bs(blk), co); - while (!data.done) { - aio_poll(blk_get_aio_context(blk), true); - } - if (data.ret < 0) { - return data.ret; - } else { - return 1; + if (ret < 0) { + return ret; } + *total = bytes; + return 1; } static int do_write_compressed(BlockBackend *blk, char *buf, int64_t offset, @@ -625,7 +599,7 @@ static int do_write_compressed(BlockBackend *blk, char *buf, int64_t offset, return -ERANGE; } - ret = blk_pwrite_compressed(blk, offset, buf, bytes); + ret = blk_pwrite_compressed(blk, offset, bytes, buf); if (ret < 0) { return ret; } @@ -668,11 +642,11 @@ static void aio_rw_done(void *opaque, int ret) } static int do_aio_readv(BlockBackend *blk, QEMUIOVector *qiov, - int64_t offset, int *total) + int64_t offset, BdrvRequestFlags flags, int *total) { int async_ret = NOT_DONE; - blk_aio_preadv(blk, offset, qiov, 0, aio_rw_done, &async_ret); + blk_aio_preadv(blk, offset, qiov, flags, aio_rw_done, &async_ret); while (async_ret == NOT_DONE) { main_loop_wait(false); } @@ -682,7 +656,7 @@ static int do_aio_readv(BlockBackend *blk, QEMUIOVector *qiov, } static int do_aio_writev(BlockBackend *blk, QEMUIOVector *qiov, - int64_t offset, int flags, int *total) + int64_t offset, BdrvRequestFlags flags, int *total) { int async_ret = NOT_DONE; @@ -712,6 +686,7 @@ static void read_help(void) " -p, -- ignored for backwards compatibility\n" " -P, -- use a pattern to verify read data\n" " -q, -- quiet mode, do not show I/O statistics\n" +" -r, -- register I/O buffer\n" " -s, -- start offset for pattern verification (only with -P)\n" " -v, -- dump buffer to standard output\n" "\n"); @@ -725,7 +700,7 @@ static const cmdinfo_t read_cmd = { .cfunc = read_f, .argmin = 2, .argmax = -1, - .args = "[-abCqv] [-P pattern [-s off] [-l len]] off len", + .args = "[-abCqrv] [-P pattern [-s off] [-l len]] off len", .oneline = "reads a number of bytes at a specified offset", .help = read_help, }; @@ -743,8 +718,9 @@ static int read_f(BlockBackend *blk, int argc, char **argv) int64_t total = 0; int pattern = 0; int64_t pattern_offset = 0, pattern_count = 0; + BdrvRequestFlags flags = 0; - while ((c = getopt(argc, argv, "bCl:pP:qs:v")) != -1) { + while ((c = getopt(argc, argv, "bCl:pP:qrs:v")) != -1) { switch (c) { case 'b': bflag = true; @@ -773,6 +749,9 @@ static int read_f(BlockBackend *blk, int argc, char **argv) case 'q': qflag = true; break; + case 'r': + flags |= BDRV_REQ_REGISTERED_BUF; + break; case 's': sflag = true; pattern_offset = cvtnum(optarg); @@ -837,15 +816,20 @@ static int read_f(BlockBackend *blk, int argc, char **argv) count); return -EINVAL; } + if (flags & BDRV_REQ_REGISTERED_BUF) { + printf("I/O buffer registration is not supported when reading " + "from vmstate\n"); + return -EINVAL; + } } - buf = qemu_io_alloc(blk, count, 0xab); + buf = qemu_io_alloc(blk, count, 0xab, flags & BDRV_REQ_REGISTERED_BUF); clock_gettime(CLOCK_MONOTONIC, &t1); if (bflag) { ret = do_load_vmstate(blk, buf, offset, count, &total); } else { - ret = do_pread(blk, buf, offset, count, &total); + ret = do_pread(blk, buf, offset, count, flags, &total); } clock_gettime(CLOCK_MONOTONIC, &t2); @@ -882,7 +866,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv) print_report("read", &t2, offset, count, total, cnt, Cflag); out: - qemu_io_free(buf); + qemu_io_free(blk, buf, count, flags & BDRV_REQ_REGISTERED_BUF); return ret; } @@ -900,8 +884,9 @@ static void readv_help(void) " Uses multiple iovec buffers if more than one byte range is specified.\n" " -C, -- report statistics in a machine parsable format\n" " -P, -- use a pattern to verify read data\n" -" -v, -- dump buffer to standard output\n" " -q, -- quiet mode, do not show I/O statistics\n" +" -r, -- register I/O buffer\n" +" -v, -- dump buffer to standard output\n" "\n"); } @@ -912,7 +897,7 @@ static const cmdinfo_t readv_cmd = { .cfunc = readv_f, .argmin = 2, .argmax = -1, - .args = "[-Cqv] [-P pattern] off len [len..]", + .args = "[-Cqrv] [-P pattern] off len [len..]", .oneline = "reads a number of bytes at a specified offset", .help = readv_help, }; @@ -930,8 +915,9 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) QEMUIOVector qiov; int pattern = 0; bool Pflag = false; + BdrvRequestFlags flags = 0; - while ((c = getopt(argc, argv, "CP:qv")) != -1) { + while ((c = getopt(argc, argv, "CP:qrv")) != -1) { switch (c) { case 'C': Cflag = true; @@ -946,6 +932,9 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) case 'q': qflag = true; break; + case 'r': + flags |= BDRV_REQ_REGISTERED_BUF; + break; case 'v': vflag = true; break; @@ -969,13 +958,14 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) optind++; nr_iov = argc - optind; - buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, 0xab); + buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, 0xab, + flags & BDRV_REQ_REGISTERED_BUF); if (buf == NULL) { return -EINVAL; } clock_gettime(CLOCK_MONOTONIC, &t1); - ret = do_aio_readv(blk, &qiov, offset, &total); + ret = do_aio_readv(blk, &qiov, offset, flags, &total); clock_gettime(CLOCK_MONOTONIC, &t2); if (ret < 0) { @@ -1010,8 +1000,8 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) print_report("read", &t2, offset, qiov.size, total, cnt, Cflag); out: + qemu_io_free(blk, buf, qiov.size, flags & BDRV_REQ_REGISTERED_BUF); qemu_iovec_destroy(&qiov); - qemu_io_free(buf); return ret; } @@ -1028,15 +1018,16 @@ static void write_help(void) " filled with a set pattern (0xcdcdcdcd).\n" " -b, -- write to the VM state rather than the virtual disk\n" " -c, -- write compressed data with blk_write_compressed\n" +" -C, -- report statistics in a machine parsable format\n" " -f, -- use Force Unit Access semantics\n" " -n, -- with -z, don't allow slow fallback\n" " -p, -- ignored for backwards compatibility\n" " -P, -- use different pattern to fill file\n" -" -s, -- use a pattern file to fill the write buffer\n" -" -C, -- report statistics in a machine parsable format\n" " -q, -- quiet mode, do not show I/O statistics\n" +" -r, -- register I/O buffer\n" +" -s, -- use a pattern file to fill the write buffer\n" " -u, -- with -z, allow unmapping\n" -" -z, -- write zeroes using blk_co_pwrite_zeroes\n" +" -z, -- write zeroes using blk_pwrite_zeroes\n" "\n"); } @@ -1049,7 +1040,7 @@ static const cmdinfo_t write_cmd = { .perm = BLK_PERM_WRITE, .argmin = 2, .argmax = -1, - .args = "[-bcCfnquz] [-P pattern | -s source_file] off len", + .args = "[-bcCfnqruz] [-P pattern | -s source_file] off len", .oneline = "writes a number of bytes at a specified offset", .help = write_help, }; @@ -1059,7 +1050,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv) struct timespec t1, t2; bool Cflag = false, qflag = false, bflag = false; bool Pflag = false, zflag = false, cflag = false, sflag = false; - int flags = 0; + BdrvRequestFlags flags = 0; int c, cnt, ret; char *buf = NULL; int64_t offset; @@ -1069,7 +1060,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv) int pattern = 0xcd; const char *file_name = NULL; - while ((c = getopt(argc, argv, "bcCfnpP:qs:uz")) != -1) { + while ((c = getopt(argc, argv, "bcCfnpP:qrs:uz")) != -1) { switch (c) { case 'b': bflag = true; @@ -1099,6 +1090,9 @@ static int write_f(BlockBackend *blk, int argc, char **argv) case 'q': qflag = true; break; + case 'r': + flags |= BDRV_REQ_REGISTERED_BUF; + break; case 's': sflag = true; file_name = optarg; @@ -1178,14 +1172,21 @@ static int write_f(BlockBackend *blk, int argc, char **argv) } } - if (!zflag) { + if (zflag) { + if (flags & BDRV_REQ_REGISTERED_BUF) { + printf("cannot combine zero write with registered I/O buffer\n"); + return -EINVAL; + } + } else { if (sflag) { - buf = qemu_io_alloc_from_file(blk, count, file_name); + buf = qemu_io_alloc_from_file(blk, count, file_name, + flags & BDRV_REQ_REGISTERED_BUF); if (!buf) { return -EINVAL; } } else { - buf = qemu_io_alloc(blk, count, pattern); + buf = qemu_io_alloc(blk, count, pattern, + flags & BDRV_REQ_REGISTERED_BUF); } } @@ -1193,7 +1194,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv) if (bflag) { ret = do_save_vmstate(blk, buf, offset, count, &total); } else if (zflag) { - ret = do_co_pwrite_zeroes(blk, offset, count, flags, &total); + ret = do_pwrite_zeroes(blk, offset, count, flags, &total); } else if (cflag) { ret = do_write_compressed(blk, buf, offset, count, &total); } else { @@ -1219,7 +1220,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv) out: if (!zflag) { - qemu_io_free(buf); + qemu_io_free(blk, buf, count, flags & BDRV_REQ_REGISTERED_BUF); } return ret; } @@ -1236,10 +1237,11 @@ writev_help(void) "\n" " Writes into a segment of the currently open file, using a buffer\n" " filled with a set pattern (0xcdcdcdcd).\n" -" -P, -- use different pattern to fill file\n" " -C, -- report statistics in a machine parsable format\n" " -f, -- use Force Unit Access semantics\n" +" -P, -- use different pattern to fill file\n" " -q, -- quiet mode, do not show I/O statistics\n" +" -r, -- register I/O buffer\n" "\n"); } @@ -1251,7 +1253,7 @@ static const cmdinfo_t writev_cmd = { .perm = BLK_PERM_WRITE, .argmin = 2, .argmax = -1, - .args = "[-Cfq] [-P pattern] off len [len..]", + .args = "[-Cfqr] [-P pattern] off len [len..]", .oneline = "writes a number of bytes at a specified offset", .help = writev_help, }; @@ -1260,7 +1262,7 @@ static int writev_f(BlockBackend *blk, int argc, char **argv) { struct timespec t1, t2; bool Cflag = false, qflag = false; - int flags = 0; + BdrvRequestFlags flags = 0; int c, cnt, ret; char *buf; int64_t offset; @@ -1270,7 +1272,7 @@ static int writev_f(BlockBackend *blk, int argc, char **argv) int pattern = 0xcd; QEMUIOVector qiov; - while ((c = getopt(argc, argv, "CfqP:")) != -1) { + while ((c = getopt(argc, argv, "CfP:qr")) != -1) { switch (c) { case 'C': Cflag = true; @@ -1281,6 +1283,9 @@ static int writev_f(BlockBackend *blk, int argc, char **argv) case 'q': qflag = true; break; + case 'r': + flags |= BDRV_REQ_REGISTERED_BUF; + break; case 'P': pattern = parse_pattern(optarg); if (pattern < 0) { @@ -1306,7 +1311,8 @@ static int writev_f(BlockBackend *blk, int argc, char **argv) optind++; nr_iov = argc - optind; - buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, pattern); + buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, pattern, + flags & BDRV_REQ_REGISTERED_BUF); if (buf == NULL) { return -EINVAL; } @@ -1331,8 +1337,8 @@ static int writev_f(BlockBackend *blk, int argc, char **argv) t2 = tsub(t2, t1); print_report("wrote", &t2, offset, qiov.size, total, cnt, Cflag); out: + qemu_io_free(blk, buf, qiov.size, flags & BDRV_REQ_REGISTERED_BUF); qemu_iovec_destroy(&qiov); - qemu_io_free(buf); return ret; } @@ -1348,6 +1354,7 @@ struct aio_ctx { bool zflag; BlockAcctCookie acct; int pattern; + BdrvRequestFlags flags; struct timespec t1; }; @@ -1377,7 +1384,8 @@ static void aio_write_done(void *opaque, int ret) ctx->qiov.size, 1, ctx->Cflag); out: if (!ctx->zflag) { - qemu_io_free(ctx->buf); + qemu_io_free(ctx->blk, ctx->buf, ctx->qiov.size, + ctx->flags & BDRV_REQ_REGISTERED_BUF); qemu_iovec_destroy(&ctx->qiov); } g_free(ctx); @@ -1422,7 +1430,8 @@ static void aio_read_done(void *opaque, int ret) print_report("read", &t2, ctx->offset, ctx->qiov.size, ctx->qiov.size, 1, ctx->Cflag); out: - qemu_io_free(ctx->buf); + qemu_io_free(ctx->blk, ctx->buf, ctx->qiov.size, + ctx->flags & BDRV_REQ_REGISTERED_BUF); qemu_iovec_destroy(&ctx->qiov); g_free(ctx); } @@ -1444,10 +1453,11 @@ static void aio_read_help(void) " considered successful once the request is submitted, independently\n" " of potential I/O errors or pattern mismatches.\n" " -C, -- report statistics in a machine parsable format\n" -" -P, -- use a pattern to verify read data\n" " -i, -- treat request as invalid, for exercising stats\n" -" -v, -- dump buffer to standard output\n" +" -P, -- use a pattern to verify read data\n" " -q, -- quiet mode, do not show I/O statistics\n" +" -r, -- register I/O buffer\n" +" -v, -- dump buffer to standard output\n" "\n"); } @@ -1458,7 +1468,7 @@ static const cmdinfo_t aio_read_cmd = { .cfunc = aio_read_f, .argmin = 2, .argmax = -1, - .args = "[-Ciqv] [-P pattern] off len [len..]", + .args = "[-Ciqrv] [-P pattern] off len [len..]", .oneline = "asynchronously reads a number of bytes", .help = aio_read_help, }; @@ -1469,7 +1479,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); ctx->blk = blk; - while ((c = getopt(argc, argv, "CP:iqv")) != -1) { + while ((c = getopt(argc, argv, "CiP:qrv")) != -1) { switch (c) { case 'C': ctx->Cflag = true; @@ -1490,6 +1500,9 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) case 'q': ctx->qflag = true; break; + case 'r': + ctx->flags |= BDRV_REQ_REGISTERED_BUF; + break; case 'v': ctx->vflag = true; break; @@ -1516,7 +1529,8 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) optind++; nr_iov = argc - optind; - ctx->buf = create_iovec(blk, &ctx->qiov, &argv[optind], nr_iov, 0xab); + ctx->buf = create_iovec(blk, &ctx->qiov, &argv[optind], nr_iov, 0xab, + ctx->flags & BDRV_REQ_REGISTERED_BUF); if (ctx->buf == NULL) { block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ); g_free(ctx); @@ -1526,7 +1540,8 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) clock_gettime(CLOCK_MONOTONIC, &ctx->t1); block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size, BLOCK_ACCT_READ); - blk_aio_preadv(blk, ctx->offset, &ctx->qiov, 0, aio_read_done, ctx); + blk_aio_preadv(blk, ctx->offset, &ctx->qiov, ctx->flags, aio_read_done, + ctx); return 0; } @@ -1547,11 +1562,12 @@ static void aio_write_help(void) " Note that due to its asynchronous nature, this command will be\n" " considered successful once the request is submitted, independently\n" " of potential I/O errors or pattern mismatches.\n" -" -P, -- use different pattern to fill file\n" " -C, -- report statistics in a machine parsable format\n" " -f, -- use Force Unit Access semantics\n" " -i, -- treat request as invalid, for exercising stats\n" +" -P, -- use different pattern to fill file\n" " -q, -- quiet mode, do not show I/O statistics\n" +" -r, -- register I/O buffer\n" " -u, -- with -z, allow unmapping\n" " -z, -- write zeroes using blk_aio_pwrite_zeroes\n" "\n"); @@ -1565,7 +1581,7 @@ static const cmdinfo_t aio_write_cmd = { .perm = BLK_PERM_WRITE, .argmin = 2, .argmax = -1, - .args = "[-Cfiquz] [-P pattern] off len [len..]", + .args = "[-Cfiqruz] [-P pattern] off len [len..]", .oneline = "asynchronously writes a number of bytes", .help = aio_write_help, }; @@ -1575,22 +1591,24 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) int nr_iov, c; int pattern = 0xcd; struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); - int flags = 0; ctx->blk = blk; - while ((c = getopt(argc, argv, "CfiqP:uz")) != -1) { + while ((c = getopt(argc, argv, "CfiP:qruz")) != -1) { switch (c) { case 'C': ctx->Cflag = true; break; case 'f': - flags |= BDRV_REQ_FUA; + ctx->flags |= BDRV_REQ_FUA; break; case 'q': ctx->qflag = true; break; + case 'r': + ctx->flags |= BDRV_REQ_REGISTERED_BUF; + break; case 'u': - flags |= BDRV_REQ_MAY_UNMAP; + ctx->flags |= BDRV_REQ_MAY_UNMAP; break; case 'P': pattern = parse_pattern(optarg); @@ -1626,7 +1644,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) return -EINVAL; } - if ((flags & BDRV_REQ_MAY_UNMAP) && !ctx->zflag) { + if ((ctx->flags & BDRV_REQ_MAY_UNMAP) && !ctx->zflag) { printf("-u requires -z to be specified\n"); g_free(ctx); return -EINVAL; @@ -1638,6 +1656,12 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) return -EINVAL; } + if (ctx->zflag && (ctx->flags & BDRV_REQ_REGISTERED_BUF)) { + printf("cannot combine zero write with registered I/O buffer\n"); + g_free(ctx); + return -EINVAL; + } + ctx->offset = cvtnum(argv[optind]); if (ctx->offset < 0) { int ret = ctx->offset; @@ -1656,12 +1680,12 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) } ctx->qiov.size = count; - blk_aio_pwrite_zeroes(blk, ctx->offset, count, flags, aio_write_done, - ctx); + blk_aio_pwrite_zeroes(blk, ctx->offset, count, ctx->flags, + aio_write_done, ctx); } else { nr_iov = argc - optind; ctx->buf = create_iovec(blk, &ctx->qiov, &argv[optind], nr_iov, - pattern); + pattern, ctx->flags & BDRV_REQ_REGISTERED_BUF); if (ctx->buf == NULL) { block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE); g_free(ctx); @@ -1672,8 +1696,8 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size, BLOCK_ACCT_WRITE); - blk_aio_pwritev(blk, ctx->offset, &ctx->qiov, flags, aio_write_done, - ctx); + blk_aio_pwritev(blk, ctx->offset, &ctx->qiov, ctx->flags, + aio_write_done, ctx); } return 0; @@ -1706,6 +1730,224 @@ static const cmdinfo_t flush_cmd = { .oneline = "flush all in-core file state to disk", }; +static inline int64_t tosector(int64_t bytes) +{ + return bytes >> BDRV_SECTOR_BITS; +} + +static int zone_report_f(BlockBackend *blk, int argc, char **argv) +{ + int ret; + int64_t offset; + unsigned int nr_zones; + + ++optind; + offset = cvtnum(argv[optind]); + ++optind; + nr_zones = cvtnum(argv[optind]); + + g_autofree BlockZoneDescriptor *zones = NULL; + zones = g_new(BlockZoneDescriptor, nr_zones); + ret = blk_zone_report(blk, offset, &nr_zones, zones); + if (ret < 0) { + printf("zone report failed: %s\n", strerror(-ret)); + } else { + for (int i = 0; i < nr_zones; ++i) { + printf("start: 0x%" PRIx64 ", len 0x%" PRIx64 ", " + "cap"" 0x%" PRIx64 ", wptr 0x%" PRIx64 ", " + "zcond:%u, [type: %u]\n", + tosector(zones[i].start), tosector(zones[i].length), + tosector(zones[i].cap), tosector(zones[i].wp), + zones[i].state, zones[i].type); + } + } + return ret; +} + +static const cmdinfo_t zone_report_cmd = { + .name = "zone_report", + .altname = "zrp", + .cfunc = zone_report_f, + .argmin = 2, + .argmax = 2, + .args = "offset number", + .oneline = "report zone information", +}; + +static int zone_open_f(BlockBackend *blk, int argc, char **argv) +{ + int ret; + int64_t offset, len; + ++optind; + offset = cvtnum(argv[optind]); + ++optind; + len = cvtnum(argv[optind]); + ret = blk_zone_mgmt(blk, BLK_ZO_OPEN, offset, len); + if (ret < 0) { + printf("zone open failed: %s\n", strerror(-ret)); + } + return ret; +} + +static const cmdinfo_t zone_open_cmd = { + .name = "zone_open", + .altname = "zo", + .cfunc = zone_open_f, + .argmin = 2, + .argmax = 2, + .args = "offset len", + .oneline = "explicit open a range of zones in zone block device", +}; + +static int zone_close_f(BlockBackend *blk, int argc, char **argv) +{ + int ret; + int64_t offset, len; + ++optind; + offset = cvtnum(argv[optind]); + ++optind; + len = cvtnum(argv[optind]); + ret = blk_zone_mgmt(blk, BLK_ZO_CLOSE, offset, len); + if (ret < 0) { + printf("zone close failed: %s\n", strerror(-ret)); + } + return ret; +} + +static const cmdinfo_t zone_close_cmd = { + .name = "zone_close", + .altname = "zc", + .cfunc = zone_close_f, + .argmin = 2, + .argmax = 2, + .args = "offset len", + .oneline = "close a range of zones in zone block device", +}; + +static int zone_finish_f(BlockBackend *blk, int argc, char **argv) +{ + int ret; + int64_t offset, len; + ++optind; + offset = cvtnum(argv[optind]); + ++optind; + len = cvtnum(argv[optind]); + ret = blk_zone_mgmt(blk, BLK_ZO_FINISH, offset, len); + if (ret < 0) { + printf("zone finish failed: %s\n", strerror(-ret)); + } + return ret; +} + +static const cmdinfo_t zone_finish_cmd = { + .name = "zone_finish", + .altname = "zf", + .cfunc = zone_finish_f, + .argmin = 2, + .argmax = 2, + .args = "offset len", + .oneline = "finish a range of zones in zone block device", +}; + +static int zone_reset_f(BlockBackend *blk, int argc, char **argv) +{ + int ret; + int64_t offset, len; + ++optind; + offset = cvtnum(argv[optind]); + ++optind; + len = cvtnum(argv[optind]); + ret = blk_zone_mgmt(blk, BLK_ZO_RESET, offset, len); + if (ret < 0) { + printf("zone reset failed: %s\n", strerror(-ret)); + } + return ret; +} + +static const cmdinfo_t zone_reset_cmd = { + .name = "zone_reset", + .altname = "zrs", + .cfunc = zone_reset_f, + .argmin = 2, + .argmax = 2, + .args = "offset len", + .oneline = "reset a zone write pointer in zone block device", +}; + +static int do_aio_zone_append(BlockBackend *blk, QEMUIOVector *qiov, + int64_t *offset, int flags, int *total) +{ + int async_ret = NOT_DONE; + + blk_aio_zone_append(blk, offset, qiov, flags, aio_rw_done, &async_ret); + while (async_ret == NOT_DONE) { + main_loop_wait(false); + } + + *total = qiov->size; + return async_ret < 0 ? async_ret : 1; +} + +static int zone_append_f(BlockBackend *blk, int argc, char **argv) +{ + int ret; + bool pflag = false; + int flags = 0; + int total = 0; + int64_t offset; + char *buf; + int c, nr_iov; + int pattern = 0xcd; + QEMUIOVector qiov; + + if (optind > argc - 3) { + return -EINVAL; + } + + if ((c = getopt(argc, argv, "p")) != -1) { + pflag = true; + } + + offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); + return offset; + } + optind++; + nr_iov = argc - optind; + buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, pattern, + flags & BDRV_REQ_REGISTERED_BUF); + if (buf == NULL) { + return -EINVAL; + } + ret = do_aio_zone_append(blk, &qiov, &offset, flags, &total); + if (ret < 0) { + printf("zone append failed: %s\n", strerror(-ret)); + goto out; + } + + if (pflag) { + printf("After zap done, the append sector is 0x%" PRIx64 "\n", + tosector(offset)); + } + +out: + qemu_io_free(blk, buf, qiov.size, + flags & BDRV_REQ_REGISTERED_BUF); + qemu_iovec_destroy(&qiov); + return ret; +} + +static const cmdinfo_t zone_append_cmd = { + .name = "zone_append", + .altname = "zap", + .cfunc = zone_append_f, + .argmin = 3, + .argmax = 4, + .args = "offset len [len..]", + .oneline = "append write a number of bytes at a specified offset", +}; + static int truncate_f(BlockBackend *blk, int argc, char **argv); static const cmdinfo_t truncate_cmd = { .name = "truncate", @@ -1795,6 +2037,9 @@ static int info_f(BlockBackend *blk, int argc, char **argv) char s1[64], s2[64]; int ret; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (bs->drv && bs->drv->format_name) { printf("format name: %s\n", bs->drv->format_name); } @@ -1819,8 +2064,9 @@ static int info_f(BlockBackend *blk, int argc, char **argv) return -EIO; } if (spec_info) { - printf("Format specific information:\n"); - bdrv_image_info_specific_dump(spec_info); + bdrv_image_info_specific_dump(spec_info, + "Format specific information:\n", + 0); qapi_free_ImageInfoSpecific(spec_info); } @@ -2498,6 +2744,12 @@ static void __attribute((constructor)) init_qemuio_commands(void) qemuio_add_command(&aio_write_cmd); qemuio_add_command(&aio_flush_cmd); qemuio_add_command(&flush_cmd); + qemuio_add_command(&zone_report_cmd); + qemuio_add_command(&zone_open_cmd); + qemuio_add_command(&zone_close_cmd); + qemuio_add_command(&zone_finish_cmd); + qemuio_add_command(&zone_reset_cmd); + qemuio_add_command(&zone_append_cmd); qemuio_add_command(&truncate_cmd); qemuio_add_command(&length_cmd); qemuio_add_command(&info_cmd); diff --git a/qemu-io.c b/qemu-io.c index 2bd7bfb650..6cb1e00385 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -414,15 +414,7 @@ static void prep_fetchline(void *opaque) static int do_qemuio_command(const char *cmd) { - int ret; - AioContext *ctx = - qemuio_blk ? blk_get_aio_context(qemuio_blk) : qemu_get_aio_context(); - - aio_context_acquire(ctx); - ret = qemuio_command(qemuio_blk, cmd); - aio_context_release(ctx); - - return ret; + return qemuio_command(qemuio_blk, cmd); } static int command_loop(void) @@ -475,10 +467,10 @@ static int command_loop(void) return last_error; } -static void add_user_command(char *optarg) +static void add_user_command(char *user_cmd) { cmdline = g_renew(char *, cmdline, ++ncmdline); - cmdline[ncmdline-1] = optarg; + cmdline[ncmdline - 1] = user_cmd; } static void reenable_tty_echo(void) diff --git a/qemu-keymap.c b/qemu-keymap.c index 4095b654a6..8c80f7a4ed 100644 --- a/qemu-keymap.c +++ b/qemu-keymap.c @@ -140,6 +140,18 @@ static void usage(FILE *out) names.options ?: "-"); } +static xkb_mod_mask_t get_mod(struct xkb_keymap *map, const char *name) +{ + xkb_mod_index_t mod; + xkb_mod_mask_t mask = 0; + + mod = xkb_keymap_mod_get_index(map, name); + if (mod != XKB_MOD_INVALID) { + mask = (1 << mod); + } + return mask; +} + int main(int argc, char *argv[]) { struct xkb_context *ctx; @@ -215,17 +227,15 @@ int main(int argc, char *argv[]) mod, xkb_keymap_mod_get_name(map, mod)); } - mod = xkb_keymap_mod_get_index(map, "Shift"); - shift = (1 << mod); - mod = xkb_keymap_mod_get_index(map, "Control"); - ctrl = (1 << mod); - mod = xkb_keymap_mod_get_index(map, "AltGr"); - altgr = (1 << mod); - mod = xkb_keymap_mod_get_index(map, "NumLock"); - numlock = (1 << mod); + shift = get_mod(map, "Shift"); + ctrl = get_mod(map, "Control"); + altgr = get_mod(map, "AltGr"); + numlock = get_mod(map, "NumLock"); state = xkb_state_new(map); xkb_keymap_key_for_each(map, walk_map, state); + xkb_state_unref(state); + state = NULL; /* add quirks */ fprintf(outfile, diff --git a/qemu-nbd.c b/qemu-nbd.c index 0cd5aa6f02..d7b3ccab21 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -73,9 +73,6 @@ #define MBR_SIZE 512 -static int verbose; -static char *srcpath; -static SocketAddress *saddr; static int persistent = 0; static enum { RUNNING, TERMINATE, TERMINATED } state; static int shared = 1; @@ -117,6 +114,7 @@ static void usage(const char *name) " --tls-creds=ID use id of an earlier --object to provide TLS\n" " --tls-authz=ID use id of an earlier --object to provide\n" " authorization\n" +" --tls-hostname=HOSTNAME override hostname used to check x509 certificate\n" " -T, --trace [[enable=]][,events=][,file=]\n" " specify tracing options\n" " --fork fork off the server process and exit the parent\n" @@ -222,6 +220,7 @@ static int qemu_nbd_client_list(SocketAddress *saddr, QCryptoTLSCreds *tls, [NBD_FLAG_SEND_RESIZE_BIT] = "resize", [NBD_FLAG_SEND_CACHE_BIT] = "cache", [NBD_FLAG_SEND_FAST_ZERO_BIT] = "fast-zero", + [NBD_FLAG_BLOCK_STAT_PAYLOAD_BIT] = "block-status-payload", }; printf(" size: %" PRIu64 "\n", list[i].size); @@ -238,6 +237,9 @@ static int qemu_nbd_client_list(SocketAddress *saddr, QCryptoTLSCreds *tls, printf(" opt block: %u\n", list[i].opt_block); printf(" max block: %u\n", list[i].max_block); } + printf(" transaction size: %s\n", + list[i].mode >= NBD_MODE_EXTENDED ? + "64-bit" : "32-bit"); if (list[i].n_contexts) { printf(" available meta contexts: %d\n", list[i].n_contexts); for (j = 0; j < list[i].n_contexts; j++) { @@ -254,6 +256,29 @@ static int qemu_nbd_client_list(SocketAddress *saddr, QCryptoTLSCreds *tls, } +struct NbdClientOpts { + char *device; + char *srcpath; + SocketAddress *saddr; + int old_stderr; + bool fork_process; + bool verbose; +}; + +static void nbd_client_release_pipe(int old_stderr) +{ + /* Close stderr so that the qemu-nbd process exits. */ + if (dup2(old_stderr, STDERR_FILENO) < 0) { + error_report("Could not release pipe to parent: %s", + strerror(errno)); + exit(EXIT_FAILURE); + } + if (old_stderr != STDOUT_FILENO && close(old_stderr) < 0) { + error_report("Could not release qemu-nbd: %s", strerror(errno)); + exit(EXIT_FAILURE); + } +} + #if HAVE_NBD_DEVICE static void *show_parts(void *arg) { @@ -274,8 +299,10 @@ static void *show_parts(void *arg) static void *nbd_client_thread(void *arg) { - char *device = arg; - NBDExportInfo info = { .request_sizes = false, .name = g_strdup("") }; + struct NbdClientOpts *opts = arg; + /* TODO: Revisit this if nbd.ko ever gains support for structured reply */ + NBDExportInfo info = { .request_sizes = false, .name = g_strdup(""), + .mode = NBD_MODE_SIMPLE }; QIOChannelSocket *sioc; int fd = -1; int ret = EXIT_FAILURE; @@ -284,24 +311,24 @@ static void *nbd_client_thread(void *arg) sioc = qio_channel_socket_new(); if (qio_channel_socket_connect_sync(sioc, - saddr, + opts->saddr, &local_error) < 0) { error_report_err(local_error); goto out; } - if (nbd_receive_negotiate(NULL, QIO_CHANNEL(sioc), - NULL, NULL, NULL, &info, &local_error) < 0) { + if (nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, NULL, NULL, + &info, &local_error) < 0) { if (local_error) { error_report_err(local_error); } goto out; } - fd = open(device, O_RDWR); + fd = open(opts->device, O_RDWR); if (fd < 0) { /* Linux-only, we can use %m in printf. */ - error_report("Failed to open %s: %m", device); + error_report("Failed to open %s: %m", opts->device); goto out; } @@ -311,14 +338,13 @@ static void *nbd_client_thread(void *arg) } /* update partition table */ - pthread_create(&show_parts_thread, NULL, show_parts, device); + pthread_create(&show_parts_thread, NULL, show_parts, opts->device); - if (verbose) { + if (opts->verbose && !opts->fork_process) { fprintf(stderr, "NBD device %s is now connected to %s\n", - device, srcpath); + opts->device, opts->srcpath); } else { - /* Close stderr so that the qemu-nbd process exits. */ - dup2(STDOUT_FILENO, STDERR_FILENO); + nbd_client_release_pipe(opts->old_stderr); } if (nbd_client(fd) < 0) { @@ -510,7 +536,6 @@ int main(int argc, char **argv) const char *bindto = NULL; const char *port = NULL; char *sockpath = NULL; - char *device = NULL; QemuOpts *sn_opts = NULL; const char *sn_id_or_name = NULL; const char *sopt = "hVb:o:p:rsnc:dvk:e:f:tl:x:T:D:AB:L"; @@ -573,13 +598,19 @@ int main(int argc, char **argv) const char *tlshostname = NULL; bool imageOpts = false; bool writethrough = false; /* Client will flush as needed. */ - bool fork_process = false; bool list = false; - int old_stderr = -1; unsigned socket_activation; const char *pid_file_name = NULL; const char *selinux_label = NULL; BlockExportOptions *export_opts; + struct NbdClientOpts opts = { + .fork_process = false, + .verbose = false, + .device = NULL, + .srcpath = NULL, + .saddr = NULL, + .old_stderr = STDOUT_FILENO, + }; #ifdef CONFIG_POSIX os_setup_early_signal_handling(); @@ -707,7 +738,7 @@ int main(int argc, char **argv) disconnect = true; break; case 'c': - device = optarg; + opts.device = optarg; break; case 'e': if (qemu_strtoi(optarg, NULL, 0, &shared) < 0 || @@ -738,7 +769,7 @@ int main(int argc, char **argv) } break; case 'v': - verbose = 1; + opts.verbose = true; break; case 'V': version(argv[0]); @@ -770,7 +801,7 @@ int main(int argc, char **argv) tlsauthz = optarg; break; case QEMU_NBD_OPT_FORK: - fork_process = true; + opts.fork_process = true; break; case 'L': list = true; @@ -790,12 +821,12 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } if (export_name || export_description || dev_offset || - device || disconnect || fmt || sn_id_or_name || bitmaps || + opts.device || disconnect || fmt || sn_id_or_name || bitmaps || alloc_depth || seen_aio || seen_discard || seen_cache) { error_report("List mode is incompatible with per-device settings"); exit(EXIT_FAILURE); } - if (fork_process) { + if (opts.fork_process) { error_report("List mode is incompatible with forking"); exit(EXIT_FAILURE); } @@ -820,7 +851,8 @@ int main(int argc, char **argv) } } else { /* Using socket activation - check user didn't use -p etc. */ - const char *err_msg = socket_activation_validate_opts(device, sockpath, + const char *err_msg = socket_activation_validate_opts(opts.device, + sockpath, bindto, port, selinux_label, list); @@ -838,7 +870,7 @@ int main(int argc, char **argv) } if (tlscredsid) { - if (device) { + if (opts.device) { error_report("TLS is not supported with a host device"); exit(EXIT_FAILURE); } @@ -868,7 +900,7 @@ int main(int argc, char **argv) if (selinux_label) { #ifdef CONFIG_SELINUX - if (sockpath == NULL && device == NULL) { + if (sockpath == NULL && opts.device == NULL) { error_report("--selinux-label is not permitted without --socket"); exit(EXIT_FAILURE); } @@ -879,13 +911,13 @@ int main(int argc, char **argv) } if (list) { - saddr = nbd_build_socket_address(sockpath, bindto, port); - return qemu_nbd_client_list(saddr, tlscreds, + opts.saddr = nbd_build_socket_address(sockpath, bindto, port); + return qemu_nbd_client_list(opts.saddr, tlscreds, tlshostname ? tlshostname : bindto); } #if !HAVE_NBD_DEVICE - if (disconnect || device) { + if (disconnect || opts.device) { error_report("Kernel /dev/nbdN support not available"); exit(EXIT_FAILURE); } @@ -907,12 +939,11 @@ int main(int argc, char **argv) } #endif - if ((device && !verbose) || fork_process) { + if ((opts.device && !opts.verbose) || opts.fork_process) { #ifndef WIN32 g_autoptr(GError) err = NULL; int stderr_fd[2]; pid_t pid; - int ret; if (!g_unix_open_pipe(stderr_fd, FD_CLOEXEC, &err)) { error_report("Error setting up communication pipe: %s", @@ -928,19 +959,40 @@ int main(int argc, char **argv) error_report("Failed to fork: %s", strerror(errno)); exit(EXIT_FAILURE); } else if (pid == 0) { + int saved_errno; + close(stderr_fd[0]); /* Remember parent's stderr if we will be restoring it. */ - if (fork_process) { - old_stderr = dup(STDERR_FILENO); + if (opts.verbose /* fork_process is set */) { + opts.old_stderr = dup(STDERR_FILENO); + if (opts.old_stderr < 0) { + error_report("Could not dup original stderr: %s", + strerror(errno)); + exit(EXIT_FAILURE); + } } ret = qemu_daemon(1, 0); + saved_errno = errno; /* dup2 will overwrite error below */ /* Temporarily redirect stderr to the parent's pipe... */ - dup2(stderr_fd[1], STDERR_FILENO); + if (dup2(stderr_fd[1], STDERR_FILENO) < 0) { + char str[256]; + snprintf(str, sizeof(str), + "%s: Failed to link stderr to the pipe: %s\n", + g_get_prgname(), strerror(errno)); + /* + * We are unable to use error_report() here as we need to get + * stderr pointed to the parent's pipe. Write to that pipe + * manually. + */ + ret = write(stderr_fd[1], str, strlen(str)); + exit(EXIT_FAILURE); + } + if (ret < 0) { - error_report("Failed to daemonize: %s", strerror(errno)); + error_report("Failed to daemonize: %s", strerror(saved_errno)); exit(EXIT_FAILURE); } @@ -979,9 +1031,9 @@ int main(int argc, char **argv) #endif /* WIN32 */ } - if (device != NULL && sockpath == NULL) { + if (opts.device != NULL && sockpath == NULL) { sockpath = g_malloc(128); - snprintf(sockpath, 128, SOCKET_PATH, basename(device)); + snprintf(sockpath, 128, SOCKET_PATH, basename(opts.device)); } server = qio_net_listener_new(); @@ -1000,8 +1052,8 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } #endif - saddr = nbd_build_socket_address(sockpath, bindto, port); - if (qio_net_listener_open_sync(server, saddr, backlog, + opts.saddr = nbd_build_socket_address(sockpath, bindto, port); + if (qio_net_listener_open_sync(server, opts.saddr, backlog, &local_err) < 0) { object_unref(OBJECT(server)); error_report_err(local_err); @@ -1036,19 +1088,19 @@ int main(int argc, char **argv) bdrv_init(); atexit(qemu_nbd_shutdown); - srcpath = argv[optind]; + opts.srcpath = argv[optind]; if (imageOpts) { - QemuOpts *opts; + QemuOpts *o; if (fmt) { error_report("--image-opts and -f are mutually exclusive"); exit(EXIT_FAILURE); } - opts = qemu_opts_parse_noisily(&file_opts, srcpath, true); - if (!opts) { + o = qemu_opts_parse_noisily(&file_opts, opts.srcpath, true); + if (!o) { qemu_opts_reset(&file_opts); exit(EXIT_FAILURE); } - options = qemu_opts_to_qdict(opts, NULL); + options = qemu_opts_to_qdict(o, NULL); qemu_opts_reset(&file_opts); blk = blk_new_open(NULL, NULL, options, flags, &local_err); } else { @@ -1056,7 +1108,7 @@ int main(int argc, char **argv) options = qdict_new(); qdict_put_str(options, "driver", fmt); } - blk = blk_new_open(srcpath, NULL, options, flags, &local_err); + blk = blk_new_open(opts.srcpath, NULL, options, flags, &local_err); } if (!blk) { @@ -1071,7 +1123,9 @@ int main(int argc, char **argv) qdict_put_str(raw_opts, "driver", "raw"); qdict_put_str(raw_opts, "file", bs->node_name); qdict_put_int(raw_opts, "offset", dev_offset); + bs = bdrv_open(NULL, NULL, raw_opts, flags, &error_fatal); + blk_remove_bs(blk); blk_insert_bs(blk, bs, &error_fatal); bdrv_unref(bs); @@ -1107,9 +1161,7 @@ int main(int argc, char **argv) .has_writable = true, .writable = !readonly, .u.nbd = { - .has_name = true, .name = g_strdup(export_name), - .has_description = !!export_description, .description = g_strdup(export_description), .has_bitmaps = !!bitmaps, .bitmaps = bitmaps, @@ -1120,11 +1172,9 @@ int main(int argc, char **argv) blk_exp_add(export_opts, &error_fatal); qapi_free_BlockExportOptions(export_opts); - if (device) { + if (opts.device) { #if HAVE_NBD_DEVICE - int ret; - - ret = pthread_create(&client_thread, NULL, nbd_client_thread, device); + ret = pthread_create(&client_thread, NULL, nbd_client_thread, &opts); if (ret != 0) { error_report("Failed to create client thread: %s", strerror(ret)); exit(EXIT_FAILURE); @@ -1149,9 +1199,8 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } - if (fork_process) { - dup2(old_stderr, STDERR_FILENO); - close(old_stderr); + if (opts.fork_process) { + nbd_client_release_pipe(opts.old_stderr); } state = RUNNING; @@ -1170,10 +1219,11 @@ int main(int argc, char **argv) qemu_opts_del(sn_opts); - if (device) { - void *ret; - pthread_join(client_thread, &ret); - exit(ret != NULL); + if (opts.device) { + void *result; + pthread_join(client_thread, &result); + ret = (intptr_t)result; + exit(ret); } else { exit(EXIT_SUCCESS); } diff --git a/qemu-options.hx b/qemu-options.hx index 60cf188da4..8ce85d4559 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1,3 +1,5 @@ +HXCOMM See docs/devel/docs.rst for the format of this file. +HXCOMM 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. @@ -26,7 +28,7 @@ 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, nvmm, whpx or tcg (default: tcg)\n" + " supported accelerators are kvm, xen, hvf, nvmm, whpx or tcg (default: tcg)\n" " vmport=on|off|auto controls emulation of vmport (default: auto)\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" @@ -36,7 +38,8 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " nvdimm=on|off controls NVDIMM support (default=off)\n" " memory-encryption=@var{} memory encryption object to use (default=none)\n" " hmat=on|off controls ACPI HMAT support (default=off)\n" - " memory-backend='backend-id' specifies explicitly provided backend for main RAM (default=none)\n", + " memory-backend='backend-id' specifies explicitly provided backend for main RAM (default=none)\n" + " cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]\n", QEMU_ARCH_ALL) SRST ``-machine [type=]name[,prop=value[,...]]`` @@ -58,7 +61,7 @@ SRST ``accel=accels1[:accels2[:...]]`` This is used to enable an accelerator. Depending on the target - architecture, kvm, xen, hax, hvf, nvmm, whpx or tcg can be available. + architecture, kvm, xen, hvf, nvmm, 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. @@ -124,6 +127,38 @@ SRST -object memory-backend-ram,id=pc.ram,size=512M,x-use-canonical-path-for-ramblock-id=off -machine memory-backend=pc.ram -m 512M + + ``cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]`` + Define a CXL Fixed Memory Window (CFMW). + + Described in the CXL 2.0 ECN: CEDT CFMWS & QTG _DSM. + + They are regions of Host Physical Addresses (HPA) on a system which + may be interleaved across one or more CXL host bridges. The system + software will assign particular devices into these windows and + configure the downstream Host-managed Device Memory (HDM) decoders + in root ports, switch ports and devices appropriately to meet the + interleave requirements before enabling the memory devices. + + ``targets.X=target`` provides the mapping to CXL host bridges + which may be identified by the id provided in the -device entry. + Multiple entries are needed to specify all the targets when + the fixed memory window represents interleaved memory. X is the + target index from 0. + + ``size=size`` sets the size of the CFMW. This must be a multiple of + 256MiB. The region will be aligned to 256MiB but the location is + platform and configuration dependent. + + ``interleave-granularity=granularity`` sets the granularity of + interleave. Default 256 (bytes). Only 256, 512, 1k, 2k, + 4k, 8k and 16k granularities supported. + + Example: + + :: + + -machine cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.targets.1=cxl.1,cxl-fmw.0.size=128G,cxl-fmw.0.interleave-granularity=512 ERST DEF("M", HAS_ARG, QEMU_OPTION_M, @@ -145,18 +180,22 @@ ERST DEF("accel", HAS_ARG, QEMU_OPTION_accel, "-accel [accel=]accelerator[,prop[=value][,...]]\n" - " select accelerator (kvm, xen, hax, hvf, nvmm, whpx or tcg; use 'help' for a list)\n" + " select accelerator (kvm, xen, hvf, nvmm, 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" + " one-insn-per-tb=on|off (one guest instruction per TCG translation block)\n" " split-wx=on|off (enable TCG split w^x mapping)\n" " tb-size=n (TCG translation block cache size)\n" " dirty-ring-size=n (KVM dirty ring GFN count, default 0)\n" - " thread=single|multi (enable multi-threaded TCG)\n", QEMU_ARCH_ALL) + " eager-split-size=n (KVM Eager Page Split chunk size, default 0, disabled. ARM only)\n" + " notify-vmexit=run|internal-error|disable,notify-window=n (enable notify VM exit and set notify window, x86 only)\n" + " thread=single|multi (enable multi-threaded TCG)\n" + " device=path (KVM device path, default /dev/kvm)\n", QEMU_ARCH_ALL) SRST ``-accel name[,prop=value[,...]]`` This is used to enable an accelerator. Depending on the target - architecture, kvm, xen, hax, hvf, nvmm, whpx or tcg can be available. By + architecture, kvm, xen, hvf, nvmm, 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. @@ -176,6 +215,12 @@ SRST ``kvm-shadow-mem=size`` Defines the size of the KVM shadow MMU. + ``one-insn-per-tb=on|off`` + Makes the TCG accelerator put only one guest instruction into + each translation block. This slows down emulation a lot, but + can be useful in some situations, such as when trying to analyse + the logs produced by the ``-d`` option. + ``split-wx=on|off`` Controls the use of split w^x mapping for the TCG code generation buffer. Some operating systems require this to be enabled, and in @@ -203,14 +248,46 @@ SRST is disabled (dirty-ring-size=0). When enabled, KVM will instead record dirty pages in a bitmap. + ``eager-split-size=n`` + KVM implements dirty page logging at the PAGE_SIZE granularity and + enabling dirty-logging on a huge-page requires breaking it into + PAGE_SIZE pages in the first place. KVM on ARM does this splitting + lazily by default. There are performance benefits in doing huge-page + split eagerly, especially in situations where TLBI costs associated + with break-before-make sequences are considerable and also if guest + workloads are read intensive. The size here specifies how many pages + to break at a time and needs to be a valid block size which is + 1GB/2MB/4KB, 32MB/16KB and 512MB/64KB for 4KB/16KB/64KB PAGE_SIZE + respectively. Be wary of specifying a higher size as it will have an + impact on the memory. By default, this feature is disabled + (eager-split-size=0). + + ``notify-vmexit=run|internal-error|disable,notify-window=n`` + Enables or disables notify VM exit support on x86 host and specify + the corresponding notify window to trigger the VM exit if enabled. + ``run`` option enables the feature. It does nothing and continue + if the exit happens. ``internal-error`` option enables the feature. + It raises a internal error. ``disable`` option doesn't enable the feature. + This feature can mitigate the CPU stuck issue due to event windows don't + open up for a specified of time (i.e. notify-window). + Default: notify-vmexit=run,notify-window=0. + + ``device=path`` + Sets the path to the KVM device node. Defaults to ``/dev/kvm``. This + option can be used to pass the KVM device to use via a file descriptor + by setting the value to ``/dev/fdset/NN``. + ERST DEF("smp", HAS_ARG, QEMU_OPTION_smp, - "-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,clusters=clusters][,cores=cores][,threads=threads]\n" + "-smp [[cpus=]n][,maxcpus=maxcpus][,drawers=drawers][,books=books][,sockets=sockets]\n" + " [,dies=dies][,clusters=clusters][,cores=cores][,threads=threads]\n" " set the number of initial CPUs to 'n' [default=1]\n" " maxcpus= maximum number of total CPUs, including\n" " offline CPUs for hotplug, etc\n" - " sockets= number of sockets on the machine board\n" + " drawers= number of drawers on the machine board\n" + " books= number of books in one drawer\n" + " sockets= number of sockets in one book\n" " dies= number of dies in one socket\n" " clusters= number of clusters in one die\n" " cores= number of cores in one cluster\n" @@ -299,6 +376,9 @@ SRST :: -smp 2 + + Note: The cluster topology will only be generated in ACPI and exposed + to guest if it's explicitly specified in -smp. ERST DEF("numa", HAS_ARG, QEMU_OPTION_numa, @@ -318,7 +398,7 @@ SRST \ ``-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-lb,initiator=node,target=node,hierarchy=hierarchy,data-type=type[,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 @@ -358,15 +438,22 @@ SRST -numa node,nodeid=0 -numa node,nodeid=1 \ -numa cpu,node-id=0,socket-id=0 -numa cpu,node-id=1,socket-id=1 - Legacy '\ ``mem``\ ' assigns a given RAM amount to a node (not supported - for 5.1 and newer machine types). '\ ``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. + '\ ``memdev``\ ' option assigns RAM from a given memory backend + device to a node. It is recommended to use '\ ``memdev``\ ' option + over legacy '\ ``mem``\ ' option. This is because '\ ``memdev``\ ' + option provides better performance and more control over the + backend's RAM (e.g. '\ ``prealloc``\ ' parameter of + '\ ``-memory-backend-ram``\ ' allows memory preallocation). + For compatibility reasons, legacy '\ ``mem``\ ' option is + supported in 5.0 and older machine types. Note that '\ ``mem``\ ' + and '\ ``memdev``\ ' are mutually exclusive. If one node uses + '\ ``memdev``\ ', the rest nodes have to use '\ ``memdev``\ ' + option, and vice versa. - '\ ``mem``\ ' and '\ ``memdev``\ ' are mutually exclusive. - Furthermore, if one node uses '\ ``memdev``\ ', all of them have to - use it. + Users must specify memory for all NUMA nodes by '\ ``memdev``\ ' + (or legacy '\ ``mem``\ ' if available). In QEMU 5.2, the support + for '\ ``-numa node``\ ' without memory specified was removed. '\ ``initiator``\ ' is an additional option that points to an initiator NUMA node that has best performance (the lowest latency or @@ -467,44 +554,6 @@ SRST -numa hmat-cache,node-id=1,size=10K,level=1,associativity=direct,policy=write-back,line=8 ERST -DEF("cxl-fixed-memory-window", HAS_ARG, QEMU_OPTION_cxl_fixed_memory_window, - "-cxl-fixed-memory-window targets.0=firsttarget,targets.1=secondtarget,size=size[,interleave-granularity=granularity]\n", - QEMU_ARCH_ALL) -SRST -``-cxl-fixed-memory-window targets.0=firsttarget,targets.1=secondtarget,size=size[,interleave-granularity=granularity]`` - Define a CXL Fixed Memory Window (CFMW). - - Described in the CXL 2.0 ECN: CEDT CFMWS & QTG _DSM. - - They are regions of Host Physical Addresses (HPA) on a system which - may be interleaved across one or more CXL host bridges. The system - software will assign particular devices into these windows and - configure the downstream Host-managed Device Memory (HDM) decoders - in root ports, switch ports and devices appropriately to meet the - interleave requirements before enabling the memory devices. - - ``targets.X=firsttarget`` provides the mapping to CXL host bridges - which may be identified by the id provied in the -device entry. - Multiple entries are needed to specify all the targets when - the fixed memory window represents interleaved memory. X is the - target index from 0. - - ``size=size`` sets the size of the CFMW. This must be a multiple of - 256MiB. The region will be aligned to 256MiB but the location is - platform and configuration dependent. - - ``interleave-granularity=granularity`` sets the granularity of - interleave. Default 256KiB. Only 256KiB, 512KiB, 1024KiB, 2048KiB - 4096KiB, 8192KiB and 16384KiB granularities supported. - - Example: - - :: - - -cxl-fixed-memory-window targets.0=cxl.0,targets.1=cxl.1,size=128G,interleave-granularity=512k - -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) @@ -629,7 +678,7 @@ DEF("m", HAS_ARG, QEMU_OPTION_m, " size: initial amount of guest memory\n" " slots: number of hotplug slots (default: none)\n" " maxmem: maximum amount of guest memory (default: none)\n" - "NOTE: Some architectures might enforce a specific granularity\n", + " Note: Some architectures might enforce a specific granularity\n", QEMU_ARCH_ALL) SRST ``-m [size=]megs[,slots=n,maxmem=size]`` @@ -689,30 +738,23 @@ SRST 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) -SRST -``-audio-help`` - Will show the -audiodev equivalent of the currently specified - (deprecated) environment variables. -ERST - DEF("audio", HAS_ARG, QEMU_OPTION_audio, + "-audio [driver=]driver[,prop[=value][,...]]\n" + " specifies default audio backend when `audiodev` is not\n" + " used to create a machine or sound device;" + " options are the same as for -audiodev\n" "-audio [driver=]driver,model=value[,prop[=value][,...]]\n" " specifies the audio backend and device to use;\n" " apart from 'model', options are the same as for -audiodev.\n" " use '-audio model=help' to show possible devices.\n", QEMU_ARCH_ALL) SRST -``-audio [driver=]driver,model=value[,prop[=value][,...]]`` - This option is a shortcut for configuring both the guest audio - hardware and the host audio backend in one go. - The host backend options are the same as with the corresponding - ``-audiodev`` options below. The guest hardware model can be set with - ``model=modelname``. Use ``model=help`` to list the available device - types. +``-audio [driver=]driver[,model=value][,prop[=value][,...]]`` + If the ``model`` option is specified, ``-audio`` is a shortcut + for configuring both the guest audio hardware and the host audio + backend in one go. The guest hardware model can be set with + ``model=modelname``. Use ``model=help`` to list the available + device types. The following two example do exactly the same, to show how ``-audio`` can be used to shorten the command line length: @@ -721,11 +763,23 @@ SRST |qemu_system| -audiodev pa,id=pa -device sb16,audiodev=pa |qemu_system| -audio pa,model=sb16 + + If the ``model`` option is not specified, ``-audio`` is used to + configure a default audio backend that will be used whenever the + ``audiodev`` property is not set on a device or machine. In + particular, ``-audio none`` ensures that no audio is produced even + for machines that have embedded sound hardware. + + In both cases, the driver option is the same as with the corresponding + ``-audiodev`` option below. Use ``driver=help`` to list the available + drivers. + ERST DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev, "-audiodev [driver=]driver,id=id[,prop[=value][,...]]\n" " specifies the audio backend to use\n" + " Use ``-audiodev help`` to list the available drivers\n" " id= identifier of the backend\n" " timer-period= timer period in microseconds\n" " in|out.mixing-engine= use mixing engine to mix streams inside QEMU\n" @@ -768,10 +822,19 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev, " in|out.name= source/sink device name\n" " in|out.latency= desired latency in microseconds\n" #endif +#ifdef CONFIG_AUDIO_PIPEWIRE + "-audiodev pipewire,id=id[,prop[=value][,...]]\n" + " in|out.name= source/sink device name\n" + " in|out.stream-name= name of pipewire stream\n" + " in|out.latency= desired latency in microseconds\n" +#endif #ifdef CONFIG_AUDIO_SDL "-audiodev sdl,id=id[,prop[=value][,...]]\n" " in|out.buffer-count= number of buffers\n" #endif +#ifdef CONFIG_AUDIO_SNDIO + "-audiodev sndio,id=id[,prop[=value][,...]]\n" +#endif #ifdef CONFIG_SPICE "-audiodev spice,id=id[,prop[=value][,...]]\n" #endif @@ -928,6 +991,21 @@ SRST Desired latency in microseconds. The PulseAudio server will try to honor this value but actual latencies may be lower or higher. +``-audiodev pipewire,id=id[,prop[=value][,...]]`` + Creates a backend using PipeWire. This backend is available on + most systems. + + PipeWire specific options are: + + ``in|out.latency=usecs`` + Desired latency in microseconds. + + ``in|out.name=sink`` + Use the specified source/sink for recording/playback. + + ``in|out.stream-name`` + Specify the name of pipewire stream. + ``-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 @@ -938,6 +1016,19 @@ SRST ``in|out.buffer-count=count`` Sets the count of the buffers. +``-audiodev sndio,id=id[,prop[=value][,...]]`` + Creates a backend using SNDIO. This backend is available on + OpenBSD and most other Unix-like systems. + + Sndio specific options are: + + ``in|out.dev=device`` + Specify the sndio device to use for input and/or output. Default + is ``default``. + + ``in|out.latency=usecs`` + Sets the desired period length in microseconds. + ``-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 @@ -1021,7 +1112,7 @@ SRST details on the external interface. ``-device isa-ipmi-kcs,bmc=id[,ioport=val][,irq=val]`` - Add a KCS IPMI interafce on the ISA bus. This also adds a + Add a KCS IPMI interface on the ISA bus. This also adds a corresponding ACPI and SMBIOS entries, if appropriate. ``bmc=id`` @@ -1041,7 +1132,7 @@ SRST is 0xe4 and the default interrupt is 5. ``-device pci-ipmi-kcs,bmc=id`` - Add a KCS IPMI interafce on the PCI bus. + Add a KCS IPMI interface on the PCI bus. ``bmc=id`` The BMC to connect to, one of ipmi-bmc-sim or ipmi-bmc-extern above. @@ -1081,6 +1172,17 @@ SRST Please also refer to the wiki page for general scenarios of VT-d emulation in QEMU: https://wiki.qemu.org/Features/VT-d. +``-device virtio-iommu-pci[,option=...]`` + This is only supported by ``-machine q35`` (x86_64) and ``-machine virt`` (ARM). + It supports below options: + + ``granule=val`` (possible values are 4k, 8k, 16k, 64k and host; default: host) + This decides the default granule to be be exposed by the + virtio-iommu. If host, the granule matches the host page size. + + ``aw-bits=val`` (val between 32 and 64, default depends on machine) + This decides the address width of the IOVA address space. + ERST DEF("name", HAS_ARG, QEMU_OPTION_name, @@ -1110,6 +1212,31 @@ DEFHEADING() DEFHEADING(Block device options:) +SRST +The QEMU block device handling options have a long history and +have gone through several iterations as the feature set and complexity +of the block layer have grown. Many online guides to QEMU often +reference older and deprecated options, which can lead to confusion. + +The most explicit way to describe disks is to use a combination of +``-device`` to specify the hardware device and ``-blockdev`` to +describe the backend. The device defines what the guest sees and the +backend describes how QEMU handles the data. It is the only guaranteed +stable interface for describing block devices and as such is +recommended for management tools and scripting. + +The ``-drive`` option combines the device and backend into a single +command line option which is a more human friendly. There is however no +interface stability guarantee although some older board models still +need updating to work with the modern blockdev forms. + +Older options like ``-hda`` are essentially macros which expand into +``-drive`` options for various drive interfaces. The original forms +bake in a lot of assumptions from the days when QEMU was emulating a +legacy PC, they are not recommended for modern configurations. + +ERST + 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) @@ -1122,10 +1249,10 @@ SRST ERST DEF("hda", HAS_ARG, QEMU_OPTION_hda, - "-hda/-hdb file use 'file' as IDE hard disk 0/1 image\n", QEMU_ARCH_ALL) + "-hda/-hdb file use 'file' as hard disk 0/1 image\n", QEMU_ARCH_ALL) 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) + "-hdc/-hdd file use 'file' as hard disk 2/3 image\n", QEMU_ARCH_ALL) DEF("hdd", HAS_ARG, QEMU_OPTION_hdd, "", QEMU_ARCH_ALL) SRST ``-hda file`` @@ -1135,18 +1262,22 @@ SRST ``-hdc file`` \ ``-hdd file`` - Use file as hard disk 0, 1, 2 or 3 image (see the :ref:`disk images` - chapter in the System Emulation Users Guide). + Use file as hard disk 0, 1, 2 or 3 image on the default bus of the + emulated machine (this is for example the IDE bus on most x86 machines, + but it can also be SCSI, virtio or something else on other target + architectures). See also the :ref:`disk images` chapter in the System + Emulation Users Guide. ERST DEF("cdrom", HAS_ARG, QEMU_OPTION_cdrom, - "-cdrom file use 'file' as IDE cdrom image (cdrom is ide1 master)\n", + "-cdrom file use 'file' as CD-ROM image\n", QEMU_ARCH_ALL) 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. + Use file as CD-ROM image on the default bus of the emulated machine + (which is IDE1 master on x86, so you cannot use ``-hdc`` and ``-cdrom`` + at the same time there). On systems that support it, you can use the + host CD-ROM by using ``/dev/cdrom`` as filename. ERST DEF("blockdev", HAS_ARG, QEMU_OPTION_blockdev, @@ -1344,6 +1475,22 @@ SRST issued on other occasions where a cluster gets freed (on/off; default: off) + ``discard-no-unref`` + When enabled, data clusters will remain preallocated when they are + no longer used, e.g. because they are discarded or converted to + zero clusters. As usual, whether the old data is discarded or kept + on the protocol level (i.e. in the image file) depends on the + setting of the pass-discard-request option. Keeping the clusters + preallocated prevents qcow2 fragmentation that would otherwise be + caused by freeing and re-allocating them later. Besides potential + performance degradation, such fragmentation can lead to increased + allocation of clusters past the end of the image file, + resulting in image files whose file length can grow much larger + than their guest disk size would suggest. + If image file length is of concern (e.g. when storing qcow2 + images directly on block devices), you should consider enabling + this option. + ``overlap-check`` Which overlap checks to perform for writes to the image (none/constant/cached/all; default: cached). For details or @@ -1566,7 +1713,7 @@ SRST .. parsed-literal:: - |qemu_system_x86| -drive file=a -drive file=b" + |qemu_system_x86| -drive file=a -drive file=b is interpreted like: @@ -1590,13 +1737,6 @@ SRST 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) -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) @@ -1606,6 +1746,14 @@ SRST the raw disk image you use is not written back. You can however force the write back by pressing C-a s (see the :ref:`disk images` chapter in the System Emulation Users Guide). + + .. warning:: + snapshot is incompatible with ``-blockdev`` (instead use qemu-img + to manually create snapshot images to attach to your blockdev). + If you have mixed ``-blockdev`` and ``-drive`` declarations you + can use the 'snapshot' property on your drive declarations + instead of this global option. + ERST DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev, @@ -1635,7 +1783,9 @@ SRST Accesses to the filesystem are done by QEMU. ``proxy`` - Accesses to the filesystem are done by virtfs-proxy-helper(1). + Accesses to the filesystem are done by virtfs-proxy-helper(1). This + option is deprecated (since QEMU 8.1) and will be removed in a future + version of QEMU. Use ``local`` instead. ``synth`` Synthetic filesystem, only used by QTests. @@ -1755,7 +1905,7 @@ SRST directory on host is made directly accessible by guest as a pass-through file system by using the 9P network protocol for communication between host and guests, if desired even accessible, shared by several guests - simultaniously. + simultaneously. Note that ``-virtfs`` is actually just a convenience shortcut for its generalized form ``-fsdev -device virtio-9p-pci``. @@ -1767,6 +1917,8 @@ SRST ``proxy`` Accesses to the filesystem are done by virtfs-proxy-helper(1). + This option is deprecated (since QEMU 8.1) and will be removed in a + future version of QEMU. Use ``local`` instead. ``synth`` Synthetic filesystem, only used by QTests. @@ -1859,8 +2011,8 @@ SRST ERST DEF("iscsi", HAS_ARG, QEMU_OPTION_iscsi, - "-iscsi [user=user][,password=password]\n" - " [,header-digest=CRC32C|CR32C-NONE|NONE-CRC32C|NONE\n" + "-iscsi [user=user][,password=password][,password-secret=secret-id]\n" + " [,header-digest=CRC32C|CR32C-NONE|NONE-CRC32C|NONE]\n" " [,initiator-name=initiator-iqn][,id=target-iqn]\n" " [,timeout=timeout]\n" " iSCSI session parameters\n", QEMU_ARCH_ALL) @@ -1943,7 +2095,8 @@ DEF("display", HAS_ARG, QEMU_OPTION_display, #endif #if defined(CONFIG_GTK) "-display gtk[,full-screen=on|off][,gl=on|off][,grab-on-hover=on|off]\n" - " [,show-cursor=on|off][,window-close=on|off]\n" + " [,show-tabs=on|off][,show-cursor=on|off][,window-close=on|off]\n" + " [,show-menubar=on|off][,zoom-to-fit=on|off]\n" #endif #if defined(CONFIG_VNC) "-display vnc=[,]\n" @@ -1953,6 +2106,8 @@ DEF("display", HAS_ARG, QEMU_OPTION_display, #endif #if defined(CONFIG_COCOA) "-display cocoa[,full-grab=on|off][,swap-opt-cmd=on|off]\n" + " [,show-cursor=on|off][,left-command-key=on|off]\n" + " [,full-screen=on|off][,zoom-to-fit=on|off]\n" #endif #if defined(CONFIG_OPENGL) "-display egl-headless[,rendernode=]\n" @@ -1960,9 +2115,6 @@ DEF("display", HAS_ARG, QEMU_OPTION_display, #if defined(CONFIG_DBUS_DISPLAY) "-display dbus[,addr=]\n" " [,gl=on|core|es|off][,rendernode=]\n" -#endif -#if defined(CONFIG_COCOA) - "-display cocoa[,show-cursor=on|off][,left-command-key=on|off]\n" #endif "-display none\n" " select display backend type\n" @@ -2028,10 +2180,19 @@ SRST ``grab-on-hover=on|off`` : Grab keyboard input on mouse hover + ``show-tabs=on|off`` : Display the tab bar for switching between the + various graphical interfaces (e.g. VGA and + virtual console character devices) by default. + ``show-cursor=on|off`` : Force showing the mouse cursor ``window-close=on|off`` : Allow to quit qemu with window close button + ``show-menubar=on|off`` : Display the main window menubar, defaults to "on" + + ``zoom-to-fit=on|off`` : Expand video output to the window size, + defaults to "off" + ``curses[,charset=]`` Display video output via curses. For graphics device models which support a text mode, QEMU can display this output using a @@ -2048,10 +2209,26 @@ SRST provides drop-down menus and other UI elements to configure and control the VM during runtime. Valid parameters are: + ``full-grab=on|off`` : Capture all key presses, including system combos. + This requires accessibility permissions, since it + performs a global grab on key events. + (default: off) See + https://support.apple.com/en-in/guide/mac-help/mh32356/mac + + ``swap-opt-cmd=on|off`` : Swap the Option and Command keys so that their + key codes match their position on non-Mac + keyboards and you can use Meta/Super and Alt + where you expect them. (default: off) + ``show-cursor=on|off`` : Force showing the mouse cursor ``left-command-key=on|off`` : Disable forwarding left command key to host + ``full-screen=on|off`` : Start in fullscreen mode + + ``zoom-to-fit=on|off`` : Expand video output to the window size, + defaults to "off" + ``egl-headless[,rendernode=]`` Offload all OpenGL operations to a local DRI device. For any graphical display, this display needs to be paired with either @@ -2095,7 +2272,7 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice, " [,tls-channel=[main|display|cursor|inputs|record|playback]]\n" " [,plaintext-channel=[main|display|cursor|inputs|record|playback]]\n" " [,sasl=on|off][,disable-ticketing=on|off]\n" - " [,password=][,password-secret=]\n" + " [,password-secret=]\n" " [,image-compression=[auto_glz|auto_lz|quic|glz|lz|off]]\n" " [,jpeg-wan-compression=[auto|never|always]]\n" " [,zlib-glz-wan-compression=[auto|never|always]]\n" @@ -2103,8 +2280,8 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice, " [,disable-agent-file-xfer=on|off][,agent-mouse=[on|off]]\n" " [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n" " [,gl=[on|off]][,rendernode=]\n" - " enable spice\n" - " at least one of {port, tls-port} is mandatory\n", + " enable spice\n" + " at least one of {port, tls-port} is mandatory\n", QEMU_ARCH_ALL) #endif SRST @@ -2121,13 +2298,6 @@ SRST ``ipv4=on|off``; \ ``ipv6=on|off``; \ ``unix=on|off`` Force using the specified IP version. - ``password=`` - Set the password you need to authenticate. - - This option is deprecated and insecure because it leaves the - password visible in the process listing. Use ``password-secret`` - instead. - ``password-secret=`` Set the ID of the ``secret`` object containing the password you need to authenticate. @@ -2292,8 +2462,10 @@ SRST OBP. ERST +#ifdef CONFIG_VNC DEF("vnc", HAS_ARG, QEMU_OPTION_vnc , "-vnc shorthand for -display vnc=\n", QEMU_ARCH_ALL) +#endif SRST ``-vnc display[,option[,option[,...]]]`` Normally, if QEMU is compiled with graphical window support, it @@ -2307,7 +2479,7 @@ SRST ``to=L`` With this option, QEMU will try next available VNC displays, - until the number L, if the origianlly defined "-vnc display" is + until the number L, if the originally defined "-vnc display" is not available, e.g. port 5900+display is already used by another application. By default, to=0. @@ -2480,7 +2652,8 @@ 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). + option slows down the IDE transfers). Synonym of ``-global + ide-device.win2k-install-hack=on``. ERST DEF("no-fd-bootchk", 0, QEMU_OPTION_no_fd_bootchk, @@ -2489,23 +2662,7 @@ DEF("no-fd-bootchk", 0, QEMU_OPTION_no_fd_bootchk, 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) -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) -SRST -``-no-hpet`` - Disable HPET support. + needed to boot from old floppy disks. Synonym of ``-m fd-bootchk=off``. ERST DEF("acpitable", HAS_ARG, QEMU_OPTION_acpitable, @@ -2541,8 +2698,10 @@ DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, " specify SMBIOS type 3 fields\n" "-smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str]\n" " [,asset=str][,part=str][,max-speed=%d][,current-speed=%d]\n" - " [,processor-id=%d]\n" + " [,processor-family=%d,processor-id=%d]\n" " specify SMBIOS type 4 fields\n" + "-smbios type=8[,external_reference=str][,internal_reference=str][,connector_type=%d][,port_type=%d]\n" + " specify SMBIOS type 8 fields\n" "-smbios type=11[,value=str][,path=filename]\n" " specify SMBIOS type 11 fields\n" "-smbios type=17[,loc_pfx=str][,bank=str][,manufacturer=str][,serial=str]\n" @@ -2550,7 +2709,7 @@ DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, " specify SMBIOS type 17 fields\n" "-smbios type=41[,designation=str][,kind=str][,instance=%d][,pcidev=str]\n" " specify SMBIOS type 41 fields\n", - QEMU_ARCH_I386 | QEMU_ARCH_ARM) + QEMU_ARCH_I386 | QEMU_ARCH_ARM | QEMU_ARCH_LOONGARCH | QEMU_ARCH_RISCV) SRST ``-smbios file=binary`` Load SMBIOS entry from binary file. @@ -2567,9 +2726,12 @@ SRST ``-smbios type=3[,manufacturer=str][,version=str][,serial=str][,asset=str][,sku=str]`` Specify SMBIOS type 3 fields -``-smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str][,asset=str][,part=str][,processor-id=%d]`` +``-smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str][,asset=str][,part=str][,processor-family=%d][,processor-id=%d]`` Specify SMBIOS type 4 fields +``-smbios type=9[,slot_designation=str][,slot_type=%d][,slot_data_bus_width=%d][,current_usage=%d][,slot_length=%d][,slot_id=%d][,slot_characteristics1=%d][,slot_characteristics12=%d][,pci_device=str]`` + Specify SMBIOS type 9 fields + ``-smbios type=11[,value=str][,path=filename]`` Specify SMBIOS type 11 fields @@ -2727,6 +2889,20 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, "-netdev socket,id=str[,fd=h][,udp=host:port][,localaddr=host:port]\n" " configure a network backend to connect to another network\n" " using an UDP tunnel\n" + "-netdev stream,id=str[,server=on|off],addr.type=inet,addr.host=host,addr.port=port[,to=maxport][,numeric=on|off][,keep-alive=on|off][,mptcp=on|off][,addr.ipv4=on|off][,addr.ipv6=on|off][,reconnect=seconds]\n" + "-netdev stream,id=str[,server=on|off],addr.type=unix,addr.path=path[,abstract=on|off][,tight=on|off][,reconnect=seconds]\n" + "-netdev stream,id=str[,server=on|off],addr.type=fd,addr.str=file-descriptor[,reconnect=seconds]\n" + " configure a network backend to connect to another network\n" + " using a socket connection in stream mode.\n" + "-netdev dgram,id=str,remote.type=inet,remote.host=maddr,remote.port=port[,local.type=inet,local.host=addr]\n" + "-netdev dgram,id=str,remote.type=inet,remote.host=maddr,remote.port=port[,local.type=fd,local.str=file-descriptor]\n" + " configure a network backend to connect to a multicast maddr and port\n" + " use ``local.host=addr`` to specify the host address to send packets from\n" + "-netdev dgram,id=str,local.type=inet,local.host=addr,local.port=port[,remote.type=inet,remote.host=addr,remote.port=port]\n" + "-netdev dgram,id=str,local.type=unix,local.path=path[,remote.type=unix,remote.path=path]\n" + "-netdev dgram,id=str,local.type=fd,local.str=file-descriptor\n" + " configure a network backend to connect to another network\n" + " using an UDP tunnel\n" #ifdef CONFIG_VDE "-netdev vde,id=str[,sock=socketpath][,port=n][,group=groupname][,mode=octalmode]\n" " configure a network backend to connect to port 'n' of a vde switch\n" @@ -2740,13 +2916,28 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, " VALE port (created on the fly) called 'name' ('nmname' is name of the \n" " netmap device, defaults to '/dev/netmap')\n" #endif +#ifdef CONFIG_AF_XDP + "-netdev af-xdp,id=str,ifname=name[,mode=native|skb][,force-copy=on|off]\n" + " [,queues=n][,start-queue=m][,inhibit=on|off][,sock-fds=x:y:...:z]\n" + " attach to the existing network interface 'name' with AF_XDP socket\n" + " use 'mode=MODE' to specify an XDP program attach mode\n" + " use 'force-copy=on|off' to force XDP copy mode even if device supports zero-copy (default: off)\n" + " use 'inhibit=on|off' to inhibit loading of a default XDP program (default: off)\n" + " with inhibit=on,\n" + " use 'sock-fds' to provide file descriptors for already open AF_XDP sockets\n" + " added to a socket map in XDP program. One socket per queue.\n" + " use 'queues=n' to specify how many queues of a multiqueue interface should be used\n" + " use 'start-queue=m' to specify the first queue that should be used\n" +#endif #ifdef CONFIG_POSIX "-netdev vhost-user,id=str,chardev=dev[,vhostforce=on|off]\n" " configure a vhost-user network, backed by a chardev 'dev'\n" #endif #ifdef __linux__ - "-netdev vhost-vdpa,id=str,vhostdev=/path/to/dev\n" + "-netdev vhost-vdpa,id=str[,vhostdev=/path/to/dev][,vhostfd=h]\n" " configure a vhost-vdpa network,Establish a vhost-vdpa netdev\n" + " use 'vhostdev=/path/to/dev' to open a vhost vdpa device\n" + " use 'vhostfd=h' to connect to an already opened vhost vdpa device\n" #endif #ifdef CONFIG_VMNET "-netdev vmnet-host,id=str[,isolated=on|off][,net-uuid=uuid]\n" @@ -2783,6 +2974,9 @@ DEF("nic", HAS_ARG, QEMU_OPTION_nic, #ifdef CONFIG_NETMAP "netmap|" #endif +#ifdef CONFIG_AF_XDP + "af-xdp|" +#endif #ifdef CONFIG_POSIX "vhost-user|" #endif @@ -2811,6 +3005,9 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, #ifdef CONFIG_NETMAP "netmap|" #endif +#ifdef CONFIG_AF_XDP + "af-xdp|" +#endif #ifdef CONFIG_VMNET "vmnet-host|vmnet-shared|vmnet-bridged|" #endif @@ -2818,7 +3015,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, " old way to initialize a host network interface\n" " (use the -netdev option if possible instead)\n", QEMU_ARCH_ALL) SRST -``-nic [tap|bridge|user|l2tpv3|vde|netmap|vhost-user|socket][,...][,mac=macaddr][,model=mn]`` +``-nic [tap|bridge|user|l2tpv3|vde|netmap|af-xdp|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 @@ -2918,6 +3115,8 @@ SRST 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). + The built-in TFTP server is read-only; it does not implement any + command for writing files. QEMU will not write to this directory. ``tftp-server-name=name`` In BOOTP reply, broadcast name as the "TFTP server name" @@ -3232,6 +3431,55 @@ SRST # launch QEMU instance |qemu_system| linux.img -nic vde,sock=/tmp/myswitch +``-netdev af-xdp,id=str,ifname=name[,mode=native|skb][,force-copy=on|off][,queues=n][,start-queue=m][,inhibit=on|off][,sock-fds=x:y:...:z]`` + Configure AF_XDP backend to connect to a network interface 'name' + using AF_XDP socket. A specific program attach mode for a default + XDP program can be forced with 'mode', defaults to best-effort, + where the likely most performant mode will be in use. Number of queues + 'n' should generally match the number or queues in the interface, + defaults to 1. Traffic arriving on non-configured device queues will + not be delivered to the network backend. + + .. parsed-literal:: + + # set number of queues to 4 + ethtool -L eth0 combined 4 + # launch QEMU instance + |qemu_system| linux.img -device virtio-net-pci,netdev=n1 \\ + -netdev af-xdp,id=n1,ifname=eth0,queues=4 + + 'start-queue' option can be specified if a particular range of queues + [m, m + n] should be in use. For example, this is may be necessary in + order to use certain NICs in native mode. Kernel allows the driver to + create a separate set of XDP queues on top of regular ones, and only + these queues can be used for AF_XDP sockets. NICs that work this way + may also require an additional traffic redirection with ethtool to these + special queues. + + .. parsed-literal:: + + # set number of queues to 1 + ethtool -L eth0 combined 1 + # redirect all the traffic to the second queue (id: 1) + # note: drivers may require non-empty key/mask pair. + ethtool -N eth0 flow-type ether \\ + dst 00:00:00:00:00:00 m FF:FF:FF:FF:FF:FE action 1 + ethtool -N eth0 flow-type ether \\ + dst 00:00:00:00:00:01 m FF:FF:FF:FF:FF:FE action 1 + # launch QEMU instance + |qemu_system| linux.img -device virtio-net-pci,netdev=n1 \\ + -netdev af-xdp,id=n1,ifname=eth0,queues=1,start-queue=1 + + XDP program can also be loaded externally. In this case 'inhibit' option + should be set to 'on' and 'sock-fds' provided with file descriptors for + already open but not bound XDP sockets already added to a socket map for + corresponding queues. One socket per queue. + + .. parsed-literal:: + + |qemu_system| linux.img -device virtio-net-pci,netdev=n1 \\ + -netdev af-xdp,id=n1,ifname=eth0,queues=3,inhibit=on,sock-fds=15:16:17 + ``-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 @@ -3251,7 +3499,7 @@ SRST -netdev type=vhost-user,id=net0,chardev=chr0 \ -device virtio-net-pci,netdev=net0 -``-netdev vhost-vdpa,vhostdev=/path/to/dev`` +``-netdev vhost-vdpa[,vhostdev=/path/to/dev][,vhostfd=h]`` Establish a vhost-vdpa netdev. vDPA device is a device that uses a datapath which complies with @@ -3309,7 +3557,7 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]\n" " [,mux=on|off][,logfile=PATH][,logappend=on|off]\n" "-chardev ringbuf,id=id[,size=size][,logfile=PATH][,logappend=on|off]\n" - "-chardev file,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + "-chardev file,id=id,path=path[,input-path=input-file][,mux=on|off][,logfile=PATH][,logappend=on|off]\n" "-chardev pipe,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" #ifdef _WIN32 "-chardev console,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" @@ -3324,11 +3572,9 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) "-chardev serial,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" - "-chardev tty,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" #endif #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) "-chardev parallel,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" - "-chardev parport,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" #endif #if defined(CONFIG_SPICE) "-chardev spicevmc,id=id,name=name[,debug=debug][,logfile=PATH][,logappend=on|off]\n" @@ -3343,7 +3589,7 @@ The general form of a character device option is: ``-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``, + ``pty``, ``stdio``, ``braille``, ``parallel``, ``spicevmc``, ``spiceport``. The specific backend will determine the applicable options. @@ -3514,13 +3760,19 @@ The available backends are: Create a ring buffer with fixed size ``size``. size must be a power of two and defaults to ``64K``. -``-chardev file,id=id,path=path`` +``-chardev file,id=id,path=path[,input-path=input-path]`` Log all traffic received from the guest to a file. ``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. + If ``input-path`` is specified, this is the path of a second file + which will be used for input. If ``input-path`` is not specified, + no input will be available from the chardev. + + Note that ``input-path`` is not supported on Windows hosts. + ``-chardev pipe,id=id,path=path`` Create a two-way connection to the guest. The behaviour differs slightly between Windows hosts and other hosts: @@ -3567,15 +3819,8 @@ The available backends are: Connect to a local BrlAPI server. ``braille`` does not take any options. -``-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``. - - ``path`` specifies the path to the tty. ``path`` is required. - ``-chardev parallel,id=id,path=path`` \ -``-chardev parport,id=id,path=path`` ``parallel`` is only available on Linux, FreeBSD and DragonFlyBSD hosts. @@ -3685,12 +3930,67 @@ DEFHEADING() #endif -DEFHEADING(Linux/Multiboot boot specific:) +DEFHEADING(Boot Image or Kernel specific:) 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. +There are broadly 4 ways you can boot a system with QEMU. + - specify a firmware and let it control finding a kernel + - specify a firmware and pass a hint to the kernel to boot + - direct kernel image boot + - manually load files into the guest's address space + +The third method is useful for quickly testing kernels but as there is +no firmware to pass configuration information to the kernel the +hardware must either be probeable, the kernel built for the exact +configuration or passed some configuration data (e.g. a DTB blob) +which tells the kernel what drivers it needs. This exact details are +often hardware specific. + +The final method is the most generic way of loading images into the +guest address space and used mostly for ``bare metal`` type +development where the reset vectors of the processor are taken into +account. + +ERST + +SRST + +For x86 machines and some other architectures ``-bios`` will generally +do the right thing with whatever it is given. For other machines the +more strict ``-pflash`` option needs an image that is sized for the +flash device for the given machine type. + +Please see the :ref:`system-targets-ref` section of the manual for +more detailed documentation. + +ERST + +DEF("bios", HAS_ARG, QEMU_OPTION_bios, \ + "-bios file set the filename for the BIOS\n", QEMU_ARCH_ALL) +SRST +``-bios file`` + Set the filename for the BIOS. +ERST + +DEF("pflash", HAS_ARG, QEMU_OPTION_pflash, + "-pflash file use 'file' as a parallel flash image\n", QEMU_ARCH_ALL) +SRST +``-pflash file`` + Use file as a parallel flash image. +ERST + +SRST + +The kernel options were designed to work with Linux kernels although +other things (like hypervisors) can be packaged up as a kernel +executable image. The exact format of a executable image is usually +architecture specific. + +The way in which the kernel is started (what address it is loaded at, +what if any information is passed to it via CPU registers, the state +of the hardware when it is started, and so on) is also architecture +specific. Typically it follows the specification laid down by the +Linux kernel for how kernels for that architecture must be started. ERST @@ -3711,15 +4011,23 @@ ERST DEF("initrd", HAS_ARG, QEMU_OPTION_initrd, \ "-initrd file use 'file' as initial ram disk\n", QEMU_ARCH_ALL) -SRST +SRST(initrd) + ``-initrd file`` Use file as initial ram disk. ``-initrd "file1 arg=foo,file2"`` This syntax is only available with multiboot. - Use file1 and file2 as modules and pass arg=foo as parameter to the - first module. + Use file1 and file2 as modules and pass ``arg=foo`` as parameter to the + first module. Commas can be provided in module parameters by doubling + them on the command line to escape them: + +``-initrd "bzImage earlyprintk=xen,,keep root=/dev/xvda1,initrd.img"`` + Multiboot only. Use bzImage as the first module with + "``earlyprintk=xen,keep root=/dev/xvda1``" as its command line, + and initrd.img as the second module. + ERST DEF("dtb", HAS_ARG, QEMU_OPTION_dtb, \ @@ -3730,6 +4038,25 @@ SRST kernel on boot. ERST +SRST + +Finally you can also manually load images directly into the address +space of the guest. This is most useful for developers who already +know the layout of their guest and take care to ensure something sane +will happen when the reset vector executes. + +The generic loader can be invoked by using the loader device: + +``-device loader,addr=,data=,data-len=[,data-be=][,cpu-num=]`` + +there is also the guest loader which operates in a similar way but +tweaks the DTB so a hypervisor loaded via ``-kernel`` can find where +the guest image is: + +``-device guest-loader,addr=[,kernel=,[bootargs=]][,initrd=]`` + +ERST + DEFHEADING() DEFHEADING(Debug/Expert options:) @@ -3783,9 +4110,13 @@ DEF("fw_cfg", HAS_ARG, QEMU_OPTION_fwcfg, SRST ``-fw_cfg [name=]name,file=file`` Add named fw\_cfg entry with contents from file file. + If the filename contains comma, you must double it (for instance, + "file=my,,file" to use file "my,file"). ``-fw_cfg [name=]name,string=str`` Add named fw\_cfg entry with contents from string str. + If the string contains comma, you must double it (for instance, + "string=my,,string" to use file "my,string"). 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 @@ -3812,10 +4143,11 @@ SRST 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 + This option can be used several times to simulate multiple serial ports. - Use ``-serial none`` to disable all serial ports. + You can use ``-serial none`` to suppress the creation of default + serial devices. Available character devices are: @@ -3837,10 +4169,17 @@ SRST [Linux only] Pseudo TTY (a new PTY is automatically allocated) ``none`` - No device is allocated. + No device is allocated. Note that for machine types which + emulate systems where a serial device is always present in + real hardware, this may be equivalent to the ``null`` option, + in that the serial device is still present but all output + is discarded. For boards where the number of serial ports is + truly variable, this suppresses the creation of the device. ``null`` - void device + A guest will see the UART or serial device as present in the + machine, but all output is discarded, and there is no input. + Conceptually equivalent to redirecting the output to ``/dev/null``. ``chardev:id`` Use a named character device defined with the ``-chardev`` @@ -3993,26 +4332,42 @@ DEF("qmp", HAS_ARG, QEMU_OPTION_qmp, \ QEMU_ARCH_ALL) SRST ``-qmp dev`` - Like -monitor but opens in 'control' mode. + Like ``-monitor`` but opens in 'control' mode. For example, to make + QMP available on localhost port 4444:: + + -qmp tcp:localhost:4444,server=on,wait=off + + Not all options are configurable via this syntax; for maximum + flexibility use the ``-mon`` option and an accompanying ``-chardev``. + ERST DEF("qmp-pretty", HAS_ARG, QEMU_OPTION_qmp_pretty, \ "-qmp-pretty dev like -qmp but uses pretty JSON formatting\n", QEMU_ARCH_ALL) SRST ``-qmp-pretty dev`` - Like -qmp but uses pretty JSON formatting. + 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) SRST ``-mon [chardev=]name[,mode=readline|control][,pretty[=on|off]]`` - Setup monitor on chardev name. ``mode=control`` configures - a QMP monitor (a JSON RPC-style protocol) and it is not the - same as HMP, the human monitor that has a "(qemu)" prompt. - ``pretty`` is only valid when ``mode=control``, + Set up a monitor connected to the chardev ``name``. + QEMU supports two monitors: the Human Monitor Protocol + (HMP; for human interaction), and the QEMU Monitor Protocol + (QMP; a JSON RPC-style protocol). + The default is HMP; ``mode=control`` selects QMP instead. + ``pretty`` is only valid when ``mode=control``, turning on JSON pretty printing to ease human reading and debugging. + + For example:: + + -chardev socket,id=mon1,host=localhost,port=4444,server=on,wait=off \ + -mon chardev=mon1,mode=control,pretty=on + + enables the QMP monitor on localhost port 4444 with pretty-printing. ERST DEF("debugcon", HAS_ARG, QEMU_OPTION_debugcon, \ @@ -4035,13 +4390,6 @@ SRST from a script. ERST -DEF("singlestep", 0, QEMU_OPTION_singlestep, \ - "-singlestep always run in singlestep mode\n", QEMU_ARCH_ALL) -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) @@ -4180,13 +4528,6 @@ SRST 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) -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_ARM | QEMU_ARCH_I386 | QEMU_ARCH_MIPS | QEMU_ARCH_PPC | @@ -4240,7 +4581,7 @@ DEF("action", HAS_ARG, QEMU_OPTION_action, " action when guest reboots [default=reset]\n" "-action shutdown=poweroff|pause\n" " action when guest shuts down [default=poweroff]\n" - "-action panic=pause|shutdown|none\n" + "-action panic=pause|shutdown|exit-failure|none\n" " action when guest panics [default=shutdown]\n" "-action watchdog=reset|shutdown|poweroff|inject-nmi|pause|debug|none\n" " action when watchdog fires [default=reset]\n", @@ -4256,7 +4597,7 @@ SRST ``-action panic=none`` ``-action reboot=shutdown,shutdown=pause`` - ``-watchdog i6300esb -action watchdog=pause`` + ``-device i6300esb -action watchdog=pause`` ERST @@ -4374,35 +4715,6 @@ SRST specifies the snapshot name 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) -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 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: - - ``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", @@ -4424,7 +4736,7 @@ SRST Examples: - ``-watchdog i6300esb -watchdog-action pause``; \ ``-watchdog ib700`` + ``-device i6300esb -watchdog-action pause`` ERST @@ -4452,6 +4764,7 @@ DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \ " prepare for incoming migration, listen on\n" \ " specified protocol and socket address\n" \ "-incoming fd:fd\n" \ + "-incoming file:filename[,offset=offset]\n" \ "-incoming exec:cmdline\n" \ " accept incoming migration on given file descriptor\n" \ " or from given external command\n" \ @@ -4468,7 +4781,11 @@ SRST Prepare for incoming migration, listen on a given unix socket. ``-incoming fd:fd`` - Accept incoming migration from a given filedescriptor. + Accept incoming migration from a given file descriptor. + +``-incoming file:filename[,offset=offset]`` + Accept incoming migration from a given file starting at offset. + offset allows the common size suffixes, or a 0x prefix, but not both. ``-incoming exec:cmdline`` Accept incoming migration as an output from specified external @@ -4498,17 +4815,6 @@ SRST ``-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 -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, \ "-runas user change to user id user just before starting the VM\n" \ @@ -4546,37 +4852,28 @@ DEF("semihosting", 0, QEMU_OPTION_semihosting, QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2 | QEMU_ARCH_RISCV) SRST ``-semihosting`` - Enable semihosting mode (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V only). + Enable :ref:`Semihosting` mode (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V only). - Note that this allows guest direct access to the host filesystem, so - should only be used with a trusted guest OS. + .. warning:: + Note that this allows guest direct access to the host filesystem, so + should only be used with a trusted guest OS. See the -semihosting-config option documentation for further information about the facilities this enables. 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-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,userspace=on|off][,arg=str[,...]]\n" \ " semihosting configuration\n", QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2 | QEMU_ARCH_RISCV) SRST -``-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,arg=str[,...]]`` - Enable and configure semihosting (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V +``-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,userspace=on|off][,arg=str[,...]]`` + Enable and configure :ref:`Semihosting` (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V only). - Note that this allows guest direct access to the host filesystem, so - should only be used with a trusted guest OS. - - On Arm this implements the standard semihosting API, version 2.0. - - On M68K this implements the "ColdFire GDB" interface used by - libgloss. - - Xtensa semihosting provides basic file IO calls, such as - open/read/write/seek/select. Tensilica baremetal libc for ISS and - linux platform "sim" use this interface. - - On RISC-V this implements the standard semihosting API, version 0.2. + .. warning:: + Note that this allows guest direct access to the host filesystem, so + should only be used with a trusted guest OS. ``target=native|gdb|auto`` Defines where the semihosting calls will be addressed, to QEMU @@ -4587,6 +4884,13 @@ SRST Send the output to a chardev backend output for native or auto output when not in gdb + ``userspace=on|off`` + Allows code running in guest userspace to access the semihosting + interface. The default is that only privileged guest code can + make semihosting calls. Note that setting ``userspace=on`` should + only be used if all guest code is trusted (for example, in + bare-metal test case code). + ``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 @@ -4684,6 +4988,34 @@ HXCOMM Internal use DEF("qtest", HAS_ARG, QEMU_OPTION_qtest, "", QEMU_ARCH_ALL) DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log, "", QEMU_ARCH_ALL) +#ifdef CONFIG_POSIX +DEF("run-with", HAS_ARG, QEMU_OPTION_run_with, + "-run-with [async-teardown=on|off][,chroot=dir]\n" + " Set miscellaneous QEMU process lifecycle options:\n" + " async-teardown=on enables asynchronous teardown (Linux only)\n" + " chroot=dir chroot to dir just before starting the VM\n", + QEMU_ARCH_ALL) +SRST +``-run-with [async-teardown=on|off][,chroot=dir]`` + Set QEMU process lifecycle options. + + ``async-teardown=on`` enables asynchronous teardown. A new process called + "cleanup/" will be created at startup sharing the address + space with the main QEMU process, using clone. It will wait for the + main QEMU process to terminate completely, and then exit. This allows + QEMU to terminate very quickly even if the guest was huge, leaving the + teardown of the address space to the cleanup process. Since the cleanup + process shares the same cgroups as the main QEMU process, accounting is + performed correctly. This only works if the cleanup process is not + forcefully killed with SIGKILL before the main QEMU process has + terminated completely. + + ``chroot=dir`` can be used for doing a chroot to the specified directory + immediately before starting the guest execution. This is especially useful + in combination with -runas. +ERST +#endif + DEF("msg", HAS_ARG, QEMU_OPTION_msg, "-msg [timestamp[=on|off]][,guest-name=[on|off]]\n" " control error message format\n" @@ -4725,6 +5057,26 @@ SRST Enable synchronization profiling. ERST +#if defined(CONFIG_TCG) && defined(CONFIG_LINUX) +DEF("perfmap", 0, QEMU_OPTION_perfmap, + "-perfmap generate a /tmp/perf-${pid}.map file for perf\n", + QEMU_ARCH_ALL) +SRST +``-perfmap`` + Generate a map file for Linux perf tools that will allow basic profiling + information to be broken down into basic blocks. +ERST + +DEF("jitdump", 0, QEMU_OPTION_jitdump, + "-jitdump generate a jit-${pid}.dump file for perf\n", + QEMU_ARCH_ALL) +SRST +``-jitdump`` + Generate a dump file for Linux perf tools that maps basic blocks to symbol + names, line numbers and JITted code. +ERST +#endif + DEFHEADING() DEFHEADING(Generic object creation:) @@ -4742,7 +5094,7 @@ SRST 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,readonly=on|off`` + ``-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,offset=offset,readonly=on|off,rom=on|off|auto`` Creates a memory file backend object, which can be used to back the guest RAM with huge pages. @@ -4812,6 +5164,10 @@ SRST such cases, users can specify the required alignment via this option. + The ``offset`` option specifies the offset into the target file + that the region starts at. You can use this parameter to back + multiple regions with a single file. + 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 @@ -4828,6 +5184,20 @@ SRST The ``readonly`` option specifies whether the backing file is opened read-only or read-write (default). + The ``rom`` option specifies whether to create Read Only Memory + (ROM) that cannot be modified by the VM. Any write attempts to such + ROM will be denied. Most use cases want proper RAM instead of ROM. + However, selected use cases, like R/O NVDIMMs, can benefit from + ROM. If set to ``on``, create ROM; if set to ``off``, create + writable RAM; if set to ``auto`` (default), the value of the + ``readonly`` option is used. This option is primarily helpful when + we want to have writable RAM in configurations that would + traditionally create ROM before the ``rom`` option was introduced: + VM templating, where we want to open a file readonly + (``readonly=on``) and mark the memory to be private for QEMU + (``share=off``). For this use case, we need writable RAM instead + of ROM, and want to also set ``rom=off``. + ``-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 @@ -4860,6 +5230,18 @@ SRST The ``share`` boolean option is on by default with memfd. + ``-object iommufd,id=id[,fd=fd]`` + Creates an iommufd backend which allows control of DMA mapping + through the ``/dev/iommu`` device. + + The ``id`` parameter is a unique ID which frontends (such as + vfio-pci of vdpa) will use to connect with the iommufd backend. + + The ``fd`` parameter is an optional pre-opened file descriptor + resulting from ``/dev/iommu`` opening. Usually the iommufd is shared + across all subsystems, bringing the benefit of centralized + reference counting. + ``-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 @@ -5105,7 +5487,7 @@ SRST KVM COLO primary: - -netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown + -netdev tap,id=hn0,vhost=off -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=on,wait=off -chardev socket,id=compare1,host=3.3.3.3,port=9004,server=on,wait=off @@ -5120,7 +5502,7 @@ SRST -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 + -netdev tap,id=hn0,vhost=off -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 @@ -5131,7 +5513,7 @@ SRST Xen COLO primary: - -netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown + -netdev tap,id=hn0,vhost=off -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=on,wait=off -chardev socket,id=compare1,host=3.3.3.3,port=9004,server=on,wait=off @@ -5144,10 +5526,10 @@ SRST -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 + -object colo-compare,id=comp0,primary_in=compare0-0,secondary_in=compare1,outdev=compare_out0,notify_dev=notify_way,iothread=iothread1 secondary: - -netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,down script=/etc/qemu-ifdown + -netdev tap,id=hn0,vhost=off -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 @@ -5158,8 +5540,8 @@ SRST 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 + Creates a cryptodev backend which executes crypto operations 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 @@ -5288,7 +5670,7 @@ SRST physical address space. The ``reduced-phys-bits`` is used to provide the number of bits we loose in physical address space. Similar to C-bit, the value is Host family dependent. On EPYC, - the value should be 5. + a guest will lose a maximum of 1 bit, so the value should be 1. The ``sev-device`` provides the device file to use for communicating with the SEV firmware running inside AMD Secure @@ -5323,7 +5705,7 @@ SRST # |qemu_system_x86| \\ ...... \\ - -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=5 \\ + -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 \\ -machine ...,memory-encryption=sev0 \\ ..... @@ -5426,7 +5808,7 @@ SRST file=/etc/qemu/vnc.allow Finally the ``/etc/qemu/vnc.allow`` file would contain the list - of x509 distingished names that are permitted access + of x509 distinguished names that are permitted access :: diff --git a/qga/channel-posix.c b/qga/channel-posix.c index 6796a02cff..465d688ecb 100644 --- a/qga/channel-posix.c +++ b/qga/channel-posix.c @@ -138,7 +138,7 @@ static gboolean ga_channel_open(GAChannel *c, const gchar *path, 0 ); if (fd == -1) { - error_setg_errno(errp, errno, "error opening channel"); + error_setg_errno(errp, errno, "error opening channel '%s'", path); return false; } #ifdef CONFIG_SOLARIS @@ -149,6 +149,25 @@ static gboolean ga_channel_open(GAChannel *c, const gchar *path, return false; } #endif +#ifdef __FreeBSD__ + /* + * In the default state channel sends echo of every command to a + * client. The client program doesn't expect this and raises an + * error. Suppress echo by resetting ECHO terminal flag. + */ + struct termios tio; + if (tcgetattr(fd, &tio) < 0) { + error_setg_errno(errp, errno, "error getting channel termios attrs"); + close(fd); + return false; + } + tio.c_lflag &= ~ECHO; + if (tcsetattr(fd, TCSAFLUSH, &tio) < 0) { + error_setg_errno(errp, errno, "error setting channel termios attrs"); + close(fd); + return false; + } +#endif /* __FreeBSD__ */ ret = ga_channel_client_add(c, fd); if (ret) { error_setg(errp, "error adding channel to main loop"); @@ -163,7 +182,7 @@ static gboolean ga_channel_open(GAChannel *c, const gchar *path, assert(fd < 0); fd = qga_open_cloexec(path, O_RDWR | O_NOCTTY | O_NONBLOCK, 0); if (fd == -1) { - error_setg_errno(errp, errno, "error opening channel"); + error_setg_errno(errp, errno, "error opening channel '%s'", path); return false; } tcgetattr(fd, &tio); diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c new file mode 100644 index 0000000000..17bddda1cf --- /dev/null +++ b/qga/commands-bsd.c @@ -0,0 +1,205 @@ +/* + * QEMU Guest Agent BSD-specific command implementations + * + * Copyright (c) Virtuozzo International GmbH. + * + * Authors: + * Alexander Ivanov + * + * 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 "qga-qapi-commands.h" +#include "qapi/qmp/qerror.h" +#include "qapi/error.h" +#include "qemu/queue.h" +#include "commands-common.h" +#include +#include +#include +#include +#include +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include +#include +#else +#include +#endif +#include + +#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) +bool build_fs_mount_list(FsMountList *mounts, Error **errp) +{ + FsMount *mount; + struct statfs *mntbuf, *mntp; + struct stat statbuf; + int i, count, ret; + + count = getmntinfo(&mntbuf, MNT_NOWAIT); + if (count == 0) { + error_setg_errno(errp, errno, "getmntinfo failed"); + return false; + } + + for (i = 0; i < count; i++) { + mntp = &mntbuf[i]; + ret = stat(mntp->f_mntonname, &statbuf); + if (ret != 0) { + error_setg_errno(errp, errno, "stat failed on %s", + mntp->f_mntonname); + return false; + } + + mount = g_new0(FsMount, 1); + + mount->dirname = g_strdup(mntp->f_mntonname); + mount->devtype = g_strdup(mntp->f_fstypename); + mount->devmajor = major(mount->dev); + mount->devminor = minor(mount->dev); + mount->fsid = mntp->f_fsid; + mount->dev = statbuf.st_dev; + + QTAILQ_INSERT_TAIL(mounts, mount, next); + } + return true; +} +#endif /* CONFIG_FSFREEZE || CONFIG_FSTRIM */ + +#if defined(CONFIG_FSFREEZE) +static int ufssuspend_fd = -1; +static int ufssuspend_cnt; + +int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints, + strList *mountpoints, + FsMountList mounts, + Error **errp) +{ + int ret; + strList *list; + struct FsMount *mount; + + if (ufssuspend_fd != -1) { + error_setg(errp, "filesystems have already frozen"); + return -1; + } + + ufssuspend_cnt = 0; + ufssuspend_fd = qemu_open(_PATH_UFSSUSPEND, O_RDWR, errp); + if (ufssuspend_fd == -1) { + return -1; + } + + QTAILQ_FOREACH_REVERSE(mount, &mounts, next) { + /* + * To issue fsfreeze in the reverse order of mounts, check if the + * mount is listed in the list here + */ + if (has_mountpoints) { + for (list = mountpoints; list; list = list->next) { + if (g_str_equal(list->value, mount->dirname)) { + break; + } + } + if (!list) { + continue; + } + } + + /* Only UFS supports suspend */ + if (!g_str_equal(mount->devtype, "ufs")) { + continue; + } + + ret = ioctl(ufssuspend_fd, UFSSUSPEND, &mount->fsid); + if (ret == -1) { + /* + * ioctl returns EBUSY for all the FS except the first one + * that was suspended + */ + if (errno == EBUSY) { + continue; + } + error_setg_errno(errp, errno, "failed to freeze %s", + mount->dirname); + goto error; + } + ufssuspend_cnt++; + } + return ufssuspend_cnt; +error: + close(ufssuspend_fd); + ufssuspend_fd = -1; + return -1; + +} + +/* + * We don't need to call UFSRESUME ioctl because all the frozen FS + * are thawed on /dev/ufssuspend closing. + */ +int qmp_guest_fsfreeze_do_thaw(Error **errp) +{ + int ret = ufssuspend_cnt; + ufssuspend_cnt = 0; + if (ufssuspend_fd != -1) { + close(ufssuspend_fd); + ufssuspend_fd = -1; + } + return ret; +} + +GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestDiskInfoList *qmp_guest_get_disks(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} +#endif /* CONFIG_FSFREEZE */ + +#ifdef HAVE_GETIFADDRS +/* + * Fill "buf" with MAC address by ifaddrs. Pointer buf must point to a + * buffer with ETHER_ADDR_LEN length at least. + * + * Returns false in case of an error, otherwise true. "obtained" arguument + * is true if a MAC address was obtained successful, otherwise false. + */ +bool guest_get_hw_addr(struct ifaddrs *ifa, unsigned char *buf, + bool *obtained, Error **errp) +{ + struct sockaddr_dl *sdp; + + *obtained = false; + + if (ifa->ifa_addr->sa_family != AF_LINK) { + /* We can get HW address only for AF_LINK family. */ + g_debug("failed to get MAC address of %s", ifa->ifa_name); + return true; + } + + sdp = (struct sockaddr_dl *)ifa->ifa_addr; + memcpy(buf, sdp->sdl_data + sdp->sdl_nlen, ETHER_ADDR_LEN); + *obtained = true; + + return true; +} +#endif /* HAVE_GETIFADDRS */ diff --git a/qga/commands-common.h b/qga/commands-common.h index d0e4a9696f..8c1c56aac9 100644 --- a/qga/commands-common.h +++ b/qga/commands-common.h @@ -10,6 +10,57 @@ #define QGA_COMMANDS_COMMON_H #include "qga-qapi-types.h" +#include "guest-agent-core.h" +#include "qemu/queue.h" + +#if defined(__linux__) +#include +#ifdef FIFREEZE +#define CONFIG_FSFREEZE +#endif +#ifdef FITRIM +#define CONFIG_FSTRIM +#endif +#endif /* __linux__ */ + +#ifdef __FreeBSD__ +#include +#ifdef UFSSUSPEND +#define CONFIG_FSFREEZE +#endif +#endif /* __FreeBSD__ */ + +#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) +typedef struct FsMount { + char *dirname; + char *devtype; + unsigned int devmajor, devminor; +#if defined(__FreeBSD__) + dev_t dev; + fsid_t fsid; +#endif + QTAILQ_ENTRY(FsMount) next; +} FsMount; + +typedef QTAILQ_HEAD(FsMountList, FsMount) FsMountList; + +bool build_fs_mount_list(FsMountList *mounts, Error **errp); +void free_fs_mount_list(FsMountList *mounts); +#endif /* CONFIG_FSFREEZE || CONFIG_FSTRIM */ + +#if defined(CONFIG_FSFREEZE) +int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints, + strList *mountpoints, + FsMountList mounts, + Error **errp); +int qmp_guest_fsfreeze_do_thaw(Error **errp); +#endif /* CONFIG_FSFREEZE */ + +#ifdef HAVE_GETIFADDRS +#include +bool guest_get_hw_addr(struct ifaddrs *ifa, unsigned char *buf, + bool *obtained, Error **errp); +#endif typedef struct GuestFileHandle GuestFileHandle; diff --git a/qga/commands-linux.c b/qga/commands-linux.c new file mode 100644 index 0000000000..214e408fcd --- /dev/null +++ b/qga/commands-linux.c @@ -0,0 +1,286 @@ +/* + * QEMU Guest Agent Linux-specific command implementations + * + * Copyright IBM Corp. 2011 + * + * Authors: + * Michael Roth + * Michal Privoznik + * + * 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/error.h" +#include "commands-common.h" +#include "cutils.h" +#include +#include + +#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) +static int dev_major_minor(const char *devpath, + unsigned int *devmajor, unsigned int *devminor) +{ + struct stat st; + + *devmajor = 0; + *devminor = 0; + + if (stat(devpath, &st) < 0) { + slog("failed to stat device file '%s': %s", devpath, strerror(errno)); + return -1; + } + if (S_ISDIR(st.st_mode)) { + /* It is bind mount */ + return -2; + } + if (S_ISBLK(st.st_mode)) { + *devmajor = major(st.st_rdev); + *devminor = minor(st.st_rdev); + return 0; + } + return -1; +} + +static bool build_fs_mount_list_from_mtab(FsMountList *mounts, Error **errp) +{ + struct mntent *ment; + FsMount *mount; + char const *mtab = "/proc/self/mounts"; + FILE *fp; + unsigned int devmajor, devminor; + + fp = setmntent(mtab, "r"); + if (!fp) { + error_setg(errp, "failed to open mtab file: '%s'", mtab); + return false; + } + + while ((ment = getmntent(fp))) { + /* + * An entry which device name doesn't start with a '/' is + * either a dummy file system or a network file system. + * Add special handling for smbfs and cifs as is done by + * coreutils as well. + */ + if ((ment->mnt_fsname[0] != '/') || + (strcmp(ment->mnt_type, "smbfs") == 0) || + (strcmp(ment->mnt_type, "cifs") == 0)) { + continue; + } + if (dev_major_minor(ment->mnt_fsname, &devmajor, &devminor) == -2) { + /* Skip bind mounts */ + continue; + } + + mount = g_new0(FsMount, 1); + mount->dirname = g_strdup(ment->mnt_dir); + mount->devtype = g_strdup(ment->mnt_type); + mount->devmajor = devmajor; + mount->devminor = devminor; + + QTAILQ_INSERT_TAIL(mounts, mount, next); + } + + endmntent(fp); + return true; +} + +static void decode_mntname(char *name, int len) +{ + int i, j = 0; + for (i = 0; i <= len; i++) { + if (name[i] != '\\') { + name[j++] = name[i]; + } else if (name[i + 1] == '\\') { + name[j++] = '\\'; + i++; + } else if (name[i + 1] >= '0' && name[i + 1] <= '3' && + name[i + 2] >= '0' && name[i + 2] <= '7' && + name[i + 3] >= '0' && name[i + 3] <= '7') { + name[j++] = (name[i + 1] - '0') * 64 + + (name[i + 2] - '0') * 8 + + (name[i + 3] - '0'); + i += 3; + } else { + name[j++] = name[i]; + } + } +} + +/* + * Walk the mount table and build a list of local file systems + */ +bool build_fs_mount_list(FsMountList *mounts, Error **errp) +{ + FsMount *mount; + char const *mountinfo = "/proc/self/mountinfo"; + FILE *fp; + char *line = NULL, *dash; + size_t n; + char check; + unsigned int devmajor, devminor; + int ret, dir_s, dir_e, type_s, type_e, dev_s, dev_e; + + fp = fopen(mountinfo, "r"); + if (!fp) { + return build_fs_mount_list_from_mtab(mounts, errp); + } + + while (getline(&line, &n, fp) != -1) { + ret = sscanf(line, "%*u %*u %u:%u %*s %n%*s%n%c", + &devmajor, &devminor, &dir_s, &dir_e, &check); + if (ret < 3) { + continue; + } + dash = strstr(line + dir_e, " - "); + if (!dash) { + continue; + } + ret = sscanf(dash, " - %n%*s%n %n%*s%n%c", + &type_s, &type_e, &dev_s, &dev_e, &check); + if (ret < 1) { + continue; + } + line[dir_e] = 0; + dash[type_e] = 0; + dash[dev_e] = 0; + decode_mntname(line + dir_s, dir_e - dir_s); + decode_mntname(dash + dev_s, dev_e - dev_s); + if (devmajor == 0) { + /* btrfs reports major number = 0 */ + if (strcmp("btrfs", dash + type_s) != 0 || + dev_major_minor(dash + dev_s, &devmajor, &devminor) < 0) { + continue; + } + } + + mount = g_new0(FsMount, 1); + mount->dirname = g_strdup(line + dir_s); + mount->devtype = g_strdup(dash + type_s); + mount->devmajor = devmajor; + mount->devminor = devminor; + + QTAILQ_INSERT_TAIL(mounts, mount, next); + } + free(line); + + fclose(fp); + return true; +} +#endif /* CONFIG_FSFREEZE || CONFIG_FSTRIM */ + +#ifdef CONFIG_FSFREEZE +/* + * Walk list of mounted file systems in the guest, and freeze the ones which + * are real local file systems. + */ +int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints, + strList *mountpoints, + FsMountList mounts, + Error **errp) +{ + struct FsMount *mount; + strList *list; + int fd, ret, i = 0; + + QTAILQ_FOREACH_REVERSE(mount, &mounts, next) { + /* To issue fsfreeze in the reverse order of mounts, check if the + * mount is listed in the list here */ + if (has_mountpoints) { + for (list = mountpoints; list; list = list->next) { + if (strcmp(list->value, mount->dirname) == 0) { + break; + } + } + if (!list) { + continue; + } + } + + fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0); + if (fd == -1) { + error_setg_errno(errp, errno, "failed to open %s", mount->dirname); + return -1; + } + + /* we try to cull filesystems we know won't work in advance, but other + * filesystems may not implement fsfreeze for less obvious reasons. + * these will report EOPNOTSUPP. we simply ignore these when tallying + * the number of frozen filesystems. + * if a filesystem is mounted more than once (aka bind mount) a + * consecutive attempt to freeze an already frozen filesystem will + * return EBUSY. + * + * any other error means a failure to freeze a filesystem we + * expect to be freezable, so return an error in those cases + * and return system to thawed state. + */ + ret = ioctl(fd, FIFREEZE); + if (ret == -1) { + if (errno != EOPNOTSUPP && errno != EBUSY) { + error_setg_errno(errp, errno, "failed to freeze %s", + mount->dirname); + close(fd); + return -1; + } + } else { + i++; + } + close(fd); + } + return i; +} + +int qmp_guest_fsfreeze_do_thaw(Error **errp) +{ + int ret; + FsMountList mounts; + FsMount *mount; + int fd, i = 0, logged; + Error *local_err = NULL; + + QTAILQ_INIT(&mounts); + if (!build_fs_mount_list(&mounts, &local_err)) { + error_propagate(errp, local_err); + return -1; + } + + QTAILQ_FOREACH(mount, &mounts, next) { + logged = false; + fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0); + if (fd == -1) { + continue; + } + /* we have no way of knowing whether a filesystem was actually unfrozen + * as a result of a successful call to FITHAW, only that if an error + * was returned the filesystem was *not* unfrozen by that particular + * call. + * + * since multiple preceding FIFREEZEs require multiple calls to FITHAW + * to unfreeze, continuing issuing FITHAW until an error is returned, + * in which case either the filesystem is in an unfreezable state, or, + * more likely, it was thawed previously (and remains so afterward). + * + * also, since the most recent successful call is the one that did + * the actual unfreeze, we can use this to provide an accurate count + * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which + * may * be useful for determining whether a filesystem was unfrozen + * during the freeze/thaw phase by a process other than qemu-ga. + */ + do { + ret = ioctl(fd, FITHAW); + if (ret == 0 && !logged) { + i++; + logged = true; + } + } while (ret == 0); + close(fd); + } + + free_fs_mount_list(&mounts); + + return i; +} +#endif /* CONFIG_FSFREEZE */ diff --git a/qga/commands-posix-ssh.c b/qga/commands-posix-ssh.c index f3a580b8cc..236f80de44 100644 --- a/qga/commands-posix-ssh.c +++ b/qga/commands-posix-ssh.c @@ -382,7 +382,7 @@ test_add_keys(void) &err); g_assert(err == NULL); - /* key2 came first, and should'nt be duplicated */ + /* key2 came first, and shouldn't be duplicated */ test_authorized_keys_equal("algo key2 comments\n" "algo key1 comments"); } diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 0469dc409d..26008db497 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -16,11 +16,9 @@ #include #include #include -#include "guest-agent-core.h" #include "qga-qapi-commands.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" -#include "qemu/queue.h" #include "qemu/host-utils.h" #include "qemu/sockets.h" #include "qemu/base64.h" @@ -35,28 +33,27 @@ #if defined(__linux__) #include -#include #include #include #ifdef CONFIG_LIBUDEV #include #endif - -#ifdef FIFREEZE -#define CONFIG_FSFREEZE -#endif -#ifdef FITRIM -#define CONFIG_FSTRIM -#endif #endif #ifdef HAVE_GETIFADDRS #include #include #include -#include -#include +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(CONFIG_SOLARIS) +#include +#include +#if !defined(ETHER_ADDR_LEN) && defined(ETHERADDRL) +#define ETHER_ADDR_LEN ETHERADDRL +#endif +#else +#include +#endif #ifdef CONFIG_SOLARIS #include #endif @@ -68,9 +65,7 @@ static void ga_wait_child(pid_t pid, int *status, Error **errp) *status = 0; - do { - rpid = waitpid(pid, status, 0); - } while (rpid == -1 && errno == EINTR); + rpid = RETRY_ON_EINTR(waitpid(pid, status, 0)); if (rpid == -1) { error_setg_errno(errp, errno, "failed to wait for child (pid: %d)", @@ -81,7 +76,7 @@ static void ga_wait_child(pid_t pid, int *status, Error **errp) g_assert(rpid == pid); } -void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp) +void qmp_guest_shutdown(const char *mode, Error **errp) { const char *shutdown_flag; Error *local_err = NULL; @@ -92,6 +87,10 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp) const char *powerdown_flag = "-i5"; const char *halt_flag = "-i0"; const char *reboot_flag = "-i6"; +#elif defined(CONFIG_BSD) + const char *powerdown_flag = "-p"; + const char *halt_flag = "-h"; + const char *reboot_flag = "-r"; #else const char *powerdown_flag = "-P"; const char *halt_flag = "-H"; @@ -99,7 +98,7 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp) #endif slog("guest-shutdown called, mode: %s", mode); - if (!has_mode || strcmp(mode, "powerdown") == 0) { + if (!mode || strcmp(mode, "powerdown") == 0) { shutdown_flag = powerdown_flag; } else if (strcmp(mode, "halt") == 0) { shutdown_flag = halt_flag; @@ -122,6 +121,9 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp) #ifdef CONFIG_SOLARIS execl("/sbin/shutdown", "shutdown", shutdown_flag, "-g0", "-y", "hypervisor initiated shutdown", (char *)NULL); +#elif defined(CONFIG_BSD) + execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0", + "hypervisor initiated shutdown", (char *)NULL); #else execl("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0", "hypervisor initiated shutdown", (char *)NULL); @@ -407,14 +409,14 @@ end: return f; } -int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, +int64_t qmp_guest_file_open(const char *path, const char *mode, Error **errp) { FILE *fh; Error *local_err = NULL; int64_t handle; - if (!has_mode) { + if (!mode) { mode = "r"; } slog("guest-file-open called, filepath: %s, mode: %s", path, mode); @@ -617,20 +619,8 @@ void qmp_guest_file_flush(int64_t handle, Error **errp) } } -/* linux-specific implementations. avoid this if at all possible. */ -#if defined(__linux__) - #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) -typedef struct FsMount { - char *dirname; - char *devtype; - unsigned int devmajor, devminor; - QTAILQ_ENTRY(FsMount) next; -} FsMount; - -typedef QTAILQ_HEAD(FsMountList, FsMount) FsMountList; - -static void free_fs_mount_list(FsMountList *mounts) +void free_fs_mount_list(FsMountList *mounts) { FsMount *mount, *temp; @@ -645,159 +635,158 @@ static void free_fs_mount_list(FsMountList *mounts) g_free(mount); } } +#endif -static int dev_major_minor(const char *devpath, - unsigned int *devmajor, unsigned int *devminor) +#if defined(CONFIG_FSFREEZE) +typedef enum { + FSFREEZE_HOOK_THAW = 0, + FSFREEZE_HOOK_FREEZE, +} FsfreezeHookArg; + +static const char *fsfreeze_hook_arg_string[] = { + "thaw", + "freeze", +}; + +static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp) { - struct stat st; + int status; + pid_t pid; + const char *hook; + const char *arg_str = fsfreeze_hook_arg_string[arg]; + Error *local_err = NULL; - *devmajor = 0; - *devminor = 0; + hook = ga_fsfreeze_hook(ga_state); + if (!hook) { + return; + } + if (access(hook, X_OK) != 0) { + error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook); + return; + } - if (stat(devpath, &st) < 0) { - slog("failed to stat device file '%s': %s", devpath, strerror(errno)); - return -1; + slog("executing fsfreeze hook with arg '%s'", arg_str); + pid = fork(); + if (pid == 0) { + setsid(); + reopen_fd_to_null(0); + reopen_fd_to_null(1); + reopen_fd_to_null(2); + + execl(hook, hook, arg_str, NULL); + _exit(EXIT_FAILURE); + } else if (pid < 0) { + error_setg_errno(errp, errno, "failed to create child process"); + return; } - if (S_ISDIR(st.st_mode)) { - /* It is bind mount */ - return -2; + + ga_wait_child(pid, &status, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; } - if (S_ISBLK(st.st_mode)) { - *devmajor = major(st.st_rdev); - *devminor = minor(st.st_rdev); - return 0; + + if (!WIFEXITED(status)) { + error_setg(errp, "fsfreeze hook has terminated abnormally"); + return; + } + + status = WEXITSTATUS(status); + if (status) { + error_setg(errp, "fsfreeze hook has failed with status %d", status); + return; } - return -1; } /* - * Walk the mount table and build a list of local file systems + * Return status of freeze/thaw */ -static bool build_fs_mount_list_from_mtab(FsMountList *mounts, Error **errp) +GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) { - struct mntent *ment; - FsMount *mount; - char const *mtab = "/proc/self/mounts"; - FILE *fp; - unsigned int devmajor, devminor; - - fp = setmntent(mtab, "r"); - if (!fp) { - error_setg(errp, "failed to open mtab file: '%s'", mtab); - return false; + if (ga_is_frozen(ga_state)) { + return GUEST_FSFREEZE_STATUS_FROZEN; } - while ((ment = getmntent(fp))) { - /* - * An entry which device name doesn't start with a '/' is - * either a dummy file system or a network file system. - * Add special handling for smbfs and cifs as is done by - * coreutils as well. - */ - if ((ment->mnt_fsname[0] != '/') || - (strcmp(ment->mnt_type, "smbfs") == 0) || - (strcmp(ment->mnt_type, "cifs") == 0)) { - continue; - } - if (dev_major_minor(ment->mnt_fsname, &devmajor, &devminor) == -2) { - /* Skip bind mounts */ - continue; - } - - mount = g_new0(FsMount, 1); - mount->dirname = g_strdup(ment->mnt_dir); - mount->devtype = g_strdup(ment->mnt_type); - mount->devmajor = devmajor; - mount->devminor = devminor; - - QTAILQ_INSERT_TAIL(mounts, mount, next); - } - - endmntent(fp); - return true; + return GUEST_FSFREEZE_STATUS_THAWED; } -static void decode_mntname(char *name, int len) +int64_t qmp_guest_fsfreeze_freeze(Error **errp) { - int i, j = 0; - for (i = 0; i <= len; i++) { - if (name[i] != '\\') { - name[j++] = name[i]; - } else if (name[i + 1] == '\\') { - name[j++] = '\\'; - i++; - } else if (name[i + 1] >= '0' && name[i + 1] <= '3' && - name[i + 2] >= '0' && name[i + 2] <= '7' && - name[i + 3] >= '0' && name[i + 3] <= '7') { - name[j++] = (name[i + 1] - '0') * 64 + - (name[i + 2] - '0') * 8 + - (name[i + 3] - '0'); - i += 3; - } else { - name[j++] = name[i]; - } - } + return qmp_guest_fsfreeze_freeze_list(false, NULL, errp); } -static bool build_fs_mount_list(FsMountList *mounts, Error **errp) +int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, + strList *mountpoints, + Error **errp) { - FsMount *mount; - char const *mountinfo = "/proc/self/mountinfo"; - FILE *fp; - char *line = NULL, *dash; - size_t n; - char check; - unsigned int devmajor, devminor; - int ret, dir_s, dir_e, type_s, type_e, dev_s, dev_e; + int ret; + FsMountList mounts; + Error *local_err = NULL; - fp = fopen(mountinfo, "r"); - if (!fp) { - return build_fs_mount_list_from_mtab(mounts, errp); + slog("guest-fsfreeze called"); + + execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -1; } - while (getline(&line, &n, fp) != -1) { - ret = sscanf(line, "%*u %*u %u:%u %*s %n%*s%n%c", - &devmajor, &devminor, &dir_s, &dir_e, &check); - if (ret < 3) { - continue; - } - dash = strstr(line + dir_e, " - "); - if (!dash) { - continue; - } - ret = sscanf(dash, " - %n%*s%n %n%*s%n%c", - &type_s, &type_e, &dev_s, &dev_e, &check); - if (ret < 1) { - continue; - } - line[dir_e] = 0; - dash[type_e] = 0; - dash[dev_e] = 0; - decode_mntname(line + dir_s, dir_e - dir_s); - decode_mntname(dash + dev_s, dev_e - dev_s); - if (devmajor == 0) { - /* btrfs reports major number = 0 */ - if (strcmp("btrfs", dash + type_s) != 0 || - dev_major_minor(dash + dev_s, &devmajor, &devminor) < 0) { - continue; - } - } - - mount = g_new0(FsMount, 1); - mount->dirname = g_strdup(line + dir_s); - mount->devtype = g_strdup(dash + type_s); - mount->devmajor = devmajor; - mount->devminor = devminor; - - QTAILQ_INSERT_TAIL(mounts, mount, next); + QTAILQ_INIT(&mounts); + if (!build_fs_mount_list(&mounts, &local_err)) { + error_propagate(errp, local_err); + return -1; } - free(line); - fclose(fp); - return true; + /* cannot risk guest agent blocking itself on a write in this state */ + ga_set_frozen(ga_state); + + ret = qmp_guest_fsfreeze_do_freeze_list(has_mountpoints, mountpoints, + mounts, errp); + + free_fs_mount_list(&mounts); + /* We may not issue any FIFREEZE here. + * Just unset ga_state here and ready for the next call. + */ + if (ret == 0) { + ga_unset_frozen(ga_state); + } else if (ret < 0) { + qmp_guest_fsfreeze_thaw(NULL); + } + return ret; +} + +int64_t qmp_guest_fsfreeze_thaw(Error **errp) +{ + int ret; + + ret = qmp_guest_fsfreeze_do_thaw(errp); + if (ret >= 0) { + ga_unset_frozen(ga_state); + execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp); + } else { + ret = 0; + } + + return ret; +} + +static void guest_fsfreeze_cleanup(void) +{ + Error *err = NULL; + + if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { + qmp_guest_fsfreeze_thaw(&err); + if (err) { + slog("failed to clean up frozen filesystems: %s", + error_get_pretty(err)); + error_free(err); + } + } } #endif +/* linux-specific implementations. avoid this if at all possible. */ +#if defined(__linux__) #if defined(CONFIG_FSFREEZE) static char *get_pci_driver(char const *syspath, int pathlen, Error **errp) @@ -893,7 +882,9 @@ static bool build_guest_fsinfo_for_pci_dev(char const *syspath, g_str_equal(driver, "sym53c8xx") || g_str_equal(driver, "virtio-pci") || g_str_equal(driver, "ahci") || - g_str_equal(driver, "nvme"))) { + g_str_equal(driver, "nvme") || + g_str_equal(driver, "xhci_hcd") || + g_str_equal(driver, "ehci-pci"))) { break; } @@ -990,6 +981,8 @@ static bool build_guest_fsinfo_for_pci_dev(char const *syspath, } } else if (strcmp(driver, "nvme") == 0) { disk->bus_type = GUEST_DISK_BUS_TYPE_NVME; + } else if (strcmp(driver, "ehci-pci") == 0 || strcmp(driver, "xhci_hcd") == 0) { + disk->bus_type = GUEST_DISK_BUS_TYPE_USB; } else { g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath); goto cleanup; @@ -1053,7 +1046,6 @@ static bool build_guest_fsinfo_for_ccw_dev(char const *syspath, return false; } - disk->has_ccw_address = true; disk->ccw_address = g_new0(GuestCCWAddress, 1); disk->ccw_address->cssid = cssid; disk->ccw_address->ssid = ssid; @@ -1100,12 +1092,10 @@ static void build_guest_fsinfo_for_real_device(char const *syspath, devnode = udev_device_get_devnode(udevice); if (devnode != NULL) { disk->dev = g_strdup(devnode); - disk->has_dev = true; } serial = udev_device_get_property_value(udevice, "ID_SERIAL"); if (serial != NULL && *serial != 0) { disk->serial = g_strdup(serial); - disk->has_serial = true; } } @@ -1124,7 +1114,7 @@ static void build_guest_fsinfo_for_real_device(char const *syspath, has_hwinf = false; } - if (has_hwinf || disk->has_dev || disk->has_serial) { + if (has_hwinf || disk->dev || disk->serial) { QAPI_LIST_PREPEND(fs->disk, disk); } else { qapi_free_GuestDiskAddress(disk); @@ -1207,7 +1197,15 @@ static void build_guest_fsinfo_for_device(char const *devpath, syspath = realpath(devpath, NULL); if (!syspath) { - error_setg_errno(errp, errno, "realpath(\"%s\")", devpath); + if (errno != ENOENT) { + error_setg_errno(errp, errno, "realpath(\"%s\")", devpath); + return; + } + + /* ENOENT: This devpath may not exist because of container config */ + if (!fs->name) { + fs->name = g_path_get_basename(devpath); + } return; } @@ -1419,7 +1417,6 @@ static void get_nvme_smart(GuestDiskInfo *disk) return; } - disk->has_smart = true; disk->smart = g_new0(GuestDiskSmart, 1); disk->smart->type = GUEST_DISK_BUS_TYPE_NVME; @@ -1457,7 +1454,7 @@ static void get_nvme_smart(GuestDiskInfo *disk) static void get_disk_smart(GuestDiskInfo *disk) { - if (disk->has_address + if (disk->address && (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) { get_nvme_smart(disk); } @@ -1510,7 +1507,6 @@ GuestDiskInfoList *qmp_guest_get_disks(Error **errp) disk->name = dev_name; disk->partition = false; disk->alias = get_alias_for_syspath(disk_dir); - disk->has_alias = (disk->alias != NULL); QAPI_LIST_PREPEND(ret, disk); /* Get address for non-virtual devices */ @@ -1530,8 +1526,6 @@ GuestDiskInfoList *qmp_guest_get_disks(Error **errp) error_get_pretty(local_err)); error_free(local_err); local_err = NULL; - } else if (disk->address != NULL) { - disk->has_address = true; } } @@ -1613,248 +1607,6 @@ GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) free_fs_mount_list(&mounts); return ret; } - - -typedef enum { - FSFREEZE_HOOK_THAW = 0, - FSFREEZE_HOOK_FREEZE, -} FsfreezeHookArg; - -static const char *fsfreeze_hook_arg_string[] = { - "thaw", - "freeze", -}; - -static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp) -{ - int status; - pid_t pid; - const char *hook; - const char *arg_str = fsfreeze_hook_arg_string[arg]; - Error *local_err = NULL; - - hook = ga_fsfreeze_hook(ga_state); - if (!hook) { - return; - } - if (access(hook, X_OK) != 0) { - error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook); - return; - } - - slog("executing fsfreeze hook with arg '%s'", arg_str); - pid = fork(); - if (pid == 0) { - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - - execl(hook, hook, arg_str, NULL); - _exit(EXIT_FAILURE); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - return; - } - - ga_wait_child(pid, &status, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - if (!WIFEXITED(status)) { - error_setg(errp, "fsfreeze hook has terminated abnormally"); - return; - } - - status = WEXITSTATUS(status); - if (status) { - error_setg(errp, "fsfreeze hook has failed with status %d", status); - return; - } -} - -/* - * Return status of freeze/thaw - */ -GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) -{ - if (ga_is_frozen(ga_state)) { - return GUEST_FSFREEZE_STATUS_FROZEN; - } - - return GUEST_FSFREEZE_STATUS_THAWED; -} - -int64_t qmp_guest_fsfreeze_freeze(Error **errp) -{ - return qmp_guest_fsfreeze_freeze_list(false, NULL, errp); -} - -/* - * Walk list of mounted file systems in the guest, and freeze the ones which - * are real local file systems. - */ -int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, - strList *mountpoints, - Error **errp) -{ - int ret = 0, i = 0; - strList *list; - FsMountList mounts; - struct FsMount *mount; - Error *local_err = NULL; - int fd; - - slog("guest-fsfreeze called"); - - execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return -1; - } - - QTAILQ_INIT(&mounts); - if (!build_fs_mount_list(&mounts, &local_err)) { - error_propagate(errp, local_err); - return -1; - } - - /* cannot risk guest agent blocking itself on a write in this state */ - ga_set_frozen(ga_state); - - QTAILQ_FOREACH_REVERSE(mount, &mounts, next) { - /* To issue fsfreeze in the reverse order of mounts, check if the - * mount is listed in the list here */ - if (has_mountpoints) { - for (list = mountpoints; list; list = list->next) { - if (strcmp(list->value, mount->dirname) == 0) { - break; - } - } - if (!list) { - continue; - } - } - - fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0); - if (fd == -1) { - error_setg_errno(errp, errno, "failed to open %s", mount->dirname); - goto error; - } - - /* we try to cull filesystems we know won't work in advance, but other - * filesystems may not implement fsfreeze for less obvious reasons. - * these will report EOPNOTSUPP. we simply ignore these when tallying - * the number of frozen filesystems. - * if a filesystem is mounted more than once (aka bind mount) a - * consecutive attempt to freeze an already frozen filesystem will - * return EBUSY. - * - * any other error means a failure to freeze a filesystem we - * expect to be freezable, so return an error in those cases - * and return system to thawed state. - */ - ret = ioctl(fd, FIFREEZE); - if (ret == -1) { - if (errno != EOPNOTSUPP && errno != EBUSY) { - error_setg_errno(errp, errno, "failed to freeze %s", - mount->dirname); - close(fd); - goto error; - } - } else { - i++; - } - close(fd); - } - - free_fs_mount_list(&mounts); - /* We may not issue any FIFREEZE here. - * Just unset ga_state here and ready for the next call. - */ - if (i == 0) { - ga_unset_frozen(ga_state); - } - return i; - -error: - free_fs_mount_list(&mounts); - qmp_guest_fsfreeze_thaw(NULL); - return 0; -} - -/* - * Walk list of frozen file systems in the guest, and thaw them. - */ -int64_t qmp_guest_fsfreeze_thaw(Error **errp) -{ - int ret; - FsMountList mounts; - FsMount *mount; - int fd, i = 0, logged; - Error *local_err = NULL; - - QTAILQ_INIT(&mounts); - if (!build_fs_mount_list(&mounts, &local_err)) { - error_propagate(errp, local_err); - return 0; - } - - QTAILQ_FOREACH(mount, &mounts, next) { - logged = false; - fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0); - if (fd == -1) { - continue; - } - /* we have no way of knowing whether a filesystem was actually unfrozen - * as a result of a successful call to FITHAW, only that if an error - * was returned the filesystem was *not* unfrozen by that particular - * call. - * - * since multiple preceding FIFREEZEs require multiple calls to FITHAW - * to unfreeze, continuing issuing FITHAW until an error is returned, - * in which case either the filesystem is in an unfreezable state, or, - * more likely, it was thawed previously (and remains so afterward). - * - * also, since the most recent successful call is the one that did - * the actual unfreeze, we can use this to provide an accurate count - * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which - * may * be useful for determining whether a filesystem was unfrozen - * during the freeze/thaw phase by a process other than qemu-ga. - */ - do { - ret = ioctl(fd, FITHAW); - if (ret == 0 && !logged) { - i++; - logged = true; - } - } while (ret == 0); - close(fd); - } - - ga_unset_frozen(ga_state); - free_fs_mount_list(&mounts); - - execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp); - - return i; -} - -static void guest_fsfreeze_cleanup(void) -{ - Error *err = NULL; - - if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { - qmp_guest_fsfreeze_thaw(&err); - if (err) { - slog("failed to clean up frozen filesystems: %s", - error_get_pretty(err)); - error_free(err); - } - } -} #endif /* CONFIG_FSFREEZE */ #if defined(CONFIG_FSTRIM) @@ -1891,7 +1643,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) if (fd == -1) { result->error = g_strdup_printf("failed to open: %s", strerror(errno)); - result->has_error = true; continue; } @@ -1906,7 +1657,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) r.minlen = has_minimum ? minimum : 0; ret = ioctl(fd, FITRIM, &r); if (ret == -1) { - result->has_error = true; if (errno == ENOTTY || errno == EOPNOTSUPP) { result->error = g_strdup("trim not supported"); } else { @@ -2175,10 +1925,10 @@ static void guest_suspend(SuspendMode mode, Error **errp) if (systemd_supports_mode(mode, &local_err)) { mode_supported = true; systemd_suspend(mode, &local_err); - } - if (!local_err) { - return; + if (!local_err) { + return; + } } error_free(local_err); @@ -2187,10 +1937,10 @@ static void guest_suspend(SuspendMode mode, Error **errp) if (pmutils_supports_mode(mode, &local_err)) { mode_supported = true; pmutils_suspend(mode, &local_err); - } - if (!local_err) { - return; + if (!local_err) { + return; + } } error_free(local_err); @@ -2364,7 +2114,9 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) return processed; } +#endif /* __linux__ */ +#if defined(__linux__) || defined(__FreeBSD__) void qmp_guest_set_user_password(const char *username, const char *password, bool crypted, @@ -2398,10 +2150,15 @@ void qmp_guest_set_user_password(const char *username, goto out; } +#ifdef __FreeBSD__ + chpasswddata = g_strdup(rawpasswddata); + passwd_path = g_find_program_in_path("pw"); +#else chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata); - chpasswdlen = strlen(chpasswddata); - passwd_path = g_find_program_in_path("chpasswd"); +#endif + + chpasswdlen = strlen(chpasswddata); if (!passwd_path) { error_setg(errp, "cannot find 'passwd' program in PATH"); @@ -2422,11 +2179,17 @@ void qmp_guest_set_user_password(const char *username, reopen_fd_to_null(1); reopen_fd_to_null(2); +#ifdef __FreeBSD__ + const char *h_arg; + h_arg = (crypted) ? "-H" : "-h"; + execl(passwd_path, "pw", "usermod", "-n", username, h_arg, "0", NULL); +#else if (crypted) { execl(passwd_path, "chpasswd", "-e", NULL); } else { execl(passwd_path, "chpasswd", NULL); } +#endif _exit(EXIT_FAILURE); } else if (pid < 0) { error_setg_errno(errp, errno, "failed to create child process"); @@ -2469,7 +2232,17 @@ out: close(datafd[1]); } } +#else /* __linux__ || __FreeBSD__ */ +void qmp_guest_set_user_password(const char *username, + const char *password, + bool crypted, + Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); +} +#endif /* __linux__ || __FreeBSD__ */ +#ifdef __linux__ static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf, int size, Error **errp) { @@ -2893,6 +2666,90 @@ GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) return guest_get_diskstats(errp); } +GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) +{ + GuestCpuStatsList *head = NULL, **tail = &head; + const char *cpustats = "/proc/stat"; + int clk_tck = sysconf(_SC_CLK_TCK); + FILE *fp; + size_t n; + char *line = NULL; + + fp = fopen(cpustats, "r"); + if (fp == NULL) { + error_setg_errno(errp, errno, "open(\"%s\")", cpustats); + return NULL; + } + + while (getline(&line, &n, fp) != -1) { + GuestCpuStats *cpustat = NULL; + GuestLinuxCpuStats *linuxcpustat; + int i; + unsigned long user, system, idle, iowait, irq, softirq, steal, guest; + unsigned long nice, guest_nice; + char name[64]; + + i = sscanf(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", + name, &user, &nice, &system, &idle, &iowait, &irq, &softirq, + &steal, &guest, &guest_nice); + + /* drop "cpu 1 2 3 ...", get "cpuX 1 2 3 ..." only */ + if ((i == EOF) || strncmp(name, "cpu", 3) || (name[3] == '\0')) { + continue; + } + + if (i < 5) { + slog("Parsing cpu stat from %s failed, see \"man proc\"", cpustats); + break; + } + + cpustat = g_new0(GuestCpuStats, 1); + cpustat->type = GUEST_CPU_STATS_TYPE_LINUX; + + linuxcpustat = &cpustat->u.q_linux; + linuxcpustat->cpu = atoi(&name[3]); + linuxcpustat->user = user * 1000 / clk_tck; + linuxcpustat->nice = nice * 1000 / clk_tck; + linuxcpustat->system = system * 1000 / clk_tck; + linuxcpustat->idle = idle * 1000 / clk_tck; + + if (i > 5) { + linuxcpustat->has_iowait = true; + linuxcpustat->iowait = iowait * 1000 / clk_tck; + } + + if (i > 6) { + linuxcpustat->has_irq = true; + linuxcpustat->irq = irq * 1000 / clk_tck; + linuxcpustat->has_softirq = true; + linuxcpustat->softirq = softirq * 1000 / clk_tck; + } + + if (i > 8) { + linuxcpustat->has_steal = true; + linuxcpustat->steal = steal * 1000 / clk_tck; + } + + if (i > 9) { + linuxcpustat->has_guest = true; + linuxcpustat->guest = guest * 1000 / clk_tck; + } + + if (i > 10) { + linuxcpustat->has_guest = true; + linuxcpustat->guest = guest * 1000 / clk_tck; + linuxcpustat->has_guestnice = true; + linuxcpustat->guestnice = guest_nice * 1000 / clk_tck; + } + + QAPI_LIST_APPEND(tail, cpustat); + } + + free(line); + fclose(fp); + return head; +} + #else /* defined(__linux__) */ void qmp_guest_suspend_disk(Error **errp) @@ -2922,14 +2779,6 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) return -1; } -void qmp_guest_set_user_password(const char *username, - const char *password, - bool crypted, - Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); -} - GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) { error_setg(errp, QERR_UNSUPPORTED); @@ -3032,6 +2881,57 @@ static int guest_get_network_stats(const char *name, return -1; } +#ifndef CONFIG_BSD +/* + * Fill "buf" with MAC address by ifaddrs. Pointer buf must point to a + * buffer with ETHER_ADDR_LEN length at least. + * + * Returns false in case of an error, otherwise true. "obtained" argument + * is true if a MAC address was obtained successful, otherwise false. + */ +bool guest_get_hw_addr(struct ifaddrs *ifa, unsigned char *buf, + bool *obtained, Error **errp) +{ + struct ifreq ifr; + int sock; + + *obtained = false; + + /* we haven't obtained HW address yet */ + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock == -1) { + error_setg_errno(errp, errno, "failed to create socket"); + return false; + } + + memset(&ifr, 0, sizeof(ifr)); + pstrcpy(ifr.ifr_name, IF_NAMESIZE, ifa->ifa_name); + if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) { + /* + * We can't get the hw addr of this interface, but that's not a + * fatal error. + */ + if (errno == EADDRNOTAVAIL) { + /* The interface doesn't have a hw addr (e.g. loopback). */ + g_debug("failed to get MAC address of %s: %s", + ifa->ifa_name, strerror(errno)); + } else{ + g_warning("failed to get MAC address of %s: %s", + ifa->ifa_name, strerror(errno)); + } + } else { +#ifdef CONFIG_SOLARIS + memcpy(buf, &ifr.ifr_addr.sa_data, ETHER_ADDR_LEN); +#else + memcpy(buf, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN); +#endif + *obtained = true; + } + close(sock); + return true; +} +#endif /* CONFIG_BSD */ + /* * Build information about guest interfaces */ @@ -3052,9 +2952,8 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) GuestNetworkInterfaceStat *interface_stat = NULL; char addr4[INET_ADDRSTRLEN]; char addr6[INET6_ADDRSTRLEN]; - int sock; - struct ifreq ifr; - unsigned char *mac_addr; + unsigned char mac_addr[ETHER_ADDR_LEN]; + bool obtained; void *p; g_debug("Processing %s interface", ifa->ifa_name); @@ -3068,46 +2967,17 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) QAPI_LIST_APPEND(tail, info); } - if (!info->has_hardware_address) { - /* we haven't obtained HW address yet */ - sock = socket(PF_INET, SOCK_STREAM, 0); - if (sock == -1) { - error_setg_errno(errp, errno, "failed to create socket"); + if (!info->hardware_address) { + if (!guest_get_hw_addr(ifa, mac_addr, &obtained, errp)) { goto error; } - - memset(&ifr, 0, sizeof(ifr)); - pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->name); - if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) { - /* - * We can't get the hw addr of this interface, but that's not a - * fatal error. Don't set info->hardware_address, but keep - * going. - */ - if (errno == EADDRNOTAVAIL) { - /* The interface doesn't have a hw addr (e.g. loopback). */ - g_debug("failed to get MAC address of %s: %s", - ifa->ifa_name, strerror(errno)); - } else{ - g_warning("failed to get MAC address of %s: %s", - ifa->ifa_name, strerror(errno)); - } - - } else { -#ifdef CONFIG_SOLARIS - mac_addr = (unsigned char *) &ifr.ifr_addr.sa_data; -#else - mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data; -#endif + if (obtained) { info->hardware_address = g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x", (int) mac_addr[0], (int) mac_addr[1], (int) mac_addr[2], (int) mac_addr[3], (int) mac_addr[4], (int) mac_addr[5]); - - info->has_hardware_address = true; } - close(sock); } if (ifa->ifa_addr && @@ -3166,14 +3036,12 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) info->has_ip_addresses = true; - if (!info->has_statistics) { + if (!info->statistics) { interface_stat = g_malloc0(sizeof(*interface_stat)); if (guest_get_network_stats(info->name, interface_stat) == -1) { - info->has_statistics = false; g_free(interface_stat); } else { info->statistics = interface_stat; - info->has_statistics = true; } } } @@ -3247,6 +3115,11 @@ GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) return NULL; } +GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} #endif /* CONFIG_FSFREEZE */ @@ -3259,8 +3132,8 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) } #endif -/* add unsupported commands to the blacklist */ -GList *ga_command_blacklist_init(GList *blacklist) +/* add unsupported commands to the list of blocked RPCs */ +GList *ga_command_init_blockedrpcs(GList *blockedrpcs) { #if !defined(__linux__) { @@ -3273,13 +3146,13 @@ GList *ga_command_blacklist_init(GList *blacklist) char **p = (char **)list; while (*p) { - blacklist = g_list_append(blacklist, g_strdup(*p++)); + blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++)); } } #endif #if !defined(HAVE_GETIFADDRS) - blacklist = g_list_append(blacklist, + blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-network-get-interfaces")); #endif @@ -3293,18 +3166,18 @@ GList *ga_command_blacklist_init(GList *blacklist) char **p = (char **)list; while (*p) { - blacklist = g_list_append(blacklist, g_strdup(*p++)); + blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++)); } } #endif #if !defined(CONFIG_FSTRIM) - blacklist = g_list_append(blacklist, g_strdup("guest-fstrim")); + blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim")); #endif - blacklist = g_list_append(blacklist, g_strdup("guest-get-devices")); + blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-get-devices")); - return blacklist; + return blockedrpcs; } /* register init/cleanup routines for stateful command groups */ @@ -3379,7 +3252,7 @@ GuestUserList *qmp_guest_get_users(Error **errp) #endif -/* Replace escaped special characters with theire real values. The replacement +/* Replace escaped special characters with their real values. The replacement * is done in place -- returned value is in the original string. */ static void ga_osrelease_replace_special(gchar *value) @@ -3475,11 +3348,8 @@ GuestOSInfo *qmp_guest_get_osinfo(Error **errp) if (uname(&kinfo) != 0) { error_setg_errno(errp, errno, "uname failed"); } else { - info->has_kernel_version = true; info->kernel_version = g_strdup(kinfo.version); - info->has_kernel_release = true; info->kernel_release = g_strdup(kinfo.release); - info->has_machine = true; info->machine = g_strdup(kinfo.machine); } @@ -3499,7 +3369,6 @@ GuestOSInfo *qmp_guest_get_osinfo(Error **errp) value = g_key_file_get_value(osrelease, "os-release", osfield, NULL); \ if (value != NULL) { \ ga_osrelease_replace_special(value); \ - info->has_ ## field = true; \ info->field = value; \ } \ } while (0) diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 36f94c0f9c..6242737b00 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -193,8 +193,7 @@ static void handle_set_nonblocking(HANDLE fh) SetNamedPipeHandleState(fh, &pipe_state, NULL, NULL); } -int64_t qmp_guest_file_open(const char *path, bool has_mode, - const char *mode, Error **errp) +int64_t qmp_guest_file_open(const char *path, const char *mode, Error **errp) { int64_t fd = -1; HANDLE fh; @@ -206,7 +205,7 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, GError *gerr = NULL; wchar_t *w_path = NULL; - if (!has_mode) { + if (!mode) { mode = "r"; } slog("guest-file-open called, filepath: %s, mode: %s", path, mode); @@ -275,13 +274,12 @@ static void acquire_privilege(const char *name, Error **errp) { HANDLE token = NULL; TOKEN_PRIVILEGES priv; - Error *local_err = NULL; if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) { if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) { - error_setg(&local_err, QERR_QGA_COMMAND_FAILED, + error_setg(errp, QERR_QGA_COMMAND_FAILED, "no luid for requested privilege"); goto out; } @@ -290,13 +288,13 @@ static void acquire_privilege(const char *name, Error **errp) priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0)) { - error_setg(&local_err, QERR_QGA_COMMAND_FAILED, + error_setg(errp, QERR_QGA_COMMAND_FAILED, "unable to acquire requested privilege"); goto out; } } else { - error_setg(&local_err, QERR_QGA_COMMAND_FAILED, + error_setg(errp, QERR_QGA_COMMAND_FAILED, "failed to open privilege token"); } @@ -304,7 +302,6 @@ out: if (token) { CloseHandle(token); } - error_propagate(errp, local_err); } static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque, @@ -317,14 +314,14 @@ static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque, } } -void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp) +void qmp_guest_shutdown(const char *mode, Error **errp) { Error *local_err = NULL; UINT shutdown_flag = EWX_FORCE; slog("guest-shutdown called, mode: %s", mode); - if (!has_mode || strcmp(mode, "powerdown") == 0) { + if (!mode || strcmp(mode, "powerdown") == 0) { shutdown_flag |= EWX_POWEROFF; } else if (strcmp(mode, "halt") == 0) { shutdown_flag |= EWX_SHUTDOWN; @@ -487,15 +484,13 @@ static GuestDiskBusType win2qemu[] = { [BusTypeSata] = GUEST_DISK_BUS_TYPE_SATA, [BusTypeSd] = GUEST_DISK_BUS_TYPE_SD, [BusTypeMmc] = GUEST_DISK_BUS_TYPE_MMC, -#if (_WIN32_WINNT >= 0x0601) [BusTypeVirtual] = GUEST_DISK_BUS_TYPE_VIRTUAL, [BusTypeFileBackedVirtual] = GUEST_DISK_BUS_TYPE_FILE_BACKED_VIRTUAL, /* - * BusTypeSpaces currently is not suported + * BusTypeSpaces currently is not supported */ [BusTypeSpaces] = GUEST_DISK_BUS_TYPE_UNKNOWN, [BusTypeNvme] = GUEST_DISK_BUS_TYPE_NVME, -#endif }; static GuestDiskBusType find_bus_type(STORAGE_BUS_TYPE bus) @@ -506,13 +501,6 @@ static GuestDiskBusType find_bus_type(STORAGE_BUS_TYPE bus) return win2qemu[(int)bus]; } -DEFINE_GUID(GUID_DEVINTERFACE_DISK, - 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, - 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); -DEFINE_GUID(GUID_DEVINTERFACE_STORAGEPORT, - 0x2accfe60L, 0xc130, 0x11d2, 0xb0, 0x82, - 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - static void get_pci_address_for_device(GuestPCIAddress *pci, HDEVINFO dev_info) { @@ -599,6 +587,18 @@ static void get_pci_address_for_device(GuestPCIAddress *pci, } } +static GuestPCIAddress *get_empty_pci_address(void) +{ + GuestPCIAddress *pci = NULL; + + pci = g_malloc0(sizeof(*pci)); + pci->domain = -1; + pci->slot = -1; + pci->function = -1; + pci->bus = -1; + return pci; +} + static GuestPCIAddress *get_pci_info(int number, Error **errp) { HDEVINFO dev_info = INVALID_HANDLE_VALUE; @@ -608,13 +608,7 @@ static GuestPCIAddress *get_pci_info(int number, Error **errp) SP_DEVICE_INTERFACE_DATA dev_iface_data; HANDLE dev_file; int i; - GuestPCIAddress *pci = NULL; - - pci = g_malloc0(sizeof(*pci)); - pci->domain = -1; - pci->slot = -1; - pci->function = -1; - pci->bus = -1; + GuestPCIAddress *pci = get_empty_pci_address(); dev_info = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); @@ -833,7 +827,6 @@ static void get_disk_properties(HANDLE vol_h, GuestDiskAddress *disk, g_debug("serial number \"%s\"", serial); if (*serial != 0) { disk->serial = g_strndup(serial, len); - disk->has_serial = true; } } out_free: @@ -872,10 +865,14 @@ static void get_single_disk_info(int disk_number, * if that doesn't hold since that suggests some other unexpected * breakage */ - disk->pci_controller = get_pci_info(disk_number, &local_err); - if (local_err) { - error_propagate(errp, local_err); - goto err_close; + if (disk->bus_type == GUEST_DISK_BUS_TYPE_USB) { + disk->pci_controller = get_empty_pci_address(); + } else { + disk->pci_controller = get_pci_info(disk_number, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto err_close; + } } if (disk->bus_type == GUEST_DISK_BUS_TYPE_SCSI || disk->bus_type == GUEST_DISK_BUS_TYPE_IDE @@ -938,6 +935,8 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp) DWORD last_err = GetLastError(); if (last_err == ERROR_MORE_DATA) { /* Try once more with big enough buffer */ + size = sizeof(VOLUME_DISK_EXTENTS) + + (sizeof(DISK_EXTENT) * (extents->NumberOfDiskExtents - 1)); g_free(extents); extents = g_malloc0(size); if (!DeviceIoControl( @@ -951,7 +950,6 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp) /* Possibly CD-ROM or a shared drive. Try to pass the volume */ g_debug("volume not on disk"); disk = g_new0(GuestDiskAddress, 1); - disk->has_dev = true; disk->dev = g_strdup(name); get_single_disk_info(0xffffffff, disk, &local_err); if (local_err) { @@ -983,7 +981,6 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp) * See also Naming Files, Paths and Namespaces: * https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#win32-device-namespaces */ - disk->has_dev = true; disk->dev = g_strdup_printf("\\\\.\\PhysicalDrive%lu", extents->Extents[i].DiskNumber); @@ -1078,7 +1075,6 @@ GuestDiskInfoList *qmp_guest_get_disks(Error **errp) g_debug(" number: %lu", sdn.DeviceNumber); address = g_new0(GuestDiskAddress, 1); - address->has_dev = true; address->dev = g_strdup(disk->name); get_single_disk_info(sdn.DeviceNumber, address, &local_err); if (local_err) { @@ -1089,7 +1085,6 @@ GuestDiskInfoList *qmp_guest_get_disks(Error **errp) address = NULL; } else { disk->address = address; - disk->has_address = true; } QAPI_LIST_PREPEND(ret, disk); @@ -1369,7 +1364,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) g_free(uc_path); if (!path) { - res->has_error = true; res->error = g_strdup(gerr->message); g_error_free(gerr); break; @@ -1387,7 +1381,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) if (!g_spawn_sync(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &out /* stdout */, NULL /* stdin */, NULL, &gerr)) { - res->has_error = true; res->error = g_strdup(gerr->message); g_error_free(gerr); } else { @@ -1403,7 +1396,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) if (g_strstr_len(lines[i], -1, "(0x") == NULL) { continue; } - res->has_error = true; res->error = g_strdup(lines[i]); break; } @@ -1683,8 +1675,6 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) (int) mac_addr[0], (int) mac_addr[1], (int) mac_addr[2], (int) mac_addr[3], (int) mac_addr[4], (int) mac_addr[5]); - - info->has_hardware_address = true; } head_addr = NULL; @@ -1713,15 +1703,13 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) info->has_ip_addresses = true; info->ip_addresses = head_addr; } - if (!info->has_statistics) { + if (!info->statistics) { interface_stat = g_malloc0(sizeof(*interface_stat)); - if (guest_get_network_stats(addr->AdapterName, - interface_stat) == -1) { - info->has_statistics = false; + if (guest_get_network_stats(addr->AdapterName, interface_stat) + == -1) { g_free(interface_stat); } else { info->statistics = interface_stat; - info->has_statistics = true; } } } @@ -2005,8 +1993,8 @@ GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) return NULL; } -/* add unsupported commands to the blacklist */ -GList *ga_command_blacklist_init(GList *blacklist) +/* add unsupported commands to the list of blocked RPCs */ +GList *ga_command_init_blockedrpcs(GList *blockedrpcs) { const char *list_unsupported[] = { "guest-suspend-hybrid", @@ -2017,7 +2005,7 @@ GList *ga_command_blacklist_init(GList *blacklist) char **p = (char **)list_unsupported; while (*p) { - blacklist = g_list_append(blacklist, g_strdup(*p++)); + blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++)); } if (!vss_init(true)) { @@ -2028,11 +2016,11 @@ GList *ga_command_blacklist_init(GList *blacklist) p = (char **)list; while (*p) { - blacklist = g_list_append(blacklist, g_strdup(*p++)); + blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++)); } } - return blacklist; + return blockedrpcs; } /* register init/cleanup routines for stateful command groups */ @@ -2113,7 +2101,6 @@ GuestUserList *qmp_guest_get_users(Error **errp) user->user = g_strdup(info->UserName); user->domain = g_strdup(info->Domain); - user->has_domain = true; user->login_time = login_time; @@ -2133,49 +2120,47 @@ GuestUserList *qmp_guest_get_users(Error **errp) typedef struct _ga_matrix_lookup_t { int major; int minor; - char const *version; - char const *version_id; + const char *version; + const char *version_id; } ga_matrix_lookup_t; -static ga_matrix_lookup_t const WIN_VERSION_MATRIX[2][7] = { - { - /* Desktop editions */ - { 5, 0, "Microsoft Windows 2000", "2000"}, - { 5, 1, "Microsoft Windows XP", "xp"}, - { 6, 0, "Microsoft Windows Vista", "vista"}, - { 6, 1, "Microsoft Windows 7" "7"}, - { 6, 2, "Microsoft Windows 8", "8"}, - { 6, 3, "Microsoft Windows 8.1", "8.1"}, - { 0, 0, 0} - },{ - /* Server editions */ - { 5, 2, "Microsoft Windows Server 2003", "2003"}, - { 6, 0, "Microsoft Windows Server 2008", "2008"}, - { 6, 1, "Microsoft Windows Server 2008 R2", "2008r2"}, - { 6, 2, "Microsoft Windows Server 2012", "2012"}, - { 6, 3, "Microsoft Windows Server 2012 R2", "2012r2"}, - { 0, 0, 0}, - { 0, 0, 0} - } +static const ga_matrix_lookup_t WIN_CLIENT_VERSION_MATRIX[] = { + { 5, 0, "Microsoft Windows 2000", "2000"}, + { 5, 1, "Microsoft Windows XP", "xp"}, + { 6, 0, "Microsoft Windows Vista", "vista"}, + { 6, 1, "Microsoft Windows 7" "7"}, + { 6, 2, "Microsoft Windows 8", "8"}, + { 6, 3, "Microsoft Windows 8.1", "8.1"}, + { } +}; + +static const ga_matrix_lookup_t WIN_SERVER_VERSION_MATRIX[] = { + { 5, 2, "Microsoft Windows Server 2003", "2003"}, + { 6, 0, "Microsoft Windows Server 2008", "2008"}, + { 6, 1, "Microsoft Windows Server 2008 R2", "2008r2"}, + { 6, 2, "Microsoft Windows Server 2012", "2012"}, + { 6, 3, "Microsoft Windows Server 2012 R2", "2012r2"}, + { }, }; typedef struct _ga_win_10_0_t { int first_build; - char const *version; - char const *version_id; + const char *version; + const char *version_id; } ga_win_10_0_t; -static ga_win_10_0_t const WIN_10_0_SERVER_VERSION_MATRIX[4] = { +static const ga_win_10_0_t WIN_10_0_SERVER_VERSION_MATRIX[] = { {14393, "Microsoft Windows Server 2016", "2016"}, {17763, "Microsoft Windows Server 2019", "2019"}, {20344, "Microsoft Windows Server 2022", "2022"}, - {0, 0} + {26040, "MIcrosoft Windows Server 2025", "2025"}, + { } }; -static ga_win_10_0_t const WIN_10_0_CLIENT_VERSION_MATRIX[3] = { +static const ga_win_10_0_t WIN_10_0_CLIENT_VERSION_MATRIX[] = { {10240, "Microsoft Windows 10", "10"}, {22000, "Microsoft Windows 11", "11"}, - {0, 0} + { } }; static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp) @@ -2198,16 +2183,17 @@ static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp) return; } -static char *ga_get_win_name(OSVERSIONINFOEXW const *os_version, bool id) +static char *ga_get_win_name(const OSVERSIONINFOEXW *os_version, bool id) { DWORD major = os_version->dwMajorVersion; DWORD minor = os_version->dwMinorVersion; DWORD build = os_version->dwBuildNumber; int tbl_idx = (os_version->wProductType != VER_NT_WORKSTATION); - ga_matrix_lookup_t const *table = WIN_VERSION_MATRIX[tbl_idx]; - ga_win_10_0_t const *win_10_0_table = tbl_idx ? + const ga_matrix_lookup_t *table = tbl_idx ? + WIN_SERVER_VERSION_MATRIX : WIN_CLIENT_VERSION_MATRIX; + const ga_win_10_0_t *win_10_0_table = tbl_idx ? WIN_10_0_SERVER_VERSION_MATRIX : WIN_10_0_CLIENT_VERSION_MATRIX; - ga_win_10_0_t const *win_10_0_version = NULL; + const ga_win_10_0_t *win_10_0_version = NULL; while (table->version != NULL) { if (major == 10 && minor == 0) { while (win_10_0_table->version != NULL) { @@ -2267,7 +2253,7 @@ static char *ga_get_win_product_name(Error **errp) } } if (err != ERROR_SUCCESS) { - error_setg_win32(errp, err, "failed to retrive ProductName"); + error_setg_win32(errp, err, "failed to retrieve ProductName"); goto fail; } @@ -2332,29 +2318,19 @@ GuestOSInfo *qmp_guest_get_osinfo(Error **errp) info = g_new0(GuestOSInfo, 1); - info->has_kernel_version = true; info->kernel_version = g_strdup_printf("%lu.%lu", os_version.dwMajorVersion, os_version.dwMinorVersion); - info->has_kernel_release = true; info->kernel_release = g_strdup_printf("%lu", os_version.dwBuildNumber); - info->has_machine = true; info->machine = ga_get_current_arch(); - info->has_id = true; info->id = g_strdup("mswindows"); - info->has_name = true; info->name = g_strdup("Microsoft Windows"); - info->has_pretty_name = true; info->pretty_name = product_name; - info->has_version = true; info->version = ga_get_win_name(&os_version, false); - info->has_version_id = true; info->version_id = ga_get_win_name(&os_version, true); - info->has_variant = true; info->variant = g_strdup(server ? "server" : "client"); - info->has_variant_id = true; info->variant_id = g_strdup(server ? "server" : "client"); return info; @@ -2478,7 +2454,6 @@ GuestDeviceInfoList *qmp_guest_get_devices(Error **errp) device_id = g_match_info_fetch(match_info, 2); device->id = g_new0(GuestDeviceId, 1); - device->has_id = true; device->id->type = GUEST_DEVICE_TYPE_PCI; id = &device->id->u.pci; id->vendor_id = g_ascii_strtoull(vendor_id, NULL, 16); @@ -2502,7 +2477,6 @@ GuestDeviceInfoList *qmp_guest_get_devices(Error **errp) error_setg(errp, "conversion to utf8 failed (driver version)"); return NULL; } - device->has_driver_version = true; date = (LPFILETIME)cm_get_property(dev_info_data.DevInst, &qga_DEVPKEY_Device_DriverDate, &cm_type); @@ -2543,3 +2517,9 @@ GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) error_setg(errp, QERR_UNSUPPORTED); return NULL; } + +GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} diff --git a/qga/commands.c b/qga/commands.c index 7ff551d092..88c1c99fe5 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -32,9 +32,8 @@ #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 - * to log for accounting purposes, check ga_logging_enabled() beforehand, - * and use the QERR_QGA_LOGGING_DISABLED to generate an error + * temporarily disabled. if it is necessary that a command be able + * to log for accounting purposes, check ga_logging_enabled() beforehand. */ void slog(const gchar *fmt, ...) { @@ -155,7 +154,7 @@ GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **errp) gei = guest_exec_info_find(pid); if (gei == NULL) { - error_setg(errp, QERR_INVALID_PARAMETER, "pid"); + error_setg(errp, "PID " PRId64 " does not exist"); return NULL; } @@ -206,18 +205,16 @@ GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **errp) } #endif if (gei->out.length > 0) { - ges->has_out_data = true; ges->out_data = g_base64_encode(gei->out.data, gei->out.length); - g_free(gei->out.data); ges->has_out_truncated = gei->out.truncated; } + g_free(gei->out.data); if (gei->err.length > 0) { - ges->has_err_data = true; ges->err_data = g_base64_encode(gei->err.data, gei->err.length); - g_free(gei->err.data); ges->has_err_truncated = gei->err.truncated; } + g_free(gei->err.data); QTAILQ_REMOVE(&guest_exec_state.processes, gei, next); g_free(gei); @@ -273,12 +270,26 @@ static void guest_exec_child_watch(GPid pid, gint status, gpointer data) g_spawn_close_pid(pid); } -/** Reset ignored signals back to default. */ static void guest_exec_task_setup(gpointer data) { #if !defined(G_OS_WIN32) + bool has_merge = *(bool *)data; struct sigaction sigact; + if (has_merge) { + /* + * FIXME: When `GLIB_VERSION_MIN_REQUIRED` is bumped to 2.58+, use + * g_spawn_async_with_fds() to be portable on windows. The current + * logic does not work on windows b/c `GSpawnChildSetupFunc` is run + * inside the parent, not the child. + */ + if (dup2(STDOUT_FILENO, STDERR_FILENO) != 0) { + slog("dup2() failed to merge stderr into stdout: %s", + strerror(errno)); + } + } + + /* Reset ignored signals back to default. */ memset(&sigact, 0, sizeof(struct sigaction)); sigact.sa_handler = SIG_DFL; @@ -382,11 +393,23 @@ close: return false; } +static GuestExecCaptureOutputMode ga_parse_capture_output( + GuestExecCaptureOutput *capture_output) +{ + if (!capture_output) + return GUEST_EXEC_CAPTURE_OUTPUT_MODE_NONE; + else if (capture_output->type == QTYPE_QBOOL) + return capture_output->u.flag ? GUEST_EXEC_CAPTURE_OUTPUT_MODE_SEPARATED + : GUEST_EXEC_CAPTURE_OUTPUT_MODE_NONE; + else + return capture_output->u.mode; +} + GuestExec *qmp_guest_exec(const char *path, bool has_arg, strList *arg, bool has_env, strList *env, - bool has_input_data, const char *input_data, - bool has_capture_output, bool capture_output, + const char *input_data, + GuestExecCaptureOutput *capture_output, Error **errp) { GPid pid; @@ -399,14 +422,16 @@ GuestExec *qmp_guest_exec(const char *path, gint in_fd, out_fd, err_fd; GIOChannel *in_ch, *out_ch, *err_ch; GSpawnFlags flags; - bool has_output = (has_capture_output && capture_output); + bool has_output = false; + bool has_merge = false; + GuestExecCaptureOutputMode output_mode; g_autofree uint8_t *input = NULL; size_t ninput = 0; arglist.value = (char *)path; arglist.next = has_arg ? arg : NULL; - if (has_input_data) { + if (input_data) { input = qbase64_decode(input_data, -1, &ninput, errp); if (!input) { return NULL; @@ -418,12 +443,36 @@ GuestExec *qmp_guest_exec(const char *path, flags = G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH_FROM_ENVP; - if (!has_output) { + + output_mode = ga_parse_capture_output(capture_output); + switch (output_mode) { + case GUEST_EXEC_CAPTURE_OUTPUT_MODE_NONE: flags |= G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL; + break; + case GUEST_EXEC_CAPTURE_OUTPUT_MODE_STDOUT: + has_output = true; + flags |= G_SPAWN_STDERR_TO_DEV_NULL; + break; + case GUEST_EXEC_CAPTURE_OUTPUT_MODE_STDERR: + has_output = true; + flags |= G_SPAWN_STDOUT_TO_DEV_NULL; + break; + case GUEST_EXEC_CAPTURE_OUTPUT_MODE_SEPARATED: + has_output = true; + break; +#if !defined(G_OS_WIN32) + case GUEST_EXEC_CAPTURE_OUTPUT_MODE_MERGED: + has_output = true; + has_merge = true; + break; +#endif + case GUEST_EXEC_CAPTURE_OUTPUT_MODE__MAX: + /* Silence warning; impossible branch */ + break; } ret = g_spawn_async_with_pipes(NULL, argv, envp, flags, - guest_exec_task_setup, NULL, &pid, has_input_data ? &in_fd : NULL, + guest_exec_task_setup, &has_merge, &pid, input_data ? &in_fd : NULL, has_output ? &out_fd : NULL, has_output ? &err_fd : NULL, &gerr); if (!ret) { error_setg(errp, QERR_QGA_COMMAND_FAILED, gerr->message); @@ -438,7 +487,7 @@ GuestExec *qmp_guest_exec(const char *path, gei->has_output = has_output; g_child_watch_add(pid, guest_exec_child_watch, gei); - if (has_input_data) { + if (input_data) { gei->in.data = g_steal_pointer(&input); gei->in.size = ninput; #ifdef G_OS_WIN32 @@ -547,7 +596,6 @@ GuestTimezone *qmp_guest_get_timezone(Error **errp) info->offset = g_time_zone_get_offset(tz, intv); name = g_time_zone_get_abbreviation(tz, intv); if (name != NULL) { - info->has_zone = true; info->zone = g_strdup(name); } g_time_zone_unref(tz); diff --git a/qga/cutils.c b/qga/cutils.c index b8e142ef64..b21bcf3683 100644 --- a/qga/cutils.c +++ b/qga/cutils.c @@ -2,8 +2,9 @@ * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ -#include "cutils.h" +#include "qemu/osdep.h" +#include "cutils.h" #include "qapi/error.h" /** diff --git a/qga/cutils.h b/qga/cutils.h index f0f30a7d28..c1f2f4b17a 100644 --- a/qga/cutils.h +++ b/qga/cutils.h @@ -1,8 +1,6 @@ #ifndef CUTILS_H_ #define CUTILS_H_ -#include "qemu/osdep.h" - int qga_open_cloexec(const char *name, int flags, mode_t mode); #endif /* CUTILS_H_ */ diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h index 29cd50402f..b4e7c52c61 100644 --- a/qga/guest-agent-core.h +++ b/qga/guest-agent-core.h @@ -24,7 +24,7 @@ typedef struct GACommandState GACommandState; extern GAState *ga_state; extern QmpCommandList ga_commands; -GList *ga_command_blacklist_init(GList *blacklist); +GList *ga_command_init_blockedrpcs(GList *blockedrpcs); void ga_command_state_init(GAState *s, GACommandState *cs); void ga_command_state_add(GACommandState *cs, void (*init)(void), diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs index 813d1c6ca6..df572adb4a 100644 --- a/qga/installer/qemu-ga.wxs +++ b/qga/installer/qemu-ga.wxs @@ -31,6 +31,7 @@ /> + @@ -101,36 +102,51 @@ - - - + + + + + + + + + + + + + + + + - + diff --git a/qga/main.c b/qga/main.c index c373fec3ee..bdf5344584 100644 --- a/qga/main.c +++ b/qga/main.c @@ -24,7 +24,6 @@ #include "qapi/qmp/qjson.h" #include "guest-agent-core.h" #include "qga-qapi-init-commands.h" -#include "qapi/qmp/qerror.h" #include "qapi/error.h" #include "channel.h" #include "qemu/cutils.h" @@ -37,17 +36,16 @@ #include "qga/service-win32.h" #include "qga/vss-win32.h" #endif -#ifdef __linux__ -#include -#ifdef FIFREEZE -#define CONFIG_FSFREEZE -#endif -#endif +#include "commands-common.h" #ifndef _WIN32 +#ifdef CONFIG_BSD +#define QGA_VIRTIO_PATH_DEFAULT "/dev/vtcon/org.qemu.guest_agent.0" +#else /* CONFIG_BSD */ #define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0" -#define QGA_STATE_RELATIVE_DIR "run" +#endif /* CONFIG_BSD */ #define QGA_SERIAL_PATH_DEFAULT "/dev/ttyS0" +#define QGA_STATE_RELATIVE_DIR "run" #else #define QGA_VIRTIO_PATH_DEFAULT "\\\\.\\Global\\org.qemu.guest_agent.0" #define QGA_STATE_RELATIVE_DIR "qemu-ga" @@ -84,10 +82,12 @@ struct GAState { #ifdef _WIN32 GAService service; HANDLE wakeup_event; + HANDLE event_log; #endif bool delimit_response; bool frozen; - GList *blacklist; + GList *blockedrpcs; + GList *allowedrpcs; char *state_filepath_isfrozen; struct { const char *log_filepath; @@ -107,7 +107,7 @@ struct GAState *ga_state; QmpCommandList ga_commands; /* commands that are safe to issue while filesystems are frozen */ -static const char *ga_freeze_whitelist[] = { +static const char *ga_freeze_allowlist[] = { "guest-ping", "guest-info", "guest-sync", @@ -223,6 +223,10 @@ void reopen_fd_to_null(int fd) static void usage(const char *cmd) { +#ifdef CONFIG_FSFREEZE + g_autofree char *fsfreeze_hook = get_relocated_path(QGA_FSFREEZE_HOOK_DEFAULT); +#endif + printf( "Usage: %s [-m -p ] []\n" "QEMU Guest Agent " QEMU_FULL_VERSION "\n" @@ -256,8 +260,10 @@ QEMU_COPYRIGHT "\n" #ifdef _WIN32 " -s, --service service commands: install, uninstall, vss-install, vss-uninstall\n" #endif -" -b, --blacklist comma-separated list of RPCs to disable (no spaces, \"?\"\n" -" to list available RPCs)\n" +" -b, --block-rpcs comma-separated list of RPCs to disable (no spaces,\n" +" use \"--block-rpcs=help\" to list available RPCs)\n" +" -a, --allow-rpcs comma-separated list of RPCs to enable (no spaces,\n" +" use \"--allow-rpcs=help\" to list available RPCs)\n" " -D, --dump-conf dump a qemu-ga config file based on current config\n" " options / command-line parameters to stdout\n" " -r, --retry-path attempt re-opening path if it's unavailable or closed\n" @@ -270,7 +276,7 @@ QEMU_HELP_BOTTOM "\n" , cmd, QGA_VIRTIO_PATH_DEFAULT, QGA_SERIAL_PATH_DEFAULT, dfl_pathnames.pidfile, #ifdef CONFIG_FSFREEZE - QGA_FSFREEZE_HOOK_DEFAULT, + fsfreeze_hook, #endif dfl_pathnames.state_dir); } @@ -310,6 +316,38 @@ void ga_enable_logging(GAState *s) s->logging_enabled = true; } +static int glib_log_level_to_system(int level) +{ + switch (level) { +#ifndef _WIN32 + case G_LOG_LEVEL_ERROR: + return LOG_ERR; + case G_LOG_LEVEL_CRITICAL: + return LOG_CRIT; + case G_LOG_LEVEL_WARNING: + return LOG_WARNING; + case G_LOG_LEVEL_MESSAGE: + return LOG_NOTICE; + case G_LOG_LEVEL_DEBUG: + return LOG_DEBUG; + case G_LOG_LEVEL_INFO: + default: + return LOG_INFO; +#else + case G_LOG_LEVEL_ERROR: + case G_LOG_LEVEL_CRITICAL: + return EVENTLOG_ERROR_TYPE; + case G_LOG_LEVEL_WARNING: + return EVENTLOG_WARNING_TYPE; + case G_LOG_LEVEL_MESSAGE: + case G_LOG_LEVEL_INFO: + case G_LOG_LEVEL_DEBUG: + default: + return EVENTLOG_INFORMATION_TYPE; +#endif + } +} + static void ga_log(const gchar *domain, GLogLevelFlags level, const gchar *msg, gpointer opaque) { @@ -321,13 +359,14 @@ static void ga_log(const gchar *domain, GLogLevelFlags level, } level &= G_LOG_LEVEL_MASK; -#ifndef _WIN32 if (g_strcmp0(domain, "syslog") == 0) { - syslog(LOG_INFO, "%s: %s", level_str, msg); - } else if (level & s->log_level) { +#ifndef _WIN32 + syslog(glib_log_level_to_system(level), "%s: %s", level_str, msg); #else - if (level & s->log_level) { + ReportEvent(s->event_log, glib_log_level_to_system(level), + 0, 1, NULL, 1, 0, &msg, NULL); #endif + } else if (level & s->log_level) { g_autoptr(GDateTime) now = g_date_time_new_now_utc(); g_autofree char *nowstr = g_date_time_format(now, "%s.%f"); fprintf(s->log_file, "%s: %s: %s\n", nowstr, level_str, msg); @@ -359,37 +398,59 @@ static gint ga_strcmp(gconstpointer str1, gconstpointer str2) } /* disable commands that aren't safe for fsfreeze */ -static void ga_disable_non_whitelisted(const QmpCommand *cmd, void *opaque) +static void ga_disable_not_allowed_freeze(const QmpCommand *cmd, void *opaque) { - bool whitelisted = false; + bool allowed = false; int i = 0; const char *name = qmp_command_name(cmd); - while (ga_freeze_whitelist[i] != NULL) { - if (strcmp(name, ga_freeze_whitelist[i]) == 0) { - whitelisted = true; + while (ga_freeze_allowlist[i] != NULL) { + if (strcmp(name, ga_freeze_allowlist[i]) == 0) { + allowed = true; } i++; } - if (!whitelisted) { + if (!allowed) { g_debug("disabling command: %s", name); qmp_disable_command(&ga_commands, name, "the agent is in frozen state"); } } -/* [re-]enable all commands, except those explicitly blacklisted by user */ -static void ga_enable_non_blacklisted(const QmpCommand *cmd, void *opaque) +/* [re-]enable all commands, except those explicitly blocked by user */ +static void ga_enable_non_blocked(const QmpCommand *cmd, void *opaque) { - GList *blacklist = opaque; + GAState *s = opaque; + GList *blockedrpcs = s->blockedrpcs; + GList *allowedrpcs = s->allowedrpcs; const char *name = qmp_command_name(cmd); - if (g_list_find_custom(blacklist, name, ga_strcmp) == NULL && - !qmp_command_is_enabled(cmd)) { + if (g_list_find_custom(blockedrpcs, name, ga_strcmp) == NULL) { + if (qmp_command_is_enabled(cmd)) { + return; + } + + if (allowedrpcs && + g_list_find_custom(allowedrpcs, name, ga_strcmp) == NULL) { + return; + } + g_debug("enabling command: %s", name); qmp_enable_command(&ga_commands, name); } } +/* disable commands that aren't allowed */ +static void ga_disable_not_allowed(const QmpCommand *cmd, void *opaque) +{ + GList *allowedrpcs = opaque; + const char *name = qmp_command_name(cmd); + + if (g_list_find_custom(allowedrpcs, name, ga_strcmp) == NULL) { + g_debug("disabling command: %s", name); + qmp_disable_command(&ga_commands, name, "the command is not allowed"); + } +} + static bool ga_create_file(const char *path) { int fd = open(path, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR); @@ -422,8 +483,8 @@ void ga_set_frozen(GAState *s) if (ga_is_frozen(s)) { return; } - /* disable all non-whitelisted (for frozen state) commands */ - qmp_for_each_command(&ga_commands, ga_disable_non_whitelisted, NULL); + /* disable all forbidden (for frozen state) commands */ + qmp_for_each_command(&ga_commands, ga_disable_not_allowed_freeze, NULL); g_warning("disabling logging due to filesystem freeze"); ga_disable_logging(s); s->frozen = true; @@ -461,8 +522,8 @@ void ga_unset_frozen(GAState *s) s->deferred_options.pid_filepath = NULL; } - /* enable all disabled, non-blacklisted commands */ - qmp_for_each_command(&ga_commands, ga_enable_non_blacklisted, s->blacklist); + /* enable all disabled, non-blocked and allowed commands */ + qmp_for_each_command(&ga_commands, ga_enable_non_blocked, s); s->frozen = false; if (!ga_delete_file(s->state_filepath_isfrozen)) { g_warning("unable to delete %s, fsfreeze may not function properly", @@ -892,7 +953,8 @@ int64_t ga_get_fd_handle(GAState *s, Error **errp) int64_t handle; g_assert(s->pstate_filepath); - /* we blacklist commands and avoid operations that potentially require + /* + * We block commands and avoid operations that potentially require * writing to disk when we're in a frozen state. this includes opening * new files, so we should never get here in that situation */ @@ -946,8 +1008,10 @@ struct GAConfig { #ifdef _WIN32 const char *service; #endif - gchar *bliststr; /* blacklist may point to this string */ - GList *blacklist; + gchar *bliststr; /* blockedrpcs may point to this string */ + gchar *aliststr; /* allowedrpcs may point to this string */ + GList *blockedrpcs; + GList *allowedrpcs; int daemonize; GLogLevelFlags log_level; int dumpconf; @@ -959,6 +1023,7 @@ static void config_load(GAConfig *config) GError *gerr = NULL; GKeyFile *keyfile; g_autofree char *conf = g_strdup(g_getenv("QGA_CONF")) ?: get_relocated_path(QGA_CONF_DEFAULT); + const gchar *blockrpcs_key = "block-rpcs"; /* read system config */ keyfile = g_key_file_new(); @@ -1005,12 +1070,31 @@ static void config_load(GAConfig *config) config->retry_path = g_key_file_get_boolean(keyfile, "general", "retry-path", &gerr); } + if (g_key_file_has_key(keyfile, "general", "blacklist", NULL)) { + g_warning("config using deprecated 'blacklist' key, should be replaced" + " with the 'block-rpcs' key."); + blockrpcs_key = "blacklist"; + } + if (g_key_file_has_key(keyfile, "general", blockrpcs_key, NULL)) { config->bliststr = - g_key_file_get_string(keyfile, "general", "blacklist", &gerr); - config->blacklist = g_list_concat(config->blacklist, + g_key_file_get_string(keyfile, "general", blockrpcs_key, &gerr); + config->blockedrpcs = g_list_concat(config->blockedrpcs, split_list(config->bliststr, ",")); } + if (g_key_file_has_key(keyfile, "general", "allow-rpcs", NULL)) { + config->aliststr = + g_key_file_get_string(keyfile, "general", "allow-rpcs", &gerr); + config->allowedrpcs = g_list_concat(config->allowedrpcs, + split_list(config->aliststr, ",")); + } + + if (g_key_file_has_key(keyfile, "general", blockrpcs_key, NULL) && + g_key_file_has_key(keyfile, "general", "allow-rpcs", NULL)) { + g_critical("wrong config, using 'block-rpcs' and 'allow-rpcs' keys at" + " the same time is not allowed"); + exit(EXIT_FAILURE); + } end: g_key_file_free(keyfile); @@ -1068,8 +1152,11 @@ static void config_dump(GAConfig *config) config->log_level == G_LOG_LEVEL_MASK); g_key_file_set_boolean(keyfile, "general", "retry-path", config->retry_path); - tmp = list_join(config->blacklist, ','); - g_key_file_set_string(keyfile, "general", "blacklist", tmp); + tmp = list_join(config->blockedrpcs, ','); + g_key_file_set_string(keyfile, "general", "block-rpcs", tmp); + g_free(tmp); + tmp = list_join(config->allowedrpcs, ','); + g_key_file_set_string(keyfile, "general", "allow-rpcs", tmp); g_free(tmp); tmp = g_key_file_to_data(keyfile, NULL, &error); @@ -1086,8 +1173,9 @@ static void config_dump(GAConfig *config) static void config_parse(GAConfig *config, int argc, char **argv) { - const char *sopt = "hVvdm:p:l:f:F::b:s:t:Dr"; + const char *sopt = "hVvdm:p:l:f:F::b:a:s:t:Dr"; int opt_ind = 0, ch; + bool block_rpcs = false, allow_rpcs = false; const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, @@ -1101,7 +1189,9 @@ static void config_parse(GAConfig *config, int argc, char **argv) { "method", 1, NULL, 'm' }, { "path", 1, NULL, 'p' }, { "daemonize", 0, NULL, 'd' }, - { "blacklist", 1, NULL, 'b' }, + { "block-rpcs", 1, NULL, 'b' }, + { "blacklist", 1, NULL, 'b' }, /* deprecated alias for 'block-rpcs' */ + { "allow-rpcs", 1, NULL, 'a' }, #ifdef _WIN32 { "service", 1, NULL, 's' }, #endif @@ -1159,8 +1249,19 @@ static void config_parse(GAConfig *config, int argc, char **argv) qmp_for_each_command(&ga_commands, ga_print_cmd, NULL); exit(EXIT_SUCCESS); } - config->blacklist = g_list_concat(config->blacklist, - split_list(optarg, ",")); + config->blockedrpcs = g_list_concat(config->blockedrpcs, + split_list(optarg, ",")); + block_rpcs = true; + break; + } + case 'a': { + if (is_help_option(optarg)) { + qmp_for_each_command(&ga_commands, ga_print_cmd, NULL); + exit(EXIT_SUCCESS); + } + config->allowedrpcs = g_list_concat(config->allowedrpcs, + split_list(optarg, ",")); + allow_rpcs = true; break; } #ifdef _WIN32 @@ -1201,6 +1302,12 @@ static void config_parse(GAConfig *config, int argc, char **argv) exit(EXIT_FAILURE); } } + + if (block_rpcs && allow_rpcs) { + g_critical("wrong commandline, using --block-rpcs and --allow-rpcs at the" + " same time is not allowed"); + exit(EXIT_FAILURE); + } } static void config_free(GAConfig *config) @@ -1211,10 +1318,12 @@ static void config_free(GAConfig *config) g_free(config->state_dir); g_free(config->channel_path); g_free(config->bliststr); + g_free(config->aliststr); #ifdef CONFIG_FSFREEZE g_free(config->fsfreeze_hook); #endif - g_list_free_full(config->blacklist, g_free); + g_list_free_full(config->blockedrpcs, g_free); + g_list_free_full(config->allowedrpcs, g_free); g_free(config); } @@ -1224,7 +1333,7 @@ static bool check_is_frozen(GAState *s) /* check if a previous instance of qemu-ga exited with filesystems' state * marked as frozen. this could be a stale value (a non-qemu-ga process * or reboot may have since unfrozen them), but better to require an - * uneeded unfreeze than to risk hanging on start-up + * unneeded unfreeze than to risk hanging on start-up */ struct stat st; if (stat(s->state_filepath_isfrozen, &st) == -1) { @@ -1274,6 +1383,13 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) g_debug("Guest agent version %s started", QEMU_FULL_VERSION); #ifdef _WIN32 + s->event_log = RegisterEventSource(NULL, "qemu-ga"); + if (!s->event_log) { + g_autofree gchar *errmsg = g_win32_error_message(GetLastError()); + g_critical("unable to register event source: %s", errmsg); + return NULL; + } + /* On win32 the state directory is application specific (be it the default * or a user override). We got past the command line parsing; let's create * the directory (with any intermediate directories). If we run into an @@ -1298,7 +1414,7 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) s->deferred_options.log_filepath = config->log_filepath; } ga_disable_logging(s); - qmp_for_each_command(&ga_commands, ga_disable_non_whitelisted, NULL); + qmp_for_each_command(&ga_commands, ga_disable_not_allowed_freeze, NULL); } else { if (config->daemonize) { become_daemon(config->pid_filepath); @@ -1322,10 +1438,19 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) return NULL; } - config->blacklist = ga_command_blacklist_init(config->blacklist); - if (config->blacklist) { - GList *l = config->blacklist; - s->blacklist = config->blacklist; + if (config->allowedrpcs) { + qmp_for_each_command(&ga_commands, ga_disable_not_allowed, config->allowedrpcs); + s->allowedrpcs = config->allowedrpcs; + } + + /* + * Some commands can be blocked due to system limitation. + * Initialize blockedrpcs list even if allowedrpcs specified. + */ + config->blockedrpcs = ga_command_init_blockedrpcs(config->blockedrpcs); + if (config->blockedrpcs) { + GList *l = config->blockedrpcs; + s->blockedrpcs = config->blockedrpcs; do { g_debug("disabling command: %s", (char *)l->data); qmp_disable_command(&ga_commands, l->data, NULL); @@ -1365,6 +1490,7 @@ static void cleanup_agent(GAState *s) { #ifdef _WIN32 CloseHandle(s->wakeup_event); + CloseHandle(s->event_log); #endif if (s->command_state) { ga_command_state_cleanup_all(s->command_state); diff --git a/qga/meson.build b/qga/meson.build index 65c1e93846..1c3d2a3d1b 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -7,9 +7,9 @@ if not have_ga endif have_qga_vss = get_option('qga_vss') \ - .require(targetos == 'windows', + .require(host_os == 'windows', error_message: 'VSS support requires Windows') \ - .require(link_language == 'cpp', + .require('cpp' in all_languages, error_message: 'VSS support requires a C++ compiler') \ .require(have_vss, error_message: '''VSS support requires VSS headers. If your Visual Studio installation doesn't have the VSS headers, @@ -22,7 +22,7 @@ have_qga_vss = get_option('qga_vss') \ Then run configure with: --extra-cxxflags="-isystem /path/to/vss/inc/win2003"''') \ .require(midl.found() or widl.found(), error_message: 'VSS support requires midl or widl') \ - .require(not enable_static, + .require(not get_option('prefer_static'), error_message: 'VSS support requires dynamic linking with GLib') \ .allowed() @@ -67,23 +67,31 @@ qga_ss.add(files( 'main.c', 'cutils.c', )) -qga_ss.add(when: 'CONFIG_POSIX', if_true: files( - 'channel-posix.c', - 'commands-posix.c', - 'commands-posix-ssh.c', -)) -qga_ss.add(when: 'CONFIG_WIN32', if_true: files( - 'channel-win32.c', - 'commands-win32.c', - 'service-win32.c', - 'vss-win32.c' -)) +if host_os == 'windows' + qga_ss.add(files( + 'channel-win32.c', + 'commands-win32.c', + 'service-win32.c', + 'vss-win32.c' + )) +else + qga_ss.add(files( + 'channel-posix.c', + 'commands-posix.c', + 'commands-posix-ssh.c', + )) + if host_os == 'linux' + qga_ss.add(files('commands-linux.c')) + elif host_os in bsd_oses + qga_ss.add(files('commands-bsd.c')) + endif +endif -qga_ss = qga_ss.apply(config_host, strict: false) +qga_ss = qga_ss.apply({}) gen_tlb = [] qga_libs = [] -if targetos == 'windows' +if host_os == 'windows' qga_libs += ['-lws2_32', '-lwinmm', '-lpowrprof', '-lwtsapi32', '-lwininet', '-liphlpapi', '-lnetapi32', '-lsetupapi', '-lcfgmgr32'] if have_qga_vss @@ -92,13 +100,30 @@ if targetos == 'windows' endif endif -qga = executable('qemu-ga', qga_ss.sources(), +qga_objs = [] +if host_os == 'windows' + windmc = find_program('windmc', required: true) + windres = find_program('windres', required: true) + + msgrc = custom_target('messages-win32.rc', + input: 'messages-win32.mc', + output: ['messages-win32.rc', 'MSG00409.bin', 'messages-win32.h'], + command: [windmc, '-h', '@OUTDIR@', '-r', '@OUTDIR@', '@INPUT@']) + msgobj = custom_target('messages-win32.o', + input: msgrc[0], + output: 'messages-win32.o', + command: [windres, '-I', '@OUTDIR@', '-o', '@OUTPUT@', '@INPUT@']) + + qga_objs = [msgobj] +endif + +qga = executable('qemu-ga', qga_ss.sources() + qga_objs, link_args: qga_libs, dependencies: [qemuutil, libudev], install: true) all_qga += qga -if targetos == 'windows' +if host_os == 'windows' qemu_ga_msi_arch = { 'x86': ['-D', 'Arch=32'], 'x86_64': ['-a', 'x64', '-D', 'Arch=64'] @@ -117,6 +142,14 @@ if targetos == 'windows' qemu_ga_msi_vss = ['-D', 'InstallVss'] deps += qga_vss endif + if glib.version().version_compare('<2.73.2') + libpcre = 'libpcre1' + else + libpcre = 'libpcre2' + endif + qga_msi_version = get_option('qemu_ga_version') == '' \ + ? meson.project_version() \ + : get_option('qemu_ga_version') qga_msi = custom_target('QGA MSI', input: files('installer/qemu-ga.wxs'), output: 'qemu-ga-@0@.msi'.format(host_arch), @@ -126,10 +159,11 @@ if targetos == 'windows' qemu_ga_msi_arch[cpu], qemu_ga_msi_vss, '-D', 'BUILD_DIR=' + meson.project_build_root(), - '-D', 'BIN_DIR=' + glib.get_variable('bindir'), - '-D', 'QEMU_GA_VERSION=' + config_host['QEMU_GA_VERSION'], - '-D', 'QEMU_GA_MANUFACTURER=' + config_host['QEMU_GA_MANUFACTURER'], - '-D', 'QEMU_GA_DISTRO=' + config_host['QEMU_GA_DISTRO'], + '-D', 'BIN_DIR=' + glib_pc.get_variable('bindir'), + '-D', 'QEMU_GA_VERSION=' + qga_msi_version, + '-D', 'QEMU_GA_MANUFACTURER=' + get_option('qemu_ga_manufacturer'), + '-D', 'QEMU_GA_DISTRO=' + get_option('qemu_ga_distro'), + '-D', 'LIBPCRE=' + libpcre, ]) all_qga += [qga_msi] alias_target('msi', qga_msi) @@ -138,7 +172,7 @@ else if get_option('guest_agent_msi').enabled() error('MSI guest agent package is available only for MinGW Windows cross-compilation') endif - install_subdir('run', install_dir: get_option('localstatedir')) + install_emptydir(get_option('localstatedir') / 'run') endif alias_target('qemu-ga', all_qga) @@ -151,7 +185,7 @@ test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) # the leak detector in build-oss-fuzz Gitlab CI test. we should re-enable # this when an alternative is implemented or when the underlying glib # issue is identified/fix -#if 'CONFIG_POSIX' in config_host +#if host_os != 'windows' if false srcs = [files('commands-posix-ssh.c')] i = 0 diff --git a/qga/messages-win32.mc b/qga/messages-win32.mc new file mode 100644 index 0000000000..e21019cebe --- /dev/null +++ b/qga/messages-win32.mc @@ -0,0 +1,9 @@ +LanguageNames=( + English=0x409:MSG00409 +) + +MessageId=1 +SymbolicName=QEMU_GA_EVENTLOG_GENERAL +Language=English +%1 +. diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 9fa20e791b..d5af155007 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -4,10 +4,10 @@ ## # = General note concerning the use of guest agent interfaces # -# "unsupported" is a higher-level error than the errors that individual -# commands might document. The caller should always be prepared to receive -# QERR_UNSUPPORTED, even if the given command doesn't specify it, or doesn't -# document any failure mode at all. +# "unsupported" is a higher-level error than the errors that +# individual commands might document. The caller should always be +# prepared to receive QERR_UNSUPPORTED, even if the given command +# doesn't specify it, or doesn't document any failure mode at all. ## ## @@ -16,8 +16,8 @@ { 'pragma': { 'doc-required': true } } -# Whitelists to permit QAPI rule violations; think twice before you -# add to them! +# Lists with items allowed to permit QAPI rule violations; think twice +# before you add to them! { 'pragma': { # Types whose member names may use '_' 'member-name-exceptions': [ @@ -33,33 +33,35 @@ 'guest-get-time', 'guest-set-vcpus', 'guest-sync', - 'guest-sync-delimited' ] } } + 'guest-sync-delimited' ], + # Types and commands with undocumented members: + 'documentation-exceptions': [ + 'GuestNVMeSmart' ] } } ## # @guest-sync-delimited: # -# Echo back a unique integer value, and prepend to response a -# leading sentinel byte (0xFF) the client can check scan for. +# Echo back a unique integer value, and prepend to response a leading +# sentinel byte (0xFF) the client can check scan for. # -# This is used by clients talking to the guest agent over the -# wire to ensure the stream is in sync and doesn't contain stale -# data from previous client. It must be issued upon initial -# connection, and after any client-side timeouts (including -# timeouts on receiving a response to this command). +# This is used by clients talking to the guest agent over the wire to +# ensure the stream is in sync and doesn't contain stale data from +# previous client. It must be issued upon initial connection, and +# after any client-side timeouts (including timeouts on receiving a +# response to this command). # # After issuing this request, all guest agent responses should be -# ignored until the response containing the unique integer value -# the client passed in is returned. Receival of the 0xFF sentinel -# byte must be handled as an indication that the client's -# lexer/tokenizer/parser state should be flushed/reset in -# preparation for reliably receiving the subsequent response. As -# an optimization, clients may opt to ignore all data until a -# sentinel value is receiving to avoid unnecessary processing of -# stale data. +# ignored until the response containing the unique integer value the +# client passed in is returned. Receival of the 0xFF sentinel byte +# must be handled as an indication that the client's +# lexer/tokenizer/parser state should be flushed/reset in preparation +# for reliably receiving the subsequent response. As an optimization, +# clients may opt to ignore all data until a sentinel value is +# receiving to avoid unnecessary processing of stale data. # -# Similarly, clients should also precede this *request* -# with a 0xFF byte to make sure the guest agent flushes any -# partially read JSON data from a previous client connection. +# Similarly, clients should also precede this *request* with a 0xFF +# byte to make sure the guest agent flushes any partially read JSON +# data from a previous client connection. # # @id: randomly generated 64-bit integer # @@ -76,28 +78,27 @@ # # Echo back a unique integer value # -# This is used by clients talking to the guest agent over the -# wire to ensure the stream is in sync and doesn't contain stale -# data from previous client. All guest agent responses should be -# ignored until the provided unique integer value is returned, -# and it is up to the client to handle stale whole or -# partially-delivered JSON text in such a way that this response -# can be obtained. +# This is used by clients talking to the guest agent over the wire to +# ensure the stream is in sync and doesn't contain stale data from +# previous client. All guest agent responses should be ignored until +# the provided unique integer value is returned, and it is up to the +# client to handle stale whole or partially-delivered JSON text in +# such a way that this response can be obtained. # -# In cases where a partial stale response was previously -# received by the client, this cannot always be done reliably. -# One particular scenario being if qemu-ga responses are fed -# character-by-character into a JSON parser. In these situations, -# using guest-sync-delimited may be optimal. +# In cases where a partial stale response was previously received by +# the client, this cannot always be done reliably. One particular +# scenario being if qemu-ga responses are fed character-by-character +# into a JSON parser. In these situations, using guest-sync-delimited +# may be optimal. # -# For clients that fetch responses line by line and convert them -# to JSON objects, guest-sync should be sufficient, but note that -# in cases where the channel is dirty some attempts at parsing the +# For clients that fetch responses line by line and convert them to +# JSON objects, guest-sync should be sufficient, but note that in +# cases where the channel is dirty some attempts at parsing the # response may result in a parser error. # -# Such clients should also precede this command -# with a 0xFF byte to make sure the guest agent flushes any -# partially read JSON data from a previous session. +# Such clients should also precede this command with a 0xFF byte to +# make sure the guest agent flushes any partially read JSON data from +# a previous session. # # @id: randomly generated 64-bit integer # @@ -121,8 +122,8 @@ ## # @guest-get-time: # -# Get the information about guest's System Time relative to -# the Epoch of 1970-01-01 in UTC. +# Get the information about guest's System Time relative to the Epoch +# of 1970-01-01 in UTC. # # Returns: Time in nanoseconds. # @@ -136,25 +137,21 @@ # # Set guest time. # -# When a guest is paused or migrated to a file then loaded -# from that file, the guest OS has no idea that there -# was a big gap in the time. Depending on how long the -# gap was, NTP might not be able to resynchronize the -# guest. +# When a guest is paused or migrated to a file then loaded from that +# file, the guest OS has no idea that there was a big gap in the time. +# Depending on how long the gap was, NTP might not be able to +# resynchronize the guest. # -# This command tries to set guest's System Time to the -# given value, then sets the Hardware Clock (RTC) to the -# current System Time. This will make it easier for a guest -# to resynchronize without waiting for NTP. If no @time is -# specified, then the time to set is read from RTC. However, -# this may not be supported on all platforms (i.e. Windows). -# If that's the case users are advised to always pass a +# This command tries to set guest's System Time to the given value, +# then sets the Hardware Clock (RTC) to the current System Time. This +# will make it easier for a guest to resynchronize without waiting for +# NTP. If no @time is specified, then the time to set is read from +# RTC. However, this may not be supported on all platforms (i.e. +# Windows). If that's the case users are advised to always pass a # value. # -# @time: time of nanoseconds, relative to the Epoch -# of 1970-01-01 in UTC. -# -# Returns: Nothing on success. +# @time: time of nanoseconds, relative to the Epoch of 1970-01-01 in +# UTC. # # Since: 1.5 ## @@ -171,7 +168,7 @@ # @enabled: whether command is currently enabled by guest admin # # @success-response: whether command returns a response on success -# (since 1.7) +# (since 1.7) # # Since: 1.1.0 ## @@ -207,15 +204,15 @@ ## # @guest-shutdown: # -# Initiate guest-activated shutdown. Note: this is an asynchronous +# Initiate guest-activated shutdown. Note: this is an asynchronous # shutdown request, with no guarantee of successful shutdown. # # @mode: "halt", "powerdown" (default), or "reboot" # -# This command does NOT return a response on success. Success condition -# is indicated by the VM exiting with a zero exit status or, when -# running with --no-shutdown, by issuing the query-status QMP command -# to confirm the VM status is "shutdown". +# This command does NOT return a response on success. Success +# condition is indicated by the VM exiting with a zero exit status or, +# when running with --no-shutdown, by issuing the query-status QMP +# command to confirm the VM status is "shutdown". # # Since: 0.15.0 ## @@ -231,7 +228,7 @@ # # @mode: open mode, as per fopen(), "r" is the default. # -# Returns: Guest file handle on success. +# Returns: Guest file handle # # Since: 0.15.0 ## @@ -246,8 +243,6 @@ # # @handle: filehandle returned by guest-file-open # -# Returns: Nothing on success. -# # Since: 0.15.0 ## { 'command': 'guest-file-close', @@ -259,7 +254,7 @@ # Result of guest agent file-read operation # # @count: number of bytes read (note: count is *before* -# base64-encoding is applied) +# base64-encoding is applied) # # @buf-b64: base64-encoded bytes read # @@ -273,15 +268,16 @@ ## # @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, maximum is 48MB) +# @count: maximum number of bytes to read (default is 4KB, maximum is +# 48MB) # -# Returns: @GuestFileRead on success. +# Returns: @GuestFileRead # # Since: 0.15.0 ## @@ -295,7 +291,7 @@ # Result of guest agent file-write operation # # @count: number of bytes written (note: count is actual bytes -# written, after base64-decoding of provided buffer) +# written, after base64-decoding of provided buffer) # # @eof: whether EOF was encountered during write operation. # @@ -313,10 +309,10 @@ # # @buf-b64: base64-encoded string representing data to be written # -# @count: bytes to write (actual bytes, after base64-decode), -# default is all content in buf-b64 buffer after base64 decoding +# @count: bytes to write (actual bytes, after base64-decode), default +# is all content in buf-b64 buffer after base64 decoding # -# Returns: @GuestFileWrite on success. +# Returns: @GuestFileWrite # # Since: 0.15.0 ## @@ -345,7 +341,9 @@ # Symbolic names for use in @guest-file-seek # # @set: Set to the specified offset (same effect as 'whence':0) +# # @cur: Add offset to the current location (same effect as 'whence':1) +# # @end: Add offset to the end of the file (same effect as 'whence':2) # # Since: 2.6 @@ -358,8 +356,9 @@ # Controls the meaning of offset to @guest-file-seek. # # @value: Integral value (0 for set, 1 for cur, 2 for end), available -# for historical reasons, and might differ from the host's or -# guest's SEEK_* values (since: 0.15) +# for historical reasons, and might differ from the host's or +# guest's SEEK_* values (since: 0.15) +# # @name: Symbolic name, and preferred interface # # Since: 2.6 @@ -371,7 +370,7 @@ # @guest-file-seek: # # Seek to a position in the file, as with fseek(), and return the -# current file position afterward. Also encapsulates ftell()'s +# current file position afterward. Also encapsulates ftell()'s # functionality, with offset=0 and whence=1. # # @handle: filehandle returned by guest-file-open @@ -380,7 +379,7 @@ # # @whence: Symbolic or numeric code for interpreting offset # -# Returns: @GuestFileSeek on success. +# Returns: @GuestFileSeek # # Since: 0.15.0 ## @@ -392,12 +391,10 @@ ## # @guest-file-flush: # -# Write file changes bufferred in userspace to disk/kernel buffers +# Write file changes buffered in userspace to disk/kernel buffers # # @handle: filehandle returned by guest-file-open # -# Returns: Nothing on success. -# # Since: 0.15.0 ## { 'command': 'guest-file-flush', @@ -420,12 +417,13 @@ ## # @guest-fsfreeze-status: # -# Get guest fsfreeze state. error state indicates +# Get guest fsfreeze state. # -# Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined below) +# 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. +# Note: This may fail to properly report the current state as a result +# of some other guest processes having issued an fs freeze/thaw. # # Since: 0.15.0 ## @@ -435,18 +433,19 @@ ## # @guest-fsfreeze-freeze: # -# Sync and freeze all freezable, local guest filesystems. If this +# Sync and freeze all freezable, local guest filesystems. If this # command succeeded, you may call @guest-fsfreeze-thaw later to # 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. +# 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. # -# 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. +# Returns: Number of file systems currently frozen. +# +# 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. # # Since: 0.15.0 ## @@ -456,15 +455,16 @@ ## # @guest-fsfreeze-freeze-list: # -# Sync and freeze specified guest filesystems. -# See also @guest-fsfreeze-freeze. +# Sync and freeze specified guest filesystems. See also +# @guest-fsfreeze-freeze. +# +# On error, all filesystems will be thawed. # # @mountpoints: an array of mountpoints of filesystems to be frozen. -# If omitted, every mounted filesystem is frozen. -# Invalid mount points are ignored. +# If omitted, every mounted filesystem is frozen. Invalid mount +# points are ignored. # -# Returns: Number of file systems currently frozen. On error, all filesystems -# will be thawed. +# Returns: Number of file systems currently frozen. # # Since: 2.2 ## @@ -480,10 +480,9 @@ # Returns: Number of file systems thawed by this call # # Note: if return value does not match the previous call to -# guest-fsfreeze-freeze, this likely means some freezable -# filesystems were unfrozen before this call, and that the -# filesystem state may have changed before issuing this -# command. +# guest-fsfreeze-freeze, this likely means some freezable +# filesystems were unfrozen before this call, and that the +# filesystem state may have changed before issuing this command. # # Since: 0.15.0 ## @@ -494,8 +493,11 @@ # @GuestFilesystemTrimResult: # # @path: path that was trimmed +# # @error: an error message when trim failed +# # @trimmed: bytes trimmed for this path +# # @minimum: reported effective minimum for this path # # Since: 2.4 @@ -519,15 +521,16 @@ # # 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) +# Returns: A @GuestFilesystemTrimResponse which contains the status of +# all trimmed paths. (since 2.4) # # Since: 1.2 ## @@ -540,25 +543,25 @@ # # Suspend guest to disk. # -# This command attempts to suspend the guest using three strategies, in this -# order: +# This command attempts to suspend the guest using three strategies, +# in this order: # # - systemd hibernate # - pm-utils (via pm-hibernate) # - manual write into sysfs # -# This command does NOT return a response on success. There is a high chance -# the command succeeded if the VM exits with a zero exit status or, when -# running with --no-shutdown, by issuing the query-status QMP command to -# to confirm the VM status is "shutdown". However, the VM could also exit -# (or set its status to "shutdown") due to other reasons. +# This command does NOT return a response on success. There is a high +# chance the command succeeded if the VM exits with a zero exit status +# or, when running with --no-shutdown, by issuing the query-status QMP +# command to to confirm the VM status is "shutdown". However, the VM +# could also exit (or set its status to "shutdown") due to other +# reasons. # -# The following errors may be returned: +# Errors: +# - 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 +# Notes: It's strongly recommended to issue the guest-sync command +# before sending commands when the guest resumes # # Since: 1.1 ## @@ -569,32 +572,32 @@ # # Suspend guest to ram. # -# This command attempts to suspend the guest using three strategies, in this -# order: +# This command attempts to suspend the guest using three strategies, +# in this order: # -# - systemd suspend -# - pm-utils (via pm-suspend) +# - systemd hibernate +# - pm-utils (via pm-hibernate) # - manual write into sysfs # # IMPORTANT: guest-suspend-ram requires working wakeup support in # QEMU. You should check QMP command query-current-machine returns -# wakeup-suspend-support: true before issuing this command. Failure in -# doing so can result in a suspended guest that QEMU will not be able to -# awaken, forcing the user to power cycle the guest to bring it back. +# wakeup-suspend-support: true before issuing this command. Failure +# in doing so can result in a suspended guest that QEMU will not be +# able to awaken, forcing the user to power cycle the guest to bring +# it back. # -# This command does NOT return a response on success. There are two options -# to check for success: +# 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" # -# The following errors may be returned: +# Errors: +# - 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 +# Notes: It's strongly recommended to issue the guest-sync command +# before sending commands when the guest resumes # # Since: 1.1 ## @@ -605,30 +608,31 @@ # # Save guest state to disk and suspend to ram. # -# This command attempts to suspend the guest by executing, in this order: +# This command attempts to suspend the guest by executing, in this +# order: # # - systemd hybrid-sleep # - pm-utils (via pm-suspend-hybrid) # # IMPORTANT: guest-suspend-hybrid requires working wakeup support in # QEMU. You should check QMP command query-current-machine returns -# wakeup-suspend-support: true before issuing this command. Failure in -# doing so can result in a suspended guest that QEMU will not be able to -# awaken, forcing the user to power cycle the guest to bring it back. +# wakeup-suspend-support: true before issuing this command. Failure +# in doing so can result in a suspended guest that QEMU will not be +# able to awaken, forcing the user to power cycle the guest to bring +# it back. # -# This command does NOT return a response on success. There are two options -# to check for success: +# 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" # -# The following errors may be returned: +# Errors: +# - 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 +# Notes: It's strongly recommended to issue the guest-sync command +# before sending commands when the guest resumes # # Since: 1.1 ## @@ -705,8 +709,8 @@ # # @ip-addresses: List of addresses assigned to @name # -# @statistics: various statistic counters related to @name -# (since 2.11) +# @statistics: various statistic counters related to @name (since +# 2.11) # # Since: 1.1 ## @@ -719,10 +723,9 @@ ## # @guest-network-get-interfaces: # -# Get list of guest IP addresses, MAC addresses -# and netmasks. +# Get list of guest IP addresses, MAC addresses and netmasks. # -# Returns: List of GuestNetworkInfo on success. +# Returns: List of GuestNetworkInterface # # Since: 1.1 ## @@ -736,10 +739,10 @@ # # @online: Whether the VCPU is enabled. # -# @can-offline: Whether offlining the VCPU is possible. This member -# is always filled in by the guest agent when the structure is -# returned, and always ignored on input (hence it can be omitted -# then). +# @can-offline: Whether offlining the VCPU is possible. This member +# is always filled in by the guest agent when the structure is +# returned, and always ignored on input (hence it can be omitted +# then). # # Since: 1.5 ## @@ -755,8 +758,8 @@ # # 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. +# Returns: The list of all VCPUs the guest knows about. Each VCPU is +# put on the list exactly once, but their order is unspecified. # # Since: 1.5 ## @@ -766,36 +769,38 @@ ## # @guest-set-vcpus: # -# Attempt to reconfigure (currently: enable/disable) logical processors inside -# the guest. +# Attempt to reconfigure (currently: enable/disable) logical +# processors inside the guest. # -# The input list is processed node by node in order. In each node @logical-id -# is used to look up the guest VCPU, for which @online specifies the requested -# state. The set of distinct @logical-id's is only required to be a subset of -# the guest-supported identifiers. There's no restriction on list length or on -# repeating the same @logical-id (with possibly different @online field). -# Preferably the input list should describe a modified subset of -# @guest-get-vcpus' return value. +# @vcpus: The logical processors to be reconfigured. This list is +# processed node by node in order. In each node @logical-id is +# used to look up the guest VCPU, for which @online specifies the +# requested state. The set of distinct @logical-id's is only +# required to be a subset of the guest-supported identifiers. +# There's no restriction on list length or on repeating the same +# @logical-id (with possibly different @online field). Preferably +# the input list should describe a modified subset of +# @guest-get-vcpus' return value. # -# Returns: The length of the initial sublist that has been successfully -# processed. The guest agent maximizes this value. Possible cases: +# 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, +# - < 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. +# +# Errors: +# - If the reconfiguration of the first node in @vcpus failed. +# Guest state has not been changed. # # Since: 1.5 ## @@ -809,24 +814,43 @@ # An enumeration of bus type of disks # # @ide: IDE disks +# # @fdc: floppy disks +# # @scsi: SCSI disks +# # @virtio: virtio disks +# # @xen: Xen disks +# # @usb: USB disks +# # @uml: UML disks +# # @sata: SATA disks +# # @sd: SD cards +# # @unknown: Unknown bus type +# # @ieee1394: Win IEEE 1394 bus type +# # @ssa: Win SSA bus type +# # @fibre: Win fiber channel bus type +# # @raid: Win RAID bus type +# # @iscsi: Win iScsi bus type +# # @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 +# # @nvme: NVMe disks (since 7.1) # # Since: 2.2; 'Unknown' and all entries below since 2.4 @@ -841,8 +865,11 @@ # @GuestPCIAddress: # # @domain: domain id +# # @bus: bus id +# # @slot: slot id +# # @function: function id # # Since: 2.2 @@ -855,8 +882,11 @@ # @GuestCCWAddress: # # @cssid: channel subsystem image id +# # @ssid: subchannel set id +# # @subchno: subchannel number +# # @devno: device number # # Since: 6.0 @@ -870,13 +900,21 @@ ## # @GuestDiskAddress: # -# @pci-controller: controller's PCI address (fields are set to -1 if invalid) +# @pci-controller: controller's PCI address (fields are set to -1 if +# invalid) +# # @bus-type: bus type +# # @bus: bus id +# # @target: target id +# # @unit: unit id +# # @serial: serial number (since: 3.1) +# # @dev: device node (POSIX) or device UNC (Windows) (since: 3.1) +# # @ccw-address: CCW address on s390x (since: 6.0) # # Since: 2.2 @@ -891,8 +929,10 @@ ## # @GuestNVMeSmart: # -# NVMe smart informations, based on NVMe specification, -# section +# NVMe smart information, based on NVMe specification, section +# +# +# TODO: document members briefly # # Since: 7.1 ## @@ -928,7 +968,7 @@ # # Disk type related smart information. # -# - @nvme: NVMe disk smart +# @type: disk bus type # # Since: 7.1 ## @@ -941,13 +981,18 @@ # @GuestDiskInfo: # # @name: device node (Linux) or device UNC (Windows) +# # @partition: whether this is a partition or disk -# @dependencies: list of device dependencies; e.g. for LVs of the LVM this will -# hold the list of PVs, for LUKS encrypted volume this will -# contain the disk where the volume is placed. (Linux) +# +# @dependencies: list of device dependencies; e.g. for LVs of the LVM +# this will hold the list of PVs, for LUKS encrypted volume this +# will contain the disk where the volume is placed. (Linux) +# # @address: disk address information (only for non-virtual devices) -# @alias: optional alias assigned to the disk, on Linux this is a name assigned -# by device mapper +# +# @alias: optional alias assigned to the disk, on Linux this is a name +# assigned by device mapper +# # @smart: disk smart information (Since 7.1) # # Since: 5.2 @@ -960,10 +1005,10 @@ ## # @guest-get-disks: # -# Returns: The list of disks in the guest. For Windows these are only the -# physical disks. On Linux these are all root block devices of -# non-zero size including e.g. removable devices, loop devices, -# NBD, etc. +# Returns: The list of disks in the guest. For Windows these are only +# the physical disks. On Linux these are all root block devices +# of non-zero size including e.g. removable devices, loop devices, +# NBD, etc. # # Since: 5.2 ## @@ -974,12 +1019,17 @@ # @GuestFilesystemInfo: # # @name: disk name +# # @mountpoint: mount point path +# # @type: file system type string +# # @used-bytes: file system used bytes (since 3.0) +# # @total-bytes: non-root file system total bytes (since 3.0) -# @disk: an array of disk hardware information that the volume lies on, -# which may be empty if the disk type is not supported +# +# @disk: an array of disk hardware information that the volume lies +# on, which may be empty if the disk type is not supported # # Since: 2.2 ## @@ -992,9 +1042,9 @@ # @guest-get-fsinfo: # # Returns: The list of filesystems information mounted in the guest. -# The returned mountpoints may be specified to -# @guest-fsfreeze-freeze-list. -# Network filesystems (such as CIFS and NFS) are not listed. +# The returned mountpoints may be specified to +# @guest-fsfreeze-freeze-list. Network filesystems (such as CIFS +# and NFS) are not listed. # # Since: 2.2 ## @@ -1005,23 +1055,23 @@ # @guest-set-user-password: # # @username: the user account whose password to change +# # @password: the new password entry string, base64 encoded +# # @crypted: true if password is already crypt()d, false if raw # -# If the @crypted flag is true, it is the caller's responsibility -# to ensure the correct crypt() encryption scheme is used. This -# command does not attempt to interpret or report on the encryption -# scheme. Refer to the documentation of the guest operating system -# in question to determine what is supported. +# If the @crypted flag is true, it is the caller's responsibility to +# ensure the correct crypt() encryption scheme is used. This command +# does not attempt to interpret or report on the encryption scheme. +# Refer to the documentation of the guest operating system in question +# to determine what is supported. # -# Not all guest operating systems will support use of the -# @crypted flag, as they may require the clear-text password +# Not all guest operating systems will support use of the @crypted +# flag, as they may require the clear-text password # # The @password parameter must always be base64 encoded before -# transmission, even if already crypt()d, to ensure it is 8-bit -# safe when passed as JSON. -# -# Returns: Nothing on success. +# transmission, even if already crypt()d, to ensure it is 8-bit safe +# when passed as JSON. # # Since: 2.3 ## @@ -1031,14 +1081,15 @@ ## # @GuestMemoryBlock: # -# @phys-index: Arbitrary guest-specific unique identifier of the MEMORY BLOCK. +# @phys-index: Arbitrary guest-specific unique identifier of the +# MEMORY BLOCK. # # @online: Whether the MEMORY BLOCK is enabled in guest. # -# @can-offline: Whether offlining the MEMORY BLOCK is possible. -# This member is always filled in by the guest agent when the -# structure is returned, and always ignored on input (hence it -# can be omitted then). +# @can-offline: Whether offlining the MEMORY BLOCK is possible. This +# member is always filled in by the guest agent when the structure +# is returned, and always ignored on input (hence it can be +# omitted then). # # Since: 2.3 ## @@ -1054,9 +1105,9 @@ # # 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. +# 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. # # Since: 2.3 ## @@ -1068,12 +1119,17 @@ # # An enumeration of memory block operation result. # -# @success: the operation of online/offline memory block is successful. -# @not-found: can't find the corresponding memoryXXX directory in sysfs. +# @success: the operation of online/offline memory block is +# successful. +# +# @not-found: can't find the corresponding memoryXXX directory in +# sysfs. +# # @operation-not-supported: for some old kernels, it does not support -# online or offline memory block. -# @operation-failed: the operation of online/offline memory block fails, -# because of some errors happen. +# online or offline memory block. +# +# @operation-failed: the operation of online/offline memory block +# fails, because of some errors happen. # # Since: 2.3 ## @@ -1088,10 +1144,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. +# @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. # # Since: 2.3 ## @@ -1103,24 +1158,27 @@ ## # @guest-set-memory-blocks: # -# Attempt to reconfigure (currently: enable/disable) state of memory blocks -# inside the guest. +# Attempt to reconfigure (currently: enable/disable) state of memory +# blocks inside the guest. # -# The input list is processed node by node in order. In each node @phys-index -# is used to look up the guest MEMORY BLOCK, for which @online specifies the -# requested state. The set of distinct @phys-index's is only required to be a -# subset of the guest-supported identifiers. There's no restriction on list -# length or on repeating the same @phys-index (with possibly different @online -# field). -# Preferably the input list should describe a modified subset of -# @guest-get-memory-blocks' return value. +# @mem-blks: The memory blocks to be reconfigured. This list is +# processed node by node in order. In each node @phys-index is +# used to look up the guest MEMORY BLOCK, for which @online +# specifies the requested state. The set of distinct +# @phys-index's is only required to be a subset of the +# guest-supported identifiers. There's no restriction on list +# length or on repeating the same @phys-index (with possibly +# different @online field). Preferably the input list should +# describe a modified subset of @guest-get-memory-blocks' return +# value. # -# Returns: The operation results, it is a list of @GuestMemoryBlockResponse, -# which is corresponding to the input list. +# Returns: The operation results, it is a list of +# @GuestMemoryBlockResponse, which is corresponding to the input +# list. # -# Note: it will return NULL if the @mem-blks list was empty on input, -# or there is an error, and in this case, guest state will not be -# changed. +# Note: it will return an empty list if the @mem-blks list was +# empty on input, or there is an error, and in this case, guest +# state will not be changed. # # Since: 2.3 ## @@ -1131,9 +1189,9 @@ ## # @GuestMemoryBlockInfo: # -# @size: the size (in bytes) of the guest memory blocks, -# which are the minimal units of memory block online/offline -# operations (also called Logical Memory Hotplug). +# @size: the size (in bytes) of the guest memory blocks, which are the +# minimal units of memory block online/offline operations (also +# called Logical Memory Hotplug). # # Since: 2.3 ## @@ -1156,17 +1214,25 @@ # @GuestExecStatus: # # @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. -# @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' -# @out-truncated: true if stdout was not fully captured -# due to size limitation. -# @err-truncated: true if stderr was not fully captured -# due to size limitation. +# +# @signal: signal number (linux) or unhandled exception code (windows) +# if the process was abnormally terminated. +# +# @out-data: base64-encoded stdout of the process. This field will +# only be populated after the process exits. +# +# @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'. This field will only be populated after the +# process exits. +# +# @out-truncated: true if stdout was not fully captured due to size +# limitation. +# +# @err-truncated: true if stderr was not fully captured due to size +# limitation. # # Since: 2.5 ## @@ -1177,12 +1243,13 @@ ## # @guest-exec-status: # -# Check status of process associated with PID retrieved via guest-exec. -# Reap the process and associated metadata if it has exited. +# Check status of process associated with PID retrieved via +# guest-exec. Reap the process and associated metadata if it has +# exited. # # @pid: pid returned from guest-exec # -# Returns: GuestExecStatus on success. +# Returns: GuestExecStatus # # Since: 2.5 ## @@ -1200,25 +1267,68 @@ { 'struct': 'GuestExec', 'data': { 'pid': 'int'} } +## +# @GuestExecCaptureOutputMode: +# +# An enumeration of guest-exec capture modes. +# +# @none: do not capture any output +# +# @stdout: only capture stdout +# +# @stderr: only capture stderr +# +# @separated: capture both stdout and stderr, but separated into +# GuestExecStatus out-data and err-data, respectively +# +# @merged: capture both stdout and stderr, but merge together into +# out-data. Not effective on windows guests. +# +# Since: 8.0 +## + { 'enum': 'GuestExecCaptureOutputMode', + 'data': [ 'none', 'stdout', 'stderr', 'separated', + { 'name': 'merged', 'if': { 'not': 'CONFIG_WIN32' } } ] } + +## +# @GuestExecCaptureOutput: +# +# Controls what guest-exec output gets captures. +# +# @flag: captures both stdout and stderr if true. Equivalent to +# GuestExecCaptureOutputMode::all. (since 2.5) +# +# @mode: capture mode; preferred interface +# +# Since: 8.0 +## + { 'alternate': 'GuestExecCaptureOutput', + 'data': { 'flag': 'bool', + 'mode': 'GuestExecCaptureOutputMode'} } + ## # @guest-exec: # # Execute a command in the guest # # @path: path or executable name to execute -# @arg: argument list to pass to executable -# @env: environment variables to pass to executable -# @input-data: data to be passed to process stdin (base64 encoded) -# @capture-output: bool flag to enable capture of -# stdout/stderr of running process. defaults to false. # -# Returns: PID on success. +# @arg: argument list to pass to executable +# +# @env: environment variables to pass to executable +# +# @input-data: data to be passed to process stdin (base64 encoded) +# +# @capture-output: bool flag to enable capture of stdout/stderr of +# running process. Defaults to false. +# +# Returns: PID # # Since: 2.5 ## { 'command': 'guest-exec', 'data': { 'path': 'str', '*arg': ['str'], '*env': ['str'], - '*input-data': 'str', '*capture-output': 'bool' }, + '*input-data': 'str', '*capture-output': 'GuestExecCaptureOutput' }, 'returns': 'GuestExec' } @@ -1237,11 +1347,11 @@ # # Return a name for the machine. # -# The returned name is not necessarily a fully-qualified domain name, or even -# present in DNS or some other name service at all. It need not even be unique -# on your local network or site, but usually it is. +# The returned name is not necessarily a fully-qualified domain name, +# or even present in DNS or some other name service at all. It need +# not even be unique on your local network or site, but usually it is. # -# Returns: the host name of the machine on success +# Returns: the host name of the machine # # Since: 2.10 ## @@ -1253,10 +1363,13 @@ # @GuestUser: # # @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. +# +# @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. # # Since: 2.10 ## @@ -1265,6 +1378,7 @@ ## # @guest-get-users: +# # Retrieves a list of currently active users on the VM. # # Returns: A unique list of users. @@ -1277,10 +1391,11 @@ ## # @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 ## @@ -1303,45 +1418,56 @@ # @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: +# Notes: On POSIX systems the fields @id, @name, @pretty-name, +# @version, @version-id, @variant and @variant-id follow the +# definition specified in os-release(5). Refer to the manual page +# for exact description of the fields. Their values are taken +# from the os-release file. If the file is not present in the +# system, or the values are not present in the file, the fields +# are not included. # -# On POSIX systems the fields @id, @name, @pretty-name, @version, @version-id, -# @variant and @variant-id follow the definition specified in os-release(5). -# Refer to the manual page for exact description of the fields. Their values -# are taken from the os-release file. If the file is not present in the system, -# or the values are not present in the file, the fields are not included. -# -# On Windows the values are filled from information gathered from the system. +# On Windows the values are filled from information gathered from +# the system. # # Since: 2.10 ## @@ -1366,6 +1492,8 @@ ## # @GuestDeviceType: +# +# @pci: PCI device ## { 'enum': 'GuestDeviceType', 'data': [ 'pci' ] } @@ -1374,6 +1502,7 @@ # @GuestDeviceIdPCI: # # @vendor-id: vendor ID +# # @device-id: device ID # # Since: 5.2 @@ -1385,7 +1514,8 @@ # @GuestDeviceId: # # Id of the device -# - @pci: PCI ID, since: 5.2 +# +# @type: device type # # Since: 5.2 ## @@ -1398,8 +1528,11 @@ # @GuestDeviceInfo: # # @driver-name: name of the associated driver +# # @driver-date: driver release date, in nanoseconds since the epoch +# # @driver-version: driver version +# # @id: device ID # # Since: 5.2 @@ -1441,10 +1574,10 @@ ## # @guest-ssh-get-authorized-keys: # -# @username: the user account to add the authorized keys +# Return the public keys from user .ssh/authorized_keys on Unix +# systems (not implemented for other systems). # -# Return the public keys from user .ssh/authorized_keys on Unix systems (not -# implemented for other systems). +# @username: the user account to add the authorized keys # # Returns: @GuestAuthorizedKeys # @@ -1458,14 +1591,15 @@ ## # @guest-ssh-add-authorized-keys: # -# @username: the user account to add the authorized keys -# @keys: the public keys to add (in OpenSSH/sshd(8) authorized_keys format) -# @reset: ignore the existing content, set it with the given keys only -# # Append public keys to user .ssh/authorized_keys on Unix systems (not # implemented for other systems). # -# Returns: Nothing on success. +# @username: the user account to add the authorized keys +# +# @keys: the public keys to add (in OpenSSH/sshd(8) authorized_keys +# format) +# +# @reset: ignore the existing content, set it with the given keys only # # Since: 5.2 ## @@ -1476,14 +1610,14 @@ ## # @guest-ssh-remove-authorized-keys: # +# Remove public keys from the user .ssh/authorized_keys on Unix +# systems (not implemented for other systems). It's not an error if +# the key is already missing. +# # @username: the user account to remove the authorized keys -# @keys: the public keys to remove (in OpenSSH/sshd(8) authorized_keys format) # -# Remove public keys from the user .ssh/authorized_keys on Unix systems (not -# implemented for other systems). It's not an error if the key is already -# missing. -# -# Returns: Nothing on success. +# @keys: the public keys to remove (in OpenSSH/sshd(8) authorized_keys +# format) # # Since: 5.2 ## @@ -1526,7 +1660,8 @@ # # @total-ticks: time spent doing I/Os (ms) # -# @weight-ticks: weighted time spent doing I/Os since the last update of this field(ms) +# @weight-ticks: weighted time spent doing I/Os since the last update +# of this field(ms) # # Since: 7.1 ## @@ -1553,11 +1688,13 @@ ## # @GuestDiskStatsInfo: # -# @name disk name +# @name: disk name # -# @major major device number of disk +# @major: major device number of disk # -# @minor minor device number of disk +# @minor: minor device number of disk +# +# @stats: I/O statistics ## { 'struct': 'GuestDiskStatsInfo', 'data': {'name': 'str', @@ -1569,6 +1706,7 @@ # @guest-get-diskstats: # # Retrieve information about disk stats. +# # Returns: List of disk stats of guest. # # Since: 7.1 @@ -1576,3 +1714,87 @@ { 'command': 'guest-get-diskstats', 'returns': ['GuestDiskStatsInfo'] } + +## +# @GuestCpuStatsType: +# +# Guest operating systems supporting CPU statistics +# +# @linux: Linux +# +# Since: 7.1 +## +{ 'enum': 'GuestCpuStatsType', + 'data': [ 'linux' ] } + + +## +# @GuestLinuxCpuStats: +# +# CPU statistics of Linux +# +# @cpu: CPU index in guest OS +# +# @user: Time spent in user mode +# +# @nice: Time spent in user mode with low priority (nice) +# +# @system: Time spent in system mode +# +# @idle: Time spent in the idle task +# +# @iowait: Time waiting for I/O to complete (since Linux 2.5.41) +# +# @irq: Time servicing interrupts (since Linux 2.6.0-test4) +# +# @softirq: Time servicing softirqs (since Linux 2.6.0-test4) +# +# @steal: Stolen time by host (since Linux 2.6.11) +# +# @guest: ime spent running a virtual CPU for guest operating systems +# under the control of the Linux kernel (since Linux 2.6.24) +# +# @guestnice: Time spent running a niced guest (since Linux 2.6.33) +# +# Since: 7.1 +## +{ 'struct': 'GuestLinuxCpuStats', + 'data': {'cpu': 'int', + 'user': 'uint64', + 'nice': 'uint64', + 'system': 'uint64', + 'idle': 'uint64', + '*iowait': 'uint64', + '*irq': 'uint64', + '*softirq': 'uint64', + '*steal': 'uint64', + '*guest': 'uint64', + '*guestnice': 'uint64' + } } + +## +# @GuestCpuStats: +# +# Get statistics of each CPU in millisecond. +# +# @type: guest operating system +# +# Since: 7.1 +## +{ 'union': 'GuestCpuStats', + 'base': { 'type': 'GuestCpuStatsType' }, + 'discriminator': 'type', + 'data': { 'linux': 'GuestLinuxCpuStats' } } + +## +# @guest-get-cpustats: +# +# Retrieve information about CPU stats. +# +# Returns: List of CPU stats of guest. +# +# Since: 7.1 +## +{ 'command': 'guest-get-cpustats', + 'returns': ['GuestCpuStats'] +} diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp index b57508fbe0..84944133f7 100644 --- a/qga/vss-win32/install.cpp +++ b/qga/vss-win32/install.cpp @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "vss-common.h" +#include "vss-debug.h" #ifdef HAVE_VSS_SDK #include #else @@ -54,7 +55,7 @@ void errmsg(DWORD err, const char *text) FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *)&msg, 0, NULL); - fprintf(stderr, "%.*s. (Error: %lx) %s\n", len, text, err, msg); + qga_debug("%.*s. (Error: %lx) %s", len, text, err, msg); LocalFree(msg); } @@ -99,6 +100,8 @@ HRESULT put_Value(ICatalogObject *pObj, LPCWSTR name, T val) /* Lookup Administrators group name from winmgmt */ static HRESULT GetAdminName(_bstr_t *name) { + qga_debug_begin; + HRESULT hr; COMPointer pLoc; COMPointer pSvc; @@ -141,6 +144,7 @@ static HRESULT GetAdminName(_bstr_t *name) } out: + qga_debug_end; return hr; } @@ -148,6 +152,8 @@ out: static HRESULT getNameByStringSID( const wchar_t *sid, LPWSTR buffer, LPDWORD bufferLen) { + qga_debug_begin; + HRESULT hr = S_OK; PSID psid = NULL; SID_NAME_USE groupType; @@ -167,6 +173,7 @@ static HRESULT getNameByStringSID( LocalFree(psid); out: + qga_debug_end; return hr; } @@ -174,6 +181,8 @@ out: static HRESULT QGAProviderFind( HRESULT (*found)(ICatalogCollection *, int, void *), void *arg) { + qga_debug_begin; + HRESULT hr; COMInitializer initializer; COMPointer pUnknown; @@ -204,41 +213,53 @@ static HRESULT QGAProviderFind( chk(pColl->SaveChanges(&n)); out: + qga_debug_end; return hr; } /* Count QGA VSS provider in COM+ Application Catalog */ static HRESULT QGAProviderCount(ICatalogCollection *coll, int i, void *arg) { + qga_debug_begin; + (*(int *)arg)++; + + qga_debug_end; return S_OK; } /* Remove QGA VSS provider from COM+ Application Catalog Collection */ static HRESULT QGAProviderRemove(ICatalogCollection *coll, int i, void *arg) { + qga_debug_begin; HRESULT hr; - fprintf(stderr, "Removing COM+ Application: %s\n", QGA_PROVIDER_NAME); + qga_debug("Removing COM+ Application: %s", QGA_PROVIDER_NAME); chk(coll->Remove(i)); out: + qga_debug_end; return hr; } /* Unregister this module from COM+ Applications Catalog */ STDAPI COMUnregister(void) { + qga_debug_begin; + HRESULT hr; DllUnregisterServer(); chk(QGAProviderFind(QGAProviderRemove, NULL)); out: + qga_debug_end; return hr; } /* Register this module to COM+ Applications Catalog */ STDAPI COMRegister(void) { + qga_debug_begin; + HRESULT hr; COMInitializer initializer; COMPointer pUnknown; @@ -258,12 +279,14 @@ STDAPI COMRegister(void) if (!g_hinstDll) { errmsg(E_FAIL, "Failed to initialize DLL"); + qga_debug_end; return E_FAIL; } chk(QGAProviderFind(QGAProviderCount, (void *)&count)); if (count) { errmsg(E_ABORT, "QGA VSS Provider is already installed"); + qga_debug_end; return E_ABORT; } @@ -304,9 +327,8 @@ STDAPI COMRegister(void) } strcpy(tlbPath, dllPath); strcpy(tlbPath+n-3, "tlb"); - fprintf(stderr, "Registering " QGA_PROVIDER_NAME ":\n"); - fprintf(stderr, " %s\n", dllPath); - fprintf(stderr, " %s\n", tlbPath); + qga_debug("Registering " QGA_PROVIDER_NAME ": %s %s", + dllPath, tlbPath); if (!PathFileExists(tlbPath)) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); errmsg(hr, "Failed to lookup tlb"); @@ -321,7 +343,7 @@ STDAPI COMRegister(void) _bstr_t(dllPath), _bstr_t(tlbPath), _bstr_t(""))); - /* Setup roles of the applicaion */ + /* Setup roles of the application */ chk(getNameByStringSID(administratorsGroupSID, buffer, &bufferLen)); chk(pApps->GetCollection(_bstr_t(L"Roles"), key, @@ -354,12 +376,24 @@ out: COMUnregister(); } + qga_debug_end; return hr; } +STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int) +{ + COMRegister(); +} + +STDAPI_(void) CALLBACK DLLCOMUnregister(HWND, HINSTANCE, LPSTR, int) +{ + COMUnregister(); +} static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data) { + qga_debug_begin; + HKEY hKey; LONG ret; DWORD size; @@ -380,6 +414,7 @@ static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data) RegCloseKey(hKey); out: + qga_debug_end; if (ret != ERROR_SUCCESS) { /* As we cannot printf within DllRegisterServer(), show a dialog. */ errmsg_dialog(ret, "Cannot add registry", key); @@ -391,6 +426,8 @@ out: /* Register this dll as a VSS provider */ STDAPI DllRegisterServer(void) { + qga_debug_begin; + COMInitializer initializer; COMPointer pVssAdmin; HRESULT hr = E_FAIL; @@ -402,7 +439,7 @@ STDAPI DllRegisterServer(void) goto out; } - /* Add this module to registery */ + /* Add this module to registry */ sprintf(key, "CLSID\\%s", g_szClsid); if (!CreateRegistryKey(key, NULL, g_szClsid)) { @@ -469,12 +506,15 @@ out: DllUnregisterServer(); } + qga_debug_end; return hr; } /* Unregister this VSS hardware provider from the system */ STDAPI DllUnregisterServer(void) { + qga_debug_begin; + TCHAR key[256]; COMInitializer initializer; COMPointer pVssAdmin; @@ -492,6 +532,7 @@ STDAPI DllUnregisterServer(void) SHDeleteKey(HKEY_CLASSES_ROOT, key); SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid); + qga_debug_end; return S_OK; /* Uninstall should never fail */ } @@ -508,7 +549,7 @@ namespace _com_util } if (mbstowcs(bstr, ascii, len) == (size_t)-1) { - fprintf(stderr, "Failed to convert string '%s' into BSTR", ascii); + qga_debug("Failed to convert string '%s' into BSTR", ascii); bstr[0] = 0; } return bstr; @@ -518,7 +559,9 @@ namespace _com_util /* Stop QGA VSS provider service using Winsvc API */ STDAPI StopService(void) { - HRESULT hr; + qga_debug_begin; + + HRESULT hr = S_OK; SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); SC_HANDLE service = NULL; @@ -542,5 +585,6 @@ STDAPI StopService(void) out: CloseServiceHandle(service); CloseServiceHandle(manager); + qga_debug_end; return hr; } diff --git a/qga/vss-win32/meson.build b/qga/vss-win32/meson.build index 9483ccd3b8..0ac918910b 100644 --- a/qga/vss-win32/meson.build +++ b/qga/vss-win32/meson.build @@ -7,7 +7,7 @@ link_args = cc.get_supported_link_arguments([ qga_vss = shared_module( 'qga-vss', - ['requester.cpp', 'provider.cpp', 'install.cpp', genh], + ['requester.cpp', 'provider.cpp', 'install.cpp', 'vss-debug.cpp', genh], name_prefix: '', cpp_args: ['-Wno-unknown-pragmas', '-Wno-delete-non-virtual-dtor', '-Wno-non-virtual-dtor'], link_args: link_args, diff --git a/qga/vss-win32/provider.cpp b/qga/vss-win32/provider.cpp index 1b885e24ee..cc72e5ef1b 100644 --- a/qga/vss-win32/provider.cpp +++ b/qga/vss-win32/provider.cpp @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "vss-common.h" +#include "vss-debug.h" #ifdef HAVE_VSS_SDK #include #else @@ -529,9 +530,11 @@ STDAPI DllCanUnloadNow() EXTERN_C BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved) { + qga_debug("begin, reason = %lu", dwReason); if (dwReason == DLL_PROCESS_ATTACH) { g_hinstDll = hinstDll; DisableThreadLibraryCalls(hinstDll); } + qga_debug_end; return TRUE; } diff --git a/qga/vss-win32/qga-vss.def b/qga/vss-win32/qga-vss.def index 927782c31b..ee97a81427 100644 --- a/qga/vss-win32/qga-vss.def +++ b/qga/vss-win32/qga-vss.def @@ -1,6 +1,8 @@ LIBRARY "QGA-PROVIDER.DLL" EXPORTS + DLLCOMRegister + DLLCOMUnregister COMRegister PRIVATE COMUnregister PRIVATE DllCanUnloadNow PRIVATE diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp index b371affeab..9884c65e70 100644 --- a/qga/vss-win32/requester.cpp +++ b/qga/vss-win32/requester.cpp @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "vss-common.h" +#include "vss-debug.h" #include "requester.h" #include "install.h" #include @@ -23,9 +24,13 @@ /* Call QueryStatus every 10 ms while waiting for frozen event */ #define VSS_TIMEOUT_EVENT_MSEC 10 -#define err_set(e, err, fmt, ...) \ - ((e)->error_setg_win32_wrapper((e)->errp, __FILE__, __LINE__, __func__, \ - err, fmt, ## __VA_ARGS__)) +#define DEFAULT_VSS_BACKUP_TYPE VSS_BT_FULL + +#define err_set(e, err, fmt, ...) { \ + (e)->error_setg_win32_wrapper((e)->errp, __FILE__, __LINE__, __func__, \ + err, fmt, ## __VA_ARGS__); \ + qga_debug(fmt, ## __VA_ARGS__); \ +} /* Bad idea, works only when (e)->errp != NULL: */ #define err_is_set(e) ((e)->errp && *(e)->errp) /* To lift this restriction, error_propagate(), like we do in QEMU code */ @@ -52,18 +57,20 @@ static struct QGAVSSContext { STDAPI requester_init(void) { + qga_debug_begin; + COMInitializer initializer; /* to call CoInitializeSecurity */ HRESULT hr = CoInitializeSecurity( NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL); if (FAILED(hr)) { - fprintf(stderr, "failed to CoInitializeSecurity (error %lx)\n", hr); + qga_debug("failed to CoInitializeSecurity (error %lx)", hr); return hr; } hLib = LoadLibraryA("VSSAPI.DLL"); if (!hLib) { - fprintf(stderr, "failed to load VSSAPI.DLL\n"); + qga_debug("failed to load VSSAPI.DLL"); return HRESULT_FROM_WIN32(GetLastError()); } @@ -76,22 +83,25 @@ STDAPI requester_init(void) #endif ); if (!pCreateVssBackupComponents) { - fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n"); + qga_debug("failed to get proc address from VSSAPI.DLL"); return HRESULT_FROM_WIN32(GetLastError()); } pVssFreeSnapshotProperties = (t_VssFreeSnapshotProperties) GetProcAddress(hLib, "VssFreeSnapshotProperties"); if (!pVssFreeSnapshotProperties) { - fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n"); + qga_debug("failed to get proc address from VSSAPI.DLL"); return HRESULT_FROM_WIN32(GetLastError()); } + qga_debug_end; return S_OK; } static void requester_cleanup(void) { + qga_debug_begin; + if (vss_ctx.hEventFrozen) { CloseHandle(vss_ctx.hEventFrozen); vss_ctx.hEventFrozen = NULL; @@ -113,10 +123,13 @@ static void requester_cleanup(void) vss_ctx.pVssbc = NULL; } vss_ctx.cFrozenVols = 0; + qga_debug_end; } STDAPI requester_deinit(void) { + qga_debug_begin; + requester_cleanup(); pCreateVssBackupComponents = NULL; @@ -126,11 +139,14 @@ STDAPI requester_deinit(void) hLib = NULL; } + qga_debug_end; return S_OK; } static HRESULT WaitForAsync(IVssAsync *pAsync) { + qga_debug_begin; + HRESULT ret, hr; do { @@ -146,11 +162,14 @@ static HRESULT WaitForAsync(IVssAsync *pAsync) } } while (ret == VSS_S_ASYNC_PENDING); + qga_debug_end; return ret; } static void AddComponents(ErrorSet *errset) { + qga_debug_begin; + unsigned int cWriters, i; VSS_ID id, idInstance, idWriter; BSTR bstrWriterName = NULL; @@ -232,10 +251,55 @@ out: if (pComponent && info) { pComponent->FreeComponentInfo(info); } + qga_debug_end; +} + +DWORD get_reg_dword_value(HKEY baseKey, LPCSTR subKey, LPCSTR valueName, + DWORD defaultData) +{ + qga_debug_begin; + + DWORD regGetValueError; + DWORD dwordData; + DWORD dataSize = sizeof(DWORD); + + regGetValueError = RegGetValue(baseKey, subKey, valueName, RRF_RT_DWORD, + NULL, &dwordData, &dataSize); + qga_debug_end; + if (regGetValueError != ERROR_SUCCESS) { + return defaultData; + } + return dwordData; +} + +bool is_valid_vss_backup_type(VSS_BACKUP_TYPE vssBT) +{ + return (vssBT > VSS_BT_UNDEFINED && vssBT < VSS_BT_OTHER); +} + +VSS_BACKUP_TYPE get_vss_backup_type( + VSS_BACKUP_TYPE defaultVssBT = DEFAULT_VSS_BACKUP_TYPE) +{ + qga_debug_begin; + + VSS_BACKUP_TYPE vssBackupType; + + vssBackupType = static_cast( + get_reg_dword_value(HKEY_LOCAL_MACHINE, + QGA_PROVIDER_REGISTRY_ADDRESS, + "VssOption", + defaultVssBT)); + qga_debug_end; + if (!is_valid_vss_backup_type(vssBackupType)) { + return defaultVssBT; + } + return vssBackupType; } void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset) { + qga_debug_begin; + COMPointer pAsync; HANDLE volume; HRESULT hr; @@ -247,9 +311,11 @@ void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset) DWORD wait_status; int num_fixed_drives = 0, i; int num_mount_points = 0; + VSS_BACKUP_TYPE vss_bt = get_vss_backup_type(); if (vss_ctx.pVssbc) { /* already frozen */ *num_vols = 0; + qga_debug("finished, already frozen"); return; } @@ -294,7 +360,7 @@ void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset) goto out; } - hr = vss_ctx.pVssbc->SetBackupState(true, true, VSS_BT_FULL, false); + hr = vss_ctx.pVssbc->SetBackupState(true, true, vss_bt, false); if (FAILED(hr)) { err_set(errset, hr, "failed to set backup state"); goto out; @@ -407,6 +473,7 @@ void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset) } } + qga_debug("preparing for backup"); hr = vss_ctx.pVssbc->PrepareForBackup(pAsync.replace()); if (SUCCEEDED(hr)) { hr = WaitForAsync(pAsync); @@ -430,6 +497,7 @@ void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset) * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen * after the applications and filesystems are frozen. */ + qga_debug("do snapshot set"); hr = vss_ctx.pVssbc->DoSnapshotSet(&vss_ctx.pAsyncSnapshot); if (FAILED(hr)) { err_set(errset, hr, "failed to do snapshot set"); @@ -476,6 +544,7 @@ void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset) *num_vols = vss_ctx.cFrozenVols = num_fixed_drives; } + qga_debug("end successful"); return; out: @@ -486,11 +555,14 @@ out: out1: requester_cleanup(); CoUninitialize(); + + qga_debug_end; } void requester_thaw(int *num_vols, void *mountpints, ErrorSet *errset) { + qga_debug_begin; COMPointer pAsync; if (!vss_ctx.hEventThaw) { @@ -499,6 +571,8 @@ void requester_thaw(int *num_vols, void *mountpints, ErrorSet *errset) * and no volumes must be frozen. We return without an error. */ *num_vols = 0; + qga_debug("finished, no volumes were frozen"); + return; } @@ -555,4 +629,6 @@ void requester_thaw(int *num_vols, void *mountpints, ErrorSet *errset) CoUninitialize(); StopService(); + + qga_debug_end; } diff --git a/qga/vss-win32/vss-debug.cpp b/qga/vss-win32/vss-debug.cpp new file mode 100644 index 0000000000..820b1c6667 --- /dev/null +++ b/qga/vss-win32/vss-debug.cpp @@ -0,0 +1,39 @@ +/* + * QEMU Guest Agent VSS debug declarations + * + * Copyright (C) 2023 Red Hat Inc + * + * Authors: + * Konstantin Kostiuk + * + * 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 "vss-debug.h" +#include "vss-common.h" + +void qga_debug_internal(const char *funcname, const char *fmt, ...) +{ + char user_string[512] = {0}; + char full_string[640] = {0}; + + va_list args; + va_start(args, fmt); + if (vsnprintf(user_string, _countof(user_string), fmt, args) <= 0) { + va_end(args); + return; + } + + va_end(args); + + if (snprintf(full_string, _countof(full_string), + QGA_PROVIDER_NAME "[%lu]: %s %s\n", + GetCurrentThreadId(), funcname, user_string) <= 0) { + return; + } + + OutputDebugString(full_string); + fputs(full_string, stderr); +} diff --git a/qga/vss-win32/vss-debug.h b/qga/vss-win32/vss-debug.h new file mode 100644 index 0000000000..7800457392 --- /dev/null +++ b/qga/vss-win32/vss-debug.h @@ -0,0 +1,25 @@ +/* + * QEMU Guest Agent VSS debug declarations + * + * Copyright (C) 2023 Red Hat Inc + * + * Authors: + * Konstantin Kostiuk + * + * 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 + +#ifndef VSS_DEBUG_H +#define VSS_DEBUG_H + +void qga_debug_internal(const char *funcname, const char *fmt, ...) G_GNUC_PRINTF(2, 3); + +#define qga_debug(fmt, ...) qga_debug_internal(__func__, fmt, ## __VA_ARGS__) +#define qga_debug_begin qga_debug("begin") +#define qga_debug_end qga_debug("end") + +#endif diff --git a/qga/vss-win32/vss-handles.h b/qga/vss-win32/vss-handles.h index 0f8a741ad2..1a7d842129 100644 --- a/qga/vss-win32/vss-handles.h +++ b/qga/vss-win32/vss-handles.h @@ -6,6 +6,9 @@ #define QGA_PROVIDER_NAME "QEMU Guest Agent VSS Provider" #define QGA_PROVIDER_LNAME L(QGA_PROVIDER_NAME) #define QGA_PROVIDER_VERSION L(QEMU_VERSION) +#define QGA_PROVIDER_REGISTRY_ADDRESS "SYSTEM\\CurrentControlSet"\ + "\\Services"\ + "\\" QGA_PROVIDER_NAME #define EVENT_NAME_FROZEN "Global\\QGAVSSEvent-frozen" #define EVENT_NAME_THAW "Global\\QGAVSSEvent-thaw" diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c index 632320d72d..51341d96e4 100644 --- a/qobject/json-lexer.c +++ b/qobject/json-lexer.c @@ -139,7 +139,7 @@ static const uint8_t json_lexer[][256] = { * bytes '\xFE', '\xFF'. Structural characters and line * endings are promising resynchronization points. Clients * may use the others to force the JSON parser into known-good - * state; see docs/interop/qmp-spec.txt. + * state; see docs/interop/qmp-spec.rst. */ [0 ... 0x1F] = IN_START | LOOKAHEAD, [0x20 ... 0xFD] = IN_RECOVERY, diff --git a/qom/meson.build b/qom/meson.build index 062a3789d8..8192243430 100644 --- a/qom/meson.build +++ b/qom/meson.build @@ -7,4 +7,4 @@ qom_ss.add(files( )) qmp_ss.add(files('qom-qmp-cmds.c')) -softmmu_ss.add(files('qom-hmp-cmds.c')) +system_ss.add(files('qom-hmp-cmds.c')) diff --git a/qom/object.c b/qom/object.c index d34608558e..d4a001cf41 100644 --- a/qom/object.c +++ b/qom/object.c @@ -31,6 +31,7 @@ * of the QOM core on QObject? */ #include "qom/qom-qobject.h" #include "qapi/qmp/qbool.h" +#include "qapi/qmp/qlist.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qemu/error-report.h" @@ -137,9 +138,46 @@ static TypeImpl *type_new(const TypeInfo *info) return ti; } +static bool type_name_is_valid(const char *name) +{ + const int slen = strlen(name); + int plen; + + g_assert(slen > 1); + + /* + * Ideally, the name should start with a letter - however, we've got + * too many names starting with a digit already, so allow digits here, + * too (except '0' which is not used yet) + */ + if (!g_ascii_isalnum(name[0]) || name[0] == '0') { + return false; + } + + plen = strspn(name, "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789-_."); + + /* Allow some legacy names with '+' in it for compatibility reasons */ + if (name[plen] == '+') { + if (plen >= 17 && g_str_has_prefix(name, "Sun-UltraSparc-I")) { + /* Allow "Sun-UltraSparc-IV+" and "Sun-UltraSparc-IIIi+" */ + return true; + } + } + + return plen == slen; +} + static TypeImpl *type_register_internal(const TypeInfo *info) { TypeImpl *ti; + + if (!type_name_is_valid(info->name)) { + fprintf(stderr, "Registering '%s' with illegal type name\n", info->name); + abort(); + } + ti = type_new(info); type_table_add(ti); @@ -220,6 +258,19 @@ static size_t type_object_get_size(TypeImpl *ti) return 0; } +static size_t type_object_get_align(TypeImpl *ti) +{ + if (ti->instance_align) { + return ti->instance_align; + } + + if (type_has_parent(ti)) { + return type_object_get_align(type_get_parent(ti)); + } + + return 0; +} + size_t object_type_get_instance_size(const char *typename) { TypeImpl *type = type_get_by_name(typename); @@ -293,6 +344,7 @@ static void type_initialize(TypeImpl *ti) ti->class_size = type_class_get_size(ti); ti->instance_size = type_object_get_size(ti); + ti->instance_align = type_object_get_align(ti); /* Any type with zero instance_size is implicitly abstract. * This means interface types are all abstract. */ @@ -526,8 +578,13 @@ void object_initialize(void *data, size_t size, const char *typename) #ifdef CONFIG_MODULES if (!type) { - module_load_qom_one(typename); - type = type_get_by_name(typename); + int rv = module_load_qom(typename, &error_fatal); + if (rv > 0) { + type = type_get_by_name(typename); + } else { + error_report("missing object type '%s'", typename); + exit(1); + } } #endif if (!type) { @@ -1033,8 +1090,13 @@ ObjectClass *module_object_class_by_name(const char *typename) oc = object_class_by_name(typename); #ifdef CONFIG_MODULES if (!oc) { - module_load_qom_one(typename); - oc = object_class_by_name(typename); + Error *local_err = NULL; + int rv = module_load_qom(typename, &local_err); + if (rv > 0) { + oc = object_class_by_name(typename); + } else if (rv < 0) { + error_report_err(local_err); + } } #endif return oc; @@ -1383,7 +1445,8 @@ bool object_property_get(Object *obj, const char *name, Visitor *v, } if (!prop->get) { - error_setg(errp, QERR_PERMISSION_DENIED); + error_setg(errp, "Property '%s.%s' is not readable", + object_get_typename(obj), name); return false; } prop->get(obj, v, name, prop->opaque, &err); @@ -1402,7 +1465,8 @@ bool object_property_set(Object *obj, const char *name, Visitor *v, } if (!prop->set) { - error_setg(errp, QERR_PERMISSION_DENIED); + error_setg(errp, "Property '%s.%s' is not writable", + object_get_typename(obj), name); return false; } prop->set(obj, v, name, prop->opaque, errp); @@ -1562,6 +1626,11 @@ 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_list(ObjectProperty *prop) +{ + object_property_set_default(prop, QOBJECT(qlist_new())); +} + void object_property_set_default_int(ObjectProperty *prop, int64_t value) { object_property_set_default(prop, QOBJECT(qnum_from_int(value))); @@ -2160,6 +2229,22 @@ Object *object_resolve_path_at(Object *parent, const char *path) return object_resolve_abs_path(parent, parts, TYPE_OBJECT); } +Object *object_resolve_type_unambiguous(const char *typename, Error **errp) +{ + bool ambig; + Object *o = object_resolve_path_type("", typename, &ambig); + + if (ambig) { + error_setg(errp, "More than one object of type %s", typename); + return NULL; + } + if (!o) { + error_setg(errp, "No object found of type %s", typename); + return NULL; + } + return o; +} + typedef struct StringProperty { char *(*get)(Object *, Error **); diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index f94b6c3193..e0833c8bfe 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -2,8 +2,8 @@ #include "qemu/cutils.h" #include "qapi/error.h" -#include "qapi/qapi-commands-qom.h" #include "qapi/qapi-visit-qom.h" +#include "qapi/qmp/qobject.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qjson.h" @@ -259,7 +259,7 @@ static void user_creatable_print_help_from_qdict(QDict *args) } } -ObjectOptions *user_creatable_parse_str(const char *optarg, Error **errp) +ObjectOptions *user_creatable_parse_str(const char *str, Error **errp) { ERRP_GUARD(); QObject *obj; @@ -267,14 +267,14 @@ ObjectOptions *user_creatable_parse_str(const char *optarg, Error **errp) Visitor *v; ObjectOptions *options; - if (optarg[0] == '{') { - obj = qobject_from_json(optarg, errp); + if (str[0] == '{') { + obj = qobject_from_json(str, errp); if (!obj) { return NULL; } v = qobject_input_visitor_new(obj); } else { - QDict *args = keyval_parse(optarg, "qom-type", &help, errp); + QDict *args = keyval_parse(str, "qom-type", &help, errp); if (*errp) { return NULL; } @@ -295,12 +295,12 @@ ObjectOptions *user_creatable_parse_str(const char *optarg, Error **errp) return options; } -bool user_creatable_add_from_str(const char *optarg, Error **errp) +bool user_creatable_add_from_str(const char *str, Error **errp) { ERRP_GUARD(); ObjectOptions *options; - options = user_creatable_parse_str(optarg, errp); + options = user_creatable_parse_str(str, errp); if (!options) { return false; } @@ -310,9 +310,9 @@ bool user_creatable_add_from_str(const char *optarg, Error **errp) return !*errp; } -void user_creatable_process_cmdline(const char *optarg) +void user_creatable_process_cmdline(const char *cmdline) { - if (!user_creatable_add_from_str(optarg, &error_fatal)) { + if (!user_creatable_add_from_str(cmdline, &error_fatal)) { /* Help was printed */ exit(EXIT_SUCCESS); } diff --git a/qom/qom-hmp-cmds.c b/qom/qom-hmp-cmds.c index 453fbfeabc..6e3a2175a4 100644 --- a/qom/qom-hmp-cmds.c +++ b/qom/qom-hmp-cmds.c @@ -13,7 +13,9 @@ #include "qapi/qapi-commands-qom.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" +#include "qemu/readline.h" #include "qom/object.h" +#include "qom/object_interfaces.h" void hmp_qom_list(Monitor *mon, const QDict *qdict) { @@ -150,3 +152,68 @@ void hmp_info_qom_tree(Monitor *mon, const QDict *dict) } print_qom_composition(mon, obj, 0); } + +void hmp_object_add(Monitor *mon, const QDict *qdict) +{ + const char *options = qdict_get_str(qdict, "object"); + Error *err = NULL; + + user_creatable_add_from_str(options, &err); + hmp_handle_error(mon, err); +} + +void hmp_object_del(Monitor *mon, const QDict *qdict) +{ + const char *id = qdict_get_str(qdict, "id"); + Error *err = NULL; + + user_creatable_del(id, &err); + hmp_handle_error(mon, err); +} + +void object_add_completion(ReadLineState *rs, int nb_args, const char *str) +{ + GSList *list, *elt; + size_t len; + + if (nb_args != 2) { + return; + } + + len = strlen(str); + readline_set_completion_index(rs, len); + list = elt = object_class_get_list(TYPE_USER_CREATABLE, false); + while (elt) { + const char *name; + + name = object_class_get_name(OBJECT_CLASS(elt->data)); + if (strcmp(name, TYPE_USER_CREATABLE)) { + readline_add_completion_of(rs, str, name); + } + elt = elt->next; + } + g_slist_free(list); +} + +void object_del_completion(ReadLineState *rs, int nb_args, const char *str) +{ + ObjectPropertyInfoList *list, *start; + size_t len; + + if (nb_args != 2) { + return; + } + len = strlen(str); + readline_set_completion_index(rs, len); + + start = list = qmp_qom_list("/objects", NULL); + while (list) { + ObjectPropertyInfo *info = list->value; + + if (!strncmp(info->type, "child<", 5)) { + readline_add_completion_of(rs, str, info->name); + } + list = list->next; + } + qapi_free_ObjectPropertyInfoList(start); +} diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c index 2e63a4c184..7c087299de 100644 --- a/qom/qom-qmp-cmds.c +++ b/qom/qom-qmp-cmds.c @@ -99,15 +99,13 @@ static void qom_list_types_tramp(ObjectClass *klass, void *data) info->name = g_strdup(object_class_get_name(klass)); info->has_abstract = info->abstract = object_class_is_abstract(klass); if (parent) { - info->has_parent = true; info->parent = g_strdup(object_class_get_name(parent)); } QAPI_LIST_PREPEND(*pret, info); } -ObjectTypeInfoList *qmp_qom_list_types(bool has_implements, - const char *implements, +ObjectTypeInfoList *qmp_qom_list_types(const char *implements, bool has_abstract, bool abstract, Error **errp) @@ -168,10 +166,8 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename, 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; QAPI_LIST_PREPEND(prop_list, info); } @@ -215,7 +211,6 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename, info = g_malloc0(sizeof(*info)); info->name = g_strdup(prop->name); info->type = g_strdup(prop->type); - info->has_description = !!prop->description; info->description = g_strdup(prop->description); QAPI_LIST_PREPEND(prop_list, info); diff --git a/replay/meson.build b/replay/meson.build index 21aefad220..4b4175e8dd 100644 --- a/replay/meson.build +++ b/replay/meson.build @@ -1,4 +1,4 @@ -softmmu_ss.add(when: 'CONFIG_TCG', if_true: files( +system_ss.add(when: 'CONFIG_TCG', if_true: files( 'replay.c', 'replay-internal.c', 'replay-events.c', diff --git a/replay/replay-char.c b/replay/replay-char.c index d2025948cf..72b1f832dd 100644 --- a/replay/replay-char.c +++ b/replay/replay-char.c @@ -48,7 +48,7 @@ void replay_register_char_driver(Chardev *chr) char_drivers[drivers_count++] = chr; } -void replay_chr_be_write(Chardev *s, uint8_t *buf, int len) +void replay_chr_be_write(Chardev *s, const uint8_t *buf, int len) { CharEvent *event = g_new0(CharEvent, 1); @@ -113,8 +113,7 @@ void replay_char_write_event_load(int *res, int *offset) *offset = replay_get_dword(); replay_finish_event(); } else { - error_report("Missing character write event in the replay log"); - exit(1); + replay_sync_error("Missing character write event in the replay log"); } } @@ -135,8 +134,7 @@ int replay_char_read_all_load(uint8_t *buf) replay_finish_event(); return res; } else { - error_report("Missing character read all event in the replay log"); - exit(1); + replay_sync_error("Missing character read all event in the replay log"); } } diff --git a/replay/replay-debugging.c b/replay/replay-debugging.c index 1cde50e9f3..82c66fff26 100644 --- a/replay/replay-debugging.c +++ b/replay/replay-debugging.c @@ -50,7 +50,6 @@ ReplayInfo *qmp_query_replay(Error **errp) retval->mode = replay_mode; if (replay_get_filename()) { retval->filename = g_strdup(replay_get_filename()); - retval->has_filename = true; } retval->icount = replay_get_current_icount(); return retval; @@ -145,7 +144,6 @@ static char *replay_find_nearest_snapshot(int64_t icount, char *ret = NULL; int rv; int nb_sns, i; - AioContext *aio_context; *snapshot_icount = -1; @@ -153,11 +151,8 @@ static char *replay_find_nearest_snapshot(int64_t icount, if (!bs) { goto fail; } - 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); for (i = 0; i < nb_sns; i++) { rv = bdrv_all_has_snapshot(sn_tab[i].name, false, NULL, NULL); diff --git a/replay/replay-input.c b/replay/replay-input.c index 1147e3d34e..bee3dbe528 100644 --- a/replay/replay-input.c +++ b/replay/replay-input.c @@ -22,6 +22,7 @@ void replay_save_input_event(InputEvent *evt) InputKeyEvent *key; InputBtnEvent *btn; InputMoveEvent *move; + InputMultiTouchEvent *mtt; replay_put_dword(evt->type); switch (evt->type) { @@ -58,6 +59,14 @@ void replay_save_input_event(InputEvent *evt) replay_put_dword(move->axis); replay_put_qword(move->value); break; + case INPUT_EVENT_KIND_MTT: + mtt = evt->u.mtt.data; + replay_put_dword(mtt->type); + replay_put_qword(mtt->slot); + replay_put_qword(mtt->tracking_id); + replay_put_dword(mtt->axis); + replay_put_qword(mtt->value); + break; case INPUT_EVENT_KIND__MAX: /* keep gcc happy */ break; @@ -73,6 +82,7 @@ InputEvent *replay_read_input_event(void) InputBtnEvent btn; InputMoveEvent rel; InputMoveEvent abs; + InputMultiTouchEvent mtt; evt.type = replay_get_dword(); switch (evt.type) { @@ -109,6 +119,14 @@ InputEvent *replay_read_input_event(void) evt.u.abs.data->axis = (InputAxis)replay_get_dword(); evt.u.abs.data->value = replay_get_qword(); break; + case INPUT_EVENT_KIND_MTT: + evt.u.mtt.data = &mtt; + evt.u.mtt.data->type = (InputMultiTouchType)replay_get_dword(); + evt.u.mtt.data->slot = replay_get_qword(); + evt.u.mtt.data->tracking_id = replay_get_qword(); + evt.u.mtt.data->axis = (InputAxis)replay_get_dword(); + evt.u.mtt.data->value = replay_get_qword(); + break; case INPUT_EVENT_KIND__MAX: /* keep gcc happy */ break; diff --git a/replay/replay-internal.c b/replay/replay-internal.c index 77d0c82327..13fcbdd8f4 100644 --- a/replay/replay-internal.c +++ b/replay/replay-internal.c @@ -175,11 +175,12 @@ void replay_fetch_data_kind(void) if (replay_file) { if (!replay_state.has_unread_data) { replay_state.data_kind = replay_get_byte(); + replay_state.current_event++; if (replay_state.data_kind == EVENT_INSTRUCTION) { replay_state.instruction_count = replay_get_dword(); } replay_check_error(); - replay_state.has_unread_data = 1; + replay_state.has_unread_data = true; if (replay_state.data_kind >= EVENT_COUNT) { error_report("Replay: unknown event kind %d", replay_state.data_kind); @@ -191,7 +192,7 @@ void replay_fetch_data_kind(void) void replay_finish_event(void) { - replay_state.has_unread_data = 0; + replay_state.has_unread_data = false; replay_fetch_data_kind(); } @@ -216,7 +217,7 @@ void replay_mutex_lock(void) { if (replay_mode != REPLAY_MODE_NONE) { unsigned long id; - g_assert(!qemu_mutex_iothread_locked()); + g_assert(!bql_locked()); g_assert(!replay_mutex_locked()); qemu_mutex_lock(&lock); id = mutex_tail++; diff --git a/replay/replay-internal.h b/replay/replay-internal.h index 89e377be90..75249b7693 100644 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -25,7 +25,12 @@ typedef enum ReplayAsyncEventKind { REPLAY_ASYNC_COUNT } ReplayAsyncEventKind; -/* Any changes to order/number of events will need to bump REPLAY_VERSION */ +/* + * Any changes to order/number of events will need to bump + * REPLAY_VERSION to prevent confusion with old logs. Also don't + * forget to update replay_event_name() to make your debugging life + * easier. + */ enum ReplayEvents { /* for instruction event */ EVENT_INSTRUCTION, @@ -63,26 +68,33 @@ enum ReplayEvents { EVENT_COUNT }; +/** + * typedef ReplayState - global tracking Replay state + * + * This structure tracks where we are in the current ReplayState + * including the logged events from the recorded replay stream. Some + * of the data is also stored/restored from VMStateDescription when VM + * save/restore events take place. + * + * @cached_clock: Cached clocks values + * @current_icount: number of processed instructions + * @instruction_count: number of instructions until next event + * @current_event: current event index + * @data_kind: current event + * @has_unread_data: true if event not yet processed + * @file_offset: offset into replay log at replay snapshot + * @block_request_id: current serialised block request id + * @read_event_id: current async read event id + */ typedef struct ReplayState { - /*! Cached clock values. */ int64_t cached_clock[REPLAY_CLOCK_COUNT]; - /*! Current icount - number of processed instructions. */ uint64_t current_icount; - /*! Number of instructions to be executed before other events happen. */ int instruction_count; - /*! Type of the currently executed event. */ + unsigned int current_event; unsigned int data_kind; - /*! Flag which indicates that event is not processed yet. */ - unsigned int has_unread_data; - /*! Temporary variable for saving current log offset. */ + bool has_unread_data; uint64_t file_offset; - /*! Next block operation id. - This counter is global, because requests from different - block devices should not get overlapping ids. */ uint64_t block_request_id; - /*! Prior value of the host clock */ - uint64_t host_clock_last; - /*! Asynchronous event id read from the log */ uint64_t read_event_id; } ReplayState; extern ReplayState replay_state; @@ -136,7 +148,7 @@ bool replay_next_event_is(int event); /*! Reads next clock value from the file. If clock kind read from the file is different from the parameter, the value is not used. */ -void replay_read_next_clock(unsigned int kind); +void replay_read_next_clock(ReplayClockKind kind); /* Asynchronous events queue */ @@ -183,6 +195,16 @@ void replay_event_net_save(void *opaque); /*! Reads network from the file. */ void *replay_event_net_load(void); +/* Diagnostics */ + +/** + * replay_sync_error(): report sync error and exit + * + * When we reach an error condition we want to report it centrally so + * we can also dump some useful information into the logs. + */ +G_NORETURN void replay_sync_error(const char *error); + /* VMState-related functions */ /* Registers replay VMState. diff --git a/replay/replay-snapshot.c b/replay/replay-snapshot.c index 10a7cf7992..ccb4d89dda 100644 --- a/replay/replay-snapshot.c +++ b/replay/replay-snapshot.c @@ -47,16 +47,17 @@ static int replay_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_replay = { .name = "replay", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .pre_save = replay_pre_save, .post_load = replay_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64_ARRAY(cached_clock, ReplayState, REPLAY_CLOCK_COUNT), VMSTATE_UINT64(current_icount, ReplayState), VMSTATE_INT32(instruction_count, ReplayState), + VMSTATE_UINT32(current_event, ReplayState), VMSTATE_UINT32(data_kind, ReplayState), - VMSTATE_UINT32(has_unread_data, ReplayState), + VMSTATE_BOOL(has_unread_data, ReplayState), VMSTATE_UINT64(file_offset, ReplayState), VMSTATE_UINT64(block_request_id, ReplayState), VMSTATE_UINT64(read_event_id, ReplayState), diff --git a/replay/replay-time.c b/replay/replay-time.c index 00ebcb7a49..ee0ebfcf09 100644 --- a/replay/replay-time.c +++ b/replay/replay-time.c @@ -48,7 +48,6 @@ void replay_read_next_clock(ReplayClockKind kind) /*! Reads next clock event from the input. */ int64_t replay_read_clock(ReplayClockKind kind, int64_t raw_icount) { - int64_t ret; g_assert(replay_file && replay_mutex_locked()); replay_advance_current_icount(raw_icount); @@ -56,7 +55,5 @@ int64_t replay_read_clock(ReplayClockKind kind, int64_t raw_icount) if (replay_next_event_is(EVENT_CLOCK + kind)) { replay_read_next_clock(kind); } - ret = replay_state.cached_clock[kind]; - - return ret; + return replay_state.cached_clock[kind]; } diff --git a/replay/replay.c b/replay/replay.c index 4c396bb376..a2c576c16e 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -38,6 +38,107 @@ static GSList *replay_blockers; uint64_t replay_break_icount = -1ULL; QEMUTimer *replay_break_timer; +/* Pretty print event names */ + +static const char *replay_async_event_name(ReplayAsyncEventKind event) +{ + switch (event) { +#define ASYNC_EVENT(_x) case REPLAY_ASYNC_EVENT_ ## _x: return "ASYNC_EVENT_"#_x + ASYNC_EVENT(BH); + ASYNC_EVENT(BH_ONESHOT); + ASYNC_EVENT(INPUT); + ASYNC_EVENT(INPUT_SYNC); + ASYNC_EVENT(CHAR_READ); + ASYNC_EVENT(BLOCK); + ASYNC_EVENT(NET); +#undef ASYNC_EVENT + default: + g_assert_not_reached(); + } +} + +static const char *replay_clock_event_name(ReplayClockKind clock) +{ + switch (clock) { +#define CLOCK_EVENT(_x) case REPLAY_CLOCK_ ## _x: return "CLOCK_" #_x + CLOCK_EVENT(HOST); + CLOCK_EVENT(VIRTUAL_RT); +#undef CLOCK_EVENT + default: + g_assert_not_reached(); + } +} + +/* Pretty print shutdown event names */ +static const char *replay_shutdown_event_name(ShutdownCause cause) +{ + switch (cause) { +#define SHUTDOWN_EVENT(_x) case SHUTDOWN_CAUSE_ ## _x: return "SHUTDOWN_CAUSE_" #_x + SHUTDOWN_EVENT(NONE); + SHUTDOWN_EVENT(HOST_ERROR); + SHUTDOWN_EVENT(HOST_QMP_QUIT); + SHUTDOWN_EVENT(HOST_QMP_SYSTEM_RESET); + SHUTDOWN_EVENT(HOST_SIGNAL); + SHUTDOWN_EVENT(HOST_UI); + SHUTDOWN_EVENT(GUEST_SHUTDOWN); + SHUTDOWN_EVENT(GUEST_RESET); + SHUTDOWN_EVENT(GUEST_PANIC); + SHUTDOWN_EVENT(SUBSYSTEM_RESET); + SHUTDOWN_EVENT(SNAPSHOT_LOAD); +#undef SHUTDOWN_EVENT + default: + g_assert_not_reached(); + } +} + +static const char *replay_checkpoint_event_name(enum ReplayCheckpoint checkpoint) +{ + switch (checkpoint) { +#define CHECKPOINT_EVENT(_x) case CHECKPOINT_ ## _x: return "CHECKPOINT_" #_x + CHECKPOINT_EVENT(CLOCK_WARP_START); + CHECKPOINT_EVENT(CLOCK_WARP_ACCOUNT); + CHECKPOINT_EVENT(RESET_REQUESTED); + CHECKPOINT_EVENT(SUSPEND_REQUESTED); + CHECKPOINT_EVENT(CLOCK_VIRTUAL); + CHECKPOINT_EVENT(CLOCK_HOST); + CHECKPOINT_EVENT(CLOCK_VIRTUAL_RT); + CHECKPOINT_EVENT(INIT); + CHECKPOINT_EVENT(RESET); +#undef CHECKPOINT_EVENT + default: + g_assert_not_reached(); + } +} + +static const char *replay_event_name(enum ReplayEvents event) +{ + /* First deal with the simple ones */ + switch (event) { +#define EVENT(_x) case EVENT_ ## _x: return "EVENT_"#_x + EVENT(INSTRUCTION); + EVENT(INTERRUPT); + EVENT(EXCEPTION); + EVENT(CHAR_WRITE); + EVENT(CHAR_READ_ALL); + EVENT(AUDIO_OUT); + EVENT(AUDIO_IN); + EVENT(RANDOM); +#undef EVENT + default: + if (event >= EVENT_ASYNC && event <= EVENT_ASYNC_LAST) { + return replay_async_event_name(event - EVENT_ASYNC); + } else if (event >= EVENT_SHUTDOWN && event <= EVENT_SHUTDOWN_LAST) { + return replay_shutdown_event_name(event - EVENT_SHUTDOWN); + } else if (event >= EVENT_CLOCK && event <= EVENT_CLOCK_LAST) { + return replay_clock_event_name(event - EVENT_CLOCK); + } else if (event >= EVENT_CHECKPOINT && event <= EVENT_CHECKPOINT_LAST) { + return replay_checkpoint_event_name(event - EVENT_CHECKPOINT); + } + } + + g_assert_not_reached(); +} + bool replay_next_event_is(int event) { bool res = false; @@ -74,7 +175,7 @@ uint64_t replay_get_current_icount(void) int replay_get_instructions(void) { int res = 0; - replay_mutex_lock(); + g_assert(replay_mutex_locked()); if (replay_next_event_is(EVENT_INSTRUCTION)) { res = replay_state.instruction_count; if (replay_break_icount != -1LL) { @@ -85,7 +186,6 @@ int replay_get_instructions(void) } } } - replay_mutex_unlock(); return res; } @@ -227,6 +327,15 @@ bool replay_has_event(void) return res; } +G_NORETURN void replay_sync_error(const char *error) +{ + error_report("%s (insn total %"PRId64"/%d left, event %d is %s)", error, + replay_state.current_icount, replay_state.instruction_count, + replay_state.current_event, + replay_event_name(replay_state.data_kind)); + abort(); +} + static void replay_enable(const char *fname, int mode) { const char *fmode = NULL; @@ -259,6 +368,7 @@ static void replay_enable(const char *fname, int mode) replay_state.data_kind = -1; replay_state.instruction_count = 0; replay_state.current_icount = 0; + replay_state.current_event = 0; replay_state.has_unread_data = 0; /* skip file header for RECORD and check it for PLAY */ @@ -339,6 +449,27 @@ void replay_start(void) replay_enable_events(); } +/* + * For none/record the answer is yes. + */ +bool replay_can_wait(void) +{ + if (replay_mode == REPLAY_MODE_PLAY) { + /* + * For playback we shouldn't ever be at a point we wait. If + * the instruction count has reached zero and we have an + * unconsumed event we should go around again and consume it. + */ + if (replay_state.instruction_count == 0 && replay_state.has_unread_data) { + return false; + } else { + replay_sync_error("Playback shouldn't have to iowait"); + } + } + return true; +} + + void replay_finish(void) { if (replay_mode == REPLAY_MODE_NONE) { @@ -366,10 +497,8 @@ void replay_finish(void) fclose(replay_file); replay_file = NULL; } - if (replay_filename) { - g_free(replay_filename); - replay_filename = NULL; - } + g_free(replay_filename); + replay_filename = NULL; g_free(replay_snapshot); replay_snapshot = NULL; @@ -378,8 +507,12 @@ void replay_finish(void) replay_mode = REPLAY_MODE_NONE; } -void replay_add_blocker(Error *reason) +void replay_add_blocker(const char *feature) { + Error *reason = NULL; + + error_setg(&reason, "Record/replay is not supported with %s", + feature); replay_blockers = g_slist_prepend(replay_blockers, reason); } diff --git a/replay/stubs-system.c b/replay/stubs-system.c index 5c262b08f1..50cefdb2d6 100644 --- a/replay/stubs-system.c +++ b/replay/stubs-system.c @@ -12,7 +12,7 @@ void replay_input_sync_event(void) qemu_input_event_sync_impl(); } -void replay_add_blocker(Error *reason) +void replay_add_blocker(const char *feature) { } void replay_audio_in(size_t *recorded, void *samples, size_t *wpos, size_t size) diff --git a/roms/Makefile b/roms/Makefile index 5e44d97890..dfed2b216a 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -41,8 +41,8 @@ x86_64_cross_prefix := $(call find-cross-prefix,x86_64) riscv32_cross_prefix := $(call find-cross-prefix,riscv32) riscv64_cross_prefix := $(call find-cross-prefix,riscv64) -# tag our seabios builds -SEABIOS_EXTRAVERSION="-prebuilt.qemu.org" +# tag our firmware builds +FIRMWARE_EXTRAVERSION = -prebuilt.qemu.org # # EfiRom utility is shipped with edk2 / tianocore, in BaseTools/ @@ -52,12 +52,13 @@ SEABIOS_EXTRAVERSION="-prebuilt.qemu.org" # EDK2_EFIROM = edk2/BaseTools/Source/C/bin/EfiRom +-include edk2-version + default help: @echo "nothing is build by default" @echo "available build targets:" @echo " bios -- update bios.bin (seabios)" @echo " vgabios -- update vgabios binaries (seabios)" - @echo " sgabios -- update sgabios binaries" @echo " pxerom -- update nic roms (bios only)" @echo " efirom -- update nic roms (bios+efi)" @echo " slof -- update slof.bin" @@ -69,6 +70,7 @@ default help: @echo " opensbi32-generic -- update OpenSBI for 32-bit generic machine" @echo " opensbi64-generic -- update OpenSBI for 64-bit generic machine" @echo " qboot -- update qboot" + @echo " hppa-firmware -- update 32- and 64-bit hppa firmware" @echo " clean -- delete the files generated by the previous" \ "build targets" @@ -91,22 +93,18 @@ build-seabios-config-%: config.% mkdir -p seabios/builds/$* cp $< seabios/builds/$*/.config $(MAKE) -C seabios \ - EXTRAVERSION=$(SEABIOS_EXTRAVERSION) \ + EXTRAVERSION=$(FIRMWARE_EXTRAVERSION) \ CROSS_PREFIX=$(x86_64_cross_prefix) \ KCONFIG_CONFIG=$(CURDIR)/seabios/builds/$*/.config \ OUT=$(CURDIR)/seabios/builds/$*/ oldnoconfig $(MAKE) -C seabios \ - EXTRAVERSION=$(SEABIOS_EXTRAVERSION) \ + EXTRAVERSION=$(FIRMWARE_EXTRAVERSION) \ CROSS_PREFIX=$(x86_64_cross_prefix) \ KCONFIG_CONFIG=$(CURDIR)/seabios/builds/$*/.config \ OUT=$(CURDIR)/seabios/builds/$*/ all -.PHONY: sgabios skiboot qboot -sgabios: - $(MAKE) -C sgabios - cp sgabios/sgabios.bin ../pc-bios - +.PHONY: skiboot qboot pxerom: $(patsubst %,pxe-rom-%,$(pxerom_variants)) @@ -131,25 +129,6 @@ build-efi-roms: build-pxe-roms CROSS_COMPILE=$(x86_64_cross_prefix) \ $(patsubst %,bin-x86_64-efi/%.efidrv,$(pxerom_targets)) -# Build scripts can pass compiler/linker flags to the EDK2 -# build tools via the EDK2_BASETOOLS_OPTFLAGS (CFLAGS) and -# EDK2_BASETOOLS_LDFLAGS (LDFLAGS) environment variables. -# -# Example: -# -# make -C roms \ -# EDK2_BASETOOLS_OPTFLAGS='...' \ -# EDK2_BASETOOLS_LDFLAGS='...' \ -# efirom -# -edk2-basetools: - cd edk2/BaseTools && git submodule update --init --force \ - Source/C/BrotliCompress/brotli - $(MAKE) -C edk2/BaseTools \ - PYTHON_COMMAND=$${EDK2_PYTHON_COMMAND:-python3} \ - EXTRA_OPTFLAGS='$(EDK2_BASETOOLS_OPTFLAGS)' \ - EXTRA_LDFLAGS='$(EDK2_BASETOOLS_LDFLAGS)' - slof: $(MAKE) -C SLOF CROSS=$(powerpc64_cross_prefix) qemu cp SLOF/boot_rom.bin ../pc-bios/slof.bin @@ -170,8 +149,21 @@ skiboot: $(MAKE) -C skiboot CROSS=$(powerpc64_cross_prefix) cp skiboot/skiboot.lid ../pc-bios/skiboot.lid -efi: edk2-basetools - $(MAKE) -f Makefile.edk2 +edk2-version: edk2 + if test -e edk2/.git; then \ + echo "EDK2_STABLE = $$(cd edk2; git describe --tags --match 'edk2-stable*')" > $@; \ + echo "EDK2_DATE = $$(cd edk2; git log -1 --pretty='format:%cd' --date='format:%m/%d/%Y')" >> $@; \ + else \ + touch $@; \ + fi + +efi: edk2-version + $(PYTHON) edk2-build.py --config edk2-build.config \ + --version-override "$(EDK2_STABLE)$(FIRMWARE_EXTRAVERSION)" \ + --release-date "$(EDK2_DATE)" \ + --silent --no-logs + rm -f ../pc-bios/edk2-*.fd.bz2 + bzip2 --verbose ../pc-bios/edk2-*.fd opensbi32-generic: $(MAKE) -C opensbi \ @@ -197,17 +189,22 @@ npcm7xx_bootrom: $(MAKE) -C vbootrom CROSS_COMPILE=$(arm_cross_prefix) cp vbootrom/npcm7xx_bootrom.bin ../pc-bios/npcm7xx_bootrom.bin +hppa-firmware: + $(MAKE) -C seabios-hppa parisc + cp seabios-hppa/out/hppa-firmware.img ../pc-bios/ + cp seabios-hppa/out-64/hppa-firmware64.img ../pc-bios/ + clean: rm -rf seabios/.config seabios/out seabios/builds - $(MAKE) -C sgabios clean - rm -f sgabios/.depend $(MAKE) -C ipxe/src veryclean $(MAKE) -C edk2/BaseTools clean + rm -rf edk2/Conf/{.cache,BuildEnv.sh,build_rule.txt,target.txt,tools_def.txt} $(MAKE) -C SLOF clean rm -rf u-boot/build-e500 $(MAKE) -C u-boot-sam460ex distclean $(MAKE) -C skiboot clean - $(MAKE) -f Makefile.edk2 clean + rm -rf Build $(MAKE) -C opensbi clean $(MAKE) -C qboot clean $(MAKE) -C vbootrom clean + $(MAKE) -C seabios-hppa clean diff --git a/roms/Makefile.edk2 b/roms/Makefile.edk2 deleted file mode 100644 index 485f2244b1..0000000000 --- a/roms/Makefile.edk2 +++ /dev/null @@ -1,178 +0,0 @@ -# Makefile for building firmware binaries and variable store templates for a -# number of virtual platforms in edk2. -# -# Copyright (C) 2019 Red Hat, Inc. -# -# This program and the accompanying materials are licensed and made available -# under the terms and conditions of the BSD License that accompanies this -# distribution. The full text of the license may be found at -# . -# -# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT -# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -SHELL = /bin/bash - -target = RELEASE -toolchain = $(shell source ./edk2-funcs.sh && qemu_edk2_get_toolchain $(1)) - -licenses := \ - edk2/License.txt \ - edk2/License-History.txt \ - edk2/OvmfPkg/License.txt \ - edk2/ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3/COPYING.txt \ - edk2/CryptoPkg/Library/OpensslLib/openssl/LICENSE - -# The "edk2-arm-vars.fd" varstore template is suitable for aarch64 as well. -# Similarly, the "edk2-i386-vars.fd" varstore template is suitable for x86_64 -# as well, independently of "secure" too. -flashdevs := \ - aarch64-code \ - arm-code \ - i386-code \ - i386-secure-code \ - x86_64-code \ - x86_64-secure-code \ - x86_64-microvm \ - \ - arm-vars \ - i386-vars - -all: $(foreach flashdev,$(flashdevs),../pc-bios/edk2-$(flashdev).fd.bz2) \ - ../pc-bios/edk2-licenses.txt - -../pc-bios/edk2-%.fd.bz2: ../pc-bios/edk2-%.fd - bzip2 -9 -c $< > $@ - -# When the build completes, we need not keep the uncompressed flash device -# files. -.INTERMEDIATE: $(foreach flashdev,$(flashdevs),../pc-bios/edk2-$(flashdev).fd) - -# Fetch edk2 submodule's submodules. If it is not in a git tree, assume -# we're building from a tarball and that they've already been fetched by -# make-release/tarball scripts. -submodules: - if test -e edk2/.git; then \ - cd edk2 && git submodule update --init --force -- \ - ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 \ - BaseTools/Source/C/BrotliCompress/brotli \ - CryptoPkg/Library/OpensslLib/openssl \ - MdeModulePkg/Library/BrotliCustomDecompressLib/brotli \ - ; \ - fi - -# See notes on the ".NOTPARALLEL" target and the "+" indicator in -# "tests/uefi-test-tools/Makefile". -.NOTPARALLEL: - -../pc-bios/edk2-aarch64-code.fd: submodules - +./edk2-build.sh \ - aarch64 \ - --arch=AARCH64 \ - --platform=ArmVirtPkg/ArmVirtQemu.dsc \ - -D NETWORK_IP6_ENABLE \ - -D NETWORK_HTTP_BOOT_ENABLE \ - -D NETWORK_TLS_ENABLE \ - -D TPM2_ENABLE \ - -D TPM2_CONFIG_ENABLE - cp edk2/Build/ArmVirtQemu-AARCH64/$(target)_$(call toolchain,aarch64)/FV/QEMU_EFI.fd \ - $@ - truncate --size=64M $@ - -../pc-bios/edk2-arm-code.fd: submodules - +./edk2-build.sh \ - arm \ - --arch=ARM \ - --platform=ArmVirtPkg/ArmVirtQemu.dsc \ - -D NETWORK_IP6_ENABLE \ - -D NETWORK_HTTP_BOOT_ENABLE \ - -D NETWORK_TLS_ENABLE \ - -D TPM2_ENABLE \ - -D TPM2_CONFIG_ENABLE - cp edk2/Build/ArmVirtQemu-ARM/$(target)_$(call toolchain,arm)/FV/QEMU_EFI.fd \ - $@ - truncate --size=64M $@ - -../pc-bios/edk2-i386-code.fd: submodules - +./edk2-build.sh \ - i386 \ - --arch=IA32 \ - --platform=OvmfPkg/OvmfPkgIa32.dsc \ - -D NETWORK_IP6_ENABLE \ - -D NETWORK_HTTP_BOOT_ENABLE \ - -D NETWORK_TLS_ENABLE \ - -D TPM_ENABLE \ - -D TPM_CONFIG_ENABLE - cp edk2/Build/OvmfIa32/$(target)_$(call toolchain,i386)/FV/OVMF_CODE.fd $@ - -../pc-bios/edk2-i386-secure-code.fd: submodules - +./edk2-build.sh \ - i386 \ - --arch=IA32 \ - --platform=OvmfPkg/OvmfPkgIa32.dsc \ - -D NETWORK_IP6_ENABLE \ - -D NETWORK_HTTP_BOOT_ENABLE \ - -D NETWORK_TLS_ENABLE \ - -D TPM_ENABLE \ - -D TPM_CONFIG_ENABLE \ - -D SECURE_BOOT_ENABLE \ - -D SMM_REQUIRE - cp edk2/Build/OvmfIa32/$(target)_$(call toolchain,i386)/FV/OVMF_CODE.fd $@ - -../pc-bios/edk2-x86_64-code.fd: submodules - +./edk2-build.sh \ - x86_64 \ - --arch=X64 \ - --platform=OvmfPkg/OvmfPkgX64.dsc \ - -D NETWORK_IP6_ENABLE \ - -D NETWORK_HTTP_BOOT_ENABLE \ - -D NETWORK_TLS_ENABLE \ - -D TPM_ENABLE \ - -D TPM_CONFIG_ENABLE - cp edk2/Build/OvmfX64/$(target)_$(call toolchain,x86_64)/FV/OVMF_CODE.fd $@ - -../pc-bios/edk2-x86_64-secure-code.fd: submodules - +./edk2-build.sh \ - x86_64 \ - --arch=IA32 \ - --arch=X64 \ - --platform=OvmfPkg/OvmfPkgIa32X64.dsc \ - -D NETWORK_IP6_ENABLE \ - -D NETWORK_HTTP_BOOT_ENABLE \ - -D NETWORK_TLS_ENABLE \ - -D TPM_ENABLE \ - -D TPM_CONFIG_ENABLE \ - -D SECURE_BOOT_ENABLE \ - -D SMM_REQUIRE - cp edk2/Build/Ovmf3264/$(target)_$(call toolchain,x86_64)/FV/OVMF_CODE.fd $@ - -../pc-bios/edk2-x86_64-microvm.fd: submodules - +./edk2-build.sh \ - x86_64 \ - --arch=X64 \ - --platform=OvmfPkg/Microvm/MicrovmX64.dsc \ - -D NETWORK_IP6_ENABLE \ - -D NETWORK_HTTP_BOOT_ENABLE \ - -D NETWORK_TLS_ENABLE - cp edk2/Build/MicrovmX64/$(target)_$(call toolchain,x86_64)/FV/MICROVM.fd $@ - -../pc-bios/edk2-arm-vars.fd: ../pc-bios/edk2-arm-code.fd - cp edk2/Build/ArmVirtQemu-ARM/$(target)_$(call toolchain,arm)/FV/QEMU_VARS.fd \ - $@ - truncate --size=64M $@ - -../pc-bios/edk2-i386-vars.fd: ../pc-bios/edk2-i386-code.fd - cp edk2/Build/OvmfIa32/$(target)_$(call toolchain,i386)/FV/OVMF_VARS.fd $@ - -# The license file accumulates several individual licenses from under edk2, -# prefixing each individual license with a header (generated by "tail") that -# states its pathname. -../pc-bios/edk2-licenses.txt: submodules - tail -n $(shell cat $(licenses) | wc -l) $(licenses) > $@ - dos2unix $@ - -clean: - rm -rf edk2/Build - cd edk2/Conf && \ - rm -rf .cache BuildEnv.sh build_rule.txt target.txt \ - tools_def.txt diff --git a/roms/SLOF b/roms/SLOF index 5b4c5acdcd..3a259df244 160000 --- a/roms/SLOF +++ b/roms/SLOF @@ -1 +1 @@ -Subproject commit 5b4c5acdcd552a4e1796aeca6bb700f6cbb0282d +Subproject commit 3a259df2449fc4a4e43ab5f33f0b2c66484b4bc3 diff --git a/roms/config.seabios-128k b/roms/config.seabios-128k index d18c802c46..0b144bb1de 100644 --- a/roms/config.seabios-128k +++ b/roms/config.seabios-128k @@ -1,21 +1,30 @@ -# for qemu machine types 1.7 + older -# need to turn off features (xhci,uas) to make it fit into 128k +# SeaBIOS Configuration for -M isapc + CONFIG_QEMU=y CONFIG_ROM_SIZE=128 CONFIG_ATA_DMA=n -CONFIG_BOOTSPLASH=n CONFIG_XEN=n -CONFIG_USB_OHCI=n -CONFIG_USB_XHCI=n -CONFIG_USB_UAS=n +CONFIG_ATA_PIO32=n +CONFIG_AHCI=n CONFIG_SDCARD=n -CONFIG_TCGBIOS=n -CONFIG_MPT_SCSI=n -CONFIG_ESP_SCSI=n -CONFIG_MEGASAS=n +CONFIG_VIRTIO_BLK=n +CONFIG_VIRTIO_SCSI=n CONFIG_PVSCSI=n +CONFIG_ESP_SCSI=n +CONFIG_LSI_SCSI=n +CONFIG_MEGASAS=n +CONFIG_MPT_SCSI=n CONFIG_NVME=n CONFIG_USE_SMM=n CONFIG_VGAHOOKS=n CONFIG_HOST_BIOS_GEOMETRY=n +CONFIG_USB=n +CONFIG_PMTIMER=n +CONFIG_PCIBIOS=n +CONFIG_DISABLE_A20=n +CONFIG_WRITABLE_UPPERMEMORY=n +CONFIG_TCGBIOS=n +CONFIG_ACPI=n CONFIG_ACPI_PARSE=n +CONFIG_DEBUG_SERIAL=n +CONFIG_DEBUG_SERIAL_MMIO=n diff --git a/roms/edk2 b/roms/edk2 index b24306f15d..edc6681206 160000 --- a/roms/edk2 +++ b/roms/edk2 @@ -1 +1 @@ -Subproject commit b24306f15daa2ff8510b06702114724b33895d3c +Subproject commit edc6681206c1a8791981a2f911d2fb8b3d2f5768 diff --git a/roms/edk2-build.config b/roms/edk2-build.config new file mode 100644 index 0000000000..cc9b211542 --- /dev/null +++ b/roms/edk2-build.config @@ -0,0 +1,133 @@ +[global] +core = edk2 + +#################################################################################### +# options + +[opts.common] +NETWORK_HTTP_BOOT_ENABLE = TRUE +NETWORK_IP6_ENABLE = TRUE +NETWORK_TLS_ENABLE = TRUE +NETWORK_ISCSI_ENABLE = TRUE +NETWORK_ALLOW_HTTP_CONNECTIONS = TRUE +TPM2_ENABLE = TRUE +TPM2_CONFIG_ENABLE = TRUE +TPM1_ENABLE = TRUE +CAVIUM_ERRATUM_27456 = TRUE + +[opts.ovmf.sb.smm] +SECURE_BOOT_ENABLE = TRUE +SMM_REQUIRE = TRUE +BUILD_SHELL = FALSE + +[opts.armvirt.silent] +DEBUG_PRINT_ERROR_LEVEL = 0x80000000 + +[pcds.nx.strict] +PcdDxeNxMemoryProtectionPolicy = 0xC000000000007FD5 +PcdUninstallMemAttrProtocol = FALSE + +[pcds.nx.broken.shim.grub] +# grub.efi uses EfiLoaderData for code +PcdDxeNxMemoryProtectionPolicy = 0xC000000000007FD1 +# shim.efi has broken MemAttr code +PcdUninstallMemAttrProtocol = TRUE + +#################################################################################### +# i386 + +[build.ovmf.i386] +desc = ovmf build (32-bit) +conf = OvmfPkg/OvmfPkgIa32.dsc +arch = IA32 +opts = common +plat = OvmfIa32 +dest = ../pc-bios +cpy1 = FV/OVMF_CODE.fd edk2-i386-code.fd +cpy2 = FV/OVMF_VARS.fd edk2-i386-vars.fd + +[build.ovmf.i386.secure] +desc = ovmf build (32-bit, secure boot) +conf = OvmfPkg/OvmfPkgIa32.dsc +arch = IA32 +opts = common + ovmf.sb.smm +plat = OvmfIa32 +dest = ../pc-bios +cpy1 = FV/OVMF_CODE.fd edk2-i386-secure-code.fd + +#################################################################################### +# x86_64 + +[build.ovmf.x86_64] +desc = ovmf build (64-bit) +conf = OvmfPkg/OvmfPkgX64.dsc +arch = X64 +opts = common +plat = OvmfX64 +dest = ../pc-bios +cpy1 = FV/OVMF_CODE.fd edk2-x86_64-code.fd + +[build.ovmf.x86_64.secure] +desc = ovmf build (64-bit, secure boot) +conf = OvmfPkg/OvmfPkgX64.dsc +arch = X64 +opts = common + ovmf.sb.smm +plat = OvmfX64 +dest = ../pc-bios +cpy1 = FV/OVMF_CODE.fd edk2-x86_64-secure-code.fd + +[build.ovmf.microvm] +desc = ovmf build for microvm +conf = OvmfPkg/Microvm/MicrovmX64.dsc +arch = X64 +opts = common +plat = MicrovmX64 +dest = ../pc-bios +cpy1 = FV/MICROVM.fd edk2-x86_64-microvm.fd + +#################################################################################### +# arm + +[build.armvirt.arm] +desc = ArmVirt build, 32-bit (arm v7) +conf = ArmVirtPkg/ArmVirtQemu.dsc +arch = ARM +opts = common + armvirt.silent +pcds = nx.broken.shim.grub +plat = ArmVirtQemu-ARM +dest = ../pc-bios +cpy1 = FV/QEMU_EFI.fd edk2-arm-code.fd +cpy2 = FV/QEMU_VARS.fd edk2-arm-vars.fd +pad1 = edk2-arm-code.fd 64m +pad2 = edk2-arm-vars.fd 64m + +#################################################################################### +# aarch64 + +[build.armvirt.aa64] +desc = ArmVirt build, 64-bit (arm v8) +conf = ArmVirtPkg/ArmVirtQemu.dsc +arch = AARCH64 +opts = common + armvirt.silent +pcds = nx.broken.shim.grub +plat = ArmVirtQemu-AARCH64 +dest = ../pc-bios +cpy1 = FV/QEMU_EFI.fd edk2-aarch64-code.fd +pad1 = edk2-aarch64-code.fd 64m + +#################################################################################### +# riscv64 + +[build.riscv.qemu] +conf = OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc +arch = RISCV64 +plat = RiscVVirtQemu +dest = ../pc-bios +cpy1 = FV/RISCV_VIRT_CODE.fd edk2-riscv-code.fd +cpy2 = FV/RISCV_VIRT_VARS.fd edk2-riscv-vars.fd +pad1 = edk2-riscv-code.fd 32m +pad2 = edk2-riscv-vars.fd 32m diff --git a/roms/edk2-build.py b/roms/edk2-build.py new file mode 100755 index 0000000000..e564765aaa --- /dev/null +++ b/roms/edk2-build.py @@ -0,0 +1,434 @@ +#!/usr/bin/python3 +""" +build helper script for edk2, see +https://gitlab.com/kraxel/edk2-build-config + +""" +import os +import sys +import time +import shutil +import argparse +import subprocess +import configparser + +rebase_prefix = "" +version_override = None +release_date = None + +# pylint: disable=unused-variable +def check_rebase(): + """ detect 'git rebase -x edk2-build.py master' testbuilds """ + global rebase_prefix + global version_override + gitdir = '.git' + + if os.path.isfile(gitdir): + with open(gitdir, 'r', encoding = 'utf-8') as f: + (unused, gitdir) = f.read().split() + + if not os.path.exists(f'{gitdir}/rebase-merge/msgnum'): + return + with open(f'{gitdir}/rebase-merge/msgnum', 'r', encoding = 'utf-8') as f: + msgnum = int(f.read()) + with open(f'{gitdir}/rebase-merge/end', 'r', encoding = 'utf-8') as f: + end = int(f.read()) + with open(f'{gitdir}/rebase-merge/head-name', 'r', encoding = 'utf-8') as f: + head = f.read().strip().split('/') + + rebase_prefix = f'[ {int(msgnum/2)} / {int(end/2)} - {head[-1]} ] ' + if msgnum != end and not version_override: + # fixed version speeds up builds + version_override = "test-build-patch-series" + +def get_coredir(cfg): + if cfg.has_option('global', 'core'): + return os.path.abspath(cfg['global']['core']) + return os.getcwd() + +def get_toolchain(cfg, build): + if cfg.has_option(build, 'tool'): + return cfg[build]['tool'] + if cfg.has_option('global', 'tool'): + return cfg['global']['tool'] + return 'GCC5' + +def get_version(cfg, silent = False): + coredir = get_coredir(cfg) + if version_override: + version = version_override + if not silent: + print('') + print(f'### version [override]: {version}') + return version + if os.environ.get('RPM_PACKAGE_NAME'): + version = os.environ.get('RPM_PACKAGE_NAME') + version += '-' + os.environ.get('RPM_PACKAGE_VERSION') + version += '-' + os.environ.get('RPM_PACKAGE_RELEASE') + if not silent: + print('') + print(f'### version [rpmbuild]: {version}') + return version + if os.path.exists(coredir + '/.git'): + cmdline = [ 'git', 'describe', '--tags', '--abbrev=8', + '--match=edk2-stable*' ] + result = subprocess.run(cmdline, cwd = coredir, + stdout = subprocess.PIPE, + check = True) + version = result.stdout.decode().strip() + if not silent: + print('') + print(f'### version [git]: {version}') + return version + return None + +def pcd_string(name, value): + return f'{name}=L{value}\\0' + +def pcd_version(cfg, silent = False): + version = get_version(cfg, silent) + if version is None: + return [] + return [ '--pcd', pcd_string('PcdFirmwareVersionString', version) ] + +def pcd_release_date(): + if release_date is None: + return [] + return [ '--pcd', pcd_string('PcdFirmwareReleaseDateString', release_date) ] + +def build_message(line, line2 = None, silent = False): + if os.environ.get('TERM') in [ 'xterm', 'xterm-256color' ]: + # setxterm title + start = '\x1b]2;' + end = '\x07' + print(f'{start}{rebase_prefix}{line}{end}', end = '') + + if silent: + print(f'### {rebase_prefix}{line}', flush = True) + else: + print('') + print('###') + print(f'### {rebase_prefix}{line}') + if line2: + print(f'### {line2}') + print('###', flush = True) + +def build_run(cmdline, name, section, silent = False, nologs = False): + if silent: + logfile = f'{section}.log' + if nologs: + print(f'### building in silent mode [no log] ...', flush = True) + else: + print(f'### building in silent mode [{logfile}] ...', flush = True) + start = time.time() + result = subprocess.run(cmdline, check = False, + stdout = subprocess.PIPE, + stderr = subprocess.STDOUT) + if not nologs: + with open(logfile, 'wb') as f: + f.write(result.stdout) + + if result.returncode: + print('### BUILD FAILURE') + print('### cmdline') + print(cmdline) + print('### output') + print(result.stdout.decode()) + print(f'### exit code: {result.returncode}') + else: + secs = int(time.time() - start) + print(f'### OK ({int(secs/60)}:{secs%60:02d})') + else: + print(cmdline, flush = True) + result = subprocess.run(cmdline, check = False) + if result.returncode: + print(f'ERROR: {cmdline[0]} exited with {result.returncode}' + f' while building {name}') + sys.exit(result.returncode) + +def build_copy(plat, tgt, toolchain, dstdir, copy): + srcdir = f'Build/{plat}/{tgt}_{toolchain}' + names = copy.split() + srcfile = names[0] + if len(names) > 1: + dstfile = names[1] + else: + dstfile = os.path.basename(srcfile) + print(f'# copy: {srcdir} / {srcfile} => {dstdir} / {dstfile}') + + src = srcdir + '/' + srcfile + dst = dstdir + '/' + dstfile + os.makedirs(os.path.dirname(dst), exist_ok = True) + shutil.copy(src, dst) + +def pad_file(dstdir, pad): + args = pad.split() + if len(args) < 2: + raise RuntimeError(f'missing arg for pad ({args})') + name = args[0] + size = args[1] + cmdline = [ + 'truncate', + '--size', size, + dstdir + '/' + name, + ] + print(f'# padding: {dstdir} / {name} => {size}') + subprocess.run(cmdline, check = True) + +# pylint: disable=too-many-branches +def build_one(cfg, build, jobs = None, silent = False, nologs = False): + b = cfg[build] + + cmdline = [ 'build' ] + cmdline += [ '-t', get_toolchain(cfg, build) ] + cmdline += [ '-p', b['conf'] ] + + if (b['conf'].startswith('OvmfPkg/') or + b['conf'].startswith('ArmVirtPkg/')): + cmdline += pcd_version(cfg, silent) + cmdline += pcd_release_date() + + if jobs: + cmdline += [ '-n', jobs ] + for arch in b['arch'].split(): + cmdline += [ '-a', arch ] + if 'opts' in b: + for name in b['opts'].split(): + section = 'opts.' + name + for opt in cfg[section]: + cmdline += [ '-D', opt + '=' + cfg[section][opt] ] + if 'pcds' in b: + for name in b['pcds'].split(): + section = 'pcds.' + name + for pcd in cfg[section]: + cmdline += [ '--pcd', pcd + '=' + cfg[section][pcd] ] + if 'tgts' in b: + tgts = b['tgts'].split() + else: + tgts = [ 'DEBUG' ] + for tgt in tgts: + desc = None + if 'desc' in b: + desc = b['desc'] + build_message(f'building: {b["conf"]} ({b["arch"]}, {tgt})', + f'description: {desc}', + silent = silent) + build_run(cmdline + [ '-b', tgt ], + b['conf'], + build + '.' + tgt, + silent, + nologs) + + if 'plat' in b: + # copy files + for cpy in b: + if not cpy.startswith('cpy'): + continue + build_copy(b['plat'], tgt, + get_toolchain(cfg, build), + b['dest'], b[cpy]) + # pad builds + for pad in b: + if not pad.startswith('pad'): + continue + pad_file(b['dest'], b[pad]) + +def build_basetools(silent = False, nologs = False): + build_message('building: BaseTools', silent = silent) + basedir = os.environ['EDK_TOOLS_PATH'] + cmdline = [ 'make', '-C', basedir ] + build_run(cmdline, 'BaseTools', 'build.basetools', silent, nologs) + +def binary_exists(name): + for pdir in os.environ['PATH'].split(':'): + if os.path.exists(pdir + '/' + name): + return True + return False + +def prepare_env(cfg, silent = False): + """ mimic Conf/BuildEnv.sh """ + workspace = os.getcwd() + packages = [ workspace, ] + path = os.environ['PATH'].split(':') + dirs = [ + 'BaseTools/Bin/Linux-x86_64', + 'BaseTools/BinWrappers/PosixLike' + ] + + if cfg.has_option('global', 'pkgs'): + for pkgdir in cfg['global']['pkgs'].split(): + packages.append(os.path.abspath(pkgdir)) + coredir = get_coredir(cfg) + if coredir != workspace: + packages.append(coredir) + + # add basetools to path + for pdir in dirs: + p = coredir + '/' + pdir + if not os.path.exists(p): + continue + if p in path: + continue + path.insert(0, p) + + # run edksetup if needed + toolsdef = coredir + '/Conf/tools_def.txt' + if not os.path.exists(toolsdef): + os.makedirs(os.path.dirname(toolsdef), exist_ok = True) + build_message('running BaseTools/BuildEnv', silent = silent) + cmdline = [ 'bash', 'BaseTools/BuildEnv' ] + subprocess.run(cmdline, cwd = coredir, check = True) + + # set variables + os.environ['PATH'] = ':'.join(path) + os.environ['PACKAGES_PATH'] = ':'.join(packages) + os.environ['WORKSPACE'] = workspace + os.environ['EDK_TOOLS_PATH'] = coredir + '/BaseTools' + os.environ['CONF_PATH'] = coredir + '/Conf' + os.environ['PYTHON_COMMAND'] = '/usr/bin/python3' + os.environ['PYTHONHASHSEED'] = '1' + + # for cross builds + if binary_exists('arm-linux-gnueabi-gcc'): + # ubuntu + os.environ['GCC5_ARM_PREFIX'] = 'arm-linux-gnueabi-' + os.environ['GCC_ARM_PREFIX'] = 'arm-linux-gnueabi-' + elif binary_exists('arm-linux-gnu-gcc'): + # fedora + os.environ['GCC5_ARM_PREFIX'] = 'arm-linux-gnu-' + os.environ['GCC_ARM_PREFIX'] = 'arm-linux-gnu-' + if binary_exists('loongarch64-linux-gnu-gcc'): + os.environ['GCC5_LOONGARCH64_PREFIX'] = 'loongarch64-linux-gnu-' + os.environ['GCC_LOONGARCH64_PREFIX'] = 'loongarch64-linux-gnu-' + + hostarch = os.uname().machine + if binary_exists('aarch64-linux-gnu-gcc') and hostarch != 'aarch64': + os.environ['GCC5_AARCH64_PREFIX'] = 'aarch64-linux-gnu-' + os.environ['GCC_AARCH64_PREFIX'] = 'aarch64-linux-gnu-' + if binary_exists('riscv64-linux-gnu-gcc') and hostarch != 'riscv64': + os.environ['GCC5_RISCV64_PREFIX'] = 'riscv64-linux-gnu-' + os.environ['GCC_RISCV64_PREFIX'] = 'riscv64-linux-gnu-' + if binary_exists('x86_64-linux-gnu-gcc') and hostarch != 'x86_64': + os.environ['GCC5_IA32_PREFIX'] = 'x86_64-linux-gnu-' + os.environ['GCC5_X64_PREFIX'] = 'x86_64-linux-gnu-' + os.environ['GCC5_BIN'] = 'x86_64-linux-gnu-' + os.environ['GCC_IA32_PREFIX'] = 'x86_64-linux-gnu-' + os.environ['GCC_X64_PREFIX'] = 'x86_64-linux-gnu-' + os.environ['GCC_BIN'] = 'x86_64-linux-gnu-' + +def build_list(cfg): + for build in cfg.sections(): + if not build.startswith('build.'): + continue + name = build.lstrip('build.') + desc = 'no description' + if 'desc' in cfg[build]: + desc = cfg[build]['desc'] + print(f'# {name:20s} - {desc}') + +def main(): + parser = argparse.ArgumentParser(prog = 'edk2-build', + description = 'edk2 build helper script') + parser.add_argument('-c', '--config', dest = 'configfile', + type = str, default = '.edk2.builds', metavar = 'FILE', + help = 'read configuration from FILE (default: .edk2.builds)') + parser.add_argument('-C', '--directory', dest = 'directory', type = str, + help = 'change to DIR before building', metavar = 'DIR') + parser.add_argument('-j', '--jobs', dest = 'jobs', type = str, + help = 'allow up to JOBS parallel build jobs', + metavar = 'JOBS') + parser.add_argument('-m', '--match', dest = 'match', + type = str, action = 'append', + help = 'only run builds matching INCLUDE (substring)', + metavar = 'INCLUDE') + parser.add_argument('-x', '--exclude', dest = 'exclude', + type = str, action = 'append', + help = 'skip builds matching EXCLUDE (substring)', + metavar = 'EXCLUDE') + parser.add_argument('-l', '--list', dest = 'list', + action = 'store_true', default = False, + help = 'list build configs available') + parser.add_argument('--silent', dest = 'silent', + action = 'store_true', default = False, + help = 'write build output to logfiles, ' + 'write to console only on errors') + parser.add_argument('--no-logs', dest = 'nologs', + action = 'store_true', default = False, + help = 'do not write build log files (with --silent)') + parser.add_argument('--core', dest = 'core', type = str, metavar = 'DIR', + help = 'location of the core edk2 repository ' + '(i.e. where BuildTools are located)') + parser.add_argument('--pkg', '--package', dest = 'pkgs', + type = str, action = 'append', metavar = 'DIR', + help = 'location(s) of additional packages ' + '(can be specified multiple times)') + parser.add_argument('-t', '--toolchain', dest = 'toolchain', + type = str, metavar = 'NAME', + help = 'tool chain to be used to build edk2') + parser.add_argument('--version-override', dest = 'version_override', + type = str, metavar = 'VERSION', + help = 'set firmware build version') + parser.add_argument('--release-date', dest = 'release_date', + type = str, metavar = 'DATE', + help = 'set firmware build release date (in MM/DD/YYYY format)') + options = parser.parse_args() + + if options.directory: + os.chdir(options.directory) + + if not os.path.exists(options.configfile): + print(f'config file "{options.configfile}" not found') + return 1 + + cfg = configparser.ConfigParser() + cfg.optionxform = str + cfg.read(options.configfile) + + if options.list: + build_list(cfg) + return 0 + + if not cfg.has_section('global'): + cfg.add_section('global') + if options.core: + cfg.set('global', 'core', options.core) + if options.pkgs: + cfg.set('global', 'pkgs', ' '.join(options.pkgs)) + if options.toolchain: + cfg.set('global', 'tool', options.toolchain) + + global version_override + global release_date + check_rebase() + if options.version_override: + version_override = options.version_override + if options.release_date: + release_date = options.release_date + + prepare_env(cfg, options.silent) + build_basetools(options.silent, options.nologs) + for build in cfg.sections(): + if not build.startswith('build.'): + continue + if options.match: + matching = False + for item in options.match: + if item in build: + matching = True + if not matching: + print(f'# skipping "{build}" (not matching "{"|".join(options.match)}")') + continue + if options.exclude: + exclude = False + for item in options.exclude: + if item in build: + print(f'# skipping "{build}" (matching "{item}")') + exclude = True + if exclude: + continue + build_one(cfg, build, options.jobs, options.silent, options.nologs) + + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/roms/edk2-build.sh b/roms/edk2-build.sh deleted file mode 100755 index ea79dc27a2..0000000000 --- a/roms/edk2-build.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash - -# Wrapper shell script for building a virtual platform firmware in edk2. -# -# Copyright (C) 2019 Red Hat, Inc. -# -# This program and the accompanying materials are licensed and made available -# under the terms and conditions of the BSD License that accompanies this -# distribution. The full text of the license may be found at -# . -# -# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT -# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -set -e -u -C - -# Save the command line arguments. We need to reset $# to 0 before sourcing -# "edksetup.sh", as it will inherit $@. -emulation_target=$1 -shift -num_args=0 -args=() -for arg in "$@"; do - args[num_args++]="$arg" -done -shift $num_args - -cd edk2 - -export PYTHON_COMMAND=${EDK2_PYTHON_COMMAND:-python3} - -# Source "edksetup.sh" carefully. -set +e +u +C -source ./edksetup.sh -ret=$? -set -e -u -C -if [ $ret -ne 0 ]; then - exit $ret -fi - -# Fetch some option arguments, and set the cross-compilation environment (if -# any), for the edk2 "build" utility. -source ../edk2-funcs.sh -edk2_toolchain=$(qemu_edk2_get_toolchain "$emulation_target") -MAKEFLAGS=$(qemu_edk2_quirk_tianocore_1607 "$MAKEFLAGS") -edk2_thread_count=$(qemu_edk2_get_thread_count "$MAKEFLAGS") -qemu_edk2_set_cross_env "$emulation_target" - -# Build the platform firmware. -build \ - --cmd-len=65536 \ - -n "$edk2_thread_count" \ - --buildtarget=RELEASE \ - --tagname="$edk2_toolchain" \ - "${args[@]}" diff --git a/roms/edk2-funcs.sh b/roms/edk2-funcs.sh deleted file mode 100644 index cd6e4f2c82..0000000000 --- a/roms/edk2-funcs.sh +++ /dev/null @@ -1,273 +0,0 @@ -# Shell script that defines functions for determining some environmental -# characteristics for the edk2 "build" utility. -# -# This script is meant to be sourced, in a bash environment. -# -# Copyright (C) 2019 Red Hat, Inc. -# -# This program and the accompanying materials are licensed and made available -# under the terms and conditions of the BSD License that accompanies this -# distribution. The full text of the license may be found at -# . -# -# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT -# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - - -# Verify whether the QEMU system emulation target is supported by the UEFI spec -# and edk2. Print a message to the standard error, and return with nonzero -# status, if verification fails. -# -# Parameters: -# $1: QEMU system emulation target -qemu_edk2_verify_arch() -{ - local emulation_target="$1" - local program_name=$(basename -- "$0") - - case "$emulation_target" in - (arm|aarch64|i386|x86_64) - ;; - (*) - printf '%s: unknown/unsupported QEMU system emulation target "%s"\n' \ - "$program_name" "$emulation_target" >&2 - return 1 - ;; - esac -} - - -# Translate the QEMU system emulation target to the edk2 architecture -# identifier. Print the result to the standard output. -# -# Parameters: -# $1: QEMU system emulation target -qemu_edk2_get_arch() -{ - local emulation_target="$1" - - if ! qemu_edk2_verify_arch "$emulation_target"; then - return 1 - fi - - case "$emulation_target" in - (arm) - printf 'ARM\n' - ;; - (aarch64) - printf 'AARCH64\n' - ;; - (i386) - printf 'IA32\n' - ;; - (x86_64) - printf 'X64\n' - ;; - esac -} - - -# Translate the QEMU system emulation target to the gcc cross-compilation -# architecture identifier. Print the result to the standard output. -# -# Parameters: -# $1: QEMU system emulation target -qemu_edk2_get_gcc_arch() -{ - local emulation_target="$1" - - if ! qemu_edk2_verify_arch "$emulation_target"; then - return 1 - fi - - case "$emulation_target" in - (arm|aarch64|x86_64) - printf '%s\n' "$emulation_target" - ;; - (i386) - printf 'i686\n' - ;; - esac -} - - -# Determine the gcc cross-compiler prefix (if any) for use with the edk2 -# toolchain. Print the result to the standard output. -# -# Parameters: -# $1: QEMU system emulation target -qemu_edk2_get_cross_prefix() -{ - local emulation_target="$1" - local gcc_arch - local host_arch - - if ! gcc_arch=$(qemu_edk2_get_gcc_arch "$emulation_target"); then - return 1 - fi - - host_arch=$(uname -m) - - if [ "$gcc_arch" == "$host_arch" ] || - ( [ "$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 -} - - -# Determine the edk2 toolchain tag for the QEMU system emulation target. Print -# the result to the standard output. Print a message to the standard error, and -# return with nonzero status, if the (conditional) gcc version check fails. -# -# Parameters: -# $1: QEMU system emulation target -qemu_edk2_get_toolchain() -{ - local emulation_target="$1" - local program_name=$(basename -- "$0") - local cross_prefix - local gcc_version - - if ! qemu_edk2_verify_arch "$emulation_target"; then - return 1 - fi - - case "$emulation_target" in - (arm|aarch64) - printf 'GCC5\n' - ;; - - (i386|x86_64) - if ! cross_prefix=$(qemu_edk2_get_cross_prefix "$emulation_target"); then - return 1 - fi - - gcc_version=$("${cross_prefix}gcc" -v 2>&1 | tail -1 | awk '{print $3}') - # Run "git-blame" on "OvmfPkg/build.sh" in edk2 for more information on - # the mapping below. - case "$gcc_version" in - ([1-3].*|4.[0-7].*) - printf '%s: unsupported gcc version "%s"\n' \ - "$program_name" "$gcc_version" >&2 - return 1 - ;; - (4.8.*) - printf 'GCC48\n' - ;; - (4.9.*|6.[0-2].*) - printf 'GCC49\n' - ;; - (*) - printf 'GCC5\n' - ;; - esac - ;; - esac -} - - -# Determine the name of the environment variable that exposes the -# cross-compiler prefix to the edk2 "build" utility. Print the result to the -# standard output. -# -# Parameters: -# $1: QEMU system emulation target -qemu_edk2_get_cross_prefix_var() -{ - local emulation_target="$1" - local edk2_toolchain - local edk2_arch - - if ! edk2_toolchain=$(qemu_edk2_get_toolchain "$emulation_target"); then - return 1 - fi - - case "$emulation_target" in - (arm|aarch64) - if ! edk2_arch=$(qemu_edk2_get_arch "$emulation_target"); then - return 1 - fi - printf '%s_%s_PREFIX\n' "$edk2_toolchain" "$edk2_arch" - ;; - (i386|x86_64) - printf '%s_BIN\n' "$edk2_toolchain" - ;; - esac -} - - -# Set and export the environment variable(s) necessary for cross-compilation, -# whenever needed by the edk2 "build" utility. -# -# Parameters: -# $1: QEMU system emulation target -qemu_edk2_set_cross_env() -{ - local emulation_target="$1" - local cross_prefix - local cross_prefix_var - - if ! cross_prefix=$(qemu_edk2_get_cross_prefix "$emulation_target"); then - return 1 - fi - - if [ -z "$cross_prefix" ]; then - # Nothing to do. - return 0 - fi - - if ! cross_prefix_var=$(qemu_edk2_get_cross_prefix_var \ - "$emulation_target"); then - return 1 - fi - - eval "export $cross_prefix_var=\$cross_prefix" -} - - -# Determine the "-n" option argument (that is, the number of modules to build -# in parallel) for the edk2 "build" utility. Print the result to the standard -# output. -# -# Parameters: -# $1: the value of the MAKEFLAGS variable -qemu_edk2_get_thread_count() -{ - local makeflags="$1" - - if [[ "$makeflags" == *--jobserver-auth=* ]] || - [[ "$makeflags" == *--jobserver-fds=* ]]; then - # If there is a job server, allow the edk2 "build" utility to parallelize - # as many module builds as there are logical CPUs in the system. The "make" - # instances forked by "build" are supposed to limit themselves through the - # job server. The zero value below causes the edk2 "build" utility to fetch - # the logical CPU count with Python's multiprocessing.cpu_count() method. - printf '0\n' - else - # Build a single module at a time. - printf '1\n' - fi -} - - -# Work around by -# filtering jobserver-related flags out of MAKEFLAGS. Print the result to the -# standard output. -# -# Parameters: -# $1: the value of the MAKEFLAGS variable -qemu_edk2_quirk_tianocore_1607() -{ - local makeflags="$1" - - printf %s "$makeflags" \ - | LC_ALL=C sed --regexp-extended \ - --expression='s/--jobserver-(auth|fds)=[0-9]+,[0-9]+//' \ - --expression='s/-j([0-9]+)?//' -} diff --git a/roms/edk2-version b/roms/edk2-version new file mode 100644 index 0000000000..1594ed8c4d --- /dev/null +++ b/roms/edk2-version @@ -0,0 +1,2 @@ +EDK2_STABLE = edk2-stable202402 +EDK2_DATE = 02/14/2024 diff --git a/roms/openbios b/roms/openbios index 0e0afae657..af97fd7af5 160000 --- a/roms/openbios +++ b/roms/openbios @@ -1 +1 @@ -Subproject commit 0e0afae6579c1efe9f0d85505b75ffe989554133 +Subproject commit af97fd7af5e7c18f591a7b987291d3db4ffb28b5 diff --git a/roms/opensbi b/roms/opensbi index 48f91ee9c9..a2b255b889 160000 --- a/roms/opensbi +++ b/roms/opensbi @@ -1 +1 @@ -Subproject commit 48f91ee9c960f048c4a7d1da4447d31e04931e38 +Subproject commit a2b255b88918715173942f2c5e1f97ac9e90c877 diff --git a/roms/qboot b/roms/qboot index a5300c4949..8ca302e86d 160000 --- a/roms/qboot +++ b/roms/qboot @@ -1 +1 @@ -Subproject commit a5300c4949b8d4de2d34bedfaed66793f48ec948 +Subproject commit 8ca302e86d685fa05b16e2b208888243da319941 diff --git a/roms/seabios b/roms/seabios index d239552ce7..a6ed6b701f 160000 --- a/roms/seabios +++ b/roms/seabios @@ -1 +1 @@ -Subproject commit d239552ce7220e448ae81f41515138f7b9e3c4db +Subproject commit a6ed6b701f0a57db0569ab98b0661c12a6ec3ff8 diff --git a/roms/seabios-hppa b/roms/seabios-hppa index 458626c4c6..03774edaad 160000 --- a/roms/seabios-hppa +++ b/roms/seabios-hppa @@ -1 +1 @@ -Subproject commit 458626c4c6441045c0612f24313c7cf1f95e71c6 +Subproject commit 03774edaad3bfae090ac96ca5450353c641637d1 diff --git a/roms/sgabios b/roms/sgabios deleted file mode 160000 index cbaee52287..0000000000 --- a/roms/sgabios +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cbaee52287e5f32373181cff50a00b6c4ac9015a diff --git a/scripts/analyse-locks-simpletrace.py b/scripts/analyse-locks-simpletrace.py index 63c11f4fce..d650dd7140 100755 --- a/scripts/analyse-locks-simpletrace.py +++ b/scripts/analyse-locks-simpletrace.py @@ -75,7 +75,7 @@ if __name__ == '__main__': (analyser.locks, analyser.locked, analyser.unlocks)) # Now dump the individual lock stats - for key, val in sorted(analyser.mutex_records.iteritems(), + for key, val in sorted(analyser.mutex_records.items(), key=lambda k_v: k_v[1]["locks"]): print ("Lock: %#x locks: %d, locked: %d, unlocked: %d" % (key, val["locks"], val["locked"], val["unlocked"])) diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index b82a1b0c58..8a254a5b6a 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -38,13 +38,13 @@ class MigrationFile(object): self.file = open(self.filename, "rb") def read64(self): - return int.from_bytes(self.file.read(8), byteorder='big', signed=True) + return int.from_bytes(self.file.read(8), byteorder='big', signed=False) def read32(self): - return int.from_bytes(self.file.read(4), byteorder='big', signed=True) + return int.from_bytes(self.file.read(4), byteorder='big', signed=False) def read16(self): - return int.from_bytes(self.file.read(2), byteorder='big', signed=True) + return int.from_bytes(self.file.read(2), byteorder='big', signed=False) def read8(self): return int.from_bytes(self.file.read(1), byteorder='big', signed=True) @@ -111,6 +111,8 @@ class RamSection(object): RAM_SAVE_FLAG_CONTINUE = 0x20 RAM_SAVE_FLAG_XBZRLE = 0x40 RAM_SAVE_FLAG_HOOK = 0x80 + RAM_SAVE_FLAG_COMPRESS_PAGE = 0x100 + RAM_SAVE_FLAG_MULTIFD_FLUSH = 0x200 def __init__(self, file, version_id, ramargs, section_key): if version_id != 4: @@ -121,6 +123,7 @@ class RamSection(object): self.TARGET_PAGE_SIZE = ramargs['page_size'] self.dump_memory = ramargs['dump_memory'] self.write_memory = ramargs['write_memory'] + self.ignore_shared = ramargs['ignore_shared'] self.sizeinfo = collections.OrderedDict() self.data = collections.OrderedDict() self.data['section sizes'] = self.sizeinfo @@ -148,17 +151,12 @@ class RamSection(object): addr &= ~(self.TARGET_PAGE_SIZE - 1) if flags & self.RAM_SAVE_FLAG_MEM_SIZE: - while True: + total_length = addr + while total_length > 0: namelen = self.file.read8() - # We assume that no RAM chunk is big enough to ever - # hit the first byte of the address, so when we see - # a zero here we know it has to be an address, not the - # length of the next block. - if namelen == 0: - self.file.file.seek(-1, 1) - break self.name = self.file.readstr(len = namelen) len = self.file.read64() + total_length -= len self.sizeinfo[self.name] = '0x%016x' % len if self.write_memory: print(self.name) @@ -167,6 +165,8 @@ class RamSection(object): f.truncate(0) f.truncate(len) self.files[self.name] = f + if self.ignore_shared: + mr_addr = self.file.read64() flags &= ~self.RAM_SAVE_FLAG_MEM_SIZE if flags & self.RAM_SAVE_FLAG_COMPRESS: @@ -205,6 +205,8 @@ class RamSection(object): raise Exception("XBZRLE RAM compression is not supported yet") elif flags & self.RAM_SAVE_FLAG_HOOK: raise Exception("RAM hooks don't make sense with files") + if flags & self.RAM_SAVE_FLAG_MULTIFD_FLUSH: + continue # End of RAM section if flags & self.RAM_SAVE_FLAG_EOS: @@ -256,13 +258,70 @@ class HTABSection(object): return "" -class ConfigurationSection(object): - def __init__(self, file): +class S390StorageAttributes(object): + STATTR_FLAG_EOS = 0x01 + STATTR_FLAG_MORE = 0x02 + STATTR_FLAG_ERROR = 0x04 + STATTR_FLAG_DONE = 0x08 + + def __init__(self, file, version_id, device, section_key): + if version_id != 0: + raise Exception("Unknown storage_attributes version %d" % version_id) + self.file = file + self.section_key = section_key def read(self): - name_len = self.file.read32() - name = self.file.readstr(len = name_len) + while True: + addr_flags = self.file.read64() + flags = addr_flags & 0xfff + if (flags & (self.STATTR_FLAG_DONE | self.STATTR_FLAG_EOS)): + return + if (flags & self.STATTR_FLAG_ERROR): + raise Exception("Error in migration stream") + count = self.file.read64() + self.file.readvar(count) + + def getDict(self): + return "" + + +class ConfigurationSection(object): + def __init__(self, file, desc): + self.file = file + self.desc = desc + self.caps = [] + + def parse_capabilities(self, vmsd_caps): + if not vmsd_caps: + return + + ncaps = vmsd_caps.data['caps_count'].data + self.caps = vmsd_caps.data['capabilities'] + + if type(self.caps) != list: + self.caps = [self.caps] + + if len(self.caps) != ncaps: + raise Exception("Number of capabilities doesn't match " + "caps_count field") + + def has_capability(self, cap): + return any([str(c) == cap for c in self.caps]) + + def read(self): + if self.desc: + version_id = self.desc['version'] + section = VMSDSection(self.file, version_id, self.desc, + 'configuration') + section.read() + self.parse_capabilities( + section.data.get("configuration/capabilities")) + else: + # backward compatibility for older streams that don't have + # the configuration section in the json + name_len = self.file.read32() + name = self.file.readstr(len = name_len) class VMSDFieldGeneric(object): def __init__(self, desc, file): @@ -284,6 +343,23 @@ class VMSDFieldGeneric(object): self.data = self.file.readvar(size) return self.data +class VMSDFieldCap(object): + def __init__(self, desc, file): + self.file = file + self.desc = desc + self.data = "" + + def __repr__(self): + return self.data + + def __str__(self): + return self.data + + def read(self): + len = self.file.read8() + self.data = self.file.readstr(len) + + class VMSDFieldInt(VMSDFieldGeneric): def __init__(self, desc, file): super(VMSDFieldInt, self).__init__(desc, file) @@ -458,6 +534,7 @@ vmsd_field_readers = { "unused_buffer" : VMSDFieldGeneric, "bitmap" : VMSDFieldGeneric, "struct" : VMSDFieldStruct, + "capability": VMSDFieldCap, "unknown" : VMSDFieldGeneric, } @@ -490,8 +567,11 @@ class MigrationDump(object): QEMU_VM_SECTION_FOOTER= 0x7e def __init__(self, filename): - self.section_classes = { ( 'ram', 0 ) : [ RamSection, None ], - ( 'spapr/htab', 0) : ( HTABSection, None ) } + self.section_classes = { + ( 'ram', 0 ) : [ RamSection, None ], + ( 's390-storage_attributes', 0 ) : [ S390StorageAttributes, None], + ( 'spapr/htab', 0) : ( HTABSection, None ) + } self.filename = filename self.vmsd_desc = None @@ -521,6 +601,7 @@ class MigrationDump(object): ramargs['page_size'] = self.vmsd_desc['page_size'] ramargs['dump_memory'] = dump_memory ramargs['write_memory'] = write_memory + ramargs['ignore_shared'] = False self.section_classes[('ram',0)][1] = ramargs while True: @@ -528,8 +609,10 @@ class MigrationDump(object): if section_type == self.QEMU_VM_EOF: break elif section_type == self.QEMU_VM_CONFIGURATION: - section = ConfigurationSection(file) + config_desc = self.vmsd_desc.get('configuration') + section = ConfigurationSection(file, config_desc) section.read() + ramargs['ignore_shared'] = section.has_capability('x-ignore-shared') elif section_type == self.QEMU_VM_SECTION_START or section_type == self.QEMU_VM_SECTION_FULL: section_id = file.read32() name = file.readstr() diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index c6169db69f..65af8063e4 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -26,8 +26,7 @@ sub_file="${sub_tdir}/submodule.tar" # independent of what the developer currently has initialized # in their checkout, because the build environment is completely # different to the host OS. -submodules="dtc slirp meson ui/keycodemapdb" -submodules="$submodules tests/fp/berkeley-softfloat-3 tests/fp/berkeley-testfloat-3" +subprojects="keycodemapdb libvfio-user berkeley-softfloat-3 berkeley-testfloat-3" sub_deinit="" function cleanup() { @@ -51,23 +50,11 @@ function tree_ish() { git archive --format tar "$(tree_ish)" > "$tar_file" test $? -ne 0 && error "failed to archive qemu" -for sm in $submodules; do - status="$(git submodule status "$sm")" - smhash="${status#[ +-]}" - smhash="${smhash%% *}" - case "$status" in - -*) - sub_deinit="$sub_deinit $sm" - git submodule update --init "$sm" - test $? -ne 0 && error "failed to update submodule $sm" - ;; - +*) - echo "WARNING: submodule $sm is out of sync" - ;; - esac - (cd $sm; git archive --format tar --prefix "$sm/" $(tree_ish)) > "$sub_file" - test $? -ne 0 && error "failed to archive submodule $sm ($smhash)" - tar --concatenate --file "$tar_file" "$sub_file" - test $? -ne 0 && error "failed append submodule $sm to $tar_file" + +for sp in $subprojects; do + meson subprojects download $sp + test $? -ne 0 && error "failed to download subproject $sp" + tar --append --file "$tar_file" --exclude=.git subprojects/$sp + test $? -ne 0 && error "failed to append subproject $sp to $tar_file" done exit 0 diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py index 08be813407..dbbde99e39 100644 --- a/scripts/block-coroutine-wrapper.py +++ b/scripts/block-coroutine-wrapper.py @@ -2,7 +2,7 @@ """Generate coroutine wrappers for block subsystem. The program parses one or several concatenated c files from stdin, -searches for functions with the 'generated_co_wrapper' specifier +searches for functions with the 'co_wrapper' specifier and generates corresponding wrappers on stdout. Usage: block-coroutine-wrapper.py generated-file.c FILE.[ch]... @@ -42,7 +42,8 @@ def gen_header(): #include "qemu/osdep.h" #include "block/coroutines.h" #include "block/block-gen.h" -#include "block/block_int.h"\ +#include "block/block_int.h" +#include "block/dirty-bitmap.h" """ @@ -62,10 +63,56 @@ class ParamDecl: class FuncDecl: - def __init__(self, return_type: str, name: str, args: str) -> None: + def __init__(self, wrapper_type: str, return_type: str, name: str, + args: str, variant: str) -> None: self.return_type = return_type.strip() self.name = name.strip() + self.struct_name = snake_to_camel(self.name) self.args = [ParamDecl(arg.strip()) for arg in args.split(',')] + self.create_only_co = 'mixed' not in variant + self.graph_rdlock = 'bdrv_rdlock' in variant + self.graph_wrlock = 'bdrv_wrlock' in variant + + self.wrapper_type = wrapper_type + + if wrapper_type == 'co': + if self.graph_wrlock: + raise ValueError(f"co function can't be wrlock: {self.name}") + subsystem, subname = self.name.split('_', 1) + self.target_name = f'{subsystem}_co_{subname}' + else: + assert wrapper_type == 'no_co' + subsystem, co_infix, subname = self.name.split('_', 2) + if co_infix != 'co': + raise ValueError(f"Invalid no_co function name: {self.name}") + if not self.create_only_co: + raise ValueError(f"no_co function can't be mixed: {self.name}") + if self.graph_rdlock and self.graph_wrlock: + raise ValueError("function can't be both rdlock and wrlock: " + f"{self.name}") + self.target_name = f'{subsystem}_{subname}' + + self.get_result = 's->ret = ' + self.ret = 'return s.ret;' + self.co_ret = 'return ' + self.return_field = self.return_type + " ret;" + if self.return_type == 'void': + self.get_result = '' + self.ret = '' + self.co_ret = '' + self.return_field = '' + + def gen_ctx(self, prefix: str = '') -> str: + t = self.args[0].type + name = self.args[0].name + if t == 'BlockDriverState *': + return f'bdrv_get_aio_context({prefix}{name})' + elif t == 'BdrvChild *': + return f'bdrv_get_aio_context({prefix}{name}->bs)' + elif t == 'BlockBackend *': + return f'blk_get_aio_context({prefix}{name})' + else: + return 'qemu_get_aio_context()' def gen_list(self, format: str) -> str: return ', '.join(format.format_map(arg.__dict__) for arg in self.args) @@ -74,17 +121,22 @@ class FuncDecl: return '\n'.join(format.format_map(arg.__dict__) for arg in self.args) -# Match wrappers declared with a generated_co_wrapper mark -func_decl_re = re.compile(r'^int\s*generated_co_wrapper\s*' +# Match wrappers declared with a co_wrapper mark +func_decl_re = re.compile(r'^(?P[a-zA-Z][a-zA-Z0-9_]* [\*]?)' + r'(\s*coroutine_fn)?' + r'\s*(?P(no_)?co)_wrapper' + r'(?P(_[a-z][a-z0-9_]*)?)\s*' r'(?P[a-z][a-z0-9_]*)' r'\((?P[^)]*)\);$', re.MULTILINE) def func_decl_iter(text: str) -> Iterator: for m in func_decl_re.finditer(text): - yield FuncDecl(return_type='int', + yield FuncDecl(wrapper_type=m.group('wrapper_type'), + return_type=m.group('return_type'), name=m.group('wrapper_name'), - args=m.group('args')) + args=m.group('args'), + variant=m.group('variant')) def snake_to_camel(func_name: str) -> str: @@ -97,24 +149,76 @@ def snake_to_camel(func_name: str) -> str: return ''.join(words) -def gen_wrapper(func: FuncDecl) -> str: +def create_mixed_wrapper(func: FuncDecl) -> str: + """ + Checks if we are already in coroutine + """ + name = func.target_name + struct_name = func.struct_name + graph_assume_lock = 'assume_graph_lock();' if func.graph_rdlock else '' + + return f"""\ +{func.return_type} {func.name}({ func.gen_list('{decl}') }) +{{ + if (qemu_in_coroutine()) {{ + {graph_assume_lock} + {func.co_ret}{name}({ func.gen_list('{name}') }); + }} else {{ + {struct_name} s = {{ + .poll_state.ctx = qemu_get_current_aio_context(), + .poll_state.in_progress = true, + +{ func.gen_block(' .{name} = {name},') } + }}; + + s.poll_state.co = qemu_coroutine_create({name}_entry, &s); + + bdrv_poll_co(&s.poll_state); + {func.ret} + }} +}}""" + + +def create_co_wrapper(func: FuncDecl) -> str: + """ + Assumes we are not in coroutine, and creates one + """ + name = func.target_name + struct_name = func.struct_name + return f"""\ +{func.return_type} {func.name}({ func.gen_list('{decl}') }) +{{ + {struct_name} s = {{ + .poll_state.ctx = qemu_get_current_aio_context(), + .poll_state.in_progress = true, + +{ func.gen_block(' .{name} = {name},') } + }}; + assert(!qemu_in_coroutine()); + + s.poll_state.co = qemu_coroutine_create({name}_entry, &s); + + bdrv_poll_co(&s.poll_state); + {func.ret} +}}""" + + +def gen_co_wrapper(func: FuncDecl) -> str: assert not '_co_' in func.name - assert func.return_type == 'int' - assert func.args[0].type in ['BlockDriverState *', 'BdrvChild *', - 'BlockBackend *'] + assert func.wrapper_type == 'co' - subsystem, subname = func.name.split('_', 1) + name = func.target_name + struct_name = func.struct_name - name = f'{subsystem}_co_{subname}' + graph_lock='' + graph_unlock='' + if func.graph_rdlock: + graph_lock=' bdrv_graph_co_rdlock();' + graph_unlock=' bdrv_graph_co_rdunlock();' - t = func.args[0].type - if t == 'BlockDriverState *': - bs = 'bs' - elif t == 'BdrvChild *': - bs = 'child->bs' - else: - bs = 'blk_bs(blk)' - struct_name = snake_to_camel(name) + creation_function = create_mixed_wrapper + if func.create_only_co: + creation_function = create_co_wrapper return f"""\ /* @@ -123,6 +227,7 @@ def gen_wrapper(func: FuncDecl) -> str: typedef struct {struct_name} {{ BdrvPollCo poll_state; + {func.return_field} { func.gen_block(' {decl};') } }} {struct_name}; @@ -130,28 +235,67 @@ static void coroutine_fn {name}_entry(void *opaque) {{ {struct_name} *s = opaque; - s->poll_state.ret = {name}({ func.gen_list('s->{name}') }); +{graph_lock} + {func.get_result}{name}({ func.gen_list('s->{name}') }); +{graph_unlock} s->poll_state.in_progress = false; aio_wait_kick(); }} -int {func.name}({ func.gen_list('{decl}') }) +{creation_function(func)}""" + + +def gen_no_co_wrapper(func: FuncDecl) -> str: + assert '_co_' in func.name + assert func.wrapper_type == 'no_co' + + name = func.target_name + struct_name = func.struct_name + + graph_lock='' + graph_unlock='' + if func.graph_rdlock: + graph_lock=' bdrv_graph_rdlock_main_loop();' + graph_unlock=' bdrv_graph_rdunlock_main_loop();' + elif func.graph_wrlock: + graph_lock=' bdrv_graph_wrlock();' + graph_unlock=' bdrv_graph_wrunlock();' + + return f"""\ +/* + * Wrappers for {name} + */ + +typedef struct {struct_name} {{ + Coroutine *co; + {func.return_field} +{ func.gen_block(' {decl};') } +}} {struct_name}; + +static void {name}_bh(void *opaque) {{ - if (qemu_in_coroutine()) {{ - return {name}({ func.gen_list('{name}') }); - }} else {{ - {struct_name} s = {{ - .poll_state.bs = {bs}, - .poll_state.in_progress = true, + {struct_name} *s = opaque; -{ func.gen_block(' .{name} = {name},') } - }}; +{graph_lock} + {func.get_result}{name}({ func.gen_list('s->{name}') }); +{graph_unlock} - s.poll_state.co = qemu_coroutine_create({name}_entry, &s); + aio_co_wake(s->co); +}} - return bdrv_poll_co(&s.poll_state); - }} +{func.return_type} coroutine_fn {func.name}({ func.gen_list('{decl}') }) +{{ + {struct_name} s = {{ + .co = qemu_coroutine_self(), +{ func.gen_block(' .{name} = {name},') } + }}; + assert(qemu_in_coroutine()); + + aio_bh_schedule_oneshot(qemu_get_aio_context(), {name}_bh, &s); + qemu_coroutine_yield(); + + {func.ret} }}""" @@ -159,7 +303,10 @@ def gen_wrappers(input_code: str) -> str: res = '' for func in func_decl_iter(input_code): res += '\n\n\n' - res += gen_wrapper(func) + if func.wrapper_type == 'co': + res += gen_co_wrapper(func) + else: + res += gen_no_co_wrapper(func) return res diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index d900d18048..7026895074 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -35,6 +35,9 @@ my $summary_file = 0; my $root; my %debug; my $help = 0; +my $codespell = 0; +my $codespellfile = "/usr/share/codespell/dictionary.txt"; +my $user_codespellfile = ""; sub help { my ($exitcode) = @_; @@ -66,6 +69,9 @@ Options: is all off) --test-only=WORD report only warnings/errors containing WORD literally + --codespell Use the codespell dictionary for spelling/typos + (default: $codespellfile) + --codespellfile Use this codespell dictionary --color[=WHEN] Use colors 'always', 'never', or only when output is a terminal ('auto'). Default is 'auto'. -h, --help, --version display this help and exit @@ -85,28 +91,50 @@ foreach (@ARGV) { } GetOptions( - 'q|quiet+' => \$quiet, - 'tree!' => \$tree, - 'signoff!' => \$chk_signoff, - 'patch!' => \$chk_patch, - 'branch!' => \$chk_branch, - 'emacs!' => \$emacs, - 'terse!' => \$terse, - 'f|file!' => \$file, - 'strict!' => \$no_warnings, - 'root=s' => \$root, - 'summary!' => \$summary, - 'mailback!' => \$mailback, - 'summary-file!' => \$summary_file, - - 'debug=s' => \%debug, - 'test-only=s' => \$tst_only, - 'color=s' => \$color, - 'no-color' => sub { $color = 'never'; }, - 'h|help' => \$help, - 'version' => \$help + 'q|quiet+' => \$quiet, + 'tree!' => \$tree, + 'signoff!' => \$chk_signoff, + 'patch!' => \$chk_patch, + 'branch!' => \$chk_branch, + 'emacs!' => \$emacs, + 'terse!' => \$terse, + 'f|file!' => \$file, + 'strict!' => \$no_warnings, + 'root=s' => \$root, + 'summary!' => \$summary, + 'mailback!' => \$mailback, + 'summary-file!' => \$summary_file, + 'debug=s' => \%debug, + 'test-only=s' => \$tst_only, + 'codespell!' => \$codespell, + 'codespellfile=s' => \$user_codespellfile, + 'color=s' => \$color, + 'no-color' => sub { $color = 'never'; }, + 'h|help' => \$help, + 'version' => \$help ) or help(1); +if ($user_codespellfile) { + # Use the user provided codespell file unconditionally + $codespellfile = $user_codespellfile; +} elsif (!(-f $codespellfile)) { + # If /usr/share/codespell/dictionary.txt is not present, try to find it + # under codespell's install directory: /data/dictionary.txt + if (($codespell || $help) && which("python3") ne "") { + my $python_codespell_dict = << "EOF"; + +import os.path as op +import codespell_lib +codespell_dir = op.dirname(codespell_lib.__file__) +codespell_file = op.join(codespell_dir, 'data', 'dictionary.txt') +print(codespell_file, end='') +EOF + + my $codespell_dict = `python3 -c "$python_codespell_dict" 2> /dev/null`; + $codespellfile = $codespell_dict if (-f $codespell_dict); + } +} + help(0) if ($help); my $exit = 0; @@ -337,6 +365,36 @@ our @typeList = ( qr{guintptr}, ); +# Load common spelling mistakes and build regular expression list. +my $misspellings; +my %spelling_fix; + +if ($codespell) { + if (open(my $spelling, '<', $codespellfile)) { + while (<$spelling>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + next if ($line =~ m/, disabled/i); + + $line =~ s/,.*$//; + + my ($suspect, $fix) = split(/->/, $line); + + $spelling_fix{$suspect} = $fix; + } + close($spelling); + } else { + warn "No codespell typos will be found - file '$codespellfile': $!\n"; + } +} + +$misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix; + # This can be modified by sub possible. Since it can be empty, be careful # about regexes that always match, because they can cause infinite loops. our @modifierList = ( @@ -466,7 +524,7 @@ sub top_of_kernel_tree { my @tree_check = ( "COPYING", "MAINTAINERS", "Makefile", "README.rst", "docs", "VERSION", - "linux-user", "softmmu" + "linux-user", "system" ); foreach my $check (@tree_check) { @@ -477,6 +535,18 @@ sub top_of_kernel_tree { return 1; } +sub which { + my ($bin) = @_; + + foreach my $path (split(/:/, $ENV{PATH})) { + if (-e "$path/$bin") { + return "$path/$bin"; + } + } + + return ""; +} + sub expand_tabs { my ($str) = @_; @@ -1585,6 +1655,21 @@ sub process { WARN("8-bit UTF-8 used in possible commit log\n" . $herecurr); } +# Check for various typo / spelling mistakes + if (defined($misspellings) && + ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { + while ($rawline =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) { + my $typo = $1; + my $blank = copy_spacing($rawline); + my $ptr = substr($blank, 0, $-[1]) . "^" x length($typo); + my $hereptr = "$hereline$ptr\n"; + my $typo_fix = $spelling_fix{lc($typo)}; + $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/); + $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/); + WARN("'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $hereptr); + } + } + # ignore non-hunk lines and lines being removed next if (!$hunk_line || $line =~ /^-/); @@ -1621,7 +1706,7 @@ sub process { my $hex = qr/%[-+ *.0-9]*([hljztL]|ll|hh)?(x|X|"\s*PRI[xX][^"]*"?)/; - # don't consider groups splitted by [.:/ ], like 2A.20:12ab + # don't consider groups split by [.:/ ], like 2A.20:12ab my $tmpline = $rawline; $tmpline =~ s/($hex[.:\/ ])+$hex//g; @@ -1667,6 +1752,7 @@ sub process { # some scripts we imported from other projects. next if ($realfile =~ /\.(s|S)$/); next if ($realfile =~ /(checkpatch|get_maintainer)\.pl$/); + next if ($realfile =~ /^target\/hexagon\/imported\/*/); if ($rawline =~ /^\+.*\t/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; @@ -1680,8 +1766,10 @@ sub process { # Block comment styles # Block comments use /* on a line of its own - if ($rawline !~ m@^\+.*/\*.*\*/[ \t)}]*$@ && #inline /*...*/ - $rawline =~ m@^\+.*/\*\*?+[ \t]*[^ \t]@) { # /* or /** non-blank + my $commentline = $rawline; + while ($commentline =~ s@^(\+.*)/\*.*\*/@$1@o) { # remove inline /*...*/ + } + if ($commentline =~ m@^\+.*/\*\*?+[ \t]*[^ \t]@) { # /* or /** non-blank WARN("Block comments use a leading /* on a separate line\n" . $herecurr); } @@ -2862,6 +2950,14 @@ sub process { if ($line =~ /\bsignal\s*\(/ && !($line =~ /SIG_(?:IGN|DFL)/)) { ERROR("use sigaction to establish signal handlers; signal is not portable\n" . $herecurr); } +# recommend qemu_bh_new_guarded instead of qemu_bh_new + if ($realfile =~ /.*\/hw\/.*/ && $line =~ /\bqemu_bh_new\s*\(/) { + ERROR("use qemu_bh_new_guarded() instead of qemu_bh_new() to avoid reentrancy problems\n" . $herecurr); + } +# recommend aio_bh_new_guarded instead of aio_bh_new + if ($realfile =~ /.*\/hw\/.*/ && $line =~ /\baio_bh_new\s*\(/) { + ERROR("use aio_bh_new_guarded() instead of aio_bh_new() to avoid reentrancy problems\n" . $herecurr); + } # check for module_init(), use category-specific init macros explicitly please if ($line =~ /^module_init\s*\(/) { ERROR("please use block_init(), type_init() etc. instead of module_init()\n" . $herecurr); @@ -2979,6 +3075,9 @@ sub process { if ($line =~ /\bsysconf\(_SC_PAGESIZE\)/) { ERROR("use qemu_real_host_page_size() instead of sysconf(_SC_PAGESIZE)\n" . $herecurr); } + if ($line =~ /\b(g_)?assert\(0\)/) { + ERROR("use g_assert_not_reached() instead of assert(0)\n" . $herecurr); + } my $non_exit_glib_asserts = qr{g_assert_cmpstr| g_assert_cmpint| g_assert_cmpuint| diff --git a/scripts/ci/gitlab-kubernetes-runners/values.yaml b/scripts/ci/gitlab-kubernetes-runners/values.yaml new file mode 100644 index 0000000000..204a96a842 --- /dev/null +++ b/scripts/ci/gitlab-kubernetes-runners/values.yaml @@ -0,0 +1,30 @@ +gitlabUrl: "https://gitlab.com/" +runnerRegistrationToken: "" +rbac: + create: true +concurrent: 200 +runners: + privileged: true + config: | + [[runners]] + limit = 100 + environment = [ + "DOCKER_HOST=tcp://docker:2376", + "DOCKER_TLS_CERTDIR=/certs", + "DOCKER_TLS_VERIFY=1", + "DOCKER_CERT_PATH=/certs/client" + ] + [runners.kubernetes] + poll_timeout = 1200 + image = "ubuntu:20.04" + cpu_request = "0.5" + service_cpu_request = "0.5" + helper_cpu_request = "0.25" + cpu_request_overwrite_max_allowed = "7" + memory_request_overwrite_max_allowed = "30Gi" + [[runners.kubernetes.volumes.empty_dir]] + name = "docker-certs" + mount_path = "/certs/client" + medium = "Memory" + [runners.kubernetes.node_selector] + agentpool = "jobs" diff --git a/scripts/ci/gitlab-pipeline-status b/scripts/ci/gitlab-pipeline-status index 924db327ff..39f3c22c66 100755 --- a/scripts/ci/gitlab-pipeline-status +++ b/scripts/ci/gitlab-pipeline-status @@ -28,7 +28,7 @@ class CommunicationFailure(Exception): class NoPipelineFound(Exception): - """Communication is successfull but pipeline is not found.""" + """Communication is successful but pipeline is not found.""" def get_local_branch_commit(branch): @@ -131,7 +131,7 @@ def create_parser(): 'checks of the pipeline status. Defaults ' 'to %(default)s')) parser.add_argument('-w', '--wait', action='store_true', default=False, - help=('Wether to wait, instead of checking only once ' + help=('Whether to wait, instead of checking only once ' 'the status of a pipeline')) parser.add_argument('-p', '--project-id', type=int, default=11167699, help=('The GitLab project ID. Defaults to the project ' diff --git a/scripts/ci/org.centos/stream/8/build-environment.yml b/scripts/ci/org.centos/stream/8/build-environment.yml index 42b0471634..1ead77e2cb 100644 --- a/scripts/ci/org.centos/stream/8/build-environment.yml +++ b/scripts/ci/org.centos/stream/8/build-environment.yml @@ -10,6 +10,14 @@ check_mode: yes register: centos_stream_8 + - name: Enable EPEL repo on CentOS Stream 8 + dnf: + name: + - epel-release + state: present + when: + - centos_stream_8 + - name: Enable PowerTools repo on CentOS Stream 8 ini_file: path: /etc/yum.repos.d/CentOS-Stream-PowerTools.repo @@ -17,35 +25,58 @@ option: enabled value: "1" when: - - ansible_facts['distribution'] == 'CentOS' - - ansible_facts['distribution_major_version'] == '8' - centos_stream_8 - name: Install basic packages to build QEMU on CentOS Stream 8 dnf: name: + - bzip2 + - bzip2-devel + - capstone-devel + - dbus-daemon - device-mapper-multipath-devel + - diffutils + - gcc + - gcc-c++ + - genisoimage + - gettext + - git + - glib2-devel - glusterfs-api-devel - gnutls-devel + - libaio-devel - libcap-ng-devel - libcurl-devel + - libepoxy-devel - libfdt-devel + - libgcrypt-devel - libiscsi-devel - libpmem-devel - librados-devel - librbd-devel - libseccomp-devel + - libslirp-devel - libssh-devel - libxkbcommon-devel + - lzo-devel + - make + - mesa-libEGL-devel + - nettle-devel - ninja-build + - nmap-ncat - numactl-devel + - pixman-devel + - python38 - python3-sphinx + - rdma-core-devel - redhat-rpm-config - snappy-devel + - spice-glib-devel - spice-server-devel - systemd-devel + - systemtap-sdt-devel + - tar + - zlib-devel state: present when: - - ansible_facts['distribution'] == 'CentOS' - - ansible_facts['distribution_major_version'] == '8' - centos_stream_8 diff --git a/scripts/ci/org.centos/stream/8/x86_64/configure b/scripts/ci/org.centos/stream/8/x86_64/configure index a7f92aff90..76781f17f4 100755 --- a/scripts/ci/org.centos/stream/8/x86_64/configure +++ b/scripts/ci/org.centos/stream/8/x86_64/configure @@ -16,6 +16,7 @@ # that patches adding downstream specific devices are not available. # ../configure \ +--python=/usr/bin/python3.8 \ --prefix="/usr" \ --libdir="/usr/lib64" \ --datadir="/usr/share" \ @@ -28,15 +29,13 @@ --extra-cflags="-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection" \ --with-suffix="qemu-kvm" \ --firmwarepath=/usr/share/qemu-firmware \ ---with-git=meson \ ---with-git-submodules=update \ --target-list="x86_64-softmmu" \ --block-drv-rw-whitelist="qcow2,raw,file,host_device,nbd,iscsi,rbd,blkdebug,luks,null-co,nvme,copy-on-read,throttle,gluster" \ --audio-drv-list="" \ --block-drv-ro-whitelist="vmdk,vhdx,vpc,https,ssh" \ --with-coroutine=ucontext \ ---with-git=git \ --tls-priority=@QEMU,SYSTEM \ +--disable-af-xdp \ --disable-attr \ --disable-auth-pam \ --disable-avx2 \ @@ -70,7 +69,6 @@ --disable-gtk \ --disable-guest-agent \ --disable-guest-agent-msi \ ---disable-hax \ --disable-hvf \ --disable-iconv \ --disable-kvm \ @@ -137,7 +135,6 @@ --disable-vhost-vdpa \ --disable-virglrenderer \ --disable-virtfs \ ---disable-virtiofsd \ --disable-vnc \ --disable-vnc-jpeg \ --disable-png \ @@ -188,9 +185,8 @@ --enable-tcg \ --enable-tools \ --enable-tpm \ ---enable-trace-backend=dtrace \ +--enable-trace-backends=dtrace \ --enable-usb-redir \ ---enable-virtiofsd \ --enable-vhost-kernel \ --enable-vhost-net \ --enable-vhost-user \ diff --git a/scripts/ci/org.centos/stream/8/x86_64/test-avocado b/scripts/ci/org.centos/stream/8/x86_64/test-avocado index 7aeecbcfb8..73e7a1a312 100755 --- a/scripts/ci/org.centos/stream/8/x86_64/test-avocado +++ b/scripts/ci/org.centos/stream/8/x86_64/test-avocado @@ -4,7 +4,7 @@ # KVM and x86_64, or tests that are generic enough to be valid for all # targets. Such a test list can be generated with: # -# ./tests/venv/bin/avocado list --filter-by-tags-include-empty \ +# ./pyvenv/bin/avocado list --filter-by-tags-include-empty \ # --filter-by-tags-include-empty-key -t accel:kvm,arch:x86_64 \ # tests/avocado/ # @@ -14,13 +14,6 @@ # * Require machine type "x-remote": # - tests/avocado/multiprocess.py:Multiprocess.test_multiprocess_x86_64 # -# * Needs superuser privileges: -# - tests/avocado/virtiofs_submounts.py:VirtiofsSubmountsTest.test_pre_virtiofsd_set_up -# - tests/avocado/virtiofs_submounts.py:VirtiofsSubmountsTest.test_pre_launch_set_up -# - tests/avocado/virtiofs_submounts.py:VirtiofsSubmountsTest.test_post_launch_set_up -# - tests/avocado/virtiofs_submounts.py:VirtiofsSubmountsTest.test_post_mount_set_up -# - tests/avocado/virtiofs_submounts.py:VirtiofsSubmountsTest.test_two_runs -# # * Requires display type "egl-headless": # - tests/avocado/virtio-gpu.py:VirtioGPUx86.test_virtio_vga_virgl # - tests/avocado/virtio-gpu.py:VirtioGPUx86.test_vhost_user_vga_virgl @@ -29,7 +22,7 @@ # - tests/avocado/virtio_check_params.py:VirtioMaxSegSettingsCheck.test_machine_types # make get-vm-images -./tests/venv/bin/avocado run \ +./pyvenv/bin/avocado run \ --job-results-dir=tests/results/ \ tests/avocado/boot_linux.py:BootLinuxX8664.test_pc_i440fx_kvm \ tests/avocado/boot_linux.py:BootLinuxX8664.test_pc_q35_kvm \ @@ -37,6 +30,8 @@ make get-vm-images tests/avocado/cpu_queries.py:QueryCPUModelExpansion.test \ tests/avocado/empty_cpu_model.py:EmptyCPUModel.test \ tests/avocado/hotplug_cpu.py:HotPlugCPU.test \ + tests/avocado/netdev-ethtool.py:NetDevEthtool.test_igb \ + tests/avocado/netdev-ethtool.py:NetDevEthtool.test_igb_nomsi \ tests/avocado/info_usernet.py:InfoUsernet.test_hostfwd \ tests/avocado/intel_iommu.py:IntelIOMMU.test_intel_iommu \ tests/avocado/intel_iommu.py:IntelIOMMU.test_intel_iommu_pt \ diff --git a/scripts/ci/setup/build-environment.yml b/scripts/ci/setup/build-environment.yml index 232525b91d..f344d1a850 100644 --- a/scripts/ci/setup/build-environment.yml +++ b/scripts/ci/setup/build-environment.yml @@ -24,7 +24,6 @@ when: - ansible_facts['distribution'] == 'Ubuntu' - ansible_facts['architecture'] == 'aarch64' - - ansible_facts['distribution_version'] == '20.04' - name: Update apt cache / upgrade packages via apt apt: @@ -33,86 +32,131 @@ when: - ansible_facts['distribution'] == 'Ubuntu' - - name: Install basic packages to build QEMU on Ubuntu 20.04 + # lcitool variables -f json ubuntu-2204 qemu | jq -r '.pkgs[]' | xargs -n 1 echo "-" + - name: Install basic packages to build QEMU on Ubuntu 22.04 package: name: + - bash + - bc + - bison + - bsdextrautils + - bzip2 + - ca-certificates - ccache + - clang + - dbus + - debianutils + - diffutils + - exuberant-ctags + - findutils + - flex + - g++ - gcc + - gcovr + - genisoimage - gettext - git - - glusterfs-common + - hostname - libaio-dev + - libasan5 + - libasound2-dev - libattr1-dev + - libbpf-dev - libbrlapi-dev - libbz2-dev + - libc6-dev - libcacard-dev - libcap-ng-dev + - libcapstone-dev + - libcmocka-dev - libcurl4-gnutls-dev + - libdaxctl-dev - libdrm-dev - libepoxy-dev - libfdt-dev + - libffi-dev - libgbm-dev + - libgcrypt20-dev + - libglib2.0-dev + - libglusterfs-dev + - libgnutls28-dev - libgtk-3-dev + - libibumad-dev - libibverbs-dev - libiscsi-dev - libjemalloc-dev - libjpeg-turbo8-dev + - libjson-c-dev + - liblttng-ust-dev - liblzo2-dev - - libncurses5-dev - libncursesw5-dev - libnfs-dev - - libnss3-dev - libnuma-dev + - libpam0g-dev + - libpcre2-dev - libpixman-1-dev - - librados-dev + - libpmem-dev + - libpng-dev + - libpulse-dev - librbd-dev - librdmacm-dev - libsasl2-dev - libsdl2-dev + - libsdl2-image-dev - libseccomp-dev + - libslirp-dev - libsnappy-dev - libspice-protocol-dev + - libspice-server-dev - libssh-dev + - libsystemd-dev + - libtasn1-6-dev + - libubsan1 + - libudev-dev + - liburing-dev - libusb-1.0-0-dev - libusbredirhost-dev - libvdeplug-dev + - libvirglrenderer-dev - libvte-2.91-dev + - libxen-dev + - libxml2-dev - libzstd-dev + - llvm + - locales - make - - python3-yaml + - meson + - multipath-tools + - ncat + - nettle-dev + - ninja-build + - openssh-client + - pkgconf + - python3 + - python3-numpy + - python3-opencv + - python3-pillow + - python3-pip - python3-sphinx - python3-sphinx-rtd-theme - - ninja-build + - python3-venv + - python3-yaml + - rpm2cpio + - sed - sparse + - systemtap-sdt-dev + - tar + - tesseract-ocr + - tesseract-ocr-eng + - texinfo - xfslibs-dev + - zlib1g-dev state: present when: - ansible_facts['distribution'] == 'Ubuntu' + - ansible_facts['distribution_version'] == '22.04' - - name: Install packages to build QEMU on Ubuntu 20.04 on non-s390x - package: - name: - - libspice-server-dev - - libxen-dev - state: present - when: - - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['architecture'] != 's390x' - - - name: Install basic packages to build QEMU on Ubuntu 20.04 - package: - name: - # Originally from tests/docker/dockerfiles/ubuntu2004.docker - - clang-10 - - genisoimage - - liblttng-ust-dev - - libslirp-dev - - netcat-openbsd - when: - - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['distribution_version'] == '20.04' - - - name: Install armhf cross-compile packages to build QEMU on AArch64 Ubuntu 20.04 + - name: Install armhf cross-compile packages to build QEMU on AArch64 Ubuntu 22.04 package: name: - binutils-arm-linux-gnueabihf @@ -127,9 +171,28 @@ - zlib1g-dev:armhf when: - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['distribution_version'] == '20.04' + - ansible_facts['distribution_version'] == '22.04' - ansible_facts['architecture'] == 'aarch64' + - name: Enable EPEL repo on EL8 + dnf: + name: + - epel-release + state: present + when: + - ansible_facts['distribution_file_variety'] in ['RedHat', 'CentOS'] + - ansible_facts['distribution_major_version'] == '8' + + - name: Enable PowerTools repo on CentOS 8 + ini_file: + path: /etc/yum.repos.d/CentOS-Stream-PowerTools.repo + section: powertools + option: enabled + value: "1" + when: + - ansible_facts['distribution_file_variety'] == 'CentOS' + - ansible_facts['distribution_major_version'] == '8' + - name: Install basic packages to build QEMU on EL8 dnf: # This list of packages start with tests/docker/dockerfiles/centos8.docker @@ -138,7 +201,9 @@ name: - bzip2 - bzip2-devel + - capstone-devel - dbus-daemon + - device-mapper-multipath-devel - diffutils - gcc - gcc-c++ @@ -146,24 +211,64 @@ - gettext - git - glib2-devel + - glusterfs-api-devel + - gnutls-devel - libaio-devel + - libcap-ng-devel + - libcurl-devel - libepoxy-devel + - libfdt-devel - libgcrypt-devel + - libiscsi-devel + - libpmem-devel + - librados-devel + - librbd-devel + - libseccomp-devel + - libssh-devel + - libxkbcommon-devel - lzo-devel - make - mesa-libEGL-devel - nettle-devel + - ninja-build - nmap-ncat - - perl-Test-Harness + - numactl-devel - pixman-devel - - python36 + - python38 + - python3-sphinx - rdma-core-devel + - redhat-rpm-config + - snappy-devel - spice-glib-devel - - spice-server + - systemd-devel - systemtap-sdt-devel - tar - zlib-devel state: present when: - - ansible_facts['distribution_file_variety'] == 'RedHat' + - ansible_facts['distribution_file_variety'] in ['RedHat', 'CentOS'] - ansible_facts['distribution_version'] == '8' + + - name: Install packages only available on x86 and aarch64 + dnf: + # Spice server not available in ppc64le + name: + - spice-server + - spice-server-devel + state: present + when: + - ansible_facts['distribution_file_variety'] in ['RedHat', 'CentOS'] + - ansible_facts['distribution_version'] == '8' + - ansible_facts['architecture'] == 'aarch64' or ansible_facts['architecture'] == 'x86_64' + + - name: Check whether the Python runtime version is managed by alternatives + stat: + path: /etc/alternatives/python3 + register: python3 + + - name: Set default Python runtime to 3.8 on EL8 + command: alternatives --set python3 /usr/bin/python3.8 + when: + - ansible_facts['distribution_file_variety'] in ['RedHat', 'CentOS'] + - ansible_facts['distribution_version'] == '8' + - python3.stat.islnk and python3.stat.lnk_target != '/usr/bin/python3.8' diff --git a/scripts/ci/setup/gitlab-runner.yml b/scripts/ci/setup/gitlab-runner.yml index 33128be85d..7bdafab511 100644 --- a/scripts/ci/setup/gitlab-runner.yml +++ b/scripts/ci/setup/gitlab-runner.yml @@ -26,6 +26,7 @@ user: user: gitlab-runner group: gitlab-runner + groups: kvm comment: GitLab Runner home: /home/gitlab-runner shell: /bin/bash @@ -48,19 +49,43 @@ - debug: msg: gitlab-runner arch is {{ gitlab_runner_arch }} - - name: Download the matching gitlab-runner + - name: Download the matching gitlab-runner (DEB) get_url: - dest: /usr/local/bin/gitlab-runner - url: "https://s3.amazonaws.com/gitlab-runner-downloads/v{{ gitlab_runner_version }}/binaries/gitlab-runner-{{ gitlab_runner_os }}-{{ gitlab_runner_arch }}" - owner: gitlab-runner - group: gitlab-runner - mode: u=rwx,g=rwx,o=rx + dest: "/root/" + url: "https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_{{ gitlab_runner_arch }}.deb" + when: + - ansible_facts['distribution'] == 'Ubuntu' + + - name: Download the matching gitlab-runner (RPM) + get_url: + dest: "/root/" + url: "https://gitlab-runner-downloads.s3.amazonaws.com/latest/rpm/gitlab-runner_{{ gitlab_runner_arch }}.rpm" + when: + - ansible_facts['distribution'] == 'CentOS' + + - name: Install gitlab-runner via package manager (DEB) + apt: deb="/root/gitlab-runner_{{ gitlab_runner_arch }}.deb" + when: + - ansible_facts['distribution'] == 'Ubuntu' + + - name: Install gitlab-runner via package manager (RPM) + yum: name="/root/gitlab-runner_{{ gitlab_runner_arch }}.rpm" + when: + - ansible_facts['distribution'] == 'CentOS' - name: Register the gitlab-runner - command: "/usr/local/bin/gitlab-runner register --non-interactive --url {{ gitlab_runner_server_url }} --registration-token {{ gitlab_runner_registration_token }} --executor shell --tag-list {{ ansible_facts[\"architecture\"] }},{{ ansible_facts[\"distribution\"]|lower }}_{{ ansible_facts[\"distribution_version\"] }} --description '{{ ansible_facts[\"distribution\"] }} {{ ansible_facts[\"distribution_version\"] }} {{ ansible_facts[\"architecture\"] }} ({{ ansible_facts[\"os_family\"] }})'" + command: "/usr/bin/gitlab-runner register --non-interactive --url {{ gitlab_runner_server_url }} --registration-token {{ gitlab_runner_registration_token }} --executor shell --tag-list {{ ansible_facts[\"architecture\"] }},{{ ansible_facts[\"distribution\"]|lower }}_{{ ansible_facts[\"distribution_version\"] }} --description '{{ ansible_facts[\"distribution\"] }} {{ ansible_facts[\"distribution_version\"] }} {{ ansible_facts[\"architecture\"] }} ({{ ansible_facts[\"os_family\"] }})'" + + # The secondary runner will still run under the single gitlab-runner service + - name: Register secondary gitlab-runner + command: "/usr/bin/gitlab-runner register --non-interactive --url {{ gitlab_runner_server_url }} --registration-token {{ gitlab_runner_registration_token }} --executor shell --tag-list aarch32,{{ ansible_facts[\"distribution\"]|lower }}_{{ ansible_facts[\"distribution_version\"] }} --description '{{ ansible_facts[\"distribution\"] }} {{ ansible_facts[\"distribution_version\"] }} {{ ansible_facts[\"architecture\"] }} ({{ ansible_facts[\"os_family\"] }})'" + when: + - ansible_facts['distribution'] == 'Ubuntu' + - ansible_facts['architecture'] == 'aarch64' + - ansible_facts['distribution_version'] == '22.04' - name: Install the gitlab-runner service using its own functionality - command: /usr/local/bin/gitlab-runner install --user gitlab-runner --working-directory /home/gitlab-runner + command: "/usr/bin/gitlab-runner install --user gitlab-runner --working-directory /home/gitlab-runner" register: gitlab_runner_install_service_result failed_when: "gitlab_runner_install_service_result.rc != 0 and \"already exists\" not in gitlab_runner_install_service_result.stderr" @@ -69,41 +94,3 @@ name: gitlab-runner state: started enabled: yes - - - name: Download secondary gitlab-runner - get_url: - dest: /usr/local/bin/gitlab-runner-arm - url: "https://s3.amazonaws.com/gitlab-runner-downloads/v{{ gitlab_runner_version }}/binaries/gitlab-runner-{{ gitlab_runner_os }}-arm" - owner: gitlab-runner - group: gitlab-runner - mode: u=rwx,g=rwx,o=rx - when: - - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['architecture'] == 'aarch64' - - ansible_facts['distribution_version'] == '20.04' - - - name: Register secondary gitlab-runner - command: "/usr/local/bin/gitlab-runner-arm register --non-interactive --url {{ gitlab_runner_server_url }} --registration-token {{ gitlab_runner_registration_token }} --executor shell --tag-list aarch32,{{ ansible_facts[\"distribution\"]|lower }}_{{ ansible_facts[\"distribution_version\"] }} --description '{{ ansible_facts[\"distribution\"] }} {{ ansible_facts[\"distribution_version\"] }} {{ ansible_facts[\"architecture\"] }} ({{ ansible_facts[\"os_family\"] }})'" - when: - - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['architecture'] == 'aarch64' - - ansible_facts['distribution_version'] == '20.04' - - - name: Install the secondary gitlab-runner service using its own functionality - command: /usr/local/bin/gitlab-runner-arm install --user gitlab-runner --working-directory /home/gitlab-runner/arm -n gitlab-runner-arm - register: gitlab_runner_install_service_result - failed_when: "gitlab_runner_install_service_result.rc != 0 and \"already exists\" not in gitlab_runner_install_service_result.stderr" - when: - - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['architecture'] == 'aarch64' - - ansible_facts['distribution_version'] == '20.04' - - - name: Enable the secondary gitlab-runner service - service: - name: gitlab-runner-arm - state: started - enabled: yes - when: - - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['architecture'] == 'aarch64' - - ansible_facts['distribution_version'] == '20.04' diff --git a/scripts/ci/setup/vars.yml.template b/scripts/ci/setup/vars.yml.template index e48089761f..4b355fb80f 100644 --- a/scripts/ci/setup/vars.yml.template +++ b/scripts/ci/setup/vars.yml.template @@ -1,5 +1,3 @@ -# The version of the gitlab-runner to use -gitlab_runner_version: 13.12.0 # The URL of the gitlab server to use, usually https://gitlab.com unless you're # using a private GitLab instance gitlab_runner_server_url: https://gitlab.com diff --git a/scripts/clean-header-guards.pl b/scripts/clean-header-guards.pl index a6680253b1..a7fd8dc99f 100755 --- a/scripts/clean-header-guards.pl +++ b/scripts/clean-header-guards.pl @@ -32,8 +32,8 @@ use warnings; use Getopt::Std; # Stuff we don't want to clean because we import it into our tree: -my $exclude = qr,^(disas/libvixl/|include/standard-headers/ - |linux-headers/|pc-bios/|tests/tcg/|tests/multiboot/),x; +my $exclude = qr,^(include/standard-headers/|linux-headers/ + |pc-bios/|tests/tcg/|tests/multiboot/),x; # Stuff that is expected to fail the preprocessing test: my $exclude_cpp = qr,^include/libdecnumber/decNumberLocal.h,; diff --git a/scripts/clean-includes b/scripts/clean-includes index aaa7d4ceb3..bdbf404024 100755 --- a/scripts/clean-includes +++ b/scripts/clean-includes @@ -51,7 +51,7 @@ GIT=no DUPHEAD=no # Extended regular expression defining files to ignore when using --all -XDIRREGEX='^(tests/tcg|tests/multiboot|pc-bios|disas/libvixl)' +XDIRREGEX='^(tests/tcg|tests/multiboot|tests/fp|tests/plugin|tests/uefi-test-tools|pc-bios|subprojects|contrib/plugins|tools/ebpf|ebpf/rss.bpf.skeleton.h|linux-user/(mips64|x86_64)/(cpu_loop|signal).c)' while true do @@ -111,7 +111,12 @@ cat >"$COCCIFILE" < 1) print $0}' - if [ $? -eq 0 ]; then +if [ "$DUPHEAD" = "yes" ] && [ -n "$files" ]; then + if egrep "^[[:space:]]*#[[:space:]]*include" $files | tr -d '[:blank:]' \ + | sort | uniq -c | grep -v '^ *1 '; then echo "Found duplicate header file includes. Please check the above files manually." exit 1 fi fi if [ "$GIT" = "yes" ]; then - git add -- "$@" + git add -- $files git commit --signoff -F - < Patch: - """generate patch that will strip typedef from the struct declartion + """generate patch that will strip typedef from the struct declaration The caller is responsible for readding the typedef somewhere else. """ diff --git a/scripts/coverage/compare_gcov_json.py b/scripts/coverage/compare_gcov_json.py new file mode 100755 index 0000000000..1b92dc2c8c --- /dev/null +++ b/scripts/coverage/compare_gcov_json.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +# +# Compare output of two gcovr JSON reports and report differences. To +# generate the required output first: +# - create two build dirs with --enable-gcov +# - run set of tests in each +# - run make coverage-html in each +# - run gcovr --json --exclude-unreachable-branches \ +# --print-summary -o coverage.json --root ../../ . *.p +# +# Author: Alex Bennée +# +# SPDX-License-Identifier: GPL-2.0-or-later +# + +import argparse +import json +import sys +from pathlib import Path + +def create_parser(): + parser = argparse.ArgumentParser( + prog='compare_gcov_json', + description='analyse the differences in coverage between two runs') + + parser.add_argument('-a', type=Path, default=None, + help=('First file to check')) + + parser.add_argument('-b', type=Path, default=None, + help=('Second file to check')) + + parser.add_argument('--verbose', action='store_true', default=False, + help=('A minimal verbosity level that prints the ' + 'overall result of the check/wait')) + return parser + + +# See https://gcovr.com/en/stable/output/json.html#json-format-reference +def load_json(json_file_path: Path, verbose = False) -> dict[str, set[int]]: + + with open(json_file_path) as f: + data = json.load(f) + + root_dir = json_file_path.absolute().parent + covered_lines = dict() + + for filecov in data["files"]: + file_path = Path(filecov["file"]) + + # account for generated files - map into src tree + resolved_path = Path(file_path).absolute() + if resolved_path.is_relative_to(root_dir): + file_path = resolved_path.relative_to(root_dir) + # print(f"remapped {resolved_path} to {file_path}") + + lines = filecov["lines"] + + executed_lines = set( + linecov["line_number"] + for linecov in filecov["lines"] + if linecov["count"] != 0 and not linecov["gcovr/noncode"] + ) + + # if this file has any coverage add it to the system + if len(executed_lines) > 0: + if verbose: + print(f"file {file_path} {len(executed_lines)}/{len(lines)}") + covered_lines[str(file_path)] = executed_lines + + return covered_lines + +def find_missing_files(first, second): + """ + Return a list of files not covered in the second set + """ + missing_files = [] + for f in sorted(first): + file_a = first[f] + try: + file_b = second[f] + except KeyError: + missing_files.append(f) + + return missing_files + +def main(): + """ + Script entry point + """ + parser = create_parser() + args = parser.parse_args() + + if not args.a or not args.b: + print("We need two files to compare") + sys.exit(1) + + first_coverage = load_json(args.a, args.verbose) + second_coverage = load_json(args.b, args.verbose) + + first_missing = find_missing_files(first_coverage, + second_coverage) + + second_missing = find_missing_files(second_coverage, + first_coverage) + + a_name = args.a.parent.name + b_name = args.b.parent.name + + print(f"{b_name} missing coverage in {len(first_missing)} files") + for f in first_missing: + print(f" {f}") + + print(f"{a_name} missing coverage in {len(second_missing)} files") + for f in second_missing: + print(f" {f}") + + +if __name__ == '__main__': + main() diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index 183f26a32c..0e62f10aad 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -12,6 +12,9 @@ avr cris ~ (/qemu)?((/include)?/hw/cris/.*|/target/cris/.*) +hexagon-gen (component should be ignored in analysis) + ~ (/qemu)?(/target/hexagon/.*generated.*) + hexagon ~ (/qemu)?(/target/hexagon/.*) @@ -21,8 +24,11 @@ hppa i386 ~ (/qemu)?((/include)?/hw/i386/.*|/target/i386/.*|/hw/intc/[^/]*apic[^/]*\.c) +loongarch + ~ (/qemu)?((/include)?/hw/(loongarch/.*|.*/loongarch.*)|/target/loongarch/.*) + m68k - ~ (/qemu)?((/include)?/hw/m68k/.*|/target/m68k/.*|(/include)?/hw(/.*)?/mcf.*) + ~ (/qemu)?((/include)?/hw/m68k/.*|/target/m68k/.*|(/include)?/hw(/.*)?/mcf.*|(/include)?/hw/nubus/.*) microblaze ~ (/qemu)?((/include)?/hw/microblaze/.*|/target/microblaze/.*) @@ -33,11 +39,14 @@ mips nios2 ~ (/qemu)?((/include)?/hw/nios2/.*|/target/nios2/.*) +openrisc + ~ (/qemu)?((/include)?/hw/openrisc/.*|/target/openrisc/.*) + ppc ~ (/qemu)?((/include)?/hw/ppc/.*|/target/ppc/.*|/hw/pci-host/(uninorth.*|dec.*|prep.*|ppc.*)|/hw/misc/macio/.*|(/include)?/hw/.*/(xics|openpic|spapr).*) riscv - ~ (/qemu)?((/include)?/hw/riscv/.*|/target/riscv/.*) + ~ (/qemu)?((/include)?/hw/riscv/.*|/target/riscv/.*|/hw/.*/(riscv_|ibex_|sifive_).*) rx ~ (/qemu)?((/include)?/hw/rx/.*|/target/rx/.*) @@ -51,12 +60,12 @@ sh4 sparc ~ (/qemu)?((/include)?/hw/sparc(64)?.*|/target/sparc/.*|/hw/.*/grlib.*|/hw/display/cg3.c) -tilegx - ~ (/qemu)?(/target/tilegx/.*) - tricore ~ (/qemu)?((/include)?/hw/tricore/.*|/target/tricore/.*) +xtensa + ~ (/qemu)?((/include)?/hw/xtensa/.*|/target/xtensa/.*) + 9pfs ~ (/qemu)?(/hw/9pfs/.*|/fsdev/.*) @@ -64,16 +73,13 @@ audio ~ (/qemu)?((/include)?/(audio|hw/audio)/.*) block - ~ (/qemu)?(/block.*|(/include?)(/hw)?/(block|storage-daemon)/.*|(/include)?/hw/ide/.*|/qemu-(img|io).*|/util/(aio|async|thread-pool).*) + ~ (/qemu)?(/block.*|(/include?)/(block|storage-daemon)/.*|(/include)?/hw/(block|ide|nvme)/.*|/qemu-(img|io).*|/util/(aio|async|thread-pool).*) char ~ (/qemu)?(/qemu-char\.c|/include/sysemu/char\.h|(/include)?/hw/char/.*) -capstone - ~ (/qemu)?(/capstone/.*) - crypto - ~ (/qemu)?((/include)?/crypto/.*|/hw/.*/crypto.*) + ~ (/qemu)?((/include)?/crypto/.*|/hw/.*/.*crypto.*|(/include/sysemu|/backends)/cryptodev.*) disas ~ (/qemu)?((/include)?/disas.*) @@ -87,9 +93,6 @@ io ipmi ~ (/qemu)?((/include)?/hw/ipmi/.*) -libvixl - ~ (/qemu)?(/disas/libvixl/.*) - migration ~ (/qemu)?((/include)?/migration/.*) @@ -103,7 +106,7 @@ net ~ (/qemu)?((/include)?(/hw)?/(net|rdma)/.*) pci - ~ (/qemu)?(/hw/pci.*|/include/hw/pci.*) + ~ (/qemu)?(/include)?/hw/(cxl/|pci).* qemu-ga ~ (/qemu)?(/qga/.*) @@ -111,12 +114,6 @@ qemu-ga scsi ~ (/qemu)?(/scsi/.*|/hw/scsi/.*|/include/hw/scsi/.*) -slirp - ~ (/qemu)?(/.*slirp.*) - -tcg - ~ (/qemu)?(/accel/tcg/.*|/replay/.*|/(.*/)?softmmu.*) - trace ~ (/qemu)?(/.*trace.*\.[ch]) @@ -132,11 +129,26 @@ user util ~ (/qemu)?(/util/.*|/include/qemu/.*) +vfio + ~ (/qemu)?(/include)?/hw/vfio/.* + +virtio + ~ (/qemu)?(/include)?/hw/virtio/.* + xen ~ (/qemu)?(.*/xen.*) -virtiofsd - ~ (/qemu)?(/tools/virtiofsd/.*) +hvf + ~ (/qemu)?(.*/hvf.*) + +kvm + ~ (/qemu)?(.*/kvm.*) + +tcg + ~ (/qemu)?(/accel/tcg|/replay|/tcg)/.* + +sysemu + ~ (/qemu)?(/system/.*|/accel/.*) (headers) ~ (/qemu)?(/include/.*) diff --git a/scripts/coverity-scan/coverity-scan.docker b/scripts/coverity-scan/coverity-scan.docker index 6f60a52d23..a349578526 100644 --- a/scripts/coverity-scan/coverity-scan.docker +++ b/scripts/coverity-scan/coverity-scan.docker @@ -15,112 +15,152 @@ # The work of actually doing the build is handled by the # run-coverity-scan script. -FROM fedora:30 -ENV PACKAGES \ - alsa-lib-devel \ - bc \ - brlapi-devel \ - bzip2 \ - bzip2-devel \ - ccache \ - clang \ - curl \ - cyrus-sasl-devel \ - dbus-daemon \ - device-mapper-multipath-devel \ - findutils \ - 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 \ - 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 \ - 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 \ - usbredir-devel \ - virglrenderer-devel \ - vte291-devel \ - wget \ - which \ - xen-devel \ - xfsprogs-devel \ - zlib-devel -ENV QEMU_CONFIGURE_OPTS --python=/usr/bin/python3 +FROM registry.fedoraproject.org/fedora:37 -RUN dnf install -y $PACKAGES -RUN rpm -q $PACKAGES | sort > /packages.txt -ENV PATH $PATH:/usr/libexec/python3-sphinx/ +RUN dnf install -y nosync && \ + echo -e '#!/bin/sh\n\ +if test -d /usr/lib64\n\ +then\n\ + export LD_PRELOAD=/usr/lib64/nosync/nosync.so\n\ +else\n\ + export LD_PRELOAD=/usr/lib/nosync/nosync.so\n\ +fi\n\ +exec "$@"' > /usr/bin/nosync && \ + chmod +x /usr/bin/nosync && \ + nosync dnf update -y && \ + nosync dnf install -y \ + SDL2-devel \ + SDL2_image-devel \ + alsa-lib-devel \ + bash \ + bc \ + bison \ + brlapi-devel \ + bzip2 \ + bzip2-devel \ + ca-certificates \ + capstone-devel \ + ccache \ + clang \ + ctags \ + cyrus-sasl-devel \ + daxctl-devel \ + dbus-daemon \ + device-mapper-multipath-devel \ + diffutils \ + findutils \ + flex \ + fuse3-devel \ + gcc \ + gcc-c++ \ + gcovr \ + genisoimage \ + gettext \ + git \ + glib2-devel \ + glib2-static \ + glibc-langpack-en \ + glibc-static \ + glusterfs-api-devel \ + gnutls-devel \ + gtk3-devel \ + hostname \ + jemalloc-devel \ + json-c-devel \ + libaio-devel \ + libasan \ + libattr-devel \ + libbpf-devel \ + libcacard-devel \ + libcap-ng-devel \ + libcmocka-devel \ + libcurl-devel \ + libdrm-devel \ + libepoxy-devel \ + libfdt-devel \ + libffi-devel \ + libgcrypt-devel \ + libiscsi-devel \ + libjpeg-devel \ + libnfs-devel \ + libpmem-devel \ + libpng-devel \ + librbd-devel \ + libseccomp-devel \ + libselinux-devel \ + libslirp-devel \ + libssh-devel \ + libtasn1-devel \ + libubsan \ + liburing-devel \ + libusbx-devel \ + libzstd-devel \ + llvm \ + lttng-ust-devel \ + lzo-devel \ + make \ + mesa-libgbm-devel \ + meson \ + ncurses-devel \ + nettle-devel \ + ninja-build \ + nmap-ncat \ + numactl-devel \ + openssh-clients \ + pam-devel \ + pcre-static \ + pixman-devel \ + pkgconfig \ + pulseaudio-libs-devel \ + python3 \ + python3-PyYAML \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx_rtd_theme \ + rdma-core-devel \ + rpm \ + sed \ + snappy-devel \ + socat \ + sparse \ + spice-protocol \ + spice-server-devel \ + systemd-devel \ + systemtap-sdt-devel \ + tar \ + tesseract \ + tesseract-langpack-eng \ + usbredir-devel \ + util-linux \ + virglrenderer-devel \ + vte291-devel \ + which \ + xen-devel \ + xfsprogs-devel \ + zlib-devel \ + zlib-static \ + zstd && \ + nosync dnf autoremove -y && \ + nosync dnf clean all -y && \ + rpm -qa | sort > /packages.txt && \ + mkdir -p /usr/libexec/ccache-wrappers && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc + +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" +ENV LANG "en_US.UTF-8" +ENV MAKE "/usr/bin/make" +ENV NINJA "/usr/bin/ninja" +ENV PYTHON "/usr/bin/python3" +ENV QEMU_CONFIGURE_OPTS --meson=internal + +RUN dnf install -y curl wget ENV COVERITY_TOOL_BASE=/coverity-tools COPY coverity_tool.tgz coverity_tool.tgz RUN mkdir -p /coverity-tools/coverity_tool && cd /coverity-tools/coverity_tool && tar xf /coverity_tool.tgz diff --git a/scripts/coverity-scan/model.c b/scripts/coverity-scan/model.c index 686d1a3008..a064d84084 100644 --- a/scripts/coverity-scan/model.c +++ b/scripts/coverity-scan/model.c @@ -42,94 +42,6 @@ typedef _Bool bool; typedef struct va_list_str *va_list; -/* exec.c */ - -typedef struct AddressSpace AddressSpace; -typedef struct MemoryRegionCache MemoryRegionCache; -typedef uint64_t hwaddr; -typedef uint32_t MemTxResult; -typedef struct MemTxAttrs {} MemTxAttrs; - -static void __bufwrite(uint8_t *buf, ssize_t len) -{ - int first, last; - __coverity_negative_sink__(len); - if (len == 0) return; - buf[0] = first; - buf[len-1] = last; - __coverity_writeall__(buf); -} - -static void __bufread(uint8_t *buf, ssize_t len) -{ - __coverity_negative_sink__(len); - if (len == 0) return; - int first = buf[0]; - int last = buf[len-1]; -} - -MemTxResult address_space_read_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, - void *buf, int len) -{ - MemTxResult result; - // TODO: investigate impact of treating reads as producing - // tainted data, with __coverity_tainted_data_argument__(buf). - __bufwrite(buf, len); - return result; -} - -MemTxResult address_space_write_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, - const void *buf, int len) -{ - MemTxResult result; - __bufread(buf, len); - return result; -} - -MemTxResult address_space_rw_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, - void *buf, int len, bool is_write) -{ - if (is_write) { - return address_space_write_cached(cache, addr, attrs, buf, len); - } else { - return address_space_read_cached(cache, addr, attrs, buf, len); - } -} - -MemTxResult address_space_read(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, - void *buf, int len) -{ - MemTxResult result; - // TODO: investigate impact of treating reads as producing - // tainted data, with __coverity_tainted_data_argument__(buf). - __bufwrite(buf, len); - return result; -} - -MemTxResult address_space_write(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, - const void *buf, int len) -{ - MemTxResult result; - __bufread(buf, len); - return result; -} - -MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, - void *buf, int len, bool is_write) -{ - if (is_write) { - return address_space_write(as, addr, attrs, buf, len); - } else { - return address_space_read(as, addr, attrs, buf, len); - } -} - /* Tainting */ typedef struct {} name2keysym_t; diff --git a/scripts/coverity-scan/run-coverity-scan b/scripts/coverity-scan/run-coverity-scan index 129672c86f..43cf770f5e 100755 --- a/scripts/coverity-scan/run-coverity-scan +++ b/scripts/coverity-scan/run-coverity-scan @@ -28,6 +28,7 @@ # project settings, if you have maintainer access there. # Command line options: +# --check-upload-only : return success if upload is possible # --dry-run : run the tools, but don't actually do the upload # --docker : create and work inside a container # --docker-engine : specify the container engine to use (docker/podman/auto); @@ -57,18 +58,18 @@ # 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 COVERITY_TOKEN, PROJNAME and DRYRUN have been initialized. +upload_permitted() { + # Check whether we can do an upload to the server; will exit *the script* + # with status 99 if the check failed (usually a bad token); + # will return from the function with status 1 if the check indicated + # that we can't upload yet (ie we are at quota) + # Assumes that COVERITY_TOKEN and PROJNAME have been initialized. echo "Checking upload permissions..." if ! up_perm="$(wget https://scan.coverity.com/api/upload_permitted --post-data "token=$COVERITY_TOKEN&project=$PROJNAME" -q -O -)"; then echo "Coverity Scan API access denied: bad token?" - exit 1 + exit 99 fi # Really up_perm is a JSON response with either @@ -76,25 +77,40 @@ check_upload_permissions() { # We do some hacky string parsing instead of properly parsing it. case "$up_perm" in *upload_permitted*true*) - echo "Coverity Scan: upload permitted" + return 0 ;; *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 + return 1 ;; *) echo "Coverity Scan upload check: unexpected result $up_perm" - exit 1 + exit 99 ;; esac } +check_upload_permissions() { + # Check whether we can do an upload to the server; will exit the script + # with status 99 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 COVERITY_TOKEN, PROJNAME and DRYRUN have been initialized. + + if upload_permitted; then + echo "Coverity Scan: upload permitted" + else + 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 + fi +} + + build_docker_image() { # build docker container including the coverity-scan tools echo "Building docker container..." @@ -116,14 +132,14 @@ update_coverity_tools () { cd "$COVERITY_TOOL_BASE" echo "Checking for new version of coverity build tools..." - wget https://scan.coverity.com/download/linux64 --post-data "token=$COVERITY_TOKEN&project=$PROJNAME&md5=1" -O coverity_tool.md5.new + wget https://scan.coverity.com/download/cxx/linux64 --post-data "token=$COVERITY_TOKEN&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=$COVERITY_TOKEN&project=$PROJNAME" -O coverity_tool.tgz + wget https://scan.coverity.com/download/cxx/linux64 --post-data "token=$COVERITY_TOKEN&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 @@ -152,9 +168,14 @@ update_coverity_tools () { DRYRUN=no UPDATE=yes DOCKER=no +PROJNAME=QEMU while [ "$#" -ge 1 ]; do case "$1" in + --check-upload-only) + shift + DRYRUN=check + ;; --dry-run) shift DRYRUN=yes @@ -251,6 +272,11 @@ if [ -z "$COVERITY_TOKEN" ]; then exit 1 fi +if [ "$DRYRUN" = check ]; then + upload_permitted + exit $? +fi + if [ -z "$COVERITY_BUILD_CMD" ]; then NPROC=$(nproc) COVERITY_BUILD_CMD="make -j$NPROC" @@ -266,7 +292,6 @@ if [ -z "$SRCDIR" ]; then SRCDIR="$PWD" fi -PROJNAME=QEMU TARBALL=cov-int.tar.xz if [ "$UPDATE" = only ]; then diff --git a/scripts/cpu-x86-uarch-abi.py b/scripts/cpu-x86-uarch-abi.py index 82ff07582f..7360e55c6e 100644 --- a/scripts/cpu-x86-uarch-abi.py +++ b/scripts/cpu-x86-uarch-abi.py @@ -85,7 +85,7 @@ skip = [ names = [] -for model in models["return"]: +for model in models: if "alias-of" in model: continue names.append(model["name"]) @@ -94,11 +94,11 @@ models = {} for name in sorted(names): cpu = shell.cmd("query-cpu-model-expansion", - { "type": "static", - "model": { "name": name }}) + type="static", + model={ "name": name }) got = {} - for (feature, present) in cpu["return"]["model"]["props"].items(): + for (feature, present) in cpu["model"]["props"].items(): if present and feature not in skip: got[feature] = True @@ -179,7 +179,6 @@ for level in range(len(abi_models)): models[name]["delta"][level] = delta def print_uarch_abi_csv(): - print("# Automatically generated from '%s'" % __file__) print("Model,baseline,v2,v3,v4") for name in models.keys(): print(name, end="") diff --git a/scripts/decodetree.py b/scripts/decodetree.py index a03dc6b5e3..e8b72da3a9 100644 --- a/scripts/decodetree.py +++ b/scripts/decodetree.py @@ -35,12 +35,14 @@ arguments = {} formats = {} allpatterns = [] anyextern = False +testforerror = False translate_prefix = 'trans' translate_scope = 'static ' input_file = '' output_file = None output_fd = None +output_null = False insntype = 'uint32_t' decode_function = 'decode' @@ -53,11 +55,89 @@ re_fld_ident = '%[a-zA-Z0-9_]*' re_fmt_ident = '@[a-zA-Z0-9_]*' re_pat_ident = '[a-zA-Z0-9_]*' +# Local implementation of a topological sort. We use the same API that +# the Python graphlib does, so that when QEMU moves forward to a +# baseline of Python 3.9 or newer this code can all be dropped and +# replaced with: +# from graphlib import TopologicalSorter, CycleError +# +# https://docs.python.org/3.9/library/graphlib.html#graphlib.TopologicalSorter +# +# We only implement the parts of TopologicalSorter we care about: +# ts = TopologicalSorter(graph=None) +# create the sorter. graph is a dictionary whose keys are +# nodes and whose values are lists of the predecessors of that node. +# (That is, if graph contains "A" -> ["B", "C"] then we must output +# B and C before A.) +# ts.static_order() +# returns a list of all the nodes in sorted order, or raises CycleError +# CycleError +# exception raised if there are cycles in the graph. The second +# element in the args attribute is a list of nodes which form a +# cycle; the first and last element are the same, eg [a, b, c, a] +# (Our implementation doesn't give the order correctly.) +# +# For our purposes we can assume that the data set is always small +# (typically 10 nodes or less, actual links in the graph very rare), +# so we don't need to worry about efficiency of implementation. +# +# The core of this implementation is from +# https://code.activestate.com/recipes/578272-topological-sort/ +# (but updated to Python 3), and is under the MIT license. + +class CycleError(ValueError): + """Subclass of ValueError raised if cycles exist in the graph""" + pass + +class TopologicalSorter: + """Topologically sort a graph""" + def __init__(self, graph=None): + self.graph = graph + + def static_order(self): + # We do the sort right here, unlike the stdlib version + from functools import reduce + data = {} + r = [] + + if not self.graph: + return [] + + # This code wants the values in the dict to be specifically sets + for k, v in self.graph.items(): + data[k] = set(v) + + # Find all items that don't depend on anything. + extra_items_in_deps = (reduce(set.union, data.values()) + - set(data.keys())) + # Add empty dependencies where needed + data.update({item:{} for item in extra_items_in_deps}) + while True: + ordered = set(item for item, dep in data.items() if not dep) + if not ordered: + break + r.extend(ordered) + data = {item: (dep - ordered) + for item, dep in data.items() + if item not in ordered} + if data: + # This doesn't give as nice results as the stdlib, which + # gives you the cycle by listing the nodes in order. Here + # we only know the nodes in the cycle but not their order. + raise CycleError(f'nodes are in a cycle', list(data.keys())) + + return r +# end TopologicalSorter + def error_with_file(file, lineno, *args): """Print an error message from file:line and args and exit.""" global output_file global output_fd + # For the test suite expected-errors case, don't print the + # string "error: ", so they don't turn up as false positives + # if you grep the meson logs for strings like that. + end = 'error: ' if not testforerror else 'detected: ' prefix = '' if file: prefix += f'{file}:' @@ -65,13 +145,13 @@ def error_with_file(file, lineno, *args): prefix += f'{lineno}:' if prefix: prefix += ' ' - print(prefix, end='error: ', file=sys.stderr) + print(prefix, end=end, file=sys.stderr) print(*args, file=sys.stderr) if output_file and output_fd: output_fd.close() os.remove(output_file) - exit(1) + exit(0 if testforerror else 1) # end error_with_file @@ -205,11 +285,14 @@ class Field: s = '' return str(self.pos) + ':' + s + str(self.len) - def str_extract(self): + def str_extract(self, lvalue_formatter): global bitop_width s = 's' if self.sign else '' return f'{s}extract{bitop_width}(insn, {self.pos}, {self.len})' + def referenced_fields(self): + return [] + def __eq__(self, other): return self.sign == other.sign and self.mask == other.mask @@ -228,12 +311,12 @@ class MultiField: def __str__(self): return str(self.subs) - def str_extract(self): + def str_extract(self, lvalue_formatter): global bitop_width ret = '0' pos = 0 for f in reversed(self.subs): - ext = f.str_extract() + ext = f.str_extract(lvalue_formatter) if pos == 0: ret = ext else: @@ -241,6 +324,12 @@ class MultiField: pos += f.len return ret + def referenced_fields(self): + l = [] + for f in self.subs: + l.extend(f.referenced_fields()) + return l + def __ne__(self, other): if len(self.subs) != len(other.subs): return True @@ -264,9 +353,12 @@ class ConstField: def __str__(self): return str(self.value) - def str_extract(self): + def str_extract(self, lvalue_formatter): return str(self.value) + def referenced_fields(self): + return [] + def __cmp__(self, other): return self.value - other.value # end ConstField @@ -283,8 +375,12 @@ class FunctionField: def __str__(self): return self.func + '(' + str(self.base) + ')' - def str_extract(self): - return self.func + '(ctx, ' + self.base.str_extract() + ')' + def str_extract(self, lvalue_formatter): + return (self.func + '(ctx, ' + + self.base.str_extract(lvalue_formatter) + ')') + + def referenced_fields(self): + return self.base.referenced_fields() def __eq__(self, other): return self.func == other.func and self.base == other.base @@ -304,9 +400,12 @@ class ParameterField: def __str__(self): return self.func - def str_extract(self): + def str_extract(self, lvalue_formatter): return self.func + '(ctx)' + def referenced_fields(self): + return [] + def __eq__(self, other): return self.func == other.func @@ -314,6 +413,32 @@ class ParameterField: return not self.__eq__(other) # end ParameterField +class NamedField: + """Class representing a field already named in the pattern""" + def __init__(self, name, sign, len): + self.mask = 0 + self.sign = sign + self.len = len + self.name = name + + def __str__(self): + return self.name + + def str_extract(self, lvalue_formatter): + global bitop_width + s = 's' if self.sign else '' + lvalue = lvalue_formatter(self.name) + return f'{s}extract{bitop_width}({lvalue}, 0, {self.len})' + + def referenced_fields(self): + return [self.name] + + def __eq__(self, other): + return self.name == other.name + + def __ne__(self, other): + return not self.__eq__(other) +# end NamedField class Arguments: """Class representing the extracted fields of a format""" @@ -337,7 +462,6 @@ class Arguments: output('} ', self.struct_name(), ';\n\n') # end Arguments - class General: """Common code between instruction formats and instruction patterns""" def __init__(self, name, lineno, base, fixb, fixm, udfm, fldm, flds, w): @@ -351,12 +475,59 @@ class General: self.fieldmask = fldm self.fields = flds self.width = w + self.dangling = None def __str__(self): return self.name + ' ' + str_match_bits(self.fixedbits, self.fixedmask) def str1(self, i): return str_indent(i) + self.__str__() + + def dangling_references(self): + # Return a list of all named references which aren't satisfied + # directly by this format/pattern. This will be either: + # * a format referring to a field which is specified by the + # pattern(s) using it + # * a pattern referring to a field which is specified by the + # format it uses + # * a user error (referring to a field that doesn't exist at all) + if self.dangling is None: + # Compute this once and cache the answer + dangling = [] + for n, f in self.fields.items(): + for r in f.referenced_fields(): + if r not in self.fields: + dangling.append(r) + self.dangling = dangling + return self.dangling + + def output_fields(self, indent, lvalue_formatter): + # We use a topological sort to ensure that any use of NamedField + # comes after the initialization of the field it is referencing. + graph = {} + for n, f in self.fields.items(): + refs = f.referenced_fields() + graph[n] = refs + + try: + ts = TopologicalSorter(graph) + for n in ts.static_order(): + # We only want to emit assignments for the keys + # in our fields list, not for anything that ends up + # in the tsort graph only because it was referenced as + # a NamedField. + try: + f = self.fields[n] + output(indent, lvalue_formatter(n), ' = ', + f.str_extract(lvalue_formatter), ';\n') + except KeyError: + pass + except CycleError as e: + # The second element of args is a list of nodes which form + # a cycle (there might be others too, but only one is reported). + # Pretty-print it to tell the user. + cycle = ' => '.join(e.args[1]) + error(self.lineno, 'field definitions form a cycle: ' + cycle) # end General @@ -370,8 +541,7 @@ class Format(General): def output_extract(self): output('static void ', self.extract_name(), '(DisasContext *ctx, ', self.base.struct_name(), ' *a, ', insntype, ' insn)\n{\n') - for n, f in self.fields.items(): - output(' a->', n, ' = ', f.str_extract(), ';\n') + self.output_fields(str_indent(4), lambda n: 'a->' + n) output('}\n\n') # end Format @@ -392,11 +562,36 @@ class Pattern(General): ind = str_indent(i) arg = self.base.base.name output(ind, '/* ', self.file, ':', str(self.lineno), ' */\n') + # We might have named references in the format that refer to fields + # in the pattern, or named references in the pattern that refer + # to fields in the format. This affects whether we extract the fields + # for the format before or after the ones for the pattern. + # For simplicity we don't allow cross references in both directions. + # This is also where we catch the syntax error of referring to + # a nonexistent field. + fmt_refs = self.base.dangling_references() + for r in fmt_refs: + if r not in self.fields: + error(self.lineno, f'format refers to undefined field {r}') + pat_refs = self.dangling_references() + for r in pat_refs: + if r not in self.base.fields: + error(self.lineno, f'pattern refers to undefined field {r}') + if pat_refs and fmt_refs: + error(self.lineno, ('pattern that uses fields defined in format ' + 'cannot use format that uses fields defined ' + 'in pattern')) + if fmt_refs: + # pattern fields first + self.output_fields(ind, lambda n: 'u.f_' + arg + '.' + n) + assert not extracted, "dangling fmt refs but it was already extracted" if not extracted: output(ind, self.base.extract_name(), '(ctx, &u.f_', arg, ', insn);\n') - for n, f in self.fields.items(): - output(ind, 'u.f_', arg, '.', n, ' = ', f.str_extract(), ';\n') + if not fmt_refs: + # pattern fields last + self.output_fields(ind, lambda n: 'u.f_' + arg + '.' + n) + output(ind, 'if (', translate_prefix, '_', self.name, '(ctx, &u.f_', arg, ')) return true;\n') @@ -473,7 +668,7 @@ class MultiPattern(General): def prop_format(self): for p in self.pats: - p.build_tree() + p.prop_format() def prop_width(self): width = None @@ -505,6 +700,12 @@ class IncMultiPattern(MultiPattern): output(ind, '}\n') else: p.output_code(i, extracted, p.fixedbits, p.fixedmask) + + def build_tree(self): + if not self.pats: + error_with_file(self.file, self.lineno, 'empty pattern group') + super().build_tree() + #end IncMultiPattern @@ -536,8 +737,10 @@ class Tree: ind = str_indent(i) # If we identified all nodes below have the same format, - # extract the fields now. - if not extracted and self.base: + # extract the fields now. But don't do it if the format relies + # on named fields from the insn pattern, as those won't have + # been initialised at this point. + if not extracted and self.base and not self.base.dangling_references(): output(ind, self.base.extract_name(), '(ctx, &u.f_', self.base.base.name, ', insn);\n') extracted = True @@ -623,7 +826,7 @@ class ExcMultiPattern(MultiPattern): return t def build_tree(self): - super().prop_format() + super().build_tree() self.tree = self.__build_tree(self.pats, self.fixedbits, self.fixedmask) @@ -659,6 +862,7 @@ def parse_field(lineno, name, toks): """Parse one instruction field from TOKS at LINENO""" global fields global insnwidth + global re_C_ident # A "simple" field will have only one entry; # a "multifield" will have several. @@ -673,6 +877,25 @@ def parse_field(lineno, name, toks): func = func[1] continue + if re.fullmatch(re_C_ident + ':s[0-9]+', t): + # Signed named field + subtoks = t.split(':') + n = subtoks[0] + le = int(subtoks[1]) + f = NamedField(n, True, le) + subs.append(f) + width += le + continue + if re.fullmatch(re_C_ident + ':[0-9]+', t): + # Unsigned named field + subtoks = t.split(':') + n = subtoks[0] + le = int(subtoks[1]) + f = NamedField(n, False, le) + subs.append(f) + width += le + continue + if re.fullmatch('[0-9]+:s[0-9]+', t): # Signed field extract subtoks = t.split(':s') @@ -1278,6 +1501,7 @@ def main(): global translate_prefix global output_fd global output_file + global output_null global input_file global insnwidth global insntype @@ -1286,11 +1510,13 @@ def main(): global bitop_width global variablewidth global anyextern + global testforerror decode_scope = 'static ' long_opts = ['decode=', 'translate=', 'output=', 'insnwidth=', - 'static-decode=', 'varinsnwidth='] + 'static-decode=', 'varinsnwidth=', 'test-for-error', + 'output-null'] try: (opts, args) = getopt.gnu_getopt(sys.argv[1:], 'o:vw:', long_opts) except getopt.GetoptError as err: @@ -1319,6 +1545,10 @@ def main(): bitop_width = 64 elif insnwidth != 32: error(0, 'cannot handle insns of width', insnwidth) + elif o == '--test-for-error': + testforerror = True + elif o == '--output-null': + output_null = True else: assert False, 'unhandled option' @@ -1348,7 +1578,9 @@ def main(): stree = build_size_tree(toppat.pats, 8, 0, 0) prop_size(stree) - if output_file: + if output_null: + output_fd = open(os.devnull, 'wt', encoding='utf-8', errors="ignore") + elif output_file: output_fd = open(output_file, 'wt', encoding='utf-8') else: output_fd = io.TextIOWrapper(sys.stdout.buffer, @@ -1417,6 +1649,7 @@ def main(): if output_file: output_fd.close() + exit(1 if testforerror else 0) # end main diff --git a/scripts/device-crash-test b/scripts/device-crash-test index 73bcb98693..da8b56edd9 100755 --- a/scripts/device-crash-test +++ b/scripts/device-crash-test @@ -43,7 +43,7 @@ except ModuleNotFoundError as exc: print(f"Module '{exc.name}' not found.") print(" Try 'make check-venv' from your build directory,") print(" and then one way to run this script is like so:") - print(f' > $builddir/tests/venv/bin/python3 "{path}"') + print(f' > $builddir/pyvenv/bin/python3 "{path}"') sys.exit(1) logger = logging.getLogger('device-crash-test') @@ -269,14 +269,14 @@ def formatTestCase(t): def qomListTypeNames(vm, **kwargs): """Run qom-list-types QMP command, return type names""" - types = vm.command('qom-list-types', **kwargs) + types = vm.cmd('qom-list-types', **kwargs) return [t['name'] for t in types] def infoQDM(vm): """Parse 'info qdm' output""" args = {'command-line': 'info qdm'} - devhelp = vm.command('human-monitor-command', **args) + devhelp = vm.cmd('human-monitor-command', **args) for l in devhelp.split('\n'): l = l.strip() if l == '' or l.endswith(':'): @@ -304,9 +304,9 @@ class QemuBinaryInfo(object): # there's no way to query DeviceClass::user_creatable using QMP, # so use 'info qdm': self.no_user_devs = set([d['name'] for d in infoQDM(vm, ) if d['no-user']]) - self.machines = list(m['name'] for m in vm.command('query-machines')) + self.machines = list(m['name'] for m in vm.cmd('query-machines')) self.user_devs = self.alldevs.difference(self.no_user_devs) - self.kvm_available = vm.command('query-kvm')['enabled'] + self.kvm_available = vm.cmd('query-kvm')['enabled'] finally: vm.shutdown() @@ -397,7 +397,7 @@ def binariesToTest(args, testcase): def accelsToTest(args, testcase): - if getBinaryInfo(args, testcase['binary']).kvm_available: + if getBinaryInfo(args, testcase['binary']).kvm_available and not args.tcg_only: yield 'kvm' yield 'tcg' @@ -510,6 +510,8 @@ def main(): help="Full mode: test cases that are expected to fail") parser.add_argument('--strict', action='store_true', dest='strict', help="Treat all warnings as fatal") + parser.add_argument('--tcg-only', action='store_true', dest='tcg_only', + help="Only test with TCG accelerator") parser.add_argument('qemu', nargs='*', metavar='QEMU', help='QEMU binary to run') args = parser.parse_args() diff --git a/scripts/feature_to_c.py b/scripts/feature_to_c.py new file mode 100644 index 0000000000..807af0e685 --- /dev/null +++ b/scripts/feature_to_c.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later + +import os, sys, xml.etree.ElementTree + +def writeliteral(indent, bytes): + sys.stdout.write(' ' * indent) + sys.stdout.write('"') + quoted = True + + for c in bytes: + if not quoted: + sys.stdout.write('\n') + sys.stdout.write(' ' * indent) + sys.stdout.write('"') + quoted = True + + if c == b'"'[0]: + sys.stdout.write('\\"') + elif c == b'\\'[0]: + sys.stdout.write('\\\\') + elif c == b'\n'[0]: + sys.stdout.write('\\n"') + quoted = False + elif c >= 32 and c < 127: + sys.stdout.write(c.to_bytes(1, 'big').decode()) + else: + sys.stdout.write(f'\{c:03o}') + + if quoted: + sys.stdout.write('"') + +sys.stdout.write('#include "qemu/osdep.h"\n' \ + '#include "exec/gdbstub.h"\n' \ + '\n' + 'const GDBFeature gdb_static_features[] = {\n') + +for input in sys.argv[1:]: + with open(input, 'rb') as file: + read = file.read() + + parser = xml.etree.ElementTree.XMLPullParser(['start', 'end']) + parser.feed(read) + events = parser.read_events() + event, element = next(events) + if event != 'start': + sys.stderr.write(f'unexpected event: {event}\n') + exit(1) + if element.tag != 'feature': + sys.stderr.write(f'unexpected start tag: {element.tag}\n') + exit(1) + + feature_name = element.attrib['name'] + regnum = 0 + regnames = [] + regnums = [] + tags = ['feature'] + for event, element in events: + if event == 'end': + if element.tag != tags[len(tags) - 1]: + sys.stderr.write(f'unexpected end tag: {element.tag}\n') + exit(1) + + tags.pop() + if element.tag == 'feature': + break + elif event == 'start': + if len(tags) < 2 and element.tag == 'reg': + if 'regnum' in element.attrib: + regnum = int(element.attrib['regnum']) + + regnames.append(element.attrib['name']) + regnums.append(regnum) + regnum += 1 + + tags.append(element.tag) + else: + raise Exception(f'unexpected event: {event}\n') + + if len(tags): + sys.stderr.write('unterminated feature tag\n') + exit(1) + + base_reg = min(regnums) + num_regs = max(regnums) - base_reg + 1 if len(regnums) else 0 + + sys.stdout.write(' {\n') + writeliteral(8, bytes(os.path.basename(input), 'utf-8')) + sys.stdout.write(',\n') + writeliteral(8, read) + sys.stdout.write(',\n') + writeliteral(8, bytes(feature_name, 'utf-8')) + sys.stdout.write(',\n (const char * const []) {\n') + + for index, regname in enumerate(regnames): + sys.stdout.write(f' [{regnums[index] - base_reg}] =\n') + writeliteral(16, bytes(regname, 'utf-8')) + sys.stdout.write(',\n') + + sys.stdout.write(f' }},\n {num_regs},\n }},\n') + +sys.stdout.write(' { NULL }\n};\n') diff --git a/scripts/feature_to_c.sh b/scripts/feature_to_c.sh deleted file mode 100644 index b1169899c1..0000000000 --- a/scripts/feature_to_c.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/sh - -# Convert text files to compilable C arrays. -# -# Copyright (C) 2007 Free Software Foundation, Inc. -# -# This file is part of GDB. -# -# 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 . - -if test -z "$1"; then - echo "Usage: $0 INPUTFILE..." - exit 1 -fi - -for input; do - arrayname=xml_feature_$(echo $input | sed 's,.*/,,; s/[-.]/_/g') - - ${AWK:-awk} 'BEGIN { n = 0 - printf "#include \"qemu/osdep.h\"\n" - print "static const char '$arrayname'[] = {" - for (i = 0; i < 255; i++) - _ord_[sprintf("%c", i)] = i - } { - split($0, line, ""); - printf " " - for (i = 1; i <= length($0); i++) { - c = line[i] - if (c == "'\''") { - printf "'\''\\'\'''\'', " - } else if (c == "\\") { - printf "'\''\\\\'\'', " - } else if (_ord_[c] >= 32 && _ord_[c] < 127) { - printf "'\''%s'\'', ", c - } else { - printf "'\''\\%03o'\'', ", _ord_[c] - } - if (i % 10 == 0) - printf "\n " - } - printf "'\''\\n'\'', \n" - } END { - print " 0 };" - }' < $input -done - -echo -echo "const char *const xml_builtin[][2] = {" - -for input; do - basename=$(echo $input | sed 's,.*/,,') - arrayname=xml_feature_$(echo $input | sed 's,.*/,,; s/[-.]/_/g') - echo " { \"$basename\", $arrayname }," -done - -echo " { (char *)0, (char *)0 }" -echo "};" diff --git a/scripts/gensyscalls.sh b/scripts/gensyscalls.sh index 8fb450e3c9..a2f7664b7b 100755 --- a/scripts/gensyscalls.sh +++ b/scripts/gensyscalls.sh @@ -44,6 +44,7 @@ read_includes() cpp -P -nostdinc -fdirectives-only \ -D_UAPI_ASM_$(upper ${arch})_BITSPERLONG_H \ + -D__ASM_$(upper ${arch})_BITSPERLONG_H \ -D__BITS_PER_LONG=${bits} \ -I${linux}/arch/${arch}/include/uapi/ \ -I${linux}/include/uapi \ @@ -99,4 +100,5 @@ 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" generate_syscall_nr hexagon 32 "$output/linux-user/hexagon/syscall_nr.h" +generate_syscall_nr loongarch 64 "$output/linux-user/loongarch64/syscall_nr.h" rm -fr "$TMP" diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl index e5499b94b4..00a0870b26 100755 --- a/scripts/get_maintainer.pl +++ b/scripts/get_maintainer.pl @@ -796,7 +796,7 @@ sub top_of_tree { && (-d "${lk_path}docs") && (-f "${lk_path}VERSION") && (-d "${lk_path}linux-user/") - && (-d "${lk_path}softmmu/")) { + && (-d "${lk_path}system/")) { return 1; } return 0; @@ -907,6 +907,7 @@ sub get_subsystem_name { if (length($subsystem) > 20) { $subsystem = substr($subsystem, 0, 17); $subsystem =~ s/\s*$//; + $subsystem =~ s/[()]//g; $subsystem = $subsystem . "..."; } return $subsystem; diff --git a/scripts/git-submodule.sh b/scripts/git-submodule.sh index e225d3a963..bb1222c772 100755 --- a/scripts/git-submodule.sh +++ b/scripts/git-submodule.sh @@ -9,95 +9,111 @@ command=$1 shift maybe_modules="$@" -# if --with-git-submodules=ignore, do nothing -test "$command" = "ignore" && exit 0 - -test -z "$GIT" && GIT=git +test -z "$maybe_modules" && exit 0 +test -z "$GIT" && GIT=$(command -v git) cd "$(dirname "$0")/.." +no_git_error= +if ! test -e ".git"; then + no_git_error='no git checkout exists' +elif test -z "$GIT"; then + no_git_error='git binary not found' +fi + +is_git() { + test -z "$no_git_error" +} + update_error() { echo "$0: $*" echo echo "Unable to automatically checkout GIT submodules '$modules'." echo "If you require use of an alternative GIT binary (for example to" - echo "enable use of a transparent proxy), then please specify it by" - echo "running configure by with the '--with-git' argument. e.g." + echo "enable use of a transparent proxy), please disable automatic" + echo "GIT submodule checkout with:" echo - echo " $ ./configure --with-git='tsocks git'" - echo - echo "Alternatively you may disable automatic GIT submodule checkout" - echo "with:" - echo - echo " $ ./configure --with-git-submodules=validate" + echo " $ ./configure --disable-download" echo echo "and then manually update submodules prior to running make, with:" echo - echo " $ scripts/git-submodule.sh update $modules" + echo " $ GIT='tsocks git' scripts/git-submodule.sh update $modules" echo exit 1 } validate_error() { - if test "$1" = "validate"; then + if is_git && test "$1" = "validate"; then echo "GIT submodules checkout is out of date, and submodules" echo "configured for validate only. Please run" echo " scripts/git-submodule.sh update $maybe_modules" echo "from the source directory or call configure with" - echo " --with-git-submodules=update" - echo "To disable GIT submodules validation, use" - echo " --with-git-submodules=ignore" + echo " --enable-download" fi exit 1 } -modules="" -for m in $maybe_modules -do - $GIT submodule status $m 1> /dev/null 2>&1 - if test $? = 0 - then - modules="$modules $m" - else - echo "warn: ignoring non-existent submodule $m" - fi -done +check_updated() { + local CURSTATUS OLDSTATUS + CURSTATUS=$($GIT submodule status $module) + OLDSTATUS=$(grep $module $substat) + test "$CURSTATUS" = "$OLDSTATUS" +} -if test -n "$maybe_modules" && ! test -e ".git" -then - echo "$0: unexpectedly called with submodules but no git checkout exists" - exit 1 +if is_git; then + test -e $substat || touch $substat + modules="" + for m in $maybe_modules + do + $GIT submodule status $m 1> /dev/null 2>&1 + if test $? = 0 + then + modules="$modules $m" + grep $m $substat > /dev/null 2>&1 || $GIT submodule status $module >> $substat + else + echo "warn: ignoring non-existent submodule $m" + fi + done +else + modules=$maybe_modules fi case "$command" in status|validate) - if test -z "$maybe_modules" - then - test -s ${substat} && validate_error "$command" || exit 0 - fi - - test -f "$substat" || validate_error "$command" for module in $modules; do - CURSTATUS=$($GIT submodule status $module) - OLDSTATUS=$(cat $substat | grep $module) - if test "$CURSTATUS" != "$OLDSTATUS"; then + if is_git; then + check_updated $module || validate_error "$command" + elif ! (set xyz "$module"/* && test -e "$2"); then + # The directory does not exist or it contains no files + echo "$0: sources not available for $module and $no_git_error" validate_error "$command" fi done - exit 0 ;; + update) - if test -z "$maybe_modules" - then - test -e $substat || touch $substat - exit 0 - fi + is_git || { + echo "$0: unexpectedly called with submodules but $no_git_error" + exit 1 + } $GIT submodule update --init $modules 1>/dev/null test $? -ne 0 && update_error "failed to update modules" + for module in $modules; do + check_updated $module || echo Updated "$module" + done - $GIT submodule status $modules > "${substat}" - test $? -ne 0 && update_error "failed to save git submodule status" >&2 + (while read -r REPLY; do + for module in $modules; do + case $REPLY in + *" $module "*) continue 2 ;; + esac + done + printf '%s\n' "$REPLY" + done + $GIT submodule status $modules + test $? -ne 0 && update_error "failed to save git submodule status" >&2) < $substat > $substat.new + mv -f $substat.new $substat ;; esac diff --git a/scripts/git.orderfile b/scripts/git.orderfile index b32203b710..8edac0380b 100644 --- a/scripts/git.orderfile +++ b/scripts/git.orderfile @@ -9,6 +9,8 @@ # git config diff.orderFile scripts/git.orderfile # +MAINTAINERS + # Documentation docs/* *.rst diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap index ce27f5e635..3fb4d5b342 100755 --- a/scripts/kvm/vmxcap +++ b/scripts/kvm/vmxcap @@ -115,6 +115,7 @@ controls = [ (50, 53): 'VMCS memory type', 54: 'INS/OUTS instruction information', 55: 'IA32_VMX_TRUE_*_CTLS support', + 56: 'Skip checks on event error code', }, msr = MSR_IA32_VMX_BASIC, ), diff --git a/scripts/make-config-poison.sh b/scripts/make-config-poison.sh index d222a04304..2b36907e23 100755 --- a/scripts/make-config-poison.sh +++ b/scripts/make-config-poison.sh @@ -4,13 +4,14 @@ if test $# = 0; then exit 0 fi -# Create list of config switches that should be poisoned in common code... -# but filter out CONFIG_TCG and CONFIG_USER_ONLY which are special. +# Create list of config switches that should be poisoned in common code, +# but filter out several which are handled manually. exec sed -n \ -e' /CONFIG_TCG/d' \ -e '/CONFIG_USER_ONLY/d' \ + -e '/CONFIG_SOFTMMU/d' \ -e '/^#define / {' \ -e 's///' \ -e 's/ .*//' \ -e 's/^/#pragma GCC poison /p' \ - -e '}' "$@" + -e '}' "$@" | sort -u diff --git a/scripts/make-release b/scripts/make-release index 05b14ecc95..6e0433de24 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -10,14 +10,27 @@ # This work is licensed under the terms of the GNU GPLv2 or later. # See the COPYING file in the top-level directory. +if [ $# -ne 2 ]; then + echo "Usage:" + echo " $0 gitrepo version" + exit 0 +fi + +# Only include wraps that are invoked with subproject() +SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3 berkeley-testfloat-3" + src="$1" version="$2" destination=qemu-${version} -git clone "${src}" ${destination} +git clone --single-branch -b "v${version}" -c advice.detachedHead=false \ + "${src}" ${destination} + pushd ${destination} -git checkout "v${version}" -git submodule update --init + +git submodule update --init --single-branch +meson subprojects download $SUBPROJECTS + (cd roms/seabios && git describe --tags --long --dirty > .version) (cd roms/skiboot && ./make_version.sh > .version) # Fetch edk2 submodule's submodules, since it won't have access to them via @@ -28,11 +41,11 @@ git submodule update --init # submodule dependencies, so we continue to handle these on a case-by-case # basis for now. (cd roms/edk2 && \ - git submodule update --init -- \ + git submodule update --init --depth 1 -- \ ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 \ BaseTools/Source/C/BrotliCompress/brotli \ CryptoPkg/Library/OpensslLib/openssl \ MdeModulePkg/Library/BrotliCustomDecompressLib/brotli) popd -tar --exclude=.git -cjf ${destination}.tar.bz2 ${destination} +tar --exclude=.git -cJf ${destination}.tar.xz ${destination} rm -rf ${destination} diff --git a/scripts/meson-buildoptions.py b/scripts/meson-buildoptions.py old mode 100755 new mode 100644 index e624c16b01..4814a8ff61 --- a/scripts/meson-buildoptions.py +++ b/scripts/meson-buildoptions.py @@ -25,35 +25,54 @@ import textwrap import shlex import sys +# Options with nonstandard names (e.g. --with/--without) or OS-dependent +# defaults. Try not to add any. SKIP_OPTIONS = { "default_devices", "fuzzing_engine", - "qemu_suffix", - "smbd", } +# Options whose name doesn't match the option for backwards compatibility +# reasons, because Meson gives them a funny name, or both OPTION_NAMES = { "b_coverage": "gcov", "b_lto": "lto", + "coroutine_backend": "with-coroutine", + "debug": "debug-info", "malloc": "enable-malloc", "pkgversion": "with-pkgversion", "qemu_firmwarepath": "firmwarepath", + "qemu_suffix": "with-suffix", "trace_backends": "enable-trace-backends", "trace_file": "with-trace-file", } +# Options that configure autodetects, even though meson defines them as boolean +AUTO_OPTIONS = { + "plugins", + "werror", +} + +# Builtin options that should be definable via configure. Some of the others +# we really do not want (e.g. c_args is defined via the native file, not +# via -D, because it's a mix of CFLAGS and --extra-cflags); for specific +# cases "../configure -D" can be used as an escape hatch. BUILTIN_OPTIONS = { "b_coverage", "b_lto", + "bindir", "datadir", + "debug", "includedir", "libdir", "libexecdir", "localedir", "localstatedir", "mandir", + "prefix", "strip", "sysconfdir", + "werror", } LINE_WIDTH = 76 @@ -61,7 +80,10 @@ LINE_WIDTH = 76 # Convert the default value of an option to the string used in # the help message -def value_to_help(value): +def get_help(opt): + if opt["name"] == "libdir": + return 'system default' + value = opt["value"] if isinstance(value, list): return ",".join(value) if isinstance(value, bool): @@ -88,7 +110,7 @@ def sh_print(line=""): def help_line(left, opt, indent, long): right = f'{opt["description"]}' if long: - value = value_to_help(opt["value"]) + value = get_help(opt) if value != "auto" and value != "": right += f" [{value}]" if "choices" in opt and long: @@ -156,12 +178,13 @@ def cli_metavar(opt): if opt["type"] == "string": return "VALUE" if opt["type"] == "array": - return "CHOICES" + return "CHOICES" if "choices" in opt else "VALUES" return "CHOICE" def print_help(options): print("meson_options_help() {") + feature_opts = [] for opt in sorted(options, key=cli_help_key): key = cli_help_key(opt) # The first section includes options that have an arguments, @@ -170,7 +193,7 @@ def print_help(options): metavar = cli_metavar(opt) left = f"--{key}={metavar}" help_line(left, opt, 27, True) - elif opt["type"] == "boolean": + elif opt["type"] == "boolean" and opt["name"] not in AUTO_OPTIONS: left = f"--{key}" help_line(left, opt, 27, False) elif allow_arg(opt): @@ -179,16 +202,17 @@ def print_help(options): else: left = f"--{key}=CHOICE" help_line(left, opt, 27, True) + else: + feature_opts.append(opt) sh_print() sh_print("Optional features, enabled with --enable-FEATURE and") sh_print("disabled with --disable-FEATURE, default is enabled if available") sh_print("(unless built with --without-default-features):") sh_print() - for opt in options: - key = opt["name"].replace("_", "-") - if opt["type"] != "boolean" and not allow_arg(opt): - help_line(key, opt, 18, False) + for opt in sorted(feature_opts, key=cli_option): + key = cli_option(opt) + help_line(key, opt, 18, False) print("}") @@ -199,7 +223,10 @@ def print_parse(options): key = cli_option(opt) name = opt["name"] if require_arg(opt): - print(f' --{key}=*) quote_sh "-D{name}=$2" ;;') + if opt["type"] == "array" and not "choices" in opt: + print(f' --{key}=*) quote_sh "-D{name}=$(meson_option_build_array $2)" ;;') + else: + print(f' --{key}=*) quote_sh "-D{name}=$2" ;;') elif opt["type"] == "boolean": print(f' --enable-{key}) printf "%s" -D{name}=true ;;') print(f' --disable-{key}) printf "%s" -D{name}=false ;;') diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 00ea4d8cd1..680fa3f581 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -1,7 +1,9 @@ # This file is generated by meson-buildoptions.py, do not edit! meson_options_help() { - printf "%s\n" ' --audio-drv-list=CHOICES Set audio driver list [default] (choices:' - printf "%s\n" ' alsa/coreaudio/default/dsound/jack/oss/pa/sdl)' + printf "%s\n" ' --audio-drv-list=CHOICES Set audio driver list [default] (choices: alsa/co' + printf "%s\n" ' reaudio/default/dsound/jack/oss/pa/pipewire/sdl/s' + printf "%s\n" ' ndio)' + printf "%s\n" ' --bindir=VALUE Executable directory [bin]' printf "%s\n" ' --block-drv-ro-whitelist=VALUE' printf "%s\n" ' set block driver read-only whitelist (by default' printf "%s\n" ' affects only QEMU, not tools like qemu-img)' @@ -10,7 +12,13 @@ meson_options_help() { printf "%s\n" ' affects only QEMU, not tools like qemu-img)' printf "%s\n" ' --datadir=VALUE Data file directory [share]' printf "%s\n" ' --disable-coroutine-pool coroutine freelist (better performance)' + printf "%s\n" ' --disable-debug-info Enable debug symbols and other information' + printf "%s\n" ' --disable-hexagon-idef-parser' + printf "%s\n" ' use idef-parser to automatically generate TCG' + printf "%s\n" ' code for the Hexagon frontend' printf "%s\n" ' --disable-install-blobs install provided firmware blobs' + printf "%s\n" ' --disable-qom-cast-debug cast debugging support' + printf "%s\n" ' --disable-relocatable toggle relocatable install' printf "%s\n" ' --docdir=VALUE Base directory for documentation installation' printf "%s\n" ' (can be empty) [share/doc]' printf "%s\n" ' --enable-block-drv-whitelist-in-tools' @@ -18,65 +26,84 @@ meson_options_help() { printf "%s\n" ' QEMU' printf "%s\n" ' --enable-cfi Control-Flow Integrity (CFI)' printf "%s\n" ' --enable-cfi-debug Verbose errors in case of CFI violation' + printf "%s\n" ' --enable-debug-graph-lock' + printf "%s\n" ' graph lock debugging support' printf "%s\n" ' --enable-debug-mutex mutex debugging support' printf "%s\n" ' --enable-debug-stack-usage' printf "%s\n" ' measure coroutine stack usage' + printf "%s\n" ' --enable-debug-tcg TCG debugging' printf "%s\n" ' --enable-fdt[=CHOICE] Whether and how to find the libfdt library' printf "%s\n" ' (choices: auto/disabled/enabled/internal/system)' printf "%s\n" ' --enable-fuzzing build fuzzing targets' printf "%s\n" ' --enable-gcov Enable coverage tracking.' - printf "%s\n" ' --enable-gprof QEMU profiling with gprof' printf "%s\n" ' --enable-lto Use link time optimization' printf "%s\n" ' --enable-malloc=CHOICE choose memory allocator to use [system] (choices:' printf "%s\n" ' jemalloc/system/tcmalloc)' printf "%s\n" ' --enable-module-upgrades try to load modules from alternate paths for' printf "%s\n" ' upgrades' - printf "%s\n" ' --enable-profiler profiler support' - printf "%s\n" ' --enable-qom-cast-debug cast debugging support' printf "%s\n" ' --enable-rng-none dummy RNG, avoid using /dev/(u)random and' printf "%s\n" ' getrandom()' - printf "%s\n" ' --enable-slirp[=CHOICE] Whether and how to find the slirp library' - printf "%s\n" ' (choices: auto/disabled/enabled/internal/system)' + printf "%s\n" ' --enable-safe-stack SafeStack Stack Smash Protection (requires' + printf "%s\n" ' clang/llvm and coroutine backend ucontext)' + printf "%s\n" ' --enable-sanitizers enable default sanitizers' printf "%s\n" ' --enable-strip Strip targets on install' printf "%s\n" ' --enable-tcg-interpreter TCG with bytecode interpreter (slow)' printf "%s\n" ' --enable-trace-backends=CHOICES' printf "%s\n" ' Set available tracing backends [log] (choices:' printf "%s\n" ' dtrace/ftrace/log/nop/simple/syslog/ust)' - printf "%s\n" ' --firmwarepath=VALUE search PATH for firmware files [qemu-firmware]' + printf "%s\n" ' --enable-tsan enable thread sanitizer' + printf "%s\n" ' --firmwarepath=VALUES search PATH for firmware files [share/qemu-' + printf "%s\n" ' firmware]' printf "%s\n" ' --iasl=VALUE Path to ACPI disassembler' printf "%s\n" ' --includedir=VALUE Header file directory [include]' printf "%s\n" ' --interp-prefix=VALUE where to find shared libraries etc., use %M for' printf "%s\n" ' cpu name [/usr/gnemul/qemu-%M]' - printf "%s\n" ' --libdir=VALUE Library directory [lib64]' + printf "%s\n" ' --libdir=VALUE Library directory [system default]' printf "%s\n" ' --libexecdir=VALUE Library executable directory [libexec]' printf "%s\n" ' --localedir=VALUE Locale data directory [share/locale]' printf "%s\n" ' --localstatedir=VALUE Localstate data directory [/var/local]' printf "%s\n" ' --mandir=VALUE Manual page directory [share/man]' - printf "%s\n" ' --sphinx-build=VALUE Use specified sphinx-build for building document' + printf "%s\n" ' --prefix=VALUE Installation prefix [/usr/local]' + printf "%s\n" ' --qemu-ga-distro=VALUE second path element in qemu-ga registry entries' + printf "%s\n" ' [Linux]' + printf "%s\n" ' --qemu-ga-manufacturer=VALUE' + printf "%s\n" ' "manufacturer" name for qemu-ga registry entries' + printf "%s\n" ' [QEMU]' + printf "%s\n" ' --qemu-ga-version=VALUE version number for qemu-ga installer' + printf "%s\n" ' --smbd=VALUE Path to smbd for slirp networking' printf "%s\n" ' --sysconfdir=VALUE Sysconf data directory [etc]' printf "%s\n" ' --tls-priority=VALUE Default TLS protocol/cipher priority string' printf "%s\n" ' [NORMAL]' + printf "%s\n" ' --with-coroutine=CHOICE coroutine backend to use (choices:' + printf "%s\n" ' auto/sigaltstack/ucontext/windows)' printf "%s\n" ' --with-pkgversion=VALUE use specified string as sub-version of the' printf "%s\n" ' package' + printf "%s\n" ' --with-suffix=VALUE Suffix for QEMU data/modules/config directories' + printf "%s\n" ' (can be empty) [qemu]' printf "%s\n" ' --with-trace-file=VALUE Trace file prefix for simple backend [trace]' printf "%s\n" '' printf "%s\n" 'Optional features, enabled with --enable-FEATURE and' printf "%s\n" 'disabled with --disable-FEATURE, default is enabled if available' printf "%s\n" '(unless built with --without-default-features):' printf "%s\n" '' + printf "%s\n" ' af-xdp AF_XDP network backend support' printf "%s\n" ' alsa ALSA sound support' printf "%s\n" ' attr attr/xattr support' printf "%s\n" ' auth-pam PAM access control' printf "%s\n" ' avx2 AVX2 optimizations' + printf "%s\n" ' avx512bw AVX512BW optimizations' printf "%s\n" ' avx512f AVX512F optimizations' + printf "%s\n" ' blkio libblkio block device driver' printf "%s\n" ' bochs bochs image format support' printf "%s\n" ' bpf eBPF support' printf "%s\n" ' brlapi brlapi character device driver' printf "%s\n" ' bzip2 bzip2 support for DMG images' + printf "%s\n" ' canokey CanoKey support' printf "%s\n" ' cap-ng cap_ng support' printf "%s\n" ' capstone Whether and how to find the capstone library' printf "%s\n" ' cloop cloop image format support' printf "%s\n" ' cocoa Cocoa user interface (macOS only)' + printf "%s\n" ' colo-proxy colo-proxy support' printf "%s\n" ' coreaudio CoreAudio sound support' printf "%s\n" ' crypto-afalg Linux AF_ALG crypto backend driver' printf "%s\n" ' curl CURL block device driver' @@ -93,9 +120,10 @@ meson_options_help() { printf "%s\n" ' glusterfs Glusterfs block device driver' printf "%s\n" ' gnutls GNUTLS cryptography support' printf "%s\n" ' gtk GTK+ user interface' + printf "%s\n" ' gtk-clipboard clipboard support for the gtk UI (EXPERIMENTAL, MAY HANG)' printf "%s\n" ' guest-agent Build QEMU Guest Agent' printf "%s\n" ' guest-agent-msi Build MSI package for the QEMU Guest Agent' - printf "%s\n" ' hax HAX acceleration support' + printf "%s\n" ' hv-balloon hv-balloon driver (requires Glib 2.68+ GTree API)' printf "%s\n" ' hvf HVF acceleration support' printf "%s\n" ' iconv Font glyph conversion support' printf "%s\n" ' jack JACK sound support' @@ -103,12 +131,15 @@ meson_options_help() { printf "%s\n" ' kvm KVM acceleration support' printf "%s\n" ' l2tpv3 l2tpv3 network backend support' printf "%s\n" ' libdaxctl libdaxctl support' + printf "%s\n" ' libdw debuginfo support' printf "%s\n" ' libiscsi libiscsi userspace initiator' + printf "%s\n" ' libkeyutils Linux keyutils support' printf "%s\n" ' libnfs libnfs block device driver' printf "%s\n" ' libpmem libpmem support' printf "%s\n" ' libssh ssh block device support' printf "%s\n" ' libudev Use libudev to enumerate host devices' printf "%s\n" ' libusb libusb support for USB passthrough' + printf "%s\n" ' libvduse build VDUSE Library' printf "%s\n" ' linux-aio Linux AIO support' printf "%s\n" ' linux-io-uring Linux io_uring support' printf "%s\n" ' live-block-migration' @@ -117,6 +148,7 @@ meson_options_help() { printf "%s\n" ' lzo lzo compression support' printf "%s\n" ' malloc-trim enable libc malloc_trim() for memory optimization' printf "%s\n" ' membarrier membarrier system call (for Linux 4.14+ or Windows' + printf "%s\n" ' modules modules support (non Windows)' printf "%s\n" ' mpath Multipath persistent reservation passthrough' printf "%s\n" ' multiprocess Out of process device emulation support' printf "%s\n" ' netmap netmap network backend support' @@ -127,6 +159,9 @@ meson_options_help() { printf "%s\n" ' oss OSS sound support' printf "%s\n" ' pa PulseAudio sound support' printf "%s\n" ' parallels parallels image format support' + printf "%s\n" ' pipewire PipeWire sound support' + printf "%s\n" ' pixman pixman support' + printf "%s\n" ' plugins TCG plugins via shared library loading' printf "%s\n" ' png PNG support with libpng' printf "%s\n" ' pvrdma Enable PVRDMA support' printf "%s\n" ' qcow1 qcow1 image format support' @@ -135,16 +170,20 @@ meson_options_help() { printf "%s\n" ' rbd Ceph block device driver' printf "%s\n" ' rdma Enable RDMA-based migration' printf "%s\n" ' replication replication support' + printf "%s\n" ' rutabaga-gfx rutabaga_gfx support' printf "%s\n" ' sdl SDL user interface' printf "%s\n" ' sdl-image SDL Image support for icons' printf "%s\n" ' seccomp seccomp support' printf "%s\n" ' selinux SELinux support in qemu-nbd' + printf "%s\n" ' slirp libslirp user mode network backend support' printf "%s\n" ' slirp-smbd use smbd (at path --smbd=*) in slirp networking' printf "%s\n" ' smartcard CA smartcard emulation support' printf "%s\n" ' snappy snappy compression support' + printf "%s\n" ' sndio sndio sound support' printf "%s\n" ' sparse sparse checker' printf "%s\n" ' spice Spice server support' printf "%s\n" ' spice-protocol Spice protocol support' + printf "%s\n" ' stack-protector compiler-provided stack protection' printf "%s\n" ' tcg TCG support' printf "%s\n" ' tools build support utilities that come with QEMU' printf "%s\n" ' tpm TPM support' @@ -152,6 +191,11 @@ meson_options_help() { printf "%s\n" ' usb-redir libusbredir support' printf "%s\n" ' vde vde network backend support' printf "%s\n" ' vdi vdi image format support' + printf "%s\n" ' vduse-blk-export' + printf "%s\n" ' VDUSE block export support' + printf "%s\n" ' vfio-user-server' + printf "%s\n" ' vfio-user server support' + printf "%s\n" ' vhdx vhdx image format support' printf "%s\n" ' vhost-crypto vhost-user crypto backend support' printf "%s\n" ' vhost-kernel vhost kernel backend support' printf "%s\n" ' vhost-net vhost-net kernel acceleration support' @@ -161,13 +205,17 @@ meson_options_help() { printf "%s\n" ' vhost-vdpa vhost-vdpa kernel backend support' printf "%s\n" ' virglrenderer virgl rendering support' printf "%s\n" ' virtfs virtio-9p support' - printf "%s\n" ' virtiofsd build virtiofs daemon (virtiofsd)' + printf "%s\n" ' virtfs-proxy-helper' + printf "%s\n" ' virtio-9p proxy helper support' + printf "%s\n" ' vmdk vmdk image format support' printf "%s\n" ' vmnet vmnet.framework network backend support' printf "%s\n" ' vnc VNC server' printf "%s\n" ' vnc-jpeg JPEG lossy compression for VNC server' printf "%s\n" ' vnc-sasl SASL authentication for VNC server' + printf "%s\n" ' vpc vpc image format support' printf "%s\n" ' vte vte support for the gtk UI' printf "%s\n" ' vvfat vvfat image format support' + printf "%s\n" ' werror Treat warnings as errors' printf "%s\n" ' whpx WHPX acceleration support' printf "%s\n" ' xen Xen backend support' printf "%s\n" ' xen-pci-passthrough' @@ -177,6 +225,8 @@ meson_options_help() { } _meson_option_parse() { case $1 in + --enable-af-xdp) printf "%s" -Daf_xdp=enabled ;; + --disable-af-xdp) printf "%s" -Daf_xdp=disabled ;; --enable-alsa) printf "%s" -Dalsa=enabled ;; --disable-alsa) printf "%s" -Dalsa=disabled ;; --enable-attr) printf "%s" -Dattr=enabled ;; @@ -186,12 +236,17 @@ _meson_option_parse() { --disable-auth-pam) printf "%s" -Dauth_pam=disabled ;; --enable-avx2) printf "%s" -Davx2=enabled ;; --disable-avx2) printf "%s" -Davx2=disabled ;; + --enable-avx512bw) printf "%s" -Davx512bw=enabled ;; + --disable-avx512bw) printf "%s" -Davx512bw=disabled ;; --enable-avx512f) printf "%s" -Davx512f=enabled ;; --disable-avx512f) printf "%s" -Davx512f=disabled ;; --enable-gcov) printf "%s" -Db_coverage=true ;; --disable-gcov) printf "%s" -Db_coverage=false ;; --enable-lto) printf "%s" -Db_lto=true ;; --disable-lto) printf "%s" -Db_lto=false ;; + --bindir=*) quote_sh "-Dbindir=$2" ;; + --enable-blkio) printf "%s" -Dblkio=enabled ;; + --disable-blkio) printf "%s" -Dblkio=disabled ;; --block-drv-ro-whitelist=*) quote_sh "-Dblock_drv_ro_whitelist=$2" ;; --block-drv-rw-whitelist=*) quote_sh "-Dblock_drv_rw_whitelist=$2" ;; --enable-block-drv-whitelist-in-tools) printf "%s" -Dblock_drv_whitelist_in_tools=true ;; @@ -204,6 +259,8 @@ _meson_option_parse() { --disable-brlapi) printf "%s" -Dbrlapi=disabled ;; --enable-bzip2) printf "%s" -Dbzip2=enabled ;; --disable-bzip2) printf "%s" -Dbzip2=disabled ;; + --enable-canokey) printf "%s" -Dcanokey=enabled ;; + --disable-canokey) printf "%s" -Dcanokey=disabled ;; --enable-cap-ng) printf "%s" -Dcap_ng=enabled ;; --disable-cap-ng) printf "%s" -Dcap_ng=disabled ;; --enable-capstone) printf "%s" -Dcapstone=enabled ;; @@ -216,8 +273,11 @@ _meson_option_parse() { --disable-cloop) printf "%s" -Dcloop=disabled ;; --enable-cocoa) printf "%s" -Dcocoa=enabled ;; --disable-cocoa) printf "%s" -Dcocoa=disabled ;; + --enable-colo-proxy) printf "%s" -Dcolo_proxy=enabled ;; + --disable-colo-proxy) printf "%s" -Dcolo_proxy=disabled ;; --enable-coreaudio) printf "%s" -Dcoreaudio=enabled ;; --disable-coreaudio) printf "%s" -Dcoreaudio=disabled ;; + --with-coroutine=*) quote_sh "-Dcoroutine_backend=$2" ;; --enable-coroutine-pool) printf "%s" -Dcoroutine_pool=true ;; --disable-coroutine-pool) printf "%s" -Dcoroutine_pool=false ;; --enable-crypto-afalg) printf "%s" -Dcrypto_afalg=enabled ;; @@ -229,10 +289,16 @@ _meson_option_parse() { --datadir=*) quote_sh "-Ddatadir=$2" ;; --enable-dbus-display) printf "%s" -Ddbus_display=enabled ;; --disable-dbus-display) printf "%s" -Ddbus_display=disabled ;; + --enable-debug-info) printf "%s" -Ddebug=true ;; + --disable-debug-info) printf "%s" -Ddebug=false ;; + --enable-debug-graph-lock) printf "%s" -Ddebug_graph_lock=true ;; + --disable-debug-graph-lock) printf "%s" -Ddebug_graph_lock=false ;; --enable-debug-mutex) printf "%s" -Ddebug_mutex=true ;; --disable-debug-mutex) printf "%s" -Ddebug_mutex=false ;; --enable-debug-stack-usage) printf "%s" -Ddebug_stack_usage=true ;; --disable-debug-stack-usage) printf "%s" -Ddebug_stack_usage=false ;; + --enable-debug-tcg) printf "%s" -Ddebug_tcg=true ;; + --disable-debug-tcg) printf "%s" -Ddebug_tcg=false ;; --enable-dmg) printf "%s" -Ddmg=enabled ;; --disable-dmg) printf "%s" -Ddmg=disabled ;; --docdir=*) quote_sh "-Ddocdir=$2" ;; @@ -259,16 +325,18 @@ _meson_option_parse() { --disable-glusterfs) printf "%s" -Dglusterfs=disabled ;; --enable-gnutls) printf "%s" -Dgnutls=enabled ;; --disable-gnutls) printf "%s" -Dgnutls=disabled ;; - --enable-gprof) printf "%s" -Dgprof=true ;; - --disable-gprof) printf "%s" -Dgprof=false ;; --enable-gtk) printf "%s" -Dgtk=enabled ;; --disable-gtk) printf "%s" -Dgtk=disabled ;; + --enable-gtk-clipboard) printf "%s" -Dgtk_clipboard=enabled ;; + --disable-gtk-clipboard) printf "%s" -Dgtk_clipboard=disabled ;; --enable-guest-agent) printf "%s" -Dguest_agent=enabled ;; --disable-guest-agent) printf "%s" -Dguest_agent=disabled ;; --enable-guest-agent-msi) printf "%s" -Dguest_agent_msi=enabled ;; --disable-guest-agent-msi) printf "%s" -Dguest_agent_msi=disabled ;; - --enable-hax) printf "%s" -Dhax=enabled ;; - --disable-hax) printf "%s" -Dhax=disabled ;; + --enable-hexagon-idef-parser) printf "%s" -Dhexagon_idef_parser=true ;; + --disable-hexagon-idef-parser) printf "%s" -Dhexagon_idef_parser=false ;; + --enable-hv-balloon) printf "%s" -Dhv_balloon=enabled ;; + --disable-hv-balloon) printf "%s" -Dhv_balloon=disabled ;; --enable-hvf) printf "%s" -Dhvf=enabled ;; --disable-hvf) printf "%s" -Dhvf=disabled ;; --iasl=*) quote_sh "-Diasl=$2" ;; @@ -289,9 +357,13 @@ _meson_option_parse() { --enable-libdaxctl) printf "%s" -Dlibdaxctl=enabled ;; --disable-libdaxctl) printf "%s" -Dlibdaxctl=disabled ;; --libdir=*) quote_sh "-Dlibdir=$2" ;; + --enable-libdw) printf "%s" -Dlibdw=enabled ;; + --disable-libdw) printf "%s" -Dlibdw=disabled ;; --libexecdir=*) quote_sh "-Dlibexecdir=$2" ;; --enable-libiscsi) printf "%s" -Dlibiscsi=enabled ;; --disable-libiscsi) printf "%s" -Dlibiscsi=disabled ;; + --enable-libkeyutils) printf "%s" -Dlibkeyutils=enabled ;; + --disable-libkeyutils) printf "%s" -Dlibkeyutils=disabled ;; --enable-libnfs) printf "%s" -Dlibnfs=enabled ;; --disable-libnfs) printf "%s" -Dlibnfs=disabled ;; --enable-libpmem) printf "%s" -Dlibpmem=enabled ;; @@ -302,6 +374,8 @@ _meson_option_parse() { --disable-libudev) printf "%s" -Dlibudev=disabled ;; --enable-libusb) printf "%s" -Dlibusb=enabled ;; --disable-libusb) printf "%s" -Dlibusb=disabled ;; + --enable-libvduse) printf "%s" -Dlibvduse=enabled ;; + --disable-libvduse) printf "%s" -Dlibvduse=disabled ;; --enable-linux-aio) printf "%s" -Dlinux_aio=enabled ;; --disable-linux-aio) printf "%s" -Dlinux_aio=disabled ;; --enable-linux-io-uring) printf "%s" -Dlinux_io_uring=enabled ;; @@ -322,6 +396,8 @@ _meson_option_parse() { --disable-membarrier) printf "%s" -Dmembarrier=disabled ;; --enable-module-upgrades) printf "%s" -Dmodule_upgrades=true ;; --disable-module-upgrades) printf "%s" -Dmodule_upgrades=false ;; + --enable-modules) printf "%s" -Dmodules=enabled ;; + --disable-modules) printf "%s" -Dmodules=disabled ;; --enable-mpath) printf "%s" -Dmpath=enabled ;; --disable-mpath) printf "%s" -Dmpath=disabled ;; --enable-multiprocess) printf "%s" -Dmultiprocess=enabled ;; @@ -342,18 +418,27 @@ _meson_option_parse() { --disable-pa) printf "%s" -Dpa=disabled ;; --enable-parallels) printf "%s" -Dparallels=enabled ;; --disable-parallels) printf "%s" -Dparallels=disabled ;; + --enable-pipewire) printf "%s" -Dpipewire=enabled ;; + --disable-pipewire) printf "%s" -Dpipewire=disabled ;; + --enable-pixman) printf "%s" -Dpixman=enabled ;; + --disable-pixman) printf "%s" -Dpixman=disabled ;; --with-pkgversion=*) quote_sh "-Dpkgversion=$2" ;; + --enable-plugins) printf "%s" -Dplugins=true ;; + --disable-plugins) printf "%s" -Dplugins=false ;; --enable-png) printf "%s" -Dpng=enabled ;; --disable-png) printf "%s" -Dpng=disabled ;; - --enable-profiler) printf "%s" -Dprofiler=true ;; - --disable-profiler) printf "%s" -Dprofiler=false ;; + --prefix=*) quote_sh "-Dprefix=$2" ;; --enable-pvrdma) printf "%s" -Dpvrdma=enabled ;; --disable-pvrdma) printf "%s" -Dpvrdma=disabled ;; --enable-qcow1) printf "%s" -Dqcow1=enabled ;; --disable-qcow1) printf "%s" -Dqcow1=disabled ;; --enable-qed) printf "%s" -Dqed=enabled ;; --disable-qed) printf "%s" -Dqed=disabled ;; - --firmwarepath=*) quote_sh "-Dqemu_firmwarepath=$2" ;; + --firmwarepath=*) quote_sh "-Dqemu_firmwarepath=$(meson_option_build_array $2)" ;; + --qemu-ga-distro=*) quote_sh "-Dqemu_ga_distro=$2" ;; + --qemu-ga-manufacturer=*) quote_sh "-Dqemu_ga_manufacturer=$2" ;; + --qemu-ga-version=*) quote_sh "-Dqemu_ga_version=$2" ;; + --with-suffix=*) quote_sh "-Dqemu_suffix=$2" ;; --enable-qga-vss) printf "%s" -Dqga_vss=enabled ;; --disable-qga-vss) printf "%s" -Dqga_vss=disabled ;; --enable-qom-cast-debug) printf "%s" -Dqom_cast_debug=true ;; @@ -362,10 +447,18 @@ _meson_option_parse() { --disable-rbd) printf "%s" -Drbd=disabled ;; --enable-rdma) printf "%s" -Drdma=enabled ;; --disable-rdma) printf "%s" -Drdma=disabled ;; + --enable-relocatable) printf "%s" -Drelocatable=true ;; + --disable-relocatable) printf "%s" -Drelocatable=false ;; --enable-replication) printf "%s" -Dreplication=enabled ;; --disable-replication) printf "%s" -Dreplication=disabled ;; --enable-rng-none) printf "%s" -Drng_none=true ;; --disable-rng-none) printf "%s" -Drng_none=false ;; + --enable-rutabaga-gfx) printf "%s" -Drutabaga_gfx=enabled ;; + --disable-rutabaga-gfx) printf "%s" -Drutabaga_gfx=disabled ;; + --enable-safe-stack) printf "%s" -Dsafe_stack=true ;; + --disable-safe-stack) printf "%s" -Dsafe_stack=false ;; + --enable-sanitizers) printf "%s" -Dsanitizers=true ;; + --disable-sanitizers) printf "%s" -Dsanitizers=false ;; --enable-sdl) printf "%s" -Dsdl=enabled ;; --disable-sdl) printf "%s" -Dsdl=disabled ;; --enable-sdl-image) printf "%s" -Dsdl_image=enabled ;; @@ -376,20 +469,23 @@ _meson_option_parse() { --disable-selinux) printf "%s" -Dselinux=disabled ;; --enable-slirp) printf "%s" -Dslirp=enabled ;; --disable-slirp) printf "%s" -Dslirp=disabled ;; - --enable-slirp=*) quote_sh "-Dslirp=$2" ;; --enable-slirp-smbd) printf "%s" -Dslirp_smbd=enabled ;; --disable-slirp-smbd) printf "%s" -Dslirp_smbd=disabled ;; --enable-smartcard) printf "%s" -Dsmartcard=enabled ;; --disable-smartcard) printf "%s" -Dsmartcard=disabled ;; + --smbd=*) quote_sh "-Dsmbd=$2" ;; --enable-snappy) printf "%s" -Dsnappy=enabled ;; --disable-snappy) printf "%s" -Dsnappy=disabled ;; + --enable-sndio) printf "%s" -Dsndio=enabled ;; + --disable-sndio) printf "%s" -Dsndio=disabled ;; --enable-sparse) printf "%s" -Dsparse=enabled ;; --disable-sparse) printf "%s" -Dsparse=disabled ;; - --sphinx-build=*) quote_sh "-Dsphinx_build=$2" ;; --enable-spice) printf "%s" -Dspice=enabled ;; --disable-spice) printf "%s" -Dspice=disabled ;; --enable-spice-protocol) printf "%s" -Dspice_protocol=enabled ;; --disable-spice-protocol) printf "%s" -Dspice_protocol=disabled ;; + --enable-stack-protector) printf "%s" -Dstack_protector=enabled ;; + --disable-stack-protector) printf "%s" -Dstack_protector=disabled ;; --enable-strip) printf "%s" -Dstrip=true ;; --disable-strip) printf "%s" -Dstrip=false ;; --sysconfdir=*) quote_sh "-Dsysconfdir=$2" ;; @@ -404,6 +500,8 @@ _meson_option_parse() { --disable-tpm) printf "%s" -Dtpm=disabled ;; --enable-trace-backends=*) quote_sh "-Dtrace_backends=$2" ;; --with-trace-file=*) quote_sh "-Dtrace_file=$2" ;; + --enable-tsan) printf "%s" -Dtsan=true ;; + --disable-tsan) printf "%s" -Dtsan=false ;; --enable-u2f) printf "%s" -Du2f=enabled ;; --disable-u2f) printf "%s" -Du2f=disabled ;; --enable-usb-redir) printf "%s" -Dusb_redir=enabled ;; @@ -412,6 +510,12 @@ _meson_option_parse() { --disable-vde) printf "%s" -Dvde=disabled ;; --enable-vdi) printf "%s" -Dvdi=enabled ;; --disable-vdi) printf "%s" -Dvdi=disabled ;; + --enable-vduse-blk-export) printf "%s" -Dvduse_blk_export=enabled ;; + --disable-vduse-blk-export) printf "%s" -Dvduse_blk_export=disabled ;; + --enable-vfio-user-server) printf "%s" -Dvfio_user_server=enabled ;; + --disable-vfio-user-server) printf "%s" -Dvfio_user_server=disabled ;; + --enable-vhdx) printf "%s" -Dvhdx=enabled ;; + --disable-vhdx) printf "%s" -Dvhdx=disabled ;; --enable-vhost-crypto) printf "%s" -Dvhost_crypto=enabled ;; --disable-vhost-crypto) printf "%s" -Dvhost_crypto=disabled ;; --enable-vhost-kernel) printf "%s" -Dvhost_kernel=enabled ;; @@ -428,8 +532,10 @@ _meson_option_parse() { --disable-virglrenderer) printf "%s" -Dvirglrenderer=disabled ;; --enable-virtfs) printf "%s" -Dvirtfs=enabled ;; --disable-virtfs) printf "%s" -Dvirtfs=disabled ;; - --enable-virtiofsd) printf "%s" -Dvirtiofsd=enabled ;; - --disable-virtiofsd) printf "%s" -Dvirtiofsd=disabled ;; + --enable-virtfs-proxy-helper) printf "%s" -Dvirtfs_proxy_helper=enabled ;; + --disable-virtfs-proxy-helper) printf "%s" -Dvirtfs_proxy_helper=disabled ;; + --enable-vmdk) printf "%s" -Dvmdk=enabled ;; + --disable-vmdk) printf "%s" -Dvmdk=disabled ;; --enable-vmnet) printf "%s" -Dvmnet=enabled ;; --disable-vmnet) printf "%s" -Dvmnet=disabled ;; --enable-vnc) printf "%s" -Dvnc=enabled ;; @@ -438,10 +544,14 @@ _meson_option_parse() { --disable-vnc-jpeg) printf "%s" -Dvnc_jpeg=disabled ;; --enable-vnc-sasl) printf "%s" -Dvnc_sasl=enabled ;; --disable-vnc-sasl) printf "%s" -Dvnc_sasl=disabled ;; + --enable-vpc) printf "%s" -Dvpc=enabled ;; + --disable-vpc) printf "%s" -Dvpc=disabled ;; --enable-vte) printf "%s" -Dvte=enabled ;; --disable-vte) printf "%s" -Dvte=disabled ;; --enable-vvfat) printf "%s" -Dvvfat=enabled ;; --disable-vvfat) printf "%s" -Dvvfat=disabled ;; + --enable-werror) printf "%s" -Dwerror=true ;; + --disable-werror) printf "%s" -Dwerror=false ;; --enable-whpx) printf "%s" -Dwhpx=enabled ;; --disable-whpx) printf "%s" -Dwhpx=disabled ;; --enable-xen) printf "%s" -Dxen=enabled ;; diff --git a/scripts/meson.build b/scripts/meson.build index 1c89e10a76..532277f5a2 100644 --- a/scripts/meson.build +++ b/scripts/meson.build @@ -1,3 +1,5 @@ if stap.found() install_data('qemu-trace-stap', install_dir: get_option('bindir')) endif + +test('xml-preprocess', files('xml-preprocess-test.py'), suite: ['unit']) diff --git a/scripts/modinfo-collect.py b/scripts/modinfo-collect.py old mode 100755 new mode 100644 diff --git a/scripts/modinfo-generate.py b/scripts/modinfo-generate.py old mode 100755 new mode 100644 diff --git a/scripts/mtest2make.py b/scripts/mtest2make.py index 0fe81efbbc..eb01a05ddb 100644 --- a/scripts/mtest2make.py +++ b/scripts/mtest2make.py @@ -27,7 +27,8 @@ SPEED = quick .speed.slow = $(foreach s,$(sort $(filter-out %-thorough, $1)), --suite $s) .speed.thorough = $(foreach s,$(sort $1), --suite $s) -.mtestargs = --no-rebuild -t 0 +TIMEOUT_MULTIPLIER = 1 +.mtestargs = --no-rebuild -t $(TIMEOUT_MULTIPLIER) ifneq ($(SPEED), quick) .mtestargs += --setup $(SPEED) endif @@ -51,10 +52,11 @@ def process_tests(test, targets, suites): test_suites = test['suite'] or ['default'] for s in test_suites: - # The suite name in the introspection info is "PROJECT:SUITE" - s = s.split(':')[1] - if s == 'slow' or s == 'thorough': - continue + # The suite name in the introspection info is "PROJECT" or "PROJECT:SUITE" + if ':' in s: + s = s.split(':')[1] + if s == 'slow' or s == 'thorough': + continue if s.endswith('-slow'): s = s[:-5] suites[s].speeds.append('slow') diff --git a/scripts/nsis.py b/scripts/nsis.py index 462d6cac3b..03ed7608a2 100644 --- a/scripts/nsis.py +++ b/scripts/nsis.py @@ -18,26 +18,52 @@ def signcode(path): return subprocess.run([cmd, path]) +def find_deps(exe_or_dll, search_path, analyzed_deps): + deps = [exe_or_dll] + output = subprocess.check_output(["objdump", "-p", exe_or_dll], text=True) + output = output.split("\n") + for line in output: + if not line.startswith("\tDLL Name: "): + continue + + dep = line.split("DLL Name: ")[1].strip() + if dep in analyzed_deps: + continue + + dll = os.path.join(search_path, dep) + if not os.path.exists(dll): + # assume it's a Windows provided dll, skip it + continue + + analyzed_deps.add(dep) + # locate the dll dependencies recursively + rdeps = find_deps(dll, search_path, analyzed_deps) + deps.extend(rdeps) + + return deps def main(): parser = argparse.ArgumentParser(description="QEMU NSIS build helper.") parser.add_argument("outfile") parser.add_argument("prefix") parser.add_argument("srcdir") + parser.add_argument("dlldir") parser.add_argument("cpu") parser.add_argument("nsisargs", nargs="*") args = parser.parse_args() + # canonicalize the Windows native prefix path + prefix = os.path.splitdrive(args.prefix)[1] destdir = tempfile.mkdtemp() try: - subprocess.run(["make", "install", "DESTDIR=" + destdir + os.path.sep]) + subprocess.run(["make", "install", "DESTDIR=" + destdir]) with open( - os.path.join(destdir + args.prefix, "system-emulations.nsh"), "w" + os.path.join(destdir + prefix, "system-emulations.nsh"), "w" ) as nsh, open( - os.path.join(destdir + args.prefix, "system-mui-text.nsh"), "w" + os.path.join(destdir + prefix, "system-mui-text.nsh"), "w" ) as muinsh: for exe in sorted(glob.glob( - os.path.join(destdir + args.prefix, "qemu-system-*.exe") + os.path.join(destdir + prefix, "qemu-system-*.exe") )): exe = os.path.basename(exe) arch = exe[12:-4] @@ -61,22 +87,36 @@ def main(): !insertmacro MUI_DESCRIPTION_TEXT ${{Section_{0}}} "{1}" """.format(arch, desc)) - for exe in glob.glob(os.path.join(destdir + args.prefix, "*.exe")): + search_path = args.dlldir + print("Searching '%s' for the dependent dlls ..." % search_path) + dlldir = os.path.join(destdir + prefix, "dll") + os.mkdir(dlldir) + + for exe in glob.glob(os.path.join(destdir + prefix, "*.exe")): signcode(exe) + # find all dll dependencies + deps = set(find_deps(exe, search_path, set())) + deps.remove(exe) + + # copy all dlls to the DLLDIR + for dep in deps: + dllfile = os.path.join(dlldir, os.path.basename(dep)) + if (os.path.exists(dllfile)): + continue + print("Copying '%s' to '%s'" % (dep, dllfile)) + shutil.copy(dep, dllfile) + makensis = [ "makensis", "-V2", "-NOCD", "-DSRCDIR=" + args.srcdir, - "-DBINDIR=" + destdir + args.prefix, + "-DBINDIR=" + destdir + prefix, ] - dlldir = "w32" if args.cpu == "x86_64": - dlldir = "w64" makensis += ["-DW64"] - if os.path.exists(os.path.join(args.srcdir, "dll")): - makensis += ["-DDLLDIR={0}/dll/{1}".format(args.srcdir, dlldir)] + makensis += ["-DDLLDIR=" + dlldir] makensis += ["-DOUTFILE=" + args.outfile] + args.nsisargs subprocess.run(makensis) diff --git a/scripts/oss-fuzz/build.sh b/scripts/oss-fuzz/build.sh index 98b56e0521..5238f83343 100755 --- a/scripts/oss-fuzz/build.sh +++ b/scripts/oss-fuzz/build.sh @@ -1,4 +1,4 @@ -#!/bin/sh -e +#!/bin/bash -e # # OSS-Fuzz build script. See: # https://google.github.io/oss-fuzz/getting-started/new-project-guide/#buildsh @@ -43,10 +43,10 @@ EXTRA_CFLAGS="$CFLAGS -U __OPTIMIZE__" if ! { [ -e "./COPYING" ] && [ -e "./MAINTAINERS" ] && [ -e "./Makefile" ] && - [ -e "./docs" ] && + [ -d "./docs" ] && [ -e "./VERSION" ] && - [ -e "./linux-user" ] && - [ -e "./softmmu" ];} ; then + [ -d "./linux-user" ] && + [ -d "./system" ];} ; then fatal "Please run the script from the top of the QEMU tree" fi @@ -64,7 +64,7 @@ mkdir -p "$DEST_DIR/lib/" # Copy the shared libraries here # Build once to get the list of dynamic lib paths, and copy them over ../configure --disable-werror --cc="$CC" --cxx="$CXX" --enable-fuzzing \ - --prefix="$DEST_DIR" --bindir="$DEST_DIR" --datadir="$DEST_DIR/data/" \ + --prefix="/opt/qemu-oss-fuzz" \ --extra-cflags="$EXTRA_CFLAGS" --target-list="i386-softmmu" if ! make "-j$(nproc)" qemu-fuzz-i386; then @@ -81,16 +81,18 @@ if [ "$GITLAB_CI" != "true" ]; then # Build a second time to build the final binary with correct rpath ../configure --disable-werror --cc="$CC" --cxx="$CXX" --enable-fuzzing \ - --prefix="$DEST_DIR" --bindir="$DEST_DIR" --datadir="$DEST_DIR/data/" \ + --prefix="/opt/qemu-oss-fuzz" \ --extra-cflags="$EXTRA_CFLAGS" --extra-ldflags="-Wl,-rpath,\$ORIGIN/lib" \ --target-list="i386-softmmu" make "-j$(nproc)" qemu-fuzz-i386 V=1 fi -# Copy over the datadir -cp -r ../pc-bios/ "$DEST_DIR/pc-bios" +# Place data files in the preinstall tree +make install DESTDIR=$DEST_DIR/qemu-bundle +rm -rf $DEST_DIR/qemu-bundle/opt/qemu-oss-fuzz/bin +rm -rf $DEST_DIR/qemu-bundle/opt/qemu-oss-fuzz/libexec -targets=$(./qemu-fuzz-i386 | awk '$1 ~ /\*/ {print $2}') +targets=$(./qemu-fuzz-i386 | grep generic-fuzz | awk '$1 ~ /\*/ {print $2}') base_copy="$DEST_DIR/qemu-fuzz-i386-target-$(echo "$targets" | head -n 1)" cp "./qemu-fuzz-i386" "$base_copy" @@ -105,7 +107,7 @@ do # to be configured. We have some generic-fuzz-{pc-q35, floppy, ...} targets # that are thin wrappers around this target that set the required # environment variables according to predefined configs. - if [ "$target" != "generic-fuzz" ]; then + if [[ $target == "generic-fuzz-"* ]]; then ln $base_copy \ "$DEST_DIR/qemu-fuzz-i386-target-$target" fi diff --git a/scripts/oss-fuzz/lsan_suppressions.txt b/scripts/oss-fuzz/lsan_suppressions.txt new file mode 100644 index 0000000000..7d90c280d0 --- /dev/null +++ b/scripts/oss-fuzz/lsan_suppressions.txt @@ -0,0 +1,5 @@ +# The tcmalloc on Fedora37 confuses things +leak:/lib64/libtcmalloc_minimal.so.4 + +# libxkbcommon also leaks in qemu-keymap +leak:/lib64/libxkbcommon.so.0 diff --git a/scripts/oss-fuzz/minimize_qtest_trace.py b/scripts/oss-fuzz/minimize_qtest_trace.py index 20825768c2..d1f3990c16 100755 --- a/scripts/oss-fuzz/minimize_qtest_trace.py +++ b/scripts/oss-fuzz/minimize_qtest_trace.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ -This takes a crashing qtest trace and tries to remove superflous operations +This takes a crashing qtest trace and tries to remove superfluous operations """ import sys @@ -38,7 +38,7 @@ crash by setting CRASH_TOKEN= Options: -M1: enable a loop around the remove minimizer, which may help decrease some - timing dependant instructions. Off by default. + timing dependent instructions. Off by default. -M2: try setting bits in operand of write/out to zero. Off by default. """.format((sys.argv[0]))) @@ -177,7 +177,7 @@ def remove_lines(newtrace, outpath): # it into two separate write commands. If splitting the data operand # from length/2^n bytes to the left does not work, try to move the pivot # to the right side, then add one to n, until length/2^n == 0. The idea - # is to prune unneccessary bytes from long writes, while accommodating + # is to prune unnecessary bytes from long writes, while accommodating # arbitrary MemoryRegion access sizes and alignments. # This algorithm will fail under some rare situations. @@ -292,7 +292,7 @@ def minimize_trace(inpath, outpath): old_len = len(newtrace) + 1 while(old_len > len(newtrace)): old_len = len(newtrace) - print("trace lenth = ", old_len) + print("trace length = ", old_len) remove_lines(newtrace, outpath) if not M1 and not M2: break diff --git a/scripts/performance/topN_callgrind.py b/scripts/performance/topN_callgrind.py index 67c59197af..f3f05fce55 100755 --- a/scripts/performance/topN_callgrind.py +++ b/scripts/performance/topN_callgrind.py @@ -4,7 +4,7 @@ # Syntax: # topN_callgrind.py [-h] [-n] -- \ # [] \ -# [] +# [] # # [-h] - Print the script arguments help message. # [-n] - Specify the number of top functions to print. diff --git a/scripts/performance/topN_perf.py b/scripts/performance/topN_perf.py index 07be195fc8..7b19e6a742 100755 --- a/scripts/performance/topN_perf.py +++ b/scripts/performance/topN_perf.py @@ -4,7 +4,7 @@ # Syntax: # topN_perf.py [-h] [-n] -- \ # [] \ -# [] +# [] # # [-h] - Print the script arguments help message. # [-n] - Specify the number of top functions to print. diff --git a/scripts/probe-gdb-support.py b/scripts/probe-gdb-support.py new file mode 100644 index 0000000000..5755255966 --- /dev/null +++ b/scripts/probe-gdb-support.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# coding: utf-8 +# +# Probe gdb for supported architectures. +# +# This is required to support testing of the gdbstub as its hard to +# handle errors gracefully during the test. Instead this script when +# passed a GDB binary will probe its architecture support and return a +# string of supported arches, stripped of guff. +# +# Copyright 2023 Linaro Ltd +# +# 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 re +from subprocess import check_output, STDOUT + +# mappings from gdb arch to QEMU target +mappings = { + "alpha" : "alpha", + "aarch64" : ["aarch64", "aarch64_be"], + "armv7": "arm", + "armv8-a" : ["aarch64", "aarch64_be"], + "avr" : "avr", + "cris" : "cris", + # no hexagon in upstream gdb + "hppa1.0" : "hppa", + "i386" : "i386", + "i386:x86-64" : "x86_64", + "Loongarch64" : "loongarch64", + "m68k" : "m68k", + "MicroBlaze" : "microblaze", + "mips:isa64" : ["mips64", "mips64el"], + "nios2" : "nios2", + "or1k" : "or1k", + "powerpc:common" : "ppc", + "powerpc:common64" : ["ppc64", "ppc64le"], + "riscv:rv32" : "riscv32", + "riscv:rv64" : "riscv64", + "s390:64-bit" : "s390x", + "sh4" : ["sh4", "sh4eb"], + "sparc": "sparc", + "sparc:v8plus": "sparc32plus", + "sparc:v9a" : "sparc64", + # no tricore in upstream gdb + "xtensa" : ["xtensa", "xtensaeb"] +} + +def do_probe(gdb): + gdb_out = check_output([gdb, + "-ex", "set architecture", + "-ex", "quit"], stderr=STDOUT) + + m = re.search(r"Valid arguments are (.*)", + gdb_out.decode("utf-8")) + + valid_arches = set() + + if m.group(1): + for arch in m.group(1).split(", "): + if arch in mappings: + mapping = mappings[arch] + if isinstance(mapping, str): + valid_arches.add(mapping) + else: + for entry in mapping: + valid_arches.add(entry) + + return valid_arches + +def main() -> None: + parser = argparse.ArgumentParser(description='Probe GDB Architectures') + parser.add_argument('gdb', help='Path to GDB binary.') + + args = parser.parse_args() + + supported = do_probe(args.gdb) + + print(" ".join(supported)) + +if __name__ == '__main__': + main() diff --git a/scripts/python_qmp_updater.py b/scripts/python_qmp_updater.py new file mode 100755 index 0000000000..494a169812 --- /dev/null +++ b/scripts/python_qmp_updater.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +# +# Intended usage: +# +# git grep -l '\.qmp(' | xargs ./scripts/python_qmp_updater.py +# + +import re +import sys +from typing import Optional + +start_reg = re.compile(r'^(?P *)(?P\w+) = (?P.*).qmp\(', + flags=re.MULTILINE) + +success_reg_templ = re.sub('\n *', '', r""" + (\n*{padding}(?P\#.*$))? + \n*{padding} + ( + self.assert_qmp\({res},\ 'return',\ {{}}\) + | + assert\ {res}\['return'\]\ ==\ {{}} + | + assert\ {res}\ ==\ {{'return':\ {{}}}} + | + self.assertEqual\({res}\['return'\],\ {{}}\) + )""") + +some_check_templ = re.sub('\n *', '', r""" + (\n*{padding}(?P\#.*$))? + \s*self.assert_qmp\({res},""") + + +def tmatch(template: str, text: str, + padding: str, res: str) -> Optional[re.Match[str]]: + return re.match(template.format(padding=padding, res=res), text, + flags=re.MULTILINE) + + +def find_closing_brace(text: str, start: int) -> int: + """ + Having '(' at text[start] search for pairing ')' and return its index. + """ + assert text[start] == '(' + + height = 1 + + for i in range(start + 1, len(text)): + if text[i] == '(': + height += 1 + elif text[i] == ')': + height -= 1 + if height == 0: + return i + + raise ValueError + + +def update(text: str) -> str: + result = '' + + while True: + m = start_reg.search(text) + if m is None: + result += text + break + + result += text[:m.start()] + + args_ind = m.end() + args_end = find_closing_brace(text, args_ind - 1) + + all_args = text[args_ind:args_end].split(',', 1) + + name = all_args[0] + args = None if len(all_args) == 1 else all_args[1] + + unchanged_call = text[m.start():args_end+1] + text = text[args_end+1:] + + padding, res, vm = m.group('padding', 'res', 'vm') + + m = tmatch(success_reg_templ, text, padding, res) + + if m is None: + result += unchanged_call + + if ('query-' not in name and + 'x-debug-block-dirty-bitmap-sha256' not in name and + not tmatch(some_check_templ, text, padding, res)): + print(unchanged_call + text[:200] + '...\n\n') + + continue + + if m.group('comment'): + result += f'{padding}{m.group("comment")}\n' + + result += f'{padding}{vm}.cmd({name}' + + if args: + result += ',' + + if '\n' in args: + m_args = re.search('(?P *).*$', args) + assert m_args is not None + + cur_padding = len(m_args.group('pad')) + expected = len(f'{padding}{res} = {vm}.qmp(') + drop = len(f'{res} = ') + if cur_padding == expected - 1: + # tolerate this bad style + drop -= 1 + elif cur_padding < expected - 1: + # assume nothing to do + drop = 0 + + if drop: + args = re.sub('\n' + ' ' * drop, '\n', args) + + result += args + + result += ')' + + text = text[m.end():] + + return result + + +for fname in sys.argv[1:]: + print(fname) + with open(fname) as f: + t = f.read() + + t = update(t) + + with open(fname, 'w') as f: + f.write(t) diff --git a/scripts/qapi/.flake8 b/scripts/qapi/.flake8 index 6b158c68b8..a873ff6730 100644 --- a/scripts/qapi/.flake8 +++ b/scripts/qapi/.flake8 @@ -1,2 +1,3 @@ [flake8] -extend-ignore = E722 # Prefer pylint's bare-except checks to flake8's +# Prefer pylint's bare-except checks to flake8's +extend-ignore = E722 diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 38ca38a7b9..d1fdf4182c 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -41,11 +41,13 @@ from .source import QAPISourceInfo def gen_command_decl(name: str, arg_type: Optional[QAPISchemaObjectType], boxed: bool, - ret_type: Optional[QAPISchemaType]) -> str: + ret_type: Optional[QAPISchemaType], + coroutine: bool) -> str: return mcgen(''' -%(c_type)s qmp_%(c_name)s(%(params)s); +%(c_type)s %(coroutine_fn)sqmp_%(c_name)s(%(params)s); ''', c_type=(ret_type and ret_type.c_type()) or 'void', + coroutine_fn='coroutine_fn ' if coroutine else '', c_name=c_name(name), params=build_params(arg_type, boxed, 'Error **errp')) @@ -64,7 +66,8 @@ def gen_call(name: str, elif arg_type: assert not arg_type.variants for memb in arg_type.members: - if memb.optional: + assert not memb.ifcond.is_present() + if memb.need_has(): argstr += 'arg.has_%s, ' % c_name(memb.name) argstr += 'arg.%s, ' % c_name(memb.name) @@ -83,7 +86,7 @@ def gen_call(name: str, trace_qmp_enter_%(name)s(req_json->str); } - ''', +''', upper=upper, name=name) ret += mcgen(''' @@ -124,13 +127,13 @@ def gen_call(name: str, trace_qmp_exit_%(name)s(ret_json->str, true); } - ''', +''', upper=upper, name=name) else: ret += mcgen(''' trace_qmp_exit_%(name)s("{}", true); - ''', +''', name=name) return ret @@ -157,16 +160,21 @@ static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, c_type=ret_type.c_type(), c_name=ret_type.c_name()) -def build_marshal_proto(name: str) -> str: - return ('void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' - % c_name(name)) +def build_marshal_proto(name: str, + coroutine: bool) -> str: + return ('void %(coroutine_fn)sqmp_marshal_%(c_name)s(%(params)s)' % { + 'coroutine_fn': 'coroutine_fn ' if coroutine else '', + 'c_name': c_name(name), + 'params': 'QDict *args, QObject **ret, Error **errp', + }) -def gen_marshal_decl(name: str) -> str: +def gen_marshal_decl(name: str, + coroutine: bool) -> str: return mcgen(''' %(proto)s; ''', - proto=build_marshal_proto(name)) + proto=build_marshal_proto(name, coroutine)) def gen_trace(name: str) -> str: @@ -181,7 +189,8 @@ def gen_marshal(name: str, arg_type: Optional[QAPISchemaObjectType], boxed: bool, ret_type: Optional[QAPISchemaType], - gen_tracing: bool) -> str: + gen_tracing: bool, + coroutine: bool) -> str: have_args = boxed or (arg_type and not arg_type.is_empty()) if have_args: assert arg_type is not None @@ -195,7 +204,7 @@ def gen_marshal(name: str, bool ok = false; Visitor *v; ''', - proto=build_marshal_proto(name)) + proto=build_marshal_proto(name, coroutine)) if ret_type: ret += mcgen(''' @@ -316,7 +325,6 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): #include "qapi/error.h" #include "%(visit)s.h" #include "%(commands)s.h" - ''', commands=commands, visit=visit)) @@ -388,10 +396,11 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) self._genh, self._genc): self._genc.add(gen_marshal_output(ret_type)) with ifcontext(ifcond, self._genh, self._genc): - self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) - self._genh.add(gen_marshal_decl(name)) + self._genh.add(gen_command_decl(name, arg_type, boxed, + ret_type, coroutine)) + self._genh.add(gen_marshal_decl(name, coroutine)) self._genc.add(gen_marshal(name, arg_type, boxed, ret_type, - self._gen_tracing)) + self._gen_tracing, coroutine)) if self._gen_tracing: self._gen_trace_events.add(gen_trace(name)) with self._temp_module('./init'): diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 489273574a..737b059e62 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -114,7 +114,7 @@ def c_name(name: str, protect: bool = True) -> str: 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) # namespace pollution: - polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386']) + polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386', 'linux']) name = re.sub(r'[^A-Za-z0-9_]', '_', name) if protect and (name in (c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words) diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 27b44c49f5..3cf01e96b6 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -60,7 +60,7 @@ def gen_param_var(typ: QAPISchemaObjectType) -> str: for memb in typ.members: ret += sep sep = ', ' - if memb.optional: + if memb.need_has(): ret += 'has_' + c_name(memb.name) + sep if memb.type.name == 'str': # Cast away const added in build_params() @@ -196,7 +196,6 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp-event.h" - ''', events=events, visit=visit, prefix=self._prefix)) diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index 5a1782b57e..cae0a08359 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -33,7 +33,6 @@ structures and contextual semantic validation. import re from typing import ( - Collection, Dict, Iterable, List, @@ -44,18 +43,10 @@ from typing import ( from .common import c_name from .error import QAPISemError -from .parser import QAPIDoc +from .parser import QAPIExpression from .source import QAPISourceInfo -# Deserialized JSON objects as returned by the parser. -# The values of this mapping are not necessary to exhaustively type -# here (and also not practical as long as mypy lacks recursive -# types), because the purpose of this module is to interrogate that -# type. -_JSONObject = Dict[str, object] - - # See check_name_str(), below. valid_name = re.compile(r'(__[a-z0-9.-]+_)?' r'(x-)?' @@ -192,11 +183,11 @@ def check_defn_name_str(name: str, info: QAPISourceInfo, meta: str) -> None: info, "%s name should not end in 'List'" % meta) -def check_keys(value: _JSONObject, +def check_keys(value: Dict[str, object], info: QAPISourceInfo, source: str, - required: Collection[str], - optional: Collection[str]) -> None: + required: List[str], + optional: List[str]) -> None: """ Ensure that a dict has a specific set of keys. @@ -229,12 +220,11 @@ def check_keys(value: _JSONObject, pprint(unknown), pprint(allowed))) -def check_flags(expr: _JSONObject, info: QAPISourceInfo) -> None: +def check_flags(expr: QAPIExpression) -> None: """ Ensure flag members (if present) have valid values. :param expr: The expression to validate. - :param info: QAPI schema source file information. :raise QAPISemError: When certain flags have an invalid value, or when @@ -243,21 +233,22 @@ def check_flags(expr: _JSONObject, info: QAPISourceInfo) -> None: for key in ('gen', 'success-response'): if key in expr and expr[key] is not False: raise QAPISemError( - info, "flag '%s' may only use false value" % key) + expr.info, "flag '%s' may only use false value" % key) for key in ('boxed', 'allow-oob', 'allow-preconfig', 'coroutine'): if key in expr and expr[key] is not True: raise QAPISemError( - info, "flag '%s' may only use true value" % key) + expr.info, "flag '%s' may only use true value" % key) if 'allow-oob' in expr and 'coroutine' in expr: # This is not necessarily a fundamental incompatibility, but # we don't have a use case and the desired semantics isn't # obvious. The simplest solution is to forbid it until we get # a use case for it. - raise QAPISemError(info, "flags 'allow-oob' and 'coroutine' " - "are incompatible") + raise QAPISemError( + expr.info, "flags 'allow-oob' and 'coroutine' are incompatible") -def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None: +def check_if(expr: Dict[str, object], + info: QAPISourceInfo, source: str) -> None: """ Validate the ``if`` member of an object. @@ -342,62 +333,56 @@ def normalize_members(members: object) -> None: members[key] = {'type': arg} -def check_type(value: Optional[object], - info: QAPISourceInfo, - source: str, - allow_array: bool = False, - allow_dict: Union[bool, str] = False) -> None: - """ - Normalize and validate the QAPI type of ``value``. +def check_type_name(value: Optional[object], + info: QAPISourceInfo, source: str) -> None: + if value is not None and not isinstance(value, str): + raise QAPISemError(info, "%s should be a type name" % source) - Python types of ``str`` or ``None`` are always allowed. + +def check_type_name_or_array(value: Optional[object], + info: QAPISourceInfo, source: str) -> None: + if value is None or isinstance(value, str): + return + + if not isinstance(value, list): + raise QAPISemError(info, + "%s should be a type name or array" % source) + + if len(value) != 1 or not isinstance(value[0], str): + raise QAPISemError(info, + "%s: array type must contain single type name" % + source) + + +def check_type_implicit(value: Optional[object], + info: QAPISourceInfo, source: str, + parent_name: Optional[str]) -> None: + """ + Normalize and validate an optional implicit struct type. + + Accept ``None`` or a ``dict`` defining an implicit struct type. + The latter is normalized in place. :param value: The value to check. :param info: QAPI schema source file information. :param source: Error string describing this ``value``. - :param allow_array: - Allow a ``List[str]`` of length 1, which indicates an array of - the type named by the list element. - :param allow_dict: - Allow a dict. Its members can be struct type members or union - branches. When the value of ``allow_dict`` is in pragma - ``member-name-exceptions``, the dict's keys may violate the - member naming rules. The dict members are normalized in place. + :param parent_name: + When the value of ``parent_name`` is in pragma + ``member-name-exceptions``, an implicit struct type may + violate the member naming rules. :raise QAPISemError: When ``value`` fails validation. - :return: None, ``value`` is normalized in-place as needed. + :return: None """ if value is None: return - # Type name - if isinstance(value, str): - return - - # Array type - if isinstance(value, list): - if not allow_array: - raise QAPISemError(info, "%s cannot be an array" % source) - if len(value) != 1 or not isinstance(value[0], str): - raise QAPISemError(info, - "%s: array type must contain single type name" % - source) - return - - # Anonymous type - - if not allow_dict: - raise QAPISemError(info, "%s should be a type name" % source) - if not isinstance(value, dict): raise QAPISemError(info, "%s should be an object or type name" % source) - permissive = False - if isinstance(allow_dict, str): - permissive = allow_dict in info.pragma.member_name_exceptions + permissive = parent_name in info.pragma.member_name_exceptions - # value is a dictionary, check that each member is okay for (key, arg) in value.items(): key_source = "%s member '%s'" % (source, key) if key.startswith('*'): @@ -410,7 +395,16 @@ def check_type(value: Optional[object], 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) + check_type_name_or_array(arg['type'], info, key_source) + + +def check_type_name_or_implicit(value: Optional[object], + info: QAPISourceInfo, source: str, + parent_name: Optional[str]) -> None: + if value is None or isinstance(value, str): + return + + check_type_implicit(value, info, source, parent_name) def check_features(features: Optional[object], @@ -447,12 +441,11 @@ def check_features(features: Optional[object], check_if(feat, info, source) -def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None: +def check_enum(expr: QAPIExpression) -> None: """ Normalize and validate this expression as an ``enum`` definition. :param expr: The expression to validate. - :param info: QAPI schema source file information. :raise QAPISemError: When ``expr`` is not a valid ``enum``. :return: None, ``expr`` is normalized in-place as needed. @@ -460,6 +453,7 @@ def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None: name = expr['enum'] members = expr['data'] prefix = expr.get('prefix') + info = expr.info if not isinstance(members, list): raise QAPISemError(info, "'data' must be an array") @@ -486,12 +480,11 @@ def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None: check_features(member.get('features'), info) -def check_struct(expr: _JSONObject, info: QAPISourceInfo) -> None: +def check_struct(expr: QAPIExpression) -> None: """ Normalize and validate this expression as a ``struct`` definition. :param expr: The expression to validate. - :param info: QAPI schema source file information. :raise QAPISemError: When ``expr`` is not a valid ``struct``. :return: None, ``expr`` is normalized in-place as needed. @@ -499,16 +492,15 @@ def check_struct(expr: _JSONObject, info: QAPISourceInfo) -> None: name = cast(str, expr['struct']) # Checked in check_exprs members = expr['data'] - check_type(members, info, "'data'", allow_dict=name) - check_type(expr.get('base'), info, "'base'") + check_type_implicit(members, expr.info, "'data'", name) + check_type_name(expr.get('base'), expr.info, "'base'") -def check_union(expr: _JSONObject, info: QAPISourceInfo) -> None: +def check_union(expr: QAPIExpression) -> None: """ Normalize and validate this expression as a ``union`` definition. :param expr: The expression to validate. - :param info: QAPI schema source file information. :raise QAPISemError: when ``expr`` is not a valid ``union``. :return: None, ``expr`` is normalized in-place as needed. @@ -517,8 +509,9 @@ def check_union(expr: _JSONObject, info: QAPISourceInfo) -> None: base = expr['base'] discriminator = expr['discriminator'] members = expr['data'] + info = expr.info - check_type(base, info, "'base'", allow_dict=name) + check_type_name_or_implicit(base, info, "'base'", name) check_name_is_str(discriminator, info, "'discriminator'") if not isinstance(members, dict): @@ -528,20 +521,20 @@ def check_union(expr: _JSONObject, info: QAPISourceInfo) -> None: source = "'data' member '%s'" % key check_keys(value, info, source, ['type'], ['if']) check_if(value, info, source) - check_type(value['type'], info, source, allow_array=not base) + check_type_name(value['type'], info, source) -def check_alternate(expr: _JSONObject, info: QAPISourceInfo) -> None: +def check_alternate(expr: QAPIExpression) -> None: """ Normalize and validate this expression as an ``alternate`` definition. :param expr: The expression to validate. - :param info: QAPI schema source file information. :raise QAPISemError: When ``expr`` is not a valid ``alternate``. :return: None, ``expr`` is normalized in-place as needed. """ members = expr['data'] + info = expr.info if not members: raise QAPISemError(info, "'data' must not be empty") @@ -554,15 +547,14 @@ def check_alternate(expr: _JSONObject, info: QAPISourceInfo) -> None: check_name_lower(key, info, source) check_keys(value, info, source, ['type'], ['if']) check_if(value, info, source) - check_type(value['type'], info, source, allow_array=True) + check_type_name_or_array(value['type'], info, source) -def check_command(expr: _JSONObject, info: QAPISourceInfo) -> None: +def check_command(expr: QAPIExpression) -> None: """ Normalize and validate this expression as a ``command`` definition. :param expr: The expression to validate. - :param info: QAPI schema source file information. :raise QAPISemError: When ``expr`` is not a valid ``command``. :return: None, ``expr`` is normalized in-place as needed. @@ -571,18 +563,20 @@ def check_command(expr: _JSONObject, info: QAPISourceInfo) -> None: rets = expr.get('returns') boxed = expr.get('boxed', False) - if boxed and args is None: - raise QAPISemError(info, "'boxed': true requires 'data'") - check_type(args, info, "'data'", allow_dict=not boxed) - check_type(rets, info, "'returns'", allow_array=True) + if boxed: + if args is None: + raise QAPISemError(expr.info, "'boxed': true requires 'data'") + check_type_name(args, expr.info, "'data'") + else: + check_type_name_or_implicit(args, expr.info, "'data'", None) + check_type_name_or_array(rets, expr.info, "'returns'") -def check_event(expr: _JSONObject, info: QAPISourceInfo) -> None: +def check_event(expr: QAPIExpression) -> None: """ Normalize and validate this expression as an ``event`` definition. :param expr: The expression to validate. - :param info: QAPI schema source file information. :raise QAPISemError: When ``expr`` is not a valid ``event``. :return: None, ``expr`` is normalized in-place as needed. @@ -590,12 +584,15 @@ def check_event(expr: _JSONObject, info: QAPISourceInfo) -> None: args = expr.get('data') boxed = expr.get('boxed', False) - if boxed and args is None: - raise QAPISemError(info, "'boxed': true requires 'data'") - check_type(args, info, "'data'", allow_dict=not boxed) + if boxed: + if args is None: + raise QAPISemError(expr.info, "'boxed': true requires 'data'") + check_type_name(args, expr.info, "'data'") + else: + check_type_name_or_implicit(args, expr.info, "'data'", None) -def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]: +def check_exprs(exprs: List[QAPIExpression]) -> List[QAPIExpression]: """ Validate and normalize a list of parsed QAPI schema expressions. @@ -607,21 +604,9 @@ def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]: :raise QAPISemError: When any expression fails validation. :return: The same list of expressions (now modified). """ - for expr_elem in exprs: - # Expression - assert isinstance(expr_elem['expr'], dict) - for key in expr_elem['expr'].keys(): - assert isinstance(key, str) - expr: _JSONObject = expr_elem['expr'] - - # QAPISourceInfo - assert isinstance(expr_elem['info'], QAPISourceInfo) - info: QAPISourceInfo = expr_elem['info'] - - # Optional[QAPIDoc] - tmp = expr_elem.get('doc') - assert tmp is None or isinstance(tmp, QAPIDoc) - doc: Optional[QAPIDoc] = tmp + for expr in exprs: + info = expr.info + doc = expr.doc if 'include' in expr: continue @@ -653,24 +638,24 @@ def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]: if meta == 'enum': check_keys(expr, info, meta, ['enum', 'data'], ['if', 'features', 'prefix']) - check_enum(expr, info) + check_enum(expr) elif meta == 'union': check_keys(expr, info, meta, ['union', 'base', 'discriminator', 'data'], ['if', 'features']) normalize_members(expr.get('base')) normalize_members(expr['data']) - check_union(expr, info) + check_union(expr) elif meta == 'alternate': check_keys(expr, info, meta, ['alternate', 'data'], ['if', 'features']) normalize_members(expr['data']) - check_alternate(expr, info) + check_alternate(expr) elif meta == 'struct': check_keys(expr, info, meta, ['struct', 'data'], ['base', 'if', 'features']) normalize_members(expr['data']) - check_struct(expr, info) + check_struct(expr) elif meta == 'command': check_keys(expr, info, meta, ['command'], @@ -678,17 +663,17 @@ def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]: 'gen', 'success-response', 'allow-oob', 'allow-preconfig', 'coroutine']) normalize_members(expr.get('data')) - check_command(expr, info) + check_command(expr) elif meta == 'event': check_keys(expr, info, meta, ['event'], ['data', 'boxed', 'if', 'features']) normalize_members(expr.get('data')) - check_event(expr, info) + check_event(expr) else: assert False, 'unexpected meta type' check_if(expr, info, meta) check_features(expr.get('features'), info) - check_flags(expr, info) + check_flags(expr) return exprs diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index 113b49134d..5412716617 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -14,6 +14,7 @@ from contextlib import contextmanager import os import re +import sys from typing import ( Dict, Iterator, @@ -80,7 +81,7 @@ class QAPIGen: if odir: os.makedirs(odir, exist_ok=True) - # use os.open for O_CREAT to create and read a non-existant file + # use os.open for O_CREAT to create and read a non-existent file fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) with os.fdopen(fd, 'r+', encoding='utf-8') as fp: text = self.get_content() @@ -119,9 +120,10 @@ def build_params(arg_type: Optional[QAPISchemaObjectType], elif arg_type: assert not arg_type.variants for memb in arg_type.members: + assert not memb.ifcond.is_present() ret += sep sep = ', ' - if memb.optional: + if memb.need_has(): ret += 'bool has_%s, ' % c_name(memb.name) ret += '%s %s' % (memb.type.c_param_type(), c_name(memb.name)) @@ -161,7 +163,7 @@ class QAPIGenC(QAPIGenCCode): def _top(self) -> str: return mcgen(''' -/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ +/* AUTOMATICALLY GENERATED by %(tool)s DO NOT MODIFY */ /* %(blurb)s @@ -173,6 +175,7 @@ class QAPIGenC(QAPIGenCCode): */ ''', + tool=os.path.basename(sys.argv[0]), blurb=self._blurb, copyright=self._copyright) def _bottom(self) -> str: @@ -194,7 +197,10 @@ class QAPIGenH(QAPIGenC): class QAPIGenTrace(QAPIGen): def _top(self) -> str: - return super()._top() + '# AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n' + return (super()._top() + + '# AUTOMATICALLY GENERATED by ' + + os.path.basename(sys.argv[0]) + + ', DO NOT MODIFY\n\n') @contextmanager diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py index fc216a53d3..316736b6a2 100644 --- a/scripts/qapi/main.py +++ b/scripts/qapi/main.py @@ -98,6 +98,6 @@ def main() -> int: builtins=args.builtins, gen_tracing=not args.suppress_tracing) except QAPIError as err: - print(f"{sys.argv[0]}: {str(err)}", file=sys.stderr) + print(err, file=sys.stderr) return 1 return 0 diff --git a/scripts/qapi/mypy.ini b/scripts/qapi/mypy.ini index 6625356429..56e0dfb132 100644 --- a/scripts/qapi/mypy.ini +++ b/scripts/qapi/mypy.ini @@ -1,7 +1,7 @@ [mypy] strict = True disallow_untyped_calls = False -python_version = 3.6 +python_version = 3.8 [mypy-qapi.schema] disallow_untyped_defs = False diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 1b006cdc13..d8f76060b8 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -21,6 +21,8 @@ from typing import ( TYPE_CHECKING, Dict, List, + Mapping, + Match, Optional, Set, Union, @@ -37,15 +39,19 @@ if TYPE_CHECKING: from .schema import QAPISchemaFeature, QAPISchemaMember -#: Represents a single Top Level QAPI schema expression. -TopLevelExpr = Dict[str, object] - # Return value alias for get_expr(). _ExprValue = Union[List[object], Dict[str, object], str, bool] -# FIXME: Consolidate and centralize definitions for TopLevelExpr, -# _ExprValue, _JSONValue, and _JSONObject; currently scattered across -# several modules. + +class QAPIExpression(Dict[str, object]): + # pylint: disable=too-few-public-methods + def __init__(self, + data: Mapping[str, object], + info: QAPISourceInfo, + doc: Optional['QAPIDoc'] = None): + super().__init__(data) + self.info = info + self.doc: Optional['QAPIDoc'] = doc class QAPIParseError(QAPISourceError): @@ -65,7 +71,7 @@ class QAPISchemaParser: Parse QAPI schema source. Parse a JSON-esque schema file and process directives. See - qapi-code-gen.txt section "Schema Syntax" for the exact syntax. + qapi-code-gen.rst section "Schema Syntax" for the exact syntax. Grammatical validation is handled later by `expr.check_exprs()`. :param fname: Source file name. @@ -100,7 +106,7 @@ class QAPISchemaParser: self.line_pos = 0 # Parser output: - self.exprs: List[Dict[str, object]] = [] + self.exprs: List[QAPIExpression] = [] self.docs: List[QAPIDoc] = [] # Showtime! @@ -128,8 +134,8 @@ class QAPISchemaParser: info = self.info if self.tok == '#': self.reject_expr_doc(cur_doc) - for cur_doc in self.get_doc(info): - self.docs.append(cur_doc) + cur_doc = self.get_doc() + self.docs.append(cur_doc) continue expr = self.get_expr() @@ -147,8 +153,7 @@ class QAPISchemaParser: "value of 'include' must be a string") incl_fname = os.path.join(os.path.dirname(self._fname), include) - self.exprs.append({'expr': {'include': incl_fname}, - 'info': info}) + self._add_expr(OrderedDict({'include': incl_fname}), info) exprs_include = self._include(include, info, incl_fname, self._included) if exprs_include: @@ -165,17 +170,18 @@ class QAPISchemaParser: for name, value in pragma.items(): self._pragma(name, value, info) else: - expr_elem = {'expr': expr, - 'info': info} - if cur_doc: - if not cur_doc.symbol: - raise QAPISemError( - cur_doc.info, "definition documentation required") - expr_elem['doc'] = cur_doc - self.exprs.append(expr_elem) + if cur_doc and not cur_doc.symbol: + raise QAPISemError( + cur_doc.info, "definition documentation required") + self._add_expr(expr, info, cur_doc) cur_doc = None self.reject_expr_doc(cur_doc) + def _add_expr(self, expr: Mapping[str, object], + info: QAPISourceInfo, + doc: Optional['QAPIDoc'] = None) -> None: + self.exprs.append(QAPIExpression(expr, info, doc)) + @staticmethod def reject_expr_doc(doc: Optional['QAPIDoc']) -> None: if doc and doc.symbol: @@ -232,6 +238,8 @@ class QAPISchemaParser: pragma.command_name_exceptions = check_list_str(name, value) elif name == 'command-returns-exceptions': pragma.command_returns_exceptions = check_list_str(name, value) + elif name == 'documentation-exceptions': + pragma.documentation_exceptions = check_list_str(name, value) elif name == 'member-name-exceptions': pragma.member_name_exceptions = check_list_str(name, value) else: @@ -341,7 +349,7 @@ class QAPISchemaParser: elif not self.tok.isspace(): # Show up to next structural, whitespace or quote # character - match = must_match('[^[\\]{}:,\\s\'"]+', + match = must_match('[^[\\]{}:,\\s\']+', self.src[self.cursor-1:]) raise QAPIParseError(self, "stray '%s'" % match.group(0)) @@ -406,39 +414,177 @@ class QAPISchemaParser: self, "expected '{', '[', string, or boolean") return expr - def get_doc(self, info: QAPISourceInfo) -> List['QAPIDoc']: + def get_doc_line(self) -> Optional[str]: + if self.tok != '#': + raise QAPIParseError( + self, "documentation comment must end with '##'") + assert isinstance(self.val, str) + if self.val.startswith('##'): + # End of doc comment + if self.val != '##': + raise QAPIParseError( + self, "junk after '##' at end of documentation comment") + return None + if self.val == '#': + return '' + if self.val[1] != ' ': + raise QAPIParseError(self, "missing space after #") + return self.val[2:].rstrip() + + @staticmethod + def _match_at_name_colon(string: str) -> Optional[Match[str]]: + return re.match(r'@([^:]*): *', string) + + def get_doc_indented(self, doc: 'QAPIDoc') -> Optional[str]: + self.accept(False) + line = self.get_doc_line() + while line == '': + doc.append_line(line) + self.accept(False) + line = self.get_doc_line() + if line is None: + return line + indent = must_match(r'\s*', line).end() + if not indent: + return line + doc.append_line(line[indent:]) + prev_line_blank = False + while True: + self.accept(False) + line = self.get_doc_line() + if line is None: + return line + if self._match_at_name_colon(line): + return line + cur_indent = must_match(r'\s*', line).end() + if line != '' and cur_indent < indent: + if prev_line_blank: + return line + raise QAPIParseError( + self, + "unexpected de-indent (expected at least %d spaces)" % + indent) + doc.append_line(line[indent:]) + prev_line_blank = True + + def get_doc_paragraph(self, doc: 'QAPIDoc') -> Optional[str]: + while True: + self.accept(False) + line = self.get_doc_line() + if line is None: + return line + if line == '': + return line + doc.append_line(line) + + def get_doc(self) -> 'QAPIDoc': if self.val != '##': raise QAPIParseError( self, "junk after '##' at start of documentation comment") - - docs = [] - cur_doc = QAPIDoc(self, info) + info = self.info self.accept(False) - while self.tok == '#': - assert isinstance(self.val, str) - if self.val.startswith('##'): - # End of doc comment - if self.val != '##': - raise QAPIParseError( - self, - "junk after '##' at end of documentation comment") - cur_doc.end_comment() - docs.append(cur_doc) - self.accept() - return docs - if self.val.startswith('# ='): - if cur_doc.symbol: + line = self.get_doc_line() + if line is not None and line.startswith('@'): + # Definition documentation + if not line.endswith(':'): + raise QAPIParseError(self, "line should end with ':'") + # Invalid names are not checked here, but the name + # provided *must* match the following definition, + # which *is* validated in expr.py. + symbol = line[1:-1] + if not symbol: + raise QAPIParseError(self, "name required after '@'") + doc = QAPIDoc(info, symbol) + self.accept(False) + line = self.get_doc_line() + no_more_args = False + + while line is not None: + # Blank lines + while line == '': + self.accept(False) + line = self.get_doc_line() + if line is None: + break + # Non-blank line, first of a section + if line == 'Features:': + if doc.features: + raise QAPIParseError( + self, "duplicated 'Features:' line") + self.accept(False) + line = self.get_doc_line() + while line == '': + self.accept(False) + line = self.get_doc_line() + while (line is not None + and (match := self._match_at_name_colon(line))): + doc.new_feature(self.info, match.group(1)) + text = line[match.end():] + if text: + doc.append_line(text) + line = self.get_doc_indented(doc) + if not doc.features: + raise QAPIParseError( + self, 'feature descriptions expected') + no_more_args = True + elif match := self._match_at_name_colon(line): + # description + if no_more_args: + raise QAPIParseError( + self, + "description of '@%s:' follows a section" + % match.group(1)) + while (line is not None + and (match := self._match_at_name_colon(line))): + doc.new_argument(self.info, match.group(1)) + text = line[match.end():] + if text: + doc.append_line(text) + line = self.get_doc_indented(doc) + no_more_args = True + elif match := re.match( + r'(Returns|Errors|Since|Notes?|Examples?|TODO): *', + line): + # tagged section + doc.new_tagged_section(self.info, match.group(1)) + text = line[match.end():] + if text: + doc.append_line(text) + line = self.get_doc_indented(doc) + no_more_args = True + elif line.startswith('='): raise QAPIParseError( self, "unexpected '=' markup in definition documentation") - if cur_doc.body.text: - cur_doc.end_comment() - docs.append(cur_doc) - cur_doc = QAPIDoc(self, info) - cur_doc.append(self.val) - self.accept(False) + else: + # tag-less paragraph + doc.ensure_untagged_section(self.info) + doc.append_line(line) + line = self.get_doc_paragraph(doc) + else: + # Free-form documentation + doc = QAPIDoc(info) + doc.ensure_untagged_section(self.info) + first = True + while line is not None: + if match := self._match_at_name_colon(line): + raise QAPIParseError( + self, + "'@%s:' not allowed in free-form documentation" + % match.group(1)) + if line.startswith('='): + if not first: + raise QAPIParseError( + self, + "'=' heading must come first in a comment block") + doc.append_line(line) + self.accept(False) + line = self.get_doc_line() + first = False - raise QAPIParseError(self, "documentation comment must end with '##'") + self.accept(False) + doc.end() + return doc class QAPIDoc: @@ -461,320 +607,110 @@ class QAPIDoc: """ class Section: - # pylint: disable=too-few-public-methods - def __init__(self, parser: QAPISchemaParser, - name: Optional[str] = None, indent: int = 0): - - # parser, for error messages about indentation - self._parser = parser - # optional section name (argument/member or section name) - self.name = name + def __init__(self, info: QAPISourceInfo, + tag: Optional[str] = None): + # section source info, i.e. where it begins + self.info = info + # section tag, if any ('Returns', '@name', ...) + self.tag = tag + # section text without tag self.text = '' - # the expected indent level of the text of this section - self._indent = indent - def append(self, line: str) -> None: - # Strip leading spaces corresponding to the expected indent level - # Blank lines are always OK. - if line: - indent = must_match(r'\s*', line).end() - if indent < self._indent: - raise QAPIParseError( - self._parser, - "unexpected de-indent (expected at least %d spaces)" % - self._indent) - line = line[self._indent:] - - self.text += line.rstrip() + '\n' + def append_line(self, line: str) -> None: + self.text += line + '\n' class ArgSection(Section): - def __init__(self, parser: QAPISchemaParser, - name: str, indent: int = 0): - super().__init__(parser, name, indent) + def __init__(self, info: QAPISourceInfo, tag: str): + super().__init__(info, tag) self.member: Optional['QAPISchemaMember'] = None def connect(self, member: 'QAPISchemaMember') -> None: self.member = member - class NullSection(Section): - """ - Immutable dummy section for use at the end of a doc block. - """ - # pylint: disable=too-few-public-methods - def append(self, line: str) -> None: - assert False, "Text appended after end_comment() called." - - def __init__(self, parser: QAPISchemaParser, info: QAPISourceInfo): - # self._parser is used to report errors with QAPIParseError. The - # resulting error position depends on the state of the parser. - # It happens to be the beginning of the comment. More or less - # servicable, but action at a distance. - self._parser = parser + def __init__(self, info: QAPISourceInfo, symbol: Optional[str] = None): + # info points to the doc comment block's first line self.info = info - self.symbol: Optional[str] = None - self.body = QAPIDoc.Section(parser) - # dicts mapping parameter/feature names to their ArgSection - self.args: Dict[str, QAPIDoc.ArgSection] = OrderedDict() - self.features: Dict[str, QAPIDoc.ArgSection] = OrderedDict() + # definition doc's symbol, None for free-form doc + self.symbol: Optional[str] = symbol + # the sections in textual order + self.all_sections: List[QAPIDoc.Section] = [QAPIDoc.Section(info)] + # the body section + self.body: Optional[QAPIDoc.Section] = self.all_sections[0] + # dicts mapping parameter/feature names to their description + self.args: Dict[str, QAPIDoc.ArgSection] = {} + self.features: Dict[str, QAPIDoc.ArgSection] = {} + # a command's "Returns" and "Errors" section + self.returns: Optional[QAPIDoc.Section] = None + self.errors: Optional[QAPIDoc.Section] = None + # "Since" section + self.since: Optional[QAPIDoc.Section] = None + # sections other than .body, .args, .features self.sections: List[QAPIDoc.Section] = [] - # the current section - self._section = self.body - self._append_line = self._append_body_line - def has_section(self, name: str) -> bool: - """Return True if we have a section with this name.""" - for i in self.sections: - if i.name == name: - return True - return False + def end(self) -> None: + for section in self.all_sections: + section.text = section.text.strip('\n') + if section.tag is not None and section.text == '': + raise QAPISemError( + section.info, "text required after '%s:'" % section.tag) - def append(self, line: str) -> None: - """ - Parse a comment line and add it to the documentation. - - The way that the line is dealt with depends on which part of - the documentation we're parsing right now: - * The body section: ._append_line is ._append_body_line - * An argument section: ._append_line is ._append_args_line - * A features section: ._append_line is ._append_features_line - * An additional section: ._append_line is ._append_various_line - """ - line = line[1:] - if not line: - self._append_freeform(line) + def ensure_untagged_section(self, info: QAPISourceInfo) -> None: + if self.all_sections and not self.all_sections[-1].tag: + # extend current section + self.all_sections[-1].text += '\n' return + # start new section + section = self.Section(info) + self.sections.append(section) + self.all_sections.append(section) - if line[0] != ' ': - raise QAPIParseError(self._parser, "missing space after #") - line = line[1:] - self._append_line(line) + def new_tagged_section(self, info: QAPISourceInfo, tag: str) -> None: + section = self.Section(info, tag) + if tag == 'Returns': + if self.returns: + raise QAPISemError( + info, "duplicated '%s' section" % tag) + self.returns = section + elif tag == 'Errors': + if self.errors: + raise QAPISemError( + info, "duplicated '%s' section" % tag) + self.errors = section + elif tag == 'Since': + if self.since: + raise QAPISemError( + info, "duplicated '%s' section" % tag) + self.since = section + self.sections.append(section) + self.all_sections.append(section) - def end_comment(self) -> None: - self._switch_section(QAPIDoc.NullSection(self._parser)) - - @staticmethod - def _is_section_tag(name: str) -> bool: - return name in ('Returns:', 'Since:', - # those are often singular or plural - 'Note:', 'Notes:', - 'Example:', 'Examples:', - 'TODO:') - - def _append_body_line(self, line: str) -> None: - """ - Process a line of documentation text in the body section. - - If this a symbol line and it is the section's first line, this - is a definition documentation block for that symbol. - - If it's a definition documentation block, another symbol line - begins the argument section for the argument named by it, and - a section tag begins an additional section. Start that - section and append the line to it. - - Else, append the line to the current section. - """ - name = line.split(' ', 1)[0] - # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't - # recognized, and get silently treated as ordinary text - if not self.symbol and not self.body.text and line.startswith('@'): - if not line.endswith(':'): - raise QAPIParseError(self._parser, "line should end with ':'") - self.symbol = line[1:-1] - # Invalid names are not checked here, but the name provided MUST - # match the following definition, which *is* validated in expr.py. - if not self.symbol: - raise QAPIParseError( - self._parser, "name required after '@'") - elif self.symbol: - # This is a definition documentation block - if name.startswith('@') and name.endswith(':'): - self._append_line = self._append_args_line - self._append_args_line(line) - elif line == 'Features:': - self._append_line = self._append_features_line - elif self._is_section_tag(name): - self._append_line = self._append_various_line - self._append_various_line(line) - else: - self._append_freeform(line) - else: - # This is a free-form documentation block - self._append_freeform(line) - - def _append_args_line(self, line: str) -> None: - """ - Process a line of documentation text in an argument section. - - A symbol line begins the next argument section, a section tag - section or a non-indented line after a blank line begins an - additional section. Start that section and append the line to - it. - - Else, append the line to the current section. - - """ - name = line.split(' ', 1)[0] - - if name.startswith('@') and name.endswith(':'): - # If line is "@arg: first line of description", find - # the index of 'f', which is the indent we expect for any - # following lines. We then remove the leading "@arg:" - # from line and replace it with spaces so that 'f' has the - # same index as it did in the original line and can be - # handled the same way we will handle following lines. - indent = must_match(r'@\S*:\s*', line).end() - line = line[indent:] - if not line: - # Line was just the "@arg:" header; following lines - # are not indented - indent = 0 - else: - line = ' ' * indent + line - self._start_args_section(name[1:-1], indent) - elif self._is_section_tag(name): - self._append_line = self._append_various_line - self._append_various_line(line) - return - elif (self._section.text.endswith('\n\n') - and line and not line[0].isspace()): - if line == 'Features:': - self._append_line = self._append_features_line - else: - self._start_section() - self._append_line = self._append_various_line - self._append_various_line(line) - return - - self._append_freeform(line) - - def _append_features_line(self, line: str) -> None: - name = line.split(' ', 1)[0] - - if name.startswith('@') and name.endswith(':'): - # If line is "@arg: first line of description", find - # the index of 'f', which is the indent we expect for any - # following lines. We then remove the leading "@arg:" - # from line and replace it with spaces so that 'f' has the - # same index as it did in the original line and can be - # handled the same way we will handle following lines. - indent = must_match(r'@\S*:\s*', line).end() - line = line[indent:] - if not line: - # Line was just the "@arg:" header; following lines - # are not indented - indent = 0 - else: - line = ' ' * indent + line - self._start_features_section(name[1:-1], indent) - elif self._is_section_tag(name): - self._append_line = self._append_various_line - self._append_various_line(line) - return - elif (self._section.text.endswith('\n\n') - and line and not line[0].isspace()): - self._start_section() - self._append_line = self._append_various_line - self._append_various_line(line) - return - - self._append_freeform(line) - - def _append_various_line(self, line: str) -> None: - """ - Process a line of documentation text in an additional section. - - A symbol line is an error. - - A section tag begins an additional section. Start that - section and append the line to it. - - Else, append the line to the current section. - """ - name = line.split(' ', 1)[0] - - if name.startswith('@') and name.endswith(':'): - raise QAPIParseError(self._parser, - "'%s' can't follow '%s' section" - % (name, self.sections[0].name)) - if self._is_section_tag(name): - # If line is "Section: first line of description", find - # the index of 'f', which is the indent we expect for any - # following lines. We then remove the leading "Section:" - # from line and replace it with spaces so that 'f' has the - # same index as it did in the original line and can be - # handled the same way we will handle following lines. - indent = must_match(r'\S*:\s*', line).end() - line = line[indent:] - if not line: - # Line was just the "Section:" header; following lines - # are not indented - indent = 0 - else: - line = ' ' * indent + line - self._start_section(name[:-1], indent) - - self._append_freeform(line) - - def _start_symbol_section( - self, - symbols_dict: Dict[str, 'QAPIDoc.ArgSection'], - name: str, - indent: int) -> None: - # FIXME invalid names other than the empty string aren't flagged + def _new_description(self, info: QAPISourceInfo, name: str, + desc: Dict[str, ArgSection]) -> None: if not name: - raise QAPIParseError(self._parser, "invalid parameter name") - if name in symbols_dict: - raise QAPIParseError(self._parser, - "'%s' parameter name duplicated" % name) - assert not self.sections - new_section = QAPIDoc.ArgSection(self._parser, name, indent) - self._switch_section(new_section) - symbols_dict[name] = new_section + raise QAPISemError(info, "invalid parameter name") + if name in desc: + raise QAPISemError(info, "'%s' parameter name duplicated" % name) + section = self.ArgSection(info, '@' + name) + self.all_sections.append(section) + desc[name] = section - def _start_args_section(self, name: str, indent: int) -> None: - self._start_symbol_section(self.args, name, indent) + def new_argument(self, info: QAPISourceInfo, name: str) -> None: + self._new_description(info, name, self.args) - def _start_features_section(self, name: str, indent: int) -> None: - self._start_symbol_section(self.features, name, indent) + def new_feature(self, info: QAPISourceInfo, name: str) -> None: + self._new_description(info, name, self.features) - def _start_section(self, name: Optional[str] = None, - indent: int = 0) -> None: - if name in ('Returns', 'Since') and self.has_section(name): - raise QAPIParseError(self._parser, - "duplicated '%s' section" % name) - new_section = QAPIDoc.Section(self._parser, name, indent) - self._switch_section(new_section) - self.sections.append(new_section) - - def _switch_section(self, new_section: 'QAPIDoc.Section') -> None: - text = self._section.text = self._section.text.strip() - - # Only the 'body' section is allowed to have an empty body. - # All other sections, including anonymous ones, must have text. - if self._section != self.body and not text: - # We do not create anonymous sections unless there is - # something to put in them; this is a parser bug. - assert self._section.name - raise QAPIParseError( - self._parser, - "empty doc section '%s'" % self._section.name) - - self._section = new_section - - def _append_freeform(self, line: str) -> None: - match = re.match(r'(@\S+:)', line) - if match: - raise QAPIParseError(self._parser, - "'%s' not allowed in free-form documentation" - % match.group(1)) - self._section.append(line) + def append_line(self, line: str) -> None: + self.all_sections[-1].append_line(line) def connect_member(self, member: 'QAPISchemaMember') -> None: if member.name not in self.args: - # Undocumented TODO outlaw - self.args[member.name] = QAPIDoc.ArgSection(self._parser, - member.name) + if self.symbol not in member.info.pragma.documentation_exceptions: + raise QAPISemError(member.info, + "%s '%s' lacks documentation" + % (member.role, member.name)) + self.args[member.name] = QAPIDoc.ArgSection( + self.info, '@' + member.name) self.args[member.name].connect(member) def connect_feature(self, feature: 'QAPISchemaFeature') -> None: @@ -784,10 +720,21 @@ class QAPIDoc: % feature.name) self.features[feature.name].connect(feature) - def check_expr(self, expr: TopLevelExpr) -> None: - if self.has_section('Returns') and 'command' not in expr: - raise QAPISemError(self.info, - "'Returns:' is only valid for commands") + def check_expr(self, expr: QAPIExpression) -> None: + if 'command' in expr: + if self.returns and 'returns' not in expr: + raise QAPISemError( + self.returns.info, + "'Returns' section, but command doesn't return anything") + else: + if self.returns: + raise QAPISemError( + self.returns.info, + "'Returns' section is only valid for commands") + if self.errors: + raise QAPISemError( + self.returns.info, + "'Errors' section is only valid for commands") def check(self) -> None: @@ -798,7 +745,7 @@ class QAPIDoc: if not section.member] if bogus: raise QAPISemError( - self.info, + args[bogus[0]].info, "documented %s%s '%s' %s not exist" % ( what, "s" if len(bogus) > 1 else "", diff --git a/scripts/qapi/pylintrc b/scripts/qapi/pylintrc index a724628203..90546df534 100644 --- a/scripts/qapi/pylintrc +++ b/scripts/qapi/pylintrc @@ -23,6 +23,7 @@ disable=fixme, too-many-statements, too-many-instance-attributes, consider-using-f-string, + useless-option-value, [REPORTS] diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 3728340c37..8ba5665bc6 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -17,7 +17,7 @@ from collections import OrderedDict import os import re -from typing import Optional +from typing import List, Optional from .common import ( POINTER_SUFFIX, @@ -29,7 +29,7 @@ from .common import ( ) from .error import QAPIError, QAPISemError, QAPISourceError from .expr import check_exprs -from .parser import QAPISchemaParser +from .parser import QAPIExpression, QAPISchemaParser class QAPISchemaIfCond: @@ -73,6 +73,12 @@ class QAPISchemaEntity: self.features = features or [] self._checked = False + def __repr__(self): + if self.name is None: + return "<%s at 0x%x>" % (type(self).__name__, id(self)) + return "<%s:%s at 0x%x>" % (type(self).__name__, self.name, + id(self)) + def c_name(self): return c_name(self.name) @@ -89,10 +95,6 @@ class QAPISchemaEntity: 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 fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME @@ -253,8 +255,13 @@ class QAPISchemaType(QAPISchemaEntity): return None return self.name + def need_has_if_optional(self): + # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant. + # Except for arrays; see QAPISchemaArrayType.need_has_if_optional(). + return not self.c_type().endswith(POINTER_SUFFIX) + def check(self, schema): - QAPISchemaEntity.check(self, schema) + super().check(schema) for feat in self.features: if feat.is_special(): raise QAPISemError( @@ -352,6 +359,11 @@ class QAPISchemaArrayType(QAPISchemaType): self._element_type_name = element_type self.element_type = None + def need_has_if_optional(self): + # When FOO is an array, we still need has_FOO to distinguish + # absent (!has_FOO) from present and empty (has_FOO && !FOO). + return True + def check(self, schema): super().check(schema) self.element_type = schema.resolve_type( @@ -455,9 +467,10 @@ class QAPISchemaObjectType(QAPISchemaType): # on behalf of info, which is not necessarily self.info def check_clash(self, info, seen): assert self._checked - assert not self.variants # not implemented for m in self.members: m.check_clash(info, seen) + if self.variants: + self.variants.check_clash(info, seen) def connect_doc(self, doc=None): super().connect_doc(doc) @@ -476,6 +489,10 @@ class QAPISchemaObjectType(QAPISchemaType): assert self.members is not None return not self.members and not self.variants + def has_conditional_members(self): + assert self.members is not None + return any(m.ifcond.is_present() for m in self.members) + def c_name(self): assert self.name != 'q_empty' return super().c_name() @@ -642,8 +659,7 @@ class QAPISchemaVariants: self.info, "branch '%s' is not a value of %s" % (v.name, self.tag_member.type.describe())) - if (not isinstance(v.type, QAPISchemaObjectType) - or v.type.variants): + if not isinstance(v.type, QAPISchemaObjectType): raise QAPISemError( self.info, "%s cannot use %s" @@ -687,6 +703,7 @@ class QAPISchemaMember: def describe(self, info): role = self.role + meta = 'type' defined_in = self.defined_in assert defined_in @@ -698,13 +715,17 @@ class QAPISchemaMember: # Implicit type created for a command's dict 'data' assert role == 'member' role = 'parameter' + meta = 'command' + defined_in = defined_in[:-4] elif defined_in.endswith('-base'): # Implicit type created for a union's dict 'base' role = 'base ' + role + defined_in = defined_in[:-5] else: assert False - elif defined_in != info.defn_name: - return "%s '%s' of type '%s'" % (role, self.name, defined_in) + + if defined_in != info.defn_name: + return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in) return "%s '%s'" % (role, self.name) @@ -745,6 +766,10 @@ class QAPISchemaObjectTypeMember(QAPISchemaMember): self.optional = optional self.features = features or [] + def need_has(self): + assert self.type + return self.optional and self.type.need_has_if_optional() + def check(self, schema): assert self.defined_in self.type = schema.resolve_type(self._type_name, self.info, @@ -803,6 +828,11 @@ class QAPISchemaCommand(QAPISchemaEntity): self.info, "command's 'data' can take %s only with 'boxed': true" % self.arg_type.describe()) + self.arg_type.check(schema) + if self.arg_type.has_conditional_members() and not self.boxed: + raise QAPISemError( + self.info, + "conditional command arguments require 'boxed': true") if self._ret_type_name: self.ret_type = schema.resolve_type( self._ret_type_name, self.info, "command's 'returns'") @@ -858,6 +888,11 @@ class QAPISchemaEvent(QAPISchemaEntity): self.info, "event's 'data' can take %s only with 'boxed': true" % self.arg_type.describe()) + self.arg_type.check(schema) + if self.arg_type.has_conditional_members() and not self.boxed: + raise QAPISemError( + self.info, + "conditional event arguments require 'boxed': true") def connect_doc(self, doc=None): super().connect_doc(doc) @@ -950,10 +985,11 @@ class QAPISchema: name = self._module_name(fname) return self._module_dict[name] - def _def_include(self, expr, info, doc): + def _def_include(self, expr: QAPIExpression): include = expr['include'] - assert doc is None - self._def_entity(QAPISchemaInclude(self._make_module(include), info)) + assert expr.doc is None + self._def_entity( + QAPISchemaInclude(self._make_module(include), expr.info)) def _def_builtin_type(self, name, json_type, c_type): self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) @@ -1031,14 +1067,15 @@ class QAPISchema: name, info, None, ifcond, None, None, members, None)) return name - def _def_enum_type(self, expr, info, doc): + def _def_enum_type(self, expr: QAPIExpression): name = expr['enum'] data = expr['data'] prefix = expr.get('prefix') ifcond = QAPISchemaIfCond(expr.get('if')) + info = expr.info features = self._make_features(expr.get('features'), info) self._def_entity(QAPISchemaEnumType( - name, info, doc, ifcond, features, + name, info, expr.doc, ifcond, features, self._make_enum_members(data, info), prefix)) def _make_member(self, name, typ, ifcond, features, info): @@ -1058,14 +1095,15 @@ class QAPISchema: value.get('features'), info) for (key, value) in data.items()] - def _def_struct_type(self, expr, info, doc): + def _def_struct_type(self, expr: QAPIExpression): name = expr['struct'] base = expr.get('base') data = expr['data'] + info = expr.info ifcond = QAPISchemaIfCond(expr.get('if')) features = self._make_features(expr.get('features'), info) self._def_entity(QAPISchemaObjectType( - name, info, doc, ifcond, features, base, + name, info, expr.doc, ifcond, features, base, self._make_members(data, info), None)) @@ -1075,11 +1113,13 @@ class QAPISchema: typ = self._make_array_type(typ[0], info) return QAPISchemaVariant(case, info, typ, ifcond) - def _def_union_type(self, expr, info, doc): + def _def_union_type(self, expr: QAPIExpression): name = expr['union'] base = expr['base'] tag_name = expr['discriminator'] data = expr['data'] + assert isinstance(data, dict) + info = expr.info ifcond = QAPISchemaIfCond(expr.get('if')) features = self._make_features(expr.get('features'), info) if isinstance(base, dict): @@ -1091,17 +1131,19 @@ class QAPISchema: QAPISchemaIfCond(value.get('if')), info) for (key, value) in data.items()] - members = [] + members: List[QAPISchemaObjectTypeMember] = [] self._def_entity( - QAPISchemaObjectType(name, info, doc, ifcond, features, + QAPISchemaObjectType(name, info, expr.doc, ifcond, features, base, members, QAPISchemaVariants( tag_name, info, None, variants))) - def _def_alternate_type(self, expr, info, doc): + def _def_alternate_type(self, expr: QAPIExpression): name = expr['alternate'] data = expr['data'] + assert isinstance(data, dict) ifcond = QAPISchemaIfCond(expr.get('if')) + info = expr.info features = self._make_features(expr.get('features'), info) variants = [ self._make_variant(key, value['type'], @@ -1110,11 +1152,11 @@ class QAPISchema: for (key, value) in data.items()] tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) self._def_entity( - QAPISchemaAlternateType(name, info, doc, ifcond, features, - QAPISchemaVariants( - None, info, tag_member, variants))) + QAPISchemaAlternateType( + name, info, expr.doc, ifcond, features, + QAPISchemaVariants(None, info, tag_member, variants))) - def _def_command(self, expr, info, doc): + def _def_command(self, expr: QAPIExpression): name = expr['command'] data = expr.get('data') rets = expr.get('returns') @@ -1125,6 +1167,7 @@ class QAPISchema: allow_preconfig = expr.get('allow-preconfig', False) coroutine = expr.get('coroutine', False) ifcond = QAPISchemaIfCond(expr.get('if')) + info = expr.info features = self._make_features(expr.get('features'), info) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( @@ -1133,44 +1176,42 @@ class QAPISchema: if isinstance(rets, list): assert len(rets) == 1 rets = self._make_array_type(rets[0], info) - self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features, - data, rets, + self._def_entity(QAPISchemaCommand(name, info, expr.doc, ifcond, + features, data, rets, gen, success_response, boxed, allow_oob, allow_preconfig, coroutine)) - def _def_event(self, expr, info, doc): + def _def_event(self, expr: QAPIExpression): name = expr['event'] data = expr.get('data') boxed = expr.get('boxed', False) ifcond = QAPISchemaIfCond(expr.get('if')) + info = expr.info 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, features, - data, boxed)) + self._def_entity(QAPISchemaEvent(name, info, expr.doc, ifcond, + features, data, boxed)) def _def_exprs(self, exprs): - for expr_elem in exprs: - expr = expr_elem['expr'] - info = expr_elem['info'] - doc = expr_elem.get('doc') + for expr in exprs: if 'enum' in expr: - self._def_enum_type(expr, info, doc) + self._def_enum_type(expr) elif 'struct' in expr: - self._def_struct_type(expr, info, doc) + self._def_struct_type(expr) elif 'union' in expr: - self._def_union_type(expr, info, doc) + self._def_union_type(expr) elif 'alternate' in expr: - self._def_alternate_type(expr, info, doc) + self._def_alternate_type(expr) elif 'command' in expr: - self._def_command(expr, info, doc) + self._def_command(expr) elif 'event' in expr: - self._def_event(expr, info, doc) + self._def_event(expr) elif 'include' in expr: - self._def_include(expr, info, doc) + self._def_include(expr) else: assert False @@ -1178,9 +1219,10 @@ class QAPISchema: for ent in self._entity_list: ent.check(self) ent.connect_doc() - ent.check_doc() for ent in self._entity_list: ent.set_module(self) + for doc in self.docs: + doc.check() def visit(self, visitor): visitor.visit_begin(self) diff --git a/scripts/qapi/source.py b/scripts/qapi/source.py index 04193cc964..7b379fdc92 100644 --- a/scripts/qapi/source.py +++ b/scripts/qapi/source.py @@ -24,6 +24,8 @@ class QAPISchemaPragma: self.command_name_exceptions: List[str] = [] # Commands allowed to return a non-dictionary self.command_returns_exceptions: List[str] = [] + # Types, commands, and events with undocumented members + self.documentation_exceptions: List[str] = [] # Types whose member names may violate case conventions self.member_name_exceptions: List[str] = [] diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 477d027001..c39d054d2c 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -142,7 +142,7 @@ def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str: ret = '' for memb in members: ret += memb.ifcond.gen_if() - if memb.optional: + if memb.need_has(): ret += mcgen(''' bool has_%(c_name)s; ''', diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 380fa197f5..c56ea4d724 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -71,6 +71,18 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) ''', c_name=c_name(name)) + sep = '' + for memb in members: + if memb.optional and not memb.need_has(): + ret += memb.ifcond.gen_if() + ret += mcgen(''' + bool has_%(c_name)s = !!obj->%(c_name)s; +''', + c_name=c_name(memb.name)) + sep = '\n' + ret += memb.ifcond.gen_endif() + ret += sep + if base: ret += mcgen(''' if (!visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, errp)) { @@ -82,10 +94,13 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) for memb in members: ret += memb.ifcond.gen_if() if memb.optional: + has = 'has_' + c_name(memb.name) + if memb.need_has(): + has = 'obj->' + has ret += mcgen(''' - if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) { + if (visit_optional(v, "%(name)s", &%(has)s)) { ''', - name=memb.name, c_name=c_name(memb.name)) + name=memb.name, has=has) indent.increase() special_features = gen_special_features(memb.features) if special_features != '0': diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index d037a1eaa2..ab60af8930 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -4,7 +4,7 @@ qemu_target_list="i386 i486 alpha arm armeb sparc sparc32plus sparc64 \ ppc ppc64 ppc64le m68k mips mipsel mipsn32 mipsn32el mips64 mips64el \ sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64 xtensa xtensaeb \ -microblaze microblazeel or1k x86_64 hexagon e2k e2k_old e2k32 e2k32_old" +microblaze microblazeel or1k x86_64 hexagon loongarch64 e2k e2k_old e2k32 e2k32_old" i386_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00' i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' @@ -140,6 +140,10 @@ hexagon_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x hexagon_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' hexagon_family=hexagon +loongarch64_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02\x01' +loongarch64_mask='\xff\xff\xff\xff\xff\xff\xff\xfc\x00\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +loongarch64_family=loongarch + e2k_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xaf\x00' e2k_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' e2k_family=e2k @@ -183,6 +187,9 @@ qemu_get_family() { riscv*) echo "riscv" ;; + loongarch*) + echo "loongarch" + ;; *) echo "$cpu" ;; diff --git a/scripts/qom-cast-macro-clean-cocci-gen.py b/scripts/qom-cast-macro-clean-cocci-gen.py new file mode 100644 index 0000000000..2fa8438a14 --- /dev/null +++ b/scripts/qom-cast-macro-clean-cocci-gen.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# +# Generate a Coccinelle semantic patch to remove pointless QOM cast. +# +# Usage: +# +# $ qom-cast-macro-clean-cocci-gen.py $(git ls-files) > qom_pointless_cast.cocci +# $ spatch \ +# --macro-file scripts/cocci-macro-file.h \ +# --sp-file qom_pointless_cast.cocci \ +# --keep-comments \ +# --use-gitgrep \ +# --in-place \ +# --dir . +# +# SPDX-FileContributor: Philippe Mathieu-Daudé +# SPDX-FileCopyrightText: 2023 Linaro Ltd. +# SPDX-License-Identifier: GPL-2.0-or-later + +import re +import sys + +assert len(sys.argv) > 0 + +def print_cocci_rule(qom_typedef, qom_cast_macro): + print(f'''@@ +typedef {qom_typedef}; +{qom_typedef} *obj; +@@ +- {qom_cast_macro}(obj) ++ obj +''') + +patterns = [ + r'DECLARE_INSTANCE_CHECKER\((\w+),\W*(\w+),\W*TYPE_\w+\)', + r'DECLARE_OBJ_CHECKERS\((\w+),\W*\w+,\W*(\w+),\W*TYPE_\w+\)', + r'OBJECT_DECLARE_TYPE\((\w+),\W*\w+,\W*(\w+)\)', + r'OBJECT_DECLARE_SIMPLE_TYPE\((\w+),\W*(\w+)\)', + r'INTERFACE_CHECK\((\w+),\W*\(\w+\),\W*TYPE_(\w+)\)', +] + +for fn in sys.argv[1:]: + try: + content = open(fn, 'rt').read() + except: + continue + for pattern in patterns: + for match in re.findall(pattern, content): + print_cocci_rule(match[0], match[1]) diff --git a/scripts/render_block_graph.py b/scripts/render_block_graph.py index 8f731a5cfe..3e1a2e3fa7 100755 --- a/scripts/render_block_graph.py +++ b/scripts/render_block_graph.py @@ -43,13 +43,13 @@ def render_block_graph(qmp, filename, format='png'): representation in @format into "@filename.@format" ''' - bds_nodes = qmp.command('query-named-block-nodes') + bds_nodes = qmp.cmd('query-named-block-nodes') bds_nodes = {n['node-name']: n for n in bds_nodes} - job_nodes = qmp.command('query-block-jobs') + job_nodes = qmp.cmd('query-block-jobs') job_nodes = {n['device']: n for n in job_nodes} - block_graph = qmp.command('x-debug-query-block-graph') + block_graph = qmp.cmd('x-debug-query-block-graph') graph = Digraph(comment='Block Nodes Graph') graph.format = format @@ -94,7 +94,7 @@ class LibvirtGuest(): def __init__(self, name): self.name = name - def command(self, cmd): + def cmd(self, cmd): # only supports qmp commands without parameters m = {'execute': cmd} ar = ['virsh', 'qemu-monitor-command', self.name, json.dumps(m)] diff --git a/scripts/replay-dump.py b/scripts/replay-dump.py index 3ba97a6d30..d668193e79 100755 --- a/scripts/replay-dump.py +++ b/scripts/replay-dump.py @@ -21,6 +21,7 @@ import argparse import struct from collections import namedtuple +from os import path # This mirrors some of the global replay state which some of the # stream loading refers to. Some decoders may read the next event so @@ -82,6 +83,12 @@ def read_qword(fin): "Read a 64 bit word" return struct.unpack('>Q', fin.read(8))[0] +def read_array(fin): + "Read a sized array" + size = read_dword(fin) + data = fin.read(size) + return data + # Generic decoder structure Decoder = namedtuple("Decoder", "eid name fn") @@ -111,10 +118,15 @@ def print_event(eid, name, string=None, event_count=None): # Decoders for each event type def decode_unimp(eid, name, _unused_dumpfile): - "Unimplimented decoder, will trigger exit" + "Unimplemented decoder, will trigger exit" print("%s not handled - will now stop" % (name)) return False +def decode_plain(eid, name, _unused_dumpfile): + "Plain events without additional data" + print_event(eid, name, "no data") + return True + # Checkpoint decoder def swallow_async_qword(eid, name, dumpfile): "Swallow a qword of data without looking at it" @@ -145,10 +157,19 @@ def decode_async(eid, name, dumpfile): return call_decode(async_decode_table, async_event_kind, dumpfile) +total_insns = 0 def decode_instruction(eid, name, dumpfile): + global total_insns ins_diff = read_dword(dumpfile) - print_event(eid, name, "0x%x" % (ins_diff)) + total_insns += ins_diff + print_event(eid, name, "+ %d -> %d" % (ins_diff, total_insns)) + return True + +def decode_char_write(eid, name, dumpfile): + res = read_dword(dumpfile) + offset = read_dword(dumpfile) + print_event(eid, name, "%d -> %d" % (offset, res)) return True def decode_audio_out(eid, name, dumpfile): @@ -189,14 +210,19 @@ def decode_clock(eid, name, dumpfile): print_event(eid, name, "0x%x" % (clock_data)) return True +def decode_random(eid, name, dumpfile): + ret = read_dword(dumpfile) + data = read_array(dumpfile) + print_event(eid, "%d bytes of random data" % len(data)) + return True # pre-MTTCG merge v5_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), Decoder(1, "EVENT_INTERRUPT", decode_interrupt), - Decoder(2, "EVENT_EXCEPTION", decode_unimp), + Decoder(2, "EVENT_EXCEPTION", decode_plain), Decoder(3, "EVENT_ASYNC", decode_async), Decoder(4, "EVENT_SHUTDOWN", decode_unimp), - Decoder(5, "EVENT_CHAR_WRITE", decode_unimp), + Decoder(5, "EVENT_CHAR_WRITE", decode_char_write), Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp), Decoder(7, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), Decoder(8, "EVENT_CLOCK_HOST", decode_clock), @@ -215,10 +241,10 @@ v5_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), # post-MTTCG merge, AUDIO support added v6_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), Decoder(1, "EVENT_INTERRUPT", decode_interrupt), - Decoder(2, "EVENT_EXCEPTION", decode_unimp), + Decoder(2, "EVENT_EXCEPTION", decode_plain), Decoder(3, "EVENT_ASYNC", decode_async), Decoder(4, "EVENT_SHUTDOWN", decode_unimp), - Decoder(5, "EVENT_CHAR_WRITE", decode_unimp), + Decoder(5, "EVENT_CHAR_WRITE", decode_char_write), Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp), Decoder(7, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), Decoder(8, "EVENT_AUDIO_OUT", decode_audio_out), @@ -250,7 +276,7 @@ v7_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), Decoder(10, "EVENT_SHUTDOWN_GUEST_RESET", decode_unimp), Decoder(11, "EVENT_SHUTDOWN_GUEST_PANIC", decode_unimp), Decoder(12, "EVENT_SHUTDOWN___MAX", decode_unimp), - Decoder(13, "EVENT_CHAR_WRITE", decode_unimp), + Decoder(13, "EVENT_CHAR_WRITE", decode_char_write), Decoder(14, "EVENT_CHAR_READ_ALL", decode_unimp), Decoder(15, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), Decoder(16, "EVENT_AUDIO_OUT", decode_audio_out), @@ -268,6 +294,48 @@ v7_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), Decoder(28, "EVENT_CP_RESET", decode_checkpoint), ] +v12_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), + Decoder(1, "EVENT_INTERRUPT", decode_interrupt), + Decoder(2, "EVENT_EXCEPTION", decode_plain), + Decoder(3, "EVENT_ASYNC", decode_async), + Decoder(4, "EVENT_ASYNC", decode_async), + Decoder(5, "EVENT_ASYNC", decode_async), + Decoder(6, "EVENT_ASYNC", decode_async), + Decoder(6, "EVENT_ASYNC", decode_async), + Decoder(8, "EVENT_ASYNC", decode_async), + Decoder(9, "EVENT_ASYNC", decode_async), + Decoder(10, "EVENT_ASYNC", decode_async), + Decoder(11, "EVENT_SHUTDOWN", decode_unimp), + Decoder(12, "EVENT_SHUTDOWN_HOST_ERR", decode_unimp), + Decoder(13, "EVENT_SHUTDOWN_HOST_QMP_QUIT", decode_unimp), + Decoder(14, "EVENT_SHUTDOWN_HOST_QMP_RESET", decode_unimp), + Decoder(14, "EVENT_SHUTDOWN_HOST_SIGNAL", decode_unimp), + Decoder(15, "EVENT_SHUTDOWN_HOST_UI", decode_unimp), + Decoder(16, "EVENT_SHUTDOWN_GUEST_SHUTDOWN", decode_unimp), + Decoder(17, "EVENT_SHUTDOWN_GUEST_RESET", decode_unimp), + Decoder(18, "EVENT_SHUTDOWN_GUEST_PANIC", decode_unimp), + Decoder(19, "EVENT_SHUTDOWN_GUEST_SUBSYSTEM_RESET", decode_unimp), + Decoder(20, "EVENT_SHUTDOWN_GUEST_SNAPSHOT_LOAD", decode_unimp), + Decoder(21, "EVENT_SHUTDOWN___MAX", decode_unimp), + Decoder(22, "EVENT_CHAR_WRITE", decode_char_write), + Decoder(23, "EVENT_CHAR_READ_ALL", decode_unimp), + Decoder(24, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), + Decoder(25, "EVENT_AUDIO_IN", decode_unimp), + Decoder(26, "EVENT_AUDIO_OUT", decode_audio_out), + Decoder(27, "EVENT_RANDOM", decode_random), + Decoder(28, "EVENT_CLOCK_HOST", decode_clock), + Decoder(29, "EVENT_CLOCK_VIRTUAL_RT", decode_clock), + Decoder(30, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint), + Decoder(31, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint), + Decoder(32, "EVENT_CP_RESET_REQUESTED", decode_checkpoint), + Decoder(33, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint), + Decoder(34, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint), + Decoder(35, "EVENT_CP_CLOCK_HOST", decode_checkpoint), + Decoder(36, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint), + Decoder(37, "EVENT_CP_INIT", decode_checkpoint_init), + Decoder(38, "EVENT_CP_RESET", decode_checkpoint), +] + def parse_arguments(): "Grab arguments for script" parser = argparse.ArgumentParser() @@ -278,14 +346,18 @@ def parse_arguments(): def decode_file(filename): "Decode a record/replay dump" dumpfile = open(filename, "rb") - + dumpsize = path.getsize(filename) # read and throwaway the header version = read_dword(dumpfile) junk = read_qword(dumpfile) + # see REPLAY_VERSION print("HEADER: version 0x%x" % (version)) - if version == 0xe02007: + if version == 0xe0200c: + event_decode_table = v12_event_table + replay_state.checkpoint_start = 30 + elif version == 0xe02007: event_decode_table = v7_event_table replay_state.checkpoint_start = 12 elif version == 0xe02006: @@ -299,8 +371,13 @@ def decode_file(filename): decode_ok = True while decode_ok: event = read_event(dumpfile) - decode_ok = call_decode(event_decode_table, event, dumpfile) + decode_ok = call_decode(event_decode_table, event, + dumpfile) + except Exception as inst: + print(f"error {inst}") + finally: + print(f"Reached {dumpfile.tell()} of {dumpsize} bytes") dumpfile.close() if __name__ == "__main__": diff --git a/scripts/shaderinclude.pl b/scripts/shaderinclude.pl deleted file mode 100644 index cd3bb40b12..0000000000 --- a/scripts/shaderinclude.pl +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; - -my $file = shift; -open FILE, "<", $file or die "open $file: $!"; -my $name = $file; -$name =~ s|.*/||; -$name =~ s/[-.]/_/g; -print "static GLchar ${name}_src[] =\n"; -while () { - chomp; - printf " \"%s\\n\"\n", $_; -} -print " \"\\n\";\n"; -close FILE; diff --git a/scripts/shaderinclude.py b/scripts/shaderinclude.py new file mode 100644 index 0000000000..ab2aade2cd --- /dev/null +++ b/scripts/shaderinclude.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 Red Hat, Inc. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import sys +import os + + +def main(args): + file_path = args[1] + basename = os.path.basename(file_path) + varname = basename.replace('-', '_').replace('.', '_') + + with os.fdopen(sys.stdout.fileno(), "wt", closefd=False, newline='\n') as stdout: + with open(file_path, "r", encoding='utf-8') as file: + print(f'static GLchar {varname}_src[] =', file=stdout) + for line in file: + line = line.rstrip() + print(f' "{line}\\n"', file=stdout) + print(' "\\n";', file=stdout) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/scripts/simplebench/bench_block_job.py b/scripts/simplebench/bench_block_job.py index 56191db44b..e575a3af10 100755 --- a/scripts/simplebench/bench_block_job.py +++ b/scripts/simplebench/bench_block_job.py @@ -39,7 +39,7 @@ def bench_block_job(cmd, cmd_args, qemu_args): binary Returns {'seconds': int} on success and {'error': str} on failure, dict may - contain addional 'vm-log' field. Return value is compatible with + contain additional 'vm-log' field. Return value is compatible with simplebench lib. """ diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py index 1f6d1ae1f3..cef81b0707 100755 --- a/scripts/simpletrace.py +++ b/scripts/simpletrace.py @@ -9,11 +9,20 @@ # # For help see docs/devel/tracing.rst +import sys import struct import inspect +import warnings from tracetool import read_events, Event from tracetool.backend.simple import is_string +__all__ = ['Analyzer', 'Analyzer2', 'process', 'run'] + +# This is the binary format that the QEMU "simple" trace backend +# emits. There is no specification documentation because the format is +# not guaranteed to be stable. Trace files must be parsed with the +# same trace-events-all file and the same simpletrace.py file that +# QEMU was built with. header_event_id = 0xffffffffffffffff header_magic = 0xf2b177cb0aa429b4 dropped_event_id = 0xfffffffffffffffe @@ -23,48 +32,19 @@ record_type_event = 1 log_header_fmt = '=QQQ' rec_header_fmt = '=QQII' +rec_header_fmt_len = struct.calcsize(rec_header_fmt) + +class SimpleException(Exception): + pass def read_header(fobj, hfmt): '''Read a trace record header''' hlen = struct.calcsize(hfmt) hdr = fobj.read(hlen) if len(hdr) != hlen: - return None + raise SimpleException('Error reading header. Wrong filetype provided?') return struct.unpack(hfmt, hdr) -def get_record(edict, idtoname, rechdr, fobj): - """Deserialize a trace record from a file into a tuple - (name, timestamp, pid, arg1, ..., arg6).""" - if rechdr is None: - return None - if rechdr[0] != dropped_event_id: - event_id = rechdr[0] - name = idtoname[event_id] - rec = (name, rechdr[1], rechdr[3]) - try: - event = edict[name] - except KeyError as e: - import sys - sys.stderr.write('%s event is logged but is not declared ' \ - 'in the trace events file, try using ' \ - 'trace-events-all instead.\n' % str(e)) - sys.exit(1) - - for type, name in event.args: - if is_string(type): - l = fobj.read(4) - (len,) = struct.unpack('=L', l) - s = fobj.read(len) - rec = rec + (s,) - else: - (value,) = struct.unpack('=Q', fobj.read(8)) - rec = rec + (value,) - else: - rec = ("dropped", rechdr[1], rechdr[3]) - (value,) = struct.unpack('=Q', fobj.read(8)) - rec = rec + (value,) - return rec - def get_mapping(fobj): (event_id, ) = struct.unpack('=Q', fobj.read(8)) (len, ) = struct.unpack('=L', fobj.read(4)) @@ -72,41 +52,47 @@ def get_mapping(fobj): return (event_id, name) -def read_record(edict, idtoname, fobj): - """Deserialize a trace record from a file into a tuple (event_num, timestamp, pid, arg1, ..., arg6).""" - rechdr = read_header(fobj, rec_header_fmt) - return get_record(edict, idtoname, rechdr, fobj) +def read_record(fobj): + """Deserialize a trace record from a file into a tuple (event_num, timestamp, pid, args).""" + event_id, timestamp_ns, record_length, record_pid = read_header(fobj, rec_header_fmt) + args_payload = fobj.read(record_length - rec_header_fmt_len) + return (event_id, timestamp_ns, record_pid, args_payload) def read_trace_header(fobj): """Read and verify trace file header""" - header = read_header(fobj, log_header_fmt) - if header is None: - raise ValueError('Not a valid trace file!') - if header[0] != header_event_id: - raise ValueError('Not a valid trace file, header id %d != %d' % - (header[0], header_event_id)) - if header[1] != header_magic: - raise ValueError('Not a valid trace file, header magic %d != %d' % - (header[1], header_magic)) + _header_event_id, _header_magic, log_version = read_header(fobj, log_header_fmt) + if _header_event_id != header_event_id: + raise ValueError(f'Not a valid trace file, header id {_header_event_id} != {header_event_id}') + if _header_magic != header_magic: + raise ValueError(f'Not a valid trace file, header magic {_header_magic} != {header_magic}') - log_version = header[2] if log_version not in [0, 2, 3, 4]: - raise ValueError('Unknown version of tracelog format!') + raise ValueError(f'Unknown version {log_version} of tracelog format!') if log_version != 4: - raise ValueError('Log format %d not supported with this QEMU release!' - % log_version) + raise ValueError(f'Log format {log_version} not supported with this QEMU release!') -def read_trace_records(edict, idtoname, fobj): - """Deserialize trace records from a file, yielding record tuples (event_num, timestamp, pid, arg1, ..., arg6). - - Note that `idtoname` is modified if the file contains mapping records. +def read_trace_records(events, fobj, read_header): + """Deserialize trace records from a file, yielding record tuples (event, event_num, timestamp, pid, arg1, ..., arg6). Args: - edict (str -> Event): events dict, indexed by name - idtoname (int -> str): event names dict, indexed by event ID + event_mapping (str -> Event): events dict, indexed by name fobj (file): input file + read_header (bool): whether headers were read from fobj """ + frameinfo = inspect.getframeinfo(inspect.currentframe()) + dropped_event = Event.build("Dropped_Event(uint64_t num_events_dropped)", + frameinfo.lineno + 1, frameinfo.filename) + + event_mapping = {e.name: e for e in events} + event_mapping["dropped"] = dropped_event + event_id_to_name = {dropped_event_id: "dropped"} + + # If there is no header assume event ID mapping matches events list + if not read_header: + for event_id, event in enumerate(events): + event_id_to_name[event_id] = event.name + while True: t = fobj.read(8) if len(t) == 0: @@ -114,19 +100,45 @@ def read_trace_records(edict, idtoname, fobj): (rectype, ) = struct.unpack('=Q', t) if rectype == record_type_mapping: - event_id, name = get_mapping(fobj) - idtoname[event_id] = name + event_id, event_name = get_mapping(fobj) + event_id_to_name[event_id] = event_name else: - rec = read_record(edict, idtoname, fobj) + event_id, timestamp_ns, pid, args_payload = read_record(fobj) + event_name = event_id_to_name[event_id] - yield rec + try: + event = event_mapping[event_name] + except KeyError as e: + raise SimpleException( + f'{e} event is logged but is not declared in the trace events' + 'file, try using trace-events-all instead.' + ) -class Analyzer(object): - """A trace file analyzer which processes trace records. + offset = 0 + args = [] + for type, _ in event.args: + if is_string(type): + (length,) = struct.unpack_from('=L', args_payload, offset=offset) + offset += 4 + s = args_payload[offset:offset+length] + offset += length + args.append(s) + else: + (value,) = struct.unpack_from('=Q', args_payload, offset=offset) + offset += 8 + args.append(value) + + yield (event_mapping[event_name], event_name, timestamp_ns, pid) + tuple(args) + +class Analyzer: + """[Deprecated. Refer to Analyzer2 instead.] + + A trace file analyzer which processes trace records. An analyzer can be passed to run() or process(). The begin() method is invoked, then each trace record is processed, and finally the end() method - is invoked. + is invoked. When Analyzer is used as a context-manager (using the `with` + statement), begin() and end() are called automatically. If a method matching a trace event name exists, it is invoked to process that trace record. Otherwise the catchall() method is invoked. @@ -160,44 +172,14 @@ class Analyzer(object): """Called if no specific method for processing a trace event has been found.""" pass - def end(self): - """Called at the end of the trace.""" - pass - -def process(events, log, analyzer, read_header=True): - """Invoke an analyzer on each event in a log.""" - if isinstance(events, str): - events = read_events(open(events, 'r'), events) - if isinstance(log, str): - log = open(log, 'rb') - - if read_header: - read_trace_header(log) - - frameinfo = inspect.getframeinfo(inspect.currentframe()) - dropped_event = Event.build("Dropped_Event(uint64_t num_events_dropped)", - frameinfo.lineno + 1, frameinfo.filename) - edict = {"dropped": dropped_event} - idtoname = {dropped_event_id: "dropped"} - - for event in events: - edict[event.name] = event - - # If there is no header assume event ID mapping matches events list - if not read_header: - for event_id, event in enumerate(events): - idtoname[event_id] = event.name - - def build_fn(analyzer, event): - if isinstance(event, str): - return analyzer.catchall - - fn = getattr(analyzer, event.name, None) + def _build_fn(self, event): + fn = getattr(self, event.name, None) if fn is None: - return analyzer.catchall + # Return early to avoid costly call to inspect.getfullargspec + return self.catchall event_argcount = len(event.args) - fn_argcount = len(inspect.getargspec(fn)[0]) - 1 + fn_argcount = len(inspect.getfullargspec(fn)[0]) - 1 if fn_argcount == event_argcount + 1: # Include timestamp as first argument return lambda _, rec: fn(*(rec[1:2] + rec[3:3 + event_argcount])) @@ -208,56 +190,170 @@ def process(events, log, analyzer, read_header=True): # Just arguments, no timestamp or pid return lambda _, rec: fn(*rec[3:3 + event_argcount]) - analyzer.begin() - fn_cache = {} - for rec in read_trace_records(edict, idtoname, log): - event_num = rec[0] - event = edict[event_num] - if event_num not in fn_cache: - fn_cache[event_num] = build_fn(analyzer, event) - fn_cache[event_num](event, rec) - analyzer.end() + def _process_event(self, rec_args, *, event, event_id, timestamp_ns, pid, **kwargs): + warnings.warn( + "Use of deprecated Analyzer class. Refer to Analyzer2 instead.", + DeprecationWarning, + ) + + if not hasattr(self, '_fn_cache'): + # NOTE: Cannot depend on downstream subclasses to have + # super().__init__() because of legacy. + self._fn_cache = {} + + rec = (event_id, timestamp_ns, pid, *rec_args) + if event_id not in self._fn_cache: + self._fn_cache[event_id] = self._build_fn(event) + self._fn_cache[event_id](event, rec) + + def end(self): + """Called at the end of the trace.""" + pass + + def __enter__(self): + self.begin() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if exc_type is None: + self.end() + return False + +class Analyzer2(Analyzer): + """A trace file analyzer which processes trace records. + + An analyzer can be passed to run() or process(). The begin() method is + invoked, then each trace record is processed, and finally the end() method + is invoked. When Analyzer is used as a context-manager (using the `with` + statement), begin() and end() are called automatically. + + If a method matching a trace event name exists, it is invoked to process + that trace record. Otherwise the catchall() method is invoked. + + The methods are called with a set of keyword-arguments. These can be ignored + using `**kwargs` or defined like any keyword-argument. + + The following keyword-arguments are available, but make sure to have an + **kwargs to allow for unmatched arguments in the future: + event: Event object of current trace + event_id: The id of the event in the current trace file + timestamp_ns: The timestamp in nanoseconds of the trace + pid: The process id recorded for the given trace + + Example: + The following method handles the runstate_set(int new_state) trace event:: + + def runstate_set(self, new_state, **kwargs): + ... + + The method can also explicitly take a timestamp keyword-argument with the + trace event arguments:: + + def runstate_set(self, new_state, *, timestamp_ns, **kwargs): + ... + + Timestamps have the uint64_t type and are in nanoseconds. + + The pid can be included in addition to the timestamp and is useful when + dealing with traces from multiple processes: + + def runstate_set(self, new_state, *, timestamp_ns, pid, **kwargs): + ... + """ + + def catchall(self, *rec_args, event, timestamp_ns, pid, event_id, **kwargs): + """Called if no specific method for processing a trace event has been found.""" + pass + + def _process_event(self, rec_args, *, event, **kwargs): + fn = getattr(self, event.name, self.catchall) + fn(*rec_args, event=event, **kwargs) + +def process(events, log, analyzer, read_header=True): + """Invoke an analyzer on each event in a log. + Args: + events (file-object or list or str): events list or file-like object or file path as str to read event data from + log (file-object or str): file-like object or file path as str to read log data from + analyzer (Analyzer): Instance of Analyzer to interpret the event data + read_header (bool, optional): Whether to read header data from the log data. Defaults to True. + """ + + if isinstance(events, str): + with open(events, 'r') as f: + events_list = read_events(f, events) + elif isinstance(events, list): + # Treat as a list of events already produced by tracetool.read_events + events_list = events + else: + # Treat as an already opened file-object + events_list = read_events(events, events.name) + + if isinstance(log, str): + with open(log, 'rb') as log_fobj: + _process(events_list, log_fobj, analyzer, read_header) + else: + # Treat `log` as an already opened file-object. We will not close it, + # as we do not own it. + _process(events_list, log, analyzer, read_header) + +def _process(events, log_fobj, analyzer, read_header=True): + """Internal function for processing + + Args: + events (list): list of events already produced by tracetool.read_events + log_fobj (file): file-object to read log data from + analyzer (Analyzer): the Analyzer to interpret the event data + read_header (bool, optional): Whether to read header data from the log data. Defaults to True. + """ + + if read_header: + read_trace_header(log_fobj) + + with analyzer: + for event, event_id, timestamp_ns, record_pid, *rec_args in read_trace_records(events, log_fobj, read_header): + analyzer._process_event( + rec_args, + event=event, + event_id=event_id, + timestamp_ns=timestamp_ns, + pid=record_pid, + ) def run(analyzer): """Execute an analyzer on a trace file given on the command-line. This function is useful as a driver for simple analysis scripts. More advanced scripts will want to call process() instead.""" - import sys - read_header = True - if len(sys.argv) == 4 and sys.argv[1] == '--no-header': - read_header = False - del sys.argv[1] - elif len(sys.argv) != 3: - sys.stderr.write('usage: %s [--no-header] ' \ - '\n' % sys.argv[0]) - sys.exit(1) + try: + # NOTE: See built-in `argparse` module for a more robust cli interface + *no_header, trace_event_path, trace_file_path = sys.argv[1:] + assert no_header == [] or no_header == ['--no-header'], 'Invalid no-header argument' + except (AssertionError, ValueError): + raise SimpleException(f'usage: {sys.argv[0]} [--no-header] \n') - events = read_events(open(sys.argv[1], 'r'), sys.argv[1]) - process(events, sys.argv[2], analyzer, read_header=read_header) + with open(trace_event_path, 'r') as events_fobj, open(trace_file_path, 'rb') as log_fobj: + process(events_fobj, log_fobj, analyzer, read_header=not no_header) if __name__ == '__main__': - class Formatter(Analyzer): + class Formatter2(Analyzer2): def __init__(self): - self.last_timestamp = None + self.last_timestamp_ns = None - def catchall(self, event, rec): - timestamp = rec[1] - if self.last_timestamp is None: - self.last_timestamp = timestamp - delta_ns = timestamp - self.last_timestamp - self.last_timestamp = timestamp + def catchall(self, *rec_args, event, timestamp_ns, pid, event_id): + if self.last_timestamp_ns is None: + self.last_timestamp_ns = timestamp_ns + delta_ns = timestamp_ns - self.last_timestamp_ns + self.last_timestamp_ns = timestamp_ns - fields = [event.name, '%0.3f' % (delta_ns / 1000.0), - 'pid=%d' % rec[2]] - i = 3 - for type, name in event.args: - if is_string(type): - fields.append('%s=%s' % (name, rec[i])) - else: - fields.append('%s=0x%x' % (name, rec[i])) - i += 1 - print(' '.join(fields)) + fields = [ + f'{name}={r}' if is_string(type) else f'{name}=0x{r:x}' + for r, (type, name) in zip(rec_args, event.args) + ] + print(f'{event.name} {delta_ns / 1000:0.3f} {pid=} ' + ' '.join(fields)) - run(Formatter()) + try: + run(Formatter2()) + except SimpleException as e: + sys.stderr.write(str(e) + "\n") + sys.exit(1) diff --git a/scripts/symlink-install-tree.py b/scripts/symlink-install-tree.py new file mode 100644 index 0000000000..8ed97e3c94 --- /dev/null +++ b/scripts/symlink-install-tree.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +from pathlib import PurePath +import errno +import json +import os +import subprocess +import sys + +def destdir_join(d1: str, d2: str) -> str: + if not d1: + return d2 + # c:\destdir + c:\prefix must produce c:\destdir\prefix + return str(PurePath(d1, *PurePath(d2).parts[1:])) + +introspect = os.environ.get('MESONINTROSPECT') +out = subprocess.run([*introspect.split(' '), '--installed'], + stdout=subprocess.PIPE, check=True).stdout +for source, dest in json.loads(out).items(): + bundle_dest = destdir_join('qemu-bundle', dest) + path = os.path.dirname(bundle_dest) + try: + os.makedirs(path, exist_ok=True) + except BaseException as e: + print(f'error making directory {path}', file=sys.stderr) + raise e + try: + os.symlink(source, bundle_dest) + except BaseException as e: + if not isinstance(e, OSError) or e.errno != errno.EEXIST: + if os.name == 'nt': + print('Please enable Developer Mode to support soft link ' + 'without Administrator permission') + print(f'error making symbolic link {dest}', file=sys.stderr) + raise e diff --git a/scripts/test-driver.py b/scripts/test-driver.py deleted file mode 100644 index eef74b29a8..0000000000 --- a/scripts/test-driver.py +++ /dev/null @@ -1,35 +0,0 @@ -#! /usr/bin/env python3 - -# Wrapper for tests that hides the output if they succeed. -# Used by "make check" -# -# Copyright (C) 2020 Red Hat, Inc. -# -# Author: Paolo Bonzini - -import subprocess -import sys -import os -import argparse - -parser = argparse.ArgumentParser(description='Test driver for QEMU') -parser.add_argument('-C', metavar='DIR', dest='dir', default='.', - help='change to DIR before doing anything else') -parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', - help='be more verbose') -parser.add_argument('test_args', nargs=argparse.REMAINDER) - -args = parser.parse_args() -os.chdir(args.dir) - -test_args = args.test_args -if test_args[0] == '--': - test_args = test_args[1:] - -if args.verbose: - result = subprocess.run(test_args, stdout=None, stderr=None) -else: - result = subprocess.run(test_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - if result.returncode: - sys.stdout.buffer.write(result.stdout) -sys.exit(result.returncode) diff --git a/scripts/tracetool.py b/scripts/tracetool.py index ab7653a5ce..5de9ce96d3 100755 --- a/scripts/tracetool.py +++ b/scripts/tracetool.py @@ -44,12 +44,9 @@ Options: --help This help message. --list-backends Print list of available backends. --check-backends Check if the given backend is valid. - --binary Full path to QEMU binary. - --target-type QEMU emulator target type ('system' or 'user'). - --target-name QEMU emulator target name. - --group Name of the event group - --probe-prefix Prefix for dtrace probe names - (default: qemu--).\ + --binary Full path to QEMU binary (required for 'stap' backend). + --group Name of the event group. + --probe-prefix Prefix for dtrace probe names (required for 'stap' backend). """ % { "script" : _SCRIPT, "backends" : backend_descr, @@ -67,7 +64,7 @@ def main(args): long_opts = ["backends=", "format=", "help", "list-backends", "check-backends", "group="] - long_opts += ["binary=", "target-type=", "target-name=", "probe-prefix="] + long_opts += ["binary=", "probe-prefix="] try: opts, args = getopt.getopt(args[1:], "", long_opts) @@ -79,8 +76,6 @@ def main(args): arg_format = "" arg_group = None binary = None - target_type = None - target_name = None probe_prefix = None for opt, arg in opts: if opt == "--help": @@ -102,10 +97,6 @@ def main(args): elif opt == "--binary": binary = arg - elif opt == '--target-type': - target_type = arg - elif opt == '--target-name': - target_name = arg elif opt == '--probe-prefix': probe_prefix = arg @@ -127,13 +118,8 @@ def main(args): if arg_format == "stap": if binary is None: error_opt("--binary is required for SystemTAP tapset generator") - if probe_prefix is None and target_type is None: - error_opt("--target-type is required for SystemTAP tapset generator") - if probe_prefix is None and target_name is None: - error_opt("--target-name is required for SystemTAP tapset generator") - if probe_prefix is None: - probe_prefix = ".".join(["qemu", target_type, target_name]) + error_opt("--probe-prefix is required for SystemTAP tapset generator") if len(args) < 2: error_opt("missing trace-events and output filepaths") diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 5393c7fc5c..b887540a55 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -18,7 +18,6 @@ import weakref import tracetool.format import tracetool.backend -import tracetool.transform def error_write(*lines): @@ -92,7 +91,7 @@ ALLOWED_TYPES = [ def validate_type(name): bits = name.split(" ") for bit in bits: - bit = re.sub("\*", "", bit) + bit = re.sub(r"\*", "", bit) if bit == "": continue if bit == "const": @@ -190,18 +189,6 @@ class Arguments: """List of argument names casted to their type.""" return ["(%s)%s" % (type_, name) for type_, name in self._args] - def transform(self, *trans): - """Return a new Arguments instance with transformed types. - - The types in the resulting Arguments instance are transformed according - to tracetool.transform.transform_type. - """ - res = [] - for type_, name in self._args: - res.append((tracetool.transform.transform_type(type_, *trans), - name)) - return Arguments(res) - class Event(object): """Event description. @@ -223,12 +210,12 @@ class Event(object): """ - _CRE = re.compile("((?P[\w\s]+)\s+)?" - "(?P\w+)" - "\((?P[^)]*)\)" - "\s*" - "(?:(?:(?P\".+),)?\s*(?P\".+))?" - "\s*") + _CRE = re.compile(r"((?P[\w\s]+)\s+)?" + r"(?P\w+)" + r"\((?P[^)]*)\)" + r"\s*" + r"(?:(?:(?P\".+),)?\s*(?P\".+))?" + r"\s*") _VALID_PROPS = set(["disable", "vcpu"]) @@ -339,7 +326,7 @@ class Event(object): fmt) # Star matching on PRI is dangerous as one might have multiple # arguments with that format, hence the non-greedy version of it. - _FMT = re.compile("(%[\d\.]*\w+|%.*?PRI\S+)") + _FMT = re.compile(r"(%[\d\.]*\w+|%.*?PRI\S+)") def formats(self): """List conversion specifiers in the argument print format string.""" @@ -358,16 +345,6 @@ class Event(object): fmt = Event.QEMU_TRACE return fmt % {"name": self.name, "NAME": self.name.upper()} - def transform(self, *trans): - """Return a new Event with transformed Arguments.""" - return Event(self.name, - list(self.properties), - self.fmt, - self.args.transform(*trans), - self.lineno, - self.filename, - self) - def read_events(fobj, fname): """Generate the output for the given (format, backends) pair. diff --git a/scripts/tracetool/backend/ftrace.py b/scripts/tracetool/backend/ftrace.py index 5fa30ccc08..baed2ae61c 100644 --- a/scripts/tracetool/backend/ftrace.py +++ b/scripts/tracetool/backend/ftrace.py @@ -12,6 +12,8 @@ __maintainer__ = "Stefan Hajnoczi" __email__ = "stefanha@redhat.com" +import os.path + from tracetool import out @@ -45,7 +47,7 @@ def generate_h(event, group): args=event.args, event_id="TRACE_" + event.name.upper(), event_lineno=event.lineno, - event_filename=event.filename, + event_filename=os.path.relpath(event.filename), fmt=event.fmt.rstrip("\n"), argnames=argnames) diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index 17ba1cd90e..de27b7e62e 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -12,6 +12,8 @@ __maintainer__ = "Stefan Hajnoczi" __email__ = "stefanha@redhat.com" +import os.path + from tracetool import out @@ -53,7 +55,7 @@ def generate_h(event, group): ' }', cond=cond, event_lineno=event.lineno, - event_filename=event.filename, + event_filename=os.path.relpath(event.filename), name=event.name, fmt=event.fmt.rstrip("\n"), argnames=argnames) diff --git a/scripts/tracetool/backend/syslog.py b/scripts/tracetool/backend/syslog.py index 5a3a00fe31..012970f6cc 100644 --- a/scripts/tracetool/backend/syslog.py +++ b/scripts/tracetool/backend/syslog.py @@ -12,6 +12,8 @@ __maintainer__ = "Stefan Hajnoczi" __email__ = "stefanha@redhat.com" +import os.path + from tracetool import out @@ -41,7 +43,7 @@ def generate_h(event, group): ' }', cond=cond, event_lineno=event.lineno, - event_filename=event.filename, + event_filename=os.path.relpath(event.filename), name=event.name, fmt=event.fmt.rstrip("\n"), argnames=argnames) diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py index c390c1844a..69edf0d588 100644 --- a/scripts/tracetool/format/c.py +++ b/scripts/tracetool/format/c.py @@ -32,19 +32,13 @@ def generate(events, backend, group): out('uint16_t %s;' % e.api(e.QEMU_DSTATE)) for e in events: - if "vcpu" in e.properties: - vcpu_id = 0 - else: - vcpu_id = "TRACE_VCPU_EVENT_NONE" out('TraceEvent %(event)s = {', ' .id = 0,', - ' .vcpu_id = %(vcpu_id)s,', ' .name = \"%(name)s\",', ' .sstate = %(sstate)s,', ' .dstate = &%(dstate)s ', '};', event = e.api(e.QEMU_EVENT), - vcpu_id = vcpu_id, name = e.name, sstate = "TRACE_%s_ENABLED" % e.name.upper(), dstate = e.api(e.QEMU_DSTATE)) diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py index e94f0be7da..ea126b07ea 100644 --- a/scripts/tracetool/format/h.py +++ b/scripts/tracetool/format/h.py @@ -16,10 +16,7 @@ from tracetool import out def generate(events, backend, group): - if group == "root": - header = "trace/control-vcpu.h" - else: - header = "trace/control.h" + header = "trace/control.h" out('/* This file is autogenerated by tracetool, do not edit. */', '', @@ -74,16 +71,7 @@ def generate(events, backend, group): out('}') - # tracer wrapper with checks (per-vCPU tracing) - if "vcpu" in e.properties: - trace_cpu = next(iter(e.args))[1] - cond = "trace_event_get_vcpu_state(%(cpu)s,"\ - " TRACE_%(id)s)"\ - % dict( - cpu=trace_cpu, - id=e.name.upper()) - else: - cond = "true" + cond = "true" out('', 'static inline void %(api)s(%(args)s)', diff --git a/scripts/tracetool/format/log_stap.py b/scripts/tracetool/format/log_stap.py index 0b6549d534..b49afababd 100644 --- a/scripts/tracetool/format/log_stap.py +++ b/scripts/tracetool/format/log_stap.py @@ -83,7 +83,7 @@ def c_fmt_to_stap(fmt): # and "%ll" is not valid at all. Similarly the size_t # based "%z" size qualifier is not valid. We just # strip all size qualifiers for sanity. - fmt = re.sub("%(\d*)(l+|z)(x|u|d)", "%\\1\\3", "".join(bits)) + fmt = re.sub(r"%(\d*)(l+|z)(x|u|d)", r"%\1\3", "".join(bits)) return fmt def generate(events, backend, group): diff --git a/scripts/tracetool/transform.py b/scripts/tracetool/transform.py deleted file mode 100644 index ea8b27799d..0000000000 --- a/scripts/tracetool/transform.py +++ /dev/null @@ -1,168 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Type-transformation rules. -""" - -__author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2012-2016, Lluís Vilanova " -__license__ = "GPL version 2 or (at your option) any later version" - -__maintainer__ = "Stefan Hajnoczi" -__email__ = "stefanha@redhat.com" - - -def _transform_type(type_, trans): - if isinstance(trans, str): - return trans - elif isinstance(trans, dict): - if type_ in trans: - return _transform_type(type_, trans[type_]) - elif None in trans: - return _transform_type(type_, trans[None]) - else: - return type_ - elif callable(trans): - return trans(type_) - else: - raise ValueError("Invalid type transformation rule: %s" % trans) - - -def transform_type(type_, *trans): - """Return a new type transformed according to the given rules. - - Applies each of the transformation rules in trans in order. - - If an element of trans is a string, return it. - - If an element of trans is a function, call it with type_ as its only - argument. - - If an element of trans is a dict, search type_ in its keys. If type_ is - a key, use the value as a transformation rule for type_. Otherwise, if - None is a key use the value as a transformation rule for type_. - - Otherwise, return type_. - - Parameters - ---------- - type_ : str - Type to transform. - trans : list of function or dict - Type transformation rules. - """ - if len(trans) == 0: - raise ValueError - res = type_ - for t in trans: - res = _transform_type(res, t) - return res - - -################################################## -# tcg -> host - -def _tcg_2_host(type_): - if type_ == "TCGv": - # force a fixed-size type (target-independent) - return "uint64_t" - else: - return type_ - -TCG_2_HOST = { - "TCGv_i32": "uint32_t", - "TCGv_i64": "uint64_t", - "TCGv_ptr": "void *", - None: _tcg_2_host, - } - - -################################################## -# host -> host compatible with tcg sizes - -HOST_2_TCG_COMPAT = { - "uint8_t": "uint32_t", - "uint16_t": "uint32_t", - } - - -################################################## -# host/tcg -> tcg - -def _host_2_tcg(type_): - if type_.startswith("TCGv"): - return type_ - raise ValueError("Don't know how to translate '%s' into a TCG type\n" % type_) - -HOST_2_TCG = { - "uint32_t": "TCGv_i32", - "uint64_t": "TCGv_i64", - "void *" : "TCGv_ptr", - "CPUArchState *": "TCGv_env", - None: _host_2_tcg, - } - - -################################################## -# tcg -> tcg helper definition - -def _tcg_2_helper_def(type_): - if type_ == "TCGv": - return "target_ulong" - else: - return type_ - -TCG_2_TCG_HELPER_DEF = { - "TCGv_i32": "uint32_t", - "TCGv_i64": "uint64_t", - "TCGv_ptr": "void *", - None: _tcg_2_helper_def, - } - - -################################################## -# tcg -> tcg helper declaration - -def _tcg_2_tcg_helper_decl_error(type_): - raise ValueError("Don't know how to translate type '%s' into a TCG helper declaration type\n" % type_) - -TCG_2_TCG_HELPER_DECL = { - "TCGv" : "tl", - "TCGv_ptr": "ptr", - "TCGv_i32": "i32", - "TCGv_i64": "i64", - "TCGv_env": "env", - None: _tcg_2_tcg_helper_decl_error, - } - - -################################################## -# host/tcg -> tcg temporal constant allocation - -def _host_2_tcg_tmp_new(type_): - if type_.startswith("TCGv"): - return "tcg_temp_new_nop" - raise ValueError("Don't know how to translate type '%s' into a TCG temporal allocation" % type_) - -HOST_2_TCG_TMP_NEW = { - "uint32_t": "tcg_const_i32", - "uint64_t": "tcg_const_i64", - "void *" : "tcg_const_ptr", - None: _host_2_tcg_tmp_new, - } - - -################################################## -# host/tcg -> tcg temporal constant deallocation - -def _host_2_tcg_tmp_free(type_): - if type_.startswith("TCGv"): - return "tcg_temp_free_nop" - raise ValueError("Don't know how to translate type '%s' into a TCG temporal deallocation" % type_) - -HOST_2_TCG_TMP_FREE = { - "uint32_t": "tcg_temp_free_i32", - "uint64_t": "tcg_temp_free_i64", - "void *" : "tcg_temp_free_ptr", - None: _host_2_tcg_tmp_free, - } diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 839a5ec614..a0006eec6f 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -156,12 +156,16 @@ for arch in $ARCHLIST; do cp_portable "$tmpdir/bootparam.h" \ "$output/include/standard-headers/asm-$arch" fi + if [ $arch = riscv ]; then + cp "$tmpdir/include/asm/ptrace.h" "$output/linux-headers/asm-riscv/" + fi done rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" -for header in kvm.h vfio.h vfio_ccw.h vfio_zdev.h vhost.h \ - psci.h psp-sev.h userfaultfd.h mman.h; do +for header in const.h stddef.h kvm.h vfio.h vfio_ccw.h vfio_zdev.h vhost.h \ + psci.h psp-sev.h userfaultfd.h memfd.h mman.h nvme_ioctl.h \ + vduse.h iommufd.h; do cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done diff --git a/scripts/vmstate-static-checker.py b/scripts/vmstate-static-checker.py index 539ead62b4..9c0e6b81f2 100755 --- a/scripts/vmstate-static-checker.py +++ b/scripts/vmstate-static-checker.py @@ -40,7 +40,7 @@ def check_fields_match(name, s_field, d_field): return True # Some fields changed names between qemu versions. This list - # is used to whitelist such changes in each section / description. + # is used to allow such changes in each section / description. changed_names = { 'apic': ['timer', 'timer_expiry'], 'e1000': ['dev', 'parent_obj'], @@ -134,6 +134,11 @@ def exists_in_substruct(fields, item): return check_fields_match(fields["Description"]["name"], substruct_fields[0]["field"], item) +def size_total(entry): + size = entry["size"] + if "num" not in entry: + return size + return size * entry["num"] def check_fields(src_fields, dest_fields, desc, sec): # This function checks for all the fields in a section. If some @@ -249,17 +254,19 @@ def check_fields(src_fields, dest_fields, desc, sec): continue if s_item["field"] == "unused" or d_item["field"] == "unused": - if s_item["size"] == d_item["size"]: + s_size = size_total(s_item) + d_size = size_total(d_item) + if s_size == d_size: continue if d_item["field"] == "unused": advance_dest = False - unused_count = d_item["size"] - s_item["size"] + unused_count = d_size - s_size; continue if s_item["field"] == "unused": advance_src = False - unused_count = s_item["size"] - d_item["size"] + unused_count = s_size - d_size continue print("Section \"" + sec + "\",", end=' ') @@ -367,7 +374,6 @@ def check_machine_type(s, d): if s["Name"] != d["Name"]: print("Warning: checking incompatible machine types:", end=' ') print("\"" + s["Name"] + "\", \"" + d["Name"] + "\"") - return def main(): diff --git a/scripts/xen-detect.c b/scripts/xen-detect.c index 85e8206490..db049e605c 100644 --- a/scripts/xen-detect.c +++ b/scripts/xen-detect.c @@ -138,66 +138,6 @@ return 0; } -#elif CONFIG_XEN_CTRL_INTERFACE_VERSION == 40600 - #include - #include - #include - #include - #if !defined(HVM_MAX_VCPUS) - # error HVM_MAX_VCPUS not defined - #endif - int main(void) { - xc_interface *xc; - xs_daemon_open(); - xc = xc_interface_open(0, 0, 0); - xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); - xc_gnttab_open(NULL, 0); - xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); - xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); - xc_hvm_create_ioreq_server(xc, 0, HVM_IOREQSRV_BUFIOREQ_ATOMIC, NULL); - xc_reserved_device_memory_map(xc, 0, 0, 0, 0, NULL, 0); - return 0; - } - -#elif CONFIG_XEN_CTRL_INTERFACE_VERSION == 40500 - #include - #include - #include - #include - #if !defined(HVM_MAX_VCPUS) - # error HVM_MAX_VCPUS not defined - #endif - int main(void) { - xc_interface *xc; - xs_daemon_open(); - xc = xc_interface_open(0, 0, 0); - xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); - xc_gnttab_open(NULL, 0); - xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); - xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); - xc_hvm_create_ioreq_server(xc, 0, 0, NULL); - return 0; - } - -#elif CONFIG_XEN_CTRL_INTERFACE_VERSION == 40200 - #include - #include - #include - #include - #if !defined(HVM_MAX_VCPUS) - # error HVM_MAX_VCPUS not defined - #endif - int main(void) { - xc_interface *xc; - xs_daemon_open(); - xc = xc_interface_open(0, 0, 0); - xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); - xc_gnttab_open(NULL, 0); - xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); - xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); - return 0; - } - #else #error invalid CONFIG_XEN_CTRL_INTERFACE_VERSION #endif diff --git a/scripts/xml-preprocess-test.py b/scripts/xml-preprocess-test.py new file mode 100644 index 0000000000..dd92579969 --- /dev/null +++ b/scripts/xml-preprocess-test.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023 Red Hat, Inc. +# +# SPDX-License-Identifier: MIT +"""Unit tests for xml-preprocess""" + +import contextlib +import importlib +import os +import platform +import subprocess +import tempfile +import unittest +from io import StringIO + +xmlpp = importlib.import_module("xml-preprocess") + + +class TestXmlPreprocess(unittest.TestCase): + """Tests for xml-preprocess.Preprocessor""" + + def test_preprocess_xml(self): + with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_file: + temp_file.write("") + temp_file_name = temp_file.name + result = xmlpp.preprocess_xml(temp_file_name) + self.assertEqual(result, "") + os.remove(temp_file_name) + + def test_save_xml(self): + with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_file: + temp_file_name = temp_file.name + xmlpp.save_xml("", temp_file_name) + self.assertTrue(os.path.isfile(temp_file_name)) + os.remove(temp_file_name) + + def test_include(self): + with tempfile.NamedTemporaryFile(mode="w", delete=False) as inc_file: + inc_file.write("Content from included file") + inc_file_name = inc_file.name + xml_str = f"" + expected = "Content from included file" + xpp = xmlpp.Preprocessor() + result = xpp.preprocess(xml_str) + self.assertEqual(result, expected) + os.remove(inc_file_name) + self.assertRaises(FileNotFoundError, xpp.preprocess, xml_str) + + def test_envvar(self): + os.environ["TEST_ENV_VAR"] = "TestValue" + xml_str = "$(env.TEST_ENV_VAR)" + expected = "TestValue" + xpp = xmlpp.Preprocessor() + result = xpp.preprocess(xml_str) + self.assertEqual(result, expected) + self.assertRaises(KeyError, xpp.preprocess, "$(env.UNKNOWN)") + + def test_sys_var(self): + xml_str = "$(sys.ARCH)" + expected = f"{platform.architecture()[0]}" + xpp = xmlpp.Preprocessor() + result = xpp.preprocess(xml_str) + self.assertEqual(result, expected) + self.assertRaises(KeyError, xpp.preprocess, "$(sys.UNKNOWN)") + + def test_cus_var(self): + xml_str = "$(var.USER)" + expected = "" + xpp = xmlpp.Preprocessor() + result = xpp.preprocess(xml_str) + self.assertEqual(result, expected) + xml_str = "$(var.USER)" + expected = "FOO" + xpp = xmlpp.Preprocessor() + result = xpp.preprocess(xml_str) + self.assertEqual(result, expected) + + def test_error_warning(self): + xml_str = "" + expected = "" + xpp = xmlpp.Preprocessor() + out = StringIO() + with contextlib.redirect_stdout(out): + result = xpp.preprocess(xml_str) + self.assertEqual(result, expected) + self.assertEqual(out.getvalue(), "[Warning]: test warn\n") + self.assertRaises(RuntimeError, xpp.preprocess, "") + + def test_cmd(self): + xpp = xmlpp.Preprocessor() + result = xpp.preprocess('') + self.assertEqual(result, "hello world") + self.assertRaises( + subprocess.CalledProcessError, + xpp.preprocess, '' + ) + + def test_foreach(self): + xpp = xmlpp.Preprocessor() + result = xpp.preprocess( + '$(var.x)' + ) + self.assertEqual(result, "abc") + + def test_if_elseif(self): + xpp = xmlpp.Preprocessor() + result = xpp.preprocess('ok') + self.assertEqual(result, "ok") + result = xpp.preprocess('ok') + self.assertEqual(result, "") + result = xpp.preprocess('okko') + self.assertEqual(result, "ok") + result = xpp.preprocess('okko') + self.assertEqual(result, "ko") + result = xpp.preprocess( + 'okok2ko' + ) + self.assertEqual(result, "ok2") + result = xpp.preprocess( + 'okokko' + ) + self.assertEqual(result, "ko") + + def test_ifdef(self): + xpp = xmlpp.Preprocessor() + result = xpp.preprocess('okko') + self.assertEqual(result, "ko") + result = xpp.preprocess( + 'okko' + ) + self.assertEqual(result, "ok") + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/xml-preprocess.py b/scripts/xml-preprocess.py new file mode 100644 index 0000000000..57f1d28912 --- /dev/null +++ b/scripts/xml-preprocess.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2017-2019 Tony Su +# Copyright (c) 2023 Red Hat, Inc. +# +# SPDX-License-Identifier: MIT +# +# Adapted from https://github.com/peitaosu/XML-Preprocessor +# +"""This is a XML Preprocessor which can be used to process your XML file before +you use it, to process conditional statements, variables, iteration +statements, error/warning, execute command, etc. + +## XML Schema + +### Include Files +``` + +``` + +### Variables +``` +$(env.EnvironmentVariable) + +$(sys.SystemVariable) + +$(var.CustomVariable) +``` + +### Conditional Statements +``` + + + + + + + + + + + +``` + +### Iteration Statements +``` + + $(var.VARNAME) + +``` + +### Errors and Warnings +``` + + + +``` + +### Commands +``` + +``` +""" + +import os +import platform +import re +import subprocess +import sys +from typing import Optional +from xml.dom import minidom + + +class Preprocessor(): + """This class holds the XML preprocessing state""" + + def __init__(self): + self.sys_vars = { + "ARCH": platform.architecture()[0], + "SOURCE": os.path.abspath(__file__), + "CURRENT": os.getcwd(), + } + self.cus_vars = {} + + def _pp_include(self, xml_str: str) -> str: + include_regex = r"(<\?include([\w\s\\/.:_-]+)\s*\?>)" + matches = re.findall(include_regex, xml_str) + for group_inc, group_xml in matches: + inc_file_path = group_xml.strip() + with open(inc_file_path, "r", encoding="utf-8") as inc_file: + inc_file_content = inc_file.read() + xml_str = xml_str.replace(group_inc, inc_file_content) + return xml_str + + def _pp_env_var(self, xml_str: str) -> str: + envvar_regex = r"(\$\(env\.(\w+)\))" + matches = re.findall(envvar_regex, xml_str) + for group_env, group_var in matches: + xml_str = xml_str.replace(group_env, os.environ[group_var]) + return xml_str + + def _pp_sys_var(self, xml_str: str) -> str: + sysvar_regex = r"(\$\(sys\.(\w+)\))" + matches = re.findall(sysvar_regex, xml_str) + for group_sys, group_var in matches: + xml_str = xml_str.replace(group_sys, self.sys_vars[group_var]) + return xml_str + + def _pp_cus_var(self, xml_str: str) -> str: + define_regex = r"(<\?define\s*(\w+)\s*=\s*([\w\s\"]+)\s*\?>)" + matches = re.findall(define_regex, xml_str) + for group_def, group_name, group_var in matches: + group_name = group_name.strip() + group_var = group_var.strip().strip("\"") + self.cus_vars[group_name] = group_var + xml_str = xml_str.replace(group_def, "") + cusvar_regex = r"(\$\(var\.(\w+)\))" + matches = re.findall(cusvar_regex, xml_str) + for group_cus, group_var in matches: + xml_str = xml_str.replace( + group_cus, + self.cus_vars.get(group_var, "") + ) + return xml_str + + def _pp_foreach(self, xml_str: str) -> str: + foreach_regex = r"(<\?foreach\s+(\w+)\s+in\s+([\w;]+)\s*\?>(.*)<\?endforeach\?>)" + matches = re.findall(foreach_regex, xml_str) + for group_for, group_name, group_vars, group_text in matches: + group_texts = "" + for var in group_vars.split(";"): + self.cus_vars[group_name] = var + group_texts += self._pp_cus_var(group_text) + xml_str = xml_str.replace(group_for, group_texts) + return xml_str + + def _pp_error_warning(self, xml_str: str) -> str: + error_regex = r"<\?error\s*\"([^\"]+)\"\s*\?>" + matches = re.findall(error_regex, xml_str) + for group_var in matches: + raise RuntimeError("[Error]: " + group_var) + warning_regex = r"(<\?warning\s*\"([^\"]+)\"\s*\?>)" + matches = re.findall(warning_regex, xml_str) + for group_wrn, group_var in matches: + print("[Warning]: " + group_var) + xml_str = xml_str.replace(group_wrn, "") + return xml_str + + def _pp_if_eval(self, xml_str: str) -> str: + ifelif_regex = ( + r"(<\?(if|elseif)\s*([^\"\s=<>!]+)\s*([!=<>]+)\s*\"*([^\"=<>!]+)\"*\s*\?>)" + ) + matches = re.findall(ifelif_regex, xml_str) + for ifelif, tag, left, operator, right in matches: + if "<" in operator or ">" in operator: + result = eval(f"{left} {operator} {right}") + else: + result = eval(f'"{left}" {operator} "{right}"') + xml_str = xml_str.replace(ifelif, f"") + return xml_str + + def _pp_ifdef_ifndef(self, xml_str: str) -> str: + ifndef_regex = r"(<\?(ifdef|ifndef)\s*([\w]+)\s*\?>)" + matches = re.findall(ifndef_regex, xml_str) + for group_ifndef, group_tag, group_var in matches: + if group_tag == "ifdef": + result = group_var in self.cus_vars + else: + result = group_var not in self.cus_vars + xml_str = xml_str.replace(group_ifndef, f"") + return xml_str + + def _pp_if_elseif(self, xml_str: str) -> str: + if_elif_else_regex = ( + r"(<\?if\s(True|False)\?>" + r"(.*?)" + r"<\?elseif\s(True|False)\?>" + r"(.*?)" + r"<\?else\?>" + r"(.*?)" + r"<\?endif\?>)" + ) + if_else_regex = ( + r"(<\?if\s(True|False)\?>" + r"(.*?)" + r"<\?else\?>" + r"(.*?)" + r"<\?endif\?>)" + ) + if_regex = r"(<\?if\s(True|False)\?>(.*?)<\?endif\?>)" + matches = re.findall(if_elif_else_regex, xml_str, re.DOTALL) + for (group_full, group_if, group_if_elif, group_elif, + group_elif_else, group_else) in matches: + result = "" + if group_if == "True": + result = group_if_elif + elif group_elif == "True": + result = group_elif_else + else: + result = group_else + xml_str = xml_str.replace(group_full, result) + matches = re.findall(if_else_regex, xml_str, re.DOTALL) + for group_full, group_if, group_if_else, group_else in matches: + result = "" + if group_if == "True": + result = group_if_else + else: + result = group_else + xml_str = xml_str.replace(group_full, result) + matches = re.findall(if_regex, xml_str, re.DOTALL) + for group_full, group_if, group_text in matches: + result = "" + if group_if == "True": + result = group_text + xml_str = xml_str.replace(group_full, result) + return xml_str + + def _pp_command(self, xml_str: str) -> str: + cmd_regex = r"(<\?cmd\s*\"([^\"]+)\"\s*\?>)" + matches = re.findall(cmd_regex, xml_str) + for group_cmd, group_exec in matches: + output = subprocess.check_output( + group_exec, shell=True, + text=True, stderr=subprocess.STDOUT + ) + xml_str = xml_str.replace(group_cmd, output) + return xml_str + + def _pp_blanks(self, xml_str: str) -> str: + right_blank_regex = r">[\n\s\t\r]*" + left_blank_regex = r"[\n\s\t\r]*<" + xml_str = re.sub(right_blank_regex, ">", xml_str) + xml_str = re.sub(left_blank_regex, "<", xml_str) + return xml_str + + def preprocess(self, xml_str: str) -> str: + fns = [ + self._pp_blanks, + self._pp_include, + self._pp_foreach, + self._pp_env_var, + self._pp_sys_var, + self._pp_cus_var, + self._pp_if_eval, + self._pp_ifdef_ifndef, + self._pp_if_elseif, + self._pp_command, + self._pp_error_warning, + ] + + while True: + changed = False + for func in fns: + out_xml = func(xml_str) + if not changed and out_xml != xml_str: + changed = True + xml_str = out_xml + if not changed: + break + + return xml_str + + +def preprocess_xml(path: str) -> str: + with open(path, "r", encoding="utf-8") as original_file: + input_xml = original_file.read() + + proc = Preprocessor() + return proc.preprocess(input_xml) + + +def save_xml(xml_str: str, path: Optional[str]): + xml = minidom.parseString(xml_str) + with open(path, "w", encoding="utf-8") if path else sys.stdout as output_file: + output_file.write(xml.toprettyxml()) + + +def main(): + if len(sys.argv) < 2: + print("Usage: xml-preprocessor input.xml [output.xml]") + sys.exit(1) + + output_file = None + if len(sys.argv) == 3: + output_file = sys.argv[2] + + input_file = sys.argv[1] + output_xml = preprocess_xml(input_file) + save_xml(output_xml, output_file) + + +if __name__ == "__main__": + main() diff --git a/scsi/meson.build b/scsi/meson.build index 53f3a1f716..cdb91e11b0 100644 --- a/scsi/meson.build +++ b/scsi/meson.build @@ -1,4 +1,6 @@ block_ss.add(files('utils.c')) -block_ss.add(when: 'CONFIG_LINUX', - if_true: files('pr-manager.c', 'pr-manager-helper.c'), - if_false: files('pr-manager-stub.c')) +if host_os == 'linux' + block_ss.add(files('pr-manager.c', 'pr-manager-helper.c')) +else + block_ss.add(files('pr-manager-stub.c')) +endif diff --git a/scsi/pr-manager.c b/scsi/pr-manager.c index 2098d7e759..fb5fc29730 100644 --- a/scsi/pr-manager.c +++ b/scsi/pr-manager.c @@ -51,7 +51,6 @@ static int pr_manager_worker(void *opaque) int coroutine_fn pr_manager_execute(PRManager *pr_mgr, AioContext *ctx, int fd, struct sg_io_hdr *hdr) { - ThreadPool *pool = aio_get_thread_pool(ctx); PRManagerData data = { .pr_mgr = pr_mgr, .fd = fd, @@ -62,7 +61,7 @@ int coroutine_fn pr_manager_execute(PRManager *pr_mgr, AioContext *ctx, int fd, /* The matching object_unref is in pr_manager_worker. */ object_ref(OBJECT(pr_mgr)); - return thread_pool_submit_co(pool, pr_manager_worker, &data); + return thread_pool_submit_co(pr_manager_worker, &data); } bool pr_manager_is_connected(PRManager *pr_mgr) diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index 196b78c00d..c6c6347e9b 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -177,10 +177,9 @@ static int do_sgio_worker(void *opaque) return status; } -static int do_sgio(int fd, const uint8_t *cdb, uint8_t *sense, - uint8_t *buf, int *sz, int dir) +static int coroutine_fn do_sgio(int fd, const uint8_t *cdb, uint8_t *sense, + uint8_t *buf, int *sz, int dir) { - ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context()); int r; PRHelperSGIOData data = { @@ -192,7 +191,7 @@ static int do_sgio(int fd, const uint8_t *cdb, uint8_t *sense, .dir = dir, }; - r = thread_pool_submit_co(pool, do_sgio_worker, &data); + r = thread_pool_submit_co(do_sgio_worker, &data); *sz = data.sz; return r; } @@ -281,11 +280,7 @@ void put_multipath_config(struct config *conf) static void multipath_pr_init(void) { udev = udev_new(); -#ifdef CONFIG_MPATH_NEW_API multipath_conf = mpath_lib_init(); -#else - mpath_lib_init(udev); -#endif } static int is_mpath(int fd) @@ -320,7 +315,7 @@ static SCSISense mpath_generic_sense(int r) } } -static int mpath_reconstruct_sense(int fd, int r, uint8_t *sense) +static int coroutine_fn mpath_reconstruct_sense(int fd, int r, uint8_t *sense) { switch (r) { case MPATH_PR_SUCCESS: @@ -372,8 +367,8 @@ static int mpath_reconstruct_sense(int fd, int r, uint8_t *sense) } } -static int multipath_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, - uint8_t *data, int sz) +static int coroutine_fn multipath_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, + uint8_t *data, int sz) { int rq_servact = cdb[1]; struct prin_resp resp; @@ -427,8 +422,8 @@ static int multipath_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, return mpath_reconstruct_sense(fd, r, sense); } -static int multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, - const uint8_t *param, int sz) +static int coroutine_fn multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, + const uint8_t *param, int sz) { int rq_servact = cdb[1]; int rq_scope = cdb[2] >> 4; @@ -545,8 +540,8 @@ static int multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, } #endif -static int do_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, - uint8_t *data, int *resp_sz) +static int coroutine_fn do_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, + uint8_t *data, int *resp_sz) { #ifdef CONFIG_MPATH if (is_mpath(fd)) { @@ -563,8 +558,8 @@ static int do_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, SG_DXFER_FROM_DEV); } -static int do_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, - const uint8_t *param, int sz) +static int coroutine_fn do_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, + const uint8_t *param, int sz) { int resp_sz; @@ -614,7 +609,7 @@ static int coroutine_fn prh_read(PRHelperClient *client, void *buf, int sz, iov.iov_base = buf; iov.iov_len = sz; n_read = qio_channel_readv_full(QIO_CHANNEL(client->ioc), &iov, 1, - &fds, &nfds, errp); + &fds, &nfds, 0, errp); if (n_read == QIO_CHANNEL_ERR_BLOCK) { qio_channel_yield(QIO_CHANNEL(client->ioc), G_IO_IN); @@ -740,8 +735,7 @@ static void coroutine_fn prh_co_entry(void *opaque) qio_channel_set_blocking(QIO_CHANNEL(client->ioc), false, NULL); - qio_channel_attach_aio_context(QIO_CHANNEL(client->ioc), - qemu_get_aio_context()); + qio_channel_set_follow_coroutine_ctx(QIO_CHANNEL(client->ioc), true); /* A very simple negotiation for future extensibility. No features * are defined so write 0. @@ -801,7 +795,6 @@ static void coroutine_fn prh_co_entry(void *opaque) } out: - qio_channel_detach_aio_context(QIO_CHANNEL(client->ioc)); object_unref(OBJECT(client->ioc)); g_free(client); } diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index b6ddaf863a..d78c6428b9 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -24,7 +24,7 @@ * * ARM Semihosting is documented in: * Semihosting for AArch32 and AArch64 Release 2.0 - * https://static.docs.arm.com/100863/0200/semihosting.pdf + * https://github.com/ARM-software/abi-aa/blob/main/semihosting/semihosting.rst * * RISC-V Semihosting is documented in: * RISC-V Semihosting @@ -32,12 +32,15 @@ */ #include "qemu/osdep.h" - +#include "qemu/timer.h" +#include "exec/gdbstub.h" +#include "gdbstub/syscalls.h" #include "semihosting/semihost.h" #include "semihosting/console.h" #include "semihosting/common-semi.h" -#include "qemu/timer.h" -#include "exec/gdbstub.h" +#include "semihosting/guestfd.h" +#include "semihosting/syscalls.h" + #ifdef CONFIG_USER_ONLY #include "qemu.h" @@ -45,9 +48,6 @@ #else #include "qemu/cutils.h" #include "hw/loader.h" -#ifdef TARGET_ARM -#include "hw/arm/boot.h" -#endif #include "hw/boards.h" #endif @@ -85,65 +85,21 @@ #define O_BINARY 0 #endif -#define GDB_O_RDONLY 0x000 -#define GDB_O_WRONLY 0x001 -#define GDB_O_RDWR 0x002 -#define GDB_O_APPEND 0x008 -#define GDB_O_CREAT 0x200 -#define GDB_O_TRUNC 0x400 -#define GDB_O_BINARY 0 - static int gdb_open_modeflags[12] = { GDB_O_RDONLY, - GDB_O_RDONLY | GDB_O_BINARY, + GDB_O_RDONLY, + GDB_O_RDWR, GDB_O_RDWR, - GDB_O_RDWR | GDB_O_BINARY, GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC, - GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY, + GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC, + GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC, GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC, - GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY, GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND, - GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY, + GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND, + GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND, GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND, - GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY }; -static int open_modeflags[12] = { - O_RDONLY, - O_RDONLY | O_BINARY, - O_RDWR, - O_RDWR | O_BINARY, - O_WRONLY | O_CREAT | O_TRUNC, - O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, - O_RDWR | O_CREAT | O_TRUNC, - O_RDWR | O_CREAT | O_TRUNC | O_BINARY, - O_WRONLY | O_CREAT | O_APPEND, - O_WRONLY | O_CREAT | O_APPEND | O_BINARY, - O_RDWR | O_CREAT | O_APPEND, - O_RDWR | O_CREAT | O_APPEND | O_BINARY -}; - -typedef enum GuestFDType { - GuestFDUnused = 0, - GuestFDHost = 1, - GuestFDGDB = 2, - GuestFDFeatureFile = 3, -} GuestFDType; - -/* - * Guest file descriptors are integer indexes into an array of - * these structures (we will dynamically resize as necessary). - */ -typedef struct GuestFD { - GuestFDType type; - union { - int hostfd; - target_ulong featurefile_offset; - }; -} GuestFD; - -static GArray *guestfd_array; - #ifndef CONFIG_USER_ONLY /** @@ -210,187 +166,55 @@ static LayoutInfo common_semi_find_bases(CPUState *cs) #endif -#ifdef TARGET_ARM -static inline target_ulong -common_semi_arg(CPUState *cs, int argno) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - if (is_a64(env)) { - return env->xregs[argno]; - } else { - return env->regs[argno]; - } -} - -static inline void -common_semi_set_ret(CPUState *cs, target_ulong ret) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - if (is_a64(env)) { - env->xregs[0] = ret; - } else { - env->regs[0] = ret; - } -} - -static inline bool -common_semi_sys_exit_extended(CPUState *cs, int nr) -{ - return (nr == TARGET_SYS_EXIT_EXTENDED || is_a64(cs->env_ptr)); -} - -#endif /* TARGET_ARM */ - -#ifdef TARGET_RISCV -static inline target_ulong -common_semi_arg(CPUState *cs, int argno) -{ - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - return env->gpr[xA0 + argno]; -} - -static inline void -common_semi_set_ret(CPUState *cs, target_ulong ret) -{ - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - env->gpr[xA0] = ret; -} - -static inline bool -common_semi_sys_exit_extended(CPUState *cs, int nr) -{ - return (nr == TARGET_SYS_EXIT_EXTENDED || sizeof(target_ulong) == 8); -} - -#endif +#include "common-semi-target.h" /* - * Allocate a new guest file descriptor and return it; if we - * couldn't allocate a new fd then return -1. - * This is a fairly simplistic implementation because we don't - * expect that most semihosting guest programs will make very - * heavy use of opening and closing fds. + * Read the input value from the argument block; fail the semihosting + * call if the memory read fails. Eventually we could use a generic + * CPUState helper function here. + * Note that GET_ARG() handles memory access errors by jumping to + * do_fault, so must be used as the first thing done in handling a + * semihosting call, to avoid accidentally leaking allocated resources. + * SET_ARG(), since it unavoidably happens late, instead returns an + * error indication (0 on success, non-0 for error) which the caller + * should check. */ -static int alloc_guestfd(void) -{ - guint i; - if (!guestfd_array) { - /* New entries zero-initialized, i.e. type GuestFDUnused */ - guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD)); - } +#define GET_ARG(n) do { \ + if (is_64bit_semihosting(env)) { \ + if (get_user_u64(arg ## n, args + (n) * 8)) { \ + goto do_fault; \ + } \ + } else { \ + if (get_user_u32(arg ## n, args + (n) * 4)) { \ + goto do_fault; \ + } \ + } \ +} while (0) - /* 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); +#define SET_ARG(n, val) \ + (is_64bit_semihosting(env) ? \ + put_user_u64(val, args + (n) * 8) : \ + put_user_u32(val, args + (n) * 4)) - if (gf->type == GuestFDUnused) { - return i; - } - } - - /* All elements already in use: expand the array */ - g_array_set_size(guestfd_array, i + 1); - return i; -} - -/* - * Look up the guestfd in the data structure; return NULL - * for out of bounds, but don't check whether the slot is unused. - * This is used internally by the other guestfd functions. - */ -static GuestFD *do_get_guestfd(int guestfd) -{ - if (!guestfd_array) { - return NULL; - } - - if (guestfd <= 0 || guestfd >= guestfd_array->len) { - return NULL; - } - - return &g_array_index(guestfd_array, GuestFD, guestfd); -} - -/* - * Associate the specified guest fd (which must have been - * allocated via alloc_fd() and not previously used) with - * the specified host/gdb fd. - */ -static void associate_guestfd(int guestfd, int hostfd) -{ - GuestFD *gf = do_get_guestfd(guestfd); - - assert(gf); - gf->type = use_gdb_syscalls() ? GuestFDGDB : GuestFDHost; - gf->hostfd = hostfd; -} - -/* - * Deallocate the specified guest file descriptor. This doesn't - * close the host fd, it merely undoes the work of alloc_fd(). - */ -static void dealloc_guestfd(int guestfd) -{ - GuestFD *gf = do_get_guestfd(guestfd); - - assert(gf); - gf->type = GuestFDUnused; -} - -/* - * Given a guest file descriptor, get the associated struct. - * If the fd is not valid, return NULL. This is the function - * used by the various semihosting calls to validate a handle - * from the guest. - * Note: calling alloc_guestfd() or dealloc_guestfd() will - * invalidate any GuestFD* obtained by calling this function. - */ -static GuestFD *get_guestfd(int guestfd) -{ - GuestFD *gf = do_get_guestfd(guestfd); - - if (!gf || gf->type == GuestFDUnused) { - return NULL; - } - return gf; -} /* * The semihosting API has no concept of its errno being thread-safe, * as the API design predates SMP CPUs and was intended as a simple * real-hardware set of debug functionality. For QEMU, we make the - * errno be per-thread in linux-user mode; in softmmu it is a simple + * errno be per-thread in linux-user mode; in system-mode it is a simple * global, and we assume that the guest takes care of avoiding any races. */ #ifndef CONFIG_USER_ONLY static target_ulong syscall_err; -#include "exec/softmmu-semi.h" +#include "semihosting/uaccess.h" #endif -static inline uint32_t set_swi_errno(CPUState *cs, uint32_t code) -{ - if (code == (uint32_t)-1) { -#ifdef CONFIG_USER_ONLY - TaskState *ts = cs->opaque; - - ts->swi_errno = errno; -#else - syscall_err = errno; -#endif - } - return code; -} - static inline uint32_t get_swi_errno(CPUState *cs) { #ifdef CONFIG_USER_ONLY - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); return ts->swi_errno; #else @@ -398,254 +222,116 @@ static inline uint32_t get_swi_errno(CPUState *cs) #endif } -static target_ulong common_semi_syscall_len; - -static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) +static void common_semi_cb(CPUState *cs, uint64_t ret, int err) { - target_ulong reg0 = common_semi_arg(cs, 0); - - if (ret == (target_ulong)-1) { - errno = err; - set_swi_errno(cs, -1); - reg0 = ret; - } else { - /* Fixup syscalls that use nonstardard return conventions. */ - switch (reg0) { - case TARGET_SYS_WRITE: - case TARGET_SYS_READ: - reg0 = common_semi_syscall_len - ret; - break; - case TARGET_SYS_SEEK: - reg0 = 0; - break; - default: - reg0 = ret; - break; - } - } - common_semi_set_ret(cs, reg0); -} - -static target_ulong common_semi_flen_buf(CPUState *cs) -{ - target_ulong sp; -#ifdef TARGET_ARM - /* Return an address in target memory of 64 bytes where the remote - * gdb should write its stat struct. (The format of this structure - * is defined by GDB's remote protocol and is not target-specific.) - * We put this on the guest's stack just below SP. - */ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - - if (is_a64(env)) { - sp = env->xregs[31]; - } else { - sp = env->regs[13]; - } + if (err) { +#ifdef CONFIG_USER_ONLY + TaskState *ts = get_task_state(cs); + ts->swi_errno = err; +#else + syscall_err = err; #endif -#ifdef TARGET_RISCV - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - - sp = env->gpr[xSP]; -#endif - - return sp - 64; -} - -static void -common_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) -{ - /* The size is always stored in big-endian order, extract - the value. We assume the size always fit in 32 bits. */ - uint32_t size; - cpu_memory_rw_debug(cs, common_semi_flen_buf(cs) + 32, - (uint8_t *)&size, 4, 0); - size = be32_to_cpu(size); - common_semi_set_ret(cs, size); - errno = err; - set_swi_errno(cs, -1); -} - -static int common_semi_open_guestfd; - -static void -common_semi_open_cb(CPUState *cs, target_ulong ret, target_ulong err) -{ - if (ret == (target_ulong)-1) { - errno = err; - set_swi_errno(cs, -1); - dealloc_guestfd(common_semi_open_guestfd); - } else { - associate_guestfd(common_semi_open_guestfd, ret); - ret = common_semi_open_guestfd; } common_semi_set_ret(cs, ret); } -static target_ulong -common_semi_gdb_syscall(CPUState *cs, gdb_syscall_complete_cb cb, - const char *fmt, ...) +/* + * Use 0xdeadbeef as the return value when there isn't a defined + * return value for the call. + */ +static void common_semi_dead_cb(CPUState *cs, uint64_t ret, int err) { - va_list va; - - va_start(va, fmt); - gdb_do_syscallv(cb, fmt, va); - va_end(va); - - /* - * FIXME: in softmmu mode, the gdbstub will schedule our callback - * to occur, but will not actually call it to complete the syscall - * until after this function has returned and we are back in the - * CPU main loop. Therefore callers to this function must not - * do anything with its return value, because it is not necessarily - * the result of the syscall, but could just be the old value of X0. - * The only thing safe to do with this is that the callers of - * do_common_semihosting() will write it straight back into X0. - * (In linux-user mode, the callback will have happened before - * gdb_do_syscallv() returns.) - * - * We should tidy this up so neither this function nor - * do_common_semihosting() return a value, so the mistake of - * doing something with the return value is not possible to make. - */ - - return common_semi_arg(cs, 0); + common_semi_set_ret(cs, 0xdeadbeef); } /* - * Types for functions implementing various semihosting calls - * for specific types of guest file descriptor. These must all - * do the work and return the required return value for the guest, - * setting the guest errno if appropriate. + * SYS_READ and SYS_WRITE always return the number of bytes not read/written. + * There is no error condition, other than returning the original length. */ -typedef uint32_t sys_closefn(CPUState *cs, GuestFD *gf); -typedef uint32_t sys_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len); -typedef uint32_t sys_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len); -typedef uint32_t sys_isattyfn(CPUState *cs, GuestFD *gf); -typedef uint32_t sys_seekfn(CPUState *cs, GuestFD *gf, - target_ulong offset); -typedef uint32_t sys_flenfn(CPUState *cs, GuestFD *gf); - -static uint32_t host_closefn(CPUState *cs, GuestFD *gf) +static void common_semi_rw_cb(CPUState *cs, uint64_t ret, int err) { - /* - * 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; + /* Recover the original length from the third argument. */ + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + target_ulong args = common_semi_arg(cs, 1); + target_ulong arg2; + GET_ARG(2); + + if (err) { + do_fault: + ret = 0; /* error: no bytes transmitted */ } - return set_swi_errno(cs, close(gf->hostfd)); + common_semi_set_ret(cs, arg2 - ret); } -static uint32_t host_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) +/* + * Convert from Posix ret+errno to Arm SYS_ISTTY return values. + * With gdbstub, err is only ever set for protocol errors to EIO. + */ +static void common_semi_istty_cb(CPUState *cs, uint64_t ret, int err) { - CPUArchState *env = cs->env_ptr; - uint32_t ret; - char *s = lock_user(VERIFY_READ, buf, len, 1); - (void) env; /* Used in arm softmmu lock_user implicitly */ - if (!s) { - /* Return bytes not written on error */ - return len; + if (err) { + ret = (err == ENOTTY ? 0 : -1); } - ret = set_swi_errno(cs, write(gf->hostfd, s, len)); - unlock_user(s, buf, 0); - if (ret == (uint32_t)-1) { + common_semi_cb(cs, ret, err); +} + +/* + * SYS_SEEK returns 0 on success, not the resulting offset. + */ +static void common_semi_seek_cb(CPUState *cs, uint64_t ret, int err) +{ + if (!err) { ret = 0; } - /* Return bytes not written */ - return len - ret; + common_semi_cb(cs, ret, err); } -static uint32_t host_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) +/* + * Return an address in target memory of 64 bytes where the remote + * gdb should write its stat struct. (The format of this structure + * is defined by GDB's remote protocol and is not target-specific.) + * We put this on the guest's stack just below SP. + */ +static target_ulong common_semi_flen_buf(CPUState *cs) { - CPUArchState *env = cs->env_ptr; - uint32_t ret; - char *s = lock_user(VERIFY_WRITE, buf, len, 0); - (void) env; /* Used in arm softmmu lock_user implicitly */ - if (!s) { - /* return bytes not read */ - return len; + target_ulong sp = common_semi_stack_bottom(cs); + return sp - 64; +} + +static void +common_semi_flen_fstat_cb(CPUState *cs, uint64_t ret, int err) +{ + if (!err) { + /* The size is always stored in big-endian order, extract the value. */ + uint64_t size; + if (cpu_memory_rw_debug(cs, common_semi_flen_buf(cs) + + offsetof(struct gdb_stat, gdb_st_size), + &size, 8, 0)) { + ret = -1, err = EFAULT; + } else { + size = be64_to_cpu(size); + if (ret != size) { + ret = -1, err = EOVERFLOW; + } + } } - do { - ret = set_swi_errno(cs, read(gf->hostfd, s, len)); - } while (ret == -1 && errno == EINTR); - unlock_user(s, buf, len); - if (ret == (uint32_t)-1) { - ret = 0; + common_semi_cb(cs, ret, err); +} + +static void +common_semi_readc_cb(CPUState *cs, uint64_t ret, int err) +{ + if (!err) { + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + uint8_t ch; + + if (get_user_u8(ch, common_semi_stack_bottom(cs) - 1)) { + ret = -1, err = EFAULT; + } else { + ret = ch; + } } - /* Return bytes not read */ - return len - ret; -} - -static uint32_t host_isattyfn(CPUState *cs, GuestFD *gf) -{ - return isatty(gf->hostfd); -} - -static uint32_t host_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset) -{ - uint32_t ret = set_swi_errno(cs, lseek(gf->hostfd, offset, SEEK_SET)); - if (ret == (uint32_t)-1) { - return -1; - } - return 0; -} - -static uint32_t host_flenfn(CPUState *cs, GuestFD *gf) -{ - struct stat buf; - uint32_t ret = set_swi_errno(cs, fstat(gf->hostfd, &buf)); - if (ret == (uint32_t)-1) { - return -1; - } - return buf.st_size; -} - -static uint32_t gdb_closefn(CPUState *cs, GuestFD *gf) -{ - return common_semi_gdb_syscall(cs, common_semi_cb, "close,%x", gf->hostfd); -} - -static uint32_t gdb_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) -{ - common_semi_syscall_len = len; - return common_semi_gdb_syscall(cs, common_semi_cb, "write,%x,%x,%x", - gf->hostfd, buf, len); -} - -static uint32_t gdb_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) -{ - common_semi_syscall_len = len; - return common_semi_gdb_syscall(cs, common_semi_cb, "read,%x,%x,%x", - gf->hostfd, buf, len); -} - -static uint32_t gdb_isattyfn(CPUState *cs, GuestFD *gf) -{ - return common_semi_gdb_syscall(cs, common_semi_cb, "isatty,%x", gf->hostfd); -} - -static uint32_t gdb_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset) -{ - return common_semi_gdb_syscall(cs, common_semi_cb, "lseek,%x,%x,0", - gf->hostfd, offset); -} - -static uint32_t gdb_flenfn(CPUState *cs, GuestFD *gf) -{ - return common_semi_gdb_syscall(cs, common_semi_flen_cb, "fstat,%x,%x", - gf->hostfd, common_semi_flen_buf(cs)); + common_semi_cb(cs, ret, err); } #define SHFB_MAGIC_0 0x53 @@ -665,202 +351,47 @@ static const uint8_t featurefile_data[] = { SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */ }; -static void init_featurefile_guestfd(int guestfd) -{ - GuestFD *gf = do_get_guestfd(guestfd); - - assert(gf); - gf->type = GuestFDFeatureFile; - gf->featurefile_offset = 0; -} - -static uint32_t featurefile_closefn(CPUState *cs, GuestFD *gf) -{ - /* Nothing to do */ - return 0; -} - -static uint32_t featurefile_writefn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) -{ - /* This fd can never be open for writing */ - - errno = EBADF; - return set_swi_errno(cs, -1); -} - -static uint32_t featurefile_readfn(CPUState *cs, GuestFD *gf, - target_ulong buf, uint32_t len) -{ - CPUArchState *env = cs->env_ptr; - uint32_t i; - char *s; - - (void) env; /* Used in arm softmmu lock_user implicitly */ - s = lock_user(VERIFY_WRITE, buf, len, 0); - if (!s) { - return len; - } - - for (i = 0; i < len; i++) { - if (gf->featurefile_offset >= sizeof(featurefile_data)) { - break; - } - s[i] = featurefile_data[gf->featurefile_offset]; - gf->featurefile_offset++; - } - - unlock_user(s, buf, len); - - /* Return number of bytes not read */ - return len - i; -} - -static uint32_t featurefile_isattyfn(CPUState *cs, GuestFD *gf) -{ - return 0; -} - -static uint32_t featurefile_seekfn(CPUState *cs, GuestFD *gf, - target_ulong offset) -{ - gf->featurefile_offset = offset; - return 0; -} - -static uint32_t featurefile_flenfn(CPUState *cs, GuestFD *gf) -{ - return sizeof(featurefile_data); -} - -typedef struct GuestFDFunctions { - sys_closefn *closefn; - sys_writefn *writefn; - sys_readfn *readfn; - sys_isattyfn *isattyfn; - sys_seekfn *seekfn; - sys_flenfn *flenfn; -} GuestFDFunctions; - -static const GuestFDFunctions guestfd_fns[] = { - [GuestFDHost] = { - .closefn = host_closefn, - .writefn = host_writefn, - .readfn = host_readfn, - .isattyfn = host_isattyfn, - .seekfn = host_seekfn, - .flenfn = host_flenfn, - }, - [GuestFDGDB] = { - .closefn = gdb_closefn, - .writefn = gdb_writefn, - .readfn = gdb_readfn, - .isattyfn = gdb_isattyfn, - .seekfn = gdb_seekfn, - .flenfn = gdb_flenfn, - }, - [GuestFDFeatureFile] = { - .closefn = featurefile_closefn, - .writefn = featurefile_writefn, - .readfn = featurefile_readfn, - .isattyfn = featurefile_isattyfn, - .seekfn = featurefile_seekfn, - .flenfn = featurefile_flenfn, - }, -}; - -/* - * Read the input value from the argument block; fail the semihosting - * call if the memory read fails. Eventually we could use a generic - * CPUState helper function here. - */ -static inline bool is_64bit_semihosting(CPUArchState *env) -{ -#if defined(TARGET_ARM) - return is_a64(env); -#elif defined(TARGET_RISCV) - return riscv_cpu_mxl(env) != MXL_RV32; -#else -#error un-handled architecture -#endif -} - - -#define GET_ARG(n) do { \ - if (is_64bit_semihosting(env)) { \ - if (get_user_u64(arg ## n, args + (n) * 8)) { \ - errno = EFAULT; \ - return set_swi_errno(cs, -1); \ - } \ - } else { \ - if (get_user_u32(arg ## n, args + (n) * 4)) { \ - errno = EFAULT; \ - return set_swi_errno(cs, -1); \ - } \ - } \ -} while (0) - -#define SET_ARG(n, val) \ - (is_64bit_semihosting(env) ? \ - put_user_u64(val, args + (n) * 8) : \ - put_user_u32(val, args + (n) * 4)) - - /* * Do a semihosting call. * * The specification always says that the "return register" either * returns a specific value or is corrupted, so we don't need to * report to our caller whether we are returning a value or trying to - * leave the register unchanged. We use 0xdeadbeef as the return value - * when there isn't a defined return value for the call. + * leave the register unchanged. */ -target_ulong do_common_semihosting(CPUState *cs) +void do_common_semihosting(CPUState *cs) { - CPUArchState *env = cs->env_ptr; + CPUArchState *env = cpu_env(cs); target_ulong args; target_ulong arg0, arg1, arg2, arg3; target_ulong ul_ret; char * s; int nr; - uint32_t ret; - uint32_t len; - GuestFD *gf; int64_t elapsed; - (void) env; /* Used implicitly by arm lock_user macro */ nr = common_semi_arg(cs, 0) & 0xffffffffU; args = common_semi_arg(cs, 1); switch (nr) { case TARGET_SYS_OPEN: { - int guestfd; + int ret, err = 0; + int hostfd; GET_ARG(0); GET_ARG(1); GET_ARG(2); s = lock_user_string(arg0); if (!s) { - errno = EFAULT; - return set_swi_errno(cs, -1); + goto do_fault; } if (arg1 >= 12) { unlock_user(s, arg0, 0); - errno = EINVAL; - return set_swi_errno(cs, -1); - } - - guestfd = alloc_guestfd(); - if (guestfd < 0) { - unlock_user(s, arg0, 0); - errno = EMFILE; - return set_swi_errno(cs, -1); + common_semi_cb(cs, -1, EINVAL); + break; } if (strcmp(s, ":tt") == 0) { - int result_fileno; - /* * We implement SH_EXT_STDOUT_STDERR, so: * open for read == stdin @@ -868,206 +399,170 @@ target_ulong do_common_semihosting(CPUState *cs) * open for append == stderr */ if (arg1 < 4) { - result_fileno = STDIN_FILENO; + hostfd = STDIN_FILENO; } else if (arg1 < 8) { - result_fileno = STDOUT_FILENO; + hostfd = STDOUT_FILENO; } else { - result_fileno = STDERR_FILENO; + hostfd = STDERR_FILENO; } - associate_guestfd(guestfd, result_fileno); - unlock_user(s, arg0, 0); - return guestfd; - } - if (strcmp(s, ":semihosting-features") == 0) { - unlock_user(s, arg0, 0); + ret = alloc_guestfd(); + associate_guestfd(ret, hostfd); + } else if (strcmp(s, ":semihosting-features") == 0) { /* We must fail opens for modes other than 0 ('r') or 1 ('rb') */ if (arg1 != 0 && arg1 != 1) { - dealloc_guestfd(guestfd); - errno = EACCES; - return set_swi_errno(cs, -1); - } - init_featurefile_guestfd(guestfd); - return guestfd; - } - - if (use_gdb_syscalls()) { - common_semi_open_guestfd = guestfd; - ret = common_semi_gdb_syscall(cs, common_semi_open_cb, - "open,%s,%x,1a4", arg0, (int)arg2 + 1, - gdb_open_modeflags[arg1]); - } else { - ret = set_swi_errno(cs, open(s, open_modeflags[arg1], 0644)); - if (ret == (uint32_t)-1) { - dealloc_guestfd(guestfd); + ret = -1; + err = EACCES; } else { - associate_guestfd(guestfd, ret); - ret = guestfd; + ret = alloc_guestfd(); + staticfile_guestfd(ret, featurefile_data, + sizeof(featurefile_data)); } + } else { + unlock_user(s, arg0, 0); + semihost_sys_open(cs, common_semi_cb, arg0, arg2 + 1, + gdb_open_modeflags[arg1], 0644); + break; } unlock_user(s, arg0, 0); - return ret; + common_semi_cb(cs, ret, err); + break; } + case TARGET_SYS_CLOSE: GET_ARG(0); + semihost_sys_close(cs, common_semi_cb, arg0); + break; - gf = get_guestfd(arg0); - if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); - } - - ret = guestfd_fns[gf->type].closefn(cs, gf); - dealloc_guestfd(arg0); - return ret; case TARGET_SYS_WRITEC: - qemu_semihosting_console_outc(cs->env_ptr, args); - return 0xdeadbeef; + /* + * FIXME: the byte to be written is in a target_ulong slot, + * which means this is wrong for a big-endian guest. + */ + semihost_sys_write_gf(cs, common_semi_dead_cb, + &console_out_gf, args, 1); + break; + case TARGET_SYS_WRITE0: - return qemu_semihosting_console_outs(cs->env_ptr, args); + { + ssize_t len = target_strlen(args); + if (len < 0) { + common_semi_dead_cb(cs, -1, EFAULT); + } else { + semihost_sys_write_gf(cs, common_semi_dead_cb, + &console_out_gf, args, len); + } + } + break; + case TARGET_SYS_WRITE: GET_ARG(0); GET_ARG(1); GET_ARG(2); - len = arg2; + semihost_sys_write(cs, common_semi_rw_cb, arg0, arg1, arg2); + break; - gf = get_guestfd(arg0); - if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); - } - - return guestfd_fns[gf->type].writefn(cs, gf, arg1, len); case TARGET_SYS_READ: GET_ARG(0); GET_ARG(1); GET_ARG(2); - len = arg2; + semihost_sys_read(cs, common_semi_rw_cb, arg0, arg1, arg2); + break; - gf = get_guestfd(arg0); - if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); - } - - return guestfd_fns[gf->type].readfn(cs, gf, arg1, len); case TARGET_SYS_READC: - return qemu_semihosting_console_inc(cs->env_ptr); + semihost_sys_read_gf(cs, common_semi_readc_cb, &console_in_gf, + common_semi_stack_bottom(cs) - 1, 1); + break; + case TARGET_SYS_ISERROR: GET_ARG(0); - return (target_long) arg0 < 0 ? 1 : 0; + common_semi_set_ret(cs, (target_long)arg0 < 0); + break; + case TARGET_SYS_ISTTY: GET_ARG(0); + semihost_sys_isatty(cs, common_semi_istty_cb, arg0); + break; - gf = get_guestfd(arg0); - if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); - } - - return guestfd_fns[gf->type].isattyfn(cs, gf); case TARGET_SYS_SEEK: GET_ARG(0); GET_ARG(1); + semihost_sys_lseek(cs, common_semi_seek_cb, arg0, arg1, GDB_SEEK_SET); + break; - gf = get_guestfd(arg0); - if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); - } - - return guestfd_fns[gf->type].seekfn(cs, gf, arg1); case TARGET_SYS_FLEN: GET_ARG(0); + semihost_sys_flen(cs, common_semi_flen_fstat_cb, common_semi_cb, + arg0, common_semi_flen_buf(cs)); + break; - gf = get_guestfd(arg0); - if (!gf) { - errno = EBADF; - return set_swi_errno(cs, -1); - } - - return guestfd_fns[gf->type].flenfn(cs, gf); case TARGET_SYS_TMPNAM: + { + int len; + char *p; + GET_ARG(0); GET_ARG(1); GET_ARG(2); - if (asprintf(&s, "/tmp/qemu-%x%02x", getpid(), - (int) (arg1 & 0xff)) < 0) { - return -1; + len = asprintf(&s, "%s/qemu-%x%02x", g_get_tmp_dir(), + getpid(), (int)arg1 & 0xff); + if (len < 0) { + common_semi_set_ret(cs, -1); + break; } - ul_ret = (target_ulong) -1; + /* Allow for trailing NUL */ + len++; /* Make sure there's enough space in the buffer */ - if (strlen(s) < arg2) { - char *output = lock_user(VERIFY_WRITE, arg0, arg2, 0); - strcpy(output, s); - unlock_user(output, arg0, arg2); - ul_ret = 0; + if (len > arg2) { + free(s); + common_semi_set_ret(cs, -1); + break; } + p = lock_user(VERIFY_WRITE, arg0, len, 0); + if (!p) { + free(s); + goto do_fault; + } + memcpy(p, s, len); + unlock_user(p, arg0, len); free(s); - return ul_ret; + common_semi_set_ret(cs, 0); + break; + } + case TARGET_SYS_REMOVE: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - ret = common_semi_gdb_syscall(cs, common_semi_cb, "unlink,%s", - arg0, (int)arg1 + 1); - } else { - s = lock_user_string(arg0); - if (!s) { - errno = EFAULT; - return set_swi_errno(cs, -1); - } - ret = set_swi_errno(cs, remove(s)); - unlock_user(s, arg0, 0); - } - return ret; + semihost_sys_remove(cs, common_semi_cb, arg0, arg1 + 1); + break; + case TARGET_SYS_RENAME: GET_ARG(0); GET_ARG(1); GET_ARG(2); GET_ARG(3); - if (use_gdb_syscalls()) { - return common_semi_gdb_syscall(cs, common_semi_cb, "rename,%s,%s", - arg0, (int)arg1 + 1, arg2, - (int)arg3 + 1); - } else { - char *s2; - s = lock_user_string(arg0); - s2 = lock_user_string(arg2); - if (!s || !s2) { - errno = EFAULT; - ret = set_swi_errno(cs, -1); - } else { - ret = set_swi_errno(cs, rename(s, s2)); - } - if (s2) - unlock_user(s2, arg2, 0); - if (s) - unlock_user(s, arg0, 0); - return ret; - } + semihost_sys_rename(cs, common_semi_cb, arg0, arg1 + 1, arg2, arg3 + 1); + break; + case TARGET_SYS_CLOCK: - return clock() / (CLOCKS_PER_SEC / 100); + common_semi_set_ret(cs, clock() / (CLOCKS_PER_SEC / 100)); + break; + case TARGET_SYS_TIME: - return set_swi_errno(cs, time(NULL)); + ul_ret = time(NULL); + common_semi_cb(cs, ul_ret, ul_ret == -1 ? errno : 0); + break; + case TARGET_SYS_SYSTEM: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - return common_semi_gdb_syscall(cs, common_semi_cb, "system,%s", - arg0, (int)arg1 + 1); - } else { - s = lock_user_string(arg0); - if (!s) { - errno = EFAULT; - return set_swi_errno(cs, -1); - } - ret = set_swi_errno(cs, system(s)); - unlock_user(s, arg0, 0); - return ret; - } + semihost_sys_system(cs, common_semi_cb, arg0, arg1 + 1); + break; + case TARGET_SYS_ERRNO: - return get_swi_errno(cs); + common_semi_set_ret(cs, get_swi_errno(cs)); + break; + case TARGET_SYS_GET_CMDLINE: { /* Build a command-line from the original argv. @@ -1091,7 +586,7 @@ target_ulong do_common_semihosting(CPUState *cs) #if !defined(CONFIG_USER_ONLY) const char *cmdline; #else - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); #endif GET_ARG(0); GET_ARG(1); @@ -1118,22 +613,20 @@ target_ulong do_common_semihosting(CPUState *cs) if (output_size > input_size) { /* Not enough space to store command-line arguments. */ - errno = E2BIG; - return set_swi_errno(cs, -1); + common_semi_cb(cs, -1, E2BIG); + break; } /* Adjust the command-line length. */ if (SET_ARG(1, output_size - 1)) { /* Couldn't write back to argument block */ - errno = EFAULT; - return set_swi_errno(cs, -1); + goto do_fault; } /* Lock the buffer on the ARM side. */ output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0); if (!output_buffer) { - errno = EFAULT; - return set_swi_errno(cs, -1); + goto do_fault; } /* Copy the command-line arguments. */ @@ -1148,9 +641,8 @@ target_ulong do_common_semihosting(CPUState *cs) if (copy_from_user(output_buffer, ts->info->arg_strings, output_size)) { - errno = EFAULT; - status = set_swi_errno(cs, -1); - goto out; + unlock_user(output_buffer, arg0, 0); + goto do_fault; } /* Separate arguments by white spaces. */ @@ -1163,15 +655,16 @@ target_ulong do_common_semihosting(CPUState *cs) #endif /* Unlock the buffer on the ARM side. */ unlock_user(output_buffer, arg0, output_size); - - return status; + common_semi_cb(cs, status, 0); } + break; + case TARGET_SYS_HEAPINFO: { target_ulong retvals[4]; int i; #ifdef CONFIG_USER_ONLY - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); target_ulong limit; #else LayoutInfo info = common_semi_find_bases(cs); @@ -1222,14 +715,18 @@ target_ulong do_common_semihosting(CPUState *cs) if (fail) { /* Couldn't write back to argument block */ - errno = EFAULT; - return set_swi_errno(cs, -1); + goto do_fault; } } - return 0; + common_semi_set_ret(cs, 0); } + break; + case TARGET_SYS_EXIT: case TARGET_SYS_EXIT_EXTENDED: + { + uint32_t ret; + if (common_semi_sys_exit_extended(cs, nr)) { /* * The A64 version of SYS_EXIT takes a parameter block, @@ -1257,36 +754,46 @@ target_ulong do_common_semihosting(CPUState *cs) } gdb_exit(ret); exit(ret); + } + case TARGET_SYS_ELAPSED: elapsed = get_clock() - clock_start; if (sizeof(target_ulong) == 8) { - SET_ARG(0, elapsed); + if (SET_ARG(0, elapsed)) { + goto do_fault; + } } else { - SET_ARG(0, (uint32_t) elapsed); - SET_ARG(1, (uint32_t) (elapsed >> 32)); + if (SET_ARG(0, (uint32_t) elapsed) || + SET_ARG(1, (uint32_t) (elapsed >> 32))) { + goto do_fault; + } } - return 0; + common_semi_set_ret(cs, 0); + break; + case TARGET_SYS_TICKFREQ: /* qemu always uses nsec */ - return 1000000000; + common_semi_set_ret(cs, 1000000000); + break; + case TARGET_SYS_SYNCCACHE: /* * Clean the D-cache and invalidate the I-cache for the specified * virtual address range. This is a nop for us since we don't * implement caches. This is only present on A64. */ -#ifdef TARGET_ARM - if (is_a64(cs->env_ptr)) { - return 0; + if (common_semi_has_synccache(env)) { + common_semi_set_ret(cs, 0); + break; } -#endif -#ifdef TARGET_RISCV - return 0; -#endif - /* fall through -- invalid for A32/T32 */ + /* fall through */ default: fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); cpu_dump_state(cs, stderr, 0); abort(); + + do_fault: + common_semi_cb(cs, -1, EFAULT); + break; } } diff --git a/semihosting/config.c b/semihosting/config.c index 50d82108e6..56283b5c3c 100644 --- a/semihosting/config.c +++ b/semihosting/config.c @@ -8,11 +8,11 @@ * targets that support it. Architecture specific handling is handled * in target/HW/HW-semi.c * - * Semihosting is sightly strange in that it is also supported by some + * Semihosting is slightly strange in that it is also supported by some * linux-user targets. However in that use case no configuration of * the outputs and command lines is supported. * - * The config module is common to all softmmu targets however as vl.c + * The config module is common to all system targets however as vl.c * needs to link against the helpers. * * SPDX-License-Identifier: GPL-2.0-or-later @@ -27,12 +27,16 @@ QemuOptsList qemu_semihosting_config_opts = { .name = "semihosting-config", + .merge_lists = true, .implied_opt_name = "enable", .head = QTAILQ_HEAD_INITIALIZER(qemu_semihosting_config_opts.head), .desc = { { .name = "enable", .type = QEMU_OPT_BOOL, + }, { + .name = "userspace", + .type = QEMU_OPT_BOOL, }, { .name = "target", .type = QEMU_OPT_STRING, @@ -49,8 +53,8 @@ QemuOptsList qemu_semihosting_config_opts = { typedef struct SemihostingConfig { bool enabled; + bool userspace_enabled; SemihostingTarget target; - Chardev *chardev; char **argv; int argc; const char *cmdline; /* concatenated argv */ @@ -59,9 +63,9 @@ typedef struct SemihostingConfig { static SemihostingConfig semihosting; static const char *semihost_chardev; -bool semihosting_enabled(void) +bool semihosting_enabled(bool is_user) { - return semihosting.enabled; + return semihosting.enabled && (!is_user || semihosting.userspace_enabled); } SemihostingTarget semihosting_get_target(void) @@ -109,39 +113,37 @@ static int add_semihosting_arg(void *opaque, void semihosting_arg_fallback(const char *file, const char *cmd) { char *cmd_token; + g_autofree char *cmd_dup = g_strdup(cmd); /* argv[0] */ add_semihosting_arg(&semihosting, "arg", file, NULL); /* split -append and initialize argv[1..n] */ - cmd_token = strtok(g_strdup(cmd), " "); + cmd_token = strtok(cmd_dup, " "); while (cmd_token) { add_semihosting_arg(&semihosting, "arg", cmd_token, NULL); cmd_token = strtok(NULL, " "); } } -Chardev *semihosting_get_chardev(void) -{ - return semihosting.chardev; -} - void qemu_semihosting_enable(void) { semihosting.enabled = true; semihosting.target = SEMIHOSTING_TARGET_AUTO; } -int qemu_semihosting_config_options(const char *optarg) +int qemu_semihosting_config_options(const char *optstr) { QemuOptsList *opt_list = qemu_find_opts("semihosting-config"); - QemuOpts *opts = qemu_opts_parse_noisily(opt_list, optarg, false); + QemuOpts *opts = qemu_opts_parse_noisily(opt_list, optstr, false); semihosting.enabled = true; if (opts != NULL) { semihosting.enabled = qemu_opt_get_bool(opts, "enable", true); + semihosting.userspace_enabled = qemu_opt_get_bool(opts, "userspace", + false); const char *target = qemu_opt_get(opts, "target"); /* setup of chardev is deferred until they are initialised */ semihost_chardev = qemu_opt_get(opts, "chardev"); @@ -154,7 +156,7 @@ int qemu_semihosting_config_options(const char *optarg) semihosting.target = SEMIHOSTING_TARGET_AUTO; } else { error_report("unsupported semihosting-config %s", - optarg); + optstr); return 1; } } else { @@ -164,23 +166,26 @@ int qemu_semihosting_config_options(const char *optarg) qemu_opt_foreach(opts, add_semihosting_arg, &semihosting, NULL); } else { - error_report("unsupported semihosting-config %s", optarg); + error_report("unsupported semihosting-config %s", optstr); return 1; } return 0; } -void qemu_semihosting_connect_chardevs(void) +/* We had to defer this until chardevs were created */ +void qemu_semihosting_chardev_init(void) { - /* We had to defer this until chardevs were created */ + Chardev *chr = NULL; + if (semihost_chardev) { - Chardev *chr = qemu_chr_find(semihost_chardev); + chr = qemu_chr_find(semihost_chardev); if (chr == NULL) { error_report("semihosting chardev '%s' not found", semihost_chardev); exit(1); } - semihosting.chardev = chr; } + + qemu_semihosting_console_init(chr); } diff --git a/semihosting/console.c b/semihosting/console.c index ef6958d844..60102bbab6 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -27,89 +27,10 @@ #include "qapi/error.h" #include "qemu/fifo8.h" -int qemu_semihosting_log_out(const char *s, int len) -{ - Chardev *chardev = semihosting_get_chardev(); - if (chardev) { - return qemu_chr_write_all(chardev, (uint8_t *) s, len); - } else { - return write(STDERR_FILENO, s, len); - } -} - -/* - * A re-implementation of lock_user_string that we can use locally - * instead of relying on softmmu-semi. Hopefully we can deprecate that - * in time. Copy string until we find a 0 or address error. - */ -static GString *copy_user_string(CPUArchState *env, target_ulong addr) -{ - CPUState *cpu = env_cpu(env); - GString *s = g_string_sized_new(128); - uint8_t c; - - do { - if (cpu_memory_rw_debug(cpu, addr++, &c, 1, 0) == 0) { - if (c) { - s = g_string_append_c(s, c); - } - } else { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: passed inaccessible address " TARGET_FMT_lx, - __func__, addr); - break; - } - } while (c!=0); - - return s; -} - -static void semihosting_cb(CPUState *cs, target_ulong ret, target_ulong err) -{ - if (ret == (target_ulong) -1) { - qemu_log("%s: gdb console output failed ("TARGET_FMT_ld")", - __func__, err); - } -} - -int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr) -{ - GString *s = copy_user_string(env, addr); - int out = s->len; - - if (use_gdb_syscalls()) { - gdb_do_syscall(semihosting_cb, "write,2,%x,%x", addr, s->len); - } else { - out = qemu_semihosting_log_out(s->str, s->len); - } - - g_string_free(s, true); - return out; -} - -void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) -{ - CPUState *cpu = env_cpu(env); - uint8_t c; - - if (cpu_memory_rw_debug(cpu, addr, &c, 1, 0) == 0) { - if (use_gdb_syscalls()) { - gdb_do_syscall(semihosting_cb, "write,2,%x,%x", addr, 1); - } else { - qemu_semihosting_log_out((const char *) &c, 1); - } - } else { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: passed inaccessible address " TARGET_FMT_lx, - __func__, addr); - } -} - -#define FIFO_SIZE 1024 - /* Access to this structure is protected by the BQL */ typedef struct SemihostingConsole { CharBackend backend; + Chardev *chr; GSList *sleeping_cpus; bool got; Fifo8 fifo; @@ -117,13 +38,13 @@ typedef struct SemihostingConsole { static SemihostingConsole console; +#define FIFO_SIZE 1024 + 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; + g_assert(bql_locked()); + return (int)fifo8_num_free(&c->fifo); } static void console_wake_up(gpointer data, gpointer user_data) @@ -137,7 +58,7 @@ static void console_wake_up(gpointer data, gpointer user_data) static void console_read(void *opaque, const uint8_t *buf, int size) { SemihostingConsole *c = opaque; - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); while (size-- && !fifo8_is_full(&c->fifo)) { fifo8_push(&c->fifo, *buf++); } @@ -145,27 +66,59 @@ static void console_read(void *opaque, const uint8_t *buf, int size) c->sleeping_cpus = NULL; } -target_ulong qemu_semihosting_console_inc(CPUArchState *env) +bool qemu_semihosting_console_ready(void) { - 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; + + g_assert(bql_locked()); + return !fifo8_is_empty(&c->fifo); } -void qemu_semihosting_console_init(void) +void qemu_semihosting_console_block_until_ready(CPUState *cs) { - Chardev *chr = semihosting_get_chardev(); + SemihostingConsole *c = &console; + g_assert(bql_locked()); + + /* Block if the fifo is completely empty. */ + if (fifo8_is_empty(&c->fifo)) { + c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, cs); + cs->halted = 1; + cs->exception_index = EXCP_HALTED; + cpu_loop_exit(cs); + /* never returns */ + } +} + +int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) +{ + SemihostingConsole *c = &console; + int ret = 0; + + qemu_semihosting_console_block_until_ready(cs); + + /* Read until buffer full or fifo exhausted. */ + do { + *(char *)(buf + ret) = fifo8_pop(&c->fifo); + ret++; + } while (ret < len && !fifo8_is_empty(&c->fifo)); + + return ret; +} + +int qemu_semihosting_console_write(void *buf, int len) +{ + if (console.chr) { + int r = qemu_chr_write_all(console.chr, (uint8_t *)buf, len); + return r < 0 ? 0 : r; + } else { + return fwrite(buf, 1, len, stderr); + } +} + +void qemu_semihosting_console_init(Chardev *chr) +{ + console.chr = chr; if (chr) { fifo8_create(&console.fifo, FIFO_SIZE); qemu_chr_fe_init(&console.backend, chr, &error_abort); @@ -175,4 +128,6 @@ void qemu_semihosting_console_init(void) NULL, NULL, &console, NULL, true); } + + qemu_semihosting_guestfd_init(); } diff --git a/semihosting/guestfd.c b/semihosting/guestfd.c new file mode 100644 index 0000000000..955c2efbd0 --- /dev/null +++ b/semihosting/guestfd.c @@ -0,0 +1,160 @@ +/* + * Hosted file support for semihosting syscalls. + * + * Copyright (c) 2005, 2007 CodeSourcery. + * Copyright (c) 2019 Linaro + * Copyright © 2020 by Keith Packard + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "gdbstub/syscalls.h" +#include "semihosting/semihost.h" +#include "semihosting/guestfd.h" +#ifdef CONFIG_USER_ONLY +#include "qemu.h" +#else +#include "semihosting/uaccess.h" +#include CONFIG_DEVICES +#endif + +static GArray *guestfd_array; + +#ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING +GuestFD console_in_gf; +GuestFD console_out_gf; +#endif + +void qemu_semihosting_guestfd_init(void) +{ + /* New entries zero-initialized, i.e. type GuestFDUnused */ + guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD)); + +#ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING + /* For ARM-compat, the console is in a separate namespace. */ + if (use_gdb_syscalls()) { + console_in_gf.type = GuestFDGDB; + console_in_gf.hostfd = 0; + console_out_gf.type = GuestFDGDB; + console_out_gf.hostfd = 2; + } else { + console_in_gf.type = GuestFDConsole; + console_out_gf.type = GuestFDConsole; + } +#else + /* Otherwise, the stdio file descriptors apply. */ + guestfd_array = g_array_set_size(guestfd_array, 3); +#ifndef CONFIG_USER_ONLY + if (!use_gdb_syscalls()) { + GuestFD *gf = &g_array_index(guestfd_array, GuestFD, 0); + gf[0].type = GuestFDConsole; + gf[1].type = GuestFDConsole; + gf[2].type = GuestFDConsole; + return; + } +#endif + associate_guestfd(0, 0); + associate_guestfd(1, 1); + associate_guestfd(2, 2); +#endif +} + +/* + * Allocate a new guest file descriptor and return it; if we + * couldn't allocate a new fd then return -1. + * This is a fairly simplistic implementation because we don't + * expect that most semihosting guest programs will make very + * heavy use of opening and closing fds. + */ +int alloc_guestfd(void) +{ + guint 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) { + return i; + } + } + + /* All elements already in use: expand the array */ + g_array_set_size(guestfd_array, i + 1); + return i; +} + +static void do_dealloc_guestfd(GuestFD *gf) +{ + gf->type = GuestFDUnused; +} + +/* + * Look up the guestfd in the data structure; return NULL + * for out of bounds, but don't check whether the slot is unused. + * This is used internally by the other guestfd functions. + */ +static GuestFD *do_get_guestfd(int guestfd) +{ + if (guestfd < 0 || guestfd >= guestfd_array->len) { + return NULL; + } + + return &g_array_index(guestfd_array, GuestFD, guestfd); +} + +/* + * Given a guest file descriptor, get the associated struct. + * If the fd is not valid, return NULL. This is the function + * used by the various semihosting calls to validate a handle + * from the guest. + * Note: calling alloc_guestfd() or dealloc_guestfd() will + * invalidate any GuestFD* obtained by calling this function. + */ +GuestFD *get_guestfd(int guestfd) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + if (!gf || gf->type == GuestFDUnused) { + return NULL; + } + return gf; +} + +/* + * Associate the specified guest fd (which must have been + * allocated via alloc_fd() and not previously used) with + * the specified host/gdb fd. + */ +void associate_guestfd(int guestfd, int hostfd) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + assert(gf); + gf->type = use_gdb_syscalls() ? GuestFDGDB : GuestFDHost; + gf->hostfd = hostfd; +} + +void staticfile_guestfd(int guestfd, const uint8_t *data, size_t len) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + assert(gf); + gf->type = GuestFDStatic; + gf->staticfile.data = data; + gf->staticfile.len = len; + gf->staticfile.off = 0; +} + +/* + * Deallocate the specified guest file descriptor. This doesn't + * close the host fd, it merely undoes the work of alloc_fd(). + */ +void dealloc_guestfd(int guestfd) +{ + GuestFD *gf = do_get_guestfd(guestfd); + + assert(gf); + do_dealloc_guestfd(gf); +} diff --git a/semihosting/meson.build b/semihosting/meson.build index ea8090abe3..b07cbd980f 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -1,6 +1,12 @@ specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( + 'guestfd.c', + 'syscalls.c', +)) + +specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SYSTEM_ONLY'], if_true: files( 'config.c', 'console.c', + 'uaccess.c', )) specific_ss.add(when: ['CONFIG_ARM_COMPATIBLE_SEMIHOSTING'], diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c new file mode 100644 index 0000000000..c40348f996 --- /dev/null +++ b/semihosting/syscalls.c @@ -0,0 +1,983 @@ +/* + * Syscall implementations for semihosting. + * + * Copyright (c) 2022 Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "gdbstub/syscalls.h" +#include "semihosting/guestfd.h" +#include "semihosting/syscalls.h" +#include "semihosting/console.h" +#ifdef CONFIG_USER_ONLY +#include "qemu.h" +#else +#include "semihosting/uaccess.h" +#endif + + +/* + * Validate or compute the length of the string (including terminator). + */ +static int validate_strlen(CPUState *cs, target_ulong str, target_ulong tlen) +{ + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + char c; + + if (tlen == 0) { + ssize_t slen = target_strlen(str); + + if (slen < 0) { + return -EFAULT; + } + if (slen >= INT32_MAX) { + return -ENAMETOOLONG; + } + return slen + 1; + } + if (tlen > INT32_MAX) { + return -ENAMETOOLONG; + } + if (get_user_u8(c, str + tlen - 1)) { + return -EFAULT; + } + if (c != 0) { + return -EINVAL; + } + return tlen; +} + +static int validate_lock_user_string(char **pstr, CPUState *cs, + target_ulong tstr, target_ulong tlen) +{ + int ret = validate_strlen(cs, tstr, tlen); + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + char *str = NULL; + + if (ret > 0) { + str = lock_user(VERIFY_READ, tstr, ret, true); + ret = str ? 0 : -EFAULT; + } + *pstr = str; + return ret; +} + +/* + * TODO: Note that gdb always stores the stat structure big-endian. + * So far, that's ok, as the only two targets using this are also + * big-endian. Until we do something with gdb, also produce the + * same big-endian result from the host. + */ +static int copy_stat_to_user(CPUState *cs, target_ulong addr, + const struct stat *s) +{ + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + struct gdb_stat *p; + + if (s->st_dev != (uint32_t)s->st_dev || + s->st_ino != (uint32_t)s->st_ino) { + return -EOVERFLOW; + } + + p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0); + if (!p) { + return -EFAULT; + } + + p->gdb_st_dev = cpu_to_be32(s->st_dev); + p->gdb_st_ino = cpu_to_be32(s->st_ino); + p->gdb_st_mode = cpu_to_be32(s->st_mode); + p->gdb_st_nlink = cpu_to_be32(s->st_nlink); + p->gdb_st_uid = cpu_to_be32(s->st_uid); + p->gdb_st_gid = cpu_to_be32(s->st_gid); + p->gdb_st_rdev = cpu_to_be32(s->st_rdev); + p->gdb_st_size = cpu_to_be64(s->st_size); +#ifdef _WIN32 + /* Windows stat is missing some fields. */ + p->gdb_st_blksize = 0; + p->gdb_st_blocks = 0; +#else + p->gdb_st_blksize = cpu_to_be64(s->st_blksize); + p->gdb_st_blocks = cpu_to_be64(s->st_blocks); +#endif + p->gdb_st_atime = cpu_to_be32(s->st_atime); + p->gdb_st_mtime = cpu_to_be32(s->st_mtime); + p->gdb_st_ctime = cpu_to_be32(s->st_ctime); + + unlock_user(p, addr, sizeof(struct gdb_stat)); + return 0; +} + +/* + * GDB semihosting syscall implementations. + */ + +static gdb_syscall_complete_cb gdb_open_complete; + +static void gdb_open_cb(CPUState *cs, uint64_t ret, int err) +{ + if (!err) { + int guestfd = alloc_guestfd(); + associate_guestfd(guestfd, ret); + ret = guestfd; + } + gdb_open_complete(cs, ret, err); +} + +static void gdb_open(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + int gdb_flags, int mode) +{ + int len = validate_strlen(cs, fname, fname_len); + if (len < 0) { + complete(cs, -1, -len); + return; + } + + gdb_open_complete = complete; + gdb_do_syscall(gdb_open_cb, "open,%s,%x,%x", + (uint64_t)fname, (uint32_t)len, + (uint32_t)gdb_flags, (uint32_t)mode); +} + +static void gdb_close(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + gdb_do_syscall(complete, "close,%x", (uint32_t)gf->hostfd); +} + +static void gdb_read(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + gdb_do_syscall(complete, "read,%x,%lx,%lx", + (uint32_t)gf->hostfd, (uint64_t)buf, (uint64_t)len); +} + +static void gdb_write(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + gdb_do_syscall(complete, "write,%x,%lx,%lx", + (uint32_t)gf->hostfd, (uint64_t)buf, (uint64_t)len); +} + +static void gdb_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, int64_t off, int gdb_whence) +{ + gdb_do_syscall(complete, "lseek,%x,%lx,%x", + (uint32_t)gf->hostfd, off, (uint32_t)gdb_whence); +} + +static void gdb_isatty(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + gdb_do_syscall(complete, "isatty,%x", (uint32_t)gf->hostfd); +} + +static void gdb_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong addr) +{ + gdb_do_syscall(complete, "fstat,%x,%lx", + (uint32_t)gf->hostfd, (uint64_t)addr); +} + +static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + target_ulong addr) +{ + int len = validate_strlen(cs, fname, fname_len); + if (len < 0) { + complete(cs, -1, -len); + return; + } + + gdb_do_syscall(complete, "stat,%s,%lx", + (uint64_t)fname, (uint32_t)len, (uint64_t)addr); +} + +static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len) +{ + int len = validate_strlen(cs, fname, fname_len); + if (len < 0) { + complete(cs, -1, -len); + return; + } + + gdb_do_syscall(complete, "unlink,%s", (uint64_t)fname, (uint32_t)len); +} + +static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong oname, target_ulong oname_len, + target_ulong nname, target_ulong nname_len) +{ + int olen, nlen; + + olen = validate_strlen(cs, oname, oname_len); + if (olen < 0) { + complete(cs, -1, -olen); + return; + } + nlen = validate_strlen(cs, nname, nname_len); + if (nlen < 0) { + complete(cs, -1, -nlen); + return; + } + + gdb_do_syscall(complete, "rename,%s,%s", + (uint64_t)oname, (uint32_t)olen, + (uint64_t)nname, (uint32_t)nlen); +} + +static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong cmd, target_ulong cmd_len) +{ + int len = validate_strlen(cs, cmd, cmd_len); + if (len < 0) { + complete(cs, -1, -len); + return; + } + + gdb_do_syscall(complete, "system,%s", (uint64_t)cmd, (uint32_t)len); +} + +static void gdb_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong tv_addr, target_ulong tz_addr) +{ + gdb_do_syscall(complete, "gettimeofday,%lx,%lx", + (uint64_t)tv_addr, (uint64_t)tz_addr); +} + +/* + * Host semihosting syscall implementations. + */ + +static void host_open(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + int gdb_flags, int mode) +{ + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + char *p; + int ret, host_flags = O_BINARY; + + ret = validate_lock_user_string(&p, cs, fname, fname_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + + if (gdb_flags & GDB_O_WRONLY) { + host_flags |= O_WRONLY; + } else if (gdb_flags & GDB_O_RDWR) { + host_flags |= O_RDWR; + } else { + host_flags |= O_RDONLY; + } + if (gdb_flags & GDB_O_CREAT) { + host_flags |= O_CREAT; + } + if (gdb_flags & GDB_O_TRUNC) { + host_flags |= O_TRUNC; + } + if (gdb_flags & GDB_O_EXCL) { + host_flags |= O_EXCL; + } + + ret = open(p, host_flags, mode); + if (ret < 0) { + complete(cs, -1, errno); + } else { + int guestfd = alloc_guestfd(); + associate_guestfd(guestfd, ret); + complete(cs, guestfd, 0); + } + unlock_user(p, fname, 0); +} + +static void host_close(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + /* + * 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 && + close(gf->hostfd) < 0) { + complete(cs, -1, errno); + } else { + complete(cs, 0, 0); + } +} + +static void host_read(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + void *ptr = lock_user(VERIFY_WRITE, buf, len, 0); + ssize_t ret; + + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + ret = RETRY_ON_EINTR(read(gf->hostfd, ptr, len)); + if (ret == -1) { + unlock_user(ptr, buf, 0); + complete(cs, -1, errno); + } else { + unlock_user(ptr, buf, ret); + complete(cs, ret, 0); + } +} + +static void host_write(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + void *ptr = lock_user(VERIFY_READ, buf, len, 1); + ssize_t ret; + + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + ret = write(gf->hostfd, ptr, len); + unlock_user(ptr, buf, 0); + complete(cs, ret, ret == -1 ? errno : 0); +} + +static void host_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, int64_t off, int whence) +{ + /* So far, all hosts use the same values. */ + QEMU_BUILD_BUG_ON(GDB_SEEK_SET != SEEK_SET); + QEMU_BUILD_BUG_ON(GDB_SEEK_CUR != SEEK_CUR); + QEMU_BUILD_BUG_ON(GDB_SEEK_END != SEEK_END); + + off_t ret = off; + int err = 0; + + if (ret == off) { + ret = lseek(gf->hostfd, ret, whence); + if (ret == -1) { + err = errno; + } + } else { + ret = -1; + err = EINVAL; + } + complete(cs, ret, err); +} + +static void host_isatty(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + int ret = isatty(gf->hostfd); + complete(cs, ret, ret ? 0 : errno); +} + +static void host_flen(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + struct stat buf; + + if (fstat(gf->hostfd, &buf) < 0) { + complete(cs, -1, errno); + } else { + complete(cs, buf.st_size, 0); + } +} + +static void host_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong addr) +{ + struct stat buf; + int ret; + + ret = fstat(gf->hostfd, &buf); + if (ret) { + complete(cs, -1, errno); + return; + } + ret = copy_stat_to_user(cs, addr, &buf); + complete(cs, ret ? -1 : 0, ret ? -ret : 0); +} + +static void host_stat(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + target_ulong addr) +{ + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + struct stat buf; + char *name; + int ret, err; + + ret = validate_lock_user_string(&name, cs, fname, fname_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + + ret = stat(name, &buf); + if (ret) { + err = errno; + } else { + ret = copy_stat_to_user(cs, addr, &buf); + err = 0; + if (ret < 0) { + err = -ret; + ret = -1; + } + } + unlock_user(name, fname, 0); + complete(cs, ret, err); +} + +static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len) +{ + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + char *p; + int ret; + + ret = validate_lock_user_string(&p, cs, fname, fname_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + + ret = remove(p); + unlock_user(p, fname, 0); + complete(cs, ret, ret ? errno : 0); +} + +static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong oname, target_ulong oname_len, + target_ulong nname, target_ulong nname_len) +{ + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + char *ostr, *nstr; + int ret; + + ret = validate_lock_user_string(&ostr, cs, oname, oname_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + ret = validate_lock_user_string(&nstr, cs, nname, nname_len); + if (ret < 0) { + unlock_user(ostr, oname, 0); + complete(cs, -1, -ret); + return; + } + + ret = rename(ostr, nstr); + unlock_user(ostr, oname, 0); + unlock_user(nstr, nname, 0); + complete(cs, ret, ret ? errno : 0); +} + +static void host_system(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong cmd, target_ulong cmd_len) +{ + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + char *p; + int ret; + + ret = validate_lock_user_string(&p, cs, cmd, cmd_len); + if (ret < 0) { + complete(cs, -1, -ret); + return; + } + + ret = system(p); + unlock_user(p, cmd, 0); + complete(cs, ret, ret == -1 ? errno : 0); +} + +static void host_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong tv_addr, target_ulong tz_addr) +{ + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + struct gdb_timeval *p; + int64_t rt; + + /* GDB fails on non-null TZ, so be consistent. */ + if (tz_addr != 0) { + complete(cs, -1, EINVAL); + return; + } + + p = lock_user(VERIFY_WRITE, tv_addr, sizeof(struct gdb_timeval), 0); + if (!p) { + complete(cs, -1, EFAULT); + return; + } + + /* TODO: Like stat, gdb always produces big-endian results; match it. */ + rt = g_get_real_time(); + p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC); + p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC); + unlock_user(p, tv_addr, sizeof(struct gdb_timeval)); +} + +#ifndef CONFIG_USER_ONLY +static void host_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, GIOCondition cond, int timeout) +{ + /* + * Since this is only used by xtensa in system mode, and stdio is + * handled through GuestFDConsole, and there are no semihosting + * system calls for sockets and the like, that means this descriptor + * must be a normal file. Normal files never block and are thus + * always ready. + */ + complete(cs, cond & (G_IO_IN | G_IO_OUT), 0); +} +#endif + +/* + * Static file semihosting syscall implementations. + */ + +static void staticfile_read(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + target_ulong rest = gf->staticfile.len - gf->staticfile.off; + void *ptr; + + if (len > rest) { + len = rest; + } + ptr = lock_user(VERIFY_WRITE, buf, len, 0); + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + memcpy(ptr, gf->staticfile.data + gf->staticfile.off, len); + gf->staticfile.off += len; + unlock_user(ptr, buf, len); + complete(cs, len, 0); +} + +static void staticfile_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, int64_t off, int gdb_whence) +{ + int64_t ret; + + switch (gdb_whence) { + case GDB_SEEK_SET: + ret = off; + break; + case GDB_SEEK_CUR: + ret = gf->staticfile.off + off; + break; + case GDB_SEEK_END: + ret = gf->staticfile.len + off; + break; + default: + ret = -1; + break; + } + if (ret >= 0 && ret <= gf->staticfile.len) { + gf->staticfile.off = ret; + complete(cs, ret, 0); + } else { + complete(cs, -1, EINVAL); + } +} + +static void staticfile_flen(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf) +{ + complete(cs, gf->staticfile.len, 0); +} + +/* + * Console semihosting syscall implementations. + */ + +static void console_read(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + char *ptr; + int ret; + + ptr = lock_user(VERIFY_WRITE, buf, len, 0); + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + ret = qemu_semihosting_console_read(cs, ptr, len); + unlock_user(ptr, buf, ret); + complete(cs, ret, 0); +} + +static void console_write(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); + char *ptr = lock_user(VERIFY_READ, buf, len, 1); + int ret; + + if (!ptr) { + complete(cs, -1, EFAULT); + return; + } + ret = qemu_semihosting_console_write(ptr, len); + unlock_user(ptr, buf, 0); + complete(cs, ret ? ret : -1, ret ? 0 : EIO); +} + +static void console_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong addr) +{ + static const struct stat tty_buf = { + .st_mode = 020666, /* S_IFCHR, ugo+rw */ + .st_rdev = 5, /* makedev(5, 0) -- linux /dev/tty */ + }; + int ret; + + ret = copy_stat_to_user(cs, addr, &tty_buf); + complete(cs, ret ? -1 : 0, ret ? -ret : 0); +} + +#ifndef CONFIG_USER_ONLY +static void console_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, GIOCondition cond, int timeout) +{ + /* The semihosting console does not support urgent data or errors. */ + cond &= G_IO_IN | G_IO_OUT; + + /* + * Since qemu_semihosting_console_write never blocks, we can + * consider output always ready -- leave G_IO_OUT alone. + * All that remains is to conditionally signal input ready. + * Since output ready causes an immediate return, only block + * for G_IO_IN alone. + * + * TODO: Implement proper timeout. For now, only support + * indefinite wait or immediate poll. + */ + if (cond == G_IO_IN && timeout < 0) { + qemu_semihosting_console_block_until_ready(cs); + /* We returned -- input must be ready. */ + } else if ((cond & G_IO_IN) && !qemu_semihosting_console_ready()) { + cond &= ~G_IO_IN; + } + + complete(cs, cond, 0); +} +#endif + +/* + * Syscall entry points. + */ + +void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + int gdb_flags, int mode) +{ + if (use_gdb_syscalls()) { + gdb_open(cs, complete, fname, fname_len, gdb_flags, mode); + } else { + host_open(cs, complete, fname, fname_len, gdb_flags, mode); + } +} + +void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, int fd) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, -1, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_close(cs, complete, gf); + break; + case GuestFDHost: + host_close(cs, complete, gf); + break; + case GuestFDStatic: + case GuestFDConsole: + complete(cs, 0, 0); + break; + default: + g_assert_not_reached(); + } + dealloc_guestfd(fd); +} + +void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + /* + * Bound length for 64-bit guests on 32-bit hosts, not overflowing ssize_t. + * Note the Linux kernel does this with MAX_RW_COUNT, so it's not a bad + * idea to do this unconditionally. + */ + if (len > INT32_MAX) { + len = INT32_MAX; + } + switch (gf->type) { + case GuestFDGDB: + gdb_read(cs, complete, gf, buf, len); + break; + case GuestFDHost: + host_read(cs, complete, gf, buf, len); + break; + case GuestFDStatic: + staticfile_read(cs, complete, gf, buf, len); + break; + case GuestFDConsole: + console_read(cs, complete, gf, buf, len); + break; + default: + g_assert_not_reached(); + } +} + +void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong buf, target_ulong len) +{ + GuestFD *gf = get_guestfd(fd); + + if (gf) { + semihost_sys_read_gf(cs, complete, gf, buf, len); + } else { + complete(cs, -1, EBADF); + } +} + +void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete, + GuestFD *gf, target_ulong buf, target_ulong len) +{ + /* + * Bound length for 64-bit guests on 32-bit hosts, not overflowing ssize_t. + * Note the Linux kernel does this with MAX_RW_COUNT, so it's not a bad + * idea to do this unconditionally. + */ + if (len > INT32_MAX) { + len = INT32_MAX; + } + switch (gf->type) { + case GuestFDGDB: + gdb_write(cs, complete, gf, buf, len); + break; + case GuestFDHost: + host_write(cs, complete, gf, buf, len); + break; + case GuestFDConsole: + console_write(cs, complete, gf, buf, len); + break; + case GuestFDStatic: + /* Static files are never open for writing: EBADF. */ + complete(cs, -1, EBADF); + break; + default: + g_assert_not_reached(); + } +} + +void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong buf, target_ulong len) +{ + GuestFD *gf = get_guestfd(fd); + + if (gf) { + semihost_sys_write_gf(cs, complete, gf, buf, len); + } else { + complete(cs, -1, EBADF); + } +} + +void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, int64_t off, int gdb_whence) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, -1, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_lseek(cs, complete, gf, off, gdb_whence); + return; + case GuestFDHost: + host_lseek(cs, complete, gf, off, gdb_whence); + break; + case GuestFDStatic: + staticfile_lseek(cs, complete, gf, off, gdb_whence); + break; + case GuestFDConsole: + complete(cs, -1, ESPIPE); + break; + default: + g_assert_not_reached(); + } +} + +void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete, int fd) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, 0, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_isatty(cs, complete, gf); + break; + case GuestFDHost: + host_isatty(cs, complete, gf); + break; + case GuestFDStatic: + complete(cs, 0, ENOTTY); + break; + case GuestFDConsole: + complete(cs, 1, 0); + break; + default: + g_assert_not_reached(); + } +} + +void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb, + gdb_syscall_complete_cb flen_cb, int fd, + target_ulong fstat_addr) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + flen_cb(cs, -1, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_fstat(cs, fstat_cb, gf, fstat_addr); + break; + case GuestFDHost: + host_flen(cs, flen_cb, gf); + break; + case GuestFDStatic: + staticfile_flen(cs, flen_cb, gf); + break; + case GuestFDConsole: + default: + g_assert_not_reached(); + } +} + +void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, target_ulong addr) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, -1, EBADF); + return; + } + switch (gf->type) { + case GuestFDGDB: + gdb_fstat(cs, complete, gf, addr); + break; + case GuestFDHost: + host_fstat(cs, complete, gf, addr); + break; + case GuestFDConsole: + console_fstat(cs, complete, gf, addr); + break; + case GuestFDStatic: + default: + g_assert_not_reached(); + } +} + +void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len, + target_ulong addr) +{ + if (use_gdb_syscalls()) { + gdb_stat(cs, complete, fname, fname_len, addr); + } else { + host_stat(cs, complete, fname, fname_len, addr); + } +} + +void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong fname, target_ulong fname_len) +{ + if (use_gdb_syscalls()) { + gdb_remove(cs, complete, fname, fname_len); + } else { + host_remove(cs, complete, fname, fname_len); + } +} + +void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong oname, target_ulong oname_len, + target_ulong nname, target_ulong nname_len) +{ + if (use_gdb_syscalls()) { + gdb_rename(cs, complete, oname, oname_len, nname, nname_len); + } else { + host_rename(cs, complete, oname, oname_len, nname, nname_len); + } +} + +void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong cmd, target_ulong cmd_len) +{ + if (use_gdb_syscalls()) { + gdb_system(cs, complete, cmd, cmd_len); + } else { + host_system(cs, complete, cmd, cmd_len); + } +} + +void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, + target_ulong tv_addr, target_ulong tz_addr) +{ + if (use_gdb_syscalls()) { + gdb_gettimeofday(cs, complete, tv_addr, tz_addr); + } else { + host_gettimeofday(cs, complete, tv_addr, tz_addr); + } +} + +#ifndef CONFIG_USER_ONLY +void semihost_sys_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, + int fd, GIOCondition cond, int timeout) +{ + GuestFD *gf = get_guestfd(fd); + + if (!gf) { + complete(cs, G_IO_NVAL, 1); + return; + } + switch (gf->type) { + case GuestFDGDB: + complete(cs, G_IO_NVAL, 1); + break; + case GuestFDHost: + host_poll_one(cs, complete, gf, cond, timeout); + break; + case GuestFDConsole: + console_poll_one(cs, complete, gf, cond, timeout); + break; + case GuestFDStatic: + default: + g_assert_not_reached(); + } +} +#endif diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c new file mode 100644 index 0000000000..dc587d73bc --- /dev/null +++ b/semihosting/uaccess.c @@ -0,0 +1,91 @@ +/* + * Helper routines to provide target memory access for semihosting + * syscalls in system emulation mode. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licensed under the GPL + */ + +#include "qemu/osdep.h" +#include "exec/exec-all.h" +#include "semihosting/uaccess.h" + +void *uaccess_lock_user(CPUArchState *env, target_ulong addr, + target_ulong len, bool copy) +{ + void *p = malloc(len); + if (p && copy) { + if (cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0)) { + free(p); + p = NULL; + } + } + return p; +} + +ssize_t uaccess_strlen_user(CPUArchState *env, target_ulong addr) +{ + int mmu_idx = cpu_mmu_index(env_cpu(env), false); + size_t len = 0; + + while (1) { + size_t left_in_page; + int flags; + void *h; + + /* Find the number of bytes remaining in the page. */ + left_in_page = TARGET_PAGE_SIZE - (addr & ~TARGET_PAGE_MASK); + + flags = probe_access_flags(env, addr, 0, MMU_DATA_LOAD, + mmu_idx, true, &h, 0); + if (flags & TLB_INVALID_MASK) { + return -1; + } + if (flags & TLB_MMIO) { + do { + uint8_t c; + if (cpu_memory_rw_debug(env_cpu(env), addr, &c, 1, 0)) { + return -1; + } + if (c == 0) { + return len; + } + addr++; + len++; + if (len > INT32_MAX) { + return -1; + } + } while (--left_in_page != 0); + } else { + char *p = memchr(h, 0, left_in_page); + if (p) { + len += p - (char *)h; + return len <= INT32_MAX ? (ssize_t)len : -1; + } + addr += left_in_page; + len += left_in_page; + if (len > INT32_MAX) { + return -1; + } + } + } +} + +char *uaccess_lock_user_string(CPUArchState *env, target_ulong addr) +{ + ssize_t len = uaccess_strlen_user(env, addr); + if (len < 0) { + return NULL; + } + return uaccess_lock_user(env, addr, len + 1, true); +} + +void uaccess_unlock_user(CPUArchState *env, void *p, + target_ulong addr, target_ulong len) +{ + if (len) { + cpu_memory_rw_debug(env_cpu(env), addr, p, len, 1); + } + free(p); +} diff --git a/slirp b/slirp deleted file mode 160000 index 9d59bb775d..0000000000 --- a/slirp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9d59bb775d6294c8b447a88512f7bb43f12a25a8 diff --git a/softmmu/meson.build b/softmmu/meson.build deleted file mode 100644 index 8138248661..0000000000 --- a/softmmu/meson.build +++ /dev/null @@ -1,35 +0,0 @@ -specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files( - 'arch_init.c', - 'ioport.c', - 'memory.c', - 'physmem.c', - 'qtest.c', -)]) - -specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: [files( - 'icount.c' -)]) - -softmmu_ss.add(files( - 'balloon.c', - 'bootdevice.c', - 'cpus.c', - 'cpu-throttle.c', - 'cpu-timers.c', - 'datadir.c', - 'dma-helpers.c', - 'globals.c', - 'memory_mapping.c', - 'qdev-monitor.c', - 'rtc.c', - 'runstate-action.c', - 'runstate.c', - 'vl.c', -), sdl, libpmem, libdaxctl) - -if have_tpm - softmmu_ss.add(files('tpm.c')) -endif - -softmmu_ss.add(when: seccomp, if_true: files('qemu-seccomp.c')) -softmmu_ss.add(when: fdt, if_true: files('device_tree.c')) diff --git a/softmmu/trace.h b/softmmu/trace.h deleted file mode 100644 index 2ad1011572..0000000000 --- a/softmmu/trace.h +++ /dev/null @@ -1 +0,0 @@ -#include "trace/trace-softmmu.h" diff --git a/stats/meson.build b/stats/meson.build new file mode 100644 index 0000000000..0728dafcd1 --- /dev/null +++ b/stats/meson.build @@ -0,0 +1 @@ +system_ss.add(files('stats-hmp-cmds.c', 'stats-qmp-cmds.c')) diff --git a/stats/stats-hmp-cmds.c b/stats/stats-hmp-cmds.c new file mode 100644 index 0000000000..1f91bf8bd5 --- /dev/null +++ b/stats/stats-hmp-cmds.c @@ -0,0 +1,252 @@ +/* + * HMP commands related to stats + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qapi/qapi-commands-stats.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qemu/cutils.h" +#include "hw/core/cpu.h" +#include "qapi/qmp/qdict.h" +#include "qapi/error.h" + +static void print_stats_schema_value(Monitor *mon, StatsSchemaValue *value) +{ + const char *unit = NULL; + monitor_printf(mon, " %s (%s%s", value->name, StatsType_str(value->type), + value->has_unit || value->exponent ? ", " : ""); + + if (value->has_unit) { + if (value->unit == STATS_UNIT_SECONDS) { + unit = "s"; + } else if (value->unit == STATS_UNIT_BYTES) { + unit = "B"; + } + } + + if (unit && value->base == 10 && + value->exponent >= -18 && value->exponent <= 18 && + value->exponent % 3 == 0) { + monitor_puts(mon, si_prefix(value->exponent)); + } else if (unit && value->base == 2 && + value->exponent >= 0 && value->exponent <= 60 && + value->exponent % 10 == 0) { + + monitor_puts(mon, iec_binary_prefix(value->exponent)); + } else if (value->exponent) { + /* Use exponential notation and write the unit's English name */ + monitor_printf(mon, "* %d^%d%s", + value->base, value->exponent, + value->has_unit ? " " : ""); + unit = NULL; + } + + if (value->has_unit) { + monitor_puts(mon, unit ? unit : StatsUnit_str(value->unit)); + } + + /* Print bucket size for linear histograms */ + if (value->type == STATS_TYPE_LINEAR_HISTOGRAM && value->has_bucket_size) { + monitor_printf(mon, ", bucket size=%d", value->bucket_size); + } + monitor_printf(mon, ")"); +} + +static StatsSchemaValueList *find_schema_value_list( + StatsSchemaList *list, StatsProvider provider, + StatsTarget target) +{ + StatsSchemaList *node; + + for (node = list; node; node = node->next) { + if (node->value->provider == provider && + node->value->target == target) { + return node->value->stats; + } + } + return NULL; +} + +static void print_stats_results(Monitor *mon, StatsTarget target, + bool show_provider, + StatsResult *result, + StatsSchemaList *schema) +{ + /* Find provider schema */ + StatsSchemaValueList *schema_value_list = + find_schema_value_list(schema, result->provider, target); + StatsList *stats_list; + + if (!schema_value_list) { + monitor_printf(mon, "failed to find schema list for %s\n", + StatsProvider_str(result->provider)); + return; + } + + if (show_provider) { + monitor_printf(mon, "provider: %s\n", + StatsProvider_str(result->provider)); + } + + for (stats_list = result->stats; stats_list; + stats_list = stats_list->next, + schema_value_list = schema_value_list->next) { + + Stats *stats = stats_list->value; + StatsValue *stats_value = stats->value; + StatsSchemaValue *schema_value = schema_value_list->value; + + /* Find schema entry */ + while (!g_str_equal(stats->name, schema_value->name)) { + if (!schema_value_list->next) { + monitor_printf(mon, "failed to find schema entry for %s\n", + stats->name); + return; + } + schema_value_list = schema_value_list->next; + schema_value = schema_value_list->value; + } + + print_stats_schema_value(mon, schema_value); + + if (stats_value->type == QTYPE_QNUM) { + monitor_printf(mon, ": %" PRId64 "\n", stats_value->u.scalar); + } else if (stats_value->type == QTYPE_QBOOL) { + monitor_printf(mon, ": %s\n", stats_value->u.boolean ? "yes" : "no"); + } else if (stats_value->type == QTYPE_QLIST) { + uint64List *list; + int i; + + monitor_printf(mon, ": "); + for (list = stats_value->u.list, i = 1; + list; + list = list->next, i++) { + monitor_printf(mon, "[%d]=%" PRId64 " ", i, list->value); + } + monitor_printf(mon, "\n"); + } + } +} + +/* Create the StatsFilter that is needed for an "info stats" invocation. */ +static StatsFilter *stats_filter(StatsTarget target, const char *names, + int cpu_index, StatsProvider provider) +{ + StatsFilter *filter = g_malloc0(sizeof(*filter)); + StatsProvider provider_idx; + StatsRequestList *request_list = NULL; + + filter->target = target; + switch (target) { + case STATS_TARGET_VM: + break; + case STATS_TARGET_VCPU: + { + strList *vcpu_list = NULL; + CPUState *cpu = qemu_get_cpu(cpu_index); + char *canonical_path = object_get_canonical_path(OBJECT(cpu)); + + QAPI_LIST_PREPEND(vcpu_list, canonical_path); + filter->u.vcpu.has_vcpus = true; + filter->u.vcpu.vcpus = vcpu_list; + break; + } + case STATS_TARGET_CRYPTODEV: + break; + default: + break; + } + + if (!names && provider == STATS_PROVIDER__MAX) { + return filter; + } + + /* + * "info stats" can only query either one or all the providers. Querying + * by name, but not by provider, requires the creation of one filter per + * provider. + */ + for (provider_idx = 0; provider_idx < STATS_PROVIDER__MAX; provider_idx++) { + if (provider == STATS_PROVIDER__MAX || provider == provider_idx) { + StatsRequest *request = g_new0(StatsRequest, 1); + request->provider = provider_idx; + if (names && !g_str_equal(names, "*")) { + request->has_names = true; + request->names = hmp_split_at_comma(names); + } + QAPI_LIST_PREPEND(request_list, request); + } + } + + filter->has_providers = true; + filter->providers = request_list; + return filter; +} + +void hmp_info_stats(Monitor *mon, const QDict *qdict) +{ + const char *target_str = qdict_get_str(qdict, "target"); + const char *provider_str = qdict_get_try_str(qdict, "provider"); + const char *names = qdict_get_try_str(qdict, "names"); + + StatsProvider provider = STATS_PROVIDER__MAX; + StatsTarget target; + Error *err = NULL; + g_autoptr(StatsSchemaList) schema = NULL; + g_autoptr(StatsResultList) stats = NULL; + g_autoptr(StatsFilter) filter = NULL; + StatsResultList *entry; + + target = qapi_enum_parse(&StatsTarget_lookup, target_str, -1, &err); + if (err) { + monitor_printf(mon, "invalid stats target %s\n", target_str); + goto exit_no_print; + } + if (provider_str) { + provider = qapi_enum_parse(&StatsProvider_lookup, provider_str, -1, &err); + if (err) { + monitor_printf(mon, "invalid stats provider %s\n", provider_str); + goto exit_no_print; + } + } + + schema = qmp_query_stats_schemas(provider_str ? true : false, + provider, &err); + if (err) { + goto exit; + } + + switch (target) { + case STATS_TARGET_VM: + filter = stats_filter(target, names, -1, provider); + break; + case STATS_TARGET_VCPU: {} + int cpu_index = monitor_get_cpu_index(mon); + filter = stats_filter(target, names, cpu_index, provider); + break; + case STATS_TARGET_CRYPTODEV: + filter = stats_filter(target, names, -1, provider); + break; + default: + abort(); + } + + stats = qmp_query_stats(filter, &err); + if (err) { + goto exit; + } + for (entry = stats; entry; entry = entry->next) { + print_stats_results(mon, target, provider_str == NULL, entry->value, schema); + } + +exit: + if (err) { + monitor_printf(mon, "%s\n", error_get_pretty(err)); + } +exit_no_print: + error_free(err); +} diff --git a/stats/stats-qmp-cmds.c b/stats/stats-qmp-cmds.c new file mode 100644 index 0000000000..e214b964fd --- /dev/null +++ b/stats/stats-qmp-cmds.c @@ -0,0 +1,164 @@ +/* + * QMP commands related to stats + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "sysemu/stats.h" +#include "qapi/qapi-commands-stats.h" +#include "qemu/queue.h" +#include "qapi/error.h" + +typedef struct StatsCallbacks { + StatsProvider provider; + StatRetrieveFunc *stats_cb; + SchemaRetrieveFunc *schemas_cb; + QTAILQ_ENTRY(StatsCallbacks) next; +} StatsCallbacks; + +static QTAILQ_HEAD(, StatsCallbacks) stats_callbacks = + QTAILQ_HEAD_INITIALIZER(stats_callbacks); + +void add_stats_callbacks(StatsProvider provider, + StatRetrieveFunc *stats_fn, + SchemaRetrieveFunc *schemas_fn) +{ + StatsCallbacks *entry = g_new(StatsCallbacks, 1); + entry->provider = provider; + entry->stats_cb = stats_fn; + entry->schemas_cb = schemas_fn; + + QTAILQ_INSERT_TAIL(&stats_callbacks, entry, next); +} + +static bool invoke_stats_cb(StatsCallbacks *entry, + StatsResultList **stats_results, + StatsFilter *filter, StatsRequest *request, + Error **errp) +{ + ERRP_GUARD(); + strList *targets = NULL; + strList *names = NULL; + + if (request) { + if (request->provider != entry->provider) { + return true; + } + if (request->has_names && !request->names) { + return true; + } + names = request->has_names ? request->names : NULL; + } + + switch (filter->target) { + case STATS_TARGET_VM: + break; + case STATS_TARGET_VCPU: + if (filter->u.vcpu.has_vcpus) { + if (!filter->u.vcpu.vcpus) { + /* No targets allowed? Return no statistics. */ + return true; + } + targets = filter->u.vcpu.vcpus; + } + break; + case STATS_TARGET_CRYPTODEV: + break; + default: + abort(); + } + + entry->stats_cb(stats_results, filter->target, names, targets, errp); + if (*errp) { + qapi_free_StatsResultList(*stats_results); + *stats_results = NULL; + return false; + } + return true; +} + +StatsResultList *qmp_query_stats(StatsFilter *filter, Error **errp) +{ + StatsResultList *stats_results = NULL; + StatsCallbacks *entry; + StatsRequestList *request; + + QTAILQ_FOREACH(entry, &stats_callbacks, next) { + if (filter->has_providers) { + for (request = filter->providers; request; request = request->next) { + if (!invoke_stats_cb(entry, &stats_results, filter, + request->value, errp)) { + break; + } + } + } else { + if (!invoke_stats_cb(entry, &stats_results, filter, NULL, errp)) { + break; + } + } + } + + return stats_results; +} + +StatsSchemaList *qmp_query_stats_schemas(bool has_provider, + StatsProvider provider, + Error **errp) +{ + ERRP_GUARD(); + StatsSchemaList *stats_results = NULL; + StatsCallbacks *entry; + + QTAILQ_FOREACH(entry, &stats_callbacks, next) { + if (!has_provider || provider == entry->provider) { + entry->schemas_cb(&stats_results, errp); + if (*errp) { + qapi_free_StatsSchemaList(stats_results); + return NULL; + } + } + } + + return stats_results; +} + +void add_stats_entry(StatsResultList **stats_results, StatsProvider provider, + const char *qom_path, StatsList *stats_list) +{ + StatsResult *entry = g_new0(StatsResult, 1); + + entry->provider = provider; + entry->qom_path = g_strdup(qom_path); + entry->stats = stats_list; + + QAPI_LIST_PREPEND(*stats_results, entry); +} + +void add_stats_schema(StatsSchemaList **schema_results, + StatsProvider provider, StatsTarget target, + StatsSchemaValueList *stats_list) +{ + StatsSchema *entry = g_new0(StatsSchema, 1); + + entry->provider = provider; + entry->target = target; + entry->stats = stats_list; + QAPI_LIST_PREPEND(*schema_results, entry); +} + +bool apply_str_list_filter(const char *string, strList *list) +{ + strList *str_list = NULL; + + if (!list) { + return true; + } + for (str_list = list; str_list; str_list = str_list->next) { + if (g_str_equal(string, str_list->value)) { + return true; + } + } + return false; +} diff --git a/storage-daemon/meson.build b/storage-daemon/meson.build index 49c9d2eac9..46267b63e7 100644 --- a/storage-daemon/meson.build +++ b/storage-daemon/meson.build @@ -5,7 +5,7 @@ qsd_ss.add(blockdev, chardev, qmp, qom, qemuutil, gnutls) subdir('qapi') if have_tools - qsd_ss = qsd_ss.apply(config_host, strict: false) + qsd_ss = qsd_ss.apply({}) qsd = executable('qemu-storage-daemon', qsd_ss.sources(), dependencies: qsd_ss.dependencies(), diff --git a/storage-daemon/qapi/qapi-schema.json b/storage-daemon/qapi/qapi-schema.json index 67749d1101..f10c949490 100644 --- a/storage-daemon/qapi/qapi-schema.json +++ b/storage-daemon/qapi/qapi-schema.json @@ -15,18 +15,26 @@ { 'include': '../../qapi/pragma.json' } +# Documentation generated with qapi-gen.py is in source order, with +# included sub-schemas inserted at the first include directive +# (subsequent include directives have no effect). To get a sane and +# stable order, it's best to include each sub-schema just once, or +# include it first right here. + +{ 'include': '../../qapi/common.json' } +{ 'include': '../../qapi/sockets.json' } +{ 'include': '../../qapi/crypto.json' } +{ 'include': '../../qapi/job.json' } + ## # = Block devices ## { 'include': '../../qapi/block-core.json' } { 'include': '../../qapi/block-export.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/authz.json' } -{ 'include': '../../qapi/qom.json' } -{ 'include': '../../qapi/sockets.json' } { 'include': '../../qapi/transaction.json' } +{ 'include': '../../qapi/control.json' } +{ 'include': '../../qapi/introspect.json' } +{ 'include': '../../qapi/qom.json' } diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c index c104817cdd..0e9354faa6 100644 --- a/storage-daemon/qemu-storage-daemon.c +++ b/storage-daemon/qemu-storage-daemon.c @@ -48,6 +48,7 @@ #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qemu/help_option.h" +#include "qemu/job.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/module.h" @@ -61,6 +62,7 @@ #include "trace/control.h" static const char *pid_file; +static char *pid_file_realpath; static volatile bool exit_requested = false; void qemu_system_killed(int signal, pid_t pid) @@ -121,6 +123,16 @@ static void help(void) " vhost-user-blk device over file descriptor\n" "\n" #endif /* CONFIG_VHOST_USER_BLK_SERVER */ +#ifdef CONFIG_VDUSE_BLK_EXPORT +" --export [type=]vduse-blk,id=,node-name=\n" +" ,name=[,writable=on|off]\n" +" [,num-queues=][,queue-size=]\n" +" [,logical-block-size=]\n" +" [,serial=]\n" +" export the specified block node as a\n" +" vduse-blk device\n" +"\n" +#endif /* CONFIG_VDUSE_BLK_EXPORT */ " --monitor [chardev=]name[,mode=control][,pretty[=on|off]]\n" " configure a QMP monitor\n" "\n" @@ -286,7 +298,11 @@ static void process_options(int argc, char *argv[], bool pre_init_pass) } case OPTION_DAEMONIZE: if (os_set_daemonize(true) < 0) { - error_report("--daemonize not supported in this build"); + /* + * --daemonize is parsed before monitor_init_globals(), so + * error_report() does not work yet + */ + fprintf(stderr, "--daemonize not supported in this build\n"); exit(EXIT_FAILURE); } break; @@ -349,7 +365,7 @@ static void process_options(int argc, char *argv[], bool pre_init_pass) static void pid_file_cleanup(void) { - unlink(pid_file); + unlink(pid_file_realpath); } static void pid_file_init(void) @@ -365,6 +381,14 @@ static void pid_file_init(void) exit(EXIT_FAILURE); } + pid_file_realpath = g_malloc(PATH_MAX); + if (!realpath(pid_file, pid_file_realpath)) { + error_report("cannot resolve PID file path: %s: %s", + pid_file, strerror(errno)); + unlink(pid_file); + exit(EXIT_FAILURE); + } + atexit(pid_file_cleanup); } @@ -387,7 +411,7 @@ int main(int argc, char *argv[]) qemu_add_opts(&qemu_trace_opts); qcrypto_init(&error_fatal); bdrv_init(); - monitor_init_globals_core(); + monitor_init_globals(); init_qmp_commands(); if (!trace_init_backends()) { diff --git a/stubs/colo-compare.c b/stubs/colo-compare.c new file mode 100644 index 0000000000..ec726665be --- /dev/null +++ b/stubs/colo-compare.c @@ -0,0 +1,7 @@ +#include "qemu/osdep.h" +#include "qemu/notify.h" +#include "net/colo-compare.h" + +void colo_compare_cleanup(void) +{ +} diff --git a/stubs/colo.c b/stubs/colo.c new file mode 100644 index 0000000000..f8c069b739 --- /dev/null +++ b/stubs/colo.c @@ -0,0 +1,36 @@ +#include "qemu/osdep.h" +#include "qemu/notify.h" +#include "net/colo-compare.h" +#include "migration/colo.h" +#include "qemu/error-report.h" +#include "qapi/qapi-commands-migration.h" + +void colo_shutdown(void) +{ +} + +int coroutine_fn colo_incoming_co(void) +{ + return 0; +} + +void colo_checkpoint_delay_set(void) +{ +} + +void migrate_start_colo_process(MigrationState *s) +{ + error_report("Impossible happened: trying to start COLO when COLO " + "module is not built in"); + abort(); +} + +bool migration_in_colo_state(void) +{ + return false; +} + +bool migration_incoming_in_colo_state(void) +{ + return false; +} diff --git a/stubs/gdbstub.c b/stubs/gdbstub.c index 2b7aee50d3..580e20702b 100644 --- a/stubs/gdbstub.c +++ b/stubs/gdbstub.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" -#include "exec/gdbstub.h" /* xml_builtin */ +#include "exec/gdbstub.h" /* gdb_static_features */ -const char *const xml_builtin[][2] = { - { NULL, NULL } +const GDBFeature gdb_static_features[] = { + { NULL } }; diff --git a/stubs/graph-lock.c b/stubs/graph-lock.c new file mode 100644 index 0000000000..177aa0a8ba --- /dev/null +++ b/stubs/graph-lock.c @@ -0,0 +1,10 @@ +#include "qemu/osdep.h" +#include "block/graph-lock.h" + +void register_aiocontext(AioContext *ctx) +{ +} + +void unregister_aiocontext(AioContext *ctx) +{ +} diff --git a/stubs/icount.c b/stubs/icount.c index 6df8c2bf7d..9f9a59f55b 100644 --- a/stubs/icount.c +++ b/stubs/icount.c @@ -4,37 +4,20 @@ /* icount - Instruction Counter API */ -int use_icount; +ICountMode use_icount = ICOUNT_DISABLED; -void icount_update(CPUState *cpu) -{ - abort(); -} -void icount_configure(QemuOpts *opts, Error **errp) +bool icount_configure(QemuOpts *opts, Error **errp) { /* signal error */ error_setg(errp, "cannot configure icount, TCG support not available"); + + return false; } int64_t icount_get_raw(void) { abort(); return 0; } -int64_t icount_get(void) -{ - abort(); - return 0; -} -int64_t icount_to_ns(int64_t icount) -{ - abort(); - return 0; -} -int64_t icount_round(int64_t count) -{ - abort(); - return 0; -} void icount_start_warp_timer(void) { abort(); @@ -43,7 +26,7 @@ void icount_account_warp_timer(void) { abort(); } - void icount_notify_exit(void) { + abort(); } diff --git a/stubs/iothread-lock.c b/stubs/iothread-lock.c index 5b45b7fc8b..d7890e5581 100644 --- a/stubs/iothread-lock.c +++ b/stubs/iothread-lock.c @@ -1,15 +1,15 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" -bool qemu_mutex_iothread_locked(void) +bool bql_locked(void) { return false; } -void qemu_mutex_lock_iothread_impl(const char *file, int line) +void bql_lock_impl(const char *file, int line) { } -void qemu_mutex_unlock_iothread(void) +void bql_unlock(void) { } diff --git a/stubs/qmp_memory_device.c b/stubs/memory_device.c similarity index 56% rename from stubs/qmp_memory_device.c rename to stubs/memory_device.c index e75cac62dc..15fd93ff67 100644 --- a/stubs/qmp_memory_device.c +++ b/stubs/memory_device.c @@ -10,3 +10,13 @@ uint64_t get_plugged_memory_size(void) { return (uint64_t)-1; } + +unsigned int memory_devices_get_reserved_memslots(void) +{ + return 0; +} + +bool memory_devices_memslot_auto_decision_active(void) +{ + return false; +} diff --git a/stubs/meson.build b/stubs/meson.build index 6f80fec761..0bf25e6ca5 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -13,6 +13,7 @@ stub_ss.add(files('error-printf.c')) stub_ss.add(files('fdset.c')) stub_ss.add(files('gdbstub.c')) stub_ss.add(files('get-vm-name.c')) +stub_ss.add(files('graph-lock.c')) if linux_io_uring.found() stub_ss.add(files('io_uring.c')) endif @@ -29,8 +30,9 @@ stub_ss.add(files('migr-blocker.c')) stub_ss.add(files('module-opts.c')) stub_ss.add(files('monitor.c')) stub_ss.add(files('monitor-core.c')) +stub_ss.add(files('physmem.c')) stub_ss.add(files('qemu-timer-notify-cb.c')) -stub_ss.add(files('qmp_memory_device.c')) +stub_ss.add(files('memory_device.c')) stub_ss.add(files('qmp-command-available.c')) stub_ss.add(files('qmp-quit.c')) stub_ss.add(files('qtest.c')) @@ -43,12 +45,13 @@ stub_ss.add(files('target-get-monitor-def.c')) stub_ss.add(files('target-monitor-defs.c')) stub_ss.add(files('trace-control.c')) stub_ss.add(files('uuid.c')) -stub_ss.add(files('vmgenid.c')) +stub_ss.add(files('colo.c')) +stub_ss.add(files('colo-compare.c')) stub_ss.add(files('vmstate.c')) stub_ss.add(files('vm-stop.c')) stub_ss.add(files('win32-kbd-hook.c')) stub_ss.add(files('cpu-synchronize-state.c')) -if have_block +if have_block or have_ga stub_ss.add(files('replay-tools.c')) endif if have_system @@ -57,6 +60,8 @@ if have_system stub_ss.add(files('semihost.c')) stub_ss.add(files('usb-dev-stub.c')) stub_ss.add(files('xen-hw-stub.c')) + stub_ss.add(files('virtio-md-pci.c')) else stub_ss.add(files('qdev.c')) endif +stub_ss.add(files('semihost-all.c')) diff --git a/stubs/migr-blocker.c b/stubs/migr-blocker.c index 5676a2f93c..11cbff268f 100644 --- a/stubs/migr-blocker.c +++ b/stubs/migr-blocker.c @@ -1,11 +1,21 @@ #include "qemu/osdep.h" #include "migration/blocker.h" -int migrate_add_blocker(Error *reason, Error **errp) +int migrate_add_blocker(Error **reasonp, Error **errp) { return 0; } -void migrate_del_blocker(Error *reason) +int migrate_add_blocker_normal(Error **reasonp, Error **errp) +{ + return 0; +} + +int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...) +{ + return 0; +} + +void migrate_del_blocker(Error **reasonp) { } diff --git a/stubs/physmem.c b/stubs/physmem.c new file mode 100644 index 0000000000..1fc5f2df29 --- /dev/null +++ b/stubs/physmem.c @@ -0,0 +1,13 @@ +#include "qemu/osdep.h" +#include "exec/cpu-common.h" + +RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset, + ram_addr_t *offset) +{ + return NULL; +} + +int qemu_ram_get_fd(RAMBlock *rb) +{ + return -1; +} diff --git a/stubs/qdev.c b/stubs/qdev.c index 187659f707..6869f6f90a 100644 --- a/stubs/qdev.c +++ b/stubs/qdev.c @@ -15,15 +15,13 @@ #include "qemu/osdep.h" #include "qapi/qapi-events-qdev.h" -void qapi_event_send_device_deleted(bool has_device, - const char *device, +void qapi_event_send_device_deleted(const char *device, const char *path) { /* Nothing to do. */ } -void qapi_event_send_device_unplug_guest_error(bool has_device, - const char *device, +void qapi_event_send_device_unplug_guest_error(const char *device, const char *path) { /* Nothing to do. */ diff --git a/stubs/ramfb.c b/stubs/ramfb.c index 48143f3354..cf64733b10 100644 --- a/stubs/ramfb.c +++ b/stubs/ramfb.c @@ -2,6 +2,8 @@ #include "qapi/error.h" #include "hw/display/ramfb.h" +const VMStateDescription ramfb_vmstate = {}; + void ramfb_display_update(QemuConsole *con, RAMFBState *s) { } diff --git a/stubs/replay-tools.c b/stubs/replay-tools.c index 43296b3d4e..3e8ca3212d 100644 --- a/stubs/replay-tools.c +++ b/stubs/replay-tools.c @@ -7,13 +7,14 @@ bool replay_events_enabled(void) return false; } -int64_t replay_save_clock(unsigned int kind, int64_t clock, int64_t raw_icount) +int64_t replay_save_clock(ReplayClockKind kind, + int64_t clock, int64_t raw_icount) { abort(); return 0; } -int64_t replay_read_clock(unsigned int kind, int64_t raw_icount) +int64_t replay_read_clock(ReplayClockKind kind, int64_t raw_icount) { abort(); return 0; @@ -48,11 +49,11 @@ void replay_mutex_unlock(void) { } -void replay_register_char_driver(Chardev *chr) +void replay_register_char_driver(struct Chardev *chr) { } -void replay_chr_be_write(Chardev *s, uint8_t *buf, int len) +void replay_chr_be_write(struct Chardev *s, const uint8_t *buf, int len) { abort(); } diff --git a/stubs/replay.c b/stubs/replay.c index 9d5b4be339..42c92e4acb 100644 --- a/stubs/replay.c +++ b/stubs/replay.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" ReplayMode replay_mode; diff --git a/stubs/semihost-all.c b/stubs/semihost-all.c new file mode 100644 index 0000000000..a2a1fc9c6f --- /dev/null +++ b/stubs/semihost-all.c @@ -0,0 +1,17 @@ +/* + * Semihosting Stubs for all targets + * + * Copyright (c) 2023 Linaro Ltd + * + * Stubs for all targets that don't actually do semihosting. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "semihosting/semihost.h" + +SemihostingTarget semihosting_get_target(void) +{ + return SEMIHOSTING_TARGET_AUTO; +} diff --git a/stubs/semihost.c b/stubs/semihost.c index 4bf2cf71b9..f26cbb7c25 100644 --- a/stubs/semihost.c +++ b/stubs/semihost.c @@ -1,9 +1,9 @@ /* - * Semihosting Stubs for SoftMMU + * Semihosting Stubs for system emulation * * Copyright (c) 2019 Linaro Ltd * - * Stubs for SoftMMU targets that don't actually do semihosting. + * Stubs for system targets that don't actually do semihosting. * * SPDX-License-Identifier: GPL-2.0-or-later */ @@ -23,16 +23,11 @@ QemuOptsList qemu_semihosting_config_opts = { }; /* Queries to config status default to off */ -bool semihosting_enabled(void) +bool semihosting_enabled(bool is_user) { return false; } -SemihostingTarget semihosting_get_target(void) -{ - return SEMIHOSTING_TARGET_AUTO; -} - /* * All the rest are empty subs. We could g_assert_not_reached() but * that adds extra weight to the final binary. Waste not want not. @@ -41,7 +36,7 @@ void qemu_semihosting_enable(void) { } -int qemu_semihosting_config_options(const char *optarg) +int qemu_semihosting_config_options(const char *optstr) { return 1; } @@ -65,10 +60,6 @@ void semihosting_arg_fallback(const char *file, const char *cmd) { } -void qemu_semihosting_connect_chardevs(void) -{ -} - -void qemu_semihosting_console_init(void) +void qemu_semihosting_chardev_init(void) { } diff --git a/stubs/trace-control.c b/stubs/trace-control.c index 7f856e5c24..b428f34c87 100644 --- a/stubs/trace-control.c +++ b/stubs/trace-control.c @@ -36,16 +36,3 @@ void trace_event_set_state_dynamic(TraceEvent *ev, bool state) } } } - -void trace_event_set_vcpu_state_dynamic(CPUState *vcpu, - TraceEvent *ev, bool state) -{ - /* should never be called on non-target binaries */ - abort(); -} - -void trace_init_vcpu(CPUState *vcpu) -{ - /* should never be called on non-target binaries */ - abort(); -} diff --git a/stubs/virtio-md-pci.c b/stubs/virtio-md-pci.c new file mode 100644 index 0000000000..ce5bba0c9d --- /dev/null +++ b/stubs/virtio-md-pci.c @@ -0,0 +1,24 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/virtio/virtio-md-pci.h" + +void virtio_md_pci_pre_plug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp) +{ + error_setg(errp, "virtio based memory devices not supported"); +} + +void virtio_md_pci_plug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp) +{ + error_setg(errp, "virtio based memory devices not supported"); +} + +void virtio_md_pci_unplug_request(VirtIOMDPCI *vmd, MachineState *ms, + Error **errp) +{ + error_setg(errp, "virtio based memory devices not supported"); +} + +void virtio_md_pci_unplug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp) +{ + error_setg(errp, "virtio based memory devices not supported"); +} diff --git a/stubs/vmgenid.c b/stubs/vmgenid.c deleted file mode 100644 index bfad656c6c..0000000000 --- a/stubs/vmgenid.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qapi/qapi-commands-machine.h" -#include "qapi/qmp/qerror.h" - -GuidInfo *qmp_query_vm_generation_id(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} diff --git a/stubs/xen-hw-stub.c b/stubs/xen-hw-stub.c index 15f3921a76..6cf0e9a4c1 100644 --- a/stubs/xen-hw-stub.c +++ b/stubs/xen-hw-stub.c @@ -15,16 +15,13 @@ int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) return -1; } -void xen_piix3_set_irq(void *opaque, int irq_num, int level) +void xen_intx_set_irq(void *opaque, int irq_num, int level) { } -void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len) -{ -} - -void xen_hvm_inject_msi(uint64_t addr, uint32_t data) +int xen_set_pci_link_route(uint8_t link, uint8_t irq) { + return -1; } int xen_is_pirq_msi(uint32_t msi_data) diff --git a/subprojects/.gitignore b/subprojects/.gitignore new file mode 100644 index 0000000000..adca0266be --- /dev/null +++ b/subprojects/.gitignore @@ -0,0 +1,8 @@ +/packagecache + +/berkeley-softfloat-3 +/berkeley-testfloat-3 +/dtc +/keycodemapdb +/libvfio-user +/slirp diff --git a/subprojects/berkeley-softfloat-3.wrap b/subprojects/berkeley-softfloat-3.wrap new file mode 100644 index 0000000000..c3e356d42f --- /dev/null +++ b/subprojects/berkeley-softfloat-3.wrap @@ -0,0 +1,5 @@ +[wrap-git] +url = https://gitlab.com/qemu-project/berkeley-softfloat-3.git +revision = b64af41c3276f97f0e181920400ee056b9c88037 +patch_directory = berkeley-softfloat-3 +depth = 1 diff --git a/subprojects/berkeley-testfloat-3.wrap b/subprojects/berkeley-testfloat-3.wrap new file mode 100644 index 0000000000..b8b12e7629 --- /dev/null +++ b/subprojects/berkeley-testfloat-3.wrap @@ -0,0 +1,5 @@ +[wrap-git] +url = https://gitlab.com/qemu-project/berkeley-testfloat-3.git +revision = e7af9751d9f9fd3b47911f51a5cfd08af256a9ab +patch_directory = berkeley-testfloat-3 +depth = 1 diff --git a/subprojects/dtc.wrap b/subprojects/dtc.wrap new file mode 100644 index 0000000000..d1bc9174e9 --- /dev/null +++ b/subprojects/dtc.wrap @@ -0,0 +1,4 @@ +[wrap-git] +url = https://gitlab.com/qemu-project/dtc.git +revision = b6910bec11614980a21e46fbccc35934b671bd81 +depth = 1 diff --git a/subprojects/keycodemapdb.wrap b/subprojects/keycodemapdb.wrap new file mode 100644 index 0000000000..dda7b0e571 --- /dev/null +++ b/subprojects/keycodemapdb.wrap @@ -0,0 +1,4 @@ +[wrap-git] +url = https://gitlab.com/qemu-project/keycodemapdb.git +revision = f5772a62ec52591ff6870b7e8ef32482371f22c6 +depth = 1 diff --git a/subprojects/libblkio.wrap b/subprojects/libblkio.wrap new file mode 100644 index 0000000000..f77af72210 --- /dev/null +++ b/subprojects/libblkio.wrap @@ -0,0 +1,6 @@ +[wrap-git] +url = https://gitlab.com/libblkio/libblkio +revision = f84cc963a444e4cb34813b2dcfc5bf8526947dc0 + +[provide] +blkio = libblkio_dep diff --git a/subprojects/libvduse/include/atomic.h b/subprojects/libvduse/include/atomic.h new file mode 120000 index 0000000000..8c2be64f7b --- /dev/null +++ b/subprojects/libvduse/include/atomic.h @@ -0,0 +1 @@ +../../../include/qemu/atomic.h \ No newline at end of file diff --git a/subprojects/libvduse/include/compiler.h b/subprojects/libvduse/include/compiler.h new file mode 120000 index 0000000000..de7b70697c --- /dev/null +++ b/subprojects/libvduse/include/compiler.h @@ -0,0 +1 @@ +../../../include/qemu/compiler.h \ No newline at end of file diff --git a/subprojects/libvduse/libvduse.c b/subprojects/libvduse/libvduse.c new file mode 100644 index 0000000000..21ffbb5b8d --- /dev/null +++ b/subprojects/libvduse/libvduse.c @@ -0,0 +1,1381 @@ +/* + * VDUSE (vDPA Device in Userspace) library + * + * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved. + * Portions of codes and concepts borrowed from libvhost-user.c, so: + * Copyright IBM, Corp. 2007 + * Copyright (c) 2016 Red Hat, Inc. + * + * Author: + * Xie Yongji + * Anthony Liguori + * Marc-André Lureau + * Victor Kaplansky + * + * 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 _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "include/atomic.h" +#include "linux-headers/linux/virtio_ring.h" +#include "linux-headers/linux/virtio_config.h" +#include "linux-headers/linux/vduse.h" +#include "libvduse.h" + +#define VDUSE_VQ_ALIGN 4096 +#define MAX_IOVA_REGIONS 256 + +#define LOG_ALIGNMENT 64 + +/* Round number down to multiple */ +#define ALIGN_DOWN(n, m) ((n) / (m) * (m)) + +/* Round number up to multiple */ +#define ALIGN_UP(n, m) ALIGN_DOWN((n) + (m) - 1, (m)) + +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif + +typedef struct VduseDescStateSplit { + uint8_t inflight; + uint8_t padding[5]; + uint16_t next; + uint64_t counter; +} VduseDescStateSplit; + +typedef struct VduseVirtqLogInflight { + uint64_t features; + uint16_t version; + uint16_t desc_num; + uint16_t last_batch_head; + uint16_t used_idx; + VduseDescStateSplit desc[]; +} VduseVirtqLogInflight; + +typedef struct VduseVirtqLog { + VduseVirtqLogInflight inflight; +} VduseVirtqLog; + +typedef struct VduseVirtqInflightDesc { + uint16_t index; + uint64_t counter; +} VduseVirtqInflightDesc; + +typedef struct VduseRing { + unsigned int num; + uint64_t desc_addr; + uint64_t avail_addr; + uint64_t used_addr; + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; +} VduseRing; + +struct VduseVirtq { + VduseRing vring; + uint16_t last_avail_idx; + uint16_t shadow_avail_idx; + uint16_t used_idx; + uint16_t signalled_used; + bool signalled_used_valid; + int index; + unsigned int inuse; + bool ready; + int fd; + VduseDev *dev; + VduseVirtqInflightDesc *resubmit_list; + uint16_t resubmit_num; + uint64_t counter; + VduseVirtqLog *log; +}; + +typedef struct VduseIovaRegion { + uint64_t iova; + uint64_t size; + uint64_t mmap_offset; + uint64_t mmap_addr; +} VduseIovaRegion; + +struct VduseDev { + VduseVirtq *vqs; + VduseIovaRegion regions[MAX_IOVA_REGIONS]; + int num_regions; + char *name; + uint32_t device_id; + uint32_t vendor_id; + uint16_t num_queues; + uint16_t queue_size; + uint64_t features; + const VduseOps *ops; + int fd; + int ctrl_fd; + void *priv; + void *log; +}; + +static inline size_t vduse_vq_log_size(uint16_t queue_size) +{ + return ALIGN_UP(sizeof(VduseDescStateSplit) * queue_size + + sizeof(VduseVirtqLogInflight), LOG_ALIGNMENT); +} + +static void *vduse_log_get(const char *filename, size_t size) +{ + void *ptr = MAP_FAILED; + int fd; + + fd = open(filename, O_RDWR | O_CREAT, 0600); + if (fd == -1) { + return MAP_FAILED; + } + + if (ftruncate(fd, size) == -1) { + goto out; + } + + ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + +out: + close(fd); + return ptr; +} + +static inline bool has_feature(uint64_t features, unsigned int fbit) +{ + assert(fbit < 64); + return !!(features & (1ULL << fbit)); +} + +static inline bool vduse_dev_has_feature(VduseDev *dev, unsigned int fbit) +{ + return has_feature(dev->features, fbit); +} + +uint64_t vduse_get_virtio_features(void) +{ + return (1ULL << VIRTIO_F_IOMMU_PLATFORM) | + (1ULL << VIRTIO_F_VERSION_1) | + (1ULL << VIRTIO_F_NOTIFY_ON_EMPTY) | + (1ULL << VIRTIO_RING_F_EVENT_IDX) | + (1ULL << VIRTIO_RING_F_INDIRECT_DESC); +} + +VduseDev *vduse_queue_get_dev(VduseVirtq *vq) +{ + return vq->dev; +} + +int vduse_queue_get_fd(VduseVirtq *vq) +{ + return vq->fd; +} + +void *vduse_dev_get_priv(VduseDev *dev) +{ + return dev->priv; +} + +VduseVirtq *vduse_dev_get_queue(VduseDev *dev, int index) +{ + return &dev->vqs[index]; +} + +int vduse_dev_get_fd(VduseDev *dev) +{ + return dev->fd; +} + +static int vduse_inject_irq(VduseDev *dev, int index) +{ + return ioctl(dev->fd, VDUSE_VQ_INJECT_IRQ, &index); +} + +static int inflight_desc_compare(const void *a, const void *b) +{ + VduseVirtqInflightDesc *desc0 = (VduseVirtqInflightDesc *)a, + *desc1 = (VduseVirtqInflightDesc *)b; + + if (desc1->counter > desc0->counter && + (desc1->counter - desc0->counter) < VIRTQUEUE_MAX_SIZE * 2) { + return 1; + } + + return -1; +} + +static int vduse_queue_check_inflights(VduseVirtq *vq) +{ + int i = 0; + VduseDev *dev = vq->dev; + + vq->used_idx = le16toh(vq->vring.used->idx); + vq->resubmit_num = 0; + vq->resubmit_list = NULL; + vq->counter = 0; + + if (unlikely(vq->log->inflight.used_idx != vq->used_idx)) { + if (vq->log->inflight.last_batch_head > VIRTQUEUE_MAX_SIZE) { + return -1; + } + + vq->log->inflight.desc[vq->log->inflight.last_batch_head].inflight = 0; + + barrier(); + + vq->log->inflight.used_idx = vq->used_idx; + } + + for (i = 0; i < vq->log->inflight.desc_num; i++) { + if (vq->log->inflight.desc[i].inflight == 1) { + vq->inuse++; + } + } + + vq->shadow_avail_idx = vq->last_avail_idx = vq->inuse + vq->used_idx; + + if (vq->inuse) { + vq->resubmit_list = calloc(vq->inuse, sizeof(VduseVirtqInflightDesc)); + if (!vq->resubmit_list) { + return -1; + } + + for (i = 0; i < vq->log->inflight.desc_num; i++) { + if (vq->log->inflight.desc[i].inflight) { + vq->resubmit_list[vq->resubmit_num].index = i; + vq->resubmit_list[vq->resubmit_num].counter = + vq->log->inflight.desc[i].counter; + vq->resubmit_num++; + } + } + + if (vq->resubmit_num > 1) { + qsort(vq->resubmit_list, vq->resubmit_num, + sizeof(VduseVirtqInflightDesc), inflight_desc_compare); + } + vq->counter = vq->resubmit_list[0].counter + 1; + } + + vduse_inject_irq(dev, vq->index); + + return 0; +} + +static int vduse_queue_inflight_get(VduseVirtq *vq, int desc_idx) +{ + vq->log->inflight.desc[desc_idx].counter = vq->counter++; + + barrier(); + + vq->log->inflight.desc[desc_idx].inflight = 1; + + return 0; +} + +static int vduse_queue_inflight_pre_put(VduseVirtq *vq, int desc_idx) +{ + vq->log->inflight.last_batch_head = desc_idx; + + return 0; +} + +static int vduse_queue_inflight_post_put(VduseVirtq *vq, int desc_idx) +{ + vq->log->inflight.desc[desc_idx].inflight = 0; + + barrier(); + + vq->log->inflight.used_idx = vq->used_idx; + + return 0; +} + +static void vduse_iova_remove_region(VduseDev *dev, uint64_t start, + uint64_t last) +{ + int i; + + if (last == start) { + return; + } + + for (i = 0; i < MAX_IOVA_REGIONS; i++) { + if (!dev->regions[i].mmap_addr) { + continue; + } + + if (start <= dev->regions[i].iova && + last >= (dev->regions[i].iova + dev->regions[i].size - 1)) { + munmap((void *)(uintptr_t)dev->regions[i].mmap_addr, + dev->regions[i].mmap_offset + dev->regions[i].size); + dev->regions[i].mmap_addr = 0; + dev->num_regions--; + } + } +} + +static int vduse_iova_add_region(VduseDev *dev, int fd, + uint64_t offset, uint64_t start, + uint64_t last, int prot) +{ + int i; + uint64_t size = last - start + 1; + void *mmap_addr = mmap(0, size + offset, prot, MAP_SHARED, fd, 0); + + if (mmap_addr == MAP_FAILED) { + close(fd); + return -EINVAL; + } + + for (i = 0; i < MAX_IOVA_REGIONS; i++) { + if (!dev->regions[i].mmap_addr) { + dev->regions[i].mmap_addr = (uint64_t)(uintptr_t)mmap_addr; + dev->regions[i].mmap_offset = offset; + dev->regions[i].iova = start; + dev->regions[i].size = size; + dev->num_regions++; + break; + } + } + assert(i < MAX_IOVA_REGIONS); + close(fd); + + return 0; +} + +static int perm_to_prot(uint8_t perm) +{ + int prot = 0; + + switch (perm) { + case VDUSE_ACCESS_WO: + prot |= PROT_WRITE; + break; + case VDUSE_ACCESS_RO: + prot |= PROT_READ; + break; + case VDUSE_ACCESS_RW: + prot |= PROT_READ | PROT_WRITE; + break; + default: + break; + } + + return prot; +} + +static inline void *iova_to_va(VduseDev *dev, uint64_t *plen, uint64_t iova) +{ + int i, ret; + struct vduse_iotlb_entry entry; + + for (i = 0; i < MAX_IOVA_REGIONS; i++) { + VduseIovaRegion *r = &dev->regions[i]; + + if (!r->mmap_addr) { + continue; + } + + if ((iova >= r->iova) && (iova < (r->iova + r->size))) { + if ((iova + *plen) > (r->iova + r->size)) { + *plen = r->iova + r->size - iova; + } + return (void *)(uintptr_t)(iova - r->iova + + r->mmap_addr + r->mmap_offset); + } + } + + entry.start = iova; + entry.last = iova + 1; + ret = ioctl(dev->fd, VDUSE_IOTLB_GET_FD, &entry); + if (ret < 0) { + return NULL; + } + + if (!vduse_iova_add_region(dev, ret, entry.offset, entry.start, + entry.last, perm_to_prot(entry.perm))) { + return iova_to_va(dev, plen, iova); + } + + return NULL; +} + +static inline uint16_t vring_avail_flags(VduseVirtq *vq) +{ + return le16toh(vq->vring.avail->flags); +} + +static inline uint16_t vring_avail_idx(VduseVirtq *vq) +{ + vq->shadow_avail_idx = le16toh(vq->vring.avail->idx); + + return vq->shadow_avail_idx; +} + +static inline uint16_t vring_avail_ring(VduseVirtq *vq, int i) +{ + return le16toh(vq->vring.avail->ring[i]); +} + +static inline uint16_t vring_get_used_event(VduseVirtq *vq) +{ + return vring_avail_ring(vq, vq->vring.num); +} + +static bool vduse_queue_get_head(VduseVirtq *vq, unsigned int idx, + unsigned int *head) +{ + /* + * Grab the next descriptor number they're advertising, and increment + * the index we've seen. + */ + *head = vring_avail_ring(vq, idx % vq->vring.num); + + /* If their number is silly, that's a fatal mistake. */ + if (*head >= vq->vring.num) { + fprintf(stderr, "Guest says index %u is available\n", *head); + return false; + } + + return true; +} + +static int +vduse_queue_read_indirect_desc(VduseDev *dev, struct vring_desc *desc, + uint64_t addr, size_t len) +{ + struct vring_desc *ori_desc; + uint64_t read_len; + + if (len > (VIRTQUEUE_MAX_SIZE * sizeof(struct vring_desc))) { + return -1; + } + + if (len == 0) { + return -1; + } + + while (len) { + read_len = len; + ori_desc = iova_to_va(dev, &read_len, addr); + if (!ori_desc) { + return -1; + } + + memcpy(desc, ori_desc, read_len); + len -= read_len; + addr += read_len; + desc += read_len; + } + + return 0; +} + +enum { + VIRTQUEUE_READ_DESC_ERROR = -1, + VIRTQUEUE_READ_DESC_DONE = 0, /* end of chain */ + VIRTQUEUE_READ_DESC_MORE = 1, /* more buffers in chain */ +}; + +static int vduse_queue_read_next_desc(struct vring_desc *desc, int i, + unsigned int max, unsigned int *next) +{ + /* If this descriptor says it doesn't chain, we're done. */ + if (!(le16toh(desc[i].flags) & VRING_DESC_F_NEXT)) { + return VIRTQUEUE_READ_DESC_DONE; + } + + /* Check they're not leading us off end of descriptors. */ + *next = desc[i].next; + /* Make sure compiler knows to grab that: we don't want it changing! */ + smp_wmb(); + + if (*next >= max) { + fprintf(stderr, "Desc next is %u\n", *next); + return VIRTQUEUE_READ_DESC_ERROR; + } + + return VIRTQUEUE_READ_DESC_MORE; +} + +/* + * Fetch avail_idx from VQ memory only when we really need to know if + * guest has added some buffers. + */ +static bool vduse_queue_empty(VduseVirtq *vq) +{ + if (unlikely(!vq->vring.avail)) { + return true; + } + + if (vq->shadow_avail_idx != vq->last_avail_idx) { + return false; + } + + return vring_avail_idx(vq) == vq->last_avail_idx; +} + +static bool vduse_queue_should_notify(VduseVirtq *vq) +{ + VduseDev *dev = vq->dev; + uint16_t old, new; + bool v; + + /* We need to expose used array entries before checking used event. */ + smp_mb(); + + /* Always notify when queue is empty (when feature acknowledge) */ + if (vduse_dev_has_feature(dev, VIRTIO_F_NOTIFY_ON_EMPTY) && + !vq->inuse && vduse_queue_empty(vq)) { + return true; + } + + if (!vduse_dev_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) { + return !(vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT); + } + + v = vq->signalled_used_valid; + vq->signalled_used_valid = true; + old = vq->signalled_used; + new = vq->signalled_used = vq->used_idx; + return !v || vring_need_event(vring_get_used_event(vq), new, old); +} + +void vduse_queue_notify(VduseVirtq *vq) +{ + VduseDev *dev = vq->dev; + + if (unlikely(!vq->vring.avail)) { + return; + } + + if (!vduse_queue_should_notify(vq)) { + return; + } + + if (vduse_inject_irq(dev, vq->index) < 0) { + fprintf(stderr, "Error inject irq for vq %d: %s\n", + vq->index, strerror(errno)); + } +} + +static inline void vring_set_avail_event(VduseVirtq *vq, uint16_t val) +{ + uint16_t val_le = htole16(val); + memcpy(&vq->vring.used->ring[vq->vring.num], &val_le, sizeof(uint16_t)); +} + +static bool vduse_queue_map_single_desc(VduseVirtq *vq, unsigned int *p_num_sg, + struct iovec *iov, unsigned int max_num_sg, + bool is_write, uint64_t pa, size_t sz) +{ + unsigned num_sg = *p_num_sg; + VduseDev *dev = vq->dev; + + assert(num_sg <= max_num_sg); + + if (!sz) { + fprintf(stderr, "virtio: zero sized buffers are not allowed\n"); + return false; + } + + while (sz) { + uint64_t len = sz; + + if (num_sg == max_num_sg) { + fprintf(stderr, + "virtio: too many descriptors in indirect table\n"); + return false; + } + + iov[num_sg].iov_base = iova_to_va(dev, &len, pa); + if (iov[num_sg].iov_base == NULL) { + fprintf(stderr, "virtio: invalid address for buffers\n"); + return false; + } + iov[num_sg++].iov_len = len; + sz -= len; + pa += len; + } + + *p_num_sg = num_sg; + return true; +} + +static void *vduse_queue_alloc_element(size_t sz, unsigned out_num, + unsigned in_num) +{ + VduseVirtqElement *elem; + size_t in_sg_ofs = ALIGN_UP(sz, __alignof__(elem->in_sg[0])); + size_t out_sg_ofs = in_sg_ofs + in_num * sizeof(elem->in_sg[0]); + size_t out_sg_end = out_sg_ofs + out_num * sizeof(elem->out_sg[0]); + + assert(sz >= sizeof(VduseVirtqElement)); + elem = malloc(out_sg_end); + if (!elem) { + return NULL; + } + elem->out_num = out_num; + elem->in_num = in_num; + elem->in_sg = (void *)elem + in_sg_ofs; + elem->out_sg = (void *)elem + out_sg_ofs; + return elem; +} + +static void *vduse_queue_map_desc(VduseVirtq *vq, unsigned int idx, size_t sz) +{ + struct vring_desc *desc = vq->vring.desc; + VduseDev *dev = vq->dev; + uint64_t desc_addr, read_len; + unsigned int desc_len; + unsigned int max = vq->vring.num; + unsigned int i = idx; + VduseVirtqElement *elem; + struct iovec iov[VIRTQUEUE_MAX_SIZE]; + struct vring_desc desc_buf[VIRTQUEUE_MAX_SIZE]; + unsigned int out_num = 0, in_num = 0; + int rc; + + if (le16toh(desc[i].flags) & VRING_DESC_F_INDIRECT) { + if (le32toh(desc[i].len) % sizeof(struct vring_desc)) { + fprintf(stderr, "Invalid size for indirect buffer table\n"); + return NULL; + } + + /* loop over the indirect descriptor table */ + desc_addr = le64toh(desc[i].addr); + desc_len = le32toh(desc[i].len); + max = desc_len / sizeof(struct vring_desc); + read_len = desc_len; + desc = iova_to_va(dev, &read_len, desc_addr); + if (unlikely(desc && read_len != desc_len)) { + /* Failed to use zero copy */ + desc = NULL; + if (!vduse_queue_read_indirect_desc(dev, desc_buf, + desc_addr, + desc_len)) { + desc = desc_buf; + } + } + if (!desc) { + fprintf(stderr, "Invalid indirect buffer table\n"); + return NULL; + } + i = 0; + } + + /* Collect all the descriptors */ + do { + if (le16toh(desc[i].flags) & VRING_DESC_F_WRITE) { + if (!vduse_queue_map_single_desc(vq, &in_num, iov + out_num, + VIRTQUEUE_MAX_SIZE - out_num, + true, le64toh(desc[i].addr), + le32toh(desc[i].len))) { + return NULL; + } + } else { + if (in_num) { + fprintf(stderr, "Incorrect order for descriptors\n"); + return NULL; + } + if (!vduse_queue_map_single_desc(vq, &out_num, iov, + VIRTQUEUE_MAX_SIZE, false, + le64toh(desc[i].addr), + le32toh(desc[i].len))) { + return NULL; + } + } + + /* If we've got too many, that implies a descriptor loop. */ + if ((in_num + out_num) > max) { + fprintf(stderr, "Looped descriptor\n"); + return NULL; + } + rc = vduse_queue_read_next_desc(desc, i, max, &i); + } while (rc == VIRTQUEUE_READ_DESC_MORE); + + if (rc == VIRTQUEUE_READ_DESC_ERROR) { + fprintf(stderr, "read descriptor error\n"); + return NULL; + } + + /* Now copy what we have collected and mapped */ + elem = vduse_queue_alloc_element(sz, out_num, in_num); + if (!elem) { + fprintf(stderr, "read descriptor error\n"); + return NULL; + } + elem->index = idx; + for (i = 0; i < out_num; i++) { + elem->out_sg[i] = iov[i]; + } + for (i = 0; i < in_num; i++) { + elem->in_sg[i] = iov[out_num + i]; + } + + return elem; +} + +void *vduse_queue_pop(VduseVirtq *vq, size_t sz) +{ + unsigned int head; + VduseVirtqElement *elem; + VduseDev *dev = vq->dev; + int i; + + if (unlikely(!vq->vring.avail)) { + return NULL; + } + + if (unlikely(vq->resubmit_list && vq->resubmit_num > 0)) { + i = (--vq->resubmit_num); + elem = vduse_queue_map_desc(vq, vq->resubmit_list[i].index, sz); + + if (!vq->resubmit_num) { + free(vq->resubmit_list); + vq->resubmit_list = NULL; + } + + return elem; + } + + if (vduse_queue_empty(vq)) { + return NULL; + } + /* Needed after virtio_queue_empty() */ + smp_rmb(); + + if (vq->inuse >= vq->vring.num) { + fprintf(stderr, "Virtqueue size exceeded: %d\n", vq->inuse); + return NULL; + } + + if (!vduse_queue_get_head(vq, vq->last_avail_idx++, &head)) { + return NULL; + } + + if (vduse_dev_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) { + vring_set_avail_event(vq, vq->last_avail_idx); + } + + elem = vduse_queue_map_desc(vq, head, sz); + + if (!elem) { + return NULL; + } + + vq->inuse++; + + vduse_queue_inflight_get(vq, head); + + return elem; +} + +static inline void vring_used_write(VduseVirtq *vq, + struct vring_used_elem *uelem, int i) +{ + struct vring_used *used = vq->vring.used; + + used->ring[i] = *uelem; +} + +static void vduse_queue_fill(VduseVirtq *vq, const VduseVirtqElement *elem, + unsigned int len, unsigned int idx) +{ + struct vring_used_elem uelem; + + if (unlikely(!vq->vring.used)) { + return; + } + + idx = (idx + vq->used_idx) % vq->vring.num; + + uelem.id = htole32(elem->index); + uelem.len = htole32(len); + vring_used_write(vq, &uelem, idx); +} + +static inline void vring_used_idx_set(VduseVirtq *vq, uint16_t val) +{ + vq->vring.used->idx = htole16(val); + vq->used_idx = val; +} + +static void vduse_queue_flush(VduseVirtq *vq, unsigned int count) +{ + uint16_t old, new; + + if (unlikely(!vq->vring.used)) { + return; + } + + /* Make sure buffer is written before we update index. */ + smp_wmb(); + + old = vq->used_idx; + new = old + count; + vring_used_idx_set(vq, new); + vq->inuse -= count; + if (unlikely((int16_t)(new - vq->signalled_used) < (uint16_t)(new - old))) { + vq->signalled_used_valid = false; + } +} + +void vduse_queue_push(VduseVirtq *vq, const VduseVirtqElement *elem, + unsigned int len) +{ + vduse_queue_fill(vq, elem, len, 0); + vduse_queue_inflight_pre_put(vq, elem->index); + vduse_queue_flush(vq, 1); + vduse_queue_inflight_post_put(vq, elem->index); +} + +static int vduse_queue_update_vring(VduseVirtq *vq, uint64_t desc_addr, + uint64_t avail_addr, uint64_t used_addr) +{ + struct VduseDev *dev = vq->dev; + uint64_t len; + + len = sizeof(struct vring_desc); + vq->vring.desc = iova_to_va(dev, &len, desc_addr); + if (len != sizeof(struct vring_desc)) { + return -EINVAL; + } + + len = sizeof(struct vring_avail); + vq->vring.avail = iova_to_va(dev, &len, avail_addr); + if (len != sizeof(struct vring_avail)) { + return -EINVAL; + } + + len = sizeof(struct vring_used); + vq->vring.used = iova_to_va(dev, &len, used_addr); + if (len != sizeof(struct vring_used)) { + return -EINVAL; + } + + if (!vq->vring.desc || !vq->vring.avail || !vq->vring.used) { + fprintf(stderr, "Failed to get vq[%d] iova mapping\n", vq->index); + return -EINVAL; + } + + return 0; +} + +static void vduse_queue_enable(VduseVirtq *vq) +{ + struct VduseDev *dev = vq->dev; + struct vduse_vq_info vq_info; + struct vduse_vq_eventfd vq_eventfd; + int fd; + + vq_info.index = vq->index; + if (ioctl(dev->fd, VDUSE_VQ_GET_INFO, &vq_info)) { + fprintf(stderr, "Failed to get vq[%d] info: %s\n", + vq->index, strerror(errno)); + return; + } + + if (!vq_info.ready) { + return; + } + + vq->vring.num = vq_info.num; + vq->vring.desc_addr = vq_info.desc_addr; + vq->vring.avail_addr = vq_info.driver_addr; + vq->vring.used_addr = vq_info.device_addr; + + if (vduse_queue_update_vring(vq, vq_info.desc_addr, + vq_info.driver_addr, vq_info.device_addr)) { + fprintf(stderr, "Failed to update vring for vq[%d]\n", vq->index); + return; + } + + fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "Failed to init eventfd for vq[%d]\n", vq->index); + return; + } + + vq_eventfd.index = vq->index; + vq_eventfd.fd = fd; + if (ioctl(dev->fd, VDUSE_VQ_SETUP_KICKFD, &vq_eventfd)) { + fprintf(stderr, "Failed to setup kick fd for vq[%d]\n", vq->index); + close(fd); + return; + } + + vq->fd = fd; + vq->signalled_used_valid = false; + vq->ready = true; + + if (vduse_queue_check_inflights(vq)) { + fprintf(stderr, "Failed to check inflights for vq[%d]\n", vq->index); + close(fd); + return; + } + + dev->ops->enable_queue(dev, vq); +} + +static void vduse_queue_disable(VduseVirtq *vq) +{ + struct VduseDev *dev = vq->dev; + struct vduse_vq_eventfd eventfd; + + if (!vq->ready) { + return; + } + + dev->ops->disable_queue(dev, vq); + + eventfd.index = vq->index; + eventfd.fd = VDUSE_EVENTFD_DEASSIGN; + ioctl(dev->fd, VDUSE_VQ_SETUP_KICKFD, &eventfd); + close(vq->fd); + + assert(vq->inuse == 0); + + vq->vring.num = 0; + vq->vring.desc_addr = 0; + vq->vring.avail_addr = 0; + vq->vring.used_addr = 0; + vq->vring.desc = 0; + vq->vring.avail = 0; + vq->vring.used = 0; + vq->ready = false; + vq->fd = -1; +} + +static void vduse_dev_start_dataplane(VduseDev *dev) +{ + int i; + + if (ioctl(dev->fd, VDUSE_DEV_GET_FEATURES, &dev->features)) { + fprintf(stderr, "Failed to get features: %s\n", strerror(errno)); + return; + } + assert(vduse_dev_has_feature(dev, VIRTIO_F_VERSION_1)); + + for (i = 0; i < dev->num_queues; i++) { + vduse_queue_enable(&dev->vqs[i]); + } +} + +static void vduse_dev_stop_dataplane(VduseDev *dev) +{ + size_t log_size = dev->num_queues * vduse_vq_log_size(VIRTQUEUE_MAX_SIZE); + int i; + + for (i = 0; i < dev->num_queues; i++) { + vduse_queue_disable(&dev->vqs[i]); + } + if (dev->log) { + memset(dev->log, 0, log_size); + } + dev->features = 0; + vduse_iova_remove_region(dev, 0, ULONG_MAX); +} + +int vduse_dev_handler(VduseDev *dev) +{ + struct vduse_dev_request req; + struct vduse_dev_response resp = { 0 }; + VduseVirtq *vq; + int i, ret; + + ret = read(dev->fd, &req, sizeof(req)); + if (ret != sizeof(req)) { + fprintf(stderr, "Read request error [%d]: %s\n", + ret, strerror(errno)); + return -errno; + } + resp.request_id = req.request_id; + + switch (req.type) { + case VDUSE_GET_VQ_STATE: + vq = &dev->vqs[req.vq_state.index]; + resp.vq_state.split.avail_index = vq->last_avail_idx; + resp.result = VDUSE_REQ_RESULT_OK; + break; + case VDUSE_SET_STATUS: + if (req.s.status & VIRTIO_CONFIG_S_DRIVER_OK) { + vduse_dev_start_dataplane(dev); + } else if (req.s.status == 0) { + vduse_dev_stop_dataplane(dev); + } + resp.result = VDUSE_REQ_RESULT_OK; + break; + case VDUSE_UPDATE_IOTLB: + /* The iova will be updated by iova_to_va() later, so just remove it */ + vduse_iova_remove_region(dev, req.iova.start, req.iova.last); + for (i = 0; i < dev->num_queues; i++) { + vq = &dev->vqs[i]; + if (vq->ready) { + if (vduse_queue_update_vring(vq, vq->vring.desc_addr, + vq->vring.avail_addr, + vq->vring.used_addr)) { + fprintf(stderr, "Failed to update vring for vq[%d]\n", + vq->index); + } + } + } + resp.result = VDUSE_REQ_RESULT_OK; + break; + default: + resp.result = VDUSE_REQ_RESULT_FAILED; + break; + } + + ret = write(dev->fd, &resp, sizeof(resp)); + if (ret != sizeof(resp)) { + fprintf(stderr, "Write request %d error [%d]: %s\n", + req.type, ret, strerror(errno)); + return -errno; + } + return 0; +} + +int vduse_dev_update_config(VduseDev *dev, uint32_t size, + uint32_t offset, char *buffer) +{ + int ret; + struct vduse_config_data *data; + + data = malloc(offsetof(struct vduse_config_data, buffer) + size); + if (!data) { + return -ENOMEM; + } + + data->offset = offset; + data->length = size; + memcpy(data->buffer, buffer, size); + + ret = ioctl(dev->fd, VDUSE_DEV_SET_CONFIG, data); + free(data); + + if (ret) { + return -errno; + } + + if (ioctl(dev->fd, VDUSE_DEV_INJECT_CONFIG_IRQ)) { + return -errno; + } + + return 0; +} + +int vduse_dev_setup_queue(VduseDev *dev, int index, int max_size) +{ + VduseVirtq *vq = &dev->vqs[index]; + struct vduse_vq_config vq_config = { 0 }; + + if (max_size > VIRTQUEUE_MAX_SIZE) { + return -EINVAL; + } + + vq_config.index = vq->index; + vq_config.max_size = max_size; + + if (ioctl(dev->fd, VDUSE_VQ_SETUP, &vq_config)) { + return -errno; + } + + vduse_queue_enable(vq); + + return 0; +} + +int vduse_set_reconnect_log_file(VduseDev *dev, const char *filename) +{ + + size_t log_size = dev->num_queues * vduse_vq_log_size(VIRTQUEUE_MAX_SIZE); + void *log; + int i; + + dev->log = log = vduse_log_get(filename, log_size); + if (log == MAP_FAILED) { + fprintf(stderr, "Failed to get vduse log\n"); + return -EINVAL; + } + + for (i = 0; i < dev->num_queues; i++) { + dev->vqs[i].log = log; + dev->vqs[i].log->inflight.desc_num = VIRTQUEUE_MAX_SIZE; + log = (void *)((char *)log + vduse_vq_log_size(VIRTQUEUE_MAX_SIZE)); + } + + return 0; +} + +static int vduse_dev_init_vqs(VduseDev *dev, uint16_t num_queues) +{ + VduseVirtq *vqs; + int i; + + vqs = calloc(sizeof(VduseVirtq), num_queues); + if (!vqs) { + return -ENOMEM; + } + + for (i = 0; i < num_queues; i++) { + vqs[i].index = i; + vqs[i].dev = dev; + vqs[i].fd = -1; + } + dev->vqs = vqs; + + return 0; +} + +static int vduse_dev_init(VduseDev *dev, const char *name, + uint16_t num_queues, const VduseOps *ops, + void *priv) +{ + char *dev_path, *dev_name; + int ret, fd; + + dev_path = malloc(strlen(name) + strlen("/dev/vduse/") + 1); + if (!dev_path) { + return -ENOMEM; + } + sprintf(dev_path, "/dev/vduse/%s", name); + + fd = open(dev_path, O_RDWR); + free(dev_path); + if (fd < 0) { + fprintf(stderr, "Failed to open vduse dev %s: %s\n", + name, strerror(errno)); + return -errno; + } + + if (ioctl(fd, VDUSE_DEV_GET_FEATURES, &dev->features)) { + fprintf(stderr, "Failed to get features: %s\n", strerror(errno)); + close(fd); + return -errno; + } + + dev_name = strdup(name); + if (!dev_name) { + close(fd); + return -ENOMEM; + } + + ret = vduse_dev_init_vqs(dev, num_queues); + if (ret) { + free(dev_name); + close(fd); + return ret; + } + + dev->name = dev_name; + dev->num_queues = num_queues; + dev->fd = fd; + dev->ops = ops; + dev->priv = priv; + + return 0; +} + +static inline bool vduse_name_is_invalid(const char *name) +{ + return strlen(name) >= VDUSE_NAME_MAX || strstr(name, ".."); +} + +VduseDev *vduse_dev_create_by_fd(int fd, uint16_t num_queues, + const VduseOps *ops, void *priv) +{ + VduseDev *dev; + int ret; + + if (!ops || !ops->enable_queue || !ops->disable_queue) { + fprintf(stderr, "Invalid parameter for vduse\n"); + return NULL; + } + + dev = calloc(sizeof(VduseDev), 1); + if (!dev) { + fprintf(stderr, "Failed to allocate vduse device\n"); + return NULL; + } + + if (ioctl(fd, VDUSE_DEV_GET_FEATURES, &dev->features)) { + fprintf(stderr, "Failed to get features: %s\n", strerror(errno)); + free(dev); + return NULL; + } + + ret = vduse_dev_init_vqs(dev, num_queues); + if (ret) { + fprintf(stderr, "Failed to init vqs\n"); + free(dev); + return NULL; + } + + dev->num_queues = num_queues; + dev->fd = fd; + dev->ops = ops; + dev->priv = priv; + + return dev; +} + +VduseDev *vduse_dev_create_by_name(const char *name, uint16_t num_queues, + const VduseOps *ops, void *priv) +{ + VduseDev *dev; + int ret; + + if (!name || vduse_name_is_invalid(name) || !ops || + !ops->enable_queue || !ops->disable_queue) { + fprintf(stderr, "Invalid parameter for vduse\n"); + return NULL; + } + + dev = calloc(sizeof(VduseDev), 1); + if (!dev) { + fprintf(stderr, "Failed to allocate vduse device\n"); + return NULL; + } + + ret = vduse_dev_init(dev, name, num_queues, ops, priv); + if (ret < 0) { + fprintf(stderr, "Failed to init vduse device %s: %s\n", + name, strerror(-ret)); + free(dev); + return NULL; + } + + return dev; +} + +VduseDev *vduse_dev_create(const char *name, uint32_t device_id, + uint32_t vendor_id, uint64_t features, + uint16_t num_queues, uint32_t config_size, + char *config, const VduseOps *ops, void *priv) +{ + VduseDev *dev; + int ret, ctrl_fd; + uint64_t version; + struct vduse_dev_config *dev_config; + size_t size = offsetof(struct vduse_dev_config, config); + + if (!name || vduse_name_is_invalid(name) || + !has_feature(features, VIRTIO_F_VERSION_1) || !config || + !config_size || !ops || !ops->enable_queue || !ops->disable_queue) { + fprintf(stderr, "Invalid parameter for vduse\n"); + return NULL; + } + + dev = calloc(sizeof(VduseDev), 1); + if (!dev) { + fprintf(stderr, "Failed to allocate vduse device\n"); + return NULL; + } + + ctrl_fd = open("/dev/vduse/control", O_RDWR); + if (ctrl_fd < 0) { + fprintf(stderr, "Failed to open /dev/vduse/control: %s\n", + strerror(errno)); + goto err_ctrl; + } + + version = VDUSE_API_VERSION; + if (ioctl(ctrl_fd, VDUSE_SET_API_VERSION, &version)) { + fprintf(stderr, "Failed to set api version %" PRIu64 ": %s\n", + version, strerror(errno)); + goto err_dev; + } + + dev_config = calloc(size + config_size, 1); + if (!dev_config) { + fprintf(stderr, "Failed to allocate config space\n"); + goto err_dev; + } + + assert(!vduse_name_is_invalid(name)); + strcpy(dev_config->name, name); + dev_config->device_id = device_id; + dev_config->vendor_id = vendor_id; + dev_config->features = features; + dev_config->vq_num = num_queues; + dev_config->vq_align = VDUSE_VQ_ALIGN; + dev_config->config_size = config_size; + memcpy(dev_config->config, config, config_size); + + ret = ioctl(ctrl_fd, VDUSE_CREATE_DEV, dev_config); + free(dev_config); + if (ret && errno != EEXIST) { + fprintf(stderr, "Failed to create vduse device %s: %s\n", + name, strerror(errno)); + goto err_dev; + } + dev->ctrl_fd = ctrl_fd; + + ret = vduse_dev_init(dev, name, num_queues, ops, priv); + if (ret < 0) { + fprintf(stderr, "Failed to init vduse device %s: %s\n", + name, strerror(-ret)); + goto err; + } + + return dev; +err: + ioctl(ctrl_fd, VDUSE_DESTROY_DEV, name); +err_dev: + close(ctrl_fd); +err_ctrl: + free(dev); + + return NULL; +} + +int vduse_dev_destroy(VduseDev *dev) +{ + size_t log_size = dev->num_queues * vduse_vq_log_size(VIRTQUEUE_MAX_SIZE); + int i, ret = 0; + + if (dev->log) { + munmap(dev->log, log_size); + } + for (i = 0; i < dev->num_queues; i++) { + free(dev->vqs[i].resubmit_list); + } + free(dev->vqs); + if (dev->fd >= 0) { + close(dev->fd); + dev->fd = -1; + } + if (dev->ctrl_fd >= 0) { + if (ioctl(dev->ctrl_fd, VDUSE_DESTROY_DEV, dev->name)) { + ret = -errno; + } + close(dev->ctrl_fd); + dev->ctrl_fd = -1; + } + free(dev->name); + free(dev); + + return ret; +} diff --git a/subprojects/libvduse/libvduse.h b/subprojects/libvduse/libvduse.h new file mode 100644 index 0000000000..32f19e7b48 --- /dev/null +++ b/subprojects/libvduse/libvduse.h @@ -0,0 +1,247 @@ +/* + * VDUSE (vDPA Device in Userspace) library + * + * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved. + * + * Author: + * Xie Yongji + * + * 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 LIBVDUSE_H +#define LIBVDUSE_H + +#include +#include + +#define VIRTQUEUE_MAX_SIZE 1024 + +/* VDUSE device structure */ +typedef struct VduseDev VduseDev; + +/* Virtqueue structure */ +typedef struct VduseVirtq VduseVirtq; + +/* Some operation of VDUSE backend */ +typedef struct VduseOps { + /* Called when virtqueue can be processed */ + void (*enable_queue)(VduseDev *dev, VduseVirtq *vq); + /* Called when virtqueue processing should be stopped */ + void (*disable_queue)(VduseDev *dev, VduseVirtq *vq); +} VduseOps; + +/* Describing elements of the I/O buffer */ +typedef struct VduseVirtqElement { + /* Descriptor table index */ + unsigned int index; + /* Number of physically-contiguous device-readable descriptors */ + unsigned int out_num; + /* Number of physically-contiguous device-writable descriptors */ + unsigned int in_num; + /* Array to store physically-contiguous device-writable descriptors */ + struct iovec *in_sg; + /* Array to store physically-contiguous device-readable descriptors */ + struct iovec *out_sg; +} VduseVirtqElement; + + +/** + * vduse_get_virtio_features: + * + * Get supported virtio features + * + * Returns: supported feature bits + */ +uint64_t vduse_get_virtio_features(void); + +/** + * vduse_queue_get_dev: + * @vq: specified virtqueue + * + * Get corresponding VDUSE device from the virtqueue. + * + * Returns: a pointer to VDUSE device on success, NULL on failure. + */ +VduseDev *vduse_queue_get_dev(VduseVirtq *vq); + +/** + * vduse_queue_get_fd: + * @vq: specified virtqueue + * + * Get the kick fd for the virtqueue. + * + * Returns: file descriptor on success, -1 on failure. + */ +int vduse_queue_get_fd(VduseVirtq *vq); + +/** + * vduse_queue_pop: + * @vq: specified virtqueue + * @sz: the size of struct to return (must be >= VduseVirtqElement) + * + * Pop an element from virtqueue available ring. + * + * Returns: a pointer to a structure containing VduseVirtqElement on success, + * NULL on failure. + */ +void *vduse_queue_pop(VduseVirtq *vq, size_t sz); + +/** + * vduse_queue_push: + * @vq: specified virtqueue + * @elem: pointer to VduseVirtqElement returned by vduse_queue_pop() + * @len: length in bytes to write + * + * Push an element to virtqueue used ring. + */ +void vduse_queue_push(VduseVirtq *vq, const VduseVirtqElement *elem, + unsigned int len); +/** + * vduse_queue_notify: + * @vq: specified virtqueue + * + * Request to notify the queue. + */ +void vduse_queue_notify(VduseVirtq *vq); + +/** + * vduse_dev_get_priv: + * @dev: VDUSE device + * + * Get the private pointer passed to vduse_dev_create(). + * + * Returns: private pointer on success, NULL on failure. + */ +void *vduse_dev_get_priv(VduseDev *dev); + +/** + * vduse_dev_get_queue: + * @dev: VDUSE device + * @index: virtqueue index + * + * Get the specified virtqueue. + * + * Returns: a pointer to the virtqueue on success, NULL on failure. + */ +VduseVirtq *vduse_dev_get_queue(VduseDev *dev, int index); + +/** + * vduse_dev_get_fd: + * @dev: VDUSE device + * + * Get the control message fd for the VDUSE device. + * + * Returns: file descriptor on success, -1 on failure. + */ +int vduse_dev_get_fd(VduseDev *dev); + +/** + * vduse_dev_handler: + * @dev: VDUSE device + * + * Used to process the control message. + * + * Returns: file descriptor on success, -errno on failure. + */ +int vduse_dev_handler(VduseDev *dev); + +/** + * vduse_dev_update_config: + * @dev: VDUSE device + * @size: the size to write to configuration space + * @offset: the offset from the beginning of configuration space + * @buffer: the buffer used to write from + * + * Update device configuration space and inject a config interrupt. + * + * Returns: 0 on success, -errno on failure. + */ +int vduse_dev_update_config(VduseDev *dev, uint32_t size, + uint32_t offset, char *buffer); + +/** + * vduse_dev_setup_queue: + * @dev: VDUSE device + * @index: virtqueue index + * @max_size: the max size of virtqueue + * + * Setup the specified virtqueue. + * + * Returns: 0 on success, -errno on failure. + */ +int vduse_dev_setup_queue(VduseDev *dev, int index, int max_size); + +/** + * vduse_set_reconnect_log_file: + * @dev: VDUSE device + * @file: filename of reconnect log + * + * Specify the file to store log for reconnecting. It should + * be called before vduse_dev_setup_queue(). + * + * Returns: 0 on success, -errno on failure. + */ +int vduse_set_reconnect_log_file(VduseDev *dev, const char *filename); + +/** + * vduse_dev_create_by_fd: + * @fd: passed file descriptor + * @num_queues: the number of virtqueues + * @ops: the operation of VDUSE backend + * @priv: private pointer + * + * Create VDUSE device from a passed file descriptor. + * + * Returns: pointer to VDUSE device on success, NULL on failure. + */ +VduseDev *vduse_dev_create_by_fd(int fd, uint16_t num_queues, + const VduseOps *ops, void *priv); + +/** + * vduse_dev_create_by_name: + * @name: VDUSE device name + * @num_queues: the number of virtqueues + * @ops: the operation of VDUSE backend + * @priv: private pointer + * + * Create VDUSE device on /dev/vduse/$NAME. + * + * Returns: pointer to VDUSE device on success, NULL on failure. + */ +VduseDev *vduse_dev_create_by_name(const char *name, uint16_t num_queues, + const VduseOps *ops, void *priv); + +/** + * vduse_dev_create: + * @name: VDUSE device name + * @device_id: virtio device id + * @vendor_id: virtio vendor id + * @features: virtio features + * @num_queues: the number of virtqueues + * @config_size: the size of the configuration space + * @config: the buffer of the configuration space + * @ops: the operation of VDUSE backend + * @priv: private pointer + * + * Create VDUSE device. + * + * Returns: pointer to VDUSE device on success, NULL on failure. + */ +VduseDev *vduse_dev_create(const char *name, uint32_t device_id, + uint32_t vendor_id, uint64_t features, + uint16_t num_queues, uint32_t config_size, + char *config, const VduseOps *ops, void *priv); + +/** + * vduse_dev_destroy: + * @dev: VDUSE device + * + * Destroy the VDUSE device. + * + * Returns: 0 on success, -errno on failure. + */ +int vduse_dev_destroy(VduseDev *dev); + +#endif diff --git a/subprojects/libvduse/linux-headers/linux b/subprojects/libvduse/linux-headers/linux new file mode 120000 index 0000000000..04f3304f79 --- /dev/null +++ b/subprojects/libvduse/linux-headers/linux @@ -0,0 +1 @@ +../../../linux-headers/linux/ \ No newline at end of file diff --git a/subprojects/libvduse/meson.build b/subprojects/libvduse/meson.build new file mode 100644 index 0000000000..3e3b53da33 --- /dev/null +++ b/subprojects/libvduse/meson.build @@ -0,0 +1,16 @@ +project('libvduse', 'c', + license: 'GPL-2.0-or-later', + default_options: ['warning_level=1', 'c_std=gnu99']) + +cc = meson.get_compiler('c') +add_project_arguments(cc.get_supported_arguments('-Wsign-compare', + '-Wdeclaration-after-statement', + '-Wstrict-aliasing'), + native: false, language: 'c') + +libvduse = static_library('vduse', + files('libvduse.c'), + c_args: '-D_GNU_SOURCE') + +libvduse_dep = declare_dependency(link_with: libvduse, + include_directories: include_directories('.')) diff --git a/subprojects/libvduse/standard-headers/linux b/subprojects/libvduse/standard-headers/linux new file mode 120000 index 0000000000..c416f068ac --- /dev/null +++ b/subprojects/libvduse/standard-headers/linux @@ -0,0 +1 @@ +../../../include/standard-headers/linux/ \ No newline at end of file diff --git a/subprojects/libvfio-user.wrap b/subprojects/libvfio-user.wrap new file mode 100644 index 0000000000..416955ca45 --- /dev/null +++ b/subprojects/libvfio-user.wrap @@ -0,0 +1,4 @@ +[wrap-git] +url = https://gitlab.com/qemu-project/libvfio-user.git +revision = 0b28d205572c80b568a1003db2c8f37ca333e4d7 +depth = 1 diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index b4cc3c2d68..a879149fef 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -13,6 +13,10 @@ * later. See the COPYING file in the top-level directory. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + /* this code avoids GLib dependency */ #include #include @@ -28,11 +32,19 @@ #include #include +/* Necessary to provide VIRTIO_F_VERSION_1 on system + * with older linux headers. Must appear before + * below. + */ +#include "standard-headers/linux/virtio_config.h" + #if defined(__linux__) #include #include #include #include +#include +#include #ifdef __NR_userfaultfd #include @@ -45,10 +57,21 @@ #include "libvhost-user.h" /* usually provided by GLib */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +#if !defined(__clang__) && (__GNUC__ == 4 && __GNUC_MINOR__ == 4) +#define G_GNUC_PRINTF(format_idx, arg_idx) \ + __attribute__((__format__(gnu_printf, format_idx, arg_idx))) +#else +#define G_GNUC_PRINTF(format_idx, arg_idx) \ + __attribute__((__format__(__printf__, format_idx, arg_idx))) +#endif +#else /* !__GNUC__ */ +#define G_GNUC_PRINTF(format_idx, arg_idx) +#endif /* !__GNUC__ */ #ifndef MIN #define MIN(x, y) ({ \ - typeof(x) _min1 = (x); \ - typeof(y) _min2 = (y); \ + __typeof__(x) _min1 = (x); \ + __typeof__(y) _min2 = (y); \ (void) (&_min1 == &_min2); \ _min1 < _min2 ? _min1 : _min2; }) #endif @@ -125,7 +148,7 @@ vu_request_to_string(unsigned int req) REQ(VHOST_USER_SET_VRING_ENABLE), REQ(VHOST_USER_SEND_RARP), REQ(VHOST_USER_NET_SET_MTU), - REQ(VHOST_USER_SET_SLAVE_REQ_FD), + REQ(VHOST_USER_SET_BACKEND_REQ_FD), REQ(VHOST_USER_IOTLB_MSG), REQ(VHOST_USER_SET_VRING_ENDIAN), REQ(VHOST_USER_GET_CONFIG), @@ -140,6 +163,7 @@ vu_request_to_string(unsigned int req) REQ(VHOST_USER_GET_MAX_MEM_SLOTS), REQ(VHOST_USER_ADD_MEM_REG), REQ(VHOST_USER_REM_MEM_REG), + REQ(VHOST_USER_GET_SHARED_OBJECT), REQ(VHOST_USER_MAX), }; #undef REQ @@ -151,7 +175,7 @@ vu_request_to_string(unsigned int req) } } -static void +static void G_GNUC_PRINTF(2, 3) vu_panic(VuDev *dev, const char *msg, ...) { char *buf = NULL; @@ -173,37 +197,65 @@ vu_panic(VuDev *dev, const char *msg, ...) */ } +/* Search for a memory region that covers this guest physical address. */ +static VuDevRegion * +vu_gpa_to_mem_region(VuDev *dev, uint64_t guest_addr) +{ + int low = 0; + int high = dev->nregions - 1; + + /* + * Memory regions cannot overlap in guest physical address space. Each + * GPA belongs to exactly one memory region, so there can only be one + * match. + * + * We store our memory regions ordered by GPA and can simply perform a + * binary search. + */ + while (low <= high) { + unsigned int mid = low + (high - low) / 2; + VuDevRegion *cur = &dev->regions[mid]; + + if (guest_addr >= cur->gpa && guest_addr < cur->gpa + cur->size) { + return cur; + } + if (guest_addr >= cur->gpa + cur->size) { + low = mid + 1; + } + if (guest_addr < cur->gpa) { + high = mid - 1; + } + } + return NULL; +} + /* Translate guest physical address to our virtual address. */ void * vu_gpa_to_va(VuDev *dev, uint64_t *plen, uint64_t guest_addr) { - int i; + VuDevRegion *r; if (*plen == 0) { return NULL; } - /* Find matching memory region. */ - for (i = 0; i < dev->nregions; i++) { - VuDevRegion *r = &dev->regions[i]; - - if ((guest_addr >= r->gpa) && (guest_addr < (r->gpa + r->size))) { - if ((guest_addr + *plen) > (r->gpa + r->size)) { - *plen = r->gpa + r->size - guest_addr; - } - return (void *)(uintptr_t) - guest_addr - r->gpa + r->mmap_addr + r->mmap_offset; - } + r = vu_gpa_to_mem_region(dev, guest_addr); + if (!r) { + return NULL; } - return NULL; + if ((guest_addr + *plen) > (r->gpa + r->size)) { + *plen = r->gpa + r->size - guest_addr; + } + return (void *)(uintptr_t)guest_addr - r->gpa + r->mmap_addr + + r->mmap_offset; } /* Translate qemu virtual address to our virtual address. */ static void * qva_to_va(VuDev *dev, uint64_t qemu_addr) { - int i; + unsigned int i; /* Find matching memory region. */ for (i = 0; i < dev->nregions; i++) { @@ -218,6 +270,221 @@ qva_to_va(VuDev *dev, uint64_t qemu_addr) return NULL; } +static void +vu_remove_all_mem_regs(VuDev *dev) +{ + unsigned int i; + + for (i = 0; i < dev->nregions; i++) { + VuDevRegion *r = &dev->regions[i]; + + munmap((void *)(uintptr_t)r->mmap_addr, r->size + r->mmap_offset); + } + dev->nregions = 0; +} + +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_is_vq_usable(VuDev *dev, VuVirtq *vq) +{ + if (unlikely(dev->broken)) { + return false; + } + + if (likely(vq->vring.avail)) { + return true; + } + + /* + * In corner cases, we might temporarily remove a memory region that + * mapped a ring. When removing a memory region we make sure to + * unmap any rings that would be impacted. Let's try to remap if we + * already succeeded mapping this ring once. + */ + if (!vq->vra.desc_user_addr || !vq->vra.used_user_addr || + !vq->vra.avail_user_addr) { + return false; + } + if (map_ring(dev, vq)) { + vu_panic(dev, "remapping queue on access"); + return false; + } + return true; +} + +static void +unmap_rings(VuDev *dev, VuDevRegion *r) +{ + int i; + + for (i = 0; i < dev->max_queues; i++) { + VuVirtq *vq = &dev->vq[i]; + const uintptr_t desc = (uintptr_t)vq->vring.desc; + const uintptr_t used = (uintptr_t)vq->vring.used; + const uintptr_t avail = (uintptr_t)vq->vring.avail; + + if (desc < r->mmap_addr || desc >= r->mmap_addr + r->size) { + continue; + } + if (used < r->mmap_addr || used >= r->mmap_addr + r->size) { + continue; + } + if (avail < r->mmap_addr || avail >= r->mmap_addr + r->size) { + continue; + } + + DPRINT("Unmapping rings of queue %d\n", i); + vq->vring.desc = NULL; + vq->vring.used = NULL; + vq->vring.avail = NULL; + } +} + +static size_t +get_fd_hugepagesize(int fd) +{ +#if defined(__linux__) + struct statfs fs; + int ret; + + do { + ret = fstatfs(fd, &fs); + } while (ret != 0 && errno == EINTR); + + if (!ret && (unsigned int)fs.f_type == HUGETLBFS_MAGIC) { + return fs.f_bsize; + } +#endif + return 0; +} + +static void +_vu_add_mem_reg(VuDev *dev, VhostUserMemoryRegion *msg_region, int fd) +{ + const uint64_t start_gpa = msg_region->guest_phys_addr; + const uint64_t end_gpa = start_gpa + msg_region->memory_size; + int prot = PROT_READ | PROT_WRITE; + uint64_t mmap_offset, fd_offset; + size_t hugepagesize; + VuDevRegion *r; + void *mmap_addr; + int low = 0; + int high = dev->nregions - 1; + unsigned int idx; + + DPRINT("Adding region %d\n", dev->nregions); + DPRINT(" guest_phys_addr: 0x%016"PRIx64"\n", + msg_region->guest_phys_addr); + DPRINT(" memory_size: 0x%016"PRIx64"\n", + msg_region->memory_size); + DPRINT(" userspace_addr: 0x%016"PRIx64"\n", + msg_region->userspace_addr); + DPRINT(" old mmap_offset: 0x%016"PRIx64"\n", + msg_region->mmap_offset); + + if (dev->postcopy_listening) { + /* + * In postcopy we're using PROT_NONE here to catch anyone + * accessing it before we userfault + */ + prot = PROT_NONE; + } + + /* + * We will add memory regions into the array sorted by GPA. Perform a + * binary search to locate the insertion point: it will be at the low + * index. + */ + while (low <= high) { + unsigned int mid = low + (high - low) / 2; + VuDevRegion *cur = &dev->regions[mid]; + + /* Overlap of GPA addresses. */ + if (start_gpa < cur->gpa + cur->size && cur->gpa < end_gpa) { + vu_panic(dev, "regions with overlapping guest physical addresses"); + return; + } + if (start_gpa >= cur->gpa + cur->size) { + low = mid + 1; + } + if (start_gpa < cur->gpa) { + high = mid - 1; + } + } + idx = low; + + /* + * Convert most of msg_region->mmap_offset to fd_offset. In almost all + * cases, this will leave us with mmap_offset == 0, mmap()'ing only + * what we really need. Only if a memory region would partially cover + * hugetlb pages, we'd get mmap_offset != 0, which usually doesn't happen + * anymore (i.e., modern QEMU). + * + * Note that mmap() with hugetlb would fail if the offset into the file + * is not aligned to the huge page size. + */ + hugepagesize = get_fd_hugepagesize(fd); + if (hugepagesize) { + fd_offset = ALIGN_DOWN(msg_region->mmap_offset, hugepagesize); + mmap_offset = msg_region->mmap_offset - fd_offset; + } else { + fd_offset = msg_region->mmap_offset; + mmap_offset = 0; + } + + DPRINT(" fd_offset: 0x%016"PRIx64"\n", + fd_offset); + DPRINT(" new mmap_offset: 0x%016"PRIx64"\n", + mmap_offset); + + mmap_addr = mmap(0, msg_region->memory_size + mmap_offset, + prot, MAP_SHARED | MAP_NORESERVE, fd, fd_offset); + if (mmap_addr == MAP_FAILED) { + vu_panic(dev, "region mmap error: %s", strerror(errno)); + return; + } + DPRINT(" mmap_addr: 0x%016"PRIx64"\n", + (uint64_t)(uintptr_t)mmap_addr); + +#if defined(__linux__) + /* Don't include all guest memory in a coredump. */ + madvise(mmap_addr, msg_region->memory_size + mmap_offset, + MADV_DONTDUMP); +#endif + + /* Shift all affected entries by 1 to open a hole at idx. */ + r = &dev->regions[idx]; + memmove(r + 1, r, sizeof(VuDevRegion) * (dev->nregions - idx)); + r->gpa = msg_region->guest_phys_addr; + r->size = msg_region->memory_size; + r->qva = msg_region->userspace_addr; + r->mmap_addr = (uint64_t)(uintptr_t)mmap_addr; + r->mmap_offset = mmap_offset; + dev->nregions++; + + if (dev->postcopy_listening) { + /* + * Return the address to QEMU so that it can translate the ufd + * fault addresses back. + */ + msg_region->userspace_addr = r->mmap_addr + r->mmap_offset; + } +} + static void vmsg_close_fds(VhostUserMsg *vmsg) { @@ -301,6 +568,7 @@ vu_message_read_default(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { fd_size = cmsg->cmsg_len - CMSG_LEN(0); vmsg->fd_num = fd_size / sizeof(int); + assert(fd_size < VHOST_MEMORY_BASELINE_NREGIONS); memcpy(vmsg->fds, CMSG_DATA(cmsg), fd_size); break; } @@ -324,7 +592,7 @@ vu_message_read_default(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) goto fail; } - assert(rc == vmsg->size); + assert((uint32_t)rc == vmsg->size); } return true; @@ -400,8 +668,8 @@ vu_send_reply(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) } /* - * Processes a reply on the slave channel. - * Entered with slave_mutex held and releases it before exit. + * Processes a reply on the backend channel. + * Entered with backend_mutex held and releases it before exit. * Returns true on success. */ static bool @@ -415,7 +683,7 @@ vu_process_message_reply(VuDev *dev, const VhostUserMsg *vmsg) goto out; } - if (!vu_message_read_default(dev, dev->slave_fd, &msg_reply)) { + if (!vu_message_read_default(dev, dev->backend_fd, &msg_reply)) { goto out; } @@ -428,7 +696,7 @@ vu_process_message_reply(VuDev *dev, const VhostUserMsg *vmsg) result = msg_reply.payload.u64 == 0; out: - pthread_mutex_unlock(&dev->slave_mutex); + pthread_mutex_unlock(&dev->backend_mutex); return result; } @@ -589,28 +857,15 @@ 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 generate_faults(VuDev *dev) { - int i; + unsigned int i; for (i = 0; i < dev->nregions; i++) { +#ifdef UFFDIO_REGISTER VuDevRegion *dev_region = &dev->regions[i]; int ret; -#ifdef UFFDIO_REGISTER + struct uffdio_register reg_struct; + /* * We should already have an open ufd. Mark each memory * range as ufd. @@ -644,21 +899,22 @@ generate_faults(VuDev *dev) { "%s: Failed to madvise(NOHUGEPAGE) region %d: %s\n", __func__, i, strerror(errno)); } - struct uffdio_register reg_struct; + reg_struct.range.start = (uintptr_t)dev_region->mmap_addr; reg_struct.range.len = dev_region->size + dev_region->mmap_offset; reg_struct.mode = UFFDIO_REGISTER_MODE_MISSING; if (ioctl(dev->postcopy_ufd, UFFDIO_REGISTER, ®_struct)) { vu_panic(dev, "%s: Failed to userfault region %d " - "@%p + size:%zx offset: %zx: (ufd=%d)%s\n", + "@%" PRIx64 " + size:%" PRIx64 " offset: %" PRIx64 + ": (ufd=%d)%s\n", __func__, i, dev_region->mmap_addr, dev_region->size, dev_region->mmap_offset, dev->postcopy_ufd, strerror(errno)); return false; } - if (!(reg_struct.ioctls & ((__u64)1 << _UFFDIO_COPY))) { + if (!(reg_struct.ioctls & (1ULL << _UFFDIO_COPY))) { vu_panic(dev, "%s Region (%d) doesn't support COPY", __func__, i); return false; @@ -684,11 +940,7 @@ generate_faults(VuDev *dev) { static bool vu_add_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { - int i; - bool track_ramblocks = dev->postcopy_listening; VhostUserMemoryRegion m = vmsg->payload.memreg.region, *msg_region = &m; - VuDevRegion *dev_region = &dev->regions[dev->nregions]; - void *mmap_addr; if (vmsg->fd_num != 1) { vmsg_close_fds(vmsg); @@ -700,7 +952,7 @@ vu_add_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { if (vmsg->size < VHOST_USER_MEM_REG_SIZE) { close(vmsg->fds[0]); vu_panic(dev, "VHOST_USER_ADD_MEM_REG requires a message size of at " - "least %d bytes and only %d bytes were received", + "least %zu bytes and only %d bytes were received", VHOST_USER_MEM_REG_SIZE, vmsg->size); return false; } @@ -718,90 +970,24 @@ vu_add_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { * we know all the postcopy client bases have been received, and we * should start generating faults. */ - if (track_ramblocks && + if (dev->postcopy_listening && vmsg->size == sizeof(vmsg->payload.u64) && vmsg->payload.u64 == 0) { (void)generate_faults(dev); return false; } - DPRINT("Adding region: %u\n", dev->nregions); - DPRINT(" guest_phys_addr: 0x%016"PRIx64"\n", - msg_region->guest_phys_addr); - DPRINT(" memory_size: 0x%016"PRIx64"\n", - msg_region->memory_size); - DPRINT(" userspace_addr 0x%016"PRIx64"\n", - msg_region->userspace_addr); - DPRINT(" mmap_offset 0x%016"PRIx64"\n", - msg_region->mmap_offset); - - dev_region->gpa = msg_region->guest_phys_addr; - dev_region->size = msg_region->memory_size; - dev_region->qva = msg_region->userspace_addr; - dev_region->mmap_offset = msg_region->mmap_offset; - - /* - * We don't use offset argument of mmap() since the - * mapped address has to be page aligned, and we use huge - * pages. - */ - if (track_ramblocks) { - /* - * In postcopy we're using PROT_NONE here to catch anyone - * accessing it before we userfault. - */ - mmap_addr = mmap(0, dev_region->size + dev_region->mmap_offset, - PROT_NONE, MAP_SHARED | MAP_NORESERVE, - vmsg->fds[0], 0); - } else { - mmap_addr = mmap(0, dev_region->size + dev_region->mmap_offset, - PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NORESERVE, - vmsg->fds[0], 0); - } - - if (mmap_addr == MAP_FAILED) { - vu_panic(dev, "region mmap error: %s", strerror(errno)); - } else { - dev_region->mmap_addr = (uint64_t)(uintptr_t)mmap_addr; - DPRINT(" mmap_addr: 0x%016"PRIx64"\n", - dev_region->mmap_addr); - } - + _vu_add_mem_reg(dev, msg_region, vmsg->fds[0]); close(vmsg->fds[0]); - if (track_ramblocks) { - /* - * Return the address to QEMU so that it can translate the ufd - * fault addresses back. - */ - msg_region->userspace_addr = (uintptr_t)(mmap_addr + - dev_region->mmap_offset); - + if (dev->postcopy_listening) { /* Send the message back to qemu with the addresses filled in. */ vmsg->fd_num = 0; - if (!vu_send_reply(dev, dev->sock, vmsg)) { - vu_panic(dev, "failed to respond to add-mem-region for postcopy"); - return false; - } - DPRINT("Successfully added new region in postcopy\n"); - dev->nregions++; - return false; - - } else { - for (i = 0; i < dev->max_queues; i++) { - if (dev->vq[i].vring.desc) { - if (map_ring(dev, &dev->vq[i])) { - vu_panic(dev, "remapping queue %d for new memory region", - i); - } - } - } - - DPRINT("Successfully added new region\n"); - dev->nregions++; - return false; + return true; } + DPRINT("Successfully added new region\n"); + return false; } static inline bool reg_equal(VuDevRegion *vudev_reg, @@ -819,8 +1005,8 @@ static inline bool reg_equal(VuDevRegion *vudev_reg, static bool vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { VhostUserMemoryRegion m = vmsg->payload.memreg.region, *msg_region = &m; - int i; - bool found = false; + unsigned int idx; + VuDevRegion *r; if (vmsg->fd_num > 1) { vmsg_close_fds(vmsg); @@ -832,7 +1018,7 @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { if (vmsg->size < VHOST_USER_MEM_REG_SIZE) { vmsg_close_fds(vmsg); vu_panic(dev, "VHOST_USER_REM_MEM_REG requires a message size of at " - "least %d bytes and only %d bytes were received", + "least %zu bytes and only %d bytes were received", VHOST_USER_MEM_REG_SIZE, vmsg->size); return false; } @@ -847,175 +1033,91 @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { DPRINT(" mmap_offset 0x%016"PRIx64"\n", msg_region->mmap_offset); - for (i = 0; i < dev->nregions; i++) { - if (reg_equal(&dev->regions[i], msg_region)) { - VuDevRegion *r = &dev->regions[i]; - void *m = (void *) (uintptr_t) r->mmap_addr; - - if (m) { - munmap(m, r->size + r->mmap_offset); - } - - /* - * Shift all affected entries by 1 to close the hole at index i and - * zero out the last entry. - */ - memmove(dev->regions + i, dev->regions + i + 1, - sizeof(VuDevRegion) * (dev->nregions - i - 1)); - memset(dev->regions + dev->nregions - 1, 0, sizeof(VuDevRegion)); - DPRINT("Successfully removed a region\n"); - dev->nregions--; - i--; - - found = true; - - /* Continue the search for eventual duplicates. */ - } - } - - if (!found) { + r = vu_gpa_to_mem_region(dev, msg_region->guest_phys_addr); + if (!r || !reg_equal(r, msg_region)) { + vmsg_close_fds(vmsg); vu_panic(dev, "Specified region not found\n"); + return false; } + /* + * There might be valid cases where we temporarily remove memory regions + * to readd them again, or remove memory regions and don't use the rings + * anymore before we set the ring addresses and restart the device. + * + * Unmap all affected rings, remapping them on demand later. This should + * be a corner case. + */ + unmap_rings(dev, r); + + munmap((void *)(uintptr_t)r->mmap_addr, r->size + r->mmap_offset); + + idx = r - dev->regions; + assert(idx < dev->nregions); + /* Shift all affected entries by 1 to close the hole. */ + memmove(r, r + 1, sizeof(VuDevRegion) * (dev->nregions - idx - 1)); + DPRINT("Successfully removed a region\n"); + dev->nregions--; + vmsg_close_fds(vmsg); return false; } static bool -vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg) +vu_get_shared_object(VuDev *dev, VhostUserMsg *vmsg) { - int i; - VhostUserMemory m = vmsg->payload.memory, *memory = &m; - dev->nregions = memory->nregions; - - DPRINT("Nregions: %u\n", memory->nregions); - for (i = 0; i < dev->nregions; i++) { - void *mmap_addr; - VhostUserMemoryRegion *msg_region = &memory->regions[i]; - VuDevRegion *dev_region = &dev->regions[i]; - - DPRINT("Region %d\n", i); - DPRINT(" guest_phys_addr: 0x%016"PRIx64"\n", - msg_region->guest_phys_addr); - DPRINT(" memory_size: 0x%016"PRIx64"\n", - msg_region->memory_size); - DPRINT(" userspace_addr 0x%016"PRIx64"\n", - msg_region->userspace_addr); - DPRINT(" mmap_offset 0x%016"PRIx64"\n", - msg_region->mmap_offset); - - dev_region->gpa = msg_region->guest_phys_addr; - dev_region->size = msg_region->memory_size; - dev_region->qva = msg_region->userspace_addr; - dev_region->mmap_offset = msg_region->mmap_offset; - - /* We don't use offset argument of mmap() since the - * mapped address has to be page aligned, and we use huge - * pages. - * In postcopy we're using PROT_NONE here to catch anyone - * accessing it before we userfault - */ - mmap_addr = mmap(0, dev_region->size + dev_region->mmap_offset, - PROT_NONE, MAP_SHARED | MAP_NORESERVE, - vmsg->fds[i], 0); - - if (mmap_addr == MAP_FAILED) { - vu_panic(dev, "region mmap error: %s", strerror(errno)); - } else { - dev_region->mmap_addr = (uint64_t)(uintptr_t)mmap_addr; - DPRINT(" mmap_addr: 0x%016"PRIx64"\n", - dev_region->mmap_addr); - } - - /* Return the address to QEMU so that it can translate the ufd - * fault addresses back. - */ - msg_region->userspace_addr = (uintptr_t)(mmap_addr + - dev_region->mmap_offset); - close(vmsg->fds[i]); + int fd_num = 0; + int dmabuf_fd = -1; + if (dev->iface->get_shared_object) { + dmabuf_fd = dev->iface->get_shared_object( + dev, &vmsg->payload.object.uuid[0]); } - - /* Send the message back to qemu with the addresses filled in */ - vmsg->fd_num = 0; - if (!vu_send_reply(dev, dev->sock, vmsg)) { - vu_panic(dev, "failed to respond to set-mem-table for postcopy"); - return false; + if (dmabuf_fd != -1) { + DPRINT("dmabuf_fd found for requested UUID\n"); + vmsg->fds[fd_num++] = dmabuf_fd; } + vmsg->fd_num = fd_num; - /* Wait for QEMU to confirm that it's registered the handler for the - * faults. - */ - if (!dev->read_msg(dev, dev->sock, vmsg) || - vmsg->size != sizeof(vmsg->payload.u64) || - vmsg->payload.u64 != 0) { - vu_panic(dev, "failed to receive valid ack for postcopy set-mem-table"); - return false; - } - - /* OK, now we can go and register the memory and generate faults */ - (void)generate_faults(dev); - - return false; + return true; } static bool vu_set_mem_table_exec(VuDev *dev, VhostUserMsg *vmsg) { - int i; VhostUserMemory m = vmsg->payload.memory, *memory = &m; + unsigned int i; - for (i = 0; i < dev->nregions; i++) { - VuDevRegion *r = &dev->regions[i]; - void *m = (void *) (uintptr_t) r->mmap_addr; - - if (m) { - munmap(m, r->size + r->mmap_offset); - } - } - dev->nregions = memory->nregions; - - if (dev->postcopy_listening) { - return vu_set_mem_table_exec_postcopy(dev, vmsg); - } + vu_remove_all_mem_regs(dev); DPRINT("Nregions: %u\n", memory->nregions); - for (i = 0; i < dev->nregions; i++) { - void *mmap_addr; - VhostUserMemoryRegion *msg_region = &memory->regions[i]; - VuDevRegion *dev_region = &dev->regions[i]; + for (i = 0; i < memory->nregions; i++) { + _vu_add_mem_reg(dev, &memory->regions[i], vmsg->fds[i]); + close(vmsg->fds[i]); + } - DPRINT("Region %d\n", i); - DPRINT(" guest_phys_addr: 0x%016"PRIx64"\n", - msg_region->guest_phys_addr); - DPRINT(" memory_size: 0x%016"PRIx64"\n", - msg_region->memory_size); - DPRINT(" userspace_addr 0x%016"PRIx64"\n", - msg_region->userspace_addr); - DPRINT(" mmap_offset 0x%016"PRIx64"\n", - msg_region->mmap_offset); - - dev_region->gpa = msg_region->guest_phys_addr; - dev_region->size = msg_region->memory_size; - dev_region->qva = msg_region->userspace_addr; - dev_region->mmap_offset = msg_region->mmap_offset; - - /* We don't use offset argument of mmap() since the - * mapped address has to be page aligned, and we use huge - * pages. */ - mmap_addr = mmap(0, dev_region->size + dev_region->mmap_offset, - PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NORESERVE, - vmsg->fds[i], 0); - - if (mmap_addr == MAP_FAILED) { - vu_panic(dev, "region mmap error: %s", strerror(errno)); - } else { - dev_region->mmap_addr = (uint64_t)(uintptr_t)mmap_addr; - DPRINT(" mmap_addr: 0x%016"PRIx64"\n", - dev_region->mmap_addr); + if (dev->postcopy_listening) { + /* Send the message back to qemu with the addresses filled in */ + vmsg->fd_num = 0; + if (!vu_send_reply(dev, dev->sock, vmsg)) { + vu_panic(dev, "failed to respond to set-mem-table for postcopy"); + return false; } - close(vmsg->fds[i]); + /* + * Wait for QEMU to confirm that it's registered the handler for the + * faults. + */ + if (!dev->read_msg(dev, dev->sock, vmsg) || + vmsg->size != sizeof(vmsg->payload.u64) || + vmsg->payload.u64 != 0) { + vu_panic(dev, "failed to receive valid ack for postcopy set-mem-table"); + return false; + } + + /* OK, now we can go and register the memory and generate faults */ + (void)generate_faults(dev); + return false; } for (i = 0; i < dev->max_queues; i++) { @@ -1353,7 +1455,7 @@ bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd, int qidx = vq - dev->vq; int fd_num = 0; VhostUserMsg vmsg = { - .request = VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG, + .request = VHOST_USER_BACKEND_VRING_HOST_NOTIFIER_MSG, .flags = VHOST_USER_VERSION | VHOST_USER_NEED_REPLY_MASK, .size = sizeof(vmsg.payload.area), .payload.area = { @@ -1371,20 +1473,119 @@ bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd, vmsg.fd_num = fd_num; - if (!vu_has_protocol_feature(dev, VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD)) { + if (!vu_has_protocol_feature(dev, VHOST_USER_PROTOCOL_F_BACKEND_SEND_FD)) { return false; } - pthread_mutex_lock(&dev->slave_mutex); - if (!vu_message_write(dev, dev->slave_fd, &vmsg)) { - pthread_mutex_unlock(&dev->slave_mutex); + pthread_mutex_lock(&dev->backend_mutex); + if (!vu_message_write(dev, dev->backend_fd, &vmsg)) { + pthread_mutex_unlock(&dev->backend_mutex); return false; } - /* Also unlocks the slave_mutex */ + /* Also unlocks the backend_mutex */ return vu_process_message_reply(dev, &vmsg); } +bool +vu_lookup_shared_object(VuDev *dev, unsigned char uuid[UUID_LEN], + int *dmabuf_fd) +{ + bool result = false; + VhostUserMsg msg_reply; + VhostUserMsg msg = { + .request = VHOST_USER_BACKEND_SHARED_OBJECT_LOOKUP, + .size = sizeof(msg.payload.object), + .flags = VHOST_USER_VERSION | VHOST_USER_NEED_REPLY_MASK, + }; + + memcpy(msg.payload.object.uuid, uuid, sizeof(uuid[0]) * UUID_LEN); + + if (!vu_has_protocol_feature(dev, VHOST_USER_PROTOCOL_F_SHARED_OBJECT)) { + return false; + } + + pthread_mutex_lock(&dev->backend_mutex); + if (!vu_message_write(dev, dev->backend_fd, &msg)) { + goto out; + } + + if (!vu_message_read_default(dev, dev->backend_fd, &msg_reply)) { + goto out; + } + + if (msg_reply.request != msg.request) { + DPRINT("Received unexpected msg type. Expected %d, received %d", + msg.request, msg_reply.request); + goto out; + } + + if (msg_reply.fd_num != 1) { + DPRINT("Received unexpected number of fds. Expected 1, received %d", + msg_reply.fd_num); + goto out; + } + + *dmabuf_fd = msg_reply.fds[0]; + result = *dmabuf_fd > 0 && msg_reply.payload.u64 == 0; +out: + pthread_mutex_unlock(&dev->backend_mutex); + + return result; +} + +static bool +vu_send_message(VuDev *dev, VhostUserMsg *vmsg) +{ + bool result = false; + pthread_mutex_lock(&dev->backend_mutex); + if (!vu_message_write(dev, dev->backend_fd, vmsg)) { + goto out; + } + + result = true; +out: + pthread_mutex_unlock(&dev->backend_mutex); + + return result; +} + +bool +vu_add_shared_object(VuDev *dev, unsigned char uuid[UUID_LEN]) +{ + VhostUserMsg msg = { + .request = VHOST_USER_BACKEND_SHARED_OBJECT_ADD, + .size = sizeof(msg.payload.object), + .flags = VHOST_USER_VERSION, + }; + + memcpy(msg.payload.object.uuid, uuid, sizeof(uuid[0]) * UUID_LEN); + + if (!vu_has_protocol_feature(dev, VHOST_USER_PROTOCOL_F_SHARED_OBJECT)) { + return false; + } + + return vu_send_message(dev, &msg); +} + +bool +vu_rm_shared_object(VuDev *dev, unsigned char uuid[UUID_LEN]) +{ + VhostUserMsg msg = { + .request = VHOST_USER_BACKEND_SHARED_OBJECT_REMOVE, + .size = sizeof(msg.payload.object), + .flags = VHOST_USER_VERSION, + }; + + memcpy(msg.payload.object.uuid, uuid, sizeof(uuid[0]) * UUID_LEN); + + if (!vu_has_protocol_feature(dev, VHOST_USER_PROTOCOL_F_SHARED_OBJECT)) { + return false; + } + + return vu_send_message(dev, &msg); +} + static bool vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg) { @@ -1445,13 +1646,13 @@ vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg) * 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.) + * implemented by the frontend.) */ 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_BACKEND_REQ | 1ULL << VHOST_USER_PROTOCOL_F_HOST_NOTIFIER | - 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD | + 1ULL << VHOST_USER_PROTOCOL_F_BACKEND_SEND_FD | 1ULL << VHOST_USER_PROTOCOL_F_REPLY_ACK | 1ULL << VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS; @@ -1482,7 +1683,7 @@ vu_set_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg) 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_BACKEND_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 @@ -1490,12 +1691,12 @@ vu_set_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg) * 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 + * socket will eventually cause the frontend 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"); + "F_IN_BAND_NOTIFICATIONS requires F_BACKEND_REQ && F_REPLY_ACK"); return false; } @@ -1532,18 +1733,18 @@ vu_set_vring_enable_exec(VuDev *dev, VhostUserMsg *vmsg) } static bool -vu_set_slave_req_fd(VuDev *dev, VhostUserMsg *vmsg) +vu_set_backend_req_fd(VuDev *dev, VhostUserMsg *vmsg) { if (vmsg->fd_num != 1) { - vu_panic(dev, "Invalid slave_req_fd message (%d fd's)", vmsg->fd_num); + vu_panic(dev, "Invalid backend_req_fd message (%d fd's)", vmsg->fd_num); return false; } - if (dev->slave_fd != -1) { - close(dev->slave_fd); + if (dev->backend_fd != -1) { + close(dev->backend_fd); } - dev->slave_fd = vmsg->fds[0]; - DPRINT("Got slave_fd: %d\n", vmsg->fds[0]); + dev->backend_fd = vmsg->fds[0]; + DPRINT("Got backend_fd: %d\n", vmsg->fds[0]); return false; } @@ -1559,7 +1760,7 @@ vu_get_config(VuDev *dev, VhostUserMsg *vmsg) } if (ret) { - /* resize to zero to indicate an error to master */ + /* resize to zero to indicate an error to frontend */ vmsg->size = 0; } @@ -1587,12 +1788,13 @@ vu_set_config(VuDev *dev, VhostUserMsg *vmsg) static bool vu_set_postcopy_advise(VuDev *dev, VhostUserMsg *vmsg) { - dev->postcopy_ufd = -1; #ifdef UFFDIO_API struct uffdio_api api_struct; dev->postcopy_ufd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); vmsg->size = 0; +#else + dev->postcopy_ufd = -1; #endif if (dev->postcopy_ufd == -1) { @@ -1827,18 +2029,11 @@ vu_handle_vring_kick(VuDev *dev, VhostUserMsg *vmsg) static bool vu_handle_get_max_memslots(VuDev *dev, VhostUserMsg *vmsg) { - vmsg->flags = VHOST_USER_REPLY_MASK | VHOST_USER_VERSION; - vmsg->size = sizeof(vmsg->payload.u64); - vmsg->payload.u64 = VHOST_USER_MAX_RAM_SLOTS; - vmsg->fd_num = 0; - - if (!vu_message_write(dev, dev->sock, vmsg)) { - vu_panic(dev, "Failed to send max ram slots: %s\n", strerror(errno)); - } + vmsg_set_reply_u64(vmsg, VHOST_USER_MAX_RAM_SLOTS); DPRINT("u64: 0x%016"PRIx64"\n", (uint64_t) VHOST_USER_MAX_RAM_SLOTS); - return false; + return true; } static bool @@ -1904,8 +2099,8 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg) return vu_get_queue_num_exec(dev, vmsg); case VHOST_USER_SET_VRING_ENABLE: return vu_set_vring_enable_exec(dev, vmsg); - case VHOST_USER_SET_SLAVE_REQ_FD: - return vu_set_slave_req_fd(dev, vmsg); + case VHOST_USER_SET_BACKEND_REQ_FD: + return vu_set_backend_req_fd(dev, vmsg); case VHOST_USER_GET_CONFIG: return vu_get_config(dev, vmsg); case VHOST_USER_SET_CONFIG: @@ -1931,6 +2126,8 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg) return vu_add_mem_reg(dev, vmsg); case VHOST_USER_REM_MEM_REG: return vu_rem_mem_reg(dev, vmsg); + case VHOST_USER_GET_SHARED_OBJECT: + return vu_get_shared_object(dev, vmsg); default: vmsg_close_fds(vmsg); vu_panic(dev, "Unhandled request: %d", vmsg->request); @@ -1977,16 +2174,9 @@ end: void vu_deinit(VuDev *dev) { - int i; + unsigned int i; - for (i = 0; i < dev->nregions; i++) { - VuDevRegion *r = &dev->regions[i]; - void *m = (void *) (uintptr_t) r->mmap_addr; - if (m != MAP_FAILED) { - munmap(m, r->size + r->mmap_offset); - } - } - dev->nregions = 0; + vu_remove_all_mem_regs(dev); for (i = 0; i < dev->max_queues; i++) { VuVirtq *vq = &dev->vq[i]; @@ -2026,11 +2216,11 @@ vu_deinit(VuDev *dev) } vu_close_log(dev); - if (dev->slave_fd != -1) { - close(dev->slave_fd); - dev->slave_fd = -1; + if (dev->backend_fd != -1) { + close(dev->backend_fd); + dev->backend_fd = -1; } - pthread_mutex_destroy(&dev->slave_mutex); + pthread_mutex_destroy(&dev->backend_mutex); if (dev->sock != -1) { close(dev->sock); @@ -2038,6 +2228,8 @@ vu_deinit(VuDev *dev) free(dev->vq); dev->vq = NULL; + free(dev->regions); + dev->regions = NULL; } bool @@ -2068,13 +2260,21 @@ 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; + pthread_mutex_init(&dev->backend_mutex, NULL); + dev->backend_fd = -1; dev->max_queues = max_queues; + dev->regions = malloc(VHOST_USER_MAX_RAM_SLOTS * sizeof(dev->regions[0])); + if (!dev->regions) { + DPRINT("%s: failed to malloc mem regions\n", __func__); + return false; + } + dev->vq = malloc(max_queues * sizeof(dev->vq[0])); if (!dev->vq) { DPRINT("%s: failed to malloc virtqueues\n", __func__); + free(dev->regions); + dev->regions = NULL; return false; } @@ -2241,8 +2441,7 @@ vu_queue_get_avail_bytes(VuDev *dev, VuVirtq *vq, unsigned int *in_bytes, idx = vq->last_avail_idx; total_bufs = in_total = out_total = 0; - if (unlikely(dev->broken) || - unlikely(!vq->vring.avail)) { + if (!vu_is_vq_usable(dev, vq)) { goto done; } @@ -2357,8 +2556,7 @@ vu_queue_avail_bytes(VuDev *dev, VuVirtq *vq, unsigned int in_bytes, bool vu_queue_empty(VuDev *dev, VuVirtq *vq) { - if (unlikely(dev->broken) || - unlikely(!vq->vring.avail)) { + if (!vu_is_vq_usable(dev, vq)) { return true; } @@ -2397,8 +2595,7 @@ vring_notify(VuDev *dev, VuVirtq *vq) static void _vu_queue_notify(VuDev *dev, VuVirtq *vq, bool sync) { - if (unlikely(dev->broken) || - unlikely(!vq->vring.avail)) { + if (!vu_is_vq_usable(dev, vq)) { return; } @@ -2410,9 +2607,9 @@ static void _vu_queue_notify(VuDev *dev, VuVirtq *vq, bool sync) 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)) { + vu_has_protocol_feature(dev, VHOST_USER_PROTOCOL_F_BACKEND_REQ)) { VhostUserMsg vmsg = { - .request = VHOST_USER_SLAVE_VRING_CALL, + .request = VHOST_USER_BACKEND_VRING_CALL, .flags = VHOST_USER_VERSION, .size = sizeof(vmsg.payload.state), .payload.state = { @@ -2427,9 +2624,9 @@ static void _vu_queue_notify(VuDev *dev, VuVirtq *vq, bool sync) vmsg.flags |= VHOST_USER_NEED_REPLY_MASK; } - vu_message_write(dev, dev->slave_fd, &vmsg); + vu_message_write(dev, dev->backend_fd, &vmsg); if (ack) { - vu_message_read_default(dev, dev->slave_fd, &vmsg); + vu_message_read_default(dev, dev->backend_fd, &vmsg); } return; } @@ -2449,6 +2646,16 @@ void vu_queue_notify_sync(VuDev *dev, VuVirtq *vq) _vu_queue_notify(dev, vq, true); } +void vu_config_change_msg(VuDev *dev) +{ + VhostUserMsg vmsg = { + .request = VHOST_USER_BACKEND_CONFIG_CHANGE_MSG, + .flags = VHOST_USER_VERSION, + }; + + vu_message_write(dev, dev->backend_fd, &vmsg); +} + static inline void vring_used_flags_set_bit(VuVirtq *vq, int mask) { @@ -2472,14 +2679,13 @@ vring_used_flags_unset_bit(VuVirtq *vq, int mask) static inline void vring_set_avail_event(VuVirtq *vq, uint16_t val) { - uint16_t *avail; + uint16_t val_le = htole16(val); if (!vq->notification) { return; } - avail = (uint16_t *)&vq->vring.used->ring[vq->vring.num]; - *avail = htole16(val); + memcpy(&vq->vring.used->ring[vq->vring.num], &val_le, sizeof(uint16_t)); } void @@ -2548,6 +2754,10 @@ virtqueue_alloc_element(size_t sz, assert(sz >= sizeof(VuVirtqElement)); elem = malloc(out_sg_end); + if (!elem) { + DPRINT("%s: failed to malloc virtqueue element\n", __func__); + return NULL; + } elem->out_num = out_num; elem->in_num = in_num; elem->in_sg = (void *)elem + in_sg_ofs; @@ -2634,6 +2844,9 @@ vu_queue_map_desc(VuDev *dev, VuVirtq *vq, unsigned int idx, size_t sz) /* Now copy what we have collected and mapped */ elem = virtqueue_alloc_element(sz, out_num, in_num); + if (!elem) { + return NULL; + } elem->index = idx; for (i = 0; i < out_num; i++) { elem->out_sg[i] = iov[i]; @@ -2707,8 +2920,7 @@ vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz) unsigned int head; VuVirtqElement *elem; - if (unlikely(dev->broken) || - unlikely(!vq->vring.avail)) { + if (!vu_is_vq_usable(dev, vq)) { return NULL; } @@ -2865,8 +3077,7 @@ vu_queue_fill(VuDev *dev, VuVirtq *vq, { struct vring_used_elem uelem; - if (unlikely(dev->broken) || - unlikely(!vq->vring.avail)) { + if (!vu_is_vq_usable(dev, vq)) { return; } @@ -2895,8 +3106,7 @@ vu_queue_flush(VuDev *dev, VuVirtq *vq, unsigned int count) { uint16_t old, new; - if (unlikely(dev->broken) || - unlikely(!vq->vring.avail)) { + if (!vu_is_vq_usable(dev, vq)) { return; } diff --git a/subprojects/libvhost-user/libvhost-user.h b/subprojects/libvhost-user/libvhost-user.h index aea7ec5061..deb40e77b3 100644 --- a/subprojects/libvhost-user/libvhost-user.h +++ b/subprojects/libvhost-user/libvhost-user.h @@ -31,15 +31,17 @@ #define VHOST_MEMORY_BASELINE_NREGIONS 8 /* - * Set a reasonable maximum number of ram slots, which will be supported by - * any architecture. + * vhost in the kernel usually supports 509 mem slots. 509 used to be the + * KVM limit, it supported 512, but 3 were used for internal purposes. This + * limit is sufficient to support many DIMMs and virtio-mem in + * "dynamic-memslots" mode. */ -#define VHOST_USER_MAX_RAM_SLOTS 32 +#define VHOST_USER_MAX_RAM_SLOTS 509 #define VHOST_USER_HDR_SIZE offsetof(VhostUserMsg, payload.u64) typedef enum VhostSetConfigType { - VHOST_SET_CONFIG_TYPE_MASTER = 0, + VHOST_SET_CONFIG_TYPE_FRONTEND = 0, VHOST_SET_CONFIG_TYPE_MIGRATION = 1, } VhostSetConfigType; @@ -54,17 +56,19 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_RARP = 2, VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, VHOST_USER_PROTOCOL_F_NET_MTU = 4, - VHOST_USER_PROTOCOL_F_SLAVE_REQ = 5, + VHOST_USER_PROTOCOL_F_BACKEND_REQ = 5, VHOST_USER_PROTOCOL_F_CROSS_ENDIAN = 6, VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7, VHOST_USER_PROTOCOL_F_PAGEFAULT = 8, VHOST_USER_PROTOCOL_F_CONFIG = 9, - VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10, + VHOST_USER_PROTOCOL_F_BACKEND_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_CONFIGURE_MEM_SLOTS = 15, - + /* Feature 16 is reserved for VHOST_USER_PROTOCOL_F_STATUS. */ + /* Feature 17 reserved for VHOST_USER_PROTOCOL_F_XEN_MMAP. */ + VHOST_USER_PROTOCOL_F_SHARED_OBJECT = 18, VHOST_USER_PROTOCOL_F_MAX }; @@ -92,7 +96,7 @@ typedef enum VhostUserRequest { VHOST_USER_SET_VRING_ENABLE = 18, VHOST_USER_SEND_RARP = 19, VHOST_USER_NET_SET_MTU = 20, - VHOST_USER_SET_SLAVE_REQ_FD = 21, + VHOST_USER_SET_BACKEND_REQ_FD = 21, VHOST_USER_IOTLB_MSG = 22, VHOST_USER_SET_VRING_ENDIAN = 23, VHOST_USER_GET_CONFIG = 24, @@ -109,18 +113,22 @@ typedef enum VhostUserRequest { VHOST_USER_GET_MAX_MEM_SLOTS = 36, VHOST_USER_ADD_MEM_REG = 37, VHOST_USER_REM_MEM_REG = 38, + VHOST_USER_GET_SHARED_OBJECT = 41, VHOST_USER_MAX } VhostUserRequest; -typedef enum VhostUserSlaveRequest { - VHOST_USER_SLAVE_NONE = 0, - 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; +typedef enum VhostUserBackendRequest { + VHOST_USER_BACKEND_NONE = 0, + VHOST_USER_BACKEND_IOTLB_MSG = 1, + VHOST_USER_BACKEND_CONFIG_CHANGE_MSG = 2, + VHOST_USER_BACKEND_VRING_HOST_NOTIFIER_MSG = 3, + VHOST_USER_BACKEND_VRING_CALL = 4, + VHOST_USER_BACKEND_VRING_ERR = 5, + VHOST_USER_BACKEND_SHARED_OBJECT_ADD = 6, + VHOST_USER_BACKEND_SHARED_OBJECT_REMOVE = 7, + VHOST_USER_BACKEND_SHARED_OBJECT_LOOKUP = 8, + VHOST_USER_BACKEND_MAX +} VhostUserBackendRequest; typedef struct VhostUserMemoryRegion { uint64_t guest_phys_addr; @@ -172,6 +180,12 @@ typedef struct VhostUserInflight { uint16_t queue_size; } VhostUserInflight; +#define UUID_LEN 16 + +typedef struct VhostUserShared { + unsigned char uuid[UUID_LEN]; +} VhostUserShared; + #if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) # define VU_PACKED __attribute__((gcc_struct, packed)) #else @@ -199,6 +213,7 @@ typedef struct VhostUserMsg { VhostUserConfig config; VhostUserVringArea area; VhostUserInflight inflight; + VhostUserShared object; } payload; int fds[VHOST_MEMORY_BASELINE_NREGIONS]; @@ -232,6 +247,7 @@ typedef int (*vu_get_config_cb) (VuDev *dev, uint8_t *config, uint32_t len); typedef int (*vu_set_config_cb) (VuDev *dev, const uint8_t *data, uint32_t offset, uint32_t size, uint32_t flags); +typedef int (*vu_get_shared_object_cb) (VuDev *dev, const unsigned char *uuid); typedef struct VuDevIface { /* called by VHOST_USER_GET_FEATURES to get the features bitmask */ @@ -258,6 +274,8 @@ typedef struct VuDevIface { vu_get_config_cb get_config; /* set the config space of the device */ vu_set_config_cb set_config; + /* get virtio shared object from the underlying vhost implementation. */ + vu_get_shared_object_cb get_shared_object; } VuDevIface; typedef void (*vu_queue_handler_cb) (VuDev *dev, int qidx); @@ -296,8 +314,10 @@ typedef struct VuVirtqInflight { * Zero value indicates a vm reset happened. */ uint16_t version; - /* The size of VuDescStateSplit array. It's equal to the virtqueue - * size. Slave could get it from queue size field of VhostUserInflight. */ + /* + * The size of VuDescStateSplit array. It's equal to the virtqueue size. + * Backend could get it from queue size field of VhostUserInflight. + */ uint16_t desc_num; /* The head of list that track the last batch of used descriptors. */ @@ -343,7 +363,7 @@ typedef struct VuVirtq { /* Notification enabled? */ bool notification; - int inuse; + unsigned int inuse; vu_queue_handler_cb handler; @@ -380,13 +400,13 @@ typedef struct VuDevInflightInfo { struct VuDev { int sock; uint32_t nregions; - VuDevRegion regions[VHOST_USER_MAX_RAM_SLOTS]; + VuDevRegion *regions; VuVirtq *vq; VuDevInflightInfo inflight_info; int log_call_fd; - /* Must be held while using slave_fd */ - pthread_mutex_t slave_mutex; - int slave_fd; + /* Must be held while using backend_fd */ + pthread_mutex_t backend_mutex; + int backend_fd; uint64_t log_size; uint8_t *log_table; uint64_t features; @@ -445,7 +465,7 @@ typedef struct VuVirtqElement { * vu_init: * @dev: a VuDev context * @max_queues: maximum number of virtqueues - * @socket: the socket connected to vhost-user master + * @socket: the socket connected to vhost-user frontend * @panic: a panic callback * @set_watch: a set_watch callback * @remove_watch: a remove_watch callback @@ -539,6 +559,44 @@ void vu_set_queue_handler(VuDev *dev, VuVirtq *vq, bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd, int size, int offset); +/** + * vu_lookup_shared_object: + * @dev: a VuDev context + * @uuid: UUID of the shared object + * @dmabuf_fd: output dma-buf file descriptor + * + * Lookup for a virtio shared object (i.e., dma-buf fd) associated with the + * received UUID. Result, if found, is stored in the dmabuf_fd argument. + * + * Returns: whether the virtio object was found. + */ +bool vu_lookup_shared_object(VuDev *dev, unsigned char uuid[UUID_LEN], + int *dmabuf_fd); + +/** + * vu_add_shared_object: + * @dev: a VuDev context + * @uuid: UUID of the shared object + * + * Registers this back-end as the exporter for the object associated with + * the received UUID. + * + * Returns: TRUE on success, FALSE on failure. + */ +bool vu_add_shared_object(VuDev *dev, unsigned char uuid[UUID_LEN]); + +/** + * vu_rm_shared_object: + * @dev: a VuDev context + * @uuid: UUID of the shared object + * + * Removes a shared object entry (i.e., back-end entry) associated with the + * received UUID key from the hash table. + * + * Returns: TRUE on success, FALSE on failure. + */ +bool vu_rm_shared_object(VuDev *dev, unsigned char uuid[UUID_LEN]); + /** * vu_queue_set_notification: * @dev: a VuDev context @@ -585,6 +643,8 @@ bool vu_queue_empty(VuDev *dev, VuVirtq *vq); */ void vu_queue_notify(VuDev *dev, VuVirtq *vq); +void vu_config_change_msg(VuDev *dev); + /** * vu_queue_notify_sync: * @dev: a VuDev context diff --git a/subprojects/libvhost-user/meson.build b/subprojects/libvhost-user/meson.build index 39825d9404..a18014e7f2 100644 --- a/subprojects/libvhost-user/meson.build +++ b/subprojects/libvhost-user/meson.build @@ -1,6 +1,12 @@ project('libvhost-user', 'c', license: 'GPL-2.0-or-later', - default_options: ['c_std=gnu99']) + default_options: ['warning_level=1', 'c_std=gnu99']) + +cc = meson.get_compiler('c') +add_project_arguments(cc.get_supported_arguments('-Wsign-compare', + '-Wdeclaration-after-statement', + '-Wstrict-aliasing'), + native: false, language: 'c') threads = dependency('threads') glib = dependency('glib-2.0') diff --git a/subprojects/packagefiles/berkeley-softfloat-3/meson.build b/subprojects/packagefiles/berkeley-softfloat-3/meson.build new file mode 100644 index 0000000000..4ce964b838 --- /dev/null +++ b/subprojects/packagefiles/berkeley-softfloat-3/meson.build @@ -0,0 +1,339 @@ +project('berkeley-softfloat-3', 'c', + default_options: ['warning_level=1', 'c_std=gnu99']) + +fpcflags = get_option('defines') + +platform_data = configuration_data() +platform_data.set('INLINE', 'static inline') +platform_data.set('LITTLEENDIAN', host_machine.endian() == 'little') +configure_file(output: 'platform.h', configuration: platform_data) + +sfdir = 'source' +sfspedir = sfdir / '8086-SSE' +sfinc = include_directories('.', sfdir / 'include', sfspedir) + +add_project_arguments([ + '-Wno-implicit-fallthrough', + '-Wno-missing-prototypes', + '-Wno-redundant-decls', + '-Wno-return-type', + '-Wno-error', +], native: false, language: 'c') + +libsoftfloat = static_library( + 'softfloat', + files( + # primitives + sfdir / 's_eq128.c', + sfdir / 's_le128.c', + sfdir / 's_lt128.c', + sfdir / 's_shortShiftLeft128.c', + sfdir / 's_shortShiftRight128.c', + sfdir / 's_shortShiftRightJam64.c', + sfdir / 's_shortShiftRightJam64Extra.c', + sfdir / 's_shortShiftRightJam128.c', + sfdir / 's_shortShiftRightJam128Extra.c', + sfdir / 's_shiftRightJam32.c', + sfdir / 's_shiftRightJam64.c', + sfdir / 's_shiftRightJam64Extra.c', + sfdir / 's_shiftRightJam128.c', + sfdir / 's_shiftRightJam128Extra.c', + sfdir / 's_shiftRightJam256M.c', + sfdir / 's_countLeadingZeros8.c', + sfdir / 's_countLeadingZeros16.c', + sfdir / 's_countLeadingZeros32.c', + sfdir / 's_countLeadingZeros64.c', + sfdir / 's_add128.c', + sfdir / 's_add256M.c', + sfdir / 's_sub128.c', + sfdir / 's_sub256M.c', + sfdir / 's_mul64ByShifted32To128.c', + sfdir / 's_mul64To128.c', + sfdir / 's_mul128By32.c', + sfdir / 's_mul128To256M.c', + sfdir / 's_approxRecip_1Ks.c', + sfdir / 's_approxRecip32_1.c', + sfdir / 's_approxRecipSqrt_1Ks.c', + sfdir / 's_approxRecipSqrt32_1.c', + # others + sfdir / 's_roundToUI32.c', + sfdir / 's_roundToUI64.c', + sfdir / 's_roundToI32.c', + sfdir / 's_roundToI64.c', + sfdir / 's_normSubnormalF16Sig.c', + sfdir / 's_roundPackToF16.c', + sfdir / 's_normRoundPackToF16.c', + sfdir / 's_addMagsF16.c', + sfdir / 's_subMagsF16.c', + sfdir / 's_mulAddF16.c', + sfdir / 's_normSubnormalF32Sig.c', + sfdir / 's_roundPackToF32.c', + sfdir / 's_normRoundPackToF32.c', + sfdir / 's_addMagsF32.c', + sfdir / 's_subMagsF32.c', + sfdir / 's_mulAddF32.c', + sfdir / 's_normSubnormalF64Sig.c', + sfdir / 's_roundPackToF64.c', + sfdir / 's_normRoundPackToF64.c', + sfdir / 's_addMagsF64.c', + sfdir / 's_subMagsF64.c', + sfdir / 's_mulAddF64.c', + sfdir / 's_normSubnormalExtF80Sig.c', + sfdir / 's_roundPackToExtF80.c', + sfdir / 's_normRoundPackToExtF80.c', + sfdir / 's_addMagsExtF80.c', + sfdir / 's_subMagsExtF80.c', + sfdir / 's_normSubnormalF128Sig.c', + sfdir / 's_roundPackToF128.c', + sfdir / 's_normRoundPackToF128.c', + sfdir / 's_addMagsF128.c', + sfdir / 's_subMagsF128.c', + sfdir / 's_mulAddF128.c', + sfdir / 'softfloat_state.c', + sfdir / 'ui32_to_f16.c', + sfdir / 'ui32_to_f32.c', + sfdir / 'ui32_to_f64.c', + sfdir / 'ui32_to_extF80.c', + sfdir / 'ui32_to_extF80M.c', + sfdir / 'ui32_to_f128.c', + sfdir / 'ui32_to_f128M.c', + sfdir / 'ui64_to_f16.c', + sfdir / 'ui64_to_f32.c', + sfdir / 'ui64_to_f64.c', + sfdir / 'ui64_to_extF80.c', + sfdir / 'ui64_to_extF80M.c', + sfdir / 'ui64_to_f128.c', + sfdir / 'ui64_to_f128M.c', + sfdir / 'i32_to_f16.c', + sfdir / 'i32_to_f32.c', + sfdir / 'i32_to_f64.c', + sfdir / 'i32_to_extF80.c', + sfdir / 'i32_to_extF80M.c', + sfdir / 'i32_to_f128.c', + sfdir / 'i32_to_f128M.c', + sfdir / 'i64_to_f16.c', + sfdir / 'i64_to_f32.c', + sfdir / 'i64_to_f64.c', + sfdir / 'i64_to_extF80.c', + sfdir / 'i64_to_extF80M.c', + sfdir / 'i64_to_f128.c', + sfdir / 'i64_to_f128M.c', + sfdir / 'f16_to_ui32.c', + sfdir / 'f16_to_ui64.c', + sfdir / 'f16_to_i32.c', + sfdir / 'f16_to_i64.c', + sfdir / 'f16_to_ui32_r_minMag.c', + sfdir / 'f16_to_ui64_r_minMag.c', + sfdir / 'f16_to_i32_r_minMag.c', + sfdir / 'f16_to_i64_r_minMag.c', + sfdir / 'f16_to_f32.c', + sfdir / 'f16_to_f64.c', + sfdir / 'f16_to_extF80.c', + sfdir / 'f16_to_extF80M.c', + sfdir / 'f16_to_f128.c', + sfdir / 'f16_to_f128M.c', + sfdir / 'f16_roundToInt.c', + sfdir / 'f16_add.c', + sfdir / 'f16_sub.c', + sfdir / 'f16_mul.c', + sfdir / 'f16_mulAdd.c', + sfdir / 'f16_div.c', + sfdir / 'f16_rem.c', + sfdir / 'f16_sqrt.c', + sfdir / 'f16_eq.c', + sfdir / 'f16_le.c', + sfdir / 'f16_lt.c', + sfdir / 'f16_eq_signaling.c', + sfdir / 'f16_le_quiet.c', + sfdir / 'f16_lt_quiet.c', + sfdir / 'f16_isSignalingNaN.c', + sfdir / 'f32_to_ui32.c', + sfdir / 'f32_to_ui64.c', + sfdir / 'f32_to_i32.c', + sfdir / 'f32_to_i64.c', + sfdir / 'f32_to_ui32_r_minMag.c', + sfdir / 'f32_to_ui64_r_minMag.c', + sfdir / 'f32_to_i32_r_minMag.c', + sfdir / 'f32_to_i64_r_minMag.c', + sfdir / 'f32_to_f16.c', + sfdir / 'f32_to_f64.c', + sfdir / 'f32_to_extF80.c', + sfdir / 'f32_to_extF80M.c', + sfdir / 'f32_to_f128.c', + sfdir / 'f32_to_f128M.c', + sfdir / 'f32_roundToInt.c', + sfdir / 'f32_add.c', + sfdir / 'f32_sub.c', + sfdir / 'f32_mul.c', + sfdir / 'f32_mulAdd.c', + sfdir / 'f32_div.c', + sfdir / 'f32_rem.c', + sfdir / 'f32_sqrt.c', + sfdir / 'f32_eq.c', + sfdir / 'f32_le.c', + sfdir / 'f32_lt.c', + sfdir / 'f32_eq_signaling.c', + sfdir / 'f32_le_quiet.c', + sfdir / 'f32_lt_quiet.c', + sfdir / 'f32_isSignalingNaN.c', + sfdir / 'f64_to_ui32.c', + sfdir / 'f64_to_ui64.c', + sfdir / 'f64_to_i32.c', + sfdir / 'f64_to_i64.c', + sfdir / 'f64_to_ui32_r_minMag.c', + sfdir / 'f64_to_ui64_r_minMag.c', + sfdir / 'f64_to_i32_r_minMag.c', + sfdir / 'f64_to_i64_r_minMag.c', + sfdir / 'f64_to_f16.c', + sfdir / 'f64_to_f32.c', + sfdir / 'f64_to_extF80.c', + sfdir / 'f64_to_extF80M.c', + sfdir / 'f64_to_f128.c', + sfdir / 'f64_to_f128M.c', + sfdir / 'f64_roundToInt.c', + sfdir / 'f64_add.c', + sfdir / 'f64_sub.c', + sfdir / 'f64_mul.c', + sfdir / 'f64_mulAdd.c', + sfdir / 'f64_div.c', + sfdir / 'f64_rem.c', + sfdir / 'f64_sqrt.c', + sfdir / 'f64_eq.c', + sfdir / 'f64_le.c', + sfdir / 'f64_lt.c', + sfdir / 'f64_eq_signaling.c', + sfdir / 'f64_le_quiet.c', + sfdir / 'f64_lt_quiet.c', + sfdir / 'f64_isSignalingNaN.c', + sfdir / 'extF80_to_ui32.c', + sfdir / 'extF80_to_ui64.c', + sfdir / 'extF80_to_i32.c', + sfdir / 'extF80_to_i64.c', + sfdir / 'extF80_to_ui32_r_minMag.c', + sfdir / 'extF80_to_ui64_r_minMag.c', + sfdir / 'extF80_to_i32_r_minMag.c', + sfdir / 'extF80_to_i64_r_minMag.c', + sfdir / 'extF80_to_f16.c', + sfdir / 'extF80_to_f32.c', + sfdir / 'extF80_to_f64.c', + sfdir / 'extF80_to_f128.c', + sfdir / 'extF80_roundToInt.c', + sfdir / 'extF80_add.c', + sfdir / 'extF80_sub.c', + sfdir / 'extF80_mul.c', + sfdir / 'extF80_div.c', + sfdir / 'extF80_rem.c', + sfdir / 'extF80_sqrt.c', + sfdir / 'extF80_eq.c', + sfdir / 'extF80_le.c', + sfdir / 'extF80_lt.c', + sfdir / 'extF80_eq_signaling.c', + sfdir / 'extF80_le_quiet.c', + sfdir / 'extF80_lt_quiet.c', + sfdir / 'extF80_isSignalingNaN.c', + sfdir / 'extF80M_to_ui32.c', + sfdir / 'extF80M_to_ui64.c', + sfdir / 'extF80M_to_i32.c', + sfdir / 'extF80M_to_i64.c', + sfdir / 'extF80M_to_ui32_r_minMag.c', + sfdir / 'extF80M_to_ui64_r_minMag.c', + sfdir / 'extF80M_to_i32_r_minMag.c', + sfdir / 'extF80M_to_i64_r_minMag.c', + sfdir / 'extF80M_to_f16.c', + sfdir / 'extF80M_to_f32.c', + sfdir / 'extF80M_to_f64.c', + sfdir / 'extF80M_to_f128M.c', + sfdir / 'extF80M_roundToInt.c', + sfdir / 'extF80M_add.c', + sfdir / 'extF80M_sub.c', + sfdir / 'extF80M_mul.c', + sfdir / 'extF80M_div.c', + sfdir / 'extF80M_rem.c', + sfdir / 'extF80M_sqrt.c', + sfdir / 'extF80M_eq.c', + sfdir / 'extF80M_le.c', + sfdir / 'extF80M_lt.c', + sfdir / 'extF80M_eq_signaling.c', + sfdir / 'extF80M_le_quiet.c', + sfdir / 'extF80M_lt_quiet.c', + sfdir / 'f128_to_ui32.c', + sfdir / 'f128_to_ui64.c', + sfdir / 'f128_to_i32.c', + sfdir / 'f128_to_i64.c', + sfdir / 'f128_to_ui32_r_minMag.c', + sfdir / 'f128_to_ui64_r_minMag.c', + sfdir / 'f128_to_i32_r_minMag.c', + sfdir / 'f128_to_i64_r_minMag.c', + sfdir / 'f128_to_f16.c', + sfdir / 'f128_to_f32.c', + sfdir / 'f128_to_extF80.c', + sfdir / 'f128_to_f64.c', + sfdir / 'f128_roundToInt.c', + sfdir / 'f128_add.c', + sfdir / 'f128_sub.c', + sfdir / 'f128_mul.c', + sfdir / 'f128_mulAdd.c', + sfdir / 'f128_div.c', + sfdir / 'f128_rem.c', + sfdir / 'f128_sqrt.c', + sfdir / 'f128_eq.c', + sfdir / 'f128_le.c', + sfdir / 'f128_lt.c', + sfdir / 'f128_eq_signaling.c', + sfdir / 'f128_le_quiet.c', + sfdir / 'f128_lt_quiet.c', + sfdir / 'f128_isSignalingNaN.c', + sfdir / 'f128M_to_ui32.c', + sfdir / 'f128M_to_ui64.c', + sfdir / 'f128M_to_i32.c', + sfdir / 'f128M_to_i64.c', + sfdir / 'f128M_to_ui32_r_minMag.c', + sfdir / 'f128M_to_ui64_r_minMag.c', + sfdir / 'f128M_to_i32_r_minMag.c', + sfdir / 'f128M_to_i64_r_minMag.c', + sfdir / 'f128M_to_f16.c', + sfdir / 'f128M_to_f32.c', + sfdir / 'f128M_to_extF80M.c', + sfdir / 'f128M_to_f64.c', + sfdir / 'f128M_roundToInt.c', + sfdir / 'f128M_add.c', + sfdir / 'f128M_sub.c', + sfdir / 'f128M_mul.c', + sfdir / 'f128M_mulAdd.c', + sfdir / 'f128M_div.c', + sfdir / 'f128M_rem.c', + sfdir / 'f128M_sqrt.c', + sfdir / 'f128M_eq.c', + sfdir / 'f128M_le.c', + sfdir / 'f128M_lt.c', + sfdir / 'f128M_eq_signaling.c', + sfdir / 'f128M_le_quiet.c', + sfdir / 'f128M_lt_quiet.c', + # spe + sfspedir / 'softfloat_raiseFlags.c', + sfspedir / 's_f16UIToCommonNaN.c', + sfspedir / 's_commonNaNToF16UI.c', + sfspedir / 's_propagateNaNF16UI.c', + sfspedir / 's_f32UIToCommonNaN.c', + sfspedir / 's_commonNaNToF32UI.c', + sfspedir / 's_propagateNaNF32UI.c', + sfspedir / 's_f64UIToCommonNaN.c', + sfspedir / 's_commonNaNToF64UI.c', + sfspedir / 's_propagateNaNF64UI.c', + sfspedir / 'extF80M_isSignalingNaN.c', + sfspedir / 's_extF80UIToCommonNaN.c', + sfspedir / 's_commonNaNToExtF80UI.c', + sfspedir / 's_propagateNaNExtF80UI.c', + sfspedir / 'f128M_isSignalingNaN.c', + sfspedir / 's_f128UIToCommonNaN.c', + sfspedir / 's_commonNaNToF128UI.c', + sfspedir / 's_propagateNaNF128UI.c', + ), + include_directories: sfinc, + c_args: fpcflags, +) + +libsoftfloat_dep = declare_dependency( + link_with: libsoftfloat, + include_directories: sfinc, + compile_args: fpcflags) diff --git a/subprojects/packagefiles/berkeley-softfloat-3/meson_options.txt b/subprojects/packagefiles/berkeley-softfloat-3/meson_options.txt new file mode 100644 index 0000000000..868ae57e80 --- /dev/null +++ b/subprojects/packagefiles/berkeley-softfloat-3/meson_options.txt @@ -0,0 +1 @@ +option('defines', type : 'array', value : []) diff --git a/subprojects/packagefiles/berkeley-testfloat-3/meson.build b/subprojects/packagefiles/berkeley-testfloat-3/meson.build new file mode 100644 index 0000000000..a41673d616 --- /dev/null +++ b/subprojects/packagefiles/berkeley-testfloat-3/meson.build @@ -0,0 +1,220 @@ +project('berkeley-testfloat-3', 'c', + default_options: ['warning_level=1', 'c_std=gnu99']) + +fpcflags = get_option('defines') + +platform_data = configuration_data() +platform_data.set('INLINE', 'static inline') +platform_data.set('LITTLEENDIAN', host_machine.endian() == 'little') +configure_file(output: 'platform.h', configuration: platform_data) + +tfdir = 'source' +tfinc = include_directories('.', tfdir) + +add_project_arguments( + [ + '-Wno-implicit-fallthrough', + '-Wno-strict-prototypes', + '-Wno-unknown-pragmas', + '-Wno-uninitialized', + '-Wno-missing-prototypes', + '-Wno-return-type', + '-Wno-unused-function', + '-Wno-missing-format-attribute', + '-Wno-error', + ] + meson.get_compiler('c').get_supported_arguments('-Wno-ignored-pragmas'), + native: false, language: 'c') + +tfgencases = [ + tfdir / 'genCases_ui32.c', + tfdir / 'genCases_ui64.c', + tfdir / 'genCases_i32.c', + tfdir / 'genCases_i64.c', + tfdir / 'genCases_f16.c', + tfdir / 'genCases_f32.c', + tfdir / 'genCases_f64.c', + tfdir / 'genCases_extF80.c', + tfdir / 'genCases_f128.c', +] + +tfwritecase = [ + tfdir / 'writeCase_a_ui32.c', + tfdir / 'writeCase_a_ui64.c', + tfdir / 'writeCase_a_f16.c', + tfdir / 'writeCase_ab_f16.c', + tfdir / 'writeCase_abc_f16.c', + tfdir / 'writeCase_a_f32.c', + tfdir / 'writeCase_ab_f32.c', + tfdir / 'writeCase_abc_f32.c', + tfdir / 'writeCase_a_f64.c', + tfdir / 'writeCase_ab_f64.c', + tfdir / 'writeCase_abc_f64.c', + tfdir / 'writeCase_a_extF80M.c', + tfdir / 'writeCase_ab_extF80M.c', + tfdir / 'writeCase_a_f128M.c', + tfdir / 'writeCase_ab_f128M.c', + tfdir / 'writeCase_abc_f128M.c', + tfdir / 'writeCase_z_bool.c', + tfdir / 'writeCase_z_ui32.c', + tfdir / 'writeCase_z_ui64.c', + tfdir / 'writeCase_z_f16.c', + tfdir / 'writeCase_z_f32.c', + tfdir / 'writeCase_z_f64.c', + tfdir / 'writeCase_z_extF80M.c', + tfdir / 'writeCase_z_f128M.c', +] + +tftest = [ + tfdir / 'test_a_ui32_z_f16.c', + tfdir / 'test_a_ui32_z_f32.c', + tfdir / 'test_a_ui32_z_f64.c', + tfdir / 'test_a_ui32_z_extF80.c', + tfdir / 'test_a_ui32_z_f128.c', + tfdir / 'test_a_ui64_z_f16.c', + tfdir / 'test_a_ui64_z_f32.c', + tfdir / 'test_a_ui64_z_f64.c', + tfdir / 'test_a_ui64_z_extF80.c', + tfdir / 'test_a_ui64_z_f128.c', + tfdir / 'test_a_i32_z_f16.c', + tfdir / 'test_a_i32_z_f32.c', + tfdir / 'test_a_i32_z_f64.c', + tfdir / 'test_a_i32_z_extF80.c', + tfdir / 'test_a_i32_z_f128.c', + tfdir / 'test_a_i64_z_f16.c', + tfdir / 'test_a_i64_z_f32.c', + tfdir / 'test_a_i64_z_f64.c', + tfdir / 'test_a_i64_z_extF80.c', + tfdir / 'test_a_i64_z_f128.c', + tfdir / 'test_a_f16_z_ui32_rx.c', + tfdir / 'test_a_f16_z_ui64_rx.c', + tfdir / 'test_a_f16_z_i32_rx.c', + tfdir / 'test_a_f16_z_i64_rx.c', + tfdir / 'test_a_f16_z_ui32_x.c', + tfdir / 'test_a_f16_z_ui64_x.c', + tfdir / 'test_a_f16_z_i32_x.c', + tfdir / 'test_a_f16_z_i64_x.c', + tfdir / 'test_a_f16_z_f32.c', + tfdir / 'test_a_f16_z_f64.c', + tfdir / 'test_a_f16_z_extF80.c', + tfdir / 'test_a_f16_z_f128.c', + tfdir / 'test_az_f16.c', + tfdir / 'test_az_f16_rx.c', + tfdir / 'test_abz_f16.c', + tfdir / 'test_abcz_f16.c', + tfdir / 'test_ab_f16_z_bool.c', + tfdir / 'test_a_f32_z_ui32_rx.c', + tfdir / 'test_a_f32_z_ui64_rx.c', + tfdir / 'test_a_f32_z_i32_rx.c', + tfdir / 'test_a_f32_z_i64_rx.c', + tfdir / 'test_a_f32_z_ui32_x.c', + tfdir / 'test_a_f32_z_ui64_x.c', + tfdir / 'test_a_f32_z_i32_x.c', + tfdir / 'test_a_f32_z_i64_x.c', + tfdir / 'test_a_f32_z_f16.c', + tfdir / 'test_a_f32_z_f64.c', + tfdir / 'test_a_f32_z_extF80.c', + tfdir / 'test_a_f32_z_f128.c', + tfdir / 'test_az_f32.c', + tfdir / 'test_az_f32_rx.c', + tfdir / 'test_abz_f32.c', + tfdir / 'test_abcz_f32.c', + tfdir / 'test_ab_f32_z_bool.c', + tfdir / 'test_a_f64_z_ui32_rx.c', + tfdir / 'test_a_f64_z_ui64_rx.c', + tfdir / 'test_a_f64_z_i32_rx.c', + tfdir / 'test_a_f64_z_i64_rx.c', + tfdir / 'test_a_f64_z_ui32_x.c', + tfdir / 'test_a_f64_z_ui64_x.c', + tfdir / 'test_a_f64_z_i32_x.c', + tfdir / 'test_a_f64_z_i64_x.c', + tfdir / 'test_a_f64_z_f16.c', + tfdir / 'test_a_f64_z_f32.c', + tfdir / 'test_a_f64_z_extF80.c', + tfdir / 'test_a_f64_z_f128.c', + tfdir / 'test_az_f64.c', + tfdir / 'test_az_f64_rx.c', + tfdir / 'test_abz_f64.c', + tfdir / 'test_abcz_f64.c', + tfdir / 'test_ab_f64_z_bool.c', + tfdir / 'test_a_extF80_z_ui32_rx.c', + tfdir / 'test_a_extF80_z_ui64_rx.c', + tfdir / 'test_a_extF80_z_i32_rx.c', + tfdir / 'test_a_extF80_z_i64_rx.c', + tfdir / 'test_a_extF80_z_ui32_x.c', + tfdir / 'test_a_extF80_z_ui64_x.c', + tfdir / 'test_a_extF80_z_i32_x.c', + tfdir / 'test_a_extF80_z_i64_x.c', + tfdir / 'test_a_extF80_z_f16.c', + tfdir / 'test_a_extF80_z_f32.c', + tfdir / 'test_a_extF80_z_f64.c', + tfdir / 'test_a_extF80_z_f128.c', + tfdir / 'test_az_extF80.c', + tfdir / 'test_az_extF80_rx.c', + tfdir / 'test_abz_extF80.c', + tfdir / 'test_ab_extF80_z_bool.c', + tfdir / 'test_a_f128_z_ui32_rx.c', + tfdir / 'test_a_f128_z_ui64_rx.c', + tfdir / 'test_a_f128_z_i32_rx.c', + tfdir / 'test_a_f128_z_i64_rx.c', + tfdir / 'test_a_f128_z_ui32_x.c', + tfdir / 'test_a_f128_z_ui64_x.c', + tfdir / 'test_a_f128_z_i32_x.c', + tfdir / 'test_a_f128_z_i64_x.c', + tfdir / 'test_a_f128_z_f16.c', + tfdir / 'test_a_f128_z_f32.c', + tfdir / 'test_a_f128_z_f64.c', + tfdir / 'test_a_f128_z_extF80.c', + tfdir / 'test_az_f128.c', + tfdir / 'test_az_f128_rx.c', + tfdir / 'test_abz_f128.c', + tfdir / 'test_abcz_f128.c', + tfdir / 'test_ab_f128_z_bool.c', +] + +libsoftfloat_proj = subproject('berkeley-softfloat-3', required: true) +libsoftfloat = libsoftfloat_proj.get_variable('libsoftfloat_dep') + +libtestfloat = static_library( + 'testfloat', + files( + tfdir / 'uint128_inline.c', + tfdir / 'uint128.c', + tfdir / 'fail.c', + tfdir / 'functions_common.c', + tfdir / 'functionInfos.c', + tfdir / 'standardFunctionInfos.c', + tfdir / 'random.c', + tfdir / 'genCases_common.c', + tfgencases, + tfdir / 'genCases_writeTestsTotal.c', + tfdir / 'verCases_inline.c', + tfdir / 'verCases_common.c', + tfdir / 'verCases_writeFunctionName.c', + tfdir / 'readHex.c', + tfdir / 'writeHex.c', + tfwritecase, + tfdir / 'testLoops_common.c', + tftest, + ), + dependencies: libsoftfloat.partial_dependency(includes: true, compile_args: true), + c_args: fpcflags, +) + +libtestfloat_dep = declare_dependency( + link_with: libtestfloat, + dependencies: libsoftfloat, + include_directories: tfinc, + compile_args: fpcflags) + +libslowfloat = static_library( + 'slowfloat', + tfdir / 'slowfloat.c', + dependencies: libsoftfloat.partial_dependency(includes: true, compile_args: true), + c_args: fpcflags, +) + +libslowfloat_dep = declare_dependency( + link_with: libslowfloat, + dependencies: libsoftfloat, + include_directories: tfinc, + compile_args: fpcflags) diff --git a/subprojects/packagefiles/berkeley-testfloat-3/meson_options.txt b/subprojects/packagefiles/berkeley-testfloat-3/meson_options.txt new file mode 100644 index 0000000000..868ae57e80 --- /dev/null +++ b/subprojects/packagefiles/berkeley-testfloat-3/meson_options.txt @@ -0,0 +1 @@ +option('defines', type : 'array', value : []) diff --git a/subprojects/slirp.wrap b/subprojects/slirp.wrap new file mode 100644 index 0000000000..a93b048962 --- /dev/null +++ b/subprojects/slirp.wrap @@ -0,0 +1,6 @@ +[wrap-git] +url = https://gitlab.freedesktop.org/slirp/libslirp.git +revision = 26be815b86e8d49add8c9a8b320239b9594ff03d + +[provide] +slirp = libslirp_dep diff --git a/softmmu/arch_init.c b/system/arch_init.c similarity index 100% rename from softmmu/arch_init.c rename to system/arch_init.c diff --git a/system/async-teardown.c b/system/async-teardown.c new file mode 100644 index 0000000000..396963c091 --- /dev/null +++ b/system/async-teardown.c @@ -0,0 +1,143 @@ +/* + * Asynchronous teardown + * + * Copyright IBM, Corp. 2022 + * + * Authors: + * Claudio Imbrenda + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at your + * option) any later version. See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include +#include +#include + +#include "qemu/async-teardown.h" + +#ifdef _SC_THREAD_STACK_MIN +#define CLONE_STACK_SIZE sysconf(_SC_THREAD_STACK_MIN) +#else +#define CLONE_STACK_SIZE 16384 +#endif + +static pid_t the_ppid; + +/* + * Close all open file descriptors. + */ +static void close_all_open_fd(void) +{ + struct dirent *de; + int fd, dfd; + DIR *dir; + +#ifdef CONFIG_CLOSE_RANGE + int r = close_range(0, ~0U, 0); + if (!r) { + /* Success, no need to try other ways. */ + return; + } +#endif + + dir = opendir("/proc/self/fd"); + if (!dir) { + /* If /proc is not mounted, there is nothing that can be done. */ + return; + } + /* Avoid closing the directory. */ + dfd = dirfd(dir); + + for (de = readdir(dir); de; de = readdir(dir)) { + fd = atoi(de->d_name); + if (fd != dfd) { + close(fd); + } + } + closedir(dir); +} + +static void hup_handler(int signal) +{ + /* Check every second if this process has been reparented. */ + while (the_ppid == getppid()) { + /* sleep() is safe to use in a signal handler. */ + sleep(1); + } + + /* At this point the parent process has terminated completely. */ + _exit(0); +} + +static int async_teardown_fn(void *arg) +{ + struct sigaction sa = { .sa_handler = hup_handler }; + sigset_t hup_signal; + char name[16]; + + /* Set a meaningful name for this process. */ + snprintf(name, 16, "cleanup/%d", the_ppid); + prctl(PR_SET_NAME, (unsigned long)name); + + /* + * Close all file descriptors that might have been inherited from the + * main qemu process when doing clone, needed to make libvirt happy. + * Not using close_range for increased compatibility with older kernels. + */ + close_all_open_fd(); + + /* Set up a handler for SIGHUP and unblock SIGHUP. */ + sigaction(SIGHUP, &sa, NULL); + sigemptyset(&hup_signal); + sigaddset(&hup_signal, SIGHUP); + sigprocmask(SIG_UNBLOCK, &hup_signal, NULL); + + /* Ask to receive SIGHUP when the parent dies. */ + prctl(PR_SET_PDEATHSIG, SIGHUP); + + /* + * Sleep forever, unless the parent process has already terminated. The + * only interruption can come from the SIGHUP signal, which in normal + * operation is received when the parent process dies. + */ + if (the_ppid == getppid()) { + pause(); + } + + /* At this point the parent process has terminated completely. */ + _exit(0); +} + +/* + * Allocate a new stack of a reasonable size, and return a pointer to its top. + */ +static void *new_stack_for_clone(void) +{ + size_t stack_size = CLONE_STACK_SIZE; + char *stack_ptr; + + /* Allocate a new stack and get a pointer to its top. */ + stack_ptr = qemu_alloc_stack(&stack_size); + stack_ptr += stack_size; + + return stack_ptr; +} + +/* + * Block all signals, start (clone) a new process sharing the address space + * with qemu (CLONE_VM), then restore signals. + */ +void init_async_teardown(void) +{ + sigset_t all_signals, old_signals; + + the_ppid = getpid(); + + sigfillset(&all_signals); + sigprocmask(SIG_BLOCK, &all_signals, &old_signals); + clone(async_teardown_fn, new_stack_for_clone(), CLONE_VM, NULL); + sigprocmask(SIG_SETMASK, &old_signals, NULL); +} diff --git a/softmmu/balloon.c b/system/balloon.c similarity index 92% rename from softmmu/balloon.c rename to system/balloon.c index e0e8969a4b..fda7af832e 100644 --- a/softmmu/balloon.c +++ b/system/balloon.c @@ -90,17 +90,17 @@ BalloonInfo *qmp_query_balloon(Error **errp) return info; } -void qmp_balloon(int64_t target, Error **errp) +void qmp_balloon(int64_t value, Error **errp) { if (!have_balloon(errp)) { return; } - if (target <= 0) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "target", "a size"); + if (value <= 0) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "value", "a size"); return; } - trace_balloon_event(balloon_opaque, target); - balloon_event_fn(balloon_opaque, target); + trace_balloon_event(balloon_opaque, value); + balloon_event_fn(balloon_opaque, value); } diff --git a/softmmu/bootdevice.c b/system/bootdevice.c similarity index 96% rename from softmmu/bootdevice.c rename to system/bootdevice.c index 2106f1026f..2579b26dc8 100644 --- a/softmmu/bootdevice.c +++ b/system/bootdevice.c @@ -101,20 +101,23 @@ void validate_bootdevices(const char *devices, Error **errp) void restore_boot_order(void *opaque) { char *normal_boot_order = opaque; - static int first = 1; + static int bootcount; - /* Restore boot order and remove ourselves after the first boot */ - if (first) { - first = 0; + switch (bootcount++) { + case 0: + /* First boot: use the one-time config */ + return; + case 1: + /* Second boot: restore normal boot order */ + if (boot_set_handler) { + qemu_boot_set(normal_boot_order, &error_abort); + } + g_free(normal_boot_order); + return; + default: + /* Subsequent boots: keep using normal boot order */ return; } - - if (boot_set_handler) { - qemu_boot_set(normal_boot_order, &error_abort); - } - - qemu_unregister_reset(restore_boot_order, normal_boot_order); - g_free(normal_boot_order); } void check_boot_index(int32_t bootindex, Error **errp) diff --git a/softmmu/cpu-throttle.c b/system/cpu-throttle.c similarity index 96% rename from softmmu/cpu-throttle.c rename to system/cpu-throttle.c index d9bb30a223..c951a6c65e 100644 --- a/softmmu/cpu-throttle.c +++ b/system/cpu-throttle.c @@ -54,12 +54,12 @@ static void cpu_throttle_thread(CPUState *cpu, run_on_cpu_data opaque) endtime_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + sleeptime_ns; while (sleeptime_ns > 0 && !cpu->stop) { if (sleeptime_ns > SCALE_MS) { - qemu_cond_timedwait_iothread(cpu->halt_cond, + qemu_cond_timedwait_bql(cpu->halt_cond, sleeptime_ns / SCALE_MS); } else { - qemu_mutex_unlock_iothread(); + bql_unlock(); g_usleep(sleeptime_ns / SCALE_US); - qemu_mutex_lock_iothread(); + bql_lock(); } sleeptime_ns = endtime_ns - qemu_clock_get_ns(QEMU_CLOCK_REALTIME); } diff --git a/softmmu/cpu-timers.c b/system/cpu-timers.c similarity index 95% rename from softmmu/cpu-timers.c rename to system/cpu-timers.c index 117408cb83..0b31c9a1b6 100644 --- a/softmmu/cpu-timers.c +++ b/system/cpu-timers.c @@ -36,7 +36,7 @@ #include "hw/core/cpu.h" #include "sysemu/cpu-timers.h" #include "sysemu/cpu-throttle.h" -#include "timers-state.h" +#include "sysemu/cpu-timers-internal.h" /* clock and ticks */ @@ -154,7 +154,7 @@ static bool adjust_timers_state_needed(void *opaque) static bool icount_shift_state_needed(void *opaque) { - return icount_enabled() == 2; + return icount_enabled() == ICOUNT_ADAPTATIVE; } /* @@ -165,7 +165,7 @@ static const VMStateDescription icount_vmstate_warp_timer = { .version_id = 1, .minimum_version_id = 1, .needed = warp_timer_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64(vm_clock_warp_start, TimersState), VMSTATE_TIMER_PTR(icount_warp_timer, TimersState), VMSTATE_END_OF_LIST() @@ -177,7 +177,7 @@ static const VMStateDescription icount_vmstate_adjust_timers = { .version_id = 1, .minimum_version_id = 1, .needed = adjust_timers_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(icount_rt_timer, TimersState), VMSTATE_TIMER_PTR(icount_vm_timer, TimersState), VMSTATE_END_OF_LIST() @@ -189,7 +189,7 @@ static const VMStateDescription icount_vmstate_shift = { .version_id = 2, .minimum_version_id = 2, .needed = icount_shift_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT16(icount_time_shift, TimersState), VMSTATE_INT64(last_delta, TimersState), VMSTATE_END_OF_LIST() @@ -204,12 +204,12 @@ static const VMStateDescription icount_vmstate_timers = { .version_id = 1, .minimum_version_id = 1, .needed = icount_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64(qemu_icount_bias, TimersState), VMSTATE_INT64(qemu_icount, TimersState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &icount_vmstate_warp_timer, &icount_vmstate_adjust_timers, &icount_vmstate_shift, @@ -221,13 +221,13 @@ static const VMStateDescription vmstate_timers = { .name = "timer", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64(cpu_ticks_offset, TimersState), VMSTATE_UNUSED(8), VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &icount_vmstate_timers, NULL } diff --git a/softmmu/cpus.c b/system/cpus.c similarity index 87% rename from softmmu/cpus.c rename to system/cpus.c index 23b30484b2..68d161d96b 100644 --- a/softmmu/cpus.c +++ b/system/cpus.c @@ -34,6 +34,7 @@ #include "sysemu/hw_accel.h" #include "exec/cpu-common.h" #include "qemu/thread.h" +#include "qemu/main-loop.h" #include "qemu/plugin.h" #include "sysemu/cpus.h" #include "qemu/guest-random.h" @@ -64,7 +65,8 @@ #endif /* CONFIG_LINUX */ -static QemuMutex qemu_global_mutex; +/* The Big QEMU Lock (BQL) */ +static QemuMutex bql; /* * The chosen accelerator is supposed to register this. @@ -200,6 +202,13 @@ bool cpus_are_resettable(void) return true; } +void cpu_exec_reset_hold(CPUState *cpu) +{ + if (cpus_accel->cpu_reset_hold) { + cpus_accel->cpu_reset_hold(cpu); + } +} + int64_t cpus_get_virtual_clock(void) { /* @@ -251,14 +260,33 @@ void cpu_interrupt(CPUState *cpu, int mask) } } +/* + * True if the vm was previously suspended, and has not been woken or reset. + */ +static int vm_was_suspended; + +void vm_set_suspended(bool suspended) +{ + vm_was_suspended = suspended; +} + +bool vm_get_suspended(void) +{ + return vm_was_suspended; +} + static int do_vm_stop(RunState state, bool send_stop) { int ret = 0; + RunState oldstate = runstate_get(); - if (runstate_is_running()) { + if (runstate_is_live(oldstate)) { + vm_was_suspended = (oldstate == RUN_STATE_SUSPENDED); runstate_set(state); cpu_disable_ticks(); - pause_all_vcpus(); + if (oldstate == RUN_STATE_RUNNING) { + pause_all_vcpus(); + } vm_state_notify(0, state); if (send_stop) { qapi_event_send_stop(); @@ -354,7 +382,7 @@ static void qemu_init_sigbus(void) /* * ALERT: when modifying this, take care that SIGBUS forwarding in - * os_mem_prealloc() will continue working as expected. + * qemu_prealloc_mem() will continue working as expected. */ memset(&action, 0, sizeof(action)); action.sa_flags = SA_SIGINFO; @@ -381,14 +409,14 @@ void qemu_init_cpu_loop(void) qemu_init_sigbus(); qemu_cond_init(&qemu_cpu_cond); qemu_cond_init(&qemu_pause_cond); - qemu_mutex_init(&qemu_global_mutex); + qemu_mutex_init(&bql); qemu_thread_get_self(&io_thread); } void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data) { - do_run_on_cpu(cpu, func, data, &qemu_global_mutex); + do_run_on_cpu(cpu, func, data, &bql); } static void qemu_cpu_stop(CPUState *cpu, bool exit) @@ -404,7 +432,7 @@ static void qemu_cpu_stop(CPUState *cpu, bool exit) void qemu_wait_io_event_common(CPUState *cpu) { - qatomic_mb_set(&cpu->thread_kicked, false); + qatomic_set_mb(&cpu->thread_kicked, false); if (cpu->stop) { qemu_cpu_stop(cpu, false); } @@ -420,35 +448,30 @@ void qemu_wait_io_event(CPUState *cpu) slept = true; qemu_plugin_vcpu_idle_cb(cpu); } - qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex); + qemu_cond_wait(cpu->halt_cond, &bql); } if (slept) { qemu_plugin_vcpu_resume_cb(cpu); } -#ifdef _WIN32 - /* Eat dummy APC queued by cpus_kick_thread. */ - if (hax_enabled()) { - SleepEx(0, TRUE); - } -#endif qemu_wait_io_event_common(cpu); } void cpus_kick_thread(CPUState *cpu) { -#ifndef _WIN32 - int err; - if (cpu->thread_kicked) { return; } cpu->thread_kicked = true; - err = pthread_kill(cpu->thread->thread, SIG_IPI); + +#ifndef _WIN32 + int err = pthread_kill(cpu->thread->thread, SIG_IPI); if (err && err != ESRCH) { fprintf(stderr, "qemu:%s: %s", __func__, strerror(err)); exit(1); } +#else + qemu_sem_post(&cpu->sem); #endif } @@ -478,46 +501,46 @@ bool qemu_in_vcpu_thread(void) return current_cpu && qemu_cpu_is_self(current_cpu); } -QEMU_DEFINE_STATIC_CO_TLS(bool, iothread_locked) +QEMU_DEFINE_STATIC_CO_TLS(bool, bql_locked) -bool qemu_mutex_iothread_locked(void) +bool bql_locked(void) { - return get_iothread_locked(); + return get_bql_locked(); } bool qemu_in_main_thread(void) { - return qemu_mutex_iothread_locked(); + return bql_locked(); } /* * The BQL is taken from so many places that it is worth profiling the * callers directly, instead of funneling them all through a single function. */ -void qemu_mutex_lock_iothread_impl(const char *file, int line) +void bql_lock_impl(const char *file, int line) { - QemuMutexLockFunc bql_lock = qatomic_read(&qemu_bql_mutex_lock_func); + QemuMutexLockFunc bql_lock_fn = qatomic_read(&bql_mutex_lock_func); - g_assert(!qemu_mutex_iothread_locked()); - bql_lock(&qemu_global_mutex, file, line); - set_iothread_locked(true); + g_assert(!bql_locked()); + bql_lock_fn(&bql, file, line); + set_bql_locked(true); } -void qemu_mutex_unlock_iothread(void) +void bql_unlock(void) { - g_assert(qemu_mutex_iothread_locked()); - set_iothread_locked(false); - qemu_mutex_unlock(&qemu_global_mutex); + g_assert(bql_locked()); + set_bql_locked(false); + qemu_mutex_unlock(&bql); } -void qemu_cond_wait_iothread(QemuCond *cond) +void qemu_cond_wait_bql(QemuCond *cond) { - qemu_cond_wait(cond, &qemu_global_mutex); + qemu_cond_wait(cond, &bql); } -void qemu_cond_timedwait_iothread(QemuCond *cond, int ms) +void qemu_cond_timedwait_bql(QemuCond *cond, int ms) { - qemu_cond_timedwait(cond, &qemu_global_mutex, ms); + qemu_cond_timedwait(cond, &bql, ms); } /* signal CPU creation */ @@ -568,15 +591,15 @@ void pause_all_vcpus(void) replay_mutex_unlock(); while (!all_vcpus_paused()) { - qemu_cond_wait(&qemu_pause_cond, &qemu_global_mutex); + qemu_cond_wait(&qemu_pause_cond, &bql); CPU_FOREACH(cpu) { qemu_cpu_kick(cpu); } } - qemu_mutex_unlock_iothread(); + bql_unlock(); replay_mutex_lock(); - qemu_mutex_lock_iothread(); + bql_lock(); } void cpu_resume(CPUState *cpu) @@ -605,9 +628,9 @@ void cpu_remove_sync(CPUState *cpu) cpu->stop = true; cpu->unplug = true; qemu_cpu_kick(cpu); - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_thread_join(cpu->thread); - qemu_mutex_lock_iothread(); + bql_lock(); } void cpus_register_accel(const AccelOpsClass *ops) @@ -617,11 +640,18 @@ void cpus_register_accel(const AccelOpsClass *ops) cpus_accel = ops; } +const AccelOpsClass *cpus_get_accel(void) +{ + /* broken if we call this early */ + assert(cpus_accel); + return cpus_accel; +} + void qemu_init_vcpu(CPUState *cpu) { MachineState *ms = MACHINE(qdev_get_machine()); - cpu->nr_cores = ms->smp.cores; + cpu->nr_cores = machine_topo_get_cores_per_socket(ms); cpu->nr_threads = ms->smp.threads; cpu->stopped = true; cpu->random_seed = qemu_guest_random_seed_thread_part1(); @@ -639,7 +669,7 @@ void qemu_init_vcpu(CPUState *cpu) cpus_accel->create_vcpu_thread(cpu); while (!cpu->created) { - qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); + qemu_cond_wait(&qemu_cpu_cond, &bql); } } @@ -669,11 +699,13 @@ int vm_stop(RunState state) /** * Prepare for (re)starting the VM. - * Returns -1 if the vCPUs are not to be restarted (e.g. if they are already - * running or in case of an error condition), 0 otherwise. + * Returns 0 if the vCPUs should be restarted, -1 on an error condition, + * and 1 otherwise. */ int vm_prepare_start(bool step_pending) { + int ret = vm_was_suspended ? 1 : 0; + RunState state = vm_was_suspended ? RUN_STATE_SUSPENDED : RUN_STATE_RUNNING; RunState requested; qemu_vmstop_requested(&requested); @@ -704,9 +736,10 @@ int vm_prepare_start(bool step_pending) qapi_event_send_resume(); cpu_enable_ticks(); - runstate_set(RUN_STATE_RUNNING); - vm_state_notify(1, RUN_STATE_RUNNING); - return 0; + runstate_set(state); + vm_state_notify(1, state); + vm_was_suspended = false; + return ret; } void vm_start(void) @@ -716,11 +749,20 @@ void vm_start(void) } } +void vm_resume(RunState state) +{ + if (runstate_is_live(state)) { + vm_start(); + } else { + runstate_set(state); + } +} + /* does a state transition even if the VM is already stopped, current state is forgotten forever */ int vm_stop_force_state(RunState state) { - if (runstate_is_running()) { + if (runstate_is_live(runstate_get())) { return vm_stop(state); } else { int ret; diff --git a/softmmu/datadir.c b/system/datadir.c similarity index 79% rename from softmmu/datadir.c rename to system/datadir.c index 160cac999a..c9237cb5d4 100644 --- a/softmmu/datadir.c +++ b/system/datadir.c @@ -83,40 +83,22 @@ void qemu_add_data_dir(char *path) data_dir[data_dir_idx++] = path; } -/* - * 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 (possibly relocated). - * - * The caller must use g_free() to free the returned data when it is - * no longer required. - */ -static char *find_datadir(void) -{ - g_autofree char *dir = NULL; - - dir = g_build_filename(qemu_get_exec_dir(), "pc-bios", NULL); - if (g_file_test(dir, G_FILE_TEST_IS_DIR)) { - return g_steal_pointer(&dir); - } - - return get_relocated_path(CONFIG_QEMU_DATADIR); -} - void qemu_add_default_firmwarepath(void) { - char **dirs; + static const char * const dirs[] = { + CONFIG_QEMU_FIRMWAREPATH + NULL + }; + size_t i; /* add configured firmware directories */ - dirs = g_strsplit(CONFIG_QEMU_FIRMWAREPATH, G_SEARCHPATH_SEPARATOR_S, 0); for (i = 0; dirs[i] != NULL; i++) { qemu_add_data_dir(get_relocated_path(dirs[i])); } - g_strfreev(dirs); /* try to find datadir relative to the executable path */ - qemu_add_data_dir(find_datadir()); + qemu_add_data_dir(get_relocated_path(CONFIG_QEMU_DATADIR)); } void qemu_list_data_dirs(void) diff --git a/softmmu/device_tree.c b/system/device_tree.c similarity index 91% rename from softmmu/device_tree.c rename to system/device_tree.c index 6ca3fad285..eb5166ca36 100644 --- a/softmmu/device_tree.c +++ b/system/device_tree.c @@ -22,10 +22,14 @@ #include "qemu/option.h" #include "qemu/bswap.h" #include "qemu/cutils.h" +#include "qemu/guest-random.h" #include "sysemu/device_tree.h" #include "hw/loader.h" #include "hw/boards.h" #include "qemu/config-file.h" +#include "qapi/qapi-commands-machine.h" +#include "qapi/qmp/qdict.h" +#include "monitor/hmp.h" #include @@ -414,9 +418,9 @@ int qemu_fdt_setprop_string_array(void *fdt, const char *node_path, } p = str = g_malloc0(total_len); for (i = 0; i < len; i++) { - int len = strlen(array[i]) + 1; - pstrcpy(p, len, array[i]); - p += len; + int offset = strlen(array[i]) + 1; + pstrcpy(p, offset, array[i]); + p += offset; } ret = qemu_fdt_setprop(fdt, node_path, prop, str, total_len); @@ -643,3 +647,57 @@ out: g_free(propcells); return ret; } + +void qmp_dumpdtb(const char *filename, Error **errp) +{ + g_autoptr(GError) err = NULL; + uint32_t size; + + if (!current_machine->fdt) { + error_setg(errp, "This machine doesn't have a FDT"); + return; + } + + size = fdt_totalsize(current_machine->fdt); + + g_assert(size > 0); + + if (!g_file_set_contents(filename, current_machine->fdt, size, &err)) { + error_setg(errp, "Error saving FDT to file %s: %s", + filename, err->message); + } +} + +void hmp_dumpdtb(Monitor *mon, const QDict *qdict) +{ + const char *filename = qdict_get_str(qdict, "filename"); + Error *local_err = NULL; + + qmp_dumpdtb(filename, &local_err); + + if (hmp_handle_error(mon, local_err)) { + return; + } + + info_report("dtb dumped to %s", filename); +} + +void qemu_fdt_randomize_seeds(void *fdt) +{ + int noffset, poffset, len; + const char *name; + uint8_t *data; + + for (noffset = fdt_next_node(fdt, 0, NULL); + noffset >= 0; + noffset = fdt_next_node(fdt, noffset, NULL)) { + for (poffset = fdt_first_property_offset(fdt, noffset); + poffset >= 0; + poffset = fdt_next_property_offset(fdt, poffset)) { + data = (uint8_t *)fdt_getprop_by_offset(fdt, poffset, &name, &len); + if (!data || strcmp(name, "rng-seed")) + continue; + qemu_guest_getrandom_nofail(data, len); + } + } +} diff --git a/system/dirtylimit.c b/system/dirtylimit.c new file mode 100644 index 0000000000..ab20da34bb --- /dev/null +++ b/system/dirtylimit.c @@ -0,0 +1,677 @@ +/* + * Dirty page rate limit implementation code + * + * Copyright (c) 2022 CHINA TELECOM CO.,LTD. + * + * Authors: + * Hyman Huang(黄勇) + * + * 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 "qapi/qapi-commands-migration.h" +#include "qapi/qmp/qdict.h" +#include "qapi/error.h" +#include "sysemu/dirtyrate.h" +#include "sysemu/dirtylimit.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "exec/memory.h" +#include "exec/target_page.h" +#include "hw/boards.h" +#include "sysemu/kvm.h" +#include "trace.h" +#include "migration/misc.h" + +/* + * Dirtylimit stop working if dirty page rate error + * value less than DIRTYLIMIT_TOLERANCE_RANGE + */ +#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ +/* + * Plus or minus vcpu sleep time linearly if dirty + * page rate error value percentage over + * DIRTYLIMIT_LINEAR_ADJUSTMENT_PCT. + * Otherwise, plus or minus a fixed vcpu sleep time. + */ +#define DIRTYLIMIT_LINEAR_ADJUSTMENT_PCT 50 +/* + * Max vcpu sleep time percentage during a cycle + * composed of dirty ring full and sleep time. + */ +#define DIRTYLIMIT_THROTTLE_PCT_MAX 99 + +struct { + VcpuStat stat; + bool running; + QemuThread thread; +} *vcpu_dirty_rate_stat; + +typedef struct VcpuDirtyLimitState { + int cpu_index; + bool enabled; + /* + * Quota dirty page rate, unit is MB/s + * zero if not enabled. + */ + uint64_t quota; +} VcpuDirtyLimitState; + +struct { + VcpuDirtyLimitState *states; + /* Max cpus number configured by user */ + int max_cpus; + /* Number of vcpu under dirtylimit */ + int limited_nvcpu; +} *dirtylimit_state; + +/* protect dirtylimit_state */ +static QemuMutex dirtylimit_mutex; + +/* dirtylimit thread quit if dirtylimit_quit is true */ +static bool dirtylimit_quit; + +static void vcpu_dirty_rate_stat_collect(void) +{ + VcpuStat stat; + int i = 0; + int64_t period = DIRTYLIMIT_CALC_TIME_MS; + + if (migrate_dirty_limit() && + migration_is_active()) { + period = migrate_vcpu_dirty_limit_period(); + } + + /* calculate vcpu dirtyrate */ + vcpu_calculate_dirtyrate(period, + &stat, + GLOBAL_DIRTY_LIMIT, + false); + + for (i = 0; i < stat.nvcpu; i++) { + vcpu_dirty_rate_stat->stat.rates[i].id = i; + vcpu_dirty_rate_stat->stat.rates[i].dirty_rate = + stat.rates[i].dirty_rate; + } + + g_free(stat.rates); +} + +static void *vcpu_dirty_rate_stat_thread(void *opaque) +{ + rcu_register_thread(); + + /* start log sync */ + global_dirty_log_change(GLOBAL_DIRTY_LIMIT, true); + + while (qatomic_read(&vcpu_dirty_rate_stat->running)) { + vcpu_dirty_rate_stat_collect(); + if (dirtylimit_in_service()) { + dirtylimit_process(); + } + } + + /* stop log sync */ + global_dirty_log_change(GLOBAL_DIRTY_LIMIT, false); + + rcu_unregister_thread(); + return NULL; +} + +int64_t vcpu_dirty_rate_get(int cpu_index) +{ + DirtyRateVcpu *rates = vcpu_dirty_rate_stat->stat.rates; + return qatomic_read_i64(&rates[cpu_index].dirty_rate); +} + +void vcpu_dirty_rate_stat_start(void) +{ + if (qatomic_read(&vcpu_dirty_rate_stat->running)) { + return; + } + + qatomic_set(&vcpu_dirty_rate_stat->running, 1); + qemu_thread_create(&vcpu_dirty_rate_stat->thread, + "dirtyrate-stat", + vcpu_dirty_rate_stat_thread, + NULL, + QEMU_THREAD_JOINABLE); +} + +void vcpu_dirty_rate_stat_stop(void) +{ + qatomic_set(&vcpu_dirty_rate_stat->running, 0); + dirtylimit_state_unlock(); + bql_unlock(); + qemu_thread_join(&vcpu_dirty_rate_stat->thread); + bql_lock(); + dirtylimit_state_lock(); +} + +void vcpu_dirty_rate_stat_initialize(void) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + int max_cpus = ms->smp.max_cpus; + + vcpu_dirty_rate_stat = + g_malloc0(sizeof(*vcpu_dirty_rate_stat)); + + vcpu_dirty_rate_stat->stat.nvcpu = max_cpus; + vcpu_dirty_rate_stat->stat.rates = + g_new0(DirtyRateVcpu, max_cpus); + + vcpu_dirty_rate_stat->running = false; +} + +void vcpu_dirty_rate_stat_finalize(void) +{ + g_free(vcpu_dirty_rate_stat->stat.rates); + vcpu_dirty_rate_stat->stat.rates = NULL; + + g_free(vcpu_dirty_rate_stat); + vcpu_dirty_rate_stat = NULL; +} + +void dirtylimit_state_lock(void) +{ + qemu_mutex_lock(&dirtylimit_mutex); +} + +void dirtylimit_state_unlock(void) +{ + qemu_mutex_unlock(&dirtylimit_mutex); +} + +static void +__attribute__((__constructor__)) dirtylimit_mutex_init(void) +{ + qemu_mutex_init(&dirtylimit_mutex); +} + +static inline VcpuDirtyLimitState *dirtylimit_vcpu_get_state(int cpu_index) +{ + return &dirtylimit_state->states[cpu_index]; +} + +void dirtylimit_state_initialize(void) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + int max_cpus = ms->smp.max_cpus; + int i; + + dirtylimit_state = g_malloc0(sizeof(*dirtylimit_state)); + + dirtylimit_state->states = + g_new0(VcpuDirtyLimitState, max_cpus); + + for (i = 0; i < max_cpus; i++) { + dirtylimit_state->states[i].cpu_index = i; + } + + dirtylimit_state->max_cpus = max_cpus; + trace_dirtylimit_state_initialize(max_cpus); +} + +void dirtylimit_state_finalize(void) +{ + g_free(dirtylimit_state->states); + dirtylimit_state->states = NULL; + + g_free(dirtylimit_state); + dirtylimit_state = NULL; + + trace_dirtylimit_state_finalize(); +} + +bool dirtylimit_in_service(void) +{ + return !!dirtylimit_state; +} + +bool dirtylimit_vcpu_index_valid(int cpu_index) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + + return !(cpu_index < 0 || + cpu_index >= ms->smp.max_cpus); +} + +static uint64_t dirtylimit_dirty_ring_full_time(uint64_t dirtyrate) +{ + static uint64_t max_dirtyrate; + uint64_t dirty_ring_size_MiB; + + dirty_ring_size_MiB = qemu_target_pages_to_MiB(kvm_dirty_ring_size()); + + if (max_dirtyrate < dirtyrate) { + max_dirtyrate = dirtyrate; + } + + return dirty_ring_size_MiB * 1000000 / max_dirtyrate; +} + +static inline bool dirtylimit_done(uint64_t quota, + uint64_t current) +{ + uint64_t min, max; + + min = MIN(quota, current); + max = MAX(quota, current); + + return ((max - min) <= DIRTYLIMIT_TOLERANCE_RANGE) ? true : false; +} + +static inline bool +dirtylimit_need_linear_adjustment(uint64_t quota, + uint64_t current) +{ + uint64_t min, max; + + min = MIN(quota, current); + max = MAX(quota, current); + + return ((max - min) * 100 / max) > DIRTYLIMIT_LINEAR_ADJUSTMENT_PCT; +} + +static void dirtylimit_set_throttle(CPUState *cpu, + uint64_t quota, + uint64_t current) +{ + int64_t ring_full_time_us = 0; + uint64_t sleep_pct = 0; + uint64_t throttle_us = 0; + + if (current == 0) { + cpu->throttle_us_per_full = 0; + return; + } + + ring_full_time_us = dirtylimit_dirty_ring_full_time(current); + + if (dirtylimit_need_linear_adjustment(quota, current)) { + if (quota < current) { + sleep_pct = (current - quota) * 100 / current; + throttle_us = + ring_full_time_us * sleep_pct / (double)(100 - sleep_pct); + cpu->throttle_us_per_full += throttle_us; + } else { + sleep_pct = (quota - current) * 100 / quota; + throttle_us = + ring_full_time_us * sleep_pct / (double)(100 - sleep_pct); + cpu->throttle_us_per_full -= throttle_us; + } + + trace_dirtylimit_throttle_pct(cpu->cpu_index, + sleep_pct, + throttle_us); + } else { + if (quota < current) { + cpu->throttle_us_per_full += ring_full_time_us / 10; + } else { + cpu->throttle_us_per_full -= ring_full_time_us / 10; + } + } + + /* + * TODO: in the big kvm_dirty_ring_size case (eg: 65536, or other scenario), + * current dirty page rate may never reach the quota, we should stop + * increasing sleep time? + */ + cpu->throttle_us_per_full = MIN(cpu->throttle_us_per_full, + ring_full_time_us * DIRTYLIMIT_THROTTLE_PCT_MAX); + + cpu->throttle_us_per_full = MAX(cpu->throttle_us_per_full, 0); +} + +static void dirtylimit_adjust_throttle(CPUState *cpu) +{ + uint64_t quota = 0; + uint64_t current = 0; + int cpu_index = cpu->cpu_index; + + quota = dirtylimit_vcpu_get_state(cpu_index)->quota; + current = vcpu_dirty_rate_get(cpu_index); + + if (!dirtylimit_done(quota, current)) { + dirtylimit_set_throttle(cpu, quota, current); + } + + return; +} + +void dirtylimit_process(void) +{ + CPUState *cpu; + + if (!qatomic_read(&dirtylimit_quit)) { + dirtylimit_state_lock(); + + if (!dirtylimit_in_service()) { + dirtylimit_state_unlock(); + return; + } + + CPU_FOREACH(cpu) { + if (!dirtylimit_vcpu_get_state(cpu->cpu_index)->enabled) { + continue; + } + dirtylimit_adjust_throttle(cpu); + } + dirtylimit_state_unlock(); + } +} + +void dirtylimit_change(bool start) +{ + if (start) { + qatomic_set(&dirtylimit_quit, 0); + } else { + qatomic_set(&dirtylimit_quit, 1); + } +} + +void dirtylimit_set_vcpu(int cpu_index, + uint64_t quota, + bool enable) +{ + trace_dirtylimit_set_vcpu(cpu_index, quota); + + if (enable) { + dirtylimit_state->states[cpu_index].quota = quota; + if (!dirtylimit_vcpu_get_state(cpu_index)->enabled) { + dirtylimit_state->limited_nvcpu++; + } + } else { + dirtylimit_state->states[cpu_index].quota = 0; + if (dirtylimit_state->states[cpu_index].enabled) { + dirtylimit_state->limited_nvcpu--; + } + } + + dirtylimit_state->states[cpu_index].enabled = enable; +} + +void dirtylimit_set_all(uint64_t quota, + bool enable) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + int max_cpus = ms->smp.max_cpus; + int i; + + for (i = 0; i < max_cpus; i++) { + dirtylimit_set_vcpu(i, quota, enable); + } +} + +void dirtylimit_vcpu_execute(CPUState *cpu) +{ + if (cpu->throttle_us_per_full) { + dirtylimit_state_lock(); + + if (dirtylimit_in_service() && + dirtylimit_vcpu_get_state(cpu->cpu_index)->enabled) { + dirtylimit_state_unlock(); + trace_dirtylimit_vcpu_execute(cpu->cpu_index, + cpu->throttle_us_per_full); + + g_usleep(cpu->throttle_us_per_full); + return; + } + + dirtylimit_state_unlock(); + } +} + +static void dirtylimit_init(void) +{ + dirtylimit_state_initialize(); + dirtylimit_change(true); + vcpu_dirty_rate_stat_initialize(); + vcpu_dirty_rate_stat_start(); +} + +static void dirtylimit_cleanup(void) +{ + vcpu_dirty_rate_stat_stop(); + vcpu_dirty_rate_stat_finalize(); + dirtylimit_change(false); + dirtylimit_state_finalize(); +} + +/* + * dirty page rate limit is not allowed to set if migration + * is running with dirty-limit capability enabled. + */ +static bool dirtylimit_is_allowed(void) +{ + if (migration_is_running() && + !migration_thread_is_self() && + migrate_dirty_limit() && + dirtylimit_in_service()) { + return false; + } + return true; +} + +void qmp_cancel_vcpu_dirty_limit(bool has_cpu_index, + int64_t cpu_index, + Error **errp) +{ + if (!kvm_enabled() || !kvm_dirty_ring_enabled()) { + return; + } + + if (has_cpu_index && !dirtylimit_vcpu_index_valid(cpu_index)) { + error_setg(errp, "incorrect cpu index specified"); + return; + } + + if (!dirtylimit_is_allowed()) { + error_setg(errp, "can't cancel dirty page rate limit while" + " migration is running"); + return; + } + + if (!dirtylimit_in_service()) { + return; + } + + dirtylimit_state_lock(); + + if (has_cpu_index) { + dirtylimit_set_vcpu(cpu_index, 0, false); + } else { + dirtylimit_set_all(0, false); + } + + if (!dirtylimit_state->limited_nvcpu) { + dirtylimit_cleanup(); + } + + dirtylimit_state_unlock(); +} + +void hmp_cancel_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1); + Error *err = NULL; + + qmp_cancel_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, &err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "[Please use 'info vcpu_dirty_limit' to query " + "dirty limit for virtual CPU]\n"); +} + +void qmp_set_vcpu_dirty_limit(bool has_cpu_index, + int64_t cpu_index, + uint64_t dirty_rate, + Error **errp) +{ + if (!kvm_enabled() || !kvm_dirty_ring_enabled()) { + error_setg(errp, "dirty page limit feature requires KVM with" + " accelerator property 'dirty-ring-size' set'"); + return; + } + + if (has_cpu_index && !dirtylimit_vcpu_index_valid(cpu_index)) { + error_setg(errp, "incorrect cpu index specified"); + return; + } + + if (!dirtylimit_is_allowed()) { + error_setg(errp, "can't set dirty page rate limit while" + " migration is running"); + return; + } + + if (!dirty_rate) { + qmp_cancel_vcpu_dirty_limit(has_cpu_index, cpu_index, errp); + return; + } + + dirtylimit_state_lock(); + + if (!dirtylimit_in_service()) { + dirtylimit_init(); + } + + if (has_cpu_index) { + dirtylimit_set_vcpu(cpu_index, dirty_rate, true); + } else { + dirtylimit_set_all(dirty_rate, true); + } + + dirtylimit_state_unlock(); +} + +void hmp_set_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + int64_t dirty_rate = qdict_get_int(qdict, "dirty_rate"); + int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1); + Error *err = NULL; + + if (dirty_rate < 0) { + error_setg(&err, "invalid dirty page limit %" PRId64, dirty_rate); + goto out; + } + + qmp_set_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, dirty_rate, &err); + +out: + hmp_handle_error(mon, err); +} + +/* Return the max throttle time of each virtual CPU */ +uint64_t dirtylimit_throttle_time_per_round(void) +{ + CPUState *cpu; + int64_t max = 0; + + CPU_FOREACH(cpu) { + if (cpu->throttle_us_per_full > max) { + max = cpu->throttle_us_per_full; + } + } + + return max; +} + +/* + * Estimate average dirty ring full time of each virtaul CPU. + * Return 0 if guest doesn't dirty memory. + */ +uint64_t dirtylimit_ring_full_time(void) +{ + CPUState *cpu; + uint64_t curr_rate = 0; + int nvcpus = 0; + + CPU_FOREACH(cpu) { + if (cpu->running) { + nvcpus++; + curr_rate += vcpu_dirty_rate_get(cpu->cpu_index); + } + } + + if (!curr_rate || !nvcpus) { + return 0; + } + + return dirtylimit_dirty_ring_full_time(curr_rate / nvcpus); +} + +static struct DirtyLimitInfo *dirtylimit_query_vcpu(int cpu_index) +{ + DirtyLimitInfo *info = NULL; + + info = g_malloc0(sizeof(*info)); + info->cpu_index = cpu_index; + info->limit_rate = dirtylimit_vcpu_get_state(cpu_index)->quota; + info->current_rate = vcpu_dirty_rate_get(cpu_index); + + return info; +} + +static struct DirtyLimitInfoList *dirtylimit_query_all(void) +{ + int i, index; + DirtyLimitInfo *info = NULL; + DirtyLimitInfoList *head = NULL, **tail = &head; + + dirtylimit_state_lock(); + + if (!dirtylimit_in_service()) { + dirtylimit_state_unlock(); + return NULL; + } + + for (i = 0; i < dirtylimit_state->max_cpus; i++) { + index = dirtylimit_state->states[i].cpu_index; + if (dirtylimit_vcpu_get_state(index)->enabled) { + info = dirtylimit_query_vcpu(index); + QAPI_LIST_APPEND(tail, info); + } + } + + dirtylimit_state_unlock(); + + return head; +} + +struct DirtyLimitInfoList *qmp_query_vcpu_dirty_limit(Error **errp) +{ + return dirtylimit_query_all(); +} + +void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + DirtyLimitInfoList *info; + g_autoptr(DirtyLimitInfoList) head = NULL; + Error *err = NULL; + + if (!dirtylimit_in_service()) { + monitor_printf(mon, "Dirty page limit not enabled!\n"); + return; + } + + head = qmp_query_vcpu_dirty_limit(&err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + for (info = head; info != NULL; info = info->next) { + monitor_printf(mon, "vcpu[%"PRIi64"], limit rate %"PRIi64 " (MB/s)," + " current rate %"PRIi64 " (MB/s)\n", + info->value->cpu_index, + info->value->limit_rate, + info->value->current_rate); + } +} diff --git a/softmmu/dma-helpers.c b/system/dma-helpers.c similarity index 96% rename from softmmu/dma-helpers.c rename to system/dma-helpers.c index 7820fec54c..9b221cf94e 100644 --- a/softmmu/dma-helpers.c +++ b/system/dma-helpers.c @@ -113,11 +113,15 @@ static void dma_complete(DMAAIOCB *dbs, int ret) static void dma_blk_cb(void *opaque, int ret) { DMAAIOCB *dbs = (DMAAIOCB *)opaque; + AioContext *ctx = dbs->ctx; dma_addr_t cur_addr, cur_len; void *mem; trace_dma_blk_cb(dbs, ret); + /* DMAAIOCB is not thread-safe and must be accessed only from dbs->ctx */ + assert(ctx == qemu_get_current_aio_context()); + dbs->acb = NULL; dbs->offset += dbs->iov.size; @@ -164,7 +168,7 @@ static void dma_blk_cb(void *opaque, int ret) if (dbs->iov.size == 0) { trace_dma_map_wait(dbs); - dbs->bh = aio_bh_new(dbs->ctx, reschedule_dma, dbs); + dbs->bh = aio_bh_new(ctx, reschedule_dma, dbs); cpu_register_map_client(dbs->bh); return; } @@ -174,10 +178,8 @@ static void dma_blk_cb(void *opaque, int ret) QEMU_ALIGN_DOWN(dbs->iov.size, dbs->align)); } - aio_context_acquire(dbs->ctx); dbs->acb = dbs->io_func(dbs->offset, &dbs->iov, dma_blk_cb, dbs, dbs->io_func_opaque); - aio_context_release(dbs->ctx); assert(dbs->acb); } @@ -204,17 +206,9 @@ static void dma_aio_cancel(BlockAIOCB *acb) } } -static AioContext *dma_get_aio_context(BlockAIOCB *acb) -{ - DMAAIOCB *dbs = container_of(acb, DMAAIOCB, common); - - return dbs->ctx; -} - static const AIOCBInfo dma_aiocb_info = { .aiocb_size = sizeof(DMAAIOCB), .cancel_async = dma_aio_cancel, - .get_aio_context = dma_get_aio_context, }; BlockAIOCB *dma_blk_io(AioContext *ctx, diff --git a/softmmu/globals.c b/system/globals.c similarity index 90% rename from softmmu/globals.c rename to system/globals.c index 527edbefdd..e353584201 100644 --- a/softmmu/globals.c +++ b/system/globals.c @@ -36,15 +36,10 @@ int display_opengl; const char* keyboard_layout; bool enable_mlock; bool enable_cpu_pm; -int nb_nics; -NICInfo nd_table[MAX_NICS]; int autostart = 1; int vga_interface_type = VGA_NONE; bool vga_interface_created; Chardev *parallel_hds[MAX_PARALLEL_PORTS]; -int win2k_install_hack; -int singlestep; -int fd_bootchk = 1; int graphic_rotate; QEMUOptionRom option_rom[MAX_OPTION_ROMS]; int nb_option_roms; @@ -63,5 +58,9 @@ QemuUUID qemu_uuid; bool qemu_uuid_set; uint32_t xen_domid; -enum xen_mode xen_mode = XEN_EMULATE; +enum xen_mode xen_mode = XEN_DISABLED; bool xen_domid_restrict; +struct evtchn_backend_ops *xen_evtchn_ops; +struct gnttab_backend_ops *xen_gnttab_ops; +struct foreignmem_backend_ops *xen_foreignmem_ops; +struct xenstore_backend_ops *xen_xenstore_ops; diff --git a/softmmu/ioport.c b/system/ioport.c similarity index 73% rename from softmmu/ioport.c rename to system/ioport.c index cb8adb0b93..fd551d0375 100644 --- a/softmmu/ioport.c +++ b/system/ioport.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ /* - * splitted out ioport related stuffs from vl.c. + * split out ioport related stuffs from vl.c. */ #include "qemu/osdep.h" @@ -32,11 +32,16 @@ #include "exec/address-spaces.h" #include "trace.h" -typedef struct MemoryRegionPortioList { +struct MemoryRegionPortioList { + Object obj; + MemoryRegion mr; void *portio_opaque; - MemoryRegionPortio ports[]; -} MemoryRegionPortioList; + MemoryRegionPortio *ports; +}; + +#define TYPE_MEMORY_REGION_PORTIO_LIST "memory-region-portio-list" +OBJECT_DECLARE_SIMPLE_TYPE(MemoryRegionPortioList, MEMORY_REGION_PORTIO_LIST) static uint64_t unassigned_io_read(void *opaque, hwaddr addr, unsigned size) { @@ -128,6 +133,7 @@ void portio_list_init(PortioList *piolist, piolist->nr = 0; piolist->regions = g_new0(MemoryRegion *, n); piolist->address_space = NULL; + piolist->addr = 0; piolist->opaque = opaque; piolist->owner = owner; piolist->name = name; @@ -147,7 +153,7 @@ void portio_list_destroy(PortioList *piolist) for (i = 0; i < piolist->nr; ++i) { mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr); object_unparent(OBJECT(&mrpio->mr)); - g_free(mrpio); + object_unref(mrpio); } g_free(piolist->regions); } @@ -176,13 +182,13 @@ static uint64_t portio_read(void *opaque, hwaddr addr, unsigned size) data = ((uint64_t)1 << (size * 8)) - 1; if (mrp) { - data = mrp->read(mrpio->portio_opaque, mrp->base + addr); + data = mrp->read(mrpio->portio_opaque, mrpio->mr.addr + addr); } else if (size == 2) { mrp = find_portio(mrpio, addr, 1, false); if (mrp) { - data = mrp->read(mrpio->portio_opaque, mrp->base + addr); + data = mrp->read(mrpio->portio_opaque, mrpio->mr.addr + addr); if (addr + 1 < mrp->offset + mrp->len) { - data |= mrp->read(mrpio->portio_opaque, mrp->base + addr + 1) << 8; + data |= mrp->read(mrpio->portio_opaque, mrpio->mr.addr + addr + 1) << 8; } else { data |= 0xff00; } @@ -198,13 +204,13 @@ static void portio_write(void *opaque, hwaddr addr, uint64_t data, const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, true); if (mrp) { - mrp->write(mrpio->portio_opaque, mrp->base + addr, data); + mrp->write(mrpio->portio_opaque, mrpio->mr.addr + addr, data); } else if (size == 2) { mrp = find_portio(mrpio, addr, 1, true); if (mrp) { - mrp->write(mrpio->portio_opaque, mrp->base + addr, data & 0xff); + mrp->write(mrpio->portio_opaque, mrpio->mr.addr + addr, data & 0xff); if (addr + 1 < mrp->offset + mrp->len) { - mrp->write(mrpio->portio_opaque, mrp->base + addr + 1, data >> 8); + mrp->write(mrpio->portio_opaque, mrpio->mr.addr + addr + 1, data >> 8); } } } @@ -224,23 +230,42 @@ static void portio_list_add_1(PortioList *piolist, unsigned off_low, unsigned off_high) { MemoryRegionPortioList *mrpio; + Object *owner; + char *name; unsigned i; /* Copy the sub-list and null-terminate it. */ - mrpio = g_malloc0(sizeof(MemoryRegionPortioList) + - sizeof(MemoryRegionPortio) * (count + 1)); + mrpio = MEMORY_REGION_PORTIO_LIST( + object_new(TYPE_MEMORY_REGION_PORTIO_LIST)); mrpio->portio_opaque = piolist->opaque; + mrpio->ports = g_malloc0(sizeof(MemoryRegionPortio) * (count + 1)); memcpy(mrpio->ports, pio_init, sizeof(MemoryRegionPortio) * count); memset(mrpio->ports + count, 0, sizeof(MemoryRegionPortio)); /* Adjust the offsets to all be zero-based for the region. */ for (i = 0; i < count; ++i) { mrpio->ports[i].offset -= off_low; - mrpio->ports[i].base = start + off_low; } - memory_region_init_io(&mrpio->mr, piolist->owner, &portio_ops, mrpio, + /* + * The MemoryRegion owner is the MemoryRegionPortioList since that manages + * the lifecycle via the refcount + */ + memory_region_init_io(&mrpio->mr, OBJECT(mrpio), &portio_ops, mrpio, piolist->name, off_high - off_low); + + /* Reparent the MemoryRegion to the piolist owner */ + object_ref(&mrpio->mr); + object_unparent(OBJECT(&mrpio->mr)); + if (!piolist->owner) { + owner = container_get(qdev_get_machine(), "/unattached"); + } else { + owner = piolist->owner; + } + name = g_strdup_printf("%s[*]", piolist->name); + object_property_add_child(owner, name, OBJECT(&mrpio->mr)); + g_free(name); + if (piolist->flush_coalesced_mmio) { memory_region_set_flush_coalesced(&mrpio->mr); } @@ -258,6 +283,7 @@ void portio_list_add(PortioList *piolist, unsigned int off_low, off_high, off_last, count; piolist->address_space = address_space; + piolist->addr = start; /* Handle the first entry specially. */ off_last = off_low = pio_start->offset; @@ -297,3 +323,51 @@ void portio_list_del(PortioList *piolist) memory_region_del_subregion(piolist->address_space, &mrpio->mr); } } + +void portio_list_set_enabled(PortioList *piolist, bool enabled) +{ + unsigned i; + + for (i = 0; i < piolist->nr; ++i) { + memory_region_set_enabled(piolist->regions[i], enabled); + } +} + +void portio_list_set_address(PortioList *piolist, uint32_t addr) +{ + MemoryRegionPortioList *mrpio; + unsigned i, j; + + for (i = 0; i < piolist->nr; ++i) { + mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr); + memory_region_set_address(&mrpio->mr, + mrpio->mr.addr - piolist->addr + addr); + for (j = 0; mrpio->ports[j].size; ++j) { + mrpio->ports[j].offset += addr - piolist->addr; + } + } + + piolist->addr = addr; +} + +static void memory_region_portio_list_finalize(Object *obj) +{ + MemoryRegionPortioList *mrpio = MEMORY_REGION_PORTIO_LIST(obj); + + object_unref(&mrpio->mr); + g_free(mrpio->ports); +} + +static const TypeInfo memory_region_portio_list_info = { + .parent = TYPE_OBJECT, + .name = TYPE_MEMORY_REGION_PORTIO_LIST, + .instance_size = sizeof(MemoryRegionPortioList), + .instance_finalize = memory_region_portio_list_finalize, +}; + +static void ioport_register_types(void) +{ + type_register_static(&memory_region_portio_list_info); +} + +type_init(ioport_register_types) diff --git a/softmmu/main.c b/system/main.c similarity index 85% rename from softmmu/main.c rename to system/main.c index c00432ff09..9b91d21ea8 100644 --- a/softmmu/main.c +++ b/system/main.c @@ -30,18 +30,20 @@ #include #endif -int qemu_main(int argc, char **argv, char **envp) +int qemu_default_main(void) { - qemu_init(argc, argv, envp); - qemu_main_loop(); - qemu_cleanup(); + int status; - return 0; + status = qemu_main_loop(); + qemu_cleanup(status); + + return status; } -#ifndef CONFIG_COCOA +int (*qemu_main)(void) = qemu_default_main; + int main(int argc, char **argv) { - return qemu_main(argc, argv, NULL); + qemu_init(argc, argv); + return qemu_main(); } -#endif diff --git a/softmmu/memory.c b/system/memory.c similarity index 92% rename from softmmu/memory.c rename to system/memory.c index 7ba2048836..a229a79988 100644 --- a/softmmu/memory.c +++ b/system/memory.c @@ -33,6 +33,7 @@ #include "qemu/accel.h" #include "hw/boards.h" #include "migration/vmstate.h" +#include "exec/address-spaces.h" //#define DEBUG_UNASSIGNED @@ -223,6 +224,7 @@ struct FlatRange { bool romd_mode; bool readonly; bool nonvolatile; + bool unmergeable; }; #define FOR_EACH_FLAT_RANGE(var, view) \ @@ -239,6 +241,7 @@ section_from_flat_range(FlatRange *fr, FlatView *fv) .offset_within_address_space = int128_get64(fr->addr.start), .readonly = fr->readonly, .nonvolatile = fr->nonvolatile, + .unmergeable = fr->unmergeable, }; } @@ -249,7 +252,8 @@ static bool flatrange_equal(FlatRange *a, FlatRange *b) && a->offset_in_region == b->offset_in_region && a->romd_mode == b->romd_mode && a->readonly == b->readonly - && a->nonvolatile == b->nonvolatile; + && a->nonvolatile == b->nonvolatile + && a->unmergeable == b->unmergeable; } static FlatView *flatview_new(MemoryRegion *mr_root) @@ -322,7 +326,8 @@ static bool can_merge(FlatRange *r1, FlatRange *r2) && r1->dirty_log_mask == r2->dirty_log_mask && r1->romd_mode == r2->romd_mode && r1->readonly == r2->readonly - && r1->nonvolatile == r2->nonvolatile; + && r1->nonvolatile == r2->nonvolatile + && !r1->unmergeable && !r2->unmergeable; } /* Attempt to simplify a view by merging adjacent ranges */ @@ -533,6 +538,7 @@ static MemTxResult access_with_adjusted_size(hwaddr addr, unsigned access_size; unsigned i; MemTxResult r = MEMTX_OK; + bool reentrancy_guard_applied = false; if (!access_size_min) { access_size_min = 1; @@ -541,6 +547,19 @@ static MemTxResult access_with_adjusted_size(hwaddr addr, access_size_max = 4; } + /* Do not allow more than one simultaneous access to a device's IO Regions */ + if (mr->dev && !mr->disable_reentrancy_guard && + !mr->ram_device && !mr->ram && !mr->rom_device && !mr->readonly) { + if (mr->dev->mem_reentrancy_guard.engaged_in_io) { + warn_report_once("Blocked re-entrant IO on MemoryRegion: " + "%s at addr: 0x%" HWADDR_PRIX, + memory_region_name(mr), addr); + return MEMTX_ACCESS_ERROR; + } + mr->dev->mem_reentrancy_guard.engaged_in_io = true; + reentrancy_guard_applied = true; + } + /* FIXME: support unaligned access? */ access_size = MAX(MIN(size, access_size_max), access_size_min); access_mask = MAKE_64BIT_MASK(0, access_size * 8); @@ -555,6 +574,9 @@ static MemTxResult access_with_adjusted_size(hwaddr addr, access_mask, attrs); } } + if (mr->dev && reentrancy_guard_applied) { + mr->dev->mem_reentrancy_guard.engaged_in_io = false; + } return r; } @@ -581,7 +603,8 @@ static void render_memory_region(FlatView *view, Int128 base, AddrRange clip, bool readonly, - bool nonvolatile) + bool nonvolatile, + bool unmergeable) { MemoryRegion *subregion; unsigned i; @@ -598,6 +621,7 @@ static void render_memory_region(FlatView *view, int128_addto(&base, int128_make64(mr->addr)); readonly |= mr->readonly; nonvolatile |= mr->nonvolatile; + unmergeable |= mr->unmergeable; tmp = addrrange_make(base, mr->size); @@ -611,14 +635,14 @@ static void render_memory_region(FlatView *view, int128_subfrom(&base, int128_make64(mr->alias->addr)); int128_subfrom(&base, int128_make64(mr->alias_offset)); render_memory_region(view, mr->alias, base, clip, - readonly, nonvolatile); + readonly, nonvolatile, unmergeable); return; } /* Render subregions in priority order. */ QTAILQ_FOREACH(subregion, &mr->subregions, subregions_link) { render_memory_region(view, subregion, base, clip, - readonly, nonvolatile); + readonly, nonvolatile, unmergeable); } if (!mr->terminates) { @@ -634,6 +658,7 @@ static void render_memory_region(FlatView *view, fr.romd_mode = mr->romd_mode; fr.readonly = readonly; fr.nonvolatile = nonvolatile; + fr.unmergeable = unmergeable; /* Render the region itself into any gaps left by the current view. */ for (i = 0; i < view->nr && int128_nz(remain); ++i) { @@ -735,7 +760,7 @@ static FlatView *generate_memory_topology(MemoryRegion *mr) if (mr) { render_memory_region(view, mr, int128_zero(), addrrange_make(int128_zero(), int128_2_64()), - false, false); + false, false, false); } flatview_simplify(view); @@ -824,6 +849,10 @@ static void address_space_update_ioeventfds(AddressSpace *as) AddrRange tmp; unsigned i; + if (!as->ioeventfd_notifiers) { + return; + } + /* * 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 @@ -1090,7 +1119,7 @@ void memory_region_transaction_commit(void) AddressSpace *as; assert(memory_region_transaction_depth); - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); --memory_region_transaction_depth; if (!memory_region_transaction_depth) { @@ -1169,6 +1198,7 @@ static void memory_region_do_init(MemoryRegion *mr, } mr->name = g_strdup(name); mr->owner = owner; + mr->dev = (DeviceState *) object_dynamic_cast(mr->owner, TYPE_DEVICE); mr->ram_block = NULL; if (name) { @@ -1280,7 +1310,7 @@ static uint64_t unassigned_mem_read(void *opaque, hwaddr addr, unsigned size) { #ifdef DEBUG_UNASSIGNED - printf("Unassigned mem read " TARGET_FMT_plx "\n", addr); + printf("Unassigned mem read " HWADDR_FMT_plx "\n", addr); #endif return 0; } @@ -1289,7 +1319,7 @@ static void unassigned_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { #ifdef DEBUG_UNASSIGNED - printf("Unassigned mem write " TARGET_FMT_plx " = 0x%"PRIx64"\n", addr, val); + printf("Unassigned mem write " HWADDR_FMT_plx " = 0x%"PRIx64"\n", addr, val); #endif } @@ -1309,22 +1339,7 @@ static uint64_t memory_region_ram_device_read(void *opaque, hwaddr addr, unsigned size) { MemoryRegion *mr = opaque; - uint64_t data = (uint64_t)~0; - - switch (size) { - case 1: - data = *(uint8_t *)(mr->ram_block->host + addr); - break; - case 2: - data = *(uint16_t *)(mr->ram_block->host + addr); - break; - case 4: - data = *(uint32_t *)(mr->ram_block->host + addr); - break; - case 8: - data = *(uint64_t *)(mr->ram_block->host + addr); - break; - } + uint64_t data = ldn_he_p(mr->ram_block->host + addr, size); trace_memory_region_ram_device_read(get_cpu_index(), mr, addr, data, size); @@ -1338,20 +1353,7 @@ static void memory_region_ram_device_write(void *opaque, hwaddr addr, trace_memory_region_ram_device_write(get_cpu_index(), mr, addr, data, size); - switch (size) { - case 1: - *(uint8_t *)(mr->ram_block->host + addr) = (uint8_t)data; - break; - case 2: - *(uint16_t *)(mr->ram_block->host + addr) = (uint16_t)data; - break; - case 4: - *(uint32_t *)(mr->ram_block->host + addr) = (uint32_t)data; - break; - case 8: - *(uint64_t *)(mr->ram_block->host + addr) = data; - break; - } + stn_he_p(mr->ram_block->host + addr, size, data); } static const MemoryRegionOps ram_device_mem_ops = { @@ -1505,7 +1507,12 @@ MemTxResult memory_region_dispatch_write(MemoryRegion *mr, adjust_endianness(mr, &data, op); - if ((!kvm_eventfds_enabled()) && + /* + * FIXME: it's not clear why under KVM the write would be processed + * directly, instead of going through eventfd. This probably should + * test "tcg_enabled() || qtest_enabled()", or should just go away. + */ + if (!kvm_enabled() && memory_region_dispatch_write_eventfds(mr, addr, data, size, attrs)) { return MEMTX_OK; } @@ -1539,16 +1546,17 @@ void memory_region_init_io(MemoryRegion *mr, mr->terminates = true; } -void memory_region_init_ram_nomigrate(MemoryRegion *mr, +bool memory_region_init_ram_nomigrate(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, Error **errp) { - memory_region_init_ram_flags_nomigrate(mr, owner, name, size, 0, errp); + return memory_region_init_ram_flags_nomigrate(mr, owner, name, + size, 0, errp); } -void memory_region_init_ram_flags_nomigrate(MemoryRegion *mr, +bool memory_region_init_ram_flags_nomigrate(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, @@ -1565,10 +1573,12 @@ void memory_region_init_ram_flags_nomigrate(MemoryRegion *mr, mr->size = int128_zero(); object_unparent(OBJECT(mr)); error_propagate(errp, err); + return false; } + return true; } -void memory_region_init_resizeable_ram(MemoryRegion *mr, +bool memory_region_init_resizeable_ram(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, @@ -1589,37 +1599,41 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, mr->size = int128_zero(); object_unparent(OBJECT(mr)); error_propagate(errp, err); + return false; } + return true; } #ifdef CONFIG_POSIX -void memory_region_init_ram_from_file(MemoryRegion *mr, +bool memory_region_init_ram_from_file(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, uint64_t align, uint32_t ram_flags, const char *path, - bool readonly, + ram_addr_t offset, Error **errp) { Error *err = NULL; memory_region_init(mr, owner, name, size); mr->ram = true; - mr->readonly = readonly; + mr->readonly = !!(ram_flags & RAM_READONLY); mr->terminates = true; mr->destructor = memory_region_destructor_ram; mr->align = align; mr->ram_block = qemu_ram_alloc_from_file(size, mr, ram_flags, path, - readonly, &err); + offset, &err); if (err) { mr->size = int128_zero(); object_unparent(OBJECT(mr)); error_propagate(errp, err); + return false; } + return true; } -void memory_region_init_ram_from_fd(MemoryRegion *mr, +bool memory_region_init_ram_from_fd(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, @@ -1631,15 +1645,18 @@ void memory_region_init_ram_from_fd(MemoryRegion *mr, Error *err = NULL; memory_region_init(mr, owner, name, size); mr->ram = true; + mr->readonly = !!(ram_flags & RAM_READONLY); mr->terminates = true; mr->destructor = memory_region_destructor_ram; mr->ram_block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, offset, - false, &err); + &err); if (err) { mr->size = int128_zero(); object_unparent(OBJECT(mr)); error_propagate(errp, err); + return false; } + return true; } #endif @@ -1656,7 +1673,7 @@ void memory_region_init_ram_ptr(MemoryRegion *mr, /* qemu_ram_alloc_from_ptr cannot fail with ptr != NULL. */ assert(ptr != NULL); - mr->ram_block = qemu_ram_alloc_from_ptr(size, ptr, mr, &error_fatal); + mr->ram_block = qemu_ram_alloc_from_ptr(size, ptr, mr, &error_abort); } void memory_region_init_ram_device_ptr(MemoryRegion *mr, @@ -1675,7 +1692,7 @@ void memory_region_init_ram_device_ptr(MemoryRegion *mr, /* qemu_ram_alloc_from_ptr cannot fail with ptr != NULL. */ assert(ptr != NULL); - mr->ram_block = qemu_ram_alloc_from_ptr(size, ptr, mr, &error_fatal); + mr->ram_block = qemu_ram_alloc_from_ptr(size, ptr, mr, &error_abort); } void memory_region_init_alias(MemoryRegion *mr, @@ -1690,17 +1707,22 @@ void memory_region_init_alias(MemoryRegion *mr, mr->alias_offset = offset; } -void memory_region_init_rom_nomigrate(MemoryRegion *mr, +bool memory_region_init_rom_nomigrate(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, Error **errp) { - memory_region_init_ram_flags_nomigrate(mr, owner, name, size, 0, errp); + if (!memory_region_init_ram_flags_nomigrate(mr, owner, name, + size, 0, errp)) { + return false; + } mr->readonly = true; + + return true; } -void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, +bool memory_region_init_rom_device_nomigrate(MemoryRegion *mr, Object *owner, const MemoryRegionOps *ops, void *opaque, @@ -1721,7 +1743,9 @@ void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, mr->size = int128_zero(); object_unparent(OBJECT(mr)); error_propagate(errp, err); + return false; } + return true; } void memory_region_init_iommu(void *_iommu_mr, @@ -1885,6 +1909,19 @@ int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, return ret; } +int memory_region_iommu_set_iova_ranges(IOMMUMemoryRegion *iommu_mr, + GList *iova_ranges, + Error **errp) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); + int ret = 0; + + if (imrc->iommu_set_iova_ranges) { + ret = imrc->iommu_set_iova_ranges(iommu_mr, iova_ranges, errp); + } + return ret; +} + int memory_region_register_iommu_notifier(MemoryRegion *mr, IOMMUNotifier *n, Error **errp) { @@ -1995,6 +2032,19 @@ void memory_region_notify_iommu_one(IOMMUNotifier *notifier, } } +void memory_region_unmap_iommu_notifier_range(IOMMUNotifier *notifier) +{ + IOMMUTLBEvent event; + + event.type = IOMMU_NOTIFIER_UNMAP; + event.entry.target_as = &address_space_memory; + event.entry.iova = notifier->start; + event.entry.perm = IOMMU_NONE; + event.entry.addr_mask = notifier->end - notifier->start; + + memory_region_notify_iommu_one(notifier, &event); +} + void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, int iommu_idx, IOMMUTLBEvent event) @@ -2048,7 +2098,7 @@ int memory_region_iommu_num_indexes(IOMMUMemoryRegion *iommu_mr) RamDiscardManager *memory_region_get_ram_discard_manager(MemoryRegion *mr) { - if (!memory_region_is_mapped(mr) || !memory_region_is_ram(mr)) { + if (!memory_region_is_ram(mr)) { return NULL; } return mr->rdm; @@ -2057,7 +2107,7 @@ RamDiscardManager *memory_region_get_ram_discard_manager(MemoryRegion *mr) void memory_region_set_ram_discard_manager(MemoryRegion *mr, RamDiscardManager *rdm) { - g_assert(memory_region_is_ram(mr) && !memory_region_is_mapped(mr)); + g_assert(memory_region_is_ram(mr)); g_assert(!rdm || !mr->rdm); mr->rdm = rdm; } @@ -2121,6 +2171,77 @@ void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, rdmc->unregister_listener(rdm, rdl); } +/* Called with rcu_read_lock held. */ +bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, + ram_addr_t *ram_addr, bool *read_only, + bool *mr_has_discard_manager) +{ + MemoryRegion *mr; + hwaddr xlat; + hwaddr len = iotlb->addr_mask + 1; + bool writable = iotlb->perm & IOMMU_WO; + + if (mr_has_discard_manager) { + *mr_has_discard_manager = false; + } + /* + * The IOMMU TLB entry we have just covers translation through + * this IOMMU to its immediate target. We need to translate + * it the rest of the way through to memory. + */ + mr = address_space_translate(&address_space_memory, iotlb->translated_addr, + &xlat, &len, writable, MEMTXATTRS_UNSPECIFIED); + if (!memory_region_is_ram(mr)) { + error_report("iommu map to non memory area %" HWADDR_PRIx "", xlat); + return false; + } else if (memory_region_has_ram_discard_manager(mr)) { + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(mr); + MemoryRegionSection tmp = { + .mr = mr, + .offset_within_region = xlat, + .size = int128_make64(len), + }; + if (mr_has_discard_manager) { + *mr_has_discard_manager = true; + } + /* + * Malicious VMs can map memory into the IOMMU, which is expected + * to remain discarded. vfio will pin all pages, populating memory. + * Disallow that. vmstate priorities make sure any RamDiscardManager + * were already restored before IOMMUs are restored. + */ + if (!ram_discard_manager_is_populated(rdm, &tmp)) { + error_report("iommu map to discarded memory (e.g., unplugged via" + " virtio-mem): %" HWADDR_PRIx "", + iotlb->translated_addr); + return false; + } + } + + /* + * Translation truncates length to the IOMMU page size, + * check that it did not truncate too much. + */ + if (len & iotlb->addr_mask) { + error_report("iommu has granularity incompatible with target AS"); + return false; + } + + if (vaddr) { + *vaddr = memory_region_get_ram_ptr(mr) + xlat; + } + + if (ram_addr) { + *ram_addr = memory_region_get_ram_addr(mr) + xlat; + } + + if (read_only) { + *read_only = !writable || mr->readonly; + } + + return true; +} + void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) { uint8_t mask = 1 << client; @@ -2152,7 +2273,7 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, * If memory region `mr' is NULL, do global sync. Otherwise, sync * dirty bitmap for the specified memory region. */ -static void memory_region_sync_dirty_bitmap(MemoryRegion *mr) +static void memory_region_sync_dirty_bitmap(MemoryRegion *mr, bool last_stage) { MemoryListener *listener; AddressSpace *as; @@ -2182,7 +2303,7 @@ static void memory_region_sync_dirty_bitmap(MemoryRegion *mr) * is to do a global sync, because we are not capable to * sync in a finer granularity. */ - listener->log_sync_global(listener); + listener->log_sync_global(listener, last_stage); trace_memory_region_sync_dirty(mr ? mr->name : "(all)", listener->name, 1); } } @@ -2246,7 +2367,7 @@ DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr, { DirtyBitmapSnapshot *snapshot; assert(mr->ram_block); - memory_region_sync_dirty_bitmap(mr); + memory_region_sync_dirty_bitmap(mr, false); snapshot = cpu_physical_memory_snapshot_and_clear_dirty(mr, addr, size, client); memory_global_after_dirty_log_sync(); return snapshot; @@ -2300,20 +2421,15 @@ void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr, int memory_region_get_fd(MemoryRegion *mr) { - int fd; - RCU_READ_LOCK_GUARD(); while (mr->alias) { mr = mr->alias; } - fd = mr->ram_block->fd; - - return fd; + return mr->ram_block->fd; } void *memory_region_get_ram_ptr(MemoryRegion *mr) { - void *ptr; uint64_t offset = 0; RCU_READ_LOCK_GUARD(); @@ -2322,9 +2438,7 @@ void *memory_region_get_ram_ptr(MemoryRegion *mr) mr = mr->alias; } assert(mr->ram_block); - ptr = qemu_map_ram_ptr(mr->ram_block, offset); - - return ptr; + return qemu_map_ram_ptr(mr->ram_block, offset); } MemoryRegion *memory_region_from_host(void *ptr, ram_addr_t *offset) @@ -2442,8 +2556,6 @@ void memory_region_clear_flush_coalesced(MemoryRegion *mr) } } -static bool userspace_eventfd_warning; - void memory_region_add_eventfd(MemoryRegion *mr, hwaddr addr, unsigned size, @@ -2460,13 +2572,6 @@ void memory_region_add_eventfd(MemoryRegion *mr, }; unsigned i; - if (kvm_enabled() && (!(kvm_eventfds_enabled() || - userspace_eventfd_warning))) { - userspace_eventfd_warning = true; - error_report("Using eventfd without MMIO binding in KVM. " - "Suboptimal performance expected"); - } - if (size) { adjust_endianness(mr, &mrfd.data, size_memop(size) | MO_TE); } @@ -2654,6 +2759,18 @@ void memory_region_set_alias_offset(MemoryRegion *mr, hwaddr offset) memory_region_transaction_commit(); } +void memory_region_set_unmergeable(MemoryRegion *mr, bool unmergeable) +{ + if (unmergeable == mr->unmergeable) { + return; + } + + memory_region_transaction_begin(); + mr->unmergeable = unmergeable; + memory_region_update_pending |= mr->enabled; + memory_region_transaction_commit(); +} + uint64_t memory_region_get_alignment(const MemoryRegion *mr) { return mr->align; @@ -2779,9 +2896,9 @@ bool memory_region_present(MemoryRegion *container, hwaddr addr) return mr && mr != container; } -void memory_global_dirty_log_sync(void) +void memory_global_dirty_log_sync(bool last_stage) { - memory_region_sync_dirty_bitmap(NULL); + memory_region_sync_dirty_bitmap(NULL, last_stage); } void memory_global_after_dirty_log_sync(void) @@ -2978,6 +3095,10 @@ void memory_listener_register(MemoryListener *listener, AddressSpace *as) } listener_add_address_space(listener, as); + + if (listener->eventfd_add || listener->eventfd_del) { + as->ioeventfd_notifiers++; + } } void memory_listener_unregister(MemoryListener *listener) @@ -2986,6 +3107,10 @@ void memory_listener_unregister(MemoryListener *listener) return; } + if (listener->eventfd_add || listener->eventfd_del) { + listener->address_space->ioeventfd_notifiers--; + } + listener_del_address_space(listener, listener->address_space); QTAILQ_REMOVE(&memory_listeners, listener, link); QTAILQ_REMOVE(&listener->address_space->listeners, listener, link_as); @@ -3136,7 +3261,6 @@ static void mtree_print_mr(const MemoryRegion *mr, unsigned int level, } if (mr->alias) { - MemoryRegionList *ml; bool found = false; /* check if the alias is already in the queue */ @@ -3155,9 +3279,9 @@ static void mtree_print_mr(const MemoryRegion *mr, unsigned int level, for (i = 0; i < level; i++) { qemu_printf(MTREE_INDENT); } - qemu_printf(TARGET_FMT_plx "-" TARGET_FMT_plx - " (prio %d, %s%s): alias %s @%s " TARGET_FMT_plx - "-" TARGET_FMT_plx "%s", + qemu_printf(HWADDR_FMT_plx "-" HWADDR_FMT_plx + " (prio %d, %s%s): alias %s @%s " HWADDR_FMT_plx + "-" HWADDR_FMT_plx "%s", cur_start, cur_end, mr->priority, mr->nonvolatile ? "nv-" : "", @@ -3177,7 +3301,7 @@ static void mtree_print_mr(const MemoryRegion *mr, unsigned int level, for (i = 0; i < level; i++) { qemu_printf(MTREE_INDENT); } - qemu_printf(TARGET_FMT_plx "-" TARGET_FMT_plx + qemu_printf(HWADDR_FMT_plx "-" HWADDR_FMT_plx " (prio %d, %s%s): %s%s", cur_start, cur_end, mr->priority, @@ -3264,8 +3388,8 @@ static void mtree_print_flatview(gpointer key, gpointer value, while (n--) { mr = range->mr; if (range->offset_in_region) { - qemu_printf(MTREE_INDENT TARGET_FMT_plx "-" TARGET_FMT_plx - " (prio %d, %s%s): %s @" TARGET_FMT_plx, + qemu_printf(MTREE_INDENT HWADDR_FMT_plx "-" HWADDR_FMT_plx + " (prio %d, %s%s): %s @" HWADDR_FMT_plx, int128_get64(range->addr.start), int128_get64(range->addr.start) + MR_SIZE(range->addr.size), @@ -3275,7 +3399,7 @@ static void mtree_print_flatview(gpointer key, gpointer value, memory_region_name(mr), range->offset_in_region); } else { - qemu_printf(MTREE_INDENT TARGET_FMT_plx "-" TARGET_FMT_plx + qemu_printf(MTREE_INDENT HWADDR_FMT_plx "-" HWADDR_FMT_plx " (prio %d, %s%s): %s", int128_get64(range->addr.start), int128_get64(range->addr.start) @@ -3454,19 +3578,16 @@ void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled) } } -void memory_region_init_ram(MemoryRegion *mr, +bool memory_region_init_ram(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, Error **errp) { DeviceState *owner_dev; - Error *err = NULL; - memory_region_init_ram_nomigrate(mr, owner, name, size, &err); - if (err) { - error_propagate(errp, err); - return; + if (!memory_region_init_ram_nomigrate(mr, owner, name, size, errp)) { + return false; } /* This will assert if owner is neither NULL nor a DeviceState. * We only want the owner here for the purposes of defining a @@ -3476,21 +3597,20 @@ void memory_region_init_ram(MemoryRegion *mr, */ owner_dev = DEVICE(owner); vmstate_register_ram(mr, owner_dev); + + return true; } -void memory_region_init_rom(MemoryRegion *mr, +bool memory_region_init_rom(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, Error **errp) { DeviceState *owner_dev; - Error *err = NULL; - memory_region_init_rom_nomigrate(mr, owner, name, size, &err); - if (err) { - error_propagate(errp, err); - return; + if (!memory_region_init_rom_nomigrate(mr, owner, name, size, errp)) { + return false; } /* This will assert if owner is neither NULL nor a DeviceState. * We only want the owner here for the purposes of defining a @@ -3500,9 +3620,11 @@ void memory_region_init_rom(MemoryRegion *mr, */ owner_dev = DEVICE(owner); vmstate_register_ram(mr, owner_dev); + + return true; } -void memory_region_init_rom_device(MemoryRegion *mr, +bool memory_region_init_rom_device(MemoryRegion *mr, Object *owner, const MemoryRegionOps *ops, void *opaque, @@ -3511,13 +3633,10 @@ void memory_region_init_rom_device(MemoryRegion *mr, Error **errp) { DeviceState *owner_dev; - Error *err = NULL; - memory_region_init_rom_device_nomigrate(mr, owner, ops, opaque, - name, size, &err); - if (err) { - error_propagate(errp, err); - return; + if (!memory_region_init_rom_device_nomigrate(mr, owner, ops, opaque, + name, size, errp)) { + return false; } /* This will assert if owner is neither NULL nor a DeviceState. * We only want the owner here for the purposes of defining a @@ -3527,10 +3646,12 @@ void memory_region_init_rom_device(MemoryRegion *mr, */ owner_dev = DEVICE(owner); vmstate_register_ram(mr, owner_dev); + + return true; } /* - * Support softmmu builds with CONFIG_FUZZ using a weak symbol and a stub for + * Support system builds with CONFIG_FUZZ using a weak symbol and a stub for * the fuzz_dma_read_cb callback */ #ifdef CONFIG_FUZZ diff --git a/memory_ldst.c.inc b/system/memory_ldst.c.inc similarity index 97% rename from memory_ldst.c.inc rename to system/memory_ldst.c.inc index 84b868f294..0e6f3940a9 100644 --- a/memory_ldst.c.inc +++ b/system/memory_ldst.c.inc @@ -61,7 +61,7 @@ static inline uint32_t glue(address_space_ldl_internal, SUFFIX)(ARG1_DECL, *result = r; } if (release_lock) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } RCU_READ_UNLOCK(); return val; @@ -130,7 +130,7 @@ static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, *result = r; } if (release_lock) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } RCU_READ_UNLOCK(); return val; @@ -186,7 +186,7 @@ uint8_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, *result = r; } if (release_lock) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } RCU_READ_UNLOCK(); return val; @@ -234,7 +234,7 @@ static inline uint16_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, *result = r; } if (release_lock) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } RCU_READ_UNLOCK(); return val; @@ -295,7 +295,7 @@ void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, *result = r; } if (release_lock) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } RCU_READ_UNLOCK(); } @@ -339,7 +339,7 @@ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, *result = r; } if (release_lock) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } RCU_READ_UNLOCK(); } @@ -391,7 +391,7 @@ void glue(address_space_stb, SUFFIX)(ARG1_DECL, *result = r; } if (release_lock) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } RCU_READ_UNLOCK(); } @@ -435,7 +435,7 @@ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, *result = r; } if (release_lock) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } RCU_READ_UNLOCK(); } @@ -499,7 +499,7 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, *result = r; } if (release_lock) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } RCU_READ_UNLOCK(); } diff --git a/softmmu/memory_mapping.c b/system/memory_mapping.c similarity index 95% rename from softmmu/memory_mapping.c rename to system/memory_mapping.c index f6f0a829fd..6f884c5b90 100644 --- a/softmmu/memory_mapping.c +++ b/system/memory_mapping.c @@ -241,8 +241,8 @@ static void guest_phys_block_add_section(GuestPhysListener *g, } #ifdef DEBUG_GUEST_PHYS_REGION_ADD - fprintf(stderr, "%s: target_start=" TARGET_FMT_plx " target_end=" - TARGET_FMT_plx ": %s (count: %u)\n", __func__, target_start, + fprintf(stderr, "%s: target_start=" HWADDR_FMT_plx " target_end=" + HWADDR_FMT_plx ": %s (count: %u)\n", __func__, target_start, target_end, predecessor ? "joined" : "added", g->list->num); #endif } @@ -291,7 +291,7 @@ void guest_phys_blocks_append(GuestPhysBlockList *list) memory_listener_unregister(&g.listener); } -static CPUState *find_paging_enabled_cpu(CPUState *start_cpu) +static CPUState *find_paging_enabled_cpu(void) { CPUState *cpu; @@ -304,26 +304,24 @@ static CPUState *find_paging_enabled_cpu(CPUState *start_cpu) return NULL; } -void qemu_get_guest_memory_mapping(MemoryMappingList *list, +bool qemu_get_guest_memory_mapping(MemoryMappingList *list, const GuestPhysBlockList *guest_phys_blocks, Error **errp) { + ERRP_GUARD(); CPUState *cpu, *first_paging_enabled_cpu; GuestPhysBlock *block; ram_addr_t offset, length; - first_paging_enabled_cpu = find_paging_enabled_cpu(first_cpu); + first_paging_enabled_cpu = find_paging_enabled_cpu(); if (first_paging_enabled_cpu) { for (cpu = first_paging_enabled_cpu; cpu != NULL; cpu = CPU_NEXT(cpu)) { - Error *err = NULL; - cpu_get_memory_mapping(cpu, list, &err); - if (err) { - error_propagate(errp, err); - return; + if (!cpu_get_memory_mapping(cpu, list, errp)) { + return false; } } - return; + return true; } /* @@ -335,6 +333,7 @@ void qemu_get_guest_memory_mapping(MemoryMappingList *list, length = block->target_end - block->target_start; create_new_memory_mapping(list, offset, offset, length); } + return true; } void qemu_get_guest_simple_memory_mapping(MemoryMappingList *list, diff --git a/system/meson.build b/system/meson.build new file mode 100644 index 0000000000..25e2117250 --- /dev/null +++ b/system/meson.build @@ -0,0 +1,38 @@ +specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: [files( + 'arch_init.c', + 'ioport.c', + 'memory.c', + 'physmem.c', + 'watchpoint.c', +)]) + +system_ss.add(files( + 'balloon.c', + 'bootdevice.c', + 'cpus.c', + 'cpu-throttle.c', + 'cpu-timers.c', + 'datadir.c', + 'dirtylimit.c', + 'dma-helpers.c', + 'globals.c', + 'memory_mapping.c', + 'qdev-monitor.c', + 'qtest.c', + 'rtc.c', + 'runstate-action.c', + 'runstate-hmp-cmds.c', + 'runstate.c', + 'tpm-hmp-cmds.c', + 'vl.c', +), sdl, libpmem, libdaxctl) + +if have_tpm + system_ss.add(files('tpm.c')) +endif + +system_ss.add(when: seccomp, if_true: files('qemu-seccomp.c')) +system_ss.add(when: fdt, if_true: files('device_tree.c')) +if host_os == 'linux' + system_ss.add(files('async-teardown.c')) +endif diff --git a/softmmu/physmem.c b/system/physmem.c similarity index 86% rename from softmmu/physmem.c rename to system/physmem.c index 657841eed0..a4fe3d2bf8 100644 --- a/softmmu/physmem.c +++ b/system/physmem.c @@ -23,6 +23,7 @@ #include "qemu/cutils.h" #include "qemu/cacheflush.h" +#include "qemu/hbitmap.h" #include "qemu/madvise.h" #ifdef CONFIG_TCG @@ -34,7 +35,7 @@ #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "hw/boards.h" -#include "hw/xen/xen.h" +#include "sysemu/xen.h" #include "sysemu/kvm.h" #include "sysemu/tcg.h" #include "sysemu/qtest.h" @@ -669,7 +670,7 @@ void tcg_iommu_init_notifier_list(CPUState *cpu) /* Called from RCU critical section */ MemoryRegionSection * -address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, +address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr orig_addr, hwaddr *xlat, hwaddr *plen, MemTxAttrs attrs, int *prot) { @@ -678,8 +679,8 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, IOMMUMemoryRegionClass *imrc; IOMMUTLBEntry iotlb; int iommu_idx; - AddressSpaceDispatch *d = - qatomic_rcu_read(&cpu->cpu_ases[asidx].memory_dispatch); + hwaddr addr = orig_addr; + AddressSpaceDispatch *d = cpu->cpu_ases[asidx].memory_dispatch; for (;;) { section = address_space_translate_internal(d, addr, &addr, plen, false); @@ -722,6 +723,16 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, return section; translate_fail: + /* + * We should be given a page-aligned address -- certainly + * tlb_set_page_with_attrs() does so. The page offset of xlat + * is used to index sections[], and PHYS_SECTION_UNASSIGNED = 0. + * The page portion of xlat will be logged by memory_region_access_valid() + * when this memory access is rejected, so use the original untranslated + * physical address. + */ + assert((orig_addr & ~TARGET_PAGE_MASK) == 0); + *xlat = orig_addr; return &d->map.sections[PHYS_SECTION_UNASSIGNED]; } @@ -769,197 +780,6 @@ AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx) return cpu->cpu_ases[asidx].as; } -/* Add a watchpoint. */ -int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, - int flags, CPUWatchpoint **watchpoint) -{ - CPUWatchpoint *wp; - vaddr in_page; - - /* forbid ranges which are empty or run off the end of the address space */ - if (len == 0 || (addr + len - 1) < addr) { - error_report("tried to set invalid watchpoint at %" - VADDR_PRIx ", len=%" VADDR_PRIu, addr, len); - return -EINVAL; - } - wp = g_malloc(sizeof(*wp)); - - wp->vaddr = addr; - wp->len = len; - wp->flags = flags; - - /* keep all GDB-injected watchpoints in front */ - if (flags & BP_GDB) { - QTAILQ_INSERT_HEAD(&cpu->watchpoints, wp, entry); - } else { - QTAILQ_INSERT_TAIL(&cpu->watchpoints, wp, entry); - } - - in_page = -(addr | TARGET_PAGE_MASK); - if (len <= in_page) { - tlb_flush_page(cpu, addr); - } else { - tlb_flush(cpu); - } - - if (watchpoint) - *watchpoint = wp; - return 0; -} - -/* Remove a specific watchpoint. */ -int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, vaddr len, - int flags) -{ - CPUWatchpoint *wp; - - QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { - if (addr == wp->vaddr && len == wp->len - && flags == (wp->flags & ~BP_WATCHPOINT_HIT)) { - cpu_watchpoint_remove_by_ref(cpu, wp); - return 0; - } - } - return -ENOENT; -} - -/* Remove a specific watchpoint by reference. */ -void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint) -{ - QTAILQ_REMOVE(&cpu->watchpoints, watchpoint, entry); - - tlb_flush_page(cpu, watchpoint->vaddr); - - g_free(watchpoint); -} - -/* Remove all matching watchpoints. */ -void cpu_watchpoint_remove_all(CPUState *cpu, int mask) -{ - CPUWatchpoint *wp, *next; - - QTAILQ_FOREACH_SAFE(wp, &cpu->watchpoints, entry, next) { - if (wp->flags & mask) { - cpu_watchpoint_remove_by_ref(cpu, wp); - } - } -} - -#ifdef CONFIG_TCG -/* Return true if this watchpoint address matches the specified - * access (ie the address range covered by the watchpoint overlaps - * partially or completely with the address range covered by the - * access). - */ -static inline bool watchpoint_address_matches(CPUWatchpoint *wp, - vaddr addr, vaddr len) -{ - /* We know the lengths are non-zero, but a little caution is - * required to avoid errors in the case where the range ends - * exactly at the top of the address space and so addr + len - * wraps round to zero. - */ - vaddr wpend = wp->vaddr + wp->len - 1; - vaddr addrend = addr + len - 1; - - return !(addr > wpend || wp->vaddr > addrend); -} - -/* Return flags for watchpoints that match addr + prot. */ -int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len) -{ - CPUWatchpoint *wp; - int ret = 0; - - QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { - if (watchpoint_address_matches(wp, addr, len)) { - ret |= wp->flags; - } - } - return ret; -} - -/* Generate a debug exception if a watchpoint has been hit. */ -void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, - MemTxAttrs attrs, int flags, uintptr_t ra) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - CPUWatchpoint *wp; - - assert(tcg_enabled()); - if (cpu->watchpoint_hit) { - /* - * We re-entered the check after replacing the TB. - * Now raise the debug interrupt so that it will - * trigger after the current instruction. - */ - qemu_mutex_lock_iothread(); - cpu_interrupt(cpu, CPU_INTERRUPT_DEBUG); - qemu_mutex_unlock_iothread(); - return; - } - - if (cc->tcg_ops->adjust_watchpoint_address) { - /* this is currently used only by ARM BE32 */ - addr = cc->tcg_ops->adjust_watchpoint_address(cpu, addr, len); - } - QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { - if (watchpoint_address_matches(wp, addr, len) - && (wp->flags & flags)) { - if (replay_running_debug()) { - /* - * replay_breakpoint reads icount. - * Force recompile to succeed, because icount may - * be read only at the end of the block. - */ - if (!cpu->can_do_io) { - /* Force execution of one insn next time. */ - cpu->cflags_next_tb = 1 | CF_LAST_IO | CF_NOIRQ | curr_cflags(cpu); - cpu_loop_exit_restore(cpu, ra); - } - /* - * Don't process the watchpoints when we are - * in a reverse debugging operation. - */ - replay_breakpoint(); - return; - } - if (flags == BP_MEM_READ) { - wp->flags |= BP_WATCHPOINT_HIT_READ; - } else { - wp->flags |= BP_WATCHPOINT_HIT_WRITE; - } - wp->hitaddr = MAX(addr, wp->vaddr); - wp->hitattrs = attrs; - - if (wp->flags & BP_CPU && cc->tcg_ops->debug_check_watchpoint && - !cc->tcg_ops->debug_check_watchpoint(cpu, wp)) { - wp->flags &= ~BP_WATCHPOINT_HIT; - continue; - } - cpu->watchpoint_hit = wp; - - mmap_lock(); - /* This call also restores vCPU state */ - tb_check_watchpoint(cpu, ra); - if (wp->flags & BP_STOP_BEFORE_ACCESS) { - cpu->exception_index = EXCP_DEBUG; - mmap_unlock(); - cpu_loop_exit(cpu); - } else { - /* Force execution of one insn next time. */ - cpu->cflags_next_tb = 1 | CF_LAST_IO | CF_NOIRQ | curr_cflags(cpu); - mmap_unlock(); - cpu_loop_exit_noexc(cpu); - } - } else { - wp->flags &= ~BP_WATCHPOINT_HIT; - } - } -} - -#endif /* CONFIG_TCG */ - /* Called from RCU critical section */ static RAMBlock *qemu_get_ram_block(ram_addr_t addr) { @@ -979,7 +799,7 @@ static RAMBlock *qemu_get_ram_block(ram_addr_t addr) abort(); found: - /* It is safe to write mru_block outside the iothread lock. This + /* It is safe to write mru_block outside the BQL. This * is what happens: * * mru_block = xxx @@ -999,7 +819,7 @@ found: return block; } -static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) +void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) { CPUState *cpu; ram_addr_t start1; @@ -1061,8 +881,8 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, memory_region_clear_dirty_bitmap(ramblock->mr, mr_offset, mr_size); } - if (dirty && tcg_enabled()) { - tlb_reset_dirty_range_all(start, length); + if (dirty) { + cpu_physical_memory_dirty_bits_cleared(start, length); } return dirty; @@ -1093,25 +913,23 @@ DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty while (page < end) { unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE; - unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE; + unsigned long ofs = page % DIRTY_MEMORY_BLOCK_SIZE; unsigned long num = MIN(end - page, - DIRTY_MEMORY_BLOCK_SIZE - offset); + DIRTY_MEMORY_BLOCK_SIZE - ofs); - assert(QEMU_IS_ALIGNED(offset, (1 << BITS_PER_LEVEL))); + assert(QEMU_IS_ALIGNED(ofs, (1 << BITS_PER_LEVEL))); assert(QEMU_IS_ALIGNED(num, (1 << BITS_PER_LEVEL))); - offset >>= BITS_PER_LEVEL; + ofs >>= BITS_PER_LEVEL; bitmap_copy_and_clear_atomic(snap->dirty + dest, - blocks->blocks[idx] + offset, + blocks->blocks[idx] + ofs, num); page += num; dest += num >> BITS_PER_LEVEL; } } - if (tcg_enabled()) { - tlb_reset_dirty_range_all(start, length); - } + cpu_physical_memory_dirty_bits_cleared(start, length); memory_region_clear_dirty_bitmap(mr, offset, length); @@ -1305,28 +1123,27 @@ GString *ram_block_format(void) GString *buf = g_string_new(""); RCU_READ_LOCK_GUARD(); - g_string_append_printf(buf, "%24s %8s %18s %18s %18s\n", - "Block Name", "PSize", "Offset", "Used", "Total"); + g_string_append_printf(buf, "%24s %8s %18s %18s %18s %18s %3s\n", + "Block Name", "PSize", "Offset", "Used", "Total", + "HVA", "RO"); + RAMBLOCK_FOREACH(block) { psize = size_to_str(block->page_size); g_string_append_printf(buf, "%24s %8s 0x%016" PRIx64 " 0x%016" PRIx64 - " 0x%016" PRIx64 "\n", block->idstr, psize, + " 0x%016" PRIx64 " 0x%016" PRIx64 " %3s\n", + block->idstr, psize, (uint64_t)block->offset, (uint64_t)block->used_length, - (uint64_t)block->max_length); + (uint64_t)block->max_length, + (uint64_t)(uintptr_t)block->host, + block->mr->readonly ? "ro" : "rw"); + g_free(psize); } return buf; } -#ifdef __linux__ -/* - * FIXME TOCTTOU: this iterates over memory backends' mem-path, which - * may or may not name the same files / on the same filesystem now as - * when we actually open and map them. Iterate over the file - * descriptors instead, and use qemu_fd_getpagesize(). - */ static int find_min_backend_pagesize(Object *obj, void *opaque) { long *hpsize_min = opaque; @@ -1380,16 +1197,6 @@ long qemu_maxrampagesize(void) object_child_foreach(memdev_root, find_max_backend_pagesize, &pagesize); return pagesize; } -#else -long qemu_minrampagesize(void) -{ - return qemu_real_host_page_size(); -} -long qemu_maxrampagesize(void) -{ - return qemu_real_host_page_size(); -} -#endif #ifdef CONFIG_POSIX static int64_t get_file_size(int fd) @@ -1479,8 +1286,7 @@ static int64_t get_file_align(int fd) static int file_ram_open(const char *path, const char *region_name, bool readonly, - bool *created, - Error **errp) + bool *created) { char *filename; char *sanitized_name; @@ -1491,10 +1297,33 @@ static int file_ram_open(const char *path, for (;;) { fd = open(path, readonly ? O_RDONLY : O_RDWR); if (fd >= 0) { + /* + * open(O_RDONLY) won't fail with EISDIR. Check manually if we + * opened a directory and fail similarly to how we fail ENOENT + * in readonly mode. Note that mkstemp() would imply O_RDWR. + */ + if (readonly) { + struct stat file_stat; + + if (fstat(fd, &file_stat)) { + close(fd); + if (errno == EINTR) { + continue; + } + return -errno; + } else if (S_ISDIR(file_stat.st_mode)) { + close(fd); + return -EISDIR; + } + } /* @path names an existing file, use it */ break; } if (errno == ENOENT) { + if (readonly) { + /* Refuse to create new, readonly files. */ + return -ENOENT; + } /* @path names a file that doesn't exist, create it */ fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0644); if (fd >= 0) { @@ -1524,10 +1353,7 @@ static int file_ram_open(const char *path, g_free(filename); } if (errno != EEXIST && errno != EINTR) { - error_setg_errno(errp, errno, - "can't open backing store %s for guest RAM", - path); - return -1; + return -errno; } /* * Try again on EINTR and EEXIST. The latter happens when @@ -1541,7 +1367,6 @@ static int file_ram_open(const char *path, static void *file_ram_alloc(RAMBlock *block, ram_addr_t memory, int fd, - bool readonly, bool truncate, off_t offset, Error **errp) @@ -1559,6 +1384,11 @@ static void *file_ram_alloc(RAMBlock *block, error_setg(errp, "alignment 0x%" PRIx64 " must be a power of two", block->mr->align); return NULL; + } else if (offset % block->page_size) { + error_setg(errp, "offset 0x%" PRIx64 + " must be multiples of page size 0x%zx", + offset, block->page_size); + return NULL; } block->mr->align = MAX(block->page_size, block->mr->align); #if defined(__s390x__) @@ -1590,11 +1420,11 @@ static void *file_ram_alloc(RAMBlock *block, * those labels. Therefore, extending the non-empty backend file * is disabled as well. */ - if (truncate && ftruncate(fd, memory)) { + if (truncate && ftruncate(fd, offset + memory)) { perror("ftruncate"); } - qemu_map_flags = readonly ? QEMU_MAP_READONLY : 0; + qemu_map_flags = (block->flags & RAM_READONLY) ? QEMU_MAP_READONLY : 0; qemu_map_flags |= (block->flags & RAM_SHARED) ? QEMU_MAP_SHARED : 0; qemu_map_flags |= (block->flags & RAM_PMEM) ? QEMU_MAP_SYNC : 0; qemu_map_flags |= (block->flags & RAM_NORESERVE) ? QEMU_MAP_NORESERVE : 0; @@ -1606,6 +1436,7 @@ static void *file_ram_alloc(RAMBlock *block, } block->fd = fd; + block->fd_offset = offset; return area; } #endif @@ -1754,7 +1585,17 @@ void qemu_ram_unset_migratable(RAMBlock *rb) rb->flags &= ~RAM_MIGRATABLE; } -/* Called with iothread lock held. */ +bool qemu_ram_is_named_file(RAMBlock *rb) +{ + return rb->flags & RAM_NAMED_FILE; +} + +int qemu_ram_get_fd(RAMBlock *rb) +{ + return rb->fd; +} + +/* Called with the BQL held. */ void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev) { RAMBlock *block; @@ -1782,7 +1623,7 @@ void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev) } } -/* Called with iothread lock held. */ +/* Called with the BQL held. */ void qemu_ram_unset_idstr(RAMBlock *block) { /* FIXME: arch_init.c assumes that this is not called throughout @@ -1837,7 +1678,8 @@ int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp) assert(block); - newsize = HOST_PAGE_ALIGN(newsize); + newsize = TARGET_PAGE_ALIGN(newsize); + newsize = REAL_HOST_PAGE_ALIGN(newsize); if (block->used_length == newsize) { /* @@ -2051,7 +1893,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) #ifdef CONFIG_POSIX RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, uint32_t ram_flags, int fd, off_t offset, - bool readonly, Error **errp) + Error **errp) { RAMBlock *new_block; Error *local_err = NULL; @@ -2059,7 +1901,8 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, /* Just support these ram flags by now. */ assert((ram_flags & ~(RAM_SHARED | RAM_PMEM | RAM_NORESERVE | - RAM_PROTECTED)) == 0); + RAM_PROTECTED | RAM_NAMED_FILE | RAM_READONLY | + RAM_READONLY_FD)) == 0); if (xen_enabled()) { error_setg(errp, "-mem-path not supported with Xen"); @@ -2072,9 +1915,11 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, return NULL; } - size = HOST_PAGE_ALIGN(size); + size = TARGET_PAGE_ALIGN(size); + size = REAL_HOST_PAGE_ALIGN(size); + file_size = get_file_size(fd); - if (file_size > 0 && file_size < size) { + if (file_size > offset && file_size < (offset + size)) { error_setg(errp, "backing store size 0x%" PRIx64 " does not match 'size' option 0x" RAM_ADDR_FMT, file_size, size); @@ -2094,8 +1939,8 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, new_block->used_length = size; new_block->max_length = size; new_block->flags = ram_flags; - new_block->host = file_ram_alloc(new_block, size, fd, readonly, - !file_size, offset, errp); + new_block->host = file_ram_alloc(new_block, size, fd, !file_size, offset, + errp); if (!new_block->host) { g_free(new_block); return NULL; @@ -2114,19 +1959,40 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, uint32_t ram_flags, const char *mem_path, - bool readonly, Error **errp) + off_t offset, Error **errp) { int fd; bool created; RAMBlock *block; - fd = file_ram_open(mem_path, memory_region_name(mr), readonly, &created, - errp); + fd = file_ram_open(mem_path, memory_region_name(mr), + !!(ram_flags & RAM_READONLY_FD), &created); if (fd < 0) { + error_setg_errno(errp, -fd, "can't open backing store %s for guest RAM", + mem_path); + if (!(ram_flags & RAM_READONLY_FD) && !(ram_flags & RAM_SHARED) && + fd == -EACCES) { + /* + * If we can open the file R/O (note: will never create a new file) + * and we are dealing with a private mapping, there are still ways + * to consume such files and get RAM instead of ROM. + */ + fd = file_ram_open(mem_path, memory_region_name(mr), true, + &created); + if (fd < 0) { + return NULL; + } + assert(!created); + close(fd); + error_append_hint(errp, "Consider opening the backing store" + " read-only but still creating writable RAM using" + " '-object memory-backend-file,readonly=on,rom=off...'" + " (see \"VM templating\" documentation)\n"); + } return NULL; } - block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, 0, readonly, errp); + block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, offset, errp); if (!block) { if (created) { unlink(mem_path); @@ -2149,13 +2015,17 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, { RAMBlock *new_block; Error *local_err = NULL; + int align; assert((ram_flags & ~(RAM_SHARED | RAM_RESIZEABLE | RAM_PREALLOC | RAM_NORESERVE)) == 0); assert(!host ^ (ram_flags & RAM_PREALLOC)); - size = HOST_PAGE_ALIGN(size); - max_size = HOST_PAGE_ALIGN(max_size); + align = qemu_real_host_page_size(); + align = MAX(align, TARGET_PAGE_SIZE); + size = ROUND_UP(size, align); + max_size = ROUND_UP(max_size, align); + new_block = g_malloc0(sizeof(*new_block)); new_block->mr = mr; new_block->resized = resized; @@ -2244,6 +2114,7 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) ram_addr_t offset; int flags; void *area, *vaddr; + int prot; RAMBLOCK_FOREACH(block) { offset = addr - block->offset; @@ -2258,13 +2129,14 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) flags |= block->flags & RAM_SHARED ? MAP_SHARED : MAP_PRIVATE; flags |= block->flags & RAM_NORESERVE ? MAP_NORESERVE : 0; + prot = PROT_READ; + prot |= block->flags & RAM_READONLY ? 0 : PROT_WRITE; if (block->fd >= 0) { - area = mmap(vaddr, length, PROT_READ | PROT_WRITE, - flags, block->fd, offset); + area = mmap(vaddr, length, prot, flags, block->fd, + offset + block->fd_offset); } else { flags |= MAP_ANONYMOUS; - area = mmap(vaddr, length, PROT_READ | PROT_WRITE, - flags, -1, 0); + area = mmap(vaddr, length, prot, flags, -1, 0); } if (area != vaddr) { error_report("Could not remap addr: " @@ -2287,10 +2159,8 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) * * Called within RCU critical section. */ -void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr) +void *qemu_map_ram_ptr(RAMBlock *block, ram_addr_t addr) { - RAMBlock *block = ram_block; - if (block == NULL) { block = qemu_get_ram_block(addr); addr -= block->offset; @@ -2315,10 +2185,9 @@ void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr) * * Called within RCU critical section. */ -static void *qemu_ram_ptr_length(RAMBlock *ram_block, ram_addr_t addr, +static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, hwaddr *size, bool lock) { - RAMBlock *block = ram_block; if (*size == 0) { return NULL; } @@ -2354,23 +2223,6 @@ ram_addr_t qemu_ram_block_host_offset(RAMBlock *rb, void *host) return res; } -/* - * Translates a host ptr back to a RAMBlock, a ram_addr and an offset - * in that RAMBlock. - * - * ptr: Host pointer to look up - * round_offset: If true round the result offset down to a page boundary - * *ram_addr: set to result ram_addr - * *offset: set to result offset within the RAMBlock - * - * Returns: RAMBlock (or NULL if not found) - * - * By the time this function returns, the returned pointer is not protected - * by RCU anymore. If the caller is not within an RCU critical section and - * does not hold the iothread lock, it must have other means of protecting the - * pointer, such as a reference to the region that includes the incoming - * ram_addr_t. - */ RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset, ram_addr_t *offset) { @@ -2434,8 +2286,10 @@ RAMBlock *qemu_ram_block_by_name(const char *name) return NULL; } -/* Some of the softmmu routines need to translate from a host pointer - (typically a TLB entry) back to a ram offset. */ +/* + * Some of the system routines need to translate from a host pointer + * (typically a TLB entry) back to a ram offset. + */ ram_addr_t qemu_ram_addr_from_host(void *ptr) { RAMBlock *block; @@ -2449,6 +2303,18 @@ ram_addr_t qemu_ram_addr_from_host(void *ptr) return block->offset + offset; } +ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) +{ + ram_addr_t ram_addr; + + ram_addr = qemu_ram_addr_from_host(ptr); + if (ram_addr == RAM_ADDR_INVALID) { + error_report("Bad ram pointer %p", ptr); + abort(); + } + return ram_addr; +} + static MemTxResult flatview_read(FlatView *fv, hwaddr addr, MemTxAttrs attrs, void *buf, hwaddr len); static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, @@ -2464,7 +2330,7 @@ static MemTxResult subpage_read(void *opaque, hwaddr addr, uint64_t *data, MemTxResult res; #if defined(DEBUG_SUBPAGE) - printf("%s: subpage %p len %u addr " TARGET_FMT_plx "\n", __func__, + printf("%s: subpage %p len %u addr " HWADDR_FMT_plx "\n", __func__, subpage, len, addr); #endif res = flatview_read(subpage->fv, addr + subpage->base, attrs, buf, len); @@ -2482,7 +2348,7 @@ static MemTxResult subpage_write(void *opaque, hwaddr addr, uint8_t buf[8]; #if defined(DEBUG_SUBPAGE) - printf("%s: subpage %p len %u addr " TARGET_FMT_plx + printf("%s: subpage %p len %u addr " HWADDR_FMT_plx " value %"PRIx64"\n", __func__, subpage, len, addr, value); #endif @@ -2496,7 +2362,7 @@ static bool subpage_accepts(void *opaque, hwaddr addr, { subpage_t *subpage = opaque; #if defined(DEBUG_SUBPAGE) - printf("%s: subpage %p %c len %u addr " TARGET_FMT_plx "\n", + printf("%s: subpage %p %c len %u addr " HWADDR_FMT_plx "\n", __func__, subpage, is_write ? 'w' : 'r', len, addr); #endif @@ -2547,7 +2413,7 @@ static subpage_t *subpage_init(FlatView *fv, hwaddr base) NULL, TARGET_PAGE_SIZE); mmio->iomem.subpage = true; #if defined(DEBUG_SUBPAGE) - printf("%s: %p base " TARGET_FMT_plx " len %08x\n", __func__, + printf("%s: %p base " HWADDR_FMT_plx " len %08x\n", __func__, mmio, base, TARGET_PAGE_SIZE); #endif @@ -2573,10 +2439,16 @@ MemoryRegionSection *iotlb_to_section(CPUState *cpu, { int asidx = cpu_asidx_from_attrs(cpu, attrs); CPUAddressSpace *cpuas = &cpu->cpu_ases[asidx]; - AddressSpaceDispatch *d = qatomic_rcu_read(&cpuas->memory_dispatch); - MemoryRegionSection *sections = d->map.sections; + AddressSpaceDispatch *d = cpuas->memory_dispatch; + int section_index = index & ~TARGET_PAGE_MASK; + MemoryRegionSection *ret; - return §ions[index & ~TARGET_PAGE_MASK]; + assert(section_index < d->map.sections_nb); + ret = d->map.sections + section_index; + assert(ret->mr); + assert(ret->mr->ops); + + return ret; } static void io_mem_init(void) @@ -2642,23 +2514,42 @@ static void tcg_log_global_after_sync(MemoryListener *listener) } } +static void tcg_commit_cpu(CPUState *cpu, run_on_cpu_data data) +{ + CPUAddressSpace *cpuas = data.host_ptr; + + cpuas->memory_dispatch = address_space_to_dispatch(cpuas->as); + tlb_flush(cpu); +} + static void tcg_commit(MemoryListener *listener) { CPUAddressSpace *cpuas; - AddressSpaceDispatch *d; + CPUState *cpu; assert(tcg_enabled()); /* since each CPU stores ram addresses in its TLB cache, we must reset the modified entries */ cpuas = container_of(listener, CPUAddressSpace, tcg_as_listener); - cpu_reloading_memory_map(); - /* The CPU and TLB are protected by the iothread lock. - * We reload the dispatch pointer now because cpu_reloading_memory_map() - * may have split the RCU critical section. + cpu = cpuas->cpu; + + /* + * Defer changes to as->memory_dispatch until the cpu is quiescent. + * Otherwise we race between (1) other cpu threads and (2) ongoing + * i/o for the current cpu thread, with data cached by mmu_lookup(). + * + * In addition, queueing the work function will kick the cpu back to + * the main loop, which will end the RCU critical section and reclaim + * the memory data structures. + * + * That said, the listener is also called during realize, before + * all of the tcg machinery for run-on is initialized: thus halt_cond. */ - d = address_space_to_dispatch(cpuas->as); - qatomic_rcu_set(&cpuas->memory_dispatch, d); - tlb_flush(cpuas->cpu); + if (cpu->halt_cond) { + async_run_on_cpu(cpu, tcg_commit_cpu, RUN_ON_CPU_HOST_PTR(cpuas)); + } else { + tcg_commit_cpu(cpu, RUN_ON_CPU_HOST_PTR(cpuas)); + } } static void memory_map_init(void) @@ -2700,7 +2591,7 @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr, } if (dirty_log_mask & (1 << DIRTY_MEMORY_CODE)) { assert(tcg_enabled()); - tb_invalidate_phys_range(addr, addr + length); + tb_invalidate_phys_range(addr, addr + length - 1); dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); } cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask); @@ -2719,7 +2610,7 @@ void memory_region_flush_rom_device(MemoryRegion *mr, hwaddr addr, hwaddr size) invalidate_and_set_dirty(mr, addr, size); } -static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) +int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) { unsigned access_size_max = mr->ops->valid.max_access_size; @@ -2746,12 +2637,12 @@ static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) return l; } -static bool prepare_mmio_access(MemoryRegion *mr) +bool prepare_mmio_access(MemoryRegion *mr) { bool release_lock = false; - if (!qemu_mutex_iothread_locked()) { - qemu_mutex_lock_iothread(); + if (!bql_locked()) { + bql_lock(); release_lock = true; } if (mr->flush_coalesced_mmio) { @@ -2788,42 +2679,69 @@ static bool flatview_access_allowed(MemoryRegion *mr, MemTxAttrs attrs, return false; } +static MemTxResult flatview_write_continue_step(MemTxAttrs attrs, + const uint8_t *buf, + hwaddr len, hwaddr mr_addr, + hwaddr *l, MemoryRegion *mr) +{ + if (!flatview_access_allowed(mr, attrs, mr_addr, *l)) { + return MEMTX_ACCESS_ERROR; + } + + if (!memory_access_is_direct(mr, true)) { + uint64_t val; + MemTxResult result; + bool release_lock = prepare_mmio_access(mr); + + *l = memory_access_size(mr, *l, mr_addr); + /* + * XXX: could force current_cpu to NULL to avoid + * potential bugs + */ + + /* + * Assure Coverity (and ourselves) that we are not going to OVERRUN + * the buffer by following ldn_he_p(). + */ +#ifdef QEMU_STATIC_ANALYSIS + assert((*l == 1 && len >= 1) || + (*l == 2 && len >= 2) || + (*l == 4 && len >= 4) || + (*l == 8 && len >= 8)); +#endif + val = ldn_he_p(buf, *l); + result = memory_region_dispatch_write(mr, mr_addr, val, + size_memop(*l), attrs); + if (release_lock) { + bql_unlock(); + } + + return result; + } else { + /* RAM case */ + uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l, + false); + + memmove(ram_ptr, buf, *l); + invalidate_and_set_dirty(mr, mr_addr, *l); + + return MEMTX_OK; + } +} + /* Called within RCU critical section. */ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, MemTxAttrs attrs, const void *ptr, - hwaddr len, hwaddr addr1, + hwaddr len, hwaddr mr_addr, hwaddr l, MemoryRegion *mr) { - uint8_t *ram_ptr; - uint64_t val; MemTxResult result = MEMTX_OK; - bool release_lock = false; const uint8_t *buf = ptr; for (;;) { - if (!flatview_access_allowed(mr, attrs, addr1, l)) { - result |= MEMTX_ACCESS_ERROR; - /* Keep going. */ - } else if (!memory_access_is_direct(mr, true)) { - release_lock |= prepare_mmio_access(mr); - l = memory_access_size(mr, l, addr1); - /* XXX: could force current_cpu to NULL to avoid - potential bugs */ - val = ldn_he_p(buf, l); - result |= memory_region_dispatch_write(mr, addr1, val, - size_memop(l), attrs); - } else { - /* RAM case */ - ram_ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false); - memcpy(ram_ptr, buf, l); - invalidate_and_set_dirty(mr, addr1, l); - } - - if (release_lock) { - qemu_mutex_unlock_iothread(); - release_lock = false; - } + result |= flatview_write_continue_step(attrs, buf, len, mr_addr, &l, + mr); len -= l; buf += l; @@ -2834,7 +2752,7 @@ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, } l = len; - mr = flatview_translate(fv, addr, &addr1, &l, true, attrs); + mr = flatview_translate(fv, addr, &mr_addr, &l, true, attrs); } return result; @@ -2845,52 +2763,76 @@ static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, const void *buf, hwaddr len) { hwaddr l; - hwaddr addr1; + hwaddr mr_addr; MemoryRegion *mr; l = len; - mr = flatview_translate(fv, addr, &addr1, &l, true, attrs); + mr = flatview_translate(fv, addr, &mr_addr, &l, true, attrs); if (!flatview_access_allowed(mr, attrs, addr, len)) { return MEMTX_ACCESS_ERROR; } return flatview_write_continue(fv, addr, attrs, buf, len, - addr1, l, mr); + mr_addr, l, mr); +} + +static MemTxResult flatview_read_continue_step(MemTxAttrs attrs, uint8_t *buf, + hwaddr len, hwaddr mr_addr, + hwaddr *l, + MemoryRegion *mr) +{ + if (!flatview_access_allowed(mr, attrs, mr_addr, *l)) { + return MEMTX_ACCESS_ERROR; + } + + if (!memory_access_is_direct(mr, false)) { + /* I/O case */ + uint64_t val; + MemTxResult result; + bool release_lock = prepare_mmio_access(mr); + + *l = memory_access_size(mr, *l, mr_addr); + result = memory_region_dispatch_read(mr, mr_addr, &val, size_memop(*l), + attrs); + + /* + * Assure Coverity (and ourselves) that we are not going to OVERRUN + * the buffer by following stn_he_p(). + */ +#ifdef QEMU_STATIC_ANALYSIS + assert((*l == 1 && len >= 1) || + (*l == 2 && len >= 2) || + (*l == 4 && len >= 4) || + (*l == 8 && len >= 8)); +#endif + stn_he_p(buf, *l, val); + + if (release_lock) { + bql_unlock(); + } + return result; + } else { + /* RAM case */ + uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l, + false); + + memcpy(buf, ram_ptr, *l); + + return MEMTX_OK; + } } /* Called within RCU critical section. */ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, MemTxAttrs attrs, void *ptr, - hwaddr len, hwaddr addr1, hwaddr l, + hwaddr len, hwaddr mr_addr, hwaddr l, MemoryRegion *mr) { - uint8_t *ram_ptr; - uint64_t val; MemTxResult result = MEMTX_OK; - bool release_lock = false; uint8_t *buf = ptr; fuzz_dma_read_cb(addr, len, mr); for (;;) { - if (!flatview_access_allowed(mr, attrs, addr1, l)) { - result |= MEMTX_ACCESS_ERROR; - /* Keep going. */ - } else if (!memory_access_is_direct(mr, false)) { - /* I/O case */ - release_lock |= prepare_mmio_access(mr); - l = memory_access_size(mr, l, addr1); - result |= memory_region_dispatch_read(mr, addr1, &val, - size_memop(l), attrs); - stn_he_p(buf, l, val); - } else { - /* RAM case */ - ram_ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false); - memcpy(buf, ram_ptr, l); - } - - if (release_lock) { - qemu_mutex_unlock_iothread(); - release_lock = false; - } + result |= flatview_read_continue_step(attrs, buf, len, mr_addr, &l, mr); len -= l; buf += l; @@ -2901,7 +2843,7 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, } l = len; - mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); + mr = flatview_translate(fv, addr, &mr_addr, &l, false, attrs); } return result; @@ -2912,16 +2854,16 @@ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, MemTxAttrs attrs, void *buf, hwaddr len) { hwaddr l; - hwaddr addr1; + hwaddr mr_addr; MemoryRegion *mr; l = len; - mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); + mr = flatview_translate(fv, addr, &mr_addr, &l, false, attrs); if (!flatview_access_allowed(mr, attrs, addr, len)) { return MEMTX_ACCESS_ERROR; } return flatview_read_continue(fv, addr, attrs, buf, len, - addr1, l, mr); + mr_addr, l, mr); } MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, @@ -3106,6 +3048,8 @@ void cpu_register_map_client(QEMUBH *bh) qemu_mutex_lock(&map_client_list_lock); client->bh = bh; QLIST_INSERT_HEAD(&map_client_list, client, link); + /* Write map_client_list before reading in_use. */ + smp_mb(); if (!qatomic_read(&bounce.in_use)) { cpu_notify_map_clients_locked(); } @@ -3225,7 +3169,6 @@ void *address_space_map(AddressSpace *as, hwaddr len = *plen; hwaddr l, xlat; MemoryRegion *mr; - void *ptr; FlatView *fv; if (len == 0) { @@ -3264,9 +3207,7 @@ void *address_space_map(AddressSpace *as, *plen = flatview_extend_translation(fv, addr, len, mr, xlat, l, is_write, attrs); fuzz_dma_read_cb(addr, *plen, mr); - ptr = qemu_ram_ptr_length(mr->ram_block, xlat, plen, true); - - return ptr; + return qemu_ram_ptr_length(mr->ram_block, xlat, plen, true); } /* Unmaps a memory region previously mapped by address_space_map(). @@ -3298,7 +3239,8 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, qemu_vfree(bounce.buffer); bounce.buffer = NULL; memory_region_unref(bounce.mr); - qatomic_mb_set(&bounce.in_use, false); + /* Clear in_use before reading map_client_list. */ + qatomic_set_mb(&bounce.in_use, false); cpu_notify_map_clients(); } @@ -3348,7 +3290,7 @@ int64_t address_space_cache_init(MemoryRegionCache *cache, * cache->xlat and the end of the section. */ diff = int128_sub(cache->mrs.size, - int128_make64(cache->xlat - cache->mrs.offset_within_region)); + int128_make64(cache->xlat - cache->mrs.offset_within_region)); l = int128_get64(int128_min(diff, int128_make64(l))); mr = cache->mrs.mr; @@ -3426,6 +3368,59 @@ static inline MemoryRegion *address_space_translate_cached( return section.mr; } +/* Called within RCU critical section. */ +static MemTxResult address_space_write_continue_cached(MemTxAttrs attrs, + const void *ptr, + hwaddr len, + hwaddr mr_addr, + hwaddr l, + MemoryRegion *mr) +{ + MemTxResult result = MEMTX_OK; + const uint8_t *buf = ptr; + + for (;;) { + result |= flatview_write_continue_step(attrs, buf, len, mr_addr, &l, + mr); + + len -= l; + buf += l; + mr_addr += l; + + if (!len) { + break; + } + + l = len; + } + + return result; +} + +/* Called within RCU critical section. */ +static MemTxResult address_space_read_continue_cached(MemTxAttrs attrs, + void *ptr, hwaddr len, + hwaddr mr_addr, hwaddr l, + MemoryRegion *mr) +{ + MemTxResult result = MEMTX_OK; + uint8_t *buf = ptr; + + for (;;) { + result |= flatview_read_continue_step(attrs, buf, len, mr_addr, &l, mr); + len -= l; + buf += l; + mr_addr += l; + + if (!len) { + break; + } + l = len; + } + + return result; +} + /* Called from RCU critical section. address_space_read_cached uses this * out of line function when the target is an MMIO or IOMMU region. */ @@ -3433,15 +3428,14 @@ MemTxResult address_space_read_cached_slow(MemoryRegionCache *cache, hwaddr addr, void *buf, hwaddr len) { - hwaddr addr1, l; + hwaddr mr_addr, l; MemoryRegion *mr; l = len; - mr = address_space_translate_cached(cache, addr, &addr1, &l, false, + mr = address_space_translate_cached(cache, addr, &mr_addr, &l, false, MEMTXATTRS_UNSPECIFIED); - return flatview_read_continue(cache->fv, - addr, MEMTXATTRS_UNSPECIFIED, buf, len, - addr1, l, mr); + return address_space_read_continue_cached(MEMTXATTRS_UNSPECIFIED, + buf, len, mr_addr, l, mr); } /* Called from RCU critical section. address_space_write_cached uses this @@ -3451,15 +3445,14 @@ MemTxResult address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr, const void *buf, hwaddr len) { - hwaddr addr1, l; + hwaddr mr_addr, l; MemoryRegion *mr; l = len; - mr = address_space_translate_cached(cache, addr, &addr1, &l, true, + mr = address_space_translate_cached(cache, addr, &mr_addr, &l, true, MEMTXATTRS_UNSPECIFIED); - return flatview_write_continue(cache->fv, - addr, MEMTXATTRS_UNSPECIFIED, buf, len, - addr1, l, mr); + return address_space_write_continue_cached(MEMTXATTRS_UNSPECIFIED, + buf, len, mr_addr, l, mr); } #define ARG1_DECL MemoryRegionCache *cache @@ -3530,19 +3523,28 @@ int qemu_target_page_bits_min(void) return TARGET_PAGE_BITS_MIN; } +/* Convert target pages to MiB (2**20). */ +size_t qemu_target_pages_to_MiB(size_t pages) +{ + int page_bits = TARGET_PAGE_BITS; + + /* So far, the largest (non-huge) page size is 64k, i.e. 16 bits. */ + g_assert(page_bits < 20); + + return pages >> (20 - page_bits); +} + bool cpu_physical_memory_is_io(hwaddr phys_addr) { MemoryRegion*mr; hwaddr l = 1; - bool res; RCU_READ_LOCK_GUARD(); mr = address_space_translate(&address_space_memory, phys_addr, &phys_addr, &l, false, MEMTXATTRS_UNSPECIFIED); - res = !(memory_region_is_ram(mr) || memory_region_is_romd(mr)); - return res; + return !(memory_region_is_ram(mr) || memory_region_is_romd(mr)); } int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque) @@ -3575,16 +3577,15 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) uint8_t *host_startaddr = rb->host + start; if (!QEMU_PTR_IS_ALIGNED(host_startaddr, rb->page_size)) { - error_report("ram_block_discard_range: Unaligned start address: %p", - host_startaddr); + error_report("%s: Unaligned start address: %p", + __func__, host_startaddr); goto err; } if ((start + length) <= rb->max_length) { bool need_madvise, need_fallocate; if (!QEMU_IS_ALIGNED(length, rb->page_size)) { - error_report("ram_block_discard_range: Unaligned length: %zx", - length); + error_report("%s: Unaligned length: %zx", __func__, length); goto err; } @@ -3595,7 +3596,7 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) * fallocate works on hugepages and shmem * shared anonymous memory requires madvise REMOVE */ - need_madvise = (rb->page_size == qemu_host_page_size); + need_madvise = (rb->page_size == qemu_real_host_page_size()); need_fallocate = rb->fd != -1; if (need_fallocate) { /* For a file, this causes the area of the file to be zero'd @@ -3603,20 +3604,47 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) * so a userfault will trigger. */ #ifdef CONFIG_FALLOCATE_PUNCH_HOLE + /* + * fallocate() will fail with readonly files. Let's print a + * proper error message. + */ + if (rb->flags & RAM_READONLY_FD) { + error_report("%s: Discarding RAM with readonly files is not" + " supported", __func__); + goto err; + + } + /* + * We'll discard data from the actual file, even though we only + * have a MAP_PRIVATE mapping, possibly messing with other + * MAP_PRIVATE/MAP_SHARED mappings. There is no easy way to + * change that behavior whithout violating the promised + * semantics of ram_block_discard_range(). + * + * Only warn, because it works as long as nobody else uses that + * file. + */ + if (!qemu_ram_is_shared(rb)) { + warn_report_once("%s: Discarding RAM" + " in private file mappings is possibly" + " dangerous, because it will modify the" + " underlying file and will affect other" + " users of the file", __func__); + } + ret = fallocate(rb->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, start, length); if (ret) { ret = -errno; - error_report("ram_block_discard_range: Failed to fallocate " - "%s:%" PRIx64 " +%zx (%d)", - rb->idstr, start, length, ret); + error_report("%s: Failed to fallocate %s:%" PRIx64 " +%zx (%d)", + __func__, rb->idstr, start, length, ret); goto err; } #else ret = -ENOSYS; - error_report("ram_block_discard_range: fallocate not available/file" + error_report("%s: fallocate not available/file" "%s:%" PRIx64 " +%zx (%d)", - rb->idstr, start, length, ret); + __func__, rb->idstr, start, length, ret); goto err; #endif } @@ -3634,25 +3662,23 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) } if (ret) { ret = -errno; - error_report("ram_block_discard_range: Failed to discard range " + error_report("%s: Failed to discard range " "%s:%" PRIx64 " +%zx (%d)", - rb->idstr, start, length, ret); + __func__, rb->idstr, start, length, ret); goto err; } #else ret = -ENOSYS; - error_report("ram_block_discard_range: MADVISE not available" - "%s:%" PRIx64 " +%zx (%d)", - rb->idstr, start, length, ret); + error_report("%s: MADVISE not available %s:%" PRIx64 " +%zx (%d)", + __func__, rb->idstr, start, length, ret); goto err; #endif } trace_ram_block_discard_range(rb->idstr, host_startaddr, length, need_madvise, need_fallocate, ret); } else { - error_report("ram_block_discard_range: Overrun block '%s' (%" PRIu64 - "/%zx/" RAM_ADDR_FMT")", - rb->idstr, start, length, rb->max_length); + error_report("%s: Overrun block '%s' (%" PRIu64 "/%zx/" RAM_ADDR_FMT")", + __func__, rb->idstr, start, length, rb->max_length); } err: @@ -3697,11 +3723,11 @@ void mtree_print_dispatch(AddressSpaceDispatch *d, MemoryRegion *root) const char *names[] = { " [unassigned]", " [not dirty]", " [ROM]", " [watch]" }; - qemu_printf(" #%d @" TARGET_FMT_plx ".." TARGET_FMT_plx + qemu_printf(" #%d @" HWADDR_FMT_plx ".." HWADDR_FMT_plx " %s%s%s%s%s", i, s->offset_within_address_space, - s->offset_within_address_space + MR_SIZE(s->mr->size), + s->offset_within_address_space + MR_SIZE(s->size), s->mr->name ? s->mr->name : "(noname)", i < ARRAY_SIZE(names) ? names[i] : "", s->mr == root ? " [ROOT]" : "", diff --git a/softmmu/qdev-monitor.c b/system/qdev-monitor.c similarity index 89% rename from softmmu/qdev-monitor.c rename to system/qdev-monitor.c index bb5897fc76..840177d19f 100644 --- a/softmmu/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -38,7 +38,6 @@ #include "qemu/option_int.h" #include "sysemu/block-backend.h" #include "migration/misc.h" -#include "migration/migration.h" #include "qemu/cutils.h" #include "hw/qdev-properties.h" #include "hw/clock.h" @@ -86,6 +85,9 @@ static const QDevAlias qdev_alias_table[] = { { "virtio-gpu-pci", "virtio-gpu", QEMU_ARCH_VIRTIO_PCI }, { "virtio-gpu-gl-device", "virtio-gpu-gl", QEMU_ARCH_VIRTIO_MMIO }, { "virtio-gpu-gl-pci", "virtio-gpu-gl", QEMU_ARCH_VIRTIO_PCI }, + { "virtio-gpu-rutabaga-device", "virtio-gpu-rutabaga", + QEMU_ARCH_VIRTIO_MMIO }, + { "virtio-gpu-rutabaga-pci", "virtio-gpu-rutabaga", QEMU_ARCH_VIRTIO_PCI }, { "virtio-input-host-device", "virtio-input-host", QEMU_ARCH_VIRTIO_MMIO }, { "virtio-input-host-ccw", "virtio-input-host", QEMU_ARCH_VIRTIO_CCW }, { "virtio-input-host-pci", "virtio-input-host", QEMU_ARCH_VIRTIO_PCI }, @@ -108,6 +110,8 @@ static const QDevAlias qdev_alias_table[] = { { "virtio-serial-device", "virtio-serial", QEMU_ARCH_VIRTIO_MMIO }, { "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_VIRTIO_CCW }, { "virtio-serial-pci", "virtio-serial", QEMU_ARCH_VIRTIO_PCI}, + { "virtio-sound-device", "virtio-sound", QEMU_ARCH_VIRTIO_MMIO }, + { "virtio-sound-pci", "virtio-sound", QEMU_ARCH_VIRTIO_PCI }, { "virtio-tablet-device", "virtio-tablet", QEMU_ARCH_VIRTIO_MMIO }, { "virtio-tablet-ccw", "virtio-tablet", QEMU_ARCH_VIRTIO_CCW }, { "virtio-tablet-pci", "virtio-tablet", QEMU_ARCH_VIRTIO_PCI }, @@ -711,7 +715,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, goto err_del_dev; } - if (!qdev_realize(DEVICE(dev), bus, errp)) { + if (!qdev_realize(dev, bus, errp)) { goto err_del_dev; } return dev; @@ -739,7 +743,6 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) } #define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__) -static void qbus_print(Monitor *mon, BusState *bus, int indent); static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props, int indent) @@ -779,13 +782,9 @@ static void bus_print_dev(BusState *bus, Monitor *mon, DeviceState *dev, int ind static void qdev_print(Monitor *mon, DeviceState *dev, int indent) { ObjectClass *class; - BusState *child; NamedGPIOList *ngl; NamedClockList *ncl; - qdev_printf("dev: %s, id \"%s\"\n", object_get_typename(OBJECT(dev)), - dev->id ? dev->id : ""); - indent += 2; QLIST_FOREACH(ngl, &dev->gpios, node) { if (ngl->num_in) { qdev_printf("gpio-in \"%s\" %d\n", ngl->name ? ngl->name : "", @@ -809,12 +808,9 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent) class = object_class_get_parent(class); } while (class != object_class_by_name(TYPE_DEVICE)); bus_print_dev(dev->parent_bus, mon, dev, indent); - QLIST_FOREACH(child, &dev->child_bus, sibling) { - qbus_print(mon, child, indent); - } } -static void qbus_print(Monitor *mon, BusState *bus, int indent) +static void qbus_print(Monitor *mon, BusState *bus, int indent, bool details) { BusChild *kid; @@ -822,16 +818,27 @@ static void qbus_print(Monitor *mon, BusState *bus, int indent) indent += 2; qdev_printf("type %s\n", object_get_typename(OBJECT(bus))); QTAILQ_FOREACH(kid, &bus->children, sibling) { + BusState *child_bus; DeviceState *dev = kid->child; - qdev_print(mon, dev, indent); + qdev_printf("dev: %s, id \"%s\"\n", object_get_typename(OBJECT(dev)), + dev->id ? dev->id : ""); + if (details) { + qdev_print(mon, dev, indent + 2); + } + QLIST_FOREACH(child_bus, &dev->child_bus, sibling) { + qbus_print(mon, child_bus, indent + 2, details); + } } } #undef qdev_printf void hmp_info_qtree(Monitor *mon, const QDict *qdict) { - if (sysbus_get_default()) - qbus_print(mon, sysbus_get_default(), 0); + bool details = !qdict_get_try_bool(qdict, "brief", false); + + if (sysbus_get_default()) { + qbus_print(mon, sysbus_get_default(), 0, details); + } } void hmp_info_qdm(Monitor *mon, const QDict *qdict) @@ -853,19 +860,18 @@ void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp) return; } dev = qdev_device_add(opts, errp); - - /* - * Drain all pending RCU callbacks. This is done because - * some bus related operations can delay a device removal - * (in this case this can happen if device is added and then - * removed due to a configuration error) - * to a RCU callback, but user might expect that this interface - * will finish its job completely once qmp command returns result - * to the user - */ - drain_call_rcu(); - if (!dev) { + /* + * Drain all pending RCU callbacks. This is done because + * some bus related operations can delay a device removal + * (in this case this can happen if device is added and then + * removed due to a configuration error) + * to a RCU callback, but user might expect that this interface + * will finish its job completely once qmp command returns result + * to the user + */ + drain_call_rcu(); + qemu_opts_del(opts); return; } @@ -885,7 +891,7 @@ static DeviceState *find_device_state(const char *id, Error **errp) dev = (DeviceState *)object_dynamic_cast(obj, TYPE_DEVICE); if (!dev) { - error_setg(errp, "%s is not a hotpluggable device", id); + error_setg(errp, "%s is not a device", id); return NULL; } @@ -899,6 +905,10 @@ void qdev_unplug(DeviceState *dev, Error **errp) HotplugHandlerClass *hdc; Error *local_err = NULL; + if (qdev_unplug_blocked(dev, errp)) { + return; + } + if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); return; @@ -969,6 +979,88 @@ void hmp_device_del(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, err); } +void device_add_completion(ReadLineState *rs, int nb_args, const char *str) +{ + GSList *list, *elt; + size_t len; + + if (nb_args != 2) { + return; + } + + len = strlen(str); + readline_set_completion_index(rs, len); + list = elt = object_class_get_list(TYPE_DEVICE, false); + while (elt) { + DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, elt->data, + TYPE_DEVICE); + + if (dc->user_creatable) { + readline_add_completion_of(rs, str, + object_class_get_name(OBJECT_CLASS(dc))); + } + elt = elt->next; + } + g_slist_free(list); +} + +static int qdev_add_hotpluggable_device(Object *obj, void *opaque) +{ + GSList **list = opaque; + DeviceState *dev = (DeviceState *)object_dynamic_cast(obj, TYPE_DEVICE); + + if (dev == NULL) { + return 0; + } + + if (dev->realized && object_property_get_bool(obj, "hotpluggable", NULL)) { + *list = g_slist_append(*list, dev); + } + + return 0; +} + +static GSList *qdev_build_hotpluggable_device_list(Object *peripheral) +{ + GSList *list = NULL; + + object_child_foreach(peripheral, qdev_add_hotpluggable_device, &list); + + return list; +} + +static void peripheral_device_del_completion(ReadLineState *rs, + const char *str) +{ + Object *peripheral = container_get(qdev_get_machine(), "/peripheral"); + GSList *list, *item; + + list = qdev_build_hotpluggable_device_list(peripheral); + if (!list) { + return; + } + + for (item = list; item; item = g_slist_next(item)) { + DeviceState *dev = item->data; + + if (dev->id) { + readline_add_completion_of(rs, str, dev->id); + } + } + + g_slist_free(list); +} + +void device_del_completion(ReadLineState *rs, int nb_args, const char *str) +{ + if (nb_args != 2) { + return; + } + + readline_set_completion_index(rs, strlen(str)); + peripheral_device_del_completion(rs, str); +} + BlockBackend *blk_by_qdev_id(const char *id, Error **errp) { DeviceState *dev; diff --git a/softmmu/qemu-seccomp.c b/system/qemu-seccomp.c similarity index 95% rename from softmmu/qemu-seccomp.c rename to system/qemu-seccomp.c index deaf8a4ef5..98ffce075c 100644 --- a/softmmu/qemu-seccomp.c +++ b/system/qemu-seccomp.c @@ -74,7 +74,7 @@ const struct scmp_arg_cmp sched_setscheduler_arg[] = { #define RULE_CLONE_FLAG(flag) \ { SCMP_SYS(clone), QEMU_SECCOMP_SET_SPAWN, \ - ARRAY_SIZE(clone_arg ## flag), clone_arg ## flag, SCMP_ACT_TRAP } + ARRAY_SIZE(clone_arg ## flag), clone_arg ## flag, SCMP_ACT_ERRNO(EPERM) } /* If no CLONE_* flags are set, except CSIGNAL, deny */ const struct scmp_arg_cmp clone_arg_none[] = { @@ -214,13 +214,13 @@ static const struct QemuSeccompSyscall denylist[] = { 0, NULL, SCMP_ACT_TRAP }, /* spawn */ { SCMP_SYS(fork), QEMU_SECCOMP_SET_SPAWN, - 0, NULL, SCMP_ACT_TRAP }, + 0, NULL, SCMP_ACT_ERRNO(EPERM) }, { SCMP_SYS(vfork), QEMU_SECCOMP_SET_SPAWN, - 0, NULL, SCMP_ACT_TRAP }, + 0, NULL, SCMP_ACT_ERRNO(EPERM) }, { SCMP_SYS(execve), QEMU_SECCOMP_SET_SPAWN, - 0, NULL, SCMP_ACT_TRAP }, + 0, NULL, SCMP_ACT_ERRNO(EPERM) }, { SCMP_SYS(clone), QEMU_SECCOMP_SET_SPAWN, - ARRAY_SIZE(clone_arg_none), clone_arg_none, SCMP_ACT_TRAP }, + ARRAY_SIZE(clone_arg_none), clone_arg_none, SCMP_ACT_ERRNO(EPERM) }, RULE_CLONE_FLAG(CLONE_VM), RULE_CLONE_FLAG(CLONE_FS), RULE_CLONE_FLAG(CLONE_FILES), @@ -283,9 +283,9 @@ static uint32_t qemu_seccomp_update_action(uint32_t action) if (action == SCMP_ACT_TRAP) { static int kill_process = -1; if (kill_process == -1) { - uint32_t action = SECCOMP_RET_KILL_PROCESS; + uint32_t testaction = SECCOMP_RET_KILL_PROCESS; - if (qemu_seccomp(SECCOMP_GET_ACTION_AVAIL, 0, &action) == 0) { + if (qemu_seccomp(SECCOMP_GET_ACTION_AVAIL, 0, &testaction) == 0) { kill_process = 1; } else { kill_process = 0; @@ -312,6 +312,19 @@ static int seccomp_start(uint32_t seccomp_opts, Error **errp) goto seccomp_return; } +#if defined(CONFIG_SECCOMP_SYSRAWRC) + /* + * This must be the first seccomp_attr_set() call to have full + * error propagation from subsequent seccomp APIs. + */ + rc = seccomp_attr_set(ctx, SCMP_FLTATR_API_SYSRAWRC, 1); + if (rc != 0) { + error_setg_errno(errp, -rc, + "failed to set seccomp rawrc attribute"); + goto seccomp_return; + } +#endif + rc = seccomp_attr_set(ctx, SCMP_FLTATR_CTL_TSYNC, 1); if (rc != 0) { error_setg_errno(errp, -rc, diff --git a/softmmu/qtest.c b/system/qtest.c similarity index 90% rename from softmmu/qtest.c rename to system/qtest.c index f8acef2628..6da58b3874 100644 --- a/softmmu/qtest.c +++ b/system/qtest.c @@ -13,14 +13,15 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "cpu.h" #include "sysemu/qtest.h" #include "sysemu/runstate.h" #include "chardev/char-fe.h" #include "exec/ioport.h" #include "exec/memory.h" +#include "exec/tswap.h" #include "hw/qdev-core.h" #include "hw/irq.h" +#include "hw/core/cpu.h" #include "qemu/accel.h" #include "sysemu/cpu-timers.h" #include "qemu/config-file.h" @@ -28,12 +29,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/cutils.h" -#include "qapi/qmp/qerror.h" #include "qom/object_interfaces.h" -#include CONFIG_DEVICES -#ifdef CONFIG_PSERIES -#include "hw/ppc/spapr_rtas.h" -#endif #define MAX_IRQ 256 @@ -264,7 +260,7 @@ static int hex2nib(char ch) } } -static void qtest_send_prefix(CharBackend *chr) +void qtest_send_prefix(CharBackend *chr) { if (!qtest_log_fp || !qtest_opened) { return; @@ -303,8 +299,7 @@ static void qtest_send(CharBackend *chr, const char *str) qtest_server_send(qtest_server_send_opaque, str); } -static void G_GNUC_PRINTF(2, 3) qtest_sendf(CharBackend *chr, - const char *fmt, ...) +void qtest_sendf(CharBackend *chr, const char *fmt, ...) { va_list ap; gchar *buffer; @@ -362,6 +357,24 @@ static void qtest_clock_warp(int64_t dest) qemu_clock_notify(QEMU_CLOCK_VIRTUAL); } +static bool (*process_command_cb)(CharBackend *chr, gchar **words); + +void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words)) +{ + assert(!process_command_cb); /* Switch to a list if we need more than one */ + + process_command_cb = pc_cb; +} + +static void qtest_install_gpio_out_intercept(DeviceState *dev, const char *name, int n) +{ + qemu_irq *disconnected = g_new0(qemu_irq, 1); + qemu_irq icpt = qemu_allocate_irq(qtest_irq_handler, + disconnected, n); + + *disconnected = qdev_intercept_gpio_out(dev, icpt, name, n); +} + static void qtest_process_command(CharBackend *chr, gchar **words) { const gchar *command; @@ -385,8 +398,13 @@ static void qtest_process_command(CharBackend *chr, gchar **words) || strcmp(words[0], "irq_intercept_in") == 0) { DeviceState *dev; NamedGPIOList *ngl; + bool is_named; + bool is_outbound; + bool interception_succeeded = false; g_assert(words[1]); + is_named = words[2] != NULL; + is_outbound = words[0][14] == 'o'; dev = DEVICE(object_resolve_path(words[1], NULL)); if (!dev) { qtest_send_prefix(chr); @@ -394,6 +412,12 @@ static void qtest_process_command(CharBackend *chr, gchar **words) return; } + if (is_named && !is_outbound) { + qtest_send_prefix(chr); + qtest_send(chr, "FAIL Interception of named in-GPIOs not yet supported\n"); + return; + } + if (irq_intercept_dev) { qtest_send_prefix(chr); if (irq_intercept_dev != dev) { @@ -405,28 +429,30 @@ static void qtest_process_command(CharBackend *chr, gchar **words) } QLIST_FOREACH(ngl, &dev->gpios, node) { - /* We don't support intercept of named GPIOs yet */ - if (ngl->name) { - continue; - } - if (words[0][14] == 'o') { - int i; - for (i = 0; i < ngl->num_out; ++i) { - qemu_irq *disconnected = g_new0(qemu_irq, 1); - qemu_irq icpt = qemu_allocate_irq(qtest_irq_handler, - disconnected, i); - - *disconnected = qdev_intercept_gpio_out(dev, icpt, - ngl->name, i); + /* We don't support inbound interception of named GPIOs yet */ + if (is_outbound) { + /* NULL is valid and matchable, for "unnamed GPIO" */ + if (g_strcmp0(ngl->name, words[2]) == 0) { + int i; + for (i = 0; i < ngl->num_out; ++i) { + qtest_install_gpio_out_intercept(dev, ngl->name, i); + } + interception_succeeded = true; } } else { qemu_irq_intercept_in(ngl->in, qtest_irq_handler, ngl->num_in); + interception_succeeded = true; } } - irq_intercept_dev = dev; + qtest_send_prefix(chr); - qtest_send(chr, "OK\n"); + if (interception_succeeded) { + irq_intercept_dev = dev; + qtest_send(chr, "OK\n"); + } else { + qtest_send(chr, "FAIL No intercepts installed\n"); + } } else if (strcmp(words[0], "set_irq_in") == 0) { DeviceState *dev; qemu_irq irq; @@ -714,30 +740,11 @@ static void qtest_process_command(CharBackend *chr, gchar **words) qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "endianness") == 0) { qtest_send_prefix(chr); -#if TARGET_BIG_ENDIAN - qtest_sendf(chr, "OK big\n"); -#else - qtest_sendf(chr, "OK little\n"); -#endif -#ifdef CONFIG_PSERIES - } else if (strcmp(words[0], "rtas") == 0) { - uint64_t res, args, ret; - unsigned long nargs, nret; - int rc; - - rc = qemu_strtoul(words[2], NULL, 0, &nargs); - g_assert(rc == 0); - rc = qemu_strtou64(words[3], NULL, 0, &args); - g_assert(rc == 0); - rc = qemu_strtoul(words[4], NULL, 0, &nret); - g_assert(rc == 0); - rc = qemu_strtou64(words[5], NULL, 0, &ret); - g_assert(rc == 0); - res = qtest_rtas_call(words[1], nargs, args, nret, ret); - - qtest_send_prefix(chr); - qtest_sendf(chr, "OK %"PRIu64"\n", res); -#endif + if (target_words_bigendian()) { + qtest_sendf(chr, "OK big\n"); + } else { + qtest_sendf(chr, "OK little\n"); + } } else if (qtest_enabled() && strcmp(words[0], "clock_step") == 0) { int64_t ns; @@ -753,12 +760,18 @@ static void qtest_process_command(CharBackend *chr, gchar **words) qtest_sendf(chr, "OK %"PRIi64"\n", (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); } else if (strcmp(words[0], "module_load") == 0) { + Error *local_err = NULL; + int rv; g_assert(words[1] && words[2]); qtest_send_prefix(chr); - if (module_load_one(words[1], words[2], false)) { + rv = module_load(words[1], words[2], &local_err); + if (rv > 0) { qtest_sendf(chr, "OK\n"); } else { + if (rv < 0) { + error_report_err(local_err); + } qtest_sendf(chr, "FAIL\n"); } } else if (qtest_enabled() && strcmp(words[0], "clock_set") == 0) { @@ -772,6 +785,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) qtest_send_prefix(chr); qtest_sendf(chr, "OK %"PRIi64"\n", (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + } else if (process_command_cb && process_command_cb(chr, words)) { + /* Command got consumed by the callback handler */ } else { qtest_send_prefix(chr); qtest_sendf(chr, "FAIL Unknown command '%s'\n", words[0]); @@ -852,7 +867,7 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error ** { ERRP_GUARD(); Chardev *chr; - Object *qtest; + Object *qobj; chr = qemu_chr_new("qtest", qtest_chrdev, NULL); if (chr == NULL) { @@ -861,18 +876,18 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error ** return; } - qtest = object_new(TYPE_QTEST); - object_property_set_str(qtest, "chardev", "qtest", &error_abort); + qobj = object_new(TYPE_QTEST); + object_property_set_str(qobj, "chardev", chr->label, &error_abort); if (qtest_log) { - object_property_set_str(qtest, "log", qtest_log, &error_abort); + object_property_set_str(qobj, "log", qtest_log, &error_abort); } - object_property_add_child(qdev_get_machine(), "qtest", qtest); - user_creatable_complete(USER_CREATABLE(qtest), errp); + object_property_add_child(qdev_get_machine(), "qtest", qobj); + user_creatable_complete(USER_CREATABLE(qobj), errp); if (*errp) { - object_unparent(qtest); + object_unparent(qobj); } object_unref(OBJECT(chr)); - object_unref(qtest); + object_unref(qobj); } static bool qtest_server_start(QTest *q, Error **errp) @@ -977,7 +992,7 @@ static void qtest_set_log(Object *obj, const char *value, Error **errp) QTest *q = QTEST(obj); if (qtest == q) { - error_setg(errp, QERR_PERMISSION_DENIED); + error_setg(errp, "Property 'log' can not be set now"); } else { g_free(q->log); q->log = g_strdup(value); @@ -997,7 +1012,7 @@ static void qtest_set_chardev(Object *obj, const char *value, Error **errp) Chardev *chr; if (qtest == q) { - error_setg(errp, QERR_PERMISSION_DENIED); + error_setg(errp, "Property 'chardev' can not be set now"); return; } diff --git a/softmmu/rtc.c b/system/rtc.c similarity index 93% rename from softmmu/rtc.c rename to system/rtc.c index 7e2956f81e..4904581abe 100644 --- a/softmmu/rtc.c +++ b/system/rtc.c @@ -33,6 +33,7 @@ #include "sysemu/replay.h" #include "sysemu/sysemu.h" #include "sysemu/rtc.h" +#include "hw/rtc/mc146818rtc.h" static enum { RTC_BASE_UTC, @@ -67,7 +68,7 @@ static time_t qemu_ref_timedate(QEMUClockType clock) return value; } -void qemu_get_timedate(struct tm *tm, int offset) +void qemu_get_timedate(struct tm *tm, time_t offset) { time_t ti = qemu_ref_timedate(rtc_clock); @@ -84,7 +85,7 @@ void qemu_get_timedate(struct tm *tm, int offset) } } -int qemu_timedate_diff(struct tm *tm) +time_t qemu_timedate_diff(struct tm *tm) { time_t seconds; @@ -151,11 +152,8 @@ void configure_rtc(QemuOpts *opts) if (!strcmp(value, "utc")) { rtc_base_type = RTC_BASE_UTC; } else if (!strcmp(value, "localtime")) { - Error *blocker = NULL; rtc_base_type = RTC_BASE_LOCALTIME; - error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED, - "-rtc base=localtime"); - replay_add_blocker(blocker); + replay_add_blocker("-rtc base=localtime"); } else { rtc_base_type = RTC_BASE_DATETIME; configure_rtc_base_datetime(value); @@ -177,10 +175,13 @@ void configure_rtc(QemuOpts *opts) value = qemu_opt_get(opts, "driftfix"); if (value) { if (!strcmp(value, "slew")) { - object_register_sugar_prop("mc146818rtc", + object_register_sugar_prop(TYPE_MC146818_RTC, "lost_tick_policy", "slew", false); + if (!object_class_by_name(TYPE_MC146818_RTC)) { + warn_report("driftfix 'slew' is not available with this machine"); + } } else if (!strcmp(value, "none")) { /* discard is default */ } else { diff --git a/softmmu/runstate-action.c b/system/runstate-action.c similarity index 100% rename from softmmu/runstate-action.c rename to system/runstate-action.c diff --git a/system/runstate-hmp-cmds.c b/system/runstate-hmp-cmds.c new file mode 100644 index 0000000000..2df670f0c0 --- /dev/null +++ b/system/runstate-hmp-cmds.c @@ -0,0 +1,95 @@ +/* + * HMP commands related to run state + * + * 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. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "exec/cpu-common.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-run-state.h" +#include "qapi/qmp/qdict.h" +#include "qemu/accel.h" + +void hmp_info_status(Monitor *mon, const QDict *qdict) +{ + StatusInfo *info; + + info = qmp_query_status(NULL); + + monitor_printf(mon, "VM status: %s", + info->running ? "running" : "paused"); + + if (!info->running && info->status != RUN_STATE_PAUSED) { + monitor_printf(mon, " (%s)", RunState_str(info->status)); + } + + monitor_printf(mon, "\n"); + + qapi_free_StatusInfo(info); +} + +void hmp_one_insn_per_tb(Monitor *mon, const QDict *qdict) +{ + const char *option = qdict_get_try_str(qdict, "option"); + AccelState *accel = current_accel(); + bool newval; + + if (!object_property_find(OBJECT(accel), "one-insn-per-tb")) { + monitor_printf(mon, + "This accelerator does not support setting one-insn-per-tb\n"); + return; + } + + if (!option || !strcmp(option, "on")) { + newval = true; + } else if (!strcmp(option, "off")) { + newval = false; + } else { + monitor_printf(mon, "unexpected option %s\n", option); + return; + } + /* If the property exists then setting it can never fail */ + object_property_set_bool(OBJECT(accel), "one-insn-per-tb", + newval, &error_abort); +} + +void hmp_watchdog_action(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + WatchdogAction action; + char *qapi_value; + + qapi_value = g_ascii_strdown(qdict_get_str(qdict, "action"), -1); + action = qapi_enum_parse(&WatchdogAction_lookup, qapi_value, -1, &err); + g_free(qapi_value); + if (err) { + hmp_handle_error(mon, err); + return; + } + qmp_watchdog_set_action(action, &error_abort); +} + +void watchdog_action_completion(ReadLineState *rs, int nb_args, const char *str) +{ + int i; + + if (nb_args != 2) { + return; + } + readline_set_completion_index(rs, strlen(str)); + for (i = 0; i < WATCHDOG_ACTION__MAX; i++) { + readline_add_completion_of(rs, str, WatchdogAction_str(i)); + } +} diff --git a/softmmu/runstate.c b/system/runstate.c similarity index 84% rename from softmmu/runstate.c rename to system/runstate.c index fac7b63259..d6ab860eca 100644 --- a/softmmu/runstate.c +++ b/system/runstate.c @@ -30,7 +30,7 @@ #include "crypto/cipher.h" #include "crypto/init.h" #include "exec/cpu-common.h" -#include "exec/gdbstub.h" +#include "gdbstub/syscalls.h" #include "hw/boards.h" #include "migration/misc.h" #include "migration/postcopy-ram.h" @@ -40,13 +40,14 @@ #include "qapi/error.h" #include "qapi/qapi-commands-run-state.h" #include "qapi/qapi-events-run-state.h" +#include "qemu/accel.h" #include "qemu/error-report.h" -#include "qemu/log.h" #include "qemu/job.h" #include "qemu/log.h" #include "qemu/module.h" #include "qemu/plugin.h" #include "qemu/sockets.h" +#include "qemu/timer.h" #include "qemu/thread.h" #include "qom/object.h" #include "qom/object_interfaces.h" @@ -76,6 +77,7 @@ typedef struct { static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_PRELAUNCH, RUN_STATE_INMIGRATE }, + { RUN_STATE_PRELAUNCH, RUN_STATE_SUSPENDED }, { RUN_STATE_DEBUG, RUN_STATE_RUNNING }, { RUN_STATE_DEBUG, RUN_STATE_FINISH_MIGRATE }, @@ -107,6 +109,7 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_PAUSED, RUN_STATE_POSTMIGRATE }, { RUN_STATE_PAUSED, RUN_STATE_PRELAUNCH }, { RUN_STATE_PAUSED, RUN_STATE_COLO}, + { RUN_STATE_PAUSED, RUN_STATE_SUSPENDED}, { RUN_STATE_POSTMIGRATE, RUN_STATE_RUNNING }, { RUN_STATE_POSTMIGRATE, RUN_STATE_FINISH_MIGRATE }, @@ -120,12 +123,20 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_FINISH_MIGRATE, RUN_STATE_PAUSED }, { RUN_STATE_FINISH_MIGRATE, RUN_STATE_POSTMIGRATE }, { RUN_STATE_FINISH_MIGRATE, RUN_STATE_PRELAUNCH }, - { RUN_STATE_FINISH_MIGRATE, RUN_STATE_COLO}, + { RUN_STATE_FINISH_MIGRATE, RUN_STATE_COLO }, + { RUN_STATE_FINISH_MIGRATE, RUN_STATE_INTERNAL_ERROR }, + { RUN_STATE_FINISH_MIGRATE, RUN_STATE_IO_ERROR }, + { RUN_STATE_FINISH_MIGRATE, RUN_STATE_SHUTDOWN }, + { RUN_STATE_FINISH_MIGRATE, RUN_STATE_SUSPENDED }, + { RUN_STATE_FINISH_MIGRATE, RUN_STATE_WATCHDOG }, + { RUN_STATE_FINISH_MIGRATE, RUN_STATE_GUEST_PANICKED }, { RUN_STATE_RESTORE_VM, RUN_STATE_RUNNING }, { RUN_STATE_RESTORE_VM, RUN_STATE_PRELAUNCH }, + { RUN_STATE_RESTORE_VM, RUN_STATE_SUSPENDED }, { RUN_STATE_COLO, RUN_STATE_RUNNING }, + { RUN_STATE_COLO, RUN_STATE_PRELAUNCH }, { RUN_STATE_COLO, RUN_STATE_SHUTDOWN}, { RUN_STATE_RUNNING, RUN_STATE_DEBUG }, @@ -141,6 +152,7 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_RUNNING, RUN_STATE_COLO}, { RUN_STATE_SAVE_VM, RUN_STATE_RUNNING }, + { RUN_STATE_SAVE_VM, RUN_STATE_SUSPENDED }, { RUN_STATE_SHUTDOWN, RUN_STATE_PAUSED }, { RUN_STATE_SHUTDOWN, RUN_STATE_FINISH_MIGRATE }, @@ -153,6 +165,10 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_SUSPENDED, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_SUSPENDED, RUN_STATE_PRELAUNCH }, { RUN_STATE_SUSPENDED, RUN_STATE_COLO}, + { RUN_STATE_SUSPENDED, RUN_STATE_PAUSED}, + { RUN_STATE_SUSPENDED, RUN_STATE_SAVE_VM }, + { RUN_STATE_SUSPENDED, RUN_STATE_RESTORE_VM }, + { RUN_STATE_SUSPENDED, RUN_STATE_SHUTDOWN }, { RUN_STATE_WATCHDOG, RUN_STATE_RUNNING }, { RUN_STATE_WATCHDOG, RUN_STATE_FINISH_MIGRATE }, @@ -173,18 +189,6 @@ bool runstate_check(RunState state) return current_run_state == state; } -bool runstate_store(char *str, size_t size) -{ - const char *state = RunState_str(current_run_state); - size_t len = strlen(state) + 1; - - if (len > size) { - return false; - } - memcpy(str, state, len); - return true; -} - static void runstate_init(void) { const RunStateTransition *p; @@ -219,6 +223,11 @@ void runstate_set(RunState new_state) current_run_state = new_state; } +RunState runstate_get(void) +{ + return current_run_state; +} + bool runstate_is_running(void) { return runstate_check(RUN_STATE_RUNNING); @@ -235,7 +244,6 @@ StatusInfo *qmp_query_status(Error **errp) StatusInfo *info = g_malloc0(sizeof(*info)); info->running = runstate_is_running(); - info->singlestep = singlestep; info->status = current_run_state; return info; @@ -263,6 +271,7 @@ void qemu_system_vmstop_request(RunState state) } struct VMChangeStateEntry { VMChangeStateHandler *cb; + VMChangeStateHandler *prepare_cb; void *opaque; QTAILQ_ENTRY(VMChangeStateEntry) entries; int priority; @@ -285,12 +294,39 @@ static QTAILQ_HEAD(, VMChangeStateEntry) vm_change_state_head = */ VMChangeStateEntry *qemu_add_vm_change_state_handler_prio( VMChangeStateHandler *cb, void *opaque, int priority) +{ + return qemu_add_vm_change_state_handler_prio_full(cb, NULL, opaque, + priority); +} + +/** + * qemu_add_vm_change_state_handler_prio_full: + * @cb: the main callback to invoke + * @prepare_cb: a callback to invoke before the main callback + * @opaque: user data passed to the callbacks + * @priority: low priorities execute first when the vm runs and the reverse is + * true when the vm stops + * + * Register a main callback function and an optional prepare callback function + * that are invoked when the vm starts or stops running. The main callback and + * the prepare callback are called in two separate phases: First all prepare + * callbacks are called and only then all main callbacks are called. As its + * name suggests, the prepare callback can be used to do some preparatory work + * before invoking the main callback. + * + * Returns: an entry to be freed using qemu_del_vm_change_state_handler() + */ +VMChangeStateEntry * +qemu_add_vm_change_state_handler_prio_full(VMChangeStateHandler *cb, + VMChangeStateHandler *prepare_cb, + void *opaque, int priority) { VMChangeStateEntry *e; VMChangeStateEntry *other; e = g_malloc0(sizeof(*e)); e->cb = cb; + e->prepare_cb = prepare_cb; e->opaque = opaque; e->priority = priority; @@ -325,10 +361,22 @@ void vm_state_notify(bool running, RunState state) trace_vm_state_notify(running, state, RunState_str(state)); if (running) { + QTAILQ_FOREACH_SAFE(e, &vm_change_state_head, entries, next) { + if (e->prepare_cb) { + e->prepare_cb(e->opaque, running, state); + } + } + QTAILQ_FOREACH_SAFE(e, &vm_change_state_head, entries, next) { e->cb(e->opaque, running, state); } } else { + QTAILQ_FOREACH_REVERSE_SAFE(e, &vm_change_state_head, entries, next) { + if (e->prepare_cb) { + e->prepare_cb(e->opaque, running, state); + } + } + QTAILQ_FOREACH_REVERSE_SAFE(e, &vm_change_state_head, entries, next) { e->cb(e->opaque, running, state); } @@ -337,6 +385,7 @@ void vm_state_notify(bool running, RunState state) static ShutdownCause reset_requested; static ShutdownCause shutdown_requested; +static int shutdown_exit_code = EXIT_SUCCESS; static int shutdown_signal; static pid_t shutdown_pid; static int powerdown_requested; @@ -440,14 +489,20 @@ void qemu_system_reset(ShutdownCause reason) cpu_synchronize_all_states(); if (mc && mc->reset) { - mc->reset(current_machine); + mc->reset(current_machine, reason); } else { - qemu_devices_reset(); + qemu_devices_reset(reason); } - if (reason && reason != SHUTDOWN_CAUSE_SUBSYSTEM_RESET) { + switch (reason) { + case SHUTDOWN_CAUSE_NONE: + case SHUTDOWN_CAUSE_SUBSYSTEM_RESET: + case SHUTDOWN_CAUSE_SNAPSHOT_LOAD: + break; + default: qapi_event_send_reset(shutdown_caused_by_guest(reason), reason); } cpu_synchronize_all_post_reset(); + vm_set_suspended(false); } /* @@ -478,17 +533,15 @@ void qemu_system_guest_panicked(GuestPanicInformation *info) */ if (panic_action == PANIC_ACTION_PAUSE || (panic_action == PANIC_ACTION_SHUTDOWN && shutdown_action == SHUTDOWN_ACTION_PAUSE)) { - qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, - !!info, info); + qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, info); vm_stop(RUN_STATE_GUEST_PANICKED); - } else if (panic_action == PANIC_ACTION_SHUTDOWN) { - qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_POWEROFF, - !!info, info); + } else if (panic_action == PANIC_ACTION_SHUTDOWN || + panic_action == PANIC_ACTION_EXIT_FAILURE) { + qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_POWEROFF, info); vm_stop(RUN_STATE_GUEST_PANICKED); qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_PANIC); } else { - qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_RUN, - !!info, info); + qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_RUN, info); } if (info) { @@ -515,13 +568,8 @@ 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); - } + qapi_event_send_guest_crashloaded(GUEST_PANIC_ACTION_RUN, info); + qapi_free_GuestPanicInformation(info); } void qemu_system_reset_request(ShutdownCause reason) @@ -618,6 +666,13 @@ void qemu_system_killed(int signal, pid_t pid) qemu_notify_event(); } +void qemu_system_shutdown_request_with_code(ShutdownCause reason, + int exit_code) +{ + shutdown_exit_code = exit_code; + qemu_system_shutdown_request(reason); +} + void qemu_system_shutdown_request(ShutdownCause reason) { trace_qemu_system_shutdown_request(reason); @@ -661,7 +716,7 @@ void qemu_system_debug_request(void) qemu_notify_event(); } -static bool main_loop_should_exit(void) +static bool main_loop_should_exit(int *status) { RunState r; ShutdownCause request; @@ -679,6 +734,12 @@ static bool main_loop_should_exit(void) if (shutdown_action == SHUTDOWN_ACTION_PAUSE) { vm_stop(RUN_STATE_SHUTDOWN); } else { + if (shutdown_exit_code != EXIT_SUCCESS) { + *status = shutdown_exit_code; + } else if (request == SHUTDOWN_CAUSE_GUEST_PANIC && + panic_action == PANIC_ACTION_EXIT_FAILURE) { + *status = EXIT_FAILURE; + } return true; } } @@ -714,20 +775,15 @@ static bool main_loop_should_exit(void) return false; } -void qemu_main_loop(void) +int qemu_main_loop(void) { -#ifdef CONFIG_PROFILER - int64_t ti; -#endif - while (!main_loop_should_exit()) { -#ifdef CONFIG_PROFILER - ti = profile_getclock(); -#endif + int status = EXIT_SUCCESS; + + while (!main_loop_should_exit(&status)) { main_loop_wait(false); -#ifdef CONFIG_PROFILER - dev_time += profile_getclock() - ti; -#endif } + + return status; } void qemu_add_exit_notifier(Notifier *notify) @@ -755,7 +811,7 @@ void qemu_init_subsystems(void) qemu_init_cpu_list(); qemu_init_cpu_loop(); - qemu_mutex_lock_iothread(); + bql_lock(); atexit(qemu_run_exit_notifiers); @@ -779,9 +835,9 @@ void qemu_init_subsystems(void) } -void qemu_cleanup(void) +void qemu_cleanup(int status) { - gdb_exit(0); + gdb_exit(status); /* * cleaning up the migration object cancels any existing migration @@ -797,21 +853,21 @@ void qemu_cleanup(void) */ blk_exp_close_all(); - /* - * We must cancel all block jobs while the block layer is drained, - * or cancelling will be affected by throttling and thus may block - * for an extended period of time. - * vm_shutdown() will bdrv_drain_all(), so we may as well include - * it in the drained section. - * We do not need to end this section, because we do not want any - * requests happening from here on anyway. - */ - bdrv_drain_all_begin(); /* No more vcpu or device emulation activity beyond this point */ vm_shutdown(); replay_finish(); + /* + * We must cancel all block jobs while the block layer is drained, + * or cancelling will be affected by throttling and thus may block + * for an extended period of time. + * Begin the drained section after vm_shutdown() to avoid requests being + * stuck in the BlockBackend's request queue. + * We do not need to end this section, because we do not want any + * requests happening from here on anyway. + */ + bdrv_drain_all_begin(); job_cancel_sync_all(); bdrv_close_all(); diff --git a/system/tpm-hmp-cmds.c b/system/tpm-hmp-cmds.c new file mode 100644 index 0000000000..9ed6ad6c4d --- /dev/null +++ b/system/tpm-hmp-cmds.c @@ -0,0 +1,65 @@ +/* + * HMP commands related to TPM + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qapi/qapi-commands-tpm.h" +#include "monitor/monitor.h" +#include "monitor/hmp.h" +#include "qapi/error.h" + +void hmp_info_tpm(Monitor *mon, const QDict *qdict) +{ +#ifdef CONFIG_TPM + TPMInfoList *info_list, *info; + Error *err = NULL; + unsigned int c = 0; + TPMPassthroughOptions *tpo; + TPMEmulatorOptions *teo; + + info_list = qmp_query_tpm(&err); + if (err) { + monitor_printf(mon, "TPM device not supported\n"); + error_free(err); + return; + } + + if (info_list) { + monitor_printf(mon, "TPM device:\n"); + } + + for (info = info_list; info; info = info->next) { + TPMInfo *ti = info->value; + monitor_printf(mon, " tpm%d: model=%s\n", + c, TpmModel_str(ti->model)); + + monitor_printf(mon, " \\ %s: type=%s", + ti->id, TpmType_str(ti->options->type)); + + switch (ti->options->type) { + case TPM_TYPE_PASSTHROUGH: + tpo = ti->options->u.passthrough.data; + monitor_printf(mon, "%s%s%s%s", + tpo->path ? ",path=" : "", + tpo->path ?: "", + tpo->cancel_path ? ",cancel-path=" : "", + tpo->cancel_path ?: ""); + break; + case TPM_TYPE_EMULATOR: + teo = ti->options->u.emulator.data; + monitor_printf(mon, ",chardev=%s", teo->chardev); + break; + case TPM_TYPE__MAX: + break; + } + monitor_printf(mon, "\n"); + c++; + } + qapi_free_TPMInfoList(info_list); +#else + monitor_printf(mon, "TPM device not supported\n"); +#endif /* CONFIG_TPM */ +} diff --git a/softmmu/tpm.c b/system/tpm.c similarity index 97% rename from softmmu/tpm.c rename to system/tpm.c index 578563f05a..7164ea7ff1 100644 --- a/softmmu/tpm.c +++ b/system/tpm.c @@ -175,15 +175,15 @@ int tpm_init(void) * Parse the TPM configuration options. * To display all available TPM backends the user may use '-tpmdev help' */ -int tpm_config_parse(QemuOptsList *opts_list, const char *optarg) +int tpm_config_parse(QemuOptsList *opts_list, const char *optstr) { QemuOpts *opts; - if (!strcmp(optarg, "help")) { + if (!strcmp(optstr, "help")) { tpm_display_backend_drivers(); return -1; } - opts = qemu_opts_parse_noisily(opts_list, optarg, true); + opts = qemu_opts_parse_noisily(opts_list, optstr, true); if (!opts) { return -1; } diff --git a/softmmu/trace-events b/system/trace-events similarity index 82% rename from softmmu/trace-events rename to system/trace-events index 9c88887b3c..69c9044151 100644 --- a/softmmu/trace-events +++ b/system/trace-events @@ -21,7 +21,7 @@ flatview_destroy(void *view, void *root) "%p (root %p)" flatview_destroy_rcu(void *view, void *root) "%p (root %p)" global_dirty_changed(unsigned int bitmask) "bitmask 0x%"PRIx32 -# softmmu.c +# cpus.c vm_stop_flush_all(int ret) "ret %d" # vl.c @@ -31,3 +31,10 @@ runstate_set(int current_state, const char *current_state_str, int new_state, co system_wakeup_request(int reason) "reason=%d" qemu_system_shutdown_request(int reason) "reason=%d" qemu_system_powerdown_request(void) "" + +#dirtylimit.c +dirtylimit_state_initialize(int max_cpus) "dirtylimit state initialize: max cpus %d" +dirtylimit_state_finalize(void) +dirtylimit_throttle_pct(int cpu_index, uint64_t pct, int64_t time_us) "CPU[%d] throttle percent: %" PRIu64 ", throttle adjust time %"PRIi64 " us" +dirtylimit_set_vcpu(int cpu_index, uint64_t quota) "CPU[%d] set dirty page rate limit %"PRIu64 +dirtylimit_vcpu_execute(int cpu_index, int64_t sleep_time_us) "CPU[%d] sleep %"PRIi64 " us" diff --git a/system/trace.h b/system/trace.h new file mode 100644 index 0000000000..cd0136dcdc --- /dev/null +++ b/system/trace.h @@ -0,0 +1 @@ +#include "trace/trace-system.h" diff --git a/softmmu/vl.c b/system/vl.c similarity index 89% rename from softmmu/vl.c rename to system/vl.c index 4c1e94b00e..c644222982 100644 --- a/softmmu/vl.c +++ b/system/vl.c @@ -49,11 +49,11 @@ #include "qemu/error-report.h" #include "qemu/sockets.h" #include "qemu/accel.h" +#include "qemu/async-teardown.h" #include "hw/usb.h" #include "hw/isa/isa.h" #include "hw/scsi/scsi.h" #include "hw/display/vga.h" -#include "sysemu/watchdog.h" #include "hw/firmware/smbios.h" #include "hw/acpi/acpi.h" #include "hw/xen/xen.h" @@ -87,17 +87,17 @@ #include "migration/colo.h" #include "migration/postcopy-ram.h" #include "sysemu/kvm.h" -#include "sysemu/hax.h" #include "qapi/qobject-input-visitor.h" #include "qemu/option.h" #include "qemu/config-file.h" -#include "qemu/qemu-options.h" #include "qemu/main-loop.h" -#include "hw/cxl/cxl.h" #ifdef CONFIG_VIRTFS #include "fsdev/qemu-fsdev.h" #endif #include "sysemu/qtest.h" +#ifdef CONFIG_TCG +#include "tcg/perf.h" +#endif #include "disas/disas.h" @@ -128,15 +128,12 @@ #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-visit-qom.h" #include "qapi/qapi-commands-ui.h" -#include "qapi/qmp/qdict.h" #include "block/qdict.h" #include "qapi/qmp/qerror.h" #include "sysemu/iothread.h" #include "qemu/guest-random.h" #include "qemu/keyval.h" -#include "config-host.h" - #define MAX_VIRTIO_CONSOLES 1 typedef struct BlockdevOptionsQueueEntry { @@ -147,12 +144,6 @@ typedef struct BlockdevOptionsQueueEntry { typedef QSIMPLEQ_HEAD(, BlockdevOptionsQueueEntry) BlockdevOptionsQueue; -typedef struct CXLFMWOptionQueueEntry { - CXLFixedMemoryWindowOptions *opts; - Location loc; - QSIMPLEQ_ENTRY(CXLFMWOptionQueueEntry) entry; -} CXLFMWOptionQueueEntry; - typedef struct ObjectOption { ObjectOptions *opts; QTAILQ_ENTRY(ObjectOption) next; @@ -179,8 +170,6 @@ static int snapshot; static bool preconfig_requested; static QemuPluginList plugin_list = QTAILQ_HEAD_INITIALIZER(plugin_list); static BlockdevOptionsQueue bdo_queue = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue); -static QSIMPLEQ_HEAD(, CXLFMWOptionQueueEntry) CXLFMW_opts = - QSIMPLEQ_HEAD_INITIALIZER(CXLFMW_opts); static bool nographic = false; static int mem_prealloc; /* force preallocation of physical target memory */ static const char *vga_model = NULL; @@ -190,11 +179,11 @@ static Chardev **serial_hds; static const char *log_mask; static const char *log_file; static bool list_data_dirs; -static const char *watchdog; static const char *qtest_chrdev; static const char *qtest_log; static int has_defaults = 1; +static int default_audio = 1; static int default_serial = 1; static int default_parallel = 1; static int default_monitor = 1; @@ -204,10 +193,11 @@ static int default_sdcard = 1; static int default_vga = 1; static int default_net = 1; -static struct { +static const struct { const char *driver; int *flag; } default_list[] = { + { .driver = "xen-console", .flag = &default_serial }, { .driver = "isa-serial", .flag = &default_serial }, { .driver = "isa-parallel", .flag = &default_parallel }, { .driver = "isa-fdc", .flag = &default_floppy }, @@ -226,6 +216,7 @@ static struct { { .driver = "ati-vga", .flag = &default_vga }, { .driver = "vhost-user-vga", .flag = &default_vga }, { .driver = "virtio-vga-gl", .flag = &default_vga }, + { .driver = "virtio-vga-rutabaga", .flag = &default_vga }, }; static QemuOptsList qemu_rtc_opts = { @@ -622,7 +613,7 @@ static int parse_add_fd(void *opaque, QemuOpts *opts, Error **errp) } /* add the duplicate fd, and optionally the opaque string, to the fd set */ - fdinfo = monitor_fdset_add_fd(dupfd, true, fdset_id, !!fd_opaque, fd_opaque, + fdinfo = monitor_fdset_add_fd(dupfd, true, fdset_id, fd_opaque, &error_abort); g_free(fdinfo); @@ -735,6 +726,12 @@ static QemuOptsList qemu_smp_opts = { { .name = "cpus", .type = QEMU_OPT_NUMBER, + }, { + .name = "drawers", + .type = QEMU_OPT_NUMBER, + }, { + .name = "books", + .type = QEMU_OPT_NUMBER, }, { .name = "sockets", .type = QEMU_OPT_NUMBER, @@ -758,6 +755,33 @@ static QemuOptsList qemu_smp_opts = { }, }; +#if defined(CONFIG_POSIX) +static QemuOptsList qemu_run_with_opts = { + .name = "run-with", + .head = QTAILQ_HEAD_INITIALIZER(qemu_run_with_opts.head), + .desc = { +#if defined(CONFIG_LINUX) + { + .name = "async-teardown", + .type = QEMU_OPT_BOOL, + }, +#endif + { + .name = "chroot", + .type = QEMU_OPT_STRING, + }, + { /* end of list */ } + }, +}; + +#define qemu_add_run_with_opts() qemu_add_opts(&qemu_run_with_opts) + +#else + +#define qemu_add_run_with_opts() + +#endif /* CONFIG_POSIX */ + static void realtime_init(void) { if (enable_mlock) { @@ -867,7 +891,7 @@ static void help(int exitcode) printf("\nDuring emulation, the following keys are useful:\n" "ctrl-alt-f toggle full screen\n" "ctrl-alt-n switch to virtual console 'n'\n" - "ctrl-alt toggle mouse and keyboard grab\n" + "ctrl-alt-g toggle mouse and keyboard grab\n" "\n" "When using -nographic, press 'ctrl-a h' to get some help.\n" "\n" @@ -876,6 +900,16 @@ static void help(int exitcode) exit(exitcode); } +enum { + +#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \ + opt_enum, +#define DEFHEADING(text) +#define ARCHHEADING(text, arch_mask) + +#include "qemu-options.def" +}; + #define HAS_ARG 0x0001 typedef struct QEMUOption { @@ -894,7 +928,7 @@ static const QEMUOption qemu_options[] = { #define ARCHHEADING(text, arch_mask) #include "qemu-options.def" - { NULL }, + { /* end of list */ } }; typedef struct VGAInterfaceInfo { @@ -967,7 +1001,18 @@ static const char * get_default_vga_model(const MachineClass *machine_class) { if (machine_class->default_display) { - return machine_class->default_display; + for (int t = 0; t < VGA_TYPE_MAX; t++) { + const VGAInterfaceInfo *ti = &vga_interfaces[t]; + + if (ti->opt_name && vga_interface_available(t) && + g_str_equal(ti->opt_name, machine_class->default_display)) { + return machine_class->default_display; + } + } + + warn_report_once("Default display '%s' is not available in this binary", + machine_class->default_display); + return NULL; } else if (vga_interface_available(VGA_CIRRUS)) { return "cirrus"; } else if (vga_interface_available(VGA_STD)) { @@ -990,7 +1035,8 @@ static void select_vgahw(const MachineClass *machine_class, const char *p) if (vga_interface_available(t) && ti->opt_name) { printf("%-20s %s%s\n", ti->opt_name, ti->name ?: "", - g_str_equal(ti->opt_name, def) ? " (default)" : ""); + (def && g_str_equal(ti->opt_name, def)) ? + " (default)" : ""); } } exit(0); @@ -1028,12 +1074,12 @@ static void select_vgahw(const MachineClass *machine_class, const char *p) } } -static void parse_display_qapi(const char *optarg) +static void parse_display_qapi(const char *str) { DisplayOptions *opts; Visitor *v; - v = qobject_input_visitor_new_str(optarg, "type", &error_fatal); + v = qobject_input_visitor_new_str(str, "type", &error_fatal); visit_type_DisplayOptions(v, NULL, &opts, &error_fatal); QAPI_CLONE_MEMBERS(DisplayOptions, &dpy, opts); @@ -1049,13 +1095,14 @@ DisplayOptions *qmp_query_display_options(Error **errp) static void parse_display(const char *p) { - const char *opts; - if (is_help_option(p)) { qemu_display_help(); exit(0); } +#ifdef CONFIG_VNC + const char *opts; + if (strstart(p, "vnc", &opts)) { /* * vnc isn't a (local) DisplayType but a protocol for remote @@ -1063,31 +1110,16 @@ static void parse_display(const char *p) */ if (*opts == '=') { vnc_parse(opts + 1); + display_remote++; } else { error_report("VNC requires a display argument vnc="); exit(1); } - } else { - parse_display_qapi(p); + return; } -} +#endif -static void parse_cxl_fixed_memory_window(const char *optarg) -{ - CXLFMWOptionQueueEntry *cfmws_entry; - Visitor *v; - - v = qobject_input_visitor_new_str(optarg, "cxl-fixed-memory-window", - &error_fatal); - cfmws_entry = g_new(CXLFMWOptionQueueEntry, 1); - visit_type_CXLFixedMemoryWindowOptions(v, NULL, &cfmws_entry->opts, - &error_fatal); - if (!cfmws_entry->opts) { - exit(1); - } - visit_free(v); - loc_save(&cfmws_entry->loc); - QSIMPLEQ_INSERT_TAIL(&CXLFMW_opts, cfmws_entry, entry); + parse_display_qapi(p); } static inline bool nonempty_str(const char *str) @@ -1201,21 +1233,21 @@ static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp) return monitor_init_opts(opts, errp); } -static void monitor_parse(const char *optarg, const char *mode, bool pretty) +static void monitor_parse(const char *str, const char *mode, bool pretty) { static int monitor_device_index = 0; QemuOpts *opts; const char *p; char label[32]; - if (strstart(optarg, "chardev:", &p)) { + if (strstart(str, "chardev:", &p)) { snprintf(label, sizeof(label), "%s", p); } else { snprintf(label, sizeof(label), "compat_monitor%d", monitor_device_index); - opts = qemu_chr_parse_compat(label, optarg, true); + opts = qemu_chr_parse_compat(label, str, true); if (!opts) { - error_report("parse error: %s", optarg); + error_report("parse error: %s", str); exit(1); } } @@ -1307,15 +1339,42 @@ static void qemu_disable_default_devices(void) default_sdcard = 0; } if (!has_defaults) { + default_audio = 0; default_monitor = 0; default_net = 0; default_vga = 0; + } else { + if (default_net && machine_class->default_nic && + !module_object_class_by_name(machine_class->default_nic)) { + warn_report("Default NIC '%s' is not available in this binary", + machine_class->default_nic); + default_net = 0; + } } } +static void qemu_setup_display(void) +{ + if (dpy.type == DISPLAY_TYPE_DEFAULT && !display_remote) { + if (!qemu_display_find_default(&dpy)) { + dpy.type = DISPLAY_TYPE_NONE; +#if defined(CONFIG_VNC) + vnc_parse("localhost:0,to=99,id=default"); + display_remote++; +#endif + } + } + if (dpy.type == DISPLAY_TYPE_DEFAULT) { + dpy.type = DISPLAY_TYPE_NONE; + } + + qemu_display_early_init(&dpy); +} + static void qemu_create_default_devices(void) { MachineClass *machine_class = MACHINE_GET_CLASS(current_machine); + const char *vc = qemu_display_get_vc(&dpy); if (is_daemonized()) { /* According to documentation and historically, -nographic redirects @@ -1335,23 +1394,29 @@ static void qemu_create_default_devices(void) } if (nographic) { - if (default_parallel) + if (default_parallel) { add_device_config(DEV_PARALLEL, "null"); + } if (default_serial && default_monitor) { add_device_config(DEV_SERIAL, "mon:stdio"); } else { - if (default_serial) + if (default_serial) { add_device_config(DEV_SERIAL, "stdio"); - if (default_monitor) + } + if (default_monitor) { monitor_parse("stdio", "readline", false); + } } } else { - if (default_serial) - add_device_config(DEV_SERIAL, "vc:80Cx24C"); - if (default_parallel) - add_device_config(DEV_PARALLEL, "vc:80Cx24C"); - if (default_monitor) - monitor_parse("vc:80Cx24C", "readline", false); + if (default_serial) { + add_device_config(DEV_SERIAL, vc ?: "null"); + } + if (default_parallel) { + add_device_config(DEV_PARALLEL, vc ?: "null"); + } + if (default_monitor && vc) { + monitor_parse(vc, "readline", false); + } } if (default_net) { @@ -1362,23 +1427,6 @@ static void qemu_create_default_devices(void) #endif } -#if defined(CONFIG_VNC) - if (!QTAILQ_EMPTY(&(qemu_find_opts("vnc")->head))) { - display_remote++; - } -#endif - if (dpy.type == DISPLAY_TYPE_DEFAULT && !display_remote) { - if (!qemu_display_find_default(&dpy)) { - dpy.type = DISPLAY_TYPE_NONE; -#if defined(CONFIG_VNC) - vnc_parse("localhost:0,to=99,id=default"); -#endif - } - } - if (dpy.type == DISPLAY_TYPE_DEFAULT) { - dpy.type = DISPLAY_TYPE_NONE; - } - /* If no default VGA is requested, the default is "none". */ if (default_vga) { vga_model = get_default_vga_model(machine_class); @@ -1391,18 +1439,22 @@ static void qemu_create_default_devices(void) static int serial_parse(const char *devname) { int index = num_serial_hds; - char label[32]; - if (strcmp(devname, "none") == 0) - return 0; - snprintf(label, sizeof(label), "serial%d", index); serial_hds = g_renew(Chardev *, serial_hds, index + 1); - serial_hds[index] = qemu_chr_new_mux_mon(label, devname, NULL); - if (!serial_hds[index]) { - error_report("could not connect serial device" - " to character backend '%s'", devname); - return -1; + if (strcmp(devname, "none") == 0) { + /* Don't allocate a serial device for this index */ + serial_hds[index] = NULL; + } else { + char label[32]; + snprintf(label, sizeof(label), "serial%d", index); + + serial_hds[index] = qemu_chr_new_mux_mon(label, devname, NULL); + if (!serial_hds[index]) { + error_report("could not connect serial device" + " to character backend '%s'", devname); + return -1; + } } num_serial_hds++; return 0; @@ -1493,7 +1545,8 @@ static gint machine_class_cmp(gconstpointer a, gconstpointer b) static void machine_help_func(const QDict *qdict) { - GSList *machines, *el; + g_autoptr(GSList) machines = NULL; + GSList *el; const char *type = qdict_get_try_str(qdict, "type"); machines = object_class_get_list(TYPE_MACHINE, false); @@ -1548,13 +1601,18 @@ machine_parse_property_opt(QemuOptsList *opts_list, const char *propname, } static const char *pid_file; -static Notifier qemu_unlink_pidfile_notifier; +struct UnlinkPidfileNotifier { + Notifier notifier; + char *pid_file_realpath; +}; +static struct UnlinkPidfileNotifier qemu_unlink_pidfile_notifier; static void qemu_unlink_pidfile(Notifier *n, void *data) { - if (pid_file) { - unlink(pid_file); - } + struct UnlinkPidfileNotifier *upn; + + upn = DO_UPCAST(struct UnlinkPidfileNotifier, notifier, n); + unlink(upn->pid_file_realpath); } static const QEMUOption *lookup_opt(int argc, char **argv, @@ -1599,13 +1657,13 @@ static const QEMUOption *lookup_opt(int argc, char **argv, static MachineClass *select_machine(QDict *qdict, Error **errp) { - const char *optarg = qdict_get_try_str(qdict, "type"); + const char *machine_type = qdict_get_try_str(qdict, "type"); GSList *machines = object_class_get_list(TYPE_MACHINE, false); MachineClass *machine_class; Error *local_err = NULL; - if (optarg) { - machine_class = find_machine(optarg, machines); + if (machine_type) { + machine_class = find_machine(machine_type, machines); qdict_del(qdict, "type"); if (!machine_class) { error_setg(&local_err, "unsupported machine type"); @@ -1749,20 +1807,20 @@ static void object_option_add_visitor(Visitor *v) QTAILQ_INSERT_TAIL(&object_opts, opt, next); } -static void object_option_parse(const char *optarg) +static void object_option_parse(const char *str) { QemuOpts *opts; const char *type; Visitor *v; - if (optarg[0] == '{') { - QObject *obj = qobject_from_json(optarg, &error_fatal); + if (str[0] == '{') { + QObject *obj = qobject_from_json(str, &error_fatal); v = qobject_input_visitor_new(obj); qobject_unref(obj); } else { opts = qemu_opts_parse_noisily(qemu_find_opts("object"), - optarg, true); + str, true); if (!opts) { exit(1); } @@ -1782,6 +1840,27 @@ static void object_option_parse(const char *optarg) visit_free(v); } +/* + * Very early object creation, before the sandbox options have been activated. + */ +static bool object_create_pre_sandbox(const char *type) +{ + /* + * Objects should in general not get initialized "too early" without + * a reason. If you add one, state the reason in a comment! + */ + + /* + * Reason: -sandbox on,resourcecontrol=deny disallows setting CPU + * affinity of threads. + */ + if (g_str_equal(type, "thread-context")) { + return true; + } + + return false; +} + /* * Initial object creation happens before all other * QEMU data types are created. The majority of objects @@ -1796,6 +1875,11 @@ static bool object_create_early(const char *type) * add one, state the reason in a comment! */ + /* Reason: already created. */ + if (object_create_pre_sandbox(type)) { + return false; + } + /* Reason: property "chardev" */ if (g_str_equal(type, "rng-egd") || g_str_equal(type, "qtest")) { @@ -1830,7 +1914,6 @@ static bool object_create_early(const char *type) * Allocation of large amounts of memory may delay * chardev initialization for too long, and trigger timeouts * on software that waits for a monitor socket to be created - * (e.g. libvirt). */ if (g_str_has_prefix(type, "memory-backend-")) { return false; @@ -1843,15 +1926,13 @@ static void qemu_apply_machine_options(QDict *qdict) { object_set_properties_from_keyval(OBJECT(current_machine), qdict, false, &error_fatal); - if (semihosting_enabled() && !semihosting_get_argc()) { + if (semihosting_enabled(false) && !semihosting_get_argc()) { /* fall back to the -kernel/-append */ semihosting_arg_fallback(current_machine->kernel_filename, current_machine->kernel_cmdline); } if (current_machine->smp.cpus > 1) { - Error *blocker = NULL; - error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED, "smp"); - replay_add_blocker(blocker); + replay_add_blocker("multiple CPUs"); } } @@ -1874,7 +1955,6 @@ static void qemu_create_early_backends(void) "ignoring option"); } - qemu_display_early_init(&dpy); qemu_console_early_init(); if (dpy.has_gl && dpy.gl != DISPLAYGL_MODE_OFF && display_opengl == 0) { @@ -1907,6 +1987,9 @@ static void qemu_create_early_backends(void) */ configure_blockdev(&bdo_queue, machine_class, snapshot); audio_init_audiodevs(); + if (default_audio) { + audio_create_default_audiodevs(); + } } @@ -1916,7 +1999,7 @@ static void qemu_create_early_backends(void) */ static bool object_create_late(const char *type) { - return !object_create_early(type); + return !object_create_early(type) && !object_create_pre_sandbox(type); } static void qemu_create_late_backends(void) @@ -1925,10 +2008,18 @@ static void qemu_create_late_backends(void) qtest_server_init(qtest_chrdev, qtest_log, &error_fatal); } - net_init_clients(&error_fatal); + net_init_clients(); object_option_foreach_add(object_create_late); + /* + * Wait for any outstanding memory prealloc from created memory + * backends to complete. + */ + if (!qemu_finish_async_prealloc_mem(&error_fatal)) { + exit(1); + } + if (tpm_init() < 0) { exit(1); } @@ -1944,22 +2035,7 @@ static void qemu_create_late_backends(void) exit(1); /* now chardevs have been created we may have semihosting to connect */ - qemu_semihosting_connect_chardevs(); - qemu_semihosting_console_init(); -} - -static void cxl_set_opts(void) -{ - while (!QSIMPLEQ_EMPTY(&CXLFMW_opts)) { - CXLFMWOptionQueueEntry *cfmws_entry = QSIMPLEQ_FIRST(&CXLFMW_opts); - - loc_restore(&cfmws_entry->loc); - QSIMPLEQ_REMOVE_HEAD(&CXLFMW_opts, entry); - cxl_fixed_memory_window_config(current_machine, cfmws_entry->opts, - &error_fatal); - qapi_free_CXLFixedMemoryWindowOptions(cfmws_entry->opts); - g_free(cfmws_entry); - } + qemu_semihosting_chardev_init(); } static void qemu_resolve_machine_memdev(void) @@ -1983,27 +2059,21 @@ static void qemu_resolve_machine_memdev(void) } } -static void parse_memory_options(const char *arg) +static void parse_memory_options(void) { - QemuOpts *opts; + QemuOpts *opts = qemu_find_opts_singleton("memory"); QDict *dict, *prop; const char *mem_str; + Location loc; - opts = qemu_opts_parse_noisily(qemu_find_opts("memory"), arg, true); - if (!opts) { - exit(EXIT_FAILURE); - } + loc_push_none(&loc); + qemu_opts_loc_restore(opts); prop = qdict_new(); if (qemu_opt_get_size(opts, "size", 0) != 0) { - mem_str = qemu_opt_get(opts, "size"); - if (!*mem_str) { - error_report("missing 'size' option value"); - exit(EXIT_FAILURE); - } - /* Fix up legacy suffix-less format */ + mem_str = qemu_opt_get(opts, "size"); if (g_ascii_isdigit(mem_str[strlen(mem_str) - 1])) { g_autofree char *mib_str = g_strdup_printf("%sM", mem_str); qdict_put_str(prop, "size", mib_str); @@ -2023,6 +2093,7 @@ static void parse_memory_options(const char *arg) qdict_put(dict, "memory", prop); keyval_merge(machine_opts_dict, dict, &error_fatal); qobject_unref(dict); + loc_pop(&loc); } static void qemu_create_machine(QDict *qdict) @@ -2047,7 +2118,6 @@ static void qemu_create_machine(QDict *qdict) } cpu_exec_init_all(); - page_size_init(); if (machine_class->hw_version) { qemu_set_hw_version(machine_class->hw_version); @@ -2087,10 +2157,10 @@ static int global_init_func(void *opaque, QemuOpts *opts, Error **errp) static bool is_qemuopts_group(const char *group) { if (g_str_equal(group, "object") || + g_str_equal(group, "audiodev") || g_str_equal(group, "machine") || g_str_equal(group, "smp-opts") || - g_str_equal(group, "boot-opts") || - g_str_equal(group, "memory")) { + g_str_equal(group, "boot-opts")) { return false; } return true; @@ -2103,6 +2173,15 @@ static void qemu_record_config_group(const char *group, QDict *dict, Visitor *v = qobject_input_visitor_new_keyval(QOBJECT(dict)); object_option_add_visitor(v); visit_free(v); + + } else if (g_str_equal(group, "audiodev")) { + Audiodev *dev = NULL; + Visitor *v = qobject_input_visitor_new_keyval(QOBJECT(dict)); + if (visit_type_Audiodev(v, NULL, &dev, errp)) { + audio_define(dev); + } + visit_free(v); + } else if (g_str_equal(group, "machine")) { /* * Cannot merge string-valued and type-safe dictionaries, so JSON @@ -2114,8 +2193,6 @@ static void qemu_record_config_group(const char *group, QDict *dict, machine_merge_property("smp", dict, &error_fatal); } else if (g_str_equal(group, "boot-opts")) { machine_merge_property("boot", dict, &error_fatal); - } else if (g_str_equal(group, "memory")) { - machine_merge_property("memory", dict, &error_fatal); } else { abort(); } @@ -2202,8 +2279,7 @@ static void user_register_global_props(void) static int do_configure_icount(void *opaque, QemuOpts *opts, Error **errp) { - icount_configure(opts, errp); - return 0; + return !icount_configure(opts, errp); } static int accelerator_set_property(void *opaque, @@ -2222,14 +2298,18 @@ static int do_configure_accelerator(void *opaque, QemuOpts *opts, Error **errp) int ret; bool qtest_with_kvm; + if (!acc) { + error_setg(errp, QERR_MISSING_PARAMETER, "accel"); + goto bad; + } + qtest_with_kvm = g_str_equal(acc, "kvm") && qtest_chrdev != NULL; if (!ac) { - *p_init_failed = true; if (!qtest_with_kvm) { error_report("invalid accelerator %s", acc); } - return 0; + goto bad; } accel = ACCEL(object_new_with_class(OBJECT_CLASS(ac))); object_apply_compat_props(OBJECT(accel)); @@ -2239,14 +2319,17 @@ static int do_configure_accelerator(void *opaque, QemuOpts *opts, Error **errp) ret = accel_init_machine(accel, current_machine); if (ret < 0) { - *p_init_failed = true; if (!qtest_with_kvm || ret != -ENOENT) { error_report("failed to initialize %s: %s", acc, strerror(-ret)); } - return 0; + goto bad; } return 1; + +bad: + *p_init_failed = true; + return 0; } static void configure_accelerators(const char *progname) @@ -2312,8 +2395,7 @@ static void configure_accelerators(const char *progname) } if (init_failed && !qtest_chrdev) { - AccelClass *ac = ACCEL_GET_CLASS(current_accel()); - error_report("falling back to %s", ac->name); + error_report("falling back to %s", current_accel_name()); } if (icount_enabled() && !tcg_enabled()) { @@ -2340,6 +2422,10 @@ static void qemu_validate_options(const QDict *machine_opts) } } + if (loadvm && incoming) { + error_report("'incoming' and 'loadvm' options are mutually exclusive"); + exit(EXIT_FAILURE); + } if (loadvm && preconfig_requested) { error_report("'preconfig' and 'loadvm' options are " "mutually exclusive"); @@ -2372,12 +2458,6 @@ static void qemu_process_sugar_options(void) } object_register_sugar_prop("memory-backend", "prealloc", "on", false); } - - if (watchdog) { - int i = select_watchdog(watchdog); - if (i > 0) - exit (i == 1 ? 1 : 0); - } } /* -action processing */ @@ -2402,6 +2482,11 @@ static int process_runstate_actions(void *opaque, QemuOpts *opts, Error **errp) static void qemu_process_early_options(void) { + qemu_opts_foreach(qemu_find_opts("name"), + parse_name, NULL, &error_fatal); + + object_option_foreach_add(object_create_pre_sandbox); + #ifdef CONFIG_SECCOMP QemuOptsList *olist = qemu_find_opts_err("sandbox", NULL); if (olist) { @@ -2409,9 +2494,6 @@ static void qemu_process_early_options(void) } #endif - qemu_opts_foreach(qemu_find_opts("name"), - parse_name, NULL, &error_fatal); - if (qemu_opts_foreach(qemu_find_opts("action"), process_runstate_actions, NULL, &error_fatal)) { exit(1); @@ -2450,7 +2532,7 @@ static void qemu_process_help_options(void) * to say '-cpu help -machine something'. */ if (cpu_option && is_help_option(cpu_option)) { - list_cpus(cpu_option); + list_cpus(); exit(0); } @@ -2473,13 +2555,31 @@ static void qemu_maybe_daemonize(const char *pid_file) os_daemonize(); rcu_disable_atfork(); - if (pid_file && !qemu_write_pidfile(pid_file, &err)) { - error_reportf_err(err, "cannot create PID file: "); - exit(1); - } + if (pid_file) { + char *pid_file_realpath = NULL; - qemu_unlink_pidfile_notifier.notify = qemu_unlink_pidfile; - qemu_add_exit_notifier(&qemu_unlink_pidfile_notifier); + if (!qemu_write_pidfile(pid_file, &err)) { + error_reportf_err(err, "cannot create PID file: "); + exit(1); + } + + pid_file_realpath = g_malloc0(PATH_MAX); + if (!realpath(pid_file, pid_file_realpath)) { + if (errno != ENOENT) { + warn_report("not removing PID file on exit: cannot resolve PID " + "file path: %s: %s", pid_file, strerror(errno)); + } + return; + } + + qemu_unlink_pidfile_notifier = (struct UnlinkPidfileNotifier) { + .notifier = { + .notify = qemu_unlink_pidfile, + }, + .pid_file_realpath = pid_file_realpath, + }; + qemu_add_exit_notifier(&qemu_unlink_pidfile_notifier.notifier); + } } static void qemu_init_displays(void) @@ -2515,11 +2615,6 @@ static void qemu_init_board(void) drive_check_orphaned(); realtime_init(); - - if (hax_enabled()) { - /* FIXME: why isn't cpu_synchronize_all_post_init enough? */ - hax_sync_vcpus(); - } } static void qemu_create_cli_devices(void) @@ -2558,7 +2653,7 @@ static void qemu_create_cli_devices(void) rom_reset_order_override(); } -static void qemu_machine_creation_done(void) +static bool qemu_machine_creation_done(Error **errp) { MachineState *machine = MACHINE(qdev_get_machine()); @@ -2581,15 +2676,15 @@ static void qemu_machine_creation_done(void) qdev_machine_creation_done(); - if (machine->cgs) { - /* - * Verify that Confidential Guest Support has actually been initialized - */ - assert(machine->cgs->ready); + if (machine->cgs && !machine->cgs->ready) { + error_setg(errp, "accelerator does not support confidential guest %s", + object_get_typename(OBJECT(machine->cgs))); + exit(1); } if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) { - exit(1); + error_setg(errp, "could not start gdbserver"); + return false; } if (!vga_interface_created && !default_vga && vga_interface_type != VGA_NONE) { @@ -2597,6 +2692,7 @@ static void qemu_machine_creation_done(void) "type does not use that option; " "No VGA device has been created"); } + return true; } void qmp_x_exit_preconfig(Error **errp) @@ -2608,11 +2704,14 @@ void qmp_x_exit_preconfig(Error **errp) qemu_init_board(); qemu_create_cli_devices(); - cxl_fixed_memory_window_link_targets(errp); - qemu_machine_creation_done(); + if (!qemu_machine_creation_done(errp)) { + return; + } if (loadvm) { + RunState state = autostart ? RUN_STATE_RUNNING : runstate_get(); load_snapshot(loadvm, NULL, false, NULL, &error_fatal); + load_snapshot_resume(state); } if (replay_mode != REPLAY_MODE_NONE) { replay_vmstate_init(); @@ -2621,7 +2720,7 @@ void qmp_x_exit_preconfig(Error **errp) if (incoming) { Error *local_err = NULL; if (strcmp(incoming, "defer") != 0) { - qmp_migrate_incoming(incoming, &local_err); + qmp_migrate_incoming(incoming, false, NULL, &local_err); if (local_err) { error_reportf_err(local_err, "-incoming %s: ", incoming); exit(1); @@ -2632,7 +2731,7 @@ void qmp_x_exit_preconfig(Error **errp) } } -void qemu_init(int argc, char **argv, char **envp) +void qemu_init(int argc, char **argv) { QemuOpts *opts; QemuOpts *icount_opts = NULL, *accel_opts = NULL; @@ -2674,11 +2773,14 @@ void qemu_init(int argc, char **argv, char **envp) qemu_add_opts(&qemu_semihosting_config_opts); qemu_add_opts(&qemu_fw_cfg_opts); qemu_add_opts(&qemu_action_opts); + qemu_add_run_with_opts(); module_call_init(MODULE_INIT_OPTS); error_init(argv[0]); qemu_init_exec_dir(argv[0]); + os_setup_limits(); + qemu_init_arch_modules(); qemu_init_subsystems(); @@ -2774,13 +2876,8 @@ void qemu_init(int argc, char **argv, char **envp) drive_add(IF_PFLASH, -1, optarg, PFLASH_OPTS); break; case QEMU_OPTION_snapshot: - { - Error *blocker = NULL; - snapshot = 1; - error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED, - "-snapshot"); - replay_add_blocker(blocker); - } + snapshot = 1; + replay_add_blocker("-snapshot"); break; case QEMU_OPTION_numa: opts = qemu_opts_parse_noisily(qemu_find_opts("numa"), @@ -2789,9 +2886,6 @@ void qemu_init(int argc, char **argv, char **envp) exit(1); } break; - case QEMU_OPTION_cxl_fixed_memory_window: - parse_cxl_fixed_memory_window(optarg); - break; case QEMU_OPTION_display: parse_display(optarg); break; @@ -2835,25 +2929,23 @@ void qemu_init(int argc, char **argv, char **envp) optarg, FD_OPTS); break; case QEMU_OPTION_no_fd_bootchk: - fd_bootchk = 0; + qdict_put_str(machine_opts_dict, "fd-bootchk", "off"); break; case QEMU_OPTION_netdev: default_net = 0; - if (net_client_parse(qemu_find_opts("netdev"), optarg) == -1) { - exit(1); + if (netdev_is_modern(optarg)) { + netdev_parse_modern(optarg); + } else { + net_client_parse(qemu_find_opts("netdev"), optarg); } break; case QEMU_OPTION_nic: default_net = 0; - if (net_client_parse(qemu_find_opts("nic"), optarg) == -1) { - exit(1); - } + net_client_parse(qemu_find_opts("nic"), optarg); break; case QEMU_OPTION_net: default_net = 0; - if (net_client_parse(qemu_find_opts("net"), optarg) == -1) { - exit(1); - } + net_client_parse(qemu_find_opts("net"), optarg); break; #ifdef CONFIG_LIBISCSI case QEMU_OPTION_iscsi: @@ -2864,38 +2956,44 @@ void qemu_init(int argc, char **argv, char **envp) } break; #endif - case QEMU_OPTION_audio_help: - audio_legacy_help(); - exit (0); - break; case QEMU_OPTION_audiodev: + default_audio = 0; audio_parse_option(optarg); break; case QEMU_OPTION_audio: { - QDict *dict = keyval_parse(optarg, "driver", NULL, &error_fatal); - char *model; + bool help; + char *model = NULL; Audiodev *dev = NULL; Visitor *v; - + QDict *dict = keyval_parse(optarg, "driver", &help, &error_fatal); + default_audio = 0; + if (help || (qdict_haskey(dict, "driver") && + is_help_option(qdict_get_str(dict, "driver")))) { + audio_help(); + exit(EXIT_SUCCESS); + } if (!qdict_haskey(dict, "id")) { qdict_put_str(dict, "id", "audiodev0"); } - if (!qdict_haskey(dict, "model")) { - error_setg(&error_fatal, "Parameter 'model' is missing"); - } - model = g_strdup(qdict_get_str(dict, "model")); - qdict_del(dict, "model"); - if (is_help_option(model)) { - show_valid_soundhw(); - exit(0); + if (qdict_haskey(dict, "model")) { + model = g_strdup(qdict_get_str(dict, "model")); + qdict_del(dict, "model"); + if (is_help_option(model)) { + show_valid_soundhw(); + exit(0); + } } v = qobject_input_visitor_new_keyval(QOBJECT(dict)); qobject_unref(dict); visit_type_Audiodev(v, NULL, &dev, &error_fatal); visit_free(v); - audio_define(dev); - select_soundhw(model, dev->id); - g_free(model); + if (model) { + audio_define(dev); + select_soundhw(model, dev->id); + g_free(model); + } else { + audio_define_default(dev, &error_fatal); + } break; } case QEMU_OPTION_h: @@ -2906,7 +3004,10 @@ void qemu_init(int argc, char **argv, char **envp) exit(0); break; case QEMU_OPTION_m: - parse_memory_options(optarg); + opts = qemu_opts_parse_noisily(qemu_find_opts("memory"), optarg, true); + if (opts == NULL) { + exit(1); + } break; #ifdef CONFIG_TPM case QEMU_OPTION_tpmdev: @@ -2930,6 +3031,14 @@ void qemu_init(int argc, char **argv, char **envp) case QEMU_OPTION_DFILTER: qemu_set_dfilter_ranges(optarg, &error_fatal); break; +#if defined(CONFIG_TCG) && defined(CONFIG_LINUX) + case QEMU_OPTION_perfmap: + perf_enable_perfmap(); + break; + case QEMU_OPTION_jitdump: + perf_enable_jitdump(); + break; +#endif case QEMU_OPTION_seed: qemu_guest_random_seed_main(optarg, &error_fatal); break; @@ -2949,9 +3058,6 @@ void qemu_init(int argc, char **argv, char **envp) case QEMU_OPTION_bios: qdict_put_str(machine_opts_dict, "firmware", optarg); break; - case QEMU_OPTION_singlestep: - singlestep = 1; - break; case QEMU_OPTION_S: autostart = 0; break; @@ -3129,14 +3235,6 @@ void qemu_init(int argc, char **argv, char **envp) default_monitor = 0; } break; - case QEMU_OPTION_watchdog: - if (watchdog) { - error_report("only one watchdog option may be given"); - exit(1); - } - warn_report("-watchdog is deprecated; use -device instead."); - watchdog = optarg; - break; case QEMU_OPTION_action: olist = qemu_find_opts("action"); if (!qemu_opts_parse_noisily(olist, optarg, false)) { @@ -3144,7 +3242,6 @@ void qemu_init(int argc, char **argv, char **envp) } break; case QEMU_OPTION_watchdog_action: { - QemuOpts *opts; opts = qemu_opts_create(qemu_find_opts("action"), NULL, 0, &error_abort); qemu_opt_set(opts, "watchdog", optarg, &error_abort); break; @@ -3170,7 +3267,7 @@ void qemu_init(int argc, char **argv, char **envp) pid_file = optarg; break; case QEMU_OPTION_win2k_hack: - win2k_install_hack = 1; + object_register_sugar_prop("ide-device", "win2k-install-hack", "true", true); break; case QEMU_OPTION_acpitable: opts = qemu_opts_parse_noisily(qemu_find_opts("acpi"), @@ -3264,15 +3361,12 @@ void qemu_init(int argc, char **argv, char **envp) machine_parse_property_opt(qemu_find_opts("smp-opts"), "smp", optarg); break; +#ifdef CONFIG_VNC case QEMU_OPTION_vnc: vnc_parse(optarg); + display_remote++; break; - case QEMU_OPTION_no_acpi: - qdict_put_str(machine_opts_dict, "acpi", "off"); - break; - case QEMU_OPTION_no_hpet: - qdict_put_str(machine_opts_dict, "hpet", "off"); - break; +#endif case QEMU_OPTION_no_reboot: olist = qemu_find_opts("action"); qemu_opts_parse_noisily(olist, "reboot=shutdown", false); @@ -3362,7 +3456,7 @@ void qemu_init(int argc, char **argv, char **envp) has_defaults = 0; break; case QEMU_OPTION_xen_domid: - if (!(accel_find("xen"))) { + if (!(accel_find("xen")) && !(accel_find("kvm"))) { error_report("Option not supported for this target"); exit(1); } @@ -3393,12 +3487,7 @@ void qemu_init(int argc, char **argv, char **envp) break; #ifdef CONFIG_SPICE case QEMU_OPTION_spice: - olist = qemu_find_opts_err("spice", NULL); - if (!olist) { - error_report("spice support is disabled"); - exit(1); - } - opts = qemu_opts_parse_noisily(olist, optarg, false); + opts = qemu_opts_parse_noisily(qemu_find_opts("spice"), optarg, false); if (!opts) { exit(1); } @@ -3453,16 +3542,16 @@ void qemu_init(int argc, char **argv, char **envp) break; case QEMU_OPTION_compat: { - CompatPolicy *opts; + CompatPolicy *opts_policy; Visitor *v; v = qobject_input_visitor_new_str(optarg, NULL, &error_fatal); - visit_type_CompatPolicy(v, NULL, &opts, &error_fatal); - QAPI_CLONE_MEMBERS(CompatPolicy, &compat_policy, opts); + visit_type_CompatPolicy(v, NULL, &opts_policy, &error_fatal); + QAPI_CLONE_MEMBERS(CompatPolicy, &compat_policy, opts_policy); - qapi_free_CompatPolicy(opts); + qapi_free_CompatPolicy(opts_policy); visit_free(v); break; } @@ -3492,11 +3581,41 @@ void qemu_init(int argc, char **argv, char **envp) case QEMU_OPTION_nouserconfig: /* Nothing to be parsed here. Especially, do not error out below. */ break; - default: - if (os_parse_cmd_args(popt->index, optarg)) { - error_report("Option not supported in this build"); +#if defined(CONFIG_POSIX) + case QEMU_OPTION_runas: + if (!os_set_runas(optarg)) { + error_report("User \"%s\" doesn't exist" + " (and is not :)", + optarg); exit(1); } + break; + case QEMU_OPTION_daemonize: + os_set_daemonize(true); + break; + case QEMU_OPTION_run_with: { + const char *str; + opts = qemu_opts_parse_noisily(qemu_find_opts("run-with"), + optarg, false); + if (!opts) { + exit(1); + } +#if defined(CONFIG_LINUX) + if (qemu_opt_get_bool(opts, "async-teardown", false)) { + init_async_teardown(); + } +#endif + str = qemu_opt_get(opts, "chroot"); + if (str) { + os_set_chroot(str); + } + break; + } +#endif /* CONFIG_POSIX */ + + default: + error_report("Option not supported in this build"); + exit(1); } } } @@ -3539,11 +3658,15 @@ void qemu_init(int argc, char **argv, char **envp) configure_rtc(qemu_find_opts_singleton("rtc")); + /* Transfer QemuOpts options into machine options */ + parse_memory_options(); + qemu_create_machine(machine_opts_dict); suspend_mux_open(); qemu_disable_default_devices(); + qemu_setup_display(); qemu_create_default_devices(); qemu_create_early_backends(); @@ -3581,16 +3704,22 @@ void qemu_init(int argc, char **argv, char **envp) machine_class->name, machine_class->deprecation_reason); } + /* + * Create backends before creating migration objects, so that it can + * check against compatibilities on the backend memories (e.g. postcopy + * over memory-backend-file objects). + */ + qemu_create_late_backends(); + phase_advance(PHASE_LATE_BACKENDS_CREATED); + /* * Note: creates a QOM object, must run only after global and * compat properties have been set up. */ migration_object_init(); - qemu_create_late_backends(); - /* parse features once if machine provides default cpu_type */ - current_machine->cpu_type = machine_class->default_cpu_type; + current_machine->cpu_type = machine_class_default_cpu_type(machine_class); if (cpu_option) { current_machine->cpu_type = parse_cpu_option(cpu_option); } @@ -3598,7 +3727,6 @@ void qemu_init(int argc, char **argv, char **envp) qemu_resolve_machine_memdev(); parse_numa_opts(current_machine); - cxl_set_opts(); if (vmstate_dump_file) { /* dump and exit */ diff --git a/system/watchpoint.c b/system/watchpoint.c new file mode 100644 index 0000000000..2aa2a9ea63 --- /dev/null +++ b/system/watchpoint.c @@ -0,0 +1,100 @@ +/* + * CPU watchpoints + * + * 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.1 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/error-report.h" +#include "exec/exec-all.h" +#include "hw/core/cpu.h" + +/* Add a watchpoint. */ +int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, + int flags, CPUWatchpoint **watchpoint) +{ + CPUWatchpoint *wp; + vaddr in_page; + + /* forbid ranges which are empty or run off the end of the address space */ + if (len == 0 || (addr + len - 1) < addr) { + error_report("tried to set invalid watchpoint at %" + VADDR_PRIx ", len=%" VADDR_PRIu, addr, len); + return -EINVAL; + } + wp = g_malloc(sizeof(*wp)); + + wp->vaddr = addr; + wp->len = len; + wp->flags = flags; + + /* keep all GDB-injected watchpoints in front */ + if (flags & BP_GDB) { + QTAILQ_INSERT_HEAD(&cpu->watchpoints, wp, entry); + } else { + QTAILQ_INSERT_TAIL(&cpu->watchpoints, wp, entry); + } + + in_page = -(addr | TARGET_PAGE_MASK); + if (len <= in_page) { + tlb_flush_page(cpu, addr); + } else { + tlb_flush(cpu); + } + + if (watchpoint) { + *watchpoint = wp; + } + return 0; +} + +/* Remove a specific watchpoint. */ +int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, vaddr len, + int flags) +{ + CPUWatchpoint *wp; + + QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { + if (addr == wp->vaddr && len == wp->len + && flags == (wp->flags & ~BP_WATCHPOINT_HIT)) { + cpu_watchpoint_remove_by_ref(cpu, wp); + return 0; + } + } + return -ENOENT; +} + +/* Remove a specific watchpoint by reference. */ +void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint) +{ + QTAILQ_REMOVE(&cpu->watchpoints, watchpoint, entry); + + tlb_flush_page(cpu, watchpoint->vaddr); + + g_free(watchpoint); +} + +/* Remove all matching watchpoints. */ +void cpu_watchpoint_remove_all(CPUState *cpu, int mask) +{ + CPUWatchpoint *wp, *next; + + QTAILQ_FOREACH_SAFE(wp, &cpu->watchpoints, entry, next) { + if (wp->flags & mask) { + cpu_watchpoint_remove_by_ref(cpu, wp); + } + } +} diff --git a/target/alpha/STATUS b/target/alpha/STATUS deleted file mode 100644 index 6c9744569e..0000000000 --- a/target/alpha/STATUS +++ /dev/null @@ -1,28 +0,0 @@ -(to be completed) - -Alpha emulation structure: -cpu.h : CPU definitions globally exported -exec.h : CPU definitions used only for translated code execution -helper.c : helpers that can be called either by the translated code - or the QEMU core, including the exception handler. -op_helper.c : helpers that can be called only from TCG -helper.h : TCG helpers prototypes -translate.c : Alpha instructions to micro-operations translator - -Code translator status: -The Alpha CPU instruction emulation should be quite complete with the -limitation that the VAX floating-point load and stores are not tested. -The 4 MMU modes are implemented. - -Linux user mode emulation status: -a few programs start to run. Most crash at a certain point, dereferencing a -NULL pointer. It seems that the UNIQUE register is not initialized properly. -It may appear that old executables, not relying on TLS support, run but -this is to be proved... - -Full system emulation status: -* Alpha PALCode emulation is in a very early stage and is not sufficient - to run any real OS. The alpha-softmmu target is not enabled for now. -* no hardware platform description is implemented -* there might be problems in the Alpha PALCode dedicated instructions - that would prevent to use a native PALCode image. diff --git a/target/alpha/clk_helper.c b/target/alpha/clk_helper.c new file mode 100644 index 0000000000..26ffc231cd --- /dev/null +++ b/target/alpha/clk_helper.c @@ -0,0 +1,32 @@ +/* + * QEMU Alpha clock helpers. + * + * Copyright (c) 2007 Jocelyn Mayer + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "exec/helper-proto.h" +#include "cpu.h" + +uint64_t helper_load_pcc(CPUAlphaState *env) +{ +#ifndef CONFIG_USER_ONLY + /* + * In system mode we have access to a decent high-resolution clock. + * In order to make OS-level time accounting work with the RPCC, + * present it with a well-timed clock fixed at 250MHz. + */ + return (((uint64_t)env->pcc_ofs << 32) + | (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) >> 2)); +#else + /* + * In user-mode, QEMU_CLOCK_VIRTUAL doesn't exist. Just pass through + * the host cpu clock ticks. Also, don't bother taking PCC_OFS into + * account. + */ + return (uint32_t)cpu_get_host_ticks(); +#endif +} diff --git a/target/alpha/cpu-param.h b/target/alpha/cpu-param.h index 17cd14e590..c969cb016b 100644 --- a/target/alpha/cpu-param.h +++ b/target/alpha/cpu-param.h @@ -9,12 +9,22 @@ #define ALPHA_CPU_PARAM_H #define TARGET_LONG_BITS 64 -#define TARGET_PAGE_BITS 13 /* ??? EV4 has 34 phys addr bits, EV5 has 40, EV6 has 44. */ #define TARGET_PHYS_ADDR_SPACE_BITS 44 -#define TARGET_VIRT_ADDR_SPACE_BITS (30 + TARGET_PAGE_BITS) -#define NB_MMU_MODES 3 +#ifdef CONFIG_USER_ONLY +/* + * Allow user-only to vary page size. Real hardware allows only 8k and 64k, + * but since any variance means guests cannot assume a fixed value, allow + * a 4k minimum to match x86 host, which can minimize emulation issues. + */ +# define TARGET_PAGE_BITS_VARY +# define TARGET_PAGE_BITS_MIN 12 +# define TARGET_VIRT_ADDR_SPACE_BITS 63 +#else +# define TARGET_PAGE_BITS 13 +# define TARGET_VIRT_ADDR_SPACE_BITS (30 + TARGET_PAGE_BITS) +#endif #endif diff --git a/target/alpha/cpu-qom.h b/target/alpha/cpu-qom.h index 1f200724b6..1b32b18d34 100644 --- a/target/alpha/cpu-qom.h +++ b/target/alpha/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU Alpha CPU + * QEMU Alpha CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -21,27 +21,12 @@ #define QEMU_ALPHA_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_ALPHA_CPU "alpha-cpu" OBJECT_DECLARE_CPU_TYPE(AlphaCPU, AlphaCPUClass, ALPHA_CPU) -/** - * AlphaCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * - * An Alpha CPU model. - */ -struct AlphaCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceReset parent_reset; -}; - +#define ALPHA_CPU_TYPE_SUFFIX "-" TYPE_ALPHA_CPU +#define ALPHA_CPU_TYPE_NAME(model) model ALPHA_CPU_TYPE_SUFFIX #endif diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index a8990d401b..05f9ee41e9 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -33,6 +33,22 @@ static void alpha_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.pc = value; } +static vaddr alpha_cpu_get_pc(CPUState *cs) +{ + AlphaCPU *cpu = ALPHA_CPU(cs); + + return cpu->env.pc; +} + +static void alpha_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + AlphaCPU *cpu = ALPHA_CPU(cs); + + cpu->env.pc = data[0]; +} + static bool alpha_cpu_has_work(CPUState *cs) { /* Here we are checking to see if the CPU should wake up from HALT. @@ -48,6 +64,11 @@ static bool alpha_cpu_has_work(CPUState *cs) | CPU_INTERRUPT_MCHK); } +static int alpha_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return alpha_env_mmu_index(cpu_env(cs)); +} + static void alpha_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) { info->mach = bfd_mach_alpha_ev6; @@ -71,23 +92,6 @@ static void alpha_cpu_realizefn(DeviceState *dev, Error **errp) acc->parent_realize(dev, errp); } -static void alpha_cpu_list_entry(gpointer data, gpointer user_data) -{ - ObjectClass *oc = data; - - qemu_printf(" %s\n", object_class_get_name(oc)); -} - -void alpha_cpu_list(void) -{ - GSList *list; - - list = object_class_get_list_sorted(TYPE_ALPHA_CPU, false); - qemu_printf("Available CPUs:\n"); - g_slist_foreach(list, alpha_cpu_list_entry, NULL); - g_slist_free(list); -} - /* Models */ typedef struct AlphaCPUAlias { const char *alias; @@ -110,8 +114,7 @@ static ObjectClass *alpha_cpu_class_by_name(const char *cpu_model) int i; oc = object_class_by_name(cpu_model); - if (oc != NULL && object_class_dynamic_cast(oc, TYPE_ALPHA_CPU) != NULL && - !object_class_is_abstract(oc)) { + if (oc != NULL && object_class_dynamic_cast(oc, TYPE_ALPHA_CPU) != NULL) { return oc; } @@ -126,55 +129,33 @@ static ObjectClass *alpha_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf(ALPHA_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; - } - - /* TODO: remove match everything nonsense */ - /* Default to ev67; no reason not to emulate insns by default. */ - if (!oc) { - oc = object_class_by_name(ALPHA_CPU_TYPE_NAME("ev67")); - } return oc; } static void ev4_cpu_initfn(Object *obj) { - AlphaCPU *cpu = ALPHA_CPU(obj); - CPUAlphaState *env = &cpu->env; - - env->implver = IMPLVER_2106x; + cpu_env(CPU(obj))->implver = IMPLVER_2106x; } static void ev5_cpu_initfn(Object *obj) { - AlphaCPU *cpu = ALPHA_CPU(obj); - CPUAlphaState *env = &cpu->env; - - env->implver = IMPLVER_21164; + cpu_env(CPU(obj))->implver = IMPLVER_21164; } static void ev56_cpu_initfn(Object *obj) { - AlphaCPU *cpu = ALPHA_CPU(obj); - CPUAlphaState *env = &cpu->env; - - env->amask |= AMASK_BWX; + cpu_env(CPU(obj))->amask |= AMASK_BWX; } static void pca56_cpu_initfn(Object *obj) { - AlphaCPU *cpu = ALPHA_CPU(obj); - CPUAlphaState *env = &cpu->env; - - env->amask |= AMASK_MVI; + cpu_env(CPU(obj))->amask |= AMASK_MVI; } static void ev6_cpu_initfn(Object *obj) { - AlphaCPU *cpu = ALPHA_CPU(obj); - CPUAlphaState *env = &cpu->env; + CPUAlphaState *env = cpu_env(CPU(obj)); env->implver = IMPLVER_21264; env->amask = AMASK_BWX | AMASK_FIX | AMASK_MVI | AMASK_TRAP; @@ -182,18 +163,12 @@ static void ev6_cpu_initfn(Object *obj) static void ev67_cpu_initfn(Object *obj) { - AlphaCPU *cpu = ALPHA_CPU(obj); - CPUAlphaState *env = &cpu->env; - - env->amask |= AMASK_CIX | AMASK_PREFETCH; + cpu_env(CPU(obj))->amask |= AMASK_CIX | AMASK_PREFETCH; } static void alpha_cpu_initfn(Object *obj) { - AlphaCPU *cpu = ALPHA_CPU(obj); - CPUAlphaState *env = &cpu->env; - - cpu_set_cpustate_pointers(cpu); + CPUAlphaState *env = cpu_env(CPU(obj)); env->lock_addr = -1; #if defined(CONFIG_USER_ONLY) @@ -216,8 +191,9 @@ static const struct SysemuCPUOps alpha_sysemu_ops = { #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps alpha_tcg_ops = { +static const TCGCPUOps alpha_tcg_ops = { .initialize = alpha_translate_init, + .restore_state_to_opc = alpha_restore_state_to_opc, #ifdef CONFIG_USER_ONLY .record_sigsegv = alpha_cpu_record_sigsegv, @@ -242,8 +218,10 @@ static void alpha_cpu_class_init(ObjectClass *oc, void *data) cc->class_by_name = alpha_cpu_class_by_name; cc->has_work = alpha_cpu_has_work; + cc->mmu_index = alpha_cpu_mmu_index; cc->dump_state = alpha_cpu_dump_state; cc->set_pc = alpha_cpu_set_pc; + cc->get_pc = alpha_cpu_get_pc; cc->gdb_read_register = alpha_cpu_gdb_read_register; cc->gdb_write_register = alpha_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY @@ -268,6 +246,7 @@ static const TypeInfo alpha_cpu_type_infos[] = { .name = TYPE_ALPHA_CPU, .parent = TYPE_CPU, .instance_size = sizeof(AlphaCPU), + .instance_align = __alignof(AlphaCPU), .instance_init = alpha_cpu_initfn, .abstract = true, .class_size = sizeof(AlphaCPUClass), diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index d0abc949a8..7188a409a0 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -191,7 +191,7 @@ enum { That said, we're only emulating Unix PALcode, and not attempting VMS, so we don't need to implement Executive and Supervisor. QEMU's own - PALcode cheats and usees the KSEG mapping for its code+data rather than + PALcode cheats and uses the KSEG mapping for its code+data rather than physical addresses. */ #define MMU_KERNEL_IDX 0 @@ -259,31 +259,39 @@ typedef struct CPUArchState { * An Alpha CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUAlphaState env; /* This alarm doesn't exist in real hardware; we wish it did. */ QEMUTimer *alarm_timer; }; +/** + * AlphaCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * An Alpha CPU model. + */ +struct AlphaCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + DeviceReset parent_reset; +}; #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_alpha_cpu; void alpha_cpu_do_interrupt(CPUState *cpu); bool alpha_cpu_exec_interrupt(CPUState *cpu, int int_req); +hwaddr alpha_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #endif /* !CONFIG_USER_ONLY */ 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, GByteArray *buf, int reg); int alpha_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); -#define cpu_list alpha_cpu_list - #include "exec/cpu-all.h" enum { @@ -362,7 +370,7 @@ enum { The Unix PALcode only uses bit 4. */ #define PS_USER_MODE 8u -/* CPUAlphaState->flags constants. These are layed out so that we +/* CPUAlphaState->flags constants. These are laid out so that we can set or reset the pieces individually by assigning to the byte, or manipulated as a whole. */ @@ -381,7 +389,7 @@ enum { #define TB_FLAG_UNALIGN (1u << 1) -static inline int cpu_mmu_index(CPUAlphaState *env, bool ifetch) +static inline int alpha_env_mmu_index(CPUAlphaState *env) { int ret = env->flags & ENV_FLAG_PS_USER ? MMU_USER_IDX : MMU_KERNEL_IDX; if (env->flags & ENV_FLAG_PAL_MODE) { @@ -429,11 +437,8 @@ enum { void alpha_translate_init(void); -#define ALPHA_CPU_TYPE_SUFFIX "-" TYPE_ALPHA_CPU -#define ALPHA_CPU_TYPE_NAME(model) model ALPHA_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_ALPHA_CPU -void alpha_cpu_list(void); G_NORETURN void dynamic_excp(CPUAlphaState *, uintptr_t, int, int); G_NORETURN void arith_excp(CPUAlphaState *, uintptr_t, int, uint64_t); @@ -462,8 +467,8 @@ void alpha_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, MemTxResult response, uintptr_t retaddr); #endif -static inline void cpu_get_tb_cpu_state(CPUAlphaState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *pflags) +static inline void cpu_get_tb_cpu_state(CPUAlphaState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) { *pc = env->pc; *cs_base = 0; diff --git a/target/alpha/fpu_helper.c b/target/alpha/fpu_helper.c index 3ff8bb456d..63d9e9ce39 100644 --- a/target/alpha/fpu_helper.c +++ b/target/alpha/fpu_helper.c @@ -453,78 +453,29 @@ uint64_t helper_cvtqs(CPUAlphaState *env, uint64_t a) static uint64_t do_cvttq(CPUAlphaState *env, uint64_t a, int roundmode) { - uint64_t frac, ret = 0; - uint32_t exp, sign, exc = 0; - int shift; + float64 fa; + int64_t ret; + uint32_t exc; - sign = (a >> 63); - exp = (uint32_t)(a >> 52) & 0x7ff; - frac = a & 0xfffffffffffffull; + fa = t_to_float64(a); + ret = float64_to_int64_modulo(fa, roundmode, &FP_STATUS); - if (exp == 0) { - if (unlikely(frac != 0) && !env->fp_status.flush_inputs_to_zero) { - goto do_underflow; - } - } else if (exp == 0x7ff) { - exc = FPCR_INV; - } else { - /* Restore implicit bit. */ - frac |= 0x10000000000000ull; + exc = get_float_exception_flags(&FP_STATUS); + if (unlikely(exc)) { + set_float_exception_flags(0, &FP_STATUS); - shift = exp - 1023 - 52; - if (shift >= 0) { - /* In this case the number is so large that we must shift - the fraction left. There is no rounding to do. */ - if (shift < 64) { - ret = frac << shift; - } - /* Check for overflow. Note the special case of -0x1p63. */ - if (shift >= 11 && a != 0xC3E0000000000000ull) { + /* We need to massage the resulting exceptions. */ + if (exc & float_flag_invalid_cvti) { + /* Overflow, either normal or infinity. */ + if (float64_is_infinity(fa)) { + exc = FPCR_INV; + } else { exc = FPCR_IOV | FPCR_INE; } - } else { - uint64_t round; - - /* In this case the number is smaller than the fraction as - represented by the 52 bit number. Here we must think - about rounding the result. Handle this by shifting the - fractional part of the number into the high bits of ROUND. - This will let us efficiently handle round-to-nearest. */ - shift = -shift; - if (shift < 63) { - ret = frac >> shift; - round = frac << (64 - shift); - } else { - /* The exponent is so small we shift out everything. - Leave a sticky bit for proper rounding below. */ - do_underflow: - round = 1; - } - - if (round) { - exc = FPCR_INE; - switch (roundmode) { - case float_round_nearest_even: - if (round == (1ull << 63)) { - /* Fraction is exactly 0.5; round to even. */ - ret += (ret & 1); - } else if (round > (1ull << 63)) { - ret += 1; - } - break; - case float_round_to_zero: - break; - case float_round_up: - ret += 1 - sign; - break; - case float_round_down: - ret += sign; - break; - } - } - } - if (sign) { - ret = -ret; + } else if (exc & float_flag_invalid) { + exc = FPCR_INV; + } else if (exc & float_flag_inexact) { + exc = FPCR_INE; } } env->error_code = exc; diff --git a/target/alpha/gdbstub.c b/target/alpha/gdbstub.c index 7db14f4431..13694fd321 100644 --- a/target/alpha/gdbstub.c +++ b/target/alpha/gdbstub.c @@ -19,12 +19,11 @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" int alpha_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - AlphaCPU *cpu = ALPHA_CPU(cs); - CPUAlphaState *env = &cpu->env; + CPUAlphaState *env = cpu_env(cs); uint64_t val; CPU_DoubleU d; @@ -59,8 +58,7 @@ int alpha_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int alpha_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - AlphaCPU *cpu = ALPHA_CPU(cs); - CPUAlphaState *env = &cpu->env; + CPUAlphaState *env = cpu_env(cs); target_ulong tmp = ldtul_p(mem_buf); CPU_DoubleU d; diff --git a/target/alpha/helper.c b/target/alpha/helper.c index a5a389b5a3..d6d4353edd 100644 --- a/target/alpha/helper.c +++ b/target/alpha/helper.c @@ -286,11 +286,10 @@ static int get_physical_address(CPUAlphaState *env, target_ulong addr, hwaddr alpha_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { - AlphaCPU *cpu = ALPHA_CPU(cs); target_ulong phys; int prot, fail; - fail = get_physical_address(&cpu->env, addr, 0, 0, &phys, &prot); + fail = get_physical_address(cpu_env(cs), addr, 0, 0, &phys, &prot); return (fail >= 0 ? -1 : phys); } @@ -298,8 +297,7 @@ bool alpha_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - AlphaCPU *cpu = ALPHA_CPU(cs); - CPUAlphaState *env = &cpu->env; + CPUAlphaState *env = cpu_env(cs); target_ulong phys; int prot, fail; @@ -325,8 +323,7 @@ bool alpha_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, void alpha_cpu_do_interrupt(CPUState *cs) { - AlphaCPU *cpu = ALPHA_CPU(cs); - CPUAlphaState *env = &cpu->env; + CPUAlphaState *env = cpu_env(cs); int i = cs->exception_index; if (qemu_loglevel_mask(CPU_LOG_INT)) { @@ -435,8 +432,7 @@ void alpha_cpu_do_interrupt(CPUState *cs) bool alpha_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { - AlphaCPU *cpu = ALPHA_CPU(cs); - CPUAlphaState *env = &cpu->env; + CPUAlphaState *env = cpu_env(cs); int idx = -1; /* We never take interrupts while in PALmode. */ @@ -487,8 +483,7 @@ void alpha_cpu_dump_state(CPUState *cs, FILE *f, int flags) "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9", "t10", "t11", "ra", "t12", "at", "gp", "sp" }; - AlphaCPU *cpu = ALPHA_CPU(cs); - CPUAlphaState *env = &cpu->env; + CPUAlphaState *env = cpu_env(cs); int i; qemu_fprintf(f, "PC " TARGET_FMT_lx " PS %02x\n", @@ -532,7 +527,7 @@ G_NORETURN void dynamic_excp(CPUAlphaState *env, uintptr_t retaddr, cs->exception_index = excp; env->error_code = error; if (retaddr) { - cpu_restore_state(cs, retaddr, true); + cpu_restore_state(cs, retaddr); /* Floating-point exceptions (our only users) point to the next PC. */ env->pc += 4; } diff --git a/target/alpha/machine.c b/target/alpha/machine.c index 2b7c8148ff..f09834f635 100644 --- a/target/alpha/machine.c +++ b/target/alpha/machine.c @@ -24,7 +24,7 @@ static const VMStateInfo vmstate_fpcr = { .put = put_fpcr, }; -static VMStateField vmstate_env_fields[] = { +static const VMStateField vmstate_env_fields[] = { VMSTATE_UINTTL_ARRAY(ir, CPUAlphaState, 31), VMSTATE_UINTTL_ARRAY(fir, CPUAlphaState, 31), /* Save the architecture value of the fpcr, not the internally @@ -73,7 +73,7 @@ static const VMStateDescription vmstate_env = { .fields = vmstate_env_fields, }; -static VMStateField vmstate_cpu_fields[] = { +static const VMStateField vmstate_cpu_fields[] = { VMSTATE_CPU(), VMSTATE_STRUCT(env, AlphaCPU, 1, vmstate_env, CPUAlphaState), VMSTATE_END_OF_LIST() diff --git a/target/alpha/mem_helper.c b/target/alpha/mem_helper.c index 47283a0612..872955f5e7 100644 --- a/target/alpha/mem_helper.c +++ b/target/alpha/mem_helper.c @@ -28,7 +28,7 @@ static void do_unaligned_access(CPUAlphaState *env, vaddr addr, uintptr_t retadd uint64_t pc; uint32_t insn; - cpu_restore_state(env_cpu(env), retaddr, true); + cpu_restore_state(env_cpu(env), retaddr); pc = env->pc; insn = cpu_ldl_code(env, pc); @@ -42,18 +42,14 @@ static void do_unaligned_access(CPUAlphaState *env, vaddr addr, uintptr_t retadd void alpha_cpu_record_sigbus(CPUState *cs, vaddr addr, MMUAccessType access_type, uintptr_t retaddr) { - AlphaCPU *cpu = ALPHA_CPU(cs); - CPUAlphaState *env = &cpu->env; - - do_unaligned_access(env, addr, retaddr); + do_unaligned_access(cpu_env(cs), addr, retaddr); } #else void alpha_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { - AlphaCPU *cpu = ALPHA_CPU(cs); - CPUAlphaState *env = &cpu->env; + CPUAlphaState *env = cpu_env(cs); do_unaligned_access(env, addr, retaddr); cs->exception_index = EXCP_UNALIGN; @@ -67,8 +63,7 @@ void alpha_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr) { - AlphaCPU *cpu = ALPHA_CPU(cs); - CPUAlphaState *env = &cpu->env; + CPUAlphaState *env = cpu_env(cs); env->trap_arg0 = addr; env->trap_arg1 = access_type == MMU_DATA_STORE ? 1 : 0; diff --git a/target/alpha/meson.build b/target/alpha/meson.build index 1aec55abb4..7dbbd55717 100644 --- a/target/alpha/meson.build +++ b/target/alpha/meson.build @@ -4,15 +4,18 @@ alpha_ss.add(files( 'fpu_helper.c', 'gdbstub.c', 'helper.c', + 'clk_helper.c', 'int_helper.c', 'mem_helper.c', - 'sys_helper.c', 'translate.c', 'vax_helper.c', )) -alpha_softmmu_ss = ss.source_set() -alpha_softmmu_ss.add(files('machine.c')) +alpha_system_ss = ss.source_set() +alpha_system_ss.add(files( + 'machine.c', + 'sys_helper.c', +)) target_arch += {'alpha': alpha_ss} -target_softmmu_arch += {'alpha': alpha_softmmu_ss} +target_system_arch += {'alpha': alpha_system_ss} diff --git a/target/alpha/sys_helper.c b/target/alpha/sys_helper.c index 25f6cb8894..768116ef32 100644 --- a/target/alpha/sys_helper.c +++ b/target/alpha/sys_helper.c @@ -20,29 +20,14 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/tb-flush.h" #include "exec/helper-proto.h" #include "sysemu/runstate.h" #include "sysemu/sysemu.h" #include "qemu/timer.h" -uint64_t helper_load_pcc(CPUAlphaState *env) -{ -#ifndef CONFIG_USER_ONLY - /* In system mode we have access to a decent high-resolution clock. - In order to make OS-level time accounting work with the RPCC, - present it with a well-timed clock fixed at 250MHz. */ - return (((uint64_t)env->pcc_ofs << 32) - | (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) >> 2)); -#else - /* In user-mode, QEMU_CLOCK_VIRTUAL doesn't exist. Just pass through the host cpu - clock ticks. Also, don't bother taking PCC_OFS into account. */ - return (uint32_t)cpu_get_host_ticks(); -#endif -} - /* PALcode support special instructions */ -#ifndef CONFIG_USER_ONLY void helper_tbia(CPUAlphaState *env) { tlb_flush(env_cpu(env)); @@ -88,5 +73,3 @@ void helper_set_alarm(CPUAlphaState *env, uint64_t expire) timer_del(cpu->alarm_timer); } } - -#endif /* CONFIG_USER_ONLY */ diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 9af1627079..a97cd54f0c 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -24,12 +24,14 @@ #include "qemu/host-utils.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 "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H #undef ALPHA_DEBUG_DISAS #define CONFIG_SOFTFLOAT_INLINE @@ -72,7 +74,7 @@ struct DisasContext { #ifdef CONFIG_USER_ONLY #define UNALIGN(C) (C)->unalign #else -#define UNALIGN(C) 0 +#define UNALIGN(C) MO_ALIGN #endif /* Target-specific return values from translate_one, indicating the @@ -93,8 +95,6 @@ static TCGv cpu_lock_value; static TCGv cpu_pal_ir[31]; #endif -#include "exec/gen-icount.h" - void alpha_translate_init(void) { #define DEF_VAR(V) { &cpu_##V, #V, offsetof(CPUAlphaState, V) } @@ -131,13 +131,13 @@ void alpha_translate_init(void) int i; for (i = 0; i < 31; i++) { - cpu_std_ir[i] = tcg_global_mem_new_i64(cpu_env, + cpu_std_ir[i] = tcg_global_mem_new_i64(tcg_env, offsetof(CPUAlphaState, ir[i]), greg_names[i]); } for (i = 0; i < 31; i++) { - cpu_fir[i] = tcg_global_mem_new_i64(cpu_env, + cpu_fir[i] = tcg_global_mem_new_i64(tcg_env, offsetof(CPUAlphaState, fir[i]), freg_names[i]); } @@ -146,7 +146,7 @@ void alpha_translate_init(void) memcpy(cpu_pal_ir, cpu_std_ir, sizeof(cpu_pal_ir)); for (i = 0; i < 8; i++) { int r = (i == 7 ? 25 : i + 8); - cpu_pal_ir[r] = tcg_global_mem_new_i64(cpu_env, + cpu_pal_ir[r] = tcg_global_mem_new_i64(tcg_env, offsetof(CPUAlphaState, shadow[i]), shadow_names[i]); @@ -155,7 +155,7 @@ void alpha_translate_init(void) for (i = 0; i < ARRAY_SIZE(vars); ++i) { const GlobalVar *v = &vars[i]; - *v->var = tcg_global_mem_new_i64(cpu_env, v->ofs, v->name); + *v->var = tcg_global_mem_new_i64(tcg_env, v->ofs, v->name); } } @@ -179,7 +179,6 @@ static void free_context_temps(DisasContext *ctx) { if (ctx->sink) { tcg_gen_discard_i64(ctx->sink); - tcg_temp_free(ctx->sink); ctx->sink = NULL; } } @@ -245,12 +244,12 @@ static int get_flag_ofs(unsigned shift) static void ld_flag_byte(TCGv val, unsigned shift) { - tcg_gen_ld8u_i64(val, cpu_env, get_flag_ofs(shift)); + tcg_gen_ld8u_i64(val, tcg_env, get_flag_ofs(shift)); } static void st_flag_byte(TCGv val, unsigned shift) { - tcg_gen_st8_i64(val, cpu_env, get_flag_ofs(shift)); + tcg_gen_st8_i64(val, tcg_env, get_flag_ofs(shift)); } static void gen_excp_1(int exception, int error_code) @@ -259,7 +258,7 @@ static void gen_excp_1(int exception, int error_code) tmp1 = tcg_constant_i32(exception); tmp2 = tcg_constant_i32(error_code); - gen_helper_excp(cpu_env, tmp1, tmp2); + gen_helper_excp(tcg_env, tmp1, tmp2); } static DisasJumpType gen_excp(DisasContext *ctx, int exception, int error_code) @@ -279,7 +278,6 @@ static void gen_ldf(DisasContext *ctx, TCGv dest, TCGv addr) TCGv_i32 tmp32 = tcg_temp_new_i32(); tcg_gen_qemu_ld_i32(tmp32, addr, ctx->mem_idx, MO_LEUL | UNALIGN(ctx)); gen_helper_memory_to_f(dest, tmp32); - tcg_temp_free_i32(tmp32); } static void gen_ldg(DisasContext *ctx, TCGv dest, TCGv addr) @@ -287,7 +285,6 @@ static void gen_ldg(DisasContext *ctx, TCGv dest, TCGv addr) TCGv tmp = tcg_temp_new(); tcg_gen_qemu_ld_i64(tmp, addr, ctx->mem_idx, MO_LEUQ | UNALIGN(ctx)); gen_helper_memory_to_g(dest, tmp); - tcg_temp_free(tmp); } static void gen_lds(DisasContext *ctx, TCGv dest, TCGv addr) @@ -295,7 +292,6 @@ static void gen_lds(DisasContext *ctx, TCGv dest, TCGv addr) TCGv_i32 tmp32 = tcg_temp_new_i32(); tcg_gen_qemu_ld_i32(tmp32, addr, ctx->mem_idx, MO_LEUL | UNALIGN(ctx)); gen_helper_memory_to_s(dest, tmp32); - tcg_temp_free_i32(tmp32); } static void gen_ldt(DisasContext *ctx, TCGv dest, TCGv addr) @@ -311,7 +307,6 @@ static void gen_load_fp(DisasContext *ctx, int ra, int rb, int32_t disp16, TCGv addr = tcg_temp_new(); tcg_gen_addi_i64(addr, load_gpr(ctx, rb), disp16); func(ctx, cpu_fir[ra], addr); - tcg_temp_free(addr); } } @@ -342,7 +337,6 @@ static void gen_load_int(DisasContext *ctx, int ra, int rb, int32_t disp16, tcg_gen_mov_i64(cpu_lock_addr, addr); tcg_gen_mov_i64(cpu_lock_value, dest); } - tcg_temp_free(addr); } static void gen_stf(DisasContext *ctx, TCGv src, TCGv addr) @@ -350,7 +344,6 @@ static void gen_stf(DisasContext *ctx, TCGv src, TCGv addr) TCGv_i32 tmp32 = tcg_temp_new_i32(); gen_helper_f_to_memory(tmp32, addr); tcg_gen_qemu_st_i32(tmp32, addr, ctx->mem_idx, MO_LEUL | UNALIGN(ctx)); - tcg_temp_free_i32(tmp32); } static void gen_stg(DisasContext *ctx, TCGv src, TCGv addr) @@ -358,7 +351,6 @@ static void gen_stg(DisasContext *ctx, TCGv src, TCGv addr) TCGv tmp = tcg_temp_new(); gen_helper_g_to_memory(tmp, src); tcg_gen_qemu_st_i64(tmp, addr, ctx->mem_idx, MO_LEUQ | UNALIGN(ctx)); - tcg_temp_free(tmp); } static void gen_sts(DisasContext *ctx, TCGv src, TCGv addr) @@ -366,7 +358,6 @@ static void gen_sts(DisasContext *ctx, TCGv src, TCGv addr) TCGv_i32 tmp32 = tcg_temp_new_i32(); gen_helper_s_to_memory(tmp32, src); tcg_gen_qemu_st_i32(tmp32, addr, ctx->mem_idx, MO_LEUL | UNALIGN(ctx)); - tcg_temp_free_i32(tmp32); } static void gen_stt(DisasContext *ctx, TCGv src, TCGv addr) @@ -380,7 +371,6 @@ static void gen_store_fp(DisasContext *ctx, int ra, int rb, int32_t disp16, TCGv addr = tcg_temp_new(); tcg_gen_addi_i64(addr, load_gpr(ctx, rb), disp16); func(ctx, load_fpr(ctx, ra), addr); - tcg_temp_free(addr); } static void gen_store_int(DisasContext *ctx, int ra, int rb, int32_t disp16, @@ -398,8 +388,6 @@ static void gen_store_int(DisasContext *ctx, int ra, int rb, int32_t disp16, src = load_gpr(ctx, ra); tcg_gen_qemu_st_i64(src, addr, ctx->mem_idx, op); - - tcg_temp_free(addr); } static DisasJumpType gen_store_conditional(DisasContext *ctx, int ra, int rb, @@ -416,7 +404,6 @@ static DisasJumpType gen_store_conditional(DisasContext *ctx, int ra, int rb, lab_fail = gen_new_label(); lab_done = gen_new_label(); tcg_gen_brcond_i64(TCG_COND_NE, addr, cpu_lock_addr, lab_fail); - tcg_temp_free_i64(addr); val = tcg_temp_new_i64(); tcg_gen_atomic_cmpxchg_i64(val, cpu_lock_addr, cpu_lock_value, @@ -426,7 +413,6 @@ static DisasJumpType gen_store_conditional(DisasContext *ctx, int ra, int rb, if (ra != 31) { tcg_gen_setcond_i64(TCG_COND_EQ, ctx->ir[ra], val, cpu_lock_value); } - tcg_temp_free_i64(val); tcg_gen_br(lab_done); gen_set_label(lab_fail); @@ -467,13 +453,13 @@ static DisasJumpType gen_bdirect(DisasContext *ctx, int ra, int32_t disp) } static DisasJumpType gen_bcond_internal(DisasContext *ctx, TCGCond cond, - TCGv cmp, int32_t disp) + TCGv cmp, uint64_t imm, int32_t disp) { uint64_t dest = ctx->base.pc_next + (disp << 2); TCGLabel *lab_true = gen_new_label(); if (use_goto_tb(ctx, dest)) { - tcg_gen_brcondi_i64(cond, cmp, 0, lab_true); + tcg_gen_brcondi_i64(cond, cmp, imm, lab_true); tcg_gen_goto_tb(0); tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); @@ -486,86 +472,71 @@ static DisasJumpType gen_bcond_internal(DisasContext *ctx, TCGCond cond, return DISAS_NORETURN; } else { - TCGv_i64 z = load_zero(ctx); + TCGv_i64 i = tcg_constant_i64(imm); TCGv_i64 d = tcg_constant_i64(dest); TCGv_i64 p = tcg_constant_i64(ctx->base.pc_next); - tcg_gen_movcond_i64(cond, cpu_pc, cmp, z, d, p); + tcg_gen_movcond_i64(cond, cpu_pc, cmp, i, d, p); return DISAS_PC_UPDATED; } } static DisasJumpType gen_bcond(DisasContext *ctx, TCGCond cond, int ra, - int32_t disp, int mask) + int32_t disp) { - if (mask) { - TCGv tmp = tcg_temp_new(); - DisasJumpType ret; - - tcg_gen_andi_i64(tmp, load_gpr(ctx, ra), 1); - ret = gen_bcond_internal(ctx, cond, tmp, disp); - tcg_temp_free(tmp); - return ret; - } - return gen_bcond_internal(ctx, cond, load_gpr(ctx, ra), disp); + return gen_bcond_internal(ctx, cond, load_gpr(ctx, ra), + is_tst_cond(cond), disp); } /* Fold -0.0 for comparison with COND. */ -static void gen_fold_mzero(TCGCond cond, TCGv dest, TCGv src) +static TCGv_i64 gen_fold_mzero(TCGCond *pcond, uint64_t *pimm, TCGv_i64 src) { - uint64_t mzero = 1ull << 63; + TCGv_i64 tmp; - switch (cond) { + *pimm = 0; + switch (*pcond) { case TCG_COND_LE: case TCG_COND_GT: /* For <= or >, the -0.0 value directly compares the way we want. */ - tcg_gen_mov_i64(dest, src); - break; + return src; case TCG_COND_EQ: case TCG_COND_NE: - /* For == or !=, we can simply mask off the sign bit and compare. */ - tcg_gen_andi_i64(dest, src, mzero - 1); - break; + /* For == or !=, we can compare without the sign bit. */ + *pcond = *pcond == TCG_COND_EQ ? TCG_COND_TSTEQ : TCG_COND_TSTNE; + *pimm = INT64_MAX; + return src; case TCG_COND_GE: case TCG_COND_LT: - /* For >= or <, map -0.0 to +0.0 via comparison and mask. */ - tcg_gen_setcondi_i64(TCG_COND_NE, dest, src, mzero); - tcg_gen_neg_i64(dest, dest); - tcg_gen_and_i64(dest, dest, src); - break; + /* For >= or <, map -0.0 to +0.0. */ + tmp = tcg_temp_new_i64(); + tcg_gen_movcond_i64(TCG_COND_EQ, tmp, + src, tcg_constant_i64(INT64_MIN), + tcg_constant_i64(0), src); + return tmp; default: - abort(); + g_assert_not_reached(); } } static DisasJumpType gen_fbcond(DisasContext *ctx, TCGCond cond, int ra, int32_t disp) { - TCGv cmp_tmp = tcg_temp_new(); - DisasJumpType ret; - - gen_fold_mzero(cond, cmp_tmp, load_fpr(ctx, ra)); - ret = gen_bcond_internal(ctx, cond, cmp_tmp, disp); - tcg_temp_free(cmp_tmp); - return ret; + uint64_t imm; + TCGv_i64 tmp = gen_fold_mzero(&cond, &imm, load_fpr(ctx, ra)); + return gen_bcond_internal(ctx, cond, tmp, imm, disp); } static void gen_fcmov(DisasContext *ctx, TCGCond cond, int ra, int rb, int rc) { - TCGv_i64 va, vb, z; - - z = load_zero(ctx); - vb = load_fpr(ctx, rb); - va = tcg_temp_new(); - gen_fold_mzero(cond, va, load_fpr(ctx, ra)); - - tcg_gen_movcond_i64(cond, dest_fpr(ctx, rc), va, z, vb, load_fpr(ctx, rc)); - - tcg_temp_free(va); + uint64_t imm; + TCGv_i64 tmp = gen_fold_mzero(&cond, &imm, load_fpr(ctx, ra)); + tcg_gen_movcond_i64(cond, dest_fpr(ctx, rc), + tmp, tcg_constant_i64(imm), + load_fpr(ctx, rb), load_fpr(ctx, rc)); } #define QUAL_RM_N 0x080 /* Round mode nearest even */ @@ -601,7 +572,7 @@ static void gen_qual_roundmode(DisasContext *ctx, int fn11) tcg_gen_movi_i32(tmp, float_round_down); break; case QUAL_RM_D: - tcg_gen_ld8u_i32(tmp, cpu_env, + tcg_gen_ld8u_i32(tmp, tcg_env, offsetof(CPUAlphaState, fpcr_dyn_round)); break; } @@ -610,13 +581,11 @@ static void gen_qual_roundmode(DisasContext *ctx, int fn11) /* ??? The "fpu/softfloat.h" interface is to call set_float_rounding_mode. With CONFIG_SOFTFLOAT that expands to an out-of-line call that just sets the one field. */ - tcg_gen_st8_i32(tmp, cpu_env, + tcg_gen_st8_i32(tmp, tcg_env, offsetof(CPUAlphaState, fp_status.float_rounding_mode)); #else gen_helper_setroundmode(tmp); #endif - - tcg_temp_free_i32(tmp); } static void gen_qual_flushzero(DisasContext *ctx, int fn11) @@ -632,7 +601,7 @@ static void gen_qual_flushzero(DisasContext *ctx, int fn11) tmp = tcg_temp_new_i32(); if (fn11) { /* Underflow is enabled, use the FPCR setting. */ - tcg_gen_ld8u_i32(tmp, cpu_env, + tcg_gen_ld8u_i32(tmp, tcg_env, offsetof(CPUAlphaState, fpcr_flush_to_zero)); } else { /* Underflow is disabled, force flush-to-zero. */ @@ -640,13 +609,11 @@ static void gen_qual_flushzero(DisasContext *ctx, int fn11) } #if defined(CONFIG_SOFTFLOAT_INLINE) - tcg_gen_st8_i32(tmp, cpu_env, + tcg_gen_st8_i32(tmp, tcg_env, offsetof(CPUAlphaState, fp_status.flush_to_zero)); #else gen_helper_setflushzero(tmp); #endif - - tcg_temp_free_i32(tmp); } static TCGv gen_ieee_input(DisasContext *ctx, int reg, int fn11, int is_cmp) @@ -659,16 +626,16 @@ static TCGv gen_ieee_input(DisasContext *ctx, int reg, int fn11, int is_cmp) val = cpu_fir[reg]; if ((fn11 & QUAL_S) == 0) { if (is_cmp) { - gen_helper_ieee_input_cmp(cpu_env, val); + gen_helper_ieee_input_cmp(tcg_env, val); } else { - gen_helper_ieee_input(cpu_env, val); + gen_helper_ieee_input(tcg_env, val); } } else { #ifndef CONFIG_USER_ONLY /* In system mode, raise exceptions for denormals like real hardware. In user mode, proceed as if the OS completion handler is handling the denormal as per spec. */ - gen_helper_ieee_input_s(cpu_env, val); + gen_helper_ieee_input_s(tcg_env, val); #endif } } @@ -701,9 +668,9 @@ static void gen_fp_exc_raise(int rc, int fn11) or if we were to do something clever with imprecise exceptions. */ reg = tcg_constant_i32(rc + 32); if (fn11 & QUAL_S) { - gen_helper_fp_exc_raise_s(cpu_env, ign, reg); + gen_helper_fp_exc_raise_s(tcg_env, ign, reg); } else { - gen_helper_fp_exc_raise(cpu_env, ign, reg); + gen_helper_fp_exc_raise(tcg_env, ign, reg); } } @@ -716,8 +683,6 @@ static void gen_cvtlq(TCGv vc, TCGv vb) tcg_gen_shri_i64(tmp, vb, 29); tcg_gen_sari_i64(vc, vb, 32); tcg_gen_deposit_i64(vc, vc, tmp, 0, 30); - - tcg_temp_free(tmp); } static void gen_ieee_arith2(DisasContext *ctx, @@ -730,7 +695,7 @@ static void gen_ieee_arith2(DisasContext *ctx, gen_qual_flushzero(ctx, fn11); vb = gen_ieee_input(ctx, rb, fn11, 0); - helper(dest_fpr(ctx, rc), cpu_env, vb); + helper(dest_fpr(ctx, rc), tcg_env, vb); gen_fp_exc_raise(rc, fn11); } @@ -757,10 +722,10 @@ static void gen_cvttq(DisasContext *ctx, int rb, int rc, int fn11) /* Almost all integer conversions use cropped rounding; special case that. */ if ((fn11 & QUAL_RM_MASK) == QUAL_RM_C) { - gen_helper_cvttq_c(vc, cpu_env, vb); + gen_helper_cvttq_c(vc, tcg_env, vb); } else { gen_qual_roundmode(ctx, fn11); - gen_helper_cvttq(vc, cpu_env, vb); + gen_helper_cvttq(vc, tcg_env, vb); } gen_fp_exc_raise(rc, fn11); } @@ -779,10 +744,10 @@ static void gen_ieee_intcvt(DisasContext *ctx, is inexact. Thus we only need to worry about exceptions when inexact handling is requested. */ if (fn11 & QUAL_I) { - helper(vc, cpu_env, vb); + helper(vc, tcg_env, vb); gen_fp_exc_raise(rc, fn11); } else { - helper(vc, cpu_env, vb); + helper(vc, tcg_env, vb); } } @@ -808,8 +773,6 @@ static void gen_cpy_mask(TCGv vc, TCGv va, TCGv vb, bool inv_a, uint64_t mask) tcg_gen_andc_i64(vc, vb, vmask); tcg_gen_or_i64(vc, vc, tmp); - - tcg_temp_free(tmp); } static void gen_ieee_arith3(DisasContext *ctx, @@ -824,7 +787,7 @@ static void gen_ieee_arith3(DisasContext *ctx, va = gen_ieee_input(ctx, ra, fn11, 0); vb = gen_ieee_input(ctx, rb, fn11, 0); vc = dest_fpr(ctx, rc); - helper(vc, cpu_env, va, vb); + helper(vc, tcg_env, va, vb); gen_fp_exc_raise(rc, fn11); } @@ -853,7 +816,7 @@ static void gen_ieee_compare(DisasContext *ctx, va = gen_ieee_input(ctx, ra, fn11, 1); vb = gen_ieee_input(ctx, rb, fn11, 1); vc = dest_fpr(ctx, rc); - helper(vc, cpu_env, va, vb); + helper(vc, tcg_env, va, vb); gen_fp_exc_raise(rc, fn11); } @@ -927,7 +890,6 @@ static void gen_ext_h(DisasContext *ctx, TCGv vc, TCGv va, int rb, bool islit, tcg_gen_neg_i64(tmp, tmp); tcg_gen_andi_i64(tmp, tmp, 0x3f); tcg_gen_shl_i64(vc, va, tmp); - tcg_temp_free(tmp); } gen_zapnoti(vc, vc, byte_mask); } @@ -948,7 +910,6 @@ static void gen_ext_l(DisasContext *ctx, TCGv vc, TCGv va, int rb, bool islit, tcg_gen_andi_i64(tmp, load_gpr(ctx, rb), 7); tcg_gen_shli_i64(tmp, tmp, 3); tcg_gen_shr_i64(vc, va, tmp); - tcg_temp_free(tmp); gen_zapnoti(vc, vc, byte_mask); } } @@ -986,8 +947,6 @@ static void gen_ins_h(DisasContext *ctx, TCGv vc, TCGv va, int rb, bool islit, tcg_gen_shr_i64(vc, tmp, shift); tcg_gen_shri_i64(vc, vc, 1); - tcg_temp_free(shift); - tcg_temp_free(tmp); } } @@ -1015,8 +974,6 @@ static void gen_ins_l(DisasContext *ctx, TCGv vc, TCGv va, int rb, bool islit, tcg_gen_andi_i64(shift, load_gpr(ctx, rb), 7); tcg_gen_shli_i64(shift, shift, 3); tcg_gen_shl_i64(vc, tmp, shift); - tcg_temp_free(shift); - tcg_temp_free(tmp); } } @@ -1047,9 +1004,6 @@ static void gen_msk_h(DisasContext *ctx, TCGv vc, TCGv va, int rb, bool islit, tcg_gen_shri_i64(mask, mask, 1); tcg_gen_andc_i64(vc, va, mask); - - tcg_temp_free(mask); - tcg_temp_free(shift); } } @@ -1069,9 +1023,6 @@ static void gen_msk_l(DisasContext *ctx, TCGv vc, TCGv va, int rb, bool islit, tcg_gen_shl_i64(mask, mask, shift); tcg_gen_andc_i64(vc, va, mask); - - tcg_temp_free(mask); - tcg_temp_free(shift); } } @@ -1098,12 +1049,12 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) break; case 0x9E: /* RDUNIQUE */ - tcg_gen_ld_i64(ctx->ir[IR_V0], cpu_env, + tcg_gen_ld_i64(ctx->ir[IR_V0], tcg_env, offsetof(CPUAlphaState, unique)); break; case 0x9F: /* WRUNIQUE */ - tcg_gen_st_i64(ctx->ir[IR_A0], cpu_env, + tcg_gen_st_i64(ctx->ir[IR_A0], tcg_env, offsetof(CPUAlphaState, unique)); break; default: @@ -1127,17 +1078,17 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) break; case 0x2D: /* WRVPTPTR */ - tcg_gen_st_i64(ctx->ir[IR_A0], cpu_env, + tcg_gen_st_i64(ctx->ir[IR_A0], tcg_env, offsetof(CPUAlphaState, vptptr)); break; case 0x31: /* WRVAL */ - tcg_gen_st_i64(ctx->ir[IR_A0], cpu_env, + tcg_gen_st_i64(ctx->ir[IR_A0], tcg_env, offsetof(CPUAlphaState, sysval)); break; case 0x32: /* RDVAL */ - tcg_gen_ld_i64(ctx->ir[IR_V0], cpu_env, + tcg_gen_ld_i64(ctx->ir[IR_V0], tcg_env, offsetof(CPUAlphaState, sysval)); break; @@ -1152,7 +1103,6 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) TCGv tmp = tcg_temp_new(); tcg_gen_andi_i64(tmp, ctx->ir[IR_A0], PS_INT_MASK); st_flag_byte(tmp, ENV_FLAG_PS_SHIFT); - tcg_temp_free(tmp); } /* Allow interrupts to be recognized right away. */ @@ -1166,23 +1116,23 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) case 0x38: /* WRUSP */ - tcg_gen_st_i64(ctx->ir[IR_A0], cpu_env, + tcg_gen_st_i64(ctx->ir[IR_A0], tcg_env, offsetof(CPUAlphaState, usp)); break; case 0x3A: /* RDUSP */ - tcg_gen_ld_i64(ctx->ir[IR_V0], cpu_env, + tcg_gen_ld_i64(ctx->ir[IR_V0], tcg_env, offsetof(CPUAlphaState, usp)); break; case 0x3C: /* WHAMI */ - tcg_gen_ld32s_i64(ctx->ir[IR_V0], cpu_env, + tcg_gen_ld32s_i64(ctx->ir[IR_V0], tcg_env, -offsetof(AlphaCPU, env) + offsetof(CPUState, cpu_index)); break; case 0x3E: /* WTINT */ - tcg_gen_st_i32(tcg_constant_i32(1), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(1), tcg_env, -offsetof(AlphaCPU, env) + offsetof(CPUState, halted)); tcg_gen_movi_i64(ctx->ir[IR_V0], 0); @@ -1214,8 +1164,7 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) } tcg_gen_movi_i64(tmp, exc_addr); - tcg_gen_st_i64(tmp, cpu_env, offsetof(CPUAlphaState, exc_addr)); - tcg_temp_free(tmp); + tcg_gen_st_i64(tmp, tcg_env, offsetof(CPUAlphaState, exc_addr)); entry += (palcode & 0x80 ? 0x2000 + (palcode - 0x80) * 64 @@ -1273,8 +1222,7 @@ static DisasJumpType gen_mfpr(DisasContext *ctx, TCGv va, int regno) case 249: /* VMTIME */ helper = gen_helper_get_vmtime; do_helper: - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); + if (translator_io_start(&ctx->base)) { helper(va); return DISAS_PC_STALE; } else { @@ -1296,9 +1244,9 @@ static DisasJumpType gen_mfpr(DisasContext *ctx, TCGv va, int regno) if (data == 0) { tcg_gen_movi_i64(va, 0); } else if (data & PR_LONG) { - tcg_gen_ld32s_i64(va, cpu_env, data & ~PR_LONG); + tcg_gen_ld32s_i64(va, tcg_env, data & ~PR_LONG); } else { - tcg_gen_ld_i64(va, cpu_env, data); + tcg_gen_ld_i64(va, tcg_env, data); } break; } @@ -1314,17 +1262,17 @@ static DisasJumpType gen_mtpr(DisasContext *ctx, TCGv vb, int regno) switch (regno) { case 255: /* TBIA */ - gen_helper_tbia(cpu_env); + gen_helper_tbia(tcg_env); break; case 254: /* TBIS */ - gen_helper_tbis(cpu_env, vb); + gen_helper_tbis(tcg_env, vb); break; case 253: /* WAIT */ - tcg_gen_st_i32(tcg_constant_i32(1), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(1), tcg_env, -offsetof(AlphaCPU, env) + offsetof(CPUState, halted)); return gen_excp(ctx, EXCP_HALTED, 0); @@ -1335,20 +1283,19 @@ static DisasJumpType gen_mtpr(DisasContext *ctx, TCGv vb, int regno) case 251: /* ALARM */ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); + if (translator_io_start(&ctx->base)) { ret = DISAS_PC_STALE; } - gen_helper_set_alarm(cpu_env, vb); + gen_helper_set_alarm(tcg_env, vb); break; case 7: /* PALBR */ - tcg_gen_st_i64(vb, cpu_env, offsetof(CPUAlphaState, palbr)); + tcg_gen_st_i64(vb, tcg_env, offsetof(CPUAlphaState, palbr)); /* Changing the PAL base register implies un-chaining all of the TBs that ended with a CALL_PAL. Since the base register usually only changes during boot, flushing everything works well. */ - gen_helper_tb_flush(cpu_env); + gen_helper_tb_flush(tcg_env); return DISAS_PC_STALE; case 32 ... 39: @@ -1370,9 +1317,9 @@ static DisasJumpType gen_mtpr(DisasContext *ctx, TCGv vb, int regno) data = cpu_pr_data(regno); if (data != 0) { if (data & PR_LONG) { - tcg_gen_st32_i64(vb, cpu_env, data & ~PR_LONG); + tcg_gen_st32_i64(vb, tcg_env, data & ~PR_LONG); } else { - tcg_gen_st_i64(vb, cpu_env, data); + tcg_gen_st_i64(vb, tcg_env, data); } } break; @@ -1550,7 +1497,6 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tcg_gen_shli_i64(tmp, va, 2); tcg_gen_add_i64(tmp, tmp, vb); tcg_gen_ext32s_i64(vc, tmp); - tcg_temp_free(tmp); break; case 0x09: /* SUBL */ @@ -1563,7 +1509,6 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tcg_gen_shli_i64(tmp, va, 2); tcg_gen_sub_i64(tmp, tmp, vb); tcg_gen_ext32s_i64(vc, tmp); - tcg_temp_free(tmp); break; case 0x0F: /* CMPBGE */ @@ -1580,7 +1525,6 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tcg_gen_shli_i64(tmp, va, 3); tcg_gen_add_i64(tmp, tmp, vb); tcg_gen_ext32s_i64(vc, tmp); - tcg_temp_free(tmp); break; case 0x1B: /* S8SUBL */ @@ -1588,7 +1532,6 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tcg_gen_shli_i64(tmp, va, 3); tcg_gen_sub_i64(tmp, tmp, vb); tcg_gen_ext32s_i64(vc, tmp); - tcg_temp_free(tmp); break; case 0x1D: /* CMPULT */ @@ -1603,7 +1546,6 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tmp = tcg_temp_new(); tcg_gen_shli_i64(tmp, va, 2); tcg_gen_add_i64(vc, tmp, vb); - tcg_temp_free(tmp); break; case 0x29: /* SUBQ */ @@ -1614,7 +1556,6 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tmp = tcg_temp_new(); tcg_gen_shli_i64(tmp, va, 2); tcg_gen_sub_i64(vc, tmp, vb); - tcg_temp_free(tmp); break; case 0x2D: /* CMPEQ */ @@ -1625,14 +1566,12 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tmp = tcg_temp_new(); tcg_gen_shli_i64(tmp, va, 3); tcg_gen_add_i64(vc, tmp, vb); - tcg_temp_free(tmp); break; case 0x3B: /* S8SUBQ */ tmp = tcg_temp_new(); tcg_gen_shli_i64(tmp, va, 3); tcg_gen_sub_i64(vc, tmp, vb); - tcg_temp_free(tmp); break; case 0x3D: /* CMPULE */ @@ -1645,8 +1584,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tcg_gen_ext32s_i64(vc, vb); tcg_gen_add_i64(tmp, tmp, vc); tcg_gen_ext32s_i64(vc, tmp); - gen_helper_check_overflow(cpu_env, vc, tmp); - tcg_temp_free(tmp); + gen_helper_check_overflow(tcg_env, vc, tmp); break; case 0x49: /* SUBL/V */ @@ -1655,8 +1593,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tcg_gen_ext32s_i64(vc, vb); tcg_gen_sub_i64(tmp, tmp, vc); tcg_gen_ext32s_i64(vc, tmp); - gen_helper_check_overflow(cpu_env, vc, tmp); - tcg_temp_free(tmp); + gen_helper_check_overflow(tcg_env, vc, tmp); break; case 0x4D: /* CMPLT */ @@ -1673,9 +1610,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tcg_gen_and_i64(tmp, tmp, tmp2); tcg_gen_shri_i64(tmp, tmp, 63); tcg_gen_movi_i64(tmp2, 0); - gen_helper_check_overflow(cpu_env, tmp, tmp2); - tcg_temp_free(tmp); - tcg_temp_free(tmp2); + gen_helper_check_overflow(tcg_env, tmp, tmp2); break; case 0x69: /* SUBQ/V */ @@ -1688,9 +1623,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tcg_gen_and_i64(tmp, tmp, tmp2); tcg_gen_shri_i64(tmp, tmp, 63); tcg_gen_movi_i64(tmp2, 0); - gen_helper_check_overflow(cpu_env, tmp, tmp2); - tcg_temp_free(tmp); - tcg_temp_free(tmp2); + gen_helper_check_overflow(tcg_env, tmp, tmp2); break; case 0x6D: /* CMPLE */ @@ -1740,19 +1673,13 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) break; case 0x14: /* CMOVLBS */ - tmp = tcg_temp_new(); - tcg_gen_andi_i64(tmp, va, 1); - tcg_gen_movcond_i64(TCG_COND_NE, vc, tmp, load_zero(ctx), + tcg_gen_movcond_i64(TCG_COND_TSTNE, vc, va, tcg_constant_i64(1), vb, load_gpr(ctx, rc)); - tcg_temp_free(tmp); break; case 0x16: /* CMOVLBC */ - tmp = tcg_temp_new(); - tcg_gen_andi_i64(tmp, va, 1); - tcg_gen_movcond_i64(TCG_COND_EQ, vc, tmp, load_zero(ctx), + tcg_gen_movcond_i64(TCG_COND_TSTEQ, vc, va, tcg_constant_i64(1), vb, load_gpr(ctx, rc)); - tcg_temp_free(tmp); break; case 0x20: /* BIS */ @@ -1884,7 +1811,6 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) vb = load_gpr(ctx, rb); tcg_gen_andi_i64(tmp, vb, 0x3f); tcg_gen_shr_i64(vc, va, tmp); - tcg_temp_free(tmp); } break; case 0x36: @@ -1900,7 +1826,6 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) vb = load_gpr(ctx, rb); tcg_gen_andi_i64(tmp, vb, 0x3f); tcg_gen_shl_i64(vc, va, tmp); - tcg_temp_free(tmp); } break; case 0x3B: @@ -1916,7 +1841,6 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) vb = load_gpr(ctx, rb); tcg_gen_andi_i64(tmp, vb, 0x3f); tcg_gen_sar_i64(vc, va, tmp); - tcg_temp_free(tmp); } break; case 0x52: @@ -1978,7 +1902,6 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) /* UMULH */ tmp = tcg_temp_new(); tcg_gen_mulu2_i64(tmp, vc, va, vb); - tcg_temp_free(tmp); break; case 0x40: /* MULL/V */ @@ -1987,8 +1910,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tcg_gen_ext32s_i64(vc, vb); tcg_gen_mul_i64(tmp, tmp, vc); tcg_gen_ext32s_i64(vc, tmp); - gen_helper_check_overflow(cpu_env, vc, tmp); - tcg_temp_free(tmp); + gen_helper_check_overflow(tcg_env, vc, tmp); break; case 0x60: /* MULQ/V */ @@ -1996,9 +1918,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tmp2 = tcg_temp_new(); tcg_gen_muls2_i64(vc, tmp, va, vb); tcg_gen_sari_i64(tmp2, vc, 63); - gen_helper_check_overflow(cpu_env, tmp, tmp2); - tcg_temp_free(tmp); - tcg_temp_free(tmp2); + gen_helper_check_overflow(tcg_env, tmp, tmp2); break; default: goto invalid_opc; @@ -2017,14 +1937,13 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) va = load_gpr(ctx, ra); tcg_gen_extrl_i64_i32(t32, va); gen_helper_memory_to_s(vc, t32); - tcg_temp_free_i32(t32); break; case 0x0A: /* SQRTF */ REQUIRE_REG_31(ra); REQUIRE_FEN; vb = load_fpr(ctx, rb); - gen_helper_sqrtf(vc, cpu_env, vb); + gen_helper_sqrtf(vc, tcg_env, vb); break; case 0x0B: /* SQRTS */ @@ -2040,7 +1959,6 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) va = load_gpr(ctx, ra); tcg_gen_extrl_i64_i32(t32, va); gen_helper_memory_to_f(vc, t32); - tcg_temp_free_i32(t32); break; case 0x24: /* ITOFT */ @@ -2054,7 +1972,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) REQUIRE_REG_31(ra); REQUIRE_FEN; vb = load_fpr(ctx, rb); - gen_helper_sqrtg(vc, cpu_env, vb); + gen_helper_sqrtg(vc, tcg_env, vb); break; case 0x02B: /* SQRTT */ @@ -2077,22 +1995,22 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) case 0x00: /* ADDF */ REQUIRE_FEN; - gen_helper_addf(vc, cpu_env, va, vb); + gen_helper_addf(vc, tcg_env, va, vb); break; case 0x01: /* SUBF */ REQUIRE_FEN; - gen_helper_subf(vc, cpu_env, va, vb); + gen_helper_subf(vc, tcg_env, va, vb); break; case 0x02: /* MULF */ REQUIRE_FEN; - gen_helper_mulf(vc, cpu_env, va, vb); + gen_helper_mulf(vc, tcg_env, va, vb); break; case 0x03: /* DIVF */ REQUIRE_FEN; - gen_helper_divf(vc, cpu_env, va, vb); + gen_helper_divf(vc, tcg_env, va, vb); break; case 0x1E: /* CVTDG -- TODO */ @@ -2101,43 +2019,43 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) case 0x20: /* ADDG */ REQUIRE_FEN; - gen_helper_addg(vc, cpu_env, va, vb); + gen_helper_addg(vc, tcg_env, va, vb); break; case 0x21: /* SUBG */ REQUIRE_FEN; - gen_helper_subg(vc, cpu_env, va, vb); + gen_helper_subg(vc, tcg_env, va, vb); break; case 0x22: /* MULG */ REQUIRE_FEN; - gen_helper_mulg(vc, cpu_env, va, vb); + gen_helper_mulg(vc, tcg_env, va, vb); break; case 0x23: /* DIVG */ REQUIRE_FEN; - gen_helper_divg(vc, cpu_env, va, vb); + gen_helper_divg(vc, tcg_env, va, vb); break; case 0x25: /* CMPGEQ */ REQUIRE_FEN; - gen_helper_cmpgeq(vc, cpu_env, va, vb); + gen_helper_cmpgeq(vc, tcg_env, va, vb); break; case 0x26: /* CMPGLT */ REQUIRE_FEN; - gen_helper_cmpglt(vc, cpu_env, va, vb); + gen_helper_cmpglt(vc, tcg_env, va, vb); break; case 0x27: /* CMPGLE */ REQUIRE_FEN; - gen_helper_cmpgle(vc, cpu_env, va, vb); + gen_helper_cmpgle(vc, tcg_env, va, vb); break; case 0x2C: /* CVTGF */ REQUIRE_REG_31(ra); REQUIRE_FEN; - gen_helper_cvtgf(vc, cpu_env, vb); + gen_helper_cvtgf(vc, tcg_env, vb); break; case 0x2D: /* CVTGD -- TODO */ @@ -2147,19 +2065,19 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) /* CVTGQ */ REQUIRE_REG_31(ra); REQUIRE_FEN; - gen_helper_cvtgq(vc, cpu_env, vb); + gen_helper_cvtgq(vc, tcg_env, vb); break; case 0x3C: /* CVTQF */ REQUIRE_REG_31(ra); REQUIRE_FEN; - gen_helper_cvtqf(vc, cpu_env, vb); + gen_helper_cvtqf(vc, tcg_env, vb); break; case 0x3E: /* CVTQG */ REQUIRE_REG_31(ra); REQUIRE_FEN; - gen_helper_cvtqg(vc, cpu_env, vb); + gen_helper_cvtqg(vc, tcg_env, vb); break; default: goto invalid_opc; @@ -2310,7 +2228,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) /* MT_FPCR */ REQUIRE_FEN; va = load_fpr(ctx, ra); - gen_helper_store_fpcr(cpu_env, va); + gen_helper_store_fpcr(tcg_env, va); if (ctx->tb_rm == QUAL_RM_D) { /* Re-do the copy of the rounding mode to fp_status the next time we use dynamic rounding. */ @@ -2321,7 +2239,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) /* MF_FPCR */ REQUIRE_FEN; va = dest_fpr(ctx, ra); - gen_helper_load_fpcr(va, cpu_env); + gen_helper_load_fpcr(va, tcg_env); break; case 0x02A: /* FCMOVEQ */ @@ -2360,7 +2278,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) REQUIRE_FEN; vc = dest_fpr(ctx, rc); vb = load_fpr(ctx, rb); - gen_helper_cvtql(vc, cpu_env, vb); + gen_helper_cvtql(vc, tcg_env, vb); gen_fp_exc_raise(rc, fn11); break; default: @@ -2397,13 +2315,10 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) case 0xC000: /* RPCC */ va = dest_gpr(ctx, ra); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - gen_helper_load_pcc(va, cpu_env); + if (translator_io_start(&ctx->base)) { ret = DISAS_PC_STALE; - } else { - gen_helper_load_pcc(va, cpu_env); } + gen_helper_load_pcc(va, tcg_env); break; case 0xE000: /* RC */ @@ -2464,21 +2379,21 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) switch ((insn >> 12) & 0xF) { case 0x0: /* Longword physical access (hw_ldl/p) */ - tcg_gen_qemu_ld_i64(va, addr, MMU_PHYS_IDX, MO_LESL); + tcg_gen_qemu_ld_i64(va, addr, MMU_PHYS_IDX, MO_LESL | MO_ALIGN); break; case 0x1: /* Quadword physical access (hw_ldq/p) */ - tcg_gen_qemu_ld_i64(va, addr, MMU_PHYS_IDX, MO_LEUQ); + tcg_gen_qemu_ld_i64(va, addr, MMU_PHYS_IDX, MO_LEUQ | MO_ALIGN); break; case 0x2: /* Longword physical access with lock (hw_ldl_l/p) */ - tcg_gen_qemu_ld_i64(va, addr, MMU_PHYS_IDX, MO_LESL); + tcg_gen_qemu_ld_i64(va, addr, MMU_PHYS_IDX, MO_LESL | MO_ALIGN); tcg_gen_mov_i64(cpu_lock_addr, addr); tcg_gen_mov_i64(cpu_lock_value, va); break; case 0x3: /* Quadword physical access with lock (hw_ldq_l/p) */ - tcg_gen_qemu_ld_i64(va, addr, MMU_PHYS_IDX, MO_LEUQ); + tcg_gen_qemu_ld_i64(va, addr, MMU_PHYS_IDX, MO_LEUQ | MO_ALIGN); tcg_gen_mov_i64(cpu_lock_addr, addr); tcg_gen_mov_i64(cpu_lock_value, va); break; @@ -2503,11 +2418,13 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) goto invalid_opc; case 0xA: /* Longword virtual access with protection check (hw_ldl/w) */ - tcg_gen_qemu_ld_i64(va, addr, MMU_KERNEL_IDX, MO_LESL); + tcg_gen_qemu_ld_i64(va, addr, MMU_KERNEL_IDX, + MO_LESL | MO_ALIGN); break; case 0xB: /* Quadword virtual access with protection check (hw_ldq/w) */ - tcg_gen_qemu_ld_i64(va, addr, MMU_KERNEL_IDX, MO_LEUQ); + tcg_gen_qemu_ld_i64(va, addr, MMU_KERNEL_IDX, + MO_LEUQ | MO_ALIGN); break; case 0xC: /* Longword virtual access with alt access mode (hw_ldl/a)*/ @@ -2518,15 +2435,16 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) case 0xE: /* Longword virtual access with alternate access mode and protection checks (hw_ldl/wa) */ - tcg_gen_qemu_ld_i64(va, addr, MMU_USER_IDX, MO_LESL); + tcg_gen_qemu_ld_i64(va, addr, MMU_USER_IDX, + MO_LESL | MO_ALIGN); break; case 0xF: /* Quadword virtual access with alternate access mode and protection checks (hw_ldq/wa) */ - tcg_gen_qemu_ld_i64(va, addr, MMU_USER_IDX, MO_LEUQ); + tcg_gen_qemu_ld_i64(va, addr, MMU_USER_IDX, + MO_LEUQ | MO_ALIGN); break; } - tcg_temp_free(addr); break; } #else @@ -2550,7 +2468,6 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) va = load_fpr(ctx, ra); gen_helper_s_to_memory(t32, va); tcg_gen_ext_i32_i64(vc, t32); - tcg_temp_free_i32(t32); break; } @@ -2697,7 +2614,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) address from EXC_ADDR. This turns out to be useful for our emulation PALcode, so continue to accept it. */ vb = dest_sink(ctx); - tcg_gen_ld_i64(vb, cpu_env, offsetof(CPUAlphaState, exc_addr)); + tcg_gen_ld_i64(vb, tcg_env, offsetof(CPUAlphaState, exc_addr)); } else { vb = load_gpr(ctx, rb); } @@ -2706,7 +2623,6 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tmp = tcg_temp_new(); tcg_gen_andi_i64(tmp, vb, 1); st_flag_byte(tmp, ENV_FLAG_PAL_SHIFT); - tcg_temp_free(tmp); tcg_gen_andi_i64(cpu_pc, vb, ~3); /* Allow interrupts to be recognized right away. */ ret = DISAS_PC_UPDATED_NOCHAIN; @@ -2727,8 +2643,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) vb = load_gpr(ctx, rb); tmp = tcg_temp_new(); tcg_gen_addi_i64(tmp, vb, disp12); - tcg_gen_qemu_st_i64(va, tmp, MMU_PHYS_IDX, MO_LESL); - tcg_temp_free(tmp); + tcg_gen_qemu_st_i64(va, tmp, MMU_PHYS_IDX, MO_LESL | MO_ALIGN); break; case 0x1: /* Quadword physical access */ @@ -2736,18 +2651,17 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) vb = load_gpr(ctx, rb); tmp = tcg_temp_new(); tcg_gen_addi_i64(tmp, vb, disp12); - tcg_gen_qemu_st_i64(va, tmp, MMU_PHYS_IDX, MO_LEUQ); - tcg_temp_free(tmp); + tcg_gen_qemu_st_i64(va, tmp, MMU_PHYS_IDX, MO_LEUQ | MO_ALIGN); break; case 0x2: /* Longword physical access with lock */ ret = gen_store_conditional(ctx, ra, rb, disp12, - MMU_PHYS_IDX, MO_LESL); + MMU_PHYS_IDX, MO_LESL | MO_ALIGN); break; case 0x3: /* Quadword physical access with lock */ ret = gen_store_conditional(ctx, ra, rb, disp12, - MMU_PHYS_IDX, MO_LEUQ); + MMU_PHYS_IDX, MO_LEUQ | MO_ALIGN); break; case 0x4: /* Longword virtual access */ @@ -2841,11 +2755,11 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) break; case 0x2A: /* LDL_L */ - gen_load_int(ctx, ra, rb, disp16, MO_LESL, 0, 1); + gen_load_int(ctx, ra, rb, disp16, MO_LESL | MO_ALIGN, 0, 1); break; case 0x2B: /* LDQ_L */ - gen_load_int(ctx, ra, rb, disp16, MO_LEUQ, 0, 1); + gen_load_int(ctx, ra, rb, disp16, MO_LEUQ | MO_ALIGN, 0, 1); break; case 0x2C: /* STL */ @@ -2858,12 +2772,12 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) case 0x2E: /* STL_C */ ret = gen_store_conditional(ctx, ra, rb, disp16, - ctx->mem_idx, MO_LESL); + ctx->mem_idx, MO_LESL | MO_ALIGN); break; case 0x2F: /* STQ_C */ ret = gen_store_conditional(ctx, ra, rb, disp16, - ctx->mem_idx, MO_LEUQ); + ctx->mem_idx, MO_LEUQ | MO_ALIGN); break; case 0x30: /* BR */ @@ -2899,35 +2813,35 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) break; case 0x38: /* BLBC */ - ret = gen_bcond(ctx, TCG_COND_EQ, ra, disp21, 1); + ret = gen_bcond(ctx, TCG_COND_TSTEQ, ra, disp21); break; case 0x39: /* BEQ */ - ret = gen_bcond(ctx, TCG_COND_EQ, ra, disp21, 0); + ret = gen_bcond(ctx, TCG_COND_EQ, ra, disp21); break; case 0x3A: /* BLT */ - ret = gen_bcond(ctx, TCG_COND_LT, ra, disp21, 0); + ret = gen_bcond(ctx, TCG_COND_LT, ra, disp21); break; case 0x3B: /* BLE */ - ret = gen_bcond(ctx, TCG_COND_LE, ra, disp21, 0); + ret = gen_bcond(ctx, TCG_COND_LE, ra, disp21); break; case 0x3C: /* BLBS */ - ret = gen_bcond(ctx, TCG_COND_NE, ra, disp21, 1); + ret = gen_bcond(ctx, TCG_COND_TSTNE, ra, disp21); break; case 0x3D: /* BNE */ - ret = gen_bcond(ctx, TCG_COND_NE, ra, disp21, 0); + ret = gen_bcond(ctx, TCG_COND_NE, ra, disp21); break; case 0x3E: /* BGE */ - ret = gen_bcond(ctx, TCG_COND_GE, ra, disp21, 0); + ret = gen_bcond(ctx, TCG_COND_GE, ra, disp21); break; case 0x3F: /* BGT */ - ret = gen_bcond(ctx, TCG_COND_GT, ra, disp21, 0); + ret = gen_bcond(ctx, TCG_COND_GT, ra, disp21); break; invalid_opc: ret = gen_invalid(ctx); @@ -2943,11 +2857,11 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) static void alpha_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - CPUAlphaState *env = cpu->env_ptr; + CPUAlphaState *env = cpu_env(cpu); int64_t bound; ctx->tbflags = ctx->base.tb->flags; - ctx->mem_idx = cpu_mmu_index(env, false); + ctx->mem_idx = alpha_env_mmu_index(env); ctx->implver = env->implver; ctx->amask = env->amask; @@ -2963,7 +2877,7 @@ static void alpha_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) the first fp insn of the TB. Alternately we could define a proper default for every TB (e.g. QUAL_RM_N or QUAL_RM_D) and make sure to reset the FP_STATUS to that default at the end of any TB that - changes the default. We could even (gasp) dynamiclly figure out + changes the default. We could even (gasp) dynamically figure out what default would be most efficient given the running program. */ ctx->tb_rm = -1; /* Similarly for flush-to-zero. */ @@ -2989,14 +2903,13 @@ static void alpha_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) static void alpha_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - CPUAlphaState *env = cpu->env_ptr; - uint32_t insn = translator_ldl(env, &ctx->base, ctx->base.pc_next); + uint32_t insn = translator_ldl(cpu_env(cpu), &ctx->base, + ctx->base.pc_next); ctx->base.pc_next += 4; ctx->base.is_jmp = translate_one(ctx, insn); free_context_temps(ctx); - translator_loop_temp_check(&ctx->base); } static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) @@ -3043,14 +2956,9 @@ static const TranslatorOps alpha_tr_ops = { .disas_log = alpha_tr_disas_log, }; -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc; - translator_loop(&alpha_tr_ops, &dc.base, cpu, tb, max_insns); -} - -void restore_state_to_opc(CPUAlphaState *env, TranslationBlock *tb, - target_ulong *data) -{ - env->pc = data[0]; + translator_loop(cpu, tb, max_insns, pc, host_pc, &alpha_tr_ops, &dc.base); } diff --git a/target/arm/Kconfig b/target/arm/Kconfig index 3f3394a22b..bf57d739cd 100644 --- a/target/arm/Kconfig +++ b/target/arm/Kconfig @@ -1,5 +1,10 @@ config ARM bool + select ARM_COMPATIBLE_SEMIHOSTING if TCG + + # We need to select this until we move m_helper.c and the + # translate.c v7m helpers under ARM_V7M. + select ARM_V7M if TCG config AARCH64 bool diff --git a/target/arm/arch_dump.c b/target/arm/arch_dump.c index b1f040e69f..06cdf4ba28 100644 --- a/target/arm/arch_dump.c +++ b/target/arm/arch_dump.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "elf.h" #include "sysemu/dump.h" +#include "cpu-features.h" /* struct user_pt_regs from arch/arm64/include/uapi/asm/ptrace.h */ struct aarch64_user_regs { @@ -232,12 +233,11 @@ static int aarch64_write_elf64_sve(WriteCoreDumpFunction f, #endif int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque) + int cpuid, DumpState *s) { struct aarch64_note note; ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; - DumpState *s = opaque; uint64_t pstate, sp; int ret, i; @@ -360,12 +360,11 @@ static int arm_write_elf32_vfp(WriteCoreDumpFunction f, CPUARMState *env, } int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque) + int cpuid, DumpState *s) { struct arm_note note; ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; - DumpState *s = opaque; int ret, i; bool fpvalid = cpu_isar_feature(aa32_vfp_simd, cpu); diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c index b75f813b40..2b2055c6ac 100644 --- a/target/arm/arm-powerctl.c +++ b/target/arm/arm-powerctl.c @@ -15,6 +15,8 @@ #include "arm-powerctl.h" #include "qemu/log.h" #include "qemu/main-loop.h" +#include "sysemu/tcg.h" +#include "target/arm/multiprocessing.h" #ifndef DEBUG_ARM_POWERCTL #define DEBUG_ARM_POWERCTL 0 @@ -36,7 +38,7 @@ CPUState *arm_get_cpu_by_id(uint64_t id) CPU_FOREACH(cpu) { ARMCPU *armcpu = ARM_CPU(cpu); - if (armcpu->mp_affinity == id) { + if (arm_cpu_mp_affinity(armcpu) == id) { return cpu; } } @@ -64,60 +66,9 @@ static void arm_set_cpu_on_async_work(CPUState *target_cpu_state, /* Initialize the cpu we are turning on */ cpu_reset(target_cpu_state); + arm_emulate_firmware_reset(target_cpu_state, info->target_el); target_cpu_state->halted = 0; - if (info->target_aa64) { - if ((info->target_el < 3) && arm_feature(&target_cpu->env, - ARM_FEATURE_EL3)) { - /* - * As target mode is AArch64, we need to set lower - * exception level (the requested level 2) to AArch64 - */ - target_cpu->env.cp15.scr_el3 |= SCR_RW; - } - - if ((info->target_el < 2) && arm_feature(&target_cpu->env, - ARM_FEATURE_EL2)) { - /* - * As target mode is AArch64, we need to set lower - * exception level (the requested level 1) to AArch64 - */ - target_cpu->env.cp15.hcr_el2 |= HCR_RW; - } - - target_cpu->env.pstate = aarch64_pstate_mode(info->target_el, true); - } else { - /* We are requested to boot in AArch32 mode */ - static const uint32_t mode_for_el[] = { 0, - ARM_CPU_MODE_SVC, - ARM_CPU_MODE_HYP, - ARM_CPU_MODE_SVC }; - - cpsr_write(&target_cpu->env, mode_for_el[info->target_el], CPSR_M, - CPSRWriteRaw); - } - - if (info->target_el == 3) { - /* Processor is in secure mode */ - target_cpu->env.cp15.scr_el3 &= ~SCR_NS; - } else { - /* 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 - * functional HVC insn. - */ - if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3) - && info->target_el == 2) { - target_cpu->env.cp15.scr_el3 |= SCR_HCE; - } - } - /* We check if the started CPU is now at the correct level */ assert(info->target_el == arm_current_el(&target_cpu->env)); @@ -127,8 +78,10 @@ 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); + if (tcg_enabled()) { + /* 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); @@ -136,7 +89,7 @@ static void arm_set_cpu_on_async_work(CPUState *target_cpu_state, g_free(info); /* Finally set the power status */ - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); target_cpu->power_state = PSCI_ON; } @@ -147,7 +100,7 @@ int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id, ARMCPU *target_cpu; struct CpuOnInfo *info; - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64 "\n", cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry, @@ -244,7 +197,7 @@ static void arm_set_cpu_on_and_reset_async_work(CPUState *target_cpu_state, target_cpu_state->halted = 0; /* Finally set the power status */ - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); target_cpu->power_state = PSCI_ON; } @@ -253,7 +206,7 @@ int arm_set_cpu_on_and_reset(uint64_t cpuid) CPUState *target_cpu_state; ARMCPU *target_cpu; - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); /* Retrieve the cpu we are powering up */ target_cpu_state = arm_get_cpu_by_id(cpuid); @@ -295,7 +248,7 @@ static void arm_set_cpu_off_async_work(CPUState *target_cpu_state, { ARMCPU *target_cpu = ARM_CPU(target_cpu_state); - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); target_cpu->power_state = PSCI_OFF; target_cpu_state->halted = 1; target_cpu_state->exception_index = EXCP_HLT; @@ -306,7 +259,7 @@ int arm_set_cpu_off(uint64_t cpuid) CPUState *target_cpu_state; ARMCPU *target_cpu; - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); DPRINTF("cpu %" PRId64 "\n", cpuid); @@ -342,7 +295,7 @@ int arm_reset_cpu(uint64_t cpuid) CPUState *target_cpu_state; ARMCPU *target_cpu; - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); DPRINTF("cpu %" PRId64 "\n", cpuid); diff --git a/target/arm/monitor.c b/target/arm/arm-qmp-cmds.c similarity index 88% rename from target/arm/monitor.c rename to target/arm/arm-qmp-cmds.c index 80c64fa355..3cc8cc738b 100644 --- a/target/arm/monitor.c +++ b/target/arm/arm-qmp-cmds.c @@ -28,7 +28,6 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-commands-machine-target.h" #include "qapi/qapi-commands-misc-target.h" -#include "qapi/qmp/qerror.h" #include "qapi/qmp/qdict.h" #include "qom/qom-qobject.h" @@ -95,7 +94,7 @@ static const char *cpu_model_advertised_features[] = { "sve640", "sve768", "sve896", "sve1024", "sve1152", "sve1280", "sve1408", "sve1536", "sve1664", "sve1792", "sve1920", "sve2048", "kvm-no-adjvtime", "kvm-steal-time", - "pauth", "pauth-impdef", + "pauth", "pauth-impdef", "pauth-qarma3", NULL }; @@ -104,7 +103,7 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, Error **errp) { CpuModelExpansionInfo *expansion_info; - const QDict *qdict_in = NULL; + const QDict *qdict_in; QDict *qdict_out; ObjectClass *oc; Object *obj; @@ -151,27 +150,20 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, } } - if (model->props) { - qdict_in = qobject_to(QDict, model->props); - if (!qdict_in) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict"); - return NULL; - } - } - obj = object_new(object_class_get_name(oc)); - if (qdict_in) { + if (model->props) { Visitor *visitor; Error *err = NULL; visitor = qobject_input_visitor_new(model->props); - if (!visit_start_struct(visitor, NULL, NULL, 0, errp)) { + if (!visit_start_struct(visitor, "model.props", NULL, 0, errp)) { visit_free(visitor); object_unref(obj); return NULL; } + qdict_in = qobject_to(QDict, model->props); i = 0; while ((name = cpu_model_advertised_features[i++]) != NULL) { if (qdict_get(qdict_in, name)) { @@ -221,10 +213,36 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, qobject_unref(qdict_out); } else { expansion_info->model->props = QOBJECT(qdict_out); - expansion_info->model->has_props = true; } object_unref(obj); return expansion_info; } + +static void arm_cpu_add_definition(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CpuDefinitionInfoList **cpu_list = user_data; + CpuDefinitionInfo *info; + const char *typename; + + typename = object_class_get_name(oc); + info = g_malloc0(sizeof(*info)); + info->name = cpu_model_from_type(typename); + info->q_typename = g_strdup(typename); + + QAPI_LIST_PREPEND(*cpu_list, info); +} + +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) +{ + CpuDefinitionInfoList *cpu_list = NULL; + GSList *list; + + list = object_class_get_list(TYPE_ARM_CPU, false); + g_slist_foreach(list, arm_cpu_add_definition, &cpu_list); + g_slist_free(list); + + return cpu_list; +} diff --git a/target/arm/common-semi-target.h b/target/arm/common-semi-target.h new file mode 100644 index 0000000000..da51f2d7f5 --- /dev/null +++ b/target/arm/common-semi-target.h @@ -0,0 +1,60 @@ +/* + * Target-specific parts of semihosting/arm-compat-semi.c. + * + * Copyright (c) 2005, 2007 CodeSourcery. + * Copyright (c) 2019, 2022 Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TARGET_ARM_COMMON_SEMI_TARGET_H +#define TARGET_ARM_COMMON_SEMI_TARGET_H + +#include "target/arm/cpu-qom.h" + +static inline target_ulong common_semi_arg(CPUState *cs, int argno) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + if (is_a64(env)) { + return env->xregs[argno]; + } else { + return env->regs[argno]; + } +} + +static inline void common_semi_set_ret(CPUState *cs, target_ulong ret) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + if (is_a64(env)) { + env->xregs[0] = ret; + } else { + env->regs[0] = ret; + } +} + +static inline bool common_semi_sys_exit_extended(CPUState *cs, int nr) +{ + return nr == TARGET_SYS_EXIT_EXTENDED || is_a64(cpu_env(cs)); +} + +static inline bool is_64bit_semihosting(CPUArchState *env) +{ + return is_a64(env); +} + +static inline target_ulong common_semi_stack_bottom(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + return is_a64(env) ? env->xregs[31] : env->regs[13]; +} + +static inline bool common_semi_has_synccache(CPUArchState *env) +{ + /* Ok for A64, invalid for A32/T32 */ + return is_a64(env); +} + +#endif diff --git a/target/arm/cortex-regs.c b/target/arm/cortex-regs.c new file mode 100644 index 0000000000..ae817b08dd --- /dev/null +++ b/target/arm/cortex-regs.c @@ -0,0 +1,76 @@ +/* + * ARM Cortex-A registers + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "cpregs.h" + + +static uint64_t l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + ARMCPU *cpu = env_archcpu(env); + + /* + * Number of cores is in [25:24]; otherwise we RAZ. + * If the board didn't configure the CPUs into clusters, + * we default to "all CPUs in one cluster", which might be + * more than the 4 that the hardware permits and which is + * all you can report in this two-bit field. Saturate to + * 0b11 (== 4 CPUs) rather than overflowing the field. + */ + return MIN(cpu->core_count - 1, 3) << 24; +} + +static const ARMCPRegInfo cortex_a72_a57_a53_cp_reginfo[] = { + { .name = "L2CTLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 11, .crm = 0, .opc2 = 2, + .access = PL1_RW, .readfn = l2ctlr_read, + .writefn = arm_cp_write_ignore }, + { .name = "L2CTLR", + .cp = 15, .opc1 = 1, .crn = 9, .crm = 0, .opc2 = 2, + .access = PL1_RW, .readfn = l2ctlr_read, + .writefn = arm_cp_write_ignore }, + { .name = "L2ECTLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 11, .crm = 0, .opc2 = 3, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "L2ECTLR", + .cp = 15, .opc1 = 1, .crn = 9, .crm = 0, .opc2 = 3, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "L2ACTLR", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUACTLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUACTLR", + .cp = 15, .opc1 = 0, .crm = 15, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, + { .name = "CPUECTLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUECTLR", + .cp = 15, .opc1 = 1, .crm = 15, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, + { .name = "CPUMERRSR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUMERRSR", + .cp = 15, .opc1 = 2, .crm = 15, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, + { .name = "L2MERRSR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 3, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "L2MERRSR", + .cp = 15, .opc1 = 3, .crm = 15, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, +}; + +void define_cortex_a72_a57_a53_cp_reginfo(ARMCPU *cpu) +{ + define_arm_cp_regs(cpu, cortex_a72_a57_a53_cp_reginfo); +} diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index d9b678c2f1..cc7c54378f 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -21,6 +21,9 @@ #ifndef TARGET_ARM_CPREGS_H #define TARGET_ARM_CPREGS_H +#include "hw/registerfields.h" +#include "target/arm/kvm-consts.h" + /* * ARMCPRegInfo type field bits: */ @@ -67,8 +70,8 @@ enum { ARM_CP_ALIAS = 1 << 8, /* * Flag: Register does I/O and therefore its accesses need to be marked - * with gen_io_start() and also end the TB. In particular, registers which - * implement clocks or timers require this. + * with translator_io_start() and also end the TB. In particular, + * registers which implement clocks or timers require this. */ ARM_CP_IO = 1 << 9, /* @@ -113,8 +116,116 @@ enum { ARM_CP_EL3_NO_EL2_UNDEF = 1 << 16, ARM_CP_EL3_NO_EL2_KEEP = 1 << 17, ARM_CP_EL3_NO_EL2_C_NZ = 1 << 18, + /* + * Flag: Access check for this sysreg is constrained by the + * ARM pseudocode function CheckSMEAccess(). + */ + ARM_CP_SME = 1 << 19, + /* + * Flag: one of the four EL2 registers which redirect to the + * equivalent EL1 register when FEAT_NV2 is enabled. + */ + ARM_CP_NV2_REDIRECT = 1 << 20, }; +/* + * Interface for defining coprocessor registers. + * Registers are defined in tables of arm_cp_reginfo structs + * which are passed to define_arm_cp_regs(). + */ + +/* + * When looking up a coprocessor register we look for it + * via an integer which encodes all of: + * coprocessor number + * Crn, Crm, opc1, opc2 fields + * 32 or 64 bit register (ie is it accessed via MRC/MCR + * or via MRRC/MCRR?) + * non-secure/secure bank (AArch32 only) + * We allow 4 bits for opc1 because MRRC/MCRR have a 4 bit field. + * (In this case crn and opc2 should be zero.) + * For AArch64, there is no 32/64 bit size distinction; + * instead all registers have a 2 bit op0, 3 bit op1 and op2, + * and 4 bit CRn and CRm. The encoding patterns are chosen + * to be easy to convert to and from the KVM encodings, and also + * so that the hashtable can contain both AArch32 and AArch64 + * registers (to allow for interprocessing where we might run + * 32 bit code on a 64 bit core). + */ +/* + * This bit is private to our hashtable cpreg; in KVM register + * IDs the AArch64/32 distinction is the KVM_REG_ARM/ARM64 + * in the upper bits of the 64 bit ID. + */ +#define CP_REG_AA64_SHIFT 28 +#define CP_REG_AA64_MASK (1 << CP_REG_AA64_SHIFT) + +/* + * To enable banking of coprocessor registers depending on ns-bit we + * add a bit to distinguish between secure and non-secure cpregs in the + * hashtable. + */ +#define CP_REG_NS_SHIFT 29 +#define CP_REG_NS_MASK (1 << CP_REG_NS_SHIFT) + +#define ENCODE_CP_REG(cp, is64, ns, crn, crm, opc1, opc2) \ + ((ns) << CP_REG_NS_SHIFT | ((cp) << 16) | ((is64) << 15) | \ + ((crn) << 11) | ((crm) << 7) | ((opc1) << 3) | (opc2)) + +#define ENCODE_AA64_CP_REG(cp, crn, crm, op0, op1, op2) \ + (CP_REG_AA64_MASK | \ + ((cp) << CP_REG_ARM_COPROC_SHIFT) | \ + ((op0) << CP_REG_ARM64_SYSREG_OP0_SHIFT) | \ + ((op1) << CP_REG_ARM64_SYSREG_OP1_SHIFT) | \ + ((crn) << CP_REG_ARM64_SYSREG_CRN_SHIFT) | \ + ((crm) << CP_REG_ARM64_SYSREG_CRM_SHIFT) | \ + ((op2) << CP_REG_ARM64_SYSREG_OP2_SHIFT)) + +/* + * Convert a full 64 bit KVM register ID to the truncated 32 bit + * version used as a key for the coprocessor register hashtable + */ +static inline uint32_t kvm_to_cpreg_id(uint64_t kvmid) +{ + uint32_t cpregid = kvmid; + if ((kvmid & CP_REG_ARCH_MASK) == CP_REG_ARM64) { + cpregid |= CP_REG_AA64_MASK; + } else { + if ((kvmid & CP_REG_SIZE_MASK) == CP_REG_SIZE_U64) { + cpregid |= (1 << 15); + } + + /* + * KVM is always non-secure so add the NS flag on AArch32 register + * entries. + */ + cpregid |= 1 << CP_REG_NS_SHIFT; + } + return cpregid; +} + +/* + * Convert a truncated 32 bit hashtable key into the full + * 64 bit KVM register ID. + */ +static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) +{ + uint64_t kvmid; + + if (cpregid & CP_REG_AA64_MASK) { + kvmid = cpregid & ~CP_REG_AA64_MASK; + kvmid |= CP_REG_SIZE_U64 | CP_REG_ARM64; + } else { + kvmid = cpregid & ~(1 << 15); + if (cpregid & (1 << 15)) { + kvmid |= CP_REG_SIZE_U64 | CP_REG_ARM; + } else { + kvmid |= CP_REG_SIZE_U32 | CP_REG_ARM; + } + } + return kvmid; +} + /* * Valid values for ARMCPRegInfo state field, indicating which of * the AArch32 and AArch64 execution states this register is visible in. @@ -219,12 +330,487 @@ typedef enum CPAccessResult { * Access fails and results in an exception syndrome 0x0 ("uncategorized"). * Note that this is not a catch-all case -- the set of cases which may * result in this failure is specifically defined by the architecture. + * This trap is always to the usual target EL, never directly to a + * specified target EL. */ CP_ACCESS_TRAP_UNCATEGORIZED = (2 << 2), - CP_ACCESS_TRAP_UNCATEGORIZED_EL2 = CP_ACCESS_TRAP_UNCATEGORIZED | 2, - CP_ACCESS_TRAP_UNCATEGORIZED_EL3 = CP_ACCESS_TRAP_UNCATEGORIZED | 3, } CPAccessResult; +/* Indexes into fgt_read[] */ +#define FGTREG_HFGRTR 0 +#define FGTREG_HDFGRTR 1 +/* Indexes into fgt_write[] */ +#define FGTREG_HFGWTR 0 +#define FGTREG_HDFGWTR 1 +/* Indexes into fgt_exec[] */ +#define FGTREG_HFGITR 0 + +FIELD(HFGRTR_EL2, AFSR0_EL1, 0, 1) +FIELD(HFGRTR_EL2, AFSR1_EL1, 1, 1) +FIELD(HFGRTR_EL2, AIDR_EL1, 2, 1) +FIELD(HFGRTR_EL2, AMAIR_EL1, 3, 1) +FIELD(HFGRTR_EL2, APDAKEY, 4, 1) +FIELD(HFGRTR_EL2, APDBKEY, 5, 1) +FIELD(HFGRTR_EL2, APGAKEY, 6, 1) +FIELD(HFGRTR_EL2, APIAKEY, 7, 1) +FIELD(HFGRTR_EL2, APIBKEY, 8, 1) +FIELD(HFGRTR_EL2, CCSIDR_EL1, 9, 1) +FIELD(HFGRTR_EL2, CLIDR_EL1, 10, 1) +FIELD(HFGRTR_EL2, CONTEXTIDR_EL1, 11, 1) +FIELD(HFGRTR_EL2, CPACR_EL1, 12, 1) +FIELD(HFGRTR_EL2, CSSELR_EL1, 13, 1) +FIELD(HFGRTR_EL2, CTR_EL0, 14, 1) +FIELD(HFGRTR_EL2, DCZID_EL0, 15, 1) +FIELD(HFGRTR_EL2, ESR_EL1, 16, 1) +FIELD(HFGRTR_EL2, FAR_EL1, 17, 1) +FIELD(HFGRTR_EL2, ISR_EL1, 18, 1) +FIELD(HFGRTR_EL2, LORC_EL1, 19, 1) +FIELD(HFGRTR_EL2, LOREA_EL1, 20, 1) +FIELD(HFGRTR_EL2, LORID_EL1, 21, 1) +FIELD(HFGRTR_EL2, LORN_EL1, 22, 1) +FIELD(HFGRTR_EL2, LORSA_EL1, 23, 1) +FIELD(HFGRTR_EL2, MAIR_EL1, 24, 1) +FIELD(HFGRTR_EL2, MIDR_EL1, 25, 1) +FIELD(HFGRTR_EL2, MPIDR_EL1, 26, 1) +FIELD(HFGRTR_EL2, PAR_EL1, 27, 1) +FIELD(HFGRTR_EL2, REVIDR_EL1, 28, 1) +FIELD(HFGRTR_EL2, SCTLR_EL1, 29, 1) +FIELD(HFGRTR_EL2, SCXTNUM_EL1, 30, 1) +FIELD(HFGRTR_EL2, SCXTNUM_EL0, 31, 1) +FIELD(HFGRTR_EL2, TCR_EL1, 32, 1) +FIELD(HFGRTR_EL2, TPIDR_EL1, 33, 1) +FIELD(HFGRTR_EL2, TPIDRRO_EL0, 34, 1) +FIELD(HFGRTR_EL2, TPIDR_EL0, 35, 1) +FIELD(HFGRTR_EL2, TTBR0_EL1, 36, 1) +FIELD(HFGRTR_EL2, TTBR1_EL1, 37, 1) +FIELD(HFGRTR_EL2, VBAR_EL1, 38, 1) +FIELD(HFGRTR_EL2, ICC_IGRPENN_EL1, 39, 1) +FIELD(HFGRTR_EL2, ERRIDR_EL1, 40, 1) +FIELD(HFGRTR_EL2, ERRSELR_EL1, 41, 1) +FIELD(HFGRTR_EL2, ERXFR_EL1, 42, 1) +FIELD(HFGRTR_EL2, ERXCTLR_EL1, 43, 1) +FIELD(HFGRTR_EL2, ERXSTATUS_EL1, 44, 1) +FIELD(HFGRTR_EL2, ERXMISCN_EL1, 45, 1) +FIELD(HFGRTR_EL2, ERXPFGF_EL1, 46, 1) +FIELD(HFGRTR_EL2, ERXPFGCTL_EL1, 47, 1) +FIELD(HFGRTR_EL2, ERXPFGCDN_EL1, 48, 1) +FIELD(HFGRTR_EL2, ERXADDR_EL1, 49, 1) +FIELD(HFGRTR_EL2, NACCDATA_EL1, 50, 1) +/* 51-53: RES0 */ +FIELD(HFGRTR_EL2, NSMPRI_EL1, 54, 1) +FIELD(HFGRTR_EL2, NTPIDR2_EL0, 55, 1) +/* 56-63: RES0 */ + +/* These match HFGRTR but bits for RO registers are RES0 */ +FIELD(HFGWTR_EL2, AFSR0_EL1, 0, 1) +FIELD(HFGWTR_EL2, AFSR1_EL1, 1, 1) +FIELD(HFGWTR_EL2, AMAIR_EL1, 3, 1) +FIELD(HFGWTR_EL2, APDAKEY, 4, 1) +FIELD(HFGWTR_EL2, APDBKEY, 5, 1) +FIELD(HFGWTR_EL2, APGAKEY, 6, 1) +FIELD(HFGWTR_EL2, APIAKEY, 7, 1) +FIELD(HFGWTR_EL2, APIBKEY, 8, 1) +FIELD(HFGWTR_EL2, CONTEXTIDR_EL1, 11, 1) +FIELD(HFGWTR_EL2, CPACR_EL1, 12, 1) +FIELD(HFGWTR_EL2, CSSELR_EL1, 13, 1) +FIELD(HFGWTR_EL2, ESR_EL1, 16, 1) +FIELD(HFGWTR_EL2, FAR_EL1, 17, 1) +FIELD(HFGWTR_EL2, LORC_EL1, 19, 1) +FIELD(HFGWTR_EL2, LOREA_EL1, 20, 1) +FIELD(HFGWTR_EL2, LORN_EL1, 22, 1) +FIELD(HFGWTR_EL2, LORSA_EL1, 23, 1) +FIELD(HFGWTR_EL2, MAIR_EL1, 24, 1) +FIELD(HFGWTR_EL2, PAR_EL1, 27, 1) +FIELD(HFGWTR_EL2, SCTLR_EL1, 29, 1) +FIELD(HFGWTR_EL2, SCXTNUM_EL1, 30, 1) +FIELD(HFGWTR_EL2, SCXTNUM_EL0, 31, 1) +FIELD(HFGWTR_EL2, TCR_EL1, 32, 1) +FIELD(HFGWTR_EL2, TPIDR_EL1, 33, 1) +FIELD(HFGWTR_EL2, TPIDRRO_EL0, 34, 1) +FIELD(HFGWTR_EL2, TPIDR_EL0, 35, 1) +FIELD(HFGWTR_EL2, TTBR0_EL1, 36, 1) +FIELD(HFGWTR_EL2, TTBR1_EL1, 37, 1) +FIELD(HFGWTR_EL2, VBAR_EL1, 38, 1) +FIELD(HFGWTR_EL2, ICC_IGRPENN_EL1, 39, 1) +FIELD(HFGWTR_EL2, ERRSELR_EL1, 41, 1) +FIELD(HFGWTR_EL2, ERXCTLR_EL1, 43, 1) +FIELD(HFGWTR_EL2, ERXSTATUS_EL1, 44, 1) +FIELD(HFGWTR_EL2, ERXMISCN_EL1, 45, 1) +FIELD(HFGWTR_EL2, ERXPFGCTL_EL1, 47, 1) +FIELD(HFGWTR_EL2, ERXPFGCDN_EL1, 48, 1) +FIELD(HFGWTR_EL2, ERXADDR_EL1, 49, 1) +FIELD(HFGWTR_EL2, NACCDATA_EL1, 50, 1) +FIELD(HFGWTR_EL2, NSMPRI_EL1, 54, 1) +FIELD(HFGWTR_EL2, NTPIDR2_EL0, 55, 1) + +FIELD(HFGITR_EL2, ICIALLUIS, 0, 1) +FIELD(HFGITR_EL2, ICIALLU, 1, 1) +FIELD(HFGITR_EL2, ICIVAU, 2, 1) +FIELD(HFGITR_EL2, DCIVAC, 3, 1) +FIELD(HFGITR_EL2, DCISW, 4, 1) +FIELD(HFGITR_EL2, DCCSW, 5, 1) +FIELD(HFGITR_EL2, DCCISW, 6, 1) +FIELD(HFGITR_EL2, DCCVAU, 7, 1) +FIELD(HFGITR_EL2, DCCVAP, 8, 1) +FIELD(HFGITR_EL2, DCCVADP, 9, 1) +FIELD(HFGITR_EL2, DCCIVAC, 10, 1) +FIELD(HFGITR_EL2, DCZVA, 11, 1) +FIELD(HFGITR_EL2, ATS1E1R, 12, 1) +FIELD(HFGITR_EL2, ATS1E1W, 13, 1) +FIELD(HFGITR_EL2, ATS1E0R, 14, 1) +FIELD(HFGITR_EL2, ATS1E0W, 15, 1) +FIELD(HFGITR_EL2, ATS1E1RP, 16, 1) +FIELD(HFGITR_EL2, ATS1E1WP, 17, 1) +FIELD(HFGITR_EL2, TLBIVMALLE1OS, 18, 1) +FIELD(HFGITR_EL2, TLBIVAE1OS, 19, 1) +FIELD(HFGITR_EL2, TLBIASIDE1OS, 20, 1) +FIELD(HFGITR_EL2, TLBIVAAE1OS, 21, 1) +FIELD(HFGITR_EL2, TLBIVALE1OS, 22, 1) +FIELD(HFGITR_EL2, TLBIVAALE1OS, 23, 1) +FIELD(HFGITR_EL2, TLBIRVAE1OS, 24, 1) +FIELD(HFGITR_EL2, TLBIRVAAE1OS, 25, 1) +FIELD(HFGITR_EL2, TLBIRVALE1OS, 26, 1) +FIELD(HFGITR_EL2, TLBIRVAALE1OS, 27, 1) +FIELD(HFGITR_EL2, TLBIVMALLE1IS, 28, 1) +FIELD(HFGITR_EL2, TLBIVAE1IS, 29, 1) +FIELD(HFGITR_EL2, TLBIASIDE1IS, 30, 1) +FIELD(HFGITR_EL2, TLBIVAAE1IS, 31, 1) +FIELD(HFGITR_EL2, TLBIVALE1IS, 32, 1) +FIELD(HFGITR_EL2, TLBIVAALE1IS, 33, 1) +FIELD(HFGITR_EL2, TLBIRVAE1IS, 34, 1) +FIELD(HFGITR_EL2, TLBIRVAAE1IS, 35, 1) +FIELD(HFGITR_EL2, TLBIRVALE1IS, 36, 1) +FIELD(HFGITR_EL2, TLBIRVAALE1IS, 37, 1) +FIELD(HFGITR_EL2, TLBIRVAE1, 38, 1) +FIELD(HFGITR_EL2, TLBIRVAAE1, 39, 1) +FIELD(HFGITR_EL2, TLBIRVALE1, 40, 1) +FIELD(HFGITR_EL2, TLBIRVAALE1, 41, 1) +FIELD(HFGITR_EL2, TLBIVMALLE1, 42, 1) +FIELD(HFGITR_EL2, TLBIVAE1, 43, 1) +FIELD(HFGITR_EL2, TLBIASIDE1, 44, 1) +FIELD(HFGITR_EL2, TLBIVAAE1, 45, 1) +FIELD(HFGITR_EL2, TLBIVALE1, 46, 1) +FIELD(HFGITR_EL2, TLBIVAALE1, 47, 1) +FIELD(HFGITR_EL2, CFPRCTX, 48, 1) +FIELD(HFGITR_EL2, DVPRCTX, 49, 1) +FIELD(HFGITR_EL2, CPPRCTX, 50, 1) +FIELD(HFGITR_EL2, ERET, 51, 1) +FIELD(HFGITR_EL2, SVC_EL0, 52, 1) +FIELD(HFGITR_EL2, SVC_EL1, 53, 1) +FIELD(HFGITR_EL2, DCCVAC, 54, 1) +FIELD(HFGITR_EL2, NBRBINJ, 55, 1) +FIELD(HFGITR_EL2, NBRBIALL, 56, 1) + +FIELD(HDFGRTR_EL2, DBGBCRN_EL1, 0, 1) +FIELD(HDFGRTR_EL2, DBGBVRN_EL1, 1, 1) +FIELD(HDFGRTR_EL2, DBGWCRN_EL1, 2, 1) +FIELD(HDFGRTR_EL2, DBGWVRN_EL1, 3, 1) +FIELD(HDFGRTR_EL2, MDSCR_EL1, 4, 1) +FIELD(HDFGRTR_EL2, DBGCLAIM, 5, 1) +FIELD(HDFGRTR_EL2, DBGAUTHSTATUS_EL1, 6, 1) +FIELD(HDFGRTR_EL2, DBGPRCR_EL1, 7, 1) +/* 8: RES0: OSLAR_EL1 is WO */ +FIELD(HDFGRTR_EL2, OSLSR_EL1, 9, 1) +FIELD(HDFGRTR_EL2, OSECCR_EL1, 10, 1) +FIELD(HDFGRTR_EL2, OSDLR_EL1, 11, 1) +FIELD(HDFGRTR_EL2, PMEVCNTRN_EL0, 12, 1) +FIELD(HDFGRTR_EL2, PMEVTYPERN_EL0, 13, 1) +FIELD(HDFGRTR_EL2, PMCCFILTR_EL0, 14, 1) +FIELD(HDFGRTR_EL2, PMCCNTR_EL0, 15, 1) +FIELD(HDFGRTR_EL2, PMCNTEN, 16, 1) +FIELD(HDFGRTR_EL2, PMINTEN, 17, 1) +FIELD(HDFGRTR_EL2, PMOVS, 18, 1) +FIELD(HDFGRTR_EL2, PMSELR_EL0, 19, 1) +/* 20: RES0: PMSWINC_EL0 is WO */ +/* 21: RES0: PMCR_EL0 is WO */ +FIELD(HDFGRTR_EL2, PMMIR_EL1, 22, 1) +FIELD(HDFGRTR_EL2, PMBLIMITR_EL1, 23, 1) +FIELD(HDFGRTR_EL2, PMBPTR_EL1, 24, 1) +FIELD(HDFGRTR_EL2, PMBSR_EL1, 25, 1) +FIELD(HDFGRTR_EL2, PMSCR_EL1, 26, 1) +FIELD(HDFGRTR_EL2, PMSEVFR_EL1, 27, 1) +FIELD(HDFGRTR_EL2, PMSFCR_EL1, 28, 1) +FIELD(HDFGRTR_EL2, PMSICR_EL1, 29, 1) +FIELD(HDFGRTR_EL2, PMSIDR_EL1, 30, 1) +FIELD(HDFGRTR_EL2, PMSIRR_EL1, 31, 1) +FIELD(HDFGRTR_EL2, PMSLATFR_EL1, 32, 1) +FIELD(HDFGRTR_EL2, TRC, 33, 1) +FIELD(HDFGRTR_EL2, TRCAUTHSTATUS, 34, 1) +FIELD(HDFGRTR_EL2, TRCAUXCTLR, 35, 1) +FIELD(HDFGRTR_EL2, TRCCLAIM, 36, 1) +FIELD(HDFGRTR_EL2, TRCCNTVRn, 37, 1) +/* 38, 39: RES0 */ +FIELD(HDFGRTR_EL2, TRCID, 40, 1) +FIELD(HDFGRTR_EL2, TRCIMSPECN, 41, 1) +/* 42: RES0: TRCOSLAR is WO */ +FIELD(HDFGRTR_EL2, TRCOSLSR, 43, 1) +FIELD(HDFGRTR_EL2, TRCPRGCTLR, 44, 1) +FIELD(HDFGRTR_EL2, TRCSEQSTR, 45, 1) +FIELD(HDFGRTR_EL2, TRCSSCSRN, 46, 1) +FIELD(HDFGRTR_EL2, TRCSTATR, 47, 1) +FIELD(HDFGRTR_EL2, TRCVICTLR, 48, 1) +/* 49: RES0: TRFCR_EL1 is WO */ +FIELD(HDFGRTR_EL2, TRBBASER_EL1, 50, 1) +FIELD(HDFGRTR_EL2, TRBIDR_EL1, 51, 1) +FIELD(HDFGRTR_EL2, TRBLIMITR_EL1, 52, 1) +FIELD(HDFGRTR_EL2, TRBMAR_EL1, 53, 1) +FIELD(HDFGRTR_EL2, TRBPTR_EL1, 54, 1) +FIELD(HDFGRTR_EL2, TRBSR_EL1, 55, 1) +FIELD(HDFGRTR_EL2, TRBTRG_EL1, 56, 1) +FIELD(HDFGRTR_EL2, PMUSERENR_EL0, 57, 1) +FIELD(HDFGRTR_EL2, PMCEIDN_EL0, 58, 1) +FIELD(HDFGRTR_EL2, NBRBIDR, 59, 1) +FIELD(HDFGRTR_EL2, NBRBCTL, 60, 1) +FIELD(HDFGRTR_EL2, NBRBDATA, 61, 1) +FIELD(HDFGRTR_EL2, NPMSNEVFR_EL1, 62, 1) +FIELD(HDFGRTR_EL2, PMBIDR_EL1, 63, 1) + +/* + * These match HDFGRTR_EL2, but bits for RO registers are RES0. + * A few bits are for WO registers, where the HDFGRTR_EL2 bit is RES0. + */ +FIELD(HDFGWTR_EL2, DBGBCRN_EL1, 0, 1) +FIELD(HDFGWTR_EL2, DBGBVRN_EL1, 1, 1) +FIELD(HDFGWTR_EL2, DBGWCRN_EL1, 2, 1) +FIELD(HDFGWTR_EL2, DBGWVRN_EL1, 3, 1) +FIELD(HDFGWTR_EL2, MDSCR_EL1, 4, 1) +FIELD(HDFGWTR_EL2, DBGCLAIM, 5, 1) +FIELD(HDFGWTR_EL2, DBGPRCR_EL1, 7, 1) +FIELD(HDFGWTR_EL2, OSLAR_EL1, 8, 1) +FIELD(HDFGWTR_EL2, OSLSR_EL1, 9, 1) +FIELD(HDFGWTR_EL2, OSECCR_EL1, 10, 1) +FIELD(HDFGWTR_EL2, OSDLR_EL1, 11, 1) +FIELD(HDFGWTR_EL2, PMEVCNTRN_EL0, 12, 1) +FIELD(HDFGWTR_EL2, PMEVTYPERN_EL0, 13, 1) +FIELD(HDFGWTR_EL2, PMCCFILTR_EL0, 14, 1) +FIELD(HDFGWTR_EL2, PMCCNTR_EL0, 15, 1) +FIELD(HDFGWTR_EL2, PMCNTEN, 16, 1) +FIELD(HDFGWTR_EL2, PMINTEN, 17, 1) +FIELD(HDFGWTR_EL2, PMOVS, 18, 1) +FIELD(HDFGWTR_EL2, PMSELR_EL0, 19, 1) +FIELD(HDFGWTR_EL2, PMSWINC_EL0, 20, 1) +FIELD(HDFGWTR_EL2, PMCR_EL0, 21, 1) +FIELD(HDFGWTR_EL2, PMBLIMITR_EL1, 23, 1) +FIELD(HDFGWTR_EL2, PMBPTR_EL1, 24, 1) +FIELD(HDFGWTR_EL2, PMBSR_EL1, 25, 1) +FIELD(HDFGWTR_EL2, PMSCR_EL1, 26, 1) +FIELD(HDFGWTR_EL2, PMSEVFR_EL1, 27, 1) +FIELD(HDFGWTR_EL2, PMSFCR_EL1, 28, 1) +FIELD(HDFGWTR_EL2, PMSICR_EL1, 29, 1) +FIELD(HDFGWTR_EL2, PMSIRR_EL1, 31, 1) +FIELD(HDFGWTR_EL2, PMSLATFR_EL1, 32, 1) +FIELD(HDFGWTR_EL2, TRC, 33, 1) +FIELD(HDFGWTR_EL2, TRCAUXCTLR, 35, 1) +FIELD(HDFGWTR_EL2, TRCCLAIM, 36, 1) +FIELD(HDFGWTR_EL2, TRCCNTVRn, 37, 1) +FIELD(HDFGWTR_EL2, TRCIMSPECN, 41, 1) +FIELD(HDFGWTR_EL2, TRCOSLAR, 42, 1) +FIELD(HDFGWTR_EL2, TRCPRGCTLR, 44, 1) +FIELD(HDFGWTR_EL2, TRCSEQSTR, 45, 1) +FIELD(HDFGWTR_EL2, TRCSSCSRN, 46, 1) +FIELD(HDFGWTR_EL2, TRCVICTLR, 48, 1) +FIELD(HDFGWTR_EL2, TRFCR_EL1, 49, 1) +FIELD(HDFGWTR_EL2, TRBBASER_EL1, 50, 1) +FIELD(HDFGWTR_EL2, TRBLIMITR_EL1, 52, 1) +FIELD(HDFGWTR_EL2, TRBMAR_EL1, 53, 1) +FIELD(HDFGWTR_EL2, TRBPTR_EL1, 54, 1) +FIELD(HDFGWTR_EL2, TRBSR_EL1, 55, 1) +FIELD(HDFGWTR_EL2, TRBTRG_EL1, 56, 1) +FIELD(HDFGWTR_EL2, PMUSERENR_EL0, 57, 1) +FIELD(HDFGWTR_EL2, NBRBCTL, 60, 1) +FIELD(HDFGWTR_EL2, NBRBDATA, 61, 1) +FIELD(HDFGWTR_EL2, NPMSNEVFR_EL1, 62, 1) + +/* Which fine-grained trap bit register to check, if any */ +FIELD(FGT, TYPE, 10, 3) +FIELD(FGT, REV, 9, 1) /* Is bit sense reversed? */ +FIELD(FGT, IDX, 6, 3) /* Index within a uint64_t[] array */ +FIELD(FGT, BITPOS, 0, 6) /* Bit position within the uint64_t */ + +/* + * Macros to define FGT_##bitname enum constants to use in ARMCPRegInfo::fgt + * fields. We assume for brevity's sake that there are no duplicated + * bit names across the various FGT registers. + */ +#define DO_BIT(REG, BITNAME) \ + FGT_##BITNAME = FGT_##REG | R_##REG##_EL2_##BITNAME##_SHIFT + +/* Some bits have reversed sense, so 0 means trap and 1 means not */ +#define DO_REV_BIT(REG, BITNAME) \ + FGT_##BITNAME = FGT_##REG | FGT_REV | R_##REG##_EL2_##BITNAME##_SHIFT + +typedef enum FGTBit { + /* + * These bits tell us which register arrays to use: + * if FGT_R is set then reads are checked against fgt_read[]; + * if FGT_W is set then writes are checked against fgt_write[]; + * if FGT_EXEC is set then all accesses are checked against fgt_exec[]. + * + * For almost all bits in the R/W register pairs, the bit exists in + * both registers for a RW register, in HFGRTR/HDFGRTR for a RO register + * with the corresponding HFGWTR/HDFGTWTR bit being RES0, and vice-versa + * for a WO register. There are unfortunately a couple of exceptions + * (PMCR_EL0, TRFCR_EL1) where the register being trapped is RW but + * the FGT system only allows trapping of writes, not reads. + * + * Note that we arrange these bits so that a 0 FGTBit means "no trap". + */ + FGT_R = 1 << R_FGT_TYPE_SHIFT, + FGT_W = 2 << R_FGT_TYPE_SHIFT, + FGT_EXEC = 4 << R_FGT_TYPE_SHIFT, + FGT_RW = FGT_R | FGT_W, + /* Bit to identify whether trap bit is reversed sense */ + FGT_REV = R_FGT_REV_MASK, + + /* + * If a bit exists in HFGRTR/HDFGRTR then either the register being + * trapped is RO or the bit also exists in HFGWTR/HDFGWTR, so we either + * want to trap for both reads and writes or else it's harmless to mark + * it as trap-on-writes. + * If a bit exists only in HFGWTR/HDFGWTR then either the register being + * trapped is WO, or else it is one of the two oddball special cases + * which are RW but have only a write trap. We mark these as only + * FGT_W so we get the right behaviour for those special cases. + * (If a bit was added in future that provided only a read trap for an + * RW register we'd need to do something special to get the FGT_R bit + * only. But this seems unlikely to happen.) + * + * So for the DO_BIT/DO_REV_BIT macros: use FGT_HFGRTR/FGT_HDFGRTR if + * the bit exists in that register. Otherwise use FGT_HFGWTR/FGT_HDFGWTR. + */ + FGT_HFGRTR = FGT_RW | (FGTREG_HFGRTR << R_FGT_IDX_SHIFT), + FGT_HFGWTR = FGT_W | (FGTREG_HFGWTR << R_FGT_IDX_SHIFT), + FGT_HDFGRTR = FGT_RW | (FGTREG_HDFGRTR << R_FGT_IDX_SHIFT), + FGT_HDFGWTR = FGT_W | (FGTREG_HDFGWTR << R_FGT_IDX_SHIFT), + FGT_HFGITR = FGT_EXEC | (FGTREG_HFGITR << R_FGT_IDX_SHIFT), + + /* Trap bits in HFGRTR_EL2 / HFGWTR_EL2, starting from bit 0. */ + DO_BIT(HFGRTR, AFSR0_EL1), + DO_BIT(HFGRTR, AFSR1_EL1), + DO_BIT(HFGRTR, AIDR_EL1), + DO_BIT(HFGRTR, AMAIR_EL1), + DO_BIT(HFGRTR, APDAKEY), + DO_BIT(HFGRTR, APDBKEY), + DO_BIT(HFGRTR, APGAKEY), + DO_BIT(HFGRTR, APIAKEY), + DO_BIT(HFGRTR, APIBKEY), + DO_BIT(HFGRTR, CCSIDR_EL1), + DO_BIT(HFGRTR, CLIDR_EL1), + DO_BIT(HFGRTR, CONTEXTIDR_EL1), + DO_BIT(HFGRTR, CPACR_EL1), + DO_BIT(HFGRTR, CSSELR_EL1), + DO_BIT(HFGRTR, CTR_EL0), + DO_BIT(HFGRTR, DCZID_EL0), + DO_BIT(HFGRTR, ESR_EL1), + DO_BIT(HFGRTR, FAR_EL1), + DO_BIT(HFGRTR, ISR_EL1), + DO_BIT(HFGRTR, LORC_EL1), + DO_BIT(HFGRTR, LOREA_EL1), + DO_BIT(HFGRTR, LORID_EL1), + DO_BIT(HFGRTR, LORN_EL1), + DO_BIT(HFGRTR, LORSA_EL1), + DO_BIT(HFGRTR, MAIR_EL1), + DO_BIT(HFGRTR, MIDR_EL1), + DO_BIT(HFGRTR, MPIDR_EL1), + DO_BIT(HFGRTR, PAR_EL1), + DO_BIT(HFGRTR, REVIDR_EL1), + DO_BIT(HFGRTR, SCTLR_EL1), + DO_BIT(HFGRTR, SCXTNUM_EL1), + DO_BIT(HFGRTR, SCXTNUM_EL0), + DO_BIT(HFGRTR, TCR_EL1), + DO_BIT(HFGRTR, TPIDR_EL1), + DO_BIT(HFGRTR, TPIDRRO_EL0), + DO_BIT(HFGRTR, TPIDR_EL0), + DO_BIT(HFGRTR, TTBR0_EL1), + DO_BIT(HFGRTR, TTBR1_EL1), + DO_BIT(HFGRTR, VBAR_EL1), + DO_BIT(HFGRTR, ICC_IGRPENN_EL1), + DO_BIT(HFGRTR, ERRIDR_EL1), + DO_REV_BIT(HFGRTR, NSMPRI_EL1), + DO_REV_BIT(HFGRTR, NTPIDR2_EL0), + + /* Trap bits in HDFGRTR_EL2 / HDFGWTR_EL2, starting from bit 0. */ + DO_BIT(HDFGRTR, DBGBCRN_EL1), + DO_BIT(HDFGRTR, DBGBVRN_EL1), + DO_BIT(HDFGRTR, DBGWCRN_EL1), + DO_BIT(HDFGRTR, DBGWVRN_EL1), + DO_BIT(HDFGRTR, MDSCR_EL1), + DO_BIT(HDFGRTR, DBGCLAIM), + DO_BIT(HDFGWTR, OSLAR_EL1), + DO_BIT(HDFGRTR, OSLSR_EL1), + DO_BIT(HDFGRTR, OSECCR_EL1), + DO_BIT(HDFGRTR, OSDLR_EL1), + DO_BIT(HDFGRTR, PMEVCNTRN_EL0), + DO_BIT(HDFGRTR, PMEVTYPERN_EL0), + DO_BIT(HDFGRTR, PMCCFILTR_EL0), + DO_BIT(HDFGRTR, PMCCNTR_EL0), + DO_BIT(HDFGRTR, PMCNTEN), + DO_BIT(HDFGRTR, PMINTEN), + DO_BIT(HDFGRTR, PMOVS), + DO_BIT(HDFGRTR, PMSELR_EL0), + DO_BIT(HDFGWTR, PMSWINC_EL0), + DO_BIT(HDFGWTR, PMCR_EL0), + DO_BIT(HDFGRTR, PMMIR_EL1), + DO_BIT(HDFGRTR, PMCEIDN_EL0), + + /* Trap bits in HFGITR_EL2, starting from bit 0 */ + DO_BIT(HFGITR, ICIALLUIS), + DO_BIT(HFGITR, ICIALLU), + DO_BIT(HFGITR, ICIVAU), + DO_BIT(HFGITR, DCIVAC), + DO_BIT(HFGITR, DCISW), + DO_BIT(HFGITR, DCCSW), + DO_BIT(HFGITR, DCCISW), + DO_BIT(HFGITR, DCCVAU), + DO_BIT(HFGITR, DCCVAP), + DO_BIT(HFGITR, DCCVADP), + DO_BIT(HFGITR, DCCIVAC), + DO_BIT(HFGITR, DCZVA), + DO_BIT(HFGITR, ATS1E1R), + DO_BIT(HFGITR, ATS1E1W), + DO_BIT(HFGITR, ATS1E0R), + DO_BIT(HFGITR, ATS1E0W), + DO_BIT(HFGITR, ATS1E1RP), + DO_BIT(HFGITR, ATS1E1WP), + DO_BIT(HFGITR, TLBIVMALLE1OS), + DO_BIT(HFGITR, TLBIVAE1OS), + DO_BIT(HFGITR, TLBIASIDE1OS), + DO_BIT(HFGITR, TLBIVAAE1OS), + DO_BIT(HFGITR, TLBIVALE1OS), + DO_BIT(HFGITR, TLBIVAALE1OS), + DO_BIT(HFGITR, TLBIRVAE1OS), + DO_BIT(HFGITR, TLBIRVAAE1OS), + DO_BIT(HFGITR, TLBIRVALE1OS), + DO_BIT(HFGITR, TLBIRVAALE1OS), + DO_BIT(HFGITR, TLBIVMALLE1IS), + DO_BIT(HFGITR, TLBIVAE1IS), + DO_BIT(HFGITR, TLBIASIDE1IS), + DO_BIT(HFGITR, TLBIVAAE1IS), + DO_BIT(HFGITR, TLBIVALE1IS), + DO_BIT(HFGITR, TLBIVAALE1IS), + DO_BIT(HFGITR, TLBIRVAE1IS), + DO_BIT(HFGITR, TLBIRVAAE1IS), + DO_BIT(HFGITR, TLBIRVALE1IS), + DO_BIT(HFGITR, TLBIRVAALE1IS), + DO_BIT(HFGITR, TLBIRVAE1), + DO_BIT(HFGITR, TLBIRVAAE1), + DO_BIT(HFGITR, TLBIRVALE1), + DO_BIT(HFGITR, TLBIRVAALE1), + DO_BIT(HFGITR, TLBIVMALLE1), + DO_BIT(HFGITR, TLBIVAE1), + DO_BIT(HFGITR, TLBIASIDE1), + DO_BIT(HFGITR, TLBIVAAE1), + DO_BIT(HFGITR, TLBIVALE1), + DO_BIT(HFGITR, TLBIVAALE1), + DO_BIT(HFGITR, CFPRCTX), + DO_BIT(HFGITR, DVPRCTX), + DO_BIT(HFGITR, CPPRCTX), + DO_BIT(HFGITR, DCCVAC), +} FGTBit; + +#undef DO_BIT +#undef DO_REV_BIT + typedef struct ARMCPRegInfo ARMCPRegInfo; /* @@ -243,6 +829,11 @@ typedef void CPResetFn(CPUARMState *env, const ARMCPRegInfo *opaque); #define CP_ANY 0xff +/* Flags in the high bits of nv2_redirect_offset */ +#define NV2_REDIR_NV1 0x4000 /* Only redirect when HCR_EL2.NV1 == 1 */ +#define NV2_REDIR_NO_NV1 0x8000 /* Only redirect when HCR_EL2.NV1 == 0 */ +#define NV2_REDIR_FLAG_MASK 0xc000 + /* Definition of an ARM coprocessor register */ struct ARMCPRegInfo { /* Name of register (useful mainly for debugging, need not be unique) */ @@ -279,6 +870,18 @@ struct ARMCPRegInfo { CPAccessRights access; /* Security state: ARM_CP_SECSTATE_* bits/values */ CPSecureState secure; + /* + * Which fine-grained trap register bit to check, if any. This + * value encodes both the trap register and bit within it. + */ + FGTBit fgt; + + /* + * Offset from VNCR_EL2 when FEAT_NV2 redirects access to memory; + * may include an NV2_REDIR_* flag. + */ + uint32_t nv2_redirect_offset; + /* * The opaque pointer passed to define_arm_cp_regs_with_opaque() when * this register was defined: can be used to hand data through to the @@ -354,7 +957,7 @@ struct ARMCPRegInfo { CPResetFn *resetfn; /* - * "Original" writefn and readfn. + * "Original" readfn, writefn, accessfn. * 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 @@ -365,6 +968,7 @@ struct ARMCPRegInfo { */ CPReadFn *orig_readfn; CPWriteFn *orig_writefn; + CPAccessFn *orig_accessfn; }; /* @@ -437,6 +1041,9 @@ void arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri, /* CPReadFn that can be used for read-as-zero behaviour */ uint64_t arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri); +/* CPWriteFn that just writes the value to ri->fieldoffset */ +void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value); + /* * CPResetFn that does nothing, for use if no reset is required even * if fieldoffset is non zero. @@ -485,4 +1092,46 @@ static inline bool arm_cpreg_in_idspace(const ARMCPRegInfo *ri) ri->crn, ri->crm); } +#ifdef CONFIG_USER_ONLY +static inline void define_cortex_a72_a57_a53_cp_reginfo(ARMCPU *cpu) { } +#else +void define_cortex_a72_a57_a53_cp_reginfo(ARMCPU *cpu); +#endif + +CPAccessResult access_tvm_trvm(CPUARMState *, const ARMCPRegInfo *, bool); + +/** + * arm_cpreg_trap_in_nv: Return true if cpreg traps in nested virtualization + * + * Return true if this cpreg is one which should be trapped to EL2 if + * it is executed at EL1 when nested virtualization is enabled via HCR_EL2.NV. + */ +static inline bool arm_cpreg_traps_in_nv(const ARMCPRegInfo *ri) +{ + /* + * The Arm ARM defines the registers to be trapped in terms of + * their names (I_TZTZL). However the underlying principle is "if + * it would UNDEF at EL1 but work at EL2 then it should trap", and + * the way the encoding of sysregs and system instructions is done + * means that the right set of registers is exactly those where + * the opc1 field is 4 or 5. (You can see this also in the assert + * we do that the opc1 field and the permissions mask line up in + * define_one_arm_cp_reg_with_opaque().) + * Checking the opc1 field is easier for us and avoids the problem + * that we do not consistently use the right architectural names + * for all sysregs, since we treat the name field as largely for debug. + * + * However we do this check, it is going to be at least potentially + * fragile to future new sysregs, but this seems the least likely + * to break. + * + * In particular, note that the released sysreg XML defines that + * the FEAT_MEC sysregs and instructions do not follow this FEAT_NV + * trapping rule, so we will need to add an ARM_CP_* flag to indicate + * "register does not trap on NV" to handle those if/when we implement + * FEAT_MEC. + */ + return ri->opc1 == 4 || ri->opc1 == 5; +} + #endif /* TARGET_ARM_CPREGS_H */ diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h new file mode 100644 index 0000000000..e5758d9fbc --- /dev/null +++ b/target/arm/cpu-features.h @@ -0,0 +1,1021 @@ +/* + * QEMU Arm CPU -- feature test functions + * + * Copyright (c) 2023 Linaro Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 TARGET_ARM_FEATURES_H +#define TARGET_ARM_FEATURES_H + +#include "hw/registerfields.h" + +/* + * 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_aa32_thumb_div(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) != 0; +} + +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_aa32_lob(const ARMISARegisters *id) +{ + /* (M-profile) low-overhead loops and branch future */ + return FIELD_EX32(id->id_isar0, ID_ISAR0, CMPBRANCH) >= 3; +} + +static inline bool isar_feature_aa32_jazelle(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar1, ID_ISAR1, JAZELLE) != 0; +} + +static inline bool isar_feature_aa32_aes(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, AES) != 0; +} + +static inline bool isar_feature_aa32_pmull(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, AES) > 1; +} + +static inline bool isar_feature_aa32_sha1(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, SHA1) != 0; +} + +static inline bool isar_feature_aa32_sha2(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, SHA2) != 0; +} + +static inline bool isar_feature_aa32_crc32(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, CRC32) != 0; +} + +static inline bool isar_feature_aa32_rdm(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, RDM) != 0; +} + +static inline bool isar_feature_aa32_vcma(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, VCMA) != 0; +} + +static inline bool isar_feature_aa32_jscvt(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, JSCVT) != 0; +} + +static inline bool isar_feature_aa32_dp(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, DP) != 0; +} + +static inline bool isar_feature_aa32_fhm(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, FHM) != 0; +} + +static inline bool isar_feature_aa32_sb(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, SB) != 0; +} + +static inline bool isar_feature_aa32_predinv(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, SPECRES) != 0; +} + +static inline bool isar_feature_aa32_bf16(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, BF16) != 0; +} + +static inline bool isar_feature_aa32_i8mm(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, I8MM) != 0; +} + +static inline bool isar_feature_aa32_ras(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_pfr0, ID_PFR0, RAS) != 0; +} + +static inline bool isar_feature_aa32_mprofile(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_pfr1, ID_PFR1, MPROGMOD) != 0; +} + +static inline bool isar_feature_aa32_m_sec_state(const ARMISARegisters *id) +{ + /* + * Return true if M-profile state handling insns + * (VSCCLRM, CLRM, FPCTX access insns) are implemented + */ + return FIELD_EX32(id->id_pfr1, ID_PFR1, SECURITY) >= 3; +} + +static inline bool isar_feature_aa32_fp16_arith(const ARMISARegisters *id) +{ + /* Sadly this is encoded differently for A-profile and M-profile */ + if (isar_feature_aa32_mprofile(id)) { + return FIELD_EX32(id->mvfr1, MVFR1, FP16) > 0; + } else { + return FIELD_EX32(id->mvfr1, MVFR1, FPHP) >= 3; + } +} + +static inline bool isar_feature_aa32_mve(const ARMISARegisters *id) +{ + /* + * Return true if MVE is supported (either integer or floating point). + * We must check for M-profile as the MVFR1 field means something + * else for A-profile. + */ + return isar_feature_aa32_mprofile(id) && + FIELD_EX32(id->mvfr1, MVFR1, MVE) > 0; +} + +static inline bool isar_feature_aa32_mve_fp(const ARMISARegisters *id) +{ + /* + * Return true if MVE is supported (either integer or floating point). + * We must check for M-profile as the MVFR1 field means something + * else for A-profile. + */ + return isar_feature_aa32_mprofile(id) && + FIELD_EX32(id->mvfr1, MVFR1, MVE) >= 2; +} + +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_EX32(id->mvfr0, MVFR0, SIMDREG) >= 2; +} + +static inline bool isar_feature_aa32_fpshvec(const ARMISARegisters *id) +{ + return FIELD_EX32(id->mvfr0, MVFR0, FPSHVEC) > 0; +} + +static inline bool isar_feature_aa32_fpsp_v2(const ARMISARegisters *id) +{ + /* 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); +} + +/* + * We always set the FP and SIMD FP16 fields to indicate identical + * levels of support (assuming SIMD is implemented at all), so + * we only need one set of accessors. + */ +static inline bool isar_feature_aa32_fp16_spconv(const ARMISARegisters *id) +{ + return FIELD_EX32(id->mvfr1, MVFR1, FPHP) > 0; +} + +static inline bool isar_feature_aa32_fp16_dpconv(const ARMISARegisters *id) +{ + 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_EX32(id->mvfr2, MVFR2, FPMISC) >= 1; +} + +static inline bool isar_feature_aa32_vcvt_dr(const ARMISARegisters *id) +{ + return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 2; +} + +static inline bool isar_feature_aa32_vrint(const ARMISARegisters *id) +{ + return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 3; +} + +static inline bool isar_feature_aa32_vminmaxnm(const ARMISARegisters *id) +{ + return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 4; +} + +static inline bool isar_feature_aa32_pxn(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr0, ID_MMFR0, VMSA) >= 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_pmuv3p1(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_pmuv3p4(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_pmuv3p5(const ARMISARegisters *id) +{ + /* 0xf means "non-standard IMPDEF PMU" */ + return FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) >= 6 && + 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; +} + +static inline bool isar_feature_aa32_tts2uxn(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr4, ID_MMFR4, XNX) != 0; +} + +static inline bool isar_feature_aa32_half_evt(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr4, ID_MMFR4, EVT) >= 1; +} + +static inline bool isar_feature_aa32_evt(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr4, ID_MMFR4, EVT) >= 2; +} + +static inline bool isar_feature_aa32_dit(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_pfr0, ID_PFR0, DIT) != 0; +} + +static inline bool isar_feature_aa32_ssbs(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_pfr2, ID_PFR2, SSBS) != 0; +} + +static inline bool isar_feature_aa32_debugv7p1(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_dfr0, ID_DFR0, COPDBG) >= 5; +} + +static inline bool isar_feature_aa32_debugv8p2(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_dfr0, ID_DFR0, COPDBG) >= 8; +} + +static inline bool isar_feature_aa32_doublelock(const ARMISARegisters *id) +{ + return FIELD_EX32(id->dbgdevid, DBGDEVID, DOUBLELOCK) > 0; +} + +/* + * 64-bit feature tests via id registers. + */ +static inline bool isar_feature_aa64_aes(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, AES) != 0; +} + +static inline bool isar_feature_aa64_pmull(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, AES) > 1; +} + +static inline bool isar_feature_aa64_sha1(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA1) != 0; +} + +static inline bool isar_feature_aa64_sha256(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA2) != 0; +} + +static inline bool isar_feature_aa64_sha512(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA2) > 1; +} + +static inline bool isar_feature_aa64_crc32(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, CRC32) != 0; +} + +static inline bool isar_feature_aa64_atomics(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, ATOMIC) != 0; +} + +static inline bool isar_feature_aa64_rdm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, RDM) != 0; +} + +static inline bool isar_feature_aa64_sha3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA3) != 0; +} + +static inline bool isar_feature_aa64_sm3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SM3) != 0; +} + +static inline bool isar_feature_aa64_sm4(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SM4) != 0; +} + +static inline bool isar_feature_aa64_dp(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, DP) != 0; +} + +static inline bool isar_feature_aa64_fhm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, FHM) != 0; +} + +static inline bool isar_feature_aa64_condm_4(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) != 0; +} + +static inline bool isar_feature_aa64_condm_5(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) >= 2; +} + +static inline bool isar_feature_aa64_rndr(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, RNDR) != 0; +} + +static inline bool isar_feature_aa64_tlbirange(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) == 2; +} + +static inline bool isar_feature_aa64_tlbios(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) != 0; +} + +static inline bool isar_feature_aa64_jscvt(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, JSCVT) != 0; +} + +static inline bool isar_feature_aa64_fcma(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FCMA) != 0; +} + +/* + * These are the values from APA/API/APA3. + * In general these must be compared '>=', per the normal Arm ARM + * treatment of fields in ID registers. + */ +typedef enum { + PauthFeat_None = 0, + PauthFeat_1 = 1, + PauthFeat_EPAC = 2, + PauthFeat_2 = 3, + PauthFeat_FPAC = 4, + PauthFeat_FPACCOMBINED = 5, +} ARMPauthFeature; + +static inline ARMPauthFeature +isar_feature_pauth_feature(const ARMISARegisters *id) +{ + /* + * Architecturally, only one of {APA,API,APA3} may be active (non-zero) + * and the other two must be zero. Thus we may avoid conditionals. + */ + return (FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, APA) | + FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, API) | + FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, APA3)); +} + +static inline bool isar_feature_aa64_pauth(const ARMISARegisters *id) +{ + /* + * Return true if any form of pauth is enabled, as this + * predicate controls migration of the 128-bit keys. + */ + return isar_feature_pauth_feature(id) != PauthFeat_None; +} + +static inline bool isar_feature_aa64_pauth_qarma5(const ARMISARegisters *id) +{ + /* + * Return true if pauth is enabled with the architected QARMA5 algorithm. + * QEMU will always enable or disable both APA and GPA. + */ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, APA) != 0; +} + +static inline bool isar_feature_aa64_pauth_qarma3(const ARMISARegisters *id) +{ + /* + * Return true if pauth is enabled with the architected QARMA3 algorithm. + * QEMU will always enable or disable both APA3 and GPA3. + */ + return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, APA3) != 0; +} + +static inline bool isar_feature_aa64_sb(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SB) != 0; +} + +static inline bool isar_feature_aa64_predinv(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SPECRES) != 0; +} + +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_bf16(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, BF16) != 0; +} + +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_i8mm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, I8MM) != 0; +} + +static inline bool isar_feature_aa64_hbc(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, BC) != 0; +} + +static inline bool isar_feature_aa64_mops(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, MOPS); +} + +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. */ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, FP) == 1; +} + +static inline bool isar_feature_aa64_aa32(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL0) >= 2; +} + +static inline bool isar_feature_aa64_aa32_el1(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL1) >= 2; +} + +static inline bool isar_feature_aa64_aa32_el2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL2) >= 2; +} + +static inline bool isar_feature_aa64_ras(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RAS) != 0; +} + +static inline bool isar_feature_aa64_doublefault(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RAS) >= 2; +} + +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_sel2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, SEL2) != 0; +} + +static inline bool isar_feature_aa64_rme(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RME) != 0; +} + +static inline bool isar_feature_aa64_dit(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, DIT) != 0; +} + +static inline bool isar_feature_aa64_scxtnum(const ARMISARegisters *id) +{ + int key = FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, CSV2); + if (key >= 2) { + return true; /* FEAT_CSV2_2 */ + } + if (key == 1) { + key = FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, CSV2_FRAC); + return key >= 2; /* FEAT_CSV2_1p2 */ + } + return false; +} + +static inline bool isar_feature_aa64_ssbs(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SSBS) != 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_mte_insn_reg(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) != 0; +} + +static inline bool isar_feature_aa64_mte(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) >= 2; +} + +static inline bool isar_feature_aa64_mte3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) >= 3; +} + +static inline bool isar_feature_aa64_sme(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SME) != 0; +} + +static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) +{ + return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 1; +} + +static inline bool isar_feature_aa64_tgran4_2_lpa2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); + return t >= 3 || (t == 0 && isar_feature_aa64_tgran4_lpa2(id)); +} + +static inline bool isar_feature_aa64_tgran16_lpa2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 2; +} + +static inline bool isar_feature_aa64_tgran16_2_lpa2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); + return t >= 3 || (t == 0 && isar_feature_aa64_tgran16_lpa2(id)); +} + +static inline bool isar_feature_aa64_tgran4(const ARMISARegisters *id) +{ + return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 0; +} + +static inline bool isar_feature_aa64_tgran16(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 1; +} + +static inline bool isar_feature_aa64_tgran64(const ARMISARegisters *id) +{ + return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN64) >= 0; +} + +static inline bool isar_feature_aa64_tgran4_2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); + return t >= 2 || (t == 0 && isar_feature_aa64_tgran4(id)); +} + +static inline bool isar_feature_aa64_tgran16_2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); + return t >= 2 || (t == 0 && isar_feature_aa64_tgran16(id)); +} + +static inline bool isar_feature_aa64_tgran64_2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN64_2); + return t >= 2 || (t == 0 && isar_feature_aa64_tgran64(id)); +} + +static inline bool isar_feature_aa64_fgt(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, FGT) != 0; +} + +static inline bool isar_feature_aa64_ecv_traps(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, ECV) > 0; +} + +static inline bool isar_feature_aa64_ecv(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, ECV) > 1; +} + +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_pan3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, PAN) >= 3; +} + +static inline bool isar_feature_aa64_hcx(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HCX) != 0; +} + +static inline bool isar_feature_aa64_tidcp1(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, TIDCP1) != 0; +} + +static inline bool isar_feature_aa64_hafs(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HAFDBS) != 0; +} + +static inline bool isar_feature_aa64_hdbs(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HAFDBS) >= 2; +} + +static inline bool isar_feature_aa64_tts2uxn(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, XNX) != 0; +} + +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_st(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, ST) != 0; +} + +static inline bool isar_feature_aa64_lse2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, AT) != 0; +} + +static inline bool isar_feature_aa64_fwb(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, FWB) != 0; +} + +static inline bool isar_feature_aa64_ids(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, IDS) != 0; +} + +static inline bool isar_feature_aa64_half_evt(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, EVT) >= 1; +} + +static inline bool isar_feature_aa64_evt(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, EVT) >= 2; +} + +static inline bool isar_feature_aa64_ccidx(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, CCIDX) != 0; +} + +static inline bool isar_feature_aa64_lva(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, VARANGE) != 0; +} + +static inline bool isar_feature_aa64_e0pd(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, E0PD) != 0; +} + +static inline bool isar_feature_aa64_nv(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, NV) != 0; +} + +static inline bool isar_feature_aa64_nv2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, NV) >= 2; +} + +static inline bool isar_feature_aa64_pmuv3p1(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_pmuv3p4(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_pmuv3p5(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 6 && + FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; +} + +static inline bool isar_feature_aa64_debugv8p2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, DEBUGVER) >= 8; +} + +static inline bool isar_feature_aa64_doublelock(const ARMISARegisters *id) +{ + return FIELD_SEX64(id->id_aa64dfr0, ID_AA64DFR0, DOUBLELOCK) >= 0; +} + +static inline bool isar_feature_aa64_sve2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SVEVER) != 0; +} + +static inline bool isar_feature_aa64_sve2_aes(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, AES) != 0; +} + +static inline bool isar_feature_aa64_sve2_pmull128(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, AES) >= 2; +} + +static inline bool isar_feature_aa64_sve2_bitperm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, BITPERM) != 0; +} + +static inline bool isar_feature_aa64_sve_bf16(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, BFLOAT16) != 0; +} + +static inline bool isar_feature_aa64_sve2_sha3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SHA3) != 0; +} + +static inline bool isar_feature_aa64_sve2_sm4(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SM4) != 0; +} + +static inline bool isar_feature_aa64_sve_i8mm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, I8MM) != 0; +} + +static inline bool isar_feature_aa64_sve_f32mm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, F32MM) != 0; +} + +static inline bool isar_feature_aa64_sve_f64mm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, F64MM) != 0; +} + +static inline bool isar_feature_aa64_sme_f64f64(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, F64F64); +} + +static inline bool isar_feature_aa64_sme_i16i64(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, I16I64) == 0xf; +} + +static inline bool isar_feature_aa64_sme_fa64(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, FA64); +} + +/* + * 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_pmuv3p1(const ARMISARegisters *id) +{ + return isar_feature_aa64_pmuv3p1(id) || isar_feature_aa32_pmuv3p1(id); +} + +static inline bool isar_feature_any_pmuv3p4(const ARMISARegisters *id) +{ + return isar_feature_aa64_pmuv3p4(id) || isar_feature_aa32_pmuv3p4(id); +} + +static inline bool isar_feature_any_pmuv3p5(const ARMISARegisters *id) +{ + return isar_feature_aa64_pmuv3p5(id) || isar_feature_aa32_pmuv3p5(id); +} + +static inline bool isar_feature_any_ccidx(const ARMISARegisters *id) +{ + return isar_feature_aa64_ccidx(id) || isar_feature_aa32_ccidx(id); +} + +static inline bool isar_feature_any_tts2uxn(const ARMISARegisters *id) +{ + return isar_feature_aa64_tts2uxn(id) || isar_feature_aa32_tts2uxn(id); +} + +static inline bool isar_feature_any_debugv8p2(const ARMISARegisters *id) +{ + return isar_feature_aa64_debugv8p2(id) || isar_feature_aa32_debugv8p2(id); +} + +static inline bool isar_feature_any_ras(const ARMISARegisters *id) +{ + return isar_feature_aa64_ras(id) || isar_feature_aa32_ras(id); +} + +static inline bool isar_feature_any_half_evt(const ARMISARegisters *id) +{ + return isar_feature_aa64_half_evt(id) || isar_feature_aa32_half_evt(id); +} + +static inline bool isar_feature_any_evt(const ARMISARegisters *id) +{ + return isar_feature_aa64_evt(id) || isar_feature_aa32_evt(id); +} + +/* + * Forward to the above feature tests given an ARMCPU pointer. + */ +#define cpu_isar_feature(name, cpu) \ + ({ ARMCPU *cpu_ = (cpu); isar_feature_##name(&cpu_->isar); }) + +#endif diff --git a/target/arm/cpu-param.h b/target/arm/cpu-param.h index 68ffb12427..da3243ab21 100644 --- a/target/arm/cpu-param.h +++ b/target/arm/cpu-param.h @@ -19,9 +19,13 @@ #endif #ifdef CONFIG_USER_ONLY -#define TARGET_PAGE_BITS 12 # ifdef TARGET_AARCH64 # define TARGET_TAGGED_ADDRESSES +/* Allow user-only to vary page size from 4k */ +# define TARGET_PAGE_BITS_VARY +# define TARGET_PAGE_BITS_MIN 12 +# else +# define TARGET_PAGE_BITS 12 # endif #else /* @@ -30,8 +34,7 @@ */ # define TARGET_PAGE_BITS_VARY # define TARGET_PAGE_BITS_MIN 10 -#endif - -#define NB_MMU_MODES 15 + +#endif #endif diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index 64c44cef2d..8e032691db 100644 --- a/target/arm/cpu-qom.h +++ b/target/arm/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU ARM CPU + * QEMU ARM CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -21,9 +21,6 @@ #define QEMU_ARM_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" - -struct arm_boot_info; #define TYPE_ARM_CPU "arm-cpu" @@ -31,67 +28,33 @@ OBJECT_DECLARE_CPU_TYPE(ARMCPU, ARMCPUClass, ARM_CPU) #define TYPE_ARM_MAX_CPU "max-" TYPE_ARM_CPU -typedef struct ARMCPUInfo { - const char *name; - void (*initfn)(Object *obj); - void (*class_init)(ObjectClass *oc, void *data); -} ARMCPUInfo; - -void arm_cpu_register(const ARMCPUInfo *info); -void aarch64_cpu_register(const ARMCPUInfo *info); - -/** - * ARMCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * - * An ARM CPU model. - */ -struct ARMCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - const ARMCPUInfo *info; - DeviceRealize parent_realize; - DeviceReset parent_reset; -}; - - #define TYPE_AARCH64_CPU "aarch64-cpu" typedef struct AArch64CPUClass AArch64CPUClass; DECLARE_CLASS_CHECKERS(AArch64CPUClass, AARCH64_CPU, TYPE_AARCH64_CPU) -struct AArch64CPUClass { - /*< private >*/ - ARMCPUClass parent_class; - /*< public >*/ +#define ARM_CPU_TYPE_SUFFIX "-" TYPE_ARM_CPU +#define ARM_CPU_TYPE_NAME(name) (name ARM_CPU_TYPE_SUFFIX) + +/* Meanings of the ARMCPU object's four inbound GPIO lines */ +#define ARM_CPU_IRQ 0 +#define ARM_CPU_FIQ 1 +#define ARM_CPU_VIRQ 2 +#define ARM_CPU_VFIQ 3 + +/* For M profile, some registers are banked secure vs non-secure; + * these are represented as a 2-element array where the first element + * is the non-secure copy and the second is the secure copy. + * When the CPU does not have implement the security extension then + * only the first element is used. + * This means that the copy for the current security state can be + * accessed via env->registerfield[env->v7m.secure] (whether the security + * extension is implemented or not). + */ +enum { + M_REG_NS = 0, + M_REG_S = 1, + M_REG_NUM_BANKS = 2, }; -void register_cp_regs_for_features(ARMCPU *cpu); -void init_cpreg_list(ARMCPU *cpu); - -/* Callback functions for the generic timer's timers. */ -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) -#define ARM_AFF1_SHIFT 8 -#define ARM_AFF1_MASK (0xFFULL << ARM_AFF1_SHIFT) -#define ARM_AFF2_SHIFT 16 -#define ARM_AFF2_MASK (0xFFULL << ARM_AFF2_SHIFT) -#define ARM_AFF3_SHIFT 32 -#define ARM_AFF3_MASK (0xFFULL << ARM_AFF3_SHIFT) -#define ARM_DEFAULT_CPUS_PER_CLUSTER 8 - -#define ARM32_AFFINITY_MASK (ARM_AFF0_MASK|ARM_AFF1_MASK|ARM_AFF2_MASK) -#define ARM64_AFFINITY_MASK \ - (ARM_AFF0_MASK|ARM_AFF1_MASK|ARM_AFF2_MASK|ARM_AFF3_MASK) -#define ARM64_AFFINITY_INVALID (~ARM64_AFFINITY_MASK) - #endif diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 1b5d535788..ab8d007a86 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -26,24 +26,30 @@ #include "target/arm/idau.h" #include "qemu/module.h" #include "qapi/error.h" -#include "qapi/visitor.h" #include "cpu.h" #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" #endif /* CONFIG_TCG */ #include "internals.h" +#include "cpu-features.h" #include "exec/exec-all.h" #include "hw/qdev-properties.h" #if !defined(CONFIG_USER_ONLY) #include "hw/loader.h" #include "hw/boards.h" -#endif +#ifdef CONFIG_TCG +#include "hw/intc/armv7m_nvic.h" +#endif /* CONFIG_TCG */ +#endif /* !CONFIG_USER_ONLY */ #include "sysemu/tcg.h" +#include "sysemu/qtest.h" #include "sysemu/hw_accel.h" #include "kvm_arm.h" #include "disas/capstone.h" #include "fpu/softfloat.h" #include "cpregs.h" +#include "target/arm/cpu-qom.h" +#include "target/arm/gtimer.h" static void arm_cpu_set_pc(CPUState *cs, vaddr value) { @@ -59,21 +65,59 @@ static void arm_cpu_set_pc(CPUState *cs, vaddr value) } } -#ifdef CONFIG_TCG -void arm_cpu_synchronize_from_tb(CPUState *cs, - const TranslationBlock *tb) +static vaddr arm_cpu_get_pc(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; - /* - * It's OK to look at env for the current mode here, because it's - * never possible for an AArch64 TB to chain to an AArch32 TB. - */ if (is_a64(env)) { - env->pc = tb->pc; + return env->pc; } else { - env->regs[15] = tb->pc; + return env->regs[15]; + } +} + +#ifdef CONFIG_TCG +void arm_cpu_synchronize_from_tb(CPUState *cs, + const TranslationBlock *tb) +{ + /* The program counter is always up to date with CF_PCREL. */ + if (!(tb_cflags(tb) & CF_PCREL)) { + CPUARMState *env = cpu_env(cs); + /* + * It's OK to look at env for the current mode here, because it's + * never possible for an AArch64 TB to chain to an AArch32 TB. + */ + if (is_a64(env)) { + env->pc = tb->pc; + } else { + env->regs[15] = tb->pc; + } + } +} + +void arm_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + CPUARMState *env = cpu_env(cs); + + if (is_a64(env)) { + if (tb_cflags(tb) & CF_PCREL) { + env->pc = (env->pc & TARGET_PAGE_MASK) | data[0]; + } else { + env->pc = data[0]; + } + env->condexec_bits = 0; + env->exception.syndrome = data[2] << ARM_INSN_START_WORD2_SHIFT; + } else { + if (tb_cflags(tb) & CF_PCREL) { + env->regs[15] = (env->regs[15] & TARGET_PAGE_MASK) | data[0]; + } else { + env->regs[15] = data[0]; + } + env->condexec_bits = data[1]; + env->exception.syndrome = data[2] << ARM_INSN_START_WORD2_SHIFT; } } #endif /* CONFIG_TCG */ @@ -89,6 +133,11 @@ static bool arm_cpu_has_work(CPUState *cs) | CPU_INTERRUPT_EXITTB); } +static int arm_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return arm_env_mmu_index(cpu_env(cs)); +} + void arm_register_pre_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void *opaque) { @@ -163,14 +212,16 @@ static void cp_reg_check_reset(gpointer key, gpointer value, gpointer opaque) assert(oldvalue == newvalue); } -static void arm_cpu_reset(DeviceState *dev) +static void arm_cpu_reset_hold(Object *obj) { - CPUState *s = CPU(dev); - ARMCPU *cpu = ARM_CPU(s); - ARMCPUClass *acc = ARM_CPU_GET_CLASS(cpu); + CPUState *cs = CPU(obj); + ARMCPU *cpu = ARM_CPU(cs); + ARMCPUClass *acc = ARM_CPU_GET_CLASS(obj); CPUARMState *env = &cpu->env; - acc->parent_reset(dev); + if (acc->parent_phases.hold) { + acc->parent_phases.hold(obj); + } memset(env, 0, offsetof(CPUARMState, end_reset_fields)); @@ -182,7 +233,7 @@ static void arm_cpu_reset(DeviceState *dev) env->vfp.xregs[ARM_VFP_MVFR1] = cpu->isar.mvfr1; env->vfp.xregs[ARM_VFP_MVFR2] = cpu->isar.mvfr2; - cpu->power_state = s->start_powered_off ? PSCI_OFF : PSCI_ON; + cpu->power_state = cs->start_powered_off ? PSCI_OFF : PSCI_ON; if (arm_feature(env, ARM_FEATURE_IWMMXT)) { env->iwmmxt.cregs[ARM_IWMMXT_wCID] = 0x69051000 | 'Q'; @@ -200,22 +251,36 @@ static void arm_cpu_reset(DeviceState *dev) SCTLR_EnDA | SCTLR_EnDB); /* Trap on btype=3 for PACIxSP. */ env->cp15.sctlr_el[1] |= SCTLR_BT0; + /* Trap on implementation defined registers. */ + if (cpu_isar_feature(aa64_tidcp1, cpu)) { + env->cp15.sctlr_el[1] |= SCTLR_TIDCP; + } /* and to the FP/Neon instructions */ env->cp15.cpacr_el1 = FIELD_DP64(env->cp15.cpacr_el1, CPACR_EL1, FPEN, 3); - /* and to the SVE instructions */ - env->cp15.cpacr_el1 = FIELD_DP64(env->cp15.cpacr_el1, - CPACR_EL1, ZEN, 3); - /* with reasonable vector length */ + /* and to the SVE instructions, with default vector length */ if (cpu_isar_feature(aa64_sve, cpu)) { + env->cp15.cpacr_el1 = FIELD_DP64(env->cp15.cpacr_el1, + CPACR_EL1, ZEN, 3); env->vfp.zcr_el[1] = cpu->sve_default_vq - 1; } + /* and for SME instructions, with default vector length, and TPIDR2 */ + if (cpu_isar_feature(aa64_sme, cpu)) { + env->cp15.sctlr_el[1] |= SCTLR_EnTP2; + env->cp15.cpacr_el1 = FIELD_DP64(env->cp15.cpacr_el1, + CPACR_EL1, SMEN, 3); + env->vfp.smcr_el[1] = cpu->sme_default_vq - 1; + if (cpu_isar_feature(aa64_sme_fa64, cpu)) { + env->vfp.smcr_el[1] = FIELD_DP64(env->vfp.smcr_el[1], + SMCR, FA64, 1); + } + } /* * Enable 48-bit address space (TODO: take reserved_va into account). * Enable TBI0 but not TBI1. * Note that this must match useronly_clean_ptr. */ - env->cp15.tcr_el[1].raw_tcr = 5 | (1ULL << 37); + env->cp15.tcr_el[1] = 5 | (1ULL << 37); /* Enable MTE */ if (cpu_isar_feature(aa64_mte, cpu)) { @@ -236,6 +301,10 @@ static void arm_cpu_reset(DeviceState *dev) * This is not yet exposed from the Linux kernel in any way. */ env->cp15.sctlr_el[1] |= SCTLR_TSCXT; + /* Disable access to Debug Communication Channel (DCC). */ + env->cp15.mdscr_el1 |= 1 << 12; + /* Enable FEAT_MOPS */ + env->cp15.sctlr_el[1] |= SCTLR_MSCEN; #else /* Reset into the highest available EL */ if (arm_feature(env, ARM_FEATURE_EL3)) { @@ -258,6 +327,10 @@ static void arm_cpu_reset(DeviceState *dev) env->cp15.cpacr_el1 = FIELD_DP64(env->cp15.cpacr_el1, CPACR, CP11, 3); #endif + if (arm_feature(env, ARM_FEATURE_V8)) { + env->cp15.rvbar = cpu->rvbar_prop; + env->regs[15] = cpu->rvbar_prop; + } } #if defined(CONFIG_USER_ONLY) @@ -365,7 +438,7 @@ static void arm_cpu_reset(DeviceState *dev) /* Load the initial SP and PC from offset 0 and 4 in the vector table */ vecbase = env->v7m.vecbase[env->v7m.secure]; - rom = rom_ptr_for_as(s->as, vecbase, 8); + rom = rom_ptr_for_as(cs->as, vecbase, 8); if (rom) { /* Address zero is covered by ROM which hasn't yet been * copied into physical memory. @@ -378,8 +451,8 @@ static void arm_cpu_reset(DeviceState *dev) * it got copied into memory. In the latter case, rom_ptr * will return a NULL pointer and we should use ldl_phys instead. */ - initial_msp = ldl_phys(s->as, vecbase); - initial_pc = ldl_phys(s->as, vecbase + 4); + initial_msp = ldl_phys(cs->as, vecbase); + initial_pc = ldl_phys(cs->as, vecbase + 4); } qemu_log_mask(CPU_LOG_INT, @@ -436,6 +509,14 @@ static void arm_cpu_reset(DeviceState *dev) sizeof(*env->pmsav7.dracr) * cpu->pmsav7_dregion); } } + + if (cpu->pmsav8r_hdregion > 0) { + memset(env->pmsav8.hprbar, 0, + sizeof(*env->pmsav8.hprbar) * cpu->pmsav8r_hdregion); + memset(env->pmsav8.hprlar, 0, + sizeof(*env->pmsav8.hprlar) * cpu->pmsav8r_hdregion); + } + env->pmsav7.rnr[M_REG_NS] = 0; env->pmsav7.rnr[M_REG_S] = 0; env->pmsav8.mair0[M_REG_NS] = 0; @@ -474,19 +555,117 @@ static void arm_cpu_reset(DeviceState *dev) } #endif - hw_breakpoint_update_all(cpu); - hw_watchpoint_update_all(cpu); - arm_rebuild_hflags(env); + if (tcg_enabled()) { + hw_breakpoint_update_all(cpu); + hw_watchpoint_update_all(cpu); + + arm_rebuild_hflags(env); + } } -#ifndef CONFIG_USER_ONLY +void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) +{ + ARMCPU *cpu = ARM_CPU(cpustate); + CPUARMState *env = &cpu->env; + bool have_el3 = arm_feature(env, ARM_FEATURE_EL3); + bool have_el2 = arm_feature(env, ARM_FEATURE_EL2); + + /* + * Check we have the EL we're aiming for. If that is the + * highest implemented EL, then cpu_reset has already done + * all the work. + */ + switch (target_el) { + case 3: + assert(have_el3); + return; + case 2: + assert(have_el2); + if (!have_el3) { + return; + } + break; + case 1: + if (!have_el3 && !have_el2) { + return; + } + break; + default: + g_assert_not_reached(); + } + + if (have_el3) { + /* + * Set the EL3 state so code can run at EL2. This should match + * the requirements set by Linux in its booting spec. + */ + if (env->aarch64) { + env->cp15.scr_el3 |= SCR_RW; + if (cpu_isar_feature(aa64_pauth, cpu)) { + env->cp15.scr_el3 |= SCR_API | SCR_APK; + } + if (cpu_isar_feature(aa64_mte, cpu)) { + env->cp15.scr_el3 |= SCR_ATA; + } + if (cpu_isar_feature(aa64_sve, cpu)) { + env->cp15.cptr_el[3] |= R_CPTR_EL3_EZ_MASK; + env->vfp.zcr_el[3] = 0xf; + } + if (cpu_isar_feature(aa64_sme, cpu)) { + env->cp15.cptr_el[3] |= R_CPTR_EL3_ESM_MASK; + env->cp15.scr_el3 |= SCR_ENTP2; + env->vfp.smcr_el[3] = 0xf; + } + if (cpu_isar_feature(aa64_hcx, cpu)) { + env->cp15.scr_el3 |= SCR_HXEN; + } + if (cpu_isar_feature(aa64_fgt, cpu)) { + env->cp15.scr_el3 |= SCR_FGTEN; + } + } + + if (target_el == 2) { + /* If the guest is at EL2 then Linux expects the HVC insn to work */ + env->cp15.scr_el3 |= SCR_HCE; + } + + /* Put CPU into non-secure state */ + env->cp15.scr_el3 |= SCR_NS; + /* Set NSACR.{CP11,CP10} so NS can access the FPU */ + env->cp15.nsacr |= 3 << 10; + } + + if (have_el2 && target_el < 2) { + /* Set EL2 state so code can run at EL1. */ + if (env->aarch64) { + env->cp15.hcr_el2 |= HCR_RW; + } + } + + /* Set the CPU to the desired state */ + if (env->aarch64) { + env->pstate = aarch64_pstate_mode(target_el, true); + } else { + static const uint32_t mode_for_el[] = { + 0, + ARM_CPU_MODE_SVC, + ARM_CPU_MODE_HYP, + ARM_CPU_MODE_SVC, + }; + + cpsr_write(env, mode_for_el[target_el], CPSR_M, CPSRWriteRaw); + } +} + + +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) 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; + CPUARMState *env = cpu_env(cs); bool pstate_unmasked; bool unmasked = false; @@ -538,14 +717,24 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, 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 || (env->cp15.scr_el3 & SCR_EEL2)) { + switch (target_el) { + case 2: + /* + * According to ARM DDI 0487H.a, an interrupt can be masked + * when HCR_E2H and HCR_TGE are both set regardless of the + * current Security state. Note that we need to revisit this + * part again once we need to support NMI. + */ + if ((hcr_el2 & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { + unmasked = true; + } + break; + case 3: + /* Interrupt cannot be masked when the target EL is 3 */ unmasked = true; + break; + default: + g_assert_not_reached(); } } else { /* @@ -597,7 +786,7 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, } /* - * The PSTATE bits only mask the interrupt if we have not overriden the + * The PSTATE bits only mask the interrupt if we have not overridden the * ability above. */ return unmasked || pstate_unmasked; @@ -606,7 +795,7 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, static bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { CPUClass *cc = CPU_GET_CLASS(cs); - CPUARMState *env = cs->env_ptr; + CPUARMState *env = cpu_env(cs); uint32_t cur_el = arm_current_el(env); bool secure = arm_is_secure(env); uint64_t hcr_el2 = arm_hcr_el2_eff(env); @@ -666,7 +855,8 @@ static bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) cc->tcg_ops->do_interrupt(cs); return true; } -#endif /* !CONFIG_USER_ONLY */ + +#endif /* CONFIG_TCG && !CONFIG_USER_ONLY */ void arm_cpu_update_virq(ARMCPU *cpu) { @@ -827,13 +1017,6 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) bool sctlr_b; if (is_a64(env)) { - /* We might not be compiled with the A64 disassembler - * because it needs a C++ compiler. Leave print_insn - * unset in this case to use the caller default behaviour. - */ -#if defined(CONFIG_ARM_A64_DIS) - info->print_insn = print_insn_arm_a64; -#endif info->cap_arch = CS_ARCH_ARM64; info->cap_insn_unit = 4; info->cap_insn_split = 4; @@ -881,9 +1064,11 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; uint32_t psr = pstate_read(env); - int i; + int i, j; int el = arm_current_el(env); + uint64_t hcr = arm_hcr_el2_eff(env); const char *ns_status; + bool sve; qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc); for (i = 0; i < 32; i++) { @@ -910,9 +1095,19 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) el, psr & PSTATE_SP ? 'h' : 't'); + if (cpu_isar_feature(aa64_sme, cpu)) { + qemu_fprintf(f, " SVCR=%08" PRIx64 " %c%c", + env->svcr, + (FIELD_EX64(env->svcr, SVCR, ZA) ? 'Z' : '-'), + (FIELD_EX64(env->svcr, SVCR, SM) ? 'S' : '-')); + } if (cpu_isar_feature(aa64_bti, cpu)) { qemu_fprintf(f, " BTYPE=%d", (psr & PSTATE_BTYPE) >> 10); } + qemu_fprintf(f, "%s%s%s", + (hcr & HCR_NV) ? " NV" : "", + (hcr & HCR_NV1) ? " NV1" : "", + (hcr & HCR_NV2) ? " NV2" : ""); if (!(flags & CPU_DUMP_FPU)) { qemu_fprintf(f, "\n"); return; @@ -924,8 +1119,16 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) qemu_fprintf(f, " FPCR=%08x FPSR=%08x\n", vfp_get_fpcr(env), vfp_get_fpsr(env)); - if (cpu_isar_feature(aa64_sve, cpu) && sve_exception_el(env, el) == 0) { - int j, zcr_len = sve_vqm1_for_el(env, el); + if (cpu_isar_feature(aa64_sme, cpu) && FIELD_EX64(env->svcr, SVCR, SM)) { + sve = sme_exception_el(env, el) == 0; + } else if (cpu_isar_feature(aa64_sve, cpu)) { + sve = sve_exception_el(env, el) == 0; + } else { + sve = false; + } + + if (sve) { + int zcr_len = sve_vqm1_for_el(env, el); for (i = 0; i <= FFR_PRED_NUM; i++) { bool eol; @@ -965,32 +1168,24 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } - for (i = 0; i < 32; i++) { - if (zcr_len == 0) { + if (zcr_len == 0) { + /* + * With vl=16, there are only 37 columns per register, + * so output two registers per line. + */ + for (i = 0; i < 32; i++) { qemu_fprintf(f, "Z%02d=%016" PRIx64 ":%016" PRIx64 "%s", i, env->vfp.zregs[i].d[1], env->vfp.zregs[i].d[0], i & 1 ? "\n" : " "); - } else if (zcr_len == 1) { - qemu_fprintf(f, "Z%02d=%016" PRIx64 ":%016" PRIx64 - ":%016" PRIx64 ":%016" PRIx64 "\n", - i, env->vfp.zregs[i].d[3], env->vfp.zregs[i].d[2], - env->vfp.zregs[i].d[1], env->vfp.zregs[i].d[0]); - } else { + } + } else { + for (i = 0; i < 32; i++) { + qemu_fprintf(f, "Z%02d=", i); for (j = zcr_len; j >= 0; j--) { - bool odd = (zcr_len - j) % 2 != 0; - if (j == zcr_len) { - qemu_fprintf(f, "Z%02d[%x-%x]=", i, j, j - 1); - } else if (!odd) { - if (j > 0) { - qemu_fprintf(f, " [%x-%x]=", j, j - 1); - } else { - qemu_fprintf(f, " [%x]=", j); - } - } qemu_fprintf(f, "%016" PRIx64 ":%016" PRIx64 "%s", env->vfp.zregs[i].d[j * 2 + 1], - env->vfp.zregs[i].d[j * 2], - odd || j == 0 ? "\n" : ":"); + env->vfp.zregs[i].d[j * 2 + 0], + j ? ":" : "\n"); } } } @@ -1001,6 +1196,24 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) i, q[1], q[0], (i & 1 ? "\n" : " ")); } } + + if (cpu_isar_feature(aa64_sme, cpu) && + FIELD_EX64(env->svcr, SVCR, ZA) && + sme_exception_el(env, el) == 0) { + int zcr_len = sve_vqm1_for_el_sm(env, el, true); + int svl = (zcr_len + 1) * 16; + int svl_lg10 = svl < 100 ? 2 : 3; + + for (i = 0; i < svl; i++) { + qemu_fprintf(f, "ZA[%0*d]=", svl_lg10, i); + for (j = zcr_len; j >= 0; --j) { + qemu_fprintf(f, "%016" PRIx64 ":%016" PRIx64 "%c", + env->zarray[i].d[2 * j + 1], + env->zarray[i].d[2 * j], + j ? ':' : '\n'); + } + } + } } #else @@ -1101,18 +1314,22 @@ static void arm_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } -uint64_t arm_cpu_mp_affinity(int idx, uint8_t clustersz) +uint64_t arm_build_mp_affinity(int idx, uint8_t clustersz) { uint32_t Aff1 = idx / clustersz; uint32_t Aff0 = idx % clustersz; return (Aff1 << ARM_AFF1_SHIFT) | Aff0; } +uint64_t arm_cpu_mp_affinity(ARMCPU *cpu) +{ + return cpu->mp_affinity; +} + static void arm_cpu_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); - cpu_set_cpustate_pointers(cpu); cpu->cp_regs = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); @@ -1122,11 +1339,13 @@ static void arm_cpu_initfn(Object *obj) #ifdef CONFIG_USER_ONLY # ifdef TARGET_AARCH64 /* - * The linux kernel defaults to 512-bit vectors, when sve is supported. - * See documentation for /proc/sys/abi/sve_default_vector_length, and - * our corresponding sve-default-vector-length cpu property. + * The linux kernel defaults to 512-bit for SVE, and 256-bit for SME. + * These values were chosen to fit within the default signal frame. + * See documentation for /proc/sys/abi/{sve,sme}_default_vector_length, + * and our corresponding cpu property. */ cpu->sve_default_vq = 4; + cpu->sme_default_vq = 2; # endif #else /* Our inbound IRQ and FIQ lines */ @@ -1186,6 +1405,9 @@ static Property arm_cpu_cfgend_property = static Property arm_cpu_has_vfp_property = DEFINE_PROP_BOOL("vfp", ARMCPU, has_vfp, true); +static Property arm_cpu_has_vfp_d32_property = + DEFINE_PROP_BOOL("vfp-d32", ARMCPU, has_vfp_d32, true); + static Property arm_cpu_has_neon_property = DEFINE_PROP_BOOL("neon", ARMCPU, has_neon, true); @@ -1252,17 +1474,108 @@ unsigned int gt_cntfrq_period_ns(ARMCPU *cpu) NANOSECONDS_PER_SECOND / cpu->gt_cntfrq_hz : 1; } +static void arm_cpu_propagate_feature_implications(ARMCPU *cpu) +{ + CPUARMState *env = &cpu->env; + bool no_aa32 = false; + + /* + * Some features automatically imply others: set the feature + * bits explicitly for these cases. + */ + + if (arm_feature(env, ARM_FEATURE_M)) { + set_feature(env, ARM_FEATURE_PMSA); + } + + if (arm_feature(env, ARM_FEATURE_V8)) { + if (arm_feature(env, ARM_FEATURE_M)) { + set_feature(env, ARM_FEATURE_V7); + } else { + set_feature(env, ARM_FEATURE_V7VE); + } + } + + /* + * There exist AArch64 cpus without AArch32 support. When KVM + * queries ID_ISAR0_EL1 on such a host, the value is UNKNOWN. + * Similarly, we cannot check ID_AA64PFR0 without AArch64 support. + * As a general principle, we also do not make ID register + * consistency checks anywhere unless using TCG, because only + * for TCG would a consistency-check failure be a QEMU bug. + */ + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + no_aa32 = !cpu_isar_feature(aa64_aa32, cpu); + } + + if (arm_feature(env, ARM_FEATURE_V7VE)) { + /* + * v7 Virtualization Extensions. In real hardware this implies + * EL2 and also the presence of the Security Extensions. + * For QEMU, for backwards-compatibility we implement some + * CPUs or CPU configs which have no actual EL2 or EL3 but do + * include the various other features that V7VE implies. + * 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(aa32_arm_div, cpu)); + set_feature(env, ARM_FEATURE_LPAE); + set_feature(env, ARM_FEATURE_V7); + } + if (arm_feature(env, ARM_FEATURE_V7)) { + set_feature(env, ARM_FEATURE_VAPA); + set_feature(env, ARM_FEATURE_THUMB2); + set_feature(env, ARM_FEATURE_MPIDR); + if (!arm_feature(env, ARM_FEATURE_M)) { + set_feature(env, ARM_FEATURE_V6K); + } else { + set_feature(env, ARM_FEATURE_V6); + } + + /* + * Always define VBAR for V7 CPUs even if it doesn't exist in + * non-EL3 configs. This is needed by some legacy boards. + */ + set_feature(env, ARM_FEATURE_VBAR); + } + if (arm_feature(env, ARM_FEATURE_V6K)) { + set_feature(env, ARM_FEATURE_V6); + set_feature(env, ARM_FEATURE_MVFR); + } + 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(aa32_jazelle, cpu)); + set_feature(env, ARM_FEATURE_AUXCR); + } + } + if (arm_feature(env, ARM_FEATURE_V5)) { + set_feature(env, ARM_FEATURE_V4T); + } + if (arm_feature(env, ARM_FEATURE_LPAE)) { + set_feature(env, ARM_FEATURE_V7MP); + } + if (arm_feature(env, ARM_FEATURE_CBAR_RO)) { + set_feature(env, ARM_FEATURE_CBAR); + } + if (arm_feature(env, ARM_FEATURE_THUMB2) && + !arm_feature(env, ARM_FEATURE_M)) { + set_feature(env, ARM_FEATURE_THUMB_DSP); + } +} + void arm_cpu_post_init(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); - /* M profile implies PMSA. We have to do this here rather than - * in realize with the other feature-implication checks because - * we look at the PMSA bit to see if we should add some properties. + /* + * Some features imply others. Figure this out now, because we + * are going to look at the feature bits in deciding which + * properties to add. */ - if (arm_feature(&cpu->env, ARM_FEATURE_M)) { - set_feature(&cpu->env, ARM_FEATURE_PMSA); - } + arm_cpu_propagate_feature_implications(cpu); if (arm_feature(&cpu->env, ARM_FEATURE_CBAR) || arm_feature(&cpu->env, ARM_FEATURE_CBAR_RO)) { @@ -1273,7 +1586,7 @@ void arm_cpu_post_init(Object *obj) qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_hivecs_property); } - if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + if (arm_feature(&cpu->env, ARM_FEATURE_V8)) { object_property_add_uint64_ptr(obj, "rvbar", &cpu->rvbar_prop, OBJ_PROP_FLAG_READWRITE); @@ -1308,12 +1621,34 @@ 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_AARCH64) - ? cpu_isar_feature(aa64_fp_simd, cpu) - : cpu_isar_feature(aa32_vfp, cpu)) { + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + if (cpu_isar_feature(aa64_fp_simd, cpu)) { + cpu->has_vfp = true; + cpu->has_vfp_d32 = true; + if (tcg_enabled() || qtest_enabled()) { + qdev_property_add_static(DEVICE(obj), + &arm_cpu_has_vfp_property); + } + } + } else if (cpu_isar_feature(aa32_vfp, cpu)) { cpu->has_vfp = true; - if (!kvm_enabled()) { - qdev_property_add_static(DEVICE(obj), &arm_cpu_has_vfp_property); + if (tcg_enabled() || qtest_enabled()) { + qdev_property_add_static(DEVICE(obj), + &arm_cpu_has_vfp_property); + } + if (cpu_isar_feature(aa32_simd_r32, cpu)) { + cpu->has_vfp_d32 = true; + /* + * The permitted values of the SIMDReg bits [3:0] on + * Armv8-A are either 0b0000 and 0b0010. On such CPUs, + * make sure that has_vfp_d32 can not be set to false. + */ + if ((tcg_enabled() || qtest_enabled()) + && !(arm_feature(&cpu->env, ARM_FEATURE_V8) + && !arm_feature(&cpu->env, ARM_FEATURE_M))) { + qdev_property_add_static(DEVICE(obj), + &arm_cpu_has_vfp_d32_property); + } } } @@ -1372,7 +1707,7 @@ void arm_cpu_post_init(Object *obj) } if (kvm_enabled()) { - kvm_arm_add_vcpu_properties(obj); + kvm_arm_add_vcpu_properties(cpu); } #ifndef CONFIG_USER_ONLY @@ -1421,6 +1756,7 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp) { Error *local_err = NULL; +#ifdef TARGET_AARCH64 if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { arm_cpu_sve_finalize(cpu, &local_err); if (local_err != NULL) { @@ -1428,6 +1764,22 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp) return; } + /* + * FEAT_SME is not architecturally dependent on FEAT_SVE (unless + * FEAT_SME_FA64 is present). However our implementation currently + * assumes it, so if the user asked for sve=off then turn off SME also. + * (KVM doesn't currently support SME at all.) + */ + if (cpu_isar_feature(aa64_sme, cpu) && !cpu_isar_feature(aa64_sve, cpu)) { + object_property_set_bool(OBJECT(cpu), "sme", false, &error_abort); + } + + arm_cpu_sme_finalize(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + arm_cpu_pauth_finalize(cpu, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -1440,6 +1792,7 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp) return; } } +#endif if (kvm_enabled()) { kvm_arm_steal_time_finalize(cpu, &local_err); @@ -1456,9 +1809,12 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) ARMCPU *cpu = ARM_CPU(dev); ARMCPUClass *acc = ARM_CPU_GET_CLASS(dev); CPUARMState *env = &cpu->env; - int pagebits; Error *local_err = NULL; - bool no_aa32 = false; + +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) + /* Use pc-relative instructions in system-mode */ + cs->tcg_cflags |= CF_PCREL; +#endif /* If we needed to query the host kernel for the CPU features * then it's possible that might have failed in the initfn, but @@ -1490,25 +1846,32 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } } - if (kvm_enabled()) { + if (!tcg_enabled() && !qtest_enabled()) { /* + * We assume that no accelerator except TCG (and the "not really an + * accelerator" qtest) can handle these features, because Arm hardware + * virtualization can't virtualize them. + * * Catch all the cases which might cause us to create more than one * address space for the CPU (otherwise we will assert() later in * cpu_address_space_init()). */ if (arm_feature(env, ARM_FEATURE_M)) { error_setg(errp, - "Cannot enable KVM when using an M-profile guest CPU"); + "Cannot enable %s when using an M-profile guest CPU", + current_accel_name()); return; } if (cpu->has_el3) { error_setg(errp, - "Cannot enable KVM when guest CPU has EL3 enabled"); + "Cannot enable %s when guest CPU has EL3 enabled", + current_accel_name()); return; } if (cpu->tag_memory) { error_setg(errp, - "Cannot enable KVM when guest CPUs has MTE enabled"); + "Cannot enable %s when guest CPUs has MTE enabled", + current_accel_name()); return; } } @@ -1552,6 +1915,17 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) return; } +#ifdef CONFIG_USER_ONLY + /* + * User mode relies on IC IVAU instructions to catch modification of + * dual-mapped code. + * + * Clear CTR_EL0.DIC to ensure that software that honors these flags uses + * IC IVAU even if the emulated processor does not normally require it. + */ + cpu->ctr = FIELD_DP64(cpu->ctr, CTR_EL0, DIC, 0); +#endif + if (arm_feature(env, ARM_FEATURE_AARCH64) && cpu->has_vfp != cpu->has_neon) { /* @@ -1563,6 +1937,19 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) return; } + if (cpu->has_vfp_d32 != cpu->has_neon) { + error_setg(errp, "ARM CPUs must have both VFP-D32 and Neon or neither"); + return; + } + + if (!cpu->has_vfp_d32) { + uint32_t u; + + u = cpu->isar.mvfr0; + u = FIELD_DP32(u, MVFR0, SIMDREG, 1); /* 16 registers */ + cpu->isar.mvfr0 = u; + } + if (!cpu->has_vfp) { uint64_t t; uint32_t u; @@ -1703,112 +2090,45 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu->isar.id_isar3 = u; } - /* Some features automatically imply others: */ - if (arm_feature(env, ARM_FEATURE_V8)) { - if (arm_feature(env, ARM_FEATURE_M)) { - set_feature(env, ARM_FEATURE_V7); - } else { - set_feature(env, ARM_FEATURE_V7VE); - } - } - - /* - * There exist AArch64 cpus without AArch32 support. When KVM - * queries ID_ISAR0_EL1 on such a host, the value is UNKNOWN. - * Similarly, we cannot check ID_AA64PFR0 without AArch64 support. - * As a general principle, we also do not make ID register - * consistency checks anywhere unless using TCG, because only - * for TCG would a consistency-check failure be a QEMU bug. - */ - if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { - no_aa32 = !cpu_isar_feature(aa64_aa32, cpu); - } - - if (arm_feature(env, ARM_FEATURE_V7VE)) { - /* v7 Virtualization Extensions. In real hardware this implies - * EL2 and also the presence of the Security Extensions. - * For QEMU, for backwards-compatibility we implement some - * CPUs or CPU configs which have no actual EL2 or EL3 but do - * include the various other features that V7VE implies. - * 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(aa32_arm_div, cpu)); - set_feature(env, ARM_FEATURE_LPAE); - set_feature(env, ARM_FEATURE_V7); - } - if (arm_feature(env, ARM_FEATURE_V7)) { - set_feature(env, ARM_FEATURE_VAPA); - set_feature(env, ARM_FEATURE_THUMB2); - set_feature(env, ARM_FEATURE_MPIDR); - if (!arm_feature(env, ARM_FEATURE_M)) { - set_feature(env, ARM_FEATURE_V6K); - } else { - set_feature(env, ARM_FEATURE_V6); - } - - /* Always define VBAR for V7 CPUs even if it doesn't exist in - * non-EL3 configs. This is needed by some legacy boards. - */ - set_feature(env, ARM_FEATURE_VBAR); - } - if (arm_feature(env, ARM_FEATURE_V6K)) { - set_feature(env, ARM_FEATURE_V6); - set_feature(env, ARM_FEATURE_MVFR); - } - 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(aa32_jazelle, cpu)); - set_feature(env, ARM_FEATURE_AUXCR); - } - } - if (arm_feature(env, ARM_FEATURE_V5)) { - set_feature(env, ARM_FEATURE_V4T); - } - if (arm_feature(env, ARM_FEATURE_LPAE)) { - set_feature(env, ARM_FEATURE_V7MP); - } - if (arm_feature(env, ARM_FEATURE_CBAR_RO)) { - set_feature(env, ARM_FEATURE_CBAR); - } - if (arm_feature(env, ARM_FEATURE_THUMB2) && - !arm_feature(env, ARM_FEATURE_M)) { - set_feature(env, ARM_FEATURE_THUMB_DSP); - } /* * 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(&cpu->env, ARM_FEATURE_AARCH64) || + assert(arm_feature(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) && - !arm_feature(env, ARM_FEATURE_PMSA)) { - /* v7VMSA drops support for the old ARMv5 tiny pages, so we - * can use 4K pages. - */ - pagebits = 12; - } else { - /* For CPUs which might have tiny 1K pages, or which have an - * MPU and might have small region sizes, stick with 1K pages. - */ - pagebits = 10; - } - if (!set_preferred_target_page_bits(pagebits)) { - /* This can only ever happen for hotplugging a CPU, or if - * the board code incorrectly creates a CPU which it has - * promised via minimum_page_size that it will not. - */ - error_setg(errp, "This CPU requires a smaller page size than the " - "system is using"); - return; +#ifndef CONFIG_USER_ONLY + { + int pagebits; + if (arm_feature(env, ARM_FEATURE_V7) && + !arm_feature(env, ARM_FEATURE_M) && + !arm_feature(env, ARM_FEATURE_PMSA)) { + /* + * v7VMSA drops support for the old ARMv5 tiny pages, + * so we can use 4K pages. + */ + pagebits = 12; + } else { + /* + * For CPUs which might have tiny 1K pages, or which have an + * MPU and might have small region sizes, stick with 1K pages. + */ + pagebits = 10; + } + if (!set_preferred_target_page_bits(pagebits)) { + /* + * This can only ever happen for hotplugging a CPU, or if + * the board code incorrectly creates a CPU which it has + * promised via minimum_page_size that it will not. + */ + error_setg(errp, "This CPU requires a smaller page size " + "than the system is using"); + return; + } } +#endif /* This cpu-id-to-MPIDR affinity is used only for TCG; KVM will override it. * We don't support setting cluster ID ([16..23]) (known as Aff2 @@ -1816,8 +2136,8 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * so these bits always RAZ. */ if (cpu->mp_affinity == ARM64_AFFINITY_INVALID) { - cpu->mp_affinity = arm_cpu_mp_affinity(cs->cpu_index, - ARM_DEFAULT_CPUS_PER_CLUSTER); + cpu->mp_affinity = arm_build_mp_affinity(cs->cpu_index, + ARM_DEFAULT_CPUS_PER_CLUSTER); } if (cpu->reset_hivecs) { @@ -1825,7 +2145,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } if (cpu->cfgend) { - if (arm_feature(&cpu->env, ARM_FEATURE_V7)) { + if (arm_feature(env, ARM_FEATURE_V7)) { cpu->reset_sctlr |= SCTLR_EE; } else { cpu->reset_sctlr |= SCTLR_B; @@ -1846,6 +2166,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu->isar.id_dfr0 = FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, COPSDBG, 0); cpu->isar.id_aa64pfr0 = FIELD_DP64(cpu->isar.id_aa64pfr0, ID_AA64PFR0, EL3, 0); + + /* Disable the realm management extension, which requires EL3. */ + cpu->isar.id_aa64pfr0 = FIELD_DP64(cpu->isar.id_aa64pfr0, + ID_AA64PFR0, RME, 0); } if (!cpu->has_el2) { @@ -1886,25 +2210,71 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) ID_PFR1, VIRTUALIZATION, 0); } -#ifndef CONFIG_USER_ONLY - if (cpu->tag_memory == NULL && cpu_isar_feature(aa64_mte, cpu)) { + if (cpu_isar_feature(aa64_mte, cpu)) { /* - * Disable the MTE feature bits if we do not have tag-memory - * provided by the machine. + * The architectural range of GM blocksize is 2-6, however qemu + * doesn't support blocksize of 2 (see HELPER(ldgm)). */ - cpu->isar.id_aa64pfr1 = - FIELD_DP64(cpu->isar.id_aa64pfr1, ID_AA64PFR1, MTE, 0); - } + if (tcg_enabled()) { + assert(cpu->gm_blocksize >= 3 && cpu->gm_blocksize <= 6); + } + +#ifndef CONFIG_USER_ONLY + /* + * If we do not have tag-memory provided by the machine, + * reduce MTE support to instructions enabled at EL0. + * This matches Cortex-A710 BROADCASTMTE input being LOW. + */ + if (cpu->tag_memory == NULL) { + cpu->isar.id_aa64pfr1 = + FIELD_DP64(cpu->isar.id_aa64pfr1, ID_AA64PFR1, MTE, 1); + } #endif + } + + if (tcg_enabled()) { + /* + * Don't report some architectural features in the ID registers + * where TCG does not yet implement it (not even a minimal + * stub version). This avoids guests falling over when they + * try to access the non-existent system registers for them. + */ + /* FEAT_SPE (Statistical Profiling Extension) */ + cpu->isar.id_aa64dfr0 = + FIELD_DP64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, PMSVER, 0); + /* FEAT_TRBE (Trace Buffer Extension) */ + cpu->isar.id_aa64dfr0 = + FIELD_DP64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, TRACEBUFFER, 0); + /* FEAT_TRF (Self-hosted Trace Extension) */ + cpu->isar.id_aa64dfr0 = + FIELD_DP64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, TRACEFILT, 0); + cpu->isar.id_dfr0 = + FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, TRACEFILT, 0); + /* Trace Macrocell system register access */ + cpu->isar.id_aa64dfr0 = + FIELD_DP64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, TRACEVER, 0); + cpu->isar.id_dfr0 = + FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, COPTRC, 0); + /* Memory mapped trace */ + cpu->isar.id_dfr0 = + FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, MMAPTRC, 0); + /* FEAT_AMU (Activity Monitors Extension) */ + cpu->isar.id_aa64pfr0 = + FIELD_DP64(cpu->isar.id_aa64pfr0, ID_AA64PFR0, AMU, 0); + cpu->isar.id_pfr0 = + FIELD_DP32(cpu->isar.id_pfr0, ID_PFR0, AMU, 0); + /* FEAT_MPAM (Memory Partitioning and Monitoring Extension) */ + cpu->isar.id_aa64pfr0 = + FIELD_DP64(cpu->isar.id_aa64pfr0, ID_AA64PFR0, MPAM, 0); + } /* MPU can be configured out of a PMSA CPU either by setting has-mpu * to false or by setting pmsav7-dregion to 0. */ - if (!cpu->has_mpu) { - cpu->pmsav7_dregion = 0; - } - if (cpu->pmsav7_dregion == 0) { + if (!cpu->has_mpu || cpu->pmsav7_dregion == 0) { cpu->has_mpu = false; + cpu->pmsav7_dregion = 0; + cpu->pmsav8r_hdregion = 0; } if (arm_feature(env, ARM_FEATURE_PMSA) && @@ -1931,6 +2301,19 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) env->pmsav7.dracr = g_new0(uint32_t, nr); } } + + if (cpu->pmsav8r_hdregion > 0xff) { + error_setg(errp, "PMSAv8 MPU EL2 #regions invalid %" PRIu32, + cpu->pmsav8r_hdregion); + return; + } + + if (cpu->pmsav8r_hdregion) { + env->pmsav8.hprbar = g_new0(uint32_t, + cpu->pmsav8r_hdregion); + env->pmsav8.hprlar = g_new0(uint32_t, + cpu->pmsav8r_hdregion); + } } if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { @@ -1951,6 +2334,12 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) set_feature(env, ARM_FEATURE_VBAR); } +#ifndef CONFIG_USER_ONLY + if (tcg_enabled() && cpu_isar_feature(aa64_rme, cpu)) { + arm_register_el_change_hook(cpu, >_rme_post_el_change, 0); + } +#endif + register_cp_regs_for_features(cpu); arm_cpu_register_gdb_regs_for_features(cpu); @@ -2047,10 +2436,7 @@ static ObjectClass *arm_cpu_class_by_name(const char *cpu_model) oc = object_class_by_name(typename); g_strfreev(cpuname); g_free(typename); - if (!oc || !object_class_dynamic_cast(oc, TYPE_ARM_CPU) || - object_class_is_abstract(oc)) { - return NULL; - } + return oc; } @@ -2063,15 +2449,15 @@ static Property arm_cpu_properties[] = { DEFINE_PROP_END_OF_LIST() }; -static gchar *arm_gdb_arch_name(CPUState *cs) +static const gchar *arm_gdb_arch_name(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - return g_strdup("iwmmxt"); + return "iwmmxt"; } - return g_strdup("arm"); + return "arm"; } #ifndef CONFIG_USER_ONLY @@ -2088,10 +2474,11 @@ static const struct SysemuCPUOps arm_sysemu_ops = { #endif #ifdef CONFIG_TCG -static const struct TCGCPUOps arm_tcg_ops = { +static const TCGCPUOps arm_tcg_ops = { .initialize = arm_translate_init, .synchronize_from_tb = arm_cpu_synchronize_from_tb, .debug_excp_handler = arm_debug_excp_handler, + .restore_state_to_opc = arm_restore_state_to_opc, #ifdef CONFIG_USER_ONLY .record_sigsegv = arm_cpu_record_sigsegv, @@ -2114,26 +2501,28 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) ARMCPUClass *acc = ARM_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(acc); DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); device_class_set_parent_realize(dc, arm_cpu_realizefn, &acc->parent_realize); device_class_set_props(dc, arm_cpu_properties); - device_class_set_parent_reset(dc, arm_cpu_reset, &acc->parent_reset); + + resettable_class_set_parent_phases(rc, NULL, arm_cpu_reset_hold, NULL, + &acc->parent_phases); cc->class_by_name = arm_cpu_class_by_name; cc->has_work = arm_cpu_has_work; + cc->mmu_index = arm_cpu_mmu_index; cc->dump_state = arm_cpu_dump_state; cc->set_pc = arm_cpu_set_pc; + cc->get_pc = arm_cpu_get_pc; cc->gdb_read_register = arm_cpu_gdb_read_register; cc->gdb_write_register = arm_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->sysemu_ops = &arm_sysemu_ops; #endif - cc->gdb_num_core_regs = 26; - cc->gdb_core_xml_file = "arm-core.xml"; cc->gdb_arch_name = arm_gdb_arch_name; - cc->gdb_get_dynamic_xml = arm_gdb_get_dynamic_xml; cc->gdb_stop_before_watchpoint = true; cc->disas_set_info = arm_disas_set_info; @@ -2153,18 +2542,17 @@ static void arm_cpu_instance_init(Object *obj) static void cpu_register_class_init(ObjectClass *oc, void *data) { ARMCPUClass *acc = ARM_CPU_CLASS(oc); + CPUClass *cc = CPU_CLASS(acc); acc->info = data; + cc->gdb_core_xml_file = "arm-core.xml"; } void arm_cpu_register(const ARMCPUInfo *info) { TypeInfo type_info = { .parent = TYPE_ARM_CPU, - .instance_size = sizeof(ARMCPU), - .instance_align = __alignof__(ARMCPU), .instance_init = arm_cpu_instance_init, - .class_size = sizeof(ARMCPUClass), .class_init = info->class_init ?: cpu_register_class_init, .class_data = (void *)info, }; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 78dbcb5592..bc0c84873f 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -25,7 +25,10 @@ #include "hw/registerfields.h" #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "exec/gdbstub.h" #include "qapi/qapi-types-common.h" +#include "target/arm/multiprocessing.h" +#include "target/arm/gtimer.h" /* ARM processors have a weak memory model */ #define TCG_GUEST_DEFAULT_MO (0) @@ -57,6 +60,7 @@ #define EXCP_UNALIGNED 22 /* v7M UNALIGNED UsageFault */ #define EXCP_DIVBYZERO 23 /* v7M DIVBYZERO UsageFault */ #define EXCP_VSERR 24 +#define EXCP_GPC 25 /* v9 Granule Protection Check Fault */ /* NB: add new EXCP_ defines to the array in arm_log_exception() too */ #define ARMV7M_EXCP_RESET 1 @@ -71,21 +75,6 @@ #define ARMV7M_EXCP_PENDSV 14 #define ARMV7M_EXCP_SYSTICK 15 -/* For M profile, some registers are banked secure vs non-secure; - * these are represented as a 2-element array where the first element - * is the non-secure copy and the second is the secure copy. - * When the CPU does not have implement the security extension then - * only the first element is used. - * This means that the copy for the current security state can be - * accessed via env->registerfield[env->v7m.secure] (whether the security - * extension is implemented or not). - */ -enum { - M_REG_NS = 0, - M_REG_S = 1, - M_REG_NUM_BANKS = 2, -}; - /* ARM-specific interrupt pending bits. */ #define CPU_INTERRUPT_FIQ CPU_INTERRUPT_TGT_EXT_1 #define CPU_INTERRUPT_VIRQ CPU_INTERRUPT_TGT_EXT_2 @@ -106,12 +95,6 @@ enum { #define offsetofhigh32(S, M) (offsetof(S, M) + sizeof(uint32_t)) #endif -/* Meanings of the ARMCPU object's four inbound GPIO lines */ -#define ARM_CPU_IRQ 0 -#define ARM_CPU_FIQ 1 -#define ARM_CPU_VIRQ 2 -#define ARM_CPU_VFIQ 3 - /* ARM-specific extra insn start words: * 1: Conditional execution bits * 2: Partial exception syndrome for data aborts @@ -119,12 +102,12 @@ enum { #define TARGET_INSN_START_EXTRA_WORDS 2 /* The 2nd extra word holding syndrome info for data aborts does not use - * the upper 6 bits nor the lower 14 bits. We mask and shift it down to + * the upper 6 bits nor the lower 13 bits. We mask and shift it down to * help the sleb128 encoder do a better job. * When restoring the CPU state, we shift it back up. */ #define ARM_INSN_START_WORD2_MASK ((1 << 26) - 1) -#define ARM_INSN_START_WORD2_SHIFT 14 +#define ARM_INSN_START_WORD2_SHIFT 13 /* We currently assume float and double are IEEE single and double precision respectively. @@ -135,23 +118,21 @@ enum { */ /** - * DynamicGDBXMLInfo: - * @desc: Contains the XML descriptions. - * @num: Number of the registers in this XML seen by GDB. + * DynamicGDBFeatureInfo: + * @desc: Contains the feature descriptions. * @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; +typedef struct DynamicGDBFeatureInfo { + GDBFeature desc; union { struct { uint32_t *keys; } cpregs; } data; -} DynamicGDBXMLInfo; +} DynamicGDBFeatureInfo; /* CPU state for each instance of a generic timer (in cp15 c14) */ typedef struct ARMGenericTimer { @@ -159,24 +140,6 @@ 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 GTIMER_HYPVIRT 4 -#define NUM_GTIMERS 5 - -typedef struct { - uint64_t raw_tcr; - uint32_t mask; - uint32_t base_mask; -} TCR; - -#define VTCR_NSW (1u << 29) -#define VTCR_NSA (1u << 30) -#define VSTCR_SW VTCR_NSW -#define VSTCR_SA VTCR_NSA - /* Define a maximum sized vector register. * For 32-bit, this is a 128-bit NEON/AdvSIMD register. * For 64-bit, this is a 2048-bit SVE register. @@ -205,14 +168,8 @@ typedef struct { #ifdef TARGET_AARCH64 # define ARM_MAX_VQ 16 -void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp); -void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp); -void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp); #else # define ARM_MAX_VQ 1 -static inline void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) { } -static inline void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) { } -static inline void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp) { } #endif typedef struct ARMVectorReg { @@ -237,6 +194,10 @@ typedef struct CPUARMTBFlags { target_ulong flags2; } CPUARMTBFlags; +typedef struct ARMMMUFaultInfo ARMMMUFaultInfo; + +typedef struct NVICState NVICState; + typedef struct CPUArchState { /* Regs for current mode. */ uint32_t regs[16]; @@ -258,6 +219,7 @@ typedef struct CPUArchState { * nRW (also known as M[4]) is kept, inverted, in env->aarch64 * DAIF (exception masks) are kept in env->daif * BTYPE is kept in env->btype + * SM and ZA are kept in env->svcr * all other bits are stored in their correct places in env->pstate */ uint32_t pstate; @@ -292,6 +254,7 @@ typedef struct CPUArchState { uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */ uint32_t btype; /* BTI branch type. spsr[11:10]. */ uint64_t daif; /* exception masks, in the bits they are in PSTATE */ + uint64_t svcr; /* PSTATE.{SM,ZA} in the bits they are in SVCR */ uint64_t elr_el[4]; /* AArch64 exception link regs */ uint64_t sp_el[4]; /* AArch64 banked stack pointers */ @@ -317,6 +280,7 @@ typedef struct CPUArchState { }; uint64_t sctlr_el[4]; }; + uint64_t vsctlr; /* Virtualization System control register. */ uint64_t cpacr_el1; /* Architectural feature access control register */ uint64_t cptr_el[4]; /* ARMv8 feature trap registers */ uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */ @@ -343,9 +307,9 @@ typedef struct CPUArchState { uint64_t vttbr_el2; /* Virtualization Translation Table Base. */ uint64_t vsttbr_el2; /* Secure Virtualization Translation Table. */ /* MMU translation table base control. */ - TCR tcr_el[4]; - TCR vtcr_el2; /* Virtualization Translation Control. */ - TCR vstcr_el2; /* Secure Virtualization Translation Control. */ + uint64_t tcr_el[4]; + uint64_t vtcr_el2; /* Virtualization Translation Control. */ + uint64_t vstcr_el2; /* Secure Virtualization Translation Control. */ uint32_t c2_data; /* MPU data cacheable bits. */ uint32_t c2_insn; /* MPU instruction cacheable bits. */ union { /* MMU domain access control register @@ -474,6 +438,7 @@ typedef struct CPUArchState { }; uint64_t tpidr_el[4]; }; + uint64_t tpidr2_el0; /* The secure banks of these registers don't map anywhere */ uint64_t tpidrurw_s; uint64_t tpidrprw_s; @@ -485,8 +450,9 @@ typedef struct CPUArchState { }; uint64_t c14_cntfrq; /* Counter Frequency register */ uint64_t c14_cntkctl; /* Timer Control register */ - uint32_t cnthctl_el2; /* Counter/Timer Hyp Control register */ + uint64_t cnthctl_el2; /* Counter/Timer Hyp Control register */ uint64_t cntvoff_el2; /* Counter Virtual Offset register */ + uint64_t cntpoff_el2; /* Counter Physical Offset register */ ARMGenericTimer c14_timer[NUM_GTIMERS]; uint32_t c15_cpar; /* XScale Coprocessor Access Register */ uint32_t c15_ticonfig; /* TI925T configuration byte. */ @@ -501,8 +467,10 @@ typedef struct CPUArchState { uint64_t dbgbcr[16]; /* breakpoint control registers */ uint64_t dbgwvr[16]; /* watchpoint value registers */ uint64_t dbgwcr[16]; /* watchpoint control registers */ + uint64_t dbgclaim; /* DBGCLAIM bits */ uint64_t mdscr_el1; uint64_t oslsr_el1; /* OS Lock Status */ + uint64_t osdlr_el1; /* OS DoubleLock status */ uint64_t mdcr_el2; uint64_t mdcr_el3; /* Stores the architectural value of the counter *the last time it was @@ -533,6 +501,24 @@ typedef struct CPUArchState { uint64_t disr_el1; uint64_t vdisr_el2; uint64_t vsesr_el2; + + /* + * Fine-Grained Trap registers. We store these as arrays so the + * access checking code doesn't have to manually select + * HFGRTR_EL2 vs HFDFGRTR_EL2 etc when looking up the bit to test. + * FEAT_FGT2 will add more elements to these arrays. + */ + uint64_t fgt_read[2]; /* HFGRTR, HDFGRTR */ + uint64_t fgt_write[2]; /* HFGWTR, HDFGWTR */ + uint64_t fgt_exec[1]; /* HFGITR */ + + /* RME registers */ + uint64_t gpccr_el3; + uint64_t gptbr_el3; + uint64_t mfar_el3; + + /* NV2 register */ + uint64_t vncr_el2; } cp15; struct { @@ -666,11 +652,19 @@ typedef struct CPUArchState { float_status standard_fp_status; float_status standard_fp_status_f16; - /* ZCR_EL[1-3] */ - uint64_t zcr_el[4]; + uint64_t zcr_el[4]; /* ZCR_EL[1-3] */ + uint64_t smcr_el[4]; /* SMCR_EL[1-3] */ } vfp; + uint64_t exclusive_addr; uint64_t exclusive_val; + /* + * Contains the 'val' for the second 64-bit register of LDXP, which comes + * from the higher address, not the high part of a complete 128-bit value. + * In some ways it might be more convenient to record the exclusive value + * as the low and high halves of a 128 bit data value, but the current + * semantics of these fields are baked into the migration format. + */ uint64_t exclusive_high; /* iwMMXt coprocessor state. */ @@ -691,16 +685,36 @@ typedef struct CPUArchState { } keys; uint64_t scxtnum_el[4]; -#endif -#if defined(CONFIG_USER_ONLY) - /* For usermode syscall translation. */ - int eabi; + /* + * SME ZA storage -- 256 x 256 byte array, with bytes in host word order, + * as we do with vfp.zregs[]. This corresponds to the architectural ZA + * array, where ZA[N] is in the least-significant bytes of env->zarray[N]. + * When SVL is less than the architectural maximum, the accessible + * storage is restricted, such that if the SVL is X bytes the guest can + * see only the bottom X elements of zarray[], and only the least + * significant X bytes of each element of the array. (In other words, + * the observable part is always square.) + * + * The ZA storage can also be considered as a set of square tiles of + * elements of different sizes. The mapping from tiles to the ZA array + * is architecturally defined, such that for tiles of elements of esz + * bytes, the Nth row (or "horizontal slice") of tile T is in + * ZA[T + N * esz]. Note that this means that each tile is not contiguous + * in the ZA storage, because its rows are striped through the ZA array. + * + * Because this is so large, keep this toward the end of the reset area, + * to keep the offsets into the rest of the structure smaller. + */ + ARMVectorReg zarray[ARM_MAX_VQ * 16]; #endif struct CPUBreakpoint *cpu_breakpoint[16]; struct CPUWatchpoint *cpu_watchpoint[16]; + /* Optional fault info across tlb lookup. */ + ARMMMUFaultInfo *tlb_fi; + /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; @@ -726,8 +740,11 @@ typedef struct CPUArchState { */ uint32_t *rbar[M_REG_NUM_BANKS]; uint32_t *rlar[M_REG_NUM_BANKS]; + uint32_t *hprbar; + uint32_t *hprlar; uint32_t mair0[M_REG_NUM_BANKS]; uint32_t mair1[M_REG_NUM_BANKS]; + uint32_t hprselr; } pmsav8; /* v8M SAU */ @@ -738,10 +755,15 @@ typedef struct CPUArchState { uint32_t ctrl; } sau; - void *nvic; +#if !defined(CONFIG_USER_ONLY) + NVICState *nvic; const struct arm_boot_info *boot_info; /* Store GICv3CPUState to access from this struct */ void *gicv3state; +#else /* CONFIG_USER_ONLY */ + /* For usermode syscall translation. */ + bool eabi; +#endif /* CONFIG_USER_ONLY */ #ifdef TARGET_TAGGED_ADDRESSES /* Linux syscall tagged address support */ @@ -782,6 +804,19 @@ typedef enum ARMPSCIState { typedef struct ARMISARegisters ARMISARegisters; +/* + * In map, each set bit is a supported vector length of (bit-number + 1) * 16 + * bytes, i.e. each bit number + 1 is the vector length in quadwords. + * + * While processing properties during initialization, corresponding init bits + * are set for bits in sve_vq_map that have been set by properties. + * + * Bits set in supported represent valid vector lengths for the CPU type. + */ +typedef struct { + uint32_t map, init, supported; +} ARMVQMap; + /** * ARMCPU: * @env: #CPUARMState @@ -789,11 +824,8 @@ typedef struct ARMISARegisters ARMISARegisters; * An ARM CPU core. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUARMState env; /* Coprocessor information */ @@ -818,8 +850,10 @@ struct ArchCPU { uint64_t *cpreg_vmstate_values; int32_t cpreg_vmstate_array_len; - DynamicGDBXMLInfo dyn_sysreg_xml; - DynamicGDBXMLInfo dyn_svereg_xml; + DynamicGDBFeatureInfo dyn_sysreg_feature; + DynamicGDBFeatureInfo dyn_svereg_feature; + DynamicGDBFeatureInfo dyn_m_systemreg_feature; + DynamicGDBFeatureInfo dyn_m_secextreg_feature; /* Timers used by the generic (architected) timer */ QEMUTimer *gt_timer[NUM_GTIMERS]; @@ -865,6 +899,8 @@ struct ArchCPU { bool has_pmu; /* CPU has VFP */ bool has_vfp; + /* CPU has 32 VFP registers */ + bool has_vfp_d32; /* CPU has Neon */ bool has_neon; /* CPU has M-profile DSP extension */ @@ -874,6 +910,8 @@ struct ArchCPU { bool has_mpu; /* PMSAv7 MPU number of supported regions */ uint32_t pmsav7_dregion; + /* PMSAv8 MPU number of supported hyp regions */ + uint32_t pmsav8r_hdregion; /* v8M SAU number of supported regions */ uint32_t sau_sregion; @@ -892,6 +930,7 @@ struct ArchCPU { */ uint32_t kvm_target; +#ifdef CONFIG_KVM /* KVM init features for this CPU */ uint32_t kvm_init_features[7]; @@ -904,6 +943,7 @@ struct ArchCPU { /* KVM steal time */ OnOffAuto kvm_steal_time; +#endif /* CONFIG_KVM */ /* Uniprocessor system with MP extensions */ bool mp_is_up; @@ -948,6 +988,7 @@ struct ArchCPU { uint32_t id_mmfr2; uint32_t id_mmfr3; uint32_t id_mmfr4; + uint32_t id_mmfr5; uint32_t id_pfr0; uint32_t id_pfr1; uint32_t id_pfr2; @@ -955,9 +996,13 @@ struct ArchCPU { uint32_t mvfr1; uint32_t mvfr2; uint32_t id_dfr0; + uint32_t id_dfr1; uint32_t dbgdidr; + uint32_t dbgdevid; + uint32_t dbgdevid1; uint64_t id_aa64isar0; uint64_t id_aa64isar1; + uint64_t id_aa64isar2; uint64_t id_aa64pfr0; uint64_t id_aa64pfr1; uint64_t id_aa64mmfr0; @@ -988,6 +1033,7 @@ struct ArchCPU { uint64_t reset_cbar; uint32_t reset_auxcr; bool reset_hivecs; + uint8_t reset_l0gptsz; /* * Intermediate values used during property parsing. @@ -995,10 +1041,14 @@ struct ArchCPU { */ bool prop_pauth; bool prop_pauth_impdef; + bool prop_pauth_qarma3; bool prop_lpa2; /* DCZ blocksize, in log_2(words), ie low 4 bits of DCZID_EL0 */ - uint32_t dcz_blocksize; + uint8_t dcz_blocksize; + /* GM blocksize, in log_2(words), ie low 4 bits of GMID_EL0 */ + uint8_t gm_blocksize; + uint64_t rvbar_prop; /* Property/input signals. */ /* Configurable aspects of GIC cpu interface (which is part of the CPU) */ @@ -1028,64 +1078,109 @@ struct ArchCPU { #ifdef CONFIG_USER_ONLY /* Used to set the default vector length at process start. */ uint32_t sve_default_vq; + uint32_t sme_default_vq; #endif - /* - * In sve_vq_map each set bit is a supported vector length of - * (bit-number + 1) * 16 bytes, i.e. each bit number + 1 is the vector - * length in quadwords. - * - * While processing properties during initialization, corresponding - * sve_vq_init bits are set for bits in sve_vq_map that have been - * set by properties. - * - * Bits set in sve_vq_supported represent valid vector lengths for - * the CPU type. - */ - uint32_t sve_vq_map; - uint32_t sve_vq_init; - uint32_t sve_vq_supported; + ARMVQMap sve_vq; + ARMVQMap sme_vq; /* Generic timer counter frequency, in Hz */ uint64_t gt_cntfrq_hz; }; +typedef struct ARMCPUInfo { + const char *name; + void (*initfn)(Object *obj); + void (*class_init)(ObjectClass *oc, void *data); +} ARMCPUInfo; + +/** + * ARMCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * An ARM CPU model. + */ +struct ARMCPUClass { + CPUClass parent_class; + + const ARMCPUInfo *info; + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; + +struct AArch64CPUClass { + ARMCPUClass parent_class; +}; + +/* Callback functions for the generic timer's timers. */ +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); + unsigned int gt_cntfrq_period_ns(ARMCPU *cpu); +void gt_rme_post_el_change(ARMCPU *cpu, void *opaque); void arm_cpu_post_init(Object *obj); -uint64_t arm_cpu_mp_affinity(int idx, uint8_t clustersz); +#define ARM_AFF0_SHIFT 0 +#define ARM_AFF0_MASK (0xFFULL << ARM_AFF0_SHIFT) +#define ARM_AFF1_SHIFT 8 +#define ARM_AFF1_MASK (0xFFULL << ARM_AFF1_SHIFT) +#define ARM_AFF2_SHIFT 16 +#define ARM_AFF2_MASK (0xFFULL << ARM_AFF2_SHIFT) +#define ARM_AFF3_SHIFT 32 +#define ARM_AFF3_MASK (0xFFULL << ARM_AFF3_SHIFT) +#define ARM_DEFAULT_CPUS_PER_CLUSTER 8 + +#define ARM32_AFFINITY_MASK (ARM_AFF0_MASK | ARM_AFF1_MASK | ARM_AFF2_MASK) +#define ARM64_AFFINITY_MASK \ + (ARM_AFF0_MASK | ARM_AFF1_MASK | ARM_AFF2_MASK | ARM_AFF3_MASK) +#define ARM64_AFFINITY_INVALID (~ARM64_AFFINITY_MASK) + +uint64_t arm_build_mp_affinity(int idx, uint8_t clustersz); #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_arm_cpu; void arm_cpu_do_interrupt(CPUState *cpu); void arm_v7m_cpu_do_interrupt(CPUState *cpu); -#endif /* !CONFIG_USER_ONLY */ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, MemTxAttrs *attrs); +#endif /* !CONFIG_USER_ONLY */ 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); -/* - * Helpers to dynamically generates XML descriptions of the sysregs - * and SVE registers. Returns the number of registers in each set. - */ -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 - * if the XML name doesn't match the predefined one. - */ -const char *arm_gdb_get_dynamic_xml(CPUState *cpu, const char *xmlname); - int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque); + int cpuid, DumpState *s); int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque); + int cpuid, DumpState *s); + +/** + * arm_emulate_firmware_reset: Emulate firmware CPU reset handling + * @cpu: CPU (which must have been freshly reset) + * @target_el: exception level to put the CPU into + * @secure: whether to put the CPU in secure state + * + * When QEMU is directly running a guest kernel at a lower level than + * EL3 it implicitly emulates some aspects of the guest firmware. + * This includes that on reset we need to configure the parts of the + * CPU corresponding to EL3 so that the real guest code can run at its + * lower exception level. This function does that post-reset CPU setup, + * for when we do direct boot of a guest kernel, and for when we + * emulate PSCI and similar firmware interfaces starting a CPU at a + * lower exception level. + * + * @target_el must be an EL implemented by the CPU between 1 and 3. + * We do not support dropping into a Secure EL other than 3. + * + * It is the responsibility of the caller to call arm_rebuild_hflags(). + */ +void arm_emulate_firmware_reset(CPUState *cpustate, int target_el); #ifdef TARGET_AARCH64 int aarch64_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); @@ -1093,8 +1188,7 @@ 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); -void aarch64_add_pauth_properties(Object *obj); +void aarch64_set_svcr(CPUARMState *env, uint64_t new, uint64_t mask); /* * SVE registers are encoded in KVM's memory in an endianness-invariant format. @@ -1125,7 +1219,6 @@ static inline void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq) { } static inline void aarch64_sve_change_el(CPUARMState *env, int o, int n, bool a) { } -static inline void aarch64_add_sve_properties(Object *obj) { } #endif void aarch64_sync_32_to_64(CPUARMState *env); @@ -1133,15 +1226,21 @@ void aarch64_sync_64_to_32(CPUARMState *env); int fp_exception_el(CPUARMState *env, int cur_el); int sve_exception_el(CPUARMState *env, int cur_el); +int sme_exception_el(CPUARMState *env, int cur_el); /** - * sve_vqm1_for_el: + * sve_vqm1_for_el_sm: * @env: CPUARMState * @el: exception level + * @sm: streaming mode * - * Compute the current SVE vector length for @el, in units of + * Compute the current vector length for @el & @sm, in units of * Quadwords Minus 1 -- the same scale used for ZCR_ELx.LEN. + * If @sm, compute for SVL, otherwise NVL. */ +uint32_t sve_vqm1_for_el_sm(CPUARMState *env, int el, bool sm); + +/* Likewise, but using @sm = PSTATE.SM. */ uint32_t sve_vqm1_for_el(CPUARMState *env, int el); static inline bool is_a64(CPUARMState *env) @@ -1198,7 +1297,7 @@ void pmu_init(ARMCPU *cpu); #define SCTLR_D (1U << 5) /* up to v5; RAO in v6 */ #define SCTLR_CP15BEN (1U << 5) /* v7 onward */ #define SCTLR_L (1U << 6) /* up to v5; RAO in v6 and v7; RAZ in v8 */ -#define SCTLR_nAA (1U << 6) /* when v8.4-LSE is implemented */ +#define SCTLR_nAA (1U << 6) /* when FEAT_LSE2 is implemented */ #define SCTLR_B (1U << 7) /* up to v6; RAZ in v7 */ #define SCTLR_ITD (1U << 7) /* v8 onward */ #define SCTLR_S (1U << 8) /* up to v6; RAZ in v7 */ @@ -1248,6 +1347,7 @@ void pmu_init(ARMCPU *cpu); #define SCTLR_EnIB (1U << 30) /* v8.3, AArch64 only */ #define SCTLR_EnIA (1U << 31) /* v8.3, AArch64 only */ #define SCTLR_DSSBS_32 (1U << 31) /* v8.5, AArch32 only */ +#define SCTLR_MSCEN (1ULL << 33) /* FEAT_MOPS */ #define SCTLR_BT0 (1ULL << 35) /* v8.5-BTI */ #define SCTLR_BT1 (1ULL << 36) /* v8.5-BTI */ #define SCTLR_ITFSB (1ULL << 37) /* v8.5-MemTag */ @@ -1271,64 +1371,6 @@ void pmu_init(ARMCPU *cpu); #define SCTLR_SPINTMASK (1ULL << 62) /* FEAT_NMI */ #define SCTLR_TIDCP (1ULL << 63) /* FEAT_TIDCP1 */ -/* Bit definitions for CPACR (AArch32 only) */ -FIELD(CPACR, CP10, 20, 2) -FIELD(CPACR, CP11, 22, 2) -FIELD(CPACR, TRCDIS, 28, 1) /* matches CPACR_EL1.TTA */ -FIELD(CPACR, D32DIS, 30, 1) /* up to v7; RAZ in v8 */ -FIELD(CPACR, ASEDIS, 31, 1) - -/* Bit definitions for CPACR_EL1 (AArch64 only) */ -FIELD(CPACR_EL1, ZEN, 16, 2) -FIELD(CPACR_EL1, FPEN, 20, 2) -FIELD(CPACR_EL1, SMEN, 24, 2) -FIELD(CPACR_EL1, TTA, 28, 1) /* matches CPACR.TRCDIS */ - -/* Bit definitions for HCPTR (AArch32 only) */ -FIELD(HCPTR, TCP10, 10, 1) -FIELD(HCPTR, TCP11, 11, 1) -FIELD(HCPTR, TASE, 15, 1) -FIELD(HCPTR, TTA, 20, 1) -FIELD(HCPTR, TAM, 30, 1) /* matches CPTR_EL2.TAM */ -FIELD(HCPTR, TCPAC, 31, 1) /* matches CPTR_EL2.TCPAC */ - -/* Bit definitions for CPTR_EL2 (AArch64 only) */ -FIELD(CPTR_EL2, TZ, 8, 1) /* !E2H */ -FIELD(CPTR_EL2, TFP, 10, 1) /* !E2H, matches HCPTR.TCP10 */ -FIELD(CPTR_EL2, TSM, 12, 1) /* !E2H */ -FIELD(CPTR_EL2, ZEN, 16, 2) /* E2H */ -FIELD(CPTR_EL2, FPEN, 20, 2) /* E2H */ -FIELD(CPTR_EL2, SMEN, 24, 2) /* E2H */ -FIELD(CPTR_EL2, TTA, 28, 1) -FIELD(CPTR_EL2, TAM, 30, 1) /* matches HCPTR.TAM */ -FIELD(CPTR_EL2, TCPAC, 31, 1) /* matches HCPTR.TCPAC */ - -/* Bit definitions for CPTR_EL3 (AArch64 only) */ -FIELD(CPTR_EL3, EZ, 8, 1) -FIELD(CPTR_EL3, TFP, 10, 1) -FIELD(CPTR_EL3, ESM, 12, 1) -FIELD(CPTR_EL3, TTA, 20, 1) -FIELD(CPTR_EL3, TAM, 30, 1) -FIELD(CPTR_EL3, TCPAC, 31, 1) - -#define MDCR_EPMAD (1U << 21) -#define MDCR_EDAD (1U << 20) -#define MDCR_SPME (1U << 17) /* MDCR_EL3 */ -#define MDCR_HPMD (1U << 17) /* MDCR_EL2 */ -#define MDCR_SDD (1U << 16) -#define MDCR_SPD (3U << 14) -#define MDCR_TDRA (1U << 11) -#define MDCR_TDOSA (1U << 10) -#define MDCR_TDA (1U << 9) -#define MDCR_TDE (1U << 8) -#define MDCR_HPME (1U << 7) -#define MDCR_TPM (1U << 6) -#define MDCR_TPMCR (1U << 5) -#define MDCR_HPMN (0x1fU) - -/* Not all of the MDCR_EL3 bits are present in the 32-bit SDCR */ -#define SDCR_VALID_MASK (MDCR_EPMAD | MDCR_EDAD | MDCR_SPME | MDCR_SPD) - #define CPSR_M (0x1fU) #define CPSR_T (1U << 5) #define CPSR_F (1U << 6) @@ -1375,22 +1417,6 @@ FIELD(CPTR_EL3, TCPAC, 31, 1) #define XPSR_NZCV CPSR_NZCV #define XPSR_IT CPSR_IT -#define TTBCR_N (7U << 0) /* TTBCR.EAE==0 */ -#define TTBCR_T0SZ (7U << 0) /* TTBCR.EAE==1 */ -#define TTBCR_PD0 (1U << 4) -#define TTBCR_PD1 (1U << 5) -#define TTBCR_EPD0 (1U << 7) -#define TTBCR_IRGN0 (3U << 8) -#define TTBCR_ORGN0 (3U << 10) -#define TTBCR_SH0 (3U << 12) -#define TTBCR_T1SZ (3U << 16) -#define TTBCR_A1 (1U << 22) -#define TTBCR_EPD1 (1U << 23) -#define TTBCR_IRGN1 (3U << 24) -#define TTBCR_ORGN1 (3U << 26) -#define TTBCR_SH1 (1U << 28) -#define TTBCR_EAE (1U << 31) - /* Bit definitions for ARMv8 SPSR (PSTATE) format. * Only these are valid when in AArch64 mode; in * AArch32 mode SPSRs are basically CPSR-format. @@ -1426,6 +1452,14 @@ FIELD(CPTR_EL3, TCPAC, 31, 1) #define PSTATE_MODE_EL1t 4 #define PSTATE_MODE_EL0t 0 +/* PSTATE bits that are accessed via SVCR and not stored in SPSR_ELx. */ +FIELD(SVCR, SM, 0, 1) +FIELD(SVCR, ZA, 1, 1) + +/* Fields for SMCR_ELx. */ +FIELD(SMCR, LEN, 0, 4) +FIELD(SMCR, FA64, 31, 1) + /* Write a new value to v7m.exception, thus transitioning into or out * of Handler mode; this may result in a change of active stack pointer. */ @@ -1567,7 +1601,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_TME (1ULL << 39) #define HCR_APK (1ULL << 40) #define HCR_API (1ULL << 41) #define HCR_NV (1ULL << 42) @@ -1576,7 +1610,7 @@ 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_GPF (1ULL << 48) #define HCR_TID4 (1ULL << 49) #define HCR_TICAB (1ULL << 50) #define HCR_AMVOFFEN (1ULL << 51) @@ -1590,48 +1624,33 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define HCR_TWEDEN (1ULL << 59) #define HCR_TWEDEL MAKE_64BIT_MASK(60, 4) -#define HCRX_ENAS0 (1ULL << 0) -#define HCRX_ENALS (1ULL << 1) -#define HCRX_ENASR (1ULL << 2) -#define HCRX_FNXS (1ULL << 3) -#define HCRX_FGTNXS (1ULL << 4) -#define HCRX_SMPME (1ULL << 5) -#define HCRX_TALLINT (1ULL << 6) -#define HCRX_VINMI (1ULL << 7) -#define HCRX_VFNMI (1ULL << 8) -#define HCRX_CMOW (1ULL << 9) -#define HCRX_MCE2 (1ULL << 10) -#define HCRX_MSCEN (1ULL << 11) - -#define HPFAR_NS (1ULL << 63) - -#define SCR_NS (1U << 0) -#define SCR_IRQ (1U << 1) -#define SCR_FIQ (1U << 2) -#define SCR_EA (1U << 3) -#define SCR_FW (1U << 4) -#define SCR_AW (1U << 5) -#define SCR_NET (1U << 6) -#define SCR_SMD (1U << 7) -#define SCR_HCE (1U << 8) -#define SCR_SIF (1U << 9) -#define SCR_RW (1U << 10) -#define SCR_ST (1U << 11) -#define SCR_TWI (1U << 12) -#define SCR_TWE (1U << 13) -#define SCR_TLOR (1U << 14) -#define SCR_TERR (1U << 15) -#define SCR_APK (1U << 16) -#define SCR_API (1U << 17) -#define SCR_EEL2 (1U << 18) -#define SCR_EASE (1U << 19) -#define SCR_NMEA (1U << 20) -#define SCR_FIEN (1U << 21) -#define SCR_ENSCXT (1U << 25) -#define SCR_ATA (1U << 26) -#define SCR_FGTEN (1U << 27) -#define SCR_ECVEN (1U << 28) -#define SCR_TWEDEN (1U << 29) +#define SCR_NS (1ULL << 0) +#define SCR_IRQ (1ULL << 1) +#define SCR_FIQ (1ULL << 2) +#define SCR_EA (1ULL << 3) +#define SCR_FW (1ULL << 4) +#define SCR_AW (1ULL << 5) +#define SCR_NET (1ULL << 6) +#define SCR_SMD (1ULL << 7) +#define SCR_HCE (1ULL << 8) +#define SCR_SIF (1ULL << 9) +#define SCR_RW (1ULL << 10) +#define SCR_ST (1ULL << 11) +#define SCR_TWI (1ULL << 12) +#define SCR_TWE (1ULL << 13) +#define SCR_TLOR (1ULL << 14) +#define SCR_TERR (1ULL << 15) +#define SCR_APK (1ULL << 16) +#define SCR_API (1ULL << 17) +#define SCR_EEL2 (1ULL << 18) +#define SCR_EASE (1ULL << 19) +#define SCR_NMEA (1ULL << 20) +#define SCR_FIEN (1ULL << 21) +#define SCR_ENSCXT (1ULL << 25) +#define SCR_ATA (1ULL << 26) +#define SCR_FGTEN (1ULL << 27) +#define SCR_ECVEN (1ULL << 28) +#define SCR_TWEDEN (1ULL << 29) #define SCR_TWEDEL MAKE_64BIT_MASK(30, 4) #define SCR_TME (1ULL << 34) #define SCR_AMVOFFEN (1ULL << 35) @@ -1641,9 +1660,7 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_TRNDR (1ULL << 40) #define SCR_ENTP2 (1ULL << 41) #define SCR_GPF (1ULL << 48) - -#define HSTR_TTEE (1 << 16) -#define HSTR_TJDBX (1 << 17) +#define SCR_NSE (1ULL << 62) /* Return the current FPSCR value. */ uint32_t vfp_get_fpscr(CPUARMState *env); @@ -2059,6 +2076,7 @@ FIELD(ID_AA64ISAR0, SHA1, 8, 4) FIELD(ID_AA64ISAR0, SHA2, 12, 4) FIELD(ID_AA64ISAR0, CRC32, 16, 4) FIELD(ID_AA64ISAR0, ATOMIC, 20, 4) +FIELD(ID_AA64ISAR0, TME, 24, 4) FIELD(ID_AA64ISAR0, RDM, 28, 4) FIELD(ID_AA64ISAR0, SHA3, 32, 4) FIELD(ID_AA64ISAR0, SM3, 36, 4) @@ -2093,6 +2111,13 @@ FIELD(ID_AA64ISAR2, APA3, 12, 4) FIELD(ID_AA64ISAR2, MOPS, 16, 4) FIELD(ID_AA64ISAR2, BC, 20, 4) FIELD(ID_AA64ISAR2, PAC_FRAC, 24, 4) +FIELD(ID_AA64ISAR2, CLRBHB, 28, 4) +FIELD(ID_AA64ISAR2, SYSREG_128, 32, 4) +FIELD(ID_AA64ISAR2, SYSINSTR_128, 36, 4) +FIELD(ID_AA64ISAR2, PRFMSLC, 40, 4) +FIELD(ID_AA64ISAR2, RPRFM, 48, 4) +FIELD(ID_AA64ISAR2, CSSC, 52, 4) +FIELD(ID_AA64ISAR2, ATS1A, 60, 4) FIELD(ID_AA64PFR0, EL0, 0, 4) FIELD(ID_AA64PFR0, EL1, 4, 4) @@ -2107,6 +2132,7 @@ FIELD(ID_AA64PFR0, SEL2, 36, 4) FIELD(ID_AA64PFR0, MPAM, 40, 4) FIELD(ID_AA64PFR0, AMU, 44, 4) FIELD(ID_AA64PFR0, DIT, 48, 4) +FIELD(ID_AA64PFR0, RME, 52, 4) FIELD(ID_AA64PFR0, CSV2, 56, 4) FIELD(ID_AA64PFR0, CSV3, 60, 4) @@ -2119,6 +2145,12 @@ FIELD(ID_AA64PFR1, SME, 24, 4) FIELD(ID_AA64PFR1, RNDR_TRAP, 28, 4) FIELD(ID_AA64PFR1, CSV2_FRAC, 32, 4) FIELD(ID_AA64PFR1, NMI, 36, 4) +FIELD(ID_AA64PFR1, MTE_FRAC, 40, 4) +FIELD(ID_AA64PFR1, GCS, 44, 4) +FIELD(ID_AA64PFR1, THE, 48, 4) +FIELD(ID_AA64PFR1, MTEX, 52, 4) +FIELD(ID_AA64PFR1, DF2, 56, 4) +FIELD(ID_AA64PFR1, PFAR, 60, 4) FIELD(ID_AA64MMFR0, PARANGE, 0, 4) FIELD(ID_AA64MMFR0, ASIDBITS, 4, 4) @@ -2150,6 +2182,7 @@ FIELD(ID_AA64MMFR1, AFP, 44, 4) FIELD(ID_AA64MMFR1, NTLBPA, 48, 4) FIELD(ID_AA64MMFR1, TIDCP1, 52, 4) FIELD(ID_AA64MMFR1, CMOW, 56, 4) +FIELD(ID_AA64MMFR1, ECBHB, 60, 4) FIELD(ID_AA64MMFR2, CNP, 0, 4) FIELD(ID_AA64MMFR2, UAO, 4, 4) @@ -2171,7 +2204,9 @@ 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, PMSS, 16, 4) FIELD(ID_AA64DFR0, WRPS, 20, 4) +FIELD(ID_AA64DFR0, SEBEP, 24, 4) FIELD(ID_AA64DFR0, CTX_CMPS, 28, 4) FIELD(ID_AA64DFR0, PMSVER, 32, 4) FIELD(ID_AA64DFR0, DOUBLELOCK, 36, 4) @@ -2179,12 +2214,14 @@ FIELD(ID_AA64DFR0, TRACEFILT, 40, 4) FIELD(ID_AA64DFR0, TRACEBUFFER, 44, 4) FIELD(ID_AA64DFR0, MTPMU, 48, 4) FIELD(ID_AA64DFR0, BRBE, 52, 4) +FIELD(ID_AA64DFR0, EXTTRCBUFF, 56, 4) FIELD(ID_AA64DFR0, HPMN0, 60, 4) FIELD(ID_AA64ZFR0, SVEVER, 0, 4) FIELD(ID_AA64ZFR0, AES, 4, 4) FIELD(ID_AA64ZFR0, BITPERM, 16, 4) FIELD(ID_AA64ZFR0, BFLOAT16, 20, 4) +FIELD(ID_AA64ZFR0, B16B16, 24, 4) FIELD(ID_AA64ZFR0, SHA3, 32, 4) FIELD(ID_AA64ZFR0, SM4, 40, 4) FIELD(ID_AA64ZFR0, I8MM, 44, 4) @@ -2192,9 +2229,13 @@ FIELD(ID_AA64ZFR0, F32MM, 52, 4) FIELD(ID_AA64ZFR0, F64MM, 56, 4) FIELD(ID_AA64SMFR0, F32F32, 32, 1) +FIELD(ID_AA64SMFR0, BI32I32, 33, 1) FIELD(ID_AA64SMFR0, B16F32, 34, 1) FIELD(ID_AA64SMFR0, F16F32, 35, 1) FIELD(ID_AA64SMFR0, I8I32, 36, 4) +FIELD(ID_AA64SMFR0, F16F16, 42, 1) +FIELD(ID_AA64SMFR0, B16B16, 43, 1) +FIELD(ID_AA64SMFR0, I16I32, 44, 4) FIELD(ID_AA64SMFR0, F64F64, 48, 1) FIELD(ID_AA64SMFR0, I16I64, 52, 4) FIELD(ID_AA64SMFR0, SMEVER, 56, 4) @@ -2219,6 +2260,15 @@ FIELD(DBGDIDR, CTX_CMPS, 20, 4) FIELD(DBGDIDR, BRPS, 24, 4) FIELD(DBGDIDR, WRPS, 28, 4) +FIELD(DBGDEVID, PCSAMPLE, 0, 4) +FIELD(DBGDEVID, WPADDRMASK, 4, 4) +FIELD(DBGDEVID, BPADDRMASK, 8, 4) +FIELD(DBGDEVID, VECTORCATCH, 12, 4) +FIELD(DBGDEVID, VIRTEXTNS, 16, 4) +FIELD(DBGDEVID, DOUBLELOCK, 20, 4) +FIELD(DBGDEVID, AUXREGS, 24, 4) +FIELD(DBGDEVID, CIDMASK, 28, 4) + FIELD(MVFR0, SIMDREG, 0, 4) FIELD(MVFR0, FPSP, 4, 4) FIELD(MVFR0, FPDP, 8, 4) @@ -2242,6 +2292,19 @@ FIELD(MVFR1, SIMDFMAC, 28, 4) FIELD(MVFR2, SIMDMISC, 0, 4) FIELD(MVFR2, FPMISC, 4, 4) +FIELD(GPCCR, PPS, 0, 3) +FIELD(GPCCR, IRGN, 8, 2) +FIELD(GPCCR, ORGN, 10, 2) +FIELD(GPCCR, SH, 12, 2) +FIELD(GPCCR, PGS, 14, 2) +FIELD(GPCCR, GPC, 16, 1) +FIELD(GPCCR, GPCP, 17, 1) +FIELD(GPCCR, L0GPTSZ, 20, 4) + +FIELD(MFAR, FPA, 12, 40) +FIELD(MFAR, NSE, 62, 1) +FIELD(MFAR, NS, 63, 1) + QEMU_BUILD_BUG_ON(ARRAY_SIZE(((ARMCPU *)0)->ccsidr) <= R_V7M_CSSELR_INDEX_MASK); /* If adding a feature bit which corresponds to a Linux ELF @@ -2296,28 +2359,59 @@ static inline int arm_feature(CPUARMState *env, int feature) void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp); +/* + * ARM v9 security states. + * The ordering of the enumeration corresponds to the low 2 bits + * of the GPI value, and (except for Root) the concat of NSE:NS. + */ + +typedef enum ARMSecuritySpace { + ARMSS_Secure = 0, + ARMSS_NonSecure = 1, + ARMSS_Root = 2, + ARMSS_Realm = 3, +} ARMSecuritySpace; + +/* Return true if @space is secure, in the pre-v9 sense. */ +static inline bool arm_space_is_secure(ARMSecuritySpace space) +{ + return space == ARMSS_Secure || space == ARMSS_Root; +} + +/* Return the ARMSecuritySpace for @secure, assuming !RME or EL[0-2]. */ +static inline ARMSecuritySpace arm_secure_to_space(bool secure) +{ + return secure ? ARMSS_Secure : ARMSS_NonSecure; +} + #if !defined(CONFIG_USER_ONLY) -/* Return true if exception levels below EL3 are in secure state, - * or would be following an exception return to that level. - * Unlike arm_is_secure() (which is always a question about the - * _current_ state of the CPU) this doesn't care about the current - * EL or mode. +/** + * arm_security_space_below_el3: + * @env: cpu context + * + * Return the security space of exception levels below EL3, following + * an exception return to those levels. Unlike arm_security_space, + * this doesn't care about the current EL. + */ +ARMSecuritySpace arm_security_space_below_el3(CPUARMState *env); + +/** + * arm_is_secure_below_el3: + * @env: cpu context + * + * Return true if exception levels below EL3 are in secure state, + * or would be following an exception return to those levels. */ static inline bool arm_is_secure_below_el3(CPUARMState *env) { - if (arm_feature(env, ARM_FEATURE_EL3)) { - return !(env->cp15.scr_el3 & SCR_NS); - } else { - /* If EL3 is not supported then the secure state is implementation - * defined, in which case QEMU defaults to non-secure. - */ - return false; - } + ARMSecuritySpace ss = arm_security_space_below_el3(env); + return ss == ARMSS_Secure; } /* Return true if the CPU is AArch64 EL3 or AArch32 Mon */ static inline bool arm_is_el3_or_mon(CPUARMState *env) { + assert(!arm_feature(env, ARM_FEATURE_M)); if (arm_feature(env, ARM_FEATURE_EL3)) { if (is_a64(env) && extract32(env->pstate, 2, 2) == 3) { /* CPU currently in AArch64 state and EL3 */ @@ -2331,41 +2425,69 @@ static inline bool arm_is_el3_or_mon(CPUARMState *env) return false; } -/* Return true if the processor is in secure state */ +/** + * arm_security_space: + * @env: cpu context + * + * Return the current security space of the cpu. + */ +ARMSecuritySpace arm_security_space(CPUARMState *env); + +/** + * arm_is_secure: + * @env: cpu context + * + * Return true if the processor is in secure state. + */ static inline bool arm_is_secure(CPUARMState *env) { - if (arm_is_el3_or_mon(env)) { - return true; - } - return arm_is_secure_below_el3(env); + return arm_space_is_secure(arm_security_space(env)); } /* * Return true if the current security state has AArch64 EL2 or AArch32 Hyp. - * This corresponds to the pseudocode EL2Enabled() + * This corresponds to the pseudocode EL2Enabled(). */ +static inline bool arm_is_el2_enabled_secstate(CPUARMState *env, + ARMSecuritySpace space) +{ + assert(space != ARMSS_Root); + return arm_feature(env, ARM_FEATURE_EL2) + && (space != ARMSS_Secure || (env->cp15.scr_el3 & SCR_EEL2)); +} + static inline bool arm_is_el2_enabled(CPUARMState *env) { - if (arm_feature(env, ARM_FEATURE_EL2)) { - if (arm_is_secure_below_el3(env)) { - return (env->cp15.scr_el3 & SCR_EEL2) != 0; - } - return true; - } - return false; + return arm_is_el2_enabled_secstate(env, arm_security_space_below_el3(env)); } #else +static inline ARMSecuritySpace arm_security_space_below_el3(CPUARMState *env) +{ + return ARMSS_NonSecure; +} + static inline bool arm_is_secure_below_el3(CPUARMState *env) { return false; } +static inline ARMSecuritySpace arm_security_space(CPUARMState *env) +{ + return ARMSS_NonSecure; +} + static inline bool arm_is_secure(CPUARMState *env) { return false; } +static inline bool arm_is_el2_enabled_secstate(CPUARMState *env, + ARMSecuritySpace space) +{ + return false; +} + static inline bool arm_is_el2_enabled(CPUARMState *env) { return false; @@ -2378,6 +2500,7 @@ static inline bool arm_is_el2_enabled(CPUARMState *env) * "for all purposes other than a direct read or write access of HCR_EL2." * Not included here is HCR_RW. */ +uint64_t arm_hcr_el2_eff_secstate(CPUARMState *env, ARMSecuritySpace space); uint64_t arm_hcr_el2_eff(CPUARMState *env); uint64_t arm_hcrx_el2_eff(CPUARMState *env); @@ -2414,7 +2537,7 @@ static inline bool arm_el_is_aa64(CPUARMState *env, int el) return aa64; } -/* Function for determing whether guest cp register reads and writes should +/* Function for determining whether guest cp register reads and writes should * access the secure or non-secure bank of a cp register. When EL3 is * operating in AArch32 state, the NS-bit determines whether the secure * instance of a cp register should be used. When EL3 is AArch64 (or if @@ -2457,224 +2580,9 @@ static inline bool access_secure_reg(CPUARMState *env) (arm_is_secure(_env) && !arm_el_is_aa64((_env), 3)), \ (_val)) -void arm_cpu_list(void); uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, uint32_t cur_el, bool secure); -/* Interface between CPU and Interrupt controller. */ -#ifndef CONFIG_USER_ONLY -bool armv7m_nvic_can_take_pending_exception(void *opaque); -#else -static inline bool armv7m_nvic_can_take_pending_exception(void *opaque) -{ - return true; -} -#endif -/** - * armv7m_nvic_set_pending: mark the specified exception as pending - * @opaque: the NVIC - * @irq: the exception number to mark pending - * @secure: false for non-banked exceptions or for the nonsecure - * version of a banked exception, true for the secure version of a banked - * exception. - * - * Marks the specified exception as pending. Note that we will assert() - * if @secure is true and @irq does not specify one of the fixed set - * of architecturally banked exceptions. - */ -void armv7m_nvic_set_pending(void *opaque, int irq, bool secure); -/** - * armv7m_nvic_set_pending_derived: mark this derived exception as pending - * @opaque: the NVIC - * @irq: the exception number to mark pending - * @secure: false for non-banked exceptions or for the nonsecure - * version of a banked exception, true for the secure version of a banked - * exception. - * - * Similar to armv7m_nvic_set_pending(), but specifically for derived - * exceptions (exceptions generated in the course of trying to take - * a different exception). - */ -void armv7m_nvic_set_pending_derived(void *opaque, int irq, bool secure); -/** - * armv7m_nvic_set_pending_lazyfp: mark this lazy FP exception as pending - * @opaque: the NVIC - * @irq: the exception number to mark pending - * @secure: false for non-banked exceptions or for the nonsecure - * version of a banked exception, true for the secure version of a banked - * exception. - * - * Similar to armv7m_nvic_set_pending(), but specifically for exceptions - * generated in the course of lazy stacking of FP registers. - */ -void armv7m_nvic_set_pending_lazyfp(void *opaque, int irq, bool secure); -/** - * armv7m_nvic_get_pending_irq_info: return highest priority pending - * exception, and whether it targets Secure state - * @opaque: the NVIC - * @pirq: set to pending exception number - * @ptargets_secure: set to whether pending exception targets Secure - * - * This function writes the number of the highest priority pending - * exception (the one which would be made active by - * armv7m_nvic_acknowledge_irq()) to @pirq, and sets @ptargets_secure - * to true if the current highest priority pending exception should - * be taken to Secure state, false for NS. - */ -void armv7m_nvic_get_pending_irq_info(void *opaque, int *pirq, - bool *ptargets_secure); -/** - * armv7m_nvic_acknowledge_irq: make highest priority pending exception active - * @opaque: the NVIC - * - * Move the current highest priority pending exception from the pending - * state to the active state, and update v7m.exception to indicate that - * it is the exception currently being handled. - */ -void armv7m_nvic_acknowledge_irq(void *opaque); -/** - * armv7m_nvic_complete_irq: complete specified interrupt or exception - * @opaque: the NVIC - * @irq: the exception number to complete - * @secure: true if this exception was secure - * - * Returns: -1 if the irq was not active - * 1 if completing this irq brought us back to base (no active irqs) - * 0 if there is still an irq active after this one was completed - * (Ignoring -1, this is the same as the RETTOBASE value before completion.) - */ -int armv7m_nvic_complete_irq(void *opaque, int irq, bool secure); -/** - * armv7m_nvic_get_ready_status(void *opaque, int irq, bool secure) - * @opaque: the NVIC - * @irq: the exception number to mark pending - * @secure: false for non-banked exceptions or for the nonsecure - * version of a banked exception, true for the secure version of a banked - * exception. - * - * Return whether an exception is "ready", i.e. whether the exception is - * enabled and is configured at a priority which would allow it to - * interrupt the current execution priority. This controls whether the - * RDY bit for it in the FPCCR is set. - */ -bool armv7m_nvic_get_ready_status(void *opaque, int irq, bool secure); -/** - * armv7m_nvic_raw_execution_priority: return the raw execution priority - * @opaque: the NVIC - * - * Returns: the raw execution priority as defined by the v8M architecture. - * This is the execution priority minus the effects of AIRCR.PRIS, - * and minus any PRIMASK/FAULTMASK/BASEPRI priority boosting. - * (v8M ARM ARM I_PKLD.) - */ -int armv7m_nvic_raw_execution_priority(void *opaque); -/** - * armv7m_nvic_neg_prio_requested: return true if the requested execution - * priority is negative for the specified security state. - * @opaque: the NVIC - * @secure: the security state to test - * This corresponds to the pseudocode IsReqExecPriNeg(). - */ -#ifndef CONFIG_USER_ONLY -bool armv7m_nvic_neg_prio_requested(void *opaque, bool secure); -#else -static inline bool armv7m_nvic_neg_prio_requested(void *opaque, bool secure) -{ - return false; -} -#endif - -/* Interface for defining coprocessor registers. - * Registers are defined in tables of arm_cp_reginfo structs - * which are passed to define_arm_cp_regs(). - */ - -/* When looking up a coprocessor register we look for it - * via an integer which encodes all of: - * coprocessor number - * Crn, Crm, opc1, opc2 fields - * 32 or 64 bit register (ie is it accessed via MRC/MCR - * or via MRRC/MCRR?) - * non-secure/secure bank (AArch32 only) - * We allow 4 bits for opc1 because MRRC/MCRR have a 4 bit field. - * (In this case crn and opc2 should be zero.) - * For AArch64, there is no 32/64 bit size distinction; - * instead all registers have a 2 bit op0, 3 bit op1 and op2, - * and 4 bit CRn and CRm. The encoding patterns are chosen - * to be easy to convert to and from the KVM encodings, and also - * so that the hashtable can contain both AArch32 and AArch64 - * registers (to allow for interprocessing where we might run - * 32 bit code on a 64 bit core). - */ -/* This bit is private to our hashtable cpreg; in KVM register - * IDs the AArch64/32 distinction is the KVM_REG_ARM/ARM64 - * in the upper bits of the 64 bit ID. - */ -#define CP_REG_AA64_SHIFT 28 -#define CP_REG_AA64_MASK (1 << CP_REG_AA64_SHIFT) - -/* To enable banking of coprocessor registers depending on ns-bit we - * add a bit to distinguish between secure and non-secure cpregs in the - * hashtable. - */ -#define CP_REG_NS_SHIFT 29 -#define CP_REG_NS_MASK (1 << CP_REG_NS_SHIFT) - -#define ENCODE_CP_REG(cp, is64, ns, crn, crm, opc1, opc2) \ - ((ns) << CP_REG_NS_SHIFT | ((cp) << 16) | ((is64) << 15) | \ - ((crn) << 11) | ((crm) << 7) | ((opc1) << 3) | (opc2)) - -#define ENCODE_AA64_CP_REG(cp, crn, crm, op0, op1, op2) \ - (CP_REG_AA64_MASK | \ - ((cp) << CP_REG_ARM_COPROC_SHIFT) | \ - ((op0) << CP_REG_ARM64_SYSREG_OP0_SHIFT) | \ - ((op1) << CP_REG_ARM64_SYSREG_OP1_SHIFT) | \ - ((crn) << CP_REG_ARM64_SYSREG_CRN_SHIFT) | \ - ((crm) << CP_REG_ARM64_SYSREG_CRM_SHIFT) | \ - ((op2) << CP_REG_ARM64_SYSREG_OP2_SHIFT)) - -/* Convert a full 64 bit KVM register ID to the truncated 32 bit - * version used as a key for the coprocessor register hashtable - */ -static inline uint32_t kvm_to_cpreg_id(uint64_t kvmid) -{ - uint32_t cpregid = kvmid; - if ((kvmid & CP_REG_ARCH_MASK) == CP_REG_ARM64) { - cpregid |= CP_REG_AA64_MASK; - } else { - if ((kvmid & CP_REG_SIZE_MASK) == CP_REG_SIZE_U64) { - cpregid |= (1 << 15); - } - - /* KVM is always non-secure so add the NS flag on AArch32 register - * entries. - */ - cpregid |= 1 << CP_REG_NS_SHIFT; - } - return cpregid; -} - -/* Convert a truncated 32 bit hashtable key into the full - * 64 bit KVM register ID. - */ -static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) -{ - uint64_t kvmid; - - if (cpregid & CP_REG_AA64_MASK) { - kvmid = cpregid & ~CP_REG_AA64_MASK; - kvmid |= CP_REG_SIZE_U64 | CP_REG_ARM64; - } else { - kvmid = cpregid & ~(1 << 15); - if (cpregid & (1 << 15)) { - kvmid |= CP_REG_SIZE_U64 | CP_REG_ARM; - } else { - kvmid |= CP_REG_SIZE_U32 | CP_REG_ARM; - } - } - return kvmid; -} - /* Return the highest implemented Exception Level */ static inline int arm_highest_el(CPUARMState *env) { @@ -2768,14 +2676,10 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync); #define ARM_CPUID_TI915T 0x54029152 #define ARM_CPUID_TI925T 0x54029252 -#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 #define TYPE_ARM_HOST_CPU "host-" TYPE_ARM_CPU -#define cpu_list arm_cpu_list - /* ARM has the following "translation regimes" (as the ARM ARM calls them): * * If EL3 is 64-bit: @@ -2816,26 +2720,29 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync); * table over and over. * 6. we need separate EL1/EL2 mmu_idx for handling the Privileged Access * Never (PAN) bit within PSTATE. + * 7. we fold together the secure and non-secure regimes for A-profile, + * because there are no banked system registers for aarch64, so the + * process of switching between secure and non-secure is + * already heavyweight. * * 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 - * 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) + * EL0 EL1&0 stage 1+2 (aka NS PL0) + * EL1 EL1&0 stage 1+2 (aka NS PL1) + * EL1 EL1&0 stage 1+2 +PAN + * EL0 EL2&0 + * EL2 EL2&0 + * EL2 EL2&0 +PAN + * EL2 (aka NS PL2) + * EL3 (aka S PL1) + * Physical (NS & S) + * Stage2 (NS & S) * - * for a total of 11 different mmu_idx. + * 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 - * NS EL2 if we ever model a Cortex-R52). + * as A profile. They only need to distinguish EL0 and EL1 (and + * EL2 if we ever model a Cortex-R52). * * M profile CPUs are rather different as they do not have a true MMU. * They have the following different MMU indexes: @@ -2874,9 +2781,6 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync); #define ARM_MMU_IDX_NOTLB 0x20 /* does not have a TLB */ #define ARM_MMU_IDX_M 0x40 /* M profile */ -/* Meanings of the bits for A profile mmu idx values */ -#define ARM_MMU_IDX_A_NS 0x8 - /* Meanings of the bits for M profile mmu idx values */ #define ARM_MMU_IDX_M_PRIV 0x1 #define ARM_MMU_IDX_M_NEGPRI 0x2 @@ -2890,22 +2794,29 @@ typedef enum ARMMMUIdx { /* * A-profile. */ - ARMMMUIdx_SE10_0 = 0 | ARM_MMU_IDX_A, - ARMMMUIdx_SE20_0 = 1 | ARM_MMU_IDX_A, - ARMMMUIdx_SE10_1 = 2 | ARM_MMU_IDX_A, - ARMMMUIdx_SE20_2 = 3 | ARM_MMU_IDX_A, - ARMMMUIdx_SE10_1_PAN = 4 | ARM_MMU_IDX_A, - ARMMMUIdx_SE20_2_PAN = 5 | ARM_MMU_IDX_A, - ARMMMUIdx_SE2 = 6 | ARM_MMU_IDX_A, - ARMMMUIdx_SE3 = 7 | ARM_MMU_IDX_A, + 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_E20_2 = 3 | ARM_MMU_IDX_A, + ARMMMUIdx_E10_1_PAN = 4 | ARM_MMU_IDX_A, + ARMMMUIdx_E20_2_PAN = 5 | ARM_MMU_IDX_A, + ARMMMUIdx_E2 = 6 | ARM_MMU_IDX_A, + ARMMMUIdx_E3 = 7 | ARM_MMU_IDX_A, - ARMMMUIdx_E10_0 = ARMMMUIdx_SE10_0 | ARM_MMU_IDX_A_NS, - ARMMMUIdx_E20_0 = ARMMMUIdx_SE20_0 | ARM_MMU_IDX_A_NS, - ARMMMUIdx_E10_1 = ARMMMUIdx_SE10_1 | ARM_MMU_IDX_A_NS, - ARMMMUIdx_E20_2 = ARMMMUIdx_SE20_2 | ARM_MMU_IDX_A_NS, - ARMMMUIdx_E10_1_PAN = ARMMMUIdx_SE10_1_PAN | ARM_MMU_IDX_A_NS, - ARMMMUIdx_E20_2_PAN = ARMMMUIdx_SE20_2_PAN | ARM_MMU_IDX_A_NS, - ARMMMUIdx_E2 = ARMMMUIdx_SE2 | ARM_MMU_IDX_A_NS, + /* + * Used for second stage of an S12 page table walk, or for descriptor + * loads during first stage of an S1 page table walk. Note that both + * are in use simultaneously for SecureEL2: the security state for + * the S2 ptw is selected by the NS bit from the S1 ptw. + */ + ARMMMUIdx_Stage2_S = 8 | ARM_MMU_IDX_A, + ARMMMUIdx_Stage2 = 9 | ARM_MMU_IDX_A, + + /* TLBs with 1-1 mapping to the physical address spaces. */ + ARMMMUIdx_Phys_S = 10 | ARM_MMU_IDX_A, + ARMMMUIdx_Phys_NS = 11 | ARM_MMU_IDX_A, + ARMMMUIdx_Phys_Root = 12 | ARM_MMU_IDX_A, + ARMMMUIdx_Phys_Realm = 13 | ARM_MMU_IDX_A, /* * These are not allocated TLBs and are used only for AT system @@ -2914,18 +2825,6 @@ typedef enum ARMMMUIdx { 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, - ARMMMUIdx_Stage1_SE0 = 3 | ARM_MMU_IDX_NOTLB, - ARMMMUIdx_Stage1_SE1 = 4 | ARM_MMU_IDX_NOTLB, - ARMMMUIdx_Stage1_SE1_PAN = 5 | ARM_MMU_IDX_NOTLB, - /* - * Not allocated a TLB: used only for second stage of an S12 page - * table walk, or for descriptor loads during first stage of an S1 - * page table walk. Note that if we ever want to have a TLB for this - * then various TLB flush insns which currently are no-ops or flush - * only stage 1 MMU indexes will need to change to flush stage 2. - */ - ARMMMUIdx_Stage2 = 6 | ARM_MMU_IDX_NOTLB, - ARMMMUIdx_Stage2_S = 7 | ARM_MMU_IDX_NOTLB, /* * M-profile. @@ -2955,14 +2854,9 @@ typedef enum ARMMMUIdxBit { TO_CORE_BIT(E2), TO_CORE_BIT(E20_2), TO_CORE_BIT(E20_2_PAN), - TO_CORE_BIT(SE10_0), - TO_CORE_BIT(SE20_0), - TO_CORE_BIT(SE10_1), - TO_CORE_BIT(SE20_2), - TO_CORE_BIT(SE10_1_PAN), - TO_CORE_BIT(SE20_2_PAN), - TO_CORE_BIT(SE2), - TO_CORE_BIT(SE3), + TO_CORE_BIT(E3), + TO_CORE_BIT(Stage2), + TO_CORE_BIT(Stage2_S), TO_CORE_BIT(MUser), TO_CORE_BIT(MPriv), @@ -2986,25 +2880,21 @@ typedef enum ARMASIdx { ARMASIdx_TagS = 3, } ARMASIdx; -/* Return the Exception Level targeted by debug exceptions. */ -static inline int arm_debug_target_el(CPUARMState *env) +static inline ARMMMUIdx arm_space_to_phys(ARMSecuritySpace space) { - bool secure = arm_is_secure(env); - bool route_to_el2 = false; + /* Assert the relative order of the physical mmu indexes. */ + QEMU_BUILD_BUG_ON(ARMSS_Secure != 0); + QEMU_BUILD_BUG_ON(ARMMMUIdx_Phys_NS != ARMMMUIdx_Phys_S + ARMSS_NonSecure); + QEMU_BUILD_BUG_ON(ARMMMUIdx_Phys_Root != ARMMMUIdx_Phys_S + ARMSS_Root); + QEMU_BUILD_BUG_ON(ARMMMUIdx_Phys_Realm != ARMMMUIdx_Phys_S + ARMSS_Realm); - if (arm_is_el2_enabled(env)) { - route_to_el2 = env->cp15.hcr_el2 & HCR_TGE || - env->cp15.mdcr_el2 & MDCR_TDE; - } + return ARMMMUIdx_Phys_S + space; +} - if (route_to_el2) { - return 2; - } else if (arm_feature(env, ARM_FEATURE_EL3) && - !arm_el_is_aa64(env, 3) && secure) { - return 3; - } else { - return 1; - } +static inline ARMSecuritySpace arm_phys_to_space(ARMMMUIdx idx) +{ + assert(idx >= ARMMMUIdx_Phys_S && idx <= ARMMMUIdx_Phys_Realm); + return idx - ARMMMUIdx_Phys_S; } static inline bool arm_v7m_csselr_razwi(ARMCPU *cpu) @@ -3015,107 +2905,6 @@ static inline bool arm_v7m_csselr_razwi(ARMCPU *cpu) return (cpu->clidr & R_V7M_CLIDR_CTYPE_ALL_MASK) != 0; } -/* See AArch64.GenerateDebugExceptionsFrom() in ARM ARM pseudocode */ -static inline bool aa64_generate_debug_exceptions(CPUARMState *env) -{ - int cur_el = arm_current_el(env); - int debug_el; - - if (cur_el == 3) { - return false; - } - - /* MDCR_EL3.SDD disables debug events from Secure state */ - if (arm_is_secure_below_el3(env) - && extract32(env->cp15.mdcr_el3, 16, 1)) { - return false; - } - - /* - * Same EL to same EL debug exceptions need MDSCR_KDE enabled - * while not masking the (D)ebug bit in DAIF. - */ - debug_el = arm_debug_target_el(env); - - if (cur_el == debug_el) { - return extract32(env->cp15.mdscr_el1, 13, 1) - && !(env->daif & PSTATE_D); - } - - /* Otherwise the debug target needs to be a higher EL */ - return debug_el > cur_el; -} - -static inline bool aa32_generate_debug_exceptions(CPUARMState *env) -{ - int el = arm_current_el(env); - - if (el == 0 && arm_el_is_aa64(env, 1)) { - return aa64_generate_debug_exceptions(env); - } - - if (arm_is_secure(env)) { - int spd; - - if (el == 0 && (env->cp15.sder & 1)) { - /* SDER.SUIDEN means debug exceptions from Secure EL0 - * are always enabled. Otherwise they are controlled by - * SDCR.SPD like those from other Secure ELs. - */ - return true; - } - - spd = extract32(env->cp15.mdcr_el3, 14, 2); - switch (spd) { - case 1: - /* SPD == 0b01 is reserved, but behaves as 0b00. */ - case 0: - /* For 0b00 we return true if external secure invasive debug - * is enabled. On real hardware this is controlled by external - * signals to the core. QEMU always permits debug, and behaves - * as if DBGEN, SPIDEN, NIDEN and SPNIDEN are all tied high. - */ - return true; - case 2: - return false; - case 3: - return true; - } - } - - return el != 2; -} - -/* Return true if debugging exceptions are currently enabled. - * This corresponds to what in ARM ARM pseudocode would be - * if UsingAArch32() then - * return AArch32.GenerateDebugExceptions() - * else - * return AArch64.GenerateDebugExceptions() - * We choose to push the if() down into this function for clarity, - * since the pseudocode has it at all callsites except for the one in - * CheckSoftwareStep(), where it is elided because both branches would - * always return the same value. - */ -static inline bool arm_generate_debug_exceptions(CPUARMState *env) -{ - if (env->aarch64) { - return aa64_generate_debug_exceptions(env); - } else { - return aa32_generate_debug_exceptions(env); - } -} - -/* Is single-stepping active? (Note that the "is EL_D AArch64?" check - * implicitly means this always returns false in pre-v8 CPUs.) - */ -static inline bool arm_singlestep_active(CPUARMState *env) -{ - return extract32(env->cp15.mdscr_el1, 0, 1) - && arm_el_is_aa64(env, arm_debug_target_el(env)) - && arm_generate_debug_exceptions(env); -} - static inline bool arm_sctlr_b(CPUARMState *env) { return @@ -3205,11 +2994,11 @@ FIELD(TBFLAG_ANY, BE_DATA, 3, 1) FIELD(TBFLAG_ANY, MMUIDX, 4, 4) /* Target EL if we take a floating-point-disabled exception */ FIELD(TBFLAG_ANY, FPEXC_EL, 8, 2) -/* For A-profile only, target EL for debug exceptions. */ -FIELD(TBFLAG_ANY, DEBUG_TARGET_EL, 10, 2) /* Memory operations require alignment: SCTLR_ELx.A or CCR.UNALIGN_TRP */ -FIELD(TBFLAG_ANY, ALIGN_MEM, 12, 1) -FIELD(TBFLAG_ANY, PSTATE__IL, 13, 1) +FIELD(TBFLAG_ANY, ALIGN_MEM, 10, 1) +FIELD(TBFLAG_ANY, PSTATE__IL, 11, 1) +FIELD(TBFLAG_ANY, FGT_ACTIVE, 12, 1) +FIELD(TBFLAG_ANY, FGT_SVC, 13, 1) /* * Bit usage when in AArch32 state, both A- and M-profile. @@ -3238,6 +3027,11 @@ FIELD(TBFLAG_A32, HSTR_ACTIVE, 9, 1) * the same thing as the current security state of the processor! */ FIELD(TBFLAG_A32, NS, 10, 1) +/* + * Indicates that SME Streaming mode is active, and SMCR_ELx.FA64 is not. + * This requires an SME trap from AArch32 mode when using NEON. + */ +FIELD(TBFLAG_A32, SME_TRAP_NONSTREAMING, 11, 1) /* * Bit usage when in AArch32 state, for M-profile only. @@ -3254,6 +3048,8 @@ FIELD(TBFLAG_M32, NEW_FP_CTXT_NEEDED, 3, 1) /* Not cached. */ FIELD(TBFLAG_M32, FPCCR_S_WRONG, 4, 1) /* Not cached. */ /* Set if MVE insns are definitely not predicated by VPR or LTPSIZE */ FIELD(TBFLAG_M32, MVE_NO_PRED, 5, 1) /* Not cached. */ +/* Set if in secure mode */ +FIELD(TBFLAG_M32, SECURE, 6, 1) /* * Bit usage when in AArch64 state @@ -3271,14 +3067,32 @@ FIELD(TBFLAG_A64, ATA, 15, 1) FIELD(TBFLAG_A64, TCMA, 16, 2) FIELD(TBFLAG_A64, MTE_ACTIVE, 18, 1) FIELD(TBFLAG_A64, MTE0_ACTIVE, 19, 1) +FIELD(TBFLAG_A64, SMEEXC_EL, 20, 2) +FIELD(TBFLAG_A64, PSTATE_SM, 22, 1) +FIELD(TBFLAG_A64, PSTATE_ZA, 23, 1) +FIELD(TBFLAG_A64, SVL, 24, 4) +/* Indicates that SME Streaming mode is active, and SMCR_ELx.FA64 is not. */ +FIELD(TBFLAG_A64, SME_TRAP_NONSTREAMING, 28, 1) +FIELD(TBFLAG_A64, TRAP_ERET, 29, 1) +FIELD(TBFLAG_A64, NAA, 30, 1) +FIELD(TBFLAG_A64, ATA0, 31, 1) +FIELD(TBFLAG_A64, NV, 32, 1) +FIELD(TBFLAG_A64, NV1, 33, 1) +FIELD(TBFLAG_A64, NV2, 34, 1) +/* Set if FEAT_NV2 RAM accesses use the EL2&0 translation regime */ +FIELD(TBFLAG_A64, NV2_MEM_E20, 35, 1) +/* Set if FEAT_NV2 RAM accesses are big-endian */ +FIELD(TBFLAG_A64, NV2_MEM_BE, 36, 1) /* - * Helpers for using the above. + * Helpers for using the above. Note that only the A64 accessors use + * FIELD_DP64() and FIELD_EX64(), because in the other cases the flags + * word either is or might be 32 bits only. */ #define DP_TBFLAG_ANY(DST, WHICH, VAL) \ (DST.flags = FIELD_DP32(DST.flags, TBFLAG_ANY, WHICH, VAL)) #define DP_TBFLAG_A64(DST, WHICH, VAL) \ - (DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_A64, WHICH, VAL)) + (DST.flags2 = FIELD_DP64(DST.flags2, TBFLAG_A64, WHICH, VAL)) #define DP_TBFLAG_A32(DST, WHICH, VAL) \ (DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_A32, WHICH, VAL)) #define DP_TBFLAG_M32(DST, WHICH, VAL) \ @@ -3287,24 +3101,11 @@ FIELD(TBFLAG_A64, MTE0_ACTIVE, 19, 1) (DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_AM32, WHICH, VAL)) #define EX_TBFLAG_ANY(IN, WHICH) FIELD_EX32(IN.flags, TBFLAG_ANY, WHICH) -#define EX_TBFLAG_A64(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_A64, WHICH) +#define EX_TBFLAG_A64(IN, WHICH) FIELD_EX64(IN.flags2, TBFLAG_A64, WHICH) #define EX_TBFLAG_A32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_A32, WHICH) #define EX_TBFLAG_M32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_M32, WHICH) #define EX_TBFLAG_AM32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_AM32, WHICH) -/** - * 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 EX_TBFLAG_ANY(env->hflags, MMUIDX); -} - /** * sve_vq * @env: the cpu context @@ -3316,6 +3117,17 @@ static inline int sve_vq(CPUARMState *env) return EX_TBFLAG_A64(env->hflags, VL) + 1; } +/** + * sme_vq + * @env: the cpu context + * + * Return the SVL cached within env->hflags, in units of quadwords. + */ +static inline int sme_vq(CPUARMState *env) +{ + return EX_TBFLAG_A64(env->hflags, SVL) + 1; +} + static inline bool bswap_code(bool sctlr_b) { #ifdef CONFIG_USER_ONLY @@ -3323,11 +3135,7 @@ static inline bool bswap_code(bool sctlr_b) * The invalid combination SCTLR.B=1/CPSR.E=1/TARGET_BIG_ENDIAN=0 * would also end up as a mixed-endian mode with BE code, LE data. */ - return -#if TARGET_BIG_ENDIAN - 1 ^ -#endif - sctlr_b; + return TARGET_BIG_ENDIAN ^ sctlr_b; #else /* All code access in ARM is little endian, and there are no loaders * doing swaps that need to be reversed @@ -3339,16 +3147,12 @@ static inline bool bswap_code(bool sctlr_b) #ifdef CONFIG_USER_ONLY static inline bool arm_cpu_bswap_data(CPUARMState *env) { - return -#if TARGET_BIG_ENDIAN - 1 ^ -#endif - arm_cpu_data_is_big_endian(env); + return TARGET_BIG_ENDIAN ^ arm_cpu_data_is_big_endian(env); } #endif -void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags); +void cpu_get_tb_cpu_state(CPUARMState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags); enum { QEMU_PSCI_CONDUIT_DISABLED = 0, @@ -3432,27 +3236,24 @@ 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]; - -/* Helper for the macros below, validating the argument type. */ -static inline MemTxAttrs *typecheck_memtxattrs(MemTxAttrs *x) -{ - return x; -} - -/* - * Lvalue macros for ARM TLB bits that we must cache in the TCG TLB. - * Using these should be a bit more self-documenting than using the - * generic target bits directly. - */ -#define arm_tlb_bti_gp(x) (typecheck_memtxattrs(x)->target_tlb_bit0) -#define arm_tlb_mte_tagged(x) (typecheck_memtxattrs(x)->target_tlb_bit1) +extern const uint64_t pred_esz_masks[5]; /* * AArch64 usage of the PAGE_TARGET_* bits for linux-user. + * Note that with the Linux kernel, PROT_MTE may not be cleared by mprotect + * mprotect but PROT_BTI may be cleared. C.f. the kernel's VM_ARCH_CLEAR. */ -#define PAGE_BTI PAGE_TARGET_1 -#define PAGE_MTE PAGE_TARGET_2 +#define PAGE_BTI PAGE_TARGET_1 +#define PAGE_MTE PAGE_TARGET_2 +#define PAGE_TARGET_STICKY PAGE_MTE + +/* We associate one allocation tag per 16 bytes, the minimum. */ +#define LOG2_TAG_GRANULE 4 +#define TAG_GRANULE (1 << LOG2_TAG_GRANULE) + +#ifdef CONFIG_USER_ONLY +#define TARGET_PAGE_DATA_SIZE (TARGET_PAGE_SIZE >> (LOG2_TAG_GRANULE + 1)) +#endif #ifdef TARGET_TAGGED_ADDRESSES /** @@ -3480,793 +3281,4 @@ static inline target_ulong cpu_untagged_addr(CPUState *cs, target_ulong x) } #endif -/* - * 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_aa32_thumb_div(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) != 0; -} - -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_aa32_lob(const ARMISARegisters *id) -{ - /* (M-profile) low-overhead loops and branch future */ - return FIELD_EX32(id->id_isar0, ID_ISAR0, CMPBRANCH) >= 3; -} - -static inline bool isar_feature_aa32_jazelle(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar1, ID_ISAR1, JAZELLE) != 0; -} - -static inline bool isar_feature_aa32_aes(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar5, ID_ISAR5, AES) != 0; -} - -static inline bool isar_feature_aa32_pmull(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar5, ID_ISAR5, AES) > 1; -} - -static inline bool isar_feature_aa32_sha1(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar5, ID_ISAR5, SHA1) != 0; -} - -static inline bool isar_feature_aa32_sha2(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar5, ID_ISAR5, SHA2) != 0; -} - -static inline bool isar_feature_aa32_crc32(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar5, ID_ISAR5, CRC32) != 0; -} - -static inline bool isar_feature_aa32_rdm(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar5, ID_ISAR5, RDM) != 0; -} - -static inline bool isar_feature_aa32_vcma(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar5, ID_ISAR5, VCMA) != 0; -} - -static inline bool isar_feature_aa32_jscvt(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar6, ID_ISAR6, JSCVT) != 0; -} - -static inline bool isar_feature_aa32_dp(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar6, ID_ISAR6, DP) != 0; -} - -static inline bool isar_feature_aa32_fhm(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar6, ID_ISAR6, FHM) != 0; -} - -static inline bool isar_feature_aa32_sb(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar6, ID_ISAR6, SB) != 0; -} - -static inline bool isar_feature_aa32_predinv(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar6, ID_ISAR6, SPECRES) != 0; -} - -static inline bool isar_feature_aa32_bf16(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar6, ID_ISAR6, BF16) != 0; -} - -static inline bool isar_feature_aa32_i8mm(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_isar6, ID_ISAR6, I8MM) != 0; -} - -static inline bool isar_feature_aa32_ras(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_pfr0, ID_PFR0, RAS) != 0; -} - -static inline bool isar_feature_aa32_mprofile(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_pfr1, ID_PFR1, MPROGMOD) != 0; -} - -static inline bool isar_feature_aa32_m_sec_state(const ARMISARegisters *id) -{ - /* - * Return true if M-profile state handling insns - * (VSCCLRM, CLRM, FPCTX access insns) are implemented - */ - return FIELD_EX32(id->id_pfr1, ID_PFR1, SECURITY) >= 3; -} - -static inline bool isar_feature_aa32_fp16_arith(const ARMISARegisters *id) -{ - /* Sadly this is encoded differently for A-profile and M-profile */ - if (isar_feature_aa32_mprofile(id)) { - return FIELD_EX32(id->mvfr1, MVFR1, FP16) > 0; - } else { - return FIELD_EX32(id->mvfr1, MVFR1, FPHP) >= 3; - } -} - -static inline bool isar_feature_aa32_mve(const ARMISARegisters *id) -{ - /* - * Return true if MVE is supported (either integer or floating point). - * We must check for M-profile as the MVFR1 field means something - * else for A-profile. - */ - return isar_feature_aa32_mprofile(id) && - FIELD_EX32(id->mvfr1, MVFR1, MVE) > 0; -} - -static inline bool isar_feature_aa32_mve_fp(const ARMISARegisters *id) -{ - /* - * Return true if MVE is supported (either integer or floating point). - * We must check for M-profile as the MVFR1 field means something - * else for A-profile. - */ - return isar_feature_aa32_mprofile(id) && - FIELD_EX32(id->mvfr1, MVFR1, MVE) >= 2; -} - -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_EX32(id->mvfr0, MVFR0, SIMDREG) >= 2; -} - -static inline bool isar_feature_aa32_fpshvec(const ARMISARegisters *id) -{ - return FIELD_EX32(id->mvfr0, MVFR0, FPSHVEC) > 0; -} - -static inline bool isar_feature_aa32_fpsp_v2(const ARMISARegisters *id) -{ - /* 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); -} - -/* - * We always set the FP and SIMD FP16 fields to indicate identical - * levels of support (assuming SIMD is implemented at all), so - * we only need one set of accessors. - */ -static inline bool isar_feature_aa32_fp16_spconv(const ARMISARegisters *id) -{ - return FIELD_EX32(id->mvfr1, MVFR1, FPHP) > 0; -} - -static inline bool isar_feature_aa32_fp16_dpconv(const ARMISARegisters *id) -{ - 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_EX32(id->mvfr2, MVFR2, FPMISC) >= 1; -} - -static inline bool isar_feature_aa32_vcvt_dr(const ARMISARegisters *id) -{ - return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 2; -} - -static inline bool isar_feature_aa32_vrint(const ARMISARegisters *id) -{ - return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 3; -} - -static inline bool isar_feature_aa32_vminmaxnm(const ARMISARegisters *id) -{ - return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 4; -} - -static inline bool isar_feature_aa32_pxn(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_mmfr0, ID_MMFR0, VMSA) >= 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; -} - -static inline bool isar_feature_aa32_tts2uxn(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, XNX) != 0; -} - -static inline bool isar_feature_aa32_dit(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_pfr0, ID_PFR0, DIT) != 0; -} - -static inline bool isar_feature_aa32_ssbs(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_pfr2, ID_PFR2, SSBS) != 0; -} - -static inline bool isar_feature_aa32_debugv8p2(const ARMISARegisters *id) -{ - return FIELD_EX32(id->id_dfr0, ID_DFR0, COPDBG) >= 8; -} - -/* - * 64-bit feature tests via id registers. - */ -static inline bool isar_feature_aa64_aes(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, AES) != 0; -} - -static inline bool isar_feature_aa64_pmull(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, AES) > 1; -} - -static inline bool isar_feature_aa64_sha1(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA1) != 0; -} - -static inline bool isar_feature_aa64_sha256(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA2) != 0; -} - -static inline bool isar_feature_aa64_sha512(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA2) > 1; -} - -static inline bool isar_feature_aa64_crc32(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, CRC32) != 0; -} - -static inline bool isar_feature_aa64_atomics(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, ATOMIC) != 0; -} - -static inline bool isar_feature_aa64_rdm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, RDM) != 0; -} - -static inline bool isar_feature_aa64_sha3(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA3) != 0; -} - -static inline bool isar_feature_aa64_sm3(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SM3) != 0; -} - -static inline bool isar_feature_aa64_sm4(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SM4) != 0; -} - -static inline bool isar_feature_aa64_dp(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, DP) != 0; -} - -static inline bool isar_feature_aa64_fhm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, FHM) != 0; -} - -static inline bool isar_feature_aa64_condm_4(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) != 0; -} - -static inline bool isar_feature_aa64_condm_5(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) >= 2; -} - -static inline bool isar_feature_aa64_rndr(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, RNDR) != 0; -} - -static inline bool isar_feature_aa64_jscvt(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, JSCVT) != 0; -} - -static inline bool isar_feature_aa64_fcma(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FCMA) != 0; -} - -static inline bool isar_feature_aa64_pauth(const ARMISARegisters *id) -{ - /* - * Return true if any form of pauth is enabled, as this - * predicate controls migration of the 128-bit keys. - */ - return (id->id_aa64isar1 & - (FIELD_DP64(0, ID_AA64ISAR1, APA, 0xf) | - FIELD_DP64(0, ID_AA64ISAR1, API, 0xf) | - FIELD_DP64(0, ID_AA64ISAR1, GPA, 0xf) | - FIELD_DP64(0, ID_AA64ISAR1, GPI, 0xf))) != 0; -} - -static inline bool isar_feature_aa64_pauth_arch(const ARMISARegisters *id) -{ - /* - * Return true if pauth is enabled with the architected QARMA algorithm. - * QEMU will always set APA+GPA to the same value. - */ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, APA) != 0; -} - -static inline bool isar_feature_aa64_tlbirange(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) == 2; -} - -static inline bool isar_feature_aa64_tlbios(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) != 0; -} - -static inline bool isar_feature_aa64_sb(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SB) != 0; -} - -static inline bool isar_feature_aa64_predinv(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SPECRES) != 0; -} - -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_bf16(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, BF16) != 0; -} - -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. */ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, FP) == 1; -} - -static inline bool isar_feature_aa64_aa32(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL0) >= 2; -} - -static inline bool isar_feature_aa64_aa32_el1(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL1) >= 2; -} - -static inline bool isar_feature_aa64_ras(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RAS) != 0; -} - -static inline bool isar_feature_aa64_doublefault(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RAS) >= 2; -} - -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_sel2(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, SEL2) != 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_hcx(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HCX) != 0; -} - -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_st(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, ST) != 0; -} - -static inline bool isar_feature_aa64_fwb(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, FWB) != 0; -} - -static inline bool isar_feature_aa64_ids(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, IDS) != 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_mte_insn_reg(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) != 0; -} - -static inline bool isar_feature_aa64_mte(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) >= 2; -} - -static inline bool isar_feature_aa64_sme(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SME) != 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_i8mm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, I8MM) != 0; -} - -static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) -{ - return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 1; -} - -static inline bool isar_feature_aa64_tgran4_2_lpa2(const ARMISARegisters *id) -{ - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); - return t >= 3 || (t == 0 && isar_feature_aa64_tgran4_lpa2(id)); -} - -static inline bool isar_feature_aa64_tgran16_lpa2(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 2; -} - -static inline bool isar_feature_aa64_tgran16_2_lpa2(const ARMISARegisters *id) -{ - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); - return t >= 3 || (t == 0 && isar_feature_aa64_tgran16_lpa2(id)); -} - -static inline bool isar_feature_aa64_ccidx(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, CCIDX) != 0; -} - -static inline bool isar_feature_aa64_lva(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, VARANGE) != 0; -} - -static inline bool isar_feature_aa64_tts2uxn(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, XNX) != 0; -} - -static inline bool isar_feature_aa64_dit(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, DIT) != 0; -} - -static inline bool isar_feature_aa64_scxtnum(const ARMISARegisters *id) -{ - int key = FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, CSV2); - if (key >= 2) { - return true; /* FEAT_CSV2_2 */ - } - if (key == 1) { - key = FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, CSV2_FRAC); - return key >= 2; /* FEAT_CSV2_1p2 */ - } - return false; -} - -static inline bool isar_feature_aa64_ssbs(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SSBS) != 0; -} - -static inline bool isar_feature_aa64_debugv8p2(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, DEBUGVER) >= 8; -} - -static inline bool isar_feature_aa64_sve2(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SVEVER) != 0; -} - -static inline bool isar_feature_aa64_sve2_aes(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, AES) != 0; -} - -static inline bool isar_feature_aa64_sve2_pmull128(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, AES) >= 2; -} - -static inline bool isar_feature_aa64_sve2_bitperm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, BITPERM) != 0; -} - -static inline bool isar_feature_aa64_sve_bf16(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, BFLOAT16) != 0; -} - -static inline bool isar_feature_aa64_sve2_sha3(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SHA3) != 0; -} - -static inline bool isar_feature_aa64_sve2_sm4(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SM4) != 0; -} - -static inline bool isar_feature_aa64_sve_i8mm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, I8MM) != 0; -} - -static inline bool isar_feature_aa64_sve_f32mm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, F32MM) != 0; -} - -static inline bool isar_feature_aa64_sve_f64mm(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, F64MM) != 0; -} - -static inline bool isar_feature_aa64_sme_f64f64(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, F64F64); -} - -static inline bool isar_feature_aa64_sme_i16i64(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, I16I64) == 0xf; -} - -static inline bool isar_feature_aa64_sme_fa64(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, FA64); -} - -/* - * 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); -} - -static inline bool isar_feature_any_tts2uxn(const ARMISARegisters *id) -{ - return isar_feature_aa64_tts2uxn(id) || isar_feature_aa32_tts2uxn(id); -} - -static inline bool isar_feature_any_debugv8p2(const ARMISARegisters *id) -{ - return isar_feature_aa64_debugv8p2(id) || isar_feature_aa32_debugv8p2(id); -} - -static inline bool isar_feature_any_ras(const ARMISARegisters *id) -{ - return isar_feature_aa64_ras(id) || isar_feature_aa32_ras(id); -} - -/* - * Forward to the above feature tests given an ARMCPU pointer. - */ -#define cpu_isar_feature(name, cpu) \ - ({ ARMCPU *cpu_ = (cpu); isar_feature_##name(&cpu_->isar); }) - #endif diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 15665c962b..985b1efe16 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -21,322 +21,19 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "cpu.h" -#ifdef CONFIG_TCG -#include "hw/core/tcg-cpu-ops.h" -#endif /* CONFIG_TCG */ +#include "cpregs.h" #include "qemu/module.h" -#if !defined(CONFIG_USER_ONLY) -#include "hw/loader.h" -#endif #include "sysemu/kvm.h" #include "sysemu/hvf.h" +#include "sysemu/qtest.h" +#include "sysemu/tcg.h" #include "kvm_arm.h" #include "hvf_arm.h" #include "qapi/visitor.h" #include "hw/qdev-properties.h" #include "internals.h" - - -static void aarch64_a57_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,cortex-a57"; - set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); - set_feature(&cpu->env, ARM_FEATURE_AARCH64); - set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); - set_feature(&cpu->env, ARM_FEATURE_EL2); - set_feature(&cpu->env, ARM_FEATURE_EL3); - set_feature(&cpu->env, ARM_FEATURE_PMU); - cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A57; - cpu->midr = 0x411fd070; - cpu->revidr = 0x00000000; - cpu->reset_fpsid = 0x41034070; - cpu->isar.mvfr0 = 0x10110222; - cpu->isar.mvfr1 = 0x12111111; - cpu->isar.mvfr2 = 0x00000043; - cpu->ctr = 0x8444c004; - cpu->reset_sctlr = 0x00c50838; - cpu->isar.id_pfr0 = 0x00000131; - cpu->isar.id_pfr1 = 0x00011011; - cpu->isar.id_dfr0 = 0x03010066; - cpu->id_afr0 = 0x00000000; - 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; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00011142; - cpu->isar.id_isar5 = 0x00011121; - cpu->isar.id_isar6 = 0; - cpu->isar.id_aa64pfr0 = 0x00002222; - cpu->isar.id_aa64dfr0 = 0x10305106; - cpu->isar.id_aa64isar0 = 0x00011120; - cpu->isar.id_aa64mmfr0 = 0x00001124; - cpu->isar.dbgdidr = 0x3516d000; - cpu->isar.reset_pmcr_el0 = 0x41013000; - cpu->clidr = 0x0a200023; - cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ - cpu->ccsidr[1] = 0x201fe012; /* 48KB L1 icache */ - cpu->ccsidr[2] = 0x70ffe07a; /* 2048KB L2 cache */ - cpu->dcz_blocksize = 4; /* 64 bytes */ - cpu->gic_num_lrs = 4; - cpu->gic_vpribits = 5; - cpu->gic_vprebits = 5; - cpu->gic_pribits = 5; - define_cortex_a72_a57_a53_cp_reginfo(cpu); -} - -static void aarch64_a53_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,cortex-a53"; - set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); - set_feature(&cpu->env, ARM_FEATURE_AARCH64); - set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); - set_feature(&cpu->env, ARM_FEATURE_EL2); - set_feature(&cpu->env, ARM_FEATURE_EL3); - set_feature(&cpu->env, ARM_FEATURE_PMU); - cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A53; - cpu->midr = 0x410fd034; - cpu->revidr = 0x00000000; - cpu->reset_fpsid = 0x41034070; - cpu->isar.mvfr0 = 0x10110222; - cpu->isar.mvfr1 = 0x12111111; - cpu->isar.mvfr2 = 0x00000043; - cpu->ctr = 0x84448004; /* L1Ip = VIPT */ - cpu->reset_sctlr = 0x00c50838; - cpu->isar.id_pfr0 = 0x00000131; - cpu->isar.id_pfr1 = 0x00011011; - cpu->isar.id_dfr0 = 0x03010066; - cpu->id_afr0 = 0x00000000; - 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; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00011142; - cpu->isar.id_isar5 = 0x00011121; - cpu->isar.id_isar6 = 0; - cpu->isar.id_aa64pfr0 = 0x00002222; - cpu->isar.id_aa64dfr0 = 0x10305106; - cpu->isar.id_aa64isar0 = 0x00011120; - cpu->isar.id_aa64mmfr0 = 0x00001122; /* 40 bit physical addr */ - cpu->isar.dbgdidr = 0x3516d000; - cpu->isar.reset_pmcr_el0 = 0x41033000; - cpu->clidr = 0x0a200023; - cpu->ccsidr[0] = 0x700fe01a; /* 32KB L1 dcache */ - cpu->ccsidr[1] = 0x201fe00a; /* 32KB L1 icache */ - cpu->ccsidr[2] = 0x707fe07a; /* 1024KB L2 cache */ - cpu->dcz_blocksize = 4; /* 64 bytes */ - cpu->gic_num_lrs = 4; - cpu->gic_vpribits = 5; - cpu->gic_vprebits = 5; - cpu->gic_pribits = 5; - define_cortex_a72_a57_a53_cp_reginfo(cpu); -} - -static void aarch64_a72_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,cortex-a72"; - set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); - set_feature(&cpu->env, ARM_FEATURE_AARCH64); - set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); - set_feature(&cpu->env, ARM_FEATURE_EL2); - set_feature(&cpu->env, ARM_FEATURE_EL3); - set_feature(&cpu->env, ARM_FEATURE_PMU); - cpu->midr = 0x410fd083; - cpu->revidr = 0x00000000; - cpu->reset_fpsid = 0x41034080; - cpu->isar.mvfr0 = 0x10110222; - cpu->isar.mvfr1 = 0x12111111; - cpu->isar.mvfr2 = 0x00000043; - cpu->ctr = 0x8444c004; - cpu->reset_sctlr = 0x00c50838; - cpu->isar.id_pfr0 = 0x00000131; - cpu->isar.id_pfr1 = 0x00011011; - cpu->isar.id_dfr0 = 0x03010066; - cpu->id_afr0 = 0x00000000; - 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; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00011142; - cpu->isar.id_isar5 = 0x00011121; - cpu->isar.id_aa64pfr0 = 0x00002222; - cpu->isar.id_aa64dfr0 = 0x10305106; - cpu->isar.id_aa64isar0 = 0x00011120; - cpu->isar.id_aa64mmfr0 = 0x00001124; - cpu->isar.dbgdidr = 0x3516d000; - cpu->isar.reset_pmcr_el0 = 0x41023000; - cpu->clidr = 0x0a200023; - cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ - cpu->ccsidr[1] = 0x201fe012; /* 48KB L1 icache */ - cpu->ccsidr[2] = 0x707fe07a; /* 1MB L2 cache */ - cpu->dcz_blocksize = 4; /* 64 bytes */ - cpu->gic_num_lrs = 4; - cpu->gic_vpribits = 5; - cpu->gic_vprebits = 5; - cpu->gic_pribits = 5; - define_cortex_a72_a57_a53_cp_reginfo(cpu); -} - -static void aarch64_a76_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,cortex-a76"; - set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); - set_feature(&cpu->env, ARM_FEATURE_AARCH64); - set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); - set_feature(&cpu->env, ARM_FEATURE_EL2); - set_feature(&cpu->env, ARM_FEATURE_EL3); - set_feature(&cpu->env, ARM_FEATURE_PMU); - - /* Ordered by B2.4 AArch64 registers by functional group */ - cpu->clidr = 0x82000023; - cpu->ctr = 0x8444C004; - cpu->dcz_blocksize = 4; - cpu->isar.id_aa64dfr0 = 0x0000000010305408ull; - cpu->isar.id_aa64isar0 = 0x0000100010211120ull; - cpu->isar.id_aa64isar1 = 0x0000000000100001ull; - cpu->isar.id_aa64mmfr0 = 0x0000000000101122ull; - cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; - cpu->isar.id_aa64mmfr2 = 0x0000000000001011ull; - cpu->isar.id_aa64pfr0 = 0x1100000010111112ull; /* GIC filled in later */ - cpu->isar.id_aa64pfr1 = 0x0000000000000010ull; - cpu->id_afr0 = 0x00000000; - cpu->isar.id_dfr0 = 0x04010088; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00010142; - cpu->isar.id_isar5 = 0x01011121; - cpu->isar.id_isar6 = 0x00000010; - cpu->isar.id_mmfr0 = 0x10201105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02122211; - cpu->isar.id_mmfr4 = 0x00021110; - cpu->isar.id_pfr0 = 0x10010131; - cpu->isar.id_pfr1 = 0x00010000; /* GIC filled in later */ - cpu->isar.id_pfr2 = 0x00000011; - cpu->midr = 0x414fd0b1; /* r4p1 */ - cpu->revidr = 0; - - /* From B2.18 CCSIDR_EL1 */ - cpu->ccsidr[0] = 0x701fe01a; /* 64KB L1 dcache */ - cpu->ccsidr[1] = 0x201fe01a; /* 64KB L1 icache */ - cpu->ccsidr[2] = 0x707fe03a; /* 512KB L2 cache */ - - /* From B2.93 SCTLR_EL3 */ - cpu->reset_sctlr = 0x30c50838; - - /* From B4.23 ICH_VTR_EL2 */ - cpu->gic_num_lrs = 4; - cpu->gic_vpribits = 5; - cpu->gic_vprebits = 5; - cpu->gic_pribits = 5; - - /* From B5.1 AdvSIMD AArch64 register summary */ - cpu->isar.mvfr0 = 0x10110222; - cpu->isar.mvfr1 = 0x13211111; - cpu->isar.mvfr2 = 0x00000043; - - /* From D5.1 AArch64 PMU register summary */ - cpu->isar.reset_pmcr_el0 = 0x410b3000; -} - -static void aarch64_neoverse_n1_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,neoverse-n1"; - set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); - set_feature(&cpu->env, ARM_FEATURE_AARCH64); - set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); - set_feature(&cpu->env, ARM_FEATURE_EL2); - set_feature(&cpu->env, ARM_FEATURE_EL3); - set_feature(&cpu->env, ARM_FEATURE_PMU); - - /* Ordered by B2.4 AArch64 registers by functional group */ - cpu->clidr = 0x82000023; - cpu->ctr = 0x8444c004; - cpu->dcz_blocksize = 4; - cpu->isar.id_aa64dfr0 = 0x0000000110305408ull; - cpu->isar.id_aa64isar0 = 0x0000100010211120ull; - cpu->isar.id_aa64isar1 = 0x0000000000100001ull; - cpu->isar.id_aa64mmfr0 = 0x0000000000101125ull; - cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; - cpu->isar.id_aa64mmfr2 = 0x0000000000001011ull; - cpu->isar.id_aa64pfr0 = 0x1100000010111112ull; /* GIC filled in later */ - cpu->isar.id_aa64pfr1 = 0x0000000000000020ull; - cpu->id_afr0 = 0x00000000; - cpu->isar.id_dfr0 = 0x04010088; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00010142; - cpu->isar.id_isar5 = 0x01011121; - cpu->isar.id_isar6 = 0x00000010; - cpu->isar.id_mmfr0 = 0x10201105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02122211; - cpu->isar.id_mmfr4 = 0x00021110; - cpu->isar.id_pfr0 = 0x10010131; - cpu->isar.id_pfr1 = 0x00010000; /* GIC filled in later */ - cpu->isar.id_pfr2 = 0x00000011; - cpu->midr = 0x414fd0c1; /* r4p1 */ - cpu->revidr = 0; - - /* From B2.23 CCSIDR_EL1 */ - cpu->ccsidr[0] = 0x701fe01a; /* 64KB L1 dcache */ - cpu->ccsidr[1] = 0x201fe01a; /* 64KB L1 icache */ - cpu->ccsidr[2] = 0x70ffe03a; /* 1MB L2 cache */ - - /* From B2.98 SCTLR_EL3 */ - cpu->reset_sctlr = 0x30c50838; - - /* From B4.23 ICH_VTR_EL2 */ - cpu->gic_num_lrs = 4; - cpu->gic_vpribits = 5; - cpu->gic_vprebits = 5; - cpu->gic_pribits = 5; - - /* From B5.1 AdvSIMD AArch64 register summary */ - cpu->isar.mvfr0 = 0x10110222; - cpu->isar.mvfr1 = 0x13211111; - cpu->isar.mvfr2 = 0x00000043; - - /* From D5.1 AArch64 PMU register summary */ - cpu->isar.reset_pmcr_el0 = 0x410c3000; -} +#include "cpu-features.h" +#include "cpregs.h" void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) { @@ -355,8 +52,8 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) * any of the above. Finally, if SVE is not disabled, then at least one * vector length must be enabled. */ - uint32_t vq_map = cpu->sve_vq_map; - uint32_t vq_init = cpu->sve_vq_init; + uint32_t vq_map = cpu->sve_vq.map; + uint32_t vq_init = cpu->sve_vq.init; uint32_t vq_supported; uint32_t vq_mask = 0; uint32_t tmp, vq, max_vq = 0; @@ -369,14 +66,14 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) */ if (kvm_enabled()) { if (kvm_arm_sve_supported()) { - cpu->sve_vq_supported = kvm_arm_sve_get_vls(CPU(cpu)); - vq_supported = cpu->sve_vq_supported; + cpu->sve_vq.supported = kvm_arm_sve_get_vls(cpu); + vq_supported = cpu->sve_vq.supported; } else { assert(!cpu_isar_feature(aa64_sve, cpu)); vq_supported = 0; } } else { - vq_supported = cpu->sve_vq_supported; + vq_supported = cpu->sve_vq.supported; } /* @@ -399,7 +96,7 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) if (kvm_enabled()) { /* - * For KVM we have to automatically enable all supported unitialized + * For KVM we have to automatically enable all supported uninitialized * lengths, even when the smaller lengths are not all powers-of-two. */ vq_map |= vq_supported & ~vq_init & vq_mask; @@ -426,10 +123,10 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) vq = ctz32(tmp) + 1; max_vq = vq <= ARM_MAX_VQ ? vq - 1 : ARM_MAX_VQ; - vq_mask = MAKE_64BIT_MASK(0, max_vq); + vq_mask = max_vq > 0 ? MAKE_64BIT_MASK(0, max_vq) : 0; vq_map = vq_supported & ~vq_init & vq_mask; - if (max_vq == 0 || vq_map == 0) { + if (vq_map == 0) { error_setg(errp, "cannot disable sve%d", vq * 128); error_append_hint(errp, "Disabling sve%d results in all " "vector lengths being disabled.\n", @@ -487,8 +184,13 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) "using only sve properties.\n"); } else { error_setg(errp, "cannot enable sve%d", vq * 128); - error_append_hint(errp, "This CPU does not support " - "the vector length %d-bits.\n", vq * 128); + if (vq_supported) { + error_append_hint(errp, "This CPU does not support " + "the vector length %d-bits.\n", vq * 128); + } else { + error_append_hint(errp, "SVE not supported by KVM " + "on this host\n"); + } } return; } else { @@ -529,76 +231,38 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) /* From now on sve_max_vq is the actual maximum supported length. */ cpu->sve_max_vq = max_vq; - cpu->sve_vq_map = vq_map; -} - -static void cpu_max_get_sve_max_vq(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ARMCPU *cpu = ARM_CPU(obj); - uint32_t value; - - /* All vector lengths are disabled when SVE is off. */ - if (!cpu_isar_feature(aa64_sve, cpu)) { - value = 0; - } else { - value = cpu->sve_max_vq; - } - visit_type_uint32(v, name, &value, errp); -} - -static void cpu_max_set_sve_max_vq(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ARMCPU *cpu = ARM_CPU(obj); - uint32_t max_vq; - - if (!visit_type_uint32(v, name, &max_vq, errp)) { - return; - } - - if (kvm_enabled() && !kvm_arm_sve_supported()) { - error_setg(errp, "cannot set sve-max-vq"); - error_append_hint(errp, "SVE not supported by KVM on this host\n"); - return; - } - - if (max_vq == 0 || max_vq > ARM_MAX_VQ) { - error_setg(errp, "unsupported SVE vector length"); - error_append_hint(errp, "Valid sve-max-vq in range [1-%d]\n", - ARM_MAX_VQ); - return; - } - - cpu->sve_max_vq = max_vq; + cpu->sve_vq.map = vq_map; } /* - * Note that cpu_arm_get/set_sve_vq cannot use the simpler - * object_property_add_bool interface because they make use - * of the contents of "name" to determine which bit on which - * to operate. + * Note that cpu_arm_{get,set}_vq cannot use the simpler + * object_property_add_bool interface because they make use of the + * contents of "name" to determine which bit on which to operate. */ -static void cpu_arm_get_sve_vq(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static void cpu_arm_get_vq(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { ARMCPU *cpu = ARM_CPU(obj); + ARMVQMap *vq_map = opaque; uint32_t vq = atoi(&name[3]) / 128; + bool sve = vq_map == &cpu->sve_vq; bool value; - /* All vector lengths are disabled when SVE is off. */ - if (!cpu_isar_feature(aa64_sve, cpu)) { + /* All vector lengths are disabled when feature is off. */ + if (sve + ? !cpu_isar_feature(aa64_sve, cpu) + : !cpu_isar_feature(aa64_sme, cpu)) { value = false; } else { - value = extract32(cpu->sve_vq_map, vq - 1, 1); + value = extract32(vq_map->map, vq - 1, 1); } visit_type_bool(v, name, &value, errp); } -static void cpu_arm_set_sve_vq(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static void cpu_arm_set_vq(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { - ARMCPU *cpu = ARM_CPU(obj); + ARMVQMap *vq_map = opaque; uint32_t vq = atoi(&name[3]) / 128; bool value; @@ -606,14 +270,8 @@ static void cpu_arm_set_sve_vq(Object *obj, Visitor *v, const char *name, return; } - if (value && kvm_enabled() && !kvm_arm_sve_supported()) { - error_setg(errp, "cannot enable %s", name); - error_append_hint(errp, "SVE not supported by KVM on this host\n"); - return; - } - - cpu->sve_vq_map = deposit32(cpu->sve_vq_map, vq - 1, 1, value); - cpu->sve_vq_init |= 1 << (vq - 1); + vq_map->map = deposit32(vq_map->map, vq - 1, 1, value); + vq_map->init |= 1 << (vq - 1); } static bool cpu_arm_get_sve(Object *obj, Error **errp) @@ -637,13 +295,85 @@ static void cpu_arm_set_sve(Object *obj, bool value, Error **errp) cpu->isar.id_aa64pfr0 = t; } -#ifdef CONFIG_USER_ONLY -/* Mirror linux /proc/sys/abi/sve_default_vector_length. */ -static void cpu_arm_set_sve_default_vec_len(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) +void arm_cpu_sme_finalize(ARMCPU *cpu, Error **errp) +{ + uint32_t vq_map = cpu->sme_vq.map; + uint32_t vq_init = cpu->sme_vq.init; + uint32_t vq_supported = cpu->sme_vq.supported; + uint32_t vq; + + if (vq_map == 0) { + if (!cpu_isar_feature(aa64_sme, cpu)) { + cpu->isar.id_aa64smfr0 = 0; + return; + } + + /* TODO: KVM will require limitations via SMCR_EL2. */ + vq_map = vq_supported & ~vq_init; + + if (vq_map == 0) { + vq = ctz32(vq_supported) + 1; + error_setg(errp, "cannot disable sme%d", vq * 128); + error_append_hint(errp, "All SME vector lengths are disabled.\n"); + error_append_hint(errp, "With SME enabled, at least one " + "vector length must be enabled.\n"); + return; + } + } else { + if (!cpu_isar_feature(aa64_sme, cpu)) { + vq = 32 - clz32(vq_map); + error_setg(errp, "cannot enable sme%d", vq * 128); + error_append_hint(errp, "SME must be enabled to enable " + "vector lengths.\n"); + error_append_hint(errp, "Add sme=on to the CPU property list.\n"); + return; + } + /* TODO: KVM will require limitations via SMCR_EL2. */ + } + + cpu->sme_vq.map = vq_map; +} + +static bool cpu_arm_get_sme(Object *obj, Error **errp) { ARMCPU *cpu = ARM_CPU(obj); + return cpu_isar_feature(aa64_sme, cpu); +} + +static void cpu_arm_set_sme(Object *obj, bool value, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + uint64_t t; + + t = cpu->isar.id_aa64pfr1; + t = FIELD_DP64(t, ID_AA64PFR1, SME, value); + cpu->isar.id_aa64pfr1 = t; +} + +static bool cpu_arm_get_sme_fa64(Object *obj, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + return cpu_isar_feature(aa64_sme, cpu) && + cpu_isar_feature(aa64_sme_fa64, cpu); +} + +static void cpu_arm_set_sme_fa64(Object *obj, bool value, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + uint64_t t; + + t = cpu->isar.id_aa64smfr0; + t = FIELD_DP64(t, ID_AA64SMFR0, FA64, value); + cpu->isar.id_aa64smfr0 = t; +} + +#ifdef CONFIG_USER_ONLY +/* Mirror linux /proc/sys/abi/{sve,sme}_default_vector_length. */ +static void cpu_arm_set_default_vec_len(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + uint32_t *ptr_default_vq = opaque; int32_t default_len, default_vq, remainder; if (!visit_type_int32(v, name, &default_len, errp)) { @@ -652,7 +382,7 @@ static void cpu_arm_set_sve_default_vec_len(Object *obj, Visitor *v, /* Undocumented, but the kernel allows -1 to indicate "maximum". */ if (default_len == -1) { - cpu->sve_default_vq = ARM_MAX_VQ; + *ptr_default_vq = ARM_MAX_VQ; return; } @@ -664,7 +394,11 @@ static void cpu_arm_set_sve_default_vec_len(Object *obj, Visitor *v, * and is the maximum architectural width of ZCR_ELx.LEN. */ if (remainder || default_vq < 1 || default_vq > 512) { - error_setg(errp, "cannot set sve-default-vector-length"); + ARMCPU *cpu = ARM_CPU(obj); + const char *which = + (ptr_default_vq == &cpu->sve_default_vq ? "sve" : "sme"); + + error_setg(errp, "cannot set %s-default-vector-length", which); if (remainder) { error_append_hint(errp, "Vector length not a multiple of 16\n"); } else if (default_vq < 1) { @@ -676,15 +410,15 @@ static void cpu_arm_set_sve_default_vec_len(Object *obj, Visitor *v, return; } - cpu->sve_default_vq = default_vq; + *ptr_default_vq = default_vq; } -static void cpu_arm_get_sve_default_vec_len(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) +static void cpu_arm_get_default_vec_len(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { - ARMCPU *cpu = ARM_CPU(obj); - int32_t value = cpu->sve_default_vq * 16; + uint32_t *ptr_default_vq = opaque; + int32_t value = *ptr_default_vq * 16; visit_type_int32(v, name, &value, errp); } @@ -692,6 +426,7 @@ static void cpu_arm_get_sve_default_vec_len(Object *obj, Visitor *v, void aarch64_add_sve_properties(Object *obj) { + ARMCPU *cpu = ARM_CPU(obj); uint32_t vq; object_property_add_bool(obj, "sve", cpu_arm_get_sve, cpu_arm_set_sve); @@ -699,57 +434,120 @@ void aarch64_add_sve_properties(Object *obj) for (vq = 1; vq <= ARM_MAX_VQ; ++vq) { char name[8]; sprintf(name, "sve%d", vq * 128); - object_property_add(obj, name, "bool", cpu_arm_get_sve_vq, - cpu_arm_set_sve_vq, NULL, NULL); + object_property_add(obj, name, "bool", cpu_arm_get_vq, + cpu_arm_set_vq, NULL, &cpu->sve_vq); } #ifdef CONFIG_USER_ONLY /* Mirror linux /proc/sys/abi/sve_default_vector_length. */ object_property_add(obj, "sve-default-vector-length", "int32", - cpu_arm_get_sve_default_vec_len, - cpu_arm_set_sve_default_vec_len, NULL, NULL); + cpu_arm_get_default_vec_len, + cpu_arm_set_default_vec_len, NULL, + &cpu->sve_default_vq); +#endif +} + +void aarch64_add_sme_properties(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + uint32_t vq; + + object_property_add_bool(obj, "sme", cpu_arm_get_sme, cpu_arm_set_sme); + object_property_add_bool(obj, "sme_fa64", cpu_arm_get_sme_fa64, + cpu_arm_set_sme_fa64); + + for (vq = 1; vq <= ARM_MAX_VQ; vq <<= 1) { + char name[8]; + sprintf(name, "sme%d", vq * 128); + object_property_add(obj, name, "bool", cpu_arm_get_vq, + cpu_arm_set_vq, NULL, &cpu->sme_vq); + } + +#ifdef CONFIG_USER_ONLY + /* Mirror linux /proc/sys/abi/sme_default_vector_length. */ + object_property_add(obj, "sme-default-vector-length", "int32", + cpu_arm_get_default_vec_len, + cpu_arm_set_default_vec_len, NULL, + &cpu->sme_default_vq); #endif } void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) { - int arch_val = 0, impdef_val = 0; - uint64_t t; + ARMPauthFeature features = cpu_isar_feature(pauth_feature, cpu); + uint64_t isar1, isar2; - /* Exit early if PAuth is enabled, and fall through to disable it */ - if ((kvm_enabled() || hvf_enabled()) && cpu->prop_pauth) { - if (!cpu_isar_feature(aa64_pauth, cpu)) { - error_setg(errp, "'pauth' feature not supported by %s on this host", - kvm_enabled() ? "KVM" : "hvf"); + /* + * These properties enable or disable Pauth as a whole, or change + * the pauth algorithm, but do not change the set of features that + * are present. We have saved a copy of those features above and + * will now place it into the field that chooses the algorithm. + * + * Begin by disabling all fields. + */ + isar1 = cpu->isar.id_aa64isar1; + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, APA, 0); + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPA, 0); + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, API, 0); + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPI, 0); + + isar2 = cpu->isar.id_aa64isar2; + isar2 = FIELD_DP64(isar2, ID_AA64ISAR2, APA3, 0); + isar2 = FIELD_DP64(isar2, ID_AA64ISAR2, GPA3, 0); + + if (kvm_enabled() || hvf_enabled()) { + /* + * Exit early if PAuth is enabled and fall through to disable it. + * The algorithm selection properties are not present. + */ + if (cpu->prop_pauth) { + if (features == 0) { + error_setg(errp, "'pauth' feature not supported by " + "%s on this host", current_accel_name()); + } + return; + } + } else { + /* Pauth properties are only present when the model supports it. */ + if (features == 0) { + assert(!cpu->prop_pauth); + return; } - return; - } + if (cpu->prop_pauth) { + if (cpu->prop_pauth_impdef && cpu->prop_pauth_qarma3) { + error_setg(errp, + "cannot enable both pauth-impdef and pauth-qarma3"); + return; + } - /* TODO: Handle HaveEnhancedPAC, HaveEnhancedPAC2, HaveFPAC. */ - if (cpu->prop_pauth) { - if (cpu->prop_pauth_impdef) { - impdef_val = 1; - } else { - arch_val = 1; + if (cpu->prop_pauth_impdef) { + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, API, features); + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPI, 1); + } else if (cpu->prop_pauth_qarma3) { + isar2 = FIELD_DP64(isar2, ID_AA64ISAR2, APA3, features); + isar2 = FIELD_DP64(isar2, ID_AA64ISAR2, GPA3, 1); + } else { + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, APA, features); + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPA, 1); + } + } else if (cpu->prop_pauth_impdef || cpu->prop_pauth_qarma3) { + error_setg(errp, "cannot enable pauth-impdef or " + "pauth-qarma3 without pauth"); + error_append_hint(errp, "Add pauth=on to the CPU property list.\n"); } - } else if (cpu->prop_pauth_impdef) { - error_setg(errp, "cannot enable pauth-impdef without pauth"); - error_append_hint(errp, "Add pauth=on to the CPU property list.\n"); } - t = cpu->isar.id_aa64isar1; - t = FIELD_DP64(t, ID_AA64ISAR1, APA, arch_val); - t = FIELD_DP64(t, ID_AA64ISAR1, GPA, arch_val); - t = FIELD_DP64(t, ID_AA64ISAR1, API, impdef_val); - t = FIELD_DP64(t, ID_AA64ISAR1, GPI, impdef_val); - cpu->isar.id_aa64isar1 = t; + cpu->isar.id_aa64isar1 = isar1; + cpu->isar.id_aa64isar2 = isar2; } static Property arm_cpu_pauth_property = DEFINE_PROP_BOOL("pauth", ARMCPU, prop_pauth, true); static Property arm_cpu_pauth_impdef_property = DEFINE_PROP_BOOL("pauth-impdef", ARMCPU, prop_pauth_impdef, false); +static Property arm_cpu_pauth_qarma3_property = + DEFINE_PROP_BOOL("pauth-qarma3", ARMCPU, prop_pauth_qarma3, false); void aarch64_add_pauth_properties(Object *obj) { @@ -769,12 +567,10 @@ void aarch64_add_pauth_properties(Object *obj) cpu->prop_pauth = cpu_isar_feature(aa64_pauth, cpu); } else { qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_impdef_property); + qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_qarma3_property); } } -static Property arm_cpu_lpa2_property = - DEFINE_PROP_BOOL("lpa2", ARMCPU, prop_lpa2, true); - void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp) { uint64_t t; @@ -795,6 +591,120 @@ void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp) cpu->isar.id_aa64mmfr0 = t; } +static void aarch64_a57_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a57"; + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); + cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A57; + cpu->midr = 0x411fd070; + cpu->revidr = 0x00000000; + cpu->reset_fpsid = 0x41034070; + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x12111111; + cpu->isar.mvfr2 = 0x00000043; + cpu->ctr = 0x8444c004; + cpu->reset_sctlr = 0x00c50838; + cpu->isar.id_pfr0 = 0x00000131; + cpu->isar.id_pfr1 = 0x00011011; + cpu->isar.id_dfr0 = 0x03010066; + cpu->id_afr0 = 0x00000000; + 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; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00011142; + cpu->isar.id_isar5 = 0x00011121; + cpu->isar.id_isar6 = 0; + cpu->isar.id_aa64pfr0 = 0x00002222; + cpu->isar.id_aa64dfr0 = 0x10305106; + cpu->isar.id_aa64isar0 = 0x00011120; + cpu->isar.id_aa64mmfr0 = 0x00001124; + cpu->isar.dbgdidr = 0x3516d000; + cpu->isar.dbgdevid = 0x01110f13; + cpu->isar.dbgdevid1 = 0x2; + cpu->isar.reset_pmcr_el0 = 0x41013000; + cpu->clidr = 0x0a200023; + cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ + cpu->ccsidr[1] = 0x201fe012; /* 48KB L1 icache */ + cpu->ccsidr[2] = 0x70ffe07a; /* 2048KB L2 cache */ + cpu->dcz_blocksize = 4; /* 64 bytes */ + cpu->gic_num_lrs = 4; + cpu->gic_vpribits = 5; + cpu->gic_vprebits = 5; + cpu->gic_pribits = 5; + define_cortex_a72_a57_a53_cp_reginfo(cpu); +} + +static void aarch64_a53_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a53"; + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); + cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A53; + cpu->midr = 0x410fd034; + cpu->revidr = 0x00000100; + cpu->reset_fpsid = 0x41034070; + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x12111111; + cpu->isar.mvfr2 = 0x00000043; + cpu->ctr = 0x84448004; /* L1Ip = VIPT */ + cpu->reset_sctlr = 0x00c50838; + cpu->isar.id_pfr0 = 0x00000131; + cpu->isar.id_pfr1 = 0x00011011; + cpu->isar.id_dfr0 = 0x03010066; + cpu->id_afr0 = 0x00000000; + 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; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00011142; + cpu->isar.id_isar5 = 0x00011121; + cpu->isar.id_isar6 = 0; + cpu->isar.id_aa64pfr0 = 0x00002222; + cpu->isar.id_aa64dfr0 = 0x10305106; + cpu->isar.id_aa64isar0 = 0x00011120; + cpu->isar.id_aa64mmfr0 = 0x00001122; /* 40 bit physical addr */ + cpu->isar.dbgdidr = 0x3516d000; + cpu->isar.dbgdevid = 0x00110f13; + cpu->isar.dbgdevid1 = 0x1; + cpu->isar.reset_pmcr_el0 = 0x41033000; + cpu->clidr = 0x0a200023; + cpu->ccsidr[0] = 0x700fe01a; /* 32KB L1 dcache */ + cpu->ccsidr[1] = 0x201fe00a; /* 32KB L1 icache */ + cpu->ccsidr[2] = 0x707fe07a; /* 1024KB L2 cache */ + cpu->dcz_blocksize = 4; /* 64 bytes */ + cpu->gic_num_lrs = 4; + cpu->gic_vpribits = 5; + cpu->gic_vprebits = 5; + cpu->gic_pribits = 5; + define_cortex_a72_a57_a53_cp_reginfo(cpu); +} + static void aarch64_host_initfn(Object *obj) { #if defined(CONFIG_KVM) @@ -813,233 +723,27 @@ static void aarch64_host_initfn(Object *obj) #endif } -/* -cpu max: if KVM is enabled, like -cpu host (best possible with this host); - * otherwise, a CPU with as many features enabled as our emulation supports. - * The version of '-cpu max' for qemu-system-arm is defined in cpu.c; - * this only needs to handle 64 bits. - */ static void aarch64_max_initfn(Object *obj) { - ARMCPU *cpu = ARM_CPU(obj); - uint64_t t; - uint32_t u; - if (kvm_enabled() || hvf_enabled()) { /* With KVM or HVF, '-cpu max' is identical to '-cpu host' */ aarch64_host_initfn(obj); return; } + if (tcg_enabled() || qtest_enabled()) { + aarch64_a57_initfn(obj); + } + /* '-cpu max' for TCG: we currently do this as "A57 with extra things" */ - - aarch64_a57_initfn(obj); - - /* - * Reset MIDR so the guest doesn't mistake our 'max' CPU type for a real - * one and try to apply errata workarounds or use impdef features we - * don't provide. - * An IMPLEMENTER field of 0 means "reserved for software use"; - * ARCHITECTURE must be 0xf indicating "v7 or later, check ID registers - * to see which features are present"; - * the VARIANT, PARTNUM and REVISION fields are all implementation - * defined and we choose to define PARTNUM just in case guest - * code needs to distinguish this QEMU CPU from other software - * implementations, though this shouldn't be needed. - */ - t = FIELD_DP64(0, MIDR_EL1, IMPLEMENTER, 0); - t = FIELD_DP64(t, MIDR_EL1, ARCHITECTURE, 0xf); - t = FIELD_DP64(t, MIDR_EL1, PARTNUM, 'Q'); - t = FIELD_DP64(t, MIDR_EL1, VARIANT, 0); - t = FIELD_DP64(t, MIDR_EL1, REVISION, 0); - cpu->midr = t; - - /* - * We're going to set FEAT_S2FWB, which mandates that CLIDR_EL1.{LoUU,LoUIS} - * are zero. - */ - u = cpu->clidr; - u = FIELD_DP32(u, CLIDR_EL1, LOUIS, 0); - u = FIELD_DP32(u, CLIDR_EL1, LOUU, 0); - cpu->clidr = u; - - t = cpu->isar.id_aa64isar0; - t = FIELD_DP64(t, ID_AA64ISAR0, AES, 2); /* FEAT_PMULL */ - t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* FEAT_SHA1 */ - t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 2); /* FEAT_SHA512 */ - t = FIELD_DP64(t, ID_AA64ISAR0, CRC32, 1); - t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 2); /* FEAT_LSE */ - t = FIELD_DP64(t, ID_AA64ISAR0, RDM, 1); /* FEAT_RDM */ - t = FIELD_DP64(t, ID_AA64ISAR0, SHA3, 1); /* FEAT_SHA3 */ - t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 1); /* FEAT_SM3 */ - t = FIELD_DP64(t, ID_AA64ISAR0, SM4, 1); /* FEAT_SM4 */ - t = FIELD_DP64(t, ID_AA64ISAR0, DP, 1); /* FEAT_DotProd */ - t = FIELD_DP64(t, ID_AA64ISAR0, FHM, 1); /* FEAT_FHM */ - t = FIELD_DP64(t, ID_AA64ISAR0, TS, 2); /* FEAT_FlagM2 */ - t = FIELD_DP64(t, ID_AA64ISAR0, TLB, 2); /* FEAT_TLBIRANGE */ - t = FIELD_DP64(t, ID_AA64ISAR0, RNDR, 1); /* FEAT_RNG */ - cpu->isar.id_aa64isar0 = t; - - t = cpu->isar.id_aa64isar1; - t = FIELD_DP64(t, ID_AA64ISAR1, DPB, 2); /* FEAT_DPB2 */ - t = FIELD_DP64(t, ID_AA64ISAR1, JSCVT, 1); /* FEAT_JSCVT */ - t = FIELD_DP64(t, ID_AA64ISAR1, FCMA, 1); /* FEAT_FCMA */ - t = FIELD_DP64(t, ID_AA64ISAR1, LRCPC, 2); /* FEAT_LRCPC2 */ - t = FIELD_DP64(t, ID_AA64ISAR1, FRINTTS, 1); /* FEAT_FRINTTS */ - t = FIELD_DP64(t, ID_AA64ISAR1, SB, 1); /* FEAT_SB */ - t = FIELD_DP64(t, ID_AA64ISAR1, SPECRES, 1); /* FEAT_SPECRES */ - t = FIELD_DP64(t, ID_AA64ISAR1, BF16, 1); /* FEAT_BF16 */ - t = FIELD_DP64(t, ID_AA64ISAR1, DGH, 1); /* FEAT_DGH */ - t = FIELD_DP64(t, ID_AA64ISAR1, I8MM, 1); /* FEAT_I8MM */ - cpu->isar.id_aa64isar1 = t; - - t = cpu->isar.id_aa64pfr0; - t = FIELD_DP64(t, ID_AA64PFR0, FP, 1); /* FEAT_FP16 */ - t = FIELD_DP64(t, ID_AA64PFR0, ADVSIMD, 1); /* FEAT_FP16 */ - t = FIELD_DP64(t, ID_AA64PFR0, RAS, 2); /* FEAT_RASv1p1 + FEAT_DoubleFault */ - t = FIELD_DP64(t, ID_AA64PFR0, SVE, 1); - t = FIELD_DP64(t, ID_AA64PFR0, SEL2, 1); /* FEAT_SEL2 */ - t = FIELD_DP64(t, ID_AA64PFR0, DIT, 1); /* FEAT_DIT */ - t = FIELD_DP64(t, ID_AA64PFR0, CSV2, 2); /* FEAT_CSV2_2 */ - t = FIELD_DP64(t, ID_AA64PFR0, CSV3, 1); /* FEAT_CSV3 */ - cpu->isar.id_aa64pfr0 = t; - - t = cpu->isar.id_aa64pfr1; - t = FIELD_DP64(t, ID_AA64PFR1, BT, 1); /* FEAT_BTI */ - t = FIELD_DP64(t, ID_AA64PFR1, SSBS, 2); /* FEAT_SSBS2 */ - /* - * Begin with full support for MTE. This will be downgraded to MTE=0 - * during realize if the board provides no tag memory, much like - * we do for EL2 with the virtualization=on property. - */ - t = FIELD_DP64(t, ID_AA64PFR1, MTE, 3); /* FEAT_MTE3 */ - t = FIELD_DP64(t, ID_AA64PFR1, RAS_FRAC, 0); /* FEAT_RASv1p1 + FEAT_DoubleFault */ - t = FIELD_DP64(t, ID_AA64PFR1, CSV2_FRAC, 0); /* FEAT_CSV2_2 */ - cpu->isar.id_aa64pfr1 = t; - - t = cpu->isar.id_aa64mmfr0; - t = FIELD_DP64(t, ID_AA64MMFR0, PARANGE, 6); /* FEAT_LPA: 52 bits */ - t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16, 1); /* 16k pages supported */ - t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16_2, 2); /* 16k stage2 supported */ - t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN64_2, 2); /* 64k stage2 supported */ - t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN4_2, 2); /* 4k stage2 supported */ - cpu->isar.id_aa64mmfr0 = t; - - t = cpu->isar.id_aa64mmfr1; - t = FIELD_DP64(t, ID_AA64MMFR1, VMIDBITS, 2); /* FEAT_VMID16 */ - t = FIELD_DP64(t, ID_AA64MMFR1, VH, 1); /* FEAT_VHE */ - t = FIELD_DP64(t, ID_AA64MMFR1, HPDS, 1); /* FEAT_HPDS */ - t = FIELD_DP64(t, ID_AA64MMFR1, LO, 1); /* FEAT_LOR */ - t = FIELD_DP64(t, ID_AA64MMFR1, PAN, 2); /* FEAT_PAN2 */ - t = FIELD_DP64(t, ID_AA64MMFR1, XNX, 1); /* FEAT_XNX */ - t = FIELD_DP64(t, ID_AA64MMFR1, HCX, 1); /* FEAT_HCX */ - cpu->isar.id_aa64mmfr1 = t; - - t = cpu->isar.id_aa64mmfr2; - t = FIELD_DP64(t, ID_AA64MMFR2, CNP, 1); /* FEAT_TTCNP */ - t = FIELD_DP64(t, ID_AA64MMFR2, UAO, 1); /* FEAT_UAO */ - t = FIELD_DP64(t, ID_AA64MMFR2, IESB, 1); /* FEAT_IESB */ - t = FIELD_DP64(t, ID_AA64MMFR2, VARANGE, 1); /* FEAT_LVA */ - t = FIELD_DP64(t, ID_AA64MMFR2, ST, 1); /* FEAT_TTST */ - t = FIELD_DP64(t, ID_AA64MMFR2, IDS, 1); /* FEAT_IDST */ - t = FIELD_DP64(t, ID_AA64MMFR2, FWB, 1); /* FEAT_S2FWB */ - t = FIELD_DP64(t, ID_AA64MMFR2, TTL, 1); /* FEAT_TTL */ - t = FIELD_DP64(t, ID_AA64MMFR2, BBM, 2); /* FEAT_BBM at level 2 */ - cpu->isar.id_aa64mmfr2 = t; - - t = cpu->isar.id_aa64zfr0; - t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 1); - t = FIELD_DP64(t, ID_AA64ZFR0, AES, 2); /* FEAT_SVE_PMULL128 */ - t = FIELD_DP64(t, ID_AA64ZFR0, BITPERM, 1); /* FEAT_SVE_BitPerm */ - t = FIELD_DP64(t, ID_AA64ZFR0, BFLOAT16, 1); /* FEAT_BF16 */ - t = FIELD_DP64(t, ID_AA64ZFR0, SHA3, 1); /* FEAT_SVE_SHA3 */ - t = FIELD_DP64(t, ID_AA64ZFR0, SM4, 1); /* FEAT_SVE_SM4 */ - t = FIELD_DP64(t, ID_AA64ZFR0, I8MM, 1); /* FEAT_I8MM */ - t = FIELD_DP64(t, ID_AA64ZFR0, F32MM, 1); /* FEAT_F32MM */ - t = FIELD_DP64(t, ID_AA64ZFR0, F64MM, 1); /* FEAT_F64MM */ - cpu->isar.id_aa64zfr0 = t; - - t = cpu->isar.id_aa64dfr0; - t = FIELD_DP64(t, ID_AA64DFR0, DEBUGVER, 9); /* FEAT_Debugv8p4 */ - t = FIELD_DP64(t, ID_AA64DFR0, PMUVER, 5); /* FEAT_PMUv3p4 */ - cpu->isar.id_aa64dfr0 = t; - - /* Replicate the same data to the 32-bit id registers. */ - aa32_max_features(cpu); - -#ifdef CONFIG_USER_ONLY - /* - * For usermode -cpu max we can use a larger and more efficient DCZ - * blocksize since we don't have to follow what the hardware does. - */ - cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */ - cpu->dcz_blocksize = 7; /* 512 bytes */ -#endif - - cpu->sve_vq_supported = MAKE_64BIT_MASK(0, ARM_MAX_VQ); - - aarch64_add_pauth_properties(obj); - aarch64_add_sve_properties(obj); - object_property_add(obj, "sve-max-vq", "uint32", cpu_max_get_sve_max_vq, - cpu_max_set_sve_max_vq, NULL, NULL); - qdev_property_add_static(DEVICE(obj), &arm_cpu_lpa2_property); -} - -static void aarch64_a64fx_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,a64fx"; - set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); - set_feature(&cpu->env, ARM_FEATURE_AARCH64); - set_feature(&cpu->env, ARM_FEATURE_EL2); - set_feature(&cpu->env, ARM_FEATURE_EL3); - set_feature(&cpu->env, ARM_FEATURE_PMU); - cpu->midr = 0x461f0010; - cpu->revidr = 0x00000000; - cpu->ctr = 0x86668006; - cpu->reset_sctlr = 0x30000180; - cpu->isar.id_aa64pfr0 = 0x0000000101111111; /* No RAS Extensions */ - cpu->isar.id_aa64pfr1 = 0x0000000000000000; - cpu->isar.id_aa64dfr0 = 0x0000000010305408; - cpu->isar.id_aa64dfr1 = 0x0000000000000000; - cpu->id_aa64afr0 = 0x0000000000000000; - cpu->id_aa64afr1 = 0x0000000000000000; - cpu->isar.id_aa64mmfr0 = 0x0000000000001122; - cpu->isar.id_aa64mmfr1 = 0x0000000011212100; - cpu->isar.id_aa64mmfr2 = 0x0000000000001011; - cpu->isar.id_aa64isar0 = 0x0000000010211120; - cpu->isar.id_aa64isar1 = 0x0000000000010001; - cpu->isar.id_aa64zfr0 = 0x0000000000000000; - cpu->clidr = 0x0000000080000023; - cpu->ccsidr[0] = 0x7007e01c; /* 64KB L1 dcache */ - cpu->ccsidr[1] = 0x2007e01c; /* 64KB L1 icache */ - cpu->ccsidr[2] = 0x70ffe07c; /* 8MB L2 cache */ - cpu->dcz_blocksize = 6; /* 256 bytes */ - cpu->gic_num_lrs = 4; - cpu->gic_vpribits = 5; - cpu->gic_vprebits = 5; - cpu->gic_pribits = 5; - - /* The A64FX supports only 128, 256 and 512 bit vector lengths */ - aarch64_add_sve_properties(obj); - cpu->sve_vq_supported = (1 << 0) /* 128bit */ - | (1 << 1) /* 256bit */ - | (1 << 3); /* 512bit */ - - cpu->isar.reset_pmcr_el0 = 0x46014040; - - /* TODO: Add A64FX specific HPC extension registers */ + if (tcg_enabled()) { + aarch64_max_tcg_initfn(obj); + } } static const ARMCPUInfo aarch64_cpus[] = { { .name = "cortex-a57", .initfn = aarch64_a57_initfn }, { .name = "cortex-a53", .initfn = aarch64_a53_initfn }, - { .name = "cortex-a72", .initfn = aarch64_a72_initfn }, - { .name = "cortex-a76", .initfn = aarch64_a76_initfn }, - { .name = "a64fx", .initfn = aarch64_a64fx_initfn }, - { .name = "neoverse-n1", .initfn = aarch64_neoverse_n1_initfn }, { .name = "max", .initfn = aarch64_max_initfn }, #if defined(CONFIG_KVM) || defined(CONFIG_HVF) { .name = "host", .initfn = aarch64_host_initfn }, @@ -1078,9 +782,9 @@ static void aarch64_cpu_finalizefn(Object *obj) { } -static gchar *aarch64_gdb_arch_name(CPUState *cs) +static const gchar *aarch64_gdb_arch_name(CPUState *cs) { - return g_strdup("aarch64"); + return "aarch64"; } static void aarch64_cpu_class_init(ObjectClass *oc, void *data) @@ -1089,7 +793,6 @@ static void aarch64_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_read_register = aarch64_cpu_gdb_read_register; cc->gdb_write_register = aarch64_cpu_gdb_write_register; - cc->gdb_num_core_regs = 34; cc->gdb_core_xml_file = "aarch64-core.xml"; cc->gdb_arch_name = aarch64_gdb_arch_name; @@ -1119,9 +822,7 @@ void aarch64_cpu_register(const ARMCPUInfo *info) { TypeInfo type_info = { .parent = TYPE_AARCH64_CPU, - .instance_size = sizeof(ARMCPU), .instance_init = aarch64_cpu_instance_init, - .class_size = sizeof(ARMCPUClass), .class_init = info->class_init ?: cpu_register_class_init, .class_data = (void *)info, }; @@ -1134,10 +835,8 @@ void aarch64_cpu_register(const ARMCPUInfo *info) static const TypeInfo aarch64_cpu_type_info = { .name = TYPE_AARCH64_CPU, .parent = TYPE_ARM_CPU, - .instance_size = sizeof(ARMCPU), .instance_finalize = aarch64_cpu_finalizefn, .abstract = true, - .class_size = sizeof(AArch64CPUClass), .class_init = aarch64_cpu_class_init, }; diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index 46893697cc..7d856acddf 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -6,10 +6,168 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "cpu.h" #include "internals.h" +#include "cpu-features.h" +#include "cpregs.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" +#include "sysemu/tcg.h" + +#ifdef CONFIG_TCG +/* Return the Exception Level targeted by debug exceptions. */ +static int arm_debug_target_el(CPUARMState *env) +{ + bool secure = arm_is_secure(env); + bool route_to_el2 = false; + + if (arm_feature(env, ARM_FEATURE_M)) { + return 1; + } + + if (arm_is_el2_enabled(env)) { + route_to_el2 = env->cp15.hcr_el2 & HCR_TGE || + env->cp15.mdcr_el2 & MDCR_TDE; + } + + if (route_to_el2) { + return 2; + } else if (arm_feature(env, ARM_FEATURE_EL3) && + !arm_el_is_aa64(env, 3) && secure) { + return 3; + } else { + return 1; + } +} + +/* + * Raise an exception to the debug target el. + * Modify syndrome to indicate when origin and target EL are the same. + */ +G_NORETURN static void +raise_exception_debug(CPUARMState *env, uint32_t excp, uint32_t syndrome) +{ + int debug_el = arm_debug_target_el(env); + int cur_el = arm_current_el(env); + + /* + * If singlestep is targeting a lower EL than the current one, then + * DisasContext.ss_active must be false and we can never get here. + * Similarly for watchpoint and breakpoint matches. + */ + assert(debug_el >= cur_el); + syndrome |= (debug_el == cur_el) << ARM_EL_EC_SHIFT; + raise_exception(env, excp, syndrome, debug_el); +} + +/* See AArch64.GenerateDebugExceptionsFrom() in ARM ARM pseudocode */ +static bool aa64_generate_debug_exceptions(CPUARMState *env) +{ + int cur_el = arm_current_el(env); + int debug_el; + + if (cur_el == 3) { + return false; + } + + /* MDCR_EL3.SDD disables debug events from Secure state */ + if (arm_is_secure_below_el3(env) + && extract32(env->cp15.mdcr_el3, 16, 1)) { + return false; + } + + /* + * Same EL to same EL debug exceptions need MDSCR_KDE enabled + * while not masking the (D)ebug bit in DAIF. + */ + debug_el = arm_debug_target_el(env); + + if (cur_el == debug_el) { + return extract32(env->cp15.mdscr_el1, 13, 1) + && !(env->daif & PSTATE_D); + } + + /* Otherwise the debug target needs to be a higher EL */ + return debug_el > cur_el; +} + +static bool aa32_generate_debug_exceptions(CPUARMState *env) +{ + int el = arm_current_el(env); + + if (el == 0 && arm_el_is_aa64(env, 1)) { + return aa64_generate_debug_exceptions(env); + } + + if (arm_is_secure(env)) { + int spd; + + if (el == 0 && (env->cp15.sder & 1)) { + /* + * SDER.SUIDEN means debug exceptions from Secure EL0 + * are always enabled. Otherwise they are controlled by + * SDCR.SPD like those from other Secure ELs. + */ + return true; + } + + spd = extract32(env->cp15.mdcr_el3, 14, 2); + switch (spd) { + case 1: + /* SPD == 0b01 is reserved, but behaves as 0b00. */ + case 0: + /* + * For 0b00 we return true if external secure invasive debug + * is enabled. On real hardware this is controlled by external + * signals to the core. QEMU always permits debug, and behaves + * as if DBGEN, SPIDEN, NIDEN and SPNIDEN are all tied high. + */ + return true; + case 2: + return false; + case 3: + return true; + } + } + + return el != 2; +} + +/* + * Return true if debugging exceptions are currently enabled. + * This corresponds to what in ARM ARM pseudocode would be + * if UsingAArch32() then + * return AArch32.GenerateDebugExceptions() + * else + * return AArch64.GenerateDebugExceptions() + * We choose to push the if() down into this function for clarity, + * since the pseudocode has it at all callsites except for the one in + * CheckSoftwareStep(), where it is elided because both branches would + * always return the same value. + */ +bool arm_generate_debug_exceptions(CPUARMState *env) +{ + if ((env->cp15.oslsr_el1 & 1) || (env->cp15.osdlr_el1 & 1)) { + return false; + } + if (is_a64(env)) { + return aa64_generate_debug_exceptions(env); + } else { + return aa32_generate_debug_exceptions(env); + } +} + +/* + * Is single-stepping active? (Note that the "is EL_D AArch64?" check + * implicitly means this always returns false in pre-v8 CPUs.) + */ +bool arm_singlestep_active(CPUARMState *env) +{ + return extract32(env->cp15.mdscr_el1, 0, 1) + && arm_el_is_aa64(env, arm_debug_target_el(env)) + && arm_generate_debug_exceptions(env); +} /* Return true if the linked breakpoint entry lbn passes its checks */ static bool linked_bp_matches(ARMCPU *cpu, int lbn) @@ -273,6 +431,37 @@ bool arm_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) return check_watchpoints(cpu); } +/* + * Return the FSR value for a debug exception (watchpoint, hardware + * breakpoint or BKPT insn) targeting the specified exception level. + */ +static uint32_t arm_debug_exception_fsr(CPUARMState *env) +{ + ARMMMUFaultInfo fi = { .type = ARMFault_Debug }; + int target_el = arm_debug_target_el(env); + bool using_lpae; + + if (arm_feature(env, ARM_FEATURE_M)) { + using_lpae = false; + } else if (target_el == 2 || arm_el_is_aa64(env, target_el)) { + using_lpae = true; + } else if (arm_feature(env, ARM_FEATURE_PMSA) && + arm_feature(env, ARM_FEATURE_V8)) { + using_lpae = true; + } else if (arm_feature(env, ARM_FEATURE_LPAE) && + (env->cp15.tcr_el[target_el] & TTBCR_EAE)) { + using_lpae = true; + } else { + using_lpae = false; + } + + if (using_lpae) { + return arm_fi_to_lfsc(&fi); + } else { + return arm_fi_to_sfsc(&fi); + } +} + void arm_debug_excp_handler(CPUState *cs) { /* @@ -286,19 +475,16 @@ void arm_debug_excp_handler(CPUState *cs) if (wp_hit) { if (wp_hit->flags & BP_CPU) { bool wnr = (wp_hit->flags & BP_WATCHPOINT_HIT_WRITE) != 0; - bool same_el = arm_debug_target_el(env) == arm_current_el(env); cs->watchpoint_hit = NULL; env->exception.fsr = arm_debug_exception_fsr(env); env->exception.vaddress = wp_hit->hitaddr; - raise_exception(env, EXCP_DATA_ABORT, - syn_watchpoint(same_el, 0, wnr), - arm_debug_target_el(env)); + raise_exception_debug(env, EXCP_DATA_ABORT, + syn_watchpoint(0, 0, wnr)); } } else { uint64_t pc = is_a64(env) ? env->pc : env->regs[15]; - bool same_el = (arm_debug_target_el(env) == arm_current_el(env)); /* * (1) GDB breakpoints should be handled first. @@ -318,9 +504,252 @@ void arm_debug_excp_handler(CPUState *cs) * exception/security level. */ env->exception.vaddress = 0; - raise_exception(env, EXCP_PREFETCH_ABORT, - syn_breakpoint(same_el), - arm_debug_target_el(env)); + raise_exception_debug(env, EXCP_PREFETCH_ABORT, syn_breakpoint(0)); + } +} + +/* + * Raise an EXCP_BKPT with the specified syndrome register value, + * targeting the correct exception level for debug exceptions. + */ +void HELPER(exception_bkpt_insn)(CPUARMState *env, uint32_t syndrome) +{ + int debug_el = arm_debug_target_el(env); + int cur_el = arm_current_el(env); + + /* FSR will only be used if the debug target EL is AArch32. */ + env->exception.fsr = arm_debug_exception_fsr(env); + /* + * FAR is UNKNOWN: clear vaddress to avoid potentially exposing + * values to the guest that it shouldn't be able to see at its + * exception/security level. + */ + env->exception.vaddress = 0; + /* + * Other kinds of architectural debug exception are ignored if + * they target an exception level below the current one (in QEMU + * this is checked by arm_generate_debug_exceptions()). Breakpoint + * instructions are special because they always generate an exception + * to somewhere: if they can't go to the configured debug exception + * level they are taken to the current exception level. + */ + if (debug_el < cur_el) { + debug_el = cur_el; + } + raise_exception(env, EXCP_BKPT, syndrome, debug_el); +} + +void HELPER(exception_swstep)(CPUARMState *env, uint32_t syndrome) +{ + raise_exception_debug(env, EXCP_UDEF, syndrome); +} + +void hw_watchpoint_update(ARMCPU *cpu, int n) +{ + CPUARMState *env = &cpu->env; + vaddr len = 0; + vaddr wvr = env->cp15.dbgwvr[n]; + uint64_t wcr = env->cp15.dbgwcr[n]; + int mask; + int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; + + if (env->cpu_watchpoint[n]) { + cpu_watchpoint_remove_by_ref(CPU(cpu), env->cpu_watchpoint[n]); + env->cpu_watchpoint[n] = NULL; + } + + if (!FIELD_EX64(wcr, DBGWCR, E)) { + /* E bit clear : watchpoint disabled */ + return; + } + + switch (FIELD_EX64(wcr, DBGWCR, LSC)) { + case 0: + /* LSC 00 is reserved and must behave as if the wp is disabled */ + return; + case 1: + flags |= BP_MEM_READ; + break; + case 2: + flags |= BP_MEM_WRITE; + break; + case 3: + flags |= BP_MEM_ACCESS; + break; + } + + /* + * Attempts to use both MASK and BAS fields simultaneously are + * CONSTRAINED UNPREDICTABLE; we opt to ignore BAS in this case, + * thus generating a watchpoint for every byte in the masked region. + */ + mask = FIELD_EX64(wcr, DBGWCR, MASK); + if (mask == 1 || mask == 2) { + /* + * Reserved values of MASK; we must act as if the mask value was + * some non-reserved value, or as if the watchpoint were disabled. + * We choose the latter. + */ + return; + } else if (mask) { + /* Watchpoint covers an aligned area up to 2GB in size */ + len = 1ULL << mask; + /* + * If masked bits in WVR are not zero it's CONSTRAINED UNPREDICTABLE + * whether the watchpoint fires when the unmasked bits match; we opt + * to generate the exceptions. + */ + wvr &= ~(len - 1); + } else { + /* Watchpoint covers bytes defined by the byte address select bits */ + int bas = FIELD_EX64(wcr, DBGWCR, BAS); + int basstart; + + 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. + * We choose to ignore any non-zero bits after the first range of 1s. + */ + basstart = ctz32(bas); + len = cto32(bas >> basstart); + wvr += basstart; + } + + cpu_watchpoint_insert(CPU(cpu), wvr, len, flags, + &env->cpu_watchpoint[n]); +} + +void hw_watchpoint_update_all(ARMCPU *cpu) +{ + int i; + CPUARMState *env = &cpu->env; + + /* + * Completely clear out existing QEMU watchpoints and our array, to + * avoid possible stale entries following migration load. + */ + cpu_watchpoint_remove_all(CPU(cpu), BP_CPU); + memset(env->cpu_watchpoint, 0, sizeof(env->cpu_watchpoint)); + + for (i = 0; i < ARRAY_SIZE(cpu->env.cpu_watchpoint); i++) { + hw_watchpoint_update(cpu, i); + } +} + +void hw_breakpoint_update(ARMCPU *cpu, int n) +{ + CPUARMState *env = &cpu->env; + uint64_t bvr = env->cp15.dbgbvr[n]; + uint64_t bcr = env->cp15.dbgbcr[n]; + vaddr addr; + int bt; + int flags = BP_CPU; + + if (env->cpu_breakpoint[n]) { + cpu_breakpoint_remove_by_ref(CPU(cpu), env->cpu_breakpoint[n]); + env->cpu_breakpoint[n] = NULL; + } + + if (!extract64(bcr, 0, 1)) { + /* E bit clear : watchpoint disabled */ + return; + } + + bt = extract64(bcr, 20, 4); + + switch (bt) { + case 4: /* unlinked address mismatch (reserved if AArch64) */ + case 5: /* linked address mismatch (reserved if AArch64) */ + qemu_log_mask(LOG_UNIMP, + "arm: address mismatch breakpoint types not implemented\n"); + return; + case 0: /* unlinked address match */ + case 1: /* linked address match */ + { + /* + * Bits [1:0] are RES0. + * + * It is IMPLEMENTATION DEFINED whether bits [63:49] + * ([63:53] for FEAT_LVA) are hardwired to a copy of the sign bit + * of the VA field ([48] or [52] for FEAT_LVA), or whether the + * value is read as written. It is CONSTRAINED UNPREDICTABLE + * whether the RESS bits are ignored when comparing an address. + * Therefore we are allowed to compare the entire register, which + * lets us avoid considering whether FEAT_LVA is actually enabled. + * + * The BAS field is used to allow setting breakpoints on 16-bit + * wide instructions; it is CONSTRAINED UNPREDICTABLE whether + * a bp will fire if the addresses covered by the bp and the addresses + * covered by the insn overlap but the insn doesn't start at the + * start of the bp address range. We choose to require the insn and + * the bp to have the same address. The constraints on writing to + * BAS enforced in dbgbcr_write mean we have only four cases: + * 0b0000 => no breakpoint + * 0b0011 => breakpoint on addr + * 0b1100 => breakpoint on addr + 2 + * 0b1111 => breakpoint on addr + * See also figure D2-3 in the v8 ARM ARM (DDI0487A.c). + */ + int bas = extract64(bcr, 5, 4); + addr = bvr & ~3ULL; + if (bas == 0) { + return; + } + if (bas == 0xc) { + addr += 2; + } + break; + } + case 2: /* unlinked context ID match */ + case 8: /* unlinked VMID match (reserved if no EL2) */ + case 10: /* unlinked context ID and VMID match (reserved if no EL2) */ + qemu_log_mask(LOG_UNIMP, + "arm: unlinked context breakpoint types not implemented\n"); + return; + case 9: /* linked VMID match (reserved if no EL2) */ + case 11: /* linked context ID and VMID match (reserved if no EL2) */ + case 3: /* linked context ID match */ + default: + /* + * We must generate no events for Linked context matches (unless + * they are linked to by some other bp/wp, which is handled in + * updates for the linking bp/wp). We choose to also generate no events + * for reserved values. + */ + return; + } + + cpu_breakpoint_insert(CPU(cpu), addr, flags, &env->cpu_breakpoint[n]); +} + +void hw_breakpoint_update_all(ARMCPU *cpu) +{ + int i; + CPUARMState *env = &cpu->env; + + /* + * Completely clear out existing QEMU breakpoints and our array, to + * avoid possible stale entries following migration load. + */ + cpu_breakpoint_remove_all(CPU(cpu), BP_CPU); + memset(env->cpu_breakpoint, 0, sizeof(env->cpu_breakpoint)); + + for (i = 0; i < ARRAY_SIZE(cpu->env.cpu_breakpoint); i++) { + hw_breakpoint_update(cpu, i); } } @@ -349,4 +778,503 @@ vaddr arm_adjust_watchpoint_address(CPUState *cs, vaddr addr, int len) return addr; } -#endif +#endif /* !CONFIG_USER_ONLY */ +#endif /* CONFIG_TCG */ + +/* + * Check for traps to "powerdown debug" registers, which are controlled + * by MDCR.TDOSA + */ +static CPAccessResult access_tdosa(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); + bool mdcr_el2_tdosa = (mdcr_el2 & MDCR_TDOSA) || (mdcr_el2 & MDCR_TDE) || + (arm_hcr_el2_eff(env) & HCR_TGE); + + if (el < 2 && mdcr_el2_tdosa) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDOSA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +/* + * Check for traps to "debug ROM" registers, which are controlled + * by MDCR_EL2.TDRA for EL2 but by the more general MDCR_EL3.TDA for EL3. + */ +static CPAccessResult access_tdra(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); + bool mdcr_el2_tdra = (mdcr_el2 & MDCR_TDRA) || (mdcr_el2 & MDCR_TDE) || + (arm_hcr_el2_eff(env) & HCR_TGE); + + if (el < 2 && mdcr_el2_tdra) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +/* + * Check for traps to general debug registers, which are controlled + * by MDCR_EL2.TDA for EL2 and MDCR_EL3.TDA for EL3. + */ +static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); + bool mdcr_el2_tda = (mdcr_el2 & MDCR_TDA) || (mdcr_el2 & MDCR_TDE) || + (arm_hcr_el2_eff(env) & HCR_TGE); + + if (el < 2 && mdcr_el2_tda) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static CPAccessResult access_dbgvcr32(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* MCDR_EL3.TDMA doesn't apply for FEAT_NV traps */ + if (arm_current_el(env) == 2 && (env->cp15.mdcr_el3 & MDCR_TDA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +/* + * Check for traps to Debug Comms Channel registers. If FEAT_FGT + * is implemented then these are controlled by MDCR_EL2.TDCC for + * EL2 and MDCR_EL3.TDCC for EL3. They are also controlled by + * the general debug access trap bits MDCR_EL2.TDA and MDCR_EL3.TDA. + * For EL0, they are also controlled by MDSCR_EL1.TDCC. + */ +static CPAccessResult access_tdcc(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); + bool mdscr_el1_tdcc = extract32(env->cp15.mdscr_el1, 12, 1); + bool mdcr_el2_tda = (mdcr_el2 & MDCR_TDA) || (mdcr_el2 & MDCR_TDE) || + (arm_hcr_el2_eff(env) & HCR_TGE); + bool mdcr_el2_tdcc = cpu_isar_feature(aa64_fgt, env_archcpu(env)) && + (mdcr_el2 & MDCR_TDCC); + bool mdcr_el3_tdcc = cpu_isar_feature(aa64_fgt, env_archcpu(env)) && + (env->cp15.mdcr_el3 & MDCR_TDCC); + + if (el < 1 && mdscr_el1_tdcc) { + return CP_ACCESS_TRAP; + } + if (el < 2 && (mdcr_el2_tda || mdcr_el2_tdcc)) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && ((env->cp15.mdcr_el3 & MDCR_TDA) || mdcr_el3_tdcc)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static void oslar_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Writes to OSLAR_EL1 may update the OS lock status, which can be + * read via a bit in OSLSR_EL1. + */ + int oslock; + + if (ri->state == ARM_CP_STATE_AA32) { + oslock = (value == 0xC5ACCE55); + } else { + oslock = value & 1; + } + + env->cp15.oslsr_el1 = deposit32(env->cp15.oslsr_el1, 1, 1, oslock); +} + +static void osdlr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + /* + * Only defined bit is bit 0 (DLK); if Feat_DoubleLock is not + * implemented this is RAZ/WI. + */ + if(arm_feature(env, ARM_FEATURE_AARCH64) + ? cpu_isar_feature(aa64_doublelock, cpu) + : cpu_isar_feature(aa32_doublelock, cpu)) { + env->cp15.osdlr_el1 = value & 1; + } +} + +static void dbgclaimset_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->cp15.dbgclaim |= (value & 0xFF); +} + +static uint64_t dbgclaimset_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* CLAIM bits are RAO */ + return 0xFF; +} + +static void dbgclaimclr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->cp15.dbgclaim &= ~(value & 0xFF); +} + +static const ARMCPRegInfo debug_cp_reginfo[] = { + /* + * DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped + * debug components. The AArch64 version of DBGDRAR is named MDRAR_EL1; + * unlike DBGDRAR it is never accessible from EL0. + * DBGDSAR is deprecated and must RAZ from v8 anyway, so it has no AArch64 + * accessor. + */ + { .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL0_R, .accessfn = access_tdra, + .type = ARM_CP_CONST | ARM_CP_NO_GDB, .resetvalue = 0 }, + { .name = "MDRAR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, + .access = PL1_R, .accessfn = access_tdra, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "DBGDSAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL0_R, .accessfn = access_tdra, + .type = ARM_CP_CONST | ARM_CP_NO_GDB, .resetvalue = 0 }, + /* Monitor debug system control register; the 32-bit alias is DBGDSCRext. */ + { .name = "MDSCR_EL1", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, + .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_MDSCR_EL1, + .nv2_redirect_offset = 0x158, + .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), + .resetvalue = 0 }, + /* + * MDCCSR_EL0[30:29] map to EDSCR[30:29]. Simply RAZ as the external + * Debug Communication Channel is not implemented. + */ + { .name = "MDCCSR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 1, .opc2 = 0, + .access = PL0_R, .accessfn = access_tdcc, + .type = ARM_CP_CONST, .resetvalue = 0 }, + /* + * These registers belong to the Debug Communications Channel, + * which is not implemented. However we implement RAZ/WI behaviour + * with trapping to prevent spurious SIGILLs if the guest OS does + * access them as the support cannot be probed for. + */ + { .name = "OSDTRRX_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, + .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 2, + .access = PL1_RW, .accessfn = access_tdcc, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "OSDTRTX_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, + .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2, + .access = PL1_RW, .accessfn = access_tdcc, + .type = ARM_CP_CONST, .resetvalue = 0 }, + /* DBGDTRTX_EL0/DBGDTRRX_EL0 depend on direction */ + { .name = "DBGDTR_EL0", .state = ARM_CP_STATE_BOTH, .cp = 14, + .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 5, .opc2 = 0, + .access = PL0_RW, .accessfn = access_tdcc, + .type = ARM_CP_CONST, .resetvalue = 0 }, + /* + * OSECCR_EL1 provides a mechanism for an operating system + * to access the contents of EDECCR. EDECCR is not implemented though, + * as is the rest of external device mechanism. + */ + { .name = "OSECCR_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, + .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 2, + .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_OSECCR_EL1, + .type = ARM_CP_CONST, .resetvalue = 0 }, + /* + * DBGDSCRint[15,12,5:2] map to MDSCR_EL1[15,12,5:2]. Map all bits as + * it is unlikely a guest will care. + * We don't implement the configurable EL0 access. + */ + { .name = "DBGDSCRint", .state = ARM_CP_STATE_AA32, + .cp = 14, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, + .type = ARM_CP_ALIAS, + .access = PL1_R, .accessfn = access_tda, + .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), }, + { .name = "OSLAR_EL1", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4, + .access = PL1_W, .type = ARM_CP_NO_RAW, + .accessfn = access_tdosa, + .fgt = FGT_OSLAR_EL1, + .writefn = oslar_write }, + { .name = "OSLSR_EL1", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 4, + .access = PL1_R, .resetvalue = 10, + .accessfn = access_tdosa, + .fgt = FGT_OSLSR_EL1, + .fieldoffset = offsetof(CPUARMState, cp15.oslsr_el1) }, + /* Dummy OSDLR_EL1: 32-bit Linux will read this */ + { .name = "OSDLR_EL1", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 3, .opc2 = 4, + .access = PL1_RW, .accessfn = access_tdosa, + .fgt = FGT_OSDLR_EL1, + .writefn = osdlr_write, + .fieldoffset = offsetof(CPUARMState, cp15.osdlr_el1) }, + /* + * Dummy DBGVCR: Linux wants to clear this on startup, but we don't + * implement vector catch debug events yet. + */ + { .name = "DBGVCR", + .cp = 14, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0, + .access = PL1_RW, .accessfn = access_tda, + .type = ARM_CP_NOP }, + /* + * Dummy MDCCINT_EL1, since we don't implement the Debug Communications + * Channel but Linux may try to access this register. The 32-bit + * alias is DBGDCCINT. + */ + { .name = "MDCCINT_EL1", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, + .access = PL1_RW, .accessfn = access_tdcc, + .type = ARM_CP_NOP }, + /* + * Dummy DBGCLAIM registers. + * "The architecture does not define any functionality for the CLAIM tag bits.", + * so we only keep the raw bits + */ + { .name = "DBGCLAIMSET_EL1", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 6, + .type = ARM_CP_ALIAS, + .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGCLAIM, + .writefn = dbgclaimset_write, .readfn = dbgclaimset_read }, + { .name = "DBGCLAIMCLR_EL1", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 6, + .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGCLAIM, + .writefn = dbgclaimclr_write, .raw_writefn = raw_write, + .fieldoffset = offsetof(CPUARMState, cp15.dbgclaim) }, +}; + +/* These are present only when EL1 supports AArch32 */ +static const ARMCPRegInfo debug_aa32_el1_reginfo[] = { + /* + * Dummy DBGVCR32_EL2 (which is only for a 64-bit hypervisor + * to save and restore a 32-bit guest's DBGVCR) + */ + { .name = "DBGVCR32_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 4, .crn = 0, .crm = 7, .opc2 = 0, + .access = PL2_RW, .accessfn = access_dbgvcr32, + .type = ARM_CP_NOP | ARM_CP_EL3_NO_EL2_KEEP }, +}; + +static const ARMCPRegInfo debug_lpae_cp_reginfo[] = { + /* 64 bit access versions of the (dummy) debug registers */ + { .name = "DBGDRAR", .cp = 14, .crm = 1, .opc1 = 0, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_64BIT | ARM_CP_NO_GDB, + .resetvalue = 0 }, + { .name = "DBGDSAR", .cp = 14, .crm = 2, .opc1 = 0, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_64BIT | ARM_CP_NO_GDB, + .resetvalue = 0 }, +}; + +static void dbgwvr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + int i = ri->crm; + + /* + * Bits [1:0] are RES0. + * + * It is IMPLEMENTATION DEFINED whether [63:49] ([63:53] with FEAT_LVA) + * are hardwired to the value of bit [48] ([52] with FEAT_LVA), or if + * they contain the value written. It is CONSTRAINED UNPREDICTABLE + * whether the RESS bits are ignored when comparing an address. + * + * Therefore we are allowed to compare the entire register, which lets + * us avoid considering whether or not FEAT_LVA is actually enabled. + */ + value &= ~3ULL; + + raw_write(env, ri, value); + if (tcg_enabled()) { + hw_watchpoint_update(cpu, i); + } +} + +static void dbgwcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + int i = ri->crm; + + raw_write(env, ri, value); + if (tcg_enabled()) { + hw_watchpoint_update(cpu, i); + } +} + +static void dbgbvr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + int i = ri->crm; + + raw_write(env, ri, value); + if (tcg_enabled()) { + hw_breakpoint_update(cpu, i); + } +} + +static void dbgbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + int i = ri->crm; + + /* + * BAS[3] is a read-only copy of BAS[2], and BAS[1] a read-only + * copy of BAS[0]. + */ + value = deposit64(value, 6, 1, extract64(value, 5, 1)); + value = deposit64(value, 8, 1, extract64(value, 7, 1)); + + raw_write(env, ri, value); + if (tcg_enabled()) { + hw_breakpoint_update(cpu, i); + } +} + +void define_debug_regs(ARMCPU *cpu) +{ + /* + * Define v7 and v8 architectural debug registers. + * These are just dummy implementations for now. + */ + int i; + int wrps, brps, ctx_cmps; + + /* + * The Arm ARM says DBGDIDR is optional and deprecated if EL1 cannot + * use AArch32. Given that bit 15 is RES1, if the value is 0 then + * the register must not exist for this cpu. + */ + if (cpu->isar.dbgdidr != 0) { + 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->isar.dbgdidr, + }; + define_one_arm_cp_reg(cpu, &dbgdidr); + } + + /* + * DBGDEVID is present in the v7 debug architecture if + * DBGDIDR.DEVID_imp is 1 (bit 15); from v7.1 and on it is + * mandatory (and bit 15 is RES1). DBGDEVID1 and DBGDEVID2 exist + * from v7.1 of the debug architecture. Because no fields have yet + * been defined in DBGDEVID2 (and quite possibly none will ever + * be) we don't define an ARMISARegisters field for it. + * These registers exist only if EL1 can use AArch32, but that + * happens naturally because they are only PL1 accessible anyway. + */ + if (extract32(cpu->isar.dbgdidr, 15, 1)) { + ARMCPRegInfo dbgdevid = { + .name = "DBGDEVID", + .cp = 14, .opc1 = 0, .crn = 7, .opc2 = 2, .crn = 7, + .access = PL1_R, .accessfn = access_tda, + .type = ARM_CP_CONST, .resetvalue = cpu->isar.dbgdevid, + }; + define_one_arm_cp_reg(cpu, &dbgdevid); + } + if (cpu_isar_feature(aa32_debugv7p1, cpu)) { + ARMCPRegInfo dbgdevid12[] = { + { + .name = "DBGDEVID1", + .cp = 14, .opc1 = 0, .crn = 7, .opc2 = 1, .crn = 7, + .access = PL1_R, .accessfn = access_tda, + .type = ARM_CP_CONST, .resetvalue = cpu->isar.dbgdevid1, + }, { + .name = "DBGDEVID2", + .cp = 14, .opc1 = 0, .crn = 7, .opc2 = 0, .crn = 7, + .access = PL1_R, .accessfn = access_tda, + .type = ARM_CP_CONST, .resetvalue = 0, + }, + }; + define_arm_cp_regs(cpu, dbgdevid12); + } + + brps = arm_num_brps(cpu); + wrps = arm_num_wrps(cpu); + ctx_cmps = arm_num_ctx_cmps(cpu); + + assert(ctx_cmps <= brps); + + define_arm_cp_regs(cpu, debug_cp_reginfo); + if (cpu_isar_feature(aa64_aa32_el1, cpu)) { + define_arm_cp_regs(cpu, debug_aa32_el1_reginfo); + } + + if (arm_feature(&cpu->env, ARM_FEATURE_LPAE)) { + define_arm_cp_regs(cpu, debug_lpae_cp_reginfo); + } + + for (i = 0; i < brps; i++) { + char *dbgbvr_el1_name = g_strdup_printf("DBGBVR%d_EL1", i); + char *dbgbcr_el1_name = g_strdup_printf("DBGBCR%d_EL1", i); + ARMCPRegInfo dbgregs[] = { + { .name = dbgbvr_el1_name, .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 4, + .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGBVRN_EL1, + .fieldoffset = offsetof(CPUARMState, cp15.dbgbvr[i]), + .writefn = dbgbvr_write, .raw_writefn = raw_write + }, + { .name = dbgbcr_el1_name, .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 5, + .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGBCRN_EL1, + .fieldoffset = offsetof(CPUARMState, cp15.dbgbcr[i]), + .writefn = dbgbcr_write, .raw_writefn = raw_write + }, + }; + define_arm_cp_regs(cpu, dbgregs); + g_free(dbgbvr_el1_name); + g_free(dbgbcr_el1_name); + } + + for (i = 0; i < wrps; i++) { + char *dbgwvr_el1_name = g_strdup_printf("DBGWVR%d_EL1", i); + char *dbgwcr_el1_name = g_strdup_printf("DBGWCR%d_EL1", i); + ARMCPRegInfo dbgregs[] = { + { .name = dbgwvr_el1_name, .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6, + .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGWVRN_EL1, + .fieldoffset = offsetof(CPUARMState, cp15.dbgwvr[i]), + .writefn = dbgwvr_write, .raw_writefn = raw_write + }, + { .name = dbgwcr_el1_name, .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 7, + .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGWCRN_EL1, + .fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]), + .writefn = dbgwcr_write, .raw_writefn = raw_write + }, + }; + define_arm_cp_regs(cpu, dbgregs); + g_free(dbgwvr_el1_name); + g_free(dbgwcr_el1_name); + } +} diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index 2f806512d0..a3bb73cfa7 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -20,14 +20,17 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/gdbstub.h" +#include "gdbstub/helpers.h" +#include "sysemu/tcg.h" #include "internals.h" +#include "cpu-features.h" #include "cpregs.h" -typedef struct RegisterSysregXmlParam { +typedef struct RegisterSysregFeatureParam { CPUState *cs; - GString *s; + GDBFeatureBuilder builder; int n; -} RegisterSysregXmlParam; +} RegisterSysregFeatureParam; /* Old gdb always expect FPA registers. Newer (xml-aware) gdb only expect whatever the target description contains. Due to a historical mishap @@ -44,21 +47,7 @@ int arm_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) /* Core integer register. */ return gdb_get_reg32(mem_buf, env->regs[n]); } - if (n < 24) { - /* FPA registers. */ - if (gdb_has_xml) { - return 0; - } - return gdb_get_zeroes(mem_buf, 12); - } - switch (n) { - case 24: - /* FPA status register. */ - if (gdb_has_xml) { - return 0; - } - return gdb_get_reg32(mem_buf, 0); - case 25: + if (n == 25) { /* CPSR, or XPSR for M-profile */ if (arm_feature(env, ARM_FEATURE_M)) { return gdb_get_reg32(mem_buf, xpsr_read(env)); @@ -98,21 +87,7 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->regs[n] = tmp; return 4; } - if (n < 24) { /* 16-23 */ - /* FPA registers (ignored). */ - if (gdb_has_xml) { - return 0; - } - return 12; - } - switch (n) { - case 24: - /* FPA status register (ignored). */ - if (gdb_has_xml) { - return 0; - } - return 4; - case 25: + if (n == 25) { /* CPSR, or XPSR for M-profile */ if (arm_feature(env, ARM_FEATURE_M)) { /* @@ -131,9 +106,10 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } -static int vfp_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) +static int vfp_gdb_get_reg(CPUState *cs, GByteArray *buf, int reg) { - ARMCPU *cpu = env_archcpu(env); + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; int nregs = cpu_isar_feature(aa32_simd_r32, cpu) ? 32 : 16; /* VFP data registers are always little-endian. */ @@ -155,9 +131,10 @@ static int vfp_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) return 0; } -static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) +static int vfp_gdb_set_reg(CPUState *cs, uint8_t *buf, int reg) { - ARMCPU *cpu = env_archcpu(env); + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; int nregs = cpu_isar_feature(aa32_simd_r32, cpu) ? 32 : 16; if (reg < nregs) { @@ -181,8 +158,11 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) return 0; } -static int vfp_gdb_get_sysreg(CPUARMState *env, GByteArray *buf, int reg) +static int vfp_gdb_get_sysreg(CPUState *cs, GByteArray *buf, int reg) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + switch (reg) { case 0: return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPSID]); @@ -192,8 +172,11 @@ static int vfp_gdb_get_sysreg(CPUARMState *env, GByteArray *buf, int reg) return 0; } -static int vfp_gdb_set_sysreg(CPUARMState *env, uint8_t *buf, int reg) +static int vfp_gdb_set_sysreg(CPUState *cs, uint8_t *buf, int reg) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + switch (reg) { case 0: env->vfp.xregs[ARM_VFP_FPSID] = ldl_p(buf); @@ -205,8 +188,11 @@ static int vfp_gdb_set_sysreg(CPUARMState *env, uint8_t *buf, int reg) return 0; } -static int mve_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) +static int mve_gdb_get_reg(CPUState *cs, GByteArray *buf, int reg) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + switch (reg) { case 0: return gdb_get_reg32(buf, env->v7m.vpr); @@ -215,8 +201,11 @@ static int mve_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) } } -static int mve_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) +static int mve_gdb_set_reg(CPUState *cs, uint8_t *buf, int reg) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + switch (reg) { case 0: env->v7m.vpr = ldl_p(buf); @@ -235,13 +224,14 @@ static int mve_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) * We return the number of bytes copied */ -static int arm_gdb_get_sysreg(CPUARMState *env, GByteArray *buf, int reg) +static int arm_gdb_get_sysreg(CPUState *cs, GByteArray *buf, int reg) { - ARMCPU *cpu = env_archcpu(env); + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; const ARMCPRegInfo *ri; uint32_t key; - key = cpu->dyn_sysreg_xml.data.cpregs.keys[reg]; + key = cpu->dyn_sysreg_feature.data.cpregs.keys[reg]; ri = get_arm_cp_reginfo(cpu->cp_regs, key); if (ri) { if (cpreg_field_is_64bit(ri)) { @@ -253,39 +243,37 @@ static int arm_gdb_get_sysreg(CPUARMState *env, GByteArray *buf, int reg) return 0; } -static int arm_gdb_set_sysreg(CPUARMState *env, uint8_t *buf, int reg) +static int arm_gdb_set_sysreg(CPUState *cs, uint8_t *buf, int reg) { return 0; } -static void arm_gen_one_xml_sysreg_tag(GString *s, DynamicGDBXMLInfo *dyn_xml, +static void arm_gen_one_feature_sysreg(GDBFeatureBuilder *builder, + DynamicGDBFeatureInfo *dyn_feature, ARMCPRegInfo *ri, uint32_t ri_key, - int bitsize, int regnum) + int bitsize, int n) { - 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->data.cpregs.keys[dyn_xml->num] = ri_key; - dyn_xml->num++; + gdb_feature_builder_append_reg(builder, ri->name, bitsize, n, + "int", "cp_regs"); + + dyn_feature->data.cpregs.keys[n] = ri_key; } -static void arm_register_sysreg_for_xml(gpointer key, gpointer value, - gpointer p) +static void arm_register_sysreg_for_feature(gpointer key, gpointer value, + gpointer p) { uint32_t ri_key = (uintptr_t)key; ARMCPRegInfo *ri = value; - RegisterSysregXmlParam *param = (RegisterSysregXmlParam *)p; - GString *s = param->s; + RegisterSysregFeatureParam *param = p; ARMCPU *cpu = ARM_CPU(param->cs); CPUARMState *env = &cpu->env; - DynamicGDBXMLInfo *dyn_xml = &cpu->dyn_sysreg_xml; + DynamicGDBFeatureInfo *dyn_feature = &cpu->dyn_sysreg_feature; 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_sysreg_tag(s , dyn_xml, ri, ri_key, 64, - param->n++); + arm_gen_one_feature_sysreg(¶m->builder, dyn_feature, + ri, ri_key, 64, param->n++); } } else { if (ri->state == ARM_CP_STATE_AA32) { @@ -294,166 +282,198 @@ static void arm_register_sysreg_for_xml(gpointer key, gpointer value, return; } if (ri->type & ARM_CP_64BIT) { - arm_gen_one_xml_sysreg_tag(s , dyn_xml, ri, ri_key, 64, - param->n++); + arm_gen_one_feature_sysreg(¶m->builder, dyn_feature, + ri, ri_key, 64, param->n++); } else { - arm_gen_one_xml_sysreg_tag(s , dyn_xml, ri, ri_key, 32, - param->n++); + arm_gen_one_feature_sysreg(¶m->builder, dyn_feature, + ri, ri_key, 32, param->n++); } } } } } -int arm_gen_dynamic_sysreg_xml(CPUState *cs, int base_reg) +static GDBFeature *arm_gen_dynamic_sysreg_feature(CPUState *cs, int base_reg) { ARMCPU *cpu = ARM_CPU(cs); - GString *s = g_string_new(NULL); - RegisterSysregXmlParam param = {cs, s, base_reg}; + RegisterSysregFeatureParam param = {cs}; + gsize num_regs = 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_sysreg_xml.desc = g_string_free(s, false); - return cpu->dyn_sysreg_xml.num; + gdb_feature_builder_init(¶m.builder, + &cpu->dyn_sysreg_feature.desc, + "org.qemu.gdb.arm.sys.regs", + "system-registers.xml", + base_reg); + cpu->dyn_sysreg_feature.data.cpregs.keys = g_new(uint32_t, num_regs); + g_hash_table_foreach(cpu->cp_regs, arm_register_sysreg_for_feature, ¶m); + gdb_feature_builder_end(¶m.builder); + return &cpu->dyn_sysreg_feature.desc; } -struct TypeSize { - const char *gdb_type; - int size; - const char sz, suffix; +#ifdef CONFIG_TCG +typedef enum { + M_SYSREG_MSP, + M_SYSREG_PSP, + M_SYSREG_PRIMASK, + M_SYSREG_CONTROL, + M_SYSREG_BASEPRI, + M_SYSREG_FAULTMASK, + M_SYSREG_MSPLIM, + M_SYSREG_PSPLIM, +} MProfileSysreg; + +static const struct { + const char *name; + int feature; +} m_sysreg_def[] = { + [M_SYSREG_MSP] = { "msp", ARM_FEATURE_M }, + [M_SYSREG_PSP] = { "psp", ARM_FEATURE_M }, + [M_SYSREG_PRIMASK] = { "primask", ARM_FEATURE_M }, + [M_SYSREG_CONTROL] = { "control", ARM_FEATURE_M }, + [M_SYSREG_BASEPRI] = { "basepri", ARM_FEATURE_M_MAIN }, + [M_SYSREG_FAULTMASK] = { "faultmask", ARM_FEATURE_M_MAIN }, + [M_SYSREG_MSPLIM] = { "msplim", ARM_FEATURE_V8 }, + [M_SYSREG_PSPLIM] = { "psplim", ARM_FEATURE_V8 }, }; -static const struct TypeSize vec_lanes[] = { - /* quads */ - { "uint128", 128, 'q', 'u' }, - { "int128", 128, 'q', 's' }, - /* 64 bit */ - { "ieee_double", 64, 'd', 'f' }, - { "uint64", 64, 'd', 'u' }, - { "int64", 64, 'd', 's' }, - /* 32 bit */ - { "ieee_single", 32, 's', 'f' }, - { "uint32", 32, 's', 'u' }, - { "int32", 32, 's', 's' }, - /* 16 bit */ - { "ieee_half", 16, 'h', 'f' }, - { "uint16", 16, 'h', 'u' }, - { "int16", 16, 'h', 's' }, - /* bytes */ - { "uint8", 8, 'b', 'u' }, - { "int8", 8, 'b', 's' }, -}; +static uint32_t *m_sysreg_ptr(CPUARMState *env, MProfileSysreg reg, bool sec) +{ + uint32_t *ptr; + switch (reg) { + case M_SYSREG_MSP: + ptr = arm_v7m_get_sp_ptr(env, sec, false, true); + break; + case M_SYSREG_PSP: + ptr = arm_v7m_get_sp_ptr(env, sec, true, true); + break; + case M_SYSREG_MSPLIM: + ptr = &env->v7m.msplim[sec]; + break; + case M_SYSREG_PSPLIM: + ptr = &env->v7m.psplim[sec]; + break; + case M_SYSREG_PRIMASK: + ptr = &env->v7m.primask[sec]; + break; + case M_SYSREG_BASEPRI: + ptr = &env->v7m.basepri[sec]; + break; + case M_SYSREG_FAULTMASK: + ptr = &env->v7m.faultmask[sec]; + break; + case M_SYSREG_CONTROL: + ptr = &env->v7m.control[sec]; + break; + default: + return NULL; + } + return arm_feature(env, m_sysreg_def[reg].feature) ? ptr : NULL; +} -int arm_gen_dynamic_svereg_xml(CPUState *cs, int base_reg) +static int m_sysreg_get(CPUARMState *env, GByteArray *buf, + MProfileSysreg reg, bool secure) +{ + uint32_t *ptr = m_sysreg_ptr(env, reg, secure); + + if (ptr == NULL) { + return 0; + } + return gdb_get_reg32(buf, *ptr); +} + +static int arm_gdb_get_m_systemreg(CPUState *cs, GByteArray *buf, int 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, j, 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, ""); + CPUARMState *env = &cpu->env; - /* 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, "svev%c%c", 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. + * Here, we emulate MRS instruction, where CONTROL has a mix of + * banked and non-banked bits. */ - for (bits = 128, i = 0; bits >= 8; bits /= 2, i++) { - const char suf[] = { 'q', 'd', 's', 'h', 'b' }; - g_string_append_printf(s, "", suf[i]); - for (j = 0; j < ARRAY_SIZE(vec_lanes); j++) { - if (vec_lanes[j].size == bits) { - g_string_append_printf(s, "", - vec_lanes[j].suffix, - vec_lanes[j].sz, vec_lanes[j].suffix); - } - } - g_string_append(s, ""); + if (reg == M_SYSREG_CONTROL) { + return gdb_get_reg32(buf, arm_v7m_mrs_control(env, env->v7m.secure)); } - /* And now the final union of unions */ - g_string_append(s, ""); - for (bits = 128, i = 0; bits >= 8; bits /= 2, i++) { - const char suf[] = { 'q', 'd', 's', 'h', 'b' }; - g_string_append_printf(s, "", - suf[i], suf[i]); - } - g_string_append(s, ""); - - /* Finally the sve prefix type */ - g_string_append_printf(s, - "", - reg_width / 8); - - /* 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; - - 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; + return m_sysreg_get(env, buf, reg, env->v7m.secure); } +static int arm_gdb_set_m_systemreg(CPUState *cs, uint8_t *buf, int reg) +{ + return 0; /* TODO */ +} -const char *arm_gdb_get_dynamic_xml(CPUState *cs, const char *xmlname) +static GDBFeature *arm_gen_dynamic_m_systemreg_feature(CPUState *cs, + int base_reg) { ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + GDBFeatureBuilder builder; + int reg = 0; + int i; - if (strcmp(xmlname, "system-registers.xml") == 0) { - return cpu->dyn_sysreg_xml.desc; - } else if (strcmp(xmlname, "sve-registers.xml") == 0) { - return cpu->dyn_svereg_xml.desc; + gdb_feature_builder_init(&builder, &cpu->dyn_m_systemreg_feature.desc, + "org.gnu.gdb.arm.m-system", "arm-m-system.xml", + base_reg); + + for (i = 0; i < ARRAY_SIZE(m_sysreg_def); i++) { + if (arm_feature(env, m_sysreg_def[i].feature)) { + gdb_feature_builder_append_reg(&builder, m_sysreg_def[i].name, 32, + reg++, "int", NULL); + } } - return NULL; + + gdb_feature_builder_end(&builder); + + return &cpu->dyn_m_systemreg_feature.desc; } +#ifndef CONFIG_USER_ONLY +/* + * For user-only, we see the non-secure registers via m_systemreg above. + * For secext, encode the non-secure view as even and secure view as odd. + */ +static int arm_gdb_get_m_secextreg(CPUState *cs, GByteArray *buf, int reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + return m_sysreg_get(env, buf, reg >> 1, reg & 1); +} + +static int arm_gdb_set_m_secextreg(CPUState *cs, uint8_t *buf, int reg) +{ + return 0; /* TODO */ +} + +static GDBFeature *arm_gen_dynamic_m_secextreg_feature(CPUState *cs, + int base_reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + GDBFeatureBuilder builder; + char *name; + int reg = 0; + int i; + + gdb_feature_builder_init(&builder, &cpu->dyn_m_secextreg_feature.desc, + "org.gnu.gdb.arm.secext", "arm-m-secext.xml", + base_reg); + + for (i = 0; i < ARRAY_SIZE(m_sysreg_def); i++) { + name = g_strconcat(m_sysreg_def[i].name, "_ns", NULL); + gdb_feature_builder_append_reg(&builder, name, 32, reg++, + "int", NULL); + name = g_strconcat(m_sysreg_def[i].name, "_s", NULL); + gdb_feature_builder_append_reg(&builder, name, 32, reg++, + "int", NULL); + } + + gdb_feature_builder_end(&builder); + + return &cpu->dyn_m_secextreg_feature.desc; +} +#endif +#endif /* CONFIG_TCG */ + void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) { CPUState *cs = CPU(cpu); @@ -466,25 +486,40 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) */ #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); + GDBFeature *feature = arm_gen_dynamic_svereg_feature(cs, cs->gdb_num_regs); + gdb_register_coprocessor(cs, aarch64_gdb_get_sve_reg, + aarch64_gdb_set_sve_reg, feature, 0); } else { - gdb_register_coprocessor(cs, aarch64_fpu_gdb_get_reg, - aarch64_fpu_gdb_set_reg, - 34, "aarch64-fpu.xml", 0); + gdb_register_coprocessor(cs, aarch64_gdb_get_fpu_reg, + aarch64_gdb_set_fpu_reg, + gdb_find_static_feature("aarch64-fpu.xml"), + 0); + } + /* + * Note that we report pauth information via the feature name + * org.gnu.gdb.aarch64.pauth_v2, not org.gnu.gdb.aarch64.pauth. + * GDB versions 9 through 12 have a bug where they will crash + * if they see the latter XML from QEMU. + */ + if (isar_feature_aa64_pauth(&cpu->isar)) { + gdb_register_coprocessor(cs, aarch64_gdb_get_pauth_reg, + aarch64_gdb_set_pauth_reg, + gdb_find_static_feature("aarch64-pauth.xml"), + 0); } #endif } else { if (arm_feature(env, ARM_FEATURE_NEON)) { gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, - 49, "arm-neon.xml", 0); + gdb_find_static_feature("arm-neon.xml"), + 0); } else if (cpu_isar_feature(aa32_simd_r32, cpu)) { gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, - 33, "arm-vfp3.xml", 0); + gdb_find_static_feature("arm-vfp3.xml"), + 0); } else if (cpu_isar_feature(aa32_vfp_simd, cpu)) { gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, - 17, "arm-vfp.xml", 0); + gdb_find_static_feature("arm-vfp.xml"), 0); } if (!arm_feature(env, ARM_FEATURE_M)) { /* @@ -492,15 +527,31 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) * expose to gdb. */ gdb_register_coprocessor(cs, vfp_gdb_get_sysreg, vfp_gdb_set_sysreg, - 2, "arm-vfp-sysregs.xml", 0); + gdb_find_static_feature("arm-vfp-sysregs.xml"), + 0); } } - if (cpu_isar_feature(aa32_mve, cpu)) { + if (cpu_isar_feature(aa32_mve, cpu) && tcg_enabled()) { gdb_register_coprocessor(cs, mve_gdb_get_reg, mve_gdb_set_reg, - 1, "arm-m-profile-mve.xml", 0); + gdb_find_static_feature("arm-m-profile-mve.xml"), + 0); } gdb_register_coprocessor(cs, arm_gdb_get_sysreg, arm_gdb_set_sysreg, - arm_gen_dynamic_sysreg_xml(cs, cs->gdb_num_regs), - "system-registers.xml", 0); + arm_gen_dynamic_sysreg_feature(cs, cs->gdb_num_regs), + 0); +#ifdef CONFIG_TCG + if (arm_feature(env, ARM_FEATURE_M) && tcg_enabled()) { + gdb_register_coprocessor(cs, + arm_gdb_get_m_systemreg, arm_gdb_set_m_systemreg, + arm_gen_dynamic_m_systemreg_feature(cs, cs->gdb_num_regs), 0); +#ifndef CONFIG_USER_ONLY + if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { + gdb_register_coprocessor(cs, + arm_gdb_get_m_secextreg, arm_gdb_set_m_secextreg, + arm_gen_dynamic_m_secextreg_feature(cs, cs->gdb_num_regs), 0); + } +#endif + } +#endif /* CONFIG_TCG */ } diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index 07a6746944..caa31ff3fa 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -20,7 +20,7 @@ #include "qemu/log.h" #include "cpu.h" #include "internals.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { @@ -72,8 +72,11 @@ int aarch64_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } -int aarch64_fpu_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) +int aarch64_gdb_get_fpu_reg(CPUState *cs, GByteArray *buf, int reg) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + switch (reg) { case 0 ... 31: { @@ -92,8 +95,11 @@ int aarch64_fpu_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) } } -int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) +int aarch64_gdb_set_fpu_reg(CPUState *cs, uint8_t *buf, int reg) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + switch (reg) { case 0 ... 31: /* 128 bit FP register */ @@ -116,9 +122,10 @@ int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) } } -int arm_gdb_get_svereg(CPUARMState *env, GByteArray *buf, int reg) +int aarch64_gdb_get_sve_reg(CPUState *cs, GByteArray *buf, int reg) { - ARMCPU *cpu = env_archcpu(env); + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; switch (reg) { /* The first 32 registers are the zregs */ @@ -164,9 +171,10 @@ int arm_gdb_get_svereg(CPUARMState *env, GByteArray *buf, int reg) return 0; } -int arm_gdb_set_svereg(CPUARMState *env, uint8_t *buf, int reg) +int aarch64_gdb_set_sve_reg(CPUState *cs, uint8_t *buf, int reg) { - ARMCPU *cpu = env_archcpu(env); + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; /* The first 32 registers are the zregs */ switch (reg) { @@ -209,3 +217,167 @@ int arm_gdb_set_svereg(CPUARMState *env, uint8_t *buf, int reg) return 0; } + +int aarch64_gdb_get_pauth_reg(CPUState *cs, GByteArray *buf, int reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + switch (reg) { + case 0: /* pauth_dmask */ + case 1: /* pauth_cmask */ + case 2: /* pauth_dmask_high */ + case 3: /* pauth_cmask_high */ + /* + * Note that older versions of this feature only contained + * pauth_{d,c}mask, for use with Linux user processes, and + * thus exclusively in the low half of the address space. + * + * To support system mode, and to debug kernels, two new regs + * were added to cover the high half of the address space. + * For the purpose of pauth_ptr_mask, we can use any well-formed + * address within the address space half -- here, 0 and -1. + */ + { + bool is_data = !(reg & 1); + bool is_high = reg & 2; + ARMMMUIdx mmu_idx = arm_stage1_mmu_idx(env); + ARMVAParameters param; + + param = aa64_va_parameters(env, -is_high, mmu_idx, is_data, false); + return gdb_get_reg64(buf, pauth_ptr_mask(param)); + } + default: + return 0; + } +} + +int aarch64_gdb_set_pauth_reg(CPUState *cs, uint8_t *buf, int reg) +{ + /* All pseudo registers are read-only. */ + return 0; +} + +static void output_vector_union_type(GDBFeatureBuilder *builder, int reg_width, + const char *name) +{ + struct TypeSize { + const char *gdb_type; + short size; + char sz, suffix; + }; + + static const struct TypeSize vec_lanes[] = { + /* quads */ + { "uint128", 128, 'q', 'u' }, + { "int128", 128, 'q', 's' }, + /* 64 bit */ + { "ieee_double", 64, 'd', 'f' }, + { "uint64", 64, 'd', 'u' }, + { "int64", 64, 'd', 's' }, + /* 32 bit */ + { "ieee_single", 32, 's', 'f' }, + { "uint32", 32, 's', 'u' }, + { "int32", 32, 's', 's' }, + /* 16 bit */ + { "ieee_half", 16, 'h', 'f' }, + { "uint16", 16, 'h', 'u' }, + { "int16", 16, 'h', 's' }, + /* bytes */ + { "uint8", 8, 'b', 'u' }, + { "int8", 8, 'b', 's' }, + }; + + static const char suf[] = { 'b', 'h', 's', 'd', 'q' }; + int i, j; + + /* First define types and totals in a whole VL */ + for (i = 0; i < ARRAY_SIZE(vec_lanes); i++) { + gdb_feature_builder_append_tag( + builder, "", + name, vec_lanes[i].sz, vec_lanes[i].suffix, + vec_lanes[i].gdb_type, reg_width / vec_lanes[i].size); + } + + /* + * 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 (i = 0; i < ARRAY_SIZE(suf); i++) { + int bits = 8 << i; + + gdb_feature_builder_append_tag(builder, "", + name, suf[i]); + for (j = 0; j < ARRAY_SIZE(vec_lanes); j++) { + if (vec_lanes[j].size == bits) { + gdb_feature_builder_append_tag( + builder, "", + vec_lanes[j].suffix, name, + vec_lanes[j].sz, vec_lanes[j].suffix); + } + } + gdb_feature_builder_append_tag(builder, ""); + } + + /* And now the final union of unions */ + gdb_feature_builder_append_tag(builder, "", name); + for (i = ARRAY_SIZE(suf) - 1; i >= 0; i--) { + gdb_feature_builder_append_tag(builder, + "", + suf[i], name, suf[i]); + } + gdb_feature_builder_append_tag(builder, ""); +} + +GDBFeature *arm_gen_dynamic_svereg_feature(CPUState *cs, int base_reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + int reg_width = cpu->sve_max_vq * 128; + int pred_width = cpu->sve_max_vq * 16; + GDBFeatureBuilder builder; + char *name; + int reg = 0; + int i; + + gdb_feature_builder_init(&builder, &cpu->dyn_svereg_feature.desc, + "org.gnu.gdb.aarch64.sve", "sve-registers.xml", + base_reg); + + /* Create the vector union type. */ + output_vector_union_type(&builder, reg_width, "svev"); + + /* Create the predicate vector type. */ + gdb_feature_builder_append_tag( + &builder, "", + pred_width / 8); + + /* Define the vector registers. */ + for (i = 0; i < 32; i++) { + name = g_strdup_printf("z%d", i); + gdb_feature_builder_append_reg(&builder, name, reg_width, reg++, + "svev", NULL); + } + + /* fpscr & status registers */ + gdb_feature_builder_append_reg(&builder, "fpsr", 32, reg++, + "int", "float"); + gdb_feature_builder_append_reg(&builder, "fpcr", 32, reg++, + "int", "float"); + + /* Define the predicate registers. */ + for (i = 0; i < 16; i++) { + name = g_strdup_printf("p%d", i); + gdb_feature_builder_append_reg(&builder, name, pred_width, reg++, + "svep", NULL); + } + gdb_feature_builder_append_reg(&builder, "ffr", pred_width, reg++, + "svep", "vector"); + + /* Define the vector length pseudo-register. */ + gdb_feature_builder_append_reg(&builder, "vg", 64, reg++, "int", NULL); + + gdb_feature_builder_end(&builder); + + return &cpu->dyn_svereg_feature.desc; +} diff --git a/target/arm/gtimer.h b/target/arm/gtimer.h new file mode 100644 index 0000000000..b992941bef --- /dev/null +++ b/target/arm/gtimer.h @@ -0,0 +1,21 @@ +/* + * ARM generic timer definitions for Arm A-class CPU + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef TARGET_ARM_GTIMER_H +#define TARGET_ARM_GTIMER_H + +enum { + GTIMER_PHYS = 0, + GTIMER_VIRT = 1, + GTIMER_HYP = 2, + GTIMER_SEC = 3, + GTIMER_HYPVIRT = 4, +#define NUM_GTIMERS 5 +}; + +#endif diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c deleted file mode 100644 index 77a8502b6b..0000000000 --- a/target/arm/helper-a64.c +++ /dev/null @@ -1,1101 +0,0 @@ -/* - * AArch64 specific helpers - * - * Copyright (c) 2013 Alexander Graf - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * 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 "cpu.h" -#include "exec/gdbstub.h" -#include "exec/helper-proto.h" -#include "qemu/host-utils.h" -#include "qemu/log.h" -#include "qemu/main-loop.h" -#include "qemu/bitops.h" -#include "internals.h" -#include "qemu/crc32c.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "qemu/int128.h" -#include "qemu/atomic128.h" -#include "fpu/softfloat.h" -#include /* For crc32 */ - -/* C2.4.7 Multiply and divide */ -/* special cases for 0 and LLONG_MIN are mandated by the standard */ -uint64_t HELPER(udiv64)(uint64_t num, uint64_t den) -{ - if (den == 0) { - return 0; - } - return num / den; -} - -int64_t HELPER(sdiv64)(int64_t num, int64_t den) -{ - if (den == 0) { - return 0; - } - if (num == LLONG_MIN && den == -1) { - return LLONG_MIN; - } - return num / den; -} - -uint64_t HELPER(rbit64)(uint64_t x) -{ - return revbit64(x); -} - -void HELPER(msr_i_spsel)(CPUARMState *env, uint32_t imm) -{ - update_spsel(env, imm); -} - -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 && !(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, - imm, 0x1f, 0), - exception_target_el(env), ra); - } -} - -void HELPER(msr_i_daifset)(CPUARMState *env, uint32_t imm) -{ - daif_check(env, 0x1e, imm, GETPC()); - env->daif |= (imm << 6) & PSTATE_DAIF; - arm_rebuild_hflags(env); -} - -void HELPER(msr_i_daifclear)(CPUARMState *env, uint32_t imm) -{ - daif_check(env, 0x1f, imm, GETPC()); - env->daif &= ~((imm << 6) & PSTATE_DAIF); - arm_rebuild_hflags(env); -} - -/* Convert a softfloat float_relation_ (as returned by - * the float*_compare functions) to the correct ARM - * NZCV flag state. - */ -static inline uint32_t float_rel_to_flags(int res) -{ - uint64_t flags; - switch (res) { - case float_relation_equal: - flags = PSTATE_Z | PSTATE_C; - break; - case float_relation_less: - flags = PSTATE_N; - break; - case float_relation_greater: - flags = PSTATE_C; - break; - case float_relation_unordered: - default: - flags = PSTATE_C | PSTATE_V; - break; - } - return flags; -} - -uint64_t HELPER(vfp_cmph_a64)(uint32_t x, uint32_t y, void *fp_status) -{ - return float_rel_to_flags(float16_compare_quiet(x, y, fp_status)); -} - -uint64_t HELPER(vfp_cmpeh_a64)(uint32_t x, uint32_t y, void *fp_status) -{ - return float_rel_to_flags(float16_compare(x, y, fp_status)); -} - -uint64_t HELPER(vfp_cmps_a64)(float32 x, float32 y, void *fp_status) -{ - return float_rel_to_flags(float32_compare_quiet(x, y, fp_status)); -} - -uint64_t HELPER(vfp_cmpes_a64)(float32 x, float32 y, void *fp_status) -{ - return float_rel_to_flags(float32_compare(x, y, fp_status)); -} - -uint64_t HELPER(vfp_cmpd_a64)(float64 x, float64 y, void *fp_status) -{ - return float_rel_to_flags(float64_compare_quiet(x, y, fp_status)); -} - -uint64_t HELPER(vfp_cmped_a64)(float64 x, float64 y, void *fp_status) -{ - return float_rel_to_flags(float64_compare(x, y, fp_status)); -} - -float32 HELPER(vfp_mulxs)(float32 a, float32 b, void *fpstp) -{ - float_status *fpst = fpstp; - - a = float32_squash_input_denormal(a, fpst); - b = float32_squash_input_denormal(b, fpst); - - if ((float32_is_zero(a) && float32_is_infinity(b)) || - (float32_is_infinity(a) && float32_is_zero(b))) { - /* 2.0 with the sign bit set to sign(A) XOR sign(B) */ - return make_float32((1U << 30) | - ((float32_val(a) ^ float32_val(b)) & (1U << 31))); - } - return float32_mul(a, b, fpst); -} - -float64 HELPER(vfp_mulxd)(float64 a, float64 b, void *fpstp) -{ - float_status *fpst = fpstp; - - a = float64_squash_input_denormal(a, fpst); - b = float64_squash_input_denormal(b, fpst); - - if ((float64_is_zero(a) && float64_is_infinity(b)) || - (float64_is_infinity(a) && float64_is_zero(b))) { - /* 2.0 with the sign bit set to sign(A) XOR sign(B) */ - return make_float64((1ULL << 62) | - ((float64_val(a) ^ float64_val(b)) & (1ULL << 63))); - } - return float64_mul(a, b, fpst); -} - -/* 64bit/double versions of the neon float compare functions */ -uint64_t HELPER(neon_ceq_f64)(float64 a, float64 b, void *fpstp) -{ - float_status *fpst = fpstp; - return -float64_eq_quiet(a, b, fpst); -} - -uint64_t HELPER(neon_cge_f64)(float64 a, float64 b, void *fpstp) -{ - float_status *fpst = fpstp; - return -float64_le(b, a, fpst); -} - -uint64_t HELPER(neon_cgt_f64)(float64 a, float64 b, void *fpstp) -{ - float_status *fpst = fpstp; - return -float64_lt(b, a, fpst); -} - -/* Reciprocal step and sqrt step. Note that unlike the A32/T32 - * versions, these do a fully fused multiply-add or - * multiply-add-and-halve. - */ - -uint32_t HELPER(recpsf_f16)(uint32_t a, uint32_t b, void *fpstp) -{ - float_status *fpst = fpstp; - - a = float16_squash_input_denormal(a, fpst); - b = float16_squash_input_denormal(b, fpst); - - a = float16_chs(a); - if ((float16_is_infinity(a) && float16_is_zero(b)) || - (float16_is_infinity(b) && float16_is_zero(a))) { - return float16_two; - } - return float16_muladd(a, b, float16_two, 0, fpst); -} - -float32 HELPER(recpsf_f32)(float32 a, float32 b, void *fpstp) -{ - float_status *fpst = fpstp; - - a = float32_squash_input_denormal(a, fpst); - b = float32_squash_input_denormal(b, fpst); - - a = float32_chs(a); - if ((float32_is_infinity(a) && float32_is_zero(b)) || - (float32_is_infinity(b) && float32_is_zero(a))) { - return float32_two; - } - return float32_muladd(a, b, float32_two, 0, fpst); -} - -float64 HELPER(recpsf_f64)(float64 a, float64 b, void *fpstp) -{ - float_status *fpst = fpstp; - - a = float64_squash_input_denormal(a, fpst); - b = float64_squash_input_denormal(b, fpst); - - a = float64_chs(a); - if ((float64_is_infinity(a) && float64_is_zero(b)) || - (float64_is_infinity(b) && float64_is_zero(a))) { - return float64_two; - } - return float64_muladd(a, b, float64_two, 0, fpst); -} - -uint32_t HELPER(rsqrtsf_f16)(uint32_t a, uint32_t b, void *fpstp) -{ - float_status *fpst = fpstp; - - a = float16_squash_input_denormal(a, fpst); - b = float16_squash_input_denormal(b, fpst); - - a = float16_chs(a); - if ((float16_is_infinity(a) && float16_is_zero(b)) || - (float16_is_infinity(b) && float16_is_zero(a))) { - return float16_one_point_five; - } - return float16_muladd(a, b, float16_three, float_muladd_halve_result, fpst); -} - -float32 HELPER(rsqrtsf_f32)(float32 a, float32 b, void *fpstp) -{ - float_status *fpst = fpstp; - - a = float32_squash_input_denormal(a, fpst); - b = float32_squash_input_denormal(b, fpst); - - a = float32_chs(a); - if ((float32_is_infinity(a) && float32_is_zero(b)) || - (float32_is_infinity(b) && float32_is_zero(a))) { - return float32_one_point_five; - } - return float32_muladd(a, b, float32_three, float_muladd_halve_result, fpst); -} - -float64 HELPER(rsqrtsf_f64)(float64 a, float64 b, void *fpstp) -{ - float_status *fpst = fpstp; - - a = float64_squash_input_denormal(a, fpst); - b = float64_squash_input_denormal(b, fpst); - - a = float64_chs(a); - if ((float64_is_infinity(a) && float64_is_zero(b)) || - (float64_is_infinity(b) && float64_is_zero(a))) { - return float64_one_point_five; - } - return float64_muladd(a, b, float64_three, float_muladd_halve_result, fpst); -} - -/* Pairwise long add: add pairs of adjacent elements into - * double-width elements in the result (eg _s8 is an 8x8->16 op) - */ -uint64_t HELPER(neon_addlp_s8)(uint64_t a) -{ - uint64_t nsignmask = 0x0080008000800080ULL; - uint64_t wsignmask = 0x8000800080008000ULL; - uint64_t elementmask = 0x00ff00ff00ff00ffULL; - uint64_t tmp1, tmp2; - uint64_t res, signres; - - /* Extract odd elements, sign extend each to a 16 bit field */ - tmp1 = a & elementmask; - tmp1 ^= nsignmask; - tmp1 |= wsignmask; - tmp1 = (tmp1 - nsignmask) ^ wsignmask; - /* Ditto for the even elements */ - tmp2 = (a >> 8) & elementmask; - tmp2 ^= nsignmask; - tmp2 |= wsignmask; - tmp2 = (tmp2 - nsignmask) ^ wsignmask; - - /* calculate the result by summing bits 0..14, 16..22, etc, - * and then adjusting the sign bits 15, 23, etc manually. - * This ensures the addition can't overflow the 16 bit field. - */ - signres = (tmp1 ^ tmp2) & wsignmask; - res = (tmp1 & ~wsignmask) + (tmp2 & ~wsignmask); - res ^= signres; - - return res; -} - -uint64_t HELPER(neon_addlp_u8)(uint64_t a) -{ - uint64_t tmp; - - tmp = a & 0x00ff00ff00ff00ffULL; - tmp += (a >> 8) & 0x00ff00ff00ff00ffULL; - return tmp; -} - -uint64_t HELPER(neon_addlp_s16)(uint64_t a) -{ - int32_t reslo, reshi; - - reslo = (int32_t)(int16_t)a + (int32_t)(int16_t)(a >> 16); - reshi = (int32_t)(int16_t)(a >> 32) + (int32_t)(int16_t)(a >> 48); - - return (uint32_t)reslo | (((uint64_t)reshi) << 32); -} - -uint64_t HELPER(neon_addlp_u16)(uint64_t a) -{ - uint64_t tmp; - - tmp = a & 0x0000ffff0000ffffULL; - tmp += (a >> 16) & 0x0000ffff0000ffffULL; - return tmp; -} - -/* Floating-point reciprocal exponent - see FPRecpX in ARM ARM */ -uint32_t HELPER(frecpx_f16)(uint32_t a, void *fpstp) -{ - float_status *fpst = fpstp; - uint16_t val16, sbit; - int16_t exp; - - if (float16_is_any_nan(a)) { - float16 nan = a; - if (float16_is_signaling_nan(a, fpst)) { - float_raise(float_flag_invalid, fpst); - if (!fpst->default_nan_mode) { - nan = float16_silence_nan(a, fpst); - } - } - if (fpst->default_nan_mode) { - nan = float16_default_nan(fpst); - } - return nan; - } - - a = float16_squash_input_denormal(a, fpst); - - val16 = float16_val(a); - sbit = 0x8000 & val16; - exp = extract32(val16, 10, 5); - - if (exp == 0) { - return make_float16(deposit32(sbit, 10, 5, 0x1e)); - } else { - return make_float16(deposit32(sbit, 10, 5, ~exp)); - } -} - -float32 HELPER(frecpx_f32)(float32 a, void *fpstp) -{ - float_status *fpst = fpstp; - uint32_t val32, sbit; - int32_t exp; - - if (float32_is_any_nan(a)) { - float32 nan = a; - if (float32_is_signaling_nan(a, fpst)) { - float_raise(float_flag_invalid, fpst); - if (!fpst->default_nan_mode) { - nan = float32_silence_nan(a, fpst); - } - } - if (fpst->default_nan_mode) { - nan = float32_default_nan(fpst); - } - return nan; - } - - a = float32_squash_input_denormal(a, fpst); - - val32 = float32_val(a); - sbit = 0x80000000ULL & val32; - exp = extract32(val32, 23, 8); - - if (exp == 0) { - return make_float32(sbit | (0xfe << 23)); - } else { - return make_float32(sbit | (~exp & 0xff) << 23); - } -} - -float64 HELPER(frecpx_f64)(float64 a, void *fpstp) -{ - float_status *fpst = fpstp; - uint64_t val64, sbit; - int64_t exp; - - if (float64_is_any_nan(a)) { - float64 nan = a; - if (float64_is_signaling_nan(a, fpst)) { - float_raise(float_flag_invalid, fpst); - if (!fpst->default_nan_mode) { - nan = float64_silence_nan(a, fpst); - } - } - if (fpst->default_nan_mode) { - nan = float64_default_nan(fpst); - } - return nan; - } - - a = float64_squash_input_denormal(a, fpst); - - val64 = float64_val(a); - sbit = 0x8000000000000000ULL & val64; - exp = extract64(float64_val(a), 52, 11); - - if (exp == 0) { - return make_float64(sbit | (0x7feULL << 52)); - } else { - return make_float64(sbit | (~exp & 0x7ffULL) << 52); - } -} - -float32 HELPER(fcvtx_f64_to_f32)(float64 a, CPUARMState *env) -{ - /* Von Neumann rounding is implemented by using round-to-zero - * and then setting the LSB of the result if Inexact was raised. - */ - float32 r; - float_status *fpst = &env->vfp.fp_status; - float_status tstat = *fpst; - int exflags; - - set_float_rounding_mode(float_round_to_zero, &tstat); - set_float_exception_flags(0, &tstat); - r = float64_to_float32(a, &tstat); - exflags = get_float_exception_flags(&tstat); - if (exflags & float_flag_inexact) { - r = make_float32(float32_val(r) | 1); - } - exflags |= get_float_exception_flags(fpst); - set_float_exception_flags(exflags, fpst); - return r; -} - -/* 64-bit versions of the CRC helpers. Note that although the operation - * (and the prototypes of crc32c() and crc32() mean that only the bottom - * 32 bits of the accumulator and result are used, we pass and return - * uint64_t for convenience of the generated code. Unlike the 32-bit - * instruction set versions, val may genuinely have 64 bits of data in it. - * The upper bytes of val (above the number specified by 'bytes') must have - * been zeroed out by the caller. - */ -uint64_t HELPER(crc32_64)(uint64_t acc, uint64_t val, uint32_t bytes) -{ - uint8_t buf[8]; - - stq_le_p(buf, val); - - /* zlib crc32 converts the accumulator and output to one's complement. */ - return crc32(acc ^ 0xffffffff, buf, bytes) ^ 0xffffffff; -} - -uint64_t HELPER(crc32c_64)(uint64_t acc, uint64_t val, uint32_t bytes) -{ - uint8_t buf[8]; - - stq_le_p(buf, val); - - /* Linux crc32c converts the output to one's complement. */ - return crc32c(acc, buf, bytes) ^ 0xffffffff; -} - -uint64_t HELPER(paired_cmpxchg64_le)(CPUARMState *env, uint64_t addr, - uint64_t new_lo, uint64_t new_hi) -{ - Int128 cmpv = int128_make128(env->exclusive_val, env->exclusive_high); - Int128 newv = int128_make128(new_lo, new_hi); - Int128 oldv; - uintptr_t ra = GETPC(); - uint64_t o0, o1; - bool success; - int mem_idx = cpu_mmu_index(env, false); - MemOpIdx oi0 = make_memop_idx(MO_LEUQ | MO_ALIGN_16, mem_idx); - MemOpIdx oi1 = make_memop_idx(MO_LEUQ, mem_idx); - - o0 = cpu_ldq_le_mmu(env, addr + 0, oi0, ra); - o1 = cpu_ldq_le_mmu(env, addr + 8, oi1, ra); - oldv = int128_make128(o0, o1); - - success = int128_eq(oldv, cmpv); - if (success) { - cpu_stq_le_mmu(env, addr + 0, int128_getlo(newv), oi1, ra); - cpu_stq_le_mmu(env, addr + 8, int128_gethi(newv), oi1, ra); - } - - return !success; -} - -uint64_t HELPER(paired_cmpxchg64_le_parallel)(CPUARMState *env, uint64_t addr, - uint64_t new_lo, uint64_t new_hi) -{ - Int128 oldv, cmpv, newv; - uintptr_t ra = GETPC(); - bool success; - int mem_idx; - MemOpIdx oi; - - assert(HAVE_CMPXCHG128); - - mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_LE | MO_128 | MO_ALIGN, mem_idx); - - cmpv = int128_make128(env->exclusive_val, env->exclusive_high); - newv = int128_make128(new_lo, new_hi); - oldv = cpu_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv, oi, ra); - - success = int128_eq(oldv, cmpv); - return !success; -} - -uint64_t HELPER(paired_cmpxchg64_be)(CPUARMState *env, uint64_t addr, - uint64_t new_lo, uint64_t new_hi) -{ - /* - * High and low need to be switched here because this is not actually a - * 128bit store but two doublewords stored consecutively - */ - Int128 cmpv = int128_make128(env->exclusive_high, env->exclusive_val); - Int128 newv = int128_make128(new_hi, new_lo); - Int128 oldv; - uintptr_t ra = GETPC(); - uint64_t o0, o1; - bool success; - int mem_idx = cpu_mmu_index(env, false); - MemOpIdx oi0 = make_memop_idx(MO_BEUQ | MO_ALIGN_16, mem_idx); - MemOpIdx oi1 = make_memop_idx(MO_BEUQ, mem_idx); - - o1 = cpu_ldq_be_mmu(env, addr + 0, oi0, ra); - o0 = cpu_ldq_be_mmu(env, addr + 8, oi1, ra); - oldv = int128_make128(o0, o1); - - success = int128_eq(oldv, cmpv); - if (success) { - cpu_stq_be_mmu(env, addr + 0, int128_gethi(newv), oi1, ra); - cpu_stq_be_mmu(env, addr + 8, int128_getlo(newv), oi1, ra); - } - - return !success; -} - -uint64_t HELPER(paired_cmpxchg64_be_parallel)(CPUARMState *env, uint64_t addr, - uint64_t new_lo, uint64_t new_hi) -{ - Int128 oldv, cmpv, newv; - uintptr_t ra = GETPC(); - bool success; - int mem_idx; - MemOpIdx oi; - - assert(HAVE_CMPXCHG128); - - mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_BE | MO_128 | MO_ALIGN, mem_idx); - - /* - * High and low need to be switched here because this is not actually a - * 128bit store but two doublewords stored consecutively - */ - cmpv = int128_make128(env->exclusive_high, env->exclusive_val); - newv = int128_make128(new_hi, new_lo); - oldv = cpu_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra); - - success = int128_eq(oldv, cmpv); - return !success; -} - -/* Writes back the old data into Rs. */ -void HELPER(casp_le_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr, - uint64_t new_lo, uint64_t new_hi) -{ - Int128 oldv, cmpv, newv; - uintptr_t ra = GETPC(); - int mem_idx; - MemOpIdx oi; - - assert(HAVE_CMPXCHG128); - - mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_LE | MO_128 | MO_ALIGN, mem_idx); - - cmpv = int128_make128(env->xregs[rs], env->xregs[rs + 1]); - newv = int128_make128(new_lo, new_hi); - oldv = cpu_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv, oi, ra); - - env->xregs[rs] = int128_getlo(oldv); - env->xregs[rs + 1] = int128_gethi(oldv); -} - -void HELPER(casp_be_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr, - uint64_t new_hi, uint64_t new_lo) -{ - Int128 oldv, cmpv, newv; - uintptr_t ra = GETPC(); - int mem_idx; - MemOpIdx oi; - - assert(HAVE_CMPXCHG128); - - mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_LE | MO_128 | MO_ALIGN, mem_idx); - - cmpv = int128_make128(env->xregs[rs + 1], env->xregs[rs]); - newv = int128_make128(new_lo, new_hi); - oldv = cpu_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra); - - env->xregs[rs + 1] = int128_getlo(oldv); - env->xregs[rs] = int128_gethi(oldv); -} - -/* - * AdvSIMD half-precision - */ - -#define ADVSIMD_HELPER(name, suffix) HELPER(glue(glue(advsimd_, name), suffix)) - -#define ADVSIMD_HALFOP(name) \ -uint32_t ADVSIMD_HELPER(name, h)(uint32_t a, uint32_t b, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - return float16_ ## name(a, b, fpst); \ -} - -ADVSIMD_HALFOP(add) -ADVSIMD_HALFOP(sub) -ADVSIMD_HALFOP(mul) -ADVSIMD_HALFOP(div) -ADVSIMD_HALFOP(min) -ADVSIMD_HALFOP(max) -ADVSIMD_HALFOP(minnum) -ADVSIMD_HALFOP(maxnum) - -#define ADVSIMD_TWOHALFOP(name) \ -uint32_t ADVSIMD_HELPER(name, 2h)(uint32_t two_a, uint32_t two_b, void *fpstp) \ -{ \ - float16 a1, a2, b1, b2; \ - uint32_t r1, r2; \ - float_status *fpst = fpstp; \ - a1 = extract32(two_a, 0, 16); \ - a2 = extract32(two_a, 16, 16); \ - b1 = extract32(two_b, 0, 16); \ - b2 = extract32(two_b, 16, 16); \ - r1 = float16_ ## name(a1, b1, fpst); \ - r2 = float16_ ## name(a2, b2, fpst); \ - return deposit32(r1, 16, 16, r2); \ -} - -ADVSIMD_TWOHALFOP(add) -ADVSIMD_TWOHALFOP(sub) -ADVSIMD_TWOHALFOP(mul) -ADVSIMD_TWOHALFOP(div) -ADVSIMD_TWOHALFOP(min) -ADVSIMD_TWOHALFOP(max) -ADVSIMD_TWOHALFOP(minnum) -ADVSIMD_TWOHALFOP(maxnum) - -/* Data processing - scalar floating-point and advanced SIMD */ -static float16 float16_mulx(float16 a, float16 b, void *fpstp) -{ - float_status *fpst = fpstp; - - a = float16_squash_input_denormal(a, fpst); - b = float16_squash_input_denormal(b, fpst); - - if ((float16_is_zero(a) && float16_is_infinity(b)) || - (float16_is_infinity(a) && float16_is_zero(b))) { - /* 2.0 with the sign bit set to sign(A) XOR sign(B) */ - return make_float16((1U << 14) | - ((float16_val(a) ^ float16_val(b)) & (1U << 15))); - } - return float16_mul(a, b, fpst); -} - -ADVSIMD_HALFOP(mulx) -ADVSIMD_TWOHALFOP(mulx) - -/* fused multiply-accumulate */ -uint32_t HELPER(advsimd_muladdh)(uint32_t a, uint32_t b, uint32_t c, - void *fpstp) -{ - float_status *fpst = fpstp; - return float16_muladd(a, b, c, 0, fpst); -} - -uint32_t HELPER(advsimd_muladd2h)(uint32_t two_a, uint32_t two_b, - uint32_t two_c, void *fpstp) -{ - float_status *fpst = fpstp; - float16 a1, a2, b1, b2, c1, c2; - uint32_t r1, r2; - a1 = extract32(two_a, 0, 16); - a2 = extract32(two_a, 16, 16); - b1 = extract32(two_b, 0, 16); - b2 = extract32(two_b, 16, 16); - c1 = extract32(two_c, 0, 16); - c2 = extract32(two_c, 16, 16); - r1 = float16_muladd(a1, b1, c1, 0, fpst); - r2 = float16_muladd(a2, b2, c2, 0, fpst); - return deposit32(r1, 16, 16, r2); -} - -/* - * Floating point comparisons produce an integer result. Softfloat - * routines return float_relation types which we convert to the 0/-1 - * Neon requires. - */ - -#define ADVSIMD_CMPRES(test) (test) ? 0xffff : 0 - -uint32_t HELPER(advsimd_ceq_f16)(uint32_t a, uint32_t b, void *fpstp) -{ - float_status *fpst = fpstp; - int compare = float16_compare_quiet(a, b, fpst); - return ADVSIMD_CMPRES(compare == float_relation_equal); -} - -uint32_t HELPER(advsimd_cge_f16)(uint32_t a, uint32_t b, void *fpstp) -{ - float_status *fpst = fpstp; - int compare = float16_compare(a, b, fpst); - return ADVSIMD_CMPRES(compare == float_relation_greater || - compare == float_relation_equal); -} - -uint32_t HELPER(advsimd_cgt_f16)(uint32_t a, uint32_t b, void *fpstp) -{ - float_status *fpst = fpstp; - int compare = float16_compare(a, b, fpst); - return ADVSIMD_CMPRES(compare == float_relation_greater); -} - -uint32_t HELPER(advsimd_acge_f16)(uint32_t a, uint32_t b, void *fpstp) -{ - float_status *fpst = fpstp; - float16 f0 = float16_abs(a); - float16 f1 = float16_abs(b); - int compare = float16_compare(f0, f1, fpst); - return ADVSIMD_CMPRES(compare == float_relation_greater || - compare == float_relation_equal); -} - -uint32_t HELPER(advsimd_acgt_f16)(uint32_t a, uint32_t b, void *fpstp) -{ - float_status *fpst = fpstp; - float16 f0 = float16_abs(a); - float16 f1 = float16_abs(b); - int compare = float16_compare(f0, f1, fpst); - return ADVSIMD_CMPRES(compare == float_relation_greater); -} - -/* round to integral */ -uint32_t HELPER(advsimd_rinth_exact)(uint32_t x, void *fp_status) -{ - return float16_round_to_int(x, fp_status); -} - -uint32_t HELPER(advsimd_rinth)(uint32_t x, void *fp_status) -{ - int old_flags = get_float_exception_flags(fp_status), new_flags; - float16 ret; - - ret = float16_round_to_int(x, fp_status); - - /* Suppress any inexact exceptions the conversion produced */ - if (!(old_flags & float_flag_inexact)) { - new_flags = get_float_exception_flags(fp_status); - set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status); - } - - return ret; -} - -/* - * Half-precision floating point conversion functions - * - * There are a multitude of conversion functions with various - * different rounding modes. This is dealt with by the calling code - * setting the mode appropriately before calling the helper. - */ - -uint32_t HELPER(advsimd_f16tosinth)(uint32_t a, void *fpstp) -{ - float_status *fpst = fpstp; - - /* Invalid if we are passed a NaN */ - if (float16_is_any_nan(a)) { - float_raise(float_flag_invalid, fpst); - return 0; - } - return float16_to_int16(a, fpst); -} - -uint32_t HELPER(advsimd_f16touinth)(uint32_t a, void *fpstp) -{ - float_status *fpst = fpstp; - - /* Invalid if we are passed a NaN */ - if (float16_is_any_nan(a)) { - float_raise(float_flag_invalid, fpst); - return 0; - } - return float16_to_uint16(a, fpst); -} - -static int el_from_spsr(uint32_t spsr) -{ - /* Return the exception level that this SPSR is requesting a return to, - * or -1 if it is invalid (an illegal return) - */ - if (spsr & PSTATE_nRW) { - switch (spsr & CPSR_M) { - case ARM_CPU_MODE_USR: - return 0; - case ARM_CPU_MODE_HYP: - return 2; - case ARM_CPU_MODE_FIQ: - case ARM_CPU_MODE_IRQ: - case ARM_CPU_MODE_SVC: - case ARM_CPU_MODE_ABT: - case ARM_CPU_MODE_UND: - case ARM_CPU_MODE_SYS: - return 1; - case ARM_CPU_MODE_MON: - /* Returning to Mon from AArch64 is never possible, - * so this is an illegal return. - */ - default: - return -1; - } - } else { - if (extract32(spsr, 1, 1)) { - /* Return with reserved M[1] bit set */ - return -1; - } - if (extract32(spsr, 0, 4) == 1) { - /* return to EL0 with M[0] bit set */ - return -1; - } - return extract32(spsr, 2, 2); - } -} - -static void cpsr_write_from_spsr_elx(CPUARMState *env, - uint32_t val) -{ - uint32_t mask; - - /* Save SPSR_ELx.SS into PSTATE. */ - env->pstate = (env->pstate & ~PSTATE_SS) | (val & PSTATE_SS); - val &= ~PSTATE_SS; - - /* Move DIT to the correct location for CPSR */ - if (val & PSTATE_DIT) { - val &= ~PSTATE_DIT; - val |= CPSR_DIT; - } - - mask = aarch32_cpsr_valid_mask(env->features, \ - &env_archcpu(env)->isar); - cpsr_write(env, val, mask, CPSRWriteRaw); -} - -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]; - int new_el; - bool return_to_aa64 = (spsr & PSTATE_nRW) == 0; - - aarch64_save_sp(env, cur_el); - - arm_clear_exclusive(env); - - /* We must squash the PSTATE.SS bit to zero unless both of the - * following hold: - * 1. debug exceptions are currently disabled - * 2. singlestep will be active in the EL we return to - * We check 1 here and 2 after we've done the pstate/cpsr write() to - * transition to the EL we're going to. - */ - if (arm_generate_debug_exceptions(env)) { - spsr &= ~PSTATE_SS; - } - - new_el = el_from_spsr(spsr); - if (new_el == -1) { - goto illegal_return; - } - if (new_el > cur_el || (new_el == 2 && !arm_is_el2_enabled(env))) { - /* Disallow return to an EL which is unimplemented or higher - * than the current one. - */ - goto illegal_return; - } - - if (new_el != 0 && arm_el_is_aa64(env, new_el) != return_to_aa64) { - /* Return to an EL which is configured for a different register width */ - goto illegal_return; - } - - if (new_el == 1 && (arm_hcr_el2_eff(env) & HCR_TGE)) { - goto illegal_return; - } - - qemu_mutex_lock_iothread(); - arm_call_pre_el_change_hook(env_archcpu(env)); - qemu_mutex_unlock_iothread(); - - if (!return_to_aa64) { - env->aarch64 = false; - /* We do a raw CPSR write because aarch64_sync_64_to_32() - * will sort the register banks out for us, and we've already - * caught all the bad-mode cases in el_from_spsr(). - */ - cpsr_write_from_spsr_elx(env, spsr); - if (!arm_singlestep_active(env)) { - env->pstate &= ~PSTATE_SS; - } - aarch64_sync_64_to_32(env); - - if (spsr & CPSR_T) { - env->regs[15] = new_pc & ~0x1; - } else { - env->regs[15] = new_pc & ~0x3; - } - helper_rebuild_hflags_a32(env, new_el); - qemu_log_mask(CPU_LOG_INT, "Exception return from AArch64 EL%d to " - "AArch32 EL%d PC 0x%" PRIx32 "\n", - cur_el, new_el, env->regs[15]); - } else { - int tbii; - - env->aarch64 = true; - 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); - 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 = EX_TBFLAG_A64(env->hflags, 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); - } - - /* - * Note that cur_el can never be 0. If new_el is 0, then - * el0_a64 is return_to_aa64, else el0_a64 is ignored. - */ - aarch64_sve_change_el(env, cur_el, new_el, return_to_aa64); - - qemu_mutex_lock_iothread(); - arm_call_el_change_hook(env_archcpu(env)); - qemu_mutex_unlock_iothread(); - - return; - -illegal_return: - /* Illegal return events of various kinds have architecturally - * mandated behaviour: - * restore NZCV and DAIF from SPSR_ELx - * set PSTATE.IL - * restore PC from ELR_ELx - * no change to exception level, execution state or stack pointer - */ - env->pstate |= PSTATE_IL; - env->pc = new_pc; - spsr &= PSTATE_NZCV | PSTATE_DAIF; - spsr |= pstate_read(env) & ~(PSTATE_NZCV | PSTATE_DAIF); - pstate_write(env, spsr); - if (!arm_singlestep_active(env)) { - env->pstate &= ~PSTATE_SS; - } - helper_rebuild_hflags_a64(env, cur_el); - qemu_log_mask(LOG_GUEST_ERROR, "Illegal exception return at EL%d: " - "resuming execution at 0x%" PRIx64 "\n", cur_el, env->pc); -} - -/* - * Square Root and Reciprocal square root - */ - -uint32_t HELPER(sqrt_f16)(uint32_t a, void *fpstp) -{ - float_status *s = 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). - */ - int blocklen = 4 << env_archcpu(env)->dcz_blocksize; - uint64_t vaddr = vaddr_in & ~(blocklen - 1); - int mmu_idx = cpu_mmu_index(env, false); - void *mem; - - /* - * Trapless lookup. In addition to actual invalid page, may - * return NULL for I/O, watchpoints, clean pages, etc. - */ - mem = tlb_vaddr_to_host(env, vaddr, MMU_DATA_STORE, mmu_idx); - -#ifndef CONFIG_USER_ONLY - if (unlikely(!mem)) { - uintptr_t ra = GETPC(); - - /* - * Trap if accessing an invalid page. DC_ZVA requires that we supply - * the original pointer for an invalid page. But watchpoints require - * that we probe the actual space. So do both. - */ - (void) probe_write(env, vaddr_in, 1, mmu_idx, ra); - mem = probe_write(env, vaddr, blocklen, mmu_idx, ra); - - if (unlikely(!mem)) { - /* - * The only remaining reason for mem == NULL is I/O. - * Just do a series of byte writes as the architecture demands. - */ - for (int i = 0; i < blocklen; i++) { - cpu_stb_mmuidx_ra(env, vaddr + i, 0, mmu_idx, ra); - } - return; - } - } -#endif - - memset(mem, 0, blocklen); -} diff --git a/target/arm/helper.c b/target/arm/helper.c index ac9942d750..a620481d7c 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7,13 +7,12 @@ */ #include "qemu/osdep.h" -#include "qemu/units.h" #include "qemu/log.h" #include "trace.h" #include "cpu.h" #include "internals.h" +#include "cpu-features.h" #include "exec/helper-proto.h" -#include "qemu/host-utils.h" #include "qemu/main-loop.h" #include "qemu/timer.h" #include "qemu/bitops.h" @@ -22,20 +21,16 @@ #include "exec/exec-all.h" #include /* For crc32 */ #include "hw/irq.h" -#include "semihosting/semihost.h" -#include "sysemu/cpus.h" #include "sysemu/cpu-timers.h" #include "sysemu/kvm.h" -#include "qemu/range.h" -#include "qapi/qapi-commands-machine-target.h" +#include "sysemu/tcg.h" #include "qapi/error.h" #include "qemu/guest-random.h" #ifdef CONFIG_TCG -#include "arm_ldst.h" -#include "exec/cpu_ldst.h" #include "semihosting/common-semi.h" #endif #include "cpregs.h" +#include "target/arm/gtimer.h" #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ @@ -51,8 +46,7 @@ static uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri) } } -static void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { assert(ri->fieldoffset); if (cpreg_field_is_64bit(ri)) { @@ -84,7 +78,8 @@ uint64_t read_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri) static void write_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t v) { - /* Raw write of a coprocessor register (as needed for migration, etc). + /* + * Raw write of a coprocessor register (as needed for migration, etc). * Note that constant registers are treated as write-ignored; the * caller should check for success by whether a readback gives the * value written. @@ -102,7 +97,8 @@ static void write_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri, static bool raw_accessors_invalid(const ARMCPRegInfo *ri) { - /* Return true if the regdef would cause an assertion if you called + /* + * Return true if the regdef would cause an assertion if you called * read_raw_cp_reg() or write_raw_cp_reg() on it (ie if it is a * program bug for it not to have the NO_RAW flag). * NB that returning false here doesn't necessarily mean that calling @@ -185,7 +181,8 @@ bool write_list_to_cpustate(ARMCPU *cpu) if (ri->type & ARM_CP_NO_RAW) { continue; } - /* Write value and confirm it reads back as written + /* + * Write value and confirm it reads back as written * (to catch read-only registers and partially read-only * registers where the incoming migration value doesn't match) */ @@ -203,7 +200,7 @@ static void add_cpreg_to_list(gpointer key, gpointer opaque) uint32_t regidx = (uintptr_t)key; const ARMCPRegInfo *ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); - if (!(ri->type & (ARM_CP_NO_RAW|ARM_CP_ALIAS))) { + if (!(ri->type & (ARM_CP_NO_RAW | ARM_CP_ALIAS))) { cpu->cpreg_indexes[cpu->cpreg_array_len] = cpreg_to_kvm_id(regidx); /* The value array need not be initialized at this point */ cpu->cpreg_array_len++; @@ -217,7 +214,7 @@ static void count_cpreg(gpointer key, gpointer opaque) ri = g_hash_table_lookup(cpu->cp_regs, key); - if (!(ri->type & (ARM_CP_NO_RAW|ARM_CP_ALIAS))) { + if (!(ri->type & (ARM_CP_NO_RAW | ARM_CP_ALIAS))) { cpu->cpreg_array_len++; } } @@ -238,7 +235,8 @@ static gint cpreg_key_compare(gconstpointer a, gconstpointer b) void init_cpreg_list(ARMCPU *cpu) { - /* Initialise the cpreg_tuples[] array based on the cp_regs hash. + /* + * Initialise the cpreg_tuples[] array based on the cp_regs hash. * Note that we require cpreg_tuples[] to be sorted by key ID. */ GList *keys; @@ -266,6 +264,18 @@ void init_cpreg_list(ARMCPU *cpu) g_list_free(keys); } +static bool arm_pan_enabled(CPUARMState *env) +{ + if (is_a64(env)) { + if ((arm_hcr_el2_eff(env) & (HCR_NV | HCR_NV1)) == (HCR_NV | HCR_NV1)) { + return false; + } + return env->pstate & PSTATE_PAN; + } else { + return env->uncached_cpsr & CPSR_PAN; + } +} + /* * Some registers are not accessible from AArch32 EL3 if SCR.NS == 0. */ @@ -280,7 +290,8 @@ static CPAccessResult access_el3_aa32ns(CPUARMState *env, return CP_ACCESS_OK; } -/* Some secure-only AArch32 registers trap to EL3 if used from +/* + * Some secure-only AArch32 registers trap to EL3 if used from * Secure EL1 (but are just ordinary UNDEF in other non-EL3 contexts). * Note that an access from Secure EL1 can only happen if EL3 is AArch64. * We assume that the .access field is set to PL1_RW. @@ -302,72 +313,8 @@ static CPAccessResult access_trap_aa32s_el1(CPUARMState *env, return CP_ACCESS_TRAP_UNCATEGORIZED; } -static uint64_t arm_mdcr_el2_eff(CPUARMState *env) -{ - return arm_is_el2_enabled(env) ? env->cp15.mdcr_el2 : 0; -} - -/* Check for traps to "powerdown debug" registers, which are controlled - * by MDCR.TDOSA - */ -static CPAccessResult access_tdosa(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - int el = arm_current_el(env); - uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); - bool mdcr_el2_tdosa = (mdcr_el2 & MDCR_TDOSA) || (mdcr_el2 & MDCR_TDE) || - (arm_hcr_el2_eff(env) & HCR_TGE); - - if (el < 2 && mdcr_el2_tdosa) { - return CP_ACCESS_TRAP_EL2; - } - if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDOSA)) { - return CP_ACCESS_TRAP_EL3; - } - return CP_ACCESS_OK; -} - -/* Check for traps to "debug ROM" registers, which are controlled - * by MDCR_EL2.TDRA for EL2 but by the more general MDCR_EL3.TDA for EL3. - */ -static CPAccessResult access_tdra(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - int el = arm_current_el(env); - uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); - bool mdcr_el2_tdra = (mdcr_el2 & MDCR_TDRA) || (mdcr_el2 & MDCR_TDE) || - (arm_hcr_el2_eff(env) & HCR_TGE); - - if (el < 2 && mdcr_el2_tdra) { - return CP_ACCESS_TRAP_EL2; - } - if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { - return CP_ACCESS_TRAP_EL3; - } - return CP_ACCESS_OK; -} - -/* Check for traps to general debug registers, which are controlled - * by MDCR_EL2.TDA for EL2 and MDCR_EL3.TDA for EL3. - */ -static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - int el = arm_current_el(env); - uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); - bool mdcr_el2_tda = (mdcr_el2 & MDCR_TDA) || (mdcr_el2 & MDCR_TDE) || - (arm_hcr_el2_eff(env) & HCR_TGE); - - if (el < 2 && mdcr_el2_tda) { - return CP_ACCESS_TRAP_EL2; - } - if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { - return CP_ACCESS_TRAP_EL3; - } - return CP_ACCESS_OK; -} - -/* Check for traps to performance monitor registers, which are controlled +/* + * Check for traps to performance monitor registers, which are controlled * by MDCR_EL2.TPM for EL2 and MDCR_EL3.TPM for EL3. */ static CPAccessResult access_tpm(CPUARMState *env, const ARMCPRegInfo *ri, @@ -386,8 +333,8 @@ static CPAccessResult access_tpm(CPUARMState *env, const ARMCPRegInfo *ri, } /* 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) +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; @@ -428,6 +375,30 @@ static CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +/* Check for traps from EL1 due to HCR_EL2.TTLB or TTLBIS. */ +static CPAccessResult access_ttlbis(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && + (arm_hcr_el2_eff(env) & (HCR_TTLB | HCR_TTLBIS))) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; +} + +#ifdef TARGET_AARCH64 +/* Check for traps from EL1 due to HCR_EL2.TTLB or TTLBOS. */ +static CPAccessResult access_ttlbos(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && + (arm_hcr_el2_eff(env) & (HCR_TTLB | HCR_TTLBOS))) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; +} +#endif + static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = env_archcpu(env); @@ -441,7 +412,8 @@ static void fcse_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) ARMCPU *cpu = env_archcpu(env); if (raw_read(env, ri) != value) { - /* Unlike real hardware the qemu TLB uses virtual addresses, + /* + * Unlike real hardware the qemu TLB uses virtual addresses, * not modified virtual addresses, so this causes a TLB flush. */ tlb_flush(CPU(cpu)); @@ -456,7 +428,8 @@ static void contextidr_write(CPUARMState *env, const ARMCPRegInfo *ri, if (raw_read(env, ri) != value && !arm_feature(env, ARM_FEATURE_PMSA) && !extended_addresses_enabled(env)) { - /* For VMSA (when not using the LPAE long descriptor page table + /* + * For VMSA (when not using the LPAE long descriptor page table * format) this register includes the ASID, so do a TLB flush. * For PMSA it is purely a process ID and no action is needed. */ @@ -465,6 +438,21 @@ static void contextidr_write(CPUARMState *env, const ARMCPRegInfo *ri, raw_write(env, ri, value); } +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. + */ + return (ARMMMUIdxBit_E10_1 | + ARMMMUIdxBit_E10_1_PAN | + ARMMMUIdxBit_E10_0 | + ARMMMUIdxBit_Stage2 | + ARMMMUIdxBit_Stage2_S); +} + + /* IS variants of TLB operations must affect all cores */ static void tlbiall_is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) @@ -567,10 +555,7 @@ static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri, { CPUState *cs = env_cpu(env); - tlb_flush_by_mmuidx(cs, - ARMMMUIdxBit_E10_1 | - ARMMMUIdxBit_E10_1_PAN | - ARMMMUIdxBit_E10_0); + tlb_flush_by_mmuidx(cs, alle1_tlbmask(env)); } static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -578,10 +563,7 @@ 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_E10_1 | - ARMMMUIdxBit_E10_1_PAN | - ARMMMUIdxBit_E10_0); + tlb_flush_by_mmuidx_all_cpus_synced(cs, alle1_tlbmask(env)); } @@ -620,8 +602,27 @@ static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, ARMMMUIdxBit_E2); } +static void tlbiipas2_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + uint64_t pageaddr = (value & MAKE_64BIT_MASK(0, 28)) << 12; + + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_Stage2); +} + +static void tlbiipas2is_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + uint64_t pageaddr = (value & MAKE_64BIT_MASK(0, 28)) << 12; + + tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, ARMMMUIdxBit_Stage2); +} + static const ARMCPRegInfo cp_reginfo[] = { - /* Define the secure and non-secure FCSE identifier CP registers + /* + * Define the secure and non-secure FCSE identifier CP registers * separately because there is no secure bank in V8 (no _EL3). This allows * the secure register to be properly reset and migrated. There is also no * v8 EL1 version of the register so the non-secure instance stands alone. @@ -636,7 +637,8 @@ static const ARMCPRegInfo cp_reginfo[] = { .access = PL1_RW, .secure = ARM_CP_SECSTATE_S, .fieldoffset = offsetof(CPUARMState, cp15.fcseidr_s), .resetvalue = 0, .writefn = fcse_write, .raw_writefn = raw_write, }, - /* Define the secure and non-secure context identifier CP registers + /* + * Define the secure and non-secure context identifier CP registers * separately because there is no secure bank in V8 (no _EL3). This allows * the secure register to be properly reset and migrated. In the * non-secure case, the 32-bit register will have reset and migration @@ -645,6 +647,8 @@ static const ARMCPRegInfo cp_reginfo[] = { { .name = "CONTEXTIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_CONTEXTIDR_EL1, + .nv2_redirect_offset = 0x108 | NV2_REDIR_NV1, .secure = ARM_CP_SECSTATE_NS, .fieldoffset = offsetof(CPUARMState, cp15.contextidr_el[1]), .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, @@ -657,7 +661,8 @@ static const ARMCPRegInfo cp_reginfo[] = { }; static const ARMCPRegInfo not_v8_cp_reginfo[] = { - /* NB: Some of these registers exist in v8 but with more precise + /* + * NB: Some of these registers exist in v8 but with more precise * definitions that don't use CP_ANY wildcards (mostly in v8_cp_reginfo[]). */ /* MMU Domain access control / MPU write buffer control */ @@ -667,7 +672,8 @@ static const ARMCPRegInfo not_v8_cp_reginfo[] = { .writefn = dacr_write, .raw_writefn = raw_write, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dacr_s), offsetoflow32(CPUARMState, cp15.dacr_ns) } }, - /* ARMv7 allocates a range of implementation defined TLB LOCKDOWN regs. + /* + * ARMv7 allocates a range of implementation defined TLB LOCKDOWN regs. * For v6 and v5, these mappings are overly broad. */ { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 0, @@ -685,7 +691,8 @@ static const ARMCPRegInfo not_v8_cp_reginfo[] = { }; static const ARMCPRegInfo not_v6_cp_reginfo[] = { - /* Not all pre-v6 cores implemented this WFI, so this is slightly + /* + * Not all pre-v6 cores implemented this WFI, so this is slightly * over-broad. */ { .name = "WFI_v5", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = 2, @@ -693,12 +700,14 @@ static const ARMCPRegInfo not_v6_cp_reginfo[] = { }; static const ARMCPRegInfo not_v7_cp_reginfo[] = { - /* Standard v6 WFI (also used in some pre-v6 cores); not in v7 (which + /* + * Standard v6 WFI (also used in some pre-v6 cores); not in v7 (which * is UNPREDICTABLE; we choose to NOP as most implementations do). */ { .name = "WFI_v6", .cp = 15, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 4, .access = PL1_W, .type = ARM_CP_WFI }, - /* L1 cache lockdown. Not architectural in v6 and earlier but in practice + /* + * L1 cache lockdown. Not architectural in v6 and earlier but in practice * implemented in 926, 946, 1026, 1136, 1176 and 11MPCore. StrongARM and * OMAPCP will override this space. */ @@ -712,14 +721,16 @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = { { .name = "DUMMY", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = CP_ANY, .access = PL1_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW, .resetvalue = 0 }, - /* We don't implement pre-v7 debug but most CPUs had at least a DBGDIDR; + /* + * We don't implement pre-v7 debug but most CPUs had at least a DBGDIDR; * implementing it as RAZ means the "debug architecture version" bits * will read as a reserved value, which should cause Linux to not try * to use the debug hardware. */ { .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* MMU TLB control. Note that the wildcarding means we cover not just + /* + * MMU TLB control. Note that the wildcarding means we cover not just * the unified TLB ops but also the dside/iside/inner-shareable variants. */ { .name = "TLBIALL", .cp = 15, .crn = 8, .crm = CP_ANY, @@ -747,7 +758,8 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, /* In ARMv8 most bits of CPACR_EL1 are RES0. */ if (!arm_feature(env, ARM_FEATURE_V8)) { - /* ARMv7 defines bits for unimplemented coprocessors as RAZ/WI. + /* + * ARMv7 defines bits for unimplemented coprocessors as RAZ/WI. * ASEDIS [31] and D32DIS [30] are both UNK/SBZP without VFP. * TRCDIS [28] is RAZ/WI since we do not implement a trace macrocell. */ @@ -763,7 +775,8 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, value |= R_CPACR_ASEDIS_MASK; } - /* VFPv3 and upwards with NEON implement 32 double precision + /* + * VFPv3 and upwards with NEON implement 32 double precision * registers (D0-D31). */ if (!cpu_isar_feature(aa32_simd_r32, env_archcpu(env))) { @@ -805,7 +818,8 @@ static uint64_t cpacr_read(CPUARMState *env, const ARMCPRegInfo *ri) static void cpacr_reset(CPUARMState *env, const ARMCPRegInfo *ri) { - /* Call cpacr_write() so that we reset with the correct RAO bits set + /* + * Call cpacr_write() so that we reset with the correct RAO bits set * for our CPU features. */ cpacr_write(env, ri, 0); @@ -846,7 +860,8 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { { .name = "MVA_prefetch", .cp = 15, .crn = 7, .crm = 13, .opc1 = 0, .opc2 = 1, .access = PL1_W, .type = ARM_CP_NOP }, - /* We need to break the TB after ISB to execute self-modifying code + /* + * We need to break the TB after ISB to execute self-modifying code * correctly and also to take any pending interrupts immediately. * So use arm_cp_write_ignore() function instead of ARM_CP_NOP flag. */ @@ -861,13 +876,16 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ifar_s), offsetof(CPUARMState, cp15.ifar_ns) }, .resetvalue = 0, }, - /* Watchpoint Fault Address Register : should actually only be present + /* + * Watchpoint Fault Address Register : should actually only be present * for 1136, 1176, 11MPCore. */ { .name = "WFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, }, { .name = "CPACR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, .accessfn = cpacr_access, + .fgt = FGT_CPACR_EL1, + .nv2_redirect_offset = 0x100 | NV2_REDIR_NV1, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.cpacr_el1), .resetfn = cpacr_reset, .writefn = cpacr_write, .readfn = cpacr_read }, }; @@ -931,30 +949,33 @@ static int64_t cycles_ns_per(uint64_t cycles) static bool instructions_supported(CPUARMState *env) { - return icount_enabled() == 1; /* Precise instruction counting */ + /* Precise instruction counting */ + return icount_enabled() == ICOUNT_PRECISE; } static uint64_t instructions_get_count(CPUARMState *env) { + assert(icount_enabled() == ICOUNT_PRECISE); return (uint64_t)icount_get_raw(); } static int64_t instructions_ns_per(uint64_t icount) { + assert(icount_enabled() == ICOUNT_PRECISE); return icount_to_ns((int64_t)icount); } #endif -static bool pmu_8_1_events_supported(CPUARMState *env) +static bool pmuv3p1_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)); + return cpu_isar_feature(any_pmuv3p1, env_archcpu(env)); } -static bool pmu_8_4_events_supported(CPUARMState *env) +static bool pmuv3p4_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)); + return cpu_isar_feature(any_pmuv3p4, env_archcpu(env)); } static uint64_t zero_event_get_count(CPUARMState *env) @@ -988,17 +1009,17 @@ static const pm_event pm_events[] = { }, #endif { .number = 0x023, /* STALL_FRONTEND */ - .supported = pmu_8_1_events_supported, + .supported = pmuv3p1_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, + .supported = pmuv3p1_events_supported, .get_count = zero_event_get_count, .ns_per_count = zero_event_ns_per, }, { .number = 0x03c, /* STALL */ - .supported = pmu_8_4_events_supported, + .supported = pmuv3p4_events_supported, .get_count = zero_event_get_count, .ns_per_count = zero_event_ns_per, }, @@ -1066,7 +1087,8 @@ static bool event_supported(uint16_t number) static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { - /* Performance monitor registers user accessibility is controlled + /* + * Performance monitor registers user accessibility is controlled * by PMUSERENR. MDCR_EL2.TPM and MDCR_EL3.TPM allow configurable * trapping to EL2 or EL3 for other accesses. */ @@ -1145,23 +1167,41 @@ static CPAccessResult pmreg_access_ccntr(CPUARMState *env, return pmreg_access(env, ri, isread); } -/* Returns true if the counter (pass 31 for PMCCNTR) should count events using +/* + * Bits in MDCR_EL2 and MDCR_EL3 which pmu_counter_enabled() looks at. + * We use these to decide whether we need to wrap a write to MDCR_EL2 + * or MDCR_EL3 in pmu_op_start()/pmu_op_finish() calls. + */ +#define MDCR_EL2_PMU_ENABLE_BITS \ + (MDCR_HPME | MDCR_HPMD | MDCR_HPMN | MDCR_HCCD | MDCR_HLP) +#define MDCR_EL3_PMU_ENABLE_BITS (MDCR_SPME | MDCR_SCCD) + +/* + * Returns true if the counter (pass 31 for PMCCNTR) should count events using * the current EL, security state, and register configuration. */ static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter) { uint64_t filter; bool e, p, u, nsk, nsu, nsh, m; - bool enabled, prohibited, filtered; + bool enabled, prohibited = false, filtered; bool secure = arm_is_secure(env); int el = arm_current_el(env); - uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); - uint8_t hpmn = mdcr_el2 & MDCR_HPMN; + uint64_t mdcr_el2; + uint8_t hpmn; + /* + * We might be called for M-profile cores where MDCR_EL2 doesn't + * exist and arm_mdcr_el2_eff() will assert, so this early-exit check + * must be before we read that value. + */ if (!arm_feature(env, ARM_FEATURE_PMU)) { return false; } + mdcr_el2 = arm_mdcr_el2_eff(env); + hpmn = mdcr_el2 & MDCR_HPMN; + if (!arm_feature(env, ARM_FEATURE_EL2) || (counter < hpmn || counter == 31)) { e = env->cp15.c9_pmcr & PMCRE; @@ -1170,19 +1210,29 @@ static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter) } enabled = e && (env->cp15.c9_pmcnten & (1 << counter)); - if (!secure) { - if (el == 2 && (counter < hpmn || counter == 31)) { - prohibited = mdcr_el2 & MDCR_HPMD; - } else { - prohibited = false; - } - } else { - prohibited = arm_feature(env, ARM_FEATURE_EL3) && - !(env->cp15.mdcr_el3 & MDCR_SPME); + /* Is event counting prohibited? */ + if (el == 2 && (counter < hpmn || counter == 31)) { + prohibited = mdcr_el2 & MDCR_HPMD; + } + if (secure) { + prohibited = prohibited || !(env->cp15.mdcr_el3 & MDCR_SPME); } - if (prohibited && counter == 31) { - prohibited = env->cp15.c9_pmcr & PMCRDP; + if (counter == 31) { + /* + * The cycle counter defaults to running. PMCR.DP says "disable + * the cycle counter when event counting is prohibited". + * Some MDCR bits disable the cycle counter specifically. + */ + prohibited = prohibited && env->cp15.c9_pmcr & PMCRDP; + if (cpu_isar_feature(any_pmuv3p5, env_archcpu(env))) { + if (secure) { + prohibited = prohibited || (env->cp15.mdcr_el3 & MDCR_SCCD); + } + if (el == 2) { + prohibited = prohibited || (mdcr_el2 & MDCR_HCCD); + } + } } if (counter == 31) { @@ -1230,6 +1280,43 @@ static void pmu_update_irq(CPUARMState *env) (env->cp15.c9_pminten & env->cp15.c9_pmovsr)); } +static bool pmccntr_clockdiv_enabled(CPUARMState *env) +{ + /* + * Return true if the clock divider is enabled and the cycle counter + * is supposed to tick only once every 64 clock cycles. This is + * controlled by PMCR.D, but if PMCR.LC is set to enable the long + * (64-bit) cycle counter PMCR.D has no effect. + */ + return (env->cp15.c9_pmcr & (PMCRD | PMCRLC)) == PMCRD; +} + +static bool pmevcntr_is_64_bit(CPUARMState *env, int counter) +{ + /* Return true if the specified event counter is configured to be 64 bit */ + + /* This isn't intended to be used with the cycle counter */ + assert(counter < 31); + + if (!cpu_isar_feature(any_pmuv3p5, env_archcpu(env))) { + return false; + } + + if (arm_feature(env, ARM_FEATURE_EL2)) { + /* + * MDCR_EL2.HLP still applies even when EL2 is disabled in the + * current security state, so we don't use arm_mdcr_el2_eff() here. + */ + bool hlp = env->cp15.mdcr_el2 & MDCR_HLP; + int hpmn = env->cp15.mdcr_el2 & MDCR_HPMN; + + if (counter >= hpmn) { + return hlp; + } + } + return env->cp15.c9_pmcr & PMCRLP; +} + /* * Ensure c15_ccnt is the guest-visible count so that operations such as * enabling/disabling the counter or filtering, modifying the count itself, @@ -1242,8 +1329,7 @@ static void pmccntr_op_start(CPUARMState *env) if (pmu_counter_enabled(env, 31)) { uint64_t eff_cycles = cycles; - if (env->cp15.c9_pmcr & PMCRD) { - /* Increment once every 64 processor clock cycles */ + if (pmccntr_clockdiv_enabled(env)) { eff_cycles /= 64; } @@ -1252,7 +1338,7 @@ static void pmccntr_op_start(CPUARMState *env) uint64_t overflow_mask = env->cp15.c9_pmcr & PMCRLC ? \ 1ull << 63 : 1ull << 31; if (env->cp15.c15_ccnt & ~new_pmccntr & overflow_mask) { - env->cp15.c9_pmovsr |= (1 << 31); + env->cp15.c9_pmovsr |= (1ULL << 31); pmu_update_irq(env); } @@ -1278,16 +1364,18 @@ static void pmccntr_op_finish(CPUARMState *env) int64_t overflow_in = cycles_ns_per(remaining_cycles); if (overflow_in > 0) { - int64_t overflow_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - overflow_in; - ARMCPU *cpu = env_archcpu(env); - timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); + int64_t overflow_at; + + if (!sadd64_overflow(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + overflow_in, &overflow_at)) { + ARMCPU *cpu = env_archcpu(env); + timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); + } } #endif uint64_t prev_cycles = env->cp15.c15_ccnt_delta; - if (env->cp15.c9_pmcr & PMCRD) { - /* Increment once every 64 processor clock cycles */ + if (pmccntr_clockdiv_enabled(env)) { prev_cycles /= 64; } env->cp15.c15_ccnt_delta = prev_cycles - env->cp15.c15_ccnt; @@ -1305,9 +1393,11 @@ static void pmevcntr_op_start(CPUARMState *env, uint8_t counter) } if (pmu_counter_enabled(env, counter)) { - uint32_t new_pmevcntr = count - env->cp15.c14_pmevcntr_delta[counter]; + uint64_t new_pmevcntr = count - env->cp15.c14_pmevcntr_delta[counter]; + uint64_t overflow_mask = pmevcntr_is_64_bit(env, counter) ? + 1ULL << 63 : 1ULL << 31; - if (env->cp15.c14_pmevcntr[counter] & ~new_pmevcntr & INT32_MIN) { + if (env->cp15.c14_pmevcntr[counter] & ~new_pmevcntr & overflow_mask) { env->cp15.c9_pmovsr |= (1 << counter); pmu_update_irq(env); } @@ -1322,15 +1412,22 @@ static void pmevcntr_op_finish(CPUARMState *env, uint8_t counter) #ifndef CONFIG_USER_ONLY uint16_t event = env->cp15.c14_pmevtyper[counter] & PMXEVTYPER_EVTCOUNT; uint16_t event_idx = supported_event_map[event]; - uint64_t delta = UINT32_MAX - - (uint32_t)env->cp15.c14_pmevcntr[counter] + 1; - int64_t overflow_in = pm_events[event_idx].ns_per_count(delta); + uint64_t delta = -(env->cp15.c14_pmevcntr[counter] + 1); + int64_t overflow_in; + + if (!pmevcntr_is_64_bit(env, counter)) { + delta = (uint32_t)delta; + } + overflow_in = pm_events[event_idx].ns_per_count(delta); if (overflow_in > 0) { - int64_t overflow_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - overflow_in; - ARMCPU *cpu = env_archcpu(env); - timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); + int64_t overflow_at; + + if (!sadd64_overflow(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + overflow_in, &overflow_at)) { + ARMCPU *cpu = env_archcpu(env); + timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); + } } #endif @@ -1404,10 +1501,28 @@ static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri, pmu_op_finish(env); } +static uint64_t pmcr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + uint64_t pmcr = env->cp15.c9_pmcr; + + /* + * If EL2 is implemented and enabled for the current security state, reads + * of PMCR.N from EL1 or EL0 return the value of MDCR_EL2.HPMN or HDCR.HPMN. + */ + if (arm_current_el(env) <= 1 && arm_is_el2_enabled(env)) { + pmcr &= ~PMCRN_MASK; + pmcr |= (env->cp15.mdcr_el2 & MDCR_HPMN) << PMCRN_SHIFT; + } + + return pmcr; +} + static void pmswinc_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { unsigned int i; + uint64_t overflow_mask, new_pmswinc; + for (i = 0; i < pmu_num_counters(env); i++) { /* Increment a counter's count iff: */ if ((value & (1 << i)) && /* counter's bit is set */ @@ -1421,9 +1536,12 @@ static void pmswinc_write(CPUARMState *env, const ARMCPRegInfo *ri, * Detect if this write causes an overflow since we can't predict * PMSWINC overflows like we can for other events */ - uint32_t new_pmswinc = env->cp15.c14_pmevcntr[i] + 1; + new_pmswinc = env->cp15.c14_pmevcntr[i] + 1; - if (env->cp15.c14_pmevcntr[i] & ~new_pmswinc & INT32_MIN) { + overflow_mask = pmevcntr_is_64_bit(env, i) ? + 1ULL << 63 : 1ULL << 31; + + if (env->cp15.c14_pmevcntr[i] & ~new_pmswinc & overflow_mask) { env->cp15.c9_pmovsr |= (1 << i); pmu_update_irq(env); } @@ -1447,7 +1565,8 @@ static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri) static void pmselr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - /* The value of PMSELR.SEL affects the behavior of PMXEVTYPER and + /* + * The value of PMSELR.SEL affects the behavior of PMXEVTYPER and * PMXEVCNTR. We allow [0..31] to be written to PMSELR here; in the * meanwhile, we check PMSELR.SEL when PMXEVTYPER and PMXEVCNTR are * accessed. @@ -1498,15 +1617,19 @@ static uint64_t pmccfiltr_read_a32(CPUARMState *env, const ARMCPRegInfo *ri) static void pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + pmu_op_start(env); value &= pmu_counter_mask(env); env->cp15.c9_pmcnten |= value; + pmu_op_finish(env); } static void pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + pmu_op_start(env); value &= pmu_counter_mask(env); env->cp15.c9_pmcnten &= ~value; + pmu_op_finish(env); } static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -1554,7 +1677,8 @@ static void pmevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri, env->cp15.c14_pmevtyper[counter] = value & PMXEVTYPER_MASK; pmevcntr_op_finish(env, counter); } - /* Attempts to access PMXEVTYPER are CONSTRAINED UNPREDICTABLE when + /* + * Attempts to access PMXEVTYPER are CONSTRAINED UNPREDICTABLE when * PMSELR value is equal to or greater than the number of implemented * counters, but not equal to 0x1f. We opt to behave as a RAZ/WI. */ @@ -1593,7 +1717,7 @@ static void pmevtyper_rawwrite(CPUARMState *env, const ARMCPRegInfo *ri, * pmevtyper_rawwrite is called between a pair of pmu_op_start and * pmu_op_finish calls when loading saved state for a migration. Because * we're potentially updating the type of event here, the value written to - * c14_pmevcntr_delta by the preceeding pmu_op_start call may be for a + * c14_pmevcntr_delta by the preceding pmu_op_start call may be for a * different counter type. Therefore, we need to set this value to the * current count for the counter type we're writing so that pmu_op_finish * has the correct count for its calculation. @@ -1626,6 +1750,10 @@ static uint64_t pmxevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri) static void pmevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value, uint8_t counter) { + if (!cpu_isar_feature(any_pmuv3p5, env_archcpu(env))) { + /* Before FEAT_PMUv3p5, top 32 bits of event counters are RES0 */ + value &= MAKE_64BIT_MASK(0, 32); + } if (counter < pmu_num_counters(env)) { pmevcntr_op_start(env, counter); env->cp15.c14_pmevcntr[counter] = value; @@ -1645,10 +1773,16 @@ static uint64_t pmevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri, pmevcntr_op_start(env, counter); ret = env->cp15.c14_pmevcntr[counter]; pmevcntr_op_finish(env, counter); + if (!cpu_isar_feature(any_pmuv3p5, env_archcpu(env))) { + /* Before FEAT_PMUv3p5, top 32 bits of event counters are RES0 */ + ret &= MAKE_64BIT_MASK(0, 32); + } return ret; } else { - /* We opt to behave as a RAZ/WI when attempts to access PM[X]EVCNTR - * are CONSTRAINED UNPREDICTABLE. */ + /* + * We opt to behave as a RAZ/WI when attempts to access PM[X]EVCNTR + * are CONSTRAINED UNPREDICTABLE. + */ return 0; } } @@ -1723,7 +1857,8 @@ static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - /* Note that even though the AArch64 view of this register has bits + /* + * Note that even though the AArch64 view of this register has bits * [10:0] all RES0 we can only mask the bottom 5, to comply with the * architectural requirements for bits which are RES0 only in some * contexts. (ARMv8 would permit us to do no masking at all, but ARMv7 @@ -1735,16 +1870,23 @@ static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri, static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { /* Begin with base v8.0 state. */ - uint32_t valid_mask = 0x3fff; + uint64_t valid_mask = 0x3fff; ARMCPU *cpu = env_archcpu(env); + uint64_t changed; - if (ri->state == ARM_CP_STATE_AA64) { - if (arm_feature(env, ARM_FEATURE_AARCH64) && - !cpu_isar_feature(aa64_aa32_el1, cpu)) { - value |= SCR_FW | SCR_AW; /* these two bits are RES1. */ + /* + * Because SCR_EL3 is the "real" cpreg and SCR is the alias, reset always + * passes the reginfo for SCR_EL3, which has type ARM_CP_STATE_AA64. + * Instead, choose the format based on the mode of EL3. + */ + if (arm_el_is_aa64(env, 3)) { + value |= SCR_FW | SCR_AW; /* RES1 */ + valid_mask &= ~SCR_NET; /* RES0 */ + + if (!cpu_isar_feature(aa64_aa32_el1, cpu) && + !cpu_isar_feature(aa64_aa32_el2, cpu)) { + value |= SCR_RW; /* RAO/WI */ } - valid_mask &= ~SCR_NET; - if (cpu_isar_feature(aa64_ras, cpu)) { valid_mask |= SCR_TERR; } @@ -1756,6 +1898,9 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) } if (cpu_isar_feature(aa64_sel2, cpu)) { valid_mask |= SCR_EEL2; + } else if (cpu_isar_feature(aa64_rme, cpu)) { + /* With RME and without SEL2, NS is RES1 (R_GSWWH, I_DJJQJ). */ + value |= SCR_NS; } if (cpu_isar_feature(aa64_mte, cpu)) { valid_mask |= SCR_ATA; @@ -1766,6 +1911,21 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) if (cpu_isar_feature(aa64_doublefault, cpu)) { valid_mask |= SCR_EASE | SCR_NMEA; } + if (cpu_isar_feature(aa64_sme, cpu)) { + valid_mask |= SCR_ENTP2; + } + if (cpu_isar_feature(aa64_hcx, cpu)) { + valid_mask |= SCR_HXEN; + } + if (cpu_isar_feature(aa64_fgt, cpu)) { + valid_mask |= SCR_FGTEN; + } + if (cpu_isar_feature(aa64_rme, cpu)) { + valid_mask |= SCR_NSE | SCR_GPF; + } + if (cpu_isar_feature(aa64_ecv, cpu)) { + valid_mask |= SCR_ECVEN; + } } else { valid_mask &= ~(SCR_RW | SCR_ST); if (cpu_isar_feature(aa32_ras, cpu)) { @@ -1776,7 +1936,8 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) if (!arm_feature(env, ARM_FEATURE_EL2)) { valid_mask &= ~SCR_HCE; - /* On ARMv7, SMD (or SCD as it is called in v7) is only + /* + * On ARMv7, SMD (or SCD as it is called in v7) is only * supported if EL2 exists. The bit is UNK/SBZP when * EL2 is unavailable. In QEMU ARMv7, we force it to always zero * when EL2 is unavailable. @@ -1790,7 +1951,22 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) /* Clear all-context RES0 bits. */ value &= valid_mask; - raw_write(env, ri, value); + changed = env->cp15.scr_el3 ^ value; + env->cp15.scr_el3 = value; + + /* + * If SCR_EL3.{NS,NSE} changes, i.e. change of security state, + * we must invalidate all TLBs below EL3. + */ + if (changed & (SCR_NS | SCR_NSE)) { + tlb_flush_by_mmuidx(env_cpu(env), (ARMMMUIdxBit_E10_0 | + ARMMMUIdxBit_E20_0 | + ARMMMUIdxBit_E10_1 | + ARMMMUIdxBit_E20_2 | + ARMMMUIdxBit_E10_1_PAN | + ARMMMUIdxBit_E20_2_PAN | + ARMMMUIdxBit_E2)); + } } static void scr_reset(CPUARMState *env, const ARMCPRegInfo *ri) @@ -1802,11 +1978,12 @@ static void scr_reset(CPUARMState *env, const ARMCPRegInfo *ri) scr_write(env, ri, 0); } -static CPAccessResult access_aa64_tid2(CPUARMState *env, - const ARMCPRegInfo *ri, - bool isread) +static CPAccessResult access_tid4(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) { - if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TID2)) { + if (arm_current_el(env) == 1 && + (arm_hcr_el2_eff(env) & (HCR_TID2 | HCR_TID4))) { return CP_ACCESS_TRAP_EL2; } @@ -1817,7 +1994,8 @@ static uint64_t ccsidr_read(CPUARMState *env, const ARMCPRegInfo *ri) { ARMCPU *cpu = env_archcpu(env); - /* Acquire the CSSELR index from the bank corresponding to the CCSIDR + /* + * Acquire the CSSELR index from the bank corresponding to the CCSIDR * bank */ uint32_t index = A32_BANKED_REG_GET(env, csselr, @@ -1892,7 +2070,8 @@ 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, .access = PL1_W, .type = ARM_CP_NOP }, - /* Performance monitors are implementation defined in v7, + /* + * Performance monitors are implementation defined in v7, * but with an ARM recommended set of registers, which we * follow. * @@ -1904,67 +2083,79 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { * or PL0_RO as appropriate and then check PMUSERENR in the helper fn. */ { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1, - .access = PL0_RW, .type = ARM_CP_ALIAS, + .access = PL0_RW, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), .writefn = pmcntenset_write, .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, .raw_writefn = raw_write }, - { .name = "PMCNTENSET_EL0", .state = ARM_CP_STATE_AA64, + { .name = "PMCNTENSET_EL0", .state = ARM_CP_STATE_AA64, .type = ARM_CP_IO, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 1, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .resetvalue = 0, .writefn = pmcntenset_write, .raw_writefn = raw_write }, { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2, .access = PL0_RW, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, .writefn = pmcntenclr_write, - .type = ARM_CP_ALIAS }, + .type = ARM_CP_ALIAS | ARM_CP_IO }, { .name = "PMCNTENCLR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 2, .access = PL0_RW, .accessfn = pmreg_access, - .type = ARM_CP_ALIAS, + .fgt = FGT_PMCNTEN, + .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .writefn = pmcntenclr_write }, { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3, .access = PL0_RW, .type = ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), .accessfn = pmreg_access, + .fgt = FGT_PMOVS, .writefn = pmovsr_write, .raw_writefn = raw_write }, { .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMOVS, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), .writefn = pmovsr_write, .raw_writefn = raw_write }, { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4, .access = PL0_W, .accessfn = pmreg_access_swinc, + .fgt = FGT_PMSWINC_EL0, .type = ARM_CP_NO_RAW | ARM_CP_IO, .writefn = pmswinc_write }, { .name = "PMSWINC_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 4, .access = PL0_W, .accessfn = pmreg_access_swinc, + .fgt = FGT_PMSWINC_EL0, .type = ARM_CP_NO_RAW | ARM_CP_IO, .writefn = pmswinc_write }, { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5, .access = PL0_RW, .type = ARM_CP_ALIAS, + .fgt = FGT_PMSELR_EL0, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmselr), .accessfn = pmreg_access_selr, .writefn = pmselr_write, .raw_writefn = raw_write}, { .name = "PMSELR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 5, .access = PL0_RW, .accessfn = pmreg_access_selr, + .fgt = FGT_PMSELR_EL0, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr), .writefn = pmselr_write, .raw_writefn = raw_write, }, { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS | ARM_CP_IO, + .fgt = FGT_PMCCNTR_EL0, .readfn = pmccntr_read, .writefn = pmccntr_write32, .accessfn = pmreg_access_ccntr }, { .name = "PMCCNTR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 0, .access = PL0_RW, .accessfn = pmreg_access_ccntr, + .fgt = FGT_PMCCNTR_EL0, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c15_ccnt), .readfn = pmccntr_read, .writefn = pmccntr_write, @@ -1972,32 +2163,38 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { { .name = "PMCCFILTR", .cp = 15, .opc1 = 0, .crn = 14, .crm = 15, .opc2 = 7, .writefn = pmccfiltr_write_a32, .readfn = pmccfiltr_read_a32, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCCFILTR_EL0, .type = ARM_CP_ALIAS | ARM_CP_IO, .resetvalue = 0, }, { .name = "PMCCFILTR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 15, .opc2 = 7, .writefn = pmccfiltr_write, .raw_writefn = raw_write, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCCFILTR_EL0, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.pmccfiltr_el0), .resetvalue = 0, }, { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1, .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = pmreg_access, + .fgt = FGT_PMEVTYPERN_EL0, .writefn = pmxevtyper_write, .readfn = pmxevtyper_read }, { .name = "PMXEVTYPER_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 1, .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = pmreg_access, + .fgt = FGT_PMEVTYPERN_EL0, .writefn = pmxevtyper_write, .readfn = pmxevtyper_read }, { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2, .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = pmreg_access_xevcntr, + .fgt = FGT_PMEVCNTRN_EL0, .writefn = pmxevcntr_write, .readfn = pmxevcntr_read }, { .name = "PMXEVCNTR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 2, .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = pmreg_access_xevcntr, + .fgt = FGT_PMEVCNTRN_EL0, .writefn = pmxevcntr_write, .readfn = pmxevcntr_read }, { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0, .access = PL0_R | PL1_RW, .accessfn = access_tpm, @@ -2012,6 +2209,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .writefn = pmuserenr_write, .raw_writefn = raw_write }, { .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_tpm, + .fgt = FGT_PMINTEN, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pminten), .resetvalue = 0, @@ -2019,68 +2217,85 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { { .name = "PMINTENSET_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 1, .access = PL1_RW, .accessfn = access_tpm, + .fgt = FGT_PMINTEN, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .writefn = pmintenset_write, .raw_writefn = raw_write, .resetvalue = 0x0 }, { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2, .access = PL1_RW, .accessfn = access_tpm, + .fgt = FGT_PMINTEN, .type = ARM_CP_ALIAS | ARM_CP_IO | ARM_CP_NO_RAW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .writefn = pmintenclr_write, }, { .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2, .access = PL1_RW, .accessfn = access_tpm, + .fgt = FGT_PMINTEN, .type = ARM_CP_ALIAS | ARM_CP_IO | ARM_CP_NO_RAW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .writefn = pmintenclr_write }, { .name = "CCSIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, .access = PL1_R, - .accessfn = access_aa64_tid2, + .accessfn = access_tid4, + .fgt = FGT_CCSIDR_EL1, .readfn = ccsidr_read, .type = ARM_CP_NO_RAW }, { .name = "CSSELR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 2, .opc2 = 0, .access = PL1_RW, - .accessfn = access_aa64_tid2, + .accessfn = access_tid4, + .fgt = FGT_CSSELR_EL1, .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 + /* + * Auxiliary ID register: this actually has an IMPDEF value but for now * just RAZ for all cores: */ { .name = "AIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 1, .crn = 0, .crm = 0, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid1, + .fgt = FGT_AIDR_EL1, .resetvalue = 0 }, - /* Auxiliary fault status registers: these also are IMPDEF, and we + /* + * 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, .accessfn = access_tvm_trvm, + .fgt = FGT_AFSR0_EL1, + .nv2_redirect_offset = 0x128 | NV2_REDIR_NV1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "AFSR1_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 1, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_AFSR1_EL1, + .nv2_redirect_offset = 0x130 | NV2_REDIR_NV1, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* MAIR can just read-as-written because we don't implement caches + /* + * 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, .accessfn = access_tvm_trvm, + .fgt = FGT_MAIR_EL1, + .nv2_redirect_offset = 0x140 | NV2_REDIR_NV1, .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, .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[3]), .resetvalue = 0 }, - /* For non-long-descriptor page tables these are PRRR and NMRR; + /* + * For non-long-descriptor page tables these are PRRR and NMRR; * regardless they still act as reads-as-written for QEMU. */ - /* MAIR0/1 are defined separately from their 64-bit counterpart which + /* + * MAIR0/1 are defined separately from their 64-bit counterpart which * allows them to assign the correct fieldoffset based on the endianness * handled in the field definitions. */ @@ -2098,6 +2313,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .resetfn = arm_cp_reset_ignore }, { .name = "ISR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 1, .opc2 = 0, + .fgt = FGT_ISR_EL1, .type = ARM_CP_NO_RAW, .access = PL1_R, .readfn = isr_read }, /* 32 bit ITLB invalidates */ { .name = "ITLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 0, @@ -2137,16 +2353,16 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { 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, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, .writefn = tlbiall_is_write }, { .name = "TLBIMVAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, .writefn = tlbimva_is_write }, { .name = "TLBIASIDIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, .writefn = tlbiasid_is_write }, { .name = "TLBIMVAAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, .writefn = tlbimvaa_is_write }, }; @@ -2154,6 +2370,7 @@ static const ARMCPRegInfo pmovsset_cp_reginfo[] = { /* PMOVSSET is not implemented in v7 before v7ve */ { .name = "PMOVSSET", .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 3, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMOVS, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), .writefn = pmovsset_write, @@ -2161,6 +2378,7 @@ static const ARMCPRegInfo pmovsset_cp_reginfo[] = { { .name = "PMOVSSET_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 3, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMOVS, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), .writefn = pmovsset_write, @@ -2211,25 +2429,30 @@ static const ARMCPRegInfo v6k_cp_reginfo[] = { { .name = "TPIDR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 2, .crn = 13, .crm = 0, .access = PL0_RW, + .fgt = FGT_TPIDR_EL0, .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[0]), .resetvalue = 0 }, { .name = "TPIDRURW", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL0_RW, + .fgt = FGT_TPIDR_EL0, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tpidrurw_s), offsetoflow32(CPUARMState, cp15.tpidrurw_ns) }, .resetfn = arm_cp_reset_ignore }, { .name = "TPIDRRO_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 3, .crn = 13, .crm = 0, - .access = PL0_R|PL1_W, + .access = PL0_R | PL1_W, + .fgt = FGT_TPIDRRO_EL0, .fieldoffset = offsetof(CPUARMState, cp15.tpidrro_el[0]), .resetvalue = 0}, { .name = "TPIDRURO", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 3, - .access = PL0_R|PL1_W, + .access = PL0_R | PL1_W, + .fgt = FGT_TPIDRRO_EL0, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tpidruro_s), offsetoflow32(CPUARMState, cp15.tpidruro_ns) }, .resetfn = arm_cp_reset_ignore }, { .name = "TPIDR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .opc2 = 4, .crn = 13, .crm = 0, .access = PL1_RW, + .fgt = FGT_TPIDR_EL1, .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[1]), .resetvalue = 0 }, { .name = "TPIDRPRW", .opc1 = 0, .cp = 15, .crn = 13, .crm = 0, .opc2 = 4, .access = PL1_RW, @@ -2243,7 +2466,8 @@ static const ARMCPRegInfo v6k_cp_reginfo[] = { static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { - /* CNTFRQ: not visible from PL0 if both PL0PCTEN and PL0VCTEN are zero. + /* + * CNTFRQ: not visible from PL0 if both PL0PCTEN and PL0VCTEN are zero. * Writable only at the highest implemented exception level. */ int el = arm_current_el(env); @@ -2300,22 +2524,7 @@ static CPAccessResult gt_counter_access(CPUARMState *env, int timeridx, 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 (has_el2 && timeridx == GTIMER_PHYS && - !extract32(env->cp15.cnthctl_el2, 1, 1)) { - return CP_ACCESS_TRAP_EL2; - } - } - break; - + /* fall through */ case 1: /* Check CNTHCTL_EL2.EL1PCTEN, which changes location based on E2H. */ if (has_el2 && timeridx == GTIMER_PHYS && @@ -2324,6 +2533,11 @@ static CPAccessResult gt_counter_access(CPUARMState *env, int timeridx, : !extract32(env->cp15.cnthctl_el2, 0, 1))) { return CP_ACCESS_TRAP_EL2; } + if (has_el2 && timeridx == GTIMER_VIRT) { + if (FIELD_EX64(env->cp15.cnthctl_el2, CNTHCTL, EL1TVCT)) { + return CP_ACCESS_TRAP_EL2; + } + } break; } return CP_ACCESS_OK; @@ -2367,6 +2581,11 @@ static CPAccessResult gt_timer_access(CPUARMState *env, int timeridx, } } } + if (has_el2 && timeridx == GTIMER_VIRT) { + if (FIELD_EX64(env->cp15.cnthctl_el2, CNTHCTL, EL1TVT)) { + return CP_ACCESS_TRAP_EL2; + } + } break; } return CP_ACCESS_OK; @@ -2402,7 +2621,8 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { - /* The AArch64 register view of the secure physical timer is + /* + * The AArch64 register view of the secure physical timer is * always accessible from EL3, and configurably accessible from * Secure EL1. */ @@ -2432,35 +2652,102 @@ static uint64_t gt_get_countervalue(CPUARMState *env) return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / gt_cntfrq_period_ns(cpu); } +static void gt_update_irq(ARMCPU *cpu, int timeridx) +{ + CPUARMState *env = &cpu->env; + uint64_t cnthctl = env->cp15.cnthctl_el2; + ARMSecuritySpace ss = arm_security_space(env); + /* ISTATUS && !IMASK */ + int irqstate = (env->cp15.c14_timer[timeridx].ctl & 6) == 4; + + /* + * If bit CNTHCTL_EL2.CNT[VP]MASK is set, it overrides IMASK. + * It is RES0 in Secure and NonSecure state. + */ + if ((ss == ARMSS_Root || ss == ARMSS_Realm) && + ((timeridx == GTIMER_VIRT && (cnthctl & R_CNTHCTL_CNTVMASK_MASK)) || + (timeridx == GTIMER_PHYS && (cnthctl & R_CNTHCTL_CNTPMASK_MASK)))) { + irqstate = 0; + } + + qemu_set_irq(cpu->gt_timer_outputs[timeridx], irqstate); + trace_arm_gt_update_irq(timeridx, irqstate); +} + +void gt_rme_post_el_change(ARMCPU *cpu, void *ignored) +{ + /* + * Changing security state between Root and Secure/NonSecure, which may + * happen when switching EL, can change the effective value of CNTHCTL_EL2 + * mask bits. Update the IRQ state accordingly. + */ + gt_update_irq(cpu, GTIMER_VIRT); + gt_update_irq(cpu, GTIMER_PHYS); +} + +static uint64_t gt_phys_raw_cnt_offset(CPUARMState *env) +{ + if ((env->cp15.scr_el3 & SCR_ECVEN) && + FIELD_EX64(env->cp15.cnthctl_el2, CNTHCTL, ECV) && + arm_is_el2_enabled(env) && + (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { + return env->cp15.cntpoff_el2; + } + return 0; +} + +static uint64_t gt_phys_cnt_offset(CPUARMState *env) +{ + if (arm_current_el(env) >= 2) { + return 0; + } + return gt_phys_raw_cnt_offset(env); +} + static void gt_recalc_timer(ARMCPU *cpu, int timeridx) { ARMGenericTimer *gt = &cpu->env.cp15.c14_timer[timeridx]; if (gt->ctl & 1) { - /* Timer enabled: calculate and set current ISTATUS, irq, and + /* + * Timer enabled: calculate and set current ISTATUS, irq, and * reset timer to when ISTATUS next has to change */ uint64_t offset = timeridx == GTIMER_VIRT ? - cpu->env.cp15.cntvoff_el2 : 0; + cpu->env.cp15.cntvoff_el2 : gt_phys_raw_cnt_offset(&cpu->env); uint64_t count = gt_get_countervalue(&cpu->env); /* Note that this must be unsigned 64 bit arithmetic: */ int istatus = count - offset >= gt->cval; uint64_t nexttick; - int irqstate; gt->ctl = deposit32(gt->ctl, 2, 1, istatus); - irqstate = (istatus && !(gt->ctl & 2)); - qemu_set_irq(cpu->gt_timer_outputs[timeridx], irqstate); - if (istatus) { - /* Next transition is when count rolls back over to zero */ - nexttick = UINT64_MAX; + /* + * Next transition is when (count - offset) rolls back over to 0. + * If offset > count then this is when count == offset; + * if offset <= count then this is when count == offset + 2^64 + * For the latter case we set nexttick to an "as far in future + * as possible" value and let the code below handle it. + */ + if (offset > count) { + nexttick = offset; + } else { + nexttick = UINT64_MAX; + } } else { - /* Next transition is when we hit cval */ - nexttick = gt->cval + offset; + /* + * Next transition is when (count - offset) == cval, i.e. + * when count == (cval + offset). + * If that would overflow, then again we set up the next interrupt + * for "as far in the future as possible" for the code below. + */ + if (uadd64_overflow(gt->cval, offset, &nexttick)) { + nexttick = UINT64_MAX; + } } - /* Note that the desired next expiry time might be beyond the + /* + * Note that the desired next expiry time might be beyond the * signed-64-bit range of a QEMUTimer -- in this case we just * set the timer for as far in the future as possible. When the * timer expires we will reset the timer for any remaining period. @@ -2470,14 +2757,14 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx) } else { timer_mod(cpu->gt_timer[timeridx], nexttick); } - trace_arm_gt_recalc(timeridx, irqstate, nexttick); + trace_arm_gt_recalc(timeridx, nexttick); } else { /* Timer disabled: ISTATUS and timer output always clear */ gt->ctl &= ~4; - qemu_set_irq(cpu->gt_timer_outputs[timeridx], 0); timer_del(cpu->gt_timer[timeridx]); trace_arm_gt_recalc_disabled(timeridx); } + gt_update_irq(cpu, timeridx); } static void gt_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri, @@ -2490,7 +2777,7 @@ static void gt_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t gt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) { - return gt_get_countervalue(env); + return gt_get_countervalue(env) - gt_phys_cnt_offset(env); } static uint64_t gt_virt_cnt_offset(CPUARMState *env) @@ -2539,6 +2826,9 @@ static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, case GTIMER_HYPVIRT: offset = gt_virt_cnt_offset(env); break; + case GTIMER_PHYS: + offset = gt_phys_cnt_offset(env); + break; } return (uint32_t)(env->cp15.c14_timer[timeridx].cval - @@ -2556,6 +2846,9 @@ static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, case GTIMER_HYPVIRT: offset = gt_virt_cnt_offset(env); break; + case GTIMER_PHYS: + offset = gt_phys_cnt_offset(env); + break; } trace_arm_gt_tval_write(timeridx, value); @@ -2577,13 +2870,12 @@ static void gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, /* Enable toggled */ gt_recalc_timer(cpu, timeridx); } else if ((oldval ^ value) & 2) { - /* IMASK toggled: don't need to recalculate, + /* + * IMASK toggled: don't need to recalculate, * just set the interrupt line based on ISTATUS */ - int irqstate = (oldval & 4) && !(value & 2); - - trace_arm_gt_imask_toggle(timeridx, irqstate); - qemu_set_irq(cpu->gt_timer_outputs[timeridx], irqstate); + trace_arm_gt_imask_toggle(timeridx); + gt_update_irq(cpu, timeridx); } } @@ -2621,9 +2913,6 @@ static int gt_phys_redir_timeridx(CPUARMState *env) case ARMMMUIdx_E20_0: case ARMMMUIdx_E20_2: case ARMMMUIdx_E20_2_PAN: - case ARMMMUIdx_SE20_0: - case ARMMMUIdx_SE20_2: - case ARMMMUIdx_SE20_2_PAN: return GTIMER_HYP; default: return GTIMER_PHYS; @@ -2636,9 +2925,6 @@ static int gt_virt_redir_timeridx(CPUARMState *env) case ARMMMUIdx_E20_0: case ARMMMUIdx_E20_2: case ARMMMUIdx_E20_2_PAN: - case ARMMMUIdx_SE20_0: - case ARMMMUIdx_SE20_2: - case ARMMMUIdx_SE20_2_PAN: return GTIMER_HYPVIRT; default: return GTIMER_VIRT; @@ -2715,6 +3001,49 @@ static void gt_virt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, gt_ctl_write(env, ri, GTIMER_VIRT, value); } +static void gt_cnthctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + uint32_t oldval = env->cp15.cnthctl_el2; + uint32_t valid_mask = + R_CNTHCTL_EL0PCTEN_E2H1_MASK | + R_CNTHCTL_EL0VCTEN_E2H1_MASK | + R_CNTHCTL_EVNTEN_MASK | + R_CNTHCTL_EVNTDIR_MASK | + R_CNTHCTL_EVNTI_MASK | + R_CNTHCTL_EL0VTEN_MASK | + R_CNTHCTL_EL0PTEN_MASK | + R_CNTHCTL_EL1PCTEN_E2H1_MASK | + R_CNTHCTL_EL1PTEN_MASK; + + if (cpu_isar_feature(aa64_rme, cpu)) { + valid_mask |= R_CNTHCTL_CNTVMASK_MASK | R_CNTHCTL_CNTPMASK_MASK; + } + if (cpu_isar_feature(aa64_ecv_traps, cpu)) { + valid_mask |= + R_CNTHCTL_EL1TVT_MASK | + R_CNTHCTL_EL1TVCT_MASK | + R_CNTHCTL_EL1NVPCT_MASK | + R_CNTHCTL_EL1NVVCT_MASK | + R_CNTHCTL_EVNTIS_MASK; + } + if (cpu_isar_feature(aa64_ecv, cpu)) { + valid_mask |= R_CNTHCTL_ECV_MASK; + } + + /* Clear RES0 bits */ + value &= valid_mask; + + raw_write(env, ri, value); + + if ((oldval ^ value) & R_CNTHCTL_CNTVMASK_MASK) { + gt_update_irq(cpu, GTIMER_VIRT); + } else if ((oldval ^ value) & R_CNTHCTL_CNTPMASK_MASK) { + gt_update_irq(cpu, GTIMER_PHYS); + } +} + static void gt_cntvoff_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -2894,7 +3223,8 @@ static void arm_gt_cntfrq_reset(CPUARMState *env, const ARMCPRegInfo *opaque) } static const ARMCPRegInfo generic_timer_cp_reginfo[] = { - /* Note that CNTFRQ is purely reads-as-written for the benefit + /* + * Note that CNTFRQ is purely reads-as-written for the benefit * of software; writing it doesn't actually change the timer frequency. * Our reset value matches the fixed frequency we implement the timer at. */ @@ -2939,6 +3269,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 1, .type = ARM_CP_IO, .access = PL0_RW, .accessfn = gt_ptimer_access, + .nv2_redirect_offset = 0x180 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), .resetvalue = 0, .readfn = gt_phys_redir_ctl_read, .raw_readfn = raw_read, @@ -2956,6 +3287,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 1, .type = ARM_CP_IO, .access = PL0_RW, .accessfn = gt_vtimer_access, + .nv2_redirect_offset = 0x170 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), .resetvalue = 0, .readfn = gt_virt_redir_ctl_read, .raw_readfn = raw_read, @@ -3035,6 +3367,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 2, .access = PL0_RW, .type = ARM_CP_IO, + .nv2_redirect_offset = 0x178 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), .resetvalue = 0, .accessfn = gt_ptimer_access, .readfn = gt_phys_redir_cval_read, .raw_readfn = raw_read, @@ -3052,12 +3385,14 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 2, .access = PL0_RW, .type = ARM_CP_IO, + .nv2_redirect_offset = 0x168 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), .resetvalue = 0, .accessfn = gt_vtimer_access, .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 + /* + * Secure timer -- this is actually restricted to only EL3 * and configurably Secure-EL1 via the accessfn. */ { .name = "CNTPS_TVAL_EL1", .state = ARM_CP_STATE_AA64, @@ -3085,18 +3420,67 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }, }; -static CPAccessResult e2h_access(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) +/* + * FEAT_ECV adds extra views of CNTVCT_EL0 and CNTPCT_EL0 which + * are "self-synchronizing". For QEMU all sysregs are self-synchronizing, + * so our implementations here are identical to the normal registers. + */ +static const ARMCPRegInfo gen_timer_ecv_cp_reginfo[] = { + { .name = "CNTVCTSS", .cp = 15, .crm = 14, .opc1 = 9, + .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_RAW | ARM_CP_IO, + .accessfn = gt_vct_access, + .readfn = gt_virt_cnt_read, .resetfn = arm_cp_reset_ignore, + }, + { .name = "CNTVCTSS_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 6, + .access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO, + .accessfn = gt_vct_access, .readfn = gt_virt_cnt_read, + }, + { .name = "CNTPCTSS", .cp = 15, .crm = 14, .opc1 = 8, + .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_RAW | ARM_CP_IO, + .accessfn = gt_pct_access, + .readfn = gt_cnt_read, .resetfn = arm_cp_reset_ignore, + }, + { .name = "CNTPCTSS_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 5, + .access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO, + .accessfn = gt_pct_access, .readfn = gt_cnt_read, + }, +}; + +static CPAccessResult gt_cntpoff_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) { - if (!(arm_hcr_el2_eff(env) & HCR_E2H)) { - return CP_ACCESS_TRAP; + if (arm_current_el(env) == 2 && arm_feature(env, ARM_FEATURE_EL3) && + !(env->cp15.scr_el3 & SCR_ECVEN)) { + return CP_ACCESS_TRAP_EL3; } return CP_ACCESS_OK; } +static void gt_cntpoff_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + + trace_arm_gt_cntpoff_write(value); + raw_write(env, ri, value); + gt_recalc_timer(cpu, GTIMER_PHYS); +} + +static const ARMCPRegInfo gen_timer_cntpoff_reginfo = { + .name = "CNTPOFF_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 0, .opc2 = 6, + .access = PL2_RW, .type = ARM_CP_IO, .resetvalue = 0, + .accessfn = gt_cntpoff_access, .writefn = gt_cntpoff_write, + .nv2_redirect_offset = 0x1a8, + .fieldoffset = offsetof(CPUARMState, cp15.cntpoff_el2), +}; #else -/* In user-mode most of the generic timer registers are inaccessible +/* + * In user-mode most of the generic timer registers are inaccessible * however modern kernels (4.12+) allow access to cntvct_el0 */ @@ -3104,7 +3488,8 @@ 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 + /* + * 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. */ @@ -3125,6 +3510,18 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }, }; +/* + * CNTVCTSS_EL0 has the same trap conditions as CNTVCT_EL0, so it also + * is exposed to userspace by Linux. + */ +static const ARMCPRegInfo gen_timer_ecv_cp_reginfo[] = { + { .name = "CNTVCTSS_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 6, + .access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO, + .readfn = gt_virt_cnt_read, + }, +}; + #endif static void par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) @@ -3145,7 +3542,8 @@ static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { if (ri->opc2 & 4) { - /* The ATS12NSO* operations must trap to EL3 or EL2 if executed in + /* + * The ATS12NSO* operations must trap to EL3 or EL2 if executed in * Secure EL1 (which can only happen if EL3 is AArch64). * They are simply UNDEF if executed from NS EL1. * They function normally from EL2 or EL3. @@ -3153,9 +3551,9 @@ static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri, if (arm_current_el(env) == 1) { if (arm_is_secure_below_el3(env)) { if (env->cp15.scr_el3 & SCR_EEL2) { - return CP_ACCESS_TRAP_UNCATEGORIZED_EL2; + return CP_ACCESS_TRAP_EL2; } - return CP_ACCESS_TRAP_UNCATEGORIZED_EL3; + return CP_ACCESS_TRAP_EL3; } return CP_ACCESS_TRAP_UNCATEGORIZED; } @@ -3164,27 +3562,41 @@ static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri, } #ifdef CONFIG_TCG -static uint64_t do_ats_write(CPUARMState *env, uint64_t value, - MMUAccessType access_type, ARMMMUIdx mmu_idx) +static int par_el1_shareability(GetPhysAddrResult *res) +{ + /* + * The PAR_EL1.SH field must be 0b10 for Device or Normal-NC + * memory -- see pseudocode PAREncodeShareability(). + */ + if (((res->cacheattrs.attrs & 0xf0) == 0) || + res->cacheattrs.attrs == 0x44 || res->cacheattrs.attrs == 0x40) { + return 2; + } + return res->cacheattrs.shareability; +} + +static uint64_t do_ats_write(CPUARMState *env, uint64_t value, + MMUAccessType access_type, ARMMMUIdx mmu_idx, + ARMSecuritySpace ss) { - hwaddr phys_addr; - target_ulong page_size; - int prot; bool ret; uint64_t par64; bool format64 = false; - MemTxAttrs attrs = {}; ARMMMUFaultInfo fi = {}; - ARMCacheAttrs cacheattrs = {}; + GetPhysAddrResult res = {}; - ret = get_phys_addr(env, value, access_type, mmu_idx, &phys_addr, &attrs, - &prot, &page_size, &fi, &cacheattrs); + /* + * I_MXTJT: Granule protection checks are not performed on the final address + * of a successful translation. + */ + ret = get_phys_addr_with_space_nogpc(env, value, access_type, mmu_idx, ss, + &res, &fi); /* * ATS operations only do S1 or S1+S2 translations, so we never * have to deal with the ARMCacheAttrs format for S2 only. */ - assert(!cacheattrs.is_s2_format); + assert(!res.cacheattrs.is_s2_format); if (ret) { /* @@ -3290,12 +3702,12 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, /* Create a 64-bit PAR */ par64 = (1 << 11); /* LPAE bit always set */ if (!ret) { - par64 |= phys_addr & ~0xfffULL; - if (!attrs.secure) { + par64 |= res.f.phys_addr & ~0xfffULL; + if (!res.f.attrs.secure) { par64 |= (1 << 9); /* NS */ } - par64 |= (uint64_t)cacheattrs.attrs << 56; /* ATTR */ - par64 |= cacheattrs.shareability << 7; /* SH */ + par64 |= (uint64_t)res.cacheattrs.attrs << 56; /* ATTR */ + par64 |= par_el1_shareability(&res) << 7; /* SH */ } else { uint32_t fsr = arm_fi_to_lfsc(&fi); @@ -3309,19 +3721,20 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, } } } else { - /* fsr is a DFSR/IFSR value for the short descriptor + /* + * fsr is a DFSR/IFSR value for the short descriptor * translation table format (with WnR always clear). * Convert it to a 32-bit PAR. */ if (!ret) { /* We do not set any attribute bits in the PAR */ - if (page_size == (1 << 24) + if (res.f.lg_page_size == 24 && arm_feature(env, ARM_FEATURE_V7)) { - par64 = (phys_addr & 0xff000000) | (1 << 1); + par64 = (res.f.phys_addr & 0xff000000) | (1 << 1); } else { - par64 = phys_addr & 0xfffff000; + par64 = res.f.phys_addr & 0xfffff000; } - if (!attrs.secure) { + if (!res.f.attrs.secure) { par64 |= (1 << 9); /* NS */ } } else { @@ -3342,24 +3755,23 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) uint64_t par64; ARMMMUIdx mmu_idx; int el = arm_current_el(env); - bool secure = arm_is_secure_below_el3(env); + ARMSecuritySpace ss = arm_security_space(env); switch (ri->opc2 & 6) { case 0: /* stage 1 current state PL1: ATS1CPR, ATS1CPW, ATS1CPRP, ATS1CPWP */ switch (el) { case 3: - mmu_idx = ARMMMUIdx_SE3; + mmu_idx = ARMMMUIdx_E3; break; case 2: - g_assert(!secure); /* ARMv8.4-SecEL2 is 64-bit only */ + g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */ /* fall through */ case 1: - if (ri->crm == 9 && (env->uncached_cpsr & CPSR_PAN)) { - mmu_idx = (secure ? ARMMMUIdx_Stage1_SE1_PAN - : ARMMMUIdx_Stage1_E1_PAN); + if (ri->crm == 9 && arm_pan_enabled(env)) { + mmu_idx = ARMMMUIdx_Stage1_E1_PAN; } else { - mmu_idx = secure ? ARMMMUIdx_Stage1_SE1 : ARMMMUIdx_Stage1_E1; + mmu_idx = ARMMMUIdx_Stage1_E1; } break; default: @@ -3370,14 +3782,14 @@ 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_SE10_0; + mmu_idx = ARMMMUIdx_E10_0; break; case 2: - g_assert(!secure); /* ARMv8.4-SecEL2 is 64-bit only */ + g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */ mmu_idx = ARMMMUIdx_Stage1_E0; break; case 1: - mmu_idx = secure ? ARMMMUIdx_Stage1_SE0 : ARMMMUIdx_Stage1_E0; + mmu_idx = ARMMMUIdx_Stage1_E0; break; default: g_assert_not_reached(); @@ -3386,16 +3798,18 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) case 4: /* stage 1+2 NonSecure PL1: ATS12NSOPR, ATS12NSOPW */ mmu_idx = ARMMMUIdx_E10_1; + ss = ARMSS_NonSecure; break; case 6: /* stage 1+2 NonSecure PL0: ATS12NSOUR, ATS12NSOUW */ mmu_idx = ARMMMUIdx_E10_0; + ss = ARMSS_NonSecure; break; default: g_assert_not_reached(); } - par64 = do_ats_write(env, value, access_type, mmu_idx); + par64 = do_ats_write(env, value, access_type, mmu_idx, ss); A32_BANKED_CURRENT_REG_SET(env, par, par64); #else @@ -3411,7 +3825,9 @@ 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_E2); + /* There is no SecureEL2 for AArch32. */ + par64 = do_ats_write(env, value, access_type, ARMMMUIdx_E2, + ARMSS_NonSecure); A32_BANKED_CURRENT_REG_SET(env, par, par64); #else @@ -3420,6 +3836,22 @@ static void ats1h_write(CPUARMState *env, const ARMCPRegInfo *ri, #endif /* CONFIG_TCG */ } +static CPAccessResult at_e012_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* + * R_NYXTL: instruction is UNDEFINED if it applies to an Exception level + * lower than EL3 and the combination SCR_EL3.{NSE,NS} is reserved. This can + * only happen when executing at EL3 because that combination also causes an + * illegal exception return. We don't need to check FEAT_RME either, because + * scr_write() ensures that the NSE bit is not set otherwise. + */ + if ((env->cp15.scr_el3 & (SCR_NSE | SCR_NS)) == SCR_NSE) { + return CP_ACCESS_TRAP; + } + return CP_ACCESS_OK; +} + static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -3427,7 +3859,16 @@ static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri, !(env->cp15.scr_el3 & (SCR_NS | SCR_EEL2))) { return CP_ACCESS_TRAP; } - return CP_ACCESS_OK; + return at_e012_access(env, ri, isread); +} + +static CPAccessResult at_s1e01_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_AT)) { + return CP_ACCESS_TRAP_EL2; + } + return at_e012_access(env, ri, isread); } static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3436,43 +3877,48 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, #ifdef CONFIG_TCG MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; ARMMMUIdx mmu_idx; - int secure = arm_is_secure_below_el3(env); + uint64_t hcr_el2 = arm_hcr_el2_eff(env); + bool regime_e20 = (hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE); + bool for_el3 = false; + ARMSecuritySpace ss; switch (ri->opc2 & 6) { case 0: switch (ri->opc1) { case 0: /* AT S1E1R, AT S1E1W, AT S1E1RP, AT S1E1WP */ - if (ri->crm == 9 && (env->pstate & PSTATE_PAN)) { - mmu_idx = (secure ? ARMMMUIdx_Stage1_SE1_PAN - : ARMMMUIdx_Stage1_E1_PAN); + if (ri->crm == 9 && arm_pan_enabled(env)) { + mmu_idx = regime_e20 ? + ARMMMUIdx_E20_2_PAN : ARMMMUIdx_Stage1_E1_PAN; } else { - mmu_idx = secure ? ARMMMUIdx_Stage1_SE1 : ARMMMUIdx_Stage1_E1; + mmu_idx = regime_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_Stage1_E1; } break; case 4: /* AT S1E2R, AT S1E2W */ - mmu_idx = secure ? ARMMMUIdx_SE2 : ARMMMUIdx_E2; + mmu_idx = hcr_el2 & HCR_E2H ? ARMMMUIdx_E20_2 : ARMMMUIdx_E2; break; case 6: /* AT S1E3R, AT S1E3W */ - mmu_idx = ARMMMUIdx_SE3; + mmu_idx = ARMMMUIdx_E3; + for_el3 = true; break; default: g_assert_not_reached(); } break; case 2: /* AT S1E0R, AT S1E0W */ - mmu_idx = secure ? ARMMMUIdx_Stage1_SE0 : ARMMMUIdx_Stage1_E0; + mmu_idx = regime_e20 ? ARMMMUIdx_E20_0 : ARMMMUIdx_Stage1_E0; break; case 4: /* AT S12E1R, AT S12E1W */ - mmu_idx = secure ? ARMMMUIdx_SE10_1 : ARMMMUIdx_E10_1; + mmu_idx = regime_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_E10_1; break; case 6: /* AT S12E0R, AT S12E0W */ - mmu_idx = secure ? ARMMMUIdx_SE10_0 : ARMMMUIdx_E10_0; + mmu_idx = regime_e20 ? ARMMMUIdx_E20_0 : ARMMMUIdx_E10_0; break; default: g_assert_not_reached(); } - env->cp15.par_el[1] = do_ats_write(env, value, access_type, mmu_idx); + ss = for_el3 ? arm_security_space(env) : arm_security_space_below_el3(env); + env->cp15.par_el[1] = do_ats_write(env, value, access_type, mmu_idx, ss); #else /* Handled by hardware accelerator. */ g_assert_not_reached(); @@ -3480,20 +3926,6 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, } #endif -static const ARMCPRegInfo vapa_cp_reginfo[] = { - { .name = "PAR", .cp = 15, .crn = 7, .crm = 4, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .resetvalue = 0, - .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.par_s), - offsetoflow32(CPUARMState, cp15.par_ns) }, - .writefn = par_write }, -#ifndef CONFIG_USER_ONLY - /* This underdecoding is safe because the reginfo is NO_RAW. */ - { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY, - .access = PL1_W, .accessfn = ats_access, - .writefn = ats_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, -#endif -}; - /* Return basic MPU access permission bits. */ static uint32_t simple_mpu_ap_bits(uint32_t val) { @@ -3589,8 +4021,225 @@ static void pmsav7_rgnr_write(CPUARMState *env, const ARMCPRegInfo *ri, raw_write(env, ri, value); } +static void prbar_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + + tlb_flush(CPU(cpu)); /* Mappings may have changed - purge! */ + env->pmsav8.rbar[M_REG_NS][env->pmsav7.rnr[M_REG_NS]] = value; +} + +static uint64_t prbar_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return env->pmsav8.rbar[M_REG_NS][env->pmsav7.rnr[M_REG_NS]]; +} + +static void prlar_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + + tlb_flush(CPU(cpu)); /* Mappings may have changed - purge! */ + env->pmsav8.rlar[M_REG_NS][env->pmsav7.rnr[M_REG_NS]] = value; +} + +static uint64_t prlar_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return env->pmsav8.rlar[M_REG_NS][env->pmsav7.rnr[M_REG_NS]]; +} + +static void prselr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + + /* + * Ignore writes that would select not implemented region. + * This is architecturally UNPREDICTABLE. + */ + if (value >= cpu->pmsav7_dregion) { + return; + } + + env->pmsav7.rnr[M_REG_NS] = value; +} + +static void hprbar_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + + tlb_flush(CPU(cpu)); /* Mappings may have changed - purge! */ + env->pmsav8.hprbar[env->pmsav8.hprselr] = value; +} + +static uint64_t hprbar_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return env->pmsav8.hprbar[env->pmsav8.hprselr]; +} + +static void hprlar_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + + tlb_flush(CPU(cpu)); /* Mappings may have changed - purge! */ + env->pmsav8.hprlar[env->pmsav8.hprselr] = value; +} + +static uint64_t hprlar_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return env->pmsav8.hprlar[env->pmsav8.hprselr]; +} + +static void hprenr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint32_t n; + uint32_t bit; + ARMCPU *cpu = env_archcpu(env); + + /* Ignore writes to unimplemented regions */ + int rmax = MIN(cpu->pmsav8r_hdregion, 32); + value &= MAKE_64BIT_MASK(0, rmax); + + tlb_flush(CPU(cpu)); /* Mappings may have changed - purge! */ + + /* Register alias is only valid for first 32 indexes */ + for (n = 0; n < rmax; ++n) { + bit = extract32(value, n, 1); + env->pmsav8.hprlar[n] = deposit32( + env->pmsav8.hprlar[n], 0, 1, bit); + } +} + +static uint64_t hprenr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + uint32_t n; + uint32_t result = 0x0; + ARMCPU *cpu = env_archcpu(env); + + /* Register alias is only valid for first 32 indexes */ + for (n = 0; n < MIN(cpu->pmsav8r_hdregion, 32); ++n) { + if (env->pmsav8.hprlar[n] & 0x1) { + result |= (0x1 << n); + } + } + return result; +} + +static void hprselr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + + /* + * Ignore writes that would select not implemented region. + * This is architecturally UNPREDICTABLE. + */ + if (value >= cpu->pmsav8r_hdregion) { + return; + } + + env->pmsav8.hprselr = value; +} + +static void pmsav8r_regn_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + uint8_t index = (extract32(ri->opc0, 0, 1) << 4) | + (extract32(ri->crm, 0, 3) << 1) | extract32(ri->opc2, 2, 1); + + tlb_flush(CPU(cpu)); /* Mappings may have changed - purge! */ + + if (ri->opc1 & 4) { + if (index >= cpu->pmsav8r_hdregion) { + return; + } + if (ri->opc2 & 0x1) { + env->pmsav8.hprlar[index] = value; + } else { + env->pmsav8.hprbar[index] = value; + } + } else { + if (index >= cpu->pmsav7_dregion) { + return; + } + if (ri->opc2 & 0x1) { + env->pmsav8.rlar[M_REG_NS][index] = value; + } else { + env->pmsav8.rbar[M_REG_NS][index] = value; + } + } +} + +static uint64_t pmsav8r_regn_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + ARMCPU *cpu = env_archcpu(env); + uint8_t index = (extract32(ri->opc0, 0, 1) << 4) | + (extract32(ri->crm, 0, 3) << 1) | extract32(ri->opc2, 2, 1); + + if (ri->opc1 & 4) { + if (index >= cpu->pmsav8r_hdregion) { + return 0x0; + } + if (ri->opc2 & 0x1) { + return env->pmsav8.hprlar[index]; + } else { + return env->pmsav8.hprbar[index]; + } + } else { + if (index >= cpu->pmsav7_dregion) { + return 0x0; + } + if (ri->opc2 & 0x1) { + return env->pmsav8.rlar[M_REG_NS][index]; + } else { + return env->pmsav8.rbar[M_REG_NS][index]; + } + } +} + +static const ARMCPRegInfo pmsav8r_cp_reginfo[] = { + { .name = "PRBAR", + .cp = 15, .opc1 = 0, .crn = 6, .crm = 3, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_NO_RAW, + .accessfn = access_tvm_trvm, + .readfn = prbar_read, .writefn = prbar_write }, + { .name = "PRLAR", + .cp = 15, .opc1 = 0, .crn = 6, .crm = 3, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_NO_RAW, + .accessfn = access_tvm_trvm, + .readfn = prlar_read, .writefn = prlar_write }, + { .name = "PRSELR", .resetvalue = 0, + .cp = 15, .opc1 = 0, .crn = 6, .crm = 2, .opc2 = 1, + .access = PL1_RW, .accessfn = access_tvm_trvm, + .writefn = prselr_write, + .fieldoffset = offsetof(CPUARMState, pmsav7.rnr[M_REG_NS]) }, + { .name = "HPRBAR", .resetvalue = 0, + .cp = 15, .opc1 = 4, .crn = 6, .crm = 3, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_NO_RAW, + .readfn = hprbar_read, .writefn = hprbar_write }, + { .name = "HPRLAR", + .cp = 15, .opc1 = 4, .crn = 6, .crm = 3, .opc2 = 1, + .access = PL2_RW, .type = ARM_CP_NO_RAW, + .readfn = hprlar_read, .writefn = hprlar_write }, + { .name = "HPRSELR", .resetvalue = 0, + .cp = 15, .opc1 = 4, .crn = 6, .crm = 2, .opc2 = 1, + .access = PL2_RW, + .writefn = hprselr_write, + .fieldoffset = offsetof(CPUARMState, pmsav8.hprselr) }, + { .name = "HPRENR", + .cp = 15, .opc1 = 4, .crn = 6, .crm = 1, .opc2 = 1, + .access = PL2_RW, .type = ARM_CP_NO_RAW, + .readfn = hprenr_read, .writefn = hprenr_write }, +}; + static const ARMCPRegInfo pmsav7_cp_reginfo[] = { - /* Reset for all these registers is handled in arm_cpu_reset(), + /* + * Reset for all these registers is handled in arm_cpu_reset(), * because the PMSAv7 is also used by M-profile CPUs, which do * not register cpregs but still need the state to be reset. */ @@ -3666,19 +4315,21 @@ static const ARMCPRegInfo pmsav5_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.c6_region[7]) }, }; -static void vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +static void vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { - TCR *tcr = raw_ptr(env, ri); - int maskshift = extract32(value, 0, 3); + ARMCPU *cpu = env_archcpu(env); if (!arm_feature(env, ARM_FEATURE_V8)) { if (arm_feature(env, ARM_FEATURE_LPAE) && (value & TTBCR_EAE)) { - /* Pre ARMv8 bits [21:19], [15:14] and [6:3] are UNK/SBZP when - * using Long-desciptor translation table format */ + /* + * Pre ARMv8 bits [21:19], [15:14] and [6:3] are UNK/SBZP when + * using Long-descriptor translation table format + */ value &= ~((7 << 19) | (3 << 14) | (0xf << 3)); } else if (arm_feature(env, ARM_FEATURE_EL3)) { - /* In an implementation that includes the Security Extensions + /* + * In an implementation that includes the Security Extensions * TTBCR has additional fields PD0 [4] and PD1 [5] for * Short-descriptor translation table format. */ @@ -3688,55 +4339,24 @@ static void vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, } } - /* Update the masks corresponding to the TCR bank being written - * Note that we always calculate mask and base_mask, but - * they are only used for short-descriptor tables (ie if EAE is 0); - * for long-descriptor tables the TCR fields are used differently - * and the mask and base_mask values are meaningless. - */ - tcr->raw_tcr = value; - tcr->mask = ~(((uint32_t)0xffffffffu) >> maskshift); - tcr->base_mask = ~((uint32_t)0x3fffu >> maskshift); -} - -static void vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - ARMCPU *cpu = env_archcpu(env); - TCR *tcr = raw_ptr(env, ri); - if (arm_feature(env, ARM_FEATURE_LPAE)) { - /* With LPAE the TTBCR could result in a change of ASID + /* + * With LPAE the TTBCR could result in a change of ASID * via the TTBCR.A1 bit, so do a TLB flush. */ tlb_flush(CPU(cpu)); } - /* Preserve the high half of TCR_EL1, set via TTBCR2. */ - value = deposit64(tcr->raw_tcr, 0, 32, value); - vmsa_ttbcr_raw_write(env, ri, value); -} - -static void vmsa_ttbcr_reset(CPUARMState *env, const ARMCPRegInfo *ri) -{ - TCR *tcr = raw_ptr(env, ri); - - /* Reset both the TCR as well as the masks corresponding to the bank of - * the TCR being reset. - */ - tcr->raw_tcr = 0; - tcr->mask = 0; - tcr->base_mask = 0xffffc000u; + raw_write(env, ri, value); } static void vmsa_tcr_el12_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = env_archcpu(env); - TCR *tcr = raw_ptr(env, ri); /* For AArch64 the A1 bit could result in a change of ASID, so TLB flush. */ tlb_flush(CPU(cpu)); - tcr->raw_tcr = value; + raw_write(env, ri, value); } static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3765,11 +4385,6 @@ static void vmsa_tcr_ttbr_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, uint16_t mask = ARMMMUIdxBit_E20_2 | ARMMMUIdxBit_E20_2_PAN | ARMMMUIdxBit_E20_0; - - if (arm_is_secure_below_el3(env)) { - mask >>= ARM_MMU_IDX_A_NS; - } - tlb_flush_by_mmuidx(env_cpu(env), mask); } raw_write(env, ri, value); @@ -3783,20 +4398,12 @@ static void vttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, /* * A change in VMID to the stage2 page table (Stage2) invalidates - * the combined stage 1&2 tlbs (EL10_1 and EL10_0). + * the stage2 and combined stage 1&2 tlbs (EL10_1 and EL10_0). */ - if (raw_read(env, ri) != value) { - uint16_t mask = ARMMMUIdxBit_E10_1 | - ARMMMUIdxBit_E10_1_PAN | - ARMMMUIdxBit_E10_0; - - if (arm_is_secure_below_el3(env)) { - mask >>= ARM_MMU_IDX_A_NS; - } - - tlb_flush_by_mmuidx(cs, mask); - raw_write(env, ri, value); + if (extract64(raw_read(env, ri) ^ value, 48, 16) != 0) { + tlb_flush_by_mmuidx(cs, alle1_tlbmask(env)); } + raw_write(env, ri, value); } static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = { @@ -3815,6 +4422,8 @@ static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = { { .name = "FAR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_FAR_EL1, + .nv2_redirect_offset = 0x220 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]), .resetvalue = 0, }, }; @@ -3823,35 +4432,44 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { { .name = "ESR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .crn = 5, .crm = 2, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_ESR_EL1, + .nv2_redirect_offset = 0x138 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[1]), .resetvalue = 0, }, { .name = "TTBR0_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, - .writefn = vmsa_ttbr_write, .resetvalue = 0, + .fgt = FGT_TTBR0_EL1, + .nv2_redirect_offset = 0x200 | NV2_REDIR_NV1, + .writefn = vmsa_ttbr_write, .resetvalue = 0, .raw_writefn = raw_write, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s), offsetof(CPUARMState, cp15.ttbr0_ns) } }, { .name = "TTBR1_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_tvm_trvm, - .writefn = vmsa_ttbr_write, .resetvalue = 0, + .fgt = FGT_TTBR1_EL1, + .nv2_redirect_offset = 0x210 | NV2_REDIR_NV1, + .writefn = vmsa_ttbr_write, .resetvalue = 0, .raw_writefn = raw_write, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s), offsetof(CPUARMState, cp15.ttbr1_ns) } }, { .name = "TCR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_TCR_EL1, + .nv2_redirect_offset = 0x120 | NV2_REDIR_NV1, .writefn = vmsa_tcr_el12_write, - .resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write, + .raw_writefn = raw_write, + .resetvalue = 0, .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[1]) }, { .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL1_RW, .accessfn = access_tvm_trvm, .type = ARM_CP_ALIAS, .writefn = vmsa_ttbcr_write, - .raw_writefn = vmsa_ttbcr_raw_write, - /* No offsetoflow32 -- pass the entire TCR to writefn/raw_writefn. */ - .bank_fieldoffsets = { offsetof(CPUARMState, cp15.tcr_el[3]), - offsetof(CPUARMState, cp15.tcr_el[1])} }, + .raw_writefn = raw_write, + .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tcr_el[3]), + offsetoflow32(CPUARMState, cp15.tcr_el[1])} }, }; -/* Note that unlike TTBCR, writing to TTBCR2 does not require flushing +/* + * Note that unlike TTBCR, writing to TTBCR2 does not require flushing * qemu tlbs nor adjusting cached masks. */ static const ARMCPRegInfo ttbcr2_reginfo = { @@ -3859,8 +4477,8 @@ static const ARMCPRegInfo ttbcr2_reginfo = { .access = PL1_RW, .accessfn = access_tvm_trvm, .type = ARM_CP_ALIAS, .bank_fieldoffsets = { - offsetofhigh32(CPUARMState, cp15.tcr_el[3].raw_tcr), - offsetofhigh32(CPUARMState, cp15.tcr_el[1].raw_tcr), + offsetofhigh32(CPUARMState, cp15.tcr_el[3]), + offsetofhigh32(CPUARMState, cp15.tcr_el[1]), }, }; @@ -3889,7 +4507,8 @@ static void omap_wfi_write(CPUARMState *env, const ARMCPRegInfo *ri, static void omap_cachemaint_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - /* On OMAP there are registers indicating the max/min index of dcache lines + /* + * On OMAP there are registers indicating the max/min index of dcache lines * containing a dirty line; cache flush operations have to reset these. */ env->cp15.c15_i_max = 0x000; @@ -3921,7 +4540,8 @@ static const ARMCPRegInfo omap_cp_reginfo[] = { .crm = 8, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_NO_RAW, .readfn = arm_cp_read_zero, .writefn = omap_wfi_write, }, - /* TODO: Peripheral port remap register: + /* + * TODO: Peripheral port remap register: * On OMAP2 mcr p15, 0, rn, c15, c2, 4 sets up the interrupt controller * base address at $rn & ~0xfff and map size of 0x200 << ($rn & 0xfff), * when MMU is off. @@ -3950,7 +4570,8 @@ static const ARMCPRegInfo xscale_cp_reginfo[] = { .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_xscaleauxcr), .resetvalue = 0, }, - /* XScale specific cache-lockdown: since we have no cache we NOP these + /* + * XScale specific cache-lockdown: since we have no cache we NOP these * and hope the guest does not really rely on cache behaviour. */ { .name = "XSCALE_LOCK_ICACHE_LINE", @@ -3968,7 +4589,8 @@ static const ARMCPRegInfo xscale_cp_reginfo[] = { }; static const ARMCPRegInfo dummy_c15_cp_reginfo[] = { - /* RAZ/WI the whole crn=15 space, when we don't have a more specific + /* + * RAZ/WI the whole crn=15 space, when we don't have a more specific * implementation of this implementation-defined space. * Ideally this should eventually disappear in favour of actually * implementing the correct behaviour for all cores. @@ -3988,27 +4610,28 @@ static const ARMCPRegInfo cache_dirty_status_cp_reginfo[] = { }; static const ARMCPRegInfo cache_block_ops_cp_reginfo[] = { - /* We never have a a block transfer operation in progress */ + /* We never have a block transfer operation in progress */ { .name = "BXSR", .cp = 15, .crn = 7, .crm = 12, .opc1 = 0, .opc2 = 4, .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW, .resetvalue = 0 }, /* The cache ops themselves: these all NOP for QEMU */ { .name = "IICR", .cp = 15, .crm = 5, .opc1 = 0, - .access = PL1_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, + .access = PL1_W, .type = ARM_CP_NOP | ARM_CP_64BIT }, { .name = "IDCR", .cp = 15, .crm = 6, .opc1 = 0, - .access = PL1_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, + .access = PL1_W, .type = ARM_CP_NOP | ARM_CP_64BIT }, { .name = "CDCR", .cp = 15, .crm = 12, .opc1 = 0, - .access = PL0_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, + .access = PL0_W, .type = ARM_CP_NOP | ARM_CP_64BIT }, { .name = "PIR", .cp = 15, .crm = 12, .opc1 = 1, - .access = PL0_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, + .access = PL0_W, .type = ARM_CP_NOP | ARM_CP_64BIT }, { .name = "PDR", .cp = 15, .crm = 12, .opc1 = 2, - .access = PL0_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, + .access = PL0_W, .type = ARM_CP_NOP | ARM_CP_64BIT }, { .name = "CIDCR", .cp = 15, .crm = 14, .opc1 = 0, - .access = PL1_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, + .access = PL1_W, .type = ARM_CP_NOP | ARM_CP_64BIT }, }; static const ARMCPRegInfo cache_test_clean_cp_reginfo[] = { - /* The cache test-and-clean instructions always return (1 << 30) + /* + * The cache test-and-clean instructions always return (1 << 30) * to indicate that there are no dirty cache lines. */ { .name = "TC_DCACHE", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 3, @@ -4044,7 +4667,8 @@ static uint64_t mpidr_read_val(CPUARMState *env) if (arm_feature(env, ARM_FEATURE_V7MP)) { mpidr |= (1U << 31); - /* Cores which are uniprocessor (non-coherent) + /* + * Cores which are uniprocessor (non-coherent) * but still implement the MP extensions set * bit 30. (For instance, Cortex-R5). */ @@ -4070,6 +4694,8 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { { .name = "AMAIR0", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_AMAIR_EL1, + .nv2_redirect_offset = 0x148 | NV2_REDIR_NV1, .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, @@ -4084,13 +4710,13 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { .type = ARM_CP_64BIT | ARM_CP_ALIAS, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s), offsetof(CPUARMState, cp15.ttbr0_ns) }, - .writefn = vmsa_ttbr_write, }, + .writefn = vmsa_ttbr_write, .raw_writefn = raw_write }, { .name = "TTBR1", .cp = 15, .crm = 2, .opc1 = 1, .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, }, + .writefn = vmsa_ttbr_write, .raw_writefn = raw_write }, }; static uint64_t aa64_fpcr_read(CPUARMState *env, const ARMCPRegInfo *ri) @@ -4224,9 +4850,7 @@ static CPAccessResult aa64_cacheop_poc_access(CPUARMState *env, return CP_ACCESS_OK; } -static CPAccessResult aa64_cacheop_pou_access(CPUARMState *env, - const ARMCPRegInfo *ri, - bool isread) +static CPAccessResult do_cacheop_pou_access(CPUARMState *env, uint64_t hcrflags) { /* Cache invalidate/clean to Point of Unification... */ switch (arm_current_el(env)) { @@ -4237,8 +4861,8 @@ static CPAccessResult aa64_cacheop_pou_access(CPUARMState *env, } /* fall through */ case 1: - /* ... EL1 must trap to EL2 if HCR_EL2.TPU is set. */ - if (arm_hcr_el2_eff(env) & HCR_TPU) { + /* ... EL1 must trap to EL2 if relevant HCR_EL2 flags are set. */ + if (arm_hcr_el2_eff(env) & hcrflags) { return CP_ACCESS_TRAP_EL2; } break; @@ -4246,7 +4870,20 @@ static CPAccessResult aa64_cacheop_pou_access(CPUARMState *env, return CP_ACCESS_OK; } -/* See: D4.7.2 TLB maintenance requirements and the TLB maintenance instructions +static CPAccessResult access_ticab(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + return do_cacheop_pou_access(env, HCR_TICAB | HCR_TPU); +} + +static CPAccessResult access_tocu(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + return do_cacheop_pou_access(env, HCR_TOCU | HCR_TPU); +} + +/* + * See: D4.7.2 TLB maintenance requirements and the TLB maintenance instructions * Page D4-1736 (DDI0487A.b) */ @@ -4264,11 +4901,21 @@ static int vae1_tlbmask(CPUARMState *env) ARMMMUIdxBit_E10_1_PAN | ARMMMUIdxBit_E10_0; } + return mask; +} - if (arm_is_secure_below_el3(env)) { - mask >>= ARM_MMU_IDX_A_NS; +static int vae2_tlbmask(CPUARMState *env) +{ + uint64_t hcr = arm_hcr_el2_eff(env); + uint16_t mask; + + if (hcr & HCR_E2H) { + mask = ARMMMUIdxBit_E20_2 | + ARMMMUIdxBit_E20_2_PAN | + ARMMMUIdxBit_E20_0; + } else { + mask = ARMMMUIdxBit_E2; } - return mask; } @@ -4276,7 +4923,7 @@ static int vae1_tlbmask(CPUARMState *env) static int tlbbits_for_regime(CPUARMState *env, ARMMMUIdx mmu_idx, uint64_t addr) { - uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; + uint64_t tcr = regime_tcr(env, mmu_idx); int tbi = aa64_va_parameter_tbi(tcr, mmu_idx); int select = extract64(addr, 55, 1); @@ -4295,8 +4942,23 @@ static int vae1_tlbbits(CPUARMState *env, uint64_t addr) mmu_idx = ARMMMUIdx_E10_0; } - if (arm_is_secure_below_el3(env)) { - mmu_idx &= ~ARM_MMU_IDX_A_NS; + return tlbbits_for_regime(env, mmu_idx, addr); +} + +static int vae2_tlbbits(CPUARMState *env, uint64_t addr) +{ + uint64_t hcr = arm_hcr_el2_eff(env); + ARMMMUIdx mmu_idx; + + /* + * Only the regime of the mmu_idx below is significant. + * Regime EL2&0 has two ranges with separate TBI configuration, while EL2 + * only has one. + */ + if (hcr & HCR_E2H) { + mmu_idx = ARMMMUIdx_E20_2; + } else { + mmu_idx = ARMMMUIdx_E2; } return tlbbits_for_regime(env, mmu_idx, addr); @@ -4324,37 +4986,12 @@ static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri, } } -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 { - return ARMMMUIdxBit_E10_1 | - ARMMMUIdxBit_E10_1_PAN | - ARMMMUIdxBit_E10_0; - } -} - static int e2_tlbmask(CPUARMState *env) { - if (arm_is_secure_below_el3(env)) { - return ARMMMUIdxBit_SE20_0 | - ARMMMUIdxBit_SE20_2 | - ARMMMUIdxBit_SE20_2_PAN | - ARMMMUIdxBit_SE2; - } else { - return ARMMMUIdxBit_E20_0 | - ARMMMUIdxBit_E20_2 | - ARMMMUIdxBit_E20_2_PAN | - ARMMMUIdxBit_E2; - } + return (ARMMMUIdxBit_E20_0 | + ARMMMUIdxBit_E20_2 | + ARMMMUIdxBit_E20_2_PAN | + ARMMMUIdxBit_E2); } static void tlbi_aa64_alle1_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -4381,7 +5018,7 @@ 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_SE3); + tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E3); } static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -4407,27 +5044,30 @@ 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_SE3); + tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E3); } static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - /* Invalidate by VA, EL2 + /* + * Invalidate by VA, EL2 * Currently handles both VAE2 and VALE2, since we don't support * flush-last-level-only. */ CPUState *cs = env_cpu(env); - int mask = e2_tlbmask(env); + int mask = vae2_tlbmask(env); uint64_t pageaddr = sextract64(value << 12, 0, 56); + int bits = vae2_tlbbits(env, pageaddr); - tlb_flush_page_by_mmuidx(cs, pageaddr, mask); + tlb_flush_page_bits_by_mmuidx(cs, pageaddr, mask, bits); } static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - /* Invalidate by VA, EL3 + /* + * Invalidate by VA, EL3 * Currently handles both VAE3 and VALE3, since we don't support * flush-last-level-only. */ @@ -4435,7 +5075,7 @@ 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_SE3); + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E3); } static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -4452,7 +5092,8 @@ static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - /* Invalidate by VA, EL1&0 (AArch64 version). + /* + * Invalidate by VA, EL1&0 (AArch64 version). * Currently handles all of VAE1, VAAE1, VAALE1 and VALE1, * since we don't support flush-for-specific-ASID-only or * flush-last-level-only. @@ -4473,11 +5114,9 @@ static void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { CPUState *cs = env_cpu(env); + int mask = vae2_tlbmask(env); uint64_t pageaddr = sextract64(value << 12, 0, 56); - bool secure = arm_is_secure_below_el3(env); - int mask = secure ? ARMMMUIdxBit_SE2 : ARMMMUIdxBit_E2; - int bits = tlbbits_for_regime(env, secure ? ARMMMUIdx_SE2 : ARMMMUIdx_E2, - pageaddr); + int bits = vae2_tlbbits(env, pageaddr); tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); } @@ -4487,10 +5126,47 @@ static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, { CPUState *cs = env_cpu(env); uint64_t pageaddr = sextract64(value << 12, 0, 56); - int bits = tlbbits_for_regime(env, ARMMMUIdx_SE3, pageaddr); + int bits = tlbbits_for_regime(env, ARMMMUIdx_E3, pageaddr); tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_SE3, bits); + ARMMMUIdxBit_E3, bits); +} + +static int ipas2e1_tlbmask(CPUARMState *env, int64_t value) +{ + /* + * The MSB of value is the NS field, which only applies if SEL2 + * is implemented and SCR_EL3.NS is not set (i.e. in secure mode). + */ + return (value >= 0 + && cpu_isar_feature(aa64_sel2, env_archcpu(env)) + && arm_is_secure_below_el3(env) + ? ARMMMUIdxBit_Stage2_S + : ARMMMUIdxBit_Stage2); +} + +static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = ipas2e1_tlbmask(env, value); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + + if (tlb_force_broadcast(env)) { + tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, mask); + } else { + tlb_flush_page_by_mmuidx(cs, pageaddr, mask); + } +} + +static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = ipas2e1_tlbmask(env, value); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + + tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, mask); } #ifdef TARGET_AARCH64 @@ -4499,25 +5175,45 @@ typedef struct { uint64_t length; } TLBIRange; +static ARMGranuleSize tlbi_range_tg_to_gran_size(int tg) +{ + /* + * Note that the TLBI range TG field encoding differs from both + * TG0 and TG1 encodings. + */ + switch (tg) { + case 1: + return Gran4K; + case 2: + return Gran16K; + case 3: + return Gran64K; + default: + return GranInvalid; + } +} + static TLBIRange tlbi_aa64_get_range(CPUARMState *env, ARMMMUIdx mmuidx, uint64_t value) { unsigned int page_size_granule, page_shift, num, scale, exponent; /* Extract one bit to represent the va selector in use. */ uint64_t select = sextract64(value, 36, 1); - ARMVAParameters param = aa64_va_parameters(env, select, mmuidx, true); + ARMVAParameters param = aa64_va_parameters(env, select, mmuidx, true, false); TLBIRange ret = { }; + ARMGranuleSize gran; page_size_granule = extract64(value, 46, 2); + gran = tlbi_range_tg_to_gran_size(page_size_granule); /* The granule encoded in value must match the granule in use. */ - if (page_size_granule != (param.using64k ? 3 : param.using16k ? 2 : 1)) { + if (gran != param.gran) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid tlbi page size granule %d\n", page_size_granule); return ret; } - page_shift = (page_size_granule - 1) * 2 + 12; + page_shift = arm_granule_bits(gran); num = extract64(value, 39, 5); scale = extract64(value, 44, 2); exponent = (5 * scale) + 1; @@ -4594,12 +5290,6 @@ static void tlbi_aa64_rvae1is_write(CPUARMState *env, do_rvae_write(env, value, vae1_tlbmask(env), true); } -static int vae2_tlbmask(CPUARMState *env) -{ - return (arm_is_secure_below_el3(env) - ? ARMMMUIdxBit_SE2 : ARMMMUIdxBit_E2); -} - static void tlbi_aa64_rvae2_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) @@ -4643,8 +5333,7 @@ static void tlbi_aa64_rvae3_write(CPUARMState *env, * flush-last-level-only. */ - do_rvae_write(env, value, ARMMMUIdxBit_SE3, - tlb_force_broadcast(env)); + do_rvae_write(env, value, ARMMMUIdxBit_E3, tlb_force_broadcast(env)); } static void tlbi_aa64_rvae3is_write(CPUARMState *env, @@ -4658,7 +5347,21 @@ static void tlbi_aa64_rvae3is_write(CPUARMState *env, * flush-last-level-only or inner/outer specific flushes. */ - do_rvae_write(env, value, ARMMMUIdxBit_SE3, true); + do_rvae_write(env, value, ARMMMUIdxBit_E3, true); +} + +static void tlbi_aa64_ripas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + do_rvae_write(env, value, ipas2e1_tlbmask(env, value), + tlb_force_broadcast(env)); +} + +static void tlbi_aa64_ripas2e1is_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + do_rvae_write(env, value, ipas2e1_tlbmask(env, value), true); } #endif @@ -4706,7 +5409,8 @@ static CPAccessResult sp_el0_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { if (!(env->pstate & PSTATE_SP)) { - /* Access to SP_EL0 is undefined if it's being used as + /* + * Access to SP_EL0 is undefined if it's being used as * the stack pointer. */ return CP_ACCESS_TRAP_UNCATEGORIZED; @@ -4746,7 +5450,8 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, } if (raw_read(env, ri) == value) { - /* Skip the TLB flush if nothing actually changed; Linux likes + /* + * Skip the TLB flush if nothing actually changed; Linux likes * to do a lot of pointless SCTLR writes. */ return; @@ -4757,7 +5462,7 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, /* This may enable/disable the MMU, so do a TLB flush. */ tlb_flush(CPU(cpu)); - if (ri->type & ARM_CP_SUPPRESS_TB_END) { + if (tcg_enabled() && ri->type & ARM_CP_SUPPRESS_TB_END) { /* * Normally we would always end the TB on an SCTLR write; see the * comment in ARMCPRegInfo sctlr initialization below for why Xscale @@ -4768,14 +5473,97 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, } } +static void mdcr_el3_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Some MDCR_EL3 bits affect whether PMU counters are running: + * if we are trying to change any of those then we must + * bracket this update with PMU start/finish calls. + */ + bool pmu_op = (env->cp15.mdcr_el3 ^ value) & MDCR_EL3_PMU_ENABLE_BITS; + + if (pmu_op) { + pmu_op_start(env); + } + env->cp15.mdcr_el3 = value; + if (pmu_op) { + pmu_op_finish(env); + } +} + static void sdcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - env->cp15.mdcr_el3 = value & SDCR_VALID_MASK; + /* Not all bits defined for MDCR_EL3 exist in the AArch32 SDCR */ + mdcr_el3_write(env, ri, value & SDCR_VALID_MASK); } +static void mdcr_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Some MDCR_EL2 bits affect whether PMU counters are running: + * if we are trying to change any of those then we must + * bracket this update with PMU start/finish calls. + */ + bool pmu_op = (env->cp15.mdcr_el2 ^ value) & MDCR_EL2_PMU_ENABLE_BITS; + + if (pmu_op) { + pmu_op_start(env); + } + env->cp15.mdcr_el2 = value; + if (pmu_op) { + pmu_op_finish(env); + } +} + +static CPAccessResult access_nv1(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1) { + uint64_t hcr_nv = arm_hcr_el2_eff(env) & (HCR_NV | HCR_NV1 | HCR_NV2); + + if (hcr_nv == (HCR_NV | HCR_NV1)) { + return CP_ACCESS_TRAP_EL2; + } + } + return CP_ACCESS_OK; +} + +#ifdef CONFIG_USER_ONLY +/* + * `IC IVAU` is handled to improve compatibility with JITs that dual-map their + * code to get around W^X restrictions, where one region is writable and the + * other is executable. + * + * Since the executable region is never written to we cannot detect code + * changes when running in user mode, and rely on the emulated JIT telling us + * that the code has changed by executing this instruction. + */ +static void ic_ivau_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t icache_line_mask, start_address, end_address; + const ARMCPU *cpu; + + cpu = env_archcpu(env); + + icache_line_mask = (4 << extract32(cpu->ctr, 0, 4)) - 1; + start_address = value & ~icache_line_mask; + end_address = value | icache_line_mask; + + mmap_lock(); + + tb_invalidate_phys_range(start_address, end_address); + + mmap_unlock(); +} +#endif + static const ARMCPRegInfo v8_cp_reginfo[] = { - /* Minimal set of EL0-visible registers. This will need to be expanded + /* + * Minimal set of EL0-visible registers. This will need to be expanded * significantly for system emulation of AArch64 CPUs. */ { .name = "NZCV", .state = ARM_CP_STATE_AA64, @@ -4798,6 +5586,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "DCZID_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 7, .crn = 0, .crm = 0, .access = PL0_R, .type = ARM_CP_NO_RAW, + .fgt = FGT_DCZID_EL0, .readfn = aa64_dczid_read }, { .name = "DC_ZVA", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 4, .opc2 = 1, @@ -4805,104 +5594,140 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { #ifndef CONFIG_USER_ONLY /* Avoid overhead of an access check that always passes in user-mode */ .accessfn = aa64_zva_access, + .fgt = FGT_DCZVA, #endif }, { .name = "CURRENTEL", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .opc2 = 2, .crn = 4, .crm = 2, .access = PL1_R, .type = ARM_CP_CURRENTEL }, - /* Cache ops: all NOPs since we don't emulate caches */ + /* + * Instruction cache ops. All of these except `IC IVAU` NOP because 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, - .accessfn = aa64_cacheop_pou_access }, + .fgt = FGT_ICIALLUIS, + .accessfn = access_ticab }, { .name = "IC_IALLU", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 5, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NOP, - .accessfn = aa64_cacheop_pou_access }, + .fgt = FGT_ICIALLU, + .accessfn = access_tocu }, { .name = "IC_IVAU", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 5, .opc2 = 1, - .access = PL0_W, .type = ARM_CP_NOP, - .accessfn = aa64_cacheop_pou_access }, + .access = PL0_W, + .fgt = FGT_ICIVAU, + .accessfn = access_tocu, +#ifdef CONFIG_USER_ONLY + .type = ARM_CP_NO_RAW, + .writefn = ic_ivau_write +#else + .type = ARM_CP_NOP +#endif + }, + /* Cache ops: all NOPs since we don't emulate caches */ { .name = "DC_IVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 1, .access = PL1_W, .accessfn = aa64_cacheop_poc_access, + .fgt = FGT_DCIVAC, .type = ARM_CP_NOP }, { .name = "DC_ISW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 2, + .fgt = FGT_DCISW, .access = PL1_W, .accessfn = access_tsw, .type = ARM_CP_NOP }, { .name = "DC_CVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 10, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NOP, + .fgt = FGT_DCCVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 10, .opc2 = 2, + .fgt = FGT_DCCSW, .access = PL1_W, .accessfn = access_tsw, .type = ARM_CP_NOP }, { .name = "DC_CVAU", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 11, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NOP, - .accessfn = aa64_cacheop_pou_access }, + .fgt = FGT_DCCVAU, + .accessfn = access_tocu }, { .name = "DC_CIVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 14, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NOP, + .fgt = FGT_DCCIVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CISW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2, + .fgt = FGT_DCCISW, .access = PL1_W, .accessfn = access_tsw, .type = ARM_CP_NOP }, /* TLBI operations */ { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVMALLE1IS, .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAE1IS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIASIDE1IS, .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAAE1IS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVALE1IS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAALE1IS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VMALLE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVMALLE1, .writefn = tlbi_aa64_vmalle1_write }, { .name = "TLBI_VAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAE1, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_ASIDE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIASIDE1, .writefn = tlbi_aa64_vmalle1_write }, { .name = "TLBI_VAAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAAE1, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_VALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVALE1, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_VAALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAALE1, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_IPAS2E1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NOP }, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ipas2e1is_write }, { .name = "TLBI_IPAS2LE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NOP }, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ipas2e1is_write }, { .name = "TLBI_ALLE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4, .access = PL2_W, .type = ARM_CP_NO_RAW, @@ -4913,10 +5738,12 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .writefn = tlbi_aa64_alle1is_write }, { .name = "TLBI_IPAS2E1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NOP }, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ipas2e1_write }, { .name = "TLBI_IPAS2LE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NOP }, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ipas2e1_write }, { .name = "TLBI_ALLE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4, .access = PL2_W, .type = ARM_CP_NO_RAW, @@ -4930,35 +5757,39 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .writefn = ats_write64 }, + .fgt = FGT_ATS1E1R, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, { .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 1, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .writefn = ats_write64 }, + .fgt = FGT_ATS1E1W, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, { .name = "AT_S1E0R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 2, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .writefn = ats_write64 }, + .fgt = FGT_ATS1E0R, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, { .name = "AT_S1E0W", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .writefn = ats_write64 }, + .fgt = FGT_ATS1E0W, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, { .name = "AT_S12E1R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 4, .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .writefn = ats_write64 }, + .accessfn = at_e012_access, .writefn = ats_write64 }, { .name = "AT_S12E1W", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 5, .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .writefn = ats_write64 }, + .accessfn = at_e012_access, .writefn = ats_write64 }, { .name = "AT_S12E0R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 6, .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .writefn = ats_write64 }, + .accessfn = at_e012_access, .writefn = ats_write64 }, { .name = "AT_S12E0W", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 7, .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .writefn = ats_write64 }, + .accessfn = at_e012_access, .writefn = ats_write64 }, /* AT S1E2* are elsewhere as they UNDEF from EL3 if EL2 is not present */ { .name = "AT_S1E3R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 8, .opc2 = 0, @@ -4972,15 +5803,16 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 7, .crm = 4, .opc2 = 0, .access = PL1_RW, .resetvalue = 0, + .fgt = FGT_PAR_EL1, .fieldoffset = offsetof(CPUARMState, cp15.par_el[1]), .writefn = par_write }, #endif /* 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, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, .writefn = tlbimva_is_write }, { .name = "TLBIMVAALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis, .writefn = tlbimvaa_is_write }, { .name = "TLBIMVAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, @@ -4997,25 +5829,29 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .writefn = tlbimva_hyp_is_write }, { .name = "TLBIIPAS2", .cp = 15, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 1, - .type = ARM_CP_NOP, .access = PL2_W }, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiipas2_hyp_write }, { .name = "TLBIIPAS2IS", .cp = 15, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1, - .type = ARM_CP_NOP, .access = PL2_W }, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiipas2is_hyp_write }, { .name = "TLBIIPAS2L", .cp = 15, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 5, - .type = ARM_CP_NOP, .access = PL2_W }, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiipas2_hyp_write }, { .name = "TLBIIPAS2LIS", .cp = 15, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 5, - .type = ARM_CP_NOP, .access = PL2_W }, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiipas2is_hyp_write }, /* 32 bit cache operations */ { .name = "ICIALLUIS", .cp = 15, .opc1 = 0, .crn = 7, .crm = 1, .opc2 = 0, - .type = ARM_CP_NOP, .access = PL1_W, .accessfn = aa64_cacheop_pou_access }, + .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_ticab }, { .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, .accessfn = aa64_cacheop_pou_access }, + .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tocu }, { .name = "ICIMVAU", .cp = 15, .opc1 = 0, .crn = 7, .crm = 5, .opc2 = 1, - .type = ARM_CP_NOP, .access = PL1_W, .accessfn = aa64_cacheop_pou_access }, + .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tocu }, { .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, @@ -5029,7 +5865,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "DCCSW", .cp = 15, .opc1 = 0, .crn = 7, .crm = 10, .opc2 = 2, .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, .accessfn = aa64_cacheop_pou_access }, + .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tocu }, { .name = "DCCIMVAC", .cp = 15, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 1, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = aa64_cacheop_poc_access }, { .name = "DCCISW", .cp = 15, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2, @@ -5043,14 +5879,17 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "ELR_EL1", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 1, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_nv1, + .nv2_redirect_offset = 0x230 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, elr_el[1]) }, { .name = "SPSR_EL1", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 0, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_nv1, + .nv2_redirect_offset = 0x160 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_SVC]) }, - /* We rely on the access checks not allowing the guest to write to the + /* + * We rely on the access checks not allowing the guest to write to the * state field when SPSel indicates that it's being used as the stack * pointer. */ @@ -5061,26 +5900,13 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, sp_el[0]) }, { .name = "SP_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 1, .opc2 = 0, - .access = PL2_RW, .type = ARM_CP_ALIAS, + .nv2_redirect_offset = 0x240, + .access = PL2_RW, .type = ARM_CP_ALIAS | ARM_CP_EL3_NO_EL2_KEEP, .fieldoffset = offsetof(CPUARMState, sp_el[1]) }, { .name = "SPSel", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 2, .opc2 = 0, .type = ARM_CP_NO_RAW, .access = PL1_RW, .readfn = spsel_read, .writefn = spsel_write }, - { .name = "FPEXC32_EL2", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 3, .opc2 = 0, - .access = PL2_RW, - .type = ARM_CP_ALIAS | ARM_CP_FPU | ARM_CP_EL3_NO_EL2_KEEP, - .fieldoffset = offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPEXC]) }, - { .name = "DACR32_EL2", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 0, .opc2 = 0, - .access = PL2_RW, .resetvalue = 0, .type = ARM_CP_EL3_NO_EL2_KEEP, - .writefn = dacr_write, .raw_writefn = raw_write, - .fieldoffset = offsetof(CPUARMState, cp15.dacr32_el2) }, - { .name = "IFSR32_EL2", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 0, .opc2 = 1, - .access = PL2_RW, .resetvalue = 0, .type = ARM_CP_EL3_NO_EL2_KEEP, - .fieldoffset = offsetof(CPUARMState, cp15.ifsr32_el2) }, { .name = "SPSR_IRQ", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 0, @@ -5102,16 +5928,37 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_FIQ]) }, { .name = "MDCR_EL3", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_IO, .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 3, .opc2 = 1, .resetvalue = 0, - .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.mdcr_el3) }, - { .name = "SDCR", .type = ARM_CP_ALIAS, + .access = PL3_RW, + .writefn = mdcr_el3_write, + .fieldoffset = offsetof(CPUARMState, cp15.mdcr_el3) }, + { .name = "SDCR", .type = ARM_CP_ALIAS | ARM_CP_IO, .cp = 15, .opc1 = 0, .crn = 1, .crm = 3, .opc2 = 1, .access = PL1_RW, .accessfn = access_trap_aa32s_el1, .writefn = sdcr_write, .fieldoffset = offsetoflow32(CPUARMState, cp15.mdcr_el3) }, }; +/* These are present only when EL1 supports AArch32 */ +static const ARMCPRegInfo v8_aa32_el1_reginfo[] = { + { .name = "FPEXC32_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 3, .opc2 = 0, + .access = PL2_RW, + .type = ARM_CP_ALIAS | ARM_CP_FPU | ARM_CP_EL3_NO_EL2_KEEP, + .fieldoffset = offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPEXC]) }, + { .name = "DACR32_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 0, .opc2 = 0, + .access = PL2_RW, .resetvalue = 0, .type = ARM_CP_EL3_NO_EL2_KEEP, + .writefn = dacr_write, .raw_writefn = raw_write, + .fieldoffset = offsetof(CPUARMState, cp15.dacr32_el2) }, + { .name = "IFSR32_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 0, .opc2 = 1, + .access = PL2_RW, .resetvalue = 0, .type = ARM_CP_EL3_NO_EL2_KEEP, + .fieldoffset = offsetof(CPUARMState, cp15.ifsr32_el2) }, +}; + static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) { ARMCPU *cpu = env_archcpu(env); @@ -5125,7 +5972,8 @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) if (arm_feature(env, ARM_FEATURE_EL3)) { valid_mask &= ~HCR_HCD; } else if (cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) { - /* Architecturally HCR.TSC is RES0 if EL3 is not implemented. + /* + * Architecturally HCR.TSC is RES0 if EL3 is not implemented. * However, if we're using the SMC PSCI conduit then QEMU is * effectively acting like EL3 firmware and so the guest at * EL2 should retain the ability to prevent EL1 from being @@ -5157,6 +6005,21 @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) if (cpu_isar_feature(aa64_fwb, cpu)) { valid_mask |= HCR_FWB; } + if (cpu_isar_feature(aa64_rme, cpu)) { + valid_mask |= HCR_GPF; + } + if (cpu_isar_feature(aa64_nv, cpu)) { + valid_mask |= HCR_NV | HCR_NV1 | HCR_AT; + } + if (cpu_isar_feature(aa64_nv2, cpu)) { + valid_mask |= HCR_NV2; + } + } + + if (cpu_isar_feature(any_evt, cpu)) { + valid_mask |= HCR_TTLBIS | HCR_TTLBOS | HCR_TICAB | HCR_TOCU | HCR_TID4; + } else if (cpu_isar_feature(any_half_evt, cpu)) { + valid_mask |= HCR_TICAB | HCR_TOCU | HCR_TID4; } /* Clear RES0 bits. */ @@ -5169,9 +6032,10 @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) * HCR_DC disables stage1 and enables stage2 translation * HCR_DCT enables tagging on (disabled) stage1 translation * HCR_FWB changes the interpretation of stage2 descriptor bits + * HCR_NV and HCR_NV1 affect interpretation of descriptor bits */ if ((env->cp15.hcr_el2 ^ value) & - (HCR_VM | HCR_PTW | HCR_DC | HCR_DCT | HCR_FWB)) { + (HCR_VM | HCR_PTW | HCR_DC | HCR_DCT | HCR_FWB | HCR_NV | HCR_NV1)) { tlb_flush(CPU(cpu)); } env->cp15.hcr_el2 = value; @@ -5180,14 +6044,14 @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) * Updates to VI and VF require us to update the status of * virtual interrupts, which are the logical OR of these bits * and the state of the input lines from the GIC. (This requires - * that we have the iothread lock, which is done by marking the + * that we have the BQL, which is done by marking the * reginfo structs as ARM_CP_IO.) * Note that if a write to HCR pends a VIRQ or VFIQ it is never * possible for it to be taken immediately, because VIRQ and * VFIQ are masked unless running at EL0 or EL1, and HCR * can only be written at EL2. */ - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); arm_cpu_update_virq(cpu); arm_cpu_update_vfiq(cpu); arm_cpu_update_vserr(cpu); @@ -5215,15 +6079,17 @@ static void hcr_writelow(CPUARMState *env, const ARMCPRegInfo *ri, } /* - * Return the effective value of HCR_EL2. + * Return the effective value of HCR_EL2, at the given security state. * Bits that are not included here: * RW (read from SCR_EL3.RW as needed) */ -uint64_t arm_hcr_el2_eff(CPUARMState *env) +uint64_t arm_hcr_el2_eff_secstate(CPUARMState *env, ARMSecuritySpace space) { uint64_t ret = env->cp15.hcr_el2; - if (!arm_is_el2_enabled(env)) { + assert(space != ARMSS_Root); + + if (!arm_is_el2_enabled_secstate(env, space)) { /* * "This register has no effect if EL2 is not enabled in the * current Security state". This is ARMv8.4-SecEL2 speak for @@ -5282,6 +6148,14 @@ uint64_t arm_hcr_el2_eff(CPUARMState *env) return ret; } +uint64_t arm_hcr_el2_eff(CPUARMState *env) +{ + if (arm_feature(env, ARM_FEATURE_M)) { + return 0; + } + return arm_hcr_el2_eff_secstate(env, arm_security_space_below_el3(env)); +} + /* * Corresponds to ARM pseudocode function ELIsInHost(). */ @@ -5315,7 +6189,10 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, { uint64_t valid_mask = 0; - /* No features adding bits to HCRX are implemented. */ + /* FEAT_MOPS adds MSCEn and MCE2 */ + if (cpu_isar_feature(aa64_mops, env_archcpu(env))) { + valid_mask |= HCRX_MSCEN | HCRX_MCE2; + } /* Clear RES0 bits. */ env->cp15.hcrx_el2 = value & valid_mask; @@ -5324,7 +6201,7 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, static CPAccessResult access_hxen(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { - if (arm_current_el(env) < 3 + if (arm_current_el(env) == 2 && arm_feature(env, ARM_FEATURE_EL3) && !(env->cp15.scr_el3 & SCR_HXEN)) { return CP_ACCESS_TRAP_EL3; @@ -5336,6 +6213,7 @@ static const ARMCPRegInfo hcrx_el2_reginfo = { .name = "HCRX_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 2, .access = PL2_RW, .writefn = hcrx_write, .accessfn = access_hxen, + .nv2_redirect_offset = 0xa0, .fieldoffset = offsetof(CPUARMState, cp15.hcrx_el2), }; @@ -5344,13 +6222,24 @@ uint64_t arm_hcrx_el2_eff(CPUARMState *env) { /* * The bits in this register behave as 0 for all purposes other than - * direct reads of the register if: - * - EL2 is not enabled in the current security state, - * - SCR_EL3.HXEn is 0. + * direct reads of the register if SCR_EL3.HXEn is 0. + * If EL2 is not enabled in the current security state, then the + * bit may behave as if 0, or as if 1, depending on the bit. + * For the moment, we treat the EL2-disabled case as taking + * priority over the HXEn-disabled case. This is true for the only + * bit for a feature which we implement where the answer is different + * for the two cases (MSCEn for FEAT_MOPS). + * This may need to be revisited for future bits. */ - if (!arm_is_el2_enabled(env) - || (arm_feature(env, ARM_FEATURE_EL3) - && !(env->cp15.scr_el3 & SCR_HXEN))) { + if (!arm_is_el2_enabled(env)) { + uint64_t hcrx = 0; + if (cpu_isar_feature(aa64_mops, env_archcpu(env))) { + /* MSCEn behaves as 1 if EL2 is not enabled */ + hcrx |= HCRX_MSCEN; + } + return hcrx; + } + if (arm_feature(env, ARM_FEATURE_EL3) && !(env->cp15.scr_el3 & SCR_HXEN)) { return 0; } return env->cp15.hcrx_el2; @@ -5391,7 +6280,8 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .type = ARM_CP_IO, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2), - .writefn = hcr_write }, + .nv2_redirect_offset = 0x78, + .writefn = hcr_write, .raw_writefn = raw_write }, { .name = "HCR", .state = ARM_CP_STATE_AA32, .type = ARM_CP_ALIAS | ARM_CP_IO, .cp = 15, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, @@ -5401,14 +6291,16 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 7, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "ELR_EL2", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_ALIAS, + .type = ARM_CP_ALIAS | ARM_CP_NV2_REDIRECT, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, elr_el[2]) }, { .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH, + .type = ARM_CP_NV2_REDIRECT, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) }, { .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH, + .type = ARM_CP_NV2_REDIRECT, .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) }, { .name = "HIFAR", .state = ARM_CP_STATE_AA32, @@ -5417,7 +6309,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .access = PL2_RW, .fieldoffset = offsetofhigh32(CPUARMState, cp15.far_el[2]) }, { .name = "SPSR_EL2", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_ALIAS, + .type = ARM_CP_ALIAS | ARM_CP_NV2_REDIRECT, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_HYP]) }, @@ -5463,29 +6355,29 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "TCR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 2, .access = PL2_RW, .writefn = vmsa_tcr_el12_write, - /* no .raw_writefn or .resetfn needed as we never use mask/base_mask */ + .raw_writefn = raw_write, .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[2]) }, { .name = "VTCR", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, .type = ARM_CP_ALIAS, .access = PL2_RW, .accessfn = access_el3_aa32ns, - .fieldoffset = offsetof(CPUARMState, cp15.vtcr_el2) }, + .fieldoffset = offsetoflow32(CPUARMState, cp15.vtcr_el2) }, { .name = "VTCR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .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 - */ + .nv2_redirect_offset = 0x40, + /* no .writefn needed as this can't cause an ASID change */ .fieldoffset = offsetof(CPUARMState, cp15.vtcr_el2) }, { .name = "VTTBR", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 6, .crm = 2, .type = ARM_CP_64BIT | ARM_CP_ALIAS, .access = PL2_RW, .accessfn = access_el3_aa32ns, .fieldoffset = offsetof(CPUARMState, cp15.vttbr_el2), - .writefn = vttbr_write }, + .writefn = vttbr_write, .raw_writefn = raw_write }, { .name = "VTTBR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 0, - .access = PL2_RW, .writefn = vttbr_write, + .access = PL2_RW, .writefn = vttbr_write, .raw_writefn = raw_write, + .nv2_redirect_offset = 0x20, .fieldoffset = offsetof(CPUARMState, cp15.vttbr_el2) }, { .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0, @@ -5494,10 +6386,12 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "TPIDR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 2, .access = PL2_RW, .resetvalue = 0, + .nv2_redirect_offset = 0x90, .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, .writefn = vmsa_tcr_ttbr_el2_write, + .access = PL2_RW, .resetvalue = 0, + .writefn = vmsa_tcr_ttbr_el2_write, .raw_writefn = raw_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, @@ -5547,7 +6441,8 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = tlbi_aa64_vae2is_write }, #ifndef CONFIG_USER_ONLY - /* Unlike the other EL2-related AT operations, these must + /* + * Unlike the other EL2-related AT operations, these must * UNDEF from EL3 if EL2 is not implemented, which is why we * define them here rather than with the rest of the AT ops. */ @@ -5561,7 +6456,8 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .access = PL2_W, .accessfn = at_s1e2_access, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC | ARM_CP_EL3_NO_EL2_UNDEF, .writefn = ats_write64 }, - /* The AArch32 ATS1H* operations are CONSTRAINED UNPREDICTABLE + /* + * The AArch32 ATS1H* operations are CONSTRAINED UNPREDICTABLE * if EL2 is not implemented; we choose to UNDEF. Behaviour at EL3 * with SCR.NS == 0 outside Monitor mode is UNPREDICTABLE; we choose * to behave as if SCR.NS was 1. @@ -5574,16 +6470,19 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .writefn = ats1h_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, { .name = "CNTHCTL_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 1, .opc2 = 0, - /* ARMv7 requires bit 0 and 1 to reset to 1. ARMv8 defines the + /* + * ARMv7 requires bit 0 and 1 to reset to 1. ARMv8 defines the * reset values as IMPDEF. We choose to reset to 3 to comply with * both ARMv7 and ARMv8. */ - .access = PL2_RW, .resetvalue = 3, + .access = PL2_RW, .type = ARM_CP_IO, .resetvalue = 3, + .writefn = gt_cnthctl_write, .raw_writefn = raw_write, .fieldoffset = offsetof(CPUARMState, cp15.cnthctl_el2) }, { .name = "CNTVOFF_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 0, .opc2 = 3, .access = PL2_RW, .type = ARM_CP_IO, .resetvalue = 0, .writefn = gt_cntvoff_write, + .nv2_redirect_offset = 0x60, .fieldoffset = offsetof(CPUARMState, cp15.cntvoff_el2) }, { .name = "CNTVOFF", .cp = 15, .opc1 = 4, .crm = 14, .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS | ARM_CP_IO, @@ -5622,6 +6521,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "HSTR_EL2", .state = ARM_CP_STATE_BOTH, .cp = 15, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 3, .access = PL2_RW, + .nv2_redirect_offset = 0x80, .fieldoffset = offsetof(CPUARMState, cp15.hstr_el2) }, }; @@ -5647,17 +6547,20 @@ static const ARMCPRegInfo el2_sec_cp_reginfo[] = { { .name = "VSTTBR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 6, .opc2 = 0, .access = PL2_RW, .accessfn = sel2_access, + .nv2_redirect_offset = 0x30, .fieldoffset = offsetof(CPUARMState, cp15.vsttbr_el2) }, { .name = "VSTCR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 6, .opc2 = 2, .access = PL2_RW, .accessfn = sel2_access, + .nv2_redirect_offset = 0x48, .fieldoffset = offsetof(CPUARMState, cp15.vstcr_el2) }, }; static CPAccessResult nsacr_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { - /* The NSACR is RW at EL3, and RO for NS EL1 and NS EL2. + /* + * The NSACR is RW at EL3, and RO for NS EL1 and NS EL2. * At Secure EL1 it traps to EL3 or EL2. */ if (arm_current_el(env) == 3) { @@ -5680,12 +6583,12 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { { .name = "SCR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 1, .opc2 = 0, .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.scr_el3), - .resetfn = scr_reset, .writefn = scr_write }, + .resetfn = scr_reset, .writefn = scr_write, .raw_writefn = raw_write }, { .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), - .writefn = scr_write }, + .writefn = scr_write, .raw_writefn = raw_write }, { .name = "SDER32_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 1, .opc2 = 1, .access = PL3_RW, .resetvalue = 0, @@ -5705,12 +6608,8 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { { .name = "TCR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 0, .opc2 = 2, .access = PL3_RW, - /* no .writefn needed as this can't cause an ASID change; - * we must provide a .raw_writefn and .resetfn because we handle - * reset and migration for the AArch32 TTBCR(S), which might be - * using mask and base_mask. - */ - .resetfn = vmsa_ttbcr_reset, .raw_writefn = vmsa_ttbcr_raw_write, + /* no .writefn needed as this can't cause an ASID change */ + .resetvalue = 0, .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[3]) }, { .name = "ELR_EL3", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, @@ -5780,6 +6679,44 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { }; #ifndef CONFIG_USER_ONLY + +static CPAccessResult e2h_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1) { + /* This must be a FEAT_NV access */ + return CP_ACCESS_OK; + } + if (!(arm_hcr_el2_eff(env) & HCR_E2H)) { + return CP_ACCESS_TRAP_UNCATEGORIZED; + } + return CP_ACCESS_OK; +} + +static CPAccessResult access_el1nvpct(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1) { + /* This must be a FEAT_NV access with NVx == 101 */ + if (FIELD_EX64(env->cp15.cnthctl_el2, CNTHCTL, EL1NVPCT)) { + return CP_ACCESS_TRAP_EL2; + } + } + return e2h_access(env, ri, isread); +} + +static CPAccessResult access_el1nvvct(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1) { + /* This must be a FEAT_NV access with NVx == 101 */ + if (FIELD_EX64(env->cp15.cnthctl_el2, CNTHCTL, EL1NVVCT)) { + return CP_ACCESS_TRAP_EL2; + } + } + return e2h_access(env, ri, isread); +} + /* Test if system register redirection is to occur in the current state. */ static bool redirect_for_e2h(CPUARMState *env) { @@ -5821,6 +6758,42 @@ static void el2_e2h_write(CPUARMState *env, const ARMCPRegInfo *ri, writefn(env, ri, value); } +static uint64_t el2_e2h_e12_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* Pass the EL1 register accessor its ri, not the EL12 alias ri */ + return ri->orig_readfn(env, ri->opaque); +} + +static void el2_e2h_e12_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Pass the EL1 register accessor its ri, not the EL12 alias ri */ + return ri->orig_writefn(env, ri->opaque, value); +} + +static CPAccessResult el2_e2h_e12_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1) { + /* + * This must be a FEAT_NV access (will either trap or redirect + * to memory). None of the registers with _EL12 aliases want to + * apply their trap controls for this kind of access, so don't + * call the orig_accessfn or do the "UNDEF when E2H is 0" check. + */ + return CP_ACCESS_OK; + } + /* FOO_EL12 aliases only exist when E2H is 1; otherwise they UNDEF */ + if (!(arm_hcr_el2_eff(env) & HCR_E2H)) { + return CP_ACCESS_TRAP_UNCATEGORIZED; + } + if (ri->orig_accessfn) { + return ri->orig_accessfn(env, ri->opaque, isread); + } + return CP_ACCESS_OK; +} + static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) { struct E2HAlias { @@ -5873,6 +6846,8 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) */ { 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 }, + { K(3, 0, 1, 2, 6), K(3, 4, 1, 2, 6), K(3, 5, 1, 2, 6), + "SMCR_EL1", "SMCR_EL2", "SMCR_EL12", isar_feature_aa64_sme }, { K(3, 0, 5, 6, 0), K(3, 4, 5, 6, 0), K(3, 5, 5, 6, 0), "TFSR_EL1", "TFSR_EL2", "TFSR_EL12", isar_feature_aa64_mte }, @@ -5918,6 +6893,41 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) new_reg->type |= ARM_CP_ALIAS; /* Remove PL1/PL0 access, leaving PL2/PL3 R/W in place. */ new_reg->access &= PL2_RW | PL3_RW; + /* The new_reg op fields are as per new_key, not the target reg */ + new_reg->crn = (a->new_key & CP_REG_ARM64_SYSREG_CRN_MASK) + >> CP_REG_ARM64_SYSREG_CRN_SHIFT; + new_reg->crm = (a->new_key & CP_REG_ARM64_SYSREG_CRM_MASK) + >> CP_REG_ARM64_SYSREG_CRM_SHIFT; + new_reg->opc0 = (a->new_key & CP_REG_ARM64_SYSREG_OP0_MASK) + >> CP_REG_ARM64_SYSREG_OP0_SHIFT; + new_reg->opc1 = (a->new_key & CP_REG_ARM64_SYSREG_OP1_MASK) + >> CP_REG_ARM64_SYSREG_OP1_SHIFT; + new_reg->opc2 = (a->new_key & CP_REG_ARM64_SYSREG_OP2_MASK) + >> CP_REG_ARM64_SYSREG_OP2_SHIFT; + new_reg->opaque = src_reg; + new_reg->orig_readfn = src_reg->readfn ?: raw_read; + new_reg->orig_writefn = src_reg->writefn ?: raw_write; + new_reg->orig_accessfn = src_reg->accessfn; + if (!new_reg->raw_readfn) { + new_reg->raw_readfn = raw_read; + } + if (!new_reg->raw_writefn) { + new_reg->raw_writefn = raw_write; + } + new_reg->readfn = el2_e2h_e12_read; + new_reg->writefn = el2_e2h_e12_write; + new_reg->accessfn = el2_e2h_e12_access; + + /* + * If the _EL1 register is redirected to memory by FEAT_NV2, + * then it shares the offset with the _EL12 register, + * and which one is redirected depends on HCR_EL2.NV1. + */ + if (new_reg->nv2_redirect_offset) { + assert(new_reg->nv2_redirect_offset & NV2_REDIR_NV1); + new_reg->nv2_redirect_offset &= ~NV2_REDIR_NV1; + new_reg->nv2_redirect_offset |= NV2_REDIR_NO_NV1; + } ok = g_hash_table_insert(cpu->cp_regs, (gpointer)(uintptr_t)a->new_key, new_reg); @@ -5971,111 +6981,6 @@ static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } -static void oslar_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Writes to OSLAR_EL1 may update the OS lock status, which can be - * read via a bit in OSLSR_EL1. - */ - int oslock; - - if (ri->state == ARM_CP_STATE_AA32) { - oslock = (value == 0xC5ACCE55); - } else { - oslock = value & 1; - } - - env->cp15.oslsr_el1 = deposit32(env->cp15.oslsr_el1, 1, 1, oslock); -} - -static const ARMCPRegInfo debug_cp_reginfo[] = { - /* DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped - * debug components. The AArch64 version of DBGDRAR is named MDRAR_EL1; - * unlike DBGDRAR it is never accessible from EL0. - * DBGDSAR is deprecated and must RAZ from v8 anyway, so it has no AArch64 - * accessor. - */ - { .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL0_R, .accessfn = access_tdra, - .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "MDRAR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, - .access = PL1_R, .accessfn = access_tdra, - .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "DBGDSAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL0_R, .accessfn = access_tdra, - .type = ARM_CP_CONST, .resetvalue = 0 }, - /* Monitor debug system control register; the 32-bit alias is DBGDSCRext. */ - { .name = "MDSCR_EL1", .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, - .access = PL1_RW, .accessfn = access_tda, - .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), - .resetvalue = 0 }, - /* - * MDCCSR_EL0[30:29] map to EDSCR[30:29]. Simply RAZ as the external - * Debug Communication Channel is not implemented. - */ - { .name = "MDCCSR_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 1, .opc2 = 0, - .access = PL0_R, .accessfn = access_tda, - .type = ARM_CP_CONST, .resetvalue = 0 }, - /* - * DBGDSCRint[15,12,5:2] map to MDSCR_EL1[15,12,5:2]. Map all bits as - * it is unlikely a guest will care. - * We don't implement the configurable EL0 access. - */ - { .name = "DBGDSCRint", .state = ARM_CP_STATE_AA32, - .cp = 14, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, - .type = ARM_CP_ALIAS, - .access = PL1_R, .accessfn = access_tda, - .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), }, - { .name = "OSLAR_EL1", .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4, - .access = PL1_W, .type = ARM_CP_NO_RAW, - .accessfn = access_tdosa, - .writefn = oslar_write }, - { .name = "OSLSR_EL1", .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 4, - .access = PL1_R, .resetvalue = 10, - .accessfn = access_tdosa, - .fieldoffset = offsetof(CPUARMState, cp15.oslsr_el1) }, - /* Dummy OSDLR_EL1: 32-bit Linux will read this */ - { .name = "OSDLR_EL1", .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 3, .opc2 = 4, - .access = PL1_RW, .accessfn = access_tdosa, - .type = ARM_CP_NOP }, - /* Dummy DBGVCR: Linux wants to clear this on startup, but we don't - * implement vector catch debug events yet. - */ - { .name = "DBGVCR", - .cp = 14, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0, - .access = PL1_RW, .accessfn = access_tda, - .type = ARM_CP_NOP }, - /* Dummy DBGVCR32_EL2 (which is only for a 64-bit hypervisor - * to save and restore a 32-bit guest's DBGVCR) - */ - { .name = "DBGVCR32_EL2", .state = ARM_CP_STATE_AA64, - .opc0 = 2, .opc1 = 4, .crn = 0, .crm = 7, .opc2 = 0, - .access = PL2_RW, .accessfn = access_tda, - .type = ARM_CP_NOP | ARM_CP_EL3_NO_EL2_KEEP }, - /* Dummy MDCCINT_EL1, since we don't implement the Debug Communications - * Channel but Linux may try to access this register. The 32-bit - * alias is DBGDCCINT. - */ - { .name = "MDCCINT_EL1", .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, - .access = PL1_RW, .accessfn = access_tda, - .type = ARM_CP_NOP }, -}; - -static const ARMCPRegInfo debug_lpae_cp_reginfo[] = { - /* 64 bit access versions of the (dummy) debug registers */ - { .name = "DBGDRAR", .cp = 14, .crm = 1, .opc1 = 0, - .access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 }, - { .name = "DBGDSAR", .cp = 14, .crm = 2, .opc1 = 0, - .access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 }, -}; - /* * Check for traps to RAS registers, which are controlled * by HCR_EL2.TERR and SCR_EL3.TERR. @@ -6139,6 +7044,10 @@ static void disr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t val) * ERRSELR_EL1 * may generate UNDEFINED, which is the effect we get by not * listing them at all. + * + * These registers have fine-grained trap bits, but UNDEF-to-EL1 + * is higher priority than FGT-to-EL2 so we do not need to list them + * in order to check for an FGT. */ static const ARMCPRegInfo minimal_ras_reginfo[] = { { .name = "DISR_EL1", .state = ARM_CP_STATE_BOTH, @@ -6148,12 +7057,15 @@ static const ARMCPRegInfo minimal_ras_reginfo[] = { { .name = "ERRIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 3, .opc2 = 0, .access = PL1_R, .accessfn = access_terr, + .fgt = FGT_ERRIDR_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "VDISR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 1, .opc2 = 1, + .nv2_redirect_offset = 0x500, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.vdisr_el2) }, { .name = "VSESR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 3, + .nv2_redirect_offset = 0x508, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.vsesr_el2) }, }; @@ -6212,26 +7124,93 @@ int sve_exception_el(CPUARMState *env, int el) return 0; } +/* + * Return the exception level to which exceptions should be taken for SME. + * C.f. the ARM pseudocode function CheckSMEAccess. + */ +int sme_exception_el(CPUARMState *env, int el) +{ +#ifndef CONFIG_USER_ONLY + if (el <= 1 && !el_is_in_host(env, el)) { + switch (FIELD_EX64(env->cp15.cpacr_el1, CPACR_EL1, SMEN)) { + case 1: + if (el != 0) { + break; + } + /* fall through */ + case 0: + case 2: + return 1; + } + } + + if (el <= 2 && arm_is_el2_enabled(env)) { + /* CPTR_EL2 changes format with HCR_EL2.E2H (regardless of TGE). */ + if (env->cp15.hcr_el2 & HCR_E2H) { + switch (FIELD_EX64(env->cp15.cptr_el[2], CPTR_EL2, SMEN)) { + case 1: + if (el != 0 || !(env->cp15.hcr_el2 & HCR_TGE)) { + break; + } + /* fall through */ + case 0: + case 2: + return 2; + } + } else { + if (FIELD_EX64(env->cp15.cptr_el[2], CPTR_EL2, TSM)) { + return 2; + } + } + } + + /* CPTR_EL3. Since ESM is negative we must check for EL3. */ + if (arm_feature(env, ARM_FEATURE_EL3) + && !FIELD_EX64(env->cp15.cptr_el[3], CPTR_EL3, ESM)) { + return 3; + } +#endif + return 0; +} + /* * Given that SVE is enabled, return the vector length for EL. */ -uint32_t sve_vqm1_for_el(CPUARMState *env, int el) +uint32_t sve_vqm1_for_el_sm(CPUARMState *env, int el, bool sm) { ARMCPU *cpu = env_archcpu(env); - uint32_t len = cpu->sve_max_vq - 1; + uint64_t *cr = env->vfp.zcr_el; + uint32_t map = cpu->sve_vq.map; + uint32_t len = ARM_MAX_VQ - 1; + + if (sm) { + cr = env->vfp.smcr_el; + map = cpu->sme_vq.map; + } if (el <= 1 && !el_is_in_host(env, el)) { - len = MIN(len, 0xf & (uint32_t)env->vfp.zcr_el[1]); + len = MIN(len, 0xf & (uint32_t)cr[1]); } if (el <= 2 && arm_feature(env, ARM_FEATURE_EL2)) { - len = MIN(len, 0xf & (uint32_t)env->vfp.zcr_el[2]); + len = MIN(len, 0xf & (uint32_t)cr[2]); } if (arm_feature(env, ARM_FEATURE_EL3)) { - len = MIN(len, 0xf & (uint32_t)env->vfp.zcr_el[3]); + len = MIN(len, 0xf & (uint32_t)cr[3]); } - len = 31 - clz32(cpu->sve_vq_map & MAKE_64BIT_MASK(0, len + 1)); - return len; + map &= MAKE_64BIT_MASK(0, len + 1); + if (map != 0) { + return 31 - clz32(map); + } + + /* Bit 0 is always set for Normal SVE -- not so for Streaming SVE. */ + assert(sm); + return ctz32(cpu->sme_vq.map); +} + +uint32_t sve_vqm1_for_el(CPUARMState *env, int el) +{ + return sve_vqm1_for_el_sm(env, el, FIELD_EX64(env->svcr, SVCR, SM)); } static void zcr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -6258,6 +7237,7 @@ static void zcr_write(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo zcr_reginfo[] = { { .name = "ZCR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 0, + .nv2_redirect_offset = 0x1e0 | NV2_REDIR_NV1, .access = PL1_RW, .type = ARM_CP_SVE, .fieldoffset = offsetof(CPUARMState, vfp.zcr_el[1]), .writefn = zcr_write, .raw_writefn = raw_write }, @@ -6273,346 +7253,249 @@ static const ARMCPRegInfo zcr_reginfo[] = { .writefn = zcr_write, .raw_writefn = raw_write }, }; -void hw_watchpoint_update(ARMCPU *cpu, int n) +#ifdef TARGET_AARCH64 +static CPAccessResult access_tpidr2(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { - CPUARMState *env = &cpu->env; - vaddr len = 0; - vaddr wvr = env->cp15.dbgwvr[n]; - uint64_t wcr = env->cp15.dbgwcr[n]; - int mask; - int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; + int el = arm_current_el(env); - if (env->cpu_watchpoint[n]) { - cpu_watchpoint_remove_by_ref(CPU(cpu), env->cpu_watchpoint[n]); - env->cpu_watchpoint[n] = NULL; - } - - if (!FIELD_EX64(wcr, DBGWCR, E)) { - /* E bit clear : watchpoint disabled */ - return; - } - - switch (FIELD_EX64(wcr, DBGWCR, LSC)) { - case 0: - /* LSC 00 is reserved and must behave as if the wp is disabled */ - return; - case 1: - flags |= BP_MEM_READ; - break; - case 2: - flags |= BP_MEM_WRITE; - break; - case 3: - flags |= BP_MEM_ACCESS; - break; - } - - /* Attempts to use both MASK and BAS fields simultaneously are - * CONSTRAINED UNPREDICTABLE; we opt to ignore BAS in this case, - * thus generating a watchpoint for every byte in the masked region. - */ - mask = FIELD_EX64(wcr, DBGWCR, MASK); - if (mask == 1 || mask == 2) { - /* Reserved values of MASK; we must act as if the mask value was - * some non-reserved value, or as if the watchpoint were disabled. - * We choose the latter. - */ - return; - } else if (mask) { - /* Watchpoint covers an aligned area up to 2GB in size */ - len = 1ULL << mask; - /* If masked bits in WVR are not zero it's CONSTRAINED UNPREDICTABLE - * whether the watchpoint fires when the unmasked bits match; we opt - * to generate the exceptions. - */ - wvr &= ~(len - 1); - } else { - /* Watchpoint covers bytes defined by the byte address select bits */ - int bas = FIELD_EX64(wcr, DBGWCR, BAS); - int basstart; - - 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 (el == 0) { + uint64_t sctlr = arm_sctlr(env, el); + if (!(sctlr & SCTLR_EnTP2)) { + return CP_ACCESS_TRAP; } - - 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. - * We choose to ignore any non-zero bits after the first range of 1s. - */ - basstart = ctz32(bas); - len = cto32(bas >> basstart); - wvr += basstart; } - - cpu_watchpoint_insert(CPU(cpu), wvr, len, flags, - &env->cpu_watchpoint[n]); + /* TODO: FEAT_FGT */ + if (el < 3 + && arm_feature(env, ARM_FEATURE_EL3) + && !(env->cp15.scr_el3 & SCR_ENTP2)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; } -void hw_watchpoint_update_all(ARMCPU *cpu) +static CPAccessResult access_smprimap(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { - int i; - CPUARMState *env = &cpu->env; - - /* Completely clear out existing QEMU watchpoints and our array, to - * avoid possible stale entries following migration load. - */ - cpu_watchpoint_remove_all(CPU(cpu), BP_CPU); - memset(env->cpu_watchpoint, 0, sizeof(env->cpu_watchpoint)); - - for (i = 0; i < ARRAY_SIZE(cpu->env.cpu_watchpoint); i++) { - hw_watchpoint_update(cpu, i); + /* If EL1 this is a FEAT_NV access and CPTR_EL3.ESM doesn't apply */ + if (arm_current_el(env) == 2 + && arm_feature(env, ARM_FEATURE_EL3) + && !FIELD_EX64(env->cp15.cptr_el[3], CPTR_EL3, ESM)) { + return CP_ACCESS_TRAP_EL3; } + return CP_ACCESS_OK; } -static void dbgwvr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +static CPAccessResult access_smpri(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { - ARMCPU *cpu = env_archcpu(env); - int i = ri->crm; + if (arm_current_el(env) < 3 + && arm_feature(env, ARM_FEATURE_EL3) + && !FIELD_EX64(env->cp15.cptr_el[3], CPTR_EL3, ESM)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +/* ResetSVEState */ +static void arm_reset_sve_state(CPUARMState *env) +{ + memset(env->vfp.zregs, 0, sizeof(env->vfp.zregs)); + /* Recall that FFR is stored as pregs[16]. */ + memset(env->vfp.pregs, 0, sizeof(env->vfp.pregs)); + vfp_set_fpcr(env, 0x0800009f); +} + +void aarch64_set_svcr(CPUARMState *env, uint64_t new, uint64_t mask) +{ + uint64_t change = (env->svcr ^ new) & mask; + + if (change == 0) { + return; + } + env->svcr ^= change; + + if (change & R_SVCR_SM_MASK) { + arm_reset_sve_state(env); + } /* - * Bits [1:0] are RES0. + * ResetSMEState. * - * It is IMPLEMENTATION DEFINED whether [63:49] ([63:53] with FEAT_LVA) - * are hardwired to the value of bit [48] ([52] with FEAT_LVA), or if - * they contain the value written. It is CONSTRAINED UNPREDICTABLE - * whether the RESS bits are ignored when comparing an address. - * - * Therefore we are allowed to compare the entire register, which lets - * us avoid considering whether or not FEAT_LVA is actually enabled. + * SetPSTATE_ZA zeros on enable and disable. We can zero this only + * on enable: while disabled, the storage is inaccessible and the + * value does not matter. We're not saving the storage in vmstate + * when disabled either. */ - value &= ~3ULL; + if (change & new & R_SVCR_ZA_MASK) { + memset(env->zarray, 0, sizeof(env->zarray)); + } + if (tcg_enabled()) { + arm_rebuild_hflags(env); + } +} + +static void svcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + aarch64_set_svcr(env, value, -1); +} + +static void smcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + int cur_el = arm_current_el(env); + int old_len = sve_vqm1_for_el(env, cur_el); + int new_len; + + QEMU_BUILD_BUG_ON(ARM_MAX_VQ > R_SMCR_LEN_MASK + 1); + value &= R_SMCR_LEN_MASK | R_SMCR_FA64_MASK; raw_write(env, ri, value); - hw_watchpoint_update(cpu, i); -} - -static void dbgwcr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - ARMCPU *cpu = env_archcpu(env); - int i = ri->crm; - - raw_write(env, ri, value); - hw_watchpoint_update(cpu, i); -} - -void hw_breakpoint_update(ARMCPU *cpu, int n) -{ - CPUARMState *env = &cpu->env; - uint64_t bvr = env->cp15.dbgbvr[n]; - uint64_t bcr = env->cp15.dbgbcr[n]; - vaddr addr; - int bt; - int flags = BP_CPU; - - if (env->cpu_breakpoint[n]) { - cpu_breakpoint_remove_by_ref(CPU(cpu), env->cpu_breakpoint[n]); - env->cpu_breakpoint[n] = NULL; - } - - if (!extract64(bcr, 0, 1)) { - /* E bit clear : watchpoint disabled */ - return; - } - - bt = extract64(bcr, 20, 4); - - switch (bt) { - case 4: /* unlinked address mismatch (reserved if AArch64) */ - case 5: /* linked address mismatch (reserved if AArch64) */ - qemu_log_mask(LOG_UNIMP, - "arm: address mismatch breakpoint types not implemented\n"); - return; - case 0: /* unlinked address match */ - case 1: /* linked address match */ - { - /* - * Bits [1:0] are RES0. - * - * It is IMPLEMENTATION DEFINED whether bits [63:49] - * ([63:53] for FEAT_LVA) are hardwired to a copy of the sign bit - * of the VA field ([48] or [52] for FEAT_LVA), or whether the - * value is read as written. It is CONSTRAINED UNPREDICTABLE - * whether the RESS bits are ignored when comparing an address. - * Therefore we are allowed to compare the entire register, which - * lets us avoid considering whether FEAT_LVA is actually enabled. - * - * The BAS field is used to allow setting breakpoints on 16-bit - * wide instructions; it is CONSTRAINED UNPREDICTABLE whether - * a bp will fire if the addresses covered by the bp and the addresses - * covered by the insn overlap but the insn doesn't start at the - * start of the bp address range. We choose to require the insn and - * the bp to have the same address. The constraints on writing to - * BAS enforced in dbgbcr_write mean we have only four cases: - * 0b0000 => no breakpoint - * 0b0011 => breakpoint on addr - * 0b1100 => breakpoint on addr + 2 - * 0b1111 => breakpoint on addr - * See also figure D2-3 in the v8 ARM ARM (DDI0487A.c). - */ - int bas = extract64(bcr, 5, 4); - addr = bvr & ~3ULL; - if (bas == 0) { - return; - } - if (bas == 0xc) { - addr += 2; - } - break; - } - case 2: /* unlinked context ID match */ - case 8: /* unlinked VMID match (reserved if no EL2) */ - case 10: /* unlinked context ID and VMID match (reserved if no EL2) */ - qemu_log_mask(LOG_UNIMP, - "arm: unlinked context breakpoint types not implemented\n"); - return; - case 9: /* linked VMID match (reserved if no EL2) */ - case 11: /* linked context ID and VMID match (reserved if no EL2) */ - case 3: /* linked context ID match */ - default: - /* We must generate no events for Linked context matches (unless - * they are linked to by some other bp/wp, which is handled in - * updates for the linking bp/wp). We choose to also generate no events - * for reserved values. - */ - return; - } - - cpu_breakpoint_insert(CPU(cpu), addr, flags, &env->cpu_breakpoint[n]); -} - -void hw_breakpoint_update_all(ARMCPU *cpu) -{ - int i; - CPUARMState *env = &cpu->env; - - /* Completely clear out existing QEMU breakpoints and our array, to - * avoid possible stale entries following migration load. - */ - cpu_breakpoint_remove_all(CPU(cpu), BP_CPU); - memset(env->cpu_breakpoint, 0, sizeof(env->cpu_breakpoint)); - - for (i = 0; i < ARRAY_SIZE(cpu->env.cpu_breakpoint); i++) { - hw_breakpoint_update(cpu, i); - } -} - -static void dbgbvr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - ARMCPU *cpu = env_archcpu(env); - int i = ri->crm; - - raw_write(env, ri, value); - hw_breakpoint_update(cpu, i); -} - -static void dbgbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - ARMCPU *cpu = env_archcpu(env); - int i = ri->crm; - - /* BAS[3] is a read-only copy of BAS[2], and BAS[1] a read-only - * copy of BAS[0]. - */ - value = deposit64(value, 6, 1, extract64(value, 5, 1)); - value = deposit64(value, 8, 1, extract64(value, 7, 1)); - - raw_write(env, ri, value); - hw_breakpoint_update(cpu, i); -} - -static void define_debug_regs(ARMCPU *cpu) -{ - /* Define v7 and v8 architectural debug registers. - * These are just dummy implementations for now. - */ - int i; - int wrps, brps, ctx_cmps; /* - * The Arm ARM says DBGDIDR is optional and deprecated if EL1 cannot - * use AArch32. Given that bit 15 is RES1, if the value is 0 then - * the register must not exist for this cpu. + * Note that it is CONSTRAINED UNPREDICTABLE what happens to ZA storage + * when SVL is widened (old values kept, or zeros). Choose to keep the + * current values for simplicity. But for QEMU internals, we must still + * apply the narrower SVL to the Zregs and Pregs -- see the comment + * above aarch64_sve_narrow_vq. */ - if (cpu->isar.dbgdidr != 0) { - 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->isar.dbgdidr, - }; - define_one_arm_cp_reg(cpu, &dbgdidr); - } - - brps = arm_num_brps(cpu); - wrps = arm_num_wrps(cpu); - ctx_cmps = arm_num_ctx_cmps(cpu); - - assert(ctx_cmps <= brps); - - define_arm_cp_regs(cpu, debug_cp_reginfo); - - if (arm_feature(&cpu->env, ARM_FEATURE_LPAE)) { - define_arm_cp_regs(cpu, debug_lpae_cp_reginfo); - } - - for (i = 0; i < brps; i++) { - char *dbgbvr_el1_name = g_strdup_printf("DBGBVR%d_EL1", i); - char *dbgbcr_el1_name = g_strdup_printf("DBGBCR%d_EL1", i); - ARMCPRegInfo dbgregs[] = { - { .name = dbgbvr_el1_name, .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 4, - .access = PL1_RW, .accessfn = access_tda, - .fieldoffset = offsetof(CPUARMState, cp15.dbgbvr[i]), - .writefn = dbgbvr_write, .raw_writefn = raw_write - }, - { .name = dbgbcr_el1_name, .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 5, - .access = PL1_RW, .accessfn = access_tda, - .fieldoffset = offsetof(CPUARMState, cp15.dbgbcr[i]), - .writefn = dbgbcr_write, .raw_writefn = raw_write - }, - }; - define_arm_cp_regs(cpu, dbgregs); - g_free(dbgbvr_el1_name); - g_free(dbgbcr_el1_name); - } - - for (i = 0; i < wrps; i++) { - char *dbgwvr_el1_name = g_strdup_printf("DBGWVR%d_EL1", i); - char *dbgwcr_el1_name = g_strdup_printf("DBGWCR%d_EL1", i); - ARMCPRegInfo dbgregs[] = { - { .name = dbgwvr_el1_name, .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6, - .access = PL1_RW, .accessfn = access_tda, - .fieldoffset = offsetof(CPUARMState, cp15.dbgwvr[i]), - .writefn = dbgwvr_write, .raw_writefn = raw_write - }, - { .name = dbgwcr_el1_name, .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 7, - .access = PL1_RW, .accessfn = access_tda, - .fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]), - .writefn = dbgwcr_write, .raw_writefn = raw_write - }, - }; - define_arm_cp_regs(cpu, dbgregs); - g_free(dbgwvr_el1_name); - g_free(dbgwcr_el1_name); + new_len = sve_vqm1_for_el(env, cur_el); + if (new_len < old_len) { + aarch64_sve_narrow_vq(env, new_len + 1); } } +static const ARMCPRegInfo sme_reginfo[] = { + { .name = "TPIDR2_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 13, .crm = 0, .opc2 = 5, + .access = PL0_RW, .accessfn = access_tpidr2, + .fgt = FGT_NTPIDR2_EL0, + .fieldoffset = offsetof(CPUARMState, cp15.tpidr2_el0) }, + { .name = "SVCR", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 4, .crm = 2, .opc2 = 2, + .access = PL0_RW, .type = ARM_CP_SME, + .fieldoffset = offsetof(CPUARMState, svcr), + .writefn = svcr_write, .raw_writefn = raw_write }, + { .name = "SMCR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 6, + .nv2_redirect_offset = 0x1f0 | NV2_REDIR_NV1, + .access = PL1_RW, .type = ARM_CP_SME, + .fieldoffset = offsetof(CPUARMState, vfp.smcr_el[1]), + .writefn = smcr_write, .raw_writefn = raw_write }, + { .name = "SMCR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 6, + .access = PL2_RW, .type = ARM_CP_SME, + .fieldoffset = offsetof(CPUARMState, vfp.smcr_el[2]), + .writefn = smcr_write, .raw_writefn = raw_write }, + { .name = "SMCR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 2, .opc2 = 6, + .access = PL3_RW, .type = ARM_CP_SME, + .fieldoffset = offsetof(CPUARMState, vfp.smcr_el[3]), + .writefn = smcr_write, .raw_writefn = raw_write }, + { .name = "SMIDR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 0, .crm = 0, .opc2 = 6, + .access = PL1_R, .accessfn = access_aa64_tid1, + /* + * IMPLEMENTOR = 0 (software) + * REVISION = 0 (implementation defined) + * SMPS = 0 (no streaming execution priority in QEMU) + * AFFINITY = 0 (streaming sve mode not shared with other PEs) + */ + .type = ARM_CP_CONST, .resetvalue = 0, }, + /* + * Because SMIDR_EL1.SMPS is 0, SMPRI_EL1 and SMPRIMAP_EL2 are RES 0. + */ + { .name = "SMPRI_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 4, + .access = PL1_RW, .accessfn = access_smpri, + .fgt = FGT_NSMPRI_EL1, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "SMPRIMAP_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 5, + .nv2_redirect_offset = 0x1f8, + .access = PL2_RW, .accessfn = access_smprimap, + .type = ARM_CP_CONST, .resetvalue = 0 }, +}; + +static void tlbi_aa64_paall_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush(cs); +} + +static void gpccr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* L0GPTSZ is RO; other bits not mentioned are RES0. */ + uint64_t rw_mask = R_GPCCR_PPS_MASK | R_GPCCR_IRGN_MASK | + R_GPCCR_ORGN_MASK | R_GPCCR_SH_MASK | R_GPCCR_PGS_MASK | + R_GPCCR_GPC_MASK | R_GPCCR_GPCP_MASK; + + env->cp15.gpccr_el3 = (value & rw_mask) | (env->cp15.gpccr_el3 & ~rw_mask); +} + +static void gpccr_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + env->cp15.gpccr_el3 = FIELD_DP64(0, GPCCR, L0GPTSZ, + env_archcpu(env)->reset_l0gptsz); +} + +static void tlbi_aa64_paallos_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_all_cpus_synced(cs); +} + +static const ARMCPRegInfo rme_reginfo[] = { + { .name = "GPCCR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 1, .opc2 = 6, + .access = PL3_RW, .writefn = gpccr_write, .resetfn = gpccr_reset, + .fieldoffset = offsetof(CPUARMState, cp15.gpccr_el3) }, + { .name = "GPTBR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 1, .opc2 = 4, + .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.gptbr_el3) }, + { .name = "MFAR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 6, .crm = 0, .opc2 = 5, + .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.mfar_el3) }, + { .name = "TLBI_PAALL", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 4, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_paall_write }, + { .name = "TLBI_PAALLOS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 4, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_paallos_write }, + /* + * QEMU does not have a way to invalidate by physical address, thus + * invalidating a range of physical addresses is accomplished by + * flushing all tlb entries in the outer shareable domain, + * just like PAALLOS. + */ + { .name = "TLBI_RPALOS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 4, .opc2 = 7, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_paallos_write }, + { .name = "TLBI_RPAOS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 4, .opc2 = 3, + .access = PL3_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_paallos_write }, + { .name = "DC_CIPAPA", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 14, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NOP }, +}; + +static const ARMCPRegInfo rme_mte_reginfo[] = { + { .name = "DC_CIGDPAPA", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 14, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NOP }, +}; +#endif /* TARGET_AARCH64 */ + static void define_pmu_regs(ARMCPU *cpu) { /* @@ -6624,18 +7507,22 @@ static void define_pmu_regs(ARMCPU *cpu) ARMCPRegInfo pmcr = { .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0, .access = PL0_RW, + .fgt = FGT_PMCR_EL0, .type = ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr), - .accessfn = pmreg_access, .writefn = pmcr_write, - .raw_writefn = raw_write, + .accessfn = pmreg_access, + .readfn = pmcr_read, .raw_readfn = raw_read, + .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, + .fgt = FGT_PMCR_EL0, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr), .resetvalue = cpu->isar.reset_pmcr_el0, + .readfn = pmcr_read, .raw_readfn = raw_read, .writefn = pmcr_write, .raw_writefn = raw_write, }; @@ -6650,23 +7537,27 @@ static void define_pmu_regs(ARMCPU *cpu) { .name = pmevcntr_name, .cp = 15, .crn = 14, .crm = 8 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, + .fgt = FGT_PMEVCNTRN_EL0, .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, .accessfn = pmreg_access_xevcntr }, { .name = pmevcntr_el0_name, .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 8 | (3 & (i >> 3)), .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access_xevcntr, .type = ARM_CP_IO, + .fgt = FGT_PMEVCNTRN_EL0, .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, .raw_readfn = pmevcntr_rawread, .raw_writefn = pmevcntr_rawwrite }, { .name = pmevtyper_name, .cp = 15, .crn = 14, .crm = 12 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, + .fgt = FGT_PMEVTYPERN_EL0, .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, .accessfn = pmreg_access }, { .name = pmevtyper_el0_name, .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 12 | (3 & (i >> 3)), .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMEVTYPERN_EL0, .type = ARM_CP_IO, .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, .raw_writefn = pmevtyper_rawwrite }, @@ -6677,31 +7568,36 @@ static void define_pmu_regs(ARMCPU *cpu) g_free(pmevtyper_name); g_free(pmevtyper_el0_name); } - if (cpu_isar_feature(aa32_pmu_8_1, cpu)) { + if (cpu_isar_feature(aa32_pmuv3p1, 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, + .fgt = FGT_PMCEIDN_EL0, .resetvalue = extract64(cpu->pmceid0, 32, 32) }, { .name = "PMCEID3", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 5, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, .resetvalue = extract64(cpu->pmceid1, 32, 32) }, }; define_arm_cp_regs(cpu, v81_pmu_regs); } - if (cpu_isar_feature(any_pmu_8_4, cpu)) { + if (cpu_isar_feature(any_pmuv3p4, 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, + .fgt = FGT_PMMIR_EL1, .resetvalue = 0 }; define_one_arm_cp_reg(cpu, &v84_pmmir); } } -/* We don't know until after realize whether there's a GICv3 +#ifndef CONFIG_USER_ONLY +/* + * 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 * at runtime. @@ -6717,7 +7613,6 @@ 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); @@ -6730,7 +7625,8 @@ static uint64_t id_aa64pfr0_read(CPUARMState *env, const ARMCPRegInfo *ri) } #endif -/* Shared logic between LORID and the rest of the LOR* registers. +/* + * Shared logic between LORID and the rest of the LOR* registers. * Secure state exclusion has already been dealt with. */ static CPAccessResult access_lor_ns(CPUARMState *env, @@ -6766,22 +7662,27 @@ static const ARMCPRegInfo lor_reginfo[] = { { .name = "LORSA_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 0, .access = PL1_RW, .accessfn = access_lor_other, + .fgt = FGT_LORSA_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "LOREA_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 1, .access = PL1_RW, .accessfn = access_lor_other, + .fgt = FGT_LOREA_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "LORN_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 2, .access = PL1_RW, .accessfn = access_lor_other, + .fgt = FGT_LORN_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "LORC_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 3, .access = PL1_RW, .accessfn = access_lor_other, + .fgt = FGT_LORC_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "LORID_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 7, .access = PL1_R, .accessfn = access_lor_ns, + .fgt = FGT_LORID_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, }; @@ -6808,100 +7709,124 @@ static const ARMCPRegInfo pauth_reginfo[] = { { .name = "APDAKEYLO_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 0, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APDAKEY, .fieldoffset = offsetof(CPUARMState, keys.apda.lo) }, { .name = "APDAKEYHI_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 1, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APDAKEY, .fieldoffset = offsetof(CPUARMState, keys.apda.hi) }, { .name = "APDBKEYLO_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 2, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APDBKEY, .fieldoffset = offsetof(CPUARMState, keys.apdb.lo) }, { .name = "APDBKEYHI_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 3, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APDBKEY, .fieldoffset = offsetof(CPUARMState, keys.apdb.hi) }, { .name = "APGAKEYLO_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 3, .opc2 = 0, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APGAKEY, .fieldoffset = offsetof(CPUARMState, keys.apga.lo) }, { .name = "APGAKEYHI_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 3, .opc2 = 1, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APGAKEY, .fieldoffset = offsetof(CPUARMState, keys.apga.hi) }, { .name = "APIAKEYLO_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 0, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APIAKEY, .fieldoffset = offsetof(CPUARMState, keys.apia.lo) }, { .name = "APIAKEYHI_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 1, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APIAKEY, .fieldoffset = offsetof(CPUARMState, keys.apia.hi) }, { .name = "APIBKEYLO_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 2, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APIBKEY, .fieldoffset = offsetof(CPUARMState, keys.apib.lo) }, { .name = "APIBKEYHI_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 3, .access = PL1_RW, .accessfn = access_pauth, + .fgt = FGT_APIBKEY, .fieldoffset = offsetof(CPUARMState, keys.apib.hi) }, }; static const ARMCPRegInfo tlbirange_reginfo[] = { { .name = "TLBI_RVAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAE1IS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 3, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAAE1IS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 5, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVALE1IS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 7, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAALE1IS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAE1OS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAAE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 3, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAAE1OS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVALE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 5, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVALE1OS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAALE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 7, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAALE1OS, .writefn = tlbi_aa64_rvae1is_write }, { .name = "TLBI_RVAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RVAAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 3, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAAE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RVALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 5, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVALE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RVAALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 7, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAALE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RIPAS2E1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 2, - .access = PL2_W, .type = ARM_CP_NOP }, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ripas2e1is_write }, { .name = "TLBI_RIPAS2LE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NOP }, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ripas2e1is_write }, { .name = "TLBI_RVAE2IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 2, .opc2 = 1, .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, @@ -6912,10 +7837,12 @@ static const ARMCPRegInfo tlbirange_reginfo[] = { .writefn = tlbi_aa64_rvae2is_write }, { .name = "TLBI_RIPAS2E1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 2, - .access = PL2_W, .type = ARM_CP_NOP }, - { .name = "TLBI_RIPAS2LE1", .state = ARM_CP_STATE_AA64, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ripas2e1_write }, + { .name = "TLBI_RIPAS2LE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NOP }, + .access = PL2_W, .type = ARM_CP_NO_RAW, + .writefn = tlbi_aa64_ripas2e1_write }, { .name = "TLBI_RVAE2OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 5, .opc2 = 1, .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, @@ -6961,27 +7888,33 @@ static const ARMCPRegInfo tlbirange_reginfo[] = { static const ARMCPRegInfo tlbios_reginfo[] = { { .name = "TLBI_VMALLE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVMALLE1OS, .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAE1OS, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_ASIDE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 2, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIASIDE1OS, .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAAE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 3, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAAE1OS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VALE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 5, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVALE1OS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VAALE1OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 7, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIVAALE1OS, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_ALLE2OS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 0, @@ -7067,21 +8000,22 @@ static const ARMCPRegInfo rndr_reginfo[] = { .access = PL0_R, .readfn = rndr_readfn }, }; -#ifndef CONFIG_USER_ONLY static void dccvap_writefn(CPUARMState *env, const ARMCPRegInfo *opaque, uint64_t value) { +#ifdef CONFIG_TCG 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); + int mem_idx = arm_env_mmu_index(env); /* This won't be crossing page boundaries */ haddr = probe_read(env, vaddr, dline_size, mem_idx, GETPC()); if (haddr) { +#ifndef CONFIG_USER_ONLY ram_addr_t offset; MemoryRegion *mr; @@ -7092,13 +8026,19 @@ static void dccvap_writefn(CPUARMState *env, const ARMCPRegInfo *opaque, if (mr) { memory_region_writeback(mr, offset, dline_size); } +#endif /*CONFIG_USER_ONLY*/ } +#else + /* Handled by hardware accelerator. */ + g_assert_not_reached(); +#endif /* CONFIG_TCG */ } static const ARMCPRegInfo dcpop_reg[] = { { .name = "DC_CVAP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 12, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NO_RAW | ARM_CP_SUPPRESS_TB_END, + .fgt = FGT_DCCVAP, .accessfn = aa64_cacheop_poc_access, .writefn = dccvap_writefn }, }; @@ -7106,9 +8046,9 @@ static const ARMCPRegInfo dcpodp_reg[] = { { .name = "DC_CVADP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 13, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NO_RAW | ARM_CP_SUPPRESS_TB_END, + .fgt = FGT_DCCVADP, .accessfn = aa64_cacheop_poc_access, .writefn = dccvap_writefn }, }; -#endif /*CONFIG_USER_ONLY*/ static CPAccessResult access_aa64_tid5(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) @@ -7124,7 +8064,46 @@ static CPAccessResult access_mte(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { int el = arm_current_el(env); + if (el < 2 && arm_is_el2_enabled(env)) { + uint64_t hcr = arm_hcr_el2_eff(env); + if (!(hcr & HCR_ATA) && (!(hcr & HCR_E2H) || !(hcr & HCR_TGE))) { + return CP_ACCESS_TRAP_EL2; + } + } + if (el < 3 && + arm_feature(env, ARM_FEATURE_EL3) && + !(env->cp15.scr_el3 & SCR_ATA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} +static CPAccessResult access_tfsr_el1(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + CPAccessResult nv1 = access_nv1(env, ri, isread); + + if (nv1 != CP_ACCESS_OK) { + return nv1; + } + return access_mte(env, ri, isread); +} + +static CPAccessResult access_tfsr_el2(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* + * TFSR_EL2: similar to generic access_mte(), but we need to + * account for FEAT_NV. At EL1 this must be a FEAT_NV access; + * if NV2 is enabled then we will redirect this to TFSR_EL1 + * after doing the HCR and SCR ATA traps; otherwise this will + * be a trap to EL2 and the HCR/SCR traps do not apply. + */ + int el = arm_current_el(env); + + if (el == 1 && (arm_hcr_el2_eff(env) & HCR_NV2)) { + return CP_ACCESS_OK; + } if (el < 2 && arm_is_el2_enabled(env)) { uint64_t hcr = arm_hcr_el2_eff(env); if (!(hcr & HCR_ATA) && (!(hcr & HCR_E2H) || !(hcr & HCR_TGE))) { @@ -7156,11 +8135,13 @@ static const ARMCPRegInfo mte_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[0]) }, { .name = "TFSR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 6, .opc2 = 0, - .access = PL1_RW, .accessfn = access_mte, + .access = PL1_RW, .accessfn = access_tfsr_el1, + .nv2_redirect_offset = 0x190 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[1]) }, { .name = "TFSR_EL2", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_NV2_REDIRECT, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 6, .opc2 = 0, - .access = PL2_RW, .accessfn = access_mte, + .access = PL2_RW, .accessfn = access_tfsr_el2, .fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[2]) }, { .name = "TFSR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 5, .crm = 6, .opc2 = 0, @@ -7174,10 +8155,6 @@ static const ARMCPRegInfo mte_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 6, .access = PL1_RW, .accessfn = access_mte, .fieldoffset = offsetof(CPUARMState, cp15.gcr_el1) }, - { .name = "GMID_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 1, .crn = 0, .crm = 0, .opc2 = 4, - .access = PL1_R, .accessfn = access_aa64_tid5, - .type = ARM_CP_CONST, .resetvalue = GMID_EL1_BS }, { .name = "TCO", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 4, .crm = 2, .opc2 = 7, .type = ARM_CP_NO_RAW, @@ -7185,28 +8162,36 @@ static const ARMCPRegInfo mte_reginfo[] = { { .name = "DC_IGVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 3, .type = ARM_CP_NOP, .access = PL1_W, + .fgt = FGT_DCIVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_IGSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 4, + .fgt = FGT_DCISW, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, { .name = "DC_IGDVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 5, .type = ARM_CP_NOP, .access = PL1_W, + .fgt = FGT_DCIVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_IGDSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 6, + .fgt = FGT_DCISW, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, { .name = "DC_CGSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 10, .opc2 = 4, + .fgt = FGT_DCCSW, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, { .name = "DC_CGDSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 10, .opc2 = 6, + .fgt = FGT_DCCSW, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, { .name = "DC_CIGSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 4, + .fgt = FGT_DCCISW, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, { .name = "DC_CIGDSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 6, + .fgt = FGT_DCCISW, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, }; @@ -7220,34 +8205,42 @@ static const ARMCPRegInfo mte_el0_cacheop_reginfo[] = { { .name = "DC_CGVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 10, .opc2 = 3, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CGDVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 10, .opc2 = 5, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CGVAP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 12, .opc2 = 3, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCVAP, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CGDVAP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 12, .opc2 = 5, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCVAP, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CGVADP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 13, .opc2 = 3, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCVADP, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CGDVADP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 13, .opc2 = 5, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCVADP, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CIGVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 14, .opc2 = 3, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCIVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CIGDVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 14, .opc2 = 5, .type = ARM_CP_NOP, .access = PL0_W, + .fgt = FGT_DCCIVAC, .accessfn = aa64_cacheop_poc_access }, { .name = "DC_GVA", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 4, .opc2 = 3, @@ -7255,6 +8248,7 @@ static const ARMCPRegInfo mte_el0_cacheop_reginfo[] = { #ifndef CONFIG_USER_ONLY /* Avoid overhead of an access check that always passes in user-mode */ .accessfn = aa64_zva_access, + .fgt = FGT_DCZVA, #endif }, { .name = "DC_GZVA", .state = ARM_CP_STATE_AA64, @@ -7263,6 +8257,7 @@ static const ARMCPRegInfo mte_el0_cacheop_reginfo[] = { #ifndef CONFIG_USER_ONLY /* Avoid overhead of an access check that always passes in user-mode */ .accessfn = aa64_zva_access, + .fgt = FGT_DCZVA, #endif }, }; @@ -7294,14 +8289,29 @@ static CPAccessResult access_scxtnum(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +static CPAccessResult access_scxtnum_el1(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + CPAccessResult nv1 = access_nv1(env, ri, isread); + + if (nv1 != CP_ACCESS_OK) { + return nv1; + } + return access_scxtnum(env, ri, isread); +} + static const ARMCPRegInfo scxtnum_reginfo[] = { { .name = "SCXTNUM_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 13, .crm = 0, .opc2 = 7, .access = PL0_RW, .accessfn = access_scxtnum, + .fgt = FGT_SCXTNUM_EL0, .fieldoffset = offsetof(CPUARMState, scxtnum_el[0]) }, { .name = "SCXTNUM_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 7, - .access = PL1_RW, .accessfn = access_scxtnum, + .access = PL1_RW, .accessfn = access_scxtnum_el1, + .fgt = FGT_SCXTNUM_EL1, + .nv2_redirect_offset = 0x188 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, scxtnum_el[1]) }, { .name = "SCXTNUM_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 7, @@ -7312,6 +8322,67 @@ static const ARMCPRegInfo scxtnum_reginfo[] = { .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, scxtnum_el[3]) }, }; + +static CPAccessResult access_fgt(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 2 && + arm_feature(env, ARM_FEATURE_EL3) && !(env->cp15.scr_el3 & SCR_FGTEN)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static const ARMCPRegInfo fgt_reginfo[] = { + { .name = "HFGRTR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 4, + .nv2_redirect_offset = 0x1b8, + .access = PL2_RW, .accessfn = access_fgt, + .fieldoffset = offsetof(CPUARMState, cp15.fgt_read[FGTREG_HFGRTR]) }, + { .name = "HFGWTR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 5, + .nv2_redirect_offset = 0x1c0, + .access = PL2_RW, .accessfn = access_fgt, + .fieldoffset = offsetof(CPUARMState, cp15.fgt_write[FGTREG_HFGWTR]) }, + { .name = "HDFGRTR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 1, .opc2 = 4, + .nv2_redirect_offset = 0x1d0, + .access = PL2_RW, .accessfn = access_fgt, + .fieldoffset = offsetof(CPUARMState, cp15.fgt_read[FGTREG_HDFGRTR]) }, + { .name = "HDFGWTR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 1, .opc2 = 5, + .nv2_redirect_offset = 0x1d8, + .access = PL2_RW, .accessfn = access_fgt, + .fieldoffset = offsetof(CPUARMState, cp15.fgt_write[FGTREG_HDFGWTR]) }, + { .name = "HFGITR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 6, + .nv2_redirect_offset = 0x1c8, + .access = PL2_RW, .accessfn = access_fgt, + .fieldoffset = offsetof(CPUARMState, cp15.fgt_exec[FGTREG_HFGITR]) }, +}; + +static void vncr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Clear the RES0 bottom 12 bits; this means at runtime we can guarantee + * that VNCR_EL2 + offset is 64-bit aligned. We don't need to do anything + * about the RESS bits at the top -- we choose the "generate an EL2 + * translation abort on use" CONSTRAINED UNPREDICTABLE option (i.e. let + * the ptw.c code detect the resulting invalid address). + */ + env->cp15.vncr_el2 = value & ~0xfffULL; +} + +static const ARMCPRegInfo nv2_reginfo[] = { + { .name = "VNCR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 2, .opc2 = 0, + .access = PL2_RW, + .writefn = vncr_write, + .nv2_redirect_offset = 0xb0, + .fieldoffset = offsetof(CPUARMState, cp15.vncr_el2) }, +}; + #endif /* TARGET_AARCH64 */ static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri, @@ -7336,24 +8407,30 @@ static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo predinv_reginfo[] = { { .name = "CFP_RCTX", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 3, .opc2 = 4, + .fgt = FGT_CFPRCTX, .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, { .name = "DVP_RCTX", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 3, .opc2 = 5, + .fgt = FGT_DVPRCTX, .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, { .name = "CPP_RCTX", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 3, .opc2 = 7, + .fgt = FGT_CPPRCTX, .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, /* * Note the AArch32 opcodes have a different OPC1. */ { .name = "CFPRCTX", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 7, .crm = 3, .opc2 = 4, + .fgt = FGT_CFPRCTX, .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, { .name = "DVPRCTX", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 7, .crm = 3, .opc2 = 5, + .fgt = FGT_DVPRCTX, .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, { .name = "CPPRCTX", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 7, .crm = 3, .opc2 = 7, + .fgt = FGT_CPPRCTX, .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, }; @@ -7367,7 +8444,7 @@ 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, + .accessfn = access_tid4, .readfn = ccsidr2_read, .type = ARM_CP_NO_RAW }, }; @@ -7442,6 +8519,7 @@ static const ARMCPRegInfo vhe_reginfo[] = { { .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, + .raw_writefn = raw_write, .fieldoffset = offsetof(CPUARMState, cp15.ttbr1_el[2]) }, #ifndef CONFIG_USER_ONLY { .name = "CNTHV_CVAL_EL2", .state = ARM_CP_STATE_AA64, @@ -7464,13 +8542,15 @@ static const ARMCPRegInfo vhe_reginfo[] = { { .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, + .access = PL2_RW, .accessfn = access_el1nvpct, + .nv2_redirect_offset = 0x180 | NV2_REDIR_NO_NV1, .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, + .access = PL2_RW, .accessfn = access_el1nvvct, + .nv2_redirect_offset = 0x170 | NV2_REDIR_NO_NV1, .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, @@ -7487,27 +8567,31 @@ static const ARMCPRegInfo vhe_reginfo[] = { .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, + .nv2_redirect_offset = 0x178 | NV2_REDIR_NO_NV1, + .access = PL2_RW, .accessfn = access_el1nvpct, .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, + .nv2_redirect_offset = 0x168 | NV2_REDIR_NO_NV1, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), - .access = PL2_RW, .accessfn = e2h_access, + .access = PL2_RW, .accessfn = access_el1nvvct, .writefn = gt_virt_cval_write, .raw_writefn = raw_write }, #endif }; #ifndef CONFIG_USER_ONLY static const ARMCPRegInfo ats1e1_reginfo[] = { - { .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64, + { .name = "AT_S1E1RP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .writefn = ats_write64 }, - { .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64, + .fgt = FGT_ATS1E1RP, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, + { .name = "AT_S1E1WP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .writefn = ats_write64 }, + .fgt = FGT_ATS1E1WP, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, }; static const ARMCPRegInfo ats1cp_reginfo[] = { @@ -7553,7 +8637,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, cp_reginfo); if (!arm_feature(env, ARM_FEATURE_V8)) { - /* Must go early as it is full of wildcards that may be + /* + * Must go early as it is full of wildcards that may be * overridden by later definitions. */ define_arm_cp_regs(cpu, not_v8_cp_reginfo); @@ -7567,15 +8652,24 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, .resetvalue = cpu->isar.id_pfr0 }, - /* ID_PFR1 is not a plain ARM_CP_CONST because we don't know + /* + * ID_PFR1 is not a plain ARM_CP_CONST because we don't know * the value of the GIC field until after we define these regs. */ { .name = "ID_PFR1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 1, .access = PL1_R, .type = ARM_CP_NO_RAW, .accessfn = access_aa32_tid3, +#ifdef CONFIG_USER_ONLY + .type = ARM_CP_CONST, + .resetvalue = cpu->isar.id_pfr1, +#else + .type = ARM_CP_NO_RAW, + .accessfn = access_aa32_tid3, .readfn = id_pfr1_read, - .writefn = arm_cp_write_ignore }, + .writefn = arm_cp_write_ignore +#endif + }, { .name = "ID_DFR0", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, @@ -7667,7 +8761,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) .name = "CLIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, - .accessfn = access_aa64_tid2, + .accessfn = access_tid4, + .fgt = FGT_CLIDR_EL1, .resetvalue = cpu->clidr }; define_one_arm_cp_reg(cpu, &clidr); @@ -7678,11 +8773,16 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, not_v7_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_V8)) { - /* AArch64 ID registers, which all have impdef reset values. + /* + * v8 ID registers, which all have impdef reset values. * Note that within the ID register ranges the unused slots * must all RAZ, not UNDEF; future architecture versions may * define new registers here. + * ID registers which are AArch64 views of the AArch32 ID registers + * which already existed in v6 and v7 are handled elsewhere, + * in v6_idregs[]. */ + int i; ARMCPRegInfo v8_idregs[] = { /* * ID_AA64PFR0_EL1 is not a plain ARM_CP_CONST in system @@ -7787,11 +8887,11 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, .resetvalue = cpu->isar.id_aa64isar1 }, - { .name = "ID_AA64ISAR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + { .name = "ID_AA64ISAR2_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = 0 }, + .resetvalue = cpu->isar.id_aa64isar2 }, { .name = "ID_AA64ISAR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, @@ -7872,7 +8972,34 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, .resetvalue = cpu->isar.mvfr2 }, - { .name = "MVFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + /* + * "0, c0, c3, {0,1,2}" are the encodings corresponding to + * AArch64 MVFR[012]_EL1. Define the STATE_AA32 encoding + * as RAZ, since it is in the "reserved for future ID + * registers, RAZ" part of the AArch32 encoding space. + */ + { .name = "RES_0_C0_C3_0", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, + .accessfn = access_aa64_tid3, + .resetvalue = 0 }, + { .name = "RES_0_C0_C3_1", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 1, + .access = PL1_R, .type = ARM_CP_CONST, + .accessfn = access_aa64_tid3, + .resetvalue = 0 }, + { .name = "RES_0_C0_C3_2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2, + .access = PL1_R, .type = ARM_CP_CONST, + .accessfn = access_aa64_tid3, + .resetvalue = 0 }, + /* + * Other encodings in "0, c0, c3, ..." are STATE_BOTH because + * they're also RAZ for AArch64, and in v8 are gradually + * being filled with AArch64-view-of-AArch32-ID-register + * for new ID registers. + */ + { .name = "RES_0_C0_C3_3", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, @@ -7882,17 +9009,17 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, .resetvalue = cpu->isar.id_pfr2 }, - { .name = "MVFR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + { .name = "ID_DFR1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = 0 }, - { .name = "MVFR6_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .resetvalue = cpu->isar.id_dfr1 }, + { .name = "ID_MMFR5", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = 0 }, - { .name = "MVFR7_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + .resetvalue = cpu->isar.id_mmfr5 }, + { .name = "RES_0_C0_C3_7", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, @@ -7900,64 +9027,171 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "PMCEID0", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 6, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, .resetvalue = extract64(cpu->pmceid0, 0, 32) }, { .name = "PMCEID0_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 6, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, .resetvalue = cpu->pmceid0 }, { .name = "PMCEID1", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 7, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, .resetvalue = extract64(cpu->pmceid1, 0, 32) }, { .name = "PMCEID1_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 7, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, .resetvalue = cpu->pmceid1 }, }; #ifdef CONFIG_USER_ONLY static const ARMCPRegUserSpaceInfo v8_user_idregs[] = { { .name = "ID_AA64PFR0_EL1", - .exported_bits = 0x000f000f00ff0000, - .fixed_bits = 0x0000000000000011 }, + .exported_bits = R_ID_AA64PFR0_FP_MASK | + R_ID_AA64PFR0_ADVSIMD_MASK | + R_ID_AA64PFR0_SVE_MASK | + R_ID_AA64PFR0_DIT_MASK, + .fixed_bits = (0x1u << R_ID_AA64PFR0_EL0_SHIFT) | + (0x1u << R_ID_AA64PFR0_EL1_SHIFT) }, { .name = "ID_AA64PFR1_EL1", - .exported_bits = 0x00000000000000f0 }, + .exported_bits = R_ID_AA64PFR1_BT_MASK | + R_ID_AA64PFR1_SSBS_MASK | + R_ID_AA64PFR1_MTE_MASK | + R_ID_AA64PFR1_SME_MASK }, { .name = "ID_AA64PFR*_EL1_RESERVED", - .is_glob = true }, - { .name = "ID_AA64ZFR0_EL1" }, + .is_glob = true }, + { .name = "ID_AA64ZFR0_EL1", + .exported_bits = R_ID_AA64ZFR0_SVEVER_MASK | + R_ID_AA64ZFR0_AES_MASK | + R_ID_AA64ZFR0_BITPERM_MASK | + R_ID_AA64ZFR0_BFLOAT16_MASK | + R_ID_AA64ZFR0_B16B16_MASK | + R_ID_AA64ZFR0_SHA3_MASK | + R_ID_AA64ZFR0_SM4_MASK | + R_ID_AA64ZFR0_I8MM_MASK | + R_ID_AA64ZFR0_F32MM_MASK | + R_ID_AA64ZFR0_F64MM_MASK }, + { .name = "ID_AA64SMFR0_EL1", + .exported_bits = R_ID_AA64SMFR0_F32F32_MASK | + R_ID_AA64SMFR0_BI32I32_MASK | + R_ID_AA64SMFR0_B16F32_MASK | + R_ID_AA64SMFR0_F16F32_MASK | + R_ID_AA64SMFR0_I8I32_MASK | + R_ID_AA64SMFR0_F16F16_MASK | + R_ID_AA64SMFR0_B16B16_MASK | + R_ID_AA64SMFR0_I16I32_MASK | + R_ID_AA64SMFR0_F64F64_MASK | + R_ID_AA64SMFR0_I16I64_MASK | + R_ID_AA64SMFR0_SMEVER_MASK | + R_ID_AA64SMFR0_FA64_MASK }, { .name = "ID_AA64MMFR0_EL1", - .fixed_bits = 0x00000000ff000000 }, - { .name = "ID_AA64MMFR1_EL1" }, + .exported_bits = R_ID_AA64MMFR0_ECV_MASK, + .fixed_bits = (0xfu << R_ID_AA64MMFR0_TGRAN64_SHIFT) | + (0xfu << R_ID_AA64MMFR0_TGRAN4_SHIFT) }, + { .name = "ID_AA64MMFR1_EL1", + .exported_bits = R_ID_AA64MMFR1_AFP_MASK }, + { .name = "ID_AA64MMFR2_EL1", + .exported_bits = R_ID_AA64MMFR2_AT_MASK }, { .name = "ID_AA64MMFR*_EL1_RESERVED", - .is_glob = true }, + .is_glob = true }, { .name = "ID_AA64DFR0_EL1", - .fixed_bits = 0x0000000000000006 }, - { .name = "ID_AA64DFR1_EL1" }, + .fixed_bits = (0x6u << R_ID_AA64DFR0_DEBUGVER_SHIFT) }, + { .name = "ID_AA64DFR1_EL1" }, { .name = "ID_AA64DFR*_EL1_RESERVED", - .is_glob = true }, + .is_glob = true }, { .name = "ID_AA64AFR*", - .is_glob = true }, + .is_glob = true }, { .name = "ID_AA64ISAR0_EL1", - .exported_bits = 0x00fffffff0fffff0 }, + .exported_bits = R_ID_AA64ISAR0_AES_MASK | + R_ID_AA64ISAR0_SHA1_MASK | + R_ID_AA64ISAR0_SHA2_MASK | + R_ID_AA64ISAR0_CRC32_MASK | + R_ID_AA64ISAR0_ATOMIC_MASK | + R_ID_AA64ISAR0_RDM_MASK | + R_ID_AA64ISAR0_SHA3_MASK | + R_ID_AA64ISAR0_SM3_MASK | + R_ID_AA64ISAR0_SM4_MASK | + R_ID_AA64ISAR0_DP_MASK | + R_ID_AA64ISAR0_FHM_MASK | + R_ID_AA64ISAR0_TS_MASK | + R_ID_AA64ISAR0_RNDR_MASK }, { .name = "ID_AA64ISAR1_EL1", - .exported_bits = 0x000000f0ffffffff }, + .exported_bits = R_ID_AA64ISAR1_DPB_MASK | + R_ID_AA64ISAR1_APA_MASK | + R_ID_AA64ISAR1_API_MASK | + R_ID_AA64ISAR1_JSCVT_MASK | + R_ID_AA64ISAR1_FCMA_MASK | + R_ID_AA64ISAR1_LRCPC_MASK | + R_ID_AA64ISAR1_GPA_MASK | + R_ID_AA64ISAR1_GPI_MASK | + R_ID_AA64ISAR1_FRINTTS_MASK | + R_ID_AA64ISAR1_SB_MASK | + R_ID_AA64ISAR1_BF16_MASK | + R_ID_AA64ISAR1_DGH_MASK | + R_ID_AA64ISAR1_I8MM_MASK }, + { .name = "ID_AA64ISAR2_EL1", + .exported_bits = R_ID_AA64ISAR2_WFXT_MASK | + R_ID_AA64ISAR2_RPRES_MASK | + R_ID_AA64ISAR2_GPA3_MASK | + R_ID_AA64ISAR2_APA3_MASK | + R_ID_AA64ISAR2_MOPS_MASK | + R_ID_AA64ISAR2_BC_MASK | + R_ID_AA64ISAR2_RPRFM_MASK | + R_ID_AA64ISAR2_CSSC_MASK }, { .name = "ID_AA64ISAR*_EL1_RESERVED", - .is_glob = true }, + .is_glob = true }, }; modify_arm_cp_regs(v8_idregs, v8_user_idregs); #endif - /* RVBAR_EL1 is only implemented if EL1 is the highest EL */ + /* + * RVBAR_EL1 and RMR_EL1 only implemented if EL1 is the highest EL. + * TODO: For RMR, a write with bit 1 set should do something with + * cpu_reset(). In the meantime, "the bit is strictly a request", + * so we are in spec just ignoring writes. + */ if (!arm_feature(env, ARM_FEATURE_EL3) && !arm_feature(env, ARM_FEATURE_EL2)) { - ARMCPRegInfo rvbar = { - .name = "RVBAR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 0, .opc2 = 1, - .access = PL1_R, - .fieldoffset = offsetof(CPUARMState, cp15.rvbar), + ARMCPRegInfo el1_reset_regs[] = { + { .name = "RVBAR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 0, .opc2 = 1, + .access = PL1_R, + .fieldoffset = offsetof(CPUARMState, cp15.rvbar) }, + { .name = "RMR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 0, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, + .resetvalue = arm_feature(env, ARM_FEATURE_AARCH64) } }; - define_one_arm_cp_reg(cpu, &rvbar); + define_arm_cp_regs(cpu, el1_reset_regs); } define_arm_cp_regs(cpu, v8_idregs); define_arm_cp_regs(cpu, v8_cp_reginfo); + if (cpu_isar_feature(aa64_aa32_el1, cpu)) { + define_arm_cp_regs(cpu, v8_aa32_el1_reginfo); + } + + for (i = 4; i < 16; i++) { + /* + * Encodings in "0, c0, {c4-c7}, {0-7}" are RAZ for AArch32. + * For pre-v8 cores there are RAZ patterns for these in + * id_pre_v8_midr_cp_reginfo[]; for v8 we do that here. + * v8 extends the "must RAZ" part of the ID register space + * to also cover c0, 0, c{8-15}, {0-7}. + * These are STATE_AA32 because in the AArch64 sysreg space + * c4-c7 is where the AArch64 ID registers live (and we've + * already defined those in v8_idregs[]), and c8-c15 are not + * "must RAZ" for AArch64. + */ + g_autofree char *name = g_strdup_printf("RES_0_C0_C%d_X", i); + ARMCPRegInfo v8_aa32_raz_idregs = { + .name = name, + .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 0, .crm = i, .opc2 = CP_ANY, + .access = PL1_R, .type = ARM_CP_CONST, + .accessfn = access_aa64_tid3, + .resetvalue = 0 }; + define_one_arm_cp_reg(cpu, &v8_aa32_raz_idregs); + } } /* @@ -7982,6 +9216,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0, .access = PL2_RW, .resetvalue = cpu->midr, .type = ARM_CP_EL3_NO_EL2_C_NZ, + .nv2_redirect_offset = 0x88, .fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) }, { .name = "VMPIDR", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5, @@ -7993,6 +9228,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5, .access = PL2_RW, .resetvalue = vmpidr_def, .type = ARM_CP_EL3_NO_EL2_C_NZ, + .nv2_redirect_offset = 0x50, .fieldoffset = offsetof(CPUARMState, cp15.vmpidr_el2) }, }; /* @@ -8000,8 +9236,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) * value is MDCR_EL2.HPMN which should reset to the value of PMCR_EL0.N. */ ARMCPRegInfo mdcr_el2 = { - .name = "MDCR_EL2", .state = ARM_CP_STATE_BOTH, + .name = "MDCR_EL2", .state = ARM_CP_STATE_BOTH, .type = ARM_CP_IO, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 1, + .writefn = mdcr_el2_write, .access = PL2_RW, .resetvalue = pmu_num_counters(env), .fieldoffset = offsetof(CPUARMState, cp15.mdcr_el2), }; @@ -8014,15 +9251,25 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_sel2, cpu)) { define_arm_cp_regs(cpu, el2_sec_cp_reginfo); } - /* RVBAR_EL2 is only implemented if EL2 is the highest EL */ + /* + * RVBAR_EL2 and RMR_EL2 only implemented if EL2 is the highest EL. + * See commentary near RMR_EL1. + */ if (!arm_feature(env, ARM_FEATURE_EL3)) { - ARMCPRegInfo rvbar = { - .name = "RVBAR_EL2", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 1, - .access = PL2_R, - .fieldoffset = offsetof(CPUARMState, cp15.rvbar), + static const ARMCPRegInfo el2_reset_regs[] = { + { .name = "RVBAR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 1, + .access = PL2_R, + .fieldoffset = offsetof(CPUARMState, cp15.rvbar) }, + { .name = "RVBAR", .type = ARM_CP_ALIAS, + .cp = 15, .opc1 = 0, .crn = 12, .crm = 0, .opc2 = 1, + .access = PL2_R, + .fieldoffset = offsetof(CPUARMState, cp15.rvbar) }, + { .name = "RMR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 2, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 1 }, }; - define_one_arm_cp_reg(cpu, &rvbar); + define_arm_cp_regs(cpu, el2_reset_regs); } } @@ -8033,8 +9280,14 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "RVBAR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 12, .crm = 0, .opc2 = 1, .access = PL3_R, - .fieldoffset = offsetof(CPUARMState, cp15.rvbar), - }, + .fieldoffset = offsetof(CPUARMState, cp15.rvbar), }, + { .name = "RMR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 12, .crm = 0, .opc2 = 2, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 1 }, + { .name = "RMR", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 12, .crm = 0, .opc2 = 2, + .access = PL3_RW, .type = ARM_CP_CONST, + .resetvalue = arm_feature(env, ARM_FEATURE_AARCH64) }, { .name = "SCTLR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 0, .opc2 = 0, .access = PL3_RW, @@ -8045,7 +9298,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, el3_regs); } - /* The behaviour of NSACR is sufficiently various that we don't + /* + * The behaviour of NSACR is sufficiently various that we don't * try to describe it in a single reginfo: * if EL3 is 64 bit, then trap to EL3 from S EL1, * reads as constant 0xc00 from NS EL1 and NS EL2 @@ -8107,7 +9361,36 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_GENERIC_TIMER)) { define_arm_cp_regs(cpu, generic_timer_cp_reginfo); } + if (cpu_isar_feature(aa64_ecv_traps, cpu)) { + define_arm_cp_regs(cpu, gen_timer_ecv_cp_reginfo); + } +#ifndef CONFIG_USER_ONLY + if (cpu_isar_feature(aa64_ecv, cpu)) { + define_one_arm_cp_reg(cpu, &gen_timer_cntpoff_reginfo); + } +#endif if (arm_feature(env, ARM_FEATURE_VAPA)) { + ARMCPRegInfo vapa_cp_reginfo[] = { + { .name = "PAR", .cp = 15, .crn = 7, .crm = 4, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .resetvalue = 0, + .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.par_s), + offsetoflow32(CPUARMState, cp15.par_ns) }, + .writefn = par_write}, +#ifndef CONFIG_USER_ONLY + /* This underdecoding is safe because the reginfo is NO_RAW. */ + { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY, + .access = PL1_W, .accessfn = ats_access, + .writefn = ats_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, +#endif + }; + + /* + * When LPAE exists this 32-bit PAR register is an alias of the + * 64-bit AArch32 PAR register defined in lpae_cp_reginfo[] + */ + if (arm_feature(env, ARM_FEATURE_LPAE)) { + vapa_cp_reginfo[0].type = ARM_CP_ALIAS | ARM_CP_NO_GDB; + } define_arm_cp_regs(cpu, vapa_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_CACHE_TEST_CLEAN)) { @@ -8137,13 +9420,15 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa32_jazelle, cpu)) { define_arm_cp_regs(cpu, jazelle_regs); } - /* Slightly awkwardly, the OMAP and StrongARM cores need all of + /* + * 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). */ { ARMCPRegInfo id_pre_v8_midr_cp_reginfo[] = { - /* Pre-v8 MIDR space. + /* + * Pre-v8 MIDR space. * Note that the MIDR isn't a simple constant register because * of the TI925 behaviour where writes to another register can * cause the MIDR value to change. @@ -8180,12 +9465,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "MIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 0, .access = PL1_R, .type = ARM_CP_NO_RAW, .resetvalue = cpu->midr, + .fgt = FGT_MIDR_EL1, .fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid), .readfn = midr_read }, - /* crn = 0 op1 = 0 crm = 0 op2 = 4,7 : AArch32 aliases of MIDR */ - { .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST, - .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 4, - .access = PL1_R, .resetvalue = cpu->midr }, + /* crn = 0 op1 = 0 crm = 0 op2 = 7 : AArch32 aliases of MIDR */ { .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST, .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 7, .access = PL1_R, .resetvalue = cpu->midr }, @@ -8193,8 +9476,14 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 6, .access = PL1_R, .accessfn = access_aa64_tid1, + .fgt = FGT_REVIDR_EL1, .type = ARM_CP_CONST, .resetvalue = cpu->revidr }, }; + ARMCPRegInfo id_v8_midr_alias_cp_reginfo = { + .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST | ARM_CP_NO_GDB, + .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 4, + .access = PL1_R, .resetvalue = cpu->midr + }; ARMCPRegInfo id_cp_reginfo[] = { /* These are common to v8 and pre-v8 */ { .name = "CTR", @@ -8204,6 +9493,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "CTR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 0, .crm = 0, .access = PL0_R, .accessfn = ctr_el0_access, + .fgt = FGT_CTR_EL0, .type = ARM_CP_CONST, .resetvalue = cpu->ctr }, /* TCMTR and TLBTR exist in v8 but have no 64-bit versions */ { .name = "TCMTR", @@ -8227,6 +9517,13 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->pmsav7_dregion << 8 }; + /* HMPUIR is specific to PMSA V8 */ + ARMCPRegInfo id_hmpuir_reginfo = { + .name = "HMPUIR", + .cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 4, + .access = PL2_R, .type = ARM_CP_CONST, + .resetvalue = cpu->pmsav8r_hdregion + }; static const ARMCPRegInfo crn0_wi_reginfo = { .name = "CRN0_WI", .cp = 15, .crn = 0, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_W, @@ -8235,15 +9532,20 @@ void register_cp_regs_for_features(ARMCPU *cpu) #ifdef CONFIG_USER_ONLY static const ARMCPRegUserSpaceInfo id_v8_user_midr_cp_reginfo[] = { { .name = "MIDR_EL1", - .exported_bits = 0x00000000ffffffff }, - { .name = "REVIDR_EL1" }, + .exported_bits = R_MIDR_EL1_REVISION_MASK | + R_MIDR_EL1_PARTNUM_MASK | + R_MIDR_EL1_ARCHITECTURE_MASK | + R_MIDR_EL1_VARIANT_MASK | + R_MIDR_EL1_IMPLEMENTER_MASK }, + { .name = "REVIDR_EL1" }, }; modify_arm_cp_regs(id_v8_midr_cp_reginfo, id_v8_user_midr_cp_reginfo); #endif if (arm_feature(env, ARM_FEATURE_OMAPCP) || arm_feature(env, ARM_FEATURE_STRONGARM)) { size_t i; - /* Register the blanket "writes ignored" value first to cover the + /* + * Register the blanket "writes ignored" value first to cover the * whole space. Then update the specific ID registers to allow write * access, so that they ignore writes rather than causing them to * UNDEF. @@ -8260,12 +9562,83 @@ void register_cp_regs_for_features(ARMCPU *cpu) } if (arm_feature(env, ARM_FEATURE_V8)) { define_arm_cp_regs(cpu, id_v8_midr_cp_reginfo); + if (!arm_feature(env, ARM_FEATURE_PMSA)) { + define_one_arm_cp_reg(cpu, &id_v8_midr_alias_cp_reginfo); + } } else { define_arm_cp_regs(cpu, id_pre_v8_midr_cp_reginfo); } define_arm_cp_regs(cpu, id_cp_reginfo); if (!arm_feature(env, ARM_FEATURE_PMSA)) { define_one_arm_cp_reg(cpu, &id_tlbtr_reginfo); + } else if (arm_feature(env, ARM_FEATURE_PMSA) && + arm_feature(env, ARM_FEATURE_V8)) { + uint32_t i = 0; + char *tmp_string; + + define_one_arm_cp_reg(cpu, &id_mpuir_reginfo); + define_one_arm_cp_reg(cpu, &id_hmpuir_reginfo); + define_arm_cp_regs(cpu, pmsav8r_cp_reginfo); + + /* Register alias is only valid for first 32 indexes */ + for (i = 0; i < MIN(cpu->pmsav7_dregion, 32); ++i) { + uint8_t crm = 0b1000 | extract32(i, 1, 3); + uint8_t opc1 = extract32(i, 4, 1); + uint8_t opc2 = extract32(i, 0, 1) << 2; + + tmp_string = g_strdup_printf("PRBAR%u", i); + ARMCPRegInfo tmp_prbarn_reginfo = { + .name = tmp_string, .type = ARM_CP_ALIAS | ARM_CP_NO_RAW, + .cp = 15, .opc1 = opc1, .crn = 6, .crm = crm, .opc2 = opc2, + .access = PL1_RW, .resetvalue = 0, + .accessfn = access_tvm_trvm, + .writefn = pmsav8r_regn_write, .readfn = pmsav8r_regn_read + }; + define_one_arm_cp_reg(cpu, &tmp_prbarn_reginfo); + g_free(tmp_string); + + opc2 = extract32(i, 0, 1) << 2 | 0x1; + tmp_string = g_strdup_printf("PRLAR%u", i); + ARMCPRegInfo tmp_prlarn_reginfo = { + .name = tmp_string, .type = ARM_CP_ALIAS | ARM_CP_NO_RAW, + .cp = 15, .opc1 = opc1, .crn = 6, .crm = crm, .opc2 = opc2, + .access = PL1_RW, .resetvalue = 0, + .accessfn = access_tvm_trvm, + .writefn = pmsav8r_regn_write, .readfn = pmsav8r_regn_read + }; + define_one_arm_cp_reg(cpu, &tmp_prlarn_reginfo); + g_free(tmp_string); + } + + /* Register alias is only valid for first 32 indexes */ + for (i = 0; i < MIN(cpu->pmsav8r_hdregion, 32); ++i) { + uint8_t crm = 0b1000 | extract32(i, 1, 3); + uint8_t opc1 = 0b100 | extract32(i, 4, 1); + uint8_t opc2 = extract32(i, 0, 1) << 2; + + tmp_string = g_strdup_printf("HPRBAR%u", i); + ARMCPRegInfo tmp_hprbarn_reginfo = { + .name = tmp_string, + .type = ARM_CP_NO_RAW, + .cp = 15, .opc1 = opc1, .crn = 6, .crm = crm, .opc2 = opc2, + .access = PL2_RW, .resetvalue = 0, + .writefn = pmsav8r_regn_write, .readfn = pmsav8r_regn_read + }; + define_one_arm_cp_reg(cpu, &tmp_hprbarn_reginfo); + g_free(tmp_string); + + opc2 = extract32(i, 0, 1) << 2 | 0x1; + tmp_string = g_strdup_printf("HPRLAR%u", i); + ARMCPRegInfo tmp_hprlarn_reginfo = { + .name = tmp_string, + .type = ARM_CP_NO_RAW, + .cp = 15, .opc1 = opc1, .crn = 6, .crm = crm, .opc2 = opc2, + .access = PL2_RW, .resetvalue = 0, + .writefn = pmsav8r_regn_write, .readfn = pmsav8r_regn_read + }; + define_one_arm_cp_reg(cpu, &tmp_hprlarn_reginfo); + g_free(tmp_string); + } } else if (arm_feature(env, ARM_FEATURE_V7)) { define_one_arm_cp_reg(cpu, &id_mpuir_reginfo); } @@ -8275,6 +9648,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) ARMCPRegInfo mpidr_cp_reginfo[] = { { .name = "MPIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5, + .fgt = FGT_MPIDR_EL1, .access = PL1_R, .readfn = mpidr_read, .type = ARM_CP_NO_RAW }, }; #ifdef CONFIG_USER_ONLY @@ -8292,6 +9666,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "ACTLR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_tacr, + .nv2_redirect_offset = 0x118, .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, @@ -8322,7 +9697,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) * AArch64 cores we might need to add a specific feature flag * to indicate cores with "flavour 2" CBAR. */ - if (arm_feature(env, ARM_FEATURE_AARCH64)) { + if (arm_feature(env, ARM_FEATURE_V8)) { /* 32 bit view is [31:18] 0...0 [43:32]. */ uint32_t cbar32 = (extract64(cpu->reset_cbar, 18, 14) << 18) | extract64(cpu->reset_cbar, 32, 12); @@ -8343,7 +9718,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) ARMCPRegInfo cbar = { .name = "CBAR", .cp = 15, .crn = 15, .crm = 0, .opc1 = 4, .opc2 = 0, - .access = PL1_R|PL3_W, .resetvalue = cpu->reset_cbar, + .access = PL1_R | PL3_W, .resetvalue = cpu->reset_cbar, .fieldoffset = offsetof(CPUARMState, cp15.c15_config_base_address) }; @@ -8361,6 +9736,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "VBAR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .writefn = vbar_write, + .accessfn = access_nv1, + .fgt = FGT_VBAR_EL1, + .nv2_redirect_offset = 0x250 | NV2_REDIR_NV1, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.vbar_s), offsetof(CPUARMState, cp15.vbar_ns) }, .resetvalue = 0 }, @@ -8374,19 +9752,33 @@ void register_cp_regs_for_features(ARMCPU *cpu) .name = "SCTLR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, + .fgt = FGT_SCTLR_EL1, + .nv2_redirect_offset = 0x110 | NV2_REDIR_NV1, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.sctlr_s), offsetof(CPUARMState, cp15.sctlr_ns) }, .writefn = sctlr_write, .resetvalue = cpu->reset_sctlr, .raw_writefn = raw_write, }; if (arm_feature(env, ARM_FEATURE_XSCALE)) { - /* Normally we would always end the TB on an SCTLR write, but Linux + /* + * Normally we would always end the TB on an SCTLR write, but Linux * arch/arm/mach-pxa/sleep.S expects two instructions following * an MMU enable to execute from cache. Imitate this behaviour. */ sctlr.type |= ARM_CP_SUPPRESS_TB_END; } define_one_arm_cp_reg(cpu, &sctlr); + + if (arm_feature(env, ARM_FEATURE_PMSA) && + arm_feature(env, ARM_FEATURE_V8)) { + ARMCPRegInfo vsctlr = { + .name = "VSCTLR", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 0, + .access = PL2_RW, .resetvalue = 0x0, + .fieldoffset = offsetoflow32(CPUARMState, cp15.vsctlr), + }; + define_one_arm_cp_reg(cpu, &vsctlr); + } } if (cpu_isar_feature(aa64_lor, cpu)) { @@ -8434,6 +9826,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) } #ifdef TARGET_AARCH64 + if (cpu_isar_feature(aa64_sme, cpu)) { + define_arm_cp_regs(cpu, sme_reginfo); + } if (cpu_isar_feature(aa64_pauth, cpu)) { define_arm_cp_regs(cpu, pauth_reginfo); } @@ -8446,7 +9841,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_tlbios, cpu)) { define_arm_cp_regs(cpu, tlbios_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); @@ -8455,7 +9849,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_one_arm_cp_reg(cpu, dcpodp_reg); } } -#endif /*CONFIG_USER_ONLY*/ /* * If full MTE is enabled, add all of the system registers. @@ -8463,6 +9856,13 @@ void register_cp_regs_for_features(ARMCPU *cpu) * then define only a RAZ/WI version of PSTATE.TCO. */ if (cpu_isar_feature(aa64_mte, cpu)) { + ARMCPRegInfo gmid_reginfo = { + .name = "GMID_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 1, .crn = 0, .crm = 0, .opc2 = 4, + .access = PL1_R, .accessfn = access_aa64_tid5, + .type = ARM_CP_CONST, .resetvalue = cpu->gm_blocksize, + }; + define_one_arm_cp_reg(cpu, &gmid_reginfo); define_arm_cp_regs(cpu, mte_reginfo); define_arm_cp_regs(cpu, mte_el0_cacheop_reginfo); } else if (cpu_isar_feature(aa64_mte_insn_reg, cpu)) { @@ -8473,6 +9873,21 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_scxtnum, cpu)) { define_arm_cp_regs(cpu, scxtnum_reginfo); } + + if (cpu_isar_feature(aa64_fgt, cpu)) { + define_arm_cp_regs(cpu, fgt_reginfo); + } + + if (cpu_isar_feature(aa64_rme, cpu)) { + define_arm_cp_regs(cpu, rme_reginfo); + if (cpu_isar_feature(aa64_mte, cpu)) { + define_arm_cp_regs(cpu, rme_mte_reginfo); + } + } + + if (cpu_isar_feature(aa64_nv2, cpu)) { + define_arm_cp_regs(cpu, nv2_reginfo); + } #endif if (cpu_isar_feature(any_predinv, cpu)) { @@ -8494,75 +9909,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) #endif } -/* Sort alphabetically by type name, except for "any". */ -static gint arm_cpu_list_compare(gconstpointer a, gconstpointer b) -{ - ObjectClass *class_a = (ObjectClass *)a; - ObjectClass *class_b = (ObjectClass *)b; - const char *name_a, *name_b; - - name_a = object_class_get_name(class_a); - name_b = object_class_get_name(class_b); - if (strcmp(name_a, "any-" TYPE_ARM_CPU) == 0) { - return 1; - } else if (strcmp(name_b, "any-" TYPE_ARM_CPU) == 0) { - return -1; - } else { - return strcmp(name_a, name_b); - } -} - -static void arm_cpu_list_entry(gpointer data, gpointer user_data) -{ - ObjectClass *oc = data; - const char *typename; - char *name; - - typename = object_class_get_name(oc); - name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_ARM_CPU)); - qemu_printf(" %s\n", name); - g_free(name); -} - -void arm_cpu_list(void) -{ - GSList *list; - - list = object_class_get_list(TYPE_ARM_CPU, false); - list = g_slist_sort(list, arm_cpu_list_compare); - qemu_printf("Available CPUs:\n"); - g_slist_foreach(list, arm_cpu_list_entry, NULL); - g_slist_free(list); -} - -static void arm_cpu_add_definition(gpointer data, gpointer user_data) -{ - ObjectClass *oc = data; - CpuDefinitionInfoList **cpu_list = user_data; - CpuDefinitionInfo *info; - const char *typename; - - typename = object_class_get_name(oc); - info = g_malloc0(sizeof(*info)); - info->name = g_strndup(typename, - strlen(typename) - strlen("-" TYPE_ARM_CPU)); - info->q_typename = g_strdup(typename); - - QAPI_LIST_PREPEND(*cpu_list, info); -} - -CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) -{ - CpuDefinitionInfoList *cpu_list = NULL; - GSList *list; - - list = object_class_get_list(TYPE_ARM_CPU, false); - g_slist_foreach(list, arm_cpu_add_definition, &cpu_list); - g_slist_free(list); - - return cpu_list; -} - /* * Private utility function for define_one_arm_cp_reg_with_opaque(): * add a single reginfo struct to the hash table. @@ -8767,7 +10113,8 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, const ARMCPRegInfo *r, void *opaque) { - /* Define implementations of coprocessor registers. + /* + * Define implementations of coprocessor registers. * We store these in a hashtable because typically * there are less than 150 registers in a space which * is 16*16*16*8*8 = 262144 in size. @@ -8834,7 +10181,8 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, default: g_assert_not_reached(); } - /* The AArch64 pseudocode CheckSystemAccess() specifies that op1 + /* + * The AArch64 pseudocode CheckSystemAccess() specifies that op1 * encodes a minimum access level for the register. We roll this * runtime check into our general permission check code, so check * here that the reginfo's specified permissions are strict enough @@ -8876,7 +10224,8 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, assert((r->access & ~mask) == 0); } - /* Check that the register definition has enough info to handle + /* + * Check that the register definition has enough info to handle * reads and writes if they are permitted. */ if (!(r->type & (ARM_CP_SPECIAL_MASK | ARM_CP_CONST))) { @@ -8901,7 +10250,8 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, continue; } if (state == ARM_CP_STATE_AA32) { - /* Under AArch32 CP registers can be common + /* + * Under AArch32 CP registers can be common * (same for secure and non-secure world) or banked. */ char *name; @@ -8927,8 +10277,10 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, g_assert_not_reached(); } } else { - /* AArch64 registers get mapped to non-secure instance - * of AArch32 */ + /* + * AArch64 registers get mapped to non-secure instance + * of AArch32 + */ add_cpreg_to_hashtable(cpu, r, opaque, state, ARM_CP_SECSTATE_NS, crm, opc1, opc2, r->name); @@ -9014,7 +10366,8 @@ void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *opaque) static int bad_mode_switch(CPUARMState *env, int mode, CPSRWriteType write_type) { - /* Return true if it is not valid for us to switch to + /* + * Return true if it is not valid for us to switch to * this CPU mode (ie all the UNPREDICTABLE cases in * the ARM ARM CPSRWriteByInstr pseudocode). */ @@ -9035,10 +10388,12 @@ static int bad_mode_switch(CPUARMState *env, int mode, CPSRWriteType write_type) case ARM_CPU_MODE_UND: case ARM_CPU_MODE_IRQ: case ARM_CPU_MODE_FIQ: - /* Note that we don't implement the IMPDEF NSACR.RFR which in v7 + /* + * Note that we don't implement the IMPDEF NSACR.RFR which in v7 * allows FIQ mode to be Secure-only. (In v8 this doesn't exist.) */ - /* If HCR.TGE is set then changes from Monitor to NS PL1 via MSR + /* + * If HCR.TGE is set then changes from Monitor to NS PL1 via MSR * and CPS are treated as illegal mode changes. */ if (write_type == CPSRWriteByInstr && @@ -9080,10 +10435,12 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, env->CF = (val >> 29) & 1; env->VF = (val << 3) & 0x80000000; } - if (mask & CPSR_Q) + if (mask & CPSR_Q) { env->QF = ((val & CPSR_Q) != 0); - if (mask & CPSR_T) + } + if (mask & CPSR_T) { env->thumb = ((val & CPSR_T) != 0); + } if (mask & CPSR_IT_0_1) { env->condexec_bits &= ~3; env->condexec_bits |= (val >> 25) & 3; @@ -9096,7 +10453,8 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, env->GE = (val >> 16) & 0xf; } - /* In a V7 implementation that includes the security extensions but does + /* + * In a V7 implementation that includes the security extensions but does * not include Virtualization Extensions the SCR.FW and SCR.AW bits control * whether non-secure software is allowed to change the CPSR_F and CPSR_A * bits respectively. @@ -9112,7 +10470,8 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, changed_daif = (env->daif ^ val) & mask; if (changed_daif & CPSR_A) { - /* Check to see if we are allowed to change the masking of async + /* + * Check to see if we are allowed to change the masking of async * abort exceptions from a non-secure state. */ if (!(env->cp15.scr_el3 & SCR_AW)) { @@ -9124,7 +10483,8 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, } if (changed_daif & CPSR_F) { - /* Check to see if we are allowed to change the masking of FIQ + /* + * Check to see if we are allowed to change the masking of FIQ * exceptions from a non-secure state. */ if (!(env->cp15.scr_el3 & SCR_FW)) { @@ -9134,7 +10494,8 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, mask &= ~CPSR_F; } - /* Check whether non-maskable FIQ (NMFI) support is enabled. + /* + * Check whether non-maskable FIQ (NMFI) support is enabled. * If this bit is set software is not allowed to mask * FIQs, but is allowed to set CPSR_F to 0. */ @@ -9154,7 +10515,8 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, if (write_type != CPSRWriteRaw && ((env->uncached_cpsr ^ val) & mask & CPSR_M)) { if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR) { - /* Note that we can only get here in USR mode if this is a + /* + * Note that we can only get here in USR mode if this is a * gdb stub write; for this case we follow the architectural * behaviour for guest writes in USR mode of ignoring an attempt * to switch mode. (Those are caught by translate.c for writes @@ -9162,7 +10524,8 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, */ mask &= ~CPSR_M; } else if (bad_mode_switch(env, val & CPSR_M, write_type)) { - /* Attempt to switch to an invalid mode: this is UNPREDICTABLE in + /* + * Attempt to switch to an invalid mode: this is UNPREDICTABLE in * v7, and has defined behaviour in v8: * + leave CPSR.M untouched * + allow changes to the other CPSR fields @@ -9192,66 +10555,11 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, } mask &= ~CACHED_CPSR_BITS; env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask); - if (rebuild_hflags) { + if (tcg_enabled() && rebuild_hflags) { arm_rebuild_hflags(env); } } -/* Sign/zero extend */ -uint32_t HELPER(sxtb16)(uint32_t x) -{ - uint32_t res; - res = (uint16_t)(int8_t)x; - res |= (uint32_t)(int8_t)(x >> 16) << 16; - return res; -} - -static void handle_possible_div0_trap(CPUARMState *env, uintptr_t ra) -{ - /* - * Take a division-by-zero exception if necessary; otherwise return - * to get the usual non-trapping division behaviour (result of 0) - */ - if (arm_feature(env, ARM_FEATURE_M) - && (env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_DIV_0_TRP_MASK)) { - raise_exception_ra(env, EXCP_DIVBYZERO, 0, 1, ra); - } -} - -uint32_t HELPER(uxtb16)(uint32_t x) -{ - uint32_t res; - res = (uint16_t)(uint8_t)x; - res |= (uint32_t)(uint8_t)(x >> 16) << 16; - return res; -} - -int32_t HELPER(sdiv)(CPUARMState *env, int32_t num, int32_t den) -{ - if (den == 0) { - handle_possible_div0_trap(env, GETPC()); - return 0; - } - if (num == INT_MIN && den == -1) { - return INT_MIN; - } - return num / den; -} - -uint32_t HELPER(udiv)(CPUARMState *env, uint32_t num, uint32_t den) -{ - if (den == 0) { - handle_possible_div0_trap(env, GETPC()); - return 0; - } - return num / den; -} - -uint32_t HELPER(rbit)(uint32_t x) -{ - return revbit32(x); -} - #ifdef CONFIG_USER_ONLY static void switch_mode(CPUARMState *env, int mode) @@ -9282,15 +10590,16 @@ static void switch_mode(CPUARMState *env, int mode) int i; old_mode = env->uncached_cpsr & CPSR_M; - if (mode == old_mode) + if (mode == old_mode) { return; + } if (old_mode == ARM_CPU_MODE_FIQ) { - memcpy (env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t)); - memcpy (env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t)); + memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t)); + memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t)); } else if (mode == ARM_CPU_MODE_FIQ) { - memcpy (env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t)); - memcpy (env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t)); + memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t)); + memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t)); } i = bank_number(old_mode); @@ -9305,7 +10614,8 @@ static void switch_mode(CPUARMState *env, int mode) env->regs[14] = env->banked_r14[r14_bank_number(mode)]; } -/* Physical Interrupt Target EL Lookup Table +/* + * Physical Interrupt Target EL Lookup Table * * [ From ARM ARM section G1.13.4 (Table G1-15) ] * @@ -9367,7 +10677,7 @@ static const int8_t target_el_table[2][2][2][2][2][4] = { uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, uint32_t cur_el, bool secure) { - CPUARMState *env = cs->env_ptr; + CPUARMState *env = cpu_env(cs); bool rw; bool scr; bool hcr; @@ -9379,7 +10689,8 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, if (arm_feature(env, ARM_FEATURE_EL3)) { rw = ((env->cp15.scr_el3 & SCR_RW) == SCR_RW); } else { - /* Either EL2 is the highest EL (and so the EL2 register width + /* + * Either EL2 is the highest EL (and so the EL2 register width * is given by is64); or there is no EL2 or EL3, in which case * the value of 'rw' does not affect the table lookup anyway. */ @@ -9446,6 +10757,7 @@ void arm_log_exception(CPUState *cs) [EXCP_UNALIGNED] = "v7M UNALIGNED UsageFault", [EXCP_DIVBYZERO] = "v7M DIVBYZERO UsageFault", [EXCP_VSERR] = "Virtual SERR", + [EXCP_GPC] = "Granule Protection Check", }; if (idx >= 0 && idx < ARRAY_SIZE(excnames)) { @@ -9654,7 +10966,8 @@ void aarch64_sync_64_to_32(CPUARMState *env) env->banked_r13[bank_number(ARM_CPU_MODE_UND)] = env->xregs[23]; } - /* Registers x24-x30 are mapped to r8-r14 in FIQ mode. If we are in FIQ + /* + * Registers x24-x30 are mapped to r8-r14 in FIQ mode. If we are in FIQ * mode, then we can copy to r8-r14. Otherwise, we copy to the * FIQ bank for r8-r14. */ @@ -9747,7 +11060,10 @@ static void take_aarch32_exception(CPUARMState *env, int new_mode, env->regs[14] = env->regs[15] + offset; } env->regs[15] = newpc; - arm_rebuild_hflags(env); + + if (tcg_enabled()) { + arm_rebuild_hflags(env); + } } static void arm_cpu_do_interrupt_aarch32_hyp(CPUState *cs) @@ -9877,6 +11193,24 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs) } if (env->exception.target_el == 2) { + /* Debug exceptions are reported differently on AArch32 */ + switch (syn_get_ec(env->exception.syndrome)) { + case EC_BREAKPOINT: + case EC_BREAKPOINT_SAME_EL: + case EC_AA32_BKPT: + case EC_VECTORCATCH: + env->exception.syndrome = syn_insn_abort(arm_current_el(env) == 2, + 0, 0, 0x22); + break; + case EC_WATCHPOINT: + env->exception.syndrome = syn_set_ec(env->exception.syndrome, + EC_DATAABORT); + break; + case EC_WATCHPOINT_SAME_EL: + env->exception.syndrome = syn_set_ec(env->exception.syndrome, + EC_DATAABORT_SAME_EL); + break; + } arm_cpu_do_interrupt_aarch32_hyp(cs); return; } @@ -9886,10 +11220,11 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs) new_mode = ARM_CPU_MODE_UND; addr = 0x04; mask = CPSR_I; - if (env->thumb) + if (env->thumb) { offset = 2; - else + } else { offset = 4; + } break; case EXCP_SWI: new_mode = ARM_CPU_MODE_SVC; @@ -10000,7 +11335,8 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs) /* High vectors. When enabled, base address cannot be remapped. */ addr += 0xffff0000; } else { - /* ARM v7 architectures provide a vector base address register to remap + /* + * ARM v7 architectures provide a vector base address register to remap * the interrupt vector table. * This register is only followed in non-monitor mode, and is banked. * Note: only bits 31:5 are valid. @@ -10127,14 +11463,17 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) unsigned int cur_el = arm_current_el(env); int rt; - /* - * Note that new_el can never be 0. If cur_el is 0, then - * el0_a64 is is_a64(), else el0_a64 is ignored. - */ - aarch64_sve_change_el(env, cur_el, new_el, is_a64(env)); + if (tcg_enabled()) { + /* + * Note that new_el can never be 0. If cur_el is 0, then + * el0_a64 is is_a64(), else el0_a64 is ignored. + */ + aarch64_sve_change_el(env, cur_el, new_el, is_a64(env)); + } if (cur_el < new_el) { - /* Entry vector offset depends on whether the implemented EL + /* + * Entry vector offset depends on whether the implemented EL * immediately lower than the target level is using AArch32 or AArch64 */ bool is_aa64; @@ -10168,6 +11507,10 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) } switch (cs->exception_index) { + case EXCP_GPC: + qemu_log_mask(CPU_LOG_INT, "...with MFAR 0x%" PRIx64 "\n", + env->cp15.mfar_el3); + /* fall through */ case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: /* @@ -10250,6 +11593,20 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) old_mode = pstate_read(env); aarch64_save_sp(env, arm_current_el(env)); env->elr_el[new_el] = env->pc; + + if (cur_el == 1 && new_el == 1) { + uint64_t hcr = arm_hcr_el2_eff(env); + if ((hcr & (HCR_NV | HCR_NV1 | HCR_NV2)) == HCR_NV || + (hcr & (HCR_NV | HCR_NV2)) == (HCR_NV | HCR_NV2)) { + /* + * FEAT_NV, FEAT_NV2 may need to report EL2 in the SPSR + * by setting M[3:2] to 0b10. + * If NV2 is disabled, change SPSR when NV,NV1 == 1,0 (I_ZJRNN) + * If NV2 is enabled, change SPSR when NV is 1 (I_DBTLM) + */ + old_mode = deposit32(old_mode, 2, 2, 2); + } + } } else { old_mode = cpsr_read_for_spsr_elx(env); env->elr_el[new_el] = env->regs[15]; @@ -10260,6 +11617,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) } env->banked_spsr[aarch64_banked_spsr_index(new_el)] = old_mode; + qemu_log_mask(CPU_LOG_INT, "...with SPSR 0x%x\n", old_mode); qemu_log_mask(CPU_LOG_INT, "...with ELR 0x%" PRIx64 "\n", env->elr_el[new_el]); @@ -10298,7 +11656,10 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) pstate_write(env, PSTATE_DAIF | new_mode); env->aarch64 = true; aarch64_restore_sp(env, new_el); - helper_rebuild_hflags_a64(env, new_el); + + if (tcg_enabled()) { + helper_rebuild_hflags_a64(env, new_el); + } env->pc = addr; @@ -10314,7 +11675,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) * trapped to the hypervisor in KVM. */ #ifdef CONFIG_TCG -static void handle_semihosting(CPUState *cs) +static void tcg_handle_semihosting(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -10323,19 +11684,20 @@ static void handle_semihosting(CPUState *cs) qemu_log_mask(CPU_LOG_INT, "...handling as semihosting call 0x%" PRIx64 "\n", env->xregs[0]); - env->xregs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->pc += 4; } else { qemu_log_mask(CPU_LOG_INT, "...handling as semihosting call 0x%x\n", env->regs[0]); - env->regs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); env->regs[15] += env->thumb ? 2 : 4; } } #endif -/* Handle a CPU exception for A and R profile CPUs. +/* + * Handle a CPU exception for A and R profile CPUs. * Do any appropriate logging, handle PSCI calls, and then hand off * to the AArch64-entry or AArch32-entry function depending on the * target exception level's register width. @@ -10362,7 +11724,7 @@ void arm_cpu_do_interrupt(CPUState *cs) env->exception.syndrome); } - if (arm_is_psci_call(cpu, cs->exception_index)) { + if (tcg_enabled() && arm_is_psci_call(cpu, cs->exception_index)) { arm_handle_psci_call(cpu); qemu_log_mask(CPU_LOG_INT, "...handled as PSCI call\n"); return; @@ -10375,16 +11737,17 @@ void arm_cpu_do_interrupt(CPUState *cs) */ #ifdef CONFIG_TCG if (cs->exception_index == EXCP_SEMIHOST) { - handle_semihosting(cs); + tcg_handle_semihosting(cs); return; } #endif - /* Hooks may change global state so BQL should be held, also the + /* + * Hooks may change global state so BQL should be held, also the * BQL needs to be held for any modification of * cs->interrupt_request. */ - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); arm_call_pre_el_change_hook(cpu); @@ -10408,8 +11771,7 @@ 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 || mmu_idx == ARMMMUIdx_SE20_0) - ? 2 : 1; + el = mmu_idx == ARMMMUIdx_E20_0 ? 2 : 1; } return env->cp15.sctlr_el[el]; } @@ -10418,7 +11780,7 @@ int aa64_va_parameter_tbi(uint64_t tcr, ARMMMUIdx mmu_idx) { if (regime_has_2_ranges(mmu_idx)) { return extract64(tcr, 37, 2); - } else if (mmu_idx == ARMMMUIdx_Stage2 || mmu_idx == ARMMMUIdx_Stage2_S) { + } else if (regime_is_stage2(mmu_idx)) { return 0; /* VTCR_EL2 */ } else { /* Replicate the single TBI bit so we always have 2 bits. */ @@ -10430,7 +11792,7 @@ 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 || mmu_idx == ARMMMUIdx_Stage2_S) { + } else if (regime_is_stage2(mmu_idx)) { return 0; /* VTCR_EL2 */ } else { /* Replicate the single TBID bit so we always have 2 bits. */ @@ -10438,7 +11800,7 @@ int aa64_va_parameter_tbid(uint64_t tcr, ARMMMUIdx mmu_idx) } } -static int aa64_va_parameter_tcma(uint64_t tcr, ARMMMUIdx mmu_idx) +int aa64_va_parameter_tcma(uint64_t tcr, ARMMMUIdx mmu_idx) { if (regime_has_2_ranges(mmu_idx)) { return extract64(tcr, 57, 2); @@ -10448,20 +11810,106 @@ static int aa64_va_parameter_tcma(uint64_t tcr, ARMMMUIdx mmu_idx) } } -ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, - ARMMMUIdx mmu_idx, bool data) +static ARMGranuleSize tg0_to_gran_size(int tg) { - uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; - bool epd, hpd, using16k, using64k, tsz_oob, ds; + switch (tg) { + case 0: + return Gran4K; + case 1: + return Gran64K; + case 2: + return Gran16K; + default: + return GranInvalid; + } +} + +static ARMGranuleSize tg1_to_gran_size(int tg) +{ + switch (tg) { + case 1: + return Gran16K; + case 2: + return Gran4K; + case 3: + return Gran64K; + default: + return GranInvalid; + } +} + +static inline bool have4k(ARMCPU *cpu, bool stage2) +{ + return stage2 ? cpu_isar_feature(aa64_tgran4_2, cpu) + : cpu_isar_feature(aa64_tgran4, cpu); +} + +static inline bool have16k(ARMCPU *cpu, bool stage2) +{ + return stage2 ? cpu_isar_feature(aa64_tgran16_2, cpu) + : cpu_isar_feature(aa64_tgran16, cpu); +} + +static inline bool have64k(ARMCPU *cpu, bool stage2) +{ + return stage2 ? cpu_isar_feature(aa64_tgran64_2, cpu) + : cpu_isar_feature(aa64_tgran64, cpu); +} + +static ARMGranuleSize sanitize_gran_size(ARMCPU *cpu, ARMGranuleSize gran, + bool stage2) +{ + switch (gran) { + case Gran4K: + if (have4k(cpu, stage2)) { + return gran; + } + break; + case Gran16K: + if (have16k(cpu, stage2)) { + return gran; + } + break; + case Gran64K: + if (have64k(cpu, stage2)) { + return gran; + } + break; + case GranInvalid: + break; + } + /* + * If the guest selects a granule size that isn't implemented, + * the architecture requires that we behave as if it selected one + * that is (with an IMPDEF choice of which one to pick). We choose + * to implement the smallest supported granule size. + */ + if (have4k(cpu, stage2)) { + return Gran4K; + } + if (have16k(cpu, stage2)) { + return Gran16K; + } + assert(have64k(cpu, stage2)); + return Gran64K; +} + +ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, + ARMMMUIdx mmu_idx, bool data, + bool el1_is_aa32) +{ + uint64_t tcr = regime_tcr(env, mmu_idx); + bool epd, hpd, tsz_oob, ds, ha, hd; int select, tsz, tbi, max_tsz, min_tsz, ps, sh; + ARMGranuleSize gran; ARMCPU *cpu = env_archcpu(env); + bool stage2 = regime_is_stage2(mmu_idx); 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 || mmu_idx == ARMMMUIdx_Stage2_S) { + gran = tg0_to_gran_size(extract32(tcr, 14, 2)); + if (stage2) { /* VTCR_EL2 */ hpd = false; } else { @@ -10470,8 +11918,12 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, epd = false; sh = extract32(tcr, 12, 2); ps = extract32(tcr, 16, 3); + ha = extract32(tcr, 21, 1) && cpu_isar_feature(aa64_hafs, cpu); + hd = extract32(tcr, 22, 1) && cpu_isar_feature(aa64_hdbs, cpu); ds = extract64(tcr, 32, 1); } else { + bool e0pd; + /* * Bit 55 is always between the two regions, and is canonical for * determining if address tagging is enabled. @@ -10479,26 +11931,34 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, select = extract64(va, 55, 1); if (!select) { tsz = extract32(tcr, 0, 6); + gran = tg0_to_gran_size(extract32(tcr, 14, 2)); epd = extract32(tcr, 7, 1); sh = extract32(tcr, 12, 2); - using64k = extract32(tcr, 14, 1); - using16k = extract32(tcr, 15, 1); hpd = extract64(tcr, 41, 1); + e0pd = extract64(tcr, 55, 1); } else { - int tg = extract32(tcr, 30, 2); - using16k = tg == 1; - using64k = tg == 3; tsz = extract32(tcr, 16, 6); + gran = tg1_to_gran_size(extract32(tcr, 30, 2)); epd = extract32(tcr, 23, 1); sh = extract32(tcr, 28, 2); hpd = extract64(tcr, 42, 1); + e0pd = extract64(tcr, 56, 1); } ps = extract64(tcr, 32, 3); + ha = extract64(tcr, 39, 1) && cpu_isar_feature(aa64_hafs, cpu); + hd = extract64(tcr, 40, 1) && cpu_isar_feature(aa64_hdbs, cpu); ds = extract64(tcr, 59, 1); + + if (e0pd && cpu_isar_feature(aa64_e0pd, cpu) && + regime_is_user(env, mmu_idx)) { + epd = true; + } } + gran = sanitize_gran_size(cpu, gran, stage2); + if (cpu_isar_feature(aa64_st, cpu)) { - max_tsz = 48 - using64k; + max_tsz = 48 - (gran == Gran64K); } else { max_tsz = 39; } @@ -10508,34 +11968,40 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, * adjust the effective value of DS, as documented. */ min_tsz = 16; - if (using64k) { + if (gran == Gran64K) { if (cpu_isar_feature(aa64_lva, cpu)) { min_tsz = 12; } ds = false; } else if (ds) { - switch (mmu_idx) { - case ARMMMUIdx_Stage2: - case ARMMMUIdx_Stage2_S: - if (using16k) { + if (regime_is_stage2(mmu_idx)) { + if (gran == Gran16K) { ds = cpu_isar_feature(aa64_tgran16_2_lpa2, cpu); } else { ds = cpu_isar_feature(aa64_tgran4_2_lpa2, cpu); } - break; - default: - if (using16k) { + } else { + if (gran == Gran16K) { ds = cpu_isar_feature(aa64_tgran16_lpa2, cpu); } else { ds = cpu_isar_feature(aa64_tgran4_lpa2, cpu); } - break; } if (ds) { min_tsz = 12; } } + if (stage2 && el1_is_aa32) { + /* + * For AArch32 EL1 the min txsz (and thus max IPA size) requirements + * are loosened: a configured IPA of 40 bits is permitted even if + * the implemented PA is less than that (and so a 40 bit IPA would + * fault for an AArch64 EL1). See R_DTLMN. + */ + min_tsz = MIN(min_tsz, 24); + } + if (tsz > max_tsz) { tsz = max_tsz; tsz_oob = true; @@ -10561,16 +12027,19 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, .tbi = tbi, .epd = epd, .hpd = hpd, - .using16k = using16k, - .using64k = using64k, .tsz_oob = tsz_oob, .ds = ds, + .ha = ha, + .hd = ha && hd, + .gran = gran, }; } -/* Note that signed overflow is undefined in C. The following routines are - careful to use unsigned types where modulo arithmetic is required. - Failure to do so _will_ break on newer gcc. */ +/* + * Note that signed overflow is undefined in C. The following routines are + * careful to use unsigned types where modulo arithmetic is required. + * Failure to do so _will_ break on newer gcc. + */ /* Signed saturating arithmetic. */ @@ -10581,10 +12050,11 @@ static inline uint16_t add16_sat(uint16_t a, uint16_t b) res = a + b; if (((res ^ a) & 0x8000) && !((a ^ b) & 0x8000)) { - if (a & 0x8000) + if (a & 0x8000) { res = 0x8000; - else + } else { res = 0x7fff; + } } return res; } @@ -10596,10 +12066,11 @@ static inline uint8_t add8_sat(uint8_t a, uint8_t b) res = a + b; if (((res ^ a) & 0x80) && !((a ^ b) & 0x80)) { - if (a & 0x80) + if (a & 0x80) { res = 0x80; - else + } else { res = 0x7f; + } } return res; } @@ -10611,10 +12082,11 @@ static inline uint16_t sub16_sat(uint16_t a, uint16_t b) res = a - b; if (((res ^ a) & 0x8000) && ((a ^ b) & 0x8000)) { - if (a & 0x8000) + if (a & 0x8000) { res = 0x8000; - else + } else { res = 0x7fff; + } } return res; } @@ -10626,10 +12098,11 @@ static inline uint8_t sub8_sat(uint8_t a, uint8_t b) res = a - b; if (((res ^ a) & 0x80) && ((a ^ b) & 0x80)) { - if (a & 0x80) + if (a & 0x80) { res = 0x80; - else + } else { res = 0x7f; + } } return res; } @@ -10647,34 +12120,38 @@ static inline uint16_t add16_usat(uint16_t a, uint16_t b) { uint16_t res; res = a + b; - if (res < a) + if (res < a) { res = 0xffff; + } return res; } static inline uint16_t sub16_usat(uint16_t a, uint16_t b) { - if (a > b) + if (a > b) { return a - b; - else + } else { return 0; + } } static inline uint8_t add8_usat(uint8_t a, uint8_t b) { uint8_t res; res = a + b; - if (res < a) + if (res < a) { res = 0xff; + } return res; } static inline uint8_t sub8_usat(uint8_t a, uint8_t b) { - if (a > b) + if (a > b) { return a - b; - else + } else { return 0; + } } #define ADD16(a, b, n) RESULT(add16_usat(a, b), n, 16); @@ -10692,7 +12169,7 @@ static inline uint8_t sub8_usat(uint8_t a, uint8_t b) RESULT(sum, n, 16); \ if (sum >= 0) \ ge |= 3 << (n * 2); \ - } while(0) + } while (0) #define SARITH8(a, b, n, op) do { \ int32_t sum; \ @@ -10700,7 +12177,7 @@ static inline uint8_t sub8_usat(uint8_t a, uint8_t b) RESULT(sum, n, 8); \ if (sum >= 0) \ ge |= 1 << n; \ - } while(0) + } while (0) #define ADD16(a, b, n) SARITH16(a, b, n, +) @@ -10719,7 +12196,7 @@ static inline uint8_t sub8_usat(uint8_t a, uint8_t b) RESULT(sum, n, 16); \ if ((sum >> 16) == 1) \ ge |= 3 << (n * 2); \ - } while(0) + } while (0) #define ADD8(a, b, n) do { \ uint32_t sum; \ @@ -10727,7 +12204,7 @@ static inline uint8_t sub8_usat(uint8_t a, uint8_t b) RESULT(sum, n, 8); \ if ((sum >> 8) == 1) \ ge |= 1 << n; \ - } while(0) + } while (0) #define SUB16(a, b, n) do { \ uint32_t sum; \ @@ -10735,7 +12212,7 @@ static inline uint8_t sub8_usat(uint8_t a, uint8_t b) RESULT(sum, n, 16); \ if ((sum >> 16) == 0) \ ge |= 3 << (n * 2); \ - } while(0) + } while (0) #define SUB8(a, b, n) do { \ uint32_t sum; \ @@ -10743,7 +12220,7 @@ static inline uint8_t sub8_usat(uint8_t a, uint8_t b) RESULT(sum, n, 8); \ if ((sum >> 8) == 0) \ ge |= 1 << n; \ - } while(0) + } while (0) #define PFX u #define ARITH_GE @@ -10778,10 +12255,11 @@ static inline uint8_t sub8_usat(uint8_t a, uint8_t b) static inline uint8_t do_usad(uint8_t a, uint8_t b) { - if (a > b) + if (a > b) { return a - b; - else + } else { return b - a; + } } /* Unsigned sum of absolute byte differences. */ @@ -10801,18 +12279,23 @@ uint32_t HELPER(sel_flags)(uint32_t flags, uint32_t a, uint32_t b) uint32_t mask; mask = 0; - if (flags & 1) + if (flags & 1) { mask |= 0xff; - if (flags & 2) + } + if (flags & 2) { mask |= 0xff00; - if (flags & 4) + } + if (flags & 4) { mask |= 0xff0000; - if (flags & 8) + } + if (flags & 8) { mask |= 0xff000000; + } return (a & mask) | (b & ~mask); } -/* CRC helpers. +/* + * CRC helpers. * The upper bytes of val (above the number specified by 'bytes') must have * been zeroed out by the caller. */ @@ -10836,7 +12319,8 @@ uint32_t HELPER(crc32c)(uint32_t acc, uint32_t val, uint32_t bytes) return crc32c(acc, buf, bytes) ^ 0xffffffff; } -/* Return the exception level to which FP-disabled exceptions should +/* + * Return the exception level to which FP-disabled exceptions should * be taken, or 0 if FP is enabled. */ int fp_exception_el(CPUARMState *env, int cur_el) @@ -10844,7 +12328,8 @@ int fp_exception_el(CPUARMState *env, int cur_el) #ifndef CONFIG_USER_ONLY uint64_t hcr_el2; - /* CPACR and the CPTR registers don't exist before v6, so FP is + /* + * CPACR and the CPTR registers don't exist before v6, so FP is * always accessible */ if (!arm_feature(env, ARM_FEATURE_V6)) { @@ -10869,7 +12354,8 @@ int fp_exception_el(CPUARMState *env, int cur_el) hcr_el2 = arm_hcr_el2_eff(env); - /* The CPACR controls traps to EL1, or PL1 if we're 32 bit: + /* + * The CPACR controls traps to EL1, or PL1 if we're 32 bit: * 0, 2 : trap EL0 and EL1/PL1 accesses * 1 : trap only EL0 accesses * 3 : trap no accesses @@ -10879,27 +12365,22 @@ int fp_exception_el(CPUARMState *env, int cur_el) int fpen = FIELD_EX64(env->cp15.cpacr_el1, CPACR_EL1, FPEN); switch (fpen) { + case 1: + if (cur_el != 0) { + break; + } + /* fall through */ 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 */ + /* Trap from Secure PL0 or PL1 to Secure PL1. */ + if (!arm_el_is_aa64(env, 3) + && (cur_el == 3 || arm_is_secure_below_el3(env))) { return 3; } - break; - case 1: - if (cur_el == 0) { + if (cur_el <= 1) { return 1; } break; - case 3: - break; } } @@ -10958,22 +12439,15 @@ int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx) switch (mmu_idx) { case ARMMMUIdx_E10_0: case ARMMMUIdx_E20_0: - case ARMMMUIdx_SE10_0: - case ARMMMUIdx_SE20_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: - case ARMMMUIdx_SE2: - case ARMMMUIdx_SE20_2: - case ARMMMUIdx_SE20_2_PAN: return 2; - case ARMMMUIdx_SE3: + case ARMMMUIdx_E3: return 3; default: g_assert_not_reached(); @@ -11007,7 +12481,7 @@ ARMMMUIdx arm_mmu_idx_el(CPUARMState *env, int el) } break; case 1: - if (env->pstate & PSTATE_PAN) { + if (arm_pan_enabled(env)) { idx = ARMMMUIdx_E10_1_PAN; } else { idx = ARMMMUIdx_E10_1; @@ -11016,7 +12490,7 @@ ARMMMUIdx arm_mmu_idx_el(CPUARMState *env, int el) case 2: /* Note that TGE does not apply at EL2. */ if (arm_hcr_el2_eff(env) & HCR_E2H) { - if (env->pstate & PSTATE_PAN) { + if (arm_pan_enabled(env)) { idx = ARMMMUIdx_E20_2_PAN; } else { idx = ARMMMUIdx_E20_2; @@ -11026,15 +12500,11 @@ ARMMMUIdx arm_mmu_idx_el(CPUARMState *env, int el) } break; case 3: - return ARMMMUIdx_SE3; + return ARMMMUIdx_E3; default: g_assert_not_reached(); } - if (arm_is_secure_below_el3(env)) { - idx &= ~ARM_MMU_IDX_A_NS; - } - return idx; } @@ -11043,313 +12513,6 @@ ARMMMUIdx arm_mmu_idx(CPUARMState *env) return arm_mmu_idx_el(env, arm_current_el(env)); } -static CPUARMTBFlags rebuild_hflags_common(CPUARMState *env, int fp_el, - ARMMMUIdx mmu_idx, - CPUARMTBFlags flags) -{ - DP_TBFLAG_ANY(flags, FPEXC_EL, fp_el); - DP_TBFLAG_ANY(flags, MMUIDX, arm_to_core_mmu_idx(mmu_idx)); - - if (arm_singlestep_active(env)) { - DP_TBFLAG_ANY(flags, SS_ACTIVE, 1); - } - return flags; -} - -static CPUARMTBFlags rebuild_hflags_common_32(CPUARMState *env, int fp_el, - ARMMMUIdx mmu_idx, - CPUARMTBFlags flags) -{ - bool sctlr_b = arm_sctlr_b(env); - - if (sctlr_b) { - DP_TBFLAG_A32(flags, SCTLR__B, 1); - } - if (arm_cpu_data_is_big_endian_a32(env, sctlr_b)) { - DP_TBFLAG_ANY(flags, BE_DATA, 1); - } - DP_TBFLAG_A32(flags, NS, !access_secure_reg(env)); - - return rebuild_hflags_common(env, fp_el, mmu_idx, flags); -} - -static CPUARMTBFlags rebuild_hflags_m32(CPUARMState *env, int fp_el, - ARMMMUIdx mmu_idx) -{ - CPUARMTBFlags flags = {}; - uint32_t ccr = env->v7m.ccr[env->v7m.secure]; - - /* Without HaveMainExt, CCR.UNALIGN_TRP is RES1. */ - if (ccr & R_V7M_CCR_UNALIGN_TRP_MASK) { - DP_TBFLAG_ANY(flags, ALIGN_MEM, 1); - } - - if (arm_v7m_is_handler_mode(env)) { - DP_TBFLAG_M32(flags, HANDLER, 1); - } - - /* - * v8M always applies stack limit checks unless CCR.STKOFHFNMIGN - * is suppressing them because the requested execution priority - * is less than 0. - */ - if (arm_feature(env, ARM_FEATURE_V8) && - !((mmu_idx & ARM_MMU_IDX_M_NEGPRI) && - (ccr & R_V7M_CCR_STKOFHFNMIGN_MASK))) { - DP_TBFLAG_M32(flags, STACKCHECK, 1); - } - - return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags); -} - -static CPUARMTBFlags rebuild_hflags_aprofile(CPUARMState *env) -{ - CPUARMTBFlags flags = {}; - - DP_TBFLAG_ANY(flags, DEBUG_TARGET_EL, arm_debug_target_el(env)); - return flags; -} - -static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, - ARMMMUIdx mmu_idx) -{ - CPUARMTBFlags flags = rebuild_hflags_aprofile(env); - int el = arm_current_el(env); - - if (arm_sctlr(env, el) & SCTLR_A) { - DP_TBFLAG_ANY(flags, ALIGN_MEM, 1); - } - - if (arm_el_is_aa64(env, 1)) { - DP_TBFLAG_A32(flags, VFPEN, 1); - } - - if (el < 2 && env->cp15.hstr_el2 && - (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { - DP_TBFLAG_A32(flags, HSTR_ACTIVE, 1); - } - - if (env->uncached_cpsr & CPSR_IL) { - DP_TBFLAG_ANY(flags, PSTATE__IL, 1); - } - - return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags); -} - -static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, - ARMMMUIdx mmu_idx) -{ - CPUARMTBFlags flags = rebuild_hflags_aprofile(env); - ARMMMUIdx stage1 = stage_1_mmu_idx(mmu_idx); - uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; - uint64_t sctlr; - int tbii, tbid; - - DP_TBFLAG_ANY(flags, AARCH64_STATE, 1); - - /* Get control bits for tagged addresses. */ - tbid = aa64_va_parameter_tbi(tcr, mmu_idx); - tbii = tbid & ~aa64_va_parameter_tbid(tcr, mmu_idx); - - DP_TBFLAG_A64(flags, TBII, tbii); - DP_TBFLAG_A64(flags, TBID, tbid); - - if (cpu_isar_feature(aa64_sve, env_archcpu(env))) { - int sve_el = sve_exception_el(env, el); - - /* - * If either FP or SVE are disabled, translator does not need len. - * If SVE EL > FP EL, FP exception has precedence, and translator - * does not need SVE EL. Save potential re-translations by forcing - * the unneeded data to zero. - */ - if (fp_el != 0) { - if (sve_el > fp_el) { - sve_el = 0; - } - } else if (sve_el == 0) { - DP_TBFLAG_A64(flags, VL, sve_vqm1_for_el(env, el)); - } - DP_TBFLAG_A64(flags, SVEEXC_EL, sve_el); - } - - sctlr = regime_sctlr(env, stage1); - - if (sctlr & SCTLR_A) { - DP_TBFLAG_ANY(flags, ALIGN_MEM, 1); - } - - if (arm_cpu_data_is_big_endian_a64(el, sctlr)) { - DP_TBFLAG_ANY(flags, BE_DATA, 1); - } - - if (cpu_isar_feature(aa64_pauth, env_archcpu(env))) { - /* - * In order to save space in flags, we record only whether - * pauth is "inactive", meaning all insns are implemented as - * a nop, or "active" when some action must be performed. - * The decision of which action to take is left to a helper. - */ - if (sctlr & (SCTLR_EnIA | SCTLR_EnIB | SCTLR_EnDA | SCTLR_EnDB)) { - DP_TBFLAG_A64(flags, PAUTH_ACTIVE, 1); - } - } - - if (cpu_isar_feature(aa64_bti, env_archcpu(env))) { - /* Note that SCTLR_EL[23].BT == SCTLR_BT1. */ - if (sctlr & (el == 0 ? SCTLR_BT0 : SCTLR_BT1)) { - DP_TBFLAG_A64(flags, BT, 1); - } - } - - /* 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 */ - DP_TBFLAG_A64(flags, UNPRIV, 1); - break; - case ARMMMUIdx_E20_2: - case ARMMMUIdx_E20_2_PAN: - case ARMMMUIdx_SE20_2: - case ARMMMUIdx_SE20_2_PAN: - /* - * 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) { - DP_TBFLAG_A64(flags, UNPRIV, 1); - } - break; - default: - break; - } - } - - if (env->pstate & PSTATE_IL) { - DP_TBFLAG_ANY(flags, PSTATE__IL, 1); - } - - if (cpu_isar_feature(aa64_mte, env_archcpu(env))) { - /* - * Set MTE_ACTIVE if any access may be Checked, and leave clear - * if all accesses must be Unchecked: - * 1) If no TBI, then there are no tags in the address to check, - * 2) If Tag Check Override, then all accesses are Unchecked, - * 3) If Tag Check Fail == 0, then Checked access have no effect, - * 4) If no Allocation Tag Access, then all accesses are Unchecked. - */ - if (allocation_tag_access_enabled(env, el, sctlr)) { - DP_TBFLAG_A64(flags, ATA, 1); - if (tbid - && !(env->pstate & PSTATE_TCO) - && (sctlr & (el == 0 ? SCTLR_TCF0 : SCTLR_TCF))) { - DP_TBFLAG_A64(flags, MTE_ACTIVE, 1); - } - } - /* And again for unprivileged accesses, if required. */ - if (EX_TBFLAG_A64(flags, UNPRIV) - && tbid - && !(env->pstate & PSTATE_TCO) - && (sctlr & SCTLR_TCF0) - && allocation_tag_access_enabled(env, 0, sctlr)) { - DP_TBFLAG_A64(flags, MTE0_ACTIVE, 1); - } - /* Cache TCMA as well as TBI. */ - DP_TBFLAG_A64(flags, TCMA, aa64_va_parameter_tcma(tcr, mmu_idx)); - } - - return rebuild_hflags_common(env, fp_el, mmu_idx, flags); -} - -static CPUARMTBFlags rebuild_hflags_internal(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); - - if (is_a64(env)) { - return rebuild_hflags_a64(env, el, fp_el, mmu_idx); - } else if (arm_feature(env, ARM_FEATURE_M)) { - return rebuild_hflags_m32(env, fp_el, mmu_idx); - } else { - return rebuild_hflags_a32(env, fp_el, mmu_idx); - } -} - -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); - ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, 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); - ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, el); - - env->hflags = rebuild_hflags_a32(env, fp_el, mmu_idx); -} - -void HELPER(rebuild_hflags_a64)(CPUARMState *env, int el) -{ - int fp_el = fp_exception_el(env, el); - ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, 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 - CPUARMTBFlags c = env->hflags; - CPUARMTBFlags r = rebuild_hflags_internal(env); - - if (unlikely(c.flags != r.flags || c.flags2 != r.flags2)) { - fprintf(stderr, "TCG hflags mismatch " - "(current:(0x%08x,0x" TARGET_FMT_lx ")" - " rebuilt:(0x%08x,0x" TARGET_FMT_lx ")\n", - c.flags, c.flags2, r.flags, r.flags2); - abort(); - } -#endif -} - static bool mve_no_pred(CPUARMState *env) { /* @@ -11379,8 +12542,8 @@ static bool mve_no_pred(CPUARMState *env) return true; } -void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *pflags) +void cpu_get_tb_cpu_state(CPUARMState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) { CPUARMTBFlags flags; @@ -11500,6 +12663,21 @@ void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq) } } +static uint32_t sve_vqm1_for_el_sm_ena(CPUARMState *env, int el, bool sm) +{ + int exc_el; + + if (sm) { + exc_el = sme_exception_el(env, el); + } else { + exc_el = sve_exception_el(env, el); + } + if (exc_el) { + return 0; /* disabled */ + } + return sve_vqm1_for_el_sm(env, el, sm); +} + /* * Notice a change in SVE vector size when changing EL. */ @@ -11508,7 +12686,7 @@ void aarch64_sve_change_el(CPUARMState *env, int old_el, { ARMCPU *cpu = env_archcpu(env); int old_len, new_len; - bool old_a64, new_a64; + bool old_a64, new_a64, sm; /* Nothing to do if no SVE. */ if (!cpu_isar_feature(aa64_sve, cpu)) { @@ -11520,6 +12698,20 @@ void aarch64_sve_change_el(CPUARMState *env, int old_el, return; } + old_a64 = old_el ? arm_el_is_aa64(env, old_el) : el0_a64; + new_a64 = new_el ? arm_el_is_aa64(env, new_el) : el0_a64; + + /* + * Both AArch64.TakeException and AArch64.ExceptionReturn + * invoke ResetSVEState when taking an exception from, or + * returning to, AArch32 state when PSTATE.SM is enabled. + */ + sm = FIELD_EX64(env->svcr, SVCR, SM); + if (old_a64 != new_a64 && sm) { + arm_reset_sve_state(env); + return; + } + /* * DDI0584A.d sec 3.2: "If SVE instructions are disabled or trapped * at ELx, or not available because the EL is in AArch32 state, then @@ -11532,12 +12724,13 @@ void aarch64_sve_change_el(CPUARMState *env, int old_el, * we already have the correct register contents when encountering the * vq0->vq0 transition between EL0->EL1. */ - old_a64 = old_el ? arm_el_is_aa64(env, old_el) : el0_a64; - old_len = (old_a64 && !sve_exception_el(env, old_el) - ? sve_vqm1_for_el(env, old_el) : 0); - new_a64 = new_el ? arm_el_is_aa64(env, new_el) : el0_a64; - new_len = (new_a64 && !sve_exception_el(env, new_el) - ? sve_vqm1_for_el(env, new_el) : 0); + old_len = new_len = 0; + if (old_a64) { + old_len = sve_vqm1_for_el_sm_ena(env, old_el, sm); + } + if (new_a64) { + new_len = sve_vqm1_for_el_sm_ena(env, new_el, sm); + } /* When changing vector length, clear inaccessible state. */ if (new_len < old_len) { @@ -11545,3 +12738,63 @@ void aarch64_sve_change_el(CPUARMState *env, int old_el, } } #endif + +#ifndef CONFIG_USER_ONLY +ARMSecuritySpace arm_security_space(CPUARMState *env) +{ + if (arm_feature(env, ARM_FEATURE_M)) { + return arm_secure_to_space(env->v7m.secure); + } + + /* + * If EL3 is not supported then the secure state is implementation + * defined, in which case QEMU defaults to non-secure. + */ + if (!arm_feature(env, ARM_FEATURE_EL3)) { + return ARMSS_NonSecure; + } + + /* Check for AArch64 EL3 or AArch32 Mon. */ + if (is_a64(env)) { + if (extract32(env->pstate, 2, 2) == 3) { + if (cpu_isar_feature(aa64_rme, env_archcpu(env))) { + return ARMSS_Root; + } else { + return ARMSS_Secure; + } + } + } else { + if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_MON) { + return ARMSS_Secure; + } + } + + return arm_security_space_below_el3(env); +} + +ARMSecuritySpace arm_security_space_below_el3(CPUARMState *env) +{ + assert(!arm_feature(env, ARM_FEATURE_M)); + + /* + * If EL3 is not supported then the secure state is implementation + * defined, in which case QEMU defaults to non-secure. + */ + if (!arm_feature(env, ARM_FEATURE_EL3)) { + return ARMSS_NonSecure; + } + + /* + * Note NSE cannot be set without RME, and NSE & !NS is Reserved. + * Ignoring NSE when !NS retains consistency without having to + * modify other predicates. + */ + if (!(env->cp15.scr_el3 & SCR_NS)) { + return ARMSS_Secure; + } else if (env->cp15.scr_el3 & SCR_NSE) { + return ARMSS_Realm; + } else { + return ARMSS_NonSecure; + } +} +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/arm/helper.h b/target/arm/helper.h index b1334e0c42..2b02733305 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -44,9 +44,11 @@ DEF_HELPER_FLAGS_2(usad8, TCG_CALL_NO_RWG_SE, i32, i32, i32) DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) -DEF_HELPER_2(exception_internal, void, env, i32) -DEF_HELPER_4(exception_with_syndrome, void, env, i32, i32, i32) -DEF_HELPER_2(exception_bkpt_insn, void, env, i32) +DEF_HELPER_2(exception_internal, noreturn, env, i32) +DEF_HELPER_3(exception_with_syndrome, noreturn, env, i32, i32) +DEF_HELPER_4(exception_with_syndrome_el, noreturn, env, i32, i32, i32) +DEF_HELPER_2(exception_bkpt_insn, noreturn, env, i32) +DEF_HELPER_2(exception_swstep, noreturn, env, i32) DEF_HELPER_2(exception_pc_alignment, noreturn, env, tl) DEF_HELPER_1(setend, void, env) DEF_HELPER_2(wfi, void, env, i32) @@ -77,11 +79,14 @@ DEF_HELPER_2(v8m_stackcheck, void, env, i32) DEF_HELPER_FLAGS_2(check_bxj_trap, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_4(access_check_cp_reg, void, env, ptr, i32, i32) -DEF_HELPER_3(set_cp_reg, void, env, ptr, i32) -DEF_HELPER_2(get_cp_reg, i32, env, ptr) -DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64) -DEF_HELPER_2(get_cp_reg64, i64, env, ptr) +DEF_HELPER_4(access_check_cp_reg, cptr, env, i32, i32, i32) +DEF_HELPER_FLAGS_2(lookup_cp_reg, TCG_CALL_NO_RWG_SE, cptr, env, i32) +DEF_HELPER_FLAGS_2(tidcp_el0, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_2(tidcp_el1, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_3(set_cp_reg, void, env, cptr, i32) +DEF_HELPER_2(get_cp_reg, i32, env, cptr) +DEF_HELPER_3(set_cp_reg64, void, env, cptr, i64) +DEF_HELPER_2(get_cp_reg64, i64, env, cptr) DEF_HELPER_2(get_r13_banked, i32, env, i32) DEF_HELPER_3(set_r13_banked, void, env, i32, i32) @@ -549,7 +554,9 @@ DEF_HELPER_FLAGS_2(neon_qzip16, TCG_CALL_NO_RWG, void, ptr, ptr) DEF_HELPER_FLAGS_2(neon_qzip32, TCG_CALL_NO_RWG, void, ptr, ptr) DEF_HELPER_FLAGS_4(crypto_aese, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_aesd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_3(crypto_aesmc, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(crypto_aesimc, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_4(crypto_sha1su0, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(crypto_sha1c, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) @@ -1017,9 +1024,28 @@ DEF_HELPER_FLAGS_6(gvec_bfmlal, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(gvec_bfmlal_idx, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sclamp_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sclamp_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sclamp_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sclamp_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_uclamp_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uclamp_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uclamp_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uclamp_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + #ifdef TARGET_AARCH64 -#include "helper-a64.h" -#include "helper-sve.h" +#include "tcg/helper-a64.h" +#include "tcg/helper-sve.h" +#include "tcg/helper-sme.h" #endif -#include "helper-mve.h" +#include "tcg/helper-mve.h" diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 060aa0ccf4..65a5601804 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -28,9 +28,126 @@ #include "arm-powerctl.h" #include "target/arm/cpu.h" #include "target/arm/internals.h" +#include "target/arm/multiprocessing.h" +#include "target/arm/gtimer.h" #include "trace/trace-target_arm_hvf.h" #include "migration/vmstate.h" +#include "exec/gdbstub.h" + +#define MDSCR_EL1_SS_SHIFT 0 +#define MDSCR_EL1_MDE_SHIFT 15 + +static const uint16_t dbgbcr_regs[] = { + HV_SYS_REG_DBGBCR0_EL1, + HV_SYS_REG_DBGBCR1_EL1, + HV_SYS_REG_DBGBCR2_EL1, + HV_SYS_REG_DBGBCR3_EL1, + HV_SYS_REG_DBGBCR4_EL1, + HV_SYS_REG_DBGBCR5_EL1, + HV_SYS_REG_DBGBCR6_EL1, + HV_SYS_REG_DBGBCR7_EL1, + HV_SYS_REG_DBGBCR8_EL1, + HV_SYS_REG_DBGBCR9_EL1, + HV_SYS_REG_DBGBCR10_EL1, + HV_SYS_REG_DBGBCR11_EL1, + HV_SYS_REG_DBGBCR12_EL1, + HV_SYS_REG_DBGBCR13_EL1, + HV_SYS_REG_DBGBCR14_EL1, + HV_SYS_REG_DBGBCR15_EL1, +}; + +static const uint16_t dbgbvr_regs[] = { + HV_SYS_REG_DBGBVR0_EL1, + HV_SYS_REG_DBGBVR1_EL1, + HV_SYS_REG_DBGBVR2_EL1, + HV_SYS_REG_DBGBVR3_EL1, + HV_SYS_REG_DBGBVR4_EL1, + HV_SYS_REG_DBGBVR5_EL1, + HV_SYS_REG_DBGBVR6_EL1, + HV_SYS_REG_DBGBVR7_EL1, + HV_SYS_REG_DBGBVR8_EL1, + HV_SYS_REG_DBGBVR9_EL1, + HV_SYS_REG_DBGBVR10_EL1, + HV_SYS_REG_DBGBVR11_EL1, + HV_SYS_REG_DBGBVR12_EL1, + HV_SYS_REG_DBGBVR13_EL1, + HV_SYS_REG_DBGBVR14_EL1, + HV_SYS_REG_DBGBVR15_EL1, +}; + +static const uint16_t dbgwcr_regs[] = { + HV_SYS_REG_DBGWCR0_EL1, + HV_SYS_REG_DBGWCR1_EL1, + HV_SYS_REG_DBGWCR2_EL1, + HV_SYS_REG_DBGWCR3_EL1, + HV_SYS_REG_DBGWCR4_EL1, + HV_SYS_REG_DBGWCR5_EL1, + HV_SYS_REG_DBGWCR6_EL1, + HV_SYS_REG_DBGWCR7_EL1, + HV_SYS_REG_DBGWCR8_EL1, + HV_SYS_REG_DBGWCR9_EL1, + HV_SYS_REG_DBGWCR10_EL1, + HV_SYS_REG_DBGWCR11_EL1, + HV_SYS_REG_DBGWCR12_EL1, + HV_SYS_REG_DBGWCR13_EL1, + HV_SYS_REG_DBGWCR14_EL1, + HV_SYS_REG_DBGWCR15_EL1, +}; + +static const uint16_t dbgwvr_regs[] = { + HV_SYS_REG_DBGWVR0_EL1, + HV_SYS_REG_DBGWVR1_EL1, + HV_SYS_REG_DBGWVR2_EL1, + HV_SYS_REG_DBGWVR3_EL1, + HV_SYS_REG_DBGWVR4_EL1, + HV_SYS_REG_DBGWVR5_EL1, + HV_SYS_REG_DBGWVR6_EL1, + HV_SYS_REG_DBGWVR7_EL1, + HV_SYS_REG_DBGWVR8_EL1, + HV_SYS_REG_DBGWVR9_EL1, + HV_SYS_REG_DBGWVR10_EL1, + HV_SYS_REG_DBGWVR11_EL1, + HV_SYS_REG_DBGWVR12_EL1, + HV_SYS_REG_DBGWVR13_EL1, + HV_SYS_REG_DBGWVR14_EL1, + HV_SYS_REG_DBGWVR15_EL1, +}; + +static inline int hvf_arm_num_brps(hv_vcpu_config_t config) +{ + uint64_t val; + hv_return_t ret; + ret = hv_vcpu_config_get_feature_reg(config, HV_FEATURE_REG_ID_AA64DFR0_EL1, + &val); + assert_hvf_ok(ret); + return FIELD_EX64(val, ID_AA64DFR0, BRPS) + 1; +} + +static inline int hvf_arm_num_wrps(hv_vcpu_config_t config) +{ + uint64_t val; + hv_return_t ret; + ret = hv_vcpu_config_get_feature_reg(config, HV_FEATURE_REG_ID_AA64DFR0_EL1, + &val); + assert_hvf_ok(ret); + return FIELD_EX64(val, ID_AA64DFR0, WRPS) + 1; +} + +void hvf_arm_init_debug(void) +{ + hv_vcpu_config_t config; + config = hv_vcpu_config_create(); + + max_hw_bps = hvf_arm_num_brps(config); + hw_breakpoints = + g_array_sized_new(true, true, sizeof(HWBreakpoint), max_hw_bps); + + max_hw_wps = hvf_arm_num_wrps(config); + hw_watchpoints = + g_array_sized_new(true, true, sizeof(HWWatchpoint), max_hw_wps); +} + #define HVF_SYSREG(crn, crm, op0, op1, op2) \ ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, crn, crm, op0, op1, op2) #define PL1_WRITE_MASK 0x4 @@ -80,6 +197,99 @@ #define SYSREG_PMCCNTR_EL0 SYSREG(3, 3, 9, 13, 0) #define SYSREG_PMCCFILTR_EL0 SYSREG(3, 3, 14, 15, 7) +#define SYSREG_ICC_AP0R0_EL1 SYSREG(3, 0, 12, 8, 4) +#define SYSREG_ICC_AP0R1_EL1 SYSREG(3, 0, 12, 8, 5) +#define SYSREG_ICC_AP0R2_EL1 SYSREG(3, 0, 12, 8, 6) +#define SYSREG_ICC_AP0R3_EL1 SYSREG(3, 0, 12, 8, 7) +#define SYSREG_ICC_AP1R0_EL1 SYSREG(3, 0, 12, 9, 0) +#define SYSREG_ICC_AP1R1_EL1 SYSREG(3, 0, 12, 9, 1) +#define SYSREG_ICC_AP1R2_EL1 SYSREG(3, 0, 12, 9, 2) +#define SYSREG_ICC_AP1R3_EL1 SYSREG(3, 0, 12, 9, 3) +#define SYSREG_ICC_ASGI1R_EL1 SYSREG(3, 0, 12, 11, 6) +#define SYSREG_ICC_BPR0_EL1 SYSREG(3, 0, 12, 8, 3) +#define SYSREG_ICC_BPR1_EL1 SYSREG(3, 0, 12, 12, 3) +#define SYSREG_ICC_CTLR_EL1 SYSREG(3, 0, 12, 12, 4) +#define SYSREG_ICC_DIR_EL1 SYSREG(3, 0, 12, 11, 1) +#define SYSREG_ICC_EOIR0_EL1 SYSREG(3, 0, 12, 8, 1) +#define SYSREG_ICC_EOIR1_EL1 SYSREG(3, 0, 12, 12, 1) +#define SYSREG_ICC_HPPIR0_EL1 SYSREG(3, 0, 12, 8, 2) +#define SYSREG_ICC_HPPIR1_EL1 SYSREG(3, 0, 12, 12, 2) +#define SYSREG_ICC_IAR0_EL1 SYSREG(3, 0, 12, 8, 0) +#define SYSREG_ICC_IAR1_EL1 SYSREG(3, 0, 12, 12, 0) +#define SYSREG_ICC_IGRPEN0_EL1 SYSREG(3, 0, 12, 12, 6) +#define SYSREG_ICC_IGRPEN1_EL1 SYSREG(3, 0, 12, 12, 7) +#define SYSREG_ICC_PMR_EL1 SYSREG(3, 0, 4, 6, 0) +#define SYSREG_ICC_RPR_EL1 SYSREG(3, 0, 12, 11, 3) +#define SYSREG_ICC_SGI0R_EL1 SYSREG(3, 0, 12, 11, 7) +#define SYSREG_ICC_SGI1R_EL1 SYSREG(3, 0, 12, 11, 5) +#define SYSREG_ICC_SRE_EL1 SYSREG(3, 0, 12, 12, 5) + +#define SYSREG_MDSCR_EL1 SYSREG(2, 0, 0, 2, 2) +#define SYSREG_DBGBVR0_EL1 SYSREG(2, 0, 0, 0, 4) +#define SYSREG_DBGBCR0_EL1 SYSREG(2, 0, 0, 0, 5) +#define SYSREG_DBGWVR0_EL1 SYSREG(2, 0, 0, 0, 6) +#define SYSREG_DBGWCR0_EL1 SYSREG(2, 0, 0, 0, 7) +#define SYSREG_DBGBVR1_EL1 SYSREG(2, 0, 0, 1, 4) +#define SYSREG_DBGBCR1_EL1 SYSREG(2, 0, 0, 1, 5) +#define SYSREG_DBGWVR1_EL1 SYSREG(2, 0, 0, 1, 6) +#define SYSREG_DBGWCR1_EL1 SYSREG(2, 0, 0, 1, 7) +#define SYSREG_DBGBVR2_EL1 SYSREG(2, 0, 0, 2, 4) +#define SYSREG_DBGBCR2_EL1 SYSREG(2, 0, 0, 2, 5) +#define SYSREG_DBGWVR2_EL1 SYSREG(2, 0, 0, 2, 6) +#define SYSREG_DBGWCR2_EL1 SYSREG(2, 0, 0, 2, 7) +#define SYSREG_DBGBVR3_EL1 SYSREG(2, 0, 0, 3, 4) +#define SYSREG_DBGBCR3_EL1 SYSREG(2, 0, 0, 3, 5) +#define SYSREG_DBGWVR3_EL1 SYSREG(2, 0, 0, 3, 6) +#define SYSREG_DBGWCR3_EL1 SYSREG(2, 0, 0, 3, 7) +#define SYSREG_DBGBVR4_EL1 SYSREG(2, 0, 0, 4, 4) +#define SYSREG_DBGBCR4_EL1 SYSREG(2, 0, 0, 4, 5) +#define SYSREG_DBGWVR4_EL1 SYSREG(2, 0, 0, 4, 6) +#define SYSREG_DBGWCR4_EL1 SYSREG(2, 0, 0, 4, 7) +#define SYSREG_DBGBVR5_EL1 SYSREG(2, 0, 0, 5, 4) +#define SYSREG_DBGBCR5_EL1 SYSREG(2, 0, 0, 5, 5) +#define SYSREG_DBGWVR5_EL1 SYSREG(2, 0, 0, 5, 6) +#define SYSREG_DBGWCR5_EL1 SYSREG(2, 0, 0, 5, 7) +#define SYSREG_DBGBVR6_EL1 SYSREG(2, 0, 0, 6, 4) +#define SYSREG_DBGBCR6_EL1 SYSREG(2, 0, 0, 6, 5) +#define SYSREG_DBGWVR6_EL1 SYSREG(2, 0, 0, 6, 6) +#define SYSREG_DBGWCR6_EL1 SYSREG(2, 0, 0, 6, 7) +#define SYSREG_DBGBVR7_EL1 SYSREG(2, 0, 0, 7, 4) +#define SYSREG_DBGBCR7_EL1 SYSREG(2, 0, 0, 7, 5) +#define SYSREG_DBGWVR7_EL1 SYSREG(2, 0, 0, 7, 6) +#define SYSREG_DBGWCR7_EL1 SYSREG(2, 0, 0, 7, 7) +#define SYSREG_DBGBVR8_EL1 SYSREG(2, 0, 0, 8, 4) +#define SYSREG_DBGBCR8_EL1 SYSREG(2, 0, 0, 8, 5) +#define SYSREG_DBGWVR8_EL1 SYSREG(2, 0, 0, 8, 6) +#define SYSREG_DBGWCR8_EL1 SYSREG(2, 0, 0, 8, 7) +#define SYSREG_DBGBVR9_EL1 SYSREG(2, 0, 0, 9, 4) +#define SYSREG_DBGBCR9_EL1 SYSREG(2, 0, 0, 9, 5) +#define SYSREG_DBGWVR9_EL1 SYSREG(2, 0, 0, 9, 6) +#define SYSREG_DBGWCR9_EL1 SYSREG(2, 0, 0, 9, 7) +#define SYSREG_DBGBVR10_EL1 SYSREG(2, 0, 0, 10, 4) +#define SYSREG_DBGBCR10_EL1 SYSREG(2, 0, 0, 10, 5) +#define SYSREG_DBGWVR10_EL1 SYSREG(2, 0, 0, 10, 6) +#define SYSREG_DBGWCR10_EL1 SYSREG(2, 0, 0, 10, 7) +#define SYSREG_DBGBVR11_EL1 SYSREG(2, 0, 0, 11, 4) +#define SYSREG_DBGBCR11_EL1 SYSREG(2, 0, 0, 11, 5) +#define SYSREG_DBGWVR11_EL1 SYSREG(2, 0, 0, 11, 6) +#define SYSREG_DBGWCR11_EL1 SYSREG(2, 0, 0, 11, 7) +#define SYSREG_DBGBVR12_EL1 SYSREG(2, 0, 0, 12, 4) +#define SYSREG_DBGBCR12_EL1 SYSREG(2, 0, 0, 12, 5) +#define SYSREG_DBGWVR12_EL1 SYSREG(2, 0, 0, 12, 6) +#define SYSREG_DBGWCR12_EL1 SYSREG(2, 0, 0, 12, 7) +#define SYSREG_DBGBVR13_EL1 SYSREG(2, 0, 0, 13, 4) +#define SYSREG_DBGBCR13_EL1 SYSREG(2, 0, 0, 13, 5) +#define SYSREG_DBGWVR13_EL1 SYSREG(2, 0, 0, 13, 6) +#define SYSREG_DBGWCR13_EL1 SYSREG(2, 0, 0, 13, 7) +#define SYSREG_DBGBVR14_EL1 SYSREG(2, 0, 0, 14, 4) +#define SYSREG_DBGBCR14_EL1 SYSREG(2, 0, 0, 14, 5) +#define SYSREG_DBGWVR14_EL1 SYSREG(2, 0, 0, 14, 6) +#define SYSREG_DBGWCR14_EL1 SYSREG(2, 0, 0, 14, 7) +#define SYSREG_DBGBVR15_EL1 SYSREG(2, 0, 0, 15, 4) +#define SYSREG_DBGBCR15_EL1 SYSREG(2, 0, 0, 15, 5) +#define SYSREG_DBGWVR15_EL1 SYSREG(2, 0, 0, 15, 6) +#define SYSREG_DBGWCR15_EL1 SYSREG(2, 0, 0, 15, 7) + #define WFX_IS_WFE (1 << 0) #define TMR_CTL_ENABLE (1 << 0) @@ -339,29 +549,29 @@ int hvf_get_registers(CPUState *cpu) int i; for (i = 0; i < ARRAY_SIZE(hvf_reg_match); i++) { - ret = hv_vcpu_get_reg(cpu->hvf->fd, hvf_reg_match[i].reg, &val); + ret = hv_vcpu_get_reg(cpu->accel->fd, hvf_reg_match[i].reg, &val); *(uint64_t *)((void *)env + hvf_reg_match[i].offset) = val; assert_hvf_ok(ret); } for (i = 0; i < ARRAY_SIZE(hvf_fpreg_match); i++) { - ret = hv_vcpu_get_simd_fp_reg(cpu->hvf->fd, hvf_fpreg_match[i].reg, + ret = hv_vcpu_get_simd_fp_reg(cpu->accel->fd, hvf_fpreg_match[i].reg, &fpval); memcpy((void *)env + hvf_fpreg_match[i].offset, &fpval, sizeof(fpval)); assert_hvf_ok(ret); } val = 0; - ret = hv_vcpu_get_reg(cpu->hvf->fd, HV_REG_FPCR, &val); + ret = hv_vcpu_get_reg(cpu->accel->fd, HV_REG_FPCR, &val); assert_hvf_ok(ret); vfp_set_fpcr(env, val); val = 0; - ret = hv_vcpu_get_reg(cpu->hvf->fd, HV_REG_FPSR, &val); + ret = hv_vcpu_get_reg(cpu->accel->fd, HV_REG_FPSR, &val); assert_hvf_ok(ret); vfp_set_fpsr(env, val); - ret = hv_vcpu_get_reg(cpu->hvf->fd, HV_REG_CPSR, &val); + ret = hv_vcpu_get_reg(cpu->accel->fd, HV_REG_CPSR, &val); assert_hvf_ok(ret); pstate_write(env, val); @@ -370,7 +580,93 @@ int hvf_get_registers(CPUState *cpu) continue; } - ret = hv_vcpu_get_sys_reg(cpu->hvf->fd, hvf_sreg_match[i].reg, &val); + if (cpu->accel->guest_debug_enabled) { + /* Handle debug registers */ + switch (hvf_sreg_match[i].reg) { + case HV_SYS_REG_DBGBVR0_EL1: + case HV_SYS_REG_DBGBCR0_EL1: + case HV_SYS_REG_DBGWVR0_EL1: + case HV_SYS_REG_DBGWCR0_EL1: + case HV_SYS_REG_DBGBVR1_EL1: + case HV_SYS_REG_DBGBCR1_EL1: + case HV_SYS_REG_DBGWVR1_EL1: + case HV_SYS_REG_DBGWCR1_EL1: + case HV_SYS_REG_DBGBVR2_EL1: + case HV_SYS_REG_DBGBCR2_EL1: + case HV_SYS_REG_DBGWVR2_EL1: + case HV_SYS_REG_DBGWCR2_EL1: + case HV_SYS_REG_DBGBVR3_EL1: + case HV_SYS_REG_DBGBCR3_EL1: + case HV_SYS_REG_DBGWVR3_EL1: + case HV_SYS_REG_DBGWCR3_EL1: + case HV_SYS_REG_DBGBVR4_EL1: + case HV_SYS_REG_DBGBCR4_EL1: + case HV_SYS_REG_DBGWVR4_EL1: + case HV_SYS_REG_DBGWCR4_EL1: + case HV_SYS_REG_DBGBVR5_EL1: + case HV_SYS_REG_DBGBCR5_EL1: + case HV_SYS_REG_DBGWVR5_EL1: + case HV_SYS_REG_DBGWCR5_EL1: + case HV_SYS_REG_DBGBVR6_EL1: + case HV_SYS_REG_DBGBCR6_EL1: + case HV_SYS_REG_DBGWVR6_EL1: + case HV_SYS_REG_DBGWCR6_EL1: + case HV_SYS_REG_DBGBVR7_EL1: + case HV_SYS_REG_DBGBCR7_EL1: + case HV_SYS_REG_DBGWVR7_EL1: + case HV_SYS_REG_DBGWCR7_EL1: + case HV_SYS_REG_DBGBVR8_EL1: + case HV_SYS_REG_DBGBCR8_EL1: + case HV_SYS_REG_DBGWVR8_EL1: + case HV_SYS_REG_DBGWCR8_EL1: + case HV_SYS_REG_DBGBVR9_EL1: + case HV_SYS_REG_DBGBCR9_EL1: + case HV_SYS_REG_DBGWVR9_EL1: + case HV_SYS_REG_DBGWCR9_EL1: + case HV_SYS_REG_DBGBVR10_EL1: + case HV_SYS_REG_DBGBCR10_EL1: + case HV_SYS_REG_DBGWVR10_EL1: + case HV_SYS_REG_DBGWCR10_EL1: + case HV_SYS_REG_DBGBVR11_EL1: + case HV_SYS_REG_DBGBCR11_EL1: + case HV_SYS_REG_DBGWVR11_EL1: + case HV_SYS_REG_DBGWCR11_EL1: + case HV_SYS_REG_DBGBVR12_EL1: + case HV_SYS_REG_DBGBCR12_EL1: + case HV_SYS_REG_DBGWVR12_EL1: + case HV_SYS_REG_DBGWCR12_EL1: + case HV_SYS_REG_DBGBVR13_EL1: + case HV_SYS_REG_DBGBCR13_EL1: + case HV_SYS_REG_DBGWVR13_EL1: + case HV_SYS_REG_DBGWCR13_EL1: + case HV_SYS_REG_DBGBVR14_EL1: + case HV_SYS_REG_DBGBCR14_EL1: + case HV_SYS_REG_DBGWVR14_EL1: + case HV_SYS_REG_DBGWCR14_EL1: + case HV_SYS_REG_DBGBVR15_EL1: + case HV_SYS_REG_DBGBCR15_EL1: + case HV_SYS_REG_DBGWVR15_EL1: + case HV_SYS_REG_DBGWCR15_EL1: { + /* + * If the guest is being debugged, the vCPU's debug registers + * are holding the gdbstub's view of the registers (set in + * hvf_arch_update_guest_debug()). + * Since the environment is used to store only the guest's view + * of the registers, don't update it with the values from the + * vCPU but simply keep the values from the previous + * environment. + */ + const ARMCPRegInfo *ri; + ri = get_arm_cp_reginfo(arm_cpu->cp_regs, hvf_sreg_match[i].key); + val = read_raw_cp_reg(env, ri); + + arm_cpu->cpreg_values[hvf_sreg_match[i].cp_idx] = val; + continue; + } + } + } + + ret = hv_vcpu_get_sys_reg(cpu->accel->fd, hvf_sreg_match[i].reg, &val); assert_hvf_ok(ret); arm_cpu->cpreg_values[hvf_sreg_match[i].cp_idx] = val; @@ -393,24 +689,24 @@ int hvf_put_registers(CPUState *cpu) for (i = 0; i < ARRAY_SIZE(hvf_reg_match); i++) { val = *(uint64_t *)((void *)env + hvf_reg_match[i].offset); - ret = hv_vcpu_set_reg(cpu->hvf->fd, hvf_reg_match[i].reg, val); + ret = hv_vcpu_set_reg(cpu->accel->fd, hvf_reg_match[i].reg, val); assert_hvf_ok(ret); } for (i = 0; i < ARRAY_SIZE(hvf_fpreg_match); i++) { memcpy(&fpval, (void *)env + hvf_fpreg_match[i].offset, sizeof(fpval)); - ret = hv_vcpu_set_simd_fp_reg(cpu->hvf->fd, hvf_fpreg_match[i].reg, + ret = hv_vcpu_set_simd_fp_reg(cpu->accel->fd, hvf_fpreg_match[i].reg, fpval); assert_hvf_ok(ret); } - ret = hv_vcpu_set_reg(cpu->hvf->fd, HV_REG_FPCR, vfp_get_fpcr(env)); + ret = hv_vcpu_set_reg(cpu->accel->fd, HV_REG_FPCR, vfp_get_fpcr(env)); assert_hvf_ok(ret); - ret = hv_vcpu_set_reg(cpu->hvf->fd, HV_REG_FPSR, vfp_get_fpsr(env)); + ret = hv_vcpu_set_reg(cpu->accel->fd, HV_REG_FPSR, vfp_get_fpsr(env)); assert_hvf_ok(ret); - ret = hv_vcpu_set_reg(cpu->hvf->fd, HV_REG_CPSR, pstate_read(env)); + ret = hv_vcpu_set_reg(cpu->accel->fd, HV_REG_CPSR, pstate_read(env)); assert_hvf_ok(ret); aarch64_save_sp(env, arm_current_el(env)); @@ -421,12 +717,88 @@ int hvf_put_registers(CPUState *cpu) continue; } + if (cpu->accel->guest_debug_enabled) { + /* Handle debug registers */ + switch (hvf_sreg_match[i].reg) { + case HV_SYS_REG_DBGBVR0_EL1: + case HV_SYS_REG_DBGBCR0_EL1: + case HV_SYS_REG_DBGWVR0_EL1: + case HV_SYS_REG_DBGWCR0_EL1: + case HV_SYS_REG_DBGBVR1_EL1: + case HV_SYS_REG_DBGBCR1_EL1: + case HV_SYS_REG_DBGWVR1_EL1: + case HV_SYS_REG_DBGWCR1_EL1: + case HV_SYS_REG_DBGBVR2_EL1: + case HV_SYS_REG_DBGBCR2_EL1: + case HV_SYS_REG_DBGWVR2_EL1: + case HV_SYS_REG_DBGWCR2_EL1: + case HV_SYS_REG_DBGBVR3_EL1: + case HV_SYS_REG_DBGBCR3_EL1: + case HV_SYS_REG_DBGWVR3_EL1: + case HV_SYS_REG_DBGWCR3_EL1: + case HV_SYS_REG_DBGBVR4_EL1: + case HV_SYS_REG_DBGBCR4_EL1: + case HV_SYS_REG_DBGWVR4_EL1: + case HV_SYS_REG_DBGWCR4_EL1: + case HV_SYS_REG_DBGBVR5_EL1: + case HV_SYS_REG_DBGBCR5_EL1: + case HV_SYS_REG_DBGWVR5_EL1: + case HV_SYS_REG_DBGWCR5_EL1: + case HV_SYS_REG_DBGBVR6_EL1: + case HV_SYS_REG_DBGBCR6_EL1: + case HV_SYS_REG_DBGWVR6_EL1: + case HV_SYS_REG_DBGWCR6_EL1: + case HV_SYS_REG_DBGBVR7_EL1: + case HV_SYS_REG_DBGBCR7_EL1: + case HV_SYS_REG_DBGWVR7_EL1: + case HV_SYS_REG_DBGWCR7_EL1: + case HV_SYS_REG_DBGBVR8_EL1: + case HV_SYS_REG_DBGBCR8_EL1: + case HV_SYS_REG_DBGWVR8_EL1: + case HV_SYS_REG_DBGWCR8_EL1: + case HV_SYS_REG_DBGBVR9_EL1: + case HV_SYS_REG_DBGBCR9_EL1: + case HV_SYS_REG_DBGWVR9_EL1: + case HV_SYS_REG_DBGWCR9_EL1: + case HV_SYS_REG_DBGBVR10_EL1: + case HV_SYS_REG_DBGBCR10_EL1: + case HV_SYS_REG_DBGWVR10_EL1: + case HV_SYS_REG_DBGWCR10_EL1: + case HV_SYS_REG_DBGBVR11_EL1: + case HV_SYS_REG_DBGBCR11_EL1: + case HV_SYS_REG_DBGWVR11_EL1: + case HV_SYS_REG_DBGWCR11_EL1: + case HV_SYS_REG_DBGBVR12_EL1: + case HV_SYS_REG_DBGBCR12_EL1: + case HV_SYS_REG_DBGWVR12_EL1: + case HV_SYS_REG_DBGWCR12_EL1: + case HV_SYS_REG_DBGBVR13_EL1: + case HV_SYS_REG_DBGBCR13_EL1: + case HV_SYS_REG_DBGWVR13_EL1: + case HV_SYS_REG_DBGWCR13_EL1: + case HV_SYS_REG_DBGBVR14_EL1: + case HV_SYS_REG_DBGBCR14_EL1: + case HV_SYS_REG_DBGWVR14_EL1: + case HV_SYS_REG_DBGWCR14_EL1: + case HV_SYS_REG_DBGBVR15_EL1: + case HV_SYS_REG_DBGBCR15_EL1: + case HV_SYS_REG_DBGWVR15_EL1: + case HV_SYS_REG_DBGWCR15_EL1: + /* + * If the guest is being debugged, the vCPU's debug registers + * are already holding the gdbstub's view of the registers (set + * in hvf_arch_update_guest_debug()). + */ + continue; + } + } + val = arm_cpu->cpreg_values[hvf_sreg_match[i].cp_idx]; - ret = hv_vcpu_set_sys_reg(cpu->hvf->fd, hvf_sreg_match[i].reg, val); + ret = hv_vcpu_set_sys_reg(cpu->accel->fd, hvf_sreg_match[i].reg, val); assert_hvf_ok(ret); } - ret = hv_vcpu_set_vtimer_offset(cpu->hvf->fd, hvf_state->vtimer_offset); + ret = hv_vcpu_set_vtimer_offset(cpu->accel->fd, hvf_state->vtimer_offset); assert_hvf_ok(ret); return 0; @@ -447,7 +819,7 @@ static void hvf_set_reg(CPUState *cpu, int rt, uint64_t val) flush_cpu_state(cpu); if (rt < 31) { - r = hv_vcpu_set_reg(cpu->hvf->fd, HV_REG_X0 + rt, val); + r = hv_vcpu_set_reg(cpu->accel->fd, HV_REG_X0 + rt, val); assert_hvf_ok(r); } } @@ -460,7 +832,7 @@ static uint64_t hvf_get_reg(CPUState *cpu, int rt) flush_cpu_state(cpu); if (rt < 31) { - r = hv_vcpu_get_reg(cpu->hvf->fd, HV_REG_X0 + rt, &val); + r = hv_vcpu_get_reg(cpu->accel->fd, HV_REG_X0 + rt, &val); assert_hvf_ok(r); } @@ -480,6 +852,7 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { HV_SYS_REG_ID_AA64DFR1_EL1, &host_isar.id_aa64dfr1 }, { HV_SYS_REG_ID_AA64ISAR0_EL1, &host_isar.id_aa64isar0 }, { HV_SYS_REG_ID_AA64ISAR1_EL1, &host_isar.id_aa64isar1 }, + /* Add ID_AA64ISAR2_EL1 here when HVF supports it */ { HV_SYS_REG_ID_AA64MMFR0_EL1, &host_isar.id_aa64mmfr0 }, { HV_SYS_REG_ID_AA64MMFR1_EL1, &host_isar.id_aa64mmfr1 }, { HV_SYS_REG_ID_AA64MMFR2_EL1, &host_isar.id_aa64mmfr2 }, @@ -602,22 +975,22 @@ int hvf_arch_init_vcpu(CPUState *cpu) assert(write_cpustate_to_list(arm_cpu, false)); /* Set CP_NO_RAW system registers on init */ - ret = hv_vcpu_set_sys_reg(cpu->hvf->fd, HV_SYS_REG_MIDR_EL1, + ret = hv_vcpu_set_sys_reg(cpu->accel->fd, HV_SYS_REG_MIDR_EL1, arm_cpu->midr); assert_hvf_ok(ret); - ret = hv_vcpu_set_sys_reg(cpu->hvf->fd, HV_SYS_REG_MPIDR_EL1, + ret = hv_vcpu_set_sys_reg(cpu->accel->fd, HV_SYS_REG_MPIDR_EL1, arm_cpu->mp_affinity); assert_hvf_ok(ret); - ret = hv_vcpu_get_sys_reg(cpu->hvf->fd, HV_SYS_REG_ID_AA64PFR0_EL1, &pfr); + ret = hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_ID_AA64PFR0_EL1, &pfr); assert_hvf_ok(ret); pfr |= env->gicv3state ? (1 << 24) : 0; - ret = hv_vcpu_set_sys_reg(cpu->hvf->fd, HV_SYS_REG_ID_AA64PFR0_EL1, pfr); + ret = hv_vcpu_set_sys_reg(cpu->accel->fd, HV_SYS_REG_ID_AA64PFR0_EL1, pfr); assert_hvf_ok(ret); /* We're limited to underlying hardware caps, override internal versions */ - ret = hv_vcpu_get_sys_reg(cpu->hvf->fd, HV_SYS_REG_ID_AA64MMFR0_EL1, + ret = hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_ID_AA64MMFR0_EL1, &arm_cpu->isar.id_aa64mmfr0); assert_hvf_ok(ret); @@ -627,7 +1000,7 @@ int hvf_arch_init_vcpu(CPUState *cpu) void hvf_kick_vcpu_thread(CPUState *cpu) { cpus_kick_thread(cpu); - hv_vcpus_exit(&cpu->hvf->fd, 1); + hv_vcpus_exit(&cpu->accel->fd, 1); } static void hvf_raise_exception(CPUState *cpu, uint32_t excp, @@ -645,7 +1018,7 @@ static void hvf_raise_exception(CPUState *cpu, uint32_t excp, static void hvf_psci_cpu_off(ARMCPU *arm_cpu) { - int32_t ret = arm_set_cpu_off(arm_cpu->mp_affinity); + int32_t ret = arm_set_cpu_off(arm_cpu_mp_affinity(arm_cpu)); assert(ret == QEMU_ARM_POWERCTL_RET_SUCCESS); } @@ -674,7 +1047,7 @@ static bool hvf_handle_psci_call(CPUState *cpu) int32_t ret = 0; trace_hvf_psci_call(param[0], param[1], param[2], param[3], - arm_cpu->mp_affinity); + arm_cpu_mp_affinity(arm_cpu)); switch (param[0]) { case QEMU_PSCI_0_2_FN_PSCI_VERSION: @@ -788,6 +1161,43 @@ static bool is_id_sysreg(uint32_t reg) SYSREG_CRM(reg) < 8; } +static uint32_t hvf_reg2cp_reg(uint32_t reg) +{ + return ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, + (reg >> SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK, + (reg >> SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK, + (reg >> SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK, + (reg >> SYSREG_OP1_SHIFT) & SYSREG_OP1_MASK, + (reg >> SYSREG_OP2_SHIFT) & SYSREG_OP2_MASK); +} + +static bool hvf_sysreg_read_cp(CPUState *cpu, uint32_t reg, uint64_t *val) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + const ARMCPRegInfo *ri; + + ri = get_arm_cp_reginfo(arm_cpu->cp_regs, hvf_reg2cp_reg(reg)); + if (ri) { + if (ri->accessfn) { + if (ri->accessfn(env, ri, true) != CP_ACCESS_OK) { + return false; + } + } + if (ri->type & ARM_CP_CONST) { + *val = ri->resetvalue; + } else if (ri->readfn) { + *val = ri->readfn(env, ri); + } else { + *val = CPREG_FIELD64(env, ri); + } + trace_hvf_vgic_read(ri->name, *val); + return true; + } + + return false; +} + static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) { ARMCPU *arm_cpu = ARM_CPU(cpu); @@ -839,6 +1249,108 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) case SYSREG_OSDLR_EL1: /* Dummy register */ break; + case SYSREG_ICC_AP0R0_EL1: + case SYSREG_ICC_AP0R1_EL1: + case SYSREG_ICC_AP0R2_EL1: + case SYSREG_ICC_AP0R3_EL1: + case SYSREG_ICC_AP1R0_EL1: + case SYSREG_ICC_AP1R1_EL1: + case SYSREG_ICC_AP1R2_EL1: + case SYSREG_ICC_AP1R3_EL1: + case SYSREG_ICC_ASGI1R_EL1: + case SYSREG_ICC_BPR0_EL1: + case SYSREG_ICC_BPR1_EL1: + case SYSREG_ICC_DIR_EL1: + case SYSREG_ICC_EOIR0_EL1: + case SYSREG_ICC_EOIR1_EL1: + case SYSREG_ICC_HPPIR0_EL1: + case SYSREG_ICC_HPPIR1_EL1: + case SYSREG_ICC_IAR0_EL1: + case SYSREG_ICC_IAR1_EL1: + case SYSREG_ICC_IGRPEN0_EL1: + case SYSREG_ICC_IGRPEN1_EL1: + case SYSREG_ICC_PMR_EL1: + case SYSREG_ICC_SGI0R_EL1: + case SYSREG_ICC_SGI1R_EL1: + case SYSREG_ICC_SRE_EL1: + case SYSREG_ICC_CTLR_EL1: + /* Call the TCG sysreg handler. This is only safe for GICv3 regs. */ + if (!hvf_sysreg_read_cp(cpu, reg, &val)) { + hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + } + break; + case SYSREG_DBGBVR0_EL1: + case SYSREG_DBGBVR1_EL1: + case SYSREG_DBGBVR2_EL1: + case SYSREG_DBGBVR3_EL1: + case SYSREG_DBGBVR4_EL1: + case SYSREG_DBGBVR5_EL1: + case SYSREG_DBGBVR6_EL1: + case SYSREG_DBGBVR7_EL1: + case SYSREG_DBGBVR8_EL1: + case SYSREG_DBGBVR9_EL1: + case SYSREG_DBGBVR10_EL1: + case SYSREG_DBGBVR11_EL1: + case SYSREG_DBGBVR12_EL1: + case SYSREG_DBGBVR13_EL1: + case SYSREG_DBGBVR14_EL1: + case SYSREG_DBGBVR15_EL1: + val = env->cp15.dbgbvr[SYSREG_CRM(reg)]; + break; + case SYSREG_DBGBCR0_EL1: + case SYSREG_DBGBCR1_EL1: + case SYSREG_DBGBCR2_EL1: + case SYSREG_DBGBCR3_EL1: + case SYSREG_DBGBCR4_EL1: + case SYSREG_DBGBCR5_EL1: + case SYSREG_DBGBCR6_EL1: + case SYSREG_DBGBCR7_EL1: + case SYSREG_DBGBCR8_EL1: + case SYSREG_DBGBCR9_EL1: + case SYSREG_DBGBCR10_EL1: + case SYSREG_DBGBCR11_EL1: + case SYSREG_DBGBCR12_EL1: + case SYSREG_DBGBCR13_EL1: + case SYSREG_DBGBCR14_EL1: + case SYSREG_DBGBCR15_EL1: + val = env->cp15.dbgbcr[SYSREG_CRM(reg)]; + break; + case SYSREG_DBGWVR0_EL1: + case SYSREG_DBGWVR1_EL1: + case SYSREG_DBGWVR2_EL1: + case SYSREG_DBGWVR3_EL1: + case SYSREG_DBGWVR4_EL1: + case SYSREG_DBGWVR5_EL1: + case SYSREG_DBGWVR6_EL1: + case SYSREG_DBGWVR7_EL1: + case SYSREG_DBGWVR8_EL1: + case SYSREG_DBGWVR9_EL1: + case SYSREG_DBGWVR10_EL1: + case SYSREG_DBGWVR11_EL1: + case SYSREG_DBGWVR12_EL1: + case SYSREG_DBGWVR13_EL1: + case SYSREG_DBGWVR14_EL1: + case SYSREG_DBGWVR15_EL1: + val = env->cp15.dbgwvr[SYSREG_CRM(reg)]; + break; + case SYSREG_DBGWCR0_EL1: + case SYSREG_DBGWCR1_EL1: + case SYSREG_DBGWCR2_EL1: + case SYSREG_DBGWCR3_EL1: + case SYSREG_DBGWCR4_EL1: + case SYSREG_DBGWCR5_EL1: + case SYSREG_DBGWCR6_EL1: + case SYSREG_DBGWCR7_EL1: + case SYSREG_DBGWCR8_EL1: + case SYSREG_DBGWCR9_EL1: + case SYSREG_DBGWCR10_EL1: + case SYSREG_DBGWCR11_EL1: + case SYSREG_DBGWCR12_EL1: + case SYSREG_DBGWCR13_EL1: + case SYSREG_DBGWCR14_EL1: + case SYSREG_DBGWCR15_EL1: + val = env->cp15.dbgwcr[SYSREG_CRM(reg)]; + break; default: if (is_id_sysreg(reg)) { /* ID system registers read as RES0 */ @@ -944,6 +1456,33 @@ static void pmswinc_write(CPUARMState *env, uint64_t value) } } +static bool hvf_sysreg_write_cp(CPUState *cpu, uint32_t reg, uint64_t val) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + const ARMCPRegInfo *ri; + + ri = get_arm_cp_reginfo(arm_cpu->cp_regs, hvf_reg2cp_reg(reg)); + + if (ri) { + if (ri->accessfn) { + if (ri->accessfn(env, ri, false) != CP_ACCESS_OK) { + return false; + } + } + if (ri->writefn) { + ri->writefn(env, ri, val); + } else { + CPREG_FIELD64(env, ri) = val; + } + + trace_hvf_vgic_write(ri->name, val); + return true; + } + + return false; +} + static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) { ARMCPU *arm_cpu = ARM_CPU(cpu); @@ -1021,6 +1560,111 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_OSDLR_EL1: /* Dummy register */ break; + case SYSREG_ICC_AP0R0_EL1: + case SYSREG_ICC_AP0R1_EL1: + case SYSREG_ICC_AP0R2_EL1: + case SYSREG_ICC_AP0R3_EL1: + case SYSREG_ICC_AP1R0_EL1: + case SYSREG_ICC_AP1R1_EL1: + case SYSREG_ICC_AP1R2_EL1: + case SYSREG_ICC_AP1R3_EL1: + case SYSREG_ICC_ASGI1R_EL1: + case SYSREG_ICC_BPR0_EL1: + case SYSREG_ICC_BPR1_EL1: + case SYSREG_ICC_CTLR_EL1: + case SYSREG_ICC_DIR_EL1: + case SYSREG_ICC_EOIR0_EL1: + case SYSREG_ICC_EOIR1_EL1: + case SYSREG_ICC_HPPIR0_EL1: + case SYSREG_ICC_HPPIR1_EL1: + case SYSREG_ICC_IAR0_EL1: + case SYSREG_ICC_IAR1_EL1: + case SYSREG_ICC_IGRPEN0_EL1: + case SYSREG_ICC_IGRPEN1_EL1: + case SYSREG_ICC_PMR_EL1: + case SYSREG_ICC_SGI0R_EL1: + case SYSREG_ICC_SGI1R_EL1: + case SYSREG_ICC_SRE_EL1: + /* Call the TCG sysreg handler. This is only safe for GICv3 regs. */ + if (!hvf_sysreg_write_cp(cpu, reg, val)) { + hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + } + break; + case SYSREG_MDSCR_EL1: + env->cp15.mdscr_el1 = val; + break; + case SYSREG_DBGBVR0_EL1: + case SYSREG_DBGBVR1_EL1: + case SYSREG_DBGBVR2_EL1: + case SYSREG_DBGBVR3_EL1: + case SYSREG_DBGBVR4_EL1: + case SYSREG_DBGBVR5_EL1: + case SYSREG_DBGBVR6_EL1: + case SYSREG_DBGBVR7_EL1: + case SYSREG_DBGBVR8_EL1: + case SYSREG_DBGBVR9_EL1: + case SYSREG_DBGBVR10_EL1: + case SYSREG_DBGBVR11_EL1: + case SYSREG_DBGBVR12_EL1: + case SYSREG_DBGBVR13_EL1: + case SYSREG_DBGBVR14_EL1: + case SYSREG_DBGBVR15_EL1: + env->cp15.dbgbvr[SYSREG_CRM(reg)] = val; + break; + case SYSREG_DBGBCR0_EL1: + case SYSREG_DBGBCR1_EL1: + case SYSREG_DBGBCR2_EL1: + case SYSREG_DBGBCR3_EL1: + case SYSREG_DBGBCR4_EL1: + case SYSREG_DBGBCR5_EL1: + case SYSREG_DBGBCR6_EL1: + case SYSREG_DBGBCR7_EL1: + case SYSREG_DBGBCR8_EL1: + case SYSREG_DBGBCR9_EL1: + case SYSREG_DBGBCR10_EL1: + case SYSREG_DBGBCR11_EL1: + case SYSREG_DBGBCR12_EL1: + case SYSREG_DBGBCR13_EL1: + case SYSREG_DBGBCR14_EL1: + case SYSREG_DBGBCR15_EL1: + env->cp15.dbgbcr[SYSREG_CRM(reg)] = val; + break; + case SYSREG_DBGWVR0_EL1: + case SYSREG_DBGWVR1_EL1: + case SYSREG_DBGWVR2_EL1: + case SYSREG_DBGWVR3_EL1: + case SYSREG_DBGWVR4_EL1: + case SYSREG_DBGWVR5_EL1: + case SYSREG_DBGWVR6_EL1: + case SYSREG_DBGWVR7_EL1: + case SYSREG_DBGWVR8_EL1: + case SYSREG_DBGWVR9_EL1: + case SYSREG_DBGWVR10_EL1: + case SYSREG_DBGWVR11_EL1: + case SYSREG_DBGWVR12_EL1: + case SYSREG_DBGWVR13_EL1: + case SYSREG_DBGWVR14_EL1: + case SYSREG_DBGWVR15_EL1: + env->cp15.dbgwvr[SYSREG_CRM(reg)] = val; + break; + case SYSREG_DBGWCR0_EL1: + case SYSREG_DBGWCR1_EL1: + case SYSREG_DBGWCR2_EL1: + case SYSREG_DBGWCR3_EL1: + case SYSREG_DBGWCR4_EL1: + case SYSREG_DBGWCR5_EL1: + case SYSREG_DBGWCR6_EL1: + case SYSREG_DBGWCR7_EL1: + case SYSREG_DBGWCR8_EL1: + case SYSREG_DBGWCR9_EL1: + case SYSREG_DBGWCR10_EL1: + case SYSREG_DBGWCR11_EL1: + case SYSREG_DBGWCR12_EL1: + case SYSREG_DBGWCR13_EL1: + case SYSREG_DBGWCR14_EL1: + case SYSREG_DBGWCR15_EL1: + env->cp15.dbgwcr[SYSREG_CRM(reg)] = val; + break; default: cpu_synchronize_state(cpu); trace_hvf_unhandled_sysreg_write(env->pc, reg, @@ -1040,13 +1684,13 @@ static int hvf_inject_interrupts(CPUState *cpu) { if (cpu->interrupt_request & CPU_INTERRUPT_FIQ) { trace_hvf_inject_fiq(); - hv_vcpu_set_pending_interrupt(cpu->hvf->fd, HV_INTERRUPT_TYPE_FIQ, + hv_vcpu_set_pending_interrupt(cpu->accel->fd, HV_INTERRUPT_TYPE_FIQ, true); } if (cpu->interrupt_request & CPU_INTERRUPT_HARD) { trace_hvf_inject_irq(); - hv_vcpu_set_pending_interrupt(cpu->hvf->fd, HV_INTERRUPT_TYPE_IRQ, + hv_vcpu_set_pending_interrupt(cpu->accel->fd, HV_INTERRUPT_TYPE_IRQ, true); } @@ -1078,10 +1722,10 @@ static void hvf_wait_for_ipi(CPUState *cpu, struct timespec *ts) * Use pselect to sleep so that other threads can IPI us while we're * sleeping. */ - qatomic_mb_set(&cpu->thread_kicked, false); - qemu_mutex_unlock_iothread(); - pselect(0, 0, 0, 0, ts, &cpu->hvf->unblock_ipi_mask); - qemu_mutex_lock_iothread(); + qatomic_set_mb(&cpu->thread_kicked, false); + bql_unlock(); + pselect(0, 0, 0, 0, ts, &cpu->accel->unblock_ipi_mask); + bql_lock(); } static void hvf_wfi(CPUState *cpu) @@ -1101,7 +1745,7 @@ static void hvf_wfi(CPUState *cpu) return; } - r = hv_vcpu_get_sys_reg(cpu->hvf->fd, HV_SYS_REG_CNTV_CTL_EL0, &ctl); + r = hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_CNTV_CTL_EL0, &ctl); assert_hvf_ok(r); if (!(ctl & 1) || (ctl & 2)) { @@ -1110,7 +1754,7 @@ static void hvf_wfi(CPUState *cpu) return; } - r = hv_vcpu_get_sys_reg(cpu->hvf->fd, HV_SYS_REG_CNTV_CVAL_EL0, &cval); + r = hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_CNTV_CVAL_EL0, &cval); assert_hvf_ok(r); ticks_to_sleep = cval - hvf_vtimer_val(); @@ -1143,12 +1787,12 @@ static void hvf_sync_vtimer(CPUState *cpu) uint64_t ctl; bool irq_state; - if (!cpu->hvf->vtimer_masked) { + if (!cpu->accel->vtimer_masked) { /* We will get notified on vtimer changes by hvf, nothing to do */ return; } - r = hv_vcpu_get_sys_reg(cpu->hvf->fd, HV_SYS_REG_CNTV_CTL_EL0, &ctl); + r = hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_CNTV_CTL_EL0, &ctl); assert_hvf_ok(r); irq_state = (ctl & (TMR_CTL_ENABLE | TMR_CTL_IMASK | TMR_CTL_ISTATUS)) == @@ -1157,8 +1801,8 @@ static void hvf_sync_vtimer(CPUState *cpu) if (!irq_state) { /* Timer no longer asserting, we can unmask it */ - hv_vcpu_set_vtimer_mask(cpu->hvf->fd, false); - cpu->hvf->vtimer_masked = false; + hv_vcpu_set_vtimer_mask(cpu->accel->fd, false); + cpu->accel->vtimer_masked = false; } } @@ -1166,11 +1810,13 @@ int hvf_vcpu_exec(CPUState *cpu) { ARMCPU *arm_cpu = ARM_CPU(cpu); CPUARMState *env = &arm_cpu->env; - hv_vcpu_exit_t *hvf_exit = cpu->hvf->exit; + int ret; + hv_vcpu_exit_t *hvf_exit = cpu->accel->exit; hv_return_t r; bool advance_pc = false; - if (hvf_inject_interrupts(cpu)) { + if (!(cpu->singlestep_enabled & SSTEP_NOIRQ) && + hvf_inject_interrupts(cpu)) { return EXCP_INTERRUPT; } @@ -1180,22 +1826,23 @@ int hvf_vcpu_exec(CPUState *cpu) flush_cpu_state(cpu); - qemu_mutex_unlock_iothread(); - assert_hvf_ok(hv_vcpu_run(cpu->hvf->fd)); + bql_unlock(); + assert_hvf_ok(hv_vcpu_run(cpu->accel->fd)); /* handle VMEXIT */ uint64_t exit_reason = hvf_exit->reason; uint64_t syndrome = hvf_exit->exception.syndrome; uint32_t ec = syn_get_ec(syndrome); - qemu_mutex_lock_iothread(); + ret = 0; + bql_lock(); switch (exit_reason) { case HV_EXIT_REASON_EXCEPTION: /* This is the main one, handle below. */ break; case HV_EXIT_REASON_VTIMER_ACTIVATED: qemu_set_irq(arm_cpu->gt_timer_outputs[GTIMER_VIRT], 1); - cpu->hvf->vtimer_masked = true; + cpu->accel->vtimer_masked = true; return 0; case HV_EXIT_REASON_CANCELED: /* we got kicked, no exit to process */ @@ -1207,6 +1854,49 @@ int hvf_vcpu_exec(CPUState *cpu) hvf_sync_vtimer(cpu); switch (ec) { + case EC_SOFTWARESTEP: { + ret = EXCP_DEBUG; + + if (!cpu->singlestep_enabled) { + error_report("EC_SOFTWARESTEP but single-stepping not enabled"); + } + break; + } + case EC_AA64_BKPT: { + ret = EXCP_DEBUG; + + cpu_synchronize_state(cpu); + + if (!hvf_find_sw_breakpoint(cpu, env->pc)) { + /* Re-inject into the guest */ + ret = 0; + hvf_raise_exception(cpu, EXCP_BKPT, syn_aa64_bkpt(0)); + } + break; + } + case EC_BREAKPOINT: { + ret = EXCP_DEBUG; + + cpu_synchronize_state(cpu); + + if (!find_hw_breakpoint(cpu, env->pc)) { + error_report("EC_BREAKPOINT but unknown hw breakpoint"); + } + break; + } + case EC_WATCHPOINT: { + ret = EXCP_DEBUG; + + cpu_synchronize_state(cpu); + + CPUWatchpoint *wp = + find_hw_watchpoint(cpu, hvf_exit->exception.virtual_address); + if (!wp) { + error_report("EXCP_DEBUG but unknown hw watchpoint"); + } + cpu->watchpoint_hit = wp; + break; + } case EC_DATAABORT: { bool isv = syndrome & ARM_EL_ISV; bool iswrite = (syndrome >> 6) & 1; @@ -1249,16 +1939,16 @@ int hvf_vcpu_exec(CPUState *cpu) uint32_t rt = (syndrome >> 5) & 0x1f; uint32_t reg = syndrome & SYSREG_MASK; uint64_t val; - int ret = 0; + int sysreg_ret = 0; if (isread) { - ret = hvf_sysreg_read(cpu, reg, rt); + sysreg_ret = hvf_sysreg_read(cpu, reg, rt); } else { val = hvf_get_reg(cpu, rt); - ret = hvf_sysreg_write(cpu, reg, val); + sysreg_ret = hvf_sysreg_write(cpu, reg, val); } - advance_pc = !ret; + advance_pc = !sysreg_ret; break; } case EC_WFX_TRAP: @@ -1306,21 +1996,26 @@ int hvf_vcpu_exec(CPUState *cpu) flush_cpu_state(cpu); - r = hv_vcpu_get_reg(cpu->hvf->fd, HV_REG_PC, &pc); + r = hv_vcpu_get_reg(cpu->accel->fd, HV_REG_PC, &pc); assert_hvf_ok(r); pc += 4; - r = hv_vcpu_set_reg(cpu->hvf->fd, HV_REG_PC, pc); + r = hv_vcpu_set_reg(cpu->accel->fd, HV_REG_PC, pc); assert_hvf_ok(r); + + /* Handle single-stepping over instructions which trigger a VM exit */ + if (cpu->singlestep_enabled) { + ret = EXCP_DEBUG; + } } - return 0; + return ret; } static const VMStateDescription vmstate_hvf_vtimer = { .name = "hvf-vtimer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(vtimer_val, HVFVTimer), VMSTATE_END_OF_LIST() }, @@ -1345,5 +2040,213 @@ int hvf_arch_init(void) hvf_state->vtimer_offset = mach_absolute_time(); vmstate_register(NULL, 0, &vmstate_hvf_vtimer, &vtimer); qemu_add_vm_change_state_handler(hvf_vm_state_change, &vtimer); + + hvf_arm_init_debug(); + return 0; } + +static const uint32_t brk_insn = 0xd4200000; + +int hvf_arch_insert_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp) +{ + if (cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) || + cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&brk_insn, 4, 1)) { + return -EINVAL; + } + return 0; +} + +int hvf_arch_remove_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp) +{ + static uint32_t brk; + + if (cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&brk, 4, 0) || + brk != brk_insn || + cpu_memory_rw_debug(cpu, bp->pc, (uint8_t *)&bp->saved_insn, 4, 1)) { + return -EINVAL; + } + return 0; +} + +int hvf_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type) +{ + switch (type) { + case GDB_BREAKPOINT_HW: + return insert_hw_breakpoint(addr); + case GDB_WATCHPOINT_READ: + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_ACCESS: + return insert_hw_watchpoint(addr, len, type); + default: + return -ENOSYS; + } +} + +int hvf_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type) +{ + switch (type) { + case GDB_BREAKPOINT_HW: + return delete_hw_breakpoint(addr); + case GDB_WATCHPOINT_READ: + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_ACCESS: + return delete_hw_watchpoint(addr, len, type); + default: + return -ENOSYS; + } +} + +void hvf_arch_remove_all_hw_breakpoints(void) +{ + if (cur_hw_wps > 0) { + g_array_remove_range(hw_watchpoints, 0, cur_hw_wps); + } + if (cur_hw_bps > 0) { + g_array_remove_range(hw_breakpoints, 0, cur_hw_bps); + } +} + +/* + * Update the vCPU with the gdbstub's view of debug registers. This view + * consists of all hardware breakpoints and watchpoints inserted so far while + * debugging the guest. + */ +static void hvf_put_gdbstub_debug_registers(CPUState *cpu) +{ + hv_return_t r = HV_SUCCESS; + int i; + + for (i = 0; i < cur_hw_bps; i++) { + HWBreakpoint *bp = get_hw_bp(i); + r = hv_vcpu_set_sys_reg(cpu->accel->fd, dbgbcr_regs[i], bp->bcr); + assert_hvf_ok(r); + r = hv_vcpu_set_sys_reg(cpu->accel->fd, dbgbvr_regs[i], bp->bvr); + assert_hvf_ok(r); + } + for (i = cur_hw_bps; i < max_hw_bps; i++) { + r = hv_vcpu_set_sys_reg(cpu->accel->fd, dbgbcr_regs[i], 0); + assert_hvf_ok(r); + r = hv_vcpu_set_sys_reg(cpu->accel->fd, dbgbvr_regs[i], 0); + assert_hvf_ok(r); + } + + for (i = 0; i < cur_hw_wps; i++) { + HWWatchpoint *wp = get_hw_wp(i); + r = hv_vcpu_set_sys_reg(cpu->accel->fd, dbgwcr_regs[i], wp->wcr); + assert_hvf_ok(r); + r = hv_vcpu_set_sys_reg(cpu->accel->fd, dbgwvr_regs[i], wp->wvr); + assert_hvf_ok(r); + } + for (i = cur_hw_wps; i < max_hw_wps; i++) { + r = hv_vcpu_set_sys_reg(cpu->accel->fd, dbgwcr_regs[i], 0); + assert_hvf_ok(r); + r = hv_vcpu_set_sys_reg(cpu->accel->fd, dbgwvr_regs[i], 0); + assert_hvf_ok(r); + } +} + +/* + * Update the vCPU with the guest's view of debug registers. This view is kept + * in the environment at all times. + */ +static void hvf_put_guest_debug_registers(CPUState *cpu) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + hv_return_t r = HV_SUCCESS; + int i; + + for (i = 0; i < max_hw_bps; i++) { + r = hv_vcpu_set_sys_reg(cpu->accel->fd, dbgbcr_regs[i], + env->cp15.dbgbcr[i]); + assert_hvf_ok(r); + r = hv_vcpu_set_sys_reg(cpu->accel->fd, dbgbvr_regs[i], + env->cp15.dbgbvr[i]); + assert_hvf_ok(r); + } + + for (i = 0; i < max_hw_wps; i++) { + r = hv_vcpu_set_sys_reg(cpu->accel->fd, dbgwcr_regs[i], + env->cp15.dbgwcr[i]); + assert_hvf_ok(r); + r = hv_vcpu_set_sys_reg(cpu->accel->fd, dbgwvr_regs[i], + env->cp15.dbgwvr[i]); + assert_hvf_ok(r); + } +} + +static inline bool hvf_arm_hw_debug_active(CPUState *cpu) +{ + return ((cur_hw_wps > 0) || (cur_hw_bps > 0)); +} + +static void hvf_arch_set_traps(void) +{ + CPUState *cpu; + bool should_enable_traps = false; + hv_return_t r = HV_SUCCESS; + + /* Check whether guest debugging is enabled for at least one vCPU; if it + * is, enable exiting the guest on all vCPUs */ + CPU_FOREACH(cpu) { + should_enable_traps |= cpu->accel->guest_debug_enabled; + } + CPU_FOREACH(cpu) { + /* Set whether debug exceptions exit the guest */ + r = hv_vcpu_set_trap_debug_exceptions(cpu->accel->fd, + should_enable_traps); + assert_hvf_ok(r); + + /* Set whether accesses to debug registers exit the guest */ + r = hv_vcpu_set_trap_debug_reg_accesses(cpu->accel->fd, + should_enable_traps); + assert_hvf_ok(r); + } +} + +void hvf_arch_update_guest_debug(CPUState *cpu) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + + /* Check whether guest debugging is enabled */ + cpu->accel->guest_debug_enabled = cpu->singlestep_enabled || + hvf_sw_breakpoints_active(cpu) || + hvf_arm_hw_debug_active(cpu); + + /* Update debug registers */ + if (cpu->accel->guest_debug_enabled) { + hvf_put_gdbstub_debug_registers(cpu); + } else { + hvf_put_guest_debug_registers(cpu); + } + + cpu_synchronize_state(cpu); + + /* Enable/disable single-stepping */ + if (cpu->singlestep_enabled) { + env->cp15.mdscr_el1 = + deposit64(env->cp15.mdscr_el1, MDSCR_EL1_SS_SHIFT, 1, 1); + pstate_write(env, pstate_read(env) | PSTATE_SS); + } else { + env->cp15.mdscr_el1 = + deposit64(env->cp15.mdscr_el1, MDSCR_EL1_SS_SHIFT, 1, 0); + } + + /* Enable/disable Breakpoint exceptions */ + if (hvf_arm_hw_debug_active(cpu)) { + env->cp15.mdscr_el1 = + deposit64(env->cp15.mdscr_el1, MDSCR_EL1_MDE_SHIFT, 1, 1); + } else { + env->cp15.mdscr_el1 = + deposit64(env->cp15.mdscr_el1, MDSCR_EL1_MDE_SHIFT, 1, 0); + } + + hvf_arch_set_traps(); +} + +bool hvf_arch_supports_guest_debug(void) +{ + return true; +} diff --git a/target/arm/hvf/meson.build b/target/arm/hvf/meson.build index 855e6cce5a..afc509a470 100644 --- a/target/arm/hvf/meson.build +++ b/target/arm/hvf/meson.build @@ -1,3 +1,3 @@ -arm_softmmu_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files( +arm_system_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files( 'hvf.c', )) diff --git a/target/arm/hvf/trace-events b/target/arm/hvf/trace-events index 820e8e0297..4fbbe4b45e 100644 --- a/target/arm/hvf/trace-events +++ b/target/arm/hvf/trace-events @@ -9,3 +9,5 @@ hvf_unknown_hvc(uint64_t x0) "unknown HVC! 0x%016"PRIx64 hvf_unknown_smc(uint64_t x0) "unknown SMC! 0x%016"PRIx64 hvf_exit(uint64_t syndrome, uint32_t ec, uint64_t pc) "exit: 0x%"PRIx64" [ec=0x%x pc=0x%"PRIx64"]" hvf_psci_call(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t cpuid) "PSCI Call x0=0x%016"PRIx64" x1=0x%016"PRIx64" x2=0x%016"PRIx64" x3=0x%016"PRIx64" cpu=0x%x" +hvf_vgic_write(const char *name, uint64_t val) "vgic write to %s [val=0x%016"PRIx64"]" +hvf_vgic_read(const char *name, uint64_t val) "vgic read from %s [val=0x%016"PRIx64"]" diff --git a/target/arm/hvf_arm.h b/target/arm/hvf_arm.h index 9a9d1a0bf5..e848c1d27d 100644 --- a/target/arm/hvf_arm.h +++ b/target/arm/hvf_arm.h @@ -13,6 +13,13 @@ #include "cpu.h" +/** + * hvf_arm_init_debug() - initialize guest debug capabilities + * + * Should be called only once before using guest debug capabilities. + */ +void hvf_arm_init_debug(void); + void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu); #endif diff --git a/target/arm/hyp_gdbstub.c b/target/arm/hyp_gdbstub.c new file mode 100644 index 0000000000..ebde2899cd --- /dev/null +++ b/target/arm/hyp_gdbstub.c @@ -0,0 +1,253 @@ +/* + * ARM implementation of KVM and HVF hooks, 64 bit specific code + * + * Copyright Mian-M. Hamayun 2013, Virtual Open Systems + * Copyright Alex Bennée 2014, 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. + * + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "internals.h" +#include "exec/gdbstub.h" + +/* Maximum and current break/watch point counts */ +int max_hw_bps, max_hw_wps; +GArray *hw_breakpoints, *hw_watchpoints; + +/** + * insert_hw_breakpoint() + * @addr: address of breakpoint + * + * See ARM ARM D2.9.1 for details but here we are only going to create + * simple un-linked breakpoints (i.e. we don't chain breakpoints + * together to match address and context or vmid). The hardware is + * capable of fancier matching but that will require exposing that + * fanciness to GDB's interface + * + * DBGBCR_EL1, Debug Breakpoint Control Registers + * + * 31 24 23 20 19 16 15 14 13 12 9 8 5 4 3 2 1 0 + * +------+------+-------+-----+----+------+-----+------+-----+---+ + * | RES0 | BT | LBN | SSC | HMC| RES0 | BAS | RES0 | PMC | E | + * +------+------+-------+-----+----+------+-----+------+-----+---+ + * + * BT: Breakpoint type (0 = unlinked address match) + * LBN: Linked BP number (0 = unused) + * SSC/HMC/PMC: Security, Higher and Priv access control (Table D-12) + * BAS: Byte Address Select (RES1 for AArch64) + * E: Enable bit + * + * DBGBVR_EL1, Debug Breakpoint Value Registers + * + * 63 53 52 49 48 2 1 0 + * +------+-----------+----------+-----+ + * | RESS | VA[52:49] | VA[48:2] | 0 0 | + * +------+-----------+----------+-----+ + * + * Depending on the addressing mode bits the top bits of the register + * are a sign extension of the highest applicable VA bit. Some + * versions of GDB don't do it correctly so we ensure they are correct + * here so future PC comparisons will work properly. + */ + +int insert_hw_breakpoint(target_ulong addr) +{ + HWBreakpoint brk = { + .bcr = 0x1, /* BCR E=1, enable */ + .bvr = sextract64(addr, 0, 53) + }; + + if (cur_hw_bps >= max_hw_bps) { + return -ENOBUFS; + } + + brk.bcr = deposit32(brk.bcr, 1, 2, 0x3); /* PMC = 11 */ + brk.bcr = deposit32(brk.bcr, 5, 4, 0xf); /* BAS = RES1 */ + + g_array_append_val(hw_breakpoints, brk); + + return 0; +} + +/** + * delete_hw_breakpoint() + * @pc: address of breakpoint + * + * Delete a breakpoint and shuffle any above down + */ + +int delete_hw_breakpoint(target_ulong pc) +{ + int i; + for (i = 0; i < hw_breakpoints->len; i++) { + HWBreakpoint *brk = get_hw_bp(i); + if (brk->bvr == pc) { + g_array_remove_index(hw_breakpoints, i); + return 0; + } + } + return -ENOENT; +} + +/** + * insert_hw_watchpoint() + * @addr: address of watch point + * @len: size of area + * @type: type of watch point + * + * See ARM ARM D2.10. As with the breakpoints we can do some advanced + * stuff if we want to. The watch points can be linked with the break + * points above to make them context aware. However for simplicity + * currently we only deal with simple read/write watch points. + * + * D7.3.11 DBGWCR_EL1, Debug Watchpoint Control Registers + * + * 31 29 28 24 23 21 20 19 16 15 14 13 12 5 4 3 2 1 0 + * +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+ + * | RES0 | MASK | RES0 | WT | LBN | SSC | HMC | BAS | LSC | PAC | E | + * +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+ + * + * MASK: num bits addr mask (0=none,01/10=res,11=3 bits (8 bytes)) + * WT: 0 - unlinked, 1 - linked (not currently used) + * LBN: Linked BP number (not currently used) + * SSC/HMC/PAC: Security, Higher and Priv access control (Table D2-11) + * BAS: Byte Address Select + * LSC: Load/Store control (01: load, 10: store, 11: both) + * E: Enable + * + * The bottom 2 bits of the value register are masked. Therefore to + * break on any sizes smaller than an unaligned word you need to set + * MASK=0, BAS=bit per byte in question. For larger regions (^2) you + * need to ensure you mask the address as required and set BAS=0xff + */ + +int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type) +{ + HWWatchpoint wp = { + .wcr = R_DBGWCR_E_MASK, /* E=1, enable */ + .wvr = addr & (~0x7ULL), + .details = { .vaddr = addr, .len = len } + }; + + if (cur_hw_wps >= max_hw_wps) { + return -ENOBUFS; + } + + /* + * HMC=0 SSC=0 PAC=3 will hit EL0 or EL1, any security state, + * valid whether EL3 is implemented or not + */ + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, PAC, 3); + + switch (type) { + case GDB_WATCHPOINT_READ: + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 1); + wp.details.flags = BP_MEM_READ; + break; + case GDB_WATCHPOINT_WRITE: + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 2); + wp.details.flags = BP_MEM_WRITE; + break; + case GDB_WATCHPOINT_ACCESS: + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 3); + wp.details.flags = BP_MEM_ACCESS; + break; + default: + g_assert_not_reached(); + break; + } + if (len <= 8) { + /* we align the address and set the bits in BAS */ + int off = addr & 0x7; + int bas = (1 << len) - 1; + + wp.wcr = deposit32(wp.wcr, 5 + off, 8 - off, bas); + } else { + /* For ranges above 8 bytes we need to be a power of 2 */ + if (is_power_of_2(len)) { + int bits = ctz64(len); + + wp.wvr &= ~((1 << bits) - 1); + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, MASK, bits); + wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, BAS, 0xff); + } else { + return -ENOBUFS; + } + } + + g_array_append_val(hw_watchpoints, wp); + return 0; +} + +bool check_watchpoint_in_range(int i, target_ulong addr) +{ + HWWatchpoint *wp = get_hw_wp(i); + uint64_t addr_top, addr_bottom = wp->wvr; + int bas = extract32(wp->wcr, 5, 8); + int mask = extract32(wp->wcr, 24, 4); + + if (mask) { + addr_top = addr_bottom + (1 << mask); + } else { + /* + * BAS must be contiguous but can offset against the base + * address in DBGWVR + */ + addr_bottom = addr_bottom + ctz32(bas); + addr_top = addr_bottom + clo32(bas); + } + + if (addr >= addr_bottom && addr <= addr_top) { + return true; + } + + return false; +} + +/** + * delete_hw_watchpoint() + * @addr: address of breakpoint + * + * Delete a breakpoint and shuffle any above down + */ + +int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type) +{ + int i; + for (i = 0; i < cur_hw_wps; i++) { + if (check_watchpoint_in_range(i, addr)) { + g_array_remove_index(hw_watchpoints, i); + return 0; + } + } + return -ENOENT; +} + +bool find_hw_breakpoint(CPUState *cpu, target_ulong pc) +{ + int i; + + for (i = 0; i < cur_hw_bps; i++) { + HWBreakpoint *bp = get_hw_bp(i); + if (bp->bvr == pc) { + return true; + } + } + return false; +} + +CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr) +{ + int i; + + for (i = 0; i < cur_hw_wps; i++) { + if (check_watchpoint_in_range(i, addr)) { + return &get_hw_wp(i)->details; + } + } + return NULL; +} diff --git a/target/arm/internals.h b/target/arm/internals.h index a1bae4588a..dd3da211a3 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -28,6 +28,7 @@ #include "hw/registerfields.h" #include "tcg/tcg-gvec-desc.h" #include "syndrome.h" +#include "cpu-features.h" /* register banks for CPU modes */ #define BANK_USRSYS 0 @@ -39,6 +40,11 @@ #define BANK_HYP 6 #define BANK_MON 7 +static inline int arm_env_mmu_index(CPUARMState *env) +{ + return EX_TBFLAG_ANY(env->hflags, MMUIDX); +} + static inline bool excp_is_internal(int excp) { /* Return true if this exception number represents a QEMU-internal @@ -93,6 +99,157 @@ FIELD(DBGWCR, WT, 20, 1) FIELD(DBGWCR, MASK, 24, 5) FIELD(DBGWCR, SSCE, 29, 1) +#define VTCR_NSW (1u << 29) +#define VTCR_NSA (1u << 30) +#define VSTCR_SW VTCR_NSW +#define VSTCR_SA VTCR_NSA + +/* Bit definitions for CPACR (AArch32 only) */ +FIELD(CPACR, CP10, 20, 2) +FIELD(CPACR, CP11, 22, 2) +FIELD(CPACR, TRCDIS, 28, 1) /* matches CPACR_EL1.TTA */ +FIELD(CPACR, D32DIS, 30, 1) /* up to v7; RAZ in v8 */ +FIELD(CPACR, ASEDIS, 31, 1) + +/* Bit definitions for CPACR_EL1 (AArch64 only) */ +FIELD(CPACR_EL1, ZEN, 16, 2) +FIELD(CPACR_EL1, FPEN, 20, 2) +FIELD(CPACR_EL1, SMEN, 24, 2) +FIELD(CPACR_EL1, TTA, 28, 1) /* matches CPACR.TRCDIS */ + +/* Bit definitions for HCPTR (AArch32 only) */ +FIELD(HCPTR, TCP10, 10, 1) +FIELD(HCPTR, TCP11, 11, 1) +FIELD(HCPTR, TASE, 15, 1) +FIELD(HCPTR, TTA, 20, 1) +FIELD(HCPTR, TAM, 30, 1) /* matches CPTR_EL2.TAM */ +FIELD(HCPTR, TCPAC, 31, 1) /* matches CPTR_EL2.TCPAC */ + +/* Bit definitions for CPTR_EL2 (AArch64 only) */ +FIELD(CPTR_EL2, TZ, 8, 1) /* !E2H */ +FIELD(CPTR_EL2, TFP, 10, 1) /* !E2H, matches HCPTR.TCP10 */ +FIELD(CPTR_EL2, TSM, 12, 1) /* !E2H */ +FIELD(CPTR_EL2, ZEN, 16, 2) /* E2H */ +FIELD(CPTR_EL2, FPEN, 20, 2) /* E2H */ +FIELD(CPTR_EL2, SMEN, 24, 2) /* E2H */ +FIELD(CPTR_EL2, TTA, 28, 1) +FIELD(CPTR_EL2, TAM, 30, 1) /* matches HCPTR.TAM */ +FIELD(CPTR_EL2, TCPAC, 31, 1) /* matches HCPTR.TCPAC */ + +/* Bit definitions for CPTR_EL3 (AArch64 only) */ +FIELD(CPTR_EL3, EZ, 8, 1) +FIELD(CPTR_EL3, TFP, 10, 1) +FIELD(CPTR_EL3, ESM, 12, 1) +FIELD(CPTR_EL3, TTA, 20, 1) +FIELD(CPTR_EL3, TAM, 30, 1) +FIELD(CPTR_EL3, TCPAC, 31, 1) + +#define MDCR_MTPME (1U << 28) +#define MDCR_TDCC (1U << 27) +#define MDCR_HLP (1U << 26) /* MDCR_EL2 */ +#define MDCR_SCCD (1U << 23) /* MDCR_EL3 */ +#define MDCR_HCCD (1U << 23) /* MDCR_EL2 */ +#define MDCR_EPMAD (1U << 21) +#define MDCR_EDAD (1U << 20) +#define MDCR_TTRF (1U << 19) +#define MDCR_STE (1U << 18) /* MDCR_EL3 */ +#define MDCR_SPME (1U << 17) /* MDCR_EL3 */ +#define MDCR_HPMD (1U << 17) /* MDCR_EL2 */ +#define MDCR_SDD (1U << 16) +#define MDCR_SPD (3U << 14) +#define MDCR_TDRA (1U << 11) +#define MDCR_TDOSA (1U << 10) +#define MDCR_TDA (1U << 9) +#define MDCR_TDE (1U << 8) +#define MDCR_HPME (1U << 7) +#define MDCR_TPM (1U << 6) +#define MDCR_TPMCR (1U << 5) +#define MDCR_HPMN (0x1fU) + +/* Not all of the MDCR_EL3 bits are present in the 32-bit SDCR */ +#define SDCR_VALID_MASK (MDCR_MTPME | MDCR_TDCC | MDCR_SCCD | \ + MDCR_EPMAD | MDCR_EDAD | MDCR_TTRF | \ + MDCR_STE | MDCR_SPME | MDCR_SPD) + +#define TTBCR_N (7U << 0) /* TTBCR.EAE==0 */ +#define TTBCR_T0SZ (7U << 0) /* TTBCR.EAE==1 */ +#define TTBCR_PD0 (1U << 4) +#define TTBCR_PD1 (1U << 5) +#define TTBCR_EPD0 (1U << 7) +#define TTBCR_IRGN0 (3U << 8) +#define TTBCR_ORGN0 (3U << 10) +#define TTBCR_SH0 (3U << 12) +#define TTBCR_T1SZ (3U << 16) +#define TTBCR_A1 (1U << 22) +#define TTBCR_EPD1 (1U << 23) +#define TTBCR_IRGN1 (3U << 24) +#define TTBCR_ORGN1 (3U << 26) +#define TTBCR_SH1 (1U << 28) +#define TTBCR_EAE (1U << 31) + +FIELD(VTCR, T0SZ, 0, 6) +FIELD(VTCR, SL0, 6, 2) +FIELD(VTCR, IRGN0, 8, 2) +FIELD(VTCR, ORGN0, 10, 2) +FIELD(VTCR, SH0, 12, 2) +FIELD(VTCR, TG0, 14, 2) +FIELD(VTCR, PS, 16, 3) +FIELD(VTCR, VS, 19, 1) +FIELD(VTCR, HA, 21, 1) +FIELD(VTCR, HD, 22, 1) +FIELD(VTCR, HWU59, 25, 1) +FIELD(VTCR, HWU60, 26, 1) +FIELD(VTCR, HWU61, 27, 1) +FIELD(VTCR, HWU62, 28, 1) +FIELD(VTCR, NSW, 29, 1) +FIELD(VTCR, NSA, 30, 1) +FIELD(VTCR, DS, 32, 1) +FIELD(VTCR, SL2, 33, 1) + +#define HCRX_ENAS0 (1ULL << 0) +#define HCRX_ENALS (1ULL << 1) +#define HCRX_ENASR (1ULL << 2) +#define HCRX_FNXS (1ULL << 3) +#define HCRX_FGTNXS (1ULL << 4) +#define HCRX_SMPME (1ULL << 5) +#define HCRX_TALLINT (1ULL << 6) +#define HCRX_VINMI (1ULL << 7) +#define HCRX_VFNMI (1ULL << 8) +#define HCRX_CMOW (1ULL << 9) +#define HCRX_MCE2 (1ULL << 10) +#define HCRX_MSCEN (1ULL << 11) + +#define HPFAR_NS (1ULL << 63) + +#define HSTR_TTEE (1 << 16) +#define HSTR_TJDBX (1 << 17) + +/* + * Depending on the value of HCR_EL2.E2H, bits 0 and 1 + * have different bit definitions, and EL1PCTEN might be + * bit 0 or bit 10. We use _E2H1 and _E2H0 suffixes to + * disambiguate if necessary. + */ +FIELD(CNTHCTL, EL0PCTEN_E2H1, 0, 1) +FIELD(CNTHCTL, EL0VCTEN_E2H1, 1, 1) +FIELD(CNTHCTL, EL1PCTEN_E2H0, 0, 1) +FIELD(CNTHCTL, EL1PCEN_E2H0, 1, 1) +FIELD(CNTHCTL, EVNTEN, 2, 1) +FIELD(CNTHCTL, EVNTDIR, 3, 1) +FIELD(CNTHCTL, EVNTI, 4, 4) +FIELD(CNTHCTL, EL0VTEN, 8, 1) +FIELD(CNTHCTL, EL0PTEN, 9, 1) +FIELD(CNTHCTL, EL1PCTEN_E2H1, 10, 1) +FIELD(CNTHCTL, EL1PTEN, 11, 1) +FIELD(CNTHCTL, ECV, 12, 1) +FIELD(CNTHCTL, EL1TVT, 13, 1) +FIELD(CNTHCTL, EL1TVCT, 14, 1) +FIELD(CNTHCTL, EL1NVPCT, 15, 1) +FIELD(CNTHCTL, EL1NVVCT, 16, 1) +FIELD(CNTHCTL, EVNTIS, 17, 1) +FIELD(CNTHCTL, CNTVMASK, 18, 1) +FIELD(CNTHCTL, CNTPMASK, 19, 1) + /* We use a few fake FSR values for internal purposes in M profile. * M profile cores don't have A/R format FSRs, but currently our * get_phys_addr() code assumes A/R profile and reports failures via @@ -182,23 +339,39 @@ static inline int r14_bank_number(int mode) return (mode == ARM_CPU_MODE_HYP) ? BANK_USRSYS : bank_number(mode); } +void arm_cpu_register(const ARMCPUInfo *info); +void aarch64_cpu_register(const ARMCPUInfo *info); + +void register_cp_regs_for_features(ARMCPU *cpu); +void init_cpreg_list(ARMCPU *cpu); + void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu); void arm_translate_init(void); +void arm_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data); + #ifdef CONFIG_TCG void arm_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb); #endif /* CONFIG_TCG */ -enum arm_fprounding { +typedef enum ARMFPRounding { FPROUNDING_TIEEVEN, FPROUNDING_POSINF, FPROUNDING_NEGINF, FPROUNDING_ZERO, FPROUNDING_TIEAWAY, FPROUNDING_ODD -}; +} ARMFPRounding; -int arm_rmode_to_sf(int rmode); +extern const FloatRoundMode arm_rmode_to_sf_map[6]; + +static inline FloatRoundMode arm_rmode_to_sf(ARMFPRounding rmode) +{ + assert((unsigned)rmode < ARRAY_SIZE(arm_rmode_to_sf_map)); + return arm_rmode_to_sf_map[rmode]; +} static inline void aarch64_save_sp(CPUARMState *env, int el) { @@ -252,9 +425,13 @@ unsigned int arm_pamax(ARMCPU *cpu); */ static inline bool extended_addresses_enabled(CPUARMState *env) { - TCR *tcr = &env->cp15.tcr_el[arm_is_secure(env) ? 3 : 1]; + uint64_t tcr = env->cp15.tcr_el[arm_is_secure(env) ? 3 : 1]; + if (arm_feature(env, ARM_FEATURE_PMSA) && + arm_feature(env, ARM_FEATURE_V8)) { + return true; + } return arm_el_is_aa64(env, 1) || - (arm_feature(env, ARM_FEATURE_LPAE) && (tcr->raw_tcr & TTBCR_EAE)); + (arm_feature(env, ARM_FEATURE_LPAE) && (tcr & TTBCR_EAE)); } /* Update a QEMU watchpoint based on the information the guest has set in the @@ -338,19 +515,33 @@ typedef enum ARMFaultType { ARMFault_AsyncExternal, ARMFault_Debug, ARMFault_TLBConflict, + ARMFault_UnsuppAtomicUpdate, ARMFault_Lockdown, ARMFault_Exclusive, ARMFault_ICacheMaint, ARMFault_QEMU_NSCExec, /* v8M: NS executing in S&NSC memory */ ARMFault_QEMU_SFault, /* v8M: SecureFault INVTRAN, INVEP or AUVIOL */ + ARMFault_GPCFOnWalk, + ARMFault_GPCFOnOutput, } ARMFaultType; +typedef enum ARMGPCF { + GPCF_None, + GPCF_AddressSize, + GPCF_Walk, + GPCF_EABT, + GPCF_Fail, +} ARMGPCF; + /** * ARMMMUFaultInfo: Information describing an ARM MMU Fault * @type: Type of fault + * @gpcf: Subtype of ARMFault_GPCFOn{Walk,Output}. * @level: Table walk level (for translation, access flag and permission faults) * @domain: Domain of the fault address (for non-LPAE CPUs only) * @s2addr: Address that caused a fault at stage 2 + * @paddr: physical address that caused a fault for gpc + * @paddr_space: physical address space that caused a fault for gpc * @stage2: True if we faulted at stage 2 * @s1ptw: True if we faulted at stage 2 while doing a stage 1 page-table walk * @s1ns: True if we faulted on a non-secure IPA while in secure state @@ -359,7 +550,10 @@ typedef enum ARMFaultType { typedef struct ARMMMUFaultInfo ARMMMUFaultInfo; struct ARMMMUFaultInfo { ARMFaultType type; + ARMGPCF gpcf; target_ulong s2addr; + target_ulong paddr; + ARMSecuritySpace paddr_space; int level; int domain; bool stage2; @@ -524,12 +718,26 @@ static inline uint32_t arm_fi_to_lfsc(ARMMMUFaultInfo *fi) case ARMFault_TLBConflict: fsc = 0x30; break; + case ARMFault_UnsuppAtomicUpdate: + fsc = 0x31; + break; case ARMFault_Lockdown: fsc = 0x34; break; case ARMFault_Exclusive: fsc = 0x35; break; + case ARMFault_GPCFOnWalk: + assert(fi->level >= -1 && fi->level <= 3); + if (fi->level < 0) { + fsc = 0b100011; + } else { + fsc = 0b100100 | fi->level; + } + break; + case ARMFault_GPCFOnOutput: + fsc = 0b101000; + break; default: /* Other faults can't occur in a context that requires a * long-format status code. @@ -585,26 +793,9 @@ static inline ARMMMUIdx core_to_aa64_mmu_idx(int mmu_idx) 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 translation regime is using LPAE format page tables */ -bool regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx); - /* * Return true if the stage 1 translation regime is using LPAE * format page tables @@ -616,6 +807,7 @@ G_NORETURN void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); +#ifndef CONFIG_USER_ONLY /* arm_cpu_do_transaction_failed: handle a memory system error response * (eg "no device/memory present at address") by raising an external abort * exception @@ -625,6 +817,7 @@ void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, MMUAccessType access_type, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr); +#endif /* Call any registered EL change hooks */ static inline void arm_call_pre_el_change_hook(ARMCPU *cpu) @@ -649,112 +842,53 @@ static inline bool regime_has_2_ranges(ARMMMUIdx mmu_idx) case ARMMMUIdx_Stage1_E0: case ARMMMUIdx_Stage1_E1: case ARMMMUIdx_Stage1_E1_PAN: - case ARMMMUIdx_Stage1_SE0: - case ARMMMUIdx_Stage1_SE1: - case ARMMMUIdx_Stage1_SE1_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: - case ARMMMUIdx_SE20_0: - case ARMMMUIdx_SE20_2: - case ARMMMUIdx_SE20_2_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_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_SE3: - case ARMMMUIdx_SE10_0: - case ARMMMUIdx_SE10_1: - case ARMMMUIdx_SE10_1_PAN: - case ARMMMUIdx_SE20_0: - case ARMMMUIdx_SE20_2: - case ARMMMUIdx_SE20_2_PAN: - case ARMMMUIdx_Stage1_SE0: - case ARMMMUIdx_Stage1_SE1: - case ARMMMUIdx_Stage1_SE1_PAN: - case ARMMMUIdx_SE2: - case ARMMMUIdx_Stage2_S: - case ARMMMUIdx_MSPrivNegPri: - case ARMMMUIdx_MSUserNegPri: - case ARMMMUIdx_MSPriv: - case ARMMMUIdx_MSUser: - return true; - default: - g_assert_not_reached(); - } -} - static inline bool regime_is_pan(CPUARMState *env, ARMMMUIdx mmu_idx) { switch (mmu_idx) { case ARMMMUIdx_Stage1_E1_PAN: - case ARMMMUIdx_Stage1_SE1_PAN: case ARMMMUIdx_E10_1_PAN: case ARMMMUIdx_E20_2_PAN: - case ARMMMUIdx_SE10_1_PAN: - case ARMMMUIdx_SE20_2_PAN: return true; default: return false; } } +static inline bool regime_is_stage2(ARMMMUIdx mmu_idx) +{ + return mmu_idx == ARMMMUIdx_Stage2 || mmu_idx == ARMMMUIdx_Stage2_S; +} + /* Return the exception level which controls this address translation regime */ static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) { switch (mmu_idx) { - case ARMMMUIdx_SE20_0: - case ARMMMUIdx_SE20_2: - case ARMMMUIdx_SE20_2_PAN: case ARMMMUIdx_E20_0: case ARMMMUIdx_E20_2: case ARMMMUIdx_E20_2_PAN: case ARMMMUIdx_Stage2: case ARMMMUIdx_Stage2_S: - case ARMMMUIdx_SE2: case ARMMMUIdx_E2: return 2; - case ARMMMUIdx_SE3: + case ARMMMUIdx_E3: return 3; - case ARMMMUIdx_SE10_0: - case ARMMMUIdx_Stage1_SE0: - return arm_el_is_aa64(env, 3) ? 1 : 3; - case ARMMMUIdx_SE10_1: - case ARMMMUIdx_SE10_1_PAN: + case ARMMMUIdx_E10_0: case ARMMMUIdx_Stage1_E0: + return arm_el_is_aa64(env, 3) || !arm_is_secure_below_el3(env) ? 1 : 3; case ARMMMUIdx_Stage1_E1: case ARMMMUIdx_Stage1_E1_PAN: - case ARMMMUIdx_Stage1_SE1: - case ARMMMUIdx_Stage1_SE1_PAN: - case ARMMMUIdx_E10_0: case ARMMMUIdx_E10_1: case ARMMMUIdx_E10_1_PAN: case ARMMMUIdx_MPrivNegPri: @@ -771,51 +905,79 @@ static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) } } +static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) +{ + switch (mmu_idx) { + case ARMMMUIdx_E20_0: + case ARMMMUIdx_Stage1_E0: + case ARMMMUIdx_MUser: + case ARMMMUIdx_MSUser: + case ARMMMUIdx_MUserNegPri: + case ARMMMUIdx_MSUserNegPri: + return true; + default: + return false; + case ARMMMUIdx_E10_0: + case ARMMMUIdx_E10_1: + case ARMMMUIdx_E10_1_PAN: + g_assert_not_reached(); + } +} + /* Return the SCTLR value which controls this address translation regime */ static inline uint64_t regime_sctlr(CPUARMState *env, ARMMMUIdx mmu_idx) { return env->cp15.sctlr_el[regime_el(env, mmu_idx)]; } -/* Return the TCR controlling this translation regime */ -static inline TCR *regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) +/* + * These are the fields in VTCR_EL2 which affect both the Secure stage 2 + * and the Non-Secure stage 2 translation regimes (and hence which are + * not present in VSTCR_EL2). + */ +#define VTCR_SHARED_FIELD_MASK \ + (R_VTCR_IRGN0_MASK | R_VTCR_ORGN0_MASK | R_VTCR_SH0_MASK | \ + R_VTCR_PS_MASK | R_VTCR_VS_MASK | R_VTCR_HA_MASK | R_VTCR_HD_MASK | \ + R_VTCR_DS_MASK) + +/* Return the value of the TCR controlling this translation regime */ +static inline uint64_t regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) { if (mmu_idx == ARMMMUIdx_Stage2) { - return &env->cp15.vtcr_el2; + return env->cp15.vtcr_el2; } if (mmu_idx == ARMMMUIdx_Stage2_S) { /* - * Note: Secure stage 2 nominally shares fields from VTCR_EL2, but - * those are not currently used by QEMU, so just return VSTCR_EL2. + * Secure stage 2 shares fields from VTCR_EL2. We merge those + * in with the VSTCR_EL2 value to synthesize a single VTCR_EL2 format + * value so the callers don't need to special case this. + * + * If a future architecture change defines bits in VSTCR_EL2 that + * overlap with these VTCR_EL2 fields we may need to revisit this. */ - return &env->cp15.vstcr_el2; + uint64_t v = env->cp15.vstcr_el2 & ~VTCR_SHARED_FIELD_MASK; + v |= env->cp15.vtcr_el2 & VTCR_SHARED_FIELD_MASK; + return v; } - return &env->cp15.tcr_el[regime_el(env, mmu_idx)]; + return env->cp15.tcr_el[regime_el(env, mmu_idx)]; } -/* Return the FSR value for a debug exception (watchpoint, hardware - * breakpoint or BKPT insn) targeting the specified exception level. - */ -static inline uint32_t arm_debug_exception_fsr(CPUARMState *env) +/* Return true if the translation regime is using LPAE format page tables */ +static inline bool regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) { - ARMMMUFaultInfo fi = { .type = ARMFault_Debug }; - int target_el = arm_debug_target_el(env); - bool using_lpae = false; - - if (target_el == 2 || arm_el_is_aa64(env, target_el)) { - using_lpae = true; - } else { - if (arm_feature(env, ARM_FEATURE_LPAE) && - (env->cp15.tcr_el[target_el].raw_tcr & TTBCR_EAE)) { - using_lpae = true; - } + int el = regime_el(env, mmu_idx); + if (el == 2 || arm_el_is_aa64(env, el)) { + return true; } - - if (using_lpae) { - return arm_fi_to_lfsc(&fi); - } else { - return arm_fi_to_sfsc(&fi); + if (arm_feature(env, ARM_FEATURE_PMSA) && + arm_feature(env, ARM_FEATURE_V8)) { + return true; } + if (arm_feature(env, ARM_FEATURE_LPAE) + && (regime_tcr(env, mmu_idx) & TTBCR_EAE)) { + return true; + } + return false; } /** @@ -934,7 +1096,7 @@ static inline const char *aarch32_mode_name(uint32_t psr) * * Update the CPU_INTERRUPT_VIRQ bit in cs->interrupt_request, following * a change to either the input VIRQ line from the GIC or the HCR_EL2.VI bit. - * Must be called with the iothread lock held. + * Must be called with the BQL held. */ void arm_cpu_update_virq(ARMCPU *cpu); @@ -943,7 +1105,7 @@ void arm_cpu_update_virq(ARMCPU *cpu); * * Update the CPU_INTERRUPT_VFIQ bit in cs->interrupt_request, following * a change to either the input VFIQ line from the GIC or the HCR_EL2.VF bit. - * Must be called with the iothread lock held. + * Must be called with the BQL held. */ void arm_cpu_update_vfiq(ARMCPU *cpu); @@ -1005,9 +1167,6 @@ static inline bool arm_mmu_idx_is_stage1_of_2(ARMMMUIdx mmu_idx) case ARMMMUIdx_Stage1_E0: case ARMMMUIdx_Stage1_E1: case ARMMMUIdx_Stage1_E1_PAN: - case ARMMMUIdx_Stage1_SE0: - case ARMMMUIdx_Stage1_SE1: - case ARMMMUIdx_Stage1_SE1_PAN: return true; default: return false; @@ -1074,6 +1233,35 @@ static inline uint32_t aarch64_pstate_valid_mask(const ARMISARegisters *id) return valid; } +/* Granule size (i.e. page size) */ +typedef enum ARMGranuleSize { + /* Same order as TG0 encoding */ + Gran4K, + Gran64K, + Gran16K, + GranInvalid, +} ARMGranuleSize; + +/** + * arm_granule_bits: Return address size of the granule in bits + * + * Return the address size of the granule in bits. This corresponds + * to the pseudocode TGxGranuleBits(). + */ +static inline int arm_granule_bits(ARMGranuleSize gran) +{ + switch (gran) { + case Gran64K: + return 16; + case Gran16K: + return 14; + case Gran4K: + return 12; + default: + g_assert_not_reached(); + } +} + /* * Parameters of a given virtual address, as extracted from the * translation control register (TCR) for a given regime. @@ -1086,32 +1274,29 @@ typedef struct ARMVAParameters { bool tbi : 1; bool epd : 1; bool hpd : 1; - bool using16k : 1; - bool using64k : 1; bool tsz_oob : 1; /* tsz has been clamped to legal range */ bool ds : 1; + bool ha : 1; + bool hd : 1; + ARMGranuleSize gran : 2; } ARMVAParameters; +/** + * aa64_va_parameters: Return parameters for an AArch64 virtual address + * @env: CPU + * @va: virtual address to look up + * @mmu_idx: determines translation regime to use + * @data: true if this is a data access + * @el1_is_aa32: true if we are asking about stage 2 when EL1 is AArch32 + * (ignored if @mmu_idx is for a stage 1 regime; only affects tsz/tsz_oob) + */ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, - ARMMMUIdx mmu_idx, bool data); + ARMMMUIdx mmu_idx, bool data, + bool el1_is_aa32); int aa64_va_parameter_tbi(uint64_t tcr, ARMMMUIdx mmu_idx); int aa64_va_parameter_tbid(uint64_t tcr, ARMMMUIdx mmu_idx); - -static inline int exception_target_el(CPUARMState *env) -{ - int target_el = MAX(1, arm_current_el(env)); - - /* - * No such thing as secure EL1 if EL3 is aarch32, - * so update the target EL to EL3 in this case. - */ - if (arm_is_secure(env) && !arm_el_is_aa64(env, 3) && target_el == 1) { - target_el = 3; - } - - return target_el; -} +int aa64_va_parameter_tcma(uint64_t tcr, ARMMMUIdx mmu_idx); /* Determine if allocation tags are available. */ static inline bool allocation_tag_access_enabled(CPUARMState *env, int el, @@ -1147,13 +1332,7 @@ typedef struct V8M_SAttributes { void v8m_security_lookup(CPUARMState *env, uint32_t address, MMUAccessType access_type, ARMMMUIdx mmu_idx, - V8M_SAttributes *sattrs); - -bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, MemTxAttrs *txattrs, - int *prot, bool *is_subpage, - ARMMMUFaultInfo *fi, uint32_t *mregion); + bool secure, V8M_SAttributes *sattrs); /* Cacheability and shareability attributes for a memory access */ typedef struct ARMCacheAttrs { @@ -1166,27 +1345,69 @@ typedef struct ARMCacheAttrs { bool is_s2_format:1; } ARMCacheAttrs; +/* Fields that are valid upon success. */ +typedef struct GetPhysAddrResult { + CPUTLBEntryFull f; + ARMCacheAttrs cacheattrs; +} GetPhysAddrResult; + +/** + * get_phys_addr: get the physical address for a virtual address + * @env: CPUARMState + * @address: virtual address to get physical address for + * @access_type: 0 for read, 1 for write, 2 for execute + * @mmu_idx: MMU index indicating required translation regime + * @result: set on translation success. + * @fi: set to fault info if the translation fails + * + * Find the physical address corresponding to the given virtual address, + * by doing a translation table walk on MMU based systems or using the + * MPU state on MPU based systems. + * + * Returns false if the translation was successful. Otherwise, phys_ptr, attrs, + * prot and page_size may not be filled in, and the populated fsr value provides + * information on why the translation aborted, in the format of a + * DFSR/IFSR fault register, with the following caveats: + * * we honour the short vs long DFSR format differences. + * * the WnR bit is never set (the caller must do this). + * * for PSMAv5 based systems we don't bother to return a full FSR format + * value. + */ bool get_phys_addr(CPUARMState *env, target_ulong address, MMUAccessType access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, - target_ulong *page_size, - ARMMMUFaultInfo *fi, ARMCacheAttrs *cacheattrs) + GetPhysAddrResult *result, ARMMMUFaultInfo *fi) __attribute__((nonnull)); +/** + * get_phys_addr_with_space_nogpc: get the physical address for a virtual + * address + * @env: CPUARMState + * @address: virtual address to get physical address for + * @access_type: 0 for read, 1 for write, 2 for execute + * @mmu_idx: MMU index indicating required translation regime + * @space: security space for the access + * @result: set on translation success. + * @fi: set to fault info if the translation fails + * + * Similar to get_phys_addr, but use the given security space and don't perform + * a Granule Protection Check on the resulting address. + */ +bool get_phys_addr_with_space_nogpc(CPUARMState *env, target_ulong address, + MMUAccessType access_type, + ARMMMUIdx mmu_idx, ARMSecuritySpace space, + GetPhysAddrResult *result, + ARMMMUFaultInfo *fi) + __attribute__((nonnull)); + +bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, + MMUAccessType access_type, ARMMMUIdx mmu_idx, + bool is_secure, GetPhysAddrResult *result, + ARMMMUFaultInfo *fi, uint32_t *mregion); + void arm_log_exception(CPUState *cs); #endif /* !CONFIG_USER_ONLY */ -/* - * The log2 of the words in the tag block, for GMID_EL1.BS. - * The is the maximum, 256 bytes, which manipulates 64-bits of tags. - */ -#define GMID_EL1_BS 6 - -/* We associate one allocation tag per 16 bytes, the minimum. */ -#define LOG2_TAG_GRANULE 4 -#define TAG_GRANULE (1 << LOG2_TAG_GRANULE) - /* * SVE predicates are 1/8 the size of SVE vectors, and cannot use * the same simd_desc() encoding due to restrictions on size. @@ -1207,11 +1428,67 @@ FIELD(MTEDESC, MIDX, 0, 4) FIELD(MTEDESC, TBI, 4, 2) FIELD(MTEDESC, TCMA, 6, 2) FIELD(MTEDESC, WRITE, 8, 1) -FIELD(MTEDESC, SIZEM1, 9, SIMD_DATA_BITS - 9) /* size - 1 */ +FIELD(MTEDESC, ALIGN, 9, 3) +FIELD(MTEDESC, SIZEM1, 12, SIMD_DATA_BITS - SVE_MTEDESC_SHIFT - 12) /* size - 1 */ bool mte_probe(CPUARMState *env, uint32_t desc, uint64_t ptr); uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t ra); +/** + * mte_mops_probe: Check where the next MTE failure is for a FEAT_MOPS operation + * @env: CPU env + * @ptr: start address of memory region (dirty pointer) + * @size: length of region (guaranteed not to cross a page boundary) + * @desc: MTEDESC descriptor word (0 means no MTE checks) + * Returns: the size of the region that can be copied without hitting + * an MTE tag failure + * + * Note that we assume that the caller has already checked the TBI + * and TCMA bits with mte_checks_needed() and an MTE check is definitely + * required. + */ +uint64_t mte_mops_probe(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t desc); + +/** + * mte_mops_probe_rev: Check where the next MTE failure is for a FEAT_MOPS + * operation going in the reverse direction + * @env: CPU env + * @ptr: *end* address of memory region (dirty pointer) + * @size: length of region (guaranteed not to cross a page boundary) + * @desc: MTEDESC descriptor word (0 means no MTE checks) + * Returns: the size of the region that can be copied without hitting + * an MTE tag failure + * + * Note that we assume that the caller has already checked the TBI + * and TCMA bits with mte_checks_needed() and an MTE check is definitely + * required. + */ +uint64_t mte_mops_probe_rev(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t desc); + +/** + * mte_check_fail: Record an MTE tag check failure + * @env: CPU env + * @desc: MTEDESC descriptor word + * @dirty_ptr: Failing dirty address + * @ra: TCG retaddr + * + * This may never return (if the MTE tag checks are configured to fault). + */ +void mte_check_fail(CPUARMState *env, uint32_t desc, + uint64_t dirty_ptr, uintptr_t ra); + +/** + * mte_mops_set_tags: Set MTE tags for a portion of a FEAT_MOPS operation + * @env: CPU env + * @dirty_ptr: Start address of memory region (dirty pointer) + * @size: length of region (guaranteed not to cross page boundary) + * @desc: MTEDESC descriptor word + */ +void mte_mops_set_tags(CPUARMState *env, uint64_t dirty_ptr, uint64_t size, + uint32_t desc); + static inline int allocation_tag_from_addr(uint64_t ptr) { return extract64(ptr, 56, 4); @@ -1280,6 +1557,7 @@ enum MVEECIState { /* Definitions for the PMU registers */ #define PMCRN_MASK 0xf800 #define PMCRN_SHIFT 11 +#define PMCRLP 0x80 #define PMCRLC 0x40 #define PMCRDP 0x20 #define PMCRX 0x10 @@ -1291,7 +1569,7 @@ enum MVEECIState { * Mask of PMCR bits writable by guest (not including WO bits like C, P, * which can be written as 1 to trigger behaviour but which stay RAZ). */ -#define PMCR_WRITABLE_MASK (PMCRLC | PMCRDP | PMCRX | PMCRD | PMCRE) +#define PMCR_WRITABLE_MASK (PMCRLP | PMCRLC | PMCRDP | PMCRX | PMCRD | PMCRE) #define PMXEVTYPER_P 0x80000000 #define PMXEVTYPER_U 0x40000000 @@ -1320,29 +1598,145 @@ static inline uint32_t pmu_num_counters(CPUARMState *env) /* Bits allowed to be set/cleared for PMCNTEN* and PMINTEN* */ static inline uint64_t pmu_counter_mask(CPUARMState *env) { - return (1 << 31) | ((1 << pmu_num_counters(env)) - 1); + return (1ULL << 31) | ((1ULL << pmu_num_counters(env)) - 1); } #ifdef TARGET_AARCH64 -int arm_gdb_get_svereg(CPUARMState *env, GByteArray *buf, int reg); -int arm_gdb_set_svereg(CPUARMState *env, uint8_t *buf, int reg); -int aarch64_fpu_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg); -int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg); +GDBFeature *arm_gen_dynamic_svereg_feature(CPUState *cpu, int base_reg); +int aarch64_gdb_get_sve_reg(CPUState *cs, GByteArray *buf, int reg); +int aarch64_gdb_set_sve_reg(CPUState *cs, uint8_t *buf, int reg); +int aarch64_gdb_get_fpu_reg(CPUState *cs, GByteArray *buf, int reg); +int aarch64_gdb_set_fpu_reg(CPUState *cs, uint8_t *buf, int reg); +int aarch64_gdb_get_pauth_reg(CPUState *cs, GByteArray *buf, int reg); +int aarch64_gdb_set_pauth_reg(CPUState *cs, uint8_t *buf, int reg); +void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp); +void arm_cpu_sme_finalize(ARMCPU *cpu, Error **errp); +void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp); +void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp); +void aarch64_max_tcg_initfn(Object *obj); +void aarch64_add_pauth_properties(Object *obj); +void aarch64_add_sve_properties(Object *obj); +void aarch64_add_sme_properties(Object *obj); #endif -#ifdef CONFIG_USER_ONLY -static inline void define_cortex_a72_a57_a53_cp_reginfo(ARMCPU *cpu) { } -#else -void define_cortex_a72_a57_a53_cp_reginfo(ARMCPU *cpu); -#endif +/* Read the CONTROL register as the MRS instruction would. */ +uint32_t arm_v7m_mrs_control(CPUARMState *env, uint32_t secure); + +/* + * Return a pointer to the location where we currently store the + * stack pointer for the requested security state and thread mode. + * This pointer will become invalid if the CPU state is updated + * such that the stack pointers are switched around (eg changing + * the SPSEL control bit). + */ +uint32_t *arm_v7m_get_sp_ptr(CPUARMState *env, bool secure, + bool threadmode, bool spsel); bool el_is_in_host(CPUARMState *env, int el); void aa32_max_features(ARMCPU *cpu); +int exception_target_el(CPUARMState *env); +bool arm_singlestep_active(CPUARMState *env); +bool arm_generate_debug_exceptions(CPUARMState *env); + +/** + * pauth_ptr_mask: + * @param: parameters defining the MMU setup + * + * Return a mask of the address bits that contain the authentication code, + * given the MMU config defined by @param. + */ +static inline uint64_t pauth_ptr_mask(ARMVAParameters param) +{ + int bot_pac_bit = 64 - param.tsz; + int top_pac_bit = 64 - 8 * param.tbi; + + return MAKE_64BIT_MASK(bot_pac_bit, top_pac_bit - bot_pac_bit); +} + +/* Add the cpreg definitions for debug related system registers */ +void define_debug_regs(ARMCPU *cpu); + +/* Effective value of MDCR_EL2 */ +static inline uint64_t arm_mdcr_el2_eff(CPUARMState *env) +{ + return arm_is_el2_enabled(env) ? env->cp15.mdcr_el2 : 0; +} /* Powers of 2 for sve_vq_map et al. */ #define SVE_VQ_POW2_MAP \ ((1 << (1 - 1)) | (1 << (2 - 1)) | \ (1 << (4 - 1)) | (1 << (8 - 1)) | (1 << (16 - 1))) +/* + * Return true if it is possible to take a fine-grained-trap to EL2. + */ +static inline bool arm_fgt_active(CPUARMState *env, int el) +{ + /* + * The Arm ARM only requires the "{E2H,TGE} != {1,1}" test for traps + * that can affect EL0, but it is harmless to do the test also for + * traps on registers that are only accessible at EL1 because if the test + * returns true then we can't be executing at EL1 anyway. + * FGT traps only happen when EL2 is enabled and EL1 is AArch64; + * traps from AArch32 only happen for the EL0 is AArch32 case. + */ + return cpu_isar_feature(aa64_fgt, env_archcpu(env)) && + el < 2 && arm_is_el2_enabled(env) && + arm_el_is_aa64(env, 1) && + (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE) && + (!arm_feature(env, ARM_FEATURE_EL3) || (env->cp15.scr_el3 & SCR_FGTEN)); +} + +void assert_hflags_rebuild_correctly(CPUARMState *env); + +/* + * Although the ARM implementation of hardware assisted debugging + * allows for different breakpoints per-core, the current GDB + * interface treats them as a global pool of registers (which seems to + * be the case for x86, ppc and s390). As a result we store one copy + * of registers which is used for all active cores. + * + * Write access is serialised by virtue of the GDB protocol which + * updates things. Read access (i.e. when the values are copied to the + * vCPU) is also gated by GDB's run control. + * + * This is not unreasonable as most of the time debugging kernels you + * never know which core will eventually execute your function. + */ + +typedef struct { + uint64_t bcr; + uint64_t bvr; +} HWBreakpoint; + +/* + * The watchpoint registers can cover more area than the requested + * watchpoint so we need to store the additional information + * somewhere. We also need to supply a CPUWatchpoint to the GDB stub + * when the watchpoint is hit. + */ +typedef struct { + uint64_t wcr; + uint64_t wvr; + CPUWatchpoint details; +} HWWatchpoint; + +/* Maximum and current break/watch point counts */ +extern int max_hw_bps, max_hw_wps; +extern GArray *hw_breakpoints, *hw_watchpoints; + +#define cur_hw_wps (hw_watchpoints->len) +#define cur_hw_bps (hw_breakpoints->len) +#define get_hw_bp(i) (&g_array_index(hw_breakpoints, HWBreakpoint, i)) +#define get_hw_wp(i) (&g_array_index(hw_watchpoints, HWWatchpoint, i)) + +bool find_hw_breakpoint(CPUState *cpu, target_ulong pc); +int insert_hw_breakpoint(target_ulong pc); +int delete_hw_breakpoint(target_ulong pc); + +bool check_watchpoint_in_range(int i, target_ulong addr); +CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr); +int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type); +int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type); #endif diff --git a/target/arm/kvm-consts.h b/target/arm/kvm-consts.h index faacf96fdc..7c6adc14f6 100644 --- a/target/arm/kvm-consts.h +++ b/target/arm/kvm-consts.h @@ -14,16 +14,16 @@ #ifndef ARM_KVM_CONSTS_H #define ARM_KVM_CONSTS_H +#ifdef NEED_CPU_H #ifdef CONFIG_KVM #include #include - #define MISMATCH_CHECK(X, Y) QEMU_BUILD_BUG_ON(X != Y) +#endif +#endif -#else - +#ifndef MISMATCH_CHECK #define MISMATCH_CHECK(X, Y) QEMU_BUILD_BUG_ON(0) - #endif #define CP_REG_SIZE_SHIFT 52 @@ -124,13 +124,10 @@ MISMATCH_CHECK(QEMU_PSCI_RET_INTERNAL_FAILURE, PSCI_RET_INTERNAL_FAILURE); MISMATCH_CHECK(QEMU_PSCI_RET_NOT_PRESENT, PSCI_RET_NOT_PRESENT); MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED); -/* Note that KVM uses overlapping values for AArch32 and AArch64 - * target CPU numbers. AArch32 targets: +/* + * Note that KVM uses overlapping values for AArch32 and AArch64 + * target CPU numbers. AArch64 targets: */ -#define QEMU_KVM_ARM_TARGET_CORTEX_A15 0 -#define QEMU_KVM_ARM_TARGET_CORTEX_A7 1 - -/* AArch64 targets: */ #define QEMU_KVM_ARM_TARGET_AEM_V8 0 #define QEMU_KVM_ARM_TARGET_FOUNDATION_V8 1 #define QEMU_KVM_ARM_TARGET_CORTEX_A57 2 diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 4339e1cd6e..ab85d628a8 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -2,6 +2,8 @@ * ARM implementation of KVM hooks * * Copyright Christoffer Dall 2009-2010 + * Copyright Mian-M. Hamayun 2013, Virtual Open Systems + * Copyright Alex Bennée 2014, 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. @@ -19,6 +21,7 @@ #include "qom/object.h" #include "qapi/error.h" #include "sysemu/sysemu.h" +#include "sysemu/runstate.h" #include "sysemu/kvm.h" #include "sysemu/kvm_int.h" #include "kvm_arm.h" @@ -28,9 +31,14 @@ #include "hw/pci/pci.h" #include "exec/memattrs.h" #include "exec/address-spaces.h" +#include "exec/gdbstub.h" #include "hw/boards.h" #include "hw/irq.h" +#include "qapi/visitor.h" #include "qemu/log.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/ghes.h" +#include "target/arm/gtimer.h" const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO @@ -40,28 +48,54 @@ static bool cap_has_mp_state; static bool cap_has_inject_serror_esr; static bool cap_has_inject_ext_dabt; +/** + * ARMHostCPUFeatures: information about the host CPU (identified + * by asking the host kernel) + */ +typedef struct ARMHostCPUFeatures { + ARMISARegisters isar; + uint64_t features; + uint32_t target; + const char *dtb_compatible; +} ARMHostCPUFeatures; + static ARMHostCPUFeatures arm_host_cpu_features; -int kvm_arm_vcpu_init(CPUState *cs) +/** + * kvm_arm_vcpu_init: + * @cpu: ARMCPU + * + * Initialize (or reinitialize) the VCPU by invoking the + * KVM_ARM_VCPU_INIT ioctl with the CPU type and feature + * bitmask specified in the CPUState. + * + * Returns: 0 if success else < 0 error code + */ +static int kvm_arm_vcpu_init(ARMCPU *cpu) { - ARMCPU *cpu = ARM_CPU(cs); struct kvm_vcpu_init init; init.target = cpu->kvm_target; memcpy(init.features, cpu->kvm_init_features, sizeof(init.features)); - return kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init); + return kvm_vcpu_ioctl(CPU(cpu), KVM_ARM_VCPU_INIT, &init); } -int kvm_arm_vcpu_finalize(CPUState *cs, int feature) +/** + * kvm_arm_vcpu_finalize: + * @cpu: ARMCPU + * @feature: feature to finalize + * + * Finalizes the configuration of the specified VCPU feature by + * invoking the KVM_ARM_VCPU_FINALIZE ioctl. Features requiring + * this are documented in the "KVM_ARM_VCPU_FINALIZE" section of + * KVM's API documentation. + * + * Returns: 0 if success else < 0 error code + */ +static int kvm_arm_vcpu_finalize(ARMCPU *cpu, int feature) { - return kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_FINALIZE, &feature); -} - -void kvm_arm_init_serror_injection(CPUState *cs) -{ - cap_has_inject_serror_esr = kvm_check_extension(cs->kvm_state, - KVM_CAP_ARM_INJECT_SERROR_ESR); + return kvm_vcpu_ioctl(CPU(cpu), KVM_ARM_VCPU_FINALIZE, &feature); } bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, @@ -79,7 +113,9 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, if (max_vm_pa_size < 0) { max_vm_pa_size = 0; } - vmfd = ioctl(kvmfd, KVM_CREATE_VM, max_vm_pa_size); + do { + vmfd = ioctl(kvmfd, KVM_CREATE_VM, max_vm_pa_size); + } while (vmfd == -1 && errno == EINTR); if (vmfd < 0) { goto err; } @@ -164,6 +200,260 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray) } } +static int read_sys_reg32(int fd, uint32_t *pret, uint64_t id) +{ + uint64_t ret; + struct kvm_one_reg idreg = { .id = id, .addr = (uintptr_t)&ret }; + int err; + + assert((id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64); + err = ioctl(fd, KVM_GET_ONE_REG, &idreg); + if (err < 0) { + return -1; + } + *pret = ret; + return 0; +} + +static int read_sys_reg64(int fd, uint64_t *pret, uint64_t id) +{ + struct kvm_one_reg idreg = { .id = id, .addr = (uintptr_t)pret }; + + assert((id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64); + return ioctl(fd, KVM_GET_ONE_REG, &idreg); +} + +static bool kvm_arm_pauth_supported(void) +{ + return (kvm_check_extension(kvm_state, KVM_CAP_ARM_PTRAUTH_ADDRESS) && + kvm_check_extension(kvm_state, KVM_CAP_ARM_PTRAUTH_GENERIC)); +} + +static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) +{ + /* Identify the feature bits corresponding to the host CPU, and + * fill out the ARMHostCPUClass fields accordingly. To do this + * we have to create a scratch VM, create a single CPU inside it, + * and then query that CPU for the relevant ID registers. + */ + int fdarray[3]; + bool sve_supported; + bool pmu_supported = false; + uint64_t features = 0; + int err; + + /* Old kernels may not know about the PREFERRED_TARGET ioctl: however + * we know these will only support creating one kind of guest CPU, + * which is its preferred CPU type. Fortunately these old kernels + * support only a very limited number of CPUs. + */ + static const uint32_t cpus_to_try[] = { + KVM_ARM_TARGET_AEM_V8, + KVM_ARM_TARGET_FOUNDATION_V8, + KVM_ARM_TARGET_CORTEX_A57, + QEMU_KVM_ARM_TARGET_NONE + }; + /* + * target = -1 informs kvm_arm_create_scratch_host_vcpu() + * to use the preferred target + */ + struct kvm_vcpu_init init = { .target = -1, }; + + /* + * Ask for SVE if supported, so that we can query ID_AA64ZFR0, + * which is otherwise RAZ. + */ + sve_supported = kvm_arm_sve_supported(); + if (sve_supported) { + init.features[0] |= 1 << KVM_ARM_VCPU_SVE; + } + + /* + * Ask for Pointer Authentication if supported, so that we get + * the unsanitized field values for AA64ISAR1_EL1. + */ + if (kvm_arm_pauth_supported()) { + init.features[0] |= (1 << KVM_ARM_VCPU_PTRAUTH_ADDRESS | + 1 << KVM_ARM_VCPU_PTRAUTH_GENERIC); + } + + if (kvm_arm_pmu_supported()) { + init.features[0] |= 1 << KVM_ARM_VCPU_PMU_V3; + pmu_supported = true; + } + + if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) { + return false; + } + + ahcf->target = init.target; + ahcf->dtb_compatible = "arm,arm-v8"; + + err = read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64pfr0, + ARM64_SYS_REG(3, 0, 0, 4, 0)); + if (unlikely(err < 0)) { + /* + * Before v4.15, the kernel only exposed a limited number of system + * registers, not including any of the interesting AArch64 ID regs. + * For the most part we could leave these fields as zero with minimal + * effect, since this does not affect the values seen by the guest. + * + * However, it could cause problems down the line for QEMU, + * so provide a minimal v8.0 default. + * + * ??? Could read MIDR and use knowledge from cpu64.c. + * ??? Could map a page of memory into our temp guest and + * run the tiniest of hand-crafted kernels to extract + * the values seen by the guest. + * ??? Either of these sounds like too much effort just + * to work around running a modern host kernel. + */ + ahcf->isar.id_aa64pfr0 = 0x00000011; /* EL1&0, AArch64 only */ + err = 0; + } 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_aa64smfr0, + ARM64_SYS_REG(3, 0, 0, 4, 5)); + 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, + ARM64_SYS_REG(3, 0, 0, 6, 1)); + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64isar2, + ARM64_SYS_REG(3, 0, 0, 6, 2)); + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr0, + 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, + * the AArch32 sysregs are present to be read, but will + * return UNKNOWN values. This is neither better nor worse + * 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_pfr0, + ARM64_SYS_REG(3, 0, 0, 1, 0)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr1, + ARM64_SYS_REG(3, 0, 0, 1, 1)); + 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, + ARM64_SYS_REG(3, 0, 0, 2, 1)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar2, + ARM64_SYS_REG(3, 0, 0, 2, 2)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar3, + ARM64_SYS_REG(3, 0, 0, 2, 3)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar4, + 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)); + + err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr0, + ARM64_SYS_REG(3, 0, 0, 3, 0)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr1, + 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)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr2, + ARM64_SYS_REG(3, 0, 0, 3, 4)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_dfr1, + ARM64_SYS_REG(3, 0, 0, 3, 5)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr5, + ARM64_SYS_REG(3, 0, 0, 3, 6)); + + /* + * 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; + } + + if (pmu_supported) { + /* PMCR_EL0 is only accessible if the vCPU has feature PMU_V3 */ + err |= read_sys_reg64(fdarray[2], &ahcf->isar.reset_pmcr_el0, + ARM64_SYS_REG(3, 3, 9, 12, 0)); + } + + if (sve_supported) { + /* + * There is a range of kernels between kernel commit 73433762fcae + * and f81cb2c3ad41 which have a bug where the kernel doesn't + * expose SYS_ID_AA64ZFR0_EL1 via the ONE_REG API unless the VM has + * enabled SVE support, which resulted in an error rather than RAZ. + * So only read the register if we set KVM_ARM_VCPU_SVE above. + */ + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64zfr0, + ARM64_SYS_REG(3, 0, 0, 4, 4)); + } + } + + kvm_arm_destroy_scratch_host_vcpu(fdarray); + + if (err < 0) { + return false; + } + + /* + * We can assume any KVM supporting CPU is at least a v8 + * with VFPv4+Neon; this in turn implies most of the other + * feature bits. + */ + features |= 1ULL << ARM_FEATURE_V8; + features |= 1ULL << ARM_FEATURE_NEON; + features |= 1ULL << ARM_FEATURE_AARCH64; + features |= 1ULL << ARM_FEATURE_PMU; + features |= 1ULL << ARM_FEATURE_GENERIC_TIMER; + + ahcf->features = features; + + return true; +} + void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) { CPUARMState *env = &cpu->env; @@ -207,10 +497,10 @@ static void kvm_steal_time_set(Object *obj, bool value, Error **errp) } /* KVM VCPU properties should be prefixed with "kvm-". */ -void kvm_arm_add_vcpu_properties(Object *obj) +void kvm_arm_add_vcpu_properties(ARMCPU *cpu) { - ARMCPU *cpu = ARM_CPU(obj); CPUARMState *env = &cpu->env; + Object *obj = OBJECT(cpu); if (arm_feature(env, ARM_FEATURE_GENERIC_TIMER)) { cpu->kvm_adjvtime = true; @@ -245,6 +535,13 @@ int kvm_arm_get_max_vm_ipa_size(MachineState *ms, bool *fixed_ipa) return ret > 0 ? ret : 40; } +int kvm_arch_get_default_type(MachineState *ms) +{ + bool fixed_ipa; + int size = kvm_arm_get_max_vm_ipa_size(ms, &fixed_ipa); + return fixed_ipa ? 0 : size; +} + int kvm_arch_init(MachineState *ms, KVMState *s) { int ret = 0; @@ -261,6 +558,10 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE); + /* Check whether user space can specify guest syndrome value */ + cap_has_inject_serror_esr = + kvm_check_extension(s, KVM_CAP_ARM_INJECT_SERROR_ESR); + if (ms->smp.cpus > 256 && !kvm_check_extension(s, KVM_CAP_ARM_IRQ_LINE_LAYOUT_2)) { error_report("Using more than 256 vcpus requires a host kernel " @@ -278,6 +579,34 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } } + if (s->kvm_eager_split_size) { + uint32_t sizes; + + sizes = kvm_vm_check_extension(s, KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES); + if (!sizes) { + s->kvm_eager_split_size = 0; + warn_report("Eager Page Split support not available"); + } else if (!(s->kvm_eager_split_size & sizes)) { + error_report("Eager Page Split requested chunk size not valid"); + ret = -EINVAL; + } else { + ret = kvm_vm_enable_cap(s, KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE, 0, + s->kvm_eager_split_size); + if (ret < 0) { + error_report("Enabling of Eager Page Split failed: %s", + strerror(-ret)); + } + } + } + + max_hw_wps = kvm_check_extension(s, KVM_CAP_GUEST_DEBUG_HW_WPS); + hw_watchpoints = g_array_sized_new(true, true, + sizeof(HWWatchpoint), max_hw_wps); + + max_hw_bps = kvm_check_extension(s, KVM_CAP_GUEST_DEBUG_HW_BPS); + hw_breakpoints = g_array_sized_new(true, true, + sizeof(HWBreakpoint), max_hw_bps); + return ret; } @@ -337,6 +666,7 @@ static MemoryListener devlistener = { .name = "kvm-arm", .region_add = kvm_arm_devlistener_add, .region_del = kvm_arm_devlistener_del, + .priority = MEMORY_LISTENER_PRIORITY_MIN, }; static void kvm_arm_set_device_addr(KVMDevice *kd) @@ -436,11 +766,36 @@ static uint64_t *kvm_arm_get_cpreg_ptr(ARMCPU *cpu, uint64_t regidx) return &cpu->cpreg_values[res - cpu->cpreg_indexes]; } -/* Initialize the ARMCPU cpreg list according to the kernel's +/** + * 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 + * by hand using code in kvm_arch_get/put_registers(). + */ +static bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx) +{ + switch (regidx & KVM_REG_ARM_COPROC_MASK) { + case KVM_REG_ARM_CORE: + case KVM_REG_ARM64_SVE: + return false; + default: + return true; + } +} + +/** + * kvm_arm_init_cpreg_list: + * @cpu: ARMCPU + * + * 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). + * + * Returns: 0 if success, else < 0 error code */ -int kvm_arm_init_cpreg_list(ARMCPU *cpu) +static int kvm_arm_init_cpreg_list(ARMCPU *cpu) { struct kvm_reg_list rl; struct kvm_reg_list *rlp; @@ -513,6 +868,28 @@ out: return ret; } +/** + * 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. + */ +static int kvm_arm_cpreg_level(uint64_t regidx) +{ + /* + * All system registers are assumed to be level KVM_PUT_RUNTIME_STATE. + * If a register should be written less often, you must add it here + * with a state of either KVM_PUT_RESET_STATE or KVM_PUT_FULL_STATE. + */ + switch (regidx) { + case KVM_REG_ARM_TIMER_CNT: + case KVM_REG_ARM_PTIMER_CNT: + return KVM_PUT_FULL_STATE; + } + return KVM_PUT_RUNTIME_STATE; +} + bool write_kvmstate_to_list(ARMCPU *cpu) { CPUState *cs = CPU(cpu); @@ -520,24 +897,19 @@ bool write_kvmstate_to_list(ARMCPU *cpu) bool ok = true; for (i = 0; i < cpu->cpreg_array_len; i++) { - struct kvm_one_reg r; uint64_t regidx = cpu->cpreg_indexes[i]; uint32_t v32; int ret; - r.id = regidx; - switch (regidx & KVM_REG_SIZE_MASK) { case KVM_REG_SIZE_U32: - r.addr = (uintptr_t)&v32; - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + ret = kvm_get_one_reg(cs, regidx, &v32); if (!ret) { cpu->cpreg_values[i] = v32; } break; case KVM_REG_SIZE_U64: - r.addr = (uintptr_t)(cpu->cpreg_values + i); - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + ret = kvm_get_one_reg(cs, regidx, cpu->cpreg_values + i); break; default: g_assert_not_reached(); @@ -556,7 +928,6 @@ bool write_list_to_kvmstate(ARMCPU *cpu, int level) bool ok = true; for (i = 0; i < cpu->cpreg_array_len; i++) { - struct kvm_one_reg r; uint64_t regidx = cpu->cpreg_indexes[i]; uint32_t v32; int ret; @@ -565,19 +936,17 @@ bool write_list_to_kvmstate(ARMCPU *cpu, int level) continue; } - r.id = regidx; switch (regidx & KVM_REG_SIZE_MASK) { case KVM_REG_SIZE_U32: v32 = cpu->cpreg_values[i]; - r.addr = (uintptr_t)&v32; + ret = kvm_set_one_reg(cs, regidx, &v32); break; case KVM_REG_SIZE_U64: - r.addr = (uintptr_t)(cpu->cpreg_values + i); + ret = kvm_set_one_reg(cs, regidx, cpu->cpreg_values + i); break; default: g_assert_not_reached(); } - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); if (ret) { /* We might fail for "unknown register" and also for * "you tried to set a register which is constant with @@ -613,7 +982,7 @@ void kvm_arm_reset_vcpu(ARMCPU *cpu) /* Re-init VCPU so that all registers are set to * their respective reset values. */ - ret = kvm_arm_vcpu_init(CPU(cpu)); + ret = kvm_arm_vcpu_init(cpu); if (ret < 0) { fprintf(stderr, "kvm_arm_vcpu_init failed: %s\n", strerror(-ret)); abort(); @@ -635,58 +1004,50 @@ void kvm_arm_reset_vcpu(ARMCPU *cpu) /* * Update KVM's MP_STATE based on what QEMU thinks it is */ -int kvm_arm_sync_mpstate_to_kvm(ARMCPU *cpu) +static int kvm_arm_sync_mpstate_to_kvm(ARMCPU *cpu) { if (cap_has_mp_state) { struct kvm_mp_state mp_state = { .mp_state = (cpu->power_state == PSCI_OFF) ? KVM_MP_STATE_STOPPED : KVM_MP_STATE_RUNNABLE }; - int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state); - if (ret) { - fprintf(stderr, "%s: failed to set MP_STATE %d/%s\n", - __func__, ret, strerror(-ret)); - return -1; - } + return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state); } - return 0; } /* * Sync the KVM MP_STATE into QEMU */ -int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu) +static int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu) { if (cap_has_mp_state) { struct kvm_mp_state mp_state; int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MP_STATE, &mp_state); if (ret) { - fprintf(stderr, "%s: failed to get MP_STATE %d/%s\n", - __func__, ret, strerror(-ret)); - abort(); + return ret; } cpu->power_state = (mp_state.mp_state == KVM_MP_STATE_STOPPED) ? PSCI_OFF : PSCI_ON; } - return 0; } -void kvm_arm_get_virtual_time(CPUState *cs) +/** + * kvm_arm_get_virtual_time: + * @cpu: ARMCPU + * + * Gets the VCPU's virtual counter and stores it in the KVM CPU state. + */ +static void kvm_arm_get_virtual_time(ARMCPU *cpu) { - 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, ®); + ret = kvm_get_one_reg(CPU(cpu), KVM_REG_ARM_TIMER_CNT, &cpu->kvm_vtime); if (ret) { error_report("Failed to get KVM_REG_ARM_TIMER_CNT"); abort(); @@ -695,20 +1056,21 @@ void kvm_arm_get_virtual_time(CPUState *cs) cpu->kvm_vtime_dirty = true; } -void kvm_arm_put_virtual_time(CPUState *cs) +/** + * kvm_arm_put_virtual_time: + * @cpu: ARMCPU + * + * Sets the VCPU's virtual counter to the value stored in the KVM CPU state. + */ +static void kvm_arm_put_virtual_time(ARMCPU *cpu) { - 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, ®); + ret = kvm_set_one_reg(CPU(cpu), KVM_REG_ARM_TIMER_CNT, &cpu->kvm_vtime); if (ret) { error_report("Failed to set KVM_REG_ARM_TIMER_CNT"); abort(); @@ -717,7 +1079,15 @@ void kvm_arm_put_virtual_time(CPUState *cs) cpu->kvm_vtime_dirty = false; } -int kvm_put_vcpu_events(ARMCPU *cpu) +/** + * kvm_put_vcpu_events: + * @cpu: ARMCPU + * + * Put VCPU related state to kvm. + * + * Returns: 0 if success else < 0 error code + */ +static int kvm_put_vcpu_events(ARMCPU *cpu) { CPUARMState *env = &cpu->env; struct kvm_vcpu_events events; @@ -746,7 +1116,15 @@ int kvm_put_vcpu_events(ARMCPU *cpu) return ret; } -int kvm_get_vcpu_events(ARMCPU *cpu) +/** + * kvm_get_vcpu_events: + * @cpu: ARMCPU + * + * Get VCPU related state from kvm. + * + * Returns: 0 if success else < 0 error code + */ +static int kvm_get_vcpu_events(ARMCPU *cpu) { CPUARMState *env = &cpu->env; struct kvm_vcpu_events events; @@ -770,6 +1148,63 @@ int kvm_get_vcpu_events(ARMCPU *cpu) return 0; } +#define ARM64_REG_ESR_EL1 ARM64_SYS_REG(3, 0, 5, 2, 0) +#define ARM64_REG_TCR_EL1 ARM64_SYS_REG(3, 0, 2, 0, 2) + +/* + * ESR_EL1 + * ISS encoding + * AARCH64: DFSC, bits [5:0] + * AARCH32: + * TTBCR.EAE == 0 + * FS[4] - DFSR[10] + * FS[3:0] - DFSR[3:0] + * TTBCR.EAE == 1 + * FS, bits [5:0] + */ +#define ESR_DFSC(aarch64, lpae, v) \ + ((aarch64 || (lpae)) ? ((v) & 0x3F) \ + : (((v) >> 6) | ((v) & 0x1F))) + +#define ESR_DFSC_EXTABT(aarch64, lpae) \ + ((aarch64) ? 0x10 : (lpae) ? 0x10 : 0x8) + +/** + * kvm_arm_verify_ext_dabt_pending: + * @cpu: ARMCPU + * + * Verify the fault status code wrt the Ext DABT injection + * + * Returns: true if the fault status code is as expected, false otherwise + */ +static bool kvm_arm_verify_ext_dabt_pending(ARMCPU *cpu) +{ + CPUState *cs = CPU(cpu); + uint64_t dfsr_val; + + if (!kvm_get_one_reg(cs, ARM64_REG_ESR_EL1, &dfsr_val)) { + CPUARMState *env = &cpu->env; + int aarch64_mode = arm_feature(env, ARM_FEATURE_AARCH64); + int lpae = 0; + + if (!aarch64_mode) { + uint64_t ttbcr; + + if (!kvm_get_one_reg(cs, ARM64_REG_TCR_EL1, &ttbcr)) { + lpae = arm_feature(env, ARM_FEATURE_LPAE) + && (ttbcr & TTBCR_EAE); + } + } + /* + * The verification here is based on the DFSC bits + * of the ESR_EL1 reg only + */ + return (ESR_DFSC(aarch64_mode, lpae, dfsr_val) == + ESR_DFSC_EXTABT(aarch64_mode, lpae)); + } + return false; +} + void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) { ARMCPU *cpu = ARM_CPU(cs); @@ -784,7 +1219,7 @@ void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) * an IMPLEMENTATION DEFINED exception (for 32-bit EL1) */ if (!arm_feature(env, ARM_FEATURE_AARCH64) && - unlikely(!kvm_arm_verify_ext_dabt_pending(cs))) { + unlikely(!kvm_arm_verify_ext_dabt_pending(cpu))) { error_report("Data abort exception with no valid ISS generated by " "guest memory access. KVM unable to emulate faulting " @@ -816,7 +1251,7 @@ MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) if (run->s.regs.device_irq_level != cpu->device_irq_level) { switched_level = cpu->device_irq_level ^ run->s.regs.device_irq_level; - qemu_mutex_lock_iothread(); + bql_lock(); if (switched_level & KVM_ARM_DEV_EL1_VTIMER) { qemu_set_irq(cpu->gt_timer_outputs[GTIMER_VIRT], @@ -845,41 +1280,39 @@ MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) /* We also mark unknown levels as processed to not waste cycles */ cpu->device_irq_level = run->s.regs.device_irq_level; - qemu_mutex_unlock_iothread(); + bql_unlock(); } return MEMTXATTRS_UNSPECIFIED; } -void kvm_arm_vm_state_change(void *opaque, bool running, RunState state) +static void kvm_arm_vm_state_change(void *opaque, bool running, RunState state) { - CPUState *cs = opaque; - ARMCPU *cpu = ARM_CPU(cs); + ARMCPU *cpu = opaque; if (running) { if (cpu->kvm_adjvtime) { - kvm_arm_put_virtual_time(cs); + kvm_arm_put_virtual_time(cpu); } } else { if (cpu->kvm_adjvtime) { - kvm_arm_get_virtual_time(cs); + kvm_arm_get_virtual_time(cpu); } } } /** * kvm_arm_handle_dabt_nisv: - * @cs: CPUState + * @cpu: ARMCPU * @esr_iss: ISS encoding (limited) for the exception from Data Abort * ISV bit set to '0b0' -> no valid instruction syndrome * @fault_ipa: faulting address for the synchronous data abort * * Returns: 0 if the exception has been handled, < 0 otherwise */ -static int kvm_arm_handle_dabt_nisv(CPUState *cs, uint64_t esr_iss, +static int kvm_arm_handle_dabt_nisv(ARMCPU *cpu, uint64_t esr_iss, uint64_t fault_ipa) { - ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; /* * Request KVM to inject the external data abort into the guest @@ -895,7 +1328,7 @@ static int kvm_arm_handle_dabt_nisv(CPUState *cs, uint64_t esr_iss, */ events.exception.ext_dabt_pending = 1; /* KVM_CAP_ARM_INJECT_EXT_DABT implies KVM_CAP_VCPU_EVENTS */ - if (!kvm_vcpu_ioctl(cs, KVM_SET_VCPU_EVENTS, &events)) { + if (!kvm_vcpu_ioctl(CPU(cpu), KVM_SET_VCPU_EVENTS, &events)) { env->ext_dabt_raised = 1; return 0; } @@ -908,19 +1341,97 @@ static int kvm_arm_handle_dabt_nisv(CPUState *cs, uint64_t esr_iss, return -1; } +/** + * kvm_arm_handle_debug: + * @cpu: ARMCPU + * @debug_exit: debug part of the KVM exit structure + * + * Returns: TRUE if the debug exception was handled. + * + * See v8 ARM ARM D7.2.27 ESR_ELx, Exception Syndrome Register + * + * To minimise translating between kernel and user-space the kernel + * ABI just provides user-space with the full exception syndrome + * register value to be decoded in QEMU. + */ +static bool kvm_arm_handle_debug(ARMCPU *cpu, + struct kvm_debug_exit_arch *debug_exit) +{ + int hsr_ec = syn_get_ec(debug_exit->hsr); + CPUState *cs = CPU(cpu); + CPUARMState *env = &cpu->env; + + /* Ensure PC is synchronised */ + kvm_cpu_synchronize_state(cs); + + switch (hsr_ec) { + case EC_SOFTWARESTEP: + if (cs->singlestep_enabled) { + return true; + } else { + /* + * The kernel should have suppressed the guest's ability to + * single step at this point so something has gone wrong. + */ + error_report("%s: guest single-step while debugging unsupported" + " (%"PRIx64", %"PRIx32")", + __func__, env->pc, debug_exit->hsr); + return false; + } + break; + case EC_AA64_BKPT: + if (kvm_find_sw_breakpoint(cs, env->pc)) { + return true; + } + break; + case EC_BREAKPOINT: + if (find_hw_breakpoint(cs, env->pc)) { + return true; + } + break; + case EC_WATCHPOINT: + { + CPUWatchpoint *wp = find_hw_watchpoint(cs, debug_exit->far); + if (wp) { + cs->watchpoint_hit = wp; + return true; + } + break; + } + default: + error_report("%s: unhandled debug exit (%"PRIx32", %"PRIx64")", + __func__, debug_exit->hsr, env->pc); + } + + /* If we are not handling the debug exception it must belong to + * the guest. Let's re-use the existing TCG interrupt code to set + * everything up properly. + */ + cs->exception_index = EXCP_BKPT; + env->exception.syndrome = debug_exit->hsr; + env->exception.vaddress = debug_exit->far; + env->exception.target_el = 1; + bql_lock(); + arm_cpu_do_interrupt(cs); + bql_unlock(); + + return false; +} + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { + ARMCPU *cpu = ARM_CPU(cs); int ret = 0; switch (run->exit_reason) { case KVM_EXIT_DEBUG: - if (kvm_arm_handle_debug(cs, &run->debug.arch)) { + if (kvm_arm_handle_debug(cpu, &run->debug.arch)) { ret = EXCP_DEBUG; } /* otherwise return to guest */ break; case KVM_EXIT_ARM_NISV: /* External DABT with no valid iss to decode */ - ret = kvm_arm_handle_dabt_nisv(cs, run->arm_nisv.esr_iss, + ret = kvm_arm_handle_dabt_nisv(cpu, run->arm_nisv.esr_iss, run->arm_nisv.fault_ipa); break; default: @@ -941,12 +1452,47 @@ int kvm_arch_process_async_events(CPUState *cs) return 0; } +/** + * kvm_arm_hw_debug_active: + * @cpu: ARMCPU + * + * Return: TRUE if any hardware breakpoints in use. + */ +static bool kvm_arm_hw_debug_active(ARMCPU *cpu) +{ + return ((cur_hw_wps > 0) || (cur_hw_bps > 0)); +} + +/** + * 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. + */ +static void kvm_arm_copy_hw_debug_data(struct kvm_guest_debug_arch *ptr) +{ + int i; + memset(ptr, 0, sizeof(struct kvm_guest_debug_arch)); + + for (i = 0; i < max_hw_wps; i++) { + HWWatchpoint *wp = get_hw_wp(i); + ptr->dbg_wcr[i] = wp->wcr; + ptr->dbg_wvr[i] = wp->wvr; + } + for (i = 0; i < max_hw_bps; i++) { + HWBreakpoint *bp = get_hw_bp(i); + ptr->dbg_bcr[i] = bp->bcr; + ptr->dbg_bvr[i] = bp->bvr; + } +} + void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg) { if (kvm_sw_breakpoints_active(cs)) { dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; } - if (kvm_arm_hw_debug_active(cs)) { + if (kvm_arm_hw_debug_active(ARM_CPU(cs))) { dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW; kvm_arm_copy_hw_debug_data(&dbg->arch); } @@ -959,7 +1505,7 @@ void kvm_arch_init_irq_routing(KVMState *s) int kvm_arch_irqchip_create(KVMState *s) { if (kvm_kernel_irqchip_split()) { - perror("-machine kernel_irqchip=split is not supported on ARM."); + error_report("-machine kernel_irqchip=split is not supported on ARM."); exit(1); } @@ -1056,3 +1602,826 @@ bool kvm_arch_cpu_check_are_resettable(void) { return true; } + +static void kvm_arch_get_eager_split_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + uint64_t value = s->kvm_eager_split_size; + + visit_type_size(v, name, &value, errp); +} + +static void kvm_arch_set_eager_split_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + uint64_t value; + + if (s->fd != -1) { + error_setg(errp, "Unable to set early-split-size after KVM has been initialized"); + return; + } + + if (!visit_type_size(v, name, &value, errp)) { + return; + } + + if (value && !is_power_of_2(value)) { + error_setg(errp, "early-split-size must be a power of two"); + return; + } + + s->kvm_eager_split_size = value; +} + +void kvm_arch_accel_class_init(ObjectClass *oc) +{ + object_class_property_add(oc, "eager-split-size", "size", + kvm_arch_get_eager_split_size, + kvm_arch_set_eager_split_size, NULL, NULL); + + object_class_property_set_description(oc, "eager-split-size", + "Eager Page Split chunk size for hugepages. (default: 0, disabled)"); +} + +int kvm_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type) +{ + switch (type) { + case GDB_BREAKPOINT_HW: + return insert_hw_breakpoint(addr); + break; + case GDB_WATCHPOINT_READ: + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_ACCESS: + return insert_hw_watchpoint(addr, len, type); + default: + return -ENOSYS; + } +} + +int kvm_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type) +{ + switch (type) { + case GDB_BREAKPOINT_HW: + return delete_hw_breakpoint(addr); + case GDB_WATCHPOINT_READ: + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_ACCESS: + return delete_hw_watchpoint(addr, len, type); + default: + return -ENOSYS; + } +} + +void kvm_arch_remove_all_hw_breakpoints(void) +{ + if (cur_hw_wps > 0) { + g_array_remove_range(hw_watchpoints, 0, cur_hw_wps); + } + if (cur_hw_bps > 0) { + g_array_remove_range(hw_breakpoints, 0, cur_hw_bps); + } +} + +static bool kvm_arm_set_device_attr(ARMCPU *cpu, struct kvm_device_attr *attr, + const char *name) +{ + int err; + + err = kvm_vcpu_ioctl(CPU(cpu), KVM_HAS_DEVICE_ATTR, attr); + if (err != 0) { + error_report("%s: KVM_HAS_DEVICE_ATTR: %s", name, strerror(-err)); + return false; + } + + err = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_DEVICE_ATTR, attr); + if (err != 0) { + error_report("%s: KVM_SET_DEVICE_ATTR: %s", name, strerror(-err)); + return false; + } + + return true; +} + +void kvm_arm_pmu_init(ARMCPU *cpu) +{ + struct kvm_device_attr attr = { + .group = KVM_ARM_VCPU_PMU_V3_CTRL, + .attr = KVM_ARM_VCPU_PMU_V3_INIT, + }; + + if (!cpu->has_pmu) { + return; + } + if (!kvm_arm_set_device_attr(cpu, &attr, "PMU")) { + error_report("failed to init PMU"); + abort(); + } +} + +void kvm_arm_pmu_set_irq(ARMCPU *cpu, int irq) +{ + struct kvm_device_attr attr = { + .group = KVM_ARM_VCPU_PMU_V3_CTRL, + .addr = (intptr_t)&irq, + .attr = KVM_ARM_VCPU_PMU_V3_IRQ, + }; + + if (!cpu->has_pmu) { + return; + } + if (!kvm_arm_set_device_attr(cpu, &attr, "PMU")) { + error_report("failed to set irq for PMU"); + abort(); + } +} + +void kvm_arm_pvtime_init(ARMCPU *cpu, uint64_t ipa) +{ + struct kvm_device_attr attr = { + .group = KVM_ARM_VCPU_PVTIME_CTRL, + .attr = KVM_ARM_VCPU_PVTIME_IPA, + .addr = (uint64_t)&ipa, + }; + + if (cpu->kvm_steal_time == ON_OFF_AUTO_OFF) { + return; + } + if (!kvm_arm_set_device_attr(cpu, &attr, "PVTIME IPA")) { + error_report("failed to init PVTIME IPA"); + abort(); + } +} + +void kvm_arm_steal_time_finalize(ARMCPU *cpu, Error **errp) +{ + bool has_steal_time = kvm_check_extension(kvm_state, KVM_CAP_STEAL_TIME); + + if (cpu->kvm_steal_time == ON_OFF_AUTO_AUTO) { + if (!has_steal_time || !arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + cpu->kvm_steal_time = ON_OFF_AUTO_OFF; + } else { + cpu->kvm_steal_time = ON_OFF_AUTO_ON; + } + } else if (cpu->kvm_steal_time == ON_OFF_AUTO_ON) { + if (!has_steal_time) { + error_setg(errp, "'kvm-steal-time' cannot be enabled " + "on this host"); + return; + } else if (!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + /* + * DEN0057A chapter 2 says "This specification only covers + * systems in which the Execution state of the hypervisor + * as well as EL1 of virtual machines is AArch64.". And, + * to ensure that, the smc/hvc calls are only specified as + * smc64/hvc64. + */ + error_setg(errp, "'kvm-steal-time' cannot be enabled " + "for AArch32 guests"); + return; + } + } +} + +bool kvm_arm_aarch32_supported(void) +{ + return kvm_check_extension(kvm_state, KVM_CAP_ARM_EL1_32BIT); +} + +bool kvm_arm_sve_supported(void) +{ + return kvm_check_extension(kvm_state, KVM_CAP_ARM_SVE); +} + +QEMU_BUILD_BUG_ON(KVM_ARM64_SVE_VQ_MIN != 1); + +uint32_t kvm_arm_sve_get_vls(ARMCPU *cpu) +{ + /* Only call this function if kvm_arm_sve_supported() returns true. */ + static uint64_t vls[KVM_ARM64_SVE_VLS_WORDS]; + static bool probed; + uint32_t vq = 0; + int i; + + /* + * KVM ensures all host CPUs support the same set of vector lengths. + * So we only need to create the scratch VCPUs once and then cache + * the results. + */ + if (!probed) { + struct kvm_vcpu_init init = { + .target = -1, + .features[0] = (1 << KVM_ARM_VCPU_SVE), + }; + struct kvm_one_reg reg = { + .id = KVM_REG_ARM64_SVE_VLS, + .addr = (uint64_t)&vls[0], + }; + int fdarray[3], ret; + + probed = true; + + if (!kvm_arm_create_scratch_host_vcpu(NULL, fdarray, &init)) { + error_report("failed to create scratch VCPU with SVE enabled"); + abort(); + } + ret = ioctl(fdarray[2], KVM_GET_ONE_REG, ®); + kvm_arm_destroy_scratch_host_vcpu(fdarray); + if (ret) { + error_report("failed to get KVM_REG_ARM64_SVE_VLS: %s", + strerror(errno)); + abort(); + } + + for (i = KVM_ARM64_SVE_VLS_WORDS - 1; i >= 0; --i) { + if (vls[i]) { + vq = 64 - clz64(vls[i]) + i * 64; + break; + } + } + if (vq > ARM_MAX_VQ) { + warn_report("KVM supports vector lengths larger than " + "QEMU can enable"); + vls[0] &= MAKE_64BIT_MASK(0, ARM_MAX_VQ); + } + } + + return vls[0]; +} + +static int kvm_arm_sve_set_vls(ARMCPU *cpu) +{ + uint64_t vls[KVM_ARM64_SVE_VLS_WORDS] = { cpu->sve_vq.map }; + + assert(cpu->sve_max_vq <= KVM_ARM64_SVE_VQ_MAX); + + return kvm_set_one_reg(CPU(cpu), KVM_REG_ARM64_SVE_VLS, &vls[0]); +} + +#define ARM_CPU_ID_MPIDR 3, 0, 0, 0, 5 + +int kvm_arch_init_vcpu(CPUState *cs) +{ + int ret; + uint64_t mpidr; + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + uint64_t psciver; + + if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE || + !object_dynamic_cast(OBJECT(cpu), TYPE_AARCH64_CPU)) { + error_report("KVM is not supported for this guest CPU type"); + return -EINVAL; + } + + qemu_add_vm_change_state_handler(kvm_arm_vm_state_change, cpu); + + /* Determine init features for this CPU */ + memset(cpu->kvm_init_features, 0, sizeof(cpu->kvm_init_features)); + if (cs->start_powered_off) { + cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_POWER_OFF; + } + if (kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PSCI_0_2)) { + cpu->psci_version = QEMU_PSCI_VERSION_0_2; + cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2; + } + if (!arm_feature(env, ARM_FEATURE_AARCH64)) { + cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_EL1_32BIT; + } + if (!kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PMU_V3)) { + cpu->has_pmu = false; + } + if (cpu->has_pmu) { + cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PMU_V3; + } else { + env->features &= ~(1ULL << ARM_FEATURE_PMU); + } + if (cpu_isar_feature(aa64_sve, cpu)) { + assert(kvm_arm_sve_supported()); + cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_SVE; + } + if (cpu_isar_feature(aa64_pauth, cpu)) { + cpu->kvm_init_features[0] |= (1 << KVM_ARM_VCPU_PTRAUTH_ADDRESS | + 1 << KVM_ARM_VCPU_PTRAUTH_GENERIC); + } + + /* Do KVM_ARM_VCPU_INIT ioctl */ + ret = kvm_arm_vcpu_init(cpu); + if (ret) { + return ret; + } + + if (cpu_isar_feature(aa64_sve, cpu)) { + ret = kvm_arm_sve_set_vls(cpu); + if (ret) { + return ret; + } + ret = kvm_arm_vcpu_finalize(cpu, KVM_ARM_VCPU_SVE); + if (ret) { + return ret; + } + } + + /* + * KVM reports the exact PSCI version it is implementing via a + * special sysreg. If it is present, use its contents to determine + * what to report to the guest in the dtb (it is the PSCI version, + * in the same 15-bits major 16-bits minor format that PSCI_VERSION + * returns). + */ + if (!kvm_get_one_reg(cs, KVM_REG_ARM_PSCI_VERSION, &psciver)) { + cpu->psci_version = psciver; + } + + /* + * When KVM is in use, PSCI is emulated in-kernel and not by qemu. + * Currently KVM has its own idea about MPIDR assignment, so we + * override our defaults with what we get from KVM. + */ + ret = kvm_get_one_reg(cs, ARM64_SYS_REG(ARM_CPU_ID_MPIDR), &mpidr); + if (ret) { + return ret; + } + cpu->mp_affinity = mpidr & ARM64_AFFINITY_MASK; + + return kvm_arm_init_cpreg_list(cpu); +} + +int kvm_arch_destroy_vcpu(CPUState *cs) +{ + return 0; +} + +/* Callers must hold the iothread mutex lock */ +static void kvm_inject_arm_sea(CPUState *c) +{ + ARMCPU *cpu = ARM_CPU(c); + CPUARMState *env = &cpu->env; + uint32_t esr; + bool same_el; + + c->exception_index = EXCP_DATA_ABORT; + env->exception.target_el = 1; + + /* + * Set the DFSC to synchronous external abort and set FnV to not valid, + * this will tell guest the FAR_ELx is UNKNOWN for this abort. + */ + same_el = arm_current_el(env) == env->exception.target_el; + esr = syn_data_abort_no_iss(same_el, 1, 0, 0, 0, 0, 0x10); + + env->exception.syndrome = esr; + + arm_cpu_do_interrupt(c); +} + +#define AARCH64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) + +#define AARCH64_SIMD_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U128 | \ + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) + +#define AARCH64_SIMD_CTRL_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U32 | \ + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) + +static int kvm_arch_put_fpsimd(CPUState *cs) +{ + CPUARMState *env = &ARM_CPU(cs)->env; + int i, ret; + + for (i = 0; i < 32; i++) { + uint64_t *q = aa64_vfp_qreg(env, i); +#if HOST_BIG_ENDIAN + uint64_t fp_val[2] = { q[1], q[0] }; + ret = kvm_set_one_reg(cs, AARCH64_SIMD_CORE_REG(fp_regs.vregs[i]), + fp_val); +#else + ret = kvm_set_one_reg(cs, AARCH64_SIMD_CORE_REG(fp_regs.vregs[i]), q); +#endif + if (ret) { + return ret; + } + } + + return 0; +} + +/* + * 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 + * code the slice index to zero for now as it's unlikely we'll need more than + * one slice for quite some time. + */ +static int kvm_arch_put_sve(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + uint64_t tmp[ARM_MAX_VQ * 2]; + uint64_t *r; + int n, ret; + + for (n = 0; n < KVM_ARM64_SVE_NUM_ZREGS; ++n) { + r = sve_bswap64(tmp, &env->vfp.zregs[n].d[0], cpu->sve_max_vq * 2); + ret = kvm_set_one_reg(cs, KVM_REG_ARM64_SVE_ZREG(n, 0), r); + if (ret) { + return ret; + } + } + + for (n = 0; n < KVM_ARM64_SVE_NUM_PREGS; ++n) { + r = sve_bswap64(tmp, r = &env->vfp.pregs[n].p[0], + DIV_ROUND_UP(cpu->sve_max_vq * 2, 8)); + ret = kvm_set_one_reg(cs, KVM_REG_ARM64_SVE_PREG(n, 0), r); + if (ret) { + return ret; + } + } + + r = sve_bswap64(tmp, &env->vfp.pregs[FFR_PRED_NUM].p[0], + DIV_ROUND_UP(cpu->sve_max_vq * 2, 8)); + ret = kvm_set_one_reg(cs, KVM_REG_ARM64_SVE_FFR(0), r); + if (ret) { + return ret; + } + + return 0; +} + +int kvm_arch_put_registers(CPUState *cs, int level) +{ + uint64_t val; + uint32_t fpr; + int i, ret; + unsigned int el; + + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + /* If we are in AArch32 mode then we need to copy the AArch32 regs to the + * AArch64 registers before pushing them out to 64-bit KVM. + */ + if (!is_a64(env)) { + aarch64_sync_32_to_64(env); + } + + for (i = 0; i < 31; i++) { + ret = kvm_set_one_reg(cs, AARCH64_CORE_REG(regs.regs[i]), + &env->xregs[i]); + if (ret) { + return ret; + } + } + + /* KVM puts SP_EL0 in regs.sp and SP_EL1 in regs.sp_el1. On the + * QEMU side we keep the current SP in xregs[31] as well. + */ + aarch64_save_sp(env, 1); + + ret = kvm_set_one_reg(cs, AARCH64_CORE_REG(regs.sp), &env->sp_el[0]); + if (ret) { + return ret; + } + + ret = kvm_set_one_reg(cs, AARCH64_CORE_REG(sp_el1), &env->sp_el[1]); + if (ret) { + return ret; + } + + /* Note that KVM thinks pstate is 64 bit but we use a uint32_t */ + if (is_a64(env)) { + val = pstate_read(env); + } else { + val = cpsr_read(env); + } + ret = kvm_set_one_reg(cs, AARCH64_CORE_REG(regs.pstate), &val); + if (ret) { + return ret; + } + + ret = kvm_set_one_reg(cs, AARCH64_CORE_REG(regs.pc), &env->pc); + if (ret) { + return ret; + } + + ret = kvm_set_one_reg(cs, AARCH64_CORE_REG(elr_el1), &env->elr_el[1]); + if (ret) { + return ret; + } + + /* Saved Program State Registers + * + * Before we restore from the banked_spsr[] array we need to + * ensure that any modifications to env->spsr are correctly + * reflected in the banks. + */ + el = arm_current_el(env); + if (el > 0 && !is_a64(env)) { + i = bank_number(env->uncached_cpsr & CPSR_M); + env->banked_spsr[i] = env->spsr; + } + + /* KVM 0-4 map to QEMU banks 1-5 */ + for (i = 0; i < KVM_NR_SPSR; i++) { + ret = kvm_set_one_reg(cs, AARCH64_CORE_REG(spsr[i]), + &env->banked_spsr[i + 1]); + if (ret) { + return ret; + } + } + + if (cpu_isar_feature(aa64_sve, cpu)) { + ret = kvm_arch_put_sve(cs); + } else { + ret = kvm_arch_put_fpsimd(cs); + } + if (ret) { + return ret; + } + + fpr = vfp_get_fpsr(env); + ret = kvm_set_one_reg(cs, AARCH64_SIMD_CTRL_REG(fp_regs.fpsr), &fpr); + if (ret) { + return ret; + } + + fpr = vfp_get_fpcr(env); + ret = kvm_set_one_reg(cs, AARCH64_SIMD_CTRL_REG(fp_regs.fpcr), &fpr); + 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; + } + + return kvm_arm_sync_mpstate_to_kvm(cpu); +} + +static int kvm_arch_get_fpsimd(CPUState *cs) +{ + CPUARMState *env = &ARM_CPU(cs)->env; + int i, ret; + + for (i = 0; i < 32; i++) { + uint64_t *q = aa64_vfp_qreg(env, i); + ret = kvm_get_one_reg(cs, AARCH64_SIMD_CORE_REG(fp_regs.vregs[i]), q); + if (ret) { + return ret; + } else { +#if HOST_BIG_ENDIAN + uint64_t t; + t = q[0], q[0] = q[1], q[1] = t; +#endif + } + } + + return 0; +} + +/* + * 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 + * code the slice index to zero for now as it's unlikely we'll need more than + * one slice for quite some time. + */ +static int kvm_arch_get_sve(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + uint64_t *r; + int n, ret; + + for (n = 0; n < KVM_ARM64_SVE_NUM_ZREGS; ++n) { + r = &env->vfp.zregs[n].d[0]; + ret = kvm_get_one_reg(cs, KVM_REG_ARM64_SVE_ZREG(n, 0), r); + if (ret) { + return ret; + } + sve_bswap64(r, r, cpu->sve_max_vq * 2); + } + + for (n = 0; n < KVM_ARM64_SVE_NUM_PREGS; ++n) { + r = &env->vfp.pregs[n].p[0]; + ret = kvm_get_one_reg(cs, KVM_REG_ARM64_SVE_PREG(n, 0), r); + if (ret) { + return ret; + } + sve_bswap64(r, r, DIV_ROUND_UP(cpu->sve_max_vq * 2, 8)); + } + + r = &env->vfp.pregs[FFR_PRED_NUM].p[0]; + ret = kvm_get_one_reg(cs, KVM_REG_ARM64_SVE_FFR(0), r); + if (ret) { + return ret; + } + sve_bswap64(r, r, DIV_ROUND_UP(cpu->sve_max_vq * 2, 8)); + + return 0; +} + +int kvm_arch_get_registers(CPUState *cs) +{ + uint64_t val; + unsigned int el; + uint32_t fpr; + int i, ret; + + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + for (i = 0; i < 31; i++) { + ret = kvm_get_one_reg(cs, AARCH64_CORE_REG(regs.regs[i]), + &env->xregs[i]); + if (ret) { + return ret; + } + } + + ret = kvm_get_one_reg(cs, AARCH64_CORE_REG(regs.sp), &env->sp_el[0]); + if (ret) { + return ret; + } + + ret = kvm_get_one_reg(cs, AARCH64_CORE_REG(sp_el1), &env->sp_el[1]); + if (ret) { + return ret; + } + + ret = kvm_get_one_reg(cs, AARCH64_CORE_REG(regs.pstate), &val); + if (ret) { + return ret; + } + + env->aarch64 = ((val & PSTATE_nRW) == 0); + if (is_a64(env)) { + pstate_write(env, val); + } else { + cpsr_write(env, val, 0xffffffff, CPSRWriteRaw); + } + + /* KVM puts SP_EL0 in regs.sp and SP_EL1 in regs.sp_el1. On the + * QEMU side we keep the current SP in xregs[31] as well. + */ + aarch64_restore_sp(env, 1); + + ret = kvm_get_one_reg(cs, AARCH64_CORE_REG(regs.pc), &env->pc); + if (ret) { + return ret; + } + + /* If we are in AArch32 mode then we need to sync the AArch32 regs with the + * incoming AArch64 regs received from 64-bit KVM. + * We must perform this after all of the registers have been acquired from + * the kernel. + */ + if (!is_a64(env)) { + aarch64_sync_64_to_32(env); + } + + ret = kvm_get_one_reg(cs, AARCH64_CORE_REG(elr_el1), &env->elr_el[1]); + if (ret) { + return ret; + } + + /* Fetch the SPSR registers + * + * KVM SPSRs 0-4 map to QEMU banks 1-5 + */ + for (i = 0; i < KVM_NR_SPSR; i++) { + ret = kvm_get_one_reg(cs, AARCH64_CORE_REG(spsr[i]), + &env->banked_spsr[i + 1]); + if (ret) { + return ret; + } + } + + el = arm_current_el(env); + if (el > 0 && !is_a64(env)) { + i = bank_number(env->uncached_cpsr & CPSR_M); + env->spsr = env->banked_spsr[i]; + } + + if (cpu_isar_feature(aa64_sve, cpu)) { + ret = kvm_arch_get_sve(cs); + } else { + ret = kvm_arch_get_fpsimd(cs); + } + if (ret) { + return ret; + } + + ret = kvm_get_one_reg(cs, AARCH64_SIMD_CTRL_REG(fp_regs.fpsr), &fpr); + if (ret) { + return ret; + } + vfp_set_fpsr(env, fpr); + + ret = kvm_get_one_reg(cs, AARCH64_SIMD_CTRL_REG(fp_regs.fpcr), &fpr); + if (ret) { + return ret; + } + vfp_set_fpcr(env, fpr); + + ret = kvm_get_vcpu_events(cpu); + if (ret) { + return ret; + } + + if (!write_kvmstate_to_list(cpu)) { + return -EINVAL; + } + /* Note that it's OK to have registers which aren't in CPUState, + * so we can ignore a failure return here. + */ + write_list_to_cpustate(cpu); + + ret = kvm_arm_sync_mpstate_to_qemu(cpu); + + /* TODO: other registers */ + return ret; +} + +void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr) +{ + ram_addr_t ram_addr; + hwaddr paddr; + + assert(code == BUS_MCEERR_AR || code == BUS_MCEERR_AO); + + if (acpi_ghes_present() && addr) { + ram_addr = qemu_ram_addr_from_host(addr); + if (ram_addr != RAM_ADDR_INVALID && + kvm_physical_memory_addr_from_host(c->kvm_state, addr, &paddr)) { + kvm_hwpoison_page_add(ram_addr); + /* + * If this is a BUS_MCEERR_AR, we know we have been called + * synchronously from the vCPU thread, so we can easily + * synchronize the state and inject an error. + * + * TODO: we currently don't tell the guest at all about + * BUS_MCEERR_AO. In that case we might either be being + * called synchronously from the vCPU thread, or a bit + * later from the main thread, so doing the injection of + * the error would be more complicated. + */ + if (code == BUS_MCEERR_AR) { + kvm_cpu_synchronize_state(c); + if (!acpi_ghes_record_errors(ACPI_HEST_SRC_ID_SEA, paddr)) { + kvm_inject_arm_sea(c); + } else { + error_report("failed to record the error"); + abort(); + } + } + return; + } + if (code == BUS_MCEERR_AO) { + error_report("Hardware memory error at addr %p for memory used by " + "QEMU itself instead of guest system!", addr); + } + } + + if (code == BUS_MCEERR_AR) { + error_report("Hardware memory error!"); + exit(1); + } +} + +/* C6.6.29 BRK instruction */ +static const uint32_t brk_insn = 0xd4200000; + +int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk_insn, 4, 1)) { + return -EINVAL; + } + return 0; +} + +int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + static uint32_t brk; + + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk, 4, 0) || + brk != brk_insn || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 1)) { + return -EINVAL; + } + return 0; +} diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c deleted file mode 100644 index ff8f65da22..0000000000 --- a/target/arm/kvm64.c +++ /dev/null @@ -1,1629 +0,0 @@ -/* - * ARM implementation of KVM hooks, 64 bit specific code - * - * Copyright Mian-M. Hamayun 2013, Virtual Open Systems - * Copyright Alex Bennée 2014, 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. - * - */ - -#include "qemu/osdep.h" -#include -#include - -#include -#include - -#include "qapi/error.h" -#include "cpu.h" -#include "qemu/timer.h" -#include "qemu/error-report.h" -#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 "internals.h" -#include "hw/acpi/acpi.h" -#include "hw/acpi/ghes.h" -#include "hw/arm/virt.h" - -static bool have_guest_debug; - -/* - * Although the ARM implementation of hardware assisted debugging - * allows for different breakpoints per-core, the current GDB - * interface treats them as a global pool of registers (which seems to - * be the case for x86, ppc and s390). As a result we store one copy - * of registers which is used for all active cores. - * - * Write access is serialised by virtue of the GDB protocol which - * updates things. Read access (i.e. when the values are copied to the - * vCPU) is also gated by GDB's run control. - * - * This is not unreasonable as most of the time debugging kernels you - * never know which core will eventually execute your function. - */ - -typedef struct { - uint64_t bcr; - uint64_t bvr; -} HWBreakpoint; - -/* The watchpoint registers can cover more area than the requested - * watchpoint so we need to store the additional information - * somewhere. We also need to supply a CPUWatchpoint to the GDB stub - * when the watchpoint is hit. - */ -typedef struct { - uint64_t wcr; - uint64_t wvr; - CPUWatchpoint details; -} HWWatchpoint; - -/* Maximum and current break/watch point counts */ -int max_hw_bps, max_hw_wps; -GArray *hw_breakpoints, *hw_watchpoints; - -#define cur_hw_wps (hw_watchpoints->len) -#define cur_hw_bps (hw_breakpoints->len) -#define get_hw_bp(i) (&g_array_index(hw_breakpoints, HWBreakpoint, i)) -#define get_hw_wp(i) (&g_array_index(hw_watchpoints, HWWatchpoint, i)) - -/** - * kvm_arm_init_debug() - check for guest debug capabilities - * @cs: CPUState - * - * kvm_check_extension returns the number of debug registers we have - * or 0 if we have none. - * - */ -static void kvm_arm_init_debug(CPUState *cs) -{ - have_guest_debug = kvm_check_extension(cs->kvm_state, - KVM_CAP_SET_GUEST_DEBUG); - - max_hw_wps = kvm_check_extension(cs->kvm_state, KVM_CAP_GUEST_DEBUG_HW_WPS); - hw_watchpoints = g_array_sized_new(true, true, - sizeof(HWWatchpoint), max_hw_wps); - - max_hw_bps = kvm_check_extension(cs->kvm_state, KVM_CAP_GUEST_DEBUG_HW_BPS); - hw_breakpoints = g_array_sized_new(true, true, - sizeof(HWBreakpoint), max_hw_bps); - return; -} - -/** - * insert_hw_breakpoint() - * @addr: address of breakpoint - * - * See ARM ARM D2.9.1 for details but here we are only going to create - * simple un-linked breakpoints (i.e. we don't chain breakpoints - * together to match address and context or vmid). The hardware is - * capable of fancier matching but that will require exposing that - * fanciness to GDB's interface - * - * DBGBCR_EL1, Debug Breakpoint Control Registers - * - * 31 24 23 20 19 16 15 14 13 12 9 8 5 4 3 2 1 0 - * +------+------+-------+-----+----+------+-----+------+-----+---+ - * | RES0 | BT | LBN | SSC | HMC| RES0 | BAS | RES0 | PMC | E | - * +------+------+-------+-----+----+------+-----+------+-----+---+ - * - * BT: Breakpoint type (0 = unlinked address match) - * LBN: Linked BP number (0 = unused) - * SSC/HMC/PMC: Security, Higher and Priv access control (Table D-12) - * BAS: Byte Address Select (RES1 for AArch64) - * E: Enable bit - * - * DBGBVR_EL1, Debug Breakpoint Value Registers - * - * 63 53 52 49 48 2 1 0 - * +------+-----------+----------+-----+ - * | RESS | VA[52:49] | VA[48:2] | 0 0 | - * +------+-----------+----------+-----+ - * - * Depending on the addressing mode bits the top bits of the register - * are a sign extension of the highest applicable VA bit. Some - * versions of GDB don't do it correctly so we ensure they are correct - * here so future PC comparisons will work properly. - */ - -static int insert_hw_breakpoint(target_ulong addr) -{ - HWBreakpoint brk = { - .bcr = 0x1, /* BCR E=1, enable */ - .bvr = sextract64(addr, 0, 53) - }; - - if (cur_hw_bps >= max_hw_bps) { - return -ENOBUFS; - } - - brk.bcr = deposit32(brk.bcr, 1, 2, 0x3); /* PMC = 11 */ - brk.bcr = deposit32(brk.bcr, 5, 4, 0xf); /* BAS = RES1 */ - - g_array_append_val(hw_breakpoints, brk); - - return 0; -} - -/** - * delete_hw_breakpoint() - * @pc: address of breakpoint - * - * Delete a breakpoint and shuffle any above down - */ - -static int delete_hw_breakpoint(target_ulong pc) -{ - int i; - for (i = 0; i < hw_breakpoints->len; i++) { - HWBreakpoint *brk = get_hw_bp(i); - if (brk->bvr == pc) { - g_array_remove_index(hw_breakpoints, i); - return 0; - } - } - return -ENOENT; -} - -/** - * insert_hw_watchpoint() - * @addr: address of watch point - * @len: size of area - * @type: type of watch point - * - * See ARM ARM D2.10. As with the breakpoints we can do some advanced - * stuff if we want to. The watch points can be linked with the break - * points above to make them context aware. However for simplicity - * currently we only deal with simple read/write watch points. - * - * D7.3.11 DBGWCR_EL1, Debug Watchpoint Control Registers - * - * 31 29 28 24 23 21 20 19 16 15 14 13 12 5 4 3 2 1 0 - * +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+ - * | RES0 | MASK | RES0 | WT | LBN | SSC | HMC | BAS | LSC | PAC | E | - * +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+ - * - * MASK: num bits addr mask (0=none,01/10=res,11=3 bits (8 bytes)) - * WT: 0 - unlinked, 1 - linked (not currently used) - * LBN: Linked BP number (not currently used) - * SSC/HMC/PAC: Security, Higher and Priv access control (Table D2-11) - * BAS: Byte Address Select - * LSC: Load/Store control (01: load, 10: store, 11: both) - * E: Enable - * - * The bottom 2 bits of the value register are masked. Therefore to - * break on any sizes smaller than an unaligned word you need to set - * MASK=0, BAS=bit per byte in question. For larger regions (^2) you - * need to ensure you mask the address as required and set BAS=0xff - */ - -static int insert_hw_watchpoint(target_ulong addr, - target_ulong len, int type) -{ - HWWatchpoint wp = { - .wcr = R_DBGWCR_E_MASK, /* E=1, enable */ - .wvr = addr & (~0x7ULL), - .details = { .vaddr = addr, .len = len } - }; - - if (cur_hw_wps >= max_hw_wps) { - return -ENOBUFS; - } - - /* - * HMC=0 SSC=0 PAC=3 will hit EL0 or EL1, any security state, - * valid whether EL3 is implemented or not - */ - wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, PAC, 3); - - switch (type) { - case GDB_WATCHPOINT_READ: - wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 1); - wp.details.flags = BP_MEM_READ; - break; - case GDB_WATCHPOINT_WRITE: - wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 2); - wp.details.flags = BP_MEM_WRITE; - break; - case GDB_WATCHPOINT_ACCESS: - wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 3); - wp.details.flags = BP_MEM_ACCESS; - break; - default: - g_assert_not_reached(); - break; - } - if (len <= 8) { - /* we align the address and set the bits in BAS */ - int off = addr & 0x7; - int bas = (1 << len) - 1; - - wp.wcr = deposit32(wp.wcr, 5 + off, 8 - off, bas); - } else { - /* For ranges above 8 bytes we need to be a power of 2 */ - if (is_power_of_2(len)) { - int bits = ctz64(len); - - wp.wvr &= ~((1 << bits) - 1); - wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, MASK, bits); - wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, BAS, 0xff); - } else { - return -ENOBUFS; - } - } - - g_array_append_val(hw_watchpoints, wp); - return 0; -} - - -static bool check_watchpoint_in_range(int i, target_ulong addr) -{ - HWWatchpoint *wp = get_hw_wp(i); - uint64_t addr_top, addr_bottom = wp->wvr; - int bas = extract32(wp->wcr, 5, 8); - int mask = extract32(wp->wcr, 24, 4); - - if (mask) { - addr_top = addr_bottom + (1 << mask); - } else { - /* BAS must be contiguous but can offset against the base - * address in DBGWVR */ - addr_bottom = addr_bottom + ctz32(bas); - addr_top = addr_bottom + clo32(bas); - } - - if (addr >= addr_bottom && addr <= addr_top) { - return true; - } - - return false; -} - -/** - * delete_hw_watchpoint() - * @addr: address of breakpoint - * - * Delete a breakpoint and shuffle any above down - */ - -static int delete_hw_watchpoint(target_ulong addr, - target_ulong len, int type) -{ - int i; - for (i = 0; i < cur_hw_wps; i++) { - if (check_watchpoint_in_range(i, addr)) { - g_array_remove_index(hw_watchpoints, i); - return 0; - } - } - return -ENOENT; -} - - -int kvm_arch_insert_hw_breakpoint(target_ulong addr, - target_ulong len, int type) -{ - switch (type) { - case GDB_BREAKPOINT_HW: - return insert_hw_breakpoint(addr); - break; - case GDB_WATCHPOINT_READ: - case GDB_WATCHPOINT_WRITE: - case GDB_WATCHPOINT_ACCESS: - return insert_hw_watchpoint(addr, len, type); - default: - return -ENOSYS; - } -} - -int kvm_arch_remove_hw_breakpoint(target_ulong addr, - target_ulong len, int type) -{ - switch (type) { - case GDB_BREAKPOINT_HW: - return delete_hw_breakpoint(addr); - case GDB_WATCHPOINT_READ: - case GDB_WATCHPOINT_WRITE: - case GDB_WATCHPOINT_ACCESS: - return delete_hw_watchpoint(addr, len, type); - default: - return -ENOSYS; - } -} - - -void kvm_arch_remove_all_hw_breakpoints(void) -{ - if (cur_hw_wps > 0) { - g_array_remove_range(hw_watchpoints, 0, cur_hw_wps); - } - if (cur_hw_bps > 0) { - g_array_remove_range(hw_breakpoints, 0, cur_hw_bps); - } -} - -void kvm_arm_copy_hw_debug_data(struct kvm_guest_debug_arch *ptr) -{ - int i; - memset(ptr, 0, sizeof(struct kvm_guest_debug_arch)); - - for (i = 0; i < max_hw_wps; i++) { - HWWatchpoint *wp = get_hw_wp(i); - ptr->dbg_wcr[i] = wp->wcr; - ptr->dbg_wvr[i] = wp->wvr; - } - for (i = 0; i < max_hw_bps; i++) { - HWBreakpoint *bp = get_hw_bp(i); - ptr->dbg_bcr[i] = bp->bcr; - ptr->dbg_bvr[i] = bp->bvr; - } -} - -bool kvm_arm_hw_debug_active(CPUState *cs) -{ - return ((cur_hw_wps > 0) || (cur_hw_bps > 0)); -} - -static bool find_hw_breakpoint(CPUState *cpu, target_ulong pc) -{ - int i; - - for (i = 0; i < cur_hw_bps; i++) { - HWBreakpoint *bp = get_hw_bp(i); - if (bp->bvr == pc) { - return true; - } - } - return false; -} - -static CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr) -{ - int i; - - for (i = 0; i < cur_hw_wps; i++) { - if (check_watchpoint_in_range(i, addr)) { - return &get_hw_wp(i)->details; - } - } - return NULL; -} - -static bool kvm_arm_set_device_attr(CPUState *cs, struct kvm_device_attr *attr, - const char *name) -{ - int err; - - err = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, attr); - if (err != 0) { - error_report("%s: KVM_HAS_DEVICE_ATTR: %s", name, strerror(-err)); - return false; - } - - err = kvm_vcpu_ioctl(cs, KVM_SET_DEVICE_ATTR, attr); - if (err != 0) { - error_report("%s: KVM_SET_DEVICE_ATTR: %s", name, strerror(-err)); - return false; - } - - return true; -} - -void kvm_arm_pmu_init(CPUState *cs) -{ - struct kvm_device_attr attr = { - .group = KVM_ARM_VCPU_PMU_V3_CTRL, - .attr = KVM_ARM_VCPU_PMU_V3_INIT, - }; - - if (!ARM_CPU(cs)->has_pmu) { - return; - } - if (!kvm_arm_set_device_attr(cs, &attr, "PMU")) { - error_report("failed to init PMU"); - abort(); - } -} - -void kvm_arm_pmu_set_irq(CPUState *cs, int irq) -{ - struct kvm_device_attr attr = { - .group = KVM_ARM_VCPU_PMU_V3_CTRL, - .addr = (intptr_t)&irq, - .attr = KVM_ARM_VCPU_PMU_V3_IRQ, - }; - - if (!ARM_CPU(cs)->has_pmu) { - return; - } - if (!kvm_arm_set_device_attr(cs, &attr, "PMU")) { - error_report("failed to set irq for PMU"); - abort(); - } -} - -void kvm_arm_pvtime_init(CPUState *cs, uint64_t ipa) -{ - struct kvm_device_attr attr = { - .group = KVM_ARM_VCPU_PVTIME_CTRL, - .attr = KVM_ARM_VCPU_PVTIME_IPA, - .addr = (uint64_t)&ipa, - }; - - if (ARM_CPU(cs)->kvm_steal_time == ON_OFF_AUTO_OFF) { - return; - } - if (!kvm_arm_set_device_attr(cs, &attr, "PVTIME IPA")) { - error_report("failed to init PVTIME IPA"); - abort(); - } -} - -static int read_sys_reg32(int fd, uint32_t *pret, uint64_t id) -{ - uint64_t ret; - struct kvm_one_reg idreg = { .id = id, .addr = (uintptr_t)&ret }; - int err; - - assert((id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64); - err = ioctl(fd, KVM_GET_ONE_REG, &idreg); - if (err < 0) { - return -1; - } - *pret = ret; - return 0; -} - -static int read_sys_reg64(int fd, uint64_t *pret, uint64_t id) -{ - struct kvm_one_reg idreg = { .id = id, .addr = (uintptr_t)pret }; - - assert((id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64); - return ioctl(fd, KVM_GET_ONE_REG, &idreg); -} - -static bool kvm_arm_pauth_supported(void) -{ - return (kvm_check_extension(kvm_state, KVM_CAP_ARM_PTRAUTH_ADDRESS) && - kvm_check_extension(kvm_state, KVM_CAP_ARM_PTRAUTH_GENERIC)); -} - -bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) -{ - /* Identify the feature bits corresponding to the host CPU, and - * fill out the ARMHostCPUClass fields accordingly. To do this - * we have to create a scratch VM, create a single CPU inside it, - * and then query that CPU for the relevant ID registers. - */ - int fdarray[3]; - bool sve_supported; - bool pmu_supported = false; - uint64_t features = 0; - uint64_t t; - int err; - - /* Old kernels may not know about the PREFERRED_TARGET ioctl: however - * we know these will only support creating one kind of guest CPU, - * which is its preferred CPU type. Fortunately these old kernels - * support only a very limited number of CPUs. - */ - static const uint32_t cpus_to_try[] = { - KVM_ARM_TARGET_AEM_V8, - KVM_ARM_TARGET_FOUNDATION_V8, - KVM_ARM_TARGET_CORTEX_A57, - QEMU_KVM_ARM_TARGET_NONE - }; - /* - * target = -1 informs kvm_arm_create_scratch_host_vcpu() - * to use the preferred target - */ - struct kvm_vcpu_init init = { .target = -1, }; - - /* - * Ask for Pointer Authentication if supported. We can't play the - * SVE trick of synthesising the ID reg as KVM won't tell us - * whether we have the architected or IMPDEF version of PAuth, so - * we have to use the actual ID regs. - */ - if (kvm_arm_pauth_supported()) { - init.features[0] |= (1 << KVM_ARM_VCPU_PTRAUTH_ADDRESS | - 1 << KVM_ARM_VCPU_PTRAUTH_GENERIC); - } - - if (kvm_arm_pmu_supported()) { - init.features[0] |= 1 << KVM_ARM_VCPU_PMU_V3; - pmu_supported = true; - } - - if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) { - return false; - } - - ahcf->target = init.target; - ahcf->dtb_compatible = "arm,arm-v8"; - - err = read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64pfr0, - ARM64_SYS_REG(3, 0, 0, 4, 0)); - if (unlikely(err < 0)) { - /* - * Before v4.15, the kernel only exposed a limited number of system - * registers, not including any of the interesting AArch64 ID regs. - * For the most part we could leave these fields as zero with minimal - * effect, since this does not affect the values seen by the guest. - * - * However, it could cause problems down the line for QEMU, - * so provide a minimal v8.0 default. - * - * ??? Could read MIDR and use knowledge from cpu64.c. - * ??? Could map a page of memory into our temp guest and - * run the tiniest of hand-crafted kernels to extract - * the values seen by the guest. - * ??? Either of these sounds like too much effort just - * to work around running a modern host kernel. - */ - ahcf->isar.id_aa64pfr0 = 0x00000011; /* EL1&0, AArch64 only */ - err = 0; - } 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_aa64smfr0, - ARM64_SYS_REG(3, 0, 0, 4, 5)); - 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, - ARM64_SYS_REG(3, 0, 0, 6, 1)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr0, - 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, - * the AArch32 sysregs are present to be read, but will - * return UNKNOWN values. This is neither better nor worse - * 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_pfr0, - ARM64_SYS_REG(3, 0, 0, 1, 0)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr1, - ARM64_SYS_REG(3, 0, 0, 1, 1)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr2, - ARM64_SYS_REG(3, 0, 0, 3, 4)); - 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, - ARM64_SYS_REG(3, 0, 0, 2, 1)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar2, - ARM64_SYS_REG(3, 0, 0, 2, 2)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar3, - ARM64_SYS_REG(3, 0, 0, 2, 3)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar4, - 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)); - - err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr0, - ARM64_SYS_REG(3, 0, 0, 3, 0)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr1, - 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; - } - - if (pmu_supported) { - /* PMCR_EL0 is only accessible if the vCPU has feature PMU_V3 */ - err |= read_sys_reg64(fdarray[2], &ahcf->isar.reset_pmcr_el0, - ARM64_SYS_REG(3, 3, 9, 12, 0)); - } - } - - sve_supported = ioctl(fdarray[0], KVM_CHECK_EXTENSION, KVM_CAP_ARM_SVE) > 0; - - /* Add feature bits that can't appear until after VCPU init. */ - if (sve_supported) { - t = ahcf->isar.id_aa64pfr0; - t = FIELD_DP64(t, ID_AA64PFR0, SVE, 1); - ahcf->isar.id_aa64pfr0 = t; - - /* - * There is a range of kernels between kernel commit 73433762fcae - * and f81cb2c3ad41 which have a bug where the kernel doesn't expose - * SYS_ID_AA64ZFR0_EL1 via the ONE_REG API unless the VM has enabled - * SVE support, so we only read it here, rather than together with all - * the other ID registers earlier. - */ - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64zfr0, - ARM64_SYS_REG(3, 0, 0, 4, 4)); - } - - kvm_arm_destroy_scratch_host_vcpu(fdarray); - - if (err < 0) { - return false; - } - - /* - * We can assume any KVM supporting CPU is at least a v8 - * with VFPv4+Neon; this in turn implies most of the other - * feature bits. - */ - features |= 1ULL << ARM_FEATURE_V8; - features |= 1ULL << ARM_FEATURE_NEON; - features |= 1ULL << ARM_FEATURE_AARCH64; - features |= 1ULL << ARM_FEATURE_PMU; - features |= 1ULL << ARM_FEATURE_GENERIC_TIMER; - - ahcf->features = features; - - return true; -} - -void kvm_arm_steal_time_finalize(ARMCPU *cpu, Error **errp) -{ - bool has_steal_time = kvm_arm_steal_time_supported(); - - if (cpu->kvm_steal_time == ON_OFF_AUTO_AUTO) { - if (!has_steal_time || !arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { - cpu->kvm_steal_time = ON_OFF_AUTO_OFF; - } else { - cpu->kvm_steal_time = ON_OFF_AUTO_ON; - } - } else if (cpu->kvm_steal_time == ON_OFF_AUTO_ON) { - if (!has_steal_time) { - error_setg(errp, "'kvm-steal-time' cannot be enabled " - "on this host"); - return; - } else if (!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { - /* - * DEN0057A chapter 2 says "This specification only covers - * systems in which the Execution state of the hypervisor - * as well as EL1 of virtual machines is AArch64.". And, - * to ensure that, the smc/hvc calls are only specified as - * smc64/hvc64. - */ - error_setg(errp, "'kvm-steal-time' cannot be enabled " - "for AArch32 guests"); - return; - } - } -} - -bool kvm_arm_aarch32_supported(void) -{ - return kvm_check_extension(kvm_state, KVM_CAP_ARM_EL1_32BIT); -} - -bool kvm_arm_sve_supported(void) -{ - return kvm_check_extension(kvm_state, KVM_CAP_ARM_SVE); -} - -bool kvm_arm_steal_time_supported(void) -{ - return kvm_check_extension(kvm_state, KVM_CAP_STEAL_TIME); -} - -QEMU_BUILD_BUG_ON(KVM_ARM64_SVE_VQ_MIN != 1); - -uint32_t kvm_arm_sve_get_vls(CPUState *cs) -{ - /* Only call this function if kvm_arm_sve_supported() returns true. */ - static uint64_t vls[KVM_ARM64_SVE_VLS_WORDS]; - static bool probed; - uint32_t vq = 0; - int i; - - /* - * KVM ensures all host CPUs support the same set of vector lengths. - * So we only need to create the scratch VCPUs once and then cache - * the results. - */ - if (!probed) { - struct kvm_vcpu_init init = { - .target = -1, - .features[0] = (1 << KVM_ARM_VCPU_SVE), - }; - struct kvm_one_reg reg = { - .id = KVM_REG_ARM64_SVE_VLS, - .addr = (uint64_t)&vls[0], - }; - int fdarray[3], ret; - - probed = true; - - if (!kvm_arm_create_scratch_host_vcpu(NULL, fdarray, &init)) { - error_report("failed to create scratch VCPU with SVE enabled"); - abort(); - } - ret = ioctl(fdarray[2], KVM_GET_ONE_REG, ®); - kvm_arm_destroy_scratch_host_vcpu(fdarray); - if (ret) { - error_report("failed to get KVM_REG_ARM64_SVE_VLS: %s", - strerror(errno)); - abort(); - } - - for (i = KVM_ARM64_SVE_VLS_WORDS - 1; i >= 0; --i) { - if (vls[i]) { - vq = 64 - clz64(vls[i]) + i * 64; - break; - } - } - if (vq > ARM_MAX_VQ) { - warn_report("KVM supports vector lengths larger than " - "QEMU can enable"); - vls[0] &= MAKE_64BIT_MASK(0, ARM_MAX_VQ); - } - } - - return vls[0]; -} - -static int kvm_arm_sve_set_vls(CPUState *cs) -{ - ARMCPU *cpu = ARM_CPU(cs); - uint64_t vls[KVM_ARM64_SVE_VLS_WORDS] = { cpu->sve_vq_map }; - struct kvm_one_reg reg = { - .id = KVM_REG_ARM64_SVE_VLS, - .addr = (uint64_t)&vls[0], - }; - - assert(cpu->sve_max_vq <= KVM_ARM64_SVE_VQ_MAX); - - return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); -} - -#define ARM_CPU_ID_MPIDR 3, 0, 0, 0, 5 - -int kvm_arch_init_vcpu(CPUState *cs) -{ - int ret; - uint64_t mpidr; - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - uint64_t psciver; - - if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE || - !object_dynamic_cast(OBJECT(cpu), TYPE_AARCH64_CPU)) { - error_report("KVM is not supported for this guest CPU type"); - 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 (cs->start_powered_off) { - cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_POWER_OFF; - } - if (kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PSCI_0_2)) { - cpu->psci_version = QEMU_PSCI_VERSION_0_2; - cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2; - } - if (!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { - cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_EL1_32BIT; - } - if (!kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PMU_V3)) { - cpu->has_pmu = false; - } - if (cpu->has_pmu) { - cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PMU_V3; - } else { - env->features &= ~(1ULL << ARM_FEATURE_PMU); - } - if (cpu_isar_feature(aa64_sve, cpu)) { - assert(kvm_arm_sve_supported()); - cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_SVE; - } - if (cpu_isar_feature(aa64_pauth, cpu)) { - cpu->kvm_init_features[0] |= (1 << KVM_ARM_VCPU_PTRAUTH_ADDRESS | - 1 << KVM_ARM_VCPU_PTRAUTH_GENERIC); - } - - /* Do KVM_ARM_VCPU_INIT ioctl */ - ret = kvm_arm_vcpu_init(cs); - if (ret) { - return ret; - } - - if (cpu_isar_feature(aa64_sve, cpu)) { - ret = kvm_arm_sve_set_vls(cs); - if (ret) { - return ret; - } - ret = kvm_arm_vcpu_finalize(cs, KVM_ARM_VCPU_SVE); - if (ret) { - return ret; - } - } - - /* - * KVM reports the exact PSCI version it is implementing via a - * special sysreg. If it is present, use its contents to determine - * what to report to the guest in the dtb (it is the PSCI version, - * in the same 15-bits major 16-bits minor format that PSCI_VERSION - * returns). - */ - if (!kvm_get_one_reg(cs, KVM_REG_ARM_PSCI_VERSION, &psciver)) { - cpu->psci_version = psciver; - } - - /* - * When KVM is in use, PSCI is emulated in-kernel and not by qemu. - * Currently KVM has its own idea about MPIDR assignment, so we - * override our defaults with what we get from KVM. - */ - ret = kvm_get_one_reg(cs, ARM64_SYS_REG(ARM_CPU_ID_MPIDR), &mpidr); - if (ret) { - return ret; - } - cpu->mp_affinity = mpidr & ARM64_AFFINITY_MASK; - - kvm_arm_init_debug(cs); - - /* Check whether user space can specify guest syndrome value */ - kvm_arm_init_serror_injection(cs); - - return kvm_arm_init_cpreg_list(cpu); -} - -int kvm_arch_destroy_vcpu(CPUState *cs) -{ - return 0; -} - -bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx) -{ - /* Return true if the regidx is a register we should synchronize - * via the cpreg_tuples array (ie is not a core or sve reg that - * we sync by hand in kvm_arch_get/put_registers()) - */ - switch (regidx & KVM_REG_ARM_COPROC_MASK) { - case KVM_REG_ARM_CORE: - case KVM_REG_ARM64_SVE: - return false; - default: - return true; - } -} - -typedef struct CPRegStateLevel { - uint64_t regidx; - int level; -} CPRegStateLevel; - -/* All system registers not listed in the following table are assumed to be - * of the level KVM_PUT_RUNTIME_STATE. If a register should be written less - * often, you must add it to this table with a state of either - * KVM_PUT_RESET_STATE or KVM_PUT_FULL_STATE. - */ -static const CPRegStateLevel non_runtime_cpregs[] = { - { KVM_REG_ARM_TIMER_CNT, KVM_PUT_FULL_STATE }, -}; - -int kvm_arm_cpreg_level(uint64_t regidx) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(non_runtime_cpregs); i++) { - const CPRegStateLevel *l = &non_runtime_cpregs[i]; - if (l->regidx == regidx) { - return l->level; - } - } - - return KVM_PUT_RUNTIME_STATE; -} - -/* Callers must hold the iothread mutex lock */ -static void kvm_inject_arm_sea(CPUState *c) -{ - ARMCPU *cpu = ARM_CPU(c); - CPUARMState *env = &cpu->env; - uint32_t esr; - bool same_el; - - c->exception_index = EXCP_DATA_ABORT; - env->exception.target_el = 1; - - /* - * Set the DFSC to synchronous external abort and set FnV to not valid, - * this will tell guest the FAR_ELx is UNKNOWN for this abort. - */ - same_el = arm_current_el(env) == env->exception.target_el; - esr = syn_data_abort_no_iss(same_el, 1, 0, 0, 0, 0, 0x10); - - env->exception.syndrome = esr; - - arm_cpu_do_interrupt(c); -} - -#define AARCH64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ - KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) - -#define AARCH64_SIMD_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U128 | \ - KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) - -#define AARCH64_SIMD_CTRL_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U32 | \ - KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) - -static int kvm_arch_put_fpsimd(CPUState *cs) -{ - CPUARMState *env = &ARM_CPU(cs)->env; - struct kvm_one_reg reg; - int i, ret; - - for (i = 0; i < 32; i++) { - uint64_t *q = aa64_vfp_qreg(env, i); -#if HOST_BIG_ENDIAN - uint64_t fp_val[2] = { q[1], q[0] }; - reg.addr = (uintptr_t)fp_val; -#else - reg.addr = (uintptr_t)q; -#endif - reg.id = AARCH64_SIMD_CORE_REG(fp_regs.vregs[i]); - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; - } - } - - return 0; -} - -/* - * 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 - * code the slice index to zero for now as it's unlikely we'll need more than - * one slice for quite some time. - */ -static int kvm_arch_put_sve(CPUState *cs) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - uint64_t tmp[ARM_MAX_VQ * 2]; - uint64_t *r; - struct kvm_one_reg reg; - int n, ret; - - for (n = 0; n < KVM_ARM64_SVE_NUM_ZREGS; ++n) { - r = sve_bswap64(tmp, &env->vfp.zregs[n].d[0], cpu->sve_max_vq * 2); - reg.addr = (uintptr_t)r; - reg.id = KVM_REG_ARM64_SVE_ZREG(n, 0); - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; - } - } - - for (n = 0; n < KVM_ARM64_SVE_NUM_PREGS; ++n) { - r = sve_bswap64(tmp, r = &env->vfp.pregs[n].p[0], - DIV_ROUND_UP(cpu->sve_max_vq * 2, 8)); - reg.addr = (uintptr_t)r; - reg.id = KVM_REG_ARM64_SVE_PREG(n, 0); - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; - } - } - - r = sve_bswap64(tmp, &env->vfp.pregs[FFR_PRED_NUM].p[0], - DIV_ROUND_UP(cpu->sve_max_vq * 2, 8)); - reg.addr = (uintptr_t)r; - reg.id = KVM_REG_ARM64_SVE_FFR(0); - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; - } - - return 0; -} - -int kvm_arch_put_registers(CPUState *cs, int level) -{ - struct kvm_one_reg reg; - uint64_t val; - uint32_t fpr; - int i, ret; - unsigned int el; - - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - - /* If we are in AArch32 mode then we need to copy the AArch32 regs to the - * AArch64 registers before pushing them out to 64-bit KVM. - */ - if (!is_a64(env)) { - aarch64_sync_32_to_64(env); - } - - for (i = 0; i < 31; i++) { - reg.id = AARCH64_CORE_REG(regs.regs[i]); - reg.addr = (uintptr_t) &env->xregs[i]; - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; - } - } - - /* KVM puts SP_EL0 in regs.sp and SP_EL1 in regs.sp_el1. On the - * QEMU side we keep the current SP in xregs[31] as well. - */ - aarch64_save_sp(env, 1); - - reg.id = AARCH64_CORE_REG(regs.sp); - reg.addr = (uintptr_t) &env->sp_el[0]; - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; - } - - reg.id = AARCH64_CORE_REG(sp_el1); - reg.addr = (uintptr_t) &env->sp_el[1]; - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; - } - - /* Note that KVM thinks pstate is 64 bit but we use a uint32_t */ - if (is_a64(env)) { - val = pstate_read(env); - } else { - val = cpsr_read(env); - } - reg.id = AARCH64_CORE_REG(regs.pstate); - reg.addr = (uintptr_t) &val; - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; - } - - reg.id = AARCH64_CORE_REG(regs.pc); - reg.addr = (uintptr_t) &env->pc; - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; - } - - reg.id = AARCH64_CORE_REG(elr_el1); - reg.addr = (uintptr_t) &env->elr_el[1]; - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; - } - - /* Saved Program State Registers - * - * Before we restore from the banked_spsr[] array we need to - * ensure that any modifications to env->spsr are correctly - * reflected in the banks. - */ - el = arm_current_el(env); - if (el > 0 && !is_a64(env)) { - i = bank_number(env->uncached_cpsr & CPSR_M); - env->banked_spsr[i] = env->spsr; - } - - /* KVM 0-4 map to QEMU banks 1-5 */ - for (i = 0; i < KVM_NR_SPSR; i++) { - reg.id = AARCH64_CORE_REG(spsr[i]); - reg.addr = (uintptr_t) &env->banked_spsr[i + 1]; - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; - } - } - - if (cpu_isar_feature(aa64_sve, cpu)) { - ret = kvm_arch_put_sve(cs); - } else { - ret = kvm_arch_put_fpsimd(cs); - } - if (ret) { - return ret; - } - - reg.addr = (uintptr_t)(&fpr); - fpr = vfp_get_fpsr(env); - reg.id = AARCH64_SIMD_CTRL_REG(fp_regs.fpsr); - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; - } - - reg.addr = (uintptr_t)(&fpr); - fpr = vfp_get_fpcr(env); - reg.id = AARCH64_SIMD_CTRL_REG(fp_regs.fpcr); - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); - 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; -} - -static int kvm_arch_get_fpsimd(CPUState *cs) -{ - CPUARMState *env = &ARM_CPU(cs)->env; - struct kvm_one_reg reg; - int i, ret; - - for (i = 0; i < 32; i++) { - uint64_t *q = aa64_vfp_qreg(env, i); - reg.id = AARCH64_SIMD_CORE_REG(fp_regs.vregs[i]); - reg.addr = (uintptr_t)q; - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); - if (ret) { - return ret; - } else { -#if HOST_BIG_ENDIAN - uint64_t t; - t = q[0], q[0] = q[1], q[1] = t; -#endif - } - } - - return 0; -} - -/* - * 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 - * code the slice index to zero for now as it's unlikely we'll need more than - * one slice for quite some time. - */ -static int kvm_arch_get_sve(CPUState *cs) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - struct kvm_one_reg reg; - uint64_t *r; - int n, ret; - - for (n = 0; n < KVM_ARM64_SVE_NUM_ZREGS; ++n) { - r = &env->vfp.zregs[n].d[0]; - reg.addr = (uintptr_t)r; - reg.id = KVM_REG_ARM64_SVE_ZREG(n, 0); - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); - if (ret) { - return ret; - } - sve_bswap64(r, r, cpu->sve_max_vq * 2); - } - - for (n = 0; n < KVM_ARM64_SVE_NUM_PREGS; ++n) { - r = &env->vfp.pregs[n].p[0]; - reg.addr = (uintptr_t)r; - reg.id = KVM_REG_ARM64_SVE_PREG(n, 0); - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); - if (ret) { - return ret; - } - sve_bswap64(r, r, DIV_ROUND_UP(cpu->sve_max_vq * 2, 8)); - } - - r = &env->vfp.pregs[FFR_PRED_NUM].p[0]; - reg.addr = (uintptr_t)r; - reg.id = KVM_REG_ARM64_SVE_FFR(0); - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); - if (ret) { - return ret; - } - sve_bswap64(r, r, DIV_ROUND_UP(cpu->sve_max_vq * 2, 8)); - - return 0; -} - -int kvm_arch_get_registers(CPUState *cs) -{ - struct kvm_one_reg reg; - uint64_t val; - unsigned int el; - uint32_t fpr; - int i, ret; - - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - - for (i = 0; i < 31; i++) { - reg.id = AARCH64_CORE_REG(regs.regs[i]); - reg.addr = (uintptr_t) &env->xregs[i]; - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); - if (ret) { - return ret; - } - } - - reg.id = AARCH64_CORE_REG(regs.sp); - reg.addr = (uintptr_t) &env->sp_el[0]; - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); - if (ret) { - return ret; - } - - reg.id = AARCH64_CORE_REG(sp_el1); - reg.addr = (uintptr_t) &env->sp_el[1]; - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); - if (ret) { - return ret; - } - - reg.id = AARCH64_CORE_REG(regs.pstate); - reg.addr = (uintptr_t) &val; - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); - if (ret) { - return ret; - } - - env->aarch64 = ((val & PSTATE_nRW) == 0); - if (is_a64(env)) { - pstate_write(env, val); - } else { - cpsr_write(env, val, 0xffffffff, CPSRWriteRaw); - } - - /* KVM puts SP_EL0 in regs.sp and SP_EL1 in regs.sp_el1. On the - * QEMU side we keep the current SP in xregs[31] as well. - */ - aarch64_restore_sp(env, 1); - - reg.id = AARCH64_CORE_REG(regs.pc); - reg.addr = (uintptr_t) &env->pc; - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); - if (ret) { - return ret; - } - - /* If we are in AArch32 mode then we need to sync the AArch32 regs with the - * incoming AArch64 regs received from 64-bit KVM. - * We must perform this after all of the registers have been acquired from - * the kernel. - */ - if (!is_a64(env)) { - aarch64_sync_64_to_32(env); - } - - reg.id = AARCH64_CORE_REG(elr_el1); - reg.addr = (uintptr_t) &env->elr_el[1]; - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); - if (ret) { - return ret; - } - - /* Fetch the SPSR registers - * - * KVM SPSRs 0-4 map to QEMU banks 1-5 - */ - for (i = 0; i < KVM_NR_SPSR; i++) { - reg.id = AARCH64_CORE_REG(spsr[i]); - reg.addr = (uintptr_t) &env->banked_spsr[i + 1]; - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); - if (ret) { - return ret; - } - } - - el = arm_current_el(env); - if (el > 0 && !is_a64(env)) { - i = bank_number(env->uncached_cpsr & CPSR_M); - env->spsr = env->banked_spsr[i]; - } - - if (cpu_isar_feature(aa64_sve, cpu)) { - ret = kvm_arch_get_sve(cs); - } else { - ret = kvm_arch_get_fpsimd(cs); - } - if (ret) { - return ret; - } - - reg.addr = (uintptr_t)(&fpr); - reg.id = AARCH64_SIMD_CTRL_REG(fp_regs.fpsr); - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); - if (ret) { - return ret; - } - vfp_set_fpsr(env, fpr); - - reg.addr = (uintptr_t)(&fpr); - reg.id = AARCH64_SIMD_CTRL_REG(fp_regs.fpcr); - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); - if (ret) { - return ret; - } - vfp_set_fpcr(env, fpr); - - ret = kvm_get_vcpu_events(cpu); - if (ret) { - return ret; - } - - if (!write_kvmstate_to_list(cpu)) { - return -EINVAL; - } - /* Note that it's OK to have registers which aren't in CPUState, - * so we can ignore a failure return here. - */ - write_list_to_cpustate(cpu); - - kvm_arm_sync_mpstate_to_qemu(cpu); - - /* TODO: other registers */ - return ret; -} - -void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr) -{ - ram_addr_t ram_addr; - hwaddr paddr; - - assert(code == BUS_MCEERR_AR || code == BUS_MCEERR_AO); - - if (acpi_ghes_present() && addr) { - ram_addr = qemu_ram_addr_from_host(addr); - if (ram_addr != RAM_ADDR_INVALID && - kvm_physical_memory_addr_from_host(c->kvm_state, addr, &paddr)) { - kvm_hwpoison_page_add(ram_addr); - /* - * If this is a BUS_MCEERR_AR, we know we have been called - * synchronously from the vCPU thread, so we can easily - * synchronize the state and inject an error. - * - * TODO: we currently don't tell the guest at all about - * BUS_MCEERR_AO. In that case we might either be being - * called synchronously from the vCPU thread, or a bit - * later from the main thread, so doing the injection of - * the error would be more complicated. - */ - if (code == BUS_MCEERR_AR) { - kvm_cpu_synchronize_state(c); - if (!acpi_ghes_record_errors(ACPI_HEST_SRC_ID_SEA, paddr)) { - kvm_inject_arm_sea(c); - } else { - error_report("failed to record the error"); - abort(); - } - } - return; - } - if (code == BUS_MCEERR_AO) { - error_report("Hardware memory error at addr %p for memory used by " - "QEMU itself instead of guest system!", addr); - } - } - - if (code == BUS_MCEERR_AR) { - error_report("Hardware memory error!"); - exit(1); - } -} - -/* C6.6.29 BRK instruction */ -static const uint32_t brk_insn = 0xd4200000; - -int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) -{ - if (have_guest_debug) { - if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) || - cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk_insn, 4, 1)) { - return -EINVAL; - } - return 0; - } else { - error_report("guest debug not supported on this kernel"); - return -EINVAL; - } -} - -int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) -{ - static uint32_t brk; - - if (have_guest_debug) { - if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk, 4, 0) || - brk != brk_insn || - cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 1)) { - return -EINVAL; - } - return 0; - } else { - error_report("guest debug not supported on this kernel"); - return -EINVAL; - } -} - -/* See v8 ARM ARM D7.2.27 ESR_ELx, Exception Syndrome Register - * - * To minimise translating between kernel and user-space the kernel - * ABI just provides user-space with the full exception syndrome - * register value to be decoded in QEMU. - */ - -bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit) -{ - int hsr_ec = syn_get_ec(debug_exit->hsr); - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - - /* Ensure PC is synchronised */ - kvm_cpu_synchronize_state(cs); - - switch (hsr_ec) { - case EC_SOFTWARESTEP: - if (cs->singlestep_enabled) { - return true; - } else { - /* - * The kernel should have suppressed the guest's ability to - * single step at this point so something has gone wrong. - */ - error_report("%s: guest single-step while debugging unsupported" - " (%"PRIx64", %"PRIx32")", - __func__, env->pc, debug_exit->hsr); - return false; - } - break; - case EC_AA64_BKPT: - if (kvm_find_sw_breakpoint(cs, env->pc)) { - return true; - } - break; - case EC_BREAKPOINT: - if (find_hw_breakpoint(cs, env->pc)) { - return true; - } - break; - case EC_WATCHPOINT: - { - CPUWatchpoint *wp = find_hw_watchpoint(cs, debug_exit->far); - if (wp) { - cs->watchpoint_hit = wp; - return true; - } - break; - } - default: - error_report("%s: unhandled debug exit (%"PRIx32", %"PRIx64")", - __func__, debug_exit->hsr, env->pc); - } - - /* If we are not handling the debug exception it must belong to - * the guest. Let's re-use the existing TCG interrupt code to set - * everything up properly. - */ - cs->exception_index = EXCP_BKPT; - env->exception.syndrome = debug_exit->hsr; - env->exception.vaddress = debug_exit->far; - env->exception.target_el = 1; - qemu_mutex_lock_iothread(); - arm_cpu_do_interrupt(cs); - qemu_mutex_unlock_iothread(); - - return false; -} - -#define ARM64_REG_ESR_EL1 ARM64_SYS_REG(3, 0, 5, 2, 0) -#define ARM64_REG_TCR_EL1 ARM64_SYS_REG(3, 0, 2, 0, 2) - -/* - * ESR_EL1 - * ISS encoding - * AARCH64: DFSC, bits [5:0] - * AARCH32: - * TTBCR.EAE == 0 - * FS[4] - DFSR[10] - * FS[3:0] - DFSR[3:0] - * TTBCR.EAE == 1 - * FS, bits [5:0] - */ -#define ESR_DFSC(aarch64, lpae, v) \ - ((aarch64 || (lpae)) ? ((v) & 0x3F) \ - : (((v) >> 6) | ((v) & 0x1F))) - -#define ESR_DFSC_EXTABT(aarch64, lpae) \ - ((aarch64) ? 0x10 : (lpae) ? 0x10 : 0x8) - -bool kvm_arm_verify_ext_dabt_pending(CPUState *cs) -{ - uint64_t dfsr_val; - - if (!kvm_get_one_reg(cs, ARM64_REG_ESR_EL1, &dfsr_val)) { - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - int aarch64_mode = arm_feature(env, ARM_FEATURE_AARCH64); - int lpae = 0; - - if (!aarch64_mode) { - uint64_t ttbcr; - - if (!kvm_get_one_reg(cs, ARM64_REG_TCR_EL1, &ttbcr)) { - lpae = arm_feature(env, ARM_FEATURE_LPAE) - && (ttbcr & TTBCR_EAE); - } - } - /* - * The verification here is based on the DFSC bits - * of the ESR_EL1 reg only - */ - return (ESR_DFSC(aarch64_mode, lpae, dfsr_val) == - ESR_DFSC_EXTABT(aarch64_mode, lpae)); - } - return false; -} diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 99017b635c..cfaa0d9bc7 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -12,38 +12,10 @@ #define QEMU_KVM_ARM_H #include "sysemu/kvm.h" -#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 - * - * Initialize (or reinitialize) the VCPU by invoking the - * KVM_ARM_VCPU_INIT ioctl with the CPU type and feature - * bitmask specified in the CPUState. - * - * Returns: 0 if success else < 0 error code - */ -int kvm_arm_vcpu_init(CPUState *cs); - -/** - * kvm_arm_vcpu_finalize: - * @cs: CPUState - * @feature: feature to finalize - * - * Finalizes the configuration of the specified VCPU feature by - * invoking the KVM_ARM_VCPU_FINALIZE ioctl. Features requiring - * this are documented in the "KVM_ARM_VCPU_FINALIZE" section of - * KVM's API documentation. - * - * Returns: 0 if success else < 0 error code - */ -int kvm_arm_vcpu_finalize(CPUState *cs, int feature); - /** * kvm_arm_register_device: * @mr: memory region for this device @@ -65,37 +37,6 @@ int kvm_arm_vcpu_finalize(CPUState *cs, int feature); void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, uint64_t attr, int dev_fd, uint64_t addr_ormask); -/** - * kvm_arm_init_cpreg_list: - * @cpu: ARMCPU - * - * 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). - * - * Returns: 0 if success, else < 0 error code - */ -int kvm_arm_init_cpreg_list(ARMCPU *cpu); - -/** - * 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 - * by hand using code in kvm_arch_get/put_registers(). - */ -bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx); - -/** - * 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. - */ -int kvm_arm_cpreg_level(uint64_t regidx); - /** * write_list_to_kvmstate: * @cpu: ARMCPU @@ -155,34 +96,6 @@ void kvm_arm_cpu_post_load(ARMCPU *cpu); */ void kvm_arm_reset_vcpu(ARMCPU *cpu); -/** - * kvm_arm_init_serror_injection: - * @cs: CPUState - * - * Check whether KVM can set guest SError syndrome. - */ -void kvm_arm_init_serror_injection(CPUState *cs); - -/** - * kvm_get_vcpu_events: - * @cpu: ARMCPU - * - * Get VCPU related state from kvm. - * - * Returns: 0 if success else < 0 error code - */ -int kvm_get_vcpu_events(ARMCPU *cpu); - -/** - * kvm_put_vcpu_events: - * @cpu: ARMCPU - * - * Put VCPU related state to kvm. - * - * Returns: 0 if success else < 0 error code - */ -int kvm_put_vcpu_events(ARMCPU *cpu); - #ifdef CONFIG_KVM /** * kvm_arm_create_scratch_host_vcpu: @@ -214,37 +127,15 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, */ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray); -/** - * ARMHostCPUFeatures: information about the host CPU (identified - * by asking the host kernel) - */ -typedef struct ARMHostCPUFeatures { - ARMISARegisters isar; - uint64_t features; - uint32_t target; - const char *dtb_compatible; -} ARMHostCPUFeatures; - -/** - * kvm_arm_get_host_cpu_features: - * @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); - /** * kvm_arm_sve_get_vls: - * @cs: CPUState + * @cpu: ARMCPU * * Get all the SVE vector lengths supported by the KVM host, setting * the bits corresponding to their length in quadwords minus one * (vq - 1) up to ARM_MAX_VQ. Return the resulting map. */ -uint32_t kvm_arm_sve_get_vls(CPUState *cs); +uint32_t kvm_arm_sve_get_vls(ARMCPU *cpu); /** * kvm_arm_set_cpu_features_from_host: @@ -257,12 +148,12 @@ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu); /** * kvm_arm_add_vcpu_properties: - * @obj: The CPU object to add the properties to + * @cpu: 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); +void kvm_arm_add_vcpu_properties(ARMCPU *cpu); /** * kvm_arm_steal_time_finalize: @@ -274,14 +165,6 @@ void kvm_arm_add_vcpu_properties(Object *obj); */ void kvm_arm_steal_time_finalize(ARMCPU *cpu, Error **errp); -/** - * kvm_arm_steal_time_supported: - * - * Returns: true if KVM can enable steal time reporting - * and false otherwise. - */ -bool kvm_arm_steal_time_supported(void); - /** * kvm_arm_aarch32_supported: * @@ -315,57 +198,19 @@ bool kvm_arm_sve_supported(void); */ int kvm_arm_get_max_vm_ipa_size(MachineState *ms, bool *fixed_ipa); -/** - * 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: - * @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, bool running, RunState state); - int kvm_arm_vgic_probe(void); -void kvm_arm_pmu_set_irq(CPUState *cs, int irq); -void kvm_arm_pmu_init(CPUState *cs); +void kvm_arm_pmu_init(ARMCPU *cpu); +void kvm_arm_pmu_set_irq(ARMCPU *cpu, int irq); /** * kvm_arm_pvtime_init: - * @cs: CPUState + * @cpu: ARMCPU * @ipa: Per-vcpu guest physical base address of the pvtime structures * * Initializes PVTIME for the VCPU, setting the PVTIME IPA to @ipa. */ -void kvm_arm_pvtime_init(CPUState *cs, uint64_t ipa); +void kvm_arm_pvtime_init(ARMCPU *cpu, uint64_t ipa); int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level); @@ -390,11 +235,6 @@ static inline bool kvm_arm_sve_supported(void) return false; } -static inline bool kvm_arm_steal_time_supported(void) -{ - return false; -} - /* * These functions should never actually be called without KVM support. */ @@ -403,7 +243,7 @@ static inline void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) g_assert_not_reached(); } -static inline void kvm_arm_add_vcpu_properties(Object *obj) +static inline void kvm_arm_add_vcpu_properties(ARMCPU *cpu) { g_assert_not_reached(); } @@ -418,17 +258,17 @@ static inline int kvm_arm_vgic_probe(void) g_assert_not_reached(); } -static inline void kvm_arm_pmu_set_irq(CPUState *cs, int irq) +static inline void kvm_arm_pmu_set_irq(ARMCPU *cpu, int irq) { g_assert_not_reached(); } -static inline void kvm_arm_pmu_init(CPUState *cs) +static inline void kvm_arm_pmu_init(ARMCPU *cpu) { g_assert_not_reached(); } -static inline void kvm_arm_pvtime_init(CPUState *cs, uint64_t ipa) +static inline void kvm_arm_pvtime_init(ARMCPU *cpu, uint64_t ipa) { g_assert_not_reached(); } @@ -438,93 +278,11 @@ static inline void kvm_arm_steal_time_finalize(ARMCPU *cpu, Error **errp) g_assert_not_reached(); } -static inline uint32_t kvm_arm_sve_get_vls(CPUState *cs) +static inline uint32_t kvm_arm_sve_get_vls(ARMCPU *cpu) { g_assert_not_reached(); } #endif -static inline const char *gic_class_name(void) -{ - return kvm_irqchip_in_kernel() ? "kvm-arm-gic" : "arm_gic"; -} - -/** - * gicv3_class_name - * - * Return name of GICv3 class to use depending on whether KVM acceleration is - * in use. May throw an error if the chosen implementation is not available. - * - * Returns: class name to use - */ -static inline const char *gicv3_class_name(void) -{ - if (kvm_irqchip_in_kernel()) { - return "kvm-arm-gicv3"; - } else { - if (kvm_enabled()) { - error_report("Userspace GICv3 is not supported with KVM"); - exit(1); - } - return "arm-gicv3"; - } -} - -/** - * kvm_arm_handle_debug: - * @cs: CPUState - * @debug_exit: debug part of the KVM exit structure - * - * Returns: TRUE if the debug exception was handled. - */ -bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit); - -/** - * kvm_arm_hw_debug_active: - * @cs: CPU State - * - * 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); - -/** - * kvm_arm_verify_ext_dabt_pending: - * @cs: CPUState - * - * Verify the fault status code wrt the Ext DABT injection - * - * Returns: true if the fault status code is as expected, false otherwise - */ -bool kvm_arm_verify_ext_dabt_pending(CPUState *cs); - -/** - * its_class_name: - * - * Return the ITS class name to use depending on whether KVM acceleration - * and KVM CAP_SIGNAL_MSI are supported - * - * Returns: class name to use or NULL - */ -static inline const char *its_class_name(void) -{ - if (kvm_irqchip_in_kernel()) { - /* KVM implementation requires this capability */ - return kvm_direct_msi_enabled() ? "arm-its-kvm" : NULL; - } else { - /* Software emulation based model */ - return "arm-gicv3-its"; - } -} - #endif diff --git a/target/arm/machine.c b/target/arm/machine.c index 285e387d2c..b2b39b2475 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -2,9 +2,12 @@ #include "cpu.h" #include "qemu/error-report.h" #include "sysemu/kvm.h" +#include "sysemu/tcg.h" #include "kvm_arm.h" #include "internals.h" +#include "cpu-features.h" #include "migration/cpu.h" +#include "target/arm/gtimer.h" static bool vfp_needed(void *opaque) { @@ -47,7 +50,7 @@ static const VMStateDescription vmstate_vfp = { .version_id = 3, .minimum_version_id = 3, .needed = vfp_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { /* For compatibility, store Qn out of Zn here. */ VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[0].d, ARMCPU, 0, 2), VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[1].d, ARMCPU, 0, 2), @@ -113,7 +116,7 @@ static const VMStateDescription vmstate_iwmmxt = { .version_id = 1, .minimum_version_id = 1, .needed = iwmmxt_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.iwmmxt.regs, ARMCPU, 16), VMSTATE_UINT32_ARRAY(env.iwmmxt.cregs, ARMCPU, 16), VMSTATE_END_OF_LIST() @@ -138,7 +141,7 @@ static const VMStateDescription vmstate_zreg_hi_reg = { .name = "cpu/sve/zreg_hi", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_SUB_ARRAY(d, ARMVectorReg, 2, ARM_MAX_VQ - 2), VMSTATE_END_OF_LIST() } @@ -148,7 +151,7 @@ static const VMStateDescription vmstate_preg_reg = { .name = "cpu/sve/preg", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(p, ARMPredicateReg, 2 * ARM_MAX_VQ / 8), VMSTATE_END_OF_LIST() } @@ -159,7 +162,7 @@ static const VMStateDescription vmstate_sve = { .version_id = 1, .minimum_version_id = 1, .needed = sve_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(env.vfp.zregs, ARMCPU, 32, 0, vmstate_zreg_hi_reg, ARMVectorReg), VMSTATE_STRUCT_ARRAY(env.vfp.pregs, ARMCPU, 17, 0, @@ -167,6 +170,39 @@ static const VMStateDescription vmstate_sve = { VMSTATE_END_OF_LIST() } }; + +static const VMStateDescription vmstate_vreg = { + .name = "vreg", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT64_ARRAY(d, ARMVectorReg, ARM_MAX_VQ * 2), + VMSTATE_END_OF_LIST() + } +}; + +static bool za_needed(void *opaque) +{ + ARMCPU *cpu = opaque; + + /* + * When ZA storage is disabled, its contents are discarded. + * It will be zeroed when ZA storage is re-enabled. + */ + return FIELD_EX64(cpu->env.svcr, SVCR, ZA); +} + +static const VMStateDescription vmstate_za = { + .name = "cpu/sme", + .version_id = 1, + .minimum_version_id = 1, + .needed = za_needed, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT_ARRAY(env.zarray, ARMCPU, ARM_MAX_VQ * 16, 0, + vmstate_vreg, ARMVectorReg), + VMSTATE_END_OF_LIST() + } +}; #endif /* AARCH64 */ static bool serror_needed(void *opaque) @@ -182,7 +218,7 @@ static const VMStateDescription vmstate_serror = { .version_id = 1, .minimum_version_id = 1, .needed = serror_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(env.serror.pending, ARMCPU), VMSTATE_UINT8(env.serror.has_esr, ARMCPU), VMSTATE_UINT64(env.serror.esr, ARMCPU), @@ -200,7 +236,7 @@ static const VMStateDescription vmstate_irq_line_state = { .version_id = 1, .minimum_version_id = 1, .needed = irq_line_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.irq_line_state, ARMCPU), VMSTATE_END_OF_LIST() } @@ -219,7 +255,7 @@ static const VMStateDescription vmstate_m_faultmask_primask = { .version_id = 1, .minimum_version_id = 1, .needed = m_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.v7m.faultmask[M_REG_NS], ARMCPU), VMSTATE_UINT32(env.v7m.primask[M_REG_NS], ARMCPU), VMSTATE_END_OF_LIST() @@ -254,7 +290,7 @@ static const VMStateDescription vmstate_m_csselr = { .version_id = 1, .minimum_version_id = 1, .needed = m_csselr_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(env.v7m.csselr, ARMCPU, M_REG_NUM_BANKS), VMSTATE_VALIDATE("CSSELR is valid", csselr_vmstate_validate), VMSTATE_END_OF_LIST() @@ -266,7 +302,7 @@ static const VMStateDescription vmstate_m_scr = { .version_id = 1, .minimum_version_id = 1, .needed = m_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.v7m.scr[M_REG_NS], ARMCPU), VMSTATE_END_OF_LIST() } @@ -277,7 +313,7 @@ static const VMStateDescription vmstate_m_other_sp = { .version_id = 1, .minimum_version_id = 1, .needed = m_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.v7m.other_sp, ARMCPU), VMSTATE_END_OF_LIST() } @@ -296,7 +332,7 @@ static const VMStateDescription vmstate_m_v8m = { .version_id = 1, .minimum_version_id = 1, .needed = m_v8m_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(env.v7m.msplim, ARMCPU, M_REG_NUM_BANKS), VMSTATE_UINT32_ARRAY(env.v7m.psplim, ARMCPU, M_REG_NUM_BANKS), VMSTATE_END_OF_LIST() @@ -308,7 +344,7 @@ static const VMStateDescription vmstate_m_fp = { .version_id = 1, .minimum_version_id = 1, .needed = vfp_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(env.v7m.fpcar, ARMCPU, M_REG_NUM_BANKS), VMSTATE_UINT32_ARRAY(env.v7m.fpccr, ARMCPU, M_REG_NUM_BANKS), VMSTATE_UINT32_ARRAY(env.v7m.fpdscr, ARMCPU, M_REG_NUM_BANKS), @@ -330,7 +366,7 @@ static const VMStateDescription vmstate_m_mve = { .version_id = 1, .minimum_version_id = 1, .needed = mve_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.v7m.vpr, ARMCPU), VMSTATE_UINT32(env.v7m.ltpsize, ARMCPU), VMSTATE_END_OF_LIST() @@ -342,7 +378,7 @@ static const VMStateDescription vmstate_m = { .version_id = 4, .minimum_version_id = 4, .needed = m_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.v7m.vecbase[M_REG_NS], ARMCPU), VMSTATE_UINT32(env.v7m.basepri[M_REG_NS], ARMCPU), VMSTATE_UINT32(env.v7m.control[M_REG_NS], ARMCPU), @@ -356,7 +392,7 @@ static const VMStateDescription vmstate_m = { VMSTATE_INT32(env.v7m.exception, ARMCPU), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_m_faultmask_primask, &vmstate_m_csselr, &vmstate_m_scr, @@ -381,7 +417,7 @@ static const VMStateDescription vmstate_thumb2ee = { .version_id = 1, .minimum_version_id = 1, .needed = thumb2ee_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.teecr, ARMCPU), VMSTATE_UINT32(env.teehbr, ARMCPU), VMSTATE_END_OF_LIST() @@ -410,7 +446,7 @@ static const VMStateDescription vmstate_pmsav7 = { .version_id = 1, .minimum_version_id = 1, .needed = pmsav7_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VARRAY_UINT32(env.pmsav7.drbar, ARMCPU, pmsav7_dregion, 0, vmstate_info_uint32, uint32_t), VMSTATE_VARRAY_UINT32(env.pmsav7.drsr, ARMCPU, pmsav7_dregion, 0, @@ -439,7 +475,7 @@ static const VMStateDescription vmstate_pmsav7_rnr = { .version_id = 1, .minimum_version_id = 1, .needed = pmsav7_rnr_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.pmsav7.rnr[M_REG_NS], ARMCPU), VMSTATE_END_OF_LIST() } @@ -454,12 +490,36 @@ static bool pmsav8_needed(void *opaque) arm_feature(env, ARM_FEATURE_V8); } +static bool pmsav8r_needed(void *opaque) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + + return arm_feature(env, ARM_FEATURE_PMSA) && + arm_feature(env, ARM_FEATURE_V8) && + !arm_feature(env, ARM_FEATURE_M); +} + +static const VMStateDescription vmstate_pmsav8r = { + .name = "cpu/pmsav8/pmsav8r", + .version_id = 1, + .minimum_version_id = 1, + .needed = pmsav8r_needed, + .fields = (const VMStateField[]) { + VMSTATE_VARRAY_UINT32(env.pmsav8.hprbar, ARMCPU, + pmsav8r_hdregion, 0, vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(env.pmsav8.hprlar, ARMCPU, + pmsav8r_hdregion, 0, vmstate_info_uint32, uint32_t), + VMSTATE_END_OF_LIST() + }, +}; + static const VMStateDescription vmstate_pmsav8 = { .name = "cpu/pmsav8", .version_id = 1, .minimum_version_id = 1, .needed = pmsav8_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VARRAY_UINT32(env.pmsav8.rbar[M_REG_NS], ARMCPU, pmsav7_dregion, 0, vmstate_info_uint32, uint32_t), VMSTATE_VARRAY_UINT32(env.pmsav8.rlar[M_REG_NS], ARMCPU, pmsav7_dregion, @@ -467,6 +527,10 @@ static const VMStateDescription vmstate_pmsav8 = { VMSTATE_UINT32(env.pmsav8.mair0[M_REG_NS], ARMCPU), VMSTATE_UINT32(env.pmsav8.mair1[M_REG_NS], ARMCPU), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_pmsav8r, + NULL } }; @@ -497,7 +561,7 @@ static const VMStateDescription vmstate_m_security = { .version_id = 1, .minimum_version_id = 1, .needed = m_security_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.v7m.secure, ARMCPU), VMSTATE_UINT32(env.v7m.other_ss_msp, ARMCPU), VMSTATE_UINT32(env.v7m.other_ss_psp, ARMCPU), @@ -709,7 +773,7 @@ static int cpu_pre_load(void *opaque) env->irq_line_state = UINT32_MAX; if (!kvm_enabled()) { - pmu_op_start(&cpu->env); + pmu_op_start(env); } return 0; @@ -778,8 +842,19 @@ static int cpu_post_load(void *opaque, int version_id) } } - hw_breakpoint_update_all(cpu); - hw_watchpoint_update_all(cpu); + /* + * Misaligned thumb pc is architecturally impossible. Fail the + * incoming migration. For TCG it would trigger the assert in + * thumb_tr_translate_insn(). + */ + if (!is_a64(env) && env->thumb && (env->regs[15] & 1)) { + return -1; + } + + if (tcg_enabled()) { + hw_breakpoint_update_all(cpu); + hw_watchpoint_update_all(cpu); + } /* * TCG gen_update_fp_context() relies on the invariant that @@ -795,19 +870,13 @@ static int cpu_post_load(void *opaque, int version_id) } } - /* - * Misaligned thumb pc is architecturally impossible. - * We have an assert in thumb_tr_translate_insn to verify this. - * Fail an incoming migrate to avoid this assert. - */ - if (!is_a64(env) && env->thumb && (env->regs[15] & 1)) { - return -1; + if (!kvm_enabled()) { + pmu_op_finish(env); } - if (!kvm_enabled()) { - pmu_op_finish(&cpu->env); + if (tcg_enabled()) { + arm_rebuild_hflags(env); } - arm_rebuild_hflags(&cpu->env); return 0; } @@ -820,7 +889,7 @@ const VMStateDescription vmstate_arm_cpu = { .post_save = cpu_post_save, .pre_load = cpu_pre_load, .post_load = cpu_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(env.regs, ARMCPU, 16), VMSTATE_UINT64_ARRAY(env.xregs, ARMCPU, 32), VMSTATE_UINT64(env.pc, ARMCPU), @@ -869,7 +938,7 @@ const VMStateDescription vmstate_arm_cpu = { }, VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_vfp, &vmstate_iwmmxt, &vmstate_m, @@ -884,6 +953,7 @@ const VMStateDescription vmstate_arm_cpu = { &vmstate_m_security, #ifdef TARGET_AARCH64 &vmstate_sve, + &vmstate_za, #endif &vmstate_serror, &vmstate_irq_line_state, diff --git a/target/arm/meson.build b/target/arm/meson.build index ac571fc45d..2e10464dbb 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -1,67 +1,41 @@ -gen = [ - decodetree.process('sve.decode', extra_args: '--decode=disas_sve'), - decodetree.process('neon-shared.decode', extra_args: '--decode=disas_neon_shared'), - decodetree.process('neon-dp.decode', extra_args: '--decode=disas_neon_dp'), - decodetree.process('neon-ls.decode', extra_args: '--decode=disas_neon_ls'), - decodetree.process('vfp.decode', extra_args: '--decode=disas_vfp'), - decodetree.process('vfp-uncond.decode', extra_args: '--decode=disas_vfp_uncond'), - decodetree.process('m-nocp.decode', extra_args: '--decode=disas_m_nocp'), - decodetree.process('mve.decode', extra_args: '--decode=disas_mve'), - decodetree.process('a32.decode', extra_args: '--static-decode=disas_a32'), - decodetree.process('a32-uncond.decode', extra_args: '--static-decode=disas_a32_uncond'), - decodetree.process('t32.decode', extra_args: '--static-decode=disas_t32'), - decodetree.process('t16.decode', extra_args: ['-w', '16', '--static-decode=disas_t16']), -] - arm_ss = ss.source_set() -arm_ss.add(gen) arm_ss.add(files( 'cpu.c', - 'crypto_helper.c', 'debug_helper.c', 'gdbstub.c', 'helper.c', - 'iwmmxt_helper.c', - 'm_helper.c', - 'mve_helper.c', - 'neon_helper.c', - 'op_helper.c', - 'tlb_helper.c', - 'translate.c', - 'translate-m-nocp.c', - 'translate-mve.c', - 'translate-neon.c', - 'translate-vfp.c', - 'vec_helper.c', 'vfp_helper.c', - 'cpu_tcg.c', )) arm_ss.add(zlib) -arm_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c', 'kvm64.c'), if_false: files('kvm-stub.c')) +arm_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c'), if_false: files('kvm-stub.c')) +arm_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c')) arm_ss.add(when: 'TARGET_AARCH64', if_true: files( 'cpu64.c', 'gdbstub64.c', - 'helper-a64.c', - 'mte_helper.c', - 'pauth_helper.c', - 'sve_helper.c', - 'translate-a64.c', - 'translate-sve.c', )) -arm_softmmu_ss = ss.source_set() -arm_softmmu_ss.add(files( +arm_system_ss = ss.source_set() +arm_system_ss.add(files( 'arch_dump.c', 'arm-powerctl.c', + 'arm-qmp-cmds.c', + 'cortex-regs.c', 'machine.c', - 'monitor.c', - 'psci.c', 'ptw.c', )) +arm_user_ss = ss.source_set() + subdir('hvf') +if 'CONFIG_TCG' in config_all_accel + subdir('tcg') +else + arm_ss.add(files('tcg-stubs.c')) +endif + target_arch += {'arm': arm_ss} -target_softmmu_arch += {'arm': arm_softmmu_ss} +target_system_arch += {'arm': arm_system_ss} +target_user_arch += {'arm': arm_user_ss} diff --git a/target/arm/multiprocessing.h b/target/arm/multiprocessing.h new file mode 100644 index 0000000000..81715d345c --- /dev/null +++ b/target/arm/multiprocessing.h @@ -0,0 +1,16 @@ +/* + * ARM multiprocessor CPU helpers + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef TARGET_ARM_MULTIPROCESSING_H +#define TARGET_ARM_MULTIPROCESSING_H + +#include "target/arm/cpu-qom.h" + +uint64_t arm_cpu_mp_affinity(ARMCPU *cpu); + +#endif diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 4d97a24808..31ae43f60e 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -9,18 +9,80 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/range.h" +#include "qemu/main-loop.h" +#include "exec/exec-all.h" #include "cpu.h" #include "internals.h" +#include "cpu-features.h" #include "idau.h" +#ifdef CONFIG_TCG +# include "tcg/oversized-guest.h" +#endif +typedef struct S1Translate { + /* + * in_mmu_idx : specifies which TTBR, TCR, etc to use for the walk. + * Together with in_space, specifies the architectural translation regime. + */ + ARMMMUIdx in_mmu_idx; + /* + * in_ptw_idx: specifies which mmuidx to use for the actual + * page table descriptor load operations. This will be one of the + * ARMMMUIdx_Stage2* or one of the ARMMMUIdx_Phys_* indexes. + * If a Secure ptw is "downgraded" to NonSecure by an NSTable bit, + * this field is updated accordingly. + */ + ARMMMUIdx in_ptw_idx; + /* + * in_space: the security space for this walk. This plus + * the in_mmu_idx specify the architectural translation regime. + * If a Secure ptw is "downgraded" to NonSecure by an NSTable bit, + * this field is updated accordingly. + * + * Note that the security space for the in_ptw_idx may be different + * from that for the in_mmu_idx. We do not need to explicitly track + * the in_ptw_idx security space because: + * - if the in_ptw_idx is an ARMMMUIdx_Phys_* then the mmuidx + * itself specifies the security space + * - if the in_ptw_idx is an ARMMMUIdx_Stage2* then the security + * space used for ptw reads is the same as that of the security + * space of the stage 1 translation for all cases except where + * stage 1 is Secure; in that case the only possibilities for + * the ptw read are Secure and NonSecure, and the in_ptw_idx + * value being Stage2 vs Stage2_S distinguishes those. + */ + ARMSecuritySpace in_space; + /* + * in_debug: is this a QEMU debug access (gdbstub, etc)? Debug + * accesses will not update the guest page table access flags + * and will not change the state of the softmmu TLBs. + */ + bool in_debug; + /* + * If this is stage 2 of a stage 1+2 page table walk, then this must + * be true if stage 1 is an EL0 access; otherwise this is ignored. + * Stage 2 is indicated by in_mmu_idx set to ARMMMUIdx_Stage2{,_S}. + */ + bool in_s1_is_el0; + bool out_rw; + bool out_be; + ARMSecuritySpace out_space; + hwaddr out_virt; + hwaddr out_phys; + void *out_host; +} S1Translate; -static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - bool s1_is_el0, hwaddr *phys_ptr, - MemTxAttrs *txattrs, int *prot, - target_ulong *page_size_ptr, - ARMMMUFaultInfo *fi, ARMCacheAttrs *cacheattrs) - __attribute__((nonnull)); +static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, + target_ulong address, + MMUAccessType access_type, + GetPhysAddrResult *result, + ARMMMUFaultInfo *fi); + +static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw, + target_ulong address, + MMUAccessType access_type, + GetPhysAddrResult *result, + ARMMMUFaultInfo *fi); /* This mapping is common between ID_AA64MMFR0.PARANGE and TCR_ELx.{I}PS. */ static const uint8_t pamax_map[] = { @@ -33,18 +95,30 @@ static const uint8_t pamax_map[] = { [6] = 52, }; -/* The cpu-specific constant value of PAMax; also used by hw/arm/virt. */ +/* + * The cpu-specific constant value of PAMax; also used by hw/arm/virt. + * Note that machvirt_init calls this on a CPU that is inited but not realized! + */ unsigned int arm_pamax(ARMCPU *cpu) { - unsigned int parange = - FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + unsigned int parange = + FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); - /* - * id_aa64mmfr0 is a read-only register so values outside of the - * supported mappings can be considered an implementation error. - */ - assert(parange < ARRAY_SIZE(pamax_map)); - return pamax_map[parange]; + /* + * id_aa64mmfr0 is a read-only register so values outside of the + * supported mappings can be considered an implementation error. + */ + assert(parange < ARRAY_SIZE(pamax_map)); + return pamax_map[parange]; + } + + if (arm_feature(&cpu->env, ARM_FEATURE_LPAE)) { + /* v7 or v8 with LPAE */ + return 40; + } + /* Anything else */ + return 32; } /* @@ -53,12 +127,6 @@ unsigned int arm_pamax(ARMCPU *cpu) ARMMMUIdx stage_1_mmu_idx(ARMMMUIdx mmu_idx) { switch (mmu_idx) { - case ARMMMUIdx_SE10_0: - return ARMMMUIdx_Stage1_SE0; - case ARMMMUIdx_SE10_1: - return ARMMMUIdx_Stage1_SE1; - case ARMMMUIdx_SE10_1_PAN: - return ARMMMUIdx_Stage1_SE1_PAN; case ARMMMUIdx_E10_0: return ARMMMUIdx_Stage1_E0; case ARMMMUIdx_E10_1: @@ -75,33 +143,52 @@ ARMMMUIdx arm_stage1_mmu_idx(CPUARMState *env) return stage_1_mmu_idx(arm_mmu_idx(env)); } +/* + * Return where we should do ptw loads from for a stage 2 walk. + * This depends on whether the address we are looking up is a + * Secure IPA or a NonSecure IPA, which we know from whether this is + * Stage2 or Stage2_S. + * If this is the Secure EL1&0 regime we need to check the NSW and SW bits. + */ +static ARMMMUIdx ptw_idx_for_stage_2(CPUARMState *env, ARMMMUIdx stage2idx) +{ + bool s2walk_secure; + + /* + * We're OK to check the current state of the CPU here because + * (1) we always invalidate all TLBs when the SCR_EL3.NS or SCR_EL3.NSE bit + * changes. + * (2) there's no way to do a lookup that cares about Stage 2 for a + * different security state to the current one for AArch64, and AArch32 + * never has a secure EL2. (AArch32 ATS12NSO[UP][RW] allow EL3 to do + * an NS stage 1+2 lookup while the NS bit is 0.) + */ + if (!arm_el_is_aa64(env, 3)) { + return ARMMMUIdx_Phys_NS; + } + + switch (arm_security_space_below_el3(env)) { + case ARMSS_NonSecure: + return ARMMMUIdx_Phys_NS; + case ARMSS_Realm: + return ARMMMUIdx_Phys_Realm; + case ARMSS_Secure: + if (stage2idx == ARMMMUIdx_Stage2_S) { + s2walk_secure = !(env->cp15.vstcr_el2 & VSTCR_SW); + } else { + s2walk_secure = !(env->cp15.vtcr_el2 & VTCR_NSW); + } + return s2walk_secure ? ARMMMUIdx_Phys_S : ARMMMUIdx_Phys_NS; + default: + g_assert_not_reached(); + } +} + static bool regime_translation_big_endian(CPUARMState *env, ARMMMUIdx mmu_idx) { return (regime_sctlr(env, mmu_idx) & SCTLR_EE) != 0; } -static bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) -{ - switch (mmu_idx) { - case ARMMMUIdx_SE10_0: - case ARMMMUIdx_E20_0: - case ARMMMUIdx_SE20_0: - case ARMMMUIdx_Stage1_E0: - case ARMMMUIdx_Stage1_SE0: - case ARMMMUIdx_MUser: - case ARMMMUIdx_MSUser: - case ARMMMUIdx_MUserNegPri: - case ARMMMUIdx_MSUserNegPri: - return true; - default: - return false; - case ARMMMUIdx_E10_0: - case ARMMMUIdx_E10_1: - case ARMMMUIdx_E10_1_PAN: - g_assert_not_reached(); - } -} - /* Return the TTBR associated with this translation regime */ static uint64_t regime_ttbr(CPUARMState *env, ARMMMUIdx mmu_idx, int ttbrn) { @@ -119,12 +206,14 @@ static uint64_t regime_ttbr(CPUARMState *env, ARMMMUIdx mmu_idx, int ttbrn) } /* Return true if the specified stage of address translation is disabled */ -static bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx) +static bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx, + ARMSecuritySpace space) { uint64_t hcr_el2; if (arm_feature(env, ARM_FEATURE_M)) { - switch (env->v7m.mpu_ctrl[regime_is_secure(env, mmu_idx)] & + bool is_secure = arm_space_is_secure(space); + switch (env->v7m.mpu_ctrl[is_secure] & (R_V7M_MPU_CTRL_ENABLE_MASK | R_V7M_MPU_CTRL_HFNMIENA_MASK)) { case R_V7M_MPU_CTRL_ENABLE_MASK: /* Enabled, but not for HardFault and NMI */ @@ -142,29 +231,257 @@ static bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx) } } - hcr_el2 = arm_hcr_el2_eff(env); - if (mmu_idx == ARMMMUIdx_Stage2 || mmu_idx == ARMMMUIdx_Stage2_S) { + switch (mmu_idx) { + case ARMMMUIdx_Stage2: + case ARMMMUIdx_Stage2_S: /* HCR.DC means HCR.VM behaves as 1 */ + hcr_el2 = arm_hcr_el2_eff_secstate(env, space); return (hcr_el2 & (HCR_DC | HCR_VM)) == 0; - } - if (hcr_el2 & HCR_TGE) { - /* TGE means that NS EL0/1 act as if SCTLR_EL1.M is zero */ - if (!regime_is_secure(env, mmu_idx) && regime_el(env, mmu_idx) == 1) { + case ARMMMUIdx_E10_0: + case ARMMMUIdx_E10_1: + case ARMMMUIdx_E10_1_PAN: + /* TGE means that EL0/1 act as if SCTLR_EL1.M is zero */ + hcr_el2 = arm_hcr_el2_eff_secstate(env, space); + if (hcr_el2 & HCR_TGE) { return true; } - } + break; - if ((hcr_el2 & HCR_DC) && arm_mmu_idx_is_stage1_of_2(mmu_idx)) { + case ARMMMUIdx_Stage1_E0: + case ARMMMUIdx_Stage1_E1: + case ARMMMUIdx_Stage1_E1_PAN: /* HCR.DC means SCTLR_EL1.M behaves as 0 */ + hcr_el2 = arm_hcr_el2_eff_secstate(env, space); + if (hcr_el2 & HCR_DC) { + return true; + } + break; + + case ARMMMUIdx_E20_0: + case ARMMMUIdx_E20_2: + case ARMMMUIdx_E20_2_PAN: + case ARMMMUIdx_E2: + case ARMMMUIdx_E3: + break; + + case ARMMMUIdx_Phys_S: + case ARMMMUIdx_Phys_NS: + case ARMMMUIdx_Phys_Root: + case ARMMMUIdx_Phys_Realm: + /* No translation for physical address spaces. */ return true; + + default: + g_assert_not_reached(); } return (regime_sctlr(env, mmu_idx) & SCTLR_M) == 0; } -static bool ptw_attrs_are_device(CPUARMState *env, ARMCacheAttrs cacheattrs) +static bool granule_protection_check(CPUARMState *env, uint64_t paddress, + ARMSecuritySpace pspace, + ARMMMUFaultInfo *fi) +{ + MemTxAttrs attrs = { + .secure = true, + .space = ARMSS_Root, + }; + ARMCPU *cpu = env_archcpu(env); + uint64_t gpccr = env->cp15.gpccr_el3; + unsigned pps, pgs, l0gptsz, level = 0; + uint64_t tableaddr, pps_mask, align, entry, index; + AddressSpace *as; + MemTxResult result; + int gpi; + + if (!FIELD_EX64(gpccr, GPCCR, GPC)) { + return true; + } + + /* + * GPC Priority 1 (R_GMGRR): + * R_JWCSM: If the configuration of GPCCR_EL3 is invalid, + * the access fails as GPT walk fault at level 0. + */ + + /* + * Configuration of PPS to a value exceeding the implemented + * physical address size is invalid. + */ + pps = FIELD_EX64(gpccr, GPCCR, PPS); + if (pps > FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE)) { + goto fault_walk; + } + pps = pamax_map[pps]; + pps_mask = MAKE_64BIT_MASK(0, pps); + + switch (FIELD_EX64(gpccr, GPCCR, SH)) { + case 0b10: /* outer shareable */ + break; + case 0b00: /* non-shareable */ + case 0b11: /* inner shareable */ + /* Inner and Outer non-cacheable requires Outer shareable. */ + if (FIELD_EX64(gpccr, GPCCR, ORGN) == 0 && + FIELD_EX64(gpccr, GPCCR, IRGN) == 0) { + goto fault_walk; + } + break; + default: /* reserved */ + goto fault_walk; + } + + switch (FIELD_EX64(gpccr, GPCCR, PGS)) { + case 0b00: /* 4KB */ + pgs = 12; + break; + case 0b01: /* 64KB */ + pgs = 16; + break; + case 0b10: /* 16KB */ + pgs = 14; + break; + default: /* reserved */ + goto fault_walk; + } + + /* Note this field is read-only and fixed at reset. */ + l0gptsz = 30 + FIELD_EX64(gpccr, GPCCR, L0GPTSZ); + + /* + * GPC Priority 2: Secure, Realm or Root address exceeds PPS. + * R_CPDSB: A NonSecure physical address input exceeding PPS + * does not experience any fault. + */ + if (paddress & ~pps_mask) { + if (pspace == ARMSS_NonSecure) { + return true; + } + goto fault_size; + } + + /* GPC Priority 3: the base address of GPTBR_EL3 exceeds PPS. */ + tableaddr = env->cp15.gptbr_el3 << 12; + if (tableaddr & ~pps_mask) { + goto fault_size; + } + + /* + * BADDR is aligned per a function of PPS and L0GPTSZ. + * These bits of GPTBR_EL3 are RES0, but are not a configuration error, + * unlike the RES0 bits of the GPT entries (R_XNKFZ). + */ + align = MAX(pps - l0gptsz + 3, 12); + align = MAKE_64BIT_MASK(0, align); + tableaddr &= ~align; + + as = arm_addressspace(env_cpu(env), attrs); + + /* Level 0 lookup. */ + index = extract64(paddress, l0gptsz, pps - l0gptsz); + tableaddr += index * 8; + entry = address_space_ldq_le(as, tableaddr, attrs, &result); + if (result != MEMTX_OK) { + goto fault_eabt; + } + + switch (extract32(entry, 0, 4)) { + case 1: /* block descriptor */ + if (entry >> 8) { + goto fault_walk; /* RES0 bits not 0 */ + } + gpi = extract32(entry, 4, 4); + goto found; + case 3: /* table descriptor */ + tableaddr = entry & ~0xf; + align = MAX(l0gptsz - pgs - 1, 12); + align = MAKE_64BIT_MASK(0, align); + if (tableaddr & (~pps_mask | align)) { + goto fault_walk; /* RES0 bits not 0 */ + } + break; + default: /* invalid */ + goto fault_walk; + } + + /* Level 1 lookup */ + level = 1; + index = extract64(paddress, pgs + 4, l0gptsz - pgs - 4); + tableaddr += index * 8; + entry = address_space_ldq_le(as, tableaddr, attrs, &result); + if (result != MEMTX_OK) { + goto fault_eabt; + } + + switch (extract32(entry, 0, 4)) { + case 1: /* contiguous descriptor */ + if (entry >> 10) { + goto fault_walk; /* RES0 bits not 0 */ + } + /* + * Because the softmmu tlb only works on units of TARGET_PAGE_SIZE, + * and because we cannot invalidate by pa, and thus will always + * flush entire tlbs, we don't actually care about the range here + * and can simply extract the GPI as the result. + */ + if (extract32(entry, 8, 2) == 0) { + goto fault_walk; /* reserved contig */ + } + gpi = extract32(entry, 4, 4); + break; + default: + index = extract64(paddress, pgs, 4); + gpi = extract64(entry, index * 4, 4); + break; + } + + found: + switch (gpi) { + case 0b0000: /* no access */ + break; + case 0b1111: /* all access */ + return true; + case 0b1000: + case 0b1001: + case 0b1010: + case 0b1011: + if (pspace == (gpi & 3)) { + return true; + } + break; + default: + goto fault_walk; /* reserved */ + } + + fi->gpcf = GPCF_Fail; + goto fault_common; + fault_eabt: + fi->gpcf = GPCF_EABT; + goto fault_common; + fault_size: + fi->gpcf = GPCF_AddressSize; + goto fault_common; + fault_walk: + fi->gpcf = GPCF_Walk; + fault_common: + fi->level = level; + fi->paddr = paddress; + fi->paddr_space = pspace; + return false; +} + +static bool S1_attrs_are_device(uint8_t attrs) +{ + /* + * This slightly under-decodes the MAIR_ELx field: + * 0b0000dd01 is Device with FEAT_XS, otherwise UNPREDICTABLE; + * 0b0000dd1x is UNPREDICTABLE. + */ + return (attrs & 0xf0) == 0; +} + +static bool S2_attrs_are_device(uint64_t hcr, uint8_t attrs) { /* * For an S1 page table walk, the stage 1 attributes are always @@ -175,43 +492,113 @@ static bool ptw_attrs_are_device(CPUARMState *env, ARMCacheAttrs cacheattrs) * With HCR_EL2.FWB == 1 this is when descriptor bit [4] is 0, ie * when cacheattrs.attrs bit [2] is 0. */ - assert(cacheattrs.is_s2_format); - if (arm_hcr_el2_eff(env) & HCR_FWB) { - return (cacheattrs.attrs & 0x4) == 0; + if (hcr & HCR_FWB) { + return (attrs & 0x4) == 0; } else { - return (cacheattrs.attrs & 0xc) == 0; + return (attrs & 0xc) == 0; } } -/* Translate a S1 pagetable walk through S2 if needed. */ -static hwaddr S1_ptw_translate(CPUARMState *env, ARMMMUIdx mmu_idx, - hwaddr addr, bool *is_secure, - ARMMMUFaultInfo *fi) +static ARMSecuritySpace S2_security_space(ARMSecuritySpace s1_space, + ARMMMUIdx s2_mmu_idx) { - if (arm_mmu_idx_is_stage1_of_2(mmu_idx) && - !regime_translation_disabled(env, ARMMMUIdx_Stage2)) { - target_ulong s2size; - hwaddr s2pa; - int s2prot; - int ret; - ARMMMUIdx s2_mmu_idx = *is_secure ? ARMMMUIdx_Stage2_S - : ARMMMUIdx_Stage2; - ARMCacheAttrs cacheattrs = {}; - MemTxAttrs txattrs = {}; - - ret = get_phys_addr_lpae(env, addr, MMU_DATA_LOAD, s2_mmu_idx, false, - &s2pa, &txattrs, &s2prot, &s2size, fi, - &cacheattrs); - if (ret) { - assert(fi->type != ARMFault_None); - fi->s2addr = addr; - fi->stage2 = true; - fi->s1ptw = true; - fi->s1ns = !*is_secure; - return ~0; + /* + * Return the security space to use for stage 2 when doing + * the S1 page table descriptor load. + */ + if (regime_is_stage2(s2_mmu_idx)) { + /* + * The security space for ptw reads is almost always the same + * as that of the security space of the stage 1 translation. + * The only exception is when stage 1 is Secure; in that case + * the ptw read might be to the Secure or the NonSecure space + * (but never Realm or Root), and the s2_mmu_idx tells us which. + * Root translations are always single-stage. + */ + if (s1_space == ARMSS_Secure) { + return arm_secure_to_space(s2_mmu_idx == ARMMMUIdx_Stage2_S); + } else { + assert(s2_mmu_idx != ARMMMUIdx_Stage2_S); + assert(s1_space != ARMSS_Root); + return s1_space; } - if ((arm_hcr_el2_eff(env) & HCR_PTW) && - ptw_attrs_are_device(env, cacheattrs)) { + } else { + /* ptw loads are from phys: the mmu idx itself says which space */ + return arm_phys_to_space(s2_mmu_idx); + } +} + +static bool fault_s1ns(ARMSecuritySpace space, ARMMMUIdx s2_mmu_idx) +{ + /* + * For stage 2 faults in Secure EL22, S1NS indicates + * whether the faulting IPA is in the Secure or NonSecure + * IPA space. For all other kinds of fault, it is false. + */ + return space == ARMSS_Secure && regime_is_stage2(s2_mmu_idx) + && s2_mmu_idx == ARMMMUIdx_Stage2_S; +} + +/* Translate a S1 pagetable walk through S2 if needed. */ +static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw, + hwaddr addr, ARMMMUFaultInfo *fi) +{ + ARMMMUIdx mmu_idx = ptw->in_mmu_idx; + ARMMMUIdx s2_mmu_idx = ptw->in_ptw_idx; + uint8_t pte_attrs; + + ptw->out_virt = addr; + + if (unlikely(ptw->in_debug)) { + /* + * From gdbstub, do not use softmmu so that we don't modify the + * state of the cpu at all, including softmmu tlb contents. + */ + ARMSecuritySpace s2_space = S2_security_space(ptw->in_space, s2_mmu_idx); + S1Translate s2ptw = { + .in_mmu_idx = s2_mmu_idx, + .in_ptw_idx = ptw_idx_for_stage_2(env, s2_mmu_idx), + .in_space = s2_space, + .in_debug = true, + }; + GetPhysAddrResult s2 = { }; + + if (get_phys_addr_gpc(env, &s2ptw, addr, MMU_DATA_LOAD, &s2, fi)) { + goto fail; + } + + ptw->out_phys = s2.f.phys_addr; + pte_attrs = s2.cacheattrs.attrs; + ptw->out_host = NULL; + ptw->out_rw = false; + ptw->out_space = s2.f.attrs.space; + } else { +#ifdef CONFIG_TCG + CPUTLBEntryFull *full; + int flags; + + env->tlb_fi = fi; + flags = probe_access_full_mmu(env, addr, 0, MMU_DATA_LOAD, + arm_to_core_mmu_idx(s2_mmu_idx), + &ptw->out_host, &full); + env->tlb_fi = NULL; + + if (unlikely(flags & TLB_INVALID_MASK)) { + goto fail; + } + ptw->out_phys = full->phys_addr | (addr & ~TARGET_PAGE_MASK); + ptw->out_rw = full->prot & PAGE_WRITE; + pte_attrs = full->extra.arm.pte_attrs; + ptw->out_space = full->attrs.space; +#else + g_assert_not_reached(); +#endif + } + + if (regime_is_stage2(s2_mmu_idx)) { + uint64_t hcr = arm_hcr_el2_eff_secstate(env, ptw->in_space); + + if ((hcr & HCR_PTW) && S2_attrs_are_device(hcr, pte_attrs)) { /* * PTW set and S1 walk touched S2 Device memory: * generate Permission fault. @@ -220,101 +607,288 @@ static hwaddr S1_ptw_translate(CPUARMState *env, ARMMMUIdx mmu_idx, fi->s2addr = addr; fi->stage2 = true; fi->s1ptw = true; - fi->s1ns = !*is_secure; - return ~0; + fi->s1ns = fault_s1ns(ptw->in_space, s2_mmu_idx); + return false; } - - if (arm_is_secure_below_el3(env)) { - /* Check if page table walk is to secure or non-secure PA space. */ - if (*is_secure) { - *is_secure = !(env->cp15.vstcr_el2.raw_tcr & VSTCR_SW); - } else { - *is_secure = !(env->cp15.vtcr_el2.raw_tcr & VTCR_NSW); - } - } else { - assert(!*is_secure); - } - - addr = s2pa; } - return addr; + + ptw->out_be = regime_translation_big_endian(env, mmu_idx); + return true; + + fail: + assert(fi->type != ARMFault_None); + if (fi->type == ARMFault_GPCFOnOutput) { + fi->type = ARMFault_GPCFOnWalk; + } + fi->s2addr = addr; + fi->stage2 = regime_is_stage2(s2_mmu_idx); + fi->s1ptw = fi->stage2; + fi->s1ns = fault_s1ns(ptw->in_space, s2_mmu_idx); + return false; } /* All loads done in the course of a page table walk go through here. */ -static uint32_t arm_ldl_ptw(CPUARMState *env, hwaddr addr, bool is_secure, - ARMMMUIdx mmu_idx, ARMMMUFaultInfo *fi) +static uint32_t arm_ldl_ptw(CPUARMState *env, S1Translate *ptw, + ARMMMUFaultInfo *fi) { CPUState *cs = env_cpu(env); - MemTxAttrs attrs = {}; - MemTxResult result = MEMTX_OK; - AddressSpace *as; + void *host = ptw->out_host; uint32_t data; - addr = S1_ptw_translate(env, mmu_idx, addr, &is_secure, fi); - attrs.secure = is_secure; - as = arm_addressspace(cs, attrs); - if (fi->s1ptw) { - return 0; - } - if (regime_translation_big_endian(env, mmu_idx)) { - data = address_space_ldl_be(as, addr, attrs, &result); + if (likely(host)) { + /* Page tables are in RAM, and we have the host address. */ + data = qatomic_read((uint32_t *)host); + if (ptw->out_be) { + data = be32_to_cpu(data); + } else { + data = le32_to_cpu(data); + } } else { - data = address_space_ldl_le(as, addr, attrs, &result); + /* Page tables are in MMIO. */ + MemTxAttrs attrs = { + .space = ptw->out_space, + .secure = arm_space_is_secure(ptw->out_space), + }; + AddressSpace *as = arm_addressspace(cs, attrs); + MemTxResult result = MEMTX_OK; + + if (ptw->out_be) { + data = address_space_ldl_be(as, ptw->out_phys, attrs, &result); + } else { + data = address_space_ldl_le(as, ptw->out_phys, attrs, &result); + } + if (unlikely(result != MEMTX_OK)) { + fi->type = ARMFault_SyncExternalOnWalk; + fi->ea = arm_extabort_type(result); + return 0; + } } - if (result == MEMTX_OK) { - return data; - } - fi->type = ARMFault_SyncExternalOnWalk; - fi->ea = arm_extabort_type(result); - return 0; + return data; } -static uint64_t arm_ldq_ptw(CPUARMState *env, hwaddr addr, bool is_secure, - ARMMMUIdx mmu_idx, ARMMMUFaultInfo *fi) +static uint64_t arm_ldq_ptw(CPUARMState *env, S1Translate *ptw, + ARMMMUFaultInfo *fi) { CPUState *cs = env_cpu(env); - MemTxAttrs attrs = {}; - MemTxResult result = MEMTX_OK; - AddressSpace *as; + void *host = ptw->out_host; uint64_t data; - addr = S1_ptw_translate(env, mmu_idx, addr, &is_secure, fi); - attrs.secure = is_secure; - as = arm_addressspace(cs, attrs); - if (fi->s1ptw) { - return 0; - } - if (regime_translation_big_endian(env, mmu_idx)) { - data = address_space_ldq_be(as, addr, attrs, &result); + if (likely(host)) { + /* Page tables are in RAM, and we have the host address. */ +#ifdef CONFIG_ATOMIC64 + data = qatomic_read__nocheck((uint64_t *)host); + if (ptw->out_be) { + data = be64_to_cpu(data); + } else { + data = le64_to_cpu(data); + } +#else + if (ptw->out_be) { + data = ldq_be_p(host); + } else { + data = ldq_le_p(host); + } +#endif } else { - data = address_space_ldq_le(as, addr, attrs, &result); + /* Page tables are in MMIO. */ + MemTxAttrs attrs = { + .space = ptw->out_space, + .secure = arm_space_is_secure(ptw->out_space), + }; + AddressSpace *as = arm_addressspace(cs, attrs); + MemTxResult result = MEMTX_OK; + + if (ptw->out_be) { + data = address_space_ldq_be(as, ptw->out_phys, attrs, &result); + } else { + data = address_space_ldq_le(as, ptw->out_phys, attrs, &result); + } + if (unlikely(result != MEMTX_OK)) { + fi->type = ARMFault_SyncExternalOnWalk; + fi->ea = arm_extabort_type(result); + return 0; + } } - if (result == MEMTX_OK) { - return data; + return data; +} + +static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val, + uint64_t new_val, S1Translate *ptw, + ARMMMUFaultInfo *fi) +{ +#if defined(TARGET_AARCH64) && defined(CONFIG_TCG) + uint64_t cur_val; + void *host = ptw->out_host; + + if (unlikely(!host)) { + /* Page table in MMIO Memory Region */ + CPUState *cs = env_cpu(env); + MemTxAttrs attrs = { + .space = ptw->out_space, + .secure = arm_space_is_secure(ptw->out_space), + }; + AddressSpace *as = arm_addressspace(cs, attrs); + MemTxResult result = MEMTX_OK; + bool need_lock = !bql_locked(); + + if (need_lock) { + bql_lock(); + } + if (ptw->out_be) { + cur_val = address_space_ldq_be(as, ptw->out_phys, attrs, &result); + if (unlikely(result != MEMTX_OK)) { + fi->type = ARMFault_SyncExternalOnWalk; + fi->ea = arm_extabort_type(result); + if (need_lock) { + bql_unlock(); + } + return old_val; + } + if (cur_val == old_val) { + address_space_stq_be(as, ptw->out_phys, new_val, attrs, &result); + if (unlikely(result != MEMTX_OK)) { + fi->type = ARMFault_SyncExternalOnWalk; + fi->ea = arm_extabort_type(result); + if (need_lock) { + bql_unlock(); + } + return old_val; + } + cur_val = new_val; + } + } else { + cur_val = address_space_ldq_le(as, ptw->out_phys, attrs, &result); + if (unlikely(result != MEMTX_OK)) { + fi->type = ARMFault_SyncExternalOnWalk; + fi->ea = arm_extabort_type(result); + if (need_lock) { + bql_unlock(); + } + return old_val; + } + if (cur_val == old_val) { + address_space_stq_le(as, ptw->out_phys, new_val, attrs, &result); + if (unlikely(result != MEMTX_OK)) { + fi->type = ARMFault_SyncExternalOnWalk; + fi->ea = arm_extabort_type(result); + if (need_lock) { + bql_unlock(); + } + return old_val; + } + cur_val = new_val; + } + } + if (need_lock) { + bql_unlock(); + } + return cur_val; } - fi->type = ARMFault_SyncExternalOnWalk; - fi->ea = arm_extabort_type(result); - return 0; + + /* + * Raising a stage2 Protection fault for an atomic update to a read-only + * page is delayed until it is certain that there is a change to make. + */ + if (unlikely(!ptw->out_rw)) { + int flags; + + env->tlb_fi = fi; + flags = probe_access_full_mmu(env, ptw->out_virt, 0, + MMU_DATA_STORE, + arm_to_core_mmu_idx(ptw->in_ptw_idx), + NULL, NULL); + env->tlb_fi = NULL; + + if (unlikely(flags & TLB_INVALID_MASK)) { + /* + * We know this must be a stage 2 fault because the granule + * protection table does not separately track read and write + * permission, so all GPC faults are caught in S1_ptw_translate(): + * we only get here for "readable but not writeable". + */ + assert(fi->type != ARMFault_None); + fi->s2addr = ptw->out_virt; + fi->stage2 = true; + fi->s1ptw = true; + fi->s1ns = fault_s1ns(ptw->in_space, ptw->in_ptw_idx); + return 0; + } + + /* In case CAS mismatches and we loop, remember writability. */ + ptw->out_rw = true; + } + +#ifdef CONFIG_ATOMIC64 + if (ptw->out_be) { + old_val = cpu_to_be64(old_val); + new_val = cpu_to_be64(new_val); + cur_val = qatomic_cmpxchg__nocheck((uint64_t *)host, old_val, new_val); + cur_val = be64_to_cpu(cur_val); + } else { + old_val = cpu_to_le64(old_val); + new_val = cpu_to_le64(new_val); + cur_val = qatomic_cmpxchg__nocheck((uint64_t *)host, old_val, new_val); + cur_val = le64_to_cpu(cur_val); + } +#else + /* + * We can't support the full 64-bit atomic cmpxchg on the host. + * Because this is only used for FEAT_HAFDBS, which is only for AA64, + * we know that TCG_OVERSIZED_GUEST is set, which means that we are + * running in round-robin mode and could only race with dma i/o. + */ +#if !TCG_OVERSIZED_GUEST +# error "Unexpected configuration" +#endif + bool locked = bql_locked(); + if (!locked) { + bql_lock(); + } + if (ptw->out_be) { + cur_val = ldq_be_p(host); + if (cur_val == old_val) { + stq_be_p(host, new_val); + } + } else { + cur_val = ldq_le_p(host); + if (cur_val == old_val) { + stq_le_p(host, new_val); + } + } + if (!locked) { + bql_unlock(); + } +#endif + + return cur_val; +#else + /* AArch32 does not have FEAT_HADFS; non-TCG guests only use debug-mode. */ + g_assert_not_reached(); +#endif } static bool get_level1_table_address(CPUARMState *env, ARMMMUIdx mmu_idx, uint32_t *table, uint32_t address) { /* Note that we can only get here for an AArch32 PL0/PL1 lookup */ - TCR *tcr = regime_tcr(env, mmu_idx); + uint64_t tcr = regime_tcr(env, mmu_idx); + int maskshift = extract32(tcr, 0, 3); + uint32_t mask = ~(((uint32_t)0xffffffffu) >> maskshift); + uint32_t base_mask; - if (address & tcr->mask) { - if (tcr->raw_tcr & TTBCR_PD1) { + if (address & mask) { + if (tcr & TTBCR_PD1) { /* Translation table walk disabled for TTBR1 */ return false; } *table = regime_ttbr(env, mmu_idx, 1) & 0xffffc000; } else { - if (tcr->raw_tcr & TTBCR_PD0) { + if (tcr & TTBCR_PD0) { /* Translation table walk disabled for TTBR0 */ return false; } - *table = regime_ttbr(env, mmu_idx, 0) & tcr->base_mask; + base_mask = ~((uint32_t)0x3fffu >> maskshift); + *table = regime_ttbr(env, mmu_idx, 0) & base_mask; } *table |= (address >> 18) & 0x3ffc; return true; @@ -326,12 +900,11 @@ static bool get_level1_table_address(CPUARMState *env, ARMMMUIdx mmu_idx, * @mmu_idx: MMU index indicating required translation regime * @ap: The 3-bit access permissions (AP[2:0]) * @domain_prot: The 2-bit domain access permissions + * @is_user: TRUE if accessing from PL0 */ -static int ap_to_rw_prot(CPUARMState *env, ARMMMUIdx mmu_idx, - int ap, int domain_prot) +static int ap_to_rw_prot_is_user(CPUARMState *env, ARMMMUIdx mmu_idx, + int ap, int domain_prot, bool is_user) { - bool is_user = regime_is_user(env, mmu_idx); - if (domain_prot == 3) { return PAGE_READ | PAGE_WRITE; } @@ -375,6 +948,20 @@ static int ap_to_rw_prot(CPUARMState *env, ARMMMUIdx mmu_idx, } } +/* + * Translate section/page access permissions to page R/W protection flags + * @env: CPUARMState + * @mmu_idx: MMU index indicating required translation regime + * @ap: The 3-bit access permissions (AP[2:0]) + * @domain_prot: The 2-bit domain access permissions + */ +static int ap_to_rw_prot(CPUARMState *env, ARMMMUIdx mmu_idx, + int ap, int domain_prot) +{ + return ap_to_rw_prot_is_user(env, mmu_idx, ap, domain_prot, + regime_is_user(env, mmu_idx)); +} + /* * Translate section/page access permissions to page R/W protection flags. * @ap: The 2-bit simple AP (AP[2:1]) @@ -401,11 +988,9 @@ static int simple_ap_to_rw_prot(CPUARMState *env, ARMMMUIdx mmu_idx, int ap) return simple_ap_to_rw_prot_is_user(ap, regime_is_user(env, mmu_idx)); } -static bool get_phys_addr_v5(CPUARMState *env, uint32_t address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, int *prot, - target_ulong *page_size, - ARMMMUFaultInfo *fi) +static bool get_phys_addr_v5(CPUARMState *env, S1Translate *ptw, + uint32_t address, MMUAccessType access_type, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { int level = 1; uint32_t table; @@ -419,19 +1004,21 @@ static bool get_phys_addr_v5(CPUARMState *env, uint32_t address, /* Pagetable walk. */ /* Lookup l1 descriptor. */ - if (!get_level1_table_address(env, mmu_idx, &table, address)) { + if (!get_level1_table_address(env, ptw->in_mmu_idx, &table, address)) { /* Section translation fault if page walk is disabled by PD0 or PD1 */ fi->type = ARMFault_Translation; goto do_fault; } - desc = arm_ldl_ptw(env, table, regime_is_secure(env, mmu_idx), - mmu_idx, fi); + if (!S1_ptw_translate(env, ptw, table, fi)) { + goto do_fault; + } + desc = arm_ldl_ptw(env, ptw, fi); if (fi->type != ARMFault_None) { goto do_fault; } type = (desc & 3); domain = (desc >> 5) & 0x0f; - if (regime_el(env, mmu_idx) == 1) { + if (regime_el(env, ptw->in_mmu_idx) == 1) { dacr = env->cp15.dacr_ns; } else { dacr = env->cp15.dacr_s; @@ -453,7 +1040,7 @@ static bool get_phys_addr_v5(CPUARMState *env, uint32_t address, /* 1Mb section. */ phys_addr = (desc & 0xfff00000) | (address & 0x000fffff); ap = (desc >> 10) & 3; - *page_size = 1024 * 1024; + result->f.lg_page_size = 20; /* 1MB */ } else { /* Lookup l2 entry. */ if (type == 1) { @@ -463,8 +1050,10 @@ static bool get_phys_addr_v5(CPUARMState *env, uint32_t address, /* Fine pagetable. */ table = (desc & 0xfffff000) | ((address >> 8) & 0xffc); } - desc = arm_ldl_ptw(env, table, regime_is_secure(env, mmu_idx), - mmu_idx, fi); + if (!S1_ptw_translate(env, ptw, table, fi)) { + goto do_fault; + } + desc = arm_ldl_ptw(env, ptw, fi); if (fi->type != ARMFault_None) { goto do_fault; } @@ -475,12 +1064,12 @@ static bool get_phys_addr_v5(CPUARMState *env, uint32_t address, case 1: /* 64k page. */ phys_addr = (desc & 0xffff0000) | (address & 0xffff); ap = (desc >> (4 + ((address >> 13) & 6))) & 3; - *page_size = 0x10000; + result->f.lg_page_size = 16; break; case 2: /* 4k page. */ phys_addr = (desc & 0xfffff000) | (address & 0xfff); ap = (desc >> (4 + ((address >> 9) & 6))) & 3; - *page_size = 0x1000; + result->f.lg_page_size = 12; break; case 3: /* 1k page, or ARMv6/XScale "extended small (4k) page" */ if (type == 1) { @@ -488,7 +1077,7 @@ static bool get_phys_addr_v5(CPUARMState *env, uint32_t address, if (arm_feature(env, ARM_FEATURE_XSCALE) || arm_feature(env, ARM_FEATURE_V6)) { phys_addr = (desc & 0xfffff000) | (address & 0xfff); - *page_size = 0x1000; + result->f.lg_page_size = 12; } else { /* * UNPREDICTABLE in ARMv5; we choose to take a @@ -499,7 +1088,7 @@ static bool get_phys_addr_v5(CPUARMState *env, uint32_t address, } } else { phys_addr = (desc & 0xfffffc00) | (address & 0x3ff); - *page_size = 0x400; + result->f.lg_page_size = 10; } ap = (desc >> 4) & 3; break; @@ -508,14 +1097,14 @@ static bool get_phys_addr_v5(CPUARMState *env, uint32_t address, g_assert_not_reached(); } } - *prot = ap_to_rw_prot(env, mmu_idx, ap, domain_prot); - *prot |= *prot ? PAGE_EXEC : 0; - if (!(*prot & (1 << access_type))) { + result->f.prot = ap_to_rw_prot(env, ptw->in_mmu_idx, ap, domain_prot); + result->f.prot |= result->f.prot ? PAGE_EXEC : 0; + if (!(result->f.prot & (1 << access_type))) { /* Access permission fault. */ fi->type = ARMFault_Permission; goto do_fault; } - *phys_ptr = phys_addr; + result->f.phys_addr = phys_addr; return false; do_fault: fi->domain = domain; @@ -523,12 +1112,12 @@ do_fault: return true; } -static bool get_phys_addr_v6(CPUARMState *env, uint32_t address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, - target_ulong *page_size, ARMMMUFaultInfo *fi) +static bool get_phys_addr_v6(CPUARMState *env, S1Translate *ptw, + uint32_t address, MMUAccessType access_type, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { ARMCPU *cpu = env_archcpu(env); + ARMMMUIdx mmu_idx = ptw->in_mmu_idx; int level = 1; uint32_t table; uint32_t desc; @@ -541,6 +1130,7 @@ static bool get_phys_addr_v6(CPUARMState *env, uint32_t address, hwaddr phys_addr; uint32_t dacr; bool ns; + int user_prot; /* Pagetable walk. */ /* Lookup l1 descriptor. */ @@ -549,8 +1139,10 @@ static bool get_phys_addr_v6(CPUARMState *env, uint32_t address, fi->type = ARMFault_Translation; goto do_fault; } - desc = arm_ldl_ptw(env, table, regime_is_secure(env, mmu_idx), - mmu_idx, fi); + if (!S1_ptw_translate(env, ptw, table, fi)) { + goto do_fault; + } + desc = arm_ldl_ptw(env, ptw, fi); if (fi->type != ARMFault_None) { goto do_fault; } @@ -586,11 +1178,11 @@ static bool get_phys_addr_v6(CPUARMState *env, uint32_t address, phys_addr = (desc & 0xff000000) | (address & 0x00ffffff); phys_addr |= (uint64_t)extract32(desc, 20, 4) << 32; phys_addr |= (uint64_t)extract32(desc, 5, 4) << 36; - *page_size = 0x1000000; + result->f.lg_page_size = 24; /* 16MB */ } else { /* Section. */ phys_addr = (desc & 0xfff00000) | (address & 0x000fffff); - *page_size = 0x100000; + result->f.lg_page_size = 20; /* 1MB */ } ap = ((desc >> 10) & 3) | ((desc >> 13) & 4); xn = desc & (1 << 4); @@ -603,8 +1195,10 @@ static bool get_phys_addr_v6(CPUARMState *env, uint32_t address, ns = extract32(desc, 3, 1); /* Lookup l2 entry. */ table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc); - desc = arm_ldl_ptw(env, table, regime_is_secure(env, mmu_idx), - mmu_idx, fi); + if (!S1_ptw_translate(env, ptw, table, fi)) { + goto do_fault; + } + desc = arm_ldl_ptw(env, ptw, fi); if (fi->type != ARMFault_None) { goto do_fault; } @@ -616,12 +1210,12 @@ static bool get_phys_addr_v6(CPUARMState *env, uint32_t address, case 1: /* 64k page. */ phys_addr = (desc & 0xffff0000) | (address & 0xffff); xn = desc & (1 << 15); - *page_size = 0x10000; + result->f.lg_page_size = 16; break; case 2: case 3: /* 4k page. */ phys_addr = (desc & 0xfffff000) | (address & 0xfff); xn = desc & 1; - *page_size = 0x1000; + result->f.lg_page_size = 12; break; default: /* Never happens, but compiler isn't smart enough to tell. */ @@ -629,7 +1223,7 @@ static bool get_phys_addr_v6(CPUARMState *env, uint32_t address, } } if (domain_prot == 3) { - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + result->f.prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; } else { if (pxn && !regime_is_user(env, mmu_idx)) { xn = 1; @@ -647,27 +1241,38 @@ static bool get_phys_addr_v6(CPUARMState *env, uint32_t address, fi->type = ARMFault_AccessFlag; goto do_fault; } - *prot = simple_ap_to_rw_prot(env, mmu_idx, ap >> 1); + result->f.prot = simple_ap_to_rw_prot(env, mmu_idx, ap >> 1); + user_prot = simple_ap_to_rw_prot_is_user(ap >> 1, 1); } else { - *prot = ap_to_rw_prot(env, mmu_idx, ap, domain_prot); + result->f.prot = ap_to_rw_prot(env, mmu_idx, ap, domain_prot); + user_prot = ap_to_rw_prot_is_user(env, mmu_idx, ap, domain_prot, 1); } - if (*prot && !xn) { - *prot |= PAGE_EXEC; + if (result->f.prot && !xn) { + result->f.prot |= PAGE_EXEC; } - if (!(*prot & (1 << access_type))) { + if (!(result->f.prot & (1 << access_type))) { /* Access permission fault. */ fi->type = ARMFault_Permission; goto do_fault; } + if (regime_is_pan(env, mmu_idx) && + !regime_is_user(env, mmu_idx) && + user_prot && + access_type != MMU_INST_FETCH) { + /* Privileged Access Never fault */ + fi->type = ARMFault_Permission; + goto do_fault; + } } if (ns) { /* The NS bit will (as required by the architecture) have no effect if * the CPU doesn't support TZ or this is a non-secure translation * regime, because the attribute will already be non-secure. */ - attrs->secure = false; + result->f.attrs.secure = false; + result->f.attrs.space = ARMSS_NonSecure; } - *phys_ptr = phys_addr; + result->f.phys_addr = phys_addr; return false; do_fault: fi->domain = domain; @@ -682,7 +1287,7 @@ do_fault: * @xn: XN (execute-never) bits * @s1_is_el0: true if this is S2 of an S1+2 walk for EL0 */ -static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0) +static int get_S2prot_noexecute(int s2ap) { int prot = 0; @@ -692,6 +1297,12 @@ static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0) if (s2ap & 2) { prot |= PAGE_WRITE; } + return prot; +} + +static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0) +{ + int prot = get_S2prot_noexecute(s2ap); if (cpu_isar_feature(any_tts2uxn, env_archcpu(env))) { switch (xn) { @@ -729,35 +1340,79 @@ static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0) * @mmu_idx: MMU index indicating required translation regime * @is_aa64: TRUE if AArch64 * @ap: The 2-bit simple AP (AP[2:1]) - * @ns: NS (non-secure) bit * @xn: XN (execute-never) bit * @pxn: PXN (privileged execute-never) bit + * @in_pa: The original input pa space + * @out_pa: The output pa space, modified by NSTable, NS, and NSE */ static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64, - int ap, int ns, int xn, int pxn) + int ap, int xn, int pxn, + ARMSecuritySpace in_pa, ARMSecuritySpace out_pa) { + ARMCPU *cpu = env_archcpu(env); bool is_user = regime_is_user(env, mmu_idx); int prot_rw, user_rw; bool have_wxn; int wxn = 0; - assert(mmu_idx != ARMMMUIdx_Stage2); - assert(mmu_idx != ARMMMUIdx_Stage2_S); + assert(!regime_is_stage2(mmu_idx)); user_rw = simple_ap_to_rw_prot_is_user(ap, true); if (is_user) { prot_rw = user_rw; } else { + /* + * PAN controls can forbid data accesses but don't affect insn fetch. + * Plain PAN forbids data accesses if EL0 has data permissions; + * PAN3 forbids data accesses if EL0 has either data or exec perms. + * Note that for AArch64 the 'user can exec' case is exactly !xn. + * We make the IMPDEF choices that SCR_EL3.SIF and Realm EL2&0 + * do not affect EPAN. + */ if (user_rw && regime_is_pan(env, mmu_idx)) { - /* PAN forbids data accesses but doesn't affect insn fetch */ + prot_rw = 0; + } else if (cpu_isar_feature(aa64_pan3, cpu) && is_aa64 && + regime_is_pan(env, mmu_idx) && + (regime_sctlr(env, mmu_idx) & SCTLR_EPAN) && !xn) { 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)) { - return prot_rw; + if (in_pa != out_pa) { + switch (in_pa) { + case ARMSS_Root: + /* + * R_ZWRVD: permission fault for insn fetched from non-Root, + * I_WWBFB: SIF has no effect in EL3. + */ + return prot_rw; + case ARMSS_Realm: + /* + * R_PKTDS: permission fault for insn fetched from non-Realm, + * for Realm EL2 or EL2&0. The corresponding fault for EL1&0 + * happens during any stage2 translation. + */ + switch (mmu_idx) { + case ARMMMUIdx_E2: + case ARMMMUIdx_E20_0: + case ARMMMUIdx_E20_2: + case ARMMMUIdx_E20_2_PAN: + return prot_rw; + default: + break; + } + break; + case ARMSS_Secure: + if (env->cp15.scr_el3 & SCR_SIF) { + return prot_rw; + } + break; + default: + /* Input NonSecure must have output NonSecure. */ + g_assert_not_reached(); + } } /* TODO have_wxn should be replaced with @@ -806,7 +1461,7 @@ static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64, static ARMVAParameters aa32_va_parameters(CPUARMState *env, uint32_t va, ARMMMUIdx mmu_idx) { - uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; + uint64_t tcr = regime_tcr(env, mmu_idx); uint32_t el = regime_el(env, mmu_idx); int select, tsz; bool epd, hpd; @@ -871,70 +1526,133 @@ static ARMVAParameters aa32_va_parameters(CPUARMState *env, uint32_t va, * check_s2_mmu_setup * @cpu: ARMCPU * @is_aa64: True if the translation regime is in AArch64 state - * @startlevel: Suggested starting level - * @inputsize: Bitsize of IPAs + * @tcr: VTCR_EL2 or VSTCR_EL2 + * @ds: Effective value of TCR.DS. + * @iasize: Bitsize of IPAs * @stride: Page-table stride (See the ARM ARM) * - * Returns true if the suggested S2 translation parameters are OK and - * false otherwise. + * Decode the starting level of the S2 lookup, returning INT_MIN if + * the configuration is invalid. */ -static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level, - int inputsize, int stride, int outputsize) +static int check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, uint64_t tcr, + bool ds, int iasize, int stride) { - const int grainsize = stride + 3; - int startsizecheck; - - /* - * Negative levels are usually not allowed... - * Except for FEAT_LPA2, 4k page table, 52-bit address space, which - * begins with level -1. Note that previous feature tests will have - * eliminated this combination if it is not enabled. - */ - if (level < (inputsize == 52 && stride == 9 ? -1 : 0)) { - return false; - } - - startsizecheck = inputsize - ((3 - level) * stride + grainsize); - if (startsizecheck < 1 || startsizecheck > stride + 4) { - return false; - } + int sl0, sl2, startlevel, granulebits, levels; + int s1_min_iasize, s1_max_iasize; + sl0 = extract32(tcr, 6, 2); if (is_aa64) { + /* + * AArch64.S2InvalidSL: Interpretation of SL depends on the page size, + * so interleave AArch64.S2StartLevel. + */ switch (stride) { - case 13: /* 64KB Pages. */ - if (level == 0 || (level == 1 && outputsize <= 42)) { - return false; + case 9: /* 4KB */ + /* SL2 is RES0 unless DS=1 & 4KB granule. */ + sl2 = extract64(tcr, 33, 1); + if (ds && sl2) { + if (sl0 != 0) { + goto fail; + } + startlevel = -1; + } else { + startlevel = 2 - sl0; + switch (sl0) { + case 2: + if (arm_pamax(cpu) < 44) { + goto fail; + } + break; + case 3: + if (!cpu_isar_feature(aa64_st, cpu)) { + goto fail; + } + startlevel = 3; + break; + } } break; - case 11: /* 16KB Pages. */ - if (level == 0 || (level == 1 && outputsize <= 40)) { - return false; + case 11: /* 16KB */ + switch (sl0) { + case 2: + if (arm_pamax(cpu) < 42) { + goto fail; + } + break; + case 3: + if (!ds) { + goto fail; + } + break; } + startlevel = 3 - sl0; break; - case 9: /* 4KB Pages. */ - if (level == 0 && outputsize <= 42) { - return false; + case 13: /* 64KB */ + switch (sl0) { + case 2: + if (arm_pamax(cpu) < 44) { + goto fail; + } + break; + case 3: + goto fail; } + startlevel = 3 - sl0; break; default: g_assert_not_reached(); } - - /* Inputsize checks. */ - if (inputsize > outputsize && - (arm_el_is_aa64(&cpu->env, 1) || inputsize > 40)) { - /* This is CONSTRAINED UNPREDICTABLE and we choose to fault. */ - return false; - } } else { - /* AArch32 only supports 4KB pages. Assert on that. */ + /* + * Things are simpler for AArch32 EL2, with only 4k pages. + * There is no separate S2InvalidSL function, but AArch32.S2Walk + * begins with walkparms.sl0 in {'1x'}. + */ assert(stride == 9); - - if (level == 0) { - return false; + if (sl0 >= 2) { + goto fail; } + startlevel = 2 - sl0; } - return true; + + /* AArch{64,32}.S2InconsistentSL are functionally equivalent. */ + levels = 3 - startlevel; + granulebits = stride + 3; + + s1_min_iasize = levels * stride + granulebits + 1; + s1_max_iasize = s1_min_iasize + (stride - 1) + 4; + + if (iasize >= s1_min_iasize && iasize <= s1_max_iasize) { + return startlevel; + } + + fail: + return INT_MIN; +} + +static bool lpae_block_desc_valid(ARMCPU *cpu, bool ds, + ARMGranuleSize gran, int level) +{ + /* + * See pseudocode AArch46.BlockDescSupported(): block descriptors + * are not valid at all levels, depending on the page size. + */ + switch (gran) { + case Gran4K: + return (level == 0 && ds) || level == 1 || level == 2; + case Gran16K: + return (level == 1 && ds) || level == 2; + case Gran64K: + return (level == 1 && arm_pamax(cpu) == 52) || level == 2; + default: + g_assert_not_reached(); + } +} + +static bool nv_nv1_enabled(CPUARMState *env, S1Translate *ptw) +{ + uint64_t hcr = arm_hcr_el2_eff_secstate(env, ptw->in_space); + return (hcr & (HCR_NV | HCR_NV1)) == (HCR_NV | HCR_NV1); } /** @@ -947,52 +1665,44 @@ static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level, * the WnR bit is never set (the caller must do this). * * @env: CPUARMState + * @ptw: Current and next stage parameters for the walk. * @address: virtual address to get physical address for * @access_type: MMU_DATA_LOAD, MMU_DATA_STORE or MMU_INST_FETCH - * @mmu_idx: MMU index indicating required translation regime - * @s1_is_el0: if @mmu_idx is ARMMMUIdx_Stage2 (so this is a stage 2 page - * table walk), must be true if this is stage 2 of a stage 1+2 - * walk for an EL0 access. If @mmu_idx is anything else, - * @s1_is_el0 is ignored. - * @phys_ptr: set to the physical address corresponding to the virtual address - * @attrs: set to the memory transaction attributes to use - * @prot: set to the permissions for the page containing phys_ptr - * @page_size_ptr: set to the size of the page containing phys_ptr + * @result: set on translation success, * @fi: set to fault info if the translation fails - * @cacheattrs: (if non-NULL) set to the cacheability/shareability attributes */ -static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - bool s1_is_el0, hwaddr *phys_ptr, - MemTxAttrs *txattrs, int *prot, - target_ulong *page_size_ptr, - ARMMMUFaultInfo *fi, ARMCacheAttrs *cacheattrs) +static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, + uint64_t address, + MMUAccessType access_type, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { ARMCPU *cpu = env_archcpu(env); - /* Read an LPAE long-descriptor translation table. */ - ARMFaultType fault_type = ARMFault_Translation; - uint32_t level; + ARMMMUIdx mmu_idx = ptw->in_mmu_idx; + int32_t level; ARMVAParameters param; uint64_t ttbr; hwaddr descaddr, indexmask, indexmask_grainsize; uint32_t tableattrs; target_ulong page_size; - uint32_t attrs; + uint64_t attrs; int32_t stride; int addrsize, inputsize, outputsize; - TCR *tcr = regime_tcr(env, mmu_idx); - int ap, ns, xn, pxn; + uint64_t tcr = regime_tcr(env, mmu_idx); + int ap, xn, pxn; uint32_t el = regime_el(env, mmu_idx); uint64_t descaddrmask; bool aarch64 = arm_el_is_aa64(env, el); - bool guarded = false; + uint64_t descriptor, new_descriptor; + ARMSecuritySpace out_space; + bool device; /* TODO: This code does not support shareability levels. */ if (aarch64) { int ps; param = aa64_va_parameters(env, address, mmu_idx, - access_type != MMU_INST_FETCH); + access_type != MMU_INST_FETCH, + !arm_el_is_aa64(env, 1)); level = 0; /* @@ -1005,8 +1715,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, * so our choice is to always raise the fault. */ if (param.tsz_oob) { - fault_type = ARMFault_Translation; - goto do_fault; + goto do_translation_fault; } addrsize = 64 - 8 * param.tbi; @@ -1021,6 +1730,14 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, ps = MIN(ps, param.ps); assert(ps < ARRAY_SIZE(pamax_map)); outputsize = pamax_map[ps]; + + /* + * With LPA2, the effective output address (OA) size is at most 48 bits + * unless TCR.DS == 1 + */ + if (!param.ds && param.gran != Gran64K) { + outputsize = MIN(outputsize, 48); + } } else { param = aa32_va_parameters(env, address, mmu_idx); level = 1; @@ -1043,18 +1760,11 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, addrsize - inputsize); if (-top_bits != param.select) { /* The gap between the two regions is a Translation fault */ - fault_type = ARMFault_Translation; - goto do_fault; + goto do_translation_fault; } } - if (param.using64k) { - stride = 13; - } else if (param.using16k) { - stride = 11; - } else { - stride = 9; - } + stride = arm_granule_bits(param.gran) - 3; /* * Note that QEMU ignores shareability and cacheability attributes, @@ -1076,10 +1786,10 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, * Translation table walk disabled => Translation fault on TLB miss * Note: This is always 0 on 64-bit EL2 and EL3. */ - goto do_fault; + goto do_translation_fault; } - if (mmu_idx != ARMMMUIdx_Stage2 && mmu_idx != ARMMMUIdx_Stage2_S) { + if (!regime_is_stage2(mmu_idx)) { /* * The starting level depends on the virtual address size (which can * be up to 48 bits) and the translation granule size. It indicates @@ -1094,41 +1804,11 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, */ level = 4 - (inputsize - 4) / stride; } else { - /* - * For stage 2 translations the starting level is specified by the - * VTCR_EL2.SL0 field (whose interpretation depends on the page size) - */ - uint32_t sl0 = extract32(tcr->raw_tcr, 6, 2); - uint32_t sl2 = extract64(tcr->raw_tcr, 33, 1); - uint32_t startlevel; - bool ok; - - /* SL2 is RES0 unless DS=1 & 4kb granule. */ - if (param.ds && stride == 9 && sl2) { - if (sl0 != 0) { - level = 0; - fault_type = ARMFault_Translation; - goto do_fault; - } - startlevel = -1; - } else if (!aarch64 || stride == 9) { - /* AArch32 or 4KB pages */ - startlevel = 2 - sl0; - - if (cpu_isar_feature(aa64_st, cpu)) { - startlevel &= 3; - } - } else { - /* 16KB or 64KB pages */ - startlevel = 3 - sl0; - } - - /* Check that the starting level is valid. */ - ok = check_s2_mmu_setup(cpu, aarch64, startlevel, - inputsize, stride, outputsize); - if (!ok) { - fault_type = ARMFault_Translation; - goto do_fault; + int startlevel = check_s2_mmu_setup(cpu, aarch64, tcr, param.ds, + inputsize, stride); + if (startlevel == INT_MIN) { + level = 0; + goto do_translation_fault; } level = startlevel; } @@ -1150,7 +1830,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, descaddr |= extract64(ttbr, 2, 4) << 48; } else if (descaddr >> outputsize) { level = 0; - fault_type = ARMFault_AddressSize; + fi->type = ARMFault_AddressSize; goto do_fault; } @@ -1176,150 +1856,307 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, descaddrmask = MAKE_64BIT_MASK(0, 40); } descaddrmask &= ~indexmask_grainsize; + tableattrs = 0; + + next_level: + descaddr |= (address >> (stride * (4 - level))) & indexmask; + descaddr &= ~7ULL; /* - * Secure accesses start with the page table in secure memory and - * can be downgraded to non-secure at any step. Non-secure accesses - * remain non-secure. We implement this by just ORing in the NSTable/NS - * bits at each step. + * Process the NSTable bit from the previous level. This changes + * the table address space and the output space from Secure to + * NonSecure. With RME, the EL3 translation regime does not change + * from Root to NonSecure. */ - tableattrs = regime_is_secure(env, mmu_idx) ? 0 : (1 << 4); - for (;;) { - uint64_t descriptor; - bool nstable; + if (ptw->in_space == ARMSS_Secure + && !regime_is_stage2(mmu_idx) + && extract32(tableattrs, 4, 1)) { + /* + * Stage2_S -> Stage2 or Phys_S -> Phys_NS + * Assert the relative order of the secure/non-secure indexes. + */ + QEMU_BUILD_BUG_ON(ARMMMUIdx_Phys_S + 1 != ARMMMUIdx_Phys_NS); + QEMU_BUILD_BUG_ON(ARMMMUIdx_Stage2_S + 1 != ARMMMUIdx_Stage2); + ptw->in_ptw_idx += 1; + ptw->in_space = ARMSS_NonSecure; + } - descaddr |= (address >> (stride * (4 - level))) & indexmask; - descaddr &= ~7ULL; - nstable = extract32(tableattrs, 4, 1); - descriptor = arm_ldq_ptw(env, descaddr, !nstable, mmu_idx, fi); + if (!S1_ptw_translate(env, ptw, descaddr, fi)) { + goto do_fault; + } + descriptor = arm_ldq_ptw(env, ptw, fi); + if (fi->type != ARMFault_None) { + goto do_fault; + } + new_descriptor = descriptor; + + restart_atomic_update: + if (!(descriptor & 1) || + (!(descriptor & 2) && + !lpae_block_desc_valid(cpu, param.ds, param.gran, level))) { + /* Invalid, or a block descriptor at an invalid level */ + goto do_translation_fault; + } + + descaddr = descriptor & descaddrmask; + + /* + * For FEAT_LPA and PS=6, bits [51:48] of descaddr are in [15:12] + * of descriptor. For FEAT_LPA2 and effective DS, bits [51:50] of + * descaddr are in [9:8]. Otherwise, if descaddr is out of range, + * raise AddressSizeFault. + */ + if (outputsize > 48) { + if (param.ds) { + descaddr |= extract64(descriptor, 8, 2) << 50; + } else { + descaddr |= extract64(descriptor, 12, 4) << 48; + } + } else if (descaddr >> outputsize) { + fi->type = ARMFault_AddressSize; + goto do_fault; + } + + if ((descriptor & 2) && (level < 3)) { + /* + * Table entry. The top five bits are attributes which may + * propagate down through lower levels of the table (and + * which are all arranged so that 0 means "no effect", so + * we can gather them up by ORing in the bits at each level). + */ + tableattrs |= extract64(descriptor, 59, 5); + level++; + indexmask = indexmask_grainsize; + goto next_level; + } + + /* + * Block entry at level 1 or 2, or page entry at level 3. + * These are basically the same thing, although the number + * of bits we pull in from the vaddr varies. Note that although + * descaddrmask masks enough of the low bits of the descriptor + * to give a correct page or table address, the address field + * in a block descriptor is smaller; so we need to explicitly + * clear the lower bits here before ORing in the low vaddr bits. + * + * Afterward, descaddr is the final physical address. + */ + page_size = (1ULL << ((stride * (4 - level)) + 3)); + descaddr &= ~(hwaddr)(page_size - 1); + descaddr |= (address & (page_size - 1)); + + if (likely(!ptw->in_debug)) { + /* + * Access flag. + * If HA is enabled, prepare to update the descriptor below. + * Otherwise, pass the access fault on to software. + */ + if (!(descriptor & (1 << 10))) { + if (param.ha) { + new_descriptor |= 1 << 10; /* AF */ + } else { + fi->type = ARMFault_AccessFlag; + goto do_fault; + } + } + + /* + * Dirty Bit. + * If HD is enabled, pre-emptively set/clear the appropriate AP/S2AP + * bit for writeback. The actual write protection test may still be + * overridden by tableattrs, to be merged below. + */ + if (param.hd + && extract64(descriptor, 51, 1) /* DBM */ + && access_type == MMU_DATA_STORE) { + if (regime_is_stage2(mmu_idx)) { + new_descriptor |= 1ull << 7; /* set S2AP[1] */ + } else { + new_descriptor &= ~(1ull << 7); /* clear AP[2] */ + } + } + } + + /* + * Extract attributes from the (modified) descriptor, and apply + * table descriptors. Stage 2 table descriptors do not include + * any attribute fields. HPD disables all the table attributes + * except NSTable (which we have already handled). + */ + attrs = new_descriptor & (MAKE_64BIT_MASK(2, 10) | MAKE_64BIT_MASK(50, 14)); + if (!regime_is_stage2(mmu_idx)) { + if (!param.hpd) { + attrs |= extract64(tableattrs, 0, 2) << 53; /* XN, PXN */ + /* + * The sense of AP[1] vs APTable[0] is reversed, as APTable[0] == 1 + * means "force PL1 access only", which means forcing AP[1] to 0. + */ + attrs &= ~(extract64(tableattrs, 2, 1) << 6); /* !APT[0] => AP[1] */ + attrs |= extract32(tableattrs, 3, 1) << 7; /* APT[1] => AP[2] */ + } + } + + ap = extract32(attrs, 6, 2); + out_space = ptw->in_space; + if (regime_is_stage2(mmu_idx)) { + /* + * R_GYNXY: For stage2 in Realm security state, bit 55 is NS. + * The bit remains ignored for other security states. + * R_YMCSL: Executing an insn fetched from non-Realm causes + * a stage2 permission fault. + */ + if (out_space == ARMSS_Realm && extract64(attrs, 55, 1)) { + out_space = ARMSS_NonSecure; + result->f.prot = get_S2prot_noexecute(ap); + } else { + xn = extract64(attrs, 53, 2); + result->f.prot = get_S2prot(env, ap, xn, ptw->in_s1_is_el0); + } + } else { + int nse, ns = extract32(attrs, 5, 1); + switch (out_space) { + case ARMSS_Root: + /* + * R_GVZML: Bit 11 becomes the NSE field in the EL3 regime. + * R_XTYPW: NSE and NS together select the output pa space. + */ + nse = extract32(attrs, 11, 1); + out_space = (nse << 1) | ns; + if (out_space == ARMSS_Secure && + !cpu_isar_feature(aa64_sel2, cpu)) { + out_space = ARMSS_NonSecure; + } + break; + case ARMSS_Secure: + if (ns) { + out_space = ARMSS_NonSecure; + } + break; + case ARMSS_Realm: + switch (mmu_idx) { + case ARMMMUIdx_Stage1_E0: + case ARMMMUIdx_Stage1_E1: + case ARMMMUIdx_Stage1_E1_PAN: + /* I_CZPRF: For Realm EL1&0 stage1, NS bit is RES0. */ + break; + case ARMMMUIdx_E2: + case ARMMMUIdx_E20_0: + case ARMMMUIdx_E20_2: + case ARMMMUIdx_E20_2_PAN: + /* + * R_LYKFZ, R_WGRZN: For Realm EL2 and EL2&1, + * NS changes the output to non-secure space. + */ + if (ns) { + out_space = ARMSS_NonSecure; + } + break; + default: + g_assert_not_reached(); + } + break; + case ARMSS_NonSecure: + /* R_QRMFF: For NonSecure state, the NS bit is RES0. */ + break; + default: + g_assert_not_reached(); + } + xn = extract64(attrs, 54, 1); + pxn = extract64(attrs, 53, 1); + + if (el == 1 && nv_nv1_enabled(env, ptw)) { + /* + * With FEAT_NV, when HCR_EL2.{NV,NV1} == {1,1}, the block/page + * descriptor bit 54 holds PXN, 53 is RES0, and the effective value + * of UXN is 0. Similarly for bits 59 and 60 in table descriptors + * (which we have already folded into bits 53 and 54 of attrs). + * AP[1] (descriptor bit 6, our ap bit 0) is treated as 0. + * Similarly, APTable[0] from the table descriptor is treated as 0; + * we already folded this into AP[1] and squashing that to 0 does + * the right thing. + */ + pxn = xn; + xn = 0; + ap &= ~1; + } + /* + * Note that we modified ptw->in_space earlier for NSTable, but + * result->f.attrs retains a copy of the original security space. + */ + result->f.prot = get_S1prot(env, mmu_idx, aarch64, ap, xn, pxn, + result->f.attrs.space, out_space); + } + + if (!(result->f.prot & (1 << access_type))) { + fi->type = ARMFault_Permission; + goto do_fault; + } + + /* If FEAT_HAFDBS has made changes, update the PTE. */ + if (new_descriptor != descriptor) { + new_descriptor = arm_casq_ptw(env, descriptor, new_descriptor, ptw, fi); if (fi->type != ARMFault_None) { goto do_fault; } - - if (!(descriptor & 1) || - (!(descriptor & 2) && (level == 3))) { - /* Invalid, or the Reserved level 3 encoding */ - goto do_fault; - } - - descaddr = descriptor & descaddrmask; - /* - * For FEAT_LPA and PS=6, bits [51:48] of descaddr are in [15:12] - * of descriptor. For FEAT_LPA2 and effective DS, bits [51:50] of - * descaddr are in [9:8]. Otherwise, if descaddr is out of range, - * raise AddressSizeFault. + * I_YZSVV says that if the in-memory descriptor has changed, + * then we must use the information in that new value + * (which might include a different output address, different + * attributes, or generate a fault). + * Restart the handling of the descriptor value from scratch. */ - if (outputsize > 48) { - if (param.ds) { - descaddr |= extract64(descriptor, 8, 2) << 50; - } else { - descaddr |= extract64(descriptor, 12, 4) << 48; - } - } else if (descaddr >> outputsize) { - fault_type = ARMFault_AddressSize; - goto do_fault; + if (new_descriptor != descriptor) { + descriptor = new_descriptor; + goto restart_atomic_update; } + } - if ((descriptor & 2) && (level < 3)) { - /* - * Table entry. The top five bits are attributes which may - * propagate down through lower levels of the table (and - * which are all arranged so that 0 means "no effect", so - * we can gather them up by ORing in the bits at each level). - */ - tableattrs |= extract64(descriptor, 59, 5); - level++; - indexmask = indexmask_grainsize; - continue; - } + result->f.attrs.space = out_space; + result->f.attrs.secure = arm_space_is_secure(out_space); + + if (regime_is_stage2(mmu_idx)) { + result->cacheattrs.is_s2_format = true; + result->cacheattrs.attrs = extract32(attrs, 2, 4); /* - * Block entry at level 1 or 2, or page entry at level 3. - * These are basically the same thing, although the number - * of bits we pull in from the vaddr varies. Note that although - * descaddrmask masks enough of the low bits of the descriptor - * to give a correct page or table address, the address field - * in a block descriptor is smaller; so we need to explicitly - * clear the lower bits here before ORing in the low vaddr bits. + * Security state does not really affect HCR_EL2.FWB; + * we only need to filter FWB for aa32 or other FEAT. */ - page_size = (1ULL << ((stride * (4 - level)) + 3)); - descaddr &= ~(page_size - 1); - descaddr |= (address & (page_size - 1)); - /* Extract attributes from the descriptor */ - attrs = extract64(descriptor, 2, 10) - | (extract64(descriptor, 52, 12) << 10); - - if (mmu_idx == ARMMMUIdx_Stage2 || mmu_idx == ARMMMUIdx_Stage2_S) { - /* Stage 2 table descriptors do not include any attribute fields */ - break; - } - /* Merge in attributes from table descriptors */ - attrs |= nstable << 3; /* NS */ - guarded = extract64(descriptor, 50, 1); /* GP */ - if (param.hpd) { - /* HPD disables all the table attributes except NSTable. */ - break; - } - attrs |= extract32(tableattrs, 0, 2) << 11; /* XN, PXN */ - /* - * The sense of AP[1] vs APTable[0] is reversed, as APTable[0] == 1 - * means "force PL1 access only", which means forcing AP[1] to 0. - */ - attrs &= ~(extract32(tableattrs, 2, 1) << 4); /* !APT[0] => AP[1] */ - attrs |= extract32(tableattrs, 3, 1) << 5; /* APT[1] => AP[2] */ - break; - } - /* - * Here descaddr is the final physical address, and attributes - * are all in attrs. - */ - fault_type = ARMFault_AccessFlag; - if ((attrs & (1 << 8)) == 0) { - /* Access flag */ - goto do_fault; - } - - ap = extract32(attrs, 4, 2); - - if (mmu_idx == ARMMMUIdx_Stage2 || mmu_idx == ARMMMUIdx_Stage2_S) { - ns = mmu_idx == ARMMMUIdx_Stage2; - xn = extract32(attrs, 11, 2); - *prot = get_S2prot(env, ap, xn, s1_is_el0); - } else { - ns = extract32(attrs, 3, 1); - xn = extract32(attrs, 12, 1); - pxn = extract32(attrs, 11, 1); - *prot = get_S1prot(env, mmu_idx, aarch64, ap, ns, xn, pxn); - } - - fault_type = ARMFault_Permission; - if (!(*prot & (1 << access_type))) { - goto do_fault; - } - - if (ns) { - /* - * The NS bit will (as required by the architecture) have no effect if - * the CPU doesn't support TZ or this is a non-secure translation - * regime, because the attribute will already be non-secure. - */ - txattrs->secure = false; - } - /* When in aarch64 mode, and BTI is enabled, remember GP in the IOTLB. */ - if (aarch64 && guarded && cpu_isar_feature(aa64_bti, cpu)) { - arm_tlb_bti_gp(txattrs) = true; - } - - if (mmu_idx == ARMMMUIdx_Stage2 || mmu_idx == ARMMMUIdx_Stage2_S) { - cacheattrs->is_s2_format = true; - cacheattrs->attrs = extract32(attrs, 0, 4); + device = S2_attrs_are_device(arm_hcr_el2_eff(env), + result->cacheattrs.attrs); } else { /* Index into MAIR registers for cache attributes */ - uint8_t attrindx = extract32(attrs, 0, 3); + uint8_t attrindx = extract32(attrs, 2, 3); uint64_t mair = env->cp15.mair_el[regime_el(env, mmu_idx)]; assert(attrindx <= 7); - cacheattrs->is_s2_format = false; - cacheattrs->attrs = extract64(mair, attrindx * 8, 8); + result->cacheattrs.is_s2_format = false; + result->cacheattrs.attrs = extract64(mair, attrindx * 8, 8); + + /* When in aarch64 mode, and BTI is enabled, remember GP in the TLB. */ + if (aarch64 && cpu_isar_feature(aa64_bti, cpu)) { + result->f.extra.arm.guarded = extract64(attrs, 50, 1); /* GP */ + } + device = S1_attrs_are_device(result->cacheattrs.attrs); + } + + /* + * Enable alignment checks on Device memory. + * + * Per R_XCHFJ, this check is mis-ordered. The correct ordering + * for alignment, permission, and stage 2 faults should be: + * - Alignment fault caused by the memory type + * - Permission fault + * - A stage 2 fault on the memory access + * but due to the way the TCG softmmu TLB operates, we will have + * implicitly done the permission check and the stage2 lookup in + * finding the TLB entry, so the alignment check cannot be done sooner. + * + * In v7, for a CPU without the Virtualization Extensions this + * access is UNPREDICTABLE; we choose to make it take the alignment + * fault as is required for a v7VE CPU. (QEMU doesn't emulate any + * CPUs with ARM_FEATURE_LPAE but not ARM_FEATURE_V7VE anyway.) + */ + if (device) { + result->f.tlb_fill_flags |= TLB_CHECK_ALIGNED; } /* @@ -1328,43 +2165,50 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, * that case comes from TCR_ELx, which we extracted earlier. */ if (param.ds) { - cacheattrs->shareability = param.sh; + result->cacheattrs.shareability = param.sh; } else { - cacheattrs->shareability = extract32(attrs, 6, 2); + result->cacheattrs.shareability = extract32(attrs, 8, 2); } - *phys_ptr = descaddr; - *page_size_ptr = page_size; + result->f.phys_addr = descaddr; + result->f.lg_page_size = ctz64(page_size); return false; -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_Stage2 || - mmu_idx == ARMMMUIdx_Stage2_S); - fi->s1ns = mmu_idx == ARMMMUIdx_Stage2; + do_translation_fault: + fi->type = ARMFault_Translation; + do_fault: + if (fi->s1ptw) { + /* Retain the existing stage 2 fi->level */ + assert(fi->stage2); + } else { + fi->level = level; + fi->stage2 = regime_is_stage2(mmu_idx); + } + fi->s1ns = fault_s1ns(ptw->in_space, mmu_idx); return true; } -static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, int *prot, +static bool get_phys_addr_pmsav5(CPUARMState *env, + S1Translate *ptw, + uint32_t address, + MMUAccessType access_type, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { int n; uint32_t mask; uint32_t base; + ARMMMUIdx mmu_idx = ptw->in_mmu_idx; bool is_user = regime_is_user(env, mmu_idx); - if (regime_translation_disabled(env, mmu_idx)) { + if (regime_translation_disabled(env, mmu_idx, ptw->in_space)) { /* MPU disabled. */ - *phys_ptr = address; - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + result->f.phys_addr = address; + result->f.prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return false; } - *phys_ptr = address; + result->f.phys_addr = address; for (n = 7; n >= 0; n--) { base = env->cp15.c6_region[n]; if ((base & 1) == 0) { @@ -1400,16 +2244,16 @@ static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address, fi->level = 1; return true; } - *prot = PAGE_READ | PAGE_WRITE; + result->f.prot = PAGE_READ | PAGE_WRITE; break; case 2: - *prot = PAGE_READ; + result->f.prot = PAGE_READ; if (!is_user) { - *prot |= PAGE_WRITE; + result->f.prot |= PAGE_WRITE; } break; case 3: - *prot = PAGE_READ | PAGE_WRITE; + result->f.prot = PAGE_READ | PAGE_WRITE; break; case 5: if (is_user) { @@ -1417,10 +2261,10 @@ static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address, fi->level = 1; return true; } - *prot = PAGE_READ; + result->f.prot = PAGE_READ; break; case 6: - *prot = PAGE_READ; + result->f.prot = PAGE_READ; break; default: /* Bad permission. */ @@ -1428,12 +2272,12 @@ static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address, fi->level = 1; return true; } - *prot |= PAGE_EXEC; + result->f.prot |= PAGE_EXEC; return false; } static void get_phys_addr_pmsav7_default(CPUARMState *env, ARMMMUIdx mmu_idx, - int32_t address, int *prot) + int32_t address, uint8_t *prot) { if (!arm_feature(env, ARM_FEATURE_M)) { *prot = PAGE_READ | PAGE_WRITE; @@ -1489,7 +2333,7 @@ static bool m_is_system_region(CPUARMState *env, uint32_t address) } static bool pmsav7_use_background_region(ARMCPU *cpu, ARMMMUIdx mmu_idx, - bool is_user) + bool is_secure, bool is_user) { /* * Return true if we should use the default memory map as a @@ -1502,28 +2346,34 @@ static bool pmsav7_use_background_region(ARMCPU *cpu, ARMMMUIdx mmu_idx, } if (arm_feature(env, ARM_FEATURE_M)) { - return env->v7m.mpu_ctrl[regime_is_secure(env, mmu_idx)] - & R_V7M_MPU_CTRL_PRIVDEFENA_MASK; - } else { - return regime_sctlr(env, mmu_idx) & SCTLR_BR; + return env->v7m.mpu_ctrl[is_secure] & R_V7M_MPU_CTRL_PRIVDEFENA_MASK; } + + if (mmu_idx == ARMMMUIdx_Stage2) { + return false; + } + + return regime_sctlr(env, mmu_idx) & SCTLR_BR; } -static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, int *prot, - target_ulong *page_size, +static bool get_phys_addr_pmsav7(CPUARMState *env, + S1Translate *ptw, + uint32_t address, + MMUAccessType access_type, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { ARMCPU *cpu = env_archcpu(env); int n; + ARMMMUIdx mmu_idx = ptw->in_mmu_idx; bool is_user = regime_is_user(env, mmu_idx); + bool secure = arm_space_is_secure(ptw->in_space); - *phys_ptr = address; - *page_size = TARGET_PAGE_SIZE; - *prot = 0; + result->f.phys_addr = address; + result->f.lg_page_size = TARGET_PAGE_BITS; + result->f.prot = 0; - if (regime_translation_disabled(env, mmu_idx) || + if (regime_translation_disabled(env, mmu_idx, ptw->in_space) || m_is_ppb_region(env, address)) { /* * MPU disabled or M profile PPB access: use default memory map. @@ -1533,7 +2383,7 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, * which always does a direct read using address_space_ldl(), rather * than going via this function, so we don't need to check that here. */ - get_phys_addr_pmsav7_default(env, mmu_idx, address, prot); + get_phys_addr_pmsav7_default(env, mmu_idx, address, &result->f.prot); } else { /* MPU enabled */ for (n = (int)cpu->pmsav7_dregion - 1; n >= 0; n--) { /* region search */ @@ -1575,7 +2425,7 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, if (ranges_overlap(base, rmask, address & TARGET_PAGE_MASK, TARGET_PAGE_SIZE)) { - *page_size = 1; + result->f.lg_page_size = 0; } continue; } @@ -1613,18 +2463,19 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, continue; } if (rsize < TARGET_PAGE_BITS) { - *page_size = 1 << rsize; + result->f.lg_page_size = rsize; } break; } if (n == -1) { /* no hits */ - if (!pmsav7_use_background_region(cpu, mmu_idx, is_user)) { + if (!pmsav7_use_background_region(cpu, mmu_idx, secure, is_user)) { /* background fault */ fi->type = ARMFault_Background; return true; } - get_phys_addr_pmsav7_default(env, mmu_idx, address, prot); + get_phys_addr_pmsav7_default(env, mmu_idx, address, + &result->f.prot); } else { /* a MPU hit! */ uint32_t ap = extract32(env->pmsav7.dracr[n], 8, 3); uint32_t xn = extract32(env->pmsav7.dracr[n], 12, 1); @@ -1641,16 +2492,16 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, case 5: break; /* no access */ case 3: - *prot |= PAGE_WRITE; + result->f.prot |= PAGE_WRITE; /* fall through */ case 2: case 6: - *prot |= PAGE_READ | PAGE_EXEC; + result->f.prot |= PAGE_READ | PAGE_EXEC; break; case 7: /* for v7M, same as 6; for R profile a reserved value */ if (arm_feature(env, ARM_FEATURE_M)) { - *prot |= PAGE_READ | PAGE_EXEC; + result->f.prot |= PAGE_READ | PAGE_EXEC; break; } /* fall through */ @@ -1666,16 +2517,16 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, case 1: case 2: case 3: - *prot |= PAGE_WRITE; + result->f.prot |= PAGE_WRITE; /* fall through */ case 5: case 6: - *prot |= PAGE_READ | PAGE_EXEC; + result->f.prot |= PAGE_READ | PAGE_EXEC; break; case 7: /* for v7M, same as 6; for R profile a reserved value */ if (arm_feature(env, ARM_FEATURE_M)) { - *prot |= PAGE_READ | PAGE_EXEC; + result->f.prot |= PAGE_READ | PAGE_EXEC; break; } /* fall through */ @@ -1688,20 +2539,39 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, /* execute never */ if (xn) { - *prot &= ~PAGE_EXEC; + result->f.prot &= ~PAGE_EXEC; } } } fi->type = ARMFault_Permission; fi->level = 1; - return !(*prot & (1 << access_type)); + return !(result->f.prot & (1 << access_type)); +} + +static uint32_t *regime_rbar(CPUARMState *env, ARMMMUIdx mmu_idx, + uint32_t secure) +{ + if (regime_el(env, mmu_idx) == 2) { + return env->pmsav8.hprbar; + } else { + return env->pmsav8.rbar[secure]; + } +} + +static uint32_t *regime_rlar(CPUARMState *env, ARMMMUIdx mmu_idx, + uint32_t secure) +{ + if (regime_el(env, mmu_idx) == 2) { + return env->pmsav8.hprlar; + } else { + return env->pmsav8.rlar[secure]; + } } bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, MMUAccessType access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, MemTxAttrs *txattrs, - int *prot, bool *is_subpage, + bool secure, GetPhysAddrResult *result, ARMMMUFaultInfo *fi, uint32_t *mregion) { /* @@ -1710,25 +2580,36 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, * mregion is (if not NULL) set to the region number which matched, * or -1 if no region number is returned (MPU off, address did not * hit a region, address hit in multiple regions). - * We set is_subpage to true if the region hit doesn't cover the - * entire TARGET_PAGE the address is within. + * If the region hit doesn't cover the entire TARGET_PAGE the address + * is within, then we set the result page_size to 1 to force the + * memory system to use a subpage. */ ARMCPU *cpu = env_archcpu(env); bool is_user = regime_is_user(env, mmu_idx); - uint32_t secure = regime_is_secure(env, mmu_idx); int n; int matchregion = -1; bool hit = false; uint32_t addr_page_base = address & TARGET_PAGE_MASK; uint32_t addr_page_limit = addr_page_base + (TARGET_PAGE_SIZE - 1); + int region_counter; - *is_subpage = false; - *phys_ptr = address; - *prot = 0; + if (regime_el(env, mmu_idx) == 2) { + region_counter = cpu->pmsav8r_hdregion; + } else { + region_counter = cpu->pmsav7_dregion; + } + + result->f.lg_page_size = TARGET_PAGE_BITS; + result->f.phys_addr = address; + result->f.prot = 0; if (mregion) { *mregion = -1; } + if (mmu_idx == ARMMMUIdx_Stage2) { + fi->stage2 = true; + } + /* * Unlike the ARM ARM pseudocode, we don't need to check whether this * was an exception vector read from the vector table (which is always @@ -1736,26 +2617,36 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, * are done in arm_v7m_load_vector(), which always does a direct * read using address_space_ldl(), rather than going via this function. */ - if (regime_translation_disabled(env, mmu_idx)) { /* MPU disabled */ + if (regime_translation_disabled(env, mmu_idx, arm_secure_to_space(secure))) { + /* MPU disabled */ hit = true; } else if (m_is_ppb_region(env, address)) { hit = true; } else { - if (pmsav7_use_background_region(cpu, mmu_idx, is_user)) { + if (pmsav7_use_background_region(cpu, mmu_idx, secure, is_user)) { hit = true; } - for (n = (int)cpu->pmsav7_dregion - 1; n >= 0; n--) { + uint32_t bitmask; + if (arm_feature(env, ARM_FEATURE_M)) { + bitmask = 0x1f; + } else { + bitmask = 0x3f; + fi->level = 0; + } + + for (n = region_counter - 1; n >= 0; n--) { /* region search */ /* - * Note that the base address is bits [31:5] from the register - * with bits [4:0] all zeroes, but the limit address is bits - * [31:5] from the register with bits [4:0] all ones. + * Note that the base address is bits [31:x] from the register + * with bits [x-1:0] all zeroes, but the limit address is bits + * [31:x] from the register with bits [x:0] all ones. Where x is + * 5 for Cortex-M and 6 for Cortex-R */ - uint32_t base = env->pmsav8.rbar[secure][n] & ~0x1f; - uint32_t limit = env->pmsav8.rlar[secure][n] | 0x1f; + uint32_t base = regime_rbar(env, mmu_idx, secure)[n] & ~bitmask; + uint32_t limit = regime_rlar(env, mmu_idx, secure)[n] | bitmask; - if (!(env->pmsav8.rlar[secure][n] & 0x1)) { + if (!(regime_rlar(env, mmu_idx, secure)[n] & 0x1)) { /* Region disabled */ continue; } @@ -1774,13 +2665,13 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, ranges_overlap(base, limit - base + 1, addr_page_base, TARGET_PAGE_SIZE)) { - *is_subpage = true; + result->f.lg_page_size = 0; } continue; } if (base > addr_page_base || limit < addr_page_limit) { - *is_subpage = true; + result->f.lg_page_size = 0; } if (matchregion != -1) { @@ -1789,7 +2680,9 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, * PMSAv7 where highest-numbered-region wins) */ fi->type = ARMFault_Permission; - fi->level = 1; + if (arm_feature(env, ARM_FEATURE_M)) { + fi->level = 1; + } return true; } @@ -1799,21 +2692,26 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, } if (!hit) { - /* background fault */ - fi->type = ARMFault_Background; + if (arm_feature(env, ARM_FEATURE_M)) { + fi->type = ARMFault_Background; + } else { + fi->type = ARMFault_Permission; + } return true; } if (matchregion == -1) { /* hit using the background region */ - get_phys_addr_pmsav7_default(env, mmu_idx, address, prot); + get_phys_addr_pmsav7_default(env, mmu_idx, address, &result->f.prot); } else { - uint32_t ap = extract32(env->pmsav8.rbar[secure][matchregion], 1, 2); - uint32_t xn = extract32(env->pmsav8.rbar[secure][matchregion], 0, 1); + uint32_t matched_rbar = regime_rbar(env, mmu_idx, secure)[matchregion]; + uint32_t matched_rlar = regime_rlar(env, mmu_idx, secure)[matchregion]; + uint32_t ap = extract32(matched_rbar, 1, 2); + uint32_t xn = extract32(matched_rbar, 0, 1); bool pxn = false; if (arm_feature(env, ARM_FEATURE_V8_1M)) { - pxn = extract32(env->pmsav8.rlar[secure][matchregion], 4, 1); + pxn = extract32(matched_rlar, 4, 1); } if (m_is_system_region(env, address)) { @@ -1821,22 +2719,47 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, xn = 1; } - *prot = simple_ap_to_rw_prot(env, mmu_idx, ap); - if (*prot && !xn && !(pxn && !is_user)) { - *prot |= PAGE_EXEC; + if (regime_el(env, mmu_idx) == 2) { + result->f.prot = simple_ap_to_rw_prot_is_user(ap, + mmu_idx != ARMMMUIdx_E2); + } else { + result->f.prot = simple_ap_to_rw_prot(env, mmu_idx, ap); } - /* - * We don't need to look the attribute up in the MAIR0/MAIR1 - * registers because that only tells us about cacheability. - */ + + if (!arm_feature(env, ARM_FEATURE_M)) { + uint8_t attrindx = extract32(matched_rlar, 1, 3); + uint64_t mair = env->cp15.mair_el[regime_el(env, mmu_idx)]; + uint8_t sh = extract32(matched_rlar, 3, 2); + + if (regime_sctlr(env, mmu_idx) & SCTLR_WXN && + result->f.prot & PAGE_WRITE && mmu_idx != ARMMMUIdx_Stage2) { + xn = 0x1; + } + + if ((regime_el(env, mmu_idx) == 1) && + regime_sctlr(env, mmu_idx) & SCTLR_UWXN && ap == 0x1) { + pxn = 0x1; + } + + result->cacheattrs.is_s2_format = false; + result->cacheattrs.attrs = extract64(mair, attrindx * 8, 8); + result->cacheattrs.shareability = sh; + } + + if (result->f.prot && !xn && !(pxn && !is_user)) { + result->f.prot |= PAGE_EXEC; + } + if (mregion) { *mregion = matchregion; } } fi->type = ARMFault_Permission; - fi->level = 1; - return !(*prot & (1 << access_type)); + if (arm_feature(env, ARM_FEATURE_M)) { + fi->level = 1; + } + return !(result->f.prot & (1 << access_type)); } static bool v8m_is_sau_exempt(CPUARMState *env, @@ -1856,8 +2779,8 @@ static bool v8m_is_sau_exempt(CPUARMState *env, } void v8m_security_lookup(CPUARMState *env, uint32_t address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - V8M_SAttributes *sattrs) + MMUAccessType access_type, ARMMMUIdx mmu_idx, + bool is_secure, V8M_SAttributes *sattrs) { /* * Look up the security attributes for this address. Compare the @@ -1885,7 +2808,7 @@ void v8m_security_lookup(CPUARMState *env, uint32_t address, } if (idau_exempt || v8m_is_sau_exempt(env, address, access_type)) { - sattrs->ns = !regime_is_secure(env, mmu_idx); + sattrs->ns = !is_secure; return; } @@ -1964,19 +2887,21 @@ void v8m_security_lookup(CPUARMState *env, uint32_t address, } } -static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, MemTxAttrs *txattrs, - int *prot, target_ulong *page_size, +static bool get_phys_addr_pmsav8(CPUARMState *env, + S1Translate *ptw, + uint32_t address, + MMUAccessType access_type, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { - uint32_t secure = regime_is_secure(env, mmu_idx); V8M_SAttributes sattrs = {}; + ARMMMUIdx mmu_idx = ptw->in_mmu_idx; + bool secure = arm_space_is_secure(ptw->in_space); bool ret; - bool mpu_is_subpage; if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { - v8m_security_lookup(env, address, access_type, mmu_idx, &sattrs); + v8m_security_lookup(env, address, access_type, mmu_idx, + secure, &sattrs); if (access_type == MMU_INST_FETCH) { /* * Instruction fetches always use the MMU bank and the @@ -2002,9 +2927,9 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, } else { fi->type = ARMFault_QEMU_SFault; } - *page_size = sattrs.subpage ? 1 : TARGET_PAGE_SIZE; - *phys_ptr = address; - *prot = 0; + result->f.lg_page_size = sattrs.subpage ? 0 : TARGET_PAGE_BITS; + result->f.phys_addr = address; + result->f.prot = 0; return true; } } else { @@ -2014,7 +2939,8 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, * might downgrade a secure access to nonsecure. */ if (sattrs.ns) { - txattrs->secure = false; + result->f.attrs.secure = false; + result->f.attrs.space = ARMSS_NonSecure; } else if (!secure) { /* * NS access to S memory must fault. @@ -2027,17 +2953,19 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, * for M_FAKE_FSR_SFAULT in arm_v7m_cpu_do_interrupt(). */ fi->type = ARMFault_QEMU_SFault; - *page_size = sattrs.subpage ? 1 : TARGET_PAGE_SIZE; - *phys_ptr = address; - *prot = 0; + result->f.lg_page_size = sattrs.subpage ? 0 : TARGET_PAGE_BITS; + result->f.phys_addr = address; + result->f.prot = 0; return true; } } } - ret = pmsav8_mpu_lookup(env, address, access_type, mmu_idx, phys_ptr, - txattrs, prot, &mpu_is_subpage, fi, NULL); - *page_size = sattrs.subpage || mpu_is_subpage ? 1 : TARGET_PAGE_SIZE; + ret = pmsav8_mpu_lookup(env, address, access_type, mmu_idx, secure, + result, fi, NULL); + if (sattrs.subpage) { + result->f.lg_page_size = 0; + } return ret; } @@ -2050,14 +2978,14 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, * ref: shared/translation/attrs/S2AttrDecode() * .../S2ConvertAttrsHints() */ -static uint8_t convert_stage2_attrs(CPUARMState *env, uint8_t s2attrs) +static uint8_t convert_stage2_attrs(uint64_t hcr, uint8_t s2attrs) { uint8_t hiattr = extract32(s2attrs, 2, 2); uint8_t loattr = extract32(s2attrs, 0, 2); uint8_t hihint = 0, lohint = 0; if (hiattr != 0) { /* normal memory */ - if (arm_hcr_el2_eff(env) & HCR_CD) { /* cache disabled */ + if (hcr & HCR_CD) { /* cache disabled */ hiattr = loattr = 1; /* non-cacheable */ } else { if (hiattr != 1) { /* Write-through or write-back */ @@ -2103,12 +3031,16 @@ static uint8_t combine_cacheattr_nibble(uint8_t s1, uint8_t s2) * s1 and s2 for the HCR_EL2.FWB == 0 case, returning the * combined attributes in MAIR_EL1 format. */ -static uint8_t combined_attrs_nofwb(CPUARMState *env, +static uint8_t combined_attrs_nofwb(uint64_t hcr, ARMCacheAttrs s1, ARMCacheAttrs s2) { uint8_t s1lo, s2lo, s1hi, s2hi, s2_mair_attrs, ret_attrs; - s2_mair_attrs = convert_stage2_attrs(env, s2.attrs); + if (s2.is_s2_format) { + s2_mair_attrs = convert_stage2_attrs(hcr, s2.attrs); + } else { + s2_mair_attrs = s2.attrs; + } s1lo = extract32(s1.attrs, 0, 4); s2lo = extract32(s2_mair_attrs, 0, 4); @@ -2163,9 +3095,10 @@ static uint8_t force_cacheattr_nibble_wb(uint8_t attr) * s1 and s2 for the HCR_EL2.FWB == 1 case, returning the * combined attributes in MAIR_EL1 format. */ -static uint8_t combined_attrs_fwb(CPUARMState *env, - ARMCacheAttrs s1, ARMCacheAttrs s2) +static uint8_t combined_attrs_fwb(ARMCacheAttrs s1, ARMCacheAttrs s2) { + assert(s2.is_s2_format && !s1.is_s2_format); + switch (s2.attrs) { case 7: /* Use stage 1 attributes */ @@ -2209,13 +3142,13 @@ static uint8_t combined_attrs_fwb(CPUARMState *env, * @s1: Attributes from stage 1 walk * @s2: Attributes from stage 2 walk */ -static ARMCacheAttrs combine_cacheattrs(CPUARMState *env, +static ARMCacheAttrs combine_cacheattrs(uint64_t hcr, ARMCacheAttrs s1, ARMCacheAttrs s2) { ARMCacheAttrs ret; bool tagged = false; - assert(s2.is_s2_format && !s1.is_s2_format); + assert(!s1.is_s2_format); ret.is_s2_format = false; if (s1.attrs == 0xf0) { @@ -2236,10 +3169,10 @@ static ARMCacheAttrs combine_cacheattrs(CPUARMState *env, } /* Combine memory type and cacheability attributes */ - if (arm_hcr_el2_eff(env) & HCR_FWB) { - ret.attrs = combined_attrs_fwb(env, s1, s2); + if (hcr & HCR_FWB) { + ret.attrs = combined_attrs_fwb(s1, s2); } else { - ret.attrs = combined_attrs_nofwb(env, s1, s2); + ret.attrs = combined_attrs_nofwb(hcr, s1, s2); } /* @@ -2262,134 +3195,275 @@ static ARMCacheAttrs combine_cacheattrs(CPUARMState *env, return ret; } -/** - * get_phys_addr - get the physical address for this virtual address - * - * Find the physical address corresponding to the given virtual address, - * by doing a translation table walk on MMU based systems or using the - * MPU state on MPU based systems. - * - * Returns false if the translation was successful. Otherwise, phys_ptr, attrs, - * prot and page_size may not be filled in, and the populated fsr value provides - * information on why the translation aborted, in the format of a - * DFSR/IFSR fault register, with the following caveats: - * * we honour the short vs long DFSR format differences. - * * the WnR bit is never set (the caller must do this). - * * for PSMAv5 based systems we don't bother to return a full FSR format - * value. - * - * @env: CPUARMState - * @address: virtual address to get physical address for - * @access_type: 0 for read, 1 for write, 2 for execute - * @mmu_idx: MMU index indicating required translation regime - * @phys_ptr: set to the physical address corresponding to the virtual address - * @attrs: set to the memory transaction attributes to use - * @prot: set to the permissions for the page containing phys_ptr - * @page_size: set to the size of the page containing phys_ptr - * @fi: set to fault info if the translation fails - * @cacheattrs: (if non-NULL) set to the cacheability/shareability attributes +/* + * MMU disabled. S1 addresses within aa64 translation regimes are + * still checked for bounds -- see AArch64.S1DisabledOutput(). */ -bool get_phys_addr(CPUARMState *env, target_ulong address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, - target_ulong *page_size, - ARMMMUFaultInfo *fi, ARMCacheAttrs *cacheattrs) +static bool get_phys_addr_disabled(CPUARMState *env, + S1Translate *ptw, + target_ulong address, + MMUAccessType access_type, + GetPhysAddrResult *result, + ARMMMUFaultInfo *fi) { - ARMMMUIdx s1_mmu_idx = stage_1_mmu_idx(mmu_idx); + ARMMMUIdx mmu_idx = ptw->in_mmu_idx; + uint8_t memattr = 0x00; /* Device nGnRnE */ + uint8_t shareability = 0; /* non-shareable */ + int r_el; - if (mmu_idx != s1_mmu_idx) { - /* - * Call ourselves recursively to do the stage 1 and then stage 2 - * translations if mmu_idx is a two-stage regime. - */ - if (arm_feature(env, ARM_FEATURE_EL2)) { - hwaddr ipa; - int s2_prot; - int ret; - bool ipa_secure; - ARMCacheAttrs cacheattrs2 = {}; - ARMMMUIdx s2_mmu_idx; - bool is_el0; + switch (mmu_idx) { + case ARMMMUIdx_Stage2: + case ARMMMUIdx_Stage2_S: + case ARMMMUIdx_Phys_S: + case ARMMMUIdx_Phys_NS: + case ARMMMUIdx_Phys_Root: + case ARMMMUIdx_Phys_Realm: + break; - ret = get_phys_addr(env, address, access_type, s1_mmu_idx, &ipa, - attrs, prot, page_size, fi, cacheattrs); + default: + 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]; + int addrtop, tbi; - /* If S1 fails or S2 is disabled, return early. */ - if (ret || regime_translation_disabled(env, ARMMMUIdx_Stage2)) { - *phys_ptr = ipa; - return ret; + 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; } - ipa_secure = attrs->secure; - if (arm_is_secure_below_el3(env)) { - if (ipa_secure) { - attrs->secure = !(env->cp15.vstcr_el2.raw_tcr & VSTCR_SW); - } else { - attrs->secure = !(env->cp15.vtcr_el2.raw_tcr & VTCR_NSW); - } - } else { - assert(!ipa_secure); - } - - s2_mmu_idx = attrs->secure ? ARMMMUIdx_Stage2_S : ARMMMUIdx_Stage2; - is_el0 = mmu_idx == ARMMMUIdx_E10_0 || mmu_idx == ARMMMUIdx_SE10_0; - - /* S1 is done. Now do S2 translation. */ - ret = get_phys_addr_lpae(env, ipa, access_type, s2_mmu_idx, is_el0, - phys_ptr, attrs, &s2_prot, - page_size, fi, &cacheattrs2); - fi->s2addr = ipa; - /* Combine the S1 and S2 perms. */ - *prot &= s2_prot; - - /* If S2 fails, return early. */ - if (ret) { - return ret; - } - - /* Combine the S1 and S2 cache attributes. */ - if (arm_hcr_el2_eff(env) & HCR_DC) { - /* - * HCR.DC forces the first stage attributes to - * Normal Non-Shareable, - * Inner Write-Back Read-Allocate Write-Allocate, - * Outer Write-Back Read-Allocate Write-Allocate. - * Do not overwrite Tagged within attrs. - */ - if (cacheattrs->attrs != 0xf0) { - cacheattrs->attrs = 0xff; - } - cacheattrs->shareability = 0; - } - *cacheattrs = combine_cacheattrs(env, *cacheattrs, cacheattrs2); - - /* Check if IPA translates to secure or non-secure PA space. */ - if (arm_is_secure_below_el3(env)) { - if (ipa_secure) { - attrs->secure = - !(env->cp15.vstcr_el2.raw_tcr & (VSTCR_SA | VSTCR_SW)); - } else { - attrs->secure = - !((env->cp15.vtcr_el2.raw_tcr & (VTCR_NSA | VTCR_NSW)) - || (env->cp15.vstcr_el2.raw_tcr & (VSTCR_SA | VSTCR_SW))); - } - } - return 0; - } else { /* - * For non-EL2 CPUs a stage1+stage2 translation is just stage 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. */ - mmu_idx = stage_1_mmu_idx(mmu_idx); + address = extract64(address, 0, 52); } + + /* Fill in cacheattr a-la AArch64.TranslateAddressS1Off. */ + if (r_el == 1) { + uint64_t hcr = arm_hcr_el2_eff_secstate(env, ptw->in_space); + if (hcr & HCR_DC) { + if (hcr & HCR_DCT) { + memattr = 0xf0; /* Tagged, Normal, WB, RWA */ + } else { + memattr = 0xff; /* Normal, WB, RWA */ + } + } + } + if (memattr == 0) { + if (access_type == MMU_INST_FETCH) { + if (regime_sctlr(env, mmu_idx) & SCTLR_I) { + memattr = 0xee; /* Normal, WT, RA, NT */ + } else { + memattr = 0x44; /* Normal, NC, No */ + } + } + shareability = 2; /* outer shareable */ + } + result->cacheattrs.is_s2_format = false; + break; + } + + result->f.phys_addr = address; + result->f.prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + result->f.lg_page_size = TARGET_PAGE_BITS; + result->cacheattrs.shareability = shareability; + result->cacheattrs.attrs = memattr; + return false; +} + +static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, + target_ulong address, + MMUAccessType access_type, + GetPhysAddrResult *result, + ARMMMUFaultInfo *fi) +{ + hwaddr ipa; + int s1_prot, s1_lgpgsz; + ARMSecuritySpace in_space = ptw->in_space; + bool ret, ipa_secure, s1_guarded; + ARMCacheAttrs cacheattrs1; + ARMSecuritySpace ipa_space; + uint64_t hcr; + + ret = get_phys_addr_nogpc(env, ptw, address, access_type, result, fi); + + /* If S1 fails, return early. */ + if (ret) { + return ret; + } + + ipa = result->f.phys_addr; + ipa_secure = result->f.attrs.secure; + ipa_space = result->f.attrs.space; + + ptw->in_s1_is_el0 = ptw->in_mmu_idx == ARMMMUIdx_Stage1_E0; + ptw->in_mmu_idx = ipa_secure ? ARMMMUIdx_Stage2_S : ARMMMUIdx_Stage2; + ptw->in_space = ipa_space; + ptw->in_ptw_idx = ptw_idx_for_stage_2(env, ptw->in_mmu_idx); + + /* + * S1 is done, now do S2 translation. + * Save the stage1 results so that we may merge prot and cacheattrs later. + */ + s1_prot = result->f.prot; + s1_lgpgsz = result->f.lg_page_size; + s1_guarded = result->f.extra.arm.guarded; + cacheattrs1 = result->cacheattrs; + memset(result, 0, sizeof(*result)); + + ret = get_phys_addr_nogpc(env, ptw, ipa, access_type, result, fi); + fi->s2addr = ipa; + + /* Combine the S1 and S2 perms. */ + result->f.prot &= s1_prot; + + /* If S2 fails, return early. */ + if (ret) { + return ret; } /* - * The page table entries may downgrade secure to non-secure, but - * cannot upgrade an non-secure translation regime's attributes - * to secure. + * If either S1 or S2 returned a result smaller than TARGET_PAGE_SIZE, + * this means "don't put this in the TLB"; in this case, return a + * result with lg_page_size == 0 to achieve that. Otherwise, + * use the maximum of the S1 & S2 page size, so that invalidation + * of pages > TARGET_PAGE_SIZE works correctly. (This works even though + * we know the combined result permissions etc only cover the minimum + * of the S1 and S2 page size, because we know that the common TLB code + * never actually creates TLB entries bigger than TARGET_PAGE_SIZE, + * and passing a larger page size value only affects invalidations.) */ - attrs->secure = regime_is_secure(env, mmu_idx); - attrs->user = regime_is_user(env, mmu_idx); + if (result->f.lg_page_size < TARGET_PAGE_BITS || + s1_lgpgsz < TARGET_PAGE_BITS) { + result->f.lg_page_size = 0; + } else if (result->f.lg_page_size < s1_lgpgsz) { + result->f.lg_page_size = s1_lgpgsz; + } + + /* Combine the S1 and S2 cache attributes. */ + hcr = arm_hcr_el2_eff_secstate(env, in_space); + if (hcr & HCR_DC) { + /* + * HCR.DC forces the first stage attributes to + * Normal Non-Shareable, + * Inner Write-Back Read-Allocate Write-Allocate, + * Outer Write-Back Read-Allocate Write-Allocate. + * Do not overwrite Tagged within attrs. + */ + if (cacheattrs1.attrs != 0xf0) { + cacheattrs1.attrs = 0xff; + } + cacheattrs1.shareability = 0; + } + result->cacheattrs = combine_cacheattrs(hcr, cacheattrs1, + result->cacheattrs); + + /* No BTI GP information in stage 2, we just use the S1 value */ + result->f.extra.arm.guarded = s1_guarded; + + /* + * Check if IPA translates to secure or non-secure PA space. + * Note that VSTCR overrides VTCR and {N}SW overrides {N}SA. + */ + if (in_space == ARMSS_Secure) { + result->f.attrs.secure = + !(env->cp15.vstcr_el2 & (VSTCR_SA | VSTCR_SW)) + && (ipa_secure + || !(env->cp15.vtcr_el2 & (VTCR_NSA | VTCR_NSW))); + result->f.attrs.space = arm_secure_to_space(result->f.attrs.secure); + } + + return false; +} + +static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, + target_ulong address, + MMUAccessType access_type, + GetPhysAddrResult *result, + ARMMMUFaultInfo *fi) +{ + ARMMMUIdx mmu_idx = ptw->in_mmu_idx; + ARMMMUIdx s1_mmu_idx; + + /* + * The page table entries may downgrade Secure to NonSecure, but + * cannot upgrade a NonSecure translation regime's attributes + * to Secure or Realm. + */ + result->f.attrs.space = ptw->in_space; + result->f.attrs.secure = arm_space_is_secure(ptw->in_space); + + switch (mmu_idx) { + case ARMMMUIdx_Phys_S: + case ARMMMUIdx_Phys_NS: + case ARMMMUIdx_Phys_Root: + case ARMMMUIdx_Phys_Realm: + /* Checking Phys early avoids special casing later vs regime_el. */ + return get_phys_addr_disabled(env, ptw, address, access_type, + result, fi); + + case ARMMMUIdx_Stage1_E0: + case ARMMMUIdx_Stage1_E1: + case ARMMMUIdx_Stage1_E1_PAN: + /* + * First stage lookup uses second stage for ptw; only + * Secure has both S and NS IPA and starts with Stage2_S. + */ + ptw->in_ptw_idx = (ptw->in_space == ARMSS_Secure) ? + ARMMMUIdx_Stage2_S : ARMMMUIdx_Stage2; + break; + + case ARMMMUIdx_Stage2: + case ARMMMUIdx_Stage2_S: + /* + * Second stage lookup uses physical for ptw; whether this is S or + * NS may depend on the SW/NSW bits if this is a stage 2 lookup for + * the Secure EL2&0 regime. + */ + ptw->in_ptw_idx = ptw_idx_for_stage_2(env, mmu_idx); + break; + + case ARMMMUIdx_E10_0: + s1_mmu_idx = ARMMMUIdx_Stage1_E0; + goto do_twostage; + case ARMMMUIdx_E10_1: + s1_mmu_idx = ARMMMUIdx_Stage1_E1; + goto do_twostage; + case ARMMMUIdx_E10_1_PAN: + s1_mmu_idx = ARMMMUIdx_Stage1_E1_PAN; + do_twostage: + /* + * Call ourselves recursively to do the stage 1 and then stage 2 + * translations if mmu_idx is a two-stage regime, and EL2 present. + * Otherwise, a stage1+stage2 translation is just stage 1. + */ + ptw->in_mmu_idx = mmu_idx = s1_mmu_idx; + if (arm_feature(env, ARM_FEATURE_EL2) && + !regime_translation_disabled(env, ARMMMUIdx_Stage2, ptw->in_space)) { + return get_phys_addr_twostage(env, ptw, address, access_type, + result, fi); + } + /* fall through */ + + default: + /* Single stage uses physical for ptw. */ + ptw->in_ptw_idx = arm_space_to_phys(ptw->in_space); + break; + } + + result->f.attrs.user = regime_is_user(env, mmu_idx); /* * Fast Context Switch Extension. This doesn't exist at all in v8. @@ -2406,20 +3480,20 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, if (arm_feature(env, ARM_FEATURE_PMSA)) { bool ret; - *page_size = TARGET_PAGE_SIZE; + result->f.lg_page_size = TARGET_PAGE_BITS; if (arm_feature(env, ARM_FEATURE_V8)) { /* PMSAv8 */ - ret = get_phys_addr_pmsav8(env, address, access_type, mmu_idx, - phys_ptr, attrs, prot, page_size, fi); + ret = get_phys_addr_pmsav8(env, ptw, address, access_type, + result, fi); } else if (arm_feature(env, ARM_FEATURE_V7)) { /* PMSAv7 */ - ret = get_phys_addr_pmsav7(env, address, access_type, mmu_idx, - phys_ptr, prot, page_size, fi); + ret = get_phys_addr_pmsav7(env, ptw, address, access_type, + result, fi); } else { /* Pre-v7 MPU */ - ret = get_phys_addr_pmsav5(env, address, access_type, mmu_idx, - phys_ptr, prot, fi); + ret = get_phys_addr_pmsav5(env, ptw, address, access_type, + result, fi); } qemu_log_mask(CPU_LOG_MMU, "PMSA MPU lookup for %s at 0x%08" PRIx32 " mmu_idx %u -> %s (prot %c%c%c)\n", @@ -2427,114 +3501,150 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, (access_type == MMU_DATA_STORE ? "writing" : "execute"), (uint32_t)address, mmu_idx, ret ? "Miss" : "Hit", - *prot & PAGE_READ ? 'r' : '-', - *prot & PAGE_WRITE ? 'w' : '-', - *prot & PAGE_EXEC ? 'x' : '-'); + result->f.prot & PAGE_READ ? 'r' : '-', + result->f.prot & PAGE_WRITE ? 'w' : '-', + result->f.prot & PAGE_EXEC ? 'x' : '-'); return ret; } /* Definitely a real MMU, not an MPU */ - if (regime_translation_disabled(env, mmu_idx)) { - uint64_t hcr; - uint8_t memattr; - - /* - * MMU disabled. S1 addresses within aa64 translation regimes are - * still checked for bounds -- see AArch64.TranslateAddressS1Off. - */ - if (mmu_idx != ARMMMUIdx_Stage2 && mmu_idx != ARMMMUIdx_Stage2_S) { - 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; - - /* Fill in cacheattr a-la AArch64.TranslateAddressS1Off. */ - hcr = arm_hcr_el2_eff(env); - cacheattrs->shareability = 0; - cacheattrs->is_s2_format = false; - if (hcr & HCR_DC) { - if (hcr & HCR_DCT) { - memattr = 0xf0; /* Tagged, Normal, WB, RWA */ - } else { - memattr = 0xff; /* Normal, WB, RWA */ - } - } else if (access_type == MMU_INST_FETCH) { - if (regime_sctlr(env, mmu_idx) & SCTLR_I) { - memattr = 0xee; /* Normal, WT, RA, NT */ - } else { - memattr = 0x44; /* Normal, NC, No */ - } - cacheattrs->shareability = 2; /* outer sharable */ - } else { - memattr = 0x00; /* Device, nGnRnE */ - } - cacheattrs->attrs = memattr; - return 0; + if (regime_translation_disabled(env, mmu_idx, ptw->in_space)) { + return get_phys_addr_disabled(env, ptw, address, access_type, + result, fi); } if (regime_using_lpae_format(env, mmu_idx)) { - return get_phys_addr_lpae(env, address, access_type, mmu_idx, false, - phys_ptr, attrs, prot, page_size, - fi, cacheattrs); - } else if (regime_sctlr(env, mmu_idx) & SCTLR_XP) { - return get_phys_addr_v6(env, address, access_type, mmu_idx, - phys_ptr, attrs, prot, page_size, fi); + return get_phys_addr_lpae(env, ptw, address, access_type, result, fi); + } else if (arm_feature(env, ARM_FEATURE_V7) || + regime_sctlr(env, mmu_idx) & SCTLR_XP) { + return get_phys_addr_v6(env, ptw, address, access_type, result, fi); } else { - return get_phys_addr_v5(env, address, access_type, mmu_idx, - phys_ptr, prot, page_size, fi); + return get_phys_addr_v5(env, ptw, address, access_type, result, fi); } } +static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw, + target_ulong address, + MMUAccessType access_type, + GetPhysAddrResult *result, + ARMMMUFaultInfo *fi) +{ + if (get_phys_addr_nogpc(env, ptw, address, access_type, result, fi)) { + return true; + } + if (!granule_protection_check(env, result->f.phys_addr, + result->f.attrs.space, fi)) { + fi->type = ARMFault_GPCFOnOutput; + return true; + } + return false; +} + +bool get_phys_addr_with_space_nogpc(CPUARMState *env, target_ulong address, + MMUAccessType access_type, + ARMMMUIdx mmu_idx, ARMSecuritySpace space, + GetPhysAddrResult *result, + ARMMMUFaultInfo *fi) +{ + S1Translate ptw = { + .in_mmu_idx = mmu_idx, + .in_space = space, + }; + return get_phys_addr_nogpc(env, &ptw, address, access_type, result, fi); +} + +bool get_phys_addr(CPUARMState *env, target_ulong address, + MMUAccessType access_type, ARMMMUIdx mmu_idx, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi) +{ + S1Translate ptw = { + .in_mmu_idx = mmu_idx, + }; + ARMSecuritySpace ss; + + switch (mmu_idx) { + 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: + ss = arm_security_space_below_el3(env); + break; + case ARMMMUIdx_Stage2: + /* + * For Secure EL2, we need this index to be NonSecure; + * otherwise this will already be NonSecure or Realm. + */ + ss = arm_security_space_below_el3(env); + if (ss == ARMSS_Secure) { + ss = ARMSS_NonSecure; + } + break; + case ARMMMUIdx_Phys_NS: + case ARMMMUIdx_MPrivNegPri: + case ARMMMUIdx_MUserNegPri: + case ARMMMUIdx_MPriv: + case ARMMMUIdx_MUser: + ss = ARMSS_NonSecure; + break; + case ARMMMUIdx_Stage2_S: + case ARMMMUIdx_Phys_S: + case ARMMMUIdx_MSPrivNegPri: + case ARMMMUIdx_MSUserNegPri: + case ARMMMUIdx_MSPriv: + case ARMMMUIdx_MSUser: + ss = ARMSS_Secure; + break; + case ARMMMUIdx_E3: + if (arm_feature(env, ARM_FEATURE_AARCH64) && + cpu_isar_feature(aa64_rme, env_archcpu(env))) { + ss = ARMSS_Root; + } else { + ss = ARMSS_Secure; + } + break; + case ARMMMUIdx_Phys_Root: + ss = ARMSS_Root; + break; + case ARMMMUIdx_Phys_Realm: + ss = ARMSS_Realm; + break; + default: + g_assert_not_reached(); + } + + ptw.in_space = ss; + return get_phys_addr_gpc(env, &ptw, address, access_type, result, fi); +} + hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, MemTxAttrs *attrs) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; - hwaddr phys_addr; - target_ulong page_size; - int prot; - bool ret; - ARMMMUFaultInfo fi = {}; ARMMMUIdx mmu_idx = arm_mmu_idx(env); - ARMCacheAttrs cacheattrs = {}; + ARMSecuritySpace ss = arm_security_space(env); + S1Translate ptw = { + .in_mmu_idx = mmu_idx, + .in_space = ss, + .in_debug = true, + }; + GetPhysAddrResult res = {}; + ARMMMUFaultInfo fi = {}; + bool ret; - *attrs = (MemTxAttrs) {}; - - ret = get_phys_addr(env, addr, MMU_DATA_LOAD, mmu_idx, &phys_addr, - attrs, &prot, &page_size, &fi, &cacheattrs); + ret = get_phys_addr_gpc(env, &ptw, addr, MMU_DATA_LOAD, &res, &fi); + *attrs = res.f.attrs; if (ret) { return -1; } - return phys_addr; + return res.f.phys_addr; } diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index 0cb26dde7d..3244e0740d 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -25,6 +25,8 @@ #ifndef TARGET_ARM_SYNDROME_H #define TARGET_ARM_SYNDROME_H +#include "qemu/bitops.h" + /* Valid Syndrome Register EC field values */ enum arm_exception_class { EC_UNCATEGORIZED = 0x00, @@ -48,12 +50,17 @@ enum arm_exception_class { EC_AA64_SMC = 0x17, EC_SYSTEMREGISTERTRAP = 0x18, EC_SVEACCESSTRAP = 0x19, + EC_ERETTRAP = 0x1a, + EC_PACFAIL = 0x1c, + EC_SMETRAP = 0x1d, + EC_GPC = 0x1e, EC_INSNABORT = 0x20, EC_INSNABORT_SAME_EL = 0x21, EC_PCALIGNMENT = 0x22, EC_DATAABORT = 0x24, EC_DATAABORT_SAME_EL = 0x25, EC_SPALIGNMENT = 0x26, + EC_MOP = 0x27, EC_AA32_FPTRAP = 0x28, EC_AA64_FPTRAP = 0x2c, EC_SERROR = 0x2f, @@ -68,17 +75,33 @@ enum arm_exception_class { EC_AA64_BKPT = 0x3c, }; +typedef enum { + SME_ET_AccessTrap, + SME_ET_Streaming, + SME_ET_NotStreaming, + SME_ET_InactiveZA, +} SMEExceptionType; + +#define ARM_EL_EC_LENGTH 6 #define ARM_EL_EC_SHIFT 26 #define ARM_EL_IL_SHIFT 25 #define ARM_EL_ISV_SHIFT 24 #define ARM_EL_IL (1 << ARM_EL_IL_SHIFT) #define ARM_EL_ISV (1 << ARM_EL_ISV_SHIFT) +/* In the Data Abort syndrome */ +#define ARM_EL_VNCR (1 << 13) + static inline uint32_t syn_get_ec(uint32_t syn) { return syn >> ARM_EL_EC_SHIFT; } +static inline uint32_t syn_set_ec(uint32_t syn, uint32_t ec) +{ + return deposit32(syn, ARM_EL_EC_SHIFT, ARM_EL_EC_LENGTH, ec); +} + /* * Utility functions for constructing various kinds of syndrome value. * Note that in general we follow the AArch64 syndrome values; in a @@ -185,12 +208,13 @@ static inline uint32_t syn_cp15_rrt_trap(int cv, int cond, int opc1, int crm, | (rt2 << 10) | (rt << 5) | (crm << 1) | isread; } -static inline uint32_t syn_fp_access_trap(int cv, int cond, bool is_16bit) +static inline uint32_t syn_fp_access_trap(int cv, int cond, bool is_16bit, + int coproc) { - /* AArch32 FP trap or any AArch64 FP/SIMD trap: TA == 0 coproc == 0xa */ + /* AArch32 FP trap or any AArch64 FP/SIMD trap: TA == 0 */ return (EC_ADVSIMDFPACCESSTRAP << ARM_EL_EC_SHIFT) | (is_16bit ? 0 : ARM_EL_IL) - | (cv << 24) | (cond << 20) | 0xa; + | (cv << 24) | (cond << 20) | coproc; } static inline uint32_t syn_simd_access_trap(int cv, int cond, bool is_16bit) @@ -203,17 +227,38 @@ static inline uint32_t syn_simd_access_trap(int cv, int cond, bool is_16bit) static inline uint32_t syn_sve_access_trap(void) { - return EC_SVEACCESSTRAP << ARM_EL_EC_SHIFT; + return (EC_SVEACCESSTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL; +} + +/* + * eret_op is bits [1:0] of the ERET instruction, so: + * 0 for ERET, 2 for ERETAA, 3 for ERETAB. + */ +static inline uint32_t syn_erettrap(int eret_op) +{ + return (EC_ERETTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL | eret_op; +} + +static inline uint32_t syn_smetrap(SMEExceptionType etype, bool is_16bit) +{ + return (EC_SMETRAP << ARM_EL_EC_SHIFT) + | (is_16bit ? 0 : ARM_EL_IL) | etype; +} + +static inline uint32_t syn_pacfail(bool data, int keynumber) +{ + int error_code = (data << 1) | keynumber; + return (EC_PACFAIL << ARM_EL_EC_SHIFT) | ARM_EL_IL | error_code; } static inline uint32_t syn_pactrap(void) { - return EC_PACTRAP << ARM_EL_EC_SHIFT; + return (EC_PACTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL; } static inline uint32_t syn_btitrap(int btype) { - return (EC_BTITRAP << ARM_EL_EC_SHIFT) | btype; + return (EC_BTITRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL | btype; } static inline uint32_t syn_bxjtrap(int cv, int cond, int rm) @@ -222,6 +267,14 @@ static inline uint32_t syn_bxjtrap(int cv, int cond, int rm) (cv << 24) | (cond << 20) | rm; } +static inline uint32_t syn_gpc(int s2ptw, int ind, int gpcsc, int vncr, + int cm, int s1ptw, int wnr, int fsc) +{ + return (EC_GPC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (s2ptw << 21) + | (ind << 20) | (gpcsc << 14) | (vncr << 13) | (cm << 8) + | (s1ptw << 7) | (wnr << 6) | fsc; +} + static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc) { return (EC_INSNABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) @@ -252,6 +305,16 @@ static inline uint32_t syn_data_abort_with_iss(int same_el, | (ea << 9) | (cm << 8) | (s1ptw << 7) | (wnr << 6) | fsc; } +/* + * Faults due to FEAT_NV2 VNCR_EL2-based accesses report as same-EL + * Data Aborts with the VNCR bit set. + */ +static inline uint32_t syn_data_abort_vncr(int ea, int wnr, int fsc) +{ + return (EC_DATAABORT << ARM_EL_EC_SHIFT) | (1 << ARM_EL_EC_SHIFT) + | ARM_EL_IL | ARM_EL_VNCR | (wnr << 6) | fsc; +} + static inline uint32_t syn_swstep(int same_el, int isv, int ex) { return (EC_SOFTWARESTEP << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) @@ -292,4 +355,15 @@ static inline uint32_t syn_serror(uint32_t extra) return (EC_SERROR << ARM_EL_EC_SHIFT) | ARM_EL_IL | extra; } +static inline uint32_t syn_mop(bool is_set, bool is_setg, int options, + bool epilogue, bool wrong_option, bool option_a, + int destreg, int srcreg, int sizereg) +{ + return (EC_MOP << ARM_EL_EC_SHIFT) | ARM_EL_IL | + (is_set << 24) | (is_setg << 23) | (options << 19) | + (epilogue << 18) | (wrong_option << 17) | (option_a << 16) | + (destreg << 10) | (srcreg << 5) | sizereg; +} + + #endif /* TARGET_ARM_SYNDROME_H */ diff --git a/target/arm/tcg-stubs.c b/target/arm/tcg-stubs.c new file mode 100644 index 0000000000..152b172e24 --- /dev/null +++ b/target/arm/tcg-stubs.c @@ -0,0 +1,27 @@ +/* + * QEMU ARM stubs for some TCG helper functions + * + * Copyright 2021 SUSE LLC + * + * 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 "cpu.h" +#include "internals.h" + +void write_v7m_exception(CPUARMState *env, uint32_t new_exc) +{ + g_assert_not_reached(); +} + +void raise_exception_ra(CPUARMState *env, uint32_t excp, uint32_t syndrome, + uint32_t target_el, uintptr_t ra) +{ + g_assert_not_reached(); +} +/* Temporarily while cpu_get_tb_cpu_state() is still in common code */ +void assert_hflags_rebuild_correctly(CPUARMState *env) +{ +} diff --git a/target/arm/a32-uncond.decode b/target/arm/tcg/a32-uncond.decode similarity index 100% rename from target/arm/a32-uncond.decode rename to target/arm/tcg/a32-uncond.decode diff --git a/target/arm/a32.decode b/target/arm/tcg/a32.decode similarity index 100% rename from target/arm/a32.decode rename to target/arm/tcg/a32.decode diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode new file mode 100644 index 0000000000..8a20dce3c8 --- /dev/null +++ b/target/arm/tcg/a64.decode @@ -0,0 +1,591 @@ +# AArch64 A64 allowed instruction decoding +# +# Copyright (c) 2023 Linaro, Ltd +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# 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 . + +# +# This file is processed by scripts/decodetree.py +# + +&r rn +&ri rd imm +&rri_sf rd rn imm sf +&i imm + + +### Data Processing - Immediate + +# PC-rel addressing + +%imm_pcrel 5:s19 29:2 +@pcrel . .. ..... ................... rd:5 &ri imm=%imm_pcrel + +ADR 0 .. 10000 ................... ..... @pcrel +ADRP 1 .. 10000 ................... ..... @pcrel + +# Add/subtract (immediate) + +%imm12_sh12 10:12 !function=shl_12 +@addsub_imm sf:1 .. ...... . imm:12 rn:5 rd:5 +@addsub_imm12 sf:1 .. ...... . ............ rn:5 rd:5 imm=%imm12_sh12 + +ADD_i . 00 100010 0 ............ ..... ..... @addsub_imm +ADD_i . 00 100010 1 ............ ..... ..... @addsub_imm12 +ADDS_i . 01 100010 0 ............ ..... ..... @addsub_imm +ADDS_i . 01 100010 1 ............ ..... ..... @addsub_imm12 + +SUB_i . 10 100010 0 ............ ..... ..... @addsub_imm +SUB_i . 10 100010 1 ............ ..... ..... @addsub_imm12 +SUBS_i . 11 100010 0 ............ ..... ..... @addsub_imm +SUBS_i . 11 100010 1 ............ ..... ..... @addsub_imm12 + +# Add/subtract (immediate with tags) + +&rri_tag rd rn uimm6 uimm4 +@addsub_imm_tag . .. ...... . uimm6:6 .. uimm4:4 rn:5 rd:5 &rri_tag + +ADDG_i 1 00 100011 0 ...... 00 .... ..... ..... @addsub_imm_tag +SUBG_i 1 10 100011 0 ...... 00 .... ..... ..... @addsub_imm_tag + +# Logical (immediate) + +&rri_log rd rn sf dbm +@logic_imm_64 1 .. ...... dbm:13 rn:5 rd:5 &rri_log sf=1 +@logic_imm_32 0 .. ...... 0 dbm:12 rn:5 rd:5 &rri_log sf=0 + +AND_i . 00 100100 . ...... ...... ..... ..... @logic_imm_64 +AND_i . 00 100100 . ...... ...... ..... ..... @logic_imm_32 +ORR_i . 01 100100 . ...... ...... ..... ..... @logic_imm_64 +ORR_i . 01 100100 . ...... ...... ..... ..... @logic_imm_32 +EOR_i . 10 100100 . ...... ...... ..... ..... @logic_imm_64 +EOR_i . 10 100100 . ...... ...... ..... ..... @logic_imm_32 +ANDS_i . 11 100100 . ...... ...... ..... ..... @logic_imm_64 +ANDS_i . 11 100100 . ...... ...... ..... ..... @logic_imm_32 + +# Move wide (immediate) + +&movw rd sf imm hw +@movw_64 1 .. ...... hw:2 imm:16 rd:5 &movw sf=1 +@movw_32 0 .. ...... 0 hw:1 imm:16 rd:5 &movw sf=0 + +MOVN . 00 100101 .. ................ ..... @movw_64 +MOVN . 00 100101 .. ................ ..... @movw_32 +MOVZ . 10 100101 .. ................ ..... @movw_64 +MOVZ . 10 100101 .. ................ ..... @movw_32 +MOVK . 11 100101 .. ................ ..... @movw_64 +MOVK . 11 100101 .. ................ ..... @movw_32 + +# Bitfield + +&bitfield rd rn sf immr imms +@bitfield_64 1 .. ...... 1 immr:6 imms:6 rn:5 rd:5 &bitfield sf=1 +@bitfield_32 0 .. ...... 0 0 immr:5 0 imms:5 rn:5 rd:5 &bitfield sf=0 + +SBFM . 00 100110 . ...... ...... ..... ..... @bitfield_64 +SBFM . 00 100110 . ...... ...... ..... ..... @bitfield_32 +BFM . 01 100110 . ...... ...... ..... ..... @bitfield_64 +BFM . 01 100110 . ...... ...... ..... ..... @bitfield_32 +UBFM . 10 100110 . ...... ...... ..... ..... @bitfield_64 +UBFM . 10 100110 . ...... ...... ..... ..... @bitfield_32 + +# Extract + +&extract rd rn rm imm sf + +EXTR 1 00 100111 1 0 rm:5 imm:6 rn:5 rd:5 &extract sf=1 +EXTR 0 00 100111 0 0 rm:5 0 imm:5 rn:5 rd:5 &extract sf=0 + +# Branches + +%imm26 0:s26 !function=times_4 +@branch . ..... .......................... &i imm=%imm26 + +B 0 00101 .......................... @branch +BL 1 00101 .......................... @branch + +%imm19 5:s19 !function=times_4 +&cbz rt imm sf nz + +CBZ sf:1 011010 nz:1 ................... rt:5 &cbz imm=%imm19 + +%imm14 5:s14 !function=times_4 +%imm31_19 31:1 19:5 +&tbz rt imm nz bitpos + +TBZ . 011011 nz:1 ..... .............. rt:5 &tbz imm=%imm14 bitpos=%imm31_19 + +# B.cond and BC.cond +B_cond 0101010 0 ................... c:1 cond:4 imm=%imm19 + +BR 1101011 0000 11111 000000 rn:5 00000 &r +BLR 1101011 0001 11111 000000 rn:5 00000 &r +RET 1101011 0010 11111 000000 rn:5 00000 &r + +&braz rn m +BRAZ 1101011 0000 11111 00001 m:1 rn:5 11111 &braz # BRAAZ, BRABZ +BLRAZ 1101011 0001 11111 00001 m:1 rn:5 11111 &braz # BLRAAZ, BLRABZ + +&reta m +RETA 1101011 0010 11111 00001 m:1 11111 11111 &reta # RETAA, RETAB + +&bra rn rm m +BRA 1101011 1000 11111 00001 m:1 rn:5 rm:5 &bra # BRAA, BRAB +BLRA 1101011 1001 11111 00001 m:1 rn:5 rm:5 &bra # BLRAA, BLRAB + +ERET 1101011 0100 11111 000000 11111 00000 +ERETA 1101011 0100 11111 00001 m:1 11111 11111 &reta # ERETAA, ERETAB + +# We don't need to decode DRPS because it always UNDEFs except when +# the processor is in halting debug state (which we don't implement). +# The pattern is listed here as documentation. +# DRPS 1101011 0101 11111 000000 11111 00000 + +# Hint instruction group +{ + [ + YIELD 1101 0101 0000 0011 0010 0000 001 11111 + WFE 1101 0101 0000 0011 0010 0000 010 11111 + WFI 1101 0101 0000 0011 0010 0000 011 11111 + # We implement WFE to never block, so our SEV/SEVL are NOPs + # SEV 1101 0101 0000 0011 0010 0000 100 11111 + # SEVL 1101 0101 0000 0011 0010 0000 101 11111 + # Our DGL is a NOP because we don't merge memory accesses anyway. + # DGL 1101 0101 0000 0011 0010 0000 110 11111 + XPACLRI 1101 0101 0000 0011 0010 0000 111 11111 + PACIA1716 1101 0101 0000 0011 0010 0001 000 11111 + PACIB1716 1101 0101 0000 0011 0010 0001 010 11111 + AUTIA1716 1101 0101 0000 0011 0010 0001 100 11111 + AUTIB1716 1101 0101 0000 0011 0010 0001 110 11111 + ESB 1101 0101 0000 0011 0010 0010 000 11111 + PACIAZ 1101 0101 0000 0011 0010 0011 000 11111 + PACIASP 1101 0101 0000 0011 0010 0011 001 11111 + PACIBZ 1101 0101 0000 0011 0010 0011 010 11111 + PACIBSP 1101 0101 0000 0011 0010 0011 011 11111 + AUTIAZ 1101 0101 0000 0011 0010 0011 100 11111 + AUTIASP 1101 0101 0000 0011 0010 0011 101 11111 + AUTIBZ 1101 0101 0000 0011 0010 0011 110 11111 + AUTIBSP 1101 0101 0000 0011 0010 0011 111 11111 + ] + # The canonical NOP has CRm == op2 == 0, but all of the space + # that isn't specifically allocated to an instruction must NOP + NOP 1101 0101 0000 0011 0010 ---- --- 11111 +} + +# Barriers + +CLREX 1101 0101 0000 0011 0011 ---- 010 11111 +DSB_DMB 1101 0101 0000 0011 0011 domain:2 types:2 10- 11111 +ISB 1101 0101 0000 0011 0011 ---- 110 11111 +SB 1101 0101 0000 0011 0011 0000 111 11111 + +# PSTATE + +CFINV 1101 0101 0000 0 000 0100 0000 000 11111 +XAFLAG 1101 0101 0000 0 000 0100 0000 001 11111 +AXFLAG 1101 0101 0000 0 000 0100 0000 010 11111 + +# These are architecturally all "MSR (immediate)"; we decode the destination +# register too because there is no commonality in our implementation. +@msr_i .... .... .... . ... .... imm:4 ... ..... +MSR_i_UAO 1101 0101 0000 0 000 0100 .... 011 11111 @msr_i +MSR_i_PAN 1101 0101 0000 0 000 0100 .... 100 11111 @msr_i +MSR_i_SPSEL 1101 0101 0000 0 000 0100 .... 101 11111 @msr_i +MSR_i_SBSS 1101 0101 0000 0 011 0100 .... 001 11111 @msr_i +MSR_i_DIT 1101 0101 0000 0 011 0100 .... 010 11111 @msr_i +MSR_i_TCO 1101 0101 0000 0 011 0100 .... 100 11111 @msr_i +MSR_i_DAIFSET 1101 0101 0000 0 011 0100 .... 110 11111 @msr_i +MSR_i_DAIFCLEAR 1101 0101 0000 0 011 0100 .... 111 11111 @msr_i +MSR_i_SVCR 1101 0101 0000 0 011 0100 0 mask:2 imm:1 011 11111 + +# MRS, MSR (register), SYS, SYSL. These are all essentially the +# same instruction as far as QEMU is concerned. +# NB: op0 is bits [20:19], but op0=0b00 is other insns, so we have +# to hand-decode it. +SYS 1101 0101 00 l:1 01 op1:3 crn:4 crm:4 op2:3 rt:5 op0=1 +SYS 1101 0101 00 l:1 10 op1:3 crn:4 crm:4 op2:3 rt:5 op0=2 +SYS 1101 0101 00 l:1 11 op1:3 crn:4 crm:4 op2:3 rt:5 op0=3 + +# Exception generation + +@i16 .... .... ... imm:16 ... .. &i +SVC 1101 0100 000 ................ 000 01 @i16 +HVC 1101 0100 000 ................ 000 10 @i16 +SMC 1101 0100 000 ................ 000 11 @i16 +BRK 1101 0100 001 ................ 000 00 @i16 +HLT 1101 0100 010 ................ 000 00 @i16 +# These insns always UNDEF unless in halting debug state, which +# we don't implement. So we don't need to decode them. The patterns +# are listed here as documentation. +# DCPS1 1101 0100 101 ................ 000 01 @i16 +# DCPS2 1101 0100 101 ................ 000 10 @i16 +# DCPS3 1101 0100 101 ................ 000 11 @i16 + +# Loads and stores + +&stxr rn rt rt2 rs sz lasr +&stlr rn rt sz lasr +@stxr sz:2 ...... ... rs:5 lasr:1 rt2:5 rn:5 rt:5 &stxr +@stlr sz:2 ...... ... ..... lasr:1 ..... rn:5 rt:5 &stlr +%imm1_30_p2 30:1 !function=plus_2 +@stxp .. ...... ... rs:5 lasr:1 rt2:5 rn:5 rt:5 &stxr sz=%imm1_30_p2 +STXR .. 001000 000 ..... . ..... ..... ..... @stxr # inc STLXR +LDXR .. 001000 010 ..... . ..... ..... ..... @stxr # inc LDAXR +STLR .. 001000 100 11111 . 11111 ..... ..... @stlr # inc STLLR +LDAR .. 001000 110 11111 . 11111 ..... ..... @stlr # inc LDLAR + +STXP 1 . 001000 001 ..... . ..... ..... ..... @stxp # inc STLXP +LDXP 1 . 001000 011 ..... . ..... ..... ..... @stxp # inc LDAXP + +# CASP, CASPA, CASPAL, CASPL (we don't decode the bits that determine +# acquire/release semantics because QEMU's cmpxchg always has those) +CASP 0 . 001000 0 - 1 rs:5 - 11111 rn:5 rt:5 sz=%imm1_30_p2 +# CAS, CASA, CASAL, CASL +CAS sz:2 001000 1 - 1 rs:5 - 11111 rn:5 rt:5 + +&ldlit rt imm sz sign +@ldlit .. ... . .. ................... rt:5 &ldlit imm=%imm19 + +LD_lit 00 011 0 00 ................... ..... @ldlit sz=2 sign=0 +LD_lit 01 011 0 00 ................... ..... @ldlit sz=3 sign=0 +LD_lit 10 011 0 00 ................... ..... @ldlit sz=2 sign=1 +LD_lit_v 00 011 1 00 ................... ..... @ldlit sz=2 sign=0 +LD_lit_v 01 011 1 00 ................... ..... @ldlit sz=3 sign=0 +LD_lit_v 10 011 1 00 ................... ..... @ldlit sz=4 sign=0 + +# PRFM +NOP 11 011 0 00 ------------------- ----- + +&ldstpair rt2 rt rn imm sz sign w p +@ldstpair .. ... . ... . imm:s7 rt2:5 rn:5 rt:5 &ldstpair + +# STNP, LDNP: Signed offset, non-temporal hint. We don't emulate caches +# so we ignore hints about data access patterns, and handle these like +# plain signed offset. +STP 00 101 0 000 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +LDP 00 101 0 000 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +STP 10 101 0 000 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +LDP 10 101 0 000 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +STP_v 00 101 1 000 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +LDP_v 00 101 1 000 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +STP_v 01 101 1 000 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +LDP_v 01 101 1 000 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +STP_v 10 101 1 000 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0 +LDP_v 10 101 1 000 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0 + +# STP and LDP: post-indexed +STP 00 101 0 001 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1 +LDP 00 101 0 001 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1 +LDP 01 101 0 001 1 ....... ..... ..... ..... @ldstpair sz=2 sign=1 p=1 w=1 +STP 10 101 0 001 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1 +LDP 10 101 0 001 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1 +STP_v 00 101 1 001 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1 +LDP_v 00 101 1 001 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=1 w=1 +STP_v 01 101 1 001 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1 +LDP_v 01 101 1 001 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1 +STP_v 10 101 1 001 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=1 w=1 +LDP_v 10 101 1 001 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=1 w=1 + +# STP and LDP: offset +STP 00 101 0 010 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +LDP 00 101 0 010 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +LDP 01 101 0 010 1 ....... ..... ..... ..... @ldstpair sz=2 sign=1 p=0 w=0 +STP 10 101 0 010 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +LDP 10 101 0 010 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +STP_v 00 101 1 010 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +LDP_v 00 101 1 010 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=0 +STP_v 01 101 1 010 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +LDP_v 01 101 1 010 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +STP_v 10 101 1 010 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0 +LDP_v 10 101 1 010 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=0 + +# STP and LDP: pre-indexed +STP 00 101 0 011 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1 +LDP 00 101 0 011 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1 +LDP 01 101 0 011 1 ....... ..... ..... ..... @ldstpair sz=2 sign=1 p=0 w=1 +STP 10 101 0 011 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1 +LDP 10 101 0 011 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1 +STP_v 00 101 1 011 0 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1 +LDP_v 00 101 1 011 1 ....... ..... ..... ..... @ldstpair sz=2 sign=0 p=0 w=1 +STP_v 01 101 1 011 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1 +LDP_v 01 101 1 011 1 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1 +STP_v 10 101 1 011 0 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=1 +LDP_v 10 101 1 011 1 ....... ..... ..... ..... @ldstpair sz=4 sign=0 p=0 w=1 + +# STGP: store tag and pair +STGP 01 101 0 001 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=1 w=1 +STGP 01 101 0 010 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=0 +STGP 01 101 0 011 0 ....... ..... ..... ..... @ldstpair sz=3 sign=0 p=0 w=1 + +# Load/store register (unscaled immediate) +&ldst_imm rt rn imm sz sign w p unpriv ext +@ldst_imm .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm unpriv=0 p=0 w=0 +@ldst_imm_pre .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm unpriv=0 p=0 w=1 +@ldst_imm_post .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm unpriv=0 p=1 w=1 +@ldst_imm_user .. ... . .. .. . imm:s9 .. rn:5 rt:5 &ldst_imm unpriv=1 p=0 w=0 + +STR_i sz:2 111 0 00 00 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 +LDR_i 00 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=1 sz=0 +LDR_i 01 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=1 sz=1 +LDR_i 10 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=1 sz=2 +LDR_i 11 111 0 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 sz=3 +LDR_i 00 111 0 00 10 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=0 sz=0 +LDR_i 01 111 0 00 10 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=0 sz=1 +LDR_i 10 111 0 00 10 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=0 sz=2 +LDR_i 00 111 0 00 11 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=1 sz=0 +LDR_i 01 111 0 00 11 0 ......... 00 ..... ..... @ldst_imm sign=1 ext=1 sz=1 + +STR_i sz:2 111 0 00 00 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 +LDR_i 00 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=1 sz=0 +LDR_i 01 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=1 sz=1 +LDR_i 10 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=1 sz=2 +LDR_i 11 111 0 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 sz=3 +LDR_i 00 111 0 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=0 sz=0 +LDR_i 01 111 0 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=0 sz=1 +LDR_i 10 111 0 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=0 sz=2 +LDR_i 00 111 0 00 11 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=1 sz=0 +LDR_i 01 111 0 00 11 0 ......... 01 ..... ..... @ldst_imm_post sign=1 ext=1 sz=1 + +STR_i sz:2 111 0 00 00 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=0 +LDR_i 00 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=1 sz=0 +LDR_i 01 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=1 sz=1 +LDR_i 10 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=1 sz=2 +LDR_i 11 111 0 00 01 0 ......... 10 ..... ..... @ldst_imm_user sign=0 ext=0 sz=3 +LDR_i 00 111 0 00 10 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=0 sz=0 +LDR_i 01 111 0 00 10 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=0 sz=1 +LDR_i 10 111 0 00 10 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=0 sz=2 +LDR_i 00 111 0 00 11 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=1 sz=0 +LDR_i 01 111 0 00 11 0 ......... 10 ..... ..... @ldst_imm_user sign=1 ext=1 sz=1 + +STR_i sz:2 111 0 00 00 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 +LDR_i 00 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=1 sz=0 +LDR_i 01 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=1 sz=1 +LDR_i 10 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=1 sz=2 +LDR_i 11 111 0 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 sz=3 +LDR_i 00 111 0 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=0 sz=0 +LDR_i 01 111 0 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=0 sz=1 +LDR_i 10 111 0 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=0 sz=2 +LDR_i 00 111 0 00 11 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=1 sz=0 +LDR_i 01 111 0 00 11 0 ......... 11 ..... ..... @ldst_imm_pre sign=1 ext=1 sz=1 + +# PRFM : prefetch memory: a no-op for QEMU +NOP 11 111 0 00 10 0 --------- 00 ----- ----- + +STR_v_i sz:2 111 1 00 00 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 +STR_v_i 00 111 1 00 10 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 sz=4 +LDR_v_i sz:2 111 1 00 01 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 +LDR_v_i 00 111 1 00 11 0 ......... 00 ..... ..... @ldst_imm sign=0 ext=0 sz=4 + +STR_v_i sz:2 111 1 00 00 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 +STR_v_i 00 111 1 00 10 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 sz=4 +LDR_v_i sz:2 111 1 00 01 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 +LDR_v_i 00 111 1 00 11 0 ......... 01 ..... ..... @ldst_imm_post sign=0 ext=0 sz=4 + +STR_v_i sz:2 111 1 00 00 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 +STR_v_i 00 111 1 00 10 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 sz=4 +LDR_v_i sz:2 111 1 00 01 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 +LDR_v_i 00 111 1 00 11 0 ......... 11 ..... ..... @ldst_imm_pre sign=0 ext=0 sz=4 + +# Load/store with an unsigned 12 bit immediate, which is scaled by the +# element size. The function gets the sz:imm and returns the scaled immediate. +%uimm_scaled 10:12 sz:3 !function=uimm_scaled + +@ldst_uimm .. ... . .. .. ............ rn:5 rt:5 &ldst_imm unpriv=0 p=0 w=0 imm=%uimm_scaled + +STR_i sz:2 111 0 01 00 ............ ..... ..... @ldst_uimm sign=0 ext=0 +LDR_i 00 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=1 sz=0 +LDR_i 01 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=1 sz=1 +LDR_i 10 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=1 sz=2 +LDR_i 11 111 0 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=3 +LDR_i 00 111 0 01 10 ............ ..... ..... @ldst_uimm sign=1 ext=0 sz=0 +LDR_i 01 111 0 01 10 ............ ..... ..... @ldst_uimm sign=1 ext=0 sz=1 +LDR_i 10 111 0 01 10 ............ ..... ..... @ldst_uimm sign=1 ext=0 sz=2 +LDR_i 00 111 0 01 11 ............ ..... ..... @ldst_uimm sign=1 ext=1 sz=0 +LDR_i 01 111 0 01 11 ............ ..... ..... @ldst_uimm sign=1 ext=1 sz=1 + +# PRFM +NOP 11 111 0 01 10 ------------ ----- ----- + +STR_v_i sz:2 111 1 01 00 ............ ..... ..... @ldst_uimm sign=0 ext=0 +STR_v_i 00 111 1 01 10 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=4 +LDR_v_i sz:2 111 1 01 01 ............ ..... ..... @ldst_uimm sign=0 ext=0 +LDR_v_i 00 111 1 01 11 ............ ..... ..... @ldst_uimm sign=0 ext=0 sz=4 + +# Load/store with register offset +&ldst rm rn rt sign ext sz opt s +@ldst .. ... . .. .. . rm:5 opt:3 s:1 .. rn:5 rt:5 &ldst +STR sz:2 111 0 00 00 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 +LDR 00 111 0 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=1 sz=0 +LDR 01 111 0 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=1 sz=1 +LDR 10 111 0 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=1 sz=2 +LDR 11 111 0 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 sz=3 +LDR 00 111 0 00 10 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=0 sz=0 +LDR 01 111 0 00 10 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=0 sz=1 +LDR 10 111 0 00 10 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=0 sz=2 +LDR 00 111 0 00 11 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=1 sz=0 +LDR 01 111 0 00 11 1 ..... ... . 10 ..... ..... @ldst sign=1 ext=1 sz=1 + +# PRFM +NOP 11 111 0 00 10 1 ----- -1- - 10 ----- ----- + +STR_v sz:2 111 1 00 00 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 +STR_v 00 111 1 00 10 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 sz=4 +LDR_v sz:2 111 1 00 01 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 +LDR_v 00 111 1 00 11 1 ..... ... . 10 ..... ..... @ldst sign=0 ext=0 sz=4 + +# Atomic memory operations +&atomic rs rn rt a r sz +@atomic sz:2 ... . .. a:1 r:1 . rs:5 . ... .. rn:5 rt:5 &atomic +LDADD .. 111 0 00 . . 1 ..... 0000 00 ..... ..... @atomic +LDCLR .. 111 0 00 . . 1 ..... 0001 00 ..... ..... @atomic +LDEOR .. 111 0 00 . . 1 ..... 0010 00 ..... ..... @atomic +LDSET .. 111 0 00 . . 1 ..... 0011 00 ..... ..... @atomic +LDSMAX .. 111 0 00 . . 1 ..... 0100 00 ..... ..... @atomic +LDSMIN .. 111 0 00 . . 1 ..... 0101 00 ..... ..... @atomic +LDUMAX .. 111 0 00 . . 1 ..... 0110 00 ..... ..... @atomic +LDUMIN .. 111 0 00 . . 1 ..... 0111 00 ..... ..... @atomic +SWP .. 111 0 00 . . 1 ..... 1000 00 ..... ..... @atomic + +LDAPR sz:2 111 0 00 1 0 1 11111 1100 00 rn:5 rt:5 + +# Load/store register (pointer authentication) + +# LDRA immediate is 10 bits signed and scaled, but the bits aren't all contiguous +%ldra_imm 22:s1 12:9 !function=times_8 + +LDRA 11 111 0 00 m:1 . 1 ......... w:1 1 rn:5 rt:5 imm=%ldra_imm + +&ldapr_stlr_i rn rt imm sz sign ext +@ldapr_stlr_i .. ...... .. . imm:9 .. rn:5 rt:5 &ldapr_stlr_i +STLR_i sz:2 011001 00 0 ......... 00 ..... ..... @ldapr_stlr_i sign=0 ext=0 +LDAPR_i sz:2 011001 01 0 ......... 00 ..... ..... @ldapr_stlr_i sign=0 ext=0 +LDAPR_i 00 011001 10 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=0 sz=0 +LDAPR_i 01 011001 10 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=0 sz=1 +LDAPR_i 10 011001 10 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=0 sz=2 +LDAPR_i 00 011001 11 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=1 sz=0 +LDAPR_i 01 011001 11 0 ......... 00 ..... ..... @ldapr_stlr_i sign=1 ext=1 sz=1 + +# Load/store multiple structures +# The 4-bit opcode in [15:12] encodes repeat count and structure elements +&ldst_mult rm rn rt sz q p rpt selem +@ldst_mult . q:1 ...... p:1 . . rm:5 .... sz:2 rn:5 rt:5 &ldst_mult +ST_mult 0 . 001100 . 0 0 ..... 0000 .. ..... ..... @ldst_mult rpt=1 selem=4 +ST_mult 0 . 001100 . 0 0 ..... 0010 .. ..... ..... @ldst_mult rpt=4 selem=1 +ST_mult 0 . 001100 . 0 0 ..... 0100 .. ..... ..... @ldst_mult rpt=1 selem=3 +ST_mult 0 . 001100 . 0 0 ..... 0110 .. ..... ..... @ldst_mult rpt=3 selem=1 +ST_mult 0 . 001100 . 0 0 ..... 0111 .. ..... ..... @ldst_mult rpt=1 selem=1 +ST_mult 0 . 001100 . 0 0 ..... 1000 .. ..... ..... @ldst_mult rpt=1 selem=2 +ST_mult 0 . 001100 . 0 0 ..... 1010 .. ..... ..... @ldst_mult rpt=2 selem=1 + +LD_mult 0 . 001100 . 1 0 ..... 0000 .. ..... ..... @ldst_mult rpt=1 selem=4 +LD_mult 0 . 001100 . 1 0 ..... 0010 .. ..... ..... @ldst_mult rpt=4 selem=1 +LD_mult 0 . 001100 . 1 0 ..... 0100 .. ..... ..... @ldst_mult rpt=1 selem=3 +LD_mult 0 . 001100 . 1 0 ..... 0110 .. ..... ..... @ldst_mult rpt=3 selem=1 +LD_mult 0 . 001100 . 1 0 ..... 0111 .. ..... ..... @ldst_mult rpt=1 selem=1 +LD_mult 0 . 001100 . 1 0 ..... 1000 .. ..... ..... @ldst_mult rpt=1 selem=2 +LD_mult 0 . 001100 . 1 0 ..... 1010 .. ..... ..... @ldst_mult rpt=2 selem=1 + +# Load/store single structure +&ldst_single rm rn rt p selem index scale + +%ldst_single_selem 13:1 21:1 !function=plus_1 + +%ldst_single_index_b 30:1 10:3 +%ldst_single_index_h 30:1 11:2 +%ldst_single_index_s 30:1 12:1 + +@ldst_single_b .. ...... p:1 .. rm:5 ...... rn:5 rt:5 \ + &ldst_single scale=0 selem=%ldst_single_selem \ + index=%ldst_single_index_b +@ldst_single_h .. ...... p:1 .. rm:5 ...... rn:5 rt:5 \ + &ldst_single scale=1 selem=%ldst_single_selem \ + index=%ldst_single_index_h +@ldst_single_s .. ...... p:1 .. rm:5 ...... rn:5 rt:5 \ + &ldst_single scale=2 selem=%ldst_single_selem \ + index=%ldst_single_index_s +@ldst_single_d . index:1 ...... p:1 .. rm:5 ...... rn:5 rt:5 \ + &ldst_single scale=3 selem=%ldst_single_selem + +ST_single 0 . 001101 . 0 . ..... 00 . ... ..... ..... @ldst_single_b +ST_single 0 . 001101 . 0 . ..... 01 . ..0 ..... ..... @ldst_single_h +ST_single 0 . 001101 . 0 . ..... 10 . .00 ..... ..... @ldst_single_s +ST_single 0 . 001101 . 0 . ..... 10 . 001 ..... ..... @ldst_single_d + +LD_single 0 . 001101 . 1 . ..... 00 . ... ..... ..... @ldst_single_b +LD_single 0 . 001101 . 1 . ..... 01 . ..0 ..... ..... @ldst_single_h +LD_single 0 . 001101 . 1 . ..... 10 . .00 ..... ..... @ldst_single_s +LD_single 0 . 001101 . 1 . ..... 10 . 001 ..... ..... @ldst_single_d + +# Replicating load case +LD_single_repl 0 q:1 001101 p:1 1 . rm:5 11 . 0 scale:2 rn:5 rt:5 selem=%ldst_single_selem + +%tag_offset 12:s9 !function=scale_by_log2_tag_granule +&ldst_tag rn rt imm p w +@ldst_tag ........ .. . ......... .. rn:5 rt:5 &ldst_tag imm=%tag_offset +@ldst_tag_mult ........ .. . 000000000 .. rn:5 rt:5 &ldst_tag imm=0 + +STZGM 11011001 00 1 ......... 00 ..... ..... @ldst_tag_mult p=0 w=0 +STG 11011001 00 1 ......... 01 ..... ..... @ldst_tag p=1 w=1 +STG 11011001 00 1 ......... 10 ..... ..... @ldst_tag p=0 w=0 +STG 11011001 00 1 ......... 11 ..... ..... @ldst_tag p=0 w=1 + +LDG 11011001 01 1 ......... 00 ..... ..... @ldst_tag p=0 w=0 +STZG 11011001 01 1 ......... 01 ..... ..... @ldst_tag p=1 w=1 +STZG 11011001 01 1 ......... 10 ..... ..... @ldst_tag p=0 w=0 +STZG 11011001 01 1 ......... 11 ..... ..... @ldst_tag p=0 w=1 + +STGM 11011001 10 1 ......... 00 ..... ..... @ldst_tag_mult p=0 w=0 +ST2G 11011001 10 1 ......... 01 ..... ..... @ldst_tag p=1 w=1 +ST2G 11011001 10 1 ......... 10 ..... ..... @ldst_tag p=0 w=0 +ST2G 11011001 10 1 ......... 11 ..... ..... @ldst_tag p=0 w=1 + +LDGM 11011001 11 1 ......... 00 ..... ..... @ldst_tag_mult p=0 w=0 +STZ2G 11011001 11 1 ......... 01 ..... ..... @ldst_tag p=1 w=1 +STZ2G 11011001 11 1 ......... 10 ..... ..... @ldst_tag p=0 w=0 +STZ2G 11011001 11 1 ......... 11 ..... ..... @ldst_tag p=0 w=1 + +# Memory operations (memset, memcpy, memmove) +# Each of these comes in a set of three, eg SETP (prologue), SETM (main), +# SETE (epilogue), and each of those has different flavours to +# indicate whether memory accesses should be unpriv or non-temporal. +# We don't distinguish temporal and non-temporal accesses, but we +# do need to report it in syndrome register values. + +# Memset +&set rs rn rd unpriv nontemp +# op2 bit 1 is nontemporal bit +@set .. ......... rs:5 .. nontemp:1 unpriv:1 .. rn:5 rd:5 &set + +SETP 00 011001110 ..... 00 . . 01 ..... ..... @set +SETM 00 011001110 ..... 01 . . 01 ..... ..... @set +SETE 00 011001110 ..... 10 . . 01 ..... ..... @set + +# Like SET, but also setting MTE tags +SETGP 00 011101110 ..... 00 . . 01 ..... ..... @set +SETGM 00 011101110 ..... 01 . . 01 ..... ..... @set +SETGE 00 011101110 ..... 10 . . 01 ..... ..... @set + +# Memmove/Memcopy: the CPY insns allow overlapping src/dest and +# copy in the correct direction; the CPYF insns always copy forwards. +# +# options has the nontemporal and unpriv bits for src and dest +&cpy rs rn rd options +@cpy .. ... . ..... rs:5 options:4 .. rn:5 rd:5 &cpy + +CPYFP 00 011 0 01000 ..... .... 01 ..... ..... @cpy +CPYFM 00 011 0 01010 ..... .... 01 ..... ..... @cpy +CPYFE 00 011 0 01100 ..... .... 01 ..... ..... @cpy +CPYP 00 011 1 01000 ..... .... 01 ..... ..... @cpy +CPYM 00 011 1 01010 ..... .... 01 ..... ..... @cpy +CPYE 00 011 1 01100 ..... .... 01 ..... ..... @cpy diff --git a/target/arm/arm_ldst.h b/target/arm/tcg/arm_ldst.h similarity index 100% rename from target/arm/arm_ldst.h rename to target/arm/tcg/arm_ldst.h diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c new file mode 100644 index 0000000000..c059c681e9 --- /dev/null +++ b/target/arm/tcg/cpu-v7m.c @@ -0,0 +1,290 @@ +/* + * QEMU ARMv7-M TCG-only CPUs. + * + * Copyright (c) 2012 SUSE LINUX Products GmbH + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "hw/core/tcg-cpu-ops.h" +#include "internals.h" + +#if !defined(CONFIG_USER_ONLY) + +#include "hw/intc/armv7m_nvic.h" + +static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + CPUClass *cc = CPU_GET_CLASS(cs); + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + bool ret = false; + + /* + * ARMv7-M interrupt masking works differently than -A or -R. + * There is no FIQ/IRQ distinction. Instead of I and F bits + * masking FIQ and IRQ interrupts, an exception is taken only + * if it is higher priority than the current execution priority + * (which depends on state like BASEPRI, FAULTMASK and the + * currently active exception). + */ + if (interrupt_request & CPU_INTERRUPT_HARD + && (armv7m_nvic_can_take_pending_exception(env->nvic))) { + cs->exception_index = EXCP_IRQ; + cc->tcg_ops->do_interrupt(cs); + ret = true; + } + return ret; +} + +#endif /* !CONFIG_USER_ONLY */ + +static void cortex_m0_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + set_feature(&cpu->env, ARM_FEATURE_V6); + set_feature(&cpu->env, ARM_FEATURE_M); + + cpu->midr = 0x410cc200; + + /* + * These ID register values are not guest visible, because + * we do not implement the Main Extension. They must be set + * to values corresponding to the Cortex-M0's implemented + * features, because QEMU generally controls its emulation + * by looking at ID register fields. We use the same values as + * for the M3. + */ + cpu->isar.id_pfr0 = 0x00000030; + cpu->isar.id_pfr1 = 0x00000200; + cpu->isar.id_dfr0 = 0x00100000; + cpu->id_afr0 = 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; + cpu->isar.id_isar3 = 0x01111110; + cpu->isar.id_isar4 = 0x01310102; + cpu->isar.id_isar5 = 0x00000000; + cpu->isar.id_isar6 = 0x00000000; +} + +static void cortex_m3_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); + cpu->midr = 0x410fc231; + cpu->pmsav7_dregion = 8; + cpu->isar.id_pfr0 = 0x00000030; + cpu->isar.id_pfr1 = 0x00000200; + cpu->isar.id_dfr0 = 0x00100000; + cpu->id_afr0 = 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; + cpu->isar.id_isar3 = 0x01111110; + cpu->isar.id_isar4 = 0x01310102; + cpu->isar.id_isar5 = 0x00000000; + cpu->isar.id_isar6 = 0x00000000; +} + +static void cortex_m4_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 = 0x410fc240; /* r0p0 */ + cpu->pmsav7_dregion = 8; + cpu->isar.mvfr0 = 0x10110021; + cpu->isar.mvfr1 = 0x11000011; + cpu->isar.mvfr2 = 0x00000000; + cpu->isar.id_pfr0 = 0x00000030; + cpu->isar.id_pfr1 = 0x00000200; + cpu->isar.id_dfr0 = 0x00100000; + cpu->id_afr0 = 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; + cpu->isar.id_isar3 = 0x01111110; + cpu->isar.id_isar4 = 0x01310102; + cpu->isar.id_isar5 = 0x00000000; + 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->isar.id_pfr0 = 0x00000030; + cpu->isar.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); + + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_M); + set_feature(&cpu->env, ARM_FEATURE_M_MAIN); + set_feature(&cpu->env, ARM_FEATURE_M_SECURITY); + set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); + cpu->midr = 0x410fd213; /* r0p3 */ + cpu->pmsav7_dregion = 16; + cpu->sau_sregion = 8; + cpu->isar.mvfr0 = 0x10110021; + cpu->isar.mvfr1 = 0x11000011; + cpu->isar.mvfr2 = 0x00000040; + cpu->isar.id_pfr0 = 0x00000030; + cpu->isar.id_pfr1 = 0x00000210; + cpu->isar.id_dfr0 = 0x00200000; + cpu->id_afr0 = 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; + cpu->isar.id_isar3 = 0x01111131; + cpu->isar.id_isar4 = 0x01310132; + cpu->isar.id_isar5 = 0x00000000; + cpu->isar.id_isar6 = 0x00000000; + cpu->clidr = 0x00000000; + cpu->ctr = 0x8000c000; +} + +static void cortex_m55_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_V8_1M); + set_feature(&cpu->env, ARM_FEATURE_M); + set_feature(&cpu->env, ARM_FEATURE_M_MAIN); + set_feature(&cpu->env, ARM_FEATURE_M_SECURITY); + set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); + cpu->midr = 0x410fd221; /* r0p1 */ + cpu->revidr = 0; + cpu->pmsav7_dregion = 16; + cpu->sau_sregion = 8; + /* These are the MVFR* values for the FPU + full MVE configuration */ + cpu->isar.mvfr0 = 0x10110221; + cpu->isar.mvfr1 = 0x12100211; + cpu->isar.mvfr2 = 0x00000040; + cpu->isar.id_pfr0 = 0x20000030; + cpu->isar.id_pfr1 = 0x00000230; + cpu->isar.id_dfr0 = 0x10200000; + cpu->id_afr0 = 0x00000000; + cpu->isar.id_mmfr0 = 0x00111040; + cpu->isar.id_mmfr1 = 0x00000000; + cpu->isar.id_mmfr2 = 0x01000000; + cpu->isar.id_mmfr3 = 0x00000011; + cpu->isar.id_isar0 = 0x01103110; + cpu->isar.id_isar1 = 0x02212000; + cpu->isar.id_isar2 = 0x20232232; + cpu->isar.id_isar3 = 0x01111131; + cpu->isar.id_isar4 = 0x01310132; + cpu->isar.id_isar5 = 0x00000000; + cpu->isar.id_isar6 = 0x00000000; + cpu->clidr = 0x00000000; /* caches not implemented */ + cpu->ctr = 0x8303c003; +} + +static const TCGCPUOps arm_v7m_tcg_ops = { + .initialize = arm_translate_init, + .synchronize_from_tb = arm_cpu_synchronize_from_tb, + .debug_excp_handler = arm_debug_excp_handler, + .restore_state_to_opc = arm_restore_state_to_opc, + +#ifdef CONFIG_USER_ONLY + .record_sigsegv = arm_cpu_record_sigsegv, + .record_sigbus = arm_cpu_record_sigbus, +#else + .tlb_fill = arm_cpu_tlb_fill, + .cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt, + .do_interrupt = arm_v7m_cpu_do_interrupt, + .do_transaction_failed = arm_cpu_do_transaction_failed, + .do_unaligned_access = arm_cpu_do_unaligned_access, + .adjust_watchpoint_address = arm_adjust_watchpoint_address, + .debug_check_watchpoint = arm_debug_check_watchpoint, + .debug_check_breakpoint = arm_debug_check_breakpoint, +#endif /* !CONFIG_USER_ONLY */ +}; + +static void arm_v7m_class_init(ObjectClass *oc, void *data) +{ + ARMCPUClass *acc = ARM_CPU_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + + acc->info = data; + cc->tcg_ops = &arm_v7m_tcg_ops; + cc->gdb_core_xml_file = "arm-m-profile.xml"; +} + +static const ARMCPUInfo arm_v7m_cpus[] = { + { .name = "cortex-m0", .initfn = cortex_m0_initfn, + .class_init = arm_v7m_class_init }, + { .name = "cortex-m3", .initfn = cortex_m3_initfn, + .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-m55", .initfn = cortex_m55_initfn, + .class_init = arm_v7m_class_init }, +}; + +static void arm_v7m_cpu_register_types(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(arm_v7m_cpus); ++i) { + arm_cpu_register(&arm_v7m_cpus[i]); + } +} + +type_init(arm_v7m_cpu_register_types) diff --git a/target/arm/cpu_tcg.c b/target/arm/tcg/cpu32.c similarity index 72% rename from target/arm/cpu_tcg.c rename to target/arm/tcg/cpu32.c index b751a19c8a..de8f2be941 100644 --- a/target/arm/cpu_tcg.c +++ b/target/arm/tcg/cpu32.c @@ -1,5 +1,5 @@ /* - * QEMU ARM TCG CPUs. + * QEMU ARM TCG-only CPUs. * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -10,9 +10,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" -#endif /* CONFIG_TCG */ #include "internals.h" #include "target/arm/idau.h" #if !defined(CONFIG_USER_ONLY) @@ -61,12 +59,17 @@ void aa32_max_features(ARMCPU *cpu) cpu->isar.id_mmfr3 = t; t = cpu->isar.id_mmfr4; - t = FIELD_DP32(t, ID_MMFR4, HPDS, 1); /* FEAT_AA32HPD */ + t = FIELD_DP32(t, ID_MMFR4, HPDS, 2); /* FEAT_HPDS2 */ t = FIELD_DP32(t, ID_MMFR4, AC2, 1); /* ACTLR2, HACTLR2 */ t = FIELD_DP32(t, ID_MMFR4, CNP, 1); /* FEAT_TTCNP */ - t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* FEAT_XNX*/ + t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* FEAT_XNX */ + t = FIELD_DP32(t, ID_MMFR4, EVT, 2); /* FEAT_EVT */ cpu->isar.id_mmfr4 = t; + t = cpu->isar.id_mmfr5; + t = FIELD_DP32(t, ID_MMFR5, ETS, 1); /* FEAT_ETS */ + cpu->isar.id_mmfr5 = t; + t = cpu->isar.id_pfr0; t = FIELD_DP32(t, ID_PFR0, CSV2, 2); /* FEAT_CVS2 */ t = FIELD_DP32(t, ID_PFR0, DIT, 1); /* FEAT_DIT */ @@ -81,98 +84,17 @@ void aa32_max_features(ARMCPU *cpu) t = cpu->isar.id_dfr0; t = FIELD_DP32(t, ID_DFR0, COPDBG, 9); /* FEAT_Debugv8p4 */ t = FIELD_DP32(t, ID_DFR0, COPSDBG, 9); /* FEAT_Debugv8p4 */ - t = FIELD_DP32(t, ID_DFR0, PERFMON, 5); /* FEAT_PMUv3p4 */ + t = FIELD_DP32(t, ID_DFR0, PERFMON, 6); /* FEAT_PMUv3p5 */ cpu->isar.id_dfr0 = t; + + t = cpu->isar.id_dfr1; + t = FIELD_DP32(t, ID_DFR1, HPMN0, 1); /* FEAT_HPMN0 */ + cpu->isar.id_dfr1 = t; } -#ifndef CONFIG_USER_ONLY -static uint64_t l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - ARMCPU *cpu = env_archcpu(env); - - /* Number of cores is in [25:24]; otherwise we RAZ */ - return (cpu->core_count - 1) << 24; -} - -static const ARMCPRegInfo cortex_a72_a57_a53_cp_reginfo[] = { - { .name = "L2CTLR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 1, .crn = 11, .crm = 0, .opc2 = 2, - .access = PL1_RW, .readfn = l2ctlr_read, - .writefn = arm_cp_write_ignore }, - { .name = "L2CTLR", - .cp = 15, .opc1 = 1, .crn = 9, .crm = 0, .opc2 = 2, - .access = PL1_RW, .readfn = l2ctlr_read, - .writefn = arm_cp_write_ignore }, - { .name = "L2ECTLR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 1, .crn = 11, .crm = 0, .opc2 = 3, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "L2ECTLR", - .cp = 15, .opc1 = 1, .crn = 9, .crm = 0, .opc2 = 3, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "L2ACTLR", .state = ARM_CP_STATE_BOTH, - .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPUACTLR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPUACTLR", - .cp = 15, .opc1 = 0, .crm = 15, - .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, - { .name = "CPUECTLR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 1, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPUECTLR", - .cp = 15, .opc1 = 1, .crm = 15, - .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, - { .name = "CPUMERRSR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 2, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPUMERRSR", - .cp = 15, .opc1 = 2, .crm = 15, - .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, - { .name = "L2MERRSR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 2, .opc2 = 3, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "L2MERRSR", - .cp = 15, .opc1 = 3, .crm = 15, - .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, -}; - -void define_cortex_a72_a57_a53_cp_reginfo(ARMCPU *cpu) -{ - define_arm_cp_regs(cpu, cortex_a72_a57_a53_cp_reginfo); -} -#endif /* !CONFIG_USER_ONLY */ - /* CPU models. These are not needed for the AArch64 linux-user build. */ #if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) -#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) -static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request) -{ - CPUClass *cc = CPU_GET_CLASS(cs); - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - bool ret = false; - - /* - * ARMv7-M interrupt masking works differently than -A or -R. - * There is no FIQ/IRQ distinction. Instead of I and F bits - * masking FIQ and IRQ interrupts, an exception is taken only - * if it is higher priority than the current execution priority - * (which depends on state like BASEPRI, FAULTMASK and the - * currently active exception). - */ - if (interrupt_request & CPU_INTERRUPT_HARD - && (armv7m_nvic_can_take_pending_exception(env->nvic))) { - cs->exception_index = EXCP_IRQ; - cc->tcg_ops->do_interrupt(cs); - ret = true; - } - return ret; -} -#endif /* !CONFIG_USER_ONLY && CONFIG_TCG */ - static void arm926_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -400,6 +322,7 @@ static void cortex_a8_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); cpu->midr = 0x410fc080; cpu->reset_fpsid = 0x410330c0; cpu->isar.mvfr0 = 0x11110222; @@ -467,6 +390,7 @@ static void cortex_a9_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); /* * Note that A9 supports the MP extensions even for * A9UP and single-core A9MP (which are both different @@ -538,7 +462,6 @@ static void cortex_a7_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_EL2); set_feature(&cpu->env, ARM_FEATURE_EL3); set_feature(&cpu->env, ARM_FEATURE_PMU); - cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A7; cpu->midr = 0x410fc075; cpu->reset_fpsid = 0x41023075; cpu->isar.mvfr0 = 0x10110222; @@ -563,6 +486,8 @@ static void cortex_a7_initfn(Object *obj) cpu->isar.id_isar3 = 0x11112131; cpu->isar.id_isar4 = 0x10011142; cpu->isar.dbgdidr = 0x3515f005; + cpu->isar.dbgdevid = 0x01110f13; + cpu->isar.dbgdevid1 = 0x1; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */ cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */ @@ -585,8 +510,9 @@ static void cortex_a15_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_EL2); set_feature(&cpu->env, ARM_FEATURE_EL3); set_feature(&cpu->env, ARM_FEATURE_PMU); - cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A15; - cpu->midr = 0x412fc0f1; + /* r4p0 cpu, not requiring expensive tlb flush errata */ + cpu->midr = 0x414fc0f0; + cpu->revidr = 0x0; cpu->reset_fpsid = 0x410430f0; cpu->isar.mvfr0 = 0x10110222; cpu->isar.mvfr1 = 0x11111111; @@ -606,6 +532,8 @@ static void cortex_a15_initfn(Object *obj) cpu->isar.id_isar3 = 0x11112131; cpu->isar.id_isar4 = 0x10011142; cpu->isar.dbgdidr = 0x3515f021; + cpu->isar.dbgdevid = 0x01110f13; + cpu->isar.dbgdevid1 = 0x0; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */ cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */ @@ -614,195 +542,6 @@ static void cortex_a15_initfn(Object *obj) define_arm_cp_regs(cpu, cortexa15_cp_reginfo); } -static void cortex_m0_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - set_feature(&cpu->env, ARM_FEATURE_V6); - set_feature(&cpu->env, ARM_FEATURE_M); - - cpu->midr = 0x410cc200; - - /* - * These ID register values are not guest visible, because - * we do not implement the Main Extension. They must be set - * to values corresponding to the Cortex-M0's implemented - * features, because QEMU generally controls its emulation - * by looking at ID register fields. We use the same values as - * for the M3. - */ - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; - cpu->isar.id_dfr0 = 0x00100000; - cpu->id_afr0 = 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; - cpu->isar.id_isar3 = 0x01111110; - cpu->isar.id_isar4 = 0x01310102; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; -} - -static void cortex_m3_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); - cpu->midr = 0x410fc231; - cpu->pmsav7_dregion = 8; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; - cpu->isar.id_dfr0 = 0x00100000; - cpu->id_afr0 = 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; - cpu->isar.id_isar3 = 0x01111110; - cpu->isar.id_isar4 = 0x01310102; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; -} - -static void cortex_m4_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 = 0x410fc240; /* r0p0 */ - cpu->pmsav7_dregion = 8; - cpu->isar.mvfr0 = 0x10110021; - cpu->isar.mvfr1 = 0x11000011; - cpu->isar.mvfr2 = 0x00000000; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; - cpu->isar.id_dfr0 = 0x00100000; - cpu->id_afr0 = 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; - cpu->isar.id_isar3 = 0x01111110; - cpu->isar.id_isar4 = 0x01310102; - cpu->isar.id_isar5 = 0x00000000; - 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->isar.id_pfr0 = 0x00000030; - cpu->isar.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); - - set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_M); - set_feature(&cpu->env, ARM_FEATURE_M_MAIN); - set_feature(&cpu->env, ARM_FEATURE_M_SECURITY); - set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); - cpu->midr = 0x410fd213; /* r0p3 */ - cpu->pmsav7_dregion = 16; - cpu->sau_sregion = 8; - cpu->isar.mvfr0 = 0x10110021; - cpu->isar.mvfr1 = 0x11000011; - cpu->isar.mvfr2 = 0x00000040; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000210; - cpu->isar.id_dfr0 = 0x00200000; - cpu->id_afr0 = 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; - cpu->isar.id_isar3 = 0x01111131; - cpu->isar.id_isar4 = 0x01310132; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; - cpu->clidr = 0x00000000; - cpu->ctr = 0x8000c000; -} - -static void cortex_m55_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_V8_1M); - set_feature(&cpu->env, ARM_FEATURE_M); - set_feature(&cpu->env, ARM_FEATURE_M_MAIN); - set_feature(&cpu->env, ARM_FEATURE_M_SECURITY); - set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); - cpu->midr = 0x410fd221; /* r0p1 */ - cpu->revidr = 0; - cpu->pmsav7_dregion = 16; - cpu->sau_sregion = 8; - /* These are the MVFR* values for the FPU + full MVE configuration */ - cpu->isar.mvfr0 = 0x10110221; - cpu->isar.mvfr1 = 0x12100211; - cpu->isar.mvfr2 = 0x00000040; - cpu->isar.id_pfr0 = 0x20000030; - cpu->isar.id_pfr1 = 0x00000230; - cpu->isar.id_dfr0 = 0x10200000; - cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00111040; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x01000000; - cpu->isar.id_mmfr3 = 0x00000011; - cpu->isar.id_isar0 = 0x01103110; - cpu->isar.id_isar1 = 0x02212000; - cpu->isar.id_isar2 = 0x20232232; - cpu->isar.id_isar3 = 0x01111131; - cpu->isar.id_isar4 = 0x01310132; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; - cpu->clidr = 0x00000000; /* caches not implemented */ - cpu->ctr = 0x8303c003; -} - static const ARMCPRegInfo cortexr5_cp_reginfo[] = { /* Dummy the TCM region regs for the moment */ { .name = "ATCM", .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 0, @@ -843,6 +582,156 @@ static void cortex_r5_initfn(Object *obj) define_arm_cp_regs(cpu, cortexr5_cp_reginfo); } +static const ARMCPRegInfo cortex_r52_cp_reginfo[] = { + { .name = "CPUACTLR", .cp = 15, .opc1 = 0, .crm = 15, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, + { .name = "IMP_ATCMREGIONR", + .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_BTCMREGIONR", + .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_CTCMREGIONR", + .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_CSCTLR", + .cp = 15, .opc1 = 1, .crn = 9, .crm = 1, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_BPCTLR", + .cp = 15, .opc1 = 1, .crn = 9, .crm = 1, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_MEMPROTCLR", + .cp = 15, .opc1 = 1, .crn = 9, .crm = 1, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_SLAVEPCTLR", + .cp = 15, .opc1 = 0, .crn = 11, .crm = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_PERIPHREGIONR", + .cp = 15, .opc1 = 0, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_FLASHIFREGIONR", + .cp = 15, .opc1 = 0, .crn = 15, .crm = 0, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_BUILDOPTR", + .cp = 15, .opc1 = 0, .crn = 15, .crm = 2, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_PINOPTR", + .cp = 15, .opc1 = 0, .crn = 15, .crm = 2, .opc2 = 7, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_QOSR", + .cp = 15, .opc1 = 1, .crn = 15, .crm = 3, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_BUSTIMEOUTR", + .cp = 15, .opc1 = 1, .crn = 15, .crm = 3, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_INTMONR", + .cp = 15, .opc1 = 1, .crn = 15, .crm = 3, .opc2 = 4, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_ICERR0", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_ICERR1", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 0, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_DCERR0", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 1, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_DCERR1", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 1, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_TCMERR0", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 2, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_TCMERR1", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 2, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_TCMSYNDR0", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 2, .opc2 = 2, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_TCMSYNDR1", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 2, .opc2 = 3, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_FLASHERR0", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 3, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_FLASHERR1", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 3, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_CDBGDR0", + .cp = 15, .opc1 = 3, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_CBDGBR1", + .cp = 15, .opc1 = 3, .crn = 15, .crm = 0, .opc2 = 1, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_TESTR0", + .cp = 15, .opc1 = 4, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_TESTR1", + .cp = 15, .opc1 = 4, .crn = 15, .crm = 0, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NOP, .resetvalue = 0 }, + { .name = "IMP_CDBGDCI", + .cp = 15, .opc1 = 0, .crn = 15, .crm = 15, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NOP, .resetvalue = 0 }, + { .name = "IMP_CDBGDCT", + .cp = 15, .opc1 = 3, .crn = 15, .crm = 2, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NOP, .resetvalue = 0 }, + { .name = "IMP_CDBGICT", + .cp = 15, .opc1 = 3, .crn = 15, .crm = 2, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NOP, .resetvalue = 0 }, + { .name = "IMP_CDBGDCD", + .cp = 15, .opc1 = 3, .crn = 15, .crm = 4, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NOP, .resetvalue = 0 }, + { .name = "IMP_CDBGICD", + .cp = 15, .opc1 = 3, .crn = 15, .crm = 4, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NOP, .resetvalue = 0 }, +}; + + +static void cortex_r52_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_PMSA); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_AUXCR); + cpu->midr = 0x411fd133; /* r1p3 */ + cpu->revidr = 0x00000000; + cpu->reset_fpsid = 0x41034023; + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x12111111; + cpu->isar.mvfr2 = 0x00000043; + cpu->ctr = 0x8144c004; + cpu->reset_sctlr = 0x30c50838; + cpu->isar.id_pfr0 = 0x00000131; + cpu->isar.id_pfr1 = 0x10111001; + cpu->isar.id_dfr0 = 0x03010006; + cpu->id_afr0 = 0x00000000; + cpu->isar.id_mmfr0 = 0x00211040; + cpu->isar.id_mmfr1 = 0x40000000; + cpu->isar.id_mmfr2 = 0x01200000; + cpu->isar.id_mmfr3 = 0xf0102211; + cpu->isar.id_mmfr4 = 0x00000010; + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232142; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00010142; + cpu->isar.id_isar5 = 0x00010001; + cpu->isar.dbgdidr = 0x77168000; + cpu->clidr = (1 << 27) | (1 << 24) | 0x3; + cpu->ccsidr[0] = 0x700fe01a; /* 32KB L1 dcache */ + cpu->ccsidr[1] = 0x201fe00a; /* 32KB L1 icache */ + + cpu->pmsav7_dregion = 16; + cpu->pmsav8r_hdregion = 16; + + define_arm_cp_regs(cpu, cortex_r52_cp_reginfo); +} + static void cortex_r5f_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -1020,41 +909,6 @@ static void pxa270c5_initfn(Object *obj) cpu->reset_sctlr = 0x00000078; } -#ifdef CONFIG_TCG -static const struct TCGCPUOps arm_v7m_tcg_ops = { - .initialize = arm_translate_init, - .synchronize_from_tb = arm_cpu_synchronize_from_tb, - .debug_excp_handler = arm_debug_excp_handler, - -#ifdef CONFIG_USER_ONLY - .record_sigsegv = arm_cpu_record_sigsegv, - .record_sigbus = arm_cpu_record_sigbus, -#else - .tlb_fill = arm_cpu_tlb_fill, - .cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt, - .do_interrupt = arm_v7m_cpu_do_interrupt, - .do_transaction_failed = arm_cpu_do_transaction_failed, - .do_unaligned_access = arm_cpu_do_unaligned_access, - .adjust_watchpoint_address = arm_adjust_watchpoint_address, - .debug_check_watchpoint = arm_debug_check_watchpoint, - .debug_check_breakpoint = arm_debug_check_breakpoint, -#endif /* !CONFIG_USER_ONLY */ -}; -#endif /* CONFIG_TCG */ - -static void arm_v7m_class_init(ObjectClass *oc, void *data) -{ - ARMCPUClass *acc = ARM_CPU_CLASS(oc); - CPUClass *cc = CPU_CLASS(oc); - - acc->info = data; -#ifdef CONFIG_TCG - cc->tcg_ops = &arm_v7m_tcg_ops; -#endif /* CONFIG_TCG */ - - cc->gdb_core_xml_file = "arm-m-profile.xml"; -} - #ifndef TARGET_AARCH64 /* * -cpu max: a CPU with as many features enabled as our emulation supports. @@ -1098,6 +952,8 @@ static void arm_max_initfn(Object *obj) cpu->isar.id_isar5 = 0x00011121; cpu->isar.id_isar6 = 0; cpu->isar.dbgdidr = 0x3516d000; + cpu->isar.dbgdevid = 0x00110f13; + cpu->isar.dbgdevid1 = 0x2; cpu->isar.reset_pmcr_el0 = 0x41013000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ @@ -1135,20 +991,9 @@ static const ARMCPUInfo arm_tcg_cpus[] = { { .name = "cortex-a8", .initfn = cortex_a8_initfn }, { .name = "cortex-a9", .initfn = cortex_a9_initfn }, { .name = "cortex-a15", .initfn = cortex_a15_initfn }, - { .name = "cortex-m0", .initfn = cortex_m0_initfn, - .class_init = arm_v7m_class_init }, - { .name = "cortex-m3", .initfn = cortex_m3_initfn, - .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-m55", .initfn = cortex_m55_initfn, - .class_init = arm_v7m_class_init }, { .name = "cortex-r5", .initfn = cortex_r5_initfn }, { .name = "cortex-r5f", .initfn = cortex_r5f_initfn }, + { .name = "cortex-r52", .initfn = cortex_r52_initfn }, { .name = "ti925t", .initfn = ti925t_initfn }, { .name = "sa1100", .initfn = sa1100_initfn }, { .name = "sa1110", .initfn = sa1110_initfn }, diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c new file mode 100644 index 0000000000..9f7a9f3d2c --- /dev/null +++ b/target/arm/tcg/cpu64.c @@ -0,0 +1,1295 @@ +/* + * QEMU AArch64 TCG CPUs + * + * Copyright (c) 2013 Linaro Ltd + * + * 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 "qapi/error.h" +#include "cpu.h" +#include "qemu/module.h" +#include "qapi/visitor.h" +#include "hw/qdev-properties.h" +#include "qemu/units.h" +#include "internals.h" +#include "cpu-features.h" +#include "cpregs.h" + +static uint64_t make_ccsidr64(unsigned assoc, unsigned linesize, + unsigned cachesize) +{ + unsigned lg_linesize = ctz32(linesize); + unsigned sets; + + /* + * The 64-bit CCSIDR_EL1 format is: + * [55:32] number of sets - 1 + * [23:3] associativity - 1 + * [2:0] log2(linesize) - 4 + * so 0 == 16 bytes, 1 == 32 bytes, 2 == 64 bytes, etc + */ + assert(assoc != 0); + assert(is_power_of_2(linesize)); + assert(lg_linesize >= 4 && lg_linesize <= 7 + 4); + + /* sets * associativity * linesize == cachesize. */ + sets = cachesize / (assoc * linesize); + assert(cachesize % (assoc * linesize) == 0); + + return ((uint64_t)(sets - 1) << 32) + | ((assoc - 1) << 3) + | (lg_linesize - 4); +} + +static void aarch64_a35_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a35"; + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); + + /* From B2.2 AArch64 identification registers. */ + cpu->midr = 0x411fd040; + cpu->revidr = 0; + cpu->ctr = 0x84448004; + cpu->isar.id_pfr0 = 0x00000131; + cpu->isar.id_pfr1 = 0x00011011; + cpu->isar.id_dfr0 = 0x03010066; + cpu->id_afr0 = 0; + 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; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00011142; + cpu->isar.id_isar5 = 0x00011121; + cpu->isar.id_aa64pfr0 = 0x00002222; + cpu->isar.id_aa64pfr1 = 0; + cpu->isar.id_aa64dfr0 = 0x10305106; + cpu->isar.id_aa64dfr1 = 0; + cpu->isar.id_aa64isar0 = 0x00011120; + cpu->isar.id_aa64isar1 = 0; + cpu->isar.id_aa64mmfr0 = 0x00101122; + cpu->isar.id_aa64mmfr1 = 0; + cpu->clidr = 0x0a200023; + cpu->dcz_blocksize = 4; + + /* From B2.4 AArch64 Virtual Memory control registers */ + cpu->reset_sctlr = 0x00c50838; + + /* From B2.10 AArch64 performance monitor registers */ + cpu->isar.reset_pmcr_el0 = 0x410a3000; + + /* From B2.29 Cache ID registers */ + cpu->ccsidr[0] = 0x700fe01a; /* 32KB L1 dcache */ + cpu->ccsidr[1] = 0x201fe00a; /* 32KB L1 icache */ + cpu->ccsidr[2] = 0x703fe03a; /* 512KB L2 cache */ + + /* From B3.5 VGIC Type register */ + cpu->gic_num_lrs = 4; + cpu->gic_vpribits = 5; + cpu->gic_vprebits = 5; + cpu->gic_pribits = 5; + + /* From C6.4 Debug ID Register */ + cpu->isar.dbgdidr = 0x3516d000; + /* From C6.5 Debug Device ID Register */ + cpu->isar.dbgdevid = 0x00110f13; + /* From C6.6 Debug Device ID Register 1 */ + cpu->isar.dbgdevid1 = 0x2; + + /* From Cortex-A35 SIMD and Floating-point Support r1p0 */ + /* From 3.2 AArch32 register summary */ + cpu->reset_fpsid = 0x41034043; + + /* From 2.2 AArch64 register summary */ + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x12111111; + cpu->isar.mvfr2 = 0x00000043; + + /* These values are the same with A53/A57/A72. */ + define_cortex_a72_a57_a53_cp_reginfo(cpu); +} + +static void cpu_max_get_sve_max_vq(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + uint32_t value; + + /* All vector lengths are disabled when SVE is off. */ + if (!cpu_isar_feature(aa64_sve, cpu)) { + value = 0; + } else { + value = cpu->sve_max_vq; + } + visit_type_uint32(v, name, &value, errp); +} + +static void cpu_max_set_sve_max_vq(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + uint32_t max_vq; + + if (!visit_type_uint32(v, name, &max_vq, errp)) { + return; + } + + if (max_vq == 0 || max_vq > ARM_MAX_VQ) { + error_setg(errp, "unsupported SVE vector length"); + error_append_hint(errp, "Valid sve-max-vq in range [1-%d]\n", + ARM_MAX_VQ); + return; + } + + cpu->sve_max_vq = max_vq; +} + +static bool cpu_arm_get_rme(Object *obj, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + return cpu_isar_feature(aa64_rme, cpu); +} + +static void cpu_arm_set_rme(Object *obj, bool value, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + uint64_t t; + + t = cpu->isar.id_aa64pfr0; + t = FIELD_DP64(t, ID_AA64PFR0, RME, value); + cpu->isar.id_aa64pfr0 = t; +} + +static void cpu_max_set_l0gptsz(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + uint32_t value; + + if (!visit_type_uint32(v, name, &value, errp)) { + return; + } + + /* Encode the value for the GPCCR_EL3 field. */ + switch (value) { + case 30: + case 34: + case 36: + case 39: + cpu->reset_l0gptsz = value - 30; + break; + default: + error_setg(errp, "invalid value for l0gptsz"); + error_append_hint(errp, "valid values are 30, 34, 36, 39\n"); + break; + } +} + +static void cpu_max_get_l0gptsz(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + uint32_t value = cpu->reset_l0gptsz + 30; + + visit_type_uint32(v, name, &value, errp); +} + +static Property arm_cpu_lpa2_property = + DEFINE_PROP_BOOL("lpa2", ARMCPU, prop_lpa2, true); + +static void aarch64_a55_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a55"; + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); + + /* Ordered by B2.4 AArch64 registers by functional group */ + cpu->clidr = 0x82000023; + cpu->ctr = 0x84448004; /* L1Ip = VIPT */ + cpu->dcz_blocksize = 4; /* 64 bytes */ + cpu->isar.id_aa64dfr0 = 0x0000000010305408ull; + cpu->isar.id_aa64isar0 = 0x0000100010211120ull; + cpu->isar.id_aa64isar1 = 0x0000000000100001ull; + cpu->isar.id_aa64mmfr0 = 0x0000000000101122ull; + cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; + cpu->isar.id_aa64mmfr2 = 0x0000000000001011ull; + cpu->isar.id_aa64pfr0 = 0x0000000010112222ull; + cpu->isar.id_aa64pfr1 = 0x0000000000000010ull; + cpu->id_afr0 = 0x00000000; + cpu->isar.id_dfr0 = 0x04010088; + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232042; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00011142; + cpu->isar.id_isar5 = 0x01011121; + cpu->isar.id_isar6 = 0x00000010; + cpu->isar.id_mmfr0 = 0x10201105; + cpu->isar.id_mmfr1 = 0x40000000; + cpu->isar.id_mmfr2 = 0x01260000; + cpu->isar.id_mmfr3 = 0x02122211; + cpu->isar.id_mmfr4 = 0x00021110; + cpu->isar.id_pfr0 = 0x10010131; + cpu->isar.id_pfr1 = 0x00011011; + cpu->isar.id_pfr2 = 0x00000011; + cpu->midr = 0x412FD050; /* r2p0 */ + cpu->revidr = 0; + + /* From B2.23 CCSIDR_EL1 */ + cpu->ccsidr[0] = 0x700fe01a; /* 32KB L1 dcache */ + cpu->ccsidr[1] = 0x200fe01a; /* 32KB L1 icache */ + cpu->ccsidr[2] = 0x703fe07a; /* 512KB L2 cache */ + + /* From B2.96 SCTLR_EL3 */ + cpu->reset_sctlr = 0x30c50838; + + /* From B4.45 ICH_VTR_EL2 */ + cpu->gic_num_lrs = 4; + cpu->gic_vpribits = 5; + cpu->gic_vprebits = 5; + cpu->gic_pribits = 5; + + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x13211111; + cpu->isar.mvfr2 = 0x00000043; + + /* From D5.4 AArch64 PMU register summary */ + cpu->isar.reset_pmcr_el0 = 0x410b3000; +} + +static void aarch64_a72_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a72"; + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); + cpu->midr = 0x410fd083; + cpu->revidr = 0x00000000; + cpu->reset_fpsid = 0x41034080; + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x12111111; + cpu->isar.mvfr2 = 0x00000043; + cpu->ctr = 0x8444c004; + cpu->reset_sctlr = 0x00c50838; + cpu->isar.id_pfr0 = 0x00000131; + cpu->isar.id_pfr1 = 0x00011011; + cpu->isar.id_dfr0 = 0x03010066; + cpu->id_afr0 = 0x00000000; + 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; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00011142; + cpu->isar.id_isar5 = 0x00011121; + cpu->isar.id_aa64pfr0 = 0x00002222; + cpu->isar.id_aa64dfr0 = 0x10305106; + cpu->isar.id_aa64isar0 = 0x00011120; + cpu->isar.id_aa64mmfr0 = 0x00001124; + cpu->isar.dbgdidr = 0x3516d000; + cpu->isar.dbgdevid = 0x01110f13; + cpu->isar.dbgdevid1 = 0x2; + cpu->isar.reset_pmcr_el0 = 0x41023000; + cpu->clidr = 0x0a200023; + cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ + cpu->ccsidr[1] = 0x201fe012; /* 48KB L1 icache */ + cpu->ccsidr[2] = 0x707fe07a; /* 1MB L2 cache */ + cpu->dcz_blocksize = 4; /* 64 bytes */ + cpu->gic_num_lrs = 4; + cpu->gic_vpribits = 5; + cpu->gic_vprebits = 5; + cpu->gic_pribits = 5; + define_cortex_a72_a57_a53_cp_reginfo(cpu); +} + +static void aarch64_a76_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a76"; + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); + + /* Ordered by B2.4 AArch64 registers by functional group */ + cpu->clidr = 0x82000023; + cpu->ctr = 0x8444C004; + cpu->dcz_blocksize = 4; + cpu->isar.id_aa64dfr0 = 0x0000000010305408ull; + cpu->isar.id_aa64isar0 = 0x0000100010211120ull; + cpu->isar.id_aa64isar1 = 0x0000000000100001ull; + cpu->isar.id_aa64mmfr0 = 0x0000000000101122ull; + cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; + cpu->isar.id_aa64mmfr2 = 0x0000000000001011ull; + cpu->isar.id_aa64pfr0 = 0x1100000010111112ull; /* GIC filled in later */ + cpu->isar.id_aa64pfr1 = 0x0000000000000010ull; + cpu->id_afr0 = 0x00000000; + cpu->isar.id_dfr0 = 0x04010088; + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232042; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00010142; + cpu->isar.id_isar5 = 0x01011121; + cpu->isar.id_isar6 = 0x00000010; + cpu->isar.id_mmfr0 = 0x10201105; + cpu->isar.id_mmfr1 = 0x40000000; + cpu->isar.id_mmfr2 = 0x01260000; + cpu->isar.id_mmfr3 = 0x02122211; + cpu->isar.id_mmfr4 = 0x00021110; + cpu->isar.id_pfr0 = 0x10010131; + cpu->isar.id_pfr1 = 0x00010000; /* GIC filled in later */ + cpu->isar.id_pfr2 = 0x00000011; + cpu->midr = 0x414fd0b1; /* r4p1 */ + cpu->revidr = 0; + + /* From B2.18 CCSIDR_EL1 */ + cpu->ccsidr[0] = 0x701fe01a; /* 64KB L1 dcache */ + cpu->ccsidr[1] = 0x201fe01a; /* 64KB L1 icache */ + cpu->ccsidr[2] = 0x707fe03a; /* 512KB L2 cache */ + + /* From B2.93 SCTLR_EL3 */ + cpu->reset_sctlr = 0x30c50838; + + /* From B4.23 ICH_VTR_EL2 */ + cpu->gic_num_lrs = 4; + cpu->gic_vpribits = 5; + cpu->gic_vprebits = 5; + cpu->gic_pribits = 5; + + /* From B5.1 AdvSIMD AArch64 register summary */ + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x13211111; + cpu->isar.mvfr2 = 0x00000043; + + /* From D5.1 AArch64 PMU register summary */ + cpu->isar.reset_pmcr_el0 = 0x410b3000; +} + +static void aarch64_a64fx_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,a64fx"; + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); + cpu->midr = 0x461f0010; + cpu->revidr = 0x00000000; + cpu->ctr = 0x86668006; + cpu->reset_sctlr = 0x30000180; + cpu->isar.id_aa64pfr0 = 0x0000000101111111; /* No RAS Extensions */ + cpu->isar.id_aa64pfr1 = 0x0000000000000000; + cpu->isar.id_aa64dfr0 = 0x0000000010305408; + cpu->isar.id_aa64dfr1 = 0x0000000000000000; + cpu->id_aa64afr0 = 0x0000000000000000; + cpu->id_aa64afr1 = 0x0000000000000000; + cpu->isar.id_aa64mmfr0 = 0x0000000000001122; + cpu->isar.id_aa64mmfr1 = 0x0000000011212100; + cpu->isar.id_aa64mmfr2 = 0x0000000000001011; + cpu->isar.id_aa64isar0 = 0x0000000010211120; + cpu->isar.id_aa64isar1 = 0x0000000000010001; + cpu->isar.id_aa64zfr0 = 0x0000000000000000; + cpu->clidr = 0x0000000080000023; + cpu->ccsidr[0] = 0x7007e01c; /* 64KB L1 dcache */ + cpu->ccsidr[1] = 0x2007e01c; /* 64KB L1 icache */ + cpu->ccsidr[2] = 0x70ffe07c; /* 8MB L2 cache */ + cpu->dcz_blocksize = 6; /* 256 bytes */ + cpu->gic_num_lrs = 4; + cpu->gic_vpribits = 5; + cpu->gic_vprebits = 5; + cpu->gic_pribits = 5; + + /* The A64FX supports only 128, 256 and 512 bit vector lengths */ + aarch64_add_sve_properties(obj); + cpu->sve_vq.supported = (1 << 0) /* 128bit */ + | (1 << 1) /* 256bit */ + | (1 << 3); /* 512bit */ + + cpu->isar.reset_pmcr_el0 = 0x46014040; + + /* TODO: Add A64FX specific HPC extension registers */ +} + +static CPAccessResult access_actlr_w(CPUARMState *env, const ARMCPRegInfo *r, + bool read) +{ + if (!read) { + int el = arm_current_el(env); + + /* Because ACTLR_EL2 is constant 0, writes below EL2 trap to EL2. */ + if (el < 2 && arm_is_el2_enabled(env)) { + return CP_ACCESS_TRAP_EL2; + } + /* Because ACTLR_EL3 is constant 0, writes below EL3 trap to EL3. */ + if (el < 3 && arm_feature(env, ARM_FEATURE_EL3)) { + return CP_ACCESS_TRAP_EL3; + } + } + return CP_ACCESS_OK; +} + +static const ARMCPRegInfo neoverse_n1_cp_reginfo[] = { + { .name = "ATCR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 7, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + /* Traps and enables are the same as for TCR_EL1. */ + .accessfn = access_tvm_trvm, .fgt = FGT_TCR_EL1, }, + { .name = "ATCR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 15, .crm = 7, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "ATCR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 7, .opc2 = 0, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "ATCR_EL12", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 5, .crn = 15, .crm = 7, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "AVTCR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 15, .crm = 7, .opc2 = 1, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUACTLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 1, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "CPUACTLR2_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 1, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "CPUACTLR3_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 1, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + /* + * Report CPUCFR_EL1.SCU as 1, as we do not implement the DSU + * (and in particular its system registers). + */ + { .name = "CPUCFR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 4 }, + { .name = "CPUECTLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 1, .opc2 = 4, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0x961563010, + .accessfn = access_actlr_w }, + { .name = "CPUPCR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 8, .opc2 = 1, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPMR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 8, .opc2 = 3, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPOR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 8, .opc2 = 2, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPSELR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 8, .opc2 = 0, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPWRCTLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 2, .opc2 = 7, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "ERXPFGCDN_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 2, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "ERXPFGCTL_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 2, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "ERXPFGF_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 2, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, +}; + +static void define_neoverse_n1_cp_reginfo(ARMCPU *cpu) +{ + define_arm_cp_regs(cpu, neoverse_n1_cp_reginfo); +} + +static const ARMCPRegInfo neoverse_v1_cp_reginfo[] = { + { .name = "CPUECTLR2_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 1, .opc2 = 5, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "CPUPPMCR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 2, .opc2 = 0, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPPMCR2_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 2, .opc2 = 1, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPPMCR3_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 2, .opc2 = 6, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, +}; + +static void define_neoverse_v1_cp_reginfo(ARMCPU *cpu) +{ + /* + * The Neoverse V1 has all of the Neoverse N1's IMPDEF + * registers and a few more of its own. + */ + define_arm_cp_regs(cpu, neoverse_n1_cp_reginfo); + define_arm_cp_regs(cpu, neoverse_v1_cp_reginfo); +} + +static void aarch64_neoverse_n1_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,neoverse-n1"; + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); + + /* Ordered by B2.4 AArch64 registers by functional group */ + cpu->clidr = 0x82000023; + cpu->ctr = 0x8444c004; + cpu->dcz_blocksize = 4; + cpu->isar.id_aa64dfr0 = 0x0000000110305408ull; + cpu->isar.id_aa64isar0 = 0x0000100010211120ull; + cpu->isar.id_aa64isar1 = 0x0000000000100001ull; + cpu->isar.id_aa64mmfr0 = 0x0000000000101125ull; + cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; + cpu->isar.id_aa64mmfr2 = 0x0000000000001011ull; + cpu->isar.id_aa64pfr0 = 0x1100000010111112ull; /* GIC filled in later */ + cpu->isar.id_aa64pfr1 = 0x0000000000000020ull; + cpu->id_afr0 = 0x00000000; + cpu->isar.id_dfr0 = 0x04010088; + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232042; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00010142; + cpu->isar.id_isar5 = 0x01011121; + cpu->isar.id_isar6 = 0x00000010; + cpu->isar.id_mmfr0 = 0x10201105; + cpu->isar.id_mmfr1 = 0x40000000; + cpu->isar.id_mmfr2 = 0x01260000; + cpu->isar.id_mmfr3 = 0x02122211; + cpu->isar.id_mmfr4 = 0x00021110; + cpu->isar.id_pfr0 = 0x10010131; + cpu->isar.id_pfr1 = 0x00010000; /* GIC filled in later */ + cpu->isar.id_pfr2 = 0x00000011; + cpu->midr = 0x414fd0c1; /* r4p1 */ + cpu->revidr = 0; + + /* From B2.23 CCSIDR_EL1 */ + cpu->ccsidr[0] = 0x701fe01a; /* 64KB L1 dcache */ + cpu->ccsidr[1] = 0x201fe01a; /* 64KB L1 icache */ + cpu->ccsidr[2] = 0x70ffe03a; /* 1MB L2 cache */ + + /* From B2.98 SCTLR_EL3 */ + cpu->reset_sctlr = 0x30c50838; + + /* From B4.23 ICH_VTR_EL2 */ + cpu->gic_num_lrs = 4; + cpu->gic_vpribits = 5; + cpu->gic_vprebits = 5; + cpu->gic_pribits = 5; + + /* From B5.1 AdvSIMD AArch64 register summary */ + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x13211111; + cpu->isar.mvfr2 = 0x00000043; + + /* From D5.1 AArch64 PMU register summary */ + cpu->isar.reset_pmcr_el0 = 0x410c3000; + + define_neoverse_n1_cp_reginfo(cpu); +} + +static void aarch64_neoverse_v1_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,neoverse-v1"; + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); + + /* Ordered by 3.2.4 AArch64 registers by functional group */ + cpu->clidr = 0x82000023; + cpu->ctr = 0xb444c004; /* With DIC and IDC set */ + cpu->dcz_blocksize = 4; + cpu->id_aa64afr0 = 0x00000000; + cpu->id_aa64afr1 = 0x00000000; + cpu->isar.id_aa64dfr0 = 0x000001f210305519ull; + cpu->isar.id_aa64dfr1 = 0x00000000; + cpu->isar.id_aa64isar0 = 0x1011111110212120ull; /* with FEAT_RNG */ + cpu->isar.id_aa64isar1 = 0x0111000001211032ull; + cpu->isar.id_aa64mmfr0 = 0x0000000000101125ull; + cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; + cpu->isar.id_aa64mmfr2 = 0x0220011102101011ull; + cpu->isar.id_aa64pfr0 = 0x1101110120111112ull; /* GIC filled in later */ + cpu->isar.id_aa64pfr1 = 0x0000000000000020ull; + cpu->id_afr0 = 0x00000000; + cpu->isar.id_dfr0 = 0x15011099; + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232042; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00010142; + cpu->isar.id_isar5 = 0x11011121; + cpu->isar.id_isar6 = 0x01100111; + cpu->isar.id_mmfr0 = 0x10201105; + cpu->isar.id_mmfr1 = 0x40000000; + cpu->isar.id_mmfr2 = 0x01260000; + cpu->isar.id_mmfr3 = 0x02122211; + cpu->isar.id_mmfr4 = 0x01021110; + cpu->isar.id_pfr0 = 0x21110131; + cpu->isar.id_pfr1 = 0x00010000; /* GIC filled in later */ + cpu->isar.id_pfr2 = 0x00000011; + cpu->midr = 0x411FD402; /* r1p2 */ + cpu->revidr = 0; + + /* + * The Neoverse-V1 r1p2 TRM lists 32-bit format CCSIDR_EL1 values, + * but also says it implements CCIDX, which means they should be + * 64-bit format. So we here use values which are based on the textual + * information in chapter 2 of the TRM: + * + * L1: 4-way set associative 64-byte line size, total size 64K. + * L2: 8-way set associative, 64 byte line size, either 512K or 1MB. + * L3: No L3 (this matches the CLIDR_EL1 value). + */ + cpu->ccsidr[0] = make_ccsidr64(4, 64, 64 * KiB); /* L1 dcache */ + cpu->ccsidr[1] = cpu->ccsidr[0]; /* L1 icache */ + cpu->ccsidr[2] = make_ccsidr64(8, 64, 1 * MiB); /* L2 cache */ + + /* From 3.2.115 SCTLR_EL3 */ + cpu->reset_sctlr = 0x30c50838; + + /* From 3.4.8 ICC_CTLR_EL3 and 3.4.23 ICH_VTR_EL2 */ + cpu->gic_num_lrs = 4; + cpu->gic_vpribits = 5; + cpu->gic_vprebits = 5; + cpu->gic_pribits = 5; + + /* From 3.5.1 AdvSIMD AArch64 register summary */ + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x13211111; + cpu->isar.mvfr2 = 0x00000043; + + /* From 3.7.5 ID_AA64ZFR0_EL1 */ + cpu->isar.id_aa64zfr0 = 0x0000100000100000; + cpu->sve_vq.supported = (1 << 0) /* 128bit */ + | (1 << 1); /* 256bit */ + + /* From 5.5.1 AArch64 PMU register summary */ + cpu->isar.reset_pmcr_el0 = 0x41213000; + + define_neoverse_v1_cp_reginfo(cpu); + + aarch64_add_pauth_properties(obj); + aarch64_add_sve_properties(obj); +} + +static const ARMCPRegInfo cortex_a710_cp_reginfo[] = { + { .name = "CPUACTLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 1, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "CPUACTLR2_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 1, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "CPUACTLR3_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 1, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "CPUACTLR4_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 1, .opc2 = 3, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "CPUECTLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 1, .opc2 = 4, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "CPUECTLR2_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 1, .opc2 = 5, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "CPUPPMCR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 2, .opc2 = 4, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPWRCTLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 2, .opc2 = 7, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "ATCR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 7, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUACTLR5_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 8, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "CPUACTLR6_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 8, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "CPUACTLR7_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 8, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, + .accessfn = access_actlr_w }, + { .name = "ATCR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 15, .crm = 7, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "AVTCR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 15, .crm = 7, .opc2 = 1, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPPMCR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 2, .opc2 = 0, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPPMCR2_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 2, .opc2 = 1, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPPMCR4_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 2, .opc2 = 4, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPPMCR5_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 2, .opc2 = 5, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPPMCR6_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 2, .opc2 = 6, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUACTLR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 4, .opc2 = 0, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "ATCR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 7, .opc2 = 0, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPSELR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 8, .opc2 = 0, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPCR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 8, .opc2 = 1, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPOR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 8, .opc2 = 2, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPMR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 8, .opc2 = 3, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPOR2_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 8, .opc2 = 4, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPMR2_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 8, .opc2 = 5, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPUPFR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 8, .opc2 = 6, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* + * Report CPUCFR_EL1.SCU as 1, as we do not implement the DSU + * (and in particular its system registers). + */ + { .name = "CPUCFR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 4 }, + + /* + * Stub RAMINDEX, as we don't actually implement caches, BTB, + * or anything else with cpu internal memory. + * "Read" zeros into the IDATA* and DDATA* output registers. + */ + { .name = "RAMINDEX_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL3_W, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IDATA0_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL3_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IDATA1_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 0, .opc2 = 1, + .access = PL3_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IDATA2_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 0, .opc2 = 2, + .access = PL3_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "DDATA0_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 1, .opc2 = 0, + .access = PL3_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "DDATA1_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 1, .opc2 = 1, + .access = PL3_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "DDATA2_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 1, .opc2 = 2, + .access = PL3_R, .type = ARM_CP_CONST, .resetvalue = 0 }, +}; + +static void aarch64_a710_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a710"; + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); + + /* Ordered by Section B.4: AArch64 registers */ + cpu->midr = 0x412FD471; /* r2p1 */ + cpu->revidr = 0; + cpu->isar.id_pfr0 = 0x21110131; + cpu->isar.id_pfr1 = 0x00010000; /* GIC filled in later */ + cpu->isar.id_dfr0 = 0x16011099; + cpu->id_afr0 = 0; + cpu->isar.id_mmfr0 = 0x10201105; + cpu->isar.id_mmfr1 = 0x40000000; + cpu->isar.id_mmfr2 = 0x01260000; + cpu->isar.id_mmfr3 = 0x02122211; + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232042; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00010142; + cpu->isar.id_isar5 = 0x11011121; /* with Crypto */ + cpu->isar.id_mmfr4 = 0x21021110; + cpu->isar.id_isar6 = 0x01111111; + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x13211111; + cpu->isar.mvfr2 = 0x00000043; + cpu->isar.id_pfr2 = 0x00000011; + cpu->isar.id_aa64pfr0 = 0x1201111120111112ull; /* GIC filled in later */ + cpu->isar.id_aa64pfr1 = 0x0000000000000221ull; + cpu->isar.id_aa64zfr0 = 0x0000110100110021ull; /* with Crypto */ + cpu->isar.id_aa64dfr0 = 0x000011f010305619ull; + cpu->isar.id_aa64dfr1 = 0; + cpu->id_aa64afr0 = 0; + cpu->id_aa64afr1 = 0; + cpu->isar.id_aa64isar0 = 0x0221111110212120ull; /* with Crypto */ + cpu->isar.id_aa64isar1 = 0x0010111101211052ull; + cpu->isar.id_aa64mmfr0 = 0x0000022200101122ull; + cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; + cpu->isar.id_aa64mmfr2 = 0x1221011110101011ull; + cpu->clidr = 0x0000001482000023ull; + cpu->gm_blocksize = 4; + cpu->ctr = 0x000000049444c004ull; + cpu->dcz_blocksize = 4; + /* TODO FEAT_MPAM: mpamidr_el1 = 0x0000_0001_0006_003f */ + + /* Section B.5.2: PMCR_EL0 */ + cpu->isar.reset_pmcr_el0 = 0xa000; /* with 20 counters */ + + /* Section B.6.7: ICH_VTR_EL2 */ + cpu->gic_num_lrs = 4; + cpu->gic_vpribits = 5; + cpu->gic_vprebits = 5; + cpu->gic_pribits = 5; + + /* Section 14: Scalable Vector Extensions support */ + cpu->sve_vq.supported = 1 << 0; /* 128bit */ + + /* + * The cortex-a710 TRM does not list CCSIDR values. The layout of + * the caches are in text in Table 7-1, Table 8-1, and Table 9-1. + * + * L1: 4-way set associative 64-byte line size, total either 32K or 64K. + * L2: 8-way set associative 64 byte line size, total either 256K or 512K. + */ + cpu->ccsidr[0] = make_ccsidr64(4, 64, 64 * KiB); /* L1 dcache */ + cpu->ccsidr[1] = cpu->ccsidr[0]; /* L1 icache */ + cpu->ccsidr[2] = make_ccsidr64(8, 64, 512 * KiB); /* L2 cache */ + + /* FIXME: Not documented -- copied from neoverse-v1 */ + cpu->reset_sctlr = 0x30c50838; + + define_arm_cp_regs(cpu, cortex_a710_cp_reginfo); + + aarch64_add_pauth_properties(obj); + aarch64_add_sve_properties(obj); +} + +/* Extra IMPDEF regs in the N2 beyond those in the A710 */ +static const ARMCPRegInfo neoverse_n2_cp_reginfo[] = { + { .name = "CPURNDBR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 3, .opc2 = 0, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPURNDPEID_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 15, .crm = 3, .opc2 = 1, + .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, +}; + +static void aarch64_neoverse_n2_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,neoverse-n2"; + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); + + /* Ordered by Section B.5: AArch64 ID registers */ + cpu->midr = 0x410FD493; /* r0p3 */ + cpu->revidr = 0; + cpu->isar.id_pfr0 = 0x21110131; + cpu->isar.id_pfr1 = 0x00010000; /* GIC filled in later */ + cpu->isar.id_dfr0 = 0x16011099; + cpu->id_afr0 = 0; + cpu->isar.id_mmfr0 = 0x10201105; + cpu->isar.id_mmfr1 = 0x40000000; + cpu->isar.id_mmfr2 = 0x01260000; + cpu->isar.id_mmfr3 = 0x02122211; + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232042; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00010142; + cpu->isar.id_isar5 = 0x11011121; /* with Crypto */ + cpu->isar.id_mmfr4 = 0x01021110; + cpu->isar.id_isar6 = 0x01111111; + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x13211111; + cpu->isar.mvfr2 = 0x00000043; + cpu->isar.id_pfr2 = 0x00000011; + cpu->isar.id_aa64pfr0 = 0x1201111120111112ull; /* GIC filled in later */ + cpu->isar.id_aa64pfr1 = 0x0000000000000221ull; + cpu->isar.id_aa64zfr0 = 0x0000110100110021ull; /* with Crypto */ + cpu->isar.id_aa64dfr0 = 0x000011f210305619ull; + cpu->isar.id_aa64dfr1 = 0; + cpu->id_aa64afr0 = 0; + cpu->id_aa64afr1 = 0; + cpu->isar.id_aa64isar0 = 0x1221111110212120ull; /* with Crypto and FEAT_RNG */ + cpu->isar.id_aa64isar1 = 0x0011111101211052ull; + cpu->isar.id_aa64mmfr0 = 0x0000022200101125ull; + cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; + cpu->isar.id_aa64mmfr2 = 0x1221011112101011ull; + cpu->clidr = 0x0000001482000023ull; + cpu->gm_blocksize = 4; + cpu->ctr = 0x00000004b444c004ull; + cpu->dcz_blocksize = 4; + /* TODO FEAT_MPAM: mpamidr_el1 = 0x0000_0001_001e_01ff */ + + /* Section B.7.2: PMCR_EL0 */ + cpu->isar.reset_pmcr_el0 = 0x3000; /* with 6 counters */ + + /* Section B.8.9: ICH_VTR_EL2 */ + cpu->gic_num_lrs = 4; + cpu->gic_vpribits = 5; + cpu->gic_vprebits = 5; + cpu->gic_pribits = 5; + + /* Section 14: Scalable Vector Extensions support */ + cpu->sve_vq.supported = 1 << 0; /* 128bit */ + + /* + * The Neoverse N2 TRM does not list CCSIDR values. The layout of + * the caches are in text in Table 7-1, Table 8-1, and Table 9-1. + * + * L1: 4-way set associative 64-byte line size, total 64K. + * L2: 8-way set associative 64 byte line size, total either 512K or 1024K. + */ + cpu->ccsidr[0] = make_ccsidr64(4, 64, 64 * KiB); /* L1 dcache */ + cpu->ccsidr[1] = cpu->ccsidr[0]; /* L1 icache */ + cpu->ccsidr[2] = make_ccsidr64(8, 64, 512 * KiB); /* L2 cache */ + + /* FIXME: Not documented -- copied from neoverse-v1 */ + cpu->reset_sctlr = 0x30c50838; + + /* + * The Neoverse N2 has all of the Cortex-A710 IMPDEF registers, + * and a few more RNG related ones. + */ + define_arm_cp_regs(cpu, cortex_a710_cp_reginfo); + define_arm_cp_regs(cpu, neoverse_n2_cp_reginfo); + + aarch64_add_pauth_properties(obj); + aarch64_add_sve_properties(obj); +} + +/* + * -cpu max: a CPU with as many features enabled as our emulation supports. + * The version of '-cpu max' for qemu-system-arm is defined in cpu32.c; + * this only needs to handle 64 bits. + */ +void aarch64_max_tcg_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + uint64_t t; + uint32_t u; + + /* + * Reset MIDR so the guest doesn't mistake our 'max' CPU type for a real + * one and try to apply errata workarounds or use impdef features we + * don't provide. + * An IMPLEMENTER field of 0 means "reserved for software use"; + * ARCHITECTURE must be 0xf indicating "v7 or later, check ID registers + * to see which features are present"; + * the VARIANT, PARTNUM and REVISION fields are all implementation + * defined and we choose to define PARTNUM just in case guest + * code needs to distinguish this QEMU CPU from other software + * implementations, though this shouldn't be needed. + */ + t = FIELD_DP64(0, MIDR_EL1, IMPLEMENTER, 0); + t = FIELD_DP64(t, MIDR_EL1, ARCHITECTURE, 0xf); + t = FIELD_DP64(t, MIDR_EL1, PARTNUM, 'Q'); + t = FIELD_DP64(t, MIDR_EL1, VARIANT, 0); + t = FIELD_DP64(t, MIDR_EL1, REVISION, 0); + cpu->midr = t; + + /* + * We're going to set FEAT_S2FWB, which mandates that CLIDR_EL1.{LoUU,LoUIS} + * are zero. + */ + u = cpu->clidr; + u = FIELD_DP32(u, CLIDR_EL1, LOUIS, 0); + u = FIELD_DP32(u, CLIDR_EL1, LOUU, 0); + cpu->clidr = u; + + /* + * Set CTR_EL0.DIC and IDC to tell the guest it doesnt' need to + * do any cache maintenance for data-to-instruction or + * instruction-to-guest coherence. (Our cache ops are nops.) + */ + t = cpu->ctr; + t = FIELD_DP64(t, CTR_EL0, IDC, 1); + t = FIELD_DP64(t, CTR_EL0, DIC, 1); + cpu->ctr = t; + + t = cpu->isar.id_aa64isar0; + t = FIELD_DP64(t, ID_AA64ISAR0, AES, 2); /* FEAT_PMULL */ + t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* FEAT_SHA1 */ + t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 2); /* FEAT_SHA512 */ + t = FIELD_DP64(t, ID_AA64ISAR0, CRC32, 1); /* FEAT_CRC32 */ + t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 2); /* FEAT_LSE */ + t = FIELD_DP64(t, ID_AA64ISAR0, RDM, 1); /* FEAT_RDM */ + t = FIELD_DP64(t, ID_AA64ISAR0, SHA3, 1); /* FEAT_SHA3 */ + t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 1); /* FEAT_SM3 */ + t = FIELD_DP64(t, ID_AA64ISAR0, SM4, 1); /* FEAT_SM4 */ + t = FIELD_DP64(t, ID_AA64ISAR0, DP, 1); /* FEAT_DotProd */ + t = FIELD_DP64(t, ID_AA64ISAR0, FHM, 1); /* FEAT_FHM */ + t = FIELD_DP64(t, ID_AA64ISAR0, TS, 2); /* FEAT_FlagM2 */ + t = FIELD_DP64(t, ID_AA64ISAR0, TLB, 2); /* FEAT_TLBIRANGE */ + t = FIELD_DP64(t, ID_AA64ISAR0, RNDR, 1); /* FEAT_RNG */ + cpu->isar.id_aa64isar0 = t; + + t = cpu->isar.id_aa64isar1; + t = FIELD_DP64(t, ID_AA64ISAR1, DPB, 2); /* FEAT_DPB2 */ + t = FIELD_DP64(t, ID_AA64ISAR1, APA, PauthFeat_FPACCOMBINED); + t = FIELD_DP64(t, ID_AA64ISAR1, API, 1); + t = FIELD_DP64(t, ID_AA64ISAR1, JSCVT, 1); /* FEAT_JSCVT */ + t = FIELD_DP64(t, ID_AA64ISAR1, FCMA, 1); /* FEAT_FCMA */ + t = FIELD_DP64(t, ID_AA64ISAR1, LRCPC, 2); /* FEAT_LRCPC2 */ + t = FIELD_DP64(t, ID_AA64ISAR1, FRINTTS, 1); /* FEAT_FRINTTS */ + t = FIELD_DP64(t, ID_AA64ISAR1, SB, 1); /* FEAT_SB */ + t = FIELD_DP64(t, ID_AA64ISAR1, SPECRES, 1); /* FEAT_SPECRES */ + t = FIELD_DP64(t, ID_AA64ISAR1, BF16, 1); /* FEAT_BF16 */ + t = FIELD_DP64(t, ID_AA64ISAR1, DGH, 1); /* FEAT_DGH */ + t = FIELD_DP64(t, ID_AA64ISAR1, I8MM, 1); /* FEAT_I8MM */ + cpu->isar.id_aa64isar1 = t; + + t = cpu->isar.id_aa64isar2; + t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* FEAT_MOPS */ + t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */ + cpu->isar.id_aa64isar2 = t; + + t = cpu->isar.id_aa64pfr0; + t = FIELD_DP64(t, ID_AA64PFR0, FP, 1); /* FEAT_FP16 */ + t = FIELD_DP64(t, ID_AA64PFR0, ADVSIMD, 1); /* FEAT_FP16 */ + t = FIELD_DP64(t, ID_AA64PFR0, RAS, 2); /* FEAT_RASv1p1 + FEAT_DoubleFault */ + t = FIELD_DP64(t, ID_AA64PFR0, SVE, 1); + t = FIELD_DP64(t, ID_AA64PFR0, SEL2, 1); /* FEAT_SEL2 */ + t = FIELD_DP64(t, ID_AA64PFR0, DIT, 1); /* FEAT_DIT */ + t = FIELD_DP64(t, ID_AA64PFR0, CSV2, 2); /* FEAT_CSV2_2 */ + t = FIELD_DP64(t, ID_AA64PFR0, CSV3, 1); /* FEAT_CSV3 */ + cpu->isar.id_aa64pfr0 = t; + + t = cpu->isar.id_aa64pfr1; + t = FIELD_DP64(t, ID_AA64PFR1, BT, 1); /* FEAT_BTI */ + t = FIELD_DP64(t, ID_AA64PFR1, SSBS, 2); /* FEAT_SSBS2 */ + /* + * Begin with full support for MTE. This will be downgraded to MTE=0 + * during realize if the board provides no tag memory, much like + * we do for EL2 with the virtualization=on property. + */ + t = FIELD_DP64(t, ID_AA64PFR1, MTE, 3); /* FEAT_MTE3 */ + t = FIELD_DP64(t, ID_AA64PFR1, RAS_FRAC, 0); /* FEAT_RASv1p1 + FEAT_DoubleFault */ + t = FIELD_DP64(t, ID_AA64PFR1, SME, 1); /* FEAT_SME */ + t = FIELD_DP64(t, ID_AA64PFR1, CSV2_FRAC, 0); /* FEAT_CSV2_2 */ + cpu->isar.id_aa64pfr1 = t; + + t = cpu->isar.id_aa64mmfr0; + t = FIELD_DP64(t, ID_AA64MMFR0, PARANGE, 6); /* FEAT_LPA: 52 bits */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16, 1); /* 16k pages supported */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16_2, 2); /* 16k stage2 supported */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN64_2, 2); /* 64k stage2 supported */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN4_2, 2); /* 4k stage2 supported */ + t = FIELD_DP64(t, ID_AA64MMFR0, FGT, 1); /* FEAT_FGT */ + t = FIELD_DP64(t, ID_AA64MMFR0, ECV, 2); /* FEAT_ECV */ + cpu->isar.id_aa64mmfr0 = t; + + t = cpu->isar.id_aa64mmfr1; + t = FIELD_DP64(t, ID_AA64MMFR1, HAFDBS, 2); /* FEAT_HAFDBS */ + t = FIELD_DP64(t, ID_AA64MMFR1, VMIDBITS, 2); /* FEAT_VMID16 */ + t = FIELD_DP64(t, ID_AA64MMFR1, VH, 1); /* FEAT_VHE */ + t = FIELD_DP64(t, ID_AA64MMFR1, HPDS, 2); /* FEAT_HPDS2 */ + t = FIELD_DP64(t, ID_AA64MMFR1, LO, 1); /* FEAT_LOR */ + t = FIELD_DP64(t, ID_AA64MMFR1, PAN, 3); /* FEAT_PAN3 */ + t = FIELD_DP64(t, ID_AA64MMFR1, XNX, 1); /* FEAT_XNX */ + t = FIELD_DP64(t, ID_AA64MMFR1, ETS, 1); /* FEAT_ETS */ + t = FIELD_DP64(t, ID_AA64MMFR1, HCX, 1); /* FEAT_HCX */ + t = FIELD_DP64(t, ID_AA64MMFR1, TIDCP1, 1); /* FEAT_TIDCP1 */ + cpu->isar.id_aa64mmfr1 = t; + + t = cpu->isar.id_aa64mmfr2; + t = FIELD_DP64(t, ID_AA64MMFR2, CNP, 1); /* FEAT_TTCNP */ + t = FIELD_DP64(t, ID_AA64MMFR2, UAO, 1); /* FEAT_UAO */ + t = FIELD_DP64(t, ID_AA64MMFR2, IESB, 1); /* FEAT_IESB */ + t = FIELD_DP64(t, ID_AA64MMFR2, VARANGE, 1); /* FEAT_LVA */ + t = FIELD_DP64(t, ID_AA64MMFR2, NV, 2); /* FEAT_NV2 */ + t = FIELD_DP64(t, ID_AA64MMFR2, ST, 1); /* FEAT_TTST */ + t = FIELD_DP64(t, ID_AA64MMFR2, AT, 1); /* FEAT_LSE2 */ + t = FIELD_DP64(t, ID_AA64MMFR2, IDS, 1); /* FEAT_IDST */ + t = FIELD_DP64(t, ID_AA64MMFR2, FWB, 1); /* FEAT_S2FWB */ + t = FIELD_DP64(t, ID_AA64MMFR2, TTL, 1); /* FEAT_TTL */ + t = FIELD_DP64(t, ID_AA64MMFR2, BBM, 2); /* FEAT_BBM at level 2 */ + t = FIELD_DP64(t, ID_AA64MMFR2, EVT, 2); /* FEAT_EVT */ + t = FIELD_DP64(t, ID_AA64MMFR2, E0PD, 1); /* FEAT_E0PD */ + cpu->isar.id_aa64mmfr2 = t; + + t = cpu->isar.id_aa64zfr0; + t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 1); + t = FIELD_DP64(t, ID_AA64ZFR0, AES, 2); /* FEAT_SVE_PMULL128 */ + t = FIELD_DP64(t, ID_AA64ZFR0, BITPERM, 1); /* FEAT_SVE_BitPerm */ + t = FIELD_DP64(t, ID_AA64ZFR0, BFLOAT16, 1); /* FEAT_BF16 */ + t = FIELD_DP64(t, ID_AA64ZFR0, SHA3, 1); /* FEAT_SVE_SHA3 */ + t = FIELD_DP64(t, ID_AA64ZFR0, SM4, 1); /* FEAT_SVE_SM4 */ + t = FIELD_DP64(t, ID_AA64ZFR0, I8MM, 1); /* FEAT_I8MM */ + t = FIELD_DP64(t, ID_AA64ZFR0, F32MM, 1); /* FEAT_F32MM */ + t = FIELD_DP64(t, ID_AA64ZFR0, F64MM, 1); /* FEAT_F64MM */ + cpu->isar.id_aa64zfr0 = t; + + t = cpu->isar.id_aa64dfr0; + t = FIELD_DP64(t, ID_AA64DFR0, DEBUGVER, 9); /* FEAT_Debugv8p4 */ + t = FIELD_DP64(t, ID_AA64DFR0, PMUVER, 6); /* FEAT_PMUv3p5 */ + t = FIELD_DP64(t, ID_AA64DFR0, HPMN0, 1); /* FEAT_HPMN0 */ + cpu->isar.id_aa64dfr0 = t; + + t = cpu->isar.id_aa64smfr0; + t = FIELD_DP64(t, ID_AA64SMFR0, F32F32, 1); /* FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, B16F32, 1); /* FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, F16F32, 1); /* FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, I8I32, 0xf); /* FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, F64F64, 1); /* FEAT_SME_F64F64 */ + t = FIELD_DP64(t, ID_AA64SMFR0, I16I64, 0xf); /* FEAT_SME_I16I64 */ + t = FIELD_DP64(t, ID_AA64SMFR0, FA64, 1); /* FEAT_SME_FA64 */ + cpu->isar.id_aa64smfr0 = t; + + /* Replicate the same data to the 32-bit id registers. */ + aa32_max_features(cpu); + +#ifdef CONFIG_USER_ONLY + /* + * For usermode -cpu max we can use a larger and more efficient DCZ + * blocksize since we don't have to follow what the hardware does. + */ + cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */ + cpu->dcz_blocksize = 7; /* 512 bytes */ +#endif + cpu->gm_blocksize = 6; /* 256 bytes */ + + cpu->sve_vq.supported = MAKE_64BIT_MASK(0, ARM_MAX_VQ); + cpu->sme_vq.supported = SVE_VQ_POW2_MAP; + + aarch64_add_pauth_properties(obj); + aarch64_add_sve_properties(obj); + aarch64_add_sme_properties(obj); + object_property_add(obj, "sve-max-vq", "uint32", cpu_max_get_sve_max_vq, + cpu_max_set_sve_max_vq, NULL, NULL); + object_property_add_bool(obj, "x-rme", cpu_arm_get_rme, cpu_arm_set_rme); + object_property_add(obj, "x-l0gptsz", "uint32", cpu_max_get_l0gptsz, + cpu_max_set_l0gptsz, NULL, NULL); + qdev_property_add_static(DEVICE(obj), &arm_cpu_lpa2_property); +} + +static const ARMCPUInfo aarch64_cpus[] = { + { .name = "cortex-a35", .initfn = aarch64_a35_initfn }, + { .name = "cortex-a55", .initfn = aarch64_a55_initfn }, + { .name = "cortex-a72", .initfn = aarch64_a72_initfn }, + { .name = "cortex-a76", .initfn = aarch64_a76_initfn }, + { .name = "cortex-a710", .initfn = aarch64_a710_initfn }, + { .name = "a64fx", .initfn = aarch64_a64fx_initfn }, + { .name = "neoverse-n1", .initfn = aarch64_neoverse_n1_initfn }, + { .name = "neoverse-v1", .initfn = aarch64_neoverse_v1_initfn }, + { .name = "neoverse-n2", .initfn = aarch64_neoverse_n2_initfn }, +}; + +static void aarch64_cpu_register_types(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(aarch64_cpus); ++i) { + aarch64_cpu_register(&aarch64_cpus[i]); + } +} + +type_init(aarch64_cpu_register_types) diff --git a/target/arm/crypto_helper.c b/target/arm/tcg/crypto_helper.c similarity index 63% rename from target/arm/crypto_helper.c rename to target/arm/tcg/crypto_helper.c index d28690321f..7cadd61e12 100644 --- a/target/arm/crypto_helper.c +++ b/target/arm/tcg/crypto_helper.c @@ -14,7 +14,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "tcg/tcg-gvec-desc.h" -#include "crypto/aes.h" +#include "crypto/aes-round.h" #include "crypto/sm4.h" #include "vec_internal.h" @@ -45,197 +45,104 @@ static void clear_tail_16(void *vd, uint32_t desc) clear_tail(vd, opr_sz, max_sz); } -static void do_crypto_aese(uint64_t *rd, uint64_t *rn, - uint64_t *rm, bool decrypt) -{ - static uint8_t const * const sbox[2] = { AES_sbox, AES_isbox }; - static uint8_t const * const shift[2] = { AES_shifts, AES_ishifts }; - union CRYPTO_STATE rk = { .l = { rm[0], rm[1] } }; - union CRYPTO_STATE st = { .l = { rn[0], rn[1] } }; - int i; - - /* xor state vector with round key */ - rk.l[0] ^= st.l[0]; - rk.l[1] ^= st.l[1]; - - /* combine ShiftRows operation and sbox substitution */ - for (i = 0; i < 16; i++) { - CR_ST_BYTE(st, i) = sbox[decrypt][CR_ST_BYTE(rk, shift[decrypt][i])]; - } - - rd[0] = st.l[0]; - rd[1] = st.l[1]; -} +static const AESState aes_zero = { }; void HELPER(crypto_aese)(void *vd, void *vn, void *vm, uint32_t desc) { intptr_t i, opr_sz = simd_oprsz(desc); - bool decrypt = simd_data(desc); for (i = 0; i < opr_sz; i += 16) { - do_crypto_aese(vd + i, vn + i, vm + i, decrypt); + AESState *ad = (AESState *)(vd + i); + AESState *st = (AESState *)(vn + i); + AESState *rk = (AESState *)(vm + i); + AESState t; + + /* + * Our uint64_t are in the wrong order for big-endian. + * The Arm AddRoundKey comes first, while the API AddRoundKey + * comes last: perform the xor here, and provide zero to API. + */ + if (HOST_BIG_ENDIAN) { + t.d[0] = st->d[1] ^ rk->d[1]; + t.d[1] = st->d[0] ^ rk->d[0]; + aesenc_SB_SR_AK(&t, &t, &aes_zero, false); + ad->d[0] = t.d[1]; + ad->d[1] = t.d[0]; + } else { + t.v = st->v ^ rk->v; + aesenc_SB_SR_AK(ad, &t, &aes_zero, false); + } } clear_tail(vd, opr_sz, simd_maxsz(desc)); } -static void do_crypto_aesmc(uint64_t *rd, uint64_t *rm, bool decrypt) +void HELPER(crypto_aesd)(void *vd, void *vn, void *vm, uint32_t desc) { - static uint32_t const mc[][256] = { { - /* MixColumns lookup table */ - 0x00000000, 0x03010102, 0x06020204, 0x05030306, - 0x0c040408, 0x0f05050a, 0x0a06060c, 0x0907070e, - 0x18080810, 0x1b090912, 0x1e0a0a14, 0x1d0b0b16, - 0x140c0c18, 0x170d0d1a, 0x120e0e1c, 0x110f0f1e, - 0x30101020, 0x33111122, 0x36121224, 0x35131326, - 0x3c141428, 0x3f15152a, 0x3a16162c, 0x3917172e, - 0x28181830, 0x2b191932, 0x2e1a1a34, 0x2d1b1b36, - 0x241c1c38, 0x271d1d3a, 0x221e1e3c, 0x211f1f3e, - 0x60202040, 0x63212142, 0x66222244, 0x65232346, - 0x6c242448, 0x6f25254a, 0x6a26264c, 0x6927274e, - 0x78282850, 0x7b292952, 0x7e2a2a54, 0x7d2b2b56, - 0x742c2c58, 0x772d2d5a, 0x722e2e5c, 0x712f2f5e, - 0x50303060, 0x53313162, 0x56323264, 0x55333366, - 0x5c343468, 0x5f35356a, 0x5a36366c, 0x5937376e, - 0x48383870, 0x4b393972, 0x4e3a3a74, 0x4d3b3b76, - 0x443c3c78, 0x473d3d7a, 0x423e3e7c, 0x413f3f7e, - 0xc0404080, 0xc3414182, 0xc6424284, 0xc5434386, - 0xcc444488, 0xcf45458a, 0xca46468c, 0xc947478e, - 0xd8484890, 0xdb494992, 0xde4a4a94, 0xdd4b4b96, - 0xd44c4c98, 0xd74d4d9a, 0xd24e4e9c, 0xd14f4f9e, - 0xf05050a0, 0xf35151a2, 0xf65252a4, 0xf55353a6, - 0xfc5454a8, 0xff5555aa, 0xfa5656ac, 0xf95757ae, - 0xe85858b0, 0xeb5959b2, 0xee5a5ab4, 0xed5b5bb6, - 0xe45c5cb8, 0xe75d5dba, 0xe25e5ebc, 0xe15f5fbe, - 0xa06060c0, 0xa36161c2, 0xa66262c4, 0xa56363c6, - 0xac6464c8, 0xaf6565ca, 0xaa6666cc, 0xa96767ce, - 0xb86868d0, 0xbb6969d2, 0xbe6a6ad4, 0xbd6b6bd6, - 0xb46c6cd8, 0xb76d6dda, 0xb26e6edc, 0xb16f6fde, - 0x907070e0, 0x937171e2, 0x967272e4, 0x957373e6, - 0x9c7474e8, 0x9f7575ea, 0x9a7676ec, 0x997777ee, - 0x887878f0, 0x8b7979f2, 0x8e7a7af4, 0x8d7b7bf6, - 0x847c7cf8, 0x877d7dfa, 0x827e7efc, 0x817f7ffe, - 0x9b80801b, 0x98818119, 0x9d82821f, 0x9e83831d, - 0x97848413, 0x94858511, 0x91868617, 0x92878715, - 0x8388880b, 0x80898909, 0x858a8a0f, 0x868b8b0d, - 0x8f8c8c03, 0x8c8d8d01, 0x898e8e07, 0x8a8f8f05, - 0xab90903b, 0xa8919139, 0xad92923f, 0xae93933d, - 0xa7949433, 0xa4959531, 0xa1969637, 0xa2979735, - 0xb398982b, 0xb0999929, 0xb59a9a2f, 0xb69b9b2d, - 0xbf9c9c23, 0xbc9d9d21, 0xb99e9e27, 0xba9f9f25, - 0xfba0a05b, 0xf8a1a159, 0xfda2a25f, 0xfea3a35d, - 0xf7a4a453, 0xf4a5a551, 0xf1a6a657, 0xf2a7a755, - 0xe3a8a84b, 0xe0a9a949, 0xe5aaaa4f, 0xe6abab4d, - 0xefacac43, 0xecadad41, 0xe9aeae47, 0xeaafaf45, - 0xcbb0b07b, 0xc8b1b179, 0xcdb2b27f, 0xceb3b37d, - 0xc7b4b473, 0xc4b5b571, 0xc1b6b677, 0xc2b7b775, - 0xd3b8b86b, 0xd0b9b969, 0xd5baba6f, 0xd6bbbb6d, - 0xdfbcbc63, 0xdcbdbd61, 0xd9bebe67, 0xdabfbf65, - 0x5bc0c09b, 0x58c1c199, 0x5dc2c29f, 0x5ec3c39d, - 0x57c4c493, 0x54c5c591, 0x51c6c697, 0x52c7c795, - 0x43c8c88b, 0x40c9c989, 0x45caca8f, 0x46cbcb8d, - 0x4fcccc83, 0x4ccdcd81, 0x49cece87, 0x4acfcf85, - 0x6bd0d0bb, 0x68d1d1b9, 0x6dd2d2bf, 0x6ed3d3bd, - 0x67d4d4b3, 0x64d5d5b1, 0x61d6d6b7, 0x62d7d7b5, - 0x73d8d8ab, 0x70d9d9a9, 0x75dadaaf, 0x76dbdbad, - 0x7fdcdca3, 0x7cdddda1, 0x79dedea7, 0x7adfdfa5, - 0x3be0e0db, 0x38e1e1d9, 0x3de2e2df, 0x3ee3e3dd, - 0x37e4e4d3, 0x34e5e5d1, 0x31e6e6d7, 0x32e7e7d5, - 0x23e8e8cb, 0x20e9e9c9, 0x25eaeacf, 0x26ebebcd, - 0x2fececc3, 0x2cededc1, 0x29eeeec7, 0x2aefefc5, - 0x0bf0f0fb, 0x08f1f1f9, 0x0df2f2ff, 0x0ef3f3fd, - 0x07f4f4f3, 0x04f5f5f1, 0x01f6f6f7, 0x02f7f7f5, - 0x13f8f8eb, 0x10f9f9e9, 0x15fafaef, 0x16fbfbed, - 0x1ffcfce3, 0x1cfdfde1, 0x19fefee7, 0x1affffe5, - }, { - /* Inverse MixColumns lookup table */ - 0x00000000, 0x0b0d090e, 0x161a121c, 0x1d171b12, - 0x2c342438, 0x27392d36, 0x3a2e3624, 0x31233f2a, - 0x58684870, 0x5365417e, 0x4e725a6c, 0x457f5362, - 0x745c6c48, 0x7f516546, 0x62467e54, 0x694b775a, - 0xb0d090e0, 0xbbdd99ee, 0xa6ca82fc, 0xadc78bf2, - 0x9ce4b4d8, 0x97e9bdd6, 0x8afea6c4, 0x81f3afca, - 0xe8b8d890, 0xe3b5d19e, 0xfea2ca8c, 0xf5afc382, - 0xc48cfca8, 0xcf81f5a6, 0xd296eeb4, 0xd99be7ba, - 0x7bbb3bdb, 0x70b632d5, 0x6da129c7, 0x66ac20c9, - 0x578f1fe3, 0x5c8216ed, 0x41950dff, 0x4a9804f1, - 0x23d373ab, 0x28de7aa5, 0x35c961b7, 0x3ec468b9, - 0x0fe75793, 0x04ea5e9d, 0x19fd458f, 0x12f04c81, - 0xcb6bab3b, 0xc066a235, 0xdd71b927, 0xd67cb029, - 0xe75f8f03, 0xec52860d, 0xf1459d1f, 0xfa489411, - 0x9303e34b, 0x980eea45, 0x8519f157, 0x8e14f859, - 0xbf37c773, 0xb43ace7d, 0xa92dd56f, 0xa220dc61, - 0xf66d76ad, 0xfd607fa3, 0xe07764b1, 0xeb7a6dbf, - 0xda595295, 0xd1545b9b, 0xcc434089, 0xc74e4987, - 0xae053edd, 0xa50837d3, 0xb81f2cc1, 0xb31225cf, - 0x82311ae5, 0x893c13eb, 0x942b08f9, 0x9f2601f7, - 0x46bde64d, 0x4db0ef43, 0x50a7f451, 0x5baafd5f, - 0x6a89c275, 0x6184cb7b, 0x7c93d069, 0x779ed967, - 0x1ed5ae3d, 0x15d8a733, 0x08cfbc21, 0x03c2b52f, - 0x32e18a05, 0x39ec830b, 0x24fb9819, 0x2ff69117, - 0x8dd64d76, 0x86db4478, 0x9bcc5f6a, 0x90c15664, - 0xa1e2694e, 0xaaef6040, 0xb7f87b52, 0xbcf5725c, - 0xd5be0506, 0xdeb30c08, 0xc3a4171a, 0xc8a91e14, - 0xf98a213e, 0xf2872830, 0xef903322, 0xe49d3a2c, - 0x3d06dd96, 0x360bd498, 0x2b1ccf8a, 0x2011c684, - 0x1132f9ae, 0x1a3ff0a0, 0x0728ebb2, 0x0c25e2bc, - 0x656e95e6, 0x6e639ce8, 0x737487fa, 0x78798ef4, - 0x495ab1de, 0x4257b8d0, 0x5f40a3c2, 0x544daacc, - 0xf7daec41, 0xfcd7e54f, 0xe1c0fe5d, 0xeacdf753, - 0xdbeec879, 0xd0e3c177, 0xcdf4da65, 0xc6f9d36b, - 0xafb2a431, 0xa4bfad3f, 0xb9a8b62d, 0xb2a5bf23, - 0x83868009, 0x888b8907, 0x959c9215, 0x9e919b1b, - 0x470a7ca1, 0x4c0775af, 0x51106ebd, 0x5a1d67b3, - 0x6b3e5899, 0x60335197, 0x7d244a85, 0x7629438b, - 0x1f6234d1, 0x146f3ddf, 0x097826cd, 0x02752fc3, - 0x335610e9, 0x385b19e7, 0x254c02f5, 0x2e410bfb, - 0x8c61d79a, 0x876cde94, 0x9a7bc586, 0x9176cc88, - 0xa055f3a2, 0xab58faac, 0xb64fe1be, 0xbd42e8b0, - 0xd4099fea, 0xdf0496e4, 0xc2138df6, 0xc91e84f8, - 0xf83dbbd2, 0xf330b2dc, 0xee27a9ce, 0xe52aa0c0, - 0x3cb1477a, 0x37bc4e74, 0x2aab5566, 0x21a65c68, - 0x10856342, 0x1b886a4c, 0x069f715e, 0x0d927850, - 0x64d90f0a, 0x6fd40604, 0x72c31d16, 0x79ce1418, - 0x48ed2b32, 0x43e0223c, 0x5ef7392e, 0x55fa3020, - 0x01b79aec, 0x0aba93e2, 0x17ad88f0, 0x1ca081fe, - 0x2d83bed4, 0x268eb7da, 0x3b99acc8, 0x3094a5c6, - 0x59dfd29c, 0x52d2db92, 0x4fc5c080, 0x44c8c98e, - 0x75ebf6a4, 0x7ee6ffaa, 0x63f1e4b8, 0x68fcedb6, - 0xb1670a0c, 0xba6a0302, 0xa77d1810, 0xac70111e, - 0x9d532e34, 0x965e273a, 0x8b493c28, 0x80443526, - 0xe90f427c, 0xe2024b72, 0xff155060, 0xf418596e, - 0xc53b6644, 0xce366f4a, 0xd3217458, 0xd82c7d56, - 0x7a0ca137, 0x7101a839, 0x6c16b32b, 0x671bba25, - 0x5638850f, 0x5d358c01, 0x40229713, 0x4b2f9e1d, - 0x2264e947, 0x2969e049, 0x347efb5b, 0x3f73f255, - 0x0e50cd7f, 0x055dc471, 0x184adf63, 0x1347d66d, - 0xcadc31d7, 0xc1d138d9, 0xdcc623cb, 0xd7cb2ac5, - 0xe6e815ef, 0xede51ce1, 0xf0f207f3, 0xfbff0efd, - 0x92b479a7, 0x99b970a9, 0x84ae6bbb, 0x8fa362b5, - 0xbe805d9f, 0xb58d5491, 0xa89a4f83, 0xa397468d, - } }; + intptr_t i, opr_sz = simd_oprsz(desc); - union CRYPTO_STATE st = { .l = { rm[0], rm[1] } }; - int i; + for (i = 0; i < opr_sz; i += 16) { + AESState *ad = (AESState *)(vd + i); + AESState *st = (AESState *)(vn + i); + AESState *rk = (AESState *)(vm + i); + AESState t; - for (i = 0; i < 16; i += 4) { - CR_ST_WORD(st, i >> 2) = - mc[decrypt][CR_ST_BYTE(st, i)] ^ - rol32(mc[decrypt][CR_ST_BYTE(st, i + 1)], 8) ^ - rol32(mc[decrypt][CR_ST_BYTE(st, i + 2)], 16) ^ - rol32(mc[decrypt][CR_ST_BYTE(st, i + 3)], 24); + /* Our uint64_t are in the wrong order for big-endian. */ + if (HOST_BIG_ENDIAN) { + t.d[0] = st->d[1] ^ rk->d[1]; + t.d[1] = st->d[0] ^ rk->d[0]; + aesdec_ISB_ISR_AK(&t, &t, &aes_zero, false); + ad->d[0] = t.d[1]; + ad->d[1] = t.d[0]; + } else { + t.v = st->v ^ rk->v; + aesdec_ISB_ISR_AK(ad, &t, &aes_zero, false); + } } - - rd[0] = st.l[0]; - rd[1] = st.l[1]; + clear_tail(vd, opr_sz, simd_maxsz(desc)); } void HELPER(crypto_aesmc)(void *vd, void *vm, uint32_t desc) { intptr_t i, opr_sz = simd_oprsz(desc); - bool decrypt = simd_data(desc); for (i = 0; i < opr_sz; i += 16) { - do_crypto_aesmc(vd + i, vm + i, decrypt); + AESState *ad = (AESState *)(vd + i); + AESState *st = (AESState *)(vm + i); + AESState t; + + /* Our uint64_t are in the wrong order for big-endian. */ + if (HOST_BIG_ENDIAN) { + t.d[0] = st->d[1]; + t.d[1] = st->d[0]; + aesenc_MC(&t, &t, false); + ad->d[0] = t.d[1]; + ad->d[1] = t.d[0]; + } else { + aesenc_MC(ad, st, false); + } + } + clear_tail(vd, opr_sz, simd_maxsz(desc)); +} + +void HELPER(crypto_aesimc)(void *vd, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + + for (i = 0; i < opr_sz; i += 16) { + AESState *ad = (AESState *)(vd + i); + AESState *st = (AESState *)(vm + i); + AESState t; + + /* Our uint64_t are in the wrong order for big-endian. */ + if (HOST_BIG_ENDIAN) { + t.d[0] = st->d[1]; + t.d[1] = st->d[0]; + aesdec_IMC(&t, &t, false); + ad->d[0] = t.d[1]; + ad->d[1] = t.d[0]; + } else { + aesdec_IMC(ad, st, false); + } } clear_tail(vd, opr_sz, simd_maxsz(desc)); } @@ -707,10 +614,7 @@ static void do_crypto_sm4e(uint64_t *rd, uint64_t *rn, uint64_t *rm) CR_ST_WORD(d, (i + 3) % 4) ^ CR_ST_WORD(n, i); - t = sm4_sbox[t & 0xff] | - sm4_sbox[(t >> 8) & 0xff] << 8 | - sm4_sbox[(t >> 16) & 0xff] << 16 | - sm4_sbox[(t >> 24) & 0xff] << 24; + t = sm4_subword(t); CR_ST_WORD(d, i) ^= t ^ rol32(t, 2) ^ rol32(t, 10) ^ rol32(t, 18) ^ rol32(t, 24); @@ -744,10 +648,7 @@ static void do_crypto_sm4ekey(uint64_t *rd, uint64_t *rn, uint64_t *rm) CR_ST_WORD(d, (i + 3) % 4) ^ CR_ST_WORD(m, i); - t = sm4_sbox[t & 0xff] | - sm4_sbox[(t >> 8) & 0xff] << 8 | - sm4_sbox[(t >> 16) & 0xff] << 16 | - sm4_sbox[(t >> 24) & 0xff] << 24; + t = sm4_subword(t); CR_ST_WORD(d, i) ^= t ^ rol32(t, 13) ^ rol32(t, 23); } diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c new file mode 100644 index 0000000000..ebaa7f00df --- /dev/null +++ b/target/arm/tcg/helper-a64.c @@ -0,0 +1,1857 @@ +/* + * AArch64 specific helpers + * + * Copyright (c) 2013 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "cpu.h" +#include "gdbstub/helpers.h" +#include "exec/helper-proto.h" +#include "qemu/host-utils.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "qemu/bitops.h" +#include "internals.h" +#include "qemu/crc32c.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "qemu/int128.h" +#include "qemu/atomic128.h" +#include "fpu/softfloat.h" +#include /* For crc32 */ + +/* C2.4.7 Multiply and divide */ +/* special cases for 0 and LLONG_MIN are mandated by the standard */ +uint64_t HELPER(udiv64)(uint64_t num, uint64_t den) +{ + if (den == 0) { + return 0; + } + return num / den; +} + +int64_t HELPER(sdiv64)(int64_t num, int64_t den) +{ + if (den == 0) { + return 0; + } + if (num == LLONG_MIN && den == -1) { + return LLONG_MIN; + } + return num / den; +} + +uint64_t HELPER(rbit64)(uint64_t x) +{ + return revbit64(x); +} + +void HELPER(msr_i_spsel)(CPUARMState *env, uint32_t imm) +{ + update_spsel(env, imm); +} + +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 && !(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, + imm, 0x1f, 0), + exception_target_el(env), ra); + } +} + +void HELPER(msr_i_daifset)(CPUARMState *env, uint32_t imm) +{ + daif_check(env, 0x1e, imm, GETPC()); + env->daif |= (imm << 6) & PSTATE_DAIF; + arm_rebuild_hflags(env); +} + +void HELPER(msr_i_daifclear)(CPUARMState *env, uint32_t imm) +{ + daif_check(env, 0x1f, imm, GETPC()); + env->daif &= ~((imm << 6) & PSTATE_DAIF); + arm_rebuild_hflags(env); +} + +/* Convert a softfloat float_relation_ (as returned by + * the float*_compare functions) to the correct ARM + * NZCV flag state. + */ +static inline uint32_t float_rel_to_flags(int res) +{ + uint64_t flags; + switch (res) { + case float_relation_equal: + flags = PSTATE_Z | PSTATE_C; + break; + case float_relation_less: + flags = PSTATE_N; + break; + case float_relation_greater: + flags = PSTATE_C; + break; + case float_relation_unordered: + default: + flags = PSTATE_C | PSTATE_V; + break; + } + return flags; +} + +uint64_t HELPER(vfp_cmph_a64)(uint32_t x, uint32_t y, void *fp_status) +{ + return float_rel_to_flags(float16_compare_quiet(x, y, fp_status)); +} + +uint64_t HELPER(vfp_cmpeh_a64)(uint32_t x, uint32_t y, void *fp_status) +{ + return float_rel_to_flags(float16_compare(x, y, fp_status)); +} + +uint64_t HELPER(vfp_cmps_a64)(float32 x, float32 y, void *fp_status) +{ + return float_rel_to_flags(float32_compare_quiet(x, y, fp_status)); +} + +uint64_t HELPER(vfp_cmpes_a64)(float32 x, float32 y, void *fp_status) +{ + return float_rel_to_flags(float32_compare(x, y, fp_status)); +} + +uint64_t HELPER(vfp_cmpd_a64)(float64 x, float64 y, void *fp_status) +{ + return float_rel_to_flags(float64_compare_quiet(x, y, fp_status)); +} + +uint64_t HELPER(vfp_cmped_a64)(float64 x, float64 y, void *fp_status) +{ + return float_rel_to_flags(float64_compare(x, y, fp_status)); +} + +float32 HELPER(vfp_mulxs)(float32 a, float32 b, void *fpstp) +{ + float_status *fpst = fpstp; + + a = float32_squash_input_denormal(a, fpst); + b = float32_squash_input_denormal(b, fpst); + + if ((float32_is_zero(a) && float32_is_infinity(b)) || + (float32_is_infinity(a) && float32_is_zero(b))) { + /* 2.0 with the sign bit set to sign(A) XOR sign(B) */ + return make_float32((1U << 30) | + ((float32_val(a) ^ float32_val(b)) & (1U << 31))); + } + return float32_mul(a, b, fpst); +} + +float64 HELPER(vfp_mulxd)(float64 a, float64 b, void *fpstp) +{ + float_status *fpst = fpstp; + + a = float64_squash_input_denormal(a, fpst); + b = float64_squash_input_denormal(b, fpst); + + if ((float64_is_zero(a) && float64_is_infinity(b)) || + (float64_is_infinity(a) && float64_is_zero(b))) { + /* 2.0 with the sign bit set to sign(A) XOR sign(B) */ + return make_float64((1ULL << 62) | + ((float64_val(a) ^ float64_val(b)) & (1ULL << 63))); + } + return float64_mul(a, b, fpst); +} + +/* 64bit/double versions of the neon float compare functions */ +uint64_t HELPER(neon_ceq_f64)(float64 a, float64 b, void *fpstp) +{ + float_status *fpst = fpstp; + return -float64_eq_quiet(a, b, fpst); +} + +uint64_t HELPER(neon_cge_f64)(float64 a, float64 b, void *fpstp) +{ + float_status *fpst = fpstp; + return -float64_le(b, a, fpst); +} + +uint64_t HELPER(neon_cgt_f64)(float64 a, float64 b, void *fpstp) +{ + float_status *fpst = fpstp; + return -float64_lt(b, a, fpst); +} + +/* Reciprocal step and sqrt step. Note that unlike the A32/T32 + * versions, these do a fully fused multiply-add or + * multiply-add-and-halve. + */ + +uint32_t HELPER(recpsf_f16)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + + a = float16_squash_input_denormal(a, fpst); + b = float16_squash_input_denormal(b, fpst); + + a = float16_chs(a); + if ((float16_is_infinity(a) && float16_is_zero(b)) || + (float16_is_infinity(b) && float16_is_zero(a))) { + return float16_two; + } + return float16_muladd(a, b, float16_two, 0, fpst); +} + +float32 HELPER(recpsf_f32)(float32 a, float32 b, void *fpstp) +{ + float_status *fpst = fpstp; + + a = float32_squash_input_denormal(a, fpst); + b = float32_squash_input_denormal(b, fpst); + + a = float32_chs(a); + if ((float32_is_infinity(a) && float32_is_zero(b)) || + (float32_is_infinity(b) && float32_is_zero(a))) { + return float32_two; + } + return float32_muladd(a, b, float32_two, 0, fpst); +} + +float64 HELPER(recpsf_f64)(float64 a, float64 b, void *fpstp) +{ + float_status *fpst = fpstp; + + a = float64_squash_input_denormal(a, fpst); + b = float64_squash_input_denormal(b, fpst); + + a = float64_chs(a); + if ((float64_is_infinity(a) && float64_is_zero(b)) || + (float64_is_infinity(b) && float64_is_zero(a))) { + return float64_two; + } + return float64_muladd(a, b, float64_two, 0, fpst); +} + +uint32_t HELPER(rsqrtsf_f16)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + + a = float16_squash_input_denormal(a, fpst); + b = float16_squash_input_denormal(b, fpst); + + a = float16_chs(a); + if ((float16_is_infinity(a) && float16_is_zero(b)) || + (float16_is_infinity(b) && float16_is_zero(a))) { + return float16_one_point_five; + } + return float16_muladd(a, b, float16_three, float_muladd_halve_result, fpst); +} + +float32 HELPER(rsqrtsf_f32)(float32 a, float32 b, void *fpstp) +{ + float_status *fpst = fpstp; + + a = float32_squash_input_denormal(a, fpst); + b = float32_squash_input_denormal(b, fpst); + + a = float32_chs(a); + if ((float32_is_infinity(a) && float32_is_zero(b)) || + (float32_is_infinity(b) && float32_is_zero(a))) { + return float32_one_point_five; + } + return float32_muladd(a, b, float32_three, float_muladd_halve_result, fpst); +} + +float64 HELPER(rsqrtsf_f64)(float64 a, float64 b, void *fpstp) +{ + float_status *fpst = fpstp; + + a = float64_squash_input_denormal(a, fpst); + b = float64_squash_input_denormal(b, fpst); + + a = float64_chs(a); + if ((float64_is_infinity(a) && float64_is_zero(b)) || + (float64_is_infinity(b) && float64_is_zero(a))) { + return float64_one_point_five; + } + return float64_muladd(a, b, float64_three, float_muladd_halve_result, fpst); +} + +/* Pairwise long add: add pairs of adjacent elements into + * double-width elements in the result (eg _s8 is an 8x8->16 op) + */ +uint64_t HELPER(neon_addlp_s8)(uint64_t a) +{ + uint64_t nsignmask = 0x0080008000800080ULL; + uint64_t wsignmask = 0x8000800080008000ULL; + uint64_t elementmask = 0x00ff00ff00ff00ffULL; + uint64_t tmp1, tmp2; + uint64_t res, signres; + + /* Extract odd elements, sign extend each to a 16 bit field */ + tmp1 = a & elementmask; + tmp1 ^= nsignmask; + tmp1 |= wsignmask; + tmp1 = (tmp1 - nsignmask) ^ wsignmask; + /* Ditto for the even elements */ + tmp2 = (a >> 8) & elementmask; + tmp2 ^= nsignmask; + tmp2 |= wsignmask; + tmp2 = (tmp2 - nsignmask) ^ wsignmask; + + /* calculate the result by summing bits 0..14, 16..22, etc, + * and then adjusting the sign bits 15, 23, etc manually. + * This ensures the addition can't overflow the 16 bit field. + */ + signres = (tmp1 ^ tmp2) & wsignmask; + res = (tmp1 & ~wsignmask) + (tmp2 & ~wsignmask); + res ^= signres; + + return res; +} + +uint64_t HELPER(neon_addlp_u8)(uint64_t a) +{ + uint64_t tmp; + + tmp = a & 0x00ff00ff00ff00ffULL; + tmp += (a >> 8) & 0x00ff00ff00ff00ffULL; + return tmp; +} + +uint64_t HELPER(neon_addlp_s16)(uint64_t a) +{ + int32_t reslo, reshi; + + reslo = (int32_t)(int16_t)a + (int32_t)(int16_t)(a >> 16); + reshi = (int32_t)(int16_t)(a >> 32) + (int32_t)(int16_t)(a >> 48); + + return (uint32_t)reslo | (((uint64_t)reshi) << 32); +} + +uint64_t HELPER(neon_addlp_u16)(uint64_t a) +{ + uint64_t tmp; + + tmp = a & 0x0000ffff0000ffffULL; + tmp += (a >> 16) & 0x0000ffff0000ffffULL; + return tmp; +} + +/* Floating-point reciprocal exponent - see FPRecpX in ARM ARM */ +uint32_t HELPER(frecpx_f16)(uint32_t a, void *fpstp) +{ + float_status *fpst = fpstp; + uint16_t val16, sbit; + int16_t exp; + + if (float16_is_any_nan(a)) { + float16 nan = a; + if (float16_is_signaling_nan(a, fpst)) { + float_raise(float_flag_invalid, fpst); + if (!fpst->default_nan_mode) { + nan = float16_silence_nan(a, fpst); + } + } + if (fpst->default_nan_mode) { + nan = float16_default_nan(fpst); + } + return nan; + } + + a = float16_squash_input_denormal(a, fpst); + + val16 = float16_val(a); + sbit = 0x8000 & val16; + exp = extract32(val16, 10, 5); + + if (exp == 0) { + return make_float16(deposit32(sbit, 10, 5, 0x1e)); + } else { + return make_float16(deposit32(sbit, 10, 5, ~exp)); + } +} + +float32 HELPER(frecpx_f32)(float32 a, void *fpstp) +{ + float_status *fpst = fpstp; + uint32_t val32, sbit; + int32_t exp; + + if (float32_is_any_nan(a)) { + float32 nan = a; + if (float32_is_signaling_nan(a, fpst)) { + float_raise(float_flag_invalid, fpst); + if (!fpst->default_nan_mode) { + nan = float32_silence_nan(a, fpst); + } + } + if (fpst->default_nan_mode) { + nan = float32_default_nan(fpst); + } + return nan; + } + + a = float32_squash_input_denormal(a, fpst); + + val32 = float32_val(a); + sbit = 0x80000000ULL & val32; + exp = extract32(val32, 23, 8); + + if (exp == 0) { + return make_float32(sbit | (0xfe << 23)); + } else { + return make_float32(sbit | (~exp & 0xff) << 23); + } +} + +float64 HELPER(frecpx_f64)(float64 a, void *fpstp) +{ + float_status *fpst = fpstp; + uint64_t val64, sbit; + int64_t exp; + + if (float64_is_any_nan(a)) { + float64 nan = a; + if (float64_is_signaling_nan(a, fpst)) { + float_raise(float_flag_invalid, fpst); + if (!fpst->default_nan_mode) { + nan = float64_silence_nan(a, fpst); + } + } + if (fpst->default_nan_mode) { + nan = float64_default_nan(fpst); + } + return nan; + } + + a = float64_squash_input_denormal(a, fpst); + + val64 = float64_val(a); + sbit = 0x8000000000000000ULL & val64; + exp = extract64(float64_val(a), 52, 11); + + if (exp == 0) { + return make_float64(sbit | (0x7feULL << 52)); + } else { + return make_float64(sbit | (~exp & 0x7ffULL) << 52); + } +} + +float32 HELPER(fcvtx_f64_to_f32)(float64 a, CPUARMState *env) +{ + /* Von Neumann rounding is implemented by using round-to-zero + * and then setting the LSB of the result if Inexact was raised. + */ + float32 r; + float_status *fpst = &env->vfp.fp_status; + float_status tstat = *fpst; + int exflags; + + set_float_rounding_mode(float_round_to_zero, &tstat); + set_float_exception_flags(0, &tstat); + r = float64_to_float32(a, &tstat); + exflags = get_float_exception_flags(&tstat); + if (exflags & float_flag_inexact) { + r = make_float32(float32_val(r) | 1); + } + exflags |= get_float_exception_flags(fpst); + set_float_exception_flags(exflags, fpst); + return r; +} + +/* 64-bit versions of the CRC helpers. Note that although the operation + * (and the prototypes of crc32c() and crc32() mean that only the bottom + * 32 bits of the accumulator and result are used, we pass and return + * uint64_t for convenience of the generated code. Unlike the 32-bit + * instruction set versions, val may genuinely have 64 bits of data in it. + * The upper bytes of val (above the number specified by 'bytes') must have + * been zeroed out by the caller. + */ +uint64_t HELPER(crc32_64)(uint64_t acc, uint64_t val, uint32_t bytes) +{ + uint8_t buf[8]; + + stq_le_p(buf, val); + + /* zlib crc32 converts the accumulator and output to one's complement. */ + return crc32(acc ^ 0xffffffff, buf, bytes) ^ 0xffffffff; +} + +uint64_t HELPER(crc32c_64)(uint64_t acc, uint64_t val, uint32_t bytes) +{ + uint8_t buf[8]; + + stq_le_p(buf, val); + + /* Linux crc32c converts the output to one's complement. */ + return crc32c(acc, buf, bytes) ^ 0xffffffff; +} + +/* + * AdvSIMD half-precision + */ + +#define ADVSIMD_HELPER(name, suffix) HELPER(glue(glue(advsimd_, name), suffix)) + +#define ADVSIMD_HALFOP(name) \ +uint32_t ADVSIMD_HELPER(name, h)(uint32_t a, uint32_t b, void *fpstp) \ +{ \ + float_status *fpst = fpstp; \ + return float16_ ## name(a, b, fpst); \ +} + +ADVSIMD_HALFOP(add) +ADVSIMD_HALFOP(sub) +ADVSIMD_HALFOP(mul) +ADVSIMD_HALFOP(div) +ADVSIMD_HALFOP(min) +ADVSIMD_HALFOP(max) +ADVSIMD_HALFOP(minnum) +ADVSIMD_HALFOP(maxnum) + +#define ADVSIMD_TWOHALFOP(name) \ +uint32_t ADVSIMD_HELPER(name, 2h)(uint32_t two_a, uint32_t two_b, void *fpstp) \ +{ \ + float16 a1, a2, b1, b2; \ + uint32_t r1, r2; \ + float_status *fpst = fpstp; \ + a1 = extract32(two_a, 0, 16); \ + a2 = extract32(two_a, 16, 16); \ + b1 = extract32(two_b, 0, 16); \ + b2 = extract32(two_b, 16, 16); \ + r1 = float16_ ## name(a1, b1, fpst); \ + r2 = float16_ ## name(a2, b2, fpst); \ + return deposit32(r1, 16, 16, r2); \ +} + +ADVSIMD_TWOHALFOP(add) +ADVSIMD_TWOHALFOP(sub) +ADVSIMD_TWOHALFOP(mul) +ADVSIMD_TWOHALFOP(div) +ADVSIMD_TWOHALFOP(min) +ADVSIMD_TWOHALFOP(max) +ADVSIMD_TWOHALFOP(minnum) +ADVSIMD_TWOHALFOP(maxnum) + +/* Data processing - scalar floating-point and advanced SIMD */ +static float16 float16_mulx(float16 a, float16 b, void *fpstp) +{ + float_status *fpst = fpstp; + + a = float16_squash_input_denormal(a, fpst); + b = float16_squash_input_denormal(b, fpst); + + if ((float16_is_zero(a) && float16_is_infinity(b)) || + (float16_is_infinity(a) && float16_is_zero(b))) { + /* 2.0 with the sign bit set to sign(A) XOR sign(B) */ + return make_float16((1U << 14) | + ((float16_val(a) ^ float16_val(b)) & (1U << 15))); + } + return float16_mul(a, b, fpst); +} + +ADVSIMD_HALFOP(mulx) +ADVSIMD_TWOHALFOP(mulx) + +/* fused multiply-accumulate */ +uint32_t HELPER(advsimd_muladdh)(uint32_t a, uint32_t b, uint32_t c, + void *fpstp) +{ + float_status *fpst = fpstp; + return float16_muladd(a, b, c, 0, fpst); +} + +uint32_t HELPER(advsimd_muladd2h)(uint32_t two_a, uint32_t two_b, + uint32_t two_c, void *fpstp) +{ + float_status *fpst = fpstp; + float16 a1, a2, b1, b2, c1, c2; + uint32_t r1, r2; + a1 = extract32(two_a, 0, 16); + a2 = extract32(two_a, 16, 16); + b1 = extract32(two_b, 0, 16); + b2 = extract32(two_b, 16, 16); + c1 = extract32(two_c, 0, 16); + c2 = extract32(two_c, 16, 16); + r1 = float16_muladd(a1, b1, c1, 0, fpst); + r2 = float16_muladd(a2, b2, c2, 0, fpst); + return deposit32(r1, 16, 16, r2); +} + +/* + * Floating point comparisons produce an integer result. Softfloat + * routines return float_relation types which we convert to the 0/-1 + * Neon requires. + */ + +#define ADVSIMD_CMPRES(test) (test) ? 0xffff : 0 + +uint32_t HELPER(advsimd_ceq_f16)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + int compare = float16_compare_quiet(a, b, fpst); + return ADVSIMD_CMPRES(compare == float_relation_equal); +} + +uint32_t HELPER(advsimd_cge_f16)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + int compare = float16_compare(a, b, fpst); + return ADVSIMD_CMPRES(compare == float_relation_greater || + compare == float_relation_equal); +} + +uint32_t HELPER(advsimd_cgt_f16)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + int compare = float16_compare(a, b, fpst); + return ADVSIMD_CMPRES(compare == float_relation_greater); +} + +uint32_t HELPER(advsimd_acge_f16)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + float16 f0 = float16_abs(a); + float16 f1 = float16_abs(b); + int compare = float16_compare(f0, f1, fpst); + return ADVSIMD_CMPRES(compare == float_relation_greater || + compare == float_relation_equal); +} + +uint32_t HELPER(advsimd_acgt_f16)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + float16 f0 = float16_abs(a); + float16 f1 = float16_abs(b); + int compare = float16_compare(f0, f1, fpst); + return ADVSIMD_CMPRES(compare == float_relation_greater); +} + +/* round to integral */ +uint32_t HELPER(advsimd_rinth_exact)(uint32_t x, void *fp_status) +{ + return float16_round_to_int(x, fp_status); +} + +uint32_t HELPER(advsimd_rinth)(uint32_t x, void *fp_status) +{ + int old_flags = get_float_exception_flags(fp_status), new_flags; + float16 ret; + + ret = float16_round_to_int(x, fp_status); + + /* Suppress any inexact exceptions the conversion produced */ + if (!(old_flags & float_flag_inexact)) { + new_flags = get_float_exception_flags(fp_status); + set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status); + } + + return ret; +} + +/* + * Half-precision floating point conversion functions + * + * There are a multitude of conversion functions with various + * different rounding modes. This is dealt with by the calling code + * setting the mode appropriately before calling the helper. + */ + +uint32_t HELPER(advsimd_f16tosinth)(uint32_t a, void *fpstp) +{ + float_status *fpst = fpstp; + + /* Invalid if we are passed a NaN */ + if (float16_is_any_nan(a)) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_int16(a, fpst); +} + +uint32_t HELPER(advsimd_f16touinth)(uint32_t a, void *fpstp) +{ + float_status *fpst = fpstp; + + /* Invalid if we are passed a NaN */ + if (float16_is_any_nan(a)) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_uint16(a, fpst); +} + +static int el_from_spsr(uint32_t spsr) +{ + /* Return the exception level that this SPSR is requesting a return to, + * or -1 if it is invalid (an illegal return) + */ + if (spsr & PSTATE_nRW) { + switch (spsr & CPSR_M) { + case ARM_CPU_MODE_USR: + return 0; + case ARM_CPU_MODE_HYP: + return 2; + case ARM_CPU_MODE_FIQ: + case ARM_CPU_MODE_IRQ: + case ARM_CPU_MODE_SVC: + case ARM_CPU_MODE_ABT: + case ARM_CPU_MODE_UND: + case ARM_CPU_MODE_SYS: + return 1; + case ARM_CPU_MODE_MON: + /* Returning to Mon from AArch64 is never possible, + * so this is an illegal return. + */ + default: + return -1; + } + } else { + if (extract32(spsr, 1, 1)) { + /* Return with reserved M[1] bit set */ + return -1; + } + if (extract32(spsr, 0, 4) == 1) { + /* return to EL0 with M[0] bit set */ + return -1; + } + return extract32(spsr, 2, 2); + } +} + +static void cpsr_write_from_spsr_elx(CPUARMState *env, + uint32_t val) +{ + uint32_t mask; + + /* Save SPSR_ELx.SS into PSTATE. */ + env->pstate = (env->pstate & ~PSTATE_SS) | (val & PSTATE_SS); + val &= ~PSTATE_SS; + + /* Move DIT to the correct location for CPSR */ + if (val & PSTATE_DIT) { + val &= ~PSTATE_DIT; + val |= CPSR_DIT; + } + + mask = aarch32_cpsr_valid_mask(env->features, \ + &env_archcpu(env)->isar); + cpsr_write(env, val, mask, CPSRWriteRaw); +} + +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]; + int new_el; + bool return_to_aa64 = (spsr & PSTATE_nRW) == 0; + + aarch64_save_sp(env, cur_el); + + arm_clear_exclusive(env); + + /* We must squash the PSTATE.SS bit to zero unless both of the + * following hold: + * 1. debug exceptions are currently disabled + * 2. singlestep will be active in the EL we return to + * We check 1 here and 2 after we've done the pstate/cpsr write() to + * transition to the EL we're going to. + */ + if (arm_generate_debug_exceptions(env)) { + spsr &= ~PSTATE_SS; + } + + /* + * FEAT_RME forbids return from EL3 with an invalid security state. + * We don't need an explicit check for FEAT_RME here because we enforce + * in scr_write() that you can't set the NSE bit without it. + */ + if (cur_el == 3 && (env->cp15.scr_el3 & (SCR_NS | SCR_NSE)) == SCR_NSE) { + goto illegal_return; + } + + new_el = el_from_spsr(spsr); + if (new_el == -1) { + goto illegal_return; + } + if (new_el > cur_el || (new_el == 2 && !arm_is_el2_enabled(env))) { + /* Disallow return to an EL which is unimplemented or higher + * than the current one. + */ + goto illegal_return; + } + + if (new_el != 0 && arm_el_is_aa64(env, new_el) != return_to_aa64) { + /* Return to an EL which is configured for a different register width */ + goto illegal_return; + } + + if (new_el == 1 && (arm_hcr_el2_eff(env) & HCR_TGE)) { + goto illegal_return; + } + + bql_lock(); + arm_call_pre_el_change_hook(env_archcpu(env)); + bql_unlock(); + + if (!return_to_aa64) { + env->aarch64 = false; + /* We do a raw CPSR write because aarch64_sync_64_to_32() + * will sort the register banks out for us, and we've already + * caught all the bad-mode cases in el_from_spsr(). + */ + cpsr_write_from_spsr_elx(env, spsr); + if (!arm_singlestep_active(env)) { + env->pstate &= ~PSTATE_SS; + } + aarch64_sync_64_to_32(env); + + if (spsr & CPSR_T) { + env->regs[15] = new_pc & ~0x1; + } else { + env->regs[15] = new_pc & ~0x3; + } + helper_rebuild_hflags_a32(env, new_el); + qemu_log_mask(CPU_LOG_INT, "Exception return from AArch64 EL%d to " + "AArch32 EL%d PC 0x%" PRIx32 "\n", + cur_el, new_el, env->regs[15]); + } else { + int tbii; + + env->aarch64 = true; + 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); + 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 = EX_TBFLAG_A64(env->hflags, TBII); + if ((tbii >> extract64(new_pc, 55, 1)) & 1) { + /* TBI is enabled. */ + int core_mmu_idx = arm_env_mmu_index(env); + 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); + } + + /* + * Note that cur_el can never be 0. If new_el is 0, then + * el0_a64 is return_to_aa64, else el0_a64 is ignored. + */ + aarch64_sve_change_el(env, cur_el, new_el, return_to_aa64); + + bql_lock(); + arm_call_el_change_hook(env_archcpu(env)); + bql_unlock(); + + return; + +illegal_return: + /* Illegal return events of various kinds have architecturally + * mandated behaviour: + * restore NZCV and DAIF from SPSR_ELx + * set PSTATE.IL + * restore PC from ELR_ELx + * no change to exception level, execution state or stack pointer + */ + env->pstate |= PSTATE_IL; + env->pc = new_pc; + spsr &= PSTATE_NZCV | PSTATE_DAIF; + spsr |= pstate_read(env) & ~(PSTATE_NZCV | PSTATE_DAIF); + pstate_write(env, spsr); + if (!arm_singlestep_active(env)) { + env->pstate &= ~PSTATE_SS; + } + helper_rebuild_hflags_a64(env, cur_el); + qemu_log_mask(LOG_GUEST_ERROR, "Illegal exception return at EL%d: " + "resuming execution at 0x%" PRIx64 "\n", cur_el, env->pc); +} + +/* + * Square Root and Reciprocal square root + */ + +uint32_t HELPER(sqrt_f16)(uint32_t a, void *fpstp) +{ + float_status *s = 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). + */ + int blocklen = 4 << env_archcpu(env)->dcz_blocksize; + uint64_t vaddr = vaddr_in & ~(blocklen - 1); + int mmu_idx = arm_env_mmu_index(env); + void *mem; + + /* + * Trapless lookup. In addition to actual invalid page, may + * return NULL for I/O, watchpoints, clean pages, etc. + */ + mem = tlb_vaddr_to_host(env, vaddr, MMU_DATA_STORE, mmu_idx); + +#ifndef CONFIG_USER_ONLY + if (unlikely(!mem)) { + uintptr_t ra = GETPC(); + + /* + * Trap if accessing an invalid page. DC_ZVA requires that we supply + * the original pointer for an invalid page. But watchpoints require + * that we probe the actual space. So do both. + */ + (void) probe_write(env, vaddr_in, 1, mmu_idx, ra); + mem = probe_write(env, vaddr, blocklen, mmu_idx, ra); + + if (unlikely(!mem)) { + /* + * The only remaining reason for mem == NULL is I/O. + * Just do a series of byte writes as the architecture demands. + */ + for (int i = 0; i < blocklen; i++) { + cpu_stb_mmuidx_ra(env, vaddr + i, 0, mmu_idx, ra); + } + return; + } + } +#endif + + memset(mem, 0, blocklen); +} + +void HELPER(unaligned_access)(CPUARMState *env, uint64_t addr, + uint32_t access_type, uint32_t mmu_idx) +{ + arm_cpu_do_unaligned_access(env_cpu(env), addr, access_type, + mmu_idx, GETPC()); +} + +/* Memory operations (memset, memmove, memcpy) */ + +/* + * Return true if the CPY* and SET* insns can execute; compare + * pseudocode CheckMOPSEnabled(), though we refactor it a little. + */ +static bool mops_enabled(CPUARMState *env) +{ + int el = arm_current_el(env); + + if (el < 2 && + (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE) && + !(arm_hcrx_el2_eff(env) & HCRX_MSCEN)) { + return false; + } + + if (el == 0) { + if (!el_is_in_host(env, 0)) { + return env->cp15.sctlr_el[1] & SCTLR_MSCEN; + } else { + return env->cp15.sctlr_el[2] & SCTLR_MSCEN; + } + } + return true; +} + +static void check_mops_enabled(CPUARMState *env, uintptr_t ra) +{ + if (!mops_enabled(env)) { + raise_exception_ra(env, EXCP_UDEF, syn_uncategorized(), + exception_target_el(env), ra); + } +} + +/* + * Return the target exception level for an exception due + * to mismatched arguments in a FEAT_MOPS copy or set. + * Compare pseudocode MismatchedCpySetTargetEL() + */ +static int mops_mismatch_exception_target_el(CPUARMState *env) +{ + int el = arm_current_el(env); + + if (el > 1) { + return el; + } + if (el == 0 && (arm_hcr_el2_eff(env) & HCR_TGE)) { + return 2; + } + if (el == 1 && (arm_hcrx_el2_eff(env) & HCRX_MCE2)) { + return 2; + } + return 1; +} + +/* + * Check whether an M or E instruction was executed with a CF value + * indicating the wrong option for this implementation. + * Assumes we are always Option A. + */ +static void check_mops_wrong_option(CPUARMState *env, uint32_t syndrome, + uintptr_t ra) +{ + if (env->CF != 0) { + syndrome |= 1 << 17; /* Set the wrong-option bit */ + raise_exception_ra(env, EXCP_UDEF, syndrome, + mops_mismatch_exception_target_el(env), ra); + } +} + +/* + * Return the maximum number of bytes we can transfer starting at addr + * without crossing a page boundary. + */ +static uint64_t page_limit(uint64_t addr) +{ + return TARGET_PAGE_ALIGN(addr + 1) - addr; +} + +/* + * Return the number of bytes we can copy starting from addr and working + * backwards without crossing a page boundary. + */ +static uint64_t page_limit_rev(uint64_t addr) +{ + return (addr & ~TARGET_PAGE_MASK) + 1; +} + +/* + * Perform part of a memory set on an area of guest memory starting at + * toaddr (a dirty address) and extending for setsize bytes. + * + * Returns the number of bytes actually set, which might be less than + * setsize; the caller should loop until the whole set has been done. + * The caller should ensure that the guest registers are correct + * for the possibility that the first byte of the set encounters + * an exception or watchpoint. We guarantee not to take any faults + * for bytes other than the first. + */ +static uint64_t set_step(CPUARMState *env, uint64_t toaddr, + uint64_t setsize, uint32_t data, int memidx, + uint32_t *mtedesc, uintptr_t ra) +{ + void *mem; + + setsize = MIN(setsize, page_limit(toaddr)); + if (*mtedesc) { + uint64_t mtesize = mte_mops_probe(env, toaddr, setsize, *mtedesc); + if (mtesize == 0) { + /* Trap, or not. All CPU state is up to date */ + mte_check_fail(env, *mtedesc, toaddr, ra); + /* Continue, with no further MTE checks required */ + *mtedesc = 0; + } else { + /* Advance to the end, or to the tag mismatch */ + setsize = MIN(setsize, mtesize); + } + } + + toaddr = useronly_clean_ptr(toaddr); + /* + * Trapless lookup: returns NULL for invalid page, I/O, + * watchpoints, clean pages, etc. + */ + mem = tlb_vaddr_to_host(env, toaddr, MMU_DATA_STORE, memidx); + +#ifndef CONFIG_USER_ONLY + if (unlikely(!mem)) { + /* + * Slow-path: just do one byte write. This will handle the + * watchpoint, invalid page, etc handling correctly. + * For clean code pages, the next iteration will see + * the page dirty and will use the fast path. + */ + cpu_stb_mmuidx_ra(env, toaddr, data, memidx, ra); + return 1; + } +#endif + /* Easy case: just memset the host memory */ + memset(mem, data, setsize); + return setsize; +} + +/* + * Similar, but setting tags. The architecture requires us to do this + * in 16-byte chunks. SETP accesses are not tag checked; they set + * the tags. + */ +static uint64_t set_step_tags(CPUARMState *env, uint64_t toaddr, + uint64_t setsize, uint32_t data, int memidx, + uint32_t *mtedesc, uintptr_t ra) +{ + void *mem; + uint64_t cleanaddr; + + setsize = MIN(setsize, page_limit(toaddr)); + + cleanaddr = useronly_clean_ptr(toaddr); + /* + * Trapless lookup: returns NULL for invalid page, I/O, + * watchpoints, clean pages, etc. + */ + mem = tlb_vaddr_to_host(env, cleanaddr, MMU_DATA_STORE, memidx); + +#ifndef CONFIG_USER_ONLY + if (unlikely(!mem)) { + /* + * Slow-path: just do one write. This will handle the + * watchpoint, invalid page, etc handling correctly. + * The architecture requires that we do 16 bytes at a time, + * and we know both ptr and size are 16 byte aligned. + * For clean code pages, the next iteration will see + * the page dirty and will use the fast path. + */ + uint64_t repldata = data * 0x0101010101010101ULL; + MemOpIdx oi16 = make_memop_idx(MO_TE | MO_128, memidx); + cpu_st16_mmu(env, toaddr, int128_make128(repldata, repldata), oi16, ra); + mte_mops_set_tags(env, toaddr, 16, *mtedesc); + return 16; + } +#endif + /* Easy case: just memset the host memory */ + memset(mem, data, setsize); + mte_mops_set_tags(env, toaddr, setsize, *mtedesc); + return setsize; +} + +typedef uint64_t StepFn(CPUARMState *env, uint64_t toaddr, + uint64_t setsize, uint32_t data, + int memidx, uint32_t *mtedesc, uintptr_t ra); + +/* Extract register numbers from a MOPS exception syndrome value */ +static int mops_destreg(uint32_t syndrome) +{ + return extract32(syndrome, 10, 5); +} + +static int mops_srcreg(uint32_t syndrome) +{ + return extract32(syndrome, 5, 5); +} + +static int mops_sizereg(uint32_t syndrome) +{ + return extract32(syndrome, 0, 5); +} + +/* + * Return true if TCMA and TBI bits mean we need to do MTE checks. + * We only need to do this once per MOPS insn, not for every page. + */ +static bool mte_checks_needed(uint64_t ptr, uint32_t desc) +{ + int bit55 = extract64(ptr, 55, 1); + + /* + * Note that tbi_check() returns true for "access checked" but + * tcma_check() returns true for "access unchecked". + */ + if (!tbi_check(desc, bit55)) { + return false; + } + return !tcma_check(desc, bit55, allocation_tag_from_addr(ptr)); +} + +/* Take an exception if the SETG addr/size are not granule aligned */ +static void check_setg_alignment(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t memidx, uintptr_t ra) +{ + if ((size != 0 && !QEMU_IS_ALIGNED(ptr, TAG_GRANULE)) || + !QEMU_IS_ALIGNED(size, TAG_GRANULE)) { + arm_cpu_do_unaligned_access(env_cpu(env), ptr, MMU_DATA_STORE, + memidx, ra); + + } +} + +static uint64_t arm_reg_or_xzr(CPUARMState *env, int reg) +{ + /* + * Runtime equivalent of cpu_reg() -- return the CPU register value, + * for contexts when index 31 means XZR (not SP). + */ + return reg == 31 ? 0 : env->xregs[reg]; +} + +/* + * For the Memory Set operation, our implementation chooses + * always to use "option A", where we update Xd to the final + * address in the SETP insn, and set Xn to be -(bytes remaining). + * On SETM and SETE insns we only need update Xn. + * + * @env: CPU + * @syndrome: syndrome value for mismatch exceptions + * (also contains the register numbers we need to use) + * @mtedesc: MTE descriptor word + * @stepfn: function which does a single part of the set operation + * @is_setg: true if this is the tag-setting SETG variant + */ +static void do_setp(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, + StepFn *stepfn, bool is_setg, uintptr_t ra) +{ + /* Prologue: we choose to do up to the next page boundary */ + int rd = mops_destreg(syndrome); + int rs = mops_srcreg(syndrome); + int rn = mops_sizereg(syndrome); + uint8_t data = arm_reg_or_xzr(env, rs); + uint32_t memidx = FIELD_EX32(mtedesc, MTEDESC, MIDX); + uint64_t toaddr = env->xregs[rd]; + uint64_t setsize = env->xregs[rn]; + uint64_t stagesetsize, step; + + check_mops_enabled(env, ra); + + if (setsize > INT64_MAX) { + setsize = INT64_MAX; + if (is_setg) { + setsize &= ~0xf; + } + } + + if (unlikely(is_setg)) { + check_setg_alignment(env, toaddr, setsize, memidx, ra); + } else if (!mte_checks_needed(toaddr, mtedesc)) { + mtedesc = 0; + } + + stagesetsize = MIN(setsize, page_limit(toaddr)); + while (stagesetsize) { + env->xregs[rd] = toaddr; + env->xregs[rn] = setsize; + step = stepfn(env, toaddr, stagesetsize, data, memidx, &mtedesc, ra); + toaddr += step; + setsize -= step; + stagesetsize -= step; + } + /* Insn completed, so update registers to the Option A format */ + env->xregs[rd] = toaddr + setsize; + env->xregs[rn] = -setsize; + + /* Set NZCV = 0000 to indicate we are an Option A implementation */ + env->NF = 0; + env->ZF = 1; /* our env->ZF encoding is inverted */ + env->CF = 0; + env->VF = 0; + return; +} + +void HELPER(setp)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) +{ + do_setp(env, syndrome, mtedesc, set_step, false, GETPC()); +} + +void HELPER(setgp)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) +{ + do_setp(env, syndrome, mtedesc, set_step_tags, true, GETPC()); +} + +static void do_setm(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, + StepFn *stepfn, bool is_setg, uintptr_t ra) +{ + /* Main: we choose to do all the full-page chunks */ + CPUState *cs = env_cpu(env); + int rd = mops_destreg(syndrome); + int rs = mops_srcreg(syndrome); + int rn = mops_sizereg(syndrome); + uint8_t data = arm_reg_or_xzr(env, rs); + uint64_t toaddr = env->xregs[rd] + env->xregs[rn]; + uint64_t setsize = -env->xregs[rn]; + uint32_t memidx = FIELD_EX32(mtedesc, MTEDESC, MIDX); + uint64_t step, stagesetsize; + + check_mops_enabled(env, ra); + + /* + * We're allowed to NOP out "no data to copy" before the consistency + * checks; we choose to do so. + */ + if (env->xregs[rn] == 0) { + return; + } + + check_mops_wrong_option(env, syndrome, ra); + + /* + * Our implementation will work fine even if we have an unaligned + * destination address, and because we update Xn every time around + * the loop below and the return value from stepfn() may be less + * than requested, we might find toaddr is unaligned. So we don't + * have an IMPDEF check for alignment here. + */ + + if (unlikely(is_setg)) { + check_setg_alignment(env, toaddr, setsize, memidx, ra); + } else if (!mte_checks_needed(toaddr, mtedesc)) { + mtedesc = 0; + } + + /* Do the actual memset: we leave the last partial page to SETE */ + stagesetsize = setsize & TARGET_PAGE_MASK; + while (stagesetsize > 0) { + step = stepfn(env, toaddr, setsize, data, memidx, &mtedesc, ra); + toaddr += step; + setsize -= step; + stagesetsize -= step; + env->xregs[rn] = -setsize; + if (stagesetsize > 0 && unlikely(cpu_loop_exit_requested(cs))) { + cpu_loop_exit_restore(cs, ra); + } + } +} + +void HELPER(setm)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) +{ + do_setm(env, syndrome, mtedesc, set_step, false, GETPC()); +} + +void HELPER(setgm)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) +{ + do_setm(env, syndrome, mtedesc, set_step_tags, true, GETPC()); +} + +static void do_sete(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, + StepFn *stepfn, bool is_setg, uintptr_t ra) +{ + /* Epilogue: do the last partial page */ + int rd = mops_destreg(syndrome); + int rs = mops_srcreg(syndrome); + int rn = mops_sizereg(syndrome); + uint8_t data = arm_reg_or_xzr(env, rs); + uint64_t toaddr = env->xregs[rd] + env->xregs[rn]; + uint64_t setsize = -env->xregs[rn]; + uint32_t memidx = FIELD_EX32(mtedesc, MTEDESC, MIDX); + uint64_t step; + + check_mops_enabled(env, ra); + + /* + * We're allowed to NOP out "no data to copy" before the consistency + * checks; we choose to do so. + */ + if (setsize == 0) { + return; + } + + check_mops_wrong_option(env, syndrome, ra); + + /* + * Our implementation has no address alignment requirements, but + * we do want to enforce the "less than a page" size requirement, + * so we don't need to have the "check for interrupts" here. + */ + if (setsize >= TARGET_PAGE_SIZE) { + raise_exception_ra(env, EXCP_UDEF, syndrome, + mops_mismatch_exception_target_el(env), ra); + } + + if (unlikely(is_setg)) { + check_setg_alignment(env, toaddr, setsize, memidx, ra); + } else if (!mte_checks_needed(toaddr, mtedesc)) { + mtedesc = 0; + } + + /* Do the actual memset */ + while (setsize > 0) { + step = stepfn(env, toaddr, setsize, data, memidx, &mtedesc, ra); + toaddr += step; + setsize -= step; + env->xregs[rn] = -setsize; + } +} + +void HELPER(sete)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) +{ + do_sete(env, syndrome, mtedesc, set_step, false, GETPC()); +} + +void HELPER(setge)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) +{ + do_sete(env, syndrome, mtedesc, set_step_tags, true, GETPC()); +} + +/* + * Perform part of a memory copy from the guest memory at fromaddr + * and extending for copysize bytes, to the guest memory at + * toaddr. Both addresses are dirty. + * + * Returns the number of bytes actually set, which might be less than + * copysize; the caller should loop until the whole copy has been done. + * The caller should ensure that the guest registers are correct + * for the possibility that the first byte of the copy encounters + * an exception or watchpoint. We guarantee not to take any faults + * for bytes other than the first. + */ +static uint64_t copy_step(CPUARMState *env, uint64_t toaddr, uint64_t fromaddr, + uint64_t copysize, int wmemidx, int rmemidx, + uint32_t *wdesc, uint32_t *rdesc, uintptr_t ra) +{ + void *rmem; + void *wmem; + + /* Don't cross a page boundary on either source or destination */ + copysize = MIN(copysize, page_limit(toaddr)); + copysize = MIN(copysize, page_limit(fromaddr)); + /* + * Handle MTE tag checks: either handle the tag mismatch for byte 0, + * or else copy up to but not including the byte with the mismatch. + */ + if (*rdesc) { + uint64_t mtesize = mte_mops_probe(env, fromaddr, copysize, *rdesc); + if (mtesize == 0) { + mte_check_fail(env, *rdesc, fromaddr, ra); + *rdesc = 0; + } else { + copysize = MIN(copysize, mtesize); + } + } + if (*wdesc) { + uint64_t mtesize = mte_mops_probe(env, toaddr, copysize, *wdesc); + if (mtesize == 0) { + mte_check_fail(env, *wdesc, toaddr, ra); + *wdesc = 0; + } else { + copysize = MIN(copysize, mtesize); + } + } + + toaddr = useronly_clean_ptr(toaddr); + fromaddr = useronly_clean_ptr(fromaddr); + /* Trapless lookup of whether we can get a host memory pointer */ + wmem = tlb_vaddr_to_host(env, toaddr, MMU_DATA_STORE, wmemidx); + rmem = tlb_vaddr_to_host(env, fromaddr, MMU_DATA_LOAD, rmemidx); + +#ifndef CONFIG_USER_ONLY + /* + * If we don't have host memory for both source and dest then just + * do a single byte copy. This will handle watchpoints, invalid pages, + * etc correctly. For clean code pages, the next iteration will see + * the page dirty and will use the fast path. + */ + if (unlikely(!rmem || !wmem)) { + uint8_t byte; + if (rmem) { + byte = *(uint8_t *)rmem; + } else { + byte = cpu_ldub_mmuidx_ra(env, fromaddr, rmemidx, ra); + } + if (wmem) { + *(uint8_t *)wmem = byte; + } else { + cpu_stb_mmuidx_ra(env, toaddr, byte, wmemidx, ra); + } + return 1; + } +#endif + /* Easy case: just memmove the host memory */ + memmove(wmem, rmem, copysize); + return copysize; +} + +/* + * Do part of a backwards memory copy. Here toaddr and fromaddr point + * to the *last* byte to be copied. + */ +static uint64_t copy_step_rev(CPUARMState *env, uint64_t toaddr, + uint64_t fromaddr, + uint64_t copysize, int wmemidx, int rmemidx, + uint32_t *wdesc, uint32_t *rdesc, uintptr_t ra) +{ + void *rmem; + void *wmem; + + /* Don't cross a page boundary on either source or destination */ + copysize = MIN(copysize, page_limit_rev(toaddr)); + copysize = MIN(copysize, page_limit_rev(fromaddr)); + + /* + * Handle MTE tag checks: either handle the tag mismatch for byte 0, + * or else copy up to but not including the byte with the mismatch. + */ + if (*rdesc) { + uint64_t mtesize = mte_mops_probe_rev(env, fromaddr, copysize, *rdesc); + if (mtesize == 0) { + mte_check_fail(env, *rdesc, fromaddr, ra); + *rdesc = 0; + } else { + copysize = MIN(copysize, mtesize); + } + } + if (*wdesc) { + uint64_t mtesize = mte_mops_probe_rev(env, toaddr, copysize, *wdesc); + if (mtesize == 0) { + mte_check_fail(env, *wdesc, toaddr, ra); + *wdesc = 0; + } else { + copysize = MIN(copysize, mtesize); + } + } + + toaddr = useronly_clean_ptr(toaddr); + fromaddr = useronly_clean_ptr(fromaddr); + /* Trapless lookup of whether we can get a host memory pointer */ + wmem = tlb_vaddr_to_host(env, toaddr, MMU_DATA_STORE, wmemidx); + rmem = tlb_vaddr_to_host(env, fromaddr, MMU_DATA_LOAD, rmemidx); + +#ifndef CONFIG_USER_ONLY + /* + * If we don't have host memory for both source and dest then just + * do a single byte copy. This will handle watchpoints, invalid pages, + * etc correctly. For clean code pages, the next iteration will see + * the page dirty and will use the fast path. + */ + if (unlikely(!rmem || !wmem)) { + uint8_t byte; + if (rmem) { + byte = *(uint8_t *)rmem; + } else { + byte = cpu_ldub_mmuidx_ra(env, fromaddr, rmemidx, ra); + } + if (wmem) { + *(uint8_t *)wmem = byte; + } else { + cpu_stb_mmuidx_ra(env, toaddr, byte, wmemidx, ra); + } + return 1; + } +#endif + /* + * Easy case: just memmove the host memory. Note that wmem and + * rmem here point to the *last* byte to copy. + */ + memmove(wmem - (copysize - 1), rmem - (copysize - 1), copysize); + return copysize; +} + +/* + * for the Memory Copy operation, our implementation chooses always + * to use "option A", where we update Xd and Xs to the final addresses + * in the CPYP insn, and then in CPYM and CPYE only need to update Xn. + * + * @env: CPU + * @syndrome: syndrome value for mismatch exceptions + * (also contains the register numbers we need to use) + * @wdesc: MTE descriptor for the writes (destination) + * @rdesc: MTE descriptor for the reads (source) + * @move: true if this is CPY (memmove), false for CPYF (memcpy forwards) + */ +static void do_cpyp(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc, uint32_t move, uintptr_t ra) +{ + int rd = mops_destreg(syndrome); + int rs = mops_srcreg(syndrome); + int rn = mops_sizereg(syndrome); + uint32_t rmemidx = FIELD_EX32(rdesc, MTEDESC, MIDX); + uint32_t wmemidx = FIELD_EX32(wdesc, MTEDESC, MIDX); + bool forwards = true; + uint64_t toaddr = env->xregs[rd]; + uint64_t fromaddr = env->xregs[rs]; + uint64_t copysize = env->xregs[rn]; + uint64_t stagecopysize, step; + + check_mops_enabled(env, ra); + + + if (move) { + /* + * Copy backwards if necessary. The direction for a non-overlapping + * copy is IMPDEF; we choose forwards. + */ + if (copysize > 0x007FFFFFFFFFFFFFULL) { + copysize = 0x007FFFFFFFFFFFFFULL; + } + uint64_t fs = extract64(fromaddr, 0, 56); + uint64_t ts = extract64(toaddr, 0, 56); + uint64_t fe = extract64(fromaddr + copysize, 0, 56); + + if (fs < ts && fe > ts) { + forwards = false; + } + } else { + if (copysize > INT64_MAX) { + copysize = INT64_MAX; + } + } + + if (!mte_checks_needed(fromaddr, rdesc)) { + rdesc = 0; + } + if (!mte_checks_needed(toaddr, wdesc)) { + wdesc = 0; + } + + if (forwards) { + stagecopysize = MIN(copysize, page_limit(toaddr)); + stagecopysize = MIN(stagecopysize, page_limit(fromaddr)); + while (stagecopysize) { + env->xregs[rd] = toaddr; + env->xregs[rs] = fromaddr; + env->xregs[rn] = copysize; + step = copy_step(env, toaddr, fromaddr, stagecopysize, + wmemidx, rmemidx, &wdesc, &rdesc, ra); + toaddr += step; + fromaddr += step; + copysize -= step; + stagecopysize -= step; + } + /* Insn completed, so update registers to the Option A format */ + env->xregs[rd] = toaddr + copysize; + env->xregs[rs] = fromaddr + copysize; + env->xregs[rn] = -copysize; + } else { + /* + * In a reverse copy the to and from addrs in Xs and Xd are the start + * of the range, but it's more convenient for us to work with pointers + * to the last byte being copied. + */ + toaddr += copysize - 1; + fromaddr += copysize - 1; + stagecopysize = MIN(copysize, page_limit_rev(toaddr)); + stagecopysize = MIN(stagecopysize, page_limit_rev(fromaddr)); + while (stagecopysize) { + env->xregs[rn] = copysize; + step = copy_step_rev(env, toaddr, fromaddr, stagecopysize, + wmemidx, rmemidx, &wdesc, &rdesc, ra); + copysize -= step; + stagecopysize -= step; + toaddr -= step; + fromaddr -= step; + } + /* + * Insn completed, so update registers to the Option A format. + * For a reverse copy this is no different to the CPYP input format. + */ + env->xregs[rn] = copysize; + } + + /* Set NZCV = 0000 to indicate we are an Option A implementation */ + env->NF = 0; + env->ZF = 1; /* our env->ZF encoding is inverted */ + env->CF = 0; + env->VF = 0; + return; +} + +void HELPER(cpyp)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc) +{ + do_cpyp(env, syndrome, wdesc, rdesc, true, GETPC()); +} + +void HELPER(cpyfp)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc) +{ + do_cpyp(env, syndrome, wdesc, rdesc, false, GETPC()); +} + +static void do_cpym(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc, uint32_t move, uintptr_t ra) +{ + /* Main: we choose to copy until less than a page remaining */ + CPUState *cs = env_cpu(env); + int rd = mops_destreg(syndrome); + int rs = mops_srcreg(syndrome); + int rn = mops_sizereg(syndrome); + uint32_t rmemidx = FIELD_EX32(rdesc, MTEDESC, MIDX); + uint32_t wmemidx = FIELD_EX32(wdesc, MTEDESC, MIDX); + bool forwards = true; + uint64_t toaddr, fromaddr, copysize, step; + + check_mops_enabled(env, ra); + + /* We choose to NOP out "no data to copy" before consistency checks */ + if (env->xregs[rn] == 0) { + return; + } + + check_mops_wrong_option(env, syndrome, ra); + + if (move) { + forwards = (int64_t)env->xregs[rn] < 0; + } + + if (forwards) { + toaddr = env->xregs[rd] + env->xregs[rn]; + fromaddr = env->xregs[rs] + env->xregs[rn]; + copysize = -env->xregs[rn]; + } else { + copysize = env->xregs[rn]; + /* This toaddr and fromaddr point to the *last* byte to copy */ + toaddr = env->xregs[rd] + copysize - 1; + fromaddr = env->xregs[rs] + copysize - 1; + } + + if (!mte_checks_needed(fromaddr, rdesc)) { + rdesc = 0; + } + if (!mte_checks_needed(toaddr, wdesc)) { + wdesc = 0; + } + + /* Our implementation has no particular parameter requirements for CPYM */ + + /* Do the actual memmove */ + if (forwards) { + while (copysize >= TARGET_PAGE_SIZE) { + step = copy_step(env, toaddr, fromaddr, copysize, + wmemidx, rmemidx, &wdesc, &rdesc, ra); + toaddr += step; + fromaddr += step; + copysize -= step; + env->xregs[rn] = -copysize; + if (copysize >= TARGET_PAGE_SIZE && + unlikely(cpu_loop_exit_requested(cs))) { + cpu_loop_exit_restore(cs, ra); + } + } + } else { + while (copysize >= TARGET_PAGE_SIZE) { + step = copy_step_rev(env, toaddr, fromaddr, copysize, + wmemidx, rmemidx, &wdesc, &rdesc, ra); + toaddr -= step; + fromaddr -= step; + copysize -= step; + env->xregs[rn] = copysize; + if (copysize >= TARGET_PAGE_SIZE && + unlikely(cpu_loop_exit_requested(cs))) { + cpu_loop_exit_restore(cs, ra); + } + } + } +} + +void HELPER(cpym)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc) +{ + do_cpym(env, syndrome, wdesc, rdesc, true, GETPC()); +} + +void HELPER(cpyfm)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc) +{ + do_cpym(env, syndrome, wdesc, rdesc, false, GETPC()); +} + +static void do_cpye(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc, uint32_t move, uintptr_t ra) +{ + /* Epilogue: do the last partial page */ + int rd = mops_destreg(syndrome); + int rs = mops_srcreg(syndrome); + int rn = mops_sizereg(syndrome); + uint32_t rmemidx = FIELD_EX32(rdesc, MTEDESC, MIDX); + uint32_t wmemidx = FIELD_EX32(wdesc, MTEDESC, MIDX); + bool forwards = true; + uint64_t toaddr, fromaddr, copysize, step; + + check_mops_enabled(env, ra); + + /* We choose to NOP out "no data to copy" before consistency checks */ + if (env->xregs[rn] == 0) { + return; + } + + check_mops_wrong_option(env, syndrome, ra); + + if (move) { + forwards = (int64_t)env->xregs[rn] < 0; + } + + if (forwards) { + toaddr = env->xregs[rd] + env->xregs[rn]; + fromaddr = env->xregs[rs] + env->xregs[rn]; + copysize = -env->xregs[rn]; + } else { + copysize = env->xregs[rn]; + /* This toaddr and fromaddr point to the *last* byte to copy */ + toaddr = env->xregs[rd] + copysize - 1; + fromaddr = env->xregs[rs] + copysize - 1; + } + + if (!mte_checks_needed(fromaddr, rdesc)) { + rdesc = 0; + } + if (!mte_checks_needed(toaddr, wdesc)) { + wdesc = 0; + } + + /* Check the size; we don't want to have do a check-for-interrupts */ + if (copysize >= TARGET_PAGE_SIZE) { + raise_exception_ra(env, EXCP_UDEF, syndrome, + mops_mismatch_exception_target_el(env), ra); + } + + /* Do the actual memmove */ + if (forwards) { + while (copysize > 0) { + step = copy_step(env, toaddr, fromaddr, copysize, + wmemidx, rmemidx, &wdesc, &rdesc, ra); + toaddr += step; + fromaddr += step; + copysize -= step; + env->xregs[rn] = -copysize; + } + } else { + while (copysize > 0) { + step = copy_step_rev(env, toaddr, fromaddr, copysize, + wmemidx, rmemidx, &wdesc, &rdesc, ra); + toaddr -= step; + fromaddr -= step; + copysize -= step; + env->xregs[rn] = copysize; + } + } +} + +void HELPER(cpye)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc) +{ + do_cpye(env, syndrome, wdesc, rdesc, true, GETPC()); +} + +void HELPER(cpyfe)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, + uint32_t rdesc) +{ + do_cpye(env, syndrome, wdesc, rdesc, false, GETPC()); +} diff --git a/target/arm/helper-a64.h b/target/arm/tcg/helper-a64.h similarity index 87% rename from target/arm/helper-a64.h rename to target/arm/tcg/helper-a64.h index 7b706571bb..575a5dab7d 100644 --- a/target/arm/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -50,14 +50,6 @@ DEF_HELPER_FLAGS_2(frecpx_f16, TCG_CALL_NO_RWG, f16, f16, ptr) DEF_HELPER_FLAGS_2(fcvtx_f64_to_f32, TCG_CALL_NO_RWG, f32, f64, env) DEF_HELPER_FLAGS_3(crc32_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) DEF_HELPER_FLAGS_3(crc32c_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) -DEF_HELPER_FLAGS_4(paired_cmpxchg64_le, TCG_CALL_NO_WG, i64, env, i64, i64, i64) -DEF_HELPER_FLAGS_4(paired_cmpxchg64_le_parallel, TCG_CALL_NO_WG, - i64, env, i64, i64, i64) -DEF_HELPER_FLAGS_4(paired_cmpxchg64_be, TCG_CALL_NO_WG, i64, env, i64, i64, i64) -DEF_HELPER_FLAGS_4(paired_cmpxchg64_be_parallel, TCG_CALL_NO_WG, - i64, env, i64, i64, i64) -DEF_HELPER_5(casp_le_parallel, void, env, i32, i64, i64, i64) -DEF_HELPER_5(casp_be_parallel, void, env, i32, i64, i64, i64) DEF_HELPER_FLAGS_3(advsimd_maxh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) DEF_HELPER_FLAGS_3(advsimd_minh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) DEF_HELPER_FLAGS_3(advsimd_maxnumh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) @@ -98,9 +90,13 @@ DEF_HELPER_FLAGS_3(pacda, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(pacdb, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(pacga, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(autia, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(autia_combined, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(autib, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(autib_combined, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(autda, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(autda_combined, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(autdb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(autdb_combined, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_2(xpaci, TCG_CALL_NO_RWG_SE, i64, env, i64) DEF_HELPER_FLAGS_2(xpacd, TCG_CALL_NO_RWG_SE, i64, env, i64) @@ -118,3 +114,20 @@ DEF_HELPER_FLAGS_2(st2g_stub, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_2(ldgm, TCG_CALL_NO_WG, i64, env, i64) DEF_HELPER_FLAGS_3(stgm, TCG_CALL_NO_WG, void, env, i64, i64) DEF_HELPER_FLAGS_3(stzgm_tags, TCG_CALL_NO_WG, void, env, i64, i64) + +DEF_HELPER_FLAGS_4(unaligned_access, TCG_CALL_NO_WG, + noreturn, env, i64, i32, i32) + +DEF_HELPER_3(setp, void, env, i32, i32) +DEF_HELPER_3(setm, void, env, i32, i32) +DEF_HELPER_3(sete, void, env, i32, i32) +DEF_HELPER_3(setgp, void, env, i32, i32) +DEF_HELPER_3(setgm, void, env, i32, i32) +DEF_HELPER_3(setge, void, env, i32, i32) + +DEF_HELPER_4(cpyp, void, env, i32, i32, i32) +DEF_HELPER_4(cpym, void, env, i32, i32, i32) +DEF_HELPER_4(cpye, void, env, i32, i32, i32) +DEF_HELPER_4(cpyfp, void, env, i32, i32, i32) +DEF_HELPER_4(cpyfm, void, env, i32, i32, i32) +DEF_HELPER_4(cpyfe, void, env, i32, i32, i32) diff --git a/target/arm/helper-mve.h b/target/arm/tcg/helper-mve.h similarity index 100% rename from target/arm/helper-mve.h rename to target/arm/tcg/helper-mve.h diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h new file mode 100644 index 0000000000..27eef49a11 --- /dev/null +++ b/target/arm/tcg/helper-sme.h @@ -0,0 +1,146 @@ +/* + * AArch64 SME specific helper definitions + * + * Copyright (c) 2022 Linaro, Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 . + */ + +DEF_HELPER_FLAGS_3(set_svcr, TCG_CALL_NO_RWG, void, env, i32, i32) + +DEF_HELPER_FLAGS_3(sme_zero, TCG_CALL_NO_RWG, void, env, i32, i32) + +/* Move to/from vertical array slices, i.e. columns, so 'c'. */ +DEF_HELPER_FLAGS_4(sme_mova_cz_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_zc_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_cz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_zc_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_cz_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_zc_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_cz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_zc_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_cz_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_mova_zc_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sme_ld1b_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1b_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1b_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1b_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_ld1h_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1h_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1h_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1h_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1h_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1h_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1h_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1h_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_ld1s_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1s_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1s_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1s_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1s_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1s_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1s_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1s_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_ld1d_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1d_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1d_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1d_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1d_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1d_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1d_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1d_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_ld1q_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1q_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1q_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1q_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1q_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1q_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1q_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1q_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_st1b_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1b_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1b_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1b_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_st1h_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1h_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1h_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1h_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1h_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1h_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1h_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1h_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_st1s_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1s_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1s_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1s_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1s_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1s_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1s_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1s_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_st1d_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1d_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1d_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1d_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1d_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1d_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1d_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1d_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_st1q_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1q_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1q_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1q_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1q_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1q_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1q_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_st1q_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_5(sme_addha_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme_addva_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme_addha_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme_addva_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_7(sme_fmopa_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_7(sme_fmopa_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_7(sme_fmopa_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_bfmopa, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_smopa_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_umopa_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_sumopa_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_usmopa_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_smopa_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_umopa_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_sumopa_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_usmopa_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/helper-sve.h b/target/arm/tcg/helper-sve.h similarity index 99% rename from target/arm/helper-sve.h rename to target/arm/tcg/helper-sve.h index dc629f851a..cc4e1d8948 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -325,6 +325,8 @@ DEF_HELPER_FLAGS_5(sve_sel_zpzz_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve_sel_zpzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sel_zpzz_q, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve2_addp_zpzz_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) @@ -717,6 +719,8 @@ DEF_HELPER_FLAGS_4(sve_revh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_revw_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme_revd_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve_rbit_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_rbit_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_rbit_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c new file mode 100644 index 0000000000..5da1b0fc1d --- /dev/null +++ b/target/arm/tcg/hflags.c @@ -0,0 +1,485 @@ +/* + * ARM hflags + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "cpu.h" +#include "internals.h" +#include "cpu-features.h" +#include "exec/helper-proto.h" +#include "cpregs.h" + +static inline bool fgt_svc(CPUARMState *env, int el) +{ + /* + * Assuming fine-grained-traps are active, return true if we + * should be trapping on SVC instructions. Only AArch64 can + * trap on an SVC at EL1, but we don't need to special-case this + * because if this is AArch32 EL1 then arm_fgt_active() is false. + * We also know el is 0 or 1. + */ + return el == 0 ? + FIELD_EX64(env->cp15.fgt_exec[FGTREG_HFGITR], HFGITR_EL2, SVC_EL0) : + FIELD_EX64(env->cp15.fgt_exec[FGTREG_HFGITR], HFGITR_EL2, SVC_EL1); +} + +/* Return true if memory alignment should be enforced. */ +static bool aprofile_require_alignment(CPUARMState *env, int el, uint64_t sctlr) +{ +#ifdef CONFIG_USER_ONLY + return false; +#else + /* Check the alignment enable bit. */ + if (sctlr & SCTLR_A) { + return true; + } + + /* + * If translation is disabled, then the default memory type is + * Device(-nGnRnE) instead of Normal, which requires that alignment + * be enforced. Since this affects all ram, it is most efficient + * to handle this during translation. + */ + if (sctlr & SCTLR_M) { + /* Translation enabled: memory type in PTE via MAIR_ELx. */ + return false; + } + if (el < 2 && (arm_hcr_el2_eff(env) & (HCR_DC | HCR_VM))) { + /* Stage 2 translation enabled: memory type in PTE. */ + return false; + } + return true; +#endif +} + +static CPUARMTBFlags rebuild_hflags_common(CPUARMState *env, int fp_el, + ARMMMUIdx mmu_idx, + CPUARMTBFlags flags) +{ + DP_TBFLAG_ANY(flags, FPEXC_EL, fp_el); + DP_TBFLAG_ANY(flags, MMUIDX, arm_to_core_mmu_idx(mmu_idx)); + + if (arm_singlestep_active(env)) { + DP_TBFLAG_ANY(flags, SS_ACTIVE, 1); + } + + return flags; +} + +static CPUARMTBFlags rebuild_hflags_common_32(CPUARMState *env, int fp_el, + ARMMMUIdx mmu_idx, + CPUARMTBFlags flags) +{ + bool sctlr_b = arm_sctlr_b(env); + + if (sctlr_b) { + DP_TBFLAG_A32(flags, SCTLR__B, 1); + } + if (arm_cpu_data_is_big_endian_a32(env, sctlr_b)) { + DP_TBFLAG_ANY(flags, BE_DATA, 1); + } + DP_TBFLAG_A32(flags, NS, !access_secure_reg(env)); + + return rebuild_hflags_common(env, fp_el, mmu_idx, flags); +} + +static CPUARMTBFlags rebuild_hflags_m32(CPUARMState *env, int fp_el, + ARMMMUIdx mmu_idx) +{ + CPUARMTBFlags flags = {}; + uint32_t ccr = env->v7m.ccr[env->v7m.secure]; + + /* Without HaveMainExt, CCR.UNALIGN_TRP is RES1. */ + if (ccr & R_V7M_CCR_UNALIGN_TRP_MASK) { + DP_TBFLAG_ANY(flags, ALIGN_MEM, 1); + } + + if (arm_v7m_is_handler_mode(env)) { + DP_TBFLAG_M32(flags, HANDLER, 1); + } + + /* + * v8M always applies stack limit checks unless CCR.STKOFHFNMIGN + * is suppressing them because the requested execution priority + * is less than 0. + */ + if (arm_feature(env, ARM_FEATURE_V8) && + !((mmu_idx & ARM_MMU_IDX_M_NEGPRI) && + (ccr & R_V7M_CCR_STKOFHFNMIGN_MASK))) { + DP_TBFLAG_M32(flags, STACKCHECK, 1); + } + + if (arm_feature(env, ARM_FEATURE_M_SECURITY) && env->v7m.secure) { + DP_TBFLAG_M32(flags, SECURE, 1); + } + + return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags); +} + +/* This corresponds to the ARM pseudocode function IsFullA64Enabled(). */ +static bool sme_fa64(CPUARMState *env, int el) +{ + if (!cpu_isar_feature(aa64_sme_fa64, env_archcpu(env))) { + return false; + } + + if (el <= 1 && !el_is_in_host(env, el)) { + if (!FIELD_EX64(env->vfp.smcr_el[1], SMCR, FA64)) { + return false; + } + } + if (el <= 2 && arm_is_el2_enabled(env)) { + if (!FIELD_EX64(env->vfp.smcr_el[2], SMCR, FA64)) { + return false; + } + } + if (arm_feature(env, ARM_FEATURE_EL3)) { + if (!FIELD_EX64(env->vfp.smcr_el[3], SMCR, FA64)) { + return false; + } + } + + return true; +} + +static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, + ARMMMUIdx mmu_idx) +{ + CPUARMTBFlags flags = {}; + int el = arm_current_el(env); + uint64_t sctlr = arm_sctlr(env, el); + + if (aprofile_require_alignment(env, el, sctlr)) { + DP_TBFLAG_ANY(flags, ALIGN_MEM, 1); + } + + if (arm_el_is_aa64(env, 1)) { + DP_TBFLAG_A32(flags, VFPEN, 1); + } + + if (el < 2 && env->cp15.hstr_el2 && arm_is_el2_enabled(env) && + (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { + DP_TBFLAG_A32(flags, HSTR_ACTIVE, 1); + } + + if (arm_fgt_active(env, el)) { + DP_TBFLAG_ANY(flags, FGT_ACTIVE, 1); + if (fgt_svc(env, el)) { + DP_TBFLAG_ANY(flags, FGT_SVC, 1); + } + } + + if (env->uncached_cpsr & CPSR_IL) { + DP_TBFLAG_ANY(flags, PSTATE__IL, 1); + } + + /* + * The SME exception we are testing for is raised via + * AArch64.CheckFPAdvSIMDEnabled(), as called from + * AArch32.CheckAdvSIMDOrFPEnabled(). + */ + if (el == 0 + && FIELD_EX64(env->svcr, SVCR, SM) + && (!arm_is_el2_enabled(env) + || (arm_el_is_aa64(env, 2) && !(env->cp15.hcr_el2 & HCR_TGE))) + && arm_el_is_aa64(env, 1) + && !sme_fa64(env, el)) { + DP_TBFLAG_A32(flags, SME_TRAP_NONSTREAMING, 1); + } + + return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags); +} + +static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, + ARMMMUIdx mmu_idx) +{ + CPUARMTBFlags flags = {}; + ARMMMUIdx stage1 = stage_1_mmu_idx(mmu_idx); + uint64_t tcr = regime_tcr(env, mmu_idx); + uint64_t hcr = arm_hcr_el2_eff(env); + uint64_t sctlr; + int tbii, tbid; + + DP_TBFLAG_ANY(flags, AARCH64_STATE, 1); + + /* Get control bits for tagged addresses. */ + tbid = aa64_va_parameter_tbi(tcr, mmu_idx); + tbii = tbid & ~aa64_va_parameter_tbid(tcr, mmu_idx); + + DP_TBFLAG_A64(flags, TBII, tbii); + DP_TBFLAG_A64(flags, TBID, tbid); + + if (cpu_isar_feature(aa64_sve, env_archcpu(env))) { + int sve_el = sve_exception_el(env, el); + + /* + * If either FP or SVE are disabled, translator does not need len. + * If SVE EL > FP EL, FP exception has precedence, and translator + * does not need SVE EL. Save potential re-translations by forcing + * the unneeded data to zero. + */ + if (fp_el != 0) { + if (sve_el > fp_el) { + sve_el = 0; + } + } else if (sve_el == 0) { + DP_TBFLAG_A64(flags, VL, sve_vqm1_for_el(env, el)); + } + DP_TBFLAG_A64(flags, SVEEXC_EL, sve_el); + } + if (cpu_isar_feature(aa64_sme, env_archcpu(env))) { + int sme_el = sme_exception_el(env, el); + bool sm = FIELD_EX64(env->svcr, SVCR, SM); + + DP_TBFLAG_A64(flags, SMEEXC_EL, sme_el); + if (sme_el == 0) { + /* Similarly, do not compute SVL if SME is disabled. */ + int svl = sve_vqm1_for_el_sm(env, el, true); + DP_TBFLAG_A64(flags, SVL, svl); + if (sm) { + /* If SVE is disabled, we will not have set VL above. */ + DP_TBFLAG_A64(flags, VL, svl); + } + } + if (sm) { + DP_TBFLAG_A64(flags, PSTATE_SM, 1); + DP_TBFLAG_A64(flags, SME_TRAP_NONSTREAMING, !sme_fa64(env, el)); + } + DP_TBFLAG_A64(flags, PSTATE_ZA, FIELD_EX64(env->svcr, SVCR, ZA)); + } + + sctlr = regime_sctlr(env, stage1); + + if (aprofile_require_alignment(env, el, sctlr)) { + DP_TBFLAG_ANY(flags, ALIGN_MEM, 1); + } + + if (arm_cpu_data_is_big_endian_a64(el, sctlr)) { + DP_TBFLAG_ANY(flags, BE_DATA, 1); + } + + if (cpu_isar_feature(aa64_pauth, env_archcpu(env))) { + /* + * In order to save space in flags, we record only whether + * pauth is "inactive", meaning all insns are implemented as + * a nop, or "active" when some action must be performed. + * The decision of which action to take is left to a helper. + */ + if (sctlr & (SCTLR_EnIA | SCTLR_EnIB | SCTLR_EnDA | SCTLR_EnDB)) { + DP_TBFLAG_A64(flags, PAUTH_ACTIVE, 1); + } + } + + if (cpu_isar_feature(aa64_bti, env_archcpu(env))) { + /* Note that SCTLR_EL[23].BT == SCTLR_BT1. */ + if (sctlr & (el == 0 ? SCTLR_BT0 : SCTLR_BT1)) { + DP_TBFLAG_A64(flags, BT, 1); + } + } + + if (cpu_isar_feature(aa64_lse2, env_archcpu(env))) { + if (sctlr & SCTLR_nAA) { + DP_TBFLAG_A64(flags, NAA, 1); + } + } + + /* 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: + /* FEAT_NV: NV,NV1 == 1,1 means we don't do UNPRIV accesses */ + if ((hcr & (HCR_NV | HCR_NV1)) != (HCR_NV | HCR_NV1)) { + DP_TBFLAG_A64(flags, UNPRIV, 1); + } + break; + case ARMMMUIdx_E20_2: + case ARMMMUIdx_E20_2_PAN: + /* + * 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) { + DP_TBFLAG_A64(flags, UNPRIV, 1); + } + break; + default: + break; + } + } + + if (env->pstate & PSTATE_IL) { + DP_TBFLAG_ANY(flags, PSTATE__IL, 1); + } + + if (arm_fgt_active(env, el)) { + DP_TBFLAG_ANY(flags, FGT_ACTIVE, 1); + if (FIELD_EX64(env->cp15.fgt_exec[FGTREG_HFGITR], HFGITR_EL2, ERET)) { + DP_TBFLAG_A64(flags, TRAP_ERET, 1); + } + if (fgt_svc(env, el)) { + DP_TBFLAG_ANY(flags, FGT_SVC, 1); + } + } + + /* + * ERET can also be trapped for FEAT_NV. arm_hcr_el2_eff() takes care + * of "is EL2 enabled" and the NV bit can only be set if FEAT_NV is present. + */ + if (el == 1 && (hcr & HCR_NV)) { + DP_TBFLAG_A64(flags, TRAP_ERET, 1); + DP_TBFLAG_A64(flags, NV, 1); + if (hcr & HCR_NV1) { + DP_TBFLAG_A64(flags, NV1, 1); + } + if (hcr & HCR_NV2) { + DP_TBFLAG_A64(flags, NV2, 1); + if (hcr & HCR_E2H) { + DP_TBFLAG_A64(flags, NV2_MEM_E20, 1); + } + if (env->cp15.sctlr_el[2] & SCTLR_EE) { + DP_TBFLAG_A64(flags, NV2_MEM_BE, 1); + } + } + } + + if (cpu_isar_feature(aa64_mte, env_archcpu(env))) { + /* + * Set MTE_ACTIVE if any access may be Checked, and leave clear + * if all accesses must be Unchecked: + * 1) If no TBI, then there are no tags in the address to check, + * 2) If Tag Check Override, then all accesses are Unchecked, + * 3) If Tag Check Fail == 0, then Checked access have no effect, + * 4) If no Allocation Tag Access, then all accesses are Unchecked. + */ + if (allocation_tag_access_enabled(env, el, sctlr)) { + DP_TBFLAG_A64(flags, ATA, 1); + if (tbid + && !(env->pstate & PSTATE_TCO) + && (sctlr & (el == 0 ? SCTLR_TCF0 : SCTLR_TCF))) { + DP_TBFLAG_A64(flags, MTE_ACTIVE, 1); + if (!EX_TBFLAG_A64(flags, UNPRIV)) { + /* + * In non-unpriv contexts (eg EL0), unpriv load/stores + * act like normal ones; duplicate the MTE info to + * avoid translate-a64.c having to check UNPRIV to see + * whether it is OK to index into MTE_ACTIVE[]. + */ + DP_TBFLAG_A64(flags, MTE0_ACTIVE, 1); + } + } + } + /* And again for unprivileged accesses, if required. */ + if (EX_TBFLAG_A64(flags, UNPRIV) + && tbid + && !(env->pstate & PSTATE_TCO) + && (sctlr & SCTLR_TCF0) + && allocation_tag_access_enabled(env, 0, sctlr)) { + DP_TBFLAG_A64(flags, MTE0_ACTIVE, 1); + } + /* + * For unpriv tag-setting accesses we also need ATA0. Again, in + * contexts where unpriv and normal insns are the same we + * duplicate the ATA bit to save effort for translate-a64.c. + */ + if (EX_TBFLAG_A64(flags, UNPRIV)) { + if (allocation_tag_access_enabled(env, 0, sctlr)) { + DP_TBFLAG_A64(flags, ATA0, 1); + } + } else { + DP_TBFLAG_A64(flags, ATA0, EX_TBFLAG_A64(flags, ATA)); + } + /* Cache TCMA as well as TBI. */ + DP_TBFLAG_A64(flags, TCMA, aa64_va_parameter_tcma(tcr, mmu_idx)); + } + + return rebuild_hflags_common(env, fp_el, mmu_idx, flags); +} + +static CPUARMTBFlags rebuild_hflags_internal(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); + + if (is_a64(env)) { + return rebuild_hflags_a64(env, el, fp_el, mmu_idx); + } else if (arm_feature(env, ARM_FEATURE_M)) { + return rebuild_hflags_m32(env, fp_el, mmu_idx); + } else { + return rebuild_hflags_a32(env, fp_el, mmu_idx); + } +} + +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); + ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, 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); + ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, el); + + env->hflags = rebuild_hflags_a32(env, fp_el, mmu_idx); +} + +void HELPER(rebuild_hflags_a64)(CPUARMState *env, int el) +{ + int fp_el = fp_exception_el(env, el); + ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, el); + + env->hflags = rebuild_hflags_a64(env, el, fp_el, mmu_idx); +} + +void assert_hflags_rebuild_correctly(CPUARMState *env) +{ +#ifdef CONFIG_DEBUG_TCG + CPUARMTBFlags c = env->hflags; + CPUARMTBFlags r = rebuild_hflags_internal(env); + + if (unlikely(c.flags != r.flags || c.flags2 != r.flags2)) { + fprintf(stderr, "TCG hflags mismatch " + "(current:(0x%08x,0x" TARGET_FMT_lx ")" + " rebuilt:(0x%08x,0x" TARGET_FMT_lx ")\n", + c.flags, c.flags2, r.flags, r.flags2); + abort(); + } +#endif +} diff --git a/target/arm/iwmmxt_helper.c b/target/arm/tcg/iwmmxt_helper.c similarity index 100% rename from target/arm/iwmmxt_helper.c rename to target/arm/tcg/iwmmxt_helper.c diff --git a/target/arm/m-nocp.decode b/target/arm/tcg/m-nocp.decode similarity index 100% rename from target/arm/m-nocp.decode rename to target/arm/tcg/m-nocp.decode diff --git a/target/arm/m_helper.c b/target/arm/tcg/m_helper.c similarity index 95% rename from target/arm/m_helper.c rename to target/arm/tcg/m_helper.c index a740c3e160..d1f1e02acc 100644 --- a/target/arm/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -7,33 +7,22 @@ */ #include "qemu/osdep.h" -#include "qemu/units.h" -#include "target/arm/idau.h" -#include "trace.h" #include "cpu.h" #include "internals.h" -#include "exec/gdbstub.h" +#include "cpu-features.h" +#include "gdbstub/helpers.h" #include "exec/helper-proto.h" -#include "qemu/host-utils.h" #include "qemu/main-loop.h" #include "qemu/bitops.h" -#include "qemu/crc32c.h" -#include "qemu/qemu-print.h" #include "qemu/log.h" #include "exec/exec-all.h" -#include /* For crc32 */ -#include "semihosting/semihost.h" -#include "sysemu/cpus.h" -#include "sysemu/kvm.h" -#include "qemu/range.h" -#include "qapi/qapi-commands-machine-target.h" -#include "qapi/error.h" -#include "qemu/guest-random.h" #ifdef CONFIG_TCG -#include "arm_ldst.h" #include "exec/cpu_ldst.h" #include "semihosting/common-semi.h" #endif +#if !defined(CONFIG_USER_ONLY) +#include "hw/intc/armv7m_nvic.h" +#endif static void v7m_msr_xpsr(CPUARMState *env, uint32_t mask, uint32_t reg, uint32_t val) @@ -69,7 +58,7 @@ static uint32_t v7m_mrs_xpsr(CPUARMState *env, uint32_t reg, unsigned el) return xpsr_read(env) & mask; } -static uint32_t v7m_mrs_control(CPUARMState *env, uint32_t secure) +uint32_t arm_v7m_mrs_control(CPUARMState *env, uint32_t secure) { uint32_t value = env->v7m.control[secure]; @@ -106,7 +95,7 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) case 0 ... 7: /* xPSR sub-fields */ return v7m_mrs_xpsr(env, reg, 0); case 20: /* CONTROL */ - return v7m_mrs_control(env, 0); + return arm_v7m_mrs_control(env, 0); default: /* Unprivileged reads others as zero. */ return 0; @@ -160,13 +149,55 @@ uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) * R: 0 because unpriv and A flag not set * SRVALID: 0 because NS * MRVALID: 0 because unpriv and A flag not set - * SREGION: 0 becaus SRVALID is 0 + * SREGION: 0 because SRVALID is 0 * MREGION: 0 because MRVALID is 0 */ return 0; } -#else +ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate) +{ + return ARMMMUIdx_MUser; +} + +#else /* !CONFIG_USER_ONLY */ + +static ARMMMUIdx arm_v7m_mmu_idx_all(CPUARMState *env, + bool secstate, bool priv, bool negpri) +{ + ARMMMUIdx mmu_idx = ARM_MMU_IDX_M; + + if (priv) { + mmu_idx |= ARM_MMU_IDX_M_PRIV; + } + + if (negpri) { + mmu_idx |= ARM_MMU_IDX_M_NEGPRI; + } + + if (secstate) { + mmu_idx |= ARM_MMU_IDX_M_S; + } + + return mmu_idx; +} + +static ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env, + bool secstate, bool priv) +{ + bool negpri = armv7m_nvic_neg_prio_requested(env->nvic, secstate); + + return arm_v7m_mmu_idx_all(env, secstate, priv, negpri); +} + +/* Return the MMU index for a v7M CPU in the specified security state */ +ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate) +{ + bool priv = arm_v7m_is_handler_mode(env) || + !(env->v7m.control[secstate] & 1); + + return arm_v7m_mmu_idx_for_secstate_and_priv(env, secstate, priv); +} /* * What kind of stack write are we doing? This affects how exceptions @@ -183,19 +214,14 @@ static bool v7m_stack_write(ARMCPU *cpu, uint32_t addr, uint32_t value, { CPUState *cs = CPU(cpu); CPUARMState *env = &cpu->env; - MemTxAttrs attrs = {}; MemTxResult txres; - target_ulong page_size; - hwaddr physaddr; - int prot; + GetPhysAddrResult res = {}; ARMMMUFaultInfo fi = {}; - ARMCacheAttrs cacheattrs = {}; bool secure = mmu_idx & ARM_MMU_IDX_M_S; int exc; bool exc_secure; - if (get_phys_addr(env, addr, MMU_DATA_STORE, mmu_idx, &physaddr, - &attrs, &prot, &page_size, &fi, &cacheattrs)) { + if (get_phys_addr(env, addr, MMU_DATA_STORE, mmu_idx, &res, &fi)) { /* MPU/SAU lookup failed */ if (fi.type == ARMFault_QEMU_SFault) { if (mode == STACK_LAZYFP) { @@ -228,8 +254,8 @@ static bool v7m_stack_write(ARMCPU *cpu, uint32_t addr, uint32_t value, } goto pend_fault; } - address_space_stl_le(arm_addressspace(cs, attrs), physaddr, value, - attrs, &txres); + address_space_stl_le(arm_addressspace(cs, res.f.attrs), res.f.phys_addr, + value, res.f.attrs, &txres); if (txres != MEMTX_OK) { /* BusFault trying to write the data */ if (mode == STACK_LAZYFP) { @@ -276,20 +302,15 @@ static bool v7m_stack_read(ARMCPU *cpu, uint32_t *dest, uint32_t addr, { CPUState *cs = CPU(cpu); CPUARMState *env = &cpu->env; - MemTxAttrs attrs = {}; MemTxResult txres; - target_ulong page_size; - hwaddr physaddr; - int prot; + GetPhysAddrResult res = {}; ARMMMUFaultInfo fi = {}; - ARMCacheAttrs cacheattrs = {}; bool secure = mmu_idx & ARM_MMU_IDX_M_S; int exc; bool exc_secure; uint32_t value; - if (get_phys_addr(env, addr, MMU_DATA_LOAD, mmu_idx, &physaddr, - &attrs, &prot, &page_size, &fi, &cacheattrs)) { + if (get_phys_addr(env, addr, MMU_DATA_LOAD, mmu_idx, &res, &fi)) { /* MPU/SAU lookup failed */ if (fi.type == ARMFault_QEMU_SFault) { qemu_log_mask(CPU_LOG_INT, @@ -308,8 +329,8 @@ static bool v7m_stack_read(ARMCPU *cpu, uint32_t *dest, uint32_t addr, goto pend_fault; } - value = address_space_ldl(arm_addressspace(cs, attrs), physaddr, - attrs, &txres); + value = address_space_ldl(arm_addressspace(cs, res.f.attrs), + res.f.phys_addr, res.f.attrs, &txres); if (txres != MEMTX_OK) { /* BusFault trying to read the data */ qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.UNSTKERR\n"); @@ -352,8 +373,8 @@ void HELPER(v7m_preserve_fp_state)(CPUARMState *env) bool ts = is_secure && (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK); bool take_exception; - /* Take the iothread lock as we are going to touch the NVIC */ - qemu_mutex_lock_iothread(); + /* Take the BQL as we are going to touch the NVIC */ + bql_lock(); /* Check the background context had access to the FPU */ if (!v7m_cpacr_pass(env, is_secure, is_priv)) { @@ -407,7 +428,7 @@ void HELPER(v7m_preserve_fp_state)(CPUARMState *env) take_exception = !stacked_ok && armv7m_nvic_can_take_pending_exception(env->nvic); - qemu_mutex_unlock_iothread(); + bql_unlock(); if (take_exception) { raise_exception_ra(env, EXCP_LAZYFP, 0, 1, GETPC()); @@ -631,42 +652,6 @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) arm_rebuild_hflags(env); } -static uint32_t *get_v7m_sp_ptr(CPUARMState *env, bool secure, bool threadmode, - bool spsel) -{ - /* - * Return a pointer to the location where we currently store the - * stack pointer for the requested security state and thread mode. - * This pointer will become invalid if the CPU state is updated - * such that the stack pointers are switched around (eg changing - * the SPSEL control bit). - * Compare the v8M ARM ARM pseudocode LookUpSP_with_security_mode(). - * Unlike that pseudocode, we require the caller to pass us in the - * SPSEL control bit value; this is because we also use this - * function in handling of pushing of the callee-saves registers - * part of the v8M stack frame (pseudocode PushCalleeStack()), - * and in the tailchain codepath the SPSEL bit comes from the exception - * return magic LR value from the previous exception. The pseudocode - * opencodes the stack-selection in PushCalleeStack(), but we prefer - * to make this utility function generic enough to do the job. - */ - bool want_psp = threadmode && spsel; - - if (secure == env->v7m.secure) { - if (want_psp == v7m_using_psp(env)) { - return &env->regs[13]; - } else { - return &env->v7m.other_sp; - } - } else { - if (want_psp) { - return &env->v7m.other_ss_psp; - } else { - return &env->v7m.other_ss_msp; - } - } -} - static bool arm_v7m_load_vector(ARMCPU *cpu, int exc, bool targets_secure, uint32_t *pvec) { @@ -699,7 +684,8 @@ static bool arm_v7m_load_vector(ARMCPU *cpu, int exc, bool targets_secure, if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { V8M_SAttributes sattrs = {}; - v8m_security_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, &sattrs); + v8m_security_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, + targets_secure, &sattrs); if (sattrs.ns) { attrs.secure = false; } else if (!targets_secure) { @@ -790,8 +776,8 @@ static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain, !mode; mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, M_REG_S, priv); - frame_sp_p = get_v7m_sp_ptr(env, M_REG_S, mode, - lr & R_V7M_EXCRET_SPSEL_MASK); + frame_sp_p = arm_v7m_get_sp_ptr(env, M_REG_S, mode, + lr & R_V7M_EXCRET_SPSEL_MASK); want_psp = mode && (lr & R_V7M_EXCRET_SPSEL_MASK); if (want_psp) { limit = env->v7m.psplim[M_REG_S]; @@ -904,7 +890,7 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain, } lr &= ~R_V7M_EXCRET_ES_MASK; - if (targets_secure || !arm_feature(env, ARM_FEATURE_M_SECURITY)) { + if (targets_secure) { lr |= R_V7M_EXCRET_ES_MASK; } lr &= ~R_V7M_EXCRET_SPSEL_MASK; @@ -998,7 +984,7 @@ static void v7m_update_fpccr(CPUARMState *env, uint32_t frameptr, * that we will need later in order to do lazy FP reg stacking. */ bool is_secure = env->v7m.secure; - void *nvic = env->nvic; + NVICState *nvic = env->nvic; /* * Some bits are unbanked and live always in fpccr[M_REG_S]; some bits * are banked and we want to update the bit in the bank for the @@ -1636,10 +1622,8 @@ static void do_v7m_exception_exit(ARMCPU *cpu) * use 'frame_sp_p' after we do something that makes it invalid. */ bool spsel = env->v7m.control[return_to_secure] & R_V7M_CONTROL_SPSEL_MASK; - uint32_t *frame_sp_p = get_v7m_sp_ptr(env, - return_to_secure, - !return_to_handler, - spsel); + uint32_t *frame_sp_p = arm_v7m_get_sp_ptr(env, return_to_secure, + !return_to_handler, spsel); uint32_t frameptr = *frame_sp_p; bool pop_ok = true; ARMMMUIdx mmu_idx; @@ -1945,7 +1929,7 @@ static bool do_v7m_function_return(ARMCPU *cpu) threadmode = !arm_v7m_is_handler_mode(env); spsel = env->v7m.control[M_REG_S] & R_V7M_CONTROL_SPSEL_MASK; - frame_sp_p = get_v7m_sp_ptr(env, true, threadmode, spsel); + frame_sp_p = arm_v7m_get_sp_ptr(env, true, threadmode, spsel); frameptr = *frame_sp_p; /* @@ -1954,8 +1938,8 @@ static bool do_v7m_function_return(ARMCPU *cpu) */ mmu_idx = arm_v7m_mmu_idx_for_secstate(env, true); oi = make_memop_idx(MO_LEUL, arm_to_core_mmu_idx(mmu_idx)); - newpc = cpu_ldl_le_mmu(env, frameptr, oi, 0); - newpsr = cpu_ldl_le_mmu(env, frameptr + 4, oi, 0); + newpc = cpu_ldl_mmu(env, frameptr, oi, 0); + newpsr = cpu_ldl_mmu(env, frameptr + 4, oi, 0); /* Consistency checks on new IPSR */ newpsr_exc = newpsr & XPSR_EXCP; @@ -1990,7 +1974,7 @@ static bool do_v7m_function_return(ARMCPU *cpu) return true; } -static bool v7m_read_half_insn(ARMCPU *cpu, ARMMMUIdx mmu_idx, +static bool v7m_read_half_insn(ARMCPU *cpu, ARMMMUIdx mmu_idx, bool secure, uint32_t addr, uint16_t *insn) { /* @@ -2008,15 +1992,11 @@ static bool v7m_read_half_insn(ARMCPU *cpu, ARMMMUIdx mmu_idx, CPUState *cs = CPU(cpu); CPUARMState *env = &cpu->env; V8M_SAttributes sattrs = {}; - MemTxAttrs attrs = {}; + GetPhysAddrResult res = {}; ARMMMUFaultInfo fi = {}; - ARMCacheAttrs cacheattrs = {}; MemTxResult txres; - target_ulong page_size; - hwaddr physaddr; - int prot; - v8m_security_lookup(env, addr, MMU_INST_FETCH, mmu_idx, &sattrs); + v8m_security_lookup(env, addr, MMU_INST_FETCH, mmu_idx, secure, &sattrs); if (!sattrs.nsc || sattrs.ns) { /* * This must be the second half of the insn, and it straddles a @@ -2028,16 +2008,15 @@ static bool v7m_read_half_insn(ARMCPU *cpu, ARMMMUIdx mmu_idx, "...really SecureFault with SFSR.INVEP\n"); return false; } - if (get_phys_addr(env, addr, MMU_INST_FETCH, mmu_idx, &physaddr, - &attrs, &prot, &page_size, &fi, &cacheattrs)) { + if (get_phys_addr(env, addr, MMU_INST_FETCH, mmu_idx, &res, &fi)) { /* the MPU lookup failed */ env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_IACCVIOL_MASK; armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM, env->v7m.secure); qemu_log_mask(CPU_LOG_INT, "...really MemManage with CFSR.IACCVIOL\n"); return false; } - *insn = address_space_lduw_le(arm_addressspace(cs, attrs), physaddr, - attrs, &txres); + *insn = address_space_lduw_le(arm_addressspace(cs, res.f.attrs), + res.f.phys_addr, res.f.attrs, &txres); if (txres != MEMTX_OK) { env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_IBUSERR_MASK; armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_BUS, false); @@ -2060,17 +2039,12 @@ static bool v7m_read_sg_stack_word(ARMCPU *cpu, ARMMMUIdx mmu_idx, */ CPUState *cs = CPU(cpu); CPUARMState *env = &cpu->env; - MemTxAttrs attrs = {}; MemTxResult txres; - target_ulong page_size; - hwaddr physaddr; - int prot; + GetPhysAddrResult res = {}; ARMMMUFaultInfo fi = {}; - ARMCacheAttrs cacheattrs = {}; uint32_t value; - if (get_phys_addr(env, addr, MMU_DATA_LOAD, mmu_idx, &physaddr, - &attrs, &prot, &page_size, &fi, &cacheattrs)) { + if (get_phys_addr(env, addr, MMU_DATA_LOAD, mmu_idx, &res, &fi)) { /* MPU/SAU lookup failed */ if (fi.type == ARMFault_QEMU_SFault) { qemu_log_mask(CPU_LOG_INT, @@ -2088,8 +2062,8 @@ static bool v7m_read_sg_stack_word(ARMCPU *cpu, ARMMMUIdx mmu_idx, } return false; } - value = address_space_ldl(arm_addressspace(cs, attrs), physaddr, - attrs, &txres); + value = address_space_ldl(arm_addressspace(cs, res.f.attrs), + res.f.phys_addr, res.f.attrs, &txres); if (txres != MEMTX_OK) { /* BusFault trying to read the data */ qemu_log_mask(CPU_LOG_INT, @@ -2127,7 +2101,7 @@ static bool v7m_handle_execute_nsc(ARMCPU *cpu) /* We want to do the MPU lookup as secure; work out what mmu_idx that is */ mmu_idx = arm_v7m_mmu_idx_for_secstate(env, true); - if (!v7m_read_half_insn(cpu, mmu_idx, env->regs[15], &insn)) { + if (!v7m_read_half_insn(cpu, mmu_idx, true, env->regs[15], &insn)) { return false; } @@ -2143,7 +2117,7 @@ static bool v7m_handle_execute_nsc(ARMCPU *cpu) goto gen_invep; } - if (!v7m_read_half_insn(cpu, mmu_idx, env->regs[15] + 2, &insn)) { + if (!v7m_read_half_insn(cpu, mmu_idx, true, env->regs[15] + 2, &insn)) { return false; } @@ -2373,7 +2347,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) "...handling as semihosting call 0x%x\n", env->regs[0]); #ifdef CONFIG_TCG - env->regs[0] = do_common_semihosting(cs); + do_common_semihosting(cs); #else g_assert_not_reached(); #endif @@ -2455,7 +2429,7 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) case 0 ... 7: /* xPSR sub-fields */ return v7m_mrs_xpsr(env, reg, el); case 20: /* CONTROL */ - return v7m_mrs_control(env, env->v7m.secure); + return arm_v7m_mrs_control(env, env->v7m.secure); case 0x94: /* CONTROL_NS */ /* * We have to handle this here because unprivileged Secure code @@ -2500,11 +2474,17 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) } return env->v7m.primask[M_REG_NS]; case 0x91: /* BASEPRI_NS */ + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } if (!env->v7m.secure) { return 0; } return env->v7m.basepri[M_REG_NS]; case 0x93: /* FAULTMASK_NS */ + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } if (!env->v7m.secure) { return 0; } @@ -2550,8 +2530,14 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) return env->v7m.primask[env->v7m.secure]; case 17: /* BASEPRI */ case 18: /* BASEPRI_MAX */ + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } return env->v7m.basepri[env->v7m.secure]; case 19: /* FAULTMASK */ + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } return env->v7m.faultmask[env->v7m.secure]; default: bad_reg: @@ -2616,13 +2602,19 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) env->v7m.primask[M_REG_NS] = val & 1; return; case 0x91: /* BASEPRI_NS */ - if (!env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_MAIN)) { + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } + if (!env->v7m.secure) { return; } env->v7m.basepri[M_REG_NS] = val & 0xff; return; case 0x93: /* FAULTMASK_NS */ - if (!env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_MAIN)) { + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } + if (!env->v7m.secure) { return; } env->v7m.faultmask[M_REG_NS] = val & 1; @@ -2790,15 +2782,10 @@ uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) V8M_SAttributes sattrs = {}; uint32_t tt_resp; bool r, rw, nsr, nsrw, mrvalid; - int prot; - ARMMMUFaultInfo fi = {}; - MemTxAttrs attrs = {}; - hwaddr phys_addr; ARMMMUIdx mmu_idx; uint32_t mregion; bool targetpriv; bool targetsec = env->v7m.secure; - bool is_subpage; /* * Work out what the security state and privilege level we're @@ -2829,18 +2816,20 @@ uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) * inspecting the other MPU state. */ if (arm_current_el(env) != 0 || alt) { + GetPhysAddrResult res = {}; + ARMMMUFaultInfo fi = {}; + /* We can ignore the return value as prot is always set */ - pmsav8_mpu_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, - &phys_addr, &attrs, &prot, &is_subpage, - &fi, &mregion); + pmsav8_mpu_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, targetsec, + &res, &fi, &mregion); if (mregion == -1) { mrvalid = false; mregion = 0; } else { mrvalid = true; } - r = prot & PAGE_READ; - rw = prot & PAGE_WRITE; + r = res.f.prot & PAGE_READ; + rw = res.f.prot & PAGE_WRITE; } else { r = false; rw = false; @@ -2849,7 +2838,8 @@ uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) } if (env->v7m.secure) { - v8m_security_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, &sattrs); + v8m_security_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, + targetsec, &sattrs); nsr = sattrs.ns && r; nsrw = sattrs.ns && rw; } else { @@ -2875,39 +2865,38 @@ uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) #endif /* !CONFIG_USER_ONLY */ -ARMMMUIdx arm_v7m_mmu_idx_all(CPUARMState *env, - bool secstate, bool priv, bool negpri) +uint32_t *arm_v7m_get_sp_ptr(CPUARMState *env, bool secure, bool threadmode, + bool spsel) { - ARMMMUIdx mmu_idx = ARM_MMU_IDX_M; + /* + * Return a pointer to the location where we currently store the + * stack pointer for the requested security state and thread mode. + * This pointer will become invalid if the CPU state is updated + * such that the stack pointers are switched around (eg changing + * the SPSEL control bit). + * Compare the v8M ARM ARM pseudocode LookUpSP_with_security_mode(). + * Unlike that pseudocode, we require the caller to pass us in the + * SPSEL control bit value; this is because we also use this + * function in handling of pushing of the callee-saves registers + * part of the v8M stack frame (pseudocode PushCalleeStack()), + * and in the tailchain codepath the SPSEL bit comes from the exception + * return magic LR value from the previous exception. The pseudocode + * opencodes the stack-selection in PushCalleeStack(), but we prefer + * to make this utility function generic enough to do the job. + */ + bool want_psp = threadmode && spsel; - if (priv) { - mmu_idx |= ARM_MMU_IDX_M_PRIV; + if (secure == env->v7m.secure) { + if (want_psp == v7m_using_psp(env)) { + return &env->regs[13]; + } else { + return &env->v7m.other_sp; + } + } else { + if (want_psp) { + return &env->v7m.other_ss_psp; + } else { + return &env->v7m.other_ss_msp; + } } - - if (negpri) { - mmu_idx |= ARM_MMU_IDX_M_NEGPRI; - } - - if (secstate) { - mmu_idx |= ARM_MMU_IDX_M_S; - } - - return mmu_idx; -} - -ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env, - bool secstate, bool priv) -{ - bool negpri = armv7m_nvic_neg_prio_requested(env->nvic, secstate); - - return arm_v7m_mmu_idx_all(env, secstate, priv, negpri); -} - -/* Return the MMU index for a v7M CPU in the specified security state */ -ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate) -{ - bool priv = arm_v7m_is_handler_mode(env) || - !(env->v7m.control[secstate] & 1); - - return arm_v7m_mmu_idx_for_secstate_and_priv(env, secstate, priv); } diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build new file mode 100644 index 0000000000..3b1a9f0fc5 --- /dev/null +++ b/target/arm/tcg/meson.build @@ -0,0 +1,60 @@ +gen_a64 = [ + decodetree.process('a64.decode', extra_args: ['--static-decode=disas_a64']), + decodetree.process('sve.decode', extra_args: '--decode=disas_sve'), + decodetree.process('sme.decode', extra_args: '--decode=disas_sme'), + decodetree.process('sme-fa64.decode', extra_args: '--static-decode=disas_sme_fa64'), +] + +gen_a32 = [ + decodetree.process('neon-shared.decode', extra_args: '--decode=disas_neon_shared'), + decodetree.process('neon-dp.decode', extra_args: '--decode=disas_neon_dp'), + decodetree.process('neon-ls.decode', extra_args: '--decode=disas_neon_ls'), + decodetree.process('vfp.decode', extra_args: '--decode=disas_vfp'), + decodetree.process('vfp-uncond.decode', extra_args: '--decode=disas_vfp_uncond'), + decodetree.process('m-nocp.decode', extra_args: '--decode=disas_m_nocp'), + decodetree.process('mve.decode', extra_args: '--decode=disas_mve'), + decodetree.process('a32.decode', extra_args: '--static-decode=disas_a32'), + decodetree.process('a32-uncond.decode', extra_args: '--static-decode=disas_a32_uncond'), + decodetree.process('t32.decode', extra_args: '--static-decode=disas_t32'), + decodetree.process('t16.decode', extra_args: ['-w', '16', '--static-decode=disas_t16']), +] + +arm_ss.add(gen_a32) +arm_ss.add(when: 'TARGET_AARCH64', if_true: gen_a64) + +arm_ss.add(files( + 'cpu32.c', + 'translate.c', + 'translate-m-nocp.c', + 'translate-mve.c', + 'translate-neon.c', + 'translate-vfp.c', + 'crypto_helper.c', + 'hflags.c', + 'iwmmxt_helper.c', + 'm_helper.c', + 'mve_helper.c', + 'neon_helper.c', + 'op_helper.c', + 'tlb_helper.c', + 'vec_helper.c', +)) + +arm_ss.add(when: 'TARGET_AARCH64', if_true: files( + 'cpu64.c', + 'translate-a64.c', + 'translate-sve.c', + 'translate-sme.c', + 'helper-a64.c', + 'mte_helper.c', + 'pauth_helper.c', + 'sme_helper.c', + 'sve_helper.c', +)) + +arm_system_ss.add(files( + 'psci.c', +)) + +arm_system_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('cpu-v7m.c')) +arm_user_ss.add(when: 'TARGET_AARCH64', if_false: files('cpu-v7m.c')) diff --git a/target/arm/mte_helper.c b/target/arm/tcg/mte_helper.c similarity index 66% rename from target/arm/mte_helper.c rename to target/arm/tcg/mte_helper.c index d11a8c70d0..d971b81370 100644 --- a/target/arm/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -25,6 +25,7 @@ #include "exec/ram_addr.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" +#include "hw/core/tcg-cpu-ops.h" #include "qapi/error.h" #include "qemu/guest-random.h" @@ -49,14 +50,14 @@ static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude) } /** - * allocation_tag_mem: + * allocation_tag_mem_probe: * @env: the cpu environment * @ptr_mmu_idx: the addressing regime to use for the virtual address * @ptr: the virtual address for which to look up tag memory * @ptr_access: the access to use for the virtual address * @ptr_size: the number of bytes in the normal memory access * @tag_access: the access to use for the tag memory - * @tag_size: the number of bytes in the tag memory access + * @probe: true to merely probe, never taking an exception * @ra: the return address for exception handling * * Our tag memory is formatted as a sequence of little-endian nibbles. @@ -65,18 +66,25 @@ static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude) * for the higher addr. * * Here, resolve the physical address from the virtual address, and return - * a pointer to the corresponding tag byte. Exit with exception if the - * virtual address is not accessible for @ptr_access. - * - * The @ptr_size and @tag_size values may not have an obvious relation - * due to the alignment of @ptr, and the number of tag checks required. + * a pointer to the corresponding tag byte. * * If there is no tag storage corresponding to @ptr, return NULL. + * + * If the page is inaccessible for @ptr_access, or has a watchpoint, there are + * three options: + * (1) probe = true, ra = 0 : pure probe -- we return NULL if the page is not + * accessible, and do not take watchpoint traps. The calling code must + * handle those cases in the right priority compared to MTE traps. + * (2) probe = false, ra = 0 : probe, no fault expected -- the caller guarantees + * that the page is going to be accessible. We will take watchpoint traps. + * (3) probe = false, ra != 0 : non-probe -- we will take both memory access + * traps and watchpoint traps. + * (probe = true, ra != 0 is invalid and will assert.) */ -static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, - uint64_t ptr, MMUAccessType ptr_access, - int ptr_size, MMUAccessType tag_access, - int tag_size, uintptr_t ra) +static uint8_t *allocation_tag_mem_probe(CPUARMState *env, int ptr_mmu_idx, + uint64_t ptr, MMUAccessType ptr_access, + int ptr_size, MMUAccessType tag_access, + bool probe, uintptr_t ra) { #ifdef CONFIG_USER_ONLY uint64_t clean_ptr = useronly_clean_ptr(ptr); @@ -84,6 +92,8 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, uint8_t *tags; uintptr_t index; + assert(!(probe && ra)); + if (!(flags & (ptr_access == MMU_DATA_STORE ? PAGE_WRITE_ORG : PAGE_READ))) { cpu_loop_exit_sigsegv(env_cpu(env), ptr, ptr_access, !(flags & PAGE_VALID), ra); @@ -95,20 +105,14 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, } tags = page_get_target_data(clean_ptr); - if (tags == NULL) { - size_t alloc_size = TARGET_PAGE_SIZE >> (LOG2_TAG_GRANULE + 1); - tags = page_alloc_target_data(clean_ptr, alloc_size); - assert(tags != NULL); - } index = extract32(ptr, LOG2_TAG_GRANULE + 1, TARGET_PAGE_BITS - LOG2_TAG_GRANULE - 1); return tags + index; #else - uintptr_t index; - CPUIOTLBEntry *iotlbentry; + CPUTLBEntryFull *full; + MemTxAttrs attrs; int in_page, flags; - ram_addr_t ptr_ra; hwaddr ptr_paddr, tag_paddr, xlat; MemoryRegion *mr; ARMASIdx tag_asi; @@ -120,34 +124,20 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, * exception for inaccessible pages, and resolves the virtual address * into the softmmu tlb. * - * When RA == 0, this is for mte_probe. The page is expected to be - * valid. Indicate to probe_access_flags no-fault, then assert that - * we received a valid page. + * When RA == 0, this is either a pure probe or a no-fault-expected probe. + * Indicate to probe_access_flags no-fault, then either return NULL + * for the pure probe, or assert that we received a valid page for the + * no-fault-expected probe. */ - flags = probe_access_flags(env, ptr, ptr_access, ptr_mmu_idx, - ra == 0, &host, ra); + flags = probe_access_full(env, ptr, 0, ptr_access, ptr_mmu_idx, + ra == 0, &host, &full, ra); + if (probe && (flags & TLB_INVALID_MASK)) { + return NULL; + } assert(!(flags & TLB_INVALID_MASK)); - /* - * Find the iotlbentry for ptr. This *must* be present in the TLB - * because we just found the mapping. - * TODO: Perhaps there should be a cputlb helper that returns a - * matching tlb entry + iotlb entry. - */ - index = tlb_index(env, ptr_mmu_idx, ptr); -# ifdef CONFIG_DEBUG_TCG - { - CPUTLBEntry *entry = tlb_entry(env, ptr_mmu_idx, ptr); - target_ulong comparator = (ptr_access == MMU_DATA_LOAD - ? entry->addr_read - : tlb_addr_write(entry)); - g_assert(tlb_hit(comparator, ptr)); - } -# endif - iotlbentry = &env_tlb(env)->d[ptr_mmu_idx].iotlb[index]; - /* If the virtual page MemAttr != Tagged, access unchecked. */ - if (!arm_tlb_mte_tagged(&iotlbentry->attrs)) { + if (full->extra.arm.pte_attrs != 0xf0) { return NULL; } @@ -162,6 +152,14 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, return NULL; } + /* + * Remember these values across the second lookup below, + * which may invalidate this pointer via tlb resize. + */ + ptr_paddr = full->phys_addr | (ptr & ~TARGET_PAGE_MASK); + attrs = full->attrs; + full = NULL; + /* * The Normal memory access can extend to the next page. E.g. a single * 8-byte access to the last byte of a page will check only the last @@ -170,43 +168,26 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, */ in_page = -(ptr | TARGET_PAGE_MASK); if (unlikely(ptr_size > in_page)) { - void *ignore; - flags |= probe_access_flags(env, ptr + in_page, ptr_access, - ptr_mmu_idx, ra == 0, &ignore, ra); + flags |= probe_access_full(env, ptr + in_page, 0, ptr_access, + ptr_mmu_idx, ra == 0, &host, &full, ra); assert(!(flags & TLB_INVALID_MASK)); } /* Any debug exception has priority over a tag check exception. */ - if (unlikely(flags & TLB_WATCHPOINT)) { + if (!probe && unlikely(flags & TLB_WATCHPOINT)) { int wp = ptr_access == MMU_DATA_LOAD ? BP_MEM_READ : BP_MEM_WRITE; assert(ra != 0); - cpu_check_watchpoint(env_cpu(env), ptr, ptr_size, - iotlbentry->attrs, wp, ra); + cpu_check_watchpoint(env_cpu(env), ptr, ptr_size, attrs, wp, ra); } - /* - * Find the physical address within the normal mem space. - * The memory region lookup must succeed because TLB_MMIO was - * not set in the cputlb lookup above. - */ - mr = memory_region_from_host(host, &ptr_ra); - tcg_debug_assert(mr != NULL); - tcg_debug_assert(memory_region_is_ram(mr)); - ptr_paddr = ptr_ra; - do { - ptr_paddr += mr->addr; - mr = mr->container; - } while (mr); - /* Convert to the physical address in tag space. */ tag_paddr = ptr_paddr >> (LOG2_TAG_GRANULE + 1); /* Look up the address in tag space. */ - tag_asi = iotlbentry->attrs.secure ? ARMASIdx_TagS : ARMASIdx_TagNS; + tag_asi = attrs.secure ? ARMASIdx_TagS : ARMASIdx_TagNS; tag_as = cpu_get_address_space(env_cpu(env), tag_asi); mr = address_space_translate(tag_as, tag_paddr, &xlat, NULL, - tag_access == MMU_DATA_STORE, - iotlbentry->attrs); + tag_access == MMU_DATA_STORE, attrs); /* * Note that @mr will never be NULL. If there is nothing in the address @@ -235,6 +216,15 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, #endif } +static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, + uint64_t ptr, MMUAccessType ptr_access, + int ptr_size, MMUAccessType tag_access, + uintptr_t ra) +{ + return allocation_tag_mem_probe(env, ptr_mmu_idx, ptr, ptr_access, + ptr_size, tag_access, false, ra); +} + uint64_t HELPER(irg)(CPUARMState *env, uint64_t rn, uint64_t rm) { uint16_t exclude = extract32(rm | env->cp15.gcr_el1, 0, 16); @@ -301,13 +291,13 @@ static int load_tag1(uint64_t ptr, uint8_t *mem) uint64_t HELPER(ldg)(CPUARMState *env, uint64_t ptr, uint64_t xt) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); uint8_t *mem; int rtag = 0; /* Trap if accessing an invalid page. */ mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_LOAD, 1, - MMU_DATA_LOAD, 1, GETPC()); + MMU_DATA_LOAD, GETPC()); /* Load if page supports tags. */ if (mem) { @@ -321,7 +311,7 @@ static void check_tag_aligned(CPUARMState *env, uint64_t ptr, uintptr_t ra) { if (unlikely(!QEMU_IS_ALIGNED(ptr, TAG_GRANULE))) { arm_cpu_do_unaligned_access(env_cpu(env), ptr, MMU_DATA_STORE, - cpu_mmu_index(env, false), ra); + arm_env_mmu_index(env), ra); g_assert_not_reached(); } } @@ -354,14 +344,14 @@ typedef void stg_store1(uint64_t, uint8_t *, int); static inline void do_stg(CPUARMState *env, uint64_t ptr, uint64_t xt, uintptr_t ra, stg_store1 store1) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); uint8_t *mem; check_tag_aligned(env, ptr, ra); /* Trap if accessing an invalid page. */ mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, TAG_GRANULE, - MMU_DATA_STORE, 1, ra); + MMU_DATA_STORE, ra); /* Store if page supports tags. */ if (mem) { @@ -381,7 +371,7 @@ void HELPER(stg_parallel)(CPUARMState *env, uint64_t ptr, uint64_t xt) void HELPER(stg_stub)(CPUARMState *env, uint64_t ptr) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); uintptr_t ra = GETPC(); check_tag_aligned(env, ptr, ra); @@ -391,7 +381,7 @@ void HELPER(stg_stub)(CPUARMState *env, uint64_t ptr) static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt, uintptr_t ra, stg_store1 store1) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); int tag = allocation_tag_from_addr(xt); uint8_t *mem1, *mem2; @@ -404,10 +394,10 @@ static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt, if (ptr & TAG_GRANULE) { /* Two stores unaligned mod TAG_GRANULE*2 -- modify two bytes. */ mem1 = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, - TAG_GRANULE, MMU_DATA_STORE, 1, ra); + TAG_GRANULE, MMU_DATA_STORE, ra); mem2 = allocation_tag_mem(env, mmu_idx, ptr + TAG_GRANULE, MMU_DATA_STORE, TAG_GRANULE, - MMU_DATA_STORE, 1, ra); + MMU_DATA_STORE, ra); /* Store if page(s) support tags. */ if (mem1) { @@ -419,7 +409,7 @@ static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt, } else { /* Two stores aligned mod TAG_GRANULE*2 -- modify one byte. */ mem1 = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, - 2 * TAG_GRANULE, MMU_DATA_STORE, 1, ra); + 2 * TAG_GRANULE, MMU_DATA_STORE, ra); if (mem1) { tag |= tag << 4; qatomic_set(mem1, tag); @@ -439,7 +429,7 @@ void HELPER(st2g_parallel)(CPUARMState *env, uint64_t ptr, uint64_t xt) void HELPER(st2g_stub)(CPUARMState *env, uint64_t ptr) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); uintptr_t ra = GETPC(); int in_page = -(ptr | TARGET_PAGE_MASK); @@ -453,46 +443,80 @@ void HELPER(st2g_stub)(CPUARMState *env, uint64_t ptr) } } -#define LDGM_STGM_SIZE (4 << GMID_EL1_BS) - uint64_t HELPER(ldgm)(CPUARMState *env, uint64_t ptr) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); uintptr_t ra = GETPC(); + int gm_bs = env_archcpu(env)->gm_blocksize; + int gm_bs_bytes = 4 << gm_bs; void *tag_mem; + uint64_t ret; + int shift; - ptr = QEMU_ALIGN_DOWN(ptr, LDGM_STGM_SIZE); + ptr = QEMU_ALIGN_DOWN(ptr, gm_bs_bytes); /* Trap if accessing an invalid page. */ tag_mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_LOAD, - LDGM_STGM_SIZE, MMU_DATA_LOAD, - LDGM_STGM_SIZE / (2 * TAG_GRANULE), ra); + gm_bs_bytes, MMU_DATA_LOAD, ra); /* The tag is squashed to zero if the page does not support tags. */ if (!tag_mem) { return 0; } - QEMU_BUILD_BUG_ON(GMID_EL1_BS != 6); /* - * We are loading 64-bits worth of tags. The ordering of elements - * within the word corresponds to a 64-bit little-endian operation. + * The ordering of elements within the word corresponds to + * a little-endian operation. Computation of shift comes from + * + * index = address + * data = tag + * + * Because of the alignment of ptr above, BS=6 has shift=0. + * All memory operations are aligned. Defer support for BS=2, + * requiring insertion or extraction of a nibble, until we + * support a cpu that requires it. */ - return ldq_le_p(tag_mem); + switch (gm_bs) { + case 3: + /* 32 bytes -> 2 tags -> 8 result bits */ + ret = *(uint8_t *)tag_mem; + break; + case 4: + /* 64 bytes -> 4 tags -> 16 result bits */ + ret = cpu_to_le16(*(uint16_t *)tag_mem); + break; + case 5: + /* 128 bytes -> 8 tags -> 32 result bits */ + ret = cpu_to_le32(*(uint32_t *)tag_mem); + break; + case 6: + /* 256 bytes -> 16 tags -> 64 result bits */ + return cpu_to_le64(*(uint64_t *)tag_mem); + default: + /* + * CPU configured with unsupported/invalid gm blocksize. + * This is detected early in arm_cpu_realizefn. + */ + g_assert_not_reached(); + } + shift = extract64(ptr, LOG2_TAG_GRANULE, 4) * 4; + return ret << shift; } void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t val) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); uintptr_t ra = GETPC(); + int gm_bs = env_archcpu(env)->gm_blocksize; + int gm_bs_bytes = 4 << gm_bs; void *tag_mem; + int shift; - ptr = QEMU_ALIGN_DOWN(ptr, LDGM_STGM_SIZE); + ptr = QEMU_ALIGN_DOWN(ptr, gm_bs_bytes); /* Trap if accessing an invalid page. */ tag_mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, - LDGM_STGM_SIZE, MMU_DATA_LOAD, - LDGM_STGM_SIZE / (2 * TAG_GRANULE), ra); + gm_bs_bytes, MMU_DATA_LOAD, ra); /* * Tag store only happens if the page support tags, @@ -502,18 +526,36 @@ void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t val) return; } - QEMU_BUILD_BUG_ON(GMID_EL1_BS != 6); - /* - * We are storing 64-bits worth of tags. The ordering of elements - * within the word corresponds to a 64-bit little-endian operation. - */ - stq_le_p(tag_mem, val); + /* See LDGM for comments on BS and on shift. */ + shift = extract64(ptr, LOG2_TAG_GRANULE, 4) * 4; + val >>= shift; + switch (gm_bs) { + case 3: + /* 32 bytes -> 2 tags -> 8 result bits */ + *(uint8_t *)tag_mem = val; + break; + case 4: + /* 64 bytes -> 4 tags -> 16 result bits */ + *(uint16_t *)tag_mem = cpu_to_le16(val); + break; + case 5: + /* 128 bytes -> 8 tags -> 32 result bits */ + *(uint32_t *)tag_mem = cpu_to_le32(val); + break; + case 6: + /* 256 bytes -> 16 tags -> 64 result bits */ + *(uint64_t *)tag_mem = cpu_to_le64(val); + break; + default: + /* cpu configured with unsupported gm blocksize. */ + g_assert_not_reached(); + } } void HELPER(stzgm_tags)(CPUARMState *env, uint64_t ptr, uint64_t val) { uintptr_t ra = GETPC(); - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); int log2_dcz_bytes, log2_tag_bytes; intptr_t dcz_bytes, tag_bytes; uint8_t *mem; @@ -530,7 +572,7 @@ void HELPER(stzgm_tags)(CPUARMState *env, uint64_t ptr, uint64_t val) ptr &= -dcz_bytes; mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, dcz_bytes, - MMU_DATA_STORE, tag_bytes, ra); + MMU_DATA_STORE, ra); if (mem) { int tag_pair = (val & 0xf) * 0x11; memset(mem, tag_pair, tag_bytes); @@ -575,8 +617,8 @@ static void mte_async_check_fail(CPUARMState *env, uint64_t dirty_ptr, } /* Record a tag check failure. */ -static void mte_check_fail(CPUARMState *env, uint32_t desc, - uint64_t dirty_ptr, uintptr_t ra) +void mte_check_fail(CPUARMState *env, uint32_t desc, + uint64_t dirty_ptr, uintptr_t ra) { int mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); ARMMMUIdx arm_mmu_idx = core_to_aa64_mmu_idx(mmu_idx); @@ -692,6 +734,55 @@ static int checkN(uint8_t *mem, int odd, int cmp, int count) return n; } +/** + * checkNrev: + * @tag: tag memory to test + * @odd: true to begin testing at tags at odd nibble + * @cmp: the tag to compare against + * @count: number of tags to test + * + * Return the number of successful tests. + * Thus a return value < @count indicates a failure. + * + * This is like checkN, but it runs backwards, checking the + * tags starting with @tag and then the tags preceding it. + * This is needed by the backwards-memory-copying operations. + */ +static int checkNrev(uint8_t *mem, int odd, int cmp, int count) +{ + int n = 0, diff; + + /* Replicate the test tag and compare. */ + cmp *= 0x11; + diff = *mem-- ^ cmp; + + if (!odd) { + goto start_even; + } + + while (1) { + /* Test odd tag. */ + if (unlikely((diff) & 0xf0)) { + break; + } + if (++n == count) { + break; + } + + start_even: + /* Test even tag. */ + if (unlikely((diff) & 0x0f)) { + break; + } + if (++n == count) { + break; + } + + diff = *mem-- ^ cmp; + } + return n; +} + /** * mte_probe_int() - helper for mte_probe and mte_check * @env: CPU environment @@ -710,8 +801,7 @@ static int mte_probe_int(CPUARMState *env, uint32_t desc, uint64_t ptr, int mmu_idx, ptr_tag, bit55; uint64_t ptr_last, prev_page, next_page; uint64_t tag_first, tag_last; - uint64_t tag_byte_first, tag_byte_last; - uint32_t sizem1, tag_count, tag_size, n, c; + uint32_t sizem1, tag_count, n, c; uint8_t *mem1, *mem2; MMUAccessType type; @@ -741,19 +831,14 @@ static int mte_probe_int(CPUARMState *env, uint32_t desc, uint64_t ptr, tag_last = QEMU_ALIGN_DOWN(ptr_last, TAG_GRANULE); tag_count = ((tag_last - tag_first) / TAG_GRANULE) + 1; - /* Round the bounds to twice the tag granule, and compute the bytes. */ - tag_byte_first = QEMU_ALIGN_DOWN(ptr, 2 * TAG_GRANULE); - tag_byte_last = QEMU_ALIGN_DOWN(ptr_last, 2 * TAG_GRANULE); - /* Locate the page boundaries. */ prev_page = ptr & TARGET_PAGE_MASK; next_page = prev_page + TARGET_PAGE_SIZE; if (likely(tag_last - prev_page < TARGET_PAGE_SIZE)) { /* Memory access stays on one page. */ - tag_size = ((tag_byte_last - tag_byte_first) / (2 * TAG_GRANULE)) + 1; mem1 = allocation_tag_mem(env, mmu_idx, ptr, type, sizem1 + 1, - MMU_DATA_LOAD, tag_size, ra); + MMU_DATA_LOAD, ra); if (!mem1) { return 1; } @@ -761,14 +846,12 @@ static int mte_probe_int(CPUARMState *env, uint32_t desc, uint64_t ptr, n = checkN(mem1, ptr & TAG_GRANULE, ptr_tag, tag_count); } else { /* Memory access crosses to next page. */ - tag_size = (next_page - tag_byte_first) / (2 * TAG_GRANULE); mem1 = allocation_tag_mem(env, mmu_idx, ptr, type, next_page - ptr, - MMU_DATA_LOAD, tag_size, ra); + MMU_DATA_LOAD, ra); - tag_size = ((tag_byte_last - next_page) / (2 * TAG_GRANULE)) + 1; mem2 = allocation_tag_mem(env, mmu_idx, next_page, type, ptr_last - next_page + 1, - MMU_DATA_LOAD, tag_size, ra); + MMU_DATA_LOAD, ra); /* * Perform all of the comparisons. @@ -817,6 +900,24 @@ uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t ra) uint64_t HELPER(mte_check)(CPUARMState *env, uint32_t desc, uint64_t ptr) { + /* + * R_XCHFJ: Alignment check not caused by memory type is priority 1, + * higher than any translation fault. When MTE is disabled, tcg + * performs the alignment check during the code generated for the + * memory access. With MTE enabled, we must check this here before + * raising any translation fault in allocation_tag_mem. + */ + unsigned align = FIELD_EX32(desc, MTEDESC, ALIGN); + if (unlikely(align)) { + align = (1u << align) - 1; + if (unlikely(ptr & align)) { + int idx = FIELD_EX32(desc, MTEDESC, MIDX); + bool w = FIELD_EX32(desc, MTEDESC, WRITE); + MMUAccessType type = w ? MMU_DATA_STORE : MMU_DATA_LOAD; + arm_cpu_do_unaligned_access(env_cpu(env), ptr, type, idx, GETPC()); + } + } + return mte_check(env, desc, ptr, GETPC()); } @@ -878,7 +979,7 @@ uint64_t HELPER(mte_check_zva)(CPUARMState *env, uint32_t desc, uint64_t ptr) mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); (void) probe_write(env, ptr, 1, mmu_idx, ra); mem = allocation_tag_mem(env, mmu_idx, align_ptr, MMU_DATA_STORE, - dcz_bytes, MMU_DATA_LOAD, tag_bytes, ra); + dcz_bytes, MMU_DATA_LOAD, ra); if (!mem) { goto done; } @@ -939,3 +1040,151 @@ uint64_t HELPER(mte_check_zva)(CPUARMState *env, uint32_t desc, uint64_t ptr) done: return useronly_clean_ptr(ptr); } + +uint64_t mte_mops_probe(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t desc) +{ + int mmu_idx, tag_count; + uint64_t ptr_tag, tag_first, tag_last; + void *mem; + bool w = FIELD_EX32(desc, MTEDESC, WRITE); + uint32_t n; + + mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); + /* True probe; this will never fault */ + mem = allocation_tag_mem_probe(env, mmu_idx, ptr, + w ? MMU_DATA_STORE : MMU_DATA_LOAD, + size, MMU_DATA_LOAD, true, 0); + if (!mem) { + return size; + } + + /* + * TODO: checkN() is not designed for checks of the size we expect + * for FEAT_MOPS operations, so we should implement this differently. + * Maybe we should do something like + * if (region start and size are aligned nicely) { + * do direct loads of 64 tag bits at a time; + * } else { + * call checkN() + * } + */ + /* Round the bounds to the tag granule, and compute the number of tags. */ + ptr_tag = allocation_tag_from_addr(ptr); + tag_first = QEMU_ALIGN_DOWN(ptr, TAG_GRANULE); + tag_last = QEMU_ALIGN_DOWN(ptr + size - 1, TAG_GRANULE); + tag_count = ((tag_last - tag_first) / TAG_GRANULE) + 1; + n = checkN(mem, ptr & TAG_GRANULE, ptr_tag, tag_count); + if (likely(n == tag_count)) { + return size; + } + + /* + * Failure; for the first granule, it's at @ptr. Otherwise + * it's at the first byte of the nth granule. Calculate how + * many bytes we can access without hitting that failure. + */ + if (n == 0) { + return 0; + } else { + return n * TAG_GRANULE - (ptr - tag_first); + } +} + +uint64_t mte_mops_probe_rev(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t desc) +{ + int mmu_idx, tag_count; + uint64_t ptr_tag, tag_first, tag_last; + void *mem; + bool w = FIELD_EX32(desc, MTEDESC, WRITE); + uint32_t n; + + mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); + /* + * True probe; this will never fault. Note that our caller passes + * us a pointer to the end of the region, but allocation_tag_mem_probe() + * wants a pointer to the start. Because we know we don't span a page + * boundary and that allocation_tag_mem_probe() doesn't otherwise care + * about the size, pass in a size of 1 byte. This is simpler than + * adjusting the ptr to point to the start of the region and then having + * to adjust the returned 'mem' to get the end of the tag memory. + */ + mem = allocation_tag_mem_probe(env, mmu_idx, ptr, + w ? MMU_DATA_STORE : MMU_DATA_LOAD, + 1, MMU_DATA_LOAD, true, 0); + if (!mem) { + return size; + } + + /* + * TODO: checkNrev() is not designed for checks of the size we expect + * for FEAT_MOPS operations, so we should implement this differently. + * Maybe we should do something like + * if (region start and size are aligned nicely) { + * do direct loads of 64 tag bits at a time; + * } else { + * call checkN() + * } + */ + /* Round the bounds to the tag granule, and compute the number of tags. */ + ptr_tag = allocation_tag_from_addr(ptr); + tag_first = QEMU_ALIGN_DOWN(ptr - (size - 1), TAG_GRANULE); + tag_last = QEMU_ALIGN_DOWN(ptr, TAG_GRANULE); + tag_count = ((tag_last - tag_first) / TAG_GRANULE) + 1; + n = checkNrev(mem, ptr & TAG_GRANULE, ptr_tag, tag_count); + if (likely(n == tag_count)) { + return size; + } + + /* + * Failure; for the first granule, it's at @ptr. Otherwise + * it's at the last byte of the nth granule. Calculate how + * many bytes we can access without hitting that failure. + */ + if (n == 0) { + return 0; + } else { + return (n - 1) * TAG_GRANULE + ((ptr + 1) - tag_last); + } +} + +void mte_mops_set_tags(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t desc) +{ + int mmu_idx, tag_count; + uint64_t ptr_tag; + void *mem; + + if (!desc) { + /* Tags not actually enabled */ + return; + } + + mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); + /* True probe: this will never fault */ + mem = allocation_tag_mem_probe(env, mmu_idx, ptr, MMU_DATA_STORE, size, + MMU_DATA_STORE, true, 0); + if (!mem) { + return; + } + + /* + * We know that ptr and size are both TAG_GRANULE aligned; store + * the tag from the pointer value into the tag memory. + */ + ptr_tag = allocation_tag_from_addr(ptr); + tag_count = size / TAG_GRANULE; + if (ptr & TAG_GRANULE) { + /* Not 2*TAG_GRANULE-aligned: store tag to first nibble */ + store_tag1_parallel(TAG_GRANULE, mem, ptr_tag); + mem++; + tag_count--; + } + memset(mem, ptr_tag | (ptr_tag << 4), tag_count / 2); + if (tag_count & 1) { + /* Final trailing unaligned nibble */ + mem += tag_count / 2; + store_tag1_parallel(0, mem, ptr_tag); + } +} diff --git a/target/arm/mve.decode b/target/arm/tcg/mve.decode similarity index 100% rename from target/arm/mve.decode rename to target/arm/tcg/mve.decode diff --git a/target/arm/mve_helper.c b/target/arm/tcg/mve_helper.c similarity index 99% rename from target/arm/mve_helper.c rename to target/arm/tcg/mve_helper.c index 403b345ea3..8b99736aad 100644 --- a/target/arm/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -26,6 +26,7 @@ #include "exec/exec-all.h" #include "tcg/tcg.h" #include "fpu/softfloat.h" +#include "crypto/clmul.h" static uint16_t mve_eci_mask(CPUARMState *env) { @@ -924,8 +925,8 @@ DO_1OP_IMM(vorri, DO_ORRI) bool qc = false; \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ bool sat = false; \ - TYPE r = FN(n[H##ESIZE(e)], m[H##ESIZE(e)], &sat); \ - mergemask(&d[H##ESIZE(e)], r, mask); \ + TYPE r_ = FN(n[H##ESIZE(e)], m[H##ESIZE(e)], &sat); \ + mergemask(&d[H##ESIZE(e)], r_, mask); \ qc |= sat & mask & 1; \ } \ if (qc) { \ @@ -984,17 +985,10 @@ DO_2OP_L(vmulltuw, 1, 4, uint32_t, 8, uint64_t, DO_MUL) * Polynomial multiply. We can always do this generating 64 bits * of the result at a time, so we don't need to use DO_2OP_L. */ -#define VMULLPH_MASK 0x00ff00ff00ff00ffULL -#define VMULLPW_MASK 0x0000ffff0000ffffULL -#define DO_VMULLPBH(N, M) pmull_h((N) & VMULLPH_MASK, (M) & VMULLPH_MASK) -#define DO_VMULLPTH(N, M) DO_VMULLPBH((N) >> 8, (M) >> 8) -#define DO_VMULLPBW(N, M) pmull_w((N) & VMULLPW_MASK, (M) & VMULLPW_MASK) -#define DO_VMULLPTW(N, M) DO_VMULLPBW((N) >> 16, (M) >> 16) - -DO_2OP(vmullpbh, 8, uint64_t, DO_VMULLPBH) -DO_2OP(vmullpth, 8, uint64_t, DO_VMULLPTH) -DO_2OP(vmullpbw, 8, uint64_t, DO_VMULLPBW) -DO_2OP(vmullptw, 8, uint64_t, DO_VMULLPTW) +DO_2OP(vmullpbh, 8, uint64_t, clmul_8x4_even) +DO_2OP(vmullpth, 8, uint64_t, clmul_8x4_odd) +DO_2OP(vmullpbw, 8, uint64_t, clmul_16x2_even) +DO_2OP(vmullptw, 8, uint64_t, clmul_16x2_odd) /* * Because the computation type is at least twice as large as required, @@ -1256,11 +1250,11 @@ DO_2OP_SAT(vqsubsw, 4, int32_t, DO_SQSUB_W) #define WRAP_QRSHL_HELPER(FN, N, M, ROUND, satp) \ ({ \ uint32_t su32 = 0; \ - typeof(N) r = FN(N, (int8_t)(M), sizeof(N) * 8, ROUND, &su32); \ + typeof(N) qrshl_ret = FN(N, (int8_t)(M), sizeof(N) * 8, ROUND, &su32); \ if (su32) { \ *satp = true; \ } \ - r; \ + qrshl_ret; \ }) #define DO_SQSHL_OP(N, M, satp) \ @@ -1298,12 +1292,12 @@ DO_2OP_SAT_U(vqrshlu, DO_UQRSHL_OP) for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ bool sat = false; \ if ((e & 1) == XCHG) { \ - TYPE r = FN(n[H##ESIZE(e)], \ + TYPE vqdmladh_ret = FN(n[H##ESIZE(e)], \ m[H##ESIZE(e - XCHG)], \ n[H##ESIZE(e + (1 - 2 * XCHG))], \ m[H##ESIZE(e + (1 - XCHG))], \ ROUND, &sat); \ - mergemask(&d[H##ESIZE(e)], r, mask); \ + mergemask(&d[H##ESIZE(e)], vqdmladh_ret, mask); \ qc |= sat & mask & 1; \ } \ } \ @@ -2460,7 +2454,7 @@ static inline int64_t do_sqrshl48_d(int64_t src, int64_t shift, return extval; } } else if (shift < 48) { - int64_t extval = sextract64(src << shift, 0, 48); + extval = sextract64(src << shift, 0, 48); if (!sat || src == (extval >> shift)) { return extval; } @@ -2492,7 +2486,7 @@ static inline uint64_t do_uqrshl48_d(uint64_t src, int64_t shift, return extval; } } else if (shift < 48) { - uint64_t extval = extract64(src << shift, 0, 48); + extval = extract64(src << shift, 0, 48); if (!sat || src == (extval >> shift)) { return extval; } diff --git a/target/arm/neon-dp.decode b/target/arm/tcg/neon-dp.decode similarity index 100% rename from target/arm/neon-dp.decode rename to target/arm/tcg/neon-dp.decode diff --git a/target/arm/neon-ls.decode b/target/arm/tcg/neon-ls.decode similarity index 100% rename from target/arm/neon-ls.decode rename to target/arm/tcg/neon-ls.decode diff --git a/target/arm/neon-shared.decode b/target/arm/tcg/neon-shared.decode similarity index 100% rename from target/arm/neon-shared.decode rename to target/arm/tcg/neon-shared.decode diff --git a/target/arm/neon_helper.c b/target/arm/tcg/neon_helper.c similarity index 100% rename from target/arm/neon_helper.c rename to target/arm/tcg/neon_helper.c diff --git a/target/arm/op_helper.c b/target/arm/tcg/op_helper.c similarity index 77% rename from target/arm/op_helper.c rename to target/arm/tcg/op_helper.c index c4bd668870..c199b69fbf 100644 --- a/target/arm/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "internals.h" +#include "cpu-features.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "cpregs.h" @@ -28,6 +29,21 @@ #define SIGNBIT (uint32_t)0x80000000 #define SIGNBIT64 ((uint64_t)1 << 63) +int exception_target_el(CPUARMState *env) +{ + int target_el = MAX(1, arm_current_el(env)); + + /* + * No such thing as secure EL1 if EL3 is aarch32, + * so update the target EL to EL3 in this case. + */ + if (arm_is_secure(env) && !arm_el_is_aa64(env, 3) && target_el == 1) { + target_el = 3; + } + + return target_el; +} + void raise_exception(CPUARMState *env, uint32_t excp, uint32_t syndrome, uint32_t target_el) { @@ -63,7 +79,7 @@ void raise_exception_ra(CPUARMState *env, uint32_t excp, uint32_t syndrome, * we must restore CPU state here before setting the syndrome * the caller passed us, and cannot use cpu_loop_exit_restore(). */ - cpu_restore_state(cs, ra, true); + cpu_restore_state(cs, ra); raise_exception(env, excp, syndrome, target_el); } @@ -105,6 +121,61 @@ void HELPER(v8m_stackcheck)(CPUARMState *env, uint32_t newvalue) } } +/* Sign/zero extend */ +uint32_t HELPER(sxtb16)(uint32_t x) +{ + uint32_t res; + res = (uint16_t)(int8_t)x; + res |= (uint32_t)(int8_t)(x >> 16) << 16; + return res; +} + +static void handle_possible_div0_trap(CPUARMState *env, uintptr_t ra) +{ + /* + * Take a division-by-zero exception if necessary; otherwise return + * to get the usual non-trapping division behaviour (result of 0) + */ + if (arm_feature(env, ARM_FEATURE_M) + && (env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_DIV_0_TRP_MASK)) { + raise_exception_ra(env, EXCP_DIVBYZERO, 0, 1, ra); + } +} + +uint32_t HELPER(uxtb16)(uint32_t x) +{ + uint32_t res; + res = (uint16_t)(uint8_t)x; + res |= (uint32_t)(uint8_t)(x >> 16) << 16; + return res; +} + +int32_t HELPER(sdiv)(CPUARMState *env, int32_t num, int32_t den) +{ + if (den == 0) { + handle_possible_div0_trap(env, GETPC()); + return 0; + } + if (num == INT_MIN && den == -1) { + return INT_MIN; + } + return num / den; +} + +uint32_t HELPER(udiv)(CPUARMState *env, uint32_t num, uint32_t den) +{ + if (den == 0) { + handle_possible_div0_trap(env, GETPC()); + return 0; + } + return num / den; +} + +uint32_t HELPER(rbit)(uint32_t x) +{ + return revbit32(x); +} + uint32_t HELPER(add_setq)(CPUARMState *env, uint32_t a, uint32_t b) { uint32_t res = a + b; @@ -366,7 +437,7 @@ void HELPER(yield)(CPUARMState *env) * those EXCP values which are special cases for QEMU to interrupt * execution and not to be used for exceptions which are passed to * the guest (those must all have syndrome information and thus should - * use exception_with_syndrome). + * use exception_with_syndrome*). */ void HELPER(exception_internal)(CPUARMState *env, uint32_t excp) { @@ -378,39 +449,20 @@ void HELPER(exception_internal)(CPUARMState *env, uint32_t excp) } /* Raise an exception with the specified syndrome register value */ -void HELPER(exception_with_syndrome)(CPUARMState *env, uint32_t excp, - uint32_t syndrome, uint32_t target_el) +void HELPER(exception_with_syndrome_el)(CPUARMState *env, uint32_t excp, + uint32_t syndrome, uint32_t target_el) { raise_exception(env, excp, syndrome, target_el); } -/* Raise an EXCP_BKPT with the specified syndrome register value, - * targeting the correct exception level for debug exceptions. +/* + * Raise an exception with the specified syndrome register value + * to the default target el. */ -void HELPER(exception_bkpt_insn)(CPUARMState *env, uint32_t syndrome) +void HELPER(exception_with_syndrome)(CPUARMState *env, uint32_t excp, + uint32_t syndrome) { - int debug_el = arm_debug_target_el(env); - int cur_el = arm_current_el(env); - - /* FSR will only be used if the debug target EL is AArch32. */ - env->exception.fsr = arm_debug_exception_fsr(env); - /* FAR is UNKNOWN: clear vaddress to avoid potentially exposing - * values to the guest that it shouldn't be able to see at its - * exception/security level. - */ - env->exception.vaddress = 0; - /* - * Other kinds of architectural debug exception are ignored if - * they target an exception level below the current one (in QEMU - * this is checked by arm_generate_debug_exceptions()). Breakpoint - * instructions are special because they always generate an exception - * to somewhere: if they can't go to the configured debug exception - * level they are taken to the current exception level. - */ - if (debug_el < cur_el) { - debug_el = cur_el; - } - raise_exception(env, EXCP_BKPT, syndrome, debug_el); + raise_exception(env, excp, syndrome, exception_target_el(env)); } uint32_t HELPER(cpsr_read)(CPUARMState *env) @@ -430,9 +482,9 @@ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val) { uint32_t mask; - qemu_mutex_lock_iothread(); + bql_lock(); arm_call_pre_el_change_hook(env_archcpu(env)); - qemu_mutex_unlock_iothread(); + bql_unlock(); mask = aarch32_cpsr_valid_mask(env->features, &env_archcpu(env)->isar); cpsr_write(env, val, mask, CPSRWriteExceptionReturn); @@ -445,9 +497,9 @@ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val) env->regs[15] &= (env->thumb ? ~1 : ~3); arm_rebuild_hflags(env); - qemu_mutex_lock_iothread(); + bql_lock(); arm_call_el_change_hook(env_archcpu(env)); - qemu_mutex_unlock_iothread(); + bql_unlock(); } /* Access to user mode registers from privileged modes. */ @@ -518,10 +570,24 @@ static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode, */ int curmode = env->uncached_cpsr & CPSR_M; - if (regno == 17) { - /* ELR_Hyp: a special case because access from tgtmode is OK */ - if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) { - goto undef; + if (tgtmode == ARM_CPU_MODE_HYP) { + /* + * Handle Hyp target regs first because some are special cases + * which don't want the usual "not accessible from tgtmode" check. + */ + switch (regno) { + case 16 ... 17: /* ELR_Hyp, SPSR_Hyp */ + if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) { + goto undef; + } + break; + case 13: + if (curmode != ARM_CPU_MODE_MON) { + goto undef; + } + break; + default: + g_assert_not_reached(); } return; } @@ -552,13 +618,6 @@ static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode, } } - if (tgtmode == ARM_CPU_MODE_HYP) { - /* SPSR_Hyp, r13_hyp: accessible from Monitor mode only */ - if (curmode != ARM_CPU_MODE_MON) { - goto undef; - } - } - return; undef: @@ -573,7 +632,12 @@ void HELPER(msr_banked)(CPUARMState *env, uint32_t value, uint32_t tgtmode, switch (regno) { case 16: /* SPSRs */ - env->banked_spsr[bank_number(tgtmode)] = value; + if (tgtmode == (env->uncached_cpsr & CPSR_M)) { + /* Only happens for SPSR_Hyp access in Hyp mode */ + env->spsr = value; + } else { + env->banked_spsr[bank_number(tgtmode)] = value; + } break; case 17: /* ELR_Hyp */ env->elr_el[2] = value; @@ -607,7 +671,12 @@ uint32_t HELPER(mrs_banked)(CPUARMState *env, uint32_t tgtmode, uint32_t regno) switch (regno) { case 16: /* SPSRs */ - return env->banked_spsr[bank_number(tgtmode)]; + if (tgtmode == (env->uncached_cpsr & CPSR_M)) { + /* Only happens for SPSR_Hyp access in Hyp mode */ + return env->spsr; + } else { + return env->banked_spsr[bank_number(tgtmode)]; + } case 17: /* ELR_Hyp */ return env->elr_el[2]; case 13: @@ -628,25 +697,46 @@ uint32_t HELPER(mrs_banked)(CPUARMState *env, uint32_t tgtmode, uint32_t regno) } } -void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, - uint32_t isread) +const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, + uint32_t syndrome, uint32_t isread) { ARMCPU *cpu = env_archcpu(env); - const ARMCPRegInfo *ri = rip; + const ARMCPRegInfo *ri = get_arm_cp_reginfo(cpu->cp_regs, key); CPAccessResult res = CP_ACCESS_OK; int target_el; + assert(ri != NULL); + if (arm_feature(env, ARM_FEATURE_XSCALE) && ri->cp < 14 && extract32(env->cp15.c15_cpar, ri->cp, 1) == 0) { res = CP_ACCESS_TRAP; goto fail; } + if (ri->accessfn) { + res = ri->accessfn(env, ri, isread); + } + /* - * 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 the access function indicates a trap from EL0 to EL1 then + * that always takes priority over the HSTR_EL2 trap. (If it indicates + * a trap to EL3, then the HSTR_EL2 trap takes priority; if it indicates + * a trap to EL2, then the syndrome is the same either way so we don't + * care whether technically the architecture says that HSTR_EL2 trap or + * the other trap takes priority. So we take the "check HSTR_EL2" path + * for all of those cases.) */ - if (!is_a64(env) && arm_current_el(env) < 2 && ri->cp == 15 && + if (res != CP_ACCESS_OK && ((res & CP_ACCESS_EL_MASK) == 0) && + arm_current_el(env) == 0) { + goto fail; + } + + /* + * HSTR_EL2 traps from EL1 are checked earlier, in generated code; + * we only need to check here for traps from EL0. + */ + if (!is_a64(env) && arm_current_el(env) == 0 && ri->cp == 15 && + arm_is_el2_enabled(env) && (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { uint32_t mask = 1 << ri->crn; @@ -663,11 +753,38 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, } } - if (ri->accessfn) { - res = ri->accessfn(env, ri, isread); + /* + * Fine-grained traps also are lower priority than undef-to-EL1, + * higher priority than trap-to-EL3, and we don't care about priority + * order with other EL2 traps because the syndrome value is the same. + */ + if (arm_fgt_active(env, arm_current_el(env))) { + uint64_t trapword = 0; + unsigned int idx = FIELD_EX32(ri->fgt, FGT, IDX); + unsigned int bitpos = FIELD_EX32(ri->fgt, FGT, BITPOS); + bool rev = FIELD_EX32(ri->fgt, FGT, REV); + bool trapbit; + + if (ri->fgt & FGT_EXEC) { + assert(idx < ARRAY_SIZE(env->cp15.fgt_exec)); + trapword = env->cp15.fgt_exec[idx]; + } else if (isread && (ri->fgt & FGT_R)) { + assert(idx < ARRAY_SIZE(env->cp15.fgt_read)); + trapword = env->cp15.fgt_read[idx]; + } else if (!isread && (ri->fgt & FGT_W)) { + assert(idx < ARRAY_SIZE(env->cp15.fgt_write)); + trapword = env->cp15.fgt_write[idx]; + } + + trapbit = extract64(trapword, bitpos, 1); + if (trapbit != rev) { + res = CP_ACCESS_TRAP_EL2; + goto fail; + } } + if (likely(res == CP_ACCESS_OK)) { - return; + return ri; } fail: @@ -675,6 +792,8 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, case CP_ACCESS_TRAP: break; case CP_ACCESS_TRAP_UNCATEGORIZED: + /* Only CP_ACCESS_TRAP traps are direct to a specified EL */ + assert((res & CP_ACCESS_EL_MASK) == 0); if (cpu_isar_feature(aa64_ids, cpu) && isread && arm_cpreg_in_idspace(ri)) { /* @@ -709,28 +828,70 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, raise_exception(env, EXCP_UDEF, syndrome, target_el); } -void HELPER(set_cp_reg)(CPUARMState *env, void *rip, uint32_t value) +const void *HELPER(lookup_cp_reg)(CPUARMState *env, uint32_t key) +{ + ARMCPU *cpu = env_archcpu(env); + const ARMCPRegInfo *ri = get_arm_cp_reginfo(cpu->cp_regs, key); + + assert(ri != NULL); + return ri; +} + +/* + * Test for HCR_EL2.TIDCP at EL1. + * Since implementation defined registers are rare, and within QEMU + * most of them are no-op, do not waste HFLAGS space for this and + * always use a helper. + */ +void HELPER(tidcp_el1)(CPUARMState *env, uint32_t syndrome) +{ + if (arm_hcr_el2_eff(env) & HCR_TIDCP) { + raise_exception_ra(env, EXCP_UDEF, syndrome, 2, GETPC()); + } +} + +/* + * Similarly, for FEAT_TIDCP1 at EL0. + * We have already checked for the presence of the feature. + */ +void HELPER(tidcp_el0)(CPUARMState *env, uint32_t syndrome) +{ + /* See arm_sctlr(), but we also need the sctlr el. */ + ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, 0); + int target_el = mmu_idx == ARMMMUIdx_E20_0 ? 2 : 1; + + /* + * The bit is not valid unless the target el is aa64, but since the + * bit test is simpler perform that first and check validity after. + */ + if ((env->cp15.sctlr_el[target_el] & SCTLR_TIDCP) + && arm_el_is_aa64(env, target_el)) { + raise_exception_ra(env, EXCP_UDEF, syndrome, target_el, GETPC()); + } +} + +void HELPER(set_cp_reg)(CPUARMState *env, const void *rip, uint32_t value) { const ARMCPRegInfo *ri = rip; if (ri->type & ARM_CP_IO) { - qemu_mutex_lock_iothread(); + bql_lock(); ri->writefn(env, ri, value); - qemu_mutex_unlock_iothread(); + bql_unlock(); } else { ri->writefn(env, ri, value); } } -uint32_t HELPER(get_cp_reg)(CPUARMState *env, void *rip) +uint32_t HELPER(get_cp_reg)(CPUARMState *env, const void *rip) { const ARMCPRegInfo *ri = rip; uint32_t res; if (ri->type & ARM_CP_IO) { - qemu_mutex_lock_iothread(); + bql_lock(); res = ri->readfn(env, ri); - qemu_mutex_unlock_iothread(); + bql_unlock(); } else { res = ri->readfn(env, ri); } @@ -738,28 +899,28 @@ uint32_t HELPER(get_cp_reg)(CPUARMState *env, void *rip) return res; } -void HELPER(set_cp_reg64)(CPUARMState *env, void *rip, uint64_t value) +void HELPER(set_cp_reg64)(CPUARMState *env, const void *rip, uint64_t value) { const ARMCPRegInfo *ri = rip; if (ri->type & ARM_CP_IO) { - qemu_mutex_lock_iothread(); + bql_lock(); ri->writefn(env, ri, value); - qemu_mutex_unlock_iothread(); + bql_unlock(); } else { ri->writefn(env, ri, value); } } -uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip) +uint64_t HELPER(get_cp_reg64)(CPUARMState *env, const void *rip) { const ARMCPRegInfo *ri = rip; uint64_t res; if (ri->type & ARM_CP_IO) { - qemu_mutex_lock_iothread(); + bql_lock(); res = ri->readfn(env, ri); - qemu_mutex_unlock_iothread(); + bql_unlock(); } else { res = ri->readfn(env, ri); } @@ -841,7 +1002,14 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) * * Conduit SMC, valid call Trap to EL2 PSCI Call * Conduit SMC, inval call Trap to EL2 Undef insn - * Conduit not SMC Undef insn Undef insn + * Conduit not SMC Undef or trap[1] Undef insn + * + * [1] In this case: + * - if HCR_EL2.NV == 1 we must trap to EL2 + * - if HCR_EL2.NV == 0 then newer architecture revisions permit + * AArch64 (but not AArch32) to trap to EL2 as an IMPDEF choice + * - otherwise we must UNDEF + * We take the IMPDEF choice to always UNDEF if HCR_EL2.NV == 0. */ /* On ARMv8 with EL3 AArch64, SMD applies to both S and NS state. @@ -855,9 +1023,12 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) : smd_flag && !secure; if (!arm_feature(env, ARM_FEATURE_EL3) && + !(arm_hcr_el2_eff(env) & HCR_NV) && cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) { - /* If we have no EL3 then SMC always UNDEFs and can't be - * trapped to EL2. PSCI-via-SMC is a sort of ersatz EL3 + /* + * If we have no EL3 then traditionally SMC always UNDEFs and can't be + * trapped to EL2. For nested virtualization, SMC can be trapped to + * the outer hypervisor. PSCI-via-SMC is a sort of ersatz EL3 * firmware within QEMU, and we want an EL2 guest to be able * to forbid its EL1 from making PSCI calls into QEMU's * "firmware" via HCR.TSC, so for these purposes treat diff --git a/target/arm/pauth_helper.c b/target/arm/tcg/pauth_helper.c similarity index 73% rename from target/arm/pauth_helper.c rename to target/arm/tcg/pauth_helper.c index d0483bf051..c4b143024f 100644 --- a/target/arm/pauth_helper.c +++ b/target/arm/tcg/pauth_helper.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" +#include "cpu-features.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" @@ -96,6 +97,21 @@ static uint64_t pac_sub(uint64_t i) return o; } +static uint64_t pac_sub1(uint64_t i) +{ + static const uint8_t sub1[16] = { + 0xa, 0xd, 0xe, 0x6, 0xf, 0x7, 0x3, 0x5, + 0x9, 0x8, 0x0, 0xc, 0xb, 0x1, 0x2, 0x4, + }; + uint64_t o = 0; + int b; + + for (b = 0; b < 64; b += 4) { + o |= (uint64_t)sub1[(i >> b) & 0xf] << b; + } + return o; +} + static uint64_t pac_inv_sub(uint64_t i) { static const uint8_t inv_sub[16] = { @@ -209,7 +225,7 @@ static uint64_t tweak_inv_shuffle(uint64_t i) } static uint64_t pauth_computepac_architected(uint64_t data, uint64_t modifier, - ARMPACKey key) + ARMPACKey key, bool isqarma3) { static const uint64_t RC[5] = { 0x0000000000000000ull, @@ -219,6 +235,7 @@ static uint64_t pauth_computepac_architected(uint64_t data, uint64_t modifier, 0x452821E638D01377ull, }; const uint64_t alpha = 0xC0AC29B7C97C50DDull; + int iterations = isqarma3 ? 2 : 4; /* * Note that in the ARM pseudocode, key0 contains bits <127:64> * and key1 contains bits <63:0> of the 128-bit key. @@ -231,7 +248,7 @@ static uint64_t pauth_computepac_architected(uint64_t data, uint64_t modifier, runningmod = modifier; workingval = data ^ key0; - for (i = 0; i <= 4; ++i) { + for (i = 0; i <= iterations; ++i) { roundkey = key1 ^ runningmod; workingval ^= roundkey; workingval ^= RC[i]; @@ -239,32 +256,48 @@ static uint64_t pauth_computepac_architected(uint64_t data, uint64_t modifier, workingval = pac_cell_shuffle(workingval); workingval = pac_mult(workingval); } - workingval = pac_sub(workingval); + if (isqarma3) { + workingval = pac_sub1(workingval); + } else { + workingval = pac_sub(workingval); + } runningmod = tweak_shuffle(runningmod); } roundkey = modk0 ^ runningmod; workingval ^= roundkey; workingval = pac_cell_shuffle(workingval); workingval = pac_mult(workingval); - workingval = pac_sub(workingval); + if (isqarma3) { + workingval = pac_sub1(workingval); + } else { + workingval = pac_sub(workingval); + } workingval = pac_cell_shuffle(workingval); workingval = pac_mult(workingval); workingval ^= key1; workingval = pac_cell_inv_shuffle(workingval); - workingval = pac_inv_sub(workingval); + if (isqarma3) { + workingval = pac_sub1(workingval); + } else { + workingval = pac_inv_sub(workingval); + } workingval = pac_mult(workingval); workingval = pac_cell_inv_shuffle(workingval); workingval ^= key0; workingval ^= runningmod; - for (i = 0; i <= 4; ++i) { - workingval = pac_inv_sub(workingval); - if (i < 4) { + for (i = 0; i <= iterations; ++i) { + if (isqarma3) { + workingval = pac_sub1(workingval); + } else { + workingval = pac_inv_sub(workingval); + } + if (i < iterations) { workingval = pac_mult(workingval); workingval = pac_cell_inv_shuffle(workingval); } runningmod = tweak_inv_shuffle(runningmod); roundkey = key1 ^ runningmod; - workingval ^= RC[4 - i]; + workingval ^= RC[iterations - i]; workingval ^= roundkey; workingval ^= alpha; } @@ -282,8 +315,10 @@ static uint64_t pauth_computepac_impdef(uint64_t data, uint64_t modifier, static uint64_t pauth_computepac(CPUARMState *env, uint64_t data, uint64_t modifier, ARMPACKey key) { - if (cpu_isar_feature(aa64_pauth_arch, env_archcpu(env))) { - return pauth_computepac_architected(data, modifier, key); + if (cpu_isar_feature(aa64_pauth_qarma5, env_archcpu(env))) { + return pauth_computepac_architected(data, modifier, key, false); + } else if (cpu_isar_feature(aa64_pauth_qarma3, env_archcpu(env))) { + return pauth_computepac_architected(data, modifier, key, true); } else { return pauth_computepac_impdef(data, modifier, key); } @@ -292,8 +327,10 @@ static uint64_t pauth_computepac(CPUARMState *env, uint64_t data, static uint64_t pauth_addpac(CPUARMState *env, uint64_t ptr, uint64_t modifier, ARMPACKey *key, bool data) { + ARMCPU *cpu = env_archcpu(env); ARMMMUIdx mmu_idx = arm_stage1_mmu_idx(env); - ARMVAParameters param = aa64_va_parameters(env, ptr, mmu_idx, data); + ARMVAParameters param = aa64_va_parameters(env, ptr, mmu_idx, data, false); + ARMPauthFeature pauth_feature = cpu_isar_feature(pauth_feature, cpu); uint64_t pac, ext_ptr, ext, test; int bot_bit, top_bit; @@ -317,17 +354,26 @@ static uint64_t pauth_addpac(CPUARMState *env, uint64_t ptr, uint64_t modifier, */ test = sextract64(ptr, bot_bit, top_bit - bot_bit); if (test != 0 && test != -1) { - /* - * Note that our top_bit is one greater than the pseudocode's - * version, hence "- 2" here. - */ - pac ^= MAKE_64BIT_MASK(top_bit - 2, 1); + if (pauth_feature >= PauthFeat_2) { + /* No action required */ + } else if (pauth_feature == PauthFeat_EPAC) { + pac = 0; + } else { + /* + * Note that our top_bit is one greater than the pseudocode's + * version, hence "- 2" here. + */ + pac ^= MAKE_64BIT_MASK(top_bit - 2, 1); + } } /* * Preserve the determination between upper and lower at bit 55, * and insert pointer authentication code. */ + if (pauth_feature >= PauthFeat_2) { + pac ^= ptr; + } if (param.tbi) { ptr &= ~MAKE_64BIT_MASK(bot_bit, 55 - bot_bit + 1); pac &= MAKE_64BIT_MASK(bot_bit, 54 - bot_bit + 1); @@ -341,29 +387,56 @@ static uint64_t pauth_addpac(CPUARMState *env, uint64_t ptr, uint64_t modifier, static uint64_t pauth_original_ptr(uint64_t ptr, ARMVAParameters param) { - /* 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; + uint64_t mask = pauth_ptr_mask(param); - return deposit64(ptr, bot_pac_bit, top_pac_bit - bot_pac_bit, extfield); + /* Note that bit 55 is used whether or not the regime has 2 ranges. */ + if (extract64(ptr, 55, 1)) { + return ptr | mask; + } else { + return ptr & ~mask; + } +} + +static G_NORETURN +void pauth_fail_exception(CPUARMState *env, bool data, + int keynumber, uintptr_t ra) +{ + raise_exception_ra(env, EXCP_UDEF, syn_pacfail(data, keynumber), + exception_target_el(env), ra); } static uint64_t pauth_auth(CPUARMState *env, uint64_t ptr, uint64_t modifier, - ARMPACKey *key, bool data, int keynumber) + ARMPACKey *key, bool data, int keynumber, + uintptr_t ra, bool is_combined) { + ARMCPU *cpu = env_archcpu(env); ARMMMUIdx mmu_idx = arm_stage1_mmu_idx(env); - ARMVAParameters param = aa64_va_parameters(env, ptr, mmu_idx, data); + ARMVAParameters param = aa64_va_parameters(env, ptr, mmu_idx, data, false); + ARMPauthFeature pauth_feature = cpu_isar_feature(pauth_feature, cpu); int bot_bit, top_bit; - uint64_t pac, orig_ptr, test; + uint64_t pac, orig_ptr, cmp_mask; orig_ptr = pauth_original_ptr(ptr, param); pac = pauth_computepac(env, orig_ptr, modifier, *key); bot_bit = 64 - param.tsz; top_bit = 64 - 8 * param.tbi; - test = (pac ^ ptr) & ~MAKE_64BIT_MASK(55, 1); - if (unlikely(extract64(test, bot_bit, top_bit - bot_bit))) { + cmp_mask = MAKE_64BIT_MASK(bot_bit, top_bit - bot_bit); + cmp_mask &= ~MAKE_64BIT_MASK(55, 1); + + if (pauth_feature >= PauthFeat_2) { + ARMPauthFeature fault_feature = + is_combined ? PauthFeat_FPACCOMBINED : PauthFeat_FPAC; + uint64_t result = ptr ^ (pac & cmp_mask); + + if (pauth_feature >= fault_feature + && ((result ^ sextract64(result, 55, 1)) & cmp_mask)) { + pauth_fail_exception(env, data, keynumber, ra); + } + return result; + } + + if ((pac ^ ptr) & cmp_mask) { int error_code = (keynumber << 1) | (keynumber ^ 1); if (param.tbi) { return deposit64(orig_ptr, 53, 2, error_code); @@ -377,7 +450,7 @@ static uint64_t pauth_auth(CPUARMState *env, uint64_t ptr, uint64_t modifier, static uint64_t pauth_strip(CPUARMState *env, uint64_t ptr, bool data) { ARMMMUIdx mmu_idx = arm_stage1_mmu_idx(env); - ARMVAParameters param = aa64_va_parameters(env, ptr, mmu_idx, data); + ARMVAParameters param = aa64_va_parameters(env, ptr, mmu_idx, data, false); return pauth_original_ptr(ptr, param); } @@ -464,44 +537,88 @@ uint64_t HELPER(pacga)(CPUARMState *env, uint64_t x, uint64_t y) return pac & 0xffffffff00000000ull; } -uint64_t HELPER(autia)(CPUARMState *env, uint64_t x, uint64_t y) +static uint64_t pauth_autia(CPUARMState *env, uint64_t x, uint64_t y, + uintptr_t ra, bool is_combined) { int el = arm_current_el(env); if (!pauth_key_enabled(env, el, SCTLR_EnIA)) { return x; } - pauth_check_trap(env, el, GETPC()); - return pauth_auth(env, x, y, &env->keys.apia, false, 0); + pauth_check_trap(env, el, ra); + return pauth_auth(env, x, y, &env->keys.apia, false, 0, ra, is_combined); } -uint64_t HELPER(autib)(CPUARMState *env, uint64_t x, uint64_t y) +uint64_t HELPER(autia)(CPUARMState *env, uint64_t x, uint64_t y) +{ + return pauth_autia(env, x, y, GETPC(), false); +} + +uint64_t HELPER(autia_combined)(CPUARMState *env, uint64_t x, uint64_t y) +{ + return pauth_autia(env, x, y, GETPC(), true); +} + +static uint64_t pauth_autib(CPUARMState *env, uint64_t x, uint64_t y, + uintptr_t ra, bool is_combined) { int el = arm_current_el(env); if (!pauth_key_enabled(env, el, SCTLR_EnIB)) { return x; } - pauth_check_trap(env, el, GETPC()); - return pauth_auth(env, x, y, &env->keys.apib, false, 1); + pauth_check_trap(env, el, ra); + return pauth_auth(env, x, y, &env->keys.apib, false, 1, ra, is_combined); } -uint64_t HELPER(autda)(CPUARMState *env, uint64_t x, uint64_t y) +uint64_t HELPER(autib)(CPUARMState *env, uint64_t x, uint64_t y) +{ + return pauth_autib(env, x, y, GETPC(), false); +} + +uint64_t HELPER(autib_combined)(CPUARMState *env, uint64_t x, uint64_t y) +{ + return pauth_autib(env, x, y, GETPC(), true); +} + +static uint64_t pauth_autda(CPUARMState *env, uint64_t x, uint64_t y, + uintptr_t ra, bool is_combined) { int el = arm_current_el(env); if (!pauth_key_enabled(env, el, SCTLR_EnDA)) { return x; } - pauth_check_trap(env, el, GETPC()); - return pauth_auth(env, x, y, &env->keys.apda, true, 0); + pauth_check_trap(env, el, ra); + return pauth_auth(env, x, y, &env->keys.apda, true, 0, ra, is_combined); } -uint64_t HELPER(autdb)(CPUARMState *env, uint64_t x, uint64_t y) +uint64_t HELPER(autda)(CPUARMState *env, uint64_t x, uint64_t y) +{ + return pauth_autda(env, x, y, GETPC(), false); +} + +uint64_t HELPER(autda_combined)(CPUARMState *env, uint64_t x, uint64_t y) +{ + return pauth_autda(env, x, y, GETPC(), true); +} + +static uint64_t pauth_autdb(CPUARMState *env, uint64_t x, uint64_t y, + uintptr_t ra, bool is_combined) { int el = arm_current_el(env); if (!pauth_key_enabled(env, el, SCTLR_EnDB)) { return x; } - pauth_check_trap(env, el, GETPC()); - return pauth_auth(env, x, y, &env->keys.apdb, true, 1); + pauth_check_trap(env, el, ra); + return pauth_auth(env, x, y, &env->keys.apdb, true, 1, ra, is_combined); +} + +uint64_t HELPER(autdb)(CPUARMState *env, uint64_t x, uint64_t y) +{ + return pauth_autdb(env, x, y, GETPC(), false); +} + +uint64_t HELPER(autdb_combined)(CPUARMState *env, uint64_t x, uint64_t y) +{ + return pauth_autdb(env, x, y, GETPC(), true); } uint64_t HELPER(xpaci)(CPUARMState *env, uint64_t a) diff --git a/target/arm/psci.c b/target/arm/tcg/psci.c similarity index 98% rename from target/arm/psci.c rename to target/arm/tcg/psci.c index 6c1239bb96..51d2ca3d30 100644 --- a/target/arm/psci.c +++ b/target/arm/tcg/psci.c @@ -24,6 +24,7 @@ #include "sysemu/runstate.h" #include "internals.h" #include "arm-powerctl.h" +#include "target/arm/multiprocessing.h" bool arm_is_psci_call(ARMCPU *cpu, int excp_type) { @@ -107,7 +108,7 @@ void arm_handle_psci_call(ARMCPU *cpu) } target_cpu = ARM_CPU(target_cpu_state); - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); ret = target_cpu->power_state; break; default: @@ -215,7 +216,7 @@ err: return; cpu_off: - ret = arm_set_cpu_off(cpu->mp_affinity); + ret = arm_set_cpu_off(arm_cpu_mp_affinity(cpu)); /* notreached */ /* sanity check in case something failed */ assert(ret == QEMU_ARM_POWERCTL_RET_SUCCESS); diff --git a/target/arm/tcg/sme-fa64.decode b/target/arm/tcg/sme-fa64.decode new file mode 100644 index 0000000000..47708ccc8d --- /dev/null +++ b/target/arm/tcg/sme-fa64.decode @@ -0,0 +1,60 @@ +# AArch64 SME allowed instruction decoding +# +# Copyright (c) 2022 Linaro, Ltd +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# 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 . + +# +# This file is processed by scripts/decodetree.py +# + +# These patterns are taken from Appendix E1.1 of DDI0616 A.a, +# Arm Architecture Reference Manual Supplement, +# The Scalable Matrix Extension (SME), for Armv9-A + +{ + [ + OK 0-00 1110 0000 0001 0010 11-- ---- ---- # SMOV W|Xd,Vn.B[0] + OK 0-00 1110 0000 0010 0010 11-- ---- ---- # SMOV W|Xd,Vn.H[0] + OK 0100 1110 0000 0100 0010 11-- ---- ---- # SMOV Xd,Vn.S[0] + OK 0000 1110 0000 0001 0011 11-- ---- ---- # UMOV Wd,Vn.B[0] + OK 0000 1110 0000 0010 0011 11-- ---- ---- # UMOV Wd,Vn.H[0] + OK 0000 1110 0000 0100 0011 11-- ---- ---- # UMOV Wd,Vn.S[0] + OK 0100 1110 0000 1000 0011 11-- ---- ---- # UMOV Xd,Vn.D[0] + ] + FAIL 0--0 111- ---- ---- ---- ---- ---- ---- # Advanced SIMD vector operations +} + +{ + [ + OK 0101 1110 --1- ---- 11-1 11-- ---- ---- # FMULX/FRECPS/FRSQRTS (scalar) + OK 0101 1110 -10- ---- 00-1 11-- ---- ---- # FMULX/FRECPS/FRSQRTS (scalar, FP16) + OK 01-1 1110 1-10 0001 11-1 10-- ---- ---- # FRECPE/FRSQRTE/FRECPX (scalar) + OK 01-1 1110 1111 1001 11-1 10-- ---- ---- # FRECPE/FRSQRTE/FRECPX (scalar, FP16) + ] + FAIL 01-1 111- ---- ---- ---- ---- ---- ---- # Advanced SIMD single-element operations +} + +FAIL 0-00 110- ---- ---- ---- ---- ---- ---- # Advanced SIMD structure load/store +FAIL 1100 1110 ---- ---- ---- ---- ---- ---- # Advanced SIMD cryptography extensions +FAIL 0001 1110 0111 1110 0000 00-- ---- ---- # FJCVTZS + +# These are the "avoidance of doubt" final table of Illegal Advanced SIMD instructions +# We don't actually need to include these, as the default is OK. +# -001 111- ---- ---- ---- ---- ---- ---- # Scalar floating-point operations +# --10 110- ---- ---- ---- ---- ---- ---- # Load/store pair of FP registers +# --01 1100 ---- ---- ---- ---- ---- ---- # Load FP register (PC-relative literal) +# --11 1100 --0- ---- ---- ---- ---- ---- # Load/store FP register (unscaled imm) +# --11 1100 --1- ---- ---- ---- ---- --10 # Load/store FP register (register offset) +# --11 1101 ---- ---- ---- ---- ---- ---- # Load/store FP register (scaled imm) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode new file mode 100644 index 0000000000..628804e37a --- /dev/null +++ b/target/arm/tcg/sme.decode @@ -0,0 +1,88 @@ +# AArch64 SME instruction descriptions +# +# Copyright (c) 2022 Linaro, Ltd +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# 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 . + +# +# This file is processed by scripts/decodetree.py +# + +### SME Misc + +ZERO 11000000 00 001 00000000000 imm:8 + +### SME Move into/from Array + +%mova_rs 13:2 !function=plus_12 +&mova esz rs pg zr za_imm v:bool to_vec:bool + +MOVA 11000000 esz:2 00000 0 v:1 .. pg:3 zr:5 0 za_imm:4 \ + &mova to_vec=0 rs=%mova_rs +MOVA 11000000 11 00000 1 v:1 .. pg:3 zr:5 0 za_imm:4 \ + &mova to_vec=0 rs=%mova_rs esz=4 + +MOVA 11000000 esz:2 00001 0 v:1 .. pg:3 0 za_imm:4 zr:5 \ + &mova to_vec=1 rs=%mova_rs +MOVA 11000000 11 00001 1 v:1 .. pg:3 0 za_imm:4 zr:5 \ + &mova to_vec=1 rs=%mova_rs esz=4 + +### SME Memory + +&ldst esz rs pg rn rm za_imm v:bool st:bool + +LDST1 1110000 0 esz:2 st:1 rm:5 v:1 .. pg:3 rn:5 0 za_imm:4 \ + &ldst rs=%mova_rs +LDST1 1110000 111 st:1 rm:5 v:1 .. pg:3 rn:5 0 za_imm:4 \ + &ldst esz=4 rs=%mova_rs + +&ldstr rv rn imm +@ldstr ....... ... . ...... .. ... rn:5 . imm:4 \ + &ldstr rv=%mova_rs + +LDR 1110000 100 0 000000 .. 000 ..... 0 .... @ldstr +STR 1110000 100 1 000000 .. 000 ..... 0 .... @ldstr + +### SME Add Vector to Array + +&adda zad zn pm pn +@adda_32 ........ .. ..... . pm:3 pn:3 zn:5 ... zad:2 &adda +@adda_64 ........ .. ..... . pm:3 pn:3 zn:5 .. zad:3 &adda + +ADDHA_s 11000000 10 01000 0 ... ... ..... 000 .. @adda_32 +ADDVA_s 11000000 10 01000 1 ... ... ..... 000 .. @adda_32 +ADDHA_d 11000000 11 01000 0 ... ... ..... 00 ... @adda_64 +ADDVA_d 11000000 11 01000 1 ... ... ..... 00 ... @adda_64 + +### SME Outer Product + +&op zad zn zm pm pn sub:bool +@op_32 ........ ... zm:5 pm:3 pn:3 zn:5 sub:1 .. zad:2 &op +@op_64 ........ ... zm:5 pm:3 pn:3 zn:5 sub:1 . zad:3 &op + +FMOPA_s 10000000 100 ..... ... ... ..... . 00 .. @op_32 +FMOPA_d 10000000 110 ..... ... ... ..... . 0 ... @op_64 + +BFMOPA 10000001 100 ..... ... ... ..... . 00 .. @op_32 +FMOPA_h 10000001 101 ..... ... ... ..... . 00 .. @op_32 + +SMOPA_s 1010000 0 10 0 ..... ... ... ..... . 00 .. @op_32 +SUMOPA_s 1010000 0 10 1 ..... ... ... ..... . 00 .. @op_32 +USMOPA_s 1010000 1 10 0 ..... ... ... ..... . 00 .. @op_32 +UMOPA_s 1010000 1 10 1 ..... ... ... ..... . 00 .. @op_32 + +SMOPA_d 1010000 0 11 0 ..... ... ... ..... . 0 ... @op_64 +SUMOPA_d 1010000 0 11 1 ..... ... ... ..... . 0 ... @op_64 +USMOPA_d 1010000 1 11 0 ..... ... ... ..... . 0 ... @op_64 +UMOPA_d 1010000 1 11 1 ..... ... ... ..... . 0 ... @op_64 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c new file mode 100644 index 0000000000..e2e0575039 --- /dev/null +++ b/target/arm/tcg/sme_helper.c @@ -0,0 +1,1179 @@ +/* + * ARM SME Operations + * + * Copyright (c) 2022 Linaro, Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "cpu.h" +#include "internals.h" +#include "tcg/tcg-gvec-desc.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" +#include "exec/exec-all.h" +#include "qemu/int128.h" +#include "fpu/softfloat.h" +#include "vec_internal.h" +#include "sve_ldst_internal.h" + +void helper_set_svcr(CPUARMState *env, uint32_t val, uint32_t mask) +{ + aarch64_set_svcr(env, val, mask); +} + +void helper_sme_zero(CPUARMState *env, uint32_t imm, uint32_t svl) +{ + uint32_t i; + + /* + * Special case clearing the entire ZA space. + * This falls into the CONSTRAINED UNPREDICTABLE zeroing of any + * parts of the ZA storage outside of SVL. + */ + if (imm == 0xff) { + memset(env->zarray, 0, sizeof(env->zarray)); + return; + } + + /* + * Recall that ZAnH.D[m] is spread across ZA[n+8*m], + * so each row is discontiguous within ZA[]. + */ + for (i = 0; i < svl; i++) { + if (imm & (1 << (i % 8))) { + memset(&env->zarray[i], 0, svl); + } + } +} + + +/* + * When considering the ZA storage as an array of elements of + * type T, the index within that array of the Nth element of + * a vertical slice of a tile can be calculated like this, + * regardless of the size of type T. This is because the tiles + * are interleaved, so if type T is size N bytes then row 1 of + * the tile is N rows away from row 0. The division by N to + * convert a byte offset into an array index and the multiplication + * by N to convert from vslice-index-within-the-tile to + * the index within the ZA storage cancel out. + */ +#define tile_vslice_index(i) ((i) * sizeof(ARMVectorReg)) + +/* + * When doing byte arithmetic on the ZA storage, the element + * byteoff bytes away in a tile vertical slice is always this + * many bytes away in the ZA storage, regardless of the + * size of the tile element, assuming that byteoff is a multiple + * of the element size. Again this is because of the interleaving + * of the tiles. For instance if we have 1 byte per element then + * each row of the ZA storage has one byte of the vslice data, + * and (counting from 0) byte 8 goes in row 8 of the storage + * at offset (8 * row-size-in-bytes). + * If we have 8 bytes per element then each row of the ZA storage + * has 8 bytes of the data, but there are 8 interleaved tiles and + * so byte 8 of the data goes into row 1 of the tile, + * which is again row 8 of the storage, so the offset is still + * (8 * row-size-in-bytes). Similarly for other element sizes. + */ +#define tile_vslice_offset(byteoff) ((byteoff) * sizeof(ARMVectorReg)) + + +/* + * Move Zreg vector to ZArray column. + */ +#define DO_MOVA_C(NAME, TYPE, H) \ +void HELPER(NAME)(void *za, void *vn, void *vg, uint32_t desc) \ +{ \ + int i, oprsz = simd_oprsz(desc); \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + *(TYPE *)(za + tile_vslice_offset(i)) = *(TYPE *)(vn + H(i)); \ + } \ + i += sizeof(TYPE); \ + pg >>= sizeof(TYPE); \ + } while (i & 15); \ + } \ +} + +DO_MOVA_C(sme_mova_cz_b, uint8_t, H1) +DO_MOVA_C(sme_mova_cz_h, uint16_t, H1_2) +DO_MOVA_C(sme_mova_cz_s, uint32_t, H1_4) + +void HELPER(sme_mova_cz_d)(void *za, void *vn, void *vg, uint32_t desc) +{ + int i, oprsz = simd_oprsz(desc) / 8; + uint8_t *pg = vg; + uint64_t *n = vn; + uint64_t *a = za; + + for (i = 0; i < oprsz; i++) { + if (pg[H1(i)] & 1) { + a[tile_vslice_index(i)] = n[i]; + } + } +} + +void HELPER(sme_mova_cz_q)(void *za, void *vn, void *vg, uint32_t desc) +{ + int i, oprsz = simd_oprsz(desc) / 16; + uint16_t *pg = vg; + Int128 *n = vn; + Int128 *a = za; + + /* + * Int128 is used here simply to copy 16 bytes, and to simplify + * the address arithmetic. + */ + for (i = 0; i < oprsz; i++) { + if (pg[H2(i)] & 1) { + a[tile_vslice_index(i)] = n[i]; + } + } +} + +#undef DO_MOVA_C + +/* + * Move ZArray column to Zreg vector. + */ +#define DO_MOVA_Z(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *za, void *vg, uint32_t desc) \ +{ \ + int i, oprsz = simd_oprsz(desc); \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + *(TYPE *)(vd + H(i)) = *(TYPE *)(za + tile_vslice_offset(i)); \ + } \ + i += sizeof(TYPE); \ + pg >>= sizeof(TYPE); \ + } while (i & 15); \ + } \ +} + +DO_MOVA_Z(sme_mova_zc_b, uint8_t, H1) +DO_MOVA_Z(sme_mova_zc_h, uint16_t, H1_2) +DO_MOVA_Z(sme_mova_zc_s, uint32_t, H1_4) + +void HELPER(sme_mova_zc_d)(void *vd, void *za, void *vg, uint32_t desc) +{ + int i, oprsz = simd_oprsz(desc) / 8; + uint8_t *pg = vg; + uint64_t *d = vd; + uint64_t *a = za; + + for (i = 0; i < oprsz; i++) { + if (pg[H1(i)] & 1) { + d[i] = a[tile_vslice_index(i)]; + } + } +} + +void HELPER(sme_mova_zc_q)(void *vd, void *za, void *vg, uint32_t desc) +{ + int i, oprsz = simd_oprsz(desc) / 16; + uint16_t *pg = vg; + Int128 *d = vd; + Int128 *a = za; + + /* + * Int128 is used here simply to copy 16 bytes, and to simplify + * the address arithmetic. + */ + for (i = 0; i < oprsz; i++, za += sizeof(ARMVectorReg)) { + if (pg[H2(i)] & 1) { + d[i] = a[tile_vslice_index(i)]; + } + } +} + +#undef DO_MOVA_Z + +/* + * Clear elements in a tile slice comprising len bytes. + */ + +typedef void ClearFn(void *ptr, size_t off, size_t len); + +static void clear_horizontal(void *ptr, size_t off, size_t len) +{ + memset(ptr + off, 0, len); +} + +static void clear_vertical_b(void *vptr, size_t off, size_t len) +{ + for (size_t i = 0; i < len; ++i) { + *(uint8_t *)(vptr + tile_vslice_offset(i + off)) = 0; + } +} + +static void clear_vertical_h(void *vptr, size_t off, size_t len) +{ + for (size_t i = 0; i < len; i += 2) { + *(uint16_t *)(vptr + tile_vslice_offset(i + off)) = 0; + } +} + +static void clear_vertical_s(void *vptr, size_t off, size_t len) +{ + for (size_t i = 0; i < len; i += 4) { + *(uint32_t *)(vptr + tile_vslice_offset(i + off)) = 0; + } +} + +static void clear_vertical_d(void *vptr, size_t off, size_t len) +{ + for (size_t i = 0; i < len; i += 8) { + *(uint64_t *)(vptr + tile_vslice_offset(i + off)) = 0; + } +} + +static void clear_vertical_q(void *vptr, size_t off, size_t len) +{ + for (size_t i = 0; i < len; i += 16) { + memset(vptr + tile_vslice_offset(i + off), 0, 16); + } +} + +/* + * Copy elements from an array into a tile slice comprising len bytes. + */ + +typedef void CopyFn(void *dst, const void *src, size_t len); + +static void copy_horizontal(void *dst, const void *src, size_t len) +{ + memcpy(dst, src, len); +} + +static void copy_vertical_b(void *vdst, const void *vsrc, size_t len) +{ + const uint8_t *src = vsrc; + uint8_t *dst = vdst; + size_t i; + + for (i = 0; i < len; ++i) { + dst[tile_vslice_index(i)] = src[i]; + } +} + +static void copy_vertical_h(void *vdst, const void *vsrc, size_t len) +{ + const uint16_t *src = vsrc; + uint16_t *dst = vdst; + size_t i; + + for (i = 0; i < len / 2; ++i) { + dst[tile_vslice_index(i)] = src[i]; + } +} + +static void copy_vertical_s(void *vdst, const void *vsrc, size_t len) +{ + const uint32_t *src = vsrc; + uint32_t *dst = vdst; + size_t i; + + for (i = 0; i < len / 4; ++i) { + dst[tile_vslice_index(i)] = src[i]; + } +} + +static void copy_vertical_d(void *vdst, const void *vsrc, size_t len) +{ + const uint64_t *src = vsrc; + uint64_t *dst = vdst; + size_t i; + + for (i = 0; i < len / 8; ++i) { + dst[tile_vslice_index(i)] = src[i]; + } +} + +static void copy_vertical_q(void *vdst, const void *vsrc, size_t len) +{ + for (size_t i = 0; i < len; i += 16) { + memcpy(vdst + tile_vslice_offset(i), vsrc + i, 16); + } +} + +/* + * Host and TLB primitives for vertical tile slice addressing. + */ + +#define DO_LD(NAME, TYPE, HOST, TLB) \ +static inline void sme_##NAME##_v_host(void *za, intptr_t off, void *host) \ +{ \ + TYPE val = HOST(host); \ + *(TYPE *)(za + tile_vslice_offset(off)) = val; \ +} \ +static inline void sme_##NAME##_v_tlb(CPUARMState *env, void *za, \ + intptr_t off, target_ulong addr, uintptr_t ra) \ +{ \ + TYPE val = TLB(env, useronly_clean_ptr(addr), ra); \ + *(TYPE *)(za + tile_vslice_offset(off)) = val; \ +} + +#define DO_ST(NAME, TYPE, HOST, TLB) \ +static inline void sme_##NAME##_v_host(void *za, intptr_t off, void *host) \ +{ \ + TYPE val = *(TYPE *)(za + tile_vslice_offset(off)); \ + HOST(host, val); \ +} \ +static inline void sme_##NAME##_v_tlb(CPUARMState *env, void *za, \ + intptr_t off, target_ulong addr, uintptr_t ra) \ +{ \ + TYPE val = *(TYPE *)(za + tile_vslice_offset(off)); \ + TLB(env, useronly_clean_ptr(addr), val, ra); \ +} + +/* + * The ARMVectorReg elements are stored in host-endian 64-bit units. + * For 128-bit quantities, the sequence defined by the Elem[] pseudocode + * corresponds to storing the two 64-bit pieces in little-endian order. + */ +#define DO_LDQ(HNAME, VNAME, BE, HOST, TLB) \ +static inline void HNAME##_host(void *za, intptr_t off, void *host) \ +{ \ + uint64_t val0 = HOST(host), val1 = HOST(host + 8); \ + uint64_t *ptr = za + off; \ + ptr[0] = BE ? val1 : val0, ptr[1] = BE ? val0 : val1; \ +} \ +static inline void VNAME##_v_host(void *za, intptr_t off, void *host) \ +{ \ + HNAME##_host(za, tile_vslice_offset(off), host); \ +} \ +static inline void HNAME##_tlb(CPUARMState *env, void *za, intptr_t off, \ + target_ulong addr, uintptr_t ra) \ +{ \ + uint64_t val0 = TLB(env, useronly_clean_ptr(addr), ra); \ + uint64_t val1 = TLB(env, useronly_clean_ptr(addr + 8), ra); \ + uint64_t *ptr = za + off; \ + ptr[0] = BE ? val1 : val0, ptr[1] = BE ? val0 : val1; \ +} \ +static inline void VNAME##_v_tlb(CPUARMState *env, void *za, intptr_t off, \ + target_ulong addr, uintptr_t ra) \ +{ \ + HNAME##_tlb(env, za, tile_vslice_offset(off), addr, ra); \ +} + +#define DO_STQ(HNAME, VNAME, BE, HOST, TLB) \ +static inline void HNAME##_host(void *za, intptr_t off, void *host) \ +{ \ + uint64_t *ptr = za + off; \ + HOST(host, ptr[BE]); \ + HOST(host + 8, ptr[!BE]); \ +} \ +static inline void VNAME##_v_host(void *za, intptr_t off, void *host) \ +{ \ + HNAME##_host(za, tile_vslice_offset(off), host); \ +} \ +static inline void HNAME##_tlb(CPUARMState *env, void *za, intptr_t off, \ + target_ulong addr, uintptr_t ra) \ +{ \ + uint64_t *ptr = za + off; \ + TLB(env, useronly_clean_ptr(addr), ptr[BE], ra); \ + TLB(env, useronly_clean_ptr(addr + 8), ptr[!BE], ra); \ +} \ +static inline void VNAME##_v_tlb(CPUARMState *env, void *za, intptr_t off, \ + target_ulong addr, uintptr_t ra) \ +{ \ + HNAME##_tlb(env, za, tile_vslice_offset(off), addr, ra); \ +} + +DO_LD(ld1b, uint8_t, ldub_p, cpu_ldub_data_ra) +DO_LD(ld1h_be, uint16_t, lduw_be_p, cpu_lduw_be_data_ra) +DO_LD(ld1h_le, uint16_t, lduw_le_p, cpu_lduw_le_data_ra) +DO_LD(ld1s_be, uint32_t, ldl_be_p, cpu_ldl_be_data_ra) +DO_LD(ld1s_le, uint32_t, ldl_le_p, cpu_ldl_le_data_ra) +DO_LD(ld1d_be, uint64_t, ldq_be_p, cpu_ldq_be_data_ra) +DO_LD(ld1d_le, uint64_t, ldq_le_p, cpu_ldq_le_data_ra) + +DO_LDQ(sve_ld1qq_be, sme_ld1q_be, 1, ldq_be_p, cpu_ldq_be_data_ra) +DO_LDQ(sve_ld1qq_le, sme_ld1q_le, 0, ldq_le_p, cpu_ldq_le_data_ra) + +DO_ST(st1b, uint8_t, stb_p, cpu_stb_data_ra) +DO_ST(st1h_be, uint16_t, stw_be_p, cpu_stw_be_data_ra) +DO_ST(st1h_le, uint16_t, stw_le_p, cpu_stw_le_data_ra) +DO_ST(st1s_be, uint32_t, stl_be_p, cpu_stl_be_data_ra) +DO_ST(st1s_le, uint32_t, stl_le_p, cpu_stl_le_data_ra) +DO_ST(st1d_be, uint64_t, stq_be_p, cpu_stq_be_data_ra) +DO_ST(st1d_le, uint64_t, stq_le_p, cpu_stq_le_data_ra) + +DO_STQ(sve_st1qq_be, sme_st1q_be, 1, stq_be_p, cpu_stq_be_data_ra) +DO_STQ(sve_st1qq_le, sme_st1q_le, 0, stq_le_p, cpu_stq_le_data_ra) + +#undef DO_LD +#undef DO_ST +#undef DO_LDQ +#undef DO_STQ + +/* + * Common helper for all contiguous predicated loads. + */ + +static inline QEMU_ALWAYS_INLINE +void sme_ld1(CPUARMState *env, void *za, uint64_t *vg, + const target_ulong addr, uint32_t desc, const uintptr_t ra, + const int esz, uint32_t mtedesc, bool vertical, + sve_ldst1_host_fn *host_fn, + sve_ldst1_tlb_fn *tlb_fn, + ClearFn *clr_fn, + CopyFn *cpy_fn) +{ + const intptr_t reg_max = simd_oprsz(desc); + const intptr_t esize = 1 << esz; + intptr_t reg_off, reg_last; + SVEContLdSt info; + void *host; + int flags; + + /* Find the active elements. */ + if (!sve_cont_ldst_elements(&info, addr, vg, reg_max, esz, esize)) { + /* The entire predicate was false; no load occurs. */ + clr_fn(za, 0, reg_max); + return; + } + + /* Probe the page(s). Exit with exception for any invalid page. */ + sve_cont_ldst_pages(&info, FAULT_ALL, env, addr, MMU_DATA_LOAD, ra); + + /* Handle watchpoints for all active elements. */ + sve_cont_ldst_watchpoints(&info, env, vg, addr, esize, esize, + BP_MEM_READ, ra); + + /* + * Handle mte checks for all active elements. + * Since TBI must be set for MTE, !mtedesc => !mte_active. + */ + if (mtedesc) { + sve_cont_ldst_mte_check(&info, env, vg, addr, esize, esize, + mtedesc, ra); + } + + flags = info.page[0].flags | info.page[1].flags; + if (unlikely(flags != 0)) { +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + /* + * At least one page includes MMIO. + * Any bus operation can fail with cpu_transaction_failed, + * which for ARM will raise SyncExternal. Perform the load + * into scratch memory to preserve register state until the end. + */ + ARMVectorReg scratch = { }; + + reg_off = info.reg_off_first[0]; + reg_last = info.reg_off_last[1]; + if (reg_last < 0) { + reg_last = info.reg_off_split; + if (reg_last < 0) { + reg_last = info.reg_off_last[0]; + } + } + + do { + uint64_t pg = vg[reg_off >> 6]; + do { + if ((pg >> (reg_off & 63)) & 1) { + tlb_fn(env, &scratch, reg_off, addr + reg_off, ra); + } + reg_off += esize; + } while (reg_off & 63); + } while (reg_off <= reg_last); + + cpy_fn(za, &scratch, reg_max); + return; +#endif + } + + /* The entire operation is in RAM, on valid pages. */ + + reg_off = info.reg_off_first[0]; + reg_last = info.reg_off_last[0]; + host = info.page[0].host; + + if (!vertical) { + memset(za, 0, reg_max); + } else if (reg_off) { + clr_fn(za, 0, reg_off); + } + + while (reg_off <= reg_last) { + uint64_t pg = vg[reg_off >> 6]; + do { + if ((pg >> (reg_off & 63)) & 1) { + host_fn(za, reg_off, host + reg_off); + } else if (vertical) { + clr_fn(za, reg_off, esize); + } + reg_off += esize; + } while (reg_off <= reg_last && (reg_off & 63)); + } + + /* + * Use the slow path to manage the cross-page misalignment. + * But we know this is RAM and cannot trap. + */ + reg_off = info.reg_off_split; + if (unlikely(reg_off >= 0)) { + tlb_fn(env, za, reg_off, addr + reg_off, ra); + } + + reg_off = info.reg_off_first[1]; + if (unlikely(reg_off >= 0)) { + reg_last = info.reg_off_last[1]; + host = info.page[1].host; + + do { + uint64_t pg = vg[reg_off >> 6]; + do { + if ((pg >> (reg_off & 63)) & 1) { + host_fn(za, reg_off, host + reg_off); + } else if (vertical) { + clr_fn(za, reg_off, esize); + } + reg_off += esize; + } while (reg_off & 63); + } while (reg_off <= reg_last); + } +} + +static inline QEMU_ALWAYS_INLINE +void sme_ld1_mte(CPUARMState *env, void *za, uint64_t *vg, + target_ulong addr, uint32_t desc, uintptr_t ra, + const int esz, bool vertical, + sve_ldst1_host_fn *host_fn, + sve_ldst1_tlb_fn *tlb_fn, + ClearFn *clr_fn, + CopyFn *cpy_fn) +{ + uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + int bit55 = extract64(addr, 55, 1); + + /* Remove mtedesc from the normal sve descriptor. */ + desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + + /* Perform gross MTE suppression early. */ + if (!tbi_check(mtedesc, bit55) || + tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { + mtedesc = 0; + } + + sme_ld1(env, za, vg, addr, desc, ra, esz, mtedesc, vertical, + host_fn, tlb_fn, clr_fn, cpy_fn); +} + +#define DO_LD(L, END, ESZ) \ +void HELPER(sme_ld1##L##END##_h)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_ld1(env, za, vg, addr, desc, GETPC(), ESZ, 0, false, \ + sve_ld1##L##L##END##_host, sve_ld1##L##L##END##_tlb, \ + clear_horizontal, copy_horizontal); \ +} \ +void HELPER(sme_ld1##L##END##_v)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_ld1(env, za, vg, addr, desc, GETPC(), ESZ, 0, true, \ + sme_ld1##L##END##_v_host, sme_ld1##L##END##_v_tlb, \ + clear_vertical_##L, copy_vertical_##L); \ +} \ +void HELPER(sme_ld1##L##END##_h_mte)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_ld1_mte(env, za, vg, addr, desc, GETPC(), ESZ, false, \ + sve_ld1##L##L##END##_host, sve_ld1##L##L##END##_tlb, \ + clear_horizontal, copy_horizontal); \ +} \ +void HELPER(sme_ld1##L##END##_v_mte)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_ld1_mte(env, za, vg, addr, desc, GETPC(), ESZ, true, \ + sme_ld1##L##END##_v_host, sme_ld1##L##END##_v_tlb, \ + clear_vertical_##L, copy_vertical_##L); \ +} + +DO_LD(b, , MO_8) +DO_LD(h, _be, MO_16) +DO_LD(h, _le, MO_16) +DO_LD(s, _be, MO_32) +DO_LD(s, _le, MO_32) +DO_LD(d, _be, MO_64) +DO_LD(d, _le, MO_64) +DO_LD(q, _be, MO_128) +DO_LD(q, _le, MO_128) + +#undef DO_LD + +/* + * Common helper for all contiguous predicated stores. + */ + +static inline QEMU_ALWAYS_INLINE +void sme_st1(CPUARMState *env, void *za, uint64_t *vg, + const target_ulong addr, uint32_t desc, const uintptr_t ra, + const int esz, uint32_t mtedesc, bool vertical, + sve_ldst1_host_fn *host_fn, + sve_ldst1_tlb_fn *tlb_fn) +{ + const intptr_t reg_max = simd_oprsz(desc); + const intptr_t esize = 1 << esz; + intptr_t reg_off, reg_last; + SVEContLdSt info; + void *host; + int flags; + + /* Find the active elements. */ + if (!sve_cont_ldst_elements(&info, addr, vg, reg_max, esz, esize)) { + /* The entire predicate was false; no store occurs. */ + return; + } + + /* Probe the page(s). Exit with exception for any invalid page. */ + sve_cont_ldst_pages(&info, FAULT_ALL, env, addr, MMU_DATA_STORE, ra); + + /* Handle watchpoints for all active elements. */ + sve_cont_ldst_watchpoints(&info, env, vg, addr, esize, esize, + BP_MEM_WRITE, ra); + + /* + * Handle mte checks for all active elements. + * Since TBI must be set for MTE, !mtedesc => !mte_active. + */ + if (mtedesc) { + sve_cont_ldst_mte_check(&info, env, vg, addr, esize, esize, + mtedesc, ra); + } + + flags = info.page[0].flags | info.page[1].flags; + if (unlikely(flags != 0)) { +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + /* + * At least one page includes MMIO. + * Any bus operation can fail with cpu_transaction_failed, + * which for ARM will raise SyncExternal. We cannot avoid + * this fault and will leave with the store incomplete. + */ + reg_off = info.reg_off_first[0]; + reg_last = info.reg_off_last[1]; + if (reg_last < 0) { + reg_last = info.reg_off_split; + if (reg_last < 0) { + reg_last = info.reg_off_last[0]; + } + } + + do { + uint64_t pg = vg[reg_off >> 6]; + do { + if ((pg >> (reg_off & 63)) & 1) { + tlb_fn(env, za, reg_off, addr + reg_off, ra); + } + reg_off += esize; + } while (reg_off & 63); + } while (reg_off <= reg_last); + return; +#endif + } + + reg_off = info.reg_off_first[0]; + reg_last = info.reg_off_last[0]; + host = info.page[0].host; + + while (reg_off <= reg_last) { + uint64_t pg = vg[reg_off >> 6]; + do { + if ((pg >> (reg_off & 63)) & 1) { + host_fn(za, reg_off, host + reg_off); + } + reg_off += 1 << esz; + } while (reg_off <= reg_last && (reg_off & 63)); + } + + /* + * Use the slow path to manage the cross-page misalignment. + * But we know this is RAM and cannot trap. + */ + reg_off = info.reg_off_split; + if (unlikely(reg_off >= 0)) { + tlb_fn(env, za, reg_off, addr + reg_off, ra); + } + + reg_off = info.reg_off_first[1]; + if (unlikely(reg_off >= 0)) { + reg_last = info.reg_off_last[1]; + host = info.page[1].host; + + do { + uint64_t pg = vg[reg_off >> 6]; + do { + if ((pg >> (reg_off & 63)) & 1) { + host_fn(za, reg_off, host + reg_off); + } + reg_off += 1 << esz; + } while (reg_off & 63); + } while (reg_off <= reg_last); + } +} + +static inline QEMU_ALWAYS_INLINE +void sme_st1_mte(CPUARMState *env, void *za, uint64_t *vg, target_ulong addr, + uint32_t desc, uintptr_t ra, int esz, bool vertical, + sve_ldst1_host_fn *host_fn, + sve_ldst1_tlb_fn *tlb_fn) +{ + uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + int bit55 = extract64(addr, 55, 1); + + /* Remove mtedesc from the normal sve descriptor. */ + desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + + /* Perform gross MTE suppression early. */ + if (!tbi_check(mtedesc, bit55) || + tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { + mtedesc = 0; + } + + sme_st1(env, za, vg, addr, desc, ra, esz, mtedesc, + vertical, host_fn, tlb_fn); +} + +#define DO_ST(L, END, ESZ) \ +void HELPER(sme_st1##L##END##_h)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_st1(env, za, vg, addr, desc, GETPC(), ESZ, 0, false, \ + sve_st1##L##L##END##_host, sve_st1##L##L##END##_tlb); \ +} \ +void HELPER(sme_st1##L##END##_v)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_st1(env, za, vg, addr, desc, GETPC(), ESZ, 0, true, \ + sme_st1##L##END##_v_host, sme_st1##L##END##_v_tlb); \ +} \ +void HELPER(sme_st1##L##END##_h_mte)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_st1_mte(env, za, vg, addr, desc, GETPC(), ESZ, false, \ + sve_st1##L##L##END##_host, sve_st1##L##L##END##_tlb); \ +} \ +void HELPER(sme_st1##L##END##_v_mte)(CPUARMState *env, void *za, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sme_st1_mte(env, za, vg, addr, desc, GETPC(), ESZ, true, \ + sme_st1##L##END##_v_host, sme_st1##L##END##_v_tlb); \ +} + +DO_ST(b, , MO_8) +DO_ST(h, _be, MO_16) +DO_ST(h, _le, MO_16) +DO_ST(s, _be, MO_32) +DO_ST(s, _le, MO_32) +DO_ST(d, _be, MO_64) +DO_ST(d, _le, MO_64) +DO_ST(q, _be, MO_128) +DO_ST(q, _le, MO_128) + +#undef DO_ST + +void HELPER(sme_addha_s)(void *vzda, void *vzn, void *vpn, + void *vpm, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_oprsz(desc) / 4; + uint64_t *pn = vpn, *pm = vpm; + uint32_t *zda = vzda, *zn = vzn; + + for (row = 0; row < oprsz; ) { + uint64_t pa = pn[row >> 4]; + do { + if (pa & 1) { + for (col = 0; col < oprsz; ) { + uint64_t pb = pm[col >> 4]; + do { + if (pb & 1) { + zda[tile_vslice_index(row) + H4(col)] += zn[H4(col)]; + } + pb >>= 4; + } while (++col & 15); + } + } + pa >>= 4; + } while (++row & 15); + } +} + +void HELPER(sme_addha_d)(void *vzda, void *vzn, void *vpn, + void *vpm, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_oprsz(desc) / 8; + uint8_t *pn = vpn, *pm = vpm; + uint64_t *zda = vzda, *zn = vzn; + + for (row = 0; row < oprsz; ++row) { + if (pn[H1(row)] & 1) { + for (col = 0; col < oprsz; ++col) { + if (pm[H1(col)] & 1) { + zda[tile_vslice_index(row) + col] += zn[col]; + } + } + } + } +} + +void HELPER(sme_addva_s)(void *vzda, void *vzn, void *vpn, + void *vpm, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_oprsz(desc) / 4; + uint64_t *pn = vpn, *pm = vpm; + uint32_t *zda = vzda, *zn = vzn; + + for (row = 0; row < oprsz; ) { + uint64_t pa = pn[row >> 4]; + do { + if (pa & 1) { + uint32_t zn_row = zn[H4(row)]; + for (col = 0; col < oprsz; ) { + uint64_t pb = pm[col >> 4]; + do { + if (pb & 1) { + zda[tile_vslice_index(row) + H4(col)] += zn_row; + } + pb >>= 4; + } while (++col & 15); + } + } + pa >>= 4; + } while (++row & 15); + } +} + +void HELPER(sme_addva_d)(void *vzda, void *vzn, void *vpn, + void *vpm, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_oprsz(desc) / 8; + uint8_t *pn = vpn, *pm = vpm; + uint64_t *zda = vzda, *zn = vzn; + + for (row = 0; row < oprsz; ++row) { + if (pn[H1(row)] & 1) { + uint64_t zn_row = zn[row]; + for (col = 0; col < oprsz; ++col) { + if (pm[H1(col)] & 1) { + zda[tile_vslice_index(row) + col] += zn_row; + } + } + } + } +} + +void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, void *vst, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_maxsz(desc); + uint32_t neg = simd_data(desc) << 31; + uint16_t *pn = vpn, *pm = vpm; + float_status fpst; + + /* + * Make a copy of float_status because this operation does not + * update the cumulative fp exception status. It also produces + * default nans. + */ + fpst = *(float_status *)vst; + set_default_nan_mode(true, &fpst); + + for (row = 0; row < oprsz; ) { + uint16_t pa = pn[H2(row >> 4)]; + do { + if (pa & 1) { + void *vza_row = vza + tile_vslice_offset(row); + uint32_t n = *(uint32_t *)(vzn + H1_4(row)) ^ neg; + + for (col = 0; col < oprsz; ) { + uint16_t pb = pm[H2(col >> 4)]; + do { + if (pb & 1) { + uint32_t *a = vza_row + H1_4(col); + uint32_t *m = vzm + H1_4(col); + *a = float32_muladd(n, *m, *a, 0, vst); + } + col += 4; + pb >>= 4; + } while (col & 15); + } + } + row += 4; + pa >>= 4; + } while (row & 15); + } +} + +void HELPER(sme_fmopa_d)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, void *vst, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_oprsz(desc) / 8; + uint64_t neg = (uint64_t)simd_data(desc) << 63; + uint64_t *za = vza, *zn = vzn, *zm = vzm; + uint8_t *pn = vpn, *pm = vpm; + float_status fpst = *(float_status *)vst; + + set_default_nan_mode(true, &fpst); + + for (row = 0; row < oprsz; ++row) { + if (pn[H1(row)] & 1) { + uint64_t *za_row = &za[tile_vslice_index(row)]; + uint64_t n = zn[row] ^ neg; + + for (col = 0; col < oprsz; ++col) { + if (pm[H1(col)] & 1) { + uint64_t *a = &za_row[col]; + *a = float64_muladd(n, zm[col], *a, 0, &fpst); + } + } + } + } +} + +/* + * Alter PAIR as needed for controlling predicates being false, + * and for NEG on an enabled row element. + */ +static inline uint32_t f16mop_adj_pair(uint32_t pair, uint32_t pg, uint32_t neg) +{ + /* + * The pseudocode uses a conditional negate after the conditional zero. + * It is simpler here to unconditionally negate before conditional zero. + */ + pair ^= neg; + if (!(pg & 1)) { + pair &= 0xffff0000u; + } + if (!(pg & 4)) { + pair &= 0x0000ffffu; + } + return pair; +} + +static float32 f16_dotadd(float32 sum, uint32_t e1, uint32_t e2, + float_status *s_std, float_status *s_odd) +{ + float64 e1r = float16_to_float64(e1 & 0xffff, true, s_std); + float64 e1c = float16_to_float64(e1 >> 16, true, s_std); + float64 e2r = float16_to_float64(e2 & 0xffff, true, s_std); + float64 e2c = float16_to_float64(e2 >> 16, true, s_std); + float64 t64; + float32 t32; + + /* + * The ARM pseudocode function FPDot performs both multiplies + * and the add with a single rounding operation. Emulate this + * by performing the first multiply in round-to-odd, then doing + * the second multiply as fused multiply-add, and rounding to + * float32 all in one step. + */ + t64 = float64_mul(e1r, e2r, s_odd); + t64 = float64r32_muladd(e1c, e2c, t64, 0, s_std); + + /* This conversion is exact, because we've already rounded. */ + t32 = float64_to_float32(t64, s_std); + + /* The final accumulation step is not fused. */ + return float32_add(sum, t32, s_std); +} + +void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, void *vst, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_maxsz(desc); + uint32_t neg = simd_data(desc) * 0x80008000u; + uint16_t *pn = vpn, *pm = vpm; + float_status fpst_odd, fpst_std; + + /* + * Make a copy of float_status because this operation does not + * update the cumulative fp exception status. It also produces + * default nans. Make a second copy with round-to-odd -- see above. + */ + fpst_std = *(float_status *)vst; + set_default_nan_mode(true, &fpst_std); + fpst_odd = fpst_std; + set_float_rounding_mode(float_round_to_odd, &fpst_odd); + + for (row = 0; row < oprsz; ) { + uint16_t prow = pn[H2(row >> 4)]; + do { + void *vza_row = vza + tile_vslice_offset(row); + uint32_t n = *(uint32_t *)(vzn + H1_4(row)); + + n = f16mop_adj_pair(n, prow, neg); + + for (col = 0; col < oprsz; ) { + uint16_t pcol = pm[H2(col >> 4)]; + do { + if (prow & pcol & 0b0101) { + uint32_t *a = vza_row + H1_4(col); + uint32_t m = *(uint32_t *)(vzm + H1_4(col)); + + m = f16mop_adj_pair(m, pcol, 0); + *a = f16_dotadd(*a, n, m, &fpst_std, &fpst_odd); + } + col += 4; + pcol >>= 4; + } while (col & 15); + } + row += 4; + prow >>= 4; + } while (row & 15); + } +} + +void HELPER(sme_bfmopa)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, uint32_t desc) +{ + intptr_t row, col, oprsz = simd_maxsz(desc); + uint32_t neg = simd_data(desc) * 0x80008000u; + uint16_t *pn = vpn, *pm = vpm; + + for (row = 0; row < oprsz; ) { + uint16_t prow = pn[H2(row >> 4)]; + do { + void *vza_row = vza + tile_vslice_offset(row); + uint32_t n = *(uint32_t *)(vzn + H1_4(row)); + + n = f16mop_adj_pair(n, prow, neg); + + for (col = 0; col < oprsz; ) { + uint16_t pcol = pm[H2(col >> 4)]; + do { + if (prow & pcol & 0b0101) { + uint32_t *a = vza_row + H1_4(col); + uint32_t m = *(uint32_t *)(vzm + H1_4(col)); + + m = f16mop_adj_pair(m, pcol, 0); + *a = bfdotadd(*a, n, m); + } + col += 4; + pcol >>= 4; + } while (col & 15); + } + row += 4; + prow >>= 4; + } while (row & 15); + } +} + +typedef uint32_t IMOPFn32(uint32_t, uint32_t, uint32_t, uint8_t, bool); +static inline void do_imopa_s(uint32_t *za, uint32_t *zn, uint32_t *zm, + uint8_t *pn, uint8_t *pm, + uint32_t desc, IMOPFn32 *fn) +{ + intptr_t row, col, oprsz = simd_oprsz(desc) / 4; + bool neg = simd_data(desc); + + for (row = 0; row < oprsz; ++row) { + uint8_t pa = (pn[H1(row >> 1)] >> ((row & 1) * 4)) & 0xf; + uint32_t *za_row = &za[tile_vslice_index(row)]; + uint32_t n = zn[H4(row)]; + + for (col = 0; col < oprsz; ++col) { + uint8_t pb = pm[H1(col >> 1)] >> ((col & 1) * 4); + uint32_t *a = &za_row[H4(col)]; + + *a = fn(n, zm[H4(col)], *a, pa & pb, neg); + } + } +} + +typedef uint64_t IMOPFn64(uint64_t, uint64_t, uint64_t, uint8_t, bool); +static inline void do_imopa_d(uint64_t *za, uint64_t *zn, uint64_t *zm, + uint8_t *pn, uint8_t *pm, + uint32_t desc, IMOPFn64 *fn) +{ + intptr_t row, col, oprsz = simd_oprsz(desc) / 8; + bool neg = simd_data(desc); + + for (row = 0; row < oprsz; ++row) { + uint8_t pa = pn[H1(row)]; + uint64_t *za_row = &za[tile_vslice_index(row)]; + uint64_t n = zn[row]; + + for (col = 0; col < oprsz; ++col) { + uint8_t pb = pm[H1(col)]; + uint64_t *a = &za_row[col]; + + *a = fn(n, zm[col], *a, pa & pb, neg); + } + } +} + +#define DEF_IMOP_32(NAME, NTYPE, MTYPE) \ +static uint32_t NAME(uint32_t n, uint32_t m, uint32_t a, uint8_t p, bool neg) \ +{ \ + uint32_t sum = 0; \ + /* Apply P to N as a mask, making the inactive elements 0. */ \ + n &= expand_pred_b(p); \ + sum += (NTYPE)(n >> 0) * (MTYPE)(m >> 0); \ + sum += (NTYPE)(n >> 8) * (MTYPE)(m >> 8); \ + sum += (NTYPE)(n >> 16) * (MTYPE)(m >> 16); \ + sum += (NTYPE)(n >> 24) * (MTYPE)(m >> 24); \ + return neg ? a - sum : a + sum; \ +} + +#define DEF_IMOP_64(NAME, NTYPE, MTYPE) \ +static uint64_t NAME(uint64_t n, uint64_t m, uint64_t a, uint8_t p, bool neg) \ +{ \ + uint64_t sum = 0; \ + /* Apply P to N as a mask, making the inactive elements 0. */ \ + n &= expand_pred_h(p); \ + sum += (NTYPE)(n >> 0) * (MTYPE)(m >> 0); \ + sum += (NTYPE)(n >> 16) * (MTYPE)(m >> 16); \ + sum += (NTYPE)(n >> 32) * (MTYPE)(m >> 32); \ + sum += (NTYPE)(n >> 48) * (MTYPE)(m >> 48); \ + return neg ? a - sum : a + sum; \ +} + +DEF_IMOP_32(smopa_s, int8_t, int8_t) +DEF_IMOP_32(umopa_s, uint8_t, uint8_t) +DEF_IMOP_32(sumopa_s, int8_t, uint8_t) +DEF_IMOP_32(usmopa_s, uint8_t, int8_t) + +DEF_IMOP_64(smopa_d, int16_t, int16_t) +DEF_IMOP_64(umopa_d, uint16_t, uint16_t) +DEF_IMOP_64(sumopa_d, int16_t, uint16_t) +DEF_IMOP_64(usmopa_d, uint16_t, int16_t) + +#define DEF_IMOPH(NAME, S) \ + void HELPER(sme_##NAME##_##S)(void *vza, void *vzn, void *vzm, \ + void *vpn, void *vpm, uint32_t desc) \ + { do_imopa_##S(vza, vzn, vzm, vpn, vpm, desc, NAME##_##S); } + +DEF_IMOPH(smopa, s) +DEF_IMOPH(umopa, s) +DEF_IMOPH(sumopa, s) +DEF_IMOPH(usmopa, s) + +DEF_IMOPH(smopa, d) +DEF_IMOPH(umopa, d) +DEF_IMOPH(sumopa, d) +DEF_IMOPH(usmopa, d) diff --git a/target/arm/sve.decode b/target/arm/tcg/sve.decode similarity index 97% rename from target/arm/sve.decode rename to target/arm/tcg/sve.decode index a54feb2f61..04b6fcc0cf 100644 --- a/target/arm/sve.decode +++ b/target/arm/tcg/sve.decode @@ -449,14 +449,17 @@ INDEX_ri 00000100 esz:2 1 imm:s5 010001 rn:5 rd:5 # SVE index generation (register start, register increment) INDEX_rr 00000100 .. 1 ..... 010011 ..... ..... @rd_rn_rm -### SVE Stack Allocation Group +### SVE / Streaming SVE Stack Allocation Group # SVE stack frame adjustment ADDVL 00000100 001 ..... 01010 ...... ..... @rd_rn_i6 +ADDSVL 00000100 001 ..... 01011 ...... ..... @rd_rn_i6 ADDPL 00000100 011 ..... 01010 ...... ..... @rd_rn_i6 +ADDSPL 00000100 011 ..... 01011 ...... ..... @rd_rn_i6 # SVE stack frame size RDVL 00000100 101 11111 01010 imm:s6 rd:5 +RDSVL 00000100 101 11111 01011 imm:s6 rd:5 ### SVE Bitwise Shift - Unpredicated Group @@ -649,6 +652,7 @@ REVB 00000101 .. 1001 00 100 ... ..... ..... @rd_pg_rn REVH 00000101 .. 1001 01 100 ... ..... ..... @rd_pg_rn REVW 00000101 .. 1001 10 100 ... ..... ..... @rd_pg_rn RBIT 00000101 .. 1001 11 100 ... ..... ..... @rd_pg_rn +REVD 00000101 00 1011 10 100 ... ..... ..... @rd_pg_rn_e0 # SVE vector splice (predicated, destructive) SPLICE 00000101 .. 101 100 100 ... ..... ..... @rdn_pg_rm @@ -1183,10 +1187,10 @@ LD1RO_zpri 1010010 .. 01 0.... 001 ... ..... ..... \ @rpri_load_msz nreg=0 # SVE 32-bit gather prefetch (scalar plus 32-bit scaled offsets) -PRF 1000010 00 -1 ----- 0-- --- ----- 0 ---- +PRF_ns 1000010 00 -1 ----- 0-- --- ----- 0 ---- # SVE 32-bit gather prefetch (vector plus immediate) -PRF 1000010 -- 00 ----- 111 --- ----- 0 ---- +PRF_ns 1000010 -- 00 ----- 111 --- ----- 0 ---- # SVE contiguous prefetch (scalar plus immediate) PRF 1000010 11 1- ----- 0-- --- ----- 0 ---- @@ -1223,13 +1227,13 @@ LD1_zpiz 1100010 .. 01 ..... 1.. ... ..... ..... \ @rpri_g_load esz=3 # SVE 64-bit gather prefetch (scalar plus 64-bit scaled offsets) -PRF 1100010 00 11 ----- 1-- --- ----- 0 ---- +PRF_ns 1100010 00 11 ----- 1-- --- ----- 0 ---- # SVE 64-bit gather prefetch (scalar plus unpacked 32-bit scaled offsets) -PRF 1100010 00 -1 ----- 0-- --- ----- 0 ---- +PRF_ns 1100010 00 -1 ----- 0-- --- ----- 0 ---- # SVE 64-bit gather prefetch (vector plus immediate) -PRF 1100010 -- 00 ----- 111 --- ----- 0 ---- +PRF_ns 1100010 -- 00 ----- 111 --- ----- 0 ---- ### SVE Memory Store Group @@ -1625,8 +1629,8 @@ STNT1_zprz 1110010 .. 10 ..... 001 ... ..... ..... \ ### SVE2 Crypto Extensions # SVE2 crypto unary operations -# AESMC and AESIMC -AESMC 01000101 00 10000011100 decrypt:1 00000 rd:5 +AESMC 01000101 00 10000011100 0 00000 rd:5 +AESIMC 01000101 00 10000011100 1 00000 rd:5 # SVE2 crypto destructive binary operations AESE 01000101 00 10001 0 11100 0 ..... ..... @rdn_rm_e0 @@ -1671,3 +1675,28 @@ BFMLALT_zzxw 01100100 11 1 ..... 0100.1 ..... ..... @rrxr_3a esz=2 ### SVE2 floating-point bfloat16 dot-product (indexed) BFDOT_zzxz 01100100 01 1 ..... 010000 ..... ..... @rrxr_2 esz=2 + +### SVE broadcast predicate element + +&psel esz pd pn pm rv imm +%psel_rv 16:2 !function=plus_12 +%psel_imm_b 22:2 19:2 +%psel_imm_h 22:2 20:1 +%psel_imm_s 22:2 +%psel_imm_d 23:1 +@psel ........ .. . ... .. .. pn:4 . pm:4 . pd:4 \ + &psel rv=%psel_rv + +PSEL 00100101 .. 1 ..1 .. 01 .... 0 .... 0 .... \ + @psel esz=0 imm=%psel_imm_b +PSEL 00100101 .. 1 .10 .. 01 .... 0 .... 0 .... \ + @psel esz=1 imm=%psel_imm_h +PSEL 00100101 .. 1 100 .. 01 .... 0 .... 0 .... \ + @psel esz=2 imm=%psel_imm_s +PSEL 00100101 .1 1 000 .. 01 .... 0 .... 0 .... \ + @psel esz=3 imm=%psel_imm_d + +### SVE clamp + +SCLAMP 01000100 .. 0 ..... 110000 ..... ..... @rda_rn_rm +UCLAMP 01000100 .. 0 ..... 110001 ..... ..... @rda_rn_rm diff --git a/target/arm/sve_helper.c b/target/arm/tcg/sve_helper.c similarity index 99% rename from target/arm/sve_helper.c rename to target/arm/tcg/sve_helper.c index 1654c0bbf9..6853f58c19 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -27,6 +27,7 @@ #include "tcg/tcg.h" #include "vec_internal.h" #include "sve_ldst_internal.h" +#include "hw/core/tcg-cpu-ops.h" /* Return a value for NZCV as per the ARM PredTest pseudofunction. @@ -931,6 +932,22 @@ DO_ZPZ_D(sve_revh_d, uint64_t, hswap64) DO_ZPZ_D(sve_revw_d, uint64_t, wswap64) +void HELPER(sme_revd_q)(void *vd, void *vn, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i += 2) { + if (pg[H1(i)] & 1) { + uint64_t n0 = n[i + 0]; + uint64_t n1 = n[i + 1]; + d[i + 0] = n1; + d[i + 1] = n0; + } + } +} + DO_ZPZ(sve_rbit_b, uint8_t, H1, revbit8) DO_ZPZ(sve_rbit_h, uint16_t, H1_2, revbit16) DO_ZPZ(sve_rbit_s, uint32_t, H1_4, revbit32) @@ -3350,10 +3367,10 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ /* We produce output faster than we consume input. \ Therefore we must be mindful of possible overlap. */ \ if (unlikely((vn - vd) < (uintptr_t)oprsz)) { \ - vn = memcpy(&tmp_n, vn, oprsz_2); \ + vn = memcpy(&tmp_n, vn, oprsz); \ } \ if (unlikely((vm - vd) < (uintptr_t)oprsz)) { \ - vm = memcpy(&tmp_m, vm, oprsz_2); \ + vm = memcpy(&tmp_m, vm, oprsz); \ } \ for (i = 0; i < oprsz_2; i += sizeof(TYPE)) { \ *(TYPE *)(vd + H(2 * i + 0)) = *(TYPE *)(vn + odd_ofs + H(i)); \ @@ -3565,6 +3582,18 @@ void HELPER(sve_sel_zpzz_d)(void *vd, void *vn, void *vm, } } +void HELPER(sve_sel_zpzz_q)(void *vd, void *vn, void *vm, + void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 16; + Int128 *d = vd, *n = vn, *m = vm; + uint16_t *pg = vg; + + for (i = 0; i < opr_sz; i += 1) { + d[i] = (pg[H2(i)] & 1 ? n : m)[i]; + } +} + /* Two operand comparison controlled by a predicate. * ??? It is very tempting to want to be able to expand this inline * with x86 instructions, e.g. @@ -5323,8 +5352,14 @@ bool sve_probe_page(SVEHostPage *info, bool nofault, CPUARMState *env, */ addr = useronly_clean_ptr(addr); - flags = probe_access_flags(env, addr, access_type, mmu_idx, nofault, +#ifdef CONFIG_USER_ONLY + flags = probe_access_flags(env, addr, 0, access_type, mmu_idx, nofault, &info->host, retaddr); +#else + CPUTLBEntryFull *full; + flags = probe_access_full(env, addr, 0, access_type, mmu_idx, nofault, + &info->host, &full, retaddr); +#endif info->flags = flags; if (flags & TLB_INVALID_MASK) { @@ -5332,32 +5367,17 @@ bool sve_probe_page(SVEHostPage *info, bool nofault, CPUARMState *env, return false; } - /* Ensure that info->host[] is relative to addr, not addr + mem_off. */ - info->host -= mem_off; - #ifdef CONFIG_USER_ONLY memset(&info->attrs, 0, sizeof(info->attrs)); + /* Require both ANON and MTE; see allocation_tag_mem(). */ + info->tagged = (flags & PAGE_ANON) && (flags & PAGE_MTE); #else - /* - * Find the iotlbentry for addr and return the transaction attributes. - * This *must* be present in the TLB because we just found the mapping. - */ - { - uintptr_t index = tlb_index(env, mmu_idx, addr); - -# ifdef CONFIG_DEBUG_TCG - CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); - target_ulong comparator = (access_type == MMU_DATA_LOAD - ? entry->addr_read - : tlb_addr_write(entry)); - g_assert(tlb_hit(comparator, addr)); -# endif - - CPUIOTLBEntry *iotlbentry = &env_tlb(env)->d[mmu_idx].iotlb[index]; - info->attrs = iotlbentry->attrs; - } + info->attrs = full->attrs; + info->tagged = full->extra.arm.pte_attrs == 0xf0; #endif + /* Ensure that info->host[] is relative to addr, not addr + mem_off. */ + info->host -= mem_off; return true; } @@ -5461,7 +5481,7 @@ bool sve_cont_ldst_pages(SVEContLdSt *info, SVEContFault fault, CPUARMState *env, target_ulong addr, MMUAccessType access_type, uintptr_t retaddr) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); int mem_off = info->mem_off_first[0]; bool nofault = fault == FAULT_NO; bool have_work = true; @@ -5586,7 +5606,7 @@ void sve_cont_ldst_mte_check(SVEContLdSt *info, CPUARMState *env, intptr_t mem_off, reg_off, reg_last; /* Process the page only if MemAttr == Tagged. */ - if (arm_tlb_mte_tagged(&info->page[0].attrs)) { + if (info->page[0].tagged) { mem_off = info->mem_off_first[0]; reg_off = info->reg_off_first[0]; reg_last = info->reg_off_split; @@ -5607,7 +5627,7 @@ void sve_cont_ldst_mte_check(SVEContLdSt *info, CPUARMState *env, } mem_off = info->mem_off_first[1]; - if (mem_off >= 0 && arm_tlb_mte_tagged(&info->page[1].attrs)) { + if (mem_off >= 0 && info->page[1].tagged) { reg_off = info->reg_off_first[1]; reg_last = info->reg_off_last[1]; @@ -5668,9 +5688,6 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr, flags = info.page[0].flags | info.page[1].flags; if (unlikely(flags != 0)) { -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else /* * At least one page includes MMIO. * Any bus operation can fail with cpu_transaction_failed, @@ -5707,7 +5724,6 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr, memcpy(&env->vfp.zregs[(rd + i) & 31], &scratch[i], reg_max); } return; -#endif } /* The entire operation is in RAM, on valid pages. */ @@ -5784,8 +5800,8 @@ void sve_ldN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr, desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); /* Perform gross MTE suppression early. */ - if (!tbi_check(desc, bit55) || - tcma_check(desc, bit55, allocation_tag_from_addr(addr))) { + if (!tbi_check(mtedesc, bit55) || + tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { mtedesc = 0; } @@ -5986,7 +6002,7 @@ void sve_ldnfff1_r(CPUARMState *env, void *vg, const target_ulong addr, * Disable MTE checking if the Tagged bit is not set. Since TBI must * be set within MTEDESC for MTE, !mtedesc => !mte_active. */ - if (arm_tlb_mte_tagged(&info.page[0].attrs)) { + if (!info.page[0].tagged) { mtedesc = 0; } @@ -6140,8 +6156,8 @@ void sve_ldnfff1_r_mte(CPUARMState *env, void *vg, target_ulong addr, desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); /* Perform gross MTE suppression early. */ - if (!tbi_check(desc, bit55) || - tcma_check(desc, bit55, allocation_tag_from_addr(addr))) { + if (!tbi_check(mtedesc, bit55) || + tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { mtedesc = 0; } @@ -6394,8 +6410,8 @@ void sve_stN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr, desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); /* Perform gross MTE suppression early. */ - if (!tbi_check(desc, bit55) || - tcma_check(desc, bit55, allocation_tag_from_addr(addr))) { + if (!tbi_check(mtedesc, bit55) || + tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { mtedesc = 0; } @@ -6513,7 +6529,7 @@ void sve_ld1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = arm_env_mmu_index(env); const intptr_t reg_max = simd_oprsz(desc); const int scale = simd_data(desc); ARMVectorReg scratch; @@ -6537,7 +6553,7 @@ void sve_ld1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, cpu_check_watchpoint(env_cpu(env), addr, msize, info.attrs, BP_MEM_READ, retaddr); } - if (mtedesc && arm_tlb_mte_tagged(&info.attrs)) { + if (mtedesc && info.tagged) { mte_check(env, mtedesc, addr, retaddr); } if (unlikely(info.flags & TLB_MMIO)) { @@ -6554,7 +6570,7 @@ void sve_ld1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, msize, info.attrs, BP_MEM_READ, retaddr); } - if (mtedesc && arm_tlb_mte_tagged(&info.attrs)) { + if (mtedesc && info.tagged) { mte_check(env, mtedesc, addr, retaddr); } tlb_fn(env, &scratch, reg_off, addr, retaddr); @@ -6699,7 +6715,7 @@ void sve_ldff1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = arm_env_mmu_index(env); const intptr_t reg_max = simd_oprsz(desc); const int scale = simd_data(desc); const int esize = 1 << esz; @@ -6707,6 +6723,7 @@ void sve_ldff1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, intptr_t reg_off; SVEHostPage info; target_ulong addr, in_page; + ARMVectorReg scratch; /* Skip to the first true predicate. */ reg_off = find_next_active(vg, 0, reg_max, esz); @@ -6716,6 +6733,11 @@ void sve_ldff1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, return; } + /* Protect against overlap between vd and vm. */ + if (unlikely(vd == vm)) { + vm = memcpy(&scratch, vm, reg_max); + } + /* * Probe the first element, allowing faults. */ @@ -6755,9 +6777,7 @@ void sve_ldff1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, (env_cpu(env), addr, msize) & BP_MEM_READ)) { goto fault; } - if (mtedesc && - arm_tlb_mte_tagged(&info.attrs) && - !mte_probe(env, mtedesc, addr)) { + if (mtedesc && info.tagged && !mte_probe(env, mtedesc, addr)) { goto fault; } @@ -6900,7 +6920,7 @@ void sve_st1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = arm_env_mmu_index(env); const intptr_t reg_max = simd_oprsz(desc); const int scale = simd_data(desc); void *host[ARM_MAX_VQ * 4]; @@ -6943,7 +6963,7 @@ void sve_st1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, info.attrs, BP_MEM_WRITE, retaddr); } - if (mtedesc && arm_tlb_mte_tagged(&info.attrs)) { + if (mtedesc && info.tagged) { mte_check(env, mtedesc, addr, retaddr); } } diff --git a/target/arm/sve_ldst_internal.h b/target/arm/tcg/sve_ldst_internal.h similarity index 99% rename from target/arm/sve_ldst_internal.h rename to target/arm/tcg/sve_ldst_internal.h index b5c473fc48..4f159ec4ad 100644 --- a/target/arm/sve_ldst_internal.h +++ b/target/arm/tcg/sve_ldst_internal.h @@ -134,6 +134,7 @@ typedef struct { void *host; int flags; MemTxAttrs attrs; + bool tagged; } SVEHostPage; bool sve_probe_page(SVEHostPage *info, bool nofault, CPUARMState *env, diff --git a/target/arm/t16.decode b/target/arm/tcg/t16.decode similarity index 100% rename from target/arm/t16.decode rename to target/arm/tcg/t16.decode diff --git a/target/arm/t32.decode b/target/arm/tcg/t32.decode similarity index 100% rename from target/arm/t32.decode rename to target/arm/tcg/t32.decode diff --git a/target/arm/tlb_helper.c b/target/arm/tcg/tlb_helper.c similarity index 54% rename from target/arm/tlb_helper.c rename to target/arm/tcg/tlb_helper.c index 7d8a86b3c4..885bf4ec14 100644 --- a/target/arm/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -8,24 +8,11 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" +#include "cpu-features.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" -/* Return true if the translation regime is using LPAE format page tables */ -bool regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) -{ - int el = regime_el(env, mmu_idx); - if (el == 2 || arm_el_is_aa64(env, el)) { - return true; - } - if (arm_feature(env, ARM_FEATURE_LPAE) - && (regime_tcr(env, mmu_idx)->raw_tcr & TTBCR_EAE)) { - return true; - } - return false; -} - /* * Returns true if the stage 1 translation regime is using LPAE format page * tables. Used when raising alignment exceptions, whose FSR changes depending @@ -38,16 +25,17 @@ bool arm_s1_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) } static inline uint32_t merge_syn_data_abort(uint32_t template_syn, + ARMMMUFaultInfo *fi, unsigned int target_el, - bool same_el, bool ea, - bool s1ptw, bool is_write, + bool same_el, bool is_write, int fsc) { uint32_t syn; /* - * ISV is only set for data aborts routed to EL2 and - * never for stage-1 page table walks faulting on stage 2. + * ISV is only set for stage-2 data aborts routed to EL2 and + * never for stage-1 page table walks faulting on stage 2 + * or for stage-1 faults. * * Furthermore, ISV is only set for certain kinds of load/stores. * If the template syndrome does not have ISV set, we should leave @@ -56,10 +44,24 @@ static inline uint32_t merge_syn_data_abort(uint32_t template_syn, * See ARMv8 specs, D7-1974: * ISS encoding for an exception from a Data Abort, the * ISV field. + * + * TODO: FEAT_LS64/FEAT_LS64_V/FEAT_SL64_ACCDATA: Translation, + * Access Flag, and Permission faults caused by LD64B, ST64B, + * ST64BV, or ST64BV0 insns report syndrome info even for stage-1 + * faults and regardless of the target EL. */ - if (!(template_syn & ARM_EL_ISV) || target_el != 2 || s1ptw) { + if (template_syn & ARM_EL_VNCR) { + /* + * FEAT_NV2 faults on accesses via VNCR_EL2 are a special case: + * they are always reported as "same EL", even though we are going + * from EL1 to EL2. + */ + assert(!fi->stage2); + syn = syn_data_abort_vncr(fi->ea, is_write, fsc); + } else if (!(template_syn & ARM_EL_ISV) || target_el != 2 + || fi->s1ptw || !fi->stage2) { syn = syn_data_abort_no_iss(same_el, 0, - ea, 0, s1ptw, is_write, fsc); + fi->ea, 0, fi->s1ptw, is_write, fsc); } else { /* * Fields: IL, ISV, SAS, SSE, SRT, SF and AR come from the template @@ -68,7 +70,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, + fi->ea, 0, fi->s1ptw, is_write, fsc, true); /* Merge the runtime syndrome with the template syndrome. */ syn |= template_syn; @@ -82,8 +84,17 @@ static uint32_t compute_fsr_fsc(CPUARMState *env, ARMMMUFaultInfo *fi, ARMMMUIdx arm_mmu_idx = core_to_arm_mmu_idx(env, mmu_idx); uint32_t fsr, fsc; - if (target_el == 2 || arm_el_is_aa64(env, target_el) || - arm_s1_regime_using_lpae_format(env, arm_mmu_idx)) { + /* + * For M-profile there is no guest-facing FSR. We compute a + * short-form value for env->exception.fsr which we will then + * examine in arm_v7m_cpu_do_interrupt(). In theory we could + * use the LPAE format instead as long as both bits of code agree + * (and arm_fi_to_lfsc() handled the M-profile specific + * ARMFault_QEMU_NSCExec and ARMFault_QEMU_SFault cases). + */ + if (!arm_feature(env, ARM_FEATURE_M) && + (target_el == 2 || arm_el_is_aa64(env, target_el) || + arm_s1_regime_using_lpae_format(env, arm_mmu_idx))) { /* * LPAE format fault status register : bottom 6 bits are * status code in the same form as needed for syndrome @@ -105,17 +116,121 @@ static uint32_t compute_fsr_fsc(CPUARMState *env, ARMMMUFaultInfo *fi, return fsr; } +static bool report_as_gpc_exception(ARMCPU *cpu, int current_el, + ARMMMUFaultInfo *fi) +{ + bool ret; + + switch (fi->gpcf) { + case GPCF_None: + return false; + case GPCF_AddressSize: + case GPCF_Walk: + case GPCF_EABT: + /* R_PYTGX: GPT faults are reported as GPC. */ + ret = true; + break; + case GPCF_Fail: + /* + * R_BLYPM: A GPF at EL3 is reported as insn or data abort. + * R_VBZMW, R_LXHQR: A GPF at EL[0-2] is reported as a GPC + * if SCR_EL3.GPF is set, otherwise an insn or data abort. + */ + ret = (cpu->env.cp15.scr_el3 & SCR_GPF) && current_el != 3; + break; + default: + g_assert_not_reached(); + } + + assert(cpu_isar_feature(aa64_rme, cpu)); + assert(fi->type == ARMFault_GPCFOnWalk || + fi->type == ARMFault_GPCFOnOutput); + if (fi->gpcf == GPCF_AddressSize) { + assert(fi->level == 0); + } else { + assert(fi->level >= 0 && fi->level <= 1); + } + + return ret; +} + +static unsigned encode_gpcsc(ARMMMUFaultInfo *fi) +{ + static uint8_t const gpcsc[] = { + [GPCF_AddressSize] = 0b000000, + [GPCF_Walk] = 0b000100, + [GPCF_Fail] = 0b001100, + [GPCF_EABT] = 0b010100, + }; + + /* Note that we've validated fi->gpcf and fi->level above. */ + return gpcsc[fi->gpcf] | fi->level; +} + static G_NORETURN void arm_deliver_fault(ARMCPU *cpu, vaddr addr, MMUAccessType access_type, int mmu_idx, ARMMMUFaultInfo *fi) { CPUARMState *env = &cpu->env; - int target_el; + int target_el = exception_target_el(env); + int current_el = arm_current_el(env); bool same_el; uint32_t syn, exc, fsr, fsc; + /* + * We know this must be a data or insn abort, and that + * env->exception.syndrome contains the template syndrome set + * up at translate time. So we can check only the VNCR bit + * (and indeed syndrome does not have the EC field in it, + * because we masked that out in disas_set_insn_syndrome()) + */ + bool is_vncr = (access_type != MMU_INST_FETCH) && + (env->exception.syndrome & ARM_EL_VNCR); + + if (is_vncr) { + /* FEAT_NV2 faults on accesses via VNCR_EL2 go to EL2 */ + target_el = 2; + } + + if (report_as_gpc_exception(cpu, current_el, fi)) { + target_el = 3; + + fsr = compute_fsr_fsc(env, fi, target_el, mmu_idx, &fsc); + + syn = syn_gpc(fi->stage2 && fi->type == ARMFault_GPCFOnWalk, + access_type == MMU_INST_FETCH, + encode_gpcsc(fi), is_vncr, + 0, fi->s1ptw, + access_type == MMU_DATA_STORE, fsc); + + env->cp15.mfar_el3 = fi->paddr; + switch (fi->paddr_space) { + case ARMSS_Secure: + break; + case ARMSS_NonSecure: + env->cp15.mfar_el3 |= R_MFAR_NS_MASK; + break; + case ARMSS_Root: + env->cp15.mfar_el3 |= R_MFAR_NSE_MASK; + break; + case ARMSS_Realm: + env->cp15.mfar_el3 |= R_MFAR_NSE_MASK | R_MFAR_NS_MASK; + break; + default: + g_assert_not_reached(); + } + + exc = EXCP_GPC; + goto do_raise; + } + + /* If SCR_EL3.GPF is unset, GPF may still be routed to EL2. */ + if (fi->gpcf == GPCF_Fail && target_el < 2) { + if (arm_hcr_el2_eff(env) & HCR_GPF) { + target_el = 2; + } + } - target_el = exception_target_el(env); if (fi->stage2) { target_el = 2; env->cp15.hpfar_el2 = extract64(fi->s2addr, 12, 47) << 4; @@ -123,17 +238,16 @@ void arm_deliver_fault(ARMCPU *cpu, vaddr addr, env->cp15.hpfar_el2 |= HPFAR_NS; } } - same_el = (arm_current_el(env) == target_el); + same_el = current_el == target_el; fsr = compute_fsr_fsc(env, fi, target_el, mmu_idx, &fsc); if (access_type == MMU_INST_FETCH) { syn = syn_insn_abort(same_el, fi->ea, fi->s1ptw, fsc); exc = EXCP_PREFETCH_ABORT; } else { - syn = merge_syn_data_abort(env->exception.syndrome, target_el, - same_el, fi->ea, fi->s1ptw, - access_type == MMU_DATA_STORE, + syn = merge_syn_data_abort(env->exception.syndrome, fi, target_el, + same_el, access_type == MMU_DATA_STORE, fsc); if (access_type == MMU_DATA_STORE && arm_feature(env, ARM_FEATURE_V6)) { @@ -142,6 +256,7 @@ void arm_deliver_fault(ARMCPU *cpu, vaddr addr, exc = EXCP_DATA_ABORT; } + do_raise: env->exception.vaddress = addr; env->exception.fsr = fsr; raise_exception(env, exc, syn, target_el); @@ -156,7 +271,7 @@ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, ARMMMUFaultInfo fi = {}; /* now we have a real cpu fault */ - cpu_restore_state(cs, retaddr, true); + cpu_restore_state(cs, retaddr); fi.type = ARMFault_Alignment; arm_deliver_fault(cpu, vaddr, access_type, mmu_idx, &fi); @@ -166,7 +281,7 @@ void helper_exception_pc_alignment(CPUARMState *env, target_ulong pc) { ARMMMUFaultInfo fi = { .type = ARMFault_Alignment }; int target_el = exception_target_el(env); - int mmu_idx = cpu_mmu_index(env, true); + int mmu_idx = arm_env_mmu_index(env); uint32_t fsc; env->exception.vaddress = pc; @@ -196,7 +311,7 @@ void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, ARMMMUFaultInfo fi = {}; /* now we have a real cpu fault */ - cpu_restore_state(cs, retaddr, true); + cpu_restore_state(cs, retaddr); fi.ea = arm_extabort_type(response); fi.type = ARMFault_SyncExternal; @@ -208,12 +323,20 @@ bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size, bool probe, uintptr_t retaddr) { ARMCPU *cpu = ARM_CPU(cs); - ARMMMUFaultInfo fi = {}; - hwaddr phys_addr; - target_ulong page_size; - int prot, ret; - MemTxAttrs attrs = {}; - ARMCacheAttrs cacheattrs = {}; + GetPhysAddrResult res = {}; + ARMMMUFaultInfo local_fi, *fi; + int ret; + + /* + * Allow S1_ptw_translate to see any fault generated here. + * Since this may recurse, read and clear. + */ + fi = cpu->env.tlb_fi; + if (fi) { + cpu->env.tlb_fi = NULL; + } else { + fi = memset(&local_fi, 0, sizeof(local_fi)); + } /* * Walk the page table and (if the mapping exists) add the page @@ -223,32 +346,29 @@ bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size, */ ret = get_phys_addr(&cpu->env, address, access_type, core_to_arm_mmu_idx(&cpu->env, mmu_idx), - &phys_addr, &attrs, &prot, &page_size, - &fi, &cacheattrs); + &res, fi); if (likely(!ret)) { /* * Map a single [sub]page. Regions smaller than our declared * target page size are handled specially, so for those we * pass in the exact addresses. */ - if (page_size >= TARGET_PAGE_SIZE) { - phys_addr &= TARGET_PAGE_MASK; + if (res.f.lg_page_size >= TARGET_PAGE_BITS) { + res.f.phys_addr &= TARGET_PAGE_MASK; address &= TARGET_PAGE_MASK; } - /* Notice and record tagged memory. */ - if (cpu_isar_feature(aa64_mte, cpu) && cacheattrs.attrs == 0xf0) { - arm_tlb_mte_tagged(&attrs) = true; - } - tlb_set_page_with_attrs(cs, address, phys_addr, attrs, - prot, mmu_idx, page_size); + res.f.extra.arm.pte_attrs = res.cacheattrs.attrs; + res.f.extra.arm.shareability = res.cacheattrs.shareability; + + tlb_set_page_full(cs, mmu_idx, address, &res.f); return true; } else if (probe) { return false; } else { /* now we have a real cpu fault */ - cpu_restore_state(cs, retaddr, true); - arm_deliver_fault(cpu, address, access_type, mmu_idx, &fi); + cpu_restore_state(cs, retaddr); + arm_deliver_fault(cpu, address, access_type, mmu_idx, fi); } } #else @@ -266,7 +386,7 @@ void arm_cpu_record_sigsegv(CPUState *cs, vaddr addr, * We report both ESR and FAR to signal handlers. * For now, it's easiest to deliver the fault normally. */ - cpu_restore_state(cs, ra, true); + cpu_restore_state(cs, ra); arm_deliver_fault(cpu, addr, access_type, MMU_USER_IDX, &fi); } diff --git a/target/arm/translate-a32.h b/target/arm/tcg/translate-a32.h similarity index 81% rename from target/arm/translate-a32.h rename to target/arm/tcg/translate-a32.h index 78a84c1414..19de6e0a1a 100644 --- a/target/arm/translate-a32.h +++ b/target/arm/tcg/translate-a32.h @@ -40,7 +40,7 @@ void write_neon_element64(TCGv_i64 src, int reg, int ele, MemOp memop); TCGv_i32 add_reg_for_lit(DisasContext *s, int reg, int ofs); void gen_set_cpsr(TCGv_i32 var, uint32_t mask); void gen_set_condexec(DisasContext *s); -void gen_set_pc_im(DisasContext *s, target_ulong val); +void gen_update_pc(DisasContext *s, target_long diff); void gen_lookup_tb(DisasContext *s); long vfp_reg_offset(bool dp, unsigned reg); long neon_full_reg_offset(unsigned reg); @@ -55,17 +55,33 @@ bool mve_skip_vmov(DisasContext *s, int vn, int index, int size); static inline TCGv_i32 load_cpu_offset(int offset) { TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_ld_i32(tmp, cpu_env, offset); + tcg_gen_ld_i32(tmp, tcg_env, offset); return tmp; } -#define load_cpu_field(name) load_cpu_offset(offsetof(CPUARMState, name)) +/* Load from a 32-bit field to a TCGv_i32 */ +#define load_cpu_field(name) \ + ({ \ + QEMU_BUILD_BUG_ON(sizeof_field(CPUARMState, name) != 4); \ + load_cpu_offset(offsetof(CPUARMState, name)); \ + }) + +/* Load from the low half of a 64-bit field to a TCGv_i32 */ +#define load_cpu_field_low32(name) \ + ({ \ + QEMU_BUILD_BUG_ON(sizeof_field(CPUARMState, name) != 8); \ + load_cpu_offset(offsetoflow32(CPUARMState, name)); \ + }) void store_cpu_offset(TCGv_i32 var, int offset, int size); -#define store_cpu_field(var, name) \ - store_cpu_offset(var, offsetof(CPUARMState, name), \ - sizeof_field(CPUARMState, name)) +#define store_cpu_field(val, name) \ + ({ \ + QEMU_BUILD_BUG_ON(sizeof_field(CPUARMState, name) != 4 \ + && sizeof_field(CPUARMState, name) != 1); \ + store_cpu_offset(val, offsetof(CPUARMState, name), \ + sizeof_field(CPUARMState, name)); \ + }) #define store_cpu_field_constant(val, name) \ store_cpu_field(tcg_constant_i32(val), name) diff --git a/target/arm/translate-a64.c b/target/arm/tcg/translate-a64.c similarity index 75% rename from target/arm/translate-a64.c rename to target/arm/tcg/translate-a64.c index d438fb89e7..2666d52711 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -18,23 +18,14 @@ */ #include "qemu/osdep.h" -#include "cpu.h" #include "exec/exec-all.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "qemu/log.h" -#include "arm_ldst.h" #include "translate.h" -#include "internals.h" -#include "qemu/host-utils.h" -#include "semihosting/semihost.h" -#include "exec/gen-icount.h" -#include "exec/helper-proto.h" -#include "exec/helper-gen.h" -#include "exec/log.h" -#include "cpregs.h" #include "translate-a64.h" -#include "qemu/atomic128.h" +#include "qemu/log.h" +#include "disas/disas.h" +#include "arm_ldst.h" +#include "semihosting/semihost.h" +#include "cpregs.h" static TCGv_i64 cpu_X[32]; static TCGv_i64 cpu_pc; @@ -56,6 +47,35 @@ enum a64_shift_type { A64_SHIFT_TYPE_ROR = 3 }; +/* + * Helpers for extracting complex instruction fields + */ + +/* + * For load/store with an unsigned 12 bit immediate scaled by the element + * size. The input has the immediate field in bits [14:3] and the element + * size in [2:0]. + */ +static int uimm_scaled(DisasContext *s, int x) +{ + unsigned imm = x >> 3; + unsigned scale = extract32(x, 0, 3); + return imm << scale; +} + +/* For load/store memory tags: scale offset by LOG2_TAG_GRANULE */ +static int scale_by_log2_tag_granule(DisasContext *s, int x) +{ + return x << LOG2_TAG_GRANULE; +} + +/* + * Include the generated decoders. + */ + +#include "decode-sme-fa64.c.inc" +#include "decode-a64.c.inc" + /* Table based decoder typedefs - used when the relevant bits for decode * are too awkwardly scattered across the instruction (eg SIMD). */ @@ -72,23 +92,31 @@ void a64_translate_init(void) { int i; - cpu_pc = tcg_global_mem_new_i64(cpu_env, + cpu_pc = tcg_global_mem_new_i64(tcg_env, offsetof(CPUARMState, pc), "pc"); for (i = 0; i < 32; i++) { - cpu_X[i] = tcg_global_mem_new_i64(cpu_env, + cpu_X[i] = tcg_global_mem_new_i64(tcg_env, offsetof(CPUARMState, xregs[i]), regnames[i]); } - cpu_exclusive_high = tcg_global_mem_new_i64(cpu_env, + cpu_exclusive_high = tcg_global_mem_new_i64(tcg_env, offsetof(CPUARMState, exclusive_high), "exclusive_high"); } /* - * Return the core mmu_idx to use for A64 "unprivileged load/store" insns + * Return the core mmu_idx to use for A64 load/store insns which + * have a "unprivileged load/store" variant. Those insns access + * EL0 if executed from an EL which has control over EL0 (usually + * EL1) but behave like normal loads and stores if executed from + * elsewhere (eg EL3). + * + * @unpriv : true for the unprivileged encoding; false for the + * normal encoding (in which case we will return the same + * thing as get_mem_index(). */ -static int get_a64_user_mem_index(DisasContext *s) +static int get_a64_user_mem_index(DisasContext *s, bool unpriv) { /* * If AccType_UNPRIV is not used, the insn uses AccType_NORMAL, @@ -96,7 +124,7 @@ static int get_a64_user_mem_index(DisasContext *s) */ ARMMMUIdx useridx = s->mmu_idx; - if (s->unpriv) { + if (unpriv && s->unpriv) { /* * We have pre-computed the condition for AccType_UNPRIV. * Therefore we should never get here with a mmu_idx for @@ -111,14 +139,6 @@ static int get_a64_user_mem_index(DisasContext *s) case ARMMMUIdx_E20_2_PAN: useridx = ARMMMUIdx_E20_0; break; - case ARMMMUIdx_SE10_1: - case ARMMMUIdx_SE10_1_PAN: - useridx = ARMMMUIdx_SE10_0; - break; - case ARMMMUIdx_SE20_2: - case ARMMMUIdx_SE20_2_PAN: - useridx = ARMMMUIdx_SE20_0; - break; default: g_assert_not_reached(); } @@ -128,7 +148,7 @@ static int get_a64_user_mem_index(DisasContext *s) static void set_btype_raw(int val) { - tcg_gen_st_i32(tcg_constant_i32(val), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(val), tcg_env, offsetof(CPUARMState, btype)); } @@ -148,9 +168,20 @@ static void reset_btype(DisasContext *s) } } -void gen_a64_set_pc_im(uint64_t val) +static void gen_pc_plus_diff(DisasContext *s, TCGv_i64 dest, target_long diff) { - tcg_gen_movi_i64(cpu_pc, val); + assert(s->pc_save != -1); + if (tb_cflags(s->base.tb) & CF_PCREL) { + tcg_gen_addi_i64(dest, cpu_pc, (s->pc_curr - s->pc_save) + diff); + } else { + tcg_gen_movi_i64(dest, s->pc_curr + diff); + } +} + +void gen_a64_update_pc(DisasContext *s, target_long diff) +{ + gen_pc_plus_diff(s, cpu_pc, diff); + s->pc_save = s->pc_curr + diff; } /* @@ -160,7 +191,7 @@ void gen_a64_set_pc_im(uint64_t val) * + for EL2 and EL3 there is only one TBI bit, and if it is set * then the address is zero-extended, clearing bits [63:56] * + for EL0 and EL1, TBI0 controls addresses with bit 55 == 0 - * and TBI1 controls addressses with bit 55 == 1. + * and TBI1 controls addresses with bit 55 == 1. * If the appropriate TBI bit is set for the address then * the address is sign-extended from bit 55 into bits [63:56] * @@ -204,6 +235,7 @@ static void gen_a64_set_pc(DisasContext *s, TCGv_i64 src) * then loading an address into the PC will clear out any tag. */ gen_top_byte_ignore(s, cpu_pc, src, s->tbii); + s->pc_save = -1; } /* @@ -220,7 +252,7 @@ static void gen_a64_set_pc(DisasContext *s, TCGv_i64 src) TCGv_i64 clean_data_tbi(DisasContext *s, TCGv_i64 addr) { - TCGv_i64 clean = new_tmp_a64(s); + TCGv_i64 clean = tcg_temp_new_i64(); #ifdef CONFIG_USER_ONLY gen_top_byte_ignore(s, clean, addr, s->tbid); #else @@ -238,7 +270,7 @@ static void gen_address_with_allocation_tag0(TCGv_i64 dst, TCGv_i64 src) static void gen_probe_access(DisasContext *s, TCGv_i64 ptr, MMUAccessType acc, int log2_size) { - gen_helper_probe_access(cpu_env, ptr, + gen_helper_probe_access(tcg_env, ptr, tcg_constant_i32(acc), tcg_constant_i32(get_mem_index(s)), tcg_constant_i32(1 << log2_size)); @@ -252,7 +284,7 @@ static void gen_probe_access(DisasContext *s, TCGv_i64 ptr, */ static TCGv_i64 gen_mte_check1_mmuidx(DisasContext *s, TCGv_i64 addr, bool is_write, bool tag_checked, - int log2_size, bool is_unpriv, + MemOp memop, bool is_unpriv, int core_idx) { if (tag_checked && s->mte_active[is_unpriv]) { @@ -263,10 +295,11 @@ static TCGv_i64 gen_mte_check1_mmuidx(DisasContext *s, TCGv_i64 addr, desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); - desc = FIELD_DP32(desc, MTEDESC, SIZEM1, (1 << log2_size) - 1); + desc = FIELD_DP32(desc, MTEDESC, ALIGN, get_alignment_bits(memop)); + desc = FIELD_DP32(desc, MTEDESC, SIZEM1, memop_size(memop) - 1); - ret = new_tmp_a64(s); - gen_helper_mte_check(ret, cpu_env, tcg_constant_i32(desc), addr); + ret = tcg_temp_new_i64(); + gen_helper_mte_check(ret, tcg_env, tcg_constant_i32(desc), addr); return ret; } @@ -274,9 +307,9 @@ static TCGv_i64 gen_mte_check1_mmuidx(DisasContext *s, TCGv_i64 addr, } TCGv_i64 gen_mte_check1(DisasContext *s, TCGv_i64 addr, bool is_write, - bool tag_checked, int log2_size) + bool tag_checked, MemOp memop) { - return gen_mte_check1_mmuidx(s, addr, is_write, tag_checked, log2_size, + return gen_mte_check1_mmuidx(s, addr, is_write, tag_checked, memop, false, get_mem_index(s)); } @@ -284,7 +317,7 @@ TCGv_i64 gen_mte_check1(DisasContext *s, TCGv_i64 addr, bool is_write, * For MTE, check multiple logical sequential accesses. */ TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, - bool tag_checked, int size) + bool tag_checked, int total_size, MemOp single_mop) { if (tag_checked && s->mte_active[0]) { TCGv_i64 ret; @@ -294,16 +327,100 @@ TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); - desc = FIELD_DP32(desc, MTEDESC, SIZEM1, size - 1); + desc = FIELD_DP32(desc, MTEDESC, ALIGN, get_alignment_bits(single_mop)); + desc = FIELD_DP32(desc, MTEDESC, SIZEM1, total_size - 1); - ret = new_tmp_a64(s); - gen_helper_mte_check(ret, cpu_env, tcg_constant_i32(desc), addr); + ret = tcg_temp_new_i64(); + gen_helper_mte_check(ret, tcg_env, tcg_constant_i32(desc), addr); return ret; } return clean_data_tbi(s, addr); } +/* + * Generate the special alignment check that applies to AccType_ATOMIC + * and AccType_ORDERED insns under FEAT_LSE2: the access need not be + * naturally aligned, but it must not cross a 16-byte boundary. + * See AArch64.CheckAlignment(). + */ +static void check_lse2_align(DisasContext *s, int rn, int imm, + bool is_write, MemOp mop) +{ + TCGv_i32 tmp; + TCGv_i64 addr; + TCGLabel *over_label; + MMUAccessType type; + int mmu_idx; + + tmp = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(tmp, cpu_reg_sp(s, rn)); + tcg_gen_addi_i32(tmp, tmp, imm & 15); + tcg_gen_andi_i32(tmp, tmp, 15); + tcg_gen_addi_i32(tmp, tmp, memop_size(mop)); + + over_label = gen_new_label(); + tcg_gen_brcondi_i32(TCG_COND_LEU, tmp, 16, over_label); + + addr = tcg_temp_new_i64(); + tcg_gen_addi_i64(addr, cpu_reg_sp(s, rn), imm); + + type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD, + mmu_idx = get_mem_index(s); + gen_helper_unaligned_access(tcg_env, addr, tcg_constant_i32(type), + tcg_constant_i32(mmu_idx)); + + gen_set_label(over_label); + +} + +/* Handle the alignment check for AccType_ATOMIC instructions. */ +static MemOp check_atomic_align(DisasContext *s, int rn, MemOp mop) +{ + MemOp size = mop & MO_SIZE; + + if (size == MO_8) { + return mop; + } + + /* + * If size == MO_128, this is a LDXP, and the operation is single-copy + * atomic for each doubleword, not the entire quadword; it still must + * be quadword aligned. + */ + if (size == MO_128) { + return finalize_memop_atom(s, MO_128 | MO_ALIGN, + MO_ATOM_IFALIGN_PAIR); + } + if (dc_isar_feature(aa64_lse2, s)) { + check_lse2_align(s, rn, 0, true, mop); + } else { + mop |= MO_ALIGN; + } + return finalize_memop(s, mop); +} + +/* Handle the alignment check for AccType_ORDERED instructions. */ +static MemOp check_ordered_align(DisasContext *s, int rn, int imm, + bool is_write, MemOp mop) +{ + MemOp size = mop & MO_SIZE; + + if (size == MO_8) { + return mop; + } + if (size == MO_128) { + return finalize_memop_atom(s, MO_128 | MO_ALIGN, + MO_ATOM_IFALIGN_PAIR); + } + if (!dc_isar_feature(aa64_lse2, s)) { + mop |= MO_ALIGN; + } else if (!s->naa) { + check_lse2_align(s, rn, imm, is_write, mop); + } + return finalize_memop(s, mop); +} + typedef struct DisasCompare64 { TCGCond cond; TCGv_i64 value; @@ -315,42 +432,37 @@ static void a64_test_cc(DisasCompare64 *c64, int cc) arm_test_cc(&c32, cc); - /* Sign-extend the 32-bit value so that the GE/LT comparisons work - * properly. The NE/EQ comparisons are also fine with this choice. */ + /* + * Sign-extend the 32-bit value so that the GE/LT comparisons work + * properly. The NE/EQ comparisons are also fine with this choice. + */ c64->cond = c32.cond; c64->value = tcg_temp_new_i64(); tcg_gen_ext_i32_i64(c64->value, c32.value); - - arm_free_cc(&c32); -} - -static void a64_free_cc(DisasCompare64 *c64) -{ - tcg_temp_free_i64(c64->value); } static void gen_rebuild_hflags(DisasContext *s) { - gen_helper_rebuild_hflags_a64(cpu_env, tcg_constant_i32(s->current_el)); + gen_helper_rebuild_hflags_a64(tcg_env, tcg_constant_i32(s->current_el)); } static void gen_exception_internal(int excp) { assert(excp_is_internal(excp)); - gen_helper_exception_internal(cpu_env, tcg_constant_i32(excp)); + gen_helper_exception_internal(tcg_env, tcg_constant_i32(excp)); } -static void gen_exception_internal_insn(DisasContext *s, uint64_t pc, int excp) +static void gen_exception_internal_insn(DisasContext *s, int excp) { - gen_a64_set_pc_im(pc); + gen_a64_update_pc(s, 0); gen_exception_internal(excp); s->base.is_jmp = DISAS_NORETURN; } static void gen_exception_bkpt_insn(DisasContext *s, uint32_t syndrome) { - gen_a64_set_pc_im(s->pc_curr); - gen_helper_exception_bkpt_insn(cpu_env, tcg_constant_i32(syndrome)); + gen_a64_update_pc(s, 0); + gen_helper_exception_bkpt_insn(tcg_env, tcg_constant_i32(syndrome)); s->base.is_jmp = DISAS_NORETURN; } @@ -378,15 +490,28 @@ static inline bool use_goto_tb(DisasContext *s, uint64_t dest) return translator_use_goto_tb(&s->base, dest); } -static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) +static void gen_goto_tb(DisasContext *s, int n, int64_t diff) { - if (use_goto_tb(s, dest)) { - tcg_gen_goto_tb(n); - gen_a64_set_pc_im(dest); + if (use_goto_tb(s, s->pc_curr + diff)) { + /* + * For pcrel, the pc must always be up-to-date on entry to + * the linked TB, so that it can use simple additions for all + * further adjustments. For !pcrel, the linked TB is compiled + * to know its full virtual address, so we can delay the + * update to pc to the unlinked path. A long chain of links + * can thus avoid many updates to the PC. + */ + if (tb_cflags(s->base.tb) & CF_PCREL) { + gen_a64_update_pc(s, diff); + tcg_gen_goto_tb(n); + } else { + tcg_gen_goto_tb(n); + gen_a64_update_pc(s, diff); + } tcg_gen_exit_tb(s->base.tb, n); s->base.is_jmp = DISAS_NORETURN; } else { - gen_a64_set_pc_im(dest); + gen_a64_update_pc(s, diff); if (s->ss_active) { gen_step_complete_exception(s); } else { @@ -396,42 +521,6 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) } } -static void init_tmp_a64_array(DisasContext *s) -{ -#ifdef CONFIG_DEBUG_TCG - memset(s->tmp_a64, 0, sizeof(s->tmp_a64)); -#endif - s->tmp_a64_count = 0; -} - -static void free_tmp_a64(DisasContext *s) -{ - int i; - for (i = 0; i < s->tmp_a64_count; i++) { - tcg_temp_free_i64(s->tmp_a64[i]); - } - init_tmp_a64_array(s); -} - -TCGv_i64 new_tmp_a64(DisasContext *s) -{ - assert(s->tmp_a64_count < TMP_A64_MAX); - return s->tmp_a64[s->tmp_a64_count++] = tcg_temp_new_i64(); -} - -TCGv_i64 new_tmp_a64_local(DisasContext *s) -{ - assert(s->tmp_a64_count < TMP_A64_MAX); - return s->tmp_a64[s->tmp_a64_count++] = tcg_temp_local_new_i64(); -} - -TCGv_i64 new_tmp_a64_zero(DisasContext *s) -{ - TCGv_i64 t = new_tmp_a64(s); - tcg_gen_movi_i64(t, 0); - return t; -} - /* * Register access functions * @@ -450,7 +539,9 @@ TCGv_i64 new_tmp_a64_zero(DisasContext *s) TCGv_i64 cpu_reg(DisasContext *s, int reg) { if (reg == 31) { - return new_tmp_a64_zero(s); + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_movi_i64(t, 0); + return t; } else { return cpu_X[reg]; } @@ -468,7 +559,7 @@ TCGv_i64 cpu_reg_sp(DisasContext *s, int reg) */ TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf) { - TCGv_i64 v = new_tmp_a64(s); + TCGv_i64 v = tcg_temp_new_i64(); if (reg != 31) { if (sf) { tcg_gen_mov_i64(v, cpu_X[reg]); @@ -483,7 +574,7 @@ TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf) TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf) { - TCGv_i64 v = new_tmp_a64(s); + TCGv_i64 v = tcg_temp_new_i64(); if (sf) { tcg_gen_mov_i64(v, cpu_X[reg]); } else { @@ -518,7 +609,7 @@ static TCGv_i64 read_fp_dreg(DisasContext *s, int reg) { TCGv_i64 v = tcg_temp_new_i64(); - tcg_gen_ld_i64(v, cpu_env, fp_reg_offset(s, reg, MO_64)); + tcg_gen_ld_i64(v, tcg_env, fp_reg_offset(s, reg, MO_64)); return v; } @@ -526,7 +617,7 @@ static TCGv_i32 read_fp_sreg(DisasContext *s, int reg) { TCGv_i32 v = tcg_temp_new_i32(); - tcg_gen_ld_i32(v, cpu_env, fp_reg_offset(s, reg, MO_32)); + tcg_gen_ld_i32(v, tcg_env, fp_reg_offset(s, reg, MO_32)); return v; } @@ -534,7 +625,7 @@ static TCGv_i32 read_fp_hreg(DisasContext *s, int reg) { TCGv_i32 v = tcg_temp_new_i32(); - tcg_gen_ld16u_i32(v, cpu_env, fp_reg_offset(s, reg, MO_16)); + tcg_gen_ld16u_i32(v, tcg_env, fp_reg_offset(s, reg, MO_16)); return v; } @@ -554,7 +645,7 @@ void write_fp_dreg(DisasContext *s, int reg, TCGv_i64 v) { unsigned ofs = fp_reg_offset(s, reg, MO_64); - tcg_gen_st_i64(v, cpu_env, ofs); + tcg_gen_st_i64(v, tcg_env, ofs); clear_vec_high(s, false, reg); } @@ -564,7 +655,6 @@ static void write_fp_sreg(DisasContext *s, int reg, TCGv_i32 v) tcg_gen_extu_i32_i64(tmp, v); write_fp_dreg(s, reg, tmp); - tcg_temp_free_i64(tmp); } /* Expand a 2-operand AdvSIMD vector operation using an expander function. */ @@ -633,7 +723,6 @@ static void gen_gvec_op3_fpst(DisasContext *s, bool is_q, int rd, int rn, vec_full_reg_offset(s, rn), vec_full_reg_offset(s, rm), fpst, is_q ? 16 : 8, vec_full_reg_size(s), data, fn); - tcg_temp_free_ptr(fpst); } /* Expand a 3-operand + qc + operation using an out-of-line helper. */ @@ -642,12 +731,11 @@ static void gen_gvec_op3_qc(DisasContext *s, bool is_q, int rd, int rn, { TCGv_ptr qc_ptr = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(qc_ptr, cpu_env, offsetof(CPUARMState, vfp.qc)); + tcg_gen_addi_ptr(qc_ptr, tcg_env, offsetof(CPUARMState, vfp.qc)); tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), vec_full_reg_offset(s, rm), qc_ptr, is_q ? 16 : 8, vec_full_reg_size(s), 0, fn); - tcg_temp_free_ptr(qc_ptr); } /* Expand a 4-operand operation using an out-of-line helper. */ @@ -675,7 +763,6 @@ static void gen_gvec_op4_fpst(DisasContext *s, bool is_q, int rd, int rn, vec_full_reg_offset(s, rm), vec_full_reg_offset(s, ra), fpst, is_q ? 16 : 8, vec_full_reg_size(s), data, fn); - tcg_temp_free_ptr(fpst); } /* Set ZF and NF based on a 64 bit result. This is alas fiddlier @@ -701,96 +788,102 @@ static inline void gen_logic_CC(int sf, TCGv_i64 result) } /* dest = T0 + T1; compute C, N, V and Z flags */ +static void gen_add64_CC(TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) +{ + TCGv_i64 result, flag, tmp; + result = tcg_temp_new_i64(); + flag = tcg_temp_new_i64(); + tmp = tcg_temp_new_i64(); + + tcg_gen_movi_i64(tmp, 0); + tcg_gen_add2_i64(result, flag, t0, tmp, t1, tmp); + + tcg_gen_extrl_i64_i32(cpu_CF, flag); + + gen_set_NZ64(result); + + tcg_gen_xor_i64(flag, result, t0); + tcg_gen_xor_i64(tmp, t0, t1); + tcg_gen_andc_i64(flag, flag, tmp); + tcg_gen_extrh_i64_i32(cpu_VF, flag); + + tcg_gen_mov_i64(dest, result); +} + +static void gen_add32_CC(TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) +{ + TCGv_i32 t0_32 = tcg_temp_new_i32(); + TCGv_i32 t1_32 = tcg_temp_new_i32(); + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_movi_i32(tmp, 0); + tcg_gen_extrl_i64_i32(t0_32, t0); + tcg_gen_extrl_i64_i32(t1_32, t1); + tcg_gen_add2_i32(cpu_NF, cpu_CF, t0_32, tmp, t1_32, tmp); + tcg_gen_mov_i32(cpu_ZF, cpu_NF); + tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32); + tcg_gen_xor_i32(tmp, t0_32, t1_32); + tcg_gen_andc_i32(cpu_VF, cpu_VF, tmp); + tcg_gen_extu_i32_i64(dest, cpu_NF); +} + static void gen_add_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) { if (sf) { - TCGv_i64 result, flag, tmp; - result = tcg_temp_new_i64(); - flag = tcg_temp_new_i64(); - tmp = tcg_temp_new_i64(); - - tcg_gen_movi_i64(tmp, 0); - tcg_gen_add2_i64(result, flag, t0, tmp, t1, tmp); - - tcg_gen_extrl_i64_i32(cpu_CF, flag); - - gen_set_NZ64(result); - - tcg_gen_xor_i64(flag, result, t0); - tcg_gen_xor_i64(tmp, t0, t1); - tcg_gen_andc_i64(flag, flag, tmp); - tcg_temp_free_i64(tmp); - tcg_gen_extrh_i64_i32(cpu_VF, flag); - - tcg_gen_mov_i64(dest, result); - tcg_temp_free_i64(result); - tcg_temp_free_i64(flag); + gen_add64_CC(dest, t0, t1); } else { - /* 32 bit arithmetic */ - TCGv_i32 t0_32 = tcg_temp_new_i32(); - TCGv_i32 t1_32 = tcg_temp_new_i32(); - TCGv_i32 tmp = tcg_temp_new_i32(); - - tcg_gen_movi_i32(tmp, 0); - tcg_gen_extrl_i64_i32(t0_32, t0); - tcg_gen_extrl_i64_i32(t1_32, t1); - tcg_gen_add2_i32(cpu_NF, cpu_CF, t0_32, tmp, t1_32, tmp); - tcg_gen_mov_i32(cpu_ZF, cpu_NF); - tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32); - tcg_gen_xor_i32(tmp, t0_32, t1_32); - tcg_gen_andc_i32(cpu_VF, cpu_VF, tmp); - tcg_gen_extu_i32_i64(dest, cpu_NF); - - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(t0_32); - tcg_temp_free_i32(t1_32); + gen_add32_CC(dest, t0, t1); } } /* dest = T0 - T1; compute C, N, V and Z flags */ +static void gen_sub64_CC(TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) +{ + /* 64 bit arithmetic */ + TCGv_i64 result, flag, tmp; + + result = tcg_temp_new_i64(); + flag = tcg_temp_new_i64(); + tcg_gen_sub_i64(result, t0, t1); + + gen_set_NZ64(result); + + tcg_gen_setcond_i64(TCG_COND_GEU, flag, t0, t1); + tcg_gen_extrl_i64_i32(cpu_CF, flag); + + tcg_gen_xor_i64(flag, result, t0); + tmp = tcg_temp_new_i64(); + tcg_gen_xor_i64(tmp, t0, t1); + tcg_gen_and_i64(flag, flag, tmp); + tcg_gen_extrh_i64_i32(cpu_VF, flag); + tcg_gen_mov_i64(dest, result); +} + +static void gen_sub32_CC(TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) +{ + /* 32 bit arithmetic */ + TCGv_i32 t0_32 = tcg_temp_new_i32(); + TCGv_i32 t1_32 = tcg_temp_new_i32(); + TCGv_i32 tmp; + + tcg_gen_extrl_i64_i32(t0_32, t0); + tcg_gen_extrl_i64_i32(t1_32, t1); + tcg_gen_sub_i32(cpu_NF, t0_32, t1_32); + tcg_gen_mov_i32(cpu_ZF, cpu_NF); + tcg_gen_setcond_i32(TCG_COND_GEU, cpu_CF, t0_32, t1_32); + tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32); + tmp = tcg_temp_new_i32(); + tcg_gen_xor_i32(tmp, t0_32, t1_32); + tcg_gen_and_i32(cpu_VF, cpu_VF, tmp); + tcg_gen_extu_i32_i64(dest, cpu_NF); +} + static void gen_sub_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) { if (sf) { - /* 64 bit arithmetic */ - TCGv_i64 result, flag, tmp; - - result = tcg_temp_new_i64(); - flag = tcg_temp_new_i64(); - tcg_gen_sub_i64(result, t0, t1); - - gen_set_NZ64(result); - - tcg_gen_setcond_i64(TCG_COND_GEU, flag, t0, t1); - tcg_gen_extrl_i64_i32(cpu_CF, flag); - - tcg_gen_xor_i64(flag, result, t0); - tmp = tcg_temp_new_i64(); - tcg_gen_xor_i64(tmp, t0, t1); - tcg_gen_and_i64(flag, flag, tmp); - tcg_temp_free_i64(tmp); - tcg_gen_extrh_i64_i32(cpu_VF, flag); - tcg_gen_mov_i64(dest, result); - tcg_temp_free_i64(flag); - tcg_temp_free_i64(result); + gen_sub64_CC(dest, t0, t1); } else { - /* 32 bit arithmetic */ - TCGv_i32 t0_32 = tcg_temp_new_i32(); - TCGv_i32 t1_32 = tcg_temp_new_i32(); - TCGv_i32 tmp; - - tcg_gen_extrl_i64_i32(t0_32, t0); - tcg_gen_extrl_i64_i32(t1_32, t1); - tcg_gen_sub_i32(cpu_NF, t0_32, t1_32); - tcg_gen_mov_i32(cpu_ZF, cpu_NF); - tcg_gen_setcond_i32(TCG_COND_GEU, cpu_CF, t0_32, t1_32); - tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32); - tmp = tcg_temp_new_i32(); - tcg_gen_xor_i32(tmp, t0_32, t1_32); - tcg_temp_free_i32(t0_32); - tcg_temp_free_i32(t1_32); - tcg_gen_and_i32(cpu_VF, cpu_VF, tmp); - tcg_temp_free_i32(tmp); - tcg_gen_extu_i32_i64(dest, cpu_NF); + gen_sub32_CC(dest, t0, t1); } } @@ -801,7 +894,6 @@ static void gen_adc(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) tcg_gen_extu_i32_i64(flag, cpu_CF); tcg_gen_add_i64(dest, t0, t1); tcg_gen_add_i64(dest, dest, flag); - tcg_temp_free_i64(flag); if (!sf) { tcg_gen_ext32u_i64(dest, dest); @@ -830,11 +922,6 @@ static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) tcg_gen_extrh_i64_i32(cpu_VF, vf_64); tcg_gen_mov_i64(dest, result); - - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(vf_64); - tcg_temp_free_i64(cf_64); - tcg_temp_free_i64(result); } else { TCGv_i32 t0_32 = tcg_temp_new_i32(); TCGv_i32 t1_32 = tcg_temp_new_i32(); @@ -851,10 +938,6 @@ static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) tcg_gen_xor_i32(tmp, t0_32, t1_32); tcg_gen_andc_i32(cpu_VF, cpu_VF, tmp); tcg_gen_extu_i32_i64(dest, cpu_NF); - - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(t1_32); - tcg_temp_free_i32(t0_32); } } @@ -871,7 +954,6 @@ static void do_gpr_st_memidx(DisasContext *s, TCGv_i64 source, unsigned int iss_srt, bool iss_sf, bool iss_ar) { - memop = finalize_memop(s, memop); tcg_gen_qemu_st_i64(source, tcg_addr, memidx, memop); if (iss_valid) { @@ -906,7 +988,6 @@ static void do_gpr_ld_memidx(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr, bool iss_valid, unsigned int iss_srt, bool iss_sf, bool iss_ar) { - memop = finalize_memop(s, memop); tcg_gen_qemu_ld_i64(dest, tcg_addr, memidx, memop); if (extend && (memop & MO_SIGN)) { @@ -940,73 +1021,50 @@ static void do_gpr_ld(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr, /* * Store from FP register to memory */ -static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, int size) +static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, MemOp mop) { /* This writes the bottom N bits of a 128 bit wide vector to memory */ TCGv_i64 tmplo = tcg_temp_new_i64(); - MemOp mop; - tcg_gen_ld_i64(tmplo, cpu_env, fp_reg_offset(s, srcidx, MO_64)); + tcg_gen_ld_i64(tmplo, tcg_env, fp_reg_offset(s, srcidx, MO_64)); - if (size < 4) { - mop = finalize_memop(s, size); + if ((mop & MO_SIZE) < MO_128) { tcg_gen_qemu_st_i64(tmplo, tcg_addr, get_mem_index(s), mop); } else { - bool be = s->be_data == MO_BE; - TCGv_i64 tcg_hiaddr = tcg_temp_new_i64(); TCGv_i64 tmphi = tcg_temp_new_i64(); + TCGv_i128 t16 = tcg_temp_new_i128(); - tcg_gen_ld_i64(tmphi, cpu_env, fp_reg_hi_offset(s, srcidx)); + tcg_gen_ld_i64(tmphi, tcg_env, fp_reg_hi_offset(s, srcidx)); + tcg_gen_concat_i64_i128(t16, tmplo, tmphi); - mop = s->be_data | MO_UQ; - tcg_gen_qemu_st_i64(be ? tmphi : tmplo, tcg_addr, get_mem_index(s), - mop | (s->align_mem ? MO_ALIGN_16 : 0)); - tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8); - tcg_gen_qemu_st_i64(be ? tmplo : tmphi, tcg_hiaddr, - get_mem_index(s), mop); - - tcg_temp_free_i64(tcg_hiaddr); - tcg_temp_free_i64(tmphi); + tcg_gen_qemu_st_i128(t16, tcg_addr, get_mem_index(s), mop); } - - tcg_temp_free_i64(tmplo); } /* * Load from memory to FP register */ -static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, int size) +static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, MemOp mop) { /* This always zero-extends and writes to a full 128 bit wide vector */ TCGv_i64 tmplo = tcg_temp_new_i64(); TCGv_i64 tmphi = NULL; - MemOp mop; - if (size < 4) { - mop = finalize_memop(s, size); + if ((mop & MO_SIZE) < MO_128) { tcg_gen_qemu_ld_i64(tmplo, tcg_addr, get_mem_index(s), mop); } else { - bool be = s->be_data == MO_BE; - TCGv_i64 tcg_hiaddr; + TCGv_i128 t16 = tcg_temp_new_i128(); + + tcg_gen_qemu_ld_i128(t16, tcg_addr, get_mem_index(s), mop); tmphi = tcg_temp_new_i64(); - tcg_hiaddr = tcg_temp_new_i64(); - - mop = s->be_data | MO_UQ; - tcg_gen_qemu_ld_i64(be ? tmphi : tmplo, tcg_addr, get_mem_index(s), - mop | (s->align_mem ? MO_ALIGN_16 : 0)); - tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8); - tcg_gen_qemu_ld_i64(be ? tmplo : tmphi, tcg_hiaddr, - get_mem_index(s), mop); - tcg_temp_free_i64(tcg_hiaddr); + tcg_gen_extr_i128_i64(tmplo, tmphi, t16); } - tcg_gen_st_i64(tmplo, cpu_env, fp_reg_offset(s, destidx, MO_64)); - tcg_temp_free_i64(tmplo); + tcg_gen_st_i64(tmplo, tcg_env, fp_reg_offset(s, destidx, MO_64)); if (tmphi) { - tcg_gen_st_i64(tmphi, cpu_env, fp_reg_hi_offset(s, destidx)); - tcg_temp_free_i64(tmphi); + tcg_gen_st_i64(tmphi, tcg_env, fp_reg_hi_offset(s, destidx)); } clear_vec_high(s, tmphi != NULL, destidx); } @@ -1030,26 +1088,26 @@ static void read_vec_element(DisasContext *s, TCGv_i64 tcg_dest, int srcidx, int vect_off = vec_reg_offset(s, srcidx, element, memop & MO_SIZE); switch ((unsigned)memop) { case MO_8: - tcg_gen_ld8u_i64(tcg_dest, cpu_env, vect_off); + tcg_gen_ld8u_i64(tcg_dest, tcg_env, vect_off); break; case MO_16: - tcg_gen_ld16u_i64(tcg_dest, cpu_env, vect_off); + tcg_gen_ld16u_i64(tcg_dest, tcg_env, vect_off); break; case MO_32: - tcg_gen_ld32u_i64(tcg_dest, cpu_env, vect_off); + tcg_gen_ld32u_i64(tcg_dest, tcg_env, vect_off); break; case MO_8|MO_SIGN: - tcg_gen_ld8s_i64(tcg_dest, cpu_env, vect_off); + tcg_gen_ld8s_i64(tcg_dest, tcg_env, vect_off); break; case MO_16|MO_SIGN: - tcg_gen_ld16s_i64(tcg_dest, cpu_env, vect_off); + tcg_gen_ld16s_i64(tcg_dest, tcg_env, vect_off); break; case MO_32|MO_SIGN: - tcg_gen_ld32s_i64(tcg_dest, cpu_env, vect_off); + tcg_gen_ld32s_i64(tcg_dest, tcg_env, vect_off); break; case MO_64: case MO_64|MO_SIGN: - tcg_gen_ld_i64(tcg_dest, cpu_env, vect_off); + tcg_gen_ld_i64(tcg_dest, tcg_env, vect_off); break; default: g_assert_not_reached(); @@ -1062,20 +1120,20 @@ static void read_vec_element_i32(DisasContext *s, TCGv_i32 tcg_dest, int srcidx, int vect_off = vec_reg_offset(s, srcidx, element, memop & MO_SIZE); switch (memop) { case MO_8: - tcg_gen_ld8u_i32(tcg_dest, cpu_env, vect_off); + tcg_gen_ld8u_i32(tcg_dest, tcg_env, vect_off); break; case MO_16: - tcg_gen_ld16u_i32(tcg_dest, cpu_env, vect_off); + tcg_gen_ld16u_i32(tcg_dest, tcg_env, vect_off); break; case MO_8|MO_SIGN: - tcg_gen_ld8s_i32(tcg_dest, cpu_env, vect_off); + tcg_gen_ld8s_i32(tcg_dest, tcg_env, vect_off); break; case MO_16|MO_SIGN: - tcg_gen_ld16s_i32(tcg_dest, cpu_env, vect_off); + tcg_gen_ld16s_i32(tcg_dest, tcg_env, vect_off); break; case MO_32: case MO_32|MO_SIGN: - tcg_gen_ld_i32(tcg_dest, cpu_env, vect_off); + tcg_gen_ld_i32(tcg_dest, tcg_env, vect_off); break; default: g_assert_not_reached(); @@ -1089,16 +1147,16 @@ static void write_vec_element(DisasContext *s, TCGv_i64 tcg_src, int destidx, int vect_off = vec_reg_offset(s, destidx, element, memop & MO_SIZE); switch (memop) { case MO_8: - tcg_gen_st8_i64(tcg_src, cpu_env, vect_off); + tcg_gen_st8_i64(tcg_src, tcg_env, vect_off); break; case MO_16: - tcg_gen_st16_i64(tcg_src, cpu_env, vect_off); + tcg_gen_st16_i64(tcg_src, tcg_env, vect_off); break; case MO_32: - tcg_gen_st32_i64(tcg_src, cpu_env, vect_off); + tcg_gen_st32_i64(tcg_src, tcg_env, vect_off); break; case MO_64: - tcg_gen_st_i64(tcg_src, cpu_env, vect_off); + tcg_gen_st_i64(tcg_src, tcg_env, vect_off); break; default: g_assert_not_reached(); @@ -1111,13 +1169,13 @@ static void write_vec_element_i32(DisasContext *s, TCGv_i32 tcg_src, int vect_off = vec_reg_offset(s, destidx, element, memop & MO_SIZE); switch (memop) { case MO_8: - tcg_gen_st8_i32(tcg_src, cpu_env, vect_off); + tcg_gen_st8_i32(tcg_src, tcg_env, vect_off); break; case MO_16: - tcg_gen_st16_i32(tcg_src, cpu_env, vect_off); + tcg_gen_st16_i32(tcg_src, tcg_env, vect_off); break; case MO_32: - tcg_gen_st_i32(tcg_src, cpu_env, vect_off); + tcg_gen_st_i32(tcg_src, tcg_env, vect_off); break; default: g_assert_not_reached(); @@ -1132,8 +1190,6 @@ static void do_vec_st(DisasContext *s, int srcidx, int element, read_vec_element(s, tcg_tmp, srcidx, element, mop & MO_SIZE); tcg_gen_qemu_st_i64(tcg_tmp, tcg_addr, get_mem_index(s), mop); - - tcg_temp_free_i64(tcg_tmp); } /* Load from memory to vector register */ @@ -1144,8 +1200,6 @@ static void do_vec_ld(DisasContext *s, int destidx, int element, tcg_gen_qemu_ld_i64(tcg_tmp, tcg_addr, get_mem_index(s), mop); write_vec_element(s, tcg_tmp, destidx, element, mop & MO_SIZE); - - tcg_temp_free_i64(tcg_tmp); } /* Check that FP/Neon access is enabled. If it is, return @@ -1155,35 +1209,109 @@ static void do_vec_ld(DisasContext *s, int destidx, int element, * unallocated-encoding checks (otherwise the syndrome information * for the resulting exception will be incorrect). */ -static bool fp_access_check(DisasContext *s) +static bool fp_access_check_only(DisasContext *s) { if (s->fp_excp_el) { assert(!s->fp_access_checked); s->fp_access_checked = true; - gen_exception_insn(s, s->pc_curr, EXCP_UDEF, - syn_fp_access_trap(1, 0xe, false), s->fp_excp_el); + gen_exception_insn_el(s, 0, EXCP_UDEF, + syn_fp_access_trap(1, 0xe, false, 0), + s->fp_excp_el); return false; } s->fp_access_checked = true; return true; } -/* Check that SVE access is enabled. If it is, return true. +static bool fp_access_check(DisasContext *s) +{ + if (!fp_access_check_only(s)) { + return false; + } + if (s->sme_trap_nonstreaming && s->is_nonstreaming) { + gen_exception_insn(s, 0, EXCP_UDEF, + syn_smetrap(SME_ET_Streaming, false)); + return false; + } + return true; +} + +/* + * Check that SVE access is enabled. If it is, return true. * If not, emit code to generate an appropriate exception and return false. + * This function corresponds to CheckSVEEnabled(). */ bool sve_access_check(DisasContext *s) { - if (s->sve_excp_el) { - assert(!s->sve_access_checked); - s->sve_access_checked = true; - - gen_exception_insn(s, s->pc_curr, EXCP_UDEF, - syn_sve_access_trap(), s->sve_excp_el); - return false; + if (s->pstate_sm || !dc_isar_feature(aa64_sve, s)) { + assert(dc_isar_feature(aa64_sme, s)); + if (!sme_sm_enabled_check(s)) { + goto fail_exit; + } + } else if (s->sve_excp_el) { + gen_exception_insn_el(s, 0, EXCP_UDEF, + syn_sve_access_trap(), s->sve_excp_el); + goto fail_exit; } s->sve_access_checked = true; return fp_access_check(s); + + fail_exit: + /* Assert that we only raise one exception per instruction. */ + assert(!s->sve_access_checked); + s->sve_access_checked = true; + return false; +} + +/* + * Check that SME access is enabled, raise an exception if not. + * Note that this function corresponds to CheckSMEAccess and is + * only used directly for cpregs. + */ +static bool sme_access_check(DisasContext *s) +{ + if (s->sme_excp_el) { + gen_exception_insn_el(s, 0, EXCP_UDEF, + syn_smetrap(SME_ET_AccessTrap, false), + s->sme_excp_el); + return false; + } + return true; +} + +/* This function corresponds to CheckSMEEnabled. */ +bool sme_enabled_check(DisasContext *s) +{ + /* + * Note that unlike sve_excp_el, we have not constrained sme_excp_el + * to be zero when fp_excp_el has priority. This is because we need + * sme_excp_el by itself for cpregs access checks. + */ + if (!s->fp_excp_el || s->sme_excp_el < s->fp_excp_el) { + s->fp_access_checked = true; + return sme_access_check(s); + } + return fp_access_check_only(s); +} + +/* Common subroutine for CheckSMEAnd*Enabled. */ +bool sme_enabled_check_with_svcr(DisasContext *s, unsigned req) +{ + if (!sme_enabled_check(s)) { + return false; + } + if (FIELD_EX64(req, SVCR, SM) && !s->pstate_sm) { + gen_exception_insn(s, 0, EXCP_UDEF, + syn_smetrap(SME_ET_NotStreaming, false)); + return false; + } + if (FIELD_EX64(req, SVCR, ZA) && !s->pstate_za) { + gen_exception_insn(s, 0, EXCP_UDEF, + syn_smetrap(SME_ET_InactiveZA, false)); + return false; + } + return true; } /* @@ -1197,41 +1325,8 @@ static void ext_and_shift_reg(TCGv_i64 tcg_out, TCGv_i64 tcg_in, int extsize = extract32(option, 0, 2); bool is_signed = extract32(option, 2, 1); - if (is_signed) { - switch (extsize) { - case 0: - tcg_gen_ext8s_i64(tcg_out, tcg_in); - break; - case 1: - tcg_gen_ext16s_i64(tcg_out, tcg_in); - break; - case 2: - tcg_gen_ext32s_i64(tcg_out, tcg_in); - break; - case 3: - tcg_gen_mov_i64(tcg_out, tcg_in); - break; - } - } else { - switch (extsize) { - case 0: - tcg_gen_ext8u_i64(tcg_out, tcg_in); - break; - case 1: - tcg_gen_ext16u_i64(tcg_out, tcg_in); - break; - case 2: - tcg_gen_ext32u_i64(tcg_out, tcg_in); - break; - case 3: - tcg_gen_mov_i64(tcg_out, tcg_in); - break; - } - } - - if (shift) { - tcg_gen_shli_i64(tcg_out, tcg_out, shift); - } + tcg_gen_ext_i64(tcg_out, tcg_in, extsize | (is_signed ? MO_SIGN : 0)); + tcg_gen_shli_i64(tcg_out, tcg_out, shift); } static inline void gen_check_sp_alignment(DisasContext *s) @@ -1280,314 +1375,515 @@ static inline AArch64DecodeFn *lookup_disas_fn(const AArch64DecodeTable *table, * match up with those in the manual. */ -/* Unconditional branch (immediate) - * 31 30 26 25 0 - * +----+-----------+-------------------------------------+ - * | op | 0 0 1 0 1 | imm26 | - * +----+-----------+-------------------------------------+ - */ -static void disas_uncond_b_imm(DisasContext *s, uint32_t insn) +static bool trans_B(DisasContext *s, arg_i *a) { - uint64_t addr = s->pc_curr + sextract32(insn, 0, 26) * 4; - - if (insn & (1U << 31)) { - /* BL Branch with link */ - tcg_gen_movi_i64(cpu_reg(s, 30), s->base.pc_next); - } - - /* B Branch / BL Branch with link */ reset_btype(s); - gen_goto_tb(s, 0, addr); + gen_goto_tb(s, 0, a->imm); + return true; } -/* Compare and branch (immediate) - * 31 30 25 24 23 5 4 0 - * +----+-------------+----+---------------------+--------+ - * | sf | 0 1 1 0 1 0 | op | imm19 | Rt | - * +----+-------------+----+---------------------+--------+ - */ -static void disas_comp_b_imm(DisasContext *s, uint32_t insn) +static bool trans_BL(DisasContext *s, arg_i *a) { - unsigned int sf, op, rt; - uint64_t addr; - TCGLabel *label_match; - TCGv_i64 tcg_cmp; - - sf = extract32(insn, 31, 1); - op = extract32(insn, 24, 1); /* 0: CBZ; 1: CBNZ */ - rt = extract32(insn, 0, 5); - addr = s->pc_curr + sextract32(insn, 5, 19) * 4; - - tcg_cmp = read_cpu_reg(s, rt, sf); - label_match = gen_new_label(); - + gen_pc_plus_diff(s, cpu_reg(s, 30), curr_insn_len(s)); reset_btype(s); - tcg_gen_brcondi_i64(op ? TCG_COND_NE : TCG_COND_EQ, - tcg_cmp, 0, label_match); - - gen_goto_tb(s, 0, s->base.pc_next); - gen_set_label(label_match); - gen_goto_tb(s, 1, addr); + gen_goto_tb(s, 0, a->imm); + return true; } -/* Test and branch (immediate) - * 31 30 25 24 23 19 18 5 4 0 - * +----+-------------+----+-------+-------------+------+ - * | b5 | 0 1 1 0 1 1 | op | b40 | imm14 | Rt | - * +----+-------------+----+-------+-------------+------+ - */ -static void disas_test_b_imm(DisasContext *s, uint32_t insn) + +static bool trans_CBZ(DisasContext *s, arg_cbz *a) { - unsigned int bit_pos, op, rt; - uint64_t addr; - TCGLabel *label_match; + DisasLabel match; TCGv_i64 tcg_cmp; - bit_pos = (extract32(insn, 31, 1) << 5) | extract32(insn, 19, 5); - op = extract32(insn, 24, 1); /* 0: TBZ; 1: TBNZ */ - addr = s->pc_curr + sextract32(insn, 5, 14) * 4; - rt = extract32(insn, 0, 5); + tcg_cmp = read_cpu_reg(s, a->rt, a->sf); + reset_btype(s); + + match = gen_disas_label(s); + tcg_gen_brcondi_i64(a->nz ? TCG_COND_NE : TCG_COND_EQ, + tcg_cmp, 0, match.label); + gen_goto_tb(s, 0, 4); + set_disas_label(s, match); + gen_goto_tb(s, 1, a->imm); + return true; +} + +static bool trans_TBZ(DisasContext *s, arg_tbz *a) +{ + DisasLabel match; + TCGv_i64 tcg_cmp; tcg_cmp = tcg_temp_new_i64(); - tcg_gen_andi_i64(tcg_cmp, cpu_reg(s, rt), (1ULL << bit_pos)); - label_match = gen_new_label(); + tcg_gen_andi_i64(tcg_cmp, cpu_reg(s, a->rt), 1ULL << a->bitpos); reset_btype(s); - tcg_gen_brcondi_i64(op ? TCG_COND_NE : TCG_COND_EQ, - tcg_cmp, 0, label_match); - tcg_temp_free_i64(tcg_cmp); - gen_goto_tb(s, 0, s->base.pc_next); - gen_set_label(label_match); - gen_goto_tb(s, 1, addr); + + match = gen_disas_label(s); + tcg_gen_brcondi_i64(a->nz ? TCG_COND_NE : TCG_COND_EQ, + tcg_cmp, 0, match.label); + gen_goto_tb(s, 0, 4); + set_disas_label(s, match); + gen_goto_tb(s, 1, a->imm); + return true; } -/* Conditional branch (immediate) - * 31 25 24 23 5 4 3 0 - * +---------------+----+---------------------+----+------+ - * | 0 1 0 1 0 1 0 | o1 | imm19 | o0 | cond | - * +---------------+----+---------------------+----+------+ - */ -static void disas_cond_b_imm(DisasContext *s, uint32_t insn) +static bool trans_B_cond(DisasContext *s, arg_B_cond *a) { - unsigned int cond; - uint64_t addr; - - if ((insn & (1 << 4)) || (insn & (1 << 24))) { - unallocated_encoding(s); - return; + /* BC.cond is only present with FEAT_HBC */ + if (a->c && !dc_isar_feature(aa64_hbc, s)) { + return false; } - addr = s->pc_curr + sextract32(insn, 5, 19) * 4; - cond = extract32(insn, 0, 4); - reset_btype(s); - if (cond < 0x0e) { + if (a->cond < 0x0e) { /* genuinely conditional branches */ - TCGLabel *label_match = gen_new_label(); - arm_gen_test_cc(cond, label_match); - gen_goto_tb(s, 0, s->base.pc_next); - gen_set_label(label_match); - gen_goto_tb(s, 1, addr); + DisasLabel match = gen_disas_label(s); + arm_gen_test_cc(a->cond, match.label); + gen_goto_tb(s, 0, 4); + set_disas_label(s, match); + gen_goto_tb(s, 1, a->imm); } else { /* 0xe and 0xf are both "always" conditions */ - gen_goto_tb(s, 0, addr); + gen_goto_tb(s, 0, a->imm); } + return true; } -/* HINT instruction group, including various allocated HINTs */ -static void handle_hint(DisasContext *s, uint32_t insn, - unsigned int op1, unsigned int op2, unsigned int crm) +static void set_btype_for_br(DisasContext *s, int rn) { - unsigned int selector = crm << 3 | op2; - - if (op1 != 3) { - unallocated_encoding(s); - return; - } - - switch (selector) { - case 0b00000: /* NOP */ - break; - case 0b00011: /* WFI */ - s->base.is_jmp = DISAS_WFI; - break; - case 0b00001: /* YIELD */ - /* When running in MTTCG we don't generate jumps to the yield and - * WFE helpers as it won't affect the scheduling of other vCPUs. - * If we wanted to more completely model WFE/SEV so we don't busy - * spin unnecessarily we would need to do something more involved. - */ - if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { - s->base.is_jmp = DISAS_YIELD; - } - break; - case 0b00010: /* WFE */ - if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { - s->base.is_jmp = DISAS_WFE; - } - break; - case 0b00100: /* SEV */ - case 0b00101: /* SEVL */ - case 0b00110: /* DGH */ - /* we treat all as NOP at least for now */ - break; - case 0b00111: /* XPACLRI */ - if (s->pauth_active) { - gen_helper_xpaci(cpu_X[30], cpu_env, cpu_X[30]); - } - break; - case 0b01000: /* PACIA1716 */ - if (s->pauth_active) { - gen_helper_pacia(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); - } - break; - case 0b01010: /* PACIB1716 */ - if (s->pauth_active) { - gen_helper_pacib(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); - } - break; - case 0b01100: /* AUTIA1716 */ - if (s->pauth_active) { - gen_helper_autia(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); - } - break; - case 0b01110: /* AUTIB1716 */ - if (s->pauth_active) { - gen_helper_autib(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); - } - break; - case 0b10000: /* ESB */ - /* Without RAS, we must implement this as NOP. */ - if (dc_isar_feature(aa64_ras, s)) { - /* - * QEMU does not have a source of physical SErrors, - * so we are only concerned with virtual SErrors. - * The pseudocode in the ARM for this case is - * if PSTATE.EL IN {EL0, EL1} && EL2Enabled() then - * AArch64.vESBOperation(); - * Most of the condition can be evaluated at translation time. - * Test for EL2 present, and defer test for SEL2 to runtime. - */ - if (s->current_el <= 1 && arm_dc_feature(s, ARM_FEATURE_EL2)) { - gen_helper_vesb(cpu_env); - } - } - break; - case 0b11000: /* PACIAZ */ - if (s->pauth_active) { - gen_helper_pacia(cpu_X[30], cpu_env, cpu_X[30], - new_tmp_a64_zero(s)); - } - break; - case 0b11001: /* PACIASP */ - if (s->pauth_active) { - gen_helper_pacia(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); - } - break; - case 0b11010: /* PACIBZ */ - if (s->pauth_active) { - gen_helper_pacib(cpu_X[30], cpu_env, cpu_X[30], - new_tmp_a64_zero(s)); - } - break; - case 0b11011: /* PACIBSP */ - if (s->pauth_active) { - gen_helper_pacib(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); - } - break; - case 0b11100: /* AUTIAZ */ - if (s->pauth_active) { - gen_helper_autia(cpu_X[30], cpu_env, cpu_X[30], - new_tmp_a64_zero(s)); - } - break; - case 0b11101: /* AUTIASP */ - if (s->pauth_active) { - gen_helper_autia(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); - } - break; - case 0b11110: /* AUTIBZ */ - if (s->pauth_active) { - gen_helper_autib(cpu_X[30], cpu_env, cpu_X[30], - new_tmp_a64_zero(s)); - } - break; - case 0b11111: /* AUTIBSP */ - if (s->pauth_active) { - gen_helper_autib(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); - } - break; - default: - /* default specified as NOP equivalent */ - break; + if (dc_isar_feature(aa64_bti, s)) { + /* BR to {x16,x17} or !guard -> 1, else 3. */ + set_btype(s, rn == 16 || rn == 17 || !s->guarded_page ? 1 : 3); } } -static void gen_clrex(DisasContext *s, uint32_t insn) +static void set_btype_for_blr(DisasContext *s) +{ + if (dc_isar_feature(aa64_bti, s)) { + /* BLR sets BTYPE to 2, regardless of source guarded page. */ + set_btype(s, 2); + } +} + +static bool trans_BR(DisasContext *s, arg_r *a) +{ + gen_a64_set_pc(s, cpu_reg(s, a->rn)); + set_btype_for_br(s, a->rn); + s->base.is_jmp = DISAS_JUMP; + return true; +} + +static bool trans_BLR(DisasContext *s, arg_r *a) +{ + TCGv_i64 dst = cpu_reg(s, a->rn); + TCGv_i64 lr = cpu_reg(s, 30); + if (dst == lr) { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_mov_i64(tmp, dst); + dst = tmp; + } + gen_pc_plus_diff(s, lr, curr_insn_len(s)); + gen_a64_set_pc(s, dst); + set_btype_for_blr(s); + s->base.is_jmp = DISAS_JUMP; + return true; +} + +static bool trans_RET(DisasContext *s, arg_r *a) +{ + gen_a64_set_pc(s, cpu_reg(s, a->rn)); + s->base.is_jmp = DISAS_JUMP; + return true; +} + +static TCGv_i64 auth_branch_target(DisasContext *s, TCGv_i64 dst, + TCGv_i64 modifier, bool use_key_a) +{ + TCGv_i64 truedst; + /* + * Return the branch target for a BRAA/RETA/etc, which is either + * just the destination dst, or that value with the pauth check + * done and the code removed from the high bits. + */ + if (!s->pauth_active) { + return dst; + } + + truedst = tcg_temp_new_i64(); + if (use_key_a) { + gen_helper_autia_combined(truedst, tcg_env, dst, modifier); + } else { + gen_helper_autib_combined(truedst, tcg_env, dst, modifier); + } + return truedst; +} + +static bool trans_BRAZ(DisasContext *s, arg_braz *a) +{ + TCGv_i64 dst; + + if (!dc_isar_feature(aa64_pauth, s)) { + return false; + } + + dst = auth_branch_target(s, cpu_reg(s, a->rn), tcg_constant_i64(0), !a->m); + gen_a64_set_pc(s, dst); + set_btype_for_br(s, a->rn); + s->base.is_jmp = DISAS_JUMP; + return true; +} + +static bool trans_BLRAZ(DisasContext *s, arg_braz *a) +{ + TCGv_i64 dst, lr; + + if (!dc_isar_feature(aa64_pauth, s)) { + return false; + } + + dst = auth_branch_target(s, cpu_reg(s, a->rn), tcg_constant_i64(0), !a->m); + lr = cpu_reg(s, 30); + if (dst == lr) { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_mov_i64(tmp, dst); + dst = tmp; + } + gen_pc_plus_diff(s, lr, curr_insn_len(s)); + gen_a64_set_pc(s, dst); + set_btype_for_blr(s); + s->base.is_jmp = DISAS_JUMP; + return true; +} + +static bool trans_RETA(DisasContext *s, arg_reta *a) +{ + TCGv_i64 dst; + + dst = auth_branch_target(s, cpu_reg(s, 30), cpu_X[31], !a->m); + gen_a64_set_pc(s, dst); + s->base.is_jmp = DISAS_JUMP; + return true; +} + +static bool trans_BRA(DisasContext *s, arg_bra *a) +{ + TCGv_i64 dst; + + if (!dc_isar_feature(aa64_pauth, s)) { + return false; + } + dst = auth_branch_target(s, cpu_reg(s,a->rn), cpu_reg_sp(s, a->rm), !a->m); + gen_a64_set_pc(s, dst); + set_btype_for_br(s, a->rn); + s->base.is_jmp = DISAS_JUMP; + return true; +} + +static bool trans_BLRA(DisasContext *s, arg_bra *a) +{ + TCGv_i64 dst, lr; + + if (!dc_isar_feature(aa64_pauth, s)) { + return false; + } + dst = auth_branch_target(s, cpu_reg(s, a->rn), cpu_reg_sp(s, a->rm), !a->m); + lr = cpu_reg(s, 30); + if (dst == lr) { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_mov_i64(tmp, dst); + dst = tmp; + } + gen_pc_plus_diff(s, lr, curr_insn_len(s)); + gen_a64_set_pc(s, dst); + set_btype_for_blr(s); + s->base.is_jmp = DISAS_JUMP; + return true; +} + +static bool trans_ERET(DisasContext *s, arg_ERET *a) +{ + TCGv_i64 dst; + + if (s->current_el == 0) { + return false; + } + if (s->trap_eret) { + gen_exception_insn_el(s, 0, EXCP_UDEF, syn_erettrap(0), 2); + return true; + } + dst = tcg_temp_new_i64(); + tcg_gen_ld_i64(dst, tcg_env, + offsetof(CPUARMState, elr_el[s->current_el])); + + translator_io_start(&s->base); + + gen_helper_exception_return(tcg_env, dst); + /* Must exit loop to check un-masked IRQs */ + s->base.is_jmp = DISAS_EXIT; + return true; +} + +static bool trans_ERETA(DisasContext *s, arg_reta *a) +{ + TCGv_i64 dst; + + if (!dc_isar_feature(aa64_pauth, s)) { + return false; + } + if (s->current_el == 0) { + return false; + } + /* The FGT trap takes precedence over an auth trap. */ + if (s->trap_eret) { + gen_exception_insn_el(s, 0, EXCP_UDEF, syn_erettrap(a->m ? 3 : 2), 2); + return true; + } + dst = tcg_temp_new_i64(); + tcg_gen_ld_i64(dst, tcg_env, + offsetof(CPUARMState, elr_el[s->current_el])); + + dst = auth_branch_target(s, dst, cpu_X[31], !a->m); + + translator_io_start(&s->base); + + gen_helper_exception_return(tcg_env, dst); + /* Must exit loop to check un-masked IRQs */ + s->base.is_jmp = DISAS_EXIT; + return true; +} + +static bool trans_NOP(DisasContext *s, arg_NOP *a) +{ + return true; +} + +static bool trans_YIELD(DisasContext *s, arg_YIELD *a) +{ + /* + * When running in MTTCG we don't generate jumps to the yield and + * WFE helpers as it won't affect the scheduling of other vCPUs. + * If we wanted to more completely model WFE/SEV so we don't busy + * spin unnecessarily we would need to do something more involved. + */ + if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { + s->base.is_jmp = DISAS_YIELD; + } + return true; +} + +static bool trans_WFI(DisasContext *s, arg_WFI *a) +{ + s->base.is_jmp = DISAS_WFI; + return true; +} + +static bool trans_WFE(DisasContext *s, arg_WFI *a) +{ + /* + * When running in MTTCG we don't generate jumps to the yield and + * WFE helpers as it won't affect the scheduling of other vCPUs. + * If we wanted to more completely model WFE/SEV so we don't busy + * spin unnecessarily we would need to do something more involved. + */ + if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { + s->base.is_jmp = DISAS_WFE; + } + return true; +} + +static bool trans_XPACLRI(DisasContext *s, arg_XPACLRI *a) +{ + if (s->pauth_active) { + gen_helper_xpaci(cpu_X[30], tcg_env, cpu_X[30]); + } + return true; +} + +static bool trans_PACIA1716(DisasContext *s, arg_PACIA1716 *a) +{ + if (s->pauth_active) { + gen_helper_pacia(cpu_X[17], tcg_env, cpu_X[17], cpu_X[16]); + } + return true; +} + +static bool trans_PACIB1716(DisasContext *s, arg_PACIB1716 *a) +{ + if (s->pauth_active) { + gen_helper_pacib(cpu_X[17], tcg_env, cpu_X[17], cpu_X[16]); + } + return true; +} + +static bool trans_AUTIA1716(DisasContext *s, arg_AUTIA1716 *a) +{ + if (s->pauth_active) { + gen_helper_autia(cpu_X[17], tcg_env, cpu_X[17], cpu_X[16]); + } + return true; +} + +static bool trans_AUTIB1716(DisasContext *s, arg_AUTIB1716 *a) +{ + if (s->pauth_active) { + gen_helper_autib(cpu_X[17], tcg_env, cpu_X[17], cpu_X[16]); + } + return true; +} + +static bool trans_ESB(DisasContext *s, arg_ESB *a) +{ + /* Without RAS, we must implement this as NOP. */ + if (dc_isar_feature(aa64_ras, s)) { + /* + * QEMU does not have a source of physical SErrors, + * so we are only concerned with virtual SErrors. + * The pseudocode in the ARM for this case is + * if PSTATE.EL IN {EL0, EL1} && EL2Enabled() then + * AArch64.vESBOperation(); + * Most of the condition can be evaluated at translation time. + * Test for EL2 present, and defer test for SEL2 to runtime. + */ + if (s->current_el <= 1 && arm_dc_feature(s, ARM_FEATURE_EL2)) { + gen_helper_vesb(tcg_env); + } + } + return true; +} + +static bool trans_PACIAZ(DisasContext *s, arg_PACIAZ *a) +{ + if (s->pauth_active) { + gen_helper_pacia(cpu_X[30], tcg_env, cpu_X[30], tcg_constant_i64(0)); + } + return true; +} + +static bool trans_PACIASP(DisasContext *s, arg_PACIASP *a) +{ + if (s->pauth_active) { + gen_helper_pacia(cpu_X[30], tcg_env, cpu_X[30], cpu_X[31]); + } + return true; +} + +static bool trans_PACIBZ(DisasContext *s, arg_PACIBZ *a) +{ + if (s->pauth_active) { + gen_helper_pacib(cpu_X[30], tcg_env, cpu_X[30], tcg_constant_i64(0)); + } + return true; +} + +static bool trans_PACIBSP(DisasContext *s, arg_PACIBSP *a) +{ + if (s->pauth_active) { + gen_helper_pacib(cpu_X[30], tcg_env, cpu_X[30], cpu_X[31]); + } + return true; +} + +static bool trans_AUTIAZ(DisasContext *s, arg_AUTIAZ *a) +{ + if (s->pauth_active) { + gen_helper_autia(cpu_X[30], tcg_env, cpu_X[30], tcg_constant_i64(0)); + } + return true; +} + +static bool trans_AUTIASP(DisasContext *s, arg_AUTIASP *a) +{ + if (s->pauth_active) { + gen_helper_autia(cpu_X[30], tcg_env, cpu_X[30], cpu_X[31]); + } + return true; +} + +static bool trans_AUTIBZ(DisasContext *s, arg_AUTIBZ *a) +{ + if (s->pauth_active) { + gen_helper_autib(cpu_X[30], tcg_env, cpu_X[30], tcg_constant_i64(0)); + } + return true; +} + +static bool trans_AUTIBSP(DisasContext *s, arg_AUTIBSP *a) +{ + if (s->pauth_active) { + gen_helper_autib(cpu_X[30], tcg_env, cpu_X[30], cpu_X[31]); + } + return true; +} + +static bool trans_CLREX(DisasContext *s, arg_CLREX *a) { tcg_gen_movi_i64(cpu_exclusive_addr, -1); + return true; } -/* CLREX, DSB, DMB, ISB */ -static void handle_sync(DisasContext *s, uint32_t insn, - unsigned int op1, unsigned int op2, unsigned int crm) +static bool trans_DSB_DMB(DisasContext *s, arg_DSB_DMB *a) { + /* We handle DSB and DMB the same way */ TCGBar bar; - if (op1 != 3) { - unallocated_encoding(s); - return; - } - - switch (op2) { - case 2: /* CLREX */ - gen_clrex(s, insn); - return; - case 4: /* DSB */ - case 5: /* DMB */ - switch (crm & 3) { - case 1: /* MBReqTypes_Reads */ - bar = TCG_BAR_SC | TCG_MO_LD_LD | TCG_MO_LD_ST; - break; - case 2: /* MBReqTypes_Writes */ - bar = TCG_BAR_SC | TCG_MO_ST_ST; - break; - default: /* MBReqTypes_All */ - bar = TCG_BAR_SC | TCG_MO_ALL; - break; - } - tcg_gen_mb(bar); - return; - case 6: /* ISB */ - /* We need to break the TB after this insn to execute - * a self-modified code correctly and also to take - * any pending interrupts immediately. - */ - reset_btype(s); - gen_goto_tb(s, 0, s->base.pc_next); - return; - - case 7: /* SB */ - if (crm != 0 || !dc_isar_feature(aa64_sb, s)) { - goto do_unallocated; - } - /* - * TODO: There is no speculation barrier opcode for TCG; - * MB and end the TB instead. - */ - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); - gen_goto_tb(s, 0, s->base.pc_next); - return; - - default: - do_unallocated: - unallocated_encoding(s); - return; + switch (a->types) { + case 1: /* MBReqTypes_Reads */ + bar = TCG_BAR_SC | TCG_MO_LD_LD | TCG_MO_LD_ST; + break; + case 2: /* MBReqTypes_Writes */ + bar = TCG_BAR_SC | TCG_MO_ST_ST; + break; + default: /* MBReqTypes_All */ + bar = TCG_BAR_SC | TCG_MO_ALL; + break; } + tcg_gen_mb(bar); + return true; } -static void gen_xaflag(void) +static bool trans_ISB(DisasContext *s, arg_ISB *a) { - TCGv_i32 z = tcg_temp_new_i32(); + /* + * We need to break the TB after this insn to execute + * self-modifying code correctly and also to take + * any pending interrupts immediately. + */ + reset_btype(s); + gen_goto_tb(s, 0, 4); + return true; +} + +static bool trans_SB(DisasContext *s, arg_SB *a) +{ + if (!dc_isar_feature(aa64_sb, s)) { + return false; + } + /* + * TODO: There is no speculation barrier opcode for TCG; + * MB and end the TB instead. + */ + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); + gen_goto_tb(s, 0, 4); + return true; +} + +static bool trans_CFINV(DisasContext *s, arg_CFINV *a) +{ + if (!dc_isar_feature(aa64_condm_4, s)) { + return false; + } + tcg_gen_xori_i32(cpu_CF, cpu_CF, 1); + return true; +} + +static bool trans_XAFLAG(DisasContext *s, arg_XAFLAG *a) +{ + TCGv_i32 z; + + if (!dc_isar_feature(aa64_condm_5, s)) { + return false; + } + + z = tcg_temp_new_i32(); tcg_gen_setcondi_i32(TCG_COND_EQ, z, cpu_ZF, 0); @@ -1612,11 +1908,15 @@ static void gen_xaflag(void) /* C | Z */ tcg_gen_or_i32(cpu_CF, cpu_CF, z); - tcg_temp_free_i32(z); + return true; } -static void gen_axflag(void) +static bool trans_AXFLAG(DisasContext *s, arg_AXFLAG *a) { + if (!dc_isar_feature(aa64_condm_5, s)) { + return false; + } + tcg_gen_sari_i32(cpu_VF, cpu_VF, 31); /* V ? -1 : 0 */ tcg_gen_andc_i32(cpu_CF, cpu_CF, cpu_VF); /* C & !V */ @@ -1625,131 +1925,134 @@ static void gen_axflag(void) tcg_gen_movi_i32(cpu_NF, 0); tcg_gen_movi_i32(cpu_VF, 0); + + return true; } -/* MSR (immediate) - move immediate to processor state field */ -static void handle_msr_i(DisasContext *s, uint32_t insn, - unsigned int op1, unsigned int op2, unsigned int crm) +static bool trans_MSR_i_UAO(DisasContext *s, arg_i *a) { - int op = op1 << 3 | op2; - - /* End the TB by default, chaining is ok. */ - s->base.is_jmp = DISAS_TOO_MANY; - - switch (op) { - case 0x00: /* CFINV */ - if (crm != 0 || !dc_isar_feature(aa64_condm_4, s)) { - goto do_unallocated; - } - tcg_gen_xori_i32(cpu_CF, cpu_CF, 1); - s->base.is_jmp = DISAS_NEXT; - break; - - case 0x01: /* XAFlag */ - if (crm != 0 || !dc_isar_feature(aa64_condm_5, s)) { - goto do_unallocated; - } - gen_xaflag(); - s->base.is_jmp = DISAS_NEXT; - break; - - case 0x02: /* AXFlag */ - if (crm != 0 || !dc_isar_feature(aa64_condm_5, s)) { - goto do_unallocated; - } - gen_axflag(); - 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); - } - gen_rebuild_hflags(s); - 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); - } - gen_rebuild_hflags(s); - break; - - case 0x05: /* SPSel */ - if (s->current_el == 0) { - goto do_unallocated; - } - gen_helper_msr_i_spsel(cpu_env, tcg_constant_i32(crm & PSTATE_SP)); - break; - - case 0x19: /* SSBS */ - if (!dc_isar_feature(aa64_ssbs, s)) { - goto do_unallocated; - } - if (crm & 1) { - set_pstate_bits(PSTATE_SSBS); - } else { - clear_pstate_bits(PSTATE_SSBS); - } - /* Don't need to rebuild hflags since SSBS is a nop */ - break; - - case 0x1a: /* DIT */ - if (!dc_isar_feature(aa64_dit, s)) { - goto do_unallocated; - } - if (crm & 1) { - set_pstate_bits(PSTATE_DIT); - } else { - clear_pstate_bits(PSTATE_DIT); - } - /* There's no need to rebuild hflags because DIT is a nop */ - break; - - case 0x1e: /* DAIFSet */ - gen_helper_msr_i_daifset(cpu_env, tcg_constant_i32(crm)); - break; - - case 0x1f: /* DAIFClear */ - gen_helper_msr_i_daifclear(cpu_env, tcg_constant_i32(crm)); - /* For DAIFClear, exit the cpu loop to re-evaluate pending IRQs. */ - s->base.is_jmp = DISAS_UPDATE_EXIT; - break; - - case 0x1c: /* TCO */ - if (dc_isar_feature(aa64_mte, s)) { - /* Full MTE is enabled -- set the TCO bit as directed. */ - if (crm & 1) { - set_pstate_bits(PSTATE_TCO); - } else { - clear_pstate_bits(PSTATE_TCO); - } - gen_rebuild_hflags(s); - /* Many factors, including TCO, go into MTE_ACTIVE. */ - s->base.is_jmp = DISAS_UPDATE_NOCHAIN; - } else if (dc_isar_feature(aa64_mte_insn_reg, s)) { - /* Only "instructions accessible at EL0" -- PSTATE.TCO is WI. */ - s->base.is_jmp = DISAS_NEXT; - } else { - goto do_unallocated; - } - break; - - default: - do_unallocated: - unallocated_encoding(s); - return; + if (!dc_isar_feature(aa64_uao, s) || s->current_el == 0) { + return false; } + if (a->imm & 1) { + set_pstate_bits(PSTATE_UAO); + } else { + clear_pstate_bits(PSTATE_UAO); + } + gen_rebuild_hflags(s); + s->base.is_jmp = DISAS_TOO_MANY; + return true; +} + +static bool trans_MSR_i_PAN(DisasContext *s, arg_i *a) +{ + if (!dc_isar_feature(aa64_pan, s) || s->current_el == 0) { + return false; + } + if (a->imm & 1) { + set_pstate_bits(PSTATE_PAN); + } else { + clear_pstate_bits(PSTATE_PAN); + } + gen_rebuild_hflags(s); + s->base.is_jmp = DISAS_TOO_MANY; + return true; +} + +static bool trans_MSR_i_SPSEL(DisasContext *s, arg_i *a) +{ + if (s->current_el == 0) { + return false; + } + gen_helper_msr_i_spsel(tcg_env, tcg_constant_i32(a->imm & PSTATE_SP)); + s->base.is_jmp = DISAS_TOO_MANY; + return true; +} + +static bool trans_MSR_i_SBSS(DisasContext *s, arg_i *a) +{ + if (!dc_isar_feature(aa64_ssbs, s)) { + return false; + } + if (a->imm & 1) { + set_pstate_bits(PSTATE_SSBS); + } else { + clear_pstate_bits(PSTATE_SSBS); + } + /* Don't need to rebuild hflags since SSBS is a nop */ + s->base.is_jmp = DISAS_TOO_MANY; + return true; +} + +static bool trans_MSR_i_DIT(DisasContext *s, arg_i *a) +{ + if (!dc_isar_feature(aa64_dit, s)) { + return false; + } + if (a->imm & 1) { + set_pstate_bits(PSTATE_DIT); + } else { + clear_pstate_bits(PSTATE_DIT); + } + /* There's no need to rebuild hflags because DIT is a nop */ + s->base.is_jmp = DISAS_TOO_MANY; + return true; +} + +static bool trans_MSR_i_TCO(DisasContext *s, arg_i *a) +{ + if (dc_isar_feature(aa64_mte, s)) { + /* Full MTE is enabled -- set the TCO bit as directed. */ + if (a->imm & 1) { + set_pstate_bits(PSTATE_TCO); + } else { + clear_pstate_bits(PSTATE_TCO); + } + gen_rebuild_hflags(s); + /* Many factors, including TCO, go into MTE_ACTIVE. */ + s->base.is_jmp = DISAS_UPDATE_NOCHAIN; + return true; + } else if (dc_isar_feature(aa64_mte_insn_reg, s)) { + /* Only "instructions accessible at EL0" -- PSTATE.TCO is WI. */ + return true; + } else { + /* Insn not present */ + return false; + } +} + +static bool trans_MSR_i_DAIFSET(DisasContext *s, arg_i *a) +{ + gen_helper_msr_i_daifset(tcg_env, tcg_constant_i32(a->imm)); + s->base.is_jmp = DISAS_TOO_MANY; + return true; +} + +static bool trans_MSR_i_DAIFCLEAR(DisasContext *s, arg_i *a) +{ + gen_helper_msr_i_daifclear(tcg_env, tcg_constant_i32(a->imm)); + /* Exit the cpu loop to re-evaluate pending IRQs. */ + s->base.is_jmp = DISAS_UPDATE_EXIT; + return true; +} + +static bool trans_MSR_i_SVCR(DisasContext *s, arg_MSR_i_SVCR *a) +{ + if (!dc_isar_feature(aa64_sme, s) || a->mask == 0) { + return false; + } + if (sme_access_check(s)) { + int old = s->pstate_sm | (s->pstate_za << 1); + int new = a->imm * 3; + + if ((old ^ new) & a->mask) { + /* At least one bit changes. */ + gen_helper_set_svcr(tcg_env, tcg_constant_i32(new), + tcg_constant_i32(a->mask)); + s->base.is_jmp = DISAS_TOO_MANY; + } + } + return true; } static void gen_get_nzcv(TCGv_i64 tcg_rt) @@ -1769,9 +2072,6 @@ static void gen_get_nzcv(TCGv_i64 tcg_rt) tcg_gen_deposit_i32(nzcv, nzcv, tmp, 28, 1); /* generate result */ tcg_gen_extu_i32_i64(tcg_rt, nzcv); - - tcg_temp_free_i32(nzcv); - tcg_temp_free_i32(tmp); } static void gen_set_nzcv(TCGv_i64 tcg_rt) @@ -1792,7 +2092,6 @@ static void gen_set_nzcv(TCGv_i64 tcg_rt) /* bit 28, V */ tcg_gen_andi_i32(cpu_VF, nzcv, (1 << 28)); tcg_gen_shli_i32(cpu_VF, cpu_VF, 3); - tcg_temp_free_i32(nzcv); } static void gen_sysreg_undef(DisasContext *s, bool isread, @@ -1815,8 +2114,7 @@ static void gen_sysreg_undef(DisasContext *s, bool isread, } else { syndrome = syn_uncategorized(); } - gen_exception_insn(s, s->pc_curr, EXCP_UDEF, syndrome, - default_exception_el(s)); + gen_exception_insn(s, 0, EXCP_UDEF, syndrome); } /* MRS - move from system register @@ -1826,16 +2124,38 @@ static void gen_sysreg_undef(DisasContext *s, bool isread, * These are all essentially the same insn in 'read' and 'write' * versions, with varying op0 fields. */ -static void handle_sys(DisasContext *s, uint32_t insn, bool isread, +static void handle_sys(DisasContext *s, bool isread, unsigned int op0, unsigned int op1, unsigned int op2, unsigned int crn, unsigned int crm, unsigned int rt) { - const ARMCPRegInfo *ri; + uint32_t key = ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, + crn, crm, op0, op1, op2); + const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key); + bool need_exit_tb = false; + bool nv_trap_to_el2 = false; + bool nv_redirect_reg = false; + bool skip_fp_access_checks = false; + bool nv2_mem_redirect = false; + TCGv_ptr tcg_ri = NULL; TCGv_i64 tcg_rt; + uint32_t syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread); - ri = get_arm_cp_reginfo(s->cp_regs, - ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, - crn, crm, op0, op1, op2)); + if (crn == 11 || crn == 15) { + /* + * Check for TIDCP trap, which must take precedence over + * the UNDEF for "no such register" etc. + */ + switch (s->current_el) { + case 0: + if (dc_isar_feature(aa64_tidcp1, s)) { + gen_helper_tidcp_el0(tcg_env, tcg_constant_i32(syndrome)); + } + break; + case 1: + gen_helper_tidcp_el1(tcg_env, tcg_constant_i32(syndrome)); + break; + } + } if (!ri) { /* Unknown register; this might be a guest error or a QEMU @@ -1848,22 +2168,69 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, return; } - /* Check access permissions */ - if (!cp_access_ok(s->current_el, ri, isread)) { - gen_sysreg_undef(s, isread, op0, op1, op2, crn, crm, rt); - return; + if (s->nv2 && ri->nv2_redirect_offset) { + /* + * Some registers always redirect to memory; some only do so if + * HCR_EL2.NV1 is 0, and some only if NV1 is 1 (these come in + * pairs which share an offset; see the table in R_CSRPQ). + */ + if (ri->nv2_redirect_offset & NV2_REDIR_NV1) { + nv2_mem_redirect = s->nv1; + } else if (ri->nv2_redirect_offset & NV2_REDIR_NO_NV1) { + nv2_mem_redirect = !s->nv1; + } else { + nv2_mem_redirect = true; + } } - if (ri->accessfn) { + /* Check access permissions */ + if (!cp_access_ok(s->current_el, ri, isread)) { + /* + * FEAT_NV/NV2 handling does not do the usual FP access checks + * for registers only accessible at EL2 (though it *does* do them + * for registers accessible at EL1). + */ + skip_fp_access_checks = true; + if (s->nv2 && (ri->type & ARM_CP_NV2_REDIRECT)) { + /* + * This is one of the few EL2 registers which should redirect + * to the equivalent EL1 register. We do that after running + * the EL2 register's accessfn. + */ + nv_redirect_reg = true; + assert(!nv2_mem_redirect); + } else if (nv2_mem_redirect) { + /* + * NV2 redirect-to-memory takes precedence over trap to EL2 or + * UNDEF to EL1. + */ + } else if (s->nv && arm_cpreg_traps_in_nv(ri)) { + /* + * This register / instruction exists and is an EL2 register, so + * we must trap to EL2 if accessed in nested virtualization EL1 + * instead of UNDEFing. We'll do that after the usual access checks. + * (This makes a difference only for a couple of registers like + * VSTTBR_EL2 where the "UNDEF if NonSecure" should take priority + * over the trap-to-EL2. Most trapped-by-FEAT_NV registers have + * an accessfn which does nothing when called from EL1, because + * the trap-to-EL3 controls which would apply to that register + * at EL2 don't take priority over the FEAT_NV trap-to-EL2.) + */ + nv_trap_to_el2 = true; + } else { + gen_sysreg_undef(s, isread, op0, op1, op2, crn, crm, rt); + return; + } + } + + if (ri->accessfn || (ri->fgt && s->fgt_active)) { /* Emit code to perform further access permissions checks at * runtime; this may result in an exception. */ - uint32_t syndrome; - - syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread); - gen_a64_set_pc_im(s->pc_curr); - gen_helper_access_check_cp_reg(cpu_env, - tcg_constant_ptr(ri), + gen_a64_update_pc(s, 0); + tcg_ri = tcg_temp_new_ptr(); + gen_helper_access_check_cp_reg(tcg_ri, tcg_env, + tcg_constant_i32(key), tcg_constant_i32(syndrome), tcg_constant_i32(isread)); } else if (ri->type & ARM_CP_RAISES_EXC) { @@ -1871,7 +2238,79 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, * The readfn or writefn might raise an exception; * synchronize the CPU state in case it does. */ - gen_a64_set_pc_im(s->pc_curr); + gen_a64_update_pc(s, 0); + } + + if (!skip_fp_access_checks) { + if ((ri->type & ARM_CP_FPU) && !fp_access_check_only(s)) { + return; + } else if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) { + return; + } else if ((ri->type & ARM_CP_SME) && !sme_access_check(s)) { + return; + } + } + + if (nv_trap_to_el2) { + gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2); + return; + } + + if (nv_redirect_reg) { + /* + * FEAT_NV2 redirection of an EL2 register to an EL1 register. + * Conveniently in all cases the encoding of the EL1 register is + * identical to the EL2 register except that opc1 is 0. + * Get the reginfo for the EL1 register to use for the actual access. + * We don't use the EL1 register's access function, and + * fine-grained-traps on EL1 also do not apply here. + */ + key = ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, + crn, crm, op0, 0, op2); + ri = get_arm_cp_reginfo(s->cp_regs, key); + assert(ri); + assert(cp_access_ok(s->current_el, ri, isread)); + /* + * We might not have done an update_pc earlier, so check we don't + * need it. We could support this in future if necessary. + */ + assert(!(ri->type & ARM_CP_RAISES_EXC)); + } + + if (nv2_mem_redirect) { + /* + * This system register is being redirected into an EL2 memory access. + * This means it is not an IO operation, doesn't change hflags, + * and need not end the TB, because it has no side effects. + * + * The access is 64-bit single copy atomic, guaranteed aligned because + * of the definition of VCNR_EL2. Its endianness depends on + * SCTLR_EL2.EE, not on the data endianness of EL1. + * It is done under either the EL2 translation regime or the EL2&0 + * translation regime, depending on HCR_EL2.E2H. It behaves as if + * PSTATE.PAN is 0. + */ + TCGv_i64 ptr = tcg_temp_new_i64(); + MemOp mop = MO_64 | MO_ALIGN | MO_ATOM_IFALIGN; + ARMMMUIdx armmemidx = s->nv2_mem_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_E2; + int memidx = arm_to_core_mmu_idx(armmemidx); + uint32_t syn; + + mop |= (s->nv2_mem_be ? MO_BE : MO_LE); + + tcg_gen_ld_i64(ptr, tcg_env, offsetof(CPUARMState, cp15.vncr_el2)); + tcg_gen_addi_i64(ptr, ptr, + (ri->nv2_redirect_offset & ~NV2_REDIR_FLAG_MASK)); + tcg_rt = cpu_reg(s, rt); + + syn = syn_data_abort_vncr(0, !isread, 0); + disas_set_insn_syndrome(s, syn); + if (isread) { + tcg_gen_qemu_ld_i64(tcg_rt, ptr, memidx, mop); + } else { + tcg_gen_qemu_st_i64(tcg_rt, ptr, memidx, mop); + } + return; } /* Handle special cases first */ @@ -1889,12 +2328,17 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, } return; case ARM_CP_CURRENTEL: - /* Reads as current EL value from pstate, which is + { + /* + * Reads as current EL value from pstate, which is * guaranteed to be constant by the tb flags. + * For nested virt we should report EL2. */ + int el = s->nv ? 2 : s->current_el; tcg_rt = cpu_reg(s, rt); - tcg_gen_movi_i64(tcg_rt, s->current_el << 2); + tcg_gen_movi_i64(tcg_rt, el << 2); return; + } case ARM_CP_DC_ZVA: /* Writes clear the aligned block of memory which rt points into. */ if (s->mte_active[0]) { @@ -1904,13 +2348,13 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); - tcg_rt = new_tmp_a64(s); - gen_helper_mte_check_zva(tcg_rt, cpu_env, + tcg_rt = tcg_temp_new_i64(); + gen_helper_mte_check_zva(tcg_rt, tcg_env, tcg_constant_i32(desc), cpu_reg(s, rt)); } else { tcg_rt = clean_data_tbi(s, cpu_reg(s, rt)); } - gen_helper_dc_zva(cpu_env, tcg_rt); + gen_helper_dc_zva(tcg_env, tcg_rt); return; case ARM_CP_DC_GVA: { @@ -1924,12 +2368,11 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, clean_addr = clean_data_tbi(s, tcg_rt); gen_probe_access(s, clean_addr, MMU_DATA_STORE, MO_8); - if (s->ata) { + if (s->ata[0]) { /* Extract the tag from the register to match STZGM. */ tag = tcg_temp_new_i64(); tcg_gen_shri_i64(tag, tcg_rt, 56); - gen_helper_stzgm_tags(cpu_env, clean_addr, tag); - tcg_temp_free_i64(tag); + gen_helper_stzgm_tags(tcg_env, clean_addr, tag); } } return; @@ -1940,28 +2383,23 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, /* For DC_GZVA, we can rely on DC_ZVA for the proper fault. */ tcg_rt = cpu_reg(s, rt); clean_addr = clean_data_tbi(s, tcg_rt); - gen_helper_dc_zva(cpu_env, clean_addr); + gen_helper_dc_zva(tcg_env, clean_addr); - if (s->ata) { + if (s->ata[0]) { /* Extract the tag from the register to match STZGM. */ tag = tcg_temp_new_i64(); tcg_gen_shri_i64(tag, tcg_rt, 56); - gen_helper_stzgm_tags(cpu_env, clean_addr, tag); - tcg_temp_free_i64(tag); + gen_helper_stzgm_tags(tcg_env, clean_addr, tag); } } return; default: g_assert_not_reached(); } - if ((ri->type & ARM_CP_FPU) && !fp_access_check(s)) { - return; - } else if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) { - return; - } - if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { - gen_io_start(); + if (ri->type & ARM_CP_IO) { + /* I/O operations must end the TB here (whether read or write) */ + need_exit_tb = translator_io_start(&s->base); } tcg_rt = cpu_reg(s, rt); @@ -1970,28 +2408,30 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, if (ri->type & ARM_CP_CONST) { tcg_gen_movi_i64(tcg_rt, ri->resetvalue); } else if (ri->readfn) { - gen_helper_get_cp_reg64(tcg_rt, cpu_env, tcg_constant_ptr(ri)); + if (!tcg_ri) { + tcg_ri = gen_lookup_cp_reg(key); + } + gen_helper_get_cp_reg64(tcg_rt, tcg_env, tcg_ri); } else { - tcg_gen_ld_i64(tcg_rt, cpu_env, ri->fieldoffset); + tcg_gen_ld_i64(tcg_rt, tcg_env, ri->fieldoffset); } } else { if (ri->type & ARM_CP_CONST) { /* If not forbidden by access permissions, treat as WI */ return; } else if (ri->writefn) { - gen_helper_set_cp_reg64(cpu_env, tcg_constant_ptr(ri), tcg_rt); + if (!tcg_ri) { + tcg_ri = gen_lookup_cp_reg(key); + } + gen_helper_set_cp_reg64(tcg_env, tcg_ri, tcg_rt); } else { - tcg_gen_st_i64(tcg_rt, cpu_env, ri->fieldoffset); + tcg_gen_st_i64(tcg_rt, tcg_env, ri->fieldoffset); } } - if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { - /* I/O operations must end the TB here (whether read or write) */ - s->base.is_jmp = DISAS_UPDATE_EXIT; - } 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. */ gen_rebuild_hflags(s); @@ -2000,381 +2440,92 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, * but allow this to be suppressed by the register definition * (usually only necessary to work around guest bugs). */ + need_exit_tb = true; + } + if (need_exit_tb) { s->base.is_jmp = DISAS_UPDATE_EXIT; } } -/* System - * 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0 - * +---------------------+---+-----+-----+-------+-------+-----+------+ - * | 1 1 0 1 0 1 0 1 0 0 | L | op0 | op1 | CRn | CRm | op2 | Rt | - * +---------------------+---+-----+-----+-------+-------+-----+------+ - */ -static void disas_system(DisasContext *s, uint32_t insn) +static bool trans_SYS(DisasContext *s, arg_SYS *a) { - unsigned int l, op0, op1, crn, crm, op2, rt; - l = extract32(insn, 21, 1); - op0 = extract32(insn, 19, 2); - op1 = extract32(insn, 16, 3); - crn = extract32(insn, 12, 4); - crm = extract32(insn, 8, 4); - op2 = extract32(insn, 5, 3); - rt = extract32(insn, 0, 5); - - if (op0 == 0) { - if (l || rt != 31) { - unallocated_encoding(s); - return; - } - switch (crn) { - case 2: /* HINT (including allocated hints like NOP, YIELD, etc) */ - handle_hint(s, insn, op1, op2, crm); - break; - case 3: /* CLREX, DSB, DMB, ISB */ - handle_sync(s, insn, op1, op2, crm); - break; - case 4: /* MSR (immediate) */ - handle_msr_i(s, insn, op1, op2, crm); - break; - default: - unallocated_encoding(s); - break; - } - return; - } - handle_sys(s, insn, l, op0, op1, op2, crn, crm, rt); + handle_sys(s, a->l, a->op0, a->op1, a->op2, a->crn, a->crm, a->rt); + return true; } -/* Exception generation - * - * 31 24 23 21 20 5 4 2 1 0 - * +-----------------+-----+------------------------+-----+----+ - * | 1 1 0 1 0 1 0 0 | opc | imm16 | op2 | LL | - * +-----------------------+------------------------+----------+ - */ -static void disas_exc(DisasContext *s, uint32_t insn) +static bool trans_SVC(DisasContext *s, arg_i *a) { - int opc = extract32(insn, 21, 3); - int op2_ll = extract32(insn, 0, 5); - int imm16 = extract32(insn, 5, 16); - - switch (opc) { - case 0: - /* For SVC, HVC and SMC we advance the single-step state - * machine before taking the exception. This is architecturally - * mandated, to ensure that single-stepping a system call - * instruction works properly. - */ - switch (op2_ll) { - case 1: /* SVC */ - gen_ss_advance(s); - gen_exception_insn(s, s->base.pc_next, EXCP_SWI, - syn_aa64_svc(imm16), default_exception_el(s)); - break; - case 2: /* HVC */ - if (s->current_el == 0) { - unallocated_encoding(s); - break; - } - /* The pre HVC helper handles cases when HVC gets trapped - * as an undefined insn by runtime configuration. - */ - gen_a64_set_pc_im(s->pc_curr); - gen_helper_pre_hvc(cpu_env); - gen_ss_advance(s); - gen_exception_insn(s, s->base.pc_next, EXCP_HVC, - syn_aa64_hvc(imm16), 2); - break; - case 3: /* SMC */ - if (s->current_el == 0) { - unallocated_encoding(s); - break; - } - gen_a64_set_pc_im(s->pc_curr); - gen_helper_pre_smc(cpu_env, tcg_constant_i32(syn_aa64_smc(imm16))); - gen_ss_advance(s); - gen_exception_insn(s, s->base.pc_next, EXCP_SMC, - syn_aa64_smc(imm16), 3); - break; - default: - unallocated_encoding(s); - break; - } - break; - case 1: - if (op2_ll != 0) { - unallocated_encoding(s); - break; - } - /* BRK */ - gen_exception_bkpt_insn(s, syn_aa64_bkpt(imm16)); - break; - case 2: - if (op2_ll != 0) { - unallocated_encoding(s); - break; - } - /* HLT. This has two purposes. - * Architecturally, it is an external halting debug instruction. - * Since QEMU doesn't implement external debug, we treat this as - * it is required for halting debug disabled: it will UNDEF. - * Secondly, "HLT 0xf000" is the A64 semihosting syscall instruction. - */ - if (semihosting_enabled() && imm16 == 0xf000) { -#ifndef CONFIG_USER_ONLY - /* In system mode, don't allow userspace access to semihosting, - * to provide some semblance of security (and for consistency - * with our 32-bit semihosting). - */ - if (s->current_el == 0) { - unallocated_encoding(s); - break; - } -#endif - gen_exception_internal_insn(s, s->pc_curr, EXCP_SEMIHOST); - } else { - unallocated_encoding(s); - } - break; - case 5: - if (op2_ll < 1 || op2_ll > 3) { - unallocated_encoding(s); - break; - } - /* DCPS1, DCPS2, DCPS3 */ - unallocated_encoding(s); - break; - default: - unallocated_encoding(s); - break; + /* + * For SVC, HVC and SMC we advance the single-step state + * machine before taking the exception. This is architecturally + * mandated, to ensure that single-stepping a system call + * instruction works properly. + */ + uint32_t syndrome = syn_aa64_svc(a->imm); + if (s->fgt_svc) { + gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2); + return true; } + gen_ss_advance(s); + gen_exception_insn(s, 4, EXCP_SWI, syndrome); + return true; } -/* Unconditional branch (register) - * 31 25 24 21 20 16 15 10 9 5 4 0 - * +---------------+-------+-------+-------+------+-------+ - * | 1 1 0 1 0 1 1 | opc | op2 | op3 | Rn | op4 | - * +---------------+-------+-------+-------+------+-------+ - */ -static void disas_uncond_b_reg(DisasContext *s, uint32_t insn) +static bool trans_HVC(DisasContext *s, arg_i *a) { - unsigned int opc, op2, op3, rn, op4; - unsigned btype_mod = 2; /* 0: BR, 1: BLR, 2: other */ - TCGv_i64 dst; - TCGv_i64 modifier; + int target_el = s->current_el == 3 ? 3 : 2; - opc = extract32(insn, 21, 4); - op2 = extract32(insn, 16, 5); - op3 = extract32(insn, 10, 6); - rn = extract32(insn, 5, 5); - op4 = extract32(insn, 0, 5); - - if (op2 != 0x1f) { - goto do_unallocated; - } - - switch (opc) { - case 0: /* BR */ - case 1: /* BLR */ - case 2: /* RET */ - btype_mod = opc; - switch (op3) { - case 0: - /* BR, BLR, RET */ - if (op4 != 0) { - goto do_unallocated; - } - dst = cpu_reg(s, rn); - break; - - case 2: - case 3: - if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - if (opc == 2) { - /* RETAA, RETAB */ - if (rn != 0x1f || op4 != 0x1f) { - goto do_unallocated; - } - rn = 30; - modifier = cpu_X[31]; - } else { - /* BRAAZ, BRABZ, BLRAAZ, BLRABZ */ - if (op4 != 0x1f) { - goto do_unallocated; - } - modifier = new_tmp_a64_zero(s); - } - if (s->pauth_active) { - dst = new_tmp_a64(s); - if (op3 == 2) { - gen_helper_autia(dst, cpu_env, cpu_reg(s, rn), modifier); - } else { - gen_helper_autib(dst, cpu_env, cpu_reg(s, rn), modifier); - } - } else { - dst = cpu_reg(s, rn); - } - break; - - default: - goto do_unallocated; - } - gen_a64_set_pc(s, dst); - /* BLR also needs to load return address */ - if (opc == 1) { - tcg_gen_movi_i64(cpu_reg(s, 30), s->base.pc_next); - } - break; - - case 8: /* BRAA */ - case 9: /* BLRAA */ - if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - if ((op3 & ~1) != 2) { - goto do_unallocated; - } - btype_mod = opc & 1; - if (s->pauth_active) { - dst = new_tmp_a64(s); - modifier = cpu_reg_sp(s, op4); - if (op3 == 2) { - gen_helper_autia(dst, cpu_env, cpu_reg(s, rn), modifier); - } else { - gen_helper_autib(dst, cpu_env, cpu_reg(s, rn), modifier); - } - } else { - dst = cpu_reg(s, rn); - } - gen_a64_set_pc(s, dst); - /* BLRAA also needs to load return address */ - if (opc == 9) { - tcg_gen_movi_i64(cpu_reg(s, 30), s->base.pc_next); - } - break; - - case 4: /* ERET */ - if (s->current_el == 0) { - goto do_unallocated; - } - switch (op3) { - case 0: /* ERET */ - if (op4 != 0) { - goto do_unallocated; - } - dst = tcg_temp_new_i64(); - tcg_gen_ld_i64(dst, cpu_env, - offsetof(CPUARMState, elr_el[s->current_el])); - break; - - case 2: /* ERETAA */ - case 3: /* ERETAB */ - if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - if (rn != 0x1f || op4 != 0x1f) { - goto do_unallocated; - } - dst = tcg_temp_new_i64(); - tcg_gen_ld_i64(dst, cpu_env, - offsetof(CPUARMState, elr_el[s->current_el])); - if (s->pauth_active) { - modifier = cpu_X[31]; - if (op3 == 2) { - gen_helper_autia(dst, cpu_env, dst, modifier); - } else { - gen_helper_autib(dst, cpu_env, dst, modifier); - } - } - break; - - default: - goto do_unallocated; - } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - - gen_helper_exception_return(cpu_env, dst); - tcg_temp_free_i64(dst); - /* Must exit loop to check un-masked IRQs */ - s->base.is_jmp = DISAS_EXIT; - return; - - case 5: /* DRPS */ - if (op3 != 0 || op4 != 0 || rn != 0x1f) { - goto do_unallocated; - } else { - unallocated_encoding(s); - } - return; - - default: - do_unallocated: + if (s->current_el == 0) { unallocated_encoding(s); - return; + return true; } - - switch (btype_mod) { - case 0: /* BR */ - if (dc_isar_feature(aa64_bti, s)) { - /* BR to {x16,x17} or !guard -> 1, else 3. */ - set_btype(s, rn == 16 || rn == 17 || !s->guarded_page ? 1 : 3); - } - break; - - case 1: /* BLR */ - if (dc_isar_feature(aa64_bti, s)) { - /* BLR sets BTYPE to 2, regardless of source guarded page. */ - set_btype(s, 2); - } - break; - - default: /* RET or none of the above. */ - /* BTYPE will be set to 0 by normal end-of-insn processing. */ - break; - } - - s->base.is_jmp = DISAS_JUMP; + /* + * The pre HVC helper handles cases when HVC gets trapped + * as an undefined insn by runtime configuration. + */ + gen_a64_update_pc(s, 0); + gen_helper_pre_hvc(tcg_env); + /* Architecture requires ss advance before we do the actual work */ + gen_ss_advance(s); + gen_exception_insn_el(s, 4, EXCP_HVC, syn_aa64_hvc(a->imm), target_el); + return true; } -/* Branches, exception generating and system instructions */ -static void disas_b_exc_sys(DisasContext *s, uint32_t insn) +static bool trans_SMC(DisasContext *s, arg_i *a) { - switch (extract32(insn, 25, 7)) { - case 0x0a: case 0x0b: - case 0x4a: case 0x4b: /* Unconditional branch (immediate) */ - disas_uncond_b_imm(s, insn); - break; - case 0x1a: case 0x5a: /* Compare & branch (immediate) */ - disas_comp_b_imm(s, insn); - break; - case 0x1b: case 0x5b: /* Test & branch (immediate) */ - disas_test_b_imm(s, insn); - break; - case 0x2a: /* Conditional branch (immediate) */ - disas_cond_b_imm(s, insn); - break; - case 0x6a: /* Exception generation / System */ - if (insn & (1 << 24)) { - if (extract32(insn, 22, 2) == 0) { - disas_system(s, insn); - } else { - unallocated_encoding(s); - } - } else { - disas_exc(s, insn); - } - break; - case 0x6b: /* Unconditional branch (register) */ - disas_uncond_b_reg(s, insn); - break; - default: + if (s->current_el == 0) { unallocated_encoding(s); - break; + return true; } + gen_a64_update_pc(s, 0); + gen_helper_pre_smc(tcg_env, tcg_constant_i32(syn_aa64_smc(a->imm))); + /* Architecture requires ss advance before we do the actual work */ + gen_ss_advance(s); + gen_exception_insn_el(s, 4, EXCP_SMC, syn_aa64_smc(a->imm), 3); + return true; +} + +static bool trans_BRK(DisasContext *s, arg_i *a) +{ + gen_exception_bkpt_insn(s, syn_aa64_bkpt(a->imm)); + return true; +} + +static bool trans_HLT(DisasContext *s, arg_i *a) +{ + /* + * HLT. This has two purposes. + * Architecturally, it is an external halting debug instruction. + * Since QEMU doesn't implement external debug, we treat this as + * it is required for halting debug disabled: it will UNDEF. + * Secondly, "HLT 0xf000" is the A64 semihosting syscall instruction. + */ + if (semihosting_enabled(s->current_el == 0) && a->imm == 0xf000) { + gen_exception_internal_insn(s, EXCP_SEMIHOST); + } else { + unallocated_encoding(s); + } + return true; } /* @@ -2388,19 +2539,22 @@ static void disas_b_exc_sys(DisasContext *s, uint32_t insn) * races in multi-threaded linux-user and when MTTCG softmmu is * enabled. */ -static void gen_load_exclusive(DisasContext *s, int rt, int rt2, - TCGv_i64 addr, int size, bool is_pair) +static void gen_load_exclusive(DisasContext *s, int rt, int rt2, int rn, + int size, bool is_pair) { int idx = get_mem_index(s); - MemOp memop = s->be_data; + TCGv_i64 dirty_addr, clean_addr; + MemOp memop = check_atomic_align(s, rn, size + is_pair); + + s->is_ldex = true; + dirty_addr = cpu_reg_sp(s, rn); + clean_addr = gen_mte_check1(s, dirty_addr, false, rn != 31, memop); g_assert(size <= 3); if (is_pair) { g_assert(size >= 2); if (size == 2) { - /* The pair must be single-copy atomic for the doubleword. */ - memop |= MO_64 | MO_ALIGN; - tcg_gen_qemu_ld_i64(cpu_exclusive_val, addr, idx, memop); + tcg_gen_qemu_ld_i64(cpu_exclusive_val, clean_addr, idx, memop); if (s->be_data == MO_LE) { tcg_gen_extract_i64(cpu_reg(s, rt), cpu_exclusive_val, 0, 32); tcg_gen_extract_i64(cpu_reg(s, rt2), cpu_exclusive_val, 32, 32); @@ -2409,30 +2563,29 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, tcg_gen_extract_i64(cpu_reg(s, rt2), cpu_exclusive_val, 0, 32); } } else { - /* The pair must be single-copy atomic for *each* doubleword, not - the entire quadword, however it must be quadword aligned. */ - memop |= MO_64; - tcg_gen_qemu_ld_i64(cpu_exclusive_val, addr, idx, - memop | MO_ALIGN_16); + TCGv_i128 t16 = tcg_temp_new_i128(); - TCGv_i64 addr2 = tcg_temp_new_i64(); - tcg_gen_addi_i64(addr2, addr, 8); - tcg_gen_qemu_ld_i64(cpu_exclusive_high, addr2, idx, memop); - tcg_temp_free_i64(addr2); + tcg_gen_qemu_ld_i128(t16, clean_addr, idx, memop); + if (s->be_data == MO_LE) { + tcg_gen_extr_i128_i64(cpu_exclusive_val, + cpu_exclusive_high, t16); + } else { + tcg_gen_extr_i128_i64(cpu_exclusive_high, + cpu_exclusive_val, t16); + } tcg_gen_mov_i64(cpu_reg(s, rt), cpu_exclusive_val); tcg_gen_mov_i64(cpu_reg(s, rt2), cpu_exclusive_high); } } else { - memop |= size | MO_ALIGN; - tcg_gen_qemu_ld_i64(cpu_exclusive_val, addr, idx, memop); + tcg_gen_qemu_ld_i64(cpu_exclusive_val, clean_addr, idx, memop); tcg_gen_mov_i64(cpu_reg(s, rt), cpu_exclusive_val); } - tcg_gen_mov_i64(cpu_exclusive_addr, addr); + tcg_gen_mov_i64(cpu_exclusive_addr, clean_addr); } static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, - TCGv_i64 addr, int size, int is_pair) + int rn, int size, int is_pair) { /* if (env->exclusive_addr == addr && env->exclusive_val == [addr] * && (!is_pair || env->exclusive_high == [addr + datasize])) { @@ -2448,9 +2601,46 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, */ TCGLabel *fail_label = gen_new_label(); TCGLabel *done_label = gen_new_label(); - TCGv_i64 tmp; + TCGv_i64 tmp, clean_addr; + MemOp memop; - tcg_gen_brcond_i64(TCG_COND_NE, addr, cpu_exclusive_addr, fail_label); + /* + * FIXME: We are out of spec here. We have recorded only the address + * from load_exclusive, not the entire range, and we assume that the + * size of the access on both sides match. The architecture allows the + * store to be smaller than the load, so long as the stored bytes are + * within the range recorded by the load. + */ + + /* See AArch64.ExclusiveMonitorsPass() and AArch64.IsExclusiveVA(). */ + clean_addr = clean_data_tbi(s, cpu_reg_sp(s, rn)); + tcg_gen_brcond_i64(TCG_COND_NE, clean_addr, cpu_exclusive_addr, fail_label); + + /* + * The write, and any associated faults, only happen if the virtual + * and physical addresses pass the exclusive monitor check. These + * faults are exceedingly unlikely, because normally the guest uses + * the exact same address register for the load_exclusive, and we + * would have recognized these faults there. + * + * It is possible to trigger an alignment fault pre-LSE2, e.g. with an + * unaligned 4-byte write within the range of an aligned 8-byte load. + * With LSE2, the store would need to cross a 16-byte boundary when the + * load did not, which would mean the store is outside the range + * recorded for the monitor, which would have failed a corrected monitor + * check above. For now, we assume no size change and retain the + * MO_ALIGN to let tcg know what we checked in the load_exclusive. + * + * It is possible to trigger an MTE fault, by performing the load with + * a virtual address with a valid tag and performing the store with the + * same virtual address and a different invalid tag. + */ + memop = size + is_pair; + if (memop == MO_128 || !dc_isar_feature(aa64_lse2, s)) { + memop |= MO_ALIGN; + } + memop = finalize_memop(s, memop); + gen_mte_check1(s, cpu_reg_sp(s, rn), true, rn != 31, memop); tmp = tcg_temp_new_i64(); if (is_pair) { @@ -2462,44 +2652,46 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, } tcg_gen_atomic_cmpxchg_i64(tmp, cpu_exclusive_addr, cpu_exclusive_val, tmp, - get_mem_index(s), - MO_64 | MO_ALIGN | s->be_data); + get_mem_index(s), memop); tcg_gen_setcond_i64(TCG_COND_NE, tmp, tmp, cpu_exclusive_val); - } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { - if (!HAVE_CMPXCHG128) { - gen_helper_exit_atomic(cpu_env); - /* - * Produce a result so we have a well-formed opcode - * stream when the following (dead) code uses 'tmp'. - * TCG will remove the dead ops for us. - */ - tcg_gen_movi_i64(tmp, 0); - } else if (s->be_data == MO_LE) { - gen_helper_paired_cmpxchg64_le_parallel(tmp, cpu_env, - cpu_exclusive_addr, - cpu_reg(s, rt), - cpu_reg(s, rt2)); - } else { - gen_helper_paired_cmpxchg64_be_parallel(tmp, cpu_env, - cpu_exclusive_addr, - cpu_reg(s, rt), - cpu_reg(s, rt2)); - } - } else if (s->be_data == MO_LE) { - gen_helper_paired_cmpxchg64_le(tmp, cpu_env, cpu_exclusive_addr, - cpu_reg(s, rt), cpu_reg(s, rt2)); } else { - gen_helper_paired_cmpxchg64_be(tmp, cpu_env, cpu_exclusive_addr, - cpu_reg(s, rt), cpu_reg(s, rt2)); + TCGv_i128 t16 = tcg_temp_new_i128(); + TCGv_i128 c16 = tcg_temp_new_i128(); + TCGv_i64 a, b; + + if (s->be_data == MO_LE) { + tcg_gen_concat_i64_i128(t16, cpu_reg(s, rt), cpu_reg(s, rt2)); + tcg_gen_concat_i64_i128(c16, cpu_exclusive_val, + cpu_exclusive_high); + } else { + tcg_gen_concat_i64_i128(t16, cpu_reg(s, rt2), cpu_reg(s, rt)); + tcg_gen_concat_i64_i128(c16, cpu_exclusive_high, + cpu_exclusive_val); + } + + tcg_gen_atomic_cmpxchg_i128(t16, cpu_exclusive_addr, c16, t16, + get_mem_index(s), memop); + + a = tcg_temp_new_i64(); + b = tcg_temp_new_i64(); + if (s->be_data == MO_LE) { + tcg_gen_extr_i128_i64(a, b, t16); + } else { + tcg_gen_extr_i128_i64(b, a, t16); + } + + tcg_gen_xor_i64(a, a, cpu_exclusive_val); + tcg_gen_xor_i64(b, b, cpu_exclusive_high); + tcg_gen_or_i64(tmp, a, b); + + tcg_gen_setcondi_i64(TCG_COND_NE, tmp, tmp, 0); } } else { tcg_gen_atomic_cmpxchg_i64(tmp, cpu_exclusive_addr, cpu_exclusive_val, - cpu_reg(s, rt), get_mem_index(s), - size | MO_ALIGN | s->be_data); + cpu_reg(s, rt), get_mem_index(s), memop); tcg_gen_setcond_i64(TCG_COND_NE, tmp, tmp, cpu_exclusive_val); } tcg_gen_mov_i64(cpu_reg(s, rd), tmp); - tcg_temp_free_i64(tmp); tcg_gen_br(done_label); gen_set_label(fail_label); @@ -2515,13 +2707,15 @@ static void gen_compare_and_swap(DisasContext *s, int rs, int rt, TCGv_i64 tcg_rt = cpu_reg(s, rt); int memidx = get_mem_index(s); TCGv_i64 clean_addr; + MemOp memop; if (rn == 31) { gen_check_sp_alignment(s); } - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), true, rn != 31, size); - tcg_gen_atomic_cmpxchg_i64(tcg_rs, clean_addr, tcg_rs, tcg_rt, memidx, - size | MO_ALIGN | s->be_data); + memop = check_atomic_align(s, rn, size); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), true, rn != 31, memop); + tcg_gen_atomic_cmpxchg_i64(tcg_rs, clean_addr, tcg_rs, tcg_rt, + memidx, memop); } static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, @@ -2533,13 +2727,15 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, TCGv_i64 t2 = cpu_reg(s, rt + 1); TCGv_i64 clean_addr; int memidx = get_mem_index(s); + MemOp memop; if (rn == 31) { gen_check_sp_alignment(s); } /* This is a single atomic access, despite the "pair". */ - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), true, rn != 31, size + 1); + memop = check_atomic_align(s, rn, size + 1); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), true, rn != 31, memop); if (size == 2) { TCGv_i64 cmp = tcg_temp_new_i64(); @@ -2553,1154 +2749,823 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, tcg_gen_concat32_i64(cmp, s2, s1); } - tcg_gen_atomic_cmpxchg_i64(cmp, clean_addr, cmp, val, memidx, - MO_64 | MO_ALIGN | s->be_data); - tcg_temp_free_i64(val); + tcg_gen_atomic_cmpxchg_i64(cmp, clean_addr, cmp, val, memidx, memop); if (s->be_data == MO_LE) { tcg_gen_extr32_i64(s1, s2, cmp); } else { tcg_gen_extr32_i64(s2, s1, cmp); } - tcg_temp_free_i64(cmp); - } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { - if (HAVE_CMPXCHG128) { - TCGv_i32 tcg_rs = tcg_constant_i32(rs); - if (s->be_data == MO_LE) { - gen_helper_casp_le_parallel(cpu_env, tcg_rs, - clean_addr, t1, t2); - } else { - gen_helper_casp_be_parallel(cpu_env, tcg_rs, - clean_addr, t1, t2); - } - } else { - gen_helper_exit_atomic(cpu_env); - s->base.is_jmp = DISAS_NORETURN; - } } else { - TCGv_i64 d1 = tcg_temp_new_i64(); - TCGv_i64 d2 = tcg_temp_new_i64(); - TCGv_i64 a2 = tcg_temp_new_i64(); - TCGv_i64 c1 = tcg_temp_new_i64(); - TCGv_i64 c2 = tcg_temp_new_i64(); - TCGv_i64 zero = tcg_constant_i64(0); + TCGv_i128 cmp = tcg_temp_new_i128(); + TCGv_i128 val = tcg_temp_new_i128(); - /* Load the two words, in memory order. */ - tcg_gen_qemu_ld_i64(d1, clean_addr, memidx, - MO_64 | MO_ALIGN_16 | s->be_data); - tcg_gen_addi_i64(a2, clean_addr, 8); - tcg_gen_qemu_ld_i64(d2, a2, memidx, MO_64 | s->be_data); + if (s->be_data == MO_LE) { + tcg_gen_concat_i64_i128(val, t1, t2); + tcg_gen_concat_i64_i128(cmp, s1, s2); + } else { + tcg_gen_concat_i64_i128(val, t2, t1); + tcg_gen_concat_i64_i128(cmp, s2, s1); + } - /* Compare the two words, also in memory order. */ - tcg_gen_setcond_i64(TCG_COND_EQ, c1, d1, s1); - tcg_gen_setcond_i64(TCG_COND_EQ, c2, d2, s2); - tcg_gen_and_i64(c2, c2, c1); + tcg_gen_atomic_cmpxchg_i128(cmp, clean_addr, cmp, val, memidx, memop); - /* If compare equal, write back new data, else write back old data. */ - tcg_gen_movcond_i64(TCG_COND_NE, c1, c2, zero, t1, d1); - tcg_gen_movcond_i64(TCG_COND_NE, c2, c2, zero, t2, d2); - tcg_gen_qemu_st_i64(c1, clean_addr, memidx, MO_64 | s->be_data); - tcg_gen_qemu_st_i64(c2, a2, memidx, MO_64 | s->be_data); - tcg_temp_free_i64(a2); - tcg_temp_free_i64(c1); - tcg_temp_free_i64(c2); - - /* Write back the data from memory to Rs. */ - tcg_gen_mov_i64(s1, d1); - tcg_gen_mov_i64(s2, d2); - tcg_temp_free_i64(d1); - tcg_temp_free_i64(d2); + if (s->be_data == MO_LE) { + tcg_gen_extr_i128_i64(s1, s2, cmp); + } else { + tcg_gen_extr_i128_i64(s2, s1, cmp); + } } } -/* Update the Sixty-Four bit (SF) registersize. This logic is derived +/* + * Compute the ISS.SF bit for syndrome information if an exception + * is taken on a load or store. This indicates whether the instruction + * is accessing a 32-bit or 64-bit register. This logic is derived * from the ARMv8 specs for LDR (Shared decode for all encodings). */ -static bool disas_ldst_compute_iss_sf(int size, bool is_signed, int opc) +static bool ldst_iss_sf(int size, bool sign, bool ext) { - int opc0 = extract32(opc, 0, 1); - int regsize; - if (is_signed) { - regsize = opc0 ? 32 : 64; - } else { - regsize = size == 3 ? 64 : 32; - } - return regsize == 64; -} - -/* Load/store exclusive - * - * 31 30 29 24 23 22 21 20 16 15 14 10 9 5 4 0 - * +-----+-------------+----+---+----+------+----+-------+------+------+ - * | sz | 0 0 1 0 0 0 | o2 | L | o1 | Rs | o0 | Rt2 | Rn | Rt | - * +-----+-------------+----+---+----+------+----+-------+------+------+ - * - * sz: 00 -> 8 bit, 01 -> 16 bit, 10 -> 32 bit, 11 -> 64 bit - * L: 0 -> store, 1 -> load - * o2: 0 -> exclusive, 1 -> not - * o1: 0 -> single register, 1 -> register pair - * o0: 1 -> load-acquire/store-release, 0 -> not - */ -static void disas_ldst_excl(DisasContext *s, uint32_t insn) -{ - int rt = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int rt2 = extract32(insn, 10, 5); - int rs = extract32(insn, 16, 5); - int is_lasr = extract32(insn, 15, 1); - int o2_L_o1_o0 = extract32(insn, 21, 3) * 2 | is_lasr; - int size = extract32(insn, 30, 2); - TCGv_i64 clean_addr; - - switch (o2_L_o1_o0) { - case 0x0: /* STXR */ - case 0x1: /* STLXR */ - if (rn == 31) { - gen_check_sp_alignment(s); - } - if (is_lasr) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - } - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), - true, rn != 31, size); - gen_store_exclusive(s, rs, rt, rt2, clean_addr, size, false); - return; - - case 0x4: /* LDXR */ - case 0x5: /* LDAXR */ - if (rn == 31) { - gen_check_sp_alignment(s); - } - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), - false, rn != 31, size); - s->is_ldex = true; - gen_load_exclusive(s, rt, rt2, clean_addr, size, false); - if (is_lasr) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); - } - return; - - case 0x8: /* STLLR */ - if (!dc_isar_feature(aa64_lor, s)) { - break; - } - /* StoreLORelease is the same as Store-Release for QEMU. */ - /* fall through */ - case 0x9: /* STLR */ - /* Generate ISS for non-exclusive accesses including LASR. */ - if (rn == 31) { - gen_check_sp_alignment(s); - } - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), - true, rn != 31, size); - /* TODO: ARMv8.4-LSE SCTLR.nAA */ - do_gpr_st(s, cpu_reg(s, rt), clean_addr, size | MO_ALIGN, true, rt, - disas_ldst_compute_iss_sf(size, false, 0), is_lasr); - return; - - case 0xc: /* LDLAR */ - if (!dc_isar_feature(aa64_lor, s)) { - break; - } - /* LoadLOAcquire is the same as Load-Acquire for QEMU. */ - /* fall through */ - case 0xd: /* LDAR */ - /* Generate ISS for non-exclusive accesses including LASR. */ - if (rn == 31) { - gen_check_sp_alignment(s); - } - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), - false, rn != 31, size); - /* TODO: ARMv8.4-LSE SCTLR.nAA */ - do_gpr_ld(s, cpu_reg(s, rt), clean_addr, size | MO_ALIGN, false, true, - rt, disas_ldst_compute_iss_sf(size, false, 0), is_lasr); - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); - return; - - case 0x2: case 0x3: /* CASP / STXP */ - if (size & 2) { /* STXP / STLXP */ - if (rn == 31) { - gen_check_sp_alignment(s); - } - if (is_lasr) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - } - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), - true, rn != 31, size); - gen_store_exclusive(s, rs, rt, rt2, clean_addr, size, true); - return; - } - if (rt2 == 31 - && ((rt | rs) & 1) == 0 - && dc_isar_feature(aa64_atomics, s)) { - /* CASP / CASPL */ - gen_compare_and_swap_pair(s, rs, rt, rn, size | 2); - return; - } - break; - - case 0x6: case 0x7: /* CASPA / LDXP */ - if (size & 2) { /* LDXP / LDAXP */ - if (rn == 31) { - gen_check_sp_alignment(s); - } - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), - false, rn != 31, size); - s->is_ldex = true; - gen_load_exclusive(s, rt, rt2, clean_addr, size, true); - if (is_lasr) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); - } - return; - } - if (rt2 == 31 - && ((rt | rs) & 1) == 0 - && dc_isar_feature(aa64_atomics, s)) { - /* CASPA / CASPAL */ - gen_compare_and_swap_pair(s, rs, rt, rn, size | 2); - return; - } - break; - - case 0xa: /* CAS */ - case 0xb: /* CASL */ - case 0xe: /* CASA */ - case 0xf: /* CASAL */ - if (rt2 == 31 && dc_isar_feature(aa64_atomics, s)) { - gen_compare_and_swap(s, rs, rt, rn, size); - return; - } - break; - } - unallocated_encoding(s); -} - -/* - * Load register (literal) - * - * 31 30 29 27 26 25 24 23 5 4 0 - * +-----+-------+---+-----+-------------------+-------+ - * | opc | 0 1 1 | V | 0 0 | imm19 | Rt | - * +-----+-------+---+-----+-------------------+-------+ - * - * V: 1 -> vector (simd/fp) - * opc (non-vector): 00 -> 32 bit, 01 -> 64 bit, - * 10-> 32 bit signed, 11 -> prefetch - * opc (vector): 00 -> 32 bit, 01 -> 64 bit, 10 -> 128 bit (11 unallocated) - */ -static void disas_ld_lit(DisasContext *s, uint32_t insn) -{ - int rt = extract32(insn, 0, 5); - int64_t imm = sextract32(insn, 5, 19) << 2; - bool is_vector = extract32(insn, 26, 1); - int opc = extract32(insn, 30, 2); - bool is_signed = false; - int size = 2; - TCGv_i64 tcg_rt, clean_addr; - - if (is_vector) { - if (opc == 3) { - unallocated_encoding(s); - return; - } - size = 2 + opc; - if (!fp_access_check(s)) { - return; - } - } else { - if (opc == 3) { - /* PRFM (literal) : prefetch */ - return; - } - size = 2 + extract32(opc, 0, 1); - is_signed = extract32(opc, 1, 1); - } - - tcg_rt = cpu_reg(s, rt); - - clean_addr = tcg_constant_i64(s->pc_curr + imm); - if (is_vector) { - do_fp_ld(s, rt, clean_addr, size); - } else { - /* Only unsigned 32bit loads target 32bit registers. */ - bool iss_sf = opc != 0; - - do_gpr_ld(s, tcg_rt, clean_addr, size + is_signed * MO_SIGN, - false, true, rt, iss_sf, false); - } -} - -/* - * LDNP (Load Pair - non-temporal hint) - * LDP (Load Pair - non vector) - * LDPSW (Load Pair Signed Word - non vector) - * STNP (Store Pair - non-temporal hint) - * STP (Store Pair - non vector) - * LDNP (Load Pair of SIMD&FP - non-temporal hint) - * LDP (Load Pair of SIMD&FP) - * STNP (Store Pair of SIMD&FP - non-temporal hint) - * STP (Store Pair of SIMD&FP) - * - * 31 30 29 27 26 25 24 23 22 21 15 14 10 9 5 4 0 - * +-----+-------+---+---+-------+---+-----------------------------+ - * | opc | 1 0 1 | V | 0 | index | L | imm7 | Rt2 | Rn | Rt | - * +-----+-------+---+---+-------+---+-------+-------+------+------+ - * - * opc: LDP/STP/LDNP/STNP 00 -> 32 bit, 10 -> 64 bit - * LDPSW/STGP 01 - * LDP/STP/LDNP/STNP (SIMD) 00 -> 32 bit, 01 -> 64 bit, 10 -> 128 bit - * V: 0 -> GPR, 1 -> Vector - * idx: 00 -> signed offset with non-temporal hint, 01 -> post-index, - * 10 -> signed offset, 11 -> pre-index - * L: 0 -> Store 1 -> Load - * - * Rt, Rt2 = GPR or SIMD registers to be stored - * Rn = general purpose register containing address - * imm7 = signed offset (multiple of 4 or 8 depending on size) - */ -static void disas_ldst_pair(DisasContext *s, uint32_t insn) -{ - int rt = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int rt2 = extract32(insn, 10, 5); - uint64_t offset = sextract64(insn, 15, 7); - int index = extract32(insn, 23, 2); - bool is_vector = extract32(insn, 26, 1); - bool is_load = extract32(insn, 22, 1); - int opc = extract32(insn, 30, 2); - - bool is_signed = false; - bool postindex = false; - bool wback = false; - bool set_tag = false; - - TCGv_i64 clean_addr, dirty_addr; - - int size; - - if (opc == 3) { - unallocated_encoding(s); - return; - } - - if (is_vector) { - size = 2 + opc; - } else if (opc == 1 && !is_load) { - /* STGP */ - if (!dc_isar_feature(aa64_mte_insn_reg, s) || index == 0) { - unallocated_encoding(s); - return; - } - size = 3; - set_tag = true; - } else { - size = 2 + extract32(opc, 1, 1); - is_signed = extract32(opc, 0, 1); - if (!is_load && is_signed) { - unallocated_encoding(s); - return; - } - } - - switch (index) { - case 1: /* post-index */ - postindex = true; - wback = true; - break; - case 0: - /* signed offset with "non-temporal" hint. Since we don't emulate - * caches we don't care about hints to the cache system about - * data access patterns, and handle this identically to plain - * signed offset. + if (sign) { + /* + * Signed loads are 64 bit results if we are not going to + * do a zero-extend from 32 to 64 after the load. + * (For a store, sign and ext are always false.) */ - if (is_signed) { - /* There is no non-temporal-hint version of LDPSW */ - unallocated_encoding(s); - return; - } - postindex = false; - break; - case 2: /* signed offset, rn not updated */ - postindex = false; - break; - case 3: /* pre-index */ - postindex = false; - wback = true; - break; + return !ext; + } else { + /* Unsigned loads/stores work at the specified size */ + return size == MO_64; + } +} + +static bool trans_STXR(DisasContext *s, arg_stxr *a) +{ + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + if (a->lasr) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + gen_store_exclusive(s, a->rs, a->rt, a->rt2, a->rn, a->sz, false); + return true; +} + +static bool trans_LDXR(DisasContext *s, arg_stxr *a) +{ + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + gen_load_exclusive(s, a->rt, a->rt2, a->rn, a->sz, false); + if (a->lasr) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } + return true; +} + +static bool trans_STLR(DisasContext *s, arg_stlr *a) +{ + TCGv_i64 clean_addr; + MemOp memop; + bool iss_sf = ldst_iss_sf(a->sz, false, false); + + /* + * StoreLORelease is the same as Store-Release for QEMU, but + * needs the feature-test. + */ + if (!a->lasr && !dc_isar_feature(aa64_lor, s)) { + return false; + } + /* Generate ISS for non-exclusive accesses including LASR. */ + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + memop = check_ordered_align(s, a->rn, 0, true, a->sz); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, a->rn), + true, a->rn != 31, memop); + do_gpr_st(s, cpu_reg(s, a->rt), clean_addr, memop, true, a->rt, + iss_sf, a->lasr); + return true; +} + +static bool trans_LDAR(DisasContext *s, arg_stlr *a) +{ + TCGv_i64 clean_addr; + MemOp memop; + bool iss_sf = ldst_iss_sf(a->sz, false, false); + + /* LoadLOAcquire is the same as Load-Acquire for QEMU. */ + if (!a->lasr && !dc_isar_feature(aa64_lor, s)) { + return false; + } + /* Generate ISS for non-exclusive accesses including LASR. */ + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + memop = check_ordered_align(s, a->rn, 0, false, a->sz); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, a->rn), + false, a->rn != 31, memop); + do_gpr_ld(s, cpu_reg(s, a->rt), clean_addr, memop, false, true, + a->rt, iss_sf, a->lasr); + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + return true; +} + +static bool trans_STXP(DisasContext *s, arg_stxr *a) +{ + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + if (a->lasr) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + gen_store_exclusive(s, a->rs, a->rt, a->rt2, a->rn, a->sz, true); + return true; +} + +static bool trans_LDXP(DisasContext *s, arg_stxr *a) +{ + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + gen_load_exclusive(s, a->rt, a->rt2, a->rn, a->sz, true); + if (a->lasr) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } + return true; +} + +static bool trans_CASP(DisasContext *s, arg_CASP *a) +{ + if (!dc_isar_feature(aa64_atomics, s)) { + return false; + } + if (((a->rt | a->rs) & 1) != 0) { + return false; } - if (is_vector && !fp_access_check(s)) { - return; + gen_compare_and_swap_pair(s, a->rs, a->rt, a->rn, a->sz); + return true; +} + +static bool trans_CAS(DisasContext *s, arg_CAS *a) +{ + if (!dc_isar_feature(aa64_atomics, s)) { + return false; } + gen_compare_and_swap(s, a->rs, a->rt, a->rn, a->sz); + return true; +} - offset <<= (set_tag ? LOG2_TAG_GRANULE : size); +static bool trans_LD_lit(DisasContext *s, arg_ldlit *a) +{ + bool iss_sf = ldst_iss_sf(a->sz, a->sign, false); + TCGv_i64 tcg_rt = cpu_reg(s, a->rt); + TCGv_i64 clean_addr = tcg_temp_new_i64(); + MemOp memop = finalize_memop(s, a->sz + a->sign * MO_SIGN); - if (rn == 31) { + gen_pc_plus_diff(s, clean_addr, a->imm); + do_gpr_ld(s, tcg_rt, clean_addr, memop, + false, true, a->rt, iss_sf, false); + return true; +} + +static bool trans_LD_lit_v(DisasContext *s, arg_ldlit *a) +{ + /* Load register (literal), vector version */ + TCGv_i64 clean_addr; + MemOp memop; + + if (!fp_access_check(s)) { + return true; + } + memop = finalize_memop_asimd(s, a->sz); + clean_addr = tcg_temp_new_i64(); + gen_pc_plus_diff(s, clean_addr, a->imm); + do_fp_ld(s, a->rt, clean_addr, memop); + return true; +} + +static void op_addr_ldstpair_pre(DisasContext *s, arg_ldstpair *a, + TCGv_i64 *clean_addr, TCGv_i64 *dirty_addr, + uint64_t offset, bool is_store, MemOp mop) +{ + if (a->rn == 31) { gen_check_sp_alignment(s); } - dirty_addr = read_cpu_reg_sp(s, rn, 1); - if (!postindex) { + *dirty_addr = read_cpu_reg_sp(s, a->rn, 1); + if (!a->p) { + tcg_gen_addi_i64(*dirty_addr, *dirty_addr, offset); + } + + *clean_addr = gen_mte_checkN(s, *dirty_addr, is_store, + (a->w || a->rn != 31), 2 << a->sz, mop); +} + +static void op_addr_ldstpair_post(DisasContext *s, arg_ldstpair *a, + TCGv_i64 dirty_addr, uint64_t offset) +{ + if (a->w) { + if (a->p) { + tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); + } + tcg_gen_mov_i64(cpu_reg_sp(s, a->rn), dirty_addr); + } +} + +static bool trans_STP(DisasContext *s, arg_ldstpair *a) +{ + uint64_t offset = a->imm << a->sz; + TCGv_i64 clean_addr, dirty_addr, tcg_rt, tcg_rt2; + MemOp mop = finalize_memop(s, a->sz); + + op_addr_ldstpair_pre(s, a, &clean_addr, &dirty_addr, offset, true, mop); + tcg_rt = cpu_reg(s, a->rt); + tcg_rt2 = cpu_reg(s, a->rt2); + /* + * We built mop above for the single logical access -- rebuild it + * now for the paired operation. + * + * With LSE2, non-sign-extending pairs are treated atomically if + * aligned, and if unaligned one of the pair will be completely + * within a 16-byte block and that element will be atomic. + * Otherwise each element is separately atomic. + * In all cases, issue one operation with the correct atomicity. + */ + mop = a->sz + 1; + if (s->align_mem) { + mop |= (a->sz == 2 ? MO_ALIGN_4 : MO_ALIGN_8); + } + mop = finalize_memop_pair(s, mop); + if (a->sz == 2) { + TCGv_i64 tmp = tcg_temp_new_i64(); + + if (s->be_data == MO_LE) { + tcg_gen_concat32_i64(tmp, tcg_rt, tcg_rt2); + } else { + tcg_gen_concat32_i64(tmp, tcg_rt2, tcg_rt); + } + tcg_gen_qemu_st_i64(tmp, clean_addr, get_mem_index(s), mop); + } else { + TCGv_i128 tmp = tcg_temp_new_i128(); + + if (s->be_data == MO_LE) { + tcg_gen_concat_i64_i128(tmp, tcg_rt, tcg_rt2); + } else { + tcg_gen_concat_i64_i128(tmp, tcg_rt2, tcg_rt); + } + tcg_gen_qemu_st_i128(tmp, clean_addr, get_mem_index(s), mop); + } + op_addr_ldstpair_post(s, a, dirty_addr, offset); + return true; +} + +static bool trans_LDP(DisasContext *s, arg_ldstpair *a) +{ + uint64_t offset = a->imm << a->sz; + TCGv_i64 clean_addr, dirty_addr, tcg_rt, tcg_rt2; + MemOp mop = finalize_memop(s, a->sz); + + op_addr_ldstpair_pre(s, a, &clean_addr, &dirty_addr, offset, false, mop); + tcg_rt = cpu_reg(s, a->rt); + tcg_rt2 = cpu_reg(s, a->rt2); + + /* + * We built mop above for the single logical access -- rebuild it + * now for the paired operation. + * + * With LSE2, non-sign-extending pairs are treated atomically if + * aligned, and if unaligned one of the pair will be completely + * within a 16-byte block and that element will be atomic. + * Otherwise each element is separately atomic. + * In all cases, issue one operation with the correct atomicity. + * + * This treats sign-extending loads like zero-extending loads, + * since that reuses the most code below. + */ + mop = a->sz + 1; + if (s->align_mem) { + mop |= (a->sz == 2 ? MO_ALIGN_4 : MO_ALIGN_8); + } + mop = finalize_memop_pair(s, mop); + if (a->sz == 2) { + int o2 = s->be_data == MO_LE ? 32 : 0; + int o1 = o2 ^ 32; + + tcg_gen_qemu_ld_i64(tcg_rt, clean_addr, get_mem_index(s), mop); + if (a->sign) { + tcg_gen_sextract_i64(tcg_rt2, tcg_rt, o2, 32); + tcg_gen_sextract_i64(tcg_rt, tcg_rt, o1, 32); + } else { + tcg_gen_extract_i64(tcg_rt2, tcg_rt, o2, 32); + tcg_gen_extract_i64(tcg_rt, tcg_rt, o1, 32); + } + } else { + TCGv_i128 tmp = tcg_temp_new_i128(); + + tcg_gen_qemu_ld_i128(tmp, clean_addr, get_mem_index(s), mop); + if (s->be_data == MO_LE) { + tcg_gen_extr_i128_i64(tcg_rt, tcg_rt2, tmp); + } else { + tcg_gen_extr_i128_i64(tcg_rt2, tcg_rt, tmp); + } + } + op_addr_ldstpair_post(s, a, dirty_addr, offset); + return true; +} + +static bool trans_STP_v(DisasContext *s, arg_ldstpair *a) +{ + uint64_t offset = a->imm << a->sz; + TCGv_i64 clean_addr, dirty_addr; + MemOp mop; + + if (!fp_access_check(s)) { + return true; + } + + /* LSE2 does not merge FP pairs; leave these as separate operations. */ + mop = finalize_memop_asimd(s, a->sz); + op_addr_ldstpair_pre(s, a, &clean_addr, &dirty_addr, offset, true, mop); + do_fp_st(s, a->rt, clean_addr, mop); + tcg_gen_addi_i64(clean_addr, clean_addr, 1 << a->sz); + do_fp_st(s, a->rt2, clean_addr, mop); + op_addr_ldstpair_post(s, a, dirty_addr, offset); + return true; +} + +static bool trans_LDP_v(DisasContext *s, arg_ldstpair *a) +{ + uint64_t offset = a->imm << a->sz; + TCGv_i64 clean_addr, dirty_addr; + MemOp mop; + + if (!fp_access_check(s)) { + return true; + } + + /* LSE2 does not merge FP pairs; leave these as separate operations. */ + mop = finalize_memop_asimd(s, a->sz); + op_addr_ldstpair_pre(s, a, &clean_addr, &dirty_addr, offset, false, mop); + do_fp_ld(s, a->rt, clean_addr, mop); + tcg_gen_addi_i64(clean_addr, clean_addr, 1 << a->sz); + do_fp_ld(s, a->rt2, clean_addr, mop); + op_addr_ldstpair_post(s, a, dirty_addr, offset); + return true; +} + +static bool trans_STGP(DisasContext *s, arg_ldstpair *a) +{ + TCGv_i64 clean_addr, dirty_addr, tcg_rt, tcg_rt2; + uint64_t offset = a->imm << LOG2_TAG_GRANULE; + MemOp mop; + TCGv_i128 tmp; + + /* STGP only comes in one size. */ + tcg_debug_assert(a->sz == MO_64); + + if (!dc_isar_feature(aa64_mte_insn_reg, s)) { + return false; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + dirty_addr = read_cpu_reg_sp(s, a->rn, 1); + if (!a->p) { tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); } - if (set_tag) { - if (!s->ata) { - /* - * TODO: We could rely on the stores below, at least for - * system mode, if we arrange to add MO_ALIGN_16. - */ - gen_helper_stg_stub(cpu_env, dirty_addr); - } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { - gen_helper_stg_parallel(cpu_env, dirty_addr, dirty_addr); - } else { - gen_helper_stg(cpu_env, dirty_addr, dirty_addr); - } - } + clean_addr = clean_data_tbi(s, dirty_addr); + tcg_rt = cpu_reg(s, a->rt); + tcg_rt2 = cpu_reg(s, a->rt2); - clean_addr = gen_mte_checkN(s, dirty_addr, !is_load, - (wback || rn != 31) && !set_tag, 2 << size); + /* + * STGP is defined as two 8-byte memory operations, aligned to TAG_GRANULE, + * and one tag operation. We implement it as one single aligned 16-byte + * memory operation for convenience. Note that the alignment ensures + * MO_ATOM_IFALIGN_PAIR produces 8-byte atomicity for the memory store. + */ + mop = finalize_memop_atom(s, MO_128 | MO_ALIGN, MO_ATOM_IFALIGN_PAIR); - if (is_vector) { - if (is_load) { - do_fp_ld(s, rt, clean_addr, size); - } else { - do_fp_st(s, rt, clean_addr, size); - } - tcg_gen_addi_i64(clean_addr, clean_addr, 1 << size); - if (is_load) { - do_fp_ld(s, rt2, clean_addr, size); - } else { - do_fp_st(s, rt2, clean_addr, size); - } + tmp = tcg_temp_new_i128(); + if (s->be_data == MO_LE) { + tcg_gen_concat_i64_i128(tmp, tcg_rt, tcg_rt2); } else { - TCGv_i64 tcg_rt = cpu_reg(s, rt); - TCGv_i64 tcg_rt2 = cpu_reg(s, rt2); + tcg_gen_concat_i64_i128(tmp, tcg_rt2, tcg_rt); + } + tcg_gen_qemu_st_i128(tmp, clean_addr, get_mem_index(s), mop); - if (is_load) { - TCGv_i64 tmp = tcg_temp_new_i64(); - - /* Do not modify tcg_rt before recognizing any exception - * from the second load. - */ - do_gpr_ld(s, tmp, clean_addr, size + is_signed * MO_SIGN, - false, false, 0, false, false); - tcg_gen_addi_i64(clean_addr, clean_addr, 1 << size); - do_gpr_ld(s, tcg_rt2, clean_addr, size + is_signed * MO_SIGN, - false, false, 0, false, false); - - tcg_gen_mov_i64(tcg_rt, tmp); - tcg_temp_free_i64(tmp); + /* Perform the tag store, if tag access enabled. */ + if (s->ata[0]) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { + gen_helper_stg_parallel(tcg_env, dirty_addr, dirty_addr); } else { - do_gpr_st(s, tcg_rt, clean_addr, size, - false, 0, false, false); - tcg_gen_addi_i64(clean_addr, clean_addr, 1 << size); - do_gpr_st(s, tcg_rt2, clean_addr, size, - false, 0, false, false); + gen_helper_stg(tcg_env, dirty_addr, dirty_addr); } } - if (wback) { - if (postindex) { - tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); - } - tcg_gen_mov_i64(cpu_reg_sp(s, rn), dirty_addr); - } + op_addr_ldstpair_post(s, a, dirty_addr, offset); + return true; } -/* - * Load/store (immediate post-indexed) - * Load/store (immediate pre-indexed) - * Load/store (unscaled immediate) - * - * 31 30 29 27 26 25 24 23 22 21 20 12 11 10 9 5 4 0 - * +----+-------+---+-----+-----+---+--------+-----+------+------+ - * |size| 1 1 1 | V | 0 0 | opc | 0 | imm9 | idx | Rn | Rt | - * +----+-------+---+-----+-----+---+--------+-----+------+------+ - * - * idx = 01 -> post-indexed, 11 pre-indexed, 00 unscaled imm. (no writeback) - 10 -> unprivileged - * V = 0 -> non-vector - * size: 00 -> 8 bit, 01 -> 16 bit, 10 -> 32 bit, 11 -> 64bit - * opc: 00 -> store, 01 -> loadu, 10 -> loads 64, 11 -> loads 32 - */ -static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, - int opc, - int size, - int rt, - bool is_vector) +static void op_addr_ldst_imm_pre(DisasContext *s, arg_ldst_imm *a, + TCGv_i64 *clean_addr, TCGv_i64 *dirty_addr, + uint64_t offset, bool is_store, MemOp mop) { - int rn = extract32(insn, 5, 5); - int imm9 = sextract32(insn, 12, 9); - int idx = extract32(insn, 10, 2); - bool is_signed = false; - bool is_store = false; - bool is_extended = false; - bool is_unpriv = (idx == 2); - bool iss_valid = !is_vector; - bool post_index; - bool writeback; int memidx; + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + *dirty_addr = read_cpu_reg_sp(s, a->rn, 1); + if (!a->p) { + tcg_gen_addi_i64(*dirty_addr, *dirty_addr, offset); + } + memidx = get_a64_user_mem_index(s, a->unpriv); + *clean_addr = gen_mte_check1_mmuidx(s, *dirty_addr, is_store, + a->w || a->rn != 31, + mop, a->unpriv, memidx); +} + +static void op_addr_ldst_imm_post(DisasContext *s, arg_ldst_imm *a, + TCGv_i64 dirty_addr, uint64_t offset) +{ + if (a->w) { + if (a->p) { + tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); + } + tcg_gen_mov_i64(cpu_reg_sp(s, a->rn), dirty_addr); + } +} + +static bool trans_STR_i(DisasContext *s, arg_ldst_imm *a) +{ + bool iss_sf, iss_valid = !a->w; + TCGv_i64 clean_addr, dirty_addr, tcg_rt; + int memidx = get_a64_user_mem_index(s, a->unpriv); + MemOp mop = finalize_memop(s, a->sz + a->sign * MO_SIGN); + + op_addr_ldst_imm_pre(s, a, &clean_addr, &dirty_addr, a->imm, true, mop); + + tcg_rt = cpu_reg(s, a->rt); + iss_sf = ldst_iss_sf(a->sz, a->sign, a->ext); + + do_gpr_st_memidx(s, tcg_rt, clean_addr, mop, memidx, + iss_valid, a->rt, iss_sf, false); + op_addr_ldst_imm_post(s, a, dirty_addr, a->imm); + return true; +} + +static bool trans_LDR_i(DisasContext *s, arg_ldst_imm *a) +{ + bool iss_sf, iss_valid = !a->w; + TCGv_i64 clean_addr, dirty_addr, tcg_rt; + int memidx = get_a64_user_mem_index(s, a->unpriv); + MemOp mop = finalize_memop(s, a->sz + a->sign * MO_SIGN); + + op_addr_ldst_imm_pre(s, a, &clean_addr, &dirty_addr, a->imm, false, mop); + + tcg_rt = cpu_reg(s, a->rt); + iss_sf = ldst_iss_sf(a->sz, a->sign, a->ext); + + do_gpr_ld_memidx(s, tcg_rt, clean_addr, mop, + a->ext, memidx, iss_valid, a->rt, iss_sf, false); + op_addr_ldst_imm_post(s, a, dirty_addr, a->imm); + return true; +} + +static bool trans_STR_v_i(DisasContext *s, arg_ldst_imm *a) +{ TCGv_i64 clean_addr, dirty_addr; + MemOp mop; - if (is_vector) { - size |= (opc & 2) << 1; - if (size > 4 || is_unpriv) { - unallocated_encoding(s); - return; - } - is_store = ((opc & 1) == 0); - if (!fp_access_check(s)) { - return; - } - } else { - if (size == 3 && opc == 2) { - /* PRFM - prefetch */ - if (idx != 0) { - unallocated_encoding(s); - return; - } - return; - } - if (opc == 3 && size > 1) { - unallocated_encoding(s); - return; - } - is_store = (opc == 0); - is_signed = extract32(opc, 1, 1); - is_extended = (size < 3) && extract32(opc, 0, 1); - } - - switch (idx) { - case 0: - case 2: - post_index = false; - writeback = false; - break; - case 1: - post_index = true; - writeback = true; - break; - case 3: - post_index = false; - writeback = true; - break; - default: - g_assert_not_reached(); - } - - if (rn == 31) { - gen_check_sp_alignment(s); - } - - dirty_addr = read_cpu_reg_sp(s, rn, 1); - if (!post_index) { - tcg_gen_addi_i64(dirty_addr, dirty_addr, imm9); - } - - memidx = is_unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); - clean_addr = gen_mte_check1_mmuidx(s, dirty_addr, is_store, - writeback || rn != 31, - size, is_unpriv, memidx); - - if (is_vector) { - if (is_store) { - do_fp_st(s, rt, clean_addr, size); - } else { - do_fp_ld(s, rt, clean_addr, size); - } - } else { - TCGv_i64 tcg_rt = cpu_reg(s, rt); - bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc); - - if (is_store) { - do_gpr_st_memidx(s, tcg_rt, clean_addr, size, memidx, - iss_valid, rt, iss_sf, false); - } else { - do_gpr_ld_memidx(s, tcg_rt, clean_addr, size + is_signed * MO_SIGN, - is_extended, memidx, - iss_valid, rt, iss_sf, false); - } - } - - if (writeback) { - TCGv_i64 tcg_rn = cpu_reg_sp(s, rn); - if (post_index) { - tcg_gen_addi_i64(dirty_addr, dirty_addr, imm9); - } - tcg_gen_mov_i64(tcg_rn, dirty_addr); + if (!fp_access_check(s)) { + return true; } + mop = finalize_memop_asimd(s, a->sz); + op_addr_ldst_imm_pre(s, a, &clean_addr, &dirty_addr, a->imm, true, mop); + do_fp_st(s, a->rt, clean_addr, mop); + op_addr_ldst_imm_post(s, a, dirty_addr, a->imm); + return true; } -/* - * Load/store (register offset) - * - * 31 30 29 27 26 25 24 23 22 21 20 16 15 13 12 11 10 9 5 4 0 - * +----+-------+---+-----+-----+---+------+-----+--+-----+----+----+ - * |size| 1 1 1 | V | 0 0 | opc | 1 | Rm | opt | S| 1 0 | Rn | Rt | - * +----+-------+---+-----+-----+---+------+-----+--+-----+----+----+ - * - * For non-vector: - * size: 00-> byte, 01 -> 16 bit, 10 -> 32bit, 11 -> 64bit - * opc: 00 -> store, 01 -> loadu, 10 -> loads 64, 11 -> loads 32 - * For vector: - * size is opc<1>:size<1:0> so 100 -> 128 bit; 110 and 111 unallocated - * opc<0>: 0 -> store, 1 -> load - * V: 1 -> vector/simd - * opt: extend encoding (see DecodeRegExtend) - * S: if S=1 then scale (essentially index by sizeof(size)) - * Rt: register to transfer into/out of - * Rn: address register or SP for base - * Rm: offset register or ZR for offset - */ -static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, - int opc, - int size, - int rt, - bool is_vector) +static bool trans_LDR_v_i(DisasContext *s, arg_ldst_imm *a) { - int rn = extract32(insn, 5, 5); - int shift = extract32(insn, 12, 1); - int rm = extract32(insn, 16, 5); - int opt = extract32(insn, 13, 3); - bool is_signed = false; - bool is_store = false; - bool is_extended = false; - - TCGv_i64 tcg_rm, clean_addr, dirty_addr; - - if (extract32(opt, 1, 1) == 0) { - unallocated_encoding(s); - return; - } - - if (is_vector) { - size |= (opc & 2) << 1; - if (size > 4) { - unallocated_encoding(s); - return; - } - is_store = !extract32(opc, 0, 1); - if (!fp_access_check(s)) { - return; - } - } else { - if (size == 3 && opc == 2) { - /* PRFM - prefetch */ - return; - } - if (opc == 3 && size > 1) { - unallocated_encoding(s); - return; - } - is_store = (opc == 0); - is_signed = extract32(opc, 1, 1); - is_extended = (size < 3) && extract32(opc, 0, 1); - } - - if (rn == 31) { - gen_check_sp_alignment(s); - } - dirty_addr = read_cpu_reg_sp(s, rn, 1); - - tcg_rm = read_cpu_reg(s, rm, 1); - ext_and_shift_reg(tcg_rm, tcg_rm, opt, shift ? size : 0); - - tcg_gen_add_i64(dirty_addr, dirty_addr, tcg_rm); - clean_addr = gen_mte_check1(s, dirty_addr, is_store, true, size); - - if (is_vector) { - if (is_store) { - do_fp_st(s, rt, clean_addr, size); - } else { - do_fp_ld(s, rt, clean_addr, size); - } - } else { - TCGv_i64 tcg_rt = cpu_reg(s, rt); - bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc); - if (is_store) { - do_gpr_st(s, tcg_rt, clean_addr, size, - true, rt, iss_sf, false); - } else { - do_gpr_ld(s, tcg_rt, clean_addr, size + is_signed * MO_SIGN, - is_extended, true, rt, iss_sf, false); - } - } -} - -/* - * Load/store (unsigned immediate) - * - * 31 30 29 27 26 25 24 23 22 21 10 9 5 - * +----+-------+---+-----+-----+------------+-------+------+ - * |size| 1 1 1 | V | 0 1 | opc | imm12 | Rn | Rt | - * +----+-------+---+-----+-----+------------+-------+------+ - * - * For non-vector: - * size: 00-> byte, 01 -> 16 bit, 10 -> 32bit, 11 -> 64bit - * opc: 00 -> store, 01 -> loadu, 10 -> loads 64, 11 -> loads 32 - * For vector: - * size is opc<1>:size<1:0> so 100 -> 128 bit; 110 and 111 unallocated - * opc<0>: 0 -> store, 1 -> load - * Rn: base address register (inc SP) - * Rt: target register - */ -static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, - int opc, - int size, - int rt, - bool is_vector) -{ - int rn = extract32(insn, 5, 5); - unsigned int imm12 = extract32(insn, 10, 12); - unsigned int offset; - TCGv_i64 clean_addr, dirty_addr; + MemOp mop; - bool is_store; - bool is_signed = false; - bool is_extended = false; - - if (is_vector) { - size |= (opc & 2) << 1; - if (size > 4) { - unallocated_encoding(s); - return; - } - is_store = !extract32(opc, 0, 1); - if (!fp_access_check(s)) { - return; - } - } else { - if (size == 3 && opc == 2) { - /* PRFM - prefetch */ - return; - } - if (opc == 3 && size > 1) { - unallocated_encoding(s); - return; - } - is_store = (opc == 0); - is_signed = extract32(opc, 1, 1); - is_extended = (size < 3) && extract32(opc, 0, 1); - } - - if (rn == 31) { - gen_check_sp_alignment(s); - } - dirty_addr = read_cpu_reg_sp(s, rn, 1); - offset = imm12 << size; - tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); - clean_addr = gen_mte_check1(s, dirty_addr, is_store, rn != 31, size); - - if (is_vector) { - if (is_store) { - do_fp_st(s, rt, clean_addr, size); - } else { - do_fp_ld(s, rt, clean_addr, size); - } - } else { - TCGv_i64 tcg_rt = cpu_reg(s, rt); - bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc); - if (is_store) { - do_gpr_st(s, tcg_rt, clean_addr, size, - true, rt, iss_sf, false); - } else { - do_gpr_ld(s, tcg_rt, clean_addr, size + is_signed * MO_SIGN, - is_extended, true, rt, iss_sf, false); - } + if (!fp_access_check(s)) { + return true; } + mop = finalize_memop_asimd(s, a->sz); + op_addr_ldst_imm_pre(s, a, &clean_addr, &dirty_addr, a->imm, false, mop); + do_fp_ld(s, a->rt, clean_addr, mop); + op_addr_ldst_imm_post(s, a, dirty_addr, a->imm); + return true; } -/* Atomic memory operations - * - * 31 30 27 26 24 22 21 16 15 12 10 5 0 - * +------+-------+---+-----+-----+---+----+----+-----+-----+----+-----+ - * | size | 1 1 1 | V | 0 0 | A R | 1 | Rs | o3 | opc | 0 0 | Rn | Rt | - * +------+-------+---+-----+-----+--------+----+-----+-----+----+-----+ - * - * Rt: the result register - * Rn: base address or SP - * Rs: the source register for the operation - * V: vector flag (always 0 as of v8.3) - * A: acquire flag - * R: release flag - */ -static void disas_ldst_atomic(DisasContext *s, uint32_t insn, - int size, int rt, bool is_vector) +static void op_addr_ldst_pre(DisasContext *s, arg_ldst *a, + TCGv_i64 *clean_addr, TCGv_i64 *dirty_addr, + bool is_store, MemOp memop) { - 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, tcg_rt, clean_addr; - AtomicThreeOpFn *fn = NULL; - MemOp mop = s->be_data | size | MO_ALIGN; + TCGv_i64 tcg_rm; - if (is_vector || !dc_isar_feature(aa64_atomics, s)) { - unallocated_encoding(s); - return; - } - switch (o3_opc) { - case 000: /* LDADD */ - fn = tcg_gen_atomic_fetch_add_i64; - break; - case 001: /* LDCLR */ - fn = tcg_gen_atomic_fetch_and_i64; - break; - case 002: /* LDEOR */ - fn = tcg_gen_atomic_fetch_xor_i64; - break; - case 003: /* LDSET */ - fn = tcg_gen_atomic_fetch_or_i64; - break; - case 004: /* LDSMAX */ - fn = tcg_gen_atomic_fetch_smax_i64; - mop |= MO_SIGN; - break; - case 005: /* LDSMIN */ - fn = tcg_gen_atomic_fetch_smin_i64; - mop |= MO_SIGN; - break; - case 006: /* LDUMAX */ - fn = tcg_gen_atomic_fetch_umax_i64; - break; - case 007: /* LDUMIN */ - fn = tcg_gen_atomic_fetch_umin_i64; - break; - 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; - } - - if (rn == 31) { + if (a->rn == 31) { gen_check_sp_alignment(s); } - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), false, rn != 31, size); + *dirty_addr = read_cpu_reg_sp(s, a->rn, 1); - 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, - true, rt, disas_ldst_compute_iss_sf(size, false, 0), true); - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); - return; + tcg_rm = read_cpu_reg(s, a->rm, 1); + ext_and_shift_reg(tcg_rm, tcg_rm, a->opt, a->s ? a->sz : 0); + + tcg_gen_add_i64(*dirty_addr, *dirty_addr, tcg_rm); + *clean_addr = gen_mte_check1(s, *dirty_addr, is_store, true, memop); +} + +static bool trans_LDR(DisasContext *s, arg_ldst *a) +{ + TCGv_i64 clean_addr, dirty_addr, tcg_rt; + bool iss_sf = ldst_iss_sf(a->sz, a->sign, a->ext); + MemOp memop; + + if (extract32(a->opt, 1, 1) == 0) { + return false; } - tcg_rs = read_cpu_reg(s, rs, true); - tcg_rt = cpu_reg(s, rt); + memop = finalize_memop(s, a->sz + a->sign * MO_SIGN); + op_addr_ldst_pre(s, a, &clean_addr, &dirty_addr, false, memop); + tcg_rt = cpu_reg(s, a->rt); + do_gpr_ld(s, tcg_rt, clean_addr, memop, + a->ext, true, a->rt, iss_sf, false); + return true; +} - if (o3_opc == 1) { /* LDCLR */ +static bool trans_STR(DisasContext *s, arg_ldst *a) +{ + TCGv_i64 clean_addr, dirty_addr, tcg_rt; + bool iss_sf = ldst_iss_sf(a->sz, a->sign, a->ext); + MemOp memop; + + if (extract32(a->opt, 1, 1) == 0) { + return false; + } + + memop = finalize_memop(s, a->sz); + op_addr_ldst_pre(s, a, &clean_addr, &dirty_addr, true, memop); + tcg_rt = cpu_reg(s, a->rt); + do_gpr_st(s, tcg_rt, clean_addr, memop, true, a->rt, iss_sf, false); + return true; +} + +static bool trans_LDR_v(DisasContext *s, arg_ldst *a) +{ + TCGv_i64 clean_addr, dirty_addr; + MemOp memop; + + if (extract32(a->opt, 1, 1) == 0) { + return false; + } + + if (!fp_access_check(s)) { + return true; + } + + memop = finalize_memop_asimd(s, a->sz); + op_addr_ldst_pre(s, a, &clean_addr, &dirty_addr, false, memop); + do_fp_ld(s, a->rt, clean_addr, memop); + return true; +} + +static bool trans_STR_v(DisasContext *s, arg_ldst *a) +{ + TCGv_i64 clean_addr, dirty_addr; + MemOp memop; + + if (extract32(a->opt, 1, 1) == 0) { + return false; + } + + if (!fp_access_check(s)) { + return true; + } + + memop = finalize_memop_asimd(s, a->sz); + op_addr_ldst_pre(s, a, &clean_addr, &dirty_addr, true, memop); + do_fp_st(s, a->rt, clean_addr, memop); + return true; +} + + +static bool do_atomic_ld(DisasContext *s, arg_atomic *a, AtomicThreeOpFn *fn, + int sign, bool invert) +{ + MemOp mop = a->sz | sign; + TCGv_i64 clean_addr, tcg_rs, tcg_rt; + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + mop = check_atomic_align(s, a->rn, mop); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, a->rn), false, + a->rn != 31, mop); + tcg_rs = read_cpu_reg(s, a->rs, true); + tcg_rt = cpu_reg(s, a->rt); + if (invert) { tcg_gen_not_i64(tcg_rs, tcg_rs); } - - /* The tcg atomic primitives are all full barriers. Therefore we + /* + * The tcg atomic primitives are all full barriers. Therefore we * can ignore the Acquire and Release bits of this instruction. */ fn(tcg_rt, clean_addr, tcg_rs, get_mem_index(s), mop); - if ((mop & MO_SIGN) && size != MO_64) { - tcg_gen_ext32u_i64(tcg_rt, tcg_rt); + if (mop & MO_SIGN) { + switch (a->sz) { + case MO_8: + tcg_gen_ext8u_i64(tcg_rt, tcg_rt); + break; + case MO_16: + tcg_gen_ext16u_i64(tcg_rt, tcg_rt); + break; + case MO_32: + tcg_gen_ext32u_i64(tcg_rt, tcg_rt); + break; + case MO_64: + break; + default: + g_assert_not_reached(); + } } + return true; } -/* - * PAC memory operations - * - * 31 30 27 26 24 22 21 12 11 10 5 0 - * +------+-------+---+-----+-----+---+--------+---+---+----+-----+ - * | size | 1 1 1 | V | 0 0 | M S | 1 | imm9 | W | 1 | Rn | Rt | - * +------+-------+---+-----+-----+---+--------+---+---+----+-----+ - * - * Rt: the result register - * Rn: base address or SP - * V: vector flag (always 0 as of v8.3) - * M: clear for key DA, set for key DB - * W: pre-indexing flag - * S: sign for imm9. - */ -static void disas_ldst_pac(DisasContext *s, uint32_t insn, - int size, int rt, bool is_vector) +TRANS_FEAT(LDADD, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_add_i64, 0, false) +TRANS_FEAT(LDCLR, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_and_i64, 0, true) +TRANS_FEAT(LDEOR, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_xor_i64, 0, false) +TRANS_FEAT(LDSET, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_or_i64, 0, false) +TRANS_FEAT(LDSMAX, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_smax_i64, MO_SIGN, false) +TRANS_FEAT(LDSMIN, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_smin_i64, MO_SIGN, false) +TRANS_FEAT(LDUMAX, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_umax_i64, 0, false) +TRANS_FEAT(LDUMIN, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_umin_i64, 0, false) +TRANS_FEAT(SWP, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_xchg_i64, 0, false) + +static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a) { - int rn = extract32(insn, 5, 5); - bool is_wback = extract32(insn, 11, 1); - bool use_key_a = !extract32(insn, 23, 1); - int offset; - TCGv_i64 clean_addr, dirty_addr, tcg_rt; + bool iss_sf = ldst_iss_sf(a->sz, false, false); + TCGv_i64 clean_addr; + MemOp mop; - if (size != 3 || is_vector || !dc_isar_feature(aa64_pauth, s)) { - unallocated_encoding(s); - return; + if (!dc_isar_feature(aa64_atomics, s) || + !dc_isar_feature(aa64_rcpc_8_3, s)) { + return false; } - - if (rn == 31) { + if (a->rn == 31) { gen_check_sp_alignment(s); } - dirty_addr = read_cpu_reg_sp(s, rn, 1); + mop = check_atomic_align(s, a->rn, a->sz); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, a->rn), false, + a->rn != 31, mop); + /* + * 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, a->rt), clean_addr, mop, false, + true, a->rt, iss_sf, true); + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + return true; +} + +static bool trans_LDRA(DisasContext *s, arg_LDRA *a) +{ + TCGv_i64 clean_addr, dirty_addr, tcg_rt; + MemOp memop; + + /* Load with pointer authentication */ + if (!dc_isar_feature(aa64_pauth, s)) { + return false; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + dirty_addr = read_cpu_reg_sp(s, a->rn, 1); if (s->pauth_active) { - if (use_key_a) { - gen_helper_autda(dirty_addr, cpu_env, dirty_addr, - new_tmp_a64_zero(s)); + if (!a->m) { + gen_helper_autda_combined(dirty_addr, tcg_env, dirty_addr, + tcg_constant_i64(0)); } else { - gen_helper_autdb(dirty_addr, cpu_env, dirty_addr, - new_tmp_a64_zero(s)); + gen_helper_autdb_combined(dirty_addr, tcg_env, dirty_addr, + tcg_constant_i64(0)); } } - /* Form the 10-bit signed, scaled offset. */ - offset = (extract32(insn, 22, 1) << 9) | extract32(insn, 12, 9); - offset = sextract32(offset << size, 0, 10 + size); - tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); + tcg_gen_addi_i64(dirty_addr, dirty_addr, a->imm); + + memop = finalize_memop(s, MO_64); /* Note that "clean" and "dirty" here refer to TBI not PAC. */ clean_addr = gen_mte_check1(s, dirty_addr, false, - is_wback || rn != 31, size); + a->w || a->rn != 31, memop); - tcg_rt = cpu_reg(s, rt); - do_gpr_ld(s, tcg_rt, clean_addr, size, - /* extend */ false, /* iss_valid */ !is_wback, - /* iss_srt */ rt, /* iss_sf */ true, /* iss_ar */ false); + tcg_rt = cpu_reg(s, a->rt); + do_gpr_ld(s, tcg_rt, clean_addr, memop, + /* extend */ false, /* iss_valid */ !a->w, + /* iss_srt */ a->rt, /* iss_sf */ true, /* iss_ar */ false); - if (is_wback) { - tcg_gen_mov_i64(cpu_reg_sp(s, rn), dirty_addr); + if (a->w) { + tcg_gen_mov_i64(cpu_reg_sp(s, a->rn), dirty_addr); } + return true; } -/* - * 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) +static bool trans_LDAPR_i(DisasContext *s, arg_ldapr_stlr_i *a) { - 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 extend = false; - bool iss_sf; - MemOp mop; + MemOp mop = a->sz | (a->sign ? MO_SIGN : 0); + bool iss_sf = ldst_iss_sf(a->sz, a->sign, a->ext); if (!dc_isar_feature(aa64_rcpc_8_4, s)) { - unallocated_encoding(s); - return; + return false; } - /* TODO: ARMv8.4-LSE SCTLR.nAA */ - mop = size | MO_ALIGN; - - 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; - } - mop |= MO_SIGN; - break; - case 3: /* LDAPURS* 32-bit variant */ - if (size > 1) { - unallocated_encoding(s); - return; - } - mop |= MO_SIGN; - extend = true; /* zero-extend 32->64 after signed load */ - break; - default: - g_assert_not_reached(); - } - - iss_sf = disas_ldst_compute_iss_sf(size, (mop & MO_SIGN) != 0, opc); - - if (rn == 31) { + if (a->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); + mop = check_ordered_align(s, a->rn, a->imm, false, mop); + dirty_addr = read_cpu_reg_sp(s, a->rn, 1); + tcg_gen_addi_i64(dirty_addr, dirty_addr, a->imm); 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, mop, 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, mop, - extend, true, rt, iss_sf, true); - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); - } + /* + * Load-AcquirePC semantics; we implement as the slightly more + * restrictive Load-Acquire. + */ + do_gpr_ld(s, cpu_reg(s, a->rt), clean_addr, mop, a->ext, true, + a->rt, iss_sf, true); + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + return true; } -/* Load/store register (all forms) */ -static void disas_ldst_reg(DisasContext *s, uint32_t insn) +static bool trans_STLR_i(DisasContext *s, arg_ldapr_stlr_i *a) { - int rt = extract32(insn, 0, 5); - int opc = extract32(insn, 22, 2); - bool is_vector = extract32(insn, 26, 1); - int size = extract32(insn, 30, 2); + TCGv_i64 clean_addr, dirty_addr; + MemOp mop = a->sz; + bool iss_sf = ldst_iss_sf(a->sz, a->sign, a->ext); - switch (extract32(insn, 24, 2)) { - case 0: - if (extract32(insn, 21, 1) == 0) { - /* Load/store register (unscaled immediate) - * Load/store immediate pre/post-indexed - * Load/store register unprivileged - */ - disas_ldst_reg_imm9(s, insn, opc, size, rt, is_vector); - return; - } - switch (extract32(insn, 10, 2)) { - case 0: - disas_ldst_atomic(s, insn, size, rt, is_vector); - return; - case 2: - disas_ldst_reg_roffset(s, insn, opc, size, rt, is_vector); - return; - default: - disas_ldst_pac(s, insn, size, rt, is_vector); - return; - } - break; - case 1: - disas_ldst_reg_unsigned_imm(s, insn, opc, size, rt, is_vector); - return; + if (!dc_isar_feature(aa64_rcpc_8_4, s)) { + return false; } - unallocated_encoding(s); + + /* TODO: ARMv8.4-LSE SCTLR.nAA */ + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + mop = check_ordered_align(s, a->rn, a->imm, true, mop); + dirty_addr = read_cpu_reg_sp(s, a->rn, 1); + tcg_gen_addi_i64(dirty_addr, dirty_addr, a->imm); + clean_addr = clean_data_tbi(s, dirty_addr); + + /* Store-Release semantics */ + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + do_gpr_st(s, cpu_reg(s, a->rt), clean_addr, mop, true, a->rt, iss_sf, true); + return true; } -/* AdvSIMD load/store multiple structures - * - * 31 30 29 23 22 21 16 15 12 11 10 9 5 4 0 - * +---+---+---------------+---+-------------+--------+------+------+------+ - * | 0 | Q | 0 0 1 1 0 0 0 | L | 0 0 0 0 0 0 | opcode | size | Rn | Rt | - * +---+---+---------------+---+-------------+--------+------+------+------+ - * - * AdvSIMD load/store multiple structures (post-indexed) - * - * 31 30 29 23 22 21 20 16 15 12 11 10 9 5 4 0 - * +---+---+---------------+---+---+---------+--------+------+------+------+ - * | 0 | Q | 0 0 1 1 0 0 1 | L | 0 | Rm | opcode | size | Rn | Rt | - * +---+---+---------------+---+---+---------+--------+------+------+------+ - * - * Rt: first (or only) SIMD&FP register to be transferred - * Rn: base address or SP - * Rm (post-index only): post-index register (when !31) or size dependent #imm - */ -static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) +static bool trans_LD_mult(DisasContext *s, arg_ldst_mult *a) { - int rt = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int rm = extract32(insn, 16, 5); - int size = extract32(insn, 10, 2); - int opcode = extract32(insn, 12, 4); - bool is_store = !extract32(insn, 22, 1); - bool is_postidx = extract32(insn, 23, 1); - bool is_q = extract32(insn, 30, 1); TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; MemOp endian, align, mop; int total; /* total bytes */ int elements; /* elements per vector */ - int rpt; /* num iterations */ - int selem; /* structure elements */ int r; + int size = a->sz; - if (extract32(insn, 31, 1) || extract32(insn, 21, 1)) { - unallocated_encoding(s); - return; + if (!a->p && a->rm != 0) { + /* For non-postindexed accesses the Rm field must be 0 */ + return false; } - - if (!is_postidx && rm != 0) { - unallocated_encoding(s); - return; + if (size == 3 && !a->q && a->selem != 1) { + return false; } - - /* From the shared decode logic */ - switch (opcode) { - case 0x0: - rpt = 1; - selem = 4; - break; - case 0x2: - rpt = 4; - selem = 1; - break; - case 0x4: - rpt = 1; - selem = 3; - break; - case 0x6: - rpt = 3; - selem = 1; - break; - case 0x7: - rpt = 1; - selem = 1; - break; - case 0x8: - rpt = 1; - selem = 2; - break; - case 0xa: - rpt = 2; - selem = 1; - break; - default: - unallocated_encoding(s); - return; - } - - if (size == 3 && !is_q && selem != 1) { - /* reserved */ - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; + return true; } - if (rn == 31) { + if (a->rn == 31) { gen_check_sp_alignment(s); } @@ -3710,22 +3575,22 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) endian = MO_LE; } - total = rpt * selem * (is_q ? 16 : 8); - tcg_rn = cpu_reg_sp(s, rn); + total = a->rpt * a->selem * (a->q ? 16 : 8); + tcg_rn = cpu_reg_sp(s, a->rn); /* * Issue the MTE check vs the logical repeat count, before we * promote consecutive little-endian elements below. */ - clean_addr = gen_mte_checkN(s, tcg_rn, is_store, is_postidx || rn != 31, - total); + clean_addr = gen_mte_checkN(s, tcg_rn, false, a->p || a->rn != 31, total, + finalize_memop_asimd(s, size)); /* * Consecutive little-endian elements from a single register * can be promoted to a larger little-endian operation. */ align = MO_ALIGN; - if (selem == 1 && endian == MO_LE) { + if (a->selem == 1 && endian == MO_LE) { align = pow2_align(size); size = 3; } @@ -3734,554 +3599,653 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) } mop = endian | size | align; - elements = (is_q ? 16 : 8) >> size; + elements = (a->q ? 16 : 8) >> size; tcg_ebytes = tcg_constant_i64(1 << size); - for (r = 0; r < rpt; r++) { + for (r = 0; r < a->rpt; r++) { int e; for (e = 0; e < elements; e++) { int xs; - for (xs = 0; xs < selem; xs++) { - int tt = (rt + r + xs) % 32; - if (is_store) { - do_vec_st(s, tt, e, clean_addr, mop); - } else { - do_vec_ld(s, tt, e, clean_addr, mop); - } + for (xs = 0; xs < a->selem; xs++) { + int tt = (a->rt + r + xs) % 32; + do_vec_ld(s, tt, e, clean_addr, mop); tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); } } } - if (!is_store) { - /* For non-quad operations, setting a slice of the low - * 64 bits of the register clears the high 64 bits (in - * the ARM ARM pseudocode this is implicit in the fact - * that 'rval' is a 64 bit wide variable). - * For quad operations, we might still need to zero the - * high bits of SVE. - */ - for (r = 0; r < rpt * selem; r++) { - int tt = (rt + r) % 32; - clear_vec_high(s, is_q, tt); - } + /* + * For non-quad operations, setting a slice of the low 64 bits of + * the register clears the high 64 bits (in the ARM ARM pseudocode + * this is implicit in the fact that 'rval' is a 64 bit wide + * variable). For quad operations, we might still need to zero + * the high bits of SVE. + */ + for (r = 0; r < a->rpt * a->selem; r++) { + int tt = (a->rt + r) % 32; + clear_vec_high(s, a->q, tt); } - if (is_postidx) { - if (rm == 31) { + if (a->p) { + if (a->rm == 31) { tcg_gen_addi_i64(tcg_rn, tcg_rn, total); } else { - tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, rm)); + tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, a->rm)); } } + return true; } -/* AdvSIMD load/store single structure - * - * 31 30 29 23 22 21 20 16 15 13 12 11 10 9 5 4 0 - * +---+---+---------------+-----+-----------+-----+---+------+------+------+ - * | 0 | Q | 0 0 1 1 0 1 0 | L R | 0 0 0 0 0 | opc | S | size | Rn | Rt | - * +---+---+---------------+-----+-----------+-----+---+------+------+------+ - * - * AdvSIMD load/store single structure (post-indexed) - * - * 31 30 29 23 22 21 20 16 15 13 12 11 10 9 5 4 0 - * +---+---+---------------+-----+-----------+-----+---+------+------+------+ - * | 0 | Q | 0 0 1 1 0 1 1 | L R | Rm | opc | S | size | Rn | Rt | - * +---+---+---------------+-----+-----------+-----+---+------+------+------+ - * - * Rt: first (or only) SIMD&FP register to be transferred - * Rn: base address or SP - * Rm (post-index only): post-index register (when !31) or size dependent #imm - * index = encoded in Q:S:size dependent on size - * - * lane_size = encoded in R, opc - * transfer width = encoded in opc, S, size - */ -static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) +static bool trans_ST_mult(DisasContext *s, arg_ldst_mult *a) { - int rt = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int rm = extract32(insn, 16, 5); - int size = extract32(insn, 10, 2); - int S = extract32(insn, 12, 1); - int opc = extract32(insn, 13, 3); - int R = extract32(insn, 21, 1); - int is_load = extract32(insn, 22, 1); - int is_postidx = extract32(insn, 23, 1); - int is_q = extract32(insn, 30, 1); + TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; + MemOp endian, align, mop; - int scale = extract32(opc, 1, 2); - int selem = (extract32(opc, 0, 1) << 1 | R) + 1; - bool replicate = false; - int index = is_q << 3 | S << 2 | size; - int xs, total; + int total; /* total bytes */ + int elements; /* elements per vector */ + int r; + int size = a->sz; + + if (!a->p && a->rm != 0) { + /* For non-postindexed accesses the Rm field must be 0 */ + return false; + } + if (size == 3 && !a->q && a->selem != 1) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + /* For our purposes, bytes are always little-endian. */ + endian = s->be_data; + if (size == 0) { + endian = MO_LE; + } + + total = a->rpt * a->selem * (a->q ? 16 : 8); + tcg_rn = cpu_reg_sp(s, a->rn); + + /* + * Issue the MTE check vs the logical repeat count, before we + * promote consecutive little-endian elements below. + */ + clean_addr = gen_mte_checkN(s, tcg_rn, true, a->p || a->rn != 31, total, + finalize_memop_asimd(s, size)); + + /* + * Consecutive little-endian elements from a single register + * can be promoted to a larger little-endian operation. + */ + align = MO_ALIGN; + if (a->selem == 1 && endian == MO_LE) { + align = pow2_align(size); + size = 3; + } + if (!s->align_mem) { + align = 0; + } + mop = endian | size | align; + + elements = (a->q ? 16 : 8) >> size; + tcg_ebytes = tcg_constant_i64(1 << size); + for (r = 0; r < a->rpt; r++) { + int e; + for (e = 0; e < elements; e++) { + int xs; + for (xs = 0; xs < a->selem; xs++) { + int tt = (a->rt + r + xs) % 32; + do_vec_st(s, tt, e, clean_addr, mop); + tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); + } + } + } + + if (a->p) { + if (a->rm == 31) { + tcg_gen_addi_i64(tcg_rn, tcg_rn, total); + } else { + tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, a->rm)); + } + } + return true; +} + +static bool trans_ST_single(DisasContext *s, arg_ldst_single *a) +{ + int xs, total, rt; TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; MemOp mop; - if (extract32(insn, 31, 1)) { - unallocated_encoding(s); - return; + if (!a->p && a->rm != 0) { + return false; } - if (!is_postidx && rm != 0) { - unallocated_encoding(s); - return; - } - - switch (scale) { - case 3: - if (!is_load || S) { - unallocated_encoding(s); - return; - } - scale = size; - replicate = true; - break; - case 0: - break; - case 1: - if (extract32(size, 0, 1)) { - unallocated_encoding(s); - return; - } - index >>= 1; - break; - case 2: - if (extract32(size, 1, 1)) { - unallocated_encoding(s); - return; - } - if (!extract32(size, 0, 1)) { - index >>= 2; - } else { - if (S) { - unallocated_encoding(s); - return; - } - index >>= 3; - scale = 3; - } - break; - default: - g_assert_not_reached(); - } - if (!fp_access_check(s)) { - return; + return true; } - if (rn == 31) { + if (a->rn == 31) { gen_check_sp_alignment(s); } - total = selem << scale; - tcg_rn = cpu_reg_sp(s, rn); + total = a->selem << a->scale; + tcg_rn = cpu_reg_sp(s, a->rn); - clean_addr = gen_mte_checkN(s, tcg_rn, !is_load, is_postidx || rn != 31, - total); - mop = finalize_memop(s, scale); + mop = finalize_memop_asimd(s, a->scale); + clean_addr = gen_mte_checkN(s, tcg_rn, true, a->p || a->rn != 31, + total, mop); - tcg_ebytes = tcg_constant_i64(1 << scale); - for (xs = 0; xs < selem; xs++) { - if (replicate) { - /* Load and replicate to all elements */ - TCGv_i64 tcg_tmp = tcg_temp_new_i64(); - - tcg_gen_qemu_ld_i64(tcg_tmp, clean_addr, get_mem_index(s), mop); - tcg_gen_gvec_dup_i64(scale, vec_full_reg_offset(s, rt), - (is_q + 1) * 8, vec_full_reg_size(s), - tcg_tmp); - tcg_temp_free_i64(tcg_tmp); - } else { - /* Load/store one element per register */ - if (is_load) { - do_vec_ld(s, rt, index, clean_addr, mop); - } else { - do_vec_st(s, rt, index, clean_addr, mop); - } - } + tcg_ebytes = tcg_constant_i64(1 << a->scale); + for (xs = 0, rt = a->rt; xs < a->selem; xs++, rt = (rt + 1) % 32) { + do_vec_st(s, rt, a->index, clean_addr, mop); tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); - rt = (rt + 1) % 32; } - if (is_postidx) { - if (rm == 31) { + if (a->p) { + if (a->rm == 31) { tcg_gen_addi_i64(tcg_rn, tcg_rn, total); } else { - tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, rm)); + tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, a->rm)); } } + return true; } -/* - * Load/Store memory tags - * - * 31 30 29 24 22 21 12 10 5 0 - * +-----+-------------+-----+---+------+-----+------+------+ - * | 1 1 | 0 1 1 0 0 1 | op1 | 1 | imm9 | op2 | Rn | Rt | - * +-----+-------------+-----+---+------+-----+------+------+ - */ -static void disas_ldst_tag(DisasContext *s, uint32_t insn) +static bool trans_LD_single(DisasContext *s, arg_ldst_single *a) { - int rt = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - uint64_t offset = sextract64(insn, 12, 9) << LOG2_TAG_GRANULE; - int op2 = extract32(insn, 10, 2); - int op1 = extract32(insn, 22, 2); - bool is_load = false, is_pair = false, is_zero = false, is_mult = false; - int index = 0; - TCGv_i64 addr, clean_addr, tcg_rt; + int xs, total, rt; + TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; + MemOp mop; - /* We checked insn bits [29:24,21] in the caller. */ - if (extract32(insn, 30, 2) != 3) { - goto do_unallocated; + if (!a->p && a->rm != 0) { + return false; + } + if (!fp_access_check(s)) { + return true; } - /* - * @index is a tri-state variable which has 3 states: - * < 0 : post-index, writeback - * = 0 : signed offset - * > 0 : pre-index, writeback - */ - switch (op1) { - case 0: - if (op2 != 0) { - /* STG */ - index = op2 - 2; - } else { - /* STZGM */ - if (s->current_el == 0 || offset != 0) { - goto do_unallocated; - } - is_mult = is_zero = true; - } - break; - case 1: - if (op2 != 0) { - /* STZG */ - is_zero = true; - index = op2 - 2; - } else { - /* LDG */ - is_load = true; - } - break; - case 2: - if (op2 != 0) { - /* ST2G */ - is_pair = true; - index = op2 - 2; - } else { - /* STGM */ - if (s->current_el == 0 || offset != 0) { - goto do_unallocated; - } - is_mult = true; - } - break; - case 3: - if (op2 != 0) { - /* STZ2G */ - is_pair = is_zero = true; - index = op2 - 2; - } else { - /* LDGM */ - if (s->current_el == 0 || offset != 0) { - goto do_unallocated; - } - is_mult = is_load = true; - } - break; - - default: - do_unallocated: - unallocated_encoding(s); - return; - } - - if (is_mult - ? !dc_isar_feature(aa64_mte, s) - : !dc_isar_feature(aa64_mte_insn_reg, s)) { - goto do_unallocated; - } - - if (rn == 31) { + if (a->rn == 31) { gen_check_sp_alignment(s); } - addr = read_cpu_reg_sp(s, rn, true); - if (index >= 0) { - /* pre-index or signed offset */ - tcg_gen_addi_i64(addr, addr, offset); + total = a->selem << a->scale; + tcg_rn = cpu_reg_sp(s, a->rn); + + mop = finalize_memop_asimd(s, a->scale); + clean_addr = gen_mte_checkN(s, tcg_rn, false, a->p || a->rn != 31, + total, mop); + + tcg_ebytes = tcg_constant_i64(1 << a->scale); + for (xs = 0, rt = a->rt; xs < a->selem; xs++, rt = (rt + 1) % 32) { + do_vec_ld(s, rt, a->index, clean_addr, mop); + tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); } - if (is_mult) { - tcg_rt = cpu_reg(s, rt); - - if (is_zero) { - int size = 4 << s->dcz_blocksize; - - if (s->ata) { - gen_helper_stzgm_tags(cpu_env, addr, tcg_rt); - } - /* - * The non-tags portion of STZGM is mostly like DC_ZVA, - * except the alignment happens before the access. - */ - clean_addr = clean_data_tbi(s, addr); - tcg_gen_andi_i64(clean_addr, clean_addr, -size); - gen_helper_dc_zva(cpu_env, clean_addr); - } else if (s->ata) { - if (is_load) { - gen_helper_ldgm(tcg_rt, cpu_env, addr); - } else { - gen_helper_stgm(cpu_env, addr, tcg_rt); - } + if (a->p) { + if (a->rm == 31) { + tcg_gen_addi_i64(tcg_rn, tcg_rn, total); } else { - MMUAccessType acc = is_load ? MMU_DATA_LOAD : MMU_DATA_STORE; - int size = 4 << GMID_EL1_BS; - - clean_addr = clean_data_tbi(s, addr); - tcg_gen_andi_i64(clean_addr, clean_addr, -size); - gen_probe_access(s, clean_addr, acc, size); - - if (is_load) { - /* The result tags are zeros. */ - tcg_gen_movi_i64(tcg_rt, 0); - } + tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, a->rm)); } - return; + } + return true; +} + +static bool trans_LD_single_repl(DisasContext *s, arg_LD_single_repl *a) +{ + int xs, total, rt; + TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; + MemOp mop; + + if (!a->p && a->rm != 0) { + return false; + } + if (!fp_access_check(s)) { + return true; } - if (is_load) { - tcg_gen_andi_i64(addr, addr, -TAG_GRANULE); - tcg_rt = cpu_reg(s, rt); - if (s->ata) { - gen_helper_ldg(tcg_rt, cpu_env, addr, tcg_rt); + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + total = a->selem << a->scale; + tcg_rn = cpu_reg_sp(s, a->rn); + + mop = finalize_memop_asimd(s, a->scale); + clean_addr = gen_mte_checkN(s, tcg_rn, false, a->p || a->rn != 31, + total, mop); + + tcg_ebytes = tcg_constant_i64(1 << a->scale); + for (xs = 0, rt = a->rt; xs < a->selem; xs++, rt = (rt + 1) % 32) { + /* Load and replicate to all elements */ + TCGv_i64 tcg_tmp = tcg_temp_new_i64(); + + tcg_gen_qemu_ld_i64(tcg_tmp, clean_addr, get_mem_index(s), mop); + tcg_gen_gvec_dup_i64(a->scale, vec_full_reg_offset(s, rt), + (a->q + 1) * 8, vec_full_reg_size(s), tcg_tmp); + tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); + } + + if (a->p) { + if (a->rm == 31) { + tcg_gen_addi_i64(tcg_rn, tcg_rn, total); } else { - clean_addr = clean_data_tbi(s, addr); - gen_probe_access(s, clean_addr, MMU_DATA_LOAD, MO_8); - gen_address_with_allocation_tag0(tcg_rt, addr); + tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, a->rm)); + } + } + return true; +} + +static bool trans_STZGM(DisasContext *s, arg_ldst_tag *a) +{ + TCGv_i64 addr, clean_addr, tcg_rt; + int size = 4 << s->dcz_blocksize; + + if (!dc_isar_feature(aa64_mte, s)) { + return false; + } + if (s->current_el == 0) { + return false; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + addr = read_cpu_reg_sp(s, a->rn, true); + tcg_gen_addi_i64(addr, addr, a->imm); + tcg_rt = cpu_reg(s, a->rt); + + if (s->ata[0]) { + gen_helper_stzgm_tags(tcg_env, addr, tcg_rt); + } + /* + * The non-tags portion of STZGM is mostly like DC_ZVA, + * except the alignment happens before the access. + */ + clean_addr = clean_data_tbi(s, addr); + tcg_gen_andi_i64(clean_addr, clean_addr, -size); + gen_helper_dc_zva(tcg_env, clean_addr); + return true; +} + +static bool trans_STGM(DisasContext *s, arg_ldst_tag *a) +{ + TCGv_i64 addr, clean_addr, tcg_rt; + + if (!dc_isar_feature(aa64_mte, s)) { + return false; + } + if (s->current_el == 0) { + return false; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + addr = read_cpu_reg_sp(s, a->rn, true); + tcg_gen_addi_i64(addr, addr, a->imm); + tcg_rt = cpu_reg(s, a->rt); + + if (s->ata[0]) { + gen_helper_stgm(tcg_env, addr, tcg_rt); + } else { + MMUAccessType acc = MMU_DATA_STORE; + int size = 4 << s->gm_blocksize; + + clean_addr = clean_data_tbi(s, addr); + tcg_gen_andi_i64(clean_addr, clean_addr, -size); + gen_probe_access(s, clean_addr, acc, size); + } + return true; +} + +static bool trans_LDGM(DisasContext *s, arg_ldst_tag *a) +{ + TCGv_i64 addr, clean_addr, tcg_rt; + + if (!dc_isar_feature(aa64_mte, s)) { + return false; + } + if (s->current_el == 0) { + return false; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + addr = read_cpu_reg_sp(s, a->rn, true); + tcg_gen_addi_i64(addr, addr, a->imm); + tcg_rt = cpu_reg(s, a->rt); + + if (s->ata[0]) { + gen_helper_ldgm(tcg_rt, tcg_env, addr); + } else { + MMUAccessType acc = MMU_DATA_LOAD; + int size = 4 << s->gm_blocksize; + + clean_addr = clean_data_tbi(s, addr); + tcg_gen_andi_i64(clean_addr, clean_addr, -size); + gen_probe_access(s, clean_addr, acc, size); + /* The result tags are zeros. */ + tcg_gen_movi_i64(tcg_rt, 0); + } + return true; +} + +static bool trans_LDG(DisasContext *s, arg_ldst_tag *a) +{ + TCGv_i64 addr, clean_addr, tcg_rt; + + if (!dc_isar_feature(aa64_mte_insn_reg, s)) { + return false; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + addr = read_cpu_reg_sp(s, a->rn, true); + if (!a->p) { + /* pre-index or signed offset */ + tcg_gen_addi_i64(addr, addr, a->imm); + } + + tcg_gen_andi_i64(addr, addr, -TAG_GRANULE); + tcg_rt = cpu_reg(s, a->rt); + if (s->ata[0]) { + gen_helper_ldg(tcg_rt, tcg_env, addr, tcg_rt); + } else { + /* + * Tag access disabled: we must check for aborts on the load + * load from [rn+offset], and then insert a 0 tag into rt. + */ + clean_addr = clean_data_tbi(s, addr); + gen_probe_access(s, clean_addr, MMU_DATA_LOAD, MO_8); + gen_address_with_allocation_tag0(tcg_rt, tcg_rt); + } + + if (a->w) { + /* pre-index or post-index */ + if (a->p) { + /* post-index */ + tcg_gen_addi_i64(addr, addr, a->imm); + } + tcg_gen_mov_i64(cpu_reg_sp(s, a->rn), addr); + } + return true; +} + +static bool do_STG(DisasContext *s, arg_ldst_tag *a, bool is_zero, bool is_pair) +{ + TCGv_i64 addr, tcg_rt; + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + addr = read_cpu_reg_sp(s, a->rn, true); + if (!a->p) { + /* pre-index or signed offset */ + tcg_gen_addi_i64(addr, addr, a->imm); + } + tcg_rt = cpu_reg_sp(s, a->rt); + if (!s->ata[0]) { + /* + * For STG and ST2G, we need to check alignment and probe memory. + * TODO: For STZG and STZ2G, we could rely on the stores below, + * at least for system mode; user-only won't enforce alignment. + */ + if (is_pair) { + gen_helper_st2g_stub(tcg_env, addr); + } else { + gen_helper_stg_stub(tcg_env, addr); + } + } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { + if (is_pair) { + gen_helper_st2g_parallel(tcg_env, addr, tcg_rt); + } else { + gen_helper_stg_parallel(tcg_env, addr, tcg_rt); } } else { - tcg_rt = cpu_reg_sp(s, rt); - if (!s->ata) { - /* - * For STG and ST2G, we need to check alignment and probe memory. - * TODO: For STZG and STZ2G, we could rely on the stores below, - * at least for system mode; user-only won't enforce alignment. - */ - if (is_pair) { - gen_helper_st2g_stub(cpu_env, addr); - } else { - gen_helper_stg_stub(cpu_env, addr); - } - } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { - if (is_pair) { - gen_helper_st2g_parallel(cpu_env, addr, tcg_rt); - } else { - gen_helper_stg_parallel(cpu_env, addr, tcg_rt); - } + if (is_pair) { + gen_helper_st2g(tcg_env, addr, tcg_rt); } else { - if (is_pair) { - gen_helper_st2g(cpu_env, addr, tcg_rt); - } else { - gen_helper_stg(cpu_env, addr, tcg_rt); - } + gen_helper_stg(tcg_env, addr, tcg_rt); } } if (is_zero) { TCGv_i64 clean_addr = clean_data_tbi(s, addr); - TCGv_i64 tcg_zero = tcg_constant_i64(0); + TCGv_i64 zero64 = tcg_constant_i64(0); + TCGv_i128 zero128 = tcg_temp_new_i128(); int mem_index = get_mem_index(s); - int i, n = (1 + is_pair) << LOG2_TAG_GRANULE; + MemOp mop = finalize_memop(s, MO_128 | MO_ALIGN); - tcg_gen_qemu_st_i64(tcg_zero, clean_addr, mem_index, - MO_UQ | MO_ALIGN_16); - for (i = 8; i < n; i += 8) { - tcg_gen_addi_i64(clean_addr, clean_addr, 8); - tcg_gen_qemu_st_i64(tcg_zero, clean_addr, mem_index, MO_UQ); + tcg_gen_concat_i64_i128(zero128, zero64, zero64); + + /* This is 1 or 2 atomic 16-byte operations. */ + tcg_gen_qemu_st_i128(zero128, clean_addr, mem_index, mop); + if (is_pair) { + tcg_gen_addi_i64(clean_addr, clean_addr, 16); + tcg_gen_qemu_st_i128(zero128, clean_addr, mem_index, mop); } } - if (index != 0) { + if (a->w) { /* pre-index or post-index */ - if (index < 0) { + if (a->p) { /* post-index */ - tcg_gen_addi_i64(addr, addr, offset); + tcg_gen_addi_i64(addr, addr, a->imm); } - tcg_gen_mov_i64(cpu_reg_sp(s, rn), addr); + tcg_gen_mov_i64(cpu_reg_sp(s, a->rn), addr); } + return true; } -/* Loads and stores */ -static void disas_ldst(DisasContext *s, uint32_t insn) +TRANS_FEAT(STG, aa64_mte_insn_reg, do_STG, a, false, false) +TRANS_FEAT(STZG, aa64_mte_insn_reg, do_STG, a, true, false) +TRANS_FEAT(ST2G, aa64_mte_insn_reg, do_STG, a, false, true) +TRANS_FEAT(STZ2G, aa64_mte_insn_reg, do_STG, a, true, true) + +typedef void SetFn(TCGv_env, TCGv_i32, TCGv_i32); + +static bool do_SET(DisasContext *s, arg_set *a, bool is_epilogue, + bool is_setg, SetFn fn) { - switch (extract32(insn, 24, 6)) { - case 0x08: /* Load/store exclusive */ - disas_ldst_excl(s, insn); - break; - case 0x18: case 0x1c: /* Load register (literal) */ - disas_ld_lit(s, insn); - break; - case 0x28: case 0x29: - case 0x2c: case 0x2d: /* Load/store pair (all forms) */ - disas_ldst_pair(s, insn); - break; - case 0x38: case 0x39: - case 0x3c: case 0x3d: /* Load/store register (all forms) */ - disas_ldst_reg(s, insn); - break; - case 0x0c: /* AdvSIMD load/store multiple structures */ - disas_ldst_multiple_struct(s, insn); - break; - case 0x0d: /* AdvSIMD load/store single structure */ - disas_ldst_single_struct(s, insn); - break; - case 0x19: - if (extract32(insn, 21, 1) != 0) { - disas_ldst_tag(s, insn); - } else if (extract32(insn, 10, 2) == 0) { - disas_ldst_ldapr_stlr(s, insn); - } else { - unallocated_encoding(s); - } - break; - default: - unallocated_encoding(s); - break; + int memidx; + uint32_t syndrome, desc = 0; + + if (is_setg && !dc_isar_feature(aa64_mte, s)) { + return false; } + + /* + * UNPREDICTABLE cases: we choose to UNDEF, which allows + * us to pull this check before the CheckMOPSEnabled() test + * (which we do in the helper function) + */ + if (a->rs == a->rn || a->rs == a->rd || a->rn == a->rd || + a->rd == 31 || a->rn == 31) { + return false; + } + + memidx = get_a64_user_mem_index(s, a->unpriv); + + /* + * We pass option_a == true, matching our implementation; + * we pass wrong_option == false: helper function may set that bit. + */ + syndrome = syn_mop(true, is_setg, (a->nontemp << 1) | a->unpriv, + is_epilogue, false, true, a->rd, a->rs, a->rn); + + if (is_setg ? s->ata[a->unpriv] : s->mte_active[a->unpriv]) { + /* We may need to do MTE tag checking, so assemble the descriptor */ + desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); + desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); + desc = FIELD_DP32(desc, MTEDESC, WRITE, true); + /* SIZEM1 and ALIGN we leave 0 (byte write) */ + } + /* The helper function always needs the memidx even with MTE disabled */ + desc = FIELD_DP32(desc, MTEDESC, MIDX, memidx); + + /* + * The helper needs the register numbers, but since they're in + * the syndrome anyway, we let it extract them from there rather + * than passing in an extra three integer arguments. + */ + fn(tcg_env, tcg_constant_i32(syndrome), tcg_constant_i32(desc)); + return true; } -/* PC-rel. addressing - * 31 30 29 28 24 23 5 4 0 - * +----+-------+-----------+-------------------+------+ - * | op | immlo | 1 0 0 0 0 | immhi | Rd | - * +----+-------+-----------+-------------------+------+ +TRANS_FEAT(SETP, aa64_mops, do_SET, a, false, false, gen_helper_setp) +TRANS_FEAT(SETM, aa64_mops, do_SET, a, false, false, gen_helper_setm) +TRANS_FEAT(SETE, aa64_mops, do_SET, a, true, false, gen_helper_sete) +TRANS_FEAT(SETGP, aa64_mops, do_SET, a, false, true, gen_helper_setgp) +TRANS_FEAT(SETGM, aa64_mops, do_SET, a, false, true, gen_helper_setgm) +TRANS_FEAT(SETGE, aa64_mops, do_SET, a, true, true, gen_helper_setge) + +typedef void CpyFn(TCGv_env, TCGv_i32, TCGv_i32, TCGv_i32); + +static bool do_CPY(DisasContext *s, arg_cpy *a, bool is_epilogue, CpyFn fn) +{ + int rmemidx, wmemidx; + uint32_t syndrome, rdesc = 0, wdesc = 0; + bool wunpriv = extract32(a->options, 0, 1); + bool runpriv = extract32(a->options, 1, 1); + + /* + * UNPREDICTABLE cases: we choose to UNDEF, which allows + * us to pull this check before the CheckMOPSEnabled() test + * (which we do in the helper function) + */ + if (a->rs == a->rn || a->rs == a->rd || a->rn == a->rd || + a->rd == 31 || a->rs == 31 || a->rn == 31) { + return false; + } + + rmemidx = get_a64_user_mem_index(s, runpriv); + wmemidx = get_a64_user_mem_index(s, wunpriv); + + /* + * We pass option_a == true, matching our implementation; + * we pass wrong_option == false: helper function may set that bit. + */ + syndrome = syn_mop(false, false, a->options, is_epilogue, + false, true, a->rd, a->rs, a->rn); + + /* If we need to do MTE tag checking, assemble the descriptors */ + if (s->mte_active[runpriv]) { + rdesc = FIELD_DP32(rdesc, MTEDESC, TBI, s->tbid); + rdesc = FIELD_DP32(rdesc, MTEDESC, TCMA, s->tcma); + } + if (s->mte_active[wunpriv]) { + wdesc = FIELD_DP32(wdesc, MTEDESC, TBI, s->tbid); + wdesc = FIELD_DP32(wdesc, MTEDESC, TCMA, s->tcma); + wdesc = FIELD_DP32(wdesc, MTEDESC, WRITE, true); + } + /* The helper function needs these parts of the descriptor regardless */ + rdesc = FIELD_DP32(rdesc, MTEDESC, MIDX, rmemidx); + wdesc = FIELD_DP32(wdesc, MTEDESC, MIDX, wmemidx); + + /* + * The helper needs the register numbers, but since they're in + * the syndrome anyway, we let it extract them from there rather + * than passing in an extra three integer arguments. + */ + fn(tcg_env, tcg_constant_i32(syndrome), tcg_constant_i32(wdesc), + tcg_constant_i32(rdesc)); + return true; +} + +TRANS_FEAT(CPYP, aa64_mops, do_CPY, a, false, gen_helper_cpyp) +TRANS_FEAT(CPYM, aa64_mops, do_CPY, a, false, gen_helper_cpym) +TRANS_FEAT(CPYE, aa64_mops, do_CPY, a, true, gen_helper_cpye) +TRANS_FEAT(CPYFP, aa64_mops, do_CPY, a, false, gen_helper_cpyfp) +TRANS_FEAT(CPYFM, aa64_mops, do_CPY, a, false, gen_helper_cpyfm) +TRANS_FEAT(CPYFE, aa64_mops, do_CPY, a, true, gen_helper_cpyfe) + +typedef void ArithTwoOp(TCGv_i64, TCGv_i64, TCGv_i64); + +static bool gen_rri(DisasContext *s, arg_rri_sf *a, + bool rd_sp, bool rn_sp, ArithTwoOp *fn) +{ + TCGv_i64 tcg_rn = rn_sp ? cpu_reg_sp(s, a->rn) : cpu_reg(s, a->rn); + TCGv_i64 tcg_rd = rd_sp ? cpu_reg_sp(s, a->rd) : cpu_reg(s, a->rd); + TCGv_i64 tcg_imm = tcg_constant_i64(a->imm); + + fn(tcg_rd, tcg_rn, tcg_imm); + if (!a->sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + return true; +} + +/* + * PC-rel. addressing */ -static void disas_pc_rel_adr(DisasContext *s, uint32_t insn) + +static bool trans_ADR(DisasContext *s, arg_ri *a) { - unsigned int page, rd; - uint64_t base; - uint64_t offset; + gen_pc_plus_diff(s, cpu_reg(s, a->rd), a->imm); + return true; +} - page = extract32(insn, 31, 1); - /* SignExtend(immhi:immlo) -> offset */ - offset = sextract64(insn, 5, 19); - offset = offset << 2 | extract32(insn, 29, 2); - rd = extract32(insn, 0, 5); - base = s->pc_curr; +static bool trans_ADRP(DisasContext *s, arg_ri *a) +{ + int64_t offset = (int64_t)a->imm << 12; - if (page) { - /* ADRP (page based) */ - base &= ~0xfff; - offset <<= 12; - } - - tcg_gen_movi_i64(cpu_reg(s, rd), base + offset); + /* The page offset is ok for CF_PCREL. */ + offset -= s->pc_curr & 0xfff; + gen_pc_plus_diff(s, cpu_reg(s, a->rd), offset); + return true; } /* * Add/subtract (immediate) - * - * 31 30 29 28 23 22 21 10 9 5 4 0 - * +--+--+--+-------------+--+-------------+-----+-----+ - * |sf|op| S| 1 0 0 0 1 0 |sh| imm12 | Rn | Rd | - * +--+--+--+-------------+--+-------------+-----+-----+ - * - * sf: 0 -> 32bit, 1 -> 64bit - * op: 0 -> add , 1 -> sub - * S: 1 -> set flags - * sh: 1 -> LSL imm by 12 */ -static void disas_add_sub_imm(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - uint64_t imm = extract32(insn, 10, 12); - bool shift = extract32(insn, 22, 1); - bool setflags = extract32(insn, 29, 1); - bool sub_op = extract32(insn, 30, 1); - bool is_64bit = extract32(insn, 31, 1); - - TCGv_i64 tcg_rn = cpu_reg_sp(s, rn); - TCGv_i64 tcg_rd = setflags ? cpu_reg(s, rd) : cpu_reg_sp(s, rd); - TCGv_i64 tcg_result; - - if (shift) { - imm <<= 12; - } - - tcg_result = tcg_temp_new_i64(); - if (!setflags) { - if (sub_op) { - tcg_gen_subi_i64(tcg_result, tcg_rn, imm); - } else { - tcg_gen_addi_i64(tcg_result, tcg_rn, imm); - } - } else { - TCGv_i64 tcg_imm = tcg_constant_i64(imm); - if (sub_op) { - gen_sub_CC(is_64bit, tcg_result, tcg_rn, tcg_imm); - } else { - gen_add_CC(is_64bit, tcg_result, tcg_rn, tcg_imm); - } - } - - if (is_64bit) { - tcg_gen_mov_i64(tcg_rd, tcg_result); - } else { - tcg_gen_ext32u_i64(tcg_rd, tcg_result); - } - - tcg_temp_free_i64(tcg_result); -} +TRANS(ADD_i, gen_rri, a, 1, 1, tcg_gen_add_i64) +TRANS(SUB_i, gen_rri, a, 1, 1, tcg_gen_sub_i64) +TRANS(ADDS_i, gen_rri, a, 0, 1, a->sf ? gen_add64_CC : gen_add32_CC) +TRANS(SUBS_i, gen_rri, a, 0, 1, a->sf ? gen_sub64_CC : gen_sub32_CC) /* * Add/subtract (immediate, with tags) - * - * 31 30 29 28 23 22 21 16 14 10 9 5 4 0 - * +--+--+--+-------------+--+---------+--+-------+-----+-----+ - * |sf|op| S| 1 0 0 0 1 1 |o2| uimm6 |o3| uimm4 | Rn | Rd | - * +--+--+--+-------------+--+---------+--+-------+-----+-----+ - * - * op: 0 -> add, 1 -> sub */ -static void disas_add_sub_imm_with_tags(DisasContext *s, uint32_t insn) + +static bool gen_add_sub_imm_with_tags(DisasContext *s, arg_rri_tag *a, + bool sub_op) { - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int uimm4 = extract32(insn, 10, 4); - int uimm6 = extract32(insn, 16, 6); - bool sub_op = extract32(insn, 30, 1); TCGv_i64 tcg_rn, tcg_rd; int imm; - /* Test all of sf=1, S=0, o2=0, o3=0. */ - if ((insn & 0xa040c000u) != 0x80000000u || - !dc_isar_feature(aa64_mte_insn_reg, s)) { - unallocated_encoding(s); - return; - } - - imm = uimm6 << LOG2_TAG_GRANULE; + imm = a->uimm6 << LOG2_TAG_GRANULE; if (sub_op) { imm = -imm; } - tcg_rn = cpu_reg_sp(s, rn); - tcg_rd = cpu_reg_sp(s, rd); + tcg_rn = cpu_reg_sp(s, a->rn); + tcg_rd = cpu_reg_sp(s, a->rd); - if (s->ata) { - gen_helper_addsubg(tcg_rd, cpu_env, tcg_rn, + if (s->ata[0]) { + gen_helper_addsubg(tcg_rd, tcg_env, tcg_rn, tcg_constant_i32(imm), - tcg_constant_i32(uimm4)); + tcg_constant_i32(a->uimm4)); } else { tcg_gen_addi_i64(tcg_rd, tcg_rn, imm); gen_address_with_allocation_tag0(tcg_rd, tcg_rd); } + return true; } +TRANS_FEAT(ADDG_i, aa64_mte_insn_reg, gen_add_sub_imm_with_tags, a, false) +TRANS_FEAT(SUBG_i, aa64_mte_insn_reg, gen_add_sub_imm_with_tags, a, true) + /* The input should be a value in the bottom e bits (with higher * bits zero); returns that value replicated into every element * of size e in a 64 bit integer. @@ -4296,14 +4260,12 @@ static uint64_t bitfield_replicate(uint64_t mask, unsigned int e) return mask; } -/* Return a value with the bottom len bits set (where 0 < len <= 64) */ -static inline uint64_t bitmask64(unsigned int length) -{ - assert(length > 0 && length <= 64); - return ~0ULL >> (64 - length); -} +/* + * Logical (immediate) + */ -/* Simplified variant of pseudocode DecodeBitMasks() for the case where we +/* + * Simplified variant of pseudocode DecodeBitMasks() for the case where we * only require the wmask. Returns false if the imms/immr/immn are a reserved * value (ie should cause a guest UNDEF exception), and true if they are * valid, in which case the decoded bit pattern is written to result. @@ -4358,10 +4320,10 @@ bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, /* Create the value of one element: s+1 set bits rotated * by r within the element (which is e bits wide)... */ - mask = bitmask64(s + 1); + mask = MAKE_64BIT_MASK(0, s + 1); if (r) { mask = (mask >> r) | (mask << (e - r)); - mask &= bitmask64(e); + mask &= MAKE_64BIT_MASK(0, e); } /* ...then replicate the element over the whole 64 bit value */ mask = bitfield_replicate(mask, e); @@ -4369,297 +4331,215 @@ bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, return true; } -/* Logical (immediate) - * 31 30 29 28 23 22 21 16 15 10 9 5 4 0 - * +----+-----+-------------+---+------+------+------+------+ - * | sf | opc | 1 0 0 1 0 0 | N | immr | imms | Rn | Rd | - * +----+-----+-------------+---+------+------+------+------+ - */ -static void disas_logic_imm(DisasContext *s, uint32_t insn) +static bool gen_rri_log(DisasContext *s, arg_rri_log *a, bool set_cc, + void (*fn)(TCGv_i64, TCGv_i64, int64_t)) { - unsigned int sf, opc, is_n, immr, imms, rn, rd; TCGv_i64 tcg_rd, tcg_rn; - uint64_t wmask; - bool is_and = false; + uint64_t imm; - sf = extract32(insn, 31, 1); - opc = extract32(insn, 29, 2); - is_n = extract32(insn, 22, 1); - immr = extract32(insn, 16, 6); - imms = extract32(insn, 10, 6); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); - - if (!sf && is_n) { - unallocated_encoding(s); - return; + /* Some immediate field values are reserved. */ + if (!logic_imm_decode_wmask(&imm, extract32(a->dbm, 12, 1), + extract32(a->dbm, 0, 6), + extract32(a->dbm, 6, 6))) { + return false; + } + if (!a->sf) { + imm &= 0xffffffffull; } - if (opc == 0x3) { /* ANDS */ - tcg_rd = cpu_reg(s, rd); - } else { - tcg_rd = cpu_reg_sp(s, rd); - } - tcg_rn = cpu_reg(s, rn); + tcg_rd = set_cc ? cpu_reg(s, a->rd) : cpu_reg_sp(s, a->rd); + tcg_rn = cpu_reg(s, a->rn); - if (!logic_imm_decode_wmask(&wmask, is_n, imms, immr)) { - /* some immediate field values are reserved */ - unallocated_encoding(s); - return; + fn(tcg_rd, tcg_rn, imm); + if (set_cc) { + gen_logic_CC(a->sf, tcg_rd); } - - if (!sf) { - wmask &= 0xffffffff; - } - - switch (opc) { - case 0x3: /* ANDS */ - case 0x0: /* AND */ - tcg_gen_andi_i64(tcg_rd, tcg_rn, wmask); - is_and = true; - break; - case 0x1: /* ORR */ - tcg_gen_ori_i64(tcg_rd, tcg_rn, wmask); - break; - case 0x2: /* EOR */ - tcg_gen_xori_i64(tcg_rd, tcg_rn, wmask); - break; - default: - assert(FALSE); /* must handle all above */ - break; - } - - if (!sf && !is_and) { - /* zero extend final result; we know we can skip this for AND - * since the immediate had the high 32 bits clear. - */ + if (!a->sf) { tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } - - if (opc == 3) { /* ANDS */ - gen_logic_CC(sf, tcg_rd); - } + return true; } +TRANS(AND_i, gen_rri_log, a, false, tcg_gen_andi_i64) +TRANS(ORR_i, gen_rri_log, a, false, tcg_gen_ori_i64) +TRANS(EOR_i, gen_rri_log, a, false, tcg_gen_xori_i64) +TRANS(ANDS_i, gen_rri_log, a, true, tcg_gen_andi_i64) + /* * Move wide (immediate) - * - * 31 30 29 28 23 22 21 20 5 4 0 - * +--+-----+-------------+-----+----------------+------+ - * |sf| opc | 1 0 0 1 0 1 | hw | imm16 | Rd | - * +--+-----+-------------+-----+----------------+------+ - * - * sf: 0 -> 32 bit, 1 -> 64 bit - * opc: 00 -> N, 10 -> Z, 11 -> K - * hw: shift/16 (0,16, and sf only 32, 48) */ -static void disas_movw_imm(DisasContext *s, uint32_t insn) + +static bool trans_MOVZ(DisasContext *s, arg_movw *a) { - int rd = extract32(insn, 0, 5); - uint64_t imm = extract32(insn, 5, 16); - int sf = extract32(insn, 31, 1); - int opc = extract32(insn, 29, 2); - int pos = extract32(insn, 21, 2) << 4; - TCGv_i64 tcg_rd = cpu_reg(s, rd); - - if (!sf && (pos >= 32)) { - unallocated_encoding(s); - return; - } - - switch (opc) { - case 0: /* MOVN */ - case 2: /* MOVZ */ - imm <<= pos; - if (opc == 0) { - imm = ~imm; - } - if (!sf) { - imm &= 0xffffffffu; - } - tcg_gen_movi_i64(tcg_rd, imm); - break; - case 3: /* MOVK */ - tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_constant_i64(imm), pos, 16); - if (!sf) { - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); - } - break; - default: - unallocated_encoding(s); - break; - } + int pos = a->hw << 4; + tcg_gen_movi_i64(cpu_reg(s, a->rd), (uint64_t)a->imm << pos); + return true; } -/* Bitfield - * 31 30 29 28 23 22 21 16 15 10 9 5 4 0 - * +----+-----+-------------+---+------+------+------+------+ - * | sf | opc | 1 0 0 1 1 0 | N | immr | imms | Rn | Rd | - * +----+-----+-------------+---+------+------+------+------+ - */ -static void disas_bitfield(DisasContext *s, uint32_t insn) +static bool trans_MOVN(DisasContext *s, arg_movw *a) { - unsigned int sf, n, opc, ri, si, rn, rd, bitsize, pos, len; - TCGv_i64 tcg_rd, tcg_tmp; + int pos = a->hw << 4; + uint64_t imm = a->imm; - sf = extract32(insn, 31, 1); - opc = extract32(insn, 29, 2); - n = extract32(insn, 22, 1); - ri = extract32(insn, 16, 6); - si = extract32(insn, 10, 6); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); - bitsize = sf ? 64 : 32; - - if (sf != n || ri >= bitsize || si >= bitsize || opc > 2) { - unallocated_encoding(s); - return; + imm = ~(imm << pos); + if (!a->sf) { + imm = (uint32_t)imm; } + tcg_gen_movi_i64(cpu_reg(s, a->rd), imm); + return true; +} - tcg_rd = cpu_reg(s, rd); +static bool trans_MOVK(DisasContext *s, arg_movw *a) +{ + int pos = a->hw << 4; + TCGv_i64 tcg_rd, tcg_im; - /* Suppress the zero-extend for !sf. Since RI and SI are constrained - to be smaller than bitsize, we'll never reference data outside the - low 32-bits anyway. */ - tcg_tmp = read_cpu_reg(s, rn, 1); + tcg_rd = cpu_reg(s, a->rd); + tcg_im = tcg_constant_i64(a->imm); + tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_im, pos, 16); + if (!a->sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + return true; +} + +/* + * Bitfield + */ + +static bool trans_SBFM(DisasContext *s, arg_SBFM *a) +{ + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + TCGv_i64 tcg_tmp = read_cpu_reg(s, a->rn, 1); + unsigned int bitsize = a->sf ? 64 : 32; + unsigned int ri = a->immr; + unsigned int si = a->imms; + unsigned int pos, len; - /* Recognize simple(r) extractions. */ if (si >= ri) { /* Wd = Wn */ len = (si - ri) + 1; - if (opc == 0) { /* SBFM: ASR, SBFX, SXTB, SXTH, SXTW */ - tcg_gen_sextract_i64(tcg_rd, tcg_tmp, ri, len); - goto done; - } else if (opc == 2) { /* UBFM: UBFX, LSR, UXTB, UXTH */ - tcg_gen_extract_i64(tcg_rd, tcg_tmp, ri, len); - return; + tcg_gen_sextract_i64(tcg_rd, tcg_tmp, ri, len); + if (!a->sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } - /* opc == 1, BFXIL fall through to deposit */ + } else { + /* Wd<32+s-r,32-r> = Wn */ + len = si + 1; + pos = (bitsize - ri) & (bitsize - 1); + + if (len < ri) { + /* + * Sign extend the destination field from len to fill the + * balance of the word. Let the deposit below insert all + * of those sign bits. + */ + tcg_gen_sextract_i64(tcg_tmp, tcg_tmp, 0, len); + len = ri; + } + + /* + * We start with zero, and we haven't modified any bits outside + * bitsize, therefore no final zero-extension is unneeded for !sf. + */ + tcg_gen_deposit_z_i64(tcg_rd, tcg_tmp, pos, len); + } + return true; +} + +static bool trans_UBFM(DisasContext *s, arg_UBFM *a) +{ + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + TCGv_i64 tcg_tmp = read_cpu_reg(s, a->rn, 1); + unsigned int bitsize = a->sf ? 64 : 32; + unsigned int ri = a->immr; + unsigned int si = a->imms; + unsigned int pos, len; + + tcg_rd = cpu_reg(s, a->rd); + tcg_tmp = read_cpu_reg(s, a->rn, 1); + + if (si >= ri) { + /* Wd = Wn */ + len = (si - ri) + 1; + tcg_gen_extract_i64(tcg_rd, tcg_tmp, ri, len); + } else { + /* Wd<32+s-r,32-r> = Wn */ + len = si + 1; + pos = (bitsize - ri) & (bitsize - 1); + tcg_gen_deposit_z_i64(tcg_rd, tcg_tmp, pos, len); + } + return true; +} + +static bool trans_BFM(DisasContext *s, arg_BFM *a) +{ + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + TCGv_i64 tcg_tmp = read_cpu_reg(s, a->rn, 1); + unsigned int bitsize = a->sf ? 64 : 32; + unsigned int ri = a->immr; + unsigned int si = a->imms; + unsigned int pos, len; + + tcg_rd = cpu_reg(s, a->rd); + tcg_tmp = read_cpu_reg(s, a->rn, 1); + + if (si >= ri) { + /* Wd = Wn */ tcg_gen_shri_i64(tcg_tmp, tcg_tmp, ri); + len = (si - ri) + 1; pos = 0; } else { - /* Handle the ri > si case with a deposit - * Wd<32+s-r,32-r> = Wn - */ + /* Wd<32+s-r,32-r> = Wn */ len = si + 1; pos = (bitsize - ri) & (bitsize - 1); } - if (opc == 0 && len < ri) { - /* SBFM: sign extend the destination field from len to fill - the balance of the word. Let the deposit below insert all - of those sign bits. */ - tcg_gen_sextract_i64(tcg_tmp, tcg_tmp, 0, len); - len = ri; - } - - if (opc == 1) { /* BFM, BFXIL */ - tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_tmp, pos, len); - } else { - /* SBFM or UBFM: We start with zero, and we haven't modified - any bits outside bitsize, therefore the zero-extension - below is unneeded. */ - tcg_gen_deposit_z_i64(tcg_rd, tcg_tmp, pos, len); - return; - } - - done: - if (!sf) { /* zero extend final result */ + tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_tmp, pos, len); + if (!a->sf) { tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } + return true; } -/* Extract - * 31 30 29 28 23 22 21 20 16 15 10 9 5 4 0 - * +----+------+-------------+---+----+------+--------+------+------+ - * | sf | op21 | 1 0 0 1 1 1 | N | o0 | Rm | imms | Rn | Rd | - * +----+------+-------------+---+----+------+--------+------+------+ - */ -static void disas_extract(DisasContext *s, uint32_t insn) +static bool trans_EXTR(DisasContext *s, arg_extract *a) { - unsigned int sf, n, rm, imm, rn, rd, bitsize, op21, op0; + TCGv_i64 tcg_rd, tcg_rm, tcg_rn; - sf = extract32(insn, 31, 1); - n = extract32(insn, 22, 1); - rm = extract32(insn, 16, 5); - imm = extract32(insn, 10, 6); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); - op21 = extract32(insn, 29, 2); - op0 = extract32(insn, 21, 1); - bitsize = sf ? 64 : 32; + tcg_rd = cpu_reg(s, a->rd); - if (sf != n || op21 || op0 || imm >= bitsize) { - unallocated_encoding(s); - } else { - TCGv_i64 tcg_rd, tcg_rm, tcg_rn; - - tcg_rd = cpu_reg(s, rd); - - if (unlikely(imm == 0)) { - /* tcg shl_i32/shl_i64 is undefined for 32/64 bit shifts, - * so an extract from bit 0 is a special case. - */ - if (sf) { - tcg_gen_mov_i64(tcg_rd, cpu_reg(s, rm)); - } else { - tcg_gen_ext32u_i64(tcg_rd, cpu_reg(s, rm)); - } + if (unlikely(a->imm == 0)) { + /* + * tcg shl_i32/shl_i64 is undefined for 32/64 bit shifts, + * so an extract from bit 0 is a special case. + */ + if (a->sf) { + tcg_gen_mov_i64(tcg_rd, cpu_reg(s, a->rm)); } else { - tcg_rm = cpu_reg(s, rm); - tcg_rn = cpu_reg(s, rn); + tcg_gen_ext32u_i64(tcg_rd, cpu_reg(s, a->rm)); + } + } else { + tcg_rm = cpu_reg(s, a->rm); + tcg_rn = cpu_reg(s, a->rn); - if (sf) { - /* Specialization to ROR happens in EXTRACT2. */ - tcg_gen_extract2_i64(tcg_rd, tcg_rm, tcg_rn, imm); + if (a->sf) { + /* Specialization to ROR happens in EXTRACT2. */ + tcg_gen_extract2_i64(tcg_rd, tcg_rm, tcg_rn, a->imm); + } else { + TCGv_i32 t0 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(t0, tcg_rm); + if (a->rm == a->rn) { + tcg_gen_rotri_i32(t0, t0, a->imm); } else { - TCGv_i32 t0 = tcg_temp_new_i32(); - - tcg_gen_extrl_i64_i32(t0, tcg_rm); - if (rm == rn) { - tcg_gen_rotri_i32(t0, t0, imm); - } else { - TCGv_i32 t1 = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(t1, tcg_rn); - tcg_gen_extract2_i32(t0, t0, t1, imm); - tcg_temp_free_i32(t1); - } - tcg_gen_extu_i32_i64(tcg_rd, t0); - tcg_temp_free_i32(t0); + TCGv_i32 t1 = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t1, tcg_rn); + tcg_gen_extract2_i32(t0, t0, t1, a->imm); } + tcg_gen_extu_i32_i64(tcg_rd, t0); } } -} - -/* Data processing - immediate */ -static void disas_data_proc_imm(DisasContext *s, uint32_t insn) -{ - switch (extract32(insn, 23, 6)) { - case 0x20: case 0x21: /* PC-rel. addressing */ - disas_pc_rel_adr(s, insn); - break; - case 0x22: /* Add/subtract (immediate) */ - disas_add_sub_imm(s, insn); - break; - case 0x23: /* Add/subtract (immediate, with tags) */ - disas_add_sub_imm_with_tags(s, insn); - break; - case 0x24: /* Logical (immediate) */ - disas_logic_imm(s, insn); - break; - case 0x25: /* Move wide (immediate) */ - disas_movw_imm(s, insn); - break; - case 0x26: /* Bitfield */ - disas_bitfield(s, insn); - break; - case 0x27: /* Extract */ - disas_extract(s, insn); - break; - default: - unallocated_encoding(s); - break; - } + return true; } /* Shift a TCGv src by TCGv shift_amount, put result in dst. @@ -4694,8 +4574,6 @@ static void shift_reg(TCGv_i64 dst, TCGv_i64 src, int sf, tcg_gen_extrl_i64_i32(t1, shift_amount); tcg_gen_rotr_i32(t0, t0, t1); tcg_gen_extu_i32_i64(dst, t0); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); } break; default: @@ -4884,8 +4762,6 @@ static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn) } else { tcg_gen_ext32u_i64(tcg_rd, tcg_result); } - - tcg_temp_free_i64(tcg_result); } /* @@ -4948,8 +4824,6 @@ static void disas_add_sub_reg(DisasContext *s, uint32_t insn) } else { tcg_gen_ext32u_i64(tcg_rd, tcg_result); } - - tcg_temp_free_i64(tcg_result); } /* Data-processing (3 source) @@ -5007,8 +4881,6 @@ static void disas_data_proc_3src(DisasContext *s, uint32_t insn) } else { tcg_gen_mulu2_i64(low_bits, tcg_rd, tcg_rn, tcg_rm); } - - tcg_temp_free_i64(low_bits); return; } @@ -5044,10 +4916,6 @@ static void disas_data_proc_3src(DisasContext *s, uint32_t insn) if (!sf) { tcg_gen_ext32u_i64(cpu_reg(s, rd), cpu_reg(s, rd)); } - - tcg_temp_free_i64(tcg_op1); - tcg_temp_free_i64(tcg_op2); - tcg_temp_free_i64(tcg_tmp); } /* Add/subtract (with carry) @@ -5073,7 +4941,7 @@ static void disas_adc_sbc(DisasContext *s, uint32_t insn) tcg_rn = cpu_reg(s, rn); if (op) { - tcg_y = new_tmp_a64(s); + tcg_y = tcg_temp_new_i64(); tcg_gen_not_i64(tcg_y, cpu_reg(s, rm)); } else { tcg_y = cpu_reg(s, rm); @@ -5127,8 +4995,6 @@ static void disas_rotate_right_into_flags(DisasContext *s, uint32_t insn) if (mask & 1) { /* V */ tcg_gen_shli_i32(cpu_VF, nzcv, 31 - 0); } - - tcg_temp_free_i32(nzcv); } /* @@ -5161,7 +5027,6 @@ static void disas_evaluate_into_flags(DisasContext *s, uint32_t insn) tcg_gen_shli_i32(cpu_VF, tmp, shift - 1); tcg_gen_mov_i32(cpu_ZF, cpu_NF); tcg_gen_xor_i32(cpu_VF, cpu_VF, cpu_NF); - tcg_temp_free_i32(tmp); } /* Conditional compare (immediate / register) @@ -5198,11 +5063,10 @@ static void disas_cc(DisasContext *s, uint32_t insn) tcg_t0 = tcg_temp_new_i32(); arm_test_cc(&c, cond); tcg_gen_setcondi_i32(tcg_invert_cond(c.cond), tcg_t0, c.value, 0); - arm_free_cc(&c); /* Load the arguments for the new comparison. */ if (is_imm) { - tcg_y = new_tmp_a64(s); + tcg_y = tcg_temp_new_i64(); tcg_gen_movi_i64(tcg_y, y); } else { tcg_y = cpu_reg(s, y); @@ -5216,7 +5080,6 @@ static void disas_cc(DisasContext *s, uint32_t insn) } else { gen_add_CC(sf, tcg_tmp, tcg_rn, tcg_y); } - tcg_temp_free_i64(tcg_tmp); /* If COND was false, force the flags to #nzcv. Compute two masks * to help with this: T1 = (COND ? 0 : -1), T2 = (COND ? -1 : 0). @@ -5264,9 +5127,6 @@ static void disas_cc(DisasContext *s, uint32_t insn) tcg_gen_and_i32(cpu_VF, cpu_VF, tcg_t2); } } - tcg_temp_free_i32(tcg_t0); - tcg_temp_free_i32(tcg_t1); - tcg_temp_free_i32(tcg_t2); } /* Conditional select @@ -5301,9 +5161,12 @@ static void disas_cond_select(DisasContext *s, uint32_t insn) if (rn == 31 && rm == 31 && (else_inc ^ else_inv)) { /* CSET & CSETM. */ - tcg_gen_setcond_i64(tcg_invert_cond(c.cond), tcg_rd, c.value, zero); if (else_inv) { - tcg_gen_neg_i64(tcg_rd, tcg_rd); + tcg_gen_negsetcond_i64(tcg_invert_cond(c.cond), + tcg_rd, c.value, zero); + } else { + tcg_gen_setcond_i64(tcg_invert_cond(c.cond), + tcg_rd, c.value, zero); } } else { TCGv_i64 t_true = cpu_reg(s, rn); @@ -5318,8 +5181,6 @@ static void disas_cond_select(DisasContext *s, uint32_t insn) tcg_gen_movcond_i64(c.cond, tcg_rd, c.value, zero, t_true, t_false); } - a64_free_cc(&c); - if (!sf) { tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } @@ -5339,7 +5200,6 @@ static void handle_clz(DisasContext *s, unsigned int sf, tcg_gen_extrl_i64_i32(tcg_tmp32, tcg_rn); tcg_gen_clzi_i32(tcg_tmp32, tcg_tmp32, 32); tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32); - tcg_temp_free_i32(tcg_tmp32); } } @@ -5357,7 +5217,6 @@ static void handle_cls(DisasContext *s, unsigned int sf, tcg_gen_extrl_i64_i32(tcg_tmp32, tcg_rn); tcg_gen_clrsb_i32(tcg_tmp32, tcg_tmp32); tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32); - tcg_temp_free_i32(tcg_tmp32); } } @@ -5375,7 +5234,6 @@ static void handle_rbit(DisasContext *s, unsigned int sf, tcg_gen_extrl_i64_i32(tcg_tmp32, tcg_rn); gen_helper_rbit(tcg_tmp32, tcg_tmp32); tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32); - tcg_temp_free_i32(tcg_tmp32); } } @@ -5421,8 +5279,6 @@ static void handle_rev16(DisasContext *s, unsigned int sf, tcg_gen_and_i64(tcg_tmp, tcg_tmp, mask); tcg_gen_shli_i64(tcg_rd, tcg_rd, 8); tcg_gen_or_i64(tcg_rd, tcg_rd, tcg_tmp); - - tcg_temp_free_i64(tcg_tmp); } /* Data-processing (1 source) @@ -5476,7 +5332,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) case MAP(1, 0x01, 0x00): /* PACIA */ if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_pacia(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + gen_helper_pacia(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); } else if (!dc_isar_feature(aa64_pauth, s)) { goto do_unallocated; } @@ -5484,7 +5340,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) case MAP(1, 0x01, 0x01): /* PACIB */ if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_pacib(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + gen_helper_pacib(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); } else if (!dc_isar_feature(aa64_pauth, s)) { goto do_unallocated; } @@ -5492,7 +5348,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) case MAP(1, 0x01, 0x02): /* PACDA */ if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_pacda(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + gen_helper_pacda(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); } else if (!dc_isar_feature(aa64_pauth, s)) { goto do_unallocated; } @@ -5500,7 +5356,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) case MAP(1, 0x01, 0x03): /* PACDB */ if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_pacdb(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + gen_helper_pacdb(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); } else if (!dc_isar_feature(aa64_pauth, s)) { goto do_unallocated; } @@ -5508,7 +5364,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) case MAP(1, 0x01, 0x04): /* AUTIA */ if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_autia(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + gen_helper_autia(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); } else if (!dc_isar_feature(aa64_pauth, s)) { goto do_unallocated; } @@ -5516,7 +5372,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) case MAP(1, 0x01, 0x05): /* AUTIB */ if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_autib(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + gen_helper_autib(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); } else if (!dc_isar_feature(aa64_pauth, s)) { goto do_unallocated; } @@ -5524,7 +5380,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) case MAP(1, 0x01, 0x06): /* AUTDA */ if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_autda(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + gen_helper_autda(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); } else if (!dc_isar_feature(aa64_pauth, s)) { goto do_unallocated; } @@ -5532,7 +5388,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) case MAP(1, 0x01, 0x07): /* AUTDB */ if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_autdb(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + gen_helper_autdb(tcg_rd, tcg_env, tcg_rd, cpu_reg_sp(s, rn)); } else if (!dc_isar_feature(aa64_pauth, s)) { goto do_unallocated; } @@ -5542,7 +5398,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) goto do_unallocated; } else if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_pacia(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + gen_helper_pacia(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); } break; case MAP(1, 0x01, 0x09): /* PACIZB */ @@ -5550,7 +5406,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) goto do_unallocated; } else if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_pacib(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + gen_helper_pacib(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); } break; case MAP(1, 0x01, 0x0a): /* PACDZA */ @@ -5558,7 +5414,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) goto do_unallocated; } else if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_pacda(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + gen_helper_pacda(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); } break; case MAP(1, 0x01, 0x0b): /* PACDZB */ @@ -5566,7 +5422,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) goto do_unallocated; } else if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_pacdb(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + gen_helper_pacdb(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); } break; case MAP(1, 0x01, 0x0c): /* AUTIZA */ @@ -5574,7 +5430,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) goto do_unallocated; } else if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_autia(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + gen_helper_autia(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); } break; case MAP(1, 0x01, 0x0d): /* AUTIZB */ @@ -5582,7 +5438,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) goto do_unallocated; } else if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_autib(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + gen_helper_autib(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); } break; case MAP(1, 0x01, 0x0e): /* AUTDZA */ @@ -5590,7 +5446,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) goto do_unallocated; } else if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_autda(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + gen_helper_autda(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); } break; case MAP(1, 0x01, 0x0f): /* AUTDZB */ @@ -5598,7 +5454,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) goto do_unallocated; } else if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_autdb(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + gen_helper_autdb(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); } break; case MAP(1, 0x01, 0x10): /* XPACI */ @@ -5606,7 +5462,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) goto do_unallocated; } else if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_xpaci(tcg_rd, cpu_env, tcg_rd); + gen_helper_xpaci(tcg_rd, tcg_env, tcg_rd); } break; case MAP(1, 0x01, 0x11): /* XPACD */ @@ -5614,7 +5470,7 @@ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) goto do_unallocated; } else if (s->pauth_active) { tcg_rd = cpu_reg(s, rd); - gen_helper_xpacd(tcg_rd, cpu_env, tcg_rd); + gen_helper_xpacd(tcg_rd, tcg_env, tcg_rd); } break; default: @@ -5633,8 +5489,8 @@ static void handle_div(DisasContext *s, bool is_signed, unsigned int sf, tcg_rd = cpu_reg(s, rd); if (!sf && is_signed) { - tcg_n = new_tmp_a64(s); - tcg_m = new_tmp_a64(s); + tcg_n = tcg_temp_new_i64(); + tcg_m = tcg_temp_new_i64(); tcg_gen_ext32s_i64(tcg_n, cpu_reg(s, rn)); tcg_gen_ext32s_i64(tcg_m, cpu_reg(s, rm)); } else { @@ -5664,7 +5520,6 @@ static void handle_shift_reg(DisasContext *s, tcg_gen_andi_i64(tcg_shift, cpu_reg(s, rm), sf ? 63 : 31); shift_reg(tcg_rd, tcg_rn, sf, shift_type, tcg_shift); - tcg_temp_free_i64(tcg_shift); } /* CRC32[BHWX], CRC32C[BHWX] */ @@ -5699,7 +5554,7 @@ static void handle_crc32(DisasContext *s, default: g_assert_not_reached(); } - tcg_val = new_tmp_a64(s); + tcg_val = tcg_temp_new_i64(); tcg_gen_andi_i64(tcg_val, cpu_reg(s, rm), mask); } @@ -5764,8 +5619,8 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) if (sf == 0 || !dc_isar_feature(aa64_mte_insn_reg, s)) { goto do_unallocated; } - if (s->ata) { - gen_helper_irg(cpu_reg_sp(s, rd), cpu_env, + if (s->ata[0]) { + gen_helper_irg(cpu_reg_sp(s, rd), tcg_env, cpu_reg_sp(s, rn), cpu_reg(s, rm)); } else { gen_address_with_allocation_tag0(cpu_reg_sp(s, rd), @@ -5781,8 +5636,6 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) tcg_gen_extract_i64(t, cpu_reg_sp(s, rn), 56, 4); tcg_gen_shl_i64(t, tcg_constant_i64(1), t); tcg_gen_or_i64(cpu_reg(s, rd), cpu_reg(s, rm), t); - - tcg_temp_free_i64(t); } break; case 8: /* LSLV */ @@ -5801,7 +5654,7 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) if (sf == 0 || !dc_isar_feature(aa64_pauth, s)) { goto do_unallocated; } - gen_helper_pacga(cpu_reg(s, rd), cpu_env, + gen_helper_pacga(cpu_reg(s, rd), tcg_env, cpu_reg(s, rn), cpu_reg_sp(s, rm)); break; case 16: @@ -5926,8 +5779,6 @@ static void handle_fp_compare(DisasContext *s, int size, } else { gen_helper_vfp_cmpd_a64(tcg_flags, tcg_vn, tcg_vm, fpst); } - tcg_temp_free_i64(tcg_vn); - tcg_temp_free_i64(tcg_vm); } else { TCGv_i32 tcg_vn = tcg_temp_new_i32(); TCGv_i32 tcg_vm = tcg_temp_new_i32(); @@ -5957,16 +5808,9 @@ static void handle_fp_compare(DisasContext *s, int size, default: g_assert_not_reached(); } - - tcg_temp_free_i32(tcg_vn); - tcg_temp_free_i32(tcg_vm); } - tcg_temp_free_ptr(fpst); - gen_set_nzcv(tcg_flags); - - tcg_temp_free_i64(tcg_flags); } /* Floating point compare @@ -6138,13 +5982,10 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) a64_test_cc(&c, cond); tcg_gen_movcond_i64(c.cond, t_true, c.value, tcg_constant_i64(0), t_true, t_false); - tcg_temp_free_i64(t_false); - a64_free_cc(&c); /* Note that sregs & hregs write back zeros to the high bits, and we've already done the zero-extension. */ write_fp_dreg(s, rd, t_true); - tcg_temp_free_i64(t_true); } /* Floating-point data-processing (1 source) - half precision */ @@ -6174,14 +6015,12 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) case 0xb: /* FRINTZ */ case 0xc: /* FRINTA */ { - TCGv_i32 tcg_rmode = tcg_const_i32(arm_rmode_to_sf(opcode & 7)); + TCGv_i32 tcg_rmode; + fpst = fpstatus_ptr(FPST_FPCR_F16); - - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + tcg_rmode = gen_set_rmode(opcode & 7, fpst); gen_helper_advsimd_rinth(tcg_res, tcg_op, fpst); - - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); - tcg_temp_free_i32(tcg_rmode); + gen_restore_rmode(tcg_rmode, fpst); break; } case 0xe: /* FRINTX */ @@ -6197,12 +6036,6 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) } write_fp_sreg(s, rd, tcg_res); - - if (fpst) { - tcg_temp_free_ptr(fpst); - } - tcg_temp_free_i32(tcg_op); - tcg_temp_free_i32(tcg_res); } /* Floating-point data-processing (1 source) - single precision */ @@ -6227,7 +6060,7 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) gen_helper_vfp_negs(tcg_res, tcg_op); goto done; case 0x3: /* FSQRT */ - gen_helper_vfp_sqrts(tcg_res, tcg_op, cpu_env); + gen_helper_vfp_sqrts(tcg_res, tcg_op, tcg_env); goto done; case 0x6: /* BFCVT */ gen_fpst = gen_helper_bfcvt; @@ -6237,7 +6070,7 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) case 0xa: /* FRINTM */ case 0xb: /* FRINTZ */ case 0xc: /* FRINTA */ - rmode = arm_rmode_to_sf(opcode & 7); + rmode = opcode & 7; gen_fpst = gen_helper_rints; break; case 0xe: /* FRINTX */ @@ -6247,14 +6080,14 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) gen_fpst = gen_helper_rints; break; case 0x10: /* FRINT32Z */ - rmode = float_round_to_zero; + rmode = FPROUNDING_ZERO; gen_fpst = gen_helper_frint32_s; break; case 0x11: /* FRINT32X */ gen_fpst = gen_helper_frint32_s; break; case 0x12: /* FRINT64Z */ - rmode = float_round_to_zero; + rmode = FPROUNDING_ZERO; gen_fpst = gen_helper_frint64_s; break; case 0x13: /* FRINT64X */ @@ -6266,20 +6099,15 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) fpst = fpstatus_ptr(FPST_FPCR); if (rmode >= 0) { - TCGv_i32 tcg_rmode = tcg_const_i32(rmode); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + TCGv_i32 tcg_rmode = gen_set_rmode(rmode, fpst); gen_fpst(tcg_res, tcg_op, fpst); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); - tcg_temp_free_i32(tcg_rmode); + gen_restore_rmode(tcg_rmode, fpst); } else { gen_fpst(tcg_res, tcg_op, fpst); } - tcg_temp_free_ptr(fpst); done: write_fp_sreg(s, rd, tcg_res); - tcg_temp_free_i32(tcg_op); - tcg_temp_free_i32(tcg_res); } /* Floating-point data-processing (1 source) - double precision */ @@ -6307,14 +6135,14 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) gen_helper_vfp_negd(tcg_res, tcg_op); goto done; case 0x3: /* FSQRT */ - gen_helper_vfp_sqrtd(tcg_res, tcg_op, cpu_env); + gen_helper_vfp_sqrtd(tcg_res, tcg_op, tcg_env); goto done; case 0x8: /* FRINTN */ case 0x9: /* FRINTP */ case 0xa: /* FRINTM */ case 0xb: /* FRINTZ */ case 0xc: /* FRINTA */ - rmode = arm_rmode_to_sf(opcode & 7); + rmode = opcode & 7; gen_fpst = gen_helper_rintd; break; case 0xe: /* FRINTX */ @@ -6324,14 +6152,14 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) gen_fpst = gen_helper_rintd; break; case 0x10: /* FRINT32Z */ - rmode = float_round_to_zero; + rmode = FPROUNDING_ZERO; gen_fpst = gen_helper_frint32_d; break; case 0x11: /* FRINT32X */ gen_fpst = gen_helper_frint32_d; break; case 0x12: /* FRINT64Z */ - rmode = float_round_to_zero; + rmode = FPROUNDING_ZERO; gen_fpst = gen_helper_frint64_d; break; case 0x13: /* FRINT64X */ @@ -6343,20 +6171,15 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) fpst = fpstatus_ptr(FPST_FPCR); if (rmode >= 0) { - TCGv_i32 tcg_rmode = tcg_const_i32(rmode); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + TCGv_i32 tcg_rmode = gen_set_rmode(rmode, fpst); gen_fpst(tcg_res, tcg_op, fpst); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); - tcg_temp_free_i32(tcg_rmode); + gen_restore_rmode(tcg_rmode, fpst); } else { gen_fpst(tcg_res, tcg_op, fpst); } - tcg_temp_free_ptr(fpst); done: write_fp_dreg(s, rd, tcg_res); - tcg_temp_free_i64(tcg_op); - tcg_temp_free_i64(tcg_res); } static void handle_fp_fcvt(DisasContext *s, int opcode, @@ -6369,9 +6192,8 @@ static void handle_fp_fcvt(DisasContext *s, int opcode, if (dtype == 1) { /* Single to double */ TCGv_i64 tcg_rd = tcg_temp_new_i64(); - gen_helper_vfp_fcvtds(tcg_rd, tcg_rn, cpu_env); + gen_helper_vfp_fcvtds(tcg_rd, tcg_rn, tcg_env); write_fp_dreg(s, rd, tcg_rd); - tcg_temp_free_i64(tcg_rd); } else { /* Single to half */ TCGv_i32 tcg_rd = tcg_temp_new_i32(); @@ -6381,11 +6203,7 @@ static void handle_fp_fcvt(DisasContext *s, int opcode, gen_helper_vfp_fcvt_f32_to_f16(tcg_rd, tcg_rn, fpst, ahp); /* write_fp_sreg is OK here because top half of tcg_rd is zero */ write_fp_sreg(s, rd, tcg_rd); - tcg_temp_free_i32(tcg_rd); - tcg_temp_free_i32(ahp); - tcg_temp_free_ptr(fpst); } - tcg_temp_free_i32(tcg_rn); break; } case 0x1: @@ -6394,19 +6212,15 @@ static void handle_fp_fcvt(DisasContext *s, int opcode, TCGv_i32 tcg_rd = tcg_temp_new_i32(); if (dtype == 0) { /* Double to single */ - gen_helper_vfp_fcvtsd(tcg_rd, tcg_rn, cpu_env); + gen_helper_vfp_fcvtsd(tcg_rd, tcg_rn, tcg_env); } else { TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); TCGv_i32 ahp = get_ahp_flag(); /* Double to half */ gen_helper_vfp_fcvt_f64_to_f16(tcg_rd, tcg_rn, fpst, ahp); /* write_fp_sreg is OK here because top half of tcg_rd is zero */ - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(ahp); } write_fp_sreg(s, rd, tcg_rd); - tcg_temp_free_i32(tcg_rd); - tcg_temp_free_i64(tcg_rn); break; } case 0x3: @@ -6420,17 +6234,12 @@ 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_i32(tcg_rd); } else { /* Half to double */ TCGv_i64 tcg_rd = tcg_temp_new_i64(); gen_helper_vfp_fcvt_f16_to_f64(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); write_fp_dreg(s, rd, tcg_rd); - 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: @@ -6578,11 +6387,6 @@ static void handle_fp_2src_single(DisasContext *s, int opcode, } write_fp_sreg(s, rd, tcg_res); - - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tcg_op1); - tcg_temp_free_i32(tcg_op2); - tcg_temp_free_i32(tcg_res); } /* Floating-point data-processing (2 source) - double precision */ @@ -6631,11 +6435,6 @@ static void handle_fp_2src_double(DisasContext *s, int opcode, } write_fp_dreg(s, rd, tcg_res); - - tcg_temp_free_ptr(fpst); - tcg_temp_free_i64(tcg_op1); - tcg_temp_free_i64(tcg_op2); - tcg_temp_free_i64(tcg_res); } /* Floating-point data-processing (2 source) - half precision */ @@ -6686,11 +6485,6 @@ static void handle_fp_2src_half(DisasContext *s, int opcode, } write_fp_sreg(s, rd, tcg_res); - - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tcg_op1); - tcg_temp_free_i32(tcg_op2); - tcg_temp_free_i32(tcg_res); } /* Floating point data-processing (2 source) @@ -6771,12 +6565,6 @@ static void handle_fp_3src_single(DisasContext *s, bool o0, bool o1, gen_helper_vfp_muladds(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst); write_fp_sreg(s, rd, tcg_res); - - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tcg_op1); - tcg_temp_free_i32(tcg_op2); - tcg_temp_free_i32(tcg_op3); - tcg_temp_free_i32(tcg_res); } /* Floating-point data-processing (3 source) - double precision */ @@ -6809,12 +6597,6 @@ static void handle_fp_3src_double(DisasContext *s, bool o0, bool o1, gen_helper_vfp_muladdd(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst); write_fp_dreg(s, rd, tcg_res); - - tcg_temp_free_ptr(fpst); - tcg_temp_free_i64(tcg_op1); - tcg_temp_free_i64(tcg_op2); - tcg_temp_free_i64(tcg_op3); - tcg_temp_free_i64(tcg_res); } /* Floating-point data-processing (3 source) - half precision */ @@ -6847,12 +6629,6 @@ static void handle_fp_3src_half(DisasContext *s, bool o0, bool o1, gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst); write_fp_sreg(s, rd, tcg_res); - - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tcg_op1); - tcg_temp_free_i32(tcg_op2); - tcg_temp_free_i32(tcg_op3); - tcg_temp_free_i32(tcg_res); } /* Floating point data-processing (3 source) @@ -6972,7 +6748,7 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, if (itof) { TCGv_i64 tcg_int = cpu_reg(s, rn); if (!sf) { - TCGv_i64 tcg_extend = new_tmp_a64(s); + TCGv_i64 tcg_extend = tcg_temp_new_i64(); if (is_signed) { tcg_gen_ext32s_i64(tcg_extend, tcg_int); @@ -6994,7 +6770,6 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, tcg_shift, tcg_fpstatus); } write_fp_dreg(s, rd, tcg_double); - tcg_temp_free_i64(tcg_double); break; case 0: /* float32 */ @@ -7007,7 +6782,6 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, tcg_shift, tcg_fpstatus); } write_fp_sreg(s, rd, tcg_single); - tcg_temp_free_i32(tcg_single); break; case 3: /* float16 */ @@ -7020,7 +6794,6 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, tcg_shift, tcg_fpstatus); } write_fp_sreg(s, rd, tcg_single); - tcg_temp_free_i32(tcg_single); break; default: @@ -7037,9 +6810,7 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, rmode = FPROUNDING_TIEAWAY; } - tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode)); - - gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); + tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); switch (type) { case 1: /* float64 */ @@ -7064,7 +6835,6 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, if (!sf) { tcg_gen_ext32u_i64(tcg_int, tcg_int); } - tcg_temp_free_i64(tcg_double); break; case 0: /* float32 */ @@ -7087,9 +6857,7 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, tcg_shift, tcg_fpstatus); } tcg_gen_extu_i32_i64(tcg_int, tcg_dest); - tcg_temp_free_i32(tcg_dest); } - tcg_temp_free_i32(tcg_single); break; case 3: /* float16 */ @@ -7112,20 +6880,15 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, tcg_shift, tcg_fpstatus); } tcg_gen_extu_i32_i64(tcg_int, tcg_dest); - tcg_temp_free_i32(tcg_dest); } - tcg_temp_free_i32(tcg_single); break; default: g_assert_not_reached(); } - gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); - tcg_temp_free_i32(tcg_rmode); + gen_restore_rmode(tcg_rmode, tcg_fpstatus); } - - tcg_temp_free_ptr(tcg_fpstatus); } /* Floating point <-> fixed point conversions @@ -7202,7 +6965,6 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) tmp = tcg_temp_new_i64(); tcg_gen_ext32u_i64(tmp, tcg_rn); write_fp_dreg(s, rd, tmp); - tcg_temp_free_i64(tmp); break; case 1: /* 64 bit */ @@ -7210,7 +6972,7 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) break; case 2: /* 64 bit to top half. */ - tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_hi_offset(s, rd)); + tcg_gen_st_i64(tcg_rn, tcg_env, fp_reg_hi_offset(s, rd)); clear_vec_high(s, true, rd); break; case 3: @@ -7218,7 +6980,6 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) tmp = tcg_temp_new_i64(); tcg_gen_ext16u_i64(tmp, tcg_rn); write_fp_dreg(s, rd, tmp); - tcg_temp_free_i64(tmp); break; default: g_assert_not_reached(); @@ -7229,19 +6990,19 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) switch (type) { case 0: /* 32 bit */ - tcg_gen_ld32u_i64(tcg_rd, cpu_env, fp_reg_offset(s, rn, MO_32)); + tcg_gen_ld32u_i64(tcg_rd, tcg_env, fp_reg_offset(s, rn, MO_32)); break; case 1: /* 64 bit */ - tcg_gen_ld_i64(tcg_rd, cpu_env, fp_reg_offset(s, rn, MO_64)); + tcg_gen_ld_i64(tcg_rd, tcg_env, fp_reg_offset(s, rn, MO_64)); break; case 2: /* 64 bits from top half */ - tcg_gen_ld_i64(tcg_rd, cpu_env, fp_reg_hi_offset(s, rn)); + tcg_gen_ld_i64(tcg_rd, tcg_env, fp_reg_hi_offset(s, rn)); break; case 3: /* 16 bit */ - tcg_gen_ld16u_i64(tcg_rd, cpu_env, fp_reg_offset(s, rn, MO_16)); + tcg_gen_ld16u_i64(tcg_rd, tcg_env, fp_reg_offset(s, rn, MO_16)); break; default: g_assert_not_reached(); @@ -7256,15 +7017,11 @@ static void handle_fjcvtzs(DisasContext *s, int rd, int rn) gen_helper_fjcvtzs(t, t, fpstatus); - tcg_temp_free_ptr(fpstatus); - tcg_gen_ext32u_i64(cpu_reg(s, rd), t); tcg_gen_extrh_i64_i32(cpu_ZF, t); tcg_gen_movi_i32(cpu_CF, 0); tcg_gen_movi_i32(cpu_NF, 0); tcg_gen_movi_i32(cpu_VF, 0); - - tcg_temp_free_i64(t); } /* Floating point <-> integer conversions @@ -7429,8 +7186,6 @@ static void do_ext64(DisasContext *s, TCGv_i64 tcg_left, TCGv_i64 tcg_right, tcg_gen_shri_i64(tcg_right, tcg_right, pos); tcg_gen_shli_i64(tcg_tmp, tcg_left, 64 - pos); tcg_gen_or_i64(tcg_right, tcg_right, tcg_tmp); - - tcg_temp_free_i64(tcg_tmp); } /* EXT @@ -7495,16 +7250,13 @@ static void disas_simd_ext(DisasContext *s, uint32_t insn) tcg_hh = tcg_temp_new_i64(); read_vec_element(s, tcg_hh, elt->reg, elt->elt, MO_64); do_ext64(s, tcg_hh, tcg_resh, pos); - tcg_temp_free_i64(tcg_hh); } } write_vec_element(s, tcg_resl, rd, 0, MO_64); - tcg_temp_free_i64(tcg_resl); if (is_q) { write_vec_element(s, tcg_resh, rd, 1, MO_64); } - tcg_temp_free_i64(tcg_resh); clear_vec_high(s, is_q, rd); } @@ -7534,7 +7286,7 @@ static void disas_simd_tb(DisasContext *s, uint32_t insn) } tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rm), cpu_env, + vec_full_reg_offset(s, rm), tcg_env, is_q ? 16 : 8, vec_full_reg_size(s), (len << 6) | (is_tbx << 5) | rn, gen_helper_simd_tblx); @@ -7559,10 +7311,10 @@ static void disas_simd_zip_trn(DisasContext *s, uint32_t insn) bool part = extract32(insn, 14, 1); bool is_q = extract32(insn, 30, 1); int esize = 8 << size; - int i, ofs; + int i; int datasize = is_q ? 128 : 64; int elements = datasize / esize; - TCGv_i64 tcg_res, tcg_resl, tcg_resh; + TCGv_i64 tcg_res[2], tcg_ele; if (opcode == 0 || (size == 3 && !is_q)) { unallocated_encoding(s); @@ -7573,37 +7325,39 @@ static void disas_simd_zip_trn(DisasContext *s, uint32_t insn) return; } - tcg_resl = tcg_const_i64(0); - tcg_resh = is_q ? tcg_const_i64(0) : NULL; - tcg_res = tcg_temp_new_i64(); + tcg_res[0] = tcg_temp_new_i64(); + tcg_res[1] = is_q ? tcg_temp_new_i64() : NULL; + tcg_ele = tcg_temp_new_i64(); for (i = 0; i < elements; i++) { + int o, w; + switch (opcode) { case 1: /* UZP1/2 */ { int midpoint = elements / 2; if (i < midpoint) { - read_vec_element(s, tcg_res, rn, 2 * i + part, size); + read_vec_element(s, tcg_ele, rn, 2 * i + part, size); } else { - read_vec_element(s, tcg_res, rm, + read_vec_element(s, tcg_ele, rm, 2 * (i - midpoint) + part, size); } break; } case 2: /* TRN1/2 */ if (i & 1) { - read_vec_element(s, tcg_res, rm, (i & ~1) + part, size); + read_vec_element(s, tcg_ele, rm, (i & ~1) + part, size); } else { - read_vec_element(s, tcg_res, rn, (i & ~1) + part, size); + read_vec_element(s, tcg_ele, rn, (i & ~1) + part, size); } break; case 3: /* ZIP1/2 */ { int base = part * elements / 2; if (i & 1) { - read_vec_element(s, tcg_res, rm, base + (i >> 1), size); + read_vec_element(s, tcg_ele, rm, base + (i >> 1), size); } else { - read_vec_element(s, tcg_res, rn, base + (i >> 1), size); + read_vec_element(s, tcg_ele, rn, base + (i >> 1), size); } break; } @@ -7611,24 +7365,18 @@ static void disas_simd_zip_trn(DisasContext *s, uint32_t insn) g_assert_not_reached(); } - ofs = i * esize; - if (ofs < 64) { - tcg_gen_shli_i64(tcg_res, tcg_res, ofs); - tcg_gen_or_i64(tcg_resl, tcg_resl, tcg_res); + w = (i * esize) / 64; + o = (i * esize) % 64; + if (o == 0) { + tcg_gen_mov_i64(tcg_res[w], tcg_ele); } else { - tcg_gen_shli_i64(tcg_res, tcg_res, ofs - 64); - tcg_gen_or_i64(tcg_resh, tcg_resh, tcg_res); + tcg_gen_shli_i64(tcg_ele, tcg_ele, o); + tcg_gen_or_i64(tcg_res[w], tcg_res[w], tcg_ele); } } - tcg_temp_free_i64(tcg_res); - - write_vec_element(s, tcg_resl, rd, 0, MO_64); - tcg_temp_free_i64(tcg_resl); - - if (is_q) { - write_vec_element(s, tcg_resh, rd, 1, MO_64); - tcg_temp_free_i64(tcg_resh); + for (i = 0; i <= is_q; ++i) { + write_vec_element(s, tcg_res[i], rd, i, MO_64); } clear_vec_high(s, is_q, rd); } @@ -7698,9 +7446,6 @@ static TCGv_i32 do_reduction_op(DisasContext *s, int fpopcode, int rn, default: g_assert_not_reached(); } - - tcg_temp_free_i32(tcg_hi); - tcg_temp_free_i32(tcg_lo); return tcg_res; } } @@ -7828,12 +7573,8 @@ static void disas_simd_across_lanes(DisasContext *s, uint32_t insn) TCGv_i32 tcg_res32 = do_reduction_op(s, fpopcode, rn, esize, (is_q ? 128 : 64), vmap, fpst); tcg_gen_extu_i32_i64(tcg_res, tcg_res32); - tcg_temp_free_i32(tcg_res32); - tcg_temp_free_ptr(fpst); } - tcg_temp_free_i64(tcg_elt); - /* Now truncate the result to the width required for the final output */ if (opcode == 0x03) { /* SADDLV, UADDLV: result is 2*esize */ @@ -7857,7 +7598,6 @@ static void disas_simd_across_lanes(DisasContext *s, uint32_t insn) } write_fp_dreg(s, rd, tcg_res); - tcg_temp_free_i64(tcg_res); } /* DUP (Element, Vector) @@ -7920,7 +7660,6 @@ static void handle_simd_dupes(DisasContext *s, int rd, int rn, tmp = tcg_temp_new_i64(); read_vec_element(s, tmp, rn, index, size); write_fp_dreg(s, rd, tmp); - tcg_temp_free_i64(tmp); } /* DUP (General) @@ -7988,8 +7727,6 @@ static void handle_simd_inse(DisasContext *s, int rd, int rn, read_vec_element(s, tmp, rn, src_index, size); 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); } @@ -8300,10 +8037,6 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) } write_fp_dreg(s, rd, tcg_res); - - tcg_temp_free_i64(tcg_op1); - tcg_temp_free_i64(tcg_op2); - tcg_temp_free_i64(tcg_res); } else { TCGv_i32 tcg_op1 = tcg_temp_new_i32(); TCGv_i32 tcg_op2 = tcg_temp_new_i32(); @@ -8355,14 +8088,6 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) } write_fp_sreg(s, rd, tcg_res); - - tcg_temp_free_i32(tcg_op1); - tcg_temp_free_i32(tcg_op2); - tcg_temp_free_i32(tcg_res); - } - - if (fpst) { - tcg_temp_free_ptr(fpst); } } @@ -8447,10 +8172,6 @@ static void handle_shri_with_rndacc(TCGv_i64 tcg_res, TCGv_i64 tcg_src, } else { tcg_gen_mov_i64(tcg_res, tcg_src); } - - if (extended_result) { - tcg_temp_free_i64(tcg_src_hi); - } } /* SSHR[RA]/USHR[RA] - Scalar shift right (optional rounding/accumulate) */ @@ -8516,9 +8237,6 @@ static void handle_scalar_simd_shri(DisasContext *s, } write_fp_dreg(s, rd, tcg_rd); - - tcg_temp_free_i64(tcg_rn); - tcg_temp_free_i64(tcg_rd); } /* SHL/SLI - Scalar shift left */ @@ -8551,9 +8269,6 @@ static void handle_scalar_simd_shli(DisasContext *s, bool insert, } write_fp_dreg(s, rd, tcg_rd); - - tcg_temp_free_i64(tcg_rn); - tcg_temp_free_i64(tcg_rd); } /* SQSHRN/SQSHRUN - Saturating (signed/unsigned) shift right with @@ -8613,7 +8328,7 @@ static void handle_vec_simd_sqshrn(DisasContext *s, bool is_scalar, bool is_q, tcg_rn = tcg_temp_new_i64(); tcg_rd = tcg_temp_new_i64(); tcg_rd_narrowed = tcg_temp_new_i32(); - tcg_final = tcg_const_i64(0); + tcg_final = tcg_temp_new_i64(); if (round) { tcg_round = tcg_constant_i64(1ULL << (shift - 1)); @@ -8625,9 +8340,13 @@ static void handle_vec_simd_sqshrn(DisasContext *s, bool is_scalar, bool is_q, read_vec_element(s, tcg_rn, rn, i, ldop); handle_shri_with_rndacc(tcg_rd, tcg_rn, tcg_round, false, is_u_shift, size+1, shift); - narrowfn(tcg_rd_narrowed, cpu_env, tcg_rd); + narrowfn(tcg_rd_narrowed, tcg_env, tcg_rd); tcg_gen_extu_i32_i64(tcg_rd, tcg_rd_narrowed); - tcg_gen_deposit_i64(tcg_final, tcg_final, tcg_rd, esize * i, esize); + if (i == 0) { + tcg_gen_extract_i64(tcg_final, tcg_rd, 0, esize); + } else { + tcg_gen_deposit_i64(tcg_final, tcg_final, tcg_rd, esize * i, esize); + } } if (!is_q) { @@ -8635,12 +8354,6 @@ static void handle_vec_simd_sqshrn(DisasContext *s, bool is_scalar, bool is_q, } else { write_vec_element(s, tcg_final, rd, 1, MO_64); } - - tcg_temp_free_i64(tcg_rn); - tcg_temp_free_i64(tcg_rd); - tcg_temp_free_i32(tcg_rd_narrowed); - tcg_temp_free_i64(tcg_final); - clear_vec_high(s, is_q, rd); } @@ -8699,10 +8412,8 @@ static void handle_simd_qshl(DisasContext *s, bool scalar, bool is_q, TCGv_i64 tcg_op = tcg_temp_new_i64(); read_vec_element(s, tcg_op, rn, pass, MO_64); - genfn(tcg_op, cpu_env, tcg_op, tcg_shift); + genfn(tcg_op, tcg_env, tcg_op, tcg_shift); write_vec_element(s, tcg_op, rd, pass, MO_64); - - tcg_temp_free_i64(tcg_op); } clear_vec_high(s, is_q, rd); } else { @@ -8730,7 +8441,7 @@ static void handle_simd_qshl(DisasContext *s, bool scalar, bool is_q, TCGv_i32 tcg_op = tcg_temp_new_i32(); read_vec_element_i32(s, tcg_op, rn, pass, memop); - genfn(tcg_op, cpu_env, tcg_op, tcg_shift); + genfn(tcg_op, tcg_env, tcg_op, tcg_shift); if (scalar) { switch (size) { case 0: @@ -8748,8 +8459,6 @@ static void handle_simd_qshl(DisasContext *s, bool scalar, bool is_q, } else { write_vec_element_i32(s, tcg_op, rd, pass, MO_32); } - - tcg_temp_free_i32(tcg_op); } if (!scalar) { @@ -8793,10 +8502,6 @@ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, write_vec_element(s, tcg_double, rd, pass, MO_64); } } - - tcg_temp_free_i64(tcg_int64); - tcg_temp_free_i64(tcg_double); - } else { TCGv_i32 tcg_int32 = tcg_temp_new_i32(); TCGv_i32 tcg_float = tcg_temp_new_i32(); @@ -8849,13 +8554,8 @@ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, write_vec_element_i32(s, tcg_float, rd, pass, size); } } - - tcg_temp_free_i32(tcg_int32); - tcg_temp_free_i32(tcg_float); } - tcg_temp_free_ptr(tcg_fpst); - clear_vec_high(s, elements << size == 16, rd); } @@ -8940,9 +8640,8 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, assert(!(is_scalar && is_q)); - tcg_rmode = tcg_const_i32(arm_rmode_to_sf(FPROUNDING_ZERO)); tcg_fpstatus = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); + tcg_rmode = gen_set_rmode(FPROUNDING_ZERO, tcg_fpstatus); fracbits = (16 << size) - immhb; tcg_shift = tcg_constant_i32(fracbits); @@ -8959,7 +8658,6 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, gen_helper_vfp_tosqd(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); } write_vec_element(s, tcg_op, rd, pass, MO_64); - tcg_temp_free_i64(tcg_op); } clear_vec_high(s, is_q, rd); } else { @@ -8995,16 +8693,13 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, } else { write_vec_element_i32(s, tcg_op, rd, pass, size); } - tcg_temp_free_i32(tcg_op); } if (!is_scalar) { clear_vec_high(s, is_q, rd); } } - gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); - tcg_temp_free_ptr(tcg_fpstatus); - tcg_temp_free_i32(tcg_rmode); + gen_restore_rmode(tcg_rmode, tcg_fpstatus); } /* AdvSIMD scalar shift by immediate @@ -9129,7 +8824,7 @@ static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn) read_vec_element(s, tcg_op2, rm, 0, MO_32 | MO_SIGN); tcg_gen_mul_i64(tcg_res, tcg_op1, tcg_op2); - gen_helper_neon_addl_saturate_s64(tcg_res, cpu_env, tcg_res, tcg_res); + gen_helper_neon_addl_saturate_s64(tcg_res, tcg_env, tcg_res, tcg_res); switch (opcode) { case 0xd: /* SQDMULL, SQDMULL2 */ @@ -9139,7 +8834,7 @@ static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn) /* fall through */ case 0x9: /* SQDMLAL, SQDMLAL2 */ read_vec_element(s, tcg_op1, rd, 0, MO_64); - gen_helper_neon_addl_saturate_s64(tcg_res, cpu_env, + gen_helper_neon_addl_saturate_s64(tcg_res, tcg_env, tcg_res, tcg_op1); break; default: @@ -9147,17 +8842,13 @@ static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn) } write_fp_dreg(s, rd, tcg_res); - - tcg_temp_free_i64(tcg_op1); - tcg_temp_free_i64(tcg_op2); - tcg_temp_free_i64(tcg_res); } else { TCGv_i32 tcg_op1 = read_fp_hreg(s, rn); TCGv_i32 tcg_op2 = read_fp_hreg(s, rm); TCGv_i64 tcg_res = tcg_temp_new_i64(); gen_helper_neon_mull_s16(tcg_res, tcg_op1, tcg_op2); - gen_helper_neon_addl_saturate_s32(tcg_res, cpu_env, tcg_res, tcg_res); + gen_helper_neon_addl_saturate_s32(tcg_res, tcg_env, tcg_res, tcg_res); switch (opcode) { case 0xd: /* SQDMULL, SQDMULL2 */ @@ -9169,9 +8860,8 @@ static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn) { TCGv_i64 tcg_op3 = tcg_temp_new_i64(); read_vec_element(s, tcg_op3, rd, 0, MO_32); - gen_helper_neon_addl_saturate_s32(tcg_res, cpu_env, + gen_helper_neon_addl_saturate_s32(tcg_res, tcg_env, tcg_res, tcg_op3); - tcg_temp_free_i64(tcg_op3); break; } default: @@ -9180,10 +8870,6 @@ static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn) tcg_gen_ext32u_i64(tcg_res, tcg_res); write_fp_dreg(s, rd, tcg_res); - - tcg_temp_free_i32(tcg_op1); - tcg_temp_free_i32(tcg_op2); - tcg_temp_free_i64(tcg_res); } } @@ -9200,26 +8886,23 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, switch (opcode) { case 0x1: /* SQADD */ if (u) { - gen_helper_neon_qadd_u64(tcg_rd, cpu_env, tcg_rn, tcg_rm); + gen_helper_neon_qadd_u64(tcg_rd, tcg_env, tcg_rn, tcg_rm); } else { - gen_helper_neon_qadd_s64(tcg_rd, cpu_env, tcg_rn, tcg_rm); + gen_helper_neon_qadd_s64(tcg_rd, tcg_env, tcg_rn, tcg_rm); } break; case 0x5: /* SQSUB */ if (u) { - gen_helper_neon_qsub_u64(tcg_rd, cpu_env, tcg_rn, tcg_rm); + gen_helper_neon_qsub_u64(tcg_rd, tcg_env, tcg_rn, tcg_rm); } else { - gen_helper_neon_qsub_s64(tcg_rd, cpu_env, tcg_rn, tcg_rm); + gen_helper_neon_qsub_s64(tcg_rd, tcg_env, tcg_rn, tcg_rm); } break; case 0x6: /* CMGT, CMHI */ - /* 64 bit integer comparison, result = test ? (2^64 - 1) : 0. - * We implement this using setcond (test) and then negating. - */ cond = u ? TCG_COND_GTU : TCG_COND_GT; do_cmop: - tcg_gen_setcond_i64(cond, tcg_rd, tcg_rn, tcg_rm); - tcg_gen_neg_i64(tcg_rd, tcg_rd); + /* 64 bit integer comparison, result = test ? -1 : 0. */ + tcg_gen_negsetcond_i64(cond, tcg_rd, tcg_rn, tcg_rm); break; case 0x7: /* CMGE, CMHS */ cond = u ? TCG_COND_GEU : TCG_COND_GE; @@ -9240,9 +8923,9 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, break; case 0x9: /* SQSHL, UQSHL */ if (u) { - gen_helper_neon_qshl_u64(tcg_rd, cpu_env, tcg_rn, tcg_rm); + gen_helper_neon_qshl_u64(tcg_rd, tcg_env, tcg_rn, tcg_rm); } else { - gen_helper_neon_qshl_s64(tcg_rd, cpu_env, tcg_rn, tcg_rm); + gen_helper_neon_qshl_s64(tcg_rd, tcg_env, tcg_rn, tcg_rm); } break; case 0xa: /* SRSHL, URSHL */ @@ -9254,9 +8937,9 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, break; case 0xb: /* SQRSHL, UQRSHL */ if (u) { - gen_helper_neon_qrshl_u64(tcg_rd, cpu_env, tcg_rn, tcg_rm); + gen_helper_neon_qrshl_u64(tcg_rd, tcg_env, tcg_rn, tcg_rm); } else { - gen_helper_neon_qrshl_s64(tcg_rd, cpu_env, tcg_rn, tcg_rm); + gen_helper_neon_qrshl_s64(tcg_rd, tcg_env, tcg_rn, tcg_rm); } break; case 0x10: /* ADD, SUB */ @@ -9358,10 +9041,6 @@ static void handle_3same_float(DisasContext *s, int size, int elements, } write_vec_element(s, tcg_res, rd, pass, MO_64); - - tcg_temp_free_i64(tcg_res); - tcg_temp_free_i64(tcg_op1); - tcg_temp_free_i64(tcg_op2); } else { /* Single */ TCGv_i32 tcg_op1 = tcg_temp_new_i32(); @@ -9443,19 +9122,12 @@ static void handle_3same_float(DisasContext *s, int size, int elements, tcg_gen_extu_i32_i64(tcg_tmp, tcg_res); write_vec_element(s, tcg_tmp, rd, pass, MO_64); - tcg_temp_free_i64(tcg_tmp); } else { write_vec_element_i32(s, tcg_res, rd, pass, MO_32); } - - tcg_temp_free_i32(tcg_res); - tcg_temp_free_i32(tcg_op1); - tcg_temp_free_i32(tcg_op2); } } - tcg_temp_free_ptr(fpst); - clear_vec_high(s, elements * (size ? 8 : 4) > 8, rd); } @@ -9541,8 +9213,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) TCGv_i64 tcg_rm = read_fp_dreg(s, rm); handle_3same_64(s, opcode, u, tcg_rd, tcg_rn, tcg_rm); - tcg_temp_free_i64(tcg_rn); - tcg_temp_free_i64(tcg_rm); } else { /* Do a single operation on the lowest element in the vector. * We use the standard Neon helpers and rely on 0 OP 0 == 0 with @@ -9613,16 +9283,11 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) g_assert_not_reached(); } - genenvfn(tcg_rd32, cpu_env, tcg_rn, tcg_rm); + genenvfn(tcg_rd32, tcg_env, tcg_rn, tcg_rm); tcg_gen_extu_i32_i64(tcg_rd, tcg_rd32); - tcg_temp_free_i32(tcg_rd32); - tcg_temp_free_i32(tcg_rn); - tcg_temp_free_i32(tcg_rm); } write_fp_dreg(s, rd, tcg_rd); - - tcg_temp_free_i64(tcg_rd); } /* AdvSIMD scalar three same FP16 @@ -9712,12 +9377,6 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, } write_fp_sreg(s, rd, tcg_res); - - - tcg_temp_free_i32(tcg_res); - tcg_temp_free_i32(tcg_op1); - tcg_temp_free_i32(tcg_op2); - tcg_temp_free_ptr(fpst); } /* AdvSIMD scalar three same extra @@ -9777,30 +9436,25 @@ static void disas_simd_scalar_three_reg_same_extra(DisasContext *s, switch (opcode) { case 0x0: /* SQRDMLAH */ if (size == 1) { - gen_helper_neon_qrdmlah_s16(ele3, cpu_env, ele1, ele2, ele3); + gen_helper_neon_qrdmlah_s16(ele3, tcg_env, ele1, ele2, ele3); } else { - gen_helper_neon_qrdmlah_s32(ele3, cpu_env, ele1, ele2, ele3); + gen_helper_neon_qrdmlah_s32(ele3, tcg_env, ele1, ele2, ele3); } break; case 0x1: /* SQRDMLSH */ if (size == 1) { - gen_helper_neon_qrdmlsh_s16(ele3, cpu_env, ele1, ele2, ele3); + gen_helper_neon_qrdmlsh_s16(ele3, tcg_env, ele1, ele2, ele3); } else { - gen_helper_neon_qrdmlsh_s32(ele3, cpu_env, ele1, ele2, ele3); + gen_helper_neon_qrdmlsh_s32(ele3, tcg_env, ele1, ele2, ele3); } break; default: g_assert_not_reached(); } - tcg_temp_free_i32(ele1); - tcg_temp_free_i32(ele2); res = tcg_temp_new_i64(); tcg_gen_extu_i32_i64(res, ele3); - tcg_temp_free_i32(ele3); - write_fp_dreg(s, rd, res); - tcg_temp_free_i64(res); } static void handle_2misc_64(DisasContext *s, int opcode, bool u, @@ -9831,20 +9485,16 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, break; case 0x7: /* SQABS, SQNEG */ if (u) { - gen_helper_neon_qneg_s64(tcg_rd, cpu_env, tcg_rn); + gen_helper_neon_qneg_s64(tcg_rd, tcg_env, tcg_rn); } else { - gen_helper_neon_qabs_s64(tcg_rd, cpu_env, tcg_rn); + gen_helper_neon_qabs_s64(tcg_rd, tcg_env, tcg_rn); } break; case 0xa: /* CMLT */ - /* 64 bit integer comparison against zero, result is - * test ? (2^64 - 1) : 0. We implement via setcond(!test) and - * subtracting 1. - */ cond = TCG_COND_LT; do_cmop: - tcg_gen_setcondi_i64(cond, tcg_rd, tcg_rn, 0); - tcg_gen_neg_i64(tcg_rd, tcg_rd); + /* 64 bit integer comparison against zero, result is test ? -1 : 0. */ + tcg_gen_negsetcond_i64(cond, tcg_rd, tcg_rn, tcg_constant_i64(0)); break; case 0x8: /* CMGT, CMGE */ cond = u ? TCG_COND_GE : TCG_COND_GT; @@ -9866,7 +9516,7 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, gen_helper_vfp_negd(tcg_rd, tcg_rn); break; case 0x7f: /* FSQRT */ - gen_helper_vfp_sqrtd(tcg_rd, tcg_rn, cpu_env); + gen_helper_vfp_sqrtd(tcg_rd, tcg_rn, tcg_env); break; case 0x1a: /* FCVTNS */ case 0x1b: /* FCVTMS */ @@ -9956,8 +9606,6 @@ static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, } write_vec_element(s, tcg_res, rd, pass, MO_64); } - tcg_temp_free_i64(tcg_res); - tcg_temp_free_i64(tcg_op); clear_vec_high(s, !is_scalar, rd); } else { @@ -10030,14 +9678,11 @@ static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, write_vec_element_i32(s, tcg_res, rd, pass, size); } } - tcg_temp_free_i32(tcg_res); - tcg_temp_free_i32(tcg_op); + if (!is_scalar) { clear_vec_high(s, is_q, rd); } } - - tcg_temp_free_ptr(fpst); } static void handle_2misc_reciprocal(DisasContext *s, int opcode, @@ -10069,8 +9714,6 @@ static void handle_2misc_reciprocal(DisasContext *s, int opcode, } write_vec_element(s, tcg_res, rd, pass, MO_64); } - tcg_temp_free_i64(tcg_res); - tcg_temp_free_i64(tcg_op); clear_vec_high(s, !is_scalar, rd); } else { TCGv_i32 tcg_op = tcg_temp_new_i32(); @@ -10109,13 +9752,10 @@ static void handle_2misc_reciprocal(DisasContext *s, int opcode, write_vec_element_i32(s, tcg_res, rd, pass, MO_32); } } - tcg_temp_free_i32(tcg_res); - tcg_temp_free_i32(tcg_op); if (!is_scalar) { clear_vec_high(s, is_q, rd); } } - tcg_temp_free_ptr(fpst); } static void handle_2misc_narrow(DisasContext *s, bool scalar, @@ -10182,7 +9822,7 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, case 0x16: /* FCVTN, FCVTN2 */ /* 32 bit to 16 bit or 64 bit to 32 bit float conversion */ if (size == 2) { - gen_helper_vfp_fcvtsd(tcg_res[pass], tcg_op, cpu_env); + gen_helper_vfp_fcvtsd(tcg_res[pass], tcg_op, tcg_env); } else { TCGv_i32 tcg_lo = tcg_temp_new_i32(); TCGv_i32 tcg_hi = tcg_temp_new_i32(); @@ -10193,17 +9833,12 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, gen_helper_vfp_fcvt_f32_to_f16(tcg_lo, tcg_lo, fpst, ahp); gen_helper_vfp_fcvt_f32_to_f16(tcg_hi, tcg_hi, fpst, ahp); tcg_gen_deposit_i32(tcg_res[pass], tcg_lo, tcg_hi, 16, 16); - tcg_temp_free_i32(tcg_lo); - tcg_temp_free_i32(tcg_hi); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(ahp); } break; case 0x36: /* BFCVTN, BFCVTN2 */ { TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); gen_helper_bfcvt_pair(tcg_res[pass], tcg_op, fpst); - tcg_temp_free_ptr(fpst); } break; case 0x56: /* FCVTXN, FCVTXN2 */ @@ -10211,7 +9846,7 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, * with von Neumann rounding (round to odd) */ assert(size == 2); - gen_helper_fcvtx_f64_to_f32(tcg_res[pass], tcg_op, cpu_env); + gen_helper_fcvtx_f64_to_f32(tcg_res[pass], tcg_op, tcg_env); break; default: g_assert_not_reached(); @@ -10220,15 +9855,12 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, if (genfn) { genfn(tcg_res[pass], tcg_op); } else if (genenvfn) { - genenvfn(tcg_res[pass], cpu_env, tcg_op); + genenvfn(tcg_res[pass], tcg_env, tcg_op); } - - tcg_temp_free_i64(tcg_op); } for (pass = 0; pass < 2; pass++) { write_vec_element_i32(s, tcg_res[pass], rd, destelt + pass, MO_32); - tcg_temp_free_i32(tcg_res[pass]); } clear_vec_high(s, is_q, rd); } @@ -10249,14 +9881,12 @@ static void handle_2misc_satacc(DisasContext *s, bool is_scalar, bool is_u, read_vec_element(s, tcg_rd, rd, pass, MO_64); if (is_u) { /* USQADD */ - gen_helper_neon_uqadd_s64(tcg_rd, cpu_env, tcg_rn, tcg_rd); + gen_helper_neon_uqadd_s64(tcg_rd, tcg_env, tcg_rn, tcg_rd); } else { /* SUQADD */ - gen_helper_neon_sqadd_u64(tcg_rd, cpu_env, tcg_rn, tcg_rd); + gen_helper_neon_sqadd_u64(tcg_rd, tcg_env, tcg_rn, tcg_rd); } write_vec_element(s, tcg_rd, rd, pass, MO_64); } - tcg_temp_free_i64(tcg_rd); - tcg_temp_free_i64(tcg_rn); clear_vec_high(s, !is_scalar, rd); } else { TCGv_i32 tcg_rn = tcg_temp_new_i32(); @@ -10281,13 +9911,13 @@ static void handle_2misc_satacc(DisasContext *s, bool is_scalar, bool is_u, if (is_u) { /* USQADD */ switch (size) { case 0: - gen_helper_neon_uqadd_s8(tcg_rd, cpu_env, tcg_rn, tcg_rd); + gen_helper_neon_uqadd_s8(tcg_rd, tcg_env, tcg_rn, tcg_rd); break; case 1: - gen_helper_neon_uqadd_s16(tcg_rd, cpu_env, tcg_rn, tcg_rd); + gen_helper_neon_uqadd_s16(tcg_rd, tcg_env, tcg_rn, tcg_rd); break; case 2: - gen_helper_neon_uqadd_s32(tcg_rd, cpu_env, tcg_rn, tcg_rd); + gen_helper_neon_uqadd_s32(tcg_rd, tcg_env, tcg_rn, tcg_rd); break; default: g_assert_not_reached(); @@ -10295,13 +9925,13 @@ static void handle_2misc_satacc(DisasContext *s, bool is_scalar, bool is_u, } else { /* SUQADD */ switch (size) { case 0: - gen_helper_neon_sqadd_u8(tcg_rd, cpu_env, tcg_rn, tcg_rd); + gen_helper_neon_sqadd_u8(tcg_rd, tcg_env, tcg_rn, tcg_rd); break; case 1: - gen_helper_neon_sqadd_u16(tcg_rd, cpu_env, tcg_rn, tcg_rd); + gen_helper_neon_sqadd_u16(tcg_rd, tcg_env, tcg_rn, tcg_rd); break; case 2: - gen_helper_neon_sqadd_u32(tcg_rd, cpu_env, tcg_rn, tcg_rd); + gen_helper_neon_sqadd_u32(tcg_rd, tcg_env, tcg_rn, tcg_rd); break; default: g_assert_not_reached(); @@ -10313,8 +9943,6 @@ static void handle_2misc_satacc(DisasContext *s, bool is_scalar, bool is_u, } write_vec_element_i32(s, tcg_rd, rd, pass, MO_32); } - tcg_temp_free_i32(tcg_rd); - tcg_temp_free_i32(tcg_rn); clear_vec_high(s, is_q, rd); } } @@ -10452,12 +10080,11 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) } if (is_fcvt) { - tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode)); tcg_fpstatus = fpstatus_ptr(FPST_FPCR); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); + tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); } else { - tcg_rmode = NULL; tcg_fpstatus = NULL; + tcg_rmode = NULL; } if (size == 3) { @@ -10466,8 +10093,6 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) handle_2misc_64(s, opcode, u, tcg_rd, tcg_rn, tcg_rmode, tcg_fpstatus); write_fp_dreg(s, rd, tcg_rd); - tcg_temp_free_i64(tcg_rd); - tcg_temp_free_i64(tcg_rn); } else { TCGv_i32 tcg_rn = tcg_temp_new_i32(); TCGv_i32 tcg_rd = tcg_temp_new_i32(); @@ -10484,7 +10109,7 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) { gen_helper_neon_qabs_s32, gen_helper_neon_qneg_s32 }, }; genfn = fns[size][u]; - genfn(tcg_rd, cpu_env, tcg_rn); + genfn(tcg_rd, tcg_env, tcg_rn); break; } case 0x1a: /* FCVTNS */ @@ -10508,14 +10133,10 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) } write_fp_sreg(s, rd, tcg_rd); - tcg_temp_free_i32(tcg_rd); - tcg_temp_free_i32(tcg_rn); } if (is_fcvt) { - gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); - tcg_temp_free_i32(tcg_rmode); - tcg_temp_free_ptr(tcg_fpstatus); + gen_restore_rmode(tcg_rmode, tcg_fpstatus); } } @@ -10617,8 +10238,8 @@ static void handle_vec_simd_wshli(DisasContext *s, bool is_q, bool is_u, int dsize = 64; int esize = 8 << size; int elements = dsize/esize; - TCGv_i64 tcg_rn = new_tmp_a64(s); - TCGv_i64 tcg_rd = new_tmp_a64(s); + TCGv_i64 tcg_rn = tcg_temp_new_i64(); + TCGv_i64 tcg_rd = tcg_temp_new_i64(); int i; if (size >= 3) { @@ -10692,9 +10313,6 @@ static void handle_vec_simd_shrn(DisasContext *s, bool is_q, } else { write_vec_element(s, tcg_final, rd, 1, MO_64); } - tcg_temp_free_i64(tcg_rn); - tcg_temp_free_i64(tcg_rd); - tcg_temp_free_i64(tcg_final); clear_vec_high(s, is_q, rd); } @@ -10865,8 +10483,6 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, tcg_gen_movcond_i64(is_u ? TCG_COND_GEU : TCG_COND_GE, tcg_passres, tcg_op1, tcg_op2, tcg_tmp1, tcg_tmp2); - tcg_temp_free_i64(tcg_tmp1); - tcg_temp_free_i64(tcg_tmp2); break; } case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ @@ -10878,7 +10494,7 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, case 11: /* SQDMLSL, SQDMLSL2 */ case 13: /* SQDMULL, SQDMULL2 */ tcg_gen_mul_i64(tcg_passres, tcg_op1, tcg_op2); - gen_helper_neon_addl_saturate_s64(tcg_passres, cpu_env, + gen_helper_neon_addl_saturate_s64(tcg_passres, tcg_env, tcg_passres, tcg_passres); break; default: @@ -10890,20 +10506,13 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, if (accop < 0) { tcg_gen_neg_i64(tcg_passres, tcg_passres); } - gen_helper_neon_addl_saturate_s64(tcg_res[pass], cpu_env, + gen_helper_neon_addl_saturate_s64(tcg_res[pass], tcg_env, tcg_res[pass], tcg_passres); } else if (accop > 0) { tcg_gen_add_i64(tcg_res[pass], tcg_res[pass], tcg_passres); } else if (accop < 0) { tcg_gen_sub_i64(tcg_res[pass], tcg_res[pass], tcg_passres); } - - if (accop != 0) { - tcg_temp_free_i64(tcg_passres); - } - - tcg_temp_free_i64(tcg_op1); - tcg_temp_free_i64(tcg_op2); } } else { /* size 0 or 1, generally helper functions */ @@ -10937,7 +10546,6 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, widenfn(tcg_passres, tcg_op1); gen_neon_addl(size, (opcode == 2), tcg_passres, tcg_passres, tcg_op2_64); - tcg_temp_free_i64(tcg_op2_64); break; } case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ @@ -10978,14 +10586,12 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, case 13: /* SQDMULL, SQDMULL2 */ assert(size == 1); gen_helper_neon_mull_s16(tcg_passres, tcg_op1, tcg_op2); - gen_helper_neon_addl_saturate_s32(tcg_passres, cpu_env, + gen_helper_neon_addl_saturate_s32(tcg_passres, tcg_env, tcg_passres, tcg_passres); break; default: g_assert_not_reached(); } - tcg_temp_free_i32(tcg_op1); - tcg_temp_free_i32(tcg_op2); if (accop != 0) { if (opcode == 9 || opcode == 11) { @@ -10993,22 +10599,19 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, if (accop < 0) { gen_helper_neon_negl_u32(tcg_passres, tcg_passres); } - gen_helper_neon_addl_saturate_s32(tcg_res[pass], cpu_env, + gen_helper_neon_addl_saturate_s32(tcg_res[pass], tcg_env, tcg_res[pass], tcg_passres); } else { gen_neon_addl(size, (accop < 0), tcg_res[pass], tcg_res[pass], tcg_passres); } - tcg_temp_free_i64(tcg_passres); } } } write_vec_element(s, tcg_res[0], rd, 0, MO_64); write_vec_element(s, tcg_res[1], rd, 1, MO_64); - tcg_temp_free_i64(tcg_res[0]); - tcg_temp_free_i64(tcg_res[1]); } static void handle_3rd_wide(DisasContext *s, int is_q, int is_u, int size, @@ -11032,17 +10635,13 @@ static void handle_3rd_wide(DisasContext *s, int is_q, int is_u, int size, read_vec_element(s, tcg_op1, rn, pass, MO_64); read_vec_element_i32(s, tcg_op2, rm, part + pass, MO_32); widenfn(tcg_op2_wide, tcg_op2); - tcg_temp_free_i32(tcg_op2); tcg_res[pass] = tcg_temp_new_i64(); gen_neon_addl(size, (opcode == 3), tcg_res[pass], tcg_op1, tcg_op2_wide); - tcg_temp_free_i64(tcg_op1); - tcg_temp_free_i64(tcg_op2_wide); } for (pass = 0; pass < 2; pass++) { write_vec_element(s, tcg_res[pass], rd, pass, MO_64); - tcg_temp_free_i64(tcg_res[pass]); } } @@ -11077,17 +10676,12 @@ static void handle_3rd_narrowing(DisasContext *s, int is_q, int is_u, int size, gen_neon_addl(size, (opcode == 6), tcg_wideres, tcg_op1, tcg_op2); - tcg_temp_free_i64(tcg_op1); - tcg_temp_free_i64(tcg_op2); - tcg_res[pass] = tcg_temp_new_i32(); gennarrow(tcg_res[pass], tcg_wideres); - tcg_temp_free_i64(tcg_wideres); } for (pass = 0; pass < 2; pass++) { write_vec_element_i32(s, tcg_res[pass], rd, pass + part, MO_32); - tcg_temp_free_i32(tcg_res[pass]); } clear_vec_high(s, is_q, rd); } @@ -11314,14 +10908,10 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, default: g_assert_not_reached(); } - - tcg_temp_free_i64(tcg_op1); - tcg_temp_free_i64(tcg_op2); } for (pass = 0; pass < 2; pass++) { write_vec_element(s, tcg_res[pass], rd, pass, MO_64); - tcg_temp_free_i64(tcg_res[pass]); } } else { int maxpass = is_q ? 4 : 2; @@ -11393,21 +10983,13 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, if (genfn) { genfn(tcg_res[pass], tcg_op1, tcg_op2); } - - tcg_temp_free_i32(tcg_op1); - tcg_temp_free_i32(tcg_op2); } for (pass = 0; pass < maxpass; pass++) { write_vec_element_i32(s, tcg_res[pass], rd, pass, MO_32); - tcg_temp_free_i32(tcg_res[pass]); } clear_vec_high(s, is_q, rd); } - - if (fpst) { - tcg_temp_free_ptr(fpst); - } } /* Floating point op subgroup of C3.6.16. */ @@ -11487,7 +11069,7 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) int data = (is_2 << 1) | is_s; tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), cpu_env, + vec_full_reg_offset(s, rm), tcg_env, is_q ? 16 : 8, vec_full_reg_size(s), data, gen_helper_gvec_fmlal_a64); } @@ -11664,10 +11246,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) handle_3same_64(s, opcode, u, tcg_res, tcg_op1, tcg_op2); write_vec_element(s, tcg_res, rd, pass, MO_64); - - tcg_temp_free_i64(tcg_res); - tcg_temp_free_i64(tcg_op1); - tcg_temp_free_i64(tcg_op2); } } else { for (pass = 0; pass < (is_q ? 4 : 2); pass++) { @@ -11746,16 +11324,12 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } if (genenvfn) { - genenvfn(tcg_res, cpu_env, tcg_op1, tcg_op2); + genenvfn(tcg_res, tcg_env, tcg_op1, tcg_op2); } else { genfn(tcg_res, tcg_op1, tcg_op2); } write_vec_element_i32(s, tcg_res, rd, pass, MO_32); - - tcg_temp_free_i32(tcg_res); - tcg_temp_free_i32(tcg_op1); - tcg_temp_free_i32(tcg_op2); } } clear_vec_high(s, is_q, rd); @@ -11926,12 +11500,7 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) for (pass = 0; pass < maxpass; pass++) { write_vec_element_i32(s, tcg_res[pass], rd, pass, MO_16); - tcg_temp_free_i32(tcg_res[pass]); } - - tcg_temp_free_i32(tcg_op1); - tcg_temp_free_i32(tcg_op2); - } else { for (pass = 0; pass < elements; pass++) { TCGv_i32 tcg_op1 = tcg_temp_new_i32(); @@ -12011,14 +11580,9 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) } write_vec_element_i32(s, tcg_res, rd, pass, MO_16); - tcg_temp_free_i32(tcg_res); - tcg_temp_free_i32(tcg_op1); - tcg_temp_free_i32(tcg_op2); } } - tcg_temp_free_ptr(fpst); - clear_vec_high(s, is_q, rd); } @@ -12229,12 +11793,10 @@ static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, tcg_res[pass] = tcg_temp_new_i64(); read_vec_element_i32(s, tcg_op, rn, srcelt + pass, MO_32); - gen_helper_vfp_fcvtds(tcg_res[pass], tcg_op, cpu_env); - tcg_temp_free_i32(tcg_op); + gen_helper_vfp_fcvtds(tcg_res[pass], tcg_op, tcg_env); } for (pass = 0; pass < 2; pass++) { write_vec_element(s, tcg_res[pass], rd, pass, MO_64); - tcg_temp_free_i64(tcg_res[pass]); } } else { /* 16 -> 32 bit fp conversion */ @@ -12252,11 +11814,7 @@ static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, } for (pass = 0; pass < 4; pass++) { write_vec_element_i32(s, tcg_res[pass], rd, pass, MO_32); - tcg_temp_free_i32(tcg_res[pass]); } - - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(ahp); } } @@ -12300,7 +11858,6 @@ static void handle_rev(DisasContext *s, int opcode, bool u, g_assert_not_reached(); } write_vec_element(s, tcg_tmp, rd, i, grp_size); - tcg_temp_free_i64(tcg_tmp); } clear_vec_high(s, is_q, rd); } else { @@ -12308,26 +11865,26 @@ static void handle_rev(DisasContext *s, int opcode, bool u, int esize = 8 << size; int elements = dsize / esize; TCGv_i64 tcg_rn = tcg_temp_new_i64(); - TCGv_i64 tcg_rd = tcg_const_i64(0); - TCGv_i64 tcg_rd_hi = tcg_const_i64(0); + TCGv_i64 tcg_rd[2]; + + for (i = 0; i < 2; i++) { + tcg_rd[i] = tcg_temp_new_i64(); + tcg_gen_movi_i64(tcg_rd[i], 0); + } for (i = 0; i < elements; i++) { int e_rev = (i & 0xf) ^ revmask; - int off = e_rev * esize; - read_vec_element(s, tcg_rn, rn, i, size); - if (off >= 64) { - tcg_gen_deposit_i64(tcg_rd_hi, tcg_rd_hi, - tcg_rn, off - 64, esize); - } else { - tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_rn, off, esize); - } - } - write_vec_element(s, tcg_rd, rd, 0, MO_64); - write_vec_element(s, tcg_rd_hi, rd, 1, MO_64); + int w = (e_rev * esize) / 64; + int o = (e_rev * esize) % 64; - tcg_temp_free_i64(tcg_rd_hi); - tcg_temp_free_i64(tcg_rd); - tcg_temp_free_i64(tcg_rn); + read_vec_element(s, tcg_rn, rn, i, size); + tcg_gen_deposit_i64(tcg_rd[w], tcg_rd[w], tcg_rn, o, esize); + } + + for (i = 0; i < 2; i++) { + write_vec_element(s, tcg_rd[i], rd, i, MO_64); + } + clear_vec_high(s, true, rd); } } @@ -12361,9 +11918,6 @@ static void handle_2misc_pairwise(DisasContext *s, int opcode, bool u, read_vec_element(s, tcg_op1, rd, pass, MO_64); tcg_gen_add_i64(tcg_res[pass], tcg_res[pass], tcg_op1); } - - tcg_temp_free_i64(tcg_op1); - tcg_temp_free_i64(tcg_op2); } } else { for (pass = 0; pass < maxpass; pass++) { @@ -12391,7 +11945,6 @@ static void handle_2misc_pairwise(DisasContext *s, int opcode, bool u, tcg_res[pass], tcg_op); } } - tcg_temp_free_i64(tcg_op); } } if (!is_q) { @@ -12399,7 +11952,6 @@ static void handle_2misc_pairwise(DisasContext *s, int opcode, bool u, } for (pass = 0; pass < 2; pass++) { write_vec_element(s, tcg_res[pass], rd, pass, MO_64); - tcg_temp_free_i64(tcg_res[pass]); } } @@ -12423,13 +11975,10 @@ static void handle_shll(DisasContext *s, bool is_q, int size, int rn, int rd) tcg_res[pass] = tcg_temp_new_i64(); widenfn(tcg_res[pass], tcg_op); tcg_gen_shli_i64(tcg_res[pass], tcg_res[pass], 8 << size); - - tcg_temp_free_i32(tcg_op); } for (pass = 0; pass < 2; pass++) { write_vec_element(s, tcg_res[pass], rd, pass, MO_64); - tcg_temp_free_i64(tcg_res[pass]); } } @@ -12448,7 +11997,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); bool need_fpstatus = false; - bool need_rmode = false; int rmode = -1; TCGv_i32 tcg_rmode; TCGv_ptr tcg_fpstatus; @@ -12598,7 +12146,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x7a: /* FCVTPU */ case 0x7b: /* FCVTZU */ need_fpstatus = true; - need_rmode = true; rmode = extract32(opcode, 5, 1) | (extract32(opcode, 0, 1) << 1); if (size == 3 && !is_q) { unallocated_encoding(s); @@ -12608,7 +12155,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x5c: /* FCVTAU */ case 0x1c: /* FCVTAS */ need_fpstatus = true; - need_rmode = true; rmode = FPROUNDING_TIEAWAY; if (size == 3 && !is_q) { unallocated_encoding(s); @@ -12667,7 +12213,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x19: /* FRINTM */ case 0x38: /* FRINTP */ case 0x39: /* FRINTZ */ - need_rmode = true; rmode = extract32(opcode, 5, 1) | (extract32(opcode, 0, 1) << 1); /* fall through */ case 0x59: /* FRINTX */ @@ -12679,7 +12224,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } break; case 0x58: /* FRINTA */ - need_rmode = true; rmode = FPROUNDING_TIEAWAY; need_fpstatus = true; if (size == 3 && !is_q) { @@ -12695,7 +12239,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) break; case 0x1e: /* FRINT32Z */ case 0x1f: /* FRINT64Z */ - need_rmode = true; rmode = FPROUNDING_ZERO; /* fall through */ case 0x5e: /* FRINT32X */ @@ -12721,14 +12264,13 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) return; } - if (need_fpstatus || need_rmode) { + if (need_fpstatus || rmode >= 0) { tcg_fpstatus = fpstatus_ptr(FPST_FPCR); } else { tcg_fpstatus = NULL; } - if (need_rmode) { - tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode)); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); + if (rmode >= 0) { + tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); } else { tcg_rmode = NULL; } @@ -12784,9 +12326,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) tcg_rmode, tcg_fpstatus); write_vec_element(s, tcg_res, rd, pass, MO_64); - - tcg_temp_free_i64(tcg_res); - tcg_temp_free_i64(tcg_op); } } else { int pass; @@ -12809,9 +12348,9 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) break; case 0x7: /* SQABS, SQNEG */ if (u) { - gen_helper_neon_qneg_s32(tcg_res, cpu_env, tcg_op); + gen_helper_neon_qneg_s32(tcg_res, tcg_env, tcg_op); } else { - gen_helper_neon_qabs_s32(tcg_res, cpu_env, tcg_op); + gen_helper_neon_qabs_s32(tcg_res, tcg_env, tcg_op); } break; case 0x2f: /* FABS */ @@ -12821,7 +12360,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) gen_helper_vfp_negs(tcg_res, tcg_op); break; case 0x7f: /* FSQRT */ - gen_helper_vfp_sqrts(tcg_res, tcg_op, cpu_env); + gen_helper_vfp_sqrts(tcg_res, tcg_op, tcg_env); break; case 0x1a: /* FCVTNS */ case 0x1b: /* FCVTMS */ @@ -12885,7 +12424,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) { gen_helper_neon_qabs_s16, gen_helper_neon_qneg_s16 }, }; genfn = fns[size][u]; - genfn(tcg_res, cpu_env, tcg_op); + genfn(tcg_res, tcg_env, tcg_op); break; } case 0x4: /* CLS, CLZ */ @@ -12909,19 +12448,12 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } write_vec_element_i32(s, tcg_res, rd, pass, MO_32); - - tcg_temp_free_i32(tcg_res); - tcg_temp_free_i32(tcg_op); } } clear_vec_high(s, is_q, rd); - if (need_rmode) { - gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); - tcg_temp_free_i32(tcg_rmode); - } - if (need_fpstatus) { - tcg_temp_free_ptr(tcg_fpstatus); + if (tcg_rmode) { + gen_restore_rmode(tcg_rmode, tcg_fpstatus); } } @@ -12950,9 +12482,8 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) int pass; TCGv_i32 tcg_rmode = NULL; TCGv_ptr tcg_fpstatus = NULL; - bool need_rmode = false; bool need_fpst = true; - int rmode; + int rmode = -1; if (!dc_isar_feature(aa64_fp16, s)) { unallocated_encoding(s); @@ -13001,27 +12532,22 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) case 0x3f: /* FRECPX */ break; case 0x18: /* FRINTN */ - need_rmode = true; only_in_vector = true; rmode = FPROUNDING_TIEEVEN; break; case 0x19: /* FRINTM */ - need_rmode = true; only_in_vector = true; rmode = FPROUNDING_NEGINF; break; case 0x38: /* FRINTP */ - need_rmode = true; only_in_vector = true; rmode = FPROUNDING_POSINF; break; case 0x39: /* FRINTZ */ - need_rmode = true; only_in_vector = true; rmode = FPROUNDING_ZERO; break; case 0x58: /* FRINTA */ - need_rmode = true; only_in_vector = true; rmode = FPROUNDING_TIEAWAY; break; @@ -13031,43 +12557,33 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) /* current rounding mode */ break; case 0x1a: /* FCVTNS */ - need_rmode = true; rmode = FPROUNDING_TIEEVEN; break; case 0x1b: /* FCVTMS */ - need_rmode = true; rmode = FPROUNDING_NEGINF; break; case 0x1c: /* FCVTAS */ - need_rmode = true; rmode = FPROUNDING_TIEAWAY; break; case 0x3a: /* FCVTPS */ - need_rmode = true; rmode = FPROUNDING_POSINF; break; case 0x3b: /* FCVTZS */ - need_rmode = true; rmode = FPROUNDING_ZERO; break; case 0x5a: /* FCVTNU */ - need_rmode = true; rmode = FPROUNDING_TIEEVEN; break; case 0x5b: /* FCVTMU */ - need_rmode = true; rmode = FPROUNDING_NEGINF; break; case 0x5c: /* FCVTAU */ - need_rmode = true; rmode = FPROUNDING_TIEAWAY; break; case 0x7a: /* FCVTPU */ - need_rmode = true; rmode = FPROUNDING_POSINF; break; case 0x7b: /* FCVTZU */ - need_rmode = true; rmode = FPROUNDING_ZERO; break; case 0x2f: /* FABS */ @@ -13100,13 +12616,12 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) return; } - if (need_rmode || need_fpst) { + if (rmode >= 0 || need_fpst) { tcg_fpstatus = fpstatus_ptr(FPST_FPCR_F16); } - if (need_rmode) { - tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode)); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); + if (rmode >= 0) { + tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); } if (is_scalar) { @@ -13147,9 +12662,6 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) /* limit any sign extension going on */ tcg_gen_andi_i32(tcg_res, tcg_res, 0xffff); write_fp_sreg(s, rd, tcg_res); - - tcg_temp_free_i32(tcg_res); - tcg_temp_free_i32(tcg_op); } else { for (pass = 0; pass < (is_q ? 8 : 4); pass++) { TCGv_i32 tcg_op = tcg_temp_new_i32(); @@ -13203,21 +12715,13 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) } write_vec_element_i32(s, tcg_res, rd, pass, MO_16); - - tcg_temp_free_i32(tcg_res); - tcg_temp_free_i32(tcg_op); } clear_vec_high(s, is_q, rd); } if (tcg_rmode) { - gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); - tcg_temp_free_i32(tcg_rmode); - } - - if (tcg_fpstatus) { - tcg_temp_free_ptr(tcg_fpstatus); + gen_restore_rmode(tcg_rmode, tcg_fpstatus); } } @@ -13357,7 +12861,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) return; } size = MO_16; - /* is_fp, but we pass cpu_env not fp_status. */ + /* is_fp, but we pass tcg_env not fp_status. */ break; default: unallocated_encoding(s); @@ -13487,7 +12991,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) size == MO_64 ? gen_helper_gvec_fcmlas_idx : gen_helper_gvec_fcmlah_idx); - tcg_temp_free_ptr(fpst); } return; @@ -13501,7 +13004,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) int data = (index << 2) | (is_2 << 1) | is_s; tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), cpu_env, + vec_full_reg_offset(s, rm), tcg_env, is_q ? 16 : 8, vec_full_reg_size(s), data, gen_helper_gvec_fmlal_idx_a64); } @@ -13592,11 +13095,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } write_vec_element(s, tcg_res, rd, pass, MO_64); - tcg_temp_free_i64(tcg_op); - tcg_temp_free_i64(tcg_res); } - tcg_temp_free_i64(tcg_idx); clear_vec_high(s, !is_scalar, rd); } else if (!is_long) { /* 32 bit floating point, or 16 or 32 bit integer. @@ -13723,19 +13223,19 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) break; case 0x0c: /* SQDMULH */ if (size == 1) { - gen_helper_neon_qdmulh_s16(tcg_res, cpu_env, + gen_helper_neon_qdmulh_s16(tcg_res, tcg_env, tcg_op, tcg_idx); } else { - gen_helper_neon_qdmulh_s32(tcg_res, cpu_env, + gen_helper_neon_qdmulh_s32(tcg_res, tcg_env, tcg_op, tcg_idx); } break; case 0x0d: /* SQRDMULH */ if (size == 1) { - gen_helper_neon_qrdmulh_s16(tcg_res, cpu_env, + gen_helper_neon_qrdmulh_s16(tcg_res, tcg_env, tcg_op, tcg_idx); } else { - gen_helper_neon_qrdmulh_s32(tcg_res, cpu_env, + gen_helper_neon_qrdmulh_s32(tcg_res, tcg_env, tcg_op, tcg_idx); } break; @@ -13743,10 +13243,10 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) read_vec_element_i32(s, tcg_res, rd, pass, is_scalar ? size : MO_32); if (size == 1) { - gen_helper_neon_qrdmlah_s16(tcg_res, cpu_env, + gen_helper_neon_qrdmlah_s16(tcg_res, tcg_env, tcg_op, tcg_idx, tcg_res); } else { - gen_helper_neon_qrdmlah_s32(tcg_res, cpu_env, + gen_helper_neon_qrdmlah_s32(tcg_res, tcg_env, tcg_op, tcg_idx, tcg_res); } break; @@ -13754,10 +13254,10 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) read_vec_element_i32(s, tcg_res, rd, pass, is_scalar ? size : MO_32); if (size == 1) { - gen_helper_neon_qrdmlsh_s16(tcg_res, cpu_env, + gen_helper_neon_qrdmlsh_s16(tcg_res, tcg_env, tcg_op, tcg_idx, tcg_res); } else { - gen_helper_neon_qrdmlsh_s32(tcg_res, cpu_env, + gen_helper_neon_qrdmlsh_s32(tcg_res, tcg_env, tcg_op, tcg_idx, tcg_res); } break; @@ -13770,12 +13270,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } else { write_vec_element_i32(s, tcg_res, rd, pass, MO_32); } - - tcg_temp_free_i32(tcg_op); - tcg_temp_free_i32(tcg_res); } - tcg_temp_free_i32(tcg_idx); clear_vec_high(s, is_q, rd); } else { /* long ops: 16x16->32 or 32x32->64 */ @@ -13816,11 +13312,10 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } tcg_gen_mul_i64(tcg_passres, tcg_op, tcg_idx); - tcg_temp_free_i64(tcg_op); if (satop) { /* saturating, doubling */ - gen_helper_neon_addl_saturate_s64(tcg_passres, cpu_env, + gen_helper_neon_addl_saturate_s64(tcg_passres, tcg_env, tcg_passres, tcg_passres); } @@ -13842,16 +13337,14 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) tcg_gen_neg_i64(tcg_passres, tcg_passres); /* fall through */ case 0x3: /* SQDMLAL, SQDMLAL2 */ - gen_helper_neon_addl_saturate_s64(tcg_res[pass], cpu_env, + gen_helper_neon_addl_saturate_s64(tcg_res[pass], tcg_env, tcg_res[pass], tcg_passres); break; default: g_assert_not_reached(); } - tcg_temp_free_i64(tcg_passres); } - tcg_temp_free_i64(tcg_idx); clear_vec_high(s, !is_scalar, rd); } else { @@ -13894,10 +13387,9 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) gen_helper_neon_mull_u16(tcg_passres, tcg_op, tcg_idx); } if (satop) { - gen_helper_neon_addl_saturate_s32(tcg_passres, cpu_env, + gen_helper_neon_addl_saturate_s32(tcg_passres, tcg_env, tcg_passres, tcg_passres); } - tcg_temp_free_i32(tcg_op); if (opcode == 0xa || opcode == 0xb) { continue; @@ -13919,16 +13411,14 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) gen_helper_neon_negl_u32(tcg_passres, tcg_passres); /* fall through */ case 0x3: /* SQDMLAL, SQDMLAL2 */ - gen_helper_neon_addl_saturate_s32(tcg_res[pass], cpu_env, + gen_helper_neon_addl_saturate_s32(tcg_res[pass], tcg_env, tcg_res[pass], tcg_passres); break; default: g_assert_not_reached(); } - tcg_temp_free_i64(tcg_passres); } - tcg_temp_free_i32(tcg_idx); if (is_scalar) { tcg_gen_ext32u_i64(tcg_res[0], tcg_res[0]); @@ -13941,13 +13431,8 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) for (pass = 0; pass < 2; pass++) { write_vec_element(s, tcg_res[pass], rd, pass, MO_64); - tcg_temp_free_i64(tcg_res[pass]); } } - - if (fpst) { - tcg_temp_free_ptr(fpst); - } } /* Crypto AES @@ -13962,7 +13447,6 @@ static void disas_crypto_aes(DisasContext *s, uint32_t insn) int opcode = extract32(insn, 12, 5); int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); - int decrypt; gen_helper_gvec_2 *genfn2 = NULL; gen_helper_gvec_3 *genfn3 = NULL; @@ -13973,20 +13457,16 @@ static void disas_crypto_aes(DisasContext *s, uint32_t insn) switch (opcode) { case 0x4: /* AESE */ - decrypt = 0; genfn3 = gen_helper_crypto_aese; break; case 0x6: /* AESMC */ - decrypt = 0; genfn2 = gen_helper_crypto_aesmc; break; case 0x5: /* AESD */ - decrypt = 1; - genfn3 = gen_helper_crypto_aese; + genfn3 = gen_helper_crypto_aesd; break; case 0x7: /* AESIMC */ - decrypt = 1; - genfn2 = gen_helper_crypto_aesmc; + genfn2 = gen_helper_crypto_aesimc; break; default: unallocated_encoding(s); @@ -13997,9 +13477,9 @@ static void disas_crypto_aes(DisasContext *s, uint32_t insn) return; } if (genfn2) { - gen_gvec_op2_ool(s, true, rd, rn, decrypt, genfn2); + gen_gvec_op2_ool(s, true, rd, rn, 0, genfn2); } else { - gen_gvec_op3_ool(s, true, rd, rd, rn, decrypt, genfn3); + gen_gvec_op3_ool(s, true, rd, rd, rn, 0, genfn3); } } @@ -14327,12 +13807,6 @@ static void disas_crypto_four_reg(DisasContext *s, uint32_t insn) } write_vec_element(s, tcg_res[0], rd, 0, MO_64); write_vec_element(s, tcg_res[1], rd, 1, MO_64); - - tcg_temp_free_i64(tcg_op1); - tcg_temp_free_i64(tcg_op2); - tcg_temp_free_i64(tcg_op3); - tcg_temp_free_i64(tcg_res[0]); - tcg_temp_free_i64(tcg_res[1]); } else { TCGv_i32 tcg_op1, tcg_op2, tcg_op3, tcg_res, tcg_zero; @@ -14355,11 +13829,6 @@ static void disas_crypto_four_reg(DisasContext *s, uint32_t insn) write_vec_element_i32(s, tcg_zero, rd, 1, MO_32); write_vec_element_i32(s, tcg_zero, rd, 2, MO_32); write_vec_element_i32(s, tcg_res, rd, 3, MO_32); - - tcg_temp_free_i32(tcg_op1); - tcg_temp_free_i32(tcg_op2); - tcg_temp_free_i32(tcg_op3); - tcg_temp_free_i32(tcg_res); } } @@ -14488,6 +13957,17 @@ static void disas_data_proc_simd_fp(DisasContext *s, uint32_t insn) } } +static bool trans_OK(DisasContext *s, arg_OK *a) +{ + return true; +} + +static bool trans_FAIL(DisasContext *s, arg_OK *a) +{ + s->is_nonstreaming = true; + return true; +} + /** * is_guarded_page: * @env: The cpu environment @@ -14501,22 +13981,21 @@ static bool is_guarded_page(CPUARMState *env, DisasContext *s) #ifdef CONFIG_USER_ONLY return page_get_flags(addr) & PAGE_BTI; #else + CPUTLBEntryFull *full; + void *host; int mmu_idx = arm_to_core_mmu_idx(s->mmu_idx); - unsigned int index = tlb_index(env, mmu_idx, addr); - CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); + int flags; /* * We test this immediately after reading an insn, which means - * that any normal page must be in the TLB. The only exception - * would be for executing from flash or device memory, which - * does not retain the TLB entry. - * - * FIXME: Assume false for those, for now. We could use - * arm_cpu_get_phys_page_attrs_debug to re-read the page - * table entry even for that case. + * that the TLB entry must be present and valid, and thus this + * access will never raise an exception. */ - return (tlb_hit(entry->addr_code, addr) && - arm_tlb_bti_gp(&env_tlb(env)->d[mmu_idx].iotlb[index].attrs)); + flags = probe_access_full(env, addr, 0, MMU_INST_FETCH, mmu_idx, + false, &host, &full, 0); + assert(!(flags & TLB_INVALID_MASK)); + + return full->extra.arm.guarded; #endif } @@ -14572,24 +14051,37 @@ static bool btype_destination_ok(uint32_t insn, bool bt, int btype) return false; } +/* C3.1 A64 instruction index by encoding */ +static void disas_a64_legacy(DisasContext *s, uint32_t insn) +{ + switch (extract32(insn, 25, 4)) { + case 0x5: + case 0xd: /* Data processing - register */ + disas_data_proc_reg(s, insn); + break; + case 0x7: + case 0xf: /* Data processing - SIMD and floating point */ + disas_data_proc_simd_fp(s, insn); + break; + default: + unallocated_encoding(s); + break; + } +} + static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUARMState *env = cpu->env_ptr; + CPUARMState *env = cpu_env(cpu); ARMCPU *arm_cpu = env_archcpu(env); CPUARMTBFlags tb_flags = arm_tbflags_from_tb(dc->base.tb); int bound, core_mmu_idx; dc->isar = &arm_cpu->isar; dc->condjmp = 0; - + dc->pc_save = dc->base.pc_first; dc->aarch64 = true; - /* If we are coming from secure EL0 in a system with a 32-bit EL3, then - * there is no secure EL1, so we route exceptions to EL3. - */ - dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) && - !arm_el_is_aa64(env, 3); dc->thumb = false; dc->sctlr_b = 0; dc->be_data = EX_TBFLAG_ANY(tb_flags, BE_DATA) ? MO_BE : MO_LE; @@ -14607,26 +14099,44 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->fp_excp_el = EX_TBFLAG_ANY(tb_flags, FPEXC_EL); dc->align_mem = EX_TBFLAG_ANY(tb_flags, ALIGN_MEM); dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL); + dc->fgt_active = EX_TBFLAG_ANY(tb_flags, FGT_ACTIVE); + dc->fgt_svc = EX_TBFLAG_ANY(tb_flags, FGT_SVC); + dc->trap_eret = EX_TBFLAG_A64(tb_flags, TRAP_ERET); dc->sve_excp_el = EX_TBFLAG_A64(tb_flags, SVEEXC_EL); + dc->sme_excp_el = EX_TBFLAG_A64(tb_flags, SMEEXC_EL); dc->vl = (EX_TBFLAG_A64(tb_flags, VL) + 1) * 16; + dc->svl = (EX_TBFLAG_A64(tb_flags, SVL) + 1) * 16; dc->pauth_active = EX_TBFLAG_A64(tb_flags, PAUTH_ACTIVE); dc->bt = EX_TBFLAG_A64(tb_flags, BT); dc->btype = EX_TBFLAG_A64(tb_flags, BTYPE); dc->unpriv = EX_TBFLAG_A64(tb_flags, UNPRIV); - dc->ata = EX_TBFLAG_A64(tb_flags, ATA); + dc->ata[0] = EX_TBFLAG_A64(tb_flags, ATA); + dc->ata[1] = EX_TBFLAG_A64(tb_flags, ATA0); dc->mte_active[0] = EX_TBFLAG_A64(tb_flags, MTE_ACTIVE); dc->mte_active[1] = EX_TBFLAG_A64(tb_flags, MTE0_ACTIVE); + dc->pstate_sm = EX_TBFLAG_A64(tb_flags, PSTATE_SM); + dc->pstate_za = EX_TBFLAG_A64(tb_flags, PSTATE_ZA); + dc->sme_trap_nonstreaming = EX_TBFLAG_A64(tb_flags, SME_TRAP_NONSTREAMING); + dc->naa = EX_TBFLAG_A64(tb_flags, NAA); + dc->nv = EX_TBFLAG_A64(tb_flags, NV); + dc->nv1 = EX_TBFLAG_A64(tb_flags, NV1); + dc->nv2 = EX_TBFLAG_A64(tb_flags, NV2); + dc->nv2_mem_e20 = EX_TBFLAG_A64(tb_flags, NV2_MEM_E20); + dc->nv2_mem_be = EX_TBFLAG_A64(tb_flags, NV2_MEM_BE); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = arm_cpu->cp_regs; dc->features = env->features; dc->dcz_blocksize = arm_cpu->dcz_blocksize; + dc->gm_blocksize = arm_cpu->gm_blocksize; #ifdef CONFIG_USER_ONLY /* In sve_probe_page, we assume TBI is enabled. */ tcg_debug_assert(dc->tbid & 1); #endif + dc->lse2 = dc_isar_feature(aa64_lse2, dc); + /* Single step state. The code-generation logic here is: * SS_ACTIVE == 0: * generate code with no special handling for single-stepping (except @@ -14645,7 +14155,6 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->ss_active = EX_TBFLAG_ANY(tb_flags, SS_ACTIVE); dc->pstate_ss = EX_TBFLAG_ANY(tb_flags, PSTATE__SS); dc->is_ldex = false; - dc->debug_target_el = EX_TBFLAG_ANY(tb_flags, DEBUG_TARGET_EL); /* Bound the number of insns to execute to those left on the page. */ bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; @@ -14655,8 +14164,6 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, bound = 1; } dc->base.max_insns = MIN(dc->base.max_insns, bound); - - init_tmp_a64_array(dc); } static void aarch64_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -14666,15 +14173,19 @@ static void aarch64_tr_tb_start(DisasContextBase *db, CPUState *cpu) static void aarch64_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); + target_ulong pc_arg = dc->base.pc_next; - tcg_gen_insn_start(dc->base.pc_next, 0, 0); - dc->insn_start = tcg_last_op(); + if (tb_cflags(dcbase->tb) & CF_PCREL) { + pc_arg &= ~TARGET_PAGE_MASK; + } + tcg_gen_insn_start(pc_arg, 0, 0); + dc->insn_start_updated = false; } static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *s = container_of(dcbase, DisasContext, base); - CPUARMState *env = cpu->env_ptr; + CPUARMState *env = cpu_env(cpu); uint64_t pc = s->base.pc_next; uint32_t insn; @@ -14705,7 +14216,7 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * start of the TB. */ assert(s->base.num_insns == 1); - gen_helper_exception_pc_alignment(cpu_env, tcg_constant_tl(pc)); + gen_helper_exception_pc_alignment(tcg_env, tcg_constant_tl(pc)); s->base.is_jmp = DISAS_NORETURN; s->base.pc_next = QEMU_ALIGN_UP(pc, 4); return; @@ -14724,8 +14235,7 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * Illegal execution state. This has priority over BTI * exceptions, but comes after instruction abort exceptions. */ - gen_exception_insn(s, s->pc_curr, EXCP_UDEF, - syn_illegalstate(), default_exception_el(s)); + gen_exception_insn(s, 0, EXCP_UDEF, syn_illegalstate()); return; } @@ -14756,9 +14266,7 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) if (s->btype != 0 && s->guarded_page && !btype_destination_ok(insn, s->bt, s->btype)) { - gen_exception_insn(s, s->pc_curr, EXCP_UDEF, - syn_btitrap(s->btype), - default_exception_el(s)); + gen_exception_insn(s, 0, EXCP_UDEF, syn_btitrap(s->btype)); return; } } else { @@ -14767,42 +14275,16 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) } } - switch (extract32(insn, 25, 4)) { - case 0x0: case 0x1: case 0x3: /* UNALLOCATED */ - unallocated_encoding(s); - break; - case 0x2: - if (!disas_sve(s, insn)) { - unallocated_encoding(s); - } - break; - case 0x8: case 0x9: /* Data processing - immediate */ - disas_data_proc_imm(s, insn); - break; - case 0xa: case 0xb: /* Branch, exception generation and system insns */ - disas_b_exc_sys(s, insn); - break; - case 0x4: - case 0x6: - case 0xc: - case 0xe: /* Loads and stores */ - disas_ldst(s, insn); - break; - case 0x5: - case 0xd: /* Data processing - register */ - disas_data_proc_reg(s, insn); - break; - case 0x7: - case 0xf: /* Data processing - SIMD and floating point */ - disas_data_proc_simd_fp(s, insn); - break; - default: - assert(FALSE); /* all 15 cases should be handled above */ - break; + s->is_nonstreaming = false; + if (s->sme_trap_nonstreaming) { + disas_sme_fa64(s, insn); } - /* if we allocated any temporaries, free them here */ - free_tmp_a64(s); + if (!disas_a64(s, insn) && + !disas_sme(s, insn) && + !disas_sve(s, insn)) { + disas_a64_legacy(s, insn); + } /* * After execution of most insns, btype is reset to 0. @@ -14811,8 +14293,6 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) if (s->btype > 0 && s->base.is_jmp != DISAS_NORETURN) { reset_btype(s); } - - translator_loop_temp_check(&s->base); } static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) @@ -14827,7 +14307,7 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) */ switch (dc->base.is_jmp) { default: - gen_a64_set_pc_im(dc->base.pc_next); + gen_a64_update_pc(dc, 4); /* fall through */ case DISAS_EXIT: case DISAS_JUMP: @@ -14840,17 +14320,17 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) switch (dc->base.is_jmp) { case DISAS_NEXT: case DISAS_TOO_MANY: - gen_goto_tb(dc, 1, dc->base.pc_next); + gen_goto_tb(dc, 1, 4); break; default: case DISAS_UPDATE_EXIT: - gen_a64_set_pc_im(dc->base.pc_next); + gen_a64_update_pc(dc, 4); /* fall through */ case DISAS_EXIT: tcg_gen_exit_tb(NULL, 0); break; case DISAS_UPDATE_NOCHAIN: - gen_a64_set_pc_im(dc->base.pc_next); + gen_a64_update_pc(dc, 4); /* fall through */ case DISAS_JUMP: tcg_gen_lookup_and_goto_ptr(); @@ -14859,20 +14339,20 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) case DISAS_SWI: break; case DISAS_WFE: - gen_a64_set_pc_im(dc->base.pc_next); - gen_helper_wfe(cpu_env); + gen_a64_update_pc(dc, 4); + gen_helper_wfe(tcg_env); break; case DISAS_YIELD: - gen_a64_set_pc_im(dc->base.pc_next); - gen_helper_yield(cpu_env); + gen_a64_update_pc(dc, 4); + gen_helper_yield(tcg_env); break; case DISAS_WFI: /* * This is a special case because we don't want to just halt * the CPU if trying to debug across a WFI. */ - gen_a64_set_pc_im(dc->base.pc_next); - gen_helper_wfi(cpu_env, tcg_constant_i32(4)); + gen_a64_update_pc(dc, 4); + gen_helper_wfi(tcg_env, tcg_constant_i32(4)); /* * The helper doesn't necessarily throw an exception, but we * must go back to the main loop to check for interrupts anyway. diff --git a/target/arm/translate-a64.h b/target/arm/tcg/translate-a64.h similarity index 60% rename from target/arm/translate-a64.h rename to target/arm/tcg/translate-a64.h index dbc917ee65..7b811b8ac5 100644 --- a/target/arm/translate-a64.h +++ b/target/arm/tcg/translate-a64.h @@ -18,9 +18,6 @@ #ifndef TARGET_ARM_TRANSLATE_A64_H #define TARGET_ARM_TRANSLATE_A64_H -TCGv_i64 new_tmp_a64(DisasContext *s); -TCGv_i64 new_tmp_a64_local(DisasContext *s); -TCGv_i64 new_tmp_a64_zero(DisasContext *s); TCGv_i64 cpu_reg(DisasContext *s, int reg); TCGv_i64 cpu_reg_sp(DisasContext *s, int reg); TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf); @@ -29,11 +26,34 @@ void write_fp_dreg(DisasContext *s, int reg, TCGv_i64 v); bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, unsigned int imms, unsigned int immr); bool sve_access_check(DisasContext *s); +bool sme_enabled_check(DisasContext *s); +bool sme_enabled_check_with_svcr(DisasContext *s, unsigned); +uint32_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, + uint32_t msz, bool is_write, uint32_t data); + +/* This function corresponds to CheckStreamingSVEEnabled. */ +static inline bool sme_sm_enabled_check(DisasContext *s) +{ + return sme_enabled_check_with_svcr(s, R_SVCR_SM_MASK); +} + +/* This function corresponds to CheckSMEAndZAEnabled. */ +static inline bool sme_za_enabled_check(DisasContext *s) +{ + return sme_enabled_check_with_svcr(s, R_SVCR_ZA_MASK); +} + +/* Note that this function corresponds to CheckStreamingSVEAndZAEnabled. */ +static inline bool sme_smza_enabled_check(DisasContext *s) +{ + return sme_enabled_check_with_svcr(s, R_SVCR_SM_MASK | R_SVCR_ZA_MASK); +} + TCGv_i64 clean_data_tbi(DisasContext *s, TCGv_i64 addr); TCGv_i64 gen_mte_check1(DisasContext *s, TCGv_i64 addr, bool is_write, - bool tag_checked, int log2_size); + bool tag_checked, MemOp memop); TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, - bool tag_checked, int size); + bool tag_checked, int total_size, MemOp memop); /* We should have at some point before trying to access an FP register * done the necessary access check, so assert that @@ -97,7 +117,7 @@ static inline int vec_full_reg_offset(DisasContext *s, int regno) static inline TCGv_ptr vec_full_reg_ptr(DisasContext *s, int regno) { TCGv_ptr ret = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(ret, cpu_env, vec_full_reg_offset(s, regno)); + tcg_gen_addi_ptr(ret, tcg_env, vec_full_reg_offset(s, regno)); return ret; } @@ -107,7 +127,66 @@ static inline int vec_full_reg_size(DisasContext *s) return s->vl; } +/* Return the byte size of the vector register, SVL / 8. */ +static inline int streaming_vec_reg_size(DisasContext *s) +{ + return s->svl; +} + +/* + * Return the offset info CPUARMState of the predicate vector register Pn. + * Note for this purpose, FFR is P16. + */ +static inline int pred_full_reg_offset(DisasContext *s, int regno) +{ + return offsetof(CPUARMState, vfp.pregs[regno]); +} + +/* Return the byte size of the whole predicate register, VL / 64. */ +static inline int pred_full_reg_size(DisasContext *s) +{ + return s->vl >> 3; +} + +/* Return the byte size of the predicate register, SVL / 64. */ +static inline int streaming_pred_reg_size(DisasContext *s) +{ + return s->svl >> 3; +} + +/* + * Round up the size of a register to a size allowed by + * the tcg vector infrastructure. Any operation which uses this + * size may assume that the bits above pred_full_reg_size are zero, + * and must leave them the same way. + * + * Note that this is not needed for the vector registers as they + * are always properly sized for tcg vectors. + */ +static inline int size_for_gvec(int size) +{ + if (size <= 8) { + return 8; + } else { + return QEMU_ALIGN_UP(size, 16); + } +} + +static inline int pred_gvec_reg_size(DisasContext *s) +{ + return size_for_gvec(pred_full_reg_size(s)); +} + +/* Return a newly allocated pointer to the predicate register. */ +static inline TCGv_ptr pred_full_reg_ptr(DisasContext *s, int regno) +{ + TCGv_ptr ret = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(ret, tcg_env, pred_full_reg_offset(s, regno)); + return ret; +} + bool disas_sve(DisasContext *, uint32_t); +bool disas_sme(DisasContext *, uint32_t); void gen_gvec_rax1(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); @@ -115,4 +194,7 @@ void gen_gvec_xar(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, int64_t shift, uint32_t opr_sz, uint32_t max_sz); +void gen_sve_ldr(DisasContext *s, TCGv_ptr, int vofs, int len, int rn, int imm); +void gen_sve_str(DisasContext *s, TCGv_ptr, int vofs, int len, int rn, int imm); + #endif /* TARGET_ARM_TRANSLATE_A64_H */ diff --git a/target/arm/translate-m-nocp.c b/target/arm/tcg/translate-m-nocp.c similarity index 92% rename from target/arm/translate-m-nocp.c rename to target/arm/tcg/translate-m-nocp.c index 27363a7b4e..f564d06ccf 100644 --- a/target/arm/translate-m-nocp.c +++ b/target/arm/tcg/translate-m-nocp.c @@ -18,8 +18,6 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" #include "translate.h" #include "translate-a32.h" @@ -87,11 +85,10 @@ static bool trans_VLLDM_VLSTM(DisasContext *s, arg_VLLDM_VLSTM *a) fptr = load_reg(s, a->rn); if (a->l) { - gen_helper_v7m_vlldm(cpu_env, fptr); + gen_helper_v7m_vlldm(tcg_env, fptr); } else { - gen_helper_v7m_vlstm(cpu_env, fptr); + gen_helper_v7m_vlstm(tcg_env, fptr); } - tcg_temp_free_i32(fptr); clear_eci_state(s); @@ -140,11 +137,11 @@ static bool trans_VSCCLRM(DisasContext *s, arg_VSCCLRM *a) tcg_gen_andi_i32(sfpa, sfpa, R_V7M_CONTROL_SFPA_MASK); tcg_gen_or_i32(sfpa, sfpa, aspen); arm_gen_condlabel(s); - tcg_gen_brcondi_i32(TCG_COND_EQ, sfpa, 0, s->condlabel); + tcg_gen_brcondi_i32(TCG_COND_EQ, sfpa, 0, s->condlabel.label); if (s->fp_excp_el != 0) { - gen_exception_insn(s, s->pc_curr, EXCP_NOCP, - syn_uncategorized(), s->fp_excp_el); + gen_exception_insn_el(s, 0, EXCP_NOCP, + syn_uncategorized(), s->fp_excp_el); return true; } @@ -303,8 +300,6 @@ static void gen_branch_fpInactive(DisasContext *s, TCGCond cond, tcg_gen_andi_i32(fpca, fpca, R_V7M_CONTROL_FPCA_MASK); tcg_gen_or_i32(fpca, fpca, aspen); tcg_gen_brcondi_i32(tcg_invert_cond(cond), fpca, 0, label); - tcg_temp_free_i32(aspen); - tcg_temp_free_i32(fpca); } static bool gen_M_fp_sysreg_write(DisasContext *s, int regno, @@ -327,8 +322,7 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno, switch (regno) { case ARM_VFP_FPSCR: tmp = loadfn(s, opaque, true); - gen_helper_vfp_set_fpscr(cpu_env, tmp); - tcg_temp_free_i32(tmp); + gen_helper_vfp_set_fpscr(tcg_env, tmp); gen_lookup_tb(s); break; case ARM_VFP_FPSCR_NZCVQC: @@ -351,7 +345,6 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno, tcg_gen_andi_i32(fpscr, fpscr, ~FPCR_NZCV_MASK); tcg_gen_or_i32(fpscr, fpscr, tmp); store_cpu_field(fpscr, vfp.xregs[ARM_VFP_FPSCR]); - tcg_temp_free_i32(tmp); break; } case ARM_VFP_FPCXT_NS: @@ -376,7 +369,7 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno, if (!vfp_access_check_m(s, true)) { /* * This was only a conditional exception, so override - * gen_exception_insn()'s default to DISAS_NORETURN + * gen_exception_insn_el()'s default to DISAS_NORETURN */ s->base.is_jmp = DISAS_NEXT; break; @@ -398,10 +391,8 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno, R_V7M_CONTROL_SFPA_SHIFT, 1); store_cpu_field(control, v7m.control[M_REG_S]); tcg_gen_andi_i32(tmp, tmp, ~FPCR_NZCV_MASK); - gen_helper_vfp_set_fpscr(cpu_env, tmp); + gen_helper_vfp_set_fpscr(tcg_env, tmp); s->base.is_jmp = DISAS_UPDATE_NOCHAIN; - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(sfpa); break; } case ARM_VFP_VPR: @@ -423,7 +414,6 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno, R_V7M_VPR_P0_SHIFT, R_V7M_VPR_P0_LENGTH); store_cpu_field(vpr, v7m.vpr); s->base.is_jmp = DISAS_UPDATE_NOCHAIN; - tcg_temp_free_i32(tmp); break; } default: @@ -461,12 +451,12 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, switch (regno) { case ARM_VFP_FPSCR: tmp = tcg_temp_new_i32(); - gen_helper_vfp_get_fpscr(tmp, cpu_env); + gen_helper_vfp_get_fpscr(tmp, tcg_env); storefn(s, opaque, tmp, true); break; case ARM_VFP_FPSCR_NZCVQC: tmp = tcg_temp_new_i32(); - gen_helper_vfp_get_fpscr(tmp, cpu_env); + gen_helper_vfp_get_fpscr(tmp, tcg_env); tcg_gen_andi_i32(tmp, tmp, FPCR_NZCVQC_MASK); storefn(s, opaque, tmp, true); break; @@ -485,13 +475,12 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, /* Bits [27:0] from FPSCR, bit [31] from CONTROL.SFPA */ tmp = tcg_temp_new_i32(); sfpa = tcg_temp_new_i32(); - gen_helper_vfp_get_fpscr(tmp, cpu_env); + gen_helper_vfp_get_fpscr(tmp, tcg_env); tcg_gen_andi_i32(tmp, tmp, ~FPCR_NZCV_MASK); control = load_cpu_field(v7m.control[M_REG_S]); tcg_gen_andi_i32(sfpa, control, R_V7M_CONTROL_SFPA_MASK); tcg_gen_shli_i32(sfpa, sfpa, 31 - R_V7M_CONTROL_SFPA_SHIFT); tcg_gen_or_i32(tmp, tmp, sfpa); - tcg_temp_free_i32(sfpa); /* * Store result before updating FPSCR etc, in case * it is a memory write which causes an exception. @@ -504,8 +493,7 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, tcg_gen_andi_i32(control, control, ~R_V7M_CONTROL_SFPA_MASK); store_cpu_field(control, v7m.control[M_REG_S]); fpscr = load_cpu_field(v7m.fpdscr[M_REG_NS]); - gen_helper_vfp_set_fpscr(cpu_env, fpscr); - tcg_temp_free_i32(fpscr); + gen_helper_vfp_set_fpscr(tcg_env, fpscr); lookup_tb = true; break; } @@ -518,7 +506,7 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, gen_branch_fpInactive(s, TCG_COND_EQ, lab_active); /* fpInactive case: reads as FPDSCR_NS */ - TCGv_i32 tmp = load_cpu_field(v7m.fpdscr[M_REG_NS]); + tmp = load_cpu_field(v7m.fpdscr[M_REG_NS]); storefn(s, opaque, tmp, true); lab_end = gen_new_label(); tcg_gen_br(lab_end); @@ -532,7 +520,7 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, if (!vfp_access_check_m(s, true)) { /* * This was only a conditional exception, so override - * gen_exception_insn()'s default to DISAS_NORETURN + * gen_exception_insn_el()'s default to DISAS_NORETURN */ s->base.is_jmp = DISAS_NEXT; break; @@ -540,23 +528,19 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, tmp = tcg_temp_new_i32(); sfpa = tcg_temp_new_i32(); fpscr = tcg_temp_new_i32(); - gen_helper_vfp_get_fpscr(fpscr, cpu_env); + gen_helper_vfp_get_fpscr(fpscr, tcg_env); tcg_gen_andi_i32(tmp, fpscr, ~FPCR_NZCV_MASK); control = load_cpu_field(v7m.control[M_REG_S]); tcg_gen_andi_i32(sfpa, control, R_V7M_CONTROL_SFPA_MASK); tcg_gen_shli_i32(sfpa, sfpa, 31 - R_V7M_CONTROL_SFPA_SHIFT); tcg_gen_or_i32(tmp, tmp, sfpa); - tcg_temp_free_i32(control); /* Store result before updating FPSCR, in case it faults */ storefn(s, opaque, tmp, true); /* If SFPA is zero then set FPSCR from FPDSCR_NS */ fpdscr = load_cpu_field(v7m.fpdscr[M_REG_NS]); tcg_gen_movcond_i32(TCG_COND_EQ, fpscr, sfpa, tcg_constant_i32(0), fpdscr, fpscr); - gen_helper_vfp_set_fpscr(cpu_env, fpscr); - tcg_temp_free_i32(sfpa); - tcg_temp_free_i32(fpdscr); - tcg_temp_free_i32(fpscr); + gen_helper_vfp_set_fpscr(tcg_env, fpscr); break; } case ARM_VFP_VPR: @@ -598,7 +582,6 @@ static void fp_sysreg_to_gpr(DisasContext *s, void *opaque, TCGv_i32 value, if (a->rt == 15) { /* Set the 4 flag bits in the CPSR */ gen_set_nzcv(value); - tcg_temp_free_i32(value); } else { store_reg(s, a->rt, value); } @@ -660,13 +643,12 @@ static void fp_sysreg_to_memory(DisasContext *s, void *opaque, TCGv_i32 value, } if (s->v8m_stackcheck && a->rn == 13 && a->w) { - gen_helper_v8m_stackcheck(cpu_env, addr); + gen_helper_v8m_stackcheck(tcg_env, addr); } if (do_access) { gen_aa32_st_i32(s, value, addr, get_mem_index(s), MO_UL | MO_ALIGN | s->be_data); - tcg_temp_free_i32(value); } if (a->w) { @@ -675,8 +657,6 @@ static void fp_sysreg_to_memory(DisasContext *s, void *opaque, TCGv_i32 value, tcg_gen_addi_i32(addr, addr, offset); } store_reg(s, a->rn, addr); - } else { - tcg_temp_free_i32(addr); } } @@ -702,7 +682,7 @@ static TCGv_i32 memory_to_fp_sysreg(DisasContext *s, void *opaque, } if (s->v8m_stackcheck && a->rn == 13 && a->w) { - gen_helper_v8m_stackcheck(cpu_env, addr); + gen_helper_v8m_stackcheck(tcg_env, addr); } if (do_access) { @@ -717,8 +697,6 @@ static TCGv_i32 memory_to_fp_sysreg(DisasContext *s, void *opaque, tcg_gen_addi_i32(addr, addr, offset); } store_reg(s, a->rn, addr); - } else { - tcg_temp_free_i32(addr); } return value; } @@ -765,14 +743,13 @@ static bool trans_NOCP(DisasContext *s, arg_nocp *a) } if (a->cp != 10) { - gen_exception_insn(s, s->pc_curr, EXCP_NOCP, - syn_uncategorized(), default_exception_el(s)); + gen_exception_insn(s, 0, EXCP_NOCP, syn_uncategorized()); return true; } if (s->fp_excp_el != 0) { - gen_exception_insn(s, s->pc_curr, EXCP_NOCP, - syn_uncategorized(), s->fp_excp_el); + gen_exception_insn_el(s, 0, EXCP_NOCP, + syn_uncategorized(), s->fp_excp_el); return true; } diff --git a/target/arm/translate-mve.c b/target/arm/tcg/translate-mve.c similarity index 95% rename from target/arm/translate-mve.c rename to target/arm/tcg/translate-mve.c index 4267d43cc7..b1a8d6a65c 100644 --- a/target/arm/translate-mve.c +++ b/target/arm/tcg/translate-mve.c @@ -18,10 +18,6 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "exec/exec-all.h" -#include "exec/gen-icount.h" #include "translate.h" #include "translate-a32.h" @@ -60,7 +56,7 @@ static inline long mve_qreg_offset(unsigned reg) static TCGv_ptr mve_qreg_ptr(unsigned reg) { TCGv_ptr ret = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(ret, cpu_env, mve_qreg_offset(reg)); + tcg_gen_addi_ptr(ret, tcg_env, mve_qreg_offset(reg)); return ret; } @@ -100,8 +96,7 @@ bool mve_eci_check(DisasContext *s) return true; default: /* Reserved value: INVSTATE UsageFault */ - gen_exception_insn(s, s->pc_curr, EXCP_INVSTATE, syn_uncategorized(), - default_exception_el(s)); + gen_exception_insn(s, 0, EXCP_INVSTATE, syn_uncategorized()); return false; } } @@ -178,8 +173,7 @@ static bool do_ldst(DisasContext *s, arg_VLDR_VSTR *a, MVEGenLdStFn *fn, } qreg = mve_qreg_ptr(a->qd); - fn(cpu_env, qreg, addr); - tcg_temp_free_ptr(qreg); + fn(tcg_env, qreg, addr); /* * Writeback always happens after the last beat of the insn, @@ -190,8 +184,6 @@ static bool do_ldst(DisasContext *s, arg_VLDR_VSTR *a, MVEGenLdStFn *fn, tcg_gen_addi_i32(addr, addr, offset); } store_reg(s, a->rn, addr); - } else { - tcg_temp_free_i32(addr); } mve_update_eci(s); return true; @@ -242,10 +234,7 @@ static bool do_ldst_sg(DisasContext *s, arg_vldst_sg *a, MVEGenLdStSGFn fn) qd = mve_qreg_ptr(a->qd); qm = mve_qreg_ptr(a->qm); - fn(cpu_env, qd, qm, addr); - tcg_temp_free_ptr(qd); - tcg_temp_free_ptr(qm); - tcg_temp_free_i32(addr); + fn(tcg_env, qd, qm, addr); mve_update_eci(s); return true; } @@ -341,9 +330,7 @@ static bool do_ldst_sg_imm(DisasContext *s, arg_vldst_sg_imm *a, qd = mve_qreg_ptr(a->qd); qm = mve_qreg_ptr(a->qm); - fn(cpu_env, qd, qm, tcg_constant_i32(offset)); - tcg_temp_free_ptr(qd); - tcg_temp_free_ptr(qm); + fn(tcg_env, qd, qm, tcg_constant_i32(offset)); mve_update_eci(s); return true; } @@ -410,13 +397,11 @@ static bool do_vldst_il(DisasContext *s, arg_vldst_il *a, MVEGenLdStIlFn *fn, * We pass the index of Qd, not a pointer, because the helper must * access multiple Q registers starting at Qd and working up. */ - fn(cpu_env, tcg_constant_i32(a->qd), rn); + fn(tcg_env, tcg_constant_i32(a->qd), rn); if (a->w) { tcg_gen_addi_i32(rn, rn, addrinc); store_reg(s, a->rn, rn); - } else { - tcg_temp_free_i32(rn); } mve_update_and_store_eci(s); return true; @@ -506,10 +491,8 @@ static bool trans_VDUP(DisasContext *s, arg_VDUP *a) } else { qd = mve_qreg_ptr(a->qd); tcg_gen_dup_i32(a->size, rt, rt); - gen_helper_mve_vdup(cpu_env, qd, rt); - tcg_temp_free_ptr(qd); + gen_helper_mve_vdup(tcg_env, qd, rt); } - tcg_temp_free_i32(rt); mve_update_eci(s); return true; } @@ -534,9 +517,7 @@ static bool do_1op_vec(DisasContext *s, arg_1op *a, MVEGenOneOpFn fn, } else { qd = mve_qreg_ptr(a->qd); qm = mve_qreg_ptr(a->qm); - fn(cpu_env, qd, qm); - tcg_temp_free_ptr(qd); - tcg_temp_free_ptr(qm); + fn(tcg_env, qd, qm); } mve_update_eci(s); return true; @@ -603,7 +584,7 @@ DO_VCVT(VCVT_FS, vcvt_hs, vcvt_fs) DO_VCVT(VCVT_FU, vcvt_hu, vcvt_fu) static bool do_vcvt_rmode(DisasContext *s, arg_1op *a, - enum arm_fprounding rmode, bool u) + ARMFPRounding rmode, bool u) { /* * Handle VCVT fp to int with specified rounding mode. @@ -631,9 +612,7 @@ static bool do_vcvt_rmode(DisasContext *s, arg_1op *a, qd = mve_qreg_ptr(a->qd); qm = mve_qreg_ptr(a->qm); - fn(cpu_env, qd, qm, tcg_constant_i32(arm_rmode_to_sf(rmode))); - tcg_temp_free_ptr(qd); - tcg_temp_free_ptr(qm); + fn(tcg_env, qd, qm, tcg_constant_i32(arm_rmode_to_sf(rmode))); mve_update_eci(s); return true; } @@ -821,10 +800,7 @@ static bool do_2op_vec(DisasContext *s, arg_2op *a, MVEGenTwoOpFn fn, qd = mve_qreg_ptr(a->qd); qn = mve_qreg_ptr(a->qn); qm = mve_qreg_ptr(a->qm); - fn(cpu_env, qd, qn, qm); - tcg_temp_free_ptr(qd); - tcg_temp_free_ptr(qn); - tcg_temp_free_ptr(qm); + fn(tcg_env, qd, qn, qm); } mve_update_eci(s); return true; @@ -1076,10 +1052,7 @@ static bool do_2op_scalar(DisasContext *s, arg_2scalar *a, qd = mve_qreg_ptr(a->qd); qn = mve_qreg_ptr(a->qn); rm = load_reg(s, a->rm); - fn(cpu_env, qd, qn, rm); - tcg_temp_free_i32(rm); - tcg_temp_free_ptr(qd); - tcg_temp_free_ptr(qn); + fn(tcg_env, qd, qn, rm); mve_update_eci(s); return true; } @@ -1173,7 +1146,7 @@ static bool do_long_dual_acc(DisasContext *s, arg_vmlaldav *a, MVEGenLongDualAccOpFn *fn) { TCGv_ptr qn, qm; - TCGv_i64 rda; + TCGv_i64 rda_i, rda_o; TCGv_i32 rdalo, rdahi; if (!dc_isar_feature(aa32_mve, s) || @@ -1200,28 +1173,24 @@ static bool do_long_dual_acc(DisasContext *s, arg_vmlaldav *a, * of an A=0 (no-accumulate) insn which does not execute the first * beat must start with the current rda value, not 0. */ + rda_o = tcg_temp_new_i64(); if (a->a || mve_skip_first_beat(s)) { - rda = tcg_temp_new_i64(); + rda_i = rda_o; rdalo = load_reg(s, a->rdalo); rdahi = load_reg(s, a->rdahi); - tcg_gen_concat_i32_i64(rda, rdalo, rdahi); - tcg_temp_free_i32(rdalo); - tcg_temp_free_i32(rdahi); + tcg_gen_concat_i32_i64(rda_i, rdalo, rdahi); } else { - rda = tcg_const_i64(0); + rda_i = tcg_constant_i64(0); } - fn(rda, cpu_env, qn, qm, rda); - tcg_temp_free_ptr(qn); - tcg_temp_free_ptr(qm); + fn(rda_o, tcg_env, qn, qm, rda_i); rdalo = tcg_temp_new_i32(); rdahi = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(rdalo, rda); - tcg_gen_extrh_i64_i32(rdahi, rda); + tcg_gen_extrl_i64_i32(rdalo, rda_o); + tcg_gen_extrh_i64_i32(rdahi, rda_o); store_reg(s, a->rdalo, rdalo); store_reg(s, a->rdahi, rdahi); - tcg_temp_free_i64(rda); mve_update_eci(s); return true; } @@ -1286,7 +1255,7 @@ static bool trans_VRMLSLDAVH(DisasContext *s, arg_vmlaldav *a) static bool do_dual_acc(DisasContext *s, arg_vmladav *a, MVEGenDualAccOpFn *fn) { TCGv_ptr qn, qm; - TCGv_i32 rda; + TCGv_i32 rda_i, rda_o; if (!dc_isar_feature(aa32_mve, s) || !mve_check_qreg_bank(s, a->qn) || @@ -1306,15 +1275,14 @@ static bool do_dual_acc(DisasContext *s, arg_vmladav *a, MVEGenDualAccOpFn *fn) * beat must start with the current rda value, not 0. */ if (a->a || mve_skip_first_beat(s)) { - rda = load_reg(s, a->rda); + rda_o = rda_i = load_reg(s, a->rda); } else { - rda = tcg_const_i32(0); + rda_i = tcg_constant_i32(0); + rda_o = tcg_temp_new_i32(); } - fn(rda, cpu_env, qn, qm, rda); - store_reg(s, a->rda, rda); - tcg_temp_free_ptr(qn); - tcg_temp_free_ptr(qm); + fn(rda_o, tcg_env, qn, qm, rda_i); + store_reg(s, a->rda, rda_o); mve_update_eci(s); return true; @@ -1409,7 +1377,7 @@ static bool trans_VPNOT(DisasContext *s, arg_VPNOT *a) return true; } - gen_helper_mve_vpnot(cpu_env); + gen_helper_mve_vpnot(tcg_env); /* This insn updates predication bits */ s->base.is_jmp = DISAS_UPDATE_NOCHAIN; mve_update_eci(s); @@ -1426,7 +1394,7 @@ static bool trans_VADDV(DisasContext *s, arg_VADDV *a) { NULL, NULL } }; TCGv_ptr qm; - TCGv_i32 rda; + TCGv_i32 rda_i, rda_o; if (!dc_isar_feature(aa32_mve, s) || a->size == 3) { @@ -1443,16 +1411,16 @@ static bool trans_VADDV(DisasContext *s, arg_VADDV *a) */ if (a->a || mve_skip_first_beat(s)) { /* Accumulate input from Rda */ - rda = load_reg(s, a->rda); + rda_o = rda_i = load_reg(s, a->rda); } else { /* Accumulate starting at zero */ - rda = tcg_const_i32(0); + rda_i = tcg_constant_i32(0); + rda_o = tcg_temp_new_i32(); } qm = mve_qreg_ptr(a->qm); - fns[a->size][a->u](rda, cpu_env, qm, rda); - store_reg(s, a->rda, rda); - tcg_temp_free_ptr(qm); + fns[a->size][a->u](rda_o, tcg_env, qm, rda_i); + store_reg(s, a->rda, rda_o); mve_update_eci(s); return true; @@ -1467,7 +1435,7 @@ static bool trans_VADDLV(DisasContext *s, arg_VADDLV *a) * No need to check Qm's bank: it is only 3 bits in decode. */ TCGv_ptr qm; - TCGv_i64 rda; + TCGv_i64 rda_i, rda_o; TCGv_i32 rdalo, rdahi; if (!dc_isar_feature(aa32_mve, s)) { @@ -1489,34 +1457,31 @@ static bool trans_VADDLV(DisasContext *s, arg_VADDLV *a) * of an A=0 (no-accumulate) insn which does not execute the first * beat must start with the current value of RdaHi:RdaLo, not zero. */ + rda_o = tcg_temp_new_i64(); if (a->a || mve_skip_first_beat(s)) { /* Accumulate input from RdaHi:RdaLo */ - rda = tcg_temp_new_i64(); + rda_i = rda_o; rdalo = load_reg(s, a->rdalo); rdahi = load_reg(s, a->rdahi); - tcg_gen_concat_i32_i64(rda, rdalo, rdahi); - tcg_temp_free_i32(rdalo); - tcg_temp_free_i32(rdahi); + tcg_gen_concat_i32_i64(rda_i, rdalo, rdahi); } else { /* Accumulate starting at zero */ - rda = tcg_const_i64(0); + rda_i = tcg_constant_i64(0); } qm = mve_qreg_ptr(a->qm); if (a->u) { - gen_helper_mve_vaddlv_u(rda, cpu_env, qm, rda); + gen_helper_mve_vaddlv_u(rda_o, tcg_env, qm, rda_i); } else { - gen_helper_mve_vaddlv_s(rda, cpu_env, qm, rda); + gen_helper_mve_vaddlv_s(rda_o, tcg_env, qm, rda_i); } - tcg_temp_free_ptr(qm); rdalo = tcg_temp_new_i32(); rdahi = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(rdalo, rda); - tcg_gen_extrh_i64_i32(rdahi, rda); + tcg_gen_extrl_i64_i32(rdalo, rda_o); + tcg_gen_extrh_i64_i32(rdahi, rda_o); store_reg(s, a->rdalo, rdalo); store_reg(s, a->rdahi, rdahi); - tcg_temp_free_i64(rda); mve_update_eci(s); return true; } @@ -1543,8 +1508,7 @@ static bool do_1imm(DisasContext *s, arg_1imm *a, MVEGenOneOpImmFn *fn, imm, 16, 16); } else { qd = mve_qreg_ptr(a->qd); - fn(cpu_env, qd, tcg_constant_i64(imm)); - tcg_temp_free_ptr(qd); + fn(tcg_env, qd, tcg_constant_i64(imm)); } mve_update_eci(s); return true; @@ -1616,9 +1580,7 @@ static bool do_2shift_vec(DisasContext *s, arg_2shift *a, MVEGenTwoOpShiftFn fn, } else { qd = mve_qreg_ptr(a->qd); qm = mve_qreg_ptr(a->qm); - fn(cpu_env, qd, qm, tcg_constant_i32(shift)); - tcg_temp_free_ptr(qd); - tcg_temp_free_ptr(qm); + fn(tcg_env, qd, qm, tcg_constant_i32(shift)); } mve_update_eci(s); return true; @@ -1723,9 +1685,7 @@ static bool do_2shift_scalar(DisasContext *s, arg_shl_scalar *a, qda = mve_qreg_ptr(a->qda); rm = load_reg(s, a->rm); - fn(cpu_env, qda, qda, rm); - tcg_temp_free_ptr(qda); - tcg_temp_free_i32(rm); + fn(tcg_env, qda, qda, rm); mve_update_eci(s); return true; } @@ -1867,9 +1827,8 @@ static bool trans_VSHLC(DisasContext *s, arg_VSHLC *a) qd = mve_qreg_ptr(a->qd); rdm = load_reg(s, a->rdm); - gen_helper_mve_vshlc(rdm, cpu_env, qd, rdm, tcg_constant_i32(a->imm)); + gen_helper_mve_vshlc(rdm, tcg_env, qd, rdm, tcg_constant_i32(a->imm)); store_reg(s, a->rdm, rdm); - tcg_temp_free_ptr(qd); mve_update_eci(s); return true; } @@ -1897,9 +1856,8 @@ static bool do_vidup(DisasContext *s, arg_vidup *a, MVEGenVIDUPFn *fn) qd = mve_qreg_ptr(a->qd); rn = load_reg(s, a->rn); - fn(rn, cpu_env, qd, rn, tcg_constant_i32(a->imm)); + fn(rn, tcg_env, qd, rn, tcg_constant_i32(a->imm)); store_reg(s, a->rn, rn); - tcg_temp_free_ptr(qd); mve_update_eci(s); return true; } @@ -1933,10 +1891,8 @@ static bool do_viwdup(DisasContext *s, arg_viwdup *a, MVEGenVIWDUPFn *fn) qd = mve_qreg_ptr(a->qd); rn = load_reg(s, a->rn); rm = load_reg(s, a->rm); - fn(rn, cpu_env, qd, rn, rm, tcg_constant_i32(a->imm)); + fn(rn, tcg_env, qd, rn, rm, tcg_constant_i32(a->imm)); store_reg(s, a->rn, rn); - tcg_temp_free_ptr(qd); - tcg_temp_free_i32(rm); mve_update_eci(s); return true; } @@ -2001,9 +1957,7 @@ static bool do_vcmp(DisasContext *s, arg_vcmp *a, MVEGenCmpFn *fn) qn = mve_qreg_ptr(a->qn); qm = mve_qreg_ptr(a->qm); - fn(cpu_env, qn, qm); - tcg_temp_free_ptr(qn); - tcg_temp_free_ptr(qm); + fn(tcg_env, qn, qm); if (a->mask) { /* VPT */ gen_vpst(s, a->mask); @@ -2034,9 +1988,7 @@ static bool do_vcmp_scalar(DisasContext *s, arg_vcmp_scalar *a, } else { rm = load_reg(s, a->rm); } - fn(cpu_env, qn, rm); - tcg_temp_free_ptr(qn); - tcg_temp_free_i32(rm); + fn(tcg_env, qn, rm); if (a->mask) { /* VPT */ gen_vpst(s, a->mask); @@ -2137,9 +2089,8 @@ static bool do_vmaxv(DisasContext *s, arg_vmaxv *a, MVEGenVADDVFn fn) qm = mve_qreg_ptr(a->qm); rda = load_reg(s, a->rda); - fn(rda, cpu_env, qm, rda); + fn(rda, tcg_env, qm, rda); store_reg(s, a->rda, rda); - tcg_temp_free_ptr(qm); mve_update_eci(s); return true; } @@ -2202,10 +2153,8 @@ static bool do_vabav(DisasContext *s, arg_vabav *a, MVEGenVABAVFn *fn) qm = mve_qreg_ptr(a->qm); qn = mve_qreg_ptr(a->qn); rda = load_reg(s, a->rda); - fn(rda, cpu_env, qn, qm, rda); + fn(rda, tcg_env, qn, qm, rda); store_reg(s, a->rda, rda); - tcg_temp_free_ptr(qm); - tcg_temp_free_ptr(qn); mve_update_eci(s); return true; } @@ -2233,7 +2182,7 @@ static bool trans_VMOV_to_2gp(DisasContext *s, arg_VMOV_to_2gp *a) * execution if it is not in an IT block. For us this means * only that if PSR.ECI says we should not be executing the beat * corresponding to the lane of the vector register being accessed - * then we should skip perfoming the move, and that we need to do + * then we should skip performing the move, and that we need to do * the usual check for bad ECI state and advance of ECI state. * (If PSR.ECI is non-zero then we cannot be in an IT block.) */ @@ -2276,7 +2225,7 @@ static bool trans_VMOV_from_2gp(DisasContext *s, arg_VMOV_to_2gp *a) * execution if it is not in an IT block. For us this means * only that if PSR.ECI says we should not be executing the beat * corresponding to the lane of the vector register being accessed - * then we should skip perfoming the move, and that we need to do + * then we should skip performing the move, and that we need to do * the usual check for bad ECI state and advance of ECI state. * (If PSR.ECI is non-zero then we cannot be in an IT block.) */ @@ -2298,12 +2247,10 @@ static bool trans_VMOV_from_2gp(DisasContext *s, arg_VMOV_to_2gp *a) if (!mve_skip_vmov(s, vd, a->idx, MO_32)) { tmp = load_reg(s, a->rt); write_neon_element32(tmp, vd, a->idx, MO_32); - tcg_temp_free_i32(tmp); } if (!mve_skip_vmov(s, vd + 1, a->idx, MO_32)) { tmp = load_reg(s, a->rt2); write_neon_element32(tmp, vd + 1, a->idx, MO_32); - tcg_temp_free_i32(tmp); } mve_update_and_store_eci(s); diff --git a/target/arm/translate-neon.c b/target/arm/tcg/translate-neon.c similarity index 95% rename from target/arm/translate-neon.c rename to target/arm/tcg/translate-neon.c index 321c17e2c7..144f18ba22 100644 --- a/target/arm/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -21,10 +21,6 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "exec/exec-all.h" -#include "exec/gen-icount.h" #include "translate.h" #include "translate-a32.h" @@ -36,7 +32,7 @@ static TCGv_ptr vfp_reg_ptr(bool dp, int reg) { TCGv_ptr ret = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(ret, cpu_env, vfp_reg_offset(dp, reg)); + tcg_gen_addi_ptr(ret, tcg_env, vfp_reg_offset(dp, reg)); return ret; } @@ -46,13 +42,13 @@ static void neon_load_element(TCGv_i32 var, int reg, int ele, MemOp mop) switch (mop) { case MO_UB: - tcg_gen_ld8u_i32(var, cpu_env, offset); + tcg_gen_ld8u_i32(var, tcg_env, offset); break; case MO_UW: - tcg_gen_ld16u_i32(var, cpu_env, offset); + tcg_gen_ld16u_i32(var, tcg_env, offset); break; case MO_UL: - tcg_gen_ld_i32(var, cpu_env, offset); + tcg_gen_ld_i32(var, tcg_env, offset); break; default: g_assert_not_reached(); @@ -65,16 +61,16 @@ static void neon_load_element64(TCGv_i64 var, int reg, int ele, MemOp mop) switch (mop) { case MO_UB: - tcg_gen_ld8u_i64(var, cpu_env, offset); + tcg_gen_ld8u_i64(var, tcg_env, offset); break; case MO_UW: - tcg_gen_ld16u_i64(var, cpu_env, offset); + tcg_gen_ld16u_i64(var, tcg_env, offset); break; case MO_UL: - tcg_gen_ld32u_i64(var, cpu_env, offset); + tcg_gen_ld32u_i64(var, tcg_env, offset); break; case MO_UQ: - tcg_gen_ld_i64(var, cpu_env, offset); + tcg_gen_ld_i64(var, tcg_env, offset); break; default: g_assert_not_reached(); @@ -87,13 +83,13 @@ static void neon_store_element(int reg, int ele, MemOp size, TCGv_i32 var) switch (size) { case MO_8: - tcg_gen_st8_i32(var, cpu_env, offset); + tcg_gen_st8_i32(var, tcg_env, offset); break; case MO_16: - tcg_gen_st16_i32(var, cpu_env, offset); + tcg_gen_st16_i32(var, tcg_env, offset); break; case MO_32: - tcg_gen_st_i32(var, cpu_env, offset); + tcg_gen_st_i32(var, tcg_env, offset); break; default: g_assert_not_reached(); @@ -106,16 +102,16 @@ static void neon_store_element64(int reg, int ele, MemOp size, TCGv_i64 var) switch (size) { case MO_8: - tcg_gen_st8_i64(var, cpu_env, offset); + tcg_gen_st8_i64(var, tcg_env, offset); break; case MO_16: - tcg_gen_st16_i64(var, cpu_env, offset); + tcg_gen_st16_i64(var, tcg_env, offset); break; case MO_32: - tcg_gen_st32_i64(var, cpu_env, offset); + tcg_gen_st32_i64(var, tcg_env, offset); break; case MO_64: - tcg_gen_st_i64(var, cpu_env, offset); + tcg_gen_st_i64(var, tcg_env, offset); break; default: g_assert_not_reached(); @@ -182,7 +178,6 @@ static bool do_neon_ddda_fpst(DisasContext *s, int q, int vd, int vn, int vm, vfp_reg_offset(1, vm), vfp_reg_offset(1, vd), fpst, opr_sz, opr_sz, data, fn_gvec_ptr); - tcg_temp_free_ptr(fpst); return true; } @@ -236,7 +231,6 @@ static bool trans_VCADD(DisasContext *s, arg_VCADD *a) vfp_reg_offset(1, a->vm), fpst, opr_sz, opr_sz, a->rot, fn_gvec_ptr); - tcg_temp_free_ptr(fpst); return true; } @@ -302,7 +296,7 @@ static bool trans_VFML(DisasContext *s, arg_VFML *a) tcg_gen_gvec_3_ptr(vfp_reg_offset(1, a->vd), vfp_reg_offset(a->q, a->vn), vfp_reg_offset(a->q, a->vm), - cpu_env, opr_sz, opr_sz, a->s, /* is_2 == 0 */ + tcg_env, opr_sz, opr_sz, a->s, /* is_2 == 0 */ gen_helper_gvec_fmlal_a32); return true; } @@ -396,7 +390,7 @@ static bool trans_VFML_scalar(DisasContext *s, arg_VFML_scalar *a) tcg_gen_gvec_3_ptr(vfp_reg_offset(1, a->vd), vfp_reg_offset(a->q, a->vn), vfp_reg_offset(a->q, a->rm), - cpu_env, opr_sz, opr_sz, + tcg_env, opr_sz, opr_sz, (a->index << 2) | a->s, /* is_2 == 0 */ gen_helper_gvec_fmlal_idx_a32); return true; @@ -433,7 +427,6 @@ static void gen_neon_ldst_base_update(DisasContext *s, int rm, int rn, TCGv_i32 index; index = load_reg(s, rm); tcg_gen_add_i32(base, base, index); - tcg_temp_free_i32(index); } store_reg(s, rn, base); } @@ -536,8 +529,6 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a) } } } - tcg_temp_free_i32(addr); - tcg_temp_free_i64(tmp64); gen_neon_ldst_base_update(s, a->rm, a->rn, nregs * interleave * 8); return true; @@ -584,7 +575,11 @@ static bool trans_VLD_all_lanes(DisasContext *s, arg_VLD_all_lanes *a) case 3: return false; case 4: - align = pow2_align(size + 2); + if (size == 2) { + align = pow2_align(3); + } else { + align = pow2_align(size + 2); + } break; default: g_assert_not_reached(); @@ -626,8 +621,6 @@ static bool trans_VLD_all_lanes(DisasContext *s, arg_VLD_all_lanes *a) /* Subsequent memory operations inherit alignment */ mop &= ~MO_AMASK; } - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(addr); gen_neon_ldst_base_update(s, a->rm, a->rn, (1 << size) * nregs); @@ -747,8 +740,6 @@ static bool trans_VLDST_single(DisasContext *s, arg_VLDST_single *a) /* Subsequent memory operations inherit alignment */ mop &= ~MO_AMASK; } - tcg_temp_free_i32(addr); - tcg_temp_free_i32(tmp); gen_neon_ldst_base_update(s, a->rm, a->rn, (1 << a->size) * nregs); @@ -929,7 +920,7 @@ DO_SHA2(SHA256SU1, gen_helper_crypto_sha256su1) #define DO_3SAME_64_ENV(INSN, FUNC) \ static void gen_##INSN##_elt(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) \ { \ - FUNC(d, cpu_env, n, m); \ + FUNC(d, tcg_env, n, m); \ } \ DO_3SAME_64(INSN, gen_##INSN##_elt) @@ -962,7 +953,7 @@ DO_3SAME_64_ENV(VQRSHL_U64, gen_helper_neon_qrshl_u64) } /* - * Some helper functions need to be passed the cpu_env. In order + * Some helper functions need to be passed the tcg_env. In order * to use those with the gvec APIs like tcg_gen_gvec_3() we need * to create wrapper functions whose prototype is a NeonGenTwoOpFn() * and which call a NeonGenTwoOpEnvFn(). @@ -970,7 +961,7 @@ DO_3SAME_64_ENV(VQRSHL_U64, gen_helper_neon_qrshl_u64) #define WRAP_ENV_FN(WRAPNAME, FUNC) \ static void WRAPNAME(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m) \ { \ - FUNC(d, cpu_env, n, m); \ + FUNC(d, tcg_env, n, m); \ } #define DO_3SAME_32_ENV(INSN, FUNC) \ @@ -1057,9 +1048,6 @@ static bool do_3same_pair(DisasContext *s, arg_3same *a, NeonGenTwoOpFn *fn) write_neon_element32(tmp, a->vd, 0, MO_32); write_neon_element32(tmp3, a->vd, 1, MO_32); - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(tmp2); - tcg_temp_free_i32(tmp3); return true; } @@ -1122,7 +1110,6 @@ DO_3SAME_VQDMULH(VQRDMULH, qrdmulh) TCGv_ptr fpst = fpstatus_ptr(FPST); \ tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, fpst, \ oprsz, maxsz, 0, FUNC); \ - tcg_temp_free_ptr(fpst); \ } #define DO_3S_FP_GVEC(INSN,SFUNC,HFUNC) \ @@ -1221,7 +1208,6 @@ static bool do_3same_fp_pair(DisasContext *s, arg_3same *a, vfp_reg_offset(1, a->vn), vfp_reg_offset(1, a->vm), fpstatus, 8, 8, 0, fn); - tcg_temp_free_ptr(fpstatus); return true; } @@ -1319,7 +1305,7 @@ static bool do_2shift_env_64(DisasContext *s, arg_2reg_shift *a, { /* * 2-reg-and-shift operations, size == 3 case, where the - * function needs to be passed cpu_env. + * function needs to be passed tcg_env. */ TCGv_i64 constimm; int pass; @@ -1352,9 +1338,8 @@ static bool do_2shift_env_64(DisasContext *s, arg_2reg_shift *a, TCGv_i64 tmp = tcg_temp_new_i64(); read_neon_element64(tmp, a->vm, pass, MO_64); - fn(tmp, cpu_env, tmp, constimm); + fn(tmp, tcg_env, tmp, constimm); write_neon_element64(tmp, a->vd, pass, MO_64); - tcg_temp_free_i64(tmp); } return true; } @@ -1364,7 +1349,7 @@ static bool do_2shift_env_32(DisasContext *s, arg_2reg_shift *a, { /* * 2-reg-and-shift operations, size < 3 case, where the - * helper needs to be passed cpu_env. + * helper needs to be passed tcg_env. */ TCGv_i32 constimm, tmp; int pass; @@ -1396,10 +1381,9 @@ static bool do_2shift_env_32(DisasContext *s, arg_2reg_shift *a, for (pass = 0; pass < (a->q ? 4 : 2); pass++) { read_neon_element32(tmp, a->vm, pass, MO_32); - fn(tmp, cpu_env, tmp, constimm); + fn(tmp, tcg_env, tmp, constimm); write_neon_element32(tmp, a->vd, pass, MO_32); } - tcg_temp_free_i32(tmp); return true; } @@ -1463,17 +1447,13 @@ static bool do_2shift_narrow_64(DisasContext *s, arg_2reg_shift *a, read_neon_element64(rm2, a->vm, 1, MO_64); shiftfn(rm1, rm1, constimm); - narrowfn(rd, cpu_env, rm1); + narrowfn(rd, tcg_env, rm1); write_neon_element32(rd, a->vd, 0, MO_32); shiftfn(rm2, rm2, constimm); - narrowfn(rd, cpu_env, rm2); + narrowfn(rd, tcg_env, rm2); write_neon_element32(rd, a->vd, 1, MO_32); - tcg_temp_free_i32(rd); - tcg_temp_free_i64(rm1); - tcg_temp_free_i64(rm2); - return true; } @@ -1533,22 +1513,17 @@ static bool do_2shift_narrow_32(DisasContext *s, arg_2reg_shift *a, shiftfn(rm2, rm2, constimm); tcg_gen_concat_i32_i64(rtmp, rm1, rm2); - tcg_temp_free_i32(rm2); - narrowfn(rm1, cpu_env, rtmp); + narrowfn(rm1, tcg_env, rtmp); write_neon_element32(rm1, a->vd, 0, MO_32); - tcg_temp_free_i32(rm1); shiftfn(rm3, rm3, constimm); shiftfn(rm4, rm4, constimm); tcg_gen_concat_i32_i64(rtmp, rm3, rm4); - tcg_temp_free_i32(rm4); - narrowfn(rm3, cpu_env, rtmp); - tcg_temp_free_i64(rtmp); + narrowfn(rm3, tcg_env, rtmp); write_neon_element32(rm3, a->vd, 1, MO_32); - tcg_temp_free_i32(rm3); return true; } @@ -1656,7 +1631,6 @@ static bool do_vshll_2sh(DisasContext *s, arg_2reg_shift *a, tmp = tcg_temp_new_i64(); widenfn(tmp, rm0); - tcg_temp_free_i32(rm0); if (a->shift != 0) { tcg_gen_shli_i64(tmp, tmp, a->shift); tcg_gen_andi_i64(tmp, tmp, ~widen_mask); @@ -1664,13 +1638,11 @@ static bool do_vshll_2sh(DisasContext *s, arg_2reg_shift *a, write_neon_element64(tmp, a->vd, 0, MO_64); widenfn(tmp, rm1); - tcg_temp_free_i32(rm1); if (a->shift != 0) { tcg_gen_shli_i64(tmp, tmp, a->shift); tcg_gen_andi_i64(tmp, tmp, ~widen_mask); } write_neon_element64(tmp, a->vd, 1, MO_64); - tcg_temp_free_i64(tmp); return true; } @@ -1729,7 +1701,6 @@ static bool do_fp_2sh(DisasContext *s, arg_2reg_shift *a, fpst = fpstatus_ptr(a->size == MO_16 ? FPST_STD_F16 : FPST_STD); tcg_gen_gvec_2_ptr(rd_ofs, rm_ofs, fpst, vec_size, vec_size, a->shift, fn); - tcg_temp_free_ptr(fpst); return true; } @@ -1845,7 +1816,6 @@ static bool do_prewiden_3d(DisasContext *s, arg_3diff *a, TCGv_i32 tmp = tcg_temp_new_i32(); read_neon_element32(tmp, a->vn, 0, MO_32); widenfn(rn0_64, tmp); - tcg_temp_free_i32(tmp); } if (src2_mop >= 0) { read_neon_element64(rm_64, a->vm, 0, src2_mop); @@ -1853,7 +1823,6 @@ static bool do_prewiden_3d(DisasContext *s, arg_3diff *a, TCGv_i32 tmp = tcg_temp_new_i32(); read_neon_element32(tmp, a->vm, 0, MO_32); widenfn(rm_64, tmp); - tcg_temp_free_i32(tmp); } opfn(rn0_64, rn0_64, rm_64); @@ -1868,7 +1837,6 @@ static bool do_prewiden_3d(DisasContext *s, arg_3diff *a, TCGv_i32 tmp = tcg_temp_new_i32(); read_neon_element32(tmp, a->vn, 1, MO_32); widenfn(rn1_64, tmp); - tcg_temp_free_i32(tmp); } if (src2_mop >= 0) { read_neon_element64(rm_64, a->vm, 1, src2_mop); @@ -1876,7 +1844,6 @@ static bool do_prewiden_3d(DisasContext *s, arg_3diff *a, TCGv_i32 tmp = tcg_temp_new_i32(); read_neon_element32(tmp, a->vm, 1, MO_32); widenfn(rm_64, tmp); - tcg_temp_free_i32(tmp); } write_neon_element64(rn0_64, a->vd, 0, MO_64); @@ -1884,10 +1851,6 @@ static bool do_prewiden_3d(DisasContext *s, arg_3diff *a, opfn(rn1_64, rn1_64, rm_64); write_neon_element64(rn1_64, a->vd, 1, MO_64); - tcg_temp_free_i64(rn0_64); - tcg_temp_free_i64(rn1_64); - tcg_temp_free_i64(rm_64); - return true; } @@ -1972,11 +1935,6 @@ static bool do_narrow_3d(DisasContext *s, arg_3diff *a, write_neon_element32(rd0, a->vd, 0, MO_32); write_neon_element32(rd1, a->vd, 1, MO_32); - tcg_temp_free_i32(rd0); - tcg_temp_free_i32(rd1); - tcg_temp_free_i64(rn_64); - tcg_temp_free_i64(rm_64); - return true; } @@ -2057,8 +2015,6 @@ static bool do_long_3d(DisasContext *s, arg_3diff *a, read_neon_element32(rn, a->vn, 1, MO_32); read_neon_element32(rm, a->vm, 1, MO_32); opfn(rd1, rn, rm); - tcg_temp_free_i32(rn); - tcg_temp_free_i32(rm); /* Don't store results until after all loads: they might overlap */ if (accfn) { @@ -2067,13 +2023,10 @@ static bool do_long_3d(DisasContext *s, arg_3diff *a, accfn(rd0, tmp, rd0); read_neon_element64(tmp, a->vd, 1, MO_64); accfn(rd1, tmp, rd1); - tcg_temp_free_i64(tmp); } write_neon_element64(rd0, a->vd, 0, MO_64); write_neon_element64(rd1, a->vd, 1, MO_64); - tcg_temp_free_i64(rd0); - tcg_temp_free_i64(rd1); return true; } @@ -2145,9 +2098,6 @@ static void gen_mull_s32(TCGv_i64 rd, TCGv_i32 rn, TCGv_i32 rm) tcg_gen_muls2_i32(lo, hi, rn, rm); tcg_gen_concat_i32_i64(rd, lo, hi); - - tcg_temp_free_i32(lo); - tcg_temp_free_i32(hi); } static void gen_mull_u32(TCGv_i64 rd, TCGv_i32 rn, TCGv_i32 rm) @@ -2157,9 +2107,6 @@ static void gen_mull_u32(TCGv_i64 rd, TCGv_i32 rn, TCGv_i32 rm) tcg_gen_mulu2_i32(lo, hi, rn, rm); tcg_gen_concat_i32_i64(rd, lo, hi); - - tcg_temp_free_i32(lo); - tcg_temp_free_i32(hi); } static bool trans_VMULL_S_3d(DisasContext *s, arg_3diff *a) @@ -2212,13 +2159,13 @@ DO_VMLAL(VMLSL_U,mull_u,sub) static void gen_VQDMULL_16(TCGv_i64 rd, TCGv_i32 rn, TCGv_i32 rm) { gen_helper_neon_mull_s16(rd, rn, rm); - gen_helper_neon_addl_saturate_s32(rd, cpu_env, rd, rd); + gen_helper_neon_addl_saturate_s32(rd, tcg_env, rd, rd); } static void gen_VQDMULL_32(TCGv_i64 rd, TCGv_i32 rn, TCGv_i32 rm) { gen_mull_s32(rd, rn, rm); - gen_helper_neon_addl_saturate_s64(rd, cpu_env, rd, rd); + gen_helper_neon_addl_saturate_s64(rd, tcg_env, rd, rd); } static bool trans_VQDMULL_3d(DisasContext *s, arg_3diff *a) @@ -2235,12 +2182,12 @@ static bool trans_VQDMULL_3d(DisasContext *s, arg_3diff *a) static void gen_VQDMLAL_acc_16(TCGv_i64 rd, TCGv_i64 rn, TCGv_i64 rm) { - gen_helper_neon_addl_saturate_s32(rd, cpu_env, rn, rm); + gen_helper_neon_addl_saturate_s32(rd, tcg_env, rn, rm); } static void gen_VQDMLAL_acc_32(TCGv_i64 rd, TCGv_i64 rn, TCGv_i64 rm) { - gen_helper_neon_addl_saturate_s64(rd, cpu_env, rn, rm); + gen_helper_neon_addl_saturate_s64(rd, tcg_env, rn, rm); } static bool trans_VQDMLAL_3d(DisasContext *s, arg_3diff *a) @@ -2264,13 +2211,13 @@ static bool trans_VQDMLAL_3d(DisasContext *s, arg_3diff *a) static void gen_VQDMLSL_acc_16(TCGv_i64 rd, TCGv_i64 rn, TCGv_i64 rm) { gen_helper_neon_negl_u32(rm, rm); - gen_helper_neon_addl_saturate_s32(rd, cpu_env, rn, rm); + gen_helper_neon_addl_saturate_s32(rd, tcg_env, rn, rm); } static void gen_VQDMLSL_acc_32(TCGv_i64 rd, TCGv_i64 rn, TCGv_i64 rm) { tcg_gen_neg_i64(rm, rm); - gen_helper_neon_addl_saturate_s64(rd, cpu_env, rn, rm); + gen_helper_neon_addl_saturate_s64(rd, tcg_env, rn, rm); } static bool trans_VQDMLSL_3d(DisasContext *s, arg_3diff *a) @@ -2340,7 +2287,6 @@ static void gen_neon_dup_low16(TCGv_i32 var) tcg_gen_ext16u_i32(var, var); tcg_gen_shli_i32(tmp, var, 16); tcg_gen_or_i32(var, var, tmp); - tcg_temp_free_i32(tmp); } static void gen_neon_dup_high16(TCGv_i32 var) @@ -2349,7 +2295,6 @@ static void gen_neon_dup_high16(TCGv_i32 var) tcg_gen_andi_i32(var, var, 0xffff0000); tcg_gen_shri_i32(tmp, var, 16); tcg_gen_or_i32(var, var, tmp); - tcg_temp_free_i32(tmp); } static inline TCGv_i32 neon_get_scalar(int size, int reg) @@ -2413,12 +2358,9 @@ static bool do_2scalar(DisasContext *s, arg_2scalar *a, TCGv_i32 rd = tcg_temp_new_i32(); read_neon_element32(rd, a->vd, pass, MO_32); accfn(tmp, rd, tmp); - tcg_temp_free_i32(rd); } write_neon_element32(tmp, a->vd, pass, MO_32); } - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(scalar); return true; } @@ -2512,7 +2454,6 @@ static bool do_2scalar_fp_vec(DisasContext *s, arg_2scalar *a, fpstatus = fpstatus_ptr(a->size == 1 ? FPST_STD_F16 : FPST_STD); tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, fpstatus, vec_size, vec_size, idx, fn); - tcg_temp_free_ptr(fpstatus); return true; } @@ -2609,13 +2550,9 @@ static bool do_vqrdmlah_2sc(DisasContext *s, arg_2scalar *a, for (pass = 0; pass < (a->q ? 4 : 2); pass++) { read_neon_element32(rn, a->vn, pass, MO_32); read_neon_element32(rd, a->vd, pass, MO_32); - opfn(rd, cpu_env, rn, scalar, rd); + opfn(rd, tcg_env, rn, scalar, rd); write_neon_element32(rd, a->vd, pass, MO_32); } - tcg_temp_free_i32(rn); - tcg_temp_free_i32(rd); - tcg_temp_free_i32(scalar); - return true; } @@ -2688,8 +2625,6 @@ static bool do_2scalar_long(DisasContext *s, arg_2scalar *a, read_neon_element32(rn, a->vn, 1, MO_32); rn1_64 = tcg_temp_new_i64(); opfn(rn1_64, rn, scalar); - tcg_temp_free_i32(rn); - tcg_temp_free_i32(scalar); if (accfn) { TCGv_i64 t64 = tcg_temp_new_i64(); @@ -2697,13 +2632,10 @@ static bool do_2scalar_long(DisasContext *s, arg_2scalar *a, accfn(rn0_64, t64, rn0_64); read_neon_element64(t64, a->vd, 1, MO_64); accfn(rn1_64, t64, rn1_64); - tcg_temp_free_i64(t64); } write_neon_element64(rn0_64, a->vd, 0, MO_64); write_neon_element64(rn1_64, a->vd, 1, MO_64); - tcg_temp_free_i64(rn0_64); - tcg_temp_free_i64(rn1_64); return true; } @@ -2838,10 +2770,6 @@ static bool trans_VEXT(DisasContext *s, arg_VEXT *a) read_neon_element64(left, a->vm, 0, MO_64); tcg_gen_extract2_i64(dest, right, left, a->imm * 8); write_neon_element64(dest, a->vd, 0, MO_64); - - tcg_temp_free_i64(left); - tcg_temp_free_i64(right); - tcg_temp_free_i64(dest); } else { /* Extract 128 bits from */ TCGv_i64 left, middle, right, destleft, destright; @@ -2868,12 +2796,6 @@ static bool trans_VEXT(DisasContext *s, arg_VEXT *a) write_neon_element64(destright, a->vd, 0, MO_64); write_neon_element64(destleft, a->vd, 1, MO_64); - - tcg_temp_free_i64(destright); - tcg_temp_free_i64(destleft); - tcg_temp_free_i64(right); - tcg_temp_free_i64(middle); - tcg_temp_free_i64(left); } return true; } @@ -2915,11 +2837,8 @@ static bool trans_VTBL(DisasContext *s, arg_VTBL *a) val = tcg_temp_new_i64(); read_neon_element64(val, a->vm, 0, MO_64); - gen_helper_neon_tbl(val, cpu_env, desc, val, def); + gen_helper_neon_tbl(val, tcg_env, desc, val, def); write_neon_element64(val, a->vd, 0, MO_64); - - tcg_temp_free_i64(def); - tcg_temp_free_i64(val); return true; } @@ -2998,9 +2917,6 @@ static bool trans_VREV64(DisasContext *s, arg_VREV64 *a) write_neon_element32(tmp[1], a->vd, pass * 2, MO_32); write_neon_element32(tmp[0], a->vd, pass * 2 + 1, MO_32); } - - tcg_temp_free_i32(tmp[0]); - tcg_temp_free_i32(tmp[1]); return true; } @@ -3051,20 +2967,15 @@ static bool do_2misc_pairwise(DisasContext *s, arg_2misc *a, widenfn(rm0_64, tmp); read_neon_element32(tmp, a->vm, pass * 2 + 1, MO_32); widenfn(rm1_64, tmp); - tcg_temp_free_i32(tmp); opfn(rd_64, rm0_64, rm1_64); - tcg_temp_free_i64(rm0_64); - tcg_temp_free_i64(rm1_64); if (accfn) { TCGv_i64 tmp64 = tcg_temp_new_i64(); read_neon_element64(tmp64, a->vd, pass, MO_64); accfn(rd_64, tmp64, rd_64); - tcg_temp_free_i64(tmp64); } write_neon_element64(rd_64, a->vd, pass, MO_64); - tcg_temp_free_i64(rd_64); } return true; } @@ -3188,8 +3099,6 @@ static bool do_zip_uzp(DisasContext *s, arg_2misc *a, pd = vfp_reg_ptr(true, a->vd); pm = vfp_reg_ptr(true, a->vm); fn(pd, pm); - tcg_temp_free_ptr(pd); - tcg_temp_free_ptr(pm); return true; } @@ -3262,14 +3171,11 @@ static bool do_vmovn(DisasContext *s, arg_2misc *a, rd1 = tcg_temp_new_i32(); read_neon_element64(rm, a->vm, 0, MO_64); - narrowfn(rd0, cpu_env, rm); + narrowfn(rd0, tcg_env, rm); read_neon_element64(rm, a->vm, 1, MO_64); - narrowfn(rd1, cpu_env, rm); + narrowfn(rd1, tcg_env, rm); write_neon_element32(rd0, a->vd, 0, MO_32); write_neon_element32(rd1, a->vd, 1, MO_32); - tcg_temp_free_i32(rd0); - tcg_temp_free_i32(rd1); - tcg_temp_free_i64(rm); return true; } @@ -3337,10 +3243,6 @@ static bool trans_VSHLL(DisasContext *s, arg_2misc *a) widenfn(rd, rm1); tcg_gen_shli_i64(rd, rd, 8 << a->size); write_neon_element64(rd, a->vd, 1, MO_64); - - tcg_temp_free_i64(rd); - tcg_temp_free_i32(rm0); - tcg_temp_free_i32(rm1); return true; } @@ -3381,11 +3283,6 @@ static bool trans_VCVT_B16_F32(DisasContext *s, arg_2misc *a) write_neon_element32(dst0, a->vd, 0, MO_32); write_neon_element32(dst1, a->vd, 1, MO_32); - - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(dst0); - tcg_temp_free_i32(dst1); - tcg_temp_free_ptr(fpst); return true; } @@ -3428,16 +3325,10 @@ static bool trans_VCVT_F16_F32(DisasContext *s, arg_2misc *a) tmp3 = tcg_temp_new_i32(); read_neon_element32(tmp3, a->vm, 3, MO_32); write_neon_element32(tmp2, a->vd, 0, MO_32); - tcg_temp_free_i32(tmp2); gen_helper_vfp_fcvt_f32_to_f16(tmp3, tmp3, fpst, ahp); tcg_gen_shli_i32(tmp3, tmp3, 16); tcg_gen_or_i32(tmp3, tmp3, tmp); write_neon_element32(tmp3, a->vd, 1, MO_32); - tcg_temp_free_i32(tmp3); - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(ahp); - tcg_temp_free_ptr(fpst); - return true; } @@ -3478,18 +3369,12 @@ static bool trans_VCVT_F32_F16(DisasContext *s, arg_2misc *a) tcg_gen_shri_i32(tmp, tmp, 16); gen_helper_vfp_fcvt_f16_to_f32(tmp, tmp, fpst, ahp); write_neon_element32(tmp, a->vd, 1, MO_32); - tcg_temp_free_i32(tmp); tcg_gen_ext16u_i32(tmp3, tmp2); gen_helper_vfp_fcvt_f16_to_f32(tmp3, tmp3, fpst, ahp); write_neon_element32(tmp3, a->vd, 2, MO_32); - tcg_temp_free_i32(tmp3); tcg_gen_shri_i32(tmp2, tmp2, 16); gen_helper_vfp_fcvt_f16_to_f32(tmp2, tmp2, fpst, ahp); write_neon_element32(tmp2, a->vd, 3, MO_32); - tcg_temp_free_i32(tmp2); - tcg_temp_free_i32(ahp); - tcg_temp_free_ptr(fpst); - return true; } @@ -3566,9 +3451,9 @@ static bool trans_VMVN(DisasContext *s, arg_2misc *a) } WRAP_2M_3_OOL_FN(gen_AESE, gen_helper_crypto_aese, 0) -WRAP_2M_3_OOL_FN(gen_AESD, gen_helper_crypto_aese, 1) +WRAP_2M_3_OOL_FN(gen_AESD, gen_helper_crypto_aesd, 0) WRAP_2M_2_OOL_FN(gen_AESMC, gen_helper_crypto_aesmc, 0) -WRAP_2M_2_OOL_FN(gen_AESIMC, gen_helper_crypto_aesmc, 1) +WRAP_2M_2_OOL_FN(gen_AESIMC, gen_helper_crypto_aesimc, 0) WRAP_2M_2_OOL_FN(gen_SHA1H, gen_helper_crypto_sha1h, 0) WRAP_2M_2_OOL_FN(gen_SHA1SU1, gen_helper_crypto_sha1su1, 0) WRAP_2M_2_OOL_FN(gen_SHA256SU0, gen_helper_crypto_sha256su0, 0) @@ -3624,8 +3509,6 @@ static bool do_2misc(DisasContext *s, arg_2misc *a, NeonGenOneOpFn *fn) fn(tmp, tmp); write_neon_element32(tmp, a->vd, pass, MO_32); } - tcg_temp_free_i32(tmp); - return true; } @@ -3742,7 +3625,7 @@ static bool trans_VRSQRTE(DisasContext *s, arg_2misc *a) #define WRAP_1OP_ENV_FN(WRAPNAME, FUNC) \ static void WRAPNAME(TCGv_i32 d, TCGv_i32 m) \ { \ - FUNC(d, cpu_env, m); \ + FUNC(d, tcg_env, m); \ } WRAP_1OP_ENV_FN(gen_VQABS_s8, gen_helper_neon_qabs_s8) @@ -3786,7 +3669,6 @@ static bool trans_VQNEG(DisasContext *s, arg_2misc *a) fpst = fpstatus_ptr(vece == MO_16 ? FPST_STD_F16 : FPST_STD); \ tcg_gen_gvec_2_ptr(rd_ofs, rm_ofs, fpst, oprsz, maxsz, 0, \ fns[vece]); \ - tcg_temp_free_ptr(fpst); \ } \ static bool trans_##INSN(DisasContext *s, arg_2misc *a) \ { \ @@ -3837,7 +3719,6 @@ static bool trans_VRINTX(DisasContext *s, arg_2misc *a) fpst = fpstatus_ptr(vece == 1 ? FPST_STD_F16 : FPST_STD); \ tcg_gen_gvec_2_ptr(rd_ofs, rm_ofs, fpst, oprsz, maxsz, \ arm_rmode_to_sf(RMODE), fns[vece]); \ - tcg_temp_free_ptr(fpst); \ } \ static bool trans_##INSN(DisasContext *s, arg_2misc *a) \ { \ @@ -3904,11 +3785,9 @@ static bool trans_VSWP(DisasContext *s, arg_2misc *a) write_neon_element64(rm, a->vd, pass, MO_64); write_neon_element64(rd, a->vm, pass, MO_64); } - tcg_temp_free_i64(rm); - tcg_temp_free_i64(rd); - return true; } + static void gen_neon_trn_u8(TCGv_i32 t0, TCGv_i32 t1) { TCGv_i32 rd, tmp; @@ -3926,9 +3805,6 @@ static void gen_neon_trn_u8(TCGv_i32 t0, TCGv_i32 t1) tcg_gen_andi_i32(tmp, t0, 0xff00ff00); tcg_gen_or_i32(t1, t1, tmp); tcg_gen_mov_i32(t0, rd); - - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(rd); } static void gen_neon_trn_u16(TCGv_i32 t0, TCGv_i32 t1) @@ -3945,9 +3821,6 @@ static void gen_neon_trn_u16(TCGv_i32 t0, TCGv_i32 t1) tcg_gen_andi_i32(tmp, t0, 0xffff0000); tcg_gen_or_i32(t1, t1, tmp); tcg_gen_mov_i32(t0, rd); - - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(rd); } static bool trans_VTRN(DisasContext *s, arg_2misc *a) @@ -3999,8 +3872,6 @@ static bool trans_VTRN(DisasContext *s, arg_2misc *a) write_neon_element32(tmp, a->vd, pass, MO_32); } } - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(tmp2); return true; } diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c new file mode 100644 index 0000000000..46c7fce8b4 --- /dev/null +++ b/target/arm/tcg/translate-sme.c @@ -0,0 +1,343 @@ +/* + * AArch64 SME translation + * + * Copyright (c) 2022 Linaro, Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "translate.h" +#include "translate-a64.h" + +/* + * Include the generated decoder. + */ + +#include "decode-sme.c.inc" + + +/* + * Resolve tile.size[index] to a host pointer, where tile and index + * are always decoded together, dependent on the element size. + */ +static TCGv_ptr get_tile_rowcol(DisasContext *s, int esz, int rs, + int tile_index, bool vertical) +{ + int tile = tile_index >> (4 - esz); + int index = esz == MO_128 ? 0 : extract32(tile_index, 0, 4 - esz); + int pos, len, offset; + TCGv_i32 tmp; + TCGv_ptr addr; + + /* Compute the final index, which is Rs+imm. */ + tmp = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(tmp, cpu_reg(s, rs)); + tcg_gen_addi_i32(tmp, tmp, index); + + /* Prepare a power-of-two modulo via extraction of @len bits. */ + len = ctz32(streaming_vec_reg_size(s)) - esz; + + if (vertical) { + /* + * Compute the byte offset of the index within the tile: + * (index % (svl / size)) * size + * = (index % (svl >> esz)) << esz + * Perform the power-of-two modulo via extraction of the low @len bits. + * Perform the multiply by shifting left by @pos bits. + * Perform these operations simultaneously via deposit into zero. + */ + pos = esz; + tcg_gen_deposit_z_i32(tmp, tmp, pos, len); + + /* + * For big-endian, adjust the indexed column byte offset within + * the uint64_t host words that make up env->zarray[]. + */ + if (HOST_BIG_ENDIAN && esz < MO_64) { + tcg_gen_xori_i32(tmp, tmp, 8 - (1 << esz)); + } + } else { + /* + * Compute the byte offset of the index within the tile: + * (index % (svl / size)) * (size * sizeof(row)) + * = (index % (svl >> esz)) << (esz + log2(sizeof(row))) + */ + pos = esz + ctz32(sizeof(ARMVectorReg)); + tcg_gen_deposit_z_i32(tmp, tmp, pos, len); + + /* Row slices are always aligned and need no endian adjustment. */ + } + + /* The tile byte offset within env->zarray is the row. */ + offset = tile * sizeof(ARMVectorReg); + + /* Include the byte offset of zarray to make this relative to env. */ + offset += offsetof(CPUARMState, zarray); + tcg_gen_addi_i32(tmp, tmp, offset); + + /* Add the byte offset to env to produce the final pointer. */ + addr = tcg_temp_new_ptr(); + tcg_gen_ext_i32_ptr(addr, tmp); + tcg_gen_add_ptr(addr, addr, tcg_env); + + return addr; +} + +/* + * Resolve tile.size[0] to a host pointer. + * Used by e.g. outer product insns where we require the entire tile. + */ +static TCGv_ptr get_tile(DisasContext *s, int esz, int tile) +{ + TCGv_ptr addr = tcg_temp_new_ptr(); + int offset; + + offset = tile * sizeof(ARMVectorReg) + offsetof(CPUARMState, zarray); + + tcg_gen_addi_ptr(addr, tcg_env, offset); + return addr; +} + +static bool trans_ZERO(DisasContext *s, arg_ZERO *a) +{ + if (!dc_isar_feature(aa64_sme, s)) { + return false; + } + if (sme_za_enabled_check(s)) { + gen_helper_sme_zero(tcg_env, tcg_constant_i32(a->imm), + tcg_constant_i32(streaming_vec_reg_size(s))); + } + return true; +} + +static bool trans_MOVA(DisasContext *s, arg_MOVA *a) +{ + static gen_helper_gvec_4 * const h_fns[5] = { + gen_helper_sve_sel_zpzz_b, gen_helper_sve_sel_zpzz_h, + gen_helper_sve_sel_zpzz_s, gen_helper_sve_sel_zpzz_d, + gen_helper_sve_sel_zpzz_q + }; + static gen_helper_gvec_3 * const cz_fns[5] = { + gen_helper_sme_mova_cz_b, gen_helper_sme_mova_cz_h, + gen_helper_sme_mova_cz_s, gen_helper_sme_mova_cz_d, + gen_helper_sme_mova_cz_q, + }; + static gen_helper_gvec_3 * const zc_fns[5] = { + gen_helper_sme_mova_zc_b, gen_helper_sme_mova_zc_h, + gen_helper_sme_mova_zc_s, gen_helper_sme_mova_zc_d, + gen_helper_sme_mova_zc_q, + }; + + TCGv_ptr t_za, t_zr, t_pg; + TCGv_i32 t_desc; + int svl; + + if (!dc_isar_feature(aa64_sme, s)) { + return false; + } + if (!sme_smza_enabled_check(s)) { + return true; + } + + t_za = get_tile_rowcol(s, a->esz, a->rs, a->za_imm, a->v); + t_zr = vec_full_reg_ptr(s, a->zr); + t_pg = pred_full_reg_ptr(s, a->pg); + + svl = streaming_vec_reg_size(s); + t_desc = tcg_constant_i32(simd_desc(svl, svl, 0)); + + if (a->v) { + /* Vertical slice -- use sme mova helpers. */ + if (a->to_vec) { + zc_fns[a->esz](t_zr, t_za, t_pg, t_desc); + } else { + cz_fns[a->esz](t_za, t_zr, t_pg, t_desc); + } + } else { + /* Horizontal slice -- reuse sve sel helpers. */ + if (a->to_vec) { + h_fns[a->esz](t_zr, t_za, t_zr, t_pg, t_desc); + } else { + h_fns[a->esz](t_za, t_zr, t_za, t_pg, t_desc); + } + } + return true; +} + +static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) +{ + typedef void GenLdSt1(TCGv_env, TCGv_ptr, TCGv_ptr, TCGv, TCGv_i32); + + /* + * Indexed by [esz][be][v][mte][st], which is (except for load/store) + * also the order in which the elements appear in the function names, + * and so how we must concatenate the pieces. + */ + +#define FN_LS(F) { gen_helper_sme_ld1##F, gen_helper_sme_st1##F } +#define FN_MTE(F) { FN_LS(F), FN_LS(F##_mte) } +#define FN_HV(F) { FN_MTE(F##_h), FN_MTE(F##_v) } +#define FN_END(L, B) { FN_HV(L), FN_HV(B) } + + static GenLdSt1 * const fns[5][2][2][2][2] = { + FN_END(b, b), + FN_END(h_le, h_be), + FN_END(s_le, s_be), + FN_END(d_le, d_be), + FN_END(q_le, q_be), + }; + +#undef FN_LS +#undef FN_MTE +#undef FN_HV +#undef FN_END + + TCGv_ptr t_za, t_pg; + TCGv_i64 addr; + uint32_t desc; + bool be = s->be_data == MO_BE; + bool mte = s->mte_active[0]; + + if (!dc_isar_feature(aa64_sme, s)) { + return false; + } + if (!sme_smza_enabled_check(s)) { + return true; + } + + t_za = get_tile_rowcol(s, a->esz, a->rs, a->za_imm, a->v); + t_pg = pred_full_reg_ptr(s, a->pg); + addr = tcg_temp_new_i64(); + + tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), a->esz); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); + + if (!mte) { + addr = clean_data_tbi(s, addr); + } + + desc = make_svemte_desc(s, streaming_vec_reg_size(s), 1, a->esz, a->st, 0); + + fns[a->esz][be][a->v][mte][a->st](tcg_env, t_za, t_pg, addr, + tcg_constant_i32(desc)); + return true; +} + +typedef void GenLdStR(DisasContext *, TCGv_ptr, int, int, int, int); + +static bool do_ldst_r(DisasContext *s, arg_ldstr *a, GenLdStR *fn) +{ + int svl = streaming_vec_reg_size(s); + int imm = a->imm; + TCGv_ptr base; + + if (!sme_za_enabled_check(s)) { + return true; + } + + /* ZA[n] equates to ZA0H.B[n]. */ + base = get_tile_rowcol(s, MO_8, a->rv, imm, false); + + fn(s, base, 0, svl, a->rn, imm * svl); + return true; +} + +TRANS_FEAT(LDR, aa64_sme, do_ldst_r, a, gen_sve_ldr) +TRANS_FEAT(STR, aa64_sme, do_ldst_r, a, gen_sve_str) + +static bool do_adda(DisasContext *s, arg_adda *a, MemOp esz, + gen_helper_gvec_4 *fn) +{ + int svl = streaming_vec_reg_size(s); + uint32_t desc = simd_desc(svl, svl, 0); + TCGv_ptr za, zn, pn, pm; + + if (!sme_smza_enabled_check(s)) { + return true; + } + + za = get_tile(s, esz, a->zad); + zn = vec_full_reg_ptr(s, a->zn); + pn = pred_full_reg_ptr(s, a->pn); + pm = pred_full_reg_ptr(s, a->pm); + + fn(za, zn, pn, pm, tcg_constant_i32(desc)); + return true; +} + +TRANS_FEAT(ADDHA_s, aa64_sme, do_adda, a, MO_32, gen_helper_sme_addha_s) +TRANS_FEAT(ADDVA_s, aa64_sme, do_adda, a, MO_32, gen_helper_sme_addva_s) +TRANS_FEAT(ADDHA_d, aa64_sme_i16i64, do_adda, a, MO_64, gen_helper_sme_addha_d) +TRANS_FEAT(ADDVA_d, aa64_sme_i16i64, do_adda, a, MO_64, gen_helper_sme_addva_d) + +static bool do_outprod(DisasContext *s, arg_op *a, MemOp esz, + gen_helper_gvec_5 *fn) +{ + int svl = streaming_vec_reg_size(s); + uint32_t desc = simd_desc(svl, svl, a->sub); + TCGv_ptr za, zn, zm, pn, pm; + + if (!sme_smza_enabled_check(s)) { + return true; + } + + za = get_tile(s, esz, a->zad); + zn = vec_full_reg_ptr(s, a->zn); + zm = vec_full_reg_ptr(s, a->zm); + pn = pred_full_reg_ptr(s, a->pn); + pm = pred_full_reg_ptr(s, a->pm); + + fn(za, zn, zm, pn, pm, tcg_constant_i32(desc)); + return true; +} + +static bool do_outprod_fpst(DisasContext *s, arg_op *a, MemOp esz, + gen_helper_gvec_5_ptr *fn) +{ + int svl = streaming_vec_reg_size(s); + uint32_t desc = simd_desc(svl, svl, a->sub); + TCGv_ptr za, zn, zm, pn, pm, fpst; + + if (!sme_smza_enabled_check(s)) { + return true; + } + + za = get_tile(s, esz, a->zad); + zn = vec_full_reg_ptr(s, a->zn); + zm = vec_full_reg_ptr(s, a->zm); + pn = pred_full_reg_ptr(s, a->pn); + pm = pred_full_reg_ptr(s, a->pm); + fpst = fpstatus_ptr(FPST_FPCR); + + fn(za, zn, zm, pn, pm, fpst, tcg_constant_i32(desc)); + return true; +} + +TRANS_FEAT(FMOPA_h, aa64_sme, do_outprod_fpst, a, MO_32, gen_helper_sme_fmopa_h) +TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, MO_32, gen_helper_sme_fmopa_s) +TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, MO_64, gen_helper_sme_fmopa_d) + +/* TODO: FEAT_EBF16 */ +TRANS_FEAT(BFMOPA, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_bfmopa) + +TRANS_FEAT(SMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_smopa_s) +TRANS_FEAT(UMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_umopa_s) +TRANS_FEAT(SUMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_sumopa_s) +TRANS_FEAT(USMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_usmopa_s) + +TRANS_FEAT(SMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_smopa_d) +TRANS_FEAT(UMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_umopa_d) +TRANS_FEAT(SUMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_sumopa_d) +TRANS_FEAT(USMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_usmopa_d) diff --git a/target/arm/translate-sve.c b/target/arm/tcg/translate-sve.c similarity index 91% rename from target/arm/translate-sve.c rename to target/arm/tcg/translate-sve.c index 67761bf2cc..ada05aa530 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -18,18 +18,7 @@ */ #include "qemu/osdep.h" -#include "cpu.h" -#include "exec/exec-all.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" -#include "internals.h" -#include "exec/helper-proto.h" -#include "exec/helper-gen.h" -#include "exec/log.h" #include "translate-a64.h" #include "fpu/softfloat.h" @@ -100,42 +89,6 @@ static inline int msz_dtype(DisasContext *s, int msz) * Implement all of the translator functions referenced by the decoder. */ -/* Return the offset info CPUARMState of the predicate vector register Pn. - * Note for this purpose, FFR is P16. - */ -static inline int pred_full_reg_offset(DisasContext *s, int regno) -{ - return offsetof(CPUARMState, vfp.pregs[regno]); -} - -/* Return the byte size of the whole predicate register, VL / 64. */ -static inline int pred_full_reg_size(DisasContext *s) -{ - return s->vl >> 3; -} - -/* Round up the size of a register to a size allowed by - * the tcg vector infrastructure. Any operation which uses this - * size may assume that the bits above pred_full_reg_size are zero, - * and must leave them the same way. - * - * Note that this is not needed for the vector registers as they - * are always properly sized for tcg vectors. - */ -static int size_for_gvec(int size) -{ - if (size <= 8) { - return 8; - } else { - return QEMU_ALIGN_UP(size, 16); - } -} - -static int pred_gvec_reg_size(DisasContext *s) -{ - return size_for_gvec(pred_full_reg_size(s)); -} - /* Invoke an out-of-line helper on 2 Zregs. */ static bool gen_gvec_ool_zz(DisasContext *s, gen_helper_gvec_2 *fn, int rd, int rn, int data) @@ -166,7 +119,6 @@ static bool gen_gvec_fpst_zz(DisasContext *s, gen_helper_gvec_2_ptr *fn, tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), status, vsz, vsz, data, fn); - tcg_temp_free_ptr(status); } return true; } @@ -217,8 +169,6 @@ static bool gen_gvec_fpst_zzz(DisasContext *s, gen_helper_gvec_3_ptr *fn, vec_full_reg_offset(s, rn), vec_full_reg_offset(s, rm), status, vsz, vsz, data, fn); - - tcg_temp_free_ptr(status); } return true; } @@ -285,7 +235,6 @@ static bool gen_gvec_fpst_zzzz(DisasContext *s, gen_helper_gvec_4_ptr *fn, { TCGv_ptr status = fpstatus_ptr(flavour); bool ret = gen_gvec_ptr_zzzz(s, fn, rd, rn, rm, ra, data, status); - tcg_temp_free_ptr(status); return ret; } @@ -307,8 +256,6 @@ static bool gen_gvec_fpst_zzzzp(DisasContext *s, gen_helper_gvec_5_ptr *fn, vec_full_reg_offset(s, ra), pred_full_reg_offset(s, pg), status, vsz, vsz, data, fn); - - tcg_temp_free_ptr(status); } return true; } @@ -357,7 +304,6 @@ static bool gen_gvec_fpst_zzp(DisasContext *s, gen_helper_gvec_3_ptr *fn, vec_full_reg_offset(s, rn), pred_full_reg_offset(s, pg), status, vsz, vsz, data, fn); - tcg_temp_free_ptr(status); } return true; } @@ -410,7 +356,6 @@ static bool gen_gvec_fpst_zzzp(DisasContext *s, gen_helper_gvec_4_ptr *fn, vec_full_reg_offset(s, rm), pred_full_reg_offset(s, pg), status, vsz, vsz, data, fn); - tcg_temp_free_ptr(status); } return true; } @@ -544,7 +489,6 @@ static void do_predtest1(TCGv_i64 d, TCGv_i64 g) gen_helper_sve_predtest1(t, d, g); do_pred_flags(t); - tcg_temp_free_i32(t); } static void do_predtest(DisasContext *s, int dofs, int gofs, int words) @@ -553,21 +497,19 @@ static void do_predtest(DisasContext *s, int dofs, int gofs, int words) TCGv_ptr gptr = tcg_temp_new_ptr(); TCGv_i32 t = tcg_temp_new_i32(); - tcg_gen_addi_ptr(dptr, cpu_env, dofs); - tcg_gen_addi_ptr(gptr, cpu_env, gofs); + tcg_gen_addi_ptr(dptr, tcg_env, dofs); + tcg_gen_addi_ptr(gptr, tcg_env, gofs); gen_helper_sve_predtest(t, dptr, gptr, tcg_constant_i32(words)); - tcg_temp_free_ptr(dptr); - tcg_temp_free_ptr(gptr); do_pred_flags(t); - tcg_temp_free_i32(t); } /* For each element size, the bits within a predicate word that are active. */ -const uint64_t pred_esz_masks[4] = { +const uint64_t pred_esz_masks[5] = { 0xffffffffffffffffull, 0x5555555555555555ull, - 0x1111111111111111ull, 0x0101010101010101ull + 0x1111111111111111ull, 0x0101010101010101ull, + 0x0001000100010001ull, }; static bool trans_INVALID(DisasContext *s, arg_INVALID *a) @@ -596,7 +538,6 @@ static void gen_xar8_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh) tcg_gen_andi_i64(d, d, mask); tcg_gen_andi_i64(t, t, ~mask); tcg_gen_or_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_xar16_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh) @@ -610,7 +551,6 @@ static void gen_xar16_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh) tcg_gen_andi_i64(d, d, mask); tcg_gen_andi_i64(t, t, ~mask); tcg_gen_or_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_xar_i32(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, int32_t sh) @@ -1016,14 +956,11 @@ static bool do_vpz_ool(DisasContext *s, arg_rpr_esz *a, t_zn = tcg_temp_new_ptr(); t_pg = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, a->rn)); - tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, a->pg)); + tcg_gen_addi_ptr(t_zn, tcg_env, vec_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, a->pg)); fn(temp, t_zn, t_pg, desc); - tcg_temp_free_ptr(t_zn); - tcg_temp_free_ptr(t_pg); write_fp_dreg(s, a->rd, temp); - tcg_temp_free_i64(temp); return true; } @@ -1272,7 +1209,7 @@ static bool do_index(DisasContext *s, int esz, int rd, desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); t_zd = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(t_zd, cpu_env, vec_full_reg_offset(s, rd)); + tcg_gen_addi_ptr(t_zd, tcg_env, vec_full_reg_offset(s, rd)); if (esz == 3) { gen_helper_sve_index_d(t_zd, start, incr, desc); } else { @@ -1288,11 +1225,7 @@ static bool do_index(DisasContext *s, int esz, int rd, tcg_gen_extrl_i64_i32(s32, start); tcg_gen_extrl_i64_i32(i32, incr); fns[esz](t_zd, s32, i32, desc); - - tcg_temp_free_i32(s32); - tcg_temp_free_i32(i32); } - tcg_temp_free_ptr(t_zd); return true; } @@ -1322,6 +1255,19 @@ static bool trans_ADDVL(DisasContext *s, arg_ADDVL *a) return true; } +static bool trans_ADDSVL(DisasContext *s, arg_ADDSVL *a) +{ + if (!dc_isar_feature(aa64_sme, s)) { + return false; + } + if (sme_enabled_check(s)) { + TCGv_i64 rd = cpu_reg_sp(s, a->rd); + TCGv_i64 rn = cpu_reg_sp(s, a->rn); + tcg_gen_addi_i64(rd, rn, a->imm * streaming_vec_reg_size(s)); + } + return true; +} + static bool trans_ADDPL(DisasContext *s, arg_ADDPL *a) { if (!dc_isar_feature(aa64_sve, s)) { @@ -1335,6 +1281,19 @@ static bool trans_ADDPL(DisasContext *s, arg_ADDPL *a) return true; } +static bool trans_ADDSPL(DisasContext *s, arg_ADDSPL *a) +{ + if (!dc_isar_feature(aa64_sme, s)) { + return false; + } + if (sme_enabled_check(s)) { + TCGv_i64 rd = cpu_reg_sp(s, a->rd); + TCGv_i64 rn = cpu_reg_sp(s, a->rn); + tcg_gen_addi_i64(rd, rn, a->imm * streaming_pred_reg_size(s)); + } + return true; +} + static bool trans_RDVL(DisasContext *s, arg_RDVL *a) { if (!dc_isar_feature(aa64_sve, s)) { @@ -1347,6 +1306,18 @@ static bool trans_RDVL(DisasContext *s, arg_RDVL *a) return true; } +static bool trans_RDSVL(DisasContext *s, arg_RDSVL *a) +{ + if (!dc_isar_feature(aa64_sme, s)) { + return false; + } + if (sme_enabled_check(s)) { + TCGv_i64 reg = cpu_reg(s, a->rd); + tcg_gen_movi_i64(reg, a->imm * streaming_vec_reg_size(s)); + } + return true; +} + /* *** SVE Compute Vector Address Group */ @@ -1356,10 +1327,10 @@ static bool do_adr(DisasContext *s, arg_rrri *a, gen_helper_gvec_3 *fn) return gen_gvec_ool_zzz(s, fn, a->rd, a->rn, a->rm, a->imm); } -TRANS_FEAT(ADR_p32, aa64_sve, do_adr, a, gen_helper_sve_adr_p32) -TRANS_FEAT(ADR_p64, aa64_sve, do_adr, a, gen_helper_sve_adr_p64) -TRANS_FEAT(ADR_s32, aa64_sve, do_adr, a, gen_helper_sve_adr_s32) -TRANS_FEAT(ADR_u32, aa64_sve, do_adr, a, gen_helper_sve_adr_u32) +TRANS_FEAT_NONSTREAMING(ADR_p32, aa64_sve, do_adr, a, gen_helper_sve_adr_p32) +TRANS_FEAT_NONSTREAMING(ADR_p64, aa64_sve, do_adr, a, gen_helper_sve_adr_p64) +TRANS_FEAT_NONSTREAMING(ADR_s32, aa64_sve, do_adr, a, gen_helper_sve_adr_s32) +TRANS_FEAT_NONSTREAMING(ADR_u32, aa64_sve, do_adr, a, gen_helper_sve_adr_u32) /* *** SVE Integer Misc - Unpredicated Group @@ -1369,14 +1340,15 @@ static gen_helper_gvec_2 * const fexpa_fns[4] = { NULL, gen_helper_sve_fexpa_h, gen_helper_sve_fexpa_s, gen_helper_sve_fexpa_d, }; -TRANS_FEAT(FEXPA, aa64_sve, gen_gvec_ool_zz, - fexpa_fns[a->esz], a->rd, a->rn, 0) +TRANS_FEAT_NONSTREAMING(FEXPA, aa64_sve, gen_gvec_ool_zz, + fexpa_fns[a->esz], a->rd, a->rn, 0) static gen_helper_gvec_3 * const ftssel_fns[4] = { NULL, gen_helper_sve_ftssel_h, gen_helper_sve_ftssel_s, gen_helper_sve_ftssel_d, }; -TRANS_FEAT(FTSSEL, aa64_sve, gen_gvec_ool_arg_zzz, ftssel_fns[a->esz], a, 0) +TRANS_FEAT_NONSTREAMING(FTSSEL, aa64_sve, gen_gvec_ool_arg_zzz, + ftssel_fns[a->esz], a, 0) /* *** SVE Predicate Logical Operations Group @@ -1407,19 +1379,14 @@ static bool do_pppp_flags(DisasContext *s, arg_rprr_s *a, TCGv_i64 pm = tcg_temp_new_i64(); TCGv_i64 pg = tcg_temp_new_i64(); - tcg_gen_ld_i64(pn, cpu_env, nofs); - tcg_gen_ld_i64(pm, cpu_env, mofs); - tcg_gen_ld_i64(pg, cpu_env, gofs); + tcg_gen_ld_i64(pn, tcg_env, nofs); + tcg_gen_ld_i64(pm, tcg_env, mofs); + tcg_gen_ld_i64(pg, tcg_env, gofs); gvec_op->fni8(pd, pn, pm, pg); - tcg_gen_st_i64(pd, cpu_env, dofs); + tcg_gen_st_i64(pd, tcg_env, dofs); do_predtest1(pd, pg); - - tcg_temp_free_i64(pd); - tcg_temp_free_i64(pn); - tcg_temp_free_i64(pm); - tcg_temp_free_i64(pg); } else { /* The operation and flags generation is large. The computation * of the flags depends on the original contents of the guarding @@ -1687,12 +1654,9 @@ static bool trans_PTEST(DisasContext *s, arg_PTEST *a) TCGv_i64 pn = tcg_temp_new_i64(); TCGv_i64 pg = tcg_temp_new_i64(); - tcg_gen_ld_i64(pn, cpu_env, nofs); - tcg_gen_ld_i64(pg, cpu_env, gofs); + tcg_gen_ld_i64(pn, tcg_env, nofs); + tcg_gen_ld_i64(pg, tcg_env, gofs); do_predtest1(pn, pg); - - tcg_temp_free_i64(pn); - tcg_temp_free_i64(pg); } else { do_predtest(s, nofs, gofs, words); } @@ -1772,7 +1736,7 @@ static bool do_predset(DisasContext *s, int esz, int rd, int pat, bool setflag) t = tcg_temp_new_i64(); if (fullsz <= 64) { tcg_gen_movi_i64(t, lastword); - tcg_gen_st_i64(t, cpu_env, ofs); + tcg_gen_st_i64(t, tcg_env, ofs); goto done; } @@ -1791,23 +1755,21 @@ static bool do_predset(DisasContext *s, int esz, int rd, int pat, bool setflag) tcg_gen_movi_i64(t, word); for (i = 0; i < QEMU_ALIGN_DOWN(setsz, 8); i += 8) { - tcg_gen_st_i64(t, cpu_env, ofs + i); + tcg_gen_st_i64(t, tcg_env, ofs + i); } if (lastword != word) { tcg_gen_movi_i64(t, lastword); - tcg_gen_st_i64(t, cpu_env, ofs + i); + tcg_gen_st_i64(t, tcg_env, ofs + i); i += 8; } if (i < fullsz) { tcg_gen_movi_i64(t, 0); for (; i < fullsz; i += 8) { - tcg_gen_st_i64(t, cpu_env, ofs + i); + tcg_gen_st_i64(t, tcg_env, ofs + i); } } done: - tcg_temp_free_i64(t); - /* PTRUES */ if (setflag) { tcg_gen_movi_i32(cpu_NF, -(word != 0)); @@ -1821,7 +1783,8 @@ static bool do_predset(DisasContext *s, int esz, int rd, int pat, bool setflag) TRANS_FEAT(PTRUE, aa64_sve, do_predset, a->esz, a->rd, a->pat, a->s) /* Note pat == 31 is #all, to set all elements. */ -TRANS_FEAT(SETFFR, aa64_sve, do_predset, 0, FFR_PRED_NUM, 31, false) +TRANS_FEAT_NONSTREAMING(SETFFR, aa64_sve, + do_predset, 0, FFR_PRED_NUM, 31, false) /* Note pat == 32 is #unimp, to set no elements. */ TRANS_FEAT(PFALSE, aa64_sve, do_predset, 0, a->rd, 32, false) @@ -1835,11 +1798,13 @@ static bool trans_RDFFR_p(DisasContext *s, arg_RDFFR_p *a) .rd = a->rd, .pg = a->pg, .s = a->s, .rn = FFR_PRED_NUM, .rm = FFR_PRED_NUM, }; + + s->is_nonstreaming = true; return trans_AND_pppp(s, &alt_a); } -TRANS_FEAT(RDFFR, aa64_sve, do_mov_p, a->rd, FFR_PRED_NUM) -TRANS_FEAT(WRFFR, aa64_sve, do_mov_p, FFR_PRED_NUM, a->rn) +TRANS_FEAT_NONSTREAMING(RDFFR, aa64_sve, do_mov_p, a->rd, FFR_PRED_NUM) +TRANS_FEAT_NONSTREAMING(WRFFR, aa64_sve, do_mov_p, FFR_PRED_NUM, a->rn) static bool do_pfirst_pnext(DisasContext *s, arg_rr_esz *a, void (*gen_fn)(TCGv_i32, TCGv_ptr, @@ -1857,16 +1822,13 @@ static bool do_pfirst_pnext(DisasContext *s, arg_rr_esz *a, desc = FIELD_DP32(desc, PREDDESC, OPRSZ, pred_full_reg_size(s)); desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); - tcg_gen_addi_ptr(t_pd, cpu_env, pred_full_reg_offset(s, a->rd)); - tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(t_pd, tcg_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, a->rn)); t = tcg_temp_new_i32(); gen_fn(t, t_pd, t_pg, tcg_constant_i32(desc)); - tcg_temp_free_ptr(t_pd); - tcg_temp_free_ptr(t_pg); do_pred_flags(t); - tcg_temp_free_i32(t); return true; } @@ -1879,7 +1841,7 @@ TRANS_FEAT(PNEXT, aa64_sve, do_pfirst_pnext, a, gen_helper_sve_pnext) /* Perform an inline saturating addition of a 32-bit value within * a 64-bit register. The second operand is known to be positive, - * which halves the comparisions we must perform to bound the result. + * which halves the comparisons we must perform to bound the result. */ static void do_sat_addsub_32(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) { @@ -1943,9 +1905,7 @@ static void do_sat_addsub_64(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) t2 = tcg_constant_i64(0); tcg_gen_movcond_i64(TCG_COND_LT, reg, t0, t2, t1, reg); } - tcg_temp_free_i64(t1); } - tcg_temp_free_i64(t0); } /* Similarly with a vector and a scalar operand. */ @@ -1959,8 +1919,8 @@ static void do_sat_addsub_vec(DisasContext *s, int esz, int rd, int rn, dptr = tcg_temp_new_ptr(); nptr = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(dptr, cpu_env, vec_full_reg_offset(s, rd)); - tcg_gen_addi_ptr(nptr, cpu_env, vec_full_reg_offset(s, rn)); + tcg_gen_addi_ptr(dptr, tcg_env, vec_full_reg_offset(s, rd)); + tcg_gen_addi_ptr(nptr, tcg_env, vec_full_reg_offset(s, rn)); desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); switch (esz) { @@ -1975,7 +1935,6 @@ static void do_sat_addsub_vec(DisasContext *s, int esz, int rd, int rn, } else { gen_helper_sve_sqaddi_b(dptr, nptr, t32, desc); } - tcg_temp_free_i32(t32); break; case MO_16: @@ -1989,7 +1948,6 @@ static void do_sat_addsub_vec(DisasContext *s, int esz, int rd, int rn, } else { gen_helper_sve_sqaddi_h(dptr, nptr, t32, desc); } - tcg_temp_free_i32(t32); break; case MO_32: @@ -2004,7 +1962,6 @@ static void do_sat_addsub_vec(DisasContext *s, int esz, int rd, int rn, } else { gen_helper_sve_sqaddi_s(dptr, nptr, t64, desc); } - tcg_temp_free_i64(t64); break; case MO_64: @@ -2018,7 +1975,6 @@ static void do_sat_addsub_vec(DisasContext *s, int esz, int rd, int rn, t64 = tcg_temp_new_i64(); tcg_gen_neg_i64(t64, val); gen_helper_sve_sqaddi_d(dptr, nptr, t64, desc); - tcg_temp_free_i64(t64); } else { gen_helper_sve_sqaddi_d(dptr, nptr, val, desc); } @@ -2027,9 +1983,6 @@ static void do_sat_addsub_vec(DisasContext *s, int esz, int rd, int rn, default: g_assert_not_reached(); } - - tcg_temp_free_ptr(dptr); - tcg_temp_free_ptr(nptr); } static bool trans_CNT_r(DisasContext *s, arg_CNT_r *a) @@ -2210,15 +2163,11 @@ static void do_cpy_m(DisasContext *s, int esz, int rd, int rn, int pg, TCGv_ptr t_zn = tcg_temp_new_ptr(); TCGv_ptr t_pg = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(t_zd, cpu_env, vec_full_reg_offset(s, rd)); - tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, rn)); - tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + tcg_gen_addi_ptr(t_zd, tcg_env, vec_full_reg_offset(s, rd)); + tcg_gen_addi_ptr(t_zn, tcg_env, vec_full_reg_offset(s, rn)); + tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, pg)); fns[esz](t_zd, t_zn, t_pg, val, desc); - - tcg_temp_free_ptr(t_zd); - tcg_temp_free_ptr(t_zn); - tcg_temp_free_ptr(t_pg); } static bool trans_FCPY(DisasContext *s, arg_FCPY *a) @@ -2361,13 +2310,10 @@ static void do_insr_i64(DisasContext *s, arg_rrr_esz *a, TCGv_i64 val) TCGv_ptr t_zd = tcg_temp_new_ptr(); TCGv_ptr t_zn = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(t_zd, cpu_env, vec_full_reg_offset(s, a->rd)); - tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(t_zd, tcg_env, vec_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(t_zn, tcg_env, vec_full_reg_offset(s, a->rn)); fns[a->esz](t_zd, t_zn, val, desc); - - tcg_temp_free_ptr(t_zd); - tcg_temp_free_ptr(t_zn); } static bool trans_INSR_f(DisasContext *s, arg_rrr_esz *a) @@ -2377,9 +2323,8 @@ static bool trans_INSR_f(DisasContext *s, arg_rrr_esz *a) } if (sve_access_check(s)) { TCGv_i64 t = tcg_temp_new_i64(); - tcg_gen_ld_i64(t, cpu_env, vec_reg_offset(s, a->rm, 0, MO_64)); + tcg_gen_ld_i64(t, tcg_env, vec_reg_offset(s, a->rm, 0, MO_64)); do_insr_i64(s, a, t); - tcg_temp_free_i64(t); } return true; } @@ -2464,15 +2409,11 @@ static bool do_perm_pred3(DisasContext *s, arg_rrr_esz *a, bool high_odd, desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); desc = FIELD_DP32(desc, PREDDESC, DATA, high_odd); - tcg_gen_addi_ptr(t_d, cpu_env, pred_full_reg_offset(s, a->rd)); - tcg_gen_addi_ptr(t_n, cpu_env, pred_full_reg_offset(s, a->rn)); - tcg_gen_addi_ptr(t_m, cpu_env, pred_full_reg_offset(s, a->rm)); + tcg_gen_addi_ptr(t_d, tcg_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(t_n, tcg_env, pred_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(t_m, tcg_env, pred_full_reg_offset(s, a->rm)); fn(t_d, t_n, t_m, tcg_constant_i32(desc)); - - tcg_temp_free_ptr(t_d); - tcg_temp_free_ptr(t_n); - tcg_temp_free_ptr(t_m); return true; } @@ -2488,17 +2429,14 @@ static bool do_perm_pred2(DisasContext *s, arg_rr_esz *a, bool high_odd, TCGv_ptr t_n = tcg_temp_new_ptr(); uint32_t desc = 0; - tcg_gen_addi_ptr(t_d, cpu_env, pred_full_reg_offset(s, a->rd)); - tcg_gen_addi_ptr(t_n, cpu_env, pred_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(t_d, tcg_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(t_n, tcg_env, pred_full_reg_offset(s, a->rn)); desc = FIELD_DP32(desc, PREDDESC, OPRSZ, vsz); desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); desc = FIELD_DP32(desc, PREDDESC, DATA, high_odd); fn(t_d, t_n, tcg_constant_i32(desc)); - - tcg_temp_free_ptr(t_d); - tcg_temp_free_ptr(t_n); return true; } @@ -2569,7 +2507,8 @@ TRANS_FEAT(TRN2_q, aa64_sve_f64mm, gen_gvec_ool_arg_zzz, static gen_helper_gvec_3 * const compact_fns[4] = { NULL, NULL, gen_helper_sve_compact_s, gen_helper_sve_compact_d }; -TRANS_FEAT(COMPACT, aa64_sve, gen_gvec_ool_arg_zpz, compact_fns[a->esz], a, 0) +TRANS_FEAT_NONSTREAMING(COMPACT, aa64_sve, gen_gvec_ool_arg_zpz, + compact_fns[a->esz], a, 0) /* Call the helper that computes the ARM LastActiveElement pseudocode * function, scaled by the element size. This includes the not found @@ -2586,11 +2525,9 @@ static void find_last_active(DisasContext *s, TCGv_i32 ret, int esz, int pg) desc = FIELD_DP32(desc, PREDDESC, OPRSZ, pred_full_reg_size(s)); desc = FIELD_DP32(desc, PREDDESC, ESZ, esz); - tcg_gen_addi_ptr(t_p, cpu_env, pred_full_reg_offset(s, pg)); + tcg_gen_addi_ptr(t_p, tcg_env, pred_full_reg_offset(s, pg)); gen_helper_sve_last_active_element(ret, t_p, tcg_constant_i32(desc)); - - tcg_temp_free_ptr(t_p); } /* Increment LAST to the offset of the next element in the vector, @@ -2653,7 +2590,6 @@ static TCGv_i64 load_last_active(DisasContext *s, TCGv_i32 last, int rm, int esz) { TCGv_ptr p = tcg_temp_new_ptr(); - TCGv_i64 r; /* Convert offset into vector into offset into ENV. * The final adjustment for the vector register base @@ -2666,12 +2602,9 @@ static TCGv_i64 load_last_active(DisasContext *s, TCGv_i32 last, } #endif tcg_gen_ext_i32_ptr(p, last); - tcg_gen_add_ptr(p, p, cpu_env); + tcg_gen_add_ptr(p, p, tcg_env); - r = load_esz(p, vec_full_reg_offset(s, rm), esz); - tcg_temp_free_ptr(p); - - return r; + return load_esz(p, vec_full_reg_offset(s, rm), esz); } /* Compute CLAST for a Zreg. */ @@ -2686,7 +2619,7 @@ static bool do_clast_vector(DisasContext *s, arg_rprr_esz *a, bool before) return true; } - last = tcg_temp_local_new_i32(); + last = tcg_temp_new_i32(); over = gen_new_label(); find_last_active(s, last, esz, a->pg); @@ -2701,11 +2634,9 @@ static bool do_clast_vector(DisasContext *s, arg_rprr_esz *a, bool before) } ele = load_last_active(s, last, a->rm, esz); - tcg_temp_free_i32(last); vsz = vec_full_reg_size(s); tcg_gen_gvec_dup_i64(esz, vec_full_reg_offset(s, a->rd), vsz, vsz, ele); - tcg_temp_free_i64(ele); /* If this insn used MOVPRFX, we may need a second move. */ if (a->rd != a->rn) { @@ -2743,18 +2674,14 @@ static void do_clast_scalar(DisasContext *s, int esz, int pg, int rm, } /* The conceit here is that while last < 0 indicates not found, after - * adjusting for cpu_env->vfp.zregs[rm], it is still a valid address + * adjusting for tcg_env->vfp.zregs[rm], it is still a valid address * from which we can load garbage. We then discard the garbage with * a conditional move. */ ele = load_last_active(s, last, rm, esz); - tcg_temp_free_i32(last); tcg_gen_movcond_i64(TCG_COND_GE, reg_val, cmp, tcg_constant_i64(0), ele, reg_val); - - tcg_temp_free_i64(cmp); - tcg_temp_free_i64(ele); } /* Compute CLAST for a Vreg. */ @@ -2763,11 +2690,10 @@ static bool do_clast_fp(DisasContext *s, arg_rpr_esz *a, bool before) if (sve_access_check(s)) { int esz = a->esz; int ofs = vec_reg_offset(s, a->rd, 0, esz); - TCGv_i64 reg = load_esz(cpu_env, ofs, esz); + TCGv_i64 reg = load_esz(tcg_env, ofs, esz); do_clast_scalar(s, esz, a->pg, a->rn, before, reg); write_fp_dreg(s, a->rd, reg); - tcg_temp_free_i64(reg); } return true; } @@ -2813,7 +2739,6 @@ static TCGv_i64 do_last_scalar(DisasContext *s, int esz, int pg, int rm, bool before) { TCGv_i32 last = tcg_temp_new_i32(); - TCGv_i64 ret; find_last_active(s, last, esz, pg); if (before) { @@ -2822,9 +2747,7 @@ static TCGv_i64 do_last_scalar(DisasContext *s, int esz, incr_last_active(s, last, esz); } - ret = load_last_active(s, last, rm, esz); - tcg_temp_free_i32(last); - return ret; + return load_last_active(s, last, rm, esz); } /* Compute LAST for a Vreg. */ @@ -2833,7 +2756,6 @@ static bool do_last_fp(DisasContext *s, arg_rpr_esz *a, bool before) if (sve_access_check(s)) { TCGv_i64 val = do_last_scalar(s, a->esz, a->pg, a->rn, before); write_fp_dreg(s, a->rd, val); - tcg_temp_free_i64(val); } return true; } @@ -2847,7 +2769,6 @@ static bool do_last_general(DisasContext *s, arg_rpr_esz *a, bool before) if (sve_access_check(s)) { TCGv_i64 val = do_last_scalar(s, a->esz, a->pg, a->rn, before); tcg_gen_mov_i64(cpu_reg(s, a->rd), val); - tcg_temp_free_i64(val); } return true; } @@ -2873,9 +2794,8 @@ static bool trans_CPY_m_v(DisasContext *s, arg_rpr_esz *a) } if (sve_access_check(s)) { int ofs = vec_reg_offset(s, a->rn, 0, a->esz); - TCGv_i64 t = load_esz(cpu_env, ofs, a->esz); + TCGv_i64 t = load_esz(tcg_env, ofs, a->esz); do_cpy_m(s, a->esz, a->rd, a->rd, a->pg, t); - tcg_temp_free_i64(t); } return true; } @@ -2894,6 +2814,8 @@ TRANS_FEAT(REVH, aa64_sve, gen_gvec_ool_arg_zpz, revh_fns[a->esz], a, 0) TRANS_FEAT(REVW, aa64_sve, gen_gvec_ool_arg_zpz, a->esz == 3 ? gen_helper_sve_revw_d : NULL, a, 0) +TRANS_FEAT(REVD, aa64_sme, gen_gvec_ool_arg_zpz, gen_helper_sme_revd_q, a, 0) + TRANS_FEAT(SPLICE, aa64_sve, gen_gvec_ool_arg_zpzz, gen_helper_sve_splice, a, a->esz) @@ -2925,21 +2847,14 @@ static bool do_ppzz_flags(DisasContext *s, arg_rprr_esz *a, zm = tcg_temp_new_ptr(); pg = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(pd, cpu_env, pred_full_reg_offset(s, a->rd)); - tcg_gen_addi_ptr(zn, cpu_env, vec_full_reg_offset(s, a->rn)); - tcg_gen_addi_ptr(zm, cpu_env, vec_full_reg_offset(s, a->rm)); - tcg_gen_addi_ptr(pg, cpu_env, pred_full_reg_offset(s, a->pg)); + tcg_gen_addi_ptr(pd, tcg_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(zn, tcg_env, vec_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(zm, tcg_env, vec_full_reg_offset(s, a->rm)); + tcg_gen_addi_ptr(pg, tcg_env, pred_full_reg_offset(s, a->pg)); gen_fn(t, pd, zn, zm, pg, tcg_constant_i32(simd_desc(vsz, vsz, 0))); - tcg_temp_free_ptr(pd); - tcg_temp_free_ptr(zn); - tcg_temp_free_ptr(zm); - tcg_temp_free_ptr(pg); - do_pred_flags(t); - - tcg_temp_free_i32(t); return true; } @@ -3005,19 +2920,13 @@ static bool do_ppzi_flags(DisasContext *s, arg_rpri_esz *a, zn = tcg_temp_new_ptr(); pg = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(pd, cpu_env, pred_full_reg_offset(s, a->rd)); - tcg_gen_addi_ptr(zn, cpu_env, vec_full_reg_offset(s, a->rn)); - tcg_gen_addi_ptr(pg, cpu_env, pred_full_reg_offset(s, a->pg)); + tcg_gen_addi_ptr(pd, tcg_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(zn, tcg_env, vec_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(pg, tcg_env, pred_full_reg_offset(s, a->pg)); gen_fn(t, pd, zn, pg, tcg_constant_i32(simd_desc(vsz, vsz, a->imm))); - tcg_temp_free_ptr(pd); - tcg_temp_free_ptr(zn); - tcg_temp_free_ptr(pg); - do_pred_flags(t); - - tcg_temp_free_i32(t); return true; } @@ -3062,23 +2971,18 @@ static bool do_brk3(DisasContext *s, arg_rprr_s *a, TCGv_ptr g = tcg_temp_new_ptr(); TCGv_i32 desc = tcg_constant_i32(FIELD_DP32(0, PREDDESC, OPRSZ, vsz)); - tcg_gen_addi_ptr(d, cpu_env, pred_full_reg_offset(s, a->rd)); - tcg_gen_addi_ptr(n, cpu_env, pred_full_reg_offset(s, a->rn)); - tcg_gen_addi_ptr(m, cpu_env, pred_full_reg_offset(s, a->rm)); - tcg_gen_addi_ptr(g, cpu_env, pred_full_reg_offset(s, a->pg)); + tcg_gen_addi_ptr(d, tcg_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(n, tcg_env, pred_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(m, tcg_env, pred_full_reg_offset(s, a->rm)); + tcg_gen_addi_ptr(g, tcg_env, pred_full_reg_offset(s, a->pg)); if (a->s) { TCGv_i32 t = tcg_temp_new_i32(); fn_s(t, d, n, m, g, desc); do_pred_flags(t); - tcg_temp_free_i32(t); } else { fn(d, n, m, g, desc); } - tcg_temp_free_ptr(d); - tcg_temp_free_ptr(n); - tcg_temp_free_ptr(m); - tcg_temp_free_ptr(g); return true; } @@ -3097,21 +3001,17 @@ static bool do_brk2(DisasContext *s, arg_rpr_s *a, TCGv_ptr g = tcg_temp_new_ptr(); TCGv_i32 desc = tcg_constant_i32(FIELD_DP32(0, PREDDESC, OPRSZ, vsz)); - tcg_gen_addi_ptr(d, cpu_env, pred_full_reg_offset(s, a->rd)); - tcg_gen_addi_ptr(n, cpu_env, pred_full_reg_offset(s, a->rn)); - tcg_gen_addi_ptr(g, cpu_env, pred_full_reg_offset(s, a->pg)); + tcg_gen_addi_ptr(d, tcg_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(n, tcg_env, pred_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(g, tcg_env, pred_full_reg_offset(s, a->pg)); if (a->s) { TCGv_i32 t = tcg_temp_new_i32(); fn_s(t, d, n, g, desc); do_pred_flags(t); - tcg_temp_free_i32(t); } else { fn(d, n, g, desc); } - tcg_temp_free_ptr(d); - tcg_temp_free_ptr(n); - tcg_temp_free_ptr(g); return true; } @@ -3144,12 +3044,11 @@ static void do_cntp(DisasContext *s, TCGv_i64 val, int esz, int pn, int pg) if (psz <= 8) { uint64_t psz_mask; - tcg_gen_ld_i64(val, cpu_env, pred_full_reg_offset(s, pn)); + tcg_gen_ld_i64(val, tcg_env, pred_full_reg_offset(s, pn)); if (pn != pg) { TCGv_i64 g = tcg_temp_new_i64(); - tcg_gen_ld_i64(g, cpu_env, pred_full_reg_offset(s, pg)); + tcg_gen_ld_i64(g, tcg_env, pred_full_reg_offset(s, pg)); tcg_gen_and_i64(val, val, g); - tcg_temp_free_i64(g); } /* Reduce the pred_esz_masks value simply to reduce the @@ -3167,12 +3066,10 @@ static void do_cntp(DisasContext *s, TCGv_i64 val, int esz, int pn, int pg) desc = FIELD_DP32(desc, PREDDESC, OPRSZ, psz); desc = FIELD_DP32(desc, PREDDESC, ESZ, esz); - tcg_gen_addi_ptr(t_pn, cpu_env, pred_full_reg_offset(s, pn)); - tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + tcg_gen_addi_ptr(t_pn, tcg_env, pred_full_reg_offset(s, pn)); + tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, pg)); gen_helper_sve_cntp(val, t_pn, t_pg, tcg_constant_i32(desc)); - tcg_temp_free_ptr(t_pn); - tcg_temp_free_ptr(t_pg); } } @@ -3202,7 +3099,6 @@ static bool trans_INCDECP_r(DisasContext *s, arg_incdec_pred *a) } else { tcg_gen_add_i64(reg, reg, val); } - tcg_temp_free_i64(val); } return true; } @@ -3287,7 +3183,6 @@ static bool trans_CTERM(DisasContext *s, arg_CTERM *a) tcg_gen_setcond_i64(cond, cmp, rn, rm); tcg_gen_extrl_i64_i32(cpu_NF, cmp); - tcg_temp_free_i64(cmp); /* VF = !NF & !CF. */ tcg_gen_xori_i32(cpu_VF, cpu_NF, 1); @@ -3384,12 +3279,10 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a) /* Set the count to zero if the condition is false. */ tcg_gen_movi_i64(t1, 0); tcg_gen_movcond_i64(cond, t0, op0, op1, t0, t1); - tcg_temp_free_i64(t1); /* Since we're bounded, pass as a 32-bit type. */ t2 = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(t2, t0); - tcg_temp_free_i64(t0); /* Scale elements to bits. */ tcg_gen_shli_i32(t2, t2, a->esz); @@ -3398,7 +3291,7 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a) desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); ptr = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(ptr, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(ptr, tcg_env, pred_full_reg_offset(s, a->rd)); if (a->lt) { gen_helper_sve_whilel(t2, ptr, t2, tcg_constant_i32(desc)); @@ -3406,9 +3299,6 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a) gen_helper_sve_whileg(t2, ptr, t2, tcg_constant_i32(desc)); } do_pred_flags(t2); - - tcg_temp_free_ptr(ptr); - tcg_temp_free_i32(t2); return true; } @@ -3440,7 +3330,6 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) tcg_gen_sub_i64(diff, op0, op1); tcg_gen_sub_i64(t1, op1, op0); tcg_gen_movcond_i64(TCG_COND_GEU, diff, op0, op1, diff, t1); - tcg_temp_free_i64(t1); /* Round down to a multiple of ESIZE. */ tcg_gen_andi_i64(diff, diff, -1 << a->esz); /* If op1 == op0, diff == 0, and the condition is always true. */ @@ -3460,19 +3349,15 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) /* Since we're bounded, pass as a 32-bit type. */ t2 = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(t2, diff); - tcg_temp_free_i64(diff); desc = FIELD_DP32(desc, PREDDESC, OPRSZ, vsz / 8); desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); ptr = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(ptr, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(ptr, tcg_env, pred_full_reg_offset(s, a->rd)); gen_helper_sve_whilel(t2, ptr, t2, tcg_constant_i32(desc)); do_pred_flags(t2); - - tcg_temp_free_ptr(ptr); - tcg_temp_free_i32(t2); return true; } @@ -3799,17 +3684,13 @@ static bool do_reduce(DisasContext *s, arg_rpr_esz *a, t_zn = tcg_temp_new_ptr(); t_pg = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, a->rn)); - tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, a->pg)); + tcg_gen_addi_ptr(t_zn, tcg_env, vec_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, a->pg)); status = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); fn(temp, t_zn, t_pg, status, t_desc); - tcg_temp_free_ptr(t_zn); - tcg_temp_free_ptr(t_pg); - tcg_temp_free_ptr(status); write_fp_dreg(s, a->rd, temp); - tcg_temp_free_i64(temp); return true; } @@ -3863,7 +3744,6 @@ static bool do_ppz_fp(DisasContext *s, arg_rpr_esz *a, vec_full_reg_offset(s, a->rn), pred_full_reg_offset(s, a->pg), status, vsz, vsz, 0, fn); - tcg_temp_free_ptr(status); } return true; } @@ -3892,9 +3772,9 @@ static gen_helper_gvec_3_ptr * const ftmad_fns[4] = { NULL, gen_helper_sve_ftmad_h, gen_helper_sve_ftmad_s, gen_helper_sve_ftmad_d, }; -TRANS_FEAT(FTMAD, aa64_sve, gen_gvec_fpst_zzz, - ftmad_fns[a->esz], a->rd, a->rn, a->rm, a->imm, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) +TRANS_FEAT_NONSTREAMING(FTMAD, aa64_sve, gen_gvec_fpst_zzz, + ftmad_fns[a->esz], a->rd, a->rn, a->rm, a->imm, + a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) /* *** SVE Floating Point Accumulating Reduction Group @@ -3917,26 +3797,22 @@ static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a) if (a->esz == 0 || !dc_isar_feature(aa64_sve, s)) { return false; } + s->is_nonstreaming = true; if (!sve_access_check(s)) { return true; } - t_val = load_esz(cpu_env, vec_reg_offset(s, a->rn, 0, a->esz), a->esz); + t_val = load_esz(tcg_env, vec_reg_offset(s, a->rn, 0, a->esz), a->esz); t_rm = tcg_temp_new_ptr(); t_pg = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(t_rm, cpu_env, vec_full_reg_offset(s, a->rm)); - tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, a->pg)); + tcg_gen_addi_ptr(t_rm, tcg_env, vec_full_reg_offset(s, a->rm)); + tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, a->pg)); t_fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); t_desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); fns[a->esz - 1](t_val, t_val, t_rm, t_pg, t_fpst, t_desc); - tcg_temp_free_ptr(t_fpst); - tcg_temp_free_ptr(t_pg); - tcg_temp_free_ptr(t_rm); - write_fp_dreg(s, a->rd, t_val); - tcg_temp_free_i64(t_val); return true; } @@ -3954,12 +3830,18 @@ static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a) DO_FP3(FADD_zzz, fadd) DO_FP3(FSUB_zzz, fsub) DO_FP3(FMUL_zzz, fmul) -DO_FP3(FTSMUL, ftsmul) DO_FP3(FRECPS, recps) DO_FP3(FRSQRTS, rsqrts) #undef DO_FP3 +static gen_helper_gvec_3_ptr * const ftsmul_fns[4] = { + NULL, gen_helper_gvec_ftsmul_h, + gen_helper_gvec_ftsmul_s, gen_helper_gvec_ftsmul_d +}; +TRANS_FEAT_NONSTREAMING(FTSMUL, aa64_sve, gen_gvec_fpst_arg_zzz, + ftsmul_fns[a->esz], a, 0) + /* *** SVE Floating Point Arithmetic - Predicated Group */ @@ -3996,18 +3878,13 @@ static void do_fp_scalar(DisasContext *s, int zd, int zn, int pg, bool is_fp16, t_zd = tcg_temp_new_ptr(); t_zn = tcg_temp_new_ptr(); t_pg = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(t_zd, cpu_env, vec_full_reg_offset(s, zd)); - tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, zn)); - tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + tcg_gen_addi_ptr(t_zd, tcg_env, vec_full_reg_offset(s, zd)); + tcg_gen_addi_ptr(t_zn, tcg_env, vec_full_reg_offset(s, zn)); + tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, pg)); status = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR); desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); fn(t_zd, t_zn, t_pg, scalar, status, desc); - - tcg_temp_free_ptr(status); - tcg_temp_free_ptr(t_pg); - tcg_temp_free_ptr(t_zn); - tcg_temp_free_ptr(t_zd); } static bool do_fp_imm(DisasContext *s, arg_rpri_esz *a, uint64_t imm, @@ -4063,7 +3940,6 @@ static bool do_fp_cmp(DisasContext *s, arg_rprr_esz *a, vec_full_reg_offset(s, a->rm), pred_full_reg_offset(s, a->pg), status, vsz, vsz, 0, fn); - tcg_temp_free_ptr(status); } return true; } @@ -4195,7 +4071,7 @@ TRANS_FEAT(FRINTX, aa64_sve, gen_gvec_fpst_arg_zpz, frintx_fns[a->esz], a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, - int mode, gen_helper_gvec_3_ptr *fn) + ARMFPRounding mode, gen_helper_gvec_3_ptr *fn) { unsigned vsz; TCGv_i32 tmode; @@ -4209,32 +4085,28 @@ static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, } vsz = vec_full_reg_size(s); - tmode = tcg_const_i32(mode); status = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); - - gen_helper_set_rmode(tmode, tmode, status); + tmode = gen_set_rmode(mode, status); tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), vec_full_reg_offset(s, a->rn), pred_full_reg_offset(s, a->pg), status, vsz, vsz, 0, fn); - gen_helper_set_rmode(tmode, tmode, status); - tcg_temp_free_i32(tmode); - tcg_temp_free_ptr(status); + gen_restore_rmode(tmode, status); return true; } TRANS_FEAT(FRINTN, aa64_sve, do_frint_mode, a, - float_round_nearest_even, frint_fns[a->esz]) + FPROUNDING_TIEEVEN, frint_fns[a->esz]) TRANS_FEAT(FRINTP, aa64_sve, do_frint_mode, a, - float_round_up, frint_fns[a->esz]) + FPROUNDING_POSINF, frint_fns[a->esz]) TRANS_FEAT(FRINTM, aa64_sve, do_frint_mode, a, - float_round_down, frint_fns[a->esz]) + FPROUNDING_NEGINF, frint_fns[a->esz]) TRANS_FEAT(FRINTZ, aa64_sve, do_frint_mode, a, - float_round_to_zero, frint_fns[a->esz]) + FPROUNDING_ZERO, frint_fns[a->esz]) TRANS_FEAT(FRINTA, aa64_sve, do_frint_mode, a, - float_round_ties_away, frint_fns[a->esz]) + FPROUNDING_TIEAWAY, frint_fns[a->esz]) static gen_helper_gvec_3_ptr * const frecpx_fns[] = { NULL, gen_helper_sve_frecpx_h, @@ -4292,18 +4164,19 @@ TRANS_FEAT(UCVTF_dd, aa64_sve, gen_gvec_fpst_arg_zpz, * The load should begin at the address Rn + IMM. */ -static void do_ldr(DisasContext *s, uint32_t vofs, int len, int rn, int imm) +void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, + int len, int rn, int imm) { - int len_align = QEMU_ALIGN_DOWN(len, 8); - int len_remain = len % 8; - int nparts = len / 8 + ctpop8(len_remain); + int len_align = QEMU_ALIGN_DOWN(len, 16); + int len_remain = len % 16; + int nparts = len / 16 + ctpop8(len_remain); int midx = get_mem_index(s); TCGv_i64 dirty_addr, clean_addr, t0, t1; + TCGv_i128 t16; dirty_addr = tcg_temp_new_i64(); tcg_gen_addi_i64(dirty_addr, cpu_reg_sp(s, rn), imm); - clean_addr = gen_mte_checkN(s, dirty_addr, false, rn != 31, len); - tcg_temp_free_i64(dirty_addr); + clean_addr = gen_mte_checkN(s, dirty_addr, false, rn != 31, len, MO_8); /* * Note that unpredicated load/store of vector/predicate registers @@ -4316,42 +4189,57 @@ static void do_ldr(DisasContext *s, uint32_t vofs, int len, int rn, int imm) int i; t0 = tcg_temp_new_i64(); - for (i = 0; i < len_align; i += 8) { - tcg_gen_qemu_ld_i64(t0, clean_addr, midx, MO_LEUQ); - tcg_gen_st_i64(t0, cpu_env, vofs + i); - tcg_gen_addi_i64(clean_addr, clean_addr, 8); + t1 = tcg_temp_new_i64(); + t16 = tcg_temp_new_i128(); + + for (i = 0; i < len_align; i += 16) { + tcg_gen_qemu_ld_i128(t16, clean_addr, midx, + MO_LE | MO_128 | MO_ATOM_NONE); + tcg_gen_extr_i128_i64(t0, t1, t16); + tcg_gen_st_i64(t0, base, vofs + i); + tcg_gen_st_i64(t1, base, vofs + i + 8); + tcg_gen_addi_i64(clean_addr, clean_addr, 16); } - tcg_temp_free_i64(t0); } else { TCGLabel *loop = gen_new_label(); - TCGv_ptr tp, i = tcg_const_local_ptr(0); - - /* Copy the clean address into a local temp, live across the loop. */ - t0 = clean_addr; - clean_addr = new_tmp_a64_local(s); - tcg_gen_mov_i64(clean_addr, t0); + TCGv_ptr tp, i = tcg_temp_new_ptr(); + tcg_gen_movi_ptr(i, 0); gen_set_label(loop); - t0 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(t0, clean_addr, midx, MO_LEUQ); - tcg_gen_addi_i64(clean_addr, clean_addr, 8); + t16 = tcg_temp_new_i128(); + tcg_gen_qemu_ld_i128(t16, clean_addr, midx, + MO_LE | MO_128 | MO_ATOM_NONE); + tcg_gen_addi_i64(clean_addr, clean_addr, 16); tp = tcg_temp_new_ptr(); - tcg_gen_add_ptr(tp, cpu_env, i); - tcg_gen_addi_ptr(i, i, 8); + tcg_gen_add_ptr(tp, base, i); + tcg_gen_addi_ptr(i, i, 16); + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + tcg_gen_extr_i128_i64(t0, t1, t16); + tcg_gen_st_i64(t0, tp, vofs); - tcg_temp_free_ptr(tp); - tcg_temp_free_i64(t0); + tcg_gen_st_i64(t1, tp, vofs + 8); tcg_gen_brcondi_ptr(TCG_COND_LTU, i, len_align, loop); - tcg_temp_free_ptr(i); } /* * Predicate register loads can be any multiple of 2. - * Note that we still store the entire 64-bit unit into cpu_env. + * Note that we still store the entire 64-bit unit into tcg_env. */ + if (len_remain >= 8) { + t0 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(t0, clean_addr, midx, MO_LEUQ | MO_ATOM_NONE); + tcg_gen_st_i64(t0, base, vofs + len_align); + len_remain -= 8; + len_align += 8; + if (len_remain) { + tcg_gen_addi_i64(clean_addr, clean_addr, 8); + } + } if (len_remain) { t0 = tcg_temp_new_i64(); switch (len_remain) { @@ -4359,39 +4247,38 @@ static void do_ldr(DisasContext *s, uint32_t vofs, int len, int rn, int imm) case 4: case 8: tcg_gen_qemu_ld_i64(t0, clean_addr, midx, - MO_LE | ctz32(len_remain)); + MO_LE | ctz32(len_remain) | MO_ATOM_NONE); break; case 6: t1 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(t0, clean_addr, midx, MO_LEUL); + tcg_gen_qemu_ld_i64(t0, clean_addr, midx, MO_LEUL | MO_ATOM_NONE); tcg_gen_addi_i64(clean_addr, clean_addr, 4); - tcg_gen_qemu_ld_i64(t1, clean_addr, midx, MO_LEUW); + tcg_gen_qemu_ld_i64(t1, clean_addr, midx, MO_LEUW | MO_ATOM_NONE); tcg_gen_deposit_i64(t0, t0, t1, 32, 32); - tcg_temp_free_i64(t1); break; default: g_assert_not_reached(); } - tcg_gen_st_i64(t0, cpu_env, vofs + len_align); - tcg_temp_free_i64(t0); + tcg_gen_st_i64(t0, base, vofs + len_align); } } /* Similarly for stores. */ -static void do_str(DisasContext *s, uint32_t vofs, int len, int rn, int imm) +void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, + int len, int rn, int imm) { - int len_align = QEMU_ALIGN_DOWN(len, 8); - int len_remain = len % 8; - int nparts = len / 8 + ctpop8(len_remain); + int len_align = QEMU_ALIGN_DOWN(len, 16); + int len_remain = len % 16; + int nparts = len / 16 + ctpop8(len_remain); int midx = get_mem_index(s); - TCGv_i64 dirty_addr, clean_addr, t0; + TCGv_i64 dirty_addr, clean_addr, t0, t1; + TCGv_i128 t16; dirty_addr = tcg_temp_new_i64(); tcg_gen_addi_i64(dirty_addr, cpu_reg_sp(s, rn), imm); - clean_addr = gen_mte_checkN(s, dirty_addr, false, rn != 31, len); - tcg_temp_free_i64(dirty_addr); + clean_addr = gen_mte_checkN(s, dirty_addr, false, rn != 31, len, MO_8); /* Note that unpredicated load/store of vector/predicate registers * are defined as a stream of bytes, which equates to little-endian @@ -4405,62 +4292,74 @@ static void do_str(DisasContext *s, uint32_t vofs, int len, int rn, int imm) int i; t0 = tcg_temp_new_i64(); - for (i = 0; i < len_align; i += 8) { - tcg_gen_ld_i64(t0, cpu_env, vofs + i); - tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUQ); - tcg_gen_addi_i64(clean_addr, clean_addr, 8); + t1 = tcg_temp_new_i64(); + t16 = tcg_temp_new_i128(); + for (i = 0; i < len_align; i += 16) { + tcg_gen_ld_i64(t0, base, vofs + i); + tcg_gen_ld_i64(t1, base, vofs + i + 8); + tcg_gen_concat_i64_i128(t16, t0, t1); + tcg_gen_qemu_st_i128(t16, clean_addr, midx, + MO_LE | MO_128 | MO_ATOM_NONE); + tcg_gen_addi_i64(clean_addr, clean_addr, 16); } - tcg_temp_free_i64(t0); } else { TCGLabel *loop = gen_new_label(); - TCGv_ptr tp, i = tcg_const_local_ptr(0); - - /* Copy the clean address into a local temp, live across the loop. */ - t0 = clean_addr; - clean_addr = new_tmp_a64_local(s); - tcg_gen_mov_i64(clean_addr, t0); + TCGv_ptr tp, i = tcg_temp_new_ptr(); + tcg_gen_movi_ptr(i, 0); gen_set_label(loop); t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); tp = tcg_temp_new_ptr(); - tcg_gen_add_ptr(tp, cpu_env, i); + tcg_gen_add_ptr(tp, base, i); tcg_gen_ld_i64(t0, tp, vofs); - tcg_gen_addi_ptr(i, i, 8); - tcg_temp_free_ptr(tp); + tcg_gen_ld_i64(t1, tp, vofs + 8); + tcg_gen_addi_ptr(i, i, 16); - tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUQ); - tcg_gen_addi_i64(clean_addr, clean_addr, 8); - tcg_temp_free_i64(t0); + t16 = tcg_temp_new_i128(); + tcg_gen_concat_i64_i128(t16, t0, t1); + + tcg_gen_qemu_st_i128(t16, clean_addr, midx, + MO_LE | MO_128 | MO_ATOM_NONE); + tcg_gen_addi_i64(clean_addr, clean_addr, 16); tcg_gen_brcondi_ptr(TCG_COND_LTU, i, len_align, loop); - tcg_temp_free_ptr(i); } /* Predicate register stores can be any multiple of 2. */ + if (len_remain >= 8) { + t0 = tcg_temp_new_i64(); + tcg_gen_ld_i64(t0, base, vofs + len_align); + tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUQ | MO_ATOM_NONE); + len_remain -= 8; + len_align += 8; + if (len_remain) { + tcg_gen_addi_i64(clean_addr, clean_addr, 8); + } + } if (len_remain) { t0 = tcg_temp_new_i64(); - tcg_gen_ld_i64(t0, cpu_env, vofs + len_align); + tcg_gen_ld_i64(t0, base, vofs + len_align); switch (len_remain) { case 2: case 4: case 8: tcg_gen_qemu_st_i64(t0, clean_addr, midx, - MO_LE | ctz32(len_remain)); + MO_LE | ctz32(len_remain) | MO_ATOM_NONE); break; case 6: - tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUL); + tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUL | MO_ATOM_NONE); tcg_gen_addi_i64(clean_addr, clean_addr, 4); tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUW); + tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUW | MO_ATOM_NONE); break; default: g_assert_not_reached(); } - tcg_temp_free_i64(t0); } } @@ -4472,7 +4371,7 @@ static bool trans_LDR_zri(DisasContext *s, arg_rri *a) if (sve_access_check(s)) { int size = vec_full_reg_size(s); int off = vec_full_reg_offset(s, a->rd); - do_ldr(s, off, size, a->rn, a->imm * size); + gen_sve_ldr(s, tcg_env, off, size, a->rn, a->imm * size); } return true; } @@ -4485,7 +4384,7 @@ static bool trans_LDR_pri(DisasContext *s, arg_rri *a) if (sve_access_check(s)) { int size = pred_full_reg_size(s); int off = pred_full_reg_offset(s, a->rd); - do_ldr(s, off, size, a->rn, a->imm * size); + gen_sve_ldr(s, tcg_env, off, size, a->rn, a->imm * size); } return true; } @@ -4498,7 +4397,7 @@ static bool trans_STR_zri(DisasContext *s, arg_rri *a) if (sve_access_check(s)) { int size = vec_full_reg_size(s); int off = vec_full_reg_offset(s, a->rd); - do_str(s, off, size, a->rn, a->imm * size); + gen_sve_str(s, tcg_env, off, size, a->rn, a->imm * size); } return true; } @@ -4511,7 +4410,7 @@ static bool trans_STR_pri(DisasContext *s, arg_rri *a) if (sve_access_check(s)) { int size = pred_full_reg_size(s); int off = pred_full_reg_offset(s, a->rd); - do_str(s, off, size, a->rn, a->imm * size); + gen_sve_str(s, tcg_env, off, size, a->rn, a->imm * size); } return true; } @@ -4538,39 +4437,51 @@ static const uint8_t dtype_esz[16] = { 3, 2, 1, 3 }; +uint32_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, + uint32_t msz, bool is_write, uint32_t data) +{ + uint32_t sizem1; + uint32_t desc = 0; + + /* Assert all of the data fits, with or without MTE enabled. */ + assert(nregs >= 1 && nregs <= 4); + sizem1 = (nregs << msz) - 1; + assert(sizem1 <= R_MTEDESC_SIZEM1_MASK >> R_MTEDESC_SIZEM1_SHIFT); + assert(data < 1u << SVE_MTEDESC_SHIFT); + + if (s->mte_active[0]) { + desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s)); + desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); + desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); + desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); + desc = FIELD_DP32(desc, MTEDESC, SIZEM1, sizem1); + desc <<= SVE_MTEDESC_SHIFT; + } + return simd_desc(vsz, vsz, desc | data); +} + static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, - int dtype, uint32_t mte_n, bool is_write, + int dtype, uint32_t nregs, bool is_write, gen_helper_gvec_mem *fn) { - unsigned vsz = vec_full_reg_size(s); TCGv_ptr t_pg; - int desc = 0; + uint32_t desc; + + if (!s->mte_active[0]) { + addr = clean_data_tbi(s, addr); + } /* * For e.g. LD4, there are not enough arguments to pass all 4 * registers as pointers, so encode the regno into the data field. * For consistency, do this even for LD1. */ - if (s->mte_active[0]) { - int msz = dtype_msz(dtype); - - desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s)); - desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); - desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); - desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); - desc = FIELD_DP32(desc, MTEDESC, SIZEM1, (mte_n << msz) - 1); - desc <<= SVE_MTEDESC_SHIFT; - } else { - addr = clean_data_tbi(s, addr); - } - - desc = simd_desc(vsz, vsz, zt | desc); + desc = make_svemte_desc(s, vec_full_reg_size(s), nregs, + dtype_msz(dtype), is_write, zt); t_pg = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); - fn(cpu_env, t_pg, addr, tcg_constant_i32(desc)); - - tcg_temp_free_ptr(t_pg); + tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, pg)); + fn(tcg_env, t_pg, addr, tcg_constant_i32(desc)); } /* Indexed by [mte][be][dtype][nreg] */ @@ -4703,7 +4614,7 @@ static void do_ld_zpa(DisasContext *s, int zt, int pg, * accessible via the instruction encoding. */ assert(fn != NULL); - do_mem_zpa(s, zt, pg, addr, dtype, nreg, false, fn); + do_mem_zpa(s, zt, pg, addr, dtype, nreg + 1, false, fn); } static bool trans_LD_zprr(DisasContext *s, arg_rprr_load *a) @@ -4712,7 +4623,7 @@ static bool trans_LD_zprr(DisasContext *s, arg_rprr_load *a) return false; } if (sve_access_check(s)) { - TCGv_i64 addr = new_tmp_a64(s); + TCGv_i64 addr = tcg_temp_new_i64(); tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), dtype_msz(a->dtype)); tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); do_ld_zpa(s, a->rd, a->pg, addr, a->dtype, a->nreg); @@ -4728,7 +4639,7 @@ static bool trans_LD_zpri(DisasContext *s, arg_rpri_load *a) if (sve_access_check(s)) { int vsz = vec_full_reg_size(s); int elements = vsz >> dtype_esz[a->dtype]; - TCGv_i64 addr = new_tmp_a64(s); + TCGv_i64 addr = tcg_temp_new_i64(); tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), (a->imm * elements * (a->nreg + 1)) @@ -4829,8 +4740,9 @@ static bool trans_LDFF1_zprr(DisasContext *s, arg_rprr_load *a) if (!dc_isar_feature(aa64_sve, s)) { return false; } + s->is_nonstreaming = true; if (sve_access_check(s)) { - TCGv_i64 addr = new_tmp_a64(s); + TCGv_i64 addr = tcg_temp_new_i64(); tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), dtype_msz(a->dtype)); tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); do_mem_zpa(s, a->rd, a->pg, addr, a->dtype, 1, false, @@ -4930,11 +4842,12 @@ static bool trans_LDNF1_zpri(DisasContext *s, arg_rpri_load *a) if (!dc_isar_feature(aa64_sve, s)) { return false; } + s->is_nonstreaming = true; if (sve_access_check(s)) { int vsz = vec_full_reg_size(s); int elements = vsz >> dtype_esz[a->dtype]; int off = (a->imm * elements) << dtype_msz(a->dtype); - TCGv_i64 addr = new_tmp_a64(s); + TCGv_i64 addr = tcg_temp_new_i64(); tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), off); do_mem_zpa(s, a->rd, a->pg, addr, a->dtype, 1, false, @@ -4948,8 +4861,13 @@ static void do_ldrq(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) unsigned vsz = vec_full_reg_size(s); TCGv_ptr t_pg; int poff; + uint32_t desc; /* Load the first quadword using the normal predicated load helpers. */ + if (!s->mte_active[0]) { + addr = clean_data_tbi(s, addr); + } + poff = pred_full_reg_offset(s, pg); if (vsz > 16) { /* @@ -4962,21 +4880,19 @@ static void do_ldrq(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) #if HOST_BIG_ENDIAN poff += 6; #endif - tcg_gen_ld16u_i64(tmp, cpu_env, poff); + tcg_gen_ld16u_i64(tmp, tcg_env, poff); poff = offsetof(CPUARMState, vfp.preg_tmp); - tcg_gen_st_i64(tmp, cpu_env, poff); - tcg_temp_free_i64(tmp); + tcg_gen_st_i64(tmp, tcg_env, poff); } t_pg = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(t_pg, cpu_env, poff); + tcg_gen_addi_ptr(t_pg, tcg_env, poff); gen_helper_gvec_mem *fn = ldr_fns[s->mte_active[0]][s->be_data == MO_BE][dtype][0]; - fn(cpu_env, t_pg, addr, tcg_constant_i32(simd_desc(16, 16, zt))); - - tcg_temp_free_ptr(t_pg); + desc = make_svemte_desc(s, 16, 1, dtype_msz(dtype), false, zt); + fn(tcg_env, t_pg, addr, tcg_constant_i32(desc)); /* Replicate that first quadword. */ if (vsz > 16) { @@ -4992,7 +4908,7 @@ static bool trans_LD1RQ_zprr(DisasContext *s, arg_rprr_load *a) } if (sve_access_check(s)) { int msz = dtype_msz(a->dtype); - TCGv_i64 addr = new_tmp_a64(s); + TCGv_i64 addr = tcg_temp_new_i64(); tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), msz); tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); do_ldrq(s, a->rd, a->pg, addr, a->dtype); @@ -5006,7 +4922,7 @@ static bool trans_LD1RQ_zpri(DisasContext *s, arg_rpri_load *a) return false; } if (sve_access_check(s)) { - TCGv_i64 addr = new_tmp_a64(s); + TCGv_i64 addr = tcg_temp_new_i64(); tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), a->imm * 16); do_ldrq(s, a->rd, a->pg, addr, a->dtype); } @@ -5019,6 +4935,7 @@ static void do_ldro(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) unsigned vsz_r32; TCGv_ptr t_pg; int poff, doff; + uint32_t desc; if (vsz < 32) { /* @@ -5031,6 +4948,9 @@ static void do_ldro(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) } /* Load the first octaword using the normal predicated load helpers. */ + if (!s->mte_active[0]) { + addr = clean_data_tbi(s, addr); + } poff = pred_full_reg_offset(s, pg); if (vsz > 32) { @@ -5044,21 +4964,19 @@ static void do_ldro(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) #if HOST_BIG_ENDIAN poff += 4; #endif - tcg_gen_ld32u_i64(tmp, cpu_env, poff); + tcg_gen_ld32u_i64(tmp, tcg_env, poff); poff = offsetof(CPUARMState, vfp.preg_tmp); - tcg_gen_st_i64(tmp, cpu_env, poff); - tcg_temp_free_i64(tmp); + tcg_gen_st_i64(tmp, tcg_env, poff); } t_pg = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(t_pg, cpu_env, poff); + tcg_gen_addi_ptr(t_pg, tcg_env, poff); gen_helper_gvec_mem *fn = ldr_fns[s->mte_active[0]][s->be_data == MO_BE][dtype][0]; - fn(cpu_env, t_pg, addr, tcg_constant_i32(simd_desc(32, 32, zt))); - - tcg_temp_free_ptr(t_pg); + desc = make_svemte_desc(s, 32, 1, dtype_msz(dtype), false, zt); + fn(tcg_env, t_pg, addr, tcg_constant_i32(desc)); /* * Replicate that first octaword. @@ -5084,8 +5002,9 @@ static bool trans_LD1RO_zprr(DisasContext *s, arg_rprr_load *a) if (a->rm == 31) { return false; } + s->is_nonstreaming = true; if (sve_access_check(s)) { - TCGv_i64 addr = new_tmp_a64(s); + TCGv_i64 addr = tcg_temp_new_i64(); tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), dtype_msz(a->dtype)); tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); do_ldro(s, a->rd, a->pg, addr, a->dtype); @@ -5098,8 +5017,9 @@ static bool trans_LD1RO_zpri(DisasContext *s, arg_rpri_load *a) if (!dc_isar_feature(aa64_sve_f64mm, s)) { return false; } + s->is_nonstreaming = true; if (sve_access_check(s)) { - TCGv_i64 addr = new_tmp_a64(s); + TCGv_i64 addr = tcg_temp_new_i64(); tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), a->imm * 32); do_ldro(s, a->rd, a->pg, addr, a->dtype); } @@ -5115,6 +5035,7 @@ static bool trans_LD1R_zpri(DisasContext *s, arg_rpri_load *a) unsigned msz = dtype_msz(a->dtype); TCGLabel *over; TCGv_i64 temp, clean_addr; + MemOp memop; if (!dc_isar_feature(aa64_sve, s)) { return false; @@ -5132,29 +5053,26 @@ static bool trans_LD1R_zpri(DisasContext *s, arg_rpri_load *a) */ uint64_t psz_mask = MAKE_64BIT_MASK(0, psz * 8); temp = tcg_temp_new_i64(); - tcg_gen_ld_i64(temp, cpu_env, pred_full_reg_offset(s, a->pg)); + tcg_gen_ld_i64(temp, tcg_env, pred_full_reg_offset(s, a->pg)); tcg_gen_andi_i64(temp, temp, pred_esz_masks[esz] & psz_mask); tcg_gen_brcondi_i64(TCG_COND_EQ, temp, 0, over); - tcg_temp_free_i64(temp); } else { TCGv_i32 t32 = tcg_temp_new_i32(); find_last_active(s, t32, esz, a->pg); tcg_gen_brcondi_i32(TCG_COND_LT, t32, 0, over); - tcg_temp_free_i32(t32); } /* Load the data. */ temp = tcg_temp_new_i64(); tcg_gen_addi_i64(temp, cpu_reg_sp(s, a->rn), a->imm << msz); - clean_addr = gen_mte_check1(s, temp, false, true, msz); - tcg_gen_qemu_ld_i64(temp, clean_addr, get_mem_index(s), - finalize_memop(s, dtype_mop[a->dtype])); + memop = finalize_memop(s, dtype_mop[a->dtype]); + clean_addr = gen_mte_check1(s, temp, false, true, memop); + tcg_gen_qemu_ld_i64(temp, clean_addr, get_mem_index(s), memop); /* Broadcast to *all* elements. */ tcg_gen_gvec_dup_i64(esz, vec_full_reg_offset(s, a->rd), vsz, vsz, temp); - tcg_temp_free_i64(temp); /* Zero the inactive elements. */ gen_set_label(over); @@ -5275,14 +5193,13 @@ static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, if (nreg == 0) { /* ST1 */ fn = fn_single[s->mte_active[0]][be][msz][esz]; - nreg = 1; } else { /* ST2, ST3, ST4 -- msz == esz, enforced by encoding */ assert(msz == esz); fn = fn_multiple[s->mte_active[0]][be][nreg - 1][msz]; } assert(fn != NULL); - do_mem_zpa(s, zt, pg, addr, msz_dtype(s, msz), nreg, true, fn); + do_mem_zpa(s, zt, pg, addr, msz_dtype(s, msz), nreg + 1, true, fn); } static bool trans_ST_zprr(DisasContext *s, arg_rprr_store *a) @@ -5294,7 +5211,7 @@ static bool trans_ST_zprr(DisasContext *s, arg_rprr_store *a) return false; } if (sve_access_check(s)) { - TCGv_i64 addr = new_tmp_a64(s); + TCGv_i64 addr = tcg_temp_new_i64(); tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), a->msz); tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); do_st_zpa(s, a->rd, a->pg, addr, a->msz, a->esz, a->nreg); @@ -5313,7 +5230,7 @@ static bool trans_ST_zpri(DisasContext *s, arg_rpri_store *a) if (sve_access_check(s)) { int vsz = vec_full_reg_size(s); int elements = vsz >> a->esz; - TCGv_i64 addr = new_tmp_a64(s); + TCGv_i64 addr = tcg_temp_new_i64(); tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), (a->imm * elements * (a->nreg + 1)) << a->msz); @@ -5330,30 +5247,17 @@ static void do_mem_zpz(DisasContext *s, int zt, int pg, int zm, int scale, TCGv_i64 scalar, int msz, bool is_write, gen_helper_gvec_mem_scatter *fn) { - unsigned vsz = vec_full_reg_size(s); TCGv_ptr t_zm = tcg_temp_new_ptr(); TCGv_ptr t_pg = tcg_temp_new_ptr(); TCGv_ptr t_zt = tcg_temp_new_ptr(); - int desc = 0; + uint32_t desc; - if (s->mte_active[0]) { - desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s)); - desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); - desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); - desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); - desc = FIELD_DP32(desc, MTEDESC, SIZEM1, (1 << msz) - 1); - desc <<= SVE_MTEDESC_SHIFT; - } - desc = simd_desc(vsz, vsz, desc | scale); + tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, pg)); + tcg_gen_addi_ptr(t_zm, tcg_env, vec_full_reg_offset(s, zm)); + tcg_gen_addi_ptr(t_zt, tcg_env, vec_full_reg_offset(s, zt)); - tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); - tcg_gen_addi_ptr(t_zm, cpu_env, vec_full_reg_offset(s, zm)); - tcg_gen_addi_ptr(t_zt, cpu_env, vec_full_reg_offset(s, zt)); - fn(cpu_env, t_zt, t_pg, t_zm, scalar, tcg_constant_i32(desc)); - - tcg_temp_free_ptr(t_zt); - tcg_temp_free_ptr(t_zm); - tcg_temp_free_ptr(t_pg); + desc = make_svemte_desc(s, vec_full_reg_size(s), 1, msz, is_write, scale); + fn(tcg_env, t_zt, t_pg, t_zm, scalar, tcg_constant_i32(desc)); } /* Indexed by [mte][be][ff][xs][u][msz]. */ @@ -5693,6 +5597,7 @@ static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a) if (!dc_isar_feature(aa64_sve, s)) { return false; } + s->is_nonstreaming = true; if (!sve_access_check(s)) { return true; } @@ -5724,6 +5629,7 @@ static bool trans_LD1_zpiz(DisasContext *s, arg_LD1_zpiz *a) if (!dc_isar_feature(aa64_sve, s)) { return false; } + s->is_nonstreaming = true; if (!sve_access_check(s)) { return true; } @@ -5758,6 +5664,7 @@ static bool trans_LDNT1_zprz(DisasContext *s, arg_LD1_zprz *a) if (!dc_isar_feature(aa64_sve2, s)) { return false; } + s->is_nonstreaming = true; if (!sve_access_check(s)) { return true; } @@ -5881,6 +5788,7 @@ static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a) if (!dc_isar_feature(aa64_sve, s)) { return false; } + s->is_nonstreaming = true; if (!sve_access_check(s)) { return true; } @@ -5911,6 +5819,7 @@ static bool trans_ST1_zpiz(DisasContext *s, arg_ST1_zpiz *a) if (!dc_isar_feature(aa64_sve, s)) { return false; } + s->is_nonstreaming = true; if (!sve_access_check(s)) { return true; } @@ -5945,6 +5854,7 @@ static bool trans_STNT1_zprz(DisasContext *s, arg_ST1_zprz *a) if (!dc_isar_feature(aa64_sve2, s)) { return false; } + s->is_nonstreaming = true; if (!sve_access_check(s)) { return true; } @@ -5989,6 +5899,17 @@ static bool trans_PRF_rr(DisasContext *s, arg_PRF_rr *a) return true; } +static bool trans_PRF_ns(DisasContext *s, arg_PRF_ns *a) +{ + if (!dc_isar_feature(aa64_sve, s)) { + return false; + } + /* Prefetch is a nop within QEMU. */ + s->is_nonstreaming = true; + (void)sve_access_check(s); + return true; +} + /* * Move Prefix * @@ -6217,9 +6138,13 @@ static bool do_trans_pmull(DisasContext *s, arg_rrr_esz *a, bool sel) gen_helper_gvec_pmull_q, gen_helper_sve2_pmull_h, NULL, gen_helper_sve2_pmull_d, }; - if (a->esz == 0 - ? !dc_isar_feature(aa64_sve2_pmull128, s) - : !dc_isar_feature(aa64_sve, s)) { + + if (a->esz == 0) { + if (!dc_isar_feature(aa64_sve2_pmull128, s)) { + return false; + } + s->is_nonstreaming = true; + } else if (!dc_isar_feature(aa64_sve, s)) { return false; } return gen_gvec_ool_arg_zzz(s, fns[a->esz], a, sel); @@ -6267,7 +6192,6 @@ static void gen_sshll_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t imm) TCGv_vec t = tcg_temp_new_vec_matching(d); tcg_gen_dupi_vec(vece, t, MAKE_64BIT_MASK(halfbits, halfbits)); tcg_gen_and_vec(vece, d, n, t); - tcg_temp_free_vec(t); } else { tcg_gen_sari_vec(vece, d, n, halfbits); tcg_gen_shli_vec(vece, d, d, shl); @@ -6325,7 +6249,6 @@ static void gen_ushll_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t imm) TCGv_vec t = tcg_temp_new_vec_matching(d); tcg_gen_dupi_vec(vece, t, MAKE_64BIT_MASK(halfbits, halfbits)); tcg_gen_and_vec(vece, d, n, t); - tcg_temp_free_vec(t); } else { tcg_gen_shri_vec(vece, d, n, halfbits); tcg_gen_shli_vec(vece, d, d, shl); @@ -6335,7 +6258,6 @@ static void gen_ushll_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t imm) TCGv_vec t = tcg_temp_new_vec_matching(d); tcg_gen_dupi_vec(vece, t, MAKE_64BIT_MASK(0, halfbits)); tcg_gen_and_vec(vece, d, n, t); - tcg_temp_free_vec(t); } else { tcg_gen_shli_vec(vece, d, n, halfbits); tcg_gen_shri_vec(vece, d, d, halfbits - shl); @@ -6407,22 +6329,22 @@ static gen_helper_gvec_3 * const bext_fns[4] = { gen_helper_sve2_bext_b, gen_helper_sve2_bext_h, gen_helper_sve2_bext_s, gen_helper_sve2_bext_d, }; -TRANS_FEAT(BEXT, aa64_sve2_bitperm, gen_gvec_ool_arg_zzz, - bext_fns[a->esz], a, 0) +TRANS_FEAT_NONSTREAMING(BEXT, aa64_sve2_bitperm, gen_gvec_ool_arg_zzz, + bext_fns[a->esz], a, 0) static gen_helper_gvec_3 * const bdep_fns[4] = { gen_helper_sve2_bdep_b, gen_helper_sve2_bdep_h, gen_helper_sve2_bdep_s, gen_helper_sve2_bdep_d, }; -TRANS_FEAT(BDEP, aa64_sve2_bitperm, gen_gvec_ool_arg_zzz, - bdep_fns[a->esz], a, 0) +TRANS_FEAT_NONSTREAMING(BDEP, aa64_sve2_bitperm, gen_gvec_ool_arg_zzz, + bdep_fns[a->esz], a, 0) static gen_helper_gvec_3 * const bgrp_fns[4] = { gen_helper_sve2_bgrp_b, gen_helper_sve2_bgrp_h, gen_helper_sve2_bgrp_s, gen_helper_sve2_bgrp_d, }; -TRANS_FEAT(BGRP, aa64_sve2_bitperm, gen_gvec_ool_arg_zzz, - bgrp_fns[a->esz], a, 0) +TRANS_FEAT_NONSTREAMING(BGRP, aa64_sve2_bitperm, gen_gvec_ool_arg_zzz, + bgrp_fns[a->esz], a, 0) static gen_helper_gvec_3 * const cadd_fns[4] = { gen_helper_sve2_cadd_b, gen_helper_sve2_cadd_h, @@ -6515,7 +6437,6 @@ static void gen_sqxtnb_vec(unsigned vece, TCGv_vec d, TCGv_vec n) tcg_gen_smin_vec(vece, d, d, t); tcg_gen_dupi_vec(vece, t, mask); tcg_gen_and_vec(vece, d, d, t); - tcg_temp_free_vec(t); } static const GVecGen2 sqxtnb_ops[3] = { @@ -6549,7 +6470,6 @@ static void gen_sqxtnt_vec(unsigned vece, TCGv_vec d, TCGv_vec n) tcg_gen_shli_vec(vece, n, n, halfbits); tcg_gen_dupi_vec(vece, t, mask); tcg_gen_bitsel_vec(vece, d, t, d, n); - tcg_temp_free_vec(t); } static const GVecGen2 sqxtnt_ops[3] = { @@ -6583,7 +6503,6 @@ static void gen_uqxtnb_vec(unsigned vece, TCGv_vec d, TCGv_vec n) tcg_gen_dupi_vec(vece, t, max); tcg_gen_umin_vec(vece, d, n, t); - tcg_temp_free_vec(t); } static const GVecGen2 uqxtnb_ops[3] = { @@ -6612,7 +6531,6 @@ static void gen_uqxtnt_vec(unsigned vece, TCGv_vec d, TCGv_vec n) tcg_gen_umin_vec(vece, n, n, t); tcg_gen_shli_vec(vece, n, n, halfbits); tcg_gen_bitsel_vec(vece, d, t, d, n); - tcg_temp_free_vec(t); } static const GVecGen2 uqxtnt_ops[3] = { @@ -6648,7 +6566,6 @@ static void gen_sqxtunb_vec(unsigned vece, TCGv_vec d, TCGv_vec n) tcg_gen_smax_vec(vece, d, n, t); tcg_gen_dupi_vec(vece, t, max); tcg_gen_umin_vec(vece, d, d, t); - tcg_temp_free_vec(t); } static const GVecGen2 sqxtunb_ops[3] = { @@ -6679,7 +6596,6 @@ static void gen_sqxtunt_vec(unsigned vece, TCGv_vec d, TCGv_vec n) tcg_gen_umin_vec(vece, n, n, t); tcg_gen_shli_vec(vece, n, n, halfbits); tcg_gen_bitsel_vec(vece, d, t, d, n); - tcg_temp_free_vec(t); } static const GVecGen2 sqxtunt_ops[3] = { @@ -6750,7 +6666,6 @@ static void gen_shrnb_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t shr) tcg_gen_shri_vec(vece, n, n, shr); tcg_gen_dupi_vec(vece, t, mask); tcg_gen_and_vec(vece, d, n, t); - tcg_temp_free_vec(t); } static const TCGOpcode shrnb_vec_list[] = { INDEX_op_shri_vec, 0 }; @@ -6809,7 +6724,6 @@ static void gen_shrnt_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t shr) tcg_gen_shli_vec(vece, n, n, halfbits - shr); tcg_gen_dupi_vec(vece, t, mask); tcg_gen_bitsel_vec(vece, d, t, d, n); - tcg_temp_free_vec(t); } static const TCGOpcode shrnt_vec_list[] = { INDEX_op_shli_vec, 0 }; @@ -6860,7 +6774,6 @@ static void gen_sqshrunb_vec(unsigned vece, TCGv_vec d, tcg_gen_smax_vec(vece, n, n, t); tcg_gen_dupi_vec(vece, t, MAKE_64BIT_MASK(0, halfbits)); tcg_gen_umin_vec(vece, d, n, t); - tcg_temp_free_vec(t); } static const TCGOpcode sqshrunb_vec_list[] = { @@ -6895,7 +6808,6 @@ static void gen_sqshrunt_vec(unsigned vece, TCGv_vec d, tcg_gen_umin_vec(vece, n, n, t); tcg_gen_shli_vec(vece, n, n, halfbits); tcg_gen_bitsel_vec(vece, d, t, d, n); - tcg_temp_free_vec(t); } static const TCGOpcode sqshrunt_vec_list[] = { @@ -6950,7 +6862,6 @@ static void gen_sqshrnb_vec(unsigned vece, TCGv_vec d, tcg_gen_smin_vec(vece, n, n, t); tcg_gen_dupi_vec(vece, t, MAKE_64BIT_MASK(0, halfbits)); tcg_gen_and_vec(vece, d, n, t); - tcg_temp_free_vec(t); } static const TCGOpcode sqshrnb_vec_list[] = { @@ -6988,7 +6899,6 @@ static void gen_sqshrnt_vec(unsigned vece, TCGv_vec d, tcg_gen_shli_vec(vece, n, n, halfbits); tcg_gen_dupi_vec(vece, t, MAKE_64BIT_MASK(0, halfbits)); tcg_gen_bitsel_vec(vece, d, t, d, n); - tcg_temp_free_vec(t); } static const TCGOpcode sqshrnt_vec_list[] = { @@ -7037,7 +6947,6 @@ static void gen_uqshrnb_vec(unsigned vece, TCGv_vec d, tcg_gen_shri_vec(vece, n, n, shr); tcg_gen_dupi_vec(vece, t, MAKE_64BIT_MASK(0, halfbits)); tcg_gen_umin_vec(vece, d, n, t); - tcg_temp_free_vec(t); } static const TCGOpcode uqshrnb_vec_list[] = { @@ -7070,7 +6979,6 @@ static void gen_uqshrnt_vec(unsigned vece, TCGv_vec d, tcg_gen_umin_vec(vece, n, n, t); tcg_gen_shli_vec(vece, n, n, halfbits); tcg_gen_bitsel_vec(vece, d, t, d, n); - tcg_temp_free_vec(t); } static const TCGOpcode uqshrnt_vec_list[] = { @@ -7130,21 +7038,21 @@ DO_SVE2_ZZZ_NARROW(RSUBHNT, rsubhnt) static gen_helper_gvec_flags_4 * const match_fns[4] = { gen_helper_sve2_match_ppzz_b, gen_helper_sve2_match_ppzz_h, NULL, NULL }; -TRANS_FEAT(MATCH, aa64_sve2, do_ppzz_flags, a, match_fns[a->esz]) +TRANS_FEAT_NONSTREAMING(MATCH, aa64_sve2, do_ppzz_flags, a, match_fns[a->esz]) static gen_helper_gvec_flags_4 * const nmatch_fns[4] = { gen_helper_sve2_nmatch_ppzz_b, gen_helper_sve2_nmatch_ppzz_h, NULL, NULL }; -TRANS_FEAT(NMATCH, aa64_sve2, do_ppzz_flags, a, nmatch_fns[a->esz]) +TRANS_FEAT_NONSTREAMING(NMATCH, aa64_sve2, do_ppzz_flags, a, nmatch_fns[a->esz]) static gen_helper_gvec_4 * const histcnt_fns[4] = { NULL, NULL, gen_helper_sve2_histcnt_s, gen_helper_sve2_histcnt_d }; -TRANS_FEAT(HISTCNT, aa64_sve2, gen_gvec_ool_arg_zpzz, - histcnt_fns[a->esz], a, 0) +TRANS_FEAT_NONSTREAMING(HISTCNT, aa64_sve2, gen_gvec_ool_arg_zpzz, + histcnt_fns[a->esz], a, 0) -TRANS_FEAT(HISTSEG, aa64_sve2, gen_gvec_ool_arg_zzz, - a->esz == 0 ? gen_helper_sve2_histseg : NULL, a, 0) +TRANS_FEAT_NONSTREAMING(HISTSEG, aa64_sve2, gen_gvec_ool_arg_zzz, + a->esz == 0 ? gen_helper_sve2_histseg : NULL, a, 0) DO_ZPZZ_FP(FADDP, aa64_sve2, sve2_faddp_zpzz) DO_ZPZZ_FP(FMAXNMP, aa64_sve2, sve2_fmaxnmp_zpzz) @@ -7156,10 +7064,12 @@ DO_ZPZZ_FP(FMINP, aa64_sve2, sve2_fminp_zpzz) * SVE Integer Multiply-Add (unpredicated) */ -TRANS_FEAT(FMMLA_s, aa64_sve_f32mm, gen_gvec_fpst_zzzz, gen_helper_fmmla_s, - a->rd, a->rn, a->rm, a->ra, 0, FPST_FPCR) -TRANS_FEAT(FMMLA_d, aa64_sve_f64mm, gen_gvec_fpst_zzzz, gen_helper_fmmla_d, - a->rd, a->rn, a->rm, a->ra, 0, FPST_FPCR) +TRANS_FEAT_NONSTREAMING(FMMLA_s, aa64_sve_f32mm, gen_gvec_fpst_zzzz, + gen_helper_fmmla_s, a->rd, a->rn, a->rm, a->ra, + 0, FPST_FPCR) +TRANS_FEAT_NONSTREAMING(FMMLA_d, aa64_sve_f64mm, gen_gvec_fpst_zzzz, + gen_helper_fmmla_d, a->rd, a->rn, a->rm, a->ra, + 0, FPST_FPCR) static gen_helper_gvec_4 * const sqdmlal_zzzw_fns[] = { NULL, gen_helper_sve2_sqdmlal_zzzw_h, @@ -7256,20 +7166,23 @@ TRANS_FEAT(SQRDCMLAH_zzzz, aa64_sve2, gen_gvec_ool_zzzz, TRANS_FEAT(USDOT_zzzz, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, a->esz == 2 ? gen_helper_gvec_usdot_b : NULL, a, 0) -TRANS_FEAT(AESMC, aa64_sve2_aes, gen_gvec_ool_zz, - gen_helper_crypto_aesmc, a->rd, a->rd, a->decrypt) +TRANS_FEAT_NONSTREAMING(AESMC, aa64_sve2_aes, gen_gvec_ool_zz, + gen_helper_crypto_aesmc, a->rd, a->rd, 0) +TRANS_FEAT_NONSTREAMING(AESIMC, aa64_sve2_aes, gen_gvec_ool_zz, + gen_helper_crypto_aesimc, a->rd, a->rd, 0) -TRANS_FEAT(AESE, aa64_sve2_aes, gen_gvec_ool_arg_zzz, - gen_helper_crypto_aese, a, false) -TRANS_FEAT(AESD, aa64_sve2_aes, gen_gvec_ool_arg_zzz, - gen_helper_crypto_aese, a, true) +TRANS_FEAT_NONSTREAMING(AESE, aa64_sve2_aes, gen_gvec_ool_arg_zzz, + gen_helper_crypto_aese, a, 0) +TRANS_FEAT_NONSTREAMING(AESD, aa64_sve2_aes, gen_gvec_ool_arg_zzz, + gen_helper_crypto_aesd, a, 0) -TRANS_FEAT(SM4E, aa64_sve2_sm4, gen_gvec_ool_arg_zzz, - gen_helper_crypto_sm4e, a, 0) -TRANS_FEAT(SM4EKEY, aa64_sve2_sm4, gen_gvec_ool_arg_zzz, - gen_helper_crypto_sm4ekey, a, 0) +TRANS_FEAT_NONSTREAMING(SM4E, aa64_sve2_sm4, gen_gvec_ool_arg_zzz, + gen_helper_crypto_sm4e, a, 0) +TRANS_FEAT_NONSTREAMING(SM4EKEY, aa64_sve2_sm4, gen_gvec_ool_arg_zzz, + gen_helper_crypto_sm4ekey, a, 0) -TRANS_FEAT(RAX1, aa64_sve2_sha3, gen_gvec_fn_arg_zzz, gen_gvec_rax1, a) +TRANS_FEAT_NONSTREAMING(RAX1, aa64_sve2_sha3, gen_gvec_fn_arg_zzz, + gen_gvec_rax1, a) TRANS_FEAT(FCVTNT_sh, aa64_sve2, gen_gvec_fpst_arg_zpz, gen_helper_sve2_fcvtnt_sh, a, 0, FPST_FPCR) @@ -7285,9 +7198,9 @@ TRANS_FEAT(FCVTLT_sd, aa64_sve2, gen_gvec_fpst_arg_zpz, gen_helper_sve2_fcvtlt_sd, a, 0, FPST_FPCR) TRANS_FEAT(FCVTX_ds, aa64_sve2, do_frint_mode, a, - float_round_to_odd, gen_helper_sve_fcvt_ds) + FPROUNDING_ODD, gen_helper_sve_fcvt_ds) TRANS_FEAT(FCVTXNT_ds, aa64_sve2, do_frint_mode, a, - float_round_to_odd, gen_helper_sve2_fcvtnt_ds) + FPROUNDING_ODD, gen_helper_sve2_fcvtnt_ds) static gen_helper_gvec_3_ptr * const flogb_fns[] = { NULL, gen_helper_flogb_h, @@ -7300,7 +7213,7 @@ static bool do_FMLAL_zzzw(DisasContext *s, arg_rrrr_esz *a, bool sub, bool sel) { return gen_gvec_ptr_zzzz(s, gen_helper_sve2_fmlal_zzzw_s, a->rd, a->rn, a->rm, a->ra, - (sel << 1) | sub, cpu_env); + (sel << 1) | sub, tcg_env); } TRANS_FEAT(FMLALB_zzzw, aa64_sve2, do_FMLAL_zzzw, a, false, false) @@ -7312,7 +7225,7 @@ static bool do_FMLAL_zzxw(DisasContext *s, arg_rrxr_esz *a, bool sub, bool sel) { return gen_gvec_ptr_zzzz(s, gen_helper_sve2_fmlal_zzxw_s, a->rd, a->rn, a->rm, a->ra, - (a->index << 2) | (sel << 1) | sub, cpu_env); + (a->index << 2) | (sel << 1) | sub, tcg_env); } TRANS_FEAT(FMLALB_zzxw, aa64_sve2, do_FMLAL_zzxw, a, false, false) @@ -7320,20 +7233,20 @@ TRANS_FEAT(FMLALT_zzxw, aa64_sve2, do_FMLAL_zzxw, a, false, true) TRANS_FEAT(FMLSLB_zzxw, aa64_sve2, do_FMLAL_zzxw, a, true, false) TRANS_FEAT(FMLSLT_zzxw, aa64_sve2, do_FMLAL_zzxw, a, true, true) -TRANS_FEAT(SMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, - gen_helper_gvec_smmla_b, a, 0) -TRANS_FEAT(USMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, - gen_helper_gvec_usmmla_b, a, 0) -TRANS_FEAT(UMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, - gen_helper_gvec_ummla_b, a, 0) +TRANS_FEAT_NONSTREAMING(SMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, + gen_helper_gvec_smmla_b, a, 0) +TRANS_FEAT_NONSTREAMING(USMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, + gen_helper_gvec_usmmla_b, a, 0) +TRANS_FEAT_NONSTREAMING(UMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, + gen_helper_gvec_ummla_b, a, 0) TRANS_FEAT(BFDOT_zzzz, aa64_sve_bf16, gen_gvec_ool_arg_zzzz, gen_helper_gvec_bfdot, a, 0) TRANS_FEAT(BFDOT_zzxz, aa64_sve_bf16, gen_gvec_ool_arg_zzxz, gen_helper_gvec_bfdot_idx, a) -TRANS_FEAT(BFMMLA, aa64_sve_bf16, gen_gvec_ool_arg_zzzz, - gen_helper_gvec_bfmmla, a, 0) +TRANS_FEAT_NONSTREAMING(BFMMLA, aa64_sve_bf16, gen_gvec_ool_arg_zzzz, + gen_helper_gvec_bfmmla, a, 0) static bool do_BFMLAL_zzzw(DisasContext *s, arg_rrrr_esz *a, bool sel) { @@ -7353,3 +7266,157 @@ static bool do_BFMLAL_zzxw(DisasContext *s, arg_rrxr_esz *a, bool sel) TRANS_FEAT(BFMLALB_zzxw, aa64_sve_bf16, do_BFMLAL_zzxw, a, false) TRANS_FEAT(BFMLALT_zzxw, aa64_sve_bf16, do_BFMLAL_zzxw, a, true) + +static bool trans_PSEL(DisasContext *s, arg_psel *a) +{ + int vl = vec_full_reg_size(s); + int pl = pred_gvec_reg_size(s); + int elements = vl >> a->esz; + TCGv_i64 tmp, didx, dbit; + TCGv_ptr ptr; + + if (!dc_isar_feature(aa64_sme, s)) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + tmp = tcg_temp_new_i64(); + dbit = tcg_temp_new_i64(); + didx = tcg_temp_new_i64(); + ptr = tcg_temp_new_ptr(); + + /* Compute the predicate element. */ + tcg_gen_addi_i64(tmp, cpu_reg(s, a->rv), a->imm); + if (is_power_of_2(elements)) { + tcg_gen_andi_i64(tmp, tmp, elements - 1); + } else { + tcg_gen_remu_i64(tmp, tmp, tcg_constant_i64(elements)); + } + + /* Extract the predicate byte and bit indices. */ + tcg_gen_shli_i64(tmp, tmp, a->esz); + tcg_gen_andi_i64(dbit, tmp, 7); + tcg_gen_shri_i64(didx, tmp, 3); + if (HOST_BIG_ENDIAN) { + tcg_gen_xori_i64(didx, didx, 7); + } + + /* Load the predicate word. */ + tcg_gen_trunc_i64_ptr(ptr, didx); + tcg_gen_add_ptr(ptr, ptr, tcg_env); + tcg_gen_ld8u_i64(tmp, ptr, pred_full_reg_offset(s, a->pm)); + + /* Extract the predicate bit and replicate to MO_64. */ + tcg_gen_shr_i64(tmp, tmp, dbit); + tcg_gen_andi_i64(tmp, tmp, 1); + tcg_gen_neg_i64(tmp, tmp); + + /* Apply to either copy the source, or write zeros. */ + tcg_gen_gvec_ands(MO_64, pred_full_reg_offset(s, a->pd), + pred_full_reg_offset(s, a->pn), tmp, pl, pl); + return true; +} + +static void gen_sclamp_i32(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_i32 a) +{ + tcg_gen_smax_i32(d, a, n); + tcg_gen_smin_i32(d, d, m); +} + +static void gen_sclamp_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 a) +{ + tcg_gen_smax_i64(d, a, n); + tcg_gen_smin_i64(d, d, m); +} + +static void gen_sclamp_vec(unsigned vece, TCGv_vec d, TCGv_vec n, + TCGv_vec m, TCGv_vec a) +{ + tcg_gen_smax_vec(vece, d, a, n); + tcg_gen_smin_vec(vece, d, d, m); +} + +static void gen_sclamp(unsigned vece, uint32_t d, uint32_t n, uint32_t m, + uint32_t a, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop[] = { + INDEX_op_smin_vec, INDEX_op_smax_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_sclamp_vec, + .fno = gen_helper_gvec_sclamp_b, + .opt_opc = vecop, + .vece = MO_8 }, + { .fniv = gen_sclamp_vec, + .fno = gen_helper_gvec_sclamp_h, + .opt_opc = vecop, + .vece = MO_16 }, + { .fni4 = gen_sclamp_i32, + .fniv = gen_sclamp_vec, + .fno = gen_helper_gvec_sclamp_s, + .opt_opc = vecop, + .vece = MO_32 }, + { .fni8 = gen_sclamp_i64, + .fniv = gen_sclamp_vec, + .fno = gen_helper_gvec_sclamp_d, + .opt_opc = vecop, + .vece = MO_64, + .prefer_i64 = TCG_TARGET_REG_BITS == 64 } + }; + tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &ops[vece]); +} + +TRANS_FEAT(SCLAMP, aa64_sme, gen_gvec_fn_arg_zzzz, gen_sclamp, a) + +static void gen_uclamp_i32(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_i32 a) +{ + tcg_gen_umax_i32(d, a, n); + tcg_gen_umin_i32(d, d, m); +} + +static void gen_uclamp_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 a) +{ + tcg_gen_umax_i64(d, a, n); + tcg_gen_umin_i64(d, d, m); +} + +static void gen_uclamp_vec(unsigned vece, TCGv_vec d, TCGv_vec n, + TCGv_vec m, TCGv_vec a) +{ + tcg_gen_umax_vec(vece, d, a, n); + tcg_gen_umin_vec(vece, d, d, m); +} + +static void gen_uclamp(unsigned vece, uint32_t d, uint32_t n, uint32_t m, + uint32_t a, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop[] = { + INDEX_op_umin_vec, INDEX_op_umax_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_uclamp_vec, + .fno = gen_helper_gvec_uclamp_b, + .opt_opc = vecop, + .vece = MO_8 }, + { .fniv = gen_uclamp_vec, + .fno = gen_helper_gvec_uclamp_h, + .opt_opc = vecop, + .vece = MO_16 }, + { .fni4 = gen_uclamp_i32, + .fniv = gen_uclamp_vec, + .fno = gen_helper_gvec_uclamp_s, + .opt_opc = vecop, + .vece = MO_32 }, + { .fni8 = gen_uclamp_i64, + .fniv = gen_uclamp_vec, + .fno = gen_helper_gvec_uclamp_d, + .opt_opc = vecop, + .vece = MO_64, + .prefer_i64 = TCG_TARGET_REG_BITS == 64 } + }; + tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &ops[vece]); +} + +TRANS_FEAT(UCLAMP, aa64_sme, gen_gvec_fn_arg_zzzz, gen_uclamp, a) diff --git a/target/arm/translate-vfp.c b/target/arm/tcg/translate-vfp.c similarity index 91% rename from target/arm/translate-vfp.c rename to target/arm/tcg/translate-vfp.c index 40a513b822..b9af03b7c3 100644 --- a/target/arm/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -21,10 +21,6 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "exec/exec-all.h" -#include "exec/gen-icount.h" #include "translate.h" #include "translate-a32.h" @@ -34,22 +30,22 @@ static inline void vfp_load_reg64(TCGv_i64 var, int reg) { - tcg_gen_ld_i64(var, cpu_env, vfp_reg_offset(true, reg)); + tcg_gen_ld_i64(var, tcg_env, vfp_reg_offset(true, reg)); } static inline void vfp_store_reg64(TCGv_i64 var, int reg) { - tcg_gen_st_i64(var, cpu_env, vfp_reg_offset(true, reg)); + tcg_gen_st_i64(var, tcg_env, vfp_reg_offset(true, reg)); } static inline void vfp_load_reg32(TCGv_i32 var, int reg) { - tcg_gen_ld_i32(var, cpu_env, vfp_reg_offset(false, reg)); + tcg_gen_ld_i32(var, tcg_env, vfp_reg_offset(false, reg)); } static inline void vfp_store_reg32(TCGv_i32 var, int reg) { - tcg_gen_st_i32(var, cpu_env, vfp_reg_offset(false, reg)); + tcg_gen_st_i32(var, tcg_env, vfp_reg_offset(false, reg)); } /* @@ -117,11 +113,10 @@ static void gen_preserve_fp_state(DisasContext *s, bool skip_context_update) * so we must mark it as an IO operation for icount (and cause * this to be the last insn in the TB). */ - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + if (translator_io_start(&s->base)) { s->base.is_jmp = DISAS_UPDATE_EXIT; - gen_io_start(); } - gen_helper_v7m_preserve_fp_state(cpu_env); + gen_helper_v7m_preserve_fp_state(tcg_env); /* * If the preserve_fp_state helper doesn't throw an exception * then it will clear LSPACT; we don't need to repeat this for @@ -149,7 +144,7 @@ static void gen_preserve_fp_state(DisasContext *s, bool skip_context_update) * Generate code for M-profile FP context handling: update the * ownership of the FP context, and create a new context if * necessary. This corresponds to the parts of the pseudocode - * ExecuteFPCheck() after the inital PreserveFPState() call. + * ExecuteFPCheck() after the initial PreserveFPState() call. */ static void gen_update_fp_context(DisasContext *s) { @@ -177,8 +172,7 @@ static void gen_update_fp_context(DisasContext *s) uint32_t bits = R_V7M_CONTROL_FPCA_MASK; fpscr = load_cpu_field(v7m.fpdscr[s->v8m_secure]); - gen_helper_vfp_set_fpscr(cpu_env, fpscr); - tcg_temp_free_i32(fpscr); + gen_helper_vfp_set_fpscr(tcg_env, fpscr); if (dc_isar_feature(aa32_mve, s)) { store_cpu_field(tcg_constant_i32(0), v7m.vpr); } @@ -219,8 +213,30 @@ static void gen_update_fp_context(DisasContext *s) static bool vfp_access_check_a(DisasContext *s, bool ignore_vfp_enabled) { if (s->fp_excp_el) { - gen_exception_insn(s, s->pc_curr, EXCP_UDEF, - syn_fp_access_trap(1, 0xe, false), s->fp_excp_el); + /* + * The full syndrome is only used for HSR when HCPTR traps: + * For v8, when TA==0, coproc is RES0. + * For v7, any use of a Floating-point instruction or access + * to a Floating-point Extension register that is trapped to + * Hyp mode because of a trap configured in the HCPTR sets + * this field to 0xA. + */ + int coproc = arm_dc_feature(s, ARM_FEATURE_V8) ? 0 : 0xa; + uint32_t syn = syn_fp_access_trap(1, 0xe, false, coproc); + + gen_exception_insn_el(s, 0, EXCP_UDEF, syn, s->fp_excp_el); + return false; + } + + /* + * Note that rebuild_hflags_a32 has already accounted for being in EL0 + * and the higher EL in A64 mode, etc. Unlike A64 mode, there do not + * appear to be any insns which touch VFP which are allowed. + */ + if (s->sme_trap_nonstreaming) { + gen_exception_insn(s, 0, EXCP_UDEF, + syn_smetrap(SME_ET_Streaming, + curr_insn_len(s) == 2)); return false; } @@ -250,8 +266,8 @@ bool vfp_access_check_m(DisasContext *s, bool skip_context_update) * the encoding space handled by the patterns in m-nocp.decode, * and for them we may need to raise NOCP here. */ - gen_exception_insn(s, s->pc_curr, EXCP_NOCP, - syn_uncategorized(), s->fp_excp_el); + gen_exception_insn_el(s, 0, EXCP_NOCP, + syn_uncategorized(), s->fp_excp_el); return false; } @@ -343,24 +359,15 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) tmp = tcg_temp_new_i64(); tcg_gen_xor_i64(tmp, vf, nf); tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, frn, frm); - tcg_temp_free_i64(tmp); break; case 3: /* gt: !Z && N == V */ tcg_gen_movcond_i64(TCG_COND_NE, dest, zf, zero, frn, frm); tmp = tcg_temp_new_i64(); tcg_gen_xor_i64(tmp, vf, nf); tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, dest, frm); - tcg_temp_free_i64(tmp); break; } vfp_store_reg64(dest, rd); - tcg_temp_free_i64(frn); - tcg_temp_free_i64(frm); - tcg_temp_free_i64(dest); - - tcg_temp_free_i64(zf); - tcg_temp_free_i64(nf); - tcg_temp_free_i64(vf); } else { TCGv_i32 frn, frm, dest; TCGv_i32 tmp, zero; @@ -383,14 +390,12 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) tmp = tcg_temp_new_i32(); tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, frn, frm); - tcg_temp_free_i32(tmp); break; case 3: /* gt: !Z && N == V */ tcg_gen_movcond_i32(TCG_COND_NE, dest, cpu_ZF, zero, frn, frm); tmp = tcg_temp_new_i32(); tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, dest, frm); - tcg_temp_free_i32(tmp); break; } /* For fp16 the top half is always zeroes */ @@ -398,9 +403,6 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) tcg_gen_andi_i32(dest, dest, 0xffff); } vfp_store_reg32(dest, rd); - tcg_temp_free_i32(frn); - tcg_temp_free_i32(frm); - tcg_temp_free_i32(dest); } return true; @@ -457,8 +459,7 @@ static bool trans_VRINT(DisasContext *s, arg_VRINT *a) fpst = fpstatus_ptr(FPST_FPCR); } - tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rounding)); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + tcg_rmode = gen_set_rmode(rounding, fpst); if (sz == 3) { TCGv_i64 tcg_op; @@ -468,8 +469,6 @@ static bool trans_VRINT(DisasContext *s, arg_VRINT *a) vfp_load_reg64(tcg_op, rm); gen_helper_rintd(tcg_res, tcg_op, fpst); vfp_store_reg64(tcg_res, rd); - tcg_temp_free_i64(tcg_op); - tcg_temp_free_i64(tcg_res); } else { TCGv_i32 tcg_op; TCGv_i32 tcg_res; @@ -482,14 +481,9 @@ static bool trans_VRINT(DisasContext *s, arg_VRINT *a) gen_helper_rints(tcg_res, tcg_op, fpst); } vfp_store_reg32(tcg_res, rd); - tcg_temp_free_i32(tcg_op); - tcg_temp_free_i32(tcg_res); } - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); - tcg_temp_free_i32(tcg_rmode); - - tcg_temp_free_ptr(fpst); + gen_restore_rmode(tcg_rmode, fpst); return true; } @@ -533,9 +527,7 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a) } tcg_shift = tcg_constant_i32(0); - - tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rounding)); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + tcg_rmode = gen_set_rmode(rounding, fpst); if (sz == 3) { TCGv_i64 tcg_double, tcg_res; @@ -551,9 +543,6 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a) } tcg_gen_extrl_i64_i32(tcg_tmp, tcg_res); vfp_store_reg32(tcg_tmp, rd); - tcg_temp_free_i32(tcg_tmp); - tcg_temp_free_i64(tcg_res); - tcg_temp_free_i64(tcg_double); } else { TCGv_i32 tcg_single, tcg_res; tcg_single = tcg_temp_new_i32(); @@ -573,15 +562,9 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a) } } vfp_store_reg32(tcg_res, rd); - tcg_temp_free_i32(tcg_res); - tcg_temp_free_i32(tcg_single); } - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); - tcg_temp_free_i32(tcg_rmode); - - tcg_temp_free_ptr(fpst); - + gen_restore_rmode(tcg_rmode, fpst); return true; } @@ -707,7 +690,6 @@ static bool trans_VMOV_from_gp(DisasContext *s, arg_VMOV_from_gp *a) if (!mve_skip_vmov(s, a->vn, a->index, a->size)) { tmp = load_reg(s, a->rt); write_neon_element32(tmp, a->vn, a->index, a->size); - tcg_temp_free_i32(tmp); } if (dc_isar_feature(aa32_mve, s)) { @@ -755,8 +737,6 @@ static bool trans_VDUP(DisasContext *s, arg_VDUP *a) tmp = load_reg(s, a->rt); tcg_gen_gvec_dup_i32(size, neon_full_reg_offset(a->vn), vec_size, vec_size, tmp); - tcg_temp_free_i32(tmp); - return true; } @@ -834,8 +814,8 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a) case ARM_VFP_FPSID: if (s->current_el == 1) { gen_set_condexec(s); - gen_set_pc_im(s, s->pc_curr); - gen_helper_check_hcr_el2_trap(cpu_env, + gen_update_pc(s, 0); + gen_helper_check_hcr_el2_trap(tcg_env, tcg_constant_i32(a->rt), tcg_constant_i32(a->reg)); } @@ -851,7 +831,7 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a) tcg_gen_andi_i32(tmp, tmp, FPCR_NZCV_MASK); } else { tmp = tcg_temp_new_i32(); - gen_helper_vfp_get_fpscr(tmp, cpu_env); + gen_helper_vfp_get_fpscr(tmp, tcg_env); } break; default: @@ -861,7 +841,6 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a) if (a->rt == 15) { /* Set the 4 flag bits in the CPSR. */ gen_set_nzcv(tmp); - tcg_temp_free_i32(tmp); } else { store_reg(s, a->rt, tmp); } @@ -876,8 +855,7 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a) break; case ARM_VFP_FPSCR: tmp = load_reg(s, a->rt); - gen_helper_vfp_set_fpscr(cpu_env, tmp); - tcg_temp_free_i32(tmp); + gen_helper_vfp_set_fpscr(tcg_env, tmp); gen_lookup_tb(s); break; case ARM_VFP_FPEXC: @@ -932,7 +910,6 @@ static bool trans_VMOV_half(DisasContext *s, arg_VMOV_single *a) tmp = load_reg(s, a->rt); tcg_gen_andi_i32(tmp, tmp, 0xffff); vfp_store_reg32(tmp, a->vn); - tcg_temp_free_i32(tmp); } return true; @@ -957,7 +934,6 @@ static bool trans_VMOV_single(DisasContext *s, arg_VMOV_single *a) if (a->rt == 15) { /* Set the 4 flag bits in the CPSR. */ gen_set_nzcv(tmp); - tcg_temp_free_i32(tmp); } else { store_reg(s, a->rt, tmp); } @@ -965,7 +941,6 @@ static bool trans_VMOV_single(DisasContext *s, arg_VMOV_single *a) /* general purpose register to VFP */ tmp = load_reg(s, a->rt); vfp_store_reg32(tmp, a->vn); - tcg_temp_free_i32(tmp); } return true; @@ -999,10 +974,8 @@ static bool trans_VMOV_64_sp(DisasContext *s, arg_VMOV_64_sp *a) /* gpreg to fpreg */ tmp = load_reg(s, a->rt); vfp_store_reg32(tmp, a->vm); - tcg_temp_free_i32(tmp); tmp = load_reg(s, a->rt2); vfp_store_reg32(tmp, a->vm + 1); - tcg_temp_free_i32(tmp); } return true; @@ -1042,10 +1015,8 @@ static bool trans_VMOV_64_dp(DisasContext *s, arg_VMOV_64_dp *a) /* gpreg to fpreg */ tmp = load_reg(s, a->rt); vfp_store_reg32(tmp, a->vm * 2); - tcg_temp_free_i32(tmp); tmp = load_reg(s, a->rt2); vfp_store_reg32(tmp, a->vm * 2 + 1); - tcg_temp_free_i32(tmp); } return true; @@ -1080,9 +1051,6 @@ static bool trans_VLDR_VSTR_hp(DisasContext *s, arg_VLDR_VSTR_sp *a) vfp_load_reg32(tmp, a->vd); gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), MO_UW | MO_ALIGN); } - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(addr); - return true; } @@ -1114,9 +1082,6 @@ static bool trans_VLDR_VSTR_sp(DisasContext *s, arg_VLDR_VSTR_sp *a) vfp_load_reg32(tmp, a->vd); gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), MO_UL | MO_ALIGN); } - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(addr); - return true; } @@ -1155,9 +1120,6 @@ static bool trans_VLDR_VSTR_dp(DisasContext *s, arg_VLDR_VSTR_dp *a) vfp_load_reg64(tmp, a->vd); gen_aa32_st_i64(s, tmp, addr, get_mem_index(s), MO_UQ | MO_ALIGN_4); } - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(addr); - return true; } @@ -1207,7 +1169,7 @@ static bool trans_VLDM_VSTM_sp(DisasContext *s, arg_VLDM_VSTM_sp *a) * value is above, it is UNKNOWN whether the limit check * triggers; we choose to trigger. */ - gen_helper_v8m_stackcheck(cpu_env, addr); + gen_helper_v8m_stackcheck(tcg_env, addr); } offset = 4; @@ -1224,7 +1186,6 @@ static bool trans_VLDM_VSTM_sp(DisasContext *s, arg_VLDM_VSTM_sp *a) } tcg_gen_addi_i32(addr, addr, offset); } - tcg_temp_free_i32(tmp); if (a->w) { /* writeback */ if (a->p) { @@ -1232,8 +1193,6 @@ static bool trans_VLDM_VSTM_sp(DisasContext *s, arg_VLDM_VSTM_sp *a) tcg_gen_addi_i32(addr, addr, offset); } store_reg(s, a->rn, addr); - } else { - tcg_temp_free_i32(addr); } clear_eci_state(s); @@ -1293,7 +1252,7 @@ static bool trans_VLDM_VSTM_dp(DisasContext *s, arg_VLDM_VSTM_dp *a) * value is above, it is UNKNOWN whether the limit check * triggers; we choose to trigger. */ - gen_helper_v8m_stackcheck(cpu_env, addr); + gen_helper_v8m_stackcheck(tcg_env, addr); } offset = 8; @@ -1310,7 +1269,6 @@ static bool trans_VLDM_VSTM_dp(DisasContext *s, arg_VLDM_VSTM_dp *a) } tcg_gen_addi_i32(addr, addr, offset); } - tcg_temp_free_i64(tmp); if (a->w) { /* writeback */ if (a->p) { @@ -1325,8 +1283,6 @@ static bool trans_VLDM_VSTM_dp(DisasContext *s, arg_VLDM_VSTM_dp *a) tcg_gen_addi_i32(addr, addr, offset); } store_reg(s, a->rn, addr); - } else { - tcg_temp_free_i32(addr); } clear_eci_state(s); @@ -1463,12 +1419,6 @@ static bool do_vfp_3op_sp(DisasContext *s, VFPGen3OpSPFn *fn, vfp_load_reg32(f1, vm); } } - - tcg_temp_free_i32(f0); - tcg_temp_free_i32(f1); - tcg_temp_free_i32(fd); - tcg_temp_free_ptr(fpst); - return true; } @@ -1511,12 +1461,6 @@ static bool do_vfp_3op_hp(DisasContext *s, VFPGen3OpSPFn *fn, } fn(fd, f0, f1, fpst); vfp_store_reg32(fd, vd); - - tcg_temp_free_i32(f0); - tcg_temp_free_i32(f1); - tcg_temp_free_i32(fd); - tcg_temp_free_ptr(fpst); - return true; } @@ -1593,12 +1537,6 @@ static bool do_vfp_3op_dp(DisasContext *s, VFPGen3OpDPFn *fn, vfp_load_reg64(f1, vm); } } - - tcg_temp_free_i64(f0); - tcg_temp_free_i64(f1); - tcg_temp_free_i64(fd); - tcg_temp_free_ptr(fpst); - return true; } @@ -1666,10 +1604,6 @@ static bool do_vfp_2op_sp(DisasContext *s, VFPGen2OpSPFn *fn, int vd, int vm) vm = vfp_advance_sreg(vm, delta_m); vfp_load_reg32(f0, vm); } - - tcg_temp_free_i32(f0); - tcg_temp_free_i32(fd); - return true; } @@ -1702,7 +1636,6 @@ static bool do_vfp_2op_hp(DisasContext *s, VFPGen2OpSPFn *fn, int vd, int vm) vfp_load_reg32(f0, vm); fn(f0, f0); vfp_store_reg32(f0, vd); - tcg_temp_free_i32(f0); return true; } @@ -1776,10 +1709,6 @@ static bool do_vfp_2op_dp(DisasContext *s, VFPGen2OpDPFn *fn, int vd, int vm) vd = vfp_advance_dreg(vm, delta_m); vfp_load_reg64(f0, vm); } - - tcg_temp_free_i64(f0); - tcg_temp_free_i64(fd); - return true; } @@ -1790,7 +1719,6 @@ static void gen_VMLA_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) gen_helper_vfp_mulh(tmp, vn, vm, fpst); gen_helper_vfp_addh(vd, vd, tmp, fpst); - tcg_temp_free_i32(tmp); } static bool trans_VMLA_hp(DisasContext *s, arg_VMLA_sp *a) @@ -1805,7 +1733,6 @@ static void gen_VMLA_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) gen_helper_vfp_muls(tmp, vn, vm, fpst); gen_helper_vfp_adds(vd, vd, tmp, fpst); - tcg_temp_free_i32(tmp); } static bool trans_VMLA_sp(DisasContext *s, arg_VMLA_sp *a) @@ -1820,7 +1747,6 @@ static void gen_VMLA_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst) gen_helper_vfp_muld(tmp, vn, vm, fpst); gen_helper_vfp_addd(vd, vd, tmp, fpst); - tcg_temp_free_i64(tmp); } static bool trans_VMLA_dp(DisasContext *s, arg_VMLA_dp *a) @@ -1839,7 +1765,6 @@ static void gen_VMLS_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) gen_helper_vfp_mulh(tmp, vn, vm, fpst); gen_helper_vfp_negh(tmp, tmp); gen_helper_vfp_addh(vd, vd, tmp, fpst); - tcg_temp_free_i32(tmp); } static bool trans_VMLS_hp(DisasContext *s, arg_VMLS_sp *a) @@ -1858,7 +1783,6 @@ static void gen_VMLS_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) gen_helper_vfp_muls(tmp, vn, vm, fpst); gen_helper_vfp_negs(tmp, tmp); gen_helper_vfp_adds(vd, vd, tmp, fpst); - tcg_temp_free_i32(tmp); } static bool trans_VMLS_sp(DisasContext *s, arg_VMLS_sp *a) @@ -1877,7 +1801,6 @@ static void gen_VMLS_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst) gen_helper_vfp_muld(tmp, vn, vm, fpst); gen_helper_vfp_negd(tmp, tmp); gen_helper_vfp_addd(vd, vd, tmp, fpst); - tcg_temp_free_i64(tmp); } static bool trans_VMLS_dp(DisasContext *s, arg_VMLS_dp *a) @@ -1898,7 +1821,6 @@ static void gen_VNMLS_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) gen_helper_vfp_mulh(tmp, vn, vm, fpst); gen_helper_vfp_negh(vd, vd); gen_helper_vfp_addh(vd, vd, tmp, fpst); - tcg_temp_free_i32(tmp); } static bool trans_VNMLS_hp(DisasContext *s, arg_VNMLS_sp *a) @@ -1919,7 +1841,6 @@ static void gen_VNMLS_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) gen_helper_vfp_muls(tmp, vn, vm, fpst); gen_helper_vfp_negs(vd, vd); gen_helper_vfp_adds(vd, vd, tmp, fpst); - tcg_temp_free_i32(tmp); } static bool trans_VNMLS_sp(DisasContext *s, arg_VNMLS_sp *a) @@ -1940,7 +1861,6 @@ static void gen_VNMLS_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst) gen_helper_vfp_muld(tmp, vn, vm, fpst); gen_helper_vfp_negd(vd, vd); gen_helper_vfp_addd(vd, vd, tmp, fpst); - tcg_temp_free_i64(tmp); } static bool trans_VNMLS_dp(DisasContext *s, arg_VNMLS_dp *a) @@ -1957,7 +1877,6 @@ static void gen_VNMLA_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) gen_helper_vfp_negh(tmp, tmp); gen_helper_vfp_negh(vd, vd); gen_helper_vfp_addh(vd, vd, tmp, fpst); - tcg_temp_free_i32(tmp); } static bool trans_VNMLA_hp(DisasContext *s, arg_VNMLA_sp *a) @@ -1974,7 +1893,6 @@ static void gen_VNMLA_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) gen_helper_vfp_negs(tmp, tmp); gen_helper_vfp_negs(vd, vd); gen_helper_vfp_adds(vd, vd, tmp, fpst); - tcg_temp_free_i32(tmp); } static bool trans_VNMLA_sp(DisasContext *s, arg_VNMLA_sp *a) @@ -1991,7 +1909,6 @@ static void gen_VNMLA_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst) gen_helper_vfp_negd(tmp, tmp); gen_helper_vfp_negd(vd, vd); gen_helper_vfp_addd(vd, vd, tmp, fpst); - tcg_temp_free_i64(tmp); } static bool trans_VNMLA_dp(DisasContext *s, arg_VNMLA_dp *a) @@ -2203,12 +2120,6 @@ static bool do_vfm_hp(DisasContext *s, arg_VFMA_sp *a, bool neg_n, bool neg_d) fpst = fpstatus_ptr(FPST_FPCR_F16); gen_helper_vfp_muladdh(vd, vn, vm, vd, fpst); vfp_store_reg32(vd, a->vd); - - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(vn); - tcg_temp_free_i32(vm); - tcg_temp_free_i32(vd); - return true; } @@ -2268,12 +2179,6 @@ static bool do_vfm_sp(DisasContext *s, arg_VFMA_sp *a, bool neg_n, bool neg_d) fpst = fpstatus_ptr(FPST_FPCR); gen_helper_vfp_muladds(vd, vn, vm, vd, fpst); vfp_store_reg32(vd, a->vd); - - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(vn); - tcg_temp_free_i32(vm); - tcg_temp_free_i32(vd); - return true; } @@ -2339,12 +2244,6 @@ static bool do_vfm_dp(DisasContext *s, arg_VFMA_dp *a, bool neg_n, bool neg_d) fpst = fpstatus_ptr(FPST_FPCR); gen_helper_vfp_muladdd(vd, vn, vm, vd, fpst); vfp_store_reg64(vd, a->vd); - - tcg_temp_free_ptr(fpst); - tcg_temp_free_i64(vn); - tcg_temp_free_i64(vm); - tcg_temp_free_i64(vd); - return true; } @@ -2520,17 +2419,17 @@ DO_VFP_2OP(VNEG, dp, gen_helper_vfp_negd, aa32_fpdp_v2) static void gen_VSQRT_hp(TCGv_i32 vd, TCGv_i32 vm) { - gen_helper_vfp_sqrth(vd, vm, cpu_env); + gen_helper_vfp_sqrth(vd, vm, tcg_env); } static void gen_VSQRT_sp(TCGv_i32 vd, TCGv_i32 vm) { - gen_helper_vfp_sqrts(vd, vm, cpu_env); + gen_helper_vfp_sqrts(vd, vm, tcg_env); } static void gen_VSQRT_dp(TCGv_i64 vd, TCGv_i64 vm) { - gen_helper_vfp_sqrtd(vd, vm, cpu_env); + gen_helper_vfp_sqrtd(vd, vm, tcg_env); } DO_VFP_2OP(VSQRT, hp, gen_VSQRT_hp, aa32_fp16_arith) @@ -2565,14 +2464,10 @@ static bool trans_VCMP_hp(DisasContext *s, arg_VCMP_sp *a) } if (a->e) { - gen_helper_vfp_cmpeh(vd, vm, cpu_env); + gen_helper_vfp_cmpeh(vd, vm, tcg_env); } else { - gen_helper_vfp_cmph(vd, vm, cpu_env); + gen_helper_vfp_cmph(vd, vm, tcg_env); } - - tcg_temp_free_i32(vd); - tcg_temp_free_i32(vm); - return true; } @@ -2604,14 +2499,10 @@ static bool trans_VCMP_sp(DisasContext *s, arg_VCMP_sp *a) } if (a->e) { - gen_helper_vfp_cmpes(vd, vm, cpu_env); + gen_helper_vfp_cmpes(vd, vm, tcg_env); } else { - gen_helper_vfp_cmps(vd, vm, cpu_env); + gen_helper_vfp_cmps(vd, vm, tcg_env); } - - tcg_temp_free_i32(vd); - tcg_temp_free_i32(vm); - return true; } @@ -2648,14 +2539,10 @@ static bool trans_VCMP_dp(DisasContext *s, arg_VCMP_dp *a) } if (a->e) { - gen_helper_vfp_cmped(vd, vm, cpu_env); + gen_helper_vfp_cmped(vd, vm, tcg_env); } else { - gen_helper_vfp_cmpd(vd, vm, cpu_env); + gen_helper_vfp_cmpd(vd, vm, tcg_env); } - - tcg_temp_free_i64(vd); - tcg_temp_free_i64(vm); - return true; } @@ -2677,12 +2564,9 @@ static bool trans_VCVT_f32_f16(DisasContext *s, arg_VCVT_f32_f16 *a) ahp_mode = get_ahp_flag(); tmp = tcg_temp_new_i32(); /* The T bit tells us if we want the low or high 16 bits of Vm */ - tcg_gen_ld16u_i32(tmp, cpu_env, vfp_f16_offset(a->vm, a->t)); + tcg_gen_ld16u_i32(tmp, tcg_env, vfp_f16_offset(a->vm, a->t)); gen_helper_vfp_fcvt_f16_to_f32(tmp, tmp, fpst, ahp_mode); vfp_store_reg32(tmp, a->vd); - tcg_temp_free_i32(ahp_mode); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tmp); return true; } @@ -2714,14 +2598,10 @@ static bool trans_VCVT_f64_f16(DisasContext *s, arg_VCVT_f64_f16 *a) ahp_mode = get_ahp_flag(); tmp = tcg_temp_new_i32(); /* The T bit tells us if we want the low or high 16 bits of Vm */ - tcg_gen_ld16u_i32(tmp, cpu_env, vfp_f16_offset(a->vm, a->t)); + tcg_gen_ld16u_i32(tmp, tcg_env, vfp_f16_offset(a->vm, a->t)); vd = tcg_temp_new_i64(); gen_helper_vfp_fcvt_f16_to_f64(vd, tmp, fpst, ahp_mode); vfp_store_reg64(vd, a->vd); - tcg_temp_free_i32(ahp_mode); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tmp); - tcg_temp_free_i64(vd); return true; } @@ -2743,9 +2623,7 @@ static bool trans_VCVT_b16_f32(DisasContext *s, arg_VCVT_b16_f32 *a) vfp_load_reg32(tmp, a->vm); gen_helper_bfcvt(tmp, tmp, fpst); - tcg_gen_st16_i32(tmp, cpu_env, vfp_f16_offset(a->vd, a->t)); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tmp); + tcg_gen_st16_i32(tmp, tcg_env, vfp_f16_offset(a->vd, a->t)); return true; } @@ -2769,10 +2647,7 @@ static bool trans_VCVT_f16_f32(DisasContext *s, arg_VCVT_f16_f32 *a) vfp_load_reg32(tmp, a->vm); gen_helper_vfp_fcvt_f32_to_f16(tmp, tmp, fpst, ahp_mode); - tcg_gen_st16_i32(tmp, cpu_env, vfp_f16_offset(a->vd, a->t)); - tcg_temp_free_i32(ahp_mode); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tmp); + tcg_gen_st16_i32(tmp, tcg_env, vfp_f16_offset(a->vd, a->t)); return true; } @@ -2807,11 +2682,7 @@ static bool trans_VCVT_f16_f64(DisasContext *s, arg_VCVT_f16_f64 *a) vfp_load_reg64(vm, a->vm); gen_helper_vfp_fcvt_f64_to_f16(tmp, vm, fpst, ahp_mode); - tcg_temp_free_i64(vm); - tcg_gen_st16_i32(tmp, cpu_env, vfp_f16_offset(a->vd, a->t)); - tcg_temp_free_i32(ahp_mode); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tmp); + tcg_gen_st16_i32(tmp, tcg_env, vfp_f16_offset(a->vd, a->t)); return true; } @@ -2833,8 +2704,6 @@ static bool trans_VRINTR_hp(DisasContext *s, arg_VRINTR_sp *a) fpst = fpstatus_ptr(FPST_FPCR_F16); gen_helper_rinth(tmp, tmp, fpst); vfp_store_reg32(tmp, a->vd); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tmp); return true; } @@ -2856,8 +2725,6 @@ static bool trans_VRINTR_sp(DisasContext *s, arg_VRINTR_sp *a) fpst = fpstatus_ptr(FPST_FPCR); gen_helper_rints(tmp, tmp, fpst); vfp_store_reg32(tmp, a->vd); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tmp); return true; } @@ -2888,8 +2755,6 @@ static bool trans_VRINTR_dp(DisasContext *s, arg_VRINTR_dp *a) fpst = fpstatus_ptr(FPST_FPCR); gen_helper_rintd(tmp, tmp, fpst); vfp_store_reg64(tmp, a->vd); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i64(tmp); return true; } @@ -2910,14 +2775,10 @@ static bool trans_VRINTZ_hp(DisasContext *s, arg_VRINTZ_sp *a) tmp = tcg_temp_new_i32(); vfp_load_reg32(tmp, a->vm); fpst = fpstatus_ptr(FPST_FPCR_F16); - tcg_rmode = tcg_const_i32(float_round_to_zero); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + tcg_rmode = gen_set_rmode(FPROUNDING_ZERO, fpst); gen_helper_rinth(tmp, tmp, fpst); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + gen_restore_rmode(tcg_rmode, fpst); vfp_store_reg32(tmp, a->vd); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tcg_rmode); - tcg_temp_free_i32(tmp); return true; } @@ -2938,14 +2799,10 @@ static bool trans_VRINTZ_sp(DisasContext *s, arg_VRINTZ_sp *a) tmp = tcg_temp_new_i32(); vfp_load_reg32(tmp, a->vm); fpst = fpstatus_ptr(FPST_FPCR); - tcg_rmode = tcg_const_i32(float_round_to_zero); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + tcg_rmode = gen_set_rmode(FPROUNDING_ZERO, fpst); gen_helper_rints(tmp, tmp, fpst); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + gen_restore_rmode(tcg_rmode, fpst); vfp_store_reg32(tmp, a->vd); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tcg_rmode); - tcg_temp_free_i32(tmp); return true; } @@ -2975,14 +2832,10 @@ static bool trans_VRINTZ_dp(DisasContext *s, arg_VRINTZ_dp *a) tmp = tcg_temp_new_i64(); vfp_load_reg64(tmp, a->vm); fpst = fpstatus_ptr(FPST_FPCR); - tcg_rmode = tcg_const_i32(float_round_to_zero); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + tcg_rmode = gen_set_rmode(FPROUNDING_ZERO, fpst); gen_helper_rintd(tmp, tmp, fpst); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + gen_restore_rmode(tcg_rmode, fpst); vfp_store_reg64(tmp, a->vd); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tcg_rmode); return true; } @@ -3004,8 +2857,6 @@ static bool trans_VRINTX_hp(DisasContext *s, arg_VRINTX_sp *a) fpst = fpstatus_ptr(FPST_FPCR_F16); gen_helper_rinth_exact(tmp, tmp, fpst); vfp_store_reg32(tmp, a->vd); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tmp); return true; } @@ -3027,8 +2878,6 @@ static bool trans_VRINTX_sp(DisasContext *s, arg_VRINTX_sp *a) fpst = fpstatus_ptr(FPST_FPCR); gen_helper_rints_exact(tmp, tmp, fpst); vfp_store_reg32(tmp, a->vd); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i32(tmp); return true; } @@ -3059,8 +2908,6 @@ static bool trans_VRINTX_dp(DisasContext *s, arg_VRINTX_dp *a) fpst = fpstatus_ptr(FPST_FPCR); gen_helper_rintd_exact(tmp, tmp, fpst); vfp_store_reg64(tmp, a->vd); - tcg_temp_free_ptr(fpst); - tcg_temp_free_i64(tmp); return true; } @@ -3085,10 +2932,8 @@ static bool trans_VCVT_sp(DisasContext *s, arg_VCVT_sp *a) vm = tcg_temp_new_i32(); vd = tcg_temp_new_i64(); vfp_load_reg32(vm, a->vm); - gen_helper_vfp_fcvtds(vd, vm, cpu_env); + gen_helper_vfp_fcvtds(vd, vm, tcg_env); vfp_store_reg64(vd, a->vd); - tcg_temp_free_i32(vm); - tcg_temp_free_i64(vd); return true; } @@ -3113,10 +2958,8 @@ static bool trans_VCVT_dp(DisasContext *s, arg_VCVT_dp *a) vd = tcg_temp_new_i32(); vm = tcg_temp_new_i64(); vfp_load_reg64(vm, a->vm); - gen_helper_vfp_fcvtsd(vd, vm, cpu_env); + gen_helper_vfp_fcvtsd(vd, vm, tcg_env); vfp_store_reg32(vd, a->vd); - tcg_temp_free_i32(vd); - tcg_temp_free_i64(vm); return true; } @@ -3144,8 +2987,6 @@ static bool trans_VCVT_int_hp(DisasContext *s, arg_VCVT_int_sp *a) gen_helper_vfp_uitoh(vm, vm, fpst); } vfp_store_reg32(vm, a->vd); - tcg_temp_free_i32(vm); - tcg_temp_free_ptr(fpst); return true; } @@ -3173,8 +3014,6 @@ static bool trans_VCVT_int_sp(DisasContext *s, arg_VCVT_int_sp *a) gen_helper_vfp_uitos(vm, vm, fpst); } vfp_store_reg32(vm, a->vd); - tcg_temp_free_i32(vm); - tcg_temp_free_ptr(fpst); return true; } @@ -3209,9 +3048,6 @@ static bool trans_VCVT_int_dp(DisasContext *s, arg_VCVT_int_dp *a) gen_helper_vfp_uitod(vd, vm, fpst); } vfp_store_reg64(vd, a->vd); - tcg_temp_free_i32(vm); - tcg_temp_free_i64(vd); - tcg_temp_free_ptr(fpst); return true; } @@ -3240,10 +3076,8 @@ static bool trans_VJCVT(DisasContext *s, arg_VJCVT *a) vm = tcg_temp_new_i64(); vd = tcg_temp_new_i32(); vfp_load_reg64(vm, a->vm); - gen_helper_vjcvt(vd, vm, cpu_env); + gen_helper_vjcvt(vd, vm, tcg_env); vfp_store_reg32(vd, a->vd); - tcg_temp_free_i64(vm); - tcg_temp_free_i32(vd); return true; } @@ -3300,8 +3134,6 @@ static bool trans_VCVT_fix_hp(DisasContext *s, arg_VCVT_fix_sp *a) } vfp_store_reg32(vd, a->vd); - tcg_temp_free_i32(vd); - tcg_temp_free_ptr(fpst); return true; } @@ -3358,8 +3190,6 @@ static bool trans_VCVT_fix_sp(DisasContext *s, arg_VCVT_fix_sp *a) } vfp_store_reg32(vd, a->vd); - tcg_temp_free_i32(vd); - tcg_temp_free_ptr(fpst); return true; } @@ -3422,8 +3252,6 @@ static bool trans_VCVT_fix_dp(DisasContext *s, arg_VCVT_fix_dp *a) } vfp_store_reg64(vd, a->vd); - tcg_temp_free_i64(vd); - tcg_temp_free_ptr(fpst); return true; } @@ -3458,8 +3286,6 @@ static bool trans_VCVT_hp_int(DisasContext *s, arg_VCVT_sp_int *a) } } vfp_store_reg32(vm, a->vd); - tcg_temp_free_i32(vm); - tcg_temp_free_ptr(fpst); return true; } @@ -3494,8 +3320,6 @@ static bool trans_VCVT_sp_int(DisasContext *s, arg_VCVT_sp_int *a) } } vfp_store_reg32(vm, a->vd); - tcg_temp_free_i32(vm); - tcg_temp_free_ptr(fpst); return true; } @@ -3537,9 +3361,6 @@ static bool trans_VCVT_dp_int(DisasContext *s, arg_VCVT_dp_int *a) } } vfp_store_reg32(vd, a->vd); - tcg_temp_free_i32(vd); - tcg_temp_free_i64(vm); - tcg_temp_free_ptr(fpst); return true; } @@ -3566,8 +3387,6 @@ static bool trans_VINS(DisasContext *s, arg_VINS *a) vfp_load_reg32(rd, a->vd); tcg_gen_deposit_i32(rd, rd, rm, 16, 16); vfp_store_reg32(rd, a->vd); - tcg_temp_free_i32(rm); - tcg_temp_free_i32(rd); return true; } @@ -3592,6 +3411,5 @@ static bool trans_VMOVX(DisasContext *s, arg_VINS *a) vfp_load_reg32(rm, a->vm); tcg_gen_shri_i32(rm, rm, 16); vfp_store_reg32(rm, a->vd); - tcg_temp_free_i32(rm); return true; } diff --git a/target/arm/translate.c b/target/arm/tcg/translate.c similarity index 88% rename from target/arm/translate.c rename to target/arm/tcg/translate.c index 87a899d638..dc49a8d806 100644 --- a/target/arm/translate.c +++ b/target/arm/tcg/translate.c @@ -20,21 +20,18 @@ */ #include "qemu/osdep.h" -#include "cpu.h" -#include "internals.h" -#include "disas/disas.h" -#include "exec/exec-all.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" +#include "translate.h" +#include "translate-a32.h" #include "qemu/log.h" -#include "qemu/bitops.h" +#include "disas/disas.h" #include "arm_ldst.h" #include "semihosting/semihost.h" -#include "exec/helper-proto.h" -#include "exec/helper-gen.h" -#include "exec/log.h" #include "cpregs.h" +#include "exec/helper-proto.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H #define ENABLE_ARCH_4T arm_dc_feature(s, ARM_FEATURE_V4T) #define ENABLE_ARCH_5 arm_dc_feature(s, ARM_FEATURE_V5) @@ -47,9 +44,6 @@ #define ENABLE_ARCH_7 arm_dc_feature(s, ARM_FEATURE_V7) #define ENABLE_ARCH_8 arm_dc_feature(s, ARM_FEATURE_V8) -#include "translate.h" -#include "translate-a32.h" - /* These are TCG temporaries used only by the legacy iwMMXt decoder */ static TCGv_i64 cpu_V0, cpu_V1, cpu_M0; /* These are TCG globals which alias CPUARMState fields */ @@ -58,8 +52,6 @@ TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF; TCGv_i64 cpu_exclusive_addr; TCGv_i64 cpu_exclusive_val; -#include "exec/gen-icount.h" - static const char * const regnames[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "pc" }; @@ -71,18 +63,18 @@ void arm_translate_init(void) int i; for (i = 0; i < 16; i++) { - cpu_R[i] = tcg_global_mem_new_i32(cpu_env, + cpu_R[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUARMState, regs[i]), regnames[i]); } - cpu_CF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, CF), "CF"); - cpu_NF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, NF), "NF"); - cpu_VF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, VF), "VF"); - cpu_ZF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, ZF), "ZF"); + cpu_CF = tcg_global_mem_new_i32(tcg_env, offsetof(CPUARMState, CF), "CF"); + cpu_NF = tcg_global_mem_new_i32(tcg_env, offsetof(CPUARMState, NF), "NF"); + cpu_VF = tcg_global_mem_new_i32(tcg_env, offsetof(CPUARMState, VF), "VF"); + cpu_ZF = tcg_global_mem_new_i32(tcg_env, offsetof(CPUARMState, ZF), "ZF"); - cpu_exclusive_addr = tcg_global_mem_new_i64(cpu_env, + cpu_exclusive_addr = tcg_global_mem_new_i64(tcg_env, offsetof(CPUARMState, exclusive_addr), "exclusive_addr"); - cpu_exclusive_val = tcg_global_mem_new_i64(cpu_env, + cpu_exclusive_val = tcg_global_mem_new_i64(tcg_env, offsetof(CPUARMState, exclusive_val), "exclusive_val"); a64_translate_init(); @@ -162,7 +154,7 @@ uint64_t asimd_imm_const(uint32_t imm, int cmode, int op) void arm_gen_condlabel(DisasContext *s) { if (!s->condjmp) { - s->condlabel = gen_new_label(); + s->condlabel = gen_disas_label(s); s->condjmp = 1; } } @@ -187,15 +179,14 @@ void store_cpu_offset(TCGv_i32 var, int offset, int size) { switch (size) { case 1: - tcg_gen_st8_i32(var, cpu_env, offset); + tcg_gen_st8_i32(var, tcg_env, offset); break; case 4: - tcg_gen_st_i32(var, cpu_env, offset); + tcg_gen_st_i32(var, tcg_env, offset); break; default: g_assert_not_reached(); } - tcg_temp_free_i32(var); } /* Save the syndrome information for a Data Abort */ @@ -237,16 +228,12 @@ static inline int get_a32_user_mem_index(DisasContext *s) * otherwise, access as if at PL0. */ switch (s->mmu_idx) { + case ARMMMUIdx_E3: 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); @@ -264,17 +251,27 @@ static inline int get_a32_user_mem_index(DisasContext *s) } } -/* The architectural value of PC. */ -static uint32_t read_pc(DisasContext *s) +/* The pc_curr difference for an architectural jump. */ +static target_long jmp_diff(DisasContext *s, target_long diff) { - return s->pc_curr + (s->thumb ? 4 : 8); + return diff + (s->thumb ? 4 : 8); +} + +static void gen_pc_plus_diff(DisasContext *s, TCGv_i32 var, target_long diff) +{ + assert(s->pc_save != -1); + if (tb_cflags(s->base.tb) & CF_PCREL) { + tcg_gen_addi_i32(var, cpu_R[15], (s->pc_curr - s->pc_save) + diff); + } else { + tcg_gen_movi_i32(var, s->pc_curr + diff); + } } /* Set a variable to the value of a CPU register. */ void load_reg_var(DisasContext *s, TCGv_i32 var, int reg) { if (reg == 15) { - tcg_gen_movi_i32(var, read_pc(s)); + gen_pc_plus_diff(s, var, jmp_diff(s, 0)); } else { tcg_gen_mov_i32(var, cpu_R[reg]); } @@ -290,7 +287,11 @@ TCGv_i32 add_reg_for_lit(DisasContext *s, int reg, int ofs) TCGv_i32 tmp = tcg_temp_new_i32(); if (reg == 15) { - tcg_gen_movi_i32(tmp, (read_pc(s) & ~3) + ofs); + /* + * This address is computed from an aligned PC: + * subtract off the low bits. + */ + gen_pc_plus_diff(s, tmp, jmp_diff(s, ofs - (s->pc_curr & 3))); } else { tcg_gen_addi_i32(tmp, cpu_R[reg], ofs); } @@ -309,12 +310,12 @@ void store_reg(DisasContext *s, int reg, TCGv_i32 var) */ tcg_gen_andi_i32(var, var, s->thumb ? ~1 : ~3); s->base.is_jmp = DISAS_JUMP; + s->pc_save = -1; } else if (reg == 13 && arm_dc_feature(s, ARM_FEATURE_M)) { /* For M-profile SP bits [1:0] are always zero */ tcg_gen_andi_i32(var, var, ~3); } tcg_gen_mov_i32(cpu_R[reg], var); - tcg_temp_free_i32(var); } /* @@ -328,7 +329,7 @@ static void store_sp_checked(DisasContext *s, TCGv_i32 var) { #ifndef CONFIG_USER_ONLY if (s->v8m_stackcheck) { - gen_helper_v8m_stackcheck(cpu_env, var); + gen_helper_v8m_stackcheck(tcg_env, var); } #endif store_reg(s, 13, var); @@ -345,7 +346,7 @@ static void store_sp_checked(DisasContext *s, TCGv_i32 var) void gen_set_cpsr(TCGv_i32 var, uint32_t mask) { - gen_helper_cpsr_write(cpu_env, var, tcg_constant_i32(mask)); + gen_helper_cpsr_write(tcg_env, var, tcg_constant_i32(mask)); } static void gen_rebuild_hflags(DisasContext *s, bool new_el) @@ -354,16 +355,16 @@ static void gen_rebuild_hflags(DisasContext *s, bool new_el) if (new_el) { if (m_profile) { - gen_helper_rebuild_hflags_m32_newel(cpu_env); + gen_helper_rebuild_hflags_m32_newel(tcg_env); } else { - gen_helper_rebuild_hflags_a32_newel(cpu_env); + gen_helper_rebuild_hflags_a32_newel(tcg_env); } } else { TCGv_i32 tcg_el = tcg_constant_i32(s->current_el); if (m_profile) { - gen_helper_rebuild_hflags_m32(cpu_env, tcg_el); + gen_helper_rebuild_hflags_m32(tcg_env, tcg_el); } else { - gen_helper_rebuild_hflags_a32(cpu_env, tcg_el); + gen_helper_rebuild_hflags_a32(tcg_env, tcg_el); } } } @@ -371,7 +372,7 @@ static void gen_rebuild_hflags(DisasContext *s, bool new_el) static void gen_exception_internal(int excp) { assert(excp_is_internal(excp)); - gen_helper_exception_internal(cpu_env, tcg_constant_i32(excp)); + gen_helper_exception_internal(tcg_env, tcg_constant_i32(excp)); } static void gen_singlestep_exception(DisasContext *s) @@ -409,12 +410,10 @@ static void gen_smul_dual(TCGv_i32 a, TCGv_i32 b) tcg_gen_ext16s_i32(tmp1, a); tcg_gen_ext16s_i32(tmp2, b); tcg_gen_mul_i32(tmp1, tmp1, tmp2); - tcg_temp_free_i32(tmp2); tcg_gen_sari_i32(a, a, 16); tcg_gen_sari_i32(b, b, 16); tcg_gen_mul_i32(b, b, a); tcg_gen_mov_i32(a, tmp1); - tcg_temp_free_i32(tmp1); } /* Byteswap each halfword. */ @@ -427,7 +426,6 @@ void gen_rev16(TCGv_i32 dest, TCGv_i32 var) tcg_gen_and_i32(var, var, mask); tcg_gen_shli_i32(var, var, 8); tcg_gen_or_i32(dest, var, tmp); - tcg_temp_free_i32(tmp); } /* Byteswap low halfword and sign extend. */ @@ -452,7 +450,6 @@ static void gen_add16(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) tcg_gen_andi_i32(t1, t1, ~0x8000); tcg_gen_add_i32(t0, t0, t1); tcg_gen_xor_i32(dest, t0, tmp); - tcg_temp_free_i32(tmp); } /* Set N and Z flags from var. */ @@ -487,7 +484,6 @@ static void gen_add_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) tcg_gen_xor_i32(cpu_VF, cpu_NF, t0); tcg_gen_xor_i32(tmp, t0, t1); tcg_gen_andc_i32(cpu_VF, cpu_VF, tmp); - tcg_temp_free_i32(tmp); tcg_gen_mov_i32(dest, cpu_NF); } @@ -508,14 +504,11 @@ static void gen_adc_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) tcg_gen_extu_i32_i64(q1, cpu_CF); tcg_gen_add_i64(q0, q0, q1); tcg_gen_extr_i64_i32(cpu_NF, cpu_CF, q0); - tcg_temp_free_i64(q0); - tcg_temp_free_i64(q1); } tcg_gen_mov_i32(cpu_ZF, cpu_NF); tcg_gen_xor_i32(cpu_VF, cpu_NF, t0); tcg_gen_xor_i32(tmp, t0, t1); tcg_gen_andc_i32(cpu_VF, cpu_VF, tmp); - tcg_temp_free_i32(tmp); tcg_gen_mov_i32(dest, cpu_NF); } @@ -530,7 +523,6 @@ static void gen_sub_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) tmp = tcg_temp_new_i32(); tcg_gen_xor_i32(tmp, t0, t1); tcg_gen_and_i32(cpu_VF, cpu_VF, tmp); - tcg_temp_free_i32(tmp); tcg_gen_mov_i32(dest, cpu_NF); } @@ -540,7 +532,6 @@ static void gen_sbc_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_not_i32(tmp, t1); gen_adc_CC(dest, t0, tmp); - tcg_temp_free_i32(tmp); } #define GEN_SHIFT(name) \ @@ -553,8 +544,6 @@ static void gen_##name(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) \ tcg_gen_##name##_i32(tmpd, t0, tmp1); \ tcg_gen_andi_i32(tmp1, t1, 0xe0); \ tcg_gen_movcond_i32(TCG_COND_NE, dest, tmp1, zero, zero, tmpd); \ - tcg_temp_free_i32(tmpd); \ - tcg_temp_free_i32(tmp1); \ } GEN_SHIFT(shl) GEN_SHIFT(shr) @@ -567,7 +556,6 @@ static void gen_sar(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) tcg_gen_andi_i32(tmp1, t1, 0xff); tcg_gen_umin_i32(tmp1, tmp1, tcg_constant_i32(31)); tcg_gen_sar_i32(dest, t0, tmp1); - tcg_temp_free_i32(tmp1); } static void shifter_out_im(TCGv_i32 var, int shift) @@ -620,7 +608,6 @@ static inline void gen_arm_shift_im(TCGv_i32 var, int shiftop, shifter_out_im(var, 0); tcg_gen_shri_i32(var, var, 1); tcg_gen_or_i32(var, var, tmp); - tcg_temp_free_i32(tmp); } } }; @@ -630,10 +617,10 @@ static inline void gen_arm_shift_reg(TCGv_i32 var, int shiftop, { if (flags) { switch (shiftop) { - case 0: gen_helper_shl_cc(var, cpu_env, var, shift); break; - case 1: gen_helper_shr_cc(var, cpu_env, var, shift); break; - case 2: gen_helper_sar_cc(var, cpu_env, var, shift); break; - case 3: gen_helper_ror_cc(var, cpu_env, var, shift); break; + case 0: gen_helper_shl_cc(var, tcg_env, var, shift); break; + case 1: gen_helper_shr_cc(var, tcg_env, var, shift); break; + case 2: gen_helper_sar_cc(var, tcg_env, var, shift); break; + case 3: gen_helper_ror_cc(var, tcg_env, var, shift); break; } } else { switch (shiftop) { @@ -650,7 +637,6 @@ static inline void gen_arm_shift_reg(TCGv_i32 var, int shiftop, tcg_gen_rotr_i32(var, var, shift); break; } } - tcg_temp_free_i32(shift); } /* @@ -661,7 +647,6 @@ void arm_test_cc(DisasCompare *cmp, int cc) { TCGv_i32 value; TCGCond cond; - bool global = true; switch (cc) { case 0: /* eq: Z */ @@ -692,7 +677,6 @@ void arm_test_cc(DisasCompare *cmp, int cc) case 9: /* ls: !C || Z -> !(C && !Z) */ cond = TCG_COND_NE; value = tcg_temp_new_i32(); - global = false; /* CF is 1 for C, so -CF is an all-bits-set mask for C; ZF is non-zero for !Z; so AND the two subexpressions. */ tcg_gen_neg_i32(value, cpu_CF); @@ -704,7 +688,6 @@ void arm_test_cc(DisasCompare *cmp, int cc) /* Since we're only interested in the sign bit, == 0 is >= 0. */ cond = TCG_COND_GE; value = tcg_temp_new_i32(); - global = false; tcg_gen_xor_i32(value, cpu_VF, cpu_NF); break; @@ -712,7 +695,6 @@ void arm_test_cc(DisasCompare *cmp, int cc) case 13: /* le: Z || N != V */ cond = TCG_COND_NE; value = tcg_temp_new_i32(); - global = false; /* (N == V) is equal to the sign bit of ~(NF ^ VF). Propagate * the sign bit then AND with ZF to yield the result. */ tcg_gen_xor_i32(value, cpu_VF, cpu_NF); @@ -740,14 +722,6 @@ void arm_test_cc(DisasCompare *cmp, int cc) no_invert: cmp->cond = cond; cmp->value = value; - cmp->value_global = global; -} - -void arm_free_cc(DisasCompare *cmp) -{ - if (!cmp->value_global) { - tcg_temp_free_i32(cmp->value); - } } void arm_jump_cc(DisasCompare *cmp, TCGLabel *label) @@ -760,7 +734,6 @@ void arm_gen_test_cc(int cc, TCGLabel *label) DisasCompare cmp; arm_test_cc(&cmp, cc); arm_jump_cc(&cmp, label); - arm_free_cc(&cmp); } void gen_set_condexec(DisasContext *s) @@ -772,9 +745,10 @@ void gen_set_condexec(DisasContext *s) } } -void gen_set_pc_im(DisasContext *s, target_ulong val) +void gen_update_pc(DisasContext *s, target_long diff) { - tcg_gen_movi_i32(cpu_R[15], val); + gen_pc_plus_diff(s, cpu_R[15], diff); + s->pc_save = s->pc_curr + diff; } /* Set PC and Thumb state from var. var is marked as dead. */ @@ -784,6 +758,7 @@ static inline void gen_bx(DisasContext *s, TCGv_i32 var) tcg_gen_andi_i32(cpu_R[15], var, ~1); tcg_gen_andi_i32(var, var, 1); store_cpu_field(var, thumb); + s->pc_save = -1; } /* @@ -825,7 +800,7 @@ static inline void gen_bx_excret(DisasContext *s, TCGv_i32 var) static inline void gen_bx_excret_final_code(DisasContext *s) { /* Generate the code to finish possible exception return and end the TB */ - TCGLabel *excret_label = gen_new_label(); + DisasLabel excret_label = gen_disas_label(s); uint32_t min_magic; if (arm_dc_feature(s, ARM_FEATURE_M_SECURITY)) { @@ -837,14 +812,14 @@ static inline void gen_bx_excret_final_code(DisasContext *s) } /* Is the new PC value in the magic range indicating exception return? */ - tcg_gen_brcondi_i32(TCG_COND_GEU, cpu_R[15], min_magic, excret_label); + tcg_gen_brcondi_i32(TCG_COND_GEU, cpu_R[15], min_magic, excret_label.label); /* No: end the TB as we would for a DISAS_JMP */ if (s->ss_active) { gen_singlestep_exception(s); } else { tcg_gen_exit_tb(NULL, 0); } - gen_set_label(excret_label); + set_disas_label(s, excret_label); /* Yes: this is an exception return. * At this point in runtime env->regs[15] and env->thumb will hold * the exception-return magic number, which do_v7m_exception_exit() @@ -866,7 +841,7 @@ static inline void gen_bxns(DisasContext *s, int rm) /* The bxns helper may raise an EXCEPTION_EXIT exception, so in theory * we need to sync state before calling it, but: - * - we don't need to do gen_set_pc_im() because the bxns helper will + * - we don't need to do gen_update_pc() because the bxns helper will * always set the PC itself * - we don't need to do gen_set_condexec() because BXNS is UNPREDICTABLE * unless it's outside an IT block or the last insn in an IT block, @@ -874,8 +849,7 @@ static inline void gen_bxns(DisasContext *s, int rm) * is correct in the non-UNPREDICTABLE cases, and we can choose * "zeroes the IT bits" as our UNPREDICTABLE behaviour otherwise. */ - gen_helper_v7m_bxns(cpu_env, var); - tcg_temp_free_i32(var); + gen_helper_v7m_bxns(tcg_env, var); s->base.is_jmp = DISAS_EXIT; } @@ -887,9 +861,8 @@ static inline void gen_blxns(DisasContext *s, int rm) * We do however need to set the PC, because the blxns helper reads it. * The blxns helper may throw an exception. */ - gen_set_pc_im(s, s->base.pc_next); - gen_helper_v7m_blxns(cpu_env, var); - tcg_temp_free_i32(var); + gen_update_pc(s, curr_insn_len(s)); + gen_helper_v7m_blxns(tcg_env, var); s->base.is_jmp = DISAS_EXIT; } @@ -927,13 +900,7 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) MemOp pow2_align(unsigned i) { static const MemOp mop_align[] = { - 0, MO_ALIGN_2, MO_ALIGN_4, MO_ALIGN_8, MO_ALIGN_16, - /* - * FIXME: TARGET_PAGE_BITS_MIN affects TLB_FLAGS_MASK such - * that 256-bit alignment (MO_ALIGN_32) cannot be supported: - * see get_alignment_bits(). Enforce only 128-bit alignment for now. - */ - MO_ALIGN_16 + 0, MO_ALIGN_2, MO_ALIGN_4, MO_ALIGN_8, MO_ALIGN_16, MO_ALIGN_32 }; g_assert(i < ARRAY_SIZE(mop_align)); return mop_align[i]; @@ -969,7 +936,6 @@ void gen_aa32_ld_internal_i32(DisasContext *s, TCGv_i32 val, { TCGv addr = gen_aa32_addr(s, a32, opc); tcg_gen_qemu_ld_i32(val, addr, index, opc); - tcg_temp_free(addr); } void gen_aa32_st_internal_i32(DisasContext *s, TCGv_i32 val, @@ -977,7 +943,6 @@ void gen_aa32_st_internal_i32(DisasContext *s, TCGv_i32 val, { TCGv addr = gen_aa32_addr(s, a32, opc); tcg_gen_qemu_st_i32(val, addr, index, opc); - tcg_temp_free(addr); } void gen_aa32_ld_internal_i64(DisasContext *s, TCGv_i64 val, @@ -991,7 +956,6 @@ void gen_aa32_ld_internal_i64(DisasContext *s, TCGv_i64 val, if (!IS_USER_ONLY && s->sctlr_b && (opc & MO_SIZE) == MO_64) { tcg_gen_rotri_i64(val, val, 32); } - tcg_temp_free(addr); } void gen_aa32_st_internal_i64(DisasContext *s, TCGv_i64 val, @@ -1004,11 +968,9 @@ void gen_aa32_st_internal_i64(DisasContext *s, TCGv_i64 val, TCGv_i64 tmp = tcg_temp_new_i64(); tcg_gen_rotri_i64(tmp, val, 32); tcg_gen_qemu_st_i64(tmp, addr, index, opc); - tcg_temp_free_i64(tmp); } else { tcg_gen_qemu_st_i64(val, addr, index, opc); } - tcg_temp_free(addr); } void gen_aa32_ld_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, @@ -1055,15 +1017,15 @@ static inline void gen_hvc(DisasContext *s, int imm16) * as an undefined insn by runtime configuration (ie before * the insn really executes). */ - gen_set_pc_im(s, s->pc_curr); - gen_helper_pre_hvc(cpu_env); + gen_update_pc(s, 0); + gen_helper_pre_hvc(tcg_env); /* Otherwise we will treat this as a real exception which * happens after execution of the insn. (The distinction matters * for the PC value reported to the exception handler and also * for single stepping.) */ s->svc_imm = imm16; - gen_set_pc_im(s, s->base.pc_next); + gen_update_pc(s, curr_insn_len(s)); s->base.is_jmp = DISAS_HVC; } @@ -1072,63 +1034,88 @@ static inline void gen_smc(DisasContext *s) /* As with HVC, we may take an exception either before or after * the insn executes. */ - gen_set_pc_im(s, s->pc_curr); - gen_helper_pre_smc(cpu_env, tcg_constant_i32(syn_aa32_smc())); - gen_set_pc_im(s, s->base.pc_next); + gen_update_pc(s, 0); + gen_helper_pre_smc(tcg_env, tcg_constant_i32(syn_aa32_smc())); + gen_update_pc(s, curr_insn_len(s)); s->base.is_jmp = DISAS_SMC; } -static void gen_exception_internal_insn(DisasContext *s, uint32_t pc, int excp) +static void gen_exception_internal_insn(DisasContext *s, int excp) { gen_set_condexec(s); - gen_set_pc_im(s, pc); + gen_update_pc(s, 0); gen_exception_internal(excp); s->base.is_jmp = DISAS_NORETURN; } -void gen_exception_insn(DisasContext *s, uint64_t pc, int excp, - uint32_t syn, uint32_t target_el) +static void gen_exception_el_v(int excp, uint32_t syndrome, TCGv_i32 tcg_el) +{ + gen_helper_exception_with_syndrome_el(tcg_env, tcg_constant_i32(excp), + tcg_constant_i32(syndrome), tcg_el); +} + +static void gen_exception_el(int excp, uint32_t syndrome, uint32_t target_el) +{ + gen_exception_el_v(excp, syndrome, tcg_constant_i32(target_el)); +} + +static void gen_exception(int excp, uint32_t syndrome) +{ + gen_helper_exception_with_syndrome(tcg_env, tcg_constant_i32(excp), + tcg_constant_i32(syndrome)); +} + +static void gen_exception_insn_el_v(DisasContext *s, target_long pc_diff, + int excp, uint32_t syn, TCGv_i32 tcg_el) { if (s->aarch64) { - gen_a64_set_pc_im(pc); + gen_a64_update_pc(s, pc_diff); } else { gen_set_condexec(s); - gen_set_pc_im(s, pc); + gen_update_pc(s, pc_diff); } - gen_exception(excp, syn, target_el); + gen_exception_el_v(excp, syn, tcg_el); + s->base.is_jmp = DISAS_NORETURN; +} + +void gen_exception_insn_el(DisasContext *s, target_long pc_diff, int excp, + uint32_t syn, uint32_t target_el) +{ + gen_exception_insn_el_v(s, pc_diff, excp, syn, + tcg_constant_i32(target_el)); +} + +void gen_exception_insn(DisasContext *s, target_long pc_diff, + int excp, uint32_t syn) +{ + if (s->aarch64) { + gen_a64_update_pc(s, pc_diff); + } else { + gen_set_condexec(s); + gen_update_pc(s, pc_diff); + } + gen_exception(excp, syn); s->base.is_jmp = DISAS_NORETURN; } static void gen_exception_bkpt_insn(DisasContext *s, uint32_t syn) { gen_set_condexec(s); - gen_set_pc_im(s, s->pc_curr); - gen_helper_exception_bkpt_insn(cpu_env, tcg_constant_i32(syn)); + gen_update_pc(s, 0); + gen_helper_exception_bkpt_insn(tcg_env, tcg_constant_i32(syn)); s->base.is_jmp = DISAS_NORETURN; } void unallocated_encoding(DisasContext *s) { /* Unallocated and reserved encodings are uncategorized */ - gen_exception_insn(s, s->pc_curr, EXCP_UDEF, syn_uncategorized(), - default_exception_el(s)); -} - -static void gen_exception_el(DisasContext *s, int excp, uint32_t syn, - TCGv_i32 tcg_el) -{ - gen_set_condexec(s); - gen_set_pc_im(s, s->pc_curr); - gen_helper_exception_with_syndrome(cpu_env, - tcg_constant_i32(excp), - tcg_constant_i32(syn), tcg_el); - s->base.is_jmp = DISAS_NORETURN; + gen_exception_insn(s, 0, EXCP_UDEF, syn_uncategorized()); } /* Force a TB lookup after an instruction that changes the CPU state. */ void gen_lookup_tb(DisasContext *s) { - tcg_gen_movi_i32(cpu_R[15], s->base.pc_next); + gen_pc_plus_diff(s, cpu_R[15], curr_insn_len(s)); s->base.is_jmp = DISAS_EXIT; } @@ -1146,12 +1133,9 @@ static inline void gen_hlt(DisasContext *s, int imm) * semihosting, to provide some semblance of security * (and for consistency with our 32-bit semihosting). */ - if (semihosting_enabled() && -#ifndef CONFIG_USER_ONLY - s->current_el != 0 && -#endif + if (semihosting_enabled(s->current_el == 0) && (imm == (s->thumb ? 0x3c : 0xf000))) { - gen_exception_internal_insn(s, s->pc_curr, EXCP_SEMIHOST); + gen_exception_internal_insn(s, EXCP_SEMIHOST); return; } @@ -1202,20 +1186,20 @@ void read_neon_element32(TCGv_i32 dest, int reg, int ele, MemOp memop) switch (memop) { case MO_SB: - tcg_gen_ld8s_i32(dest, cpu_env, off); + tcg_gen_ld8s_i32(dest, tcg_env, off); break; case MO_UB: - tcg_gen_ld8u_i32(dest, cpu_env, off); + tcg_gen_ld8u_i32(dest, tcg_env, off); break; case MO_SW: - tcg_gen_ld16s_i32(dest, cpu_env, off); + tcg_gen_ld16s_i32(dest, tcg_env, off); break; case MO_UW: - tcg_gen_ld16u_i32(dest, cpu_env, off); + tcg_gen_ld16u_i32(dest, tcg_env, off); break; case MO_UL: case MO_SL: - tcg_gen_ld_i32(dest, cpu_env, off); + tcg_gen_ld_i32(dest, tcg_env, off); break; default: g_assert_not_reached(); @@ -1228,13 +1212,13 @@ void read_neon_element64(TCGv_i64 dest, int reg, int ele, MemOp memop) switch (memop) { case MO_SL: - tcg_gen_ld32s_i64(dest, cpu_env, off); + tcg_gen_ld32s_i64(dest, tcg_env, off); break; case MO_UL: - tcg_gen_ld32u_i64(dest, cpu_env, off); + tcg_gen_ld32u_i64(dest, tcg_env, off); break; case MO_UQ: - tcg_gen_ld_i64(dest, cpu_env, off); + tcg_gen_ld_i64(dest, tcg_env, off); break; default: g_assert_not_reached(); @@ -1247,13 +1231,13 @@ void write_neon_element32(TCGv_i32 src, int reg, int ele, MemOp memop) switch (memop) { case MO_8: - tcg_gen_st8_i32(src, cpu_env, off); + tcg_gen_st8_i32(src, tcg_env, off); break; case MO_16: - tcg_gen_st16_i32(src, cpu_env, off); + tcg_gen_st16_i32(src, tcg_env, off); break; case MO_32: - tcg_gen_st_i32(src, cpu_env, off); + tcg_gen_st_i32(src, tcg_env, off); break; default: g_assert_not_reached(); @@ -1266,10 +1250,10 @@ void write_neon_element64(TCGv_i64 src, int reg, int ele, MemOp memop) switch (memop) { case MO_32: - tcg_gen_st32_i64(src, cpu_env, off); + tcg_gen_st32_i64(src, tcg_env, off); break; case MO_64: - tcg_gen_st_i64(src, cpu_env, off); + tcg_gen_st_i64(src, tcg_env, off); break; default: g_assert_not_reached(); @@ -1280,25 +1264,24 @@ void write_neon_element64(TCGv_i64 src, int reg, int ele, MemOp memop) static inline void iwmmxt_load_reg(TCGv_i64 var, int reg) { - tcg_gen_ld_i64(var, cpu_env, offsetof(CPUARMState, iwmmxt.regs[reg])); + tcg_gen_ld_i64(var, tcg_env, offsetof(CPUARMState, iwmmxt.regs[reg])); } static inline void iwmmxt_store_reg(TCGv_i64 var, int reg) { - tcg_gen_st_i64(var, cpu_env, offsetof(CPUARMState, iwmmxt.regs[reg])); + tcg_gen_st_i64(var, tcg_env, offsetof(CPUARMState, iwmmxt.regs[reg])); } static inline TCGv_i32 iwmmxt_load_creg(int reg) { TCGv_i32 var = tcg_temp_new_i32(); - tcg_gen_ld_i32(var, cpu_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); + tcg_gen_ld_i32(var, tcg_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); return var; } static inline void iwmmxt_store_creg(int reg, TCGv_i32 var) { - tcg_gen_st_i32(var, cpu_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); - tcg_temp_free_i32(var); + tcg_gen_st_i32(var, tcg_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); } static inline void gen_op_iwmmxt_movq_wRn_M0(int rn) @@ -1340,7 +1323,7 @@ static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ { \ iwmmxt_load_reg(cpu_V1, rn); \ - gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0, cpu_V1); \ + gen_helper_iwmmxt_##name(cpu_M0, tcg_env, cpu_M0, cpu_V1); \ } #define IWMMXT_OP_ENV_SIZE(name) \ @@ -1351,7 +1334,7 @@ IWMMXT_OP_ENV(name##l) #define IWMMXT_OP_ENV1(name) \ static inline void gen_op_iwmmxt_##name##_M0(void) \ { \ - gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0); \ + gen_helper_iwmmxt_##name(cpu_M0, tcg_env, cpu_M0); \ } IWMMXT_OP(maddsq) @@ -1457,10 +1440,9 @@ static inline int gen_iwmmxt_address(DisasContext *s, uint32_t insn, else tcg_gen_addi_i32(tmp, tmp, -offset); tcg_gen_mov_i32(dest, tmp); - if (insn & (1 << 21)) + if (insn & (1 << 21)) { store_reg(s, rd, tmp); - else - tcg_temp_free_i32(tmp); + } } else if (insn & (1 << 21)) { /* Post indexed */ tcg_gen_mov_i32(dest, tmp); @@ -1492,7 +1474,6 @@ static inline int gen_iwmmxt_shift(uint32_t insn, uint32_t mask, TCGv_i32 dest) } tcg_gen_andi_i32(tmp, tmp, mask); tcg_gen_mov_i32(dest, tmp); - tcg_temp_free_i32(tmp); return 0; } @@ -1525,7 +1506,6 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) wrd = (insn >> 12) & 0xf; addr = tcg_temp_new_i32(); if (gen_iwmmxt_address(s, insn, addr)) { - tcg_temp_free_i32(addr); return 1; } if (insn & ARM_CP_RW_BIT) { @@ -1553,7 +1533,6 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) } if (i) { tcg_gen_extu_i32_i64(cpu_M0, tmp); - tcg_temp_free_i32(tmp); } gen_op_iwmmxt_movq_wRn_M0(wrd); } @@ -1581,9 +1560,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) } } } - tcg_temp_free_i32(tmp); } - tcg_temp_free_i32(addr); return 0; } @@ -1618,7 +1595,6 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) tmp = iwmmxt_load_creg(wrd); tmp2 = load_reg(s, rd); tcg_gen_andc_i32(tmp, tmp, tmp2); - tcg_temp_free_i32(tmp2); iwmmxt_store_creg(wrd, tmp); break; case ARM_IWMMXT_wCGR0: @@ -1831,7 +1807,6 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) tcg_gen_andi_i32(tmp, tmp, 7); iwmmxt_load_reg(cpu_V1, rd1); gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, tmp); - tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; @@ -1859,7 +1834,6 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) g_assert_not_reached(); } gen_helper_iwmmxt_insr(cpu_M0, cpu_M0, tmp, tmp2, tmp3); - tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; @@ -1913,7 +1887,6 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) } tcg_gen_shli_i32(tmp, tmp, 28); gen_set_nzcv(tmp); - tcg_temp_free_i32(tmp); break; case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */ if (((insn >> 6) & 3) == 3) @@ -1932,7 +1905,6 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_helper_iwmmxt_bcstl(cpu_M0, tmp); break; } - tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; @@ -1961,8 +1933,6 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) break; } gen_set_nzcv(tmp); - tcg_temp_free_i32(tmp2); - tcg_temp_free_i32(tmp); break; case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */ wrd = (insn >> 12) & 0xf; @@ -2009,8 +1979,6 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) break; } gen_set_nzcv(tmp); - tcg_temp_free_i32(tmp2); - tcg_temp_free_i32(tmp); break; case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */ rd = (insn >> 12) & 0xf; @@ -2135,21 +2103,19 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_M0_wRn(rd0); tmp = tcg_temp_new_i32(); if (gen_iwmmxt_shift(insn, 0xff, tmp)) { - tcg_temp_free_i32(tmp); return 1; } switch ((insn >> 22) & 3) { case 1: - gen_helper_iwmmxt_srlw(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_srlw(cpu_M0, tcg_env, cpu_M0, tmp); break; case 2: - gen_helper_iwmmxt_srll(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_srll(cpu_M0, tcg_env, cpu_M0, tmp); break; case 3: - gen_helper_iwmmxt_srlq(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_srlq(cpu_M0, tcg_env, cpu_M0, tmp); break; } - tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); @@ -2163,21 +2129,19 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_M0_wRn(rd0); tmp = tcg_temp_new_i32(); if (gen_iwmmxt_shift(insn, 0xff, tmp)) { - tcg_temp_free_i32(tmp); return 1; } switch ((insn >> 22) & 3) { case 1: - gen_helper_iwmmxt_sraw(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_sraw(cpu_M0, tcg_env, cpu_M0, tmp); break; case 2: - gen_helper_iwmmxt_sral(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_sral(cpu_M0, tcg_env, cpu_M0, tmp); break; case 3: - gen_helper_iwmmxt_sraq(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_sraq(cpu_M0, tcg_env, cpu_M0, tmp); break; } - tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); @@ -2191,21 +2155,19 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_M0_wRn(rd0); tmp = tcg_temp_new_i32(); if (gen_iwmmxt_shift(insn, 0xff, tmp)) { - tcg_temp_free_i32(tmp); return 1; } switch ((insn >> 22) & 3) { case 1: - gen_helper_iwmmxt_sllw(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_sllw(cpu_M0, tcg_env, cpu_M0, tmp); break; case 2: - gen_helper_iwmmxt_slll(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_slll(cpu_M0, tcg_env, cpu_M0, tmp); break; case 3: - gen_helper_iwmmxt_sllq(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_sllq(cpu_M0, tcg_env, cpu_M0, tmp); break; } - tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); @@ -2221,27 +2183,23 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) switch ((insn >> 22) & 3) { case 1: if (gen_iwmmxt_shift(insn, 0xf, tmp)) { - tcg_temp_free_i32(tmp); return 1; } - gen_helper_iwmmxt_rorw(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_rorw(cpu_M0, tcg_env, cpu_M0, tmp); break; case 2: if (gen_iwmmxt_shift(insn, 0x1f, tmp)) { - tcg_temp_free_i32(tmp); return 1; } - gen_helper_iwmmxt_rorl(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_rorl(cpu_M0, tcg_env, cpu_M0, tmp); break; case 3: if (gen_iwmmxt_shift(insn, 0x3f, tmp)) { - tcg_temp_free_i32(tmp); return 1; } - gen_helper_iwmmxt_rorq(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_rorq(cpu_M0, tcg_env, cpu_M0, tmp); break; } - tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); @@ -2371,7 +2329,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) rd0 = (insn >> 16) & 0xf; gen_op_iwmmxt_movq_M0_wRn(rd0); tmp = tcg_constant_i32(((insn >> 16) & 0xf0) | (insn & 0x0f)); - gen_helper_iwmmxt_shufh(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_shufh(cpu_M0, tcg_env, cpu_M0, tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); @@ -2480,12 +2438,8 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, tmp, tmp2); break; default: - tcg_temp_free_i32(tmp2); - tcg_temp_free_i32(tmp); return 1; } - tcg_temp_free_i32(tmp2); - tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; @@ -2534,8 +2488,6 @@ static int disas_dsp_insn(DisasContext *s, uint32_t insn) default: return 1; } - tcg_temp_free_i32(tmp2); - tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(acc); return 0; @@ -2574,25 +2526,38 @@ static void gen_goto_ptr(void) * cpu_loop_exec. Any live exit_requests will be processed as we * enter the next TB. */ -static void gen_goto_tb(DisasContext *s, int n, target_ulong dest) +static void gen_goto_tb(DisasContext *s, int n, target_long diff) { - if (translator_use_goto_tb(&s->base, dest)) { - tcg_gen_goto_tb(n); - gen_set_pc_im(s, dest); + if (translator_use_goto_tb(&s->base, s->pc_curr + diff)) { + /* + * For pcrel, the pc must always be up-to-date on entry to + * the linked TB, so that it can use simple additions for all + * further adjustments. For !pcrel, the linked TB is compiled + * to know its full virtual address, so we can delay the + * update to pc to the unlinked path. A long chain of links + * can thus avoid many updates to the PC. + */ + if (tb_cflags(s->base.tb) & CF_PCREL) { + gen_update_pc(s, diff); + tcg_gen_goto_tb(n); + } else { + tcg_gen_goto_tb(n); + gen_update_pc(s, diff); + } tcg_gen_exit_tb(s->base.tb, n); } else { - gen_set_pc_im(s, dest); + gen_update_pc(s, diff); gen_goto_ptr(); } s->base.is_jmp = DISAS_NORETURN; } /* Jump, specifying which TB number to use if we gen_goto_tb() */ -static inline void gen_jmp_tb(DisasContext *s, uint32_t dest, int tbno) +static void gen_jmp_tb(DisasContext *s, target_long diff, int tbno) { if (unlikely(s->ss_active)) { /* An indirect jump so that we still trigger the debug exception. */ - gen_set_pc_im(s, dest); + gen_update_pc(s, diff); s->base.is_jmp = DISAS_JUMP; return; } @@ -2609,7 +2574,7 @@ static inline void gen_jmp_tb(DisasContext *s, uint32_t dest, int tbno) * gen_jmp(); * on the second call to gen_jmp(). */ - gen_goto_tb(s, tbno, dest); + gen_goto_tb(s, tbno, diff); break; case DISAS_UPDATE_NOCHAIN: case DISAS_UPDATE_EXIT: @@ -2618,7 +2583,7 @@ static inline void gen_jmp_tb(DisasContext *s, uint32_t dest, int tbno) * Avoid using goto_tb so we really do exit back to the main loop * and don't chain to another TB. */ - gen_set_pc_im(s, dest); + gen_update_pc(s, diff); gen_goto_ptr(); s->base.is_jmp = DISAS_NORETURN; break; @@ -2631,9 +2596,9 @@ static inline void gen_jmp_tb(DisasContext *s, uint32_t dest, int tbno) } } -static inline void gen_jmp(DisasContext *s, uint32_t dest) +static inline void gen_jmp(DisasContext *s, target_long diff) { - gen_jmp_tb(s, dest, 0); + gen_jmp_tb(s, diff, 0); } static inline void gen_mulxy(TCGv_i32 t0, TCGv_i32 t1, int x, int y) @@ -2699,7 +2664,6 @@ static int gen_set_psr(DisasContext *s, uint32_t mask, int spsr, TCGv_i32 t0) } else { gen_set_cpsr(t0, mask); } - tcg_temp_free_i32(t0); gen_lookup_tb(s); return 0; } @@ -2735,8 +2699,6 @@ static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn, * an exception and return false. Otherwise it will return true, * and set *tgtmode and *regno appropriately. */ - int exc_target = default_exception_el(s); - /* These instructions are present only in ARMv8, or in ARMv7 with the * Virtualization Extensions. */ @@ -2840,27 +2802,34 @@ static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn, if (arm_dc_feature(s, ARM_FEATURE_AARCH64) && dc_isar_feature(aa64_sel2, s)) { /* Target EL is EL<3 minus SCR_EL3.EEL2> */ - tcg_el = load_cpu_field(cp15.scr_el3); + tcg_el = load_cpu_field_low32(cp15.scr_el3); tcg_gen_sextract_i32(tcg_el, tcg_el, ctz32(SCR_EEL2), 1); tcg_gen_addi_i32(tcg_el, tcg_el, 3); } else { tcg_el = tcg_constant_i32(3); } - gen_exception_el(s, EXCP_UDEF, syn_uncategorized(), tcg_el); - tcg_temp_free_i32(tcg_el); + gen_exception_insn_el_v(s, 0, EXCP_UDEF, + syn_uncategorized(), tcg_el); return false; } break; case ARM_CPU_MODE_HYP: /* - * SPSR_hyp and r13_hyp can only be accessed from Monitor mode - * (and so we can forbid accesses from EL2 or below). elr_hyp - * can be accessed also from Hyp mode, so forbid accesses from - * EL0 or EL1. + * r13_hyp can only be accessed from Monitor mode, and so we + * can forbid accesses from EL2 or below. + * elr_hyp can be accessed also from Hyp mode, so forbid + * accesses from EL0 or EL1. + * SPSR_hyp is supposed to be in the same category as r13_hyp + * and UNPREDICTABLE if accessed from anything except Monitor + * mode. However there is some real-world code that will do + * it because at least some hardware happens to permit the + * access. (Notably a standard Cortex-R52 startup code fragment + * does this.) So we permit SPSR_hyp from Hyp mode also, to allow + * this (incorrect) guest code to run. */ - if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 2 || - (s->current_el < 3 && *regno != 17)) { + if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 2 + || (s->current_el < 3 && *regno != 16 && *regno != 17)) { goto undef; } break; @@ -2872,8 +2841,7 @@ static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn, undef: /* If we get here then some access check did not pass */ - gen_exception_insn(s, s->pc_curr, EXCP_UDEF, - syn_uncategorized(), exc_target); + gen_exception_insn(s, 0, EXCP_UDEF, syn_uncategorized()); return false; } @@ -2888,12 +2856,11 @@ static void gen_msr_banked(DisasContext *s, int r, int sysm, int rn) /* Sync state because msr_banked() can raise exceptions */ gen_set_condexec(s); - gen_set_pc_im(s, s->pc_curr); + gen_update_pc(s, 0); tcg_reg = load_reg(s, rn); - gen_helper_msr_banked(cpu_env, tcg_reg, + gen_helper_msr_banked(tcg_env, tcg_reg, tcg_constant_i32(tgtmode), tcg_constant_i32(regno)); - tcg_temp_free_i32(tcg_reg); s->base.is_jmp = DISAS_UPDATE_EXIT; } @@ -2908,9 +2875,9 @@ static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn) /* Sync state because mrs_banked() can raise exceptions */ gen_set_condexec(s); - gen_set_pc_im(s, s->pc_curr); + gen_update_pc(s, 0); tcg_reg = tcg_temp_new_i32(); - gen_helper_mrs_banked(tcg_reg, cpu_env, + gen_helper_mrs_banked(tcg_reg, tcg_env, tcg_constant_i32(tgtmode), tcg_constant_i32(regno)); store_reg(s, rn, tcg_reg); @@ -2924,7 +2891,6 @@ static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn) static void store_pc_exc_ret(DisasContext *s, TCGv_i32 pc) { tcg_gen_mov_i32(cpu_R[15], pc); - tcg_temp_free_i32(pc); } /* Generate a v6 exception return. Marks both values as dead. */ @@ -2935,11 +2901,8 @@ static void gen_rfe(DisasContext *s, TCGv_i32 pc, TCGv_i32 cpsr) * appropriately depending on the new Thumb bit, so it must * be called after storing the new PC. */ - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_cpsr_write_eret(cpu_env, cpsr); - tcg_temp_free_i32(cpsr); + translator_io_start(&s->base); + gen_helper_cpsr_write_eret(tcg_env, cpsr); /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; } @@ -2956,10 +2919,9 @@ static void gen_gvec_fn3_qc(uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, { TCGv_ptr qc_ptr = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(qc_ptr, cpu_env, offsetof(CPUARMState, vfp.qc)); + tcg_gen_addi_ptr(qc_ptr, tcg_env, offsetof(CPUARMState, vfp.qc)); tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, qc_ptr, opr_sz, max_sz, 0, fn); - tcg_temp_free_ptr(qc_ptr); } void gen_gvec_sqrdmlah_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, @@ -2982,56 +2944,16 @@ void gen_gvec_sqrdmlsh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]); } -#define GEN_CMP0(NAME, COND) \ - static void gen_##NAME##0_i32(TCGv_i32 d, TCGv_i32 a) \ - { \ - tcg_gen_setcondi_i32(COND, d, a, 0); \ - tcg_gen_neg_i32(d, d); \ - } \ - static void gen_##NAME##0_i64(TCGv_i64 d, TCGv_i64 a) \ - { \ - tcg_gen_setcondi_i64(COND, d, a, 0); \ - tcg_gen_neg_i64(d, d); \ - } \ - static void gen_##NAME##0_vec(unsigned vece, TCGv_vec d, TCGv_vec a) \ - { \ - TCGv_vec zero = tcg_constant_vec_matching(d, vece, 0); \ - tcg_gen_cmp_vec(COND, vece, d, a, zero); \ - } \ - void gen_gvec_##NAME##0(unsigned vece, uint32_t d, uint32_t m, \ - uint32_t opr_sz, uint32_t max_sz) \ - { \ - const GVecGen2 op[4] = { \ - { .fno = gen_helper_gvec_##NAME##0_b, \ - .fniv = gen_##NAME##0_vec, \ - .opt_opc = vecop_list_cmp, \ - .vece = MO_8 }, \ - { .fno = gen_helper_gvec_##NAME##0_h, \ - .fniv = gen_##NAME##0_vec, \ - .opt_opc = vecop_list_cmp, \ - .vece = MO_16 }, \ - { .fni4 = gen_##NAME##0_i32, \ - .fniv = gen_##NAME##0_vec, \ - .opt_opc = vecop_list_cmp, \ - .vece = MO_32 }, \ - { .fni8 = gen_##NAME##0_i64, \ - .fniv = gen_##NAME##0_vec, \ - .opt_opc = vecop_list_cmp, \ - .prefer_i64 = TCG_TARGET_REG_BITS == 64, \ - .vece = MO_64 }, \ - }; \ - tcg_gen_gvec_2(d, m, opr_sz, max_sz, &op[vece]); \ - } +#define GEN_CMP0(NAME, COND) \ + void NAME(unsigned vece, uint32_t d, uint32_t m, \ + uint32_t opr_sz, uint32_t max_sz) \ + { tcg_gen_gvec_cmpi(COND, vece, d, m, 0, opr_sz, max_sz); } -static const TCGOpcode vecop_list_cmp[] = { - INDEX_op_cmp_vec, 0 -}; - -GEN_CMP0(ceq, TCG_COND_EQ) -GEN_CMP0(cle, TCG_COND_LE) -GEN_CMP0(cge, TCG_COND_GE) -GEN_CMP0(clt, TCG_COND_LT) -GEN_CMP0(cgt, TCG_COND_GT) +GEN_CMP0(gen_gvec_ceq0, TCG_COND_EQ) +GEN_CMP0(gen_gvec_cle0, TCG_COND_LE) +GEN_CMP0(gen_gvec_cge0, TCG_COND_GE) +GEN_CMP0(gen_gvec_clt0, TCG_COND_LT) +GEN_CMP0(gen_gvec_cgt0, TCG_COND_GT) #undef GEN_CMP0 @@ -3092,7 +3014,7 @@ void gen_gvec_ssra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, .vece = MO_32 }, { .fni8 = gen_ssra64_i64, .fniv = gen_ssra_vec, - .fno = gen_helper_gvec_ssra_b, + .fno = gen_helper_gvec_ssra_d, .prefer_i64 = TCG_TARGET_REG_BITS == 64, .opt_opc = vecop_list, .load_dest = true, @@ -3205,7 +3127,6 @@ static void gen_srshr8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); tcg_gen_vec_sar8i_i64(d, a, sh); tcg_gen_vec_add8_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_srshr16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) @@ -3216,7 +3137,6 @@ static void gen_srshr16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); tcg_gen_vec_sar16i_i64(d, a, sh); tcg_gen_vec_add16_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_srshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) @@ -3232,7 +3152,6 @@ static void gen_srshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) tcg_gen_extract_i32(t, a, sh - 1, 1); tcg_gen_sari_i32(d, a, sh); tcg_gen_add_i32(d, d, t); - tcg_temp_free_i32(t); } static void gen_srshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) @@ -3242,7 +3161,6 @@ static void gen_srshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) tcg_gen_extract_i64(t, a, sh - 1, 1); tcg_gen_sari_i64(d, a, sh); tcg_gen_add_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_srshr_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) @@ -3255,9 +3173,6 @@ static void gen_srshr_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) tcg_gen_and_vec(vece, t, t, ones); tcg_gen_sari_vec(vece, d, a, sh); tcg_gen_add_vec(vece, d, d, t); - - tcg_temp_free_vec(t); - tcg_temp_free_vec(ones); } void gen_gvec_srshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, @@ -3313,7 +3228,6 @@ static void gen_srsra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) gen_srshr8_i64(t, a, sh); tcg_gen_vec_add8_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_srsra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) @@ -3322,7 +3236,6 @@ static void gen_srsra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) gen_srshr16_i64(t, a, sh); tcg_gen_vec_add16_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_srsra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) @@ -3331,7 +3244,6 @@ static void gen_srsra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) gen_srshr32_i32(t, a, sh); tcg_gen_add_i32(d, d, t); - tcg_temp_free_i32(t); } static void gen_srsra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) @@ -3340,7 +3252,6 @@ static void gen_srsra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) gen_srshr64_i64(t, a, sh); tcg_gen_add_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_srsra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) @@ -3349,7 +3260,6 @@ static void gen_srsra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) gen_srshr_vec(vece, t, a, sh); tcg_gen_add_vec(vece, d, d, t); - tcg_temp_free_vec(t); } void gen_gvec_srsra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, @@ -3412,7 +3322,6 @@ static void gen_urshr8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); tcg_gen_vec_shr8i_i64(d, a, sh); tcg_gen_vec_add8_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_urshr16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) @@ -3423,7 +3332,6 @@ static void gen_urshr16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); tcg_gen_vec_shr16i_i64(d, a, sh); tcg_gen_vec_add16_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_urshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) @@ -3439,7 +3347,6 @@ static void gen_urshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) tcg_gen_extract_i32(t, a, sh - 1, 1); tcg_gen_shri_i32(d, a, sh); tcg_gen_add_i32(d, d, t); - tcg_temp_free_i32(t); } static void gen_urshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) @@ -3449,7 +3356,6 @@ static void gen_urshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) tcg_gen_extract_i64(t, a, sh - 1, 1); tcg_gen_shri_i64(d, a, sh); tcg_gen_add_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_urshr_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t shift) @@ -3462,9 +3368,6 @@ static void gen_urshr_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t shift) tcg_gen_and_vec(vece, t, t, ones); tcg_gen_shri_vec(vece, d, a, shift); tcg_gen_add_vec(vece, d, d, t); - - tcg_temp_free_vec(t); - tcg_temp_free_vec(ones); } void gen_gvec_urshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, @@ -3523,7 +3426,6 @@ static void gen_ursra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) gen_urshr8_i64(t, a, sh); } tcg_gen_vec_add8_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_ursra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) @@ -3536,7 +3438,6 @@ static void gen_ursra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) gen_urshr16_i64(t, a, sh); } tcg_gen_vec_add16_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_ursra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) @@ -3549,7 +3450,6 @@ static void gen_ursra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) gen_urshr32_i32(t, a, sh); } tcg_gen_add_i32(d, d, t); - tcg_temp_free_i32(t); } static void gen_ursra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) @@ -3562,7 +3462,6 @@ static void gen_ursra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) gen_urshr64_i64(t, a, sh); } tcg_gen_add_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_ursra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) @@ -3575,7 +3474,6 @@ static void gen_ursra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) gen_urshr_vec(vece, t, a, sh); } tcg_gen_add_vec(vece, d, d, t); - tcg_temp_free_vec(t); } void gen_gvec_ursra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, @@ -3628,7 +3526,6 @@ static void gen_shr8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) tcg_gen_andi_i64(t, t, mask); tcg_gen_andi_i64(d, d, ~mask); tcg_gen_or_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_shr16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) @@ -3640,7 +3537,6 @@ static void gen_shr16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) tcg_gen_andi_i64(t, t, mask); tcg_gen_andi_i64(d, d, ~mask); tcg_gen_or_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_shr32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) @@ -3664,9 +3560,6 @@ static void gen_shr_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) tcg_gen_shri_vec(vece, t, a, sh); tcg_gen_and_vec(vece, d, d, m); tcg_gen_or_vec(vece, d, d, t); - - tcg_temp_free_vec(t); - tcg_temp_free_vec(m); } void gen_gvec_sri(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, @@ -3723,7 +3616,6 @@ static void gen_shl8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) tcg_gen_andi_i64(t, t, mask); tcg_gen_andi_i64(d, d, ~mask); tcg_gen_or_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_shl16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) @@ -3735,7 +3627,6 @@ static void gen_shl16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) tcg_gen_andi_i64(t, t, mask); tcg_gen_andi_i64(d, d, ~mask); tcg_gen_or_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_shl32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) @@ -3757,9 +3648,6 @@ static void gen_shl_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) tcg_gen_dupi_vec(vece, m, MAKE_64BIT_MASK(0, sh)); tcg_gen_and_vec(vece, d, d, m); tcg_gen_or_vec(vece, d, d, t); - - tcg_temp_free_vec(t); - tcg_temp_free_vec(m); } void gen_gvec_sli(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, @@ -3936,15 +3824,13 @@ void gen_gvec_mls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, static void gen_cmtst_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { tcg_gen_and_i32(d, a, b); - tcg_gen_setcondi_i32(TCG_COND_NE, d, d, 0); - tcg_gen_neg_i32(d, d); + tcg_gen_negsetcond_i32(TCG_COND_NE, d, d, tcg_constant_i32(0)); } void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) { tcg_gen_and_i64(d, a, b); - tcg_gen_setcondi_i64(TCG_COND_NE, d, d, 0); - tcg_gen_neg_i64(d, d); + tcg_gen_negsetcond_i64(TCG_COND_NE, d, d, tcg_constant_i64(0)); } static void gen_cmtst_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) @@ -4000,11 +3886,6 @@ void gen_ushl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) 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); } void gen_ushl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) @@ -4027,11 +3908,6 @@ void gen_ushl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) 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); } static void gen_ushl_vec(unsigned vece, TCGv_vec dst, @@ -4051,7 +3927,6 @@ static void gen_ushl_vec(unsigned vece, TCGv_vec 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); } /* @@ -4084,12 +3959,6 @@ static void gen_ushl_vec(unsigned vece, TCGv_vec dst, 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); } void gen_gvec_ushl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, @@ -4141,11 +4010,6 @@ void gen_sshl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) 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); } void gen_sshl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) @@ -4169,11 +4033,6 @@ void gen_sshl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) 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); } static void gen_sshl_vec(unsigned vece, TCGv_vec dst, @@ -4218,12 +4077,6 @@ static void gen_sshl_vec(unsigned vece, TCGv_vec dst, 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); } void gen_gvec_sshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, @@ -4262,7 +4115,6 @@ static void gen_uqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, tcg_gen_usadd_vec(vece, t, a, b); tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); tcg_gen_or_vec(vece, sat, sat, x); - tcg_temp_free_vec(x); } void gen_gvec_uqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, @@ -4305,7 +4157,6 @@ static void gen_sqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, tcg_gen_ssadd_vec(vece, t, a, b); tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); tcg_gen_or_vec(vece, sat, sat, x); - tcg_temp_free_vec(x); } void gen_gvec_sqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, @@ -4348,7 +4199,6 @@ static void gen_uqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, tcg_gen_ussub_vec(vece, t, a, b); tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); tcg_gen_or_vec(vece, sat, sat, x); - tcg_temp_free_vec(x); } void gen_gvec_uqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, @@ -4391,7 +4241,6 @@ static void gen_sqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, tcg_gen_sssub_vec(vece, t, a, b); tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); tcg_gen_or_vec(vece, sat, sat, x); - tcg_temp_free_vec(x); } void gen_gvec_sqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, @@ -4433,7 +4282,6 @@ static void gen_sabd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) tcg_gen_sub_i32(t, a, b); tcg_gen_sub_i32(d, b, a); tcg_gen_movcond_i32(TCG_COND_LT, d, a, b, d, t); - tcg_temp_free_i32(t); } static void gen_sabd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) @@ -4443,7 +4291,6 @@ static void gen_sabd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) tcg_gen_sub_i64(t, a, b); tcg_gen_sub_i64(d, b, a); tcg_gen_movcond_i64(TCG_COND_LT, d, a, b, d, t); - tcg_temp_free_i64(t); } static void gen_sabd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) @@ -4453,7 +4300,6 @@ static void gen_sabd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) tcg_gen_smin_vec(vece, t, a, b); tcg_gen_smax_vec(vece, d, a, b); tcg_gen_sub_vec(vece, d, d, t); - tcg_temp_free_vec(t); } void gen_gvec_sabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, @@ -4493,7 +4339,6 @@ static void gen_uabd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) tcg_gen_sub_i32(t, a, b); tcg_gen_sub_i32(d, b, a); tcg_gen_movcond_i32(TCG_COND_LTU, d, a, b, d, t); - tcg_temp_free_i32(t); } static void gen_uabd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) @@ -4503,7 +4348,6 @@ static void gen_uabd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) tcg_gen_sub_i64(t, a, b); tcg_gen_sub_i64(d, b, a); tcg_gen_movcond_i64(TCG_COND_LTU, d, a, b, d, t); - tcg_temp_free_i64(t); } static void gen_uabd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) @@ -4513,7 +4357,6 @@ static void gen_uabd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) tcg_gen_umin_vec(vece, t, a, b); tcg_gen_umax_vec(vece, d, a, b); tcg_gen_sub_vec(vece, d, d, t); - tcg_temp_free_vec(t); } void gen_gvec_uabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, @@ -4551,7 +4394,6 @@ static void gen_saba_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) TCGv_i32 t = tcg_temp_new_i32(); gen_sabd_i32(t, a, b); tcg_gen_add_i32(d, d, t); - tcg_temp_free_i32(t); } static void gen_saba_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) @@ -4559,7 +4401,6 @@ static void gen_saba_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) TCGv_i64 t = tcg_temp_new_i64(); gen_sabd_i64(t, a, b); tcg_gen_add_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_saba_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) @@ -4567,7 +4408,6 @@ static void gen_saba_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) TCGv_vec t = tcg_temp_new_vec_matching(d); gen_sabd_vec(vece, t, a, b); tcg_gen_add_vec(vece, d, d, t); - tcg_temp_free_vec(t); } void gen_gvec_saba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, @@ -4610,7 +4450,6 @@ static void gen_uaba_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) TCGv_i32 t = tcg_temp_new_i32(); gen_uabd_i32(t, a, b); tcg_gen_add_i32(d, d, t); - tcg_temp_free_i32(t); } static void gen_uaba_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) @@ -4618,7 +4457,6 @@ static void gen_uaba_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) TCGv_i64 t = tcg_temp_new_i64(); gen_uabd_i64(t, a, b); tcg_gen_add_i64(d, d, t); - tcg_temp_free_i64(t); } static void gen_uaba_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) @@ -4626,7 +4464,6 @@ static void gen_uaba_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) TCGv_vec t = tcg_temp_new_vec_matching(d); gen_uabd_vec(vece, t, a, b); tcg_gen_add_vec(vece, d, d, t); - tcg_temp_free_vec(t); } void gen_gvec_uaba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, @@ -4664,225 +4501,291 @@ void gen_gvec_uaba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } +static bool aa32_cpreg_encoding_in_impdef_space(uint8_t crn, uint8_t crm) +{ + static const uint16_t mask[3] = { + 0b0000000111100111, /* crn == 9, crm == {c0-c2, c5-c8} */ + 0b0000000100010011, /* crn == 10, crm == {c0, c1, c4, c8} */ + 0b1000000111111111, /* crn == 11, crm == {c0-c8, c15} */ + }; + + if (crn >= 9 && crn <= 11) { + return (mask[crn - 9] >> crm) & 1; + } + return false; +} + static void do_coproc_insn(DisasContext *s, int cpnum, int is64, int opc1, int crn, int crm, int opc2, bool isread, int rt, int rt2) { - const ARMCPRegInfo *ri; + uint32_t key = ENCODE_CP_REG(cpnum, is64, s->ns, crn, crm, opc1, opc2); + const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key); + TCGv_ptr tcg_ri = NULL; + bool need_exit_tb = false; + uint32_t syndrome; - ri = get_arm_cp_reginfo(s->cp_regs, - ENCODE_CP_REG(cpnum, is64, s->ns, crn, crm, opc1, opc2)); - if (ri) { - bool need_exit_tb; - - /* Check access permissions */ - if (!cp_access_ok(s->current_el, ri, isread)) { - unallocated_encoding(s); - return; - } - - 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. - * Note that on XScale all cp0..c13 registers do an access check - * call in order to handle c15_cpar. - */ - uint32_t syndrome; - - /* Note that since we are an implementation which takes an - * exception on a trapped conditional instruction only if the - * instruction passes its condition code check, we can take - * advantage of the clause in the ARM ARM that allows us to set - * the COND field in the instruction to 0xE in all cases. - * We could fish the actual condition out of the insn (ARM) - * or the condexec bits (Thumb) but it isn't necessary. - */ - switch (cpnum) { - case 14: - if (is64) { - syndrome = syn_cp14_rrt_trap(1, 0xe, opc1, crm, rt, rt2, - isread, false); - } else { - syndrome = syn_cp14_rt_trap(1, 0xe, opc1, opc2, crn, crm, - rt, isread, false); - } - break; - case 15: - if (is64) { - syndrome = syn_cp15_rrt_trap(1, 0xe, opc1, crm, rt, rt2, - isread, false); - } else { - syndrome = syn_cp15_rt_trap(1, 0xe, opc1, opc2, crn, crm, - rt, isread, false); - } - break; - default: - /* ARMv8 defines that only coprocessors 14 and 15 exist, - * so this can only happen if this is an ARMv7 or earlier CPU, - * in which case the syndrome information won't actually be - * guest visible. - */ - assert(!arm_dc_feature(s, ARM_FEATURE_V8)); - syndrome = syn_uncategorized(); - break; - } - - gen_set_condexec(s); - gen_set_pc_im(s, s->pc_curr); - gen_helper_access_check_cp_reg(cpu_env, - tcg_constant_ptr(ri), - tcg_constant_i32(syndrome), - tcg_constant_i32(isread)); - } else if (ri->type & ARM_CP_RAISES_EXC) { - /* - * The readfn or writefn might raise an exception; - * synchronize the CPU state in case it does. - */ - gen_set_condexec(s); - gen_set_pc_im(s, s->pc_curr); - } - - /* Handle special cases first */ - switch (ri->type & ARM_CP_SPECIAL_MASK) { - case 0: - break; - case ARM_CP_NOP: - return; - case ARM_CP_WFI: - if (isread) { - unallocated_encoding(s); - return; - } - gen_set_pc_im(s, s->base.pc_next); - s->base.is_jmp = DISAS_WFI; - return; - default: - g_assert_not_reached(); - } - - if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { - gen_io_start(); - } - - if (isread) { - /* Read */ - if (is64) { - TCGv_i64 tmp64; - TCGv_i32 tmp; - if (ri->type & ARM_CP_CONST) { - tmp64 = tcg_constant_i64(ri->resetvalue); - } else if (ri->readfn) { - tmp64 = tcg_temp_new_i64(); - gen_helper_get_cp_reg64(tmp64, cpu_env, - tcg_constant_ptr(ri)); - } else { - tmp64 = tcg_temp_new_i64(); - tcg_gen_ld_i64(tmp64, cpu_env, ri->fieldoffset); - } - tmp = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(tmp, tmp64); - store_reg(s, rt, tmp); - tmp = tcg_temp_new_i32(); - tcg_gen_extrh_i64_i32(tmp, tmp64); - tcg_temp_free_i64(tmp64); - store_reg(s, rt2, tmp); - } else { - TCGv_i32 tmp; - if (ri->type & ARM_CP_CONST) { - tmp = tcg_constant_i32(ri->resetvalue); - } else if (ri->readfn) { - tmp = tcg_temp_new_i32(); - gen_helper_get_cp_reg(tmp, cpu_env, tcg_constant_ptr(ri)); - } else { - tmp = load_cpu_offset(ri->fieldoffset); - } - if (rt == 15) { - /* Destination register of r15 for 32 bit loads sets - * the condition codes from the high 4 bits of the value - */ - gen_set_nzcv(tmp); - tcg_temp_free_i32(tmp); - } else { - store_reg(s, rt, tmp); - } - } + /* + * Note that since we are an implementation which takes an + * exception on a trapped conditional instruction only if the + * instruction passes its condition code check, we can take + * advantage of the clause in the ARM ARM that allows us to set + * the COND field in the instruction to 0xE in all cases. + * We could fish the actual condition out of the insn (ARM) + * or the condexec bits (Thumb) but it isn't necessary. + */ + switch (cpnum) { + case 14: + if (is64) { + syndrome = syn_cp14_rrt_trap(1, 0xe, opc1, crm, rt, rt2, + isread, false); } else { - /* Write */ - if (ri->type & ARM_CP_CONST) { - /* If not forbidden by access permissions, treat as WI */ - return; - } - - if (is64) { - TCGv_i32 tmplo, tmphi; - TCGv_i64 tmp64 = tcg_temp_new_i64(); - tmplo = load_reg(s, rt); - tmphi = load_reg(s, rt2); - tcg_gen_concat_i32_i64(tmp64, tmplo, tmphi); - tcg_temp_free_i32(tmplo); - tcg_temp_free_i32(tmphi); - if (ri->writefn) { - gen_helper_set_cp_reg64(cpu_env, tcg_constant_ptr(ri), - tmp64); - } else { - tcg_gen_st_i64(tmp64, cpu_env, ri->fieldoffset); - } - tcg_temp_free_i64(tmp64); - } else { - TCGv_i32 tmp = load_reg(s, rt); - if (ri->writefn) { - gen_helper_set_cp_reg(cpu_env, tcg_constant_ptr(ri), tmp); - tcg_temp_free_i32(tmp); - } else { - store_cpu_offset(tmp, ri->fieldoffset, 4); - } - } + syndrome = syn_cp14_rt_trap(1, 0xe, opc1, opc2, crn, crm, + rt, isread, false); } + break; + case 15: + if (is64) { + syndrome = syn_cp15_rrt_trap(1, 0xe, opc1, crm, rt, rt2, + isread, false); + } else { + syndrome = syn_cp15_rt_trap(1, 0xe, opc1, opc2, crn, crm, + rt, isread, false); + } + break; + default: + /* + * ARMv8 defines that only coprocessors 14 and 15 exist, + * so this can only happen if this is an ARMv7 or earlier CPU, + * in which case the syndrome information won't actually be + * guest visible. + */ + assert(!arm_dc_feature(s, ARM_FEATURE_V8)); + syndrome = syn_uncategorized(); + break; + } - /* I/O operations must end the TB here (whether read or write) */ - need_exit_tb = ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && - (ri->type & ARM_CP_IO)); + if (s->hstr_active && cpnum == 15 && s->current_el == 1) { + /* + * At EL1, check for a HSTR_EL2 trap, which must take precedence + * over the UNDEF for "no such register" or the UNDEF for "access + * permissions forbid this EL1 access". HSTR_EL2 traps from EL0 + * only happen if the cpreg doesn't UNDEF at EL0, so we do those in + * access_check_cp_reg(), after the checks for whether the access + * configurably trapped to EL1. + */ + uint32_t maskbit = is64 ? crm : crn; - if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) { + if (maskbit != 4 && maskbit != 14) { + /* T4 and T14 are RES0 so never cause traps */ + TCGv_i32 t; + DisasLabel over = gen_disas_label(s); + + t = load_cpu_offset(offsetoflow32(CPUARMState, cp15.hstr_el2)); + tcg_gen_andi_i32(t, t, 1u << maskbit); + tcg_gen_brcondi_i32(TCG_COND_EQ, t, 0, over.label); + + gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2); /* - * A write to any coprocessor register that ends a TB - * must rebuild the hflags for the next TB. + * gen_exception_insn() will set is_jmp to DISAS_NORETURN, + * but since we're conditionally branching over it, we want + * to assume continue-to-next-instruction. */ - gen_rebuild_hflags(s, ri->type & ARM_CP_NEWEL); - /* - * We default to ending the TB on a coprocessor register write, - * but allow this to be suppressed by the register definition - * (usually only necessary to work around guest bugs). - */ - need_exit_tb = true; - } - if (need_exit_tb) { - gen_lookup_tb(s); + s->base.is_jmp = DISAS_NEXT; + set_disas_label(s, over); } + } + if (cpnum == 15 && aa32_cpreg_encoding_in_impdef_space(crn, crm)) { + /* + * Check for TIDCP trap, which must take precedence over the UNDEF + * for "no such register" etc. It shares precedence with HSTR, + * but raises the same exception, so order doesn't matter. + */ + switch (s->current_el) { + case 0: + if (arm_dc_feature(s, ARM_FEATURE_AARCH64) + && dc_isar_feature(aa64_tidcp1, s)) { + gen_helper_tidcp_el0(tcg_env, tcg_constant_i32(syndrome)); + } + break; + case 1: + gen_helper_tidcp_el1(tcg_env, tcg_constant_i32(syndrome)); + break; + } + } + + if (!ri) { + /* + * Unknown register; this might be a guest error or a QEMU + * unimplemented feature. + */ + if (is64) { + qemu_log_mask(LOG_UNIMP, "%s access to unsupported AArch32 " + "64 bit system register cp:%d opc1: %d crm:%d " + "(%s)\n", + isread ? "read" : "write", cpnum, opc1, crm, + s->ns ? "non-secure" : "secure"); + } else { + qemu_log_mask(LOG_UNIMP, "%s access to unsupported AArch32 " + "system register cp:%d opc1:%d crn:%d crm:%d " + "opc2:%d (%s)\n", + isread ? "read" : "write", cpnum, opc1, crn, + crm, opc2, s->ns ? "non-secure" : "secure"); + } + unallocated_encoding(s); return; } - /* Unknown register; this might be a guest error or a QEMU - * unimplemented feature. - */ - if (is64) { - qemu_log_mask(LOG_UNIMP, "%s access to unsupported AArch32 " - "64 bit system register cp:%d opc1: %d crm:%d " - "(%s)\n", - isread ? "read" : "write", cpnum, opc1, crm, - s->ns ? "non-secure" : "secure"); - } else { - qemu_log_mask(LOG_UNIMP, "%s access to unsupported AArch32 " - "system register cp:%d opc1:%d crn:%d crm:%d opc2:%d " - "(%s)\n", - isread ? "read" : "write", cpnum, opc1, crn, crm, opc2, - s->ns ? "non-secure" : "secure"); + /* Check access permissions */ + if (!cp_access_ok(s->current_el, ri, isread)) { + unallocated_encoding(s); + return; } - unallocated_encoding(s); - return; + if ((s->hstr_active && s->current_el == 0) || ri->accessfn || + (ri->fgt && s->fgt_active) || + (arm_dc_feature(s, ARM_FEATURE_XSCALE) && cpnum < 14)) { + /* + * Emit code to perform further access permissions checks at + * runtime; this may result in an exception. + * Note that on XScale all cp0..c13 registers do an access check + * call in order to handle c15_cpar. + */ + gen_set_condexec(s); + gen_update_pc(s, 0); + tcg_ri = tcg_temp_new_ptr(); + gen_helper_access_check_cp_reg(tcg_ri, tcg_env, + tcg_constant_i32(key), + tcg_constant_i32(syndrome), + tcg_constant_i32(isread)); + } else if (ri->type & ARM_CP_RAISES_EXC) { + /* + * The readfn or writefn might raise an exception; + * synchronize the CPU state in case it does. + */ + gen_set_condexec(s); + gen_update_pc(s, 0); + } + + /* Handle special cases first */ + switch (ri->type & ARM_CP_SPECIAL_MASK) { + case 0: + break; + case ARM_CP_NOP: + return; + case ARM_CP_WFI: + if (isread) { + unallocated_encoding(s); + } else { + gen_update_pc(s, curr_insn_len(s)); + s->base.is_jmp = DISAS_WFI; + } + return; + default: + g_assert_not_reached(); + } + + if (ri->type & ARM_CP_IO) { + /* I/O operations must end the TB here (whether read or write) */ + need_exit_tb = translator_io_start(&s->base); + } + + if (isread) { + /* Read */ + if (is64) { + TCGv_i64 tmp64; + TCGv_i32 tmp; + if (ri->type & ARM_CP_CONST) { + tmp64 = tcg_constant_i64(ri->resetvalue); + } else if (ri->readfn) { + if (!tcg_ri) { + tcg_ri = gen_lookup_cp_reg(key); + } + tmp64 = tcg_temp_new_i64(); + gen_helper_get_cp_reg64(tmp64, tcg_env, tcg_ri); + } else { + tmp64 = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp64, tcg_env, ri->fieldoffset); + } + tmp = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(tmp, tmp64); + store_reg(s, rt, tmp); + tmp = tcg_temp_new_i32(); + tcg_gen_extrh_i64_i32(tmp, tmp64); + store_reg(s, rt2, tmp); + } else { + TCGv_i32 tmp; + if (ri->type & ARM_CP_CONST) { + tmp = tcg_constant_i32(ri->resetvalue); + } else if (ri->readfn) { + if (!tcg_ri) { + tcg_ri = gen_lookup_cp_reg(key); + } + tmp = tcg_temp_new_i32(); + gen_helper_get_cp_reg(tmp, tcg_env, tcg_ri); + } else { + tmp = load_cpu_offset(ri->fieldoffset); + } + if (rt == 15) { + /* Destination register of r15 for 32 bit loads sets + * the condition codes from the high 4 bits of the value + */ + gen_set_nzcv(tmp); + } else { + store_reg(s, rt, tmp); + } + } + } else { + /* Write */ + if (ri->type & ARM_CP_CONST) { + /* If not forbidden by access permissions, treat as WI */ + return; + } + + if (is64) { + TCGv_i32 tmplo, tmphi; + TCGv_i64 tmp64 = tcg_temp_new_i64(); + tmplo = load_reg(s, rt); + tmphi = load_reg(s, rt2); + tcg_gen_concat_i32_i64(tmp64, tmplo, tmphi); + if (ri->writefn) { + if (!tcg_ri) { + tcg_ri = gen_lookup_cp_reg(key); + } + gen_helper_set_cp_reg64(tcg_env, tcg_ri, tmp64); + } else { + tcg_gen_st_i64(tmp64, tcg_env, ri->fieldoffset); + } + } else { + TCGv_i32 tmp = load_reg(s, rt); + if (ri->writefn) { + if (!tcg_ri) { + tcg_ri = gen_lookup_cp_reg(key); + } + gen_helper_set_cp_reg(tcg_env, tcg_ri, tmp); + } else { + store_cpu_offset(tmp, ri->fieldoffset, 4); + } + } + } + + if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) { + /* + * A write to any coprocessor register that ends a TB + * must rebuild the hflags for the next TB. + */ + gen_rebuild_hflags(s, ri->type & ARM_CP_NEWEL); + /* + * We default to ending the TB on a coprocessor register write, + * but allow this to be suppressed by the register definition + * (usually only necessary to work around guest bugs). + */ + need_exit_tb = true; + } + if (need_exit_tb) { + gen_lookup_tb(s); + } } /* Decode XScale DSP or iWMMXt insn (in the copro space, cp=0 or 1) */ @@ -4927,10 +4830,7 @@ static void gen_addq(DisasContext *s, TCGv_i64 val, int rlow, int rhigh) tmph = load_reg(s, rhigh); tmp = tcg_temp_new_i64(); tcg_gen_concat_i32_i64(tmp, tmpl, tmph); - tcg_temp_free_i32(tmpl); - tcg_temp_free_i32(tmph); tcg_gen_add_i64(val, val, tmp); - tcg_temp_free_i64(tmp); } /* Set N and Z flags from hi|lo. */ @@ -4969,15 +4869,12 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, TCGv taddr = gen_aa32_addr(s, addr, opc); tcg_gen_qemu_ld_i64(t64, taddr, get_mem_index(s), opc); - tcg_temp_free(taddr); tcg_gen_mov_i64(cpu_exclusive_val, t64); if (s->be_data == MO_BE) { tcg_gen_extr_i64_i32(tmp2, tmp, t64); } else { tcg_gen_extr_i64_i32(tmp, tmp2, t64); } - tcg_temp_free_i64(t64); - store_reg(s, rt2, tmp2); } else { gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), opc); @@ -5014,7 +4911,6 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, extaddr = tcg_temp_new_i64(); tcg_gen_extu_i32_i64(extaddr, addr); tcg_gen_brcond_i64(TCG_COND_NE, extaddr, cpu_exclusive_addr, fail_label); - tcg_temp_free_i64(extaddr); taddr = gen_aa32_addr(s, addr, opc); t0 = tcg_temp_new_i32(); @@ -5039,27 +4935,19 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, } else { tcg_gen_concat_i32_i64(n64, t1, t2); } - tcg_temp_free_i32(t2); tcg_gen_atomic_cmpxchg_i64(o64, taddr, cpu_exclusive_val, n64, get_mem_index(s), opc); - tcg_temp_free_i64(n64); tcg_gen_setcond_i64(TCG_COND_NE, o64, o64, cpu_exclusive_val); tcg_gen_extrl_i64_i32(t0, o64); - - tcg_temp_free_i64(o64); } else { t2 = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(t2, cpu_exclusive_val); tcg_gen_atomic_cmpxchg_i32(t0, taddr, t2, t1, get_mem_index(s), opc); tcg_gen_setcond_i32(TCG_COND_NE, t0, t0, t2); - tcg_temp_free_i32(t2); } - tcg_temp_free_i32(t1); - tcg_temp_free(taddr); tcg_gen_mov_i32(cpu_R[rd], t0); - tcg_temp_free_i32(t0); tcg_gen_br(done_label); gen_set_label(fail_label); @@ -5097,7 +4985,7 @@ static void gen_srs(DisasContext *s, * For the UNPREDICTABLE cases we choose to UNDEF. */ if (s->current_el == 1 && !s->ns && mode == ARM_CPU_MODE_MON) { - gen_exception_insn(s, s->pc_curr, EXCP_UDEF, syn_uncategorized(), 3); + gen_exception_insn_el(s, 0, EXCP_UDEF, syn_uncategorized(), 3); return; } @@ -5140,8 +5028,8 @@ static void gen_srs(DisasContext *s, addr = tcg_temp_new_i32(); /* get_r13_banked() will raise an exception if called from System mode */ gen_set_condexec(s); - gen_set_pc_im(s, s->pc_curr); - gen_helper_get_r13_banked(addr, cpu_env, tcg_constant_i32(mode)); + gen_update_pc(s, 0); + gen_helper_get_r13_banked(addr, tcg_env, tcg_constant_i32(mode)); switch (amode) { case 0: /* DA */ offset = -4; @@ -5161,11 +5049,9 @@ static void gen_srs(DisasContext *s, tcg_gen_addi_i32(addr, addr, offset); tmp = load_reg(s, 14); gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), MO_UL | MO_ALIGN); - tcg_temp_free_i32(tmp); tmp = load_cpu_field(spsr); tcg_gen_addi_i32(addr, addr, 4); gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), MO_UL | MO_ALIGN); - tcg_temp_free_i32(tmp); if (writeback) { switch (amode) { case 0: @@ -5184,9 +5070,8 @@ static void gen_srs(DisasContext *s, g_assert_not_reached(); } tcg_gen_addi_i32(addr, addr, offset); - gen_helper_set_r13_banked(cpu_env, tcg_constant_i32(mode), addr); + gen_helper_set_r13_banked(tcg_env, tcg_constant_i32(mode), addr); } - tcg_temp_free_i32(addr); s->base.is_jmp = DISAS_UPDATE_EXIT; } @@ -5194,7 +5079,7 @@ static void gen_srs(DisasContext *s, static void arm_skip_unless(DisasContext *s, uint32_t cond) { arm_gen_condlabel(s); - arm_gen_test_cc(cond ^ 1, s->condlabel); + arm_gen_test_cc(cond ^ 1, s->condlabel.label); } @@ -5378,7 +5263,6 @@ static bool store_reg_kind(DisasContext *s, int rd, { switch (kind) { case STREG_NONE: - tcg_temp_free_i32(val); return true; case STREG_NORMAL: /* See ALUWritePC: Interworking only from a32 mode. */ @@ -5415,7 +5299,6 @@ static bool op_s_rrr_shi(DisasContext *s, arg_s_rrr_shi *a, tmp1 = load_reg(s, a->rn); gen(tmp1, tmp1, tmp2); - tcg_temp_free_i32(tmp2); if (logic_cc) { gen_logic_CC(tmp1); @@ -5457,7 +5340,6 @@ static bool op_s_rrr_shr(DisasContext *s, arg_s_rrr_shr *a, tmp1 = load_reg(s, a->rn); gen(tmp1, tmp1, tmp2); - tcg_temp_free_i32(tmp2); if (logic_cc) { gen_logic_CC(tmp1); @@ -5716,7 +5598,6 @@ static bool do_mve_shl_ri(DisasContext *s, arg_mve_shl_ri *a, tcg_gen_extrh_i64_i32(rdahi, rda); store_reg(s, a->rdalo, rdalo); store_reg(s, a->rdahi, rdahi); - tcg_temp_free_i64(rda); return true; } @@ -5738,7 +5619,7 @@ static bool trans_LSRL_ri(DisasContext *s, arg_mve_shl_ri *a) static void gen_mve_sqshll(TCGv_i64 r, TCGv_i64 n, int64_t shift) { - gen_helper_mve_sqshll(r, cpu_env, n, tcg_constant_i32(shift)); + gen_helper_mve_sqshll(r, tcg_env, n, tcg_constant_i32(shift)); } static bool trans_SQSHLL_ri(DisasContext *s, arg_mve_shl_ri *a) @@ -5748,7 +5629,7 @@ static bool trans_SQSHLL_ri(DisasContext *s, arg_mve_shl_ri *a) static void gen_mve_uqshll(TCGv_i64 r, TCGv_i64 n, int64_t shift) { - gen_helper_mve_uqshll(r, cpu_env, n, tcg_constant_i32(shift)); + gen_helper_mve_uqshll(r, tcg_env, n, tcg_constant_i32(shift)); } static bool trans_UQSHLL_ri(DisasContext *s, arg_mve_shl_ri *a) @@ -5794,13 +5675,12 @@ static bool do_mve_shl_rr(DisasContext *s, arg_mve_shl_rr *a, WideShiftFn *fn) tcg_gen_concat_i32_i64(rda, rdalo, rdahi); /* The helper takes care of the sign-extension of the low 8 bits of Rm */ - fn(rda, cpu_env, rda, cpu_R[a->rm]); + fn(rda, tcg_env, rda, cpu_R[a->rm]); tcg_gen_extrl_i64_i32(rdalo, rda); tcg_gen_extrh_i64_i32(rdahi, rda); store_reg(s, a->rdalo, rdalo); store_reg(s, a->rdahi, rdahi); - tcg_temp_free_i64(rda); return true; } @@ -5869,7 +5749,7 @@ static bool trans_SRSHR_ri(DisasContext *s, arg_mve_sh_ri *a) static void gen_mve_sqshl(TCGv_i32 r, TCGv_i32 n, int32_t shift) { - gen_helper_mve_sqshl(r, cpu_env, n, tcg_constant_i32(shift)); + gen_helper_mve_sqshl(r, tcg_env, n, tcg_constant_i32(shift)); } static bool trans_SQSHL_ri(DisasContext *s, arg_mve_sh_ri *a) @@ -5879,7 +5759,7 @@ static bool trans_SQSHL_ri(DisasContext *s, arg_mve_sh_ri *a) static void gen_mve_uqshl(TCGv_i32 r, TCGv_i32 n, int32_t shift) { - gen_helper_mve_uqshl(r, cpu_env, n, tcg_constant_i32(shift)); + gen_helper_mve_uqshl(r, tcg_env, n, tcg_constant_i32(shift)); } static bool trans_UQSHL_ri(DisasContext *s, arg_mve_sh_ri *a) @@ -5903,7 +5783,7 @@ static bool do_mve_sh_rr(DisasContext *s, arg_mve_sh_rr *a, ShiftFn *fn) } /* The helper takes care of the sign-extension of the low 8 bits of Rm */ - fn(cpu_R[a->rda], cpu_env, cpu_R[a->rda], cpu_R[a->rm]); + fn(cpu_R[a->rda], tcg_env, cpu_R[a->rda], cpu_R[a->rm]); return true; } @@ -5928,11 +5808,9 @@ static bool op_mla(DisasContext *s, arg_s_rrrr *a, bool add) t1 = load_reg(s, a->rn); t2 = load_reg(s, a->rm); tcg_gen_mul_i32(t1, t1, t2); - tcg_temp_free_i32(t2); if (add) { t2 = load_reg(s, a->ra); tcg_gen_add_i32(t1, t1, t2); - tcg_temp_free_i32(t2); } if (a->s) { gen_logic_CC(t1); @@ -5961,10 +5839,8 @@ static bool trans_MLS(DisasContext *s, arg_MLS *a) t1 = load_reg(s, a->rn); t2 = load_reg(s, a->rm); tcg_gen_mul_i32(t1, t1, t2); - tcg_temp_free_i32(t2); t2 = load_reg(s, a->ra); tcg_gen_sub_i32(t1, t2, t1); - tcg_temp_free_i32(t2); store_reg(s, a->rd, t1); return true; } @@ -5984,8 +5860,6 @@ static bool op_mlal(DisasContext *s, arg_s_rrrr *a, bool uns, bool add) t2 = load_reg(s, a->ra); t3 = load_reg(s, a->rd); tcg_gen_add2_i32(t0, t1, t0, t1, t2, t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); } if (a->s) { gen_logicq_cc(t0, t1); @@ -6031,10 +5905,8 @@ static bool trans_UMAAL(DisasContext *s, arg_UMAAL *a) zero = tcg_constant_i32(0); t2 = load_reg(s, a->ra); tcg_gen_add2_i32(t0, t1, t0, t1, t2, zero); - tcg_temp_free_i32(t2); t2 = load_reg(s, a->rd); tcg_gen_add2_i32(t0, t1, t0, t1, t2, zero); - tcg_temp_free_i32(t2); store_reg(s, a->ra, t0); store_reg(s, a->rd, t1); return true; @@ -6057,14 +5929,13 @@ static bool op_qaddsub(DisasContext *s, arg_rrr *a, bool add, bool doub) t0 = load_reg(s, a->rm); t1 = load_reg(s, a->rn); if (doub) { - gen_helper_add_saturate(t1, cpu_env, t1, t1); + gen_helper_add_saturate(t1, tcg_env, t1, t1); } if (add) { - gen_helper_add_saturate(t0, cpu_env, t0, t1); + gen_helper_add_saturate(t0, tcg_env, t0, t1); } else { - gen_helper_sub_saturate(t0, cpu_env, t0, t1); + gen_helper_sub_saturate(t0, tcg_env, t0, t1); } - tcg_temp_free_i32(t1); store_reg(s, a->rd, t0); return true; } @@ -6100,7 +5971,6 @@ static bool op_smlaxxx(DisasContext *s, arg_rrrr *a, t0 = load_reg(s, a->rn); t1 = load_reg(s, a->rm); gen_mulxy(t0, t1, nt, mt); - tcg_temp_free_i32(t1); switch (add_long) { case 0: @@ -6108,8 +5978,7 @@ static bool op_smlaxxx(DisasContext *s, arg_rrrr *a, break; case 1: t1 = load_reg(s, a->ra); - gen_helper_add_setq(t0, cpu_env, t0, t1); - tcg_temp_free_i32(t1); + gen_helper_add_setq(t0, tcg_env, t0, t1); store_reg(s, a->rd, t0); break; case 2: @@ -6119,8 +5988,6 @@ static bool op_smlaxxx(DisasContext *s, arg_rrrr *a, t1 = tcg_temp_new_i32(); tcg_gen_sari_i32(t1, t0, 31); tcg_gen_add2_i32(tl, th, tl, th, t0, t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); store_reg(s, a->ra, tl); store_reg(s, a->rd, th); break; @@ -6173,11 +6040,9 @@ static bool op_smlawx(DisasContext *s, arg_rrrr *a, bool add, bool mt) tcg_gen_shli_i32(t1, t1, 16); } tcg_gen_muls2_i32(t0, t1, t0, t1); - tcg_temp_free_i32(t0); if (add) { t0 = load_reg(s, a->ra); - gen_helper_add_setq(t1, cpu_env, t1, t0); - tcg_temp_free_i32(t0); + gen_helper_add_setq(t1, tcg_env, t1, t0); } store_reg(s, a->rd, t1); return true; @@ -6209,7 +6074,7 @@ static bool trans_YIELD(DisasContext *s, arg_YIELD *a) * scheduling of other vCPUs. */ if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_set_pc_im(s, s->base.pc_next); + gen_update_pc(s, curr_insn_len(s)); s->base.is_jmp = DISAS_YIELD; } return true; @@ -6225,7 +6090,7 @@ static bool trans_WFE(DisasContext *s, arg_WFE *a) * implemented so we can't sleep like WFI does. */ if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_set_pc_im(s, s->base.pc_next); + gen_update_pc(s, curr_insn_len(s)); s->base.is_jmp = DISAS_WFE; } return true; @@ -6234,7 +6099,7 @@ static bool trans_WFE(DisasContext *s, arg_WFE *a) static bool trans_WFI(DisasContext *s, arg_WFI *a) { /* For WFI, halt the vCPU until an IRQ. */ - gen_set_pc_im(s, s->base.pc_next); + gen_update_pc(s, curr_insn_len(s)); s->base.is_jmp = DISAS_WFI; return true; } @@ -6256,7 +6121,7 @@ static bool trans_ESB(DisasContext *s, arg_ESB *a) * Test for EL2 present, and defer test for SEL2 to runtime. */ if (s->current_el <= 1 && arm_dc_feature(s, ARM_FEATURE_EL2)) { - gen_helper_vesb(cpu_env); + gen_helper_vesb(tcg_env); } } return true; @@ -6310,7 +6175,6 @@ static bool op_crc32(DisasContext *s, arg_rrr *a, bool c, MemOp sz) } else { gen_helper_crc32(t1, t1, t2, t3); } - tcg_temp_free_i32(t2); store_reg(s, a->rd, t1); return true; } @@ -6365,7 +6229,7 @@ static bool trans_MRS_reg(DisasContext *s, arg_MRS_reg *a) tmp = load_cpu_field(spsr); } else { tmp = tcg_temp_new_i32(); - gen_helper_cpsr_read(tmp, cpu_env); + gen_helper_cpsr_read(tmp, tcg_env); } store_reg(s, a->rd, tmp); return true; @@ -6394,7 +6258,7 @@ static bool trans_MRS_v7m(DisasContext *s, arg_MRS_v7m *a) return false; } tmp = tcg_temp_new_i32(); - gen_helper_v7m_mrs(tmp, cpu_env, tcg_constant_i32(a->sysm)); + gen_helper_v7m_mrs(tmp, tcg_env, tcg_constant_i32(a->sysm)); store_reg(s, a->rd, tmp); return true; } @@ -6408,8 +6272,7 @@ static bool trans_MSR_v7m(DisasContext *s, arg_MSR_v7m *a) } addr = tcg_constant_i32((a->mask << 10) | a->sysm); reg = load_reg(s, a->rn); - gen_helper_v7m_msr(cpu_env, addr, reg); - tcg_temp_free_i32(reg); + gen_helper_v7m_msr(tcg_env, addr, reg); /* If we wrote to CONTROL, the EL might have changed */ gen_rebuild_hflags(s, true); gen_lookup_tb(s); @@ -6440,7 +6303,7 @@ static bool trans_BXJ(DisasContext *s, arg_BXJ *a) if (!arm_dc_feature(s, ARM_FEATURE_V8) && arm_dc_feature(s, ARM_FEATURE_EL2) && s->current_el < 2 && s->ns) { - gen_helper_check_bxj_trap(cpu_env, tcg_constant_i32(a->rm)); + gen_helper_check_bxj_trap(tcg_env, tcg_constant_i32(a->rm)); } /* Trivial implementation equivalent to bx. */ gen_bx(s, load_reg(s, a->rm)); @@ -6455,7 +6318,7 @@ static bool trans_BLX_r(DisasContext *s, arg_BLX_r *a) return false; } tmp = load_reg(s, a->rm); - tcg_gen_movi_i32(cpu_R[14], s->base.pc_next | s->thumb); + gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | s->thumb); gen_bx(s, tmp); return true; } @@ -6512,7 +6375,7 @@ static bool trans_ERET(DisasContext *s, arg_ERET *a) } if (s->current_el == 2) { /* ERET from Hyp uses ELR_Hyp, not LR */ - tmp = load_cpu_field(elr_el[2]); + tmp = load_cpu_field_low32(elr_el[2]); } else { tmp = load_reg(s, 14); } @@ -6534,12 +6397,9 @@ static bool trans_BKPT(DisasContext *s, arg_BKPT *a) /* BKPT is OK with ECI set and leaves it untouched */ s->eci_handled = true; if (arm_dc_feature(s, ARM_FEATURE_M) && - semihosting_enabled() && -#ifndef CONFIG_USER_ONLY - !IS_USER(s) && -#endif + semihosting_enabled(s->current_el == 0) && (a->imm == 0xab)) { - gen_exception_internal_insn(s, s->pc_curr, EXCP_SEMIHOST); + gen_exception_internal_insn(s, EXCP_SEMIHOST); } else { gen_exception_bkpt_insn(s, syn_aa32_bkpt(a->imm, false)); } @@ -6621,8 +6481,7 @@ static bool trans_TT(DisasContext *s, arg_TT *a) addr = load_reg(s, a->rn); tmp = tcg_temp_new_i32(); - gen_helper_v7m_tt(tmp, cpu_env, addr, tcg_constant_i32((a->A << 1) | a->T)); - tcg_temp_free_i32(addr); + gen_helper_v7m_tt(tmp, tcg_env, addr, tcg_constant_i32((a->A << 1) | a->T)); store_reg(s, a->rd, tmp); return true; } @@ -6638,7 +6497,7 @@ 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) { + if (curr_insn_len(s) == 2) { ret |= ISSIs16Bit; } } else { @@ -6652,7 +6511,7 @@ static TCGv_i32 op_addr_rr_pre(DisasContext *s, arg_ldst_rr *a) TCGv_i32 addr = load_reg(s, a->rn); if (s->v8m_stackcheck && a->rn == 13 && a->w) { - gen_helper_v8m_stackcheck(cpu_env, addr); + gen_helper_v8m_stackcheck(tcg_env, addr); } if (a->p) { @@ -6663,7 +6522,6 @@ static TCGv_i32 op_addr_rr_pre(DisasContext *s, arg_ldst_rr *a) } else { tcg_gen_sub_i32(addr, addr, ofs); } - tcg_temp_free_i32(ofs); } return addr; } @@ -6679,9 +6537,7 @@ static void op_addr_rr_post(DisasContext *s, arg_ldst_rr *a, } else { tcg_gen_sub_i32(addr, addr, ofs); } - tcg_temp_free_i32(ofs); } else if (!a->w) { - tcg_temp_free_i32(addr); return; } tcg_gen_addi_i32(addr, addr, address_offset); @@ -6728,7 +6584,6 @@ static bool op_store_rr(DisasContext *s, arg_ldst_rr *a, tmp = load_reg(s, a->rt); gen_aa32_st_i32(s, tmp, addr, mem_idx, mop); disas_set_da_iss(s, mop, issinfo); - tcg_temp_free_i32(tmp); op_addr_rr_post(s, a, addr, 0); return true; @@ -6779,13 +6634,11 @@ static bool trans_STRD_rr(DisasContext *s, arg_ldst_rr *a) tmp = load_reg(s, a->rt); gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - tcg_temp_free_i32(tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = load_reg(s, a->rt + 1); gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - tcg_temp_free_i32(tmp); op_addr_rr_post(s, a, addr, -4); return true; @@ -6813,10 +6666,9 @@ static TCGv_i32 op_addr_ri_pre(DisasContext *s, arg_ldst_ri *a) if (!a->u) { TCGv_i32 newsp = tcg_temp_new_i32(); tcg_gen_addi_i32(newsp, cpu_R[13], ofs); - gen_helper_v8m_stackcheck(cpu_env, newsp); - tcg_temp_free_i32(newsp); + gen_helper_v8m_stackcheck(tcg_env, newsp); } else { - gen_helper_v8m_stackcheck(cpu_env, cpu_R[13]); + gen_helper_v8m_stackcheck(tcg_env, cpu_R[13]); } } @@ -6833,7 +6685,6 @@ static void op_addr_ri_post(DisasContext *s, arg_ldst_ri *a, address_offset -= a->imm; } } else if (!a->w) { - tcg_temp_free_i32(addr); return; } tcg_gen_addi_i32(addr, addr, address_offset); @@ -6880,7 +6731,6 @@ static bool op_store_ri(DisasContext *s, arg_ldst_ri *a, tmp = load_reg(s, a->rt); gen_aa32_st_i32(s, tmp, addr, mem_idx, mop); disas_set_da_iss(s, mop, issinfo); - tcg_temp_free_i32(tmp); op_addr_ri_post(s, a, addr, 0); return true; @@ -6934,13 +6784,11 @@ static bool op_strd_ri(DisasContext *s, arg_ldst_ri *a, int rt2) tmp = load_reg(s, a->rt); gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - tcg_temp_free_i32(tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = load_reg(s, rt2); gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - tcg_temp_free_i32(tmp); op_addr_ri_post(s, a, addr, -4); return true; @@ -7005,11 +6853,9 @@ static bool op_swp(DisasContext *s, arg_SWP *a, MemOp opc) opc |= s->be_data; addr = load_reg(s, a->rn); taddr = gen_aa32_addr(s, addr, opc); - tcg_temp_free_i32(addr); tmp = load_reg(s, a->rt2); tcg_gen_atomic_xchg_i32(tmp, taddr, tmp, get_mem_index(s), opc); - tcg_temp_free(taddr); store_reg(s, a->rt, tmp); return true; @@ -7051,12 +6897,11 @@ static bool op_strex(DisasContext *s, arg_STREX *a, MemOp mop, bool rel) tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); } - addr = tcg_temp_local_new_i32(); + addr = tcg_temp_new_i32(); load_reg_var(s, addr, a->rn); tcg_gen_addi_i32(addr, addr, a->imm); gen_store_exclusive(s, a->rd, a->rt, a->rt2, addr, mop); - tcg_temp_free_i32(addr); return true; } @@ -7168,8 +7013,6 @@ static bool op_stl(DisasContext *s, arg_STL *a, MemOp mop) gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), mop | MO_ALIGN); disas_set_da_iss(s, mop, a->rt | ISSIsAcqRel | ISSIsWrite); - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(addr); return true; } @@ -7204,12 +7047,11 @@ static bool op_ldrex(DisasContext *s, arg_LDREX *a, MemOp mop, bool acq) return true; } - addr = tcg_temp_local_new_i32(); + addr = tcg_temp_new_i32(); load_reg_var(s, addr, a->rn); tcg_gen_addi_i32(addr, addr, a->imm); gen_load_exclusive(s, a->rt, a->rt2, addr, mop); - tcg_temp_free_i32(addr); if (acq) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); @@ -7323,7 +7165,6 @@ static bool op_lda(DisasContext *s, arg_LDA *a, MemOp mop) tmp = tcg_temp_new_i32(); gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), mop | MO_ALIGN); disas_set_da_iss(s, mop, a->rt | ISSIsAcqRel); - tcg_temp_free_i32(addr); store_reg(s, a->rt, tmp); tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); @@ -7360,11 +7201,9 @@ static bool trans_USADA8(DisasContext *s, arg_USADA8 *a) t1 = load_reg(s, a->rn); t2 = load_reg(s, a->rm); gen_helper_usad8(t1, t1, t2); - tcg_temp_free_i32(t2); if (a->ra != 15) { t2 = load_reg(s, a->ra); tcg_gen_add_i32(t1, t1, t2); - tcg_temp_free_i32(t2); } store_reg(s, a->rd, t1); return true; @@ -7407,8 +7246,8 @@ static bool trans_UBFX(DisasContext *s, arg_UBFX *a) static bool trans_BFCI(DisasContext *s, arg_BFCI *a) { - TCGv_i32 tmp; int msb = a->msb, lsb = a->lsb; + TCGv_i32 t_in, t_rd; int width; if (!ENABLE_ARCH_6T2) { @@ -7423,17 +7262,14 @@ static bool trans_BFCI(DisasContext *s, arg_BFCI *a) width = msb + 1 - lsb; if (a->rn == 15) { /* BFC */ - tmp = tcg_const_i32(0); + t_in = tcg_constant_i32(0); } else { /* BFI */ - tmp = load_reg(s, a->rn); + t_in = load_reg(s, a->rn); } - if (width != 32) { - TCGv_i32 tmp2 = load_reg(s, a->rd); - tcg_gen_deposit_i32(tmp, tmp2, tmp, lsb, width); - tcg_temp_free_i32(tmp2); - } - store_reg(s, a->rd, tmp); + t_rd = load_reg(s, a->rd); + tcg_gen_deposit_i32(t_rd, t_rd, t_in, lsb, width); + store_reg(s, a->rd, t_rd); return true; } @@ -7463,7 +7299,6 @@ static bool op_par_addsub(DisasContext *s, arg_rrr *a, gen(t0, t0, t1); - tcg_temp_free_i32(t1); store_reg(s, a->rd, t0); return true; } @@ -7485,11 +7320,9 @@ static bool op_par_addsub_ge(DisasContext *s, arg_rrr *a, t1 = load_reg(s, a->rm); ge = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(ge, cpu_env, offsetof(CPUARMState, GE)); + tcg_gen_addi_ptr(ge, tcg_env, offsetof(CPUARMState, GE)); gen(t0, t0, t1, ge); - tcg_temp_free_ptr(ge); - tcg_temp_free_i32(t1); store_reg(s, a->rd, t0); return true; } @@ -7580,7 +7413,6 @@ static bool trans_PKH(DisasContext *s, arg_PKH *a) tcg_gen_shli_i32(tm, tm, shift); tcg_gen_deposit_i32(tn, tm, tn, 0, 16); } - tcg_temp_free_i32(tm); store_reg(s, a->rd, tn); return true; } @@ -7602,7 +7434,7 @@ static bool op_sat(DisasContext *s, arg_sat *a, tcg_gen_shli_i32(tmp, tmp, shift); } - gen(tmp, cpu_env, tmp, tcg_constant_i32(a->satimm)); + gen(tmp, tcg_env, tmp, tcg_constant_i32(a->satimm)); store_reg(s, a->rd, tmp); return true; @@ -7655,7 +7487,6 @@ static bool op_xta(DisasContext *s, arg_rrr_rot *a, if (a->rn != 15) { TCGv_i32 tmp2 = load_reg(s, a->rn); gen_add(tmp, tmp, tmp2); - tcg_temp_free_i32(tmp2); } store_reg(s, a->rd, tmp); return true; @@ -7710,10 +7541,8 @@ static bool trans_SEL(DisasContext *s, arg_rrr *a) t1 = load_reg(s, a->rn); t2 = load_reg(s, a->rm); t3 = tcg_temp_new_i32(); - tcg_gen_ld_i32(t3, cpu_env, offsetof(CPUARMState, GE)); + tcg_gen_ld_i32(t3, tcg_env, offsetof(CPUARMState, GE)); gen_helper_sel_flags(t1, t3, t1, t2); - tcg_temp_free_i32(t3); - tcg_temp_free_i32(t2); store_reg(s, a->rd, t1); return true; } @@ -7787,17 +7616,14 @@ static bool op_smlad(DisasContext *s, arg_rrrr *a, bool m_swap, bool sub) * addition of Ra. */ tcg_gen_sub_i32(t1, t1, t2); - tcg_temp_free_i32(t2); if (a->ra != 15) { t2 = load_reg(s, a->ra); - gen_helper_add_setq(t1, cpu_env, t1, t2); - tcg_temp_free_i32(t2); + gen_helper_add_setq(t1, tcg_env, t1, t2); } } else if (a->ra == 15) { /* Single saturation-checking addition */ - gen_helper_add_setq(t1, cpu_env, t1, t2); - tcg_temp_free_i32(t2); + gen_helper_add_setq(t1, tcg_env, t1, t2); } else { /* * We need to add the products and Ra together and then @@ -7817,10 +7643,8 @@ static bool op_smlad(DisasContext *s, arg_rrrr *a, bool m_swap, bool sub) load_reg_var(s, t2, a->ra); tcg_gen_ext_i32_i64(q64, t2); tcg_gen_add_i64(p64, p64, q64); - tcg_temp_free_i64(q64); tcg_gen_extr_i64_i32(t1, t2, p64); - tcg_temp_free_i64(p64); /* * t1 is the low half of the result which goes into Rd. * We have overflow and must set Q if the high half (t2) @@ -7832,8 +7656,6 @@ static bool op_smlad(DisasContext *s, arg_rrrr *a, bool m_swap, bool sub) one = tcg_constant_i32(1); tcg_gen_movcond_i32(TCG_COND_NE, qf, t2, t3, one, qf); store_cpu_field(qf, QF); - tcg_temp_free_i32(t3); - tcg_temp_free_i32(t2); } store_reg(s, a->rd, t1); return true; @@ -7879,19 +7701,15 @@ static bool op_smlald(DisasContext *s, arg_rrrr *a, bool m_swap, bool sub) l2 = tcg_temp_new_i64(); tcg_gen_ext_i32_i64(l1, t1); tcg_gen_ext_i32_i64(l2, t2); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); if (sub) { tcg_gen_sub_i64(l1, l1, l2); } else { tcg_gen_add_i64(l1, l1, l2); } - tcg_temp_free_i64(l2); gen_addq(s, l1, a->ra, a->rd); gen_storeq_reg(s, a->ra, a->rd, l1); - tcg_temp_free_i64(l1); return true; } @@ -7941,7 +7759,6 @@ static bool op_smmla(DisasContext *s, arg_rrrr *a, bool round, bool sub) } else { tcg_gen_add_i32(t1, t1, t3); } - tcg_temp_free_i32(t3); } if (round) { /* @@ -7951,7 +7768,6 @@ static bool op_smmla(DisasContext *s, arg_rrrr *a, bool round, bool sub) tcg_gen_shri_i32(t2, t2, 31); tcg_gen_add_i32(t1, t1, t2); } - tcg_temp_free_i32(t2); store_reg(s, a->rd, t1); return true; } @@ -7989,11 +7805,10 @@ static bool op_div(DisasContext *s, arg_rrr *a, bool u) t1 = load_reg(s, a->rn); t2 = load_reg(s, a->rm); if (u) { - gen_helper_udiv(t1, cpu_env, t1, t2); + gen_helper_udiv(t1, tcg_env, t1, t2); } else { - gen_helper_sdiv(t1, cpu_env, t1, t2); + gen_helper_sdiv(t1, tcg_env, t1, t2); } - tcg_temp_free_i32(t2); store_reg(s, a->rd, t1); return true; } @@ -8034,14 +7849,14 @@ static TCGv_i32 op_addr_block_pre(DisasContext *s, arg_ldst_block *a, int n) * If the writeback is incrementing SP rather than * decrementing it, and the initial SP is below the * stack limit but the final written-back SP would - * be above, then then we must not perform any memory + * be above, then we must not perform any memory * accesses, but it is IMPDEF whether we generate * an exception. We choose to do so in this case. * At this point 'addr' is the lowest address, so * either the original SP (if incrementing) or our * final SP (if decrementing), so that's what we check. */ - gen_helper_v8m_stackcheck(cpu_env, addr); + gen_helper_v8m_stackcheck(tcg_env, addr); } return addr; @@ -8065,12 +7880,10 @@ static void op_addr_block_post(DisasContext *s, arg_ldst_block *a, tcg_gen_addi_i32(addr, addr, -((n - 1) * 4)); } store_reg(s, a->rn, addr); - } else { - tcg_temp_free_i32(addr); } } -static bool op_stm(DisasContext *s, arg_ldst_block *a, int min_n) +static bool op_stm(DisasContext *s, arg_ldst_block *a) { int i, j, n, list, mem_idx; bool user = a->u; @@ -8087,7 +7900,14 @@ static bool op_stm(DisasContext *s, arg_ldst_block *a, int min_n) list = a->list; n = ctpop16(list); - if (n < min_n || a->rn == 15) { + /* + * This is UNPREDICTABLE for n < 1 in all encodings, and we choose + * to UNDEF. In the T32 STM encoding n == 1 is also UNPREDICTABLE, + * but hardware treats it like the A32 version and implements the + * single-register-store, and some in-the-wild (buggy) software + * assumes that, so we don't UNDEF on that case. + */ + if (n < 1 || a->rn == 15) { unallocated_encoding(s); return true; } @@ -8104,12 +7924,11 @@ static bool op_stm(DisasContext *s, arg_ldst_block *a, int min_n) if (user && i != 15) { tmp = tcg_temp_new_i32(); - gen_helper_get_user_reg(tmp, cpu_env, tcg_constant_i32(i)); + gen_helper_get_user_reg(tmp, tcg_env, tcg_constant_i32(i)); } else { tmp = load_reg(s, i); } gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - tcg_temp_free_i32(tmp); /* No need to add after the last transfer. */ if (++j != n) { @@ -8124,8 +7943,7 @@ static bool op_stm(DisasContext *s, arg_ldst_block *a, int min_n) static bool trans_STM(DisasContext *s, arg_ldst_block *a) { - /* BitCount(list) < 1 is UNPREDICTABLE */ - return op_stm(s, a, 1); + return op_stm(s, a); } static bool trans_STM_t32(DisasContext *s, arg_ldst_block *a) @@ -8135,11 +7953,10 @@ static bool trans_STM_t32(DisasContext *s, arg_ldst_block *a) unallocated_encoding(s); return true; } - /* BitCount(list) < 2 is UNPREDICTABLE */ - return op_stm(s, a, 2); + return op_stm(s, a); } -static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n) +static bool do_ldm(DisasContext *s, arg_ldst_block *a) { int i, j, n, list, mem_idx; bool loaded_base; @@ -8168,7 +7985,14 @@ static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n) list = a->list; n = ctpop16(list); - if (n < min_n || a->rn == 15) { + /* + * This is UNPREDICTABLE for n < 1 in all encodings, and we choose + * to UNDEF. In the T32 LDM encoding n == 1 is also UNPREDICTABLE, + * but hardware treats it like the A32 version and implements the + * single-register-load, and some in-the-wild (buggy) software + * assumes that, so we don't UNDEF on that case. + */ + if (n < 1 || a->rn == 15) { unallocated_encoding(s); return true; } @@ -8188,8 +8012,7 @@ static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n) tmp = tcg_temp_new_i32(); gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); if (user) { - gen_helper_set_user_reg(cpu_env, tcg_constant_i32(i), tmp); - tcg_temp_free_i32(tmp); + gen_helper_set_user_reg(tcg_env, tcg_constant_i32(i), tmp); } else if (i == a->rn) { loaded_var = tmp; loaded_base = true; @@ -8215,11 +8038,8 @@ static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n) if (exc_return) { /* Restore CPSR from SPSR. */ tmp = load_cpu_field(spsr); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_cpsr_write_eret(cpu_env, tmp); - tcg_temp_free_i32(tmp); + translator_io_start(&s->base); + gen_helper_cpsr_write_eret(tcg_env, tmp); /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; } @@ -8238,8 +8058,7 @@ static bool trans_LDM_a32(DisasContext *s, arg_ldst_block *a) unallocated_encoding(s); return true; } - /* BitCount(list) < 1 is UNPREDICTABLE */ - return do_ldm(s, a, 1); + return do_ldm(s, a); } static bool trans_LDM_t32(DisasContext *s, arg_ldst_block *a) @@ -8249,16 +8068,14 @@ static bool trans_LDM_t32(DisasContext *s, arg_ldst_block *a) unallocated_encoding(s); return true; } - /* BitCount(list) < 2 is UNPREDICTABLE */ - return do_ldm(s, a, 2); + return do_ldm(s, a); } static bool trans_LDM_t16(DisasContext *s, arg_ldst_block *a) { /* Writeback is conditional on the base register not being loaded. */ a->w = !(a->list & (1 << a->rn)); - /* BitCount(list) < 1 is UNPREDICTABLE */ - return do_ldm(s, a, 1); + return do_ldm(s, a); } static bool trans_CLRM(DisasContext *s, arg_CLRM *a) @@ -8293,7 +8110,7 @@ static bool trans_CLRM(DisasContext *s, arg_CLRM *a) * Clear APSR (by calling the MSR helper with the same argument * as for "MSR APSR_nzcvqg, Rn": mask = 0b1100, SYSM=0) */ - gen_helper_v7m_msr(cpu_env, tcg_constant_i32(0xc00), zero); + gen_helper_v7m_msr(tcg_env, tcg_constant_i32(0xc00), zero); } clear_eci_state(s); return true; @@ -8305,7 +8122,7 @@ static bool trans_CLRM(DisasContext *s, arg_CLRM *a) static bool trans_B(DisasContext *s, arg_i *a) { - gen_jmp(s, read_pc(s) + a->imm); + gen_jmp(s, jmp_diff(s, a->imm)); return true; } @@ -8320,14 +8137,14 @@ static bool trans_B_cond_thumb(DisasContext *s, arg_ci *a) return true; } arm_skip_unless(s, a->cond); - gen_jmp(s, read_pc(s) + a->imm); + gen_jmp(s, jmp_diff(s, a->imm)); return true; } static bool trans_BL(DisasContext *s, arg_i *a) { - tcg_gen_movi_i32(cpu_R[14], s->base.pc_next | s->thumb); - gen_jmp(s, read_pc(s) + a->imm); + gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | s->thumb); + gen_jmp(s, jmp_diff(s, a->imm)); return true; } @@ -8345,16 +8162,17 @@ static bool trans_BLX_i(DisasContext *s, arg_BLX_i *a) if (s->thumb && (a->imm & 2)) { return false; } - tcg_gen_movi_i32(cpu_R[14], s->base.pc_next | s->thumb); + gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | s->thumb); store_cpu_field_constant(!s->thumb, thumb); - gen_jmp(s, (read_pc(s) & ~3) + a->imm); + /* This jump is computed from an aligned PC: subtract off the low bits. */ + gen_jmp(s, jmp_diff(s, a->imm - (s->pc_curr & 3))); return true; } static bool trans_BL_BLX_prefix(DisasContext *s, arg_BL_BLX_prefix *a) { assert(!arm_dc_feature(s, ARM_FEATURE_THUMB2)); - tcg_gen_movi_i32(cpu_R[14], read_pc(s) + (a->imm << 12)); + gen_pc_plus_diff(s, cpu_R[14], jmp_diff(s, a->imm << 12)); return true; } @@ -8364,7 +8182,7 @@ static bool trans_BL_suffix(DisasContext *s, arg_BL_suffix *a) assert(!arm_dc_feature(s, ARM_FEATURE_THUMB2)); tcg_gen_addi_i32(tmp, cpu_R[14], (a->imm << 1) | 1); - tcg_gen_movi_i32(cpu_R[14], s->base.pc_next | 1); + gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | 1); gen_bx(s, tmp); return true; } @@ -8380,7 +8198,7 @@ static bool trans_BLX_suffix(DisasContext *s, arg_BLX_suffix *a) tmp = tcg_temp_new_i32(); tcg_gen_addi_i32(tmp, cpu_R[14], a->imm << 1); tcg_gen_andi_i32(tmp, tmp, 0xfffffffc); - tcg_gen_movi_i32(cpu_R[14], s->base.pc_next | 1); + gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | 1); gen_bx(s, tmp); return true; } @@ -8447,7 +8265,7 @@ static bool trans_WLS(DisasContext *s, arg_WLS *a) { /* M-profile low-overhead while-loop start */ TCGv_i32 tmp; - TCGLabel *nextlabel; + DisasLabel nextlabel; if (!dc_isar_feature(aa32_lob, s)) { return false; @@ -8482,14 +8300,14 @@ static bool trans_WLS(DisasContext *s, arg_WLS *a) * Do the check-and-raise-exception by hand. */ if (s->fp_excp_el) { - gen_exception_insn(s, s->pc_curr, EXCP_NOCP, - syn_uncategorized(), s->fp_excp_el); + gen_exception_insn_el(s, 0, EXCP_NOCP, + syn_uncategorized(), s->fp_excp_el); return true; } } - nextlabel = gen_new_label(); - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_R[a->rn], 0, nextlabel); + nextlabel = gen_disas_label(s); + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_R[a->rn], 0, nextlabel.label); tmp = load_reg(s, a->rn); store_reg(s, 14, tmp); if (a->size != 4) { @@ -8508,10 +8326,10 @@ static bool trans_WLS(DisasContext *s, arg_WLS *a) * when we take this upcoming exit from this TB, so gen_jmp_tb() is OK. */ } - gen_jmp_tb(s, s->base.pc_next, 1); + gen_jmp_tb(s, curr_insn_len(s), 1); - gen_set_label(nextlabel); - gen_jmp(s, read_pc(s) + a->imm); + set_disas_label(s, nextlabel); + gen_jmp(s, jmp_diff(s, a->imm)); return true; } @@ -8526,7 +8344,7 @@ static bool trans_LE(DisasContext *s, arg_LE *a) * any faster. */ TCGv_i32 tmp; - TCGLabel *loopend; + DisasLabel loopend; bool fpu_active; if (!dc_isar_feature(aa32_lob, s)) { @@ -8581,18 +8399,16 @@ static bool trans_LE(DisasContext *s, arg_LE *a) if (!a->tp && dc_isar_feature(aa32_mve, s) && fpu_active) { /* Need to do a runtime check for LTPSIZE != 4 */ - TCGLabel *skipexc = gen_new_label(); + DisasLabel skipexc = gen_disas_label(s); tmp = load_cpu_field(v7m.ltpsize); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 4, skipexc); - tcg_temp_free_i32(tmp); - gen_exception_insn(s, s->pc_curr, EXCP_INVSTATE, syn_uncategorized(), - default_exception_el(s)); - gen_set_label(skipexc); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 4, skipexc.label); + gen_exception_insn(s, 0, EXCP_INVSTATE, syn_uncategorized()); + set_disas_label(s, skipexc); } if (a->f) { /* Loop-forever: just jump back to the loop start */ - gen_jmp(s, read_pc(s) - a->imm); + gen_jmp(s, jmp_diff(s, -a->imm)); return true; } @@ -8602,36 +8418,34 @@ static bool trans_LE(DisasContext *s, arg_LE *a) * loop decrement value is 1. For LETP we need to calculate the decrement * value from LTPSIZE. */ - loopend = gen_new_label(); + loopend = gen_disas_label(s); if (!a->tp) { - tcg_gen_brcondi_i32(TCG_COND_LEU, cpu_R[14], 1, loopend); + tcg_gen_brcondi_i32(TCG_COND_LEU, cpu_R[14], 1, loopend.label); tcg_gen_addi_i32(cpu_R[14], cpu_R[14], -1); } else { /* * Decrement by 1 << (4 - LTPSIZE). We need to use a TCG local * so that decr stays live after the brcondi. */ - TCGv_i32 decr = tcg_temp_local_new_i32(); + TCGv_i32 decr = tcg_temp_new_i32(); TCGv_i32 ltpsize = load_cpu_field(v7m.ltpsize); tcg_gen_sub_i32(decr, tcg_constant_i32(4), ltpsize); tcg_gen_shl_i32(decr, tcg_constant_i32(1), decr); - tcg_temp_free_i32(ltpsize); - tcg_gen_brcond_i32(TCG_COND_LEU, cpu_R[14], decr, loopend); + tcg_gen_brcond_i32(TCG_COND_LEU, cpu_R[14], decr, loopend.label); tcg_gen_sub_i32(cpu_R[14], cpu_R[14], decr); - tcg_temp_free_i32(decr); } /* Jump back to the loop start */ - gen_jmp(s, read_pc(s) - a->imm); + gen_jmp(s, jmp_diff(s, -a->imm)); - gen_set_label(loopend); + set_disas_label(s, loopend); if (a->tp) { /* Exits from tail-pred loops must reset LTPSIZE to 4 */ store_cpu_field(tcg_constant_i32(4), v7m.ltpsize); } /* End TB, continuing to following insn */ - gen_jmp_tb(s, s->base.pc_next, 1); + gen_jmp_tb(s, curr_insn_len(s), 1); return true; } @@ -8683,9 +8497,7 @@ static bool trans_VCTP(DisasContext *s, arg_VCTP *a) tcg_gen_movcond_i32(TCG_COND_LEU, masklen, masklen, tcg_constant_i32(1 << (4 - a->size)), rn_shifted, tcg_constant_i32(16)); - gen_helper_mve_vctp(cpu_env, masklen); - tcg_temp_free_i32(masklen); - tcg_temp_free_i32(rn_shifted); + gen_helper_mve_vctp(tcg_env, masklen); /* This insn updates predication bits */ s->base.is_jmp = DISAS_UPDATE_NOCHAIN; mve_update_eci(s); @@ -8704,10 +8516,10 @@ static bool op_tbranch(DisasContext *s, arg_tbranch *a, bool half) tcg_gen_add_i32(addr, addr, tmp); gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), half ? MO_UW : MO_UB); - tcg_temp_free_i32(addr); tcg_gen_add_i32(tmp, tmp, tmp); - tcg_gen_addi_i32(tmp, tmp, read_pc(s)); + gen_pc_plus_diff(s, addr, jmp_diff(s, 0)); + tcg_gen_add_i32(tmp, tmp, addr); store_reg(s, 15, tmp); return true; } @@ -8728,9 +8540,8 @@ static bool trans_CBZ(DisasContext *s, arg_CBZ *a) arm_gen_condlabel(s); tcg_gen_brcondi_i32(a->nz ? TCG_COND_EQ : TCG_COND_NE, - tmp, 0, s->condlabel); - tcg_temp_free_i32(tmp); - gen_jmp(s, read_pc(s) + a->imm); + tmp, 0, s->condlabel.label); + gen_jmp(s, jmp_diff(s, a->imm)); return true; } @@ -8743,16 +8554,19 @@ static bool trans_SVC(DisasContext *s, arg_SVC *a) { const uint32_t semihost_imm = s->thumb ? 0xab : 0x123456; - if (!arm_dc_feature(s, ARM_FEATURE_M) && semihosting_enabled() && -#ifndef CONFIG_USER_ONLY - !IS_USER(s) && -#endif + if (!arm_dc_feature(s, ARM_FEATURE_M) && + semihosting_enabled(s->current_el == 0) && (a->imm == semihost_imm)) { - gen_exception_internal_insn(s, s->pc_curr, EXCP_SEMIHOST); + gen_exception_internal_insn(s, EXCP_SEMIHOST); } else { - gen_set_pc_im(s, s->base.pc_next); - s->svc_imm = a->imm; - s->base.is_jmp = DISAS_SWI; + if (s->fgt_svc) { + uint32_t syndrome = syn_aa32_svc(a->imm, s->thumb); + gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2); + } else { + gen_update_pc(s, curr_insn_len(s)); + s->svc_imm = a->imm; + s->base.is_jmp = DISAS_SWI; + } } return true; } @@ -8793,8 +8607,6 @@ static bool trans_RFE(DisasContext *s, arg_RFE *a) /* Base writeback. */ tcg_gen_addi_i32(addr, addr, post_offset[a->pu]); store_reg(s, a->rn, addr); - } else { - tcg_temp_free_i32(addr); } gen_rfe(s, t1, t2); return true; @@ -8863,12 +8675,12 @@ static bool trans_CPS_v7m(DisasContext *s, arg_CPS_v7m *a) /* FAULTMASK */ if (a->F) { addr = tcg_constant_i32(19); - gen_helper_v7m_msr(cpu_env, addr, tmp); + gen_helper_v7m_msr(tcg_env, addr, tmp); } /* PRIMASK */ if (a->I) { addr = tcg_constant_i32(16); - gen_helper_v7m_msr(cpu_env, addr, tmp); + gen_helper_v7m_msr(tcg_env, addr, tmp); } gen_rebuild_hflags(s, false); gen_lookup_tb(s); @@ -8938,7 +8750,7 @@ static bool trans_SETEND(DisasContext *s, arg_SETEND *a) return false; } if (a->E != (s->be_data == MO_BE)) { - gen_helper_setend(cpu_env); + gen_helper_setend(tcg_env); s->base.is_jmp = DISAS_UPDATE_EXIT; } return true; @@ -8988,7 +8800,7 @@ static bool trans_IT(DisasContext *s, arg_IT *a) /* v8.1M CSEL/CSINC/CSNEG/CSINV */ static bool trans_CSEL(DisasContext *s, arg_CSEL *a) { - TCGv_i32 rn, rm, zero; + TCGv_i32 rn, rm; DisasCompare c; if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) { @@ -9006,16 +8818,17 @@ static bool trans_CSEL(DisasContext *s, arg_CSEL *a) } /* In this insn input reg fields of 0b1111 mean "zero", not "PC" */ - zero = tcg_constant_i32(0); + rn = tcg_temp_new_i32(); + rm = tcg_temp_new_i32(); if (a->rn == 15) { - rn = zero; + tcg_gen_movi_i32(rn, 0); } else { - rn = load_reg(s, a->rn); + load_reg_var(s, rn, a->rn); } if (a->rm == 15) { - rm = zero; + tcg_gen_movi_i32(rm, 0); } else { - rm = load_reg(s, a->rm); + load_reg_var(s, rm, a->rm); } switch (a->op) { @@ -9035,12 +8848,9 @@ static bool trans_CSEL(DisasContext *s, arg_CSEL *a) } arm_test_cc(&c, a->fcond); - tcg_gen_movcond_i32(c.cond, rn, c.value, zero, rn, rm); - arm_free_cc(&c); + tcg_gen_movcond_i32(c.cond, rn, c.value, tcg_constant_i32(0), rn, rm); store_reg(s, a->rd, rn); - tcg_temp_free_i32(rm); - return true; } @@ -9056,8 +8866,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) * UsageFault exception. */ if (arm_dc_feature(s, ARM_FEATURE_M)) { - gen_exception_insn(s, s->pc_curr, EXCP_INVSTATE, syn_uncategorized(), - default_exception_el(s)); + gen_exception_insn(s, 0, EXCP_INVSTATE, syn_uncategorized()); return; } @@ -9066,8 +8875,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) * Illegal execution state. This has priority over BTI * exceptions, but comes after instruction abort exceptions. */ - gen_exception_insn(s, s->pc_curr, EXCP_UDEF, - syn_illegalstate(), default_exception_el(s)); + gen_exception_insn(s, 0, EXCP_UDEF, syn_illegalstate()); return; } @@ -9291,20 +9099,15 @@ static bool insn_crosses_page(CPUARMState *env, DisasContext *s) static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUARMState *env = cs->env_ptr; + CPUARMState *env = cpu_env(cs); ARMCPU *cpu = env_archcpu(env); CPUARMTBFlags tb_flags = arm_tbflags_from_tb(dc->base.tb); uint32_t condexec, core_mmu_idx; dc->isar = &cpu->isar; dc->condjmp = 0; - + dc->pc_save = dc->base.pc_first; dc->aarch64 = false; - /* If we are coming from secure EL0 in a system with a 32-bit EL3, then - * there is no secure EL1, so we route exceptions to EL3. - */ - dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) && - !arm_el_is_aa64(env, 3); dc->thumb = EX_TBFLAG_AM32(tb_flags, THUMB); dc->be_data = EX_TBFLAG_ANY(tb_flags, BE_DATA) ? MO_BE : MO_LE; condexec = EX_TBFLAG_AM32(tb_flags, CONDEXEC); @@ -9321,7 +9124,6 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) */ dc->eci = dc->condexec_mask = dc->condexec_cond = 0; dc->eci_handled = false; - dc->insn_eci_rewind = NULL; if (condexec & 0xf) { dc->condexec_mask = (condexec & 0xf) << 1; dc->condexec_cond = condexec >> 4; @@ -9340,13 +9142,14 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->fp_excp_el = EX_TBFLAG_ANY(tb_flags, FPEXC_EL); dc->align_mem = EX_TBFLAG_ANY(tb_flags, ALIGN_MEM); dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL); + dc->fgt_active = EX_TBFLAG_ANY(tb_flags, FGT_ACTIVE); + dc->fgt_svc = EX_TBFLAG_ANY(tb_flags, FGT_SVC); if (arm_feature(env, ARM_FEATURE_M)) { dc->vfp_enabled = 1; dc->be_data = MO_TE; dc->v7m_handler_mode = EX_TBFLAG_M32(tb_flags, HANDLER); - dc->v8m_secure = arm_feature(env, ARM_FEATURE_M_SECURITY) && - regime_is_secure(env, dc->mmu_idx); + dc->v8m_secure = EX_TBFLAG_M32(tb_flags, SECURE); dc->v8m_stackcheck = EX_TBFLAG_M32(tb_flags, STACKCHECK); dc->v8m_fpccr_s_wrong = EX_TBFLAG_M32(tb_flags, FPCCR_S_WRONG); dc->v7m_new_fp_ctxt_needed = @@ -9354,7 +9157,6 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->v7m_lspact = EX_TBFLAG_M32(tb_flags, LSPACT); dc->mve_no_pred = EX_TBFLAG_M32(tb_flags, MVE_NO_PRED); } else { - dc->debug_target_el = EX_TBFLAG_ANY(tb_flags, DEBUG_TARGET_EL); dc->sctlr_b = EX_TBFLAG_A32(tb_flags, SCTLR__B); dc->hstr_active = EX_TBFLAG_A32(tb_flags, HSTR_ACTIVE); dc->ns = EX_TBFLAG_A32(tb_flags, NS); @@ -9365,7 +9167,10 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->vec_len = EX_TBFLAG_A32(tb_flags, VECLEN); dc->vec_stride = EX_TBFLAG_A32(tb_flags, VECSTRIDE); } + dc->sme_trap_nonstreaming = + EX_TBFLAG_A32(tb_flags, SME_TRAP_NONSTREAMING); } + dc->lse2 = false; /* applies only to aarch64 */ dc->cp_regs = cpu->cp_regs; dc->features = env->features; @@ -9457,14 +9262,18 @@ static void arm_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) * fields here. */ uint32_t condexec_bits; + target_ulong pc_arg = dc->base.pc_next; + if (tb_cflags(dcbase->tb) & CF_PCREL) { + pc_arg &= ~TARGET_PAGE_MASK; + } if (dc->eci) { condexec_bits = dc->eci << 4; } else { condexec_bits = (dc->condexec_cond << 4) | (dc->condexec_mask >> 1); } - tcg_gen_insn_start(dc->base.pc_next, condexec_bits, 0); - dc->insn_start = tcg_last_op(); + tcg_gen_insn_start(pc_arg, condexec_bits, 0); + dc->insn_start_updated = false; } static bool arm_check_kernelpage(DisasContext *dc) @@ -9506,17 +9315,19 @@ static bool arm_check_ss_active(DisasContext *dc) static void arm_post_translate_insn(DisasContext *dc) { - if (dc->condjmp && !dc->base.is_jmp) { - gen_set_label(dc->condlabel); + if (dc->condjmp && dc->base.is_jmp == DISAS_NEXT) { + if (dc->pc_save != dc->condlabel.pc_save) { + gen_update_pc(dc, dc->condlabel.pc_save - dc->pc_save); + } + gen_set_label(dc->condlabel.label); dc->condjmp = 0; } - translator_loop_temp_check(&dc->base); } static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUARMState *env = cpu->env_ptr; + CPUARMState *env = cpu_env(cpu); uint32_t pc = dc->base.pc_next; unsigned int insn; @@ -9534,7 +9345,7 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * be possible after an indirect branch, at the start of the TB. */ assert(dc->base.num_insns == 1); - gen_helper_exception_pc_alignment(cpu_env, tcg_constant_tl(pc)); + gen_helper_exception_pc_alignment(tcg_env, tcg_constant_tl(pc)); dc->base.is_jmp = DISAS_NORETURN; dc->base.pc_next = QEMU_ALIGN_UP(pc, 4); return; @@ -9606,10 +9417,13 @@ static bool thumb_insn_is_unconditional(DisasContext *s, uint32_t insn) static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUARMState *env = cpu->env_ptr; + CPUARMState *env = cpu_env(cpu); uint32_t pc = dc->base.pc_next; uint32_t insn; bool is_16bit; + /* TCG op to rewind to if this turns out to be an invalid ECI state */ + TCGOp *insn_eci_rewind = NULL; + target_ulong insn_eci_pc_save = -1; /* Misaligned thumb PC is architecturally impossible. */ assert((dc->base.pc_next & 1) == 0); @@ -9636,8 +9450,7 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * Illegal execution state. This has priority over BTI * exceptions, but comes after instruction abort exceptions. */ - gen_exception_insn(dc, dc->pc_curr, EXCP_UDEF, - syn_illegalstate(), default_exception_el(dc)); + gen_exception_insn(dc, 0, EXCP_UDEF, syn_illegalstate()); return; } @@ -9672,7 +9485,8 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * insn" case. We will rewind to the marker (ie throwing away * all the generated code) and instead emit "take exception". */ - dc->insn_eci_rewind = tcg_last_op(); + insn_eci_rewind = tcg_last_op(); + insn_eci_pc_save = dc->pc_save; } if (dc->condexec_mask && !thumb_insn_is_unconditional(dc, insn)) { @@ -9708,10 +9522,10 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * Insn wasn't valid for ECI/ICI at all: undo what we * just generated and instead emit an exception */ - tcg_remove_ops_after(dc->insn_eci_rewind); + tcg_remove_ops_after(insn_eci_rewind); + dc->pc_save = insn_eci_pc_save; dc->condjmp = 0; - gen_exception_insn(dc, dc->pc_curr, EXCP_INVSTATE, syn_uncategorized(), - default_exception_el(dc)); + gen_exception_insn(dc, 0, EXCP_INVSTATE, syn_uncategorized()); } arm_post_translate_insn(dc); @@ -9757,22 +9571,21 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) switch (dc->base.is_jmp) { case DISAS_SWI: gen_ss_advance(dc); - gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb), - default_exception_el(dc)); + gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb)); break; case DISAS_HVC: gen_ss_advance(dc); - gen_exception(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2); + gen_exception_el(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2); break; case DISAS_SMC: gen_ss_advance(dc); - gen_exception(EXCP_SMC, syn_aa32_smc(), 3); + gen_exception_el(EXCP_SMC, syn_aa32_smc(), 3); break; case DISAS_NEXT: case DISAS_TOO_MANY: case DISAS_UPDATE_EXIT: case DISAS_UPDATE_NOCHAIN: - gen_set_pc_im(dc, dc->base.pc_next); + gen_update_pc(dc, curr_insn_len(dc)); /* fall through */ default: /* FIXME: Single stepping a WFI insn will not halt the CPU. */ @@ -9793,16 +9606,16 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) switch (dc->base.is_jmp) { case DISAS_NEXT: case DISAS_TOO_MANY: - gen_goto_tb(dc, 1, dc->base.pc_next); + gen_goto_tb(dc, 1, curr_insn_len(dc)); break; case DISAS_UPDATE_NOCHAIN: - gen_set_pc_im(dc, dc->base.pc_next); + gen_update_pc(dc, curr_insn_len(dc)); /* fall through */ case DISAS_JUMP: gen_goto_ptr(); break; case DISAS_UPDATE_EXIT: - gen_set_pc_im(dc, dc->base.pc_next); + gen_update_pc(dc, curr_insn_len(dc)); /* fall through */ default: /* indicate that the hash table must be used to find the next TB */ @@ -9812,8 +9625,7 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) /* nothing more to generate */ break; case DISAS_WFI: - gen_helper_wfi(cpu_env, - tcg_constant_i32(dc->base.pc_next - dc->pc_curr)); + gen_helper_wfi(tcg_env, tcg_constant_i32(curr_insn_len(dc))); /* * The helper doesn't necessarily throw an exception, but we * must go back to the main loop to check for interrupts anyway. @@ -9821,33 +9633,32 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) tcg_gen_exit_tb(NULL, 0); break; case DISAS_WFE: - gen_helper_wfe(cpu_env); + gen_helper_wfe(tcg_env); break; case DISAS_YIELD: - gen_helper_yield(cpu_env); + gen_helper_yield(tcg_env); break; case DISAS_SWI: - gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb), - default_exception_el(dc)); + gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb)); break; case DISAS_HVC: - gen_exception(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2); + gen_exception_el(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2); break; case DISAS_SMC: - gen_exception(EXCP_SMC, syn_aa32_smc(), 3); + gen_exception_el(EXCP_SMC, syn_aa32_smc(), 3); break; } } if (dc->condjmp) { /* "Condition failed" instruction codepath for the branch/trap insn */ - gen_set_label(dc->condlabel); + set_disas_label(dc, dc->condlabel); gen_set_condexec(dc); if (unlikely(dc->ss_active)) { - gen_set_pc_im(dc, dc->base.pc_next); + gen_update_pc(dc, curr_insn_len(dc)); gen_singlestep_exception(dc); } else { - gen_goto_tb(dc, 1, dc->base.pc_next); + gen_goto_tb(dc, 1, curr_insn_len(dc)); } } } @@ -9880,7 +9691,8 @@ static const TranslatorOps thumb_translator_ops = { }; /* generate intermediate code for basic block 'tb'. */ -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc = { }; const TranslatorOps *ops = &arm_translator_ops; @@ -9895,19 +9707,5 @@ void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns) } #endif - translator_loop(ops, &dc.base, cpu, tb, max_insns); -} - -void restore_state_to_opc(CPUARMState *env, TranslationBlock *tb, - target_ulong *data) -{ - if (is_a64(env)) { - env->pc = data[0]; - env->condexec_bits = 0; - env->exception.syndrome = data[2] << ARM_INSN_START_WORD2_SHIFT; - } else { - env->regs[15] = data[0]; - env->condexec_bits = data[1]; - env->exception.syndrome = data[2] << ARM_INSN_START_WORD2_SHIFT; - } + translator_loop(cpu, tb, max_insns, pc, host_pc, ops, &dc.base); } diff --git a/target/arm/translate.h b/target/arm/tcg/translate.h similarity index 72% rename from target/arm/translate.h rename to target/arm/tcg/translate.h index f473a21ed4..dc66ff2190 100644 --- a/target/arm/translate.h +++ b/target/arm/tcg/translate.h @@ -1,23 +1,52 @@ #ifndef TARGET_ARM_TRANSLATE_H #define TARGET_ARM_TRANSLATE_H +#include "cpu.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" +#include "exec/exec-all.h" #include "exec/translator.h" +#include "exec/helper-gen.h" #include "internals.h" - +#include "cpu-features.h" /* internal defines */ + +/* + * Save pc_save across a branch, so that we may restore the value from + * before the branch at the point the label is emitted. + */ +typedef struct DisasLabel { + TCGLabel *label; + target_ulong pc_save; +} DisasLabel; + typedef struct DisasContext { DisasContextBase base; const ARMISARegisters *isar; /* The address of the current instruction being translated. */ target_ulong pc_curr; + /* + * For CF_PCREL, the full value of cpu_pc is not known + * (although the page offset is known). For convenience, the + * translation loop uses the full virtual address that triggered + * the translation, from base.pc_start through pc_curr. + * For efficiency, we do not update cpu_pc for every instruction. + * Instead, pc_save has the value of pc_curr at the time of the + * last update to cpu_pc, which allows us to compute the addend + * needed to bring cpu_pc current: pc_curr - pc_save. + * If cpu_pc now contains the destination of an indirect branch, + * pc_save contains -1 to indicate that relative updates are no + * longer possible. + */ + target_ulong pc_save; target_ulong page_start; uint32_t insn; /* Nonzero if this instruction has been conditionally skipped. */ int condjmp; /* The label that will be jumped to when the instruction is skipped. */ - TCGLabel *condlabel; + DisasLabel condlabel; /* Thumb-2 conditional execution bits. */ int condexec_mask; int condexec_cond; @@ -28,8 +57,6 @@ typedef struct DisasContext { * after decode (ie after any UNDEF checks) */ bool eci_handled; - /* TCG op to rewind to if this turns out to be an invalid ECI state */ - TCGOp *insn_eci_rewind; int sctlr_b; MemOp be_data; #if !defined(CONFIG_USER_ONLY) @@ -42,9 +69,9 @@ typedef struct DisasContext { bool ns; /* Use non-secure CPREG bank on access */ int fp_excp_el; /* FP exception EL or 0 if enabled */ int sve_excp_el; /* SVE exception EL or 0 if enabled */ + int sme_excp_el; /* SME exception EL or 0 if enabled */ int vl; /* current vector length in bytes */ - /* Flag indicating that exceptions from secure mode are routed to EL3. */ - bool secure_routed_to_el3; + int svl; /* current streaming vector length in bytes */ bool vfp_enabled; /* FP enabled via FPSCR.EN */ int vec_len; int vec_stride; @@ -59,12 +86,11 @@ typedef struct DisasContext { */ uint32_t svc_imm; int current_el; - /* Debug target exception level for single-step exceptions */ - int debug_target_el; GHashTable *cp_regs; uint64_t features; /* CPU features bits */ bool aarch64; bool thumb; + bool lse2; /* Because unallocated encodings generate different exception syndrome * information from traps due to FP being disabled, we can't do a single * "is fp access disabled" check at a high level in the decode tree. @@ -88,8 +114,8 @@ typedef struct DisasContext { bool unpriv; /* True if v8.3-PAuth is active. */ bool pauth_active; - /* True if v8.5-MTE access to tags is enabled. */ - bool ata; + /* True if v8.5-MTE access to tags is enabled; index with is_unpriv. */ + bool ata[2]; /* True if v8.5-MTE tag checks affect the PE; index with is_unpriv. */ bool mte_active[2]; /* True with v8.5-BTI and SCTLR_ELx.BT* set. */ @@ -100,8 +126,34 @@ typedef struct DisasContext { bool align_mem; /* True if PSTATE.IL is set */ bool pstate_il; + /* True if PSTATE.SM is set. */ + bool pstate_sm; + /* True if PSTATE.ZA is set. */ + bool pstate_za; + /* True if non-streaming insns should raise an SME Streaming exception. */ + bool sme_trap_nonstreaming; + /* True if the current instruction is non-streaming. */ + bool is_nonstreaming; /* True if MVE insns are definitely not predicated by VPR or LTPSIZE */ bool mve_no_pred; + /* True if fine-grained traps are active */ + bool fgt_active; + /* True if fine-grained trap on SVC is enabled */ + bool fgt_svc; + /* True if a trap on ERET is enabled (FGT or NV) */ + bool trap_eret; + /* True if FEAT_LSE2 SCTLR_ELx.nAA is set */ + bool naa; + /* True if FEAT_NV HCR_EL2.NV is enabled */ + bool nv; + /* True if NV enabled and HCR_EL2.NV1 is set */ + bool nv1; + /* True if NV enabled and HCR_EL2.NV2 is set */ + bool nv2; + /* True if NV2 enabled and NV2 RAM accesses use EL2&0 translation regime */ + bool nv2_mem_e20; + /* True if NV2 enabled and NV2 RAM accesses are big-endian */ + bool nv2_mem_be; /* * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. * < 0, set by the current instruction. @@ -109,21 +161,21 @@ typedef struct DisasContext { int8_t btype; /* A copy of cpu->dcz_blocksize. */ uint8_t dcz_blocksize; + /* A copy of cpu->gm_blocksize. */ + uint8_t gm_blocksize; /* True if this page is guarded. */ bool guarded_page; + /* True if the current insn_start has been updated. */ + bool insn_start_updated; /* Bottom two bits of XScale c15_cpar coprocessor access control reg */ int c15_cpar; - /* TCG op of the current insn_start. */ - TCGOp *insn_start; -#define TMP_A64_MAX 16 - int tmp_a64_count; - TCGv_i64 tmp_a64[TMP_A64_MAX]; + /* Offset from VNCR_EL2 when FEAT_NV2 redirects this reg to memory */ + uint32_t nv2_redirect_offset; } DisasContext; typedef struct DisasCompare { TCGCond cond; TCGv_i32 value; - bool value_global; } DisasCompare; /* Share the TCG temporaries common between 32 and 64 bit modes. */ @@ -150,6 +202,11 @@ static inline int plus_2(DisasContext *s, int x) return x + 2; } +static inline int plus_12(DisasContext *s, int x) +{ + return x + 12; +} + static inline int times_2(DisasContext *s, int x) { return x * 2; @@ -160,6 +217,11 @@ static inline int times_4(DisasContext *s, int x) return x * 4; } +static inline int times_8(DisasContext *s, int x) +{ + return x * 8; +} + static inline int times_2_plus_1(DisasContext *s, int x) { return x * 2 + 1; @@ -185,6 +247,11 @@ static inline int rsub_8(DisasContext *s, int x) return 8 - x; } +static inline int shl_12(DisasContext *s, int x) +{ + return x << 12; +} + static inline int neon_3same_fp_size(DisasContext *s, int x) { /* Convert 0==fp32, 1==fp16 into a MO_* value */ @@ -201,20 +268,6 @@ static inline int get_mem_index(DisasContext *s) return arm_to_core_mmu_idx(s->mmu_idx); } -/* Function used to determine the target exception EL when otherwise not known - * or default. - */ -static inline int default_exception_el(DisasContext *s) -{ - /* If we are coming from secure EL0 in a system with a 32-bit EL3, then - * there is no secure EL1, so we route exceptions to EL3. Otherwise, - * 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_SE10_0 && s->secure_routed_to_el3) - ? 3 : MAX(1, s->current_el); -} - static inline void disas_set_insn_syndrome(DisasContext *s, uint32_t syn) { /* We don't need to save all of the syndrome so we mask and shift @@ -223,10 +276,15 @@ static inline void disas_set_insn_syndrome(DisasContext *s, uint32_t syn) syn &= ARM_INSN_START_WORD2_MASK; syn >>= ARM_INSN_START_WORD2_SHIFT; - /* We check and clear insn_start_idx to catch multiple updates. */ - assert(s->insn_start != NULL); - tcg_set_insn_start_param(s->insn_start, 2, syn); - s->insn_start = NULL; + /* Check for multiple updates. */ + assert(!s->insn_start_updated); + s->insn_start_updated = true; + tcg_set_insn_start_param(s->base.insn_start, 2, syn); +} + +static inline int curr_insn_len(DisasContext *s) +{ + return s->base.pc_next - s->pc_curr; } /* is_jmp field values */ @@ -252,7 +310,7 @@ static inline void disas_set_insn_syndrome(DisasContext *s, uint32_t syn) * For instructions which want an immediate exit to the main loop, as opposed * to attempting to use lookup_and_goto_ptr. Unlike DISAS_UPDATE_EXIT, this * doesn't write the PC on exiting the translation loop so you need to ensure - * something (gen_a64_set_pc_im or runtime helper) has done so before we reach + * something (gen_a64_update_pc or runtime helper) has done so before we reach * return from cpu_tb_exec. */ #define DISAS_EXIT DISAS_TARGET_9 @@ -261,33 +319,34 @@ static inline void disas_set_insn_syndrome(DisasContext *s, uint32_t syn) #ifdef TARGET_AARCH64 void a64_translate_init(void); -void gen_a64_set_pc_im(uint64_t val); +void gen_a64_update_pc(DisasContext *s, target_long diff); extern const TranslatorOps aarch64_translator_ops; #else static inline void a64_translate_init(void) { } -static inline void gen_a64_set_pc_im(uint64_t val) +static inline void gen_a64_update_pc(DisasContext *s, target_long diff) { } #endif void arm_test_cc(DisasCompare *cmp, int cc); -void arm_free_cc(DisasCompare *cmp); void arm_jump_cc(DisasCompare *cmp, TCGLabel *label); void arm_gen_test_cc(int cc, TCGLabel *label); MemOp pow2_align(unsigned i); void unallocated_encoding(DisasContext *s); -void gen_exception_insn(DisasContext *s, uint64_t pc, int excp, - uint32_t syn, uint32_t target_el); +void gen_exception_insn_el(DisasContext *s, target_long pc_diff, int excp, + uint32_t syn, uint32_t target_el); +void gen_exception_insn(DisasContext *s, target_long pc_diff, + int excp, uint32_t syn); /* Return state of Alternate Half-precision flag, caller frees result */ static inline TCGv_i32 get_ahp_flag(void) { TCGv_i32 ret = tcg_temp_new_i32(); - tcg_gen_ld_i32(ret, cpu_env, + tcg_gen_ld_i32(ret, tcg_env, offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPSCR])); tcg_gen_extract_i32(ret, ret, 26, 1); @@ -301,10 +360,9 @@ static inline void set_pstate_bits(uint32_t bits) tcg_debug_assert(!(bits & CACHED_PSTATE_BITS)); - tcg_gen_ld_i32(p, cpu_env, offsetof(CPUARMState, pstate)); + tcg_gen_ld_i32(p, tcg_env, offsetof(CPUARMState, pstate)); tcg_gen_ori_i32(p, p, bits); - tcg_gen_st_i32(p, cpu_env, offsetof(CPUARMState, pstate)); - tcg_temp_free_i32(p); + tcg_gen_st_i32(p, tcg_env, offsetof(CPUARMState, pstate)); } /* Clear bits within PSTATE. */ @@ -314,10 +372,9 @@ static inline void clear_pstate_bits(uint32_t bits) tcg_debug_assert(!(bits & CACHED_PSTATE_BITS)); - tcg_gen_ld_i32(p, cpu_env, offsetof(CPUARMState, pstate)); + tcg_gen_ld_i32(p, tcg_env, offsetof(CPUARMState, pstate)); tcg_gen_andi_i32(p, p, ~bits); - tcg_gen_st_i32(p, cpu_env, offsetof(CPUARMState, pstate)); - tcg_temp_free_i32(p); + tcg_gen_st_i32(p, tcg_env, offsetof(CPUARMState, pstate)); } /* If the singlestep state is Active-not-pending, advance to Active-pending. */ @@ -329,26 +386,12 @@ static inline void gen_ss_advance(DisasContext *s) } } -static inline void gen_exception(int excp, uint32_t syndrome, - uint32_t target_el) -{ - gen_helper_exception_with_syndrome(cpu_env, tcg_constant_i32(excp), - tcg_constant_i32(syndrome), - tcg_constant_i32(target_el)); -} - /* Generate an architectural singlestep exception */ static inline void gen_swstep_exception(DisasContext *s, int isv, int ex) { - bool same_el = (s->debug_target_el == s->current_el); - - /* - * If singlestep is targeting a lower EL than the current one, - * then s->ss_active must be false and we can never get here. - */ - assert(s->debug_target_el >= s->current_el); - - gen_exception(EXCP_UDEF, syn_swstep(same_el, isv, ex), s->debug_target_el); + /* Fill in the same_el field of the syndrome in the helper. */ + uint32_t syn = syn_swstep(false, isv, ex); + gen_helper_exception_swstep(tcg_env, tcg_constant_i32(syn)); } /* @@ -531,17 +574,18 @@ static inline TCGv_ptr fpstatus_ptr(ARMFPStatusFlavour flavour) default: g_assert_not_reached(); } - tcg_gen_addi_ptr(statusptr, cpu_env, offset); + tcg_gen_addi_ptr(statusptr, tcg_env, offset); return statusptr; } /** - * finalize_memop: + * finalize_memop_atom: * @s: DisasContext * @opc: size+sign+align of the memory operation + * @atom: atomicity of the memory operation * - * Build the complete MemOp for a memory operation, including alignment - * and endianness. + * Build the complete MemOp for a memory operation, including alignment, + * endianness, and atomicity. * * If (op & MO_AMASK) then the operation already contains the required * alignment, e.g. for AccType_ATOMIC. Otherwise, this an optionally @@ -551,12 +595,63 @@ static inline TCGv_ptr fpstatus_ptr(ARMFPStatusFlavour flavour) * and this is applied here. Note that there is no way to indicate that * no alignment should ever be enforced; this must be handled manually. */ -static inline MemOp finalize_memop(DisasContext *s, MemOp opc) +static inline MemOp finalize_memop_atom(DisasContext *s, MemOp opc, MemOp atom) { if (s->align_mem && !(opc & MO_AMASK)) { opc |= MO_ALIGN; } - return opc | s->be_data; + return opc | atom | s->be_data; +} + +/** + * finalize_memop: + * @s: DisasContext + * @opc: size+sign+align of the memory operation + * + * Like finalize_memop_atom, but with default atomicity. + */ +static inline MemOp finalize_memop(DisasContext *s, MemOp opc) +{ + MemOp atom = s->lse2 ? MO_ATOM_WITHIN16 : MO_ATOM_IFALIGN; + return finalize_memop_atom(s, opc, atom); +} + +/** + * finalize_memop_pair: + * @s: DisasContext + * @opc: size+sign+align of the memory operation + * + * Like finalize_memop_atom, but with atomicity for a pair. + * C.f. Pseudocode for Mem[], operand ispair. + */ +static inline MemOp finalize_memop_pair(DisasContext *s, MemOp opc) +{ + MemOp atom = s->lse2 ? MO_ATOM_WITHIN16_PAIR : MO_ATOM_IFALIGN_PAIR; + return finalize_memop_atom(s, opc, atom); +} + +/** + * finalize_memop_asimd: + * @s: DisasContext + * @opc: size+sign+align of the memory operation + * + * Like finalize_memop_atom, but with atomicity of AccessType_ASIMD. + */ +static inline MemOp finalize_memop_asimd(DisasContext *s, MemOp opc) +{ + /* + * In the pseudocode for Mem[], with AccessType_ASIMD, size == 16, + * if IsAligned(8), the first case provides separate atomicity for + * the pair of 64-bit accesses. If !IsAligned(8), the middle cases + * do not apply, and we're left with the final case of no atomicity. + * Thus MO_ATOM_IFALIGN_PAIR. + * + * For other sizes, normal LSE2 rules apply. + */ + if ((opc & MO_SIZE) == MO_128) { + return finalize_memop_atom(s, opc, MO_ATOM_IFALIGN_PAIR); + } + return finalize_memop(s, opc); } /** @@ -576,6 +671,52 @@ static inline MemOp finalize_memop(DisasContext *s, MemOp opc) */ uint64_t asimd_imm_const(uint32_t imm, int cmode, int op); +/* + * gen_disas_label: + * Create a label and cache a copy of pc_save. + */ +static inline DisasLabel gen_disas_label(DisasContext *s) +{ + return (DisasLabel){ + .label = gen_new_label(), + .pc_save = s->pc_save, + }; +} + +/* + * set_disas_label: + * Emit a label and restore the cached copy of pc_save. + */ +static inline void set_disas_label(DisasContext *s, DisasLabel l) +{ + gen_set_label(l.label); + s->pc_save = l.pc_save; +} + +static inline TCGv_ptr gen_lookup_cp_reg(uint32_t key) +{ + TCGv_ptr ret = tcg_temp_new_ptr(); + gen_helper_lookup_cp_reg(ret, tcg_env, tcg_constant_i32(key)); + return ret; +} + +/* + * Set and reset rounding mode around another operation. + */ +static inline TCGv_i32 gen_set_rmode(ARMFPRounding rmode, TCGv_ptr fpst) +{ + TCGv_i32 new = tcg_constant_i32(arm_rmode_to_sf(rmode)); + TCGv_i32 old = tcg_temp_new_i32(); + + gen_helper_set_rmode(old, new, fpst); + return old; +} + +static inline void gen_restore_rmode(TCGv_i32 old, TCGv_ptr fpst) +{ + gen_helper_set_rmode(old, old, fpst); +} + /* * Helpers for implementing sets of trans_* functions. * Defer the implementation of NAME to FUNC, with optional extra arguments. @@ -587,4 +728,11 @@ uint64_t asimd_imm_const(uint32_t imm, int cmode, int op); static bool trans_##NAME(DisasContext *s, arg_##NAME *a) \ { return dc_isar_feature(FEAT, s) && FUNC(s, __VA_ARGS__); } +#define TRANS_FEAT_NONSTREAMING(NAME, FEAT, FUNC, ...) \ + static bool trans_##NAME(DisasContext *s, arg_##NAME *a) \ + { \ + s->is_nonstreaming = true; \ + return dc_isar_feature(FEAT, s) && FUNC(s, __VA_ARGS__); \ + } + #endif /* TARGET_ARM_TRANSLATE_H */ diff --git a/target/arm/vec_helper.c b/target/arm/tcg/vec_helper.c similarity index 97% rename from target/arm/vec_helper.c rename to target/arm/tcg/vec_helper.c index 9a9c034e36..1f93510b85 100644 --- a/target/arm/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -23,6 +23,7 @@ #include "tcg/tcg-gvec-desc.h" #include "fpu/softfloat.h" #include "qemu/int128.h" +#include "crypto/clmul.h" #include "vec_internal.h" /* @@ -1986,21 +1987,11 @@ void HELPER(gvec_ushl_h)(void *vd, void *vn, void *vm, uint32_t desc) */ void HELPER(gvec_pmul_b)(void *vd, void *vn, void *vm, uint32_t desc) { - intptr_t i, j, opr_sz = simd_oprsz(desc); + 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]; - 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; + d[i] = clmul_8x8_low(n[i], m[i]); } clear_tail(d, opr_sz, simd_maxsz(desc)); } @@ -2012,84 +2003,28 @@ void HELPER(gvec_pmul_b)(void *vd, void *vn, void *vm, uint32_t desc) */ 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 i, 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; + Int128 r = clmul_64(n[i + hi], m[i + hi]); + d[i] = int128_getlo(r); + d[i + 1] = int128_gethi(r); } 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); -} - -uint64_t pmull_w(uint64_t op1, uint64_t op2) -{ - uint64_t result = 0; - int i; - for (i = 0; i < 16; ++i) { - uint64_t mask = (op1 & 0x0000000100000001ull) * 0xffffffff; - result ^= op2 & mask; - op1 >>= 1; - op2 <<= 1; - } - return result; -} - -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)); + d[0] = clmul_8x4_packed(nn, mm); nn >>= 32; mm >>= 32; - d[1] = pmull_h(expand_byte_to_half(nn), expand_byte_to_half(mm)); + d[1] = clmul_8x4_packed(nn, mm); clear_tail(d, 16, simd_maxsz(desc)); } @@ -2102,25 +2037,10 @@ void HELPER(sve2_pmull_h)(void *vd, void *vn, void *vm, uint32_t 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); + d[i] = clmul_8x4_even(n[i] >> shift, m[i] >> shift); } } -static uint64_t pmull_d(uint64_t op1, uint64_t op2) -{ - uint64_t result = 0; - int i; - - for (i = 0; i < 32; ++i) { - uint64_t mask = -((op1 >> i) & 1); - result ^= (op2 << i) & mask; - } - return result; -} - void HELPER(sve2_pmull_d)(void *vd, void *vn, void *vm, uint32_t desc) { intptr_t sel = H4(simd_data(desc)); @@ -2129,7 +2049,7 @@ void HELPER(sve2_pmull_d)(void *vd, void *vn, void *vm, uint32_t desc) uint64_t *d = vd; for (i = 0; i < opr_sz / 8; ++i) { - d[i] = pmull_d(n[2 * i + sel], m[2 * i + sel]); + d[i] = clmul_32(n[2 * i + sel], m[2 * i + sel]); } } #endif @@ -2626,7 +2546,7 @@ void HELPER(gvec_bfmmla)(void *vd, void *vn, void *vm, void *va, uint32_t desc) * Process the entire segment at once, writing back the * results only after we've consumed all of the inputs. * - * Key to indicies by column: + * Key to indices by column: * i j i k j k */ sum00 = a[s + H4(0 + 0)]; @@ -2690,3 +2610,27 @@ void HELPER(gvec_bfmlal_idx)(void *vd, void *vn, void *vm, } clear_tail(d, opr_sz, simd_maxsz(desc)); } + +#define DO_CLAMP(NAME, TYPE) \ +void HELPER(NAME)(void *d, void *n, void *m, void *a, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + for (i = 0; i < opr_sz; i += sizeof(TYPE)) { \ + TYPE aa = *(TYPE *)(a + i); \ + TYPE nn = *(TYPE *)(n + i); \ + TYPE mm = *(TYPE *)(m + i); \ + TYPE dd = MIN(MAX(aa, nn), mm); \ + *(TYPE *)(d + i) = dd; \ + } \ + clear_tail(d, opr_sz, simd_maxsz(desc)); \ +} + +DO_CLAMP(gvec_sclamp_b, int8_t) +DO_CLAMP(gvec_sclamp_h, int16_t) +DO_CLAMP(gvec_sclamp_s, int32_t) +DO_CLAMP(gvec_sclamp_d, int64_t) + +DO_CLAMP(gvec_uclamp_b, uint8_t) +DO_CLAMP(gvec_uclamp_h, uint16_t) +DO_CLAMP(gvec_uclamp_s, uint32_t) +DO_CLAMP(gvec_uclamp_d, uint64_t) diff --git a/target/arm/vec_internal.h b/target/arm/tcg/vec_internal.h similarity index 95% rename from target/arm/vec_internal.h rename to target/arm/tcg/vec_internal.h index 1f4ed80ff7..3ca1b94ccf 100644 --- a/target/arm/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -219,17 +219,6 @@ int16_t do_sqrdmlah_h(int16_t, int16_t, int16_t, bool, bool, uint32_t *); int32_t do_sqrdmlah_s(int32_t, int32_t, int32_t, bool, bool, uint32_t *); int64_t do_sqrdmlah_d(int64_t, int64_t, int64_t, bool, bool); -/* - * 8 x 8 -> 16 vector polynomial multiply where the inputs are - * in the low 8 bits of each 16-bit element -*/ -uint64_t pmull_h(uint64_t op1, uint64_t op2); -/* - * 16 x 16 -> 32 vector polynomial multiply where the inputs are - * in the low 16 bits of each 32-bit element - */ -uint64_t pmull_w(uint64_t op1, uint64_t op2); - /** * bfdotadd: * @sum: addend diff --git a/target/arm/vfp-uncond.decode b/target/arm/tcg/vfp-uncond.decode similarity index 100% rename from target/arm/vfp-uncond.decode rename to target/arm/tcg/vfp-uncond.decode diff --git a/target/arm/vfp.decode b/target/arm/tcg/vfp.decode similarity index 100% rename from target/arm/vfp.decode rename to target/arm/tcg/vfp.decode diff --git a/target/arm/trace-events b/target/arm/trace-events index 2a0ba7bffc..4438dce7be 100644 --- a/target/arm/trace-events +++ b/target/arm/trace-events @@ -1,13 +1,15 @@ # See docs/devel/tracing.rst for syntax documentation. # helper.c -arm_gt_recalc(int timer, int irqstate, uint64_t nexttick) "gt recalc: timer %d irqstate %d next tick 0x%" PRIx64 -arm_gt_recalc_disabled(int timer) "gt recalc: timer %d irqstate 0 timer disabled" +arm_gt_recalc(int timer, uint64_t nexttick) "gt recalc: timer %d next tick 0x%" PRIx64 +arm_gt_recalc_disabled(int timer) "gt recalc: timer %d timer disabled" arm_gt_cval_write(int timer, uint64_t value) "gt_cval_write: timer %d value 0x%" PRIx64 arm_gt_tval_write(int timer, uint64_t value) "gt_tval_write: timer %d value 0x%" PRIx64 arm_gt_ctl_write(int timer, uint64_t value) "gt_ctl_write: timer %d value 0x%" PRIx64 -arm_gt_imask_toggle(int timer, int irqstate) "gt_ctl_write: timer %d IMASK toggle, new irqstate %d" +arm_gt_imask_toggle(int timer) "gt_ctl_write: timer %d IMASK toggle" arm_gt_cntvoff_write(uint64_t value) "gt_cntvoff_write: value 0x%" PRIx64 +arm_gt_cntpoff_write(uint64_t value) "gt_cntpoff_write: value 0x%" PRIx64 +arm_gt_update_irq(int timer, int irqstate) "gt_update_irq: timer %d irqstate %d" # kvm.c kvm_arm_fixup_msi_route(uint64_t iova, uint64_t gpa) "MSI iova = 0x%"PRIx64" is translated into 0x%"PRIx64 diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 24e3d820a5..3e5e37abbe 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "internals.h" +#include "cpu-features.h" #ifdef CONFIG_TCG #include "qemu/log.h" #include "fpu/softfloat.h" @@ -1104,33 +1105,14 @@ float64 HELPER(rintd)(float64 x, void *fp_status) } /* Convert ARM rounding mode to softfloat */ -int arm_rmode_to_sf(int rmode) -{ - switch (rmode) { - case FPROUNDING_TIEAWAY: - rmode = float_round_ties_away; - break; - case FPROUNDING_ODD: - /* FIXME: add support for TIEAWAY and ODD */ - qemu_log_mask(LOG_UNIMP, "arm: unimplemented rounding mode: %d\n", - rmode); - /* fall through for now */ - case FPROUNDING_TIEEVEN: - default: - rmode = float_round_nearest_even; - break; - case FPROUNDING_POSINF: - rmode = float_round_up; - break; - case FPROUNDING_NEGINF: - rmode = float_round_down; - break; - case FPROUNDING_ZERO: - rmode = float_round_to_zero; - break; - } - return rmode; -} +const FloatRoundMode arm_rmode_to_sf_map[] = { + [FPROUNDING_TIEEVEN] = float_round_nearest_even, + [FPROUNDING_POSINF] = float_round_up, + [FPROUNDING_NEGINF] = float_round_down, + [FPROUNDING_ZERO] = float_round_to_zero, + [FPROUNDING_TIEAWAY] = float_round_ties_away, + [FPROUNDING_ODD] = float_round_to_odd, +}; /* * Implement float64 to int32_t conversion without saturation; @@ -1139,68 +1121,21 @@ int arm_rmode_to_sf(int rmode) uint64_t HELPER(fjcvtzs)(float64 value, void *vstatus) { float_status *status = vstatus; - uint32_t exp, sign; - uint64_t frac; - uint32_t inexact = 1; /* !Z */ + uint32_t inexact, frac; + uint32_t e_old, e_new; - sign = extract64(value, 63, 1); - exp = extract64(value, 52, 11); - frac = extract64(value, 0, 52); + e_old = get_float_exception_flags(status); + set_float_exception_flags(0, status); + frac = float64_to_int32_modulo(value, float_round_to_zero, status); + e_new = get_float_exception_flags(status); + set_float_exception_flags(e_old | e_new, status); - if (exp == 0) { - /* While not inexact for IEEE FP, -0.0 is inexact for JavaScript. */ - inexact = sign; - if (frac != 0) { - if (status->flush_inputs_to_zero) { - float_raise(float_flag_input_denormal, status); - } else { - float_raise(float_flag_inexact, status); - inexact = 1; - } - } - frac = 0; - } else if (exp == 0x7ff) { - /* This operation raises Invalid for both NaN and overflow (Inf). */ - float_raise(float_flag_invalid, status); - frac = 0; + if (value == float64_chs(float64_zero)) { + /* While not inexact for IEEE FP, -0.0 is inexact for JavaScript. */ + inexact = 1; } else { - int true_exp = exp - 1023; - int shift = true_exp - 52; - - /* Restore implicit bit. */ - frac |= 1ull << 52; - - /* Shift the fraction into place. */ - if (shift >= 0) { - /* The number is so large we must shift the fraction left. */ - if (shift >= 64) { - /* The fraction is shifted out entirely. */ - frac = 0; - } else { - frac <<= shift; - } - } else if (shift > -64) { - /* Normal case -- shift right and notice if bits shift out. */ - inexact = (frac << (64 + shift)) != 0; - frac >>= -shift; - } else { - /* The fraction is shifted out entirely. */ - frac = 0; - } - - /* Notice overflow or inexact exceptions. */ - if (true_exp > 31 || frac > (sign ? 0x80000000ull : 0x7fffffff)) { - /* Overflow, for which this operation raises invalid. */ - float_raise(float_flag_invalid, status); - inexact = 1; - } else if (inexact) { - float_raise(float_flag_inexact, status); - } - - /* Honor the sign. */ - if (sign) { - frac = -frac; - } + /* Normal inexact or overflow or NaN */ + inexact = e_new & (float_flag_inexact | float_flag_invalid); } /* Pack the result and the env->ZF representation of Z together. */ diff --git a/target/avr/cpu-param.h b/target/avr/cpu-param.h index 7ef4e7c679..9a92bc74fc 100644 --- a/target/avr/cpu-param.h +++ b/target/avr/cpu-param.h @@ -31,6 +31,5 @@ #define TARGET_PAGE_BITS 8 #define TARGET_PHYS_ADDR_SPACE_BITS 24 #define TARGET_VIRT_ADDR_SPACE_BITS 24 -#define NB_MMU_MODES 2 #endif diff --git a/target/avr/cpu-qom.h b/target/avr/cpu-qom.h index b5c3507d6d..38dbcc0535 100644 --- a/target/avr/cpu-qom.h +++ b/target/avr/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU AVR CPU + * QEMU AVR CPU QOM header (target agnostic) * * Copyright (c) 2016-2020 Michael Rolnik * @@ -22,26 +22,12 @@ #define TARGET_AVR_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_AVR_CPU "avr-cpu" OBJECT_DECLARE_CPU_TYPE(AVRCPU, AVRCPUClass, AVR_CPU) -/** - * AVRCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * - * A AVR CPU model. - */ -struct AVRCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - DeviceRealize parent_realize; - DeviceReset parent_reset; -}; - +#define AVR_CPU_TYPE_SUFFIX "-" TYPE_AVR_CPU +#define AVR_CPU_TYPE_NAME(name) (name AVR_CPU_TYPE_SUFFIX) #endif /* TARGET_AVR_CPU_QOM_H */ diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 5d70e34dd5..45ee1b5f89 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -24,6 +24,8 @@ #include "exec/exec-all.h" #include "cpu.h" #include "disas/dis-asm.h" +#include "tcg/debug-assert.h" +#include "hw/qdev-properties.h" static void avr_cpu_set_pc(CPUState *cs, vaddr value) { @@ -32,32 +34,48 @@ static void avr_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.pc_w = value / 2; /* internally PC points to words */ } -static bool avr_cpu_has_work(CPUState *cs) +static vaddr avr_cpu_get_pc(CPUState *cs) { AVRCPU *cpu = AVR_CPU(cs); - CPUAVRState *env = &cpu->env; + return cpu->env.pc_w * 2; +} + +static bool avr_cpu_has_work(CPUState *cs) +{ return (cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RESET)) - && cpu_interrupts_enabled(env); + && cpu_interrupts_enabled(cpu_env(cs)); +} + +static int avr_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return ifetch ? MMU_CODE_IDX : MMU_DATA_IDX; } static void avr_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - AVRCPU *cpu = AVR_CPU(cs); - CPUAVRState *env = &cpu->env; - - env->pc_w = tb->pc / 2; /* internally PC points to words */ + tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + cpu_env(cs)->pc_w = tb->pc / 2; /* internally PC points to words */ } -static void avr_cpu_reset(DeviceState *ds) +static void avr_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) { - CPUState *cs = CPU(ds); + cpu_env(cs)->pc_w = data[0]; +} + +static void avr_cpu_reset_hold(Object *obj) +{ + CPUState *cs = CPU(obj); AVRCPU *cpu = AVR_CPU(cs); - AVRCPUClass *mcc = AVR_CPU_GET_CLASS(cpu); + AVRCPUClass *mcc = AVR_CPU_GET_CLASS(obj); CPUAVRState *env = &cpu->env; - mcc->parent_reset(ds); + if (mcc->parent_phases.hold) { + mcc->parent_phases.hold(obj); + } env->pc_w = 0; env->sregI = 1; @@ -74,7 +92,7 @@ static void avr_cpu_reset(DeviceState *ds) env->rampY = 0; env->rampZ = 0; env->eind = 0; - env->sp = 0; + env->sp = cpu->init_sp; env->skip = 0; @@ -126,29 +144,24 @@ static void avr_cpu_initfn(Object *obj) { AVRCPU *cpu = AVR_CPU(obj); - cpu_set_cpustate_pointers(cpu); - /* Set the number of interrupts supported by the CPU. */ qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int, sizeof(cpu->env.intsrc) * 8); } +static Property avr_cpu_properties[] = { + DEFINE_PROP_UINT32("init-sp", AVRCPU, init_sp, 0), + DEFINE_PROP_END_OF_LIST() +}; + static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) { - ObjectClass *oc; - - oc = object_class_by_name(cpu_model); - if (object_class_dynamic_cast(oc, TYPE_AVR_CPU) == NULL || - object_class_is_abstract(oc)) { - oc = NULL; - } - return oc; + return object_class_by_name(cpu_model); } static void avr_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - AVRCPU *cpu = AVR_CPU(cs); - CPUAVRState *env = &cpu->env; + CPUAVRState *env = cpu_env(cs); int i; qemu_fprintf(f, "\n"); @@ -192,9 +205,10 @@ static const struct SysemuCPUOps avr_sysemu_ops = { #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps avr_tcg_ops = { +static const TCGCPUOps avr_tcg_ops = { .initialize = avr_cpu_tcg_init, .synchronize_from_tb = avr_cpu_synchronize_from_tb, + .restore_state_to_opc = avr_restore_state_to_opc, .cpu_exec_interrupt = avr_cpu_exec_interrupt, .tlb_fill = avr_cpu_tlb_fill, .do_interrupt = avr_cpu_do_interrupt, @@ -205,23 +219,28 @@ static void avr_cpu_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); AVRCPUClass *mcc = AVR_CPU_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); device_class_set_parent_realize(dc, avr_cpu_realizefn, &mcc->parent_realize); - device_class_set_parent_reset(dc, avr_cpu_reset, &mcc->parent_reset); + + device_class_set_props(dc, avr_cpu_properties); + + resettable_class_set_parent_phases(rc, NULL, avr_cpu_reset_hold, NULL, + &mcc->parent_phases); cc->class_by_name = avr_cpu_class_by_name; cc->has_work = avr_cpu_has_work; + cc->mmu_index = avr_cpu_mmu_index; cc->dump_state = avr_cpu_dump_state; cc->set_pc = avr_cpu_set_pc; - cc->memory_rw_debug = avr_cpu_memory_rw_debug; + cc->get_pc = avr_cpu_get_pc; dc->vmsd = &vms_avr_cpu; cc->sysemu_ops = &avr_sysemu_ops; cc->disas_set_info = avr_cpu_disas_set_info; cc->gdb_read_register = avr_cpu_gdb_read_register; cc->gdb_write_register = avr_cpu_gdb_write_register; cc->gdb_adjust_breakpoint = avr_cpu_gdb_adjust_breakpoint; - cc->gdb_num_core_regs = 35; cc->gdb_core_xml_file = "avr-cpu.xml"; cc->tcg_ops = &avr_tcg_ops; } @@ -252,8 +271,7 @@ static void avr_cpu_class_init(ObjectClass *oc, void *data) */ static void avr_avr5_initfn(Object *obj) { - AVRCPU *cpu = AVR_CPU(obj); - CPUAVRState *env = &cpu->env; + CPUAVRState *env = cpu_env(CPU(obj)); set_avr_feature(env, AVR_FEATURE_LPM); set_avr_feature(env, AVR_FEATURE_IJMP_ICALL); @@ -281,8 +299,7 @@ static void avr_avr5_initfn(Object *obj) */ static void avr_avr51_initfn(Object *obj) { - AVRCPU *cpu = AVR_CPU(obj); - CPUAVRState *env = &cpu->env; + CPUAVRState *env = cpu_env(CPU(obj)); set_avr_feature(env, AVR_FEATURE_LPM); set_avr_feature(env, AVR_FEATURE_IJMP_ICALL); @@ -311,8 +328,7 @@ static void avr_avr51_initfn(Object *obj) */ static void avr_avr6_initfn(Object *obj) { - AVRCPU *cpu = AVR_CPU(obj); - CPUAVRState *env = &cpu->env; + CPUAVRState *env = cpu_env(CPU(obj)); set_avr_feature(env, AVR_FEATURE_LPM); set_avr_feature(env, AVR_FEATURE_IJMP_ICALL); @@ -338,21 +354,6 @@ typedef struct AVRCPUInfo { } AVRCPUInfo; -static void avr_cpu_list_entry(gpointer data, gpointer user_data) -{ - const char *typename = object_class_get_name(OBJECT_CLASS(data)); - - qemu_printf("%s\n", typename); -} - -void avr_cpu_list(void) -{ - GSList *list; - list = object_class_get_list_sorted(TYPE_AVR_CPU, false); - g_slist_foreach(list, avr_cpu_list_entry, NULL); - g_slist_free(list); -} - #define DEFINE_AVR_CPU_TYPE(model, initfn) \ { \ .parent = TYPE_AVR_CPU, \ @@ -365,6 +366,7 @@ static const TypeInfo avr_cpu_type_info[] = { .name = TYPE_AVR_CPU, .parent = TYPE_CPU, .instance_size = sizeof(AVRCPU), + .instance_align = __alignof(AVRCPU), .instance_init = avr_cpu_initfn, .class_size = sizeof(AVRCPUClass), .class_init = avr_cpu_class_init, diff --git a/target/avr/cpu.h b/target/avr/cpu.h index d304f33301..d185d20dcb 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -28,8 +28,6 @@ #error "AVR 8-bit does not support user mode" #endif -#define AVR_CPU_TYPE_SUFFIX "-" TYPE_AVR_CPU -#define AVR_CPU_TYPE_NAME(name) (name AVR_CPU_TYPE_SUFFIX) #define CPU_RESOLVING_TYPE TYPE_AVR_CPU #define TCG_GUEST_DEFAULT_MO 0 @@ -144,12 +142,26 @@ typedef struct CPUArchState { * A AVR CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUAVRState env; + + /* Initial value of stack pointer */ + uint32_t init_sp; +}; + +/** + * AVRCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A AVR CPU model. + */ +struct AVRCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; }; extern const struct VMStateDescription vms_avr_cpu; @@ -172,28 +184,17 @@ static inline void set_avr_feature(CPUAVRState *env, int feature) env->features |= (1U << feature); } -#define cpu_list avr_cpu_list -#define cpu_mmu_index avr_cpu_mmu_index - -static inline int avr_cpu_mmu_index(CPUAVRState *env, bool ifetch) -{ - return ifetch ? MMU_CODE_IDX : MMU_DATA_IDX; -} - void avr_cpu_tcg_init(void); -void avr_cpu_list(void); int cpu_avr_exec(CPUState *cpu); -int avr_cpu_memory_rw_debug(CPUState *cs, vaddr address, uint8_t *buf, - int len, bool is_write); enum { TB_FLAGS_FULL_ACCESS = 1, TB_FLAGS_SKIP = 2, }; -static inline void cpu_get_tb_cpu_state(CPUAVRState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *pflags) +static inline void cpu_get_tb_cpu_state(CPUAVRState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) { uint32_t flags = 0; @@ -217,8 +218,7 @@ static inline int cpu_interrupts_enabled(CPUAVRState *env) static inline uint8_t cpu_get_sreg(CPUAVRState *env) { - uint8_t sreg; - sreg = (env->sregC) << 0 + return (env->sregC) << 0 | (env->sregZ) << 1 | (env->sregN) << 2 | (env->sregV) << 3 @@ -226,7 +226,6 @@ static inline uint8_t cpu_get_sreg(CPUAVRState *env) | (env->sregH) << 5 | (env->sregT) << 6 | (env->sregI) << 7; - return sreg; } static inline void cpu_set_sreg(CPUAVRState *env, uint8_t sreg) diff --git a/target/avr/gdbstub.c b/target/avr/gdbstub.c index 1c1b908c92..2eeee2bf4e 100644 --- a/target/avr/gdbstub.c +++ b/target/avr/gdbstub.c @@ -19,12 +19,11 @@ */ #include "qemu/osdep.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" int avr_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - AVRCPU *cpu = AVR_CPU(cs); - CPUAVRState *env = &cpu->env; + CPUAVRState *env = cpu_env(cs); /* R */ if (n < 32) { @@ -53,8 +52,7 @@ int avr_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int avr_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - AVRCPU *cpu = AVR_CPU(cs); - CPUAVRState *env = &cpu->env; + CPUAVRState *env = cpu_env(cs); /* R */ if (n < 32) { diff --git a/target/avr/helper.c b/target/avr/helper.c index c27f702901..eeca415c43 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -20,50 +20,55 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qemu/error-report.h" #include "cpu.h" #include "hw/core/tcg-cpu-ops.h" #include "exec/exec-all.h" +#include "exec/cpu_ldst.h" #include "exec/address-spaces.h" #include "exec/helper-proto.h" bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { - bool ret = false; - CPUClass *cc = CPU_GET_CLASS(cs); - AVRCPU *cpu = AVR_CPU(cs); - CPUAVRState *env = &cpu->env; + CPUAVRState *env = cpu_env(cs); + + /* + * We cannot separate a skip from the next instruction, + * as the skip would not be preserved across the interrupt. + * Separating the two insn normally only happens at page boundaries. + */ + if (env->skip) { + return false; + } if (interrupt_request & CPU_INTERRUPT_RESET) { if (cpu_interrupts_enabled(env)) { cs->exception_index = EXCP_RESET; - cc->tcg_ops->do_interrupt(cs); + avr_cpu_do_interrupt(cs); cs->interrupt_request &= ~CPU_INTERRUPT_RESET; - - ret = true; + return true; } } if (interrupt_request & CPU_INTERRUPT_HARD) { if (cpu_interrupts_enabled(env) && env->intsrc != 0) { - int index = ctz32(env->intsrc); + int index = ctz64(env->intsrc); cs->exception_index = EXCP_INT(index); - cc->tcg_ops->do_interrupt(cs); + avr_cpu_do_interrupt(cs); env->intsrc &= env->intsrc - 1; /* clear the interrupt */ if (!env->intsrc) { cs->interrupt_request &= ~CPU_INTERRUPT_HARD; } - - ret = true; + return true; } } - return ret; + return false; } void avr_cpu_do_interrupt(CPUState *cs) { - AVRCPU *cpu = AVR_CPU(cs); - CPUAVRState *env = &cpu->env; + CPUAVRState *env = cpu_env(cs); uint32_t ret = env->pc_w; int vector = 0; @@ -73,7 +78,7 @@ void avr_cpu_do_interrupt(CPUState *cs) if (cs->exception_index == EXCP_RESET) { vector = 0; } else if (env->intsrc != 0) { - vector = ctz32(env->intsrc) + 1; + vector = ctz64(env->intsrc) + 1; } if (avr_feature(env, AVR_FEATURE_3_BYTE_PC)) { @@ -93,12 +98,6 @@ void avr_cpu_do_interrupt(CPUState *cs) cs->exception_index = -1; } -int avr_cpu_memory_rw_debug(CPUState *cs, vaddr addr, uint8_t *buf, - int len, bool is_write) -{ - return cpu_memory_rw_debug(cs, addr, buf, len, is_write); -} - hwaddr avr_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { return addr; /* I assume 1:1 address correspondence */ @@ -108,38 +107,48 @@ bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - int prot = 0; - MemTxAttrs attrs = {}; + int prot, page_size = TARGET_PAGE_SIZE; uint32_t paddr; address &= TARGET_PAGE_MASK; if (mmu_idx == MMU_CODE_IDX) { - /* access to code in flash */ + /* Access to code in flash. */ paddr = OFFSET_CODE + address; prot = PAGE_READ | PAGE_EXEC; - if (paddr + TARGET_PAGE_SIZE > OFFSET_DATA) { + if (paddr >= OFFSET_DATA) { + /* + * This should not be possible via any architectural operations. + * There is certainly not an exception that we can deliver. + * Accept probing that might come from generic code. + */ + if (probe) { + return false; + } error_report("execution left flash memory"); abort(); } - } else if (address < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) { - /* - * access to CPU registers, exit and rebuilt this TB to use full access - * incase it touches specially handled registers like SREG or SP - */ - AVRCPU *cpu = AVR_CPU(cs); - CPUAVRState *env = &cpu->env; - env->fullacc = 1; - cpu_loop_exit_restore(cs, retaddr); } else { - /* access to memory. nothing special */ + /* Access to memory. */ paddr = OFFSET_DATA + address; prot = PAGE_READ | PAGE_WRITE; + if (address < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) { + /* + * Access to CPU registers, exit and rebuilt this TB to use + * full access in case it touches specially handled registers + * like SREG or SP. For probing, set page_size = 1, in order + * to force tlb_fill to be called for the next access. + */ + if (probe) { + page_size = 1; + } else { + cpu_env(cs)->fullacc = 1; + cpu_loop_exit_restore(cs, retaddr); + } + } } - tlb_set_page_with_attrs(cs, address, paddr, attrs, prot, - mmu_idx, TARGET_PAGE_SIZE); - + tlb_set_page(cs, address, paddr, prot, mmu_idx, page_size); return true; } diff --git a/target/avr/machine.c b/target/avr/machine.c index 16f7a3e031..4402862fb9 100644 --- a/target/avr/machine.c +++ b/target/avr/machine.c @@ -100,7 +100,7 @@ const VMStateDescription vms_avr_cpu = { .name = "cpu", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.pc_w, AVRCPU), VMSTATE_UINT32(env.sp, AVRCPU), VMSTATE_UINT32(env.skip, AVRCPU), diff --git a/target/avr/meson.build b/target/avr/meson.build index 7e8e29c59d..3e172bde1c 100644 --- a/target/avr/meson.build +++ b/target/avr/meson.build @@ -4,7 +4,7 @@ gen = [ ] avr_ss = ss.source_set() -avr_softmmu_ss = ss.source_set() +avr_system_ss = ss.source_set() avr_ss.add(gen) avr_ss.add(files( @@ -14,7 +14,7 @@ avr_ss.add(files( 'gdbstub.c', 'disas.c')) -avr_softmmu_ss.add(files('machine.c')) +avr_system_ss.add(files('machine.c')) target_arch += {'avr': avr_ss} -target_softmmu_arch += {'avr': avr_softmmu_ss} +target_system_arch += {'avr': avr_system_ss} diff --git a/target/avr/translate.c b/target/avr/translate.c index dc9c3d6bcc..87e2bd5ef1 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -29,7 +29,11 @@ #include "exec/helper-gen.h" #include "exec/log.h" #include "exec/translator.h" -#include "exec/gen-icount.h" + +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + /* * Define if you want a BREAK instruction translated to a breakpoint @@ -107,11 +111,6 @@ struct DisasContext { * tcg_gen_brcond_tl(skip_cond, skip_var0, skip_var1, skip_label); * } * - * if (free_skip_var0) { - * tcg_temp_free(skip_var0); - * free_skip_var0 = false; - * } - * * translate(ctx); * * if (skip_label) { @@ -121,7 +120,6 @@ struct DisasContext { TCGv skip_var0; TCGv skip_var1; TCGCond skip_cond; - bool free_skip_var0; }; void avr_cpu_tcg_init(void) @@ -129,25 +127,25 @@ void avr_cpu_tcg_init(void) int i; #define AVR_REG_OFFS(x) offsetof(CPUAVRState, x) - cpu_pc = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(pc_w), "pc"); - cpu_Cf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregC), "Cf"); - cpu_Zf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregZ), "Zf"); - cpu_Nf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregN), "Nf"); - cpu_Vf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregV), "Vf"); - cpu_Sf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregS), "Sf"); - cpu_Hf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregH), "Hf"); - cpu_Tf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregT), "Tf"); - cpu_If = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregI), "If"); - cpu_rampD = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampD), "rampD"); - cpu_rampX = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampX), "rampX"); - cpu_rampY = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampY), "rampY"); - cpu_rampZ = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampZ), "rampZ"); - cpu_eind = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(eind), "eind"); - cpu_sp = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sp), "sp"); - cpu_skip = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(skip), "skip"); + cpu_pc = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(pc_w), "pc"); + cpu_Cf = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregC), "Cf"); + cpu_Zf = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregZ), "Zf"); + cpu_Nf = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregN), "Nf"); + cpu_Vf = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregV), "Vf"); + cpu_Sf = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregS), "Sf"); + cpu_Hf = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregH), "Hf"); + cpu_Tf = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregT), "Tf"); + cpu_If = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregI), "If"); + cpu_rampD = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(rampD), "rampD"); + cpu_rampX = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(rampX), "rampX"); + cpu_rampY = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(rampY), "rampY"); + cpu_rampZ = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(rampZ), "rampZ"); + cpu_eind = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(eind), "eind"); + cpu_sp = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sp), "sp"); + cpu_skip = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(skip), "skip"); for (i = 0; i < NUMBER_OF_CPU_REGISTERS; i++) { - cpu_r[i] = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(r[i]), + cpu_r[i] = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(r[i]), reg_names[i]); } #undef AVR_REG_OFFS @@ -186,7 +184,7 @@ static int append_16(DisasContext *ctx, int x) static bool avr_have_feature(DisasContext *ctx, int feature) { if (!avr_feature(ctx->env, feature)) { - gen_helper_unsupported(cpu_env); + gen_helper_unsupported(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; return false; } @@ -227,10 +225,6 @@ static void gen_add_CHf(TCGv R, TCGv Rd, TCGv Rr) tcg_gen_shri_tl(cpu_Cf, t1, 7); /* Cf = t1(7) */ tcg_gen_shri_tl(cpu_Hf, t1, 3); /* Hf = t1(3) */ tcg_gen_andi_tl(cpu_Hf, cpu_Hf, 1); - - tcg_temp_free_i32(t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t1); } static void gen_add_Vf(TCGv R, TCGv Rd, TCGv Rr) @@ -245,9 +239,6 @@ static void gen_add_Vf(TCGv R, TCGv Rd, TCGv Rr) tcg_gen_andc_tl(t1, t1, t2); tcg_gen_shri_tl(cpu_Vf, t1, 7); /* Vf = t1(7) */ - - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t1); } static void gen_sub_CHf(TCGv R, TCGv Rd, TCGv Rr) @@ -265,10 +256,6 @@ static void gen_sub_CHf(TCGv R, TCGv Rd, TCGv Rr) tcg_gen_shri_tl(cpu_Cf, t2, 7); /* Cf = t2(7) */ tcg_gen_shri_tl(cpu_Hf, t2, 3); /* Hf = t2(3) */ tcg_gen_andi_tl(cpu_Hf, cpu_Hf, 1); - - tcg_temp_free_i32(t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t1); } static void gen_sub_Vf(TCGv R, TCGv Rd, TCGv Rr) @@ -283,9 +270,6 @@ static void gen_sub_Vf(TCGv R, TCGv Rd, TCGv Rr) tcg_gen_and_tl(t1, t1, t2); tcg_gen_shri_tl(cpu_Vf, t1, 7); /* Vf = t1(7) */ - - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t1); } static void gen_NSf(TCGv R) @@ -323,9 +307,6 @@ static bool trans_ADD(DisasContext *ctx, arg_ADD *a) /* update output registers */ tcg_gen_mov_tl(Rd, R); - - tcg_temp_free_i32(R); - return true; } @@ -350,9 +331,6 @@ static bool trans_ADC(DisasContext *ctx, arg_ADC *a) /* update output registers */ tcg_gen_mov_tl(Rd, R); - - tcg_temp_free_i32(R); - return true; } @@ -391,10 +369,6 @@ static bool trans_ADIW(DisasContext *ctx, arg_ADIW *a) /* update output registers */ tcg_gen_andi_tl(RdL, R, 0xff); tcg_gen_shri_tl(RdH, R, 8); - - tcg_temp_free_i32(Rd); - tcg_temp_free_i32(R); - return true; } @@ -419,9 +393,6 @@ static bool trans_SUB(DisasContext *ctx, arg_SUB *a) /* update output registers */ tcg_gen_mov_tl(Rd, R); - - tcg_temp_free_i32(R); - return true; } @@ -433,7 +404,7 @@ static bool trans_SUB(DisasContext *ctx, arg_SUB *a) static bool trans_SUBI(DisasContext *ctx, arg_SUBI *a) { TCGv Rd = cpu_r[a->rd]; - TCGv Rr = tcg_const_i32(a->imm); + TCGv Rr = tcg_constant_i32(a->imm); TCGv R = tcg_temp_new_i32(); tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Imm */ @@ -446,10 +417,6 @@ static bool trans_SUBI(DisasContext *ctx, arg_SUBI *a) /* update output registers */ tcg_gen_mov_tl(Rd, R); - - tcg_temp_free_i32(R); - tcg_temp_free_i32(Rr); - return true; } @@ -462,7 +429,7 @@ static bool trans_SBC(DisasContext *ctx, arg_SBC *a) TCGv Rd = cpu_r[a->rd]; TCGv Rr = cpu_r[a->rr]; TCGv R = tcg_temp_new_i32(); - TCGv zero = tcg_const_i32(0); + TCGv zero = tcg_constant_i32(0); tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr - Cf */ tcg_gen_sub_tl(R, R, cpu_Cf); @@ -481,10 +448,6 @@ static bool trans_SBC(DisasContext *ctx, arg_SBC *a) /* update output registers */ tcg_gen_mov_tl(Rd, R); - - tcg_temp_free_i32(zero); - tcg_temp_free_i32(R); - return true; } @@ -494,9 +457,9 @@ static bool trans_SBC(DisasContext *ctx, arg_SBC *a) static bool trans_SBCI(DisasContext *ctx, arg_SBCI *a) { TCGv Rd = cpu_r[a->rd]; - TCGv Rr = tcg_const_i32(a->imm); + TCGv Rr = tcg_constant_i32(a->imm); TCGv R = tcg_temp_new_i32(); - TCGv zero = tcg_const_i32(0); + TCGv zero = tcg_constant_i32(0); tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr - Cf */ tcg_gen_sub_tl(R, R, cpu_Cf); @@ -515,11 +478,6 @@ static bool trans_SBCI(DisasContext *ctx, arg_SBCI *a) /* update output registers */ tcg_gen_mov_tl(Rd, R); - - tcg_temp_free_i32(zero); - tcg_temp_free_i32(R); - tcg_temp_free_i32(Rr); - return true; } @@ -558,10 +516,6 @@ static bool trans_SBIW(DisasContext *ctx, arg_SBIW *a) /* update output registers */ tcg_gen_andi_tl(RdL, R, 0xff); tcg_gen_shri_tl(RdH, R, 8); - - tcg_temp_free_i32(Rd); - tcg_temp_free_i32(R); - return true; } @@ -584,9 +538,6 @@ static bool trans_AND(DisasContext *ctx, arg_AND *a) /* update output registers */ tcg_gen_mov_tl(Rd, R); - - tcg_temp_free_i32(R); - return true; } @@ -626,9 +577,6 @@ static bool trans_OR(DisasContext *ctx, arg_OR *a) /* update output registers */ tcg_gen_mov_tl(Rd, R); - - tcg_temp_free_i32(R); - return true; } @@ -676,7 +624,6 @@ static bool trans_EOR(DisasContext *ctx, arg_EOR *a) static bool trans_COM(DisasContext *ctx, arg_COM *a) { TCGv Rd = cpu_r[a->rd]; - TCGv R = tcg_temp_new_i32(); tcg_gen_xori_tl(Rd, Rd, 0xff); @@ -684,9 +631,6 @@ static bool trans_COM(DisasContext *ctx, arg_COM *a) tcg_gen_movi_tl(cpu_Cf, 1); /* Cf = 1 */ tcg_gen_movi_tl(cpu_Vf, 0); /* Vf = 0 */ gen_ZNSf(Rd); - - tcg_temp_free_i32(R); - return true; } @@ -697,7 +641,7 @@ static bool trans_COM(DisasContext *ctx, arg_COM *a) static bool trans_NEG(DisasContext *ctx, arg_NEG *a) { TCGv Rd = cpu_r[a->rd]; - TCGv t0 = tcg_const_i32(0); + TCGv t0 = tcg_constant_i32(0); TCGv R = tcg_temp_new_i32(); tcg_gen_sub_tl(R, t0, Rd); /* R = 0 - Rd */ @@ -710,10 +654,6 @@ static bool trans_NEG(DisasContext *ctx, arg_NEG *a) /* update output registers */ tcg_gen_mov_tl(Rd, R); - - tcg_temp_free_i32(t0); - tcg_temp_free_i32(R); - return true; } @@ -783,9 +723,6 @@ static bool trans_MUL(DisasContext *ctx, arg_MUL *a) /* update status register */ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */ - - tcg_temp_free_i32(R); - return true; } @@ -816,11 +753,6 @@ static bool trans_MULS(DisasContext *ctx, arg_MULS *a) /* update status register */ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */ - - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(R); - return true; } @@ -850,10 +782,6 @@ static bool trans_MULSU(DisasContext *ctx, arg_MULSU *a) /* update status register */ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */ - - tcg_temp_free_i32(t0); - tcg_temp_free_i32(R); - return true; } @@ -884,10 +812,6 @@ static bool trans_FMUL(DisasContext *ctx, arg_FMUL *a) tcg_gen_andi_tl(R0, R, 0xff); tcg_gen_shri_tl(R1, R, 8); tcg_gen_andi_tl(R1, R1, 0xff); - - - tcg_temp_free_i32(R); - return true; } @@ -923,11 +847,6 @@ static bool trans_FMULS(DisasContext *ctx, arg_FMULS *a) tcg_gen_andi_tl(R0, R, 0xff); tcg_gen_shri_tl(R1, R, 8); tcg_gen_andi_tl(R1, R1, 0xff); - - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(R); - return true; } @@ -961,10 +880,6 @@ static bool trans_FMULSU(DisasContext *ctx, arg_FMULSU *a) tcg_gen_andi_tl(R0, R, 0xff); tcg_gen_shri_tl(R1, R, 8); tcg_gen_andi_tl(R1, R1, 0xff); - - tcg_temp_free_i32(t0); - tcg_temp_free_i32(R); - return true; } @@ -1019,35 +934,24 @@ static void gen_jmp_z(DisasContext *ctx) static void gen_push_ret(DisasContext *ctx, int ret) { if (avr_feature(ctx->env, AVR_FEATURE_1_BYTE_PC)) { - - TCGv t0 = tcg_const_i32((ret & 0x0000ff)); + TCGv t0 = tcg_constant_i32(ret & 0x0000ff); tcg_gen_qemu_st_tl(t0, cpu_sp, MMU_DATA_IDX, MO_UB); tcg_gen_subi_tl(cpu_sp, cpu_sp, 1); - - tcg_temp_free_i32(t0); } else if (avr_feature(ctx->env, AVR_FEATURE_2_BYTE_PC)) { - - TCGv t0 = tcg_const_i32((ret & 0x00ffff)); + TCGv t0 = tcg_constant_i32(ret & 0x00ffff); tcg_gen_subi_tl(cpu_sp, cpu_sp, 1); tcg_gen_qemu_st_tl(t0, cpu_sp, MMU_DATA_IDX, MO_BEUW); tcg_gen_subi_tl(cpu_sp, cpu_sp, 1); - - tcg_temp_free_i32(t0); - } else if (avr_feature(ctx->env, AVR_FEATURE_3_BYTE_PC)) { - - TCGv lo = tcg_const_i32((ret & 0x0000ff)); - TCGv hi = tcg_const_i32((ret & 0xffff00) >> 8); + TCGv lo = tcg_constant_i32(ret & 0x0000ff); + TCGv hi = tcg_constant_i32((ret & 0xffff00) >> 8); tcg_gen_qemu_st_tl(lo, cpu_sp, MMU_DATA_IDX, MO_UB); tcg_gen_subi_tl(cpu_sp, cpu_sp, 2); tcg_gen_qemu_st_tl(hi, cpu_sp, MMU_DATA_IDX, MO_BEUW); tcg_gen_subi_tl(cpu_sp, cpu_sp, 1); - - tcg_temp_free_i32(lo); - tcg_temp_free_i32(hi); } } @@ -1071,9 +975,6 @@ static void gen_pop_ret(DisasContext *ctx, TCGv ret) tcg_gen_qemu_ld_tl(lo, cpu_sp, MMU_DATA_IDX, MO_UB); tcg_gen_deposit_tl(ret, lo, hi, 8, 16); - - tcg_temp_free_i32(lo); - tcg_temp_free_i32(hi); } } @@ -1301,9 +1202,6 @@ static bool trans_CP(DisasContext *ctx, arg_CP *a) gen_sub_CHf(R, Rd, Rr); gen_sub_Vf(R, Rd, Rr); gen_ZNSf(R); - - tcg_temp_free_i32(R); - return true; } @@ -1317,7 +1215,7 @@ static bool trans_CPC(DisasContext *ctx, arg_CPC *a) TCGv Rd = cpu_r[a->rd]; TCGv Rr = cpu_r[a->rr]; TCGv R = tcg_temp_new_i32(); - TCGv zero = tcg_const_i32(0); + TCGv zero = tcg_constant_i32(0); tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr - Cf */ tcg_gen_sub_tl(R, R, cpu_Cf); @@ -1332,10 +1230,6 @@ static bool trans_CPC(DisasContext *ctx, arg_CPC *a) * cleared otherwise. */ tcg_gen_movcond_tl(TCG_COND_EQ, cpu_Zf, R, zero, cpu_Zf, zero); - - tcg_temp_free_i32(zero); - tcg_temp_free_i32(R); - return true; } @@ -1348,7 +1242,7 @@ static bool trans_CPI(DisasContext *ctx, arg_CPI *a) { TCGv Rd = cpu_r[a->rd]; int Imm = a->imm; - TCGv Rr = tcg_const_i32(Imm); + TCGv Rr = tcg_constant_i32(Imm); TCGv R = tcg_temp_new_i32(); tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr */ @@ -1358,10 +1252,6 @@ static bool trans_CPI(DisasContext *ctx, arg_CPI *a) gen_sub_CHf(R, Rd, Rr); gen_sub_Vf(R, Rd, Rr); gen_ZNSf(R); - - tcg_temp_free_i32(R); - tcg_temp_free_i32(Rr); - return true; } @@ -1375,7 +1265,6 @@ static bool trans_SBRC(DisasContext *ctx, arg_SBRC *a) ctx->skip_cond = TCG_COND_EQ; ctx->skip_var0 = tcg_temp_new(); - ctx->free_skip_var0 = true; tcg_gen_andi_tl(ctx->skip_var0, Rr, 1 << a->bit); return true; @@ -1391,7 +1280,6 @@ static bool trans_SBRS(DisasContext *ctx, arg_SBRS *a) ctx->skip_cond = TCG_COND_NE; ctx->skip_var0 = tcg_temp_new(); - ctx->free_skip_var0 = true; tcg_gen_andi_tl(ctx->skip_var0, Rr, 1 << a->bit); return true; @@ -1404,13 +1292,13 @@ static bool trans_SBRS(DisasContext *ctx, arg_SBRS *a) */ static bool trans_SBIC(DisasContext *ctx, arg_SBIC *a) { - TCGv temp = tcg_const_i32(a->reg); + TCGv data = tcg_temp_new_i32(); + TCGv port = tcg_constant_i32(a->reg); - gen_helper_inb(temp, cpu_env, temp); - tcg_gen_andi_tl(temp, temp, 1 << a->bit); + gen_helper_inb(data, tcg_env, port); + tcg_gen_andi_tl(data, data, 1 << a->bit); ctx->skip_cond = TCG_COND_EQ; - ctx->skip_var0 = temp; - ctx->free_skip_var0 = true; + ctx->skip_var0 = data; return true; } @@ -1422,13 +1310,13 @@ static bool trans_SBIC(DisasContext *ctx, arg_SBIC *a) */ static bool trans_SBIS(DisasContext *ctx, arg_SBIS *a) { - TCGv temp = tcg_const_i32(a->reg); + TCGv data = tcg_temp_new_i32(); + TCGv port = tcg_constant_i32(a->reg); - gen_helper_inb(temp, cpu_env, temp); - tcg_gen_andi_tl(temp, temp, 1 << a->bit); + gen_helper_inb(data, tcg_env, port); + tcg_gen_andi_tl(data, data, 1 << a->bit); ctx->skip_cond = TCG_COND_NE; - ctx->skip_var0 = temp; - ctx->free_skip_var0 = true; + ctx->skip_var0 = data; return true; } @@ -1606,18 +1494,18 @@ static TCGv gen_get_zaddr(void) static void gen_data_store(DisasContext *ctx, TCGv data, TCGv addr) { if (ctx->base.tb->flags & TB_FLAGS_FULL_ACCESS) { - gen_helper_fullwr(cpu_env, data, addr); + gen_helper_fullwr(tcg_env, data, addr); } else { - tcg_gen_qemu_st8(data, addr, MMU_DATA_IDX); /* mem[addr] = data */ + tcg_gen_qemu_st_tl(data, addr, MMU_DATA_IDX, MO_UB); } } static void gen_data_load(DisasContext *ctx, TCGv data, TCGv addr) { if (ctx->base.tb->flags & TB_FLAGS_FULL_ACCESS) { - gen_helper_fullrd(data, cpu_env, addr); + gen_helper_fullrd(data, tcg_env, addr); } else { - tcg_gen_qemu_ld8u(data, addr, MMU_DATA_IDX); /* data = mem[addr] */ + tcg_gen_qemu_ld_tl(data, addr, MMU_DATA_IDX, MO_UB); } } @@ -1697,9 +1585,6 @@ static bool trans_LDS(DisasContext *ctx, arg_LDS *a) tcg_gen_ori_tl(addr, addr, a->imm); gen_data_load(ctx, Rd, addr); - - tcg_temp_free_i32(addr); - return true; } @@ -1734,9 +1619,6 @@ static bool trans_LDX1(DisasContext *ctx, arg_LDX1 *a) TCGv addr = gen_get_xaddr(); gen_data_load(ctx, Rd, addr); - - tcg_temp_free_i32(addr); - return true; } @@ -1749,9 +1631,6 @@ static bool trans_LDX2(DisasContext *ctx, arg_LDX2 *a) tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */ gen_set_xaddr(addr); - - tcg_temp_free_i32(addr); - return true; } @@ -1763,9 +1642,6 @@ static bool trans_LDX3(DisasContext *ctx, arg_LDX3 *a) tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */ gen_data_load(ctx, Rd, addr); gen_set_xaddr(addr); - - tcg_temp_free_i32(addr); - return true; } @@ -1803,9 +1679,6 @@ static bool trans_LDY2(DisasContext *ctx, arg_LDY2 *a) tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */ gen_set_yaddr(addr); - - tcg_temp_free_i32(addr); - return true; } @@ -1817,9 +1690,6 @@ static bool trans_LDY3(DisasContext *ctx, arg_LDY3 *a) tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */ gen_data_load(ctx, Rd, addr); gen_set_yaddr(addr); - - tcg_temp_free_i32(addr); - return true; } @@ -1830,9 +1700,6 @@ static bool trans_LDDY(DisasContext *ctx, arg_LDDY *a) tcg_gen_addi_tl(addr, addr, a->imm); /* addr = addr + q */ gen_data_load(ctx, Rd, addr); - - tcg_temp_free_i32(addr); - return true; } @@ -1874,9 +1741,6 @@ static bool trans_LDZ2(DisasContext *ctx, arg_LDZ2 *a) tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */ gen_set_zaddr(addr); - - tcg_temp_free_i32(addr); - return true; } @@ -1889,9 +1753,6 @@ static bool trans_LDZ3(DisasContext *ctx, arg_LDZ3 *a) gen_data_load(ctx, Rd, addr); gen_set_zaddr(addr); - - tcg_temp_free_i32(addr); - return true; } @@ -1902,9 +1763,6 @@ static bool trans_LDDZ(DisasContext *ctx, arg_LDDZ *a) tcg_gen_addi_tl(addr, addr, a->imm); /* addr = addr + q */ gen_data_load(ctx, Rd, addr); - - tcg_temp_free_i32(addr); - return true; } @@ -1931,9 +1789,6 @@ static bool trans_STS(DisasContext *ctx, arg_STS *a) tcg_gen_shli_tl(addr, addr, 16); tcg_gen_ori_tl(addr, addr, a->imm); gen_data_store(ctx, Rd, addr); - - tcg_temp_free_i32(addr); - return true; } @@ -1964,9 +1819,6 @@ static bool trans_STX1(DisasContext *ctx, arg_STX1 *a) TCGv addr = gen_get_xaddr(); gen_data_store(ctx, Rd, addr); - - tcg_temp_free_i32(addr); - return true; } @@ -1978,9 +1830,6 @@ static bool trans_STX2(DisasContext *ctx, arg_STX2 *a) gen_data_store(ctx, Rd, addr); tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */ gen_set_xaddr(addr); - - tcg_temp_free_i32(addr); - return true; } @@ -1992,9 +1841,6 @@ static bool trans_STX3(DisasContext *ctx, arg_STX3 *a) tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */ gen_data_store(ctx, Rd, addr); gen_set_xaddr(addr); - - tcg_temp_free_i32(addr); - return true; } @@ -2029,9 +1875,6 @@ static bool trans_STY2(DisasContext *ctx, arg_STY2 *a) gen_data_store(ctx, Rd, addr); tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */ gen_set_yaddr(addr); - - tcg_temp_free_i32(addr); - return true; } @@ -2043,9 +1886,6 @@ static bool trans_STY3(DisasContext *ctx, arg_STY3 *a) tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */ gen_data_store(ctx, Rd, addr); gen_set_yaddr(addr); - - tcg_temp_free_i32(addr); - return true; } @@ -2056,9 +1896,6 @@ static bool trans_STDY(DisasContext *ctx, arg_STDY *a) tcg_gen_addi_tl(addr, addr, a->imm); /* addr = addr + q */ gen_data_store(ctx, Rd, addr); - - tcg_temp_free_i32(addr); - return true; } @@ -2094,9 +1931,6 @@ static bool trans_STZ2(DisasContext *ctx, arg_STZ2 *a) tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */ gen_set_zaddr(addr); - - tcg_temp_free_i32(addr); - return true; } @@ -2109,9 +1943,6 @@ static bool trans_STZ3(DisasContext *ctx, arg_STZ3 *a) gen_data_store(ctx, Rd, addr); gen_set_zaddr(addr); - - tcg_temp_free_i32(addr); - return true; } @@ -2122,9 +1953,6 @@ static bool trans_STDZ(DisasContext *ctx, arg_STDZ *a) tcg_gen_addi_tl(addr, addr, a->imm); /* addr = addr + q */ gen_data_store(ctx, Rd, addr); - - tcg_temp_free_i32(addr); - return true; } @@ -2155,10 +1983,7 @@ static bool trans_LPM1(DisasContext *ctx, arg_LPM1 *a) tcg_gen_shli_tl(addr, H, 8); /* addr = H:L */ tcg_gen_or_tl(addr, addr, L); - tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */ - - tcg_temp_free_i32(addr); - + tcg_gen_qemu_ld_tl(Rd, addr, MMU_CODE_IDX, MO_UB); return true; } @@ -2175,10 +2000,7 @@ static bool trans_LPM2(DisasContext *ctx, arg_LPM2 *a) tcg_gen_shli_tl(addr, H, 8); /* addr = H:L */ tcg_gen_or_tl(addr, addr, L); - tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */ - - tcg_temp_free_i32(addr); - + tcg_gen_qemu_ld_tl(Rd, addr, MMU_CODE_IDX, MO_UB); return true; } @@ -2195,14 +2017,11 @@ static bool trans_LPMX(DisasContext *ctx, arg_LPMX *a) tcg_gen_shli_tl(addr, H, 8); /* addr = H:L */ tcg_gen_or_tl(addr, addr, L); - tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */ + tcg_gen_qemu_ld_tl(Rd, addr, MMU_CODE_IDX, MO_UB); tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */ tcg_gen_andi_tl(L, addr, 0xff); tcg_gen_shri_tl(addr, addr, 8); tcg_gen_andi_tl(H, addr, 0xff); - - tcg_temp_free_i32(addr); - return true; } @@ -2230,10 +2049,7 @@ static bool trans_ELPM1(DisasContext *ctx, arg_ELPM1 *a) TCGv Rd = cpu_r[0]; TCGv addr = gen_get_zaddr(); - tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */ - - tcg_temp_free_i32(addr); - + tcg_gen_qemu_ld_tl(Rd, addr, MMU_CODE_IDX, MO_UB); return true; } @@ -2246,10 +2062,7 @@ static bool trans_ELPM2(DisasContext *ctx, arg_ELPM2 *a) TCGv Rd = cpu_r[a->rd]; TCGv addr = gen_get_zaddr(); - tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */ - - tcg_temp_free_i32(addr); - + tcg_gen_qemu_ld_tl(Rd, addr, MMU_CODE_IDX, MO_UB); return true; } @@ -2262,12 +2075,9 @@ static bool trans_ELPMX(DisasContext *ctx, arg_ELPMX *a) TCGv Rd = cpu_r[a->rd]; TCGv addr = gen_get_zaddr(); - tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */ + tcg_gen_qemu_ld_tl(Rd, addr, MMU_CODE_IDX, MO_UB); tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */ gen_set_zaddr(addr); - - tcg_temp_free_i32(addr); - return true; } @@ -2318,12 +2128,9 @@ static bool trans_SPMX(DisasContext *ctx, arg_SPMX *a) static bool trans_IN(DisasContext *ctx, arg_IN *a) { TCGv Rd = cpu_r[a->rd]; - TCGv port = tcg_const_i32(a->imm); - - gen_helper_inb(Rd, cpu_env, port); - - tcg_temp_free_i32(port); + TCGv port = tcg_constant_i32(a->imm); + gen_helper_inb(Rd, tcg_env, port); return true; } @@ -2334,12 +2141,9 @@ static bool trans_IN(DisasContext *ctx, arg_IN *a) static bool trans_OUT(DisasContext *ctx, arg_OUT *a) { TCGv Rd = cpu_r[a->rd]; - TCGv port = tcg_const_i32(a->imm); - - gen_helper_outb(cpu_env, port, Rd); - - tcg_temp_free_i32(port); + TCGv port = tcg_constant_i32(a->imm); + gen_helper_outb(tcg_env, port, Rd); return true; } @@ -2407,10 +2211,6 @@ static bool trans_XCH(DisasContext *ctx, arg_XCH *a) gen_data_load(ctx, t0, addr); gen_data_store(ctx, Rd, addr); tcg_gen_mov_tl(Rd, t0); - - tcg_temp_free_i32(t0); - tcg_temp_free_i32(addr); - return true; } @@ -2440,11 +2240,6 @@ static bool trans_LAS(DisasContext *ctx, arg_LAS *a) tcg_gen_or_tl(t1, t0, Rr); tcg_gen_mov_tl(Rr, t0); /* Rr = t0 */ gen_data_store(ctx, t1, addr); /* mem[addr] = t1 */ - - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(addr); - return true; } @@ -2475,11 +2270,6 @@ static bool trans_LAC(DisasContext *ctx, arg_LAC *a) tcg_gen_andc_tl(t1, t0, Rr); /* t1 = t0 & (0xff - Rr) = t0 & ~Rr */ tcg_gen_mov_tl(Rr, t0); /* Rr = t0 */ gen_data_store(ctx, t1, addr); /* mem[addr] = t1 */ - - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(addr); - return true; } @@ -2510,11 +2300,6 @@ static bool trans_LAT(DisasContext *ctx, arg_LAT *a) tcg_gen_xor_tl(t1, t0, Rd); tcg_gen_mov_tl(Rd, t0); /* Rd = t0 */ gen_data_store(ctx, t1, addr); /* mem[addr] = t1 */ - - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(addr); - return true; } @@ -2573,9 +2358,6 @@ static bool trans_ROR(DisasContext *ctx, arg_ROR *a) /* update status register */ gen_rshift_ZNVSf(Rd); - - tcg_temp_free_i32(t0); - return true; } @@ -2600,9 +2382,6 @@ static bool trans_ASR(DisasContext *ctx, arg_ASR *a) /* update status register */ gen_rshift_ZNVSf(Rd); - - tcg_temp_free_i32(t0); - return true; } @@ -2620,10 +2399,6 @@ static bool trans_SWAP(DisasContext *ctx, arg_SWAP *a) tcg_gen_andi_tl(t1, Rd, 0xf0); tcg_gen_shri_tl(t1, t1, 4); tcg_gen_or_tl(Rd, t0, t1); - - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t0); - return true; } @@ -2634,15 +2409,11 @@ static bool trans_SWAP(DisasContext *ctx, arg_SWAP *a) static bool trans_SBI(DisasContext *ctx, arg_SBI *a) { TCGv data = tcg_temp_new_i32(); - TCGv port = tcg_const_i32(a->reg); + TCGv port = tcg_constant_i32(a->reg); - gen_helper_inb(data, cpu_env, port); + gen_helper_inb(data, tcg_env, port); tcg_gen_ori_tl(data, data, 1 << a->bit); - gen_helper_outb(cpu_env, port, data); - - tcg_temp_free_i32(port); - tcg_temp_free_i32(data); - + gen_helper_outb(tcg_env, port, data); return true; } @@ -2653,15 +2424,11 @@ static bool trans_SBI(DisasContext *ctx, arg_SBI *a) static bool trans_CBI(DisasContext *ctx, arg_CBI *a) { TCGv data = tcg_temp_new_i32(); - TCGv port = tcg_const_i32(a->reg); + TCGv port = tcg_constant_i32(a->reg); - gen_helper_inb(data, cpu_env, port); + gen_helper_inb(data, tcg_env, port); tcg_gen_andi_tl(data, data, ~(1 << a->bit)); - gen_helper_outb(cpu_env, port, data); - - tcg_temp_free_i32(data); - tcg_temp_free_i32(port); - + gen_helper_outb(tcg_env, port, data); return true; } @@ -2689,9 +2456,6 @@ static bool trans_BLD(DisasContext *ctx, arg_BLD *a) tcg_gen_andi_tl(Rd, Rd, ~(1u << a->bit)); /* clear bit */ tcg_gen_shli_tl(t1, cpu_Tf, a->bit); /* create mask */ tcg_gen_or_tl(Rd, Rd, t1); - - tcg_temp_free_i32(t1); - return true; } @@ -2787,7 +2551,7 @@ static bool trans_BREAK(DisasContext *ctx, arg_BREAK *a) #ifdef BREAKPOINT_ON_BREAK tcg_gen_movi_tl(cpu_pc, ctx->npc - 1); - gen_helper_debug(cpu_env); + gen_helper_debug(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #else /* NOP */ @@ -2813,7 +2577,7 @@ static bool trans_NOP(DisasContext *ctx, arg_NOP *a) */ static bool trans_SLEEP(DisasContext *ctx, arg_SLEEP *a) { - gen_helper_sleep(cpu_env); + gen_helper_sleep(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; return true; } @@ -2825,7 +2589,7 @@ static bool trans_SLEEP(DisasContext *ctx, arg_SLEEP *a) */ static bool trans_WDR(DisasContext *ctx, arg_WDR *a) { - gen_helper_wdr(cpu_env); + gen_helper_wdr(tcg_env); return true; } @@ -2844,7 +2608,7 @@ static void translate(DisasContext *ctx) uint32_t opcode = next_word(ctx); if (!decode_insn(ctx, opcode)) { - gen_helper_unsupported(cpu_env); + gen_helper_unsupported(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; } } @@ -2886,10 +2650,6 @@ static bool canonicalize_skip(DisasContext *ctx) ctx->skip_cond = TCG_COND_NE; break; } - if (ctx->free_skip_var0) { - tcg_temp_free(ctx->skip_var0); - ctx->free_skip_var0 = false; - } ctx->skip_var0 = cpu_skip; return true; } @@ -2897,11 +2657,10 @@ static bool canonicalize_skip(DisasContext *ctx) static void avr_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - CPUAVRState *env = cs->env_ptr; uint32_t tb_flags = ctx->base.tb->flags; ctx->cs = cs; - ctx->env = env; + ctx->env = cpu_env(cs); ctx->npc = ctx->base.pc_first / 2; ctx->skip_cond = TCG_COND_NEVER; @@ -2944,7 +2703,6 @@ static void avr_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) * This ensures that cpu_skip is non-zero after the label * if and only if the skipped insn itself sets a skip. */ - ctx->free_skip_var0 = true; ctx->skip_var0 = tcg_temp_new(); tcg_gen_mov_tl(ctx->skip_var0, cpu_skip); tcg_gen_movi_tl(cpu_skip, 0); @@ -2956,10 +2714,6 @@ static void avr_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ctx->skip_var1, skip_label); ctx->skip_var1 = NULL; } - if (ctx->free_skip_var0) { - tcg_temp_free(ctx->skip_var0); - ctx->free_skip_var0 = false; - } ctx->skip_cond = TCG_COND_NEVER; ctx->skip_var0 = NULL; } @@ -2971,8 +2725,18 @@ static void avr_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) if (skip_label) { canonicalize_skip(ctx); gen_set_label(skip_label); - if (ctx->base.is_jmp == DISAS_NORETURN) { + + switch (ctx->base.is_jmp) { + case DISAS_NORETURN: ctx->base.is_jmp = DISAS_CHAIN; + break; + case DISAS_NEXT: + if (ctx->base.tb->flags & TB_FLAGS_SKIP) { + ctx->base.is_jmp = DISAS_TOO_MANY; + } + break; + default: + break; } } @@ -2989,6 +2753,11 @@ static void avr_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); bool nonconst_skip = canonicalize_skip(ctx); + /* + * Because we disable interrupts while env->skip is set, + * we must return to the main loop to re-evaluate afterward. + */ + bool force_exit = ctx->base.tb->flags & TB_FLAGS_SKIP; switch (ctx->base.is_jmp) { case DISAS_NORETURN: @@ -2997,7 +2766,7 @@ static void avr_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) case DISAS_NEXT: case DISAS_TOO_MANY: case DISAS_CHAIN: - if (!nonconst_skip) { + if (!nonconst_skip && !force_exit) { /* Note gen_goto_tb checks singlestep. */ gen_goto_tb(ctx, 1, ctx->npc); break; @@ -3005,8 +2774,11 @@ static void avr_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) tcg_gen_movi_tl(cpu_pc, ctx->npc); /* fall through */ case DISAS_LOOKUP: - tcg_gen_lookup_and_goto_ptr(); - break; + if (!force_exit) { + tcg_gen_lookup_and_goto_ptr(); + break; + } + /* fall through */ case DISAS_EXIT: tcg_gen_exit_tb(NULL, 0); break; @@ -3031,14 +2803,9 @@ static const TranslatorOps avr_tr_ops = { .disas_log = avr_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc = { }; - translator_loop(&avr_tr_ops, &dc.base, cs, tb, max_insns); -} - -void restore_state_to_opc(CPUAVRState *env, TranslationBlock *tb, - target_ulong *data) -{ - env->pc_w = data[0]; + translator_loop(cs, tb, max_insns, pc, host_pc, &avr_tr_ops, &dc.base); } diff --git a/target/cris/cpu-param.h b/target/cris/cpu-param.h index 12ec22d8df..b31b742c0d 100644 --- a/target/cris/cpu-param.h +++ b/target/cris/cpu-param.h @@ -12,6 +12,5 @@ #define TARGET_PAGE_BITS 13 #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -#define NB_MMU_MODES 2 #endif diff --git a/target/cris/cpu-qom.h b/target/cris/cpu-qom.h index 71e8af0e70..741ca97a1b 100644 --- a/target/cris/cpu-qom.h +++ b/target/cris/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU CRIS CPU + * QEMU CRIS CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -21,30 +21,12 @@ #define QEMU_CRIS_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_CRIS_CPU "cris-cpu" OBJECT_DECLARE_CPU_TYPE(CRISCPU, CRISCPUClass, CRIS_CPU) -/** - * CRISCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * @vr: Version Register value. - * - * A CRIS CPU model. - */ -struct CRISCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceReset parent_reset; - - uint32_t vr; -}; - +#define CRIS_CPU_TYPE_SUFFIX "-" TYPE_CRIS_CPU +#define CRIS_CPU_TYPE_NAME(name) (name CRIS_CPU_TYPE_SUFFIX) #endif diff --git a/target/cris/cpu.c b/target/cris/cpu.c index ed6c781342..eb4bddcb7e 100644 --- a/target/cris/cpu.c +++ b/target/cris/cpu.c @@ -35,20 +35,42 @@ static void cris_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.pc = value; } +static vaddr cris_cpu_get_pc(CPUState *cs) +{ + CRISCPU *cpu = CRIS_CPU(cs); + + return cpu->env.pc; +} + +static void cris_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + CRISCPU *cpu = CRIS_CPU(cs); + + cpu->env.pc = data[0]; +} + static bool cris_cpu_has_work(CPUState *cs) { return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } -static void cris_cpu_reset(DeviceState *dev) +static int cris_cpu_mmu_index(CPUState *cs, bool ifetch) { - CPUState *s = CPU(dev); - CRISCPU *cpu = CRIS_CPU(s); - CRISCPUClass *ccc = CRIS_CPU_GET_CLASS(cpu); - CPUCRISState *env = &cpu->env; + return !!(cpu_env(cs)->pregs[PR_CCS] & U_FLAG); +} + +static void cris_cpu_reset_hold(Object *obj) +{ + CPUState *cs = CPU(obj); + CRISCPUClass *ccc = CRIS_CPU_GET_CLASS(obj); + CPUCRISState *env = cpu_env(cs); uint32_t vr; - ccc->parent_reset(dev); + if (ccc->parent_phases.hold) { + ccc->parent_phases.hold(obj); + } vr = env->pregs[PR_VR]; memset(env, 0, offsetof(CPUCRISState, end_reset_fields)); @@ -77,51 +99,10 @@ static ObjectClass *cris_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf(CRIS_CPU_TYPE_NAME("%s"), cpu_model); oc = object_class_by_name(typename); g_free(typename); - if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_CRIS_CPU) || - object_class_is_abstract(oc))) { - oc = NULL; - } + return oc; } -/* Sort alphabetically by VR. */ -static gint cris_cpu_list_compare(gconstpointer a, gconstpointer b) -{ - CRISCPUClass *ccc_a = CRIS_CPU_CLASS(a); - CRISCPUClass *ccc_b = CRIS_CPU_CLASS(b); - - /* */ - if (ccc_a->vr > ccc_b->vr) { - return 1; - } else if (ccc_a->vr < ccc_b->vr) { - return -1; - } else { - return 0; - } -} - -static void cris_cpu_list_entry(gpointer data, gpointer user_data) -{ - ObjectClass *oc = data; - const char *typename = object_class_get_name(oc); - char *name; - - name = g_strndup(typename, strlen(typename) - strlen(CRIS_CPU_TYPE_SUFFIX)); - qemu_printf(" %s\n", name); - g_free(name); -} - -void cris_cpu_list(void) -{ - GSList *list; - - list = object_class_get_list(TYPE_CRIS_CPU, false); - list = g_slist_sort(list, cris_cpu_list_compare); - qemu_printf("Available CPUs:\n"); - g_slist_foreach(list, cris_cpu_list_entry, NULL); - g_slist_free(list); -} - static void cris_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -165,10 +146,7 @@ static void cris_cpu_set_irq(void *opaque, int irq, int level) static void cris_disas_set_info(CPUState *cpu, disassemble_info *info) { - CRISCPU *cc = CRIS_CPU(cpu); - CPUCRISState *env = &cc->env; - - if (env->pregs[PR_VR] != 32) { + if (cpu_env(cpu)->pregs[PR_VR] != 32) { info->mach = bfd_mach_cris_v0_v10; info->print_insn = print_insn_crisv10; } else { @@ -183,8 +161,6 @@ static void cris_cpu_initfn(Object *obj) CRISCPUClass *ccc = CRIS_CPU_GET_CLASS(obj); CPUCRISState *env = &cpu->env; - cpu_set_cpustate_pointers(cpu); - env->pregs[PR_VR] = ccc->vr; #ifndef CONFIG_USER_ONLY @@ -203,8 +179,9 @@ static const struct SysemuCPUOps cris_sysemu_ops = { #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps crisv10_tcg_ops = { +static const TCGCPUOps crisv10_tcg_ops = { .initialize = cris_initialize_crisv10_tcg, + .restore_state_to_opc = cris_restore_state_to_opc, #ifndef CONFIG_USER_ONLY .tlb_fill = cris_cpu_tlb_fill, @@ -213,8 +190,9 @@ static const struct TCGCPUOps crisv10_tcg_ops = { #endif /* !CONFIG_USER_ONLY */ }; -static const struct TCGCPUOps crisv32_tcg_ops = { +static const TCGCPUOps crisv32_tcg_ops = { .initialize = cris_initialize_tcg, + .restore_state_to_opc = cris_restore_state_to_opc, #ifndef CONFIG_USER_ONLY .tlb_fill = cris_cpu_tlb_fill, @@ -287,16 +265,20 @@ static void cris_cpu_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); device_class_set_parent_realize(dc, cris_cpu_realizefn, &ccc->parent_realize); - device_class_set_parent_reset(dc, cris_cpu_reset, &ccc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, cris_cpu_reset_hold, NULL, + &ccc->parent_phases); cc->class_by_name = cris_cpu_class_by_name; cc->has_work = cris_cpu_has_work; + cc->mmu_index = cris_cpu_mmu_index; cc->dump_state = cris_cpu_dump_state; cc->set_pc = cris_cpu_set_pc; + cc->get_pc = cris_cpu_get_pc; cc->gdb_read_register = cris_cpu_gdb_read_register; cc->gdb_write_register = cris_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY @@ -322,6 +304,7 @@ static const TypeInfo cris_cpu_model_type_infos[] = { .name = TYPE_CRIS_CPU, .parent = TYPE_CPU, .instance_size = sizeof(CRISCPU), + .instance_align = __alignof(CRISCPU), .instance_init = cris_cpu_initfn, .abstract = true, .class_size = sizeof(CRISCPUClass), diff --git a/target/cris/cpu.h b/target/cris/cpu.h index e6776f25b1..3904e5448c 100644 --- a/target/cris/cpu.h +++ b/target/cris/cpu.h @@ -174,14 +174,27 @@ typedef struct CPUArchState { * A CRIS CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUCRISState env; }; +/** + * CRISCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * @vr: Version Register value. + * + * A CRIS CPU model. + */ +struct CRISCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; + + uint32_t vr; +}; #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_cris_cpu; @@ -193,12 +206,11 @@ bool cris_cpu_exec_interrupt(CPUState *cpu, int int_req); bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +hwaddr cris_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #endif 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, 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); @@ -244,16 +256,10 @@ enum { /* CRIS uses 8k pages. */ #define MMAP_SHIFT TARGET_PAGE_BITS -#define CRIS_CPU_TYPE_SUFFIX "-" TYPE_CRIS_CPU -#define CRIS_CPU_TYPE_NAME(name) (name CRIS_CPU_TYPE_SUFFIX) #define CPU_RESOLVING_TYPE TYPE_CRIS_CPU /* MMU modes definitions */ #define MMU_USER_IDX 1 -static inline int cpu_mmu_index (CPUCRISState *env, bool ifetch) -{ - return !!(env->pregs[PR_CCS] & U_FLAG); -} /* Support function regs. */ #define SFR_RW_GC_CFG 0][0 @@ -267,8 +273,8 @@ static inline int cpu_mmu_index (CPUCRISState *env, bool ifetch) #include "exec/cpu-all.h" -static inline void cpu_get_tb_cpu_state(CPUCRISState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) +static inline void cpu_get_tb_cpu_state(CPUCRISState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { *pc = env->pc; *cs_base = 0; @@ -277,7 +283,4 @@ static inline void cpu_get_tb_cpu_state(CPUCRISState *env, target_ulong *pc, | X_FLAG | PFIX_FLAG)); } -#define cpu_list cris_cpu_list -void cris_cpu_list(void); - #endif diff --git a/target/cris/gdbstub.c b/target/cris/gdbstub.c index 2418d575b1..9e87069da8 100644 --- a/target/cris/gdbstub.c +++ b/target/cris/gdbstub.c @@ -19,12 +19,11 @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" int crisv10_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - CRISCPU *cpu = CRIS_CPU(cs); - CPUCRISState *env = &cpu->env; + CPUCRISState *env = cpu_env(cs); if (n < 15) { return gdb_get_reg32(mem_buf, env->regs[n]); @@ -55,8 +54,7 @@ int crisv10_cpu_gdb_read_register(CPUState *cs, GByteArray *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; + CPUCRISState *env = cpu_env(cs); uint8_t srs; srs = env->pregs[PR_SRS]; @@ -90,8 +88,7 @@ int cris_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int cris_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - CRISCPU *cpu = CRIS_CPU(cs); - CPUCRISState *env = &cpu->env; + CPUCRISState *env = cpu_env(cs); uint32_t tmp; if (n > 49) { diff --git a/target/cris/helper.c b/target/cris/helper.c index 91e4aeb178..1c3f86876f 100644 --- a/target/cris/helper.c +++ b/target/cris/helper.c @@ -53,8 +53,7 @@ bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - CRISCPU *cpu = CRIS_CPU(cs); - CPUCRISState *env = &cpu->env; + CPUCRISState *env = cpu_env(cs); struct cris_mmu_result res; int prot, miss; target_ulong phy; @@ -87,7 +86,7 @@ bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size, cs->exception_index = EXCP_BUSFAULT; env->fault_vector = res.bf_vec; if (retaddr) { - if (cpu_restore_state(cs, retaddr, true)) { + if (cpu_restore_state(cs, retaddr)) { /* Evaluate flags after retranslation. */ helper_top_evaluate_flags(env); } @@ -97,8 +96,7 @@ bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size, void crisv10_cpu_do_interrupt(CPUState *cs) { - CRISCPU *cpu = CRIS_CPU(cs); - CPUCRISState *env = &cpu->env; + CPUCRISState *env = cpu_env(cs); int ex_vec = -1; D_LOG("exception index=%d interrupt_req=%d\n", @@ -113,7 +111,7 @@ void crisv10_cpu_do_interrupt(CPUState *cs) assert(!(env->pregs[PR_CCS] & PFIX_FLAG)); switch (cs->exception_index) { case EXCP_BREAK: - /* These exceptions are genereated by the core itself. + /* These exceptions are generated by the core itself. ERP should point to the insn following the brk. */ ex_vec = env->trap_vector; env->pregs[PRV10_BRP] = env->pc; @@ -159,8 +157,7 @@ void crisv10_cpu_do_interrupt(CPUState *cs) void cris_cpu_do_interrupt(CPUState *cs) { - CRISCPU *cpu = CRIS_CPU(cs); - CPUCRISState *env = &cpu->env; + CPUCRISState *env = cpu_env(cs); int ex_vec = -1; D_LOG("exception index=%d interrupt_req=%d\n", @@ -169,7 +166,7 @@ void cris_cpu_do_interrupt(CPUState *cs) switch (cs->exception_index) { case EXCP_BREAK: - /* These exceptions are genereated by the core itself. + /* These exceptions are generated by the core itself. ERP should point to the insn following the brk. */ ex_vec = env->trap_vector; env->pregs[PR_ERP] = env->pc; @@ -228,7 +225,7 @@ void cris_cpu_do_interrupt(CPUState *cs) undefined. */ env->pc = cpu_ldl_code(env, env->pregs[PR_EBP] + ex_vec * 4); - /* Clear the excption_index to avoid spurios hw_aborts for recursive + /* Clear the excption_index to avoid spurious hw_aborts for recursive bus faults. */ cs->exception_index = -1; @@ -262,8 +259,7 @@ hwaddr cris_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) bool cris_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { CPUClass *cc = CPU_GET_CLASS(cs); - CRISCPU *cpu = CRIS_CPU(cs); - CPUCRISState *env = &cpu->env; + CPUCRISState *env = cpu_env(cs); bool ret = false; if (interrupt_request & CPU_INTERRUPT_HARD diff --git a/target/cris/machine.c b/target/cris/machine.c index f370f33486..7b9bde872a 100644 --- a/target/cris/machine.c +++ b/target/cris/machine.c @@ -26,7 +26,7 @@ static const VMStateDescription vmstate_tlbset = { .name = "cpu/tlbset", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(lo, TLBSet), VMSTATE_UINT32(hi, TLBSet), VMSTATE_END_OF_LIST() @@ -37,7 +37,7 @@ static const VMStateDescription vmstate_cris_env = { .name = "env", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, CPUCRISState, 16), VMSTATE_UINT32_ARRAY(pregs, CPUCRISState, 16), VMSTATE_UINT32(pc, CPUCRISState), @@ -85,7 +85,7 @@ const VMStateDescription vmstate_cris_cpu = { .name = "cpu", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CPU(), VMSTATE_STRUCT(env, CRISCPU, 1, vmstate_cris_env, CPUCRISState), VMSTATE_END_OF_LIST() diff --git a/target/cris/meson.build b/target/cris/meson.build index c1e326d950..bbfcdf7f7a 100644 --- a/target/cris/meson.build +++ b/target/cris/meson.build @@ -6,12 +6,12 @@ cris_ss.add(files( 'translate.c', )) -cris_softmmu_ss = ss.source_set() -cris_softmmu_ss.add(files( +cris_system_ss = ss.source_set() +cris_system_ss.add(files( 'helper.c', 'machine.c', 'mmu.c', )) target_arch += {'cris': cris_ss} -target_softmmu_arch += {'cris': cris_softmmu_ss} +target_system_arch += {'cris': cris_system_ss} diff --git a/target/cris/op_helper.c b/target/cris/op_helper.c index d55a18a213..98a9aaf504 100644 --- a/target/cris/op_helper.c +++ b/target/cris/op_helper.c @@ -24,7 +24,6 @@ #include "exec/helper-proto.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" //#define CRIS_OP_HELPER_DEBUG @@ -231,7 +230,7 @@ static inline uint32_t evaluate_flags_writeback(CPUCRISState *env, { unsigned int x, z, mask; - /* Extended arithmetics, leave the z flag alone. */ + /* Extended arithmetic, leave the z flag alone. */ x = env->cc_x; mask = env->cc_mask | X_FLAG; if (x) { diff --git a/target/cris/translate.c b/target/cris/translate.c index ac101344a3..b3a4d61d0a 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -34,11 +34,13 @@ #include "exec/translator.h" #include "crisv32-decode.h" #include "qemu/qemu-print.h" - #include "exec/helper-gen.h" - #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + #define DISAS_CRIS 0 #if DISAS_CRIS @@ -86,14 +88,13 @@ static TCGv env_btaken; static TCGv env_btarget; static TCGv env_pc; -#include "exec/gen-icount.h" - /* This is the state at translation time. */ typedef struct DisasContext { DisasContextBase base; CRISCPU *cpu; target_ulong pc, ppc; + int mem_index; /* Decoder. */ unsigned int (*decoder)(CPUCRISState *env, struct DisasContext *dc); @@ -171,15 +172,11 @@ static const int preg_sizes[] = { }; #define t_gen_mov_TN_env(tn, member) \ - tcg_gen_ld_tl(tn, cpu_env, offsetof(CPUCRISState, member)) + tcg_gen_ld_tl(tn, tcg_env, offsetof(CPUCRISState, member)) #define t_gen_mov_env_TN(member, tn) \ - tcg_gen_st_tl(tn, cpu_env, offsetof(CPUCRISState, member)) + tcg_gen_st_tl(tn, tcg_env, offsetof(CPUCRISState, member)) #define t_gen_movi_env_TN(member, c) \ - do { \ - TCGv tc = tcg_const_tl(c); \ - t_gen_mov_env_TN(member, tc); \ - tcg_temp_free(tc); \ - } while (0) + t_gen_mov_env_TN(member, tcg_constant_tl(c)) static inline void t_gen_mov_TN_preg(TCGv tn, int r) { @@ -201,10 +198,10 @@ static inline void t_gen_mov_preg_TN(DisasContext *dc, int r, TCGv tn) tcg_gen_andi_tl(cpu_PR[r], tn, 3); } else { if (r == PR_PID) { - gen_helper_tlb_flush_pid(cpu_env, tn); + gen_helper_tlb_flush_pid(tcg_env, tn); } if (dc->tb_flags & S_FLAG && r == PR_SPC) { - gen_helper_spc_write(cpu_env, tn); + gen_helper_spc_write(tcg_env, tn); } else if (r == PR_CCS) { dc->cpustate_changed = 1; } @@ -269,9 +266,7 @@ static void cris_lock_irq(DisasContext *dc) static inline void t_gen_raise_exception(uint32_t index) { - TCGv_i32 tmp = tcg_const_i32(index); - gen_helper_raise_exception(cpu_env, tmp); - tcg_temp_free_i32(tmp); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(index)); } static void t_gen_lsl(TCGv d, TCGv a, TCGv b) @@ -279,15 +274,13 @@ static void t_gen_lsl(TCGv d, TCGv a, TCGv b) TCGv t0, t_31; t0 = tcg_temp_new(); - t_31 = tcg_const_tl(31); + t_31 = tcg_constant_tl(31); tcg_gen_shl_tl(d, a, b); tcg_gen_sub_tl(t0, t_31, b); tcg_gen_sar_tl(t0, t0, t_31); tcg_gen_and_tl(t0, t0, d); tcg_gen_xor_tl(d, d, t0); - tcg_temp_free(t0); - tcg_temp_free(t_31); } static void t_gen_lsr(TCGv d, TCGv a, TCGv b) @@ -303,8 +296,6 @@ static void t_gen_lsr(TCGv d, TCGv a, TCGv b) tcg_gen_sar_tl(t0, t0, t_31); tcg_gen_and_tl(t0, t0, d); tcg_gen_xor_tl(d, d, t0); - tcg_temp_free(t0); - tcg_temp_free(t_31); } static void t_gen_asr(TCGv d, TCGv a, TCGv b) @@ -319,8 +310,6 @@ static void t_gen_asr(TCGv d, TCGv a, TCGv b) tcg_gen_sub_tl(t0, t_31, b); tcg_gen_sar_tl(t0, t0, t_31); tcg_gen_or_tl(d, d, t0); - tcg_temp_free(t0); - tcg_temp_free(t_31); } static void t_gen_cris_dstep(TCGv d, TCGv a, TCGv b) @@ -335,7 +324,6 @@ static void t_gen_cris_dstep(TCGv d, TCGv a, TCGv b) tcg_gen_shli_tl(d, a, 1); tcg_gen_sub_tl(t, d, b); tcg_gen_movcond_tl(TCG_COND_GEU, d, d, b, t, d); - tcg_temp_free(t); } static void t_gen_cris_mstep(TCGv d, TCGv a, TCGv b, TCGv ccs) @@ -353,10 +341,9 @@ static void t_gen_cris_mstep(TCGv d, TCGv a, TCGv b, TCGv ccs) tcg_gen_sari_tl(t, t, 31); tcg_gen_and_tl(t, t, b); tcg_gen_add_tl(d, d, t); - tcg_temp_free(t); } -/* Extended arithmetics on CRIS. */ +/* Extended arithmetic on CRIS. */ static inline void t_gen_add_flag(TCGv d, int flag) { TCGv c; @@ -369,7 +356,6 @@ static inline void t_gen_add_flag(TCGv d, int flag) tcg_gen_shri_tl(c, c, flag); } tcg_gen_add_tl(d, d, c); - tcg_temp_free(c); } static inline void t_gen_addx_carry(DisasContext *dc, TCGv d) @@ -381,7 +367,6 @@ static inline void t_gen_addx_carry(DisasContext *dc, TCGv d) /* C flag is already at bit 0. */ tcg_gen_andi_tl(c, c, C_FLAG); tcg_gen_add_tl(d, d, c); - tcg_temp_free(c); } } @@ -394,7 +379,6 @@ static inline void t_gen_subx_carry(DisasContext *dc, TCGv d) /* C flag is already at bit 0. */ tcg_gen_andi_tl(c, c, C_FLAG); tcg_gen_sub_tl(d, d, c); - tcg_temp_free(c); } } @@ -414,8 +398,6 @@ static inline void t_gen_swapb(TCGv d, TCGv s) tcg_gen_shri_tl(t, org_s, 8); tcg_gen_andi_tl(t, t, 0x00ff00ff); tcg_gen_or_tl(d, d, t); - tcg_temp_free(t); - tcg_temp_free(org_s); } /* Swap the halfwords of the s operand. */ @@ -428,18 +410,19 @@ static inline void t_gen_swapw(TCGv d, TCGv s) tcg_gen_shli_tl(d, t, 16); tcg_gen_shri_tl(t, t, 16); tcg_gen_or_tl(d, d, t); - tcg_temp_free(t); } -/* Reverse the within each byte. - T0 = (((T0 << 7) & 0x80808080) | - ((T0 << 5) & 0x40404040) | - ((T0 << 3) & 0x20202020) | - ((T0 << 1) & 0x10101010) | - ((T0 >> 1) & 0x08080808) | - ((T0 >> 3) & 0x04040404) | - ((T0 >> 5) & 0x02020202) | - ((T0 >> 7) & 0x01010101)); +/* + * Reverse the bits within each byte. + * + * T0 = ((T0 << 7) & 0x80808080) + * | ((T0 << 5) & 0x40404040) + * | ((T0 << 3) & 0x20202020) + * | ((T0 << 1) & 0x10101010) + * | ((T0 >> 1) & 0x08080808) + * | ((T0 >> 3) & 0x04040404) + * | ((T0 >> 5) & 0x02020202) + * | ((T0 >> 7) & 0x01010101); */ static void t_gen_swapr(TCGv d, TCGv s) { @@ -475,8 +458,6 @@ static void t_gen_swapr(TCGv d, TCGv s) tcg_gen_andi_tl(t, t, bitrev[i].mask); tcg_gen_or_tl(d, d, t); } - tcg_temp_free(t); - tcg_temp_free(org_s); } static bool use_goto_tb(DisasContext *dc, target_ulong dest) @@ -524,17 +505,17 @@ static void cris_evaluate_flags(DisasContext *dc) switch (dc->cc_op) { case CC_OP_MCP: - gen_helper_evaluate_flags_mcp(cpu_PR[PR_CCS], cpu_env, + gen_helper_evaluate_flags_mcp(cpu_PR[PR_CCS], tcg_env, cpu_PR[PR_CCS], cc_src, cc_dest, cc_result); break; case CC_OP_MULS: - gen_helper_evaluate_flags_muls(cpu_PR[PR_CCS], cpu_env, + gen_helper_evaluate_flags_muls(cpu_PR[PR_CCS], tcg_env, cpu_PR[PR_CCS], cc_result, cpu_PR[PR_MOF]); break; case CC_OP_MULU: - gen_helper_evaluate_flags_mulu(cpu_PR[PR_CCS], cpu_env, + gen_helper_evaluate_flags_mulu(cpu_PR[PR_CCS], tcg_env, cpu_PR[PR_CCS], cc_result, cpu_PR[PR_MOF]); break; @@ -548,14 +529,14 @@ static void cris_evaluate_flags(DisasContext *dc) switch (dc->cc_size) { case 4: gen_helper_evaluate_flags_move_4(cpu_PR[PR_CCS], - cpu_env, cpu_PR[PR_CCS], cc_result); + tcg_env, cpu_PR[PR_CCS], cc_result); break; case 2: gen_helper_evaluate_flags_move_2(cpu_PR[PR_CCS], - cpu_env, cpu_PR[PR_CCS], cc_result); + tcg_env, cpu_PR[PR_CCS], cc_result); break; default: - gen_helper_evaluate_flags(cpu_env); + gen_helper_evaluate_flags(tcg_env); break; } break; @@ -565,21 +546,21 @@ static void cris_evaluate_flags(DisasContext *dc) case CC_OP_SUB: case CC_OP_CMP: if (dc->cc_size == 4) { - gen_helper_evaluate_flags_sub_4(cpu_PR[PR_CCS], cpu_env, + gen_helper_evaluate_flags_sub_4(cpu_PR[PR_CCS], tcg_env, cpu_PR[PR_CCS], cc_src, cc_dest, cc_result); } else { - gen_helper_evaluate_flags(cpu_env); + gen_helper_evaluate_flags(tcg_env); } break; default: switch (dc->cc_size) { case 4: - gen_helper_evaluate_flags_alu_4(cpu_PR[PR_CCS], cpu_env, + gen_helper_evaluate_flags_alu_4(cpu_PR[PR_CCS], tcg_env, cpu_PR[PR_CCS], cc_src, cc_dest, cc_result); break; default: - gen_helper_evaluate_flags(cpu_env); + gen_helper_evaluate_flags(tcg_env); break; } break; @@ -668,7 +649,7 @@ static void cris_alu_op_exec(DisasContext *dc, int op, switch (op) { case CC_OP_ADD: tcg_gen_add_tl(dst, a, b); - /* Extended arithmetics. */ + /* Extended arithmetic. */ t_gen_addx_carry(dc, dst); break; case CC_OP_ADDC: @@ -681,7 +662,7 @@ static void cris_alu_op_exec(DisasContext *dc, int op, break; case CC_OP_SUB: tcg_gen_sub_tl(dst, a, b); - /* Extended arithmetics. */ + /* Extended arithmetic. */ t_gen_subx_carry(dc, dst); break; case CC_OP_MOVE: @@ -707,7 +688,7 @@ static void cris_alu_op_exec(DisasContext *dc, int op, break; case CC_OP_NEG: tcg_gen_neg_tl(dst, b); - /* Extended arithmetics. */ + /* Extended arithmetic. */ t_gen_subx_carry(dc, dst); break; case CC_OP_LZ: @@ -730,7 +711,7 @@ static void cris_alu_op_exec(DisasContext *dc, int op, break; case CC_OP_CMP: tcg_gen_sub_tl(dst, a, b); - /* Extended arithmetics. */ + /* Extended arithmetic. */ t_gen_subx_carry(dc, dst); break; default: @@ -778,9 +759,6 @@ static void cris_alu(DisasContext *dc, int op, } tcg_gen_or_tl(d, d, tmp); } - if (tmp != d) { - tcg_temp_free(tmp); - } } static int arith_cc(DisasContext *dc) @@ -919,8 +897,6 @@ static void gen_tst_cc (DisasContext *dc, TCGv cc, int cond) tcg_gen_shli_tl(cc, tmp, 2); tcg_gen_and_tl(cc, tmp, cc); tcg_gen_andi_tl(cc, cc, Z_FLAG); - - tcg_temp_free(tmp); } break; case CC_GE: @@ -959,9 +935,6 @@ static void gen_tst_cc (DisasContext *dc, TCGv cc, int cond) tcg_gen_xori_tl(n, n, 2); tcg_gen_and_tl(cc, z, n); tcg_gen_andi_tl(cc, cc, 2); - - tcg_temp_free(n); - tcg_temp_free(z); } break; case CC_LE: @@ -980,9 +953,6 @@ static void gen_tst_cc (DisasContext *dc, TCGv cc, int cond) tcg_gen_xor_tl(n, n, cpu_PR[PR_CCS]); tcg_gen_or_tl(cc, z, n); tcg_gen_andi_tl(cc, cc, 2); - - tcg_temp_free(n); - tcg_temp_free(z); } break; case CC_P: @@ -1039,37 +1009,31 @@ static inline void cris_prepare_jmp (DisasContext *dc, unsigned int type) static void gen_load64(DisasContext *dc, TCGv_i64 dst, TCGv addr) { - int mem_index = cpu_mmu_index(&dc->cpu->env, false); - /* If we get a fault on a delayslot we must keep the jmp state in the cpu-state to be able to re-execute the jmp. */ if (dc->delayed_branch == 1) { cris_store_direct_jmp(dc); } - tcg_gen_qemu_ld_i64(dst, addr, mem_index, MO_TEUQ); + tcg_gen_qemu_ld_i64(dst, addr, dc->mem_index, MO_TEUQ); } static void gen_load(DisasContext *dc, TCGv dst, TCGv addr, unsigned int size, int sign) { - int mem_index = cpu_mmu_index(&dc->cpu->env, false); - /* If we get a fault on a delayslot we must keep the jmp state in the cpu-state to be able to re-execute the jmp. */ if (dc->delayed_branch == 1) { cris_store_direct_jmp(dc); } - tcg_gen_qemu_ld_tl(dst, addr, mem_index, + tcg_gen_qemu_ld_tl(dst, addr, dc->mem_index, MO_TE + ctz32(size) + (sign ? MO_SIGN : 0)); } static void gen_store (DisasContext *dc, TCGv addr, TCGv val, unsigned int size) { - int mem_index = cpu_mmu_index(&dc->cpu->env, false); - /* If we get a fault on a delayslot we must keep the jmp state in the cpu-state to be able to re-execute the jmp. */ if (dc->delayed_branch == 1) { @@ -1086,7 +1050,7 @@ static void gen_store (DisasContext *dc, TCGv addr, TCGv val, return; } - tcg_gen_qemu_st_tl(val, addr, mem_index, MO_TE + ctz32(size)); + tcg_gen_qemu_st_tl(val, addr, dc->mem_index, MO_TE + ctz32(size)); if (dc->flags_x) { cris_evaluate_flags(dc); @@ -1279,10 +1243,9 @@ static int dec_addq(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_const_tl(dc->op1); + c = tcg_constant_tl(dc->op1); cris_alu(dc, CC_OP_ADD, cpu_R[dc->op2], cpu_R[dc->op2], c, 4); - tcg_temp_free(c); return 2; } static int dec_moveq(CPUCRISState *env, DisasContext *dc) @@ -1304,10 +1267,9 @@ static int dec_subq(CPUCRISState *env, DisasContext *dc) LOG_DIS("subq %u, $r%u\n", dc->op1, dc->op2); cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_const_tl(dc->op1); + c = tcg_constant_tl(dc->op1); cris_alu(dc, CC_OP_SUB, cpu_R[dc->op2], cpu_R[dc->op2], c, 4); - tcg_temp_free(c); return 2; } static int dec_cmpq(CPUCRISState *env, DisasContext *dc) @@ -1320,10 +1282,9 @@ static int dec_cmpq(CPUCRISState *env, DisasContext *dc) LOG_DIS("cmpq %d, $r%d\n", imm, dc->op2); cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_const_tl(imm); + c = tcg_constant_tl(imm); cris_alu(dc, CC_OP_CMP, cpu_R[dc->op2], cpu_R[dc->op2], c, 4); - tcg_temp_free(c); return 2; } static int dec_andq(CPUCRISState *env, DisasContext *dc) @@ -1336,10 +1297,9 @@ static int dec_andq(CPUCRISState *env, DisasContext *dc) LOG_DIS("andq %d, $r%d\n", imm, dc->op2); cris_cc_mask(dc, CC_MASK_NZ); - c = tcg_const_tl(imm); + c = tcg_constant_tl(imm); cris_alu(dc, CC_OP_AND, cpu_R[dc->op2], cpu_R[dc->op2], c, 4); - tcg_temp_free(c); return 2; } static int dec_orq(CPUCRISState *env, DisasContext *dc) @@ -1351,10 +1311,9 @@ static int dec_orq(CPUCRISState *env, DisasContext *dc) LOG_DIS("orq %d, $r%d\n", imm, dc->op2); cris_cc_mask(dc, CC_MASK_NZ); - c = tcg_const_tl(imm); + c = tcg_constant_tl(imm); cris_alu(dc, CC_OP_OR, cpu_R[dc->op2], cpu_R[dc->op2], c, 4); - tcg_temp_free(c); return 2; } static int dec_btstq(CPUCRISState *env, DisasContext *dc) @@ -1364,11 +1323,10 @@ static int dec_btstq(CPUCRISState *env, DisasContext *dc) LOG_DIS("btstq %u, $r%d\n", dc->op1, dc->op2); cris_cc_mask(dc, CC_MASK_NZ); - c = tcg_const_tl(dc->op1); + c = tcg_constant_tl(dc->op1); cris_evaluate_flags(dc); - gen_helper_btst(cpu_PR[PR_CCS], cpu_env, cpu_R[dc->op2], + gen_helper_btst(cpu_PR[PR_CCS], tcg_env, cpu_R[dc->op2], c, cpu_PR[PR_CCS]); - tcg_temp_free(c); cris_alu(dc, CC_OP_MOVE, cpu_R[dc->op2], cpu_R[dc->op2], cpu_R[dc->op2], 4); cris_update_cc_op(dc, CC_OP_FLAGS, 4); @@ -1437,7 +1395,6 @@ static int dec_move_r(CPUCRISState *env, DisasContext *dc) cris_alu(dc, CC_OP_MOVE, cpu_R[dc->op2], cpu_R[dc->op2], t0, size); - tcg_temp_free(t0); } return 2; } @@ -1467,14 +1424,6 @@ static inline void cris_alu_alloc_temps(DisasContext *dc, int size, TCGv *t) } } -static inline void cris_alu_free_temps(DisasContext *dc, int size, TCGv *t) -{ - if (size != 4) { - tcg_temp_free(t[0]); - tcg_temp_free(t[1]); - } -} - static int dec_and_r(CPUCRISState *env, DisasContext *dc) { TCGv t[2]; @@ -1488,7 +1437,6 @@ static int dec_and_r(CPUCRISState *env, DisasContext *dc) cris_alu_alloc_temps(dc, size, t); dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]); cris_alu(dc, CC_OP_AND, cpu_R[dc->op2], t[0], t[1], size); - cris_alu_free_temps(dc, size, t); return 2; } @@ -1501,7 +1449,6 @@ static int dec_lz_r(CPUCRISState *env, DisasContext *dc) t0 = tcg_temp_new(); dec_prep_alu_r(dc, dc->op1, dc->op2, 4, 0, cpu_R[dc->op2], t0); cris_alu(dc, CC_OP_LZ, cpu_R[dc->op2], cpu_R[dc->op2], t0, 4); - tcg_temp_free(t0); return 2; } @@ -1518,7 +1465,6 @@ static int dec_lsl_r(CPUCRISState *env, DisasContext *dc) dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]); tcg_gen_andi_tl(t[1], t[1], 63); cris_alu(dc, CC_OP_LSL, cpu_R[dc->op2], t[0], t[1], size); - cris_alu_free_temps(dc, size, t); return 2; } @@ -1535,7 +1481,6 @@ static int dec_lsr_r(CPUCRISState *env, DisasContext *dc) dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]); tcg_gen_andi_tl(t[1], t[1], 63); cris_alu(dc, CC_OP_LSR, cpu_R[dc->op2], t[0], t[1], size); - cris_alu_free_temps(dc, size, t); return 2; } @@ -1552,7 +1497,6 @@ static int dec_asr_r(CPUCRISState *env, DisasContext *dc) dec_prep_alu_r(dc, dc->op1, dc->op2, size, 1, t[0], t[1]); tcg_gen_andi_tl(t[1], t[1], 63); cris_alu(dc, CC_OP_ASR, cpu_R[dc->op2], t[0], t[1], size); - cris_alu_free_temps(dc, size, t); return 2; } @@ -1568,7 +1512,6 @@ static int dec_muls_r(CPUCRISState *env, DisasContext *dc) dec_prep_alu_r(dc, dc->op1, dc->op2, size, 1, t[0], t[1]); cris_alu(dc, CC_OP_MULS, cpu_R[dc->op2], t[0], t[1], 4); - cris_alu_free_temps(dc, size, t); return 2; } @@ -1584,7 +1527,6 @@ static int dec_mulu_r(CPUCRISState *env, DisasContext *dc) dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]); cris_alu(dc, CC_OP_MULU, cpu_R[dc->op2], t[0], t[1], 4); - cris_alu_free_temps(dc, size, t); return 2; } @@ -1610,7 +1552,6 @@ static int dec_xor_r(CPUCRISState *env, DisasContext *dc) dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]); cris_alu(dc, CC_OP_XOR, cpu_R[dc->op2], t[0], t[1], 4); - cris_alu_free_temps(dc, size, t); return 2; } @@ -1621,10 +1562,9 @@ static int dec_bound_r(CPUCRISState *env, DisasContext *dc) LOG_DIS("bound.%c $r%u, $r%u\n", memsize_char(size), dc->op1, dc->op2); cris_cc_mask(dc, CC_MASK_NZ); - l0 = tcg_temp_local_new(); + l0 = tcg_temp_new(); dec_prep_move_r(dc, dc->op1, dc->op2, size, 0, l0); cris_alu(dc, CC_OP_BOUND, cpu_R[dc->op2], cpu_R[dc->op2], l0, 4); - tcg_temp_free(l0); return 2; } @@ -1639,7 +1579,6 @@ static int dec_cmp_r(CPUCRISState *env, DisasContext *dc) dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]); cris_alu(dc, CC_OP_CMP, cpu_R[dc->op2], t[0], t[1], size); - cris_alu_free_temps(dc, size, t); return 2; } @@ -1666,7 +1605,6 @@ static int dec_add_r(CPUCRISState *env, DisasContext *dc) dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]); cris_alu(dc, CC_OP_ADD, cpu_R[dc->op2], t[0], t[1], size); - cris_alu_free_temps(dc, size, t); return 2; } @@ -1741,7 +1679,6 @@ static int dec_swap_r(CPUCRISState *env, DisasContext *dc) t_gen_swapr(t0, t0); } cris_alu(dc, CC_OP_MOVE, cpu_R[dc->op1], cpu_R[dc->op1], t0, 4); - tcg_temp_free(t0); return 2; } @@ -1755,7 +1692,6 @@ static int dec_or_r(CPUCRISState *env, DisasContext *dc) cris_alu_alloc_temps(dc, size, t); dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]); cris_alu(dc, CC_OP_OR, cpu_R[dc->op2], t[0], t[1], size); - cris_alu_free_temps(dc, size, t); return 2; } @@ -1768,7 +1704,6 @@ static int dec_addi_r(CPUCRISState *env, DisasContext *dc) t0 = tcg_temp_new(); tcg_gen_shli_tl(t0, cpu_R[dc->op2], dc->zzsize); tcg_gen_add_tl(cpu_R[dc->op1], cpu_R[dc->op1], t0); - tcg_temp_free(t0); return 2; } @@ -1781,7 +1716,6 @@ static int dec_addi_acr(CPUCRISState *env, DisasContext *dc) t0 = tcg_temp_new(); tcg_gen_shli_tl(t0, cpu_R[dc->op2], dc->zzsize); tcg_gen_add_tl(cpu_R[R_ACR], cpu_R[dc->op1], t0); - tcg_temp_free(t0); return 2; } @@ -1796,7 +1730,6 @@ static int dec_neg_r(CPUCRISState *env, DisasContext *dc) dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]); cris_alu(dc, CC_OP_NEG, cpu_R[dc->op2], t[0], t[1], size); - cris_alu_free_temps(dc, size, t); return 2; } @@ -1806,7 +1739,7 @@ static int dec_btst_r(CPUCRISState *env, DisasContext *dc) dc->op1, dc->op2); cris_cc_mask(dc, CC_MASK_NZ); cris_evaluate_flags(dc); - gen_helper_btst(cpu_PR[PR_CCS], cpu_env, cpu_R[dc->op2], + gen_helper_btst(cpu_PR[PR_CCS], tcg_env, cpu_R[dc->op2], cpu_R[dc->op1], cpu_PR[PR_CCS]); cris_alu(dc, CC_OP_MOVE, cpu_R[dc->op2], cpu_R[dc->op2], cpu_R[dc->op2], 4); @@ -1825,7 +1758,6 @@ static int dec_sub_r(CPUCRISState *env, DisasContext *dc) cris_alu_alloc_temps(dc, size, t); dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]); cris_alu(dc, CC_OP_SUB, cpu_R[dc->op2], t[0], t[1], size); - cris_alu_free_temps(dc, size, t); return 2; } @@ -1842,7 +1774,6 @@ static int dec_movu_r(CPUCRISState *env, DisasContext *dc) t0 = tcg_temp_new(); dec_prep_move_r(dc, dc->op1, dc->op2, size, 0, t0); cris_alu(dc, CC_OP_MOVE, cpu_R[dc->op2], cpu_R[dc->op2], t0, 4); - tcg_temp_free(t0); return 2; } @@ -1861,7 +1792,6 @@ static int dec_movs_r(CPUCRISState *env, DisasContext *dc) t_gen_sext(t0, cpu_R[dc->op1], size); cris_alu(dc, CC_OP_MOVE, cpu_R[dc->op2], cpu_R[dc->op1], t0, 4); - tcg_temp_free(t0); return 2; } @@ -1879,7 +1809,6 @@ static int dec_addu_r(CPUCRISState *env, DisasContext *dc) /* Size can only be qi or hi. */ t_gen_zext(t0, cpu_R[dc->op1], size); cris_alu(dc, CC_OP_ADD, cpu_R[dc->op2], cpu_R[dc->op2], t0, 4); - tcg_temp_free(t0); return 2; } @@ -1898,7 +1827,6 @@ static int dec_adds_r(CPUCRISState *env, DisasContext *dc) t_gen_sext(t0, cpu_R[dc->op1], size); cris_alu(dc, CC_OP_ADD, cpu_R[dc->op2], cpu_R[dc->op2], t0, 4); - tcg_temp_free(t0); return 2; } @@ -1917,7 +1845,6 @@ static int dec_subu_r(CPUCRISState *env, DisasContext *dc) t_gen_zext(t0, cpu_R[dc->op1], size); cris_alu(dc, CC_OP_SUB, cpu_R[dc->op2], cpu_R[dc->op2], t0, 4); - tcg_temp_free(t0); return 2; } @@ -1936,7 +1863,6 @@ static int dec_subs_r(CPUCRISState *env, DisasContext *dc) t_gen_sext(t0, cpu_R[dc->op1], size); cris_alu(dc, CC_OP_SUB, cpu_R[dc->op2], cpu_R[dc->op2], t0, 4); - tcg_temp_free(t0); return 2; } @@ -2012,24 +1938,20 @@ static int dec_move_rs(CPUCRISState *env, DisasContext *dc) { TCGv c2, c1; LOG_DIS("move $r%u, $s%u\n", dc->op1, dc->op2); - c1 = tcg_const_tl(dc->op1); - c2 = tcg_const_tl(dc->op2); + c1 = tcg_constant_tl(dc->op1); + c2 = tcg_constant_tl(dc->op2); cris_cc_mask(dc, 0); - gen_helper_movl_sreg_reg(cpu_env, c2, c1); - tcg_temp_free(c1); - tcg_temp_free(c2); + gen_helper_movl_sreg_reg(tcg_env, c2, c1); return 2; } static int dec_move_sr(CPUCRISState *env, DisasContext *dc) { TCGv c2, c1; LOG_DIS("move $s%u, $r%u\n", dc->op2, dc->op1); - c1 = tcg_const_tl(dc->op1); - c2 = tcg_const_tl(dc->op2); + c1 = tcg_constant_tl(dc->op1); + c2 = tcg_constant_tl(dc->op2); cris_cc_mask(dc, 0); - gen_helper_movl_reg_sreg(cpu_env, c1, c2); - tcg_temp_free(c1); - tcg_temp_free(c2); + gen_helper_movl_reg_sreg(tcg_env, c1, c2); return 2; } @@ -2049,7 +1971,6 @@ static int dec_move_rp(CPUCRISState *env, DisasContext *dc) tcg_gen_andi_tl(t[0], t[0], 0x39f); tcg_gen_andi_tl(t[1], cpu_PR[PR_CCS], ~0x39f); tcg_gen_or_tl(t[0], t[1], t[0]); - tcg_temp_free(t[1]); } } else { tcg_gen_mov_tl(t[0], cpu_R[dc->op1]); @@ -2060,7 +1981,6 @@ static int dec_move_rp(CPUCRISState *env, DisasContext *dc) cris_update_cc_op(dc, CC_OP_FLAGS, 4); dc->flags_uptodate = 1; } - tcg_temp_free(t[0]); return 2; } static int dec_move_pr(CPUCRISState *env, DisasContext *dc) @@ -2081,7 +2001,6 @@ static int dec_move_pr(CPUCRISState *env, DisasContext *dc) cris_alu(dc, CC_OP_MOVE, cpu_R[dc->op1], cpu_R[dc->op1], t0, preg_sizes[dc->op2]); - tcg_temp_free(t0); } return 2; } @@ -2109,7 +2028,6 @@ static int dec_move_mr(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, CC_MASK_NZ); cris_alu(dc, CC_OP_MOVE, cpu_R[dc->op2], cpu_R[dc->op2], t0, memsize); - tcg_temp_free(t0); } do_postinc(dc, memsize); return insn_len; @@ -2121,12 +2039,6 @@ static inline void cris_alu_m_alloc_temps(TCGv *t) t[1] = tcg_temp_new(); } -static inline void cris_alu_m_free_temps(TCGv *t) -{ - tcg_temp_free(t[0]); - tcg_temp_free(t[1]); -} - static int dec_movs_m(CPUCRISState *env, DisasContext *dc) { TCGv t[2]; @@ -2144,7 +2056,6 @@ static int dec_movs_m(CPUCRISState *env, DisasContext *dc) cris_alu(dc, CC_OP_MOVE, cpu_R[dc->op2], cpu_R[dc->op2], t[1], 4); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2165,7 +2076,6 @@ static int dec_addu_m(CPUCRISState *env, DisasContext *dc) cris_alu(dc, CC_OP_ADD, cpu_R[dc->op2], cpu_R[dc->op2], t[1], 4); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2185,7 +2095,6 @@ static int dec_adds_m(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, CC_MASK_NZVC); cris_alu(dc, CC_OP_ADD, cpu_R[dc->op2], cpu_R[dc->op2], t[1], 4); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2205,7 +2114,6 @@ static int dec_subu_m(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, CC_MASK_NZVC); cris_alu(dc, CC_OP_SUB, cpu_R[dc->op2], cpu_R[dc->op2], t[1], 4); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2225,7 +2133,6 @@ static int dec_subs_m(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, CC_MASK_NZVC); cris_alu(dc, CC_OP_SUB, cpu_R[dc->op2], cpu_R[dc->op2], t[1], 4); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2245,7 +2152,6 @@ static int dec_movu_m(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, CC_MASK_NZ); cris_alu(dc, CC_OP_MOVE, cpu_R[dc->op2], cpu_R[dc->op2], t[1], 4); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2264,7 +2170,6 @@ static int dec_cmpu_m(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, CC_MASK_NZVC); cris_alu(dc, CC_OP_CMP, cpu_R[dc->op2], cpu_R[dc->op2], t[1], 4); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2285,7 +2190,6 @@ static int dec_cmps_m(CPUCRISState *env, DisasContext *dc) cpu_R[dc->op2], cpu_R[dc->op2], t[1], memsize_zz(dc)); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2306,7 +2210,6 @@ static int dec_cmp_m(CPUCRISState *env, DisasContext *dc) cpu_R[dc->op2], cpu_R[dc->op2], t[1], memsize_zz(dc)); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2327,12 +2230,10 @@ static int dec_test_m(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, CC_MASK_NZ); tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~3); - c = tcg_const_tl(0); + c = tcg_constant_tl(0); cris_alu(dc, CC_OP_CMP, cpu_R[dc->op2], t[1], c, memsize_zz(dc)); - tcg_temp_free(c); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2351,7 +2252,6 @@ static int dec_and_m(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, CC_MASK_NZ); cris_alu(dc, CC_OP_AND, cpu_R[dc->op2], t[0], t[1], memsize_zz(dc)); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2371,7 +2271,6 @@ static int dec_add_m(CPUCRISState *env, DisasContext *dc) cris_alu(dc, CC_OP_ADD, cpu_R[dc->op2], t[0], t[1], memsize_zz(dc)); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2390,7 +2289,6 @@ static int dec_addo_m(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, 0); cris_alu(dc, CC_OP_ADD, cpu_R[R_ACR], t[0], t[1], 4); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2404,14 +2302,12 @@ static int dec_bound_m(CPUCRISState *env, DisasContext *dc) dc->op1, dc->postinc ? "+]" : "]", dc->op2); - l[0] = tcg_temp_local_new(); - l[1] = tcg_temp_local_new(); + l[0] = tcg_temp_new(); + l[1] = tcg_temp_new(); insn_len = dec_prep_alu_m(env, dc, 0, memsize, l[0], l[1]); cris_cc_mask(dc, CC_MASK_NZ); cris_alu(dc, CC_OP_BOUND, cpu_R[dc->op2], l[0], l[1], 4); do_postinc(dc, memsize); - tcg_temp_free(l[0]); - tcg_temp_free(l[1]); return insn_len; } @@ -2433,7 +2329,6 @@ static int dec_addc_mr(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, CC_MASK_NZVC); cris_alu(dc, CC_OP_ADDC, cpu_R[dc->op2], t[0], t[1], 4); do_postinc(dc, 4); - cris_alu_m_free_temps(t); return insn_len; } @@ -2452,7 +2347,6 @@ static int dec_sub_m(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, CC_MASK_NZVC); cris_alu(dc, CC_OP_SUB, cpu_R[dc->op2], t[0], t[1], memsize); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2472,7 +2366,6 @@ static int dec_or_m(CPUCRISState *env, DisasContext *dc) cris_alu(dc, CC_OP_OR, cpu_R[dc->op2], t[0], t[1], memsize_zz(dc)); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2504,7 +2397,6 @@ static int dec_move_mp(CPUCRISState *env, DisasContext *dc) t_gen_mov_preg_TN(dc, dc->op2, t[1]); do_postinc(dc, memsize); - cris_alu_m_free_temps(t); return insn_len; } @@ -2527,7 +2419,6 @@ static int dec_move_pm(CPUCRISState *env, DisasContext *dc) t_gen_mov_TN_preg(t0, dc->op2); cris_flush_cc_state(dc); gen_store(dc, cpu_R[dc->op1], t0, memsize); - tcg_temp_free(t0); cris_cc_mask(dc, 0); if (dc->postinc) { @@ -2562,17 +2453,14 @@ static int dec_movem_mr(CPUCRISState *env, DisasContext *dc) } else { tmp32 = NULL; } - tcg_temp_free(addr); for (i = 0; i < (nr >> 1); i++) { tcg_gen_extrl_i64_i32(cpu_R[i * 2], tmp[i]); tcg_gen_shri_i64(tmp[i], tmp[i], 32); tcg_gen_extrl_i64_i32(cpu_R[i * 2 + 1], tmp[i]); - tcg_temp_free_i64(tmp[i]); } if (nr & 1) { tcg_gen_mov_tl(cpu_R[dc->op2], tmp32); - tcg_temp_free(tmp32); } /* writeback the updated pointer value. */ @@ -2610,8 +2498,6 @@ static int dec_movem_rm(CPUCRISState *env, DisasContext *dc) tcg_gen_mov_tl(cpu_R[dc->op1], addr); } cris_cc_mask(dc, 0); - tcg_temp_free(tmp); - tcg_temp_free(addr); return 2; } @@ -2689,9 +2575,8 @@ static int dec_jas_r(CPUCRISState *env, DisasContext *dc) if (dc->op2 > 15) { abort(); } - c = tcg_const_tl(dc->pc + 4); + c = tcg_constant_tl(dc->pc + 4); t_gen_mov_preg_TN(dc, dc->op2, c); - tcg_temp_free(c); cris_prepare_jmp(dc, JMP_INDIRECT); return 2; @@ -2706,10 +2591,9 @@ static int dec_jas_im(CPUCRISState *env, DisasContext *dc) LOG_DIS("jas 0x%x\n", imm); cris_cc_mask(dc, 0); - c = tcg_const_tl(dc->pc + 8); + c = tcg_constant_tl(dc->pc + 8); /* Store the return address in Pd. */ t_gen_mov_preg_TN(dc, dc->op2, c); - tcg_temp_free(c); dc->jmp_pc = imm; cris_prepare_jmp(dc, JMP_DIRECT); @@ -2725,10 +2609,9 @@ static int dec_jasc_im(CPUCRISState *env, DisasContext *dc) LOG_DIS("jasc 0x%x\n", imm); cris_cc_mask(dc, 0); - c = tcg_const_tl(dc->pc + 8 + 4); + c = tcg_constant_tl(dc->pc + 8 + 4); /* Store the return address in Pd. */ t_gen_mov_preg_TN(dc, dc->op2, c); - tcg_temp_free(c); dc->jmp_pc = imm; cris_prepare_jmp(dc, JMP_DIRECT); @@ -2742,9 +2625,8 @@ static int dec_jasc_r(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, 0); /* Store the return address in Pd. */ tcg_gen_mov_tl(env_btarget, cpu_R[dc->op1]); - c = tcg_const_tl(dc->pc + 4 + 4); + c = tcg_constant_tl(dc->pc + 4 + 4); t_gen_mov_preg_TN(dc, dc->op2, c); - tcg_temp_free(c); cris_prepare_jmp(dc, JMP_INDIRECT); return 2; } @@ -2775,10 +2657,9 @@ static int dec_bas_im(CPUCRISState *env, DisasContext *dc) LOG_DIS("bas 0x%x, $p%u\n", dc->pc + simm, dc->op2); cris_cc_mask(dc, 0); - c = tcg_const_tl(dc->pc + 8); + c = tcg_constant_tl(dc->pc + 8); /* Store the return address in Pd. */ t_gen_mov_preg_TN(dc, dc->op2, c); - tcg_temp_free(c); dc->jmp_pc = dc->pc + simm; cris_prepare_jmp(dc, JMP_DIRECT); @@ -2793,10 +2674,9 @@ static int dec_basc_im(CPUCRISState *env, DisasContext *dc) LOG_DIS("basc 0x%x, $p%u\n", dc->pc + simm, dc->op2); cris_cc_mask(dc, 0); - c = tcg_const_tl(dc->pc + 12); + c = tcg_constant_tl(dc->pc + 12); /* Store the return address in Pd. */ t_gen_mov_preg_TN(dc, dc->op2, c); - tcg_temp_free(c); dc->jmp_pc = dc->pc + simm; cris_prepare_jmp(dc, JMP_DIRECT); @@ -2808,7 +2688,7 @@ static int dec_rfe_etc(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, 0); if (dc->op2 == 15) { - tcg_gen_st_i32(tcg_const_i32(1), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(1), tcg_env, -offsetof(CRISCPU, env) + offsetof(CPUState, halted)); tcg_gen_movi_tl(env_pc, dc->pc + 2); t_gen_raise_exception(EXCP_HLT); @@ -2821,7 +2701,7 @@ static int dec_rfe_etc(CPUCRISState *env, DisasContext *dc) /* rfe. */ LOG_DIS("rfe\n"); cris_evaluate_flags(dc); - gen_helper_rfe(cpu_env); + gen_helper_rfe(tcg_env); dc->base.is_jmp = DISAS_UPDATE; dc->cpustate_changed = true; break; @@ -2829,7 +2709,7 @@ static int dec_rfe_etc(CPUCRISState *env, DisasContext *dc) /* rfn. */ LOG_DIS("rfn\n"); cris_evaluate_flags(dc); - gen_helper_rfn(cpu_env); + gen_helper_rfn(tcg_env); dc->base.is_jmp = DISAS_UPDATE; dc->cpustate_changed = true; break; @@ -3041,12 +2921,12 @@ static unsigned int crisv32_decoder(CPUCRISState *env, DisasContext *dc) * On QEMU care needs to be taken when a branch+delayslot sequence is broken * and the branch and delayslot don't share pages. * - * The TB contaning the branch insn will set up env->btarget and evaluate + * The TB containing the branch insn will set up env->btarget and evaluate * env->btaken. When the translation loop exits we will note that the branch * sequence is broken and let env->dslot be the size of the branch insn (those * vary in length). * - * The TB contaning the delayslot will have the PC of its real insn (i.e no lsb + * The TB containing the delayslot will have the PC of its real insn (i.e no lsb * set). It will also expect to have env->dslot setup with the size of the * delay slot so that env->pc - env->dslot point to the branch insn. This TB * will execute the dslot and take the branch, either to btarget or just one @@ -3063,7 +2943,7 @@ static unsigned int crisv32_decoder(CPUCRISState *env, DisasContext *dc) static void cris_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUCRISState *env = cs->env_ptr; + CPUCRISState *env = cpu_env(cs); uint32_t tb_flags = dc->base.tb->flags; uint32_t pc_start; @@ -3086,6 +2966,7 @@ static void cris_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->cpu = env_archcpu(env); dc->ppc = pc_start; dc->pc = pc_start; + dc->mem_index = cpu_mmu_index(cs, false); dc->flags_uptodate = 1; dc->flags_x = tb_flags & X_FLAG; dc->cc_x_uptodate = 0; @@ -3121,7 +3002,6 @@ static void cris_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) static void cris_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUCRISState *env = cs->env_ptr; unsigned int insn_len; /* Pretty disas. */ @@ -3129,7 +3009,7 @@ static void cris_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) dc->clear_x = 1; - insn_len = dc->decoder(env, dc); + insn_len = dc->decoder(cpu_env(cs), dc); dc->ppc = dc->pc; dc->pc += insn_len; dc->base.pc_next += insn_len; @@ -3260,7 +3140,7 @@ static void cris_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) tcg_gen_lookup_and_goto_ptr(); break; case DISAS_UPDATE: - /* Indicate that interupts must be re-evaluated before the next TB. */ + /* Indicate that interrupts must be re-evaluated before the next TB. */ tcg_gen_exit_tb(NULL, 0); break; default: @@ -3286,16 +3166,16 @@ static const TranslatorOps cris_tr_ops = { .disas_log = cris_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc; - translator_loop(&cris_tr_ops, &dc.base, cs, tb, max_insns); + translator_loop(cs, tb, max_insns, pc, host_pc, &cris_tr_ops, &dc.base); } void cris_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - CRISCPU *cpu = CRIS_CPU(cs); - CPUCRISState *env = &cpu->env; + CPUCRISState *env = cpu_env(cs); const char * const *regnames; const char * const *pregnames; int i; @@ -3352,48 +3232,42 @@ void cris_initialize_tcg(void) { int i; - cc_x = tcg_global_mem_new(cpu_env, + cc_x = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, cc_x), "cc_x"); - cc_src = tcg_global_mem_new(cpu_env, + cc_src = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, cc_src), "cc_src"); - cc_dest = tcg_global_mem_new(cpu_env, + cc_dest = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, cc_dest), "cc_dest"); - cc_result = tcg_global_mem_new(cpu_env, + cc_result = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, cc_result), "cc_result"); - cc_op = tcg_global_mem_new(cpu_env, + cc_op = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, cc_op), "cc_op"); - cc_size = tcg_global_mem_new(cpu_env, + cc_size = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, cc_size), "cc_size"); - cc_mask = tcg_global_mem_new(cpu_env, + cc_mask = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, cc_mask), "cc_mask"); - env_pc = tcg_global_mem_new(cpu_env, + env_pc = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, pc), "pc"); - env_btarget = tcg_global_mem_new(cpu_env, + env_btarget = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, btarget), "btarget"); - env_btaken = tcg_global_mem_new(cpu_env, + env_btaken = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, btaken), "btaken"); for (i = 0; i < 16; i++) { - cpu_R[i] = tcg_global_mem_new(cpu_env, + cpu_R[i] = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, regs[i]), regnames_v32[i]); } for (i = 0; i < 16; i++) { - cpu_PR[i] = tcg_global_mem_new(cpu_env, + cpu_PR[i] = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, pregs[i]), pregnames_v32[i]); } } - -void restore_state_to_opc(CPUCRISState *env, TranslationBlock *tb, - target_ulong *data) -{ - env->pc = data[0]; -} diff --git a/target/cris/translate_v10.c.inc b/target/cris/translate_v10.c.inc index f500e93447..73fc27c15d 100644 --- a/target/cris/translate_v10.c.inc +++ b/target/cris/translate_v10.c.inc @@ -68,9 +68,9 @@ static void gen_store_v10_conditional(DisasContext *dc, TCGv addr, TCGv val, unsigned int size, int mem_index) { TCGLabel *l1 = gen_new_label(); - TCGv taddr = tcg_temp_local_new(); - TCGv tval = tcg_temp_local_new(); - TCGv t1 = tcg_temp_local_new(); + TCGv taddr = tcg_temp_new(); + TCGv tval = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); dc->postinc = 0; cris_evaluate_flags(dc); @@ -80,26 +80,17 @@ static void gen_store_v10_conditional(DisasContext *dc, TCGv addr, TCGv val, /* Store only if F flag isn't set */ tcg_gen_andi_tl(t1, cpu_PR[PR_CCS], F_FLAG_V10); tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); - if (size == 1) { - tcg_gen_qemu_st8(tval, taddr, mem_index); - } else if (size == 2) { - tcg_gen_qemu_st16(tval, taddr, mem_index); - } else { - tcg_gen_qemu_st32(tval, taddr, mem_index); - } + + tcg_gen_qemu_st_tl(tval, taddr, mem_index, ctz32(size) | MO_TE); + gen_set_label(l1); tcg_gen_shri_tl(t1, t1, 1); /* shift F to P position */ tcg_gen_or_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], t1); /*P=F*/ - tcg_temp_free(t1); - tcg_temp_free(tval); - tcg_temp_free(taddr); } static void gen_store_v10(DisasContext *dc, TCGv addr, TCGv val, unsigned int size) { - int mem_index = cpu_mmu_index(&dc->cpu->env, false); - /* If we get a fault on a delayslot we must keep the jmp state in the cpu-state to be able to re-execute the jmp. */ if (dc->delayed_branch == 1) { @@ -108,17 +99,11 @@ static void gen_store_v10(DisasContext *dc, TCGv addr, TCGv val, /* Conditional writes. */ if (dc->flags_x) { - gen_store_v10_conditional(dc, addr, val, size, mem_index); + gen_store_v10_conditional(dc, addr, val, size, dc->mem_index); return; } - if (size == 1) { - tcg_gen_qemu_st8(val, addr, mem_index); - } else if (size == 2) { - tcg_gen_qemu_st16(val, addr, mem_index); - } else { - tcg_gen_qemu_st32(val, addr, mem_index); - } + tcg_gen_qemu_st_tl(val, addr, dc->mem_index, ctz32(size) | MO_TE); } @@ -215,7 +200,6 @@ static int dec10_prep_move_m(CPUCRISState *env, DisasContext *dc, else t_gen_zext(dst, dst, memsize); insn_len += crisv10_post_memaddr(dc, memsize); - tcg_temp_free(addr); } if (dc->mode == CRISV10_MODE_INDIRECT && (dc->tb_flags & PFIX_FLAG)) { @@ -255,37 +239,33 @@ static unsigned int dec10_quick_imm(DisasContext *dc) LOG_DIS("moveq %d, $r%d\n", simm, dc->dst); cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_const_tl(simm); + c = tcg_constant_tl(simm); cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], cpu_R[dc->dst], c, 4); - tcg_temp_free(c); break; case CRISV10_QIMM_CMPQ: LOG_DIS("cmpq %d, $r%d\n", simm, dc->dst); cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_const_tl(simm); + c = tcg_constant_tl(simm); cris_alu(dc, CC_OP_CMP, cpu_R[dc->dst], cpu_R[dc->dst], c, 4); - tcg_temp_free(c); break; case CRISV10_QIMM_ADDQ: LOG_DIS("addq %d, $r%d\n", imm, dc->dst); cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_const_tl(imm); + c = tcg_constant_tl(imm); cris_alu(dc, CC_OP_ADD, cpu_R[dc->dst], cpu_R[dc->dst], c, 4); - tcg_temp_free(c); break; case CRISV10_QIMM_ANDQ: LOG_DIS("andq %d, $r%d\n", simm, dc->dst); cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_const_tl(simm); + c = tcg_constant_tl(simm); cris_alu(dc, CC_OP_AND, cpu_R[dc->dst], cpu_R[dc->dst], c, 4); - tcg_temp_free(c); break; case CRISV10_QIMM_ASHQ: LOG_DIS("ashq %d, $r%d\n", simm, dc->dst); @@ -293,17 +273,16 @@ static unsigned int dec10_quick_imm(DisasContext *dc) cris_cc_mask(dc, CC_MASK_NZVC); op = imm & (1 << 5); imm &= 0x1f; - c = tcg_const_tl(imm); + c = tcg_constant_tl(imm); if (op) { cris_alu(dc, CC_OP_ASR, cpu_R[dc->dst], cpu_R[dc->dst], c, 4); } else { /* BTST */ cris_update_cc_op(dc, CC_OP_FLAGS, 4); - gen_helper_btst(cpu_PR[PR_CCS], cpu_env, cpu_R[dc->dst], + gen_helper_btst(cpu_PR[PR_CCS], tcg_env, cpu_R[dc->dst], c, cpu_PR[PR_CCS]); } - tcg_temp_free(c); break; case CRISV10_QIMM_LSHQ: LOG_DIS("lshq %d, $r%d\n", simm, dc->dst); @@ -314,28 +293,25 @@ static unsigned int dec10_quick_imm(DisasContext *dc) } imm &= 0x1f; cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_const_tl(imm); + c = tcg_constant_tl(imm); cris_alu(dc, op, cpu_R[dc->dst], cpu_R[dc->dst], c, 4); - tcg_temp_free(c); break; case CRISV10_QIMM_SUBQ: LOG_DIS("subq %d, $r%d\n", imm, dc->dst); cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_const_tl(imm); + c = tcg_constant_tl(imm); cris_alu(dc, CC_OP_SUB, cpu_R[dc->dst], cpu_R[dc->dst], c, 4); - tcg_temp_free(c); break; case CRISV10_QIMM_ORQ: LOG_DIS("andq %d, $r%d\n", simm, dc->dst); cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_const_tl(simm); + c = tcg_constant_tl(simm); cris_alu(dc, CC_OP_OR, cpu_R[dc->dst], cpu_R[dc->dst], c, 4); - tcg_temp_free(c); break; case CRISV10_QIMM_BCC_R0: @@ -426,18 +402,15 @@ static void dec10_reg_alu(DisasContext *dc, int op, int size, int sext) assert(dc->dst != 15); cris_alu(dc, op, cpu_R[dc->dst], t[0], t[1], size); - tcg_temp_free(t[0]); - tcg_temp_free(t[1]); } static void dec10_reg_bound(DisasContext *dc, int size) { TCGv t; - t = tcg_temp_local_new(); + t = tcg_temp_new(); t_gen_zext(t, cpu_R[dc->src], size); cris_alu(dc, CC_OP_BOUND, cpu_R[dc->dst], cpu_R[dc->dst], t, 4); - tcg_temp_free(t); } static void dec10_reg_mul(DisasContext *dc, int size, int sext) @@ -451,9 +424,6 @@ static void dec10_reg_mul(DisasContext *dc, int size, int sext) t[0], t[1], cpu_R[dc->dst], cpu_R[dc->src]); cris_alu(dc, op, cpu_R[dc->dst], t[0], t[1], 4); - - tcg_temp_free(t[0]); - tcg_temp_free(t[1]); } @@ -472,7 +442,6 @@ static void dec10_reg_movs(DisasContext *dc) t_gen_zext(t, cpu_R[dc->src], size); cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], cpu_R[dc->dst], t, 4); - tcg_temp_free(t); } static void dec10_reg_alux(DisasContext *dc, int op) @@ -490,7 +459,6 @@ static void dec10_reg_alux(DisasContext *dc, int op) t_gen_zext(t, cpu_R[dc->src], size); cris_alu(dc, op, cpu_R[dc->dst], cpu_R[dc->dst], t, 4); - tcg_temp_free(t); } static void dec10_reg_mov_pr(DisasContext *dc) @@ -522,7 +490,6 @@ static void dec10_reg_abs(DisasContext *dc) tcg_gen_sub_tl(t0, cpu_R[dc->dst], t0); cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], cpu_R[dc->dst], t0, 4); - tcg_temp_free(t0); } static void dec10_reg_swap(DisasContext *dc) @@ -543,7 +510,6 @@ static void dec10_reg_swap(DisasContext *dc) if (dc->dst & 1) t_gen_swapr(t0, t0); cris_alu(dc, CC_OP_MOVE, cpu_R[dc->src], cpu_R[dc->src], t0, 4); - tcg_temp_free(t0); } static void dec10_reg_scc(DisasContext *dc) @@ -623,7 +589,6 @@ static unsigned int dec10_reg(DisasContext *dc) LOG_DIS("addi r%d r%d size=%d\n", dc->src, dc->dst, dc->size); tcg_gen_shli_tl(t, cpu_R[dc->dst], dc->size & 3); tcg_gen_add_tl(cpu_R[dc->src], cpu_R[dc->src], t); - tcg_temp_free(t); break; case CRISV10_REG_LSL: LOG_DIS("lsl $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); @@ -669,7 +634,6 @@ static unsigned int dec10_reg(DisasContext *dc) } else { tcg_gen_add_tl(cpu_PR[PR_PREFIX], cpu_R[dc->src], t); } - tcg_temp_free(t); cris_set_prefix(dc); break; @@ -730,7 +694,7 @@ static unsigned int dec10_reg(DisasContext *dc) LOG_DIS("btst $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); cris_cc_mask(dc, CC_MASK_NZVC); cris_update_cc_op(dc, CC_OP_FLAGS, 4); - gen_helper_btst(cpu_PR[PR_CCS], cpu_env, cpu_R[dc->dst], + gen_helper_btst(cpu_PR[PR_CCS], tcg_env, cpu_R[dc->dst], cpu_R[dc->src], cpu_PR[PR_CCS]); break; case CRISV10_REG_DSTEP: @@ -778,7 +742,6 @@ static unsigned int dec10_ind_move_m_r(CPUCRISState *env, DisasContext *dc, dc->delayed_branch = 1; } - tcg_temp_free(t); return insn_len; } @@ -792,7 +755,6 @@ static unsigned int dec10_ind_move_r_m(DisasContext *dc, unsigned int size) crisv10_prepare_memaddr(dc, addr, size); gen_store_v10(dc, addr, cpu_R[dc->dst], size); insn_len += crisv10_post_memaddr(dc, size); - tcg_temp_free(addr); return insn_len; } @@ -800,12 +762,11 @@ static unsigned int dec10_ind_move_r_m(DisasContext *dc, unsigned int size) static unsigned int dec10_ind_move_m_pr(CPUCRISState *env, DisasContext *dc) { unsigned int insn_len = 2, rd = dc->dst; - TCGv t, addr; + TCGv t; LOG_DIS("move.%d $p%d, [$r%d]\n", dc->size, dc->dst, dc->src); cris_lock_irq(dc); - addr = tcg_temp_new(); t = tcg_temp_new(); insn_len += dec10_prep_move_m(env, dc, 0, 4, t); if (rd == 15) { @@ -816,8 +777,6 @@ static unsigned int dec10_ind_move_m_pr(CPUCRISState *env, DisasContext *dc) tcg_gen_mov_tl(cpu_PR[rd], t); dc->cpustate_changed = 1; } - tcg_temp_free(addr); - tcg_temp_free(t); return insn_len; } @@ -835,12 +794,10 @@ static unsigned int dec10_ind_move_pr_m(DisasContext *dc) cris_evaluate_flags(dc); tcg_gen_andi_tl(t0, cpu_PR[PR_CCS], ~PFIX_FLAG); gen_store_v10(dc, addr, t0, size); - tcg_temp_free(t0); } else { gen_store_v10(dc, addr, cpu_PR[dc->dst], size); } insn_len += crisv10_post_memaddr(dc, size); - tcg_temp_free(addr); cris_lock_irq(dc); return insn_len; @@ -874,8 +831,6 @@ static void dec10_movem_r_m(DisasContext *dc) if (!pfix && dc->mode == CRISV10_MODE_AUTOINC) { tcg_gen_mov_tl(cpu_R[dc->src], addr); } - tcg_temp_free(addr); - tcg_temp_free(t0); } static void dec10_movem_m_r(DisasContext *dc) @@ -902,8 +857,6 @@ static void dec10_movem_m_r(DisasContext *dc) if (!pfix && dc->mode == CRISV10_MODE_AUTOINC) { tcg_gen_mov_tl(cpu_R[dc->src], addr); } - tcg_temp_free(addr); - tcg_temp_free(t0); } static int dec10_ind_alu(CPUCRISState *env, DisasContext *dc, @@ -922,9 +875,6 @@ static int dec10_ind_alu(CPUCRISState *env, DisasContext *dc, dc->delayed_branch = 1; return insn_len; } - - cris_alu_m_free_temps(t); - return insn_len; } @@ -935,7 +885,7 @@ static int dec10_ind_bound(CPUCRISState *env, DisasContext *dc, int rd = dc->dst; TCGv t; - t = tcg_temp_local_new(); + t = tcg_temp_new(); insn_len += dec10_prep_move_m(env, dc, 0, size, t); cris_alu(dc, CC_OP_BOUND, cpu_R[dc->dst], cpu_R[rd], t, 4); if (dc->dst == 15) { @@ -944,7 +894,6 @@ static int dec10_ind_bound(CPUCRISState *env, DisasContext *dc, dc->delayed_branch = 1; } - tcg_temp_free(t); return insn_len; } @@ -969,7 +918,6 @@ static int dec10_alux_m(CPUCRISState *env, DisasContext *dc, int op) dc->delayed_branch = 1; } - tcg_temp_free(t); return insn_len; } @@ -1054,11 +1002,9 @@ static unsigned int dec10_ind(CPUCRISState *env, DisasContext *dc) cris_alu_m_alloc_temps(t); insn_len += dec10_prep_move_m(env, dc, 0, size, t[0]); tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~3); - c = tcg_const_tl(0); + c = tcg_constant_tl(0); cris_alu(dc, CC_OP_CMP, cpu_R[dc->dst], t[0], c, size); - tcg_temp_free(c); - cris_alu_m_free_temps(t); break; case CRISV10_IND_ADD: LOG_DIS("add size=%d op=%d %d\n", size, dc->src, dc->dst); @@ -1153,9 +1099,8 @@ static unsigned int dec10_ind(CPUCRISState *env, DisasContext *dc) if (dc->mode == CRISV10_MODE_AUTOINC) insn_len += size; - c = tcg_const_tl(dc->pc + insn_len); + c = tcg_constant_tl(dc->pc + insn_len); t_gen_mov_preg_TN(dc, dc->dst, c); - tcg_temp_free(c); dc->jmp_pc = imm; cris_prepare_jmp(dc, JMP_DIRECT); dc->delayed_branch--; /* v10 has no dslot here. */ @@ -1164,9 +1109,8 @@ static unsigned int dec10_ind(CPUCRISState *env, DisasContext *dc) LOG_DIS("break %d\n", dc->src); cris_evaluate_flags(dc); tcg_gen_movi_tl(env_pc, dc->pc + 2); - c = tcg_const_tl(dc->src + 2); + c = tcg_constant_tl(dc->src + 2); t_gen_mov_env_TN(trap_vector, c); - tcg_temp_free(c); t_gen_raise_exception(EXCP_BREAK); dc->base.is_jmp = DISAS_NORETURN; return insn_len; @@ -1174,15 +1118,13 @@ static unsigned int dec10_ind(CPUCRISState *env, DisasContext *dc) LOG_DIS("%d: jump.%d %d r%d r%d\n", __LINE__, size, dc->opcode, dc->src, dc->dst); t[0] = tcg_temp_new(); - c = tcg_const_tl(dc->pc + insn_len); + c = tcg_constant_tl(dc->pc + insn_len); t_gen_mov_preg_TN(dc, dc->dst, c); - tcg_temp_free(c); crisv10_prepare_memaddr(dc, t[0], size); gen_load(dc, env_btarget, t[0], 4, 0); insn_len += crisv10_post_memaddr(dc, size); cris_prepare_jmp(dc, JMP_INDIRECT); dc->delayed_branch--; /* v10 has no dslot here. */ - tcg_temp_free(t[0]); } break; @@ -1199,9 +1141,8 @@ static unsigned int dec10_ind(CPUCRISState *env, DisasContext *dc) LOG_DIS("jmp pc=%x opcode=%d r%d r%d\n", dc->pc, dc->opcode, dc->dst, dc->src); tcg_gen_mov_tl(env_btarget, cpu_R[dc->src]); - c = tcg_const_tl(dc->pc + insn_len); + c = tcg_constant_tl(dc->pc + insn_len); t_gen_mov_preg_TN(dc, dc->dst, c); - tcg_temp_free(c); cris_prepare_jmp(dc, JMP_INDIRECT); dc->delayed_branch--; /* v10 has no dslot here. */ break; @@ -1292,41 +1233,41 @@ void cris_initialize_crisv10_tcg(void) { int i; - cc_x = tcg_global_mem_new(cpu_env, + cc_x = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, cc_x), "cc_x"); - cc_src = tcg_global_mem_new(cpu_env, + cc_src = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, cc_src), "cc_src"); - cc_dest = tcg_global_mem_new(cpu_env, + cc_dest = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, cc_dest), "cc_dest"); - cc_result = tcg_global_mem_new(cpu_env, + cc_result = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, cc_result), "cc_result"); - cc_op = tcg_global_mem_new(cpu_env, + cc_op = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, cc_op), "cc_op"); - cc_size = tcg_global_mem_new(cpu_env, + cc_size = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, cc_size), "cc_size"); - cc_mask = tcg_global_mem_new(cpu_env, + cc_mask = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, cc_mask), "cc_mask"); - env_pc = tcg_global_mem_new(cpu_env, + env_pc = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, pc), "pc"); - env_btarget = tcg_global_mem_new(cpu_env, + env_btarget = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, btarget), "btarget"); - env_btaken = tcg_global_mem_new(cpu_env, + env_btaken = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, btaken), "btaken"); for (i = 0; i < 16; i++) { - cpu_R[i] = tcg_global_mem_new(cpu_env, + cpu_R[i] = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, regs[i]), regnames_v10[i]); } for (i = 0; i < 16; i++) { - cpu_PR[i] = tcg_global_mem_new(cpu_env, + cpu_PR[i] = tcg_global_mem_new(tcg_env, offsetof(CPUCRISState, pregs[i]), pregnames_v10[i]); } diff --git a/target/hexagon/README b/target/hexagon/README index 372e24747c..746ebec378 100644 --- a/target/hexagon/README +++ b/target/hexagon/README @@ -4,10 +4,10 @@ is a wide vector coprocessor designed for high performance computer vision, image processing, machine learning, and other workloads. The following versions of the Hexagon core are supported - Scalar core: v67 - https://developer.qualcomm.com/downloads/qualcomm-hexagon-v67-programmer-s-reference-manual - HVX extension: v66 - https://developer.qualcomm.com/downloads/qualcomm-hexagon-v66-hvx-programmer-s-reference-manual + Scalar core: v73 + https://developer.qualcomm.com/downloads/qualcomm-hexagon-v73-programmers-reference-manual-rev-aa + HVX extension: v73 + https://developer.qualcomm.com/downloads/qualcomm-hexagon-v73-hvx-programmers-reference-manual-rev-aa We presented an overview of the project at the 2019 KVM Forum. https://kvmforum2019.sched.com/event/Tmwc/qemu-hexagon-automatic-translation-of-the-isa-manual-pseudcode-to-tiny-code-instructions-of-a-vliw-architecture-niccolo-izzo-revng-taylor-simpson-qualcomm-innovation-center @@ -27,6 +27,10 @@ Hexagon-specific code are encode*.def Encoding patterns for each instruction iclass.def Instruction class definitions used to determine legal VLIW slots for each instruction + qemu/target/hexagon/idef-parser + Parser that, given the high-level definitions of an instruction, + produces a C function generating equivalent tiny code instructions. + See README.rst. qemu/linux-user/hexagon Helpers for loading the ELF file and making Linux system calls, signals, etc @@ -47,6 +51,8 @@ header files in /target/hexagon gen_tcg_funcs.py -> tcg_funcs_generated.c.inc gen_tcg_func_table.py -> tcg_func_table_generated.c.inc gen_helper_funcs.py -> helper_funcs_generated.c.inc + gen_idef_parser_funcs.py -> idef_parser_input.h + gen_analyze_funcs.py -> analyze_funcs_generated.c.inc Qemu helper functions have 3 parts DEF_HELPER declaration indicates the signature of the helper @@ -76,14 +82,12 @@ tcg_funcs_generated.c.inc Insn *insn, Packet *pkt) { - TCGv RdV = tcg_temp_local_new(); + TCGv RdV = tcg_temp_new(); const int RdN = insn->regno[0]; TCGv RsV = hex_gpr[insn->regno[1]]; TCGv RtV = hex_gpr[insn->regno[2]]; - gen_helper_A2_add(RdV, cpu_env, RsV, RtV); - gen_log_reg_write(RdN, RdV); - ctx_log_reg_write(ctx, RdN); - tcg_temp_free(RdV); + gen_helper_A2_add(RdV, tcg_env, RsV, RtV); + gen_log_reg_write(ctx, RdN, RdV); } helper_funcs_generated.c.inc @@ -132,35 +136,25 @@ For HVX vectors, the generator behaves slightly differently. The wide vectors won't fit in a TCGv or TCGv_i64, so we pass TCGv_ptr variables to pass the address to helper functions. Here's an example for an HVX vector-add-word istruction. - static void generate_V6_vaddw( - CPUHexagonState *env, - DisasContext *ctx, - Insn *insn, - Packet *pkt) + static void generate_V6_vaddw(DisasContext *ctx) { + Insn *insn __attribute__((unused)) = ctx->insn; const int VdN = insn->regno[0]; const intptr_t VdV_off = ctx_future_vreg_off(ctx, VdN, 1, true); - TCGv_ptr VdV = tcg_temp_local_new_ptr(); - tcg_gen_addi_ptr(VdV, cpu_env, VdV_off); + TCGv_ptr VdV = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(VdV, tcg_env, VdV_off); const int VuN = insn->regno[1]; const intptr_t VuV_off = vreg_src_off(ctx, VuN); - TCGv_ptr VuV = tcg_temp_local_new_ptr(); + TCGv_ptr VuV = tcg_temp_new_ptr(); const int VvN = insn->regno[2]; const intptr_t VvV_off = vreg_src_off(ctx, VvN); - TCGv_ptr VvV = tcg_temp_local_new_ptr(); - tcg_gen_addi_ptr(VuV, cpu_env, VuV_off); - tcg_gen_addi_ptr(VvV, cpu_env, VvV_off); - TCGv slot = tcg_constant_tl(insn->slot); - gen_helper_V6_vaddw(cpu_env, VdV, VuV, VvV, slot); - tcg_temp_free(slot); - gen_log_vreg_write(ctx, VdV_off, VdN, EXT_DFL, insn->slot, false); - ctx_log_vreg_write(ctx, VdN, EXT_DFL, false); - tcg_temp_free_ptr(VdV); - tcg_temp_free_ptr(VuV); - tcg_temp_free_ptr(VvV); + TCGv_ptr VvV = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(VuV, tcg_env, VuV_off); + tcg_gen_addi_ptr(VvV, tcg_env, VvV_off); + gen_helper_V6_vaddw(tcg_env, VdV, VuV, VvV); } Notice that we also generate a variable named _off for each operand of @@ -173,12 +167,9 @@ functions from tcg-op-gvec.h. Here's the override for this instruction. Finally, we notice that the override doesn't use the TCGv_ptr variables, so we don't generate them when an override is present. Here is what we generate when the override is present. - static void generate_V6_vaddw( - CPUHexagonState *env, - DisasContext *ctx, - Insn *insn, - Packet *pkt) + static void generate_V6_vaddw(DisasContext *ctx) { + Insn *insn __attribute__((unused)) = ctx->insn; const int VdN = insn->regno[0]; const intptr_t VdV_off = ctx_future_vreg_off(ctx, VdN, 1, true); @@ -189,16 +180,26 @@ when the override is present. const intptr_t VvV_off = vreg_src_off(ctx, VvN); fGEN_TCG_V6_vaddw({ fHIDE(int i;) fVFOREACH(32, i) { VdV.w[i] = VuV.w[i] + VvV.w[i] ; } }); - gen_log_vreg_write(ctx, VdV_off, VdN, EXT_DFL, insn->slot, false); - ctx_log_vreg_write(ctx, VdN, EXT_DFL, false); } +We also generate an analyze_ function for each instruction. Currently, +these functions record the writes to registers by calling ctx_log_*. During +gen_start_packet, we invoke the analyze_ function for each instruction in +the packet, and we mark the implicit writes. After the analysis is performed, +we initialize the result register for each of the predicated assignments. + In addition to instruction semantics, we use a generator to create the decode -tree. This generation is also a two step process. The first step is to run -target/hexagon/gen_dectree_import.c to produce +tree. This generation is a four step process. +Step 1 is to run target/hexagon/gen_dectree_import.c to produce /target/hexagon/iset.py -This file is imported by target/hexagon/dectree.py to produce - /target/hexagon/dectree_generated.h.inc +Step 2 is to import iset.py into target/hexagon/gen_decodetree.py to produce + /target/hexagon/normal_decode_generated + /target/hexagon/hvx_decode_generated + /target/hexagon/subinsn_*_decode_generated +Step 3 is to process the above files with QEMU's decodetree.py to produce + /target/hexagon/decode_*_generated.c.inc +Step 4 is to import iset.py into target/hexagon/gen_trans_funcs.py to produce + /target/hexagon/decodetree_trans_funcs_generated.c.inc *** Key Files *** @@ -244,7 +245,7 @@ helper_funcs_generated.c.inc. There are also several helpers used for debugging VLIW packet semantics differ from serial semantics in that all input operands are read, then the operations are performed, then all the results are written. -For exmaple, this packet performs a swap of registers r0 and r1 +For example, this packet performs a swap of registers r0 and r1 { r0 = r1; r1 = r0 } Note that the result is different if the instructions are executed serially. @@ -277,10 +278,8 @@ For Hexagon Vector eXtensions (HVX), the following fields are used VRegs Vector registers future_VRegs Registers to be stored during packet commit tmp_VRegs Temporary registers *not* stored during commit - VRegs_updated Mask of predicated vector writes QRegs Q (vector predicate) registers future_QRegs Registers to be stored during packet commit - QRegs_updated Mask of predicated vector writes *** Debugging *** @@ -311,4 +310,4 @@ Here are some handy places to set breakpoints At the start of execution of a packet for a given PC br helper_debug_start_packet if env->gpr[41] == 0xdeadbeef At the end of execution of a packet for a given PC - br helper_debug_commit_end if env->this_PC == 0xdeadbeef + br helper_debug_commit_end if this_PC == 0xdeadbeef diff --git a/target/hexagon/arch.c b/target/hexagon/arch.c index da79b41c4d..d053d68487 100644 --- a/target/hexagon/arch.c +++ b/target/hexagon/arch.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -224,6 +224,7 @@ void arch_fpop_start(CPUHexagonState *env) void arch_fpop_end(CPUHexagonState *env) { + const bool pkt_need_commit = true; int flags = get_float_exception_flags(&env->fp_status); if (flags != 0) { SOFTFLOAT_TEST_FLAG(float_flag_inexact, FPINPF, FPINPE); diff --git a/target/hexagon/attribs_def.h.inc b/target/hexagon/attribs_def.h.inc index dc890a557f..87942d46f4 100644 --- a/target/hexagon/attribs_def.h.inc +++ b/target/hexagon/attribs_def.h.inc @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,8 +38,26 @@ DEF_ATTRIB(SUBINSN, "sub-instruction", "", "") /* Load and Store attributes */ DEF_ATTRIB(LOAD, "Loads from memory", "", "") DEF_ATTRIB(STORE, "Stores to memory", "", "") +DEF_ATTRIB(STOREIMMED, "Stores immed to memory", "", "") +DEF_ATTRIB(MEMSIZE_0B, "Memory width is 0 byte", "", "") +DEF_ATTRIB(MEMSIZE_1B, "Memory width is 1 byte", "", "") +DEF_ATTRIB(MEMSIZE_2B, "Memory width is 2 bytes", "", "") +DEF_ATTRIB(MEMSIZE_4B, "Memory width is 4 bytes", "", "") +DEF_ATTRIB(MEMSIZE_8B, "Memory width is 8 bytes", "", "") +DEF_ATTRIB(SCALAR_LOAD, "Load is scalar", "", "") +DEF_ATTRIB(SCALAR_STORE, "Store is scalar", "", "") +DEF_ATTRIB(REGWRSIZE_1B, "Memory width is 1 byte", "", "") +DEF_ATTRIB(REGWRSIZE_2B, "Memory width is 2 bytes", "", "") +DEF_ATTRIB(REGWRSIZE_4B, "Memory width is 4 bytes", "", "") +DEF_ATTRIB(REGWRSIZE_8B, "Memory width is 8 bytes", "", "") DEF_ATTRIB(MEMLIKE, "Memory-like instruction", "", "") DEF_ATTRIB(MEMLIKE_PACKET_RULES, "follows Memory-like packet rules", "", "") +DEF_ATTRIB(RELEASE, "Releases a lock", "", "") +DEF_ATTRIB(ACQUIRE, "Acquires a lock", "", "") + +DEF_ATTRIB(RLS_INNER, "Store release inner visibility", "", "") +DEF_ATTRIB(RLS_ALL_THREAD, "Store release among all threads", "", "") +DEF_ATTRIB(RLS_SAME_THREAD, "Store release with the same thread", "", "") /* V6 Vector attributes */ DEF_ATTRIB(CVI, "Executes on the HVX extension", "", "") @@ -51,26 +69,35 @@ DEF_ATTRIB(CVI_VP_VS, "Double vector permute/shft insn executes on HVX", "", "") DEF_ATTRIB(CVI_VX, "Multiply instruction executes on HVX", "", "") DEF_ATTRIB(CVI_VX_DV, "Double vector multiply insn executes on HVX", "", "") DEF_ATTRIB(CVI_VS, "Shift instruction executes on HVX", "", "") +DEF_ATTRIB(CVI_VS_3SRC, "This shift needs to borrow a source register", "", "") DEF_ATTRIB(CVI_VS_VX, "Permute/shift and multiply insn executes on HVX", "", "") DEF_ATTRIB(CVI_VA, "ALU instruction executes on HVX", "", "") DEF_ATTRIB(CVI_VA_DV, "Double vector alu instruction executes on HVX", "", "") DEF_ATTRIB(CVI_4SLOT, "Consumes all the vector execution resources", "", "") DEF_ATTRIB(CVI_TMP, "Transient Memory Load not written to register", "", "") +DEF_ATTRIB(CVI_REMAP, "Register Renaming not written to register file", "", "") DEF_ATTRIB(CVI_GATHER, "CVI Gather operation", "", "") DEF_ATTRIB(CVI_SCATTER, "CVI Scatter operation", "", "") DEF_ATTRIB(CVI_SCATTER_RELEASE, "CVI Store Release for scatter", "", "") DEF_ATTRIB(CVI_TMP_DST, "CVI instruction that doesn't write a register", "", "") DEF_ATTRIB(CVI_SLOT23, "Can execute in slot 2 or slot 3 (HVX)", "", "") +DEF_ATTRIB(VTCM_ALLBANK_ACCESS, "Allocates in all VTCM schedulers.", "", "") /* Change-of-flow attributes */ DEF_ATTRIB(JUMP, "Jump-type instruction", "", "") DEF_ATTRIB(INDIRECT, "Absolute register jump", "", "") DEF_ATTRIB(CALL, "Function call instruction", "", "") DEF_ATTRIB(COF, "Change-of-flow instruction", "", "") +DEF_ATTRIB(HINTED_COF, "This instruction is a hinted change-of-flow", "", "") DEF_ATTRIB(CONDEXEC, "May be cancelled by a predicate", "", "") DEF_ATTRIB(DOTNEWVALUE, "Uses a register value generated in this pkt", "", "") DEF_ATTRIB(NEWCMPJUMP, "Compound compare and jump", "", "") +DEF_ATTRIB(NVSTORE, "New-value store", "", "") +DEF_ATTRIB(MEMOP, "memop", "", "") + +DEF_ATTRIB(ROPS_2, "Compound instruction worth 2 RISC-ops", "", "") +DEF_ATTRIB(ROPS_3, "Compound instruction worth 3 RISC-ops", "", "") /* access to implicit registers */ DEF_ATTRIB(IMPLICIT_WRITES_LR, "Writes the link register", "", "UREG.LR") @@ -85,8 +112,14 @@ DEF_ATTRIB(IMPLICIT_WRITES_P1, "Writes Predicate 1", "", "UREG.P1") DEF_ATTRIB(IMPLICIT_WRITES_P2, "Writes Predicate 1", "", "UREG.P2") DEF_ATTRIB(IMPLICIT_WRITES_P3, "May write Predicate 3", "", "UREG.P3") DEF_ATTRIB(IMPLICIT_READS_PC, "Reads the PC register", "", "") +DEF_ATTRIB(IMPLICIT_READS_P0, "Reads the P0 register", "", "") +DEF_ATTRIB(IMPLICIT_READS_P1, "Reads the P1 register", "", "") +DEF_ATTRIB(IMPLICIT_READS_P2, "Reads the P2 register", "", "") +DEF_ATTRIB(IMPLICIT_READS_P3, "Reads the P3 register", "", "") DEF_ATTRIB(IMPLICIT_WRITES_USR, "May write USR", "", "") -DEF_ATTRIB(WRITES_PRED_REG, "Writes a predicate register", "", "") +DEF_ATTRIB(COMMUTES, "The operation is communitive", "", "") +DEF_ATTRIB(DEALLOCRET, "dealloc_return", "", "") +DEF_ATTRIB(DEALLOCFRAME, "deallocframe", "", "") DEF_ATTRIB(CRSLOT23, "Can execute in slot 2 or slot 3 (CR)", "", "") DEF_ATTRIB(IT_NOP, "nop instruction", "", "") @@ -94,17 +127,21 @@ DEF_ATTRIB(IT_EXTENDER, "constant extender instruction", "", "") /* Restrictions to make note of */ +DEF_ATTRIB(RESTRICT_COF_MAX1, "One change-of-flow per packet", "", "") +DEF_ATTRIB(RESTRICT_NOPACKET, "Not allowed in a packet", "", "") DEF_ATTRIB(RESTRICT_SLOT0ONLY, "Must execute on slot0", "", "") DEF_ATTRIB(RESTRICT_SLOT1ONLY, "Must execute on slot1", "", "") DEF_ATTRIB(RESTRICT_SLOT2ONLY, "Must execute on slot2", "", "") DEF_ATTRIB(RESTRICT_SLOT3ONLY, "Must execute on slot3", "", "") DEF_ATTRIB(RESTRICT_NOSLOT1, "No slot 1 instruction in parallel", "", "") DEF_ATTRIB(RESTRICT_PREFERSLOT0, "Try to encode into slot 0", "", "") +DEF_ATTRIB(RESTRICT_PACKET_AXOK, "May exist with A-type or X-type", "", "") DEF_ATTRIB(ICOP, "Instruction cache op", "", "") DEF_ATTRIB(HWLOOP0_END, "Ends HW loop0", "", "") DEF_ATTRIB(HWLOOP1_END, "Ends HW loop1", "", "") +DEF_ATTRIB(RET_TYPE, "return type", "", "") DEF_ATTRIB(DCZEROA, "dczeroa type", "", "") DEF_ATTRIB(ICFLUSHOP, "icflush op type", "", "") DEF_ATTRIB(DCFLUSHOP, "dcflush op type", "", "") @@ -116,5 +153,24 @@ DEF_ATTRIB(L2FETCH, "Instruction is l2fetch type", "", "") DEF_ATTRIB(ICINVA, "icinva", "", "") DEF_ATTRIB(DCCLEANINVA, "dccleaninva", "", "") +DEF_ATTRIB(NO_INTRINSIC, "Don't generate an intrisic", "", "") + +/* Documentation Notes */ +DEF_ATTRIB(NOTE_CONDITIONAL, "can be conditionally executed", "", "") +DEF_ATTRIB(NOTE_NEWVAL_SLOT0, "New-value oprnd must execute on slot 0", "", "") +DEF_ATTRIB(NOTE_PRIV, "Monitor-level feature", "", "") +DEF_ATTRIB(NOTE_NOPACKET, "solo instruction", "", "") +DEF_ATTRIB(NOTE_AXOK, "May only be grouped with ALU32 or non-FP XTYPE.", "", "") +DEF_ATTRIB(NOTE_LATEPRED, "The predicate can not be used as a .new", "", "") +DEF_ATTRIB(NOTE_NVSLOT0, "Can execute only in slot 0 (ST)", "", "") +DEF_ATTRIB(NOTE_NOVP, "Cannot be paired with a HVX permute instruction", "", "") +DEF_ATTRIB(NOTE_VA_UNARY, "Combined with HVX ALU op (must be unary)", "", "") + +/* V6 MMVector Notes for Documentation */ +DEF_ATTRIB(NOTE_SHIFT_RESOURCE, "Uses the HVX shift resource.", "", "") +/* Restrictions to make note of */ +DEF_ATTRIB(RESTRICT_NOSLOT1_STORE, "Packet must not have slot 1 store", "", "") +DEF_ATTRIB(RESTRICT_LATEPRED, "Predicate can not be used as a .new.", "", "") + /* Keep this as the last attribute: */ DEF_ATTRIB(ZZ_LASTATTRIB, "Last attribute in the file", "", "") diff --git a/target/hexagon/cpu-param.h b/target/hexagon/cpu-param.h index e8ed5468d9..71b4a9b83e 100644 --- a/target/hexagon/cpu-param.h +++ b/target/hexagon/cpu-param.h @@ -24,6 +24,4 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 36 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -#define NB_MMU_MODES 1 - #endif diff --git a/target/hexagon/cpu-qom.h b/target/hexagon/cpu-qom.h new file mode 100644 index 0000000000..da92fe7468 --- /dev/null +++ b/target/hexagon/cpu-qom.h @@ -0,0 +1,27 @@ +/* + * QEMU Hexagon CPU QOM header (target agnostic) + * + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_HEXAGON_CPU_QOM_H +#define QEMU_HEXAGON_CPU_QOM_H + +#include "hw/core/cpu.h" + +#define TYPE_HEXAGON_CPU "hexagon-cpu" + +#define HEXAGON_CPU_TYPE_SUFFIX "-" TYPE_HEXAGON_CPU +#define HEXAGON_CPU_TYPE_NAME(name) (name HEXAGON_CPU_TYPE_SUFFIX) + +#define TYPE_HEXAGON_CPU_V67 HEXAGON_CPU_TYPE_NAME("v67") +#define TYPE_HEXAGON_CPU_V68 HEXAGON_CPU_TYPE_NAME("v68") +#define TYPE_HEXAGON_CPU_V69 HEXAGON_CPU_TYPE_NAME("v69") +#define TYPE_HEXAGON_CPU_V71 HEXAGON_CPU_TYPE_NAME("v71") +#define TYPE_HEXAGON_CPU_V73 HEXAGON_CPU_TYPE_NAME("v73") + +OBJECT_DECLARE_CPU_TYPE(HexagonCPU, HexagonCPUClass, HEXAGON_CPU) + +#endif diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index fa9bd702d6..3a716b9be3 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,10 +23,14 @@ #include "qapi/error.h" #include "hw/qdev-properties.h" #include "fpu/softfloat-helpers.h" +#include "tcg/tcg.h" +#include "exec/gdbstub.h" -static void hexagon_v67_cpu_init(Object *obj) -{ -} +static void hexagon_v67_cpu_init(Object *obj) { } +static void hexagon_v68_cpu_init(Object *obj) { } +static void hexagon_v69_cpu_init(Object *obj) { } +static void hexagon_v71_cpu_init(Object *obj) { } +static void hexagon_v73_cpu_init(Object *obj) { } static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model) { @@ -39,10 +43,7 @@ static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model) oc = object_class_by_name(typename); g_strfreev(cpuname); g_free(typename); - if (!oc || !object_class_dynamic_cast(oc, TYPE_HEXAGON_CPU) || - object_class_is_abstract(oc)) { - return NULL; - } + return oc; } @@ -51,6 +52,8 @@ static Property hexagon_lldb_compat_property = static Property hexagon_lldb_stack_adjust_property = DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, 0, qdev_prop_uint32, target_ulong); +static Property hexagon_short_circuit_property = + DEFINE_PROP_BOOL("short-circuit", HexagonCPU, short_circuit, true); const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", @@ -86,7 +89,7 @@ static target_ulong adjust_stack_ptrs(CPUHexagonState *env, target_ulong addr) return addr; } -/* HEX_REG_P3_0 (aka C4) is an alias for the predicate registers */ +/* HEX_REG_P3_0_ALIASED (aka C4) is an alias for the predicate registers */ static target_ulong read_p3_0(CPUHexagonState *env) { int32_t control_reg = 0; @@ -102,7 +105,7 @@ static void print_reg(FILE *f, CPUHexagonState *env, int regnum) { target_ulong value; - if (regnum == HEX_REG_P3_0) { + if (regnum == HEX_REG_P3_0_ALIASED) { value = read_p3_0(env); } else { value = regnum < 32 ? adjust_stack_ptrs(env, env->gpr[regnum]) @@ -198,7 +201,7 @@ static void hexagon_dump(CPUHexagonState *env, FILE *f, int flags) print_reg(f, env, HEX_REG_M0); print_reg(f, env, HEX_REG_M1); print_reg(f, env, HEX_REG_USR); - print_reg(f, env, HEX_REG_P3_0); + print_reg(f, env, HEX_REG_P3_0_ALIASED); print_reg(f, env, HEX_REG_GP); print_reg(f, env, HEX_REG_UGP); print_reg(f, env, HEX_REG_PC); @@ -233,10 +236,7 @@ static void hexagon_dump(CPUHexagonState *env, FILE *f, int flags) static void hexagon_dump_state(CPUState *cs, FILE *f, int flags) { - HexagonCPU *cpu = HEXAGON_CPU(cs); - CPUHexagonState *env = &cpu->env; - - hexagon_dump(env, f, flags); + hexagon_dump(cpu_env(cs), f, flags); } void hexagon_debug(CPUHexagonState *env) @@ -246,17 +246,19 @@ void hexagon_debug(CPUHexagonState *env) static void hexagon_cpu_set_pc(CPUState *cs, vaddr value) { - HexagonCPU *cpu = HEXAGON_CPU(cs); - CPUHexagonState *env = &cpu->env; - env->gpr[HEX_REG_PC] = value; + cpu_env(cs)->gpr[HEX_REG_PC] = value; +} + +static vaddr hexagon_cpu_get_pc(CPUState *cs) +{ + return cpu_env(cs)->gpr[HEX_REG_PC]; } static void hexagon_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - HexagonCPU *cpu = HEXAGON_CPU(cs); - CPUHexagonState *env = &cpu->env; - env->gpr[HEX_REG_PC] = tb->pc; + tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + cpu_env(cs)->gpr[HEX_REG_PC] = tb->pc; } static bool hexagon_cpu_has_work(CPUState *cs) @@ -264,20 +266,22 @@ static bool hexagon_cpu_has_work(CPUState *cs) return true; } -void restore_state_to_opc(CPUHexagonState *env, TranslationBlock *tb, - target_ulong *data) +static void hexagon_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) { - env->gpr[HEX_REG_PC] = data[0]; + cpu_env(cs)->gpr[HEX_REG_PC] = data[0]; } -static void hexagon_cpu_reset(DeviceState *dev) +static void hexagon_cpu_reset_hold(Object *obj) { - CPUState *cs = CPU(dev); - HexagonCPU *cpu = HEXAGON_CPU(cs); - HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(cpu); - CPUHexagonState *env = &cpu->env; + CPUState *cs = CPU(obj); + HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(obj); + CPUHexagonState *env = cpu_env(cs); - mcc->parent_reset(dev); + if (mcc->parent_phases.hold) { + mcc->parent_phases.hold(obj); + } set_default_nan_mode(1, &env->fp_status); set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status); @@ -300,6 +304,10 @@ static void hexagon_cpu_realize(DeviceState *dev, Error **errp) return; } + gdb_register_coprocessor(cs, hexagon_hvx_gdb_read_register, + hexagon_hvx_gdb_write_register, + gdb_find_static_feature("hexagon-hvx.xml"), 0); + qemu_init_vcpu(cs); cpu_reset(cs); @@ -308,18 +316,17 @@ static void hexagon_cpu_realize(DeviceState *dev, Error **errp) static void hexagon_cpu_init(Object *obj) { - HexagonCPU *cpu = HEXAGON_CPU(obj); - - cpu_set_cpustate_pointers(cpu); qdev_property_add_static(DEVICE(obj), &hexagon_lldb_compat_property); qdev_property_add_static(DEVICE(obj), &hexagon_lldb_stack_adjust_property); + qdev_property_add_static(DEVICE(obj), &hexagon_short_circuit_property); } #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps hexagon_tcg_ops = { +static const TCGCPUOps hexagon_tcg_ops = { .initialize = hexagon_translate_init, .synchronize_from_tb = hexagon_cpu_synchronize_from_tb, + .restore_state_to_opc = hexagon_restore_state_to_opc, }; static void hexagon_cpu_class_init(ObjectClass *c, void *data) @@ -327,20 +334,23 @@ static void hexagon_cpu_class_init(ObjectClass *c, void *data) HexagonCPUClass *mcc = HEXAGON_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); DeviceClass *dc = DEVICE_CLASS(c); + ResettableClass *rc = RESETTABLE_CLASS(c); device_class_set_parent_realize(dc, hexagon_cpu_realize, &mcc->parent_realize); - device_class_set_parent_reset(dc, hexagon_cpu_reset, &mcc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, hexagon_cpu_reset_hold, NULL, + &mcc->parent_phases); cc->class_by_name = hexagon_cpu_class_by_name; cc->has_work = hexagon_cpu_has_work; cc->dump_state = hexagon_dump_state; cc->set_pc = hexagon_cpu_set_pc; + cc->get_pc = hexagon_cpu_get_pc; cc->gdb_read_register = hexagon_gdb_read_register; cc->gdb_write_register = hexagon_gdb_write_register; - cc->gdb_num_core_regs = TOTAL_PER_THREAD_REGS + NUM_VREGS + NUM_QREGS; cc->gdb_stop_before_watchpoint = true; + cc->gdb_core_xml_file = "hexagon-core.xml"; cc->disas_set_info = hexagon_cpu_disas_set_info; cc->tcg_ops = &hexagon_tcg_ops; } @@ -357,12 +367,17 @@ static const TypeInfo hexagon_cpu_type_infos[] = { .name = TYPE_HEXAGON_CPU, .parent = TYPE_CPU, .instance_size = sizeof(HexagonCPU), + .instance_align = __alignof(HexagonCPU), .instance_init = hexagon_cpu_init, .abstract = true, .class_size = sizeof(HexagonCPUClass), .class_init = hexagon_cpu_class_init, }, DEFINE_CPU(TYPE_HEXAGON_CPU_V67, hexagon_v67_cpu_init), + DEFINE_CPU(TYPE_HEXAGON_CPU_V68, hexagon_v68_cpu_init), + DEFINE_CPU(TYPE_HEXAGON_CPU_V69, hexagon_v69_cpu_init), + DEFINE_CPU(TYPE_HEXAGON_CPU_V71, hexagon_v71_cpu_init), + DEFINE_CPU(TYPE_HEXAGON_CPU_V73, hexagon_v73_cpu_init), }; DEFINE_TYPES(hexagon_cpu_type_infos) diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 2a65a57bab..3eef58fe8f 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,11 +20,11 @@ #include "fpu/softfloat-types.h" +#include "cpu-qom.h" #include "exec/cpu-defs.h" #include "hex_regs.h" #include "mmvec/mmvec.h" -#include "qom/object.h" -#include "hw/core/cpu.h" +#include "hw/registerfields.h" #define NUM_PREGS 4 #define TOTAL_PER_THREAD_REGS 64 @@ -35,14 +35,8 @@ #define PRED_WRITES_MAX 5 /* 4 insns + endloop */ #define VSTORES_MAX 2 -#define TYPE_HEXAGON_CPU "hexagon-cpu" - -#define HEXAGON_CPU_TYPE_SUFFIX "-" TYPE_HEXAGON_CPU -#define HEXAGON_CPU_TYPE_NAME(name) (name HEXAGON_CPU_TYPE_SUFFIX) #define CPU_RESOLVING_TYPE TYPE_HEXAGON_CPU -#define TYPE_HEXAGON_CPU_V67 HEXAGON_CPU_TYPE_NAME("v67") - #define MMU_USER_IDX 0 typedef struct { @@ -77,29 +71,21 @@ typedef struct { typedef struct CPUArchState { target_ulong gpr[TOTAL_PER_THREAD_REGS]; target_ulong pred[NUM_PREGS]; - target_ulong branch_taken; - target_ulong next_PC; /* For comparing with LLDB on target - see adjust_stack_ptrs function */ target_ulong last_pc_dumped; target_ulong stack_start; uint8_t slot_cancelled; - target_ulong new_value[TOTAL_PER_THREAD_REGS]; + target_ulong new_value_usr; /* * Only used when HEX_DEBUG is on, but unconditionally included * to reduce recompile time when turning HEX_DEBUG on/off. */ - target_ulong this_PC; target_ulong reg_written[TOTAL_PER_THREAD_REGS]; - target_ulong new_pred_value[NUM_PREGS]; - target_ulong pred_written; - MemLog mem_log_stores[STORES_MAX]; - target_ulong pkt_has_store_s1; - target_ulong dczero_addr; float_status fp_status; @@ -111,11 +97,8 @@ typedef struct CPUArchState { MMVector future_VRegs[VECTOR_TEMPS_MAX] QEMU_ALIGNED(16); MMVector tmp_VRegs[VECTOR_TEMPS_MAX] QEMU_ALIGNED(16); - VRegMask VRegs_updated; - MMQReg QRegs[NUM_QREGS] QEMU_ALIGNED(16); MMQReg future_QRegs[NUM_QREGS] QEMU_ALIGNED(16); - QRegMask QRegs_updated; /* Temporaries used within instructions */ MMVectorPair VuuV QEMU_ALIGNED(16); @@ -130,48 +113,37 @@ typedef struct CPUArchState { VTCMStoreLog vtcm_log; } CPUHexagonState; -OBJECT_DECLARE_CPU_TYPE(HexagonCPU, HexagonCPUClass, HEXAGON_CPU) - typedef struct HexagonCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ + DeviceRealize parent_realize; - DeviceReset parent_reset; + ResettablePhases parent_phases; } HexagonCPUClass; struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; + CPUHexagonState env; bool lldb_compat; target_ulong lldb_stack_adjust; + bool short_circuit; }; #include "cpu_bits.h" -static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) +FIELD(TB_FLAGS, IS_TIGHT_LOOP, 0, 1) + +static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { + uint32_t hex_flags = 0; *pc = env->gpr[HEX_REG_PC]; *cs_base = 0; -#ifdef CONFIG_USER_ONLY - *flags = 0; -#else -#error System mode not supported on Hexagon yet -#endif -} - -static inline int cpu_mmu_index(CPUHexagonState *env, bool ifetch) -{ -#ifdef CONFIG_USER_ONLY - return MMU_USER_IDX; -#else -#error System mode not supported on Hexagon yet -#endif + if (*pc == env->gpr[HEX_REG_SA0]) { + hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP, 1); + } + *flags = hex_flags; } typedef HexagonCPU ArchCPU; diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index 6f0f27b4ba..a40210ca1e 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,174 +52,41 @@ DEF_REGMAP(R_8, 8, 0, 1, 2, 3, 4, 5, 6, 7) #define DECODE_MAPPED_REG(OPNUM, NAME) \ insn->regno[OPNUM] = DECODE_REGISTER_##NAME[insn->regno[OPNUM]]; -typedef struct { - const struct DectreeTable *table_link; - const struct DectreeTable *table_link_b; - Opcode opcode; - enum { - DECTREE_ENTRY_INVALID, - DECTREE_TABLE_LINK, - DECTREE_SUBINSNS, - DECTREE_EXTSPACE, - DECTREE_TERMINAL - } type; -} DectreeEntry; +/* Helper functions for decode_*_generated.c.inc */ +#define DECODE_MAPPED(NAME) \ +static int decode_mapped_reg_##NAME(DisasContext *ctx, int x) \ +{ \ + return DECODE_REGISTER_##NAME[x]; \ +} +DECODE_MAPPED(R_16) +DECODE_MAPPED(R_8) +DECODE_MAPPED(R__8) -typedef struct DectreeTable { - unsigned int (*lookup_function)(int startbit, int width, uint32_t opcode); - unsigned int size; - unsigned int startbit; - unsigned int width; - const DectreeEntry table[]; -} DectreeTable; - -#define DECODE_NEW_TABLE(TAG, SIZE, WHATNOT) \ - static const DectreeTable dectree_table_##TAG; -#define TABLE_LINK(TABLE) /* NOTHING */ -#define TERMINAL(TAG, ENC) /* NOTHING */ -#define SUBINSNS(TAG, CLASSA, CLASSB, ENC) /* NOTHING */ -#define EXTSPACE(TAG, ENC) /* NOTHING */ -#define INVALID() /* NOTHING */ -#define DECODE_END_TABLE(...) /* NOTHING */ -#define DECODE_MATCH_INFO(...) /* NOTHING */ -#define DECODE_LEGACY_MATCH_INFO(...) /* NOTHING */ -#define DECODE_OPINFO(...) /* NOTHING */ - -#include "dectree_generated.h.inc" - -#undef DECODE_OPINFO -#undef DECODE_MATCH_INFO -#undef DECODE_LEGACY_MATCH_INFO -#undef DECODE_END_TABLE -#undef INVALID -#undef TERMINAL -#undef SUBINSNS -#undef EXTSPACE -#undef TABLE_LINK -#undef DECODE_NEW_TABLE -#undef DECODE_SEPARATOR_BITS - -#define DECODE_SEPARATOR_BITS(START, WIDTH) NULL, START, WIDTH -#define DECODE_NEW_TABLE_HELPER(TAG, SIZE, FN, START, WIDTH) \ - static const DectreeTable dectree_table_##TAG = { \ - .size = SIZE, \ - .lookup_function = FN, \ - .startbit = START, \ - .width = WIDTH, \ - .table = { -#define DECODE_NEW_TABLE(TAG, SIZE, WHATNOT) \ - DECODE_NEW_TABLE_HELPER(TAG, SIZE, WHATNOT) - -#define TABLE_LINK(TABLE) \ - { .type = DECTREE_TABLE_LINK, .table_link = &dectree_table_##TABLE }, -#define TERMINAL(TAG, ENC) \ - { .type = DECTREE_TERMINAL, .opcode = TAG }, -#define SUBINSNS(TAG, CLASSA, CLASSB, ENC) \ - { \ - .type = DECTREE_SUBINSNS, \ - .table_link = &dectree_table_DECODE_SUBINSN_##CLASSA, \ - .table_link_b = &dectree_table_DECODE_SUBINSN_##CLASSB \ - }, -#define EXTSPACE(TAG, ENC) { .type = DECTREE_EXTSPACE }, -#define INVALID() { .type = DECTREE_ENTRY_INVALID, .opcode = XX_LAST_OPCODE }, - -#define DECODE_END_TABLE(...) } }; - -#define DECODE_MATCH_INFO(...) /* NOTHING */ -#define DECODE_LEGACY_MATCH_INFO(...) /* NOTHING */ -#define DECODE_OPINFO(...) /* NOTHING */ - -#include "dectree_generated.h.inc" - -#undef DECODE_OPINFO -#undef DECODE_MATCH_INFO -#undef DECODE_LEGACY_MATCH_INFO -#undef DECODE_END_TABLE -#undef INVALID -#undef TERMINAL -#undef SUBINSNS -#undef EXTSPACE -#undef TABLE_LINK -#undef DECODE_NEW_TABLE -#undef DECODE_NEW_TABLE_HELPER -#undef DECODE_SEPARATOR_BITS - -static const DectreeTable dectree_table_DECODE_EXT_EXT_noext = { - .size = 1, .lookup_function = NULL, .startbit = 0, .width = 0, - .table = { - { .type = DECTREE_ENTRY_INVALID, .opcode = XX_LAST_OPCODE }, - } -}; - -static const DectreeTable *ext_trees[XX_LAST_EXT_IDX]; - -static void decode_ext_init(void) +/* Helper function for decodetree_trans_funcs_generated.c.inc */ +static int shift_left(DisasContext *ctx, int x, int n, int immno) { - int i; - for (i = EXT_IDX_noext; i < EXT_IDX_noext_AFTER; i++) { - ext_trees[i] = &dectree_table_DECODE_EXT_EXT_noext; - } - for (i = EXT_IDX_mmvec; i < EXT_IDX_mmvec_AFTER; i++) { - ext_trees[i] = &dectree_table_DECODE_EXT_EXT_mmvec; + int ret = x; + Insn *insn = ctx->insn; + if (!insn->extension_valid || + insn->which_extended != immno) { + ret <<= n; } + return ret; } -typedef struct { - uint32_t mask; - uint32_t match; -} DecodeITableEntry; +/* Include the generated decoder for 32 bit insn */ +#include "decode_normal_generated.c.inc" +#include "decode_hvx_generated.c.inc" -#define DECODE_NEW_TABLE(TAG, SIZE, WHATNOT) /* NOTHING */ -#define TABLE_LINK(TABLE) /* NOTHING */ -#define TERMINAL(TAG, ENC) /* NOTHING */ -#define SUBINSNS(TAG, CLASSA, CLASSB, ENC) /* NOTHING */ -#define EXTSPACE(TAG, ENC) /* NOTHING */ -#define INVALID() /* NOTHING */ -#define DECODE_END_TABLE(...) /* NOTHING */ -#define DECODE_OPINFO(...) /* NOTHING */ +/* Include the generated decoder for 16 bit insn */ +#include "decode_subinsn_a_generated.c.inc" +#include "decode_subinsn_l1_generated.c.inc" +#include "decode_subinsn_l2_generated.c.inc" +#include "decode_subinsn_s1_generated.c.inc" +#include "decode_subinsn_s2_generated.c.inc" -#define DECODE_MATCH_INFO_NORMAL(TAG, MASK, MATCH) \ - [TAG] = { \ - .mask = MASK, \ - .match = MATCH, \ - }, - -#define DECODE_MATCH_INFO_NULL(TAG, MASK, MATCH) \ - [TAG] = { .match = ~0 }, - -#define DECODE_MATCH_INFO(...) DECODE_MATCH_INFO_NORMAL(__VA_ARGS__) -#define DECODE_LEGACY_MATCH_INFO(...) /* NOTHING */ - -static const DecodeITableEntry decode_itable[XX_LAST_OPCODE] = { -#include "dectree_generated.h.inc" -}; - -#undef DECODE_MATCH_INFO -#define DECODE_MATCH_INFO(...) DECODE_MATCH_INFO_NULL(__VA_ARGS__) - -#undef DECODE_LEGACY_MATCH_INFO -#define DECODE_LEGACY_MATCH_INFO(...) DECODE_MATCH_INFO_NORMAL(__VA_ARGS__) - -static const DecodeITableEntry decode_legacy_itable[XX_LAST_OPCODE] = { -#include "dectree_generated.h.inc" -}; - -#undef DECODE_OPINFO -#undef DECODE_MATCH_INFO -#undef DECODE_LEGACY_MATCH_INFO -#undef DECODE_END_TABLE -#undef INVALID -#undef TERMINAL -#undef SUBINSNS -#undef EXTSPACE -#undef TABLE_LINK -#undef DECODE_NEW_TABLE -#undef DECODE_SEPARATOR_BITS - -void decode_init(void) -{ - decode_ext_init(); -} +/* Include the generated helpers for the decoder */ +#include "decodetree_trans_funcs_generated.c.inc" void decode_send_insn_to(Packet *packet, int start, int newloc) { @@ -388,6 +255,7 @@ static void decode_set_insn_attr_fields(Packet *pkt) uint16_t opcode; pkt->pkt_has_cof = false; + pkt->pkt_has_multi_cof = false; pkt->pkt_has_endloop = false; pkt->pkt_has_dczeroa = false; @@ -402,20 +270,33 @@ static void decode_set_insn_attr_fields(Packet *pkt) } if (GET_ATTRIB(opcode, A_STORE)) { - if (pkt->insn[i].slot == 0) { - pkt->pkt_has_store_s0 = true; - } else { - pkt->pkt_has_store_s1 = true; + if (GET_ATTRIB(opcode, A_SCALAR_STORE) && + !GET_ATTRIB(opcode, A_MEMSIZE_0B)) { + if (pkt->insn[i].slot == 0) { + pkt->pkt_has_store_s0 = true; + } else { + pkt->pkt_has_store_s1 = true; + } } } - pkt->pkt_has_cof |= decode_opcode_can_jump(opcode); + if (decode_opcode_can_jump(opcode)) { + if (pkt->pkt_has_cof) { + pkt->pkt_has_multi_cof = true; + } + pkt->pkt_has_cof = true; + } pkt->insn[i].is_endloop = decode_opcode_ends_loop(opcode); pkt->pkt_has_endloop |= pkt->insn[i].is_endloop; - pkt->pkt_has_cof |= pkt->pkt_has_endloop; + if (pkt->pkt_has_endloop) { + if (pkt->pkt_has_cof) { + pkt->pkt_has_multi_cof = true; + } + pkt->pkt_has_cof = true; + } } } @@ -536,7 +417,7 @@ apply_extender(Packet *pkt, int i, uint32_t extender) int immed_num; uint32_t base_immed; - immed_num = opcode_which_immediate_is_extended(pkt->insn[i].opcode); + immed_num = pkt->insn[i].which_extended; base_immed = pkt->insn[i].immed[immed_num]; pkt->insn[i].immed[immed_num] = extender | fZXTN(6, 32, base_immed); @@ -579,188 +460,98 @@ static SlotMask get_valid_slots(const Packet *pkt, unsigned int slot) } } -#define DECODE_NEW_TABLE(TAG, SIZE, WHATNOT) /* NOTHING */ -#define TABLE_LINK(TABLE) /* NOTHING */ -#define TERMINAL(TAG, ENC) /* NOTHING */ -#define SUBINSNS(TAG, CLASSA, CLASSB, ENC) /* NOTHING */ -#define EXTSPACE(TAG, ENC) /* NOTHING */ -#define INVALID() /* NOTHING */ -#define DECODE_END_TABLE(...) /* NOTHING */ -#define DECODE_MATCH_INFO(...) /* NOTHING */ -#define DECODE_LEGACY_MATCH_INFO(...) /* NOTHING */ - -#define DECODE_REG(REGNO, WIDTH, STARTBIT) \ - insn->regno[REGNO] = ((encoding >> STARTBIT) & ((1 << WIDTH) - 1)); - -#define DECODE_IMPL_REG(REGNO, VAL) \ - insn->regno[REGNO] = VAL; - -#define DECODE_IMM(IMMNO, WIDTH, STARTBIT, VALSTART) \ - insn->immed[IMMNO] |= (((encoding >> STARTBIT) & ((1 << WIDTH) - 1))) << \ - (VALSTART); - -#define DECODE_IMM_SXT(IMMNO, WIDTH) \ - insn->immed[IMMNO] = ((((int32_t)insn->immed[IMMNO]) << (32 - WIDTH)) >> \ - (32 - WIDTH)); - -#define DECODE_IMM_NEG(IMMNO, WIDTH) \ - insn->immed[IMMNO] = -insn->immed[IMMNO]; - -#define DECODE_IMM_SHIFT(IMMNO, SHAMT) \ - if ((!insn->extension_valid) || \ - (insn->which_extended != IMMNO)) { \ - insn->immed[IMMNO] <<= SHAMT; \ - } - -#define DECODE_OPINFO(TAG, BEH) \ - case TAG: \ - { BEH } \ - break; \ +/* + * Section 10.3 of the Hexagon V73 Programmer's Reference Manual + * + * A duplex is encoded as a 32-bit instruction with bits [15:14] set to 00. + * The sub-instructions that comprise a duplex are encoded as 13-bit fields + * in the duplex. + * + * Per table 10-4, the 4-bit duplex iclass is encoded in bits 31:29, 13 + */ +static uint32_t get_duplex_iclass(uint32_t encoding) +{ + uint32_t iclass = extract32(encoding, 13, 1); + iclass = deposit32(iclass, 1, 3, extract32(encoding, 29, 3)); + return iclass; +} /* - * Fill in the operands of the instruction - * dectree_generated.h.inc has a DECODE_OPINFO entry for each opcode - * For example, - * DECODE_OPINFO(A2_addi, - * DECODE_REG(0,5,0) - * DECODE_REG(1,5,16) - * DECODE_IMM(0,7,21,9) - * DECODE_IMM(0,9,5,0) - * DECODE_IMM_SXT(0,16) - * with the macros defined above, we'll fill in a switch statement - * where each case is an opcode tag. + * Per table 10-5, the duplex ICLASS field values that specify the group of + * each sub-instruction in a duplex + * + * This table points to the decode instruction for each entry in the table */ -static void -decode_op(Insn *insn, Opcode tag, uint32_t encoding) -{ - insn->immed[0] = 0; - insn->immed[1] = 0; - insn->opcode = tag; - if (insn->extension_valid) { - insn->which_extended = opcode_which_immediate_is_extended(tag); - } +typedef bool (*subinsn_decode_func)(DisasContext *ctx, uint16_t insn); +typedef struct { + subinsn_decode_func decode_slot0_subinsn; + subinsn_decode_func decode_slot1_subinsn; +} subinsn_decode_groups; - switch (tag) { -#include "dectree_generated.h.inc" - default: - break; - } +static const subinsn_decode_groups decode_groups[16] = { + [0x0] = { decode_subinsn_l1, decode_subinsn_l1 }, + [0x1] = { decode_subinsn_l2, decode_subinsn_l1 }, + [0x2] = { decode_subinsn_l2, decode_subinsn_l2 }, + [0x3] = { decode_subinsn_a, decode_subinsn_a }, + [0x4] = { decode_subinsn_l1, decode_subinsn_a }, + [0x5] = { decode_subinsn_l2, decode_subinsn_a }, + [0x6] = { decode_subinsn_s1, decode_subinsn_a }, + [0x7] = { decode_subinsn_s2, decode_subinsn_a }, + [0x8] = { decode_subinsn_s1, decode_subinsn_l1 }, + [0x9] = { decode_subinsn_s1, decode_subinsn_l2 }, + [0xa] = { decode_subinsn_s1, decode_subinsn_s1 }, + [0xb] = { decode_subinsn_s2, decode_subinsn_s1 }, + [0xc] = { decode_subinsn_s2, decode_subinsn_l1 }, + [0xd] = { decode_subinsn_s2, decode_subinsn_l2 }, + [0xe] = { decode_subinsn_s2, decode_subinsn_s2 }, + [0xf] = { NULL, NULL }, /* Reserved */ +}; - insn->generate = opcode_genptr[tag]; - - insn->iclass = iclass_bits(encoding); -} - -#undef DECODE_REG -#undef DECODE_IMPL_REG -#undef DECODE_IMM -#undef DECODE_IMM_SHIFT -#undef DECODE_OPINFO -#undef DECODE_MATCH_INFO -#undef DECODE_LEGACY_MATCH_INFO -#undef DECODE_END_TABLE -#undef INVALID -#undef TERMINAL -#undef SUBINSNS -#undef EXTSPACE -#undef TABLE_LINK -#undef DECODE_NEW_TABLE -#undef DECODE_SEPARATOR_BITS - -static unsigned int -decode_subinsn_tablewalk(Insn *insn, const DectreeTable *table, - uint32_t encoding) -{ - unsigned int i; - Opcode opc; - if (table->lookup_function) { - i = table->lookup_function(table->startbit, table->width, encoding); - } else { - i = extract32(encoding, table->startbit, table->width); - } - if (table->table[i].type == DECTREE_TABLE_LINK) { - return decode_subinsn_tablewalk(insn, table->table[i].table_link, - encoding); - } else if (table->table[i].type == DECTREE_TERMINAL) { - opc = table->table[i].opcode; - if ((encoding & decode_itable[opc].mask) != decode_itable[opc].match) { - return 0; - } - decode_op(insn, opc, encoding); - return 1; - } else { - return 0; - } -} - -static unsigned int get_insn_a(uint32_t encoding) +static uint16_t get_slot0_subinsn(uint32_t encoding) { return extract32(encoding, 0, 13); } -static unsigned int get_insn_b(uint32_t encoding) +static uint16_t get_slot1_subinsn(uint32_t encoding) { return extract32(encoding, 16, 13); } static unsigned int -decode_insns_tablewalk(Insn *insn, const DectreeTable *table, - uint32_t encoding) +decode_insns(DisasContext *ctx, Insn *insn, uint32_t encoding) { - unsigned int i; - unsigned int a, b; - Opcode opc; - if (table->lookup_function) { - i = table->lookup_function(table->startbit, table->width, encoding); - } else { - i = extract32(encoding, table->startbit, table->width); - } - if (table->table[i].type == DECTREE_TABLE_LINK) { - return decode_insns_tablewalk(insn, table->table[i].table_link, - encoding); - } else if (table->table[i].type == DECTREE_SUBINSNS) { - a = get_insn_a(encoding); - b = get_insn_b(encoding); - b = decode_subinsn_tablewalk(insn, table->table[i].table_link_b, b); - a = decode_subinsn_tablewalk(insn + 1, table->table[i].table_link, a); - if ((a == 0) || (b == 0)) { - return 0; + if (parse_bits(encoding) != 0) { + if (decode_normal(ctx, encoding) || + decode_hvx(ctx, encoding)) { + insn->generate = opcode_genptr[insn->opcode]; + insn->iclass = iclass_bits(encoding); + return 1; } - return 2; - } else if (table->table[i].type == DECTREE_TERMINAL) { - opc = table->table[i].opcode; - if ((encoding & decode_itable[opc].mask) != decode_itable[opc].match) { - if ((encoding & decode_legacy_itable[opc].mask) != - decode_legacy_itable[opc].match) { - return 0; + g_assert_not_reached(); + } else { + uint32_t iclass = get_duplex_iclass(encoding); + unsigned int slot0_subinsn = get_slot0_subinsn(encoding); + unsigned int slot1_subinsn = get_slot1_subinsn(encoding); + subinsn_decode_func decode_slot0_subinsn = + decode_groups[iclass].decode_slot0_subinsn; + subinsn_decode_func decode_slot1_subinsn = + decode_groups[iclass].decode_slot1_subinsn; + + /* The slot1 subinsn needs to be in the packet first */ + if (decode_slot1_subinsn(ctx, slot1_subinsn)) { + insn->generate = opcode_genptr[insn->opcode]; + insn->iclass = iclass_bits(encoding); + ctx->insn = ++insn; + if (decode_slot0_subinsn(ctx, slot0_subinsn)) { + insn->generate = opcode_genptr[insn->opcode]; + insn->iclass = iclass_bits(encoding); + return 2; } } - decode_op(insn, opc, encoding); - return 1; - } else if (table->table[i].type == DECTREE_EXTSPACE) { - /* - * For now, HVX will be the only coproc - */ - return decode_insns_tablewalk(insn, ext_trees[EXT_IDX_mmvec], encoding); - } else { - return 0; + g_assert_not_reached(); } } -static unsigned int -decode_insns(Insn *insn, uint32_t encoding) -{ - const DectreeTable *table; - if (parse_bits(encoding) != 0) { - /* Start with PP table - 32 bit instructions */ - table = &dectree_table_DECODE_ROOT_32; - } else { - /* start with EE table - duplex instructions */ - table = &dectree_table_DECODE_ROOT_EE; - } - return decode_insns_tablewalk(insn, table, encoding); -} - static void decode_add_endloop_insn(Insn *insn, int loopnum) { if (loopnum == 10) { @@ -783,7 +574,26 @@ static bool decode_parsebits_is_loopend(uint32_t encoding32) return bits == 0x2; } -static void +static bool has_valid_slot_assignment(Packet *pkt) +{ + int used_slots = 0; + for (int i = 0; i < pkt->num_insns; i++) { + int slot_mask; + Insn *insn = &pkt->insn[i]; + if (decode_opcode_ends_loop(insn->opcode)) { + /* We overload slot 0 for endloop. */ + continue; + } + slot_mask = 1 << insn->slot; + if (used_slots & slot_mask) { + return false; + } + used_slots |= slot_mask; + } + return true; +} + +static bool decode_set_slot_number(Packet *pkt) { int slot; @@ -872,6 +682,8 @@ decode_set_slot_number(Packet *pkt) /* Then push it to slot0 */ pkt->insn[slot1_iidx].slot = 0; } + + return has_valid_slot_assignment(pkt); } /* @@ -881,8 +693,8 @@ decode_set_slot_number(Packet *pkt) * or number of words used on success */ -int decode_packet(int max_words, const uint32_t *words, Packet *pkt, - bool disas_only) +int decode_packet(DisasContext *ctx, int max_words, const uint32_t *words, + Packet *pkt, bool disas_only) { int num_insns = 0; int words_read = 0; @@ -895,9 +707,11 @@ int decode_packet(int max_words, const uint32_t *words, Packet *pkt, memset(pkt, 0, sizeof(*pkt)); /* Try to build packet */ while (!end_of_packet && (words_read < max_words)) { + Insn *insn = &pkt->insn[num_insns]; + ctx->insn = insn; encoding32 = words[words_read]; end_of_packet = is_packet_end(encoding32); - new_insns = decode_insns(&pkt->insn[num_insns], encoding32); + new_insns = decode_insns(ctx, insn, encoding32); g_assert(new_insns > 0); /* * If we saw an extender, mark next word extended so immediate @@ -947,8 +761,11 @@ int decode_packet(int max_words, const uint32_t *words, Packet *pkt, decode_apply_extenders(pkt); if (!disas_only) { decode_remove_extenders(pkt); + if (!decode_set_slot_number(pkt)) { + /* Invalid packet */ + return 0; + } } - decode_set_slot_number(pkt); decode_fill_newvalue_regno(pkt); if (pkt->pkt_has_hvx) { @@ -968,9 +785,13 @@ int decode_packet(int max_words, const uint32_t *words, Packet *pkt, int disassemble_hexagon(uint32_t *words, int nwords, bfd_vma pc, GString *buf) { + DisasContext ctx; Packet pkt; - if (decode_packet(nwords, words, &pkt, true) > 0) { + memset(&ctx, 0, sizeof(DisasContext)); + ctx.pkt = &pkt; + + if (decode_packet(&ctx, nwords, words, &pkt, true) > 0) { snprint_a_pkt_disas(buf, &pkt, words, pc); return pkt.encod_pkt_size_in_bytes; } else { diff --git a/target/hexagon/decode.h b/target/hexagon/decode.h index c66f5ea64d..3f3012b978 100644 --- a/target/hexagon/decode.h +++ b/target/hexagon/decode.h @@ -21,12 +21,13 @@ #include "cpu.h" #include "opcodes.h" #include "insn.h" +#include "translate.h" void decode_init(void); void decode_send_insn_to(Packet *packet, int start, int newloc); -int decode_packet(int max_words, const uint32_t *words, Packet *pkt, - bool disas_only); +int decode_packet(DisasContext *ctx, int max_words, const uint32_t *words, + Packet *pkt, bool disas_only); #endif diff --git a/target/hexagon/dectree.py b/target/hexagon/dectree.py deleted file mode 100755 index 29467ec7d7..0000000000 --- a/target/hexagon/dectree.py +++ /dev/null @@ -1,351 +0,0 @@ -#!/usr/bin/env python3 - -## -## Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published by -## the Free Software Foundation; either version 2 of the License, or -## (at your option) any later version. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with this program; if not, see . -## - -import io -import re - -import sys -import iset - -encs = {tag : ''.join(reversed(iset.iset[tag]['enc'].replace(' ', ''))) - for tag in iset.tags if iset.iset[tag]['enc'] != 'MISSING ENCODING'} - -enc_classes = set([iset.iset[tag]['enc_class'] for tag in encs.keys()]) -subinsn_enc_classes = \ - set([enc_class for enc_class in enc_classes \ - if enc_class.startswith('SUBINSN_')]) -ext_enc_classes = \ - set([enc_class for enc_class in enc_classes \ - if enc_class not in ('NORMAL', '16BIT') and \ - not enc_class.startswith('SUBINSN_')]) - -try: - subinsn_groupings = iset.subinsn_groupings -except AttributeError: - subinsn_groupings = {} - -for (tag, subinsn_grouping) in subinsn_groupings.items(): - encs[tag] = ''.join(reversed(subinsn_grouping['enc'].replace(' ', ''))) - -dectree_normal = {'leaves' : set()} -dectree_16bit = {'leaves' : set()} -dectree_subinsn_groupings = {'leaves' : set()} -dectree_subinsns = {name : {'leaves' : set()} for name in subinsn_enc_classes} -dectree_extensions = {name : {'leaves' : set()} for name in ext_enc_classes} - -for tag in encs.keys(): - if tag in subinsn_groupings: - dectree_subinsn_groupings['leaves'].add(tag) - continue - enc_class = iset.iset[tag]['enc_class'] - if enc_class.startswith('SUBINSN_'): - if len(encs[tag]) != 32: - encs[tag] = encs[tag] + '0' * (32 - len(encs[tag])) - dectree_subinsns[enc_class]['leaves'].add(tag) - elif enc_class == '16BIT': - if len(encs[tag]) != 16: - raise Exception('Tag "{}" has enc_class "{}" and not an encoding ' + - 'width of 16 bits!'.format(tag, enc_class)) - dectree_16bit['leaves'].add(tag) - else: - if len(encs[tag]) != 32: - raise Exception('Tag "{}" has enc_class "{}" and not an encoding ' + - 'width of 32 bits!'.format(tag, enc_class)) - if enc_class == 'NORMAL': - dectree_normal['leaves'].add(tag) - else: - dectree_extensions[enc_class]['leaves'].add(tag) - -faketags = set() -for (tag, enc) in iset.enc_ext_spaces.items(): - faketags.add(tag) - encs[tag] = ''.join(reversed(enc.replace(' ', ''))) - dectree_normal['leaves'].add(tag) - -faketags |= set(subinsn_groupings.keys()) - -def every_bit_counts(bitset): - for i in range(1, len(next(iter(bitset)))): - if len(set([bits[:i] + bits[i+1:] for bits in bitset])) == len(bitset): - return False - return True - -def auto_separate(node): - tags = node['leaves'] - if len(tags) <= 1: - return - enc_width = len(encs[next(iter(tags))]) - opcode_bit_for_all = \ - [all([encs[tag][i] in '01' \ - for tag in tags]) for i in range(enc_width)] - opcode_bit_is_0_for_all = \ - [opcode_bit_for_all[i] and all([encs[tag][i] == '0' \ - for tag in tags]) for i in range(enc_width)] - opcode_bit_is_1_for_all = \ - [opcode_bit_for_all[i] and all([encs[tag][i] == '1' \ - for tag in tags]) for i in range(enc_width)] - differentiator_opcode_bit = \ - [opcode_bit_for_all[i] and \ - not (opcode_bit_is_0_for_all[i] or \ - opcode_bit_is_1_for_all[i]) \ - for i in range(enc_width)] - best_width = 0 - for width in range(4, 0, -1): - for lsb in range(enc_width - width, -1, -1): - bitset = set([encs[tag][lsb:lsb+width] for tag in tags]) - if all(differentiator_opcode_bit[lsb:lsb+width]) and \ - (len(bitset) == len(tags) or every_bit_counts(bitset)): - best_width = width - best_lsb = lsb - caught_all_tags = len(bitset) == len(tags) - break - if best_width != 0: - break - if best_width == 0: - raise Exception('Could not find a way to differentiate the encodings ' + - 'of the following tags:\n{}'.format('\n'.join(tags))) - if caught_all_tags: - for width in range(1, best_width): - for lsb in range(enc_width - width, -1, -1): - bitset = set([encs[tag][lsb:lsb+width] for tag in tags]) - if all(differentiator_opcode_bit[lsb:lsb+width]) and \ - len(bitset) == len(tags): - best_width = width - best_lsb = lsb - break - else: - continue - break - node['separator_lsb'] = best_lsb - node['separator_width'] = best_width - node['children'] = [] - for value in range(2 ** best_width): - child = {} - bits = ''.join(reversed('{:0{}b}'.format(value, best_width))) - child['leaves'] = \ - set([tag for tag in tags \ - if encs[tag][best_lsb:best_lsb+best_width] == bits]) - node['children'].append(child) - for child in node['children']: - auto_separate(child) - -auto_separate(dectree_normal) -auto_separate(dectree_16bit) -if subinsn_groupings: - auto_separate(dectree_subinsn_groupings) -for dectree_subinsn in dectree_subinsns.values(): - auto_separate(dectree_subinsn) -for dectree_ext in dectree_extensions.values(): - auto_separate(dectree_ext) - -for tag in faketags: - del encs[tag] - -def table_name(parents, node): - path = parents + [node] - root = path[0] - tag = next(iter(node['leaves'])) - if tag in subinsn_groupings: - enc_width = len(subinsn_groupings[tag]['enc'].replace(' ', '')) - else: - tag = next(iter(node['leaves'] - faketags)) - enc_width = len(encs[tag]) - determining_bits = ['_'] * enc_width - for (parent, child) in zip(path[:-1], path[1:]): - lsb = parent['separator_lsb'] - width = parent['separator_width'] - value = parent['children'].index(child) - determining_bits[lsb:lsb+width] = \ - list(reversed('{:0{}b}'.format(value, width))) - if tag in subinsn_groupings: - name = 'DECODE_ROOT_EE' - else: - enc_class = iset.iset[tag]['enc_class'] - if enc_class in ext_enc_classes: - name = 'DECODE_EXT_{}'.format(enc_class) - elif enc_class in subinsn_enc_classes: - name = 'DECODE_SUBINSN_{}'.format(enc_class) - else: - name = 'DECODE_ROOT_{}'.format(enc_width) - if node != root: - name += '_' + ''.join(reversed(determining_bits)) - return name - -def print_node(f, node, parents): - if len(node['leaves']) <= 1: - return - name = table_name(parents, node) - lsb = node['separator_lsb'] - width = node['separator_width'] - print('DECODE_NEW_TABLE({},{},DECODE_SEPARATOR_BITS({},{}))'.\ - format(name, 2 ** width, lsb, width), file=f) - for child in node['children']: - if len(child['leaves']) == 0: - print('INVALID()', file=f) - elif len(child['leaves']) == 1: - (tag,) = child['leaves'] - if tag in subinsn_groupings: - class_a = subinsn_groupings[tag]['class_a'] - class_b = subinsn_groupings[tag]['class_b'] - enc = subinsn_groupings[tag]['enc'].replace(' ', '') - if 'RESERVED' in tag: - print('INVALID()', file=f) - else: - print('SUBINSNS({},{},{},"{}")'.\ - format(tag, class_a, class_b, enc), file=f) - elif tag in iset.enc_ext_spaces: - enc = iset.enc_ext_spaces[tag].replace(' ', '') - print('EXTSPACE({},"{}")'.format(tag, enc), file=f) - else: - enc = ''.join(reversed(encs[tag])) - print('TERMINAL({},"{}")'.format(tag, enc), file=f) - else: - print('TABLE_LINK({})'.format(table_name(parents + [node], child)), - file=f) - print('DECODE_END_TABLE({},{},DECODE_SEPARATOR_BITS({},{}))'.\ - format(name, 2 ** width, lsb, width), file=f) - print(file=f) - parents.append(node) - for child in node['children']: - print_node(f, child, parents) - parents.pop() - -def print_tree(f, tree): - print_node(f, tree, []) - -def print_match_info(f): - for tag in sorted(encs.keys(), key=iset.tags.index): - enc = ''.join(reversed(encs[tag])) - mask = int(re.sub(r'[^1]', r'0', enc.replace('0', '1')), 2) - match = int(re.sub(r'[^01]', r'0', enc), 2) - suffix = '' - print('DECODE{}_MATCH_INFO({},0x{:x}U,0x{:x}U)'.\ - format(suffix, tag, mask, match), file=f) - -regre = re.compile( - r'((? 1: - raise Exception('Tag "{}" has split register field!'.\ - format(tag)) - reg_enc_field = reg_enc_fields[0] - if 2 ** len(reg_enc_field) != reg_num_choices: - raise Exception('Tag "{}" has incorrect register field width!'.\ - format(tag)) - print(' DECODE_REG({},{},{})'.\ - format(regno, len(reg_enc_field), enc.index(reg_enc_field)), - file=f) - if reg_type in num_registers and \ - reg_num_choices != num_registers[reg_type]: - print(' DECODE_MAPPED_REG({},{})'.\ - format(regno, reg_mapping), file=f) - regno += 1 - def implicit_register_key(reg): - return implicit_registers[reg] - for reg in sorted( - set([r for r in (iset.iset[tag]['rregs'].split(',') + \ - iset.iset[tag]['wregs'].split(',')) \ - if r in implicit_registers]), key=implicit_register_key): - print(' DECODE_IMPL_REG({},{})'.\ - format(regno, implicit_registers[reg]), file=f) - regno += 1 - if imms and imms[0][0].isupper(): - imms = reversed(imms) - for imm in imms: - if imm[0].isupper(): - immno = 1 - else: - immno = 0 - imm_type = imm[0] - imm_width = int(imm[1]) - imm_shift = imm[2] - if imm_shift: - imm_shift = int(imm_shift) - else: - imm_shift = 0 - if imm_type.islower(): - imm_letter = 'i' - else: - imm_letter = 'I' - remainder = imm_width - for m in reversed(list(re.finditer(imm_letter + '+', enc))): - remainder -= m.end() - m.start() - print(' DECODE_IMM({},{},{},{})'.\ - format(immno, m.end() - m.start(), m.start(), remainder), - file=f) - if remainder != 0: - if imm[2]: - imm[2] = ':' + imm[2] - raise Exception('Tag "{}" has an incorrect number of ' + \ - 'encoding bits for immediate "{}"'.\ - format(tag, ''.join(imm))) - if imm_type.lower() in 'sr': - print(' DECODE_IMM_SXT({},{})'.\ - format(immno, imm_width), file=f) - if imm_type.lower() == 'n': - print(' DECODE_IMM_NEG({},{})'.\ - format(immno, imm_width), file=f) - if imm_shift: - print(' DECODE_IMM_SHIFT({},{})'.\ - format(immno, imm_shift), file=f) - print(')', file=f) - -if __name__ == '__main__': - with open(sys.argv[1], 'w') as f: - print_tree(f, dectree_normal) - print_tree(f, dectree_16bit) - if subinsn_groupings: - print_tree(f, dectree_subinsn_groupings) - for (name, dectree_subinsn) in sorted(dectree_subinsns.items()): - print_tree(f, dectree_subinsn) - for (name, dectree_ext) in sorted(dectree_extensions.items()): - print_tree(f, dectree_ext) - print_match_info(f) - print_op_info(f) diff --git a/target/hexagon/fma_emu.c b/target/hexagon/fma_emu.c index d3b45d494f..05a56d8c10 100644 --- a/target/hexagon/fma_emu.c +++ b/target/hexagon/fma_emu.c @@ -415,7 +415,7 @@ static SUFFIX accum_round_##SUFFIX(Accum a, float_status * fp_status) \ * We want to normalize left until we have a leading one in bit 24 \ * Theoretically, we only need to shift a maximum of one to the left if we \ * shifted out lots of bits from B, or if we had no shift / 1 shift sticky \ - * shoudl be 0 \ + * should be 0 \ */ \ while ((int128_getlo(a.mant) & (1ULL << MANTBITS)) == 0) { \ a = accum_norm_left(a); \ diff --git a/target/hexagon/gdbstub.c b/target/hexagon/gdbstub.c index d152d01bfe..502c6987f0 100644 --- a/target/hexagon/gdbstub.c +++ b/target/hexagon/gdbstub.c @@ -16,14 +16,21 @@ */ #include "qemu/osdep.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "cpu.h" #include "internal.h" int hexagon_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - HexagonCPU *cpu = HEXAGON_CPU(cs); - CPUHexagonState *env = &cpu->env; + CPUHexagonState *env = cpu_env(cs); + + if (n == HEX_REG_P3_0_ALIASED) { + uint32_t p3_0 = 0; + for (int i = 0; i < NUM_PREGS; i++) { + p3_0 = deposit32(p3_0, i * 8, 8, env->pred[i]); + } + return gdb_get_regl(mem_buf, p3_0); + } if (n < TOTAL_PER_THREAD_REGS) { return gdb_get_regl(mem_buf, env->gpr[n]); @@ -34,8 +41,15 @@ int hexagon_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int hexagon_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - HexagonCPU *cpu = HEXAGON_CPU(cs); - CPUHexagonState *env = &cpu->env; + CPUHexagonState *env = cpu_env(cs); + + if (n == HEX_REG_P3_0_ALIASED) { + uint32_t p3_0 = ldtul_p(mem_buf); + for (int i = 0; i < NUM_PREGS; i++) { + env->pred[i] = extract32(p3_0, i * 8, 8); + } + return sizeof(target_ulong); + } if (n < TOTAL_PER_THREAD_REGS) { env->gpr[n] = ldtul_p(mem_buf); @@ -44,3 +58,77 @@ int hexagon_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) g_assert_not_reached(); } + +static int gdb_get_vreg(CPUHexagonState *env, GByteArray *mem_buf, int n) +{ + int total = 0; + int i; + for (i = 0; i < ARRAY_SIZE(env->VRegs[n].uw); i++) { + total += gdb_get_regl(mem_buf, env->VRegs[n].uw[i]); + } + return total; +} + +static int gdb_get_qreg(CPUHexagonState *env, GByteArray *mem_buf, int n) +{ + int total = 0; + int i; + for (i = 0; i < ARRAY_SIZE(env->QRegs[n].uw); i++) { + total += gdb_get_regl(mem_buf, env->QRegs[n].uw[i]); + } + return total; +} + +int hexagon_hvx_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) +{ + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env = &cpu->env; + + if (n < NUM_VREGS) { + return gdb_get_vreg(env, mem_buf, n); + } + n -= NUM_VREGS; + + if (n < NUM_QREGS) { + return gdb_get_qreg(env, mem_buf, n); + } + + g_assert_not_reached(); +} + +static int gdb_put_vreg(CPUHexagonState *env, uint8_t *mem_buf, int n) +{ + int i; + for (i = 0; i < ARRAY_SIZE(env->VRegs[n].uw); i++) { + env->VRegs[n].uw[i] = ldtul_p(mem_buf); + mem_buf += 4; + } + return MAX_VEC_SIZE_BYTES; +} + +static int gdb_put_qreg(CPUHexagonState *env, uint8_t *mem_buf, int n) +{ + int i; + for (i = 0; i < ARRAY_SIZE(env->QRegs[n].uw); i++) { + env->QRegs[n].uw[i] = ldtul_p(mem_buf); + mem_buf += 4; + } + return MAX_VEC_SIZE_BYTES / 8; +} + +int hexagon_hvx_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env = &cpu->env; + + if (n < NUM_VREGS) { + return gdb_put_vreg(env, mem_buf, n); + } + n -= NUM_VREGS; + + if (n < NUM_QREGS) { + return gdb_put_qreg(env, mem_buf, n); + } + + g_assert_not_reached(); +} diff --git a/target/hexagon/gen_analyze_funcs.py b/target/hexagon/gen_analyze_funcs.py new file mode 100755 index 0000000000..a9af666cef --- /dev/null +++ b/target/hexagon/gen_analyze_funcs.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 + +## +## Copyright(c) 2022-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sys +import re +import string +import hex_common + + +## +## Generate the code to analyze the instruction +## For A2_add: Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;} +## We produce: +## static void analyze_A2_add(DisasContext *ctx) +## { +## Insn *insn G_GNUC_UNUSED = ctx->insn; +## const int RdN = insn->regno[0]; +## ctx_log_reg_write(ctx, RdN, false); +## const int RsN = insn->regno[1]; +## ctx_log_reg_read(ctx, RsN); +## const int RtN = insn->regno[2]; +## ctx_log_reg_read(ctx, RtN); +## } +## +def gen_analyze_func(f, tag, regs, imms): + f.write(f"static void analyze_{tag}(DisasContext *ctx)\n") + f.write("{\n") + + f.write(" Insn *insn G_GNUC_UNUSED = ctx->insn;\n") + + i = 0 + ## Analyze all the registers + for regtype, regid in regs: + reg = hex_common.get_register(tag, regtype, regid) + if reg.is_written(): + reg.analyze_write(f, tag, i) + else: + reg.analyze_read(f, i) + i += 1 + + has_generated_helper = not hex_common.skip_qemu_helper( + tag + ) and not hex_common.is_idef_parser_enabled(tag) + + ## Mark HVX instructions with generated helpers + if (has_generated_helper and + "A_CVI" in hex_common.attribdict[tag]): + f.write(" ctx->has_hvx_helper = true;\n") + + f.write("}\n\n") + + +def main(): + hex_common.read_semantics_file(sys.argv[1]) + hex_common.read_attribs_file(sys.argv[2]) + hex_common.read_overrides_file(sys.argv[3]) + hex_common.read_overrides_file(sys.argv[4]) + ## Whether or not idef-parser is enabled is + ## determined by the number of arguments to + ## this script: + ## + ## 5 args. -> not enabled, + ## 6 args. -> idef-parser enabled. + ## + ## The 6:th arg. then holds a list of the successfully + ## parsed instructions. + is_idef_parser_enabled = len(sys.argv) > 6 + if is_idef_parser_enabled: + hex_common.read_idef_parser_enabled_file(sys.argv[5]) + hex_common.calculate_attribs() + hex_common.init_registers() + tagregs = hex_common.get_tagregs() + tagimms = hex_common.get_tagimms() + + with open(sys.argv[-1], "w") as f: + f.write("#ifndef HEXAGON_TCG_FUNCS_H\n") + f.write("#define HEXAGON_TCG_FUNCS_H\n\n") + + for tag in hex_common.tags: + gen_analyze_func(f, tag, tagregs[tag], tagimms[tag]) + + f.write("#endif /* HEXAGON_TCG_FUNCS_H */\n") + + +if __name__ == "__main__": + main() diff --git a/target/hexagon/gen_decodetree.py b/target/hexagon/gen_decodetree.py new file mode 100755 index 0000000000..a4fcd622c5 --- /dev/null +++ b/target/hexagon/gen_decodetree.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 + +## +## Copyright (c) 2024 Taylor Simpson +## +## 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 io +import re + +import sys +import textwrap +import iset +import hex_common + +encs = { + tag: "".join(reversed(iset.iset[tag]["enc"].replace(" ", ""))) + for tag in iset.tags + if iset.iset[tag]["enc"] != "MISSING ENCODING" +} + + +regre = re.compile(r"((? 1: + raise Exception(f"{tag} has split register field!") + reg_enc_field = reg_enc_fields[0] + if 2 ** len(reg_enc_field) != reg_num_choices: + raise Exception(f"{tag} has incorrect register field width!") + + f.write(f"%{tag}_{reg_type}{reg_id}\t" + f"{enc.index(reg_enc_field)}:{len(reg_enc_field)}") + + if (reg_type in num_registers and + reg_num_choices != num_registers[reg_type]): + f.write(f"\t!function=decode_mapped_reg_{reg_mapping}") + f.write("\n") + + # Write the field definitions for the immediates + for imm in imms: + immno = 1 if imm[0].isupper() else 0 + imm_type = imm[0] + imm_width = int(imm[1]) + imm_letter = "i" if imm_type.islower() else "I" + fields = [] + sign_mark = "s" if imm_type.lower() in "sr" else "" + for m in reversed(list(re.finditer(imm_letter + "+", enc))): + fields.append(f"{m.start()}:{sign_mark}{m.end() - m.start()}") + sign_mark = "" + field_str = " ".join(fields) + f.write(f"%{tag}_{imm_type}{imm_letter}\t{field_str}\n") + + ## Handle instructions with unused encoding letters + ## Change the unused letters to ignored + if tag in tags_with_unused_d_encoding: + enc_str = enc_str.replace("d", "-") + if tag in tags_with_unused_t_encoding: + enc_str = enc_str.replace("t", "-") + + # Replace the operand letters with . + for x in operand_letters: + enc_str = enc_str.replace(x, ".") + + # Write the instruction format + f.write(f"@{tag}\t{enc_str}") + for reg in regs: + reg_type = reg[0] + reg_id = reg[1] + f.write(f" {reg_type}{reg_id}=%{tag}_{reg_type}{reg_id}") + for imm in imms: + imm_type = imm[0] + imm_letter = "i" if imm_type.islower() else "I" + f.write(f" {imm_type}{imm_letter}=%{tag}_{imm_type}{imm_letter}") + + if not is_subinsn: + f.write(" %PP") + f.write("\n") + + # Replace the 0s and 1s with . + enc_str = enc_str.replace("0", ".").replace("1", ".") + + # Write the instruction pattern + f.write(f"{tag}\t{enc_str} @{tag}\n") + + +if __name__ == "__main__": + hex_common.read_semantics_file(sys.argv[1]) + class_to_decode = sys.argv[2] + with open(sys.argv[3], "w") as f: + gen_decodetree_file(f, class_to_decode) diff --git a/target/hexagon/gen_dectree_import.c b/target/hexagon/gen_dectree_import.c index ee354677fd..87f20c14f1 100644 --- a/target/hexagon/gen_dectree_import.c +++ b/target/hexagon/gen_dectree_import.c @@ -56,24 +56,6 @@ const char * const opcode_syntax[XX_LAST_OPCODE] = { #undef EXTINSN }; -const char * const opcode_rregs[] = { -#define REGINFO(TAG, REGINFO, RREGS, WREGS) RREGS, -#define IMMINFO(TAG, SIGN, SIZE, SHAMT, SIGN2, SIZE2, SHAMT2) /* nothing */ -#include "op_regs_generated.h.inc" - NULL -#undef REGINFO -#undef IMMINFO -}; - -const char * const opcode_wregs[] = { -#define REGINFO(TAG, REGINFO, RREGS, WREGS) WREGS, -#define IMMINFO(TAG, SIGN, SIZE, SHAMT, SIGN2, SIZE2, SHAMT2) /* nothing */ -#include "op_regs_generated.h.inc" - NULL -#undef REGINFO -#undef IMMINFO -}; - const OpcodeEncoding opcode_encodings[] = { #define DEF_ENC32(TAG, ENCSTR) \ [TAG] = { .encoding = ENCSTR }, @@ -130,8 +112,6 @@ static void gen_iset_table(FILE *out) fprintf(out, "\t\'%s\' : {\n", opcode_names[i]); fprintf(out, "\t\t\'tag\' : \'%s\',\n", opcode_names[i]); fprintf(out, "\t\t\'syntax\' : \'%s\',\n", opcode_syntax[i]); - fprintf(out, "\t\t\'rregs\' : \'%s\',\n", opcode_rregs[i]); - fprintf(out, "\t\t\'wregs\' : \'%s\',\n", opcode_wregs[i]); fprintf(out, "\t\t\'enc\' : \'%s\',\n", get_opcode_enc(i)); fprintf(out, "\t\t\'enc_class\' : \'%s\',\n", get_opcode_enc_class(i)); fprintf(out, "\t},\n"); @@ -150,33 +130,6 @@ static void gen_tags_list(FILE *out) fprintf(out, "];\n\n"); } -static void gen_enc_ext_spaces_table(FILE *out) -{ - fprintf(out, "enc_ext_spaces = {\n"); -#define DEF_EXT_SPACE(SPACEID, ENCSTR) \ - fprintf(out, "\t\'%s\' : \'%s\',\n", #SPACEID, ENCSTR); -#include "imported/encode.def" -#undef DEF_EXT_SPACE - fprintf(out, "};\n\n"); -} - -static void gen_subinsn_groupings_table(FILE *out) -{ - fprintf(out, "subinsn_groupings = {\n"); -#define DEF_PACKED32(TAG, TYPEA, TYPEB, ENCSTR) \ - do { \ - fprintf(out, "\t\'%s\' : {\n", #TAG); \ - fprintf(out, "\t\t\'name\' : \'%s\',\n", #TAG); \ - fprintf(out, "\t\t\'class_a\' : \'%s\',\n", #TYPEA); \ - fprintf(out, "\t\t\'class_b\' : \'%s\',\n", #TYPEB); \ - fprintf(out, "\t\t\'enc\' : \'%s\',\n", ENCSTR); \ - fprintf(out, "\t},\n"); \ - } while (0); -#include "imported/encode.def" -#undef DEF_PACKED32 - fprintf(out, "};\n\n"); -} - int main(int argc, char *argv[]) { FILE *outfile; @@ -193,8 +146,6 @@ int main(int argc, char *argv[]) gen_iset_table(outfile); gen_tags_list(outfile); - gen_enc_ext_spaces_table(outfile); - gen_subinsn_groupings_table(outfile); fclose(outfile); return 0; diff --git a/target/hexagon/gen_helper_funcs.py b/target/hexagon/gen_helper_funcs.py index a446c45384..9cc3d69c49 100755 --- a/target/hexagon/gen_helper_funcs.py +++ b/target/hexagon/gen_helper_funcs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -22,133 +22,6 @@ import re import string import hex_common -## -## Helpers for gen_helper_function -## -def gen_decl_ea(f): - f.write(" uint32_t EA;\n") - -def gen_helper_return_type(f,regtype,regid,regno): - if regno > 1 : f.write(", ") - f.write("int32_t") - -def gen_helper_return_type_pair(f,regtype,regid,regno): - if regno > 1 : f.write(", ") - f.write("int64_t") - -def gen_helper_arg(f,regtype,regid,regno): - if regno > 0 : f.write(", " ) - f.write("int32_t %s%sV" % (regtype,regid)) - -def gen_helper_arg_new(f,regtype,regid,regno): - if regno >= 0 : f.write(", " ) - f.write("int32_t %s%sN" % (regtype,regid)) - -def gen_helper_arg_pair(f,regtype,regid,regno): - if regno >= 0 : f.write(", ") - f.write("int64_t %s%sV" % (regtype,regid)) - -def gen_helper_arg_ext(f,regtype,regid,regno): - if regno > 0 : f.write(", ") - f.write("void *%s%sV_void" % (regtype,regid)) - -def gen_helper_arg_ext_pair(f,regtype,regid,regno): - if regno > 0 : f.write(", ") - f.write("void *%s%sV_void" % (regtype,regid)) - -def gen_helper_arg_opn(f,regtype,regid,i,tag): - if (hex_common.is_pair(regid)): - if (hex_common.is_hvx_reg(regtype)): - gen_helper_arg_ext_pair(f,regtype,regid,i) - else: - gen_helper_arg_pair(f,regtype,regid,i) - elif (hex_common.is_single(regid)): - if hex_common.is_old_val(regtype, regid, tag): - if (hex_common.is_hvx_reg(regtype)): - gen_helper_arg_ext(f,regtype,regid,i) - else: - gen_helper_arg(f,regtype,regid,i) - elif hex_common.is_new_val(regtype, regid, tag): - gen_helper_arg_new(f,regtype,regid,i) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) - -def gen_helper_arg_imm(f,immlett): - f.write(", int32_t %s" % (hex_common.imm_name(immlett))) - -def gen_helper_dest_decl(f,regtype,regid,regno,subfield=""): - f.write(" int32_t %s%sV%s = 0;\n" % \ - (regtype,regid,subfield)) - -def gen_helper_dest_decl_pair(f,regtype,regid,regno,subfield=""): - f.write(" int64_t %s%sV%s = 0;\n" % \ - (regtype,regid,subfield)) - -def gen_helper_dest_decl_ext(f,regtype,regid): - if (regtype == "Q"): - f.write(" /* %s%sV is *(MMQReg *)(%s%sV_void) */\n" % \ - (regtype,regid,regtype,regid)) - else: - f.write(" /* %s%sV is *(MMVector *)(%s%sV_void) */\n" % \ - (regtype,regid,regtype,regid)) - -def gen_helper_dest_decl_ext_pair(f,regtype,regid,regno): - f.write(" /* %s%sV is *(MMVectorPair *))%s%sV_void) */\n" % \ - (regtype,regid,regtype, regid)) - -def gen_helper_dest_decl_opn(f,regtype,regid,i): - if (hex_common.is_pair(regid)): - if (hex_common.is_hvx_reg(regtype)): - gen_helper_dest_decl_ext_pair(f,regtype,regid, i) - else: - gen_helper_dest_decl_pair(f,regtype,regid,i) - elif (hex_common.is_single(regid)): - if (hex_common.is_hvx_reg(regtype)): - gen_helper_dest_decl_ext(f,regtype,regid) - else: - gen_helper_dest_decl(f,regtype,regid,i) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) - -def gen_helper_src_var_ext(f,regtype,regid): - if (regtype == "Q"): - f.write(" /* %s%sV is *(MMQReg *)(%s%sV_void) */\n" % \ - (regtype,regid,regtype,regid)) - else: - f.write(" /* %s%sV is *(MMVector *)(%s%sV_void) */\n" % \ - (regtype,regid,regtype,regid)) - -def gen_helper_src_var_ext_pair(f,regtype,regid,regno): - f.write(" /* %s%sV%s is *(MMVectorPair *)(%s%sV%s_void) */\n" % \ - (regtype,regid,regno,regtype,regid,regno)) - -def gen_helper_return(f,regtype,regid,regno): - f.write(" return %s%sV;\n" % (regtype,regid)) - -def gen_helper_return_pair(f,regtype,regid,regno): - f.write(" return %s%sV;\n" % (regtype,regid)) - -def gen_helper_dst_write_ext(f,regtype,regid): - return - -def gen_helper_dst_write_ext_pair(f,regtype,regid): - return - -def gen_helper_return_opn(f, regtype, regid, i): - if (hex_common.is_pair(regid)): - if (hex_common.is_hvx_reg(regtype)): - gen_helper_dst_write_ext_pair(f,regtype,regid) - else: - gen_helper_return_pair(f,regtype,regid,i) - elif (hex_common.is_single(regid)): - if (hex_common.is_hvx_reg(regtype)): - gen_helper_dst_write_ext(f,regtype,regid) - else: - gen_helper_return(f,regtype,regid,i) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) ## ## Generate the TCG code to call the helper @@ -156,10 +29,8 @@ def gen_helper_return_opn(f, regtype, regid, i): ## We produce: ## int32_t HELPER(A2_add)(CPUHexagonState *env, int32_t RsV, int32_t RtV) ## { -## uint32_t slot __attribute__(unused)) = 4; ## int32_t RdV = 0; ## { RdV=RsV+RtV;} -## COUNT_HELPER(A2_add); ## return RdV; ## } ## @@ -167,149 +38,114 @@ def gen_helper_function(f, tag, tagregs, tagimms): regs = tagregs[tag] imms = tagimms[tag] - numresults = 0 - numscalarresults = 0 - numscalarreadwrite = 0 - for regtype,regid,toss,numregs in regs: - if (hex_common.is_written(regid)): - numresults += 1 - if (hex_common.is_scalar_reg(regtype)): - numscalarresults += 1 - if (hex_common.is_readwrite(regid)): - if (hex_common.is_scalar_reg(regtype)): - numscalarreadwrite += 1 + ret_type = hex_common.helper_ret_type(tag, regs).func_arg - if (numscalarresults > 1): - ## The helper is bogus when there is more than one result - f.write("void HELPER(%s)(CPUHexagonState *env) { BOGUS_HELPER(%s); }\n" - % (tag, tag)) - else: - ## The return type of the function is the type of the destination - ## register (if scalar) - i=0 - for regtype,regid,toss,numregs in regs: - if (hex_common.is_written(regid)): - if (hex_common.is_pair(regid)): - if (hex_common.is_hvx_reg(regtype)): - continue - else: - gen_helper_return_type_pair(f,regtype,regid,i) - elif (hex_common.is_single(regid)): - if (hex_common.is_hvx_reg(regtype)): - continue - else: - gen_helper_return_type(f,regtype,regid,i) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) - i += 1 + declared = [] + for arg in hex_common.helper_args(tag, regs, imms): + declared.append(arg.func_arg) - if (numscalarresults == 0): - f.write("void") - f.write(" HELPER(%s)(CPUHexagonState *env" % tag) + arguments = ", ".join(declared) + f.write(f"{ret_type} HELPER({tag})({arguments})\n") + f.write("{\n") + if hex_common.need_ea(tag): + f.write(hex_common.code_fmt(f"""\ + uint32_t EA; + """)) + ## Declare the return variable + if not hex_common.is_predicated(tag): + for regtype, regid in regs: + reg = hex_common.get_register(tag, regtype, regid) + if reg.is_writeonly() and not reg.is_hvx_reg(): + f.write(hex_common.code_fmt(f"""\ + {reg.helper_arg_type()} {reg.helper_arg_name()} = 0; + """)) - ## Arguments include the vector destination operands - i = 1 - for regtype,regid,toss,numregs in regs: - if (hex_common.is_written(regid)): - if (hex_common.is_pair(regid)): - if (hex_common.is_hvx_reg(regtype)): - gen_helper_arg_ext_pair(f,regtype,regid,i) - else: - continue - elif (hex_common.is_single(regid)): - if (hex_common.is_hvx_reg(regtype)): - gen_helper_arg_ext(f,regtype,regid,i) - else: - # This is the return value of the function - continue - else: - print("Bad register parse: ",regtype,regid,toss,numregs) - i += 1 + ## Print useful information about HVX registers + for regtype, regid in regs: + reg = hex_common.get_register(tag, regtype, regid) + if reg.is_hvx_reg(): + reg.helper_hvx_desc(f) - ## Arguments to the helper function are the source regs and immediates - for regtype,regid,toss,numregs in regs: - if (hex_common.is_read(regid)): - if (hex_common.is_hvx_reg(regtype) and - hex_common.is_readwrite(regid)): - continue - gen_helper_arg_opn(f,regtype,regid,i,tag) - i += 1 - for immlett,bits,immshift in imms: - gen_helper_arg_imm(f,immlett) - i += 1 + if hex_common.need_slot(tag): + if "A_LOAD" in hex_common.attribdict[tag]: + f.write(hex_common.code_fmt(f"""\ + bool pkt_has_store_s1 = slotval & 0x1; + """)) + f.write(hex_common.code_fmt(f"""\ + uint32_t slot = slotval >> 1; + """)) - if hex_common.need_slot(tag): - if i > 0: f.write(", ") - f.write("uint32_t slot") - i += 1 - if hex_common.need_part1(tag): - if i > 0: f.write(", ") - f.write("uint32_t part1") - f.write(")\n{\n") - if (not hex_common.need_slot(tag)): - f.write(" uint32_t slot __attribute__((unused)) = 4;\n" ) - if hex_common.need_ea(tag): gen_decl_ea(f) - ## Declare the return variable - i=0 - for regtype,regid,toss,numregs in regs: - if (hex_common.is_writeonly(regid)): - gen_helper_dest_decl_opn(f,regtype,regid,i) - i += 1 + if "A_FPOP" in hex_common.attribdict[tag]: + f.write(hex_common.code_fmt(f"""\ + arch_fpop_start(env); + """)) - for regtype,regid,toss,numregs in regs: - if (hex_common.is_read(regid)): - if (hex_common.is_pair(regid)): - if (hex_common.is_hvx_reg(regtype)): - gen_helper_src_var_ext_pair(f,regtype,regid,i) - elif (hex_common.is_single(regid)): - if (hex_common.is_hvx_reg(regtype)): - gen_helper_src_var_ext(f,regtype,regid) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) + f.write(hex_common.code_fmt(f"""\ + {hex_common.semdict[tag]} + """)) - if 'A_FPOP' in hex_common.attribdict[tag]: - f.write(' arch_fpop_start(env);\n'); + if "A_FPOP" in hex_common.attribdict[tag]: + f.write(hex_common.code_fmt(f"""\ + arch_fpop_end(env); + """)) - f.write(" %s\n" % hex_common.semdict[tag]) + ## Return the scalar result + for regtype, regid in regs: + reg = hex_common.get_register(tag, regtype, regid) + if reg.is_written() and not reg.is_hvx_reg(): + f.write(hex_common.code_fmt(f"""\ + return {reg.helper_arg_name()}; + """)) - if 'A_FPOP' in hex_common.attribdict[tag]: - f.write(' arch_fpop_end(env);\n'); + f.write("}\n\n") + ## End of the helper definition - ## Save/return the return variable - for regtype,regid,toss,numregs in regs: - if (hex_common.is_written(regid)): - gen_helper_return_opn(f, regtype, regid, i) - f.write("}\n\n") - ## End of the helper definition def main(): hex_common.read_semantics_file(sys.argv[1]) hex_common.read_attribs_file(sys.argv[2]) hex_common.read_overrides_file(sys.argv[3]) hex_common.read_overrides_file(sys.argv[4]) + ## Whether or not idef-parser is enabled is + ## determined by the number of arguments to + ## this script: + ## + ## 5 args. -> not enabled, + ## 6 args. -> idef-parser enabled. + ## + ## The 6:th arg. then holds a list of the successfully + ## parsed instructions. + is_idef_parser_enabled = len(sys.argv) > 6 + if is_idef_parser_enabled: + hex_common.read_idef_parser_enabled_file(sys.argv[5]) hex_common.calculate_attribs() + hex_common.init_registers() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[5], 'w') as f: + output_file = sys.argv[-1] + with open(output_file, "w") as f: for tag in hex_common.tags: ## Skip the priv instructions - if ( "A_PRIV" in hex_common.attribdict[tag] ) : + if "A_PRIV" in hex_common.attribdict[tag]: continue ## Skip the guest instructions - if ( "A_GUEST" in hex_common.attribdict[tag] ) : + if "A_GUEST" in hex_common.attribdict[tag]: continue ## Skip the diag instructions - if ( tag == "Y6_diag" ) : + if tag == "Y6_diag": continue - if ( tag == "Y6_diag0" ) : + if tag == "Y6_diag0": continue - if ( tag == "Y6_diag1" ) : + if tag == "Y6_diag1": continue - if ( hex_common.skip_qemu_helper(tag) ): + if hex_common.skip_qemu_helper(tag): + continue + if hex_common.is_idef_parser_enabled(tag): continue gen_helper_function(f, tag, tagregs, tagimms) + if __name__ == "__main__": main() diff --git a/target/hexagon/gen_helper_protos.py b/target/hexagon/gen_helper_protos.py index 3b4e993fd1..c82b0f54e4 100755 --- a/target/hexagon/gen_helper_protos.py +++ b/target/hexagon/gen_helper_protos.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -22,37 +22,6 @@ import re import string import hex_common -## -## Helpers for gen_helper_prototype -## -def_helper_types = { - 'N' : 's32', - 'O' : 's32', - 'P' : 's32', - 'M' : 's32', - 'C' : 's32', - 'R' : 's32', - 'V' : 'ptr', - 'Q' : 'ptr' -} - -def_helper_types_pair = { - 'R' : 's64', - 'C' : 's64', - 'S' : 's64', - 'G' : 's64', - 'V' : 'ptr', - 'Q' : 'ptr' -} - -def gen_def_helper_opn(f, tag, regtype, regid, toss, numregs, i): - if (hex_common.is_pair(regid)): - f.write(", %s" % (def_helper_types_pair[regtype])) - elif (hex_common.is_single(regid)): - f.write(", %s" % (def_helper_types[regtype])) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) - ## ## Generate the DEF_HELPER prototype for an instruction ## For A2_add: Rd32=add(Rs32,Rt32) @@ -63,103 +32,63 @@ def gen_helper_prototype(f, tag, tagregs, tagimms): regs = tagregs[tag] imms = tagimms[tag] - numresults = 0 - numscalarresults = 0 - numscalarreadwrite = 0 - for regtype,regid,toss,numregs in regs: - if (hex_common.is_written(regid)): - numresults += 1 - if (hex_common.is_scalar_reg(regtype)): - numscalarresults += 1 - if (hex_common.is_readwrite(regid)): - if (hex_common.is_scalar_reg(regtype)): - numscalarreadwrite += 1 + declared = [] + ret_type = hex_common.helper_ret_type(tag, regs).proto_arg + declared.append(ret_type) - if (numscalarresults > 1): - ## The helper is bogus when there is more than one result - f.write('DEF_HELPER_1(%s, void, env)\n' % tag) - else: - ## Figure out how many arguments the helper will take - if (numscalarresults == 0): - def_helper_size = len(regs)+len(imms)+numscalarreadwrite+1 - if hex_common.need_part1(tag): def_helper_size += 1 - if hex_common.need_slot(tag): def_helper_size += 1 - f.write('DEF_HELPER_%s(%s' % (def_helper_size, tag)) - ## The return type is void - f.write(', void' ) - else: - def_helper_size = len(regs)+len(imms)+numscalarreadwrite - if hex_common.need_part1(tag): def_helper_size += 1 - if hex_common.need_slot(tag): def_helper_size += 1 - f.write('DEF_HELPER_%s(%s' % (def_helper_size, tag)) + for arg in hex_common.helper_args(tag, regs, imms): + declared.append(arg.proto_arg) - ## Generate the qemu DEF_HELPER type for each result - ## Iterate over this list twice - ## - Emit the scalar result - ## - Emit the vector result - i=0 - for regtype,regid,toss,numregs in regs: - if (hex_common.is_written(regid)): - if (not hex_common.is_hvx_reg(regtype)): - gen_def_helper_opn(f, tag, regtype, regid, toss, numregs, i) - i += 1 + arguments = ", ".join(declared) + f.write(f"DEF_HELPER_{len(declared) - 1}({tag}, {arguments})\n") - ## Put the env between the outputs and inputs - f.write(', env' ) - i += 1 - - # Second pass - for regtype,regid,toss,numregs in regs: - if (hex_common.is_written(regid)): - if (hex_common.is_hvx_reg(regtype)): - gen_def_helper_opn(f, tag, regtype, regid, toss, numregs, i) - i += 1 - - ## Generate the qemu type for each input operand (regs and immediates) - for regtype,regid,toss,numregs in regs: - if (hex_common.is_read(regid)): - if (hex_common.is_hvx_reg(regtype) and - hex_common.is_readwrite(regid)): - continue - gen_def_helper_opn(f, tag, regtype, regid, toss, numregs, i) - i += 1 - for immlett,bits,immshift in imms: - f.write(", s32") - - ## Add the arguments for the instruction slot and part1 (if needed) - if hex_common.need_slot(tag): f.write(', i32' ) - if hex_common.need_part1(tag): f.write(' , i32' ) - f.write(')\n') def main(): hex_common.read_semantics_file(sys.argv[1]) hex_common.read_attribs_file(sys.argv[2]) hex_common.read_overrides_file(sys.argv[3]) hex_common.read_overrides_file(sys.argv[4]) + ## Whether or not idef-parser is enabled is + ## determined by the number of arguments to + ## this script: + ## + ## 5 args. -> not enabled, + ## 6 args. -> idef-parser enabled. + ## + ## The 6:th arg. then holds a list of the successfully + ## parsed instructions. + is_idef_parser_enabled = len(sys.argv) > 6 + if is_idef_parser_enabled: + hex_common.read_idef_parser_enabled_file(sys.argv[5]) hex_common.calculate_attribs() + hex_common.init_registers() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[5], 'w') as f: + output_file = sys.argv[-1] + with open(output_file, "w") as f: for tag in hex_common.tags: ## Skip the priv instructions - if ( "A_PRIV" in hex_common.attribdict[tag] ) : + if "A_PRIV" in hex_common.attribdict[tag]: continue ## Skip the guest instructions - if ( "A_GUEST" in hex_common.attribdict[tag] ) : + if "A_GUEST" in hex_common.attribdict[tag]: continue ## Skip the diag instructions - if ( tag == "Y6_diag" ) : + if tag == "Y6_diag": continue - if ( tag == "Y6_diag0" ) : + if tag == "Y6_diag0": continue - if ( tag == "Y6_diag1" ) : + if tag == "Y6_diag1": continue - if ( hex_common.skip_qemu_helper(tag) ): + if hex_common.skip_qemu_helper(tag): + continue + if hex_common.is_idef_parser_enabled(tag): continue gen_helper_prototype(f, tag, tagregs, tagimms) + if __name__ == "__main__": main() diff --git a/target/hexagon/gen_idef_parser_funcs.py b/target/hexagon/gen_idef_parser_funcs.py new file mode 100644 index 0000000000..550a48cb7b --- /dev/null +++ b/target/hexagon/gen_idef_parser_funcs.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 + +## +## Copyright(c) 2019-2023 rev.ng Labs Srl. All Rights Reserved. +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sys +import re +import string +from io import StringIO + +import hex_common + + +## +## Generate code to be fed to the idef_parser +## +## Consider A2_add: +## +## Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;} +## +## We produce: +## +## A2_add(RdV, in RsV, in RtV) { +## { RdV=RsV+RtV;} +## } +## +## A2_add represents the instruction tag. Then we have a list of TCGv +## that the code generated by the parser can expect in input. Some of +## them are inputs ("in" prefix), while some others are outputs. +## +def main(): + hex_common.read_semantics_file(sys.argv[1]) + hex_common.read_attribs_file(sys.argv[2]) + hex_common.calculate_attribs() + hex_common.init_registers() + tagregs = hex_common.get_tagregs() + tagimms = hex_common.get_tagimms() + + with open(sys.argv[3], "w") as f: + f.write('#include "macros.inc"\n\n') + + for tag in hex_common.tags: + ## Skip the priv instructions + if "A_PRIV" in hex_common.attribdict[tag]: + continue + ## Skip the guest instructions + if "A_GUEST" in hex_common.attribdict[tag]: + continue + ## Skip instructions that saturate in a ternary expression + if tag in {"S2_asr_r_r_sat", "S2_asl_r_r_sat"}: + continue + ## Skip instructions using switch + if tag in {"S4_vrcrotate_acc", "S4_vrcrotate"}: + continue + ## Skip trap instructions + if tag in {"J2_trap0", "J2_trap1"}: + continue + ## Skip 128-bit instructions + if tag in {"A7_croundd_ri", "A7_croundd_rr"}: + continue + if tag in { + "M7_wcmpyrw", + "M7_wcmpyrwc", + "M7_wcmpyiw", + "M7_wcmpyiwc", + "M7_wcmpyrw_rnd", + "M7_wcmpyrwc_rnd", + "M7_wcmpyiw_rnd", + "M7_wcmpyiwc_rnd", + }: + continue + ## Skip interleave/deinterleave instructions + if tag in {"S2_interleave", "S2_deinterleave"}: + continue + ## Skip instructions using bit reverse + if tag in { + "S2_brev", + "S2_brevp", + "S2_ct0", + "S2_ct1", + "S2_ct0p", + "S2_ct1p", + "A4_tlbmatch", + }: + continue + ## Skip other unsupported instructions + if tag == "S2_cabacdecbin" or tag == "A5_ACS": + continue + if tag.startswith("Y"): + continue + if tag.startswith("V6_"): + continue + if ( tag.startswith("F") and + tag not in { + "F2_sfimm_p", + "F2_sfimm_n", + "F2_dfimm_p", + "F2_dfimm_n", + "F2_dfmpyll", + "F2_dfmpylh" + }): + continue + if tag.endswith("_locked"): + continue + if "A_COF" in hex_common.attribdict[tag]: + continue + if ( tag.startswith('R6_release_') ): + continue + ## Skip instructions that are incompatible with short-circuit + ## packet register writes + if ( tag == 'S2_insert' or + tag == 'S2_insert_rp' or + tag == 'S2_asr_r_svw_trun' or + tag == 'A2_swiz' ): + continue + + regs = tagregs[tag] + imms = tagimms[tag] + + arguments = [] + for regtype, regid in regs: + reg = hex_common.get_register(tag, regtype, regid) + prefix = "in " if reg.is_read() else "" + arguments.append(f"{prefix}{reg.reg_tcg()}") + + for immlett, bits, immshift in imms: + arguments.append(hex_common.imm_name(immlett)) + + f.write(f"{tag}({', '.join(arguments)}) {{\n") + f.write(" ") + if hex_common.need_ea(tag): + f.write("size4u_t EA; ") + f.write(f"{hex_common.semdict[tag]}\n") + f.write("}\n\n") + + +if __name__ == "__main__": + main() diff --git a/target/hexagon/gen_op_attribs.py b/target/hexagon/gen_op_attribs.py index 6a1a1ca21d..41074b8573 100755 --- a/target/hexagon/gen_op_attribs.py +++ b/target/hexagon/gen_op_attribs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ import re import string import hex_common + def main(): hex_common.read_semantics_file(sys.argv[1]) hex_common.read_attribs_file(sys.argv[2]) @@ -30,10 +31,13 @@ def main(): ## ## Generate all the attributes associated with each instruction ## - with open(sys.argv[3], 'w') as f: + with open(sys.argv[3], "w") as f: for tag in hex_common.tags: - f.write('OP_ATTRIB(%s,ATTRIBS(%s))\n' % \ - (tag, ','.join(sorted(hex_common.attribdict[tag])))) + f.write( + f"OP_ATTRIB({tag},ATTRIBS(" + f'{",".join(sorted(hex_common.attribdict[tag]))}))\n' + ) + if __name__ == "__main__": main() diff --git a/target/hexagon/gen_op_regs.py b/target/hexagon/gen_op_regs.py index e8137d4a12..7b7b33895a 100755 --- a/target/hexagon/gen_op_regs.py +++ b/target/hexagon/gen_op_regs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -22,21 +22,25 @@ import re import string import hex_common + ## ## Generate the register and immediate operands for each instruction ## def calculate_regid_reg(tag): - def letter_inc(x): return chr(ord(x)+1) - ordered_implregs = [ 'SP','FP','LR' ] - srcdst_lett = 'X' - src_lett = 'S' - dst_lett = 'D' + def letter_inc(x): + return chr(ord(x) + 1) + + ordered_implregs = ["SP", "FP", "LR"] + srcdst_lett = "X" + src_lett = "S" + dst_lett = "D" retstr = "" mapdict = {} for reg in ordered_implregs: reg_rd = 0 reg_wr = 0 - if ('A_IMPLICIT_WRITES_'+reg) in hex_common.attribdict[tag]: reg_wr = 1 + if ("A_IMPLICIT_WRITES_" + reg) in hex_common.attribdict[tag]: + reg_wr = 1 if reg_rd and reg_wr: retstr += srcdst_lett mapdict[srcdst_lett] = reg @@ -49,62 +53,73 @@ def calculate_regid_reg(tag): retstr += dst_lett mapdict[dst_lett] = reg dst_lett = letter_inc(dst_lett) - return retstr,mapdict + return retstr, mapdict + def calculate_regid_letters(tag): - retstr,mapdict = calculate_regid_reg(tag) + retstr, mapdict = calculate_regid_reg(tag) return retstr + def strip_reg_prefix(x): - y=x.replace('UREG.','') - y=y.replace('MREG.','') - return y.replace('GREG.','') + y = x.replace("UREG.", "") + y = y.replace("MREG.", "") + return y.replace("GREG.", "") + def main(): hex_common.read_semantics_file(sys.argv[1]) hex_common.read_attribs_file(sys.argv[2]) - tagregs = hex_common.get_tagregs() + hex_common.init_registers() + tagregs = hex_common.get_tagregs(full=True) tagimms = hex_common.get_tagimms() - with open(sys.argv[3], 'w') as f: + with open(sys.argv[3], "w") as f: for tag in hex_common.tags: regs = tagregs[tag] rregs = [] wregs = [] regids = "" - for regtype,regid,toss,numregs in regs: - if hex_common.is_read(regid): - if regid[0] not in regids: regids += regid[0] - rregs.append(regtype+regid+numregs) - if hex_common.is_written(regid): - wregs.append(regtype+regid+numregs) - if regid[0] not in regids: regids += regid[0] + for regtype, regid, _, numregs in regs: + reg = hex_common.get_register(tag, regtype, regid) + if reg.is_read(): + if regid[0] not in regids: + regids += regid[0] + rregs.append(regtype + regid + numregs) + if reg.is_written(): + wregs.append(regtype + regid + numregs) + if regid[0] not in regids: + regids += regid[0] for attrib in hex_common.attribdict[tag]: - if hex_common.attribinfo[attrib]['rreg']: - rregs.append(strip_reg_prefix(attribinfo[attrib]['rreg'])) - if hex_common.attribinfo[attrib]['wreg']: - wregs.append(strip_reg_prefix(attribinfo[attrib]['wreg'])) + if hex_common.attribinfo[attrib]["rreg"]: + rregs.append(strip_reg_prefix(attribinfo[attrib]["rreg"])) + if hex_common.attribinfo[attrib]["wreg"]: + wregs.append(strip_reg_prefix(attribinfo[attrib]["wreg"])) regids += calculate_regid_letters(tag) - f.write('REGINFO(%s,"%s",\t/*RD:*/\t"%s",\t/*WR:*/\t"%s")\n' % \ - (tag,regids,",".join(rregs),",".join(wregs))) + f.write( + f'REGINFO({tag},"{regids}",\t/*RD:*/\t"{",".join(rregs)}",' + f'\t/*WR:*/\t"{",".join(wregs)}")\n' + ) for tag in hex_common.tags: imms = tagimms[tag] - f.write( 'IMMINFO(%s' % tag) + f.write(f"IMMINFO({tag}") if not imms: - f.write(''','u',0,0,'U',0,0''') - for sign,size,shamt in imms: - if sign == 'r': sign = 's' + f.write(""",'u',0,0,'U',0,0""") + for sign, size, shamt in imms: + if sign == "r": + sign = "s" if not shamt: shamt = "0" - f.write(''','%s',%s,%s''' % (sign,size,shamt)) + f.write(f""",'{sign}',{size},{shamt}""") if len(imms) == 1: if sign.isupper(): - myu = 'u' + myu = "u" else: - myu = 'U' - f.write(''','%s',0,0''' % myu) - f.write(')\n') + myu = "U" + f.write(f""",'{myu}',0,0""") + f.write(")\n") + if __name__ == "__main__": main() diff --git a/target/hexagon/gen_opcodes_def.py b/target/hexagon/gen_opcodes_def.py index fa604a8db9..cddd868fe3 100755 --- a/target/hexagon/gen_opcodes_def.py +++ b/target/hexagon/gen_opcodes_def.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -22,15 +22,17 @@ import re import string import hex_common + def main(): hex_common.read_semantics_file(sys.argv[1]) ## ## Generate a list of all the opcodes ## - with open(sys.argv[3], 'w') as f: + with open(sys.argv[3], "w") as f: for tag in hex_common.tags: - f.write ( "OPCODE(%s),\n" % (tag) ) + f.write(f"OPCODE({tag}),\n") + if __name__ == "__main__": main() diff --git a/target/hexagon/gen_printinsn.py b/target/hexagon/gen_printinsn.py index 12737bf8a0..e570bd7c6a 100755 --- a/target/hexagon/gen_printinsn.py +++ b/target/hexagon/gen_printinsn.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -22,24 +22,26 @@ import re import string import hex_common + ## ## Generate data for printing each instruction (format string + operands) ## def regprinter(m): str = m.group(1) - str += ":".join(["%d"]*len(m.group(2))) + str += ":".join(["%d"] * len(m.group(2))) str += m.group(3) - if ('S' in m.group(1)) and (len(m.group(2)) == 1): + if ("S" in m.group(1)) and (len(m.group(2)) == 1): str += "/%s" - elif ('C' in m.group(1)) and (len(m.group(2)) == 1): + elif ("C" in m.group(1)) and (len(m.group(2)) == 1): str += "/%s" return str + def spacify(s): # Regular expression that matches any operator that contains '=' character: - opswithequal_re = '[-+^&|!<>=]?=' + opswithequal_re = "[-+^&|!<>=]?=" # Regular expression that matches any assignment operator. - assignment_re = '[-+^&|]?=' + assignment_re = "[-+^&|]?=" # Out of the operators that contain the = sign, if the operator is also an # assignment, spaces will be added around it, unless it's enclosed within @@ -54,9 +56,9 @@ def spacify(s): pc = 0 while i < slen: c = s[i] - if c == '(': + if c == "(": pc += 1 - elif c == ')': + elif c == ")": pc -= 1 paren_count[i] = pc i += 1 @@ -76,31 +78,33 @@ def spacify(s): if paren_count[ms] == 0: # Check if the entire string t is an assignment. am = assign.match(t) - if am and len(am.group(0)) == me-ms: + if am and len(am.group(0)) == me - ms: # Don't add spaces if they are already there. - if ms > 0 and s[ms-1] != ' ': - out.append(' ') + if ms > 0 and s[ms - 1] != " ": + out.append(" ") out += t - if me < slen and s[me] != ' ': - out.append(' ') + if me < slen and s[me] != " ": + out.append(" ") continue # If this is not an assignment, just append it to the output # string. out += t # Append the remaining part of the string. - out += s[pos:len(s)] - return ''.join(out) + out += s[pos : len(s)] + return "".join(out) + def main(): hex_common.read_semantics_file(sys.argv[1]) hex_common.read_attribs_file(sys.argv[2]) - immext_casere = re.compile(r'IMMEXT\(([A-Za-z])') + immext_casere = re.compile(r"IMMEXT\(([A-Za-z])") - with open(sys.argv[3], 'w') as f: + with open(sys.argv[3], "w") as f: for tag in hex_common.tags: - if not hex_common.behdict[tag]: continue + if not hex_common.behdict[tag]: + continue extendable_upper_imm = False extendable_lower_imm = False m = immext_casere.search(hex_common.semdict[tag]) @@ -110,46 +114,45 @@ def main(): else: extendable_lower_imm = True beh = hex_common.behdict[tag] - beh = hex_common.regre.sub(regprinter,beh) - beh = hex_common.absimmre.sub(r"#%s0x%x",beh) - beh = hex_common.relimmre.sub(r"PC+%s%d",beh) + beh = hex_common.regre.sub(regprinter, beh) + beh = hex_common.absimmre.sub(r"#%s0x%x", beh) + beh = hex_common.relimmre.sub(r"PC+%s%d", beh) beh = spacify(beh) # Print out a literal "%s" at the end, used to match empty string # so C won't complain at us - if ("A_VECX" in hex_common.attribdict[tag]): + if "A_VECX" in hex_common.attribdict[tag]: macname = "DEF_VECX_PRINTINFO" - else: macname = "DEF_PRINTINFO" - f.write('%s(%s,"%s%%s"' % (macname,tag,beh)) - regs_or_imms = \ - hex_common.reg_or_immre.findall(hex_common.behdict[tag]) + else: + macname = "DEF_PRINTINFO" + f.write(f'{macname}({tag},"{beh}%s"') + regs_or_imms = hex_common.reg_or_immre.findall(hex_common.behdict[tag]) ri = 0 seenregs = {} - for allregs,a,b,c,d,allimm,immlett,bits,immshift in regs_or_imms: + for allregs, a, b, c, d, allimm, immlett, bits, immshift in regs_or_imms: if a: - #register + # register if b in seenregs: regno = seenregs[b] else: regno = ri if len(b) == 1: - f.write(', insn->regno[%d]' % regno) - if 'S' in a: - f.write(', sreg2str(insn->regno[%d])' % regno) - elif 'C' in a: - f.write(', creg2str(insn->regno[%d])' % regno) + f.write(f", insn->regno[{regno}]") + if "S" in a: + f.write(f", sreg2str(insn->regno[{regno}])") + elif "C" in a: + f.write(f", creg2str(insn->regno[{regno}])") elif len(b) == 2: - f.write(', insn->regno[%d] + 1, insn->regno[%d]' % \ - (regno,regno)) + f.write(f", insn->regno[{regno}] + 1" f", insn->regno[{regno}]") else: print("Put some stuff to handle quads here") if b not in seenregs: seenregs[b] = ri ri += 1 else: - #immediate - if (immlett.isupper()): + # immediate + if immlett.isupper(): if extendable_upper_imm: - if immlett in 'rR': + if immlett in "rR": f.write(',insn->extension_valid?"##":""') else: f.write(',insn->extension_valid?"#":""') @@ -158,16 +161,17 @@ def main(): ii = 1 else: if extendable_lower_imm: - if immlett in 'rR': + if immlett in "rR": f.write(',insn->extension_valid?"##":""') else: f.write(',insn->extension_valid?"#":""') else: f.write(',""') ii = 0 - f.write(', insn->immed[%d]' % ii) + f.write(f", insn->immed[{ii}]") # append empty string so there is at least one more arg f.write(',"")\n') + if __name__ == "__main__": main() diff --git a/target/hexagon/gen_shortcode.py b/target/hexagon/gen_shortcode.py index 9b589d0189..deb94446c4 100755 --- a/target/hexagon/gen_shortcode.py +++ b/target/hexagon/gen_shortcode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -22,8 +22,10 @@ import re import string import hex_common + def gen_shortcode(f, tag): - f.write('DEF_SHORTCODE(%s, %s)\n' % (tag, hex_common.semdict[tag])) + f.write(f"DEF_SHORTCODE({tag}, {hex_common.semdict[tag]})\n") + def main(): hex_common.read_semantics_file(sys.argv[1]) @@ -32,29 +34,30 @@ def main(): tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[3], 'w') as f: + with open(sys.argv[3], "w") as f: f.write("#ifndef DEF_SHORTCODE\n") f.write("#define DEF_SHORTCODE(TAG,SHORTCODE) /* Nothing */\n") f.write("#endif\n") for tag in hex_common.tags: ## Skip the priv instructions - if ( "A_PRIV" in hex_common.attribdict[tag] ) : + if "A_PRIV" in hex_common.attribdict[tag]: continue ## Skip the guest instructions - if ( "A_GUEST" in hex_common.attribdict[tag] ) : + if "A_GUEST" in hex_common.attribdict[tag]: continue ## Skip the diag instructions - if ( tag == "Y6_diag" ) : + if tag == "Y6_diag": continue - if ( tag == "Y6_diag0" ) : + if tag == "Y6_diag0": continue - if ( tag == "Y6_diag1" ) : + if tag == "Y6_diag1": continue gen_shortcode(f, tag) f.write("#undef DEF_SHORTCODE\n") + if __name__ == "__main__": main() diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h index c6f0879b6e..1c4391b415 100644 --- a/target/hexagon/gen_tcg.h +++ b/target/hexagon/gen_tcg.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -68,16 +68,14 @@ do { \ TCGv tcgv_siV = tcg_constant_tl(siV); \ tcg_gen_mov_tl(EA, RxV); \ - gen_helper_fcircadd(RxV, RxV, tcgv_siV, MuV, \ - hex_gpr[HEX_REG_CS0 + MuN]); \ + gen_helper_fcircadd(RxV, RxV, tcgv_siV, MuV, CS); \ } while (0) #define GET_EA_pcr(SHIFT) \ do { \ TCGv ireg = tcg_temp_new(); \ tcg_gen_mov_tl(EA, RxV); \ gen_read_ireg(ireg, MuV, (SHIFT)); \ - gen_helper_fcircadd(RxV, RxV, ireg, MuV, hex_gpr[HEX_REG_CS0 + MuN]); \ - tcg_temp_free(ireg); \ + gen_helper_fcircadd(RxV, RxV, ireg, MuV, CS); \ } while (0) /* Instructions with multiple definitions */ @@ -114,9 +112,8 @@ TCGv ireg = tcg_temp_new(); \ tcg_gen_mov_tl(EA, RxV); \ gen_read_ireg(ireg, MuV, SHIFT); \ - gen_helper_fcircadd(RxV, RxV, ireg, MuV, hex_gpr[HEX_REG_CS0 + MuN]); \ + gen_helper_fcircadd(RxV, RxV, ireg, MuV, CS); \ LOAD; \ - tcg_temp_free(ireg); \ } while (0) #define fGEN_TCG_L2_loadrub_pcr(SHORTCODE) \ @@ -168,8 +165,6 @@ for (int i = 0; i < 2; i++) { \ gen_set_half(i, RdV, gen_get_byte(byte, i, tmp, (SIGN))); \ } \ - tcg_temp_free(tmp); \ - tcg_temp_free(byte); \ } while (0) #define fGEN_TCG_L2_loadbzw2_io(SHORTCODE) \ @@ -222,8 +217,6 @@ for (int i = 0; i < 4; i++) { \ gen_set_half_i64(i, RddV, gen_get_byte(byte, i, tmp, (SIGN))); \ } \ - tcg_temp_free(tmp); \ - tcg_temp_free(byte); \ } while (0) #define fGEN_TCG_L2_loadbzw4_io(SHORTCODE) \ @@ -273,8 +266,6 @@ tcg_gen_extu_i32_i64(tmp_i64, tmp); \ tcg_gen_shri_i64(RyyV, RyyV, 16); \ tcg_gen_deposit_i64(RyyV, RyyV, tmp_i64, 48, 16); \ - tcg_temp_free(tmp); \ - tcg_temp_free_i64(tmp_i64); \ } while (0) #define fGEN_TCG_L4_loadalignh_ur(SHORTCODE) \ @@ -304,8 +295,6 @@ tcg_gen_extu_i32_i64(tmp_i64, tmp); \ tcg_gen_shri_i64(RyyV, RyyV, 8); \ tcg_gen_deposit_i64(RyyV, RyyV, tmp_i64, 56, 8); \ - tcg_temp_free(tmp); \ - tcg_temp_free_i64(tmp_i64); \ } while (0) #define fGEN_TCG_L2_loadalignb_io(SHORTCODE) \ @@ -337,16 +326,14 @@ */ #define fGEN_TCG_PRED_LOAD(GET_EA, PRED, SIZE, SIGN) \ do { \ - TCGv LSB = tcg_temp_local_new(); \ + TCGv LSB = tcg_temp_new(); \ TCGLabel *label = gen_new_label(); \ - GET_EA; \ + tcg_gen_movi_tl(EA, 0); \ PRED; \ - PRED_LOAD_CANCEL(LSB, EA); \ - tcg_gen_movi_tl(RdV, 0); \ + CHECK_NOSHUF_PRED(GET_EA, SIZE, LSB); \ tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, label); \ - fLOAD(1, SIZE, SIGN, EA, RdV); \ + fLOAD(1, SIZE, SIGN, EA, RdV); \ gen_set_label(label); \ - tcg_temp_free(LSB); \ } while (0) #define fGEN_TCG_L2_ploadrubt_pi(SHORTCODE) \ @@ -396,16 +383,14 @@ /* Predicated loads into a register pair */ #define fGEN_TCG_PRED_LOAD_PAIR(GET_EA, PRED) \ do { \ - TCGv LSB = tcg_temp_local_new(); \ + TCGv LSB = tcg_temp_new(); \ TCGLabel *label = gen_new_label(); \ - GET_EA; \ + tcg_gen_movi_tl(EA, 0); \ PRED; \ - PRED_LOAD_CANCEL(LSB, EA); \ - tcg_gen_movi_i64(RddV, 0); \ + CHECK_NOSHUF_PRED(GET_EA, 8, LSB); \ tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, label); \ - fLOAD(1, 8, u, EA, RddV); \ + fLOAD(1, 8, u, EA, RddV); \ gen_set_label(label); \ - tcg_temp_free(LSB); \ } while (0) #define fGEN_TCG_L2_ploadrdt_pi(SHORTCODE) \ @@ -429,25 +414,20 @@ #define fGEN_TCG_STORE(SHORTCODE) \ do { \ - TCGv HALF = tcg_temp_new(); \ - TCGv BYTE = tcg_temp_new(); \ + TCGv HALF G_GNUC_UNUSED = tcg_temp_new(); \ + TCGv BYTE G_GNUC_UNUSED = tcg_temp_new(); \ SHORTCODE; \ - tcg_temp_free(HALF); \ - tcg_temp_free(BYTE); \ } while (0) #define fGEN_TCG_STORE_pcr(SHIFT, STORE) \ do { \ TCGv ireg = tcg_temp_new(); \ - TCGv HALF = tcg_temp_new(); \ - TCGv BYTE = tcg_temp_new(); \ + TCGv HALF G_GNUC_UNUSED = tcg_temp_new(); \ + TCGv BYTE G_GNUC_UNUSED = tcg_temp_new(); \ tcg_gen_mov_tl(EA, RxV); \ gen_read_ireg(ireg, MuV, SHIFT); \ - gen_helper_fcircadd(RxV, RxV, ireg, MuV, hex_gpr[HEX_REG_CS0 + MuN]); \ + gen_helper_fcircadd(RxV, RxV, ireg, MuV, CS); \ STORE; \ - tcg_temp_free(ireg); \ - tcg_temp_free(HALF); \ - tcg_temp_free(BYTE); \ } while (0) #define fGEN_TCG_S2_storerb_pbr(SHORTCODE) \ @@ -506,14 +486,121 @@ #define fGEN_TCG_S2_storerinew_pcr(SHORTCODE) \ fGEN_TCG_STORE_pcr(2, fSTORE(1, 4, EA, NtN)) +/* dczeroa clears the 32 byte cache line at the address given */ +#define fGEN_TCG_Y2_dczeroa(SHORTCODE) SHORTCODE + +/* In linux-user mode, these are not modelled, suppress compiler warning */ +#define fGEN_TCG_Y2_dcinva(SHORTCODE) \ + do { RsV = RsV; } while (0) +#define fGEN_TCG_Y2_dccleaninva(SHORTCODE) \ + do { RsV = RsV; } while (0) +#define fGEN_TCG_Y2_dccleana(SHORTCODE) \ + do { RsV = RsV; } while (0) +#define fGEN_TCG_Y2_icinva(SHORTCODE) \ + do { RsV = RsV; } while (0) + +/* + * allocframe(#uiV) + * RxV == r29 + */ +#define fGEN_TCG_S2_allocframe(SHORTCODE) \ + gen_allocframe(ctx, RxV, uiV) + +/* sub-instruction version (no RxV, so handle it manually) */ +#define fGEN_TCG_SS2_allocframe(SHORTCODE) \ + do { \ + TCGv r29 = tcg_temp_new(); \ + tcg_gen_mov_tl(r29, hex_gpr[HEX_REG_SP]); \ + gen_allocframe(ctx, r29, uiV); \ + gen_log_reg_write(ctx, HEX_REG_SP, r29); \ + } while (0) + +/* + * Rdd32 = deallocframe(Rs32):raw + * RddV == r31:30 + * RsV == r30 + */ +#define fGEN_TCG_L2_deallocframe(SHORTCODE) \ + gen_deallocframe(ctx, RddV, RsV) + +/* sub-instruction version (no RddV/RsV, so handle it manually) */ +#define fGEN_TCG_SL2_deallocframe(SHORTCODE) \ + do { \ + TCGv_i64 r31_30 = tcg_temp_new_i64(); \ + gen_deallocframe(ctx, r31_30, hex_gpr[HEX_REG_FP]); \ + gen_log_reg_write_pair(ctx, HEX_REG_FP, r31_30); \ + } while (0) + +/* + * dealloc_return + * Assembler mapped to + * r31:30 = dealloc_return(r30):raw + */ +#define fGEN_TCG_L4_return(SHORTCODE) \ + gen_return(ctx, RddV, RsV) + +/* + * sub-instruction version (no RddV, so handle it manually) + */ +#define fGEN_TCG_SL2_return(SHORTCODE) \ + do { \ + TCGv_i64 RddV = get_result_gpr_pair(ctx, HEX_REG_FP); \ + gen_return(ctx, RddV, hex_gpr[HEX_REG_FP]); \ + gen_log_reg_write_pair(ctx, HEX_REG_FP, RddV); \ + } while (0) + +/* + * Conditional returns follow this naming convention + * _t predicate true + * _f predicate false + * _tnew_pt predicate.new true predict taken + * _fnew_pt predicate.new false predict taken + * _tnew_pnt predicate.new true predict not taken + * _fnew_pnt predicate.new false predict not taken + * Predictions are not modelled in QEMU + * + * Example: + * if (p1) r31:30 = dealloc_return(r30):raw + */ +#define fGEN_TCG_L4_return_t(SHORTCODE) \ + gen_cond_return(ctx, RddV, RsV, PvV, TCG_COND_EQ); +#define fGEN_TCG_L4_return_f(SHORTCODE) \ + gen_cond_return(ctx, RddV, RsV, PvV, TCG_COND_NE) +#define fGEN_TCG_L4_return_tnew_pt(SHORTCODE) \ + gen_cond_return(ctx, RddV, RsV, PvN, TCG_COND_EQ) +#define fGEN_TCG_L4_return_fnew_pt(SHORTCODE) \ + gen_cond_return(ctx, RddV, RsV, PvN, TCG_COND_NE) +#define fGEN_TCG_L4_return_tnew_pnt(SHORTCODE) \ + gen_cond_return(ctx, RddV, RsV, PvN, TCG_COND_EQ) +#define fGEN_TCG_L4_return_fnew_pnt(SHORTCODE) \ + gen_cond_return(ctx, RddV, RsV, PvN, TCG_COND_NE) + +#define fGEN_TCG_SL2_return_t(SHORTCODE) \ + gen_cond_return_subinsn(ctx, TCG_COND_EQ, hex_pred[0]) +#define fGEN_TCG_SL2_return_f(SHORTCODE) \ + gen_cond_return_subinsn(ctx, TCG_COND_NE, hex_pred[0]) +#define fGEN_TCG_SL2_return_tnew(SHORTCODE) \ + gen_cond_return_subinsn(ctx, TCG_COND_EQ, ctx->new_pred_value[0]) +#define fGEN_TCG_SL2_return_fnew(SHORTCODE) \ + gen_cond_return_subinsn(ctx, TCG_COND_NE, ctx->new_pred_value[0]) + /* * Mathematical operations with more than one definition require * special handling */ #define fGEN_TCG_A5_ACS(SHORTCODE) \ do { \ - gen_helper_vacsh_pred(PeV, cpu_env, RxxV, RssV, RttV); \ - gen_helper_vacsh_val(RxxV, cpu_env, RxxV, RssV, RttV); \ + gen_helper_vacsh_pred(PeV, tcg_env, RxxV, RssV, RttV); \ + gen_helper_vacsh_val(RxxV, tcg_env, RxxV, RssV, RttV, \ + tcg_constant_tl(ctx->need_commit)); \ + } while (0) + +#define fGEN_TCG_S2_cabacdecbin(SHORTCODE) \ + do { \ + TCGv p0 = tcg_temp_new(); \ + gen_helper_cabacdecbin_pred(p0, RssV, RttV); \ + gen_helper_cabacdecbin_val(RddV, RssV, RttV); \ + gen_log_pred_write(ctx, 0, p0); \ } while (0) /* @@ -526,10 +613,9 @@ #define fGEN_TCG_F2_sfrecipa(SHORTCODE) \ do { \ TCGv_i64 tmp = tcg_temp_new_i64(); \ - gen_helper_sfrecipa(tmp, cpu_env, RsV, RtV); \ + gen_helper_sfrecipa(tmp, tcg_env, RsV, RtV); \ tcg_gen_extrh_i64_i32(RdV, tmp); \ tcg_gen_extrl_i64_i32(PeV, tmp); \ - tcg_temp_free_i64(tmp); \ } while (0) /* @@ -542,10 +628,9 @@ #define fGEN_TCG_F2_sfinvsqrta(SHORTCODE) \ do { \ TCGv_i64 tmp = tcg_temp_new_i64(); \ - gen_helper_sfinvsqrta(tmp, cpu_env, RsV); \ + gen_helper_sfinvsqrta(tmp, tcg_env, RsV); \ tcg_gen_extrh_i64_i32(RdV, tmp); \ tcg_gen_extrl_i64_i32(PeV, tmp); \ - tcg_temp_free_i64(tmp); \ } while (0) /* @@ -563,7 +648,6 @@ tcg_gen_add2_i64(RddV, carry, RddV, carry, RttV, zero); \ tcg_gen_extrl_i64_i32(PxV, carry); \ gen_8bitsof(PxV, PxV); \ - tcg_temp_free_i64(carry); \ } while (0) /* r5:4 = sub(r1:0, r3:2, p1):carry */ @@ -579,8 +663,6 @@ tcg_gen_add2_i64(RddV, carry, RddV, carry, not_RttV, zero); \ tcg_gen_extrl_i64_i32(PxV, carry); \ gen_8bitsof(PxV, PxV); \ - tcg_temp_free_i64(carry); \ - tcg_temp_free_i64(not_RttV); \ } while (0) /* @@ -605,129 +687,639 @@ tcg_gen_umin_tl(tmp, left, right); \ gen_set_byte_i64(i, RddV, tmp); \ } \ - tcg_temp_free(left); \ - tcg_temp_free(right); \ - tcg_temp_free(tmp); \ } while (0) +#define fGEN_TCG_J2_call(SHORTCODE) \ + gen_call(ctx, riV) +#define fGEN_TCG_J2_callr(SHORTCODE) \ + gen_callr(ctx, RsV) +#define fGEN_TCG_J2_callrh(SHORTCODE) \ + gen_callr(ctx, RsV) + +#define fGEN_TCG_J2_callt(SHORTCODE) \ + gen_cond_call(ctx, PuV, TCG_COND_EQ, riV) +#define fGEN_TCG_J2_callf(SHORTCODE) \ + gen_cond_call(ctx, PuV, TCG_COND_NE, riV) +#define fGEN_TCG_J2_callrt(SHORTCODE) \ + gen_cond_callr(ctx, TCG_COND_EQ, PuV, RsV) +#define fGEN_TCG_J2_callrf(SHORTCODE) \ + gen_cond_callr(ctx, TCG_COND_NE, PuV, RsV) + +#define fGEN_TCG_J2_loop0r(SHORTCODE) \ + gen_loop0r(ctx, RsV, riV) +#define fGEN_TCG_J2_loop1r(SHORTCODE) \ + gen_loop1r(ctx, RsV, riV) +#define fGEN_TCG_J2_loop0i(SHORTCODE) \ + gen_loop0i(ctx, UiV, riV) +#define fGEN_TCG_J2_loop1i(SHORTCODE) \ + gen_loop1i(ctx, UiV, riV) +#define fGEN_TCG_J2_ploop1sr(SHORTCODE) \ + gen_ploopNsr(ctx, 1, RsV, riV) +#define fGEN_TCG_J2_ploop1si(SHORTCODE) \ + gen_ploopNsi(ctx, 1, UiV, riV) +#define fGEN_TCG_J2_ploop2sr(SHORTCODE) \ + gen_ploopNsr(ctx, 2, RsV, riV) +#define fGEN_TCG_J2_ploop2si(SHORTCODE) \ + gen_ploopNsi(ctx, 2, UiV, riV) +#define fGEN_TCG_J2_ploop3sr(SHORTCODE) \ + gen_ploopNsr(ctx, 3, RsV, riV) +#define fGEN_TCG_J2_ploop3si(SHORTCODE) \ + gen_ploopNsi(ctx, 3, UiV, riV) + +#define fGEN_TCG_J2_endloop0(SHORTCODE) \ + gen_endloop0(ctx) +#define fGEN_TCG_J2_endloop1(SHORTCODE) \ + gen_endloop1(ctx) +#define fGEN_TCG_J2_endloop01(SHORTCODE) \ + gen_endloop01(ctx) + +/* + * Compound compare and jump instructions + * Here is a primer to understand the tag names + * + * Comparison + * cmpeqi compare equal to an immediate + * cmpgti compare greater than an immediate + * cmpgtiu compare greater than an unsigned immediate + * cmpeqn1 compare equal to negative 1 + * cmpgtn1 compare greater than negative 1 + * cmpeq compare equal (two registers) + * cmpgtu compare greater than unsigned (two registers) + * tstbit0 test bit zero + * + * Condition + * tp0 p0 is true p0 = cmp.eq(r0,#5); if (p0.new) jump:nt address + * fp0 p0 is false p0 = cmp.eq(r0,#5); if (!p0.new) jump:nt address + * tp1 p1 is true p1 = cmp.eq(r0,#5); if (p1.new) jump:nt address + * fp1 p1 is false p1 = cmp.eq(r0,#5); if (!p1.new) jump:nt address + * + * Prediction (not modelled in qemu) + * _nt not taken + * _t taken + */ +#define fGEN_TCG_J4_cmpeq_tp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_jmp_t(ctx, 0, TCG_COND_EQ, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpeq_tp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_jmp_t(ctx, 0, TCG_COND_EQ, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpeq_fp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_jmp_f(ctx, 0, TCG_COND_EQ, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpeq_fp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_jmp_f(ctx, 0, TCG_COND_EQ, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpeq_tp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_jmp_t(ctx, 1, TCG_COND_EQ, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpeq_tp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_jmp_t(ctx, 1, TCG_COND_EQ, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpeq_fp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_jmp_f(ctx, 1, TCG_COND_EQ, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpeq_fp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_jmp_f(ctx, 1, TCG_COND_EQ, RsV, RtV, riV) + +#define fGEN_TCG_J4_cmpgt_tp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_jmp_t(ctx, 0, TCG_COND_GT, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpgt_tp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_jmp_t(ctx, 0, TCG_COND_GT, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpgt_fp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_jmp_f(ctx, 0, TCG_COND_GT, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpgt_fp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_jmp_f(ctx, 0, TCG_COND_GT, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpgt_tp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_jmp_t(ctx, 1, TCG_COND_GT, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpgt_tp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_jmp_t(ctx, 1, TCG_COND_GT, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpgt_fp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_jmp_f(ctx, 1, TCG_COND_GT, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpgt_fp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_jmp_f(ctx, 1, TCG_COND_GT, RsV, RtV, riV) + +#define fGEN_TCG_J4_cmpgtu_tp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_jmp_t(ctx, 0, TCG_COND_GTU, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpgtu_tp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_jmp_t(ctx, 0, TCG_COND_GTU, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpgtu_fp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_jmp_f(ctx, 0, TCG_COND_GTU, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpgtu_fp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_jmp_f(ctx, 0, TCG_COND_GTU, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpgtu_tp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_jmp_t(ctx, 1, TCG_COND_GTU, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpgtu_tp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_jmp_t(ctx, 1, TCG_COND_GTU, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpgtu_fp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_jmp_f(ctx, 1, TCG_COND_GTU, RsV, RtV, riV) +#define fGEN_TCG_J4_cmpgtu_fp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_jmp_f(ctx, 1, TCG_COND_GTU, RsV, RtV, riV) + +#define fGEN_TCG_J4_cmpeqi_tp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_t(ctx, 0, TCG_COND_EQ, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpeqi_tp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_t(ctx, 0, TCG_COND_EQ, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpeqi_fp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_f(ctx, 0, TCG_COND_EQ, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpeqi_fp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_f(ctx, 0, TCG_COND_EQ, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpeqi_tp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_t(ctx, 1, TCG_COND_EQ, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpeqi_tp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_t(ctx, 1, TCG_COND_EQ, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpeqi_fp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_f(ctx, 1, TCG_COND_EQ, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpeqi_fp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_f(ctx, 1, TCG_COND_EQ, RsV, UiV, riV) + +#define fGEN_TCG_J4_cmpgti_tp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_t(ctx, 0, TCG_COND_GT, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpgti_tp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_t(ctx, 0, TCG_COND_GT, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpgti_fp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_f(ctx, 0, TCG_COND_GT, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpgti_fp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_f(ctx, 0, TCG_COND_GT, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpgti_tp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_t(ctx, 1, TCG_COND_GT, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpgti_tp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_t(ctx, 1, TCG_COND_GT, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpgti_fp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_f(ctx, 1, TCG_COND_GT, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpgti_fp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_f(ctx, 1, TCG_COND_GT, RsV, UiV, riV) + +#define fGEN_TCG_J4_cmpgtui_tp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_t(ctx, 0, TCG_COND_GTU, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpgtui_tp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_t(ctx, 0, TCG_COND_GTU, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpgtui_fp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_f(ctx, 0, TCG_COND_GTU, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpgtui_fp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_f(ctx, 0, TCG_COND_GTU, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpgtui_tp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_t(ctx, 1, TCG_COND_GTU, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpgtui_tp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_t(ctx, 1, TCG_COND_GTU, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpgtui_fp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_f(ctx, 1, TCG_COND_GTU, RsV, UiV, riV) +#define fGEN_TCG_J4_cmpgtui_fp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmpi_jmp_f(ctx, 1, TCG_COND_GTU, RsV, UiV, riV) + +#define fGEN_TCG_J4_cmpeqn1_tp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_t(ctx, 0, TCG_COND_EQ, RsV, riV) +#define fGEN_TCG_J4_cmpeqn1_tp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_t(ctx, 0, TCG_COND_EQ, RsV, riV) +#define fGEN_TCG_J4_cmpeqn1_fp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_f(ctx, 0, TCG_COND_EQ, RsV, riV) +#define fGEN_TCG_J4_cmpeqn1_fp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_f(ctx, 0, TCG_COND_EQ, RsV, riV) +#define fGEN_TCG_J4_cmpeqn1_tp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_t(ctx, 1, TCG_COND_EQ, RsV, riV) +#define fGEN_TCG_J4_cmpeqn1_tp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_t(ctx, 1, TCG_COND_EQ, RsV, riV) +#define fGEN_TCG_J4_cmpeqn1_fp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_f(ctx, 1, TCG_COND_EQ, RsV, riV) +#define fGEN_TCG_J4_cmpeqn1_fp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_f(ctx, 1, TCG_COND_EQ, RsV, riV) + +#define fGEN_TCG_J4_cmpgtn1_tp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_t(ctx, 0, TCG_COND_GT, RsV, riV) +#define fGEN_TCG_J4_cmpgtn1_tp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_t(ctx, 0, TCG_COND_GT, RsV, riV) +#define fGEN_TCG_J4_cmpgtn1_fp0_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_f(ctx, 0, TCG_COND_GT, RsV, riV) +#define fGEN_TCG_J4_cmpgtn1_fp0_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_f(ctx, 0, TCG_COND_GT, RsV, riV) +#define fGEN_TCG_J4_cmpgtn1_tp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_t(ctx, 1, TCG_COND_GT, RsV, riV) +#define fGEN_TCG_J4_cmpgtn1_tp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_t(ctx, 1, TCG_COND_GT, RsV, riV) +#define fGEN_TCG_J4_cmpgtn1_fp1_jump_t(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_f(ctx, 1, TCG_COND_GT, RsV, riV) +#define fGEN_TCG_J4_cmpgtn1_fp1_jump_nt(SHORTCODE) \ + gen_cmpnd_cmp_n1_jmp_f(ctx, 1, TCG_COND_GT, RsV, riV) + +#define fGEN_TCG_J4_tstbit0_tp0_jump_nt(SHORTCODE) \ + gen_cmpnd_tstbit0_jmp(ctx, 0, RsV, TCG_COND_EQ, riV) +#define fGEN_TCG_J4_tstbit0_tp0_jump_t(SHORTCODE) \ + gen_cmpnd_tstbit0_jmp(ctx, 0, RsV, TCG_COND_EQ, riV) +#define fGEN_TCG_J4_tstbit0_fp0_jump_nt(SHORTCODE) \ + gen_cmpnd_tstbit0_jmp(ctx, 0, RsV, TCG_COND_NE, riV) +#define fGEN_TCG_J4_tstbit0_fp0_jump_t(SHORTCODE) \ + gen_cmpnd_tstbit0_jmp(ctx, 0, RsV, TCG_COND_NE, riV) +#define fGEN_TCG_J4_tstbit0_tp1_jump_nt(SHORTCODE) \ + gen_cmpnd_tstbit0_jmp(ctx, 1, RsV, TCG_COND_EQ, riV) +#define fGEN_TCG_J4_tstbit0_tp1_jump_t(SHORTCODE) \ + gen_cmpnd_tstbit0_jmp(ctx, 1, RsV, TCG_COND_EQ, riV) +#define fGEN_TCG_J4_tstbit0_fp1_jump_nt(SHORTCODE) \ + gen_cmpnd_tstbit0_jmp(ctx, 1, RsV, TCG_COND_NE, riV) +#define fGEN_TCG_J4_tstbit0_fp1_jump_t(SHORTCODE) \ + gen_cmpnd_tstbit0_jmp(ctx, 1, RsV, TCG_COND_NE, riV) + +/* p0 = cmp.eq(r0, #7) */ +#define fGEN_TCG_SA1_cmpeqi(SHORTCODE) \ + do { \ + TCGv p0 = tcg_temp_new(); \ + gen_comparei(TCG_COND_EQ, p0, RsV, uiV); \ + gen_log_pred_write(ctx, 0, p0); \ + } while (0) + +#define fGEN_TCG_J2_jump(SHORTCODE) \ + gen_jump(ctx, riV) +#define fGEN_TCG_J2_jumpr(SHORTCODE) \ + gen_jumpr(ctx, RsV) +#define fGEN_TCG_J2_jumprh(SHORTCODE) \ + gen_jumpr(ctx, RsV) +#define fGEN_TCG_J4_jumpseti(SHORTCODE) \ + do { \ + tcg_gen_movi_tl(RdV, UiV); \ + gen_jump(ctx, riV); \ + } while (0) + +#define fGEN_TCG_cond_jumpt(COND) \ + do { \ + TCGv LSB = tcg_temp_new(); \ + COND; \ + gen_cond_jump(ctx, TCG_COND_EQ, LSB, riV); \ + } while (0) +#define fGEN_TCG_cond_jumpf(COND) \ + do { \ + TCGv LSB = tcg_temp_new(); \ + COND; \ + gen_cond_jump(ctx, TCG_COND_NE, LSB, riV); \ + } while (0) + +#define fGEN_TCG_J2_jumpt(SHORTCODE) \ + fGEN_TCG_cond_jumpt(fLSBOLD(PuV)) +#define fGEN_TCG_J2_jumptpt(SHORTCODE) \ + fGEN_TCG_cond_jumpt(fLSBOLD(PuV)) +#define fGEN_TCG_J2_jumpf(SHORTCODE) \ + fGEN_TCG_cond_jumpf(fLSBOLD(PuV)) +#define fGEN_TCG_J2_jumpfpt(SHORTCODE) \ + fGEN_TCG_cond_jumpf(fLSBOLD(PuV)) +#define fGEN_TCG_J2_jumptnew(SHORTCODE) \ + gen_cond_jump(ctx, TCG_COND_EQ, PuN, riV) +#define fGEN_TCG_J2_jumptnewpt(SHORTCODE) \ + gen_cond_jump(ctx, TCG_COND_EQ, PuN, riV) +#define fGEN_TCG_J2_jumpfnewpt(SHORTCODE) \ + fGEN_TCG_cond_jumpf(fLSBNEW(PuN)) +#define fGEN_TCG_J2_jumpfnew(SHORTCODE) \ + fGEN_TCG_cond_jumpf(fLSBNEW(PuN)) +#define fGEN_TCG_J2_jumprz(SHORTCODE) \ + fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_NE, LSB, RsV, 0)) +#define fGEN_TCG_J2_jumprzpt(SHORTCODE) \ + fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_NE, LSB, RsV, 0)) +#define fGEN_TCG_J2_jumprnz(SHORTCODE) \ + fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_EQ, LSB, RsV, 0)) +#define fGEN_TCG_J2_jumprnzpt(SHORTCODE) \ + fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_EQ, LSB, RsV, 0)) +#define fGEN_TCG_J2_jumprgtez(SHORTCODE) \ + fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_GE, LSB, RsV, 0)) +#define fGEN_TCG_J2_jumprgtezpt(SHORTCODE) \ + fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_GE, LSB, RsV, 0)) +#define fGEN_TCG_J2_jumprltez(SHORTCODE) \ + fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_LE, LSB, RsV, 0)) +#define fGEN_TCG_J2_jumprltezpt(SHORTCODE) \ + fGEN_TCG_cond_jumpt(tcg_gen_setcondi_tl(TCG_COND_LE, LSB, RsV, 0)) + +#define fGEN_TCG_cond_jumprt(COND) \ + do { \ + TCGv LSB = tcg_temp_new(); \ + COND; \ + gen_cond_jumpr(ctx, RsV, TCG_COND_EQ, LSB); \ + } while (0) +#define fGEN_TCG_cond_jumprf(COND) \ + do { \ + TCGv LSB = tcg_temp_new(); \ + COND; \ + gen_cond_jumpr(ctx, RsV, TCG_COND_NE, LSB); \ + } while (0) + +#define fGEN_TCG_J2_jumprt(SHORTCODE) \ + fGEN_TCG_cond_jumprt(fLSBOLD(PuV)) +#define fGEN_TCG_J2_jumprtpt(SHORTCODE) \ + fGEN_TCG_cond_jumprt(fLSBOLD(PuV)) +#define fGEN_TCG_J2_jumprf(SHORTCODE) \ + fGEN_TCG_cond_jumprf(fLSBOLD(PuV)) +#define fGEN_TCG_J2_jumprfpt(SHORTCODE) \ + fGEN_TCG_cond_jumprf(fLSBOLD(PuV)) +#define fGEN_TCG_J2_jumprtnew(SHORTCODE) \ + fGEN_TCG_cond_jumprt(fLSBNEW(PuN)) +#define fGEN_TCG_J2_jumprtnewpt(SHORTCODE) \ + fGEN_TCG_cond_jumprt(fLSBNEW(PuN)) +#define fGEN_TCG_J2_jumprfnew(SHORTCODE) \ + fGEN_TCG_cond_jumprf(fLSBNEW(PuN)) +#define fGEN_TCG_J2_jumprfnewpt(SHORTCODE) \ + fGEN_TCG_cond_jumprf(fLSBNEW(PuN)) + +/* + * New value compare & jump instructions + * if ([!]COND(r0.new, r1) jump:t address + * if ([!]COND(r0.new, #7) jump:t address + */ +#define fGEN_TCG_J4_cmpgt_t_jumpnv_t(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_GT, NsN, RtV, riV) +#define fGEN_TCG_J4_cmpgt_t_jumpnv_nt(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_GT, NsN, RtV, riV) +#define fGEN_TCG_J4_cmpgt_f_jumpnv_t(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_LE, NsN, RtV, riV) +#define fGEN_TCG_J4_cmpgt_f_jumpnv_nt(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_LE, NsN, RtV, riV) + +#define fGEN_TCG_J4_cmpeq_t_jumpnv_t(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_EQ, NsN, RtV, riV) +#define fGEN_TCG_J4_cmpeq_t_jumpnv_nt(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_EQ, NsN, RtV, riV) +#define fGEN_TCG_J4_cmpeq_f_jumpnv_t(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_NE, NsN, RtV, riV) +#define fGEN_TCG_J4_cmpeq_f_jumpnv_nt(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_NE, NsN, RtV, riV) + +#define fGEN_TCG_J4_cmplt_t_jumpnv_t(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_LT, NsN, RtV, riV) +#define fGEN_TCG_J4_cmplt_t_jumpnv_nt(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_LT, NsN, RtV, riV) +#define fGEN_TCG_J4_cmplt_f_jumpnv_t(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_GE, NsN, RtV, riV) +#define fGEN_TCG_J4_cmplt_f_jumpnv_nt(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_GE, NsN, RtV, riV) + +#define fGEN_TCG_J4_cmpeqi_t_jumpnv_t(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_EQ, NsN, UiV, riV) +#define fGEN_TCG_J4_cmpeqi_t_jumpnv_nt(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_EQ, NsN, UiV, riV) +#define fGEN_TCG_J4_cmpeqi_f_jumpnv_t(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_NE, NsN, UiV, riV) +#define fGEN_TCG_J4_cmpeqi_f_jumpnv_nt(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_NE, NsN, UiV, riV) + +#define fGEN_TCG_J4_cmpgti_t_jumpnv_t(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_GT, NsN, UiV, riV) +#define fGEN_TCG_J4_cmpgti_t_jumpnv_nt(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_GT, NsN, UiV, riV) +#define fGEN_TCG_J4_cmpgti_f_jumpnv_t(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_LE, NsN, UiV, riV) +#define fGEN_TCG_J4_cmpgti_f_jumpnv_nt(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_LE, NsN, UiV, riV) + +#define fGEN_TCG_J4_cmpltu_t_jumpnv_t(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_LTU, NsN, RtV, riV) +#define fGEN_TCG_J4_cmpltu_t_jumpnv_nt(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_LTU, NsN, RtV, riV) +#define fGEN_TCG_J4_cmpltu_f_jumpnv_t(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_GEU, NsN, RtV, riV) +#define fGEN_TCG_J4_cmpltu_f_jumpnv_nt(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_GEU, NsN, RtV, riV) + +#define fGEN_TCG_J4_cmpgtui_t_jumpnv_t(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_GTU, NsN, UiV, riV) +#define fGEN_TCG_J4_cmpgtui_t_jumpnv_nt(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_GTU, NsN, UiV, riV) +#define fGEN_TCG_J4_cmpgtui_f_jumpnv_t(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_LEU, NsN, UiV, riV) +#define fGEN_TCG_J4_cmpgtui_f_jumpnv_nt(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_LEU, NsN, UiV, riV) + +#define fGEN_TCG_J4_cmpgtu_t_jumpnv_t(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_GTU, NsN, RtV, riV) +#define fGEN_TCG_J4_cmpgtu_t_jumpnv_nt(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_GTU, NsN, RtV, riV) +#define fGEN_TCG_J4_cmpgtu_f_jumpnv_t(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_LEU, NsN, RtV, riV) +#define fGEN_TCG_J4_cmpgtu_f_jumpnv_nt(SHORTCODE) \ + gen_cmp_jumpnv(ctx, TCG_COND_LEU, NsN, RtV, riV) + +#define fGEN_TCG_J4_cmpeqn1_t_jumpnv_t(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_EQ, NsN, -1, riV) +#define fGEN_TCG_J4_cmpeqn1_t_jumpnv_nt(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_EQ, NsN, -1, riV) +#define fGEN_TCG_J4_cmpeqn1_f_jumpnv_t(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_NE, NsN, -1, riV) +#define fGEN_TCG_J4_cmpeqn1_f_jumpnv_nt(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_NE, NsN, -1, riV) + +#define fGEN_TCG_J4_cmpgtn1_t_jumpnv_t(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_GT, NsN, -1, riV) +#define fGEN_TCG_J4_cmpgtn1_t_jumpnv_nt(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_GT, NsN, -1, riV) +#define fGEN_TCG_J4_cmpgtn1_f_jumpnv_t(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_LE, NsN, -1, riV) +#define fGEN_TCG_J4_cmpgtn1_f_jumpnv_nt(SHORTCODE) \ + gen_cmpi_jumpnv(ctx, TCG_COND_LE, NsN, -1, riV) + +#define fGEN_TCG_J4_tstbit0_t_jumpnv_t(SHORTCODE) \ + gen_testbit0_jumpnv(ctx, NsN, TCG_COND_EQ, riV) +#define fGEN_TCG_J4_tstbit0_t_jumpnv_nt(SHORTCODE) \ + gen_testbit0_jumpnv(ctx, NsN, TCG_COND_EQ, riV) +#define fGEN_TCG_J4_tstbit0_f_jumpnv_t(SHORTCODE) \ + gen_testbit0_jumpnv(ctx, NsN, TCG_COND_NE, riV) +#define fGEN_TCG_J4_tstbit0_f_jumpnv_nt(SHORTCODE) \ + gen_testbit0_jumpnv(ctx, NsN, TCG_COND_NE, riV) + +/* r0 = r1 ; jump address */ +#define fGEN_TCG_J4_jumpsetr(SHORTCODE) \ + do { \ + tcg_gen_mov_tl(RdV, RsV); \ + gen_jump(ctx, riV); \ + } while (0) + +/* if (p0.new) r0 = #0 */ +#define fGEN_TCG_SA1_clrtnew(SHORTCODE) \ + do { \ + tcg_gen_movcond_tl(TCG_COND_EQ, RdV, \ + ctx->new_pred_value[0], tcg_constant_tl(0), \ + RdV, tcg_constant_tl(0)); \ + } while (0) + +/* if (!p0.new) r0 = #0 */ +#define fGEN_TCG_SA1_clrfnew(SHORTCODE) \ + do { \ + tcg_gen_movcond_tl(TCG_COND_NE, RdV, \ + ctx->new_pred_value[0], tcg_constant_tl(0), \ + RdV, tcg_constant_tl(0)); \ + } while (0) + +#define fGEN_TCG_J2_pause(SHORTCODE) \ + do { \ + uiV = uiV; \ + tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->next_PC); \ + } while (0) + +/* r0 = asr(r1, r2):sat */ +#define fGEN_TCG_S2_asr_r_r_sat(SHORTCODE) \ + gen_asr_r_r_sat(ctx, RdV, RsV, RtV) + +/* r0 = asl(r1, r2):sat */ +#define fGEN_TCG_S2_asl_r_r_sat(SHORTCODE) \ + gen_asl_r_r_sat(ctx, RdV, RsV, RtV) + +#define fGEN_TCG_SL2_jumpr31(SHORTCODE) \ + gen_jumpr(ctx, hex_gpr[HEX_REG_LR]) + +#define fGEN_TCG_SL2_jumpr31_t(SHORTCODE) \ + gen_cond_jumpr31(ctx, TCG_COND_EQ, hex_pred[0]) +#define fGEN_TCG_SL2_jumpr31_f(SHORTCODE) \ + gen_cond_jumpr31(ctx, TCG_COND_NE, hex_pred[0]) + +#define fGEN_TCG_SL2_jumpr31_tnew(SHORTCODE) \ + gen_cond_jumpr31(ctx, TCG_COND_EQ, ctx->new_pred_value[0]) +#define fGEN_TCG_SL2_jumpr31_fnew(SHORTCODE) \ + gen_cond_jumpr31(ctx, TCG_COND_NE, ctx->new_pred_value[0]) + +/* Count trailing zeros/ones */ +#define fGEN_TCG_S2_ct0(SHORTCODE) \ + do { \ + tcg_gen_ctzi_tl(RdV, RsV, 32); \ + } while (0) +#define fGEN_TCG_S2_ct1(SHORTCODE) \ + do { \ + tcg_gen_not_tl(RdV, RsV); \ + tcg_gen_ctzi_tl(RdV, RdV, 32); \ + } while (0) +#define fGEN_TCG_S2_ct0p(SHORTCODE) \ + do { \ + TCGv_i64 tmp = tcg_temp_new_i64(); \ + tcg_gen_ctzi_i64(tmp, RssV, 64); \ + tcg_gen_extrl_i64_i32(RdV, tmp); \ + } while (0) +#define fGEN_TCG_S2_ct1p(SHORTCODE) \ + do { \ + TCGv_i64 tmp = tcg_temp_new_i64(); \ + tcg_gen_not_i64(tmp, RssV); \ + tcg_gen_ctzi_i64(tmp, tmp, 64); \ + tcg_gen_extrl_i64_i32(RdV, tmp); \ + } while (0) + +#define fGEN_TCG_S2_insert(SHORTCODE) \ + do { \ + int width = uiV; \ + int offset = UiV; \ + if (width != 0) { \ + if (offset + width > 32) { \ + width = 32 - offset; \ + } \ + tcg_gen_deposit_tl(RxV, RxV, RsV, offset, width); \ + } \ + } while (0) +#define fGEN_TCG_S2_insert_rp(SHORTCODE) \ + gen_insert_rp(ctx, RxV, RsV, RttV) +#define fGEN_TCG_S2_asr_r_svw_trun(SHORTCODE) \ + gen_asr_r_svw_trun(ctx, RdV, RssV, RtV) +#define fGEN_TCG_A2_swiz(SHORTCODE) \ + tcg_gen_bswap_tl(RdV, RsV) + /* Floating point */ #define fGEN_TCG_F2_conv_sf2df(SHORTCODE) \ - gen_helper_conv_sf2df(RddV, cpu_env, RsV) + gen_helper_conv_sf2df(RddV, tcg_env, RsV) #define fGEN_TCG_F2_conv_df2sf(SHORTCODE) \ - gen_helper_conv_df2sf(RdV, cpu_env, RssV) + gen_helper_conv_df2sf(RdV, tcg_env, RssV) #define fGEN_TCG_F2_conv_uw2sf(SHORTCODE) \ - gen_helper_conv_uw2sf(RdV, cpu_env, RsV) + gen_helper_conv_uw2sf(RdV, tcg_env, RsV) #define fGEN_TCG_F2_conv_uw2df(SHORTCODE) \ - gen_helper_conv_uw2df(RddV, cpu_env, RsV) + gen_helper_conv_uw2df(RddV, tcg_env, RsV) #define fGEN_TCG_F2_conv_w2sf(SHORTCODE) \ - gen_helper_conv_w2sf(RdV, cpu_env, RsV) + gen_helper_conv_w2sf(RdV, tcg_env, RsV) #define fGEN_TCG_F2_conv_w2df(SHORTCODE) \ - gen_helper_conv_w2df(RddV, cpu_env, RsV) + gen_helper_conv_w2df(RddV, tcg_env, RsV) #define fGEN_TCG_F2_conv_ud2sf(SHORTCODE) \ - gen_helper_conv_ud2sf(RdV, cpu_env, RssV) + gen_helper_conv_ud2sf(RdV, tcg_env, RssV) #define fGEN_TCG_F2_conv_ud2df(SHORTCODE) \ - gen_helper_conv_ud2df(RddV, cpu_env, RssV) + gen_helper_conv_ud2df(RddV, tcg_env, RssV) #define fGEN_TCG_F2_conv_d2sf(SHORTCODE) \ - gen_helper_conv_d2sf(RdV, cpu_env, RssV) + gen_helper_conv_d2sf(RdV, tcg_env, RssV) #define fGEN_TCG_F2_conv_d2df(SHORTCODE) \ - gen_helper_conv_d2df(RddV, cpu_env, RssV) + gen_helper_conv_d2df(RddV, tcg_env, RssV) #define fGEN_TCG_F2_conv_sf2uw(SHORTCODE) \ - gen_helper_conv_sf2uw(RdV, cpu_env, RsV) + gen_helper_conv_sf2uw(RdV, tcg_env, RsV) #define fGEN_TCG_F2_conv_sf2w(SHORTCODE) \ - gen_helper_conv_sf2w(RdV, cpu_env, RsV) + gen_helper_conv_sf2w(RdV, tcg_env, RsV) #define fGEN_TCG_F2_conv_sf2ud(SHORTCODE) \ - gen_helper_conv_sf2ud(RddV, cpu_env, RsV) + gen_helper_conv_sf2ud(RddV, tcg_env, RsV) #define fGEN_TCG_F2_conv_sf2d(SHORTCODE) \ - gen_helper_conv_sf2d(RddV, cpu_env, RsV) + gen_helper_conv_sf2d(RddV, tcg_env, RsV) #define fGEN_TCG_F2_conv_df2uw(SHORTCODE) \ - gen_helper_conv_df2uw(RdV, cpu_env, RssV) + gen_helper_conv_df2uw(RdV, tcg_env, RssV) #define fGEN_TCG_F2_conv_df2w(SHORTCODE) \ - gen_helper_conv_df2w(RdV, cpu_env, RssV) + gen_helper_conv_df2w(RdV, tcg_env, RssV) #define fGEN_TCG_F2_conv_df2ud(SHORTCODE) \ - gen_helper_conv_df2ud(RddV, cpu_env, RssV) + gen_helper_conv_df2ud(RddV, tcg_env, RssV) #define fGEN_TCG_F2_conv_df2d(SHORTCODE) \ - gen_helper_conv_df2d(RddV, cpu_env, RssV) + gen_helper_conv_df2d(RddV, tcg_env, RssV) #define fGEN_TCG_F2_conv_sf2uw_chop(SHORTCODE) \ - gen_helper_conv_sf2uw_chop(RdV, cpu_env, RsV) + gen_helper_conv_sf2uw_chop(RdV, tcg_env, RsV) #define fGEN_TCG_F2_conv_sf2w_chop(SHORTCODE) \ - gen_helper_conv_sf2w_chop(RdV, cpu_env, RsV) + gen_helper_conv_sf2w_chop(RdV, tcg_env, RsV) #define fGEN_TCG_F2_conv_sf2ud_chop(SHORTCODE) \ - gen_helper_conv_sf2ud_chop(RddV, cpu_env, RsV) + gen_helper_conv_sf2ud_chop(RddV, tcg_env, RsV) #define fGEN_TCG_F2_conv_sf2d_chop(SHORTCODE) \ - gen_helper_conv_sf2d_chop(RddV, cpu_env, RsV) + gen_helper_conv_sf2d_chop(RddV, tcg_env, RsV) #define fGEN_TCG_F2_conv_df2uw_chop(SHORTCODE) \ - gen_helper_conv_df2uw_chop(RdV, cpu_env, RssV) + gen_helper_conv_df2uw_chop(RdV, tcg_env, RssV) #define fGEN_TCG_F2_conv_df2w_chop(SHORTCODE) \ - gen_helper_conv_df2w_chop(RdV, cpu_env, RssV) + gen_helper_conv_df2w_chop(RdV, tcg_env, RssV) #define fGEN_TCG_F2_conv_df2ud_chop(SHORTCODE) \ - gen_helper_conv_df2ud_chop(RddV, cpu_env, RssV) + gen_helper_conv_df2ud_chop(RddV, tcg_env, RssV) #define fGEN_TCG_F2_conv_df2d_chop(SHORTCODE) \ - gen_helper_conv_df2d_chop(RddV, cpu_env, RssV) + gen_helper_conv_df2d_chop(RddV, tcg_env, RssV) #define fGEN_TCG_F2_sfadd(SHORTCODE) \ - gen_helper_sfadd(RdV, cpu_env, RsV, RtV) + gen_helper_sfadd(RdV, tcg_env, RsV, RtV) #define fGEN_TCG_F2_sfsub(SHORTCODE) \ - gen_helper_sfsub(RdV, cpu_env, RsV, RtV) + gen_helper_sfsub(RdV, tcg_env, RsV, RtV) #define fGEN_TCG_F2_sfcmpeq(SHORTCODE) \ - gen_helper_sfcmpeq(PdV, cpu_env, RsV, RtV) + gen_helper_sfcmpeq(PdV, tcg_env, RsV, RtV) #define fGEN_TCG_F2_sfcmpgt(SHORTCODE) \ - gen_helper_sfcmpgt(PdV, cpu_env, RsV, RtV) + gen_helper_sfcmpgt(PdV, tcg_env, RsV, RtV) #define fGEN_TCG_F2_sfcmpge(SHORTCODE) \ - gen_helper_sfcmpge(PdV, cpu_env, RsV, RtV) + gen_helper_sfcmpge(PdV, tcg_env, RsV, RtV) #define fGEN_TCG_F2_sfcmpuo(SHORTCODE) \ - gen_helper_sfcmpuo(PdV, cpu_env, RsV, RtV) + gen_helper_sfcmpuo(PdV, tcg_env, RsV, RtV) #define fGEN_TCG_F2_sfmax(SHORTCODE) \ - gen_helper_sfmax(RdV, cpu_env, RsV, RtV) + gen_helper_sfmax(RdV, tcg_env, RsV, RtV) #define fGEN_TCG_F2_sfmin(SHORTCODE) \ - gen_helper_sfmin(RdV, cpu_env, RsV, RtV) + gen_helper_sfmin(RdV, tcg_env, RsV, RtV) #define fGEN_TCG_F2_sfclass(SHORTCODE) \ do { \ TCGv imm = tcg_constant_tl(uiV); \ - gen_helper_sfclass(PdV, cpu_env, RsV, imm); \ + gen_helper_sfclass(PdV, tcg_env, RsV, imm); \ } while (0) #define fGEN_TCG_F2_sffixupn(SHORTCODE) \ - gen_helper_sffixupn(RdV, cpu_env, RsV, RtV) + gen_helper_sffixupn(RdV, tcg_env, RsV, RtV) #define fGEN_TCG_F2_sffixupd(SHORTCODE) \ - gen_helper_sffixupd(RdV, cpu_env, RsV, RtV) + gen_helper_sffixupd(RdV, tcg_env, RsV, RtV) #define fGEN_TCG_F2_sffixupr(SHORTCODE) \ - gen_helper_sffixupr(RdV, cpu_env, RsV) + gen_helper_sffixupr(RdV, tcg_env, RsV) #define fGEN_TCG_F2_dfadd(SHORTCODE) \ - gen_helper_dfadd(RddV, cpu_env, RssV, RttV) + gen_helper_dfadd(RddV, tcg_env, RssV, RttV) #define fGEN_TCG_F2_dfsub(SHORTCODE) \ - gen_helper_dfsub(RddV, cpu_env, RssV, RttV) + gen_helper_dfsub(RddV, tcg_env, RssV, RttV) #define fGEN_TCG_F2_dfmax(SHORTCODE) \ - gen_helper_dfmax(RddV, cpu_env, RssV, RttV) + gen_helper_dfmax(RddV, tcg_env, RssV, RttV) #define fGEN_TCG_F2_dfmin(SHORTCODE) \ - gen_helper_dfmin(RddV, cpu_env, RssV, RttV) + gen_helper_dfmin(RddV, tcg_env, RssV, RttV) #define fGEN_TCG_F2_dfcmpeq(SHORTCODE) \ - gen_helper_dfcmpeq(PdV, cpu_env, RssV, RttV) + gen_helper_dfcmpeq(PdV, tcg_env, RssV, RttV) #define fGEN_TCG_F2_dfcmpgt(SHORTCODE) \ - gen_helper_dfcmpgt(PdV, cpu_env, RssV, RttV) + gen_helper_dfcmpgt(PdV, tcg_env, RssV, RttV) #define fGEN_TCG_F2_dfcmpge(SHORTCODE) \ - gen_helper_dfcmpge(PdV, cpu_env, RssV, RttV) + gen_helper_dfcmpge(PdV, tcg_env, RssV, RttV) #define fGEN_TCG_F2_dfcmpuo(SHORTCODE) \ - gen_helper_dfcmpuo(PdV, cpu_env, RssV, RttV) + gen_helper_dfcmpuo(PdV, tcg_env, RssV, RttV) #define fGEN_TCG_F2_dfclass(SHORTCODE) \ do { \ TCGv imm = tcg_constant_tl(uiV); \ - gen_helper_dfclass(PdV, cpu_env, RssV, imm); \ + gen_helper_dfclass(PdV, tcg_env, RssV, imm); \ } while (0) #define fGEN_TCG_F2_sfmpy(SHORTCODE) \ - gen_helper_sfmpy(RdV, cpu_env, RsV, RtV) + gen_helper_sfmpy(RdV, tcg_env, RsV, RtV) #define fGEN_TCG_F2_sffma(SHORTCODE) \ - gen_helper_sffma(RxV, cpu_env, RxV, RsV, RtV) + gen_helper_sffma(RxV, tcg_env, RxV, RsV, RtV) #define fGEN_TCG_F2_sffma_sc(SHORTCODE) \ - gen_helper_sffma_sc(RxV, cpu_env, RxV, RsV, RtV, PuV) + gen_helper_sffma_sc(RxV, tcg_env, RxV, RsV, RtV, PuV) #define fGEN_TCG_F2_sffms(SHORTCODE) \ - gen_helper_sffms(RxV, cpu_env, RxV, RsV, RtV) + gen_helper_sffms(RxV, tcg_env, RxV, RsV, RtV) #define fGEN_TCG_F2_sffma_lib(SHORTCODE) \ - gen_helper_sffma_lib(RxV, cpu_env, RxV, RsV, RtV) + gen_helper_sffma_lib(RxV, tcg_env, RxV, RsV, RtV) #define fGEN_TCG_F2_sffms_lib(SHORTCODE) \ - gen_helper_sffms_lib(RxV, cpu_env, RxV, RsV, RtV) + gen_helper_sffms_lib(RxV, tcg_env, RxV, RsV, RtV) #define fGEN_TCG_F2_dfmpyfix(SHORTCODE) \ - gen_helper_dfmpyfix(RddV, cpu_env, RssV, RttV) + gen_helper_dfmpyfix(RddV, tcg_env, RssV, RttV) #define fGEN_TCG_F2_dfmpyhh(SHORTCODE) \ - gen_helper_dfmpyhh(RxxV, cpu_env, RxxV, RssV, RttV) + gen_helper_dfmpyhh(RxxV, tcg_env, RxxV, RssV, RttV) /* Nothing to do for these in qemu, need to suppress compiler warnings */ #define fGEN_TCG_Y4_l2fetch(SHORTCODE) \ @@ -739,5 +1331,41 @@ do { \ RsV = RsV; \ } while (0) +#define fGEN_TCG_Y2_isync(SHORTCODE) \ + do { } while (0) +#define fGEN_TCG_Y2_barrier(SHORTCODE) \ + do { } while (0) +#define fGEN_TCG_Y2_syncht(SHORTCODE) \ + do { } while (0) +#define fGEN_TCG_Y2_dcfetchbo(SHORTCODE) \ + do { \ + RsV = RsV; \ + uiV = uiV; \ + } while (0) +#define fGEN_TCG_L2_loadw_aq(SHORTCODE) SHORTCODE +#define fGEN_TCG_L4_loadd_aq(SHORTCODE) SHORTCODE + +/* Nothing to do for these in qemu, need to suppress compiler warnings */ +#define fGEN_TCG_R6_release_at_vi(SHORTCODE) \ + do { \ + RsV = RsV; \ + } while (0) +#define fGEN_TCG_R6_release_st_vi(SHORTCODE) \ + do { \ + RsV = RsV; \ + } while (0) + +#define fGEN_TCG_S2_storew_rl_at_vi(SHORTCODE) SHORTCODE +#define fGEN_TCG_S4_stored_rl_at_vi(SHORTCODE) SHORTCODE +#define fGEN_TCG_S2_storew_rl_st_vi(SHORTCODE) SHORTCODE +#define fGEN_TCG_S4_stored_rl_st_vi(SHORTCODE) SHORTCODE + +#define fGEN_TCG_J2_trap0(SHORTCODE) \ + do { \ + uiV = uiV; \ + tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->pkt->pc); \ + TCGv excp = tcg_constant_tl(HEX_EXCP_TRAP0); \ + gen_helper_raise_exception(tcg_env, excp); \ + } while (0) #endif diff --git a/target/hexagon/gen_tcg_func_table.py b/target/hexagon/gen_tcg_func_table.py index 4809d3273e..f998ef0992 100755 --- a/target/hexagon/gen_tcg_func_table.py +++ b/target/hexagon/gen_tcg_func_table.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ import re import string import hex_common + def main(): hex_common.read_semantics_file(sys.argv[1]) hex_common.read_attribs_file(sys.argv[2]) @@ -29,30 +30,31 @@ def main(): tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[3], 'w') as f: + with open(sys.argv[3], "w") as f: f.write("#ifndef HEXAGON_FUNC_TABLE_H\n") f.write("#define HEXAGON_FUNC_TABLE_H\n\n") f.write("const SemanticInsn opcode_genptr[XX_LAST_OPCODE] = {\n") for tag in hex_common.tags: ## Skip the priv instructions - if ( "A_PRIV" in hex_common.attribdict[tag] ) : + if "A_PRIV" in hex_common.attribdict[tag]: continue ## Skip the guest instructions - if ( "A_GUEST" in hex_common.attribdict[tag] ) : + if "A_GUEST" in hex_common.attribdict[tag]: continue ## Skip the diag instructions - if ( tag == "Y6_diag" ) : + if tag == "Y6_diag": continue - if ( tag == "Y6_diag0" ) : + if tag == "Y6_diag0": continue - if ( tag == "Y6_diag1" ) : + if tag == "Y6_diag1": continue - f.write(" [%s] = generate_%s,\n" % (tag, tag)) + f.write(f" [{tag}] = generate_{tag},\n") f.write("};\n\n") f.write("#endif /* HEXAGON_FUNC_TABLE_H */\n") + if __name__ == "__main__": main() diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index 1fd9de95d5..3d8e3cb6a2 100755 --- a/target/hexagon/gen_tcg_funcs.py +++ b/target/hexagon/gen_tcg_funcs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -22,686 +22,139 @@ import re import string import hex_common -## -## Helpers for gen_tcg_func -## -def gen_decl_ea_tcg(f, tag): - if ('A_CONDEXEC' in hex_common.attribdict[tag] or - 'A_LOAD' in hex_common.attribdict[tag]): - f.write(" TCGv EA = tcg_temp_local_new();\n") - else: - f.write(" TCGv EA = tcg_temp_new();\n") - -def gen_free_ea_tcg(f): - f.write(" tcg_temp_free(EA);\n") - -def genptr_decl_pair_writable(f, tag, regtype, regid, regno): - regN="%s%sN" % (regtype,regid) - f.write(" TCGv_i64 %s%sV = tcg_temp_local_new_i64();\n" % \ - (regtype, regid)) - if (regtype == "C"): - f.write(" const int %s = insn->regno[%d] + HEX_REG_SA0;\n" % \ - (regN, regno)) - else: - f.write(" const int %s = insn->regno[%d];\n" % (regN, regno)) - if ('A_CONDEXEC' in hex_common.attribdict[tag]): - f.write(" if (!is_preloaded(ctx, %s)) {\n" % regN) - f.write(" tcg_gen_mov_tl(hex_new_value[%s], hex_gpr[%s]);\n" % \ - (regN, regN)) - f.write(" }\n") - f.write(" if (!is_preloaded(ctx, %s + 1)) {\n" % regN) - f.write(" tcg_gen_mov_tl(hex_new_value[%s + 1], hex_gpr[%s + 1]);\n" % \ - (regN, regN)) - f.write(" }\n") - -def genptr_decl_writable(f, tag, regtype, regid, regno): - regN="%s%sN" % (regtype,regid) - f.write(" TCGv %s%sV = tcg_temp_local_new();\n" % \ - (regtype, regid)) - if (regtype == "C"): - f.write(" const int %s = insn->regno[%d] + HEX_REG_SA0;\n" % \ - (regN, regno)) - else: - f.write(" const int %s = insn->regno[%d];\n" % (regN, regno)) - if ('A_CONDEXEC' in hex_common.attribdict[tag]): - f.write(" if (!is_preloaded(ctx, %s)) {\n" % regN) - f.write(" tcg_gen_mov_tl(hex_new_value[%s], hex_gpr[%s]);\n" % \ - (regN, regN)) - f.write(" }\n") - -def genptr_decl(f, tag, regtype, regid, regno): - regN="%s%sN" % (regtype,regid) - if (regtype == "R"): - if (regid in {"ss", "tt"}): - f.write(" TCGv_i64 %s%sV = tcg_temp_local_new_i64();\n" % \ - (regtype, regid)) - f.write(" const int %s = insn->regno[%d];\n" % \ - (regN, regno)) - elif (regid in {"dd", "ee", "xx", "yy"}): - genptr_decl_pair_writable(f, tag, regtype, regid, regno) - elif (regid in {"s", "t", "u", "v"}): - f.write(" TCGv %s%sV = hex_gpr[insn->regno[%d]];\n" % \ - (regtype, regid, regno)) - elif (regid in {"d", "e", "x", "y"}): - genptr_decl_writable(f, tag, regtype, regid, regno) - else: - print("Bad register parse: ", regtype, regid) - elif (regtype == "P"): - if (regid in {"s", "t", "u", "v"}): - f.write(" TCGv %s%sV = hex_pred[insn->regno[%d]];\n" % \ - (regtype, regid, regno)) - elif (regid in {"d", "e", "x"}): - genptr_decl_writable(f, tag, regtype, regid, regno) - else: - print("Bad register parse: ", regtype, regid) - elif (regtype == "C"): - if (regid == "ss"): - f.write(" TCGv_i64 %s%sV = tcg_temp_local_new_i64();\n" % \ - (regtype, regid)) - f.write(" const int %s = insn->regno[%d] + HEX_REG_SA0;\n" % \ - (regN, regno)) - elif (regid == "dd"): - genptr_decl_pair_writable(f, tag, regtype, regid, regno) - elif (regid == "s"): - f.write(" TCGv %s%sV = tcg_temp_local_new();\n" % \ - (regtype, regid)) - f.write(" const int %s%sN = insn->regno[%d] + HEX_REG_SA0;\n" % \ - (regtype, regid, regno)) - elif (regid == "d"): - genptr_decl_writable(f, tag, regtype, regid, regno) - else: - print("Bad register parse: ", regtype, regid) - elif (regtype == "M"): - if (regid == "u"): - f.write(" const int %s%sN = insn->regno[%d];\n"% \ - (regtype, regid, regno)) - f.write(" TCGv %s%sV = hex_gpr[%s%sN + HEX_REG_M0];\n" % \ - (regtype, regid, regtype, regid)) - else: - print("Bad register parse: ", regtype, regid) - elif (regtype == "V"): - if (regid in {"dd"}): - f.write(" const int %s%sN = insn->regno[%d];\n" %\ - (regtype, regid, regno)) - f.write(" const intptr_t %s%sV_off =\n" %\ - (regtype, regid)) - if (hex_common.is_tmp_result(tag)): - f.write(" ctx_tmp_vreg_off(ctx, %s%sN, 2, true);\n" % \ - (regtype, regid)) - else: - f.write(" ctx_future_vreg_off(ctx, %s%sN," % \ - (regtype, regid)) - f.write(" 2, true);\n") - if (not hex_common.skip_qemu_helper(tag)): - f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \ - (regtype, regid)) - f.write(" tcg_gen_addi_ptr(%s%sV, cpu_env, %s%sV_off);\n" % \ - (regtype, regid, regtype, regid)) - elif (regid in {"uu", "vv", "xx"}): - f.write(" const int %s%sN = insn->regno[%d];\n" % \ - (regtype, regid, regno)) - f.write(" const intptr_t %s%sV_off =\n" % \ - (regtype, regid)) - f.write(" offsetof(CPUHexagonState, %s%sV);\n" % \ - (regtype, regid)) - if (not hex_common.skip_qemu_helper(tag)): - f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \ - (regtype, regid)) - f.write(" tcg_gen_addi_ptr(%s%sV, cpu_env, %s%sV_off);\n" % \ - (regtype, regid, regtype, regid)) - elif (regid in {"s", "u", "v", "w"}): - f.write(" const int %s%sN = insn->regno[%d];\n" % \ - (regtype, regid, regno)) - f.write(" const intptr_t %s%sV_off =\n" % \ - (regtype, regid)) - f.write(" vreg_src_off(ctx, %s%sN);\n" % \ - (regtype, regid)) - if (not hex_common.skip_qemu_helper(tag)): - f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \ - (regtype, regid)) - elif (regid in {"d", "x", "y"}): - f.write(" const int %s%sN = insn->regno[%d];\n" % \ - (regtype, regid, regno)) - f.write(" const intptr_t %s%sV_off =\n" % \ - (regtype, regid)) - if (hex_common.is_tmp_result(tag)): - f.write(" ctx_tmp_vreg_off(ctx, %s%sN, 1, true);\n" % \ - (regtype, regid)) - else: - f.write(" ctx_future_vreg_off(ctx, %s%sN," % \ - (regtype, regid)) - f.write(" 1, true);\n"); - if (not hex_common.skip_qemu_helper(tag)): - f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \ - (regtype, regid)) - f.write(" tcg_gen_addi_ptr(%s%sV, cpu_env, %s%sV_off);\n" % \ - (regtype, regid, regtype, regid)) - else: - print("Bad register parse: ", regtype, regid) - elif (regtype == "Q"): - if (regid in {"d", "e", "x"}): - f.write(" const int %s%sN = insn->regno[%d];\n" % \ - (regtype, regid, regno)) - f.write(" const intptr_t %s%sV_off =\n" % \ - (regtype, regid)) - f.write(" offsetof(CPUHexagonState,\n") - f.write(" future_QRegs[%s%sN]);\n" % \ - (regtype, regid)) - if (not hex_common.skip_qemu_helper(tag)): - f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \ - (regtype, regid)) - f.write(" tcg_gen_addi_ptr(%s%sV, cpu_env, %s%sV_off);\n" % \ - (regtype, regid, regtype, regid)) - elif (regid in {"s", "t", "u", "v"}): - f.write(" const int %s%sN = insn->regno[%d];\n" % \ - (regtype, regid, regno)) - f.write(" const intptr_t %s%sV_off =\n" %\ - (regtype, regid)) - f.write(" offsetof(CPUHexagonState, QRegs[%s%sN]);\n" % \ - (regtype, regid)) - if (not hex_common.skip_qemu_helper(tag)): - f.write(" TCGv_ptr %s%sV = tcg_temp_new_ptr();\n" % \ - (regtype, regid)) - else: - print("Bad register parse: ", regtype, regid) - else: - print("Bad register parse: ", regtype, regid) - -def genptr_decl_new(f, tag, regtype, regid, regno): - if (regtype == "N"): - if (regid in {"s", "t"}): - f.write(" TCGv %s%sN = hex_new_value[insn->regno[%d]];\n" % \ - (regtype, regid, regno)) - else: - print("Bad register parse: ", regtype, regid) - elif (regtype == "P"): - if (regid in {"t", "u", "v"}): - f.write(" TCGv %s%sN = hex_new_pred_value[insn->regno[%d]];\n" % \ - (regtype, regid, regno)) - else: - print("Bad register parse: ", regtype, regid) - elif (regtype == "O"): - if (regid == "s"): - f.write(" const intptr_t %s%sN_num = insn->regno[%d];\n" % \ - (regtype, regid, regno)) - if (hex_common.skip_qemu_helper(tag)): - f.write(" const intptr_t %s%sN_off =\n" % \ - (regtype, regid)) - f.write(" ctx_future_vreg_off(ctx, %s%sN_num," % \ - (regtype, regid)) - f.write(" 1, true);\n") - else: - f.write(" TCGv %s%sN = tcg_constant_tl(%s%sN_num);\n" % \ - (regtype, regid, regtype, regid)) - else: - print("Bad register parse: ", regtype, regid) - else: - print("Bad register parse: ", regtype, regid) - -def genptr_decl_opn(f, tag, regtype, regid, toss, numregs, i): - if (hex_common.is_pair(regid)): - genptr_decl(f, tag, regtype, regid, i) - elif (hex_common.is_single(regid)): - if hex_common.is_old_val(regtype, regid, tag): - genptr_decl(f,tag, regtype, regid, i) - elif hex_common.is_new_val(regtype, regid, tag): - genptr_decl_new(f, tag, regtype, regid, i) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) - -def genptr_decl_imm(f,immlett): - if (immlett.isupper()): - i = 1 - else: - i = 0 - f.write(" int %s = insn->immed[%d];\n" % \ - (hex_common.imm_name(immlett), i)) - -def genptr_free(f, tag, regtype, regid, regno): - if (regtype == "R"): - if (regid in {"dd", "ss", "tt", "xx", "yy"}): - f.write(" tcg_temp_free_i64(%s%sV);\n" % (regtype, regid)) - elif (regid in {"d", "e", "x", "y"}): - f.write(" tcg_temp_free(%s%sV);\n" % (regtype, regid)) - elif (regid not in {"s", "t", "u", "v"}): - print("Bad register parse: ",regtype,regid) - elif (regtype == "P"): - if (regid in {"d", "e", "x"}): - f.write(" tcg_temp_free(%s%sV);\n" % (regtype, regid)) - elif (regid not in {"s", "t", "u", "v"}): - print("Bad register parse: ",regtype,regid) - elif (regtype == "C"): - if (regid in {"dd", "ss"}): - f.write(" tcg_temp_free_i64(%s%sV);\n" % (regtype, regid)) - elif (regid in {"d", "s"}): - f.write(" tcg_temp_free(%s%sV);\n" % (regtype, regid)) - else: - print("Bad register parse: ",regtype,regid) - elif (regtype == "M"): - if (regid != "u"): - print("Bad register parse: ", regtype, regid) - elif (regtype == "V"): - if (regid in {"dd", "uu", "vv", "xx", \ - "d", "s", "u", "v", "w", "x", "y"}): - if (not hex_common.skip_qemu_helper(tag)): - f.write(" tcg_temp_free_ptr(%s%sV);\n" % \ - (regtype, regid)) - else: - print("Bad register parse: ", regtype, regid) - elif (regtype == "Q"): - if (regid in {"d", "e", "s", "t", "u", "v", "x"}): - if (not hex_common.skip_qemu_helper(tag)): - f.write(" tcg_temp_free_ptr(%s%sV);\n" % \ - (regtype, regid)) - else: - print("Bad register parse: ", regtype, regid) - else: - print("Bad register parse: ", regtype, regid) - -def genptr_free_new(f, tag, regtype, regid, regno): - if (regtype == "N"): - if (regid not in {"s", "t"}): - print("Bad register parse: ", regtype, regid) - elif (regtype == "P"): - if (regid not in {"t", "u", "v"}): - print("Bad register parse: ", regtype, regid) - elif (regtype == "O"): - if (regid != "s"): - print("Bad register parse: ", regtype, regid) - else: - print("Bad register parse: ", regtype, regid) - -def genptr_free_opn(f,regtype,regid,i,tag): - if (hex_common.is_pair(regid)): - genptr_free(f, tag, regtype, regid, i) - elif (hex_common.is_single(regid)): - if hex_common.is_old_val(regtype, regid, tag): - genptr_free(f, tag, regtype, regid, i) - elif hex_common.is_new_val(regtype, regid, tag): - genptr_free_new(f, tag, regtype, regid, i) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) - -def genptr_src_read(f, tag, regtype, regid): - if (regtype == "R"): - if (regid in {"ss", "tt", "xx", "yy"}): - f.write(" tcg_gen_concat_i32_i64(%s%sV, hex_gpr[%s%sN],\n" % \ - (regtype, regid, regtype, regid)) - f.write(" hex_gpr[%s%sN + 1]);\n" % \ - (regtype, regid)) - elif (regid in {"x", "y"}): - f.write(" tcg_gen_mov_tl(%s%sV, hex_gpr[%s%sN]);\n" % \ - (regtype,regid,regtype,regid)) - elif (regid not in {"s", "t", "u", "v"}): - print("Bad register parse: ", regtype, regid) - elif (regtype == "P"): - if (regid == "x"): - f.write(" tcg_gen_mov_tl(%s%sV, hex_pred[%s%sN]);\n" % \ - (regtype, regid, regtype, regid)) - elif (regid not in {"s", "t", "u", "v"}): - print("Bad register parse: ", regtype, regid) - elif (regtype == "C"): - if (regid == "ss"): - f.write(" gen_read_ctrl_reg_pair(ctx, %s%sN, %s%sV);\n" % \ - (regtype, regid, regtype, regid)) - elif (regid == "s"): - f.write(" gen_read_ctrl_reg(ctx, %s%sN, %s%sV);\n" % \ - (regtype, regid, regtype, regid)) - else: - print("Bad register parse: ", regtype, regid) - elif (regtype == "M"): - if (regid != "u"): - print("Bad register parse: ", regtype, regid) - elif (regtype == "V"): - if (regid in {"uu", "vv", "xx"}): - f.write(" tcg_gen_gvec_mov(MO_64, %s%sV_off,\n" % \ - (regtype, regid)) - f.write(" vreg_src_off(ctx, %s%sN),\n" % \ - (regtype, regid)) - f.write(" sizeof(MMVector), sizeof(MMVector));\n") - f.write(" tcg_gen_gvec_mov(MO_64,\n") - f.write(" %s%sV_off + sizeof(MMVector),\n" % \ - (regtype, regid)) - f.write(" vreg_src_off(ctx, %s%sN ^ 1),\n" % \ - (regtype, regid)) - f.write(" sizeof(MMVector), sizeof(MMVector));\n") - elif (regid in {"s", "u", "v", "w"}): - if (not hex_common.skip_qemu_helper(tag)): - f.write(" tcg_gen_addi_ptr(%s%sV, cpu_env, %s%sV_off);\n" % \ - (regtype, regid, regtype, regid)) - elif (regid in {"x", "y"}): - f.write(" tcg_gen_gvec_mov(MO_64, %s%sV_off,\n" % \ - (regtype, regid)) - f.write(" vreg_src_off(ctx, %s%sN),\n" % \ - (regtype, regid)) - f.write(" sizeof(MMVector), sizeof(MMVector));\n") - if (not hex_common.skip_qemu_helper(tag)): - f.write(" tcg_gen_addi_ptr(%s%sV, cpu_env, %s%sV_off);\n" % \ - (regtype, regid, regtype, regid)) - else: - print("Bad register parse: ", regtype, regid) - elif (regtype == "Q"): - if (regid in {"s", "t", "u", "v"}): - if (not hex_common.skip_qemu_helper(tag)): - f.write(" tcg_gen_addi_ptr(%s%sV, cpu_env, %s%sV_off);\n" % \ - (regtype, regid, regtype, regid)) - elif (regid in {"x"}): - f.write(" tcg_gen_gvec_mov(MO_64, %s%sV_off,\n" % \ - (regtype, regid)) - f.write(" offsetof(CPUHexagonState, QRegs[%s%sN]),\n" % \ - (regtype, regid)) - f.write(" sizeof(MMQReg), sizeof(MMQReg));\n") - else: - print("Bad register parse: ", regtype, regid) - else: - print("Bad register parse: ", regtype, regid) - -def genptr_src_read_new(f,regtype,regid): - if (regtype == "N"): - if (regid not in {"s", "t"}): - print("Bad register parse: ", regtype, regid) - elif (regtype == "P"): - if (regid not in {"t", "u", "v"}): - print("Bad register parse: ", regtype, regid) - elif (regtype == "O"): - if (regid != "s"): - print("Bad register parse: ", regtype, regid) - else: - print("Bad register parse: ", regtype, regid) - -def genptr_src_read_opn(f,regtype,regid,tag): - if (hex_common.is_pair(regid)): - genptr_src_read(f, tag, regtype, regid) - elif (hex_common.is_single(regid)): - if hex_common.is_old_val(regtype, regid, tag): - genptr_src_read(f, tag, regtype, regid) - elif hex_common.is_new_val(regtype, regid, tag): - genptr_src_read_new(f,regtype,regid) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) - -def gen_helper_call_opn(f, tag, regtype, regid, toss, numregs, i): - if (i > 0): f.write(", ") - if (hex_common.is_pair(regid)): - f.write("%s%sV" % (regtype,regid)) - elif (hex_common.is_single(regid)): - if hex_common.is_old_val(regtype, regid, tag): - f.write("%s%sV" % (regtype,regid)) - elif hex_common.is_new_val(regtype, regid, tag): - f.write("%s%sN" % (regtype,regid)) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) - -def gen_helper_decl_imm(f,immlett): - f.write(" TCGv tcgv_%s = tcg_constant_tl(%s);\n" % \ - (hex_common.imm_name(immlett), hex_common.imm_name(immlett))) - -def gen_helper_call_imm(f,immlett): - f.write(", tcgv_%s" % hex_common.imm_name(immlett)) - -def genptr_dst_write_pair(f, tag, regtype, regid): - if ('A_CONDEXEC' in hex_common.attribdict[tag]): - f.write(" gen_log_predicated_reg_write_pair(%s%sN, %s%sV, insn->slot);\n" % \ - (regtype, regid, regtype, regid)) - else: - f.write(" gen_log_reg_write_pair(%s%sN, %s%sV);\n" % \ - (regtype, regid, regtype, regid)) - f.write(" ctx_log_reg_write_pair(ctx, %s%sN);\n" % \ - (regtype, regid)) - -def genptr_dst_write(f, tag, regtype, regid): - if (regtype == "R"): - if (regid in {"dd", "xx", "yy"}): - genptr_dst_write_pair(f, tag, regtype, regid) - elif (regid in {"d", "e", "x", "y"}): - if ('A_CONDEXEC' in hex_common.attribdict[tag]): - f.write(" gen_log_predicated_reg_write(%s%sN, %s%sV,\n" % \ - (regtype, regid, regtype, regid)) - f.write(" insn->slot);\n") - else: - f.write(" gen_log_reg_write(%s%sN, %s%sV);\n" % \ - (regtype, regid, regtype, regid)) - f.write(" ctx_log_reg_write(ctx, %s%sN);\n" % \ - (regtype, regid)) - else: - print("Bad register parse: ", regtype, regid) - elif (regtype == "P"): - if (regid in {"d", "e", "x"}): - f.write(" gen_log_pred_write(ctx, %s%sN, %s%sV);\n" % \ - (regtype, regid, regtype, regid)) - f.write(" ctx_log_pred_write(ctx, %s%sN);\n" % \ - (regtype, regid)) - else: - print("Bad register parse: ", regtype, regid) - elif (regtype == "C"): - if (regid == "dd"): - f.write(" gen_write_ctrl_reg_pair(ctx, %s%sN, %s%sV);\n" % \ - (regtype, regid, regtype, regid)) - elif (regid == "d"): - f.write(" gen_write_ctrl_reg(ctx, %s%sN, %s%sV);\n" % \ - (regtype, regid, regtype, regid)) - else: - print("Bad register parse: ", regtype, regid) - else: - print("Bad register parse: ", regtype, regid) - -def genptr_dst_write_ext(f, tag, regtype, regid, newv="EXT_DFL"): - if (regtype == "V"): - if (regid in {"dd", "xx", "yy"}): - if ('A_CONDEXEC' in hex_common.attribdict[tag]): - is_predicated = "true" - else: - is_predicated = "false" - f.write(" gen_log_vreg_write_pair(ctx, %s%sV_off, %s%sN, " % \ - (regtype, regid, regtype, regid)) - f.write("%s, insn->slot, %s);\n" % \ - (newv, is_predicated)) - f.write(" ctx_log_vreg_write_pair(ctx, %s%sN, %s,\n" % \ - (regtype, regid, newv)) - f.write(" %s);\n" % (is_predicated)) - elif (regid in {"d", "x", "y"}): - if ('A_CONDEXEC' in hex_common.attribdict[tag]): - is_predicated = "true" - else: - is_predicated = "false" - f.write(" gen_log_vreg_write(ctx, %s%sV_off, %s%sN, %s, " % \ - (regtype, regid, regtype, regid, newv)) - f.write("insn->slot, %s);\n" % \ - (is_predicated)) - f.write(" ctx_log_vreg_write(ctx, %s%sN, %s, %s);\n" % \ - (regtype, regid, newv, is_predicated)) - else: - print("Bad register parse: ", regtype, regid) - elif (regtype == "Q"): - if (regid in {"d", "e", "x"}): - if ('A_CONDEXEC' in hex_common.attribdict[tag]): - is_predicated = "true" - else: - is_predicated = "false" - f.write(" gen_log_qreg_write(%s%sV_off, %s%sN, %s, " % \ - (regtype, regid, regtype, regid, newv)) - f.write("insn->slot, %s);\n" % (is_predicated)) - f.write(" ctx_log_qreg_write(ctx, %s%sN, %s);\n" % \ - (regtype, regid, is_predicated)) - else: - print("Bad register parse: ", regtype, regid) - else: - print("Bad register parse: ", regtype, regid) - -def genptr_dst_write_opn(f,regtype, regid, tag): - if (hex_common.is_pair(regid)): - if (hex_common.is_hvx_reg(regtype)): - if (hex_common.is_tmp_result(tag)): - genptr_dst_write_ext(f, tag, regtype, regid, "EXT_TMP") - else: - genptr_dst_write_ext(f, tag, regtype, regid) - else: - genptr_dst_write(f, tag, regtype, regid) - elif (hex_common.is_single(regid)): - if (hex_common.is_hvx_reg(regtype)): - if (hex_common.is_new_result(tag)): - genptr_dst_write_ext(f, tag, regtype, regid, "EXT_NEW") - if (hex_common.is_tmp_result(tag)): - genptr_dst_write_ext(f, tag, regtype, regid, "EXT_TMP") - else: - genptr_dst_write_ext(f, tag, regtype, regid, "EXT_DFL") - else: - genptr_dst_write(f, tag, regtype, regid) - else: - print("Bad register parse: ",regtype,regid,toss,numregs) ## ## Generate the TCG code to call the helper ## For A2_add: Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;} ## We produce: -## static void generate_A2_add() -## CPUHexagonState *env -## DisasContext *ctx, -## Insn *insn, -## Packet *pkt) -## { -## TCGv RdV = tcg_temp_local_new(); -## const int RdN = insn->regno[0]; -## TCGv RsV = hex_gpr[insn->regno[1]]; -## TCGv RtV = hex_gpr[insn->regno[2]]; -## -## gen_log_reg_write(RdN, RdV); -## ctx_log_reg_write(ctx, RdN); -## tcg_temp_free(RdV); -## } +## static void generate_A2_add(DisasContext *ctx) +## { +## Insn *insn G_GNUC_UNUSED = ctx->insn; +## const int RdN = insn->regno[0]; +## TCGv RdV = get_result_gpr(ctx, RdN); +## TCGv RsV = hex_gpr[insn->regno[1]]; +## TCGv RtV = hex_gpr[insn->regno[2]]; +## +## gen_log_reg_write(ctx, RdN, RdV); +## } ## ## where depends on hex_common.skip_qemu_helper(tag) ## if hex_common.skip_qemu_helper(tag) is True ## is fGEN_TCG_A2_add({ RdV=RsV+RtV;}); ## if hex_common.skip_qemu_helper(tag) is False -## is gen_helper_A2_add(RdV, cpu_env, RsV, RtV); +## is gen_helper_A2_add(RdV, tcg_env, RsV, RtV); ## def gen_tcg_func(f, tag, regs, imms): - f.write("static void generate_%s(\n" %tag) - f.write(" CPUHexagonState *env,\n") - f.write(" DisasContext *ctx,\n") - f.write(" Insn *insn,\n") - f.write(" Packet *pkt)\n") - f.write('{\n') - if hex_common.need_ea(tag): gen_decl_ea_tcg(f, tag) - i=0 + f.write(f"static void generate_{tag}(DisasContext *ctx)\n") + f.write("{\n") + + f.write(" Insn *insn G_GNUC_UNUSED = ctx->insn;\n") + + if hex_common.need_ea(tag): + f.write(" TCGv EA G_GNUC_UNUSED = tcg_temp_new();\n") + ## Declare all the operands (regs and immediates) - for regtype,regid,toss,numregs in regs: - genptr_decl_opn(f, tag, regtype, regid, toss, numregs, i) + i = 0 + for regtype, regid in regs: + reg = hex_common.get_register(tag, regtype, regid) + reg.decl_tcg(f, tag, i) i += 1 - for immlett,bits,immshift in imms: - genptr_decl_imm(f,immlett) + for immlett, bits, immshift in imms: + i = 1 if immlett.isupper() else 0 + f.write(f" int {hex_common.imm_name(immlett)} = insn->immed[{i}];\n") - if 'A_PRIV' in hex_common.attribdict[tag]: - f.write(' fCHECKFORPRIV();\n') - if 'A_GUEST' in hex_common.attribdict[tag]: - f.write(' fCHECKFORGUEST();\n') + if hex_common.is_idef_parser_enabled(tag): + declared = [] + ## Handle registers + for regtype, regid in regs: + reg = hex_common.get_register(tag, regtype, regid) + reg.idef_arg(declared) + ## Handle immediates + for immlett, bits, immshift in imms: + declared.append(hex_common.imm_name(immlett)) - ## Read all the inputs - for regtype,regid,toss,numregs in regs: - if (hex_common.is_read(regid)): - genptr_src_read_opn(f,regtype,regid,tag) + arguments = ", ".join(["ctx", "ctx->insn", "ctx->pkt"] + declared) + f.write(f" emit_{tag}({arguments});\n") - if ( hex_common.skip_qemu_helper(tag) ): - f.write(" fGEN_TCG_%s(%s);\n" % (tag, hex_common.semdict[tag])) + elif hex_common.skip_qemu_helper(tag): + f.write(f" fGEN_TCG_{tag}({hex_common.semdict[tag]});\n") else: ## Generate the call to the helper - for immlett,bits,immshift in imms: - gen_helper_decl_imm(f,immlett) - if hex_common.need_part1(tag): - f.write(" TCGv part1 = tcg_constant_tl(insn->part1);\n") - if hex_common.need_slot(tag): - f.write(" TCGv slot = tcg_constant_tl(insn->slot);\n") - f.write(" gen_helper_%s(" % (tag)) - i=0 - ## If there is a scalar result, it is the return type - for regtype,regid,toss,numregs in regs: - if (hex_common.is_written(regid)): - if (hex_common.is_hvx_reg(regtype)): - continue - gen_helper_call_opn(f, tag, regtype, regid, toss, numregs, i) - i += 1 - if (i > 0): f.write(", ") - f.write("cpu_env") - i=1 - for regtype,regid,toss,numregs in regs: - if (hex_common.is_written(regid)): - if (not hex_common.is_hvx_reg(regtype)): - continue - gen_helper_call_opn(f, tag, regtype, regid, toss, numregs, i) - i += 1 - for regtype,regid,toss,numregs in regs: - if (hex_common.is_read(regid)): - if (hex_common.is_hvx_reg(regtype) and - hex_common.is_readwrite(regid)): - continue - gen_helper_call_opn(f, tag, regtype, regid, toss, numregs, i) - i += 1 - for immlett,bits,immshift in imms: - gen_helper_call_imm(f,immlett) + declared = [] + ret_type = hex_common.helper_ret_type(tag, regs).call_arg + if ret_type != "void": + declared.append(ret_type) - if hex_common.need_slot(tag): f.write(", slot") - if hex_common.need_part1(tag): f.write(", part1" ) - f.write(");\n") + for arg in hex_common.helper_args(tag, regs, imms): + declared.append(arg.call_arg) + + arguments = ", ".join(declared) + f.write(f" gen_helper_{tag}({arguments});\n") ## Write all the outputs - for regtype,regid,toss,numregs in regs: - if (hex_common.is_written(regid)): - genptr_dst_write_opn(f,regtype, regid, tag) - - ## Free all the operands (regs and immediates) - if hex_common.need_ea(tag): gen_free_ea_tcg(f) - for regtype,regid,toss,numregs in regs: - genptr_free_opn(f,regtype,regid,i,tag) - i += 1 + for regtype, regid in regs: + reg = hex_common.get_register(tag, regtype, regid) + if reg.is_written(): + reg.log_write(f, tag) f.write("}\n\n") + def gen_def_tcg_func(f, tag, tagregs, tagimms): regs = tagregs[tag] imms = tagimms[tag] gen_tcg_func(f, tag, regs, imms) + def main(): hex_common.read_semantics_file(sys.argv[1]) hex_common.read_attribs_file(sys.argv[2]) hex_common.read_overrides_file(sys.argv[3]) hex_common.read_overrides_file(sys.argv[4]) hex_common.calculate_attribs() + hex_common.init_registers() + ## Whether or not idef-parser is enabled is + ## determined by the number of arguments to + ## this script: + ## + ## 5 args. -> not enabled, + ## 6 args. -> idef-parser enabled. + ## + ## The 6:th arg. then holds a list of the successfully + ## parsed instructions. + is_idef_parser_enabled = len(sys.argv) > 6 + if is_idef_parser_enabled: + hex_common.read_idef_parser_enabled_file(sys.argv[5]) tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[5], 'w') as f: + output_file = sys.argv[-1] + with open(output_file, "w") as f: f.write("#ifndef HEXAGON_TCG_FUNCS_H\n") f.write("#define HEXAGON_TCG_FUNCS_H\n\n") + if is_idef_parser_enabled: + f.write('#include "idef-generated-emitter.h.inc"\n\n') for tag in hex_common.tags: ## Skip the priv instructions - if ( "A_PRIV" in hex_common.attribdict[tag] ) : + if "A_PRIV" in hex_common.attribdict[tag]: continue ## Skip the guest instructions - if ( "A_GUEST" in hex_common.attribdict[tag] ) : + if "A_GUEST" in hex_common.attribdict[tag]: continue ## Skip the diag instructions - if ( tag == "Y6_diag" ) : + if tag == "Y6_diag": continue - if ( tag == "Y6_diag0" ) : + if tag == "Y6_diag0": continue - if ( tag == "Y6_diag1" ) : + if tag == "Y6_diag1": continue gen_def_tcg_func(f, tag, tagregs, tagimms) f.write("#endif /* HEXAGON_TCG_FUNCS_H */\n") + if __name__ == "__main__": main() diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index cdcc9382bb..0da64d467e 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,7 +43,7 @@ static inline void assert_vhist_tmp(DisasContext *ctx) #define fGEN_TCG_V6_vhist(SHORTCODE) \ if (!ctx->pre_commit) { \ assert_vhist_tmp(ctx); \ - gen_helper_vhist(cpu_env); \ + gen_helper_vhist(tcg_env); \ } #define fGEN_TCG_V6_vhistq(SHORTCODE) \ do { \ @@ -53,13 +53,13 @@ static inline void assert_vhist_tmp(DisasContext *ctx) sizeof(MMVector), sizeof(MMVector)); \ } else { \ assert_vhist_tmp(ctx); \ - gen_helper_vhistq(cpu_env); \ + gen_helper_vhistq(tcg_env); \ } \ } while (0) #define fGEN_TCG_V6_vwhist256(SHORTCODE) \ if (!ctx->pre_commit) { \ assert_vhist_tmp(ctx); \ - gen_helper_vwhist256(cpu_env); \ + gen_helper_vwhist256(tcg_env); \ } #define fGEN_TCG_V6_vwhist256q(SHORTCODE) \ do { \ @@ -69,13 +69,13 @@ static inline void assert_vhist_tmp(DisasContext *ctx) sizeof(MMVector), sizeof(MMVector)); \ } else { \ assert_vhist_tmp(ctx); \ - gen_helper_vwhist256q(cpu_env); \ + gen_helper_vwhist256q(tcg_env); \ } \ } while (0) #define fGEN_TCG_V6_vwhist256_sat(SHORTCODE) \ if (!ctx->pre_commit) { \ assert_vhist_tmp(ctx); \ - gen_helper_vwhist256_sat(cpu_env); \ + gen_helper_vwhist256_sat(tcg_env); \ } #define fGEN_TCG_V6_vwhist256q_sat(SHORTCODE) \ do { \ @@ -85,13 +85,13 @@ static inline void assert_vhist_tmp(DisasContext *ctx) sizeof(MMVector), sizeof(MMVector)); \ } else { \ assert_vhist_tmp(ctx); \ - gen_helper_vwhist256q_sat(cpu_env); \ + gen_helper_vwhist256q_sat(tcg_env); \ } \ } while (0) #define fGEN_TCG_V6_vwhist128(SHORTCODE) \ if (!ctx->pre_commit) { \ assert_vhist_tmp(ctx); \ - gen_helper_vwhist128(cpu_env); \ + gen_helper_vwhist128(tcg_env); \ } #define fGEN_TCG_V6_vwhist128q(SHORTCODE) \ do { \ @@ -101,14 +101,14 @@ static inline void assert_vhist_tmp(DisasContext *ctx) sizeof(MMVector), sizeof(MMVector)); \ } else { \ assert_vhist_tmp(ctx); \ - gen_helper_vwhist128q(cpu_env); \ + gen_helper_vwhist128q(tcg_env); \ } \ } while (0) #define fGEN_TCG_V6_vwhist128m(SHORTCODE) \ if (!ctx->pre_commit) { \ TCGv tcgv_uiV = tcg_constant_tl(uiV); \ assert_vhist_tmp(ctx); \ - gen_helper_vwhist128m(cpu_env, tcgv_uiV); \ + gen_helper_vwhist128m(tcg_env, tcgv_uiV); \ } #define fGEN_TCG_V6_vwhist128qm(SHORTCODE) \ do { \ @@ -119,7 +119,7 @@ static inline void assert_vhist_tmp(DisasContext *ctx) } else { \ TCGv tcgv_uiV = tcg_constant_tl(uiV); \ assert_vhist_tmp(ctx); \ - gen_helper_vwhist128qm(cpu_env, tcgv_uiV); \ + gen_helper_vwhist128qm(tcg_env, tcgv_uiV); \ } \ } while (0) @@ -128,22 +128,51 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_gvec_mov(MO_64, VdV_off, VuV_off, \ sizeof(MMVector), sizeof(MMVector)) +#define fGEN_TCG_V6_vassign_tmp(SHORTCODE) \ + tcg_gen_gvec_mov(MO_64, VdV_off, VuV_off, \ + sizeof(MMVector), sizeof(MMVector)) + +#define fGEN_TCG_V6_vcombine_tmp(SHORTCODE) \ + do { \ + tcg_gen_gvec_mov(MO_64, VddV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_gen_gvec_mov(MO_64, VddV_off + sizeof(MMVector), VuV_off, \ + sizeof(MMVector), sizeof(MMVector)); \ + } while (0) + +/* + * Vector combine + * + * Be careful that the source and dest don't overlap + */ +#define fGEN_TCG_V6_vcombine(SHORTCODE) \ + do { \ + if (VddV_off != VuV_off) { \ + tcg_gen_gvec_mov(MO_64, VddV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_gen_gvec_mov(MO_64, VddV_off + sizeof(MMVector), VuV_off, \ + sizeof(MMVector), sizeof(MMVector)); \ + } else { \ + intptr_t tmpoff = offsetof(CPUHexagonState, vtmp); \ + tcg_gen_gvec_mov(MO_64, tmpoff, VuV_off, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_gen_gvec_mov(MO_64, VddV_off, VvV_off, \ + sizeof(MMVector), sizeof(MMVector)); \ + tcg_gen_gvec_mov(MO_64, VddV_off + sizeof(MMVector), tmpoff, \ + sizeof(MMVector), sizeof(MMVector)); \ + } \ + } while (0) + /* Vector conditional move */ #define fGEN_TCG_VEC_CMOV(PRED) \ do { \ TCGv lsb = tcg_temp_new(); \ TCGLabel *false_label = gen_new_label(); \ - TCGLabel *end_label = gen_new_label(); \ tcg_gen_andi_tl(lsb, PsV, 1); \ tcg_gen_brcondi_tl(TCG_COND_NE, lsb, PRED, false_label); \ - tcg_temp_free(lsb); \ tcg_gen_gvec_mov(MO_64, VdV_off, VuV_off, \ sizeof(MMVector), sizeof(MMVector)); \ - tcg_gen_br(end_label); \ gen_set_label(false_label); \ - tcg_gen_ori_tl(hex_slot_cancelled, hex_slot_cancelled, \ - 1 << insn->slot); \ - gen_set_label(end_label); \ } while (0) @@ -212,7 +241,6 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_andi_tl(shift, RtV, 15); \ tcg_gen_gvec_sars(MO_16, VdV_off, VuV_off, shift, \ sizeof(MMVector), sizeof(MMVector)); \ - tcg_temp_free(shift); \ } while (0) #define fGEN_TCG_V6_vasrh_acc(SHORTCODE) \ @@ -224,7 +252,6 @@ static inline void assert_vhist_tmp(DisasContext *ctx) sizeof(MMVector), sizeof(MMVector)); \ tcg_gen_gvec_add(MO_16, VxV_off, VxV_off, tmpoff, \ sizeof(MMVector), sizeof(MMVector)); \ - tcg_temp_free(shift); \ } while (0) #define fGEN_TCG_V6_vasrw(SHORTCODE) \ @@ -233,7 +260,6 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_andi_tl(shift, RtV, 31); \ tcg_gen_gvec_sars(MO_32, VdV_off, VuV_off, shift, \ sizeof(MMVector), sizeof(MMVector)); \ - tcg_temp_free(shift); \ } while (0) #define fGEN_TCG_V6_vasrw_acc(SHORTCODE) \ @@ -245,7 +271,6 @@ static inline void assert_vhist_tmp(DisasContext *ctx) sizeof(MMVector), sizeof(MMVector)); \ tcg_gen_gvec_add(MO_32, VxV_off, VxV_off, tmpoff, \ sizeof(MMVector), sizeof(MMVector)); \ - tcg_temp_free(shift); \ } while (0) #define fGEN_TCG_V6_vlsrb(SHORTCODE) \ @@ -254,7 +279,6 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_andi_tl(shift, RtV, 7); \ tcg_gen_gvec_shrs(MO_8, VdV_off, VuV_off, shift, \ sizeof(MMVector), sizeof(MMVector)); \ - tcg_temp_free(shift); \ } while (0) #define fGEN_TCG_V6_vlsrh(SHORTCODE) \ @@ -263,7 +287,6 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_andi_tl(shift, RtV, 15); \ tcg_gen_gvec_shrs(MO_16, VdV_off, VuV_off, shift, \ sizeof(MMVector), sizeof(MMVector)); \ - tcg_temp_free(shift); \ } while (0) #define fGEN_TCG_V6_vlsrw(SHORTCODE) \ @@ -272,7 +295,6 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_andi_tl(shift, RtV, 31); \ tcg_gen_gvec_shrs(MO_32, VdV_off, VuV_off, shift, \ sizeof(MMVector), sizeof(MMVector)); \ - tcg_temp_free(shift); \ } while (0) /* Vector shift left - various forms */ @@ -282,7 +304,6 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_andi_tl(shift, RtV, 7); \ tcg_gen_gvec_shls(MO_8, VdV_off, VuV_off, shift, \ sizeof(MMVector), sizeof(MMVector)); \ - tcg_temp_free(shift); \ } while (0) #define fGEN_TCG_V6_vaslh(SHORTCODE) \ @@ -291,7 +312,6 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_andi_tl(shift, RtV, 15); \ tcg_gen_gvec_shls(MO_16, VdV_off, VuV_off, shift, \ sizeof(MMVector), sizeof(MMVector)); \ - tcg_temp_free(shift); \ } while (0) #define fGEN_TCG_V6_vaslh_acc(SHORTCODE) \ @@ -303,7 +323,6 @@ static inline void assert_vhist_tmp(DisasContext *ctx) sizeof(MMVector), sizeof(MMVector)); \ tcg_gen_gvec_add(MO_16, VxV_off, VxV_off, tmpoff, \ sizeof(MMVector), sizeof(MMVector)); \ - tcg_temp_free(shift); \ } while (0) #define fGEN_TCG_V6_vaslw(SHORTCODE) \ @@ -312,7 +331,6 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_andi_tl(shift, RtV, 31); \ tcg_gen_gvec_shls(MO_32, VdV_off, VuV_off, shift, \ sizeof(MMVector), sizeof(MMVector)); \ - tcg_temp_free(shift); \ } while (0) #define fGEN_TCG_V6_vaslw_acc(SHORTCODE) \ @@ -324,7 +342,6 @@ static inline void assert_vhist_tmp(DisasContext *ctx) sizeof(MMVector), sizeof(MMVector)); \ tcg_gen_gvec_add(MO_32, VxV_off, VxV_off, tmpoff, \ sizeof(MMVector), sizeof(MMVector)); \ - tcg_temp_free(shift); \ } while (0) /* Vector max - various forms */ @@ -560,18 +577,12 @@ static inline void assert_vhist_tmp(DisasContext *ctx) do { \ TCGv LSB = tcg_temp_new(); \ TCGLabel *false_label = gen_new_label(); \ - TCGLabel *end_label = gen_new_label(); \ GET_EA; \ PRED; \ tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, false_label); \ - tcg_temp_free(LSB); \ gen_vreg_load(ctx, DSTOFF, EA, true); \ INC; \ - tcg_gen_br(end_label); \ gen_set_label(false_label); \ - tcg_gen_ori_tl(hex_slot_cancelled, hex_slot_cancelled, \ - 1 << insn->slot); \ - gen_set_label(end_label); \ } while (0) #define fGEN_TCG_PRED_VEC_LOAD_pred_pi \ @@ -697,7 +708,7 @@ static inline void assert_vhist_tmp(DisasContext *ctx) #define fGEN_TCG_NEWVAL_VEC_STORE(GET_EA, INC) \ do { \ GET_EA; \ - gen_vreg_store(ctx, insn, pkt, EA, OsN_off, insn->slot, true); \ + gen_vreg_store(ctx, EA, OsN_off, insn->slot, true); \ INC; \ } while (0) @@ -731,18 +742,12 @@ static inline void assert_vhist_tmp(DisasContext *ctx) do { \ TCGv LSB = tcg_temp_new(); \ TCGLabel *false_label = gen_new_label(); \ - TCGLabel *end_label = gen_new_label(); \ GET_EA; \ PRED; \ tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, false_label); \ - tcg_temp_free(LSB); \ - gen_vreg_store(ctx, insn, pkt, EA, SRCOFF, insn->slot, ALIGN); \ + gen_vreg_store(ctx, EA, SRCOFF, insn->slot, ALIGN); \ INC; \ - tcg_gen_br(end_label); \ gen_set_label(false_label); \ - tcg_gen_ori_tl(hex_slot_cancelled, hex_slot_cancelled, \ - 1 << insn->slot); \ - gen_set_label(end_label); \ } while (0) #define fGEN_TCG_PRED_VEC_STORE_pred_pi(ALIGN) \ diff --git a/target/hexagon/gen_trans_funcs.py b/target/hexagon/gen_trans_funcs.py new file mode 100755 index 0000000000..53e844a44b --- /dev/null +++ b/target/hexagon/gen_trans_funcs.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 + +## +## Copyright (c) 2024 Taylor Simpson +## +## 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 io +import re + +import sys +import textwrap +import iset +import hex_common + +encs = { + tag: "".join(reversed(iset.iset[tag]["enc"].replace(" ", ""))) + for tag in iset.tags + if iset.iset[tag]["enc"] != "MISSING ENCODING" +} + + +regre = re.compile(r"((?which_extended = {0 if letter.islower() else 1}; + """)) + +## +## Generate the QEMU decodetree trans_ function for each instruction +## For A2_add: Rd32=add(Rs32,Rt32) +## We produce: +## static bool trans_A2_add(DisasContext *ctx, arg_A2_add *args) +## { +## Insn *insn = ctx->insn; +## insn->opcode = A2_add; +## insn->regno[0] = args->Rd; +## insn->regno[1] = args->Rs; +## insn->regno[2] = args->Rt; +## return true; +## } +## +def gen_trans_funcs(f): + f.write(f"/* DO NOT MODIFY - This file is generated by {sys.argv[0]} */\n\n") + for tag in sorted(encs.keys(), key=iset.tags.index): + regs = ordered_unique(regre.findall(iset.iset[tag]["syntax"])) + imms = ordered_unique(immre.findall(iset.iset[tag]["syntax"])) + + f.write(textwrap.dedent(f"""\ + static bool trans_{tag}(DisasContext *ctx, arg_{tag} *args) + {open_curly} + Insn *insn = ctx->insn; + insn->opcode = {tag}; + """)) + + regno = 0 + for reg in regs: + reg_type = reg[0] + reg_id = reg[1] + f.write(code_fmt(f"""\ + insn->regno[{regno}] = args->{reg_type}{reg_id}; + """)) + regno += 1 + + if len(imms) != 0: + mark_which_imm_extended(f, tag) + + for imm in imms: + imm_type = imm[0] + imm_letter = "i" if imm_type.islower() else "I" + immno = 0 if imm_type.islower() else 1 + imm_shift = int(imm[2]) if imm[2] else 0 + if imm_shift: + f.write(code_fmt(f"""\ + insn->immed[{immno}] = + shift_left(ctx, args->{imm_type}{imm_letter}, + {imm_shift}, {immno}); + """)) + else: + f.write(code_fmt(f"""\ + insn->immed[{immno}] = args->{imm_type}{imm_letter}; + """)) + + f.write(textwrap.dedent(f"""\ + return true; + {close_curly} + """)) + + +if __name__ == "__main__": + hex_common.read_semantics_file(sys.argv[1]) + with open(sys.argv[2], "w") as f: + gen_trans_funcs(f) diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index cd6af4bceb..dbae6c570a 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,7 @@ #include "internal.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" +#include "exec/helper-gen.h" #include "insn.h" #include "opcodes.h" #include "translate.h" @@ -29,93 +30,111 @@ #undef QEMU_GENERATE #include "gen_tcg.h" #include "gen_tcg_hvx.h" +#include "genptr.h" -static inline void gen_log_predicated_reg_write(int rnum, TCGv val, int slot) +TCGv gen_read_reg(TCGv result, int num) { - TCGv zero = tcg_constant_tl(0); - TCGv slot_mask = tcg_temp_new(); - - tcg_gen_andi_tl(slot_mask, hex_slot_cancelled, 1 << slot); - tcg_gen_movcond_tl(TCG_COND_EQ, hex_new_value[rnum], slot_mask, zero, - val, hex_new_value[rnum]); - if (HEX_DEBUG) { - /* - * Do this so HELPER(debug_commit_end) will know - * - * Note that slot_mask indicates the value is not written - * (i.e., slot was cancelled), so we create a true/false value before - * or'ing with hex_reg_written[rnum]. - */ - tcg_gen_setcond_tl(TCG_COND_EQ, slot_mask, slot_mask, zero); - tcg_gen_or_tl(hex_reg_written[rnum], hex_reg_written[rnum], slot_mask); - } - - tcg_temp_free(slot_mask); + tcg_gen_mov_tl(result, hex_gpr[num]); + return result; } -static inline void gen_log_reg_write(int rnum, TCGv val) +TCGv gen_read_preg(TCGv pred, uint8_t num) { - tcg_gen_mov_tl(hex_new_value[rnum], val); + tcg_gen_mov_tl(pred, hex_pred[num]); + return pred; +} + +#define IMMUTABLE (~0) + +const target_ulong reg_immut_masks[TOTAL_PER_THREAD_REGS] = { + [HEX_REG_USR] = 0xc13000c0, + [HEX_REG_PC] = IMMUTABLE, + [HEX_REG_GP] = 0x3f, + [HEX_REG_UPCYCLELO] = IMMUTABLE, + [HEX_REG_UPCYCLEHI] = IMMUTABLE, + [HEX_REG_UTIMERLO] = IMMUTABLE, + [HEX_REG_UTIMERHI] = IMMUTABLE, +}; + +static inline void gen_masked_reg_write(TCGv new_val, TCGv cur_val, + target_ulong reg_mask) +{ + if (reg_mask) { + TCGv tmp = tcg_temp_new(); + + /* new_val = (new_val & ~reg_mask) | (cur_val & reg_mask) */ + tcg_gen_andi_tl(new_val, new_val, ~reg_mask); + tcg_gen_andi_tl(tmp, cur_val, reg_mask); + tcg_gen_or_tl(new_val, new_val, tmp); + } +} + +TCGv get_result_gpr(DisasContext *ctx, int rnum) +{ + if (ctx->need_commit) { + if (rnum == HEX_REG_USR) { + return hex_new_value_usr; + } else { + if (ctx->new_value[rnum] == NULL) { + ctx->new_value[rnum] = tcg_temp_new(); + tcg_gen_movi_tl(ctx->new_value[rnum], 0); + } + return ctx->new_value[rnum]; + } + } else { + return hex_gpr[rnum]; + } +} + +static TCGv_i64 get_result_gpr_pair(DisasContext *ctx, int rnum) +{ + TCGv_i64 result = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(result, get_result_gpr(ctx, rnum), + get_result_gpr(ctx, rnum + 1)); + return result; +} + +void gen_log_reg_write(DisasContext *ctx, int rnum, TCGv val) +{ + const target_ulong reg_mask = reg_immut_masks[rnum]; + + gen_masked_reg_write(val, hex_gpr[rnum], reg_mask); + tcg_gen_mov_tl(get_result_gpr(ctx, rnum), val); if (HEX_DEBUG) { /* Do this so HELPER(debug_commit_end) will know */ tcg_gen_movi_tl(hex_reg_written[rnum], 1); } } -static void gen_log_predicated_reg_write_pair(int rnum, TCGv_i64 val, int slot) +static void gen_log_reg_write_pair(DisasContext *ctx, int rnum, TCGv_i64 val) { TCGv val32 = tcg_temp_new(); - TCGv zero = tcg_constant_tl(0); - TCGv slot_mask = tcg_temp_new(); - tcg_gen_andi_tl(slot_mask, hex_slot_cancelled, 1 << slot); /* Low word */ tcg_gen_extrl_i64_i32(val32, val); - tcg_gen_movcond_tl(TCG_COND_EQ, hex_new_value[rnum], - slot_mask, zero, - val32, hex_new_value[rnum]); + gen_log_reg_write(ctx, rnum, val32); + /* High word */ tcg_gen_extrh_i64_i32(val32, val); - tcg_gen_movcond_tl(TCG_COND_EQ, hex_new_value[rnum + 1], - slot_mask, zero, - val32, hex_new_value[rnum + 1]); - if (HEX_DEBUG) { - /* - * Do this so HELPER(debug_commit_end) will know - * - * Note that slot_mask indicates the value is not written - * (i.e., slot was cancelled), so we create a true/false value before - * or'ing with hex_reg_written[rnum]. - */ - tcg_gen_setcond_tl(TCG_COND_EQ, slot_mask, slot_mask, zero); - tcg_gen_or_tl(hex_reg_written[rnum], hex_reg_written[rnum], slot_mask); - tcg_gen_or_tl(hex_reg_written[rnum + 1], hex_reg_written[rnum + 1], - slot_mask); - } - - tcg_temp_free(val32); - tcg_temp_free(slot_mask); + gen_log_reg_write(ctx, rnum + 1, val32); } -static void gen_log_reg_write_pair(int rnum, TCGv_i64 val) +TCGv get_result_pred(DisasContext *ctx, int pnum) { - /* Low word */ - tcg_gen_extrl_i64_i32(hex_new_value[rnum], val); - if (HEX_DEBUG) { - /* Do this so HELPER(debug_commit_end) will know */ - tcg_gen_movi_tl(hex_reg_written[rnum], 1); - } - - /* High word */ - tcg_gen_extrh_i64_i32(hex_new_value[rnum + 1], val); - if (HEX_DEBUG) { - /* Do this so HELPER(debug_commit_end) will know */ - tcg_gen_movi_tl(hex_reg_written[rnum + 1], 1); + if (ctx->need_commit) { + if (ctx->new_pred_value[pnum] == NULL) { + ctx->new_pred_value[pnum] = tcg_temp_new(); + tcg_gen_movi_tl(ctx->new_pred_value[pnum], 0); + } + return ctx->new_pred_value[pnum]; + } else { + return hex_pred[pnum]; } } -static inline void gen_log_pred_write(DisasContext *ctx, int pnum, TCGv val) +void gen_log_pred_write(DisasContext *ctx, int pnum, TCGv val) { + TCGv pred = get_result_pred(ctx, pnum); TCGv base_val = tcg_temp_new(); tcg_gen_andi_tl(base_val, val, 0xff); @@ -128,14 +147,14 @@ static inline void gen_log_pred_write(DisasContext *ctx, int pnum, TCGv val) * straight assignment. Otherwise, do an and. */ if (!test_bit(pnum, ctx->pregs_written)) { - tcg_gen_mov_tl(hex_new_pred_value[pnum], base_val); + tcg_gen_mov_tl(pred, base_val); } else { - tcg_gen_and_tl(hex_new_pred_value[pnum], - hex_new_pred_value[pnum], base_val); + tcg_gen_and_tl(pred, pred, base_val); } - tcg_gen_ori_tl(hex_pred_written, hex_pred_written, 1 << pnum); - - tcg_temp_free(base_val); + if (HEX_DEBUG) { + tcg_gen_ori_tl(ctx->pred_written, ctx->pred_written, 1 << pnum); + } + set_bit(pnum, ctx->pregs_written); } static inline void gen_read_p3_0(TCGv control_reg) @@ -148,7 +167,7 @@ static inline void gen_read_p3_0(TCGv control_reg) /* * Certain control registers require special handling on read - * HEX_REG_P3_0 aliased to the predicate registers + * HEX_REG_P3_0_ALIASED aliased to the predicate registers * -> concat the 4 predicate registers together * HEX_REG_PC actual value stored in DisasContext * -> assign from ctx->base.pc_next @@ -158,7 +177,7 @@ static inline void gen_read_p3_0(TCGv control_reg) static inline void gen_read_ctrl_reg(DisasContext *ctx, const int reg_num, TCGv dest) { - if (reg_num == HEX_REG_P3_0) { + if (reg_num == HEX_REG_P3_0_ALIASED) { gen_read_p3_0(dest); } else if (reg_num == HEX_REG_PC) { tcg_gen_movi_tl(dest, ctx->base.pc_next); @@ -179,11 +198,10 @@ static inline void gen_read_ctrl_reg(DisasContext *ctx, const int reg_num, static inline void gen_read_ctrl_reg_pair(DisasContext *ctx, const int reg_num, TCGv_i64 dest) { - if (reg_num == HEX_REG_P3_0) { + if (reg_num == HEX_REG_P3_0_ALIASED) { TCGv p3_0 = tcg_temp_new(); gen_read_p3_0(p3_0); tcg_gen_concat_i32_i64(dest, p3_0, hex_gpr[reg_num + 1]); - tcg_temp_free(p3_0); } else if (reg_num == HEX_REG_PC - 1) { TCGv pc = tcg_constant_tl(ctx->base.pc_next); tcg_gen_concat_i32_i64(dest, hex_gpr[reg_num], pc); @@ -195,14 +213,11 @@ static inline void gen_read_ctrl_reg_pair(DisasContext *ctx, const int reg_num, tcg_gen_addi_tl(insn_cnt, hex_gpr[HEX_REG_QEMU_INSN_CNT], ctx->num_insns); tcg_gen_concat_i32_i64(dest, pkt_cnt, insn_cnt); - tcg_temp_free(pkt_cnt); - tcg_temp_free(insn_cnt); } else if (reg_num == HEX_REG_QEMU_HVX_CNT) { TCGv hvx_cnt = tcg_temp_new(); tcg_gen_addi_tl(hvx_cnt, hex_gpr[HEX_REG_QEMU_HVX_CNT], ctx->num_hvx_insns); tcg_gen_concat_i32_i64(dest, hvx_cnt, hex_gpr[reg_num + 1]); - tcg_temp_free(hvx_cnt); } else { tcg_gen_concat_i32_i64(dest, hex_gpr[reg_num], @@ -216,14 +231,12 @@ static void gen_write_p3_0(DisasContext *ctx, TCGv control_reg) for (int i = 0; i < NUM_PREGS; i++) { tcg_gen_extract_tl(hex_p8, control_reg, i * 8, 8); gen_log_pred_write(ctx, i, hex_p8); - ctx_log_pred_write(ctx, i); } - tcg_temp_free(hex_p8); } /* * Certain control registers require special handling on write - * HEX_REG_P3_0 aliased to the predicate registers + * HEX_REG_P3_0_ALIASED aliased to the predicate registers * -> break the value across 4 predicate registers * HEX_REG_QEMU_*_CNT changes in current TB in DisasContext * -> clear the changes @@ -231,11 +244,10 @@ static void gen_write_p3_0(DisasContext *ctx, TCGv control_reg) static inline void gen_write_ctrl_reg(DisasContext *ctx, int reg_num, TCGv val) { - if (reg_num == HEX_REG_P3_0) { + if (reg_num == HEX_REG_P3_0_ALIASED) { gen_write_p3_0(ctx, val); } else { - gen_log_reg_write(reg_num, val); - ctx_log_reg_write(ctx, reg_num); + gen_log_reg_write(ctx, reg_num, val); if (reg_num == HEX_REG_QEMU_PKT_CNT) { ctx->num_packets = 0; } @@ -251,17 +263,15 @@ static inline void gen_write_ctrl_reg(DisasContext *ctx, int reg_num, static inline void gen_write_ctrl_reg_pair(DisasContext *ctx, int reg_num, TCGv_i64 val) { - if (reg_num == HEX_REG_P3_0) { + if (reg_num == HEX_REG_P3_0_ALIASED) { + TCGv result = get_result_gpr(ctx, reg_num + 1); TCGv val32 = tcg_temp_new(); tcg_gen_extrl_i64_i32(val32, val); gen_write_p3_0(ctx, val32); tcg_gen_extrh_i64_i32(val32, val); - gen_log_reg_write(reg_num + 1, val32); - tcg_temp_free(val32); - ctx_log_reg_write(ctx, reg_num + 1); + tcg_gen_mov_tl(result, val32); } else { - gen_log_reg_write_pair(reg_num, val); - ctx_log_reg_write_pair(ctx, reg_num); + gen_log_reg_write_pair(ctx, reg_num, val); if (reg_num == HEX_REG_QEMU_PKT_CNT) { ctx->num_packets = 0; ctx->num_insns = 0; @@ -272,7 +282,7 @@ static inline void gen_write_ctrl_reg_pair(DisasContext *ctx, int reg_num, } } -static TCGv gen_get_byte(TCGv result, int N, TCGv src, bool sign) +TCGv gen_get_byte(TCGv result, int N, TCGv src, bool sign) { if (sign) { tcg_gen_sextract_tl(result, src, N * 8, 8); @@ -282,7 +292,7 @@ static TCGv gen_get_byte(TCGv result, int N, TCGv src, bool sign) return result; } -static TCGv gen_get_byte_i64(TCGv result, int N, TCGv_i64 src, bool sign) +TCGv gen_get_byte_i64(TCGv result, int N, TCGv_i64 src, bool sign) { TCGv_i64 res64 = tcg_temp_new_i64(); if (sign) { @@ -291,12 +301,11 @@ static TCGv gen_get_byte_i64(TCGv result, int N, TCGv_i64 src, bool sign) tcg_gen_extract_i64(res64, src, N * 8, 8); } tcg_gen_extrl_i64_i32(result, res64); - tcg_temp_free_i64(res64); return result; } -static inline TCGv gen_get_half(TCGv result, int N, TCGv src, bool sign) +TCGv gen_get_half(TCGv result, int N, TCGv src, bool sign) { if (sign) { tcg_gen_sextract_tl(result, src, N * 16, 16); @@ -306,37 +315,35 @@ static inline TCGv gen_get_half(TCGv result, int N, TCGv src, bool sign) return result; } -static inline void gen_set_half(int N, TCGv result, TCGv src) +void gen_set_half(int N, TCGv result, TCGv src) { tcg_gen_deposit_tl(result, result, src, N * 16, 16); } -static inline void gen_set_half_i64(int N, TCGv_i64 result, TCGv src) +void gen_set_half_i64(int N, TCGv_i64 result, TCGv src) { TCGv_i64 src64 = tcg_temp_new_i64(); tcg_gen_extu_i32_i64(src64, src); tcg_gen_deposit_i64(result, result, src64, N * 16, 16); - tcg_temp_free_i64(src64); } -static void gen_set_byte_i64(int N, TCGv_i64 result, TCGv src) +void gen_set_byte_i64(int N, TCGv_i64 result, TCGv src) { TCGv_i64 src64 = tcg_temp_new_i64(); tcg_gen_extu_i32_i64(src64, src); tcg_gen_deposit_i64(result, result, src64, N * 8, 8); - tcg_temp_free_i64(src64); } static inline void gen_load_locked4u(TCGv dest, TCGv vaddr, int mem_index) { - tcg_gen_qemu_ld32u(dest, vaddr, mem_index); + tcg_gen_qemu_ld_tl(dest, vaddr, mem_index, MO_TEUL); tcg_gen_mov_tl(hex_llsc_addr, vaddr); tcg_gen_mov_tl(hex_llsc_val, dest); } static inline void gen_load_locked8u(TCGv_i64 dest, TCGv vaddr, int mem_index) { - tcg_gen_qemu_ld64(dest, vaddr, mem_index); + tcg_gen_qemu_ld_i64(dest, vaddr, mem_index, MO_TEUQ); tcg_gen_mov_tl(hex_llsc_addr, vaddr); tcg_gen_mov_i64(hex_llsc_val_i64, dest); } @@ -357,7 +364,6 @@ static inline void gen_store_conditional4(DisasContext *ctx, ctx->mem_idx, MO_32); tcg_gen_movcond_tl(TCG_COND_EQ, pred, tmp, hex_llsc_val, one, zero); - tcg_temp_free(tmp); tcg_gen_br(done); gen_set_label(fail); @@ -384,7 +390,6 @@ static inline void gen_store_conditional8(DisasContext *ctx, tcg_gen_movcond_i64(TCG_COND_EQ, tmp, tmp, hex_llsc_val_i64, one, zero); tcg_gen_extrl_i64_i32(pred, tmp); - tcg_temp_free_i64(tmp); tcg_gen_br(done); gen_set_label(fail); @@ -394,72 +399,68 @@ static inline void gen_store_conditional8(DisasContext *ctx, tcg_gen_movi_tl(hex_llsc_addr, ~0); } -static inline void gen_store32(TCGv vaddr, TCGv src, int width, int slot) +#ifndef CONFIG_HEXAGON_IDEF_PARSER +static TCGv gen_slotval(DisasContext *ctx) +{ + int slotval = (ctx->pkt->pkt_has_store_s1 & 1) | (ctx->insn->slot << 1); + return tcg_constant_tl(slotval); +} +#endif + +void gen_store32(TCGv vaddr, TCGv src, int width, uint32_t slot) { tcg_gen_mov_tl(hex_store_addr[slot], vaddr); tcg_gen_movi_tl(hex_store_width[slot], width); tcg_gen_mov_tl(hex_store_val32[slot], src); } -static inline void gen_store1(TCGv_env cpu_env, TCGv vaddr, TCGv src, - DisasContext *ctx, int slot) +void gen_store1(TCGv_env tcg_env, TCGv vaddr, TCGv src, uint32_t slot) { gen_store32(vaddr, src, 1, slot); - ctx->store_width[slot] = 1; } -static inline void gen_store1i(TCGv_env cpu_env, TCGv vaddr, int32_t src, - DisasContext *ctx, int slot) +void gen_store1i(TCGv_env tcg_env, TCGv vaddr, int32_t src, uint32_t slot) { TCGv tmp = tcg_constant_tl(src); - gen_store1(cpu_env, vaddr, tmp, ctx, slot); + gen_store1(tcg_env, vaddr, tmp, slot); } -static inline void gen_store2(TCGv_env cpu_env, TCGv vaddr, TCGv src, - DisasContext *ctx, int slot) +void gen_store2(TCGv_env tcg_env, TCGv vaddr, TCGv src, uint32_t slot) { gen_store32(vaddr, src, 2, slot); - ctx->store_width[slot] = 2; } -static inline void gen_store2i(TCGv_env cpu_env, TCGv vaddr, int32_t src, - DisasContext *ctx, int slot) +void gen_store2i(TCGv_env tcg_env, TCGv vaddr, int32_t src, uint32_t slot) { TCGv tmp = tcg_constant_tl(src); - gen_store2(cpu_env, vaddr, tmp, ctx, slot); + gen_store2(tcg_env, vaddr, tmp, slot); } -static inline void gen_store4(TCGv_env cpu_env, TCGv vaddr, TCGv src, - DisasContext *ctx, int slot) +void gen_store4(TCGv_env tcg_env, TCGv vaddr, TCGv src, uint32_t slot) { gen_store32(vaddr, src, 4, slot); - ctx->store_width[slot] = 4; } -static inline void gen_store4i(TCGv_env cpu_env, TCGv vaddr, int32_t src, - DisasContext *ctx, int slot) +void gen_store4i(TCGv_env tcg_env, TCGv vaddr, int32_t src, uint32_t slot) { TCGv tmp = tcg_constant_tl(src); - gen_store4(cpu_env, vaddr, tmp, ctx, slot); + gen_store4(tcg_env, vaddr, tmp, slot); } -static inline void gen_store8(TCGv_env cpu_env, TCGv vaddr, TCGv_i64 src, - DisasContext *ctx, int slot) +void gen_store8(TCGv_env tcg_env, TCGv vaddr, TCGv_i64 src, uint32_t slot) { tcg_gen_mov_tl(hex_store_addr[slot], vaddr); tcg_gen_movi_tl(hex_store_width[slot], 8); tcg_gen_mov_i64(hex_store_val64[slot], src); - ctx->store_width[slot] = 8; } -static inline void gen_store8i(TCGv_env cpu_env, TCGv vaddr, int64_t src, - DisasContext *ctx, int slot) +void gen_store8i(TCGv_env tcg_env, TCGv vaddr, int64_t src, uint32_t slot) { TCGv_i64 tmp = tcg_constant_i64(src); - gen_store8(cpu_env, vaddr, tmp, ctx, slot); + gen_store8(tcg_env, vaddr, tmp, slot); } -static TCGv gen_8bitsof(TCGv result, TCGv value) +TCGv gen_8bitsof(TCGv result, TCGv value) { TCGv zero = tcg_constant_tl(0); TCGv ones = tcg_constant_tl(0xff); @@ -468,6 +469,720 @@ static TCGv gen_8bitsof(TCGv result, TCGv value) return result; } +static void gen_write_new_pc_addr(DisasContext *ctx, TCGv addr, + TCGCond cond, TCGv pred) +{ + TCGLabel *pred_false = NULL; + if (cond != TCG_COND_ALWAYS) { + pred_false = gen_new_label(); + tcg_gen_brcondi_tl(cond, pred, 0, pred_false); + } + + if (ctx->pkt->pkt_has_multi_cof) { + /* If there are multiple branches in a packet, ignore the second one */ + tcg_gen_movcond_tl(TCG_COND_NE, hex_gpr[HEX_REG_PC], + ctx->branch_taken, tcg_constant_tl(0), + hex_gpr[HEX_REG_PC], addr); + tcg_gen_movi_tl(ctx->branch_taken, 1); + } else { + tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], addr); + } + + if (cond != TCG_COND_ALWAYS) { + gen_set_label(pred_false); + } +} + +static void gen_write_new_pc_pcrel(DisasContext *ctx, int pc_off, + TCGCond cond, TCGv pred) +{ + target_ulong dest = ctx->pkt->pc + pc_off; + if (ctx->pkt->pkt_has_multi_cof) { + gen_write_new_pc_addr(ctx, tcg_constant_tl(dest), cond, pred); + } else { + /* Defer this jump to the end of the TB */ + ctx->branch_cond = TCG_COND_ALWAYS; + if (pred != NULL) { + ctx->branch_cond = cond; + tcg_gen_mov_tl(ctx->branch_taken, pred); + } + ctx->branch_dest = dest; + } +} + +void gen_set_usr_field(DisasContext *ctx, int field, TCGv val) +{ + TCGv usr = get_result_gpr(ctx, HEX_REG_USR); + tcg_gen_deposit_tl(usr, usr, val, + reg_field_info[field].offset, + reg_field_info[field].width); +} + +void gen_set_usr_fieldi(DisasContext *ctx, int field, int x) +{ + if (reg_field_info[field].width == 1) { + TCGv usr = get_result_gpr(ctx, HEX_REG_USR); + target_ulong bit = 1 << reg_field_info[field].offset; + if ((x & 1) == 1) { + tcg_gen_ori_tl(usr, usr, bit); + } else { + tcg_gen_andi_tl(usr, usr, ~bit); + } + } else { + TCGv val = tcg_constant_tl(x); + gen_set_usr_field(ctx, field, val); + } +} + +static void gen_compare(TCGCond cond, TCGv res, TCGv arg1, TCGv arg2) +{ + TCGv one = tcg_constant_tl(0xff); + TCGv zero = tcg_constant_tl(0); + + tcg_gen_movcond_tl(cond, res, arg1, arg2, one, zero); +} + +#ifndef CONFIG_HEXAGON_IDEF_PARSER +static inline void gen_loop0r(DisasContext *ctx, TCGv RsV, int riV) +{ + fIMMEXT(riV); + fPCALIGN(riV); + gen_log_reg_write(ctx, HEX_REG_LC0, RsV); + gen_log_reg_write(ctx, HEX_REG_SA0, tcg_constant_tl(ctx->pkt->pc + riV)); + gen_set_usr_fieldi(ctx, USR_LPCFG, 0); +} + +static void gen_loop0i(DisasContext *ctx, int count, int riV) +{ + gen_loop0r(ctx, tcg_constant_tl(count), riV); +} + +static inline void gen_loop1r(DisasContext *ctx, TCGv RsV, int riV) +{ + fIMMEXT(riV); + fPCALIGN(riV); + gen_log_reg_write(ctx, HEX_REG_LC1, RsV); + gen_log_reg_write(ctx, HEX_REG_SA1, tcg_constant_tl(ctx->pkt->pc + riV)); +} + +static void gen_loop1i(DisasContext *ctx, int count, int riV) +{ + gen_loop1r(ctx, tcg_constant_tl(count), riV); +} + +static void gen_ploopNsr(DisasContext *ctx, int N, TCGv RsV, int riV) +{ + fIMMEXT(riV); + fPCALIGN(riV); + gen_log_reg_write(ctx, HEX_REG_LC0, RsV); + gen_log_reg_write(ctx, HEX_REG_SA0, tcg_constant_tl(ctx->pkt->pc + riV)); + gen_set_usr_fieldi(ctx, USR_LPCFG, N); + gen_log_pred_write(ctx, 3, tcg_constant_tl(0)); +} + +static void gen_ploopNsi(DisasContext *ctx, int N, int count, int riV) +{ + gen_ploopNsr(ctx, N, tcg_constant_tl(count), riV); +} + +static inline void gen_comparei(TCGCond cond, TCGv res, TCGv arg1, int arg2) +{ + gen_compare(cond, res, arg1, tcg_constant_tl(arg2)); +} +#endif + +static void gen_cond_jumpr(DisasContext *ctx, TCGv dst_pc, + TCGCond cond, TCGv pred) +{ + gen_write_new_pc_addr(ctx, dst_pc, cond, pred); +} + +static void gen_cond_jumpr31(DisasContext *ctx, TCGCond cond, TCGv pred) +{ + TCGv LSB = tcg_temp_new(); + tcg_gen_andi_tl(LSB, pred, 1); + gen_cond_jumpr(ctx, hex_gpr[HEX_REG_LR], cond, LSB); +} + +static void gen_cond_jump(DisasContext *ctx, TCGCond cond, TCGv pred, + int pc_off) +{ + gen_write_new_pc_pcrel(ctx, pc_off, cond, pred); +} + +static void gen_cmpnd_cmp_jmp(DisasContext *ctx, + int pnum, TCGCond cond1, TCGv arg1, TCGv arg2, + TCGCond cond2, int pc_off) +{ + if (ctx->insn->part1) { + TCGv pred = tcg_temp_new(); + gen_compare(cond1, pred, arg1, arg2); + gen_log_pred_write(ctx, pnum, pred); + } else { + TCGv pred = tcg_temp_new(); + tcg_gen_mov_tl(pred, ctx->new_pred_value[pnum]); + gen_cond_jump(ctx, cond2, pred, pc_off); + } +} + +static void gen_cmpnd_cmp_jmp_t(DisasContext *ctx, + int pnum, TCGCond cond, TCGv arg1, TCGv arg2, + int pc_off) +{ + gen_cmpnd_cmp_jmp(ctx, pnum, cond, arg1, arg2, TCG_COND_EQ, pc_off); +} + +static void gen_cmpnd_cmp_jmp_f(DisasContext *ctx, + int pnum, TCGCond cond, TCGv arg1, TCGv arg2, + int pc_off) +{ + gen_cmpnd_cmp_jmp(ctx, pnum, cond, arg1, arg2, TCG_COND_NE, pc_off); +} + +static void gen_cmpnd_cmpi_jmp_t(DisasContext *ctx, + int pnum, TCGCond cond, TCGv arg1, int arg2, + int pc_off) +{ + TCGv tmp = tcg_constant_tl(arg2); + gen_cmpnd_cmp_jmp(ctx, pnum, cond, arg1, tmp, TCG_COND_EQ, pc_off); +} + +static void gen_cmpnd_cmpi_jmp_f(DisasContext *ctx, + int pnum, TCGCond cond, TCGv arg1, int arg2, + int pc_off) +{ + TCGv tmp = tcg_constant_tl(arg2); + gen_cmpnd_cmp_jmp(ctx, pnum, cond, arg1, tmp, TCG_COND_NE, pc_off); +} + +static void gen_cmpnd_cmp_n1_jmp_t(DisasContext *ctx, int pnum, TCGCond cond, + TCGv arg, int pc_off) +{ + gen_cmpnd_cmpi_jmp_t(ctx, pnum, cond, arg, -1, pc_off); +} + +static void gen_cmpnd_cmp_n1_jmp_f(DisasContext *ctx, int pnum, TCGCond cond, + TCGv arg, int pc_off) +{ + gen_cmpnd_cmpi_jmp_f(ctx, pnum, cond, arg, -1, pc_off); +} + +static void gen_cmpnd_tstbit0_jmp(DisasContext *ctx, + int pnum, TCGv arg, TCGCond cond, int pc_off) +{ + if (ctx->insn->part1) { + TCGv pred = tcg_temp_new(); + tcg_gen_andi_tl(pred, arg, 1); + gen_8bitsof(pred, pred); + gen_log_pred_write(ctx, pnum, pred); + } else { + TCGv pred = tcg_temp_new(); + tcg_gen_mov_tl(pred, ctx->new_pred_value[pnum]); + gen_cond_jump(ctx, cond, pred, pc_off); + } +} + +static void gen_testbit0_jumpnv(DisasContext *ctx, + TCGv arg, TCGCond cond, int pc_off) +{ + TCGv pred = tcg_temp_new(); + tcg_gen_andi_tl(pred, arg, 1); + gen_cond_jump(ctx, cond, pred, pc_off); +} + +static void gen_jump(DisasContext *ctx, int pc_off) +{ + gen_write_new_pc_pcrel(ctx, pc_off, TCG_COND_ALWAYS, NULL); +} + +static void gen_jumpr(DisasContext *ctx, TCGv new_pc) +{ + gen_write_new_pc_addr(ctx, new_pc, TCG_COND_ALWAYS, NULL); +} + +static void gen_call(DisasContext *ctx, int pc_off) +{ + TCGv lr = get_result_gpr(ctx, HEX_REG_LR); + tcg_gen_movi_tl(lr, ctx->next_PC); + gen_write_new_pc_pcrel(ctx, pc_off, TCG_COND_ALWAYS, NULL); +} + +static void gen_callr(DisasContext *ctx, TCGv new_pc) +{ + TCGv lr = get_result_gpr(ctx, HEX_REG_LR); + tcg_gen_movi_tl(lr, ctx->next_PC); + gen_write_new_pc_addr(ctx, new_pc, TCG_COND_ALWAYS, NULL); +} + +static void gen_cond_call(DisasContext *ctx, TCGv pred, + TCGCond cond, int pc_off) +{ + TCGv lr = get_result_gpr(ctx, HEX_REG_LR); + TCGv lsb = tcg_temp_new(); + TCGLabel *skip = gen_new_label(); + tcg_gen_andi_tl(lsb, pred, 1); + gen_write_new_pc_pcrel(ctx, pc_off, cond, lsb); + tcg_gen_brcondi_tl(cond, lsb, 0, skip); + tcg_gen_movi_tl(lr, ctx->next_PC); + gen_set_label(skip); +} + +static void gen_cond_callr(DisasContext *ctx, + TCGCond cond, TCGv pred, TCGv new_pc) +{ + TCGv lsb = tcg_temp_new(); + TCGLabel *skip = gen_new_label(); + tcg_gen_andi_tl(lsb, pred, 1); + tcg_gen_brcondi_tl(cond, lsb, 0, skip); + gen_callr(ctx, new_pc); + gen_set_label(skip); +} + +#ifndef CONFIG_HEXAGON_IDEF_PARSER +/* frame = ((LR << 32) | FP) ^ (FRAMEKEY << 32)) */ +static TCGv_i64 gen_frame_scramble(void) +{ + TCGv_i64 frame = tcg_temp_new_i64(); + TCGv tmp = tcg_temp_new(); + tcg_gen_xor_tl(tmp, hex_gpr[HEX_REG_LR], hex_gpr[HEX_REG_FRAMEKEY]); + tcg_gen_concat_i32_i64(frame, hex_gpr[HEX_REG_FP], tmp); + return frame; +} +#endif + +/* frame ^= (int64_t)FRAMEKEY << 32 */ +static void gen_frame_unscramble(TCGv_i64 frame) +{ + TCGv_i64 framekey = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(framekey, hex_gpr[HEX_REG_FRAMEKEY]); + tcg_gen_shli_i64(framekey, framekey, 32); + tcg_gen_xor_i64(frame, frame, framekey); +} + +static void gen_load_frame(DisasContext *ctx, TCGv_i64 frame, TCGv EA) +{ + Insn *insn = ctx->insn; /* Needed for CHECK_NOSHUF */ + CHECK_NOSHUF(EA, 8); + tcg_gen_qemu_ld_i64(frame, EA, ctx->mem_idx, MO_TEUQ); +} + +#ifndef CONFIG_HEXAGON_IDEF_PARSER +/* Stack overflow check */ +static void gen_framecheck(TCGv EA, int framesize) +{ + /* Not modelled in linux-user mode */ + /* Placeholder for system mode */ +#ifndef CONFIG_USER_ONLY + g_assert_not_reached(); +#endif +} + +static void gen_allocframe(DisasContext *ctx, TCGv r29, int framesize) +{ + TCGv r30 = tcg_temp_new(); + TCGv_i64 frame; + tcg_gen_addi_tl(r30, r29, -8); + frame = gen_frame_scramble(); + gen_store8(tcg_env, r30, frame, ctx->insn->slot); + gen_log_reg_write(ctx, HEX_REG_FP, r30); + gen_framecheck(r30, framesize); + tcg_gen_subi_tl(r29, r30, framesize); +} + +static void gen_deallocframe(DisasContext *ctx, TCGv_i64 r31_30, TCGv r30) +{ + TCGv r29 = tcg_temp_new(); + TCGv_i64 frame = tcg_temp_new_i64(); + gen_load_frame(ctx, frame, r30); + gen_frame_unscramble(frame); + tcg_gen_mov_i64(r31_30, frame); + tcg_gen_addi_tl(r29, r30, 8); + gen_log_reg_write(ctx, HEX_REG_SP, r29); +} +#endif + +static void gen_return(DisasContext *ctx, TCGv_i64 dst, TCGv src) +{ + /* + * frame = *src + * dst = frame_unscramble(frame) + * SP = src + 8 + * PC = dst.w[1] + */ + TCGv_i64 frame = tcg_temp_new_i64(); + TCGv r31 = tcg_temp_new(); + TCGv r29 = get_result_gpr(ctx, HEX_REG_SP); + + gen_load_frame(ctx, frame, src); + gen_frame_unscramble(frame); + tcg_gen_mov_i64(dst, frame); + tcg_gen_addi_tl(r29, src, 8); + tcg_gen_extrh_i64_i32(r31, dst); + gen_jumpr(ctx, r31); +} + +/* if (pred) dst = dealloc_return(src):raw */ +static void gen_cond_return(DisasContext *ctx, TCGv_i64 dst, TCGv src, + TCGv pred, TCGCond cond) +{ + TCGv LSB = tcg_temp_new(); + TCGLabel *skip = gen_new_label(); + tcg_gen_andi_tl(LSB, pred, 1); + + tcg_gen_brcondi_tl(cond, LSB, 0, skip); + gen_return(ctx, dst, src); + gen_set_label(skip); +} + +/* sub-instruction version (no RddV, so handle it manually) */ +static void gen_cond_return_subinsn(DisasContext *ctx, TCGCond cond, TCGv pred) +{ + TCGv_i64 RddV = get_result_gpr_pair(ctx, HEX_REG_FP); + gen_cond_return(ctx, RddV, hex_gpr[HEX_REG_FP], pred, cond); + gen_log_reg_write_pair(ctx, HEX_REG_FP, RddV); +} + +static void gen_endloop0(DisasContext *ctx) +{ + TCGv lpcfg = tcg_temp_new(); + + GET_USR_FIELD(USR_LPCFG, lpcfg); + + /* + * if (lpcfg == 1) { + * p3 = 0xff; + * } + */ + TCGLabel *label1 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_NE, lpcfg, 1, label1); + { + gen_log_pred_write(ctx, 3, tcg_constant_tl(0xff)); + } + gen_set_label(label1); + + /* + * if (lpcfg) { + * SET_USR_FIELD(USR_LPCFG, lpcfg - 1); + * } + */ + TCGLabel *label2 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, lpcfg, 0, label2); + { + tcg_gen_subi_tl(lpcfg, lpcfg, 1); + gen_set_usr_field(ctx, USR_LPCFG, lpcfg); + } + gen_set_label(label2); + + /* + * If we're in a tight loop, we'll do this at the end of the TB to take + * advantage of direct block chaining. + */ + if (!ctx->is_tight_loop) { + /* + * if (LC0 > 1) { + * PC = SA0; + * LC0--; + * } + */ + TCGLabel *label3 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_LEU, hex_gpr[HEX_REG_LC0], 1, label3); + { + TCGv lc0 = get_result_gpr(ctx, HEX_REG_LC0); + gen_jumpr(ctx, hex_gpr[HEX_REG_SA0]); + tcg_gen_subi_tl(lc0, hex_gpr[HEX_REG_LC0], 1); + } + gen_set_label(label3); + } +} + +static void gen_endloop1(DisasContext *ctx) +{ + /* + * if (LC1 > 1) { + * PC = SA1; + * LC1--; + * } + */ + TCGLabel *label = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_LEU, hex_gpr[HEX_REG_LC1], 1, label); + { + TCGv lc1 = get_result_gpr(ctx, HEX_REG_LC1); + gen_jumpr(ctx, hex_gpr[HEX_REG_SA1]); + tcg_gen_subi_tl(lc1, hex_gpr[HEX_REG_LC1], 1); + } + gen_set_label(label); +} + +static void gen_endloop01(DisasContext *ctx) +{ + TCGv lpcfg = tcg_temp_new(); + TCGLabel *label1 = gen_new_label(); + TCGLabel *label2 = gen_new_label(); + TCGLabel *label3 = gen_new_label(); + TCGLabel *done = gen_new_label(); + + GET_USR_FIELD(USR_LPCFG, lpcfg); + + /* + * if (lpcfg == 1) { + * p3 = 0xff; + * } + */ + tcg_gen_brcondi_tl(TCG_COND_NE, lpcfg, 1, label1); + { + gen_log_pred_write(ctx, 3, tcg_constant_tl(0xff)); + } + gen_set_label(label1); + + /* + * if (lpcfg) { + * SET_USR_FIELD(USR_LPCFG, lpcfg - 1); + * } + */ + tcg_gen_brcondi_tl(TCG_COND_EQ, lpcfg, 0, label2); + { + tcg_gen_subi_tl(lpcfg, lpcfg, 1); + gen_set_usr_field(ctx, USR_LPCFG, lpcfg); + } + gen_set_label(label2); + + /* + * if (LC0 > 1) { + * PC = SA0; + * LC0--; + * } else if (LC1 > 1) { + * PC = SA1; + * LC1--; + * } + */ + tcg_gen_brcondi_tl(TCG_COND_LEU, hex_gpr[HEX_REG_LC0], 1, label3); + { + TCGv lc0 = get_result_gpr(ctx, HEX_REG_LC0); + gen_jumpr(ctx, hex_gpr[HEX_REG_SA0]); + tcg_gen_subi_tl(lc0, hex_gpr[HEX_REG_LC0], 1); + tcg_gen_br(done); + } + gen_set_label(label3); + tcg_gen_brcondi_tl(TCG_COND_LEU, hex_gpr[HEX_REG_LC1], 1, done); + { + TCGv lc1 = get_result_gpr(ctx, HEX_REG_LC1); + gen_jumpr(ctx, hex_gpr[HEX_REG_SA1]); + tcg_gen_subi_tl(lc1, hex_gpr[HEX_REG_LC1], 1); + } + gen_set_label(done); +} + +static void gen_cmp_jumpnv(DisasContext *ctx, + TCGCond cond, TCGv val, TCGv src, int pc_off) +{ + TCGv pred = tcg_temp_new(); + tcg_gen_setcond_tl(cond, pred, val, src); + gen_cond_jump(ctx, TCG_COND_EQ, pred, pc_off); +} + +static void gen_cmpi_jumpnv(DisasContext *ctx, + TCGCond cond, TCGv val, int src, int pc_off) +{ + TCGv pred = tcg_temp_new(); + tcg_gen_setcondi_tl(cond, pred, val, src); + gen_cond_jump(ctx, TCG_COND_EQ, pred, pc_off); +} + +/* Shift left with saturation */ +static void gen_shl_sat(DisasContext *ctx, TCGv dst, TCGv src, TCGv shift_amt) +{ + TCGv tmp = tcg_temp_new(); /* In case dst == src */ + TCGv usr = get_result_gpr(ctx, HEX_REG_USR); + TCGv sh32 = tcg_temp_new(); + TCGv dst_sar = tcg_temp_new(); + TCGv ovf = tcg_temp_new(); + TCGv satval = tcg_temp_new(); + TCGv min = tcg_constant_tl(0x80000000); + TCGv max = tcg_constant_tl(0x7fffffff); + + /* + * Possible values for shift_amt are 0 .. 64 + * We need special handling for values above 31 + * + * sh32 = shift & 31; + * dst = sh32 == shift ? src : 0; + * dst <<= sh32; + * dst_sar = dst >> sh32; + * satval = src < 0 ? min : max; + * if (dst_asr != src) { + * usr.OVF |= 1; + * dst = satval; + * } + */ + + tcg_gen_andi_tl(sh32, shift_amt, 31); + tcg_gen_movcond_tl(TCG_COND_EQ, tmp, sh32, shift_amt, + src, tcg_constant_tl(0)); + tcg_gen_shl_tl(tmp, tmp, sh32); + tcg_gen_sar_tl(dst_sar, tmp, sh32); + tcg_gen_movcond_tl(TCG_COND_LT, satval, src, tcg_constant_tl(0), min, max); + + tcg_gen_setcond_tl(TCG_COND_NE, ovf, dst_sar, src); + tcg_gen_shli_tl(ovf, ovf, reg_field_info[USR_OVF].offset); + tcg_gen_or_tl(usr, usr, ovf); + + tcg_gen_movcond_tl(TCG_COND_EQ, dst, dst_sar, src, tmp, satval); +} + +static void gen_sar(TCGv dst, TCGv src, TCGv shift_amt) +{ + /* + * Shift arithmetic right + * Robust when shift_amt is >31 bits + */ + TCGv tmp = tcg_temp_new(); + tcg_gen_umin_tl(tmp, shift_amt, tcg_constant_tl(31)); + tcg_gen_sar_tl(dst, src, tmp); +} + +/* Bidirectional shift right with saturation */ +static void gen_asr_r_r_sat(DisasContext *ctx, TCGv RdV, TCGv RsV, TCGv RtV) +{ + TCGv shift_amt = tcg_temp_new(); + TCGLabel *positive = gen_new_label(); + TCGLabel *done = gen_new_label(); + + tcg_gen_sextract_i32(shift_amt, RtV, 0, 7); + tcg_gen_brcondi_tl(TCG_COND_GE, shift_amt, 0, positive); + + /* Negative shift amount => shift left */ + tcg_gen_neg_tl(shift_amt, shift_amt); + gen_shl_sat(ctx, RdV, RsV, shift_amt); + tcg_gen_br(done); + + gen_set_label(positive); + /* Positive shift amount => shift right */ + gen_sar(RdV, RsV, shift_amt); + + gen_set_label(done); +} + +/* Bidirectional shift left with saturation */ +static void gen_asl_r_r_sat(DisasContext *ctx, TCGv RdV, TCGv RsV, TCGv RtV) +{ + TCGv shift_amt = tcg_temp_new(); + TCGLabel *positive = gen_new_label(); + TCGLabel *done = gen_new_label(); + + tcg_gen_sextract_i32(shift_amt, RtV, 0, 7); + tcg_gen_brcondi_tl(TCG_COND_GE, shift_amt, 0, positive); + + /* Negative shift amount => shift right */ + tcg_gen_neg_tl(shift_amt, shift_amt); + gen_sar(RdV, RsV, shift_amt); + tcg_gen_br(done); + + gen_set_label(positive); + /* Positive shift amount => shift left */ + gen_shl_sat(ctx, RdV, RsV, shift_amt); + + gen_set_label(done); +} + +static void gen_insert_rp(DisasContext *ctx, TCGv RxV, TCGv RsV, TCGv_i64 RttV) +{ + /* + * int width = fZXTN(6, 32, (fGETWORD(1, RttV))); + * int offset = fSXTN(7, 32, (fGETWORD(0, RttV))); + * size8u_t mask = ((fCONSTLL(1) << width) - 1); + * if (offset < 0) { + * RxV = 0; + * } else { + * RxV &= ~(mask << offset); + * RxV |= ((RsV & mask) << offset); + * } + */ + + TCGv width = tcg_temp_new(); + TCGv offset = tcg_temp_new(); + TCGv_i64 mask = tcg_temp_new_i64(); + TCGv_i64 result = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_new_i64(); + TCGv_i64 offset64 = tcg_temp_new_i64(); + TCGLabel *label = gen_new_label(); + TCGLabel *done = gen_new_label(); + + tcg_gen_extrh_i64_i32(width, RttV); + tcg_gen_extract_tl(width, width, 0, 6); + tcg_gen_extrl_i64_i32(offset, RttV); + tcg_gen_sextract_tl(offset, offset, 0, 7); + /* Possible values for offset are -64 .. 63 */ + tcg_gen_brcondi_tl(TCG_COND_GE, offset, 0, label); + /* For negative offsets, zero out the result */ + tcg_gen_movi_tl(RxV, 0); + tcg_gen_br(done); + gen_set_label(label); + /* At this point, possible values of offset are 0 .. 63 */ + tcg_gen_ext_i32_i64(mask, width); + tcg_gen_shl_i64(mask, tcg_constant_i64(1), mask); + tcg_gen_subi_i64(mask, mask, 1); + tcg_gen_extu_i32_i64(result, RxV); + tcg_gen_ext_i32_i64(tmp, offset); + tcg_gen_shl_i64(tmp, mask, tmp); + tcg_gen_andc_i64(result, result, tmp); + tcg_gen_extu_i32_i64(tmp, RsV); + tcg_gen_and_i64(tmp, tmp, mask); + tcg_gen_extu_i32_i64(offset64, offset); + tcg_gen_shl_i64(tmp, tmp, offset64); + tcg_gen_or_i64(result, result, tmp); + tcg_gen_extrl_i64_i32(RxV, result); + gen_set_label(done); +} + +static void gen_asr_r_svw_trun(DisasContext *ctx, TCGv RdV, + TCGv_i64 RssV, TCGv RtV) +{ + /* + * for (int i = 0; i < 2; i++) { + * fSETHALF(i, RdV, fGETHALF(0, ((fSXTN(7, 32, RtV) > 0) ? + * (fCAST4_8s(fGETWORD(i, RssV)) >> fSXTN(7, 32, RtV)) : + * (fCAST4_8s(fGETWORD(i, RssV)) << -fSXTN(7, 32, RtV))))); + * } + */ + TCGv shift_amt32 = tcg_temp_new(); + TCGv_i64 shift_amt64 = tcg_temp_new_i64(); + TCGv_i64 tmp64 = tcg_temp_new_i64(); + TCGv tmp32 = tcg_temp_new(); + TCGLabel *label = gen_new_label(); + TCGLabel *zero = gen_new_label(); + TCGLabel *done = gen_new_label(); + + tcg_gen_sextract_tl(shift_amt32, RtV, 0, 7); + /* Possible values of shift_amt32 are -64 .. 63 */ + tcg_gen_brcondi_tl(TCG_COND_LE, shift_amt32, 0, label); + /* After branch, possible values of shift_amt32 are 1 .. 63 */ + tcg_gen_ext_i32_i64(shift_amt64, shift_amt32); + for (int i = 0; i < 2; i++) { + tcg_gen_sextract_i64(tmp64, RssV, i * 32, 32); + tcg_gen_sar_i64(tmp64, tmp64, shift_amt64); + tcg_gen_extrl_i64_i32(tmp32, tmp64); + tcg_gen_deposit_tl(RdV, RdV, tmp32, i * 16, 16); + } + tcg_gen_br(done); + gen_set_label(label); + tcg_gen_neg_tl(shift_amt32, shift_amt32); + /*At this point, possible values of shift_amt32 are 0 .. 64 */ + tcg_gen_brcondi_tl(TCG_COND_GT, shift_amt32, 63, zero); + /*At this point, possible values of shift_amt32 are 0 .. 63 */ + tcg_gen_ext_i32_i64(shift_amt64, shift_amt32); + for (int i = 0; i < 2; i++) { + tcg_gen_sextract_i64(tmp64, RssV, i * 32, 32); + tcg_gen_shl_i64(tmp64, tmp64, shift_amt64); + tcg_gen_extrl_i64_i32(tmp32, tmp64); + tcg_gen_deposit_tl(RdV, RdV, tmp32, i * 16, 16); + } + tcg_gen_br(done); + gen_set_label(zero); + /* When the shift_amt is 64, zero out the result */ + tcg_gen_movi_tl(RdV, 0); + gen_set_label(done); +} + static intptr_t vreg_src_off(DisasContext *ctx, int num) { intptr_t offset = offsetof(CPUHexagonState, VRegs[num]); @@ -482,69 +1197,35 @@ static intptr_t vreg_src_off(DisasContext *ctx, int num) } static void gen_log_vreg_write(DisasContext *ctx, intptr_t srcoff, int num, - VRegWriteType type, int slot_num, - bool is_predicated) + VRegWriteType type) { - TCGLabel *label_end = NULL; intptr_t dstoff; - if (is_predicated) { - TCGv cancelled = tcg_temp_local_new(); - label_end = gen_new_label(); - - /* Don't do anything if the slot was cancelled */ - tcg_gen_extract_tl(cancelled, hex_slot_cancelled, slot_num, 1); - tcg_gen_brcondi_tl(TCG_COND_NE, cancelled, 0, label_end); - tcg_temp_free(cancelled); - } - if (type != EXT_TMP) { dstoff = ctx_future_vreg_off(ctx, num, 1, true); tcg_gen_gvec_mov(MO_64, dstoff, srcoff, sizeof(MMVector), sizeof(MMVector)); - tcg_gen_ori_tl(hex_VRegs_updated, hex_VRegs_updated, 1 << num); } else { dstoff = ctx_tmp_vreg_off(ctx, num, 1, false); tcg_gen_gvec_mov(MO_64, dstoff, srcoff, sizeof(MMVector), sizeof(MMVector)); } - - if (is_predicated) { - gen_set_label(label_end); - } } static void gen_log_vreg_write_pair(DisasContext *ctx, intptr_t srcoff, int num, - VRegWriteType type, int slot_num, - bool is_predicated) + VRegWriteType type) { - gen_log_vreg_write(ctx, srcoff, num ^ 0, type, slot_num, is_predicated); + gen_log_vreg_write(ctx, srcoff, num ^ 0, type); srcoff += sizeof(MMVector); - gen_log_vreg_write(ctx, srcoff, num ^ 1, type, slot_num, is_predicated); + gen_log_vreg_write(ctx, srcoff, num ^ 1, type); } -static void gen_log_qreg_write(intptr_t srcoff, int num, int vnew, - int slot_num, bool is_predicated) +static intptr_t get_result_qreg(DisasContext *ctx, int qnum) { - TCGLabel *label_end = NULL; - intptr_t dstoff; - - if (is_predicated) { - TCGv cancelled = tcg_temp_local_new(); - label_end = gen_new_label(); - - /* Don't do anything if the slot was cancelled */ - tcg_gen_extract_tl(cancelled, hex_slot_cancelled, slot_num, 1); - tcg_gen_brcondi_tl(TCG_COND_NE, cancelled, 0, label_end); - tcg_temp_free(cancelled); - } - - dstoff = offsetof(CPUHexagonState, future_QRegs[num]); - tcg_gen_gvec_mov(MO_64, dstoff, srcoff, sizeof(MMQReg), sizeof(MMQReg)); - - if (is_predicated) { - tcg_gen_ori_tl(hex_QRegs_updated, hex_QRegs_updated, 1 << num); - gen_set_label(label_end); + if (ctx->need_commit) { + return offsetof(CPUHexagonState, future_QRegs[qnum]); + } else { + return offsetof(CPUHexagonState, QRegs[qnum]); } } @@ -556,22 +1237,21 @@ static void gen_vreg_load(DisasContext *ctx, intptr_t dstoff, TCGv src, tcg_gen_andi_tl(src, src, ~((int32_t)sizeof(MMVector) - 1)); } for (int i = 0; i < sizeof(MMVector) / 8; i++) { - tcg_gen_qemu_ld64(tmp, src, ctx->mem_idx); + tcg_gen_qemu_ld_i64(tmp, src, ctx->mem_idx, MO_TEUQ); tcg_gen_addi_tl(src, src, 8); - tcg_gen_st_i64(tmp, cpu_env, dstoff + i * 8); + tcg_gen_st_i64(tmp, tcg_env, dstoff + i * 8); } - tcg_temp_free_i64(tmp); } -static void gen_vreg_store(DisasContext *ctx, Insn *insn, Packet *pkt, - TCGv EA, intptr_t srcoff, int slot, bool aligned) +static void gen_vreg_store(DisasContext *ctx, TCGv EA, intptr_t srcoff, + int slot, bool aligned) { intptr_t dstoff = offsetof(CPUHexagonState, vstore[slot].data); intptr_t maskoff = offsetof(CPUHexagonState, vstore[slot].mask); - if (is_gather_store_insn(insn, pkt)) { + if (is_gather_store_insn(ctx)) { TCGv sl = tcg_constant_tl(slot); - gen_helper_gather_store(cpu_env, EA, sl); + gen_helper_gather_store(tcg_env, EA, sl); return; } @@ -621,7 +1301,7 @@ static void vec_to_qvec(size_t size, intptr_t dstoff, intptr_t srcoff) TCGv_i64 ones = tcg_constant_i64(~0); for (int i = 0; i < sizeof(MMVector) / 8; i++) { - tcg_gen_ld_i64(tmp, cpu_env, srcoff + i * 8); + tcg_gen_ld_i64(tmp, tcg_env, srcoff + i * 8); tcg_gen_movi_i64(mask, 0); for (int j = 0; j < 8; j += size) { @@ -630,12 +1310,153 @@ static void vec_to_qvec(size_t size, intptr_t dstoff, intptr_t srcoff) tcg_gen_deposit_i64(mask, mask, bits, j, size); } - tcg_gen_st8_i64(mask, cpu_env, dstoff + i); + tcg_gen_st8_i64(mask, tcg_env, dstoff + i); } - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(word); - tcg_temp_free_i64(bits); - tcg_temp_free_i64(mask); +} + +void probe_noshuf_load(TCGv va, int s, int mi) +{ + TCGv size = tcg_constant_tl(s); + TCGv mem_idx = tcg_constant_tl(mi); + gen_helper_probe_noshuf_load(tcg_env, va, size, mem_idx); +} + +/* + * Note: Since this function might branch, `val` is + * required to be a `tcg_temp_local`. + */ +void gen_set_usr_field_if(DisasContext *ctx, int field, TCGv val) +{ + /* Sets the USR field if `val` is non-zero */ + if (reg_field_info[field].width == 1) { + TCGv usr = get_result_gpr(ctx, HEX_REG_USR); + TCGv tmp = tcg_temp_new(); + tcg_gen_extract_tl(tmp, val, 0, reg_field_info[field].width); + tcg_gen_shli_tl(tmp, tmp, reg_field_info[field].offset); + tcg_gen_or_tl(usr, usr, tmp); + } else { + TCGLabel *skip_label = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, val, 0, skip_label); + gen_set_usr_field(ctx, field, val); + gen_set_label(skip_label); + } +} + +void gen_sat_i32(TCGv dest, TCGv source, int width) +{ + TCGv max_val = tcg_constant_tl((1 << (width - 1)) - 1); + TCGv min_val = tcg_constant_tl(-(1 << (width - 1))); + tcg_gen_smin_tl(dest, source, max_val); + tcg_gen_smax_tl(dest, dest, min_val); +} + +void gen_sat_i32_ovfl(TCGv ovfl, TCGv dest, TCGv source, int width) +{ + TCGv tmp = tcg_temp_new(); /* In case dest == source */ + gen_sat_i32(tmp, source, width); + tcg_gen_setcond_tl(TCG_COND_NE, ovfl, source, tmp); + tcg_gen_mov_tl(dest, tmp); +} + +void gen_satu_i32(TCGv dest, TCGv source, int width) +{ + TCGv tmp = tcg_temp_new(); /* In case dest == source */ + TCGv max_val = tcg_constant_tl((1 << width) - 1); + TCGv zero = tcg_constant_tl(0); + tcg_gen_movcond_tl(TCG_COND_GTU, tmp, source, max_val, max_val, source); + tcg_gen_movcond_tl(TCG_COND_LT, tmp, source, zero, zero, tmp); + tcg_gen_mov_tl(dest, tmp); +} + +void gen_satu_i32_ovfl(TCGv ovfl, TCGv dest, TCGv source, int width) +{ + TCGv tmp = tcg_temp_new(); /* In case dest == source */ + gen_satu_i32(tmp, source, width); + tcg_gen_setcond_tl(TCG_COND_NE, ovfl, source, tmp); + tcg_gen_mov_tl(dest, tmp); +} + +void gen_sat_i64(TCGv_i64 dest, TCGv_i64 source, int width) +{ + TCGv_i64 max_val = tcg_constant_i64((1LL << (width - 1)) - 1LL); + TCGv_i64 min_val = tcg_constant_i64(-(1LL << (width - 1))); + tcg_gen_smin_i64(dest, source, max_val); + tcg_gen_smax_i64(dest, dest, min_val); +} + +void gen_sat_i64_ovfl(TCGv ovfl, TCGv_i64 dest, TCGv_i64 source, int width) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); /* In case dest == source */ + TCGv_i64 ovfl_64; + gen_sat_i64(tmp, source, width); + ovfl_64 = tcg_temp_new_i64(); + tcg_gen_setcond_i64(TCG_COND_NE, ovfl_64, tmp, source); + tcg_gen_mov_i64(dest, tmp); + tcg_gen_trunc_i64_tl(ovfl, ovfl_64); +} + +void gen_satu_i64(TCGv_i64 dest, TCGv_i64 source, int width) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); /* In case dest == source */ + TCGv_i64 max_val = tcg_constant_i64((1LL << width) - 1LL); + TCGv_i64 zero = tcg_constant_i64(0); + tcg_gen_movcond_i64(TCG_COND_GTU, tmp, source, max_val, max_val, source); + tcg_gen_movcond_i64(TCG_COND_LT, tmp, source, zero, zero, tmp); + tcg_gen_mov_i64(dest, tmp); +} + +void gen_satu_i64_ovfl(TCGv ovfl, TCGv_i64 dest, TCGv_i64 source, int width) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); /* In case dest == source */ + TCGv_i64 ovfl_64; + gen_satu_i64(tmp, source, width); + ovfl_64 = tcg_temp_new_i64(); + tcg_gen_setcond_i64(TCG_COND_NE, ovfl_64, tmp, source); + tcg_gen_mov_i64(dest, tmp); + tcg_gen_trunc_i64_tl(ovfl, ovfl_64); +} + +/* Implements the fADDSAT64 macro in TCG */ +void gen_add_sat_i64(DisasContext *ctx, TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 sum = tcg_temp_new_i64(); + TCGv_i64 xor = tcg_temp_new_i64(); + TCGv_i64 cond1 = tcg_temp_new_i64(); + TCGv_i64 cond2 = tcg_temp_new_i64(); + TCGv_i64 cond3 = tcg_temp_new_i64(); + TCGv_i64 mask = tcg_constant_i64(0x8000000000000000ULL); + TCGv_i64 max_pos = tcg_constant_i64(0x7FFFFFFFFFFFFFFFLL); + TCGv_i64 max_neg = tcg_constant_i64(0x8000000000000000LL); + TCGv_i64 zero = tcg_constant_i64(0); + TCGLabel *no_ovfl_label = gen_new_label(); + TCGLabel *ovfl_label = gen_new_label(); + TCGLabel *ret_label = gen_new_label(); + + tcg_gen_add_i64(sum, a, b); + tcg_gen_xor_i64(xor, a, b); + + /* if (xor & mask) */ + tcg_gen_and_i64(cond1, xor, mask); + tcg_gen_brcondi_i64(TCG_COND_NE, cond1, 0, no_ovfl_label); + + /* else if ((a ^ sum) & mask) */ + tcg_gen_xor_i64(cond2, a, sum); + tcg_gen_and_i64(cond2, cond2, mask); + tcg_gen_brcondi_i64(TCG_COND_NE, cond2, 0, ovfl_label); + /* fallthrough to no_ovfl_label branch */ + + /* if branch */ + gen_set_label(no_ovfl_label); + tcg_gen_mov_i64(ret, sum); + tcg_gen_br(ret_label); + + /* else if branch */ + gen_set_label(ovfl_label); + tcg_gen_and_i64(cond3, sum, mask); + tcg_gen_movcond_i64(TCG_COND_NE, ret, cond3, zero, max_pos, max_neg); + gen_set_usr_fieldi(ctx, USR_OVF, 1); + + gen_set_label(ret_label); } #include "tcg_funcs_generated.c.inc" diff --git a/target/hexagon/genptr.h b/target/hexagon/genptr.h index c158005d2a..a4b43c2910 100644 --- a/target/hexagon/genptr.h +++ b/target/hexagon/genptr.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,7 +19,47 @@ #define HEXAGON_GENPTR_H #include "insn.h" +#include "tcg/tcg.h" +#include "translate.h" extern const SemanticInsn opcode_genptr[]; +void gen_store32(TCGv vaddr, TCGv src, int width, uint32_t slot); +void gen_store1(TCGv_env cpu_env, TCGv vaddr, TCGv src, uint32_t slot); +void gen_store2(TCGv_env cpu_env, TCGv vaddr, TCGv src, uint32_t slot); +void gen_store4(TCGv_env cpu_env, TCGv vaddr, TCGv src, uint32_t slot); +void gen_store8(TCGv_env cpu_env, TCGv vaddr, TCGv_i64 src, uint32_t slot); +void gen_store1i(TCGv_env cpu_env, TCGv vaddr, int32_t src, uint32_t slot); +void gen_store2i(TCGv_env cpu_env, TCGv vaddr, int32_t src, uint32_t slot); +void gen_store4i(TCGv_env cpu_env, TCGv vaddr, int32_t src, uint32_t slot); +void gen_store8i(TCGv_env cpu_env, TCGv vaddr, int64_t src, uint32_t slot); +TCGv gen_read_reg(TCGv result, int num); +TCGv gen_read_preg(TCGv pred, uint8_t num); +TCGv get_result_gpr(DisasContext *ctx, int rnum); +TCGv get_result_pred(DisasContext *ctx, int pnum); +void gen_log_reg_write(DisasContext *ctx, int rnum, TCGv val); +void gen_log_pred_write(DisasContext *ctx, int pnum, TCGv val); +void gen_set_usr_field(DisasContext *ctx, int field, TCGv val); +void gen_set_usr_fieldi(DisasContext *ctx, int field, int x); +void gen_set_usr_field_if(DisasContext *ctx, int field, TCGv val); +void gen_sat_i32(TCGv dest, TCGv source, int width); +void gen_sat_i32_ovfl(TCGv ovfl, TCGv dest, TCGv source, int width); +void gen_satu_i32(TCGv dest, TCGv source, int width); +void gen_satu_i32_ovfl(TCGv ovfl, TCGv dest, TCGv source, int width); +void gen_sat_i64(TCGv_i64 dest, TCGv_i64 source, int width); +void gen_sat_i64_ovfl(TCGv ovfl, TCGv_i64 dest, TCGv_i64 source, int width); +void gen_satu_i64(TCGv_i64 dest, TCGv_i64 source, int width); +void gen_satu_i64_ovfl(TCGv ovfl, TCGv_i64 dest, TCGv_i64 source, int width); +void gen_add_sat_i64(DisasContext *ctx, TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b); +TCGv gen_8bitsof(TCGv result, TCGv value); +void gen_set_byte_i64(int N, TCGv_i64 result, TCGv src); +TCGv gen_get_byte(TCGv result, int N, TCGv src, bool sign); +TCGv gen_get_byte_i64(TCGv result, int N, TCGv_i64 src, bool sign); +TCGv gen_get_half(TCGv result, int N, TCGv src, bool sign); +void gen_set_half(int N, TCGv result, TCGv src); +void gen_set_half_i64(int N, TCGv_i64 result, TCGv src); +void probe_noshuf_load(TCGv va, int s, int mi); + +extern const target_ulong reg_immut_masks[TOTAL_PER_THREAD_REGS]; + #endif diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h index c89aa4ed4d..fa0ebaf7c8 100644 --- a/target/hexagon/helper.h +++ b/target/hexagon/helper.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_RETURN, noreturn, env, i32) DEF_HELPER_1(debug_start_packet, void, env) DEF_HELPER_FLAGS_3(debug_check_store_width, TCG_CALL_NO_WG, void, env, int, int) -DEF_HELPER_FLAGS_3(debug_commit_end, TCG_CALL_NO_WG, void, env, int, int) +DEF_HELPER_FLAGS_5(debug_commit_end, TCG_CALL_NO_WG, void, env, i32, int, int, int) DEF_HELPER_2(commit_store, void, env, int) DEF_HELPER_3(gather_store, void, env, i32, int) DEF_HELPER_1(commit_hvx_stores, void, env) @@ -29,8 +29,10 @@ DEF_HELPER_FLAGS_4(fcircadd, TCG_CALL_NO_RWG_SE, s32, s32, s32, s32, s32) DEF_HELPER_FLAGS_1(fbrev, TCG_CALL_NO_RWG_SE, i32, i32) DEF_HELPER_3(sfrecipa, i64, env, f32, f32) DEF_HELPER_2(sfinvsqrta, i64, env, f32) -DEF_HELPER_4(vacsh_val, s64, env, s64, s64, s64) +DEF_HELPER_5(vacsh_val, s64, env, s64, s64, s64, i32) DEF_HELPER_FLAGS_4(vacsh_pred, TCG_CALL_NO_RWG_SE, s32, env, s64, s64, s64) +DEF_HELPER_FLAGS_2(cabacdecbin_val, TCG_CALL_NO_RWG_SE, s64, s64, s64) +DEF_HELPER_FLAGS_2(cabacdecbin_pred, TCG_CALL_NO_RWG_SE, s32, s64, s64) /* Floating point */ DEF_HELPER_2(conv_sf2df, f64, env, f32) @@ -104,6 +106,7 @@ DEF_HELPER_1(vwhist128q, void, env) DEF_HELPER_2(vwhist128m, void, env, s32) DEF_HELPER_2(vwhist128qm, void, env, s32) +DEF_HELPER_4(probe_noshuf_load, void, env, i32, int, int) DEF_HELPER_2(probe_pkt_scalar_store_s0, void, env, int) DEF_HELPER_2(probe_hvx_stores, void, env, int) -DEF_HELPER_3(probe_pkt_scalar_hvx_stores, void, env, int, int) +DEF_HELPER_2(probe_pkt_scalar_hvx_stores, void, env, int) diff --git a/target/hexagon/hex_arch_types.h b/target/hexagon/hex_arch_types.h index 885f68f760..52a7f2b2f3 100644 --- a/target/hexagon/hex_arch_types.h +++ b/target/hexagon/hex_arch_types.h @@ -18,7 +18,6 @@ #ifndef HEXAGON_HEX_ARCH_TYPES_H #define HEXAGON_HEX_ARCH_TYPES_H -#include "qemu/osdep.h" #include "mmvec/mmvec.h" #include "qemu/int128.h" diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index c81aca8d2a..195620c7ec 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -20,14 +20,18 @@ import sys import re import string +import textwrap -behdict = {} # tag ->behavior -semdict = {} # tag -> semantics -attribdict = {} # tag -> attributes -macros = {} # macro -> macro information... -attribinfo = {} # Register information and misc -tags = [] # list of all tags -overrides = {} # tags with helper overrides +behdict = {} # tag ->behavior +semdict = {} # tag -> semantics +attribdict = {} # tag -> attributes +macros = {} # macro -> macro information... +attribinfo = {} # Register information and misc +registers = {} # register -> register functions +new_registers = {} +tags = [] # list of all tags +overrides = {} # tags with helper overrides +idef_parser_enabled = {} # tags enabled for idef-parser # We should do this as a hash for performance, # but to keep order let's keep it as a list. @@ -36,84 +40,108 @@ def uniquify(seq): seen_add = seen.add return [x for x in seq if x not in seen and not seen_add(x)] -regre = re.compile( - r"((?" % l) - macro.attribs |= expand_macro_attribs( - macros[submacro], allmac_re) + raise Exception(f"Couldn't find macro: <{l}>") + macro.attribs |= expand_macro_attribs(macros[submacro], allmac_re) finished_macros.add(macro.key) return macro.attribs + # When qemu needs an attribute that isn't in the imported files, # we'll add it here. def add_qemu_macro_attrib(name, attrib): macros[name].attribs.add(attrib) -immextre = re.compile(r'f(MUST_)?IMMEXT[(]([UuSsRr])') + +immextre = re.compile(r"f(MUST_)?IMMEXT[(]([UuSsRr])") + + +def is_cond_jump(tag): + if tag == "J2_rte": + return False + if "A_HWLOOP0_END" in attribdict[tag] or "A_HWLOOP1_END" in attribdict[tag]: + return False + return re.compile(r"(if.*fBRANCH)|(if.*fJUMPR)").search(semdict[tag]) != None + + +def is_cond_call(tag): + return re.compile(r"(if.*fCALL)").search(semdict[tag]) != None + + def calculate_attribs(): - add_qemu_macro_attrib('fREAD_PC', 'A_IMPLICIT_READS_PC') - add_qemu_macro_attrib('fTRAP', 'A_IMPLICIT_READS_PC') - add_qemu_macro_attrib('fWRITE_P0', 'A_WRITES_PRED_REG') - add_qemu_macro_attrib('fWRITE_P1', 'A_WRITES_PRED_REG') - add_qemu_macro_attrib('fWRITE_P2', 'A_WRITES_PRED_REG') - add_qemu_macro_attrib('fWRITE_P3', 'A_WRITES_PRED_REG') - add_qemu_macro_attrib('fSET_OVERFLOW', 'A_IMPLICIT_WRITES_USR') - add_qemu_macro_attrib('fSET_LPCFG', 'A_IMPLICIT_WRITES_USR') + add_qemu_macro_attrib("fREAD_PC", "A_IMPLICIT_READS_PC") + add_qemu_macro_attrib("fTRAP", "A_IMPLICIT_READS_PC") + add_qemu_macro_attrib("fSET_OVERFLOW", "A_IMPLICIT_WRITES_USR") + add_qemu_macro_attrib("fSET_LPCFG", "A_IMPLICIT_WRITES_USR") + add_qemu_macro_attrib("fLOAD", "A_SCALAR_LOAD") + add_qemu_macro_attrib("fSTORE", "A_SCALAR_STORE") + add_qemu_macro_attrib('fLSBNEW0', 'A_IMPLICIT_READS_P0') + add_qemu_macro_attrib('fLSBNEW0NOT', 'A_IMPLICIT_READS_P0') + add_qemu_macro_attrib('fREAD_P0', 'A_IMPLICIT_READS_P0') + add_qemu_macro_attrib('fLSBNEW1', 'A_IMPLICIT_READS_P1') + add_qemu_macro_attrib('fLSBNEW1NOT', 'A_IMPLICIT_READS_P1') + add_qemu_macro_attrib('fREAD_P3', 'A_IMPLICIT_READS_P3') # Recurse down macros, find attributes from sub-macros macroValues = list(macros.values()) - allmacros_restr = "|".join(set([ m.re.pattern for m in macroValues ])) + allmacros_restr = "|".join(set([m.re.pattern for m in macroValues])) allmacros_re = re.compile(allmacros_restr) for macro in macroValues: - expand_macro_attribs(macro,allmacros_re) + expand_macro_attribs(macro, allmacros_re) # Append attributes to all instructions for tag in tags: for macname in allmacros_re.findall(semdict[tag]): - if not macname: continue + if not macname: + continue macro = macros[macname] attribdict[tag] |= set(macro.attribs) - # Figure out which instructions write predicate registers - tagregs = get_tagregs() + # Mark conditional jumps and calls + # Not all instructions are properly marked with A_CONDEXEC for tag in tags: - regs = tagregs[tag] - for regtype, regid, toss, numregs in regs: - if regtype == "P" and is_written(regid): - attribdict[tag].add('A_WRITES_PRED_REG') + if is_cond_jump(tag) or is_cond_call(tag): + attribdict[tag].add("A_CONDEXEC") + def SEMANTICS(tag, beh, sem): - #print tag,beh,sem + # print tag,beh,sem behdict[tag] = beh semdict[tag] = sem attribdict[tag] = set() - tags.append(tag) # dicts have no order, this is for order + tags.append(tag) # dicts have no order, this is for order + def ATTRIBUTES(tag, attribstring): - attribstring = \ - attribstring.replace("ATTRIBS","").replace("(","").replace(")","") + attribstring = attribstring.replace("ATTRIBS", "").replace("(", "").replace(")", "") if not attribstring: return attribs = attribstring.split(",") for attrib in attribs: attribdict[tag].add(attrib.strip()) + class Macro(object): - __slots__ = ['key','name', 'beh', 'attribs', 're'] + __slots__ = ["key", "name", "beh", "attribs", "re"] + def __init__(self, name, beh, attribs): self.key = name self.name = name @@ -121,20 +149,25 @@ class Macro(object): self.attribs = set(attribs) self.re = re.compile("\\b" + name + "\\b") -def MACROATTRIB(macname,beh,attribstring): - attribstring = attribstring.replace("(","").replace(")","") + +def MACROATTRIB(macname, beh, attribstring): + attribstring = attribstring.replace("(", "").replace(")", "") if attribstring: attribs = attribstring.split(",") else: attribs = [] - macros[macname] = Macro(macname,beh,attribs) + macros[macname] = Macro(macname, beh, attribs) -def compute_tag_regs(tag): - return uniquify(regre.findall(behdict[tag])) +def compute_tag_regs(tag, full): + tagregs = regre.findall(behdict[tag]) + if not full: + tagregs = map(lambda reg: reg[:2], tagregs) + return uniquify(tagregs) def compute_tag_immediates(tag): return uniquify(immre.findall(behdict[tag])) + ## ## tagregs is the main data structure we'll use ## tagregs[tag] will contain the registers used by an instruction @@ -156,72 +189,65 @@ def compute_tag_immediates(tag): ## x, y read-write register ## xx, yy read-write register pair ## -def get_tagregs(): - return dict(zip(tags, list(map(compute_tag_regs, tags)))) +def get_tagregs(full=False): + compute_func = lambda tag: compute_tag_regs(tag, full) + return dict(zip(tags, list(map(compute_func, tags)))) def get_tagimms(): return dict(zip(tags, list(map(compute_tag_immediates, tags)))) -def is_pair(regid): - return len(regid) == 2 - -def is_single(regid): - return len(regid) == 1 - -def is_written(regid): - return regid[0] in "dexy" - -def is_writeonly(regid): - return regid[0] in "de" - -def is_read(regid): - return regid[0] in "stuvwxy" - -def is_readwrite(regid): - return regid[0] in "xy" - -def is_scalar_reg(regtype): - return regtype in "RPC" - -def is_hvx_reg(regtype): - return regtype in "VQ" - -def is_old_val(regtype, regid, tag): - return regtype+regid+'V' in semdict[tag] - -def is_new_val(regtype, regid, tag): - return regtype+regid+'N' in semdict[tag] def need_slot(tag): - if ('A_CONDEXEC' in attribdict[tag] or - 'A_STORE' in attribdict[tag] or - 'A_LOAD' in attribdict[tag]): + if ( + "A_CVI_SCATTER" not in attribdict[tag] + and "A_CVI_GATHER" not in attribdict[tag] + and ("A_STORE" in attribdict[tag] + or "A_LOAD" in attribdict[tag]) + ): return 1 else: return 0 + def need_part1(tag): return re.compile(r"fPART1").search(semdict[tag]) + def need_ea(tag): return re.compile(r"\bEA\b").search(semdict[tag]) + +def need_PC(tag): + return "A_IMPLICIT_READS_PC" in attribdict[tag] + + +def need_next_PC(tag): + return "A_CALL" in attribdict[tag] + + +def need_pkt_has_multi_cof(tag): + return "A_COF" in attribdict[tag] + + +def need_pkt_need_commit(tag): + return 'A_IMPLICIT_WRITES_USR' in attribdict[tag] + + def skip_qemu_helper(tag): return tag in overrides.keys() -def is_tmp_result(tag): - return ('A_CVI_TMP' in attribdict[tag] or - 'A_CVI_TMP_DST' in attribdict[tag]) -def is_new_result(tag): - return ('A_CVI_NEW' in attribdict[tag]) +def is_idef_parser_enabled(tag): + return tag in idef_parser_enabled + def imm_name(immlett): - return "%siV" % immlett + return f"{immlett}iV" + def read_semantics_file(name): eval_line = "" - for line in open(name, 'rt').readlines(): + for line in open(name, "rt").readlines(): if not line.startswith("#"): eval_line += line if line.endswith("\\\n"): @@ -230,20 +256,878 @@ def read_semantics_file(name): eval(eval_line.strip()) eval_line = "" + def read_attribs_file(name): - attribre = re.compile(r'DEF_ATTRIB\(([A-Za-z0-9_]+), ([^,]*), ' + - r'"([A-Za-z0-9_\.]*)", "([A-Za-z0-9_\.]*)"\)') - for line in open(name, 'rt').readlines(): + attribre = re.compile( + r"DEF_ATTRIB\(([A-Za-z0-9_]+), ([^,]*), " + + r'"([A-Za-z0-9_\.]*)", "([A-Za-z0-9_\.]*)"\)' + ) + for line in open(name, "rt").readlines(): if not attribre.match(line): continue - (attrib_base,descr,rreg,wreg) = attribre.findall(line)[0] - attrib_base = 'A_' + attrib_base - attribinfo[attrib_base] = {'rreg':rreg, 'wreg':wreg, 'descr':descr} + (attrib_base, descr, rreg, wreg) = attribre.findall(line)[0] + attrib_base = "A_" + attrib_base + attribinfo[attrib_base] = {"rreg": rreg, "wreg": wreg, "descr": descr} + def read_overrides_file(name): - overridere = re.compile("#define fGEN_TCG_([A-Za-z0-9_]+)\(.*") - for line in open(name, 'rt').readlines(): + overridere = re.compile(r"#define fGEN_TCG_([A-Za-z0-9_]+)\(.*") + for line in open(name, "rt").readlines(): if not overridere.match(line): continue tag = overridere.findall(line)[0] overrides[tag] = True + + +def read_idef_parser_enabled_file(name): + global idef_parser_enabled + with open(name, "r") as idef_parser_enabled_file: + lines = idef_parser_enabled_file.read().strip().split("\n") + idef_parser_enabled = set(lines) + + +def is_predicated(tag): + return "A_CONDEXEC" in attribdict[tag] + + +def code_fmt(txt): + return textwrap.indent(textwrap.dedent(txt), " ") + + +def hvx_newv(tag): + if "A_CVI_NEW" in attribdict[tag]: + return "EXT_NEW" + elif "A_CVI_TMP" in attribdict[tag] or "A_CVI_TMP_DST" in attribdict[tag]: + return "EXT_TMP" + else: + return "EXT_DFL" + +def vreg_offset_func(tag): + if "A_CVI_TMP" in attribdict[tag] or "A_CVI_TMP_DST" in attribdict[tag]: + return "ctx_tmp_vreg_off" + else: + return "ctx_future_vreg_off" + +class HelperArg: + def __init__(self, proto_arg, call_arg, func_arg): + self.proto_arg = proto_arg + self.call_arg = call_arg + self.func_arg = func_arg + +class Register: + def __init__(self, regtype, regid): + self.regtype = regtype + self.regid = regid + self.reg_num = f"{regtype}{regid}N" + def decl_reg_num(self, f, regno): + f.write(code_fmt(f"""\ + const int {self.reg_num} = insn->regno[{regno}]; + """)) + def idef_arg(self, declared): + declared.append(self.reg_tcg()) + def helper_arg(self): + return HelperArg( + self.helper_proto_type(), + self.reg_tcg(), + f"{self.helper_arg_type()} {self.helper_arg_name()}" + ) + +# +# Every register is either Single or Pair or Hvx +# +class Scalar: + def is_scalar_reg(self): + return True + def is_hvx_reg(self): + return False + def helper_arg_name(self): + return self.reg_tcg() + +class Single(Scalar): + def helper_proto_type(self): + return "s32" + def helper_arg_type(self): + return "int32_t" + +class Pair(Scalar): + def helper_proto_type(self): + return "s64" + def helper_arg_type(self): + return "int64_t" + +class Hvx: + def is_scalar_reg(self): + return False + def is_hvx_reg(self): + return True + def hvx_off(self): + return f"{self.reg_tcg()}_off" + def helper_proto_type(self): + return "ptr" + def helper_arg_type(self): + return "void *" + def helper_arg_name(self): + return f"{self.reg_tcg()}_void" + +# +# Every register is either Dest or OldSource or NewSource or ReadWrite +# +class Dest: + def reg_tcg(self): + return f"{self.regtype}{self.regid}V" + def is_written(self): + return True + def is_writeonly(self): + return True + def is_read(self): + return False + def is_readwrite(self): + return False + +class Source: + def is_written(self): + return False + def is_writeonly(self): + return False + def is_read(self): + return True + def is_readwrite(self): + return False + +class OldSource(Source): + def reg_tcg(self): + return f"{self.regtype}{self.regid}V" + +class NewSource(Source): + def reg_tcg(self): + return f"{self.regtype}{self.regid}N" + +class ReadWrite: + def reg_tcg(self): + return f"{self.regtype}{self.regid}V" + def is_written(self): + return True + def is_writeonly(self): + return False + def is_read(self): + return True + def is_readwrite(self): + return True + +class GprDest(Register, Single, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = get_result_gpr(ctx, {self.reg_num}); + """)) + def log_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_reg_write(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + predicated = "true" if is_predicated(tag) else "false" + f.write(code_fmt(f"""\ + ctx_log_reg_write(ctx, {self.reg_num}, {predicated}); + """)) + +class GprSource(Register, Single, OldSource): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = hex_gpr[{self.reg_num}]; + """)) + def analyze_read(self, f, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_reg_read(ctx, {self.reg_num}); + """)) + +class GprNewSource(Register, Single, NewSource): + def decl_tcg(self, f, tag, regno): + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = get_result_gpr(ctx, insn->regno[{regno}]); + """)) + def analyze_read(self, f, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_reg_read(ctx, {self.reg_num}); + """)) + +class GprReadWrite(Register, Single, ReadWrite): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = get_result_gpr(ctx, {self.reg_num}); + """)) + ## For read/write registers, we need to get the original value into + ## the result TCGv. For predicated instructions, this is done in + ## gen_start_packet. For un-predicated instructions, we do it here. + if not is_predicated(tag): + f.write(code_fmt(f"""\ + tcg_gen_mov_tl({self.reg_tcg()}, hex_gpr[{self.reg_num}]); + """)) + def log_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_reg_write(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + predicated = "true" if is_predicated(tag) else "false" + f.write(code_fmt(f"""\ + ctx_log_reg_write(ctx, {self.reg_num}, {predicated}); + """)) + +class ControlDest(Register, Single, Dest): + def decl_reg_num(self, f, regno): + f.write(code_fmt(f"""\ + const int {self.reg_num} = insn->regno[{regno}] + HEX_REG_SA0; + """)) + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = get_result_gpr(ctx, {self.reg_num}); + """)) + def log_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_write_ctrl_reg(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + predicated = "true" if is_predicated(tag) else "false" + f.write(code_fmt(f"""\ + ctx_log_reg_write(ctx, {self.reg_num}, {predicated}); + """)) + +class ControlSource(Register, Single, OldSource): + def decl_reg_num(self, f, regno): + f.write(code_fmt(f"""\ + const int {self.reg_num} = insn->regno[{regno}] + HEX_REG_SA0; + """)) + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno); + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = tcg_temp_new(); + gen_read_ctrl_reg(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_read(self, f, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_reg_read(ctx, {self.reg_num}); + """)) + +class ModifierSource(Register, Single, OldSource): + def decl_reg_num(self, f, regno): + f.write(code_fmt(f"""\ + const int {self.reg_num} = insn->regno[{regno}] + HEX_REG_M0; + """)) + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = hex_gpr[{self.reg_num}]; + TCGv CS G_GNUC_UNUSED = + hex_gpr[{self.reg_num} - HEX_REG_M0 + HEX_REG_CS0]; + """)) + def idef_arg(self, declared): + declared.append(self.reg_tcg()) + declared.append("CS") + def analyze_read(self, f, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_reg_read(ctx, {self.reg_num}); + """)) + +class PredDest(Register, Single, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = tcg_temp_new(); + """)) + def log_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_pred_write(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_pred_write(ctx, {self.reg_num}); + """)) + +class PredSource(Register, Single, OldSource): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = hex_pred[{self.reg_num}]; + """)) + def analyze_read(self, f, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_pred_read(ctx, {self.reg_num}); + """)) + +class PredNewSource(Register, Single, NewSource): + def decl_tcg(self, f, tag, regno): + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = get_result_pred(ctx, insn->regno[{regno}]); + """)) + def analyze_read(self, f, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_pred_read(ctx, {self.reg_num}); + """)) + +class PredReadWrite(Register, Single, ReadWrite): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv {self.reg_tcg()} = tcg_temp_new(); + tcg_gen_mov_tl({self.reg_tcg()}, hex_pred[{self.reg_num}]); + """)) + def log_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_pred_write(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_pred_write(ctx, {self.reg_num}); + """)) + +class PairDest(Register, Pair, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i64 {self.reg_tcg()} = + get_result_gpr_pair(ctx, {self.reg_num}); + """)) + def log_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_reg_write_pair(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + predicated = "true" if is_predicated(tag) else "false" + f.write(code_fmt(f"""\ + ctx_log_reg_write_pair(ctx, {self.reg_num}, {predicated}); + """)) + +class PairSource(Register, Pair, OldSource): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i64 {self.reg_tcg()} = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64({self.reg_tcg()}, + hex_gpr[{self.reg_num}], + hex_gpr[{self.reg_num} + 1]); + """)) + def analyze_read(self, f, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_reg_read_pair(ctx, {self.reg_num}); + """)) + +class PairReadWrite(Register, Pair, ReadWrite): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i64 {self.reg_tcg()} = + get_result_gpr_pair(ctx, {self.reg_num}); + tcg_gen_concat_i32_i64({self.reg_tcg()}, + hex_gpr[{self.reg_num}], + hex_gpr[{self.reg_num} + 1]); + """)) + def log_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_reg_write_pair(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + predicated = "true" if is_predicated(tag) else "false" + f.write(code_fmt(f"""\ + ctx_log_reg_write_pair(ctx, {self.reg_num}, {predicated}); + """)) + +class ControlPairDest(Register, Pair, Dest): + def decl_reg_num(self, f, regno): + f.write(code_fmt(f"""\ + const int {self.reg_num} = insn->regno[{regno}] + HEX_REG_SA0; + """)) + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i64 {self.reg_tcg()} = + get_result_gpr_pair(ctx, {self.reg_num}); + """)) + def log_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_write_ctrl_reg_pair(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + predicated = "true" if is_predicated(tag) else "false" + f.write(code_fmt(f"""\ + ctx_log_reg_write_pair(ctx, {self.reg_num}, {predicated}); + """)) + +class ControlPairSource(Register, Pair, OldSource): + def decl_reg_num(self, f, regno): + f.write(code_fmt(f"""\ + const int {self.reg_num} = insn->regno[{regno}] + HEX_REG_SA0; + """)) + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i64 {self.reg_tcg()} = tcg_temp_new_i64(); + gen_read_ctrl_reg_pair(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_read(self, f, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_reg_read_pair(ctx, {self.reg_num}); + """)) + +class VRegDest(Register, Hvx, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + const intptr_t {self.hvx_off()} = + {vreg_offset_func(tag)}(ctx, {self.reg_num}, 1, true); + """)) + if not skip_qemu_helper(tag): + f.write(code_fmt(f"""\ + TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); + tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); + """)) + def log_write(self, f, tag): + pass + def helper_hvx_desc(self, f): + f.write(code_fmt(f"""\ + /* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */ + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + newv = hvx_newv(tag) + predicated = "true" if is_predicated(tag) else "false" + f.write(code_fmt(f"""\ + ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated}); + """)) + +class VRegSource(Register, Hvx, OldSource): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + const intptr_t {self.hvx_off()} = vreg_src_off(ctx, {self.reg_num}); + """)) + if not skip_qemu_helper(tag): + f.write(code_fmt(f"""\ + TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); + tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); + """)) + def helper_hvx_desc(self, f): + f.write(code_fmt(f"""\ + /* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */ + """)) + def analyze_read(self, f, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_vreg_read(ctx, {self.reg_num}); + """)) + +class VRegNewSource(Register, Hvx, NewSource): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + if skip_qemu_helper(tag): + f.write(code_fmt(f"""\ + const intptr_t {self.hvx_off()} = + ctx_future_vreg_off(ctx, {self.reg_num}, 1, true); + """)) + def helper_hvx_desc(self, f): + f.write(code_fmt(f"""\ + /* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */ + """)) + def analyze_read(self, f, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_vreg_read(ctx, {self.reg_num}); + """)) + +class VRegReadWrite(Register, Hvx, ReadWrite): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + const intptr_t {self.hvx_off()} = + {vreg_offset_func(tag)}(ctx, {self.reg_num}, 1, true); + tcg_gen_gvec_mov(MO_64, {self.hvx_off()}, + vreg_src_off(ctx, {self.reg_num}), + sizeof(MMVector), sizeof(MMVector)); + """)) + if not skip_qemu_helper(tag): + f.write(code_fmt(f"""\ + TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); + tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); + """)) + def log_write(self, f, tag): + pass + def helper_hvx_desc(self, f): + f.write(code_fmt(f"""\ + /* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */ + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + newv = hvx_newv(tag) + predicated = "true" if is_predicated(tag) else "false" + f.write(code_fmt(f"""\ + ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated}); + """)) + +class VRegTmp(Register, Hvx, ReadWrite): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + const intptr_t {self.hvx_off()} = offsetof(CPUHexagonState, vtmp); + """)) + if not skip_qemu_helper(tag): + f.write(code_fmt(f"""\ + TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); + tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); + tcg_gen_gvec_mov(MO_64, {self.hvx_off()}, + vreg_src_off(ctx, {self.reg_num}), + sizeof(MMVector), sizeof(MMVector)); + """)) + def log_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_vreg_write(ctx, {self.hvx_off()}, {self.reg_num}, + {hvx_newv(tag)}); + """)) + def helper_hvx_desc(self, f): + f.write(code_fmt(f"""\ + /* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */ + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + newv = hvx_newv(tag) + predicated = "true" if is_predicated(tag) else "false" + f.write(code_fmt(f"""\ + ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated}); + """)) + +class VRegPairDest(Register, Hvx, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + const intptr_t {self.hvx_off()} = + {vreg_offset_func(tag)}(ctx, {self.reg_num}, 2, true); + """)) + if not skip_qemu_helper(tag): + f.write(code_fmt(f"""\ + TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); + tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); + """)) + def log_write(self, f, tag): + pass + def helper_hvx_desc(self, f): + f.write(code_fmt(f"""\ + /* {self.reg_tcg()} is *(MMVectorPair *)({self.helper_arg_name()}) */ + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + newv = hvx_newv(tag) + predicated = "true" if is_predicated(tag) else "false" + f.write(code_fmt(f"""\ + ctx_log_vreg_write_pair(ctx, {self.reg_num}, {newv}, {predicated}); + """)) + +class VRegPairSource(Register, Hvx, OldSource): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + const intptr_t {self.hvx_off()} = + offsetof(CPUHexagonState, {self.reg_tcg()}); + tcg_gen_gvec_mov(MO_64, {self.hvx_off()}, + vreg_src_off(ctx, {self.reg_num}), + sizeof(MMVector), sizeof(MMVector)); + tcg_gen_gvec_mov(MO_64, {self.hvx_off()} + sizeof(MMVector), + vreg_src_off(ctx, {self.reg_num} ^ 1), + sizeof(MMVector), sizeof(MMVector)); + """)) + if not skip_qemu_helper(tag): + f.write(code_fmt(f"""\ + TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); + tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); + """)) + def helper_hvx_desc(self, f): + f.write(code_fmt(f"""\ + /* {self.reg_tcg()} is *(MMVectorPair *)({self.helper_arg_name()}) */ + """)) + def analyze_read(self, f, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_vreg_read_pair(ctx, {self.reg_num}); + """)) + +class VRegPairReadWrite(Register, Hvx, ReadWrite): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + const intptr_t {self.hvx_off()} = + offsetof(CPUHexagonState, {self.reg_tcg()}); + tcg_gen_gvec_mov(MO_64, {self.hvx_off()}, + vreg_src_off(ctx, {self.reg_num}), + sizeof(MMVector), sizeof(MMVector)); + tcg_gen_gvec_mov(MO_64, {self.hvx_off()} + sizeof(MMVector), + vreg_src_off(ctx, {self.reg_num} ^ 1), + sizeof(MMVector), sizeof(MMVector)); + """)) + if not skip_qemu_helper(tag): + f.write(code_fmt(f"""\ + TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); + tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); + """)) + def log_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_vreg_write_pair(ctx, {self.hvx_off()}, {self.reg_num}, + {hvx_newv(tag)}); + """)) + def helper_hvx_desc(self, f): + f.write(code_fmt(f"""\ + /* {self.reg_tcg()} is *(MMVectorPair *)({self.helper_arg_name()}) */ + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + newv = hvx_newv(tag) + predicated = "true" if is_predicated(tag) else "false" + f.write(code_fmt(f"""\ + ctx_log_vreg_write_pair(ctx, {self.reg_num}, {newv}, {predicated}); + """)) + +class QRegDest(Register, Hvx, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + const intptr_t {self.hvx_off()} = + get_result_qreg(ctx, {self.reg_num}); + """)) + if not skip_qemu_helper(tag): + f.write(code_fmt(f"""\ + TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); + tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); + """)) + def log_write(self, f, tag): + pass + def helper_hvx_desc(self, f): + f.write(code_fmt(f"""\ + /* {self.reg_tcg()} is *(MMQReg *)({self.helper_arg_name()}) */ + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_qreg_write(ctx, {self.reg_num}); + """)) + +class QRegSource(Register, Hvx, OldSource): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + const intptr_t {self.hvx_off()} = + offsetof(CPUHexagonState, QRegs[{self.reg_num}]); + """)) + if not skip_qemu_helper(tag): + f.write(code_fmt(f"""\ + TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); + tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); + """)) + def helper_hvx_desc(self, f): + f.write(code_fmt(f"""\ + /* {self.reg_tcg()} is *(MMQReg *)({self.helper_arg_name()}) */ + """)) + def analyze_read(self, f, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_qreg_read(ctx, {self.reg_num}); + """)) + +class QRegReadWrite(Register, Hvx, ReadWrite): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + const intptr_t {self.hvx_off()} = + get_result_qreg(ctx, {self.reg_num}); + tcg_gen_gvec_mov(MO_64, {self.hvx_off()}, + offsetof(CPUHexagonState, QRegs[{self.reg_num}]), + sizeof(MMQReg), sizeof(MMQReg)); + """)) + if not skip_qemu_helper(tag): + f.write(code_fmt(f"""\ + TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); + tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); + """)) + def log_write(self, f, tag): + pass + def helper_hvx_desc(self, f): + f.write(code_fmt(f"""\ + /* {self.reg_tcg()} is *(MMQReg *)({self.helper_arg_name()}) */ + """)) + def analyze_write(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + ctx_log_qreg_write(ctx, {self.reg_num}); + """)) + +def init_registers(): + regs = { + GprDest("R", "d"), + GprDest("R", "e"), + GprSource("R", "s"), + GprSource("R", "t"), + GprSource("R", "u"), + GprSource("R", "v"), + GprReadWrite("R", "x"), + GprReadWrite("R", "y"), + ControlDest("C", "d"), + ControlSource("C", "s"), + ModifierSource("M", "u"), + PredDest("P", "d"), + PredDest("P", "e"), + PredSource("P", "s"), + PredSource("P", "t"), + PredSource("P", "u"), + PredSource("P", "v"), + PredReadWrite("P", "x"), + PairDest("R", "dd"), + PairDest("R", "ee"), + PairSource("R", "ss"), + PairSource("R", "tt"), + PairReadWrite("R", "xx"), + PairReadWrite("R", "yy"), + ControlPairDest("C", "dd"), + ControlPairSource("C", "ss"), + VRegDest("V", "d"), + VRegSource("V", "s"), + VRegSource("V", "u"), + VRegSource("V", "v"), + VRegSource("V", "w"), + VRegReadWrite("V", "x"), + VRegTmp("V", "y"), + VRegPairDest("V", "dd"), + VRegPairSource("V", "uu"), + VRegPairSource("V", "vv"), + VRegPairReadWrite("V", "xx"), + QRegDest("Q", "d"), + QRegDest("Q", "e"), + QRegSource("Q", "s"), + QRegSource("Q", "t"), + QRegSource("Q", "u"), + QRegSource("Q", "v"), + QRegReadWrite("Q", "x"), + } + for reg in regs: + registers[f"{reg.regtype}{reg.regid}"] = reg + + new_regs = { + GprNewSource("N", "s"), + GprNewSource("N", "t"), + PredNewSource("P", "t"), + PredNewSource("P", "u"), + PredNewSource("P", "v"), + VRegNewSource("O", "s"), + } + for reg in new_regs: + new_registers[f"{reg.regtype}{reg.regid}"] = reg + +def get_register(tag, regtype, regid): + if f"{regtype}{regid}V" in semdict[tag]: + return registers[f"{regtype}{regid}"] + else: + return new_registers[f"{regtype}{regid}"] + +def helper_ret_type(tag, regs): + ## If there is a scalar result, it is the return type + return_type = HelperArg( "void", "void", "void") + numscalarresults = 0 + for regtype, regid in regs: + reg = get_register(tag, regtype, regid) + if reg.is_written() and reg.is_scalar_reg(): + return_type = HelperArg( + reg.helper_proto_type(), + reg.reg_tcg(), + reg.helper_arg_type() + ) + if numscalarresults > 1: + raise Exception("numscalarresults > 1") + return return_type + +def helper_args(tag, regs, imms): + args = [] + + ## First argument is the CPU state + args.append(HelperArg( + "env", + "tcg_env", + "CPUHexagonState *env" + )) + + ## For predicated instructions, we pass in the destination register + if is_predicated(tag): + for regtype, regid in regs: + reg = get_register(tag, regtype, regid) + if reg.is_writeonly() and not reg.is_hvx_reg(): + args.append(reg.helper_arg()) + + ## Pass the HVX destination registers + for regtype, regid in regs: + reg = get_register(tag, regtype, regid) + if reg.is_written() and reg.is_hvx_reg(): + args.append(reg.helper_arg()) + + ## Pass the source registers + for regtype, regid in regs: + reg = get_register(tag, regtype, regid) + if reg.is_read() and not (reg.is_hvx_reg() and reg.is_readwrite()): + args.append(reg.helper_arg()) + + ## Pass the immediates + for immlett, bits, immshift in imms: + args.append(HelperArg( + "s32", + f"tcg_constant_tl({imm_name(immlett)})", + f"int32_t {imm_name(immlett)}" + )) + + ## Other stuff the helper might need + if need_pkt_has_multi_cof(tag): + args.append(HelperArg( + "i32", + "tcg_constant_tl(ctx->pkt->pkt_has_multi_cof)", + "uint32_t pkt_has_multi_cof" + )) + if need_pkt_need_commit(tag): + args.append(HelperArg( + "i32", + "tcg_constant_tl(ctx->need_commit)", + "uint32_t pkt_need_commit" + )) + if need_PC(tag): + args.append(HelperArg( + "i32", + "tcg_constant_tl(ctx->pkt->pc)", + "target_ulong PC" + )) + if need_next_PC(tag): + args.append(HelperArg( + "i32", + "tcg_constant_tl(ctx->next_PC)", + "target_ulong next_PC" + )) + if need_slot(tag): + args.append(HelperArg( + "i32", + "gen_slotval(ctx)", + "uint32_t slotval" + )) + if need_part1(tag): + args.append(HelperArg( + "i32", + "tcg_constant_tl(insn->part1)" + "uint32_t part1" + )) + return args diff --git a/target/hexagon/hex_regs.h b/target/hexagon/hex_regs.h index a63c2c0fd5..bddfc28021 100644 --- a/target/hexagon/hex_regs.h +++ b/target/hexagon/hex_regs.h @@ -58,7 +58,7 @@ enum { HEX_REG_LC0 = 33, HEX_REG_SA1 = 34, HEX_REG_LC1 = 35, - HEX_REG_P3_0 = 36, + HEX_REG_P3_0_ALIASED = 36, HEX_REG_M0 = 38, HEX_REG_M1 = 39, HEX_REG_USR = 40, diff --git a/target/hexagon/iclass.c b/target/hexagon/iclass.c index 6091286993..c3f8523b27 100644 --- a/target/hexagon/iclass.c +++ b/target/hexagon/iclass.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -51,8 +51,10 @@ SlotMask find_iclass_slots(Opcode opcode, int itype) return SLOTS_0; } else if ((opcode == J2_trap0) || (opcode == Y2_isync) || - (opcode == J2_pause) || (opcode == J4_hintjumpr)) { + (opcode == J2_pause)) { return SLOTS_2; + } else if (opcode == J4_hintjumpr) { + return SLOTS_23; } else if (GET_ATTRIB(opcode, A_CRSLOT23)) { return SLOTS_23; } else if (GET_ATTRIB(opcode, A_RESTRICT_PREFERSLOT0)) { diff --git a/target/hexagon/idef-parser/README.rst b/target/hexagon/idef-parser/README.rst new file mode 100644 index 0000000000..d0aa34309b --- /dev/null +++ b/target/hexagon/idef-parser/README.rst @@ -0,0 +1,714 @@ +Hexagon ISA instruction definitions to tinycode generator compiler +------------------------------------------------------------------ + +idef-parser is a small compiler able to translate the Hexagon ISA description +language into tinycode generator code, that can be easily integrated into QEMU. + +Compilation Example +------------------- + +To better understand the scope of the idef-parser, we'll explore an applicative +example. Let's start by one of the simplest Hexagon instruction: the ``add``. + +The ISA description language represents the ``add`` instruction as +follows: + +.. code:: c + + A2_add(RdV, in RsV, in RtV) { + { RdV=RsV+RtV;} + } + +idef-parser will compile the above code into the following code: + +.. code:: c + + /* A2_add */ + void emit_A2_add(DisasContext *ctx, Insn *insn, Packet *pkt, TCGv_i32 RdV, + TCGv_i32 RsV, TCGv_i32 RtV) + /* { RdV=RsV+RtV;} */ + { + TCGv_i32 tmp_0 = tcg_temp_new_i32(); + tcg_gen_add_i32(tmp_0, RsV, RtV); + tcg_gen_mov_i32(RdV, tmp_0); + } + +The output of the compilation process will be a function, containing the +tinycode generator code, implementing the correct semantics. That function will +not access any global variable, because all the accessed data structures will be +passed explicitly as function parameters. Among the passed parameters we will +have TCGv (tinycode variables) representing the input and output registers of +the architecture, integers representing the immediates that come from the code, +and other data structures which hold information about the disassemblation +context (``DisasContext`` struct). + +Let's begin by describing the input code. The ``add`` instruction is associated +with a unique identifier, in this case ``A2_add``, which allows to distinguish +variants of the same instruction, and expresses the class to which the +instruction belongs, in this case ``A2`` corresponds to the Hexagon +``ALU32/ALU`` instruction subclass. + +After the instruction identifier, we have a series of parameters that represents +TCG variables that will be passed to the generated function. Parameters marked +with ``in`` are already initialized, while the others are output parameters. + +We will leverage this information to infer several information: + +- Fill in the output function signature with the correct TCGv registers +- Fill in the output function signature with the immediate integers +- Keep track of which registers, among the declared one, have been + initialized + +Let's now observe the actual instruction description code, in this case: + +.. code:: c + + { RdV=RsV+RtV;} + +This code is composed by a subset of the C syntax, and is the result of the +application of some macro definitions contained in the ``macros.h`` file. + +This file is used to reduce the complexity of the input language where complex +variants of similar constructs can be mapped to a unique primitive, so that the +idef-parser has to handle a lower number of computation primitives. + +As you may notice, the description code modifies the registers which have been +declared by the declaration statements. In this case all the three registers +will be declared, ``RsV`` and ``RtV`` will also be read and ``RdV`` will be +written. + +Now let's have a quick look at the generated code, line by line. + +:: + + TCGv_i32 tmp_0 = tcg_temp_new_i32(); + +This code starts by declaring a temporary TCGv to hold the result from the sum +operation. + +:: + + tcg_gen_add_i32(tmp_0, RsV, RtV); + +Then, we are generating the sum tinycode operator between the selected +registers, storing the result in the just declared temporary. + +:: + + tcg_gen_mov_i32(RdV, tmp_0); + +The result of the addition is now stored in the temporary, we move it into the +correct destination register. This code may seem inefficient, but QEMU will +perform some optimizations on the tinycode, reducing the unnecessary copy. + +Parser Input +------------ + +Before moving on to the structure of idef-parser itself, let us spend some words +on its' input. There are two preprocessing steps applied to the generated +instruction semantics in ``semantics_generated.pyinc`` that we need to consider. +Firstly, + +:: + + gen_idef_parser_funcs.py + +which takes instruction semantics in ``semantics_generated.pyinc`` to C-like +pseudo code, output into ``idef_parser_input.h.inc``. For instance, the +``J2_jumpr`` instruction which jumps to an address stored in a register +argument. This is instruction is defined as + +:: + + SEMANTICS( \ + "J2_jumpr", \ + "jumpr Rs32", \ + """{fJUMPR(RsN,RsV,COF_TYPE_JUMPR);}""" \ + ) + +in ``semantics_generated.pyinc``. Running ``gen_idef_parser_funcs.py`` +we obtain the pseudo code + +:: + + J2_jumpr(in RsV) { + {fJUMPR(RsN,RsV,COF_TYPE_JUMPR);} + } + +with macros such as ``fJUMPR`` intact. + +The second step is to expand macros into a form suitable for our parser. +These macros are defined in ``idef-parser/macros.inc`` and the step is +carried out by the ``prepare`` script which runs the C preprocessor on +``idef_parser_input.h.inc`` to produce +``idef_parser_input.preprocessed.h.inc``. + +To finish the above example, after preprocessing ``J2_jumpr`` we obtain + +:: + + J2_jumpr(in RsV) { + {(PC = RsV);} + } + +where ``fJUMPR(RsN,RsV,COF_TYPE_JUMPR);`` was expanded to ``(PC = RsV)``, +signifying a write to the Program Counter ``PC``. Note, that ``PC`` in +this expression is not a variable in the strict C sense since it is not +declared anywhere, but rather a symbol which is easy to match in +idef-parser later on. + +Parser Structure +---------------- + +The idef-parser is built using the ``flex`` and ``bison``. + +``flex`` is used to split the input string into tokens, each described using a +regular expression. The token description is contained in the +``idef-parser.lex`` source file. The flex-generated scanner takes care also to +extract from the input text other meaningful information, e.g., the numerical +value in case of an immediate constant, and decorates the token with the +extracted information. + +``bison`` is used to generate the actual parser, starting from the parsing +description contained in the ``idef-parser.y`` file. The generated parser +executes the ``main`` function at the end of the ``idef-parser.y`` file, which +opens input and output files, creates the parsing context, and eventually calls +the ``yyparse()`` function, which starts the execution of the LALR(1) parser +(see `Wikipedia `__ for more +information about LALR parsing techniques). The LALR(1) parser, whenever it has +to shift a token, calls the ``yylex()`` function, which is defined by the +flex-generated code, and reads the input file returning the next scanned token. + +The tokens are mapped on the source language grammar, defined in the +``idef-parser.y`` file to build a unique syntactic tree, according to the +specified operator precedences and associativity rules. + +The grammar describes the whole file which contains the Hexagon instruction +descriptions, therefore it starts from the ``input`` nonterminal, which is a +list of instructions, each instruction is represented by the following grammar +rule, representing the structure of the input file shown above: + +:: + + instruction : INAME arguments code + | error + + arguments : '(' ')' + | '(' argument_list ')'; + + argument_list : argument_decl ',' argument_list + | argument_decl + + argument_decl : REG + | PRED + | IN REG + | IN PRED + | IMM + | var + ; + + code : '{' statements '}' + + statements : statements statement + | statement + + statement : control_statement + | var_decl ';' + | rvalue ';' + | code_block + | ';' + + code_block : '{' statements '}' + | '{' '}' + +With this initial portion of the grammar we are defining the instruction, its' +arguments, and its' statements. Each argument is defined by the +``argument_decl`` rule, and can be either + +:: + + Description Example + ---------------------------------------- + output register RsV + output predicate register P0 + input register in RsV + input predicate register in P0 + immediate value 1234 + local variable EA + +Note, the only local variable allowed to be used as an argument is the effective +address ``EA``. Similarly, each statement can be a ``control_statement``, a +variable declaration such as ``int a;``, a code block, which is just a +bracket-enclosed list of statements, a ``';'``, which is a ``nop`` instruction, +and an ``rvalue ';'``. + +Expressions +~~~~~~~~~~~ + +Allowed in the input code are C language expressions with a few exceptions +to simplify parsing. For instance, variable names such as ``RdV``, ``RssV``, +``PdV``, ``CsV``, and other idiomatic register names from Hexagon, are +reserved specifically for register arguments. These arguments then map to +``TCGv_i32`` or ``TCGv_i64`` depending on the register size. Similarly, ``UiV``, +``riV``, etc. refer to immediate arguments and will map to C integers. + +Also, as mentioned earlier, the names ``PC``, ``SP``, ``FP``, etc. are used to +refer to Hexagon registers such as the program counter, stack pointer, and frame +pointer seen here. Writes to these registers then correspond to assignments +``PC = ...``, and reads correspond to uses of the variable ``PC``. + +Moreover, another example of one such exception is the selective expansion of +macros present in ``macros.h``. As an example, consider the ``fABS`` macro which +in plain C is defined as + +:: + + #define fABS(A) (((A) < 0) ? (-(A)) : (A)) + +and returns the absolute value of the argument ``A``. This macro is not included +in ``idef-parser/macros.inc`` and as such is not expanded and kept as a "call" +``fABS(...)``. Reason being, that ``fABS`` is easier to match and map to +``tcg_gen_abs_``, compared to the full ternary expression above. Loads of +macros in ``macros.h`` are kept unexpanded to aid in parsing, as seen in the +example above, for more information see ``idef-parser/idef-parser.lex``. + +Finally, in mapping these input expressions to tinycode generators, idef-parser +tries to perform as much as possible in plain C. Such as, performing binary +operations in C instead of tinycode generators, thus effectively constant +folding the expression. + +Variables and Variable Declarations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Similarly to C, variables in the input code must be explicitly declared, such as +``int var1;`` which declares an uninitialized variable ``var1``. Initialization +``int var2 = 0;`` is also allowed and behaves as expected. In tinycode +generators the previous declarations are mapped to + +:: + + int var1; -> TCGv_i32 var1 = tcg_temp_new_i32(); + + int var2 = 0; -> TCGv_i32 var1 = tcg_temp_new_i32(); + tcg_gen_movi_i32(j, ((int64_t) 0ULL)); + +which are later automatically freed at the end of the function they're declared +in. Contrary to C, we only allow variables to be declared with an integer type +specified in the following table (without permutation of keywords) + +:: + + type bit-width signedness + ---------------------------------------------------------- + int 32 signed + signed + signed int + + unsigned 32 unsigned + unsigned int + + long 64 signed + long int + signed long + signed long int + + unsigned long 64 unsigned + unsigned long int + + long long 64 signed + long long int + signed long long + signed long long int + + unsigned long long 64 unsigned + unsigned long long int + + size[1,2,4,8][s,u]_t 8-64 signed or unsigned + +In idef-parser, variable names are matched by a generic ``VARID`` token, +which will feature the variable name as a decoration. For a variable declaration +idef-parser calls ``gen_varid_allocate`` with the ``VARID`` token to save the +name, size, and bit width of the newly declared variable. In addition, this +function also ensures that variables aren't declared multiple times, and prints +and error message if that is the case. Upon use of a variable, the ``VARID`` +token is used to lookup the size and bit width of the variable. + +Type System +~~~~~~~~~~~ + +idef-parser features a simple type system which is used to correctly implement +the signedness and bit width of the operations. + +The type of each ``rvalue`` is determined by two attributes: its bit width +(``unsigned bit_width``) and its signedness (``HexSignedness signedness``). + +For each operation, the type of ``rvalue``\ s influence the way in which the +operands are handled and emitted. For example a right shift between signed +operators will be an arithmetic shift, while one between unsigned operators +will be a logical shift. If one of the two operands is signed, and the other +is unsigned, the operation will be signed. + +The bit width also influences the outcome of the operations, in particular while +the input languages features a fine granularity type system, with types of 8, +16, 32, 64 (and more for vectorial instructions) bits, the tinycode only +features 32 and 64 bit widths. We propagate as much as possible the fine +granularity type, until the value has to be used inside an operation between +``rvalue``\ s; in that case if one of the two operands is greater than 32 bits +we promote the whole operation to 64 bit, taking care of properly extending the +two operands. Fortunately, the most critical instructions already feature +explicit casts and zero/sign extensions which are properly propagated down to +our parser. + +The combination of ``rvalue``\ s are handled through the use of the +``gen_bin_op`` and ``gen_bin_cmp`` helper functions. These two functions handle +the appropriate compile-time or run-time emission of operations to perform the +required computation. + +Control Statements +~~~~~~~~~~~~~~~~~~ + +``control_statement``\ s are all the statements which modify the order of +execution of the generated code according to input parameters. They are expanded +by the following grammar rule: + +:: + + control_statement : frame_check + | cancel_statement + | if_statement + | for_statement + | fpart1_statement + +``if_statement``\ s require the emission of labels and branch instructions which +effectively perform conditional jumps (``tcg_gen_brcondi``) according to the +value of an expression. Note, the tinycode generators we produce for conditional +statements do not perfectly mirror what would be expected in C, for instance we +do not reproduce short-circuiting of the ``&&`` operator, and use of the ``||`` +operator is disallowed. All the predicated instructions, and in general all the +instructions where there could be alternative values assigned to an ``lvalue``, +like C-style ternary expressions: + +:: + + rvalue : rvalue QMARK rvalue COLON rvalue + +are handled using the conditional move tinycode instruction +(``tcg_gen_movcond``), which avoids the additional complexity of managing labels +and jumps. + +Instead, regarding the ``for`` loops, exploiting the fact that they always +iterate on immediate values, therefore their iteration ranges are always known +at compile time, we implemented those emitting plain C ``for`` loops. This is +possible because the loops will be executed in the QEMU code, leading to the +consequential unrolling of the for loop, since the tinycode generator +instructions will be executed multiple times, and the respective generated +tinycode will represent the unrolled execution of the loop. + +Parsing Context +~~~~~~~~~~~~~~~ + +All the helper functions in ``idef-parser.y`` carry two fixed parameters, which +are the parsing context ``c`` and the ``YYLLOC`` location information. The +context is explicitly passed to all the functions because the parser we generate +is a reentrant one, meaning that it does not have any global variable, and +therefore the instruction compilation could easily be parallelized in the +future. Finally for each rule we propagate information about the location of the +involved tokens to generate pretty error reporting, able to highlight the +portion of the input code which generated each error. + +Debugging +--------- + +Developing the idef-parser can lead to two types of errors: compile-time errors +and parsing errors. + +Compile-time errors in Bison-generated parsers are usually due to conflicts in +the described grammar. Conflicts forbid the grammar to produce a unique +derivation tree, thus must be solved (except for the dangling else problem, +which is marked as expected through the ``%expect 1`` Bison option). + +For solving conflicts you need a basic understanding of `shift-reduce conflicts +`__ +and `reduce-reduce conflicts +`__, +then, if you are using a Bison version > 3.7.1 you can ask Bison to generate +some counterexamples which highlight ambiguous derivations, passing the +``-Wcex`` option to Bison. In general shift/reduce conflicts are solved by +redesigning the grammar in an unambiguous way or by setting the token priority +correctly, while reduce/reduce conflicts are solved by redesigning the +interested part of the grammar. + +Run-time errors can be divided between lexing and parsing errors, lexing errors +are hard to detect, since the ``var`` token will catch everything which is not +caught by other tokens, but easy to fix, because most of the time a simple +regex editing will be enough. + +idef-parser features a fancy parsing error reporting scheme, which for each +parsing error reports the fragment of the input text which was involved in the +parsing rule that generated an error. + +Implementing an instruction goes through several sequential steps, here are some +suggestions to make each instruction proceed to the next step. + +- not-emitted + + Means that the parsing of the input code relative to that instruction failed, + this could be due to a lexical error or to some mismatch between the order of + valid tokens and a parser rule. You should check that tokens are correctly + identified and mapped, and that there is a rule matching the token sequence + that you need to parse. + +- emitted + + This instruction class contains all the instructions which are emitted but + fail to compile when included in QEMU. The compilation errors are shown by + the QEMU building process and will lead to fixing the bug. Most common + errors regard the mismatch of parameters for tinycode generator functions, + which boil down to errors in the idef-parser type system. + +- compiled + + These instruction generate valid tinycode generator code, which however fail + the QEMU or the harness tests, these cases must be handled manually by + looking into the failing tests and looking at the generated tinycode + generator instruction and at the generated tinycode itself. Tip: handle the + failing harness tests first, because they usually feature only a single + instruction, thus will require less execution trace navigation. If a + multi-threaded test fail, fixing all the other tests will be the easier + option, hoping that the multi-threaded one will be indirectly fixed. + + An example of debugging this type of failure is provided in the following + section. + +- tests-passed + + This is the final goal for each instruction, meaning that the instruction + passes the test suite. + +Another approach to fix QEMU system test, where many instructions might fail, is +to compare the execution trace of your implementation with the reference +implementations already present in QEMU. To do so you should obtain a QEMU build +where the instruction pass the test, and run it with the following command: + +:: + + sudo unshare -p sudo -u bash -c \ + 'env -i -d cpu ' + +And do the same for your implementation, the generated execution traces will be +inherently aligned and can be inspected for behavioral differences using the +``diff`` tool. + +Example of debugging erroneous tinycode generator code +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The goal of this section is to provide a complete example of debugging +incorrectly emitted tinycode generator for a single instruction. + +Let's first introduce a bug in the tinycode generator of the ``A2_add`` +instruction, + +:: + + void emit_A2_add(DisasContext *ctx, Insn *insn, Packet *pkt, TCGv_i32 RdV, + TCGv_i32 RsV, TCGv_i32 RtV) + /* RdV=RsV+RtV;} */ + { + TCGv_i32 tmp_0 = tcg_temp_new_i32(); + tcg_gen_add_i32(tmp_0, RsV, RsV); + tcg_gen_mov_i32(RdV, tmp_0); + } + +Here the bug, albeit hard to spot, is in ``tcg_gen_add_i32(tmp_0, RsV, RsV);`` +where we compute ``RsV + RsV`` instead of ``RsV + RtV``, as would be expected. +This particular bug is a bit tricky to pinpoint when debugging, since the +``A2_add`` instruction is so ubiquitous. As a result, pretty much all tests will +fail and therefore not provide a lot of information about the bug. + +For example, let's run the ``check-tcg`` tests + +:: + + make check-tcg TIMEOUT=1200 \ + DOCKER_IMAGE=debian-hexagon-cross \ + ENGINE=podman V=1 \ + DOCKER_CROSS_CC_GUEST=hexagon-unknown-linux-musl-clang + +In the output, we find a failure in the very first test case ``float_convs`` +due to a segmentation fault. Similarly, all harness and libc tests will fail as +well. At this point we have no clue where the actual bug lies, and need to start +ruling out instructions. As such a good starting point is to utilize the debug +options ``-d in_asm,cpu`` of QEMU to inspect the Hexagon instructions being run, +alongside the CPU state. We additionally need a working version of the emulator +to compare our buggy CPU state against, running + +:: + + meson configure -Dhexagon_idef_parser=false + +will disable the idef-parser for all instructions and fallback on manual +tinycode generator overrides, or on helper function implementations. Recompiling +gives us ``qemu-hexagon`` which passes all tests. If ``qemu-hexagon-buggy`` is +our binary with the incorrect tinycode generators, we can compare the CPU state +between the two versions + +:: + + ./qemu-hexagon-buggy -d in_asm,cpu float_convs &> out_buggy + ./qemu-hexagon -d in_asm,cpu float_convs &> out_working + +Looking at ``diff -u out_buggy out_working`` shows us that the CPU state begins +to diverge on line 141, with an incorrect value in the ``R1`` register + +:: + + @@ -138,7 +138,7 @@ + + General Purpose Registers = { + r0 = 0x4100f9c0 + - r1 = 0x00042108 + + r1 = 0x00000000 + r2 = 0x00021084 + r3 = 0x00000000 + r4 = 0x00000000 + +If we also look into ``out_buggy`` directly we can inspect the input assembly +which the caused the incorrect CPU state, around line 141 we find + +:: + + 116 | ---------------- + 117 | IN: _start_c + 118 | 0x000210b0: 0xa09dc002 { allocframe(R29,#0x10):raw } + ... | ... + 137 | 0x000210fc: 0x5a00c4aa { call PC+2388 } + 138 | + 139 | General Purpose Registers = { + 140 | r0 = 0x4100fa70 + 141 | r1 = 0x00042108 + 142 | r2 = 0x00021084 + 143 | r3 = 0x00000000 + +Importantly, we see some Hexagon assembly followed by a dump of the CPU state, +now the CPU state is actually dumped before the input assembly above is ran. +As such, we are actually interested in the instructions ran before this. + +Scrolling up a bit, we find + +:: + + 54 | ---------------- + 55 | IN: _start + 56 | 0x00021088: 0x6a09c002 { R2 = C9/pc } + 57 | 0x0002108c: 0xbfe2ff82 { R2 = add(R2,#0xfffffffc) } + 58 | 0x00021090: 0x9182c001 { R1 = memw(R2+#0x0) } + 59 | 0x00021094: 0xf302c101 { R1 = add(R2,R1) } + 60 | 0x00021098: 0x7800c01e { R30 = #0x0 } + 61 | 0x0002109c: 0x707dc000 { R0 = R29 } + 62 | 0x000210a0: 0x763dfe1d { R29 = and(R29,#0xfffffff0) } + 63 | 0x000210a4: 0xa79dfdfe { memw(R29+#0xfffffff8) = R29 } + 64 | 0x000210a8: 0xbffdff1d { R29 = add(R29,#0xfffffff8) } + 65 | 0x000210ac: 0x5a00c002 { call PC+4 } + 66 | + 67 | General Purpose Registers = { + 68 | r0 = 0x00000000 + 69 | r1 = 0x00000000 + 70 | r2 = 0x00000000 + 71 | r3 = 0x00000000 + +Remember, the instructions on lines 56-65 are ran on the CPU state shown below +instructions, and as the CPU state has not diverged at this point, we know the +starting state is accurate. The bug must then lie within the instructions shown +here. Next we may notice that ``R1`` is only touched by lines 57 and 58, that is +by + +:: + + 58 | 0x00021090: 0x9182c001 { R1 = memw(R2+#0x0) } + 59 | 0x00021094: 0xf302c101 { R1 = add(R2,R1) } + +Therefore, we are either dealing with an correct load instruction +``R1 = memw(R2+#0x0)`` or with an incorrect add ``R1 = add(R2,R1)``. At this +point it might be easy enough to go directly to the emitted code for the +instructions mentioned and look for bugs, but we could also run +``./qemu-heaxgon -d op,in_asm float_conv`` where we find for the following +tinycode for the Hexagon ``add`` instruction + +:: + + ---- 00021094 + mov_i32 pkt_has_store_s1,$0x0 + add_i32 tmp0,r2,r2 + mov_i32 loc2,tmp0 + mov_i32 new_r1,loc2 + mov_i32 r1,new_r1 + +Here we have finally located our bug ``add_i32 tmp0,r2,r2``. + +Limitations and Future Development +---------------------------------- + +The main limitation of the current parser is given by the syntax-driven nature +of the Bison-generated parsers. This has the severe implication of only being +able to generate code in the order of evaluation of the various rules, without, +in any case, being able to backtrack and alter the generated code. + +An example limitation is highlighted by this statement of the input language: + +:: + + { (PsV==0xff) ? (PdV=0xff) : (PdV=0x00); } + +This ternary assignment, when written in this form requires us to emit some +proper control flow statements, which emit a jump to the first or to the second +code block, whose implementation is extremely convoluted, because when matching +the ternary assignment, the code evaluating the two assignments will be already +generated. + +Instead we pre-process that statement, making it become: + +:: + + { PdV = ((PsV==0xff)) ? 0xff : 0x00; } + +Which can be easily matched by the following parser rules: + +:: + + statement | rvalue ';' + + rvalue : rvalue QMARK rvalue COLON rvalue + | rvalue EQ rvalue + | LPAR rvalue RPAR + | assign_statement + | IMM + + assign_statement : pred ASSIGN rvalue + +Another example that highlight the limitation of the flex/bison parser can be +found even in the add operation we already saw: + +:: + + TCGv_i32 tmp_0 = tcg_temp_new_i32(); + tcg_gen_add_i32(tmp_0, RsV, RtV); + tcg_gen_mov_i32(RdV, tmp_0); + +The fact that we cannot directly use ``RdV`` as the destination of the sum is a +consequence of the syntax-driven nature of the parser. In fact when we parse the +assignment, the ``rvalue`` token, representing the sum has already been reduced, +and thus its code emitted and unchangeable. We rely on the fact that QEMU will +optimize our code reducing the useless move operations and the relative +temporaries. + +A possible improvement of the parser regards the support for vectorial +instructions and floating point instructions, which will require the extension +of the scanner, the parser, and a partial re-design of the type system, allowing +to build the vectorial semantics over the available vectorial tinycode generator +primitives. + +A more radical improvement will use the parser, not to generate directly the +tinycode generator code, but to generate an intermediate representation like the +LLVM IR, which in turn could be compiled using the clang TCG backend. That code +could be furtherly optimized, overcoming the limitations of the syntax-driven +parsing and could lead to a more optimized generated code. diff --git a/target/hexagon/idef-parser/idef-parser.h b/target/hexagon/idef-parser/idef-parser.h new file mode 100644 index 0000000000..3faa1deecd --- /dev/null +++ b/target/hexagon/idef-parser/idef-parser.h @@ -0,0 +1,251 @@ +/* + * Copyright(c) 2019-2022 rev.ng Labs Srl. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef IDEF_PARSER_H +#define IDEF_PARSER_H + +#include +#include +#include +#include + +#define TCGV_NAME_SIZE 7 +#define MAX_WRITTEN_REGS 32 +#define OFFSET_STR_LEN 32 +#define ALLOC_LIST_LEN 32 +#define ALLOC_NAME_SIZE 32 +#define INIT_LIST_LEN 32 +#define OUT_BUF_LEN (1024 * 1024) +#define SIGNATURE_BUF_LEN (128 * 1024) +#define HEADER_BUF_LEN (128 * 1024) + +/* Variadic macros to wrap the buffer printing functions */ +#define EMIT(c, ...) \ + do { \ + g_string_append_printf((c)->out_str, __VA_ARGS__); \ + } while (0) + +#define EMIT_SIG(c, ...) \ + do { \ + g_string_append_printf((c)->signature_str, __VA_ARGS__); \ + } while (0) + +#define EMIT_HEAD(c, ...) \ + do { \ + g_string_append_printf((c)->header_str, __VA_ARGS__); \ + } while (0) + +/** + * Type of register, assigned to the HexReg.type field + */ +typedef enum { GENERAL_PURPOSE, CONTROL, MODIFIER, DOTNEW } HexRegType; + +typedef enum { UNKNOWN_SIGNEDNESS, SIGNED, UNSIGNED } HexSignedness; + +/** + * Semantic record of the REG tokens, identifying registers + */ +typedef struct HexReg { + uint8_t id; /**< Identifier of the register */ + HexRegType type; /**< Type of the register */ + unsigned bit_width; /**< Bit width of the reg, 32 or 64 bits */ +} HexReg; + +/** + * Data structure, identifying a TCGv temporary value + */ +typedef struct HexTmp { + unsigned index; /**< Index of the TCGv temporary value */ +} HexTmp; + +/** + * Enum of the possible immediate, an immediate is a value which is known + * at tinycode generation time, e.g. an integer value, not a TCGv + */ +enum ImmUnionTag { + I, + VARIABLE, + VALUE, + QEMU_TMP, + IMM_PC, + IMM_CONSTEXT, +}; + +/** + * Semantic record of the IMM token, identifying an immediate constant + */ +typedef struct HexImm { + union { + char id; /**< Identifier, used when type is VARIABLE */ + uint64_t value; /**< Immediate value, used when type is VALUE */ + uint64_t index; /**< Index, used when type is QEMU_TMP */ + }; + enum ImmUnionTag type; /**< Type of the immediate */ +} HexImm; + +/** + * Semantic record of the PRED token, identifying a predicate + */ +typedef struct HexPred { + char id; /**< Identifier of the predicate */ +} HexPred; + +/** + * Semantic record of the SAT token, identifying the saturate operator + * Note: All saturates are assumed to implicitly set overflow. + */ +typedef struct HexSat { + HexSignedness signedness; /**< Signedness of the sat. op. */ +} HexSat; + +/** + * Semantic record of the CAST token, identifying the cast operator + */ +typedef struct HexCast { + unsigned bit_width; /**< Bit width of the cast operator */ + HexSignedness signedness; /**< Unsigned flag for the cast operator */ +} HexCast; + +/** + * Semantic record of the EXTRACT token, identifying the cast operator + */ +typedef struct HexExtract { + unsigned bit_width; /**< Bit width of the extract operator */ + unsigned storage_bit_width; /**< Actual bit width of the extract operator */ + HexSignedness signedness; /**< Unsigned flag for the extract operator */ +} HexExtract; + +/** + * Semantic record of the MPY token, identifying the fMPY multiplication + * operator + */ +typedef struct HexMpy { + unsigned first_bit_width; /**< Bit width of 1st operand of fMPY */ + unsigned second_bit_width; /**< Bit width of 2nd operand of fMPY */ + HexSignedness first_signedness; /**< Signedness of 1st operand of fMPY */ + HexSignedness second_signedness; /**< Signedness of 2nd operand of fMPY */ +} HexMpy; + +/** + * Semantic record of the VARID token, identifying declared variables + * of the input language + */ +typedef struct HexVar { + GString *name; /**< Name of the VARID variable */ +} HexVar; + +/** + * Data structure uniquely identifying a declared VARID variable, used for + * keeping track of declared variable, so that any variable is declared only + * once, and its properties are propagated through all the subsequent instances + * of that variable + */ +typedef struct Var { + GString *name; /**< Name of the VARID variable */ + uint8_t bit_width; /**< Bit width of the VARID variable */ + HexSignedness signedness; /**< Unsigned flag for the VARID var */ +} Var; + +/** + * Enum of the possible rvalue types, used in the HexValue.type field + */ +typedef enum RvalueUnionTag { + REGISTER, REGISTER_ARG, TEMP, IMMEDIATE, PREDICATE, VARID +} RvalueUnionTag; + +/** + * Semantic record of the rvalue token, identifying any numeric value, + * immediate or register based. The rvalue tokens are combined together + * through the use of several operators, to encode expressions + */ +typedef struct HexValue { + union { + HexReg reg; /**< rvalue of register type */ + HexTmp tmp; /**< rvalue of temporary type */ + HexImm imm; /**< rvalue of immediate type */ + HexPred pred; /**< rvalue of predicate type */ + HexVar var; /**< rvalue of declared variable type */ + }; + RvalueUnionTag type; /**< Type of the rvalue */ + unsigned bit_width; /**< Bit width of the rvalue */ + HexSignedness signedness; /**< Unsigned flag for the rvalue */ + bool is_dotnew; /**< rvalue of predicate type is dotnew? */ +} HexValue; + +/** + * State of ternary operator + */ +typedef enum TernaryState { IN_LEFT, IN_RIGHT } TernaryState; + +/** + * Data structure used to handle side effects inside ternary operators + */ +typedef struct Ternary { + TernaryState state; + HexValue cond; +} Ternary; + +/** + * Operator type, used for referencing the correct operator when calling the + * gen_bin_op() function, which in turn will generate the correct code to + * execute the operation between the two rvalues + */ +typedef enum OpType { + ADD_OP, SUB_OP, MUL_OP, ASL_OP, ASR_OP, LSR_OP, ANDB_OP, ORB_OP, + XORB_OP, ANDL_OP, MINI_OP, MAXI_OP +} OpType; + +/** + * Data structure including instruction specific information, to be cleared + * out after the compilation of each instruction + */ +typedef struct Inst { + GString *name; /**< Name of the compiled instruction */ + char *code_begin; /**< Beginning of instruction input code */ + char *code_end; /**< End of instruction input code */ + unsigned tmp_count; /**< Index of the last declared TCGv temp */ + unsigned qemu_tmp_count; /**< Index of the last declared int temp */ + unsigned if_count; /**< Index of the last declared if label */ + unsigned error_count; /**< Number of generated errors */ + GArray *allocated; /**< Allocated declaredVARID vars */ + GArray *init_list; /**< List of initialized registers */ + GArray *strings; /**< Strings allocated by the instruction */ +} Inst; + +/** + * Data structure representing the whole translation context, which in a + * reentrant flex/bison parser just like ours is passed between the scanner + * and the parser, holding all the necessary information to perform the + * parsing, this data structure survives between the compilation of different + * instructions + */ +typedef struct Context { + void *scanner; /**< Reentrant parser state pointer */ + char *input_buffer; /**< Buffer containing the input code */ + GString *out_str; /**< String containing the output code */ + GString *signature_str; /**< String containing the signatures code */ + GString *header_str; /**< String containing the header code */ + FILE *defines_file; /**< FILE * of the generated header */ + FILE *output_file; /**< FILE * of the C output file */ + FILE *enabled_file; /**< FILE * of the list of enabled inst */ + GArray *ternary; /**< Array to track nesting of ternary ops */ + unsigned total_insn; /**< Number of instructions in input file */ + unsigned implemented_insn; /**< Instruction compiled without errors */ + Inst inst; /**< Parsing data of the current inst */ +} Context; + +#endif /* IDEF_PARSER_H */ diff --git a/target/hexagon/idef-parser/idef-parser.lex b/target/hexagon/idef-parser/idef-parser.lex new file mode 100644 index 0000000000..cd5958ec90 --- /dev/null +++ b/target/hexagon/idef-parser/idef-parser.lex @@ -0,0 +1,475 @@ +%option noyywrap noinput nounput +%option 8bit reentrant bison-bridge +%option warn nodefault +%option bison-locations + +%{ +/* + * Copyright(c) 2019-2023 rev.ng Labs Srl. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include + +#include "hex_regs.h" + +#include "idef-parser.h" +#include "idef-parser.tab.h" + +/* Keep track of scanner position for error message printout */ +#define YY_USER_ACTION yylloc->first_column = yylloc->last_column; \ + for (int i = 0; yytext[i] != '\0'; i++) { \ + yylloc->last_column++; \ + } + +/* Global Error Counter */ +int error_count; + +%} + +/* Definitions */ +DIGIT [0-9] +LOWER_ID [a-z] +UPPER_ID [A-Z] +ID LOWER_ID|UPPER_ID +INST_NAME [A-Z]+[0-9]_([A-Za-z]|[0-9]|_)+ +HEX_DIGIT [0-9a-fA-F] +REG_ID_32 e|s|d|t|u|v|x|y +REG_ID_64 ee|ss|dd|tt|uu|vv|xx|yy +SYS_ID_32 s|d +SYS_ID_64 ss|dd +PRED_ID d|s|t|u|v|e|x|x +IMM_ID r|s|S|u|U +VAR_ID [a-zA-Z_][a-zA-Z0-9_]* +SIGN_ID s|u +STRING_LIT \"(\\.|[^"\\])*\" + +/* Tokens */ +%% + +[ \t\f\v]+ { /* Ignore whitespaces. */ } +[\n\r]+ { /* Ignore newlines. */ } +^#.*$ { /* Ignore linemarkers. */ } + +{INST_NAME} { yylval->string = g_string_new(yytext); + return INAME; } +"fFLOAT" | +"fUNFLOAT" | +"fDOUBLE" | +"fUNDOUBLE" | +"0.0" | +"0x1.0p52" | +"0x1.0p-52" { return FAIL; } +"in" { return IN; } +"R"{REG_ID_32}"V" { + yylval->rvalue.type = REGISTER_ARG; + yylval->rvalue.reg.type = GENERAL_PURPOSE; + yylval->rvalue.reg.id = yytext[1]; + yylval->rvalue.reg.bit_width = 32; + yylval->rvalue.bit_width = 32; + yylval->rvalue.is_dotnew = false; + yylval->rvalue.signedness = SIGNED; + return REG; } +"R"{REG_ID_64}"V" { + yylval->rvalue.type = REGISTER_ARG; + yylval->rvalue.reg.type = GENERAL_PURPOSE; + yylval->rvalue.reg.id = yytext[1]; + yylval->rvalue.reg.bit_width = 64; + yylval->rvalue.bit_width = 64; + yylval->rvalue.is_dotnew = false; + yylval->rvalue.signedness = SIGNED; + return REG; } +"MuV" { + yylval->rvalue.type = REGISTER_ARG; + yylval->rvalue.reg.type = MODIFIER; + yylval->rvalue.reg.id = 'u'; + yylval->rvalue.reg.bit_width = 32; + yylval->rvalue.bit_width = 32; + yylval->rvalue.signedness = SIGNED; + return REG; } +"C"{REG_ID_32}"V" { + yylval->rvalue.type = REGISTER_ARG; + yylval->rvalue.reg.type = CONTROL; + yylval->rvalue.reg.id = yytext[1]; + yylval->rvalue.reg.bit_width = 32; + yylval->rvalue.bit_width = 32; + yylval->rvalue.is_dotnew = false; + yylval->rvalue.signedness = SIGNED; + return REG; } +"C"{REG_ID_64}"V" { + yylval->rvalue.type = REGISTER_ARG; + yylval->rvalue.reg.type = CONTROL; + yylval->rvalue.reg.id = yytext[1]; + yylval->rvalue.reg.bit_width = 64; + yylval->rvalue.bit_width = 64; + yylval->rvalue.is_dotnew = false; + yylval->rvalue.signedness = SIGNED; + return REG; } +{IMM_ID}"iV" { + yylval->rvalue.type = IMMEDIATE; + yylval->rvalue.signedness = SIGNED; + yylval->rvalue.imm.type = VARIABLE; + yylval->rvalue.imm.id = yytext[0]; + yylval->rvalue.bit_width = 32; + yylval->rvalue.is_dotnew = false; + return IMM; } +"P"{PRED_ID}"V" { + yylval->rvalue.type = PREDICATE; + yylval->rvalue.pred.id = yytext[1]; + yylval->rvalue.bit_width = 32; + yylval->rvalue.is_dotnew = false; + yylval->rvalue.signedness = SIGNED; + return PRED; } +"P"{PRED_ID}"N" { + yylval->rvalue.type = PREDICATE; + yylval->rvalue.pred.id = yytext[1]; + yylval->rvalue.bit_width = 32; + yylval->rvalue.is_dotnew = true; + yylval->rvalue.signedness = SIGNED; + return PRED; } +"+=" { return INC; } +"-=" { return DEC; } +"++" { return PLUSPLUS; } +"&=" { return ANDA; } +"|=" { return ORA; } +"^=" { return XORA; } +"<<" { return ASL; } +">>" { return ASR; } +">>>" { return LSR; } +"==" { return EQ; } +"!=" { return NEQ; } +"<=" { return LTE; } +">=" { return GTE; } +"&&" { return ANDL; } +"else" { return ELSE; } +"for" { return FOR; } +"fREAD_IREG" { return ICIRC; } +"if" { return IF; } +"fFRAME_SCRAMBLE" | +"fFRAME_UNSCRAMBLE" { return FSCR; } +"fFRAMECHECK" { return FCHK; } +"Constant_extended" { return CONSTEXT; } +"fCL1_"{DIGIT} { return LOCNT; } +"fbrev" { return BREV; } +"fSXTN" { return SXT; } +"fZXTN" { return ZXT; } +"fDF_MAX" | +"fSF_MAX" | +"fMAX" { return MAX; } +"fDF_MIN" | +"fSF_MIN" | +"fMIN" { return MIN; } +"fABS" { return ABS; } +"fRNDN" { return ROUND; } +"fCRND" { return CROUND; } +"fCRNDN" { return CROUND; } +"fPM_CIRI" { return CIRCADD; } +"fPM_CIRR" { return CIRCADD; } +"fCOUNTONES_"{DIGIT} { return COUNTONES; } +"fSATN" { yylval->sat.signedness = SIGNED; + return SAT; } +"fSATUN" { yylval->sat.signedness = UNSIGNED; + return SAT; } +"fCONSTLL" { yylval->cast.bit_width = 64; + yylval->cast.signedness = SIGNED; + return CAST; } +"fSE32_64" { yylval->cast.bit_width = 64; + yylval->cast.signedness = SIGNED; + return CAST; } +"fCAST4_4u" { yylval->cast.bit_width = 32; + yylval->cast.signedness = UNSIGNED; + return CAST; } +"fCAST4_8s" { yylval->cast.bit_width = 64; + yylval->cast.signedness = SIGNED; + return CAST; } +"fCAST4_8u" { return CAST4_8U; } +"fCAST4u" { yylval->cast.bit_width = 32; + yylval->cast.signedness = UNSIGNED; + return CAST; } +"fNEWREG" | +"fCAST4_4s" | +"fCAST4s" { yylval->cast.bit_width = 32; + yylval->cast.signedness = SIGNED; + return CAST; } +"fCAST8_8u" { yylval->cast.bit_width = 64; + yylval->cast.signedness = UNSIGNED; + return CAST; } +"fCAST8u" { yylval->cast.bit_width = 64; + yylval->cast.signedness = UNSIGNED; + return CAST; } +"fCAST8_8s" | +"fCAST8s" { yylval->cast.bit_width = 64; + yylval->cast.signedness = SIGNED; + return CAST; } +"fGETBIT" { yylval->extract.bit_width = 1; + yylval->extract.storage_bit_width = 1; + yylval->extract.signedness = UNSIGNED; + return EXTRACT; } +"fGETBYTE" { yylval->extract.bit_width = 8; + yylval->extract.storage_bit_width = 8; + yylval->extract.signedness = SIGNED; + return EXTRACT; } +"fGETUBYTE" { yylval->extract.bit_width = 8; + yylval->extract.storage_bit_width = 8; + yylval->extract.signedness = UNSIGNED; + return EXTRACT; } +"fGETHALF" { yylval->extract.bit_width = 16; + yylval->extract.storage_bit_width = 16; + yylval->extract.signedness = SIGNED; + return EXTRACT; } +"fGETUHALF" { yylval->extract.bit_width = 16; + yylval->extract.storage_bit_width = 16; + yylval->extract.signedness = UNSIGNED; + return EXTRACT; } +"fGETWORD" { yylval->extract.bit_width = 32; + yylval->extract.storage_bit_width = 64; + yylval->extract.signedness = SIGNED; + return EXTRACT; } +"fGETUWORD" { yylval->extract.bit_width = 32; + yylval->extract.storage_bit_width = 64; + yylval->extract.signedness = UNSIGNED; + return EXTRACT; } +"fEXTRACTU_RANGE" { return EXTRANGE; } +"fSETBIT" { yylval->cast.bit_width = 1; + yylval->cast.signedness = SIGNED; + return DEPOSIT; } +"fSETBYTE" { yylval->cast.bit_width = 8; + yylval->cast.signedness = SIGNED; + return DEPOSIT; } +"fSETHALF" { yylval->cast.bit_width = 16; + yylval->cast.signedness = SIGNED; + return SETHALF; } +"fSETWORD" { yylval->cast.bit_width = 32; + yylval->cast.signedness = SIGNED; + return DEPOSIT; } +"fINSERT_BITS" { return INSBITS; } +"fSETBITS" { return SETBITS; } +"fMPY16UU" { yylval->mpy.first_bit_width = 16; + yylval->mpy.second_bit_width = 16; + yylval->mpy.first_signedness = UNSIGNED; + yylval->mpy.second_signedness = UNSIGNED; + return MPY; } +"fMPY16SU" { yylval->mpy.first_bit_width = 16; + yylval->mpy.second_bit_width = 16; + yylval->mpy.first_signedness = SIGNED; + yylval->mpy.second_signedness = UNSIGNED; + return MPY; } +"fMPY16SS" { yylval->mpy.first_bit_width = 16; + yylval->mpy.second_bit_width = 16; + yylval->mpy.first_signedness = SIGNED; + yylval->mpy.second_signedness = SIGNED; + return MPY; } +"fMPY32UU" { yylval->mpy.first_bit_width = 32; + yylval->mpy.second_bit_width = 32; + yylval->mpy.first_signedness = UNSIGNED; + yylval->mpy.second_signedness = UNSIGNED; + return MPY; } +"fMPY32SU" { yylval->mpy.first_bit_width = 32; + yylval->mpy.second_bit_width = 32; + yylval->mpy.first_signedness = SIGNED; + yylval->mpy.second_signedness = UNSIGNED; + return MPY; } +"fSFMPY" | +"fMPY32SS" { yylval->mpy.first_bit_width = 32; + yylval->mpy.second_bit_width = 32; + yylval->mpy.first_signedness = SIGNED; + yylval->mpy.second_signedness = SIGNED; + return MPY; } +"fMPY3216SS" { yylval->mpy.first_bit_width = 32; + yylval->mpy.second_bit_width = 16; + yylval->mpy.first_signedness = SIGNED; + yylval->mpy.second_signedness = SIGNED; + return MPY; } +"fMPY3216SU" { yylval->mpy.first_bit_width = 32; + yylval->mpy.second_bit_width = 16; + yylval->mpy.first_signedness = SIGNED; + yylval->mpy.second_signedness = UNSIGNED; + return MPY; } +"fNEWREG_ST" | +"fIMMEXT" | +"fMUST_IMMEXT" | +"fPASS" | +"fECHO" { return IDENTITY; } +"(size8u_t)" { yylval->cast.bit_width = 64; + yylval->cast.signedness = UNSIGNED; + return CAST; } +"(unsigned int)" { yylval->cast.bit_width = 32; + yylval->cast.signedness = UNSIGNED; + return CAST; } +"fREAD_PC()" { return PC; } +"USR.LPCFG" { return LPCFG; } +"LOAD_CANCEL(EA)" { return LOAD_CANCEL; } +"STORE_CANCEL(EA)" { return STORE_CANCEL; } +"CANCEL" { return CANCEL; } +"N"{LOWER_ID}"N" { yylval->rvalue.type = REGISTER_ARG; + yylval->rvalue.reg.type = DOTNEW; + yylval->rvalue.reg.id = yytext[1]; + yylval->rvalue.reg.bit_width = 32; + yylval->rvalue.bit_width = 32; + yylval->rvalue.signedness = UNSIGNED; + return REG; } +"fREAD_SP()" | +"SP" { yylval->rvalue.type = REGISTER; + yylval->rvalue.reg.type = GENERAL_PURPOSE; + yylval->rvalue.reg.id = HEX_REG_SP; + yylval->rvalue.reg.bit_width = 32; + yylval->rvalue.bit_width = 32; + yylval->rvalue.signedness = UNSIGNED; + return REG; } +"fREAD_FP()" | +"FP" { yylval->rvalue.type = REGISTER; + yylval->rvalue.reg.type = GENERAL_PURPOSE; + yylval->rvalue.reg.id = HEX_REG_FP; + yylval->rvalue.reg.bit_width = 32; + yylval->rvalue.bit_width = 32; + yylval->rvalue.signedness = UNSIGNED; + return REG; } +"fREAD_LR()" | +"LR" { yylval->rvalue.type = REGISTER; + yylval->rvalue.reg.type = GENERAL_PURPOSE; + yylval->rvalue.reg.id = HEX_REG_LR; + yylval->rvalue.reg.bit_width = 32; + yylval->rvalue.bit_width = 32; + yylval->rvalue.signedness = UNSIGNED; + return REG; } +"fREAD_GP()" | +"GP" { yylval->rvalue.type = REGISTER; + yylval->rvalue.reg.type = CONTROL; + yylval->rvalue.reg.id = HEX_REG_GP; + yylval->rvalue.reg.bit_width = 32; + yylval->rvalue.bit_width = 32; + yylval->rvalue.signedness = UNSIGNED; + return REG; } +"LC"[01] { yylval->rvalue.type = REGISTER; + yylval->rvalue.reg.type = CONTROL; + yylval->rvalue.reg.id = HEX_REG_LC0 + + (yytext[2] - '0') * 2; + yylval->rvalue.reg.bit_width = 32; + yylval->rvalue.bit_width = 32; + yylval->rvalue.signedness = UNSIGNED; + return REG; } +"SA"[01] { yylval->rvalue.type = REGISTER; + yylval->rvalue.reg.type = CONTROL; + yylval->rvalue.reg.id = HEX_REG_SA0 + + (yytext[2] - '0') * 2; + yylval->rvalue.reg.bit_width = 32; + yylval->rvalue.bit_width = 32; + yylval->rvalue.signedness = UNSIGNED; + return REG; } +"fREAD_P0()" { yylval->rvalue.type = PREDICATE; + yylval->rvalue.pred.id = '0'; + yylval->rvalue.bit_width = 32; + return PRED; } +[pP]{DIGIT} { yylval->rvalue.type = PREDICATE; + yylval->rvalue.pred.id = yytext[1]; + yylval->rvalue.bit_width = 32; + yylval->rvalue.is_dotnew = false; + return PRED; } +[pP]{DIGIT}[nN] { yylval->rvalue.type = PREDICATE; + yylval->rvalue.pred.id = yytext[1]; + yylval->rvalue.bit_width = 32; + yylval->rvalue.is_dotnew = true; + return PRED; } +"fLSBNEW" { return LSBNEW; } +"N" { yylval->rvalue.type = IMMEDIATE; + yylval->rvalue.bit_width = 32; + yylval->rvalue.imm.type = VARIABLE; + yylval->rvalue.imm.id = 'N'; + return IMM; } +"i" { yylval->rvalue.type = IMMEDIATE; + yylval->rvalue.bit_width = 32; + yylval->rvalue.signedness = SIGNED; + yylval->rvalue.imm.type = I; + return IMM; } +{SIGN_ID} { if (yytext[0] == 'u') { + yylval->signedness = UNSIGNED; + } else { + yylval->signedness = SIGNED; + } + return SIGN; + } +"0x"{HEX_DIGIT}+ { uint64_t value = strtoull(yytext, NULL, 0); + yylval->rvalue.type = IMMEDIATE; + yylval->rvalue.imm.type = VALUE; + yylval->rvalue.imm.value = value; + if (value <= INT_MAX) { + yylval->rvalue.bit_width = sizeof(int) * 8; + yylval->rvalue.signedness = SIGNED; + } else if (value <= UINT_MAX) { + yylval->rvalue.bit_width = sizeof(unsigned int) * 8; + yylval->rvalue.signedness = UNSIGNED; + } else if (value <= LONG_MAX) { + yylval->rvalue.bit_width = sizeof(long) * 8; + yylval->rvalue.signedness = SIGNED; + } else if (value <= ULONG_MAX) { + yylval->rvalue.bit_width = sizeof(unsigned long) * 8; + yylval->rvalue.signedness = UNSIGNED; + } else { + g_assert_not_reached(); + } + return IMM; } +{DIGIT}+ { int64_t value = strtoll(yytext, NULL, 0); + yylval->rvalue.type = IMMEDIATE; + yylval->rvalue.imm.type = VALUE; + yylval->rvalue.imm.value = value; + if (value >= INT_MIN && value <= INT_MAX) { + yylval->rvalue.bit_width = sizeof(int) * 8; + yylval->rvalue.signedness = SIGNED; + } else if (value >= LONG_MIN && value <= LONG_MAX) { + yylval->rvalue.bit_width = sizeof(long) * 8; + yylval->rvalue.signedness = SIGNED; + } else { + g_assert_not_reached(); + } + return IMM; } +"0x"{HEX_DIGIT}+"ULL" | +{DIGIT}+"ULL" { yylval->rvalue.type = IMMEDIATE; + yylval->rvalue.bit_width = 64; + yylval->rvalue.signedness = UNSIGNED; + yylval->rvalue.imm.type = VALUE; + yylval->rvalue.imm.value = strtoull(yytext, NULL, 0); + return IMM; } +"fLOAD" { return LOAD; } +"fSTORE" { return STORE; } +"fROTL" { return ROTL; } +"fCARRY_FROM_ADD" { return CARRY_FROM_ADD; } +"fADDSAT64" { return ADDSAT64; } +"size"[1248][us]"_t" { /* Handles "size_t" variants of int types */ + const unsigned int bits_per_byte = 8; + const unsigned int bytes = yytext[4] - '0'; + yylval->rvalue.bit_width = bits_per_byte * bytes; + if (yytext[5] == 'u') { + yylval->rvalue.signedness = UNSIGNED; + } else { + yylval->rvalue.signedness = SIGNED; + } + return TYPE_SIZE_T; } +"unsigned" { return TYPE_UNSIGNED; } +"long" { return TYPE_LONG; } +"int" { return TYPE_INT; } +"const" { /* Emit no token */ } +{VAR_ID} { /* Variable name, we adopt the C names convention */ + yylval->rvalue.type = VARID; + yylval->rvalue.var.name = g_string_new(yytext); + /* Default to an unknown signedness and 0 width. */ + yylval->rvalue.bit_width = 0; + yylval->rvalue.signedness = UNKNOWN_SIGNEDNESS; + return VAR; } +"fatal("{STRING_LIT}")" { /* Emit no token */ } +"fHINTJR(RsV)" { /* Emit no token */ } +. { return yytext[0]; } + +%% diff --git a/target/hexagon/idef-parser/idef-parser.y b/target/hexagon/idef-parser/idef-parser.y new file mode 100644 index 0000000000..cd2612eb8c --- /dev/null +++ b/target/hexagon/idef-parser/idef-parser.y @@ -0,0 +1,915 @@ +%{ +/* + * Copyright(c) 2019-2023 rev.ng Labs Srl. All Rights Reserved. + * + * 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 "idef-parser.h" +#include "parser-helpers.h" +#include "idef-parser.tab.h" +#include "idef-parser.yy.h" + +/* Uncomment this to disable yyasserts */ +/* #define NDEBUG */ + +#define ERR_LINE_CONTEXT 40 + +%} + +%lex-param {void *scanner} +%parse-param {void *scanner} +%parse-param {Context *c} + +%define parse.error verbose +%define parse.lac full +%define api.pure full + +%locations + +%union { + GString *string; + HexValue rvalue; + HexSat sat; + HexCast cast; + HexExtract extract; + HexMpy mpy; + HexSignedness signedness; + int index; +} + +/* Tokens */ +%start input + +%expect 1 + +%token IN INAME VAR +%token ABS CROUND ROUND CIRCADD COUNTONES INC DEC ANDA ORA XORA PLUSPLUS ASL +%token ASR LSR EQ NEQ LTE GTE MIN MAX ANDL FOR ICIRC IF MUN FSCR FCHK SXT +%token ZXT CONSTEXT LOCNT BREV SIGN LOAD STORE PC LPCFG +%token LOAD_CANCEL STORE_CANCEL CANCEL IDENTITY ROTL INSBITS SETBITS EXTRANGE +%token CAST4_8U FAIL CARRY_FROM_ADD ADDSAT64 LSBNEW +%token TYPE_SIZE_T TYPE_INT TYPE_SIGNED TYPE_UNSIGNED TYPE_LONG + +%token REG IMM PRED +%token ELSE +%token MPY +%token SAT +%token CAST DEPOSIT SETHALF +%token EXTRACT +%type INAME +%type rvalue lvalue VAR assign_statement var var_decl var_type +%type FAIL +%type TYPE_SIGNED TYPE_UNSIGNED TYPE_INT TYPE_LONG TYPE_SIZE_T +%type if_stmt IF +%type SIGN + +/* Operator Precedences */ +%left MIN MAX +%left '(' +%left ',' +%left '=' +%right CIRCADD +%right INC DEC ANDA ORA XORA +%left '?' ':' +%left ANDL +%left '|' +%left '^' ANDOR +%left '&' +%left EQ NEQ +%left '<' '>' LTE GTE +%left ASL ASR LSR +%right ABS +%left '-' '+' +%left '*' '/' '%' MPY +%right '~' '!' +%left '[' +%right CAST +%right LOCNT BREV + +/* Bison Grammar */ +%% + +/* Input file containing the description of each hexagon instruction */ +input : instructions + { + /* Suppress warning about unused yynerrs */ + (void) yynerrs; + YYACCEPT; + } + ; + +instructions : instruction instructions + | %empty + ; + +instruction : INAME + { + gen_inst(c, $1); + } + arguments + { + EMIT_SIG(c, ")"); + EMIT_HEAD(c, "{\n"); + } + code + { + gen_inst_code(c, &@1); + } + | error /* Recover gracefully after instruction compilation error */ + { + free_instruction(c); + } + ; + +arguments : '(' ')' + | '(' argument_list ')'; + +argument_list : argument_decl ',' argument_list + | argument_decl + ; + +var : VAR + { + track_string(c, $1.var.name); + $$ = $1; + } + ; + +/* + * Here the integer types are defined from valid combinations of + * `signed`, `unsigned`, `int`, and `long` tokens. The `signed` + * and `unsigned` tokens are here assumed to always be placed + * first in the type declaration, which is not the case in + * normal C. Similarly, `int` is assumed to always be placed + * last in the type. + */ +type_int : TYPE_INT + | TYPE_SIGNED + | TYPE_SIGNED TYPE_INT; +type_uint : TYPE_UNSIGNED + | TYPE_UNSIGNED TYPE_INT; +type_ulonglong : TYPE_UNSIGNED TYPE_LONG TYPE_LONG + | TYPE_UNSIGNED TYPE_LONG TYPE_LONG TYPE_INT; + +/* + * Here the various valid int types defined above specify + * their `signedness` and `bit_width`. The LP64 convention + * is assumed where longs are 64-bit, long longs are then + * assumed to also be 64-bit. + */ +var_type : TYPE_SIZE_T + { + yyassert(c, &@1, $1.bit_width <= 64, + "Variables with size > 64-bit are not supported!"); + $$ = $1; + } + | type_int + { + $$.signedness = SIGNED; + $$.bit_width = 32; + } + | type_uint + { + $$.signedness = UNSIGNED; + $$.bit_width = 32; + } + | type_ulonglong + { + $$.signedness = UNSIGNED; + $$.bit_width = 64; + } + ; + +/* Rule to capture declarations of VARs */ +var_decl : var_type IMM + { + /* + * Rule to capture "int i;" declarations since "i" is special + * and assumed to be always be IMM. Moreover, "i" is only + * assumed to be used in for-loops. + * + * Therefore we want to NOP these declarations. + */ + yyassert(c, &@2, $2.imm.type == I, + "Variable declaration with immedaties only allowed" + " for the loop induction variable \"i\""); + $$ = $2; + } + | var_type var + { + /* + * Allocate new variable, this checks that it hasn't already + * been declared. + */ + gen_varid_allocate(c, &@1, &$2, $1.bit_width, $1.signedness); + /* Copy var for variable name */ + $$ = $2; + /* Copy type info from var_type */ + $$.signedness = $1.signedness; + $$.bit_width = $1.bit_width; + } + ; + +/* Return the modified registers list */ +code : '{' statements '}' + { + c->inst.code_begin = c->input_buffer + @2.first_column - 1; + c->inst.code_end = c->input_buffer + @2.last_column - 1; + } + | '{' + { + /* Nop */ + } + '}' + ; + +argument_decl : REG + { + emit_arg(c, &@1, &$1); + /* Enqueue register into initialization list */ + g_array_append_val(c->inst.init_list, $1); + } + | PRED + { + emit_arg(c, &@1, &$1); + /* Enqueue predicate into initialization list */ + g_array_append_val(c->inst.init_list, $1); + } + | IN REG + { + emit_arg(c, &@2, &$2); + } + | IN PRED + { + emit_arg(c, &@2, &$2); + } + | IMM + { + EMIT_SIG(c, ", int %ciV", $1.imm.id); + } + ; + +code_block : '{' statements '}' + | '{' '}' + ; + +/* A list of one or more statements */ +statements : statements statement + | statement + ; + +/* Statements can be assignment (rvalue ';'), control or memory statements */ +statement : control_statement + | var_decl ';' + | rvalue ';' + | code_block + | ';' + ; + +assign_statement : lvalue '=' rvalue + { + @1.last_column = @3.last_column; + gen_assign(c, &@1, &$1, &$3); + $$ = $1; + } + | var_decl '=' rvalue + { + @1.last_column = @3.last_column; + gen_assign(c, &@1, &$1, &$3); + $$ = $1; + } + | lvalue INC rvalue + { + @1.last_column = @3.last_column; + HexValue tmp = gen_bin_op(c, &@1, ADD_OP, &$1, &$3); + gen_assign(c, &@1, &$1, &tmp); + $$ = $1; + } + | lvalue DEC rvalue + { + @1.last_column = @3.last_column; + HexValue tmp = gen_bin_op(c, &@1, SUB_OP, &$1, &$3); + gen_assign(c, &@1, &$1, &tmp); + $$ = $1; + } + | lvalue ANDA rvalue + { + @1.last_column = @3.last_column; + HexValue tmp = gen_bin_op(c, &@1, ANDB_OP, &$1, &$3); + gen_assign(c, &@1, &$1, &tmp); + $$ = $1; + } + | lvalue ORA rvalue + { + @1.last_column = @3.last_column; + HexValue tmp = gen_bin_op(c, &@1, ORB_OP, &$1, &$3); + gen_assign(c, &@1, &$1, &tmp); + $$ = $1; + } + | lvalue XORA rvalue + { + @1.last_column = @3.last_column; + HexValue tmp = gen_bin_op(c, &@1, XORB_OP, &$1, &$3); + gen_assign(c, &@1, &$1, &tmp); + $$ = $1; + } + | PRED '=' rvalue + { + @1.last_column = @3.last_column; + gen_pred_assign(c, &@1, &$1, &$3); + } + | IMM '=' rvalue + { + @1.last_column = @3.last_column; + yyassert(c, &@1, $3.type == IMMEDIATE, + "Cannot assign non-immediate to immediate!"); + yyassert(c, &@1, $1.imm.type == VARIABLE, + "Cannot assign to non-variable!"); + /* Assign to the function argument */ + OUT(c, &@1, &$1, " = ", &$3, ";\n"); + $$ = $1; + } + | LOAD '(' IMM ',' IMM ',' SIGN ',' var ',' lvalue ')' + { + @1.last_column = @12.last_column; + yyassert(c, &@1, !is_inside_ternary(c), + "Assignment side-effect not modeled!"); + yyassert(c, &@1, $3.imm.value == 1, + "LOAD of arrays not supported!"); + gen_load(c, &@1, &$5, $7, &$9, &$11); + } + | STORE '(' IMM ',' IMM ',' var ',' rvalue ')' + /* Store primitive */ + { + @1.last_column = @10.last_column; + yyassert(c, &@1, !is_inside_ternary(c), + "Assignment side-effect not modeled!"); + yyassert(c, &@1, $3.imm.value == 1, + "STORE of arrays not supported!"); + gen_store(c, &@1, &$5, &$7, &$9); + } + | LPCFG '=' rvalue + { + @1.last_column = @3.last_column; + yyassert(c, &@1, !is_inside_ternary(c), + "Assignment side-effect not modeled!"); + $3 = gen_rvalue_truncate(c, &@1, &$3); + $3 = rvalue_materialize(c, &@1, &$3); + OUT(c, &@1, "gen_set_usr_field(ctx, USR_LPCFG, ", &$3, ");\n"); + } + | DEPOSIT '(' rvalue ',' rvalue ',' rvalue ')' + { + @1.last_column = @8.last_column; + yyassert(c, &@1, !is_inside_ternary(c), + "Assignment side-effect not modeled!"); + gen_deposit_op(c, &@1, &$5, &$7, &$3, &$1); + } + | SETHALF '(' rvalue ',' lvalue ',' rvalue ')' + { + @1.last_column = @8.last_column; + yyassert(c, &@1, !is_inside_ternary(c), + "Assignment side-effect not modeled!"); + gen_sethalf(c, &@1, &$1, &$3, &$5, &$7); + } + | SETBITS '(' rvalue ',' rvalue ',' rvalue ',' rvalue ')' + { + @1.last_column = @10.last_column; + yyassert(c, &@1, !is_inside_ternary(c), + "Assignment side-effect not modeled!"); + gen_setbits(c, &@1, &$3, &$5, &$7, &$9); + } + | INSBITS '(' lvalue ',' rvalue ',' rvalue ',' rvalue ')' + { + @1.last_column = @10.last_column; + yyassert(c, &@1, !is_inside_ternary(c), + "Assignment side-effect not modeled!"); + gen_rdeposit_op(c, &@1, &$3, &$9, &$7, &$5); + } + | IDENTITY '(' rvalue ')' + { + @1.last_column = @4.last_column; + $$ = $3; + } + ; + +control_statement : frame_check + | cancel_statement + | if_statement + | for_statement + ; + +frame_check : FCHK '(' rvalue ',' rvalue ')' ';' + ; + +cancel_statement : LOAD_CANCEL + { + gen_load_cancel(c, &@1); + } + | STORE_CANCEL + { + gen_cancel(c, &@1); + } + | CANCEL + ; + +if_statement : if_stmt + { + /* Fix else label */ + OUT(c, &@1, "gen_set_label(if_label_", &$1, ");\n"); + } + | if_stmt ELSE + { + @1.last_column = @2.last_column; + $2 = gen_if_else(c, &@1, $1); + } + statement + { + OUT(c, &@1, "gen_set_label(if_label_", &$2, ");\n"); + } + ; + +for_statement : FOR '(' IMM '=' IMM ';' IMM '<' IMM ';' IMM PLUSPLUS ')' + { + yyassert(c, &@3, + $3.imm.type == I && + $7.imm.type == I && + $11.imm.type == I, + "Loop induction variable must be \"i\""); + @1.last_column = @13.last_column; + OUT(c, &@1, "for (int ", &$3, " = ", &$5, "; ", + &$7, " < ", &$9); + OUT(c, &@1, "; ", &$11, "++) {\n"); + } + code_block + { + OUT(c, &@1, "}\n"); + } + ; + +if_stmt : IF '(' rvalue ')' + { + @1.last_column = @3.last_column; + $1 = gen_if_cond(c, &@1, &$3); + } + statement + { + $$ = $1; + } + ; + +rvalue : FAIL + { + yyassert(c, &@1, false, "Encountered a FAIL token as rvalue.\n"); + } + | assign_statement + | REG + { + $$ = $1; + } + | IMM + { + $$ = $1; + } + | PRED + { + $$ = gen_rvalue_pred(c, &@1, &$1); + } + | PC + { + /* Read PC from the CR */ + HexValue rvalue; + memset(&rvalue, 0, sizeof(HexValue)); + rvalue.type = IMMEDIATE; + rvalue.imm.type = IMM_PC; + rvalue.bit_width = 32; + rvalue.signedness = UNSIGNED; + $$ = rvalue; + } + | CONSTEXT + { + HexValue rvalue; + memset(&rvalue, 0, sizeof(HexValue)); + rvalue.type = IMMEDIATE; + rvalue.imm.type = IMM_CONSTEXT; + rvalue.signedness = UNSIGNED; + rvalue.is_dotnew = false; + $$ = rvalue; + } + | var + { + $$ = gen_rvalue_var(c, &@1, &$1); + } + | MPY '(' rvalue ',' rvalue ')' + { + @1.last_column = @6.last_column; + $$ = gen_rvalue_mpy(c, &@1, &$1, &$3, &$5); + } + | rvalue '+' rvalue + { + @1.last_column = @3.last_column; + $$ = gen_bin_op(c, &@1, ADD_OP, &$1, &$3); + } + | rvalue '-' rvalue + { + @1.last_column = @3.last_column; + $$ = gen_bin_op(c, &@1, SUB_OP, &$1, &$3); + } + | rvalue '*' rvalue + { + @1.last_column = @3.last_column; + $$ = gen_bin_op(c, &@1, MUL_OP, &$1, &$3); + } + | rvalue ASL rvalue + { + @1.last_column = @3.last_column; + $$ = gen_bin_op(c, &@1, ASL_OP, &$1, &$3); + } + | rvalue ASR rvalue + { + @1.last_column = @3.last_column; + assert_signedness(c, &@1, $1.signedness); + if ($1.signedness == UNSIGNED) { + $$ = gen_bin_op(c, &@1, LSR_OP, &$1, &$3); + } else if ($1.signedness == SIGNED) { + $$ = gen_bin_op(c, &@1, ASR_OP, &$1, &$3); + } + } + | rvalue LSR rvalue + { + @1.last_column = @3.last_column; + $$ = gen_bin_op(c, &@1, LSR_OP, &$1, &$3); + } + | rvalue '&' rvalue + { + @1.last_column = @3.last_column; + $$ = gen_bin_op(c, &@1, ANDB_OP, &$1, &$3); + } + | rvalue '|' rvalue + { + @1.last_column = @3.last_column; + $$ = gen_bin_op(c, &@1, ORB_OP, &$1, &$3); + } + | rvalue '^' rvalue + { + @1.last_column = @3.last_column; + $$ = gen_bin_op(c, &@1, XORB_OP, &$1, &$3); + } + | rvalue ANDL rvalue + { + @1.last_column = @3.last_column; + $$ = gen_bin_op(c, &@1, ANDL_OP, &$1, &$3); + } + | MIN '(' rvalue ',' rvalue ')' + { + @1.last_column = @3.last_column; + $$ = gen_bin_op(c, &@1, MINI_OP, &$3, &$5); + } + | MAX '(' rvalue ',' rvalue ')' + { + @1.last_column = @3.last_column; + $$ = gen_bin_op(c, &@1, MAXI_OP, &$3, &$5); + } + | '~' rvalue + { + @1.last_column = @2.last_column; + $$ = gen_rvalue_not(c, &@1, &$2); + } + | '!' rvalue + { + @1.last_column = @2.last_column; + $$ = gen_rvalue_notl(c, &@1, &$2); + } + | SAT '(' IMM ',' rvalue ')' + { + @1.last_column = @6.last_column; + $$ = gen_rvalue_sat(c, &@1, &$1, &$3, &$5); + } + | CAST rvalue + { + @1.last_column = @2.last_column; + $$ = gen_cast_op(c, &@1, &$2, $1.bit_width, $1.signedness); + } + | rvalue EQ rvalue + { + @1.last_column = @3.last_column; + $$ = gen_bin_cmp(c, &@1, TCG_COND_EQ, &$1, &$3); + } + | rvalue NEQ rvalue + { + @1.last_column = @3.last_column; + $$ = gen_bin_cmp(c, &@1, TCG_COND_NE, &$1, &$3); + } + | rvalue '<' rvalue + { + @1.last_column = @3.last_column; + + assert_signedness(c, &@1, $1.signedness); + assert_signedness(c, &@1, $3.signedness); + if ($1.signedness == UNSIGNED || $3.signedness == UNSIGNED) { + $$ = gen_bin_cmp(c, &@1, TCG_COND_LTU, &$1, &$3); + } else { + $$ = gen_bin_cmp(c, &@1, TCG_COND_LT, &$1, &$3); + } + } + | rvalue '>' rvalue + { + @1.last_column = @3.last_column; + + assert_signedness(c, &@1, $1.signedness); + assert_signedness(c, &@1, $3.signedness); + if ($1.signedness == UNSIGNED || $3.signedness == UNSIGNED) { + $$ = gen_bin_cmp(c, &@1, TCG_COND_GTU, &$1, &$3); + } else { + $$ = gen_bin_cmp(c, &@1, TCG_COND_GT, &$1, &$3); + } + } + | rvalue LTE rvalue + { + @1.last_column = @3.last_column; + + assert_signedness(c, &@1, $1.signedness); + assert_signedness(c, &@1, $3.signedness); + if ($1.signedness == UNSIGNED || $3.signedness == UNSIGNED) { + $$ = gen_bin_cmp(c, &@1, TCG_COND_LEU, &$1, &$3); + } else { + $$ = gen_bin_cmp(c, &@1, TCG_COND_LE, &$1, &$3); + } + } + | rvalue GTE rvalue + { + @1.last_column = @3.last_column; + + assert_signedness(c, &@1, $1.signedness); + assert_signedness(c, &@1, $3.signedness); + if ($1.signedness == UNSIGNED || $3.signedness == UNSIGNED) { + $$ = gen_bin_cmp(c, &@1, TCG_COND_GEU, &$1, &$3); + } else { + $$ = gen_bin_cmp(c, &@1, TCG_COND_GE, &$1, &$3); + } + } + | rvalue '?' + { + Ternary t = { 0 }; + t.state = IN_LEFT; + t.cond = $1; + g_array_append_val(c->ternary, t); + } + rvalue ':' + { + Ternary *t = &g_array_index(c->ternary, Ternary, + c->ternary->len - 1); + t->state = IN_RIGHT; + } + rvalue + { + @1.last_column = @5.last_column; + $$ = gen_rvalue_ternary(c, &@1, &$1, &$4, &$7); + } + | FSCR '(' rvalue ')' + { + @1.last_column = @4.last_column; + $$ = gen_rvalue_fscr(c, &@1, &$3); + } + | SXT '(' rvalue ',' IMM ',' rvalue ')' + { + @1.last_column = @8.last_column; + yyassert(c, &@1, $5.type == IMMEDIATE && + $5.imm.type == VALUE, + "SXT expects immediate values\n"); + $$ = gen_extend_op(c, &@1, &$3, 64, &$7, SIGNED); + } + | ZXT '(' rvalue ',' IMM ',' rvalue ')' + { + @1.last_column = @8.last_column; + yyassert(c, &@1, $5.type == IMMEDIATE && + $5.imm.type == VALUE, + "ZXT expects immediate values\n"); + $$ = gen_extend_op(c, &@1, &$3, 64, &$7, UNSIGNED); + } + | '(' rvalue ')' + { + $$ = $2; + } + | ABS rvalue + { + @1.last_column = @2.last_column; + $$ = gen_rvalue_abs(c, &@1, &$2); + } + | CROUND '(' rvalue ',' rvalue ')' + { + @1.last_column = @6.last_column; + $$ = gen_convround_n(c, &@1, &$3, &$5); + } + | CROUND '(' rvalue ')' + { + @1.last_column = @4.last_column; + $$ = gen_convround(c, &@1, &$3); + } + | ROUND '(' rvalue ',' rvalue ')' + { + @1.last_column = @6.last_column; + $$ = gen_round(c, &@1, &$3, &$5); + } + | '-' rvalue + { + @1.last_column = @2.last_column; + $$ = gen_rvalue_neg(c, &@1, &$2); + } + | ICIRC '(' rvalue ')' ASL IMM + { + @1.last_column = @6.last_column; + $$ = gen_tmp(c, &@1, 32, UNSIGNED); + OUT(c, &@1, "gen_read_ireg(", &$$, ", ", &$3, ", ", &$6, ");\n"); + } + | CIRCADD '(' rvalue ',' rvalue ',' rvalue ')' + { + @1.last_column = @8.last_column; + gen_circ_op(c, &@1, &$3, &$5, &$7); + } + | LOCNT '(' rvalue ')' + { + @1.last_column = @4.last_column; + /* Leading ones count */ + $$ = gen_locnt_op(c, &@1, &$3); + } + | COUNTONES '(' rvalue ')' + { + @1.last_column = @4.last_column; + /* Ones count */ + $$ = gen_ctpop_op(c, &@1, &$3); + } + | EXTRACT '(' rvalue ',' rvalue ')' + { + @1.last_column = @6.last_column; + $$ = gen_extract_op(c, &@1, &$5, &$3, &$1); + } + | EXTRANGE '(' rvalue ',' rvalue ',' rvalue ')' + { + @1.last_column = @8.last_column; + yyassert(c, &@1, $5.type == IMMEDIATE && + $5.imm.type == VALUE && + $7.type == IMMEDIATE && + $7.imm.type == VALUE, + "Range extract needs immediate values!\n"); + $$ = gen_rextract_op(c, + &@1, + &$3, + $7.imm.value, + $5.imm.value - $7.imm.value + 1); + } + | CAST4_8U '(' rvalue ')' + { + @1.last_column = @4.last_column; + $$ = gen_rvalue_truncate(c, &@1, &$3); + $$.signedness = UNSIGNED; + $$ = rvalue_materialize(c, &@1, &$$); + $$ = gen_rvalue_extend(c, &@1, &$$); + } + | BREV '(' rvalue ')' + { + @1.last_column = @4.last_column; + $$ = gen_rvalue_brev(c, &@1, &$3); + } + | ROTL '(' rvalue ',' rvalue ')' + { + @1.last_column = @6.last_column; + $$ = gen_rotl(c, &@1, &$3, &$5); + } + | ADDSAT64 '(' rvalue ',' rvalue ',' rvalue ')' + { + @1.last_column = @8.last_column; + gen_addsat64(c, &@1, &$3, &$5, &$7); + } + | CARRY_FROM_ADD '(' rvalue ',' rvalue ',' rvalue ')' + { + @1.last_column = @8.last_column; + $$ = gen_carry_from_add(c, &@1, &$3, &$5, &$7); + } + | LSBNEW '(' rvalue ')' + { + @1.last_column = @4.last_column; + HexValue one = gen_imm_value(c, &@1, 1, 32, UNSIGNED); + $$ = gen_bin_op(c, &@1, ANDB_OP, &$3, &one); + } + ; + +lvalue : FAIL + { + @1.last_column = @1.last_column; + yyassert(c, &@1, false, "Encountered a FAIL token as lvalue.\n"); + } + | REG + { + $$ = $1; + } + | var + { + $$ = $1; + } + ; + +%% + +int main(int argc, char **argv) +{ + if (argc != 5) { + fprintf(stderr, + "Semantics: Hexagon ISA to tinycode generator compiler\n\n"); + fprintf(stderr, + "Usage: ./semantics IDEFS EMITTER_C EMITTER_H " + "ENABLED_INSTRUCTIONS_LIST\n"); + return 1; + } + + enum { + ARG_INDEX_ARGV0 = 0, + ARG_INDEX_IDEFS, + ARG_INDEX_EMITTER_C, + ARG_INDEX_EMITTER_H, + ARG_INDEX_ENABLED_INSTRUCTIONS_LIST + }; + + FILE *enabled_file = fopen(argv[ARG_INDEX_ENABLED_INSTRUCTIONS_LIST], "w"); + + FILE *output_file = fopen(argv[ARG_INDEX_EMITTER_C], "w"); + fputs("#include \"qemu/osdep.h\"\n", output_file); + fputs("#include \"qemu/log.h\"\n", output_file); + fputs("#include \"cpu.h\"\n", output_file); + fputs("#include \"internal.h\"\n", output_file); + fputs("#include \"tcg/tcg.h\"\n", output_file); + fputs("#include \"tcg/tcg-op.h\"\n", output_file); + fputs("#include \"exec/helper-gen.h\"\n", output_file); + fputs("#include \"insn.h\"\n", output_file); + fputs("#include \"opcodes.h\"\n", output_file); + fputs("#include \"translate.h\"\n", output_file); + fputs("#define QEMU_GENERATE\n", output_file); + fputs("#include \"genptr.h\"\n", output_file); + fputs("#include \"macros.h\"\n", output_file); + fprintf(output_file, "#include \"%s\"\n", argv[ARG_INDEX_EMITTER_H]); + + FILE *defines_file = fopen(argv[ARG_INDEX_EMITTER_H], "w"); + assert(defines_file != NULL); + fputs("#ifndef HEX_EMITTER_H\n", defines_file); + fputs("#define HEX_EMITTER_H\n", defines_file); + fputs("\n", defines_file); + fputs("#include \"insn.h\"\n\n", defines_file); + + /* Parser input file */ + Context context = { 0 }; + context.defines_file = defines_file; + context.output_file = output_file; + context.enabled_file = enabled_file; + /* Initialize buffers */ + context.out_str = g_string_new(NULL); + context.signature_str = g_string_new(NULL); + context.header_str = g_string_new(NULL); + context.ternary = g_array_new(FALSE, TRUE, sizeof(Ternary)); + /* Read input file */ + FILE *input_file = fopen(argv[ARG_INDEX_IDEFS], "r"); + fseek(input_file, 0L, SEEK_END); + long input_size = ftell(input_file); + context.input_buffer = (char *) calloc(input_size + 1, sizeof(char)); + fseek(input_file, 0L, SEEK_SET); + size_t read_chars = fread(context.input_buffer, + sizeof(char), + input_size, + input_file); + if (read_chars != (size_t) input_size) { + fprintf(stderr, "Error: an error occurred while reading input file!\n"); + return -1; + } + yylex_init(&context.scanner); + YY_BUFFER_STATE buffer; + buffer = yy_scan_string(context.input_buffer, context.scanner); + /* Start the parsing procedure */ + yyparse(context.scanner, &context); + if (context.implemented_insn != context.total_insn) { + fprintf(stderr, + "Warning: %d/%d meta instructions have been implemented!\n", + context.implemented_insn, + context.total_insn); + } + fputs("#endif " START_COMMENT " HEX_EMITTER_h " END_COMMENT "\n", + defines_file); + /* Cleanup */ + yy_delete_buffer(buffer, context.scanner); + yylex_destroy(context.scanner); + free(context.input_buffer); + g_string_free(context.out_str, TRUE); + g_string_free(context.signature_str, TRUE); + g_string_free(context.header_str, TRUE); + g_array_free(context.ternary, TRUE); + fclose(output_file); + fclose(input_file); + fclose(defines_file); + fclose(enabled_file); + + return 0; +} diff --git a/target/hexagon/idef-parser/macros.inc b/target/hexagon/idef-parser/macros.inc new file mode 100644 index 0000000000..94975d9583 --- /dev/null +++ b/target/hexagon/idef-parser/macros.inc @@ -0,0 +1,131 @@ +/* + * Copyright(c) 2019-2022 rev.ng Labs Srl. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* Copy rules */ +#define fLSBOLD(VAL) (fGETBIT(0, VAL)) +#define fSATH(VAL) fSATN(16, VAL) +#define fSATUH(VAL) fSATUN(16, VAL) +#define fVSATH(VAL) fVSATN(16, VAL) +#define fVSATUH(VAL) fVSATUN(16, VAL) +#define fSATUB(VAL) fSATUN(8, VAL) +#define fSATB(VAL) fSATN(8, VAL) +#define fVSATUB(VAL) fVSATUN(8, VAL) +#define fVSATB(VAL) fVSATN(8, VAL) +#define fCALL(A) fWRITE_LR(fREAD_NPC()); fWRITE_NPC(A); +#define fCALLR(A) fWRITE_LR(fREAD_NPC()); fWRITE_NPC(A); +#define fCAST2_8s(A) fSXTN(16, 64, A) +#define fCAST2_8u(A) fZXTN(16, 64, A) +#define fVSATW(A) fVSATN(32, fCAST8_8s(A)) +#define fSATW(A) fSATN(32, fCAST8_8s(A)) +#define fVSAT(A) fVSATN(32, A) +#define fSAT(A) fSATN(32, A) + +/* Ease parsing */ +#define f8BITSOF(VAL) ((VAL) ? 0xff : 0x00) +#define fREAD_GP() (Constant_extended ? (0) : GP) +#define fCLIP(DST, SRC, U) (DST = fMIN((1 << U) - 1, fMAX(SRC, -(1 << U)))) +#define fBIDIR_ASHIFTL(SRC, SHAMT, REGSTYPE) \ + ((SHAMT > 0) ? \ + (fCAST##REGSTYPE##s(SRC) << SHAMT) : \ + (fCAST##REGSTYPE##s(SRC) >> -SHAMT)) + +#define fBIDIR_LSHIFTL(SRC, SHAMT, REGSTYPE) \ + ((SHAMT > 0) ? \ + (fCAST##REGSTYPE##u(SRC) << SHAMT) : \ + (fCAST##REGSTYPE##u(SRC) >>> -SHAMT)) + +#define fBIDIR_ASHIFTR(SRC, SHAMT, REGSTYPE) \ + ((SHAMT > 0) ? \ + (fCAST##REGSTYPE##s(SRC) >> SHAMT) : \ + (fCAST##REGSTYPE##s(SRC) << -SHAMT)) + +#define fBIDIR_SHIFTR(SRC, SHAMT, REGSTYPE) \ + (((SHAMT) < 0) ? ((fCAST##REGSTYPE(SRC) << ((-(SHAMT)) - 1)) << 1) \ + : (fCAST##REGSTYPE(SRC) >> (SHAMT))) + +#define fBIDIR_LSHIFTR(SRC, SHAMT, REGSTYPE) \ + fBIDIR_SHIFTR(SRC, SHAMT, REGSTYPE##u) + +#define fSATVALN(N, VAL) \ + fSET_OVERFLOW( \ + ((VAL) < 0) ? (-(1LL << ((N) - 1))) : ((1LL << ((N) - 1)) - 1) \ + ) + +#define fSAT_ORIG_SHL(A, ORIG_REG) \ + (((fCAST4s((fSAT(A)) ^ (fCAST4s(ORIG_REG)))) < 0) \ + ? fSATVALN(32, (fCAST4s(ORIG_REG))) \ + : ((((ORIG_REG) > 0) && ((A) == 0)) ? fSATVALN(32, (ORIG_REG)) \ + : fSAT(A))) + +#define fBIDIR_ASHIFTR_SAT(SRC, SHAMT, REGSTYPE) \ + (((SHAMT) < 0) ? fSAT_ORIG_SHL((fCAST##REGSTYPE##s(SRC) \ + << ((-(SHAMT)) - 1)) << 1, (SRC)) \ + : (fCAST##REGSTYPE##s(SRC) >> (SHAMT))) + +#define fBIDIR_ASHIFTL_SAT(SRC, SHAMT, REGSTYPE) \ + (((SHAMT) < 0) \ + ? ((fCAST##REGSTYPE##s(SRC) >> ((-(SHAMT)) - 1)) >> 1) \ + : fSAT_ORIG_SHL(fCAST##REGSTYPE##s(SRC) << (SHAMT), (SRC))) + +#define fEXTRACTU_BIDIR(INREG, WIDTH, OFFSET) \ + (fZXTN(WIDTH, 32, fBIDIR_LSHIFTR((INREG), (OFFSET), 4_8))) + +/* Least significant bit operations */ +#define fLSBNEW0 fLSBNEW(P0N) +#define fLSBNEW1 fLSBNEW(P1N) +#define fLSBOLDNOT(VAL) fGETBIT(0, ~VAL) +#define fLSBNEWNOT(PRED) (fLSBNEW(~PRED)) +#define fLSBNEW0NOT fLSBNEW(~P0N) +#define fLSBNEW1NOT fLSBNEW(~P1N) + +/* Assignments */ +#define fPCALIGN(IMM) (IMM = IMM & ~3) +#define fWRITE_LR(A) (LR = A) +#define fWRITE_FP(A) (FP = A) +#define fWRITE_SP(A) (SP = A) +#define fWRITE_LOOP_REGS0(START, COUNT) SA0 = START; (LC0 = COUNT) +#define fWRITE_LOOP_REGS1(START, COUNT) SA1 = START; (LC1 = COUNT) +#define fWRITE_LC1(VAL) (LC1 = VAL) +#define fSET_LPCFG(VAL) (USR.LPCFG = VAL) +#define fWRITE_P0(VAL) P0 = VAL; +#define fWRITE_P1(VAL) P1 = VAL; +#define fWRITE_P3(VAL) P3 = VAL; +#define fEA_RI(REG, IMM) (EA = REG + IMM) +#define fEA_RRs(REG, REG2, SCALE) (EA = REG + (REG2 << SCALE)) +#define fEA_IRs(IMM, REG, SCALE) (EA = IMM + (REG << SCALE)) +#define fEA_IMM(IMM) (EA = IMM) +#define fEA_REG(REG) (EA = REG) +#define fEA_BREVR(REG) (EA = fbrev(REG)) +#define fEA_GPI(IMM) (EA = fREAD_GP() + IMM) +#define fPM_I(REG, IMM) (REG = REG + IMM) +#define fPM_M(REG, MVAL) (REG = REG + MVAL) + +/* Unary operators */ +#define fROUND(A) (A + 0x8000) + +/* Binary operators */ +#define fSCALE(N, A) (A << N) +#define fASHIFTR(SRC, SHAMT, REGSTYPE) (fCAST##REGSTYPE##s(SRC) >> SHAMT) +#define fLSHIFTR(SRC, SHAMT, REGSTYPE) (SRC >>> SHAMT) +#define fROTL(SRC, SHAMT, REGSTYPE) fROTL(SRC, SHAMT) +#define fASHIFTL(SRC, SHAMT, REGSTYPE) (fCAST##REGSTYPE##s(SRC) << SHAMT) + +/* Include fHIDE macros which hide type declarations */ +#define fHIDE(A) A + +/* Purge non-relevant parts */ +#define fBRANCH_SPECULATE_STALL(A, B, C, D, E) diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c new file mode 100644 index 0000000000..95f2b43076 --- /dev/null +++ b/target/hexagon/idef-parser/parser-helpers.c @@ -0,0 +1,2139 @@ +/* + * Copyright(c) 2019-2023 rev.ng Labs Srl. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "idef-parser.h" +#include "parser-helpers.h" +#include "idef-parser.tab.h" +#include "idef-parser.yy.h" + +void yyerror(YYLTYPE *locp, + yyscan_t scanner __attribute__((unused)), + Context *c, + const char *s) +{ + const char *code_ptr = c->input_buffer; + + fprintf(stderr, "WARNING (%s): '%s'\n", c->inst.name->str, s); + + fprintf(stderr, "Problematic range: "); + for (int i = locp->first_column; i < locp->last_column; i++) { + if (code_ptr[i] != '\n') { + fprintf(stderr, "%c", code_ptr[i]); + } + } + fprintf(stderr, "\n"); + + for (unsigned i = 0; + i < 80 && + code_ptr[locp->first_column - 10 + i] != '\0' && + code_ptr[locp->first_column - 10 + i] != '\n'; + i++) { + fprintf(stderr, "%c", code_ptr[locp->first_column - 10 + i]); + } + fprintf(stderr, "\n"); + for (unsigned i = 0; i < 9; i++) { + fprintf(stderr, " "); + } + fprintf(stderr, "^"); + for (int i = 0; i < (locp->last_column - locp->first_column) - 1; i++) { + fprintf(stderr, "~"); + } + fprintf(stderr, "\n"); + c->inst.error_count++; +} + +bool is_direct_predicate(HexValue *value) +{ + return value->pred.id >= '0' && value->pred.id <= '3'; +} + +bool is_inside_ternary(Context *c) +{ + return c->ternary->len > 0; +} + +/* Print functions */ +void str_print(Context *c, YYLTYPE *locp, const char *string) +{ + (void) locp; + EMIT(c, "%s", string); +} + +void uint8_print(Context *c, YYLTYPE *locp, uint8_t *num) +{ + (void) locp; + EMIT(c, "%u", *num); +} + +void uint64_print(Context *c, YYLTYPE *locp, uint64_t *num) +{ + (void) locp; + EMIT(c, "%" PRIu64, *num); +} + +void int_print(Context *c, YYLTYPE *locp, int *num) +{ + (void) locp; + EMIT(c, "%d", *num); +} + +void uint_print(Context *c, YYLTYPE *locp, unsigned *num) +{ + (void) locp; + EMIT(c, "%u", *num); +} + +void tmp_print(Context *c, YYLTYPE *locp, HexTmp *tmp) +{ + (void) locp; + EMIT(c, "tmp_%d", tmp->index); +} + +void pred_print(Context *c, YYLTYPE *locp, HexPred *pred, bool is_dotnew) +{ + (void) locp; + char suffix = is_dotnew ? 'N' : 'V'; + EMIT(c, "P%c%c", pred->id, suffix); +} + +void reg_compose(Context *c, YYLTYPE *locp, HexReg *reg, char reg_id[5]) +{ + memset(reg_id, 0, 5 * sizeof(char)); + switch (reg->type) { + case GENERAL_PURPOSE: + reg_id[0] = 'R'; + break; + case CONTROL: + reg_id[0] = 'C'; + break; + case MODIFIER: + reg_id[0] = 'M'; + break; + case DOTNEW: + reg_id[0] = 'N'; + reg_id[1] = reg->id; + reg_id[2] = 'N'; + return; + } + switch (reg->bit_width) { + case 32: + reg_id[1] = reg->id; + reg_id[2] = 'V'; + break; + case 64: + reg_id[1] = reg->id; + reg_id[2] = reg->id; + reg_id[3] = 'V'; + break; + default: + yyassert(c, locp, false, "Unhandled register bit width!\n"); + } +} + +static void reg_arg_print(Context *c, YYLTYPE *locp, HexReg *reg) +{ + char reg_id[5]; + reg_compose(c, locp, reg, reg_id); + EMIT(c, "%s", reg_id); +} + +void reg_print(Context *c, YYLTYPE *locp, HexReg *reg) +{ + (void) locp; + EMIT(c, "hex_gpr[%u]", reg->id); +} + +void imm_print(Context *c, YYLTYPE *locp, HexValue *rvalue) +{ + HexImm *imm = &rvalue->imm; + switch (imm->type) { + case I: + EMIT(c, "i"); + break; + case VARIABLE: + EMIT(c, "%ciV", imm->id); + break; + case VALUE: + if (rvalue->bit_width == 32) { + if (rvalue->signedness == UNSIGNED) { + EMIT(c, "((uint32_t) 0x%" PRIx32 ")", (uint32_t) imm->value); + } else { + EMIT(c, "((int32_t) 0x%" PRIx32 ")", (int32_t) imm->value); + } + } else if (rvalue->bit_width == 64) { + if (rvalue->signedness == UNSIGNED) { + EMIT(c, "((uint64_t) 0x%" PRIx64 "ULL)", (uint64_t) imm->value); + } else { + EMIT(c, "((int64_t) 0x%" PRIx64 "LL)", (int64_t) imm->value); + } + } else { + g_assert_not_reached(); + } + break; + case QEMU_TMP: + EMIT(c, "qemu_tmp_%" PRIu64, imm->index); + break; + case IMM_PC: + EMIT(c, "ctx->base.pc_next"); + break; + case IMM_CONSTEXT: + EMIT(c, "insn->extension_valid"); + break; + default: + yyassert(c, locp, false, "Cannot print this expression!"); + } +} + +void var_print(Context *c, YYLTYPE *locp, HexVar *var) +{ + (void) locp; + EMIT(c, "%s", var->name->str); +} + +void rvalue_print(Context *c, YYLTYPE *locp, void *pointer) +{ + HexValue *rvalue = (HexValue *) pointer; + switch (rvalue->type) { + case REGISTER: + reg_print(c, locp, &rvalue->reg); + break; + case REGISTER_ARG: + reg_arg_print(c, locp, &rvalue->reg); + break; + case TEMP: + tmp_print(c, locp, &rvalue->tmp); + break; + case IMMEDIATE: + imm_print(c, locp, rvalue); + break; + case VARID: + var_print(c, locp, &rvalue->var); + break; + case PREDICATE: + pred_print(c, locp, &rvalue->pred, rvalue->is_dotnew); + break; + default: + yyassert(c, locp, false, "Cannot print this expression!"); + } +} + +void out_assert(Context *c, YYLTYPE *locp, + void *dummy __attribute__((unused))) +{ + yyassert(c, locp, false, "Unhandled print type!"); +} + +/* Copy output code buffer */ +void commit(Context *c) +{ + /* Emit instruction pseudocode */ + EMIT_SIG(c, "\n" START_COMMENT " "); + for (char *x = c->inst.code_begin; x < c->inst.code_end; x++) { + EMIT_SIG(c, "%c", *x); + } + EMIT_SIG(c, " " END_COMMENT "\n"); + + /* Commit instruction code to output file */ + fwrite(c->signature_str->str, sizeof(char), c->signature_str->len, + c->output_file); + fwrite(c->header_str->str, sizeof(char), c->header_str->len, + c->output_file); + fwrite(c->out_str->str, sizeof(char), c->out_str->len, + c->output_file); + + fwrite(c->signature_str->str, sizeof(char), c->signature_str->len, + c->defines_file); + fprintf(c->defines_file, ";\n"); +} + +static void gen_c_int_type(Context *c, YYLTYPE *locp, unsigned bit_width, + HexSignedness signedness) +{ + const char *signstr = (signedness == UNSIGNED) ? "u" : ""; + OUT(c, locp, signstr, "int", &bit_width, "_t"); +} + +static HexValue gen_constant(Context *c, + YYLTYPE *locp, + const char *value, + unsigned bit_width, + HexSignedness signedness) +{ + HexValue rvalue; + assert(bit_width == 32 || bit_width == 64); + memset(&rvalue, 0, sizeof(HexValue)); + rvalue.type = TEMP; + rvalue.bit_width = bit_width; + rvalue.signedness = signedness; + rvalue.is_dotnew = false; + rvalue.tmp.index = c->inst.tmp_count; + OUT(c, locp, "TCGv_i", &bit_width, " tmp_", &c->inst.tmp_count, + " = tcg_constant_i", &bit_width, "(", value, ");\n"); + c->inst.tmp_count++; + return rvalue; +} + +/* Temporary values creation */ +HexValue gen_tmp(Context *c, + YYLTYPE *locp, + unsigned bit_width, + HexSignedness signedness) +{ + HexValue rvalue; + assert(bit_width == 32 || bit_width == 64); + memset(&rvalue, 0, sizeof(HexValue)); + rvalue.type = TEMP; + rvalue.bit_width = bit_width; + rvalue.signedness = signedness; + rvalue.is_dotnew = false; + rvalue.tmp.index = c->inst.tmp_count; + OUT(c, locp, "TCGv_i", &bit_width, " tmp_", &c->inst.tmp_count, + " = tcg_temp_new_i", &bit_width, "();\n"); + c->inst.tmp_count++; + return rvalue; +} + +static HexValue gen_constant_from_imm(Context *c, + YYLTYPE *locp, + HexValue *value) +{ + HexValue rvalue; + assert(value->type == IMMEDIATE); + memset(&rvalue, 0, sizeof(HexValue)); + rvalue.type = TEMP; + rvalue.bit_width = value->bit_width; + rvalue.signedness = value->signedness; + rvalue.is_dotnew = false; + rvalue.tmp.index = c->inst.tmp_count; + /* + * Here we output the call to `tcg_constant_i` in + * order to create the temporary value. Note, that we + * add a cast + * + * `tcg_constant_i`((int_t) ...)` + * + * This cast is required to avoid implicit integer + * conversion warnings since all immediates are + * output as `((int64_t) 123ULL)`, even if the + * integer is 32-bit. + */ + OUT(c, locp, "TCGv_i", &rvalue.bit_width, " tmp_", &c->inst.tmp_count); + OUT(c, locp, " = tcg_constant_i", &rvalue.bit_width, + "((int", &rvalue.bit_width, "_t) (", value, "));\n"); + + c->inst.tmp_count++; + return rvalue; +} + +HexValue gen_imm_value(Context *c __attribute__((unused)), + YYLTYPE *locp, + int value, + unsigned bit_width, + HexSignedness signedness) +{ + (void) locp; + HexValue rvalue; + assert(bit_width == 32 || bit_width == 64); + memset(&rvalue, 0, sizeof(HexValue)); + rvalue.type = IMMEDIATE; + rvalue.bit_width = bit_width; + rvalue.signedness = signedness; + rvalue.is_dotnew = false; + rvalue.imm.type = VALUE; + rvalue.imm.value = value; + return rvalue; +} + +HexValue gen_imm_qemu_tmp(Context *c, YYLTYPE *locp, unsigned bit_width, + HexSignedness signedness) +{ + (void) locp; + HexValue rvalue; + assert(bit_width == 32 || bit_width == 64); + memset(&rvalue, 0, sizeof(HexValue)); + rvalue.type = IMMEDIATE; + rvalue.is_dotnew = false; + rvalue.bit_width = bit_width; + rvalue.signedness = signedness; + rvalue.imm.type = QEMU_TMP; + rvalue.imm.index = c->inst.qemu_tmp_count++; + return rvalue; +} + +HexValue rvalue_materialize(Context *c, YYLTYPE *locp, HexValue *rvalue) +{ + if (rvalue->type == IMMEDIATE) { + return gen_constant_from_imm(c, locp, rvalue); + } + return *rvalue; +} + +HexValue gen_rvalue_extend(Context *c, YYLTYPE *locp, HexValue *rvalue) +{ + assert_signedness(c, locp, rvalue->signedness); + if (rvalue->bit_width > 32) { + return *rvalue; + } + + if (rvalue->type == IMMEDIATE) { + HexValue res = gen_imm_qemu_tmp(c, locp, 64, rvalue->signedness); + gen_c_int_type(c, locp, 64, rvalue->signedness); + OUT(c, locp, " ", &res, " = ("); + gen_c_int_type(c, locp, 64, rvalue->signedness); + OUT(c, locp, ")", rvalue, ";\n"); + return res; + } else { + HexValue res = gen_tmp(c, locp, 64, rvalue->signedness); + bool is_unsigned = (rvalue->signedness == UNSIGNED); + const char *sign_suffix = is_unsigned ? "u" : ""; + OUT(c, locp, "tcg_gen_ext", sign_suffix, + "_i32_i64(", &res, ", ", rvalue, ");\n"); + return res; + } +} + +HexValue gen_rvalue_truncate(Context *c, YYLTYPE *locp, HexValue *rvalue) +{ + if (rvalue->type == IMMEDIATE) { + HexValue res = *rvalue; + res.bit_width = 32; + return res; + } else { + if (rvalue->bit_width == 64) { + HexValue res = gen_tmp(c, locp, 32, rvalue->signedness); + OUT(c, locp, "tcg_gen_trunc_i64_tl(", &res, ", ", rvalue, ");\n"); + return res; + } + } + return *rvalue; +} + +/* + * Attempts to lookup the `Var` struct associated with the given `varid`. + * The `dst` argument is populated with the found name, bit_width, and + * signedness, given that `dst` is non-NULL. Returns true if the lookup + * succeeded and false otherwise. + */ +static bool try_find_variable(Context *c, YYLTYPE *locp, + HexValue *dst, + HexValue *varid) +{ + yyassert(c, locp, varid, "varid to lookup is NULL"); + yyassert(c, locp, varid->type == VARID, + "Can only lookup variables by varid"); + for (unsigned i = 0; i < c->inst.allocated->len; i++) { + Var *curr = &g_array_index(c->inst.allocated, Var, i); + if (g_string_equal(varid->var.name, curr->name)) { + if (dst) { + dst->var.name = curr->name; + dst->bit_width = curr->bit_width; + dst->signedness = curr->signedness; + } + return true; + } + } + return false; +} + +/* Calls `try_find_variable` and asserts success. */ +static void find_variable(Context *c, YYLTYPE *locp, + HexValue *dst, + HexValue *varid) +{ + bool found = try_find_variable(c, locp, dst, varid); + yyassert(c, locp, found, "Use of undeclared variable!\n"); +} + +/* Handle signedness, if both unsigned -> result is unsigned, else signed */ +static inline HexSignedness bin_op_signedness(Context *c, YYLTYPE *locp, + HexSignedness sign1, + HexSignedness sign2) +{ + assert_signedness(c, locp, sign1); + assert_signedness(c, locp, sign2); + return (sign1 == UNSIGNED && sign2 == UNSIGNED) ? UNSIGNED : SIGNED; +} + +void gen_varid_allocate(Context *c, + YYLTYPE *locp, + HexValue *varid, + unsigned bit_width, + HexSignedness signedness) +{ + const char *bit_suffix = (bit_width == 64) ? "i64" : "i32"; + bool found = try_find_variable(c, locp, NULL, varid); + Var new_var; + + memset(&new_var, 0, sizeof(Var)); + + yyassert(c, locp, !found, "Redeclaration of variables not allowed!"); + assert_signedness(c, locp, signedness); + + /* `varid` only carries name information */ + new_var.name = varid->var.name; + new_var.bit_width = bit_width; + new_var.signedness = signedness; + + EMIT_HEAD(c, "TCGv_%s %s", bit_suffix, varid->var.name->str); + EMIT_HEAD(c, " = tcg_temp_new_%s();\n", bit_suffix); + g_array_append_val(c->inst.allocated, new_var); +} + +enum OpTypes { + IMM_IMM = 0, + IMM_REG = 1, + REG_IMM = 2, + REG_REG = 3, +}; + +HexValue gen_bin_cmp(Context *c, + YYLTYPE *locp, + TCGCond type, + HexValue *op1, + HexValue *op2) +{ + HexValue op1_m = *op1; + HexValue op2_m = *op2; + enum OpTypes op_types = (op1_m.type != IMMEDIATE) << 1 + | (op2_m.type != IMMEDIATE); + + bool op_is64bit = op1_m.bit_width == 64 || op2_m.bit_width == 64; + const char *bit_suffix = op_is64bit ? "i64" : "i32"; + unsigned bit_width = (op_is64bit) ? 64 : 32; + HexValue res = gen_tmp(c, locp, bit_width, UNSIGNED); + + /* Extend to 64-bits, if required */ + if (op_is64bit) { + op1_m = gen_rvalue_extend(c, locp, &op1_m); + op2_m = gen_rvalue_extend(c, locp, &op2_m); + } + + switch (op_types) { + case IMM_IMM: + case IMM_REG: + yyassert(c, locp, false, "Binary comparisons between IMM op IMM and" + "IMM op REG not handled!"); + break; + case REG_IMM: + OUT(c, locp, "tcg_gen_setcondi_", bit_suffix, "("); + OUT(c, locp, cond_to_str(type), ", ", &res, ", ", &op1_m, ", ", &op2_m, + ");\n"); + break; + case REG_REG: + OUT(c, locp, "tcg_gen_setcond_", bit_suffix, "("); + OUT(c, locp, cond_to_str(type), ", ", &res, ", ", &op1_m, ", ", &op2_m, + ");\n"); + break; + default: + fprintf(stderr, "Error in evaluating immediateness!"); + abort(); + } + return res; +} + +static void gen_simple_op(Context *c, YYLTYPE *locp, unsigned bit_width, + const char *bit_suffix, HexValue *res, + enum OpTypes op_types, + HexValue *op1, + HexValue *op2, + const char *imm_imm, + const char *imm_reg, + const char *reg_imm, + const char *reg_reg) +{ + switch (op_types) { + case IMM_IMM: { + HexSignedness signedness = bin_op_signedness(c, locp, + op1->signedness, + op2->signedness); + gen_c_int_type(c, locp, bit_width, signedness); + OUT(c, locp, " ", res, + " = ", op1, imm_imm, op2, ";\n"); + } break; + case IMM_REG: + OUT(c, locp, imm_reg, bit_suffix, + "(", res, ", ", op2, ", ", op1, ");\n"); + break; + case REG_IMM: + OUT(c, locp, reg_imm, bit_suffix, + "(", res, ", ", op1, ", ", op2, ");\n"); + break; + case REG_REG: + OUT(c, locp, reg_reg, bit_suffix, + "(", res, ", ", op1, ", ", op2, ");\n"); + break; + } +} + +static void gen_sub_op(Context *c, YYLTYPE *locp, unsigned bit_width, + const char *bit_suffix, HexValue *res, + enum OpTypes op_types, HexValue *op1, + HexValue *op2) +{ + switch (op_types) { + case IMM_IMM: { + HexSignedness signedness = bin_op_signedness(c, locp, + op1->signedness, + op2->signedness); + gen_c_int_type(c, locp, bit_width, signedness); + OUT(c, locp, " ", res, + " = ", op1, " - ", op2, ";\n"); + } break; + case IMM_REG: { + OUT(c, locp, "tcg_gen_subfi_", bit_suffix, + "(", res, ", ", op1, ", ", op2, ");\n"); + } break; + case REG_IMM: { + OUT(c, locp, "tcg_gen_subi_", bit_suffix, + "(", res, ", ", op1, ", ", op2, ");\n"); + } break; + case REG_REG: { + OUT(c, locp, "tcg_gen_sub_", bit_suffix, + "(", res, ", ", op1, ", ", op2, ");\n"); + } break; + } +} + +static void gen_asl_op(Context *c, YYLTYPE *locp, unsigned bit_width, + bool op_is64bit, const char *bit_suffix, + HexValue *res, enum OpTypes op_types, + HexValue *op1, HexValue *op2) +{ + HexValue op1_m = *op1; + HexValue op2_m = *op2; + switch (op_types) { + case IMM_IMM: { + HexSignedness signedness = bin_op_signedness(c, locp, + op1->signedness, + op2->signedness); + gen_c_int_type(c, locp, bit_width, signedness); + OUT(c, locp, " ", res, + " = ", op1, " << ", op2, ";\n"); + } break; + case REG_IMM: { + OUT(c, locp, "if (", op2, " >= ", &bit_width, ") {\n"); + OUT(c, locp, "tcg_gen_movi_", bit_suffix, "(", res, ", 0);\n"); + OUT(c, locp, "} else {\n"); + OUT(c, locp, "tcg_gen_shli_", bit_suffix, + "(", res, ", ", op1, ", ", op2, ");\n"); + OUT(c, locp, "}\n"); + } break; + case IMM_REG: + op1_m.bit_width = bit_width; + op1_m = rvalue_materialize(c, locp, &op1_m); + /* fallthrough */ + case REG_REG: { + OUT(c, locp, "tcg_gen_shl_", bit_suffix, + "(", res, ", ", &op1_m, ", ", op2, ");\n"); + } break; + } + if (op_types == IMM_REG || op_types == REG_REG) { + /* + * Handle left shift by 64/32 which hexagon-sim expects to clear out + * register + */ + HexValue zero = gen_constant(c, locp, "0", bit_width, UNSIGNED); + HexValue edge = gen_imm_value(c, locp, bit_width, bit_width, UNSIGNED); + edge = rvalue_materialize(c, locp, &edge); + if (op_is64bit) { + op2_m = gen_rvalue_extend(c, locp, &op2_m); + } + op1_m = rvalue_materialize(c, locp, &op1_m); + op2_m = rvalue_materialize(c, locp, &op2_m); + OUT(c, locp, "tcg_gen_movcond_i", &bit_width); + OUT(c, locp, "(TCG_COND_GEU, ", res, ", ", &op2_m, ", ", &edge); + OUT(c, locp, ", ", &zero, ", ", res, ");\n"); + } +} + +static void gen_asr_op(Context *c, YYLTYPE *locp, unsigned bit_width, + bool op_is64bit, const char *bit_suffix, + HexValue *res, enum OpTypes op_types, + HexValue *op1, HexValue *op2) +{ + HexValue op1_m = *op1; + HexValue op2_m = *op2; + switch (op_types) { + case IMM_IMM: + case IMM_REG: + yyassert(c, locp, false, "ASR between IMM op IMM, and IMM op REG" + " not handled!"); + break; + case REG_IMM: { + HexSignedness signedness = bin_op_signedness(c, locp, + op1->signedness, + op2->signedness); + OUT(c, locp, "{\n"); + gen_c_int_type(c, locp, bit_width, signedness); + OUT(c, locp, " shift = ", op2, ";\n"); + OUT(c, locp, "if (", op2, " >= ", &bit_width, ") {\n"); + OUT(c, locp, " shift = ", &bit_width, " - 1;\n"); + OUT(c, locp, "}\n"); + OUT(c, locp, "tcg_gen_sari_", bit_suffix, + "(", res, ", ", op1, ", shift);\n}\n"); + } break; + case REG_REG: + OUT(c, locp, "tcg_gen_sar_", bit_suffix, + "(", res, ", ", &op1_m, ", ", op2, ");\n"); + break; + } + if (op_types == REG_REG) { + /* Handle right shift by values >= bit_width */ + const char *offset = op_is64bit ? "63" : "31"; + HexValue tmp = gen_tmp(c, locp, bit_width, SIGNED); + HexValue zero = gen_constant(c, locp, "0", bit_width, SIGNED); + HexValue edge = gen_imm_value(c, locp, bit_width, bit_width, UNSIGNED); + + edge = rvalue_materialize(c, locp, &edge); + if (op_is64bit) { + op2_m = gen_rvalue_extend(c, locp, &op2_m); + } + op1_m = rvalue_materialize(c, locp, &op1_m); + op2_m = rvalue_materialize(c, locp, &op2_m); + + OUT(c, locp, "tcg_gen_extract_", bit_suffix, "(", + &tmp, ", ", &op1_m, ", ", offset, ", 1);\n"); + OUT(c, locp, "tcg_gen_sub_", bit_suffix, "(", + &tmp, ", ", &zero, ", ", &tmp, ");\n"); + OUT(c, locp, "tcg_gen_movcond_i", &bit_width); + OUT(c, locp, "(TCG_COND_GEU, ", res, ", ", &op2_m, ", ", &edge); + OUT(c, locp, ", ", &tmp, ", ", res, ");\n"); + } +} + +static void gen_lsr_op(Context *c, YYLTYPE *locp, unsigned bit_width, + bool op_is64bit, const char *bit_suffix, + HexValue *res, enum OpTypes op_types, + HexValue *op1, HexValue *op2) +{ + HexValue op1_m = *op1; + HexValue op2_m = *op2; + switch (op_types) { + case IMM_IMM: + case IMM_REG: + yyassert(c, locp, false, "LSR between IMM op IMM, and IMM op REG" + " not handled!"); + break; + case REG_IMM: + OUT(c, locp, "if (", op2, " >= ", &bit_width, ") {\n"); + OUT(c, locp, "tcg_gen_movi_", bit_suffix, "(", res, ", 0);\n"); + OUT(c, locp, "} else {\n"); + OUT(c, locp, "tcg_gen_shri_", bit_suffix, + "(", res, ", ", op1, ", ", op2, ");\n"); + OUT(c, locp, "}\n"); + break; + case REG_REG: + OUT(c, locp, "tcg_gen_shr_", bit_suffix, + "(", res, ", ", &op1_m, ", ", op2, ");\n"); + break; + } + if (op_types == REG_REG) { + /* Handle right shift by values >= bit_width */ + HexValue zero = gen_constant(c, locp, "0", bit_width, UNSIGNED); + HexValue edge = gen_imm_value(c, locp, bit_width, bit_width, UNSIGNED); + edge = rvalue_materialize(c, locp, &edge); + if (op_is64bit) { + op2_m = gen_rvalue_extend(c, locp, &op2_m); + } + op1_m = rvalue_materialize(c, locp, &op1_m); + op2_m = rvalue_materialize(c, locp, &op2_m); + OUT(c, locp, "tcg_gen_movcond_i", &bit_width); + OUT(c, locp, "(TCG_COND_GEU, ", res, ", ", &op2_m, ", ", &edge); + OUT(c, locp, ", ", &zero, ", ", res, ");\n"); + } +} + +/* + * Note: This implementation of logical `and` does not mirror that in C. + * We do not short-circuit logical expressions! + */ +static void gen_andl_op(Context *c, YYLTYPE *locp, unsigned bit_width, + const char *bit_suffix, HexValue *res, + enum OpTypes op_types, HexValue *op1, + HexValue *op2) +{ + (void) bit_width; + HexValue tmp1, tmp2; + HexValue zero = gen_constant(c, locp, "0", 32, UNSIGNED); + memset(&tmp1, 0, sizeof(HexValue)); + memset(&tmp2, 0, sizeof(HexValue)); + switch (op_types) { + case IMM_IMM: + case IMM_REG: + case REG_IMM: + yyassert(c, locp, false, "ANDL between IMM op IMM, IMM op REG, and" + " REG op IMM, not handled!"); + break; + case REG_REG: + tmp1 = gen_bin_cmp(c, locp, TCG_COND_NE, op1, &zero); + tmp2 = gen_bin_cmp(c, locp, TCG_COND_NE, op2, &zero); + OUT(c, locp, "tcg_gen_and_", bit_suffix, + "(", res, ", ", &tmp1, ", ", &tmp2, ");\n"); + break; + } +} + +static void gen_minmax_op(Context *c, YYLTYPE *locp, unsigned bit_width, + HexValue *res, enum OpTypes op_types, + HexValue *op1, HexValue *op2, bool minmax) +{ + const char *mm; + HexValue op1_m = *op1; + HexValue op2_m = *op2; + bool is_unsigned; + + assert_signedness(c, locp, res->signedness); + is_unsigned = res->signedness == UNSIGNED; + + if (minmax) { + /* Max */ + mm = is_unsigned ? "tcg_gen_umax" : "tcg_gen_smax"; + } else { + /* Min */ + mm = is_unsigned ? "tcg_gen_umin" : "tcg_gen_smin"; + } + switch (op_types) { + case IMM_IMM: + yyassert(c, locp, false, "MINMAX between IMM op IMM, not handled!"); + break; + case IMM_REG: + op1_m.bit_width = bit_width; + op1_m = rvalue_materialize(c, locp, &op1_m); + OUT(c, locp, mm, "_i", &bit_width, "("); + OUT(c, locp, res, ", ", &op1_m, ", ", op2, ");\n"); + break; + case REG_IMM: + op2_m.bit_width = bit_width; + op2_m = rvalue_materialize(c, locp, &op2_m); + /* Fallthrough */ + case REG_REG: + OUT(c, locp, mm, "_i", &bit_width, "("); + OUT(c, locp, res, ", ", op1, ", ", &op2_m, ");\n"); + break; + } +} + +/* Code generation functions */ +HexValue gen_bin_op(Context *c, + YYLTYPE *locp, + OpType type, + HexValue *op1, + HexValue *op2) +{ + /* Replicate operands to avoid side effects */ + HexValue op1_m = *op1; + HexValue op2_m = *op2; + enum OpTypes op_types; + bool op_is64bit; + HexSignedness signedness; + unsigned bit_width; + const char *bit_suffix; + HexValue res; + + memset(&res, 0, sizeof(HexValue)); + + /* + * If the operands are VARID's we need to look up the + * type information. + */ + if (op1_m.type == VARID) { + find_variable(c, locp, &op1_m, &op1_m); + } + if (op2_m.type == VARID) { + find_variable(c, locp, &op2_m, &op2_m); + } + + op_types = (op1_m.type != IMMEDIATE) << 1 + | (op2_m.type != IMMEDIATE); + op_is64bit = op1_m.bit_width == 64 || op2_m.bit_width == 64; + /* Shift greater than 32 are 64 bits wide */ + + if (type == ASL_OP && op2_m.type == IMMEDIATE && + op2_m.imm.type == VALUE && op2_m.imm.value >= 32) { + op_is64bit = true; + } + + bit_width = (op_is64bit) ? 64 : 32; + bit_suffix = op_is64bit ? "i64" : "i32"; + + /* Extend to 64-bits, if required */ + if (op_is64bit) { + op1_m = gen_rvalue_extend(c, locp, &op1_m); + op2_m = gen_rvalue_extend(c, locp, &op2_m); + } + + signedness = bin_op_signedness(c, locp, op1_m.signedness, op2_m.signedness); + if (op_types != IMM_IMM) { + res = gen_tmp(c, locp, bit_width, signedness); + } else { + res = gen_imm_qemu_tmp(c, locp, bit_width, signedness); + } + + switch (type) { + case ADD_OP: + gen_simple_op(c, locp, bit_width, bit_suffix, &res, + op_types, &op1_m, &op2_m, + " + ", + "tcg_gen_addi_", + "tcg_gen_addi_", + "tcg_gen_add_"); + break; + case SUB_OP: + gen_sub_op(c, locp, bit_width, bit_suffix, &res, op_types, + &op1_m, &op2_m); + break; + case MUL_OP: + gen_simple_op(c, locp, bit_width, bit_suffix, &res, + op_types, &op1_m, &op2_m, + " * ", + "tcg_gen_muli_", + "tcg_gen_muli_", + "tcg_gen_mul_"); + break; + case ASL_OP: + gen_asl_op(c, locp, bit_width, op_is64bit, bit_suffix, &res, op_types, + &op1_m, &op2_m); + break; + case ASR_OP: + gen_asr_op(c, locp, bit_width, op_is64bit, bit_suffix, &res, op_types, + &op1_m, &op2_m); + break; + case LSR_OP: + gen_lsr_op(c, locp, bit_width, op_is64bit, bit_suffix, &res, op_types, + &op1_m, &op2_m); + break; + case ANDB_OP: + gen_simple_op(c, locp, bit_width, bit_suffix, &res, + op_types, &op1_m, &op2_m, + " & ", + "tcg_gen_andi_", + "tcg_gen_andi_", + "tcg_gen_and_"); + break; + case ORB_OP: + gen_simple_op(c, locp, bit_width, bit_suffix, &res, + op_types, &op1_m, &op2_m, + " | ", + "tcg_gen_ori_", + "tcg_gen_ori_", + "tcg_gen_or_"); + break; + case XORB_OP: + gen_simple_op(c, locp, bit_width, bit_suffix, &res, + op_types, &op1_m, &op2_m, + " ^ ", + "tcg_gen_xori_", + "tcg_gen_xori_", + "tcg_gen_xor_"); + break; + case ANDL_OP: + gen_andl_op(c, locp, bit_width, bit_suffix, &res, op_types, &op1_m, + &op2_m); + break; + case MINI_OP: + gen_minmax_op(c, locp, bit_width, &res, op_types, &op1_m, &op2_m, + false); + break; + case MAXI_OP: + gen_minmax_op(c, locp, bit_width, &res, op_types, &op1_m, &op2_m, true); + break; + } + return res; +} + +HexValue gen_cast_op(Context *c, + YYLTYPE *locp, + HexValue *src, + unsigned target_width, + HexSignedness signedness) +{ + HexValue res; + assert_signedness(c, locp, src->signedness); + if (src->bit_width == target_width) { + res = *src; + } else if (src->bit_width < target_width) { + res = gen_rvalue_extend(c, locp, src); + } else { + /* src->bit_width > target_width */ + res = gen_rvalue_truncate(c, locp, src); + } + res.signedness = signedness; + return res; +} + + +/* + * Implements an extension when the `src_width` is an immediate. + * If the `value` to extend is also an immediate we use `extract/sextract` + * from QEMU `bitops.h`. If `value` is a TCGv then we rely on + * `tcg_gen_extract/tcg_gen_sextract`. + */ +static HexValue gen_extend_imm_width_op(Context *c, + YYLTYPE *locp, + HexValue *src_width, + unsigned dst_width, + HexValue *value, + HexSignedness signedness) +{ + /* + * If the source width is not an immediate value, we need to guard + * our extend op with if statements to handle the case where + * `src_width_m` is 0. + */ + const char *sign_prefix; + bool need_guarding; + + assert_signedness(c, locp, signedness); + assert(dst_width == 64 || dst_width == 32); + assert(src_width->type == IMMEDIATE); + + sign_prefix = (signedness == UNSIGNED) ? "" : "s"; + need_guarding = (src_width->imm.type != VALUE); + + if (src_width->imm.type == VALUE && + src_width->imm.value == 0) { + /* + * We can bail out early if the source width is known to be zero + * at translation time. + */ + return gen_imm_value(c, locp, 0, dst_width, signedness); + } + + if (value->type == IMMEDIATE) { + /* + * If both the value and source width are immediates, + * we can perform the extension at translation time + * using QEMUs bitops. + */ + HexValue res = gen_imm_qemu_tmp(c, locp, dst_width, signedness); + gen_c_int_type(c, locp, dst_width, signedness); + OUT(c, locp, " ", &res, " = 0;\n"); + if (need_guarding) { + OUT(c, locp, "if (", src_width, " != 0) {\n"); + } + OUT(c, locp, &res, " = ", sign_prefix, "extract", &dst_width); + OUT(c, locp, "(", value, ", 0, ", src_width, ");\n"); + if (need_guarding) { + OUT(c, locp, "}\n"); + } + return res; + } else { + /* + * If the source width is an immediate and the value to + * extend is a TCGv, then use tcg_gen_extract/tcg_gen_sextract + */ + HexValue res = gen_tmp(c, locp, dst_width, signedness); + + /* + * If the width is an immediate value we know it is non-zero + * at this point, otherwise we need an if-statement + */ + if (need_guarding) { + OUT(c, locp, "if (", src_width, " != 0) {\n"); + } + OUT(c, locp, "tcg_gen_", sign_prefix, "extract_i", &dst_width); + OUT(c, locp, "(", &res, ", ", value, ", 0, ", src_width, + ");\n"); + if (need_guarding) { + OUT(c, locp, "} else {\n"); + OUT(c, locp, "tcg_gen_movi_i", &dst_width, "(", &res, + ", 0);\n"); + OUT(c, locp, "}\n"); + } + return res; + } +} + +/* + * Implements an extension when the `src_width` is given by + * a TCGv. Here we need to reimplement the behaviour of + * `tcg_gen_extract` and the like using shifts and masks. + */ +static HexValue gen_extend_tcg_width_op(Context *c, + YYLTYPE *locp, + HexValue *src_width, + unsigned dst_width, + HexValue *value, + HexSignedness signedness) +{ + HexValue src_width_m = rvalue_materialize(c, locp, src_width); + HexValue zero = gen_constant(c, locp, "0", dst_width, UNSIGNED); + HexValue shift = gen_tmp(c, locp, dst_width, UNSIGNED); + HexValue res; + + assert_signedness(c, locp, signedness); + assert(dst_width == 64 || dst_width == 32); + assert(src_width->type != IMMEDIATE); + + res = gen_tmp(c, locp, dst_width, signedness); + + OUT(c, locp, "tcg_gen_subfi_i", &dst_width); + OUT(c, locp, "(", &shift, ", ", &dst_width, ", ", &src_width_m, ");\n"); + if (signedness == UNSIGNED) { + HexValue mask = gen_constant(c, locp, "-1", dst_width, UNSIGNED); + OUT(c, locp, "tcg_gen_shr_i", &dst_width, "(", + &res, ", ", &mask, ", ", &shift, ");\n"); + OUT(c, locp, "tcg_gen_and_i", &dst_width, "(", + &res, ", ", &res, ", ", value, ");\n"); + } else { + OUT(c, locp, "tcg_gen_shl_i", &dst_width, "(", + &res, ", ", value, ", ", &shift, ");\n"); + OUT(c, locp, "tcg_gen_sar_i", &dst_width, "(", + &res, ", ", &res, ", ", &shift, ");\n"); + } + OUT(c, locp, "tcg_gen_movcond_i", &dst_width, "(TCG_COND_EQ, ", &res, + ", "); + OUT(c, locp, &src_width_m, ", ", &zero, ", ", &zero, ", ", &res, + ");\n"); + + return res; +} + +HexValue gen_extend_op(Context *c, + YYLTYPE *locp, + HexValue *src_width, + unsigned dst_width, + HexValue *value, + HexSignedness signedness) +{ + unsigned bit_width = (dst_width == 64) ? 64 : 32; + HexValue value_m = *value; + HexValue src_width_m = *src_width; + + assert_signedness(c, locp, signedness); + yyassert(c, locp, value_m.bit_width <= bit_width && + src_width_m.bit_width <= bit_width, + "Extending to a size smaller than the current size" + " makes no sense"); + + if (value_m.bit_width < bit_width) { + value_m = gen_rvalue_extend(c, locp, &value_m); + } + + if (src_width_m.bit_width < bit_width) { + src_width_m = gen_rvalue_extend(c, locp, &src_width_m); + } + + if (src_width_m.type == IMMEDIATE) { + return gen_extend_imm_width_op(c, locp, &src_width_m, bit_width, + &value_m, signedness); + } else { + return gen_extend_tcg_width_op(c, locp, &src_width_m, bit_width, + &value_m, signedness); + } +} + +/* + * Implements `rdeposit` for the special case where `width` + * is of TCGv type. In this case we need to reimplement the behaviour + * of `tcg_gen_deposit*` using binary operations and masks/shifts. + * + * Note: this is the only type of `rdeposit` that occurs, meaning the + * `width` is _NEVER_ of IMMEDIATE type. + */ +void gen_rdeposit_op(Context *c, + YYLTYPE *locp, + HexValue *dst, + HexValue *value, + HexValue *begin, + HexValue *width) +{ + /* + * Otherwise if the width is not known, we fallback on reimplementing + * deposit in TCG. + */ + HexValue begin_m = *begin; + HexValue value_m = *value; + HexValue width_m = *width; + const char *mask_str = (dst->bit_width == 32) + ? "0xffffffffUL" + : "0xffffffffffffffffUL"; + HexValue mask = gen_constant(c, locp, mask_str, dst->bit_width, + UNSIGNED); + const char *dst_width_str = (dst->bit_width == 32) ? "32" : "64"; + HexValue k64 = gen_constant(c, locp, dst_width_str, dst->bit_width, + UNSIGNED); + HexValue res; + HexValue zero; + + assert(dst->bit_width >= value->bit_width); + assert(begin->type == IMMEDIATE && begin->imm.type == VALUE); + assert(dst->type == REGISTER_ARG); + + yyassert(c, locp, width->type != IMMEDIATE, + "Immediate index to rdeposit not handled!"); + + yyassert(c, locp, value_m.bit_width == dst->bit_width && + begin_m.bit_width == dst->bit_width && + width_m.bit_width == dst->bit_width, + "Extension/truncation should be taken care of" + " before rdeposit!"); + + width_m = rvalue_materialize(c, locp, &width_m); + + /* + * mask = 0xffffffffffffffff >> (64 - width) + * mask = mask << begin + * value = (value << begin) & mask + * res = dst & ~mask + * res = res | value + * dst = (width != 0) ? res : dst + */ + k64 = gen_bin_op(c, locp, SUB_OP, &k64, &width_m); + mask = gen_bin_op(c, locp, LSR_OP, &mask, &k64); + mask = gen_bin_op(c, locp, ASL_OP, &mask, &begin_m); + value_m = gen_bin_op(c, locp, ASL_OP, &value_m, &begin_m); + value_m = gen_bin_op(c, locp, ANDB_OP, &value_m, &mask); + + OUT(c, locp, "tcg_gen_not_i", &dst->bit_width, "(", &mask, ", ", + &mask, ");\n"); + res = gen_bin_op(c, locp, ANDB_OP, dst, &mask); + res = gen_bin_op(c, locp, ORB_OP, &res, &value_m); + + /* + * We don't need to truncate `res` here, since all operations involved use + * the same bit width. + */ + + /* If the width is zero, then return the identity dst = dst */ + zero = gen_constant(c, locp, "0", res.bit_width, UNSIGNED); + OUT(c, locp, "tcg_gen_movcond_i", &res.bit_width, "(TCG_COND_NE, ", + dst); + OUT(c, locp, ", ", &width_m, ", ", &zero, ", ", &res, ", ", dst, + ");\n"); +} + +void gen_deposit_op(Context *c, + YYLTYPE *locp, + HexValue *dst, + HexValue *value, + HexValue *index, + HexCast *cast) +{ + HexValue value_m = *value; + unsigned bit_width = (dst->bit_width == 64) ? 64 : 32; + unsigned width = cast->bit_width; + + yyassert(c, locp, index->type == IMMEDIATE, + "Deposit index must be immediate!\n"); + + /* + * Using tcg_gen_deposit_i**(dst, dst, ...) requires dst to be + * initialized. + */ + gen_inst_init_args(c, locp); + + /* If the destination value is 32, truncate the value, otherwise extend */ + if (dst->bit_width != value->bit_width) { + if (bit_width == 32) { + value_m = gen_rvalue_truncate(c, locp, &value_m); + } else { + value_m = gen_rvalue_extend(c, locp, &value_m); + } + } + value_m = rvalue_materialize(c, locp, &value_m); + OUT(c, locp, "tcg_gen_deposit_i", &bit_width, "(", dst, ", ", dst, ", "); + OUT(c, locp, &value_m, ", ", index, " * ", &width, ", ", &width, ");\n"); +} + +HexValue gen_rextract_op(Context *c, + YYLTYPE *locp, + HexValue *src, + unsigned begin, + unsigned width) +{ + unsigned bit_width = (src->bit_width == 64) ? 64 : 32; + HexValue res = gen_tmp(c, locp, bit_width, UNSIGNED); + OUT(c, locp, "tcg_gen_extract_i", &bit_width, "(", &res); + OUT(c, locp, ", ", src, ", ", &begin, ", ", &width, ");\n"); + return res; +} + +HexValue gen_extract_op(Context *c, + YYLTYPE *locp, + HexValue *src, + HexValue *index, + HexExtract *extract) +{ + unsigned bit_width = (src->bit_width == 64) ? 64 : 32; + unsigned width = extract->bit_width; + const char *sign_prefix; + HexValue res; + + yyassert(c, locp, index->type == IMMEDIATE, + "Extract index must be immediate!\n"); + assert_signedness(c, locp, extract->signedness); + + sign_prefix = (extract->signedness == UNSIGNED) ? "" : "s"; + res = gen_tmp(c, locp, bit_width, extract->signedness); + + OUT(c, locp, "tcg_gen_", sign_prefix, "extract_i", &bit_width, + "(", &res, ", ", src); + OUT(c, locp, ", ", index, " * ", &width, ", ", &width, ");\n"); + + /* Some extract operations have bit_width != storage_bit_width */ + if (extract->storage_bit_width > bit_width) { + HexValue tmp = gen_tmp(c, locp, extract->storage_bit_width, + extract->signedness); + const char *sign_suffix = (extract->signedness == UNSIGNED) ? "u" : ""; + OUT(c, locp, "tcg_gen_ext", sign_suffix, "_i32_i64(", + &tmp, ", ", &res, ");\n"); + res = tmp; + } + return res; +} + +void gen_write_reg(Context *c, YYLTYPE *locp, HexValue *reg, HexValue *value) +{ + HexValue value_m = *value; + yyassert(c, locp, reg->type == REGISTER, "reg must be a register!"); + value_m = gen_rvalue_truncate(c, locp, &value_m); + value_m = rvalue_materialize(c, locp, &value_m); + OUT(c, + locp, + "gen_log_reg_write(ctx, ", ®->reg.id, ", ", + &value_m, ");\n"); +} + +void gen_assign(Context *c, + YYLTYPE *locp, + HexValue *dst, + HexValue *value) +{ + HexValue value_m = *value; + unsigned bit_width; + + yyassert(c, locp, !is_inside_ternary(c), + "Assign in ternary not allowed!"); + + if (dst->type == REGISTER) { + gen_write_reg(c, locp, dst, &value_m); + return; + } + + if (dst->type == VARID) { + find_variable(c, locp, dst, dst); + } + bit_width = dst->bit_width == 64 ? 64 : 32; + + if (bit_width != value_m.bit_width) { + if (bit_width == 64) { + value_m = gen_rvalue_extend(c, locp, &value_m); + } else { + value_m = gen_rvalue_truncate(c, locp, &value_m); + } + } + + const char *imm_suffix = (value_m.type == IMMEDIATE) ? "i" : ""; + OUT(c, locp, "tcg_gen_mov", imm_suffix, "_i", &bit_width, + "(", dst, ", ", &value_m, ");\n"); +} + +HexValue gen_convround(Context *c, + YYLTYPE *locp, + HexValue *src) +{ + HexValue src_m = *src; + unsigned bit_width = src_m.bit_width; + const char *size = (bit_width == 32) ? "32" : "64"; + HexValue res = gen_tmp(c, locp, bit_width, src->signedness); + HexValue mask = gen_constant(c, locp, "0x3", bit_width, UNSIGNED); + HexValue one = gen_constant(c, locp, "1", bit_width, UNSIGNED); + HexValue and; + HexValue src_p1; + + and = gen_bin_op(c, locp, ANDB_OP, &src_m, &mask); + src_p1 = gen_bin_op(c, locp, ADD_OP, &src_m, &one); + + OUT(c, locp, "tcg_gen_movcond_i", size, "(TCG_COND_EQ, ", &res); + OUT(c, locp, ", ", &and, ", ", &mask, ", "); + OUT(c, locp, &src_p1, ", ", &src_m, ");\n"); + + return res; +} + +static HexValue gen_convround_n_b(Context *c, + YYLTYPE *locp, + HexValue *a, + HexValue *n) +{ + HexValue one = gen_constant(c, locp, "1", 32, UNSIGNED); + HexValue res = gen_tmp(c, locp, 64, UNSIGNED); + HexValue tmp = gen_tmp(c, locp, 32, UNSIGNED); + HexValue tmp_64 = gen_tmp(c, locp, 64, UNSIGNED); + + assert(n->type != IMMEDIATE); + OUT(c, locp, "tcg_gen_ext_i32_i64(", &res, ", ", a, ");\n"); + OUT(c, locp, "tcg_gen_shl_i32(", &tmp); + OUT(c, locp, ", ", &one, ", ", n, ");\n"); + OUT(c, locp, "tcg_gen_and_i32(", &tmp); + OUT(c, locp, ", ", &tmp, ", ", a, ");\n"); + OUT(c, locp, "tcg_gen_shri_i32(", &tmp); + OUT(c, locp, ", ", &tmp, ", 1);\n"); + OUT(c, locp, "tcg_gen_ext_i32_i64(", &tmp_64, ", ", &tmp, ");\n"); + OUT(c, locp, "tcg_gen_add_i64(", &res); + OUT(c, locp, ", ", &res, ", ", &tmp_64, ");\n"); + + return res; +} + +static HexValue gen_convround_n_c(Context *c, + YYLTYPE *locp, + HexValue *a, + HexValue *n) +{ + HexValue res = gen_tmp(c, locp, 64, UNSIGNED); + HexValue one = gen_constant(c, locp, "1", 32, UNSIGNED); + HexValue tmp = gen_tmp(c, locp, 32, UNSIGNED); + HexValue tmp_64 = gen_tmp(c, locp, 64, UNSIGNED); + + OUT(c, locp, "tcg_gen_ext_i32_i64(", &res, ", ", a, ");\n"); + OUT(c, locp, "tcg_gen_subi_i32(", &tmp); + OUT(c, locp, ", ", n, ", 1);\n"); + OUT(c, locp, "tcg_gen_shl_i32(", &tmp); + OUT(c, locp, ", ", &one, ", ", &tmp, ");\n"); + OUT(c, locp, "tcg_gen_ext_i32_i64(", &tmp_64, ", ", &tmp, ");\n"); + OUT(c, locp, "tcg_gen_add_i64(", &res); + OUT(c, locp, ", ", &res, ", ", &tmp_64, ");\n"); + + return res; +} + +HexValue gen_convround_n(Context *c, + YYLTYPE *locp, + HexValue *src, + HexValue *pos) +{ + HexValue zero = gen_constant(c, locp, "0", 64, UNSIGNED); + HexValue l_32 = gen_constant(c, locp, "1", 32, UNSIGNED); + HexValue cond = gen_tmp(c, locp, 32, UNSIGNED); + HexValue cond_64 = gen_tmp(c, locp, 64, UNSIGNED); + HexValue mask = gen_tmp(c, locp, 32, UNSIGNED); + HexValue n_64 = gen_tmp(c, locp, 64, UNSIGNED); + HexValue res = gen_tmp(c, locp, 64, UNSIGNED); + /* If input is 64 bit cast it to 32 */ + HexValue src_casted = gen_cast_op(c, locp, src, 32, src->signedness); + HexValue pos_casted = gen_cast_op(c, locp, pos, 32, pos->signedness); + HexValue r1; + HexValue r2; + HexValue r3; + + src_casted = rvalue_materialize(c, locp, &src_casted); + pos_casted = rvalue_materialize(c, locp, &pos_casted); + + /* + * r1, r2, and r3 represent the results of three different branches. + * - r1 picked if pos_casted == 0 + * - r2 picked if (src_casted & ((1 << (pos_casted - 1)) - 1)) == 0), + * that is if bits 0, ..., pos_casted-1 are all 0. + * - r3 picked otherwise. + */ + r1 = gen_rvalue_extend(c, locp, &src_casted); + r2 = gen_convround_n_b(c, locp, &src_casted, &pos_casted); + r3 = gen_convround_n_c(c, locp, &src_casted, &pos_casted); + + /* + * Calculate the condition + * (src_casted & ((1 << (pos_casted - 1)) - 1)) == 0), + * which checks if the bits 0,...,pos-1 are all 0. + */ + OUT(c, locp, "tcg_gen_sub_i32(", &mask); + OUT(c, locp, ", ", &pos_casted, ", ", &l_32, ");\n"); + OUT(c, locp, "tcg_gen_shl_i32(", &mask); + OUT(c, locp, ", ", &l_32, ", ", &mask, ");\n"); + OUT(c, locp, "tcg_gen_sub_i32(", &mask); + OUT(c, locp, ", ", &mask, ", ", &l_32, ");\n"); + OUT(c, locp, "tcg_gen_and_i32(", &cond); + OUT(c, locp, ", ", &src_casted, ", ", &mask, ");\n"); + OUT(c, locp, "tcg_gen_extu_i32_i64(", &cond_64, ", ", &cond, ");\n"); + + OUT(c, locp, "tcg_gen_ext_i32_i64(", &n_64, ", ", &pos_casted, ");\n"); + + /* + * if the bits 0, ..., pos_casted-1 are all 0, then pick r2 otherwise, + * pick r3. + */ + OUT(c, locp, "tcg_gen_movcond_i64"); + OUT(c, locp, "(TCG_COND_EQ, ", &res, ", ", &cond_64, ", ", &zero); + OUT(c, locp, ", ", &r2, ", ", &r3, ");\n"); + + /* Lastly, if the pos_casted == 0, then pick r1 */ + OUT(c, locp, "tcg_gen_movcond_i64"); + OUT(c, locp, "(TCG_COND_EQ, ", &res, ", ", &n_64, ", ", &zero); + OUT(c, locp, ", ", &r1, ", ", &res, ");\n"); + + /* Finally shift back val >>= n */ + OUT(c, locp, "tcg_gen_shr_i64(", &res); + OUT(c, locp, ", ", &res, ", ", &n_64, ");\n"); + + res = gen_rvalue_truncate(c, locp, &res); + return res; +} + +HexValue gen_round(Context *c, + YYLTYPE *locp, + HexValue *src, + HexValue *pos) +{ + HexValue zero = gen_constant(c, locp, "0", 64, UNSIGNED); + HexValue one = gen_constant(c, locp, "1", 64, UNSIGNED); + HexValue res; + HexValue n_m1; + HexValue shifted; + HexValue sum; + HexValue src_width; + HexValue a; + HexValue b; + + assert_signedness(c, locp, src->signedness); + yyassert(c, locp, src->bit_width <= 32, + "fRNDN not implemented for bit widths > 32!"); + + res = gen_tmp(c, locp, 64, src->signedness); + + src_width = gen_imm_value(c, locp, src->bit_width, 32, UNSIGNED); + a = gen_extend_op(c, locp, &src_width, 64, src, SIGNED); + a = rvalue_materialize(c, locp, &a); + + src_width = gen_imm_value(c, locp, 5, 32, UNSIGNED); + b = gen_extend_op(c, locp, &src_width, 64, pos, UNSIGNED); + b = rvalue_materialize(c, locp, &b); + + n_m1 = gen_bin_op(c, locp, SUB_OP, &b, &one); + shifted = gen_bin_op(c, locp, ASL_OP, &one, &n_m1); + sum = gen_bin_op(c, locp, ADD_OP, &shifted, &a); + + OUT(c, locp, "tcg_gen_movcond_i64"); + OUT(c, locp, "(TCG_COND_EQ, ", &res, ", ", &b, ", ", &zero); + OUT(c, locp, ", ", &a, ", ", &sum, ");\n"); + + return res; +} + +/* Circular addressing mode with auto-increment */ +void gen_circ_op(Context *c, + YYLTYPE *locp, + HexValue *addr, + HexValue *increment, + HexValue *modifier) +{ + HexValue increment_m = *increment; + increment_m = rvalue_materialize(c, locp, &increment_m); + OUT(c, + locp, + "gen_helper_fcircadd(", + addr, + ", ", + addr, + ", ", + &increment_m, + ", ", + modifier); + OUT(c, locp, ", CS);\n"); +} + +HexValue gen_locnt_op(Context *c, YYLTYPE *locp, HexValue *src) +{ + const char *bit_suffix = src->bit_width == 64 ? "64" : "32"; + HexValue src_m = *src; + HexValue res; + + assert_signedness(c, locp, src->signedness); + res = gen_tmp(c, locp, src->bit_width == 64 ? 64 : 32, src->signedness); + src_m = rvalue_materialize(c, locp, &src_m); + OUT(c, locp, "tcg_gen_not_i", bit_suffix, "(", + &res, ", ", &src_m, ");\n"); + OUT(c, locp, "tcg_gen_clzi_i", bit_suffix, "(", &res, ", ", &res, ", "); + OUT(c, locp, bit_suffix, ");\n"); + return res; +} + +HexValue gen_ctpop_op(Context *c, YYLTYPE *locp, HexValue *src) +{ + const char *bit_suffix = src->bit_width == 64 ? "64" : "32"; + HexValue src_m = *src; + HexValue res; + assert_signedness(c, locp, src->signedness); + res = gen_tmp(c, locp, src->bit_width == 64 ? 64 : 32, src->signedness); + src_m = rvalue_materialize(c, locp, &src_m); + OUT(c, locp, "tcg_gen_ctpop_i", bit_suffix, + "(", &res, ", ", &src_m, ");\n"); + return res; +} + +HexValue gen_rotl(Context *c, YYLTYPE *locp, HexValue *src, HexValue *width) +{ + const char *suffix = src->bit_width == 64 ? "i64" : "i32"; + HexValue amount = *width; + HexValue res; + assert_signedness(c, locp, src->signedness); + res = gen_tmp(c, locp, src->bit_width, src->signedness); + if (amount.bit_width < src->bit_width) { + amount = gen_rvalue_extend(c, locp, &amount); + } else { + amount = gen_rvalue_truncate(c, locp, &amount); + } + amount = rvalue_materialize(c, locp, &amount); + OUT(c, locp, "tcg_gen_rotl_", suffix, "(", + &res, ", ", src, ", ", &amount, ");\n"); + + return res; +} + +HexValue gen_carry_from_add(Context *c, + YYLTYPE *locp, + HexValue *op1, + HexValue *op2, + HexValue *op3) +{ + HexValue zero = gen_constant(c, locp, "0", 64, UNSIGNED); + HexValue res = gen_tmp(c, locp, 64, UNSIGNED); + HexValue cf = gen_tmp(c, locp, 64, UNSIGNED); + HexValue op1_m = rvalue_materialize(c, locp, op1); + HexValue op2_m = rvalue_materialize(c, locp, op2); + HexValue op3_m = rvalue_materialize(c, locp, op3); + op3_m = gen_rvalue_extend(c, locp, &op3_m); + + OUT(c, locp, "tcg_gen_add2_i64(", &res, ", ", &cf, ", ", &op1_m, ", ", + &zero); + OUT(c, locp, ", ", &op3_m, ", ", &zero, ");\n"); + OUT(c, locp, "tcg_gen_add2_i64(", &res, ", ", &cf, ", ", &res, ", ", &cf); + OUT(c, locp, ", ", &op2_m, ", ", &zero, ");\n"); + + return cf; +} + +void gen_addsat64(Context *c, + YYLTYPE *locp, + HexValue *dst, + HexValue *op1, + HexValue *op2) +{ + HexValue op1_m = rvalue_materialize(c, locp, op1); + HexValue op2_m = rvalue_materialize(c, locp, op2); + OUT(c, locp, "gen_add_sat_i64(ctx, ", dst, ", ", &op1_m, ", ", + &op2_m, ");\n"); +} + +void gen_inst(Context *c, GString *iname) +{ + c->total_insn++; + c->inst.name = iname; + c->inst.allocated = g_array_new(FALSE, FALSE, sizeof(Var)); + c->inst.init_list = g_array_new(FALSE, FALSE, sizeof(HexValue)); + c->inst.strings = g_array_new(FALSE, FALSE, sizeof(GString *)); + EMIT_SIG(c, "void emit_%s(DisasContext *ctx, Insn *insn, Packet *pkt", + c->inst.name->str); +} + + +/* + * Initialize declared but uninitialized registers, but only for + * non-conditional instructions + */ +void gen_inst_init_args(Context *c, YYLTYPE *locp) +{ + if (!c->inst.init_list) { + return; + } + + for (unsigned i = 0; i < c->inst.init_list->len; i++) { + HexValue *val = &g_array_index(c->inst.init_list, HexValue, i); + if (val->type == REGISTER_ARG) { + /* Nothing to do here */ + } else if (val->type == PREDICATE) { + char suffix = val->is_dotnew ? 'N' : 'V'; + EMIT_HEAD(c, "tcg_gen_movi_i%u(P%c%c, 0);\n", val->bit_width, + val->pred.id, suffix); + } else { + yyassert(c, locp, false, "Invalid arg type!"); + } + } + + /* Free argument init list once we have initialized everything */ + g_array_free(c->inst.init_list, TRUE); + c->inst.init_list = NULL; +} + +void gen_inst_code(Context *c, YYLTYPE *locp) +{ + if (c->inst.error_count != 0) { + fprintf(stderr, + "Parsing of instruction %s generated %d errors!\n", + c->inst.name->str, + c->inst.error_count); + } else { + c->implemented_insn++; + fprintf(c->enabled_file, "%s\n", c->inst.name->str); + emit_footer(c); + commit(c); + } + free_instruction(c); +} + +void gen_pred_assign(Context *c, YYLTYPE *locp, HexValue *left_pred, + HexValue *right_pred) +{ + char pred_id[2] = {left_pred->pred.id, 0}; + bool is_direct = is_direct_predicate(left_pred); + HexValue r = rvalue_materialize(c, locp, right_pred); + r = gen_rvalue_truncate(c, locp, &r); + yyassert(c, locp, !is_inside_ternary(c), + "Predicate assign not allowed in ternary!"); + /* Extract predicate TCGv */ + if (is_direct) { + *left_pred = gen_tmp(c, locp, 32, UNSIGNED); + } + /* Extract first 8 bits, and store new predicate value */ + OUT(c, locp, "tcg_gen_andi_i32(", left_pred, ", ", &r, ", 0xff);\n"); + if (is_direct) { + OUT(c, locp, "gen_log_pred_write(ctx, ", pred_id, ", ", left_pred, + ");\n"); + } +} + +void gen_cancel(Context *c, YYLTYPE *locp) +{ + OUT(c, locp, "gen_cancel(insn->slot);\n"); +} + +void gen_load_cancel(Context *c, YYLTYPE *locp) +{ + OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_store_s1) {\n"); + OUT(c, locp, "ctx->s1_store_processed = false;\n"); + OUT(c, locp, "process_store(ctx, 1);\n"); + OUT(c, locp, "}\n"); +} + +void gen_load(Context *c, YYLTYPE *locp, HexValue *width, + HexSignedness signedness, HexValue *ea, HexValue *dst) +{ + unsigned dst_bit_width; + unsigned src_bit_width; + + /* Memop width is specified in the load macro */ + assert_signedness(c, locp, signedness); + + /* If dst is a variable, assert that is declared and load the type info */ + if (dst->type == VARID) { + find_variable(c, locp, dst, dst); + } + + src_bit_width = width->imm.value * 8; + dst_bit_width = MAX(dst->bit_width, 32); + + /* Lookup the effective address EA */ + find_variable(c, locp, ea, ea); + OUT(c, locp, "if (insn->slot == 0 && pkt->pkt_has_store_s1) {\n"); + OUT(c, locp, "probe_noshuf_load(", ea, ", ", width, ", ctx->mem_idx);\n"); + OUT(c, locp, "process_store(ctx, 1);\n"); + OUT(c, locp, "}\n"); + + OUT(c, locp, "tcg_gen_qemu_ld_i", &dst_bit_width); + OUT(c, locp, "("); + OUT(c, locp, dst, ", ", ea, ", ctx->mem_idx, MO_", &src_bit_width); + if (signedness == SIGNED) { + OUT(c, locp, " | MO_SIGN"); + } + OUT(c, locp, " | MO_TE);\n"); +} + +void gen_store(Context *c, YYLTYPE *locp, HexValue *width, HexValue *ea, + HexValue *src) +{ + HexValue src_m = *src; + /* Memop width is specified in the store macro */ + unsigned mem_width = width->imm.value; + /* Lookup the effective address EA */ + find_variable(c, locp, ea, ea); + src_m = rvalue_materialize(c, locp, &src_m); + OUT(c, locp, "gen_store", &mem_width, "(tcg_env, ", ea, ", ", &src_m); + OUT(c, locp, ", insn->slot);\n"); +} + +void gen_sethalf(Context *c, YYLTYPE *locp, HexCast *sh, HexValue *n, + HexValue *dst, HexValue *value) +{ + yyassert(c, locp, n->type == IMMEDIATE, + "Deposit index must be immediate!\n"); + if (dst->type == VARID) { + find_variable(c, locp, dst, dst); + } + + gen_deposit_op(c, locp, dst, value, n, sh); +} + +void gen_setbits(Context *c, YYLTYPE *locp, HexValue *hi, HexValue *lo, + HexValue *dst, HexValue *value) +{ + unsigned len; + HexValue tmp; + + yyassert(c, locp, hi->type == IMMEDIATE && + hi->imm.type == VALUE && + lo->type == IMMEDIATE && + lo->imm.type == VALUE, + "Range deposit needs immediate values!\n"); + + *value = gen_rvalue_truncate(c, locp, value); + len = hi->imm.value + 1 - lo->imm.value; + tmp = gen_tmp(c, locp, 32, value->signedness); + /* Emit an `and` to ensure `value` is either 0 or 1. */ + OUT(c, locp, "tcg_gen_andi_i32(", &tmp, ", ", value, ", 1);\n"); + /* Use `neg` to map 0 -> 0 and 1 -> 0xffff... */ + OUT(c, locp, "tcg_gen_neg_i32(", &tmp, ", ", &tmp, ");\n"); + OUT(c, locp, "tcg_gen_deposit_i32(", dst, ", ", dst, + ", ", &tmp, ", "); + OUT(c, locp, lo, ", ", &len, ");\n"); +} + +unsigned gen_if_cond(Context *c, YYLTYPE *locp, HexValue *cond) +{ + const char *bit_suffix; + /* Generate an end label, if false branch to that label */ + OUT(c, locp, "TCGLabel *if_label_", &c->inst.if_count, + " = gen_new_label();\n"); + *cond = rvalue_materialize(c, locp, cond); + bit_suffix = (cond->bit_width == 64) ? "i64" : "i32"; + OUT(c, locp, "tcg_gen_brcondi_", bit_suffix, "(TCG_COND_EQ, ", cond, + ", 0, if_label_", &c->inst.if_count, ");\n"); + return c->inst.if_count++; +} + +unsigned gen_if_else(Context *c, YYLTYPE *locp, unsigned index) +{ + unsigned if_index = c->inst.if_count++; + /* Generate label to jump if else is not verified */ + OUT(c, locp, "TCGLabel *if_label_", &if_index, + " = gen_new_label();\n"); + /* Jump out of the else statement */ + OUT(c, locp, "tcg_gen_br(if_label_", &if_index, ");\n"); + /* Fix the else label */ + OUT(c, locp, "gen_set_label(if_label_", &index, ");\n"); + return if_index; +} + +HexValue gen_rvalue_pred(Context *c, YYLTYPE *locp, HexValue *pred) +{ + /* Predicted instructions need to zero out result args */ + gen_inst_init_args(c, locp); + + if (is_direct_predicate(pred)) { + bool is_dotnew = pred->is_dotnew; + char predicate_id[2] = { pred->pred.id, '\0' }; + char *pred_str = (char *) &predicate_id; + *pred = gen_tmp(c, locp, 32, UNSIGNED); + if (is_dotnew) { + OUT(c, locp, "tcg_gen_mov_i32(", pred, + ", ctx->new_pred_value["); + OUT(c, locp, pred_str, "]);\n"); + } else { + OUT(c, locp, "gen_read_preg(", pred, ", ", pred_str, ");\n"); + } + } + + return *pred; +} + +HexValue gen_rvalue_var(Context *c, YYLTYPE *locp, HexValue *var) +{ + find_variable(c, locp, var, var); + return *var; +} + +HexValue gen_rvalue_mpy(Context *c, YYLTYPE *locp, HexMpy *mpy, + HexValue *op1, HexValue *op2) +{ + HexValue res; + memset(&res, 0, sizeof(HexValue)); + + assert_signedness(c, locp, mpy->first_signedness); + assert_signedness(c, locp, mpy->second_signedness); + + *op1 = gen_cast_op(c, locp, op1, mpy->first_bit_width * 2, + mpy->first_signedness); + /* Handle fMPTY3216.. */ + if (mpy->first_bit_width == 32) { + *op2 = gen_cast_op(c, locp, op2, 64, mpy->second_signedness); + } else { + *op2 = gen_cast_op(c, locp, op2, mpy->second_bit_width * 2, + mpy->second_signedness); + } + res = gen_bin_op(c, locp, MUL_OP, op1, op2); + /* Handle special cases required by the language */ + if (mpy->first_bit_width == 16 && mpy->second_bit_width == 16) { + HexValue src_width = gen_imm_value(c, locp, 32, 32, UNSIGNED); + HexSignedness signedness = bin_op_signedness(c, locp, + mpy->first_signedness, + mpy->second_signedness); + res = gen_extend_op(c, locp, &src_width, 64, &res, + signedness); + } + return res; +} + +static inline HexValue gen_rvalue_simple_unary(Context *c, YYLTYPE *locp, + HexValue *value, + const char *c_code, + const char *tcg_code) +{ + unsigned bit_width = (value->bit_width == 64) ? 64 : 32; + HexValue res; + if (value->type == IMMEDIATE) { + res = gen_imm_qemu_tmp(c, locp, bit_width, value->signedness); + gen_c_int_type(c, locp, value->bit_width, value->signedness); + OUT(c, locp, " ", &res, " = ", c_code, "(", value, ");\n"); + } else { + res = gen_tmp(c, locp, bit_width, value->signedness); + OUT(c, locp, tcg_code, "_i", &bit_width, "(", &res, ", ", value, + ");\n"); + } + return res; +} + + +HexValue gen_rvalue_not(Context *c, YYLTYPE *locp, HexValue *value) +{ + return gen_rvalue_simple_unary(c, locp, value, "~", "tcg_gen_not"); +} + +HexValue gen_rvalue_notl(Context *c, YYLTYPE *locp, HexValue *value) +{ + unsigned bit_width = (value->bit_width == 64) ? 64 : 32; + HexValue res; + if (value->type == IMMEDIATE) { + res = gen_imm_qemu_tmp(c, locp, bit_width, value->signedness); + gen_c_int_type(c, locp, value->bit_width, value->signedness); + OUT(c, locp, " ", &res, " = !(", value, ");\n"); + } else { + HexValue zero = gen_constant(c, locp, "0", bit_width, UNSIGNED); + HexValue one = gen_constant(c, locp, "0xff", bit_width, UNSIGNED); + res = gen_tmp(c, locp, bit_width, value->signedness); + OUT(c, locp, "tcg_gen_movcond_i", &bit_width); + OUT(c, locp, "(TCG_COND_EQ, ", &res, ", ", value, ", ", &zero); + OUT(c, locp, ", ", &one, ", ", &zero, ");\n"); + } + return res; +} + +HexValue gen_rvalue_sat(Context *c, YYLTYPE *locp, HexSat *sat, + HexValue *width, HexValue *value) +{ + const char *unsigned_str; + const char *bit_suffix = (value->bit_width == 64) ? "i64" : "i32"; + HexValue res; + HexValue ovfl; + /* + * Note: all saturates are assumed to implicitly set overflow. + * This assumption holds for the instructions currently parsed + * by idef-parser. + */ + yyassert(c, locp, width->imm.value < value->bit_width, + "To compute overflow, source width must be greater than" + " saturation width!"); + yyassert(c, locp, !is_inside_ternary(c), + "Saturating from within a ternary is not allowed!"); + assert_signedness(c, locp, sat->signedness); + + unsigned_str = (sat->signedness == UNSIGNED) ? "u" : ""; + res = gen_tmp(c, locp, value->bit_width, sat->signedness); + ovfl = gen_tmp(c, locp, 32, sat->signedness); + OUT(c, locp, "gen_sat", unsigned_str, "_", bit_suffix, "_ovfl("); + OUT(c, locp, &ovfl, ", ", &res, ", ", value, ", ", &width->imm.value, + ");\n"); + OUT(c, locp, "gen_set_usr_field_if(ctx, USR_OVF,", &ovfl, ");\n"); + + return res; +} + +HexValue gen_rvalue_fscr(Context *c, YYLTYPE *locp, HexValue *value) +{ + HexValue key = gen_tmp(c, locp, 64, UNSIGNED); + HexValue res = gen_tmp(c, locp, 64, UNSIGNED); + HexValue frame_key = gen_tmp(c, locp, 32, UNSIGNED); + *value = gen_rvalue_extend(c, locp, value); + OUT(c, locp, "gen_read_reg(", &frame_key, ", HEX_REG_FRAMEKEY);\n"); + OUT(c, locp, "tcg_gen_concat_i32_i64(", + &key, ", ", &frame_key, ", ", &frame_key, ");\n"); + OUT(c, locp, "tcg_gen_xor_i64(", &res, ", ", value, ", ", &key, ");\n"); + return res; +} + +HexValue gen_rvalue_abs(Context *c, YYLTYPE *locp, HexValue *value) +{ + return gen_rvalue_simple_unary(c, locp, value, "abs", "tcg_gen_abs"); +} + +HexValue gen_rvalue_neg(Context *c, YYLTYPE *locp, HexValue *value) +{ + return gen_rvalue_simple_unary(c, locp, value, "-", "tcg_gen_neg"); +} + +HexValue gen_rvalue_brev(Context *c, YYLTYPE *locp, HexValue *value) +{ + HexValue res; + yyassert(c, locp, value->bit_width <= 32, + "fbrev not implemented for 64-bit integers!"); + res = gen_tmp(c, locp, value->bit_width, value->signedness); + *value = rvalue_materialize(c, locp, value); + OUT(c, locp, "gen_helper_fbrev(", &res, ", ", value, ");\n"); + return res; +} + +HexValue gen_rvalue_ternary(Context *c, YYLTYPE *locp, HexValue *cond, + HexValue *true_branch, HexValue *false_branch) +{ + bool is_64bit = (true_branch->bit_width == 64) || + (false_branch->bit_width == 64); + unsigned bit_width = (is_64bit) ? 64 : 32; + HexValue zero = gen_constant(c, locp, "0", bit_width, UNSIGNED); + HexValue res = gen_tmp(c, locp, bit_width, UNSIGNED); + + if (is_64bit) { + *cond = gen_rvalue_extend(c, locp, cond); + *true_branch = gen_rvalue_extend(c, locp, true_branch); + *false_branch = gen_rvalue_extend(c, locp, false_branch); + } else { + *cond = gen_rvalue_truncate(c, locp, cond); + } + *cond = rvalue_materialize(c, locp, cond); + *true_branch = rvalue_materialize(c, locp, true_branch); + *false_branch = rvalue_materialize(c, locp, false_branch); + + OUT(c, locp, "tcg_gen_movcond_i", &bit_width); + OUT(c, locp, "(TCG_COND_NE, ", &res, ", ", cond, ", ", &zero); + OUT(c, locp, ", ", true_branch, ", ", false_branch, ");\n"); + + assert(c->ternary->len > 0); + g_array_remove_index(c->ternary, c->ternary->len - 1); + + return res; +} + +const char *cond_to_str(TCGCond cond) +{ + switch (cond) { + case TCG_COND_NEVER: + return "TCG_COND_NEVER"; + case TCG_COND_ALWAYS: + return "TCG_COND_ALWAYS"; + case TCG_COND_EQ: + return "TCG_COND_EQ"; + case TCG_COND_NE: + return "TCG_COND_NE"; + case TCG_COND_LT: + return "TCG_COND_LT"; + case TCG_COND_GE: + return "TCG_COND_GE"; + case TCG_COND_LE: + return "TCG_COND_LE"; + case TCG_COND_GT: + return "TCG_COND_GT"; + case TCG_COND_LTU: + return "TCG_COND_LTU"; + case TCG_COND_GEU: + return "TCG_COND_GEU"; + case TCG_COND_LEU: + return "TCG_COND_LEU"; + case TCG_COND_GTU: + return "TCG_COND_GTU"; + default: + abort(); + } +} + +void emit_arg(Context *c, YYLTYPE *locp, HexValue *arg) +{ + switch (arg->type) { + case REGISTER_ARG: + if (arg->reg.type == DOTNEW) { + EMIT_SIG(c, ", TCGv N%cN", arg->reg.id); + } else { + bool is64 = (arg->bit_width == 64); + const char *type = is64 ? "TCGv_i64" : "TCGv_i32"; + char reg_id[5]; + reg_compose(c, locp, &(arg->reg), reg_id); + EMIT_SIG(c, ", %s %s", type, reg_id); + /* MuV register requires also CS for circular addressing*/ + if (arg->reg.type == MODIFIER) { + EMIT_SIG(c, ", TCGv CS"); + } + } + break; + case PREDICATE: + { + char suffix = arg->is_dotnew ? 'N' : 'V'; + EMIT_SIG(c, ", TCGv P%c%c", arg->pred.id, suffix); + } + break; + default: + { + fprintf(stderr, "emit_arg got unsupported argument!"); + abort(); + } + } +} + +void emit_footer(Context *c) +{ + EMIT(c, "}\n"); + EMIT(c, "\n"); +} + +void track_string(Context *c, GString *s) +{ + g_array_append_val(c->inst.strings, s); +} + +void free_instruction(Context *c) +{ + assert(!is_inside_ternary(c)); + /* Free the strings */ + g_string_truncate(c->signature_str, 0); + g_string_truncate(c->out_str, 0); + g_string_truncate(c->header_str, 0); + /* Free strings allocated by the instruction */ + for (unsigned i = 0; i < c->inst.strings->len; i++) { + g_string_free(g_array_index(c->inst.strings, GString*, i), TRUE); + } + g_array_free(c->inst.strings, TRUE); + /* Free INAME token value */ + g_string_free(c->inst.name, TRUE); + /* Free variables and registers */ + g_array_free(c->inst.allocated, TRUE); + /* Initialize instruction-specific portion of the context */ + memset(&(c->inst), 0, sizeof(Inst)); +} + +void assert_signedness(Context *c, + YYLTYPE *locp, + HexSignedness signedness) +{ + yyassert(c, locp, + signedness != UNKNOWN_SIGNEDNESS, + "Unspecified signedness"); +} diff --git a/target/hexagon/idef-parser/parser-helpers.h b/target/hexagon/idef-parser/parser-helpers.h new file mode 100644 index 0000000000..7c58087169 --- /dev/null +++ b/target/hexagon/idef-parser/parser-helpers.h @@ -0,0 +1,366 @@ +/* + * Copyright(c) 2019-2022 rev.ng Labs Srl. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef PARSER_HELPERS_H +#define PARSER_HELPERS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tcg/tcg-cond.h" + +#include "idef-parser.tab.h" +#include "idef-parser.yy.h" +#include "idef-parser.h" + +/* Decomment this to disable yyasserts */ +/* #define NDEBUG */ + +#define ERR_LINE_CONTEXT 40 + +#define START_COMMENT "/" "*" +#define END_COMMENT "*" "/" + +void yyerror(YYLTYPE *locp, + yyscan_t scanner __attribute__((unused)), + Context *c, + const char *s); + +#ifndef NDEBUG +#define yyassert(context, locp, condition, msg) \ + if (!(condition)) { \ + yyerror(locp, (context)->scanner, (context), (msg)); \ + } +#endif + +bool is_direct_predicate(HexValue *value); + +bool is_inside_ternary(Context *c); + +/** + * Print functions + */ + +void str_print(Context *c, YYLTYPE *locp, const char *string); + +void uint8_print(Context *c, YYLTYPE *locp, uint8_t *num); + +void uint64_print(Context *c, YYLTYPE *locp, uint64_t *num); + +void int_print(Context *c, YYLTYPE *locp, int *num); + +void uint_print(Context *c, YYLTYPE *locp, unsigned *num); + +void tmp_print(Context *c, YYLTYPE *locp, HexTmp *tmp); + +void pred_print(Context *c, YYLTYPE *locp, HexPred *pred, bool is_dotnew); + +void reg_compose(Context *c, YYLTYPE *locp, HexReg *reg, char reg_id[5]); + +void reg_print(Context *c, YYLTYPE *locp, HexReg *reg); + +void imm_print(Context *c, YYLTYPE *locp, HexValue *rvalue); + +void var_print(Context *c, YYLTYPE *locp, HexVar *var); + +void rvalue_print(Context *c, YYLTYPE *locp, void *pointer); + +void out_assert(Context *c, YYLTYPE *locp, void *dummy); + +/** + * Copies output code buffer into stdout + */ +void commit(Context *c); + +#define OUT_IMPL(c, locp, x) \ + _Generic(*(x), \ + char: str_print, \ + uint8_t: uint8_print, \ + uint64_t: uint64_print, \ + int: int_print, \ + unsigned: uint_print, \ + HexValue: rvalue_print, \ + default: out_assert \ + )(c, locp, x); + +/* FOREACH macro */ +#define FE_1(c, locp, WHAT, X) WHAT(c, locp, X) +#define FE_2(c, locp, WHAT, X, ...) \ + WHAT(c, locp, X)FE_1(c, locp, WHAT, __VA_ARGS__) +#define FE_3(c, locp, WHAT, X, ...) \ + WHAT(c, locp, X)FE_2(c, locp, WHAT, __VA_ARGS__) +#define FE_4(c, locp, WHAT, X, ...) \ + WHAT(c, locp, X)FE_3(c, locp, WHAT, __VA_ARGS__) +#define FE_5(c, locp, WHAT, X, ...) \ + WHAT(c, locp, X)FE_4(c, locp, WHAT, __VA_ARGS__) +#define FE_6(c, locp, WHAT, X, ...) \ + WHAT(c, locp, X)FE_5(c, locp, WHAT, __VA_ARGS__) +#define FE_7(c, locp, WHAT, X, ...) \ + WHAT(c, locp, X)FE_6(c, locp, WHAT, __VA_ARGS__) +#define FE_8(c, locp, WHAT, X, ...) \ + WHAT(c, locp, X)FE_7(c, locp, WHAT, __VA_ARGS__) +#define FE_9(c, locp, WHAT, X, ...) \ + WHAT(c, locp, X)FE_8(c, locp, WHAT, __VA_ARGS__) +/* repeat as needed */ + +#define GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, NAME, ...) NAME + +#define FOR_EACH(c, locp, action, ...) \ + do { \ + GET_MACRO(__VA_ARGS__, \ + FE_9, \ + FE_8, \ + FE_7, \ + FE_6, \ + FE_5, \ + FE_4, \ + FE_3, \ + FE_2, \ + FE_1)(c, locp, action, \ + __VA_ARGS__) \ + } while (0) + +#define OUT(c, locp, ...) FOR_EACH((c), (locp), OUT_IMPL, __VA_ARGS__) + +const char *cmp_swap(Context *c, YYLTYPE *locp, const char *type); + +/** + * Temporary values creation + */ + +HexValue gen_tmp(Context *c, + YYLTYPE *locp, + unsigned bit_width, + HexSignedness signedness); + +HexValue gen_imm_value(Context *c __attribute__((unused)), + YYLTYPE *locp, + int value, + unsigned bit_width, + HexSignedness signedness); + +HexValue gen_imm_qemu_tmp(Context *c, YYLTYPE *locp, unsigned bit_width, + HexSignedness signedness); + +HexValue rvalue_materialize(Context *c, YYLTYPE *locp, HexValue *rvalue); + +HexValue gen_rvalue_extend(Context *c, YYLTYPE *locp, HexValue *rvalue); + +HexValue gen_rvalue_truncate(Context *c, YYLTYPE *locp, HexValue *rvalue); + +void gen_varid_allocate(Context *c, + YYLTYPE *locp, + HexValue *varid, + unsigned bit_width, + HexSignedness signedness); + +/** + * Code generation functions + */ + +HexValue gen_bin_cmp(Context *c, + YYLTYPE *locp, + TCGCond type, + HexValue *op1, + HexValue *op2); + +HexValue gen_bin_op(Context *c, + YYLTYPE *locp, + OpType type, + HexValue *op1, + HexValue *op2); + +HexValue gen_cast_op(Context *c, + YYLTYPE *locp, + HexValue *src, + unsigned target_width, + HexSignedness signedness); + +/** + * gen_extend_op extends a region of src_width_ptr bits stored in a + * value_ptr to the size of dst_width. Note: src_width_ptr is a + * HexValue * to handle the special case where it is unknown at + * translation time. + */ +HexValue gen_extend_op(Context *c, + YYLTYPE *locp, + HexValue *src_width, + unsigned dst_width, + HexValue *value, + HexSignedness signedness); + +void gen_rdeposit_op(Context *c, + YYLTYPE *locp, + HexValue *dst, + HexValue *value, + HexValue *begin, + HexValue *width); + +void gen_deposit_op(Context *c, + YYLTYPE *locp, + HexValue *dst, + HexValue *value, + HexValue *index, + HexCast *cast); + +HexValue gen_rextract_op(Context *c, + YYLTYPE *locp, + HexValue *src, + unsigned begin, + unsigned width); + +HexValue gen_extract_op(Context *c, + YYLTYPE *locp, + HexValue *src, + HexValue *index, + HexExtract *extract); + +HexValue gen_read_reg(Context *c, YYLTYPE *locp, HexValue *reg); + +void gen_write_reg(Context *c, YYLTYPE *locp, HexValue *reg, HexValue *value); + +void gen_assign(Context *c, + YYLTYPE *locp, + HexValue *dst, + HexValue *value); + +HexValue gen_convround(Context *c, + YYLTYPE *locp, + HexValue *src); + +HexValue gen_round(Context *c, + YYLTYPE *locp, + HexValue *src, + HexValue *position); + +HexValue gen_convround_n(Context *c, + YYLTYPE *locp, + HexValue *src, + HexValue *pos); + +/** + * Circular addressing mode with auto-increment + */ +void gen_circ_op(Context *c, + YYLTYPE *locp, + HexValue *addr, + HexValue *increment, + HexValue *modifier); + +HexValue gen_locnt_op(Context *c, YYLTYPE *locp, HexValue *src); + +HexValue gen_ctpop_op(Context *c, YYLTYPE *locp, HexValue *src); + +HexValue gen_rotl(Context *c, YYLTYPE *locp, HexValue *src, HexValue *n); + +HexValue gen_deinterleave(Context *c, YYLTYPE *locp, HexValue *mixed); + +HexValue gen_interleave(Context *c, + YYLTYPE *locp, + HexValue *odd, + HexValue *even); + +HexValue gen_carry_from_add(Context *c, + YYLTYPE *locp, + HexValue *op1, + HexValue *op2, + HexValue *op3); + +void gen_addsat64(Context *c, + YYLTYPE *locp, + HexValue *dst, + HexValue *op1, + HexValue *op2); + +void gen_inst(Context *c, GString *iname); + +void gen_inst_init_args(Context *c, YYLTYPE *locp); + +void gen_inst_code(Context *c, YYLTYPE *locp); + +void gen_pred_assign(Context *c, YYLTYPE *locp, HexValue *left_pred, + HexValue *right_pred); + +void gen_cancel(Context *c, YYLTYPE *locp); + +void gen_load_cancel(Context *c, YYLTYPE *locp); + +void gen_load(Context *c, YYLTYPE *locp, HexValue *size, + HexSignedness signedness, HexValue *ea, HexValue *dst); + +void gen_store(Context *c, YYLTYPE *locp, HexValue *size, HexValue *ea, + HexValue *src); + +void gen_sethalf(Context *c, YYLTYPE *locp, HexCast *sh, HexValue *n, + HexValue *dst, HexValue *value); + +void gen_setbits(Context *c, YYLTYPE *locp, HexValue *hi, HexValue *lo, + HexValue *dst, HexValue *value); + +unsigned gen_if_cond(Context *c, YYLTYPE *locp, HexValue *cond); + +unsigned gen_if_else(Context *c, YYLTYPE *locp, unsigned index); + +HexValue gen_rvalue_pred(Context *c, YYLTYPE *locp, HexValue *pred); + +HexValue gen_rvalue_var(Context *c, YYLTYPE *locp, HexValue *var); + +HexValue gen_rvalue_mpy(Context *c, YYLTYPE *locp, HexMpy *mpy, HexValue *op1, + HexValue *op2); + +HexValue gen_rvalue_not(Context *c, YYLTYPE *locp, HexValue *value); + +HexValue gen_rvalue_notl(Context *c, YYLTYPE *locp, HexValue *value); + +HexValue gen_rvalue_sat(Context *c, YYLTYPE *locp, HexSat *sat, HexValue *n, + HexValue *value); + +HexValue gen_rvalue_fscr(Context *c, YYLTYPE *locp, HexValue *value); + +HexValue gen_rvalue_abs(Context *c, YYLTYPE *locp, HexValue *value); + +HexValue gen_rvalue_neg(Context *c, YYLTYPE *locp, HexValue *value); + +HexValue gen_rvalue_brev(Context *c, YYLTYPE *locp, HexValue *value); + +HexValue gen_rvalue_ternary(Context *c, YYLTYPE *locp, HexValue *cond, + HexValue *true_branch, HexValue *false_branch); + +const char *cond_to_str(TCGCond cond); + +void emit_header(Context *c); + +void emit_arg(Context *c, YYLTYPE *locp, HexValue *arg); + +void emit_footer(Context *c); + +void track_string(Context *c, GString *s); + +void free_instruction(Context *c); + +void assert_signedness(Context *c, + YYLTYPE *locp, + HexSignedness signedness); + +#endif /* PARSER_HELPERS_h */ diff --git a/target/hexagon/idef-parser/prepare b/target/hexagon/idef-parser/prepare new file mode 100755 index 0000000000..cb3622d4f8 --- /dev/null +++ b/target/hexagon/idef-parser/prepare @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# +# Copyright(c) 2019-2021 rev.ng Labs Srl. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# + +set -e +set -o pipefail + +# Run the preprocessor and drop comments +cpp "$@" diff --git a/target/hexagon/imported/alu.idef b/target/hexagon/imported/alu.idef index 58477ae40a..b855676989 100644 --- a/target/hexagon/imported/alu.idef +++ b/target/hexagon/imported/alu.idef @@ -292,16 +292,16 @@ Q6INSN(A4_combineii,"Rdd32=combine(#s8,#U6)",ATTRIBS(),"Set two small immediates Q6INSN(A2_combine_hh,"Rd32=combine(Rt.H32,Rs.H32)",ATTRIBS(), -"Combine two halfs into a register", {RdV = (fGETUHALF(1,RtV)<<16) | fGETUHALF(1,RsV);}) +"Combine two halves into a register", {RdV = (fGETUHALF(1,RtV)<<16) | fGETUHALF(1,RsV);}) Q6INSN(A2_combine_hl,"Rd32=combine(Rt.H32,Rs.L32)",ATTRIBS(), -"Combine two halfs into a register", {RdV = (fGETUHALF(1,RtV)<<16) | fGETUHALF(0,RsV);}) +"Combine two halves into a register", {RdV = (fGETUHALF(1,RtV)<<16) | fGETUHALF(0,RsV);}) Q6INSN(A2_combine_lh,"Rd32=combine(Rt.L32,Rs.H32)",ATTRIBS(), -"Combine two halfs into a register", {RdV = (fGETUHALF(0,RtV)<<16) | fGETUHALF(1,RsV);}) +"Combine two halves into a register", {RdV = (fGETUHALF(0,RtV)<<16) | fGETUHALF(1,RsV);}) Q6INSN(A2_combine_ll,"Rd32=combine(Rt.L32,Rs.L32)",ATTRIBS(), -"Combine two halfs into a register", {RdV = (fGETUHALF(0,RtV)<<16) | fGETUHALF(0,RsV);}) +"Combine two halves into a register", {RdV = (fGETUHALF(0,RtV)<<16) | fGETUHALF(0,RsV);}) Q6INSN(A2_tfril,"Rx.L32=#u16",ATTRIBS(), "Set low 16-bits, leave upper 16 unchanged",{ fSETHALF(0,RxV,uiV);}) @@ -1142,9 +1142,9 @@ Q6INSN(A4_cround_rr,"Rd32=cround(Rs32,Rt32)",ATTRIBS(),"Convergent Round", {RdV tmp128 = fSHIFTR128(tmp128, SHIFT);\ DST = fCAST16S_8S(tmp128);\ } else {\ - size16s_t rndbit_128 = fCAST8S_16S((1LL << (SHIFT - 1))); \ - size16s_t src_128 = fCAST8S_16S(SRC); \ - size16s_t tmp128 = fADD128(src_128, rndbit_128);\ + rndbit_128 = fCAST8S_16S((1LL << (SHIFT - 1))); \ + src_128 = fCAST8S_16S(SRC); \ + tmp128 = fADD128(src_128, rndbit_128);\ tmp128 = fSHIFTR128(tmp128, SHIFT);\ DST = fCAST16S_8S(tmp128);\ } diff --git a/target/hexagon/imported/branch.idef b/target/hexagon/imported/branch.idef index 88f5f48cce..93e2e375a5 100644 --- a/target/hexagon/imported/branch.idef +++ b/target/hexagon/imported/branch.idef @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,6 +34,9 @@ Q6INSN(J2_jump,"jump #r22:2",ATTRIBS(A_JDIR), "direct unconditional jump", Q6INSN(J2_jumpr,"jumpr Rs32",ATTRIBS(A_JINDIR), "indirect unconditional jump", {fJUMPR(RsN,RsV,COF_TYPE_JUMPR);}) +Q6INSN(J2_jumprh,"jumprh Rs32",ATTRIBS(A_JINDIR, A_HINTED_COF), "indirect unconditional jump", +{fJUMPR(RsN,RsV,COF_TYPE_JUMPR);}) + #define OLDCOND_JUMP(TAG,OPER,OPER2,ATTRIB,DESCR,SEMANTICS) \ Q6INSN(TAG##t,"if (Pu4) "OPER":nt "OPER2,ATTRIB,DESCR,{fBRANCH_SPECULATE_STALL(fLSBOLD(PuV),,SPECULATE_NOT_TAKEN,12,0); if (fLSBOLD(PuV)) { SEMANTICS; }}) \ Q6INSN(TAG##f,"if (!Pu4) "OPER":nt "OPER2,ATTRIB,DESCR,{fBRANCH_SPECULATE_STALL(fLSBOLDNOT(PuV),,SPECULATE_NOT_TAKEN,12,0); if (fLSBOLDNOT(PuV)) { SEMANTICS; }}) \ @@ -196,6 +199,8 @@ Q6INSN(J2_callrt,"if (Pu4) callr Rs32",ATTRIBS(CINDIR_STD),"indirect conditional Q6INSN(J2_callrf,"if (!Pu4) callr Rs32",ATTRIBS(CINDIR_STD),"indirect conditional call if false", {fBRANCH_SPECULATE_STALL(fLSBOLDNOT(PuV),,SPECULATE_NOT_TAKEN,12,0);if (fLSBOLDNOT(PuV)) { fCALLR(RsV); }}) +Q6INSN(J2_callrh,"callrh Rs32",ATTRIBS(CINDIR_STD, A_HINTED_COF), "hinted indirect unconditional call", +{ fCALLR(RsV); }) diff --git a/target/hexagon/imported/encode_pp.def b/target/hexagon/imported/encode_pp.def index 939c6fc55f..0cd30a5e85 100644 --- a/target/hexagon/imported/encode_pp.def +++ b/target/hexagon/imported/encode_pp.def @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -382,14 +382,23 @@ DEF_ENC32(L4_return_fnew_pt, ICLASS_LD" 011 0 000 sssss PP1110vv ---ddddd") DEF_ENC32(L4_return_tnew_pnt, ICLASS_LD" 011 0 000 sssss PP0010vv ---ddddd") DEF_ENC32(L4_return_fnew_pnt, ICLASS_LD" 011 0 000 sssss PP1010vv ---ddddd") -DEF_ENC32(L2_loadw_locked,ICLASS_LD" 001 0 000 sssss PP00---- -00ddddd") +DEF_ENC32(L2_loadw_locked,ICLASS_LD" 001 0 000 sssss PP000--- 000ddddd") +DEF_ENC32(L2_loadw_aq, ICLASS_LD" 001 0 000 sssss PP001--- 000ddddd") +DEF_ENC32(L4_loadd_aq, ICLASS_LD" 001 0 000 sssss PP011--- 000ddddd") +DEF_ENC32(R6_release_at_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --0011dd") +DEF_ENC32(R6_release_st_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --1011dd") +DEF_ENC32(S2_storew_rl_at_vi, ICLASS_ST" 000 01 01sssss PP-ttttt --0010dd") +DEF_ENC32(S2_storew_rl_st_vi, ICLASS_ST" 000 01 01sssss PP-ttttt --1010dd") -DEF_ENC32(L4_loadd_locked,ICLASS_LD" 001 0 000 sssss PP01---- -00ddddd") +DEF_ENC32(S4_stored_rl_at_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --0010dd") +DEF_ENC32(S4_stored_rl_st_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --1010dd") + +DEF_ENC32(L4_loadd_locked,ICLASS_LD" 001 0 000 sssss PP010--- 000ddddd") DEF_EXT_SPACE(EXTRACTW, ICLASS_LD" 001 0 000 iiiii PP0iiiii -01iiiii") DEF_ENC32(Y2_dcfetchbo, ICLASS_LD" 010 0 000 sssss PP0--iii iiiiiiii") @@ -479,8 +488,8 @@ STD_PST_ENC(rinew, "1 101","10ttt") /* x bus/cache */ /* x store/cache */ DEF_ENC32(S2_allocframe, ICLASS_ST" 000 01 00xxxxx PP000iii iiiiiiii") -DEF_ENC32(S2_storew_locked,ICLASS_ST" 000 01 01sssss PP-ttttt ------dd") -DEF_ENC32(S4_stored_locked,ICLASS_ST" 000 01 11sssss PP0ttttt ------dd") +DEF_ENC32(S2_storew_locked,ICLASS_ST" 000 01 01sssss PP-ttttt ----00dd") +DEF_ENC32(S4_stored_locked,ICLASS_ST" 000 01 11sssss PP0ttttt ----00dd") DEF_ENC32(Y2_dczeroa, ICLASS_ST" 000 01 10sssss PP0----- --------") @@ -515,6 +524,7 @@ DEF_FIELD32(ICLASS_J" 110- -------- PP-!---- --------",J_PT,"Predict-taken") DEF_FIELDROW_DESC32(ICLASS_J" 0000 -------- PP------ --------","[#0] PC=(Rs), R31=return") DEF_ENC32(J2_callr, ICLASS_J" 0000 101sssss PP------ --------") +DEF_ENC32(J2_callrh, ICLASS_J" 0000 110sssss PP------ --------") DEF_FIELDROW_DESC32(ICLASS_J" 0001 -------- PP------ --------","[#1] if (Pu) PC=(Rs), R31=return") DEF_ENC32(J2_callrt, ICLASS_J" 0001 000sssss PP----uu --------") @@ -522,6 +532,7 @@ DEF_ENC32(J2_callrf, ICLASS_J" 0001 001sssss PP----uu --------") DEF_FIELDROW_DESC32(ICLASS_J" 0010 -------- PP------ --------","[#2] PC=(Rs); ") DEF_ENC32(J2_jumpr, ICLASS_J" 0010 100sssss PP------ --------") +DEF_ENC32(J2_jumprh, ICLASS_J" 0010 110sssss PP------ --------") DEF_ENC32(J4_hintjumpr, ICLASS_J" 0010 101sssss PP------ --------") DEF_FIELDROW_DESC32(ICLASS_J" 0011 -------- PP------ --------","[#3] if (Pu) PC=(Rs) ") @@ -944,13 +955,6 @@ MPY_ENC(F2_dfmpyfix, "1000","ddddd","0","0","1","0","11") MPY_ENC(F2_dfmin, "1000","ddddd","0","0","1","1","11") MPY_ENC(F2_dfmax, "1000","ddddd","0","1","0","0","11") MPY_ENC(F2_dfmpyll, "1000","ddddd","0","1","0","1","11") -#ifdef ADD_DP_OPS -MPY_ENC(F2_dfdivcheat, "1000","ddddd","0","0","0","1","00") - -MPY_ENC(F2_dffixupn, "1000","ddddd","0","1","0","1","11") -MPY_ENC(F2_dffixupd, "1000","ddddd","0","1","1","0","11") -MPY_ENC(F2_dfrecipa, "1000","ddddd","0","1","1","1","ee") -#endif MPY_ENC(M7_dcmpyrw, "1000","ddddd","0","0","0","1","10") MPY_ENC(M7_dcmpyrwc, "1000","ddddd","0","0","1","1","10") @@ -1024,15 +1028,6 @@ MPY_ENC(M5_vdmacbsu, "1010","xxxxx","0","1","0","0","01") MPY_ENC(F2_dfmpylh, "1010","xxxxx","0","0","0","0","11") MPY_ENC(F2_dfmpyhh, "1010","xxxxx","0","0","0","1","11") -#ifdef ADD_DP_OPS -MPY_ENC(F2_dfmpyhh, "1010","xxxxx","0","0","1","0","11") -MPY_ENC(F2_dffma, "1010","xxxxx","0","0","0","0","11") -MPY_ENC(F2_dffms, "1010","xxxxx","0","0","0","1","11") - -MPY_ENC(F2_dffma_lib, "1010","xxxxx","0","0","1","0","11") -MPY_ENC(F2_dffms_lib, "1010","xxxxx","0","0","1","1","11") -MPY_ENC(F2_dffma_sc, "1010","xxxxx","0","1","1","1","uu") -#endif MPY_ENC(M7_dcmpyrw_acc, "1010","xxxxx","0","0","0","1","10") @@ -1547,15 +1542,8 @@ SH2_RR_ENC(F2_conv_df2d, "0000","111","0","0 00","ddddd") SH2_RR_ENC(F2_conv_df2ud, "0000","111","0","0 01","ddddd") SH2_RR_ENC(F2_conv_ud2df, "0000","111","0","0 10","ddddd") SH2_RR_ENC(F2_conv_d2df, "0000","111","0","0 11","ddddd") -#ifdef ADD_DP_OPS -SH2_RR_ENC(F2_dffixupr, "0000","111","0","1 00","ddddd") -SH2_RR_ENC(F2_dfsqrtcheat, "0000","111","0","1 01","ddddd") -#endif SH2_RR_ENC(F2_conv_df2d_chop, "0000","111","0","1 10","ddddd") SH2_RR_ENC(F2_conv_df2ud_chop,"0000","111","0","1 11","ddddd") -#ifdef ADD_DP_OPS -SH2_RR_ENC(F2_dfinvsqrta, "0000","111","1","0 ee","ddddd") -#endif diff --git a/target/hexagon/imported/ldst.idef b/target/hexagon/imported/ldst.idef index 359d3b744e..53198176a9 100644 --- a/target/hexagon/imported/ldst.idef +++ b/target/hexagon/imported/ldst.idef @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,12 +31,12 @@ Q6INSN(L2_##TAG##_pci, OPER"(Rx32++#s4:"SHFT":circ(Mu2))",ATTRIB,DESCR,{fEA_REG( Q6INSN(L2_##TAG##_pcr, OPER"(Rx32++I:circ(Mu2))", ATTRIB,DESCR,{fEA_REG(RxV); fPM_CIRR(RxV,fREAD_IREG(MuV)<> shamt)); \ + shamt = VvV.SRCTYPE2[2*i+1] & SHAMTMASK; \ + DSTM(1,VdV.SRCTYPE[i],SATFUNC(RNDFUNC(VuuV.v[1].SRCTYPE[i],shamt) >> shamt))) + +/* WORD TO HALF*/ +NARROWING_VECTOR_SHIFT(32,vasrvwuhsat,fSETHALF,uh,w,uh,:sat,fVSATUH,fVNOROUND,0xF) +NARROWING_VECTOR_SHIFT(32,vasrvwuhrndsat,fSETHALF,uh,w,uh,:rnd:sat,fVSATUH,fVROUND,0xF) +/* HALF TO BYTE*/ +NARROWING_VECTOR_SHIFT(16,vasrvuhubsat,fSETBYTE,ub,uh,ub,:sat,fVSATUB,fVNOROUND,0x7) +NARROWING_VECTOR_SHIFT(16,vasrvuhubrndsat,fSETBYTE,ub,uh,ub,:rnd:sat,fVSATUB,fVROUND,0x7) + NARROWING_SHIFT_NOV1(16,vasruhubsat,fSETBYTE,ub,uh,:sat,fVSATUB,fVNOROUND,0x7) NARROWING_SHIFT_NOV1(16,vasruhubrndsat,fSETBYTE,ub,uh,:rnd:sat,fVSATUB,fVROUND,0x7) @@ -1360,6 +1383,9 @@ ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(16,vmpyhvsrs,"Vd32=vmpyh(Vu32,Vv32):<<1:rnd:s +ITERATOR_INSN_MPY_SLOT(16,vmpyuhvs, "Vd32.uh=vmpy(Vu32.uh,Vv32.uh):>>16", +"Vector by Vector Unsigned Halfword Multiply with 16 bit rightshift", + VdV.uh[i] = fGETUHALF(1,fMPY16UU(VuV.uh[i],VvV.uh[i]))) ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(32,vmpyhus, "Vdd32=vmpyhus(Vu32,Vv32)","Vdd32.w=vmpy(Vu32.h,Vv32.uh)", @@ -2019,11 +2045,11 @@ VxV.uw[0] = RtV;) -ITERATOR_INSN_MPY_SLOT_LATE(32,lvsplatw, "Vd32=vsplat(Rt32)", "Replicates scalar accross words in vector", VdV.uw[i] = RtV) +ITERATOR_INSN_MPY_SLOT_LATE(32,lvsplatw, "Vd32=vsplat(Rt32)", "Replicates scalar across words in vector", VdV.uw[i] = RtV) -ITERATOR_INSN_MPY_SLOT_LATE(16,lvsplath, "Vd32.h=vsplat(Rt32)", "Replicates scalar accross halves in vector", VdV.uh[i] = RtV) +ITERATOR_INSN_MPY_SLOT_LATE(16,lvsplath, "Vd32.h=vsplat(Rt32)", "Replicates scalar across halves in vector", VdV.uh[i] = RtV) -ITERATOR_INSN_MPY_SLOT_LATE(8,lvsplatb, "Vd32.b=vsplat(Rt32)", "Replicates scalar accross bytes in vector", VdV.ub[i] = RtV) +ITERATOR_INSN_MPY_SLOT_LATE(8,lvsplatb, "Vd32.b=vsplat(Rt32)", "Replicates scalar across bytes in vector", VdV.ub[i] = RtV) ITERATOR_INSN_ANY_SLOT(32,vassign,"Vd32=Vu32","Copy a vector",VdV.w[i]=VuV.w[i]) @@ -2038,6 +2064,24 @@ ITERATOR_INSN_ANY_SLOT_DOUBLE_VEC(8,vcombine,"Vdd32=vcombine(Vu32,Vv32)", /////////////////////////////////////////////////////////////////////////// +EXTINSN(V6_vcombine_tmp, "Vdd32.tmp=vcombine(Vu32,Vv32)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_REMAP,A_CVI_TMP,A_NO_INTRINSIC), +"Vector assign tmp, Any two to Vector Pair ", +{ + fHIDE(int i;) + fVFOREACH(8, i) { + VddV.v[0].ub[i] = VvV.ub[i]; + VddV.v[1].ub[i] = VuV.ub[i]; + } +}) + +EXTINSN(V6_vassign_tmp, "Vd32.tmp=Vu32", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_REMAP,A_CVI_TMP,A_NO_INTRINSIC), +"Vector assign tmp, Any two to Vector Pair ", +{ + fHIDE(int i;) + fVFOREACH(32, i) { + VdV.w[i]=VuV.w[i]; + } +}) /********************************************************* * GENERAL PERMUTE NETWORKS @@ -2507,6 +2551,281 @@ EXTINSN(V6_vscattermhw , "vscatter(Rt32,Mu2,Vvv32.w).h=Vw32", ATTRIBS(A_EXTENSIO }) +ITERATOR_INSN_MPY_SLOT_DOUBLE_VEC_VX_FWD(32, v6mpyvubs10_vxx, "Vxx32.w+=v6mpy(Vuu32.ub,Vvv32.b,#u2):v", "", + fHIDE(size2s_t c00;) + fGET10BIT(c00, VvvV.v[0].uw[i], 0) + fHIDE(size2s_t c01;) + fGET10BIT(c01, VvvV.v[0].uw[i], 1) + fHIDE(size2s_t c02;) + fGET10BIT(c02, VvvV.v[0].uw[i], 2) + + fHIDE(size2s_t c10;) + fGET10BIT(c10, VvvV.v[1].uw[i], 0) + fHIDE(size2s_t c11;) + fGET10BIT(c11, VvvV.v[1].uw[i], 1) + fHIDE(size2s_t c12;) + fGET10BIT(c12, VvvV.v[1].uw[i], 2) + + if (uiV == 0) { + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c10); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c11); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[1].uw[i]), c12); + + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c00); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c01); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c02); + + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c10); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c11); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c12); + + } else if (uiV == 1) { + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c00); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c01); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[1].uw[i]), c02); + + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c10); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c11); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[1].uw[i]), c12); + + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c00); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c01); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c02); + + } else if (uiV == 2) { + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c10); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c11); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c12); + + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[0].uw[i]), c00); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c01); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c02); + + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[0].uw[i]), c10); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c11); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c12); + + } else if (uiV == 3) { + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c00); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c01); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c02); + + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c10); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c11); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c12); + + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[0].uw[i]), c00); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c01); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c02); + } +) +ITERATOR_INSN_MPY_SLOT_DOUBLE_VEC_VX_FWD(32, v6mpyhubs10_vxx, "Vxx32.w+=v6mpy(Vuu32.ub,Vvv32.b,#u2):h", "", + fHIDE(size2s_t c00;) + fGET10BIT(c00, VvvV.v[0].uw[i], 0) + fHIDE(size2s_t c01;) + fGET10BIT(c01, VvvV.v[0].uw[i], 1) + fHIDE(size2s_t c02;) + fGET10BIT(c02, VvvV.v[0].uw[i], 2) + fHIDE(size2s_t c10;) + fGET10BIT(c10, VvvV.v[1].uw[i], 0) + fHIDE(size2s_t c11;) + fGET10BIT(c11, VvvV.v[1].uw[i], 1) + fHIDE(size2s_t c12;) + fGET10BIT(c12, VvvV.v[1].uw[i], 2) + + if (uiV == 0) { + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[1].uw[i]), c10); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c11); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c12); + + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c00); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c01); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c02); + + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c10); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c11); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c12); + + } else if (uiV == 1) { + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[1].uw[i]), c00); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c01); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c02); + + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[1].uw[i]), c10); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c11); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c12); + + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c00); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c01); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c02); + + } else if (uiV == 2) { + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c10); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c11); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c12); + + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c00); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c01); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[0].uw[i]), c02); + + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c10); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c11); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[0].uw[i]), c12); + + } else if (uiV == 3) { + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c00); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c01); + VxxV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c02); + + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c10); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c11); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c12); + + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c00); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c01); + VxxV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[0].uw[i]), c02); + } +) + + +ITERATOR_INSN_MPY_SLOT_DOUBLE_VEC(32, v6mpyvubs10, "Vdd32.w=v6mpy(Vuu32.ub,Vvv32.b,#u2):v", "", + fHIDE(short c00;) + fGET10BIT(c00, VvvV.v[0].uw[i], 0) + fHIDE(short c01;) + fGET10BIT(c01, VvvV.v[0].uw[i], 1) + fHIDE(short c02;) + fGET10BIT(c02, VvvV.v[0].uw[i], 2) + fHIDE(short c10;) + fGET10BIT(c10, VvvV.v[1].uw[i], 0) + fHIDE(short c11;) + fGET10BIT(c11, VvvV.v[1].uw[i], 1) + fHIDE(short c12;) + fGET10BIT(c12, VvvV.v[1].uw[i], 2) + + + + if (uiV == 0) { + VddV.v[1].w[i] = fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c10); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c11); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[1].uw[i]), c12); + + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c00); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c01); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c02); + + VddV.v[0].w[i] = fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c10); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c11); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c12); + + } else if (uiV == 1) { + VddV.v[1].w[i] = fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c00); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c01); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[1].uw[i]), c02); + + VddV.v[0].w[i] = fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c10); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c11); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[1].uw[i]), c12); + + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c00); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c01); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c02); + + } else if (uiV == 2) { + VddV.v[1].w[i] = fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c10); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c11); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c12); + + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[0].uw[i]), c00); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c01); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c02); + + VddV.v[0].w[i] = fMPY16US(fGETUBYTE(0,VuuV.v[0].uw[i]), c10); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c11); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c12); + + } else if (uiV == 3) { + VddV.v[1].w[i] = fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c00); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c01); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c02); + + VddV.v[0].w[i] = fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c10); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c11); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c12); + + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[0].uw[i]), c00); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c01); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c02); + } +) + +ITERATOR_INSN_MPY_SLOT_DOUBLE_VEC(32, v6mpyhubs10, "Vdd32.w=v6mpy(Vuu32.ub,Vvv32.b,#u2):h", "", + fHIDE(short c00;) + fGET10BIT(c00, VvvV.v[0].uw[i], 0) + fHIDE(short c01;) + fGET10BIT(c01, VvvV.v[0].uw[i], 1) + fHIDE(short c02;) + fGET10BIT(c02, VvvV.v[0].uw[i], 2) + fHIDE(short c10;) + fGET10BIT(c10, VvvV.v[1].uw[i], 0) + fHIDE(short c11;) + fGET10BIT(c11, VvvV.v[1].uw[i], 1) + fHIDE(short c12;) + fGET10BIT(c12, VvvV.v[1].uw[i], 2) + + if (uiV == 0) { + VddV.v[1].w[i] = fMPY16US(fGETUBYTE(3,VuuV.v[1].uw[i]), c10); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c11); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c12); + + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c00); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c01); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c02); + + VddV.v[0].w[i] = fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c10); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c11); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c12); + + } else if (uiV == 1) { + VddV.v[1].w[i] = fMPY16US(fGETUBYTE(3,VuuV.v[1].uw[i]), c00); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c01); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c02); + + VddV.v[0].w[i] = fMPY16US(fGETUBYTE(3,VuuV.v[1].uw[i]), c10); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c11); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c12); + + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[1].uw[i]), c00); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c01); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c02); + + } else if (uiV == 2) { + VddV.v[1].w[i] = fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c10); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c11); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c12); + + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c00); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c01); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[0].uw[i]), c02); + + VddV.v[0].w[i] = fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c10); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c11); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[0].uw[i]), c12); + + } else if (uiV == 3) { + VddV.v[1].w[i] = fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c00); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c01); + VddV.v[1].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c02); + + VddV.v[0].w[i] = fMPY16US(fGETUBYTE(1,VuuV.v[1].uw[i]), c10); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(3,VuuV.v[0].uw[i]), c11); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(1,VuuV.v[0].uw[i]), c12); + + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[1].uw[i]), c00); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(2,VuuV.v[0].uw[i]), c01); + VddV.v[0].w[i] += fMPY16US(fGETUBYTE(0,VuuV.v[0].uw[i]), c02); + } +) + EXTINSN(V6_vscattermhwq, "if (Qs4) vscatter(Rt32,Mu2,Vvv32.w).h=Vw32", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_SCATTER,A_CVI_VA_DV,A_CVI_VM,A_MEMLIKE), "Scatter halfwords conditional", { diff --git a/target/hexagon/imported/subinsns.idef b/target/hexagon/imported/subinsns.idef index ec1c74f479..be0ae8779d 100644 --- a/target/hexagon/imported/subinsns.idef +++ b/target/hexagon/imported/subinsns.idef @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,18 +39,18 @@ Q6INSN(SA1_clrf, "if (!p0) Rd16=#0", ATTRIBS(A_SUBINSN),"clear if false Q6INSN(SA1_addsp, "Rd16=add(r29,#u6:2)", ATTRIBS(A_SUBINSN),"Add", { RdV=fREAD_SP()+uiV; }) Q6INSN(SA1_inc, "Rd16=add(Rs16,#1)", ATTRIBS(A_SUBINSN),"Inc", { RdV=RsV+1;}) Q6INSN(SA1_dec, "Rd16=add(Rs16,#-1)", ATTRIBS(A_SUBINSN),"Dec", { RdV=RsV-1;}) -Q6INSN(SA1_addrx, "Rx16=add(Rx16,Rs16)", ATTRIBS(A_SUBINSN),"Add", { RxV=RxV+RsV; }) +Q6INSN(SA1_addrx, "Rx16=add(Rx16,Rs16)", ATTRIBS(A_SUBINSN,A_COMMUTES),"Add", { RxV=RxV+RsV; }) Q6INSN(SA1_zxtb, "Rd16=and(Rs16,#255)", ATTRIBS(A_SUBINSN),"Zxtb", { RdV= fZXTN(8,32,RsV);}) Q6INSN(SA1_and1, "Rd16=and(Rs16,#1)", ATTRIBS(A_SUBINSN),"And #1", { RdV= RsV&1;}) Q6INSN(SA1_sxtb, "Rd16=sxtb(Rs16)", ATTRIBS(A_SUBINSN),"Sxtb", { RdV= fSXTN(8,32,RsV);}) Q6INSN(SA1_zxth, "Rd16=zxth(Rs16)", ATTRIBS(A_SUBINSN),"Zxth", { RdV= fZXTN(16,32,RsV);}) Q6INSN(SA1_sxth, "Rd16=sxth(Rs16)", ATTRIBS(A_SUBINSN),"Sxth", { RdV= fSXTN(16,32,RsV);}) -Q6INSN(SA1_combinezr,"Rdd8=combine(#0,Rs16)", ATTRIBS(A_SUBINSN),"Combines", { fSETWORD(0,RddV,RsV); fSETWORD(1,RddV,0); }) -Q6INSN(SA1_combinerz,"Rdd8=combine(Rs16,#0)", ATTRIBS(A_SUBINSN),"Combines", { fSETWORD(0,RddV,0); fSETWORD(1,RddV,RsV); }) -Q6INSN(SA1_combine0i,"Rdd8=combine(#0,#u2)", ATTRIBS(A_SUBINSN),"Combines", { fSETWORD(0,RddV,uiV); fSETWORD(1,RddV,0); }) -Q6INSN(SA1_combine1i,"Rdd8=combine(#1,#u2)", ATTRIBS(A_SUBINSN),"Combines", { fSETWORD(0,RddV,uiV); fSETWORD(1,RddV,1); }) -Q6INSN(SA1_combine2i,"Rdd8=combine(#2,#u2)", ATTRIBS(A_SUBINSN),"Combines", { fSETWORD(0,RddV,uiV); fSETWORD(1,RddV,2); }) -Q6INSN(SA1_combine3i,"Rdd8=combine(#3,#u2)", ATTRIBS(A_SUBINSN),"Combines", { fSETWORD(0,RddV,uiV); fSETWORD(1,RddV,3); }) +Q6INSN(SA1_combinezr,"Rdd8=combine(#0,Rs16)", ATTRIBS(A_SUBINSN,A_ROPS_2),"Combines", { fSETWORD(0,RddV,RsV); fSETWORD(1,RddV,0); }) +Q6INSN(SA1_combinerz,"Rdd8=combine(Rs16,#0)", ATTRIBS(A_SUBINSN,A_ROPS_2),"Combines", { fSETWORD(0,RddV,0); fSETWORD(1,RddV,RsV); }) +Q6INSN(SA1_combine0i,"Rdd8=combine(#0,#u2)", ATTRIBS(A_SUBINSN,A_ROPS_2),"Combines", { fSETWORD(0,RddV,uiV); fSETWORD(1,RddV,0); }) +Q6INSN(SA1_combine1i,"Rdd8=combine(#1,#u2)", ATTRIBS(A_SUBINSN,A_ROPS_2),"Combines", { fSETWORD(0,RddV,uiV); fSETWORD(1,RddV,1); }) +Q6INSN(SA1_combine2i,"Rdd8=combine(#2,#u2)", ATTRIBS(A_SUBINSN,A_ROPS_2),"Combines", { fSETWORD(0,RddV,uiV); fSETWORD(1,RddV,2); }) +Q6INSN(SA1_combine3i,"Rdd8=combine(#3,#u2)", ATTRIBS(A_SUBINSN,A_ROPS_2),"Combines", { fSETWORD(0,RddV,uiV); fSETWORD(1,RddV,3); }) Q6INSN(SA1_cmpeqi, "p0=cmp.eq(Rs16,#u2)", ATTRIBS(A_SUBINSN),"CompareImmed",{fWRITE_P0(f8BITSOF(RsV==uiV));}) @@ -62,16 +62,16 @@ Q6INSN(SA1_cmpeqi, "p0=cmp.eq(Rs16,#u2)", ATTRIBS(A_SUBINSN),"CompareImmed", /* */ /*****************************************************************/ -Q6INSN(SL1_loadri_io, "Rd16=memw(Rs16+#u4:2)", ATTRIBS(A_LOAD,A_SUBINSN),"load word", {fEA_RI(RsV,uiV); fLOAD(1,4,u,EA,RdV);}) -Q6INSN(SL1_loadrub_io, "Rd16=memub(Rs16+#u4:0)",ATTRIBS(A_LOAD,A_SUBINSN),"load byte", {fEA_RI(RsV,uiV); fLOAD(1,1,u,EA,RdV);}) +Q6INSN(SL1_loadri_io, "Rd16=memw(Rs16+#u4:2)", ATTRIBS(A_REGWRSIZE_4B,A_MEMSIZE_4B,A_LOAD,A_SUBINSN),"load word", {fEA_RI(RsV,uiV); fLOAD(1,4,u,EA,RdV);}) +Q6INSN(SL1_loadrub_io, "Rd16=memub(Rs16+#u4:0)",ATTRIBS(A_MEMSIZE_1B,A_LOAD,A_SUBINSN,A_REGWRSIZE_1B),"load byte", {fEA_RI(RsV,uiV); fLOAD(1,1,u,EA,RdV);}) -Q6INSN(SL2_loadrh_io, "Rd16=memh(Rs16+#u3:1)", ATTRIBS(A_LOAD,A_SUBINSN),"load half", {fEA_RI(RsV,uiV); fLOAD(1,2,s,EA,RdV);}) -Q6INSN(SL2_loadruh_io, "Rd16=memuh(Rs16+#u3:1)",ATTRIBS(A_LOAD,A_SUBINSN),"load half", {fEA_RI(RsV,uiV); fLOAD(1,2,u,EA,RdV);}) -Q6INSN(SL2_loadrb_io, "Rd16=memb(Rs16+#u3:0)", ATTRIBS(A_LOAD,A_SUBINSN),"load byte", {fEA_RI(RsV,uiV); fLOAD(1,1,s,EA,RdV);}) -Q6INSN(SL2_loadri_sp, "Rd16=memw(r29+#u5:2)", ATTRIBS(A_LOAD,A_SUBINSN),"load word", {fEA_RI(fREAD_SP(),uiV); fLOAD(1,4,u,EA,RdV);}) -Q6INSN(SL2_loadrd_sp, "Rdd8=memd(r29+#u5:3)", ATTRIBS(A_LOAD,A_SUBINSN),"load dword",{fEA_RI(fREAD_SP(),uiV); fLOAD(1,8,u,EA,RddV);}) +Q6INSN(SL2_loadrh_io, "Rd16=memh(Rs16+#u3:1)", ATTRIBS(A_REGWRSIZE_2B,A_MEMSIZE_2B,A_LOAD,A_SUBINSN),"load half", {fEA_RI(RsV,uiV); fLOAD(1,2,s,EA,RdV);}) +Q6INSN(SL2_loadruh_io, "Rd16=memuh(Rs16+#u3:1)",ATTRIBS(A_REGWRSIZE_2B,A_MEMSIZE_2B,A_LOAD,A_SUBINSN),"load half", {fEA_RI(RsV,uiV); fLOAD(1,2,u,EA,RdV);}) +Q6INSN(SL2_loadrb_io, "Rd16=memb(Rs16+#u3:0)", ATTRIBS(A_MEMSIZE_1B,A_LOAD,A_SUBINSN),"load byte", {fEA_RI(RsV,uiV); fLOAD(1,1,s,EA,RdV);}) +Q6INSN(SL2_loadri_sp, "Rd16=memw(r29+#u5:2)", ATTRIBS(A_REGWRSIZE_4B,A_MEMSIZE_4B,A_LOAD,A_SUBINSN),"load word", {fEA_RI(fREAD_SP(),uiV); fLOAD(1,4,u,EA,RdV);}) +Q6INSN(SL2_loadrd_sp, "Rdd8=memd(r29+#u5:3)", ATTRIBS(A_REGWRSIZE_8B,A_MEMSIZE_8B,A_LOAD,A_SUBINSN),"load dword",{fEA_RI(fREAD_SP(),uiV); fLOAD(1,8,u,EA,RddV);}) -Q6INSN(SL2_deallocframe,"deallocframe", ATTRIBS(A_SUBINSN,A_LOAD), "Deallocate stack frame", +Q6INSN(SL2_deallocframe,"deallocframe", ATTRIBS(A_REGWRSIZE_8B,A_SUBINSN,A_MEMSIZE_8B,A_LOAD,A_DEALLOCFRAME), "Deallocate stack frame", { fHIDE(size8u_t tmp;) fEA_REG(fREAD_FP()); fLOAD(1,8,u,EA,tmp); tmp = fFRAME_UNSCRAMBLE(tmp); @@ -79,7 +79,7 @@ Q6INSN(SL2_deallocframe,"deallocframe", ATTRIBS(A_SUBINSN,A_LOAD), "Deallocate s fWRITE_FP(fGETWORD(0,tmp)); fWRITE_SP(EA+8); }) -Q6INSN(SL2_return,"dealloc_return", ATTRIBS(A_JINDIR,A_SUBINSN,A_LOAD,A_RETURN,A_RESTRICT_SLOT0ONLY), "Deallocate stack frame and return", +Q6INSN(SL2_return,"dealloc_return", ATTRIBS(A_REGWRSIZE_8B,A_JINDIR,A_SUBINSN,A_ROPS_2,A_MEMSIZE_8B,A_LOAD,A_RETURN,A_RESTRICT_SLOT0ONLY,A_RET_TYPE,A_DEALLOCRET), "Deallocate stack frame and return", { fHIDE(size8u_t tmp;) fEA_REG(fREAD_FP()); fLOAD(1,8,u,EA,tmp); tmp = fFRAME_UNSCRAMBLE(tmp); @@ -88,40 +88,40 @@ Q6INSN(SL2_return,"dealloc_return", ATTRIBS(A_JINDIR,A_SUBINSN,A_LOAD,A_RETURN,A fWRITE_SP(EA+8); fJUMPR(REG_LR,fGETWORD(1,tmp),COF_TYPE_JUMPR);}) -Q6INSN(SL2_return_t,"if (p0) dealloc_return", ATTRIBS(A_JINDIROLD,A_SUBINSN,A_LOAD,A_RETURN,A_RESTRICT_SLOT0ONLY), "Deallocate stack frame and return", +Q6INSN(SL2_return_t,"if (p0) dealloc_return", ATTRIBS(A_REGWRSIZE_8B,A_JINDIROLD,A_SUBINSN,A_ROPS_2,A_MEMSIZE_8B,A_LOAD,A_RETURN,A_RESTRICT_SLOT0ONLY,A_RET_TYPE), "Deallocate stack frame and return", { fHIDE(size8u_t tmp;); fBRANCH_SPECULATE_STALL(fLSBOLD(fREAD_P0()),, SPECULATE_NOT_TAKEN,4,0); fEA_REG(fREAD_FP()); if (fLSBOLD(fREAD_P0())) { fLOAD(1,8,u,EA,tmp); tmp = fFRAME_UNSCRAMBLE(tmp); fWRITE_LR(fGETWORD(1,tmp)); fWRITE_FP(fGETWORD(0,tmp)); fWRITE_SP(EA+8); fJUMPR(REG_LR,fGETWORD(1,tmp),COF_TYPE_JUMPR);} else {LOAD_CANCEL(EA);} }) -Q6INSN(SL2_return_f,"if (!p0) dealloc_return", ATTRIBS(A_JINDIROLD,A_SUBINSN,A_LOAD,A_RETURN,A_RESTRICT_SLOT0ONLY), "Deallocate stack frame and return", +Q6INSN(SL2_return_f,"if (!p0) dealloc_return", ATTRIBS(A_REGWRSIZE_8B,A_JINDIROLD,A_SUBINSN,A_ROPS_2,A_MEMSIZE_8B,A_LOAD,A_RETURN,A_RESTRICT_SLOT0ONLY,A_RET_TYPE), "Deallocate stack frame and return", { fHIDE(size8u_t tmp;);fBRANCH_SPECULATE_STALL(fLSBOLDNOT(fREAD_P0()),, SPECULATE_NOT_TAKEN,4,0); fEA_REG(fREAD_FP()); if (fLSBOLDNOT(fREAD_P0())) { fLOAD(1,8,u,EA,tmp); tmp = fFRAME_UNSCRAMBLE(tmp); fWRITE_LR(fGETWORD(1,tmp)); fWRITE_FP(fGETWORD(0,tmp)); fWRITE_SP(EA+8); fJUMPR(REG_LR,fGETWORD(1,tmp),COF_TYPE_JUMPR);} else {LOAD_CANCEL(EA);} }) -Q6INSN(SL2_return_tnew,"if (p0.new) dealloc_return:nt", ATTRIBS(A_JINDIRNEW,A_SUBINSN,A_LOAD,A_RETURN,A_RESTRICT_SLOT0ONLY), "Deallocate stack frame and return", +Q6INSN(SL2_return_tnew,"if (p0.new) dealloc_return:nt", ATTRIBS(A_REGWRSIZE_8B,A_JINDIRNEW,A_SUBINSN,A_ROPS_2,A_MEMSIZE_8B,A_LOAD,A_RETURN,A_RESTRICT_SLOT0ONLY,A_RET_TYPE), "Deallocate stack frame and return", { fHIDE(size8u_t tmp;) fBRANCH_SPECULATE_STALL(fLSBNEW0,, SPECULATE_NOT_TAKEN , 4,3); fEA_REG(fREAD_FP()); if (fLSBNEW0) { fLOAD(1,8,u,EA,tmp); tmp = fFRAME_UNSCRAMBLE(tmp); fWRITE_LR(fGETWORD(1,tmp)); fWRITE_FP(fGETWORD(0,tmp)); fWRITE_SP(EA+8); fJUMPR(REG_LR,fGETWORD(1,tmp),COF_TYPE_JUMPR);} else {LOAD_CANCEL(EA);} }) -Q6INSN(SL2_return_fnew,"if (!p0.new) dealloc_return:nt", ATTRIBS(A_JINDIRNEW,A_SUBINSN,A_LOAD,A_RETURN,A_RESTRICT_SLOT0ONLY), "Deallocate stack frame and return", +Q6INSN(SL2_return_fnew,"if (!p0.new) dealloc_return:nt", ATTRIBS(A_REGWRSIZE_8B,A_JINDIRNEW,A_SUBINSN,A_ROPS_2,A_MEMSIZE_8B,A_LOAD,A_RETURN,A_RESTRICT_SLOT0ONLY,A_RET_TYPE), "Deallocate stack frame and return", { fHIDE(size8u_t tmp;) fBRANCH_SPECULATE_STALL(fLSBNEW0NOT,, SPECULATE_NOT_TAKEN , 4,3); fEA_REG(fREAD_FP()); if (fLSBNEW0NOT) { fLOAD(1,8,u,EA,tmp); tmp = fFRAME_UNSCRAMBLE(tmp); fWRITE_LR(fGETWORD(1,tmp)); fWRITE_FP(fGETWORD(0,tmp)); fWRITE_SP(EA+8); fJUMPR(REG_LR,fGETWORD(1,tmp),COF_TYPE_JUMPR);} else {LOAD_CANCEL(EA);} }) -Q6INSN(SL2_jumpr31,"jumpr r31",ATTRIBS(A_SUBINSN,A_JINDIR,A_RESTRICT_SLOT0ONLY),"indirect unconditional jump", +Q6INSN(SL2_jumpr31,"jumpr r31",ATTRIBS(A_SUBINSN,A_JINDIR,A_RESTRICT_SLOT0ONLY,A_RET_TYPE),"indirect unconditional jump", { fJUMPR(REG_LR,fREAD_LR(),COF_TYPE_JUMPR);}) -Q6INSN(SL2_jumpr31_t,"if (p0) jumpr r31",ATTRIBS(A_SUBINSN,A_JINDIROLD,A_RESTRICT_SLOT0ONLY),"indirect conditional jump if true", +Q6INSN(SL2_jumpr31_t,"if (p0) jumpr r31",ATTRIBS(A_SUBINSN,A_JINDIROLD,A_NOTE_CONDITIONAL,A_RESTRICT_SLOT0ONLY,A_RET_TYPE),"indirect conditional jump if true", {fBRANCH_SPECULATE_STALL(fLSBOLD(fREAD_P0()),, SPECULATE_TAKEN,4,0); if (fLSBOLD(fREAD_P0())) {fJUMPR(REG_LR,fREAD_LR(),COF_TYPE_JUMPR);}}) -Q6INSN(SL2_jumpr31_f,"if (!p0) jumpr r31",ATTRIBS(A_SUBINSN,A_JINDIROLD,A_RESTRICT_SLOT0ONLY),"indirect conditional jump if false", +Q6INSN(SL2_jumpr31_f,"if (!p0) jumpr r31",ATTRIBS(A_SUBINSN,A_JINDIROLD,A_NOTE_CONDITIONAL,A_RESTRICT_SLOT0ONLY,A_RET_TYPE),"indirect conditional jump if false", {fBRANCH_SPECULATE_STALL(fLSBOLDNOT(fREAD_P0()),, SPECULATE_TAKEN,4,0); if (fLSBOLDNOT(fREAD_P0())) {fJUMPR(REG_LR,fREAD_LR(),COF_TYPE_JUMPR);}}) -Q6INSN(SL2_jumpr31_tnew,"if (p0.new) jumpr:nt r31",ATTRIBS(A_SUBINSN,A_JINDIRNEW,A_RESTRICT_SLOT0ONLY),"indirect conditional jump if true", +Q6INSN(SL2_jumpr31_tnew,"if (p0.new) jumpr:nt r31",ATTRIBS(A_SUBINSN,A_JINDIRNEW,A_NOTE_CONDITIONAL,A_RESTRICT_SLOT0ONLY,A_RET_TYPE),"indirect conditional jump if true", {fBRANCH_SPECULATE_STALL(fLSBNEW0,, SPECULATE_NOT_TAKEN , 4,3); if (fLSBNEW0) {fJUMPR(REG_LR,fREAD_LR(),COF_TYPE_JUMPR);}}) -Q6INSN(SL2_jumpr31_fnew,"if (!p0.new) jumpr:nt r31",ATTRIBS(A_SUBINSN,A_JINDIRNEW,A_RESTRICT_SLOT0ONLY),"indirect conditional jump if false", +Q6INSN(SL2_jumpr31_fnew,"if (!p0.new) jumpr:nt r31",ATTRIBS(A_SUBINSN,A_JINDIRNEW,A_NOTE_CONDITIONAL,A_RESTRICT_SLOT0ONLY,A_RET_TYPE),"indirect conditional jump if false", {fBRANCH_SPECULATE_STALL(fLSBNEW0NOT,, SPECULATE_NOT_TAKEN , 4,3); if (fLSBNEW0NOT) {fJUMPR(REG_LR,fREAD_LR(),COF_TYPE_JUMPR);}}) @@ -134,16 +134,16 @@ Q6INSN(SL2_jumpr31_fnew,"if (!p0.new) jumpr:nt r31",ATTRIBS(A_SUBINSN,A_JINDIRNE /* */ /*****************************************************************/ -Q6INSN(SS1_storew_io, "memw(Rs16+#u4:2)=Rt16", ATTRIBS(A_STORE,A_SUBINSN), "store word", {fEA_RI(RsV,uiV); fSTORE(1,4,EA,RtV);}) -Q6INSN(SS1_storeb_io, "memb(Rs16+#u4:0)=Rt16", ATTRIBS(A_STORE,A_SUBINSN), "store byte", {fEA_RI(RsV,uiV); fSTORE(1,1,EA,fGETBYTE(0,RtV));}) -Q6INSN(SS2_storeh_io, "memh(Rs16+#u3:1)=Rt16", ATTRIBS(A_STORE,A_SUBINSN), "store half", {fEA_RI(RsV,uiV); fSTORE(1,2,EA,fGETHALF(0,RtV));}) -Q6INSN(SS2_stored_sp, "memd(r29+#s6:3)=Rtt8", ATTRIBS(A_STORE,A_SUBINSN), "store dword",{fEA_RI(fREAD_SP(),siV); fSTORE(1,8,EA,RttV);}) -Q6INSN(SS2_storew_sp, "memw(r29+#u5:2)=Rt16", ATTRIBS(A_STORE,A_SUBINSN), "store word", {fEA_RI(fREAD_SP(),uiV); fSTORE(1,4,EA,RtV);}) -Q6INSN(SS2_storewi0, "memw(Rs16+#u4:2)=#0", ATTRIBS(A_STORE,A_SUBINSN), "store word", {fEA_RI(RsV,uiV); fSTORE(1,4,EA,0);}) -Q6INSN(SS2_storebi0, "memb(Rs16+#u4:0)=#0", ATTRIBS(A_STORE,A_SUBINSN), "store byte", {fEA_RI(RsV,uiV); fSTORE(1,1,EA,0);}) -Q6INSN(SS2_storewi1, "memw(Rs16+#u4:2)=#1", ATTRIBS(A_STORE,A_SUBINSN), "store word", {fEA_RI(RsV,uiV); fSTORE(1,4,EA,1);}) -Q6INSN(SS2_storebi1, "memb(Rs16+#u4:0)=#1", ATTRIBS(A_STORE,A_SUBINSN), "store byte", {fEA_RI(RsV,uiV); fSTORE(1,1,EA,1);}) +Q6INSN(SS1_storew_io, "memw(Rs16+#u4:2)=Rt16", ATTRIBS(A_REGWRSIZE_4B,A_MEMSIZE_4B,A_STORE,A_SUBINSN), "store word", {fEA_RI(RsV,uiV); fSTORE(1,4,EA,RtV);}) +Q6INSN(SS1_storeb_io, "memb(Rs16+#u4:0)=Rt16", ATTRIBS(A_MEMSIZE_1B,A_STORE,A_SUBINSN), "store byte", {fEA_RI(RsV,uiV); fSTORE(1,1,EA,fGETBYTE(0,RtV));}) +Q6INSN(SS2_storeh_io, "memh(Rs16+#u3:1)=Rt16", ATTRIBS(A_MEMSIZE_2B,A_STORE,A_SUBINSN), "store half", {fEA_RI(RsV,uiV); fSTORE(1,2,EA,fGETHALF(0,RtV));}) +Q6INSN(SS2_stored_sp, "memd(r29+#s6:3)=Rtt8", ATTRIBS(A_REGWRSIZE_8B,A_MEMSIZE_8B,A_STORE,A_SUBINSN), "store dword",{fEA_RI(fREAD_SP(),siV); fSTORE(1,8,EA,RttV);}) +Q6INSN(SS2_storew_sp, "memw(r29+#u5:2)=Rt16", ATTRIBS(A_REGWRSIZE_4B,A_MEMSIZE_4B,A_STORE,A_SUBINSN), "store word", {fEA_RI(fREAD_SP(),uiV); fSTORE(1,4,EA,RtV);}) +Q6INSN(SS2_storewi0, "memw(Rs16+#u4:2)=#0", ATTRIBS(A_REGWRSIZE_4B,A_MEMSIZE_4B,A_STORE,A_SUBINSN,A_ROPS_2), "store word", {fEA_RI(RsV,uiV); fSTORE(1,4,EA,0);}) +Q6INSN(SS2_storebi0, "memb(Rs16+#u4:0)=#0", ATTRIBS(A_MEMSIZE_1B,A_STORE,A_SUBINSN,A_ROPS_2), "store byte", {fEA_RI(RsV,uiV); fSTORE(1,1,EA,0);}) +Q6INSN(SS2_storewi1, "memw(Rs16+#u4:2)=#1", ATTRIBS(A_REGWRSIZE_4B,A_MEMSIZE_4B,A_STORE,A_SUBINSN,A_ROPS_2), "store word", {fEA_RI(RsV,uiV); fSTORE(1,4,EA,1);}) +Q6INSN(SS2_storebi1, "memb(Rs16+#u4:0)=#1", ATTRIBS(A_MEMSIZE_1B,A_STORE,A_SUBINSN,A_ROPS_2), "store byte", {fEA_RI(RsV,uiV); fSTORE(1,1,EA,1);}) -Q6INSN(SS2_allocframe,"allocframe(#u5:3)", ATTRIBS(A_SUBINSN,A_STORE,A_RESTRICT_SLOT0ONLY), "Allocate stack frame", +Q6INSN(SS2_allocframe,"allocframe(#u5:3)", ATTRIBS(A_REGWRSIZE_8B,A_SUBINSN,A_MEMSIZE_8B,A_STORE,A_RESTRICT_SLOT0ONLY), "Allocate stack frame", { fEA_RI(fREAD_SP(),-8); fSTORE(1,8,EA,fFRAME_SCRAMBLE((fCAST8_8u(fREAD_LR()) << 32) | fCAST4_4u(fREAD_FP()))); fWRITE_FP(EA); fFRAMECHECK(EA-uiV,EA); fWRITE_SP(EA-uiV); }) diff --git a/target/hexagon/insn.h b/target/hexagon/insn.h index aa26389147..3e7a22c91e 100644 --- a/target/hexagon/insn.h +++ b/target/hexagon/insn.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,10 +28,7 @@ struct Instruction; struct Packet; struct DisasContext; -typedef void (*SemanticInsn)(CPUHexagonState *env, - struct DisasContext *ctx, - struct Instruction *insn, - struct Packet *pkt); +typedef void (*SemanticInsn)(struct DisasContext *ctx); struct Instruction { SemanticInsn generate; /* pointer to genptr routine */ @@ -57,9 +54,11 @@ typedef struct Instruction Insn; struct Packet { uint16_t num_insns; uint16_t encod_pkt_size_in_bytes; + uint32_t pc; /* Pre-decodes about COF */ bool pkt_has_cof; /* Has any change-of-flow */ + bool pkt_has_multi_cof; /* Has more than one change-of-flow */ bool pkt_has_endloop; bool pkt_has_dczeroa; diff --git a/target/hexagon/internal.h b/target/hexagon/internal.h index b1bfadc3f5..beb08cb7e3 100644 --- a/target/hexagon/internal.h +++ b/target/hexagon/internal.h @@ -33,6 +33,8 @@ int hexagon_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int hexagon_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +int hexagon_hvx_gdb_read_register(CPUState *env, GByteArray *mem_buf, int n); +int hexagon_hvx_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n); void hexagon_debug_vreg(CPUHexagonState *env, int regnum); void hexagon_debug_qreg(CPUHexagonState *env, int regnum); diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index a78e84faa4..1376d6ccc1 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,16 +22,6 @@ #include "hex_regs.h" #include "reg_fields.h" -#ifdef QEMU_GENERATE -#define READ_REG(dest, NUM) gen_read_reg(dest, NUM) -#else -#define READ_REG(NUM) (env->gpr[(NUM)]) -#define READ_PREG(NUM) (env->pred[NUM]) - -#define WRITE_RREG(NUM, VAL) log_reg_write(env, NUM, VAL, slot) -#define WRITE_PREG(NUM, VAL) log_pred_write(env, NUM, VAL) -#endif - #define PCALIGN 4 #define PCALIGN_MASK (PCALIGN - 1) @@ -48,22 +38,23 @@ #define TYPE_INT(X) __builtin_types_compatible_p(typeof(X), int) #define TYPE_TCGV(X) __builtin_types_compatible_p(typeof(X), TCGv) #define TYPE_TCGV_I64(X) __builtin_types_compatible_p(typeof(X), TCGv_i64) - -#define SET_USR_FIELD_FUNC(X) \ - __builtin_choose_expr(TYPE_INT(X), \ - gen_set_usr_fieldi, \ - __builtin_choose_expr(TYPE_TCGV(X), \ - gen_set_usr_field, (void)0)) -#define SET_USR_FIELD(FIELD, VAL) \ - SET_USR_FIELD_FUNC(VAL)(FIELD, VAL) #else #define GET_USR_FIELD(FIELD) \ fEXTRACTU_BITS(env->gpr[HEX_REG_USR], reg_field_info[FIELD].width, \ reg_field_info[FIELD].offset) #define SET_USR_FIELD(FIELD, VAL) \ - fINSERT_BITS(env->new_value[HEX_REG_USR], reg_field_info[FIELD].width, \ - reg_field_info[FIELD].offset, (VAL)) + do { \ + if (pkt_need_commit) { \ + fINSERT_BITS(env->new_value_usr, \ + reg_field_info[FIELD].width, \ + reg_field_info[FIELD].offset, (VAL)); \ + } else { \ + fINSERT_BITS(env->gpr[HEX_REG_USR], \ + reg_field_info[FIELD].width, \ + reg_field_info[FIELD].offset, (VAL)); \ + } \ + } while (0) #endif #ifdef QEMU_GENERATE @@ -87,50 +78,67 @@ * * * For qemu, we look for a load in slot 0 when there is a store in slot 1 - * in the same packet. When we see this, we call a helper that merges the - * bytes from the store buffer with the value loaded from memory. + * in the same packet. When we see this, we call a helper that probes the + * load to make sure it doesn't fault. Then, we process the store ahead of + * the actual load. + */ -#define CHECK_NOSHUF \ +#define CHECK_NOSHUF(VA, SIZE) \ do { \ - if (insn->slot == 0 && pkt->pkt_has_store_s1) { \ - process_store(ctx, pkt, 1); \ + if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ + probe_noshuf_load(VA, SIZE, ctx->mem_idx); \ + process_store(ctx, 1); \ + } \ + } while (0) + +#define CHECK_NOSHUF_PRED(GET_EA, SIZE, PRED) \ + do { \ + TCGLabel *noshuf_label = gen_new_label(); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, PRED, 0, noshuf_label); \ + GET_EA; \ + if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ + probe_noshuf_load(EA, SIZE, ctx->mem_idx); \ + } \ + gen_set_label(noshuf_label); \ + if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ + process_store(ctx, 1); \ } \ } while (0) #define MEM_LOAD1s(DST, VA) \ do { \ - CHECK_NOSHUF; \ - tcg_gen_qemu_ld8s(DST, VA, ctx->mem_idx); \ + CHECK_NOSHUF(VA, 1); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_SB); \ } while (0) #define MEM_LOAD1u(DST, VA) \ do { \ - CHECK_NOSHUF; \ - tcg_gen_qemu_ld8u(DST, VA, ctx->mem_idx); \ + CHECK_NOSHUF(VA, 1); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_UB); \ } while (0) #define MEM_LOAD2s(DST, VA) \ do { \ - CHECK_NOSHUF; \ - tcg_gen_qemu_ld16s(DST, VA, ctx->mem_idx); \ + CHECK_NOSHUF(VA, 2); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TESW); \ } while (0) #define MEM_LOAD2u(DST, VA) \ do { \ - CHECK_NOSHUF; \ - tcg_gen_qemu_ld16u(DST, VA, ctx->mem_idx); \ + CHECK_NOSHUF(VA, 2); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TEUW); \ } while (0) #define MEM_LOAD4s(DST, VA) \ do { \ - CHECK_NOSHUF; \ - tcg_gen_qemu_ld32s(DST, VA, ctx->mem_idx); \ + CHECK_NOSHUF(VA, 4); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TESL); \ } while (0) #define MEM_LOAD4u(DST, VA) \ do { \ - CHECK_NOSHUF; \ - tcg_gen_qemu_ld32s(DST, VA, ctx->mem_idx); \ + CHECK_NOSHUF(VA, 4); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TEUL); \ } while (0) #define MEM_LOAD8u(DST, VA) \ do { \ - CHECK_NOSHUF; \ - tcg_gen_qemu_ld64(DST, VA, ctx->mem_idx); \ + CHECK_NOSHUF(VA, 8); \ + tcg_gen_qemu_ld_i64(DST, VA, ctx->mem_idx, MO_TEUQ); \ } while (0) #define MEM_STORE1_FUNC(X) \ @@ -139,7 +147,7 @@ __builtin_choose_expr(TYPE_TCGV(X), \ gen_store1, (void)0)) #define MEM_STORE1(VA, DATA, SLOT) \ - MEM_STORE1_FUNC(DATA)(cpu_env, VA, DATA, ctx, SLOT) + MEM_STORE1_FUNC(DATA)(tcg_env, VA, DATA, SLOT) #define MEM_STORE2_FUNC(X) \ __builtin_choose_expr(TYPE_INT(X), \ @@ -147,7 +155,7 @@ __builtin_choose_expr(TYPE_TCGV(X), \ gen_store2, (void)0)) #define MEM_STORE2(VA, DATA, SLOT) \ - MEM_STORE2_FUNC(DATA)(cpu_env, VA, DATA, ctx, SLOT) + MEM_STORE2_FUNC(DATA)(tcg_env, VA, DATA, SLOT) #define MEM_STORE4_FUNC(X) \ __builtin_choose_expr(TYPE_INT(X), \ @@ -155,7 +163,7 @@ __builtin_choose_expr(TYPE_TCGV(X), \ gen_store4, (void)0)) #define MEM_STORE4(VA, DATA, SLOT) \ - MEM_STORE4_FUNC(DATA)(cpu_env, VA, DATA, ctx, SLOT) + MEM_STORE4_FUNC(DATA)(tcg_env, VA, DATA, SLOT) #define MEM_STORE8_FUNC(X) \ __builtin_choose_expr(TYPE_INT(X), \ @@ -163,44 +171,27 @@ __builtin_choose_expr(TYPE_TCGV_I64(X), \ gen_store8, (void)0)) #define MEM_STORE8(VA, DATA, SLOT) \ - MEM_STORE8_FUNC(DATA)(cpu_env, VA, DATA, ctx, SLOT) + MEM_STORE8_FUNC(DATA)(tcg_env, VA, DATA, SLOT) #else -#define MEM_LOAD1s(VA) ((int8_t)mem_load1(env, slot, VA)) -#define MEM_LOAD1u(VA) ((uint8_t)mem_load1(env, slot, VA)) -#define MEM_LOAD2s(VA) ((int16_t)mem_load2(env, slot, VA)) -#define MEM_LOAD2u(VA) ((uint16_t)mem_load2(env, slot, VA)) -#define MEM_LOAD4s(VA) ((int32_t)mem_load4(env, slot, VA)) -#define MEM_LOAD4u(VA) ((uint32_t)mem_load4(env, slot, VA)) -#define MEM_LOAD8s(VA) ((int64_t)mem_load8(env, slot, VA)) -#define MEM_LOAD8u(VA) ((uint64_t)mem_load8(env, slot, VA)) - #define MEM_STORE1(VA, DATA, SLOT) log_store32(env, VA, DATA, 1, SLOT) #define MEM_STORE2(VA, DATA, SLOT) log_store32(env, VA, DATA, 2, SLOT) #define MEM_STORE4(VA, DATA, SLOT) log_store32(env, VA, DATA, 4, SLOT) #define MEM_STORE8(VA, DATA, SLOT) log_store64(env, VA, DATA, 8, SLOT) #endif -#define CANCEL cancel_slot(env, slot) +#ifdef QEMU_GENERATE +static inline void gen_cancel(uint32_t slot) +{ + tcg_gen_ori_tl(hex_slot_cancelled, hex_slot_cancelled, 1 << slot); +} + +#define CANCEL gen_cancel(slot); +#else +#define CANCEL do { } while (0) +#endif #define LOAD_CANCEL(EA) do { CANCEL; } while (0) -#ifdef QEMU_GENERATE -static inline void gen_pred_cancel(TCGv pred, int slot_num) - { - TCGv slot_mask = tcg_temp_new(); - TCGv tmp = tcg_temp_new(); - TCGv zero = tcg_constant_tl(0); - tcg_gen_ori_tl(slot_mask, hex_slot_cancelled, 1 << slot_num); - tcg_gen_andi_tl(tmp, pred, 1); - tcg_gen_movcond_tl(TCG_COND_EQ, hex_slot_cancelled, tmp, zero, - slot_mask, hex_slot_cancelled); - tcg_temp_free(slot_mask); - tcg_temp_free(tmp); -} -#define PRED_LOAD_CANCEL(PRED, EA) \ - gen_pred_cancel(PRED, insn->is_endloop ? 4 : insn->slot) -#endif - #define STORE_CANCEL(EA) { env->slot_cancelled |= (1 << slot); } #define fMAX(A, B) (((A) > (B)) ? (A) : (B)) @@ -236,12 +227,8 @@ static inline void gen_pred_cancel(TCGv pred, int slot_num) #ifdef QEMU_GENERATE #define fLSBNEW(PVAL) tcg_gen_andi_tl(LSB, (PVAL), 1) -#define fLSBNEW0 tcg_gen_andi_tl(LSB, hex_new_pred_value[0], 1) -#define fLSBNEW1 tcg_gen_andi_tl(LSB, hex_new_pred_value[1], 1) #else #define fLSBNEW(PVAL) ((PVAL) & 1) -#define fLSBNEW0 (env->new_pred_value[0] & 1) -#define fLSBNEW1 (env->new_pred_value[1] & 1) #endif #ifdef QEMU_GENERATE @@ -350,81 +337,40 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) tcg_gen_deposit_tl(result, msb, lsb, 0, 7); tcg_gen_shli_tl(result, result, shift); - - tcg_temp_free(msb); - tcg_temp_free(lsb); - return result; } -#define fREAD_IREG(VAL, SHIFT) gen_read_ireg(ireg, (VAL), (SHIFT)) -#else -#define fREAD_IREG(VAL) \ - (fSXTN(11, 64, (((VAL) & 0xf0000000) >> 21) | ((VAL >> 17) & 0x7f))) #endif -#define fREAD_LR() (READ_REG(HEX_REG_LR)) +#define fREAD_LR() (env->gpr[HEX_REG_LR]) -#define fWRITE_LR(A) WRITE_RREG(HEX_REG_LR, A) -#define fWRITE_FP(A) WRITE_RREG(HEX_REG_FP, A) -#define fWRITE_SP(A) WRITE_RREG(HEX_REG_SP, A) - -#define fREAD_SP() (READ_REG(HEX_REG_SP)) -#define fREAD_LC0 (READ_REG(HEX_REG_LC0)) -#define fREAD_LC1 (READ_REG(HEX_REG_LC1)) -#define fREAD_SA0 (READ_REG(HEX_REG_SA0)) -#define fREAD_SA1 (READ_REG(HEX_REG_SA1)) -#define fREAD_FP() (READ_REG(HEX_REG_FP)) +#define fREAD_SP() (env->gpr[HEX_REG_SP]) +#define fREAD_LC0 (env->gpr[HEX_REG_LC0]) +#define fREAD_LC1 (env->gpr[HEX_REG_LC1]) +#define fREAD_SA0 (env->gpr[HEX_REG_SA0]) +#define fREAD_SA1 (env->gpr[HEX_REG_SA1]) +#define fREAD_FP() (env->gpr[HEX_REG_FP]) #ifdef FIXME /* Figure out how to get insn->extension_valid to helper */ #define fREAD_GP() \ - (insn->extension_valid ? 0 : READ_REG(HEX_REG_GP)) + (insn->extension_valid ? 0 : env->gpr[HEX_REG_GP]) #else -#define fREAD_GP() READ_REG(HEX_REG_GP) +#define fREAD_GP() (env->gpr[HEX_REG_GP]) #endif -#define fREAD_PC() (READ_REG(HEX_REG_PC)) +#define fREAD_PC() (PC) -#define fREAD_NPC() (env->next_PC & (0xfffffffe)) - -#define fREAD_P0() (READ_PREG(0)) -#define fREAD_P3() (READ_PREG(3)) +#define fREAD_P0() (env->pred[0]) #define fCHECK_PCALIGN(A) -#define fWRITE_NPC(A) write_new_pc(env, A) +#define fWRITE_NPC(A) write_new_pc(env, pkt_has_multi_cof != 0, A) #define fBRANCH(LOC, TYPE) fWRITE_NPC(LOC) #define fJUMPR(REGNO, TARGET, TYPE) fBRANCH(TARGET, COF_TYPE_JUMPR) #define fHINTJR(TARGET) { /* Not modelled in qemu */} -#define fCALL(A) \ - do { \ - fWRITE_LR(fREAD_NPC()); \ - fBRANCH(A, COF_TYPE_CALL); \ - } while (0) -#define fCALLR(A) \ - do { \ - fWRITE_LR(fREAD_NPC()); \ - fBRANCH(A, COF_TYPE_CALLR); \ - } while (0) -#define fWRITE_LOOP_REGS0(START, COUNT) \ - do { \ - WRITE_RREG(HEX_REG_LC0, COUNT); \ - WRITE_RREG(HEX_REG_SA0, START); \ - } while (0) -#define fWRITE_LOOP_REGS1(START, COUNT) \ - do { \ - WRITE_RREG(HEX_REG_LC1, COUNT); \ - WRITE_RREG(HEX_REG_SA1, START);\ - } while (0) -#define fWRITE_LC0(VAL) WRITE_RREG(HEX_REG_LC0, VAL) -#define fWRITE_LC1(VAL) WRITE_RREG(HEX_REG_LC1, VAL) #define fSET_OVERFLOW() SET_USR_FIELD(USR_OVF, 1) #define fSET_LPCFG(VAL) SET_USR_FIELD(USR_LPCFG, (VAL)) #define fGET_LPCFG (GET_USR_FIELD(USR_LPCFG)) -#define fWRITE_P0(VAL) WRITE_PREG(0, VAL) -#define fWRITE_P1(VAL) WRITE_PREG(1, VAL) -#define fWRITE_P2(VAL) WRITE_PREG(2, VAL) -#define fWRITE_P3(VAL) WRITE_PREG(3, VAL) #define fPART1(WORK) if (part1) { WORK; return; } #define fCAST4u(A) ((uint32_t)(A)) #define fCAST4s(A) ((int32_t)(A)) @@ -486,7 +432,6 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) TCGv tmp = tcg_temp_new(); \ tcg_gen_shli_tl(tmp, REG2, SCALE); \ tcg_gen_add_tl(EA, REG, tmp); \ - tcg_temp_free(tmp); \ } while (0) #define fEA_IRs(IMM, REG, SCALE) \ do { \ @@ -517,8 +462,7 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #define fPM_CIRI(REG, IMM, MVAL) \ do { \ TCGv tcgv_siV = tcg_constant_tl(siV); \ - gen_helper_fcircadd(REG, REG, tcgv_siV, MuV, \ - hex_gpr[HEX_REG_CS0 + MuN]); \ + gen_helper_fcircadd(REG, REG, tcgv_siV, MuV, CS); \ } while (0) #else #define fEA_IMM(IMM) do { EA = (IMM); } while (0) @@ -576,13 +520,21 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #ifdef QEMU_GENERATE #define fLOAD(NUM, SIZE, SIGN, EA, DST) MEM_LOAD##SIZE##SIGN(DST, EA) #else +#define MEM_LOAD1 cpu_ldub_data_ra +#define MEM_LOAD2 cpu_lduw_data_ra +#define MEM_LOAD4 cpu_ldl_data_ra +#define MEM_LOAD8 cpu_ldq_data_ra + #define fLOAD(NUM, SIZE, SIGN, EA, DST) \ - DST = (size##SIZE##SIGN##_t)MEM_LOAD##SIZE##SIGN(EA) + do { \ + check_noshuf(env, pkt_has_store_s1, slot, EA, SIZE, GETPC()); \ + DST = (size##SIZE##SIGN##_t)MEM_LOAD##SIZE(env, EA, GETPC()); \ + } while (0) #endif #define fMEMOP(NUM, SIZE, SIGN, EA, FNTYPE, VALUE) -#define fGET_FRAMEKEY() READ_REG(HEX_REG_FRAMEKEY) +#define fGET_FRAMEKEY() (env->gpr[HEX_REG_FRAMEKEY]) #define fFRAME_SCRAMBLE(VAL) ((VAL) ^ (fCAST8u(fGET_FRAMEKEY()) << 32)) #define fFRAME_UNSCRAMBLE(VAL) fFRAME_SCRAMBLE(VAL) @@ -692,22 +644,14 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) fEXTRACTU_BITS(env->gpr[HEX_REG_##REG], \ reg_field_info[FIELD].width, \ reg_field_info[FIELD].offset) -#define fGET_FIELD(VAL, FIELD) -#define fSET_FIELD(VAL, FIELD, NEWVAL) -#define fBARRIER() -#define fSYNCH() -#define fISYNC() -#define fDCFETCH(REG) \ - do { (void)REG; } while (0) /* Nothing to do in qemu */ -#define fICINVA(REG) \ - do { (void)REG; } while (0) /* Nothing to do in qemu */ -#define fL2FETCH(ADDR, HEIGHT, WIDTH, STRIDE, FLAGS) -#define fDCCLEANA(REG) \ - do { (void)REG; } while (0) /* Nothing to do in qemu */ -#define fDCCLEANINVA(REG) \ - do { (void)REG; } while (0) /* Nothing to do in qemu */ -#define fDCZEROA(REG) do { env->dczero_addr = (REG); } while (0) +#ifdef QEMU_GENERATE +#define fDCZEROA(REG) \ + do { \ + ctx->dczero_addr = tcg_temp_new(); \ + tcg_gen_mov_tl(ctx->dczero_addr, (REG)); \ + } while (0) +#endif #define fBRANCH_SPECULATE_STALL(DOTNEWVAL, JUMP_COND, SPEC_DIR, HINTBITNUM, \ STRBITNUM) /* Nothing */ diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index b61243103f..fb480afc03 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -1,5 +1,5 @@ ## -## Copyright(c) 2020-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2020-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ hex_common_py = 'hex_common.py' attribs_def = meson.current_source_dir() / 'attribs_def.h.inc' gen_tcg_h = meson.current_source_dir() / 'gen_tcg.h' gen_tcg_hvx_h = meson.current_source_dir() / 'gen_tcg_hvx.h' +idef_parser_dir = meson.current_source_dir() / 'idef-parser' # # Step 1 @@ -42,10 +43,7 @@ hexagon_ss.add(semantics_generated) # Step 2 # We use Python scripts to generate the following files # shortcode_generated.h.inc -# helper_protos_generated.h.inc -# tcg_funcs_generated.c.inc # tcg_func_table_generated.c.inc -# helper_funcs_generated.c.inc # printinsn_generated.h.inc # op_regs_generated.h.inc # op_attribs_generated.h.inc @@ -60,24 +58,6 @@ shortcode_generated = custom_target( ) hexagon_ss.add(shortcode_generated) -helper_protos_generated = custom_target( - 'helper_protos_generated.h.inc', - output: 'helper_protos_generated.h.inc', - depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], - command: [python, files('gen_helper_protos.py'), semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h, '@OUTPUT@'], -) -hexagon_ss.add(helper_protos_generated) - -tcg_funcs_generated = custom_target( - 'tcg_funcs_generated.c.inc', - output: 'tcg_funcs_generated.c.inc', - depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], - command: [python, files('gen_tcg_funcs.py'), semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h, '@OUTPUT@'], -) -hexagon_ss.add(tcg_funcs_generated) - tcg_func_table_generated = custom_target( 'tcg_func_table_generated.c.inc', output: 'tcg_func_table_generated.c.inc', @@ -87,15 +67,6 @@ tcg_func_table_generated = custom_target( ) hexagon_ss.add(tcg_func_table_generated) -helper_funcs_generated = custom_target( - 'helper_funcs_generated.c.inc', - output: 'helper_funcs_generated.c.inc', - depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], - command: [python, files('gen_helper_funcs.py'), semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h, '@OUTPUT@'], -) -hexagon_ss.add(helper_funcs_generated) - printinsn_generated = custom_target( 'printinsn_generated.h.inc', output: 'printinsn_generated.h.inc', @@ -151,16 +122,149 @@ hexagon_ss.add(iset_py) # # Step 4 -# We use the dectree.py script to generate the decode tree header file +# Generate the input to the QEMU decodetree.py script # -dectree_generated = custom_target( - 'dectree_generated.h.inc', - output: 'dectree_generated.h.inc', - depends: [iset_py], +normal_decode_generated = custom_target( + 'normal_decode_generated', + output: 'normal_decode_generated', + depends: [iset_py, semantics_generated], env: {'PYTHONPATH': meson.current_build_dir()}, - command: [python, files('dectree.py'), '@OUTPUT@'], + command: [python, files('gen_decodetree.py'), semantics_generated, 'NORMAL', '@OUTPUT@'], ) -hexagon_ss.add(dectree_generated) +hexagon_ss.add(normal_decode_generated) + +hvx_decode_generated = custom_target( + 'hvx_decode_generated', + output: 'hvx_decode_generated', + depends: [iset_py, semantics_generated], + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files('gen_decodetree.py'), semantics_generated, 'EXT_mmvec', '@OUTPUT@'], +) +hexagon_ss.add(hvx_decode_generated) + +subinsn_a_decode_generated = custom_target( + 'subinsn_a_decode_generated', + output: 'subinsn_a_decode_generated', + depends: [iset_py, semantics_generated], + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files('gen_decodetree.py'), semantics_generated, 'SUBINSN_A', '@OUTPUT@'], +) +hexagon_ss.add(subinsn_a_decode_generated) + +subinsn_l1_decode_generated = custom_target( + 'subinsn_l1_decode_generated', + output: 'subinsn_l1_decode_generated', + depends: [iset_py, semantics_generated], + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files('gen_decodetree.py'), semantics_generated, 'SUBINSN_L1', '@OUTPUT@'], +) +hexagon_ss.add(subinsn_l1_decode_generated) + +subinsn_l2_decode_generated = custom_target( + 'subinsn_l2_decode_generated', + output: 'subinsn_l2_decode_generated', + depends: [iset_py, semantics_generated], + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files('gen_decodetree.py'), semantics_generated, 'SUBINSN_L2', '@OUTPUT@'], +) +hexagon_ss.add(subinsn_l2_decode_generated) + +subinsn_s1_decode_generated = custom_target( + 'subinsn_s1_decode_generated', + output: 'subinsn_s1_decode_generated', + depends: [iset_py, semantics_generated], + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files('gen_decodetree.py'), semantics_generated, 'SUBINSN_S1', '@OUTPUT@'], +) +hexagon_ss.add(subinsn_s1_decode_generated) + +subinsn_s2_decode_generated = custom_target( + 'subinsn_s2_decode_generated', + output: 'subinsn_s2_decode_generated', + depends: [iset_py, semantics_generated], + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files('gen_decodetree.py'), semantics_generated, 'SUBINSN_S2', '@OUTPUT@'], +) +hexagon_ss.add(subinsn_s2_decode_generated) + +# +# Run the QEMU decodetree.py script to produce the instruction decoder +# +decodetree_py = meson.current_source_dir() / '../../scripts/decodetree.py' +decode_normal_generated = custom_target( + 'decode_normal_generated.c.inc', + output: 'decode_normal_generated.c.inc', + input: normal_decode_generated, + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files(decodetree_py), normal_decode_generated, '--static-decode=decode_normal', '-o', '@OUTPUT@'], +) +hexagon_ss.add(decode_normal_generated) + +decode_hvx_generated = custom_target( + 'decode_hvx_generated.c.inc', + output: 'decode_hvx_generated.c.inc', + input: hvx_decode_generated, + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files(decodetree_py), hvx_decode_generated, '--static-decode=decode_hvx', '-o', '@OUTPUT@'], +) +hexagon_ss.add(decode_hvx_generated) + +decode_subinsn_a_generated = custom_target( + 'decode_subinsn_a_generated.c.inc', + output: 'decode_subinsn_a_generated.c.inc', + input: subinsn_a_decode_generated, + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files(decodetree_py), subinsn_a_decode_generated, ['--static-decode=decode_subinsn_a', '--insnwidth=16'], '-o', '@OUTPUT@'], +) +hexagon_ss.add(decode_subinsn_a_generated) + +decode_subinsn_l1_generated = custom_target( + 'decode_subinsn_l1_generated.c.inc', + output: 'decode_subinsn_l1_generated.c.inc', + input: subinsn_l1_decode_generated, + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files(decodetree_py), subinsn_l1_decode_generated, ['--static-decode=decode_subinsn_l1', '--insnwidth=16'], '-o', '@OUTPUT@'], +) +hexagon_ss.add(decode_subinsn_l1_generated) + +decode_subinsn_l2_generated = custom_target( + 'decode_subinsn_l2_generated.c.inc', + output: 'decode_subinsn_l2_generated.c.inc', + input: subinsn_l2_decode_generated, + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files(decodetree_py), subinsn_l2_decode_generated, ['--static-decode=decode_subinsn_l2', '--insnwidth=16'], '-o', '@OUTPUT@'], +) +hexagon_ss.add(decode_subinsn_l2_generated) + +decode_subinsn_s1_generated = custom_target( + 'decode_subinsn_s1_generated.c.inc', + output: 'decode_subinsn_s1_generated.c.inc', + input: subinsn_s1_decode_generated, + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files(decodetree_py), subinsn_s1_decode_generated, ['--static-decode=decode_subinsn_s1', '--insnwidth=16'], '-o', '@OUTPUT@'], +) +hexagon_ss.add(decode_subinsn_s1_generated) + +decode_subinsn_s2_generated = custom_target( + 'decode_subinsn_s2_generated.c.inc', + output: 'decode_subinsn_s2_generated.c.inc', + input: subinsn_s2_decode_generated, + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files(decodetree_py), subinsn_s2_decode_generated, ['--static-decode=decode_subinsn_s2', '--insnwidth=16'], '-o', '@OUTPUT@'], +) +hexagon_ss.add(decode_subinsn_s2_generated) + +# +# Generate the trans_* functions that the decoder will use +# +decodetree_trans_funcs_generated = custom_target( + 'decodetree_trans_funcs_generated.c.inc', + output: 'decodetree_trans_funcs_generated.c.inc', + depends: [iset_py, semantics_generated], + env: {'PYTHONPATH': meson.current_build_dir()}, + command: [python, files('gen_trans_funcs.py'), semantics_generated, '@OUTPUT@'], +) +hexagon_ss.add(decodetree_trans_funcs_generated) hexagon_ss.add(files( 'cpu.c', @@ -179,4 +283,139 @@ hexagon_ss.add(files( 'mmvec/system_ext_mmvec.c', )) +# +# Step 4.5 +# We use flex/bison based idef-parser to generate TCG code for a lot +# of instructions. idef-parser outputs +# idef-generated-emitter.c +# idef-generated-emitter.h.inc +# idef-generated-enabled-instructions +# +idef_parser_enabled = get_option('hexagon_idef_parser') +if idef_parser_enabled and 'hexagon-linux-user' in target_dirs + idef_parser_input_generated = custom_target( + 'idef_parser_input.h.inc', + output: 'idef_parser_input.h.inc', + depends: [semantics_generated], + depend_files: [hex_common_py], + command: [python, files('gen_idef_parser_funcs.py'), semantics_generated, attribs_def, '@OUTPUT@'], + ) + + preprocessed_idef_parser_input_generated = custom_target( + 'idef_parser_input.preprocessed.h.inc', + output: 'idef_parser_input.preprocessed.h.inc', + input: idef_parser_input_generated, + depend_files: [idef_parser_dir / 'macros.inc'], + command: [idef_parser_dir / 'prepare', '@INPUT@', '-I' + idef_parser_dir, '-o', '@OUTPUT@'], + ) + + flex = generator( + find_program('flex'), + output: ['@BASENAME@.yy.c', '@BASENAME@.yy.h'], + arguments: ['-o', '@OUTPUT0@', '--header-file=@OUTPUT1@', '@INPUT@'] + ) + + bison = generator( + find_program('bison', version: '>=3.0'), + output: ['@BASENAME@.tab.c', '@BASENAME@.tab.h'], + arguments: ['@INPUT@', '--defines=@OUTPUT1@', '--output=@OUTPUT0@'] + ) + + glib_dep = dependency('glib-2.0', native: true) + + idef_parser = executable( + 'idef-parser', + [flex.process(idef_parser_dir / 'idef-parser.lex'), + bison.process(idef_parser_dir / 'idef-parser.y'), + idef_parser_dir / 'parser-helpers.c'], + include_directories: ['idef-parser', '../../include/'], + dependencies: [glib_dep], + native: true + ) + + idef_generated_tcg = custom_target( + 'idef-generated-tcg', + output: ['idef-generated-emitter.c', + 'idef-generated-emitter.h.inc', + 'idef-generated-enabled-instructions'], + input: preprocessed_idef_parser_input_generated, + depend_files: [hex_common_py], + command: [idef_parser, '@INPUT@', '@OUTPUT0@', '@OUTPUT1@', '@OUTPUT2@'] + ) + + indent = find_program('indent', required: false) + if indent.found() + idef_generated_tcg_c = custom_target( + 'indent', + input: idef_generated_tcg[0], + output: 'idef-generated-emitter.indented.c', + command: [indent, '-linux', '@INPUT@', '-o', '@OUTPUT@'] + ) + else + idef_generated_tcg_c = custom_target( + 'copy', + input: idef_generated_tcg[0], + output: 'idef-generated-emitter.indented.c', + command: ['cp', '@INPUT@', '@OUTPUT@'] + ) + endif + + idef_generated_list = idef_generated_tcg[2].full_path() + + hexagon_ss.add(idef_generated_tcg_c) + + # Setup input and dependencies for the next step, this depends on whether or + # not idef-parser is enabled + helper_dep = [semantics_generated, idef_generated_tcg_c, idef_generated_tcg] + helper_in = [semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h, idef_generated_list] +else + # Setup input and dependencies for the next step, this depends on whether or + # not idef-parser is enabled + helper_dep = [semantics_generated] + helper_in = [semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h] +endif + +# +# Step 5 +# We use Python scripts to generate the following files +# helper_protos_generated.h.inc +# helper_funcs_generated.c.inc +# tcg_funcs_generated.c.inc +# +helper_protos_generated = custom_target( + 'helper_protos_generated.h.inc', + output: 'helper_protos_generated.h.inc', + depends: helper_dep, + depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + command: [python, files('gen_helper_protos.py'), helper_in, '@OUTPUT@'], +) +hexagon_ss.add(helper_protos_generated) + +helper_funcs_generated = custom_target( + 'helper_funcs_generated.c.inc', + output: 'helper_funcs_generated.c.inc', + depends: helper_dep, + depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + command: [python, files('gen_helper_funcs.py'), helper_in, '@OUTPUT@'], +) +hexagon_ss.add(helper_funcs_generated) + +tcg_funcs_generated = custom_target( + 'tcg_funcs_generated.c.inc', + output: 'tcg_funcs_generated.c.inc', + depends: helper_dep, + depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + command: [python, files('gen_tcg_funcs.py'), helper_in, '@OUTPUT@'], +) +hexagon_ss.add(tcg_funcs_generated) + +analyze_funcs_generated = custom_target( + 'analyze_funcs_generated.c.inc', + output: 'analyze_funcs_generated.c.inc', + depends: helper_dep, + depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + command: [python, files('gen_analyze_funcs.py'), helper_in, '@OUTPUT@'], +) +hexagon_ss.add(analyze_funcs_generated) + target_arch += {'hexagon': hexagon_ss} diff --git a/target/hexagon/mmvec/decode_ext_mmvec.c b/target/hexagon/mmvec/decode_ext_mmvec.c index 061a65ab88..202d84c7c0 100644 --- a/target/hexagon/mmvec/decode_ext_mmvec.c +++ b/target/hexagon/mmvec/decode_ext_mmvec.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,7 +33,6 @@ check_new_value(Packet *pkt) const char *dststr = NULL; uint16_t def_opcode; char letter; - int def_regnum; for (i = 1; i < pkt->num_insns; i++) { uint16_t use_opcode = pkt->insn[i].opcode; @@ -78,7 +77,6 @@ check_new_value(Packet *pkt) } } if ((dststr == NULL) && GET_ATTRIB(def_opcode, A_CVI_GATHER)) { - def_regnum = 0; pkt->insn[i].regno[use_regidx] = def_oreg; pkt->insn[i].new_value_producer_slot = pkt->insn[def_idx].slot; } else { @@ -86,7 +84,7 @@ check_new_value(Packet *pkt) /* still not there, we have a bad packet */ g_assert_not_reached(); } - def_regnum = pkt->insn[def_idx].regno[dststr - reginfo]; + int def_regnum = pkt->insn[def_idx].regno[dststr - reginfo]; /* Now patch up the consumer with the register number */ pkt->insn[i].regno[use_regidx] = def_regnum ^ def_oreg; /* special case for (Vx,Vy) */ @@ -148,9 +146,9 @@ decode_shuffle_for_execution_vops(Packet *pkt) int i; for (i = 0; i < pkt->num_insns; i++) { uint16_t opcode = pkt->insn[i].opcode; - if (GET_ATTRIB(opcode, A_LOAD) && - (GET_ATTRIB(opcode, A_CVI_NEW) || - GET_ATTRIB(opcode, A_CVI_TMP))) { + if ((GET_ATTRIB(opcode, A_LOAD) && + GET_ATTRIB(opcode, A_CVI_NEW)) || + GET_ATTRIB(opcode, A_CVI_TMP)) { /* * Find prior consuming vector instructions * Move to end of packet diff --git a/target/hexagon/mmvec/macros.h b/target/hexagon/mmvec/macros.h index 8345753580..1ceb9453ee 100644 --- a/target/hexagon/mmvec/macros.h +++ b/target/hexagon/mmvec/macros.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,7 +18,6 @@ #ifndef HEXAGON_MMVEC_MACROS_H #define HEXAGON_MMVEC_MACROS_H -#include "qemu/osdep.h" #include "qemu/host-utils.h" #include "arch.h" #include "mmvec/system_ext_mmvec.h" @@ -202,7 +201,7 @@ } while (0) #define SCATTER_OP_WRITE_TO_MEM(TYPE) \ do { \ - uintptr_t ra = GETPC(); \ + ra = GETPC(); \ for (int i = 0; i < sizeof(MMVector); i += sizeof(TYPE)) { \ if (test_bit(i, env->vtcm_log.mask)) { \ TYPE dst = 0; \ @@ -288,7 +287,7 @@ #endif #ifdef QEMU_GENERATE #define fSTOREMMV(EA, SRC) \ - gen_vreg_store(ctx, insn, pkt, EA, SRC##_off, insn->slot, true) + gen_vreg_store(ctx, EA, SRC##_off, insn->slot, true) #endif #ifdef QEMU_GENERATE #define fSTOREMMVQ(EA, SRC, MASK) \ @@ -300,7 +299,7 @@ #endif #ifdef QEMU_GENERATE #define fSTOREMMVU(EA, SRC) \ - gen_vreg_store(ctx, insn, pkt, EA, SRC##_off, insn->slot, false) + gen_vreg_store(ctx, EA, SRC##_off, insn->slot, false) #endif #define fVFOREACH(WIDTH, VAR) for (VAR = 0; VAR < fVELEM(WIDTH); VAR++) #define fVARRAY_ELEMENT_ACCESS(ARRAY, TYPE, INDEX) \ @@ -347,4 +346,11 @@ #define fUARCH_NOTE_PUMP_2X() #define IV1DEAD() + +#define fGET10BIT(COE, VAL, POS) \ + do { \ + COE = (sextract32(VAL, 24 + 2 * POS, 2) << 8) | \ + extract32(VAL, POS * 8, 8); \ + } while (0); + #endif diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index a5ed819c04..da10ac5847 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,6 +29,8 @@ #include "fma_emu.h" #include "mmvec/mmvec.h" #include "mmvec/macros.h" +#include "op_helper.h" +#include "translate.h" #define SF_BIAS 127 #define SF_MANTBITS 23 @@ -50,40 +52,8 @@ G_NORETURN void HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp) do_raise_exception_err(env, excp, 0); } -static void log_reg_write(CPUHexagonState *env, int rnum, - target_ulong val, uint32_t slot) -{ - HEX_DEBUG_LOG("log_reg_write[%d] = " TARGET_FMT_ld " (0x" TARGET_FMT_lx ")", - rnum, val, val); - if (val == env->gpr[rnum]) { - HEX_DEBUG_LOG(" NO CHANGE"); - } - HEX_DEBUG_LOG("\n"); - - env->new_value[rnum] = val; - if (HEX_DEBUG) { - /* Do this so HELPER(debug_commit_end) will know */ - env->reg_written[rnum] = 1; - } -} - -static void log_pred_write(CPUHexagonState *env, int pnum, target_ulong val) -{ - HEX_DEBUG_LOG("log_pred_write[%d] = " TARGET_FMT_ld - " (0x" TARGET_FMT_lx ")\n", - pnum, val, val); - - /* Multiple writes to the same preg are and'ed together */ - if (env->pred_written & (1 << pnum)) { - env->new_pred_value[pnum] &= val & 0xff; - } else { - env->new_pred_value[pnum] = val & 0xff; - env->pred_written |= 1 << pnum; - } -} - -static void log_store32(CPUHexagonState *env, target_ulong addr, - target_ulong val, int width, int slot) +void log_store32(CPUHexagonState *env, target_ulong addr, + target_ulong val, int width, int slot) { HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx ", %" PRId32 " [0x08%" PRIx32 "])\n", @@ -93,8 +63,8 @@ static void log_store32(CPUHexagonState *env, target_ulong addr, env->mem_log_stores[slot].data32 = val; } -static void log_store64(CPUHexagonState *env, target_ulong addr, - int64_t val, int width, int slot) +void log_store64(CPUHexagonState *env, target_ulong addr, + int64_t val, int width, int slot) { HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx ", %" PRId64 " [0x016%" PRIx64 "])\n", @@ -104,24 +74,6 @@ static void log_store64(CPUHexagonState *env, target_ulong addr, env->mem_log_stores[slot].data64 = val; } -static void write_new_pc(CPUHexagonState *env, target_ulong addr) -{ - HEX_DEBUG_LOG("write_new_pc(0x" TARGET_FMT_lx ")\n", addr); - - /* - * If more than one branch is taken in a packet, only the first one - * is actually done. - */ - if (env->branch_taken) { - HEX_DEBUG_LOG("INFO: multiple branches taken in same packet, " - "ignoring the second one\n"); - } else { - fCHECK_PCALIGN(addr); - env->branch_taken = 1; - env->next_PC = addr; - } -} - /* Handy place to set a breakpoint */ void HELPER(debug_start_packet)(CPUHexagonState *env) { @@ -143,9 +95,8 @@ void HELPER(debug_check_store_width)(CPUHexagonState *env, int slot, int check) } } -void HELPER(commit_store)(CPUHexagonState *env, int slot_num) +static void commit_store(CPUHexagonState *env, int slot_num, uintptr_t ra) { - uintptr_t ra = GETPC(); uint8_t width = env->mem_log_stores[slot_num].width; target_ulong va = env->mem_log_stores[slot_num].va; @@ -167,6 +118,12 @@ void HELPER(commit_store)(CPUHexagonState *env, int slot_num) } } +void HELPER(commit_store)(CPUHexagonState *env, int slot_num) +{ + uintptr_t ra = GETPC(); + commit_store(env, slot_num, ra); +} + void HELPER(gather_store)(CPUHexagonState *env, uint32_t addr, int slot) { mem_gather_store(env, addr, slot); @@ -175,10 +132,9 @@ void HELPER(gather_store)(CPUHexagonState *env, uint32_t addr, int slot) void HELPER(commit_hvx_stores)(CPUHexagonState *env) { uintptr_t ra = GETPC(); - int i; /* Normal (possibly masked) vector store */ - for (i = 0; i < VSTORES_MAX; i++) { + for (int i = 0; i < VSTORES_MAX; i++) { if (env->vstore_pending[i]) { env->vstore_pending[i] = 0; target_ulong va = env->vstore[i].va; @@ -205,7 +161,7 @@ void HELPER(commit_hvx_stores)(CPUHexagonState *env) g_assert_not_reached(); } } else { - for (i = 0; i < sizeof(MMVector); i++) { + for (int i = 0; i < sizeof(MMVector); i++) { if (test_bit(i, env->vtcm_log.mask)) { cpu_stb_data_ra(env, env->vtcm_log.va[i], env->vtcm_log.data.ub[i], ra); @@ -251,14 +207,14 @@ static void print_store(CPUHexagonState *env, int slot) } /* This function is a handy place to set a breakpoint */ -void HELPER(debug_commit_end)(CPUHexagonState *env, int has_st0, int has_st1) +void HELPER(debug_commit_end)(CPUHexagonState *env, uint32_t this_PC, + int pred_written, int has_st0, int has_st1) { bool reg_printed = false; bool pred_printed = false; int i; - HEX_DEBUG_LOG("Packet committed: pc = 0x" TARGET_FMT_lx "\n", - env->this_PC); + HEX_DEBUG_LOG("Packet committed: pc = 0x" TARGET_FMT_lx "\n", this_PC); HEX_DEBUG_LOG("slot_cancelled = %d\n", env->slot_cancelled); for (i = 0; i < TOTAL_PER_THREAD_REGS; i++) { @@ -268,18 +224,18 @@ void HELPER(debug_commit_end)(CPUHexagonState *env, int has_st0, int has_st1) reg_printed = true; } HEX_DEBUG_LOG("\tr%d = " TARGET_FMT_ld " (0x" TARGET_FMT_lx ")\n", - i, env->new_value[i], env->new_value[i]); + i, env->gpr[i], env->gpr[i]); } } for (i = 0; i < NUM_PREGS; i++) { - if (env->pred_written & (1 << i)) { + if (pred_written & (1 << i)) { if (!pred_printed) { HEX_DEBUG_LOG("Predicates written\n"); pred_printed = true; } HEX_DEBUG_LOG("\tp%d = 0x" TARGET_FMT_lx "\n", - i, env->new_pred_value[i]); + i, env->pred[i]); } } @@ -293,7 +249,7 @@ void HELPER(debug_commit_end)(CPUHexagonState *env, int has_st0, int has_st1) } } - HEX_DEBUG_LOG("Next PC = " TARGET_FMT_lx "\n", env->next_PC); + HEX_DEBUG_LOG("Next PC = " TARGET_FMT_lx "\n", env->gpr[HEX_REG_PC]); HEX_DEBUG_LOG("Exec counters: pkt = " TARGET_FMT_lx ", insn = " TARGET_FMT_lx ", hvx = " TARGET_FMT_lx "\n", @@ -400,7 +356,8 @@ uint64_t HELPER(sfinvsqrta)(CPUHexagonState *env, float32 RsV) } int64_t HELPER(vacsh_val)(CPUHexagonState *env, - int64_t RxxV, int64_t RssV, int64_t RttV) + int64_t RxxV, int64_t RssV, int64_t RttV, + uint32_t pkt_need_commit) { for (int i = 0; i < 4; i++) { int xv = sextract64(RxxV, i * 16, 16); @@ -432,29 +389,123 @@ int32_t HELPER(vacsh_pred)(CPUHexagonState *env, return PeV; } -static void probe_store(CPUHexagonState *env, int slot, int mmu_idx) +int64_t HELPER(cabacdecbin_val)(int64_t RssV, int64_t RttV) { - if (!(env->slot_cancelled & (1 << slot))) { + int64_t RddV = 0; + size4u_t state; + size4u_t valMPS; + size4u_t bitpos; + size4u_t range; + size4u_t offset; + size4u_t rLPS; + size4u_t rMPS; + + state = fEXTRACTU_RANGE(fGETWORD(1, RttV), 5, 0); + valMPS = fEXTRACTU_RANGE(fGETWORD(1, RttV), 8, 8); + bitpos = fEXTRACTU_RANGE(fGETWORD(0, RttV), 4, 0); + range = fGETWORD(0, RssV); + offset = fGETWORD(1, RssV); + + /* calculate rLPS */ + range <<= bitpos; + offset <<= bitpos; + rLPS = rLPS_table_64x4[state][(range >> 29) & 3]; + rLPS = rLPS << 23; /* left aligned */ + + /* calculate rMPS */ + rMPS = (range & 0xff800000) - rLPS; + + /* most probable region */ + if (offset < rMPS) { + RddV = AC_next_state_MPS_64[state]; + fINSERT_RANGE(RddV, 8, 8, valMPS); + fINSERT_RANGE(RddV, 31, 23, (rMPS >> 23)); + fSETWORD(1, RddV, offset); + } + /* least probable region */ + else { + RddV = AC_next_state_LPS_64[state]; + fINSERT_RANGE(RddV, 8, 8, ((!state) ? (1 - valMPS) : (valMPS))); + fINSERT_RANGE(RddV, 31, 23, (rLPS >> 23)); + fSETWORD(1, RddV, (offset - rMPS)); + } + return RddV; +} + +int32_t HELPER(cabacdecbin_pred)(int64_t RssV, int64_t RttV) +{ + int32_t p0 = 0; + size4u_t state; + size4u_t valMPS; + size4u_t bitpos; + size4u_t range; + size4u_t offset; + size4u_t rLPS; + size4u_t rMPS; + + state = fEXTRACTU_RANGE(fGETWORD(1, RttV), 5, 0); + valMPS = fEXTRACTU_RANGE(fGETWORD(1, RttV), 8, 8); + bitpos = fEXTRACTU_RANGE(fGETWORD(0, RttV), 4, 0); + range = fGETWORD(0, RssV); + offset = fGETWORD(1, RssV); + + /* calculate rLPS */ + range <<= bitpos; + offset <<= bitpos; + rLPS = rLPS_table_64x4[state][(range >> 29) & 3]; + rLPS = rLPS << 23; /* left aligned */ + + /* calculate rMPS */ + rMPS = (range & 0xff800000) - rLPS; + + /* most probable region */ + if (offset < rMPS) { + p0 = valMPS; + + } + /* least probable region */ + else { + p0 = valMPS ^ 1; + } + return p0; +} + +static void probe_store(CPUHexagonState *env, int slot, int mmu_idx, + bool is_predicated, uintptr_t retaddr) +{ + if (!is_predicated || !(env->slot_cancelled & (1 << slot))) { size1u_t width = env->mem_log_stores[slot].width; target_ulong va = env->mem_log_stores[slot].va; - uintptr_t ra = GETPC(); - probe_write(env, va, width, mmu_idx, ra); + probe_write(env, va, width, mmu_idx, retaddr); } } -/* Called during packet commit when there are two scalar stores */ -void HELPER(probe_pkt_scalar_store_s0)(CPUHexagonState *env, int mmu_idx) -{ - probe_store(env, 0, mmu_idx); -} - -void HELPER(probe_hvx_stores)(CPUHexagonState *env, int mmu_idx) +/* + * Called from a mem_noshuf packet to make sure the load doesn't + * raise an exception + */ +void HELPER(probe_noshuf_load)(CPUHexagonState *env, target_ulong va, + int size, int mmu_idx) { uintptr_t retaddr = GETPC(); - int i; + probe_read(env, va, size, mmu_idx, retaddr); +} +/* Called during packet commit when there are two scalar stores */ +void HELPER(probe_pkt_scalar_store_s0)(CPUHexagonState *env, int args) +{ + int mmu_idx = FIELD_EX32(args, PROBE_PKT_SCALAR_STORE_S0, MMU_IDX); + bool is_predicated = + FIELD_EX32(args, PROBE_PKT_SCALAR_STORE_S0, IS_PREDICATED); + uintptr_t ra = GETPC(); + probe_store(env, 0, mmu_idx, is_predicated, ra); +} + +static void probe_hvx_stores(CPUHexagonState *env, int mmu_idx, + uintptr_t retaddr) +{ /* Normal (possibly masked) vector store */ - for (i = 0; i < VSTORES_MAX; i++) { + for (int i = 0; i < VSTORES_MAX; i++) { if (env->vstore_pending[i]) { target_ulong va = env->vstore[i].va; int size = env->vstore[i].size; @@ -489,24 +540,35 @@ void HELPER(probe_hvx_stores)(CPUHexagonState *env, int mmu_idx) } } -void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask, - int mmu_idx) +void HELPER(probe_hvx_stores)(CPUHexagonState *env, int mmu_idx) { - bool has_st0 = (mask >> 0) & 1; - bool has_st1 = (mask >> 1) & 1; - bool has_hvx_stores = (mask >> 2) & 1; + uintptr_t retaddr = GETPC(); + probe_hvx_stores(env, mmu_idx, retaddr); +} + +void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask) +{ + bool has_st0 = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_ST0); + bool has_st1 = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_ST1); + bool has_hvx_stores = + FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_HVX_STORES); + bool s0_is_pred = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, S0_IS_PRED); + bool s1_is_pred = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, S1_IS_PRED); + int mmu_idx = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, MMU_IDX); + uintptr_t ra = GETPC(); if (has_st0) { - probe_store(env, 0, mmu_idx); + probe_store(env, 0, mmu_idx, s0_is_pred, ra); } if (has_st1) { - probe_store(env, 1, mmu_idx); + probe_store(env, 1, mmu_idx, s1_is_pred, ra); } if (has_hvx_stores) { - HELPER(probe_hvx_stores)(env, mmu_idx); + probe_hvx_stores(env, mmu_idx, ra); } } +#ifndef CONFIG_HEXAGON_IDEF_PARSER /* * mem_noshuf * Section 5.5 of the Hexagon V67 Programmer's Reference Manual @@ -514,45 +576,17 @@ void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask, * If the load is in slot 0 and there is a store in slot1 (that * wasn't cancelled), we have to do the store first. */ -static void check_noshuf(CPUHexagonState *env, uint32_t slot) +static void check_noshuf(CPUHexagonState *env, bool pkt_has_store_s1, + uint32_t slot, target_ulong vaddr, int size, + uintptr_t ra) { - if (slot == 0 && env->pkt_has_store_s1 && + if (slot == 0 && pkt_has_store_s1 && ((env->slot_cancelled & (1 << 1)) == 0)) { - HELPER(commit_store)(env, 1); + probe_read(env, vaddr, size, MMU_USER_IDX, ra); + commit_store(env, 1, ra); } } - -static uint8_t mem_load1(CPUHexagonState *env, uint32_t slot, - target_ulong vaddr) -{ - uintptr_t ra = GETPC(); - check_noshuf(env, slot); - return cpu_ldub_data_ra(env, vaddr, ra); -} - -static uint16_t mem_load2(CPUHexagonState *env, uint32_t slot, - target_ulong vaddr) -{ - uintptr_t ra = GETPC(); - check_noshuf(env, slot); - return cpu_lduw_data_ra(env, vaddr, ra); -} - -static uint32_t mem_load4(CPUHexagonState *env, uint32_t slot, - target_ulong vaddr) -{ - uintptr_t ra = GETPC(); - check_noshuf(env, slot); - return cpu_ldl_data_ra(env, vaddr, ra); -} - -static uint64_t mem_load8(CPUHexagonState *env, uint32_t slot, - target_ulong vaddr) -{ - uintptr_t ra = GETPC(); - check_noshuf(env, slot); - return cpu_ldq_data_ra(env, vaddr, ra); -} +#endif /* Floating point */ float64 HELPER(conv_sf2df)(CPUHexagonState *env, float32 RsV) @@ -1177,7 +1211,7 @@ float32 HELPER(sffms)(CPUHexagonState *env, float32 RxV, { float32 neg_RsV; arch_fpop_start(env); - neg_RsV = float32_sub(float32_zero, RsV, &env->fp_status); + neg_RsV = float32_set_sign(RsV, float32_is_neg(RsV) ? 0 : 1); RxV = internal_fmafx(neg_RsV, RtV, RxV, 0, &env->fp_status); arch_fpop_end(env); return RxV; @@ -1452,12 +1486,6 @@ void HELPER(vwhist128qm)(CPUHexagonState *env, int32_t uiV) } } -static void cancel_slot(CPUHexagonState *env, uint32_t slot) -{ - HEX_DEBUG_LOG("Slot %d cancelled\n", slot); - env->slot_cancelled |= (1 << slot); -} - /* These macros can be referenced in the generated helper functions */ #define warn(...) /* Nothing */ #define fatal(...) g_assert_not_reached(); diff --git a/target/hexagon/op_helper.h b/target/hexagon/op_helper.h new file mode 100644 index 0000000000..66119cf3d4 --- /dev/null +++ b/target/hexagon/op_helper.h @@ -0,0 +1,27 @@ +/* + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef HEXAGON_OP_HELPER_H +#define HEXAGON_OP_HELPER_H + +/* Misc functions */ +void log_store64(CPUHexagonState *env, target_ulong addr, + int64_t val, int width, int slot); +void log_store32(CPUHexagonState *env, target_ulong addr, + target_ulong val, int width, int slot); + +#endif diff --git a/target/hexagon/opcodes.c b/target/hexagon/opcodes.c index 35d790cdd5..1f7f3def38 100644 --- a/target/hexagon/opcodes.c +++ b/target/hexagon/opcodes.c @@ -111,33 +111,4 @@ void opcode_init(void) #include "op_attribs_generated.h.inc" #undef OP_ATTRIB #undef ATTRIBS - - decode_init(); -} - - -#define NEEDLE "IMMEXT(" - -int opcode_which_immediate_is_extended(Opcode opcode) -{ - const char *p; - - g_assert(opcode < XX_LAST_OPCODE); - g_assert(GET_ATTRIB(opcode, A_EXTENDABLE)); - - p = opcode_short_semantics[opcode]; - p = strstr(p, NEEDLE); - g_assert(p); - p += strlen(NEEDLE); - while (isspace(*p)) { - p++; - } - /* lower is always imm 0, upper always imm 1. */ - if (islower(*p)) { - return 0; - } else if (isupper(*p)) { - return 1; - } else { - g_assert_not_reached(); - } } diff --git a/target/hexagon/opcodes.h b/target/hexagon/opcodes.h index 6e90e00fe2..fa7e321950 100644 --- a/target/hexagon/opcodes.h +++ b/target/hexagon/opcodes.h @@ -53,6 +53,4 @@ extern const OpcodeEncoding opcode_encodings[XX_LAST_OPCODE]; void opcode_init(void); -int opcode_which_immediate_is_extended(Opcode opcode); - #endif diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index d4fc92f7e9..f163eefe97 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,36 +20,43 @@ #include "cpu.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" -#include "exec/cpu_ldst.h" +#include "exec/helper-gen.h" +#include "exec/helper-proto.h" +#include "exec/translation-block.h" #include "exec/log.h" #include "internal.h" #include "attribs.h" #include "insn.h" #include "decode.h" #include "translate.h" +#include "genptr.h" #include "printinsn.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + +#include "analyze_funcs_generated.c.inc" + +typedef void (*AnalyzeInsn)(DisasContext *ctx); +static const AnalyzeInsn opcode_analyze[XX_LAST_OPCODE] = { +#define OPCODE(X) [X] = analyze_##X +#include "opcodes_def_generated.h.inc" +#undef OPCODE +}; + TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; TCGv hex_pred[NUM_PREGS]; -TCGv hex_next_PC; -TCGv hex_this_PC; TCGv hex_slot_cancelled; -TCGv hex_branch_taken; -TCGv hex_new_value[TOTAL_PER_THREAD_REGS]; +TCGv hex_new_value_usr; TCGv hex_reg_written[TOTAL_PER_THREAD_REGS]; -TCGv hex_new_pred_value[NUM_PREGS]; -TCGv hex_pred_written; TCGv hex_store_addr[STORES_MAX]; TCGv hex_store_width[STORES_MAX]; TCGv hex_store_val32[STORES_MAX]; TCGv_i64 hex_store_val64[STORES_MAX]; -TCGv hex_pkt_has_store_s1; -TCGv hex_dczero_addr; TCGv hex_llsc_addr; TCGv hex_llsc_val; TCGv_i64 hex_llsc_val_i64; -TCGv hex_VRegs_updated; -TCGv hex_QRegs_updated; TCGv hex_vstore_addr[VSTORES_MAX]; TCGv hex_vstore_size[VSTORES_MAX]; TCGv hex_vstore_pending[VSTORES_MAX]; @@ -63,6 +70,10 @@ intptr_t ctx_future_vreg_off(DisasContext *ctx, int regnum, { intptr_t offset; + if (!ctx->need_commit) { + return offsetof(CPUHexagonState, VRegs[regnum]); + } + /* See if it is already allocated */ for (int i = 0; i < ctx->future_vregs_idx; i++) { if (ctx->future_vregs_num[i] == regnum) { @@ -104,7 +115,7 @@ intptr_t ctx_tmp_vreg_off(DisasContext *ctx, int regnum, static void gen_exception_raw(int excp) { - gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp)); } static void gen_exec_counters(DisasContext *ctx) @@ -117,18 +128,67 @@ static void gen_exec_counters(DisasContext *ctx) hex_gpr[HEX_REG_QEMU_HVX_CNT], ctx->num_hvx_insns); } +static bool use_goto_tb(DisasContext *ctx, target_ulong dest) +{ + return translator_use_goto_tb(&ctx->base, dest); +} + +static void gen_goto_tb(DisasContext *ctx, int idx, target_ulong dest, bool + move_to_pc) +{ + if (use_goto_tb(ctx, dest)) { + tcg_gen_goto_tb(idx); + if (move_to_pc) { + tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], dest); + } + tcg_gen_exit_tb(ctx->base.tb, idx); + } else { + if (move_to_pc) { + tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], dest); + } + tcg_gen_lookup_and_goto_ptr(); + } +} + static void gen_end_tb(DisasContext *ctx) { + Packet *pkt = ctx->pkt; + gen_exec_counters(ctx); - tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], hex_next_PC); - tcg_gen_exit_tb(NULL, 0); + + if (ctx->branch_cond != TCG_COND_NEVER) { + if (ctx->branch_cond != TCG_COND_ALWAYS) { + TCGLabel *skip = gen_new_label(); + tcg_gen_brcondi_tl(ctx->branch_cond, ctx->branch_taken, 0, skip); + gen_goto_tb(ctx, 0, ctx->branch_dest, true); + gen_set_label(skip); + gen_goto_tb(ctx, 1, ctx->next_PC, false); + } else { + gen_goto_tb(ctx, 0, ctx->branch_dest, true); + } + } else if (ctx->is_tight_loop && + pkt->insn[pkt->num_insns - 1].opcode == J2_endloop0) { + /* + * When we're in a tight loop, we defer the endloop0 processing + * to take advantage of direct block chaining + */ + TCGLabel *skip = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_LEU, hex_gpr[HEX_REG_LC0], 1, skip); + tcg_gen_subi_tl(hex_gpr[HEX_REG_LC0], hex_gpr[HEX_REG_LC0], 1); + gen_goto_tb(ctx, 0, ctx->base.tb->pc, true); + gen_set_label(skip); + gen_goto_tb(ctx, 1, ctx->next_PC, false); + } else { + tcg_gen_lookup_and_goto_ptr(); + } + ctx->base.is_jmp = DISAS_NORETURN; } static void gen_exception_end_tb(DisasContext *ctx, int excp) { gen_exec_counters(ctx); - tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], hex_next_PC); + tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->next_PC); gen_exception_raw(excp); ctx->base.is_jmp = DISAS_NORETURN; @@ -174,7 +234,8 @@ static int read_packet_words(CPUHexagonState *env, DisasContext *ctx, g_assert(ctx->base.num_insns == 1); } - HEX_DEBUG_LOG("decode_packet: pc = 0x%x\n", ctx->base.pc_next); + HEX_DEBUG_LOG("decode_packet: pc = 0x%" VADDR_PRIx "\n", + ctx->base.pc_next); HEX_DEBUG_LOG(" words = { "); for (int i = 0; i < nwords; i++) { HEX_DEBUG_LOG("0x%x, ", words[i]); @@ -194,74 +255,358 @@ static bool check_for_attrib(Packet *pkt, int attrib) return false; } -static bool need_pc(Packet *pkt) -{ - return check_for_attrib(pkt, A_IMPLICIT_READS_PC); -} - static bool need_slot_cancelled(Packet *pkt) { - return check_for_attrib(pkt, A_CONDEXEC); + /* We only need slot_cancelled for conditional store instructions */ + for (int i = 0; i < pkt->num_insns; i++) { + uint16_t opcode = pkt->insn[i].opcode; + if (GET_ATTRIB(opcode, A_CONDEXEC) && + GET_ATTRIB(opcode, A_SCALAR_STORE)) { + return true; + } + } + return false; } -static bool need_pred_written(Packet *pkt) +static bool need_next_PC(DisasContext *ctx) { - return check_for_attrib(pkt, A_WRITES_PRED_REG); + Packet *pkt = ctx->pkt; + + /* Check for conditional control flow or HW loop end */ + for (int i = 0; i < pkt->num_insns; i++) { + uint16_t opcode = pkt->insn[i].opcode; + if (GET_ATTRIB(opcode, A_CONDEXEC) && GET_ATTRIB(opcode, A_COF)) { + return true; + } + if (GET_ATTRIB(opcode, A_HWLOOP0_END) || + GET_ATTRIB(opcode, A_HWLOOP1_END)) { + return true; + } + } + return false; } -static void gen_start_packet(DisasContext *ctx, Packet *pkt) +/* + * The opcode_analyze functions mark most of the writes in a packet + * However, there are some implicit writes marked as attributes + * of the applicable instructions. + */ +static void mark_implicit_reg_write(DisasContext *ctx, int attrib, int rnum) { + uint16_t opcode = ctx->insn->opcode; + if (GET_ATTRIB(opcode, attrib)) { + /* + * USR is used to set overflow and FP exceptions, + * so treat it as conditional + */ + bool is_predicated = GET_ATTRIB(opcode, A_CONDEXEC) || + rnum == HEX_REG_USR; + + /* LC0/LC1 is conditionally written by endloop instructions */ + if ((rnum == HEX_REG_LC0 || rnum == HEX_REG_LC1) && + (opcode == J2_endloop0 || + opcode == J2_endloop1 || + opcode == J2_endloop01)) { + is_predicated = true; + } + + ctx_log_reg_write(ctx, rnum, is_predicated); + } +} + +static void mark_implicit_reg_writes(DisasContext *ctx) +{ + mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_FP, HEX_REG_FP); + mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SP, HEX_REG_SP); + mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LR, HEX_REG_LR); + mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LC0, HEX_REG_LC0); + mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SA0, HEX_REG_SA0); + mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_LC1, HEX_REG_LC1); + mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_SA1, HEX_REG_SA1); + mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_USR, HEX_REG_USR); + mark_implicit_reg_write(ctx, A_FPOP, HEX_REG_USR); +} + +static void mark_implicit_pred_write(DisasContext *ctx, int attrib, int pnum) +{ + if (GET_ATTRIB(ctx->insn->opcode, attrib)) { + ctx_log_pred_write(ctx, pnum); + } +} + +static void mark_implicit_pred_writes(DisasContext *ctx) +{ + mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P0, 0); + mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P1, 1); + mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P2, 2); + mark_implicit_pred_write(ctx, A_IMPLICIT_WRITES_P3, 3); +} + +static bool pkt_raises_exception(Packet *pkt) +{ + if (check_for_attrib(pkt, A_LOAD) || + check_for_attrib(pkt, A_STORE)) { + return true; + } + return false; +} + +static bool need_commit(DisasContext *ctx) +{ + Packet *pkt = ctx->pkt; + + /* + * If the short-circuit property is set to false, we'll always do the commit + */ + if (!ctx->short_circuit) { + return true; + } + + if (pkt_raises_exception(pkt)) { + return true; + } + + /* Registers with immutability flags require new_value */ + for (int i = 0; i < ctx->reg_log_idx; i++) { + int rnum = ctx->reg_log[i]; + if (reg_immut_masks[rnum]) { + return true; + } + } + + /* Floating point instructions are hard-coded to use new_value */ + if (check_for_attrib(pkt, A_FPOP)) { + return true; + } + + if (pkt->num_insns == 1) { + if (pkt->pkt_has_hvx) { + /* + * The HVX instructions with generated helpers use + * pass-by-reference, so they need the read/write overlap + * check below. + * The HVX instructions with overrides are OK. + */ + if (!ctx->has_hvx_helper) { + return false; + } + } else { + return false; + } + } + + /* Check for overlap between register reads and writes */ + for (int i = 0; i < ctx->reg_log_idx; i++) { + int rnum = ctx->reg_log[i]; + if (test_bit(rnum, ctx->regs_read)) { + return true; + } + } + + /* Check for overlap between predicate reads and writes */ + for (int i = 0; i < ctx->preg_log_idx; i++) { + int pnum = ctx->preg_log[i]; + if (test_bit(pnum, ctx->pregs_read)) { + return true; + } + } + + /* Check for overlap between HVX reads and writes */ + for (int i = 0; i < ctx->vreg_log_idx; i++) { + int vnum = ctx->vreg_log[i]; + if (test_bit(vnum, ctx->vregs_read)) { + return true; + } + } + if (!bitmap_empty(ctx->vregs_updated_tmp, NUM_VREGS)) { + int i = find_first_bit(ctx->vregs_updated_tmp, NUM_VREGS); + while (i < NUM_VREGS) { + if (test_bit(i, ctx->vregs_read)) { + return true; + } + i = find_next_bit(ctx->vregs_updated_tmp, NUM_VREGS, i + 1); + } + } + if (!bitmap_empty(ctx->vregs_select, NUM_VREGS)) { + int i = find_first_bit(ctx->vregs_select, NUM_VREGS); + while (i < NUM_VREGS) { + if (test_bit(i, ctx->vregs_read)) { + return true; + } + i = find_next_bit(ctx->vregs_select, NUM_VREGS, i + 1); + } + } + + /* Check for overlap between HVX predicate reads and writes */ + for (int i = 0; i < ctx->qreg_log_idx; i++) { + int qnum = ctx->qreg_log[i]; + if (test_bit(qnum, ctx->qregs_read)) { + return true; + } + } + + return false; +} + +static void mark_implicit_pred_read(DisasContext *ctx, int attrib, int pnum) +{ + if (GET_ATTRIB(ctx->insn->opcode, attrib)) { + ctx_log_pred_read(ctx, pnum); + } +} + +static void mark_implicit_pred_reads(DisasContext *ctx) +{ + mark_implicit_pred_read(ctx, A_IMPLICIT_READS_P0, 0); + mark_implicit_pred_read(ctx, A_IMPLICIT_READS_P1, 1); + mark_implicit_pred_read(ctx, A_IMPLICIT_READS_P3, 2); + mark_implicit_pred_read(ctx, A_IMPLICIT_READS_P3, 3); +} + +static void analyze_packet(DisasContext *ctx) +{ + Packet *pkt = ctx->pkt; + ctx->has_hvx_helper = false; + for (int i = 0; i < pkt->num_insns; i++) { + Insn *insn = &pkt->insn[i]; + ctx->insn = insn; + if (opcode_analyze[insn->opcode]) { + opcode_analyze[insn->opcode](ctx); + } + mark_implicit_reg_writes(ctx); + mark_implicit_pred_writes(ctx); + mark_implicit_pred_reads(ctx); + } + + ctx->need_commit = need_commit(ctx); +} + +static void gen_start_packet(DisasContext *ctx) +{ + Packet *pkt = ctx->pkt; target_ulong next_PC = ctx->base.pc_next + pkt->encod_pkt_size_in_bytes; int i; /* Clear out the disassembly context */ + ctx->next_PC = next_PC; ctx->reg_log_idx = 0; bitmap_zero(ctx->regs_written, TOTAL_PER_THREAD_REGS); + bitmap_zero(ctx->regs_read, TOTAL_PER_THREAD_REGS); + bitmap_zero(ctx->predicated_regs, TOTAL_PER_THREAD_REGS); ctx->preg_log_idx = 0; bitmap_zero(ctx->pregs_written, NUM_PREGS); + bitmap_zero(ctx->pregs_read, NUM_PREGS); ctx->future_vregs_idx = 0; ctx->tmp_vregs_idx = 0; ctx->vreg_log_idx = 0; bitmap_zero(ctx->vregs_updated_tmp, NUM_VREGS); bitmap_zero(ctx->vregs_updated, NUM_VREGS); bitmap_zero(ctx->vregs_select, NUM_VREGS); + bitmap_zero(ctx->predicated_future_vregs, NUM_VREGS); + bitmap_zero(ctx->predicated_tmp_vregs, NUM_VREGS); + bitmap_zero(ctx->vregs_read, NUM_VREGS); + bitmap_zero(ctx->qregs_read, NUM_QREGS); ctx->qreg_log_idx = 0; for (i = 0; i < STORES_MAX; i++) { ctx->store_width[i] = 0; } - tcg_gen_movi_tl(hex_pkt_has_store_s1, pkt->pkt_has_store_s1); ctx->s1_store_processed = false; ctx->pre_commit = true; + for (i = 0; i < TOTAL_PER_THREAD_REGS; i++) { + ctx->new_value[i] = NULL; + } + for (i = 0; i < NUM_PREGS; i++) { + ctx->new_pred_value[i] = NULL; + } + + analyze_packet(ctx); + + /* + * pregs_written is used both in the analyze phase as well as the code + * gen phase, so clear it again. + */ + bitmap_zero(ctx->pregs_written, NUM_PREGS); if (HEX_DEBUG) { /* Handy place to set a breakpoint before the packet executes */ - gen_helper_debug_start_packet(cpu_env); - tcg_gen_movi_tl(hex_this_PC, ctx->base.pc_next); + gen_helper_debug_start_packet(tcg_env); } /* Initialize the runtime state for packet semantics */ - if (need_pc(pkt)) { - tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->base.pc_next); - } if (need_slot_cancelled(pkt)) { tcg_gen_movi_tl(hex_slot_cancelled, 0); } + ctx->branch_taken = NULL; if (pkt->pkt_has_cof) { - tcg_gen_movi_tl(hex_branch_taken, 0); - tcg_gen_movi_tl(hex_next_PC, next_PC); + ctx->branch_taken = tcg_temp_new(); + if (pkt->pkt_has_multi_cof) { + tcg_gen_movi_tl(ctx->branch_taken, 0); + } + if (need_next_PC(ctx)) { + tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], next_PC); + } } - if (need_pred_written(pkt)) { - tcg_gen_movi_tl(hex_pred_written, 0); + if (HEX_DEBUG) { + ctx->pred_written = tcg_temp_new(); + tcg_gen_movi_tl(ctx->pred_written, 0); } - if (pkt->pkt_has_hvx) { - tcg_gen_movi_tl(hex_VRegs_updated, 0); - tcg_gen_movi_tl(hex_QRegs_updated, 0); + /* Preload the predicated registers into get_result_gpr(ctx, i) */ + if (ctx->need_commit && + !bitmap_empty(ctx->predicated_regs, TOTAL_PER_THREAD_REGS)) { + i = find_first_bit(ctx->predicated_regs, TOTAL_PER_THREAD_REGS); + while (i < TOTAL_PER_THREAD_REGS) { + tcg_gen_mov_tl(get_result_gpr(ctx, i), hex_gpr[i]); + i = find_next_bit(ctx->predicated_regs, TOTAL_PER_THREAD_REGS, + i + 1); + } + } + + /* + * Preload the predicated pred registers into ctx->new_pred_value[pred_num] + * Only endloop instructions conditionally write to pred registers + */ + if (ctx->need_commit && pkt->pkt_has_endloop) { + for (i = 0; i < ctx->preg_log_idx; i++) { + int pred_num = ctx->preg_log[i]; + ctx->new_pred_value[pred_num] = tcg_temp_new(); + tcg_gen_mov_tl(ctx->new_pred_value[pred_num], hex_pred[pred_num]); + } + } + + /* Preload the predicated HVX registers into future_VRegs and tmp_VRegs */ + if (!bitmap_empty(ctx->predicated_future_vregs, NUM_VREGS)) { + i = find_first_bit(ctx->predicated_future_vregs, NUM_VREGS); + while (i < NUM_VREGS) { + const intptr_t VdV_off = + ctx_future_vreg_off(ctx, i, 1, true); + intptr_t src_off = offsetof(CPUHexagonState, VRegs[i]); + tcg_gen_gvec_mov(MO_64, VdV_off, + src_off, + sizeof(MMVector), + sizeof(MMVector)); + i = find_next_bit(ctx->predicated_future_vregs, NUM_VREGS, i + 1); + } + } + if (!bitmap_empty(ctx->predicated_tmp_vregs, NUM_VREGS)) { + i = find_first_bit(ctx->predicated_tmp_vregs, NUM_VREGS); + while (i < NUM_VREGS) { + const intptr_t VdV_off = + ctx_tmp_vreg_off(ctx, i, 1, true); + intptr_t src_off = offsetof(CPUHexagonState, VRegs[i]); + tcg_gen_gvec_mov(MO_64, VdV_off, + src_off, + sizeof(MMVector), + sizeof(MMVector)); + i = find_next_bit(ctx->predicated_tmp_vregs, NUM_VREGS, i + 1); + } } } -bool is_gather_store_insn(Insn *insn, Packet *pkt) +bool is_gather_store_insn(DisasContext *ctx) { + Packet *pkt = ctx->pkt; + Insn *insn = ctx->insn; if (GET_ATTRIB(insn->opcode, A_CVI_NEW) && insn->new_value_producer_slot == 1) { /* Look for gather instruction */ @@ -275,65 +620,38 @@ bool is_gather_store_insn(Insn *insn, Packet *pkt) return false; } -/* - * The LOG_*_WRITE macros mark most of the writes in a packet - * However, there are some implicit writes marked as attributes - * of the applicable instructions. - */ -static void mark_implicit_reg_write(DisasContext *ctx, Insn *insn, - int attrib, int rnum) +static void mark_store_width(DisasContext *ctx) { - if (GET_ATTRIB(insn->opcode, attrib)) { - /* - * USR is used to set overflow and FP exceptions, - * so treat it as conditional - */ - bool is_predicated = GET_ATTRIB(insn->opcode, A_CONDEXEC) || - rnum == HEX_REG_USR; - if (is_predicated && !is_preloaded(ctx, rnum)) { - tcg_gen_mov_tl(hex_new_value[rnum], hex_gpr[rnum]); + uint16_t opcode = ctx->insn->opcode; + uint32_t slot = ctx->insn->slot; + uint8_t width = 0; + + if (GET_ATTRIB(opcode, A_SCALAR_STORE)) { + if (GET_ATTRIB(opcode, A_MEMSIZE_0B)) { + return; } - - ctx_log_reg_write(ctx, rnum); + if (GET_ATTRIB(opcode, A_MEMSIZE_1B)) { + width |= 1; + } + if (GET_ATTRIB(opcode, A_MEMSIZE_2B)) { + width |= 2; + } + if (GET_ATTRIB(opcode, A_MEMSIZE_4B)) { + width |= 4; + } + if (GET_ATTRIB(opcode, A_MEMSIZE_8B)) { + width |= 8; + } + tcg_debug_assert(is_power_of_2(width)); + ctx->store_width[slot] = width; } } -static void mark_implicit_pred_write(DisasContext *ctx, Insn *insn, - int attrib, int pnum) +static void gen_insn(DisasContext *ctx) { - if (GET_ATTRIB(insn->opcode, attrib)) { - ctx_log_pred_write(ctx, pnum); - } -} - -static void mark_implicit_reg_writes(DisasContext *ctx, Insn *insn) -{ - mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_FP, HEX_REG_FP); - mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_SP, HEX_REG_SP); - mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_LR, HEX_REG_LR); - mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_LC0, HEX_REG_LC0); - mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_SA0, HEX_REG_SA0); - mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_LC1, HEX_REG_LC1); - mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_SA1, HEX_REG_SA1); - mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_USR, HEX_REG_USR); - mark_implicit_reg_write(ctx, insn, A_FPOP, HEX_REG_USR); -} - -static void mark_implicit_pred_writes(DisasContext *ctx, Insn *insn) -{ - mark_implicit_pred_write(ctx, insn, A_IMPLICIT_WRITES_P0, 0); - mark_implicit_pred_write(ctx, insn, A_IMPLICIT_WRITES_P1, 1); - mark_implicit_pred_write(ctx, insn, A_IMPLICIT_WRITES_P2, 2); - mark_implicit_pred_write(ctx, insn, A_IMPLICIT_WRITES_P3, 3); -} - -static void gen_insn(CPUHexagonState *env, DisasContext *ctx, - Insn *insn, Packet *pkt) -{ - if (insn->generate) { - mark_implicit_reg_writes(ctx, insn); - insn->generate(env, ctx, insn, pkt); - mark_implicit_pred_writes(ctx, insn); + if (ctx->insn->generate) { + ctx->insn->generate(ctx); + mark_store_width(ctx); } else { gen_exception_end_tb(ctx, HEX_EXCP_INVALID_OPCODE); } @@ -346,60 +664,45 @@ static void gen_reg_writes(DisasContext *ctx) { int i; - for (i = 0; i < ctx->reg_log_idx; i++) { - int reg_num = ctx->reg_log[i]; - - tcg_gen_mov_tl(hex_gpr[reg_num], hex_new_value[reg_num]); - } -} - -static void gen_pred_writes(DisasContext *ctx, Packet *pkt) -{ - int i; - - /* Early exit if the log is empty */ - if (!ctx->preg_log_idx) { + /* Early exit if not needed */ + if (!ctx->need_commit) { return; } - /* - * Only endloop instructions will conditionally - * write a predicate. If there are no endloop - * instructions, we can use the non-conditional - * write of the predicates. - */ - if (pkt->pkt_has_endloop) { - TCGv zero = tcg_constant_tl(0); - TCGv pred_written = tcg_temp_new(); - for (i = 0; i < ctx->preg_log_idx; i++) { - int pred_num = ctx->preg_log[i]; + for (i = 0; i < ctx->reg_log_idx; i++) { + int reg_num = ctx->reg_log[i]; - tcg_gen_andi_tl(pred_written, hex_pred_written, 1 << pred_num); - tcg_gen_movcond_tl(TCG_COND_NE, hex_pred[pred_num], - pred_written, zero, - hex_new_pred_value[pred_num], - hex_pred[pred_num]); - } - tcg_temp_free(pred_written); - } else { - for (i = 0; i < ctx->preg_log_idx; i++) { - int pred_num = ctx->preg_log[i]; - tcg_gen_mov_tl(hex_pred[pred_num], hex_new_pred_value[pred_num]); - if (HEX_DEBUG) { - /* Do this so HELPER(debug_commit_end) will know */ - tcg_gen_ori_tl(hex_pred_written, hex_pred_written, - 1 << pred_num); - } + tcg_gen_mov_tl(hex_gpr[reg_num], get_result_gpr(ctx, reg_num)); + + /* + * ctx->is_tight_loop is set when SA0 points to the beginning of the TB. + * If we write to SA0, we have to turn off tight loop handling. + */ + if (reg_num == HEX_REG_SA0) { + ctx->is_tight_loop = false; } } } +static void gen_pred_writes(DisasContext *ctx) +{ + /* Early exit if not needed or the log is empty */ + if (!ctx->need_commit || !ctx->preg_log_idx) { + return; + } + + for (int i = 0; i < ctx->preg_log_idx; i++) { + int pred_num = ctx->preg_log[i]; + tcg_gen_mov_tl(hex_pred[pred_num], ctx->new_pred_value[pred_num]); + } +} + static void gen_check_store_width(DisasContext *ctx, int slot_num) { if (HEX_DEBUG) { TCGv slot = tcg_constant_tl(slot_num); TCGv check = tcg_constant_tl(ctx->store_width[slot_num]); - gen_helper_debug_check_store_width(cpu_env, slot, check); + gen_helper_debug_check_store_width(tcg_env, slot, check); } } @@ -414,9 +717,9 @@ static bool slot_is_predicated(Packet *pkt, int slot_num) g_assert_not_reached(); } -void process_store(DisasContext *ctx, Packet *pkt, int slot_num) +void process_store(DisasContext *ctx, int slot_num) { - bool is_predicated = slot_is_predicated(pkt, slot_num); + bool is_predicated = slot_is_predicated(ctx->pkt, slot_num); TCGLabel *label_end = NULL; /* @@ -435,10 +738,9 @@ void process_store(DisasContext *ctx, Packet *pkt, int slot_num) /* Don't do anything if the slot was cancelled */ tcg_gen_extract_tl(cancelled, hex_slot_cancelled, slot_num, 1); tcg_gen_brcondi_tl(TCG_COND_NE, cancelled, 0, label_end); - tcg_temp_free(cancelled); } { - TCGv address = tcg_temp_local_new(); + TCGv address = tcg_temp_new(); tcg_gen_mov_tl(address, hex_store_addr[slot_num]); /* @@ -452,27 +754,27 @@ void process_store(DisasContext *ctx, Packet *pkt, int slot_num) switch (ctx->store_width[slot_num]) { case 1: gen_check_store_width(ctx, slot_num); - tcg_gen_qemu_st8(hex_store_val32[slot_num], - hex_store_addr[slot_num], - ctx->mem_idx); + tcg_gen_qemu_st_tl(hex_store_val32[slot_num], + hex_store_addr[slot_num], + ctx->mem_idx, MO_UB); break; case 2: gen_check_store_width(ctx, slot_num); - tcg_gen_qemu_st16(hex_store_val32[slot_num], - hex_store_addr[slot_num], - ctx->mem_idx); + tcg_gen_qemu_st_tl(hex_store_val32[slot_num], + hex_store_addr[slot_num], + ctx->mem_idx, MO_TEUW); break; case 4: gen_check_store_width(ctx, slot_num); - tcg_gen_qemu_st32(hex_store_val32[slot_num], - hex_store_addr[slot_num], - ctx->mem_idx); + tcg_gen_qemu_st_tl(hex_store_val32[slot_num], + hex_store_addr[slot_num], + ctx->mem_idx, MO_TEUL); break; case 8: gen_check_store_width(ctx, slot_num); - tcg_gen_qemu_st64(hex_store_val64[slot_num], - hex_store_addr[slot_num], - ctx->mem_idx); + tcg_gen_qemu_st_i64(hex_store_val64[slot_num], + hex_store_addr[slot_num], + ctx->mem_idx, MO_TEUQ); break; default: { @@ -482,49 +784,49 @@ void process_store(DisasContext *ctx, Packet *pkt, int slot_num) * avoid branching based on the width at runtime. */ TCGv slot = tcg_constant_tl(slot_num); - gen_helper_commit_store(cpu_env, slot); + gen_helper_commit_store(tcg_env, slot); } } - tcg_temp_free(address); } if (is_predicated) { gen_set_label(label_end); } } -static void process_store_log(DisasContext *ctx, Packet *pkt) +static void process_store_log(DisasContext *ctx) { /* * When a packet has two stores, the hardware processes * slot 1 and then slot 0. This will be important when * the memory accesses overlap. */ - if (pkt->pkt_has_store_s1 && !pkt->pkt_has_dczeroa) { - process_store(ctx, pkt, 1); + Packet *pkt = ctx->pkt; + if (pkt->pkt_has_store_s1) { + g_assert(!pkt->pkt_has_dczeroa); + process_store(ctx, 1); } - if (pkt->pkt_has_store_s0 && !pkt->pkt_has_dczeroa) { - process_store(ctx, pkt, 0); + if (pkt->pkt_has_store_s0) { + g_assert(!pkt->pkt_has_dczeroa); + process_store(ctx, 0); } } /* Zero out a 32-bit cache line */ -static void process_dczeroa(DisasContext *ctx, Packet *pkt) +static void process_dczeroa(DisasContext *ctx) { - if (pkt->pkt_has_dczeroa) { + if (ctx->pkt->pkt_has_dczeroa) { /* Store 32 bytes of zero starting at (addr & ~0x1f) */ TCGv addr = tcg_temp_new(); TCGv_i64 zero = tcg_constant_i64(0); - tcg_gen_andi_tl(addr, hex_dczero_addr, ~0x1f); - tcg_gen_qemu_st64(zero, addr, ctx->mem_idx); + tcg_gen_andi_tl(addr, ctx->dczero_addr, ~0x1f); + tcg_gen_qemu_st_i64(zero, addr, ctx->mem_idx, MO_UQ); tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_st64(zero, addr, ctx->mem_idx); + tcg_gen_qemu_st_i64(zero, addr, ctx->mem_idx, MO_UQ); tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_st64(zero, addr, ctx->mem_idx); + tcg_gen_qemu_st_i64(zero, addr, ctx->mem_idx, MO_UQ); tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_st64(zero, addr, ctx->mem_idx); - - tcg_temp_free(addr); + tcg_gen_qemu_st_i64(zero, addr, ctx->mem_idx, MO_UQ); } } @@ -540,83 +842,54 @@ static bool pkt_has_hvx_store(Packet *pkt) return false; } -static void gen_commit_hvx(DisasContext *ctx, Packet *pkt) +static void gen_commit_hvx(DisasContext *ctx) { int i; + /* Early exit if not needed */ + if (!ctx->need_commit) { + g_assert(!pkt_has_hvx_store(ctx->pkt)); + return; + } + /* * for (i = 0; i < ctx->vreg_log_idx; i++) { * int rnum = ctx->vreg_log[i]; - * if (ctx->vreg_is_predicated[i]) { - * if (env->VRegs_updated & (1 << rnum)) { - * env->VRegs[rnum] = env->future_VRegs[rnum]; - * } - * } else { - * env->VRegs[rnum] = env->future_VRegs[rnum]; - * } + * env->VRegs[rnum] = env->future_VRegs[rnum]; * } */ for (i = 0; i < ctx->vreg_log_idx; i++) { int rnum = ctx->vreg_log[i]; - bool is_predicated = ctx->vreg_is_predicated[i]; intptr_t dstoff = offsetof(CPUHexagonState, VRegs[rnum]); intptr_t srcoff = ctx_future_vreg_off(ctx, rnum, 1, false); size_t size = sizeof(MMVector); - if (is_predicated) { - TCGv cmp = tcg_temp_new(); - TCGLabel *label_skip = gen_new_label(); - - tcg_gen_andi_tl(cmp, hex_VRegs_updated, 1 << rnum); - tcg_gen_brcondi_tl(TCG_COND_EQ, cmp, 0, label_skip); - tcg_temp_free(cmp); - tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size); - gen_set_label(label_skip); - } else { - tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size); - } + tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size); } /* * for (i = 0; i < ctx->qreg_log_idx; i++) { * int rnum = ctx->qreg_log[i]; - * if (ctx->qreg_is_predicated[i]) { - * if (env->QRegs_updated) & (1 << rnum)) { - * env->QRegs[rnum] = env->future_QRegs[rnum]; - * } - * } else { - * env->QRegs[rnum] = env->future_QRegs[rnum]; - * } + * env->QRegs[rnum] = env->future_QRegs[rnum]; * } */ for (i = 0; i < ctx->qreg_log_idx; i++) { int rnum = ctx->qreg_log[i]; - bool is_predicated = ctx->qreg_is_predicated[i]; intptr_t dstoff = offsetof(CPUHexagonState, QRegs[rnum]); intptr_t srcoff = offsetof(CPUHexagonState, future_QRegs[rnum]); size_t size = sizeof(MMQReg); - if (is_predicated) { - TCGv cmp = tcg_temp_new(); - TCGLabel *label_skip = gen_new_label(); - - tcg_gen_andi_tl(cmp, hex_QRegs_updated, 1 << rnum); - tcg_gen_brcondi_tl(TCG_COND_EQ, cmp, 0, label_skip); - tcg_temp_free(cmp); - tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size); - gen_set_label(label_skip); - } else { - tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size); - } + tcg_gen_gvec_mov(MO_64, dstoff, srcoff, size, size); } - if (pkt_has_hvx_store(pkt)) { - gen_helper_commit_hvx_stores(cpu_env); + if (pkt_has_hvx_store(ctx->pkt)) { + gen_helper_commit_hvx_stores(tcg_env); } } -static void update_exec_counters(DisasContext *ctx, Packet *pkt) +static void update_exec_counters(DisasContext *ctx) { + Packet *pkt = ctx->pkt; int num_insns = pkt->num_insns; int num_real_insns = 0; int num_hvx_insns = 0; @@ -637,8 +910,7 @@ static void update_exec_counters(DisasContext *ctx, Packet *pkt) ctx->num_hvx_insns += num_hvx_insns; } -static void gen_commit_packet(CPUHexagonState *env, DisasContext *ctx, - Packet *pkt) +static void gen_commit_packet(DisasContext *ctx) { /* * If there is more than one store in a packet, make sure they are all OK @@ -657,6 +929,7 @@ static void gen_commit_packet(CPUHexagonState *env, DisasContext *ctx, * store. Therefore, we call process_store_log before anything else * involved in committing the packet. */ + Packet *pkt = ctx->pkt; bool has_store_s0 = pkt->pkt_has_store_s0; bool has_store_s1 = (pkt->pkt_has_store_s1 && !ctx->s1_store_processed); bool has_hvx_store = pkt_has_hvx_store(pkt); @@ -665,46 +938,67 @@ static void gen_commit_packet(CPUHexagonState *env, DisasContext *ctx, * The dczeroa will be the store in slot 0, check that we don't have * a store in slot 1 or an HVX store. */ - g_assert(has_store_s0 && !has_store_s1 && !has_hvx_store); - process_dczeroa(ctx, pkt); + g_assert(!has_store_s1 && !has_hvx_store); + process_dczeroa(ctx); } else if (has_hvx_store) { - TCGv mem_idx = tcg_constant_tl(ctx->mem_idx); - if (!has_store_s0 && !has_store_s1) { - gen_helper_probe_hvx_stores(cpu_env, mem_idx); + TCGv mem_idx = tcg_constant_tl(ctx->mem_idx); + gen_helper_probe_hvx_stores(tcg_env, mem_idx); } else { int mask = 0; - TCGv mask_tcgv; if (has_store_s0) { - mask |= (1 << 0); + mask = + FIELD_DP32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_ST0, 1); } if (has_store_s1) { - mask |= (1 << 1); + mask = + FIELD_DP32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_ST1, 1); } if (has_hvx_store) { - mask |= (1 << 2); + mask = + FIELD_DP32(mask, PROBE_PKT_SCALAR_HVX_STORES, + HAS_HVX_STORES, 1); } - mask_tcgv = tcg_constant_tl(mask); - gen_helper_probe_pkt_scalar_hvx_stores(cpu_env, mask_tcgv, mem_idx); + if (has_store_s0 && slot_is_predicated(pkt, 0)) { + mask = + FIELD_DP32(mask, PROBE_PKT_SCALAR_HVX_STORES, + S0_IS_PRED, 1); + } + if (has_store_s1 && slot_is_predicated(pkt, 1)) { + mask = + FIELD_DP32(mask, PROBE_PKT_SCALAR_HVX_STORES, + S1_IS_PRED, 1); + } + mask = FIELD_DP32(mask, PROBE_PKT_SCALAR_HVX_STORES, MMU_IDX, + ctx->mem_idx); + gen_helper_probe_pkt_scalar_hvx_stores(tcg_env, + tcg_constant_tl(mask)); } } else if (has_store_s0 && has_store_s1) { /* * process_store_log will execute the slot 1 store first, * so we only have to probe the store in slot 0 */ - TCGv mem_idx = tcg_constant_tl(ctx->mem_idx); - gen_helper_probe_pkt_scalar_store_s0(cpu_env, mem_idx); + int args = 0; + args = + FIELD_DP32(args, PROBE_PKT_SCALAR_STORE_S0, MMU_IDX, ctx->mem_idx); + if (slot_is_predicated(pkt, 0)) { + args = + FIELD_DP32(args, PROBE_PKT_SCALAR_STORE_S0, IS_PREDICATED, 1); + } + TCGv args_tcgv = tcg_constant_tl(args); + gen_helper_probe_pkt_scalar_store_s0(tcg_env, args_tcgv); } - process_store_log(ctx, pkt); + process_store_log(ctx); gen_reg_writes(ctx); - gen_pred_writes(ctx, pkt); + gen_pred_writes(ctx); if (pkt->pkt_has_hvx) { - gen_commit_hvx(ctx, pkt); + gen_commit_hvx(ctx); } - update_exec_counters(ctx, pkt); + update_exec_counters(ctx); if (HEX_DEBUG) { TCGv has_st0 = tcg_constant_tl(pkt->pkt_has_store_s0 && !pkt->pkt_has_dczeroa); @@ -712,12 +1006,14 @@ static void gen_commit_packet(CPUHexagonState *env, DisasContext *ctx, tcg_constant_tl(pkt->pkt_has_store_s1 && !pkt->pkt_has_dczeroa); /* Handy place to set a breakpoint at the end of execution */ - gen_helper_debug_commit_end(cpu_env, has_st0, has_st1); + gen_helper_debug_commit_end(tcg_env, tcg_constant_tl(ctx->pkt->pc), + ctx->pred_written, has_st0, has_st1); } if (pkt->vhist_insn != NULL) { ctx->pre_commit = false; - pkt->vhist_insn->generate(env, ctx, pkt->vhist_insn, pkt); + ctx->insn = pkt->vhist_insn; + pkt->vhist_insn->generate(ctx); } if (pkt->pkt_has_cof) { @@ -738,13 +1034,16 @@ static void decode_and_translate_packet(CPUHexagonState *env, DisasContext *ctx) return; } - if (decode_packet(nwords, words, &pkt, false) > 0) { + ctx->pkt = &pkt; + if (decode_packet(ctx, nwords, words, &pkt, false) > 0) { + pkt.pc = ctx->base.pc_next; HEX_DEBUG_PRINT_PKT(&pkt); - gen_start_packet(ctx, &pkt); + gen_start_packet(ctx); for (i = 0; i < pkt.num_insns; i++) { - gen_insn(env, ctx, &pkt.insn[i], &pkt); + ctx->insn = &pkt.insn[i]; + gen_insn(ctx); } - gen_commit_packet(env, ctx, &pkt); + gen_commit_packet(ctx); ctx->base.pc_next += pkt.encod_pkt_size_in_bytes; } else { gen_exception_end_tb(ctx, HEX_EXCP_INVALID_PACKET); @@ -755,11 +1054,16 @@ static void hexagon_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + HexagonCPU *hex_cpu = env_archcpu(cpu_env(cs)); + uint32_t hex_flags = dcbase->tb->flags; ctx->mem_idx = MMU_USER_IDX; ctx->num_packets = 0; ctx->num_insns = 0; ctx->num_hvx_insns = 0; + ctx->branch_cond = TCG_COND_NEVER; + ctx->is_tight_loop = FIELD_EX32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP); + ctx->short_circuit = hex_cpu->short_circuit; } static void hexagon_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -791,7 +1095,7 @@ static bool pkt_crosses_page(CPUHexagonState *env, DisasContext *ctx) static void hexagon_tr_translate_packet(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - CPUHexagonState *env = cpu->env_ptr; + CPUHexagonState *env = cpu_env(cpu); decode_and_translate_packet(env, ctx); @@ -850,17 +1154,17 @@ static const TranslatorOps hexagon_tr_ops = { .disas_log = hexagon_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; - translator_loop(&hexagon_tr_ops, &ctx.base, cs, tb, max_insns); + translator_loop(cs, tb, max_insns, pc, host_pc, + &hexagon_tr_ops, &ctx.base); } #define NAME_LEN 64 -static char new_value_names[TOTAL_PER_THREAD_REGS][NAME_LEN]; static char reg_written_names[TOTAL_PER_THREAD_REGS][NAME_LEN]; -static char new_pred_value_names[NUM_PREGS][NAME_LEN]; static char store_addr_names[STORES_MAX][NAME_LEN]; static char store_width_names[STORES_MAX][NAME_LEN]; static char store_val32_names[STORES_MAX][NAME_LEN]; @@ -876,92 +1180,68 @@ void hexagon_translate_init(void) opcode_init(); for (i = 0; i < TOTAL_PER_THREAD_REGS; i++) { - hex_gpr[i] = tcg_global_mem_new(cpu_env, + hex_gpr[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, gpr[i]), hexagon_regnames[i]); - snprintf(new_value_names[i], NAME_LEN, "new_%s", hexagon_regnames[i]); - hex_new_value[i] = tcg_global_mem_new(cpu_env, - offsetof(CPUHexagonState, new_value[i]), - new_value_names[i]); - if (HEX_DEBUG) { snprintf(reg_written_names[i], NAME_LEN, "reg_written_%s", hexagon_regnames[i]); - hex_reg_written[i] = tcg_global_mem_new(cpu_env, + hex_reg_written[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, reg_written[i]), reg_written_names[i]); } } + hex_new_value_usr = tcg_global_mem_new(tcg_env, + offsetof(CPUHexagonState, new_value_usr), "new_value_usr"); + for (i = 0; i < NUM_PREGS; i++) { - hex_pred[i] = tcg_global_mem_new(cpu_env, + hex_pred[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, pred[i]), hexagon_prednames[i]); - - snprintf(new_pred_value_names[i], NAME_LEN, "new_pred_%s", - hexagon_prednames[i]); - hex_new_pred_value[i] = tcg_global_mem_new(cpu_env, - offsetof(CPUHexagonState, new_pred_value[i]), - new_pred_value_names[i]); } - hex_pred_written = tcg_global_mem_new(cpu_env, - offsetof(CPUHexagonState, pred_written), "pred_written"); - hex_next_PC = tcg_global_mem_new(cpu_env, - offsetof(CPUHexagonState, next_PC), "next_PC"); - hex_this_PC = tcg_global_mem_new(cpu_env, - offsetof(CPUHexagonState, this_PC), "this_PC"); - hex_slot_cancelled = tcg_global_mem_new(cpu_env, + hex_slot_cancelled = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, slot_cancelled), "slot_cancelled"); - hex_branch_taken = tcg_global_mem_new(cpu_env, - offsetof(CPUHexagonState, branch_taken), "branch_taken"); - hex_pkt_has_store_s1 = tcg_global_mem_new(cpu_env, - offsetof(CPUHexagonState, pkt_has_store_s1), "pkt_has_store_s1"); - hex_dczero_addr = tcg_global_mem_new(cpu_env, - offsetof(CPUHexagonState, dczero_addr), "dczero_addr"); - hex_llsc_addr = tcg_global_mem_new(cpu_env, + hex_llsc_addr = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, llsc_addr), "llsc_addr"); - hex_llsc_val = tcg_global_mem_new(cpu_env, + hex_llsc_val = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, llsc_val), "llsc_val"); - hex_llsc_val_i64 = tcg_global_mem_new_i64(cpu_env, + hex_llsc_val_i64 = tcg_global_mem_new_i64(tcg_env, offsetof(CPUHexagonState, llsc_val_i64), "llsc_val_i64"); - hex_VRegs_updated = tcg_global_mem_new(cpu_env, - offsetof(CPUHexagonState, VRegs_updated), "VRegs_updated"); - hex_QRegs_updated = tcg_global_mem_new(cpu_env, - offsetof(CPUHexagonState, QRegs_updated), "QRegs_updated"); for (i = 0; i < STORES_MAX; i++) { snprintf(store_addr_names[i], NAME_LEN, "store_addr_%d", i); - hex_store_addr[i] = tcg_global_mem_new(cpu_env, + hex_store_addr[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, mem_log_stores[i].va), store_addr_names[i]); snprintf(store_width_names[i], NAME_LEN, "store_width_%d", i); - hex_store_width[i] = tcg_global_mem_new(cpu_env, + hex_store_width[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, mem_log_stores[i].width), store_width_names[i]); snprintf(store_val32_names[i], NAME_LEN, "store_val32_%d", i); - hex_store_val32[i] = tcg_global_mem_new(cpu_env, + hex_store_val32[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, mem_log_stores[i].data32), store_val32_names[i]); snprintf(store_val64_names[i], NAME_LEN, "store_val64_%d", i); - hex_store_val64[i] = tcg_global_mem_new_i64(cpu_env, + hex_store_val64[i] = tcg_global_mem_new_i64(tcg_env, offsetof(CPUHexagonState, mem_log_stores[i].data64), store_val64_names[i]); } - for (int i = 0; i < VSTORES_MAX; i++) { + for (i = 0; i < VSTORES_MAX; i++) { snprintf(vstore_addr_names[i], NAME_LEN, "vstore_addr_%d", i); - hex_vstore_addr[i] = tcg_global_mem_new(cpu_env, + hex_vstore_addr[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, vstore[i].va), vstore_addr_names[i]); snprintf(vstore_size_names[i], NAME_LEN, "vstore_size_%d", i); - hex_vstore_size[i] = tcg_global_mem_new(cpu_env, + hex_vstore_size[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, vstore[i].size), vstore_size_names[i]); snprintf(vstore_pending_names[i], NAME_LEN, "vstore_pending_%d", i); - hex_vstore_pending[i] = tcg_global_mem_new(cpu_env, + hex_vstore_pending[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, vstore_pending[i]), vstore_pending_names[i]); } diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index a245172827..4dd59c6726 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,10 +23,14 @@ #include "cpu.h" #include "exec/translator.h" #include "tcg/tcg-op.h" +#include "insn.h" #include "internal.h" typedef struct DisasContext { DisasContextBase base; + Packet *pkt; + Insn *insn; + uint32_t next_PC; uint32_t mem_idx; uint32_t num_packets; uint32_t num_insns; @@ -34,9 +38,12 @@ typedef struct DisasContext { int reg_log[REG_WRITES_MAX]; int reg_log_idx; DECLARE_BITMAP(regs_written, TOTAL_PER_THREAD_REGS); + DECLARE_BITMAP(regs_read, TOTAL_PER_THREAD_REGS); + DECLARE_BITMAP(predicated_regs, TOTAL_PER_THREAD_REGS); int preg_log[PRED_WRITES_MAX]; int preg_log_idx; DECLARE_BITMAP(pregs_written, NUM_PREGS); + DECLARE_BITMAP(pregs_read, NUM_PREGS); uint8_t store_width[STORES_MAX]; bool s1_store_processed; int future_vregs_idx; @@ -44,43 +51,79 @@ typedef struct DisasContext { int tmp_vregs_idx; int tmp_vregs_num[VECTOR_TEMPS_MAX]; int vreg_log[NUM_VREGS]; - bool vreg_is_predicated[NUM_VREGS]; int vreg_log_idx; DECLARE_BITMAP(vregs_updated_tmp, NUM_VREGS); DECLARE_BITMAP(vregs_updated, NUM_VREGS); DECLARE_BITMAP(vregs_select, NUM_VREGS); + DECLARE_BITMAP(predicated_future_vregs, NUM_VREGS); + DECLARE_BITMAP(predicated_tmp_vregs, NUM_VREGS); + DECLARE_BITMAP(vregs_read, NUM_VREGS); int qreg_log[NUM_QREGS]; - bool qreg_is_predicated[NUM_QREGS]; int qreg_log_idx; + DECLARE_BITMAP(qregs_read, NUM_QREGS); bool pre_commit; + bool need_commit; + TCGCond branch_cond; + target_ulong branch_dest; + bool is_tight_loop; + bool short_circuit; + bool has_hvx_helper; + TCGv new_value[TOTAL_PER_THREAD_REGS]; + TCGv new_pred_value[NUM_PREGS]; + TCGv pred_written; + TCGv branch_taken; + TCGv dczero_addr; } DisasContext; -static inline void ctx_log_reg_write(DisasContext *ctx, int rnum) -{ - if (test_bit(rnum, ctx->regs_written)) { - HEX_DEBUG_LOG("WARNING: Multiple writes to r%d\n", rnum); - } - ctx->reg_log[ctx->reg_log_idx] = rnum; - ctx->reg_log_idx++; - set_bit(rnum, ctx->regs_written); -} - -static inline void ctx_log_reg_write_pair(DisasContext *ctx, int rnum) -{ - ctx_log_reg_write(ctx, rnum); - ctx_log_reg_write(ctx, rnum + 1); -} - static inline void ctx_log_pred_write(DisasContext *ctx, int pnum) { - ctx->preg_log[ctx->preg_log_idx] = pnum; - ctx->preg_log_idx++; - set_bit(pnum, ctx->pregs_written); + if (!test_bit(pnum, ctx->pregs_written)) { + ctx->preg_log[ctx->preg_log_idx] = pnum; + ctx->preg_log_idx++; + set_bit(pnum, ctx->pregs_written); + } } -static inline bool is_preloaded(DisasContext *ctx, int num) +static inline void ctx_log_pred_read(DisasContext *ctx, int pnum) { - return test_bit(num, ctx->regs_written); + set_bit(pnum, ctx->pregs_read); +} + +static inline void ctx_log_reg_write(DisasContext *ctx, int rnum, + bool is_predicated) +{ + if (rnum == HEX_REG_P3_0_ALIASED) { + for (int i = 0; i < NUM_PREGS; i++) { + ctx_log_pred_write(ctx, i); + } + } else { + if (!test_bit(rnum, ctx->regs_written)) { + ctx->reg_log[ctx->reg_log_idx] = rnum; + ctx->reg_log_idx++; + set_bit(rnum, ctx->regs_written); + } + if (is_predicated) { + set_bit(rnum, ctx->predicated_regs); + } + } +} + +static inline void ctx_log_reg_write_pair(DisasContext *ctx, int rnum, + bool is_predicated) +{ + ctx_log_reg_write(ctx, rnum, is_predicated); + ctx_log_reg_write(ctx, rnum + 1, is_predicated); +} + +static inline void ctx_log_reg_read(DisasContext *ctx, int rnum) +{ + set_bit(rnum, ctx->regs_read); +} + +static inline void ctx_log_reg_read_pair(DisasContext *ctx, int rnum) +{ + ctx_log_reg_read(ctx, rnum); + ctx_log_reg_read(ctx, rnum + 1); } intptr_t ctx_future_vreg_off(DisasContext *ctx, int regnum, @@ -93,17 +136,25 @@ static inline void ctx_log_vreg_write(DisasContext *ctx, bool is_predicated) { if (type != EXT_TMP) { - ctx->vreg_log[ctx->vreg_log_idx] = rnum; - ctx->vreg_is_predicated[ctx->vreg_log_idx] = is_predicated; - ctx->vreg_log_idx++; + if (!test_bit(rnum, ctx->vregs_updated)) { + ctx->vreg_log[ctx->vreg_log_idx] = rnum; + ctx->vreg_log_idx++; + set_bit(rnum, ctx->vregs_updated); + } set_bit(rnum, ctx->vregs_updated); + if (is_predicated) { + set_bit(rnum, ctx->predicated_future_vregs); + } } if (type == EXT_NEW) { set_bit(rnum, ctx->vregs_select); } if (type == EXT_TMP) { set_bit(rnum, ctx->vregs_updated_tmp); + if (is_predicated) { + set_bit(rnum, ctx->predicated_tmp_vregs); + } } } @@ -115,38 +166,56 @@ static inline void ctx_log_vreg_write_pair(DisasContext *ctx, ctx_log_vreg_write(ctx, rnum ^ 1, type, is_predicated); } +static inline void ctx_log_vreg_read(DisasContext *ctx, int rnum) +{ + set_bit(rnum, ctx->vregs_read); +} + +static inline void ctx_log_vreg_read_pair(DisasContext *ctx, int rnum) +{ + ctx_log_vreg_read(ctx, rnum ^ 0); + ctx_log_vreg_read(ctx, rnum ^ 1); +} + static inline void ctx_log_qreg_write(DisasContext *ctx, - int rnum, bool is_predicated) + int rnum) { ctx->qreg_log[ctx->qreg_log_idx] = rnum; - ctx->qreg_is_predicated[ctx->qreg_log_idx] = is_predicated; ctx->qreg_log_idx++; } +static inline void ctx_log_qreg_read(DisasContext *ctx, int qnum) +{ + set_bit(qnum, ctx->qregs_read); +} + extern TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; extern TCGv hex_pred[NUM_PREGS]; -extern TCGv hex_next_PC; -extern TCGv hex_this_PC; extern TCGv hex_slot_cancelled; -extern TCGv hex_branch_taken; -extern TCGv hex_new_value[TOTAL_PER_THREAD_REGS]; +extern TCGv hex_new_value_usr; extern TCGv hex_reg_written[TOTAL_PER_THREAD_REGS]; -extern TCGv hex_new_pred_value[NUM_PREGS]; -extern TCGv hex_pred_written; extern TCGv hex_store_addr[STORES_MAX]; extern TCGv hex_store_width[STORES_MAX]; extern TCGv hex_store_val32[STORES_MAX]; extern TCGv_i64 hex_store_val64[STORES_MAX]; -extern TCGv hex_dczero_addr; extern TCGv hex_llsc_addr; extern TCGv hex_llsc_val; extern TCGv_i64 hex_llsc_val_i64; -extern TCGv hex_VRegs_updated; -extern TCGv hex_QRegs_updated; extern TCGv hex_vstore_addr[VSTORES_MAX]; extern TCGv hex_vstore_size[VSTORES_MAX]; extern TCGv hex_vstore_pending[VSTORES_MAX]; -bool is_gather_store_insn(Insn *insn, Packet *pkt); -void process_store(DisasContext *ctx, Packet *pkt, int slot_num); +bool is_gather_store_insn(DisasContext *ctx); +void process_store(DisasContext *ctx, int slot_num); + +FIELD(PROBE_PKT_SCALAR_STORE_S0, MMU_IDX, 0, 2) +FIELD(PROBE_PKT_SCALAR_STORE_S0, IS_PREDICATED, 2, 1) + +FIELD(PROBE_PKT_SCALAR_HVX_STORES, HAS_ST0, 0, 1) +FIELD(PROBE_PKT_SCALAR_HVX_STORES, HAS_ST1, 1, 1) +FIELD(PROBE_PKT_SCALAR_HVX_STORES, HAS_HVX_STORES, 2, 1) +FIELD(PROBE_PKT_SCALAR_HVX_STORES, S0_IS_PRED, 3, 1) +FIELD(PROBE_PKT_SCALAR_HVX_STORES, S1_IS_PRED, 4, 1) +FIELD(PROBE_PKT_SCALAR_HVX_STORES, MMU_IDX, 5, 2) + #endif diff --git a/target/hppa/cpu-param.h b/target/hppa/cpu-param.h index a48a2701ae..bb3d7ef6f7 100644 --- a/target/hppa/cpu-param.h +++ b/target/hppa/cpu-param.h @@ -8,27 +8,17 @@ #ifndef HPPA_CPU_PARAM_H #define HPPA_CPU_PARAM_H -#ifdef TARGET_HPPA64 -# define TARGET_LONG_BITS 64 -# define TARGET_REGISTER_BITS 64 -# define TARGET_VIRT_ADDR_SPACE_BITS 64 -# define TARGET_PHYS_ADDR_SPACE_BITS 64 -#elif defined(CONFIG_USER_ONLY) -# define TARGET_LONG_BITS 32 -# define TARGET_REGISTER_BITS 32 +#define TARGET_LONG_BITS 64 + +#if defined(CONFIG_USER_ONLY) && defined(TARGET_ABI32) +# define TARGET_PHYS_ADDR_SPACE_BITS 32 # define TARGET_VIRT_ADDR_SPACE_BITS 32 -# define TARGET_PHYS_ADDR_SPACE_BITS 32 #else -/* - * In order to form the GVA from space:offset, - * we need a 64-bit virtual address space. - */ -# define TARGET_LONG_BITS 64 -# define TARGET_REGISTER_BITS 32 +/* ??? PA-8000 through 8600 have 40 bits; PA-8700 and 8900 have 44 bits. */ +# define TARGET_PHYS_ADDR_SPACE_BITS 40 # define TARGET_VIRT_ADDR_SPACE_BITS 64 -# define TARGET_PHYS_ADDR_SPACE_BITS 32 #endif + #define TARGET_PAGE_BITS 12 -#define NB_MMU_MODES 5 #endif diff --git a/target/hppa/cpu-qom.h b/target/hppa/cpu-qom.h index b96e0318c7..5c454bf543 100644 --- a/target/hppa/cpu-qom.h +++ b/target/hppa/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU HPPA CPU + * QEMU HPPA CPU QOM header (target agnostic) * * Copyright (c) 2016 Richard Henderson * @@ -21,27 +21,10 @@ #define QEMU_HPPA_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_HPPA_CPU "hppa-cpu" +#define TYPE_HPPA64_CPU "hppa64-cpu" OBJECT_DECLARE_CPU_TYPE(HPPACPU, HPPACPUClass, HPPA_CPU) -/** - * HPPACPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * - * An HPPA CPU model. - */ -struct HPPACPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceReset parent_reset; -}; - - #endif diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index a6f52caf14..3831cb6db2 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -26,7 +26,7 @@ #include "qemu/module.h" #include "exec/exec-all.h" #include "fpu/softfloat.h" - +#include "tcg/tcg.h" static void hppa_cpu_set_pc(CPUState *cs, vaddr value) { @@ -36,11 +36,20 @@ static void hppa_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.iaoq_b = value + 4; } +static vaddr hppa_cpu_get_pc(CPUState *cs) +{ + HPPACPU *cpu = HPPA_CPU(cs); + + return cpu->env.iaoq_f; +} + static void hppa_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { HPPACPU *cpu = HPPA_CPU(cs); + tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + #ifdef CONFIG_USER_ONLY cpu->env.iaoq_f = tb->pc; cpu->env.iaoq_b = tb->cs_base; @@ -61,11 +70,41 @@ static void hppa_cpu_synchronize_from_tb(CPUState *cs, cpu->env.psw_n = (tb->flags & PSW_N) != 0; } +static void hppa_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + HPPACPU *cpu = HPPA_CPU(cs); + + cpu->env.iaoq_f = data[0]; + if (data[1] != (target_ulong)-1) { + cpu->env.iaoq_b = data[1]; + } + cpu->env.unwind_breg = data[2]; + /* + * Since we were executing the instruction at IAOQ_F, and took some + * sort of action that provoked the cpu_restore_state, we can infer + * that the instruction was not nullified. + */ + cpu->env.psw_n = 0; +} + static bool hppa_cpu_has_work(CPUState *cs) { return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } +static int hppa_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + CPUHPPAState *env = cpu_env(cs); + + if (env->psw & (ifetch ? PSW_C : PSW_D)) { + return PRIV_P_TO_MMU_IDX(env->iaoq_f & 3, env->psw & PSW_P); + } + /* mmu disabled */ + return env->psw & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX; +} + static void hppa_cpu_disas_set_info(CPUState *cs, disassemble_info *info) { info->mach = bfd_mach_hppa20; @@ -82,13 +121,10 @@ void hppa_cpu_do_unaligned_access(CPUState *cs, vaddr addr, CPUHPPAState *env = &cpu->env; cs->exception_index = EXCP_UNALIGN; - if (env->psw & PSW_Q) { - /* ??? Needs tweaking for hppa64. */ - env->cr[CR_IOR] = addr; - env->cr[CR_ISR] = addr >> 32; - } + cpu_restore_state(cs, retaddr); + hppa_set_ior_and_isr(env, addr, MMU_IDX_MMU_DISABLED(mmu_idx)); - cpu_loop_exit_restore(cs, retaddr); + cpu_loop_exit(cs); } #endif /* CONFIG_USER_ONLY */ @@ -110,8 +146,10 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp) #ifndef CONFIG_USER_ONLY { HPPACPU *cpu = HPPA_CPU(cs); + cpu->alarm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hppa_cpu_alarm_timer, cpu); + hppa_ptlbe(&cpu->env); } #endif } @@ -122,7 +160,6 @@ static void hppa_cpu_initfn(Object *obj) HPPACPU *cpu = HPPA_CPU(obj); CPUHPPAState *env = &cpu->env; - cpu_set_cpustate_pointers(cpu); cs->exception_index = -1; cpu_hppa_loaded_fr0(env); cpu_hppa_put_psw(env, PSW_W); @@ -130,7 +167,9 @@ static void hppa_cpu_initfn(Object *obj) static ObjectClass *hppa_cpu_class_by_name(const char *cpu_model) { - return object_class_by_name(TYPE_HPPA_CPU); + g_autofree char *typename = g_strconcat(cpu_model, "-cpu", NULL); + + return object_class_by_name(typename); } #ifndef CONFIG_USER_ONLY @@ -143,15 +182,17 @@ static const struct SysemuCPUOps hppa_sysemu_ops = { #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps hppa_tcg_ops = { +static const TCGCPUOps hppa_tcg_ops = { .initialize = hppa_translate_init, .synchronize_from_tb = hppa_cpu_synchronize_from_tb, + .restore_state_to_opc = hppa_restore_state_to_opc, #ifndef CONFIG_USER_ONLY .tlb_fill = hppa_cpu_tlb_fill, .cpu_exec_interrupt = hppa_cpu_exec_interrupt, .do_interrupt = hppa_cpu_do_interrupt, .do_unaligned_access = hppa_cpu_do_unaligned_access, + .do_transaction_failed = hppa_cpu_do_transaction_failed, #endif /* !CONFIG_USER_ONLY */ }; @@ -166,8 +207,10 @@ static void hppa_cpu_class_init(ObjectClass *oc, void *data) cc->class_by_name = hppa_cpu_class_by_name; cc->has_work = hppa_cpu_has_work; + cc->mmu_index = hppa_cpu_mmu_index; cc->dump_state = hppa_cpu_dump_state; cc->set_pc = hppa_cpu_set_pc; + cc->get_pc = hppa_cpu_get_pc; cc->gdb_read_register = hppa_cpu_gdb_read_register; cc->gdb_write_register = hppa_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY @@ -179,19 +222,21 @@ static void hppa_cpu_class_init(ObjectClass *oc, void *data) cc->tcg_ops = &hppa_tcg_ops; } -static const TypeInfo hppa_cpu_type_info = { - .name = TYPE_HPPA_CPU, - .parent = TYPE_CPU, - .instance_size = sizeof(HPPACPU), - .instance_init = hppa_cpu_initfn, - .abstract = false, - .class_size = sizeof(HPPACPUClass), - .class_init = hppa_cpu_class_init, +static const TypeInfo hppa_cpu_type_infos[] = { + { + .name = TYPE_HPPA_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(HPPACPU), + .instance_align = __alignof(HPPACPU), + .instance_init = hppa_cpu_initfn, + .abstract = false, + .class_size = sizeof(HPPACPUClass), + .class_init = hppa_cpu_class_init, + }, + { + .name = TYPE_HPPA64_CPU, + .parent = TYPE_HPPA_CPU, + }, }; -static void hppa_cpu_register_types(void) -{ - type_register_static(&hppa_cpu_type_info); -} - -type_init(hppa_cpu_register_types) +DEFINE_TYPES(hppa_cpu_type_infos) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 6f3b6beecf..a072d0bb63 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -23,6 +23,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" +#include "qemu/interval-tree.h" /* PA-RISC 1.x processors have a strong memory model. */ /* ??? While we do not yet implement PA-RISC 2.0, those processors have @@ -30,12 +31,37 @@ basis. It's probably easier to fall back to a strong memory model. */ #define TCG_GUEST_DEFAULT_MO TCG_MO_ALL -#define MMU_KERNEL_IDX 0 -#define MMU_USER_IDX 3 -#define MMU_PHYS_IDX 4 -#define TARGET_INSN_START_EXTRA_WORDS 1 +#define MMU_ABS_W_IDX 6 +#define MMU_ABS_IDX 7 +#define MMU_KERNEL_IDX 8 +#define MMU_KERNEL_P_IDX 9 +#define MMU_PL1_IDX 10 +#define MMU_PL1_P_IDX 11 +#define MMU_PL2_IDX 12 +#define MMU_PL2_P_IDX 13 +#define MMU_USER_IDX 14 +#define MMU_USER_P_IDX 15 -/* Hardware exceptions, interupts, faults, and traps. */ +#define MMU_IDX_MMU_DISABLED(MIDX) ((MIDX) < MMU_KERNEL_IDX) +#define MMU_IDX_TO_PRIV(MIDX) (((MIDX) - MMU_KERNEL_IDX) / 2) +#define MMU_IDX_TO_P(MIDX) (((MIDX) - MMU_KERNEL_IDX) & 1) +#define PRIV_P_TO_MMU_IDX(PRIV, P) ((PRIV) * 2 + !!(P) + MMU_KERNEL_IDX) + +#define TARGET_INSN_START_EXTRA_WORDS 2 + +/* No need to flush MMU_ABS*_IDX */ +#define HPPA_MMU_FLUSH_MASK \ + (1 << MMU_KERNEL_IDX | 1 << MMU_KERNEL_P_IDX | \ + 1 << MMU_PL1_IDX | 1 << MMU_PL1_P_IDX | \ + 1 << MMU_PL2_IDX | 1 << MMU_PL2_P_IDX | \ + 1 << MMU_USER_IDX | 1 << MMU_USER_P_IDX) + +/* Indices to flush for access_id changes. */ +#define HPPA_MMU_FLUSH_P_MASK \ + (1 << MMU_KERNEL_P_IDX | 1 << MMU_PL1_P_IDX | \ + 1 << MMU_PL2_P_IDX | 1 << MMU_USER_P_IDX) + +/* Hardware exceptions, interrupts, faults, and traps. */ #define EXCP_HPMC 1 /* high priority machine check */ #define EXCP_POWER_FAIL 2 #define EXCP_RC 3 /* recovery counter */ @@ -96,11 +122,7 @@ #define PSW_T 0x01000000 #define PSW_S 0x02000000 #define PSW_E 0x04000000 -#ifdef TARGET_HPPA64 #define PSW_W 0x08000000 /* PA2.0 only */ -#else -#define PSW_W 0 -#endif #define PSW_Z 0x40000000 /* PA1.x only */ #define PSW_Y 0x80000000 /* PA1.x only */ @@ -113,15 +135,12 @@ #define PSW_SM_P PSW_P #define PSW_SM_Q PSW_Q /* Enable Interrupt State Collection */ #define PSW_SM_R PSW_R /* Enable Recover Counter Trap */ -#ifdef TARGET_HPPA64 #define PSW_SM_E 0x100 #define PSW_SM_W 0x200 /* PA2.0 only : Enable Wide Mode */ -#else -#define PSW_SM_E 0 -#define PSW_SM_W 0 -#endif #define CR_RC 0 +#define CR_PSW_DEFAULT 6 /* see SeaBIOS PDC_PSW firmware call */ +#define PDC_PSW_WIDE_BIT 2 #define CR_PID1 8 #define CR_PID2 9 #define CR_PID3 12 @@ -139,42 +158,37 @@ #define CR_IPSW 22 #define CR_EIRR 23 -#if TARGET_REGISTER_BITS == 32 -typedef uint32_t target_ureg; -typedef int32_t target_sreg; -#define TREG_FMT_lx "%08"PRIx32 -#define TREG_FMT_ld "%"PRId32 -#else -typedef uint64_t target_ureg; -typedef int64_t target_sreg; -#define TREG_FMT_lx "%016"PRIx64 -#define TREG_FMT_ld "%"PRId64 -#endif +typedef struct HPPATLBEntry { + union { + IntervalTreeNode itree; + struct HPPATLBEntry *unused_next; + }; + + target_ulong pa; + + unsigned entry_valid : 1; -typedef struct { - uint64_t va_b; - uint64_t va_e; - target_ureg pa; unsigned u : 1; unsigned t : 1; unsigned d : 1; unsigned b : 1; - unsigned page_size : 4; unsigned ar_type : 3; unsigned ar_pl1 : 2; unsigned ar_pl2 : 2; - unsigned entry_valid : 1; unsigned access_id : 16; -} hppa_tlb_entry; +} HPPATLBEntry; typedef struct CPUArchState { - target_ureg gr[32]; + target_ulong iaoq_f; /* front */ + target_ulong iaoq_b; /* back, aka next instruction */ + + target_ulong gr[32]; uint64_t fr[32]; uint64_t sr[8]; /* stored shifted into place for gva */ - target_ureg psw; /* All psw bits except the following: */ - target_ureg psw_n; /* boolean */ - target_sreg psw_v; /* in most significant bit */ + target_ulong psw; /* All psw bits except the following: */ + target_ulong psw_n; /* boolean */ + target_long psw_v; /* in most significant bit */ /* Splitting the carry-borrow field into the MSB and "the rest", allows * for "the rest" to be deleted when it is unused, but the MSB is in use. @@ -183,29 +197,49 @@ typedef struct CPUArchState { * host has the appropriate add-with-carry insn to compute the msb). * Therefore the carry bits are stored as: cb_msb : cb & 0x11111110. */ - target_ureg psw_cb; /* in least significant bit of next nibble */ - target_ureg psw_cb_msb; /* boolean */ + target_ulong psw_cb; /* in least significant bit of next nibble */ + target_ulong psw_cb_msb; /* boolean */ - target_ureg iaoq_f; /* front */ - target_ureg iaoq_b; /* back, aka next instruction */ uint64_t iasq_f; uint64_t iasq_b; uint32_t fr0_shadow; /* flags, c, ca/cq, rm, d, enables */ float_status fp_status; - target_ureg cr[32]; /* control registers */ - target_ureg cr_back[2]; /* back of cr17/cr18 */ - target_ureg shadow[7]; /* shadow registers */ + target_ulong cr[32]; /* control registers */ + target_ulong cr_back[2]; /* back of cr17/cr18 */ + target_ulong shadow[7]; /* shadow registers */ - /* ??? The number of entries isn't specified by the architecture. */ + /* + * During unwind of a memory insn, the base register of the address. + * This is used to construct CR_IOR for pa2.0. + */ + uint32_t unwind_breg; + + /* + * ??? The number of entries isn't specified by the architecture. + * BTLBs are not supported in 64-bit machines. + */ +#define PA10_BTLB_FIXED 16 +#define PA10_BTLB_VARIABLE 0 #define HPPA_TLB_ENTRIES 256 -#define HPPA_BTLB_ENTRIES 0 - /* ??? Implement a unified itlb/dtlb for the moment. */ - /* ??? We should use a more intelligent data structure. */ - hppa_tlb_entry tlb[HPPA_TLB_ENTRIES]; + /* Index for round-robin tlb eviction. */ uint32_t tlb_last; + + /* + * For pa1.x, the partial initialized, still invalid tlb entry + * which has had ITLBA performed, but not yet ITLBP. + */ + HPPATLBEntry *tlb_partial; + + /* Linked list of all invalid (unused) tlb entries. */ + HPPATLBEntry *tlb_unused; + + /* Root of the search tree for all valid tlb entries. */ + IntervalTreeRoot tlb_root; + + HPPATLBEntry tlb[HPPA_TLB_ENTRIES]; } CPUHPPAState; /** @@ -215,50 +249,68 @@ typedef struct CPUArchState { * An HPPA CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUHPPAState env; QEMUTimer *alarm_timer; }; +/** + * HPPACPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * An HPPA CPU model. + */ +struct HPPACPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + DeviceReset parent_reset; +}; + #include "exec/cpu-all.h" -static inline int cpu_mmu_index(CPUHPPAState *env, bool ifetch) +static inline bool hppa_is_pa20(CPUHPPAState *env) { -#ifdef CONFIG_USER_ONLY - return MMU_USER_IDX; -#else - if (env->psw & (ifetch ? PSW_C : PSW_D)) { - return env->iaoq_f & 3; - } - return MMU_PHYS_IDX; /* mmu disabled */ -#endif + return object_dynamic_cast(OBJECT(env_cpu(env)), TYPE_HPPA64_CPU) != NULL; +} + +static inline int HPPA_BTLB_ENTRIES(CPUHPPAState *env) +{ + return hppa_is_pa20(env) ? 0 : PA10_BTLB_FIXED + PA10_BTLB_VARIABLE; } void hppa_translate_init(void); #define CPU_RESOLVING_TYPE TYPE_HPPA_CPU -static inline target_ulong hppa_form_gva_psw(target_ureg psw, uint64_t spc, - target_ureg off) +static inline uint64_t gva_offset_mask(target_ulong psw) +{ + return (psw & PSW_W + ? MAKE_64BIT_MASK(0, 62) + : MAKE_64BIT_MASK(0, 32)); +} + +static inline target_ulong hppa_form_gva_psw(target_ulong psw, uint64_t spc, + target_ulong off) { #ifdef CONFIG_USER_ONLY return off; #else - off &= (psw & PSW_W ? 0x3fffffffffffffffull : 0xffffffffull); - return spc | off; + return spc | (off & gva_offset_mask(psw)); #endif } static inline target_ulong hppa_form_gva(CPUHPPAState *env, uint64_t spc, - target_ureg off) + target_ulong off) { return hppa_form_gva_psw(env->psw, spc, off); } +hwaddr hppa_abs_to_phys_pa2_w0(vaddr addr); +hwaddr hppa_abs_to_phys_pa2_w1(vaddr addr); + /* * Since PSW_{I,CB} will never need to be in tb->flags, reuse them. * TB_FLAG_SR_SAME indicates that SR4 through SR7 all contain the @@ -268,28 +320,26 @@ static inline target_ulong hppa_form_gva(CPUHPPAState *env, uint64_t spc, #define TB_FLAG_PRIV_SHIFT 8 #define TB_FLAG_UNALIGN 0x400 -static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, target_ulong *pc, - target_ulong *cs_base, - uint32_t *pflags) +static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) { uint32_t flags = env->psw_n * PSW_N; /* TB lookup assumes that PC contains the complete virtual address. If we leave space+offset separate, we'll get ITLB misses to an incomplete virtual address. This also means that we must separate - out current cpu priviledge from the low bits of IAOQ_F. */ + out current cpu privilege from the low bits of IAOQ_F. */ #ifdef CONFIG_USER_ONLY *pc = env->iaoq_f & -4; *cs_base = env->iaoq_b & -4; flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; #else - /* ??? E, T, H, L, B, P bits need to be here, when implemented. */ - flags |= env->psw & (PSW_W | PSW_C | PSW_D); + /* ??? E, T, H, L, B bits need to be here, when implemented. */ + flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; - *pc = (env->psw & PSW_C - ? hppa_form_gva_psw(env->psw, env->iasq_f, env->iaoq_f & -4) - : env->iaoq_f & -4); + *pc = hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0), + env->iaoq_f & -4); *cs_base = env->iasq_f; /* Insert a difference between IAOQ_B and IAOQ_F within the otherwise zero @@ -297,8 +347,8 @@ static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, target_ulong *pc, which is the primary case we care about -- using goto_tb within a page. Failure is indicated by a zero difference. */ if (env->iasq_f == env->iasq_b) { - target_sreg diff = env->iaoq_b - env->iaoq_f; - if (TARGET_REGISTER_BITS == 32 || diff == (int32_t)diff) { + target_long diff = env->iaoq_b - env->iaoq_f; + if (diff == (int32_t)diff) { *cs_base |= (uint32_t)diff; } } @@ -312,8 +362,8 @@ static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, target_ulong *pc, *pflags = flags; } -target_ureg cpu_hppa_get_psw(CPUHPPAState *env); -void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg); +target_ulong cpu_hppa_get_psw(CPUHPPAState *env); +void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong); void cpu_hppa_loaded_fr0(CPUHPPAState *env); #ifdef CONFIG_USER_ONLY @@ -322,18 +372,26 @@ static inline void cpu_hppa_change_prot_id(CPUHPPAState *env) { } void cpu_hppa_change_prot_id(CPUHPPAState *env); #endif -hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr); 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_dump_state(CPUState *cs, FILE *f, int); #ifndef CONFIG_USER_ONLY +void hppa_ptlbe(CPUHPPAState *env); +hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr); +void hppa_set_ior_and_isr(CPUHPPAState *env, vaddr addr, bool mmu_disabled); bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); void hppa_cpu_do_interrupt(CPUState *cpu); bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req); int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, - int type, hwaddr *pphys, int *pprot); + int type, hwaddr *pphys, int *pprot, + HPPATLBEntry **tlb_entry); +void hppa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, + vaddr addr, unsigned size, + MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, uintptr_t retaddr); extern const MemoryRegionOps hppa_io_eir_ops; extern const VMStateDescription vmstate_hppa_cpu; void hppa_cpu_alarm_timer(void *); @@ -341,4 +399,6 @@ int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr); #endif G_NORETURN void hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra); +#define CPU_RESOLVING_TYPE TYPE_HPPA_CPU + #endif /* HPPA_CPU_H */ diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c new file mode 100644 index 0000000000..576f283b04 --- /dev/null +++ b/target/hppa/fpu_helper.c @@ -0,0 +1,450 @@ +/* + * Helpers for HPPA FPU instructions. + * + * Copyright (c) 2016 Richard Henderson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "fpu/softfloat.h" + +void HELPER(loaded_fr0)(CPUHPPAState *env) +{ + uint32_t shadow = env->fr[0] >> 32; + int rm, d; + + env->fr0_shadow = shadow; + + switch (extract32(shadow, 9, 2)) { + default: + rm = float_round_nearest_even; + break; + case 1: + rm = float_round_to_zero; + break; + case 2: + rm = float_round_up; + break; + case 3: + rm = float_round_down; + break; + } + set_float_rounding_mode(rm, &env->fp_status); + + d = extract32(shadow, 5, 1); + set_flush_to_zero(d, &env->fp_status); + set_flush_inputs_to_zero(d, &env->fp_status); +} + +void cpu_hppa_loaded_fr0(CPUHPPAState *env) +{ + helper_loaded_fr0(env); +} + +#define CONVERT_BIT(X, SRC, DST) \ + ((SRC) > (DST) \ + ? (X) / ((SRC) / (DST)) & (DST) \ + : ((X) & (SRC)) * ((DST) / (SRC))) + +static void update_fr0_op(CPUHPPAState *env, uintptr_t ra) +{ + uint32_t soft_exp = get_float_exception_flags(&env->fp_status); + uint32_t hard_exp = 0; + uint32_t shadow = env->fr0_shadow; + + if (likely(soft_exp == 0)) { + env->fr[0] = (uint64_t)shadow << 32; + return; + } + set_float_exception_flags(0, &env->fp_status); + + hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, 1u << 0); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, 1u << 1); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, 1u << 2); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, 1u << 3); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, 1u << 4); + shadow |= hard_exp << (32 - 5); + env->fr0_shadow = shadow; + env->fr[0] = (uint64_t)shadow << 32; + + if (hard_exp & shadow) { + hppa_dynamic_excp(env, EXCP_ASSIST, ra); + } +} + +float32 HELPER(fsqrt_s)(CPUHPPAState *env, float32 arg) +{ + float32 ret = float32_sqrt(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(frnd_s)(CPUHPPAState *env, float32 arg) +{ + float32 ret = float32_round_to_int(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fadd_s)(CPUHPPAState *env, float32 a, float32 b) +{ + float32 ret = float32_add(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fsub_s)(CPUHPPAState *env, float32 a, float32 b) +{ + float32 ret = float32_sub(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fmpy_s)(CPUHPPAState *env, float32 a, float32 b) +{ + float32 ret = float32_mul(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fdiv_s)(CPUHPPAState *env, float32 a, float32 b) +{ + float32 ret = float32_div(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fsqrt_d)(CPUHPPAState *env, float64 arg) +{ + float64 ret = float64_sqrt(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(frnd_d)(CPUHPPAState *env, float64 arg) +{ + float64 ret = float64_round_to_int(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fadd_d)(CPUHPPAState *env, float64 a, float64 b) +{ + float64 ret = float64_add(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fsub_d)(CPUHPPAState *env, float64 a, float64 b) +{ + float64 ret = float64_sub(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fmpy_d)(CPUHPPAState *env, float64 a, float64 b) +{ + float64 ret = float64_mul(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fdiv_d)(CPUHPPAState *env, float64 a, float64 b) +{ + float64 ret = float64_div(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg) +{ + float64 ret = float32_to_float64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_d_s)(CPUHPPAState *env, float64 arg) +{ + float32 ret = float64_to_float32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_w_s)(CPUHPPAState *env, int32_t arg) +{ + float32 ret = int32_to_float32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_dw_s)(CPUHPPAState *env, int64_t arg) +{ + float32 ret = int64_to_float32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_w_d)(CPUHPPAState *env, int32_t arg) +{ + float64 ret = int32_to_float64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_dw_d)(CPUHPPAState *env, int64_t arg) +{ + float64 ret = int64_to_float64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int32_t HELPER(fcnv_s_w)(CPUHPPAState *env, float32 arg) +{ + int32_t ret = float32_to_int32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int32_t HELPER(fcnv_d_w)(CPUHPPAState *env, float64 arg) +{ + int32_t ret = float64_to_int32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int64_t HELPER(fcnv_s_dw)(CPUHPPAState *env, float32 arg) +{ + int64_t ret = float32_to_int64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int64_t HELPER(fcnv_d_dw)(CPUHPPAState *env, float64 arg) +{ + int64_t ret = float64_to_int64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int32_t HELPER(fcnv_t_s_w)(CPUHPPAState *env, float32 arg) +{ + int32_t ret = float32_to_int32_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int32_t HELPER(fcnv_t_d_w)(CPUHPPAState *env, float64 arg) +{ + int32_t ret = float64_to_int32_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int64_t HELPER(fcnv_t_s_dw)(CPUHPPAState *env, float32 arg) +{ + int64_t ret = float32_to_int64_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int64_t HELPER(fcnv_t_d_dw)(CPUHPPAState *env, float64 arg) +{ + int64_t ret = float64_to_int64_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_uw_s)(CPUHPPAState *env, uint32_t arg) +{ + float32 ret = uint32_to_float32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_udw_s)(CPUHPPAState *env, uint64_t arg) +{ + float32 ret = uint64_to_float32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_uw_d)(CPUHPPAState *env, uint32_t arg) +{ + float64 ret = uint32_to_float64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_udw_d)(CPUHPPAState *env, uint64_t arg) +{ + float64 ret = uint64_to_float64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint32_t HELPER(fcnv_s_uw)(CPUHPPAState *env, float32 arg) +{ + uint32_t ret = float32_to_uint32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint32_t HELPER(fcnv_d_uw)(CPUHPPAState *env, float64 arg) +{ + uint32_t ret = float64_to_uint32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint64_t HELPER(fcnv_s_udw)(CPUHPPAState *env, float32 arg) +{ + uint64_t ret = float32_to_uint64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint64_t HELPER(fcnv_d_udw)(CPUHPPAState *env, float64 arg) +{ + uint64_t ret = float64_to_uint64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint32_t HELPER(fcnv_t_s_uw)(CPUHPPAState *env, float32 arg) +{ + uint32_t ret = float32_to_uint32_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint32_t HELPER(fcnv_t_d_uw)(CPUHPPAState *env, float64 arg) +{ + uint32_t ret = float64_to_uint32_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint64_t HELPER(fcnv_t_s_udw)(CPUHPPAState *env, float32 arg) +{ + uint64_t ret = float32_to_uint64_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint64_t HELPER(fcnv_t_d_udw)(CPUHPPAState *env, float64 arg) +{ + uint64_t ret = float64_to_uint64_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +static void update_fr0_cmp(CPUHPPAState *env, uint32_t y, + uint32_t c, FloatRelation r) +{ + uint32_t shadow = env->fr0_shadow; + + switch (r) { + case float_relation_greater: + c = extract32(c, 4, 1); + break; + case float_relation_less: + c = extract32(c, 3, 1); + break; + case float_relation_equal: + c = extract32(c, 2, 1); + break; + case float_relation_unordered: + c = extract32(c, 1, 1); + break; + default: + g_assert_not_reached(); + } + + if (y) { + /* targeted comparison */ + /* set fpsr[ca[y - 1]] to current compare */ + shadow = deposit32(shadow, 21 - (y - 1), 1, c); + } else { + /* queued comparison */ + /* shift cq right by one place */ + shadow = deposit32(shadow, 11, 10, extract32(shadow, 12, 10)); + /* move fpsr[c] to fpsr[cq[0]] */ + shadow = deposit32(shadow, 21, 1, extract32(shadow, 26, 1)); + /* set fpsr[c] to current compare */ + shadow = deposit32(shadow, 26, 1, c); + } + + env->fr0_shadow = shadow; + env->fr[0] = (uint64_t)shadow << 32; +} + +void HELPER(fcmp_s)(CPUHPPAState *env, float32 a, float32 b, + uint32_t y, uint32_t c) +{ + FloatRelation r; + if (c & 1) { + r = float32_compare(a, b, &env->fp_status); + } else { + r = float32_compare_quiet(a, b, &env->fp_status); + } + update_fr0_op(env, GETPC()); + update_fr0_cmp(env, y, c, r); +} + +void HELPER(fcmp_d)(CPUHPPAState *env, float64 a, float64 b, + uint32_t y, uint32_t c) +{ + FloatRelation r; + if (c & 1) { + r = float64_compare(a, b, &env->fp_status); + } else { + r = float64_compare_quiet(a, b, &env->fp_status); + } + update_fr0_op(env, GETPC()); + update_fr0_cmp(env, y, c, r); +} + +float32 HELPER(fmpyfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) +{ + float32 ret = float32_muladd(a, b, c, 0, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fmpynfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) +{ + float32 ret = float32_muladd(a, b, c, float_muladd_negate_product, + &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fmpyfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) +{ + float64 ret = float64_muladd(a, b, c, 0, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fmpynfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) +{ + float64 ret = float64_muladd(a, b, c, float_muladd_negate_product, + &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c index 729c37b2ca..4a965b38d7 100644 --- a/target/hppa/gdbstub.c +++ b/target/hppa/gdbstub.c @@ -19,13 +19,18 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" + +/* + * GDB 15 only supports PA1.0 via the remote protocol, and ignores + * any provided xml. Which means that any attempt to provide more + * data results in "Remote 'g' packet reply is too long". + */ int hppa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - HPPACPU *cpu = HPPA_CPU(cs); - CPUHPPAState *env = &cpu->env; - target_ureg val; + CPUHPPAState *env = cpu_env(cs); + uint32_t val; switch (n) { case 0: @@ -139,24 +144,13 @@ int hppa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) break; } - if (TARGET_REGISTER_BITS == 64) { - return gdb_get_reg64(mem_buf, val); - } else { - return gdb_get_reg32(mem_buf, val); - } + return gdb_get_reg32(mem_buf, val); } int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - HPPACPU *cpu = HPPA_CPU(cs); - CPUHPPAState *env = &cpu->env; - target_ureg val; - - if (TARGET_REGISTER_BITS == 64) { - val = ldq_p(mem_buf); - } else { - val = ldl_p(mem_buf); - } + CPUHPPAState *env = cpu_env(cs); + uint32_t val = ldl_p(mem_buf); switch (n) { case 0: @@ -166,7 +160,7 @@ int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->gr[n] = val; break; case 32: - env->cr[CR_SAR] = val; + env->cr[CR_SAR] = val & (hppa_is_pa20(env) ? 63 : 31); break; case 33: env->iaoq_f = val; @@ -278,5 +272,5 @@ int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) } break; } - return sizeof(target_ureg); + return 4; } diff --git a/target/hppa/helper.c b/target/hppa/helper.c index e2758d8df3..9d217d051c 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -25,22 +25,32 @@ #include "exec/helper-proto.h" #include "qemu/qemu-print.h" -target_ureg cpu_hppa_get_psw(CPUHPPAState *env) +target_ulong cpu_hppa_get_psw(CPUHPPAState *env) { - target_ureg psw; + target_ulong psw; + target_ulong mask1 = (target_ulong)-1 / 0xf; + target_ulong maskf = (target_ulong)-1 / 0xffff * 0xf; /* Fold carry bits down to 8 consecutive bits. */ - /* ??? Needs tweaking for hppa64. */ - /* .......b...c...d...e...f...g...h */ - psw = (env->psw_cb >> 4) & 0x01111111; - /* .......b..bc..cd..de..ef..fg..gh */ + /* ^^^b^^^c^^^d^^^e^^^f^^^g^^^h^^^i^^^j^^^k^^^l^^^m^^^n^^^o^^^p^^^^ */ + psw = (env->psw_cb >> 4) & mask1; + /* .......b...c...d...e...f...g...h...i...j...k...l...m...n...o...p */ psw |= psw >> 3; - /* .............bcd............efgh */ - psw |= (psw >> 6) & 0x000f000f; - /* .........................bcdefgh */ - psw |= (psw >> 12) & 0xf; - psw |= env->psw_cb_msb << 7; - psw = (psw & 0xff) << 8; + /* .......b..bc..cd..de..ef..fg..gh..hi..ij..jk..kl..lm..mn..no..op */ + psw |= psw >> 6; + psw &= maskf; + /* .............bcd............efgh............ijkl............mnop */ + psw |= psw >> 12; + /* .............bcd.........bcdefgh........efghijkl........ijklmnop */ + psw |= env->psw_cb_msb << 39; + /* .............bcd........abcdefgh........efghijkl........ijklmnop */ + + /* For hppa64, the two 8-bit fields are discontiguous. */ + if (hppa_is_pa20(env)) { + psw = (psw & 0xff00000000ull) | ((psw & 0xff) << 8); + } else { + psw = (psw & 0xff) << 8; + } psw |= env->psw_n * PSW_N; psw |= (env->psw_v < 0) * PSW_V; @@ -49,16 +59,37 @@ target_ureg cpu_hppa_get_psw(CPUHPPAState *env) return psw; } -void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw) +void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw) { - target_ureg old_psw = env->psw; - target_ureg cb = 0; + uint64_t reserved; + target_ulong cb = 0; + + /* Do not allow reserved bits to be set. */ + if (hppa_is_pa20(env)) { + reserved = MAKE_64BIT_MASK(40, 24) | MAKE_64BIT_MASK(28, 4); + reserved |= PSW_G; /* PA1.x only */ + reserved |= PSW_E; /* not implemented */ + } else { + reserved = MAKE_64BIT_MASK(32, 32) | MAKE_64BIT_MASK(28, 2); + reserved |= PSW_O | PSW_W; /* PA2.0 only */ + reserved |= PSW_E | PSW_Y | PSW_Z; /* not implemented */ + } + psw &= ~reserved; + + env->psw = psw & (uint32_t)~(PSW_N | PSW_V | PSW_CB); - env->psw = psw & ~(PSW_N | PSW_V | PSW_CB); env->psw_n = (psw / PSW_N) & 1; env->psw_v = -((psw / PSW_V) & 1); - env->psw_cb_msb = (psw >> 15) & 1; + env->psw_cb_msb = (psw >> 39) & 1; + cb |= ((psw >> 38) & 1) << 60; + cb |= ((psw >> 37) & 1) << 56; + cb |= ((psw >> 36) & 1) << 52; + cb |= ((psw >> 35) & 1) << 48; + cb |= ((psw >> 34) & 1) << 44; + cb |= ((psw >> 33) & 1) << 40; + cb |= ((psw >> 32) & 1) << 36; + cb |= ((psw >> 15) & 1) << 32; cb |= ((psw >> 14) & 1) << 28; cb |= ((psw >> 13) & 1) << 24; cb |= ((psw >> 12) & 1) << 20; @@ -67,27 +98,30 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw) cb |= ((psw >> 9) & 1) << 8; cb |= ((psw >> 8) & 1) << 4; env->psw_cb = cb; - - /* If PSW_P changes, it affects how we translate addresses. */ - if ((psw ^ old_psw) & PSW_P) { -#ifndef CONFIG_USER_ONLY - tlb_flush_by_mmuidx(env_cpu(env), 0xf); -#endif - } } void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - HPPACPU *cpu = HPPA_CPU(cs); - CPUHPPAState *env = &cpu->env; - target_ureg psw = cpu_hppa_get_psw(env); - target_ureg psw_cb; + CPUHPPAState *env = cpu_env(cs); + target_ulong psw = cpu_hppa_get_psw(env); + target_ulong psw_cb; char psw_c[20]; - int i; + int i, w; + uint64_t m; - qemu_fprintf(f, "IA_F " TARGET_FMT_lx " IA_B " TARGET_FMT_lx "\n", + if (hppa_is_pa20(env)) { + w = 16; + m = UINT64_MAX; + } else { + w = 8; + m = UINT32_MAX; + } + + qemu_fprintf(f, "IA_F " TARGET_FMT_lx " IA_B " TARGET_FMT_lx + " IIR %0*" PRIx64 "\n", hppa_form_gva_psw(psw, env->iasq_f, env->iaoq_f), - hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b)); + hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b), + w, m & env->cr[CR_IIR]); psw_c[0] = (psw & PSW_W ? 'W' : '-'); psw_c[1] = (psw & PSW_E ? 'E' : '-'); @@ -108,13 +142,15 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) psw_c[16] = (psw & PSW_D ? 'D' : '-'); psw_c[17] = (psw & PSW_I ? 'I' : '-'); psw_c[18] = '\0'; - psw_cb = ((env->psw_cb >> 4) & 0x01111111) | (env->psw_cb_msb << 28); + psw_cb = ((env->psw_cb >> 4) & 0x1111111111111111ull) + | (env->psw_cb_msb << 60); - qemu_fprintf(f, "PSW " TREG_FMT_lx " CB " TREG_FMT_lx " %s\n", - psw, psw_cb, psw_c); + qemu_fprintf(f, "PSW %0*" PRIx64 " CB %0*" PRIx64 " %s\n", + w, m & psw, w, m & psw_cb, psw_c); for (i = 0; i < 32; i++) { - qemu_fprintf(f, "GR%02d " TREG_FMT_lx "%c", i, env->gr[i], + qemu_fprintf(f, "GR%02d %0*" PRIx64 "%c", + i, w, m & env->gr[i], (i & 3) == 3 ? '\n' : ' '); } #ifndef CONFIG_USER_ONLY diff --git a/target/hppa/helper.h b/target/hppa/helper.h index c7e35ce8c7..5900fd70bc 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -1,24 +1,28 @@ -#if TARGET_REGISTER_BITS == 64 -# define dh_alias_tr i64 -# define dh_typecode_tr dh_typecode_i64 -#else -# define dh_alias_tr i32 -# define dh_typecode_tr dh_typecode_i32 -#endif -#define dh_ctype_tr target_ureg - DEF_HELPER_2(excp, noreturn, env, int) -DEF_HELPER_FLAGS_2(tsv, TCG_CALL_NO_WG, void, env, tr) -DEF_HELPER_FLAGS_2(tcond, TCG_CALL_NO_WG, void, env, tr) +DEF_HELPER_FLAGS_2(tsv, TCG_CALL_NO_WG, void, env, tl) +DEF_HELPER_FLAGS_2(tcond, TCG_CALL_NO_WG, void, env, tl) -DEF_HELPER_FLAGS_3(stby_b, TCG_CALL_NO_WG, void, env, tl, tr) -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_3(stby_b, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(stby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(stby_e, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(stby_e_parallel, TCG_CALL_NO_WG, void, env, tl, tl) + +DEF_HELPER_FLAGS_3(stdby_b, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(stdby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(stdby_e, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(stdby_e_parallel, TCG_CALL_NO_WG, void, env, tl, tl) 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_2(hadd_ss, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(hadd_us, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(havg, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_3(hshladd, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) +DEF_HELPER_FLAGS_3(hshradd, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) +DEF_HELPER_FLAGS_2(hsub_ss, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(hsub_us, TCG_CALL_NO_RWG_SE, i64, i64, i64) + +DEF_HELPER_FLAGS_4(probe, TCG_CALL_NO_WG, tl, env, tl, i32, i32) DEF_HELPER_FLAGS_1(loaded_fr0, TCG_CALL_NO_RWG, void, env) @@ -77,22 +81,25 @@ DEF_HELPER_FLAGS_4(fmpynfadd_s, TCG_CALL_NO_RWG, i32, env, i32, i32, i32) DEF_HELPER_FLAGS_4(fmpyfadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_4(fmpynfadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) -DEF_HELPER_FLAGS_0(read_interval_timer, TCG_CALL_NO_RWG, tr) +DEF_HELPER_FLAGS_0(read_interval_timer, TCG_CALL_NO_RWG, tl) #ifndef CONFIG_USER_ONLY DEF_HELPER_1(halt, noreturn, env) DEF_HELPER_1(reset, noreturn, env) -DEF_HELPER_1(getshadowregs, void, env) DEF_HELPER_1(rfi, void, env) DEF_HELPER_1(rfi_r, void, env) -DEF_HELPER_FLAGS_2(write_interval_timer, TCG_CALL_NO_RWG, void, env, tr) -DEF_HELPER_FLAGS_2(write_eirr, TCG_CALL_NO_RWG, void, env, tr) -DEF_HELPER_FLAGS_2(write_eiem, TCG_CALL_NO_RWG, void, env, tr) -DEF_HELPER_FLAGS_2(swap_system_mask, TCG_CALL_NO_RWG, tr, env, tr) -DEF_HELPER_FLAGS_3(itlba, TCG_CALL_NO_RWG, void, env, tl, tr) -DEF_HELPER_FLAGS_3(itlbp, TCG_CALL_NO_RWG, void, env, tl, tr) +DEF_HELPER_FLAGS_2(write_interval_timer, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(write_eirr, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(swap_system_mask, TCG_CALL_NO_RWG, tl, env, tl) +DEF_HELPER_FLAGS_3(itlba_pa11, TCG_CALL_NO_RWG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(itlbp_pa11, TCG_CALL_NO_RWG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(idtlbt_pa20, TCG_CALL_NO_RWG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(iitlbt_pa20, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_FLAGS_2(ptlb, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(ptlb_l, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_1(ptlbe, TCG_CALL_NO_RWG, void, env) -DEF_HELPER_FLAGS_2(lpa, TCG_CALL_NO_WG, tr, env, tl) +DEF_HELPER_FLAGS_2(lpa, TCG_CALL_NO_WG, tl, env, tl) DEF_HELPER_FLAGS_1(change_prot_id, TCG_CALL_NO_RWG, void, env) +DEF_HELPER_1(diag_btlb, void, env) +DEF_HELPER_1(diag_console_output, void, env) #endif diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index c7a7e997f9..71074a64c1 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -24,16 +24,17 @@ %assemble_sr3 13:1 14:2 %assemble_sr3x 13:1 14:2 !function=expand_sr3x -%assemble_11a 0:s1 4:10 !function=expand_shl3 +%assemble_11a 4:12 0:1 !function=expand_11a %assemble_12 0:s1 2:1 3:10 !function=expand_shl2 -%assemble_12a 0:s1 3:11 !function=expand_shl2 +%assemble_12a 3:13 0:1 !function=expand_12a +%assemble_16 0:16 !function=expand_16 %assemble_17 0:s1 16:5 2:1 3:10 !function=expand_shl2 %assemble_22 0:s1 16:10 2:1 3:10 !function=expand_shl2 +%assemble_sp 14:2 !function=sp0_if_wide %assemble_21 0:s1 1:11 14:2 16:5 12:2 !function=expand_shl11 %lowsign_11 0:s1 1:10 -%lowsign_14 0:s1 1:13 %sm_imm 16:10 !function=expand_sm_imm @@ -46,41 +47,67 @@ %im5_0 0:s1 1:4 %im5_16 16:s1 17:4 +%len5 0:5 !function=assemble_6 +%len6_8 8:1 0:5 !function=assemble_6 +%len6_12 12:1 0:5 !function=assemble_6 +%cpos6_11 11:1 5:5 %ma_to_m 5:1 13:1 !function=ma_to_m %ma2_to_m 2:2 !function=ma_to_m %pos_to_m 0:1 !function=pos_to_m %neg_to_m 0:1 !function=neg_to_m %a_to_m 2:1 !function=neg_to_m +%cmpbid_c 13:2 !function=cmpbid_c +%d_5 5:1 !function=pa20_d +%d_11 11:1 !function=pa20_d +%d_13 13:1 !function=pa20_d #### # Argument set definitions #### +&empty + # All insns that need to form a virtual address should use this set. &ldst t b x disp sp m scale size -&rr_cf t r cf +&rr_cf_d t r cf d +&rrr t r1 r2 &rrr_cf t r1 r2 cf -&rrr_cf_sh t r1 r2 cf sh +&rrr_cf_d t r1 r2 cf d +&rrr_sh t r1 r2 sh +&rrr_cf_d_sh t r1 r2 cf d sh +&rri t r i &rri_cf t r i cf +&rri_cf_d t r i cf d &rrb_c_f disp n c f r1 r2 +&rrb_c_d_f disp n c d f r1 r2 &rib_c_f disp n c f r i +&rib_c_d_f disp n c d f r i #### # Format definitions #### -@rr_cf ...... r:5 ..... cf:4 ....... t:5 &rr_cf +@rr_cf_d ...... r:5 ..... cf:4 ...... . t:5 &rr_cf_d d=%d_5 +@rrr ...... r2:5 r1:5 .... ....... t:5 &rrr @rrr_cf ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf -@rrr_cf_sh ...... r2:5 r1:5 cf:4 .... sh:2 . t:5 &rrr_cf_sh -@rrr_cf_sh0 ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf_sh sh=0 +@rrr_cf_d ...... r2:5 r1:5 cf:4 ...... . t:5 &rrr_cf_d d=%d_5 +@rrr_sh ...... r2:5 r1:5 ........ sh:2 . t:5 &rrr_sh +@rrr_cf_d_sh ...... r2:5 r1:5 cf:4 .... sh:2 . t:5 &rrr_cf_d_sh d=%d_5 +@rrr_cf_d_sh0 ...... r2:5 r1:5 cf:4 ...... . t:5 &rrr_cf_d_sh d=%d_5 sh=0 @rri_cf ...... r:5 t:5 cf:4 . ........... &rri_cf i=%lowsign_11 +@rri_cf_d ...... r:5 t:5 cf:4 . ........... \ + &rri_cf_d d=%d_11 i=%lowsign_11 @rrb_cf ...... r2:5 r1:5 c:3 ........... n:1 . \ &rrb_c_f disp=%assemble_12 +@rrb_cdf ...... r2:5 r1:5 c:3 ........... n:1 . \ + &rrb_c_d_f disp=%assemble_12 @rib_cf ...... r:5 ..... c:3 ........... n:1 . \ &rib_c_f disp=%assemble_12 i=%im5_16 +@rib_cdf ...... r:5 ..... c:3 ........... n:1 . \ + &rib_c_d_f disp=%assemble_12 i=%im5_16 #### # System @@ -123,13 +150,14 @@ getshadowregs 1111 1111 1111 1101 1110 1010 1101 0010 nop 000001 ----- ----- -- 11001010 0 ----- # fdc, disp nop_addrx 000001 ..... ..... -- 01001010 . ----- @addrx # fdc, index nop_addrx 000001 ..... ..... -- 01001011 . ----- @addrx # fdce -nop_addrx 000001 ..... ..... --- 0001010 . ----- @addrx # fic 0x0a -nop_addrx 000001 ..... ..... -- 01001111 . 00000 @addrx # fic 0x4f -nop_addrx 000001 ..... ..... --- 0001011 . ----- @addrx # fice +fic 000001 ..... ..... --- 0001010 . ----- @addrx # fic 0x0a +fic 000001 ..... ..... -- 01001111 . 00000 @addrx # fic 0x4f +fic 000001 ..... ..... --- 0001011 . ----- @addrx # fice nop_addrx 000001 ..... ..... -- 01001110 . 00000 @addrx # pdc probe 000001 b:5 ri:5 sp:2 imm:1 100011 write:1 0 t:5 +# pa1.x tlb insert instructions ixtlbx 000001 b:5 r:5 sp:2 0100000 addr:1 0 00000 data=1 ixtlbx 000001 b:5 r:5 ... 000000 addr:1 0 00000 \ sp=%assemble_sr3x data=0 @@ -137,9 +165,26 @@ ixtlbx 000001 b:5 r:5 ... 000000 addr:1 0 00000 \ # pcxl and pcxl2 Fast TLB Insert instructions ixtlbxf 000001 00000 r:5 00 0 data:1 01000 addr:1 0 00000 -pxtlbx 000001 b:5 x:5 sp:2 0100100 local:1 m:1 ----- data=1 -pxtlbx 000001 b:5 x:5 ... 000100 local:1 m:1 ----- \ - sp=%assemble_sr3x data=0 +# pa2.0 tlb insert idtlbt and iitlbt instructions +ixtlbt 000001 r2:5 r1:5 000 data:1 100000 0 00000 # idtlbt + +# pdtlb, pitlb +pxtlb 000001 b:5 x:5 sp:2 01001000 m:1 ----- \ + &ldst disp=0 scale=0 size=0 t=0 +pxtlb 000001 b:5 x:5 ... 0001000 m:1 ----- \ + &ldst disp=0 scale=0 size=0 t=0 sp=%assemble_sr3x + +# ... pa20 local +pxtlb_l 000001 b:5 x:5 sp:2 01011000 m:1 ----- \ + &ldst disp=0 scale=0 size=0 t=0 +pxtlb_l 000001 b:5 x:5 ... 0011000 m:1 ----- \ + &ldst disp=0 scale=0 size=0 t=0 sp=%assemble_sr3x + +# pdtlbe, pitlbe +pxtlbe 000001 b:5 x:5 sp:2 01001001 m:1 ----- \ + &ldst disp=0 scale=0 size=0 t=0 +pxtlbe 000001 b:5 x:5 ... 0001001 m:1 ----- \ + &ldst disp=0 scale=0 size=0 t=0 sp=%assemble_sr3x lpa 000001 b:5 x:5 sp:2 01001101 m:1 t:5 \ &ldst disp=0 scale=0 size=0 @@ -150,34 +195,40 @@ lci 000001 ----- ----- -- 01001100 0 t:5 # Arith/Log #### -andcm 000010 ..... ..... .... 000000 - ..... @rrr_cf -and 000010 ..... ..... .... 001000 - ..... @rrr_cf -or 000010 ..... ..... .... 001001 - ..... @rrr_cf -xor 000010 ..... ..... .... 001010 0 ..... @rrr_cf -uxor 000010 ..... ..... .... 001110 0 ..... @rrr_cf +andcm 000010 ..... ..... .... 000000 . ..... @rrr_cf_d +and 000010 ..... ..... .... 001000 . ..... @rrr_cf_d +or 000010 ..... ..... .... 001001 . ..... @rrr_cf_d +xor 000010 ..... ..... .... 001010 . ..... @rrr_cf_d +uxor 000010 ..... ..... .... 001110 . ..... @rrr_cf_d ds 000010 ..... ..... .... 010001 0 ..... @rrr_cf -cmpclr 000010 ..... ..... .... 100010 0 ..... @rrr_cf -uaddcm 000010 ..... ..... .... 100110 0 ..... @rrr_cf -uaddcm_tc 000010 ..... ..... .... 100111 0 ..... @rrr_cf -dcor 000010 ..... 00000 .... 101110 0 ..... @rr_cf -dcor_i 000010 ..... 00000 .... 101111 0 ..... @rr_cf +cmpclr 000010 ..... ..... .... 100010 . ..... @rrr_cf_d +uaddcm 000010 ..... ..... .... 100110 . ..... @rrr_cf_d +uaddcm_tc 000010 ..... ..... .... 100111 . ..... @rrr_cf_d +dcor 000010 ..... 00000 .... 101110 . ..... @rr_cf_d +dcor_i 000010 ..... 00000 .... 101111 . ..... @rr_cf_d -add 000010 ..... ..... .... 0110.. - ..... @rrr_cf_sh -add_l 000010 ..... ..... .... 1010.. 0 ..... @rrr_cf_sh -add_tsv 000010 ..... ..... .... 1110.. 0 ..... @rrr_cf_sh -add_c 000010 ..... ..... .... 011100 0 ..... @rrr_cf_sh0 -add_c_tsv 000010 ..... ..... .... 111100 0 ..... @rrr_cf_sh0 +add 000010 ..... ..... .... 0110.. . ..... @rrr_cf_d_sh +add_l 000010 ..... ..... .... 1010.. . ..... @rrr_cf_d_sh +add_tsv 000010 ..... ..... .... 1110.. . ..... @rrr_cf_d_sh +{ + add_c 000010 ..... ..... .... 011100 . ..... @rrr_cf_d_sh0 + hshladd 000010 ..... ..... 0000 0111.. 0 ..... @rrr_sh +} +add_c_tsv 000010 ..... ..... .... 111100 . ..... @rrr_cf_d_sh0 -sub 000010 ..... ..... .... 010000 - ..... @rrr_cf -sub_tsv 000010 ..... ..... .... 110000 0 ..... @rrr_cf -sub_tc 000010 ..... ..... .... 010011 0 ..... @rrr_cf -sub_tsv_tc 000010 ..... ..... .... 110011 0 ..... @rrr_cf -sub_b 000010 ..... ..... .... 010100 0 ..... @rrr_cf -sub_b_tsv 000010 ..... ..... .... 110100 0 ..... @rrr_cf +sub 000010 ..... ..... .... 010000 . ..... @rrr_cf_d +sub_tsv 000010 ..... ..... .... 110000 . ..... @rrr_cf_d +sub_tc 000010 ..... ..... .... 010011 . ..... @rrr_cf_d +sub_tsv_tc 000010 ..... ..... .... 110011 . ..... @rrr_cf_d +{ + sub_b 000010 ..... ..... .... 010100 . ..... @rrr_cf_d + hshradd 000010 ..... ..... 0000 0101.. 0 ..... @rrr_sh +} +sub_b_tsv 000010 ..... ..... .... 110100 . ..... @rrr_cf_d ldil 001000 t:5 ..................... i=%assemble_21 addil 001010 r:5 ..................... i=%assemble_21 -ldo 001101 b:5 t:5 -- .............. i=%lowsign_14 +ldo 001101 b:5 t:5 ................ i=%assemble_16 addi 101101 ..... ..... .... 0 ........... @rri_cf addi_tsv 101101 ..... ..... .... 1 ........... @rri_cf @@ -187,7 +238,28 @@ addi_tc_tsv 101100 ..... ..... .... 1 ........... @rri_cf subi 100101 ..... ..... .... 0 ........... @rri_cf subi_tsv 100101 ..... ..... .... 1 ........... @rri_cf -cmpiclr 100100 ..... ..... .... 0 ........... @rri_cf +cmpiclr 100100 ..... ..... .... . ........... @rri_cf_d + +hadd 000010 ..... ..... 00000011 11 0 ..... @rrr +hadd_ss 000010 ..... ..... 00000011 01 0 ..... @rrr +hadd_us 000010 ..... ..... 00000011 00 0 ..... @rrr + +havg 000010 ..... ..... 00000010 11 0 ..... @rrr + +hshl 111110 00000 r:5 100010 i:4 0 t:5 &rri +hshr_s 111110 r:5 00000 110011 i:4 0 t:5 &rri +hshr_u 111110 r:5 00000 110010 i:4 0 t:5 &rri + +hsub 000010 ..... ..... 00000001 11 0 ..... @rrr +hsub_ss 000010 ..... ..... 00000001 01 0 ..... @rrr +hsub_us 000010 ..... ..... 00000001 00 0 ..... @rrr + +mixh_l 111110 ..... ..... 1 00 00100000 ..... @rrr +mixh_r 111110 ..... ..... 1 10 00100000 ..... @rrr +mixw_l 111110 ..... ..... 1 00 00000000 ..... @rrr +mixw_r 111110 ..... ..... 1 10 00000000 ..... @rrr + +permh 111110 r1:5 r2:5 0 c0:2 0 c1:2 c2:2 c3:2 0 t:5 #### # Index Mem @@ -204,10 +276,16 @@ ld 000011 ..... ..... .. . 0 -- 00 size:2 ...... @ldstx st 000011 ..... ..... .. . 1 -- 10 size:2 ...... @stim5 ldc 000011 ..... ..... .. . 1 -- 0111 ...... @ldim5 size=2 ldc 000011 ..... ..... .. . 0 -- 0111 ...... @ldstx size=2 +ldc 000011 ..... ..... .. . 1 -- 0101 ...... @ldim5 size=3 +ldc 000011 ..... ..... .. . 0 -- 0101 ...... @ldstx size=3 lda 000011 ..... ..... .. . 1 -- 0110 ...... @ldim5 size=2 lda 000011 ..... ..... .. . 0 -- 0110 ...... @ldstx size=2 +lda 000011 ..... ..... .. . 1 -- 0100 ...... @ldim5 size=3 +lda 000011 ..... ..... .. . 0 -- 0100 ...... @ldstx size=3 sta 000011 ..... ..... .. . 1 -- 1110 ...... @stim5 size=2 +sta 000011 ..... ..... .. . 1 -- 1111 ...... @stim5 size=3 stby 000011 b:5 r:5 sp:2 a:1 1 -- 1100 m:1 ..... disp=%im5_0 +stdby 000011 b:5 r:5 sp:2 a:1 1 -- 1101 m:1 ..... disp=%im5_0 @fldstwx ...... b:5 x:5 sp:2 scale:1 ....... m:1 ..... \ &ldst t=%rt64 disp=0 size=2 @@ -233,12 +311,18 @@ fstd 001011 ..... ..... .. . 1 -- 100 0 . ..... @fldstdi # Offset Mem #### -@ldstim14 ...... b:5 t:5 sp:2 .............. \ - &ldst disp=%lowsign_14 x=0 scale=0 m=0 -@ldstim14m ...... b:5 t:5 sp:2 .............. \ - &ldst disp=%lowsign_14 x=0 scale=0 m=%neg_to_m -@ldstim12m ...... b:5 t:5 sp:2 .............. \ - &ldst disp=%assemble_12a x=0 scale=0 m=%pos_to_m +@ldstim11 ...... b:5 t:5 ................ \ + &ldst sp=%assemble_sp disp=%assemble_11a \ + m=%ma2_to_m x=0 scale=0 size=3 +@ldstim14 ...... b:5 t:5 ................ \ + &ldst sp=%assemble_sp disp=%assemble_16 \ + x=0 scale=0 m=0 +@ldstim14m ...... b:5 t:5 ................ \ + &ldst sp=%assemble_sp disp=%assemble_16 \ + x=0 scale=0 m=%neg_to_m +@ldstim12m ...... b:5 t:5 ................ \ + &ldst sp=%assemble_sp disp=%assemble_12a \ + x=0 scale=0 m=%pos_to_m # LDB, LDH, LDW, LDWM ld 010000 ..... ..... .. .............. @ldstim14 size=0 @@ -254,21 +338,25 @@ st 011010 ..... ..... .. .............. @ldstim14 size=2 st 011011 ..... ..... .. .............. @ldstim14m size=2 st 011111 ..... ..... .. ...........10. @ldstim12m size=2 -fldw 010110 b:5 ..... sp:2 .............. \ - &ldst disp=%assemble_12a t=%rm64 m=%a_to_m x=0 scale=0 size=2 -fldw 010111 b:5 ..... sp:2 ...........0.. \ - &ldst disp=%assemble_12a t=%rm64 m=0 x=0 scale=0 size=2 +fldw 010110 b:5 ..... ................ \ + &ldst disp=%assemble_12a sp=%assemble_sp \ + t=%rm64 m=%a_to_m x=0 scale=0 size=2 +fldw 010111 b:5 ..... .............0.. \ + &ldst disp=%assemble_12a sp=%assemble_sp \ + t=%rm64 m=0 x=0 scale=0 size=2 -fstw 011110 b:5 ..... sp:2 .............. \ - &ldst disp=%assemble_12a t=%rm64 m=%a_to_m x=0 scale=0 size=2 -fstw 011111 b:5 ..... sp:2 ...........0.. \ - &ldst disp=%assemble_12a t=%rm64 m=0 x=0 scale=0 size=2 +fstw 011110 b:5 ..... ................ \ + &ldst disp=%assemble_12a sp=%assemble_sp \ + t=%rm64 m=%a_to_m x=0 scale=0 size=2 +fstw 011111 b:5 ..... .............0.. \ + &ldst disp=%assemble_12a sp=%assemble_sp \ + t=%rm64 m=0 x=0 scale=0 size=2 -fldd 010100 b:5 t:5 sp:2 .......... .. 1 . \ - &ldst disp=%assemble_11a m=%ma2_to_m x=0 scale=0 size=3 +ld 010100 ..... ..... .. ............0. @ldstim11 +fldd 010100 ..... ..... .. ............1. @ldstim11 -fstd 011100 b:5 t:5 sp:2 .......... .. 1 . \ - &ldst disp=%assemble_11a m=%ma2_to_m x=0 scale=0 size=3 +st 011100 ..... ..... .. ............0. @ldstim11 +fstd 011100 ..... ..... .. ............1. @ldstim11 #### # Floating-point Multiply Add @@ -286,16 +374,22 @@ fmpysub_d 100110 ..... ..... ..... ..... 1 ..... @mpyadd # Conditional Branches #### -bb_sar 110000 00000 r:5 c:1 10 ........... n:1 . disp=%assemble_12 -bb_imm 110001 p:5 r:5 c:1 10 ........... n:1 . disp=%assemble_12 +bb_sar 110000 00000 r:5 c:1 1 . ........... n:1 . \ + disp=%assemble_12 d=%d_13 +bb_imm 110001 p:5 r:5 c:1 1 . ........... n:1 . \ + disp=%assemble_12 d=%d_13 movb 110010 ..... ..... ... ........... . . @rrb_cf f=0 movbi 110011 ..... ..... ... ........... . . @rib_cf f=0 -cmpb 100000 ..... ..... ... ........... . . @rrb_cf f=0 -cmpb 100010 ..... ..... ... ........... . . @rrb_cf f=1 -cmpbi 100001 ..... ..... ... ........... . . @rib_cf f=0 -cmpbi 100011 ..... ..... ... ........... . . @rib_cf f=1 +cmpb 100000 ..... ..... ... ........... . . @rrb_cdf d=0 f=0 +cmpb 100010 ..... ..... ... ........... . . @rrb_cdf d=0 f=1 +cmpb 100111 ..... ..... ... ........... . . @rrb_cdf d=1 f=0 +cmpb 101111 ..... ..... ... ........... . . @rrb_cdf d=1 f=1 +cmpbi 100001 ..... ..... ... ........... . . @rib_cdf d=0 f=0 +cmpbi 100011 ..... ..... ... ........... . . @rib_cdf d=0 f=1 +cmpbi 111011 r:5 ..... f:1 .. ........... n:1 . \ + &rib_c_d_f d=1 disp=%assemble_12 c=%cmpbid_c i=%im5_16 addb 101000 ..... ..... ... ........... . . @rrb_cf f=0 addb 101010 ..... ..... ... ........... . . @rrb_cf f=1 @@ -306,16 +400,28 @@ addbi 101011 ..... ..... ... ........... . . @rib_cf f=1 # Shift, Extract, Deposit #### -shrpw_sar 110100 r2:5 r1:5 c:3 00 0 00000 t:5 -shrpw_imm 110100 r2:5 r1:5 c:3 01 0 cpos:5 t:5 +shrp_sar 110100 r2:5 r1:5 c:3 00 0 d:1 0000 t:5 +shrp_imm 110100 r2:5 r1:5 c:3 01 0 cpos:5 t:5 d=0 +shrp_imm 110100 r2:5 r1:5 c:3 0. 1 ..... t:5 \ + d=1 cpos=%cpos6_11 -extrw_sar 110100 r:5 t:5 c:3 10 se:1 00000 clen:5 -extrw_imm 110100 r:5 t:5 c:3 11 se:1 pos:5 clen:5 +extr_sar 110100 r:5 t:5 c:3 10 se:1 00 000 ..... d=0 len=%len5 +extr_sar 110100 r:5 t:5 c:3 10 se:1 1. 000 ..... d=1 len=%len6_8 +extr_imm 110100 r:5 t:5 c:3 11 se:1 pos:5 ..... d=0 len=%len5 +extr_imm 110110 r:5 t:5 c:3 .. se:1 ..... ..... \ + d=1 len=%len6_12 pos=%cpos6_11 -depw_sar 110101 t:5 r:5 c:3 00 nz:1 00000 clen:5 -depw_imm 110101 t:5 r:5 c:3 01 nz:1 cpos:5 clen:5 -depwi_sar 110101 t:5 ..... c:3 10 nz:1 00000 clen:5 i=%im5_16 -depwi_imm 110101 t:5 ..... c:3 11 nz:1 cpos:5 clen:5 i=%im5_16 +dep_sar 110101 t:5 r:5 c:3 00 nz:1 00 000 ..... d=0 len=%len5 +dep_sar 110101 t:5 r:5 c:3 00 nz:1 1. 000 ..... d=1 len=%len6_8 +dep_imm 110101 t:5 r:5 c:3 01 nz:1 cpos:5 ..... d=0 len=%len5 +dep_imm 111100 t:5 r:5 c:3 .. nz:1 ..... ..... \ + d=1 len=%len6_12 cpos=%cpos6_11 +depi_sar 110101 t:5 ..... c:3 10 nz:1 d:1 . 000 ..... \ + i=%im5_16 len=%len6_8 +depi_imm 110101 t:5 ..... c:3 11 nz:1 cpos:5 ..... \ + d=0 i=%im5_16 len=%len5 +depi_imm 111101 t:5 ..... c:3 .. nz:1 ..... ..... \ + d=1 i=%im5_16 len=%len6_12 cpos=%cpos6_11 #### # Branch External @@ -343,6 +449,8 @@ bl 111010 ..... ..... 101 ........... n:1 . &BL l=2 \ disp=%assemble_22 b_gate 111010 ..... ..... 001 ........... . . @bl blr 111010 l:5 x:5 010 00000000000 n:1 0 +nopbts 111010 00000 00000 010 0---------1 0 1 # clrbts/popbts +nopbts 111010 00000 ----- 010 00000000000 0 1 # pushbts/pushnom bv 111010 b:5 x:5 110 00000000000 n:1 0 bve 111010 b:5 00000 110 10000000000 n:1 - l=0 bve 111010 b:5 00000 111 10000000000 n:1 - l=2 @@ -384,14 +492,11 @@ fmpyfadd_d 101110 rm1:5 rm2:5 ... 0 1 ..0 0 0 neg:1 t:5 ra3=%rc32 @f0e_f_3 ...... ..... ..... ... .0 110 ..0 ..... \ &fclass3 r1=%ra64 r2=%rb64 t=%rt64 -@f0e_d_3 ...... r1:5 r2:5 ... 01 110 000 t:5 +@f0e_d_3 ...... r1:5 r2:5 ... 01 110 000 t:5 &fclass3 # Floating point class 0 -# FID. With r = t = 0, which via fcpy puts 0 into fr0. -# This is machine/revision = 0, which is reserved for simulator. -fcpy_f 001100 00000 00000 00000 000000 00000 \ - &fclass01 r=0 t=0 +fid_f 001100 00000 00000 000 00 000000 00000 fcpy_f 001100 ..... ..... 010 00 ...... ..... @f0c_0 fabs_f 001100 ..... ..... 011 00 ...... ..... @f0c_0 @@ -531,4 +636,18 @@ fdiv_d 001110 ..... ..... 011 ..... ... ..... @f0e_d_3 xmpyu 001110 ..... ..... 010 .0111 .00 t:5 r1=%ra64 r2=%rb64 # diag -diag 000101 ----- ----- ---- ---- ---- ---- +{ + [ + diag_btlb 000101 00 0000 0000 0000 0001 0000 0000 + diag_cout 000101 00 0000 0000 0000 0001 0000 0001 + + # For 32-bit PA-7300LC (PCX-L2) + diag_getshadowregs_pa1 000101 00 0000 0000 0001 1010 0000 0000 + diag_putshadowregs_pa1 000101 00 0000 0000 0001 1010 0100 0000 + + # For 64-bit PA8700 (PCX-W2) + diag_getshadowregs_pa2 000101 00 0111 1000 0001 1000 0100 0000 + diag_putshadowregs_pa2 000101 00 0111 0000 0001 1000 0100 0000 + ] + diag_unimp 000101 i:26 +} diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index f599dccfff..a667ee380d 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -25,11 +25,10 @@ #include "hw/core/cpu.h" #include "hw/hppa/hppa_hardware.h" -#ifndef CONFIG_USER_ONLY static void eval_interrupt(HPPACPU *cpu) { CPUState *cs = CPU(cpu); - if (cpu->env.cr[CR_EIRR] & cpu->env.cr[CR_EIEM]) { + if (cpu->env.cr[CR_EIRR]) { cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); @@ -38,7 +37,7 @@ static void eval_interrupt(HPPACPU *cpu) /* Each CPU has a word mapped into the GSC bus. Anything on the GSC bus * can write to this word to raise an external interrupt on the target CPU. - * This includes the system controler (DINO) for regular devices, or + * This includes the system controller (DINO) for regular devices, or * another CPU for SMP interprocessor interrupts. */ static uint64_t io_eir_read(void *opaque, hwaddr addr, unsigned size) @@ -53,9 +52,17 @@ static void io_eir_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { HPPACPU *cpu = opaque; - int le_bit = ~data & (TARGET_REGISTER_BITS - 1); + CPUHPPAState *env = &cpu->env; + int widthm1 = 31; + int le_bit; - cpu->env.cr[CR_EIRR] |= (target_ureg)1 << le_bit; + /* The default PSW.W controls the width of EIRR. */ + if (hppa_is_pa20(env) && env->cr[CR_PSW_DEFAULT] & PDC_PSW_WIDE_BIT) { + widthm1 = 63; + } + le_bit = ~data & widthm1; + + env->cr[CR_EIRR] |= 1ull << le_bit; eval_interrupt(cpu); } @@ -74,20 +81,12 @@ void hppa_cpu_alarm_timer(void *opaque) io_eir_write(opaque, 0, 0, 4); } -void HELPER(write_eirr)(CPUHPPAState *env, target_ureg val) +void HELPER(write_eirr)(CPUHPPAState *env, target_ulong val) { env->cr[CR_EIRR] &= ~val; - qemu_mutex_lock_iothread(); + bql_lock(); eval_interrupt(env_archcpu(env)); - qemu_mutex_unlock_iothread(); -} - -void HELPER(write_eiem)(CPUHPPAState *env, target_ureg val) -{ - env->cr[CR_EIEM] = val; - qemu_mutex_lock_iothread(); - eval_interrupt(env_archcpu(env)); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void hppa_cpu_do_interrupt(CPUState *cs) @@ -95,25 +94,39 @@ void hppa_cpu_do_interrupt(CPUState *cs) HPPACPU *cpu = HPPA_CPU(cs); CPUHPPAState *env = &cpu->env; int i = cs->exception_index; - target_ureg iaoq_f = env->iaoq_f; - target_ureg iaoq_b = env->iaoq_b; - uint64_t iasq_f = env->iasq_f; - uint64_t iasq_b = env->iasq_b; - - target_ureg old_psw; + uint64_t old_psw; /* As documented in pa2.0 -- interruption handling. */ /* step 1 */ env->cr[CR_IPSW] = old_psw = cpu_hppa_get_psw(env); - /* step 2 -- note PSW_W == 0 for !HPPA64. */ - cpu_hppa_put_psw(env, PSW_W | (i == EXCP_HPMC ? PSW_M : 0)); + /* step 2 -- Note PSW_W is masked out again for pa1.x */ + cpu_hppa_put_psw(env, + (env->cr[CR_PSW_DEFAULT] & PDC_PSW_WIDE_BIT ? PSW_W : 0) | + (i == EXCP_HPMC ? PSW_M : 0)); /* step 3 */ - env->cr[CR_IIASQ] = iasq_f >> 32; - env->cr_back[0] = iasq_b >> 32; - env->cr[CR_IIAOQ] = iaoq_f; - env->cr_back[1] = iaoq_b; + /* + * IIASQ is the top bits of the virtual address, or zero if translation + * is disabled -- with PSW_W == 0, this will reduce to the space. + */ + if (old_psw & PSW_C) { + env->cr[CR_IIASQ] = + hppa_form_gva_psw(old_psw, env->iasq_f, env->iaoq_f) >> 32; + env->cr_back[0] = + hppa_form_gva_psw(old_psw, env->iasq_b, env->iaoq_b) >> 32; + } else { + env->cr[CR_IIASQ] = 0; + env->cr_back[0] = 0; + } + /* IIAOQ is the full offset for wide mode, or 32 bits for narrow mode. */ + if (old_psw & PSW_W) { + env->cr[CR_IIAOQ] = env->iaoq_f; + env->cr_back[1] = env->iaoq_b; + } else { + env->cr[CR_IIAOQ] = (uint32_t)env->iaoq_f; + env->cr_back[1] = (uint32_t)env->iaoq_b; + } if (old_psw & PSW_Q) { /* step 5 */ @@ -146,16 +159,15 @@ void hppa_cpu_do_interrupt(CPUState *cs) /* ??? An alternate fool-proof method would be to store the instruction data into the unwind info. That's probably a bit too much in the way of extra storage required. */ - vaddr vaddr; - hwaddr paddr; + vaddr vaddr = env->iaoq_f & -4; + hwaddr paddr = vaddr; - paddr = vaddr = iaoq_f & -4; if (old_psw & PSW_C) { int prot, t; - vaddr = hppa_form_gva_psw(old_psw, iasq_f, vaddr); + vaddr = hppa_form_gva_psw(old_psw, env->iasq_f, vaddr); t = hppa_get_physical_address(env, vaddr, MMU_KERNEL_IDX, - 0, &paddr, &prot); + 0, &paddr, &prot, NULL); if (t >= 0) { /* We can't re-load the instruction. */ env->cr[CR_IIR] = 0; @@ -183,14 +195,14 @@ void hppa_cpu_do_interrupt(CPUState *cs) /* step 7 */ if (i == EXCP_TOC) { - env->iaoq_f = FIRMWARE_START; + env->iaoq_f = hppa_form_gva(env, 0, FIRMWARE_START); /* help SeaBIOS and provide iaoq_b and iasq_back in shadow regs */ env->gr[24] = env->cr_back[0]; env->gr[25] = env->cr_back[1]; } else { - env->iaoq_f = env->cr[CR_IVA] + 32 * i; + env->iaoq_f = hppa_form_gva(env, 0, env->cr[CR_IVA] + 32 * i); } - env->iaoq_b = env->iaoq_f + 4; + env->iaoq_b = hppa_form_gva(env, 0, env->iaoq_f + 4); env->iasq_f = 0; env->iasq_b = 0; @@ -240,14 +252,10 @@ void hppa_cpu_do_interrupt(CPUState *cs) snprintf(unknown, sizeof(unknown), "unknown %d", i); name = unknown; } - qemu_log("INT %6d: %s @ " TARGET_FMT_lx "," TARGET_FMT_lx - " -> " TREG_FMT_lx " " TARGET_FMT_lx "\n", - ++count, name, - hppa_form_gva(env, iasq_f, iaoq_f), - hppa_form_gva(env, iasq_b, iaoq_b), - env->iaoq_f, - hppa_form_gva(env, (uint64_t)env->cr[CR_ISR] << 32, - env->cr[CR_IOR])); + qemu_log("INT %6d: %s @ " TARGET_FMT_lx ":" TARGET_FMT_lx + " for " TARGET_FMT_lx ":" TARGET_FMT_lx "\n", + ++count, name, env->cr[CR_IIASQ], env->cr[CR_IIAOQ], + env->cr[CR_ISR], env->cr[CR_IOR]); } cs->exception_index = -1; } @@ -266,12 +274,12 @@ bool hppa_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } /* If interrupts are requested and enabled, raise them. */ - if ((env->psw & PSW_I) && (interrupt_request & CPU_INTERRUPT_HARD)) { + if ((interrupt_request & CPU_INTERRUPT_HARD) + && (env->psw & PSW_I) + && (env->cr[CR_EIRR] & env->cr[CR_EIEM])) { cs->exception_index = EXCP_EXT_INTERRUPT; hppa_cpu_do_interrupt(cs); return true; } return false; } - -#endif /* !CONFIG_USER_ONLY */ diff --git a/target/hppa/machine.c b/target/hppa/machine.c index 905991d7f9..211bfcf640 100644 --- a/target/hppa/machine.c +++ b/target/hppa/machine.c @@ -21,33 +21,12 @@ #include "cpu.h" #include "migration/cpu.h" -#if TARGET_REGISTER_BITS == 64 -#define qemu_put_betr qemu_put_be64 -#define qemu_get_betr qemu_get_be64 -#define VMSTATE_UINTTL_V(_f, _s, _v) \ - VMSTATE_UINT64_V(_f, _s, _v) -#define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \ - VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v) -#else -#define qemu_put_betr qemu_put_be32 -#define qemu_get_betr qemu_get_be32 -#define VMSTATE_UINTTR_V(_f, _s, _v) \ - VMSTATE_UINT32_V(_f, _s, _v) -#define VMSTATE_UINTTR_ARRAY_V(_f, _s, _n, _v) \ - VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v) -#endif - -#define VMSTATE_UINTTR(_f, _s) \ - VMSTATE_UINTTR_V(_f, _s, 0) -#define VMSTATE_UINTTR_ARRAY(_f, _s, _n) \ - VMSTATE_UINTTR_ARRAY_V(_f, _s, _n, 0) - static int get_psw(QEMUFile *f, void *opaque, size_t size, const VMStateField *field) { CPUHPPAState *env = opaque; - cpu_hppa_put_psw(env, qemu_get_betr(f)); + cpu_hppa_put_psw(env, qemu_get_be64(f)); return 0; } @@ -55,7 +34,7 @@ static int put_psw(QEMUFile *f, void *opaque, size_t size, const VMStateField *field, JSONWriter *vmdesc) { CPUHPPAState *env = opaque; - qemu_put_betr(f, cpu_hppa_get_psw(env)); + qemu_put_be64(f, cpu_hppa_get_psw(env)); return 0; } @@ -65,70 +44,138 @@ static const VMStateInfo vmstate_psw = { .put = put_psw, }; -/* FIXME: Use the PA2.0 format, which is a superset of the PA1.1 format. */ static int get_tlb(QEMUFile *f, void *opaque, size_t size, const VMStateField *field) { - hppa_tlb_entry *ent = opaque; - uint32_t val; + HPPATLBEntry *ent = opaque; + uint64_t val; - memset(ent, 0, sizeof(*ent)); + ent->itree.start = qemu_get_be64(f); + ent->itree.last = qemu_get_be64(f); + ent->pa = qemu_get_be64(f); + val = qemu_get_be64(f); - ent->va_b = qemu_get_be64(f); - ent->pa = qemu_get_betr(f); - val = qemu_get_be32(f); - - ent->entry_valid = extract32(val, 0, 1); - ent->access_id = extract32(val, 1, 18); - ent->u = extract32(val, 19, 1); - ent->ar_pl2 = extract32(val, 20, 2); - ent->ar_pl1 = extract32(val, 22, 2); - ent->ar_type = extract32(val, 24, 3); - ent->b = extract32(val, 27, 1); - ent->d = extract32(val, 28, 1); - ent->t = extract32(val, 29, 1); - - ent->va_e = ent->va_b + TARGET_PAGE_SIZE - 1; + if (val) { + ent->t = extract64(val, 61, 1); + ent->d = extract64(val, 60, 1); + ent->b = extract64(val, 59, 1); + ent->ar_type = extract64(val, 56, 3); + ent->ar_pl1 = extract64(val, 54, 2); + ent->ar_pl2 = extract64(val, 52, 2); + ent->u = extract64(val, 51, 1); + /* o = bit 50 */ + /* p = bit 49 */ + ent->access_id = extract64(val, 1, 31); + ent->entry_valid = 1; + } return 0; } static int put_tlb(QEMUFile *f, void *opaque, size_t size, const VMStateField *field, JSONWriter *vmdesc) { - hppa_tlb_entry *ent = opaque; - uint32_t val = 0; + HPPATLBEntry *ent = opaque; + uint64_t val = 0; if (ent->entry_valid) { val = 1; - val = deposit32(val, 1, 18, ent->access_id); - val = deposit32(val, 19, 1, ent->u); - val = deposit32(val, 20, 2, ent->ar_pl2); - val = deposit32(val, 22, 2, ent->ar_pl1); - val = deposit32(val, 24, 3, ent->ar_type); - val = deposit32(val, 27, 1, ent->b); - val = deposit32(val, 28, 1, ent->d); - val = deposit32(val, 29, 1, ent->t); + val = deposit64(val, 61, 1, ent->t); + val = deposit64(val, 60, 1, ent->d); + val = deposit64(val, 59, 1, ent->b); + val = deposit64(val, 56, 3, ent->ar_type); + val = deposit64(val, 54, 2, ent->ar_pl1); + val = deposit64(val, 52, 2, ent->ar_pl2); + val = deposit64(val, 51, 1, ent->u); + /* o = bit 50 */ + /* p = bit 49 */ + val = deposit64(val, 1, 31, ent->access_id); } - qemu_put_be64(f, ent->va_b); - qemu_put_betr(f, ent->pa); - qemu_put_be32(f, val); + qemu_put_be64(f, ent->itree.start); + qemu_put_be64(f, ent->itree.last); + qemu_put_be64(f, ent->pa); + qemu_put_be64(f, val); return 0; } -static const VMStateInfo vmstate_tlb = { +static const VMStateInfo vmstate_tlb_entry = { .name = "tlb entry", .get = get_tlb, .put = put_tlb, }; -static VMStateField vmstate_env_fields[] = { - VMSTATE_UINTTR_ARRAY(gr, CPUHPPAState, 32), +static int tlb_pre_load(void *opaque) +{ + CPUHPPAState *env = opaque; + + /* + * Zap the entire tlb, on-the-side data structures and all. + * Each tlb entry will have data re-filled by put_tlb. + */ + memset(env->tlb, 0, sizeof(env->tlb)); + memset(&env->tlb_root, 0, sizeof(env->tlb_root)); + env->tlb_unused = NULL; + env->tlb_partial = NULL; + + return 0; +} + +static int tlb_post_load(void *opaque, int version_id) +{ + CPUHPPAState *env = opaque; + uint32_t btlb_entries = HPPA_BTLB_ENTRIES(env); + HPPATLBEntry **unused = &env->tlb_unused; + HPPATLBEntry *partial = NULL; + + /* + * Re-create the interval tree from the valid entries. + * Truly invalid entries should have start == end == 0. + * Otherwise it should be the in-flight tlb_partial entry. + */ + for (uint32_t i = 0; i < ARRAY_SIZE(env->tlb); ++i) { + HPPATLBEntry *e = &env->tlb[i]; + + if (e->entry_valid) { + interval_tree_insert(&e->itree, &env->tlb_root); + } else if (i < btlb_entries) { + /* btlb not in unused list */ + } else if (partial == NULL && e->itree.start < e->itree.last) { + partial = e; + } else { + *unused = e; + unused = &e->unused_next; + } + } + env->tlb_partial = partial; + *unused = NULL; + + return 0; +} + +static const VMStateField vmstate_tlb_fields[] = { + VMSTATE_ARRAY(tlb, CPUHPPAState, + ARRAY_SIZE(((CPUHPPAState *)0)->tlb), + 0, vmstate_tlb_entry, HPPATLBEntry), + VMSTATE_UINT32(tlb_last, CPUHPPAState), + VMSTATE_END_OF_LIST() +}; + +static const VMStateDescription vmstate_tlb = { + .name = "env/tlb", + .version_id = 1, + .minimum_version_id = 1, + .fields = vmstate_tlb_fields, + .pre_load = tlb_pre_load, + .post_load = tlb_post_load, +}; + +static const VMStateField vmstate_env_fields[] = { + VMSTATE_UINT64_ARRAY(gr, CPUHPPAState, 32), VMSTATE_UINT64_ARRAY(fr, CPUHPPAState, 32), VMSTATE_UINT64_ARRAY(sr, CPUHPPAState, 8), - VMSTATE_UINTTR_ARRAY(cr, CPUHPPAState, 32), - VMSTATE_UINTTR_ARRAY(cr_back, CPUHPPAState, 2), - VMSTATE_UINTTR_ARRAY(shadow, CPUHPPAState, 7), + VMSTATE_UINT64_ARRAY(cr, CPUHPPAState, 32), + VMSTATE_UINT64_ARRAY(cr_back, CPUHPPAState, 2), + VMSTATE_UINT64_ARRAY(shadow, CPUHPPAState, 7), /* Save the architecture value of the psw, not the internally expanded version. Since this architecture value does not @@ -145,28 +192,29 @@ static VMStateField vmstate_env_fields[] = { .offset = 0 }, - VMSTATE_UINTTR(iaoq_f, CPUHPPAState), - VMSTATE_UINTTR(iaoq_b, CPUHPPAState), + VMSTATE_UINT64(iaoq_f, CPUHPPAState), + VMSTATE_UINT64(iaoq_b, CPUHPPAState), VMSTATE_UINT64(iasq_f, CPUHPPAState), VMSTATE_UINT64(iasq_b, CPUHPPAState), VMSTATE_UINT32(fr0_shadow, CPUHPPAState), - - VMSTATE_ARRAY(tlb, CPUHPPAState, ARRAY_SIZE(((CPUHPPAState *)0)->tlb), - 0, vmstate_tlb, hppa_tlb_entry), - VMSTATE_UINT32(tlb_last, CPUHPPAState), - VMSTATE_END_OF_LIST() }; +static const VMStateDescription * const vmstate_env_subsections[] = { + &vmstate_tlb, + NULL +}; + static const VMStateDescription vmstate_env = { .name = "env", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 3, + .minimum_version_id = 3, .fields = vmstate_env_fields, + .subsections = vmstate_env_subsections, }; -static VMStateField vmstate_cpu_fields[] = { +static const VMStateField vmstate_cpu_fields[] = { VMSTATE_CPU(), VMSTATE_STRUCT(env, HPPACPU, 1, vmstate_env, CPUHPPAState), VMSTATE_END_OF_LIST() diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 5046cc8f9d..84785b5a5c 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -25,82 +25,230 @@ #include "hw/core/cpu.h" #include "trace.h" -static hppa_tlb_entry *hppa_find_tlb(CPUHPPAState *env, vaddr addr) +hwaddr hppa_abs_to_phys_pa2_w1(vaddr addr) { - int i; + /* + * Figure H-8 "62-bit Absolute Accesses when PSW W-bit is 1" describes + * an algorithm in which a 62-bit absolute address is transformed to + * a 64-bit physical address. This must then be combined with that + * pictured in Figure H-11 "Physical Address Space Mapping", in which + * the full physical address is truncated to the N-bit physical address + * supported by the implementation. + * + * Since the supported physical address space is below 54 bits, the + * H-8 algorithm is moot and all that is left is to truncate. + */ + QEMU_BUILD_BUG_ON(TARGET_PHYS_ADDR_SPACE_BITS > 54); + return sextract64(addr, 0, TARGET_PHYS_ADDR_SPACE_BITS); +} - for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) { - hppa_tlb_entry *ent = &env->tlb[i]; - if (ent->va_b <= addr && addr <= ent->va_e) { - trace_hppa_tlb_find_entry(env, ent + i, ent->entry_valid, - ent->va_b, ent->va_e, ent->pa); - return ent; - } +hwaddr hppa_abs_to_phys_pa2_w0(vaddr addr) +{ + /* + * See Figure H-10, "Absolute Accesses when PSW W-bit is 0", + * combined with Figure H-11, as above. + */ + if (likely(extract32(addr, 28, 4) != 0xf)) { + /* Memory address space */ + addr = (uint32_t)addr; + } else if (extract32(addr, 24, 4) != 0) { + /* I/O address space */ + addr = (int32_t)addr; + } else { + /* + * PDC address space: + * Figures H-10 and H-11 of the parisc2.0 spec do not specify + * where to map into the 64-bit PDC address space. + * We map with an offset which equals the 32-bit address, which + * is what can be seen on physical machines too. + */ + addr = (uint32_t)addr; + addr |= -1ull << (TARGET_PHYS_ADDR_SPACE_BITS - 4); + } + return addr; +} + +static HPPATLBEntry *hppa_find_tlb(CPUHPPAState *env, vaddr addr) +{ + IntervalTreeNode *i = interval_tree_iter_first(&env->tlb_root, addr, addr); + + if (i) { + HPPATLBEntry *ent = container_of(i, HPPATLBEntry, itree); + trace_hppa_tlb_find_entry(env, ent, ent->entry_valid, + ent->itree.start, ent->itree.last, ent->pa); + return ent; } trace_hppa_tlb_find_entry_not_found(env, addr); return NULL; } -static void hppa_flush_tlb_ent(CPUHPPAState *env, hppa_tlb_entry *ent) +static void hppa_flush_tlb_ent(CPUHPPAState *env, HPPATLBEntry *ent, + bool force_flush_btlb) { CPUState *cs = env_cpu(env); - unsigned i, n = 1 << (2 * ent->page_size); - uint64_t addr = ent->va_b; + bool is_btlb; - trace_hppa_tlb_flush_ent(env, ent, ent->va_b, ent->va_e, ent->pa); - - for (i = 0; i < n; ++i, addr += TARGET_PAGE_SIZE) { - /* Do not flush MMU_PHYS_IDX. */ - tlb_flush_page_by_mmuidx(cs, addr, 0xf); + if (!ent->entry_valid) { + return; } + trace_hppa_tlb_flush_ent(env, ent, ent->itree.start, + ent->itree.last, ent->pa); + + tlb_flush_range_by_mmuidx(cs, ent->itree.start, + ent->itree.last - ent->itree.start + 1, + HPPA_MMU_FLUSH_MASK, TARGET_LONG_BITS); + + /* Never clear BTLBs, unless forced to do so. */ + is_btlb = ent < &env->tlb[HPPA_BTLB_ENTRIES(env)]; + if (is_btlb && !force_flush_btlb) { + return; + } + + interval_tree_remove(&ent->itree, &env->tlb_root); memset(ent, 0, sizeof(*ent)); - ent->va_b = -1; + + if (!is_btlb) { + ent->unused_next = env->tlb_unused; + env->tlb_unused = ent; + } } -static hppa_tlb_entry *hppa_alloc_tlb_ent(CPUHPPAState *env) +static void hppa_flush_tlb_range(CPUHPPAState *env, vaddr va_b, vaddr va_e) { - hppa_tlb_entry *ent; - uint32_t i = env->tlb_last; + IntervalTreeNode *i, *n; - env->tlb_last = (i == ARRAY_SIZE(env->tlb) - 1 ? 0 : i + 1); - ent = &env->tlb[i]; + i = interval_tree_iter_first(&env->tlb_root, va_b, va_e); + for (; i ; i = n) { + HPPATLBEntry *ent = container_of(i, HPPATLBEntry, itree); - hppa_flush_tlb_ent(env, ent); + /* + * Find the next entry now: In the normal case the current entry + * will be removed, but in the BTLB case it will remain. + */ + n = interval_tree_iter_next(i, va_b, va_e); + hppa_flush_tlb_ent(env, ent, false); + } +} + +static HPPATLBEntry *hppa_alloc_tlb_ent(CPUHPPAState *env) +{ + HPPATLBEntry *ent = env->tlb_unused; + + if (ent == NULL) { + uint32_t btlb_entries = HPPA_BTLB_ENTRIES(env); + uint32_t i = env->tlb_last; + + if (i < btlb_entries || i >= ARRAY_SIZE(env->tlb)) { + i = btlb_entries; + } + env->tlb_last = i + 1; + + ent = &env->tlb[i]; + hppa_flush_tlb_ent(env, ent, false); + } + + env->tlb_unused = ent->unused_next; return ent; } +#define ACCESS_ID_MASK 0xffff + +/* Return the set of protections allowed by a PID match. */ +static int match_prot_id_1(uint32_t access_id, uint32_t prot_id) +{ + if (((access_id ^ (prot_id >> 1)) & ACCESS_ID_MASK) == 0) { + return (prot_id & 1 + ? PAGE_EXEC | PAGE_READ + : PAGE_EXEC | PAGE_READ | PAGE_WRITE); + } + return 0; +} + +static int match_prot_id32(CPUHPPAState *env, uint32_t access_id) +{ + int r, i; + + for (i = CR_PID1; i <= CR_PID4; ++i) { + r = match_prot_id_1(access_id, env->cr[i]); + if (r) { + return r; + } + } + return 0; +} + +static int match_prot_id64(CPUHPPAState *env, uint32_t access_id) +{ + int r, i; + + for (i = CR_PID1; i <= CR_PID4; ++i) { + r = match_prot_id_1(access_id, env->cr[i]); + if (r) { + return r; + } + r = match_prot_id_1(access_id, env->cr[i] >> 32); + if (r) { + return r; + } + } + return 0; +} + int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, - int type, hwaddr *pphys, int *pprot) + int type, hwaddr *pphys, int *pprot, + HPPATLBEntry **tlb_entry) { hwaddr phys; - int prot, r_prot, w_prot, x_prot; - hppa_tlb_entry *ent; + int prot, r_prot, w_prot, x_prot, priv; + HPPATLBEntry *ent; int ret = -1; - /* Virtual translation disabled. Direct map virtual to physical. */ - if (mmu_idx == MMU_PHYS_IDX) { - phys = addr; + if (tlb_entry) { + *tlb_entry = NULL; + } + + /* Virtual translation disabled. Map absolute to physical. */ + if (MMU_IDX_MMU_DISABLED(mmu_idx)) { + switch (mmu_idx) { + case MMU_ABS_W_IDX: + phys = hppa_abs_to_phys_pa2_w1(addr); + break; + case MMU_ABS_IDX: + if (hppa_is_pa20(env)) { + phys = hppa_abs_to_phys_pa2_w0(addr); + } else { + phys = (uint32_t)addr; + } + break; + default: + g_assert_not_reached(); + } prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; goto egress; } /* Find a valid tlb entry that matches the virtual address. */ ent = hppa_find_tlb(env, addr); - if (ent == NULL || !ent->entry_valid) { + if (ent == NULL) { phys = 0; prot = 0; ret = (type == PAGE_EXEC) ? EXCP_ITLB_MISS : EXCP_DTLB_MISS; goto egress; } + if (tlb_entry) { + *tlb_entry = ent; + } + /* We now know the physical address. */ - phys = ent->pa + (addr & ~TARGET_PAGE_MASK); + phys = ent->pa + (addr - ent->itree.start); /* Map TLB access_rights field to QEMU protection. */ - r_prot = (mmu_idx <= ent->ar_pl1) * PAGE_READ; - w_prot = (mmu_idx <= ent->ar_pl2) * PAGE_WRITE; - x_prot = (ent->ar_pl2 <= mmu_idx && mmu_idx <= ent->ar_pl1) * PAGE_EXEC; + priv = MMU_IDX_TO_PRIV(mmu_idx); + r_prot = (priv <= ent->ar_pl1) * PAGE_READ; + w_prot = (priv <= ent->ar_pl2) * PAGE_WRITE; + x_prot = (ent->ar_pl2 <= priv && priv <= ent->ar_pl1) * PAGE_EXEC; switch (ent->ar_type) { case 0: /* read-only: data page */ prot = r_prot; @@ -119,29 +267,30 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, break; } - /* access_id == 0 means public page and no check is performed */ - if ((env->psw & PSW_P) && ent->access_id) { - /* If bits [31:1] match, and bit 0 is set, suppress write. */ - int match = ent->access_id * 2 + 1; - - if (match == env->cr[CR_PID1] || match == env->cr[CR_PID2] || - match == env->cr[CR_PID3] || match == env->cr[CR_PID4]) { - prot &= PAGE_READ | PAGE_EXEC; - if (type == PAGE_WRITE) { - ret = EXCP_DMPI; - goto egress; - } - } - } - - /* No guest access type indicates a non-architectural access from - within QEMU. Bypass checks for access, D, B and T bits. */ + /* + * No guest access type indicates a non-architectural access from + * within QEMU. Bypass checks for access, D, B, P and T bits. + */ if (type == 0) { goto egress; } + /* access_id == 0 means public page and no check is performed */ + if (ent->access_id && MMU_IDX_TO_P(mmu_idx)) { + int access_prot = (hppa_is_pa20(env) + ? match_prot_id64(env, ent->access_id) + : match_prot_id32(env, ent->access_id)); + if (unlikely(!(type & access_prot))) { + /* Not allowed -- Inst/Data Memory Protection Id Fault. */ + ret = type & PAGE_EXEC ? EXCP_IMP : EXCP_DMPI; + goto egress; + } + /* Otherwise exclude permissions not allowed (i.e WD). */ + prot &= access_prot; + } + if (unlikely(!(prot & type))) { - /* The access isn't allowed -- Inst/Data Memory Protection Fault. */ + /* Not allowed -- Inst/Data Memory Access Rights Fault. */ ret = (type & PAGE_EXEC) ? EXCP_IMP : EXCP_DMAR; goto egress; } @@ -183,17 +332,16 @@ hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { HPPACPU *cpu = HPPA_CPU(cs); hwaddr phys; - int prot, excp; + int prot, excp, mmu_idx; /* If the (data) mmu is disabled, bypass translation. */ /* ??? We really ought to know if the code mmu is disabled too, in order to get the correct debugging dumps. */ - if (!(cpu->env.psw & PSW_D)) { - return addr; - } + mmu_idx = (cpu->env.psw & PSW_D ? MMU_KERNEL_IDX : + cpu->env.psw & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX); - excp = hppa_get_physical_address(&cpu->env, addr, MMU_KERNEL_IDX, 0, - &phys, &prot); + excp = hppa_get_physical_address(&cpu->env, addr, mmu_idx, 0, + &phys, &prot, NULL); /* Since we're translating for debugging, the only error that is a hard error is no translation at all. Otherwise, while a real cpu @@ -201,12 +349,81 @@ hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) return excp == EXCP_DTLB_MISS ? -1 : phys; } +void hppa_set_ior_and_isr(CPUHPPAState *env, vaddr addr, bool mmu_disabled) +{ + if (env->psw & PSW_Q) { + /* + * For pa1.x, the offset and space never overlap, and so we + * simply extract the high and low part of the virtual address. + * + * For pa2.0, the formation of these are described in section + * "Interruption Parameter Registers", page 2-15. + */ + env->cr[CR_IOR] = (uint32_t)addr; + env->cr[CR_ISR] = addr >> 32; + + if (hppa_is_pa20(env)) { + if (mmu_disabled) { + /* + * If data translation was disabled, the ISR contains + * the upper portion of the abs address, zero-extended. + */ + env->cr[CR_ISR] &= 0x3fffffff; + } else { + /* + * If data translation was enabled, the upper two bits + * of the IOR (the b field) are equal to the two space + * bits from the base register used to form the gva. + */ + uint64_t b; + + b = env->unwind_breg ? env->gr[env->unwind_breg] : 0; + b >>= (env->psw & PSW_W ? 62 : 30); + env->cr[CR_IOR] |= b << 62; + } + } + } +} + +G_NORETURN static void +raise_exception_with_ior(CPUHPPAState *env, int excp, uintptr_t retaddr, + vaddr addr, bool mmu_disabled) +{ + CPUState *cs = env_cpu(env); + + cs->exception_index = excp; + cpu_restore_state(cs, retaddr); + hppa_set_ior_and_isr(env, addr, mmu_disabled); + + cpu_loop_exit(cs); +} + +void hppa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, + vaddr addr, unsigned size, + MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, uintptr_t retaddr) +{ + CPUHPPAState *env = cpu_env(cs); + + qemu_log_mask(LOG_GUEST_ERROR, "HPMC at " TARGET_FMT_lx ":" TARGET_FMT_lx + " while accessing I/O at %#08" HWADDR_PRIx "\n", + env->iasq_f, env->iaoq_f, physaddr); + + /* FIXME: Enable HPMC exceptions when firmware has clean device probing */ + if (0) { + raise_exception_with_ior(env, EXCP_HPMC, retaddr, addr, + MMU_IDX_MMU_DISABLED(mmu_idx)); + } +} + bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, MMUAccessType type, int mmu_idx, bool probe, uintptr_t retaddr) { HPPACPU *cpu = HPPA_CPU(cs); CPUHPPAState *env = &cpu->env; + HPPATLBEntry *ent; int prot, excp, a_prot; hwaddr phys; @@ -223,71 +440,58 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, } excp = hppa_get_physical_address(env, addr, mmu_idx, - a_prot, &phys, &prot); + a_prot, &phys, &prot, &ent); if (unlikely(excp >= 0)) { if (probe) { return false; } trace_hppa_tlb_fill_excp(env, addr, size, type, mmu_idx); + /* Failure. Raise the indicated exception. */ - cs->exception_index = excp; - if (cpu->env.psw & PSW_Q) { - /* ??? Needs tweaking for hppa64. */ - cpu->env.cr[CR_IOR] = addr; - cpu->env.cr[CR_ISR] = addr >> 32; - } - cpu_loop_exit_restore(cs, retaddr); + raise_exception_with_ior(env, excp, retaddr, addr, + MMU_IDX_MMU_DISABLED(mmu_idx)); } trace_hppa_tlb_fill_success(env, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK, size, type, mmu_idx); - /* Success! Store the translation into the QEMU TLB. */ + + /* + * Success! Store the translation into the QEMU TLB. + * Note that we always install a single-page entry, because that + * is what works best with softmmu -- anything else will trigger + * the large page protection mask. We do not require this, + * because we record the large page here in the hppa tlb. + */ tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK, prot, mmu_idx, TARGET_PAGE_SIZE); return true; } /* Insert (Insn/Data) TLB Address. Note this is PA 1.1 only. */ -void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg) +void HELPER(itlba_pa11)(CPUHPPAState *env, target_ulong addr, target_ulong reg) { - hppa_tlb_entry *empty = NULL; - int i; + HPPATLBEntry *ent; - /* Zap any old entries covering ADDR; notice empty entries on the way. */ - for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) { - hppa_tlb_entry *ent = &env->tlb[i]; - if (ent->va_b <= addr && addr <= ent->va_e) { - if (ent->entry_valid) { - hppa_flush_tlb_ent(env, ent); - } - if (!empty) { - empty = ent; - } - } + /* Zap any old entries covering ADDR. */ + addr &= TARGET_PAGE_MASK; + hppa_flush_tlb_range(env, addr, addr + TARGET_PAGE_SIZE - 1); + + ent = env->tlb_partial; + if (ent == NULL) { + ent = hppa_alloc_tlb_ent(env); + env->tlb_partial = ent; } - /* If we didn't see an empty entry, evict one. */ - if (empty == NULL) { - empty = hppa_alloc_tlb_ent(env); - } - - /* Note that empty->entry_valid == 0 already. */ - empty->va_b = addr & TARGET_PAGE_MASK; - empty->va_e = empty->va_b + TARGET_PAGE_SIZE - 1; - empty->pa = extract32(reg, 5, 20) << TARGET_PAGE_BITS; - trace_hppa_tlb_itlba(env, empty, empty->va_b, empty->va_e, empty->pa); + /* Note that ent->entry_valid == 0 already. */ + ent->itree.start = addr; + ent->itree.last = addr + TARGET_PAGE_SIZE - 1; + ent->pa = extract32(reg, 5, 20) << TARGET_PAGE_BITS; + trace_hppa_tlb_itlba(env, ent, ent->itree.start, ent->itree.last, ent->pa); } -/* Insert (Insn/Data) TLB Protection. Note this is PA 1.1 only. */ -void HELPER(itlbp)(CPUHPPAState *env, target_ulong addr, target_ureg reg) +static void set_access_bits_pa11(CPUHPPAState *env, HPPATLBEntry *ent, + target_ulong reg) { - hppa_tlb_entry *ent = hppa_find_tlb(env, addr); - - if (unlikely(ent == NULL)) { - qemu_log_mask(LOG_GUEST_ERROR, "ITLBP not following ITLBA\n"); - return; - } - ent->access_id = extract32(reg, 1, 18); ent->u = extract32(reg, 19, 1); ent->ar_pl2 = extract32(reg, 20, 2); @@ -297,36 +501,159 @@ void HELPER(itlbp)(CPUHPPAState *env, target_ulong addr, target_ureg reg) ent->d = extract32(reg, 28, 1); ent->t = extract32(reg, 29, 1); ent->entry_valid = 1; + + interval_tree_insert(&ent->itree, &env->tlb_root); trace_hppa_tlb_itlbp(env, ent, ent->access_id, ent->u, ent->ar_pl2, ent->ar_pl1, ent->ar_type, ent->b, ent->d, ent->t); } -/* Purge (Insn/Data) TLB. This is explicitly page-based, and is - synchronous across all processors. */ -static void ptlb_work(CPUState *cpu, run_on_cpu_data data) +/* Insert (Insn/Data) TLB Protection. Note this is PA 1.1 only. */ +void HELPER(itlbp_pa11)(CPUHPPAState *env, target_ulong addr, target_ulong reg) { - CPUHPPAState *env = cpu->env_ptr; - target_ulong addr = (target_ulong) data.target_ptr; - hppa_tlb_entry *ent = hppa_find_tlb(env, addr); + HPPATLBEntry *ent = env->tlb_partial; - if (ent && ent->entry_valid) { - hppa_flush_tlb_ent(env, ent); + if (ent) { + env->tlb_partial = NULL; + if (ent->itree.start <= addr && addr <= ent->itree.last) { + set_access_bits_pa11(env, ent, reg); + return; + } } + qemu_log_mask(LOG_GUEST_ERROR, "ITLBP not following ITLBA\n"); } +static void itlbt_pa20(CPUHPPAState *env, target_ulong r1, + target_ulong r2, vaddr va_b) +{ + HPPATLBEntry *ent; + vaddr va_e; + uint64_t va_size; + int mask_shift; + + mask_shift = 2 * (r1 & 0xf); + va_size = (uint64_t)TARGET_PAGE_SIZE << mask_shift; + va_b &= -va_size; + va_e = va_b + va_size - 1; + + hppa_flush_tlb_range(env, va_b, va_e); + ent = hppa_alloc_tlb_ent(env); + + ent->itree.start = va_b; + ent->itree.last = va_e; + + /* Extract all 52 bits present in the page table entry. */ + ent->pa = r1 << (TARGET_PAGE_BITS - 5); + /* Align per the page size. */ + ent->pa &= TARGET_PAGE_MASK << mask_shift; + /* Ignore the bits beyond physical address space. */ + ent->pa = sextract64(ent->pa, 0, TARGET_PHYS_ADDR_SPACE_BITS); + + ent->t = extract64(r2, 61, 1); + ent->d = extract64(r2, 60, 1); + ent->b = extract64(r2, 59, 1); + ent->ar_type = extract64(r2, 56, 3); + ent->ar_pl1 = extract64(r2, 54, 2); + ent->ar_pl2 = extract64(r2, 52, 2); + ent->u = extract64(r2, 51, 1); + /* o = bit 50 */ + /* p = bit 49 */ + ent->access_id = extract64(r2, 1, 31); + ent->entry_valid = 1; + + interval_tree_insert(&ent->itree, &env->tlb_root); + trace_hppa_tlb_itlba(env, ent, ent->itree.start, ent->itree.last, ent->pa); + trace_hppa_tlb_itlbp(env, ent, ent->access_id, ent->u, + ent->ar_pl2, ent->ar_pl1, ent->ar_type, + ent->b, ent->d, ent->t); +} + +void HELPER(idtlbt_pa20)(CPUHPPAState *env, target_ulong r1, target_ulong r2) +{ + vaddr va_b = deposit64(env->cr[CR_IOR], 32, 32, env->cr[CR_ISR]); + itlbt_pa20(env, r1, r2, va_b); +} + +void HELPER(iitlbt_pa20)(CPUHPPAState *env, target_ulong r1, target_ulong r2) +{ + vaddr va_b = deposit64(env->cr[CR_IIAOQ], 32, 32, env->cr[CR_IIASQ]); + itlbt_pa20(env, r1, r2, va_b); +} + +/* Purge (Insn/Data) TLB. */ +static void ptlb_work(CPUState *cpu, run_on_cpu_data data) +{ + vaddr start = data.target_ptr; + vaddr end; + + /* + * PA2.0 allows a range of pages encoded into GR[b], which we have + * copied into the bottom bits of the otherwise page-aligned address. + * PA1.x will always provide zero here, for a single page flush. + */ + end = start & 0xf; + start &= TARGET_PAGE_MASK; + end = (vaddr)TARGET_PAGE_SIZE << (2 * end); + end = start + end - 1; + + hppa_flush_tlb_range(cpu_env(cpu), start, end); +} + +/* This is local to the current cpu. */ +void HELPER(ptlb_l)(CPUHPPAState *env, target_ulong addr) +{ + trace_hppa_tlb_ptlb_local(env); + ptlb_work(env_cpu(env), RUN_ON_CPU_TARGET_PTR(addr)); +} + +/* This is synchronous across all processors. */ void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr) { CPUState *src = env_cpu(env); CPUState *cpu; + bool wait = false; + trace_hppa_tlb_ptlb(env); run_on_cpu_data data = RUN_ON_CPU_TARGET_PTR(addr); CPU_FOREACH(cpu) { if (cpu != src) { async_run_on_cpu(cpu, ptlb_work, data); + wait = true; } } - async_safe_run_on_cpu(src, ptlb_work, data); + if (wait) { + async_safe_run_on_cpu(src, ptlb_work, data); + } else { + ptlb_work(src, data); + } +} + +void hppa_ptlbe(CPUHPPAState *env) +{ + uint32_t btlb_entries = HPPA_BTLB_ENTRIES(env); + uint32_t i; + + /* Zap the (non-btlb) tlb entries themselves. */ + memset(&env->tlb[btlb_entries], 0, + sizeof(env->tlb) - btlb_entries * sizeof(env->tlb[0])); + env->tlb_last = btlb_entries; + env->tlb_partial = NULL; + + /* Put them all onto the unused list. */ + env->tlb_unused = &env->tlb[btlb_entries]; + for (i = btlb_entries; i < ARRAY_SIZE(env->tlb) - 1; ++i) { + env->tlb[i].unused_next = &env->tlb[i + 1]; + } + + /* Re-initialize the interval tree with only the btlb entries. */ + memset(&env->tlb_root, 0, sizeof(env->tlb_root)); + for (i = 0; i < btlb_entries; ++i) { + if (env->tlb[i].entry_valid) { + interval_tree_insert(&env->tlb[i].itree, &env->tlb_root); + } + } + + tlb_flush_by_mmuidx(env_cpu(env), HPPA_MMU_FLUSH_MASK); } /* Purge (Insn/Data) TLB entry. This affects an implementation-defined @@ -334,15 +661,13 @@ void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr) void HELPER(ptlbe)(CPUHPPAState *env) { trace_hppa_tlb_ptlbe(env); - memset(env->tlb, 0, sizeof(env->tlb)); - tlb_flush_by_mmuidx(env_cpu(env), 0xf); + qemu_log_mask(CPU_LOG_MMU, "FLUSH ALL TLB ENTRIES\n"); + hppa_ptlbe(env); } void cpu_hppa_change_prot_id(CPUHPPAState *env) { - if (env->psw & PSW_P) { - tlb_flush_by_mmuidx(env_cpu(env), 0xf); - } + tlb_flush_by_mmuidx(env_cpu(env), HPPA_MMU_FLUSH_P_MASK); } void HELPER(change_prot_id)(CPUHPPAState *env) @@ -350,24 +675,19 @@ void HELPER(change_prot_id)(CPUHPPAState *env) cpu_hppa_change_prot_id(env); } -target_ureg HELPER(lpa)(CPUHPPAState *env, target_ulong addr) +target_ulong HELPER(lpa)(CPUHPPAState *env, target_ulong addr) { hwaddr phys; int prot, excp; excp = hppa_get_physical_address(env, addr, MMU_KERNEL_IDX, 0, - &phys, &prot); + &phys, &prot, NULL); if (excp >= 0) { - if (env->psw & PSW_Q) { - /* ??? Needs tweaking for hppa64. */ - env->cr[CR_IOR] = addr; - env->cr[CR_ISR] = addr >> 32; - } if (excp == EXCP_DTLB_MISS) { excp = EXCP_NA_DTLB_MISS; } trace_hppa_tlb_lpa_failed(env, addr); - hppa_dynamic_excp(env, excp, GETPC()); + raise_exception_with_ior(env, excp, GETPC(), addr, false); } trace_hppa_tlb_lpa_success(env, addr, phys); return phys; @@ -376,6 +696,102 @@ target_ureg HELPER(lpa)(CPUHPPAState *env, target_ulong addr) /* Return the ar_type of the TLB at VADDR, or -1. */ int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr) { - hppa_tlb_entry *ent = hppa_find_tlb(env, vaddr); + HPPATLBEntry *ent = hppa_find_tlb(env, vaddr); return ent ? ent->ar_type : -1; } + +/* + * diag_btlb() emulates the PDC PDC_BLOCK_TLB firmware call to + * allow operating systems to modify the Block TLB (BTLB) entries. + * For implementation details see page 1-13 in + * https://parisc.wiki.kernel.org/images-parisc/e/ef/Pdc11-v0.96-Ch1-procs.pdf + */ +void HELPER(diag_btlb)(CPUHPPAState *env) +{ + unsigned int phys_page, len, slot; + int mmu_idx = cpu_mmu_index(env_cpu(env), 0); + uintptr_t ra = GETPC(); + HPPATLBEntry *btlb; + uint64_t virt_page; + uint32_t *vaddr; + uint32_t btlb_entries = HPPA_BTLB_ENTRIES(env); + + /* BTLBs are not supported on 64-bit CPUs */ + if (btlb_entries == 0) { + env->gr[28] = -1; /* nonexistent procedure */ + return; + } + + env->gr[28] = 0; /* PDC_OK */ + + switch (env->gr[25]) { + case 0: + /* return BTLB parameters */ + qemu_log_mask(CPU_LOG_MMU, "PDC_BLOCK_TLB: PDC_BTLB_INFO\n"); + vaddr = probe_access(env, env->gr[24], 4 * sizeof(uint32_t), + MMU_DATA_STORE, mmu_idx, ra); + if (vaddr == NULL) { + env->gr[28] = -10; /* invalid argument */ + } else { + vaddr[0] = cpu_to_be32(1); + vaddr[1] = cpu_to_be32(16 * 1024); + vaddr[2] = cpu_to_be32(PA10_BTLB_FIXED); + vaddr[3] = cpu_to_be32(PA10_BTLB_VARIABLE); + } + break; + case 1: + /* insert BTLB entry */ + virt_page = env->gr[24]; /* upper 32 bits */ + virt_page <<= 32; + virt_page |= env->gr[23]; /* lower 32 bits */ + phys_page = env->gr[22]; + len = env->gr[21]; + slot = env->gr[19]; + qemu_log_mask(CPU_LOG_MMU, "PDC_BLOCK_TLB: PDC_BTLB_INSERT " + "0x%08llx-0x%08llx: vpage 0x%llx for phys page 0x%04x len %d " + "into slot %d\n", + (long long) virt_page << TARGET_PAGE_BITS, + (long long) (virt_page + len) << TARGET_PAGE_BITS, + (long long) virt_page, phys_page, len, slot); + if (slot < btlb_entries) { + btlb = &env->tlb[slot]; + + /* Force flush of possibly existing BTLB entry. */ + hppa_flush_tlb_ent(env, btlb, true); + + /* Create new BTLB entry */ + btlb->itree.start = virt_page << TARGET_PAGE_BITS; + btlb->itree.last = btlb->itree.start + len * TARGET_PAGE_SIZE - 1; + btlb->pa = phys_page << TARGET_PAGE_BITS; + set_access_bits_pa11(env, btlb, env->gr[20]); + btlb->t = 0; + btlb->d = 1; + } else { + env->gr[28] = -10; /* invalid argument */ + } + break; + case 2: + /* Purge BTLB entry */ + slot = env->gr[22]; + qemu_log_mask(CPU_LOG_MMU, "PDC_BLOCK_TLB: PDC_BTLB_PURGE slot %d\n", + slot); + if (slot < btlb_entries) { + btlb = &env->tlb[slot]; + hppa_flush_tlb_ent(env, btlb, true); + } else { + env->gr[28] = -10; /* invalid argument */ + } + break; + case 3: + /* Purge all BTLB entries */ + qemu_log_mask(CPU_LOG_MMU, "PDC_BLOCK_TLB: PDC_BTLB_PURGE_ALL\n"); + for (slot = 0; slot < btlb_entries; slot++) { + btlb = &env->tlb[slot]; + hppa_flush_tlb_ent(env, btlb, true); + } + break; + default: + env->gr[28] = -2; /* nonexistent option */ + break; + } +} diff --git a/target/hppa/meson.build b/target/hppa/meson.build index 021e42a2d0..f47e54f5fa 100644 --- a/target/hppa/meson.build +++ b/target/hppa/meson.build @@ -4,18 +4,20 @@ hppa_ss = ss.source_set() hppa_ss.add(gen) hppa_ss.add(files( 'cpu.c', + 'fpu_helper.c', 'gdbstub.c', 'helper.c', - 'int_helper.c', 'op_helper.c', 'translate.c', )) -hppa_softmmu_ss = ss.source_set() -hppa_softmmu_ss.add(files( +hppa_system_ss = ss.source_set() +hppa_system_ss.add(files( + 'int_helper.c', 'machine.c', 'mem_helper.c', + 'sys_helper.c', )) target_arch += {'hppa': hppa_ss} -target_softmmu_arch += {'hppa': hppa_softmmu_ss} +target_system_arch += {'hppa': hppa_system_ss} diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index cd304f051e..6cf49f33b7 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -24,8 +24,6 @@ #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" -#include "fpu/softfloat.h" #include "trace.h" G_NORETURN void HELPER(excp)(CPUHPPAState *env, int excp) @@ -44,25 +42,25 @@ G_NORETURN void hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra) cpu_loop_exit_restore(cs, ra); } -void HELPER(tsv)(CPUHPPAState *env, target_ureg cond) +void HELPER(tsv)(CPUHPPAState *env, target_ulong cond) { - if (unlikely((target_sreg)cond < 0)) { + if (unlikely((target_long)cond < 0)) { hppa_dynamic_excp(env, EXCP_OVERFLOW, GETPC()); } } -void HELPER(tcond)(CPUHPPAState *env, target_ureg cond) +void HELPER(tcond)(CPUHPPAState *env, target_ulong cond) { if (unlikely(cond)) { hppa_dynamic_excp(env, EXCP_COND, GETPC()); } } -static void atomic_store_3(CPUHPPAState *env, target_ulong addr, - uint32_t val, uintptr_t ra) +static void atomic_store_mask32(CPUHPPAState *env, target_ulong addr, + uint32_t val, uint32_t mask, uintptr_t ra) { - int mmu_idx = cpu_mmu_index(env, 0); - uint32_t old, new, cmp, mask, *haddr; + int mmu_idx = cpu_mmu_index(env_cpu(env), 0); + uint32_t old, new, cmp, *haddr; void *vaddr; vaddr = probe_access(env, addr, 3, MMU_DATA_STORE, mmu_idx, ra); @@ -83,7 +81,36 @@ static void atomic_store_3(CPUHPPAState *env, target_ulong addr, } } -static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val, +static void atomic_store_mask64(CPUHPPAState *env, target_ulong addr, + uint64_t val, uint64_t mask, + int size, uintptr_t ra) +{ +#ifdef CONFIG_ATOMIC64 + int mmu_idx = cpu_mmu_index(env_cpu(env), 0); + uint64_t old, new, cmp, *haddr; + void *vaddr; + + vaddr = probe_access(env, addr, size, MMU_DATA_STORE, mmu_idx, ra); + if (vaddr == NULL) { + cpu_loop_exit_atomic(env_cpu(env), ra); + } + haddr = (uint64_t *)((uintptr_t)vaddr & -8); + + old = *haddr; + while (1) { + new = be32_to_cpu((cpu_to_be32(old) & ~mask) | (val & mask)); + cmp = qatomic_cmpxchg__nocheck(haddr, old, new); + if (cmp == old) { + return; + } + old = cmp; + } +#else + cpu_loop_exit_atomic(env_cpu(env), ra); +#endif +} + +static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ulong val, bool parallel, uintptr_t ra) { switch (addr & 3) { @@ -96,7 +123,7 @@ static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val, case 1: /* The 3 byte store must appear atomic. */ if (parallel) { - atomic_store_3(env, addr, val, ra); + atomic_store_mask32(env, addr, val, 0x00ffffffu, ra); } else { cpu_stb_data_ra(env, addr, val >> 16, ra); cpu_stw_data_ra(env, addr + 1, val, ra); @@ -108,25 +135,92 @@ static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val, } } -void HELPER(stby_b)(CPUHPPAState *env, target_ulong addr, target_ureg val) +static void do_stdby_b(CPUHPPAState *env, target_ulong addr, uint64_t val, + bool parallel, uintptr_t ra) +{ + switch (addr & 7) { + case 7: + cpu_stb_data_ra(env, addr, val, ra); + break; + case 6: + cpu_stw_data_ra(env, addr, val, ra); + break; + case 5: + /* The 3 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask32(env, addr, val, 0x00ffffffu, ra); + } else { + cpu_stb_data_ra(env, addr, val >> 16, ra); + cpu_stw_data_ra(env, addr + 1, val, ra); + } + break; + case 4: + cpu_stl_data_ra(env, addr, val, ra); + break; + case 3: + /* The 5 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask64(env, addr, val, 0x000000ffffffffffull, 5, ra); + } else { + cpu_stb_data_ra(env, addr, val >> 32, ra); + cpu_stl_data_ra(env, addr + 1, val, ra); + } + break; + case 2: + /* The 6 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask64(env, addr, val, 0x0000ffffffffffffull, 6, ra); + } else { + cpu_stw_data_ra(env, addr, val >> 32, ra); + cpu_stl_data_ra(env, addr + 2, val, ra); + } + break; + case 1: + /* The 7 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask64(env, addr, val, 0x00ffffffffffffffull, 7, ra); + } else { + cpu_stb_data_ra(env, addr, val >> 48, ra); + cpu_stw_data_ra(env, addr + 1, val >> 32, ra); + cpu_stl_data_ra(env, addr + 3, val, ra); + } + break; + default: + cpu_stq_data_ra(env, addr, val, ra); + break; + } +} + +void HELPER(stby_b)(CPUHPPAState *env, target_ulong addr, target_ulong val) { do_stby_b(env, addr, val, false, GETPC()); } void HELPER(stby_b_parallel)(CPUHPPAState *env, target_ulong addr, - target_ureg val) + target_ulong val) { do_stby_b(env, addr, val, true, GETPC()); } -static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ureg val, +void HELPER(stdby_b)(CPUHPPAState *env, target_ulong addr, target_ulong val) +{ + do_stdby_b(env, addr, val, false, GETPC()); +} + +void HELPER(stdby_b_parallel)(CPUHPPAState *env, target_ulong addr, + target_ulong val) +{ + do_stdby_b(env, addr, val, true, GETPC()); +} + +static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ulong val, bool parallel, uintptr_t ra) { switch (addr & 3) { case 3: /* The 3 byte store must appear atomic. */ if (parallel) { - atomic_store_3(env, addr - 3, val, ra); + atomic_store_mask32(env, addr - 3, val, 0xffffff00u, ra); } else { cpu_stw_data_ra(env, addr - 3, val >> 16, ra); cpu_stb_data_ra(env, addr - 1, val >> 8, ra); @@ -141,22 +235,94 @@ static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ureg val, default: /* Nothing is stored, but protection is checked and the cacheline is marked dirty. */ - probe_write(env, addr, 0, cpu_mmu_index(env, 0), ra); + probe_write(env, addr, 0, cpu_mmu_index(env_cpu(env), 0), ra); break; } } -void HELPER(stby_e)(CPUHPPAState *env, target_ulong addr, target_ureg val) +static void do_stdby_e(CPUHPPAState *env, target_ulong addr, uint64_t val, + bool parallel, uintptr_t ra) +{ + switch (addr & 7) { + case 7: + /* The 7 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask64(env, addr - 7, val, + 0xffffffffffffff00ull, 7, ra); + } else { + cpu_stl_data_ra(env, addr - 7, val >> 32, ra); + cpu_stw_data_ra(env, addr - 3, val >> 16, ra); + cpu_stb_data_ra(env, addr - 1, val >> 8, ra); + } + break; + case 6: + /* The 6 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask64(env, addr - 6, val, + 0xffffffffffff0000ull, 6, ra); + } else { + cpu_stl_data_ra(env, addr - 6, val >> 32, ra); + cpu_stw_data_ra(env, addr - 2, val >> 16, ra); + } + break; + case 5: + /* The 5 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask64(env, addr - 5, val, + 0xffffffffff000000ull, 5, ra); + } else { + cpu_stl_data_ra(env, addr - 5, val >> 32, ra); + cpu_stb_data_ra(env, addr - 1, val >> 24, ra); + } + break; + case 4: + cpu_stl_data_ra(env, addr - 4, val >> 32, ra); + break; + case 3: + /* The 3 byte store must appear atomic. */ + if (parallel) { + atomic_store_mask32(env, addr - 3, val >> 32, 0xffffff00u, ra); + } else { + cpu_stw_data_ra(env, addr - 3, val >> 48, ra); + cpu_stb_data_ra(env, addr - 1, val >> 40, ra); + } + break; + case 2: + cpu_stw_data_ra(env, addr - 2, val >> 48, ra); + break; + case 1: + cpu_stb_data_ra(env, addr - 1, val >> 56, ra); + break; + default: + /* Nothing is stored, but protection is checked and the + cacheline is marked dirty. */ + probe_write(env, addr, 0, cpu_mmu_index(env_cpu(env), 0), ra); + break; + } +} + +void HELPER(stby_e)(CPUHPPAState *env, target_ulong addr, target_ulong val) { do_stby_e(env, addr, val, false, GETPC()); } void HELPER(stby_e_parallel)(CPUHPPAState *env, target_ulong addr, - target_ureg val) + target_ulong val) { do_stby_e(env, addr, val, true, GETPC()); } +void HELPER(stdby_e)(CPUHPPAState *env, target_ulong addr, target_ulong val) +{ + do_stdby_e(env, addr, val, false, GETPC()); +} + +void HELPER(stdby_e_parallel)(CPUHPPAState *env, target_ulong addr, + target_ulong val) +{ + do_stdby_e(env, addr, val, true, GETPC()); +} + void HELPER(ldc_check)(target_ulong addr) { if (unlikely(addr & 0xf)) { @@ -166,13 +332,13 @@ void HELPER(ldc_check)(target_ulong addr) } } -target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr, +target_ulong HELPER(probe)(CPUHPPAState *env, target_ulong addr, uint32_t level, uint32_t want) { #ifdef CONFIG_USER_ONLY return page_check_range(addr, 1, want); #else - int prot, excp; + int prot, excp, mmu_idx; hwaddr phys; trace_hppa_tlb_probe(addr, level, want); @@ -181,449 +347,22 @@ target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr, return 0; } - excp = hppa_get_physical_address(env, addr, level, 0, &phys, &prot); + mmu_idx = PRIV_P_TO_MMU_IDX(level, env->psw & PSW_P); + excp = hppa_get_physical_address(env, addr, mmu_idx, 0, &phys, + &prot, NULL); if (excp >= 0) { - if (env->psw & PSW_Q) { - /* ??? Needs tweaking for hppa64. */ - env->cr[CR_IOR] = addr; - env->cr[CR_ISR] = addr >> 32; - } + cpu_restore_state(env_cpu(env), GETPC()); + hppa_set_ior_and_isr(env, addr, MMU_IDX_MMU_DISABLED(mmu_idx)); if (excp == EXCP_DTLB_MISS) { excp = EXCP_NA_DTLB_MISS; } - hppa_dynamic_excp(env, excp, GETPC()); + helper_excp(env, excp); } return (want & prot) != 0; #endif } -void HELPER(loaded_fr0)(CPUHPPAState *env) -{ - uint32_t shadow = env->fr[0] >> 32; - int rm, d; - - env->fr0_shadow = shadow; - - switch (extract32(shadow, 9, 2)) { - default: - rm = float_round_nearest_even; - break; - case 1: - rm = float_round_to_zero; - break; - case 2: - rm = float_round_up; - break; - case 3: - rm = float_round_down; - break; - } - set_float_rounding_mode(rm, &env->fp_status); - - d = extract32(shadow, 5, 1); - set_flush_to_zero(d, &env->fp_status); - set_flush_inputs_to_zero(d, &env->fp_status); -} - -void cpu_hppa_loaded_fr0(CPUHPPAState *env) -{ - helper_loaded_fr0(env); -} - -#define CONVERT_BIT(X, SRC, DST) \ - ((SRC) > (DST) \ - ? (X) / ((SRC) / (DST)) & (DST) \ - : ((X) & (SRC)) * ((DST) / (SRC))) - -static void update_fr0_op(CPUHPPAState *env, uintptr_t ra) -{ - uint32_t soft_exp = get_float_exception_flags(&env->fp_status); - uint32_t hard_exp = 0; - uint32_t shadow = env->fr0_shadow; - - if (likely(soft_exp == 0)) { - env->fr[0] = (uint64_t)shadow << 32; - return; - } - set_float_exception_flags(0, &env->fp_status); - - hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, 1u << 0); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, 1u << 1); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, 1u << 2); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, 1u << 3); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, 1u << 4); - shadow |= hard_exp << (32 - 5); - env->fr0_shadow = shadow; - env->fr[0] = (uint64_t)shadow << 32; - - if (hard_exp & shadow) { - hppa_dynamic_excp(env, EXCP_ASSIST, ra); - } -} - -float32 HELPER(fsqrt_s)(CPUHPPAState *env, float32 arg) -{ - float32 ret = float32_sqrt(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(frnd_s)(CPUHPPAState *env, float32 arg) -{ - float32 ret = float32_round_to_int(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fadd_s)(CPUHPPAState *env, float32 a, float32 b) -{ - float32 ret = float32_add(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fsub_s)(CPUHPPAState *env, float32 a, float32 b) -{ - float32 ret = float32_sub(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fmpy_s)(CPUHPPAState *env, float32 a, float32 b) -{ - float32 ret = float32_mul(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fdiv_s)(CPUHPPAState *env, float32 a, float32 b) -{ - float32 ret = float32_div(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fsqrt_d)(CPUHPPAState *env, float64 arg) -{ - float64 ret = float64_sqrt(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(frnd_d)(CPUHPPAState *env, float64 arg) -{ - float64 ret = float64_round_to_int(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fadd_d)(CPUHPPAState *env, float64 a, float64 b) -{ - float64 ret = float64_add(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fsub_d)(CPUHPPAState *env, float64 a, float64 b) -{ - float64 ret = float64_sub(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fmpy_d)(CPUHPPAState *env, float64 a, float64 b) -{ - float64 ret = float64_mul(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fdiv_d)(CPUHPPAState *env, float64 a, float64 b) -{ - float64 ret = float64_div(a, b, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg) -{ - float64 ret = float32_to_float64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fcnv_d_s)(CPUHPPAState *env, float64 arg) -{ - float32 ret = float64_to_float32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fcnv_w_s)(CPUHPPAState *env, int32_t arg) -{ - float32 ret = int32_to_float32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fcnv_dw_s)(CPUHPPAState *env, int64_t arg) -{ - float32 ret = int64_to_float32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fcnv_w_d)(CPUHPPAState *env, int32_t arg) -{ - float64 ret = int32_to_float64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fcnv_dw_d)(CPUHPPAState *env, int64_t arg) -{ - float64 ret = int64_to_float64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int32_t HELPER(fcnv_s_w)(CPUHPPAState *env, float32 arg) -{ - int32_t ret = float32_to_int32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int32_t HELPER(fcnv_d_w)(CPUHPPAState *env, float64 arg) -{ - int32_t ret = float64_to_int32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int64_t HELPER(fcnv_s_dw)(CPUHPPAState *env, float32 arg) -{ - int64_t ret = float32_to_int64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int64_t HELPER(fcnv_d_dw)(CPUHPPAState *env, float64 arg) -{ - int64_t ret = float64_to_int64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int32_t HELPER(fcnv_t_s_w)(CPUHPPAState *env, float32 arg) -{ - int32_t ret = float32_to_int32_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int32_t HELPER(fcnv_t_d_w)(CPUHPPAState *env, float64 arg) -{ - int32_t ret = float64_to_int32_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int64_t HELPER(fcnv_t_s_dw)(CPUHPPAState *env, float32 arg) -{ - int64_t ret = float32_to_int64_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -int64_t HELPER(fcnv_t_d_dw)(CPUHPPAState *env, float64 arg) -{ - int64_t ret = float64_to_int64_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fcnv_uw_s)(CPUHPPAState *env, uint32_t arg) -{ - float32 ret = uint32_to_float32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fcnv_udw_s)(CPUHPPAState *env, uint64_t arg) -{ - float32 ret = uint64_to_float32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fcnv_uw_d)(CPUHPPAState *env, uint32_t arg) -{ - float64 ret = uint32_to_float64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fcnv_udw_d)(CPUHPPAState *env, uint64_t arg) -{ - float64 ret = uint64_to_float64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint32_t HELPER(fcnv_s_uw)(CPUHPPAState *env, float32 arg) -{ - uint32_t ret = float32_to_uint32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint32_t HELPER(fcnv_d_uw)(CPUHPPAState *env, float64 arg) -{ - uint32_t ret = float64_to_uint32(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint64_t HELPER(fcnv_s_udw)(CPUHPPAState *env, float32 arg) -{ - uint64_t ret = float32_to_uint64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint64_t HELPER(fcnv_d_udw)(CPUHPPAState *env, float64 arg) -{ - uint64_t ret = float64_to_uint64(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint32_t HELPER(fcnv_t_s_uw)(CPUHPPAState *env, float32 arg) -{ - uint32_t ret = float32_to_uint32_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint32_t HELPER(fcnv_t_d_uw)(CPUHPPAState *env, float64 arg) -{ - uint32_t ret = float64_to_uint32_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint64_t HELPER(fcnv_t_s_udw)(CPUHPPAState *env, float32 arg) -{ - uint64_t ret = float32_to_uint64_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -uint64_t HELPER(fcnv_t_d_udw)(CPUHPPAState *env, float64 arg) -{ - uint64_t ret = float64_to_uint64_round_to_zero(arg, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -static void update_fr0_cmp(CPUHPPAState *env, uint32_t y, - uint32_t c, FloatRelation r) -{ - uint32_t shadow = env->fr0_shadow; - - switch (r) { - case float_relation_greater: - c = extract32(c, 4, 1); - break; - case float_relation_less: - c = extract32(c, 3, 1); - break; - case float_relation_equal: - c = extract32(c, 2, 1); - break; - case float_relation_unordered: - c = extract32(c, 1, 1); - break; - default: - g_assert_not_reached(); - } - - if (y) { - /* targeted comparison */ - /* set fpsr[ca[y - 1]] to current compare */ - shadow = deposit32(shadow, 21 - (y - 1), 1, c); - } else { - /* queued comparison */ - /* shift cq right by one place */ - shadow = deposit32(shadow, 11, 10, extract32(shadow, 12, 10)); - /* move fpsr[c] to fpsr[cq[0]] */ - shadow = deposit32(shadow, 21, 1, extract32(shadow, 26, 1)); - /* set fpsr[c] to current compare */ - shadow = deposit32(shadow, 26, 1, c); - } - - env->fr0_shadow = shadow; - env->fr[0] = (uint64_t)shadow << 32; -} - -void HELPER(fcmp_s)(CPUHPPAState *env, float32 a, float32 b, - uint32_t y, uint32_t c) -{ - FloatRelation r; - if (c & 1) { - r = float32_compare(a, b, &env->fp_status); - } else { - r = float32_compare_quiet(a, b, &env->fp_status); - } - update_fr0_op(env, GETPC()); - update_fr0_cmp(env, y, c, r); -} - -void HELPER(fcmp_d)(CPUHPPAState *env, float64 a, float64 b, - uint32_t y, uint32_t c) -{ - FloatRelation r; - if (c & 1) { - r = float64_compare(a, b, &env->fp_status); - } else { - r = float64_compare_quiet(a, b, &env->fp_status); - } - update_fr0_op(env, GETPC()); - update_fr0_cmp(env, y, c, r); -} - -float32 HELPER(fmpyfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) -{ - float32 ret = float32_muladd(a, b, c, 0, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float32 HELPER(fmpynfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) -{ - float32 ret = float32_muladd(a, b, c, float_muladd_negate_product, - &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fmpyfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) -{ - float64 ret = float64_muladd(a, b, c, 0, &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -float64 HELPER(fmpynfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) -{ - float64 ret = float64_muladd(a, b, c, float_muladd_negate_product, - &env->fp_status); - update_fr0_op(env, GETPC()); - return ret; -} - -target_ureg HELPER(read_interval_timer)(void) +target_ulong HELPER(read_interval_timer)(void) { #ifdef CONFIG_USER_ONLY /* In user-mode, QEMU_CLOCK_VIRTUAL doesn't exist. @@ -637,78 +376,112 @@ target_ureg HELPER(read_interval_timer)(void) #endif } -#ifndef CONFIG_USER_ONLY -void HELPER(write_interval_timer)(CPUHPPAState *env, target_ureg val) +uint64_t HELPER(hadd_ss)(uint64_t r1, uint64_t r2) { - HPPACPU *cpu = env_archcpu(env); - uint64_t current = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - uint64_t timeout; + uint64_t ret = 0; - /* Even in 64-bit mode, the comparator is always 32-bit. But the - value we expose to the guest is 1/4 of the speed of the clock, - so moosh in 34 bits. */ - timeout = deposit64(current, 0, 34, (uint64_t)val << 2); + for (int i = 0; i < 64; i += 16) { + int f1 = sextract64(r1, i, 16); + int f2 = sextract64(r2, i, 16); + int fr = f1 + f2; - /* If the mooshing puts the clock in the past, advance to next round. */ - if (timeout < current + 1000) { - timeout += 1ULL << 34; + fr = MIN(fr, INT16_MAX); + fr = MAX(fr, INT16_MIN); + ret = deposit64(ret, i, 16, fr); } - - cpu->env.cr[CR_IT] = timeout; - timer_mod(cpu->alarm_timer, timeout); + return ret; } -void HELPER(halt)(CPUHPPAState *env) +uint64_t HELPER(hadd_us)(uint64_t r1, uint64_t r2) { - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); - helper_excp(env, EXCP_HLT); + uint64_t ret = 0; + + for (int i = 0; i < 64; i += 16) { + int f1 = extract64(r1, i, 16); + int f2 = sextract64(r2, i, 16); + int fr = f1 + f2; + + fr = MIN(fr, UINT16_MAX); + fr = MAX(fr, 0); + ret = deposit64(ret, i, 16, fr); + } + return ret; } -void HELPER(reset)(CPUHPPAState *env) +uint64_t HELPER(havg)(uint64_t r1, uint64_t r2) { - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - helper_excp(env, EXCP_HLT); + uint64_t ret = 0; + + for (int i = 0; i < 64; i += 16) { + int f1 = extract64(r1, i, 16); + int f2 = extract64(r2, i, 16); + int fr = f1 + f2; + + ret = deposit64(ret, i, 16, (fr >> 1) | (fr & 1)); + } + return ret; } -target_ureg HELPER(swap_system_mask)(CPUHPPAState *env, target_ureg nsm) +uint64_t HELPER(hsub_ss)(uint64_t r1, uint64_t r2) { - target_ulong psw = env->psw; - /* - * Setting the PSW Q bit to 1, if it was not already 1, is an - * undefined operation. - * - * However, HP-UX 10.20 does this with the SSM instruction. - * Tested this on HP9000/712 and HP9000/785/C3750 and both - * machines set the Q bit from 0 to 1 without an exception, - * so let this go without comment. - */ - env->psw = (psw & ~PSW_SM) | (nsm & PSW_SM); - return psw & PSW_SM; + uint64_t ret = 0; + + for (int i = 0; i < 64; i += 16) { + int f1 = sextract64(r1, i, 16); + int f2 = sextract64(r2, i, 16); + int fr = f1 - f2; + + fr = MIN(fr, INT16_MAX); + fr = MAX(fr, INT16_MIN); + ret = deposit64(ret, i, 16, fr); + } + return ret; } -void HELPER(rfi)(CPUHPPAState *env) +uint64_t HELPER(hsub_us)(uint64_t r1, uint64_t r2) { - env->iasq_f = (uint64_t)env->cr[CR_IIASQ] << 32; - env->iasq_b = (uint64_t)env->cr_back[0] << 32; - env->iaoq_f = env->cr[CR_IIAOQ]; - env->iaoq_b = env->cr_back[1]; - cpu_hppa_put_psw(env, env->cr[CR_IPSW]); + uint64_t ret = 0; + + for (int i = 0; i < 64; i += 16) { + int f1 = extract64(r1, i, 16); + int f2 = sextract64(r2, i, 16); + int fr = f1 - f2; + + fr = MIN(fr, UINT16_MAX); + fr = MAX(fr, 0); + ret = deposit64(ret, i, 16, fr); + } + return ret; } -void HELPER(getshadowregs)(CPUHPPAState *env) +uint64_t HELPER(hshladd)(uint64_t r1, uint64_t r2, uint32_t sh) { - env->gr[1] = env->shadow[0]; - env->gr[8] = env->shadow[1]; - env->gr[9] = env->shadow[2]; - env->gr[16] = env->shadow[3]; - env->gr[17] = env->shadow[4]; - env->gr[24] = env->shadow[5]; - env->gr[25] = env->shadow[6]; + uint64_t ret = 0; + + for (int i = 0; i < 64; i += 16) { + int f1 = sextract64(r1, i, 16); + int f2 = sextract64(r2, i, 16); + int fr = (f1 << sh) + f2; + + fr = MIN(fr, INT16_MAX); + fr = MAX(fr, INT16_MIN); + ret = deposit64(ret, i, 16, fr); + } + return ret; } -void HELPER(rfi_r)(CPUHPPAState *env) +uint64_t HELPER(hshradd)(uint64_t r1, uint64_t r2, uint32_t sh) { - helper_getshadowregs(env); - helper_rfi(env); + uint64_t ret = 0; + + for (int i = 0; i < 64; i += 16) { + int f1 = sextract64(r1, i, 16); + int f2 = sextract64(r2, i, 16); + int fr = (f1 >> sh) + f2; + + fr = MIN(fr, INT16_MAX); + fr = MAX(fr, INT16_MIN); + ret = deposit64(ret, i, 16, fr); + } + return ret; } -#endif diff --git a/target/hppa/sys_helper.c b/target/hppa/sys_helper.c new file mode 100644 index 0000000000..22d6c89964 --- /dev/null +++ b/target/hppa/sys_helper.c @@ -0,0 +1,147 @@ +/* + * Helpers for HPPA system instructions. + * + * Copyright (c) 2016 Richard Henderson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "qemu/timer.h" +#include "sysemu/runstate.h" +#include "sysemu/sysemu.h" +#include "chardev/char-fe.h" + +void HELPER(write_interval_timer)(CPUHPPAState *env, target_ulong val) +{ + HPPACPU *cpu = env_archcpu(env); + uint64_t current = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint64_t timeout; + + /* + * Even in 64-bit mode, the comparator is always 32-bit. But the + * value we expose to the guest is 1/4 of the speed of the clock, + * so moosh in 34 bits. + */ + timeout = deposit64(current, 0, 34, (uint64_t)val << 2); + + /* If the mooshing puts the clock in the past, advance to next round. */ + if (timeout < current + 1000) { + timeout += 1ULL << 34; + } + + cpu->env.cr[CR_IT] = timeout; + timer_mod(cpu->alarm_timer, timeout); +} + +void HELPER(halt)(CPUHPPAState *env) +{ + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + helper_excp(env, EXCP_HLT); +} + +void HELPER(reset)(CPUHPPAState *env) +{ + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + helper_excp(env, EXCP_HLT); +} + +target_ulong HELPER(swap_system_mask)(CPUHPPAState *env, target_ulong nsm) +{ + target_ulong psw = env->psw; + /* + * Setting the PSW Q bit to 1, if it was not already 1, is an + * undefined operation. + * + * However, HP-UX 10.20 does this with the SSM instruction. + * Tested this on HP9000/712 and HP9000/785/C3750 and both + * machines set the Q bit from 0 to 1 without an exception, + * so let this go without comment. + */ + env->psw = (psw & ~PSW_SM) | (nsm & PSW_SM); + return psw & PSW_SM; +} + +void HELPER(rfi)(CPUHPPAState *env) +{ + uint64_t mask; + + cpu_hppa_put_psw(env, env->cr[CR_IPSW]); + + /* + * For pa2.0, IIASQ is the top bits of the virtual address. + * To recreate the space identifier, remove the offset bits. + * For pa1.x, the mask reduces to no change to space. + */ + mask = gva_offset_mask(env->psw); + + env->iaoq_f = env->cr[CR_IIAOQ]; + env->iaoq_b = env->cr_back[1]; + env->iasq_f = (env->cr[CR_IIASQ] << 32) & ~(env->iaoq_f & mask); + env->iasq_b = (env->cr_back[0] << 32) & ~(env->iaoq_b & mask); +} + +static void getshadowregs(CPUHPPAState *env) +{ + env->gr[1] = env->shadow[0]; + env->gr[8] = env->shadow[1]; + env->gr[9] = env->shadow[2]; + env->gr[16] = env->shadow[3]; + env->gr[17] = env->shadow[4]; + env->gr[24] = env->shadow[5]; + env->gr[25] = env->shadow[6]; +} + +void HELPER(rfi_r)(CPUHPPAState *env) +{ + getshadowregs(env); + helper_rfi(env); +} + +#ifndef CONFIG_USER_ONLY +/* + * diag_console_output() is a helper function used during the initial bootup + * process of the SeaBIOS-hppa firmware. During the bootup phase, addresses of + * serial ports on e.g. PCI busses are unknown and most other devices haven't + * been initialized and configured yet. With help of a simple "diag" assembler + * instruction and an ASCII character code in register %r26 firmware can easily + * print debug output without any dependencies to the first serial port and use + * that as serial console. + */ +void HELPER(diag_console_output)(CPUHPPAState *env) +{ + CharBackend *serial_backend; + Chardev *serial_port; + unsigned char c; + + /* find first serial port */ + serial_port = serial_hd(0); + if (!serial_port) { + return; + } + + /* get serial_backend for the serial port */ + serial_backend = serial_port->be; + if (!serial_backend || + !qemu_chr_fe_backend_connected(serial_backend)) { + return; + } + + c = (unsigned char)env->gr[26]; + qemu_chr_fe_write(serial_backend, &c, sizeof(c)); +} +#endif diff --git a/target/hppa/trace-events b/target/hppa/trace-events index 8931517890..a10ba73d5d 100644 --- a/target/hppa/trace-events +++ b/target/hppa/trace-events @@ -10,6 +10,7 @@ disable hppa_tlb_fill_success(void *env, uint64_t addr, uint64_t phys, int size, disable hppa_tlb_itlba(void *env, void *ent, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p va_b=0x%lx va_e=0x%lx pa=0x%lx" disable hppa_tlb_itlbp(void *env, void *ent, int access_id, int u, int pl2, int pl1, int type, int b, int d, int t) "env=%p ent=%p access_id=%x u=%d pl2=%d pl1=%d type=%d b=%d d=%d t=%d" disable hppa_tlb_ptlb(void *env) "env=%p" +disable hppa_tlb_ptlb_local(void *env) "env=%p" disable hppa_tlb_ptlbe(void *env) "env=%p" disable hppa_tlb_lpa_success(void *env, uint64_t addr, uint64_t phys) "env=%p addr=0x%lx phys=0x%lx" disable hppa_tlb_lpa_failed(void *env, uint64_t addr) "env=%p addr=0x%lx" diff --git a/target/hppa/translate.c b/target/hppa/translate.c index b8dbfee5e9..42fa480950 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -23,257 +23,45 @@ #include "qemu/host-utils.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" -#include "exec/cpu_ldst.h" +#include "tcg/tcg-op-gvec.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" #include "exec/log.h" -/* Since we have a distinction between register size and address size, - we need to redefine all of these. */ +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H -#undef TCGv +/* Choose to use explicit sizes within this file. */ #undef tcg_temp_new -#undef tcg_global_mem_new -#undef tcg_temp_local_new -#undef tcg_temp_free - -#if TARGET_LONG_BITS == 64 -#define TCGv_tl TCGv_i64 -#define tcg_temp_new_tl tcg_temp_new_i64 -#define tcg_temp_free_tl tcg_temp_free_i64 -#if TARGET_REGISTER_BITS == 64 -#define tcg_gen_extu_reg_tl tcg_gen_mov_i64 -#else -#define tcg_gen_extu_reg_tl tcg_gen_extu_i32_i64 -#endif -#else -#define TCGv_tl TCGv_i32 -#define tcg_temp_new_tl tcg_temp_new_i32 -#define tcg_temp_free_tl tcg_temp_free_i32 -#define tcg_gen_extu_reg_tl tcg_gen_mov_i32 -#endif - -#if TARGET_REGISTER_BITS == 64 -#define TCGv_reg TCGv_i64 - -#define tcg_temp_new tcg_temp_new_i64 -#define tcg_global_mem_new tcg_global_mem_new_i64 -#define tcg_temp_local_new tcg_temp_local_new_i64 -#define tcg_temp_free tcg_temp_free_i64 - -#define tcg_gen_movi_reg tcg_gen_movi_i64 -#define tcg_gen_mov_reg tcg_gen_mov_i64 -#define tcg_gen_ld8u_reg tcg_gen_ld8u_i64 -#define tcg_gen_ld8s_reg tcg_gen_ld8s_i64 -#define tcg_gen_ld16u_reg tcg_gen_ld16u_i64 -#define tcg_gen_ld16s_reg tcg_gen_ld16s_i64 -#define tcg_gen_ld32u_reg tcg_gen_ld32u_i64 -#define tcg_gen_ld32s_reg tcg_gen_ld32s_i64 -#define tcg_gen_ld_reg tcg_gen_ld_i64 -#define tcg_gen_st8_reg tcg_gen_st8_i64 -#define tcg_gen_st16_reg tcg_gen_st16_i64 -#define tcg_gen_st32_reg tcg_gen_st32_i64 -#define tcg_gen_st_reg tcg_gen_st_i64 -#define tcg_gen_add_reg tcg_gen_add_i64 -#define tcg_gen_addi_reg tcg_gen_addi_i64 -#define tcg_gen_sub_reg tcg_gen_sub_i64 -#define tcg_gen_neg_reg tcg_gen_neg_i64 -#define tcg_gen_subfi_reg tcg_gen_subfi_i64 -#define tcg_gen_subi_reg tcg_gen_subi_i64 -#define tcg_gen_and_reg tcg_gen_and_i64 -#define tcg_gen_andi_reg tcg_gen_andi_i64 -#define tcg_gen_or_reg tcg_gen_or_i64 -#define tcg_gen_ori_reg tcg_gen_ori_i64 -#define tcg_gen_xor_reg tcg_gen_xor_i64 -#define tcg_gen_xori_reg tcg_gen_xori_i64 -#define tcg_gen_not_reg tcg_gen_not_i64 -#define tcg_gen_shl_reg tcg_gen_shl_i64 -#define tcg_gen_shli_reg tcg_gen_shli_i64 -#define tcg_gen_shr_reg tcg_gen_shr_i64 -#define tcg_gen_shri_reg tcg_gen_shri_i64 -#define tcg_gen_sar_reg tcg_gen_sar_i64 -#define tcg_gen_sari_reg tcg_gen_sari_i64 -#define tcg_gen_brcond_reg tcg_gen_brcond_i64 -#define tcg_gen_brcondi_reg tcg_gen_brcondi_i64 -#define tcg_gen_setcond_reg tcg_gen_setcond_i64 -#define tcg_gen_setcondi_reg tcg_gen_setcondi_i64 -#define tcg_gen_mul_reg tcg_gen_mul_i64 -#define tcg_gen_muli_reg tcg_gen_muli_i64 -#define tcg_gen_div_reg tcg_gen_div_i64 -#define tcg_gen_rem_reg tcg_gen_rem_i64 -#define tcg_gen_divu_reg tcg_gen_divu_i64 -#define tcg_gen_remu_reg tcg_gen_remu_i64 -#define tcg_gen_discard_reg tcg_gen_discard_i64 -#define tcg_gen_trunc_reg_i32 tcg_gen_extrl_i64_i32 -#define tcg_gen_trunc_i64_reg tcg_gen_mov_i64 -#define tcg_gen_extu_i32_reg tcg_gen_extu_i32_i64 -#define tcg_gen_ext_i32_reg tcg_gen_ext_i32_i64 -#define tcg_gen_extu_reg_i64 tcg_gen_mov_i64 -#define tcg_gen_ext_reg_i64 tcg_gen_mov_i64 -#define tcg_gen_ext8u_reg tcg_gen_ext8u_i64 -#define tcg_gen_ext8s_reg tcg_gen_ext8s_i64 -#define tcg_gen_ext16u_reg tcg_gen_ext16u_i64 -#define tcg_gen_ext16s_reg tcg_gen_ext16s_i64 -#define tcg_gen_ext32u_reg tcg_gen_ext32u_i64 -#define tcg_gen_ext32s_reg tcg_gen_ext32s_i64 -#define tcg_gen_bswap16_reg tcg_gen_bswap16_i64 -#define tcg_gen_bswap32_reg tcg_gen_bswap32_i64 -#define tcg_gen_bswap64_reg tcg_gen_bswap64_i64 -#define tcg_gen_concat_reg_i64 tcg_gen_concat32_i64 -#define tcg_gen_andc_reg tcg_gen_andc_i64 -#define tcg_gen_eqv_reg tcg_gen_eqv_i64 -#define tcg_gen_nand_reg tcg_gen_nand_i64 -#define tcg_gen_nor_reg tcg_gen_nor_i64 -#define tcg_gen_orc_reg tcg_gen_orc_i64 -#define tcg_gen_clz_reg tcg_gen_clz_i64 -#define tcg_gen_ctz_reg tcg_gen_ctz_i64 -#define tcg_gen_clzi_reg tcg_gen_clzi_i64 -#define tcg_gen_ctzi_reg tcg_gen_ctzi_i64 -#define tcg_gen_clrsb_reg tcg_gen_clrsb_i64 -#define tcg_gen_ctpop_reg tcg_gen_ctpop_i64 -#define tcg_gen_rotl_reg tcg_gen_rotl_i64 -#define tcg_gen_rotli_reg tcg_gen_rotli_i64 -#define tcg_gen_rotr_reg tcg_gen_rotr_i64 -#define tcg_gen_rotri_reg tcg_gen_rotri_i64 -#define tcg_gen_deposit_reg tcg_gen_deposit_i64 -#define tcg_gen_deposit_z_reg tcg_gen_deposit_z_i64 -#define tcg_gen_extract_reg tcg_gen_extract_i64 -#define tcg_gen_sextract_reg tcg_gen_sextract_i64 -#define tcg_gen_extract2_reg tcg_gen_extract2_i64 -#define tcg_const_reg tcg_const_i64 -#define tcg_const_local_reg tcg_const_local_i64 -#define tcg_constant_reg tcg_constant_i64 -#define tcg_gen_movcond_reg tcg_gen_movcond_i64 -#define tcg_gen_add2_reg tcg_gen_add2_i64 -#define tcg_gen_sub2_reg tcg_gen_sub2_i64 -#define tcg_gen_qemu_ld_reg tcg_gen_qemu_ld_i64 -#define tcg_gen_qemu_st_reg tcg_gen_qemu_st_i64 -#define tcg_gen_atomic_xchg_reg tcg_gen_atomic_xchg_i64 -#define tcg_gen_trunc_reg_ptr tcg_gen_trunc_i64_ptr -#else -#define TCGv_reg TCGv_i32 -#define tcg_temp_new tcg_temp_new_i32 -#define tcg_global_mem_new tcg_global_mem_new_i32 -#define tcg_temp_local_new tcg_temp_local_new_i32 -#define tcg_temp_free tcg_temp_free_i32 - -#define tcg_gen_movi_reg tcg_gen_movi_i32 -#define tcg_gen_mov_reg tcg_gen_mov_i32 -#define tcg_gen_ld8u_reg tcg_gen_ld8u_i32 -#define tcg_gen_ld8s_reg tcg_gen_ld8s_i32 -#define tcg_gen_ld16u_reg tcg_gen_ld16u_i32 -#define tcg_gen_ld16s_reg tcg_gen_ld16s_i32 -#define tcg_gen_ld32u_reg tcg_gen_ld_i32 -#define tcg_gen_ld32s_reg tcg_gen_ld_i32 -#define tcg_gen_ld_reg tcg_gen_ld_i32 -#define tcg_gen_st8_reg tcg_gen_st8_i32 -#define tcg_gen_st16_reg tcg_gen_st16_i32 -#define tcg_gen_st32_reg tcg_gen_st32_i32 -#define tcg_gen_st_reg tcg_gen_st_i32 -#define tcg_gen_add_reg tcg_gen_add_i32 -#define tcg_gen_addi_reg tcg_gen_addi_i32 -#define tcg_gen_sub_reg tcg_gen_sub_i32 -#define tcg_gen_neg_reg tcg_gen_neg_i32 -#define tcg_gen_subfi_reg tcg_gen_subfi_i32 -#define tcg_gen_subi_reg tcg_gen_subi_i32 -#define tcg_gen_and_reg tcg_gen_and_i32 -#define tcg_gen_andi_reg tcg_gen_andi_i32 -#define tcg_gen_or_reg tcg_gen_or_i32 -#define tcg_gen_ori_reg tcg_gen_ori_i32 -#define tcg_gen_xor_reg tcg_gen_xor_i32 -#define tcg_gen_xori_reg tcg_gen_xori_i32 -#define tcg_gen_not_reg tcg_gen_not_i32 -#define tcg_gen_shl_reg tcg_gen_shl_i32 -#define tcg_gen_shli_reg tcg_gen_shli_i32 -#define tcg_gen_shr_reg tcg_gen_shr_i32 -#define tcg_gen_shri_reg tcg_gen_shri_i32 -#define tcg_gen_sar_reg tcg_gen_sar_i32 -#define tcg_gen_sari_reg tcg_gen_sari_i32 -#define tcg_gen_brcond_reg tcg_gen_brcond_i32 -#define tcg_gen_brcondi_reg tcg_gen_brcondi_i32 -#define tcg_gen_setcond_reg tcg_gen_setcond_i32 -#define tcg_gen_setcondi_reg tcg_gen_setcondi_i32 -#define tcg_gen_mul_reg tcg_gen_mul_i32 -#define tcg_gen_muli_reg tcg_gen_muli_i32 -#define tcg_gen_div_reg tcg_gen_div_i32 -#define tcg_gen_rem_reg tcg_gen_rem_i32 -#define tcg_gen_divu_reg tcg_gen_divu_i32 -#define tcg_gen_remu_reg tcg_gen_remu_i32 -#define tcg_gen_discard_reg tcg_gen_discard_i32 -#define tcg_gen_trunc_reg_i32 tcg_gen_mov_i32 -#define tcg_gen_trunc_i64_reg tcg_gen_extrl_i64_i32 -#define tcg_gen_extu_i32_reg tcg_gen_mov_i32 -#define tcg_gen_ext_i32_reg tcg_gen_mov_i32 -#define tcg_gen_extu_reg_i64 tcg_gen_extu_i32_i64 -#define tcg_gen_ext_reg_i64 tcg_gen_ext_i32_i64 -#define tcg_gen_ext8u_reg tcg_gen_ext8u_i32 -#define tcg_gen_ext8s_reg tcg_gen_ext8s_i32 -#define tcg_gen_ext16u_reg tcg_gen_ext16u_i32 -#define tcg_gen_ext16s_reg tcg_gen_ext16s_i32 -#define tcg_gen_ext32u_reg tcg_gen_mov_i32 -#define tcg_gen_ext32s_reg tcg_gen_mov_i32 -#define tcg_gen_bswap16_reg tcg_gen_bswap16_i32 -#define tcg_gen_bswap32_reg tcg_gen_bswap32_i32 -#define tcg_gen_concat_reg_i64 tcg_gen_concat_i32_i64 -#define tcg_gen_andc_reg tcg_gen_andc_i32 -#define tcg_gen_eqv_reg tcg_gen_eqv_i32 -#define tcg_gen_nand_reg tcg_gen_nand_i32 -#define tcg_gen_nor_reg tcg_gen_nor_i32 -#define tcg_gen_orc_reg tcg_gen_orc_i32 -#define tcg_gen_clz_reg tcg_gen_clz_i32 -#define tcg_gen_ctz_reg tcg_gen_ctz_i32 -#define tcg_gen_clzi_reg tcg_gen_clzi_i32 -#define tcg_gen_ctzi_reg tcg_gen_ctzi_i32 -#define tcg_gen_clrsb_reg tcg_gen_clrsb_i32 -#define tcg_gen_ctpop_reg tcg_gen_ctpop_i32 -#define tcg_gen_rotl_reg tcg_gen_rotl_i32 -#define tcg_gen_rotli_reg tcg_gen_rotli_i32 -#define tcg_gen_rotr_reg tcg_gen_rotr_i32 -#define tcg_gen_rotri_reg tcg_gen_rotri_i32 -#define tcg_gen_deposit_reg tcg_gen_deposit_i32 -#define tcg_gen_deposit_z_reg tcg_gen_deposit_z_i32 -#define tcg_gen_extract_reg tcg_gen_extract_i32 -#define tcg_gen_sextract_reg tcg_gen_sextract_i32 -#define tcg_gen_extract2_reg tcg_gen_extract2_i32 -#define tcg_const_reg tcg_const_i32 -#define tcg_const_local_reg tcg_const_local_i32 -#define tcg_constant_reg tcg_constant_i32 -#define tcg_gen_movcond_reg tcg_gen_movcond_i32 -#define tcg_gen_add2_reg tcg_gen_add2_i32 -#define tcg_gen_sub2_reg tcg_gen_sub2_i32 -#define tcg_gen_qemu_ld_reg tcg_gen_qemu_ld_i32 -#define tcg_gen_qemu_st_reg tcg_gen_qemu_st_i32 -#define tcg_gen_atomic_xchg_reg tcg_gen_atomic_xchg_i32 -#define tcg_gen_trunc_reg_ptr tcg_gen_ext_i32_ptr -#endif /* TARGET_REGISTER_BITS */ typedef struct DisasCond { TCGCond c; - TCGv_reg a0, a1; + TCGv_i64 a0, a1; } DisasCond; typedef struct DisasContext { DisasContextBase base; CPUState *cs; - target_ureg iaoq_f; - target_ureg iaoq_b; - target_ureg iaoq_n; - TCGv_reg iaoq_n_var; - - int ntempr, ntempl; - TCGv_reg tempr[8]; - TCGv_tl templ[4]; + uint64_t iaoq_f; + uint64_t iaoq_b; + uint64_t iaoq_n; + TCGv_i64 iaoq_n_var; DisasCond null_cond; TCGLabel *null_lab; + TCGv_i64 zero; + uint32_t insn; uint32_t tb_flags; int mmu_idx; int privilege; bool psw_n_nonzero; + bool is_pa20; + bool insn_start_updated; #ifdef CONFIG_USER_ONLY MemOp unalign; @@ -281,19 +69,24 @@ typedef struct DisasContext { } DisasContext; #ifdef CONFIG_USER_ONLY -#define UNALIGN(C) (C)->unalign +#define UNALIGN(C) (C)->unalign +#define MMU_DISABLED(C) false #else -#define UNALIGN(C) 0 +#define UNALIGN(C) MO_ALIGN +#define MMU_DISABLED(C) MMU_IDX_MMU_DISABLED((C)->mmu_idx) #endif /* Note that ssm/rsm instructions number PSW_W and PSW_E differently. */ static int expand_sm_imm(DisasContext *ctx, int val) { - if (val & PSW_SM_E) { - val = (val & ~PSW_SM_E) | PSW_E; - } - if (val & PSW_SM_W) { - val = (val & ~PSW_SM_W) | PSW_W; + /* Keep unimplemented bits disabled -- see cpu_hppa_put_psw. */ + if (ctx->is_pa20) { + if (val & PSW_SM_W) { + val |= PSW_W; + } + val &= ~(PSW_SM_W | PSW_SM_E | PSW_G); + } else { + val &= ~(PSW_SM_W | PSW_SM_E | PSW_O); } return val; } @@ -328,18 +121,93 @@ static int expand_shl2(DisasContext *ctx, int val) return val << 2; } -/* Used for fp memory ops. */ -static int expand_shl3(DisasContext *ctx, int val) -{ - return val << 3; -} - /* Used for assemble_21. */ static int expand_shl11(DisasContext *ctx, int val) { return val << 11; } +static int assemble_6(DisasContext *ctx, int val) +{ + /* + * Officially, 32 * x + 32 - y. + * Here, x is already in bit 5, and y is [4:0]. + * Since -y = ~y + 1, in 5 bits 32 - y => y ^ 31 + 1, + * with the overflow from bit 4 summing with x. + */ + return (val ^ 31) + 1; +} + +/* Expander for assemble_16a(s,cat(im10a,0),i). */ +static int expand_11a(DisasContext *ctx, int val) +{ + /* + * @val is bit 0 and bits [4:15]. + * Swizzle thing around depending on PSW.W. + */ + int im10a = extract32(val, 1, 10); + int s = extract32(val, 11, 2); + int i = (-(val & 1) << 13) | (im10a << 3); + + if (ctx->tb_flags & PSW_W) { + i ^= s << 13; + } + return i; +} + +/* Expander for assemble_16a(s,im11a,i). */ +static int expand_12a(DisasContext *ctx, int val) +{ + /* + * @val is bit 0 and bits [3:15]. + * Swizzle thing around depending on PSW.W. + */ + int im11a = extract32(val, 1, 11); + int s = extract32(val, 12, 2); + int i = (-(val & 1) << 13) | (im11a << 2); + + if (ctx->tb_flags & PSW_W) { + i ^= s << 13; + } + return i; +} + +/* Expander for assemble_16(s,im14). */ +static int expand_16(DisasContext *ctx, int val) +{ + /* + * @val is bits [0:15], containing both im14 and s. + * Swizzle thing around depending on PSW.W. + */ + int s = extract32(val, 14, 2); + int i = (-(val & 1) << 13) | extract32(val, 1, 13); + + if (ctx->tb_flags & PSW_W) { + i ^= s << 13; + } + return i; +} + +/* The sp field is only present with !PSW_W. */ +static int sp0_if_wide(DisasContext *ctx, int sp) +{ + return ctx->tb_flags & PSW_W ? 0 : sp; +} + +/* Translate CMPI doubleword conditions to standard. */ +static int cmpbid_c(DisasContext *ctx, int val) +{ + return val ? val : 4; /* 0 == "*<<" */ +} + +/* + * In many places pa1.x did not decode the bit that later became + * the pa2.0 D bit. Suppress D unless the cpu is pa2.0. + */ +static int pa20_d(DisasContext *ctx, int val) +{ + return ctx->is_pa20 & val; +} /* Include the auto-generated decoder. */ #include "decode-insns.c.inc" @@ -358,26 +226,24 @@ static int expand_shl11(DisasContext *ctx, int val) #define DISAS_EXIT DISAS_TARGET_3 /* global register indexes */ -static TCGv_reg cpu_gr[32]; +static TCGv_i64 cpu_gr[32]; static TCGv_i64 cpu_sr[4]; static TCGv_i64 cpu_srH; -static TCGv_reg cpu_iaoq_f; -static TCGv_reg cpu_iaoq_b; +static TCGv_i64 cpu_iaoq_f; +static TCGv_i64 cpu_iaoq_b; static TCGv_i64 cpu_iasq_f; static TCGv_i64 cpu_iasq_b; -static TCGv_reg cpu_sar; -static TCGv_reg cpu_psw_n; -static TCGv_reg cpu_psw_v; -static TCGv_reg cpu_psw_cb; -static TCGv_reg cpu_psw_cb_msb; - -#include "exec/gen-icount.h" +static TCGv_i64 cpu_sar; +static TCGv_i64 cpu_psw_n; +static TCGv_i64 cpu_psw_v; +static TCGv_i64 cpu_psw_cb; +static TCGv_i64 cpu_psw_cb_msb; void hppa_translate_init(void) { #define DEF_VAR(V) { &cpu_##V, #V, offsetof(CPUHPPAState, V) } - typedef struct { TCGv_reg *var; const char *name; int ofs; } GlobalVar; + typedef struct { TCGv_i64 *var; const char *name; int ofs; } GlobalVar; static const GlobalVar vars[] = { { &cpu_sar, "sar", offsetof(CPUHPPAState, cr[CR_SAR]) }, DEF_VAR(psw_n), @@ -406,32 +272,39 @@ void hppa_translate_init(void) cpu_gr[0] = NULL; for (i = 1; i < 32; i++) { - cpu_gr[i] = tcg_global_mem_new(cpu_env, + cpu_gr[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHPPAState, gr[i]), gr_names[i]); } for (i = 0; i < 4; i++) { - cpu_sr[i] = tcg_global_mem_new_i64(cpu_env, + cpu_sr[i] = tcg_global_mem_new_i64(tcg_env, offsetof(CPUHPPAState, sr[i]), sr_names[i]); } - cpu_srH = tcg_global_mem_new_i64(cpu_env, + cpu_srH = tcg_global_mem_new_i64(tcg_env, offsetof(CPUHPPAState, sr[4]), sr_names[4]); for (i = 0; i < ARRAY_SIZE(vars); ++i) { const GlobalVar *v = &vars[i]; - *v->var = tcg_global_mem_new(cpu_env, v->ofs, v->name); + *v->var = tcg_global_mem_new(tcg_env, v->ofs, v->name); } - cpu_iasq_f = tcg_global_mem_new_i64(cpu_env, + cpu_iasq_f = tcg_global_mem_new_i64(tcg_env, offsetof(CPUHPPAState, iasq_f), "iasq_f"); - cpu_iasq_b = tcg_global_mem_new_i64(cpu_env, + cpu_iasq_b = tcg_global_mem_new_i64(tcg_env, offsetof(CPUHPPAState, iasq_b), "iasq_b"); } +static void set_insn_breg(DisasContext *ctx, int breg) +{ + assert(!ctx->insn_start_updated); + ctx->insn_start_updated = true; + tcg_set_insn_start_param(ctx->base.insn_start, 2, breg); +} + static DisasCond cond_make_f(void) { return (DisasCond){ @@ -455,46 +328,42 @@ static DisasCond cond_make_n(void) return (DisasCond){ .c = TCG_COND_NE, .a0 = cpu_psw_n, - .a1 = tcg_constant_reg(0) + .a1 = tcg_constant_i64(0) }; } -static DisasCond cond_make_0_tmp(TCGCond c, TCGv_reg a0) +static DisasCond cond_make_tmp(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) { assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); - return (DisasCond){ - .c = c, .a0 = a0, .a1 = tcg_constant_reg(0) - }; + return (DisasCond){ .c = c, .a0 = a0, .a1 = a1 }; } -static DisasCond cond_make_0(TCGCond c, TCGv_reg a0) +static DisasCond cond_make_0_tmp(TCGCond c, TCGv_i64 a0) { - TCGv_reg tmp = tcg_temp_new(); - tcg_gen_mov_reg(tmp, a0); + return cond_make_tmp(c, a0, tcg_constant_i64(0)); +} + +static DisasCond cond_make_0(TCGCond c, TCGv_i64 a0) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_mov_i64(tmp, a0); return cond_make_0_tmp(c, tmp); } -static DisasCond cond_make(TCGCond c, TCGv_reg a0, TCGv_reg a1) +static DisasCond cond_make(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) { - DisasCond r = { .c = c }; + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); - assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); - r.a0 = tcg_temp_new(); - tcg_gen_mov_reg(r.a0, a0); - r.a1 = tcg_temp_new(); - tcg_gen_mov_reg(r.a1, a1); - - return r; + tcg_gen_mov_i64(t0, a0); + tcg_gen_mov_i64(t1, a1); + return cond_make_tmp(c, t0, t1); } static void cond_free(DisasCond *cond) { switch (cond->c) { default: - if (cond->a0 != cpu_psw_n) { - tcg_temp_free(cond->a0); - } - tcg_temp_free(cond->a1); cond->a0 = NULL; cond->a1 = NULL; /* fallthru */ @@ -506,60 +375,35 @@ static void cond_free(DisasCond *cond) } } -static TCGv_reg get_temp(DisasContext *ctx) -{ - unsigned i = ctx->ntempr++; - g_assert(i < ARRAY_SIZE(ctx->tempr)); - return ctx->tempr[i] = tcg_temp_new(); -} - -#ifndef CONFIG_USER_ONLY -static TCGv_tl get_temp_tl(DisasContext *ctx) -{ - unsigned i = ctx->ntempl++; - g_assert(i < ARRAY_SIZE(ctx->templ)); - return ctx->templ[i] = tcg_temp_new_tl(); -} -#endif - -static TCGv_reg load_const(DisasContext *ctx, target_sreg v) -{ - TCGv_reg t = get_temp(ctx); - tcg_gen_movi_reg(t, v); - return t; -} - -static TCGv_reg load_gpr(DisasContext *ctx, unsigned reg) +static TCGv_i64 load_gpr(DisasContext *ctx, unsigned reg) { if (reg == 0) { - TCGv_reg t = get_temp(ctx); - tcg_gen_movi_reg(t, 0); - return t; + return ctx->zero; } else { return cpu_gr[reg]; } } -static TCGv_reg dest_gpr(DisasContext *ctx, unsigned reg) +static TCGv_i64 dest_gpr(DisasContext *ctx, unsigned reg) { if (reg == 0 || ctx->null_cond.c != TCG_COND_NEVER) { - return get_temp(ctx); + return tcg_temp_new_i64(); } else { return cpu_gr[reg]; } } -static void save_or_nullify(DisasContext *ctx, TCGv_reg dest, TCGv_reg t) +static void save_or_nullify(DisasContext *ctx, TCGv_i64 dest, TCGv_i64 t) { if (ctx->null_cond.c != TCG_COND_NEVER) { - tcg_gen_movcond_reg(ctx->null_cond.c, dest, ctx->null_cond.a0, + tcg_gen_movcond_i64(ctx->null_cond.c, dest, ctx->null_cond.a0, ctx->null_cond.a1, dest, t); } else { - tcg_gen_mov_reg(dest, t); + tcg_gen_mov_i64(dest, t); } } -static void save_gpr(DisasContext *ctx, unsigned reg, TCGv_reg t) +static void save_gpr(DisasContext *ctx, unsigned reg, TCGv_i64 t) { if (reg != 0) { save_or_nullify(ctx, cpu_gr[reg], t); @@ -577,7 +421,7 @@ static void save_gpr(DisasContext *ctx, unsigned reg, TCGv_reg t) static TCGv_i32 load_frw_i32(unsigned rt) { TCGv_i32 ret = tcg_temp_new_i32(); - tcg_gen_ld_i32(ret, cpu_env, + tcg_gen_ld_i32(ret, tcg_env, offsetof(CPUHPPAState, fr[rt & 31]) + (rt & 32 ? LO_OFS : HI_OFS)); return ret; @@ -586,7 +430,9 @@ static TCGv_i32 load_frw_i32(unsigned rt) static TCGv_i32 load_frw0_i32(unsigned rt) { if (rt == 0) { - return tcg_const_i32(0); + TCGv_i32 ret = tcg_temp_new_i32(); + tcg_gen_movi_i32(ret, 0); + return ret; } else { return load_frw_i32(rt); } @@ -594,20 +440,20 @@ static TCGv_i32 load_frw0_i32(unsigned rt) static TCGv_i64 load_frw0_i64(unsigned rt) { + TCGv_i64 ret = tcg_temp_new_i64(); if (rt == 0) { - return tcg_const_i64(0); + tcg_gen_movi_i64(ret, 0); } else { - TCGv_i64 ret = tcg_temp_new_i64(); - tcg_gen_ld32u_i64(ret, cpu_env, + tcg_gen_ld32u_i64(ret, tcg_env, offsetof(CPUHPPAState, fr[rt & 31]) + (rt & 32 ? LO_OFS : HI_OFS)); - return ret; } + return ret; } static void save_frw_i32(unsigned rt, TCGv_i32 val) { - tcg_gen_st_i32(val, cpu_env, + tcg_gen_st_i32(val, tcg_env, offsetof(CPUHPPAState, fr[rt & 31]) + (rt & 32 ? LO_OFS : HI_OFS)); } @@ -618,14 +464,16 @@ static void save_frw_i32(unsigned rt, TCGv_i32 val) static TCGv_i64 load_frd(unsigned rt) { TCGv_i64 ret = tcg_temp_new_i64(); - tcg_gen_ld_i64(ret, cpu_env, offsetof(CPUHPPAState, fr[rt])); + tcg_gen_ld_i64(ret, tcg_env, offsetof(CPUHPPAState, fr[rt])); return ret; } static TCGv_i64 load_frd0(unsigned rt) { if (rt == 0) { - return tcg_const_i64(0); + TCGv_i64 ret = tcg_temp_new_i64(); + tcg_gen_movi_i64(ret, 0); + return ret; } else { return load_frd(rt); } @@ -633,7 +481,7 @@ static TCGv_i64 load_frd0(unsigned rt) static void save_frd(unsigned rt, TCGv_i64 val) { - tcg_gen_st_i64(val, cpu_env, offsetof(CPUHPPAState, fr[rt])); + tcg_gen_st_i64(val, tcg_env, offsetof(CPUHPPAState, fr[rt])); } static void load_spr(DisasContext *ctx, TCGv_i64 dest, unsigned reg) @@ -646,7 +494,7 @@ static void load_spr(DisasContext *ctx, TCGv_i64 dest, unsigned reg) } else if (ctx->tb_flags & TB_FLAG_SR_SAME) { tcg_gen_mov_i64(dest, cpu_srH); } else { - tcg_gen_ld_i64(dest, cpu_env, offsetof(CPUHPPAState, sr[reg])); + tcg_gen_ld_i64(dest, tcg_env, offsetof(CPUHPPAState, sr[reg])); } #endif } @@ -663,18 +511,18 @@ static void nullify_over(DisasContext *ctx) /* If we're using PSW[N], copy it to a temp because... */ if (ctx->null_cond.a0 == cpu_psw_n) { - ctx->null_cond.a0 = tcg_temp_new(); - tcg_gen_mov_reg(ctx->null_cond.a0, cpu_psw_n); + ctx->null_cond.a0 = tcg_temp_new_i64(); + tcg_gen_mov_i64(ctx->null_cond.a0, cpu_psw_n); } /* ... we clear it before branching over the implementation, so that (1) it's clear after nullifying this insn and (2) if this insn nullifies the next, PSW[N] is valid. */ if (ctx->psw_n_nonzero) { ctx->psw_n_nonzero = false; - tcg_gen_movi_reg(cpu_psw_n, 0); + tcg_gen_movi_i64(cpu_psw_n, 0); } - tcg_gen_brcond_reg(ctx->null_cond.c, ctx->null_cond.a0, + tcg_gen_brcond_i64(ctx->null_cond.c, ctx->null_cond.a0, ctx->null_cond.a1, ctx->null_lab); cond_free(&ctx->null_cond); } @@ -685,12 +533,12 @@ static void nullify_save(DisasContext *ctx) { if (ctx->null_cond.c == TCG_COND_NEVER) { if (ctx->psw_n_nonzero) { - tcg_gen_movi_reg(cpu_psw_n, 0); + tcg_gen_movi_i64(cpu_psw_n, 0); } return; } if (ctx->null_cond.a0 != cpu_psw_n) { - tcg_gen_setcond_reg(ctx->null_cond.c, cpu_psw_n, + tcg_gen_setcond_i64(ctx->null_cond.c, cpu_psw_n, ctx->null_cond.a0, ctx->null_cond.a1); ctx->psw_n_nonzero = true; } @@ -703,7 +551,7 @@ static void nullify_save(DisasContext *ctx) static void nullify_set(DisasContext *ctx, bool x) { if (ctx->psw_n_nonzero || x) { - tcg_gen_movi_reg(cpu_psw_n, x); + tcg_gen_movi_i64(cpu_psw_n, x); } } @@ -746,29 +594,42 @@ static bool nullify_end(DisasContext *ctx) return true; } -static void copy_iaoq_entry(TCGv_reg dest, target_ureg ival, TCGv_reg vval) +static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, + uint64_t ival, TCGv_i64 vval) { - if (unlikely(ival == -1)) { - tcg_gen_mov_reg(dest, vval); + uint64_t mask = gva_offset_mask(ctx->tb_flags); + + if (ival != -1) { + tcg_gen_movi_i64(dest, ival & mask); + return; + } + tcg_debug_assert(vval != NULL); + + /* + * We know that the IAOQ is already properly masked. + * This optimization is primarily for "iaoq_f = iaoq_b". + */ + if (vval == cpu_iaoq_f || vval == cpu_iaoq_b) { + tcg_gen_mov_i64(dest, vval); } else { - tcg_gen_movi_reg(dest, ival); + tcg_gen_andi_i64(dest, vval, mask); } } -static inline target_ureg iaoq_dest(DisasContext *ctx, target_sreg disp) +static inline uint64_t iaoq_dest(DisasContext *ctx, int64_t disp) { return ctx->iaoq_f + disp + 8; } static void gen_excp_1(int exception) { - gen_helper_excp(cpu_env, tcg_constant_i32(exception)); + gen_helper_excp(tcg_env, tcg_constant_i32(exception)); } static void gen_excp(DisasContext *ctx, int exception) { - copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); - copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); + copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); nullify_save(ctx); gen_excp_1(exception); ctx->base.is_jmp = DISAS_NORETURN; @@ -777,8 +638,8 @@ static void gen_excp(DisasContext *ctx, int exception) static bool gen_excp_iir(DisasContext *ctx, int exc) { nullify_over(ctx); - tcg_gen_st_reg(tcg_constant_reg(ctx->insn), - cpu_env, offsetof(CPUHPPAState, cr[CR_IIR])); + tcg_gen_st_i64(tcg_constant_i64(ctx->insn), + tcg_env, offsetof(CPUHPPAState, cr[CR_IIR])); gen_excp(ctx, exc); return nullify_end(ctx); } @@ -800,7 +661,7 @@ static bool gen_illegal(DisasContext *ctx) } while (0) #endif -static bool use_goto_tb(DisasContext *ctx, target_ureg dest) +static bool use_goto_tb(DisasContext *ctx, uint64_t dest) { return translator_use_goto_tb(&ctx->base, dest); } @@ -816,16 +677,16 @@ static bool use_nullify_skip(DisasContext *ctx) } static void gen_goto_tb(DisasContext *ctx, int which, - target_ureg f, target_ureg b) + uint64_t f, uint64_t b) { if (f != -1 && b != -1 && use_goto_tb(ctx, f)) { tcg_gen_goto_tb(which); - tcg_gen_movi_reg(cpu_iaoq_f, f); - tcg_gen_movi_reg(cpu_iaoq_b, b); + copy_iaoq_entry(ctx, cpu_iaoq_f, f, NULL); + copy_iaoq_entry(ctx, cpu_iaoq_b, b, NULL); tcg_gen_exit_tb(ctx->base.tb, which); } else { - copy_iaoq_entry(cpu_iaoq_f, f, cpu_iaoq_b); - copy_iaoq_entry(cpu_iaoq_b, b, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_iaoq_f, f, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_b, b, ctx->iaoq_n_var); tcg_gen_lookup_and_goto_ptr(); } } @@ -845,22 +706,30 @@ static bool cond_need_cb(int c) * the Parisc 1.1 Architecture Reference Manual for details. */ -static DisasCond do_cond(unsigned cf, TCGv_reg res, - TCGv_reg cb_msb, TCGv_reg sv) +static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, + TCGv_i64 res, TCGv_i64 uv, TCGv_i64 sv) { DisasCond cond; - TCGv_reg tmp; + TCGv_i64 tmp; switch (cf >> 1) { case 0: /* Never / TR (0 / 1) */ cond = cond_make_f(); break; case 1: /* = / <> (Z / !Z) */ + if (!d) { + tmp = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(tmp, res); + res = tmp; + } cond = cond_make_0(TCG_COND_EQ, res); break; case 2: /* < / >= (N ^ V / !(N ^ V) */ - tmp = tcg_temp_new(); - tcg_gen_xor_reg(tmp, res, sv); + tmp = tcg_temp_new_i64(); + tcg_gen_xor_i64(tmp, res, sv); + if (!d) { + tcg_gen_ext32s_i64(tmp, tmp); + } cond = cond_make_0_tmp(TCG_COND_LT, tmp); break; case 3: /* <= / > (N ^ V) | Z / !((N ^ V) | Z) */ @@ -873,27 +742,40 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res, * !(~(res ^ sv) >> 31) | !res * !(~(res ^ sv) >> 31 & res) */ - tmp = tcg_temp_new(); - tcg_gen_eqv_reg(tmp, res, sv); - tcg_gen_sari_reg(tmp, tmp, TARGET_REGISTER_BITS - 1); - tcg_gen_and_reg(tmp, tmp, res); + tmp = tcg_temp_new_i64(); + tcg_gen_eqv_i64(tmp, res, sv); + if (!d) { + tcg_gen_sextract_i64(tmp, tmp, 31, 1); + tcg_gen_and_i64(tmp, tmp, res); + tcg_gen_ext32u_i64(tmp, tmp); + } else { + tcg_gen_sari_i64(tmp, tmp, 63); + tcg_gen_and_i64(tmp, tmp, res); + } cond = cond_make_0_tmp(TCG_COND_EQ, tmp); break; - case 4: /* NUV / UV (!C / C) */ - cond = cond_make_0(TCG_COND_EQ, cb_msb); + case 4: /* NUV / UV (!UV / UV) */ + cond = cond_make_0(TCG_COND_EQ, uv); break; - case 5: /* ZNV / VNZ (!C | Z / C & !Z) */ - tmp = tcg_temp_new(); - tcg_gen_neg_reg(tmp, cb_msb); - tcg_gen_and_reg(tmp, tmp, res); + case 5: /* ZNV / VNZ (!UV | Z / UV & !Z) */ + tmp = tcg_temp_new_i64(); + tcg_gen_movcond_i64(TCG_COND_EQ, tmp, uv, ctx->zero, ctx->zero, res); + if (!d) { + tcg_gen_ext32u_i64(tmp, tmp); + } cond = cond_make_0_tmp(TCG_COND_EQ, tmp); break; case 6: /* SV / NSV (V / !V) */ + if (!d) { + tmp = tcg_temp_new_i64(); + tcg_gen_ext32s_i64(tmp, sv); + sv = tmp; + } cond = cond_make_0(TCG_COND_LT, sv); break; case 7: /* OD / EV */ - tmp = tcg_temp_new(); - tcg_gen_andi_reg(tmp, res, 1); + tmp = tcg_temp_new_i64(); + tcg_gen_andi_i64(tmp, res, 1); cond = cond_make_0_tmp(TCG_COND_NE, tmp); break; default: @@ -910,35 +792,55 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res, can use the inputs directly. This can allow other computation to be deleted as unused. */ -static DisasCond do_sub_cond(unsigned cf, TCGv_reg res, - TCGv_reg in1, TCGv_reg in2, TCGv_reg sv) +static DisasCond do_sub_cond(DisasContext *ctx, unsigned cf, bool d, + TCGv_i64 res, TCGv_i64 in1, + TCGv_i64 in2, TCGv_i64 sv) { - DisasCond cond; + TCGCond tc; + bool ext_uns; switch (cf >> 1) { case 1: /* = / <> */ - cond = cond_make(TCG_COND_EQ, in1, in2); + tc = TCG_COND_EQ; + ext_uns = true; break; case 2: /* < / >= */ - cond = cond_make(TCG_COND_LT, in1, in2); + tc = TCG_COND_LT; + ext_uns = false; break; case 3: /* <= / > */ - cond = cond_make(TCG_COND_LE, in1, in2); + tc = TCG_COND_LE; + ext_uns = false; break; case 4: /* << / >>= */ - cond = cond_make(TCG_COND_LTU, in1, in2); + tc = TCG_COND_LTU; + ext_uns = true; break; case 5: /* <<= / >> */ - cond = cond_make(TCG_COND_LEU, in1, in2); + tc = TCG_COND_LEU; + ext_uns = true; break; default: - return do_cond(cf, res, NULL, sv); - } - if (cf & 1) { - cond.c = tcg_invert_cond(cond.c); + return do_cond(ctx, cf, d, res, NULL, sv); } - return cond; + if (cf & 1) { + tc = tcg_invert_cond(tc); + } + if (!d) { + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + if (ext_uns) { + tcg_gen_ext32u_i64(t1, in1); + tcg_gen_ext32u_i64(t2, in2); + } else { + tcg_gen_ext32s_i64(t1, in1); + tcg_gen_ext32s_i64(t2, in2); + } + return cond_make_tmp(tc, t1, t2); + } + return cond_make(tc, in1, in2); } /* @@ -950,8 +852,12 @@ static DisasCond do_sub_cond(unsigned cf, TCGv_reg res, * how cases c={2,3} are treated. */ -static DisasCond do_log_cond(unsigned cf, TCGv_reg res) +static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, bool d, + TCGv_i64 res) { + TCGCond tc; + bool ext_uns; + switch (cf) { case 0: /* never */ case 9: /* undef, C */ @@ -966,30 +872,55 @@ static DisasCond do_log_cond(unsigned cf, TCGv_reg res) return cond_make_t(); case 2: /* == */ - return cond_make_0(TCG_COND_EQ, res); + tc = TCG_COND_EQ; + ext_uns = true; + break; case 3: /* <> */ - return cond_make_0(TCG_COND_NE, res); + tc = TCG_COND_NE; + ext_uns = true; + break; case 4: /* < */ - return cond_make_0(TCG_COND_LT, res); + tc = TCG_COND_LT; + ext_uns = false; + break; case 5: /* >= */ - return cond_make_0(TCG_COND_GE, res); + tc = TCG_COND_GE; + ext_uns = false; + break; case 6: /* <= */ - return cond_make_0(TCG_COND_LE, res); + tc = TCG_COND_LE; + ext_uns = false; + break; case 7: /* > */ - return cond_make_0(TCG_COND_GT, res); + tc = TCG_COND_GT; + ext_uns = false; + break; case 14: /* OD */ case 15: /* EV */ - return do_cond(cf, res, NULL, NULL); + return do_cond(ctx, cf, d, res, NULL, NULL); default: g_assert_not_reached(); } + + if (!d) { + TCGv_i64 tmp = tcg_temp_new_i64(); + + if (ext_uns) { + tcg_gen_ext32u_i64(tmp, res); + } else { + tcg_gen_ext32s_i64(tmp, res); + } + return cond_make_0_tmp(tc, tmp); + } + return cond_make_0(tc, res); } /* Similar, but for shift/extract/deposit conditions. */ -static DisasCond do_sed_cond(unsigned orig, TCGv_reg res) +static DisasCond do_sed_cond(DisasContext *ctx, unsigned orig, bool d, + TCGv_i64 res) { unsigned c, f; @@ -1002,171 +933,195 @@ static DisasCond do_sed_cond(unsigned orig, TCGv_reg res) } f = (orig & 4) / 4; - return do_log_cond(c * 2 + f, res); + return do_log_cond(ctx, c * 2 + f, d, res); } -/* Similar, but for unit conditions. */ - -static DisasCond do_unit_cond(unsigned cf, TCGv_reg res, - TCGv_reg in1, TCGv_reg in2) +/* Similar, but for unit zero conditions. */ +static DisasCond do_unit_zero_cond(unsigned cf, bool d, TCGv_i64 res) { - DisasCond cond; - TCGv_reg tmp, cb = NULL; - - if (cf & 8) { - /* Since we want to test lots of carry-out bits all at once, do not - * do our normal thing and compute carry-in of bit B+1 since that - * leaves us with carry bits spread across two words. - */ - cb = tcg_temp_new(); - tmp = tcg_temp_new(); - tcg_gen_or_reg(cb, in1, in2); - tcg_gen_and_reg(tmp, in1, in2); - tcg_gen_andc_reg(cb, cb, res); - tcg_gen_or_reg(cb, cb, tmp); - tcg_temp_free(tmp); - } + TCGv_i64 tmp; + uint64_t d_repl = d ? 0x0000000100000001ull : 1; + uint64_t ones = 0, sgns = 0; switch (cf >> 1) { - case 0: /* never / TR */ - case 1: /* undefined */ - case 5: /* undefined */ - cond = cond_make_f(); + case 1: /* SBW / NBW */ + if (d) { + ones = d_repl; + sgns = d_repl << 31; + } break; - case 2: /* SBZ / NBZ */ - /* See hasless(v,1) from - * https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord - */ - tmp = tcg_temp_new(); - tcg_gen_subi_reg(tmp, res, 0x01010101u); - tcg_gen_andc_reg(tmp, tmp, res); - tcg_gen_andi_reg(tmp, tmp, 0x80808080u); - cond = cond_make_0(TCG_COND_NE, tmp); - tcg_temp_free(tmp); + ones = d_repl * 0x01010101u; + sgns = ones << 7; break; - case 3: /* SHZ / NHZ */ - tmp = tcg_temp_new(); - tcg_gen_subi_reg(tmp, res, 0x00010001u); - tcg_gen_andc_reg(tmp, tmp, res); - tcg_gen_andi_reg(tmp, tmp, 0x80008000u); - cond = cond_make_0(TCG_COND_NE, tmp); - tcg_temp_free(tmp); + ones = d_repl * 0x00010001u; + sgns = ones << 15; break; - - case 4: /* SDC / NDC */ - tcg_gen_andi_reg(cb, cb, 0x88888888u); - cond = cond_make_0(TCG_COND_NE, cb); - break; - - case 6: /* SBC / NBC */ - tcg_gen_andi_reg(cb, cb, 0x80808080u); - cond = cond_make_0(TCG_COND_NE, cb); - break; - - case 7: /* SHC / NHC */ - tcg_gen_andi_reg(cb, cb, 0x80008000u); - cond = cond_make_0(TCG_COND_NE, cb); - break; - - default: - g_assert_not_reached(); } - if (cf & 8) { - tcg_temp_free(cb); - } - if (cf & 1) { - cond.c = tcg_invert_cond(cond.c); + if (ones == 0) { + /* Undefined, or 0/1 (never/always). */ + return cf & 1 ? cond_make_t() : cond_make_f(); } - return cond; + /* + * See hasless(v,1) from + * https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord + */ + tmp = tcg_temp_new_i64(); + tcg_gen_subi_i64(tmp, res, ones); + tcg_gen_andc_i64(tmp, tmp, res); + tcg_gen_andi_i64(tmp, tmp, sgns); + + return cond_make_0_tmp(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, tmp); +} + +static TCGv_i64 get_carry(DisasContext *ctx, bool d, + TCGv_i64 cb, TCGv_i64 cb_msb) +{ + if (!d) { + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_extract_i64(t, cb, 32, 1); + return t; + } + return cb_msb; +} + +static TCGv_i64 get_psw_carry(DisasContext *ctx, bool d) +{ + return get_carry(ctx, d, cpu_psw_cb, cpu_psw_cb_msb); } /* Compute signed overflow for addition. */ -static TCGv_reg do_add_sv(DisasContext *ctx, TCGv_reg res, - TCGv_reg in1, TCGv_reg in2) +static TCGv_i64 do_add_sv(DisasContext *ctx, TCGv_i64 res, + TCGv_i64 in1, TCGv_i64 in2, + TCGv_i64 orig_in1, int shift, bool d) { - TCGv_reg sv = get_temp(ctx); - TCGv_reg tmp = tcg_temp_new(); + TCGv_i64 sv = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_xor_reg(sv, res, in1); - tcg_gen_xor_reg(tmp, in1, in2); - tcg_gen_andc_reg(sv, sv, tmp); - tcg_temp_free(tmp); + tcg_gen_xor_i64(sv, res, in1); + tcg_gen_xor_i64(tmp, in1, in2); + tcg_gen_andc_i64(sv, sv, tmp); + switch (shift) { + case 0: + break; + case 1: + /* Shift left by one and compare the sign. */ + tcg_gen_add_i64(tmp, orig_in1, orig_in1); + tcg_gen_xor_i64(tmp, tmp, orig_in1); + /* Incorporate into the overflow. */ + tcg_gen_or_i64(sv, sv, tmp); + break; + default: + { + int sign_bit = d ? 63 : 31; + + /* Compare the sign against all lower bits. */ + tcg_gen_sextract_i64(tmp, orig_in1, sign_bit, 1); + tcg_gen_xor_i64(tmp, tmp, orig_in1); + /* + * If one of the bits shifting into or through the sign + * differs, then we have overflow. + */ + tcg_gen_extract_i64(tmp, tmp, sign_bit - shift, shift); + tcg_gen_movcond_i64(TCG_COND_NE, sv, tmp, ctx->zero, + tcg_constant_i64(-1), sv); + } + } return sv; } +/* Compute unsigned overflow for addition. */ +static TCGv_i64 do_add_uv(DisasContext *ctx, TCGv_i64 cb, TCGv_i64 cb_msb, + TCGv_i64 in1, int shift, bool d) +{ + if (shift == 0) { + return get_carry(ctx, d, cb, cb_msb); + } else { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_extract_i64(tmp, in1, (d ? 63 : 31) - shift, shift); + tcg_gen_or_i64(tmp, tmp, get_carry(ctx, d, cb, cb_msb)); + return tmp; + } +} + /* Compute signed overflow for subtraction. */ -static TCGv_reg do_sub_sv(DisasContext *ctx, TCGv_reg res, - TCGv_reg in1, TCGv_reg in2) +static TCGv_i64 do_sub_sv(DisasContext *ctx, TCGv_i64 res, + TCGv_i64 in1, TCGv_i64 in2) { - TCGv_reg sv = get_temp(ctx); - TCGv_reg tmp = tcg_temp_new(); + TCGv_i64 sv = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_xor_reg(sv, res, in1); - tcg_gen_xor_reg(tmp, in1, in2); - tcg_gen_and_reg(sv, sv, tmp); - tcg_temp_free(tmp); + tcg_gen_xor_i64(sv, res, in1); + tcg_gen_xor_i64(tmp, in1, in2); + tcg_gen_and_i64(sv, sv, tmp); return sv; } -static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned shift, bool is_l, - bool is_tsv, bool is_tc, bool is_c, unsigned cf) +static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1, + TCGv_i64 in2, unsigned shift, bool is_l, + bool is_tsv, bool is_tc, bool is_c, unsigned cf, bool d) { - TCGv_reg dest, cb, cb_msb, sv, tmp; + TCGv_i64 dest, cb, cb_msb, in1, uv, sv, tmp; unsigned c = cf >> 1; DisasCond cond; - dest = tcg_temp_new(); + dest = tcg_temp_new_i64(); cb = NULL; cb_msb = NULL; + in1 = orig_in1; if (shift) { - tmp = get_temp(ctx); - tcg_gen_shli_reg(tmp, in1, shift); + tmp = tcg_temp_new_i64(); + tcg_gen_shli_i64(tmp, in1, shift); in1 = tmp; } if (!is_l || cond_need_cb(c)) { - TCGv_reg zero = tcg_constant_reg(0); - cb_msb = get_temp(ctx); - tcg_gen_add2_reg(dest, cb_msb, in1, zero, in2, zero); + cb_msb = tcg_temp_new_i64(); + cb = tcg_temp_new_i64(); + + tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero, in2, ctx->zero); if (is_c) { - tcg_gen_add2_reg(dest, cb_msb, dest, cb_msb, cpu_psw_cb_msb, zero); - } - if (!is_l) { - cb = get_temp(ctx); - tcg_gen_xor_reg(cb, in1, in2); - tcg_gen_xor_reg(cb, cb, dest); + tcg_gen_add2_i64(dest, cb_msb, dest, cb_msb, + get_psw_carry(ctx, d), ctx->zero); } + tcg_gen_xor_i64(cb, in1, in2); + tcg_gen_xor_i64(cb, cb, dest); } else { - tcg_gen_add_reg(dest, in1, in2); + tcg_gen_add_i64(dest, in1, in2); if (is_c) { - tcg_gen_add_reg(dest, dest, cpu_psw_cb_msb); + tcg_gen_add_i64(dest, dest, get_psw_carry(ctx, d)); } } /* Compute signed overflow if required. */ sv = NULL; if (is_tsv || cond_need_sv(c)) { - sv = do_add_sv(ctx, dest, in1, in2); + sv = do_add_sv(ctx, dest, in1, in2, orig_in1, shift, d); if (is_tsv) { - /* ??? Need to include overflow from shift. */ - gen_helper_tsv(cpu_env, sv); + if (!d) { + tcg_gen_ext32s_i64(sv, sv); + } + gen_helper_tsv(tcg_env, sv); } } + /* Compute unsigned overflow if required. */ + uv = NULL; + if (cond_need_cb(c)) { + uv = do_add_uv(ctx, cb, cb_msb, orig_in1, shift, d); + } + /* Emit any conditional trap before any writeback. */ - cond = do_cond(cf, dest, cb_msb, sv); + cond = do_cond(ctx, cf, d, dest, uv, sv); if (is_tc) { - tmp = tcg_temp_new(); - tcg_gen_setcond_reg(cond.c, tmp, cond.a0, cond.a1); - gen_helper_tcond(cpu_env, tmp); - tcg_temp_free(tmp); + tmp = tcg_temp_new_i64(); + tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); + gen_helper_tcond(tcg_env, tmp); } /* Write back the result. */ @@ -1175,68 +1130,71 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, save_or_nullify(ctx, cpu_psw_cb_msb, cb_msb); } save_gpr(ctx, rt, dest); - tcg_temp_free(dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); ctx->null_cond = cond; } -static bool do_add_reg(DisasContext *ctx, arg_rrr_cf_sh *a, +static bool do_add_reg(DisasContext *ctx, arg_rrr_cf_d_sh *a, bool is_l, bool is_tsv, bool is_tc, bool is_c) { - TCGv_reg tcg_r1, tcg_r2; + TCGv_i64 tcg_r1, tcg_r2; if (a->cf) { nullify_over(ctx); } tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); - do_add(ctx, a->t, tcg_r1, tcg_r2, a->sh, is_l, is_tsv, is_tc, is_c, a->cf); + do_add(ctx, a->t, tcg_r1, tcg_r2, a->sh, is_l, + is_tsv, is_tc, is_c, a->cf, a->d); return nullify_end(ctx); } static bool do_add_imm(DisasContext *ctx, arg_rri_cf *a, bool is_tsv, bool is_tc) { - TCGv_reg tcg_im, tcg_r2; + TCGv_i64 tcg_im, tcg_r2; if (a->cf) { nullify_over(ctx); } - tcg_im = load_const(ctx, a->i); + tcg_im = tcg_constant_i64(a->i); tcg_r2 = load_gpr(ctx, a->r); - do_add(ctx, a->t, tcg_im, tcg_r2, 0, 0, is_tsv, is_tc, 0, a->cf); + /* All ADDI conditions are 32-bit. */ + do_add(ctx, a->t, tcg_im, tcg_r2, 0, 0, is_tsv, is_tc, 0, a->cf, false); return nullify_end(ctx); } -static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, bool is_tsv, bool is_b, - bool is_tc, unsigned cf) +static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, + TCGv_i64 in2, bool is_tsv, bool is_b, + bool is_tc, unsigned cf, bool d) { - TCGv_reg dest, sv, cb, cb_msb, zero, tmp; + TCGv_i64 dest, sv, cb, cb_msb, tmp; unsigned c = cf >> 1; DisasCond cond; - dest = tcg_temp_new(); - cb = tcg_temp_new(); - cb_msb = tcg_temp_new(); + dest = tcg_temp_new_i64(); + cb = tcg_temp_new_i64(); + cb_msb = tcg_temp_new_i64(); - zero = tcg_constant_reg(0); if (is_b) { /* DEST,C = IN1 + ~IN2 + C. */ - tcg_gen_not_reg(cb, in2); - tcg_gen_add2_reg(dest, cb_msb, in1, zero, cpu_psw_cb_msb, zero); - tcg_gen_add2_reg(dest, cb_msb, dest, cb_msb, cb, zero); - tcg_gen_xor_reg(cb, cb, in1); - tcg_gen_xor_reg(cb, cb, dest); + tcg_gen_not_i64(cb, in2); + tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero, + get_psw_carry(ctx, d), ctx->zero); + tcg_gen_add2_i64(dest, cb_msb, dest, cb_msb, cb, ctx->zero); + tcg_gen_xor_i64(cb, cb, in1); + tcg_gen_xor_i64(cb, cb, dest); } else { - /* DEST,C = IN1 + ~IN2 + 1. We can produce the same result in fewer - operations by seeding the high word with 1 and subtracting. */ - tcg_gen_movi_reg(cb_msb, 1); - tcg_gen_sub2_reg(dest, cb_msb, in1, cb_msb, in2, zero); - tcg_gen_eqv_reg(cb, in1, in2); - tcg_gen_xor_reg(cb, cb, dest); + /* + * DEST,C = IN1 + ~IN2 + 1. We can produce the same result in fewer + * operations by seeding the high word with 1 and subtracting. + */ + TCGv_i64 one = tcg_constant_i64(1); + tcg_gen_sub2_i64(dest, cb_msb, in1, one, in2, ctx->zero); + tcg_gen_eqv_i64(cb, in1, in2); + tcg_gen_xor_i64(cb, cb, dest); } /* Compute signed overflow if required. */ @@ -1244,73 +1202,73 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, if (is_tsv || cond_need_sv(c)) { sv = do_sub_sv(ctx, dest, in1, in2); if (is_tsv) { - gen_helper_tsv(cpu_env, sv); + if (!d) { + tcg_gen_ext32s_i64(sv, sv); + } + gen_helper_tsv(tcg_env, sv); } } /* Compute the condition. We cannot use the special case for borrow. */ if (!is_b) { - cond = do_sub_cond(cf, dest, in1, in2, sv); + cond = do_sub_cond(ctx, cf, d, dest, in1, in2, sv); } else { - cond = do_cond(cf, dest, cb_msb, sv); + cond = do_cond(ctx, cf, d, dest, get_carry(ctx, d, cb, cb_msb), sv); } /* Emit any conditional trap before any writeback. */ if (is_tc) { - tmp = tcg_temp_new(); - tcg_gen_setcond_reg(cond.c, tmp, cond.a0, cond.a1); - gen_helper_tcond(cpu_env, tmp); - tcg_temp_free(tmp); + tmp = tcg_temp_new_i64(); + tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); + gen_helper_tcond(tcg_env, tmp); } /* Write back the result. */ save_or_nullify(ctx, cpu_psw_cb, cb); save_or_nullify(ctx, cpu_psw_cb_msb, cb_msb); save_gpr(ctx, rt, dest); - tcg_temp_free(dest); - tcg_temp_free(cb); - tcg_temp_free(cb_msb); /* Install the new nullification. */ cond_free(&ctx->null_cond); ctx->null_cond = cond; } -static bool do_sub_reg(DisasContext *ctx, arg_rrr_cf *a, +static bool do_sub_reg(DisasContext *ctx, arg_rrr_cf_d *a, bool is_tsv, bool is_b, bool is_tc) { - TCGv_reg tcg_r1, tcg_r2; + TCGv_i64 tcg_r1, tcg_r2; if (a->cf) { nullify_over(ctx); } tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); - do_sub(ctx, a->t, tcg_r1, tcg_r2, is_tsv, is_b, is_tc, a->cf); + do_sub(ctx, a->t, tcg_r1, tcg_r2, is_tsv, is_b, is_tc, a->cf, a->d); return nullify_end(ctx); } static bool do_sub_imm(DisasContext *ctx, arg_rri_cf *a, bool is_tsv) { - TCGv_reg tcg_im, tcg_r2; + TCGv_i64 tcg_im, tcg_r2; if (a->cf) { nullify_over(ctx); } - tcg_im = load_const(ctx, a->i); + tcg_im = tcg_constant_i64(a->i); tcg_r2 = load_gpr(ctx, a->r); - do_sub(ctx, a->t, tcg_im, tcg_r2, is_tsv, 0, 0, a->cf); + /* All SUBI conditions are 32-bit. */ + do_sub(ctx, a->t, tcg_im, tcg_r2, is_tsv, 0, 0, a->cf, false); return nullify_end(ctx); } -static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf) +static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_i64 in1, + TCGv_i64 in2, unsigned cf, bool d) { - TCGv_reg dest, sv; + TCGv_i64 dest, sv; DisasCond cond; - dest = tcg_temp_new(); - tcg_gen_sub_reg(dest, in1, in2); + dest = tcg_temp_new_i64(); + tcg_gen_sub_i64(dest, in1, in2); /* Compute signed overflow if required. */ sv = NULL; @@ -1319,23 +1277,22 @@ static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, } /* Form the condition for the compare. */ - cond = do_sub_cond(cf, dest, in1, in2, sv); + cond = do_sub_cond(ctx, cf, d, dest, in1, in2, sv); /* Clear. */ - tcg_gen_movi_reg(dest, 0); + tcg_gen_movi_i64(dest, 0); save_gpr(ctx, rt, dest); - tcg_temp_free(dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); ctx->null_cond = cond; } -static void do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf, - void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) +static void do_log(DisasContext *ctx, unsigned rt, TCGv_i64 in1, + TCGv_i64 in2, unsigned cf, bool d, + void (*fn)(TCGv_i64, TCGv_i64, TCGv_i64)) { - TCGv_reg dest = dest_gpr(ctx, rt); + TCGv_i64 dest = dest_gpr(ctx, rt); /* Perform the operation, and writeback. */ fn(dest, in1, in2); @@ -1344,53 +1301,104 @@ static void do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Install the new nullification. */ cond_free(&ctx->null_cond); if (cf) { - ctx->null_cond = do_log_cond(cf, dest); + ctx->null_cond = do_log_cond(ctx, cf, d, dest); } } -static bool do_log_reg(DisasContext *ctx, arg_rrr_cf *a, - void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) +static bool do_log_reg(DisasContext *ctx, arg_rrr_cf_d *a, + void (*fn)(TCGv_i64, TCGv_i64, TCGv_i64)) { - TCGv_reg tcg_r1, tcg_r2; + TCGv_i64 tcg_r1, tcg_r2; if (a->cf) { nullify_over(ctx); } tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); - do_log(ctx, a->t, tcg_r1, tcg_r2, a->cf, fn); + do_log(ctx, a->t, tcg_r1, tcg_r2, a->cf, a->d, fn); return nullify_end(ctx); } -static void do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf, bool is_tc, - void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) +static void do_unit_addsub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, + TCGv_i64 in2, unsigned cf, bool d, + bool is_tc, bool is_add) { - TCGv_reg dest; + TCGv_i64 dest = tcg_temp_new_i64(); + uint64_t test_cb = 0; DisasCond cond; - if (cf == 0) { - dest = dest_gpr(ctx, rt); - fn(dest, in1, in2); - save_gpr(ctx, rt, dest); - cond_free(&ctx->null_cond); - } else { - dest = tcg_temp_new(); - fn(dest, in1, in2); - - cond = do_unit_cond(cf, dest, in1, in2); - - if (is_tc) { - TCGv_reg tmp = tcg_temp_new(); - tcg_gen_setcond_reg(cond.c, tmp, cond.a0, cond.a1); - gen_helper_tcond(cpu_env, tmp); - tcg_temp_free(tmp); + /* Select which carry-out bits to test. */ + switch (cf >> 1) { + case 4: /* NDC / SDC -- 4-bit carries */ + test_cb = dup_const(MO_8, 0x88); + break; + case 5: /* NWC / SWC -- 32-bit carries */ + if (d) { + test_cb = dup_const(MO_32, INT32_MIN); + } else { + cf &= 1; /* undefined -- map to never/always */ } - save_gpr(ctx, rt, dest); - - cond_free(&ctx->null_cond); - ctx->null_cond = cond; + break; + case 6: /* NBC / SBC -- 8-bit carries */ + test_cb = dup_const(MO_8, INT8_MIN); + break; + case 7: /* NHC / SHC -- 16-bit carries */ + test_cb = dup_const(MO_16, INT16_MIN); + break; } + if (!d) { + test_cb = (uint32_t)test_cb; + } + + if (!test_cb) { + /* No need to compute carries if we don't need to test them. */ + if (is_add) { + tcg_gen_add_i64(dest, in1, in2); + } else { + tcg_gen_sub_i64(dest, in1, in2); + } + cond = do_unit_zero_cond(cf, d, dest); + } else { + TCGv_i64 cb = tcg_temp_new_i64(); + + if (d) { + TCGv_i64 cb_msb = tcg_temp_new_i64(); + if (is_add) { + tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero, in2, ctx->zero); + tcg_gen_xor_i64(cb, in1, in2); + } else { + /* See do_sub, !is_b. */ + TCGv_i64 one = tcg_constant_i64(1); + tcg_gen_sub2_i64(dest, cb_msb, in1, one, in2, ctx->zero); + tcg_gen_eqv_i64(cb, in1, in2); + } + tcg_gen_xor_i64(cb, cb, dest); + tcg_gen_extract2_i64(cb, cb, cb_msb, 1); + } else { + if (is_add) { + tcg_gen_add_i64(dest, in1, in2); + tcg_gen_xor_i64(cb, in1, in2); + } else { + tcg_gen_sub_i64(dest, in1, in2); + tcg_gen_eqv_i64(cb, in1, in2); + } + tcg_gen_xor_i64(cb, cb, dest); + tcg_gen_shri_i64(cb, cb, 1); + } + + tcg_gen_andi_i64(cb, cb, test_cb); + cond = cond_make_0_tmp(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, cb); + } + + if (is_tc) { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); + gen_helper_tcond(tcg_env, tmp); + } + save_gpr(ctx, rt, dest); + + cond_free(&ctx->null_cond); + ctx->null_cond = cond; } #ifndef CONFIG_USER_ONLY @@ -1398,17 +1406,17 @@ static void do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1, from the top 2 bits of the base register. There are a few system instructions that have a 3-bit space specifier, for which SR0 is not special. To handle this, pass ~SP. */ -static TCGv_i64 space_select(DisasContext *ctx, int sp, TCGv_reg base) +static TCGv_i64 space_select(DisasContext *ctx, int sp, TCGv_i64 base) { TCGv_ptr ptr; - TCGv_reg tmp; + TCGv_i64 tmp; TCGv_i64 spc; if (sp != 0) { if (sp < 0) { sp = ~sp; } - spc = get_temp_tl(ctx); + spc = tcg_temp_new_i64(); load_spr(ctx, spc, sp); return spc; } @@ -1417,54 +1425,51 @@ static TCGv_i64 space_select(DisasContext *ctx, int sp, TCGv_reg base) } ptr = tcg_temp_new_ptr(); - tmp = tcg_temp_new(); - spc = get_temp_tl(ctx); + tmp = tcg_temp_new_i64(); + spc = tcg_temp_new_i64(); - tcg_gen_shri_reg(tmp, base, TARGET_REGISTER_BITS - 5); - tcg_gen_andi_reg(tmp, tmp, 030); - tcg_gen_trunc_reg_ptr(ptr, tmp); - tcg_temp_free(tmp); + /* Extract top 2 bits of the address, shift left 3 for uint64_t index. */ + tcg_gen_shri_i64(tmp, base, (ctx->tb_flags & PSW_W ? 64 : 32) - 5); + tcg_gen_andi_i64(tmp, tmp, 030); + tcg_gen_trunc_i64_ptr(ptr, tmp); - tcg_gen_add_ptr(ptr, ptr, cpu_env); + tcg_gen_add_ptr(ptr, ptr, tcg_env); tcg_gen_ld_i64(spc, ptr, offsetof(CPUHPPAState, sr[4])); - tcg_temp_free_ptr(ptr); return spc; } #endif -static void form_gva(DisasContext *ctx, TCGv_tl *pgva, TCGv_reg *pofs, - unsigned rb, unsigned rx, int scale, target_sreg disp, +static void form_gva(DisasContext *ctx, TCGv_i64 *pgva, TCGv_i64 *pofs, + unsigned rb, unsigned rx, int scale, int64_t disp, unsigned sp, int modify, bool is_phys) { - TCGv_reg base = load_gpr(ctx, rb); - TCGv_reg ofs; + TCGv_i64 base = load_gpr(ctx, rb); + TCGv_i64 ofs; + TCGv_i64 addr; + + set_insn_breg(ctx, rb); /* Note that RX is mutually exclusive with DISP. */ if (rx) { - ofs = get_temp(ctx); - tcg_gen_shli_reg(ofs, cpu_gr[rx], scale); - tcg_gen_add_reg(ofs, ofs, base); + ofs = tcg_temp_new_i64(); + tcg_gen_shli_i64(ofs, cpu_gr[rx], scale); + tcg_gen_add_i64(ofs, ofs, base); } else if (disp || modify) { - ofs = get_temp(ctx); - tcg_gen_addi_reg(ofs, base, disp); + ofs = tcg_temp_new_i64(); + tcg_gen_addi_i64(ofs, base, disp); } else { ofs = base; } *pofs = ofs; -#ifdef CONFIG_USER_ONLY - *pgva = (modify <= 0 ? ofs : base); -#else - TCGv_tl addr = get_temp_tl(ctx); - tcg_gen_extu_reg_tl(addr, modify <= 0 ? ofs : base); - if (ctx->tb_flags & PSW_W) { - tcg_gen_andi_tl(addr, addr, 0x3fffffffffffffffull); - } + *pgva = addr = tcg_temp_new_i64(); + tcg_gen_andi_i64(addr, modify <= 0 ? ofs : base, + gva_offset_mask(ctx->tb_flags)); +#ifndef CONFIG_USER_ONLY if (!is_phys) { - tcg_gen_or_tl(addr, addr, space_select(ctx, sp, base)); + tcg_gen_or_i64(addr, addr, space_select(ctx, sp, base)); } - *pgva = addr; #endif } @@ -1474,35 +1479,35 @@ static void form_gva(DisasContext *ctx, TCGv_tl *pgva, TCGv_reg *pofs, * = 0 for no base register update. */ static void do_load_32(DisasContext *ctx, TCGv_i32 dest, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { - TCGv_reg ofs; - TCGv_tl addr; + TCGv_i64 ofs; + TCGv_i64 addr; /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, - ctx->mmu_idx == MMU_PHYS_IDX); - tcg_gen_qemu_ld_reg(dest, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); + MMU_DISABLED(ctx)); + tcg_gen_qemu_ld_i32(dest, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); if (modify) { save_gpr(ctx, rb, ofs); } } static void do_load_64(DisasContext *ctx, TCGv_i64 dest, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { - TCGv_reg ofs; - TCGv_tl addr; + TCGv_i64 ofs; + TCGv_i64 addr; /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, - ctx->mmu_idx == MMU_PHYS_IDX); + MMU_DISABLED(ctx)); tcg_gen_qemu_ld_i64(dest, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); if (modify) { save_gpr(ctx, rb, ofs); @@ -1510,17 +1515,17 @@ static void do_load_64(DisasContext *ctx, TCGv_i64 dest, unsigned rb, } static void do_store_32(DisasContext *ctx, TCGv_i32 src, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { - TCGv_reg ofs; - TCGv_tl addr; + TCGv_i64 ofs; + TCGv_i64 addr; /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, - ctx->mmu_idx == MMU_PHYS_IDX); + MMU_DISABLED(ctx)); tcg_gen_qemu_st_i32(src, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); if (modify) { save_gpr(ctx, rb, ofs); @@ -1528,36 +1533,28 @@ static void do_store_32(DisasContext *ctx, TCGv_i32 src, unsigned rb, } static void do_store_64(DisasContext *ctx, TCGv_i64 src, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { - TCGv_reg ofs; - TCGv_tl addr; + TCGv_i64 ofs; + TCGv_i64 addr; /* Caller uses nullify_over/nullify_end. */ assert(ctx->null_cond.c == TCG_COND_NEVER); form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, - ctx->mmu_idx == MMU_PHYS_IDX); + MMU_DISABLED(ctx)); tcg_gen_qemu_st_i64(src, addr, ctx->mmu_idx, mop | UNALIGN(ctx)); if (modify) { save_gpr(ctx, rb, ofs); } } -#if TARGET_REGISTER_BITS == 64 -#define do_load_reg do_load_64 -#define do_store_reg do_store_64 -#else -#define do_load_reg do_load_32 -#define do_store_reg do_store_32 -#endif - static bool do_load(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify, MemOp mop) { - TCGv_reg dest; + TCGv_i64 dest; nullify_over(ctx); @@ -1566,16 +1563,16 @@ static bool do_load(DisasContext *ctx, unsigned rt, unsigned rb, dest = dest_gpr(ctx, rt); } else { /* Make sure if RT == RB, we see the result of the load. */ - dest = get_temp(ctx); + dest = tcg_temp_new_i64(); } - do_load_reg(ctx, dest, rb, rx, scale, disp, sp, modify, mop); + do_load_64(ctx, dest, rb, rx, scale, disp, sp, modify, mop); save_gpr(ctx, rt, dest); return nullify_end(ctx); } static bool do_floadw(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify) { TCGv_i32 tmp; @@ -1585,10 +1582,9 @@ static bool do_floadw(DisasContext *ctx, unsigned rt, unsigned rb, tmp = tcg_temp_new_i32(); do_load_32(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TEUL); save_frw_i32(rt, tmp); - tcg_temp_free_i32(tmp); if (rt == 0) { - gen_helper_loaded_fr0(cpu_env); + gen_helper_loaded_fr0(tcg_env); } return nullify_end(ctx); @@ -1601,7 +1597,7 @@ static bool trans_fldw(DisasContext *ctx, arg_ldst *a) } static bool do_floadd(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify) { TCGv_i64 tmp; @@ -1611,10 +1607,9 @@ static bool do_floadd(DisasContext *ctx, unsigned rt, unsigned rb, tmp = tcg_temp_new_i64(); do_load_64(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TEUQ); save_frd(rt, tmp); - tcg_temp_free_i64(tmp); if (rt == 0) { - gen_helper_loaded_fr0(cpu_env); + gen_helper_loaded_fr0(tcg_env); } return nullify_end(ctx); @@ -1627,16 +1622,16 @@ static bool trans_fldd(DisasContext *ctx, arg_ldst *a) } static bool do_store(DisasContext *ctx, unsigned rt, unsigned rb, - target_sreg disp, unsigned sp, + int64_t disp, unsigned sp, int modify, MemOp mop) { nullify_over(ctx); - do_store_reg(ctx, load_gpr(ctx, rt), rb, 0, 0, disp, sp, modify, mop); + do_store_64(ctx, load_gpr(ctx, rt), rb, 0, 0, disp, sp, modify, mop); return nullify_end(ctx); } static bool do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify) { TCGv_i32 tmp; @@ -1645,7 +1640,6 @@ static bool do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb, tmp = load_frw_i32(rt); do_store_32(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TEUL); - tcg_temp_free_i32(tmp); return nullify_end(ctx); } @@ -1657,7 +1651,7 @@ static bool trans_fstw(DisasContext *ctx, arg_ldst *a) } static bool do_fstored(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, + unsigned rx, int scale, int64_t disp, unsigned sp, int modify) { TCGv_i64 tmp; @@ -1666,7 +1660,6 @@ static bool do_fstored(DisasContext *ctx, unsigned rt, unsigned rb, tmp = load_frd(rt); do_store_64(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TEUQ); - tcg_temp_free_i64(tmp); return nullify_end(ctx); } @@ -1685,10 +1678,9 @@ static bool do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra, nullify_over(ctx); tmp = load_frw0_i32(ra); - func(tmp, cpu_env, tmp); + func(tmp, tcg_env, tmp); save_frw_i32(rt, tmp); - tcg_temp_free_i32(tmp); return nullify_end(ctx); } @@ -1702,11 +1694,9 @@ static bool do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra, src = load_frd(ra); dst = tcg_temp_new_i32(); - func(dst, cpu_env, src); + func(dst, tcg_env, src); - tcg_temp_free_i64(src); save_frw_i32(rt, dst); - tcg_temp_free_i32(dst); return nullify_end(ctx); } @@ -1718,10 +1708,9 @@ static bool do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra, nullify_over(ctx); tmp = load_frd0(ra); - func(tmp, cpu_env, tmp); + func(tmp, tcg_env, tmp); save_frd(rt, tmp); - tcg_temp_free_i64(tmp); return nullify_end(ctx); } @@ -1735,11 +1724,9 @@ static bool do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra, src = load_frw0_i32(ra); dst = tcg_temp_new_i64(); - func(dst, cpu_env, src); + func(dst, tcg_env, src); - tcg_temp_free_i32(src); save_frd(rt, dst); - tcg_temp_free_i64(dst); return nullify_end(ctx); } @@ -1753,11 +1740,9 @@ static bool do_fop_weww(DisasContext *ctx, unsigned rt, a = load_frw0_i32(ra); b = load_frw0_i32(rb); - func(a, cpu_env, a, b); + func(a, tcg_env, a, b); - tcg_temp_free_i32(b); save_frw_i32(rt, a); - tcg_temp_free_i32(a); return nullify_end(ctx); } @@ -1771,22 +1756,20 @@ static bool do_fop_dedd(DisasContext *ctx, unsigned rt, a = load_frd0(ra); b = load_frd0(rb); - func(a, cpu_env, a, b); + func(a, tcg_env, a, b); - tcg_temp_free_i64(b); save_frd(rt, a); - tcg_temp_free_i64(a); return nullify_end(ctx); } /* Emit an unconditional branch to a direct target, which may or may not have already had nullification handled. */ -static bool do_dbranch(DisasContext *ctx, target_ureg dest, +static bool do_dbranch(DisasContext *ctx, uint64_t dest, unsigned link, bool is_n) { if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { if (link != 0) { - copy_iaoq_entry(cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); } ctx->iaoq_n = dest; if (is_n) { @@ -1796,7 +1779,7 @@ static bool do_dbranch(DisasContext *ctx, target_ureg dest, nullify_over(ctx); if (link != 0) { - copy_iaoq_entry(cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); } if (is_n && use_nullify_skip(ctx)) { @@ -1818,10 +1801,10 @@ static bool do_dbranch(DisasContext *ctx, target_ureg dest, /* Emit a conditional branch to a direct target. If the branch itself is nullified, we should have already used nullify_over. */ -static bool do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n, +static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, DisasCond *cond) { - target_ureg dest = iaoq_dest(ctx, disp); + uint64_t dest = iaoq_dest(ctx, disp); TCGLabel *taken = NULL; TCGCond c = cond->c; bool n; @@ -1837,7 +1820,7 @@ static bool do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n, } taken = gen_new_label(); - tcg_gen_brcond_reg(c, cond->a0, cond->a1, taken); + tcg_gen_brcond_i64(c, cond->a0, cond->a1, taken); cond_free(cond); /* Not taken: Condition not satisfied; nullify on backward branches. */ @@ -1854,7 +1837,7 @@ static bool do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n, if (ctx->iaoq_n == -1) { /* The temporary iaoq_n_var died at the branch above. Regenerate it here instead of saving it. */ - tcg_gen_addi_reg(ctx->iaoq_n_var, cpu_iaoq_b, 4); + tcg_gen_addi_i64(ctx->iaoq_n_var, cpu_iaoq_b, 4); } gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n); } @@ -1884,24 +1867,25 @@ static bool do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n, /* Emit an unconditional branch to an indirect target. This handles nullification of the branch itself. */ -static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, +static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, unsigned link, bool is_n) { - TCGv_reg a0, a1, next, tmp; + TCGv_i64 a0, a1, next, tmp; TCGCond c; assert(ctx->null_lab == NULL); if (ctx->null_cond.c == TCG_COND_NEVER) { if (link != 0) { - copy_iaoq_entry(cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); } - next = get_temp(ctx); - tcg_gen_mov_reg(next, dest); + next = tcg_temp_new_i64(); + tcg_gen_mov_i64(next, dest); if (is_n) { if (use_nullify_skip(ctx)) { - tcg_gen_mov_reg(cpu_iaoq_f, next); - tcg_gen_addi_reg(cpu_iaoq_b, next, 4); + copy_iaoq_entry(ctx, cpu_iaoq_f, -1, next); + tcg_gen_addi_i64(next, next, 4); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next); nullify_set(ctx, 0); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; return true; @@ -1923,12 +1907,14 @@ static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, /* We do have to handle the non-local temporary, DEST, before branching. Since IOAQ_F is not really live at this point, we can simply store DEST optimistically. Similarly with IAOQ_B. */ - tcg_gen_mov_reg(cpu_iaoq_f, dest); - tcg_gen_addi_reg(cpu_iaoq_b, dest, 4); + copy_iaoq_entry(ctx, cpu_iaoq_f, -1, dest); + next = tcg_temp_new_i64(); + tcg_gen_addi_i64(next, dest, 4); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next); nullify_over(ctx); if (link != 0) { - tcg_gen_movi_reg(cpu_gr[link], ctx->iaoq_n); + copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); } tcg_gen_lookup_and_goto_ptr(); return nullify_end(ctx); @@ -1937,23 +1923,23 @@ static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, a0 = ctx->null_cond.a0; a1 = ctx->null_cond.a1; - tmp = tcg_temp_new(); - next = get_temp(ctx); + tmp = tcg_temp_new_i64(); + next = tcg_temp_new_i64(); - copy_iaoq_entry(tmp, ctx->iaoq_n, ctx->iaoq_n_var); - tcg_gen_movcond_reg(c, next, a0, a1, tmp, dest); + copy_iaoq_entry(ctx, tmp, ctx->iaoq_n, ctx->iaoq_n_var); + tcg_gen_movcond_i64(c, next, a0, a1, tmp, dest); ctx->iaoq_n = -1; ctx->iaoq_n_var = next; if (link != 0) { - tcg_gen_movcond_reg(c, cpu_gr[link], a0, a1, cpu_gr[link], tmp); + tcg_gen_movcond_i64(c, cpu_gr[link], a0, a1, cpu_gr[link], tmp); } if (is_n) { /* The branch nullifies the next insn, which means the state of N after the branch is the inverse of the state of N that applied to the branch. */ - tcg_gen_setcond_reg(tcg_invert_cond(c), cpu_psw_n, a0, a1); + tcg_gen_setcond_i64(tcg_invert_cond(c), cpu_psw_n, a0, a1); cond_free(&ctx->null_cond); ctx->null_cond = cond_make_n(); ctx->psw_n_nonzero = true; @@ -1971,23 +1957,23 @@ static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, * IAOQ_Next{30..31} ← IAOQ_Front{30..31}; * which keeps the privilege level from being increased. */ -static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset) +static TCGv_i64 do_ibranch_priv(DisasContext *ctx, TCGv_i64 offset) { - TCGv_reg dest; + TCGv_i64 dest; switch (ctx->privilege) { case 0: /* Privilege 0 is maximum and is allowed to decrease. */ return offset; case 3: /* Privilege 3 is minimum and is never allowed to increase. */ - dest = get_temp(ctx); - tcg_gen_ori_reg(dest, offset, 3); + dest = tcg_temp_new_i64(); + tcg_gen_ori_i64(dest, offset, 3); break; default: - dest = get_temp(ctx); - tcg_gen_andi_reg(dest, offset, -4); - tcg_gen_ori_reg(dest, dest, ctx->privilege); - tcg_gen_movcond_reg(TCG_COND_GTU, dest, dest, offset, dest, offset); + dest = tcg_temp_new_i64(); + tcg_gen_andi_i64(dest, offset, -4); + tcg_gen_ori_i64(dest, dest, ctx->privilege); + tcg_gen_movcond_i64(TCG_COND_GTU, dest, dest, offset, dest, offset); break; } return dest; @@ -2003,14 +1989,16 @@ static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset) aforementioned BE. */ static void do_page_zero(DisasContext *ctx) { + TCGv_i64 tmp; + /* If by some means we get here with PSW[N]=1, that implies that the B,GATE instruction would be skipped, and we'd fault on the - next insn within the privilaged page. */ + next insn within the privileged page. */ switch (ctx->null_cond.c) { case TCG_COND_NEVER: break; case TCG_COND_ALWAYS: - tcg_gen_movi_reg(cpu_psw_n, 0); + tcg_gen_movi_i64(cpu_psw_n, 0); goto do_sigill; default: /* Since this is always the first (and only) insn within the @@ -2038,9 +2026,12 @@ static void do_page_zero(DisasContext *ctx) break; case 0xe0: /* SET_THREAD_POINTER */ - tcg_gen_st_reg(cpu_gr[26], cpu_env, offsetof(CPUHPPAState, cr[27])); - tcg_gen_ori_reg(cpu_iaoq_f, cpu_gr[31], 3); - tcg_gen_addi_reg(cpu_iaoq_b, cpu_iaoq_f, 4); + tcg_gen_st_i64(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27])); + tmp = tcg_temp_new_i64(); + tcg_gen_ori_i64(tmp, cpu_gr[31], 3); + copy_iaoq_entry(ctx, cpu_iaoq_f, -1, tmp); + tcg_gen_addi_i64(tmp, tmp, 4); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; break; @@ -2081,8 +2072,8 @@ static bool trans_sync(DisasContext *ctx, arg_sync *a) static bool trans_mfia(DisasContext *ctx, arg_mfia *a) { unsigned rt = a->t; - TCGv_reg tmp = dest_gpr(ctx, rt); - tcg_gen_movi_reg(tmp, ctx->iaoq_f); + TCGv_i64 tmp = dest_gpr(ctx, rt); + tcg_gen_movi_i64(tmp, ctx->iaoq_f & ~3ULL); save_gpr(ctx, rt, tmp); cond_free(&ctx->null_cond); @@ -2094,15 +2085,11 @@ static bool trans_mfsp(DisasContext *ctx, arg_mfsp *a) unsigned rt = a->t; unsigned rs = a->sp; TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_reg t1 = tcg_temp_new(); load_spr(ctx, t0, rs); tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_trunc_i64_reg(t1, t0); - save_gpr(ctx, rt, t1); - tcg_temp_free(t1); - tcg_temp_free_i64(t0); + save_gpr(ctx, rt, t0); cond_free(&ctx->null_cond); return true; @@ -2112,32 +2099,27 @@ static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) { unsigned rt = a->t; unsigned ctl = a->r; - TCGv_reg tmp; + TCGv_i64 tmp; switch (ctl) { case CR_SAR: -#ifdef TARGET_HPPA64 if (a->e == 0) { /* MFSAR without ,W masks low 5 bits. */ tmp = dest_gpr(ctx, rt); - tcg_gen_andi_reg(tmp, cpu_sar, 31); + tcg_gen_andi_i64(tmp, cpu_sar, 31); save_gpr(ctx, rt, tmp); goto done; } -#endif save_gpr(ctx, rt, cpu_sar); goto done; case CR_IT: /* Interval Timer */ /* FIXME: Respect PSW_S bit. */ nullify_over(ctx); tmp = dest_gpr(ctx, rt); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - gen_helper_read_interval_timer(tmp); + if (translator_io_start(&ctx->base)) { ctx->base.is_jmp = DISAS_IAQ_N_STALE; - } else { - gen_helper_read_interval_timer(tmp); } + gen_helper_read_interval_timer(tmp); save_gpr(ctx, rt, tmp); return nullify_end(ctx); case 26: @@ -2149,8 +2131,8 @@ static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) break; } - tmp = get_temp(ctx); - tcg_gen_ld_reg(tmp, cpu_env, offsetof(CPUHPPAState, cr[ctl])); + tmp = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUHPPAState, cr[ctl])); save_gpr(ctx, rt, tmp); done: @@ -2162,24 +2144,22 @@ static bool trans_mtsp(DisasContext *ctx, arg_mtsp *a) { unsigned rr = a->r; unsigned rs = a->sp; - TCGv_i64 t64; + TCGv_i64 tmp; if (rs >= 5) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_REG); } nullify_over(ctx); - t64 = tcg_temp_new_i64(); - tcg_gen_extu_reg_i64(t64, load_gpr(ctx, rr)); - tcg_gen_shli_i64(t64, t64, 32); + tmp = tcg_temp_new_i64(); + tcg_gen_shli_i64(tmp, load_gpr(ctx, rr), 32); if (rs >= 4) { - tcg_gen_st_i64(t64, cpu_env, offsetof(CPUHPPAState, sr[rs])); + tcg_gen_st_i64(tmp, tcg_env, offsetof(CPUHPPAState, sr[rs])); ctx->tb_flags &= ~TB_FLAG_SR_SAME; } else { - tcg_gen_mov_i64(cpu_sr[rs], t64); + tcg_gen_mov_i64(cpu_sr[rs], tmp); } - tcg_temp_free_i64(t64); return nullify_end(ctx); } @@ -2187,15 +2167,14 @@ static bool trans_mtsp(DisasContext *ctx, arg_mtsp *a) static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) { unsigned ctl = a->t; - TCGv_reg reg; - TCGv_reg tmp; + TCGv_i64 reg; + TCGv_i64 tmp; if (ctl == CR_SAR) { reg = load_gpr(ctx, a->r); - tmp = tcg_temp_new(); - tcg_gen_andi_reg(tmp, reg, TARGET_REGISTER_BITS - 1); + tmp = tcg_temp_new_i64(); + tcg_gen_andi_i64(tmp, reg, ctx->is_pa20 ? 63 : 31); save_or_nullify(ctx, cpu_sar, tmp); - tcg_temp_free(tmp); cond_free(&ctx->null_cond); return true; @@ -2206,17 +2185,26 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) #ifndef CONFIG_USER_ONLY nullify_over(ctx); - reg = load_gpr(ctx, a->r); + + if (ctx->is_pa20) { + reg = load_gpr(ctx, a->r); + } else { + reg = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(reg, load_gpr(ctx, a->r)); + } switch (ctl) { case CR_IT: - gen_helper_write_interval_timer(cpu_env, reg); + if (translator_io_start(&ctx->base)) { + ctx->base.is_jmp = DISAS_IAQ_N_STALE; + } + gen_helper_write_interval_timer(tcg_env, reg); break; case CR_EIRR: - gen_helper_write_eirr(cpu_env, reg); - break; - case CR_EIEM: - gen_helper_write_eiem(cpu_env, reg); + /* Helper modifies interrupt lines and is therefore IO. */ + translator_io_start(&ctx->base); + gen_helper_write_eirr(tcg_env, reg); + /* Exit to re-evaluate interrupts in the main loop. */ ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; break; @@ -2224,11 +2212,11 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) case CR_IIAOQ: /* FIXME: Respect PSW_Q bit */ /* The write advances the queue and stores to the back element. */ - tmp = get_temp(ctx); - tcg_gen_ld_reg(tmp, cpu_env, + tmp = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUHPPAState, cr_back[ctl - CR_IIASQ])); - tcg_gen_st_reg(tmp, cpu_env, offsetof(CPUHPPAState, cr[ctl])); - tcg_gen_st_reg(reg, cpu_env, + tcg_gen_st_i64(tmp, tcg_env, offsetof(CPUHPPAState, cr[ctl])); + tcg_gen_st_i64(reg, tcg_env, offsetof(CPUHPPAState, cr_back[ctl - CR_IIASQ])); break; @@ -2236,14 +2224,18 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) case CR_PID2: case CR_PID3: case CR_PID4: - tcg_gen_st_reg(reg, cpu_env, offsetof(CPUHPPAState, cr[ctl])); + tcg_gen_st_i64(reg, tcg_env, offsetof(CPUHPPAState, cr[ctl])); #ifndef CONFIG_USER_ONLY - gen_helper_change_prot_id(cpu_env); + gen_helper_change_prot_id(tcg_env); #endif break; + case CR_EIEM: + /* Exit to re-evaluate interrupts in the main loop. */ + ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; + /* FALLTHRU */ default: - tcg_gen_st_reg(reg, cpu_env, offsetof(CPUHPPAState, cr[ctl])); + tcg_gen_st_i64(reg, tcg_env, offsetof(CPUHPPAState, cr[ctl])); break; } return nullify_end(ctx); @@ -2252,12 +2244,11 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) static bool trans_mtsarcm(DisasContext *ctx, arg_mtsarcm *a) { - TCGv_reg tmp = tcg_temp_new(); + TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_not_reg(tmp, load_gpr(ctx, a->r)); - tcg_gen_andi_reg(tmp, tmp, TARGET_REGISTER_BITS - 1); + tcg_gen_not_i64(tmp, load_gpr(ctx, a->r)); + tcg_gen_andi_i64(tmp, tmp, ctx->is_pa20 ? 63 : 31); save_or_nullify(ctx, cpu_sar, tmp); - tcg_temp_free(tmp); cond_free(&ctx->null_cond); return true; @@ -2265,19 +2256,14 @@ static bool trans_mtsarcm(DisasContext *ctx, arg_mtsarcm *a) static bool trans_ldsid(DisasContext *ctx, arg_ldsid *a) { - TCGv_reg dest = dest_gpr(ctx, a->t); + TCGv_i64 dest = dest_gpr(ctx, a->t); #ifdef CONFIG_USER_ONLY /* We don't implement space registers in user mode. */ - tcg_gen_movi_reg(dest, 0); + tcg_gen_movi_i64(dest, 0); #else - TCGv_i64 t0 = tcg_temp_new_i64(); - - tcg_gen_mov_i64(t0, space_select(ctx, a->sp, load_gpr(ctx, a->b))); - tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_trunc_i64_reg(dest, t0); - - tcg_temp_free_i64(t0); + tcg_gen_mov_i64(dest, space_select(ctx, a->sp, load_gpr(ctx, a->b))); + tcg_gen_shri_i64(dest, dest, 32); #endif save_gpr(ctx, a->t, dest); @@ -2287,16 +2273,22 @@ static bool trans_ldsid(DisasContext *ctx, arg_ldsid *a) static bool trans_rsm(DisasContext *ctx, arg_rsm *a) { +#ifdef CONFIG_USER_ONLY CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); -#ifndef CONFIG_USER_ONLY - TCGv_reg tmp; +#else + TCGv_i64 tmp; + + /* HP-UX 11i and HP ODE use rsm for read-access to PSW */ + if (a->i) { + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); + } nullify_over(ctx); - tmp = get_temp(ctx); - tcg_gen_ld_reg(tmp, cpu_env, offsetof(CPUHPPAState, psw)); - tcg_gen_andi_reg(tmp, tmp, ~a->i); - gen_helper_swap_system_mask(tmp, cpu_env, tmp); + tmp = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUHPPAState, psw)); + tcg_gen_andi_i64(tmp, tmp, ~a->i); + gen_helper_swap_system_mask(tmp, tcg_env, tmp); save_gpr(ctx, a->t, tmp); /* Exit the TB to recognize new interrupts, e.g. PSW_M. */ @@ -2309,14 +2301,14 @@ static bool trans_ssm(DisasContext *ctx, arg_ssm *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY - TCGv_reg tmp; + TCGv_i64 tmp; nullify_over(ctx); - tmp = get_temp(ctx); - tcg_gen_ld_reg(tmp, cpu_env, offsetof(CPUHPPAState, psw)); - tcg_gen_ori_reg(tmp, tmp, a->i); - gen_helper_swap_system_mask(tmp, cpu_env, tmp); + tmp = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUHPPAState, psw)); + tcg_gen_ori_i64(tmp, tmp, a->i); + gen_helper_swap_system_mask(tmp, tcg_env, tmp); save_gpr(ctx, a->t, tmp); /* Exit the TB to recognize new interrupts, e.g. PSW_I. */ @@ -2329,12 +2321,12 @@ static bool trans_mtsm(DisasContext *ctx, arg_mtsm *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY - TCGv_reg tmp, reg; + TCGv_i64 tmp, reg; nullify_over(ctx); reg = load_gpr(ctx, a->r); - tmp = get_temp(ctx); - gen_helper_swap_system_mask(tmp, cpu_env, reg); + tmp = tcg_temp_new_i64(); + gen_helper_swap_system_mask(tmp, tcg_env, reg); /* Exit the TB to recognize new interrupts. */ ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; @@ -2349,9 +2341,9 @@ static bool do_rfi(DisasContext *ctx, bool rfi_r) nullify_over(ctx); if (rfi_r) { - gen_helper_rfi_r(cpu_env); + gen_helper_rfi_r(tcg_env); } else { - gen_helper_rfi(cpu_env); + gen_helper_rfi(tcg_env); } /* Exit the TB to recognize new interrupts. */ tcg_gen_exit_tb(NULL, 0); @@ -2376,7 +2368,7 @@ static bool trans_halt(DisasContext *ctx, arg_halt *a) CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY nullify_over(ctx); - gen_helper_halt(cpu_env); + gen_helper_halt(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; return nullify_end(ctx); #endif @@ -2387,42 +2379,72 @@ static bool trans_reset(DisasContext *ctx, arg_reset *a) CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY nullify_over(ctx); - gen_helper_reset(cpu_env); + gen_helper_reset(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; return nullify_end(ctx); #endif } -static bool trans_getshadowregs(DisasContext *ctx, arg_getshadowregs *a) +static bool do_getshadowregs(DisasContext *ctx) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); -#ifndef CONFIG_USER_ONLY nullify_over(ctx); - gen_helper_getshadowregs(cpu_env); + tcg_gen_ld_i64(cpu_gr[1], tcg_env, offsetof(CPUHPPAState, shadow[0])); + tcg_gen_ld_i64(cpu_gr[8], tcg_env, offsetof(CPUHPPAState, shadow[1])); + tcg_gen_ld_i64(cpu_gr[9], tcg_env, offsetof(CPUHPPAState, shadow[2])); + tcg_gen_ld_i64(cpu_gr[16], tcg_env, offsetof(CPUHPPAState, shadow[3])); + tcg_gen_ld_i64(cpu_gr[17], tcg_env, offsetof(CPUHPPAState, shadow[4])); + tcg_gen_ld_i64(cpu_gr[24], tcg_env, offsetof(CPUHPPAState, shadow[5])); + tcg_gen_ld_i64(cpu_gr[25], tcg_env, offsetof(CPUHPPAState, shadow[6])); return nullify_end(ctx); -#endif +} + +static bool do_putshadowregs(DisasContext *ctx) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); + nullify_over(ctx); + tcg_gen_st_i64(cpu_gr[1], tcg_env, offsetof(CPUHPPAState, shadow[0])); + tcg_gen_st_i64(cpu_gr[8], tcg_env, offsetof(CPUHPPAState, shadow[1])); + tcg_gen_st_i64(cpu_gr[9], tcg_env, offsetof(CPUHPPAState, shadow[2])); + tcg_gen_st_i64(cpu_gr[16], tcg_env, offsetof(CPUHPPAState, shadow[3])); + tcg_gen_st_i64(cpu_gr[17], tcg_env, offsetof(CPUHPPAState, shadow[4])); + tcg_gen_st_i64(cpu_gr[24], tcg_env, offsetof(CPUHPPAState, shadow[5])); + tcg_gen_st_i64(cpu_gr[25], tcg_env, offsetof(CPUHPPAState, shadow[6])); + return nullify_end(ctx); +} + +static bool trans_getshadowregs(DisasContext *ctx, arg_getshadowregs *a) +{ + return do_getshadowregs(ctx); } static bool trans_nop_addrx(DisasContext *ctx, arg_ldst *a) { if (a->m) { - TCGv_reg dest = dest_gpr(ctx, a->b); - TCGv_reg src1 = load_gpr(ctx, a->b); - TCGv_reg src2 = load_gpr(ctx, a->x); + TCGv_i64 dest = dest_gpr(ctx, a->b); + TCGv_i64 src1 = load_gpr(ctx, a->b); + TCGv_i64 src2 = load_gpr(ctx, a->x); /* The only thing we need to do is the base register modification. */ - tcg_gen_add_reg(dest, src1, src2); + tcg_gen_add_i64(dest, src1, src2); save_gpr(ctx, a->b, dest); } cond_free(&ctx->null_cond); return true; } +static bool trans_fic(DisasContext *ctx, arg_ldst *a) +{ + /* End TB for flush instruction cache, so we pick up new insns. */ + ctx->base.is_jmp = DISAS_IAQ_N_STALE; + return trans_nop_addrx(ctx, a); +} + static bool trans_probe(DisasContext *ctx, arg_probe *a) { - TCGv_reg dest, ofs; + TCGv_i64 dest, ofs; TCGv_i32 level, want; - TCGv_tl addr; + TCGv_i64 addr; nullify_over(ctx); @@ -2430,17 +2452,15 @@ static bool trans_probe(DisasContext *ctx, arg_probe *a) form_gva(ctx, &addr, &ofs, a->b, 0, 0, 0, a->sp, 0, false); if (a->imm) { - level = tcg_constant_i32(a->ri); + level = tcg_constant_i32(a->ri & 3); } else { level = tcg_temp_new_i32(); - tcg_gen_trunc_reg_i32(level, load_gpr(ctx, a->ri)); + tcg_gen_extrl_i64_i32(level, load_gpr(ctx, a->ri)); tcg_gen_andi_i32(level, level, 3); } want = tcg_constant_i32(a->write ? PAGE_WRITE : PAGE_READ); - gen_helper_probe(dest, cpu_env, addr, level, want); - - tcg_temp_free_i32(level); + gen_helper_probe(dest, tcg_env, addr, level, want); save_gpr(ctx, a->t, dest); return nullify_end(ctx); @@ -2448,19 +2468,22 @@ static bool trans_probe(DisasContext *ctx, arg_probe *a) static bool trans_ixtlbx(DisasContext *ctx, arg_ixtlbx *a) { + if (ctx->is_pa20) { + return false; + } CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY - TCGv_tl addr; - TCGv_reg ofs, reg; + TCGv_i64 addr; + TCGv_i64 ofs, reg; nullify_over(ctx); form_gva(ctx, &addr, &ofs, a->b, 0, 0, 0, a->sp, 0, false); reg = load_gpr(ctx, a->r); if (a->addr) { - gen_helper_itlba(cpu_env, addr, reg); + gen_helper_itlba_pa11(tcg_env, addr, reg); } else { - gen_helper_itlbp(cpu_env, addr, reg); + gen_helper_itlbp_pa11(tcg_env, addr, reg); } /* Exit TB for TLB change if mmu is enabled. */ @@ -2471,24 +2494,62 @@ static bool trans_ixtlbx(DisasContext *ctx, arg_ixtlbx *a) #endif } -static bool trans_pxtlbx(DisasContext *ctx, arg_pxtlbx *a) +static bool do_pxtlb(DisasContext *ctx, arg_ldst *a, bool local) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY - TCGv_tl addr; - TCGv_reg ofs; + TCGv_i64 addr; + TCGv_i64 ofs; nullify_over(ctx); form_gva(ctx, &addr, &ofs, a->b, a->x, 0, 0, a->sp, a->m, false); + + /* + * Page align now, rather than later, so that we can add in the + * page_size field from pa2.0 from the low 4 bits of GR[b]. + */ + tcg_gen_andi_i64(addr, addr, TARGET_PAGE_MASK); + if (ctx->is_pa20) { + tcg_gen_deposit_i64(addr, addr, load_gpr(ctx, a->b), 0, 4); + } + + if (local) { + gen_helper_ptlb_l(tcg_env, addr); + } else { + gen_helper_ptlb(tcg_env, addr); + } + if (a->m) { save_gpr(ctx, a->b, ofs); } - if (a->local) { - gen_helper_ptlbe(cpu_env); - } else { - gen_helper_ptlb(cpu_env, addr); + + /* Exit TB for TLB change if mmu is enabled. */ + if (ctx->tb_flags & PSW_C) { + ctx->base.is_jmp = DISAS_IAQ_N_STALE; } + return nullify_end(ctx); +#endif +} + +static bool trans_pxtlb(DisasContext *ctx, arg_ldst *a) +{ + return do_pxtlb(ctx, a, false); +} + +static bool trans_pxtlb_l(DisasContext *ctx, arg_ldst *a) +{ + return ctx->is_pa20 && do_pxtlb(ctx, a, true); +} + +static bool trans_pxtlbe(DisasContext *ctx, arg_ldst *a) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY + nullify_over(ctx); + + trans_nop_addrx(ctx, a); + gen_helper_ptlbe(tcg_env); /* Exit TB for TLB change if mmu is enabled. */ if (ctx->tb_flags & PSW_C) { @@ -2506,10 +2567,13 @@ static bool trans_pxtlbx(DisasContext *ctx, arg_pxtlbx *a) */ static bool trans_ixtlbxf(DisasContext *ctx, arg_ixtlbxf *a) { + if (ctx->is_pa20) { + return false; + } CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY - TCGv_tl addr, atl, stl; - TCGv_reg reg; + TCGv_i64 addr, atl, stl; + TCGv_i64 reg; nullify_over(ctx); @@ -2517,32 +2581,27 @@ static bool trans_ixtlbxf(DisasContext *ctx, arg_ixtlbxf *a) * FIXME: * if (not (pcxl or pcxl2)) * return gen_illegal(ctx); - * - * Note for future: these are 32-bit systems; no hppa64. */ - atl = tcg_temp_new_tl(); - stl = tcg_temp_new_tl(); - addr = tcg_temp_new_tl(); + atl = tcg_temp_new_i64(); + stl = tcg_temp_new_i64(); + addr = tcg_temp_new_i64(); - tcg_gen_ld32u_i64(stl, cpu_env, + tcg_gen_ld32u_i64(stl, tcg_env, a->data ? offsetof(CPUHPPAState, cr[CR_ISR]) : offsetof(CPUHPPAState, cr[CR_IIASQ])); - tcg_gen_ld32u_i64(atl, cpu_env, + tcg_gen_ld32u_i64(atl, tcg_env, a->data ? offsetof(CPUHPPAState, cr[CR_IOR]) : offsetof(CPUHPPAState, cr[CR_IIAOQ])); tcg_gen_shli_i64(stl, stl, 32); - tcg_gen_or_tl(addr, atl, stl); - tcg_temp_free_tl(atl); - tcg_temp_free_tl(stl); + tcg_gen_or_i64(addr, atl, stl); reg = load_gpr(ctx, a->r); if (a->addr) { - gen_helper_itlba(cpu_env, addr, reg); + gen_helper_itlba_pa11(tcg_env, addr, reg); } else { - gen_helper_itlbp(cpu_env, addr, reg); + gen_helper_itlbp_pa11(tcg_env, addr, reg); } - tcg_temp_free_tl(addr); /* Exit TB for TLB change if mmu is enabled. */ if (ctx->tb_flags & PSW_C) { @@ -2552,26 +2611,51 @@ static bool trans_ixtlbxf(DisasContext *ctx, arg_ixtlbxf *a) #endif } +static bool trans_ixtlbt(DisasContext *ctx, arg_ixtlbt *a) +{ + if (!ctx->is_pa20) { + return false; + } + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY + nullify_over(ctx); + { + TCGv_i64 src1 = load_gpr(ctx, a->r1); + TCGv_i64 src2 = load_gpr(ctx, a->r2); + + if (a->data) { + gen_helper_idtlbt_pa20(tcg_env, src1, src2); + } else { + gen_helper_iitlbt_pa20(tcg_env, src1, src2); + } + } + /* Exit TB for TLB change if mmu is enabled. */ + if (ctx->tb_flags & PSW_C) { + ctx->base.is_jmp = DISAS_IAQ_N_STALE; + } + return nullify_end(ctx); +#endif +} + static bool trans_lpa(DisasContext *ctx, arg_ldst *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY - TCGv_tl vaddr; - TCGv_reg ofs, paddr; + TCGv_i64 vaddr; + TCGv_i64 ofs, paddr; nullify_over(ctx); form_gva(ctx, &vaddr, &ofs, a->b, a->x, 0, 0, a->sp, a->m, false); - paddr = tcg_temp_new(); - gen_helper_lpa(paddr, cpu_env, vaddr); + paddr = tcg_temp_new_i64(); + gen_helper_lpa(paddr, tcg_env, vaddr); /* Note that physical address result overrides base modification. */ if (a->m) { save_gpr(ctx, a->b, ofs); } save_gpr(ctx, a->t, paddr); - tcg_temp_free(paddr); return nullify_end(ctx); #endif @@ -2585,78 +2669,78 @@ static bool trans_lci(DisasContext *ctx, arg_lci *a) physical address. Two addresses with the same CI have a coherent view of the cache. Our implementation is to return 0 for all, since the entire address space is coherent. */ - save_gpr(ctx, a->t, tcg_constant_reg(0)); + save_gpr(ctx, a->t, ctx->zero); cond_free(&ctx->null_cond); return true; } -static bool trans_add(DisasContext *ctx, arg_rrr_cf_sh *a) +static bool trans_add(DisasContext *ctx, arg_rrr_cf_d_sh *a) { return do_add_reg(ctx, a, false, false, false, false); } -static bool trans_add_l(DisasContext *ctx, arg_rrr_cf_sh *a) +static bool trans_add_l(DisasContext *ctx, arg_rrr_cf_d_sh *a) { return do_add_reg(ctx, a, true, false, false, false); } -static bool trans_add_tsv(DisasContext *ctx, arg_rrr_cf_sh *a) +static bool trans_add_tsv(DisasContext *ctx, arg_rrr_cf_d_sh *a) { return do_add_reg(ctx, a, false, true, false, false); } -static bool trans_add_c(DisasContext *ctx, arg_rrr_cf_sh *a) +static bool trans_add_c(DisasContext *ctx, arg_rrr_cf_d_sh *a) { return do_add_reg(ctx, a, false, false, false, true); } -static bool trans_add_c_tsv(DisasContext *ctx, arg_rrr_cf_sh *a) +static bool trans_add_c_tsv(DisasContext *ctx, arg_rrr_cf_d_sh *a) { return do_add_reg(ctx, a, false, true, false, true); } -static bool trans_sub(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_sub(DisasContext *ctx, arg_rrr_cf_d *a) { return do_sub_reg(ctx, a, false, false, false); } -static bool trans_sub_tsv(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_sub_tsv(DisasContext *ctx, arg_rrr_cf_d *a) { return do_sub_reg(ctx, a, true, false, false); } -static bool trans_sub_tc(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_sub_tc(DisasContext *ctx, arg_rrr_cf_d *a) { return do_sub_reg(ctx, a, false, false, true); } -static bool trans_sub_tsv_tc(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_sub_tsv_tc(DisasContext *ctx, arg_rrr_cf_d *a) { return do_sub_reg(ctx, a, true, false, true); } -static bool trans_sub_b(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_sub_b(DisasContext *ctx, arg_rrr_cf_d *a) { return do_sub_reg(ctx, a, false, true, false); } -static bool trans_sub_b_tsv(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_sub_b_tsv(DisasContext *ctx, arg_rrr_cf_d *a) { return do_sub_reg(ctx, a, true, true, false); } -static bool trans_andcm(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_andcm(DisasContext *ctx, arg_rrr_cf_d *a) { - return do_log_reg(ctx, a, tcg_gen_andc_reg); + return do_log_reg(ctx, a, tcg_gen_andc_i64); } -static bool trans_and(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_and(DisasContext *ctx, arg_rrr_cf_d *a) { - return do_log_reg(ctx, a, tcg_gen_and_reg); + return do_log_reg(ctx, a, tcg_gen_and_i64); } -static bool trans_or(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) { if (a->cf == 0) { unsigned r2 = a->r2; @@ -2669,8 +2753,8 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf *a) } if (r2 == 0) { /* COPY */ if (r1 == 0) { - TCGv_reg dest = dest_gpr(ctx, rt); - tcg_gen_movi_reg(dest, 0); + TCGv_i64 dest = dest_gpr(ctx, rt); + tcg_gen_movi_i64(dest, 0); save_gpr(ctx, rt, dest); } else { save_gpr(ctx, rt, cpu_gr[r1]); @@ -2691,12 +2775,12 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf *a) nullify_over(ctx); /* Advance the instruction queue. */ - copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); - copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); nullify_set(ctx, 0); /* Tell the qemu main loop to halt until this cpu has work. */ - tcg_gen_st_i32(tcg_constant_i32(1), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(1), tcg_env, offsetof(CPUState, halted) - offsetof(HPPACPU, env)); gen_excp_1(EXCP_HALTED); ctx->base.is_jmp = DISAS_NORETURN; @@ -2705,150 +2789,181 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf *a) } #endif } - return do_log_reg(ctx, a, tcg_gen_or_reg); + return do_log_reg(ctx, a, tcg_gen_or_i64); } -static bool trans_xor(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_xor(DisasContext *ctx, arg_rrr_cf_d *a) { - return do_log_reg(ctx, a, tcg_gen_xor_reg); + return do_log_reg(ctx, a, tcg_gen_xor_i64); } -static bool trans_cmpclr(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_cmpclr(DisasContext *ctx, arg_rrr_cf_d *a) { - TCGv_reg tcg_r1, tcg_r2; + TCGv_i64 tcg_r1, tcg_r2; if (a->cf) { nullify_over(ctx); } tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); - do_cmpclr(ctx, a->t, tcg_r1, tcg_r2, a->cf); + do_cmpclr(ctx, a->t, tcg_r1, tcg_r2, a->cf, a->d); return nullify_end(ctx); } -static bool trans_uxor(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_uxor(DisasContext *ctx, arg_rrr_cf_d *a) { - TCGv_reg tcg_r1, tcg_r2; + TCGv_i64 tcg_r1, tcg_r2, dest; if (a->cf) { nullify_over(ctx); } + tcg_r1 = load_gpr(ctx, a->r1); tcg_r2 = load_gpr(ctx, a->r2); - do_unit(ctx, a->t, tcg_r1, tcg_r2, a->cf, false, tcg_gen_xor_reg); - return nullify_end(ctx); -} + dest = dest_gpr(ctx, a->t); -static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf *a, bool is_tc) -{ - TCGv_reg tcg_r1, tcg_r2, tmp; + tcg_gen_xor_i64(dest, tcg_r1, tcg_r2); + save_gpr(ctx, a->t, dest); + cond_free(&ctx->null_cond); if (a->cf) { - nullify_over(ctx); + ctx->null_cond = do_unit_zero_cond(a->cf, a->d, dest); } - tcg_r1 = load_gpr(ctx, a->r1); - tcg_r2 = load_gpr(ctx, a->r2); - tmp = get_temp(ctx); - tcg_gen_not_reg(tmp, tcg_r2); - do_unit(ctx, a->t, tcg_r1, tmp, a->cf, is_tc, tcg_gen_add_reg); + return nullify_end(ctx); } -static bool trans_uaddcm(DisasContext *ctx, arg_rrr_cf *a) +static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf_d *a, bool is_tc) +{ + TCGv_i64 tcg_r1, tcg_r2, tmp; + + if (a->cf == 0) { + tcg_r2 = load_gpr(ctx, a->r2); + tmp = dest_gpr(ctx, a->t); + + if (a->r1 == 0) { + /* UADDCM r0,src,dst is the common idiom for dst = ~src. */ + tcg_gen_not_i64(tmp, tcg_r2); + } else { + /* + * Recall that r1 - r2 == r1 + ~r2 + 1. + * Thus r1 + ~r2 == r1 - r2 - 1, + * which does not require an extra temporary. + */ + tcg_r1 = load_gpr(ctx, a->r1); + tcg_gen_sub_i64(tmp, tcg_r1, tcg_r2); + tcg_gen_subi_i64(tmp, tmp, 1); + } + save_gpr(ctx, a->t, tmp); + cond_free(&ctx->null_cond); + return true; + } + + nullify_over(ctx); + tcg_r1 = load_gpr(ctx, a->r1); + tcg_r2 = load_gpr(ctx, a->r2); + tmp = tcg_temp_new_i64(); + tcg_gen_not_i64(tmp, tcg_r2); + do_unit_addsub(ctx, a->t, tcg_r1, tmp, a->cf, a->d, is_tc, true); + return nullify_end(ctx); +} + +static bool trans_uaddcm(DisasContext *ctx, arg_rrr_cf_d *a) { return do_uaddcm(ctx, a, false); } -static bool trans_uaddcm_tc(DisasContext *ctx, arg_rrr_cf *a) +static bool trans_uaddcm_tc(DisasContext *ctx, arg_rrr_cf_d *a) { return do_uaddcm(ctx, a, true); } -static bool do_dcor(DisasContext *ctx, arg_rr_cf *a, bool is_i) +static bool do_dcor(DisasContext *ctx, arg_rr_cf_d *a, bool is_i) { - TCGv_reg tmp; + TCGv_i64 tmp; nullify_over(ctx); - tmp = get_temp(ctx); - tcg_gen_shri_reg(tmp, cpu_psw_cb, 3); + tmp = tcg_temp_new_i64(); + tcg_gen_extract2_i64(tmp, cpu_psw_cb, cpu_psw_cb_msb, 4); if (!is_i) { - tcg_gen_not_reg(tmp, tmp); + tcg_gen_not_i64(tmp, tmp); } - tcg_gen_andi_reg(tmp, tmp, 0x11111111); - tcg_gen_muli_reg(tmp, tmp, 6); - do_unit(ctx, a->t, load_gpr(ctx, a->r), tmp, a->cf, false, - is_i ? tcg_gen_add_reg : tcg_gen_sub_reg); + tcg_gen_andi_i64(tmp, tmp, (uint64_t)0x1111111111111111ull); + tcg_gen_muli_i64(tmp, tmp, 6); + do_unit_addsub(ctx, a->t, load_gpr(ctx, a->r), tmp, + a->cf, a->d, false, is_i); return nullify_end(ctx); } -static bool trans_dcor(DisasContext *ctx, arg_rr_cf *a) +static bool trans_dcor(DisasContext *ctx, arg_rr_cf_d *a) { return do_dcor(ctx, a, false); } -static bool trans_dcor_i(DisasContext *ctx, arg_rr_cf *a) +static bool trans_dcor_i(DisasContext *ctx, arg_rr_cf_d *a) { return do_dcor(ctx, a, true); } static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) { - TCGv_reg dest, add1, add2, addc, zero, in1, in2; + TCGv_i64 dest, add1, add2, addc, in1, in2; nullify_over(ctx); in1 = load_gpr(ctx, a->r1); in2 = load_gpr(ctx, a->r2); - add1 = tcg_temp_new(); - add2 = tcg_temp_new(); - addc = tcg_temp_new(); - dest = tcg_temp_new(); - zero = tcg_constant_reg(0); + add1 = tcg_temp_new_i64(); + add2 = tcg_temp_new_i64(); + addc = tcg_temp_new_i64(); + dest = tcg_temp_new_i64(); /* Form R1 << 1 | PSW[CB]{8}. */ - tcg_gen_add_reg(add1, in1, in1); - tcg_gen_add_reg(add1, add1, cpu_psw_cb_msb); + tcg_gen_add_i64(add1, in1, in1); + tcg_gen_add_i64(add1, add1, get_psw_carry(ctx, false)); - /* Add or subtract R2, depending on PSW[V]. Proper computation of - carry{8} requires that we subtract via + ~R2 + 1, as described in - the manual. By extracting and masking V, we can produce the - proper inputs to the addition without movcond. */ - tcg_gen_sari_reg(addc, cpu_psw_v, TARGET_REGISTER_BITS - 1); - tcg_gen_xor_reg(add2, in2, addc); - tcg_gen_andi_reg(addc, addc, 1); - /* ??? This is only correct for 32-bit. */ - tcg_gen_add2_i32(dest, cpu_psw_cb_msb, add1, zero, add2, zero); - tcg_gen_add2_i32(dest, cpu_psw_cb_msb, dest, cpu_psw_cb_msb, addc, zero); + /* + * Add or subtract R2, depending on PSW[V]. Proper computation of + * carry requires that we subtract via + ~R2 + 1, as described in + * the manual. By extracting and masking V, we can produce the + * proper inputs to the addition without movcond. + */ + tcg_gen_sextract_i64(addc, cpu_psw_v, 31, 1); + tcg_gen_xor_i64(add2, in2, addc); + tcg_gen_andi_i64(addc, addc, 1); - tcg_temp_free(addc); + tcg_gen_add2_i64(dest, cpu_psw_cb_msb, add1, ctx->zero, add2, ctx->zero); + tcg_gen_add2_i64(dest, cpu_psw_cb_msb, dest, cpu_psw_cb_msb, + addc, ctx->zero); /* Write back the result register. */ save_gpr(ctx, a->t, dest); /* Write back PSW[CB]. */ - tcg_gen_xor_reg(cpu_psw_cb, add1, add2); - tcg_gen_xor_reg(cpu_psw_cb, cpu_psw_cb, dest); + tcg_gen_xor_i64(cpu_psw_cb, add1, add2); + tcg_gen_xor_i64(cpu_psw_cb, cpu_psw_cb, dest); - /* Write back PSW[V] for the division step. */ - tcg_gen_neg_reg(cpu_psw_v, cpu_psw_cb_msb); - tcg_gen_xor_reg(cpu_psw_v, cpu_psw_v, in2); + /* + * Write back PSW[V] for the division step. + * Shift cb{8} from where it lives in bit 32 to bit 31, + * so that it overlaps r2{32} in bit 31. + */ + tcg_gen_shri_i64(cpu_psw_v, cpu_psw_cb, 1); + tcg_gen_xor_i64(cpu_psw_v, cpu_psw_v, in2); /* Install the new nullification. */ if (a->cf) { - TCGv_reg sv = NULL; + TCGv_i64 sv = NULL, uv = NULL; if (cond_need_sv(a->cf >> 1)) { - /* ??? The lshift is supposed to contribute to overflow. */ - sv = do_add_sv(ctx, dest, add1, add2); + sv = do_add_sv(ctx, dest, add1, add2, in1, 1, false); + } else if (cond_need_cb(a->cf >> 1)) { + uv = do_add_uv(ctx, cpu_psw_cb, NULL, in1, 1, false); } - ctx->null_cond = do_cond(a->cf, dest, cpu_psw_cb_msb, sv); + ctx->null_cond = do_cond(ctx, a->cf, false, dest, uv, sv); } - tcg_temp_free(add1); - tcg_temp_free(add2); - tcg_temp_free(dest); - return nullify_end(ctx); } @@ -2882,23 +2997,241 @@ static bool trans_subi_tsv(DisasContext *ctx, arg_rri_cf *a) return do_sub_imm(ctx, a, true); } -static bool trans_cmpiclr(DisasContext *ctx, arg_rri_cf *a) +static bool trans_cmpiclr(DisasContext *ctx, arg_rri_cf_d *a) { - TCGv_reg tcg_im, tcg_r2; + TCGv_i64 tcg_im, tcg_r2; if (a->cf) { nullify_over(ctx); } - tcg_im = load_const(ctx, a->i); + tcg_im = tcg_constant_i64(a->i); tcg_r2 = load_gpr(ctx, a->r); - do_cmpclr(ctx, a->t, tcg_im, tcg_r2, a->cf); + do_cmpclr(ctx, a->t, tcg_im, tcg_r2, a->cf, a->d); return nullify_end(ctx); } +static bool do_multimedia(DisasContext *ctx, arg_rrr *a, + void (*fn)(TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 r1, r2, dest; + + if (!ctx->is_pa20) { + return false; + } + + nullify_over(ctx); + + r1 = load_gpr(ctx, a->r1); + r2 = load_gpr(ctx, a->r2); + dest = dest_gpr(ctx, a->t); + + fn(dest, r1, r2); + save_gpr(ctx, a->t, dest); + + return nullify_end(ctx); +} + +static bool do_multimedia_sh(DisasContext *ctx, arg_rri *a, + void (*fn)(TCGv_i64, TCGv_i64, int64_t)) +{ + TCGv_i64 r, dest; + + if (!ctx->is_pa20) { + return false; + } + + nullify_over(ctx); + + r = load_gpr(ctx, a->r); + dest = dest_gpr(ctx, a->t); + + fn(dest, r, a->i); + save_gpr(ctx, a->t, dest); + + return nullify_end(ctx); +} + +static bool do_multimedia_shadd(DisasContext *ctx, arg_rrr_sh *a, + void (*fn)(TCGv_i64, TCGv_i64, + TCGv_i64, TCGv_i32)) +{ + TCGv_i64 r1, r2, dest; + + if (!ctx->is_pa20) { + return false; + } + + nullify_over(ctx); + + r1 = load_gpr(ctx, a->r1); + r2 = load_gpr(ctx, a->r2); + dest = dest_gpr(ctx, a->t); + + fn(dest, r1, r2, tcg_constant_i32(a->sh)); + save_gpr(ctx, a->t, dest); + + return nullify_end(ctx); +} + +static bool trans_hadd(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, tcg_gen_vec_add16_i64); +} + +static bool trans_hadd_ss(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_helper_hadd_ss); +} + +static bool trans_hadd_us(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_helper_hadd_us); +} + +static bool trans_havg(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_helper_havg); +} + +static bool trans_hshl(DisasContext *ctx, arg_rri *a) +{ + return do_multimedia_sh(ctx, a, tcg_gen_vec_shl16i_i64); +} + +static bool trans_hshr_s(DisasContext *ctx, arg_rri *a) +{ + return do_multimedia_sh(ctx, a, tcg_gen_vec_sar16i_i64); +} + +static bool trans_hshr_u(DisasContext *ctx, arg_rri *a) +{ + return do_multimedia_sh(ctx, a, tcg_gen_vec_shr16i_i64); +} + +static bool trans_hshladd(DisasContext *ctx, arg_rrr_sh *a) +{ + return do_multimedia_shadd(ctx, a, gen_helper_hshladd); +} + +static bool trans_hshradd(DisasContext *ctx, arg_rrr_sh *a) +{ + return do_multimedia_shadd(ctx, a, gen_helper_hshradd); +} + +static bool trans_hsub(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, tcg_gen_vec_sub16_i64); +} + +static bool trans_hsub_ss(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_helper_hsub_ss); +} + +static bool trans_hsub_us(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_helper_hsub_us); +} + +static void gen_mixh_l(TCGv_i64 dst, TCGv_i64 r1, TCGv_i64 r2) +{ + uint64_t mask = 0xffff0000ffff0000ull; + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_andi_i64(tmp, r2, mask); + tcg_gen_andi_i64(dst, r1, mask); + tcg_gen_shri_i64(tmp, tmp, 16); + tcg_gen_or_i64(dst, dst, tmp); +} + +static bool trans_mixh_l(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_mixh_l); +} + +static void gen_mixh_r(TCGv_i64 dst, TCGv_i64 r1, TCGv_i64 r2) +{ + uint64_t mask = 0x0000ffff0000ffffull; + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_andi_i64(tmp, r1, mask); + tcg_gen_andi_i64(dst, r2, mask); + tcg_gen_shli_i64(tmp, tmp, 16); + tcg_gen_or_i64(dst, dst, tmp); +} + +static bool trans_mixh_r(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_mixh_r); +} + +static void gen_mixw_l(TCGv_i64 dst, TCGv_i64 r1, TCGv_i64 r2) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_shri_i64(tmp, r2, 32); + tcg_gen_deposit_i64(dst, r1, tmp, 0, 32); +} + +static bool trans_mixw_l(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_mixw_l); +} + +static void gen_mixw_r(TCGv_i64 dst, TCGv_i64 r1, TCGv_i64 r2) +{ + tcg_gen_deposit_i64(dst, r2, r1, 32, 32); +} + +static bool trans_mixw_r(DisasContext *ctx, arg_rrr *a) +{ + return do_multimedia(ctx, a, gen_mixw_r); +} + +static bool trans_permh(DisasContext *ctx, arg_permh *a) +{ + TCGv_i64 r, t0, t1, t2, t3; + + if (!ctx->is_pa20) { + return false; + } + + nullify_over(ctx); + + r = load_gpr(ctx, a->r1); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + t3 = tcg_temp_new_i64(); + + tcg_gen_extract_i64(t0, r, (3 - a->c0) * 16, 16); + tcg_gen_extract_i64(t1, r, (3 - a->c1) * 16, 16); + tcg_gen_extract_i64(t2, r, (3 - a->c2) * 16, 16); + tcg_gen_extract_i64(t3, r, (3 - a->c3) * 16, 16); + + tcg_gen_deposit_i64(t0, t1, t0, 16, 48); + tcg_gen_deposit_i64(t2, t3, t2, 16, 48); + tcg_gen_deposit_i64(t0, t2, t0, 32, 32); + + save_gpr(ctx, a->t, t0); + return nullify_end(ctx); +} + static bool trans_ld(DisasContext *ctx, arg_ldst *a) { + if (ctx->is_pa20) { + /* + * With pa20, LDB, LDH, LDW, LDD to %g0 are prefetches. + * Any base modification still occurs. + */ + if (a->t == 0) { + return trans_nop_addrx(ctx, a); + } + } else if (a->size > MO_32) { + return gen_illegal(ctx); + } return do_load(ctx, a->t, a->b, a->x, a->scale ? a->size : 0, a->disp, a->sp, a->m, a->size | MO_TE); } @@ -2906,27 +3239,34 @@ static bool trans_ld(DisasContext *ctx, arg_ldst *a) static bool trans_st(DisasContext *ctx, arg_ldst *a) { assert(a->x == 0 && a->scale == 0); + if (!ctx->is_pa20 && a->size > MO_32) { + return gen_illegal(ctx); + } return do_store(ctx, a->t, a->b, a->disp, a->sp, a->m, a->size | MO_TE); } static bool trans_ldc(DisasContext *ctx, arg_ldst *a) { MemOp mop = MO_TE | MO_ALIGN | a->size; - TCGv_reg zero, dest, ofs; - TCGv_tl addr; + TCGv_i64 dest, ofs; + TCGv_i64 addr; + + if (!ctx->is_pa20 && a->size > MO_32) { + return gen_illegal(ctx); + } nullify_over(ctx); if (a->m) { /* Base register modification. Make sure if RT == RB, we see the result of the load. */ - dest = get_temp(ctx); + dest = tcg_temp_new_i64(); } else { dest = dest_gpr(ctx, a->t); } - 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); + form_gva(ctx, &addr, &ofs, a->b, a->x, a->scale ? 3 : 0, + a->disp, a->sp, a->m, MMU_DISABLED(ctx)); /* * For hppa1.1, LDCW is undefined unless aligned mod 16. @@ -2938,8 +3278,7 @@ static bool trans_ldc(DisasContext *ctx, arg_ldst *a) */ gen_helper_ldc_check(addr); - zero = tcg_constant_reg(0); - tcg_gen_atomic_xchg_reg(dest, addr, zero, ctx->mmu_idx, mop); + tcg_gen_atomic_xchg_i64(dest, addr, ctx->zero, ctx->mmu_idx, mop); if (a->m) { save_gpr(ctx, a->b, ofs); @@ -2951,29 +3290,63 @@ static bool trans_ldc(DisasContext *ctx, arg_ldst *a) static bool trans_stby(DisasContext *ctx, arg_stby *a) { - TCGv_reg ofs, val; - TCGv_tl addr; + TCGv_i64 ofs, val; + TCGv_i64 addr; nullify_over(ctx); form_gva(ctx, &addr, &ofs, a->b, 0, 0, a->disp, a->sp, a->m, - ctx->mmu_idx == MMU_PHYS_IDX); + MMU_DISABLED(ctx)); val = load_gpr(ctx, a->r); if (a->a) { if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { - gen_helper_stby_e_parallel(cpu_env, addr, val); + gen_helper_stby_e_parallel(tcg_env, addr, val); } else { - gen_helper_stby_e(cpu_env, addr, val); + gen_helper_stby_e(tcg_env, addr, val); } } else { if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { - gen_helper_stby_b_parallel(cpu_env, addr, val); + gen_helper_stby_b_parallel(tcg_env, addr, val); } else { - gen_helper_stby_b(cpu_env, addr, val); + gen_helper_stby_b(tcg_env, addr, val); } } if (a->m) { - tcg_gen_andi_reg(ofs, ofs, ~3); + tcg_gen_andi_i64(ofs, ofs, ~3); + save_gpr(ctx, a->b, ofs); + } + + return nullify_end(ctx); +} + +static bool trans_stdby(DisasContext *ctx, arg_stby *a) +{ + TCGv_i64 ofs, val; + TCGv_i64 addr; + + if (!ctx->is_pa20) { + return false; + } + nullify_over(ctx); + + form_gva(ctx, &addr, &ofs, a->b, 0, 0, a->disp, a->sp, a->m, + MMU_DISABLED(ctx)); + val = load_gpr(ctx, a->r); + if (a->a) { + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + gen_helper_stdby_e_parallel(tcg_env, addr, val); + } else { + gen_helper_stdby_e(tcg_env, addr, val); + } + } else { + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + gen_helper_stdby_b_parallel(tcg_env, addr, val); + } else { + gen_helper_stdby_b(tcg_env, addr, val); + } + } + if (a->m) { + tcg_gen_andi_i64(ofs, ofs, ~7); save_gpr(ctx, a->b, ofs); } @@ -2985,7 +3358,7 @@ static bool trans_lda(DisasContext *ctx, arg_ldst *a) int hold_mmu_idx = ctx->mmu_idx; CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); - ctx->mmu_idx = MMU_PHYS_IDX; + ctx->mmu_idx = ctx->tb_flags & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX; trans_ld(ctx, a); ctx->mmu_idx = hold_mmu_idx; return true; @@ -2996,7 +3369,7 @@ static bool trans_sta(DisasContext *ctx, arg_ldst *a) int hold_mmu_idx = ctx->mmu_idx; CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); - ctx->mmu_idx = MMU_PHYS_IDX; + ctx->mmu_idx = ctx->tb_flags & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX; trans_st(ctx, a); ctx->mmu_idx = hold_mmu_idx; return true; @@ -3004,9 +3377,9 @@ static bool trans_sta(DisasContext *ctx, arg_ldst *a) static bool trans_ldil(DisasContext *ctx, arg_ldil *a) { - TCGv_reg tcg_rt = dest_gpr(ctx, a->t); + TCGv_i64 tcg_rt = dest_gpr(ctx, a->t); - tcg_gen_movi_reg(tcg_rt, a->i); + tcg_gen_movi_i64(tcg_rt, a->i); save_gpr(ctx, a->t, tcg_rt); cond_free(&ctx->null_cond); return true; @@ -3014,10 +3387,10 @@ static bool trans_ldil(DisasContext *ctx, arg_ldil *a) static bool trans_addil(DisasContext *ctx, arg_addil *a) { - TCGv_reg tcg_rt = load_gpr(ctx, a->r); - TCGv_reg tcg_r1 = dest_gpr(ctx, 1); + TCGv_i64 tcg_rt = load_gpr(ctx, a->r); + TCGv_i64 tcg_r1 = dest_gpr(ctx, 1); - tcg_gen_addi_reg(tcg_r1, tcg_rt, a->i); + tcg_gen_addi_i64(tcg_r1, tcg_rt, a->i); save_gpr(ctx, 1, tcg_r1); cond_free(&ctx->null_cond); return true; @@ -3025,77 +3398,101 @@ static bool trans_addil(DisasContext *ctx, arg_addil *a) static bool trans_ldo(DisasContext *ctx, arg_ldo *a) { - TCGv_reg tcg_rt = dest_gpr(ctx, a->t); + TCGv_i64 tcg_rt = dest_gpr(ctx, a->t); /* Special case rb == 0, for the LDI pseudo-op. - The COPY pseudo-op is handled for free within tcg_gen_addi_tl. */ + The COPY pseudo-op is handled for free within tcg_gen_addi_i64. */ if (a->b == 0) { - tcg_gen_movi_reg(tcg_rt, a->i); + tcg_gen_movi_i64(tcg_rt, a->i); } else { - tcg_gen_addi_reg(tcg_rt, cpu_gr[a->b], a->i); + tcg_gen_addi_i64(tcg_rt, cpu_gr[a->b], a->i); } save_gpr(ctx, a->t, tcg_rt); cond_free(&ctx->null_cond); return true; } -static bool do_cmpb(DisasContext *ctx, unsigned r, TCGv_reg in1, - unsigned c, unsigned f, unsigned n, int disp) +static bool do_cmpb(DisasContext *ctx, unsigned r, TCGv_i64 in1, + unsigned c, unsigned f, bool d, unsigned n, int disp) { - TCGv_reg dest, in2, sv; + TCGv_i64 dest, in2, sv; DisasCond cond; in2 = load_gpr(ctx, r); - dest = get_temp(ctx); + dest = tcg_temp_new_i64(); - tcg_gen_sub_reg(dest, in1, in2); + tcg_gen_sub_i64(dest, in1, in2); sv = NULL; if (cond_need_sv(c)) { sv = do_sub_sv(ctx, dest, in1, in2); } - cond = do_sub_cond(c * 2 + f, dest, in1, in2, sv); + cond = do_sub_cond(ctx, c * 2 + f, d, dest, in1, in2, sv); return do_cbranch(ctx, disp, n, &cond); } static bool trans_cmpb(DisasContext *ctx, arg_cmpb *a) { + if (!ctx->is_pa20 && a->d) { + return false; + } nullify_over(ctx); - return do_cmpb(ctx, a->r2, load_gpr(ctx, a->r1), a->c, a->f, a->n, a->disp); + return do_cmpb(ctx, a->r2, load_gpr(ctx, a->r1), + a->c, a->f, a->d, a->n, a->disp); } static bool trans_cmpbi(DisasContext *ctx, arg_cmpbi *a) { + if (!ctx->is_pa20 && a->d) { + return false; + } nullify_over(ctx); - return do_cmpb(ctx, a->r, load_const(ctx, a->i), a->c, a->f, a->n, a->disp); + return do_cmpb(ctx, a->r, tcg_constant_i64(a->i), + a->c, a->f, a->d, a->n, a->disp); } -static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1, +static bool do_addb(DisasContext *ctx, unsigned r, TCGv_i64 in1, unsigned c, unsigned f, unsigned n, int disp) { - TCGv_reg dest, in2, sv, cb_msb; + TCGv_i64 dest, in2, sv, cb_cond; DisasCond cond; + bool d = false; + + /* + * For hppa64, the ADDB conditions change with PSW.W, + * dropping ZNV, SV, OD in favor of double-word EQ, LT, LE. + */ + if (ctx->tb_flags & PSW_W) { + d = c >= 5; + if (d) { + c &= 3; + } + } in2 = load_gpr(ctx, r); - dest = tcg_temp_new(); + dest = tcg_temp_new_i64(); sv = NULL; - cb_msb = NULL; + cb_cond = NULL; if (cond_need_cb(c)) { - cb_msb = get_temp(ctx); - tcg_gen_movi_reg(cb_msb, 0); - tcg_gen_add2_reg(dest, cb_msb, in1, cb_msb, in2, cb_msb); + TCGv_i64 cb = tcg_temp_new_i64(); + TCGv_i64 cb_msb = tcg_temp_new_i64(); + + tcg_gen_movi_i64(cb_msb, 0); + tcg_gen_add2_i64(dest, cb_msb, in1, cb_msb, in2, cb_msb); + tcg_gen_xor_i64(cb, in1, in2); + tcg_gen_xor_i64(cb, cb, dest); + cb_cond = get_carry(ctx, d, cb, cb_msb); } else { - tcg_gen_add_reg(dest, in1, in2); + tcg_gen_add_i64(dest, in1, in2); } if (cond_need_sv(c)) { - sv = do_add_sv(ctx, dest, in1, in2); + sv = do_add_sv(ctx, dest, in1, in2, in1, 0, d); } - cond = do_cond(c * 2 + f, dest, cb_msb, sv); + cond = do_cond(ctx, c * 2 + f, d, dest, cb_cond, sv); save_gpr(ctx, r, dest); - tcg_temp_free(dest); return do_cbranch(ctx, disp, n, &cond); } @@ -3108,222 +3505,289 @@ static bool trans_addb(DisasContext *ctx, arg_addb *a) static bool trans_addbi(DisasContext *ctx, arg_addbi *a) { nullify_over(ctx); - return do_addb(ctx, a->r, load_const(ctx, a->i), a->c, a->f, a->n, a->disp); + return do_addb(ctx, a->r, tcg_constant_i64(a->i), a->c, a->f, a->n, a->disp); } static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) { - TCGv_reg tmp, tcg_r; + TCGv_i64 tmp, tcg_r; DisasCond cond; nullify_over(ctx); - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_r = load_gpr(ctx, a->r); - tcg_gen_shl_reg(tmp, tcg_r, cpu_sar); + if (a->d) { + tcg_gen_shl_i64(tmp, tcg_r, cpu_sar); + } else { + /* Force shift into [32,63] */ + tcg_gen_ori_i64(tmp, cpu_sar, 32); + tcg_gen_shl_i64(tmp, tcg_r, tmp); + } - cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); - tcg_temp_free(tmp); + cond = cond_make_0_tmp(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); return do_cbranch(ctx, a->disp, a->n, &cond); } static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a) { - TCGv_reg tmp, tcg_r; + TCGv_i64 tmp, tcg_r; DisasCond cond; + int p; nullify_over(ctx); - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); tcg_r = load_gpr(ctx, a->r); - tcg_gen_shli_reg(tmp, tcg_r, a->p); + p = a->p | (a->d ? 0 : 32); + tcg_gen_shli_i64(tmp, tcg_r, p); cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); - tcg_temp_free(tmp); return do_cbranch(ctx, a->disp, a->n, &cond); } static bool trans_movb(DisasContext *ctx, arg_movb *a) { - TCGv_reg dest; + TCGv_i64 dest; DisasCond cond; nullify_over(ctx); dest = dest_gpr(ctx, a->r2); if (a->r1 == 0) { - tcg_gen_movi_reg(dest, 0); + tcg_gen_movi_i64(dest, 0); } else { - tcg_gen_mov_reg(dest, cpu_gr[a->r1]); + tcg_gen_mov_i64(dest, cpu_gr[a->r1]); } - cond = do_sed_cond(a->c, dest); + /* All MOVB conditions are 32-bit. */ + cond = do_sed_cond(ctx, a->c, false, dest); return do_cbranch(ctx, a->disp, a->n, &cond); } static bool trans_movbi(DisasContext *ctx, arg_movbi *a) { - TCGv_reg dest; + TCGv_i64 dest; DisasCond cond; nullify_over(ctx); dest = dest_gpr(ctx, a->r); - tcg_gen_movi_reg(dest, a->i); + tcg_gen_movi_i64(dest, a->i); - cond = do_sed_cond(a->c, dest); + /* All MOVBI conditions are 32-bit. */ + cond = do_sed_cond(ctx, a->c, false, dest); return do_cbranch(ctx, a->disp, a->n, &cond); } -static bool trans_shrpw_sar(DisasContext *ctx, arg_shrpw_sar *a) +static bool trans_shrp_sar(DisasContext *ctx, arg_shrp_sar *a) { - TCGv_reg dest; + TCGv_i64 dest, src2; + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } dest = dest_gpr(ctx, a->t); + src2 = load_gpr(ctx, a->r2); if (a->r1 == 0) { - tcg_gen_ext32u_reg(dest, load_gpr(ctx, a->r2)); - tcg_gen_shr_reg(dest, dest, cpu_sar); + if (a->d) { + tcg_gen_shr_i64(dest, src2, cpu_sar); + } else { + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_ext32u_i64(dest, src2); + tcg_gen_andi_i64(tmp, cpu_sar, 31); + tcg_gen_shr_i64(dest, dest, tmp); + } } else if (a->r1 == a->r2) { - TCGv_i32 t32 = tcg_temp_new_i32(); - tcg_gen_trunc_reg_i32(t32, load_gpr(ctx, a->r2)); - tcg_gen_rotr_i32(t32, t32, cpu_sar); - tcg_gen_extu_i32_reg(dest, t32); - tcg_temp_free_i32(t32); + if (a->d) { + tcg_gen_rotr_i64(dest, src2, cpu_sar); + } else { + TCGv_i32 t32 = tcg_temp_new_i32(); + TCGv_i32 s32 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(t32, src2); + tcg_gen_extrl_i64_i32(s32, cpu_sar); + tcg_gen_andi_i32(s32, s32, 31); + tcg_gen_rotr_i32(t32, t32, s32); + tcg_gen_extu_i32_i64(dest, t32); + } } else { - TCGv_i64 t = tcg_temp_new_i64(); - TCGv_i64 s = tcg_temp_new_i64(); + TCGv_i64 src1 = load_gpr(ctx, a->r1); - tcg_gen_concat_reg_i64(t, load_gpr(ctx, a->r2), load_gpr(ctx, a->r1)); - tcg_gen_extu_reg_i64(s, cpu_sar); - tcg_gen_shr_i64(t, t, s); - tcg_gen_trunc_i64_reg(dest, t); + if (a->d) { + TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 n = tcg_temp_new_i64(); - tcg_temp_free_i64(t); - tcg_temp_free_i64(s); + tcg_gen_xori_i64(n, cpu_sar, 63); + tcg_gen_shl_i64(t, src1, n); + tcg_gen_shli_i64(t, t, 1); + tcg_gen_shr_i64(dest, src2, cpu_sar); + tcg_gen_or_i64(dest, dest, t); + } else { + TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 s = tcg_temp_new_i64(); + + tcg_gen_concat32_i64(t, src2, src1); + tcg_gen_andi_i64(s, cpu_sar, 31); + tcg_gen_shr_i64(dest, t, s); + } } save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); } return nullify_end(ctx); } -static bool trans_shrpw_imm(DisasContext *ctx, arg_shrpw_imm *a) +static bool trans_shrp_imm(DisasContext *ctx, arg_shrp_imm *a) { - unsigned sa = 31 - a->cpos; - TCGv_reg dest, t2; + unsigned width, sa; + TCGv_i64 dest, t2; + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } + width = a->d ? 64 : 32; + sa = width - 1 - a->cpos; + dest = dest_gpr(ctx, a->t); t2 = load_gpr(ctx, a->r2); if (a->r1 == 0) { - tcg_gen_extract_reg(dest, t2, sa, 32 - sa); - } else if (TARGET_REGISTER_BITS == 32) { - tcg_gen_extract2_reg(dest, t2, cpu_gr[a->r1], sa); - } else if (a->r1 == a->r2) { - TCGv_i32 t32 = tcg_temp_new_i32(); - tcg_gen_trunc_reg_i32(t32, t2); - tcg_gen_rotri_i32(t32, t32, sa); - tcg_gen_extu_i32_reg(dest, t32); - tcg_temp_free_i32(t32); + tcg_gen_extract_i64(dest, t2, sa, width - sa); + } else if (width == TARGET_LONG_BITS) { + tcg_gen_extract2_i64(dest, t2, cpu_gr[a->r1], sa); } else { - TCGv_i64 t64 = tcg_temp_new_i64(); - tcg_gen_concat_reg_i64(t64, t2, cpu_gr[a->r1]); - tcg_gen_shri_i64(t64, t64, sa); - tcg_gen_trunc_i64_reg(dest, t64); - tcg_temp_free_i64(t64); + assert(!a->d); + if (a->r1 == a->r2) { + TCGv_i32 t32 = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t32, t2); + tcg_gen_rotri_i32(t32, t32, sa); + tcg_gen_extu_i32_i64(dest, t32); + } else { + tcg_gen_concat32_i64(dest, t2, cpu_gr[a->r1]); + tcg_gen_extract_i64(dest, dest, sa, 32); + } } save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); } return nullify_end(ctx); } -static bool trans_extrw_sar(DisasContext *ctx, arg_extrw_sar *a) +static bool trans_extr_sar(DisasContext *ctx, arg_extr_sar *a) { - unsigned len = 32 - a->clen; - TCGv_reg dest, src, tmp; + unsigned widthm1 = a->d ? 63 : 31; + TCGv_i64 dest, src, tmp; + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } dest = dest_gpr(ctx, a->t); src = load_gpr(ctx, a->r); - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i64(); /* Recall that SAR is using big-endian bit numbering. */ - tcg_gen_xori_reg(tmp, cpu_sar, TARGET_REGISTER_BITS - 1); + tcg_gen_andi_i64(tmp, cpu_sar, widthm1); + tcg_gen_xori_i64(tmp, tmp, widthm1); + if (a->se) { - tcg_gen_sar_reg(dest, src, tmp); - tcg_gen_sextract_reg(dest, dest, 0, len); + if (!a->d) { + tcg_gen_ext32s_i64(dest, src); + src = dest; + } + tcg_gen_sar_i64(dest, src, tmp); + tcg_gen_sextract_i64(dest, dest, 0, a->len); } else { - tcg_gen_shr_reg(dest, src, tmp); - tcg_gen_extract_reg(dest, dest, 0, len); + if (!a->d) { + tcg_gen_ext32u_i64(dest, src); + src = dest; + } + tcg_gen_shr_i64(dest, src, tmp); + tcg_gen_extract_i64(dest, dest, 0, a->len); } - tcg_temp_free(tmp); save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); } return nullify_end(ctx); } -static bool trans_extrw_imm(DisasContext *ctx, arg_extrw_imm *a) +static bool trans_extr_imm(DisasContext *ctx, arg_extr_imm *a) { - unsigned len = 32 - a->clen; - unsigned cpos = 31 - a->pos; - TCGv_reg dest, src; + unsigned len, cpos, width; + TCGv_i64 dest, src; + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } + len = a->len; + width = a->d ? 64 : 32; + cpos = width - 1 - a->pos; + if (cpos + len > width) { + len = width - cpos; + } + dest = dest_gpr(ctx, a->t); src = load_gpr(ctx, a->r); if (a->se) { - tcg_gen_sextract_reg(dest, src, cpos, len); + tcg_gen_sextract_i64(dest, src, cpos, len); } else { - tcg_gen_extract_reg(dest, src, cpos, len); + tcg_gen_extract_i64(dest, src, cpos, len); } save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); } return nullify_end(ctx); } -static bool trans_depwi_imm(DisasContext *ctx, arg_depwi_imm *a) +static bool trans_depi_imm(DisasContext *ctx, arg_depi_imm *a) { - unsigned len = 32 - a->clen; - target_sreg mask0, mask1; - TCGv_reg dest; + unsigned len, width; + uint64_t mask0, mask1; + TCGv_i64 dest; + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } - if (a->cpos + len > 32) { - len = 32 - a->cpos; + + len = a->len; + width = a->d ? 64 : 32; + if (a->cpos + len > width) { + len = width - a->cpos; } dest = dest_gpr(ctx, a->t); @@ -3331,112 +3795,122 @@ static bool trans_depwi_imm(DisasContext *ctx, arg_depwi_imm *a) mask1 = deposit64(-1, a->cpos, len, a->i); if (a->nz) { - TCGv_reg src = load_gpr(ctx, a->t); - if (mask1 != -1) { - tcg_gen_andi_reg(dest, src, mask1); - src = dest; - } - tcg_gen_ori_reg(dest, src, mask0); + TCGv_i64 src = load_gpr(ctx, a->t); + tcg_gen_andi_i64(dest, src, mask1); + tcg_gen_ori_i64(dest, dest, mask0); } else { - tcg_gen_movi_reg(dest, mask0); + tcg_gen_movi_i64(dest, mask0); } save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); } return nullify_end(ctx); } -static bool trans_depw_imm(DisasContext *ctx, arg_depw_imm *a) +static bool trans_dep_imm(DisasContext *ctx, arg_dep_imm *a) { unsigned rs = a->nz ? a->t : 0; - unsigned len = 32 - a->clen; - TCGv_reg dest, val; + unsigned len, width; + TCGv_i64 dest, val; + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } - if (a->cpos + len > 32) { - len = 32 - a->cpos; + + len = a->len; + width = a->d ? 64 : 32; + if (a->cpos + len > width) { + len = width - a->cpos; } dest = dest_gpr(ctx, a->t); val = load_gpr(ctx, a->r); if (rs == 0) { - tcg_gen_deposit_z_reg(dest, val, a->cpos, len); + tcg_gen_deposit_z_i64(dest, val, a->cpos, len); } else { - tcg_gen_deposit_reg(dest, cpu_gr[rs], val, a->cpos, len); + tcg_gen_deposit_i64(dest, cpu_gr[rs], val, a->cpos, len); } save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); if (a->c) { - ctx->null_cond = do_sed_cond(a->c, dest); + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); } return nullify_end(ctx); } -static bool do_depw_sar(DisasContext *ctx, unsigned rt, unsigned c, - unsigned nz, unsigned clen, TCGv_reg val) +static bool do_dep_sar(DisasContext *ctx, unsigned rt, unsigned c, + bool d, bool nz, unsigned len, TCGv_i64 val) { unsigned rs = nz ? rt : 0; - unsigned len = 32 - clen; - TCGv_reg mask, tmp, shift, dest; - unsigned msb = 1U << (len - 1); + unsigned widthm1 = d ? 63 : 31; + TCGv_i64 mask, tmp, shift, dest; + uint64_t msb = 1ULL << (len - 1); dest = dest_gpr(ctx, rt); - shift = tcg_temp_new(); - tmp = tcg_temp_new(); + shift = tcg_temp_new_i64(); + tmp = tcg_temp_new_i64(); /* Convert big-endian bit numbering in SAR to left-shift. */ - tcg_gen_xori_reg(shift, cpu_sar, TARGET_REGISTER_BITS - 1); + tcg_gen_andi_i64(shift, cpu_sar, widthm1); + tcg_gen_xori_i64(shift, shift, widthm1); - mask = tcg_const_reg(msb + (msb - 1)); - tcg_gen_and_reg(tmp, val, mask); + mask = tcg_temp_new_i64(); + tcg_gen_movi_i64(mask, msb + (msb - 1)); + tcg_gen_and_i64(tmp, val, mask); if (rs) { - tcg_gen_shl_reg(mask, mask, shift); - tcg_gen_shl_reg(tmp, tmp, shift); - tcg_gen_andc_reg(dest, cpu_gr[rs], mask); - tcg_gen_or_reg(dest, dest, tmp); + tcg_gen_shl_i64(mask, mask, shift); + tcg_gen_shl_i64(tmp, tmp, shift); + tcg_gen_andc_i64(dest, cpu_gr[rs], mask); + tcg_gen_or_i64(dest, dest, tmp); } else { - tcg_gen_shl_reg(dest, tmp, shift); + tcg_gen_shl_i64(dest, tmp, shift); } - tcg_temp_free(shift); - tcg_temp_free(mask); - tcg_temp_free(tmp); save_gpr(ctx, rt, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); if (c) { - ctx->null_cond = do_sed_cond(c, dest); + ctx->null_cond = do_sed_cond(ctx, c, d, dest); } return nullify_end(ctx); } -static bool trans_depw_sar(DisasContext *ctx, arg_depw_sar *a) +static bool trans_dep_sar(DisasContext *ctx, arg_dep_sar *a) { + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } - return do_depw_sar(ctx, a->t, a->c, a->nz, a->clen, load_gpr(ctx, a->r)); + return do_dep_sar(ctx, a->t, a->c, a->d, a->nz, a->len, + load_gpr(ctx, a->r)); } -static bool trans_depwi_sar(DisasContext *ctx, arg_depwi_sar *a) +static bool trans_depi_sar(DisasContext *ctx, arg_depi_sar *a) { + if (!ctx->is_pa20 && a->d) { + return false; + } if (a->c) { nullify_over(ctx); } - return do_depw_sar(ctx, a->t, a->c, a->nz, a->clen, load_const(ctx, a->i)); + return do_dep_sar(ctx, a->t, a->c, a->d, a->nz, a->len, + tcg_constant_i64(a->i)); } static bool trans_be(DisasContext *ctx, arg_be *a) { - TCGv_reg tmp; + TCGv_i64 tmp; #ifdef CONFIG_USER_ONLY /* ??? It seems like there should be a good way of using @@ -3454,8 +3928,8 @@ static bool trans_be(DisasContext *ctx, arg_be *a) nullify_over(ctx); #endif - tmp = get_temp(ctx); - tcg_gen_addi_reg(tmp, load_gpr(ctx, a->b), a->disp); + tmp = tcg_temp_new_i64(); + tcg_gen_addi_i64(tmp, load_gpr(ctx, a->b), a->disp); tmp = do_ibranch_priv(ctx, tmp); #ifdef CONFIG_USER_ONLY @@ -3465,24 +3939,25 @@ static bool trans_be(DisasContext *ctx, arg_be *a) load_spr(ctx, new_spc, a->sp); if (a->l) { - copy_iaoq_entry(cpu_gr[31], ctx->iaoq_n, ctx->iaoq_n_var); - tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_f); + copy_iaoq_entry(ctx, cpu_gr[31], ctx->iaoq_n, ctx->iaoq_n_var); + tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_b); } if (a->n && use_nullify_skip(ctx)) { - tcg_gen_mov_reg(cpu_iaoq_f, tmp); - tcg_gen_addi_reg(cpu_iaoq_b, cpu_iaoq_f, 4); + copy_iaoq_entry(ctx, cpu_iaoq_f, -1, tmp); + tcg_gen_addi_i64(tmp, tmp, 4); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); tcg_gen_mov_i64(cpu_iasq_f, new_spc); tcg_gen_mov_i64(cpu_iasq_b, cpu_iasq_f); + nullify_set(ctx, 0); } else { - copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); if (ctx->iaoq_b == -1) { tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); } - tcg_gen_mov_reg(cpu_iaoq_b, tmp); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); tcg_gen_mov_i64(cpu_iasq_b, new_spc); nullify_set(ctx, a->n); } - tcg_temp_free_i64(new_spc); tcg_gen_lookup_and_goto_ptr(); ctx->base.is_jmp = DISAS_NORETURN; return nullify_end(ctx); @@ -3496,7 +3971,7 @@ static bool trans_bl(DisasContext *ctx, arg_bl *a) static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) { - target_ureg dest = iaoq_dest(ctx, a->disp); + uint64_t dest = iaoq_dest(ctx, a->disp); nullify_over(ctx); @@ -3517,8 +3992,7 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) #ifndef CONFIG_USER_ONLY if (ctx->tb_flags & PSW_C) { - CPUHPPAState *env = ctx->cs->env_ptr; - int type = hppa_artype_for_page(env, ctx->base.pc_next); + int type = hppa_artype_for_page(cpu_env(ctx->cs), ctx->base.pc_next); /* If we could not find a TLB entry, then we need to generate an ITLB miss exception so the kernel will provide it. The resulting TLB fill operation will invalidate this TB and @@ -3530,7 +4004,7 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) } /* No change for non-gateway pages or for priv decrease. */ if (type >= 4 && type - 4 < ctx->privilege) { - dest = deposit32(dest, 0, 2, type - 4); + dest = deposit64(dest, 0, 2, type - 4); } } else { dest &= -4; /* priv = 0 */ @@ -3538,11 +4012,11 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) #endif if (a->l) { - TCGv_reg tmp = dest_gpr(ctx, a->l); + TCGv_i64 tmp = dest_gpr(ctx, a->l); if (ctx->privilege < 3) { - tcg_gen_andi_reg(tmp, tmp, -4); + tcg_gen_andi_i64(tmp, tmp, -4); } - tcg_gen_ori_reg(tmp, tmp, ctx->privilege); + tcg_gen_ori_i64(tmp, tmp, ctx->privilege); save_gpr(ctx, a->l, tmp); } @@ -3552,9 +4026,9 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) static bool trans_blr(DisasContext *ctx, arg_blr *a) { if (a->x) { - TCGv_reg tmp = get_temp(ctx); - tcg_gen_shli_reg(tmp, load_gpr(ctx, a->x), 3); - tcg_gen_addi_reg(tmp, tmp, ctx->iaoq_f + 8); + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_shli_i64(tmp, load_gpr(ctx, a->x), 3); + tcg_gen_addi_i64(tmp, tmp, ctx->iaoq_f + 8); /* The computation here never changes privilege level. */ return do_ibranch(ctx, tmp, a->l, a->n); } else { @@ -3565,14 +4039,14 @@ static bool trans_blr(DisasContext *ctx, arg_blr *a) static bool trans_bv(DisasContext *ctx, arg_bv *a) { - TCGv_reg dest; + TCGv_i64 dest; if (a->x == 0) { dest = load_gpr(ctx, a->b); } else { - dest = get_temp(ctx); - tcg_gen_shli_reg(dest, load_gpr(ctx, a->x), 3); - tcg_gen_add_reg(dest, dest, load_gpr(ctx, a->b)); + dest = tcg_temp_new_i64(); + tcg_gen_shli_i64(dest, load_gpr(ctx, a->x), 3); + tcg_gen_add_i64(dest, dest, load_gpr(ctx, a->b)); } dest = do_ibranch_priv(ctx, dest); return do_ibranch(ctx, dest, 0, a->n); @@ -3580,7 +4054,7 @@ static bool trans_bv(DisasContext *ctx, arg_bv *a) static bool trans_bve(DisasContext *ctx, arg_bve *a) { - TCGv_reg dest; + TCGv_i64 dest; #ifdef CONFIG_USER_ONLY dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); @@ -3589,14 +4063,14 @@ static bool trans_bve(DisasContext *ctx, arg_bve *a) nullify_over(ctx); dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); - copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); if (ctx->iaoq_b == -1) { tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); } - copy_iaoq_entry(cpu_iaoq_b, -1, dest); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, dest); tcg_gen_mov_i64(cpu_iasq_b, space_select(ctx, 0, dest)); if (a->l) { - copy_iaoq_entry(cpu_gr[a->l], ctx->iaoq_n, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_gr[a->l], ctx->iaoq_n, ctx->iaoq_n_var); } nullify_set(ctx, a->n); tcg_gen_lookup_and_goto_ptr(); @@ -3605,6 +4079,12 @@ static bool trans_bve(DisasContext *ctx, arg_bve *a) #endif } +static bool trans_nopbts(DisasContext *ctx, arg_nopbts *a) +{ + /* All branch target stack instructions implement as nop. */ + return ctx->is_pa20; +} + /* * Float class 0 */ @@ -3614,6 +4094,21 @@ static void gen_fcpy_f(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) tcg_gen_mov_i32(dst, src); } +static bool trans_fid_f(DisasContext *ctx, arg_fid_f *a) +{ + uint64_t ret; + + if (ctx->is_pa20) { + ret = 0x13080000000000ULL; /* PA8700 (PCX-W2) */ + } else { + ret = 0x0f080000000000ULL; /* PA7300LC (PCX-L2) */ + } + + nullify_over(ctx); + save_frd(0, tcg_constant_i64(ret)); + return nullify_end(ctx); +} + static bool trans_fcpy_f(DisasContext *ctx, arg_fclass01 *a) { return do_fop_wew(ctx, a->t, a->r, gen_fcpy_f); @@ -3858,10 +4353,7 @@ static bool trans_fcmp_f(DisasContext *ctx, arg_fclass2 *a) ty = tcg_constant_i32(a->y); tc = tcg_constant_i32(a->c); - gen_helper_fcmp_s(cpu_env, ta, tb, ty, tc); - - tcg_temp_free_i32(ta); - tcg_temp_free_i32(tb); + gen_helper_fcmp_s(tcg_env, ta, tb, ty, tc); return nullify_end(ctx); } @@ -3878,22 +4370,19 @@ static bool trans_fcmp_d(DisasContext *ctx, arg_fclass2 *a) ty = tcg_constant_i32(a->y); tc = tcg_constant_i32(a->c); - gen_helper_fcmp_d(cpu_env, ta, tb, ty, tc); - - tcg_temp_free_i64(ta); - tcg_temp_free_i64(tb); + gen_helper_fcmp_d(tcg_env, ta, tb, ty, tc); return nullify_end(ctx); } static bool trans_ftest(DisasContext *ctx, arg_ftest *a) { - TCGv_reg t; + TCGv_i64 t; nullify_over(ctx); - t = get_temp(ctx); - tcg_gen_ld32u_reg(t, cpu_env, offsetof(CPUHPPAState, fr0_shadow)); + t = tcg_temp_new_i64(); + tcg_gen_ld32u_i64(t, tcg_env, offsetof(CPUHPPAState, fr0_shadow)); if (a->y == 1) { int mask; @@ -3901,7 +4390,7 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) switch (a->c) { case 0: /* simple */ - tcg_gen_andi_reg(t, t, 0x4000000); + tcg_gen_andi_i64(t, t, 0x4000000); ctx->null_cond = cond_make_0(TCG_COND_NE, t); goto done; case 2: /* rej */ @@ -3930,19 +4419,18 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) return true; } if (inv) { - TCGv_reg c = load_const(ctx, mask); - tcg_gen_or_reg(t, t, c); + TCGv_i64 c = tcg_constant_i64(mask); + tcg_gen_or_i64(t, t, c); ctx->null_cond = cond_make(TCG_COND_EQ, t, c); } else { - tcg_gen_andi_reg(t, t, mask); + tcg_gen_andi_i64(t, t, mask); ctx->null_cond = cond_make_0(TCG_COND_EQ, t); } } else { unsigned cbit = (a->y ^ 1) - 1; - tcg_gen_extract_reg(t, t, 21 - cbit, 1); + tcg_gen_extract_i64(t, t, 21 - cbit, 1); ctx->null_cond = cond_make_0(TCG_COND_NE, t); - tcg_temp_free(t); } done: @@ -4003,8 +4491,6 @@ static bool trans_xmpyu(DisasContext *ctx, arg_xmpyu *a) y = load_frw0_i64(a->r2); tcg_gen_mul_i64(x, x, y); save_frd(a->t, x); - tcg_temp_free_i64(x); - tcg_temp_free_i64(y); return nullify_end(ctx); } @@ -4073,15 +4559,12 @@ static bool trans_fmpyfadd_f(DisasContext *ctx, arg_fmpyfadd_f *a) z = load_frw0_i32(a->ra3); if (a->neg) { - gen_helper_fmpynfadd_s(x, cpu_env, x, y, z); + gen_helper_fmpynfadd_s(x, tcg_env, x, y, z); } else { - gen_helper_fmpyfadd_s(x, cpu_env, x, y, z); + gen_helper_fmpyfadd_s(x, tcg_env, x, y, z); } - tcg_temp_free_i32(y); - tcg_temp_free_i32(z); save_frw_i32(a->t, x); - tcg_temp_free_i32(x); return nullify_end(ctx); } @@ -4095,22 +4578,61 @@ static bool trans_fmpyfadd_d(DisasContext *ctx, arg_fmpyfadd_d *a) z = load_frd0(a->ra3); if (a->neg) { - gen_helper_fmpynfadd_d(x, cpu_env, x, y, z); + gen_helper_fmpynfadd_d(x, tcg_env, x, y, z); } else { - gen_helper_fmpyfadd_d(x, cpu_env, x, y, z); + gen_helper_fmpyfadd_d(x, tcg_env, x, y, z); } - tcg_temp_free_i64(y); - tcg_temp_free_i64(z); save_frd(a->t, x); - tcg_temp_free_i64(x); return nullify_end(ctx); } -static bool trans_diag(DisasContext *ctx, arg_diag *a) +/* Emulate PDC BTLB, called by SeaBIOS-hppa */ +static bool trans_diag_btlb(DisasContext *ctx, arg_diag_btlb *a) { - qemu_log_mask(LOG_UNIMP, "DIAG opcode ignored\n"); - cond_free(&ctx->null_cond); + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY + nullify_over(ctx); + gen_helper_diag_btlb(tcg_env); + return nullify_end(ctx); +#endif +} + +/* Print char in %r26 to first serial console, used by SeaBIOS-hppa */ +static bool trans_diag_cout(DisasContext *ctx, arg_diag_cout *a) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY + nullify_over(ctx); + gen_helper_diag_console_output(tcg_env); + return nullify_end(ctx); +#endif +} + +static bool trans_diag_getshadowregs_pa1(DisasContext *ctx, arg_empty *a) +{ + return !ctx->is_pa20 && do_getshadowregs(ctx); +} + +static bool trans_diag_getshadowregs_pa2(DisasContext *ctx, arg_empty *a) +{ + return ctx->is_pa20 && do_getshadowregs(ctx); +} + +static bool trans_diag_putshadowregs_pa1(DisasContext *ctx, arg_empty *a) +{ + return !ctx->is_pa20 && do_putshadowregs(ctx); +} + +static bool trans_diag_putshadowregs_pa2(DisasContext *ctx, arg_empty *a) +{ + return ctx->is_pa20 && do_putshadowregs(ctx); +} + +static bool trans_diag_unimp(DisasContext *ctx, arg_diag_unimp *a) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); + qemu_log_mask(LOG_UNIMP, "DIAG opcode 0x%04x ignored\n", a->i); return true; } @@ -4121,16 +4643,19 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->cs = cs; ctx->tb_flags = ctx->base.tb->flags; + ctx->is_pa20 = hppa_is_pa20(cpu_env(cs)); #ifdef CONFIG_USER_ONLY - ctx->privilege = MMU_USER_IDX; + ctx->privilege = MMU_IDX_TO_PRIV(MMU_USER_IDX); ctx->mmu_idx = MMU_USER_IDX; - ctx->iaoq_f = ctx->base.pc_first | MMU_USER_IDX; - ctx->iaoq_b = ctx->base.tb->cs_base | MMU_USER_IDX; + ctx->iaoq_f = ctx->base.pc_first | ctx->privilege; + ctx->iaoq_b = ctx->base.tb->cs_base | ctx->privilege; ctx->unalign = (ctx->tb_flags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN); #else ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3; - ctx->mmu_idx = (ctx->tb_flags & PSW_D ? ctx->privilege : MMU_PHYS_IDX); + ctx->mmu_idx = (ctx->tb_flags & PSW_D + ? PRIV_P_TO_MMU_IDX(ctx->privilege, ctx->tb_flags & PSW_P) + : ctx->tb_flags & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX); /* Recover the IAOQ values from the GVA + PRIV. */ uint64_t cs_base = ctx->base.tb->cs_base; @@ -4143,14 +4668,11 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->iaoq_n = -1; ctx->iaoq_n_var = NULL; + ctx->zero = tcg_constant_i64(0); + /* Bound the number of instructions by those left on the page. */ bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; ctx->base.max_insns = MIN(ctx->base.max_insns, bound); - - ctx->ntempr = 0; - ctx->ntempl = 0; - memset(ctx->tempr, 0, sizeof(ctx->tempr)); - memset(ctx->templ, 0, sizeof(ctx->templ)); } static void hppa_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) @@ -4171,15 +4693,15 @@ static void hppa_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - tcg_gen_insn_start(ctx->iaoq_f, ctx->iaoq_b); + tcg_gen_insn_start(ctx->iaoq_f, ctx->iaoq_b, 0); + ctx->insn_start_updated = false; } static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - CPUHPPAState *env = cs->env_ptr; + CPUHPPAState *env = cpu_env(cs); DisasJumpType ret; - int i, n; /* Execute one insn. */ #ifdef CONFIG_USER_ONLY @@ -4198,8 +4720,8 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) This will be overwritten by a branch. */ if (ctx->iaoq_b == -1) { ctx->iaoq_n = -1; - ctx->iaoq_n_var = get_temp(ctx); - tcg_gen_addi_reg(ctx->iaoq_n_var, cpu_iaoq_b, 4); + ctx->iaoq_n_var = tcg_temp_new_i64(); + tcg_gen_addi_i64(ctx->iaoq_n_var, cpu_iaoq_b, 4); } else { ctx->iaoq_n = ctx->iaoq_b + 4; ctx->iaoq_n_var = NULL; @@ -4218,18 +4740,6 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) } } - /* Free any temporaries allocated. */ - for (i = 0, n = ctx->ntempr; i < n; ++i) { - tcg_temp_free(ctx->tempr[i]); - ctx->tempr[i] = NULL; - } - for (i = 0, n = ctx->ntempl; i < n; ++i) { - tcg_temp_free_tl(ctx->templ[i]); - ctx->templ[i] = NULL; - } - ctx->ntempr = 0; - ctx->ntempl = 0; - /* Advance the insn queue. Note that this check also detects a priority change within the instruction queue. */ if (ret == DISAS_NEXT && ctx->iaoq_b != ctx->iaoq_f + 4) { @@ -4257,8 +4767,8 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) case DISAS_IAQ_N_STALE: case DISAS_IAQ_N_STALE_EXIT: if (ctx->iaoq_f == -1) { - tcg_gen_mov_reg(cpu_iaoq_f, cpu_iaoq_b); - copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_iaoq_f, -1, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); #ifndef CONFIG_USER_ONLY tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); #endif @@ -4267,7 +4777,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ? DISAS_EXIT : DISAS_IAQ_N_UPDATED); } else if (ctx->iaoq_b == -1) { - tcg_gen_mov_reg(cpu_iaoq_b, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, ctx->iaoq_n_var); } break; @@ -4287,8 +4797,8 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) case DISAS_TOO_MANY: case DISAS_IAQ_N_STALE: case DISAS_IAQ_N_STALE_EXIT: - copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); - copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); + copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); nullify_save(ctx); /* FALLTHRU */ case DISAS_IAQ_N_UPDATED: @@ -4340,21 +4850,9 @@ static const TranslatorOps hppa_tr_ops = { .disas_log = hppa_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; - translator_loop(&hppa_tr_ops, &ctx.base, cs, tb, max_insns); -} - -void restore_state_to_opc(CPUHPPAState *env, TranslationBlock *tb, - target_ulong *data) -{ - env->iaoq_f = data[0]; - if (data[1] != (target_ureg)-1) { - env->iaoq_b = data[1]; - } - /* Since we were executing the instruction at IAOQ_F, and took some - sort of action that provoked the cpu_restore_state, we can infer - that the instruction was not nullified. */ - env->psw_n = 0; + translator_loop(cs, tb, max_insns, pc, host_pc, &hppa_tr_ops, &ctx.base); } diff --git a/target/i386/arch_dump.c b/target/i386/arch_dump.c index 004141fc04..c290910a04 100644 --- a/target/i386/arch_dump.c +++ b/target/i386/arch_dump.c @@ -42,7 +42,7 @@ typedef struct { static int x86_64_write_elf64_note(WriteCoreDumpFunction f, CPUX86State *env, int id, - void *opaque) + DumpState *s) { x86_64_user_regs_struct regs; Elf64_Nhdr *note; @@ -94,7 +94,7 @@ static int x86_64_write_elf64_note(WriteCoreDumpFunction f, buf += descsz - sizeof(x86_64_user_regs_struct)-sizeof(target_ulong); memcpy(buf, ®s, sizeof(x86_64_user_regs_struct)); - ret = f(note, note_size, opaque); + ret = f(note, note_size, s); g_free(note); if (ret < 0) { return -1; @@ -148,7 +148,7 @@ static void x86_fill_elf_prstatus(x86_elf_prstatus *prstatus, CPUX86State *env, } static int x86_write_elf64_note(WriteCoreDumpFunction f, CPUX86State *env, - int id, void *opaque) + int id, DumpState *s) { x86_elf_prstatus prstatus; Elf64_Nhdr *note; @@ -170,7 +170,7 @@ static int x86_write_elf64_note(WriteCoreDumpFunction f, CPUX86State *env, buf += ROUND_UP(name_size, 4); memcpy(buf, &prstatus, sizeof(prstatus)); - ret = f(note, note_size, opaque); + ret = f(note, note_size, s); g_free(note); if (ret < 0) { return -1; @@ -180,7 +180,7 @@ static int x86_write_elf64_note(WriteCoreDumpFunction f, CPUX86State *env, } int x86_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque) + int cpuid, DumpState *s) { X86CPU *cpu = X86_CPU(cs); int ret; @@ -189,10 +189,10 @@ int x86_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, bool lma = !!(first_x86_cpu->env.hflags & HF_LMA_MASK); if (lma) { - ret = x86_64_write_elf64_note(f, &cpu->env, cpuid, opaque); + ret = x86_64_write_elf64_note(f, &cpu->env, cpuid, s); } else { #endif - ret = x86_write_elf64_note(f, &cpu->env, cpuid, opaque); + ret = x86_write_elf64_note(f, &cpu->env, cpuid, s); #ifdef TARGET_X86_64 } #endif @@ -201,7 +201,7 @@ int x86_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, } int x86_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque) + int cpuid, DumpState *s) { X86CPU *cpu = X86_CPU(cs); x86_elf_prstatus prstatus; @@ -224,7 +224,7 @@ int x86_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, buf += ROUND_UP(name_size, 4); memcpy(buf, &prstatus, sizeof(prstatus)); - ret = f(note, note_size, opaque); + ret = f(note, note_size, s); g_free(note); if (ret < 0) { return -1; @@ -329,7 +329,7 @@ static void qemu_get_cpustate(QEMUCPUState *s, CPUX86State *env) static inline int cpu_write_qemu_note(WriteCoreDumpFunction f, CPUX86State *env, - void *opaque, + DumpState *s, int type) { QEMUCPUState state; @@ -369,7 +369,7 @@ static inline int cpu_write_qemu_note(WriteCoreDumpFunction f, buf += ROUND_UP(name_size, 4); memcpy(buf, &state, sizeof(state)); - ret = f(note, note_size, opaque); + ret = f(note, note_size, s); g_free(note); if (ret < 0) { return -1; @@ -379,19 +379,19 @@ static inline int cpu_write_qemu_note(WriteCoreDumpFunction f, } int x86_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cs, - void *opaque) + DumpState *s) { X86CPU *cpu = X86_CPU(cs); - return cpu_write_qemu_note(f, &cpu->env, opaque, 1); + return cpu_write_qemu_note(f, &cpu->env, s, 1); } int x86_cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cs, - void *opaque) + DumpState *s) { X86CPU *cpu = X86_CPU(cs); - return cpu_write_qemu_note(f, &cpu->env, opaque, 0); + return cpu_write_qemu_note(f, &cpu->env, s, 0); } int cpu_get_dump_info(ArchDumpInfo *info, diff --git a/target/i386/arch_memory_mapping.c b/target/i386/arch_memory_mapping.c index 271cb5e41b..d1ff659128 100644 --- a/target/i386/arch_memory_mapping.c +++ b/target/i386/arch_memory_mapping.c @@ -266,7 +266,7 @@ static void walk_pml5e(MemoryMappingList *list, AddressSpace *as, } #endif -void x86_cpu_get_memory_mapping(CPUState *cs, MemoryMappingList *list, +bool x86_cpu_get_memory_mapping(CPUState *cs, MemoryMappingList *list, Error **errp) { X86CPU *cpu = X86_CPU(cs); @@ -275,7 +275,7 @@ void x86_cpu_get_memory_mapping(CPUState *cs, MemoryMappingList *list, if (!cpu_paging_enabled(cs)) { /* paging is disabled */ - return; + return true; } a20_mask = x86_get_a20_mask(env); @@ -310,5 +310,7 @@ void x86_cpu_get_memory_mapping(CPUState *cs, MemoryMappingList *list, pse = !!(env->cr[4] & CR4_PSE_MASK); walk_pde2(list, cs->as, pde_addr, a20_mask, pse); } + + return true; } diff --git a/target/i386/cpu-dump.c b/target/i386/cpu-dump.c index 08ac957e99..40697064d9 100644 --- a/target/i386/cpu-dump.c +++ b/target/i386/cpu-dump.c @@ -335,10 +335,7 @@ void x86_cpu_dump_local_apic_state(CPUState *cs, int flags) } qemu_printf(" PPR 0x%02x\n", apic_get_ppr(s)); } -#else -void x86_cpu_dump_local_apic_state(CPUState *cs, int flags) -{ -} + #endif /* !CONFIG_USER_ONLY */ #define DUMP_CODE_BYTES_TOTAL 50 diff --git a/target/i386/cpu-param.h b/target/i386/cpu-param.h index 9740bd7abd..911b4cd51b 100644 --- a/target/i386/cpu-param.h +++ b/target/i386/cpu-param.h @@ -23,6 +23,5 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif #define TARGET_PAGE_BITS 12 -#define NB_MMU_MODES 3 #endif diff --git a/target/i386/cpu-qom.h b/target/i386/cpu-qom.h index c557a522e1..d4e216d000 100644 --- a/target/i386/cpu-qom.h +++ b/target/i386/cpu-qom.h @@ -21,8 +21,6 @@ #define QEMU_I386_CPU_QOM_H #include "hw/core/cpu.h" -#include "qemu/notify.h" -#include "qom/object.h" #ifdef TARGET_X86_64 #define TYPE_X86_CPU "x86_64-cpu" @@ -32,43 +30,7 @@ OBJECT_DECLARE_CPU_TYPE(X86CPU, X86CPUClass, X86_CPU) -typedef struct X86CPUModel X86CPUModel; - -/** - * X86CPUClass: - * @cpu_def: CPU model definition - * @host_cpuid_required: Whether CPU model requires cpuid from host. - * @ordering: Ordering on the "-cpu help" CPU model list. - * @migration_safe: See CpuDefinitionInfo::migration_safe - * @static_model: See CpuDefinitionInfo::static - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * - * An x86 CPU model or family. - */ -struct X86CPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - /* CPU definition, automatically loaded by instance_init if not NULL. - * Should be eventually replaced by subclass-specific property defaults. - */ - X86CPUModel *model; - - bool host_cpuid_required; - int ordering; - bool migration_safe; - bool static_model; - - /* Optional description of CPU model. - * If unavailable, cpu_def->model_id is used */ - const char *model_description; - - DeviceRealize parent_realize; - DeviceUnrealize parent_unrealize; - DeviceReset parent_reset; -}; - +#define X86_CPU_TYPE_SUFFIX "-" TYPE_X86_CPU +#define X86_CPU_TYPE_NAME(name) (name X86_CPU_TYPE_SUFFIX) #endif diff --git a/target/i386/cpu-sysemu.c b/target/i386/cpu-sysemu.c index a6f47b7d11..3f9093d285 100644 --- a/target/i386/cpu-sysemu.c +++ b/target/i386/cpu-sysemu.c @@ -19,12 +19,13 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "sysemu/kvm.h" #include "sysemu/xen.h" #include "sysemu/whpx.h" -#include "kvm/kvm_i386.h" #include "qapi/error.h" #include "qapi/qapi-visit-run-state.h" #include "qapi/qmp/qdict.h" +#include "qapi/qobject-input-visitor.h" #include "qom/qom-qobject.h" #include "qapi/qapi-commands-machine-target.h" #include "hw/qdev-properties.h" @@ -129,20 +130,36 @@ static void x86_cpu_to_dict_full(X86CPU *cpu, QDict *props) } } -static void object_apply_props(Object *obj, QDict *props, Error **errp) +static void object_apply_props(Object *obj, QObject *props, + const char *props_arg_name, Error **errp) { + Visitor *visitor; + QDict *qdict; const QDictEntry *prop; - for (prop = qdict_first(props); prop; prop = qdict_next(props, prop)) { - if (!object_property_set_qobject(obj, qdict_entry_key(prop), - qdict_entry_value(prop), errp)) { - break; + visitor = qobject_input_visitor_new(props); + if (!visit_start_struct(visitor, props_arg_name, NULL, 0, errp)) { + visit_free(visitor); + return; + } + + qdict = qobject_to(QDict, props); + for (prop = qdict_first(qdict); prop; prop = qdict_next(qdict, prop)) { + if (!object_property_set(obj, qdict_entry_key(prop), + visitor, errp)) { + goto out; } } + + visit_check_struct(visitor, errp); +out: + visit_end_struct(visitor, NULL); + visit_free(visitor); } /* Create X86CPU object according to model+props specification */ -static X86CPU *x86_cpu_from_model(const char *model, QDict *props, Error **errp) +static X86CPU *x86_cpu_from_model(const char *model, QObject *props, + const char *props_arg_name, Error **errp) { X86CPU *xc = NULL; X86CPUClass *xcc; @@ -156,7 +173,7 @@ static X86CPU *x86_cpu_from_model(const char *model, QDict *props, Error **errp) xc = X86_CPU(object_new_with_class(OBJECT_CLASS(xcc))); if (props) { - object_apply_props(OBJECT(xc), props, &err); + object_apply_props(OBJECT(xc), props, props_arg_name, &err); if (err) { goto out; } @@ -187,10 +204,7 @@ qmp_query_cpu_model_expansion(CpuModelExpansionType type, QDict *props = NULL; const char *base_name; - xc = x86_cpu_from_model(model->name, - model->has_props ? - qobject_to(QDict, model->props) : - NULL, &err); + xc = x86_cpu_from_model(model->name, model->props, "model.props", &err); if (err) { goto out; } @@ -198,7 +212,6 @@ qmp_query_cpu_model_expansion(CpuModelExpansionType type, props = qdict_new(); ret->model = g_new0(CpuModelInfo, 1); ret->model->props = QOBJECT(props); - ret->model->has_props = true; switch (type) { case CPU_MODEL_EXPANSION_TYPE_STATIC: @@ -238,6 +251,16 @@ void cpu_clear_apic_feature(CPUX86State *env) env->features[FEAT_1_EDX] &= ~CPUID_APIC; } +void cpu_set_apic_feature(CPUX86State *env) +{ + env->features[FEAT_1_EDX] |= CPUID_APIC; +} + +bool cpu_has_x2apic_feature(CPUX86State *env) +{ + return env->features[FEAT_1_ECX] & CPUID_EXT_X2APIC; +} + bool cpu_is_bsp(X86CPU *cpu) { return cpu_get_apic_base(cpu->apic_state) & MSR_IA32_APICBASE_BSP; @@ -250,12 +273,16 @@ void x86_cpu_machine_reset_cb(void *opaque) cpu_reset(CPU(cpu)); } -APICCommonClass *apic_get_class(void) +APICCommonClass *apic_get_class(Error **errp) { const char *apic_type = "apic"; /* TODO: in-kernel irqchip for hvf */ - if (kvm_apic_in_kernel()) { + if (kvm_enabled()) { + if (!kvm_irqchip_in_kernel()) { + error_setg(errp, "KVM does not support userspace APIC"); + return NULL; + } apic_type = "kvm-apic"; } else if (xen_enabled()) { apic_type = "xen-apic"; @@ -269,19 +296,28 @@ APICCommonClass *apic_get_class(void) void x86_cpu_apic_create(X86CPU *cpu, Error **errp) { APICCommonState *apic; - ObjectClass *apic_class = OBJECT_CLASS(apic_get_class()); + APICCommonClass *apic_class = apic_get_class(errp); - cpu->apic_state = DEVICE(object_new_with_class(apic_class)); + if (!apic_class) { + return; + } + cpu->apic_state = DEVICE(object_new_with_class(OBJECT_CLASS(apic_class))); object_property_add_child(OBJECT(cpu), "lapic", OBJECT(cpu->apic_state)); object_unref(OBJECT(cpu->apic_state)); - qdev_prop_set_uint32(cpu->apic_state, "id", cpu->apic_id); /* TODO: convert to link<> */ apic = APIC_COMMON(cpu->apic_state); apic->cpu = cpu; apic->apicbase = APIC_DEFAULT_ADDRESS | MSR_IA32_APICBASE_ENABLE; + + /* + * apic_common_set_id needs to check if the CPU has x2APIC + * feature in case APIC ID >= 255, so we need to set apic->cpu + * before setting APIC ID + */ + qdev_prop_set_uint32(cpu->apic_state, "id", cpu->apic_id); } void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 6a57ef13af..33760a2ee1 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -24,18 +24,20 @@ #include "qemu/hw-version.h" #include "cpu.h" #include "tcg/helper-tcg.h" -#include "sysemu/reset.h" #include "sysemu/hvf.h" +#include "hvf/hvf-i386.h" #include "kvm/kvm_i386.h" #include "sev.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "qapi/qapi-visit-machine.h" #include "qapi/qmp/qerror.h" -#include "qapi/qapi-commands-machine-target.h" #include "standard-headers/asm-x86/kvm_para.h" #include "hw/qdev-properties.h" #include "hw/i386/topology.h" #ifndef CONFIG_USER_ONLY +#include "sysemu/reset.h" +#include "qapi/qapi-commands-machine-target.h" #include "exec/address-spaces.h" #include "hw/boards.h" #include "hw/i386/sgx-epc.h" @@ -44,6 +46,8 @@ #include "disas/capstone.h" #include "cpu-internal.h" +static void x86_cpu_realizefn(DeviceState *dev, Error **errp); + /* Helpers for building CPUID[2] descriptors: */ struct CPUID2CacheDescriptorInfo { @@ -620,49 +624,124 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, CPUID_MTRR, CPUID_MCA, CPUID_CLFLUSH (needed for Win64) */ /* missing: CPUID_VME, CPUID_DTS, CPUID_SS, CPUID_HT, CPUID_TM, CPUID_PBE */ + +/* + * Kernel-only features that can be shown to usermode programs even if + * they aren't actually supported by TCG, because qemu-user only runs + * in CPL=3; remove them if they are ever implemented for system emulation. + */ +#if defined CONFIG_USER_ONLY +#define CPUID_EXT_KERNEL_FEATURES \ + (CPUID_EXT_PCID | CPUID_EXT_TSC_DEADLINE_TIMER) +#else +#define CPUID_EXT_KERNEL_FEATURES 0 +#endif #define TCG_EXT_FEATURES (CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | \ CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 | CPUID_EXT_CX16 | \ CPUID_EXT_SSE41 | CPUID_EXT_SSE42 | CPUID_EXT_POPCNT | \ CPUID_EXT_XSAVE | /* CPUID_EXT_OSXSAVE is dynamic */ \ CPUID_EXT_MOVBE | CPUID_EXT_AES | CPUID_EXT_HYPERVISOR | \ - CPUID_EXT_RDRAND) + CPUID_EXT_RDRAND | CPUID_EXT_AVX | CPUID_EXT_F16C | \ + CPUID_EXT_FMA | CPUID_EXT_X2APIC | CPUID_EXT_KERNEL_FEATURES) /* missing: CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_VMX, CPUID_EXT_SMX, - CPUID_EXT_EST, CPUID_EXT_TM2, CPUID_EXT_CID, CPUID_EXT_FMA, + CPUID_EXT_EST, CPUID_EXT_TM2, CPUID_EXT_CID, CPUID_EXT_XTPR, CPUID_EXT_PDCM, CPUID_EXT_PCID, CPUID_EXT_DCA, - CPUID_EXT_X2APIC, CPUID_EXT_TSC_DEADLINE_TIMER, CPUID_EXT_AVX, - CPUID_EXT_F16C */ + CPUID_EXT_TSC_DEADLINE_TIMER + */ #ifdef TARGET_X86_64 -#define TCG_EXT2_X86_64_FEATURES (CPUID_EXT2_SYSCALL | CPUID_EXT2_LM) +#define TCG_EXT2_X86_64_FEATURES CPUID_EXT2_LM #else #define TCG_EXT2_X86_64_FEATURES 0 #endif +/* + * CPUID_*_KERNEL_FEATURES denotes bits and features that are not usable + * in usermode or by 32-bit programs. Those are added to supported + * TCG features unconditionally in user-mode emulation mode. This may + * indeed seem strange or incorrect, but it works because code running + * under usermode emulation cannot access them. + * + * Even for long mode, qemu-i386 is not running "a userspace program on a + * 32-bit CPU"; it's running "a userspace program with a 32-bit code segment" + * and therefore using the 32-bit ABI; the CPU itself might be 64-bit + * but again the difference is only visible in kernel mode. + */ +#if defined CONFIG_LINUX_USER +#define CPUID_EXT2_KERNEL_FEATURES (CPUID_EXT2_LM | CPUID_EXT2_FFXSR) +#elif defined CONFIG_USER_ONLY +/* FIXME: Long mode not yet supported for i386 bsd-user */ +#define CPUID_EXT2_KERNEL_FEATURES CPUID_EXT2_FFXSR +#else +#define CPUID_EXT2_KERNEL_FEATURES 0 +#endif + #define TCG_EXT2_FEATURES ((TCG_FEATURES & CPUID_EXT2_AMD_ALIASES) | \ CPUID_EXT2_NX | CPUID_EXT2_MMXEXT | CPUID_EXT2_RDTSCP | \ CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT | CPUID_EXT2_PDPE1GB | \ - TCG_EXT2_X86_64_FEATURES) + CPUID_EXT2_SYSCALL | TCG_EXT2_X86_64_FEATURES | \ + CPUID_EXT2_KERNEL_FEATURES) + +#if defined CONFIG_USER_ONLY +#define CPUID_EXT3_KERNEL_FEATURES CPUID_EXT3_OSVW +#else +#define CPUID_EXT3_KERNEL_FEATURES 0 +#endif + #define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \ - CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A) + CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A | \ + CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_KERNEL_FEATURES) + #define TCG_EXT4_FEATURES 0 + +#if defined CONFIG_USER_ONLY +#define CPUID_SVM_KERNEL_FEATURES (CPUID_SVM_NRIPSAVE | CPUID_SVM_VNMI) +#else +#define CPUID_SVM_KERNEL_FEATURES 0 +#endif #define TCG_SVM_FEATURES (CPUID_SVM_NPT | CPUID_SVM_VGIF | \ - CPUID_SVM_SVME_ADDR_CHK) + CPUID_SVM_SVME_ADDR_CHK | CPUID_SVM_KERNEL_FEATURES) + #define TCG_KVM_FEATURES 0 + +#if defined CONFIG_USER_ONLY +#define CPUID_7_0_EBX_KERNEL_FEATURES CPUID_7_0_EBX_INVPCID +#else +#define CPUID_7_0_EBX_KERNEL_FEATURES 0 +#endif #define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \ CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \ CPUID_7_0_EBX_PCOMMIT | CPUID_7_0_EBX_CLFLUSHOPT | \ CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_FSGSBASE | \ - CPUID_7_0_EBX_ERMS) + CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_RDSEED | \ + CPUID_7_0_EBX_SHA_NI | CPUID_7_0_EBX_KERNEL_FEATURES) /* missing: - CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2, - CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM, - CPUID_7_0_EBX_RDSEED */ + CPUID_7_0_EBX_HLE + CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM */ + +#if !defined CONFIG_USER_ONLY || defined CONFIG_LINUX +#define TCG_7_0_ECX_RDPID CPUID_7_0_ECX_RDPID +#else +#define TCG_7_0_ECX_RDPID 0 +#endif #define TCG_7_0_ECX_FEATURES (CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU | \ /* CPUID_7_0_ECX_OSPKE is dynamic */ \ - CPUID_7_0_ECX_LA57 | CPUID_7_0_ECX_PKS) -#define TCG_7_0_EDX_FEATURES 0 -#define TCG_7_1_EAX_FEATURES 0 + CPUID_7_0_ECX_LA57 | CPUID_7_0_ECX_PKS | CPUID_7_0_ECX_VAES | \ + TCG_7_0_ECX_RDPID) + +#if defined CONFIG_USER_ONLY +#define CPUID_7_0_EDX_KERNEL_FEATURES (CPUID_7_0_EDX_SPEC_CTRL | \ + CPUID_7_0_EDX_ARCH_CAPABILITIES | CPUID_7_0_EDX_SPEC_CTRL_SSBD) +#else +#define CPUID_7_0_EDX_KERNEL_FEATURES 0 +#endif +#define TCG_7_0_EDX_FEATURES (CPUID_7_0_EDX_FSRM | CPUID_7_0_EDX_KERNEL_FEATURES) + +#define TCG_7_1_EAX_FEATURES (CPUID_7_1_EAX_FZRM | CPUID_7_1_EAX_FSRS | \ + CPUID_7_1_EAX_FSRC | CPUID_7_1_EAX_CMPCCXADD) +#define TCG_7_1_EDX_FEATURES 0 +#define TCG_7_2_EDX_FEATURES 0 #define TCG_APM_FEATURES 0 #define TCG_6_EAX_FEATURES CPUID_6_EAX_ARAT #define TCG_XSAVE_FEATURES (CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1) @@ -673,6 +752,18 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, #define TCG_SGX_12_0_EBX_FEATURES 0 #define TCG_SGX_12_1_EAX_FEATURES 0 +#if defined CONFIG_USER_ONLY +#define CPUID_8000_0008_EBX_KERNEL_FEATURES (CPUID_8000_0008_EBX_IBPB | \ + CPUID_8000_0008_EBX_IBRS | CPUID_8000_0008_EBX_STIBP | \ + CPUID_8000_0008_EBX_STIBP_ALWAYS_ON | CPUID_8000_0008_EBX_AMD_SSBD | \ + CPUID_8000_0008_EBX_AMD_PSFD) +#else +#define CPUID_8000_0008_EBX_KERNEL_FEATURES 0 +#endif + +#define TCG_8000_0008_EBX (CPUID_8000_0008_EBX_XSAVEERPTR | \ + CPUID_8000_0008_EBX_WBNOINVD | CPUID_8000_0008_EBX_KERNEL_FEATURES) + FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_1_EDX] = { .type = CPUID_FEATURE_WORD, @@ -688,6 +779,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { }, .cpuid = {.eax = 1, .reg = R_EDX, }, .tcg_features = TCG_FEATURES, + .no_autoenable_flags = CPUID_HT, }, [FEAT_1_ECX] = { .type = CPUID_FEATURE_WORD, @@ -765,7 +857,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .feat_names = { "kvmclock", "kvm-nopiodelay", "kvm-mmu", "kvmclock", "kvm-asyncpf", "kvm-steal-time", "kvm-pv-eoi", "kvm-pv-unhalt", - NULL, "kvm-pv-tlb-flush", NULL, "kvm-pv-ipi", + NULL, "kvm-pv-tlb-flush", "kvm-asyncpf-vmexit", "kvm-pv-ipi", "kvm-poll-control", "kvm-pv-sched-yield", "kvm-asyncpf-int", "kvm-msi-ext-dest-id", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -804,7 +896,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "pfthreshold", "avic", NULL, "v-vmsave-vmload", "vgif", NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, + NULL, "vnmi", NULL, NULL, "svme-addr-chk", NULL, NULL, NULL, }, .cpuid = { .eax = 0x8000000A, .reg = R_EDX, }, @@ -858,7 +950,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "tsx-ldtrk", NULL, NULL /* pconfig */, "arch-lbr", NULL, NULL, "amx-bf16", "avx512-fp16", "amx-tile", "amx-int8", "spec-ctrl", "stibp", - NULL, "arch-capabilities", "core-capability", "ssbd", + "flush-l1d", "arch-capabilities", "core-capability", "ssbd", }, .cpuid = { .eax = 7, @@ -871,11 +963,11 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .type = CPUID_FEATURE_WORD, .feat_names = { NULL, NULL, NULL, NULL, - "avx-vnni", "avx512-bf16", NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, + "avx-vnni", "avx512-bf16", NULL, "cmpccxadd", + NULL, NULL, "fzrm", "fsrs", + "fsrc", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, "amx-fp16", NULL, "avx-ifma", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }, @@ -886,6 +978,44 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { }, .tcg_features = TCG_7_1_EAX_FEATURES, }, + [FEAT_7_1_EDX] = { + .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL, NULL, NULL, NULL, + "avx-vnni-int8", "avx-ne-convert", NULL, NULL, + "amx-complex", NULL, NULL, NULL, + NULL, NULL, "prefetchiti", NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid = { + .eax = 7, + .needs_ecx = true, .ecx = 1, + .reg = R_EDX, + }, + .tcg_features = TCG_7_1_EDX_FEATURES, + }, + [FEAT_7_2_EDX] = { + .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL, NULL, NULL, NULL, + NULL, "mcdt-no", NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid = { + .eax = 7, + .needs_ecx = true, .ecx = 2, + .reg = R_EDX, + }, + .tcg_features = TCG_7_2_EDX_FEATURES, + }, [FEAT_8000_0007_EDX] = { .type = CPUID_FEATURE_WORD, .feat_names = { @@ -909,12 +1039,28 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, NULL, NULL, NULL, "wbnoinvd", NULL, NULL, "ibpb", NULL, "ibrs", "amd-stibp", - NULL, NULL, NULL, NULL, + NULL, "stibp-always-on", NULL, NULL, NULL, NULL, NULL, NULL, "amd-ssbd", "virt-ssbd", "amd-no-ssb", NULL, - NULL, NULL, NULL, NULL, + "amd-psfd", NULL, NULL, NULL, }, .cpuid = { .eax = 0x80000008, .reg = R_EBX, }, + .tcg_features = TCG_8000_0008_EBX, + .unmigratable_flags = 0, + }, + [FEAT_8000_0021_EAX] = { + .type = CPUID_FEATURE_WORD, + .feat_names = { + "no-nested-data-bp", NULL, "lfence-always-serializing", NULL, + NULL, NULL, "null-sel-clr-base", NULL, + "auto-ibrs", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid = { .eax = 0x80000021, .reg = R_EAX, }, .tcg_features = 0, .unmigratable_flags = 0, }, @@ -1009,15 +1155,22 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "rdctl-no", "ibrs-all", "rsba", "skip-l1dfl-vmentry", "ssb-no", "mds-no", "pschange-mc-no", "tsx-ctrl", "taa-no", NULL, NULL, NULL, + NULL, "sbdr-ssdp-no", "fbsdp-no", "psdp-no", + NULL, "fb-clear", NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, + "pbrsb-no", NULL, "gds-no", NULL, NULL, NULL, NULL, NULL, }, .msr = { .index = MSR_IA32_ARCH_CAPABILITIES, }, + /* + * FEAT_ARCH_CAPABILITIES only affects a read-only MSR, which + * cannot be read from user mode. Therefore, it has no impact + > on any user-mode operation, and warnings about unsupported + * features do not matter. + */ + .tcg_features = ~0U, }, [FEAT_CORE_CAPABILITY] = { .type = MSR_FEATURE_WORD, @@ -1078,7 +1231,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "vmx-invpcid-exit", "vmx-vmfunc", "vmx-shadow-vmcs", "vmx-encls-exit", "vmx-rdseed-exit", "vmx-pml", NULL, NULL, "vmx-xsaves", NULL, NULL, NULL, - NULL, "vmx-tsc-scaling", NULL, NULL, + NULL, "vmx-tsc-scaling", "vmx-enable-user-wait-pause", NULL, NULL, NULL, NULL, NULL, }, .msr = { @@ -1190,6 +1343,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .feat_names = { [54] = "vmx-ins-outs", [55] = "vmx-true-ctls", + [56] = "vmx-any-errcode", }, .msr = { .index = MSR_IA32_VMX_BASIC, @@ -1233,7 +1387,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .feat_names = { "sgx1", "sgx2", NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "sgx-edeccssa", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -1273,7 +1427,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .feat_names = { NULL, "sgx-debug", "sgx-mode64", NULL, "sgx-provisionkey", "sgx-tokenkey", NULL, "sgx-kss", - NULL, NULL, NULL, NULL, + NULL, NULL, "sgx-aex-notify", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -1395,6 +1549,10 @@ static FeatureDep feature_dependencies[] = { .from = { FEAT_8000_0001_ECX, CPUID_EXT3_SVM }, .to = { FEAT_SVM, ~0ull }, }, + { + .from = { FEAT_VMX_SECONDARY_CTLS, VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE }, + .to = { FEAT_7_0_ECX, CPUID_7_0_ECX_WAITPKG }, + }, }; typedef struct X86RegisterInfo32 { @@ -1467,7 +1625,7 @@ ExtSaveArea x86_ext_save_areas[XSAVE_STATE_AREA_COUNT] = { }, }; -static uint32_t xsave_area_size(uint64_t mask, bool compacted) +uint32_t xsave_area_size(uint64_t mask, bool compacted) { uint64_t ret = x86_ext_save_areas[0].size; const ExtSaveArea *esa; @@ -1587,8 +1745,7 @@ static char *x86_cpu_class_get_model_name(X86CPUClass *cc) { const char *class_name = object_class_get_name(OBJECT_CLASS(cc)); assert(g_str_has_suffix(class_name, X86_CPU_TYPE_SUFFIX)); - return g_strndup(class_name, - strlen(class_name) - strlen(X86_CPU_TYPE_SUFFIX)); + return cpu_model_from_type(class_name); } typedef struct X86CPUVersionDefinition { @@ -1596,6 +1753,7 @@ typedef struct X86CPUVersionDefinition { const char *alias; const char *note; PropValue *props; + const CPUCaches *const cache_info; } X86CPUVersionDefinition; /* Base definition for a CPU model */ @@ -1704,6 +1862,56 @@ static const CPUCaches epyc_cache_info = { }, }; +static CPUCaches epyc_v4_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 = 64 * KiB, + .line_size = 64, + .associativity = 4, + .partitions = 1, + .sets = 256, + .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 = 8 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 8192, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = false, + }, +}; + static const CPUCaches epyc_rome_cache_info = { .l1d_cache = &(CPUCacheInfo) { .type = DATA_CACHE, @@ -1754,6 +1962,56 @@ static const CPUCaches epyc_rome_cache_info = { }, }; +static const CPUCaches epyc_rome_v3_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 = false, + }, +}; + static const CPUCaches epyc_milan_cache_info = { .l1d_cache = &(CPUCacheInfo) { .type = DATA_CACHE, @@ -1804,6 +2062,106 @@ static const CPUCaches epyc_milan_cache_info = { }, }; +static const CPUCaches epyc_milan_v2_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 = 32 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 32768, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = false, + }, +}; + +static const CPUCaches epyc_genoa_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 = 1 * MiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 2048, + .lines_per_tag = 1, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 32 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 32768, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = false, + }, +}; + /* The following VMX features are not supported by KVM and are left out in the * CPU definitions: * @@ -1821,7 +2179,7 @@ static const CPUCaches epyc_milan_cache_info = { * Conceal VM entries from PT * Enable ENCLS exiting * Mode-based execute control (XS/XU) - s TSC scaling (Skylake Server and newer) + * TSC scaling (Skylake Server and newer) * GPA translation for PT (IceLake and newer) * User wait and pause * ENCLV exiting @@ -3467,6 +3825,280 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .name = "SapphireRapids", + .level = 0x20, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 143, + .stepping = 4, + /* + * please keep the ascending order so that we can have a clear view of + * bit position of each feature. + */ + .features[FEAT_1_EDX] = + CPUID_FP87 | CPUID_VME | CPUID_DE | CPUID_PSE | CPUID_TSC | + CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | + CPUID_SEP | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | + CPUID_PAT | CPUID_PSE36 | CPUID_CLFLUSH | CPUID_MMX | CPUID_FXSR | + CPUID_SSE | CPUID_SSE2, + .features[FEAT_1_ECX] = + CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSSE3 | + CPUID_EXT_FMA | CPUID_EXT_CX16 | CPUID_EXT_PCID | CPUID_EXT_SSE41 | + CPUID_EXT_SSE42 | CPUID_EXT_X2APIC | CPUID_EXT_MOVBE | + CPUID_EXT_POPCNT | CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_AES | + CPUID_EXT_XSAVE | CPUID_EXT_AVX | CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | CPUID_EXT2_PDPE1GB | + CPUID_EXT2_RDTSCP | CPUID_EXT2_LM, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_LAHF_LM | CPUID_EXT3_ABM | CPUID_EXT3_3DNOWPREFETCH, + .features[FEAT_8000_0008_EBX] = + CPUID_8000_0008_EBX_WBNOINVD, + .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_AVX512F | CPUID_7_0_EBX_AVX512DQ | + CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | + CPUID_7_0_EBX_AVX512IFMA | CPUID_7_0_EBX_CLFLUSHOPT | + CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_AVX512CD | CPUID_7_0_EBX_SHA_NI | + CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512VL, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_AVX512_VBMI | CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU | + CPUID_7_0_ECX_AVX512_VBMI2 | CPUID_7_0_ECX_GFNI | + CPUID_7_0_ECX_VAES | CPUID_7_0_ECX_VPCLMULQDQ | + CPUID_7_0_ECX_AVX512VNNI | CPUID_7_0_ECX_AVX512BITALG | + CPUID_7_0_ECX_AVX512_VPOPCNTDQ | CPUID_7_0_ECX_LA57 | + CPUID_7_0_ECX_RDPID | CPUID_7_0_ECX_BUS_LOCK_DETECT, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_FSRM | CPUID_7_0_EDX_SERIALIZE | + CPUID_7_0_EDX_TSX_LDTRK | CPUID_7_0_EDX_AMX_BF16 | + CPUID_7_0_EDX_AVX512_FP16 | CPUID_7_0_EDX_AMX_TILE | + CPUID_7_0_EDX_AMX_INT8 | CPUID_7_0_EDX_SPEC_CTRL | + CPUID_7_0_EDX_ARCH_CAPABILITIES | CPUID_7_0_EDX_SPEC_CTRL_SSBD, + .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_XSAVE] = + CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | + CPUID_XSAVE_XGETBV1 | CPUID_XSAVE_XSAVES | CPUID_D_1_EAX_XFD, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .features[FEAT_7_1_EAX] = + CPUID_7_1_EAX_AVX_VNNI | CPUID_7_1_EAX_AVX512_BF16 | + CPUID_7_1_EAX_FZRM | CPUID_7_1_EAX_FSRS | CPUID_7_1_EAX_FSRC, + .features[FEAT_VMX_BASIC] = + MSR_VMX_BASIC_INS_OUTS | MSR_VMX_BASIC_TRUE_CTLS, + .features[FEAT_VMX_ENTRY_CTLS] = + VMX_VM_ENTRY_LOAD_DEBUG_CONTROLS | VMX_VM_ENTRY_IA32E_MODE | + VMX_VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_ENTRY_LOAD_IA32_PAT | 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_PAGE_WALK_LENGTH_5 | + MSR_VMX_EPT_WB | MSR_VMX_EPT_2MB | MSR_VMX_EPT_1GB | + MSR_VMX_EPT_INVEPT | MSR_VMX_EPT_AD_BITS | + 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, + .features[FEAT_VMX_EXIT_CTLS] = + VMX_VM_EXIT_SAVE_DEBUG_CONTROLS | + VMX_VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_EXIT_ACK_INTR_ON_EXIT | VMX_VM_EXIT_SAVE_IA32_PAT | + VMX_VM_EXIT_LOAD_IA32_PAT | VMX_VM_EXIT_SAVE_IA32_EFER | + VMX_VM_EXIT_LOAD_IA32_EFER | VMX_VM_EXIT_SAVE_VMX_PREEMPTION_TIMER, + .features[FEAT_VMX_MISC] = + MSR_VMX_MISC_STORE_LMA | MSR_VMX_MISC_ACTIVITY_HLT | + 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_CR3_LOAD_EXITING | VMX_CPU_BASED_CR3_STORE_EXITING | + VMX_CPU_BASED_CR8_LOAD_EXITING | VMX_CPU_BASED_CR8_STORE_EXITING | + VMX_CPU_BASED_TPR_SHADOW | VMX_CPU_BASED_VIRTUAL_NMI_PENDING | + VMX_CPU_BASED_MOV_DR_EXITING | VMX_CPU_BASED_UNCOND_IO_EXITING | + VMX_CPU_BASED_USE_IO_BITMAPS | VMX_CPU_BASED_MONITOR_TRAP_FLAG | + VMX_CPU_BASED_USE_MSR_BITMAPS | VMX_CPU_BASED_MONITOR_EXITING | + VMX_CPU_BASED_PAUSE_EXITING | + VMX_CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, + .features[FEAT_VMX_SECONDARY_CTLS] = + VMX_SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | + 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_WBINVD_EXITING | + 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 | + VMX_SECONDARY_EXEC_XSAVES, + .features[FEAT_VMX_VMFUNC] = + MSR_VMX_VMFUNC_EPT_SWITCHING, + .xlevel = 0x80000008, + .model_id = "Intel Xeon Processor (SapphireRapids)", + .versions = (X86CPUVersionDefinition[]) { + { .version = 1 }, + { + .version = 2, + .props = (PropValue[]) { + { "sbdr-ssdp-no", "on" }, + { "fbsdp-no", "on" }, + { "psdp-no", "on" }, + { /* end of list */ } + } + }, + { /* end of list */ } + } + }, + { + .name = "GraniteRapids", + .level = 0x20, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 173, + .stepping = 0, + /* + * please keep the ascending order so that we can have a clear view of + * bit position of each feature. + */ + .features[FEAT_1_EDX] = + CPUID_FP87 | CPUID_VME | CPUID_DE | CPUID_PSE | CPUID_TSC | + CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | + CPUID_SEP | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | + CPUID_PAT | CPUID_PSE36 | CPUID_CLFLUSH | CPUID_MMX | CPUID_FXSR | + CPUID_SSE | CPUID_SSE2, + .features[FEAT_1_ECX] = + CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSSE3 | + CPUID_EXT_FMA | CPUID_EXT_CX16 | CPUID_EXT_PCID | CPUID_EXT_SSE41 | + CPUID_EXT_SSE42 | CPUID_EXT_X2APIC | CPUID_EXT_MOVBE | + CPUID_EXT_POPCNT | CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_AES | + CPUID_EXT_XSAVE | CPUID_EXT_AVX | CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | CPUID_EXT2_PDPE1GB | + CPUID_EXT2_RDTSCP | CPUID_EXT2_LM, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_LAHF_LM | CPUID_EXT3_ABM | CPUID_EXT3_3DNOWPREFETCH, + .features[FEAT_8000_0008_EBX] = + CPUID_8000_0008_EBX_WBNOINVD, + .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_AVX512F | CPUID_7_0_EBX_AVX512DQ | + CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | + CPUID_7_0_EBX_AVX512IFMA | CPUID_7_0_EBX_CLFLUSHOPT | + CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_AVX512CD | CPUID_7_0_EBX_SHA_NI | + CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512VL, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_AVX512_VBMI | CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU | + CPUID_7_0_ECX_AVX512_VBMI2 | CPUID_7_0_ECX_GFNI | + CPUID_7_0_ECX_VAES | CPUID_7_0_ECX_VPCLMULQDQ | + CPUID_7_0_ECX_AVX512VNNI | CPUID_7_0_ECX_AVX512BITALG | + CPUID_7_0_ECX_AVX512_VPOPCNTDQ | CPUID_7_0_ECX_LA57 | + CPUID_7_0_ECX_RDPID | CPUID_7_0_ECX_BUS_LOCK_DETECT, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_FSRM | CPUID_7_0_EDX_SERIALIZE | + CPUID_7_0_EDX_TSX_LDTRK | CPUID_7_0_EDX_AMX_BF16 | + CPUID_7_0_EDX_AVX512_FP16 | CPUID_7_0_EDX_AMX_TILE | + CPUID_7_0_EDX_AMX_INT8 | CPUID_7_0_EDX_SPEC_CTRL | + CPUID_7_0_EDX_ARCH_CAPABILITIES | CPUID_7_0_EDX_SPEC_CTRL_SSBD, + .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 | + MSR_ARCH_CAP_SBDR_SSDP_NO | MSR_ARCH_CAP_FBSDP_NO | + MSR_ARCH_CAP_PSDP_NO | MSR_ARCH_CAP_PBRSB_NO, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | + CPUID_XSAVE_XGETBV1 | CPUID_XSAVE_XSAVES | CPUID_D_1_EAX_XFD, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .features[FEAT_7_1_EAX] = + CPUID_7_1_EAX_AVX_VNNI | CPUID_7_1_EAX_AVX512_BF16 | + CPUID_7_1_EAX_FZRM | CPUID_7_1_EAX_FSRS | CPUID_7_1_EAX_FSRC | + CPUID_7_1_EAX_AMX_FP16, + .features[FEAT_7_1_EDX] = + CPUID_7_1_EDX_PREFETCHITI, + .features[FEAT_7_2_EDX] = + CPUID_7_2_EDX_MCDT_NO, + .features[FEAT_VMX_BASIC] = + MSR_VMX_BASIC_INS_OUTS | MSR_VMX_BASIC_TRUE_CTLS, + .features[FEAT_VMX_ENTRY_CTLS] = + VMX_VM_ENTRY_LOAD_DEBUG_CONTROLS | VMX_VM_ENTRY_IA32E_MODE | + VMX_VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_ENTRY_LOAD_IA32_PAT | 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_PAGE_WALK_LENGTH_5 | + MSR_VMX_EPT_WB | MSR_VMX_EPT_2MB | MSR_VMX_EPT_1GB | + MSR_VMX_EPT_INVEPT | MSR_VMX_EPT_AD_BITS | + 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, + .features[FEAT_VMX_EXIT_CTLS] = + VMX_VM_EXIT_SAVE_DEBUG_CONTROLS | + VMX_VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_EXIT_ACK_INTR_ON_EXIT | VMX_VM_EXIT_SAVE_IA32_PAT | + VMX_VM_EXIT_LOAD_IA32_PAT | VMX_VM_EXIT_SAVE_IA32_EFER | + VMX_VM_EXIT_LOAD_IA32_EFER | VMX_VM_EXIT_SAVE_VMX_PREEMPTION_TIMER, + .features[FEAT_VMX_MISC] = + MSR_VMX_MISC_STORE_LMA | MSR_VMX_MISC_ACTIVITY_HLT | + 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_CR3_LOAD_EXITING | VMX_CPU_BASED_CR3_STORE_EXITING | + VMX_CPU_BASED_CR8_LOAD_EXITING | VMX_CPU_BASED_CR8_STORE_EXITING | + VMX_CPU_BASED_TPR_SHADOW | VMX_CPU_BASED_VIRTUAL_NMI_PENDING | + VMX_CPU_BASED_MOV_DR_EXITING | VMX_CPU_BASED_UNCOND_IO_EXITING | + VMX_CPU_BASED_USE_IO_BITMAPS | VMX_CPU_BASED_MONITOR_TRAP_FLAG | + VMX_CPU_BASED_USE_MSR_BITMAPS | VMX_CPU_BASED_MONITOR_EXITING | + VMX_CPU_BASED_PAUSE_EXITING | + VMX_CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, + .features[FEAT_VMX_SECONDARY_CTLS] = + VMX_SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | + 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_WBINVD_EXITING | + 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 | + VMX_SECONDARY_EXEC_XSAVES, + .features[FEAT_VMX_VMFUNC] = + MSR_VMX_VMFUNC_EPT_SWITCHING, + .xlevel = 0x80000008, + .model_id = "Intel Xeon Processor (GraniteRapids)", + .versions = (X86CPUVersionDefinition[]) { + { .version = 1 }, + { /* end of list */ }, + }, + }, { .name = "Denverton", .level = 21, @@ -3632,7 +4264,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { CPUID_7_0_EDX_CORE_CAPABILITY, .features[FEAT_CORE_CAPABILITY] = MSR_CORE_CAP_SPLIT_LOCK_DETECT, - /* XSAVES is is added in version 3 */ + /* XSAVES is added in version 3 */ .features[FEAT_XSAVE] = CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | CPUID_XSAVE_XGETBV1, @@ -3959,6 +4591,15 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .version = 4, + .props = (PropValue[]) { + { "model-id", + "AMD EPYC-v4 Processor" }, + { /* end of list */ } + }, + .cache_info = &epyc_v4_cache_info + }, { /* end of list */ } } }, @@ -4078,6 +4719,25 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .version = 3, + .props = (PropValue[]) { + { "model-id", + "AMD EPYC-Rome-v3 Processor" }, + { /* end of list */ } + }, + .cache_info = &epyc_rome_v3_cache_info + }, + { + .version = 4, + .props = (PropValue[]) { + /* Erratum 1386 */ + { "model-id", + "AMD EPYC-Rome-v4 Processor (no XSAVES)" }, + { "xsaves", "off" }, + { /* end of list */ } + }, + }, { /* end of list */ } } }, @@ -4135,6 +4795,98 @@ static const X86CPUDefinition builtin_x86_defs[] = { .xlevel = 0x8000001E, .model_id = "AMD EPYC-Milan Processor", .cache_info = &epyc_milan_cache_info, + .versions = (X86CPUVersionDefinition[]) { + { .version = 1 }, + { + .version = 2, + .props = (PropValue[]) { + { "model-id", + "AMD EPYC-Milan-v2 Processor" }, + { "vaes", "on" }, + { "vpclmulqdq", "on" }, + { "stibp-always-on", "on" }, + { "amd-psfd", "on" }, + { "no-nested-data-bp", "on" }, + { "lfence-always-serializing", "on" }, + { "null-sel-clr-base", "on" }, + { /* end of list */ } + }, + .cache_info = &epyc_milan_v2_cache_info + }, + { /* end of list */ } + } + }, + { + .name = "EPYC-Genoa", + .level = 0xd, + .vendor = CPUID_VENDOR_AMD, + .family = 25, + .model = 17, + .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_PCID | 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_IBRS | CPUID_8000_0008_EBX_STIBP | + CPUID_8000_0008_EBX_STIBP_ALWAYS_ON | + CPUID_8000_0008_EBX_AMD_SSBD | CPUID_8000_0008_EBX_AMD_PSFD, + .features[FEAT_8000_0021_EAX] = + CPUID_8000_0021_EAX_No_NESTED_DATA_BP | + CPUID_8000_0021_EAX_LFENCE_ALWAYS_SERIALIZING | + CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE | + CPUID_8000_0021_EAX_AUTO_IBRS, + .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_ERMS | + CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_AVX512F | + CPUID_7_0_EBX_AVX512DQ | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_AVX512IFMA | + CPUID_7_0_EBX_CLFLUSHOPT | CPUID_7_0_EBX_CLWB | + CPUID_7_0_EBX_AVX512CD | CPUID_7_0_EBX_SHA_NI | + CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512VL, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_AVX512_VBMI | CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU | + CPUID_7_0_ECX_AVX512_VBMI2 | CPUID_7_0_ECX_GFNI | + CPUID_7_0_ECX_VAES | CPUID_7_0_ECX_VPCLMULQDQ | + CPUID_7_0_ECX_AVX512VNNI | CPUID_7_0_ECX_AVX512BITALG | + CPUID_7_0_ECX_AVX512_VPOPCNTDQ | CPUID_7_0_ECX_LA57 | + CPUID_7_0_ECX_RDPID, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_FSRM, + .features[FEAT_7_1_EAX] = + CPUID_7_1_EAX_AVX512_BF16, + .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 | CPUID_SVM_VNMI | + CPUID_SVM_SVME_ADDR_CHK, + .xlevel = 0x80000022, + .model_id = "AMD EPYC-Genoa Processor", + .cache_info = &epyc_genoa_cache_info, }, }; @@ -4185,6 +4937,25 @@ static Property max_x86_cpu_properties[] = { DEFINE_PROP_END_OF_LIST() }; +static void max_x86_cpu_realize(DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + + if (!object_property_get_int(obj, "family", &error_abort)) { + if (X86_CPU(obj)->env.features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { + object_property_set_int(obj, "family", 15, &error_abort); + object_property_set_int(obj, "model", 107, &error_abort); + object_property_set_int(obj, "stepping", 1, &error_abort); + } else { + object_property_set_int(obj, "family", 6, &error_abort); + object_property_set_int(obj, "model", 6, &error_abort); + object_property_set_int(obj, "stepping", 3, &error_abort); + } + } + + x86_cpu_realizefn(dev, errp); +} + static void max_x86_cpu_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -4196,6 +4967,7 @@ static void max_x86_cpu_class_init(ObjectClass *oc, void *data) "Enables all features supported by the accelerator in the current host"; device_class_set_props(dc, max_x86_cpu_properties); + dc->realize = max_x86_cpu_realize; } static void max_x86_cpu_initfn(Object *obj) @@ -4214,15 +4986,6 @@ static void max_x86_cpu_initfn(Object *obj) */ object_property_set_str(OBJECT(cpu), "vendor", CPUID_VENDOR_AMD, &error_abort); -#ifdef TARGET_X86_64 - object_property_set_int(OBJECT(cpu), "family", 15, &error_abort); - object_property_set_int(OBJECT(cpu), "model", 107, &error_abort); - object_property_set_int(OBJECT(cpu), "stepping", 1, &error_abort); -#else - object_property_set_int(OBJECT(cpu), "family", 6, &error_abort); - object_property_set_int(OBJECT(cpu), "model", 6, &error_abort); - object_property_set_int(OBJECT(cpu), "stepping", 3, &error_abort); -#endif object_property_set_str(OBJECT(cpu), "model-id", "QEMU TCG CPU version " QEMU_HW_VERSION, &error_abort); @@ -4429,7 +5192,8 @@ static void x86_cpuid_set_vendor(Object *obj, const char *value, int i; if (strlen(value) != CPUID_VENDOR_SZ) { - error_setg(errp, QERR_PROPERTY_VALUE_BAD, "", "vendor", value); + error_setg(errp, "value of property 'vendor' must consist of" + " exactly " stringify(CPUID_VENDOR_SZ) " characters"); return; } @@ -4580,7 +5344,7 @@ static const char *x86_cpu_feature_name(FeatureWord w, int bitnr) return name; } -/* Compatibily hack to maintain legacy +-feat semantic, +/* Compatibility hack to maintain legacy +-feat semantic, * where +-feat overwrites any feature set by * feat=on|feat even if the later is parsed after +-feat * (i.e. "-x2apic,x2apic=on" will result in x2apic disabled) @@ -4713,40 +5477,6 @@ static void x86_cpu_get_unavailable_features(Object *obj, Visitor *v, visit_type_strList(v, "unavailable-features", &result, errp); } -/* Check for missing features that may prevent the CPU class from - * running using the current machine and accelerator. - */ -static void x86_cpu_class_check_missing_features(X86CPUClass *xcc, - strList **list) -{ - strList **tail = list; - X86CPU *xc; - Error *err = NULL; - - if (xcc->host_cpuid_required && !accel_uses_host_cpuid()) { - QAPI_LIST_APPEND(tail, g_strdup("kvm")); - return; - } - - xc = X86_CPU(object_new_with_class(OBJECT_CLASS(xcc))); - - x86_cpu_expand_features(xc, &err); - if (err) { - /* Errors at x86_cpu_expand_features should never happen, - * but in case it does, just report the model as not - * runnable at all using the "type" property. - */ - QAPI_LIST_APPEND(tail, g_strdup("type")); - error_free(err); - } - - x86_cpu_filter_features(xc, false); - - x86_cpu_list_feature_names(xc->filtered_features, tail); - - object_unref(OBJECT(xc)); -} - /* Print all cpuid feature names in featureset */ static void listflags(GList *features) @@ -4837,6 +5567,11 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) desc = g_strdup_printf("%s", model_id); } + if (cc->model && cc->model->cpudef->deprecation_note) { + g_autofree char *olddesc = desc; + desc = g_strdup_printf("%s (deprecated)", olddesc); + } + qemu_printf("x86 %-20s %s\n", name, desc); } @@ -4870,6 +5605,42 @@ void x86_cpu_list(void) g_list_free(names); } +#ifndef CONFIG_USER_ONLY + +/* Check for missing features that may prevent the CPU class from + * running using the current machine and accelerator. + */ +static void x86_cpu_class_check_missing_features(X86CPUClass *xcc, + strList **list) +{ + strList **tail = list; + X86CPU *xc; + Error *err = NULL; + + if (xcc->host_cpuid_required && !accel_uses_host_cpuid()) { + QAPI_LIST_APPEND(tail, g_strdup("kvm")); + return; + } + + xc = X86_CPU(object_new_with_class(OBJECT_CLASS(xcc))); + + x86_cpu_expand_features(xc, &err); + if (err) { + /* Errors at x86_cpu_expand_features should never happen, + * but in case it does, just report the model as not + * runnable at all using the "type" property. + */ + QAPI_LIST_APPEND(tail, g_strdup("type")); + error_free(err); + } + + x86_cpu_filter_features(xc, false); + + x86_cpu_list_feature_names(xc->filtered_features, tail); + + object_unref(OBJECT(xc)); +} + static void x86_cpu_definition_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; @@ -4896,7 +5667,6 @@ static void x86_cpu_definition_entry(gpointer data, gpointer user_data) */ if (default_cpu_version != CPU_VERSION_LEGACY) { info->alias_of = x86_cpu_class_get_alias_of(cc); - info->has_alias_of = !!info->alias_of; } QAPI_LIST_PREPEND(*cpu_list, info); @@ -4911,6 +5681,8 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) return cpu_list; } +#endif /* !CONFIG_USER_ONLY */ + uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, bool migratable_only) { @@ -4943,7 +5715,15 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, } #ifndef TARGET_X86_64 if (w == FEAT_8000_0001_EDX) { - r &= ~CPUID_EXT2_LM; + /* + * 32-bit TCG can emulate 64-bit compatibility mode. If there is no + * way for userspace to get out of its 32-bit jail, we can leave + * the LM bit set. + */ + uint32_t unavail = tcg_enabled() + ? CPUID_EXT2_LM & ~CPUID_EXT2_KERNEL_FEATURES + : CPUID_EXT2_LM; + r &= ~unavail; } #endif if (migratable_only) { @@ -5053,6 +5833,31 @@ static void x86_cpu_apply_version_props(X86CPU *cpu, X86CPUModel *model) assert(vdef->version == version); } +static const CPUCaches *x86_cpu_get_versioned_cache_info(X86CPU *cpu, + X86CPUModel *model) +{ + const X86CPUVersionDefinition *vdef; + X86CPUVersion version = x86_cpu_model_resolve_version(model); + const CPUCaches *cache_info = model->cpudef->cache_info; + + if (version == CPU_VERSION_LEGACY) { + return cache_info; + } + + for (vdef = x86_cpu_def_get_versions(model->cpudef); vdef->version; vdef++) { + if (vdef->cache_info) { + cache_info = vdef->cache_info; + } + + if (vdef->version == version) { + break; + } + } + + assert(vdef->version == version); + return cache_info; +} + /* * Load data from X86CPUDefinition into a X86CPU object. * Only for builtin_x86_defs models initialized with x86_register_cpudef_types. @@ -5085,7 +5890,7 @@ static void x86_cpu_load_model(X86CPU *cpu, X86CPUModel *model) } /* legacy-cache defaults to 'off' if CPU model provides cache info */ - cpu->legacy_cache = !def->cache_info; + cpu->legacy_cache = !x86_cpu_get_versioned_cache_info(cpu, model); env->features[FEAT_1_ECX] |= CPUID_EXT_HYPERVISOR; @@ -5113,12 +5918,12 @@ static void x86_cpu_load_model(X86CPU *cpu, X86CPUModel *model) memset(&env->user_features, 0, sizeof(env->user_features)); } -static gchar *x86_gdb_arch_name(CPUState *cs) +static const gchar *x86_gdb_arch_name(CPUState *cs) { #ifdef TARGET_X86_64 - return g_strdup("i386:x86-64"); + return "i386:x86-64"; #else - return g_strdup("i386"); + return "i386"; #endif } @@ -5174,9 +5979,10 @@ static void x86_register_cpudef_types(const X86CPUDefinition *def) /* Versioned models: */ 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 = g_new0(X86CPUModel, 1); m->cpudef = def; m->version = vdef->version; m->note = vdef->note; @@ -5214,7 +6020,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, X86CPUTopoInfo topo_info; topo_info.dies_per_pkg = env->nr_dies; - topo_info.cores_per_die = cs->nr_cores; + topo_info.cores_per_die = cs->nr_cores / env->nr_dies; topo_info.threads_per_core = cs->nr_threads; /* Calculate & apply limits for different index ranges */ @@ -5290,8 +6096,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, */ if (*eax & 31) { int host_vcpus_per_cache = 1 + ((*eax & 0x3FFC000) >> 14); - int vcpus_per_socket = env->nr_dies * cs->nr_cores * - cs->nr_threads; + int vcpus_per_socket = cs->nr_cores * cs->nr_threads; if (cs->nr_cores > 1) { *eax &= ~0xFC000000; *eax |= (pow2ceil(cs->nr_cores) - 1) << 26; @@ -5353,6 +6158,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, case 7: /* Structured Extended Feature Flags Enumeration Leaf */ if (count == 0) { + uint32_t eax_0_unused, ebx_0, ecx_0, edx_0_unused; + /* Maximum ECX value for sub-leaves */ *eax = env->cpuid_level_func7; *ebx = env->features[FEAT_7_0_EBX]; /* Feature flags */ @@ -5367,24 +6174,27 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, * support enabling SGX and/or SGX flexible launch control, * then we need to update the VM's CPUID values accordingly. */ - if ((*ebx & CPUID_7_0_EBX_SGX) && - (!kvm_enabled() || - !(kvm_arch_get_supported_cpuid(cs->kvm_state, 0x7, 0, R_EBX) & - CPUID_7_0_EBX_SGX))) { + x86_cpu_get_supported_cpuid(0x7, 0, + &eax_0_unused, &ebx_0, + &ecx_0, &edx_0_unused); + if ((*ebx & CPUID_7_0_EBX_SGX) && !(ebx_0 & CPUID_7_0_EBX_SGX)) { *ebx &= ~CPUID_7_0_EBX_SGX; } - if ((*ecx & CPUID_7_0_ECX_SGX_LC) && - (!(*ebx & CPUID_7_0_EBX_SGX) || !kvm_enabled() || - !(kvm_arch_get_supported_cpuid(cs->kvm_state, 0x7, 0, R_ECX) & - CPUID_7_0_ECX_SGX_LC))) { + if ((*ecx & CPUID_7_0_ECX_SGX_LC) + && (!(*ebx & CPUID_7_0_EBX_SGX) || !(ecx_0 & CPUID_7_0_ECX_SGX_LC))) { *ecx &= ~CPUID_7_0_ECX_SGX_LC; } } else if (count == 1) { *eax = env->features[FEAT_7_1_EAX]; + *edx = env->features[FEAT_7_1_EDX]; + *ebx = 0; + *ecx = 0; + } else if (count == 2) { + *edx = env->features[FEAT_7_2_EDX]; + *eax = 0; *ebx = 0; *ecx = 0; - *edx = 0; } else { *eax = 0; *ebx = 0; @@ -5401,7 +6211,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0xA: /* Architectural Performance Monitoring Leaf */ - if (accel_uses_host_cpuid() && cpu->enable_pmu) { + if (cpu->enable_pmu) { x86_cpu_get_supported_cpuid(0xA, count, eax, ebx, ecx, edx); } else { *eax = 0; @@ -5441,8 +6251,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ebx &= 0xffff; /* The count doesn't need to be reliable. */ break; case 0x1C: - if (accel_uses_host_cpuid() && cpu->enable_pmu && - (env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_ARCH_LBR)) { + if (cpu->enable_pmu && (env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_ARCH_LBR)) { x86_cpu_get_supported_cpuid(0x1C, 0, eax, ebx, ecx, edx); *edx = 0; } @@ -5464,12 +6273,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 1: *eax = apicid_die_offset(&topo_info); - *ebx = cs->nr_cores * cs->nr_threads; + *ebx = topo_info.cores_per_die * topo_info.threads_per_core; *ecx |= CPUID_TOPOLOGY_LEVEL_CORE; break; case 2: *eax = apicid_pkg_offset(&topo_info); - *ebx = env->nr_dies * cs->nr_cores * cs->nr_threads; + *ebx = cs->nr_cores * cs->nr_threads; *ecx |= CPUID_TOPOLOGY_LEVEL_DIE; break; default: @@ -5498,7 +6307,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, * The initial value of xcr0 and ebx == 0, On host without kvm * commit 412a3c41(e.g., CentOS 6), the ebx's value always == 0 * even through guest update xcr0, this will crash some legacy guest - * (e.g., CentOS 6), So set ebx == ecx to workaroud it. + * (e.g., CentOS 6), So set ebx == ecx to workaround it. */ *ebx = kvm_enabled() ? *ecx : xsave_area_size(env->xcr0, false); } else if (count == 1) { @@ -5516,9 +6325,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } else { *ecx &= ~XSTATE_ARCH_LBR_MASK; } - } else if (count == 0xf && - accel_uses_host_cpuid() && cpu->enable_pmu && - (env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_ARCH_LBR)) { + } else if (count == 0xf && cpu->enable_pmu + && (env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_ARCH_LBR)) { x86_cpu_get_supported_cpuid(0xD, count, eax, ebx, ecx, edx); } else if (count < ARRAY_SIZE(x86_ext_save_areas)) { const ExtSaveArea *esa = &x86_ext_save_areas[count]; @@ -5579,8 +6387,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } else { *eax &= env->features[FEAT_SGX_12_1_EAX]; *ebx &= 0; /* ebx reserve */ - *ecx &= env->features[FEAT_XSAVE_XSS_LO]; - *edx &= env->features[FEAT_XSAVE_XSS_HI]; + *ecx &= env->features[FEAT_XSAVE_XCR0_LO]; + *edx &= env->features[FEAT_XSAVE_XCR0_HI]; /* FP and SSE are always allowed regardless of XSAVE/XCR0. */ *ecx |= XSTATE_FP_MASK | XSTATE_SSE_MASK; @@ -5604,6 +6412,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; } + /* + * If these are changed, they should stay in sync with + * x86_cpu_filter_features(). + */ if (count == 0) { *eax = INTEL_PT_MAX_SUBLEAF; *ebx = INTEL_PT_MINIMAL_EBX; @@ -5618,7 +6430,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; } case 0x1D: { - /* AMX TILE */ + /* AMX TILE, for now hardcoded for Sapphire Rapids*/ *eax = 0; *ebx = 0; *ecx = 0; @@ -5639,7 +6451,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; } case 0x1E: { - /* AMX TMUL */ + /* AMX TMUL, for now hardcoded for Sapphire Rapids */ *eax = 0; *ebx = 0; *ecx = 0; @@ -5701,6 +6513,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx |= 1 << 1; /* CmpLegacy bit */ } } + if (tcg_enabled() && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 && + !(env->hflags & HF_LMA_MASK)) { + *edx &= ~CPUID_EXT2_SYSCALL; + } break; case 0x80000002: case 0x80000003: @@ -5848,10 +6664,14 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (sev_enabled()) { *eax = 0x2; *eax |= sev_es_enabled() ? 0x8 : 0; - *ebx = sev_get_cbit_position(); - *ebx |= sev_get_reduced_phys_bits() << 6; + *ebx = sev_get_cbit_position() & 0x3f; /* EBX[5:0] */ + *ebx |= (sev_get_reduced_phys_bits() & 0x3f) << 6; /* EBX[11:6] */ } break; + case 0x80000021: + *eax = env->features[FEAT_8000_0021_EAX]; + *ebx = *ecx = *edx = 0; + break; default: /* reserved values: zero */ *eax = 0; @@ -5873,17 +6693,19 @@ static void x86_cpu_set_sgxlepubkeyhash(CPUX86State *env) #endif } -static void x86_cpu_reset(DeviceState *dev) +static void x86_cpu_reset_hold(Object *obj) { - CPUState *s = CPU(dev); - X86CPU *cpu = X86_CPU(s); - X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); + CPUState *cs = CPU(obj); + X86CPU *cpu = X86_CPU(cs); + X86CPUClass *xcc = X86_CPU_GET_CLASS(obj); CPUX86State *env = &cpu->env; target_ulong cr4; uint64_t xcr0; int i; - xcc->parent_reset(dev); + if (xcc->parent_phases.hold) { + xcc->parent_phases.hold(obj); + } memset(env, 0, offsetof(CPUX86State, end_reset_fields)); @@ -5963,8 +6785,8 @@ static void x86_cpu_reset(DeviceState *dev) memset(env->dr, 0, sizeof(env->dr)); env->dr[6] = DR6_FIXED_1; env->dr[7] = DR7_FIXED_1; - cpu_breakpoint_remove_all(s, BP_CPU); - cpu_watchpoint_remove_all(s, BP_CPU); + cpu_breakpoint_remove_all(cs, BP_CPU); + cpu_watchpoint_remove_all(cs, BP_CPU); cr4 = 0; xcr0 = XSTATE_FP_MASK; @@ -6012,11 +6834,12 @@ static void x86_cpu_reset(DeviceState *dev) env->exception_has_payload = false; env->exception_payload = 0; env->nmi_injected = false; + env->triple_fault_pending = false; #if !defined(CONFIG_USER_ONLY) /* We hard-wire the BSP to the first CPU. */ - apic_designate_bsp(cpu->apic_state, s->cpu_index == 0); + apic_designate_bsp(cpu->apic_state, cs->cpu_index == 0); - s->halted = !cpu_is_bsp(cpu); + cs->halted = !cpu_is_bsp(cpu); if (kvm_enabled()) { kvm_arch_reset_vcpu(cpu); @@ -6029,6 +6852,19 @@ static void x86_cpu_reset(DeviceState *dev) #endif } +void x86_cpu_after_reset(X86CPU *cpu) +{ +#ifndef CONFIG_USER_ONLY + if (kvm_enabled()) { + kvm_arch_after_reset_vcpu(cpu); + } + + if (cpu->apic_state) { + device_cold_reset(cpu->apic_state); + } +#endif +} + static void mce_init(X86CPU *cpu) { CPUX86State *cenv = &cpu->env; @@ -6095,6 +6931,8 @@ static void x86_cpu_enable_xsave_components(X86CPU *cpu) if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) { env->features[FEAT_XSAVE_XCR0_LO] = 0; env->features[FEAT_XSAVE_XCR0_HI] = 0; + env->features[FEAT_XSAVE_XSS_LO] = 0; + env->features[FEAT_XSAVE_XSS_HI] = 0; return; } @@ -6113,9 +6951,9 @@ static void x86_cpu_enable_xsave_components(X86CPU *cpu) } env->features[FEAT_XSAVE_XCR0_LO] = mask & CPUID_XSTATE_XCR0_MASK; - env->features[FEAT_XSAVE_XCR0_HI] = mask >> 32; + env->features[FEAT_XSAVE_XCR0_HI] = (mask & CPUID_XSTATE_XCR0_MASK) >> 32; env->features[FEAT_XSAVE_XSS_LO] = mask & CPUID_XSTATE_XSS_MASK; - env->features[FEAT_XSAVE_XSS_HI] = mask >> 32; + env->features[FEAT_XSAVE_XSS_HI] = (mask & CPUID_XSTATE_XSS_MASK) >> 32; } /***** Steps involved on loading and filtering CPUID data @@ -6224,6 +7062,8 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) x86_cpu_adjust_feat_level(cpu, FEAT_6_EAX); x86_cpu_adjust_feat_level(cpu, FEAT_7_0_ECX); x86_cpu_adjust_feat_level(cpu, FEAT_7_1_EAX); + x86_cpu_adjust_feat_level(cpu, FEAT_7_1_EDX); + x86_cpu_adjust_feat_level(cpu, FEAT_7_2_EDX); x86_cpu_adjust_feat_level(cpu, FEAT_8000_0001_EDX); x86_cpu_adjust_feat_level(cpu, FEAT_8000_0001_ECX); x86_cpu_adjust_feat_level(cpu, FEAT_8000_0007_EDX); @@ -6265,6 +7105,10 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000001F); } + if (env->features[FEAT_8000_0021_EAX]) { + x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x80000021); + } + /* SGX requires CPUID[0x12] for EPC enumeration */ if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_SGX) { x86_cpu_adjust_level(cpu, &env->cpuid_min_level, 0x12); @@ -6285,8 +7129,8 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) env->cpuid_xlevel2 = env->cpuid_min_xlevel2; } - if (kvm_enabled()) { - kvm_hyperv_expand_features(cpu, errp); + if (kvm_enabled() && !kvm_hyperv_expand_features(cpu, errp)) { + return; } } @@ -6316,14 +7160,19 @@ static void x86_cpu_filter_features(X86CPU *cpu, bool verbose) mark_unavailable_features(cpu, w, unavailable_features, prefix); } + /* + * Check that KVM actually allows the processor tracing features that + * are advertised by cpu_x86_cpuid(). Keep these two in sync. + */ if ((env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) && kvm_enabled()) { - KVMState *s = CPU(cpu)->kvm_state; - uint32_t eax_0 = kvm_arch_get_supported_cpuid(s, 0x14, 0, R_EAX); - uint32_t ebx_0 = kvm_arch_get_supported_cpuid(s, 0x14, 0, R_EBX); - uint32_t ecx_0 = kvm_arch_get_supported_cpuid(s, 0x14, 0, R_ECX); - uint32_t eax_1 = kvm_arch_get_supported_cpuid(s, 0x14, 1, R_EAX); - uint32_t ebx_1 = kvm_arch_get_supported_cpuid(s, 0x14, 1, R_EBX); + uint32_t eax_0, ebx_0, ecx_0, edx_0_unused; + uint32_t eax_1, ebx_1, ecx_1_unused, edx_1_unused; + + x86_cpu_get_supported_cpuid(0x14, 0, + &eax_0, &ebx_0, &ecx_0, &edx_0_unused); + x86_cpu_get_supported_cpuid(0x14, 1, + &eax_1, &ebx_1, &ecx_1_unused, &edx_1_unused); if (!eax_0 || ((ebx_0 & INTEL_PT_MINIMAL_EBX) != INTEL_PT_MINIMAL_EBX) || @@ -6384,6 +7233,11 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) static bool ht_warned; unsigned requested_lbr_fmt; +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) + /* Use pc-relative instructions in system-mode */ + cs->tcg_cflags |= CF_PCREL; +#endif + if (cpu->apic_id == UNASSIGNED_APIC_ID) { error_setg(errp, "apic-id property was not initialized properly"); return; @@ -6534,7 +7388,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) return; } - if (env->features[FEAT_1_EDX] & CPUID_PSE36) { + if (env->features[FEAT_1_EDX] & (CPUID_PSE36 | CPUID_PAE)) { cpu->phys_bits = 36; } else { cpu->phys_bits = 32; @@ -6543,14 +7397,17 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) /* Cache information initialization */ if (!cpu->legacy_cache) { - if (!xcc->model || !xcc->model->cpudef->cache_info) { + const CPUCaches *cache_info = + x86_cpu_get_versioned_cache_info(cpu, xcc->model); + + if (!xcc->model || !cache_info) { g_autofree char *name = x86_cpu_class_get_model_name(xcc); error_setg(errp, "CPU model '%s' doesn't support legacy-cache=off", name); return; } env->cache_info_cpuid2 = env->cache_info_cpuid4 = env->cache_info_amd = - *xcc->model->cpudef->cache_info; + *cache_info; } else { /* Build legacy cache information */ env->cache_info_cpuid2.l1d_cache = &legacy_l1d_cache; @@ -6746,7 +7603,6 @@ static void x86_cpu_initfn(Object *obj) CPUX86State *env = &cpu->env; env->nr_dies = 1; - cpu_set_cpustate_pointers(cpu); object_property_add(obj, "feature-words", "X86CPUFeatureWordInfo", x86_cpu_get_feature_words, @@ -6819,6 +7675,14 @@ static void x86_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.eip = value; } +static vaddr x86_cpu_get_pc(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + + /* Match cpu_get_tb_cpu_state. */ + return cpu->env.eip + cpu->env.segs[R_CS].base; +} + int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request) { X86CPU *cpu = X86_CPU(cs); @@ -6868,6 +7732,18 @@ static bool x86_cpu_has_work(CPUState *cs) return x86_cpu_pending_interrupt(cs, cs->interrupt_request) != 0; } +static int x86_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + CPUX86State *env = cpu_env(cs); + int mmu_index_32 = (env->hflags & HF_CS64_MASK) ? 0 : 1; + int mmu_index_base = + (env->hflags & HF_CPL_MASK) == 3 ? MMU_USER64_IDX : + !(env->hflags & HF_SMAP_MASK) ? MMU_KNOSMAP64_IDX : + (env->eflags & AC_MASK) ? MMU_KNOSMAP64_IDX : MMU_KSMAP64_IDX; + + return mmu_index_base + mmu_index_32; +} + static void x86_disas_set_info(CPUState *cs, disassemble_info *info) { X86CPU *cpu = X86_CPU(cs); @@ -7042,6 +7918,7 @@ static Property x86_cpu_properties[] = { * own cache information (see x86_cpu_load_def()). */ DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, true), + DEFINE_PROP_BOOL("xen-vapic", X86CPU, xen_vapic, false), /* * From "Requirements for Implementing the Microsoft @@ -7085,6 +7962,7 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) X86CPUClass *xcc = X86_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); FeatureWord w; device_class_set_parent_realize(dc, x86_cpu_realizefn, @@ -7093,14 +7971,17 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) &xcc->parent_unrealize); device_class_set_props(dc, x86_cpu_properties); - device_class_set_parent_reset(dc, x86_cpu_reset, &xcc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, x86_cpu_reset_hold, NULL, + &xcc->parent_phases); cc->reset_dump_flags = CPU_DUMP_FPU | CPU_DUMP_CCOP; cc->class_by_name = x86_cpu_class_by_name; cc->parse_features = x86_cpu_parse_featurestr; cc->has_work = x86_cpu_has_work; + cc->mmu_index = x86_cpu_mmu_index; cc->dump_state = x86_cpu_dump_state; cc->set_pc = x86_cpu_set_pc; + cc->get_pc = x86_cpu_get_pc; cc->gdb_read_register = x86_cpu_gdb_read_register; cc->gdb_write_register = x86_cpu_gdb_write_register; cc->get_arch_id = x86_cpu_get_arch_id; @@ -7112,10 +7993,8 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) cc->gdb_arch_name = x86_gdb_arch_name; #ifdef TARGET_X86_64 cc->gdb_core_xml_file = "i386-64bit.xml"; - cc->gdb_num_core_regs = 66; #else cc->gdb_core_xml_file = "i386-32bit.xml"; - cc->gdb_num_core_regs = 50; #endif cc->disas_set_info = x86_disas_set_info; @@ -7166,6 +8045,7 @@ static const TypeInfo x86_cpu_type_info = { .name = TYPE_X86_CPU, .parent = TYPE_CPU, .instance_size = sizeof(X86CPU), + .instance_align = __alignof(X86CPU), .instance_init = x86_cpu_initfn, .instance_post_init = x86_cpu_post_initfn, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 82004b65b9..6b05738079 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -26,6 +26,9 @@ #include "exec/cpu-defs.h" #include "qapi/qapi-types-common.h" #include "qemu/cpu-float.h" +#include "qemu/timer.h" + +#define XEN_NR_VIRQS 24 /* The x86 has a strong memory model with some store-after-load re-ordering */ #define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) @@ -169,6 +172,7 @@ typedef enum X86Seg { #define HF_MPX_EN_SHIFT 25 /* MPX Enabled (CR4+XCR0+BNDCFGx) */ #define HF_MPX_IU_SHIFT 26 /* BND registers in-use */ #define HF_UMIP_SHIFT 27 /* CR4.UMIP */ +#define HF_AVX_EN_SHIFT 28 /* AVX Enabled (CR4+XCR0) */ #define HF_CPL_MASK (3 << HF_CPL_SHIFT) #define HF_INHIBIT_IRQ_MASK (1 << HF_INHIBIT_IRQ_SHIFT) @@ -195,6 +199,7 @@ typedef enum X86Seg { #define HF_MPX_EN_MASK (1 << HF_MPX_EN_SHIFT) #define HF_MPX_IU_MASK (1 << HF_MPX_IU_SHIFT) #define HF_UMIP_MASK (1 << HF_UMIP_SHIFT) +#define HF_AVX_EN_MASK (1 << HF_AVX_EN_SHIFT) /* hflags2 */ @@ -374,6 +379,10 @@ typedef enum X86Seg { #define MSR_IA32_APICBASE_ENABLE (1<<11) #define MSR_IA32_APICBASE_EXTD (1 << 10) #define MSR_IA32_APICBASE_BASE (0xfffffU<<12) +#define MSR_IA32_APICBASE_RESERVED \ + (~(uint64_t)(MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE \ + | MSR_IA32_APICBASE_EXTD | MSR_IA32_APICBASE_BASE)) + #define MSR_IA32_FEATURE_CONTROL 0x0000003a #define MSR_TSC_ADJUST 0x0000003b #define MSR_IA32_SPEC_CTRL 0x48 @@ -540,6 +549,9 @@ typedef enum X86Seg { #define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x00000490 #define MSR_IA32_VMX_VMFUNC 0x00000491 +#define MSR_APIC_START 0x00000800 +#define MSR_APIC_END 0x000008ff + #define XSTATE_FP_BIT 0 #define XSTATE_SSE_BIT 1 #define XSTATE_YMM_BIT 2 @@ -595,6 +607,7 @@ typedef enum FeatureWord { FEAT_8000_0001_ECX, /* CPUID[8000_0001].ECX */ FEAT_8000_0007_EDX, /* CPUID[8000_0007].EDX */ FEAT_8000_0008_EBX, /* CPUID[8000_0008].EBX */ + FEAT_8000_0021_EAX, /* CPUID[8000_0021].EAX */ FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */ FEAT_KVM, /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */ FEAT_KVM_HINTS, /* CPUID[4000_0001].EDX */ @@ -621,6 +634,8 @@ typedef enum FeatureWord { FEAT_SGX_12_1_EAX, /* CPUID[EAX=0x12,ECX=1].EAX (SGX ATTRIBUTES[31:0]) */ FEAT_XSAVE_XSS_LO, /* CPUID[EAX=0xd,ECX=1].ECX */ FEAT_XSAVE_XSS_HI, /* CPUID[EAX=0xd,ECX=1].EDX */ + FEAT_7_1_EDX, /* CPUID[EAX=7,ECX=1].EDX */ + FEAT_7_2_EDX, /* CPUID[EAX=7,ECX=2].EDX */ FEATURE_WORDS, } FeatureWord; @@ -720,7 +735,7 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_EXT2_3DNOWEXT (1U << 30) #define CPUID_EXT2_3DNOW (1U << 31) -/* CPUID[8000_0001].EDX bits that are aliase of CPUID[1].EDX bits on AMD CPUs */ +/* CPUID[8000_0001].EDX bits that are aliases of CPUID[1].EDX bits on AMD CPUs */ #define CPUID_EXT2_AMD_ALIASES (CPUID_EXT2_FPU | CPUID_EXT2_VME | \ CPUID_EXT2_DE | CPUID_EXT2_PSE | \ CPUID_EXT2_TSC | CPUID_EXT2_MSR | \ @@ -767,6 +782,7 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_SVM_AVIC (1U << 13) #define CPUID_SVM_V_VMSAVE_VMLOAD (1U << 15) #define CPUID_SVM_VGIF (1U << 16) +#define CPUID_SVM_VNMI (1U << 25) #define CPUID_SVM_SVME_ADDR_CHK (1U << 28) /* Support RDFSBASE/RDGSBASE/WRFSBASE/WRGSBASE */ @@ -879,14 +895,20 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_7_0_EDX_TSX_LDTRK (1U << 16) /* Architectural LBRs */ #define CPUID_7_0_EDX_ARCH_LBR (1U << 19) +/* AMX_BF16 instruction */ +#define CPUID_7_0_EDX_AMX_BF16 (1U << 22) /* AVX512_FP16 instruction */ #define CPUID_7_0_EDX_AVX512_FP16 (1U << 23) /* AMX tile (two-dimensional register) */ #define CPUID_7_0_EDX_AMX_TILE (1U << 24) +/* AMX_INT8 instruction */ +#define CPUID_7_0_EDX_AMX_INT8 (1U << 25) /* Speculation Control */ #define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) /* Single Thread Indirect Branch Predictors */ #define CPUID_7_0_EDX_STIBP (1U << 27) +/* Flush L1D cache */ +#define CPUID_7_0_EDX_FLUSH_L1D (1U << 28) /* Arch Capabilities */ #define CPUID_7_0_EDX_ARCH_CAPABILITIES (1U << 29) /* Core Capability */ @@ -898,6 +920,31 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_7_1_EAX_AVX_VNNI (1U << 4) /* AVX512 BFloat16 Instruction */ #define CPUID_7_1_EAX_AVX512_BF16 (1U << 5) +/* CMPCCXADD Instructions */ +#define CPUID_7_1_EAX_CMPCCXADD (1U << 7) +/* Fast Zero REP MOVS */ +#define CPUID_7_1_EAX_FZRM (1U << 10) +/* Fast Short REP STOS */ +#define CPUID_7_1_EAX_FSRS (1U << 11) +/* Fast Short REP CMPS/SCAS */ +#define CPUID_7_1_EAX_FSRC (1U << 12) +/* Support Tile Computational Operations on FP16 Numbers */ +#define CPUID_7_1_EAX_AMX_FP16 (1U << 21) +/* Support for VPMADD52[H,L]UQ */ +#define CPUID_7_1_EAX_AVX_IFMA (1U << 23) + +/* Support for VPDPB[SU,UU,SS]D[,S] */ +#define CPUID_7_1_EDX_AVX_VNNI_INT8 (1U << 4) +/* AVX NE CONVERT Instructions */ +#define CPUID_7_1_EDX_AVX_NE_CONVERT (1U << 5) +/* AMX COMPLEX Instructions */ +#define CPUID_7_1_EDX_AMX_COMPLEX (1U << 8) +/* PREFETCHIT0/1 Instructions */ +#define CPUID_7_1_EDX_PREFETCHITI (1U << 14) + +/* Do not exhibit MXCSR Configuration Dependent Timing (MCDT) behavior */ +#define CPUID_7_2_EDX_MCDT_NO (1U << 5) + /* XFD Extend Feature Disabled */ #define CPUID_D_1_EAX_XFD (1U << 4) @@ -916,8 +963,21 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_8000_0008_EBX_IBRS (1U << 14) /* Single Thread Indirect Branch Predictors */ #define CPUID_8000_0008_EBX_STIBP (1U << 15) +/* STIBP mode has enhanced performance and may be left always on */ +#define CPUID_8000_0008_EBX_STIBP_ALWAYS_ON (1U << 17) /* Speculative Store Bypass Disable */ #define CPUID_8000_0008_EBX_AMD_SSBD (1U << 24) +/* Predictive Store Forwarding Disable */ +#define CPUID_8000_0008_EBX_AMD_PSFD (1U << 28) + +/* Processor ignores nested data breakpoints */ +#define CPUID_8000_0021_EAX_No_NESTED_DATA_BP (1U << 0) +/* LFENCE is always serializing */ +#define CPUID_8000_0021_EAX_LFENCE_ALWAYS_SERIALIZING (1U << 2) +/* Null Selector Clears Base */ +#define CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE (1U << 6) +/* Automatic IBRS */ +#define CPUID_8000_0021_EAX_AUTO_IBRS (1U << 8) #define CPUID_XSAVE_XSAVEOPT (1U << 0) #define CPUID_XSAVE_XSAVEC (1U << 1) @@ -971,6 +1031,11 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #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_ARCH_CAP_SBDR_SSDP_NO (1U << 13) +#define MSR_ARCH_CAP_FBSDP_NO (1U << 14) +#define MSR_ARCH_CAP_PSDP_NO (1U << 15) +#define MSR_ARCH_CAP_FB_CLEAR (1U << 17) +#define MSR_ARCH_CAP_PBRSB_NO (1U << 24) #define MSR_CORE_CAP_SPLIT_LOCK_DETECT (1U << 5) @@ -981,6 +1046,7 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define MSR_VMX_BASIC_DUAL_MONITOR (1ULL << 49) #define MSR_VMX_BASIC_INS_OUTS (1ULL << 54) #define MSR_VMX_BASIC_TRUE_CTLS (1ULL << 55) +#define MSR_VMX_BASIC_ANY_ERRCODE (1ULL << 56) #define MSR_VMX_MISC_PREEMPTION_TIMER_SHIFT_MASK 0x1Full #define MSR_VMX_MISC_STORE_LMA (1ULL << 5) @@ -1055,6 +1121,7 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define VMX_SECONDARY_EXEC_ENABLE_PML 0x00020000 #define VMX_SECONDARY_EXEC_XSAVES 0x00100000 #define VMX_SECONDARY_EXEC_TSC_SCALING 0x02000000 +#define VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE 0x04000000 #define VMX_PIN_BASED_EXT_INTR_MASK 0x00000001 #define VMX_PIN_BASED_NMI_EXITING 0x00000008 @@ -1225,6 +1292,7 @@ typedef enum { CC_OP_NB, } CCOp; +QEMU_BUILD_BUG_ON(CC_OP_NB >= 128); typedef struct SegmentCache { uint32_t selector; @@ -1233,18 +1301,35 @@ typedef struct SegmentCache { uint32_t flags; } SegmentCache; -#define MMREG_UNION(n, bits) \ - union n { \ - uint8_t _b_##n[(bits)/8]; \ - uint16_t _w_##n[(bits)/16]; \ - uint32_t _l_##n[(bits)/32]; \ - uint64_t _q_##n[(bits)/64]; \ - float32 _s_##n[(bits)/32]; \ - float64 _d_##n[(bits)/64]; \ - } +typedef union MMXReg { + uint8_t _b_MMXReg[64 / 8]; + uint16_t _w_MMXReg[64 / 16]; + uint32_t _l_MMXReg[64 / 32]; + uint64_t _q_MMXReg[64 / 64]; + float32 _s_MMXReg[64 / 32]; + float64 _d_MMXReg[64 / 64]; +} MMXReg; -typedef MMREG_UNION(ZMMReg, 512) ZMMReg; -typedef MMREG_UNION(MMXReg, 64) MMXReg; +typedef union XMMReg { + uint64_t _q_XMMReg[128 / 64]; +} XMMReg; + +typedef union YMMReg { + uint64_t _q_YMMReg[256 / 64]; + XMMReg _x_YMMReg[256 / 128]; +} YMMReg; + +typedef union ZMMReg { + uint8_t _b_ZMMReg[512 / 8]; + uint16_t _w_ZMMReg[512 / 16]; + uint32_t _l_ZMMReg[512 / 32]; + uint64_t _q_ZMMReg[512 / 64]; + float16 _h_ZMMReg[512 / 16]; + float32 _s_ZMMReg[512 / 32]; + float64 _d_ZMMReg[512 / 64]; + XMMReg _x_ZMMReg[512 / 128]; + YMMReg _y_ZMMReg[512 / 256]; +} ZMMReg; typedef struct BNDReg { uint64_t lb; @@ -1264,9 +1349,17 @@ typedef struct BNDCSReg { #define ZMM_B(n) _b_ZMMReg[63 - (n)] #define ZMM_W(n) _w_ZMMReg[31 - (n)] #define ZMM_L(n) _l_ZMMReg[15 - (n)] +#define ZMM_H(n) _h_ZMMReg[31 - (n)] #define ZMM_S(n) _s_ZMMReg[15 - (n)] #define ZMM_Q(n) _q_ZMMReg[7 - (n)] #define ZMM_D(n) _d_ZMMReg[7 - (n)] +#define ZMM_X(n) _x_ZMMReg[3 - (n)] +#define ZMM_Y(n) _y_ZMMReg[1 - (n)] + +#define XMM_Q(n) _q_XMMReg[1 - (n)] + +#define YMM_Q(n) _q_YMMReg[3 - (n)] +#define YMM_X(n) _x_YMMReg[1 - (n)] #define MMX_B(n) _b_MMXReg[7 - (n)] #define MMX_W(n) _w_MMXReg[3 - (n)] @@ -1276,9 +1369,17 @@ typedef struct BNDCSReg { #define ZMM_B(n) _b_ZMMReg[n] #define ZMM_W(n) _w_ZMMReg[n] #define ZMM_L(n) _l_ZMMReg[n] +#define ZMM_H(n) _h_ZMMReg[n] #define ZMM_S(n) _s_ZMMReg[n] #define ZMM_Q(n) _q_ZMMReg[n] #define ZMM_D(n) _d_ZMMReg[n] +#define ZMM_X(n) _x_ZMMReg[n] +#define ZMM_Y(n) _y_ZMMReg[n] + +#define XMM_Q(n) _q_XMMReg[n] + +#define YMM_Q(n) _q_YMMReg[n] +#define YMM_X(n) _x_YMMReg[n] #define MMX_B(n) _b_MMXReg[n] #define MMX_W(n) _w_MMXReg[n] @@ -1556,8 +1657,8 @@ typedef struct CPUArchState { float_status mmx_status; /* for 3DNow! float ops */ float_status sse_status; uint32_t mxcsr; - ZMMReg xmm_regs[CPU_NB_REGS == 8 ? 8 : 32]; - ZMMReg xmm_t0; + ZMMReg xmm_regs[CPU_NB_REGS == 8 ? 8 : 32] QEMU_ALIGNED(16); + ZMMReg xmm_t0 QEMU_ALIGNED(16); MMXReg mmx_t0; uint64_t opmask_regs[NB_OPMASK_REGS]; @@ -1739,6 +1840,7 @@ typedef struct CPUArchState { uint8_t has_error_code; uint8_t exception_has_payload; uint64_t exception_payload; + uint8_t triple_fault_pending; uint32_t ins_len; uint32_t sipi_vector; bool tsc_valid; @@ -1752,6 +1854,20 @@ typedef struct CPUArchState { #endif #if defined(CONFIG_KVM) struct kvm_nested_state *nested_state; + MemoryRegion *xen_vcpu_info_mr; + void *xen_vcpu_info_hva; + uint64_t xen_vcpu_info_gpa; + uint64_t xen_vcpu_info_default_gpa; + uint64_t xen_vcpu_time_info_gpa; + uint64_t xen_vcpu_runstate_gpa; + uint8_t xen_vcpu_callback_vector; + bool xen_callback_asserted; + uint16_t xen_virq[XEN_NR_VIRQS]; + uint64_t xen_singleshot_timer_ns; + QEMUTimer *xen_singleshot_timer; + uint64_t xen_periodic_timer_period; + QEMUTimer *xen_periodic_timer; + QemuMutex xen_timers_lock; #endif #if defined(CONFIG_HVF) HVFX86LazyFlags hvf_lflags; @@ -1774,6 +1890,7 @@ typedef struct CPUArchState { TPRAccess tpr_access_type; + /* Number of dies within this CPU package. */ unsigned nr_dies; } CPUX86State; @@ -1789,11 +1906,8 @@ struct kvm_msrs; * An x86 CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUX86State env; VMChangeStateEntry *vmsentry; @@ -1928,8 +2042,48 @@ struct ArchCPU { int32_t thread_id; int32_t hv_max_vps; + + bool xen_vapic; }; +typedef struct X86CPUModel X86CPUModel; + +/** + * X86CPUClass: + * @cpu_def: CPU model definition + * @host_cpuid_required: Whether CPU model requires cpuid from host. + * @ordering: Ordering on the "-cpu help" CPU model list. + * @migration_safe: See CpuDefinitionInfo::migration_safe + * @static_model: See CpuDefinitionInfo::static + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * An x86 CPU model or family. + */ +struct X86CPUClass { + CPUClass parent_class; + + /* + * CPU definition, automatically loaded by instance_init if not NULL. + * Should be eventually replaced by subclass-specific property defaults. + */ + X86CPUModel *model; + + bool host_cpuid_required; + int ordering; + bool migration_safe; + bool static_model; + + /* + * Optional description of CPU model. + * If unavailable, cpu_def->model_id is used. + */ + const char *model_description; + + DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; + ResettablePhases parent_phases; +}; #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_x86_cpu; @@ -1938,22 +2092,19 @@ extern const VMStateDescription vmstate_x86_cpu; int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request); int x86_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, - int cpuid, void *opaque); + int cpuid, DumpState *s); int x86_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cpu, - int cpuid, void *opaque); + int cpuid, DumpState *s); int x86_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cpu, - void *opaque); + DumpState *s); int x86_cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, - void *opaque); + DumpState *s); -void x86_cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, +bool x86_cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, Error **errp); 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, GByteArray *buf, int reg); int x86_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); @@ -1961,9 +2112,11 @@ void x86_cpu_list(void); int cpu_x86_support_mca_broadcast(CPUX86State *env); #ifndef CONFIG_USER_ONLY +hwaddr x86_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, + MemTxAttrs *attrs); int cpu_get_pic_interrupt(CPUX86State *s); -/* MSDOS compatibility mode FPU exception support */ +/* MS-DOS compatibility mode FPU exception support */ void x86_register_ferr_irq(qemu_irq irq); void fpu_check_raise_ferr_irq(CPUX86State *s); void cpu_set_ignne(void); @@ -2070,6 +2223,8 @@ void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32); void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32); void cpu_x86_fxsave(CPUX86State *s, target_ulong ptr); void cpu_x86_fxrstor(CPUX86State *s, target_ulong ptr); +void cpu_x86_xsave(CPUX86State *s, target_ulong ptr); +void cpu_x86_xrstor(CPUX86State *s, target_ulong ptr); /* cpu.c */ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, @@ -2079,6 +2234,8 @@ typedef struct PropValue { } PropValue; void x86_cpu_apply_props(X86CPU *cpu, PropValue *props); +void x86_cpu_after_reset(X86CPU *cpu); + uint32_t cpu_x86_virtual_addr_width(CPUX86State *env); /* cpu.c other functions (cpuid) */ @@ -2086,11 +2243,14 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); void cpu_clear_apic_feature(CPUX86State *env); +void cpu_set_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); +bool cpu_has_x2apic_feature(CPUX86State *env); /* helper.c */ void x86_cpu_set_a20(X86CPU *cpu, int a20_state); +void cpu_sync_avx_hflag(CPUX86State *env); #ifndef CONFIG_USER_ONLY static inline int x86_asidx_from_attrs(CPUState *cs, MemTxAttrs attrs) @@ -2128,8 +2288,6 @@ void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7); /* hw/pc.c */ uint64_t cpu_get_tsc(CPUX86State *env); -#define X86_CPU_TYPE_SUFFIX "-" TYPE_X86_CPU -#define X86_CPU_TYPE_NAME(name) (name X86_CPU_TYPE_SUFFIX) #define CPU_RESOLVING_TYPE TYPE_X86_CPU #ifdef TARGET_X86_64 @@ -2141,21 +2299,47 @@ uint64_t cpu_get_tsc(CPUX86State *env); #define cpu_list x86_cpu_list /* MMU modes definitions */ -#define MMU_KSMAP_IDX 0 -#define MMU_USER_IDX 1 -#define MMU_KNOSMAP_IDX 2 -static inline int cpu_mmu_index(CPUX86State *env, bool ifetch) +#define MMU_KSMAP64_IDX 0 +#define MMU_KSMAP32_IDX 1 +#define MMU_USER64_IDX 2 +#define MMU_USER32_IDX 3 +#define MMU_KNOSMAP64_IDX 4 +#define MMU_KNOSMAP32_IDX 5 +#define MMU_PHYS_IDX 6 +#define MMU_NESTED_IDX 7 + +#ifdef CONFIG_USER_ONLY +#ifdef TARGET_X86_64 +#define MMU_USER_IDX MMU_USER64_IDX +#else +#define MMU_USER_IDX MMU_USER32_IDX +#endif +#endif + +static inline bool is_mmu_index_smap(int mmu_index) { - return (env->hflags & HF_CPL_MASK) == 3 ? MMU_USER_IDX : - (!(env->hflags & HF_SMAP_MASK) || (env->eflags & AC_MASK)) - ? MMU_KNOSMAP_IDX : MMU_KSMAP_IDX; + return (mmu_index & ~1) == MMU_KSMAP64_IDX; +} + +static inline bool is_mmu_index_user(int mmu_index) +{ + return (mmu_index & ~1) == MMU_USER64_IDX; +} + +static inline bool is_mmu_index_32(int mmu_index) +{ + assert(mmu_index < MMU_PHYS_IDX); + return mmu_index & 1; } static inline int cpu_mmu_index_kernel(CPUX86State *env) { - return !(env->hflags & HF_SMAP_MASK) ? MMU_KNOSMAP_IDX : - ((env->hflags & HF_CPL_MASK) < 3 && (env->eflags & AC_MASK)) - ? MMU_KNOSMAP_IDX : MMU_KSMAP_IDX; + int mmu_index_32 = (env->hflags & HF_LMA_MASK) ? 0 : 1; + int mmu_index_base = + !(env->hflags & HF_SMAP_MASK) ? MMU_KNOSMAP64_IDX : + ((env->hflags & HF_CPL_MASK) < 3 && (env->eflags & AC_MASK)) ? MMU_KNOSMAP64_IDX : MMU_KSMAP64_IDX; + + return mmu_index_base + mmu_index_32; } #define CC_DST (env->cc_dst) @@ -2170,17 +2354,21 @@ static inline int cpu_mmu_index_kernel(CPUX86State *env) #include "hw/i386/apic.h" #endif -static inline void cpu_get_tb_cpu_state(CPUX86State *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) +static inline void cpu_get_tb_cpu_state(CPUX86State *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { - *cs_base = env->segs[R_CS].base; - *pc = *cs_base + env->eip; *flags = env->hflags | (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK | AC_MASK)); + if (env->hflags & HF_CS64_MASK) { + *cs_base = 0; + *pc = env->eip; + } else { + *cs_base = env->segs[R_CS].base; + *pc = (uint32_t)(*cs_base + env->eip); + } } void do_cpu_init(X86CPU *cpu); -void do_cpu_sipi(X86CPU *cpu); #define MCE_INJECT_BROADCAST 1 #define MCE_INJECT_UNCOND_AO 2 @@ -2189,13 +2377,13 @@ void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int bank, uint64_t status, uint64_t mcg_status, uint64_t addr, uint64_t misc, int flags); -uint32_t cpu_cc_compute_all(CPUX86State *env1, int op); +uint32_t cpu_cc_compute_all(CPUX86State *env1); static inline uint32_t cpu_compute_eflags(CPUX86State *env) { uint32_t eflags = env->eflags; if (tcg_enabled()) { - eflags |= cpu_cc_compute_all(env, CC_OP) | (env->df & DF_MASK); + eflags |= cpu_cc_compute_all(env) | (env->df & DF_MASK); } return eflags; } @@ -2270,9 +2458,6 @@ static inline void cpu_set_fpuc(CPUX86State *env, uint16_t fpuc) } } -/* mem_helper.c */ -void helper_lock_init(void); - /* svm_helper.c */ #ifdef CONFIG_USER_ONLY static inline void @@ -2315,17 +2500,24 @@ typedef int X86CPUVersion; */ void x86_cpu_set_default_version(X86CPUVersion version); +#ifndef CONFIG_USER_ONLY + +void do_cpu_sipi(X86CPU *cpu); + #define APIC_DEFAULT_ADDRESS 0xfee00000 #define APIC_SPACE_SIZE 0x100000 /* cpu-dump.c */ void x86_cpu_dump_local_apic_state(CPUState *cs, int flags); +#endif + /* cpu.c */ bool cpu_is_bsp(X86CPU *cpu); void x86_cpu_xrstor_all_areas(X86CPU *cpu, const void *buf, uint32_t buflen); void x86_cpu_xsave_all_areas(X86CPU *cpu, void *buf, uint32_t buflen); +uint32_t xsave_area_size(uint64_t mask, bool compacted); void x86_update_hflags(CPUX86State* env); static inline bool hyperv_feat_enabled(X86CPU *cpu, int feat) @@ -2378,8 +2570,6 @@ static inline bool ctl_has_irq(CPUX86State *env) return (env->int_ctl & V_IRQ_MASK) && (int_prio >= tpr); } -hwaddr get_hphys(CPUState *cs, hwaddr gphys, MMUAccessType access_type, - int *prot); #if defined(TARGET_X86_64) && \ defined(CONFIG_USER_ONLY) && \ defined(CONFIG_LINUX) diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c index c3a2cf6f28..ebb000df6a 100644 --- a/target/i386/gdbstub.c +++ b/target/i386/gdbstub.c @@ -19,7 +19,7 @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "include/gdbstub/helpers.h" #ifdef TARGET_X86_64 static const int gpr_map[16] = { @@ -121,7 +121,9 @@ int x86_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) return gdb_get_reg32(mem_buf, env->regs[gpr_map32[n]]); } } else if (n >= IDX_FP_REGS && n < IDX_FP_REGS + 8) { - floatx80 *fp = (floatx80 *) &env->fpregs[n - IDX_FP_REGS]; + int st_index = n - IDX_FP_REGS; + int r_index = (st_index + env->fpstt) % 8; + floatx80 *fp = &env->fpregs[r_index].d; 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; diff --git a/target/i386/hax/hax-accel-ops.c b/target/i386/hax/hax-accel-ops.c deleted file mode 100644 index 18114fe34d..0000000000 --- a/target/i386/hax/hax-accel-ops.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * QEMU HAX support - * - * Copyright IBM, Corp. 2008 - * Red Hat, Inc. 2008 - * - * Authors: - * Anthony Liguori - * Glauber Costa - * - * Copyright (c) 2011 Intel Corporation - * Written by: - * Jiang Yunhong - * Xin Xiaohui - * Zhang Xiantao - * - * 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 "qemu/main-loop.h" -#include "sysemu/runstate.h" -#include "sysemu/cpus.h" -#include "qemu/guest-random.h" - -#include "hax-accel-ops.h" - -static void *hax_cpu_thread_fn(void *arg) -{ - CPUState *cpu = arg; - int r; - - rcu_register_thread(); - qemu_mutex_lock_iothread(); - qemu_thread_get_self(cpu->thread); - - cpu->thread_id = qemu_get_thread_id(); - current_cpu = cpu; - hax_init_vcpu(cpu); - cpu_thread_signal_created(cpu); - qemu_guest_random_seed_thread_part2(cpu->random_seed); - - do { - if (cpu_can_run(cpu)) { - r = hax_smp_cpu_exec(cpu); - if (r == EXCP_DEBUG) { - cpu_handle_guest_debug(cpu); - } - } - - qemu_wait_io_event(cpu); - } while (!cpu->unplug || cpu_can_run(cpu)); - rcu_unregister_thread(); - return NULL; -} - -static void hax_start_vcpu_thread(CPUState *cpu) -{ - char thread_name[VCPU_THREAD_NAME_SIZE]; - - cpu->thread = g_new0(QemuThread, 1); - cpu->halt_cond = g_new0(QemuCond, 1); - qemu_cond_init(cpu->halt_cond); - - snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HAX", - cpu->cpu_index); - qemu_thread_create(cpu->thread, thread_name, hax_cpu_thread_fn, - cpu, QEMU_THREAD_JOINABLE); -#ifdef _WIN32 - cpu->hThread = qemu_thread_get_handle(cpu->thread); -#endif -} - -static void hax_accel_ops_class_init(ObjectClass *oc, void *data) -{ - AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); - - ops->create_vcpu_thread = hax_start_vcpu_thread; - ops->kick_vcpu_thread = hax_kick_vcpu_thread; - - ops->synchronize_post_reset = hax_cpu_synchronize_post_reset; - ops->synchronize_post_init = hax_cpu_synchronize_post_init; - ops->synchronize_state = hax_cpu_synchronize_state; - ops->synchronize_pre_loadvm = hax_cpu_synchronize_pre_loadvm; -} - -static const TypeInfo hax_accel_ops_type = { - .name = ACCEL_OPS_NAME("hax"), - - .parent = TYPE_ACCEL_OPS, - .class_init = hax_accel_ops_class_init, - .abstract = true, -}; - -static void hax_accel_ops_register_types(void) -{ - type_register_static(&hax_accel_ops_type); -} -type_init(hax_accel_ops_register_types); diff --git a/target/i386/hax/hax-accel-ops.h b/target/i386/hax/hax-accel-ops.h deleted file mode 100644 index 9e357e7b40..0000000000 --- a/target/i386/hax/hax-accel-ops.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Accelerator CPUS Interface - * - * Copyright 2020 SUSE LLC - * - * 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 TARGET_I386_HAX_ACCEL_OPS_H -#define TARGET_I386_HAX_ACCEL_OPS_H - -#include "sysemu/cpus.h" - -#include "hax-interface.h" -#include "hax-i386.h" - -int hax_init_vcpu(CPUState *cpu); -int hax_smp_cpu_exec(CPUState *cpu); -int hax_populate_ram(uint64_t va, uint64_t size); - -void hax_cpu_synchronize_state(CPUState *cpu); -void hax_cpu_synchronize_post_reset(CPUState *cpu); -void hax_cpu_synchronize_post_init(CPUState *cpu); -void hax_cpu_synchronize_pre_loadvm(CPUState *cpu); - -int hax_vcpu_destroy(CPUState *cpu); -void hax_raise_event(CPUState *cpu); -void hax_reset_vcpu_state(void *opaque); - -#endif /* TARGET_I386_HAX_ACCEL_OPS_H */ diff --git a/target/i386/hax/hax-all.c b/target/i386/hax/hax-all.c deleted file mode 100644 index b185ee8de4..0000000000 --- a/target/i386/hax/hax-all.c +++ /dev/null @@ -1,1134 +0,0 @@ -/* - * QEMU HAX support - * - * Copyright IBM, Corp. 2008 - * Red Hat, Inc. 2008 - * - * Authors: - * Anthony Liguori - * Glauber Costa - * - * Copyright (c) 2011 Intel Corporation - * Written by: - * Jiang Yunhong - * Xin Xiaohui - * Zhang Xiantao - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -/* - * HAX common code for both windows and darwin - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/address-spaces.h" - -#include "qemu/accel.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "hw/boards.h" - -#include "hax-accel-ops.h" - -#define DEBUG_HAX 0 - -#define DPRINTF(fmt, ...) \ - do { \ - if (DEBUG_HAX) { \ - fprintf(stdout, fmt, ## __VA_ARGS__); \ - } \ - } while (0) - -/* Current version */ -const uint32_t hax_cur_version = 0x4; /* API v4: unmapping and MMIO moves */ -/* Minimum HAX kernel version */ -const uint32_t hax_min_version = 0x4; /* API v4: supports unmapping */ - -bool hax_allowed; - -struct hax_state hax_global; - -static void hax_vcpu_sync_state(CPUArchState *env, int modified); -static int hax_arch_get_registers(CPUArchState *env); - -int valid_hax_tunnel_size(uint16_t size) -{ - return size >= sizeof(struct hax_tunnel); -} - -hax_fd hax_vcpu_get_fd(CPUArchState *env) -{ - struct hax_vcpu_state *vcpu = env_cpu(env)->hax_vcpu; - if (!vcpu) { - return HAX_INVALID_FD; - } - return vcpu->fd; -} - -static int hax_get_capability(struct hax_state *hax) -{ - int ret; - struct hax_capabilityinfo capinfo, *cap = &capinfo; - - ret = hax_capability(hax, cap); - if (ret) { - return ret; - } - - if ((cap->wstatus & HAX_CAP_WORKSTATUS_MASK) == HAX_CAP_STATUS_NOTWORKING) { - if (cap->winfo & HAX_CAP_FAILREASON_VT) { - DPRINTF - ("VTX feature is not enabled, HAX driver will not work.\n"); - } else if (cap->winfo & HAX_CAP_FAILREASON_NX) { - DPRINTF - ("NX feature is not enabled, HAX driver will not work.\n"); - } - return -ENXIO; - - } - - if (!(cap->winfo & HAX_CAP_UG)) { - fprintf(stderr, "UG mode is not supported by the hardware.\n"); - return -ENOTSUP; - } - - hax->supports_64bit_ramblock = !!(cap->winfo & HAX_CAP_64BIT_RAMBLOCK); - - if (cap->wstatus & HAX_CAP_MEMQUOTA) { - if (cap->mem_quota < hax->mem_quota) { - fprintf(stderr, "The VM memory needed exceeds the driver limit.\n"); - return -ENOSPC; - } - } - return 0; -} - -static int hax_version_support(struct hax_state *hax) -{ - int ret; - struct hax_module_version version; - - ret = hax_mod_version(hax, &version); - if (ret < 0) { - return 0; - } - - if (hax_min_version > version.cur_version) { - fprintf(stderr, "Incompatible HAX module version %d,", - version.cur_version); - fprintf(stderr, "requires minimum version %d\n", hax_min_version); - return 0; - } - if (hax_cur_version < version.compat_version) { - fprintf(stderr, "Incompatible QEMU HAX API version %x,", - hax_cur_version); - fprintf(stderr, "requires minimum HAX API version %x\n", - version.compat_version); - return 0; - } - - return 1; -} - -int hax_vcpu_create(int id) -{ - struct hax_vcpu_state *vcpu = NULL; - int ret; - - if (!hax_global.vm) { - fprintf(stderr, "vcpu %x created failed, vm is null\n", id); - return -1; - } - - if (hax_global.vm->vcpus[id]) { - fprintf(stderr, "vcpu %x allocated already\n", id); - return 0; - } - - vcpu = g_new0(struct hax_vcpu_state, 1); - - ret = hax_host_create_vcpu(hax_global.vm->fd, id); - if (ret) { - fprintf(stderr, "Failed to create vcpu %x\n", id); - goto error; - } - - vcpu->vcpu_id = id; - vcpu->fd = hax_host_open_vcpu(hax_global.vm->id, id); - if (hax_invalid_fd(vcpu->fd)) { - fprintf(stderr, "Failed to open the vcpu\n"); - ret = -ENODEV; - goto error; - } - - hax_global.vm->vcpus[id] = vcpu; - - ret = hax_host_setup_vcpu_channel(vcpu); - if (ret) { - fprintf(stderr, "Invalid hax tunnel size\n"); - ret = -EINVAL; - goto error; - } - return 0; - - error: - /* vcpu and tunnel will be closed automatically */ - if (vcpu && !hax_invalid_fd(vcpu->fd)) { - hax_close_fd(vcpu->fd); - } - - hax_global.vm->vcpus[id] = NULL; - g_free(vcpu); - return -1; -} - -int hax_vcpu_destroy(CPUState *cpu) -{ - struct hax_vcpu_state *vcpu = cpu->hax_vcpu; - - if (!hax_global.vm) { - fprintf(stderr, "vcpu %x destroy failed, vm is null\n", vcpu->vcpu_id); - return -1; - } - - if (!vcpu) { - return 0; - } - - /* - * 1. The hax_tunnel is also destroyed when vcpu is destroyed - * 2. close fd will cause hax module vcpu be cleaned - */ - hax_close_fd(vcpu->fd); - hax_global.vm->vcpus[vcpu->vcpu_id] = NULL; - g_free(vcpu); - return 0; -} - -int hax_init_vcpu(CPUState *cpu) -{ - int ret; - - ret = hax_vcpu_create(cpu->cpu_index); - if (ret < 0) { - fprintf(stderr, "Failed to create HAX vcpu\n"); - exit(-1); - } - - cpu->hax_vcpu = hax_global.vm->vcpus[cpu->cpu_index]; - cpu->vcpu_dirty = true; - qemu_register_reset(hax_reset_vcpu_state, cpu->env_ptr); - - return ret; -} - -struct hax_vm *hax_vm_create(struct hax_state *hax, int max_cpus) -{ - struct hax_vm *vm; - int vm_id = 0, ret, i; - - if (hax_invalid_fd(hax->fd)) { - return NULL; - } - - if (hax->vm) { - return hax->vm; - } - - if (max_cpus > HAX_MAX_VCPU) { - fprintf(stderr, "Maximum VCPU number QEMU supported is %d\n", HAX_MAX_VCPU); - return NULL; - } - - vm = g_new0(struct hax_vm, 1); - - ret = hax_host_create_vm(hax, &vm_id); - if (ret) { - fprintf(stderr, "Failed to create vm %x\n", ret); - goto error; - } - vm->id = vm_id; - vm->fd = hax_host_open_vm(hax, vm_id); - if (hax_invalid_fd(vm->fd)) { - fprintf(stderr, "Failed to open vm %d\n", vm_id); - goto error; - } - - vm->numvcpus = max_cpus; - vm->vcpus = g_new0(struct hax_vcpu_state *, vm->numvcpus); - for (i = 0; i < vm->numvcpus; i++) { - vm->vcpus[i] = NULL; - } - - hax->vm = vm; - return vm; - - error: - g_free(vm); - hax->vm = NULL; - return NULL; -} - -int hax_vm_destroy(struct hax_vm *vm) -{ - int i; - - for (i = 0; i < vm->numvcpus; i++) - if (vm->vcpus[i]) { - fprintf(stderr, "VCPU should be cleaned before vm clean\n"); - return -1; - } - hax_close_fd(vm->fd); - vm->numvcpus = 0; - g_free(vm->vcpus); - g_free(vm); - hax_global.vm = NULL; - return 0; -} - -static int hax_init(ram_addr_t ram_size, int max_cpus) -{ - struct hax_state *hax = NULL; - struct hax_qemu_version qversion; - int ret; - - hax = &hax_global; - - memset(hax, 0, sizeof(struct hax_state)); - hax->mem_quota = ram_size; - - hax->fd = hax_mod_open(); - if (hax_invalid_fd(hax->fd)) { - hax->fd = 0; - ret = -ENODEV; - goto error; - } - - ret = hax_get_capability(hax); - - if (ret) { - if (ret != -ENOSPC) { - ret = -EINVAL; - } - goto error; - } - - if (!hax_version_support(hax)) { - ret = -EINVAL; - goto error; - } - - hax->vm = hax_vm_create(hax, max_cpus); - if (!hax->vm) { - fprintf(stderr, "Failed to create HAX VM\n"); - ret = -EINVAL; - goto error; - } - - hax_memory_init(); - - qversion.cur_version = hax_cur_version; - qversion.min_version = hax_min_version; - hax_notify_qemu_version(hax->vm->fd, &qversion); - - return ret; - error: - if (hax->vm) { - hax_vm_destroy(hax->vm); - } - if (hax->fd) { - hax_mod_close(hax); - } - - return ret; -} - -static int hax_accel_init(MachineState *ms) -{ - int ret = hax_init(ms->ram_size, (int)ms->smp.max_cpus); - - if (ret && (ret != -ENOSPC)) { - fprintf(stderr, "No accelerator found.\n"); - } else { - fprintf(stdout, "HAX is %s and emulator runs in %s mode.\n", - !ret ? "working" : "not working", - !ret ? "fast virt" : "emulation"); - } - return ret; -} - -static int hax_handle_fastmmio(CPUArchState *env, struct hax_fastmmio *hft) -{ - if (hft->direction < 2) { - cpu_physical_memory_rw(hft->gpa, &hft->value, hft->size, - hft->direction); - } else { - /* - * HAX API v4 supports transferring data between two MMIO addresses, - * hft->gpa and hft->gpa2 (instructions such as MOVS require this): - * hft->direction == 2: gpa ==> gpa2 - */ - uint64_t value; - cpu_physical_memory_read(hft->gpa, &value, hft->size); - cpu_physical_memory_write(hft->gpa2, &value, hft->size); - } - - return 0; -} - -static int hax_handle_io(CPUArchState *env, uint32_t df, uint16_t port, - int direction, int size, int count, void *buffer) -{ - uint8_t *ptr; - int i; - MemTxAttrs attrs = { 0 }; - - if (!df) { - ptr = (uint8_t *) buffer; - } else { - ptr = buffer + size * count - size; - } - for (i = 0; i < count; i++) { - address_space_rw(&address_space_io, port, attrs, - ptr, size, direction == HAX_EXIT_IO_OUT); - if (!df) { - ptr += size; - } else { - ptr -= size; - } - } - - return 0; -} - -static int hax_vcpu_interrupt(CPUArchState *env) -{ - CPUState *cpu = env_cpu(env); - struct hax_vcpu_state *vcpu = cpu->hax_vcpu; - struct hax_tunnel *ht = vcpu->tunnel; - - /* - * Try to inject an interrupt if the guest can accept it - * Unlike KVM, HAX kernel check for the eflags, instead of qemu - */ - if (ht->ready_for_interrupt_injection && - (cpu->interrupt_request & CPU_INTERRUPT_HARD)) { - int irq; - - irq = cpu_get_pic_interrupt(env); - if (irq >= 0) { - hax_inject_interrupt(env, irq); - cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; - } - } - - /* If we have an interrupt but the guest is not ready to receive an - * interrupt, request an interrupt window exit. This will - * cause a return to userspace as soon as the guest is ready to - * receive interrupts. */ - if ((cpu->interrupt_request & CPU_INTERRUPT_HARD)) { - ht->request_interrupt_window = 1; - } else { - ht->request_interrupt_window = 0; - } - return 0; -} - -void hax_raise_event(CPUState *cpu) -{ - struct hax_vcpu_state *vcpu = cpu->hax_vcpu; - - if (!vcpu) { - return; - } - vcpu->tunnel->user_event_pending = 1; -} - -/* - * Ask hax kernel module to run the CPU for us till: - * 1. Guest crash or shutdown - * 2. Need QEMU's emulation like guest execute MMIO instruction - * 3. Guest execute HLT - * 4. QEMU have Signal/event pending - * 5. An unknown VMX exit happens - */ -static int hax_vcpu_hax_exec(CPUArchState *env) -{ - int ret = 0; - CPUState *cpu = env_cpu(env); - X86CPU *x86_cpu = X86_CPU(cpu); - struct hax_vcpu_state *vcpu = cpu->hax_vcpu; - struct hax_tunnel *ht = vcpu->tunnel; - - if (!hax_enabled()) { - DPRINTF("Trying to vcpu execute at eip:" TARGET_FMT_lx "\n", env->eip); - return 0; - } - - if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { - cpu->interrupt_request &= ~CPU_INTERRUPT_POLL; - apic_poll_irq(x86_cpu->apic_state); - } - - /* After a vcpu is halted (either because it is an AP and has just been - * reset, or because it has executed the HLT instruction), it will not be - * run (hax_vcpu_run()) until it is unhalted. The next few if blocks check - * for events that may change the halted state of this vcpu: - * a) Maskable interrupt, when RFLAGS.IF is 1; - * Note: env->eflags may not reflect the current RFLAGS state, because - * it is not updated after each hax_vcpu_run(). We cannot afford - * to fail to recognize any unhalt-by-maskable-interrupt event - * (in which case the vcpu will halt forever), and yet we cannot - * afford the overhead of hax_vcpu_sync_state(). The current - * solution is to err on the side of caution and have the HLT - * handler (see case HAX_EXIT_HLT below) unconditionally set the - * IF_MASK bit in env->eflags, which, in effect, disables the - * RFLAGS.IF check. - * b) NMI; - * c) INIT signal; - * d) SIPI signal. - */ - if (((cpu->interrupt_request & CPU_INTERRUPT_HARD) && - (env->eflags & IF_MASK)) || - (cpu->interrupt_request & CPU_INTERRUPT_NMI)) { - cpu->halted = 0; - } - - if (cpu->interrupt_request & CPU_INTERRUPT_INIT) { - DPRINTF("\nhax_vcpu_hax_exec: handling INIT for %d\n", - cpu->cpu_index); - do_cpu_init(x86_cpu); - hax_vcpu_sync_state(env, 1); - } - - if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) { - DPRINTF("hax_vcpu_hax_exec: handling SIPI for %d\n", - cpu->cpu_index); - hax_vcpu_sync_state(env, 0); - do_cpu_sipi(x86_cpu); - hax_vcpu_sync_state(env, 1); - } - - if (cpu->halted) { - /* If this vcpu is halted, we must not ask HAXM to run it. Instead, we - * break out of hax_smp_cpu_exec() as if this vcpu had executed HLT. - * That way, this vcpu thread will be trapped in qemu_wait_io_event(), - * until the vcpu is unhalted. - */ - cpu->exception_index = EXCP_HLT; - return 0; - } - - do { - int hax_ret; - - if (cpu->exit_request) { - ret = 1; - break; - } - - hax_vcpu_interrupt(env); - - qemu_mutex_unlock_iothread(); - cpu_exec_start(cpu); - hax_ret = hax_vcpu_run(vcpu); - cpu_exec_end(cpu); - qemu_mutex_lock_iothread(); - - /* Simply continue the vcpu_run if system call interrupted */ - if (hax_ret == -EINTR || hax_ret == -EAGAIN) { - DPRINTF("io window interrupted\n"); - continue; - } - - if (hax_ret < 0) { - fprintf(stderr, "vcpu run failed for vcpu %x\n", vcpu->vcpu_id); - abort(); - } - switch (ht->_exit_status) { - case HAX_EXIT_IO: - ret = hax_handle_io(env, ht->pio._df, ht->pio._port, - ht->pio._direction, - ht->pio._size, ht->pio._count, vcpu->iobuf); - break; - case HAX_EXIT_FAST_MMIO: - ret = hax_handle_fastmmio(env, (struct hax_fastmmio *) vcpu->iobuf); - break; - /* Guest state changed, currently only for shutdown */ - case HAX_EXIT_STATECHANGE: - fprintf(stdout, "VCPU shutdown request\n"); - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); - hax_vcpu_sync_state(env, 0); - ret = 1; - break; - case HAX_EXIT_UNKNOWN_VMEXIT: - fprintf(stderr, "Unknown VMX exit %x from guest\n", - ht->_exit_reason); - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - hax_vcpu_sync_state(env, 0); - cpu_dump_state(cpu, stderr, 0); - ret = -1; - break; - case HAX_EXIT_HLT: - if (!(cpu->interrupt_request & CPU_INTERRUPT_HARD) && - !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) { - /* hlt instruction with interrupt disabled is shutdown */ - env->eflags |= IF_MASK; - cpu->halted = 1; - cpu->exception_index = EXCP_HLT; - ret = 1; - } - break; - /* these situations will continue to hax module */ - case HAX_EXIT_INTERRUPT: - case HAX_EXIT_PAUSED: - break; - case HAX_EXIT_MMIO: - /* Should not happen on UG system */ - fprintf(stderr, "HAX: unsupported MMIO emulation\n"); - ret = -1; - break; - case HAX_EXIT_REAL: - /* Should not happen on UG system */ - fprintf(stderr, "HAX: unimplemented real mode emulation\n"); - ret = -1; - break; - default: - fprintf(stderr, "Unknown exit %x from HAX\n", ht->_exit_status); - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - hax_vcpu_sync_state(env, 0); - cpu_dump_state(cpu, stderr, 0); - ret = 1; - break; - } - } while (!ret); - - if (cpu->exit_request) { - cpu->exit_request = 0; - cpu->exception_index = EXCP_INTERRUPT; - } - return ret < 0; -} - -static void do_hax_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) -{ - CPUArchState *env = cpu->env_ptr; - - hax_arch_get_registers(env); - cpu->vcpu_dirty = true; -} - -void hax_cpu_synchronize_state(CPUState *cpu) -{ - if (!cpu->vcpu_dirty) { - run_on_cpu(cpu, do_hax_cpu_synchronize_state, RUN_ON_CPU_NULL); - } -} - -static void do_hax_cpu_synchronize_post_reset(CPUState *cpu, - run_on_cpu_data arg) -{ - CPUArchState *env = cpu->env_ptr; - - hax_vcpu_sync_state(env, 1); - cpu->vcpu_dirty = false; -} - -void hax_cpu_synchronize_post_reset(CPUState *cpu) -{ - run_on_cpu(cpu, do_hax_cpu_synchronize_post_reset, RUN_ON_CPU_NULL); -} - -static void do_hax_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) -{ - CPUArchState *env = cpu->env_ptr; - - hax_vcpu_sync_state(env, 1); - cpu->vcpu_dirty = false; -} - -void hax_cpu_synchronize_post_init(CPUState *cpu) -{ - run_on_cpu(cpu, do_hax_cpu_synchronize_post_init, RUN_ON_CPU_NULL); -} - -static void do_hax_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg) -{ - cpu->vcpu_dirty = true; -} - -void hax_cpu_synchronize_pre_loadvm(CPUState *cpu) -{ - run_on_cpu(cpu, do_hax_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL); -} - -int hax_smp_cpu_exec(CPUState *cpu) -{ - CPUArchState *env = cpu->env_ptr; - int fatal; - int ret; - - while (1) { - if (cpu->exception_index >= EXCP_INTERRUPT) { - ret = cpu->exception_index; - cpu->exception_index = -1; - break; - } - - fatal = hax_vcpu_hax_exec(env); - - if (fatal) { - fprintf(stderr, "Unsupported HAX vcpu return\n"); - abort(); - } - } - - return ret; -} - -static void set_v8086_seg(struct segment_desc_t *lhs, const SegmentCache *rhs) -{ - memset(lhs, 0, sizeof(struct segment_desc_t)); - lhs->selector = rhs->selector; - lhs->base = rhs->base; - lhs->limit = rhs->limit; - lhs->type = 3; - lhs->present = 1; - lhs->dpl = 3; - lhs->operand_size = 0; - lhs->desc = 1; - lhs->long_mode = 0; - lhs->granularity = 0; - lhs->available = 0; -} - -static void get_seg(SegmentCache *lhs, const struct segment_desc_t *rhs) -{ - lhs->selector = rhs->selector; - lhs->base = rhs->base; - lhs->limit = rhs->limit; - lhs->flags = (rhs->type << DESC_TYPE_SHIFT) - | (rhs->present * DESC_P_MASK) - | (rhs->dpl << DESC_DPL_SHIFT) - | (rhs->operand_size << DESC_B_SHIFT) - | (rhs->desc * DESC_S_MASK) - | (rhs->long_mode << DESC_L_SHIFT) - | (rhs->granularity * DESC_G_MASK) | (rhs->available * DESC_AVL_MASK); -} - -static void set_seg(struct segment_desc_t *lhs, const SegmentCache *rhs) -{ - unsigned flags = rhs->flags; - - memset(lhs, 0, sizeof(struct segment_desc_t)); - lhs->selector = rhs->selector; - lhs->base = rhs->base; - lhs->limit = rhs->limit; - lhs->type = (flags >> DESC_TYPE_SHIFT) & 15; - lhs->present = (flags & DESC_P_MASK) != 0; - lhs->dpl = rhs->selector & 3; - lhs->operand_size = (flags >> DESC_B_SHIFT) & 1; - lhs->desc = (flags & DESC_S_MASK) != 0; - lhs->long_mode = (flags >> DESC_L_SHIFT) & 1; - lhs->granularity = (flags & DESC_G_MASK) != 0; - lhs->available = (flags & DESC_AVL_MASK) != 0; -} - -static void hax_getput_reg(uint64_t *hax_reg, target_ulong *qemu_reg, int set) -{ - target_ulong reg = *hax_reg; - - if (set) { - *hax_reg = *qemu_reg; - } else { - *qemu_reg = reg; - } -} - -/* The sregs has been synced with HAX kernel already before this call */ -static int hax_get_segments(CPUArchState *env, struct vcpu_state_t *sregs) -{ - get_seg(&env->segs[R_CS], &sregs->_cs); - get_seg(&env->segs[R_DS], &sregs->_ds); - get_seg(&env->segs[R_ES], &sregs->_es); - get_seg(&env->segs[R_FS], &sregs->_fs); - get_seg(&env->segs[R_GS], &sregs->_gs); - get_seg(&env->segs[R_SS], &sregs->_ss); - - get_seg(&env->tr, &sregs->_tr); - get_seg(&env->ldt, &sregs->_ldt); - env->idt.limit = sregs->_idt.limit; - env->idt.base = sregs->_idt.base; - env->gdt.limit = sregs->_gdt.limit; - env->gdt.base = sregs->_gdt.base; - return 0; -} - -static int hax_set_segments(CPUArchState *env, struct vcpu_state_t *sregs) -{ - if ((env->eflags & VM_MASK)) { - set_v8086_seg(&sregs->_cs, &env->segs[R_CS]); - set_v8086_seg(&sregs->_ds, &env->segs[R_DS]); - set_v8086_seg(&sregs->_es, &env->segs[R_ES]); - set_v8086_seg(&sregs->_fs, &env->segs[R_FS]); - set_v8086_seg(&sregs->_gs, &env->segs[R_GS]); - set_v8086_seg(&sregs->_ss, &env->segs[R_SS]); - } else { - set_seg(&sregs->_cs, &env->segs[R_CS]); - set_seg(&sregs->_ds, &env->segs[R_DS]); - set_seg(&sregs->_es, &env->segs[R_ES]); - set_seg(&sregs->_fs, &env->segs[R_FS]); - set_seg(&sregs->_gs, &env->segs[R_GS]); - set_seg(&sregs->_ss, &env->segs[R_SS]); - - if (env->cr[0] & CR0_PE_MASK) { - /* force ss cpl to cs cpl */ - sregs->_ss.selector = (sregs->_ss.selector & ~3) | - (sregs->_cs.selector & 3); - sregs->_ss.dpl = sregs->_ss.selector & 3; - } - } - - set_seg(&sregs->_tr, &env->tr); - set_seg(&sregs->_ldt, &env->ldt); - sregs->_idt.limit = env->idt.limit; - sregs->_idt.base = env->idt.base; - sregs->_gdt.limit = env->gdt.limit; - sregs->_gdt.base = env->gdt.base; - return 0; -} - -static int hax_sync_vcpu_register(CPUArchState *env, int set) -{ - struct vcpu_state_t regs; - int ret; - memset(®s, 0, sizeof(struct vcpu_state_t)); - - if (!set) { - ret = hax_sync_vcpu_state(env, ®s, 0); - if (ret < 0) { - return -1; - } - } - - /* generic register */ - hax_getput_reg(®s._rax, &env->regs[R_EAX], set); - hax_getput_reg(®s._rbx, &env->regs[R_EBX], set); - hax_getput_reg(®s._rcx, &env->regs[R_ECX], set); - hax_getput_reg(®s._rdx, &env->regs[R_EDX], set); - hax_getput_reg(®s._rsi, &env->regs[R_ESI], set); - hax_getput_reg(®s._rdi, &env->regs[R_EDI], set); - hax_getput_reg(®s._rsp, &env->regs[R_ESP], set); - hax_getput_reg(®s._rbp, &env->regs[R_EBP], set); -#ifdef TARGET_X86_64 - hax_getput_reg(®s._r8, &env->regs[8], set); - hax_getput_reg(®s._r9, &env->regs[9], set); - hax_getput_reg(®s._r10, &env->regs[10], set); - hax_getput_reg(®s._r11, &env->regs[11], set); - hax_getput_reg(®s._r12, &env->regs[12], set); - hax_getput_reg(®s._r13, &env->regs[13], set); - hax_getput_reg(®s._r14, &env->regs[14], set); - hax_getput_reg(®s._r15, &env->regs[15], set); -#endif - hax_getput_reg(®s._rflags, &env->eflags, set); - hax_getput_reg(®s._rip, &env->eip, set); - - if (set) { - regs._cr0 = env->cr[0]; - regs._cr2 = env->cr[2]; - regs._cr3 = env->cr[3]; - regs._cr4 = env->cr[4]; - hax_set_segments(env, ®s); - } else { - env->cr[0] = regs._cr0; - env->cr[2] = regs._cr2; - env->cr[3] = regs._cr3; - env->cr[4] = regs._cr4; - hax_get_segments(env, ®s); - } - - if (set) { - ret = hax_sync_vcpu_state(env, ®s, 1); - if (ret < 0) { - return -1; - } - } - return 0; -} - -static void hax_msr_entry_set(struct vmx_msr *item, uint32_t index, - uint64_t value) -{ - item->entry = index; - item->value = value; -} - -static int hax_get_msrs(CPUArchState *env) -{ - struct hax_msr_data md; - struct vmx_msr *msrs = md.entries; - int ret, i, n; - - n = 0; - msrs[n++].entry = MSR_IA32_SYSENTER_CS; - msrs[n++].entry = MSR_IA32_SYSENTER_ESP; - msrs[n++].entry = MSR_IA32_SYSENTER_EIP; - msrs[n++].entry = MSR_IA32_TSC; -#ifdef TARGET_X86_64 - msrs[n++].entry = MSR_EFER; - msrs[n++].entry = MSR_STAR; - msrs[n++].entry = MSR_LSTAR; - msrs[n++].entry = MSR_CSTAR; - msrs[n++].entry = MSR_FMASK; - msrs[n++].entry = MSR_KERNELGSBASE; -#endif - md.nr_msr = n; - ret = hax_sync_msr(env, &md, 0); - if (ret < 0) { - return ret; - } - - for (i = 0; i < md.done; i++) { - switch (msrs[i].entry) { - case MSR_IA32_SYSENTER_CS: - env->sysenter_cs = msrs[i].value; - break; - case MSR_IA32_SYSENTER_ESP: - env->sysenter_esp = msrs[i].value; - break; - case MSR_IA32_SYSENTER_EIP: - env->sysenter_eip = msrs[i].value; - break; - case MSR_IA32_TSC: - env->tsc = msrs[i].value; - break; -#ifdef TARGET_X86_64 - case MSR_EFER: - env->efer = msrs[i].value; - break; - case MSR_STAR: - env->star = msrs[i].value; - break; - case MSR_LSTAR: - env->lstar = msrs[i].value; - break; - case MSR_CSTAR: - env->cstar = msrs[i].value; - break; - case MSR_FMASK: - env->fmask = msrs[i].value; - break; - case MSR_KERNELGSBASE: - env->kernelgsbase = msrs[i].value; - break; -#endif - } - } - - return 0; -} - -static int hax_set_msrs(CPUArchState *env) -{ - struct hax_msr_data md; - struct vmx_msr *msrs; - msrs = md.entries; - int n = 0; - - memset(&md, 0, sizeof(struct hax_msr_data)); - hax_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_CS, env->sysenter_cs); - hax_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_ESP, env->sysenter_esp); - hax_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_EIP, env->sysenter_eip); - hax_msr_entry_set(&msrs[n++], MSR_IA32_TSC, env->tsc); -#ifdef TARGET_X86_64 - hax_msr_entry_set(&msrs[n++], MSR_EFER, env->efer); - hax_msr_entry_set(&msrs[n++], MSR_STAR, env->star); - hax_msr_entry_set(&msrs[n++], MSR_LSTAR, env->lstar); - hax_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar); - hax_msr_entry_set(&msrs[n++], MSR_FMASK, env->fmask); - hax_msr_entry_set(&msrs[n++], MSR_KERNELGSBASE, env->kernelgsbase); -#endif - md.nr_msr = n; - md.done = 0; - - return hax_sync_msr(env, &md, 1); -} - -static int hax_get_fpu(CPUArchState *env) -{ - struct fx_layout fpu; - int i, ret; - - ret = hax_sync_fpu(env, &fpu, 0); - if (ret < 0) { - return ret; - } - - env->fpstt = (fpu.fsw >> 11) & 7; - env->fpus = fpu.fsw; - env->fpuc = fpu.fcw; - for (i = 0; i < 8; ++i) { - env->fptags[i] = !((fpu.ftw >> i) & 1); - } - memcpy(env->fpregs, fpu.st_mm, sizeof(env->fpregs)); - - for (i = 0; i < 8; i++) { - env->xmm_regs[i].ZMM_Q(0) = ldq_p(&fpu.mmx_1[i][0]); - env->xmm_regs[i].ZMM_Q(1) = ldq_p(&fpu.mmx_1[i][8]); - if (CPU_NB_REGS > 8) { - env->xmm_regs[i + 8].ZMM_Q(0) = ldq_p(&fpu.mmx_2[i][0]); - env->xmm_regs[i + 8].ZMM_Q(1) = ldq_p(&fpu.mmx_2[i][8]); - } - } - env->mxcsr = fpu.mxcsr; - - return 0; -} - -static int hax_set_fpu(CPUArchState *env) -{ - struct fx_layout fpu; - int i; - - memset(&fpu, 0, sizeof(fpu)); - fpu.fsw = env->fpus & ~(7 << 11); - fpu.fsw |= (env->fpstt & 7) << 11; - fpu.fcw = env->fpuc; - - for (i = 0; i < 8; ++i) { - fpu.ftw |= (!env->fptags[i]) << i; - } - - memcpy(fpu.st_mm, env->fpregs, sizeof(env->fpregs)); - for (i = 0; i < 8; i++) { - stq_p(&fpu.mmx_1[i][0], env->xmm_regs[i].ZMM_Q(0)); - stq_p(&fpu.mmx_1[i][8], env->xmm_regs[i].ZMM_Q(1)); - if (CPU_NB_REGS > 8) { - stq_p(&fpu.mmx_2[i][0], env->xmm_regs[i + 8].ZMM_Q(0)); - stq_p(&fpu.mmx_2[i][8], env->xmm_regs[i + 8].ZMM_Q(1)); - } - } - - fpu.mxcsr = env->mxcsr; - - return hax_sync_fpu(env, &fpu, 1); -} - -static int hax_arch_get_registers(CPUArchState *env) -{ - int ret; - - ret = hax_sync_vcpu_register(env, 0); - if (ret < 0) { - return ret; - } - - ret = hax_get_fpu(env); - if (ret < 0) { - return ret; - } - - ret = hax_get_msrs(env); - if (ret < 0) { - return ret; - } - - x86_update_hflags(env); - return 0; -} - -static int hax_arch_set_registers(CPUArchState *env) -{ - int ret; - ret = hax_sync_vcpu_register(env, 1); - - if (ret < 0) { - fprintf(stderr, "Failed to sync vcpu reg\n"); - return ret; - } - ret = hax_set_fpu(env); - if (ret < 0) { - fprintf(stderr, "FPU failed\n"); - return ret; - } - ret = hax_set_msrs(env); - if (ret < 0) { - fprintf(stderr, "MSR failed\n"); - return ret; - } - - return 0; -} - -static void hax_vcpu_sync_state(CPUArchState *env, int modified) -{ - if (hax_enabled()) { - if (modified) { - hax_arch_set_registers(env); - } else { - hax_arch_get_registers(env); - } - } -} - -/* - * much simpler than kvm, at least in first stage because: - * We don't need consider the device pass-through, we don't need - * consider the framebuffer, and we may even remove the bios at all - */ -int hax_sync_vcpus(void) -{ - if (hax_enabled()) { - CPUState *cpu; - - cpu = first_cpu; - if (!cpu) { - return 0; - } - - for (; cpu != NULL; cpu = CPU_NEXT(cpu)) { - int ret; - - ret = hax_arch_set_registers(cpu->env_ptr); - if (ret < 0) { - return ret; - } - } - } - - return 0; -} - -void hax_reset_vcpu_state(void *opaque) -{ - CPUState *cpu; - for (cpu = first_cpu; cpu != NULL; cpu = CPU_NEXT(cpu)) { - cpu->hax_vcpu->tunnel->user_event_pending = 0; - cpu->hax_vcpu->tunnel->ready_for_interrupt_injection = 0; - } -} - -static void hax_accel_class_init(ObjectClass *oc, void *data) -{ - AccelClass *ac = ACCEL_CLASS(oc); - ac->name = "HAX"; - ac->init_machine = hax_accel_init; - ac->allowed = &hax_allowed; -} - -static const TypeInfo hax_accel_type = { - .name = ACCEL_CLASS_NAME("hax"), - .parent = TYPE_ACCEL, - .class_init = hax_accel_class_init, -}; - -static void hax_type_init(void) -{ - type_register_static(&hax_accel_type); -} - -type_init(hax_type_init); diff --git a/target/i386/hax/hax-i386.h b/target/i386/hax/hax-i386.h deleted file mode 100644 index efbb346238..0000000000 --- a/target/i386/hax/hax-i386.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * QEMU HAXM support - * - * Copyright (c) 2011 Intel Corporation - * Written by: - * Jiang Yunhong - * - * 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 HAX_I386_H -#define HAX_I386_H - -#include "cpu.h" -#include "sysemu/hax.h" - -#ifdef CONFIG_POSIX -typedef int hax_fd; -#endif - -#ifdef CONFIG_WIN32 -typedef HANDLE hax_fd; -#endif - -extern struct hax_state hax_global; -struct hax_vcpu_state { - hax_fd fd; - int vcpu_id; - struct hax_tunnel *tunnel; - unsigned char *iobuf; -}; - -struct hax_state { - hax_fd fd; /* the global hax device interface */ - uint32_t version; - struct hax_vm *vm; - uint64_t mem_quota; - bool supports_64bit_ramblock; -}; - -#define HAX_MAX_VCPU 0x10 - -struct hax_vm { - hax_fd fd; - int id; - int numvcpus; - struct hax_vcpu_state **vcpus; -}; - -#ifdef NEED_CPU_H -/* Functions exported to host specific mode */ -hax_fd hax_vcpu_get_fd(CPUArchState *env); -int valid_hax_tunnel_size(uint16_t size); - -/* Host specific functions */ -int hax_mod_version(struct hax_state *hax, struct hax_module_version *version); -int hax_inject_interrupt(CPUArchState *env, int vector); -struct hax_vm *hax_vm_create(struct hax_state *hax, int max_cpus); -int hax_vcpu_run(struct hax_vcpu_state *vcpu); -int hax_vcpu_create(int id); -void hax_kick_vcpu_thread(CPUState *cpu); - -int hax_sync_vcpu_state(CPUArchState *env, struct vcpu_state_t *state, - int set); -int hax_sync_msr(CPUArchState *env, struct hax_msr_data *msrs, int set); -int hax_sync_fpu(CPUArchState *env, struct fx_layout *fl, int set); -#endif - -int hax_vm_destroy(struct hax_vm *vm); -int hax_capability(struct hax_state *hax, struct hax_capabilityinfo *cap); -int hax_notify_qemu_version(hax_fd vm_fd, struct hax_qemu_version *qversion); -int hax_set_ram(uint64_t start_pa, uint32_t size, uint64_t host_va, int flags); - -/* Common host function */ -int hax_host_create_vm(struct hax_state *hax, int *vm_id); -hax_fd hax_host_open_vm(struct hax_state *hax, int vm_id); -int hax_host_create_vcpu(hax_fd vm_fd, int vcpuid); -hax_fd hax_host_open_vcpu(int vmid, int vcpuid); -int hax_host_setup_vcpu_channel(struct hax_vcpu_state *vcpu); -hax_fd hax_mod_open(void); -void hax_memory_init(void); - - -#ifdef CONFIG_POSIX -#include "hax-posix.h" -#endif - -#ifdef CONFIG_WIN32 -#include "hax-windows.h" -#endif - -#include "hax-interface.h" - -#endif diff --git a/target/i386/hax/hax-interface.h b/target/i386/hax/hax-interface.h deleted file mode 100644 index 537ae084e9..0000000000 --- a/target/i386/hax/hax-interface.h +++ /dev/null @@ -1,369 +0,0 @@ -/* - * QEMU HAXM support - * - * Copyright (c) 2011 Intel Corporation - * Written by: - * Jiang Yunhong - * Xin Xiaohui - * Zhang Xiantao - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -/* Interface with HAX kernel module */ - -#ifndef HAX_INTERFACE_H -#define HAX_INTERFACE_H - -/* fx_layout has 3 formats table 3-56, 512bytes */ -struct fx_layout { - uint16_t fcw; - uint16_t fsw; - uint8_t ftw; - uint8_t res1; - uint16_t fop; - union { - struct { - uint32_t fip; - uint16_t fcs; - uint16_t res2; - }; - uint64_t fpu_ip; - }; - union { - struct { - uint32_t fdp; - uint16_t fds; - uint16_t res3; - }; - uint64_t fpu_dp; - }; - uint32_t mxcsr; - uint32_t mxcsr_mask; - uint8_t st_mm[8][16]; - uint8_t mmx_1[8][16]; - uint8_t mmx_2[8][16]; - uint8_t pad[96]; -} __attribute__ ((aligned(8))); - -struct vmx_msr { - uint64_t entry; - uint64_t value; -} __attribute__ ((__packed__)); - -/* - * Fixed array is not good, but it makes Mac support a bit easier by avoiding - * memory map or copyin staff. - */ -#define HAX_MAX_MSR_ARRAY 0x20 -struct hax_msr_data { - uint16_t nr_msr; - uint16_t done; - uint16_t pad[2]; - struct vmx_msr entries[HAX_MAX_MSR_ARRAY]; -} __attribute__ ((__packed__)); - -union interruptibility_state_t { - uint32_t raw; - struct { - uint32_t sti_blocking:1; - uint32_t movss_blocking:1; - uint32_t smi_blocking:1; - uint32_t nmi_blocking:1; - uint32_t reserved:28; - }; - uint64_t pad; -}; - -typedef union interruptibility_state_t interruptibility_state_t; - -/* Segment descriptor */ -struct segment_desc_t { - uint16_t selector; - uint16_t _dummy; - uint32_t limit; - uint64_t base; - union { - struct { - uint32_t type:4; - uint32_t desc:1; - uint32_t dpl:2; - uint32_t present:1; - uint32_t:4; - uint32_t available:1; - uint32_t long_mode:1; - uint32_t operand_size:1; - uint32_t granularity:1; - uint32_t null:1; - uint32_t:15; - }; - uint32_t ar; - }; - uint32_t ipad; -}; - -typedef struct segment_desc_t segment_desc_t; - -struct vcpu_state_t { - union { - uint64_t _regs[16]; - struct { - union { - struct { - uint8_t _al, _ah; - }; - uint16_t _ax; - uint32_t _eax; - uint64_t _rax; - }; - union { - struct { - uint8_t _cl, _ch; - }; - uint16_t _cx; - uint32_t _ecx; - uint64_t _rcx; - }; - union { - struct { - uint8_t _dl, _dh; - }; - uint16_t _dx; - uint32_t _edx; - uint64_t _rdx; - }; - union { - struct { - uint8_t _bl, _bh; - }; - uint16_t _bx; - uint32_t _ebx; - uint64_t _rbx; - }; - union { - uint16_t _sp; - uint32_t _esp; - uint64_t _rsp; - }; - union { - uint16_t _bp; - uint32_t _ebp; - uint64_t _rbp; - }; - union { - uint16_t _si; - uint32_t _esi; - uint64_t _rsi; - }; - union { - uint16_t _di; - uint32_t _edi; - uint64_t _rdi; - }; - - uint64_t _r8; - uint64_t _r9; - uint64_t _r10; - uint64_t _r11; - uint64_t _r12; - uint64_t _r13; - uint64_t _r14; - uint64_t _r15; - }; - }; - - union { - uint32_t _eip; - uint64_t _rip; - }; - - union { - uint32_t _eflags; - uint64_t _rflags; - }; - - segment_desc_t _cs; - segment_desc_t _ss; - segment_desc_t _ds; - segment_desc_t _es; - segment_desc_t _fs; - segment_desc_t _gs; - segment_desc_t _ldt; - segment_desc_t _tr; - - segment_desc_t _gdt; - segment_desc_t _idt; - - uint64_t _cr0; - uint64_t _cr2; - uint64_t _cr3; - uint64_t _cr4; - - uint64_t _dr0; - uint64_t _dr1; - uint64_t _dr2; - uint64_t _dr3; - uint64_t _dr6; - uint64_t _dr7; - uint64_t _pde; - - uint32_t _efer; - - uint32_t _sysenter_cs; - uint64_t _sysenter_eip; - uint64_t _sysenter_esp; - - uint32_t _activity_state; - uint32_t pad; - interruptibility_state_t _interruptibility_state; -}; - -/* HAX exit status */ -enum exit_status { - /* IO port request */ - HAX_EXIT_IO = 1, - /* MMIO instruction emulation */ - HAX_EXIT_MMIO, - /* QEMU emulation mode request, currently means guest enter non-PG mode */ - HAX_EXIT_REAL, - /* - * Interrupt window open, qemu can inject interrupt now - * Also used when signal pending since at that time qemu usually need - * check interrupt - */ - HAX_EXIT_INTERRUPT, - /* Unknown vmexit, mostly trigger reboot */ - HAX_EXIT_UNKNOWN_VMEXIT, - /* HALT from guest */ - HAX_EXIT_HLT, - /* Reboot request, like because of tripple fault in guest */ - HAX_EXIT_STATECHANGE, - /* the vcpu is now only paused when destroy, so simply return to hax */ - HAX_EXIT_PAUSED, - HAX_EXIT_FAST_MMIO, -}; - -/* - * The interface definition: - * 1. vcpu_run execute will return 0 on success, otherwise mean failed - * 2. exit_status return the exit reason, as stated in enum exit_status - * 3. exit_reason is the vmx exit reason - */ -struct hax_tunnel { - uint32_t _exit_reason; - uint32_t _exit_flag; - uint32_t _exit_status; - uint32_t user_event_pending; - int ready_for_interrupt_injection; - int request_interrupt_window; - union { - struct { - /* 0: read, 1: write */ -#define HAX_EXIT_IO_IN 1 -#define HAX_EXIT_IO_OUT 0 - uint8_t _direction; - uint8_t _df; - uint16_t _size; - uint16_t _port; - uint16_t _count; - uint8_t _flags; - uint8_t _pad0; - uint16_t _pad1; - uint32_t _pad2; - uint64_t _vaddr; - } pio; - struct { - uint64_t gla; - } mmio; - struct { - } state; - }; -} __attribute__ ((__packed__)); - -struct hax_module_version { - uint32_t compat_version; - uint32_t cur_version; -} __attribute__ ((__packed__)); - -/* This interface is support only after API version 2 */ -struct hax_qemu_version { - /* Current API version in QEMU */ - uint32_t cur_version; - /* The minimum API version supported by QEMU */ - uint32_t min_version; -} __attribute__ ((__packed__)); - -/* The mac specfic interface to qemu, mostly is ioctl related */ -struct hax_tunnel_info { - uint64_t va; - uint64_t io_va; - uint16_t size; - uint16_t pad[3]; -} __attribute__ ((__packed__)); - -struct hax_alloc_ram_info { - uint32_t size; - uint32_t pad; - uint64_t va; -} __attribute__ ((__packed__)); - -struct hax_ramblock_info { - uint64_t start_va; - uint64_t size; - uint64_t reserved; -} __attribute__ ((__packed__)); - -#define HAX_RAM_INFO_ROM 0x01 /* Read-Only */ -#define HAX_RAM_INFO_INVALID 0x80 /* Unmapped, usually used for MMIO */ -struct hax_set_ram_info { - uint64_t pa_start; - uint32_t size; - uint8_t flags; - uint8_t pad[3]; - uint64_t va; -} __attribute__ ((__packed__)); - -#define HAX_CAP_STATUS_WORKING 0x1 -#define HAX_CAP_STATUS_NOTWORKING 0x0 -#define HAX_CAP_WORKSTATUS_MASK 0x1 - -#define HAX_CAP_FAILREASON_VT 0x1 -#define HAX_CAP_FAILREASON_NX 0x2 - -#define HAX_CAP_MEMQUOTA 0x2 -#define HAX_CAP_UG 0x4 -#define HAX_CAP_64BIT_RAMBLOCK 0x8 - -struct hax_capabilityinfo { - /* bit 0: 1 - working - * 0 - not working, possibly because NT/NX disabled - * bit 1: 1 - memory limitation working - * 0 - no memory limitation - */ - uint16_t wstatus; - /* valid when not working - * bit 0: VT not enabeld - * bit 1: NX not enabled*/ - uint16_t winfo; - uint32_t pad; - uint64_t mem_quota; -} __attribute__ ((__packed__)); - -struct hax_fastmmio { - uint64_t gpa; - union { - uint64_t value; - uint64_t gpa2; /* since HAX API v4 */ - }; - uint8_t size; - uint8_t direction; - uint16_t reg_index; - uint32_t pad0; - uint64_t _cr0; - uint64_t _cr2; - uint64_t _cr3; - uint64_t _cr4; -} __attribute__ ((__packed__)); -#endif diff --git a/target/i386/hax/hax-mem.c b/target/i386/hax/hax-mem.c deleted file mode 100644 index 05dbe8cce3..0000000000 --- a/target/i386/hax/hax-mem.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * HAX memory mapping operations - * - * Copyright (c) 2015-16 Intel Corporation - * Copyright 2016 Google, 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 "cpu.h" -#include "exec/address-spaces.h" -#include "qemu/error-report.h" - -#include "hax-accel-ops.h" -#include "qemu/queue.h" - -#define DEBUG_HAX_MEM 0 - -#define DPRINTF(fmt, ...) \ - do { \ - if (DEBUG_HAX_MEM) { \ - fprintf(stdout, fmt, ## __VA_ARGS__); \ - } \ - } while (0) - -/** - * HAXMapping: describes a pending guest physical memory mapping - * - * @start_pa: a guest physical address marking the start of the region; must be - * page-aligned - * @size: a guest physical address marking the end of the region; must be - * page-aligned - * @host_va: the host virtual address of the start of the mapping - * @flags: mapping parameters e.g. HAX_RAM_INFO_ROM or HAX_RAM_INFO_INVALID - * @entry: additional fields for linking #HAXMapping instances together - */ -typedef struct HAXMapping { - uint64_t start_pa; - uint32_t size; - uint64_t host_va; - int flags; - QTAILQ_ENTRY(HAXMapping) entry; -} HAXMapping; - -/* - * A doubly-linked list (actually a tail queue) of the pending page mappings - * for the ongoing memory transaction. - * - * It is used to optimize the number of page mapping updates done through the - * kernel module. For example, it's effective when a driver is digging an MMIO - * hole inside an existing memory mapping. It will get a deletion of the whole - * region, then the addition of the 2 remaining RAM areas around the hole and - * finally the memory transaction commit. During the commit, it will effectively - * send to the kernel only the removal of the pages from the MMIO hole after - * having computed locally the result of the deletion and additions. - */ -static QTAILQ_HEAD(, HAXMapping) mappings = - QTAILQ_HEAD_INITIALIZER(mappings); - -/** - * hax_mapping_dump_list: dumps @mappings to stdout (for debugging) - */ -static void hax_mapping_dump_list(void) -{ - HAXMapping *entry; - - DPRINTF("%s updates:\n", __func__); - QTAILQ_FOREACH(entry, &mappings, entry) { - DPRINTF("\t%c 0x%016" PRIx64 "->0x%016" PRIx64 " VA 0x%016" PRIx64 - "%s\n", entry->flags & HAX_RAM_INFO_INVALID ? '-' : '+', - entry->start_pa, entry->start_pa + entry->size, entry->host_va, - entry->flags & HAX_RAM_INFO_ROM ? " ROM" : ""); - } -} - -static void hax_insert_mapping_before(HAXMapping *next, uint64_t start_pa, - uint32_t size, uint64_t host_va, - uint8_t flags) -{ - HAXMapping *entry; - - entry = g_malloc0(sizeof(*entry)); - entry->start_pa = start_pa; - entry->size = size; - entry->host_va = host_va; - entry->flags = flags; - if (!next) { - QTAILQ_INSERT_TAIL(&mappings, entry, entry); - } else { - QTAILQ_INSERT_BEFORE(next, entry, entry); - } -} - -static bool hax_mapping_is_opposite(HAXMapping *entry, uint64_t host_va, - uint8_t flags) -{ - /* removed then added without change for the read-only flag */ - bool nop_flags = (entry->flags ^ flags) == HAX_RAM_INFO_INVALID; - - return (entry->host_va == host_va) && nop_flags; -} - -static void hax_update_mapping(uint64_t start_pa, uint32_t size, - uint64_t host_va, uint8_t flags) -{ - uint64_t end_pa = start_pa + size; - HAXMapping *entry, *next; - - QTAILQ_FOREACH_SAFE(entry, &mappings, entry, next) { - uint32_t chunk_sz; - if (start_pa >= entry->start_pa + entry->size) { - continue; - } - if (start_pa < entry->start_pa) { - chunk_sz = end_pa <= entry->start_pa ? size - : entry->start_pa - start_pa; - hax_insert_mapping_before(entry, start_pa, chunk_sz, - host_va, flags); - start_pa += chunk_sz; - host_va += chunk_sz; - size -= chunk_sz; - } else if (start_pa > entry->start_pa) { - /* split the existing chunk at start_pa */ - chunk_sz = start_pa - entry->start_pa; - hax_insert_mapping_before(entry, entry->start_pa, chunk_sz, - entry->host_va, entry->flags); - entry->start_pa += chunk_sz; - entry->host_va += chunk_sz; - entry->size -= chunk_sz; - } - /* now start_pa == entry->start_pa */ - chunk_sz = MIN(size, entry->size); - if (chunk_sz) { - bool nop = hax_mapping_is_opposite(entry, host_va, flags); - bool partial = chunk_sz < entry->size; - if (partial) { - /* remove the beginning of the existing chunk */ - entry->start_pa += chunk_sz; - entry->host_va += chunk_sz; - entry->size -= chunk_sz; - if (!nop) { - hax_insert_mapping_before(entry, start_pa, chunk_sz, - host_va, flags); - } - } else { /* affects the full mapping entry */ - if (nop) { /* no change to this mapping, remove it */ - QTAILQ_REMOVE(&mappings, entry, entry); - g_free(entry); - } else { /* update mapping properties */ - entry->host_va = host_va; - entry->flags = flags; - } - } - start_pa += chunk_sz; - host_va += chunk_sz; - size -= chunk_sz; - } - if (!size) { /* we are done */ - break; - } - } - if (size) { /* add the leftover */ - hax_insert_mapping_before(NULL, start_pa, size, host_va, flags); - } -} - -static void hax_process_section(MemoryRegionSection *section, uint8_t flags) -{ - MemoryRegion *mr = section->mr; - hwaddr start_pa = section->offset_within_address_space; - ram_addr_t size = int128_get64(section->size); - unsigned int delta; - uint64_t host_va; - uint32_t max_mapping_size; - - /* We only care about RAM and ROM regions */ - if (!memory_region_is_ram(mr)) { - if (memory_region_is_romd(mr)) { - /* HAXM kernel module does not support ROMD yet */ - warn_report("Ignoring ROMD region 0x%016" PRIx64 "->0x%016" PRIx64, - start_pa, start_pa + size); - } - return; - } - - /* Adjust start_pa and size so that they are page-aligned. (Cf - * kvm_set_phys_mem() in kvm-all.c). - */ - delta = qemu_real_host_page_size() - (start_pa & ~qemu_real_host_page_mask()); - delta &= ~qemu_real_host_page_mask(); - if (delta > size) { - return; - } - start_pa += delta; - size -= delta; - size &= qemu_real_host_page_mask(); - if (!size || (start_pa & ~qemu_real_host_page_mask())) { - return; - } - - host_va = (uintptr_t)memory_region_get_ram_ptr(mr) - + section->offset_within_region + delta; - if (memory_region_is_rom(section->mr)) { - flags |= HAX_RAM_INFO_ROM; - } - - /* - * The kernel module interface uses 32-bit sizes: - * https://github.com/intel/haxm/blob/master/API.md#hax_vm_ioctl_set_ram - * - * If the mapping size is longer than 32 bits, we can't process it in one - * call into the kernel. Instead, we split the mapping into smaller ones, - * and call hax_update_mapping() on each. - */ - max_mapping_size = UINT32_MAX & qemu_real_host_page_mask(); - while (size > max_mapping_size) { - hax_update_mapping(start_pa, max_mapping_size, host_va, flags); - start_pa += max_mapping_size; - size -= max_mapping_size; - host_va += max_mapping_size; - } - /* Now size <= max_mapping_size */ - hax_update_mapping(start_pa, (uint32_t)size, host_va, flags); -} - -static void hax_region_add(MemoryListener *listener, - MemoryRegionSection *section) -{ - memory_region_ref(section->mr); - hax_process_section(section, 0); -} - -static void hax_region_del(MemoryListener *listener, - MemoryRegionSection *section) -{ - hax_process_section(section, HAX_RAM_INFO_INVALID); - memory_region_unref(section->mr); -} - -static void hax_transaction_begin(MemoryListener *listener) -{ - g_assert(QTAILQ_EMPTY(&mappings)); -} - -static void hax_transaction_commit(MemoryListener *listener) -{ - if (!QTAILQ_EMPTY(&mappings)) { - HAXMapping *entry, *next; - - if (DEBUG_HAX_MEM) { - hax_mapping_dump_list(); - } - QTAILQ_FOREACH_SAFE(entry, &mappings, entry, next) { - if (entry->flags & HAX_RAM_INFO_INVALID) { - /* for unmapping, put the values expected by the kernel */ - entry->flags = HAX_RAM_INFO_INVALID; - entry->host_va = 0; - } - if (hax_set_ram(entry->start_pa, entry->size, - entry->host_va, entry->flags)) { - fprintf(stderr, "%s: Failed mapping @0x%016" PRIx64 "+0x%" - PRIx32 " flags %02x\n", __func__, entry->start_pa, - entry->size, entry->flags); - } - QTAILQ_REMOVE(&mappings, entry, entry); - g_free(entry); - } - } -} - -/* currently we fake the dirty bitmap sync, always dirty */ -static void hax_log_sync(MemoryListener *listener, - MemoryRegionSection *section) -{ - MemoryRegion *mr = section->mr; - - if (!memory_region_is_ram(mr)) { - /* Skip MMIO regions */ - return; - } - - memory_region_set_dirty(mr, 0, int128_get64(section->size)); -} - -static MemoryListener hax_memory_listener = { - .name = "hax", - .begin = hax_transaction_begin, - .commit = hax_transaction_commit, - .region_add = hax_region_add, - .region_del = hax_region_del, - .log_sync = hax_log_sync, - .priority = 10, -}; - -static void hax_ram_block_added(RAMBlockNotifier *n, void *host, size_t size, - size_t max_size) -{ - /* - * We must register each RAM block with the HAXM kernel module, or - * hax_set_ram() will fail for any mapping into the RAM block: - * https://github.com/intel/haxm/blob/master/API.md#hax_vm_ioctl_alloc_ram - * - * Old versions of the HAXM kernel module (< 6.2.0) used to preallocate all - * host physical pages for the RAM block as part of this registration - * process, hence the name hax_populate_ram(). - */ - if (hax_populate_ram((uint64_t)(uintptr_t)host, max_size) < 0) { - fprintf(stderr, "HAX failed to populate RAM\n"); - abort(); - } -} - -static struct RAMBlockNotifier hax_ram_notifier = { - .ram_block_added = hax_ram_block_added, -}; - -void hax_memory_init(void) -{ - ram_block_notifier_add(&hax_ram_notifier); - memory_listener_register(&hax_memory_listener, &address_space_memory); -} diff --git a/target/i386/hax/hax-posix.c b/target/i386/hax/hax-posix.c deleted file mode 100644 index ac1a51096e..0000000000 --- a/target/i386/hax/hax-posix.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * QEMU HAXM support - * - * Copyright (c) 2011 Intel Corporation - * Written by: - * Jiang Yunhong - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -/* HAX module interface - darwin version */ -#include "qemu/osdep.h" -#include - -#include "sysemu/cpus.h" -#include "hax-accel-ops.h" - -hax_fd hax_mod_open(void) -{ - int fd = open("/dev/HAX", O_RDWR); - if (fd == -1) { - fprintf(stderr, "Failed to open the hax module\n"); - } - - qemu_set_cloexec(fd); - - return fd; -} - -int hax_populate_ram(uint64_t va, uint64_t size) -{ - int ret; - - if (!hax_global.vm || !hax_global.vm->fd) { - fprintf(stderr, "Allocate memory before vm create?\n"); - return -EINVAL; - } - - if (hax_global.supports_64bit_ramblock) { - struct hax_ramblock_info ramblock = { - .start_va = va, - .size = size, - .reserved = 0 - }; - - ret = ioctl(hax_global.vm->fd, HAX_VM_IOCTL_ADD_RAMBLOCK, &ramblock); - } else { - struct hax_alloc_ram_info info = { - .size = (uint32_t)size, - .pad = 0, - .va = va - }; - - ret = ioctl(hax_global.vm->fd, HAX_VM_IOCTL_ALLOC_RAM, &info); - } - if (ret < 0) { - fprintf(stderr, "Failed to register RAM block: ret=%d, va=0x%" PRIx64 - ", size=0x%" PRIx64 ", method=%s\n", ret, va, size, - hax_global.supports_64bit_ramblock ? "new" : "legacy"); - return ret; - } - return 0; -} - -int hax_set_ram(uint64_t start_pa, uint32_t size, uint64_t host_va, int flags) -{ - struct hax_set_ram_info info; - int ret; - - info.pa_start = start_pa; - info.size = size; - info.va = host_va; - info.flags = (uint8_t) flags; - - ret = ioctl(hax_global.vm->fd, HAX_VM_IOCTL_SET_RAM, &info); - if (ret < 0) { - return -errno; - } - return 0; -} - -int hax_capability(struct hax_state *hax, struct hax_capabilityinfo *cap) -{ - int ret; - - ret = ioctl(hax->fd, HAX_IOCTL_CAPABILITY, cap); - if (ret == -1) { - fprintf(stderr, "Failed to get HAX capability\n"); - return -errno; - } - - return 0; -} - -int hax_mod_version(struct hax_state *hax, struct hax_module_version *version) -{ - int ret; - - ret = ioctl(hax->fd, HAX_IOCTL_VERSION, version); - if (ret == -1) { - fprintf(stderr, "Failed to get HAX version\n"); - return -errno; - } - - return 0; -} - -static char *hax_vm_devfs_string(int vm_id) -{ - return g_strdup_printf("/dev/hax_vm/vm%02d", vm_id); -} - -static char *hax_vcpu_devfs_string(int vm_id, int vcpu_id) -{ - 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) -{ - int ret; - int vm_id = 0; - - if (hax_invalid_fd(hax->fd)) { - return -EINVAL; - } - - if (hax->vm) { - return 0; - } - - ret = ioctl(hax->fd, HAX_IOCTL_CREATE_VM, &vm_id); - *vmid = vm_id; - return ret; -} - -hax_fd hax_host_open_vm(struct hax_state *hax, int vm_id) -{ - hax_fd fd; - char *vm_name = NULL; - - vm_name = hax_vm_devfs_string(vm_id); - if (!vm_name) { - return -1; - } - - fd = open(vm_name, O_RDWR); - g_free(vm_name); - - qemu_set_cloexec(fd); - - return fd; -} - -int hax_notify_qemu_version(hax_fd vm_fd, struct hax_qemu_version *qversion) -{ - int ret; - - if (hax_invalid_fd(vm_fd)) { - return -EINVAL; - } - - ret = ioctl(vm_fd, HAX_VM_IOCTL_NOTIFY_QEMU_VERSION, qversion); - - if (ret < 0) { - fprintf(stderr, "Failed to notify qemu API version\n"); - return ret; - } - return 0; -} - -/* Simply assume the size should be bigger than the hax_tunnel, - * since the hax_tunnel can be extended later with compatibility considered - */ -int hax_host_create_vcpu(hax_fd vm_fd, int vcpuid) -{ - int ret; - - ret = ioctl(vm_fd, HAX_VM_IOCTL_VCPU_CREATE, &vcpuid); - if (ret < 0) { - fprintf(stderr, "Failed to create vcpu %x\n", vcpuid); - } - - return ret; -} - -hax_fd hax_host_open_vcpu(int vmid, int vcpuid) -{ - char *devfs_path = NULL; - hax_fd fd; - - devfs_path = hax_vcpu_devfs_string(vmid, vcpuid); - if (!devfs_path) { - fprintf(stderr, "Failed to get the devfs\n"); - return -EINVAL; - } - - fd = open(devfs_path, O_RDWR); - g_free(devfs_path); - if (fd < 0) { - fprintf(stderr, "Failed to open the vcpu devfs\n"); - } - qemu_set_cloexec(fd); - return fd; -} - -int hax_host_setup_vcpu_channel(struct hax_vcpu_state *vcpu) -{ - int ret; - struct hax_tunnel_info info; - - ret = ioctl(vcpu->fd, HAX_VCPU_IOCTL_SETUP_TUNNEL, &info); - if (ret) { - fprintf(stderr, "Failed to setup the hax tunnel\n"); - return ret; - } - - if (!valid_hax_tunnel_size(info.size)) { - fprintf(stderr, "Invalid hax tunnel size %x\n", info.size); - ret = -EINVAL; - return ret; - } - - vcpu->tunnel = (struct hax_tunnel *) (intptr_t) (info.va); - vcpu->iobuf = (unsigned char *) (intptr_t) (info.io_va); - return 0; -} - -int hax_vcpu_run(struct hax_vcpu_state *vcpu) -{ - return ioctl(vcpu->fd, HAX_VCPU_IOCTL_RUN, NULL); -} - -int hax_sync_fpu(CPUArchState *env, struct fx_layout *fl, int set) -{ - int ret, fd; - - fd = hax_vcpu_get_fd(env); - if (fd <= 0) { - return -1; - } - - if (set) { - ret = ioctl(fd, HAX_VCPU_IOCTL_SET_FPU, fl); - } else { - ret = ioctl(fd, HAX_VCPU_IOCTL_GET_FPU, fl); - } - return ret; -} - -int hax_sync_msr(CPUArchState *env, struct hax_msr_data *msrs, int set) -{ - int ret, fd; - - fd = hax_vcpu_get_fd(env); - if (fd <= 0) { - return -1; - } - if (set) { - ret = ioctl(fd, HAX_VCPU_IOCTL_SET_MSRS, msrs); - } else { - ret = ioctl(fd, HAX_VCPU_IOCTL_GET_MSRS, msrs); - } - return ret; -} - -int hax_sync_vcpu_state(CPUArchState *env, struct vcpu_state_t *state, int set) -{ - int ret, fd; - - fd = hax_vcpu_get_fd(env); - if (fd <= 0) { - return -1; - } - - if (set) { - ret = ioctl(fd, HAX_VCPU_SET_REGS, state); - } else { - ret = ioctl(fd, HAX_VCPU_GET_REGS, state); - } - return ret; -} - -int hax_inject_interrupt(CPUArchState *env, int vector) -{ - int fd; - - fd = hax_vcpu_get_fd(env); - if (fd <= 0) { - return -1; - } - - return ioctl(fd, HAX_VCPU_IOCTL_INTERRUPT, &vector); -} - -void hax_kick_vcpu_thread(CPUState *cpu) -{ - /* - * FIXME: race condition with the exit_request check in - * hax_vcpu_hax_exec - */ - cpu->exit_request = 1; - cpus_kick_thread(cpu); -} diff --git a/target/i386/hax/hax-posix.h b/target/i386/hax/hax-posix.h deleted file mode 100644 index fb7c64426d..0000000000 --- a/target/i386/hax/hax-posix.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * QEMU HAXM support - * - * Copyright (c) 2011 Intel Corporation - * Written by: - * Jiang Yunhong - * Xin Xiaohui - * Zhang Xiantao - * - * 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 TARGET_I386_HAX_POSIX_H -#define TARGET_I386_HAX_POSIX_H - -#include - -#define HAX_INVALID_FD (-1) -static inline int hax_invalid_fd(hax_fd fd) -{ - return fd <= 0; -} - -static inline void hax_mod_close(struct hax_state *hax) -{ - close(hax->fd); -} - -static inline void hax_close_fd(hax_fd fd) -{ - close(fd); -} - -/* HAX model level ioctl */ -#define HAX_IOCTL_VERSION _IOWR(0, 0x20, struct hax_module_version) -#define HAX_IOCTL_CREATE_VM _IOWR(0, 0x21, uint32_t) -#define HAX_IOCTL_DESTROY_VM _IOW(0, 0x22, uint32_t) -#define HAX_IOCTL_CAPABILITY _IOR(0, 0x23, struct hax_capabilityinfo) - -#define HAX_VM_IOCTL_VCPU_CREATE _IOWR(0, 0x80, uint32_t) -#define HAX_VM_IOCTL_ALLOC_RAM _IOWR(0, 0x81, struct hax_alloc_ram_info) -#define HAX_VM_IOCTL_SET_RAM _IOWR(0, 0x82, struct hax_set_ram_info) -#define HAX_VM_IOCTL_VCPU_DESTROY _IOW(0, 0x83, uint32_t) -#define HAX_VM_IOCTL_NOTIFY_QEMU_VERSION _IOW(0, 0x84, struct hax_qemu_version) -#define HAX_VM_IOCTL_ADD_RAMBLOCK _IOW(0, 0x85, struct hax_ramblock_info) - -#define HAX_VCPU_IOCTL_RUN _IO(0, 0xc0) -#define HAX_VCPU_IOCTL_SET_MSRS _IOWR(0, 0xc1, struct hax_msr_data) -#define HAX_VCPU_IOCTL_GET_MSRS _IOWR(0, 0xc2, struct hax_msr_data) - -#define HAX_VCPU_IOCTL_SET_FPU _IOW(0, 0xc3, struct fx_layout) -#define HAX_VCPU_IOCTL_GET_FPU _IOR(0, 0xc4, struct fx_layout) - -#define HAX_VCPU_IOCTL_SETUP_TUNNEL _IOWR(0, 0xc5, struct hax_tunnel_info) -#define HAX_VCPU_IOCTL_INTERRUPT _IOWR(0, 0xc6, uint32_t) -#define HAX_VCPU_SET_REGS _IOWR(0, 0xc7, struct vcpu_state_t) -#define HAX_VCPU_GET_REGS _IOWR(0, 0xc8, struct vcpu_state_t) - -#endif /* TARGET_I386_HAX_POSIX_H */ diff --git a/target/i386/hax/hax-windows.c b/target/i386/hax/hax-windows.c deleted file mode 100644 index 59afa213a6..0000000000 --- a/target/i386/hax/hax-windows.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - * QEMU HAXM support - * - * Copyright (c) 2011 Intel Corporation - * Written by: - * Jiang Yunhong - * - * 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 "cpu.h" -#include "hax-accel-ops.h" - -/* - * return 0 when success, -1 when driver not loaded, - * other negative value for other failure - */ -static int hax_open_device(hax_fd *fd) -{ - uint32_t errNum = 0; - HANDLE hDevice; - - if (!fd) { - return -2; - } - - hDevice = CreateFile("\\\\.\\HAX", - GENERIC_READ | GENERIC_WRITE, - 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - if (hDevice == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Failed to open the HAX device!\n"); - errNum = GetLastError(); - if (errNum == ERROR_FILE_NOT_FOUND) { - return -1; - } - return -2; - } - *fd = hDevice; - return 0; -} - -/* hax_fd hax_mod_open */ - hax_fd hax_mod_open(void) -{ - int ret; - hax_fd fd = NULL; - - ret = hax_open_device(&fd); - if (ret != 0) { - fprintf(stderr, "Open HAX device failed\n"); - } - - return fd; -} - -int hax_populate_ram(uint64_t va, uint64_t size) -{ - int ret; - HANDLE hDeviceVM; - DWORD dSize = 0; - - if (!hax_global.vm || !hax_global.vm->fd) { - fprintf(stderr, "Allocate memory before vm create?\n"); - return -EINVAL; - } - - hDeviceVM = hax_global.vm->fd; - if (hax_global.supports_64bit_ramblock) { - struct hax_ramblock_info ramblock = { - .start_va = va, - .size = size, - .reserved = 0 - }; - - ret = DeviceIoControl(hDeviceVM, - HAX_VM_IOCTL_ADD_RAMBLOCK, - &ramblock, sizeof(ramblock), NULL, 0, &dSize, - (LPOVERLAPPED) NULL); - } else { - struct hax_alloc_ram_info info = { - .size = (uint32_t) size, - .pad = 0, - .va = va - }; - - ret = DeviceIoControl(hDeviceVM, - HAX_VM_IOCTL_ALLOC_RAM, - &info, sizeof(info), NULL, 0, &dSize, - (LPOVERLAPPED) NULL); - } - - if (!ret) { - fprintf(stderr, "Failed to register RAM block: va=0x%" PRIx64 - ", size=0x%" PRIx64 ", method=%s\n", va, size, - hax_global.supports_64bit_ramblock ? "new" : "legacy"); - return ret; - } - - return 0; -} - -int hax_set_ram(uint64_t start_pa, uint32_t size, uint64_t host_va, int flags) -{ - struct hax_set_ram_info info; - HANDLE hDeviceVM = hax_global.vm->fd; - DWORD dSize = 0; - int ret; - - info.pa_start = start_pa; - info.size = size; - info.va = host_va; - info.flags = (uint8_t) flags; - - ret = DeviceIoControl(hDeviceVM, HAX_VM_IOCTL_SET_RAM, - &info, sizeof(info), NULL, 0, &dSize, - (LPOVERLAPPED) NULL); - - if (!ret) { - return -EFAULT; - } else { - return 0; - } -} - -int hax_capability(struct hax_state *hax, struct hax_capabilityinfo *cap) -{ - int ret; - HANDLE hDevice = hax->fd; /* handle to hax module */ - DWORD dSize = 0; - DWORD err = 0; - - if (hax_invalid_fd(hDevice)) { - fprintf(stderr, "Invalid fd for hax device!\n"); - return -ENODEV; - } - - ret = DeviceIoControl(hDevice, HAX_IOCTL_CAPABILITY, NULL, 0, cap, - sizeof(*cap), &dSize, (LPOVERLAPPED) NULL); - - if (!ret) { - err = GetLastError(); - if (err == ERROR_INSUFFICIENT_BUFFER || err == ERROR_MORE_DATA) { - fprintf(stderr, "hax capability is too long to hold.\n"); - } - fprintf(stderr, "Failed to get Hax capability:%luu\n", err); - return -EFAULT; - } else { - return 0; - } -} - -int hax_mod_version(struct hax_state *hax, struct hax_module_version *version) -{ - int ret; - HANDLE hDevice = hax->fd; /* handle to hax module */ - DWORD dSize = 0; - DWORD err = 0; - - if (hax_invalid_fd(hDevice)) { - fprintf(stderr, "Invalid fd for hax device!\n"); - return -ENODEV; - } - - ret = DeviceIoControl(hDevice, - HAX_IOCTL_VERSION, - NULL, 0, - version, sizeof(*version), &dSize, - (LPOVERLAPPED) NULL); - - if (!ret) { - err = GetLastError(); - if (err == ERROR_INSUFFICIENT_BUFFER || err == ERROR_MORE_DATA) { - fprintf(stderr, "hax module verion is too long to hold.\n"); - } - fprintf(stderr, "Failed to get Hax module version:%lu\n", err); - return -EFAULT; - } else { - return 0; - } -} - -static char *hax_vm_devfs_string(int vm_id) -{ - return g_strdup_printf("\\\\.\\hax_vm%02d", vm_id); -} - -static char *hax_vcpu_devfs_string(int vm_id, int vcpu_id) -{ - return g_strdup_printf("\\\\.\\hax_vm%02d_vcpu%02d", vm_id, vcpu_id); -} - -int hax_host_create_vm(struct hax_state *hax, int *vmid) -{ - int ret; - int vm_id = 0; - DWORD dSize = 0; - - if (hax_invalid_fd(hax->fd)) { - return -EINVAL; - } - - if (hax->vm) { - return 0; - } - - ret = DeviceIoControl(hax->fd, - HAX_IOCTL_CREATE_VM, - NULL, 0, &vm_id, sizeof(vm_id), &dSize, - (LPOVERLAPPED) NULL); - if (!ret) { - fprintf(stderr, "Failed to create VM. Error code: %lu\n", - GetLastError()); - return -1; - } - *vmid = vm_id; - return 0; -} - -hax_fd hax_host_open_vm(struct hax_state *hax, int vm_id) -{ - char *vm_name = NULL; - hax_fd hDeviceVM; - - vm_name = hax_vm_devfs_string(vm_id); - if (!vm_name) { - fprintf(stderr, "Failed to open VM. VM name is null\n"); - return INVALID_HANDLE_VALUE; - } - - hDeviceVM = CreateFile(vm_name, - GENERIC_READ | GENERIC_WRITE, - 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hDeviceVM == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Open the vm device error:%s, ec:%lu\n", - vm_name, GetLastError()); - } - - g_free(vm_name); - return hDeviceVM; -} - -int hax_notify_qemu_version(hax_fd vm_fd, struct hax_qemu_version *qversion) -{ - int ret; - DWORD dSize = 0; - if (hax_invalid_fd(vm_fd)) { - return -EINVAL; - } - ret = DeviceIoControl(vm_fd, - HAX_VM_IOCTL_NOTIFY_QEMU_VERSION, - qversion, sizeof(struct hax_qemu_version), - NULL, 0, &dSize, (LPOVERLAPPED) NULL); - if (!ret) { - fprintf(stderr, "Failed to notify qemu API version\n"); - return -1; - } - return 0; -} - -int hax_host_create_vcpu(hax_fd vm_fd, int vcpuid) -{ - int ret; - DWORD dSize = 0; - - ret = DeviceIoControl(vm_fd, - HAX_VM_IOCTL_VCPU_CREATE, - &vcpuid, sizeof(vcpuid), NULL, 0, &dSize, - (LPOVERLAPPED) NULL); - if (!ret) { - fprintf(stderr, "Failed to create vcpu %x\n", vcpuid); - return -1; - } - - return 0; -} - -hax_fd hax_host_open_vcpu(int vmid, int vcpuid) -{ - char *devfs_path = NULL; - hax_fd hDeviceVCPU; - - devfs_path = hax_vcpu_devfs_string(vmid, vcpuid); - if (!devfs_path) { - fprintf(stderr, "Failed to get the devfs\n"); - return INVALID_HANDLE_VALUE; - } - - hDeviceVCPU = CreateFile(devfs_path, - GENERIC_READ | GENERIC_WRITE, - 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, - NULL); - - if (hDeviceVCPU == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Failed to open the vcpu devfs\n"); - } - g_free(devfs_path); - return hDeviceVCPU; -} - -int hax_host_setup_vcpu_channel(struct hax_vcpu_state *vcpu) -{ - hax_fd hDeviceVCPU = vcpu->fd; - int ret; - struct hax_tunnel_info info; - DWORD dSize = 0; - - ret = DeviceIoControl(hDeviceVCPU, - HAX_VCPU_IOCTL_SETUP_TUNNEL, - NULL, 0, &info, sizeof(info), &dSize, - (LPOVERLAPPED) NULL); - if (!ret) { - fprintf(stderr, "Failed to setup the hax tunnel\n"); - return -1; - } - - if (!valid_hax_tunnel_size(info.size)) { - fprintf(stderr, "Invalid hax tunnel size %x\n", info.size); - ret = -EINVAL; - return ret; - } - vcpu->tunnel = (struct hax_tunnel *) (intptr_t) (info.va); - vcpu->iobuf = (unsigned char *) (intptr_t) (info.io_va); - return 0; -} - -int hax_vcpu_run(struct hax_vcpu_state *vcpu) -{ - int ret; - HANDLE hDeviceVCPU = vcpu->fd; - DWORD dSize = 0; - - ret = DeviceIoControl(hDeviceVCPU, - HAX_VCPU_IOCTL_RUN, - NULL, 0, NULL, 0, &dSize, (LPOVERLAPPED) NULL); - if (!ret) { - return -EFAULT; - } else { - return 0; - } -} - -int hax_sync_fpu(CPUArchState *env, struct fx_layout *fl, int set) -{ - int ret; - hax_fd fd; - HANDLE hDeviceVCPU; - DWORD dSize = 0; - - fd = hax_vcpu_get_fd(env); - if (hax_invalid_fd(fd)) { - return -1; - } - - hDeviceVCPU = fd; - - if (set) { - ret = DeviceIoControl(hDeviceVCPU, - HAX_VCPU_IOCTL_SET_FPU, - fl, sizeof(*fl), NULL, 0, &dSize, - (LPOVERLAPPED) NULL); - } else { - ret = DeviceIoControl(hDeviceVCPU, - HAX_VCPU_IOCTL_GET_FPU, - NULL, 0, fl, sizeof(*fl), &dSize, - (LPOVERLAPPED) NULL); - } - if (!ret) { - return -EFAULT; - } else { - return 0; - } -} - -int hax_sync_msr(CPUArchState *env, struct hax_msr_data *msrs, int set) -{ - int ret; - hax_fd fd; - HANDLE hDeviceVCPU; - DWORD dSize = 0; - - fd = hax_vcpu_get_fd(env); - if (hax_invalid_fd(fd)) { - return -1; - } - hDeviceVCPU = fd; - - if (set) { - ret = DeviceIoControl(hDeviceVCPU, - HAX_VCPU_IOCTL_SET_MSRS, - msrs, sizeof(*msrs), - msrs, sizeof(*msrs), &dSize, (LPOVERLAPPED) NULL); - } else { - ret = DeviceIoControl(hDeviceVCPU, - HAX_VCPU_IOCTL_GET_MSRS, - msrs, sizeof(*msrs), - msrs, sizeof(*msrs), &dSize, (LPOVERLAPPED) NULL); - } - if (!ret) { - return -EFAULT; - } else { - return 0; - } -} - -int hax_sync_vcpu_state(CPUArchState *env, struct vcpu_state_t *state, int set) -{ - int ret; - hax_fd fd; - HANDLE hDeviceVCPU; - DWORD dSize; - - fd = hax_vcpu_get_fd(env); - if (hax_invalid_fd(fd)) { - return -1; - } - - hDeviceVCPU = fd; - - if (set) { - ret = DeviceIoControl(hDeviceVCPU, - HAX_VCPU_SET_REGS, - state, sizeof(*state), - NULL, 0, &dSize, (LPOVERLAPPED) NULL); - } else { - ret = DeviceIoControl(hDeviceVCPU, - HAX_VCPU_GET_REGS, - NULL, 0, - state, sizeof(*state), &dSize, - (LPOVERLAPPED) NULL); - } - if (!ret) { - return -EFAULT; - } else { - return 0; - } -} - -int hax_inject_interrupt(CPUArchState *env, int vector) -{ - int ret; - hax_fd fd; - HANDLE hDeviceVCPU; - DWORD dSize; - - fd = hax_vcpu_get_fd(env); - if (hax_invalid_fd(fd)) { - return -1; - } - - hDeviceVCPU = fd; - - ret = DeviceIoControl(hDeviceVCPU, - HAX_VCPU_IOCTL_INTERRUPT, - &vector, sizeof(vector), NULL, 0, &dSize, - (LPOVERLAPPED) NULL); - if (!ret) { - return -EFAULT; - } else { - return 0; - } -} - -static void CALLBACK dummy_apc_func(ULONG_PTR unused) -{ -} - -void hax_kick_vcpu_thread(CPUState *cpu) -{ - /* - * FIXME: race condition with the exit_request check in - * hax_vcpu_hax_exec - */ - cpu->exit_request = 1; - if (!qemu_cpu_is_self(cpu)) { - if (!QueueUserAPC(dummy_apc_func, cpu->hThread, 0)) { - fprintf(stderr, "%s: QueueUserAPC failed with error %lu\n", - __func__, GetLastError()); - exit(1); - } - } -} diff --git a/target/i386/hax/hax-windows.h b/target/i386/hax/hax-windows.h deleted file mode 100644 index b1f5d4f32f..0000000000 --- a/target/i386/hax/hax-windows.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * QEMU HAXM support - * - * Copyright IBM, Corp. 2008 - * - * Authors: - * Anthony Liguori - * - * Copyright (c) 2011 Intel Corporation - * Written by: - * Jiang Yunhong - * Xin Xiaohui - * Zhang Xiantao - * - * 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 TARGET_I386_HAX_WINDOWS_H -#define TARGET_I386_HAX_WINDOWS_H - -#include -#include - -#include "hax-accel-ops.h" - -#define HAX_INVALID_FD INVALID_HANDLE_VALUE - -static inline void hax_mod_close(struct hax_state *hax) -{ - CloseHandle(hax->fd); -} - -static inline void hax_close_fd(hax_fd fd) -{ - CloseHandle(fd); -} - -static inline int hax_invalid_fd(hax_fd fd) -{ - return (fd == INVALID_HANDLE_VALUE); -} - -#define HAX_DEVICE_TYPE 0x4000 - -#define HAX_IOCTL_VERSION CTL_CODE(HAX_DEVICE_TYPE, 0x900, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) -#define HAX_IOCTL_CREATE_VM CTL_CODE(HAX_DEVICE_TYPE, 0x901, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) -#define HAX_IOCTL_CAPABILITY CTL_CODE(HAX_DEVICE_TYPE, 0x910, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define HAX_VM_IOCTL_VCPU_CREATE CTL_CODE(HAX_DEVICE_TYPE, 0x902, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) -#define HAX_VM_IOCTL_ALLOC_RAM CTL_CODE(HAX_DEVICE_TYPE, 0x903, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) -#define HAX_VM_IOCTL_SET_RAM CTL_CODE(HAX_DEVICE_TYPE, 0x904, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) -#define HAX_VM_IOCTL_VCPU_DESTROY CTL_CODE(HAX_DEVICE_TYPE, 0x905, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) -#define HAX_VM_IOCTL_ADD_RAMBLOCK CTL_CODE(HAX_DEVICE_TYPE, 0x913, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define HAX_VCPU_IOCTL_RUN CTL_CODE(HAX_DEVICE_TYPE, 0x906, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) -#define HAX_VCPU_IOCTL_SET_MSRS CTL_CODE(HAX_DEVICE_TYPE, 0x907, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) -#define HAX_VCPU_IOCTL_GET_MSRS CTL_CODE(HAX_DEVICE_TYPE, 0x908, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) -#define HAX_VCPU_IOCTL_SET_FPU CTL_CODE(HAX_DEVICE_TYPE, 0x909, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) -#define HAX_VCPU_IOCTL_GET_FPU CTL_CODE(HAX_DEVICE_TYPE, 0x90a, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define HAX_VCPU_IOCTL_SETUP_TUNNEL CTL_CODE(HAX_DEVICE_TYPE, 0x90b, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) -#define HAX_VCPU_IOCTL_INTERRUPT CTL_CODE(HAX_DEVICE_TYPE, 0x90c, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) -#define HAX_VCPU_SET_REGS CTL_CODE(HAX_DEVICE_TYPE, 0x90d, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) -#define HAX_VCPU_GET_REGS CTL_CODE(HAX_DEVICE_TYPE, 0x90e, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define HAX_VM_IOCTL_NOTIFY_QEMU_VERSION CTL_CODE(HAX_DEVICE_TYPE, 0x910, \ - METHOD_BUFFERED, \ - FILE_ANY_ACCESS) -#endif /* TARGET_I386_HAX_WINDOWS_H */ diff --git a/target/i386/hax/meson.build b/target/i386/hax/meson.build deleted file mode 100644 index d6c520fb6b..0000000000 --- a/target/i386/hax/meson.build +++ /dev/null @@ -1,7 +0,0 @@ -i386_softmmu_ss.add(when: 'CONFIG_HAX', if_true: files( - 'hax-all.c', - 'hax-mem.c', - 'hax-accel-ops.c', -)) -i386_softmmu_ss.add(when: ['CONFIG_HAX', 'CONFIG_POSIX'], if_true: files('hax-posix.c')) -i386_softmmu_ss.add(when: ['CONFIG_HAX', 'CONFIG_WIN32'], if_true: files('hax-windows.c')) diff --git a/target/i386/helper.c b/target/i386/helper.c index fa409e9c44..23ccb23a5b 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -22,12 +22,26 @@ #include "cpu.h" #include "exec/exec-all.h" #include "sysemu/runstate.h" -#include "kvm/kvm_i386.h" #ifndef CONFIG_USER_ONLY #include "sysemu/hw_accel.h" #include "monitor/monitor.h" +#include "kvm/kvm_i386.h" #endif #include "qemu/log.h" +#ifdef CONFIG_TCG +#include "tcg/insn-start-words.h" +#endif + +void cpu_sync_avx_hflag(CPUX86State *env) +{ + if ((env->cr[4] & CR4_OSXSAVE_MASK) + && (env->xcr0 & (XSTATE_SSE_MASK | XSTATE_YMM_MASK)) + == (XSTATE_SSE_MASK | XSTATE_YMM_MASK)) { + env->hflags |= HF_AVX_EN_MASK; + } else{ + env->hflags &= ~HF_AVX_EN_MASK; + } +} void cpu_sync_bndcs_hflags(CPUX86State *env) { @@ -209,6 +223,7 @@ void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4) env->hflags = hflags; cpu_sync_bndcs_hflags(env); + cpu_sync_avx_hflag(env); } #if !defined(CONFIG_USER_ONLY) @@ -497,6 +512,27 @@ void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int bank, } } +static inline target_ulong get_memio_eip(CPUX86State *env) +{ +#ifdef CONFIG_TCG + uint64_t data[TARGET_INSN_START_WORDS]; + CPUState *cs = env_cpu(env); + + if (!cpu_unwind_state_data(cs, cs->mem_io_pc, data)) { + return env->eip; + } + + /* Per x86_restore_state_to_opc. */ + if (cs->tcg_cflags & CF_PCREL) { + return (env->eip & TARGET_PAGE_MASK) | data[0]; + } else { + return data[0] - env->segs[R_CS].base; + } +#else + qemu_build_not_reached(); +#endif +} + void cpu_report_tpr_access(CPUX86State *env, TPRAccess access) { X86CPU *cpu = env_archcpu(env); @@ -507,9 +543,9 @@ void cpu_report_tpr_access(CPUX86State *env, TPRAccess access) cpu_interrupt(cs, CPU_INTERRUPT_TPR); } else if (tcg_enabled()) { - cpu_restore_state(cs, cs->mem_io_pc, false); + target_ulong eip = get_memio_eip(env); - apic_handle_tpr_access_report(cpu->apic_state, env->eip, access); + apic_handle_tpr_access_report(cpu->apic_state, eip, access); } } #endif /* !CONFIG_USER_ONLY */ @@ -544,9 +580,9 @@ int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector, return 1; } -#if !defined(CONFIG_USER_ONLY) void do_cpu_init(X86CPU *cpu) { +#if !defined(CONFIG_USER_ONLY) CPUState *cs = CPU(cpu); CPUX86State *env = &cpu->env; CPUX86State *save = g_new(CPUX86State, 1); @@ -565,22 +601,15 @@ void do_cpu_init(X86CPU *cpu) kvm_arch_do_init_vcpu(cpu); } apic_init_reset(cpu->apic_state); +#endif /* CONFIG_USER_ONLY */ } +#ifndef CONFIG_USER_ONLY + void do_cpu_sipi(X86CPU *cpu) { apic_sipi(cpu->apic_state); } -#else -void do_cpu_init(X86CPU *cpu) -{ -} -void do_cpu_sipi(X86CPU *cpu) -{ -} -#endif - -#ifndef CONFIG_USER_ONLY void cpu_load_efer(CPUX86State *env, uint64_t val) { diff --git a/target/i386/helper.h b/target/i386/helper.h index ac3b4d1ee3..ac2b04abd6 100644 --- a/target/i386/helper.h +++ b/target/i386/helper.h @@ -37,7 +37,7 @@ DEF_HELPER_2(lldt, void, env, int) DEF_HELPER_2(ltr, void, env, int) DEF_HELPER_3(load_seg, void, env, int, int) DEF_HELPER_4(ljmp_protected, void, env, int, tl, tl) -DEF_HELPER_5(lcall_real, void, env, int, tl, int, int) +DEF_HELPER_5(lcall_real, void, env, i32, i32, int, i32) DEF_HELPER_5(lcall_protected, void, env, int, tl, int, tl) DEF_HELPER_2(iret_real, void, env, int) DEF_HELPER_3(iret_protected, void, env, int, int) @@ -51,18 +51,11 @@ DEF_HELPER_FLAGS_2(get_dr, TCG_CALL_NO_WG, tl, env, int) DEF_HELPER_1(sysenter, void, env) DEF_HELPER_2(sysexit, void, env, int) -#ifdef TARGET_X86_64 DEF_HELPER_2(syscall, void, env, int) DEF_HELPER_2(sysret, void, env, int) -#endif DEF_HELPER_FLAGS_2(pause, TCG_CALL_NO_WG, noreturn, env, int) -DEF_HELPER_1(reset_rf, void, env) DEF_HELPER_FLAGS_3(raise_interrupt, TCG_CALL_NO_WG, noreturn, env, int, int) DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, int) -DEF_HELPER_1(cli, void, env) -DEF_HELPER_1(sti, void, env) -DEF_HELPER_1(clac, void, env) -DEF_HELPER_1(stac, void, env) DEF_HELPER_3(boundw, void, env, tl, int) DEF_HELPER_3(boundl, void, env, tl, int) @@ -71,17 +64,11 @@ DEF_HELPER_1(rsm, void, env) #endif /* !CONFIG_USER_ONLY */ DEF_HELPER_2(into, void, env, int) -DEF_HELPER_2(cmpxchg8b_unlocked, void, env, tl) -DEF_HELPER_2(cmpxchg8b, void, env, tl) -#ifdef TARGET_X86_64 -DEF_HELPER_2(cmpxchg16b_unlocked, void, env, tl) -DEF_HELPER_2(cmpxchg16b, void, env, tl) -#endif DEF_HELPER_FLAGS_1(single_step, TCG_CALL_NO_WG, noreturn, env) DEF_HELPER_1(rechecking_single_step, void, env) DEF_HELPER_1(cpuid, void, env) +DEF_HELPER_FLAGS_1(rdpid, TCG_CALL_NO_WG, tl, env) DEF_HELPER_1(rdtsc, void, env) -DEF_HELPER_1(rdtscp, void, env) DEF_HELPER_FLAGS_1(rdpmc, TCG_CALL_NO_WG, noreturn, env) #ifndef CONFIG_USER_ONLY @@ -212,12 +199,13 @@ DEF_HELPER_2(ldmxcsr, void, env, i32) DEF_HELPER_1(update_mxcsr, void, env) DEF_HELPER_1(enter_mmx, void, env) DEF_HELPER_1(emms, void, env) -DEF_HELPER_3(movq, void, env, ptr, ptr) #define SHIFT 0 -#include "ops_sse_header.h" +#include "tcg/ops_sse_header.h.inc" #define SHIFT 1 -#include "ops_sse_header.h" +#include "tcg/ops_sse_header.h.inc" +#define SHIFT 2 +#include "tcg/ops_sse_header.h.inc" DEF_HELPER_3(rclb, tl, env, tl, tl) DEF_HELPER_3(rclw, tl, env, tl, tl) diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index 10f8aba86e..92ecb7254b 100644 --- a/target/i386/host-cpu.c +++ b/target/i386/host-cpu.c @@ -11,6 +11,7 @@ #include "cpu.h" #include "host-cpu.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "sysemu/sysemu.h" /* Note: Only safe for use on x86(-64) hosts */ diff --git a/target/i386/hvf/README.md b/target/i386/hvf/README.md index 2d33477aca..64a8935237 100644 --- a/target/i386/hvf/README.md +++ b/target/i386/hvf/README.md @@ -4,4 +4,4 @@ These sources (and ../hvf-all.c) are adapted from Veertu Inc's vdhh (Veertu Desk 1. Adapt to our current QEMU's `CPUState` structure and `address_space_rw` API; many struct members have been moved around (emulated x86 state, xsave_buf) due to historical differences + QEMU needing to handle more emulation targets. 2. Removal of `apic_page` and hyperv-related functionality. -3. More relaxed use of `qemu_mutex_lock_iothread`. +3. More relaxed use of `bql_lock`. diff --git a/target/i386/hvf/hvf-cpu.c b/target/i386/hvf/hvf-cpu.c index 333db59898..ac617f17e7 100644 --- a/target/i386/hvf/hvf-cpu.c +++ b/target/i386/hvf/hvf-cpu.c @@ -15,6 +15,7 @@ #include "hw/boards.h" #include "sysemu/hvf.h" #include "hw/core/accel-cpu.h" +#include "hvf-i386.h" static void hvf_cpu_max_instance_init(X86CPU *cpu) { @@ -77,7 +78,7 @@ static void hvf_cpu_accel_class_init(ObjectClass *oc, void *data) { AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); - acc->cpu_realizefn = host_cpu_realizefn; + acc->cpu_target_realize = host_cpu_realizefn; acc->cpu_instance_init = hvf_cpu_instance_init; } diff --git a/target/i386/hvf/hvf-i386.h b/target/i386/hvf/hvf-i386.h index 76e9235524..e99c02cd4b 100644 --- a/target/i386/hvf/hvf-i386.h +++ b/target/i386/hvf/hvf-i386.h @@ -16,19 +16,11 @@ #ifndef HVF_I386_H #define HVF_I386_H -#include "qemu/accel.h" -#include "sysemu/hvf.h" -#include "sysemu/hvf_int.h" -#include "cpu.h" -#include "x86.h" +uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, int reg); void hvf_handle_io(CPUArchState *, uint16_t, void *, int, int, int); -#ifdef NEED_CPU_H -/* Functions exported to host specific mode */ - /* Host specific functions */ int hvf_inject_interrupt(CPUArchState *env, int vector); -#endif #endif diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index f8833277ab..1ed8ed5154 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -81,11 +81,11 @@ void vmx_update_tpr(CPUState *cpu) int tpr = cpu_get_apic_tpr(x86_cpu->apic_state) << 4; int irr = apic_get_highest_priority_irr(x86_cpu->apic_state); - wreg(cpu->hvf->fd, HV_X86_TPR, tpr); + wreg(cpu->accel->fd, HV_X86_TPR, tpr); if (irr == -1) { - wvmcs(cpu->hvf->fd, VMCS_TPR_THRESHOLD, 0); + wvmcs(cpu->accel->fd, VMCS_TPR_THRESHOLD, 0); } else { - wvmcs(cpu->hvf->fd, VMCS_TPR_THRESHOLD, (irr > tpr) ? tpr >> 4 : + wvmcs(cpu->accel->fd, VMCS_TPR_THRESHOLD, (irr > tpr) ? tpr >> 4 : irr >> 4); } } @@ -93,7 +93,7 @@ void vmx_update_tpr(CPUState *cpu) static void update_apic_tpr(CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); - int tpr = rreg(cpu->hvf->fd, HV_X86_TPR) >> 4; + int tpr = rreg(cpu->accel->fd, HV_X86_TPR) >> 4; cpu_set_apic_tpr(x86_cpu->apic_state, tpr); } @@ -221,6 +221,7 @@ int hvf_arch_init_vcpu(CPUState *cpu) { X86CPU *x86cpu = X86_CPU(cpu); CPUX86State *env = &x86cpu->env; + uint64_t reqCap; init_emu(); init_decoder(); @@ -255,27 +256,34 @@ int hvf_arch_init_vcpu(CPUState *cpu) } /* set VMCS control fields */ - wvmcs(cpu->hvf->fd, VMCS_PIN_BASED_CTLS, + wvmcs(cpu->accel->fd, VMCS_PIN_BASED_CTLS, cap2ctrl(hvf_state->hvf_caps->vmx_cap_pinbased, - VMCS_PIN_BASED_CTLS_EXTINT | - VMCS_PIN_BASED_CTLS_NMI | - VMCS_PIN_BASED_CTLS_VNMI)); - wvmcs(cpu->hvf->fd, VMCS_PRI_PROC_BASED_CTLS, + VMCS_PIN_BASED_CTLS_EXTINT | + VMCS_PIN_BASED_CTLS_NMI | + VMCS_PIN_BASED_CTLS_VNMI)); + wvmcs(cpu->accel->fd, VMCS_PRI_PROC_BASED_CTLS, cap2ctrl(hvf_state->hvf_caps->vmx_cap_procbased, - VMCS_PRI_PROC_BASED_CTLS_HLT | - VMCS_PRI_PROC_BASED_CTLS_MWAIT | - VMCS_PRI_PROC_BASED_CTLS_TSC_OFFSET | - VMCS_PRI_PROC_BASED_CTLS_TPR_SHADOW) | + VMCS_PRI_PROC_BASED_CTLS_HLT | + VMCS_PRI_PROC_BASED_CTLS_MWAIT | + VMCS_PRI_PROC_BASED_CTLS_TSC_OFFSET | + VMCS_PRI_PROC_BASED_CTLS_TPR_SHADOW) | VMCS_PRI_PROC_BASED_CTLS_SEC_CONTROL); - wvmcs(cpu->hvf->fd, VMCS_SEC_PROC_BASED_CTLS, - cap2ctrl(hvf_state->hvf_caps->vmx_cap_procbased2, - VMCS_PRI_PROC_BASED2_CTLS_APIC_ACCESSES)); - wvmcs(cpu->hvf->fd, VMCS_ENTRY_CTLS, cap2ctrl(hvf_state->hvf_caps->vmx_cap_entry, - 0)); - wvmcs(cpu->hvf->fd, VMCS_EXCEPTION_BITMAP, 0); /* Double fault */ + reqCap = VMCS_PRI_PROC_BASED2_CTLS_APIC_ACCESSES; - wvmcs(cpu->hvf->fd, VMCS_TPR_THRESHOLD, 0); + /* Is RDTSCP support in CPUID? If so, enable it in the VMCS. */ + if (hvf_get_supported_cpuid(0x80000001, 0, R_EDX) & CPUID_EXT2_RDTSCP) { + reqCap |= VMCS_PRI_PROC_BASED2_CTLS_RDTSCP; + } + + wvmcs(cpu->accel->fd, VMCS_SEC_PROC_BASED_CTLS, + cap2ctrl(hvf_state->hvf_caps->vmx_cap_procbased2, reqCap)); + + wvmcs(cpu->accel->fd, VMCS_ENTRY_CTLS, + cap2ctrl(hvf_state->hvf_caps->vmx_cap_entry, 0)); + wvmcs(cpu->accel->fd, VMCS_EXCEPTION_BITMAP, 0); /* Double fault */ + + wvmcs(cpu->accel->fd, VMCS_TPR_THRESHOLD, 0); x86cpu = X86_CPU(cpu); x86cpu->env.xsave_buf_len = 4096; @@ -287,18 +295,18 @@ int hvf_arch_init_vcpu(CPUState *cpu) */ assert(hvf_get_supported_cpuid(0xd, 0, R_ECX) <= x86cpu->env.xsave_buf_len); - hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_STAR, 1); - hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_LSTAR, 1); - hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_CSTAR, 1); - hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_FMASK, 1); - hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_FSBASE, 1); - hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_GSBASE, 1); - hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_KERNELGSBASE, 1); - hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_TSC_AUX, 1); - hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_IA32_TSC, 1); - hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_IA32_SYSENTER_CS, 1); - hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_IA32_SYSENTER_EIP, 1); - hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_IA32_SYSENTER_ESP, 1); + hv_vcpu_enable_native_msr(cpu->accel->fd, MSR_STAR, 1); + hv_vcpu_enable_native_msr(cpu->accel->fd, MSR_LSTAR, 1); + hv_vcpu_enable_native_msr(cpu->accel->fd, MSR_CSTAR, 1); + hv_vcpu_enable_native_msr(cpu->accel->fd, MSR_FMASK, 1); + hv_vcpu_enable_native_msr(cpu->accel->fd, MSR_FSBASE, 1); + hv_vcpu_enable_native_msr(cpu->accel->fd, MSR_GSBASE, 1); + hv_vcpu_enable_native_msr(cpu->accel->fd, MSR_KERNELGSBASE, 1); + hv_vcpu_enable_native_msr(cpu->accel->fd, MSR_TSC_AUX, 1); + hv_vcpu_enable_native_msr(cpu->accel->fd, MSR_IA32_TSC, 1); + hv_vcpu_enable_native_msr(cpu->accel->fd, MSR_IA32_SYSENTER_CS, 1); + hv_vcpu_enable_native_msr(cpu->accel->fd, MSR_IA32_SYSENTER_EIP, 1); + hv_vcpu_enable_native_msr(cpu->accel->fd, MSR_IA32_SYSENTER_ESP, 1); return 0; } @@ -339,16 +347,16 @@ static void hvf_store_events(CPUState *cpu, uint32_t ins_len, uint64_t idtvec_in } if (idtvec_info & VMCS_IDT_VEC_ERRCODE_VALID) { env->has_error_code = true; - env->error_code = rvmcs(cpu->hvf->fd, VMCS_IDT_VECTORING_ERROR); + env->error_code = rvmcs(cpu->accel->fd, VMCS_IDT_VECTORING_ERROR); } } - if ((rvmcs(cpu->hvf->fd, VMCS_GUEST_INTERRUPTIBILITY) & + if ((rvmcs(cpu->accel->fd, VMCS_GUEST_INTERRUPTIBILITY) & VMCS_INTERRUPTIBILITY_NMI_BLOCKING)) { env->hflags2 |= HF2_NMI_MASK; } else { env->hflags2 &= ~HF2_NMI_MASK; } - if (rvmcs(cpu->hvf->fd, VMCS_GUEST_INTERRUPTIBILITY) & + if (rvmcs(cpu->accel->fd, VMCS_GUEST_INTERRUPTIBILITY) & (VMCS_INTERRUPTIBILITY_STI_BLOCKING | VMCS_INTERRUPTIBILITY_MOVSS_BLOCKING)) { env->hflags |= HF_INHIBIT_IRQ_MASK; @@ -421,28 +429,28 @@ int hvf_vcpu_exec(CPUState *cpu) } vmx_update_tpr(cpu); - qemu_mutex_unlock_iothread(); + bql_unlock(); if (!cpu_is_bsp(X86_CPU(cpu)) && cpu->halted) { - qemu_mutex_lock_iothread(); + bql_lock(); return EXCP_HLT; } - hv_return_t r = hv_vcpu_run(cpu->hvf->fd); + hv_return_t r = hv_vcpu_run(cpu->accel->fd); assert_hvf_ok(r); /* handle VMEXIT */ - uint64_t exit_reason = rvmcs(cpu->hvf->fd, VMCS_EXIT_REASON); - uint64_t exit_qual = rvmcs(cpu->hvf->fd, VMCS_EXIT_QUALIFICATION); - uint32_t ins_len = (uint32_t)rvmcs(cpu->hvf->fd, + uint64_t exit_reason = rvmcs(cpu->accel->fd, VMCS_EXIT_REASON); + uint64_t exit_qual = rvmcs(cpu->accel->fd, VMCS_EXIT_QUALIFICATION); + uint32_t ins_len = (uint32_t)rvmcs(cpu->accel->fd, VMCS_EXIT_INSTRUCTION_LENGTH); - uint64_t idtvec_info = rvmcs(cpu->hvf->fd, VMCS_IDT_VECTORING_INFO); + uint64_t idtvec_info = rvmcs(cpu->accel->fd, VMCS_IDT_VECTORING_INFO); hvf_store_events(cpu, ins_len, idtvec_info); - rip = rreg(cpu->hvf->fd, HV_X86_RIP); - env->eflags = rreg(cpu->hvf->fd, HV_X86_RFLAGS); + rip = rreg(cpu->accel->fd, HV_X86_RIP); + env->eflags = rreg(cpu->accel->fd, HV_X86_RFLAGS); - qemu_mutex_lock_iothread(); + bql_lock(); update_apic_tpr(cpu); current_cpu = cpu; @@ -470,7 +478,7 @@ int hvf_vcpu_exec(CPUState *cpu) case EXIT_REASON_EPT_FAULT: { hvf_slot *slot; - uint64_t gpa = rvmcs(cpu->hvf->fd, VMCS_GUEST_PHYSICAL_ADDRESS); + uint64_t gpa = rvmcs(cpu->accel->fd, VMCS_GUEST_PHYSICAL_ADDRESS); if (((idtvec_info & VMCS_IDT_VEC_VALID) == 0) && ((exit_qual & EXIT_QUAL_NMIUDTI) != 0)) { @@ -515,7 +523,7 @@ int hvf_vcpu_exec(CPUState *cpu) store_regs(cpu); break; } else if (!string && !in) { - RAX(env) = rreg(cpu->hvf->fd, HV_X86_RAX); + RAX(env) = rreg(cpu->accel->fd, HV_X86_RAX); hvf_handle_io(env, port, &RAX(env), 1, size, 1); macvm_set_rip(cpu, rip + ins_len); break; @@ -531,21 +539,21 @@ int hvf_vcpu_exec(CPUState *cpu) break; } case EXIT_REASON_CPUID: { - uint32_t rax = (uint32_t)rreg(cpu->hvf->fd, HV_X86_RAX); - uint32_t rbx = (uint32_t)rreg(cpu->hvf->fd, HV_X86_RBX); - uint32_t rcx = (uint32_t)rreg(cpu->hvf->fd, HV_X86_RCX); - uint32_t rdx = (uint32_t)rreg(cpu->hvf->fd, HV_X86_RDX); + uint32_t rax = (uint32_t)rreg(cpu->accel->fd, HV_X86_RAX); + uint32_t rbx = (uint32_t)rreg(cpu->accel->fd, HV_X86_RBX); + uint32_t rcx = (uint32_t)rreg(cpu->accel->fd, HV_X86_RCX); + uint32_t rdx = (uint32_t)rreg(cpu->accel->fd, HV_X86_RDX); if (rax == 1) { /* CPUID1.ecx.OSXSAVE needs to know CR4 */ - env->cr[4] = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR4); + env->cr[4] = rvmcs(cpu->accel->fd, VMCS_GUEST_CR4); } hvf_cpu_x86_cpuid(env, rax, rcx, &rax, &rbx, &rcx, &rdx); - wreg(cpu->hvf->fd, HV_X86_RAX, rax); - wreg(cpu->hvf->fd, HV_X86_RBX, rbx); - wreg(cpu->hvf->fd, HV_X86_RCX, rcx); - wreg(cpu->hvf->fd, HV_X86_RDX, rdx); + wreg(cpu->accel->fd, HV_X86_RAX, rax); + wreg(cpu->accel->fd, HV_X86_RBX, rbx); + wreg(cpu->accel->fd, HV_X86_RCX, rcx); + wreg(cpu->accel->fd, HV_X86_RDX, rdx); macvm_set_rip(cpu, rip + ins_len); break; @@ -553,16 +561,16 @@ int hvf_vcpu_exec(CPUState *cpu) case EXIT_REASON_XSETBV: { X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; - uint32_t eax = (uint32_t)rreg(cpu->hvf->fd, HV_X86_RAX); - uint32_t ecx = (uint32_t)rreg(cpu->hvf->fd, HV_X86_RCX); - uint32_t edx = (uint32_t)rreg(cpu->hvf->fd, HV_X86_RDX); + uint32_t eax = (uint32_t)rreg(cpu->accel->fd, HV_X86_RAX); + uint32_t ecx = (uint32_t)rreg(cpu->accel->fd, HV_X86_RCX); + uint32_t edx = (uint32_t)rreg(cpu->accel->fd, HV_X86_RDX); if (ecx) { macvm_set_rip(cpu, rip + ins_len); break; } env->xcr0 = ((uint64_t)edx << 32) | eax; - wreg(cpu->hvf->fd, HV_X86_XCR0, env->xcr0 | 1); + wreg(cpu->accel->fd, HV_X86_XCR0, env->xcr0 | 1); macvm_set_rip(cpu, rip + ins_len); break; } @@ -583,9 +591,9 @@ int hvf_vcpu_exec(CPUState *cpu) { load_regs(cpu); if (exit_reason == EXIT_REASON_RDMSR) { - simulate_rdmsr(cpu); + simulate_rdmsr(env); } else { - simulate_wrmsr(cpu); + simulate_wrmsr(env); } env->eip += ins_len; store_regs(cpu); @@ -601,11 +609,11 @@ int hvf_vcpu_exec(CPUState *cpu) switch (cr) { case 0x0: { - macvm_set_cr0(cpu->hvf->fd, RRX(env, reg)); + macvm_set_cr0(cpu->accel->fd, RRX(env, reg)); break; } case 4: { - macvm_set_cr4(cpu->hvf->fd, RRX(env, reg)); + macvm_set_cr4(cpu->accel->fd, RRX(env, reg)); break; } case 8: { @@ -641,7 +649,7 @@ int hvf_vcpu_exec(CPUState *cpu) break; } case EXIT_REASON_TASK_SWITCH: { - uint64_t vinfo = rvmcs(cpu->hvf->fd, VMCS_IDT_VECTORING_INFO); + uint64_t vinfo = rvmcs(cpu->accel->fd, VMCS_IDT_VECTORING_INFO); x68_segment_selector sel = {.sel = exit_qual & 0xffff}; vmx_handle_task_switch(cpu, sel, (exit_qual >> 30) & 0x3, vinfo & VMCS_INTR_VALID, vinfo & VECTORING_INFO_VECTOR_MASK, vinfo @@ -654,8 +662,8 @@ int hvf_vcpu_exec(CPUState *cpu) break; } case EXIT_REASON_RDPMC: - wreg(cpu->hvf->fd, HV_X86_RAX, 0); - wreg(cpu->hvf->fd, HV_X86_RDX, 0); + wreg(cpu->accel->fd, HV_X86_RAX, 0); + wreg(cpu->accel->fd, HV_X86_RDX, 0); macvm_set_rip(cpu, rip + ins_len); break; case VMX_REASON_VMCALL: @@ -671,3 +679,36 @@ int hvf_vcpu_exec(CPUState *cpu) return ret; } + +int hvf_arch_insert_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp) +{ + return -ENOSYS; +} + +int hvf_arch_remove_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp) +{ + return -ENOSYS; +} + +int hvf_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type) +{ + return -ENOSYS; +} + +int hvf_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type) +{ + return -ENOSYS; +} + +void hvf_arch_remove_all_hw_breakpoints(void) +{ +} + +void hvf_arch_update_guest_debug(CPUState *cpu) +{ +} + +bool hvf_arch_supports_guest_debug(void) +{ + return false; +} diff --git a/target/i386/hvf/meson.build b/target/i386/hvf/meson.build index f6d4c394d3..05c3c8cf18 100644 --- a/target/i386/hvf/meson.build +++ b/target/i386/hvf/meson.build @@ -1,4 +1,4 @@ -i386_softmmu_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files( +i386_system_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files( 'hvf.c', 'x86.c', 'x86_cpuid.c', diff --git a/target/i386/hvf/vmcs.h b/target/i386/hvf/vmcs.h index b4692f63f6..aee6f75dfd 100644 --- a/target/i386/hvf/vmcs.h +++ b/target/i386/hvf/vmcs.h @@ -354,7 +354,7 @@ #define VMCS_PRI_PROC_BASED_CTLS_TSC_OFFSET (1 << 3) #define VMCS_PRI_PROC_BASED_CTLS_HLT (1 << 7) #define VMCS_PRI_PROC_BASED_CTLS_MWAIT (1 << 10) -#define VMCS_PRI_PROC_BASED_CTLS_TSC (1 << 12) +#define VMCS_PRI_PROC_BASED_CTLS_RDTSC (1 << 12) #define VMCS_PRI_PROC_BASED_CTLS_CR8_LOAD (1 << 19) #define VMCS_PRI_PROC_BASED_CTLS_CR8_STORE (1 << 20) #define VMCS_PRI_PROC_BASED_CTLS_TPR_SHADOW (1 << 21) @@ -362,6 +362,7 @@ #define VMCS_PRI_PROC_BASED_CTLS_SEC_CONTROL (1 << 31) #define VMCS_PRI_PROC_BASED2_CTLS_APIC_ACCESSES (1 << 0) +#define VMCS_PRI_PROC_BASED2_CTLS_RDTSCP (1 << 3) #define VMCS_PRI_PROC_BASED2_CTLS_X2APIC (1 << 4) enum task_switch_reason { diff --git a/target/i386/hvf/vmx.h b/target/i386/hvf/vmx.h index fcd9a95e5b..0fffcfa46c 100644 --- a/target/i386/hvf/vmx.h +++ b/target/i386/hvf/vmx.h @@ -180,15 +180,15 @@ static inline void macvm_set_rip(CPUState *cpu, uint64_t rip) uint64_t val; /* BUG, should take considering overlap.. */ - wreg(cpu->hvf->fd, HV_X86_RIP, rip); + wreg(cpu->accel->fd, HV_X86_RIP, rip); env->eip = rip; /* after moving forward in rip, we need to clean INTERRUPTABILITY */ - val = rvmcs(cpu->hvf->fd, VMCS_GUEST_INTERRUPTIBILITY); + val = rvmcs(cpu->accel->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, + wvmcs(cpu->accel->fd, VMCS_GUEST_INTERRUPTIBILITY, val & ~(VMCS_INTERRUPTIBILITY_STI_BLOCKING | VMCS_INTERRUPTIBILITY_MOVSS_BLOCKING)); } @@ -200,9 +200,9 @@ static inline void vmx_clear_nmi_blocking(CPUState *cpu) CPUX86State *env = &x86_cpu->env; env->hflags2 &= ~HF2_NMI_MASK; - uint32_t gi = (uint32_t) rvmcs(cpu->hvf->fd, VMCS_GUEST_INTERRUPTIBILITY); + uint32_t gi = (uint32_t) rvmcs(cpu->accel->fd, VMCS_GUEST_INTERRUPTIBILITY); gi &= ~VMCS_INTERRUPTIBILITY_NMI_BLOCKING; - wvmcs(cpu->hvf->fd, VMCS_GUEST_INTERRUPTIBILITY, gi); + wvmcs(cpu->accel->fd, VMCS_GUEST_INTERRUPTIBILITY, gi); } static inline void vmx_set_nmi_blocking(CPUState *cpu) @@ -211,16 +211,16 @@ static inline void vmx_set_nmi_blocking(CPUState *cpu) CPUX86State *env = &x86_cpu->env; env->hflags2 |= HF2_NMI_MASK; - uint32_t gi = (uint32_t)rvmcs(cpu->hvf->fd, VMCS_GUEST_INTERRUPTIBILITY); + uint32_t gi = (uint32_t)rvmcs(cpu->accel->fd, VMCS_GUEST_INTERRUPTIBILITY); gi |= VMCS_INTERRUPTIBILITY_NMI_BLOCKING; - wvmcs(cpu->hvf->fd, VMCS_GUEST_INTERRUPTIBILITY, gi); + wvmcs(cpu->accel->fd, VMCS_GUEST_INTERRUPTIBILITY, gi); } static inline void vmx_set_nmi_window_exiting(CPUState *cpu) { uint64_t val; - val = rvmcs(cpu->hvf->fd, VMCS_PRI_PROC_BASED_CTLS); - wvmcs(cpu->hvf->fd, VMCS_PRI_PROC_BASED_CTLS, val | + val = rvmcs(cpu->accel->fd, VMCS_PRI_PROC_BASED_CTLS); + wvmcs(cpu->accel->fd, VMCS_PRI_PROC_BASED_CTLS, val | VMCS_PRI_PROC_BASED_CTLS_NMI_WINDOW_EXITING); } @@ -229,8 +229,8 @@ static inline void vmx_clear_nmi_window_exiting(CPUState *cpu) { uint64_t val; - val = rvmcs(cpu->hvf->fd, VMCS_PRI_PROC_BASED_CTLS); - wvmcs(cpu->hvf->fd, VMCS_PRI_PROC_BASED_CTLS, val & + val = rvmcs(cpu->accel->fd, VMCS_PRI_PROC_BASED_CTLS); + wvmcs(cpu->accel->fd, VMCS_PRI_PROC_BASED_CTLS, val & ~VMCS_PRI_PROC_BASED_CTLS_NMI_WINDOW_EXITING); } diff --git a/target/i386/hvf/x86.c b/target/i386/hvf/x86.c index d086584f26..80e36136d0 100644 --- a/target/i386/hvf/x86.c +++ b/target/i386/hvf/x86.c @@ -46,7 +46,7 @@ return ar; }*/ -bool x86_read_segment_descriptor(struct CPUState *cpu, +bool x86_read_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, x68_segment_selector sel) { @@ -61,11 +61,11 @@ bool x86_read_segment_descriptor(struct CPUState *cpu, } if (GDT_SEL == sel.ti) { - base = rvmcs(cpu->hvf->fd, VMCS_GUEST_GDTR_BASE); - limit = rvmcs(cpu->hvf->fd, VMCS_GUEST_GDTR_LIMIT); + base = rvmcs(cpu->accel->fd, VMCS_GUEST_GDTR_BASE); + limit = rvmcs(cpu->accel->fd, VMCS_GUEST_GDTR_LIMIT); } else { - base = rvmcs(cpu->hvf->fd, VMCS_GUEST_LDTR_BASE); - limit = rvmcs(cpu->hvf->fd, VMCS_GUEST_LDTR_LIMIT); + base = rvmcs(cpu->accel->fd, VMCS_GUEST_LDTR_BASE); + limit = rvmcs(cpu->accel->fd, VMCS_GUEST_LDTR_LIMIT); } if (sel.index * 8 >= limit) { @@ -76,7 +76,7 @@ bool x86_read_segment_descriptor(struct CPUState *cpu, return true; } -bool x86_write_segment_descriptor(struct CPUState *cpu, +bool x86_write_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, x68_segment_selector sel) { @@ -84,11 +84,11 @@ bool x86_write_segment_descriptor(struct CPUState *cpu, uint32_t limit; if (GDT_SEL == sel.ti) { - base = rvmcs(cpu->hvf->fd, VMCS_GUEST_GDTR_BASE); - limit = rvmcs(cpu->hvf->fd, VMCS_GUEST_GDTR_LIMIT); + base = rvmcs(cpu->accel->fd, VMCS_GUEST_GDTR_BASE); + limit = rvmcs(cpu->accel->fd, VMCS_GUEST_GDTR_LIMIT); } else { - base = rvmcs(cpu->hvf->fd, VMCS_GUEST_LDTR_BASE); - limit = rvmcs(cpu->hvf->fd, VMCS_GUEST_LDTR_LIMIT); + base = rvmcs(cpu->accel->fd, VMCS_GUEST_LDTR_BASE); + limit = rvmcs(cpu->accel->fd, VMCS_GUEST_LDTR_LIMIT); } if (sel.index * 8 >= limit) { @@ -99,11 +99,11 @@ bool x86_write_segment_descriptor(struct CPUState *cpu, return true; } -bool x86_read_call_gate(struct CPUState *cpu, struct x86_call_gate *idt_desc, +bool x86_read_call_gate(CPUState *cpu, struct x86_call_gate *idt_desc, int gate) { - target_ulong base = rvmcs(cpu->hvf->fd, VMCS_GUEST_IDTR_BASE); - uint32_t limit = rvmcs(cpu->hvf->fd, VMCS_GUEST_IDTR_LIMIT); + target_ulong base = rvmcs(cpu->accel->fd, VMCS_GUEST_IDTR_BASE); + uint32_t limit = rvmcs(cpu->accel->fd, VMCS_GUEST_IDTR_LIMIT); memset(idt_desc, 0, sizeof(*idt_desc)); if (gate * 8 >= limit) { @@ -115,30 +115,30 @@ bool x86_read_call_gate(struct CPUState *cpu, struct x86_call_gate *idt_desc, return true; } -bool x86_is_protected(struct CPUState *cpu) +bool x86_is_protected(CPUState *cpu) { - uint64_t cr0 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0); + uint64_t cr0 = rvmcs(cpu->accel->fd, VMCS_GUEST_CR0); return cr0 & CR0_PE_MASK; } -bool x86_is_real(struct CPUState *cpu) +bool x86_is_real(CPUState *cpu) { return !x86_is_protected(cpu); } -bool x86_is_v8086(struct CPUState *cpu) +bool x86_is_v8086(CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; return x86_is_protected(cpu) && (env->eflags & VM_MASK); } -bool x86_is_long_mode(struct CPUState *cpu) +bool x86_is_long_mode(CPUState *cpu) { - return rvmcs(cpu->hvf->fd, VMCS_GUEST_IA32_EFER) & MSR_EFER_LMA; + return rvmcs(cpu->accel->fd, VMCS_GUEST_IA32_EFER) & MSR_EFER_LMA; } -bool x86_is_long64_mode(struct CPUState *cpu) +bool x86_is_long64_mode(CPUState *cpu) { struct vmx_segment desc; vmx_read_segment_descriptor(cpu, &desc, R_CS); @@ -146,24 +146,24 @@ bool x86_is_long64_mode(struct CPUState *cpu) return x86_is_long_mode(cpu) && ((desc.ar >> 13) & 1); } -bool x86_is_paging_mode(struct CPUState *cpu) +bool x86_is_paging_mode(CPUState *cpu) { - uint64_t cr0 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0); + uint64_t cr0 = rvmcs(cpu->accel->fd, VMCS_GUEST_CR0); return cr0 & CR0_PG_MASK; } -bool x86_is_pae_enabled(struct CPUState *cpu) +bool x86_is_pae_enabled(CPUState *cpu) { - uint64_t cr4 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR4); + uint64_t cr4 = rvmcs(cpu->accel->fd, VMCS_GUEST_CR4); return cr4 & CR4_PAE_MASK; } -target_ulong linear_addr(struct CPUState *cpu, target_ulong addr, X86Seg seg) +target_ulong linear_addr(CPUState *cpu, target_ulong addr, X86Seg seg) { return vmx_read_segment_base(cpu, seg) + addr; } -target_ulong linear_addr_size(struct CPUState *cpu, target_ulong addr, int size, +target_ulong linear_addr_size(CPUState *cpu, target_ulong addr, int size, X86Seg seg) { switch (size) { @@ -179,7 +179,7 @@ target_ulong linear_addr_size(struct CPUState *cpu, target_ulong addr, int size, return linear_addr(cpu, addr, seg); } -target_ulong linear_rip(struct CPUState *cpu, target_ulong rip) +target_ulong linear_rip(CPUState *cpu, target_ulong rip) { return linear_addr(cpu, rip, R_CS); } diff --git a/target/i386/hvf/x86.h b/target/i386/hvf/x86.h index 947b98da41..3570f29aa9 100644 --- a/target/i386/hvf/x86.h +++ b/target/i386/hvf/x86.h @@ -248,30 +248,30 @@ typedef struct x68_segment_selector { #define BH(cpu) RH(cpu, R_EBX) /* deal with GDT/LDT descriptors in memory */ -bool x86_read_segment_descriptor(struct CPUState *cpu, +bool x86_read_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, x68_segment_selector sel); -bool x86_write_segment_descriptor(struct CPUState *cpu, +bool x86_write_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, x68_segment_selector sel); -bool x86_read_call_gate(struct CPUState *cpu, struct x86_call_gate *idt_desc, +bool x86_read_call_gate(CPUState *cpu, struct x86_call_gate *idt_desc, int gate); /* helpers */ -bool x86_is_protected(struct CPUState *cpu); -bool x86_is_real(struct CPUState *cpu); -bool x86_is_v8086(struct CPUState *cpu); -bool x86_is_long_mode(struct CPUState *cpu); -bool x86_is_long64_mode(struct CPUState *cpu); -bool x86_is_paging_mode(struct CPUState *cpu); -bool x86_is_pae_enabled(struct CPUState *cpu); +bool x86_is_protected(CPUState *cpu); +bool x86_is_real(CPUState *cpu); +bool x86_is_v8086(CPUState *cpu); +bool x86_is_long_mode(CPUState *cpu); +bool x86_is_long64_mode(CPUState *cpu); +bool x86_is_paging_mode(CPUState *cpu); +bool x86_is_pae_enabled(CPUState *cpu); enum X86Seg; -target_ulong linear_addr(struct CPUState *cpu, target_ulong addr, enum X86Seg seg); -target_ulong linear_addr_size(struct CPUState *cpu, target_ulong addr, int size, +target_ulong linear_addr(CPUState *cpu, target_ulong addr, enum X86Seg seg); +target_ulong linear_addr_size(CPUState *cpu, target_ulong addr, int size, enum X86Seg seg); -target_ulong linear_rip(struct CPUState *cpu, target_ulong rip); +target_ulong linear_rip(CPUState *cpu, target_ulong rip); static inline uint64_t rdtscp(void) { diff --git a/target/i386/hvf/x86_cpuid.c b/target/i386/hvf/x86_cpuid.c index f24dd50e48..9380b90496 100644 --- a/target/i386/hvf/x86_cpuid.c +++ b/target/i386/hvf/x86_cpuid.c @@ -25,6 +25,7 @@ #include "x86.h" #include "vmx.h" #include "sysemu/hvf.h" +#include "hvf-i386.h" static bool xgetbv(uint32_t cpuid_ecx, uint32_t idx, uint64_t *xcr) { @@ -95,7 +96,8 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, ebx &= ~CPUID_7_0_EBX_INVPCID; } - ecx &= CPUID_7_0_ECX_AVX512_VBMI | CPUID_7_0_ECX_AVX512_VPOPCNTDQ; + ecx &= CPUID_7_0_ECX_AVX512_VBMI | CPUID_7_0_ECX_AVX512_VPOPCNTDQ | + CPUID_7_0_ECX_RDPID; edx &= CPUID_7_0_EDX_AVX512_4VNNIW | CPUID_7_0_EDX_AVX512_4FMAPS; } else { ebx = 0; @@ -132,11 +134,11 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, CPUID_FXSR | CPUID_EXT2_FXSR | CPUID_EXT2_PDPE1GB | CPUID_EXT2_3DNOWEXT | CPUID_EXT2_3DNOW | CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX; hv_vmx_read_capability(HV_VMX_CAP_PROCBASED2, &cap); - if (!(cap & CPU_BASED2_RDTSCP)) { + if (!(cap2ctrl(cap, CPU_BASED2_RDTSCP) & CPU_BASED2_RDTSCP)) { edx &= ~CPUID_EXT2_RDTSCP; } hv_vmx_read_capability(HV_VMX_CAP_PROCBASED, &cap); - if (!(cap & CPU_BASED_TSC_OFFSET)) { + if (!(cap2ctrl(cap, CPU_BASED_TSC_OFFSET) & CPU_BASED_TSC_OFFSET)) { edx &= ~CPUID_EXT2_RDTSCP; } ecx &= CPUID_EXT3_LAHF_LM | CPUID_EXT3_CMP_LEG | CPUID_EXT3_CR8LEG | diff --git a/target/i386/hvf/x86_descr.c b/target/i386/hvf/x86_descr.c index a484942cfc..f33836d6cb 100644 --- a/target/i386/hvf/x86_descr.c +++ b/target/i386/hvf/x86_descr.c @@ -47,50 +47,52 @@ static const struct vmx_segment_field { uint32_t vmx_read_segment_limit(CPUState *cpu, X86Seg seg) { - return (uint32_t)rvmcs(cpu->hvf->fd, vmx_segment_fields[seg].limit); + return (uint32_t)rvmcs(cpu->accel->fd, vmx_segment_fields[seg].limit); } uint32_t vmx_read_segment_ar(CPUState *cpu, X86Seg seg) { - return (uint32_t)rvmcs(cpu->hvf->fd, vmx_segment_fields[seg].ar_bytes); + return (uint32_t)rvmcs(cpu->accel->fd, vmx_segment_fields[seg].ar_bytes); } uint64_t vmx_read_segment_base(CPUState *cpu, X86Seg seg) { - return rvmcs(cpu->hvf->fd, vmx_segment_fields[seg].base); + return rvmcs(cpu->accel->fd, vmx_segment_fields[seg].base); } x68_segment_selector vmx_read_segment_selector(CPUState *cpu, X86Seg seg) { x68_segment_selector sel; - sel.sel = rvmcs(cpu->hvf->fd, vmx_segment_fields[seg].selector); + sel.sel = rvmcs(cpu->accel->fd, vmx_segment_fields[seg].selector); return sel; } -void vmx_write_segment_selector(struct CPUState *cpu, x68_segment_selector selector, X86Seg seg) +void vmx_write_segment_selector(CPUState *cpu, x68_segment_selector selector, X86Seg seg) { - wvmcs(cpu->hvf->fd, vmx_segment_fields[seg].selector, selector.sel); + wvmcs(cpu->accel->fd, vmx_segment_fields[seg].selector, selector.sel); } -void vmx_read_segment_descriptor(struct CPUState *cpu, struct vmx_segment *desc, X86Seg seg) +void vmx_read_segment_descriptor(CPUState *cpu, struct vmx_segment *desc, X86Seg seg) { - desc->sel = rvmcs(cpu->hvf->fd, vmx_segment_fields[seg].selector); - desc->base = rvmcs(cpu->hvf->fd, vmx_segment_fields[seg].base); - desc->limit = rvmcs(cpu->hvf->fd, vmx_segment_fields[seg].limit); - desc->ar = rvmcs(cpu->hvf->fd, vmx_segment_fields[seg].ar_bytes); + desc->sel = rvmcs(cpu->accel->fd, vmx_segment_fields[seg].selector); + desc->base = rvmcs(cpu->accel->fd, vmx_segment_fields[seg].base); + desc->limit = rvmcs(cpu->accel->fd, vmx_segment_fields[seg].limit); + desc->ar = rvmcs(cpu->accel->fd, vmx_segment_fields[seg].ar_bytes); } void vmx_write_segment_descriptor(CPUState *cpu, struct vmx_segment *desc, X86Seg seg) { const struct vmx_segment_field *sf = &vmx_segment_fields[seg]; - wvmcs(cpu->hvf->fd, sf->base, desc->base); - wvmcs(cpu->hvf->fd, sf->limit, desc->limit); - wvmcs(cpu->hvf->fd, sf->selector, desc->sel); - wvmcs(cpu->hvf->fd, sf->ar_bytes, desc->ar); + wvmcs(cpu->accel->fd, sf->base, desc->base); + wvmcs(cpu->accel->fd, sf->limit, desc->limit); + wvmcs(cpu->accel->fd, sf->selector, desc->sel); + wvmcs(cpu->accel->fd, sf->ar_bytes, desc->ar); } -void x86_segment_descriptor_to_vmx(struct CPUState *cpu, x68_segment_selector selector, struct x86_segment_descriptor *desc, struct vmx_segment *vmx_desc) +void x86_segment_descriptor_to_vmx(CPUState *cpu, x68_segment_selector selector, + struct x86_segment_descriptor *desc, + struct vmx_segment *vmx_desc) { vmx_desc->sel = selector.sel; vmx_desc->base = x86_segment_base(desc); @@ -107,7 +109,8 @@ void x86_segment_descriptor_to_vmx(struct CPUState *cpu, x68_segment_selector se desc->type; } -void vmx_segment_to_x86_descriptor(struct CPUState *cpu, struct vmx_segment *vmx_desc, struct x86_segment_descriptor *desc) +void vmx_segment_to_x86_descriptor(CPUState *cpu, struct vmx_segment *vmx_desc, + struct x86_segment_descriptor *desc) { x86_set_segment_limit(desc, vmx_desc->limit); x86_set_segment_base(desc, vmx_desc->base); diff --git a/target/i386/hvf/x86_descr.h b/target/i386/hvf/x86_descr.h index c356932fa4..9f06014b56 100644 --- a/target/i386/hvf/x86_descr.h +++ b/target/i386/hvf/x86_descr.h @@ -29,29 +29,29 @@ typedef struct vmx_segment { } vmx_segment; /* deal with vmstate descriptors */ -void vmx_read_segment_descriptor(struct CPUState *cpu, +void vmx_read_segment_descriptor(CPUState *cpu, struct vmx_segment *desc, enum X86Seg seg); void vmx_write_segment_descriptor(CPUState *cpu, struct vmx_segment *desc, enum X86Seg seg); -x68_segment_selector vmx_read_segment_selector(struct CPUState *cpu, +x68_segment_selector vmx_read_segment_selector(CPUState *cpu, enum X86Seg seg); -void vmx_write_segment_selector(struct CPUState *cpu, +void vmx_write_segment_selector(CPUState *cpu, x68_segment_selector selector, enum X86Seg seg); -uint64_t vmx_read_segment_base(struct CPUState *cpu, enum X86Seg seg); -void vmx_write_segment_base(struct CPUState *cpu, enum X86Seg seg, +uint64_t vmx_read_segment_base(CPUState *cpu, enum X86Seg seg); +void vmx_write_segment_base(CPUState *cpu, enum X86Seg seg, uint64_t base); -void x86_segment_descriptor_to_vmx(struct CPUState *cpu, +void x86_segment_descriptor_to_vmx(CPUState *cpu, x68_segment_selector selector, struct x86_segment_descriptor *desc, struct vmx_segment *vmx_desc); uint32_t vmx_read_segment_limit(CPUState *cpu, enum X86Seg seg); uint32_t vmx_read_segment_ar(CPUState *cpu, enum X86Seg seg); -void vmx_segment_to_x86_descriptor(struct CPUState *cpu, +void vmx_segment_to_x86_descriptor(CPUState *cpu, struct vmx_segment *vmx_desc, struct x86_segment_descriptor *desc); diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index f5704f63e8..3a3f0a50d0 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -45,7 +45,7 @@ #include "vmcs.h" #include "vmx.h" -void hvf_handle_io(struct CPUState *cpu, uint16_t port, void *data, +void hvf_handle_io(CPUState *cs, uint16_t port, void *data, int direction, int size, uint32_t count); #define EXEC_2OP_FLAGS_CMD(env, decode, cmd, FLAGS_FUNC, save_res) \ @@ -663,35 +663,34 @@ static void exec_lods(CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -void simulate_rdmsr(struct CPUState *cpu) +void simulate_rdmsr(CPUX86State *env) { - X86CPU *x86_cpu = X86_CPU(cpu); - CPUX86State *env = &x86_cpu->env; + X86CPU *cpu = env_archcpu(env); CPUState *cs = env_cpu(env); uint32_t msr = ECX(env); uint64_t val = 0; switch (msr) { case MSR_IA32_TSC: - val = rdtscp() + rvmcs(cpu->hvf->fd, VMCS_TSC_OFFSET); + val = rdtscp() + rvmcs(cs->accel->fd, VMCS_TSC_OFFSET); break; case MSR_IA32_APICBASE: - val = cpu_get_apic_base(X86_CPU(cpu)->apic_state); + val = cpu_get_apic_base(cpu->apic_state); break; case MSR_IA32_UCODE_REV: - val = x86_cpu->ucode_rev; + val = cpu->ucode_rev; break; case MSR_EFER: - val = rvmcs(cpu->hvf->fd, VMCS_GUEST_IA32_EFER); + val = rvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER); break; case MSR_FSBASE: - val = rvmcs(cpu->hvf->fd, VMCS_GUEST_FS_BASE); + val = rvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE); break; case MSR_GSBASE: - val = rvmcs(cpu->hvf->fd, VMCS_GUEST_GS_BASE); + val = rvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE); break; case MSR_KERNELGSBASE: - val = rvmcs(cpu->hvf->fd, VMCS_HOST_FS_BASE); + val = rvmcs(cs->accel->fd, VMCS_HOST_FS_BASE); break; case MSR_STAR: abort(); @@ -746,7 +745,7 @@ void simulate_rdmsr(struct CPUState *cpu) val = env->mtrr_deftype; break; case MSR_CORE_THREAD_COUNT: - val = cs->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */ + val = cs->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */ val |= ((uint32_t)cs->nr_cores << 16); /* core count, bits 31..16 */ break; default: @@ -761,14 +760,14 @@ void simulate_rdmsr(struct CPUState *cpu) static void exec_rdmsr(CPUX86State *env, struct x86_decode *decode) { - simulate_rdmsr(env_cpu(env)); + simulate_rdmsr(env); env->eip += decode->len; } -void simulate_wrmsr(struct CPUState *cpu) +void simulate_wrmsr(CPUX86State *env) { - X86CPU *x86_cpu = X86_CPU(cpu); - CPUX86State *env = &x86_cpu->env; + X86CPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); uint32_t msr = ECX(env); uint64_t data = ((uint64_t)EDX(env) << 32) | EAX(env); @@ -776,16 +775,16 @@ void simulate_wrmsr(struct CPUState *cpu) case MSR_IA32_TSC: break; case MSR_IA32_APICBASE: - cpu_set_apic_base(X86_CPU(cpu)->apic_state, data); + cpu_set_apic_base(cpu->apic_state, data); break; case MSR_FSBASE: - wvmcs(cpu->hvf->fd, VMCS_GUEST_FS_BASE, data); + wvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE, data); break; case MSR_GSBASE: - wvmcs(cpu->hvf->fd, VMCS_GUEST_GS_BASE, data); + wvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE, data); break; case MSR_KERNELGSBASE: - wvmcs(cpu->hvf->fd, VMCS_HOST_FS_BASE, data); + wvmcs(cs->accel->fd, VMCS_HOST_FS_BASE, data); break; case MSR_STAR: abort(); @@ -797,10 +796,10 @@ void simulate_wrmsr(struct CPUState *cpu) abort(); break; case MSR_EFER: - /*printf("new efer %llx\n", EFER(cpu));*/ - wvmcs(cpu->hvf->fd, VMCS_GUEST_IA32_EFER, data); + /*printf("new efer %llx\n", EFER(cs));*/ + wvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER, data); if (data & MSR_EFER_NXE) { - hv_vcpu_invalidate_tlb(cpu->hvf->fd); + hv_vcpu_invalidate_tlb(cs->accel->fd); } break; case MSR_MTRRphysBase(0): @@ -849,14 +848,14 @@ void simulate_wrmsr(struct CPUState *cpu) /* Related to support known hypervisor interface */ /* if (g_hypervisor_iface) - g_hypervisor_iface->wrmsr_handler(cpu, msr, data); + g_hypervisor_iface->wrmsr_handler(cs, msr, data); - printf("write msr %llx\n", RCX(cpu));*/ + printf("write msr %llx\n", RCX(cs));*/ } static void exec_wrmsr(CPUX86State *env, struct x86_decode *decode) { - simulate_wrmsr(env_cpu(env)); + simulate_wrmsr(env); env->eip += decode->len; } @@ -1418,56 +1417,56 @@ static void init_cmd_handler() } } -void load_regs(struct CPUState *cpu) +void load_regs(CPUState *cs) { - X86CPU *x86_cpu = X86_CPU(cpu); - CPUX86State *env = &x86_cpu->env; + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; int i = 0; - RRX(env, R_EAX) = rreg(cpu->hvf->fd, HV_X86_RAX); - RRX(env, R_EBX) = rreg(cpu->hvf->fd, HV_X86_RBX); - RRX(env, R_ECX) = rreg(cpu->hvf->fd, HV_X86_RCX); - RRX(env, R_EDX) = rreg(cpu->hvf->fd, HV_X86_RDX); - RRX(env, R_ESI) = rreg(cpu->hvf->fd, HV_X86_RSI); - RRX(env, R_EDI) = rreg(cpu->hvf->fd, HV_X86_RDI); - RRX(env, R_ESP) = rreg(cpu->hvf->fd, HV_X86_RSP); - RRX(env, R_EBP) = rreg(cpu->hvf->fd, HV_X86_RBP); + RRX(env, R_EAX) = rreg(cs->accel->fd, HV_X86_RAX); + RRX(env, R_EBX) = rreg(cs->accel->fd, HV_X86_RBX); + RRX(env, R_ECX) = rreg(cs->accel->fd, HV_X86_RCX); + RRX(env, R_EDX) = rreg(cs->accel->fd, HV_X86_RDX); + RRX(env, R_ESI) = rreg(cs->accel->fd, HV_X86_RSI); + RRX(env, R_EDI) = rreg(cs->accel->fd, HV_X86_RDI); + RRX(env, R_ESP) = rreg(cs->accel->fd, HV_X86_RSP); + RRX(env, R_EBP) = rreg(cs->accel->fd, HV_X86_RBP); for (i = 8; i < 16; i++) { - RRX(env, i) = rreg(cpu->hvf->fd, HV_X86_RAX + i); + RRX(env, i) = rreg(cs->accel->fd, HV_X86_RAX + i); } - env->eflags = rreg(cpu->hvf->fd, HV_X86_RFLAGS); + env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); rflags_to_lflags(env); - env->eip = rreg(cpu->hvf->fd, HV_X86_RIP); + env->eip = rreg(cs->accel->fd, HV_X86_RIP); } -void store_regs(struct CPUState *cpu) +void store_regs(CPUState *cs) { - X86CPU *x86_cpu = X86_CPU(cpu); - CPUX86State *env = &x86_cpu->env; + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; int i = 0; - wreg(cpu->hvf->fd, HV_X86_RAX, RAX(env)); - wreg(cpu->hvf->fd, HV_X86_RBX, RBX(env)); - wreg(cpu->hvf->fd, HV_X86_RCX, RCX(env)); - wreg(cpu->hvf->fd, HV_X86_RDX, RDX(env)); - wreg(cpu->hvf->fd, HV_X86_RSI, RSI(env)); - wreg(cpu->hvf->fd, HV_X86_RDI, RDI(env)); - wreg(cpu->hvf->fd, HV_X86_RBP, RBP(env)); - wreg(cpu->hvf->fd, HV_X86_RSP, RSP(env)); + wreg(cs->accel->fd, HV_X86_RAX, RAX(env)); + wreg(cs->accel->fd, HV_X86_RBX, RBX(env)); + wreg(cs->accel->fd, HV_X86_RCX, RCX(env)); + wreg(cs->accel->fd, HV_X86_RDX, RDX(env)); + wreg(cs->accel->fd, HV_X86_RSI, RSI(env)); + wreg(cs->accel->fd, HV_X86_RDI, RDI(env)); + wreg(cs->accel->fd, HV_X86_RBP, RBP(env)); + wreg(cs->accel->fd, HV_X86_RSP, RSP(env)); for (i = 8; i < 16; i++) { - wreg(cpu->hvf->fd, HV_X86_RAX + i, RRX(env, i)); + wreg(cs->accel->fd, HV_X86_RAX + i, RRX(env, i)); } lflags_to_rflags(env); - wreg(cpu->hvf->fd, HV_X86_RFLAGS, env->eflags); - macvm_set_rip(cpu, env->eip); + wreg(cs->accel->fd, HV_X86_RFLAGS, env->eflags); + macvm_set_rip(cs, env->eip); } bool exec_instruction(CPUX86State *env, struct x86_decode *ins) { - /*if (hvf_vcpu_id(cpu)) - printf("%d, %llx: exec_instruction %s\n", hvf_vcpu_id(cpu), env->eip, + /*if (hvf_vcpu_id(cs)) + printf("%d, %llx: exec_instruction %s\n", hvf_vcpu_id(cs), env->eip, decode_cmd_to_string(ins->cmd));*/ if (!_cmd_handler[ins->cmd].handler) { diff --git a/target/i386/hvf/x86_emu.h b/target/i386/hvf/x86_emu.h index 640da90b30..8bd97608c4 100644 --- a/target/i386/hvf/x86_emu.h +++ b/target/i386/hvf/x86_emu.h @@ -26,11 +26,11 @@ void init_emu(void); bool exec_instruction(CPUX86State *env, struct x86_decode *ins); -void load_regs(struct CPUState *cpu); -void store_regs(struct CPUState *cpu); +void load_regs(CPUState *cpu); +void store_regs(CPUState *cpu); -void simulate_rdmsr(struct CPUState *cpu); -void simulate_wrmsr(struct CPUState *cpu); +void simulate_rdmsr(CPUX86State *env); +void simulate_wrmsr(CPUX86State *env); target_ulong read_reg(CPUX86State *env, int reg, int size); void write_reg(CPUX86State *env, int reg, target_ulong val, int size); diff --git a/target/i386/hvf/x86_mmu.c b/target/i386/hvf/x86_mmu.c index 96d117567e..649074a7d2 100644 --- a/target/i386/hvf/x86_mmu.c +++ b/target/i386/hvf/x86_mmu.c @@ -49,7 +49,7 @@ struct gpt_translation { bool exec_access; }; -static int gpt_top_level(struct CPUState *cpu, bool pae) +static int gpt_top_level(CPUState *cpu, bool pae) { if (!pae) { return 2; @@ -73,7 +73,7 @@ static inline int pte_size(bool pae) } -static bool get_pt_entry(struct CPUState *cpu, struct gpt_translation *pt, +static bool get_pt_entry(CPUState *cpu, struct gpt_translation *pt, int level, bool pae) { int index; @@ -95,7 +95,7 @@ static bool get_pt_entry(struct CPUState *cpu, struct gpt_translation *pt, } /* test page table entry */ -static bool test_pt_entry(struct CPUState *cpu, struct gpt_translation *pt, +static bool test_pt_entry(CPUState *cpu, struct gpt_translation *pt, int level, bool *is_large, bool pae) { uint64_t pte = pt->pte[level]; @@ -126,7 +126,7 @@ static bool test_pt_entry(struct CPUState *cpu, struct gpt_translation *pt, pt->err_code |= MMU_PAGE_PT; } - uint32_t cr0 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0); + uint32_t cr0 = rvmcs(cpu->accel->fd, VMCS_GUEST_CR0); /* check protection */ if (cr0 & CR0_WP_MASK) { if (pt->write_access && !pte_write_access(pte)) { @@ -166,12 +166,12 @@ static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae) -static bool walk_gpt(struct CPUState *cpu, target_ulong addr, int err_code, +static bool walk_gpt(CPUState *cpu, target_ulong addr, int err_code, struct gpt_translation *pt, bool pae) { int top_level, level; bool is_large = false; - target_ulong cr3 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR3); + target_ulong cr3 = rvmcs(cpu->accel->fd, VMCS_GUEST_CR3); uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK; memset(pt, 0, sizeof(*pt)); @@ -205,7 +205,7 @@ static bool walk_gpt(struct CPUState *cpu, target_ulong addr, int err_code, } -bool mmu_gva_to_gpa(struct CPUState *cpu, target_ulong gva, uint64_t *gpa) +bool mmu_gva_to_gpa(CPUState *cpu, target_ulong gva, uint64_t *gpa) { bool res; struct gpt_translation pt; @@ -225,7 +225,7 @@ bool mmu_gva_to_gpa(struct CPUState *cpu, target_ulong gva, uint64_t *gpa) return false; } -void vmx_write_mem(struct CPUState *cpu, target_ulong gva, void *data, int bytes) +void vmx_write_mem(CPUState *cpu, target_ulong gva, void *data, int bytes) { uint64_t gpa; @@ -246,7 +246,7 @@ void vmx_write_mem(struct CPUState *cpu, target_ulong gva, void *data, int bytes } } -void vmx_read_mem(struct CPUState *cpu, void *data, target_ulong gva, int bytes) +void vmx_read_mem(CPUState *cpu, void *data, target_ulong gva, int bytes) { uint64_t gpa; diff --git a/target/i386/hvf/x86_mmu.h b/target/i386/hvf/x86_mmu.h index 9ae8a548de..9447ae072c 100644 --- a/target/i386/hvf/x86_mmu.h +++ b/target/i386/hvf/x86_mmu.h @@ -36,9 +36,9 @@ #define MMU_PAGE_US (1 << 2) #define MMU_PAGE_NX (1 << 3) -bool mmu_gva_to_gpa(struct CPUState *cpu, target_ulong gva, uint64_t *gpa); +bool mmu_gva_to_gpa(CPUState *cpu, target_ulong gva, uint64_t *gpa); -void vmx_write_mem(struct CPUState *cpu, target_ulong gva, void *data, int bytes); -void vmx_read_mem(struct CPUState *cpu, void *data, target_ulong gva, int bytes); +void vmx_write_mem(CPUState *cpu, target_ulong gva, void *data, int bytes); +void vmx_read_mem(CPUState *cpu, void *data, target_ulong gva, int bytes); #endif /* X86_MMU_H */ diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c index beaeec0687..f09bfbdda5 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -61,7 +61,7 @@ static void load_state_from_tss32(CPUState *cpu, struct x86_tss_segment32 *tss) X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; - wvmcs(cpu->hvf->fd, VMCS_GUEST_CR3, tss->cr3); + wvmcs(cpu->accel->fd, VMCS_GUEST_CR3, tss->cr3); env->eip = tss->eip; env->eflags = tss->eflags | 2; @@ -110,11 +110,11 @@ static int task_switch_32(CPUState *cpu, x68_segment_selector tss_sel, x68_segme void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type) { - uint64_t rip = rreg(cpu->hvf->fd, HV_X86_RIP); + uint64_t rip = rreg(cpu->accel->fd, HV_X86_RIP); if (!gate_valid || (gate_type != VMCS_INTR_T_HWEXCEPTION && gate_type != VMCS_INTR_T_HWINTR && gate_type != VMCS_INTR_T_NMI)) { - int ins_len = rvmcs(cpu->hvf->fd, VMCS_EXIT_INSTRUCTION_LENGTH); + int ins_len = rvmcs(cpu->accel->fd, VMCS_EXIT_INSTRUCTION_LENGTH); macvm_set_rip(cpu, rip + ins_len); return; } @@ -173,12 +173,12 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea //ret = task_switch_16(cpu, tss_sel, old_tss_sel, old_tss_base, &next_tss_desc); VM_PANIC("task_switch_16"); - macvm_set_cr0(cpu->hvf->fd, rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0) | + macvm_set_cr0(cpu->accel->fd, rvmcs(cpu->accel->fd, VMCS_GUEST_CR0) | CR0_TS_MASK); x86_segment_descriptor_to_vmx(cpu, tss_sel, &next_tss_desc, &vmx_seg); vmx_write_segment_descriptor(cpu, &vmx_seg, R_TR); store_regs(cpu); - hv_vcpu_invalidate_tlb(cpu->hvf->fd); + hv_vcpu_invalidate_tlb(cpu->accel->fd); } diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 69d4fb8cf5..be2c46246e 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -32,14 +32,14 @@ #include #include -void hvf_set_segment(struct CPUState *cpu, struct vmx_segment *vmx_seg, +void hvf_set_segment(CPUState *cs, struct vmx_segment *vmx_seg, SegmentCache *qseg, bool is_tr) { vmx_seg->sel = qseg->selector; vmx_seg->base = qseg->base; vmx_seg->limit = qseg->limit; - if (!qseg->selector && !x86_is_real(cpu) && !is_tr) { + if (!qseg->selector && !x86_is_real(cs) && !is_tr) { /* the TR register is usable after processor reset despite * having a null selector */ vmx_seg->ar = 1 << 16; @@ -70,279 +70,279 @@ void hvf_get_segment(SegmentCache *qseg, struct vmx_segment *vmx_seg) (((vmx_seg->ar >> 15) & 1) << DESC_G_SHIFT); } -void hvf_put_xsave(CPUState *cpu_state) +void hvf_put_xsave(CPUState *cs) { - void *xsave = X86_CPU(cpu_state)->env.xsave_buf; - uint32_t xsave_len = X86_CPU(cpu_state)->env.xsave_buf_len; + void *xsave = X86_CPU(cs)->env.xsave_buf; + uint32_t xsave_len = X86_CPU(cs)->env.xsave_buf_len; - x86_cpu_xsave_all_areas(X86_CPU(cpu_state), xsave, xsave_len); + x86_cpu_xsave_all_areas(X86_CPU(cs), xsave, xsave_len); - if (hv_vcpu_write_fpstate(cpu_state->hvf->fd, xsave, xsave_len)) { + if (hv_vcpu_write_fpstate(cs->accel->fd, xsave, xsave_len)) { abort(); } } -static void hvf_put_segments(CPUState *cpu_state) +static void hvf_put_segments(CPUState *cs) { - CPUX86State *env = &X86_CPU(cpu_state)->env; + CPUX86State *env = &X86_CPU(cs)->env; struct vmx_segment seg; - wvmcs(cpu_state->hvf->fd, VMCS_GUEST_IDTR_LIMIT, env->idt.limit); - wvmcs(cpu_state->hvf->fd, VMCS_GUEST_IDTR_BASE, env->idt.base); + wvmcs(cs->accel->fd, VMCS_GUEST_IDTR_LIMIT, env->idt.limit); + wvmcs(cs->accel->fd, VMCS_GUEST_IDTR_BASE, env->idt.base); - wvmcs(cpu_state->hvf->fd, VMCS_GUEST_GDTR_LIMIT, env->gdt.limit); - wvmcs(cpu_state->hvf->fd, VMCS_GUEST_GDTR_BASE, env->gdt.base); + wvmcs(cs->accel->fd, VMCS_GUEST_GDTR_LIMIT, env->gdt.limit); + wvmcs(cs->accel->fd, VMCS_GUEST_GDTR_BASE, env->gdt.base); - /* wvmcs(cpu_state->hvf->fd, VMCS_GUEST_CR2, env->cr[2]); */ - wvmcs(cpu_state->hvf->fd, VMCS_GUEST_CR3, env->cr[3]); - vmx_update_tpr(cpu_state); - wvmcs(cpu_state->hvf->fd, VMCS_GUEST_IA32_EFER, env->efer); + /* wvmcs(cs->accel->fd, VMCS_GUEST_CR2, env->cr[2]); */ + wvmcs(cs->accel->fd, VMCS_GUEST_CR3, env->cr[3]); + vmx_update_tpr(cs); + wvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER, env->efer); - macvm_set_cr4(cpu_state->hvf->fd, env->cr[4]); - macvm_set_cr0(cpu_state->hvf->fd, env->cr[0]); + macvm_set_cr4(cs->accel->fd, env->cr[4]); + macvm_set_cr0(cs->accel->fd, env->cr[0]); - hvf_set_segment(cpu_state, &seg, &env->segs[R_CS], false); - vmx_write_segment_descriptor(cpu_state, &seg, R_CS); + hvf_set_segment(cs, &seg, &env->segs[R_CS], false); + vmx_write_segment_descriptor(cs, &seg, R_CS); - hvf_set_segment(cpu_state, &seg, &env->segs[R_DS], false); - vmx_write_segment_descriptor(cpu_state, &seg, R_DS); + hvf_set_segment(cs, &seg, &env->segs[R_DS], false); + vmx_write_segment_descriptor(cs, &seg, R_DS); - hvf_set_segment(cpu_state, &seg, &env->segs[R_ES], false); - vmx_write_segment_descriptor(cpu_state, &seg, R_ES); + hvf_set_segment(cs, &seg, &env->segs[R_ES], false); + vmx_write_segment_descriptor(cs, &seg, R_ES); - hvf_set_segment(cpu_state, &seg, &env->segs[R_SS], false); - vmx_write_segment_descriptor(cpu_state, &seg, R_SS); + hvf_set_segment(cs, &seg, &env->segs[R_SS], false); + vmx_write_segment_descriptor(cs, &seg, R_SS); - hvf_set_segment(cpu_state, &seg, &env->segs[R_FS], false); - vmx_write_segment_descriptor(cpu_state, &seg, R_FS); + hvf_set_segment(cs, &seg, &env->segs[R_FS], false); + vmx_write_segment_descriptor(cs, &seg, R_FS); - hvf_set_segment(cpu_state, &seg, &env->segs[R_GS], false); - vmx_write_segment_descriptor(cpu_state, &seg, R_GS); + hvf_set_segment(cs, &seg, &env->segs[R_GS], false); + vmx_write_segment_descriptor(cs, &seg, R_GS); - hvf_set_segment(cpu_state, &seg, &env->tr, true); - vmx_write_segment_descriptor(cpu_state, &seg, R_TR); + hvf_set_segment(cs, &seg, &env->tr, true); + vmx_write_segment_descriptor(cs, &seg, R_TR); - hvf_set_segment(cpu_state, &seg, &env->ldt, false); - vmx_write_segment_descriptor(cpu_state, &seg, R_LDTR); + hvf_set_segment(cs, &seg, &env->ldt, false); + vmx_write_segment_descriptor(cs, &seg, R_LDTR); } -void hvf_put_msrs(CPUState *cpu_state) +void hvf_put_msrs(CPUState *cs) { - CPUX86State *env = &X86_CPU(cpu_state)->env; + CPUX86State *env = &X86_CPU(cs)->env; - hv_vcpu_write_msr(cpu_state->hvf->fd, MSR_IA32_SYSENTER_CS, + hv_vcpu_write_msr(cs->accel->fd, MSR_IA32_SYSENTER_CS, env->sysenter_cs); - hv_vcpu_write_msr(cpu_state->hvf->fd, MSR_IA32_SYSENTER_ESP, + hv_vcpu_write_msr(cs->accel->fd, MSR_IA32_SYSENTER_ESP, env->sysenter_esp); - hv_vcpu_write_msr(cpu_state->hvf->fd, MSR_IA32_SYSENTER_EIP, + hv_vcpu_write_msr(cs->accel->fd, MSR_IA32_SYSENTER_EIP, env->sysenter_eip); - hv_vcpu_write_msr(cpu_state->hvf->fd, MSR_STAR, env->star); + hv_vcpu_write_msr(cs->accel->fd, MSR_STAR, env->star); #ifdef TARGET_X86_64 - hv_vcpu_write_msr(cpu_state->hvf->fd, MSR_CSTAR, env->cstar); - hv_vcpu_write_msr(cpu_state->hvf->fd, MSR_KERNELGSBASE, env->kernelgsbase); - hv_vcpu_write_msr(cpu_state->hvf->fd, MSR_FMASK, env->fmask); - hv_vcpu_write_msr(cpu_state->hvf->fd, MSR_LSTAR, env->lstar); + hv_vcpu_write_msr(cs->accel->fd, MSR_CSTAR, env->cstar); + hv_vcpu_write_msr(cs->accel->fd, MSR_KERNELGSBASE, env->kernelgsbase); + hv_vcpu_write_msr(cs->accel->fd, MSR_FMASK, env->fmask); + hv_vcpu_write_msr(cs->accel->fd, MSR_LSTAR, env->lstar); #endif - hv_vcpu_write_msr(cpu_state->hvf->fd, MSR_GSBASE, env->segs[R_GS].base); - hv_vcpu_write_msr(cpu_state->hvf->fd, MSR_FSBASE, env->segs[R_FS].base); + hv_vcpu_write_msr(cs->accel->fd, MSR_GSBASE, env->segs[R_GS].base); + hv_vcpu_write_msr(cs->accel->fd, MSR_FSBASE, env->segs[R_FS].base); } -void hvf_get_xsave(CPUState *cpu_state) +void hvf_get_xsave(CPUState *cs) { - void *xsave = X86_CPU(cpu_state)->env.xsave_buf; - uint32_t xsave_len = X86_CPU(cpu_state)->env.xsave_buf_len; + void *xsave = X86_CPU(cs)->env.xsave_buf; + uint32_t xsave_len = X86_CPU(cs)->env.xsave_buf_len; - if (hv_vcpu_read_fpstate(cpu_state->hvf->fd, xsave, xsave_len)) { + if (hv_vcpu_read_fpstate(cs->accel->fd, xsave, xsave_len)) { abort(); } - x86_cpu_xrstor_all_areas(X86_CPU(cpu_state), xsave, xsave_len); + x86_cpu_xrstor_all_areas(X86_CPU(cs), xsave, xsave_len); } -static void hvf_get_segments(CPUState *cpu_state) +static void hvf_get_segments(CPUState *cs) { - CPUX86State *env = &X86_CPU(cpu_state)->env; + CPUX86State *env = &X86_CPU(cs)->env; struct vmx_segment seg; env->interrupt_injected = -1; - vmx_read_segment_descriptor(cpu_state, &seg, R_CS); + vmx_read_segment_descriptor(cs, &seg, R_CS); hvf_get_segment(&env->segs[R_CS], &seg); - vmx_read_segment_descriptor(cpu_state, &seg, R_DS); + vmx_read_segment_descriptor(cs, &seg, R_DS); hvf_get_segment(&env->segs[R_DS], &seg); - vmx_read_segment_descriptor(cpu_state, &seg, R_ES); + vmx_read_segment_descriptor(cs, &seg, R_ES); hvf_get_segment(&env->segs[R_ES], &seg); - vmx_read_segment_descriptor(cpu_state, &seg, R_FS); + vmx_read_segment_descriptor(cs, &seg, R_FS); hvf_get_segment(&env->segs[R_FS], &seg); - vmx_read_segment_descriptor(cpu_state, &seg, R_GS); + vmx_read_segment_descriptor(cs, &seg, R_GS); hvf_get_segment(&env->segs[R_GS], &seg); - vmx_read_segment_descriptor(cpu_state, &seg, R_SS); + vmx_read_segment_descriptor(cs, &seg, R_SS); hvf_get_segment(&env->segs[R_SS], &seg); - vmx_read_segment_descriptor(cpu_state, &seg, R_TR); + vmx_read_segment_descriptor(cs, &seg, R_TR); hvf_get_segment(&env->tr, &seg); - vmx_read_segment_descriptor(cpu_state, &seg, R_LDTR); + vmx_read_segment_descriptor(cs, &seg, R_LDTR); hvf_get_segment(&env->ldt, &seg); - env->idt.limit = rvmcs(cpu_state->hvf->fd, VMCS_GUEST_IDTR_LIMIT); - env->idt.base = rvmcs(cpu_state->hvf->fd, VMCS_GUEST_IDTR_BASE); - env->gdt.limit = rvmcs(cpu_state->hvf->fd, VMCS_GUEST_GDTR_LIMIT); - env->gdt.base = rvmcs(cpu_state->hvf->fd, VMCS_GUEST_GDTR_BASE); + env->idt.limit = rvmcs(cs->accel->fd, VMCS_GUEST_IDTR_LIMIT); + env->idt.base = rvmcs(cs->accel->fd, VMCS_GUEST_IDTR_BASE); + env->gdt.limit = rvmcs(cs->accel->fd, VMCS_GUEST_GDTR_LIMIT); + env->gdt.base = rvmcs(cs->accel->fd, VMCS_GUEST_GDTR_BASE); - env->cr[0] = rvmcs(cpu_state->hvf->fd, VMCS_GUEST_CR0); + env->cr[0] = rvmcs(cs->accel->fd, VMCS_GUEST_CR0); env->cr[2] = 0; - env->cr[3] = rvmcs(cpu_state->hvf->fd, VMCS_GUEST_CR3); - env->cr[4] = rvmcs(cpu_state->hvf->fd, VMCS_GUEST_CR4); + env->cr[3] = rvmcs(cs->accel->fd, VMCS_GUEST_CR3); + env->cr[4] = rvmcs(cs->accel->fd, VMCS_GUEST_CR4); - env->efer = rvmcs(cpu_state->hvf->fd, VMCS_GUEST_IA32_EFER); + env->efer = rvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER); } -void hvf_get_msrs(CPUState *cpu_state) +void hvf_get_msrs(CPUState *cs) { - CPUX86State *env = &X86_CPU(cpu_state)->env; + CPUX86State *env = &X86_CPU(cs)->env; uint64_t tmp; - hv_vcpu_read_msr(cpu_state->hvf->fd, MSR_IA32_SYSENTER_CS, &tmp); + hv_vcpu_read_msr(cs->accel->fd, MSR_IA32_SYSENTER_CS, &tmp); env->sysenter_cs = tmp; - hv_vcpu_read_msr(cpu_state->hvf->fd, MSR_IA32_SYSENTER_ESP, &tmp); + hv_vcpu_read_msr(cs->accel->fd, MSR_IA32_SYSENTER_ESP, &tmp); env->sysenter_esp = tmp; - hv_vcpu_read_msr(cpu_state->hvf->fd, MSR_IA32_SYSENTER_EIP, &tmp); + hv_vcpu_read_msr(cs->accel->fd, MSR_IA32_SYSENTER_EIP, &tmp); env->sysenter_eip = tmp; - hv_vcpu_read_msr(cpu_state->hvf->fd, MSR_STAR, &env->star); + hv_vcpu_read_msr(cs->accel->fd, MSR_STAR, &env->star); #ifdef TARGET_X86_64 - hv_vcpu_read_msr(cpu_state->hvf->fd, MSR_CSTAR, &env->cstar); - hv_vcpu_read_msr(cpu_state->hvf->fd, MSR_KERNELGSBASE, &env->kernelgsbase); - hv_vcpu_read_msr(cpu_state->hvf->fd, MSR_FMASK, &env->fmask); - hv_vcpu_read_msr(cpu_state->hvf->fd, MSR_LSTAR, &env->lstar); + hv_vcpu_read_msr(cs->accel->fd, MSR_CSTAR, &env->cstar); + hv_vcpu_read_msr(cs->accel->fd, MSR_KERNELGSBASE, &env->kernelgsbase); + hv_vcpu_read_msr(cs->accel->fd, MSR_FMASK, &env->fmask); + hv_vcpu_read_msr(cs->accel->fd, MSR_LSTAR, &env->lstar); #endif - hv_vcpu_read_msr(cpu_state->hvf->fd, MSR_IA32_APICBASE, &tmp); + hv_vcpu_read_msr(cs->accel->fd, MSR_IA32_APICBASE, &tmp); - env->tsc = rdtscp() + rvmcs(cpu_state->hvf->fd, VMCS_TSC_OFFSET); + env->tsc = rdtscp() + rvmcs(cs->accel->fd, VMCS_TSC_OFFSET); } -int hvf_put_registers(CPUState *cpu_state) +int hvf_put_registers(CPUState *cs) { - X86CPU *x86cpu = X86_CPU(cpu_state); + X86CPU *x86cpu = X86_CPU(cs); CPUX86State *env = &x86cpu->env; - wreg(cpu_state->hvf->fd, HV_X86_RAX, env->regs[R_EAX]); - wreg(cpu_state->hvf->fd, HV_X86_RBX, env->regs[R_EBX]); - wreg(cpu_state->hvf->fd, HV_X86_RCX, env->regs[R_ECX]); - wreg(cpu_state->hvf->fd, HV_X86_RDX, env->regs[R_EDX]); - wreg(cpu_state->hvf->fd, HV_X86_RBP, env->regs[R_EBP]); - wreg(cpu_state->hvf->fd, HV_X86_RSP, env->regs[R_ESP]); - wreg(cpu_state->hvf->fd, HV_X86_RSI, env->regs[R_ESI]); - wreg(cpu_state->hvf->fd, HV_X86_RDI, env->regs[R_EDI]); - wreg(cpu_state->hvf->fd, HV_X86_R8, env->regs[8]); - wreg(cpu_state->hvf->fd, HV_X86_R9, env->regs[9]); - wreg(cpu_state->hvf->fd, HV_X86_R10, env->regs[10]); - wreg(cpu_state->hvf->fd, HV_X86_R11, env->regs[11]); - wreg(cpu_state->hvf->fd, HV_X86_R12, env->regs[12]); - wreg(cpu_state->hvf->fd, HV_X86_R13, env->regs[13]); - wreg(cpu_state->hvf->fd, HV_X86_R14, env->regs[14]); - wreg(cpu_state->hvf->fd, HV_X86_R15, env->regs[15]); - wreg(cpu_state->hvf->fd, HV_X86_RFLAGS, env->eflags); - wreg(cpu_state->hvf->fd, HV_X86_RIP, env->eip); + wreg(cs->accel->fd, HV_X86_RAX, env->regs[R_EAX]); + wreg(cs->accel->fd, HV_X86_RBX, env->regs[R_EBX]); + wreg(cs->accel->fd, HV_X86_RCX, env->regs[R_ECX]); + wreg(cs->accel->fd, HV_X86_RDX, env->regs[R_EDX]); + wreg(cs->accel->fd, HV_X86_RBP, env->regs[R_EBP]); + wreg(cs->accel->fd, HV_X86_RSP, env->regs[R_ESP]); + wreg(cs->accel->fd, HV_X86_RSI, env->regs[R_ESI]); + wreg(cs->accel->fd, HV_X86_RDI, env->regs[R_EDI]); + wreg(cs->accel->fd, HV_X86_R8, env->regs[8]); + wreg(cs->accel->fd, HV_X86_R9, env->regs[9]); + wreg(cs->accel->fd, HV_X86_R10, env->regs[10]); + wreg(cs->accel->fd, HV_X86_R11, env->regs[11]); + wreg(cs->accel->fd, HV_X86_R12, env->regs[12]); + wreg(cs->accel->fd, HV_X86_R13, env->regs[13]); + wreg(cs->accel->fd, HV_X86_R14, env->regs[14]); + wreg(cs->accel->fd, HV_X86_R15, env->regs[15]); + wreg(cs->accel->fd, HV_X86_RFLAGS, env->eflags); + wreg(cs->accel->fd, HV_X86_RIP, env->eip); - wreg(cpu_state->hvf->fd, HV_X86_XCR0, env->xcr0); + wreg(cs->accel->fd, HV_X86_XCR0, env->xcr0); - hvf_put_xsave(cpu_state); + hvf_put_xsave(cs); - hvf_put_segments(cpu_state); + hvf_put_segments(cs); - hvf_put_msrs(cpu_state); + hvf_put_msrs(cs); - wreg(cpu_state->hvf->fd, HV_X86_DR0, env->dr[0]); - wreg(cpu_state->hvf->fd, HV_X86_DR1, env->dr[1]); - wreg(cpu_state->hvf->fd, HV_X86_DR2, env->dr[2]); - wreg(cpu_state->hvf->fd, HV_X86_DR3, env->dr[3]); - wreg(cpu_state->hvf->fd, HV_X86_DR4, env->dr[4]); - wreg(cpu_state->hvf->fd, HV_X86_DR5, env->dr[5]); - wreg(cpu_state->hvf->fd, HV_X86_DR6, env->dr[6]); - wreg(cpu_state->hvf->fd, HV_X86_DR7, env->dr[7]); + wreg(cs->accel->fd, HV_X86_DR0, env->dr[0]); + wreg(cs->accel->fd, HV_X86_DR1, env->dr[1]); + wreg(cs->accel->fd, HV_X86_DR2, env->dr[2]); + wreg(cs->accel->fd, HV_X86_DR3, env->dr[3]); + wreg(cs->accel->fd, HV_X86_DR4, env->dr[4]); + wreg(cs->accel->fd, HV_X86_DR5, env->dr[5]); + wreg(cs->accel->fd, HV_X86_DR6, env->dr[6]); + wreg(cs->accel->fd, HV_X86_DR7, env->dr[7]); return 0; } -int hvf_get_registers(CPUState *cpu_state) +int hvf_get_registers(CPUState *cs) { - X86CPU *x86cpu = X86_CPU(cpu_state); + X86CPU *x86cpu = X86_CPU(cs); CPUX86State *env = &x86cpu->env; - env->regs[R_EAX] = rreg(cpu_state->hvf->fd, HV_X86_RAX); - env->regs[R_EBX] = rreg(cpu_state->hvf->fd, HV_X86_RBX); - env->regs[R_ECX] = rreg(cpu_state->hvf->fd, HV_X86_RCX); - env->regs[R_EDX] = rreg(cpu_state->hvf->fd, HV_X86_RDX); - env->regs[R_EBP] = rreg(cpu_state->hvf->fd, HV_X86_RBP); - env->regs[R_ESP] = rreg(cpu_state->hvf->fd, HV_X86_RSP); - env->regs[R_ESI] = rreg(cpu_state->hvf->fd, HV_X86_RSI); - env->regs[R_EDI] = rreg(cpu_state->hvf->fd, HV_X86_RDI); - env->regs[8] = rreg(cpu_state->hvf->fd, HV_X86_R8); - env->regs[9] = rreg(cpu_state->hvf->fd, HV_X86_R9); - env->regs[10] = rreg(cpu_state->hvf->fd, HV_X86_R10); - env->regs[11] = rreg(cpu_state->hvf->fd, HV_X86_R11); - env->regs[12] = rreg(cpu_state->hvf->fd, HV_X86_R12); - env->regs[13] = rreg(cpu_state->hvf->fd, HV_X86_R13); - env->regs[14] = rreg(cpu_state->hvf->fd, HV_X86_R14); - env->regs[15] = rreg(cpu_state->hvf->fd, HV_X86_R15); + env->regs[R_EAX] = rreg(cs->accel->fd, HV_X86_RAX); + env->regs[R_EBX] = rreg(cs->accel->fd, HV_X86_RBX); + env->regs[R_ECX] = rreg(cs->accel->fd, HV_X86_RCX); + env->regs[R_EDX] = rreg(cs->accel->fd, HV_X86_RDX); + env->regs[R_EBP] = rreg(cs->accel->fd, HV_X86_RBP); + env->regs[R_ESP] = rreg(cs->accel->fd, HV_X86_RSP); + env->regs[R_ESI] = rreg(cs->accel->fd, HV_X86_RSI); + env->regs[R_EDI] = rreg(cs->accel->fd, HV_X86_RDI); + env->regs[8] = rreg(cs->accel->fd, HV_X86_R8); + env->regs[9] = rreg(cs->accel->fd, HV_X86_R9); + env->regs[10] = rreg(cs->accel->fd, HV_X86_R10); + env->regs[11] = rreg(cs->accel->fd, HV_X86_R11); + env->regs[12] = rreg(cs->accel->fd, HV_X86_R12); + env->regs[13] = rreg(cs->accel->fd, HV_X86_R13); + env->regs[14] = rreg(cs->accel->fd, HV_X86_R14); + env->regs[15] = rreg(cs->accel->fd, HV_X86_R15); - env->eflags = rreg(cpu_state->hvf->fd, HV_X86_RFLAGS); - env->eip = rreg(cpu_state->hvf->fd, HV_X86_RIP); + env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); + env->eip = rreg(cs->accel->fd, HV_X86_RIP); - hvf_get_xsave(cpu_state); - env->xcr0 = rreg(cpu_state->hvf->fd, HV_X86_XCR0); + hvf_get_xsave(cs); + env->xcr0 = rreg(cs->accel->fd, HV_X86_XCR0); - hvf_get_segments(cpu_state); - hvf_get_msrs(cpu_state); + hvf_get_segments(cs); + hvf_get_msrs(cs); - env->dr[0] = rreg(cpu_state->hvf->fd, HV_X86_DR0); - env->dr[1] = rreg(cpu_state->hvf->fd, HV_X86_DR1); - env->dr[2] = rreg(cpu_state->hvf->fd, HV_X86_DR2); - env->dr[3] = rreg(cpu_state->hvf->fd, HV_X86_DR3); - env->dr[4] = rreg(cpu_state->hvf->fd, HV_X86_DR4); - env->dr[5] = rreg(cpu_state->hvf->fd, HV_X86_DR5); - env->dr[6] = rreg(cpu_state->hvf->fd, HV_X86_DR6); - env->dr[7] = rreg(cpu_state->hvf->fd, HV_X86_DR7); + env->dr[0] = rreg(cs->accel->fd, HV_X86_DR0); + env->dr[1] = rreg(cs->accel->fd, HV_X86_DR1); + env->dr[2] = rreg(cs->accel->fd, HV_X86_DR2); + env->dr[3] = rreg(cs->accel->fd, HV_X86_DR3); + env->dr[4] = rreg(cs->accel->fd, HV_X86_DR4); + env->dr[5] = rreg(cs->accel->fd, HV_X86_DR5); + env->dr[6] = rreg(cs->accel->fd, HV_X86_DR6); + env->dr[7] = rreg(cs->accel->fd, HV_X86_DR7); x86_update_hflags(env); return 0; } -static void vmx_set_int_window_exiting(CPUState *cpu) +static void vmx_set_int_window_exiting(CPUState *cs) { uint64_t val; - val = rvmcs(cpu->hvf->fd, VMCS_PRI_PROC_BASED_CTLS); - wvmcs(cpu->hvf->fd, VMCS_PRI_PROC_BASED_CTLS, val | + val = rvmcs(cs->accel->fd, VMCS_PRI_PROC_BASED_CTLS); + wvmcs(cs->accel->fd, VMCS_PRI_PROC_BASED_CTLS, val | VMCS_PRI_PROC_BASED_CTLS_INT_WINDOW_EXITING); } -void vmx_clear_int_window_exiting(CPUState *cpu) +void vmx_clear_int_window_exiting(CPUState *cs) { uint64_t val; - val = rvmcs(cpu->hvf->fd, VMCS_PRI_PROC_BASED_CTLS); - wvmcs(cpu->hvf->fd, VMCS_PRI_PROC_BASED_CTLS, val & + val = rvmcs(cs->accel->fd, VMCS_PRI_PROC_BASED_CTLS); + wvmcs(cs->accel->fd, VMCS_PRI_PROC_BASED_CTLS, val & ~VMCS_PRI_PROC_BASED_CTLS_INT_WINDOW_EXITING); } -bool hvf_inject_interrupts(CPUState *cpu_state) +bool hvf_inject_interrupts(CPUState *cs) { - X86CPU *x86cpu = X86_CPU(cpu_state); + X86CPU *x86cpu = X86_CPU(cs); CPUX86State *env = &x86cpu->env; uint8_t vector; @@ -372,89 +372,89 @@ bool hvf_inject_interrupts(CPUState *cpu_state) uint64_t info = 0; if (have_event) { info = vector | intr_type | VMCS_INTR_VALID; - uint64_t reason = rvmcs(cpu_state->hvf->fd, VMCS_EXIT_REASON); + uint64_t reason = rvmcs(cs->accel->fd, VMCS_EXIT_REASON); if (env->nmi_injected && reason != EXIT_REASON_TASK_SWITCH) { - vmx_clear_nmi_blocking(cpu_state); + vmx_clear_nmi_blocking(cs); } if (!(env->hflags2 & HF2_NMI_MASK) || intr_type != VMCS_INTR_T_NMI) { info &= ~(1 << 12); /* clear undefined bit */ if (intr_type == VMCS_INTR_T_SWINTR || intr_type == VMCS_INTR_T_SWEXCEPTION) { - wvmcs(cpu_state->hvf->fd, VMCS_ENTRY_INST_LENGTH, env->ins_len); + wvmcs(cs->accel->fd, VMCS_ENTRY_INST_LENGTH, env->ins_len); } if (env->has_error_code) { - wvmcs(cpu_state->hvf->fd, VMCS_ENTRY_EXCEPTION_ERROR, + wvmcs(cs->accel->fd, VMCS_ENTRY_EXCEPTION_ERROR, env->error_code); /* Indicate that VMCS_ENTRY_EXCEPTION_ERROR is valid */ info |= VMCS_INTR_DEL_ERRCODE; } /*printf("reinject %lx err %d\n", info, err);*/ - wvmcs(cpu_state->hvf->fd, VMCS_ENTRY_INTR_INFO, info); + wvmcs(cs->accel->fd, VMCS_ENTRY_INTR_INFO, info); }; } - if (cpu_state->interrupt_request & CPU_INTERRUPT_NMI) { + if (cs->interrupt_request & CPU_INTERRUPT_NMI) { if (!(env->hflags2 & HF2_NMI_MASK) && !(info & VMCS_INTR_VALID)) { - cpu_state->interrupt_request &= ~CPU_INTERRUPT_NMI; + cs->interrupt_request &= ~CPU_INTERRUPT_NMI; info = VMCS_INTR_VALID | VMCS_INTR_T_NMI | EXCP02_NMI; - wvmcs(cpu_state->hvf->fd, VMCS_ENTRY_INTR_INFO, info); + wvmcs(cs->accel->fd, VMCS_ENTRY_INTR_INFO, info); } else { - vmx_set_nmi_window_exiting(cpu_state); + vmx_set_nmi_window_exiting(cs); } } if (!(env->hflags & HF_INHIBIT_IRQ_MASK) && - (cpu_state->interrupt_request & CPU_INTERRUPT_HARD) && + (cs->interrupt_request & CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK) && !(info & VMCS_INTR_VALID)) { - int line = cpu_get_pic_interrupt(&x86cpu->env); - cpu_state->interrupt_request &= ~CPU_INTERRUPT_HARD; + int line = cpu_get_pic_interrupt(env); + cs->interrupt_request &= ~CPU_INTERRUPT_HARD; if (line >= 0) { - wvmcs(cpu_state->hvf->fd, VMCS_ENTRY_INTR_INFO, line | + wvmcs(cs->accel->fd, VMCS_ENTRY_INTR_INFO, line | VMCS_INTR_VALID | VMCS_INTR_T_HWINTR); } } - if (cpu_state->interrupt_request & CPU_INTERRUPT_HARD) { - vmx_set_int_window_exiting(cpu_state); + if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + vmx_set_int_window_exiting(cs); } - return (cpu_state->interrupt_request + return (cs->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)); } -int hvf_process_events(CPUState *cpu_state) +int hvf_process_events(CPUState *cs) { - X86CPU *cpu = X86_CPU(cpu_state); + X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - if (!cpu_state->vcpu_dirty) { + if (!cs->vcpu_dirty) { /* light weight sync for CPU_INTERRUPT_HARD and IF_MASK */ - env->eflags = rreg(cpu_state->hvf->fd, HV_X86_RFLAGS); + env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); } - if (cpu_state->interrupt_request & CPU_INTERRUPT_INIT) { - cpu_synchronize_state(cpu_state); + if (cs->interrupt_request & CPU_INTERRUPT_INIT) { + cpu_synchronize_state(cs); do_cpu_init(cpu); } - if (cpu_state->interrupt_request & CPU_INTERRUPT_POLL) { - cpu_state->interrupt_request &= ~CPU_INTERRUPT_POLL; + if (cs->interrupt_request & CPU_INTERRUPT_POLL) { + cs->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(cpu->apic_state); } - if (((cpu_state->interrupt_request & CPU_INTERRUPT_HARD) && + if (((cs->interrupt_request & CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || - (cpu_state->interrupt_request & CPU_INTERRUPT_NMI)) { - cpu_state->halted = 0; + (cs->interrupt_request & CPU_INTERRUPT_NMI)) { + cs->halted = 0; } - if (cpu_state->interrupt_request & CPU_INTERRUPT_SIPI) { - cpu_synchronize_state(cpu_state); + if (cs->interrupt_request & CPU_INTERRUPT_SIPI) { + cpu_synchronize_state(cs); do_cpu_sipi(cpu); } - if (cpu_state->interrupt_request & CPU_INTERRUPT_TPR) { - cpu_state->interrupt_request &= ~CPU_INTERRUPT_TPR; - cpu_synchronize_state(cpu_state); + if (cs->interrupt_request & CPU_INTERRUPT_TPR) { + cs->interrupt_request &= ~CPU_INTERRUPT_TPR; + cpu_synchronize_state(cs); apic_handle_tpr_access_report(cpu->apic_state, env->eip, env->tpr_access_type); } - return cpu_state->halted; + return cs->halted; } diff --git a/target/i386/hvf/x86hvf.h b/target/i386/hvf/x86hvf.h index db6003d6bd..423a89b6ad 100644 --- a/target/i386/hvf/x86hvf.h +++ b/target/i386/hvf/x86hvf.h @@ -20,15 +20,15 @@ #include "cpu.h" #include "x86_descr.h" -int hvf_process_events(CPUState *); -bool hvf_inject_interrupts(CPUState *); -void hvf_set_segment(struct CPUState *cpu, struct vmx_segment *vmx_seg, +int hvf_process_events(CPUState *cs); +bool hvf_inject_interrupts(CPUState *cs); +void hvf_set_segment(CPUState *cs, struct vmx_segment *vmx_seg, SegmentCache *qseg, bool is_tr); void hvf_get_segment(SegmentCache *qseg, struct vmx_segment *vmx_seg); -void hvf_put_xsave(CPUState *cpu_state); -void hvf_put_msrs(CPUState *cpu_state); -void hvf_get_xsave(CPUState *cpu_state); -void hvf_get_msrs(CPUState *cpu_state); -void vmx_clear_int_window_exiting(CPUState *cpu); -void vmx_update_tpr(CPUState *cpu); +void hvf_put_xsave(CPUState *cs); +void hvf_put_msrs(CPUState *cs); +void hvf_get_xsave(CPUState *cs); +void hvf_get_msrs(CPUState *cs); +void vmx_clear_int_window_exiting(CPUState *cs); +void vmx_update_tpr(CPUState *cs); #endif diff --git a/target/i386/kvm/hyperv-stub.c b/target/i386/kvm/hyperv-stub.c index 778ed782e6..3263dcf05d 100644 --- a/target/i386/kvm/hyperv-stub.c +++ b/target/i386/kvm/hyperv-stub.c @@ -52,3 +52,7 @@ void hyperv_x86_synic_reset(X86CPU *cpu) void hyperv_x86_synic_update(X86CPU *cpu) { } + +void hyperv_x86_set_vmbus_recommended_features_enabled(void) +{ +} diff --git a/target/i386/kvm/hyperv.c b/target/i386/kvm/hyperv.c index 9026ef3a81..f2a3fe650a 100644 --- a/target/i386/kvm/hyperv.c +++ b/target/i386/kvm/hyperv.c @@ -23,6 +23,10 @@ int hyperv_x86_synic_add(X86CPU *cpu) return 0; } +/* + * All devices possibly using SynIC have to be reset before calling this to let + * them remove their SINT routes first. + */ void hyperv_x86_synic_reset(X86CPU *cpu) { hyperv_synic_reset(CPU(cpu)); @@ -41,9 +45,9 @@ void hyperv_x86_synic_update(X86CPU *cpu) static void async_synic_update(CPUState *cs, run_on_cpu_data data) { - qemu_mutex_lock_iothread(); + bql_lock(); hyperv_x86_synic_update(X86_CPU(cs)); - qemu_mutex_unlock_iothread(); + bql_unlock(); } int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) @@ -145,3 +149,8 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) return -1; } } + +void hyperv_x86_set_vmbus_recommended_features_enabled(void) +{ + hyperv_set_vmbus_recommended_features_enabled(); +} diff --git a/target/i386/kvm/hyperv.h b/target/i386/kvm/hyperv.h index 67543296c3..e3982c8f4d 100644 --- a/target/i386/kvm/hyperv.h +++ b/target/i386/kvm/hyperv.h @@ -26,4 +26,6 @@ int hyperv_x86_synic_add(X86CPU *cpu); void hyperv_x86_synic_reset(X86CPU *cpu); void hyperv_x86_synic_update(X86CPU *cpu); +void hyperv_x86_set_vmbus_recommended_features_enabled(void); + #endif diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index 7237378a7d..9c791b7b05 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -35,8 +35,9 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) * x86_cpu_realize(): * -> x86_cpu_expand_features() * -> cpu_exec_realizefn(): - * -> accel_cpu_realizefn() + * -> accel_cpu_common_realize() * kvm_cpu_realizefn() -> host_cpu_realizefn() + * -> cpu_common_realizefn() * -> check/update ucode_rev, phys_bits, mwait */ if (cpu->max_features) { @@ -190,7 +191,7 @@ static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data) { AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); - acc->cpu_realizefn = kvm_cpu_realizefn; + acc->cpu_target_realize = kvm_cpu_realizefn; acc->cpu_instance_init = kvm_cpu_instance_init; } static const TypeInfo kvm_cpu_accel_type_info = { diff --git a/target/i386/kvm/kvm-stub.c b/target/i386/kvm/kvm-stub.c deleted file mode 100644 index f6e7e4466e..0000000000 --- a/target/i386/kvm/kvm-stub.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * QEMU KVM x86 specific function stubs - * - * Copyright Linaro Limited 2012 - * - * Author: Peter Maydell - * - * 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 "cpu.h" -#include "kvm_i386.h" - -#ifndef __OPTIMIZE__ -bool kvm_has_smm(void) -{ - return 1; -} - -bool kvm_enable_x2apic(void) -{ - return false; -} - -/* This function is only called inside conditionals which we - * rely on the compiler to optimize out when CONFIG_KVM is not - * defined. - */ -uint32_t kvm_arch_get_supported_cpuid(KVMState *env, uint32_t function, - uint32_t index, int reg) -{ - abort(); -} -#endif - -bool kvm_hv_vpindex_settable(void) -{ - return false; -} - -bool kvm_hyperv_expand_features(X86CPU *cpu, Error **errp) -{ - abort(); -} diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index f148a6d52f..e68cbe9293 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -15,12 +15,14 @@ #include "qemu/osdep.h" #include "qapi/qapi-events-run-state.h" #include "qapi/error.h" +#include "qapi/visitor.h" #include #include #include #include #include "standard-headers/asm-x86/kvm_para.h" +#include "hw/xen/interface/arch-x86/cpuid.h" #include "cpu.h" #include "host-cpu.h" @@ -30,16 +32,20 @@ #include "sysemu/runstate.h" #include "kvm_i386.h" #include "sev.h" +#include "xen-emu.h" #include "hyperv.h" #include "hyperv-proto.h" #include "exec/gdbstub.h" #include "qemu/host-utils.h" #include "qemu/main-loop.h" +#include "qemu/ratelimit.h" #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qemu/memalign.h" #include "hw/i386/x86.h" +#include "hw/i386/kvm/xen_evtchn.h" +#include "hw/i386/pc.h" #include "hw/i386/apic.h" #include "hw/i386/apic_internal.h" #include "hw/i386/apic-msidef.h" @@ -47,6 +53,8 @@ #include "hw/i386/x86-iommu.h" #include "hw/i386/e820_memory_layout.h" +#include "hw/xen/xen.h" + #include "hw/pci/pci.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" @@ -83,6 +91,15 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_INFO(SET_TSS_ADDR), KVM_CAP_INFO(EXT_CPUID), KVM_CAP_INFO(MP_STATE), + KVM_CAP_INFO(SIGNAL_MSI), + KVM_CAP_INFO(IRQ_ROUTING), + KVM_CAP_INFO(DEBUGREGS), + KVM_CAP_INFO(XSAVE), + KVM_CAP_INFO(VCPU_EVENTS), + KVM_CAP_INFO(X86_ROBUST_SINGLESTEP), + KVM_CAP_INFO(MCE), + KVM_CAP_INFO(ADJUST_CLOCK), + KVM_CAP_INFO(SET_IDENTITY_MAP_ADDR), KVM_CAP_LAST_INFO }; @@ -126,12 +143,11 @@ static uint32_t has_architectural_pmu_version; static uint32_t num_architectural_pmu_gp_counters; static uint32_t num_architectural_pmu_fixed_counters; -static int has_xsave; static int has_xsave2; static int has_xcrs; -static int has_pit_state2; static int has_sregs2; static int has_exception_payload; +static int has_triple_fault_event; static bool has_msr_mcg_ext_ctl; @@ -139,15 +155,12 @@ static struct kvm_cpuid2 *cpuid_cache; static struct kvm_cpuid2 *hv_cpuid_cache; static struct kvm_msr_list *kvm_feature_msrs; +static KVMMSRHandlers msr_handlers[KVM_MSR_FILTER_MAX_RANGES]; + #define BUS_LOCK_SLICE_TIME 1000000000ULL /* ns */ static RateLimit bus_lock_ratelimit_ctrl; static int kvm_get_one_msr(X86CPU *cpu, int index, uint64_t *value); -int kvm_has_pit_state2(void) -{ - return has_pit_state2; -} - bool kvm_has_smm(void) { return kvm_vm_check_extension(kvm_state, KVM_CAP_X86_SMM); @@ -157,12 +170,7 @@ bool kvm_has_adjust_clock_stable(void) { int ret = kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK); - return (ret == KVM_CLOCK_TSC_STABLE); -} - -bool kvm_has_adjust_clock(void) -{ - return kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK); + return (ret & KVM_CLOCK_TSC_STABLE); } bool kvm_has_exception_payload(void) @@ -347,7 +355,7 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, { struct kvm_cpuid2 *cpuid; uint32_t ret = 0; - uint32_t cpuid_1_edx; + uint32_t cpuid_1_edx, unused; uint64_t bitmask; cpuid = get_supported_cpuid(s); @@ -362,6 +370,8 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, if (function == 1 && reg == R_EDX) { /* KVM before 2.6.30 misreports the following features */ ret |= CPUID_MTRR | CPUID_PAT | CPUID_MCE | CPUID_MCA; + /* KVM never reports CPUID_HT but QEMU can support when vcpus > 1 */ + ret |= CPUID_HT; } else if (function == 1 && reg == R_ECX) { /* We can set the hypervisor flag, even if KVM does not return it on * GET_SUPPORTED_CPUID @@ -394,10 +404,20 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, } else if (function == 6 && reg == R_EAX) { ret |= CPUID_6_EAX_ARAT; /* safe to allow because of emulated APIC */ } else if (function == 7 && index == 0 && reg == R_EBX) { + /* Not new instructions, just an optimization. */ + uint32_t ebx; + host_cpuid(7, 0, &unused, &ebx, &unused, &unused); + ret |= ebx & CPUID_7_0_EBX_ERMS; + if (host_tsx_broken()) { ret &= ~(CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_HLE); } } else if (function == 7 && index == 0 && reg == R_EDX) { + /* Not new instructions, just an optimization. */ + uint32_t edx; + host_cpuid(7, 0, &unused, &unused, &unused, &edx); + ret |= edx & CPUID_7_0_EDX_FSRM; + /* * Linux v4.17-v4.20 incorrectly return ARCH_CAPABILITIES on SVM hosts. * We can detect the bug by checking if MSR_IA32_ARCH_CAPABILITIES is @@ -406,6 +426,15 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, if (!has_msr_arch_capabs) { ret &= ~CPUID_7_0_EDX_ARCH_CAPABILITIES; } + } else if (function == 7 && index == 1 && reg == R_EAX) { + /* Not new instructions, just an optimization. */ + uint32_t eax; + host_cpuid(7, 1, &eax, &unused, &unused, &unused); + ret |= eax & (CPUID_7_1_EAX_FZRM | CPUID_7_1_EAX_FSRS | CPUID_7_1_EAX_FSRC); + } else if (function == 7 && index == 2 && reg == R_EDX) { + uint32_t edx; + host_cpuid(7, 2, &unused, &unused, &unused, &edx); + ret |= edx & CPUID_7_2_EDX_MCDT_NO; } else if (function == 0xd && index == 0 && (reg == R_EAX || reg == R_EDX)) { /* @@ -545,14 +574,8 @@ uint64_t kvm_arch_get_supported_msr_feature(KVMState *s, uint32_t index) static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap, int *max_banks) { - int r; - - r = kvm_check_extension(s, KVM_CAP_MCE); - if (r > 0) { - *max_banks = r; - return kvm_ioctl(s, KVM_X86_GET_MCE_CAP_SUPPORTED, mce_cap); - } - return -ENOSYS; + *max_banks = kvm_check_extension(s, KVM_CAP_MCE); + return kvm_ioctl(s, KVM_X86_GET_MCE_CAP_SUPPORTED, mce_cap); } static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code) @@ -655,15 +678,6 @@ void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr) emit_hypervisor_memory_failure(MEMORY_FAILURE_ACTION_IGNORE, false); } -static void kvm_reset_exception(CPUX86State *env) -{ - env->exception_nr = -1; - env->exception_pending = 0; - env->exception_injected = 0; - env->exception_has_payload = false; - env->exception_payload = 0; -} - static void kvm_queue_exception(CPUX86State *env, int32_t exception_nr, uint8_t exception_has_payload, @@ -696,38 +710,6 @@ static void kvm_queue_exception(CPUX86State *env, } } -static int kvm_inject_mce_oldstyle(X86CPU *cpu) -{ - CPUX86State *env = &cpu->env; - - if (!kvm_has_vcpu_events() && env->exception_nr == EXCP12_MCHK) { - unsigned int bank, bank_num = env->mcg_cap & 0xff; - struct kvm_x86_mce mce; - - kvm_reset_exception(env); - - /* - * There must be at least one bank in use if an MCE is pending. - * Find it and use its values for the event injection. - */ - for (bank = 0; bank < bank_num; bank++) { - if (env->mce_banks[bank * 4 + 1] & MCI_STATUS_VAL) { - break; - } - } - assert(bank < bank_num); - - mce.bank = bank; - mce.status = env->mce_banks[bank * 4 + 1]; - mce.mcg_status = env->mcg_status; - mce.addr = env->mce_banks[bank * 4 + 2]; - mce.misc = env->mce_banks[bank * 4 + 3]; - - return kvm_vcpu_ioctl(CPU(cpu), KVM_X86_SET_MCE, &mce); - } - return 0; -} - static void cpu_update_state(void *opaque, bool running, RunState state) { CPUX86State *env = opaque; @@ -1571,7 +1553,7 @@ static int hyperv_init_vcpu(X86CPU *cpu) error_setg(&hv_passthrough_mig_blocker, "'hv-passthrough' CPU flag prevents migration, use explicit" " set of hv-* flags instead"); - ret = migrate_add_blocker(hv_passthrough_mig_blocker, &local_err); + ret = migrate_add_blocker(&hv_passthrough_mig_blocker, &local_err); if (ret < 0) { error_report_err(local_err); return ret; @@ -1585,7 +1567,7 @@ static int hyperv_init_vcpu(X86CPU *cpu) " use explicit 'hv-no-nonarch-coresharing=on' instead (but" " make sure SMT is disabled and/or that vCPUs are properly" " pinned)"); - ret = migrate_add_blocker(hv_no_nonarch_cs_mig_blocker, &local_err); + ret = migrate_add_blocker(&hv_no_nonarch_cs_mig_blocker, &local_err); if (ret < 0) { error_report_err(local_err); return ret; @@ -1668,6 +1650,13 @@ static int hyperv_init_vcpu(X86CPU *cpu) } } + /* Skip SynIC and VP_INDEX since they are hard deps already */ + if (hyperv_feat_enabled(cpu, HYPERV_FEAT_STIMER) && + hyperv_feat_enabled(cpu, HYPERV_FEAT_VAPIC) && + hyperv_feat_enabled(cpu, HYPERV_FEAT_RUNTIME)) { + hyperv_x86_set_vmbus_recommended_features_enabled(); + } + return 0; } @@ -1679,10 +1668,8 @@ static void kvm_init_xsave(CPUX86State *env) { if (has_xsave2) { env->xsave_buf_len = QEMU_ALIGN_UP(has_xsave2, 4096); - } else if (has_xsave) { - env->xsave_buf_len = sizeof(struct kvm_xsave); } else { - return; + env->xsave_buf_len = sizeof(struct kvm_xsave); } env->xsave_buf = qemu_memalign(4096, env->xsave_buf_len); @@ -1695,6 +1682,30 @@ static void kvm_init_xsave(CPUX86State *env) env->xsave_buf_len); } +static void kvm_init_nested_state(CPUX86State *env) +{ + struct kvm_vmx_nested_state_hdr *vmx_hdr; + uint32_t size; + + if (!env->nested_state) { + return; + } + + size = env->nested_state->size; + + memset(env->nested_state, 0, size); + env->nested_state->size = size; + + if (cpu_has_vmx(env)) { + env->nested_state->format = KVM_STATE_NESTED_FORMAT_VMX; + vmx_hdr = &env->nested_state->hdr.vmx; + vmx_hdr->vmxon_pa = -1ull; + vmx_hdr->vmcs12_pa = -1ull; + } else if (cpu_has_svm(env)) { + env->nested_state->format = KVM_STATE_NESTED_FORMAT_SVM; + } +} + int kvm_arch_init_vcpu(CPUState *cs) { struct { @@ -1771,7 +1782,86 @@ int kvm_arch_init_vcpu(CPUState *cs) has_msr_hv_hypercall = true; } - if (cpu->expose_kvm) { + if (cs->kvm_state->xen_version) { +#ifdef CONFIG_XEN_EMU + struct kvm_cpuid_entry2 *xen_max_leaf; + + memcpy(signature, "XenVMMXenVMM", 12); + + xen_max_leaf = c = &cpuid_data.entries[cpuid_i++]; + c->function = kvm_base + XEN_CPUID_SIGNATURE; + c->eax = kvm_base + XEN_CPUID_TIME; + c->ebx = signature[0]; + c->ecx = signature[1]; + c->edx = signature[2]; + + c = &cpuid_data.entries[cpuid_i++]; + c->function = kvm_base + XEN_CPUID_VENDOR; + c->eax = cs->kvm_state->xen_version; + c->ebx = 0; + c->ecx = 0; + c->edx = 0; + + c = &cpuid_data.entries[cpuid_i++]; + c->function = kvm_base + XEN_CPUID_HVM_MSR; + /* Number of hypercall-transfer pages */ + c->eax = 1; + /* Hypercall MSR base address */ + if (hyperv_enabled(cpu)) { + c->ebx = XEN_HYPERCALL_MSR_HYPERV; + kvm_xen_init(cs->kvm_state, c->ebx); + } else { + c->ebx = XEN_HYPERCALL_MSR; + } + c->ecx = 0; + c->edx = 0; + + c = &cpuid_data.entries[cpuid_i++]; + c->function = kvm_base + XEN_CPUID_TIME; + c->eax = ((!!tsc_is_stable_and_known(env) << 1) | + (!!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_RDTSCP) << 2)); + /* default=0 (emulate if necessary) */ + c->ebx = 0; + /* guest tsc frequency */ + c->ecx = env->user_tsc_khz; + /* guest tsc incarnation (migration count) */ + c->edx = 0; + + c = &cpuid_data.entries[cpuid_i++]; + c->function = kvm_base + XEN_CPUID_HVM; + xen_max_leaf->eax = kvm_base + XEN_CPUID_HVM; + if (cs->kvm_state->xen_version >= XEN_VERSION(4, 5)) { + c->function = kvm_base + XEN_CPUID_HVM; + + if (cpu->xen_vapic) { + c->eax |= XEN_HVM_CPUID_APIC_ACCESS_VIRT; + c->eax |= XEN_HVM_CPUID_X2APIC_VIRT; + } + + c->eax |= XEN_HVM_CPUID_IOMMU_MAPPINGS; + + if (cs->kvm_state->xen_version >= XEN_VERSION(4, 6)) { + c->eax |= XEN_HVM_CPUID_VCPU_ID_PRESENT; + c->ebx = cs->cpu_index; + } + + if (cs->kvm_state->xen_version >= XEN_VERSION(4, 17)) { + c->eax |= XEN_HVM_CPUID_UPCALL_VECTOR; + } + } + + r = kvm_xen_init_vcpu(cs); + if (r) { + return r; + } + + kvm_base += 0x100; +#else /* CONFIG_XEN_EMU */ + /* This should never happen as kvm_arch_init() would have died first. */ + fprintf(stderr, "Cannot enable Xen CPUID without Xen support\n"); + abort(); +#endif + } else if (cpu->expose_kvm) { memcpy(signature, "KVMKVMKVM\0\0\0", 12); c = &cpuid_data.entries[cpuid_i++]; c->function = KVM_CPUID_SIGNATURE | kvm_base; @@ -1831,6 +1921,7 @@ int kvm_arch_init_vcpu(CPUState *cs) } case 0x1f: if (env->nr_dies < 2) { + cpuid_i--; break; } /* fallthrough */ @@ -1842,10 +1933,6 @@ int kvm_arch_init_vcpu(CPUState *cs) break; } - if (i == 0x1f && j == 64) { - break; - } - c->function = i; c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; c->index = j; @@ -1871,7 +1958,6 @@ int kvm_arch_init_vcpu(CPUState *cs) c = &cpuid_data.entries[cpuid_i++]; } break; - case 0x7: case 0x12: for (j = 0; ; j++) { c->function = i; @@ -1891,6 +1977,7 @@ int kvm_arch_init_vcpu(CPUState *cs) c = &cpuid_data.entries[cpuid_i++]; } break; + case 0x7: case 0x14: case 0x1d: case 0x1e: { @@ -2023,8 +2110,7 @@ int kvm_arch_init_vcpu(CPUState *cs) if (((env->cpuid_version >> 8)&0xF) >= 6 && (env->features[FEAT_1_EDX] & (CPUID_MCE | CPUID_MCA)) == - (CPUID_MCE | CPUID_MCA) - && kvm_check_extension(cs->kvm_state, KVM_CAP_MCE) > 0) { + (CPUID_MCE | CPUID_MCA)) { uint64_t mcg_cap, unsupported_caps; int banks; int ret; @@ -2082,7 +2168,7 @@ int kvm_arch_init_vcpu(CPUState *cs) error_setg(&invtsc_mig_blocker, "State blocked by non-migratable CPU device" " (invtsc flag)"); - r = migrate_add_blocker(invtsc_mig_blocker, &local_err); + r = migrate_add_blocker(&invtsc_mig_blocker, &local_err); if (r < 0) { error_report_err(local_err); return r; @@ -2122,19 +2208,10 @@ int kvm_arch_init_vcpu(CPUState *cs) assert(max_nested_state_len >= offsetof(struct kvm_nested_state, data)); if (cpu_has_vmx(env) || cpu_has_svm(env)) { - struct kvm_vmx_nested_state_hdr *vmx_hdr; - env->nested_state = g_malloc0(max_nested_state_len); env->nested_state->size = max_nested_state_len; - if (cpu_has_vmx(env)) { - env->nested_state->format = KVM_STATE_NESTED_FORMAT_VMX; - vmx_hdr = &env->nested_state->hdr.vmx; - vmx_hdr->vmxon_pa = -1ull; - vmx_hdr->vmcs12_pa = -1ull; - } else { - env->nested_state->format = KVM_STATE_NESTED_FORMAT_SVM; - } + kvm_init_nested_state(env); } } @@ -2149,7 +2226,7 @@ int kvm_arch_init_vcpu(CPUState *cs) return 0; fail: - migrate_del_blocker(invtsc_mig_blocker); + migrate_del_blocker(&invtsc_mig_blocker); return r; } @@ -2161,15 +2238,11 @@ int kvm_arch_destroy_vcpu(CPUState *cs) g_free(env->xsave_buf); - if (cpu->kvm_msr_buf) { - g_free(cpu->kvm_msr_buf); - cpu->kvm_msr_buf = NULL; - } + g_free(cpu->kvm_msr_buf); + cpu->kvm_msr_buf = NULL; - if (env->nested_state) { - g_free(env->nested_state); - env->nested_state = NULL; - } + g_free(env->nested_state); + env->nested_state = NULL; qemu_del_vm_change_state_handler(cpu->vmsentry); @@ -2188,18 +2261,30 @@ void kvm_arch_reset_vcpu(X86CPU *cpu) env->mp_state = KVM_MP_STATE_RUNNABLE; } + /* enabled by default */ + env->poll_control_msr = 1; + + kvm_init_nested_state(env); + + sev_es_set_reset_vector(CPU(cpu)); +} + +void kvm_arch_after_reset_vcpu(X86CPU *cpu) +{ + CPUX86State *env = &cpu->env; + int i; + + /* + * Reset SynIC after all other devices have been reset to let them remove + * their SINT routes first. + */ if (hyperv_feat_enabled(cpu, HYPERV_FEAT_SYNIC)) { - int i; for (i = 0; i < ARRAY_SIZE(env->msr_hv_synic_sint); i++) { env->msr_hv_synic_sint[i] = HV_SINT_MASKED; } hyperv_x86_synic_reset(cpu); } - /* enabled by default */ - env->poll_control_msr = 1; - - sev_es_set_reset_vector(CPU(cpu)); } void kvm_arch_do_init_vcpu(X86CPU *cpu) @@ -2235,8 +2320,7 @@ static int kvm_get_supported_feature_msrs(KVMState *s) } assert(msr_list.nmsrs > 0); - kvm_feature_msrs = (struct kvm_msr_list *) \ - g_malloc0(sizeof(msr_list) + + kvm_feature_msrs = g_malloc0(sizeof(msr_list) + msr_list.nmsrs * sizeof(msr_list.indices[0])); kvm_feature_msrs->nmsrs = msr_list.nmsrs; @@ -2384,6 +2468,17 @@ static int kvm_get_supported_msrs(KVMState *s) return ret; } +static bool kvm_rdmsr_core_thread_count(X86CPU *cpu, uint32_t msr, + uint64_t *val) +{ + CPUState *cs = CPU(cpu); + + *val = cs->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */ + *val |= ((uint32_t)cs->nr_cores << 16); /* core count, bits 31..16 */ + + return true; +} + static Notifier smram_machine_done; static KVMMemoryListener smram_listener; static AddressSpace smram_address_space; @@ -2418,6 +2513,11 @@ static void register_smram_listener(Notifier *n, void *unused) &smram_address_space, 1, "kvm-smram"); } +int kvm_arch_get_default_type(MachineState *ms) +{ + return 0; +} + int kvm_arch_init(MachineState *ms, KVMState *s) { uint64_t identity_base = 0xfffbc000; @@ -2444,14 +2544,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) return ret; } - if (!kvm_check_extension(s, KVM_CAP_IRQ_ROUTING)) { - error_report("kvm: KVM_CAP_IRQ_ROUTING not supported by KVM"); - return -ENOTSUP; - } - - has_xsave = kvm_check_extension(s, KVM_CAP_XSAVE); has_xcrs = kvm_check_extension(s, KVM_CAP_XCRS); - has_pit_state2 = kvm_check_extension(s, KVM_CAP_PIT_STATE2); has_sregs2 = kvm_check_extension(s, KVM_CAP_SREGS2) > 0; hv_vpindex_settable = kvm_check_extension(s, KVM_CAP_HYPERV_VP_INDEX); @@ -2466,6 +2559,34 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } } + has_triple_fault_event = kvm_check_extension(s, KVM_CAP_X86_TRIPLE_FAULT_EVENT); + if (has_triple_fault_event) { + ret = kvm_vm_enable_cap(s, KVM_CAP_X86_TRIPLE_FAULT_EVENT, 0, true); + if (ret < 0) { + error_report("kvm: Failed to enable triple fault event cap: %s", + strerror(-ret)); + return ret; + } + } + + if (s->xen_version) { +#ifdef CONFIG_XEN_EMU + if (!object_dynamic_cast(OBJECT(ms), TYPE_PC_MACHINE)) { + error_report("kvm: Xen support only available in PC machine"); + return -ENOTSUP; + } + /* hyperv_enabled() doesn't work yet. */ + uint32_t msr = XEN_HYPERCALL_MSR; + ret = kvm_xen_init(s, msr); + if (ret < 0) { + return ret; + } +#else + error_report("kvm: Xen support not enabled in qemu"); + return -ENOTSUP; +#endif + } + ret = kvm_get_supported_msrs(s); if (ret < 0) { return ret; @@ -2481,20 +2602,13 @@ int kvm_arch_init(MachineState *ms, KVMState *s) * In order to use vm86 mode, an EPT identity map and a TSS are needed. * Since these must be part of guest physical memory, we need to allocate * them, both by setting their start addresses in the kernel and by - * creating a corresponding e820 entry. We need 4 pages before the BIOS. - * - * Older KVM versions may not support setting the identity map base. In - * that case we need to stick with the default, i.e. a 256K maximum BIOS - * size. + * creating a corresponding e820 entry. We need 4 pages before the BIOS, + * so this value allows up to 16M BIOSes. */ - if (kvm_check_extension(s, KVM_CAP_SET_IDENTITY_MAP_ADDR)) { - /* Allows up to 16M BIOSes. */ - identity_base = 0xfeffc000; - - ret = kvm_vm_ioctl(s, KVM_SET_IDENTITY_MAP_ADDR, &identity_base); - if (ret < 0) { - return ret; - } + identity_base = 0xfeffc000; + ret = kvm_vm_ioctl(s, KVM_SET_IDENTITY_MAP_ADDR, &identity_base); + if (ret < 0) { + return ret; } /* Set TSS base one page after EPT identity map. */ @@ -2528,8 +2642,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s) if (enable_cpu_pm) { int disable_exits = kvm_check_extension(s, KVM_CAP_X86_DISABLE_EXITS); - int ret; - /* Work around for kernel header with a typo. TODO: fix header and drop. */ #if defined(KVM_X86_DISABLE_EXITS_HTL) && !defined(KVM_X86_DISABLE_EXITS_HLT) #define KVM_X86_DISABLE_EXITS_HLT KVM_X86_DISABLE_EXITS_HTL @@ -2571,6 +2683,40 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } } + if (s->notify_vmexit != NOTIFY_VMEXIT_OPTION_DISABLE && + kvm_check_extension(s, KVM_CAP_X86_NOTIFY_VMEXIT)) { + uint64_t notify_window_flags = + ((uint64_t)s->notify_window << 32) | + KVM_X86_NOTIFY_VMEXIT_ENABLED | + KVM_X86_NOTIFY_VMEXIT_USER; + ret = kvm_vm_enable_cap(s, KVM_CAP_X86_NOTIFY_VMEXIT, 0, + notify_window_flags); + if (ret < 0) { + error_report("kvm: Failed to enable notify vmexit cap: %s", + strerror(-ret)); + return ret; + } + } + if (kvm_vm_check_extension(s, KVM_CAP_X86_USER_SPACE_MSR)) { + bool r; + + ret = kvm_vm_enable_cap(s, KVM_CAP_X86_USER_SPACE_MSR, 0, + KVM_MSR_EXIT_REASON_FILTER); + if (ret) { + error_report("Could not enable user space MSRs: %s", + strerror(-ret)); + exit(1); + } + + r = kvm_filter_msr(s, MSR_CORE_THREAD_COUNT, + kvm_rdmsr_core_thread_count, NULL); + if (!r) { + error_report("Could not install MSR_CORE_THREAD_COUNT handler: %s", + strerror(-ret)); + exit(1); + } + } + return 0; } @@ -2674,40 +2820,11 @@ static int kvm_getput_regs(X86CPU *cpu, int set) return ret; } -static int kvm_put_fpu(X86CPU *cpu) -{ - CPUX86State *env = &cpu->env; - struct kvm_fpu fpu; - int i; - - memset(&fpu, 0, sizeof fpu); - fpu.fsw = env->fpus & ~(7 << 11); - fpu.fsw |= (env->fpstt & 7) << 11; - fpu.fcw = env->fpuc; - fpu.last_opcode = env->fpop; - fpu.last_ip = env->fpip; - fpu.last_dp = env->fpdp; - for (i = 0; i < 8; ++i) { - fpu.ftwx |= (!env->fptags[i]) << i; - } - memcpy(fpu.fpr, env->fpregs, sizeof env->fpregs); - for (i = 0; i < CPU_NB_REGS; i++) { - stq_p(&fpu.xmm[i][0], env->xmm_regs[i].ZMM_Q(0)); - stq_p(&fpu.xmm[i][8], env->xmm_regs[i].ZMM_Q(1)); - } - fpu.mxcsr = env->mxcsr; - - return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_FPU, &fpu); -} - static int kvm_put_xsave(X86CPU *cpu) { CPUX86State *env = &cpu->env; void *xsave = env->xsave_buf; - if (!has_xsave) { - return kvm_put_fpu(cpu); - } x86_cpu_xsave_all_areas(cpu, xsave, env->xsave_buf_len); return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_XSAVE, xsave); @@ -3405,7 +3522,7 @@ static int kvm_put_msrs(X86CPU *cpu, int level) if (kvm_enabled() && cpu->enable_pmu && (env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_ARCH_LBR)) { uint64_t depth; - int i, ret; + int ret; /* * Only migrate Arch LBR states when the host Arch LBR depth @@ -3438,8 +3555,6 @@ static int kvm_put_msrs(X86CPU *cpu, int level) } if (env->mcg_cap) { - int i; - kvm_msr_entry_add(cpu, MSR_MCG_STATUS, env->mcg_status); kvm_msr_entry_add(cpu, MSR_MCG_CTL, env->mcg_ctl); if (has_msr_mcg_ext_ctl) { @@ -3454,46 +3569,12 @@ static int kvm_put_msrs(X86CPU *cpu, int level) } -static int kvm_get_fpu(X86CPU *cpu) -{ - CPUX86State *env = &cpu->env; - struct kvm_fpu fpu; - int i, ret; - - ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_FPU, &fpu); - if (ret < 0) { - return ret; - } - - env->fpstt = (fpu.fsw >> 11) & 7; - env->fpus = fpu.fsw; - env->fpuc = fpu.fcw; - env->fpop = fpu.last_opcode; - env->fpip = fpu.last_ip; - env->fpdp = fpu.last_dp; - for (i = 0; i < 8; ++i) { - env->fptags[i] = !((fpu.ftwx >> i) & 1); - } - memcpy(env->fpregs, fpu.fpr, sizeof env->fpregs); - for (i = 0; i < CPU_NB_REGS; i++) { - env->xmm_regs[i].ZMM_Q(0) = ldq_p(&fpu.xmm[i][0]); - env->xmm_regs[i].ZMM_Q(1) = ldq_p(&fpu.xmm[i][8]); - } - env->mxcsr = fpu.mxcsr; - - return 0; -} - static int kvm_get_xsave(X86CPU *cpu) { CPUX86State *env = &cpu->env; void *xsave = env->xsave_buf; int type, ret; - if (!has_xsave) { - return kvm_get_fpu(cpu); - } - type = has_xsave2 ? KVM_GET_XSAVE2 : KVM_GET_XSAVE; ret = kvm_vcpu_ioctl(CPU(cpu), type, xsave); if (ret < 0) { @@ -3566,6 +3647,10 @@ static int kvm_get_sregs(X86CPU *cpu) env->cr[4] = sregs.cr4; env->efer = sregs.efer; + if (sev_es_enabled() && env->efer & MSR_EFER_LME && + env->cr[0] & CR0_PG_MASK) { + env->efer |= MSR_EFER_LMA; + } /* changes to apic base and cr8/tpr are read back via kvm_arch_post_run */ x86_update_hflags(env); @@ -3605,6 +3690,10 @@ static int kvm_get_sregs2(X86CPU *cpu) env->cr[4] = sregs.cr4; env->efer = sregs.efer; + if (sev_es_enabled() && env->efer & MSR_EFER_LME && + env->cr[0] & CR0_PG_MASK) { + env->efer |= MSR_EFER_LMA; + } env->pdptrs_valid = sregs.flags & KVM_SREGS2_FLAGS_PDPTRS_VALID; @@ -3836,7 +3925,6 @@ static int kvm_get_msrs(X86CPU *cpu) if (kvm_enabled() && cpu->enable_pmu && (env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_ARCH_LBR)) { uint64_t depth; - int i, ret; ret = kvm_get_one_msr(cpu, MSR_ARCH_LBR_DEPTH, &depth); if (ret == 1 && depth == ARCH_LBR_NR_ENTRIES) { @@ -4225,10 +4313,6 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level) CPUX86State *env = &cpu->env; struct kvm_vcpu_events events = {}; - if (!kvm_has_vcpu_events()) { - return 0; - } - events.flags = 0; if (has_exception_payload) { @@ -4282,6 +4366,11 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level) } } + if (has_triple_fault_event) { + events.flags |= KVM_VCPUEVENT_VALID_TRIPLE_FAULT; + events.triple_fault.pending = env->triple_fault_pending; + } + return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_VCPU_EVENTS, &events); } @@ -4291,10 +4380,6 @@ static int kvm_get_vcpu_events(X86CPU *cpu) struct kvm_vcpu_events events; int ret; - if (!kvm_has_vcpu_events()) { - return 0; - } - memset(&events, 0, sizeof(events)); ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_VCPU_EVENTS, &events); if (ret < 0) { @@ -4351,52 +4436,21 @@ static int kvm_get_vcpu_events(X86CPU *cpu) } } + if (events.flags & KVM_VCPUEVENT_VALID_TRIPLE_FAULT) { + env->triple_fault_pending = events.triple_fault.pending; + } + env->sipi_vector = events.sipi_vector; return 0; } -static int kvm_guest_debug_workarounds(X86CPU *cpu) -{ - CPUState *cs = CPU(cpu); - CPUX86State *env = &cpu->env; - int ret = 0; - unsigned long reinject_trap = 0; - - if (!kvm_has_vcpu_events()) { - if (env->exception_nr == EXCP01_DB) { - reinject_trap = KVM_GUESTDBG_INJECT_DB; - } else if (env->exception_injected == EXCP03_INT3) { - reinject_trap = KVM_GUESTDBG_INJECT_BP; - } - kvm_reset_exception(env); - } - - /* - * Kernels before KVM_CAP_X86_ROBUST_SINGLESTEP overwrote flags.TF - * injected via SET_GUEST_DEBUG while updating GP regs. Work around this - * by updating the debug state once again if single-stepping is on. - * Another reason to call kvm_update_guest_debug here is a pending debug - * trap raise by the guest. On kernels without SET_VCPU_EVENTS we have to - * reinject them via SET_GUEST_DEBUG. - */ - if (reinject_trap || - (!kvm_has_robust_singlestep() && cs->singlestep_enabled)) { - ret = kvm_update_guest_debug(cs, reinject_trap); - } - return ret; -} - static int kvm_put_debugregs(X86CPU *cpu) { CPUX86State *env = &cpu->env; struct kvm_debugregs dbgregs; int i; - if (!kvm_has_debugregs()) { - return 0; - } - memset(&dbgregs, 0, sizeof(dbgregs)); for (i = 0; i < 4; i++) { dbgregs.db[i] = env->dr[i]; @@ -4414,10 +4468,6 @@ static int kvm_get_debugregs(X86CPU *cpu) struct kvm_debugregs dbgregs; int i, ret; - if (!kvm_has_debugregs()) { - return 0; - } - ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_DEBUGREGS, &dbgregs); if (ret < 0) { return ret; @@ -4512,6 +4562,18 @@ int kvm_arch_put_registers(CPUState *cpu, int level) assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); + /* + * Put MSR_IA32_FEATURE_CONTROL first, this ensures the VM gets out of VMX + * root operation upon vCPU reset. kvm_put_msr_feature_control() should also + * precede kvm_put_nested_state() when 'real' nested state is set. + */ + if (level >= KVM_PUT_RESET_STATE) { + ret = kvm_put_msr_feature_control(x86_cpu); + if (ret < 0) { + return ret; + } + } + /* must be before kvm_put_nested_state so that EFER.SVME is set */ ret = has_sregs2 ? kvm_put_sregs2(x86_cpu) : kvm_put_sregs(x86_cpu); if (ret < 0) { @@ -4523,11 +4585,6 @@ int kvm_arch_put_registers(CPUState *cpu, int level) if (ret < 0) { return ret; } - - ret = kvm_put_msr_feature_control(x86_cpu); - if (ret < 0) { - return ret; - } } if (level == KVM_PUT_FULL_STATE) { @@ -4539,6 +4596,15 @@ int kvm_arch_put_registers(CPUState *cpu, int level) kvm_arch_set_tsc_khz(cpu); } +#ifdef CONFIG_XEN_EMU + if (xen_mode == XEN_EMULATE && level == KVM_PUT_FULL_STATE) { + ret = kvm_put_xen_state(cpu); + if (ret < 0) { + return ret; + } + } +#endif + ret = kvm_getput_regs(x86_cpu, 1); if (ret < 0) { return ret; @@ -4551,11 +4617,6 @@ int kvm_arch_put_registers(CPUState *cpu, int level) if (ret < 0) { return ret; } - /* must be before kvm_put_msrs */ - ret = kvm_inject_mce_oldstyle(x86_cpu); - if (ret < 0) { - return ret; - } ret = kvm_put_msrs(x86_cpu, level); if (ret < 0) { return ret; @@ -4579,11 +4640,6 @@ int kvm_arch_put_registers(CPUState *cpu, int level) if (ret < 0) { return ret; } - /* must be last */ - ret = kvm_guest_debug_workarounds(x86_cpu); - if (ret < 0) { - return ret; - } return 0; } @@ -4638,6 +4694,14 @@ int kvm_arch_get_registers(CPUState *cs) if (ret < 0) { goto out; } +#ifdef CONFIG_XEN_EMU + if (xen_mode == XEN_EMULATE) { + ret = kvm_get_xen_state(cs); + if (ret < 0) { + goto out; + } + } +#endif ret = 0; out: cpu_sync_bndcs_hflags(&cpu->env); @@ -4653,9 +4717,9 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) /* Inject NMI */ if (cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { if (cpu->interrupt_request & CPU_INTERRUPT_NMI) { - qemu_mutex_lock_iothread(); + bql_lock(); cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; - qemu_mutex_unlock_iothread(); + bql_unlock(); DPRINTF("injected NMI\n"); ret = kvm_vcpu_ioctl(cpu, KVM_NMI); if (ret < 0) { @@ -4664,9 +4728,9 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) } } if (cpu->interrupt_request & CPU_INTERRUPT_SMI) { - qemu_mutex_lock_iothread(); + bql_lock(); cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; - qemu_mutex_unlock_iothread(); + bql_unlock(); DPRINTF("injected SMI\n"); ret = kvm_vcpu_ioctl(cpu, KVM_SMI); if (ret < 0) { @@ -4677,7 +4741,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) } if (!kvm_pic_in_kernel()) { - qemu_mutex_lock_iothread(); + bql_lock(); } /* Force the VCPU out of its inner loop to process any INIT requests @@ -4730,7 +4794,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) DPRINTF("setting tpr\n"); run->cr8 = cpu_get_apic_tpr(x86_cpu->apic_state); - qemu_mutex_unlock_iothread(); + bql_unlock(); } } @@ -4762,15 +4826,28 @@ MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run) kvm_rate_limit_on_bus_lock(); } +#ifdef CONFIG_XEN_EMU + /* + * If the callback is asserted as a GSI (or PCI INTx) then check if + * vcpu_info->evtchn_upcall_pending has been cleared, and deassert + * the callback IRQ if so. Ideally we could hook into the PIC/IOAPIC + * EOI and only resample then, exactly how the VFIO eventfd pairs + * are designed to work for level triggered interrupts. + */ + if (x86_cpu->env.xen_callback_asserted) { + kvm_xen_maybe_deassert_callback(cpu); + } +#endif + /* We need to protect the apic state against concurrent accesses from * different threads in case the userspace irqchip is used. */ if (!kvm_irqchip_in_kernel()) { - qemu_mutex_lock_iothread(); + bql_lock(); } cpu_set_apic_tpr(x86_cpu->apic_state, run->cr8); cpu_set_apic_base(x86_cpu->apic_state, run->apic_base); if (!kvm_irqchip_in_kernel()) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } return cpu_get_mem_attrs(env); } @@ -4910,8 +4987,7 @@ static int find_hw_breakpoint(target_ulong addr, int len, int type) return -1; } -int kvm_arch_insert_hw_breakpoint(target_ulong addr, - target_ulong len, int type) +int kvm_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type) { switch (type) { case GDB_BREAKPOINT_HW: @@ -4951,8 +5027,7 @@ int kvm_arch_insert_hw_breakpoint(target_ulong addr, return 0; } -int kvm_arch_remove_hw_breakpoint(target_ulong addr, - target_ulong len, int type) +int kvm_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type) { int n; @@ -5053,6 +5128,108 @@ void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) } } +static bool kvm_install_msr_filters(KVMState *s) +{ + uint64_t zero = 0; + struct kvm_msr_filter filter = { + .flags = KVM_MSR_FILTER_DEFAULT_ALLOW, + }; + int r, i, j = 0; + + for (i = 0; i < KVM_MSR_FILTER_MAX_RANGES; i++) { + KVMMSRHandlers *handler = &msr_handlers[i]; + if (handler->msr) { + struct kvm_msr_filter_range *range = &filter.ranges[j++]; + + *range = (struct kvm_msr_filter_range) { + .flags = 0, + .nmsrs = 1, + .base = handler->msr, + .bitmap = (__u8 *)&zero, + }; + + if (handler->rdmsr) { + range->flags |= KVM_MSR_FILTER_READ; + } + + if (handler->wrmsr) { + range->flags |= KVM_MSR_FILTER_WRITE; + } + } + } + + r = kvm_vm_ioctl(s, KVM_X86_SET_MSR_FILTER, &filter); + if (r) { + return false; + } + + return true; +} + +bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, + QEMUWRMSRHandler *wrmsr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) { + if (!msr_handlers[i].msr) { + msr_handlers[i] = (KVMMSRHandlers) { + .msr = msr, + .rdmsr = rdmsr, + .wrmsr = wrmsr, + }; + + if (!kvm_install_msr_filters(s)) { + msr_handlers[i] = (KVMMSRHandlers) { }; + return false; + } + + return true; + } + } + + return false; +} + +static int kvm_handle_rdmsr(X86CPU *cpu, struct kvm_run *run) +{ + int i; + bool r; + + for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) { + KVMMSRHandlers *handler = &msr_handlers[i]; + if (run->msr.index == handler->msr) { + if (handler->rdmsr) { + r = handler->rdmsr(cpu, handler->msr, + (uint64_t *)&run->msr.data); + run->msr.error = r ? 0 : 1; + return 0; + } + } + } + + assert(false); +} + +static int kvm_handle_wrmsr(X86CPU *cpu, struct kvm_run *run) +{ + int i; + bool r; + + for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) { + KVMMSRHandlers *handler = &msr_handlers[i]; + if (run->msr.index == handler->msr) { + if (handler->wrmsr) { + r = handler->wrmsr(cpu, handler->msr, run->msr.data); + run->msr.error = r ? 0 : 1; + return 0; + } + } + } + + assert(false); +} + static bool has_sgx_provisioning; static bool __kvm_enable_sgx_provisioning(KVMState *s) @@ -5097,21 +5274,24 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) X86CPU *cpu = X86_CPU(cs); uint64_t code; int ret; + bool ctx_invalid; + char str[256]; + KVMState *state; switch (run->exit_reason) { case KVM_EXIT_HLT: DPRINTF("handle_hlt\n"); - qemu_mutex_lock_iothread(); + bql_lock(); ret = kvm_handle_halt(cpu); - qemu_mutex_unlock_iothread(); + bql_unlock(); break; case KVM_EXIT_SET_TPR: ret = 0; break; case KVM_EXIT_TPR_ACCESS: - qemu_mutex_lock_iothread(); + bql_lock(); ret = kvm_handle_tpr_access(cpu); - qemu_mutex_unlock_iothread(); + bql_unlock(); break; case KVM_EXIT_FAIL_ENTRY: code = run->fail_entry.hardware_entry_failure_reason; @@ -5137,9 +5317,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) break; case KVM_EXIT_DEBUG: DPRINTF("kvm_exit_debug\n"); - qemu_mutex_lock_iothread(); + bql_lock(); ret = kvm_handle_debug(cpu, &run->debug.arch); - qemu_mutex_unlock_iothread(); + bql_unlock(); break; case KVM_EXIT_HYPERV: ret = kvm_hv_handle_exit(cpu, &run->hyperv); @@ -5152,6 +5332,36 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) /* already handled in kvm_arch_post_run */ ret = 0; break; + case KVM_EXIT_NOTIFY: + ctx_invalid = !!(run->notify.flags & KVM_NOTIFY_CONTEXT_INVALID); + state = KVM_STATE(current_accel()); + sprintf(str, "Encounter a notify exit with %svalid context in" + " guest. There can be possible misbehaves in guest." + " Please have a look.", ctx_invalid ? "in" : ""); + if (ctx_invalid || + state->notify_vmexit == NOTIFY_VMEXIT_OPTION_INTERNAL_ERROR) { + warn_report("KVM internal error: %s", str); + ret = -1; + } else { + warn_report_once("KVM: %s", str); + ret = 0; + } + break; + case KVM_EXIT_X86_RDMSR: + /* We only enable MSR filtering, any other exit is bogus */ + assert(run->msr.reason == KVM_MSR_EXIT_REASON_FILTER); + ret = kvm_handle_rdmsr(cpu, run); + break; + case KVM_EXIT_X86_WRMSR: + /* We only enable MSR filtering, any other exit is bogus */ + assert(run->msr.reason == KVM_MSR_EXIT_REASON_FILTER); + ret = kvm_handle_wrmsr(cpu, run); + break; +#ifdef CONFIG_XEN_EMU + case KVM_EXIT_XEN: + ret = kvm_xen_handle_exit(cpu, &run->xen); + break; +#endif default: fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); ret = -1; @@ -5269,7 +5479,7 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, } /* - * Handled untranslated compatibilty format interrupt with + * Handled untranslated compatibility format interrupt with * extended destination ID in the low bits 11-5. */ dst.address = kvm_swizzle_msi_ext_dest_id(dst.address); @@ -5280,6 +5490,20 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, } } +#ifdef CONFIG_XEN_EMU + if (xen_mode == XEN_EMULATE) { + int handled = xen_evtchn_translate_pirq_msi(route, address, data); + + /* + * If it was a PIRQ and successfully routed (handled == 0) or it was + * an error (handled < 0), return. If it wasn't a PIRQ, keep going. + */ + if (handled <= 0) { + return handled; + } + } +#endif + address = kvm_swizzle_msi_ext_dest_id(address); route->u.msi.address_hi = address >> VTD_MSI_ADDR_HI_SHIFT; route->u.msi.address_lo = address & VTD_MSI_ADDR_LO_MASK; @@ -5299,8 +5523,8 @@ struct MSIRouteEntry { static QLIST_HEAD(, MSIRouteEntry) msi_route_list = \ QLIST_HEAD_INITIALIZER(msi_route_list); -static void kvm_update_msi_routes_all(void *private, bool global, - uint32_t index, uint32_t mask) +void kvm_update_msi_routes_all(void *private, bool global, + uint32_t index, uint32_t mask) { int cnt = 0, vector; MSIRouteEntry *entry; @@ -5428,3 +5652,180 @@ void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask) mask &= ~BIT_ULL(bit); } } + +static int kvm_arch_get_notify_vmexit(Object *obj, Error **errp) +{ + KVMState *s = KVM_STATE(obj); + return s->notify_vmexit; +} + +static void kvm_arch_set_notify_vmexit(Object *obj, int value, Error **errp) +{ + KVMState *s = KVM_STATE(obj); + + if (s->fd != -1) { + error_setg(errp, "Cannot set properties after the accelerator has been initialized"); + return; + } + + s->notify_vmexit = value; +} + +static void kvm_arch_get_notify_window(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + uint32_t value = s->notify_window; + + visit_type_uint32(v, name, &value, errp); +} + +static void kvm_arch_set_notify_window(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + uint32_t value; + + if (s->fd != -1) { + error_setg(errp, "Cannot set properties after the accelerator has been initialized"); + return; + } + + if (!visit_type_uint32(v, name, &value, errp)) { + return; + } + + s->notify_window = value; +} + +static void kvm_arch_get_xen_version(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + uint32_t value = s->xen_version; + + visit_type_uint32(v, name, &value, errp); +} + +static void kvm_arch_set_xen_version(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + Error *error = NULL; + uint32_t value; + + visit_type_uint32(v, name, &value, &error); + if (error) { + error_propagate(errp, error); + return; + } + + s->xen_version = value; + if (value && xen_mode == XEN_DISABLED) { + xen_mode = XEN_EMULATE; + } +} + +static void kvm_arch_get_xen_gnttab_max_frames(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + uint16_t value = s->xen_gnttab_max_frames; + + visit_type_uint16(v, name, &value, errp); +} + +static void kvm_arch_set_xen_gnttab_max_frames(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + Error *error = NULL; + uint16_t value; + + visit_type_uint16(v, name, &value, &error); + if (error) { + error_propagate(errp, error); + return; + } + + s->xen_gnttab_max_frames = value; +} + +static void kvm_arch_get_xen_evtchn_max_pirq(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + uint16_t value = s->xen_evtchn_max_pirq; + + visit_type_uint16(v, name, &value, errp); +} + +static void kvm_arch_set_xen_evtchn_max_pirq(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + Error *error = NULL; + uint16_t value; + + visit_type_uint16(v, name, &value, &error); + if (error) { + error_propagate(errp, error); + return; + } + + s->xen_evtchn_max_pirq = value; +} + +void kvm_arch_accel_class_init(ObjectClass *oc) +{ + object_class_property_add_enum(oc, "notify-vmexit", "NotifyVMexitOption", + &NotifyVmexitOption_lookup, + kvm_arch_get_notify_vmexit, + kvm_arch_set_notify_vmexit); + object_class_property_set_description(oc, "notify-vmexit", + "Enable notify VM exit"); + + object_class_property_add(oc, "notify-window", "uint32", + kvm_arch_get_notify_window, + kvm_arch_set_notify_window, + NULL, NULL); + object_class_property_set_description(oc, "notify-window", + "Clock cycles without an event window " + "after which a notification VM exit occurs"); + + object_class_property_add(oc, "xen-version", "uint32", + kvm_arch_get_xen_version, + kvm_arch_set_xen_version, + NULL, NULL); + object_class_property_set_description(oc, "xen-version", + "Xen version to be emulated " + "(in XENVER_version form " + "e.g. 0x4000a for 4.10)"); + + object_class_property_add(oc, "xen-gnttab-max-frames", "uint16", + kvm_arch_get_xen_gnttab_max_frames, + kvm_arch_set_xen_gnttab_max_frames, + NULL, NULL); + object_class_property_set_description(oc, "xen-gnttab-max-frames", + "Maximum number of grant table frames"); + + object_class_property_add(oc, "xen-evtchn-max-pirq", "uint16", + kvm_arch_get_xen_evtchn_max_pirq, + kvm_arch_set_xen_evtchn_max_pirq, + NULL, NULL); + object_class_property_set_description(oc, "xen-evtchn-max-pirq", + "Maximum number of Xen PIRQs"); +} + +void kvm_set_max_apic_id(uint32_t max_apic_id) +{ + kvm_vm_enable_cap(kvm_state, KVM_CAP_MAX_VCPU_ID, 0, max_apic_id); +} diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 4124912c20..30fedcffea 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -13,8 +13,6 @@ #include "sysemu/kvm.h" -#define kvm_apic_in_kernel() (kvm_irqchip_in_kernel()) - #ifdef CONFIG_KVM #define kvm_pit_in_kernel() \ @@ -33,25 +31,51 @@ #endif /* CONFIG_KVM */ bool kvm_has_smm(void); -bool kvm_has_adjust_clock(void); +bool kvm_enable_x2apic(void); +bool kvm_hv_vpindex_settable(void); + +bool kvm_enable_sgx_provisioning(KVMState *s); +bool kvm_hyperv_expand_features(X86CPU *cpu, Error **errp); + +void kvm_arch_reset_vcpu(X86CPU *cs); +void kvm_arch_after_reset_vcpu(X86CPU *cpu); +void kvm_arch_do_init_vcpu(X86CPU *cs); +uint32_t kvm_arch_get_supported_cpuid(KVMState *env, uint32_t function, + uint32_t index, int reg); +uint64_t kvm_arch_get_supported_msr_feature(KVMState *s, uint32_t index); + +void kvm_set_max_apic_id(uint32_t max_apic_id); +void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask); + +#ifdef CONFIG_KVM + bool kvm_has_adjust_clock_stable(void); bool kvm_has_exception_payload(void); void kvm_synchronize_all_tsc(void); -void kvm_arch_reset_vcpu(X86CPU *cs); -void kvm_arch_do_init_vcpu(X86CPU *cs); +void kvm_get_apic_state(DeviceState *d, struct kvm_lapic_state *kapic); void kvm_put_apicbase(X86CPU *cpu, uint64_t value); -bool kvm_enable_x2apic(void); bool kvm_has_x2apic_api(void); bool kvm_has_waitpkg(void); -bool kvm_hv_vpindex_settable(void); -bool kvm_hyperv_expand_features(X86CPU *cpu, Error **errp); - uint64_t kvm_swizzle_msi_ext_dest_id(uint64_t address); +void kvm_update_msi_routes_all(void *private, bool global, + uint32_t index, uint32_t mask); -bool kvm_enable_sgx_provisioning(KVMState *s); -void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask); +typedef bool QEMURDMSRHandler(X86CPU *cpu, uint32_t msr, uint64_t *val); +typedef bool QEMUWRMSRHandler(X86CPU *cpu, uint32_t msr, uint64_t val); +typedef struct kvm_msr_handlers { + uint32_t msr; + QEMURDMSRHandler *rdmsr; + QEMUWRMSRHandler *wrmsr; +} KVMMSRHandlers; + +bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, + QEMUWRMSRHandler *wrmsr); + +#endif /* CONFIG_KVM */ + +void kvm_pc_setup_irq_routing(bool pci_enabled); #endif diff --git a/target/i386/kvm/meson.build b/target/i386/kvm/meson.build index 736df8b72e..84d9143e60 100644 --- a/target/i386/kvm/meson.build +++ b/target/i386/kvm/meson.build @@ -1,14 +1,14 @@ -i386_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) +i386_kvm_ss = ss.source_set() -i386_softmmu_kvm_ss = ss.source_set() - -i386_softmmu_kvm_ss.add(files( +i386_kvm_ss.add(files( 'kvm.c', 'kvm-cpu.c', )) -i386_softmmu_kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c')) +i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen-emu.c')) -i386_softmmu_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c')) +i386_kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c')) -i386_softmmu_ss.add_all(when: 'CONFIG_KVM', if_true: i386_softmmu_kvm_ss) +i386_system_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c')) + +i386_system_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss) diff --git a/target/i386/kvm/trace-events b/target/i386/kvm/trace-events index 7c369db1e1..b365a8e8e2 100644 --- a/target/i386/kvm/trace-events +++ b/target/i386/kvm/trace-events @@ -5,3 +5,10 @@ kvm_x86_fixup_msi_error(uint32_t gsi) "VT-d failed to remap interrupt for GSI %" kvm_x86_add_msi_route(int virq) "Adding route entry for virq %d" kvm_x86_remove_msi_route(int virq) "Removing route entry for virq %d" kvm_x86_update_msi_routes(int num) "Updated %d MSI routes" + +# xen-emu.c +kvm_xen_hypercall(int cpu, uint8_t cpl, uint64_t input, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t ret) "xen_hypercall: cpu %d cpl %d input %" PRIu64 " a0 0x%" PRIx64 " a1 0x%" PRIx64 " a2 0x%" PRIx64" ret 0x%" PRIx64 +kvm_xen_soft_reset(void) "" +kvm_xen_set_shared_info(uint64_t gfn) "shared info at gfn 0x%" PRIx64 +kvm_xen_set_vcpu_attr(int cpu, int type, uint64_t gpa) "vcpu attr cpu %d type %d gpa 0x%" PRIx64 +kvm_xen_set_vcpu_callback(int cpu, int vector) "callback vcpu %d vector %d" diff --git a/target/i386/kvm/xen-compat.h b/target/i386/kvm/xen-compat.h new file mode 100644 index 0000000000..7f30180cc2 --- /dev/null +++ b/target/i386/kvm/xen-compat.h @@ -0,0 +1,70 @@ +/* + * Xen HVM emulation support in KVM + * + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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_I386_KVM_XEN_COMPAT_H +#define QEMU_I386_KVM_XEN_COMPAT_H + +#include "hw/xen/interface/memory.h" + +typedef uint32_t compat_pfn_t; +typedef uint32_t compat_ulong_t; +typedef uint32_t compat_ptr_t; + +#define __DEFINE_COMPAT_HANDLE(name, type) \ + typedef struct { \ + compat_ptr_t c; \ + type *_[0] __attribute__((packed)); \ + } __compat_handle_ ## name; \ + +#define DEFINE_COMPAT_HANDLE(name) __DEFINE_COMPAT_HANDLE(name, name) +#define COMPAT_HANDLE(name) __compat_handle_ ## name + +DEFINE_COMPAT_HANDLE(compat_pfn_t); +DEFINE_COMPAT_HANDLE(compat_ulong_t); +DEFINE_COMPAT_HANDLE(int); + +struct compat_xen_add_to_physmap { + domid_t domid; + uint16_t size; + unsigned int space; + compat_ulong_t idx; + compat_pfn_t gpfn; +}; + +struct compat_xen_add_to_physmap_batch { + domid_t domid; + uint16_t space; + uint16_t size; + uint16_t extra; + COMPAT_HANDLE(compat_ulong_t) idxs; + COMPAT_HANDLE(compat_pfn_t) gpfns; + COMPAT_HANDLE(int) errs; +}; + +struct compat_physdev_map_pirq { + domid_t domid; + uint16_t pad; + /* IN */ + int type; + /* IN (ignored for ..._MULTI_MSI) */ + int index; + /* IN or OUT */ + int pirq; + /* IN - high 16 bits hold segment for ..._MSI_SEG and ..._MULTI_MSI */ + int bus; + /* IN */ + int devfn; + /* IN (also OUT for ..._MULTI_MSI) */ + int entry_nr; + /* IN */ + uint64_t table_base; +} __attribute__((packed)); + +#endif /* QEMU_I386_XEN_COMPAT_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c new file mode 100644 index 0000000000..fc2c2321ac --- /dev/null +++ b/target/i386/kvm/xen-emu.c @@ -0,0 +1,1945 @@ +/* + * Xen HVM emulation support in KVM + * + * Copyright © 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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/log.h" +#include "qemu/main-loop.h" +#include "qemu/error-report.h" +#include "hw/xen/xen.h" +#include "sysemu/kvm_int.h" +#include "sysemu/kvm_xen.h" +#include "kvm/kvm_i386.h" +#include "exec/address-spaces.h" +#include "xen-emu.h" +#include "trace.h" +#include "sysemu/runstate.h" + +#include "hw/pci/msi.h" +#include "hw/i386/apic-msidef.h" +#include "hw/i386/e820_memory_layout.h" +#include "hw/i386/kvm/xen_overlay.h" +#include "hw/i386/kvm/xen_evtchn.h" +#include "hw/i386/kvm/xen_gnttab.h" +#include "hw/i386/kvm/xen_primary_console.h" +#include "hw/i386/kvm/xen_xenstore.h" + +#include "hw/xen/interface/version.h" +#include "hw/xen/interface/sched.h" +#include "hw/xen/interface/memory.h" +#include "hw/xen/interface/hvm/hvm_op.h" +#include "hw/xen/interface/hvm/params.h" +#include "hw/xen/interface/vcpu.h" +#include "hw/xen/interface/event_channel.h" +#include "hw/xen/interface/grant_table.h" + +#include "xen-compat.h" + +static void xen_vcpu_singleshot_timer_event(void *opaque); +static void xen_vcpu_periodic_timer_event(void *opaque); +static int vcpuop_stop_singleshot_timer(CPUState *cs); + +#ifdef TARGET_X86_64 +#define hypercall_compat32(longmode) (!(longmode)) +#else +#define hypercall_compat32(longmode) (false) +#endif + +static bool kvm_gva_to_gpa(CPUState *cs, uint64_t gva, uint64_t *gpa, + size_t *len, bool is_write) +{ + struct kvm_translation tr = { + .linear_address = gva, + }; + + if (len) { + *len = TARGET_PAGE_SIZE - (gva & ~TARGET_PAGE_MASK); + } + + if (kvm_vcpu_ioctl(cs, KVM_TRANSLATE, &tr) || !tr.valid || + (is_write && !tr.writeable)) { + return false; + } + *gpa = tr.physical_address; + return true; +} + +static int kvm_gva_rw(CPUState *cs, uint64_t gva, void *_buf, size_t sz, + bool is_write) +{ + uint8_t *buf = (uint8_t *)_buf; + uint64_t gpa; + size_t len; + + while (sz) { + if (!kvm_gva_to_gpa(cs, gva, &gpa, &len, is_write)) { + return -EFAULT; + } + if (len > sz) { + len = sz; + } + + cpu_physical_memory_rw(gpa, buf, len, is_write); + + buf += len; + sz -= len; + gva += len; + } + + return 0; +} + +static inline int kvm_copy_from_gva(CPUState *cs, uint64_t gva, void *buf, + size_t sz) +{ + return kvm_gva_rw(cs, gva, buf, sz, false); +} + +static inline int kvm_copy_to_gva(CPUState *cs, uint64_t gva, void *buf, + size_t sz) +{ + return kvm_gva_rw(cs, gva, buf, sz, true); +} + +int kvm_xen_init(KVMState *s, uint32_t hypercall_msr) +{ + const int required_caps = KVM_XEN_HVM_CONFIG_HYPERCALL_MSR | + KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL | KVM_XEN_HVM_CONFIG_SHARED_INFO; + struct kvm_xen_hvm_config cfg = { + .msr = hypercall_msr, + .flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL, + }; + int xen_caps, ret; + + xen_caps = kvm_check_extension(s, KVM_CAP_XEN_HVM); + if (required_caps & ~xen_caps) { + error_report("kvm: Xen HVM guest support not present or insufficient"); + return -ENOSYS; + } + + if (xen_caps & KVM_XEN_HVM_CONFIG_EVTCHN_SEND) { + struct kvm_xen_hvm_attr ha = { + .type = KVM_XEN_ATTR_TYPE_XEN_VERSION, + .u.xen_version = s->xen_version, + }; + (void)kvm_vm_ioctl(s, KVM_XEN_HVM_SET_ATTR, &ha); + + cfg.flags |= KVM_XEN_HVM_CONFIG_EVTCHN_SEND; + } + + ret = kvm_vm_ioctl(s, KVM_XEN_HVM_CONFIG, &cfg); + if (ret < 0) { + error_report("kvm: Failed to enable Xen HVM support: %s", + strerror(-ret)); + return ret; + } + + /* If called a second time, don't repeat the rest of the setup. */ + if (s->xen_caps) { + return 0; + } + + /* + * Event channel delivery via GSI/PCI_INTX needs to poll the vcpu_info + * of vCPU0 to deassert the IRQ when ->evtchn_upcall_pending is cleared. + * + * In the kernel, there's a notifier hook on the PIC/IOAPIC which allows + * such things to be polled at precisely the right time. We *could* do + * it nicely in the kernel: check vcpu_info[0]->evtchn_upcall_pending at + * the moment the IRQ is acked, and see if it should be reasserted. + * + * But the in-kernel irqchip is deprecated, so we're unlikely to add + * that support in the kernel. Insist on using the split irqchip mode + * instead. + * + * This leaves us polling for the level going low in QEMU, which lacks + * the appropriate hooks in its PIC/IOAPIC code. Even VFIO is sending a + * spurious 'ack' to an INTX IRQ every time there's any MMIO access to + * the device (for which it has to unmap the device and trap access, for + * some period after an IRQ!!). In the Xen case, we do it on exit from + * KVM_RUN, if the flag is set to say that the GSI is currently asserted. + * Which is kind of icky, but less so than the VFIO one. I may fix them + * both later... + */ + if (!kvm_kernel_irqchip_split()) { + error_report("kvm: Xen support requires kernel-irqchip=split"); + return -EINVAL; + } + + s->xen_caps = xen_caps; + + /* Tell fw_cfg to notify the BIOS to reserve the range. */ + ret = e820_add_entry(XEN_SPECIAL_AREA_ADDR, XEN_SPECIAL_AREA_SIZE, + E820_RESERVED); + if (ret < 0) { + fprintf(stderr, "e820_add_entry() table is full\n"); + return ret; + } + + /* The pages couldn't be overlaid until KVM was initialized */ + xen_primary_console_reset(); + xen_xenstore_reset(); + + return 0; +} + +int kvm_xen_init_vcpu(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + int err; + + /* + * The kernel needs to know the Xen/ACPI vCPU ID because that's + * what the guest uses in hypercalls such as timers. It doesn't + * match the APIC ID which is generally used for talking to the + * kernel about vCPUs. And if vCPU threads race with creating + * their KVM vCPUs out of order, it doesn't necessarily match + * with the kernel's internal vCPU indices either. + */ + if (kvm_xen_has_cap(EVTCHN_SEND)) { + struct kvm_xen_vcpu_attr va = { + .type = KVM_XEN_VCPU_ATTR_TYPE_VCPU_ID, + .u.vcpu_id = cs->cpu_index, + }; + err = kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_SET_ATTR, &va); + if (err) { + error_report("kvm: Failed to set Xen vCPU ID attribute: %s", + strerror(-err)); + return err; + } + } + + env->xen_vcpu_info_gpa = INVALID_GPA; + env->xen_vcpu_info_default_gpa = INVALID_GPA; + env->xen_vcpu_time_info_gpa = INVALID_GPA; + env->xen_vcpu_runstate_gpa = INVALID_GPA; + + qemu_mutex_init(&env->xen_timers_lock); + env->xen_singleshot_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + xen_vcpu_singleshot_timer_event, + cpu); + if (!env->xen_singleshot_timer) { + return -ENOMEM; + } + env->xen_singleshot_timer->opaque = cs; + + env->xen_periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + xen_vcpu_periodic_timer_event, + cpu); + if (!env->xen_periodic_timer) { + return -ENOMEM; + } + env->xen_periodic_timer->opaque = cs; + + return 0; +} + +uint32_t kvm_xen_get_caps(void) +{ + return kvm_state->xen_caps; +} + +static bool kvm_xen_hcall_xen_version(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, uint64_t arg) +{ + int err = 0; + + switch (cmd) { + case XENVER_get_features: { + struct xen_feature_info fi; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(fi) == 8); + + err = kvm_copy_from_gva(CPU(cpu), arg, &fi, sizeof(fi)); + if (err) { + break; + } + + fi.submap = 0; + if (fi.submap_idx == 0) { + fi.submap |= 1 << XENFEAT_writable_page_tables | + 1 << XENFEAT_writable_descriptor_tables | + 1 << XENFEAT_auto_translated_physmap | + 1 << XENFEAT_hvm_callback_vector | + 1 << XENFEAT_hvm_safe_pvclock | + 1 << XENFEAT_hvm_pirqs; + } + + err = kvm_copy_to_gva(CPU(cpu), arg, &fi, sizeof(fi)); + break; + } + + default: + return false; + } + + exit->u.hcall.result = err; + return true; +} + +static int kvm_xen_set_vcpu_attr(CPUState *cs, uint16_t type, uint64_t gpa) +{ + struct kvm_xen_vcpu_attr xhsi; + + xhsi.type = type; + xhsi.u.gpa = gpa; + + trace_kvm_xen_set_vcpu_attr(cs->cpu_index, type, gpa); + + return kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_SET_ATTR, &xhsi); +} + +static int kvm_xen_set_vcpu_callback_vector(CPUState *cs) +{ + uint8_t vector = X86_CPU(cs)->env.xen_vcpu_callback_vector; + struct kvm_xen_vcpu_attr xva; + + xva.type = KVM_XEN_VCPU_ATTR_TYPE_UPCALL_VECTOR; + xva.u.vector = vector; + + trace_kvm_xen_set_vcpu_callback(cs->cpu_index, vector); + + return kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_SET_ATTR, &xva); +} + +static void do_set_vcpu_callback_vector(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + env->xen_vcpu_callback_vector = data.host_int; + + if (kvm_xen_has_cap(EVTCHN_SEND)) { + kvm_xen_set_vcpu_callback_vector(cs); + } +} + +static int set_vcpu_info(CPUState *cs, uint64_t gpa) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + MemoryRegionSection mrs = { .mr = NULL }; + void *vcpu_info_hva = NULL; + int ret; + + ret = kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, gpa); + if (ret || gpa == INVALID_GPA) { + goto out; + } + + mrs = memory_region_find(get_system_memory(), gpa, + sizeof(struct vcpu_info)); + if (mrs.mr && mrs.mr->ram_block && + !int128_lt(mrs.size, int128_make64(sizeof(struct vcpu_info)))) { + vcpu_info_hva = qemu_map_ram_ptr(mrs.mr->ram_block, + mrs.offset_within_region); + } + if (!vcpu_info_hva) { + if (mrs.mr) { + memory_region_unref(mrs.mr); + mrs.mr = NULL; + } + ret = -EINVAL; + } + + out: + if (env->xen_vcpu_info_mr) { + memory_region_unref(env->xen_vcpu_info_mr); + } + env->xen_vcpu_info_hva = vcpu_info_hva; + env->xen_vcpu_info_mr = mrs.mr; + return ret; +} + +static void do_set_vcpu_info_default_gpa(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + env->xen_vcpu_info_default_gpa = data.host_ulong; + + /* Changing the default does nothing if a vcpu_info was explicitly set. */ + if (env->xen_vcpu_info_gpa == INVALID_GPA) { + set_vcpu_info(cs, env->xen_vcpu_info_default_gpa); + } +} + +static void do_set_vcpu_info_gpa(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + env->xen_vcpu_info_gpa = data.host_ulong; + + set_vcpu_info(cs, env->xen_vcpu_info_gpa); +} + +void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id) +{ + CPUState *cs = qemu_get_cpu(vcpu_id); + if (!cs) { + return NULL; + } + + return X86_CPU(cs)->env.xen_vcpu_info_hva; +} + +void kvm_xen_maybe_deassert_callback(CPUState *cs) +{ + CPUX86State *env = &X86_CPU(cs)->env; + struct vcpu_info *vi = env->xen_vcpu_info_hva; + if (!vi) { + return; + } + + /* If the evtchn_upcall_pending flag is cleared, turn the GSI off. */ + if (!vi->evtchn_upcall_pending) { + bql_lock(); + /* + * Check again now we have the lock, because it may have been + * asserted in the interim. And we don't want to take the lock + * every time because this is a fast path. + */ + if (!vi->evtchn_upcall_pending) { + X86_CPU(cs)->env.xen_callback_asserted = false; + xen_evtchn_set_callback_level(0); + } + bql_unlock(); + } +} + +void kvm_xen_set_callback_asserted(void) +{ + CPUState *cs = qemu_get_cpu(0); + + if (cs) { + X86_CPU(cs)->env.xen_callback_asserted = true; + } +} + +bool kvm_xen_has_vcpu_callback_vector(void) +{ + CPUState *cs = qemu_get_cpu(0); + + return cs && !!X86_CPU(cs)->env.xen_vcpu_callback_vector; +} + +void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type) +{ + CPUState *cs = qemu_get_cpu(vcpu_id); + uint8_t vector; + + if (!cs) { + return; + } + + vector = X86_CPU(cs)->env.xen_vcpu_callback_vector; + if (vector) { + /* + * The per-vCPU callback vector injected via lapic. Just + * deliver it as an MSI. + */ + MSIMessage msg = { + .address = APIC_DEFAULT_ADDRESS | + (X86_CPU(cs)->apic_id << MSI_ADDR_DEST_ID_SHIFT), + .data = vector | (1UL << MSI_DATA_LEVEL_SHIFT), + }; + kvm_irqchip_send_msi(kvm_state, msg); + return; + } + + switch (type) { + case HVM_PARAM_CALLBACK_TYPE_VECTOR: + /* + * If the evtchn_upcall_pending field in the vcpu_info is set, then + * KVM will automatically deliver the vector on entering the vCPU + * so all we have to do is kick it out. + */ + qemu_cpu_kick(cs); + break; + + case HVM_PARAM_CALLBACK_TYPE_GSI: + case HVM_PARAM_CALLBACK_TYPE_PCI_INTX: + if (vcpu_id == 0) { + xen_evtchn_set_callback_level(1); + } + break; + } +} + +/* Must always be called with xen_timers_lock held */ +static int kvm_xen_set_vcpu_timer(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + struct kvm_xen_vcpu_attr va = { + .type = KVM_XEN_VCPU_ATTR_TYPE_TIMER, + .u.timer.port = env->xen_virq[VIRQ_TIMER], + .u.timer.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL, + .u.timer.expires_ns = env->xen_singleshot_timer_ns, + }; + + return kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_SET_ATTR, &va); +} + +static void do_set_vcpu_timer_virq(CPUState *cs, run_on_cpu_data data) +{ + QEMU_LOCK_GUARD(&X86_CPU(cs)->env.xen_timers_lock); + kvm_xen_set_vcpu_timer(cs); +} + +int kvm_xen_set_vcpu_virq(uint32_t vcpu_id, uint16_t virq, uint16_t port) +{ + CPUState *cs = qemu_get_cpu(vcpu_id); + + if (!cs) { + return -ENOENT; + } + + /* cpu.h doesn't include the actual Xen header. */ + qemu_build_assert(NR_VIRQS == XEN_NR_VIRQS); + + if (virq >= NR_VIRQS) { + return -EINVAL; + } + + if (port && X86_CPU(cs)->env.xen_virq[virq]) { + return -EEXIST; + } + + X86_CPU(cs)->env.xen_virq[virq] = port; + if (virq == VIRQ_TIMER && kvm_xen_has_cap(EVTCHN_SEND)) { + async_run_on_cpu(cs, do_set_vcpu_timer_virq, + RUN_ON_CPU_HOST_INT(port)); + } + return 0; +} + +static void do_set_vcpu_time_info_gpa(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + env->xen_vcpu_time_info_gpa = data.host_ulong; + + kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO, + env->xen_vcpu_time_info_gpa); +} + +static void do_set_vcpu_runstate_gpa(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + env->xen_vcpu_runstate_gpa = data.host_ulong; + + kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR, + env->xen_vcpu_runstate_gpa); +} + +static void do_vcpu_soft_reset(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + env->xen_vcpu_info_gpa = INVALID_GPA; + env->xen_vcpu_info_default_gpa = INVALID_GPA; + env->xen_vcpu_time_info_gpa = INVALID_GPA; + env->xen_vcpu_runstate_gpa = INVALID_GPA; + env->xen_vcpu_callback_vector = 0; + memset(env->xen_virq, 0, sizeof(env->xen_virq)); + + set_vcpu_info(cs, INVALID_GPA); + kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO, + INVALID_GPA); + kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR, + INVALID_GPA); + if (kvm_xen_has_cap(EVTCHN_SEND)) { + kvm_xen_set_vcpu_callback_vector(cs); + + QEMU_LOCK_GUARD(&X86_CPU(cs)->env.xen_timers_lock); + env->xen_singleshot_timer_ns = 0; + kvm_xen_set_vcpu_timer(cs); + } else { + vcpuop_stop_singleshot_timer(cs); + }; + +} + +static int xen_set_shared_info(uint64_t gfn) +{ + uint64_t gpa = gfn << TARGET_PAGE_BITS; + int i, err; + + BQL_LOCK_GUARD(); + + /* + * The xen_overlay device tells KVM about it too, since it had to + * do that on migration load anyway (unless we're going to jump + * through lots of hoops to maintain the fiction that this isn't + * KVM-specific. + */ + err = xen_overlay_map_shinfo_page(gpa); + if (err) { + return err; + } + + trace_kvm_xen_set_shared_info(gfn); + + for (i = 0; i < XEN_LEGACY_MAX_VCPUS; i++) { + CPUState *cpu = qemu_get_cpu(i); + if (cpu) { + async_run_on_cpu(cpu, do_set_vcpu_info_default_gpa, + RUN_ON_CPU_HOST_ULONG(gpa)); + } + gpa += sizeof(vcpu_info_t); + } + + return err; +} + +static int add_to_physmap_one(uint32_t space, uint64_t idx, uint64_t gfn) +{ + switch (space) { + case XENMAPSPACE_shared_info: + if (idx > 0) { + return -EINVAL; + } + return xen_set_shared_info(gfn); + + case XENMAPSPACE_grant_table: + return xen_gnttab_map_page(idx, gfn); + + case XENMAPSPACE_gmfn: + case XENMAPSPACE_gmfn_range: + return -ENOTSUP; + + case XENMAPSPACE_gmfn_foreign: + case XENMAPSPACE_dev_mmio: + return -EPERM; + + default: + return -EINVAL; + } +} + +static int do_add_to_physmap(struct kvm_xen_exit *exit, X86CPU *cpu, + uint64_t arg) +{ + struct xen_add_to_physmap xatp; + CPUState *cs = CPU(cpu); + + if (hypercall_compat32(exit->u.hcall.longmode)) { + struct compat_xen_add_to_physmap xatp32; + + qemu_build_assert(sizeof(struct compat_xen_add_to_physmap) == 16); + if (kvm_copy_from_gva(cs, arg, &xatp32, sizeof(xatp32))) { + return -EFAULT; + } + xatp.domid = xatp32.domid; + xatp.size = xatp32.size; + xatp.space = xatp32.space; + xatp.idx = xatp32.idx; + xatp.gpfn = xatp32.gpfn; + } else { + if (kvm_copy_from_gva(cs, arg, &xatp, sizeof(xatp))) { + return -EFAULT; + } + } + + if (xatp.domid != DOMID_SELF && xatp.domid != xen_domid) { + return -ESRCH; + } + + return add_to_physmap_one(xatp.space, xatp.idx, xatp.gpfn); +} + +static int do_add_to_physmap_batch(struct kvm_xen_exit *exit, X86CPU *cpu, + uint64_t arg) +{ + struct xen_add_to_physmap_batch xatpb; + unsigned long idxs_gva, gpfns_gva, errs_gva; + CPUState *cs = CPU(cpu); + size_t op_sz; + + if (hypercall_compat32(exit->u.hcall.longmode)) { + struct compat_xen_add_to_physmap_batch xatpb32; + + qemu_build_assert(sizeof(struct compat_xen_add_to_physmap_batch) == 20); + if (kvm_copy_from_gva(cs, arg, &xatpb32, sizeof(xatpb32))) { + return -EFAULT; + } + xatpb.domid = xatpb32.domid; + xatpb.space = xatpb32.space; + xatpb.size = xatpb32.size; + + idxs_gva = xatpb32.idxs.c; + gpfns_gva = xatpb32.gpfns.c; + errs_gva = xatpb32.errs.c; + op_sz = sizeof(uint32_t); + } else { + if (kvm_copy_from_gva(cs, arg, &xatpb, sizeof(xatpb))) { + return -EFAULT; + } + op_sz = sizeof(unsigned long); + idxs_gva = (unsigned long)xatpb.idxs.p; + gpfns_gva = (unsigned long)xatpb.gpfns.p; + errs_gva = (unsigned long)xatpb.errs.p; + } + + if (xatpb.domid != DOMID_SELF && xatpb.domid != xen_domid) { + return -ESRCH; + } + + /* Explicitly invalid for the batch op. Not that we implement it anyway. */ + if (xatpb.space == XENMAPSPACE_gmfn_range) { + return -EINVAL; + } + + while (xatpb.size--) { + unsigned long idx = 0; + unsigned long gpfn = 0; + int err; + + /* For 32-bit compat this only copies the low 32 bits of each */ + if (kvm_copy_from_gva(cs, idxs_gva, &idx, op_sz) || + kvm_copy_from_gva(cs, gpfns_gva, &gpfn, op_sz)) { + return -EFAULT; + } + idxs_gva += op_sz; + gpfns_gva += op_sz; + + err = add_to_physmap_one(xatpb.space, idx, gpfn); + + if (kvm_copy_to_gva(cs, errs_gva, &err, sizeof(err))) { + return -EFAULT; + } + errs_gva += sizeof(err); + } + return 0; +} + +static bool kvm_xen_hcall_memory_op(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, uint64_t arg) +{ + int err; + + switch (cmd) { + case XENMEM_add_to_physmap: + err = do_add_to_physmap(exit, cpu, arg); + break; + + case XENMEM_add_to_physmap_batch: + err = do_add_to_physmap_batch(exit, cpu, arg); + break; + + default: + return false; + } + + exit->u.hcall.result = err; + return true; +} + +static bool handle_set_param(struct kvm_xen_exit *exit, X86CPU *cpu, + uint64_t arg) +{ + CPUState *cs = CPU(cpu); + struct xen_hvm_param hp; + int err = 0; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(hp) == 16); + + if (kvm_copy_from_gva(cs, arg, &hp, sizeof(hp))) { + err = -EFAULT; + goto out; + } + + if (hp.domid != DOMID_SELF && hp.domid != xen_domid) { + err = -ESRCH; + goto out; + } + + switch (hp.index) { + case HVM_PARAM_CALLBACK_IRQ: + bql_lock(); + err = xen_evtchn_set_callback_param(hp.value); + bql_unlock(); + xen_set_long_mode(exit->u.hcall.longmode); + break; + default: + return false; + } + +out: + exit->u.hcall.result = err; + return true; +} + +static bool handle_get_param(struct kvm_xen_exit *exit, X86CPU *cpu, + uint64_t arg) +{ + CPUState *cs = CPU(cpu); + struct xen_hvm_param hp; + int err = 0; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(hp) == 16); + + if (kvm_copy_from_gva(cs, arg, &hp, sizeof(hp))) { + err = -EFAULT; + goto out; + } + + if (hp.domid != DOMID_SELF && hp.domid != xen_domid) { + err = -ESRCH; + goto out; + } + + switch (hp.index) { + case HVM_PARAM_STORE_PFN: + hp.value = XEN_SPECIAL_PFN(XENSTORE); + break; + case HVM_PARAM_STORE_EVTCHN: + hp.value = xen_xenstore_get_port(); + break; + case HVM_PARAM_CONSOLE_PFN: + hp.value = xen_primary_console_get_pfn(); + if (!hp.value) { + err = -EINVAL; + } + break; + case HVM_PARAM_CONSOLE_EVTCHN: + hp.value = xen_primary_console_get_port(); + if (!hp.value) { + err = -EINVAL; + } + break; + default: + return false; + } + + if (!err && kvm_copy_to_gva(cs, arg, &hp, sizeof(hp))) { + err = -EFAULT; + } +out: + exit->u.hcall.result = err; + return true; +} + +static int kvm_xen_hcall_evtchn_upcall_vector(struct kvm_xen_exit *exit, + X86CPU *cpu, uint64_t arg) +{ + struct xen_hvm_evtchn_upcall_vector up; + CPUState *target_cs; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(up) == 8); + + if (kvm_copy_from_gva(CPU(cpu), arg, &up, sizeof(up))) { + return -EFAULT; + } + + if (up.vector < 0x10) { + return -EINVAL; + } + + target_cs = qemu_get_cpu(up.vcpu); + if (!target_cs) { + return -EINVAL; + } + + async_run_on_cpu(target_cs, do_set_vcpu_callback_vector, + RUN_ON_CPU_HOST_INT(up.vector)); + return 0; +} + +static bool kvm_xen_hcall_hvm_op(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, uint64_t arg) +{ + int ret = -ENOSYS; + switch (cmd) { + case HVMOP_set_evtchn_upcall_vector: + ret = kvm_xen_hcall_evtchn_upcall_vector(exit, cpu, arg); + break; + + case HVMOP_pagetable_dying: + ret = -ENOSYS; + break; + + case HVMOP_set_param: + return handle_set_param(exit, cpu, arg); + + case HVMOP_get_param: + return handle_get_param(exit, cpu, arg); + + default: + return false; + } + + exit->u.hcall.result = ret; + return true; +} + +static int vcpuop_register_vcpu_info(CPUState *cs, CPUState *target, + uint64_t arg) +{ + struct vcpu_register_vcpu_info rvi; + uint64_t gpa; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(rvi) == 16); + qemu_build_assert(sizeof(struct vcpu_info) == 64); + + if (!target) { + return -ENOENT; + } + + if (kvm_copy_from_gva(cs, arg, &rvi, sizeof(rvi))) { + return -EFAULT; + } + + if (rvi.offset > TARGET_PAGE_SIZE - sizeof(struct vcpu_info)) { + return -EINVAL; + } + + gpa = ((rvi.mfn << TARGET_PAGE_BITS) + rvi.offset); + async_run_on_cpu(target, do_set_vcpu_info_gpa, RUN_ON_CPU_HOST_ULONG(gpa)); + return 0; +} + +static int vcpuop_register_vcpu_time_info(CPUState *cs, CPUState *target, + uint64_t arg) +{ + struct vcpu_register_time_memory_area tma; + uint64_t gpa; + size_t len; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(tma) == 8); + qemu_build_assert(sizeof(struct vcpu_time_info) == 32); + + if (!target) { + return -ENOENT; + } + + if (kvm_copy_from_gva(cs, arg, &tma, sizeof(tma))) { + return -EFAULT; + } + + /* + * Xen actually uses the GVA and does the translation through the guest + * page tables each time. But Linux/KVM uses the GPA, on the assumption + * that guests only ever use *global* addresses (kernel virtual addresses) + * for it. If Linux is changed to redo the GVA→GPA translation each time, + * it will offer a new vCPU attribute for that, and we'll use it instead. + */ + if (!kvm_gva_to_gpa(cs, tma.addr.p, &gpa, &len, false) || + len < sizeof(struct vcpu_time_info)) { + return -EFAULT; + } + + async_run_on_cpu(target, do_set_vcpu_time_info_gpa, + RUN_ON_CPU_HOST_ULONG(gpa)); + return 0; +} + +static int vcpuop_register_runstate_info(CPUState *cs, CPUState *target, + uint64_t arg) +{ + struct vcpu_register_runstate_memory_area rma; + uint64_t gpa; + size_t len; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(rma) == 8); + /* The runstate area actually does change size, but Linux copes. */ + + if (!target) { + return -ENOENT; + } + + if (kvm_copy_from_gva(cs, arg, &rma, sizeof(rma))) { + return -EFAULT; + } + + /* As with vcpu_time_info, Xen actually uses the GVA but KVM doesn't. */ + if (!kvm_gva_to_gpa(cs, rma.addr.p, &gpa, &len, false)) { + return -EFAULT; + } + + async_run_on_cpu(target, do_set_vcpu_runstate_gpa, + RUN_ON_CPU_HOST_ULONG(gpa)); + return 0; +} + +static uint64_t kvm_get_current_ns(void) +{ + struct kvm_clock_data data; + int ret; + + ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data); + if (ret < 0) { + fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret)); + abort(); + } + + return data.clock; +} + +static void xen_vcpu_singleshot_timer_event(void *opaque) +{ + CPUState *cpu = opaque; + CPUX86State *env = &X86_CPU(cpu)->env; + uint16_t port = env->xen_virq[VIRQ_TIMER]; + + if (likely(port)) { + xen_evtchn_set_port(port); + } + + qemu_mutex_lock(&env->xen_timers_lock); + env->xen_singleshot_timer_ns = 0; + qemu_mutex_unlock(&env->xen_timers_lock); +} + +static void xen_vcpu_periodic_timer_event(void *opaque) +{ + CPUState *cpu = opaque; + CPUX86State *env = &X86_CPU(cpu)->env; + uint16_t port = env->xen_virq[VIRQ_TIMER]; + int64_t qemu_now; + + if (likely(port)) { + xen_evtchn_set_port(port); + } + + qemu_mutex_lock(&env->xen_timers_lock); + + qemu_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + timer_mod_ns(env->xen_periodic_timer, + qemu_now + env->xen_periodic_timer_period); + + qemu_mutex_unlock(&env->xen_timers_lock); +} + +static int do_set_periodic_timer(CPUState *target, uint64_t period_ns) +{ + CPUX86State *tenv = &X86_CPU(target)->env; + int64_t qemu_now; + + timer_del(tenv->xen_periodic_timer); + + qemu_mutex_lock(&tenv->xen_timers_lock); + + qemu_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + timer_mod_ns(tenv->xen_periodic_timer, qemu_now + period_ns); + tenv->xen_periodic_timer_period = period_ns; + + qemu_mutex_unlock(&tenv->xen_timers_lock); + return 0; +} + +#define MILLISECS(_ms) ((int64_t)((_ms) * 1000000ULL)) +#define MICROSECS(_us) ((int64_t)((_us) * 1000ULL)) +#define STIME_MAX ((time_t)((int64_t)~0ull >> 1)) +/* Chosen so (NOW() + delta) won't overflow without an uptime of 200 years */ +#define STIME_DELTA_MAX ((int64_t)((uint64_t)~0ull >> 2)) + +static int vcpuop_set_periodic_timer(CPUState *cs, CPUState *target, + uint64_t arg) +{ + struct vcpu_set_periodic_timer spt; + + qemu_build_assert(sizeof(spt) == 8); + if (kvm_copy_from_gva(cs, arg, &spt, sizeof(spt))) { + return -EFAULT; + } + + if (spt.period_ns < MILLISECS(1) || spt.period_ns > STIME_DELTA_MAX) { + return -EINVAL; + } + + return do_set_periodic_timer(target, spt.period_ns); +} + +static int vcpuop_stop_periodic_timer(CPUState *target) +{ + CPUX86State *tenv = &X86_CPU(target)->env; + + qemu_mutex_lock(&tenv->xen_timers_lock); + + timer_del(tenv->xen_periodic_timer); + tenv->xen_periodic_timer_period = 0; + + qemu_mutex_unlock(&tenv->xen_timers_lock); + return 0; +} + +/* + * Userspace handling of timer, for older kernels. + * Must always be called with xen_timers_lock held. + */ +static int do_set_singleshot_timer(CPUState *cs, uint64_t timeout_abs, + bool linux_wa) +{ + CPUX86State *env = &X86_CPU(cs)->env; + int64_t now = kvm_get_current_ns(); + int64_t qemu_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t delta = timeout_abs - now; + + if (linux_wa && unlikely((int64_t)timeout_abs < 0 || + (delta > 0 && (uint32_t)(delta >> 50) != 0))) { + /* + * Xen has a 'Linux workaround' in do_set_timer_op() which checks + * for negative absolute timeout values (caused by integer + * overflow), and for values about 13 days in the future (2^50ns) + * which would be caused by jiffies overflow. For those cases, it + * sets the timeout 100ms in the future (not *too* soon, since if + * a guest really did set a long timeout on purpose we don't want + * to keep churning CPU time by waking it up). + */ + delta = (100 * SCALE_MS); + timeout_abs = now + delta; + } + + timer_mod_ns(env->xen_singleshot_timer, qemu_now + delta); + env->xen_singleshot_timer_ns = now + delta; + return 0; +} + +static int vcpuop_set_singleshot_timer(CPUState *cs, uint64_t arg) +{ + struct vcpu_set_singleshot_timer sst = { 0 }; + + /* + * The struct is a uint64_t followed by a uint32_t. On 32-bit that + * makes it 12 bytes. On 64-bit it gets padded to 16. The parts + * that get used are identical, and there's four bytes of padding + * unused at the end. For true Xen compatibility we should attempt + * to copy the full 16 bytes from 64-bit guests, and return -EFAULT + * if we can't get the padding too. But that's daft. Just copy what + * we need. + */ + qemu_build_assert(offsetof(struct vcpu_set_singleshot_timer, flags) == 8); + qemu_build_assert(sizeof(sst) >= 12); + + if (kvm_copy_from_gva(cs, arg, &sst, 12)) { + return -EFAULT; + } + + QEMU_LOCK_GUARD(&X86_CPU(cs)->env.xen_timers_lock); + + /* + * We ignore the VCPU_SSHOTTMR_future flag, just as Xen now does. + * The only guest that ever used it, got it wrong. + * https://xenbits.xen.org/gitweb/?p=xen.git;a=commitdiff;h=19c6cbd909 + */ + return do_set_singleshot_timer(cs, sst.timeout_abs_ns, false); +} + +static int vcpuop_stop_singleshot_timer(CPUState *cs) +{ + CPUX86State *env = &X86_CPU(cs)->env; + + qemu_mutex_lock(&env->xen_timers_lock); + + timer_del(env->xen_singleshot_timer); + env->xen_singleshot_timer_ns = 0; + + qemu_mutex_unlock(&env->xen_timers_lock); + return 0; +} + +static bool kvm_xen_hcall_set_timer_op(struct kvm_xen_exit *exit, X86CPU *cpu, + uint64_t timeout) +{ + int err; + + if (unlikely(timeout == 0)) { + err = vcpuop_stop_singleshot_timer(CPU(cpu)); + } else { + QEMU_LOCK_GUARD(&X86_CPU(cpu)->env.xen_timers_lock); + err = do_set_singleshot_timer(CPU(cpu), timeout, true); + } + exit->u.hcall.result = err; + return true; +} + +static bool kvm_xen_hcall_vcpu_op(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, int vcpu_id, uint64_t arg) +{ + CPUState *cs = CPU(cpu); + CPUState *dest = cs->cpu_index == vcpu_id ? cs : qemu_get_cpu(vcpu_id); + int err; + + if (!dest) { + err = -ENOENT; + goto out; + } + + switch (cmd) { + case VCPUOP_register_runstate_memory_area: + err = vcpuop_register_runstate_info(cs, dest, arg); + break; + case VCPUOP_register_vcpu_time_memory_area: + err = vcpuop_register_vcpu_time_info(cs, dest, arg); + break; + case VCPUOP_register_vcpu_info: + err = vcpuop_register_vcpu_info(cs, dest, arg); + break; + case VCPUOP_set_singleshot_timer: { + if (cs->cpu_index == vcpu_id) { + err = vcpuop_set_singleshot_timer(dest, arg); + } else { + err = -EINVAL; + } + break; + } + case VCPUOP_stop_singleshot_timer: + if (cs->cpu_index == vcpu_id) { + err = vcpuop_stop_singleshot_timer(dest); + } else { + err = -EINVAL; + } + break; + case VCPUOP_set_periodic_timer: { + err = vcpuop_set_periodic_timer(cs, dest, arg); + break; + } + case VCPUOP_stop_periodic_timer: + err = vcpuop_stop_periodic_timer(dest); + break; + + default: + return false; + } + + out: + exit->u.hcall.result = err; + return true; +} + +static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, uint64_t arg) +{ + CPUState *cs = CPU(cpu); + int err = -ENOSYS; + + switch (cmd) { + case EVTCHNOP_init_control: + case EVTCHNOP_expand_array: + case EVTCHNOP_set_priority: + /* We do not support FIFO channels at this point */ + err = -ENOSYS; + break; + + case EVTCHNOP_status: { + struct evtchn_status status; + + qemu_build_assert(sizeof(status) == 24); + if (kvm_copy_from_gva(cs, arg, &status, sizeof(status))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_status_op(&status); + if (!err && kvm_copy_to_gva(cs, arg, &status, sizeof(status))) { + err = -EFAULT; + } + break; + } + case EVTCHNOP_close: { + struct evtchn_close close; + + qemu_build_assert(sizeof(close) == 4); + if (kvm_copy_from_gva(cs, arg, &close, sizeof(close))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_close_op(&close); + break; + } + case EVTCHNOP_unmask: { + struct evtchn_unmask unmask; + + qemu_build_assert(sizeof(unmask) == 4); + if (kvm_copy_from_gva(cs, arg, &unmask, sizeof(unmask))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_unmask_op(&unmask); + break; + } + case EVTCHNOP_bind_virq: { + struct evtchn_bind_virq virq; + + qemu_build_assert(sizeof(virq) == 12); + if (kvm_copy_from_gva(cs, arg, &virq, sizeof(virq))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_bind_virq_op(&virq); + if (!err && kvm_copy_to_gva(cs, arg, &virq, sizeof(virq))) { + err = -EFAULT; + } + break; + } + case EVTCHNOP_bind_pirq: { + struct evtchn_bind_pirq pirq; + + qemu_build_assert(sizeof(pirq) == 12); + if (kvm_copy_from_gva(cs, arg, &pirq, sizeof(pirq))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_bind_pirq_op(&pirq); + if (!err && kvm_copy_to_gva(cs, arg, &pirq, sizeof(pirq))) { + err = -EFAULT; + } + break; + } + case EVTCHNOP_bind_ipi: { + struct evtchn_bind_ipi ipi; + + qemu_build_assert(sizeof(ipi) == 8); + if (kvm_copy_from_gva(cs, arg, &ipi, sizeof(ipi))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_bind_ipi_op(&ipi); + if (!err && kvm_copy_to_gva(cs, arg, &ipi, sizeof(ipi))) { + err = -EFAULT; + } + break; + } + case EVTCHNOP_send: { + struct evtchn_send send; + + qemu_build_assert(sizeof(send) == 4); + if (kvm_copy_from_gva(cs, arg, &send, sizeof(send))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_send_op(&send); + break; + } + case EVTCHNOP_alloc_unbound: { + struct evtchn_alloc_unbound alloc; + + qemu_build_assert(sizeof(alloc) == 8); + if (kvm_copy_from_gva(cs, arg, &alloc, sizeof(alloc))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_alloc_unbound_op(&alloc); + if (!err && kvm_copy_to_gva(cs, arg, &alloc, sizeof(alloc))) { + err = -EFAULT; + } + break; + } + case EVTCHNOP_bind_interdomain: { + struct evtchn_bind_interdomain interdomain; + + qemu_build_assert(sizeof(interdomain) == 12); + if (kvm_copy_from_gva(cs, arg, &interdomain, sizeof(interdomain))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_bind_interdomain_op(&interdomain); + if (!err && + kvm_copy_to_gva(cs, arg, &interdomain, sizeof(interdomain))) { + err = -EFAULT; + } + break; + } + case EVTCHNOP_bind_vcpu: { + struct evtchn_bind_vcpu vcpu; + + qemu_build_assert(sizeof(vcpu) == 8); + if (kvm_copy_from_gva(cs, arg, &vcpu, sizeof(vcpu))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_bind_vcpu_op(&vcpu); + break; + } + case EVTCHNOP_reset: { + struct evtchn_reset reset; + + qemu_build_assert(sizeof(reset) == 2); + if (kvm_copy_from_gva(cs, arg, &reset, sizeof(reset))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_reset_op(&reset); + break; + } + default: + return false; + } + + exit->u.hcall.result = err; + return true; +} + +int kvm_xen_soft_reset(void) +{ + CPUState *cpu; + int err; + + assert(bql_locked()); + + trace_kvm_xen_soft_reset(); + + err = xen_evtchn_soft_reset(); + if (err) { + return err; + } + + /* + * Zero is the reset/startup state for HVM_PARAM_CALLBACK_IRQ. Strictly, + * it maps to HVM_PARAM_CALLBACK_TYPE_GSI with GSI#0, but Xen refuses to + * to deliver to the timer interrupt and treats that as 'disabled'. + */ + err = xen_evtchn_set_callback_param(0); + if (err) { + return err; + } + + CPU_FOREACH(cpu) { + async_run_on_cpu(cpu, do_vcpu_soft_reset, RUN_ON_CPU_NULL); + } + + err = xen_overlay_map_shinfo_page(INVALID_GFN); + if (err) { + return err; + } + + err = xen_gnttab_reset(); + if (err) { + return err; + } + + err = xen_primary_console_reset(); + if (err) { + return err; + } + + err = xen_xenstore_reset(); + if (err) { + return err; + } + + return 0; +} + +static int schedop_shutdown(CPUState *cs, uint64_t arg) +{ + struct sched_shutdown shutdown; + int ret = 0; + + /* No need for 32/64 compat handling */ + qemu_build_assert(sizeof(shutdown) == 4); + + if (kvm_copy_from_gva(cs, arg, &shutdown, sizeof(shutdown))) { + return -EFAULT; + } + + switch (shutdown.reason) { + case SHUTDOWN_crash: + cpu_dump_state(cs, stderr, CPU_DUMP_CODE); + qemu_system_guest_panicked(NULL); + break; + + case SHUTDOWN_reboot: + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + break; + + case SHUTDOWN_poweroff: + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + break; + + case SHUTDOWN_soft_reset: + bql_lock(); + ret = kvm_xen_soft_reset(); + bql_unlock(); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static bool kvm_xen_hcall_sched_op(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, uint64_t arg) +{ + CPUState *cs = CPU(cpu); + int err = -ENOSYS; + + switch (cmd) { + case SCHEDOP_shutdown: + err = schedop_shutdown(cs, arg); + break; + + case SCHEDOP_poll: + /* + * Linux will panic if this doesn't work. Just yield; it's not + * worth overthinking it because with event channel handling + * in KVM, the kernel will intercept this and it will never + * reach QEMU anyway. The semantics of the hypercall explicltly + * permit spurious wakeups. + */ + case SCHEDOP_yield: + sched_yield(); + err = 0; + break; + + default: + return false; + } + + exit->u.hcall.result = err; + return true; +} + +static bool kvm_xen_hcall_gnttab_op(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, uint64_t arg, int count) +{ + CPUState *cs = CPU(cpu); + int err; + + switch (cmd) { + case GNTTABOP_set_version: { + struct gnttab_set_version set; + + qemu_build_assert(sizeof(set) == 4); + if (kvm_copy_from_gva(cs, arg, &set, sizeof(set))) { + err = -EFAULT; + break; + } + + err = xen_gnttab_set_version_op(&set); + if (!err && kvm_copy_to_gva(cs, arg, &set, sizeof(set))) { + err = -EFAULT; + } + break; + } + case GNTTABOP_get_version: { + struct gnttab_get_version get; + + qemu_build_assert(sizeof(get) == 8); + if (kvm_copy_from_gva(cs, arg, &get, sizeof(get))) { + err = -EFAULT; + break; + } + + err = xen_gnttab_get_version_op(&get); + if (!err && kvm_copy_to_gva(cs, arg, &get, sizeof(get))) { + err = -EFAULT; + } + break; + } + case GNTTABOP_query_size: { + struct gnttab_query_size size; + + qemu_build_assert(sizeof(size) == 16); + if (kvm_copy_from_gva(cs, arg, &size, sizeof(size))) { + err = -EFAULT; + break; + } + + err = xen_gnttab_query_size_op(&size); + if (!err && kvm_copy_to_gva(cs, arg, &size, sizeof(size))) { + err = -EFAULT; + } + break; + } + case GNTTABOP_setup_table: + case GNTTABOP_copy: + case GNTTABOP_map_grant_ref: + case GNTTABOP_unmap_grant_ref: + case GNTTABOP_swap_grant_ref: + return false; + + default: + /* Xen explicitly returns -ENOSYS to HVM guests for all others */ + err = -ENOSYS; + break; + } + + exit->u.hcall.result = err; + return true; +} + +static bool kvm_xen_hcall_physdev_op(struct kvm_xen_exit *exit, X86CPU *cpu, + int cmd, uint64_t arg) +{ + CPUState *cs = CPU(cpu); + int err; + + switch (cmd) { + case PHYSDEVOP_map_pirq: { + struct physdev_map_pirq map; + + if (hypercall_compat32(exit->u.hcall.longmode)) { + struct compat_physdev_map_pirq *map32 = (void *)↦ + + if (kvm_copy_from_gva(cs, arg, map32, sizeof(*map32))) { + return -EFAULT; + } + + /* + * The only thing that's different is the alignment of the + * uint64_t table_base at the end, which gets padding to make + * it 64-bit aligned in the 64-bit version. + */ + qemu_build_assert(sizeof(*map32) == 36); + qemu_build_assert(offsetof(struct physdev_map_pirq, entry_nr) == + offsetof(struct compat_physdev_map_pirq, entry_nr)); + memmove(&map.table_base, &map32->table_base, sizeof(map.table_base)); + } else { + if (kvm_copy_from_gva(cs, arg, &map, sizeof(map))) { + err = -EFAULT; + break; + } + } + err = xen_physdev_map_pirq(&map); + /* + * Since table_base is an IN parameter and won't be changed, just + * copy the size of the compat structure back to the guest. + */ + if (!err && kvm_copy_to_gva(cs, arg, &map, + sizeof(struct compat_physdev_map_pirq))) { + err = -EFAULT; + } + break; + } + case PHYSDEVOP_unmap_pirq: { + struct physdev_unmap_pirq unmap; + + qemu_build_assert(sizeof(unmap) == 8); + if (kvm_copy_from_gva(cs, arg, &unmap, sizeof(unmap))) { + err = -EFAULT; + break; + } + + err = xen_physdev_unmap_pirq(&unmap); + if (!err && kvm_copy_to_gva(cs, arg, &unmap, sizeof(unmap))) { + err = -EFAULT; + } + break; + } + case PHYSDEVOP_eoi: { + struct physdev_eoi eoi; + + qemu_build_assert(sizeof(eoi) == 4); + if (kvm_copy_from_gva(cs, arg, &eoi, sizeof(eoi))) { + err = -EFAULT; + break; + } + + err = xen_physdev_eoi_pirq(&eoi); + if (!err && kvm_copy_to_gva(cs, arg, &eoi, sizeof(eoi))) { + err = -EFAULT; + } + break; + } + case PHYSDEVOP_irq_status_query: { + struct physdev_irq_status_query query; + + qemu_build_assert(sizeof(query) == 8); + if (kvm_copy_from_gva(cs, arg, &query, sizeof(query))) { + err = -EFAULT; + break; + } + + err = xen_physdev_query_pirq(&query); + if (!err && kvm_copy_to_gva(cs, arg, &query, sizeof(query))) { + err = -EFAULT; + } + break; + } + case PHYSDEVOP_get_free_pirq: { + struct physdev_get_free_pirq get; + + qemu_build_assert(sizeof(get) == 8); + if (kvm_copy_from_gva(cs, arg, &get, sizeof(get))) { + err = -EFAULT; + break; + } + + err = xen_physdev_get_free_pirq(&get); + if (!err && kvm_copy_to_gva(cs, arg, &get, sizeof(get))) { + err = -EFAULT; + } + break; + } + case PHYSDEVOP_pirq_eoi_gmfn_v2: /* FreeBSD 13 makes this hypercall */ + err = -ENOSYS; + break; + + default: + return false; + } + + exit->u.hcall.result = err; + return true; +} + +static bool do_kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) +{ + uint16_t code = exit->u.hcall.input; + + if (exit->u.hcall.cpl > 0) { + exit->u.hcall.result = -EPERM; + return true; + } + + switch (code) { + case __HYPERVISOR_set_timer_op: + if (exit->u.hcall.longmode) { + return kvm_xen_hcall_set_timer_op(exit, cpu, + exit->u.hcall.params[0]); + } else { + /* In 32-bit mode, the 64-bit timer value is in two args. */ + uint64_t val = ((uint64_t)exit->u.hcall.params[1]) << 32 | + (uint32_t)exit->u.hcall.params[0]; + return kvm_xen_hcall_set_timer_op(exit, cpu, val); + } + case __HYPERVISOR_grant_table_op: + return kvm_xen_hcall_gnttab_op(exit, cpu, exit->u.hcall.params[0], + exit->u.hcall.params[1], + exit->u.hcall.params[2]); + case __HYPERVISOR_sched_op: + return kvm_xen_hcall_sched_op(exit, cpu, exit->u.hcall.params[0], + exit->u.hcall.params[1]); + case __HYPERVISOR_event_channel_op: + return kvm_xen_hcall_evtchn_op(exit, cpu, exit->u.hcall.params[0], + exit->u.hcall.params[1]); + case __HYPERVISOR_vcpu_op: + return kvm_xen_hcall_vcpu_op(exit, cpu, + exit->u.hcall.params[0], + exit->u.hcall.params[1], + exit->u.hcall.params[2]); + case __HYPERVISOR_hvm_op: + return kvm_xen_hcall_hvm_op(exit, cpu, exit->u.hcall.params[0], + exit->u.hcall.params[1]); + case __HYPERVISOR_memory_op: + return kvm_xen_hcall_memory_op(exit, cpu, exit->u.hcall.params[0], + exit->u.hcall.params[1]); + case __HYPERVISOR_physdev_op: + return kvm_xen_hcall_physdev_op(exit, cpu, exit->u.hcall.params[0], + exit->u.hcall.params[1]); + case __HYPERVISOR_xen_version: + return kvm_xen_hcall_xen_version(exit, cpu, exit->u.hcall.params[0], + exit->u.hcall.params[1]); + default: + return false; + } +} + +int kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit) +{ + if (exit->type != KVM_EXIT_XEN_HCALL) { + return -1; + } + + /* + * The kernel latches the guest 32/64 mode when the MSR is used to fill + * the hypercall page. So if we see a hypercall in a mode that doesn't + * match our own idea of the guest mode, fetch the kernel's idea of the + * "long mode" to remain in sync. + */ + if (exit->u.hcall.longmode != xen_is_long_mode()) { + xen_sync_long_mode(); + } + + if (!do_kvm_xen_handle_exit(cpu, exit)) { + /* + * Some hypercalls will be deliberately "implemented" by returning + * -ENOSYS. This case is for hypercalls which are unexpected. + */ + exit->u.hcall.result = -ENOSYS; + qemu_log_mask(LOG_UNIMP, "Unimplemented Xen hypercall %" + PRId64 " (0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64 ")\n", + (uint64_t)exit->u.hcall.input, + (uint64_t)exit->u.hcall.params[0], + (uint64_t)exit->u.hcall.params[1], + (uint64_t)exit->u.hcall.params[2]); + } + + trace_kvm_xen_hypercall(CPU(cpu)->cpu_index, exit->u.hcall.cpl, + exit->u.hcall.input, exit->u.hcall.params[0], + exit->u.hcall.params[1], exit->u.hcall.params[2], + exit->u.hcall.result); + return 0; +} + +uint16_t kvm_xen_get_gnttab_max_frames(void) +{ + KVMState *s = KVM_STATE(current_accel()); + return s->xen_gnttab_max_frames; +} + +uint16_t kvm_xen_get_evtchn_max_pirq(void) +{ + KVMState *s = KVM_STATE(current_accel()); + return s->xen_evtchn_max_pirq; +} + +int kvm_put_xen_state(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + uint64_t gpa; + int ret; + + gpa = env->xen_vcpu_info_gpa; + if (gpa == INVALID_GPA) { + gpa = env->xen_vcpu_info_default_gpa; + } + + if (gpa != INVALID_GPA) { + ret = set_vcpu_info(cs, gpa); + if (ret < 0) { + return ret; + } + } + + gpa = env->xen_vcpu_time_info_gpa; + if (gpa != INVALID_GPA) { + ret = kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO, + gpa); + if (ret < 0) { + return ret; + } + } + + gpa = env->xen_vcpu_runstate_gpa; + if (gpa != INVALID_GPA) { + ret = kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR, + gpa); + if (ret < 0) { + return ret; + } + } + + if (env->xen_periodic_timer_period) { + ret = do_set_periodic_timer(cs, env->xen_periodic_timer_period); + if (ret < 0) { + return ret; + } + } + + if (!kvm_xen_has_cap(EVTCHN_SEND)) { + /* + * If the kernel has EVTCHN_SEND support then it handles timers too, + * so the timer will be restored by kvm_xen_set_vcpu_timer() below. + */ + QEMU_LOCK_GUARD(&env->xen_timers_lock); + if (env->xen_singleshot_timer_ns) { + ret = do_set_singleshot_timer(cs, env->xen_singleshot_timer_ns, + false); + if (ret < 0) { + return ret; + } + } + return 0; + } + + if (env->xen_vcpu_callback_vector) { + ret = kvm_xen_set_vcpu_callback_vector(cs); + if (ret < 0) { + return ret; + } + } + + if (env->xen_virq[VIRQ_TIMER]) { + do_set_vcpu_timer_virq(cs, + RUN_ON_CPU_HOST_INT(env->xen_virq[VIRQ_TIMER])); + } + return 0; +} + +int kvm_get_xen_state(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + uint64_t gpa; + int ret; + + /* + * The kernel does not mark vcpu_info as dirty when it delivers interrupts + * to it. It's up to userspace to *assume* that any page shared thus is + * always considered dirty. The shared_info page is different since it's + * an overlay and migrated separately anyway. + */ + gpa = env->xen_vcpu_info_gpa; + if (gpa == INVALID_GPA) { + gpa = env->xen_vcpu_info_default_gpa; + } + if (gpa != INVALID_GPA) { + MemoryRegionSection mrs = memory_region_find(get_system_memory(), + gpa, + sizeof(struct vcpu_info)); + if (mrs.mr && + !int128_lt(mrs.size, int128_make64(sizeof(struct vcpu_info)))) { + memory_region_set_dirty(mrs.mr, mrs.offset_within_region, + sizeof(struct vcpu_info)); + } + } + + if (!kvm_xen_has_cap(EVTCHN_SEND)) { + return 0; + } + + /* + * If the kernel is accelerating timers, read out the current value of the + * singleshot timer deadline. + */ + if (env->xen_virq[VIRQ_TIMER]) { + struct kvm_xen_vcpu_attr va = { + .type = KVM_XEN_VCPU_ATTR_TYPE_TIMER, + }; + ret = kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_GET_ATTR, &va); + if (ret < 0) { + return ret; + } + + /* + * This locking is fairly pointless, and is here to appease Coverity. + * There is an unavoidable race condition if a different vCPU sets a + * timer for this vCPU after the value has been read out. But that's + * OK in practice because *all* the vCPUs need to be stopped before + * we set about migrating their state. + */ + QEMU_LOCK_GUARD(&X86_CPU(cs)->env.xen_timers_lock); + env->xen_singleshot_timer_ns = va.u.timer.expires_ns; + } + + return 0; +} diff --git a/target/i386/kvm/xen-emu.h b/target/i386/kvm/xen-emu.h new file mode 100644 index 0000000000..fe85e0b195 --- /dev/null +++ b/target/i386/kvm/xen-emu.h @@ -0,0 +1,33 @@ +/* + * Xen HVM emulation support in KVM + * + * Copyright © 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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_I386_KVM_XEN_EMU_H +#define QEMU_I386_KVM_XEN_EMU_H + +#define XEN_HYPERCALL_MSR 0x40000000 +#define XEN_HYPERCALL_MSR_HYPERV 0x40000200 + +#define XEN_CPUID_SIGNATURE 0 +#define XEN_CPUID_VENDOR 1 +#define XEN_CPUID_HVM_MSR 2 +#define XEN_CPUID_TIME 3 +#define XEN_CPUID_HVM 4 + +#define XEN_VERSION(maj, min) ((maj) << 16 | (min)) + +int kvm_xen_init(KVMState *s, uint32_t hypercall_msr); +int kvm_xen_init_vcpu(CPUState *cs); +int kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit); +int kvm_put_xen_state(CPUState *cs); +int kvm_get_xen_state(CPUState *cs); +void kvm_xen_maybe_deassert_callback(CPUState *cs); + +#endif /* QEMU_I386_KVM_XEN_EMU_H */ diff --git a/target/i386/machine.c b/target/i386/machine.c index cecd476e98..c3ae320814 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -6,8 +6,10 @@ #include "kvm/hyperv.h" #include "hw/i386/x86.h" #include "kvm/kvm_i386.h" +#include "hw/xen/xen.h" #include "sysemu/kvm.h" +#include "sysemu/kvm_xen.h" #include "sysemu/tcg.h" #include "qemu/error-report.h" @@ -16,7 +18,7 @@ static const VMStateDescription vmstate_segment = { .name = "segment", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(selector, SegmentCache), VMSTATE_UINTTL(base, SegmentCache), VMSTATE_UINT32(limit, SegmentCache), @@ -41,7 +43,7 @@ static const VMStateDescription vmstate_xmm_reg = { .name = "xmm_reg", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(ZMM_Q(0), ZMMReg), VMSTATE_UINT64(ZMM_Q(1), ZMMReg), VMSTATE_END_OF_LIST() @@ -57,7 +59,7 @@ static const VMStateDescription vmstate_ymmh_reg = { .name = "ymmh_reg", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(ZMM_Q(2), ZMMReg), VMSTATE_UINT64(ZMM_Q(3), ZMMReg), VMSTATE_END_OF_LIST() @@ -72,7 +74,7 @@ static const VMStateDescription vmstate_zmmh_reg = { .name = "zmmh_reg", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(ZMM_Q(4), ZMMReg), VMSTATE_UINT64(ZMM_Q(5), ZMMReg), VMSTATE_UINT64(ZMM_Q(6), ZMMReg), @@ -90,7 +92,7 @@ static const VMStateDescription vmstate_hi16_zmm_reg = { .name = "hi16_zmm_reg", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(ZMM_Q(0), ZMMReg), VMSTATE_UINT64(ZMM_Q(1), ZMMReg), VMSTATE_UINT64(ZMM_Q(2), ZMMReg), @@ -112,7 +114,7 @@ static const VMStateDescription vmstate_bnd_regs = { .name = "bnd_regs", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(lb, BNDReg), VMSTATE_UINT64(ub, BNDReg), VMSTATE_END_OF_LIST() @@ -126,7 +128,7 @@ static const VMStateDescription vmstate_mtrr_var = { .name = "mtrr_var", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(base, MTRRVar), VMSTATE_UINT64(mask, MTRRVar), VMSTATE_END_OF_LIST() @@ -140,7 +142,7 @@ static const VMStateDescription vmstate_lbr_records_var = { .name = "lbr_records_var", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(from, LBREntry), VMSTATE_UINT64(to, LBREntry), VMSTATE_UINT64(info, LBREntry), @@ -199,7 +201,7 @@ static const VMStateDescription vmstate_fpreg_tmp = { .name = "fpreg_tmp", .post_load = fpreg_post_load, .pre_save = fpreg_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(tmp_mant, x86_FPReg_tmp), VMSTATE_UINT16(tmp_exp, x86_FPReg_tmp), VMSTATE_END_OF_LIST() @@ -208,7 +210,7 @@ static const VMStateDescription vmstate_fpreg_tmp = { static const VMStateDescription vmstate_fpreg = { .name = "fpreg", - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_WITH_TMP(FPReg, x86_FPReg_tmp, vmstate_fpreg_tmp), VMSTATE_END_OF_LIST() } @@ -280,12 +282,12 @@ static int cpu_pre_save(void *opaque) * 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. + * important to save the exception payload separately. * * Therefore, if an exception is not in a pending state * or vCPU is not in guest-mode, it is not important to * distinguish between a pending and injected exception - * and we don't need to store seperately the exception payload. + * and we don't need to store separately the exception payload. * * In order to preserve better backwards-compatible migration, * convert a pending exception to an injected exception in @@ -451,7 +453,7 @@ static const VMStateDescription vmstate_exception_info = { .version_id = 1, .minimum_version_id = 1, .needed = exception_info_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(env.exception_pending, X86CPU), VMSTATE_UINT8(env.exception_injected, X86CPU), VMSTATE_UINT8(env.exception_has_payload, X86CPU), @@ -473,7 +475,7 @@ static const VMStateDescription vmstate_steal_time_msr = { .version_id = 1, .minimum_version_id = 1, .needed = steal_time_msr_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.steal_time_msr, X86CPU), VMSTATE_END_OF_LIST() } @@ -484,7 +486,7 @@ static const VMStateDescription vmstate_async_pf_msr = { .version_id = 1, .minimum_version_id = 1, .needed = async_pf_msr_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.async_pf_en_msr, X86CPU), VMSTATE_END_OF_LIST() } @@ -495,7 +497,7 @@ static const VMStateDescription vmstate_async_pf_int_msr = { .version_id = 1, .minimum_version_id = 1, .needed = async_pf_int_msr_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.async_pf_int_msr, X86CPU), VMSTATE_END_OF_LIST() } @@ -506,7 +508,7 @@ static const VMStateDescription vmstate_pv_eoi_msr = { .version_id = 1, .minimum_version_id = 1, .needed = pv_eoi_msr_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.pv_eoi_en_msr, X86CPU), VMSTATE_END_OF_LIST() } @@ -517,7 +519,7 @@ static const VMStateDescription vmstate_poll_control_msr = { .version_id = 1, .minimum_version_id = 1, .needed = poll_control_msr_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.poll_control_msr, X86CPU), VMSTATE_END_OF_LIST() } @@ -536,7 +538,7 @@ static const VMStateDescription vmstate_fpop_ip_dp = { .version_id = 1, .minimum_version_id = 1, .needed = fpop_ip_dp_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(env.fpop, X86CPU), VMSTATE_UINT64(env.fpip, X86CPU), VMSTATE_UINT64(env.fpdp, X86CPU), @@ -557,7 +559,7 @@ static const VMStateDescription vmstate_msr_tsc_adjust = { .version_id = 1, .minimum_version_id = 1, .needed = tsc_adjust_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.tsc_adjust, X86CPU), VMSTATE_END_OF_LIST() } @@ -576,7 +578,7 @@ static const VMStateDescription vmstate_msr_smi_count = { .version_id = 1, .minimum_version_id = 1, .needed = msr_smi_count_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.msr_smi_count, X86CPU), VMSTATE_END_OF_LIST() } @@ -595,7 +597,7 @@ static const VMStateDescription vmstate_msr_tscdeadline = { .version_id = 1, .minimum_version_id = 1, .needed = tscdeadline_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.tsc_deadline, X86CPU), VMSTATE_END_OF_LIST() } @@ -622,7 +624,7 @@ static const VMStateDescription vmstate_msr_ia32_misc_enable = { .version_id = 1, .minimum_version_id = 1, .needed = misc_enable_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.msr_ia32_misc_enable, X86CPU), VMSTATE_END_OF_LIST() } @@ -633,7 +635,7 @@ static const VMStateDescription vmstate_msr_ia32_feature_control = { .version_id = 1, .minimum_version_id = 1, .needed = feature_control_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.msr_ia32_feature_control, X86CPU), VMSTATE_END_OF_LIST() } @@ -668,7 +670,7 @@ static const VMStateDescription vmstate_msr_architectural_pmu = { .version_id = 1, .minimum_version_id = 1, .needed = pmu_enable_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.msr_fixed_ctr_ctrl, X86CPU), VMSTATE_UINT64(env.msr_global_ctrl, X86CPU), VMSTATE_UINT64(env.msr_global_status, X86CPU), @@ -704,7 +706,7 @@ static const VMStateDescription vmstate_mpx = { .version_id = 1, .minimum_version_id = 1, .needed = mpx_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BND_REGS(env.bnd_regs, X86CPU, 4), VMSTATE_UINT64(env.bndcs_regs.cfgu, X86CPU), VMSTATE_UINT64(env.bndcs_regs.sts, X86CPU), @@ -726,7 +728,7 @@ static const VMStateDescription vmstate_msr_hyperv_hypercall = { .version_id = 1, .minimum_version_id = 1, .needed = hyperv_hypercall_enable_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.msr_hv_guest_os_id, X86CPU), VMSTATE_UINT64(env.msr_hv_hypercall, X86CPU), VMSTATE_END_OF_LIST() @@ -746,7 +748,7 @@ static const VMStateDescription vmstate_msr_hyperv_vapic = { .version_id = 1, .minimum_version_id = 1, .needed = hyperv_vapic_enable_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.msr_hv_vapic, X86CPU), VMSTATE_END_OF_LIST() } @@ -765,7 +767,7 @@ static const VMStateDescription vmstate_msr_hyperv_time = { .version_id = 1, .minimum_version_id = 1, .needed = hyperv_time_enable_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.msr_hv_tsc, X86CPU), VMSTATE_END_OF_LIST() } @@ -790,7 +792,7 @@ static const VMStateDescription vmstate_msr_hyperv_crash = { .version_id = 1, .minimum_version_id = 1, .needed = hyperv_crash_enable_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.msr_hv_crash_params, X86CPU, HV_CRASH_PARAMS), VMSTATE_END_OF_LIST() } @@ -813,7 +815,7 @@ static const VMStateDescription vmstate_msr_hyperv_runtime = { .version_id = 1, .minimum_version_id = 1, .needed = hyperv_runtime_enable_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.msr_hv_runtime, X86CPU), VMSTATE_END_OF_LIST() } @@ -853,7 +855,7 @@ static const VMStateDescription vmstate_msr_hyperv_synic = { .minimum_version_id = 1, .needed = hyperv_synic_enable_needed, .post_load = hyperv_synic_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.msr_hv_synic_control, X86CPU), VMSTATE_UINT64(env.msr_hv_synic_evt_page, X86CPU), VMSTATE_UINT64(env.msr_hv_synic_msg_page, X86CPU), @@ -881,7 +883,7 @@ static const VMStateDescription vmstate_msr_hyperv_stimer = { .version_id = 1, .minimum_version_id = 1, .needed = hyperv_stimer_enable_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.msr_hv_stimer_config, X86CPU, HV_STIMER_COUNT), VMSTATE_UINT64_ARRAY(env.msr_hv_stimer_count, X86CPU, HV_STIMER_COUNT), @@ -924,7 +926,7 @@ static const VMStateDescription vmstate_msr_hyperv_reenlightenment = { .minimum_version_id = 1, .needed = hyperv_reenlightenment_enable_needed, .post_load = hyperv_reenlightenment_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.msr_hv_reenlightenment_control, X86CPU), VMSTATE_UINT64(env.msr_hv_tsc_emulation_control, X86CPU), VMSTATE_UINT64(env.msr_hv_tsc_emulation_status, X86CPU), @@ -968,7 +970,7 @@ static const VMStateDescription vmstate_avx512 = { .version_id = 1, .minimum_version_id = 1, .needed = avx512_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.opmask_regs, X86CPU, NB_OPMASK_REGS), VMSTATE_ZMMH_REGS_VARS(env.xmm_regs, X86CPU, 0), #ifdef TARGET_X86_64 @@ -991,7 +993,7 @@ static const VMStateDescription vmstate_xss = { .version_id = 1, .minimum_version_id = 1, .needed = xss_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.xss, X86CPU), VMSTATE_END_OF_LIST() } @@ -1010,7 +1012,7 @@ static const VMStateDescription vmstate_umwait = { .version_id = 1, .minimum_version_id = 1, .needed = umwait_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.umwait, X86CPU), VMSTATE_END_OF_LIST() } @@ -1029,7 +1031,7 @@ static const VMStateDescription vmstate_pkru = { .version_id = 1, .minimum_version_id = 1, .needed = pkru_needed, - .fields = (VMStateField[]){ + .fields = (const VMStateField[]){ VMSTATE_UINT32(env.pkru, X86CPU), VMSTATE_END_OF_LIST() } @@ -1048,7 +1050,7 @@ static const VMStateDescription vmstate_pkrs = { .version_id = 1, .minimum_version_id = 1, .needed = pkrs_needed, - .fields = (VMStateField[]){ + .fields = (const VMStateField[]){ VMSTATE_UINT32(env.pkrs, X86CPU), VMSTATE_END_OF_LIST() } @@ -1068,7 +1070,7 @@ static const VMStateDescription vmstate_tsc_khz = { .version_id = 1, .minimum_version_id = 1, .needed = tsc_khz_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64(env.tsc_khz, X86CPU), VMSTATE_END_OF_LIST() } @@ -1088,7 +1090,7 @@ static const VMStateDescription vmstate_vmx_vmcs12 = { .version_id = 1, .minimum_version_id = 1, .needed = vmx_vmcs12_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(data.vmx[0].vmcs12, struct kvm_nested_state, KVM_STATE_NESTED_VMX_VMCS_SIZE), @@ -1108,7 +1110,7 @@ static const VMStateDescription vmstate_vmx_shadow_vmcs12 = { .version_id = 1, .minimum_version_id = 1, .needed = vmx_shadow_vmcs12_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(data.vmx[0].shadow_vmcs12, struct kvm_nested_state, KVM_STATE_NESTED_VMX_VMCS_SIZE), @@ -1129,13 +1131,13 @@ static const VMStateDescription vmstate_vmx_nested_state = { .version_id = 1, .minimum_version_id = 1, .needed = vmx_nested_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_U64(hdr.vmx.vmxon_pa, struct kvm_nested_state), VMSTATE_U64(hdr.vmx.vmcs12_pa, struct kvm_nested_state), VMSTATE_U16(hdr.vmx.smm.flags, struct kvm_nested_state), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_vmx_vmcs12, &vmstate_vmx_shadow_vmcs12, NULL, @@ -1160,7 +1162,7 @@ static const VMStateDescription vmstate_svm_nested_state = { .version_id = 1, .minimum_version_id = 1, .needed = svm_nested_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_U64(hdr.svm.vmcb_pa, struct kvm_nested_state), VMSTATE_UINT8_ARRAY(data.svm[0].vmcb12, struct kvm_nested_state, @@ -1230,13 +1232,13 @@ static const VMStateDescription vmstate_kvm_nested_state = { .name = "cpu/kvm_nested_state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_U16(flags, struct kvm_nested_state), VMSTATE_U16(format, struct kvm_nested_state), VMSTATE_U32(size, struct kvm_nested_state), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_vmx_nested_state, &vmstate_svm_nested_state, NULL @@ -1249,7 +1251,7 @@ static const VMStateDescription vmstate_nested_state = { .minimum_version_id = 1, .needed = nested_state_needed, .post_load = nested_state_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_POINTER(env.nested_state, X86CPU, vmstate_kvm_nested_state, struct kvm_nested_state), @@ -1257,6 +1259,28 @@ static const VMStateDescription vmstate_nested_state = { } }; +static bool xen_vcpu_needed(void *opaque) +{ + return (xen_mode == XEN_EMULATE); +} + +static const VMStateDescription vmstate_xen_vcpu = { + .name = "cpu/xen_vcpu", + .version_id = 1, + .minimum_version_id = 1, + .needed = xen_vcpu_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(env.xen_vcpu_info_gpa, X86CPU), + VMSTATE_UINT64(env.xen_vcpu_info_default_gpa, X86CPU), + VMSTATE_UINT64(env.xen_vcpu_time_info_gpa, X86CPU), + VMSTATE_UINT64(env.xen_vcpu_runstate_gpa, X86CPU), + VMSTATE_UINT8(env.xen_vcpu_callback_vector, X86CPU), + VMSTATE_UINT16_ARRAY(env.xen_virq, X86CPU, XEN_NR_VIRQS), + VMSTATE_UINT64(env.xen_singleshot_timer_ns, X86CPU), + VMSTATE_UINT64(env.xen_periodic_timer_period, X86CPU), + VMSTATE_END_OF_LIST() + } +}; #endif static bool mcg_ext_ctl_needed(void *opaque) @@ -1271,7 +1295,7 @@ static const VMStateDescription vmstate_mcg_ext_ctl = { .version_id = 1, .minimum_version_id = 1, .needed = mcg_ext_ctl_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.mcg_ext_ctl, X86CPU), VMSTATE_END_OF_LIST() } @@ -1290,7 +1314,7 @@ static const VMStateDescription vmstate_spec_ctrl = { .version_id = 1, .minimum_version_id = 1, .needed = spec_ctrl_needed, - .fields = (VMStateField[]){ + .fields = (const VMStateField[]){ VMSTATE_UINT64(env.spec_ctrl, X86CPU), VMSTATE_END_OF_LIST() } @@ -1310,7 +1334,7 @@ static const VMStateDescription amd_tsc_scale_msr_ctrl = { .version_id = 1, .minimum_version_id = 1, .needed = amd_tsc_scale_msr_needed, - .fields = (VMStateField[]){ + .fields = (const VMStateField[]){ VMSTATE_UINT64(env.amd_tsc_scale_msr, X86CPU), VMSTATE_END_OF_LIST() } @@ -1343,7 +1367,7 @@ static const VMStateDescription vmstate_msr_intel_pt = { .version_id = 1, .minimum_version_id = 1, .needed = intel_pt_enable_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.msr_rtit_ctrl, X86CPU), VMSTATE_UINT64(env.msr_rtit_status, X86CPU), VMSTATE_UINT64(env.msr_rtit_output_base, X86CPU), @@ -1367,7 +1391,7 @@ static const VMStateDescription vmstate_msr_virt_ssbd = { .version_id = 1, .minimum_version_id = 1, .needed = virt_ssbd_needed, - .fields = (VMStateField[]){ + .fields = (const VMStateField[]){ VMSTATE_UINT64(env.virt_ssbd, X86CPU), VMSTATE_END_OF_LIST() } @@ -1386,7 +1410,7 @@ static const VMStateDescription vmstate_svm_npt = { .version_id = 1, .minimum_version_id = 1, .needed = svm_npt_needed, - .fields = (VMStateField[]){ + .fields = (const VMStateField[]){ VMSTATE_UINT64(env.nested_cr3, X86CPU), VMSTATE_UINT32(env.nested_pg_mode, X86CPU), VMSTATE_END_OF_LIST() @@ -1406,7 +1430,7 @@ static const VMStateDescription vmstate_svm_guest = { .version_id = 1, .minimum_version_id = 1, .needed = svm_guest_needed, - .fields = (VMStateField[]){ + .fields = (const VMStateField[]){ VMSTATE_UINT32(env.int_ctl, X86CPU), VMSTATE_END_OF_LIST() } @@ -1426,7 +1450,7 @@ static const VMStateDescription vmstate_efer32 = { .version_id = 1, .minimum_version_id = 1, .needed = intel_efer32_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.efer, X86CPU), VMSTATE_END_OF_LIST() } @@ -1446,7 +1470,7 @@ static const VMStateDescription vmstate_msr_tsx_ctrl = { .version_id = 1, .minimum_version_id = 1, .needed = msr_tsx_ctrl_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.tsx_ctrl, X86CPU), VMSTATE_END_OF_LIST() } @@ -1465,7 +1489,7 @@ static const VMStateDescription vmstate_msr_intel_sgx = { .version_id = 1, .minimum_version_id = 1, .needed = intel_sgx_msrs_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.msr_ia32_sgxlepubkeyhash, X86CPU, 4), VMSTATE_END_OF_LIST() } @@ -1493,7 +1517,7 @@ static const VMStateDescription vmstate_pdptrs = { .minimum_version_id = 1, .needed = pdptrs_needed, .post_load = pdptrs_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.pdptrs, X86CPU, 4), VMSTATE_END_OF_LIST() } @@ -1512,7 +1536,7 @@ static const VMStateDescription vmstate_msr_xfd = { .version_id = 1, .minimum_version_id = 1, .needed = xfd_msrs_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.msr_xfd, X86CPU), VMSTATE_UINT64(env.msr_xfd_err, X86CPU), VMSTATE_END_OF_LIST() @@ -1533,7 +1557,7 @@ static const VMStateDescription vmstate_amx_xtile = { .version_id = 1, .minimum_version_id = 1, .needed = amx_xtile_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(env.xtilecfg, X86CPU, 64), VMSTATE_UINT8_ARRAY(env.xtiledata, X86CPU, 8192), VMSTATE_END_OF_LIST() @@ -1554,7 +1578,7 @@ static const VMStateDescription vmstate_arch_lbr = { .version_id = 1, .minimum_version_id = 1, .needed = arch_lbr_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.msr_lbr_ctl, X86CPU), VMSTATE_UINT64(env.msr_lbr_depth, X86CPU), VMSTATE_LBR_VARS(env.lbr_records, X86CPU, ARCH_LBR_NR_ENTRIES, 1), @@ -1562,13 +1586,32 @@ static const VMStateDescription vmstate_arch_lbr = { } }; +static bool triple_fault_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return env->triple_fault_pending; +} + +static const VMStateDescription vmstate_triple_fault = { + .name = "cpu/triple_fault", + .version_id = 1, + .minimum_version_id = 1, + .needed = triple_fault_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT8(env.triple_fault_pending, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_x86_cpu = { .name = "cpu", .version_id = 12, .minimum_version_id = 11, .pre_save = cpu_pre_save, .post_load = cpu_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.regs, X86CPU, CPU_NB_REGS), VMSTATE_UINTTL(env.eip, X86CPU), VMSTATE_UINTTL(env.eflags, X86CPU), @@ -1656,7 +1699,7 @@ const VMStateDescription vmstate_x86_cpu = { VMSTATE_END_OF_LIST() /* The above list is not sorted /wrt version numbers, watch out! */ }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_exception_info, &vmstate_async_pf_msr, &vmstate_async_pf_int_msr, @@ -1697,6 +1740,7 @@ const VMStateDescription vmstate_x86_cpu = { #endif #ifdef CONFIG_KVM &vmstate_nested_state, + &vmstate_xen_vcpu, #endif &vmstate_msr_tsx_ctrl, &vmstate_msr_intel_sgx, @@ -1706,6 +1750,7 @@ const VMStateDescription vmstate_x86_cpu = { &vmstate_amx_xtile, #endif &vmstate_arch_lbr, + &vmstate_triple_fault, NULL } }; diff --git a/target/i386/meson.build b/target/i386/meson.build index ae38dc9563..7c74bfa859 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -12,25 +12,24 @@ i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c')) i386_ss.add(when: 'CONFIG_KVM', if_true: files('host-cpu.c')) i386_ss.add(when: 'CONFIG_HVF', if_true: files('host-cpu.c')) -i386_softmmu_ss = ss.source_set() -i386_softmmu_ss.add(files( +i386_system_ss = ss.source_set() +i386_system_ss.add(files( 'arch_dump.c', 'arch_memory_mapping.c', 'machine.c', 'monitor.c', 'cpu-sysemu.c', )) -i386_softmmu_ss.add(when: 'CONFIG_SEV', if_true: files('sev.c'), if_false: files('sev-sysemu-stub.c')) +i386_system_ss.add(when: 'CONFIG_SEV', if_true: files('sev.c'), if_false: files('sev-sysemu-stub.c')) i386_user_ss = ss.source_set() subdir('kvm') -subdir('hax') subdir('whpx') subdir('nvmm') subdir('hvf') subdir('tcg') target_arch += {'i386': i386_ss} -target_softmmu_arch += {'i386': i386_softmmu_ss} +target_system_arch += {'i386': i386_system_ss} target_user_arch += {'i386': i386_user_ss} diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 8e4b4d600c..3a281dab02 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -28,12 +28,11 @@ #include "monitor/hmp-target.h" #include "monitor/hmp.h" #include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" +#include "sysemu/hw_accel.h" #include "sysemu/kvm.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc-target.h" #include "qapi/qapi-commands-misc.h" -#include "hw/i386/pc.h" /* Perform linear address sign extension */ static hwaddr addr_canonical(CPUArchState *env, hwaddr addr) @@ -57,7 +56,7 @@ static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr, { addr = addr_canonical(env, addr); - monitor_printf(mon, TARGET_FMT_plx ": " TARGET_FMT_plx + monitor_printf(mon, HWADDR_FMT_plx ": " HWADDR_FMT_plx " %c%c%c%c%c%c%c%c%c\n", addr, pte & mask, @@ -258,8 +257,8 @@ static void mem_print(Monitor *mon, CPUArchState *env, prot1 = *plast_prot; if (prot != prot1) { if (*pstart != -1) { - monitor_printf(mon, TARGET_FMT_plx "-" TARGET_FMT_plx " " - TARGET_FMT_plx " %c%c%c\n", + monitor_printf(mon, HWADDR_FMT_plx "-" HWADDR_FMT_plx " " + HWADDR_FMT_plx " %c%c%c\n", addr_canonical(env, *pstart), addr_canonical(env, end), addr_canonical(env, end - *pstart), @@ -655,7 +654,11 @@ void hmp_info_local_apic(Monitor *mon, const QDict *qdict) if (qdict_haskey(qdict, "apic-id")) { int id = qdict_get_try_int(qdict, "apic-id", 0); + cs = cpu_by_arch_id(id); + if (cs) { + cpu_synchronize_state(cs); + } } else { cs = mon_get_cpu(mon); } diff --git a/target/i386/nvmm/meson.build b/target/i386/nvmm/meson.build index 733e334083..885a708665 100644 --- a/target/i386/nvmm/meson.build +++ b/target/i386/nvmm/meson.build @@ -1,8 +1,8 @@ -i386_softmmu_ss.add(when: 'CONFIG_NVMM', if_true: +i386_system_ss.add(when: 'CONFIG_NVMM', if_true: files( 'nvmm-all.c', 'nvmm-accel-ops.c', ) ) -i386_softmmu_ss.add(when: 'CONFIG_NVMM', if_true: nvmm) +i386_system_ss.add(when: 'CONFIG_NVMM', if_true: nvmm) diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index 6c46101ac1..6b2bfd9b9c 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -25,7 +25,7 @@ static void *qemu_nvmm_cpu_thread_fn(void *arg) rcu_register_thread(); - qemu_mutex_lock_iothread(); + bql_lock(); qemu_thread_get_self(cpu->thread); cpu->thread_id = qemu_get_thread_id(); current_cpu = cpu; @@ -48,14 +48,14 @@ static void *qemu_nvmm_cpu_thread_fn(void *arg) } } while (cpu_thread_is_idle(cpu)) { - qemu_cond_wait_iothread(cpu->halt_cond); + qemu_cond_wait_bql(cpu->halt_cond); } qemu_wait_io_event_common(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); nvmm_destroy_vcpu(cpu); cpu_thread_signal_destroyed(cpu); - qemu_mutex_unlock_iothread(); + bql_unlock(); rcu_unregister_thread(); return NULL; } diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index b75738ee9c..49a3a3b916 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -26,7 +26,7 @@ #include -struct qemu_vcpu { +struct AccelCPUState { struct nvmm_vcpu vcpu; uint8_t tpr; bool stop; @@ -49,12 +49,6 @@ struct qemu_machine { static bool nvmm_allowed; static struct qemu_machine qemu_mach; -static struct qemu_vcpu * -get_qemu_vcpu(CPUState *cpu) -{ - return (struct qemu_vcpu *)cpu->hax_vcpu; -} - static struct nvmm_machine * get_nvmm_mach(void) { @@ -84,9 +78,9 @@ nvmm_set_segment(struct nvmm_x64_state_seg *nseg, const SegmentCache *qseg) static void nvmm_set_registers(CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; + CPUX86State *env = cpu_env(cpu); struct nvmm_machine *mach = get_nvmm_mach(); - struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); + AccelCPUState *qcpu = cpu->accel; struct nvmm_vcpu *vcpu = &qcpu->vcpu; struct nvmm_x64_state *state = vcpu->state; uint64_t bitmap; @@ -221,9 +215,9 @@ nvmm_get_segment(SegmentCache *qseg, const struct nvmm_x64_state_seg *nseg) static void nvmm_get_registers(CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; + CPUX86State *env = cpu_env(cpu); struct nvmm_machine *mach = get_nvmm_mach(); - struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); + AccelCPUState *qcpu = cpu->accel; struct nvmm_vcpu *vcpu = &qcpu->vcpu; X86CPU *x86_cpu = X86_CPU(cpu); struct nvmm_x64_state *state = vcpu->state; @@ -346,8 +340,7 @@ nvmm_get_registers(CPUState *cpu) static bool nvmm_can_take_int(CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; - struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); + AccelCPUState *qcpu = cpu->accel; struct nvmm_vcpu *vcpu = &qcpu->vcpu; struct nvmm_machine *mach = get_nvmm_mach(); @@ -355,7 +348,7 @@ nvmm_can_take_int(CPUState *cpu) return false; } - if (qcpu->int_shadow || !(env->eflags & IF_MASK)) { + if (qcpu->int_shadow || !(cpu_env(cpu)->eflags & IF_MASK)) { struct nvmm_x64_state *state = vcpu->state; /* Exit on interrupt window. */ @@ -372,7 +365,7 @@ nvmm_can_take_int(CPUState *cpu) static bool nvmm_can_take_nmi(CPUState *cpu) { - struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); + AccelCPUState *qcpu = cpu->accel; /* * Contrary to INTs, NMIs always schedule an exit when they are @@ -393,9 +386,9 @@ nvmm_can_take_nmi(CPUState *cpu) static void nvmm_vcpu_pre_run(CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; + CPUX86State *env = cpu_env(cpu); struct nvmm_machine *mach = get_nvmm_mach(); - struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); + AccelCPUState *qcpu = cpu->accel; struct nvmm_vcpu *vcpu = &qcpu->vcpu; X86CPU *x86_cpu = X86_CPU(cpu); struct nvmm_x64_state *state = vcpu->state; @@ -405,7 +398,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) uint8_t tpr; int ret; - qemu_mutex_lock_iothread(); + bql_lock(); tpr = cpu_get_apic_tpr(x86_cpu->apic_state); if (tpr != qcpu->tpr) { @@ -468,7 +461,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) } } - qemu_mutex_unlock_iothread(); + bql_unlock(); } /* @@ -478,9 +471,9 @@ nvmm_vcpu_pre_run(CPUState *cpu) static void nvmm_vcpu_post_run(CPUState *cpu, struct nvmm_vcpu_exit *exit) { - struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); - CPUX86State *env = cpu->env_ptr; + AccelCPUState *qcpu = cpu->accel; X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; uint64_t tpr; env->eflags = exit->exitstate.rflags; @@ -491,9 +484,9 @@ nvmm_vcpu_post_run(CPUState *cpu, struct nvmm_vcpu_exit *exit) tpr = exit->exitstate.cr8; if (qcpu->tpr != tpr) { qcpu->tpr = tpr; - qemu_mutex_lock_iothread(); + bql_lock(); cpu_set_apic_tpr(x86_cpu->apic_state, qcpu->tpr); - qemu_mutex_unlock_iothread(); + bql_unlock(); } } @@ -565,7 +558,7 @@ static int nvmm_handle_rdmsr(struct nvmm_machine *mach, CPUState *cpu, struct nvmm_vcpu_exit *exit) { - struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); + AccelCPUState *qcpu = cpu->accel; struct nvmm_vcpu *vcpu = &qcpu->vcpu; X86CPU *x86_cpu = X86_CPU(cpu); struct nvmm_x64_state *state = vcpu->state; @@ -610,7 +603,7 @@ static int nvmm_handle_wrmsr(struct nvmm_machine *mach, CPUState *cpu, struct nvmm_vcpu_exit *exit) { - struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); + AccelCPUState *qcpu = cpu->accel; struct nvmm_vcpu *vcpu = &qcpu->vcpu; X86CPU *x86_cpu = X86_CPU(cpu); struct nvmm_x64_state *state = vcpu->state; @@ -651,20 +644,19 @@ static int nvmm_handle_halted(struct nvmm_machine *mach, CPUState *cpu, struct nvmm_vcpu_exit *exit) { - CPUX86State *env = cpu->env_ptr; int ret = 0; - qemu_mutex_lock_iothread(); + bql_lock(); if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) && - (env->eflags & IF_MASK)) && + (cpu_env(cpu)->eflags & IF_MASK)) && !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) { cpu->exception_index = EXCP_HLT; cpu->halted = true; ret = 1; } - qemu_mutex_unlock_iothread(); + bql_unlock(); return ret; } @@ -684,11 +676,11 @@ nvmm_inject_ud(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) static int nvmm_vcpu_loop(CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; struct nvmm_machine *mach = get_nvmm_mach(); - struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); + AccelCPUState *qcpu = cpu->accel; struct nvmm_vcpu *vcpu = &qcpu->vcpu; X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; struct nvmm_vcpu_exit *exit = vcpu->exit; int ret; @@ -727,7 +719,7 @@ nvmm_vcpu_loop(CPUState *cpu) return 0; } - qemu_mutex_unlock_iothread(); + bql_unlock(); cpu_exec_start(cpu); /* @@ -812,16 +804,16 @@ nvmm_vcpu_loop(CPUState *cpu) error_report("NVMM: Unexpected VM exit code 0x%lx [hw=0x%lx]", exit->reason, exit->u.inv.hwcode); nvmm_get_registers(cpu); - qemu_mutex_lock_iothread(); + bql_lock(); qemu_system_guest_panicked(cpu_get_crash_info(cpu)); - qemu_mutex_unlock_iothread(); + bql_unlock(); ret = -1; break; } } while (ret == 0); cpu_exec_end(cpu); - qemu_mutex_lock_iothread(); + bql_lock(); qatomic_set(&cpu->exit_request, false); @@ -892,7 +884,7 @@ static void nvmm_ipi_signal(int sigcpu) { if (current_cpu) { - struct qemu_vcpu *qcpu = get_qemu_vcpu(current_cpu); + AccelCPUState *qcpu = current_cpu->accel; #if NVMM_USER_VERSION >= 2 struct nvmm_vcpu *vcpu = &qcpu->vcpu; nvmm_vcpu_stop(vcpu); @@ -926,7 +918,7 @@ nvmm_init_vcpu(CPUState *cpu) struct nvmm_vcpu_conf_cpuid cpuid; struct nvmm_vcpu_conf_tpr tpr; Error *local_error = NULL; - struct qemu_vcpu *qcpu; + AccelCPUState *qcpu; int ret, err; nvmm_init_cpu_signals(); @@ -935,18 +927,13 @@ nvmm_init_vcpu(CPUState *cpu) error_setg(&nvmm_migration_blocker, "NVMM: Migration not supported"); - if (migrate_add_blocker(nvmm_migration_blocker, &local_error) < 0) { + if (migrate_add_blocker(&nvmm_migration_blocker, &local_error) < 0) { error_report_err(local_error); - error_free(nvmm_migration_blocker); return -EINVAL; } } - qcpu = g_malloc0(sizeof(*qcpu)); - if (qcpu == NULL) { - error_report("NVMM: Failed to allocate VCPU context."); - return -ENOMEM; - } + qcpu = g_new0(AccelCPUState, 1); ret = nvmm_vcpu_create(mach, cpu->cpu_index, &qcpu->vcpu); if (ret == -1) { @@ -995,7 +982,7 @@ nvmm_init_vcpu(CPUState *cpu) } cpu->vcpu_dirty = true; - cpu->hax_vcpu = (struct hax_vcpu_state *)qcpu; + cpu->accel = qcpu; return 0; } @@ -1027,10 +1014,10 @@ void nvmm_destroy_vcpu(CPUState *cpu) { struct nvmm_machine *mach = get_nvmm_mach(); - struct qemu_vcpu *qcpu = get_qemu_vcpu(cpu); + AccelCPUState *qcpu = cpu->accel; nvmm_vcpu_destroy(mach, &qcpu->vcpu); - g_free(cpu->hax_vcpu); + g_free(cpu->accel); } /* -------------------------------------------------------------------------- */ @@ -1138,7 +1125,7 @@ static MemoryListener nvmm_memory_listener = { .region_add = nvmm_region_add, .region_del = nvmm_region_del, .log_sync = nvmm_log_sync, - .priority = 10, + .priority = MEMORY_LISTENER_PRIORITY_ACCEL, }; static void diff --git a/target/i386/ops_sse.h b/target/i386/ops_sse.h index 535440f882..6a465a35fd 100644 --- a/target/i386/ops_sse.h +++ b/target/i386/ops_sse.h @@ -19,10 +19,11 @@ */ #include "crypto/aes.h" +#include "crypto/aes-round.h" +#include "crypto/clmul.h" #if SHIFT == 0 #define Reg MMXReg -#define SIZE 8 #define XMM_ONLY(...) #define B(n) MMX_B(n) #define W(n) MMX_W(n) @@ -31,284 +32,211 @@ #define SUFFIX _mmx #else #define Reg ZMMReg -#define SIZE 16 #define XMM_ONLY(...) __VA_ARGS__ #define B(n) ZMM_B(n) #define W(n) ZMM_W(n) #define L(n) ZMM_L(n) #define Q(n) ZMM_Q(n) +#if SHIFT == 1 #define SUFFIX _xmm -#endif - -/* - * Copy the relevant parts of a Reg value around. In the case where - * sizeof(Reg) > SIZE, these helpers operate only on the lower bytes of - * a 64 byte ZMMReg, so we must copy only those and keep the top bytes - * untouched in the guest-visible destination destination register. - * Note that the "lower bytes" are placed last in memory on big-endian - * hosts, which store the vector backwards in memory. In that case the - * copy *starts* at B(SIZE - 1) and ends at B(0), the opposite of - * the little-endian case. - */ -#if HOST_BIG_ENDIAN -#define MOVE(d, r) memcpy(&((d).B(SIZE - 1)), &(r).B(SIZE - 1), SIZE) #else -#define MOVE(d, r) memcpy(&(d).B(0), &(r).B(0), SIZE) +#define SUFFIX _ymm +#endif #endif -void glue(helper_psrlw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +#define LANE_WIDTH (SHIFT ? 16 : 8) +#define PACK_WIDTH (LANE_WIDTH / 2) + +#if SHIFT == 0 +#define FPSRL(x, c) ((x) >> shift) +#define FPSRAW(x, c) ((int16_t)(x) >> shift) +#define FPSRAL(x, c) ((int32_t)(x) >> shift) +#define FPSLL(x, c) ((x) << shift) +#endif + +void glue(helper_psrlw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, Reg *c) { int shift; - - if (s->Q(0) > 15) { - d->Q(0) = 0; -#if SHIFT == 1 - d->Q(1) = 0; -#endif + if (c->Q(0) > 15) { + for (int i = 0; i < 1 << SHIFT; i++) { + d->Q(i) = 0; + } } else { - shift = s->B(0); - d->W(0) >>= shift; - d->W(1) >>= shift; - d->W(2) >>= shift; - d->W(3) >>= shift; -#if SHIFT == 1 - d->W(4) >>= shift; - d->W(5) >>= shift; - d->W(6) >>= shift; - d->W(7) >>= shift; -#endif + shift = c->B(0); + for (int i = 0; i < 4 << SHIFT; i++) { + d->W(i) = FPSRL(s->W(i), shift); + } } } -void glue(helper_psraw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_psllw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, Reg *c) { int shift; + if (c->Q(0) > 15) { + for (int i = 0; i < 1 << SHIFT; i++) { + d->Q(i) = 0; + } + } else { + shift = c->B(0); + for (int i = 0; i < 4 << SHIFT; i++) { + d->W(i) = FPSLL(s->W(i), shift); + } + } +} - if (s->Q(0) > 15) { +void glue(helper_psraw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, Reg *c) +{ + int shift; + if (c->Q(0) > 15) { shift = 15; } else { - shift = s->B(0); + shift = c->B(0); + } + for (int i = 0; i < 4 << SHIFT; i++) { + d->W(i) = FPSRAW(s->W(i), shift); } - d->W(0) = (int16_t)d->W(0) >> shift; - d->W(1) = (int16_t)d->W(1) >> shift; - d->W(2) = (int16_t)d->W(2) >> shift; - d->W(3) = (int16_t)d->W(3) >> shift; -#if SHIFT == 1 - d->W(4) = (int16_t)d->W(4) >> shift; - d->W(5) = (int16_t)d->W(5) >> shift; - d->W(6) = (int16_t)d->W(6) >> shift; - d->W(7) = (int16_t)d->W(7) >> shift; -#endif } -void glue(helper_psllw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_psrld, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, Reg *c) { int shift; - - if (s->Q(0) > 15) { - d->Q(0) = 0; -#if SHIFT == 1 - d->Q(1) = 0; -#endif + if (c->Q(0) > 31) { + for (int i = 0; i < 1 << SHIFT; i++) { + d->Q(i) = 0; + } } else { - shift = s->B(0); - d->W(0) <<= shift; - d->W(1) <<= shift; - d->W(2) <<= shift; - d->W(3) <<= shift; -#if SHIFT == 1 - d->W(4) <<= shift; - d->W(5) <<= shift; - d->W(6) <<= shift; - d->W(7) <<= shift; -#endif + shift = c->B(0); + for (int i = 0; i < 2 << SHIFT; i++) { + d->L(i) = FPSRL(s->L(i), shift); + } } } -void glue(helper_psrld, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_pslld, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, Reg *c) { int shift; - - if (s->Q(0) > 31) { - d->Q(0) = 0; -#if SHIFT == 1 - d->Q(1) = 0; -#endif + if (c->Q(0) > 31) { + for (int i = 0; i < 1 << SHIFT; i++) { + d->Q(i) = 0; + } } else { - shift = s->B(0); - d->L(0) >>= shift; - d->L(1) >>= shift; -#if SHIFT == 1 - d->L(2) >>= shift; - d->L(3) >>= shift; -#endif + shift = c->B(0); + for (int i = 0; i < 2 << SHIFT; i++) { + d->L(i) = FPSLL(s->L(i), shift); + } } } -void glue(helper_psrad, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_psrad, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, Reg *c) { int shift; - - if (s->Q(0) > 31) { + if (c->Q(0) > 31) { shift = 31; } else { - shift = s->B(0); + shift = c->B(0); + } + for (int i = 0; i < 2 << SHIFT; i++) { + d->L(i) = FPSRAL(s->L(i), shift); } - d->L(0) = (int32_t)d->L(0) >> shift; - d->L(1) = (int32_t)d->L(1) >> shift; -#if SHIFT == 1 - d->L(2) = (int32_t)d->L(2) >> shift; - d->L(3) = (int32_t)d->L(3) >> shift; -#endif } -void glue(helper_pslld, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_psrlq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, Reg *c) { int shift; - - if (s->Q(0) > 31) { - d->Q(0) = 0; -#if SHIFT == 1 - d->Q(1) = 0; -#endif + if (c->Q(0) > 63) { + for (int i = 0; i < 1 << SHIFT; i++) { + d->Q(i) = 0; + } } else { - shift = s->B(0); - d->L(0) <<= shift; - d->L(1) <<= shift; -#if SHIFT == 1 - d->L(2) <<= shift; - d->L(3) <<= shift; -#endif + shift = c->B(0); + for (int i = 0; i < 1 << SHIFT; i++) { + d->Q(i) = FPSRL(s->Q(i), shift); + } } } -void glue(helper_psrlq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_psllq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, Reg *c) { int shift; - - if (s->Q(0) > 63) { - d->Q(0) = 0; -#if SHIFT == 1 - d->Q(1) = 0; -#endif + if (c->Q(0) > 63) { + for (int i = 0; i < 1 << SHIFT; i++) { + d->Q(i) = 0; + } } else { - shift = s->B(0); - d->Q(0) >>= shift; -#if SHIFT == 1 - d->Q(1) >>= shift; -#endif + shift = c->B(0); + for (int i = 0; i < 1 << SHIFT; i++) { + d->Q(i) = FPSLL(s->Q(i), shift); + } } } -void glue(helper_psllq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +#if SHIFT >= 1 +void glue(helper_psrldq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, Reg *c) { - int shift; + int shift, i, j; - if (s->Q(0) > 63) { - d->Q(0) = 0; -#if SHIFT == 1 - d->Q(1) = 0; -#endif - } else { - shift = s->B(0); - d->Q(0) <<= shift; -#if SHIFT == 1 - d->Q(1) <<= shift; -#endif - } -} - -#if SHIFT == 1 -void glue(helper_psrldq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) -{ - int shift, i; - - shift = s->L(0); + shift = c->L(0); if (shift > 16) { shift = 16; } - for (i = 0; i < 16 - shift; i++) { - d->B(i) = d->B(i + shift); - } - for (i = 16 - shift; i < 16; i++) { - d->B(i) = 0; + for (j = 0; j < 8 << SHIFT; j += LANE_WIDTH) { + for (i = 0; i < 16 - shift; i++) { + d->B(j + i) = s->B(j + i + shift); + } + for (i = 16 - shift; i < 16; i++) { + d->B(j + i) = 0; + } } } -void glue(helper_pslldq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_pslldq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, Reg *c) { - int shift, i; + int shift, i, j; - shift = s->L(0); + shift = c->L(0); if (shift > 16) { shift = 16; } - for (i = 15; i >= shift; i--) { - d->B(i) = d->B(i - shift); - } - for (i = 0; i < shift; i++) { - d->B(i) = 0; + for (j = 0; j < 8 << SHIFT; j += LANE_WIDTH) { + for (i = 15; i >= shift; i--) { + d->B(j + i) = s->B(j + i - shift); + } + for (i = 0; i < shift; i++) { + d->B(j + i) = 0; + } } } #endif +#define SSE_HELPER_1(name, elem, num, F) \ + void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) \ + { \ + int n = num; \ + for (int i = 0; i < n; i++) { \ + d->elem(i) = F(s->elem(i)); \ + } \ + } + +#define SSE_HELPER_2(name, elem, num, F) \ + void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) \ + { \ + int n = num; \ + for (int i = 0; i < n; i++) { \ + d->elem(i) = F(v->elem(i), s->elem(i)); \ + } \ + } + #define SSE_HELPER_B(name, F) \ - void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) \ - { \ - d->B(0) = F(d->B(0), s->B(0)); \ - d->B(1) = F(d->B(1), s->B(1)); \ - d->B(2) = F(d->B(2), s->B(2)); \ - d->B(3) = F(d->B(3), s->B(3)); \ - d->B(4) = F(d->B(4), s->B(4)); \ - d->B(5) = F(d->B(5), s->B(5)); \ - d->B(6) = F(d->B(6), s->B(6)); \ - d->B(7) = F(d->B(7), s->B(7)); \ - XMM_ONLY( \ - d->B(8) = F(d->B(8), s->B(8)); \ - d->B(9) = F(d->B(9), s->B(9)); \ - d->B(10) = F(d->B(10), s->B(10)); \ - d->B(11) = F(d->B(11), s->B(11)); \ - d->B(12) = F(d->B(12), s->B(12)); \ - d->B(13) = F(d->B(13), s->B(13)); \ - d->B(14) = F(d->B(14), s->B(14)); \ - d->B(15) = F(d->B(15), s->B(15)); \ - ) \ - } + SSE_HELPER_2(name, B, 8 << SHIFT, F) #define SSE_HELPER_W(name, F) \ - void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) \ - { \ - d->W(0) = F(d->W(0), s->W(0)); \ - d->W(1) = F(d->W(1), s->W(1)); \ - d->W(2) = F(d->W(2), s->W(2)); \ - d->W(3) = F(d->W(3), s->W(3)); \ - XMM_ONLY( \ - d->W(4) = F(d->W(4), s->W(4)); \ - d->W(5) = F(d->W(5), s->W(5)); \ - d->W(6) = F(d->W(6), s->W(6)); \ - d->W(7) = F(d->W(7), s->W(7)); \ - ) \ - } + SSE_HELPER_2(name, W, 4 << SHIFT, F) #define SSE_HELPER_L(name, F) \ - void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) \ - { \ - d->L(0) = F(d->L(0), s->L(0)); \ - d->L(1) = F(d->L(1), s->L(1)); \ - XMM_ONLY( \ - d->L(2) = F(d->L(2), s->L(2)); \ - d->L(3) = F(d->L(3), s->L(3)); \ - ) \ - } + SSE_HELPER_2(name, L, 2 << SHIFT, F) #define SSE_HELPER_Q(name, F) \ - void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) \ - { \ - d->Q(0) = F(d->Q(0), s->Q(0)); \ - XMM_ONLY( \ - d->Q(1) = F(d->Q(1), s->Q(1)); \ - ) \ - } + SSE_HELPER_2(name, Q, 1 << SHIFT, F) #if SHIFT == 0 static inline int satub(int x) @@ -371,17 +299,6 @@ static inline int satsw(int x) #define FMAXUB(a, b) ((a) > (b)) ? (a) : (b) #define FMAXSW(a, b) ((int16_t)(a) > (int16_t)(b)) ? (a) : (b) -#define FAND(a, b) ((a) & (b)) -#define FANDN(a, b) ((~(a)) & (b)) -#define FOR(a, b) ((a) | (b)) -#define FXOR(a, b) ((a) ^ (b)) - -#define FCMPGTB(a, b) ((int8_t)(a) > (int8_t)(b) ? -1 : 0) -#define FCMPGTW(a, b) ((int16_t)(a) > (int16_t)(b) ? -1 : 0) -#define FCMPGTL(a, b) ((int32_t)(a) > (int32_t)(b) ? -1 : 0) -#define FCMPEQ(a, b) ((a) == (b) ? -1 : 0) - -#define FMULLW(a, b) ((a) * (b)) #define FMULHRW(a, b) (((int16_t)(a) * (int16_t)(b) + 0x8000) >> 16) #define FMULHUW(a, b) ((a) * (b) >> 16) #define FMULHW(a, b) ((int16_t)(a) * (int16_t)(b) >> 16) @@ -389,70 +306,38 @@ static inline int satsw(int x) #define FAVG(a, b) (((a) + (b) + 1) >> 1) #endif -SSE_HELPER_B(helper_paddb, FADD) -SSE_HELPER_W(helper_paddw, FADD) -SSE_HELPER_L(helper_paddl, FADD) -SSE_HELPER_Q(helper_paddq, FADD) - -SSE_HELPER_B(helper_psubb, FSUB) -SSE_HELPER_W(helper_psubw, FSUB) -SSE_HELPER_L(helper_psubl, FSUB) -SSE_HELPER_Q(helper_psubq, FSUB) - -SSE_HELPER_B(helper_paddusb, FADDUB) -SSE_HELPER_B(helper_paddsb, FADDSB) -SSE_HELPER_B(helper_psubusb, FSUBUB) -SSE_HELPER_B(helper_psubsb, FSUBSB) - -SSE_HELPER_W(helper_paddusw, FADDUW) -SSE_HELPER_W(helper_paddsw, FADDSW) -SSE_HELPER_W(helper_psubusw, FSUBUW) -SSE_HELPER_W(helper_psubsw, FSUBSW) - -SSE_HELPER_B(helper_pminub, FMINUB) -SSE_HELPER_B(helper_pmaxub, FMAXUB) - -SSE_HELPER_W(helper_pminsw, FMINSW) -SSE_HELPER_W(helper_pmaxsw, FMAXSW) - -SSE_HELPER_Q(helper_pand, FAND) -SSE_HELPER_Q(helper_pandn, FANDN) -SSE_HELPER_Q(helper_por, FOR) -SSE_HELPER_Q(helper_pxor, FXOR) - -SSE_HELPER_B(helper_pcmpgtb, FCMPGTB) -SSE_HELPER_W(helper_pcmpgtw, FCMPGTW) -SSE_HELPER_L(helper_pcmpgtl, FCMPGTL) - -SSE_HELPER_B(helper_pcmpeqb, FCMPEQ) -SSE_HELPER_W(helper_pcmpeqw, FCMPEQ) -SSE_HELPER_L(helper_pcmpeql, FCMPEQ) - -SSE_HELPER_W(helper_pmullw, FMULLW) -#if SHIFT == 0 -SSE_HELPER_W(helper_pmulhrw, FMULHRW) -#endif SSE_HELPER_W(helper_pmulhuw, FMULHUW) SSE_HELPER_W(helper_pmulhw, FMULHW) +#if SHIFT == 0 +void glue(helper_pmulhrw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ + d->W(0) = FMULHRW(d->W(0), s->W(0)); + d->W(1) = FMULHRW(d->W(1), s->W(1)); + d->W(2) = FMULHRW(d->W(2), s->W(2)); + d->W(3) = FMULHRW(d->W(3), s->W(3)); +} +#endif + SSE_HELPER_B(helper_pavgb, FAVG) SSE_HELPER_W(helper_pavgw, FAVG) -void glue(helper_pmuludq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_pmuludq, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - d->Q(0) = (uint64_t)s->L(0) * (uint64_t)d->L(0); -#if SHIFT == 1 - d->Q(1) = (uint64_t)s->L(2) * (uint64_t)d->L(2); -#endif + int i; + + for (i = 0; i < (1 << SHIFT); i++) { + d->Q(i) = (uint64_t)s->L(i * 2) * (uint64_t)v->L(i * 2); + } } -void glue(helper_pmaddwd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_pmaddwd, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { int i; for (i = 0; i < (2 << SHIFT); i++) { - d->L(i) = (int16_t)s->W(2 * i) * (int16_t)d->W(2 * i) + - (int16_t)s->W(2 * i + 1) * (int16_t)d->W(2 * i + 1); + d->L(i) = (int16_t)s->W(2 * i) * (int16_t)v->W(2 * i) + + (int16_t)s->W(2 * i + 1) * (int16_t)v->W(2 * i + 1); } } @@ -466,34 +351,25 @@ static inline int abs1(int a) } } #endif -void glue(helper_psadbw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_psadbw, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - unsigned int val; + int i; - val = 0; - val += abs1(d->B(0) - s->B(0)); - val += abs1(d->B(1) - s->B(1)); - val += abs1(d->B(2) - s->B(2)); - val += abs1(d->B(3) - s->B(3)); - val += abs1(d->B(4) - s->B(4)); - val += abs1(d->B(5) - s->B(5)); - val += abs1(d->B(6) - s->B(6)); - val += abs1(d->B(7) - s->B(7)); - d->Q(0) = val; -#if SHIFT == 1 - val = 0; - val += abs1(d->B(8) - s->B(8)); - val += abs1(d->B(9) - s->B(9)); - val += abs1(d->B(10) - s->B(10)); - val += abs1(d->B(11) - s->B(11)); - val += abs1(d->B(12) - s->B(12)); - val += abs1(d->B(13) - s->B(13)); - val += abs1(d->B(14) - s->B(14)); - val += abs1(d->B(15) - s->B(15)); - d->Q(1) = val; -#endif + for (i = 0; i < (1 << SHIFT); i++) { + unsigned int val = 0; + val += abs1(v->B(8 * i + 0) - s->B(8 * i + 0)); + val += abs1(v->B(8 * i + 1) - s->B(8 * i + 1)); + val += abs1(v->B(8 * i + 2) - s->B(8 * i + 2)); + val += abs1(v->B(8 * i + 3) - s->B(8 * i + 3)); + val += abs1(v->B(8 * i + 4) - s->B(8 * i + 4)); + val += abs1(v->B(8 * i + 5) - s->B(8 * i + 5)); + val += abs1(v->B(8 * i + 6) - s->B(8 * i + 6)); + val += abs1(v->B(8 * i + 7) - s->B(8 * i + 7)); + d->Q(i) = val; + } } +#if SHIFT < 2 void glue(helper_maskmov, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, target_ulong a0) { @@ -505,128 +381,140 @@ void glue(helper_maskmov, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, } } } +#endif -void glue(helper_movl_mm_T0, SUFFIX)(Reg *d, uint32_t val) -{ - d->L(0) = val; - d->L(1) = 0; -#if SHIFT == 1 - d->Q(1) = 0; -#endif -} - -#ifdef TARGET_X86_64 -void glue(helper_movq_mm_T0, SUFFIX)(Reg *d, uint64_t val) -{ - d->Q(0) = val; -#if SHIFT == 1 - d->Q(1) = 0; -#endif -} -#endif +#define SHUFFLE4(F, a, b, offset) do { \ + r0 = a->F((order & 3) + offset); \ + r1 = a->F(((order >> 2) & 3) + offset); \ + r2 = b->F(((order >> 4) & 3) + offset); \ + r3 = b->F(((order >> 6) & 3) + offset); \ + d->F(offset) = r0; \ + d->F(offset + 1) = r1; \ + d->F(offset + 2) = r2; \ + d->F(offset + 3) = r3; \ + } while (0) #if SHIFT == 0 void glue(helper_pshufw, SUFFIX)(Reg *d, Reg *s, int order) { - Reg r; + uint16_t r0, r1, r2, r3; - r.W(0) = s->W(order & 3); - r.W(1) = s->W((order >> 2) & 3); - r.W(2) = s->W((order >> 4) & 3); - r.W(3) = s->W((order >> 6) & 3); - MOVE(*d, r); + SHUFFLE4(W, s, s, 0); } #else -void helper_shufps(Reg *d, Reg *s, int order) +void glue(helper_shufps, SUFFIX)(Reg *d, Reg *v, Reg *s, int order) { - Reg r; + uint32_t r0, r1, r2, r3; + int i; - r.L(0) = d->L(order & 3); - r.L(1) = d->L((order >> 2) & 3); - r.L(2) = s->L((order >> 4) & 3); - r.L(3) = s->L((order >> 6) & 3); - MOVE(*d, r); + for (i = 0; i < 2 << SHIFT; i += 4) { + SHUFFLE4(L, v, s, i); + } } -void helper_shufpd(Reg *d, Reg *s, int order) +void glue(helper_shufpd, SUFFIX)(Reg *d, Reg *v, Reg *s, int order) { - Reg r; + uint64_t r0, r1; + int i; - r.Q(0) = d->Q(order & 1); - r.Q(1) = s->Q((order >> 1) & 1); - MOVE(*d, r); + for (i = 0; i < 1 << SHIFT; i += 2) { + r0 = v->Q(((order & 1) & 1) + i); + r1 = s->Q(((order >> 1) & 1) + i); + d->Q(i) = r0; + d->Q(i + 1) = r1; + order >>= 2; + } } void glue(helper_pshufd, SUFFIX)(Reg *d, Reg *s, int order) { - Reg r; + uint32_t r0, r1, r2, r3; + int i; - r.L(0) = s->L(order & 3); - r.L(1) = s->L((order >> 2) & 3); - r.L(2) = s->L((order >> 4) & 3); - r.L(3) = s->L((order >> 6) & 3); - MOVE(*d, r); + for (i = 0; i < 2 << SHIFT; i += 4) { + SHUFFLE4(L, s, s, i); + } } void glue(helper_pshuflw, SUFFIX)(Reg *d, Reg *s, int order) { - Reg r; + uint16_t r0, r1, r2, r3; + int i, j; - r.W(0) = s->W(order & 3); - r.W(1) = s->W((order >> 2) & 3); - r.W(2) = s->W((order >> 4) & 3); - r.W(3) = s->W((order >> 6) & 3); - r.Q(1) = s->Q(1); - MOVE(*d, r); + for (i = 0, j = 1; j < 1 << SHIFT; i += 8, j += 2) { + SHUFFLE4(W, s, s, i); + d->Q(j) = s->Q(j); + } } void glue(helper_pshufhw, SUFFIX)(Reg *d, Reg *s, int order) { - Reg r; + uint16_t r0, r1, r2, r3; + int i, j; - r.Q(0) = s->Q(0); - r.W(4) = s->W(4 + (order & 3)); - r.W(5) = s->W(4 + ((order >> 2) & 3)); - r.W(6) = s->W(4 + ((order >> 4) & 3)); - r.W(7) = s->W(4 + ((order >> 6) & 3)); - MOVE(*d, r); + for (i = 4, j = 0; j < 1 << SHIFT; i += 8, j += 2) { + d->Q(j) = s->Q(j); + SHUFFLE4(W, s, s, i); + } } #endif -#if SHIFT == 1 +#if SHIFT >= 1 /* FPU ops */ /* XXX: not accurate */ -#define SSE_HELPER_S(name, F) \ - void helper_ ## name ## ps(CPUX86State *env, Reg *d, Reg *s) \ +#define SSE_HELPER_P(name, F) \ + void glue(helper_ ## name ## ps, SUFFIX)(CPUX86State *env, \ + Reg *d, Reg *v, Reg *s) \ { \ - d->ZMM_S(0) = F(32, d->ZMM_S(0), s->ZMM_S(0)); \ - d->ZMM_S(1) = F(32, d->ZMM_S(1), s->ZMM_S(1)); \ - d->ZMM_S(2) = F(32, d->ZMM_S(2), s->ZMM_S(2)); \ - d->ZMM_S(3) = F(32, d->ZMM_S(3), s->ZMM_S(3)); \ + int i; \ + for (i = 0; i < 2 << SHIFT; i++) { \ + d->ZMM_S(i) = F(32, v->ZMM_S(i), s->ZMM_S(i)); \ + } \ } \ \ - void helper_ ## name ## ss(CPUX86State *env, Reg *d, Reg *s) \ + void glue(helper_ ## name ## pd, SUFFIX)(CPUX86State *env, \ + Reg *d, Reg *v, Reg *s) \ { \ - d->ZMM_S(0) = F(32, d->ZMM_S(0), s->ZMM_S(0)); \ - } \ - \ - void helper_ ## name ## pd(CPUX86State *env, Reg *d, Reg *s) \ - { \ - d->ZMM_D(0) = F(64, d->ZMM_D(0), s->ZMM_D(0)); \ - d->ZMM_D(1) = F(64, d->ZMM_D(1), s->ZMM_D(1)); \ - } \ - \ - void helper_ ## name ## sd(CPUX86State *env, Reg *d, Reg *s) \ - { \ - d->ZMM_D(0) = F(64, d->ZMM_D(0), s->ZMM_D(0)); \ + int i; \ + for (i = 0; i < 1 << SHIFT; i++) { \ + d->ZMM_D(i) = F(64, v->ZMM_D(i), s->ZMM_D(i)); \ + } \ } +#if SHIFT == 1 + +#define SSE_HELPER_S(name, F) \ + SSE_HELPER_P(name, F) \ + \ + void helper_ ## name ## ss(CPUX86State *env, Reg *d, Reg *v, Reg *s)\ + { \ + int i; \ + d->ZMM_S(0) = F(32, v->ZMM_S(0), s->ZMM_S(0)); \ + for (i = 1; i < 2 << SHIFT; i++) { \ + d->ZMM_L(i) = v->ZMM_L(i); \ + } \ + } \ + \ + void helper_ ## name ## sd(CPUX86State *env, Reg *d, Reg *v, Reg *s)\ + { \ + int i; \ + d->ZMM_D(0) = F(64, v->ZMM_D(0), s->ZMM_D(0)); \ + for (i = 1; i < 1 << SHIFT; i++) { \ + d->ZMM_Q(i) = v->ZMM_Q(i); \ + } \ + } + +#else + +#define SSE_HELPER_S(name, F) SSE_HELPER_P(name, F) + +#endif + #define FPU_ADD(size, a, b) float ## size ## _add(a, b, &env->sse_status) #define FPU_SUB(size, a, b) float ## size ## _sub(a, b, &env->sse_status) #define FPU_MUL(size, a, b) float ## size ## _mul(a, b, &env->sse_status) #define FPU_DIV(size, a, b) float ## size ## _div(a, b, &env->sse_status) -#define FPU_SQRT(size, a, b) float ## size ## _sqrt(b, &env->sse_status) /* Note that the choice of comparison op here is important to get the * special cases right: for min and max Intel specifies that (-0,0), @@ -643,56 +531,131 @@ SSE_HELPER_S(mul, FPU_MUL) SSE_HELPER_S(div, FPU_DIV) SSE_HELPER_S(min, FPU_MIN) SSE_HELPER_S(max, FPU_MAX) -SSE_HELPER_S(sqrt, FPU_SQRT) +void glue(helper_sqrtps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ + int i; + for (i = 0; i < 2 << SHIFT; i++) { + d->ZMM_S(i) = float32_sqrt(s->ZMM_S(i), &env->sse_status); + } +} + +void glue(helper_sqrtpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ + int i; + for (i = 0; i < 1 << SHIFT; i++) { + d->ZMM_D(i) = float64_sqrt(s->ZMM_D(i), &env->sse_status); + } +} + +#if SHIFT == 1 +void helper_sqrtss(CPUX86State *env, Reg *d, Reg *v, Reg *s) +{ + int i; + d->ZMM_S(0) = float32_sqrt(s->ZMM_S(0), &env->sse_status); + for (i = 1; i < 2 << SHIFT; i++) { + d->ZMM_L(i) = v->ZMM_L(i); + } +} + +void helper_sqrtsd(CPUX86State *env, Reg *d, Reg *v, Reg *s) +{ + int i; + d->ZMM_D(0) = float64_sqrt(s->ZMM_D(0), &env->sse_status); + for (i = 1; i < 1 << SHIFT; i++) { + d->ZMM_Q(i) = v->ZMM_Q(i); + } +} +#endif /* float to float conversions */ -void helper_cvtps2pd(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_cvtps2pd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) { - float32 s0, s1; - - s0 = s->ZMM_S(0); - s1 = s->ZMM_S(1); - d->ZMM_D(0) = float32_to_float64(s0, &env->sse_status); - d->ZMM_D(1) = float32_to_float64(s1, &env->sse_status); + int i; + for (i = 1 << SHIFT; --i >= 0; ) { + d->ZMM_D(i) = float32_to_float64(s->ZMM_S(i), &env->sse_status); + } } -void helper_cvtpd2ps(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_cvtpd2ps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) { - d->ZMM_S(0) = float64_to_float32(s->ZMM_D(0), &env->sse_status); - d->ZMM_S(1) = float64_to_float32(s->ZMM_D(1), &env->sse_status); - d->Q(1) = 0; + int i; + for (i = 0; i < 1 << SHIFT; i++) { + d->ZMM_S(i) = float64_to_float32(s->ZMM_D(i), &env->sse_status); + } + for (i >>= 1; i < 1 << SHIFT; i++) { + d->Q(i) = 0; + } } -void helper_cvtss2sd(CPUX86State *env, Reg *d, Reg *s) +#if SHIFT >= 1 +void glue(helper_cvtph2ps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) { + int i; + + for (i = 2 << SHIFT; --i >= 0; ) { + d->ZMM_S(i) = float16_to_float32(s->ZMM_H(i), true, &env->sse_status); + } +} + +void glue(helper_cvtps2ph, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, int mode) +{ + int i; + FloatRoundMode prev_rounding_mode = env->sse_status.float_rounding_mode; + if (!(mode & (1 << 2))) { + set_x86_rounding_mode(mode & 3, &env->sse_status); + } + + for (i = 0; i < 2 << SHIFT; i++) { + d->ZMM_H(i) = float32_to_float16(s->ZMM_S(i), true, &env->sse_status); + } + for (i >>= 2; i < 1 << SHIFT; i++) { + d->Q(i) = 0; + } + + env->sse_status.float_rounding_mode = prev_rounding_mode; +} +#endif + +#if SHIFT == 1 +void helper_cvtss2sd(CPUX86State *env, Reg *d, Reg *v, Reg *s) +{ + int i; d->ZMM_D(0) = float32_to_float64(s->ZMM_S(0), &env->sse_status); + for (i = 1; i < 1 << SHIFT; i++) { + d->ZMM_Q(i) = v->ZMM_Q(i); + } } -void helper_cvtsd2ss(CPUX86State *env, Reg *d, Reg *s) +void helper_cvtsd2ss(CPUX86State *env, Reg *d, Reg *v, Reg *s) { + int i; d->ZMM_S(0) = float64_to_float32(s->ZMM_D(0), &env->sse_status); + for (i = 1; i < 2 << SHIFT; i++) { + d->ZMM_L(i) = v->ZMM_L(i); + } } +#endif /* integer to float */ -void helper_cvtdq2ps(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_cvtdq2ps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) { - d->ZMM_S(0) = int32_to_float32(s->ZMM_L(0), &env->sse_status); - d->ZMM_S(1) = int32_to_float32(s->ZMM_L(1), &env->sse_status); - d->ZMM_S(2) = int32_to_float32(s->ZMM_L(2), &env->sse_status); - d->ZMM_S(3) = int32_to_float32(s->ZMM_L(3), &env->sse_status); + int i; + for (i = 0; i < 2 << SHIFT; i++) { + d->ZMM_S(i) = int32_to_float32(s->ZMM_L(i), &env->sse_status); + } } -void helper_cvtdq2pd(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_cvtdq2pd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) { - int32_t l0, l1; - - l0 = (int32_t)s->ZMM_L(0); - l1 = (int32_t)s->ZMM_L(1); - d->ZMM_D(0) = int32_to_float64(l0, &env->sse_status); - d->ZMM_D(1) = int32_to_float64(l1, &env->sse_status); + int i; + for (i = 1 << SHIFT; --i >= 0; ) { + int32_t l = s->ZMM_L(i); + d->ZMM_D(i) = int32_to_float64(l, &env->sse_status); + } } +#if SHIFT == 1 void helper_cvtpi2ps(CPUX86State *env, ZMMReg *d, MMXReg *s) { d->ZMM_S(0) = int32_to_float32(s->MMX_L(0), &env->sse_status); @@ -727,8 +690,11 @@ void helper_cvtsq2sd(CPUX86State *env, ZMMReg *d, uint64_t val) } #endif +#endif + /* float to integer */ +#if SHIFT == 1 /* * x86 mandates that we return the indefinite integer value for the result * of any float-to-integer conversion that raises the 'invalid' exception. @@ -759,22 +725,28 @@ WRAP_FLOATCONV(int64_t, float32_to_int64, float32, INT64_MIN) WRAP_FLOATCONV(int64_t, float32_to_int64_round_to_zero, float32, INT64_MIN) WRAP_FLOATCONV(int64_t, float64_to_int64, float64, INT64_MIN) WRAP_FLOATCONV(int64_t, float64_to_int64_round_to_zero, float64, INT64_MIN) +#endif -void helper_cvtps2dq(CPUX86State *env, ZMMReg *d, ZMMReg *s) +void glue(helper_cvtps2dq, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->ZMM_L(0) = x86_float32_to_int32(s->ZMM_S(0), &env->sse_status); - d->ZMM_L(1) = x86_float32_to_int32(s->ZMM_S(1), &env->sse_status); - d->ZMM_L(2) = x86_float32_to_int32(s->ZMM_S(2), &env->sse_status); - d->ZMM_L(3) = x86_float32_to_int32(s->ZMM_S(3), &env->sse_status); + int i; + for (i = 0; i < 2 << SHIFT; i++) { + d->ZMM_L(i) = x86_float32_to_int32(s->ZMM_S(i), &env->sse_status); + } } -void helper_cvtpd2dq(CPUX86State *env, ZMMReg *d, ZMMReg *s) +void glue(helper_cvtpd2dq, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->ZMM_L(0) = x86_float64_to_int32(s->ZMM_D(0), &env->sse_status); - d->ZMM_L(1) = x86_float64_to_int32(s->ZMM_D(1), &env->sse_status); - d->ZMM_Q(1) = 0; + int i; + for (i = 0; i < 1 << SHIFT; i++) { + d->ZMM_L(i) = x86_float64_to_int32(s->ZMM_D(i), &env->sse_status); + } + for (i >>= 1; i < 1 << SHIFT; i++) { + d->Q(i) = 0; + } } +#if SHIFT == 1 void helper_cvtps2pi(CPUX86State *env, MMXReg *d, ZMMReg *s) { d->MMX_L(0) = x86_float32_to_int32(s->ZMM_S(0), &env->sse_status); @@ -808,23 +780,31 @@ int64_t helper_cvtsd2sq(CPUX86State *env, ZMMReg *s) return x86_float64_to_int64(s->ZMM_D(0), &env->sse_status); } #endif +#endif /* float to integer truncated */ -void helper_cvttps2dq(CPUX86State *env, ZMMReg *d, ZMMReg *s) +void glue(helper_cvttps2dq, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->ZMM_L(0) = x86_float32_to_int32_round_to_zero(s->ZMM_S(0), &env->sse_status); - d->ZMM_L(1) = x86_float32_to_int32_round_to_zero(s->ZMM_S(1), &env->sse_status); - d->ZMM_L(2) = x86_float32_to_int32_round_to_zero(s->ZMM_S(2), &env->sse_status); - d->ZMM_L(3) = x86_float32_to_int32_round_to_zero(s->ZMM_S(3), &env->sse_status); + int i; + for (i = 0; i < 2 << SHIFT; i++) { + d->ZMM_L(i) = x86_float32_to_int32_round_to_zero(s->ZMM_S(i), + &env->sse_status); + } } -void helper_cvttpd2dq(CPUX86State *env, ZMMReg *d, ZMMReg *s) +void glue(helper_cvttpd2dq, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->ZMM_L(0) = x86_float64_to_int32_round_to_zero(s->ZMM_D(0), &env->sse_status); - d->ZMM_L(1) = x86_float64_to_int32_round_to_zero(s->ZMM_D(1), &env->sse_status); - d->ZMM_Q(1) = 0; + int i; + for (i = 0; i < 1 << SHIFT; i++) { + d->ZMM_L(i) = x86_float64_to_int32_round_to_zero(s->ZMM_D(i), + &env->sse_status); + } + for (i >>= 1; i < 1 << SHIFT; i++) { + d->Q(i) = 0; + } } +#if SHIFT == 1 void helper_cvttps2pi(CPUX86State *env, MMXReg *d, ZMMReg *s) { d->MMX_L(0) = x86_float32_to_int32_round_to_zero(s->ZMM_S(0), &env->sse_status); @@ -858,51 +838,59 @@ int64_t helper_cvttsd2sq(CPUX86State *env, ZMMReg *s) return x86_float64_to_int64_round_to_zero(s->ZMM_D(0), &env->sse_status); } #endif +#endif -void helper_rsqrtps(CPUX86State *env, ZMMReg *d, ZMMReg *s) +void glue(helper_rsqrtps, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) { uint8_t old_flags = get_float_exception_flags(&env->sse_status); - d->ZMM_S(0) = float32_div(float32_one, - float32_sqrt(s->ZMM_S(0), &env->sse_status), - &env->sse_status); - d->ZMM_S(1) = float32_div(float32_one, - float32_sqrt(s->ZMM_S(1), &env->sse_status), - &env->sse_status); - d->ZMM_S(2) = float32_div(float32_one, - float32_sqrt(s->ZMM_S(2), &env->sse_status), - &env->sse_status); - d->ZMM_S(3) = float32_div(float32_one, - float32_sqrt(s->ZMM_S(3), &env->sse_status), - &env->sse_status); + int i; + for (i = 0; i < 2 << SHIFT; i++) { + d->ZMM_S(i) = float32_div(float32_one, + float32_sqrt(s->ZMM_S(i), &env->sse_status), + &env->sse_status); + } set_float_exception_flags(old_flags, &env->sse_status); } -void helper_rsqrtss(CPUX86State *env, ZMMReg *d, ZMMReg *s) +#if SHIFT == 1 +void helper_rsqrtss(CPUX86State *env, ZMMReg *d, ZMMReg *v, ZMMReg *s) { uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int i; d->ZMM_S(0) = float32_div(float32_one, float32_sqrt(s->ZMM_S(0), &env->sse_status), &env->sse_status); set_float_exception_flags(old_flags, &env->sse_status); + for (i = 1; i < 2 << SHIFT; i++) { + d->ZMM_L(i) = v->ZMM_L(i); + } } +#endif -void helper_rcpps(CPUX86State *env, ZMMReg *d, ZMMReg *s) +void glue(helper_rcpps, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) { uint8_t old_flags = get_float_exception_flags(&env->sse_status); - d->ZMM_S(0) = float32_div(float32_one, s->ZMM_S(0), &env->sse_status); - d->ZMM_S(1) = float32_div(float32_one, s->ZMM_S(1), &env->sse_status); - d->ZMM_S(2) = float32_div(float32_one, s->ZMM_S(2), &env->sse_status); - d->ZMM_S(3) = float32_div(float32_one, s->ZMM_S(3), &env->sse_status); + int i; + for (i = 0; i < 2 << SHIFT; i++) { + d->ZMM_S(i) = float32_div(float32_one, s->ZMM_S(i), &env->sse_status); + } set_float_exception_flags(old_flags, &env->sse_status); } -void helper_rcpss(CPUX86State *env, ZMMReg *d, ZMMReg *s) +#if SHIFT == 1 +void helper_rcpss(CPUX86State *env, ZMMReg *d, ZMMReg *v, ZMMReg *s) { uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int i; d->ZMM_S(0) = float32_div(float32_one, s->ZMM_S(0), &env->sse_status); + for (i = 1; i < 2 << SHIFT; i++) { + d->ZMM_L(i) = v->ZMM_L(i); + } set_float_exception_flags(old_flags, &env->sse_status); } +#endif +#if SHIFT == 1 static inline uint64_t helper_extrq(uint64_t src, int shift, int len) { uint64_t mask; @@ -917,7 +905,7 @@ static inline uint64_t helper_extrq(uint64_t src, int shift, int len) void helper_extrq_r(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->ZMM_Q(0) = helper_extrq(d->ZMM_Q(0), s->ZMM_B(1), s->ZMM_B(0)); + d->ZMM_Q(0) = helper_extrq(d->ZMM_Q(0), s->ZMM_B(1) & 63, s->ZMM_B(0) & 63); } void helper_extrq_i(CPUX86State *env, ZMMReg *d, int index, int length) @@ -925,7 +913,7 @@ void helper_extrq_i(CPUX86State *env, ZMMReg *d, int index, int length) d->ZMM_Q(0) = helper_extrq(d->ZMM_Q(0), index, length); } -static inline uint64_t helper_insertq(uint64_t src, int shift, int len) +static inline uint64_t helper_insertq(uint64_t dest, uint64_t src, int shift, int len) { uint64_t mask; @@ -934,125 +922,184 @@ static inline uint64_t helper_insertq(uint64_t src, int shift, int len) } else { mask = (1ULL << len) - 1; } - return (src & ~(mask << shift)) | ((src & mask) << shift); + return (dest & ~(mask << shift)) | ((src & mask) << shift); } void helper_insertq_r(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - d->ZMM_Q(0) = helper_insertq(s->ZMM_Q(0), s->ZMM_B(9), s->ZMM_B(8)); + d->ZMM_Q(0) = helper_insertq(d->ZMM_Q(0), s->ZMM_Q(0), s->ZMM_B(9) & 63, s->ZMM_B(8) & 63); } -void helper_insertq_i(CPUX86State *env, ZMMReg *d, int index, int length) +void helper_insertq_i(CPUX86State *env, ZMMReg *d, ZMMReg *s, int index, int length) { - d->ZMM_Q(0) = helper_insertq(d->ZMM_Q(0), index, length); + d->ZMM_Q(0) = helper_insertq(d->ZMM_Q(0), s->ZMM_Q(0), index, length); +} +#endif + +#define SSE_HELPER_HPS(name, F) \ +void glue(helper_ ## name, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) \ +{ \ + float32 r[2 << SHIFT]; \ + int i, j, k; \ + for (k = 0; k < 2 << SHIFT; k += LANE_WIDTH / 4) { \ + for (i = j = 0; j < 4; i++, j += 2) { \ + r[i + k] = F(v->ZMM_S(j + k), v->ZMM_S(j + k + 1), &env->sse_status); \ + } \ + for (j = 0; j < 4; i++, j += 2) { \ + r[i + k] = F(s->ZMM_S(j + k), s->ZMM_S(j + k + 1), &env->sse_status); \ + } \ + } \ + for (i = 0; i < 2 << SHIFT; i++) { \ + d->ZMM_S(i) = r[i]; \ + } \ } -void helper_haddps(CPUX86State *env, ZMMReg *d, ZMMReg *s) +SSE_HELPER_HPS(haddps, float32_add) +SSE_HELPER_HPS(hsubps, float32_sub) + +#define SSE_HELPER_HPD(name, F) \ +void glue(helper_ ## name, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) \ +{ \ + float64 r[1 << SHIFT]; \ + int i, j, k; \ + for (k = 0; k < 1 << SHIFT; k += LANE_WIDTH / 8) { \ + for (i = j = 0; j < 2; i++, j += 2) { \ + r[i + k] = F(v->ZMM_D(j + k), v->ZMM_D(j + k + 1), &env->sse_status); \ + } \ + for (j = 0; j < 2; i++, j += 2) { \ + r[i + k] = F(s->ZMM_D(j + k), s->ZMM_D(j + k + 1), &env->sse_status); \ + } \ + } \ + for (i = 0; i < 1 << SHIFT; i++) { \ + d->ZMM_D(i) = r[i]; \ + } \ +} + +SSE_HELPER_HPD(haddpd, float64_add) +SSE_HELPER_HPD(hsubpd, float64_sub) + +void glue(helper_addsubps, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - ZMMReg r; - - r.ZMM_S(0) = float32_add(d->ZMM_S(0), d->ZMM_S(1), &env->sse_status); - r.ZMM_S(1) = float32_add(d->ZMM_S(2), d->ZMM_S(3), &env->sse_status); - r.ZMM_S(2) = float32_add(s->ZMM_S(0), s->ZMM_S(1), &env->sse_status); - r.ZMM_S(3) = float32_add(s->ZMM_S(2), s->ZMM_S(3), &env->sse_status); - MOVE(*d, r); + int i; + for (i = 0; i < 2 << SHIFT; i += 2) { + d->ZMM_S(i) = float32_sub(v->ZMM_S(i), s->ZMM_S(i), &env->sse_status); + d->ZMM_S(i+1) = float32_add(v->ZMM_S(i+1), s->ZMM_S(i+1), &env->sse_status); + } } -void helper_haddpd(CPUX86State *env, ZMMReg *d, ZMMReg *s) +void glue(helper_addsubpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - ZMMReg r; - - r.ZMM_D(0) = float64_add(d->ZMM_D(0), d->ZMM_D(1), &env->sse_status); - r.ZMM_D(1) = float64_add(s->ZMM_D(0), s->ZMM_D(1), &env->sse_status); - MOVE(*d, r); + int i; + for (i = 0; i < 1 << SHIFT; i += 2) { + d->ZMM_D(i) = float64_sub(v->ZMM_D(i), s->ZMM_D(i), &env->sse_status); + d->ZMM_D(i+1) = float64_add(v->ZMM_D(i+1), s->ZMM_D(i+1), &env->sse_status); + } } -void helper_hsubps(CPUX86State *env, ZMMReg *d, ZMMReg *s) -{ - ZMMReg r; - - r.ZMM_S(0) = float32_sub(d->ZMM_S(0), d->ZMM_S(1), &env->sse_status); - r.ZMM_S(1) = float32_sub(d->ZMM_S(2), d->ZMM_S(3), &env->sse_status); - r.ZMM_S(2) = float32_sub(s->ZMM_S(0), s->ZMM_S(1), &env->sse_status); - r.ZMM_S(3) = float32_sub(s->ZMM_S(2), s->ZMM_S(3), &env->sse_status); - MOVE(*d, r); -} - -void helper_hsubpd(CPUX86State *env, ZMMReg *d, ZMMReg *s) -{ - ZMMReg r; - - r.ZMM_D(0) = float64_sub(d->ZMM_D(0), d->ZMM_D(1), &env->sse_status); - r.ZMM_D(1) = float64_sub(s->ZMM_D(0), s->ZMM_D(1), &env->sse_status); - MOVE(*d, r); -} - -void helper_addsubps(CPUX86State *env, ZMMReg *d, ZMMReg *s) -{ - d->ZMM_S(0) = float32_sub(d->ZMM_S(0), s->ZMM_S(0), &env->sse_status); - d->ZMM_S(1) = float32_add(d->ZMM_S(1), s->ZMM_S(1), &env->sse_status); - d->ZMM_S(2) = float32_sub(d->ZMM_S(2), s->ZMM_S(2), &env->sse_status); - d->ZMM_S(3) = float32_add(d->ZMM_S(3), s->ZMM_S(3), &env->sse_status); -} - -void helper_addsubpd(CPUX86State *env, ZMMReg *d, ZMMReg *s) -{ - d->ZMM_D(0) = float64_sub(d->ZMM_D(0), s->ZMM_D(0), &env->sse_status); - d->ZMM_D(1) = float64_add(d->ZMM_D(1), s->ZMM_D(1), &env->sse_status); -} - -/* XXX: unordered */ -#define SSE_HELPER_CMP(name, F) \ - void helper_ ## name ## ps(CPUX86State *env, Reg *d, Reg *s) \ +#define SSE_HELPER_CMP_P(name, F, C) \ + void glue(helper_ ## name ## ps, SUFFIX)(CPUX86State *env, \ + Reg *d, Reg *v, Reg *s) \ { \ - d->ZMM_L(0) = F(32, d->ZMM_S(0), s->ZMM_S(0)); \ - d->ZMM_L(1) = F(32, d->ZMM_S(1), s->ZMM_S(1)); \ - d->ZMM_L(2) = F(32, d->ZMM_S(2), s->ZMM_S(2)); \ - d->ZMM_L(3) = F(32, d->ZMM_S(3), s->ZMM_S(3)); \ + int i; \ + for (i = 0; i < 2 << SHIFT; i++) { \ + d->ZMM_L(i) = C(F(32, v->ZMM_S(i), s->ZMM_S(i))) ? -1 : 0; \ + } \ } \ \ - void helper_ ## name ## ss(CPUX86State *env, Reg *d, Reg *s) \ + void glue(helper_ ## name ## pd, SUFFIX)(CPUX86State *env, \ + Reg *d, Reg *v, Reg *s) \ { \ - d->ZMM_L(0) = F(32, d->ZMM_S(0), s->ZMM_S(0)); \ - } \ - \ - void helper_ ## name ## pd(CPUX86State *env, Reg *d, Reg *s) \ - { \ - d->ZMM_Q(0) = F(64, d->ZMM_D(0), s->ZMM_D(0)); \ - d->ZMM_Q(1) = F(64, d->ZMM_D(1), s->ZMM_D(1)); \ - } \ - \ - void helper_ ## name ## sd(CPUX86State *env, Reg *d, Reg *s) \ - { \ - d->ZMM_Q(0) = F(64, d->ZMM_D(0), s->ZMM_D(0)); \ + int i; \ + for (i = 0; i < 1 << SHIFT; i++) { \ + d->ZMM_Q(i) = C(F(64, v->ZMM_D(i), s->ZMM_D(i))) ? -1 : 0; \ + } \ } -#define FPU_CMPEQ(size, a, b) \ - (float ## size ## _eq_quiet(a, b, &env->sse_status) ? -1 : 0) -#define FPU_CMPLT(size, a, b) \ - (float ## size ## _lt(a, b, &env->sse_status) ? -1 : 0) -#define FPU_CMPLE(size, a, b) \ - (float ## size ## _le(a, b, &env->sse_status) ? -1 : 0) -#define FPU_CMPUNORD(size, a, b) \ - (float ## size ## _unordered_quiet(a, b, &env->sse_status) ? -1 : 0) -#define FPU_CMPNEQ(size, a, b) \ - (float ## size ## _eq_quiet(a, b, &env->sse_status) ? 0 : -1) -#define FPU_CMPNLT(size, a, b) \ - (float ## size ## _lt(a, b, &env->sse_status) ? 0 : -1) -#define FPU_CMPNLE(size, a, b) \ - (float ## size ## _le(a, b, &env->sse_status) ? 0 : -1) -#define FPU_CMPORD(size, a, b) \ - (float ## size ## _unordered_quiet(a, b, &env->sse_status) ? 0 : -1) +#if SHIFT == 1 +#define SSE_HELPER_CMP(name, F, C) \ + SSE_HELPER_CMP_P(name, F, C) \ + void helper_ ## name ## ss(CPUX86State *env, Reg *d, Reg *v, Reg *s) \ + { \ + int i; \ + d->ZMM_L(0) = C(F(32, v->ZMM_S(0), s->ZMM_S(0))) ? -1 : 0; \ + for (i = 1; i < 2 << SHIFT; i++) { \ + d->ZMM_L(i) = v->ZMM_L(i); \ + } \ + } \ + \ + void helper_ ## name ## sd(CPUX86State *env, Reg *d, Reg *v, Reg *s) \ + { \ + int i; \ + d->ZMM_Q(0) = C(F(64, v->ZMM_D(0), s->ZMM_D(0))) ? -1 : 0; \ + for (i = 1; i < 1 << SHIFT; i++) { \ + d->ZMM_Q(i) = v->ZMM_Q(i); \ + } \ + } -SSE_HELPER_CMP(cmpeq, FPU_CMPEQ) -SSE_HELPER_CMP(cmplt, FPU_CMPLT) -SSE_HELPER_CMP(cmple, FPU_CMPLE) -SSE_HELPER_CMP(cmpunord, FPU_CMPUNORD) -SSE_HELPER_CMP(cmpneq, FPU_CMPNEQ) -SSE_HELPER_CMP(cmpnlt, FPU_CMPNLT) -SSE_HELPER_CMP(cmpnle, FPU_CMPNLE) -SSE_HELPER_CMP(cmpord, FPU_CMPORD) +static inline bool FPU_EQU(FloatRelation x) +{ + return (x == float_relation_equal || x == float_relation_unordered); +} +static inline bool FPU_GE(FloatRelation x) +{ + return (x == float_relation_equal || x == float_relation_greater); +} +#define FPU_EQ(x) (x == float_relation_equal) +#define FPU_LT(x) (x == float_relation_less) +#define FPU_LE(x) (x <= float_relation_equal) +#define FPU_GT(x) (x == float_relation_greater) +#define FPU_UNORD(x) (x == float_relation_unordered) +/* We must make sure we evaluate the argument in case it is a signalling NAN */ +#define FPU_FALSE(x) (x == float_relation_equal && 0) +#define FPU_CMPQ(size, a, b) \ + float ## size ## _compare_quiet(a, b, &env->sse_status) +#define FPU_CMPS(size, a, b) \ + float ## size ## _compare(a, b, &env->sse_status) + +#else +#define SSE_HELPER_CMP(name, F, C) SSE_HELPER_CMP_P(name, F, C) +#endif + +SSE_HELPER_CMP(cmpeq, FPU_CMPQ, FPU_EQ) +SSE_HELPER_CMP(cmplt, FPU_CMPS, FPU_LT) +SSE_HELPER_CMP(cmple, FPU_CMPS, FPU_LE) +SSE_HELPER_CMP(cmpunord, FPU_CMPQ, FPU_UNORD) +SSE_HELPER_CMP(cmpneq, FPU_CMPQ, !FPU_EQ) +SSE_HELPER_CMP(cmpnlt, FPU_CMPS, !FPU_LT) +SSE_HELPER_CMP(cmpnle, FPU_CMPS, !FPU_LE) +SSE_HELPER_CMP(cmpord, FPU_CMPQ, !FPU_UNORD) + +SSE_HELPER_CMP(cmpequ, FPU_CMPQ, FPU_EQU) +SSE_HELPER_CMP(cmpnge, FPU_CMPS, !FPU_GE) +SSE_HELPER_CMP(cmpngt, FPU_CMPS, !FPU_GT) +SSE_HELPER_CMP(cmpfalse, FPU_CMPQ, FPU_FALSE) +SSE_HELPER_CMP(cmpnequ, FPU_CMPQ, !FPU_EQU) +SSE_HELPER_CMP(cmpge, FPU_CMPS, FPU_GE) +SSE_HELPER_CMP(cmpgt, FPU_CMPS, FPU_GT) +SSE_HELPER_CMP(cmptrue, FPU_CMPQ, !FPU_FALSE) + +SSE_HELPER_CMP(cmpeqs, FPU_CMPS, FPU_EQ) +SSE_HELPER_CMP(cmpltq, FPU_CMPQ, FPU_LT) +SSE_HELPER_CMP(cmpleq, FPU_CMPQ, FPU_LE) +SSE_HELPER_CMP(cmpunords, FPU_CMPS, FPU_UNORD) +SSE_HELPER_CMP(cmpneqq, FPU_CMPS, !FPU_EQ) +SSE_HELPER_CMP(cmpnltq, FPU_CMPQ, !FPU_LT) +SSE_HELPER_CMP(cmpnleq, FPU_CMPQ, !FPU_LE) +SSE_HELPER_CMP(cmpords, FPU_CMPS, !FPU_UNORD) + +SSE_HELPER_CMP(cmpequs, FPU_CMPS, FPU_EQU) +SSE_HELPER_CMP(cmpngeq, FPU_CMPQ, !FPU_GE) +SSE_HELPER_CMP(cmpngtq, FPU_CMPQ, !FPU_GT) +SSE_HELPER_CMP(cmpfalses, FPU_CMPS, FPU_FALSE) +SSE_HELPER_CMP(cmpnequs, FPU_CMPS, !FPU_EQU) +SSE_HELPER_CMP(cmpgeq, FPU_CMPQ, FPU_GE) +SSE_HELPER_CMP(cmpgtq, FPU_CMPQ, FPU_GT) +SSE_HELPER_CMP(cmptrues, FPU_CMPS, !FPU_FALSE) + +#undef SSE_HELPER_CMP + +#if SHIFT == 1 static const int comis_eflags[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C}; void helper_ucomiss(CPUX86State *env, Reg *d, Reg *s) @@ -1098,205 +1145,154 @@ void helper_comisd(CPUX86State *env, Reg *d, Reg *s) ret = float64_compare(d0, d1, &env->sse_status); CC_SRC = comis_eflags[ret + 1]; } +#endif -uint32_t helper_movmskps(CPUX86State *env, Reg *s) +uint32_t glue(helper_movmskps, SUFFIX)(CPUX86State *env, Reg *s) { - int b0, b1, b2, b3; + uint32_t mask; + int i; - b0 = s->ZMM_L(0) >> 31; - b1 = s->ZMM_L(1) >> 31; - b2 = s->ZMM_L(2) >> 31; - b3 = s->ZMM_L(3) >> 31; - return b0 | (b1 << 1) | (b2 << 2) | (b3 << 3); + mask = 0; + for (i = 0; i < 2 << SHIFT; i++) { + mask |= (s->ZMM_L(i) >> (31 - i)) & (1 << i); + } + return mask; } -uint32_t helper_movmskpd(CPUX86State *env, Reg *s) +uint32_t glue(helper_movmskpd, SUFFIX)(CPUX86State *env, Reg *s) { - int b0, b1; + uint32_t mask; + int i; - b0 = s->ZMM_L(1) >> 31; - b1 = s->ZMM_L(3) >> 31; - return b0 | (b1 << 1); + mask = 0; + for (i = 0; i < 1 << SHIFT; i++) { + mask |= (s->ZMM_Q(i) >> (63 - i)) & (1 << i); + } + return mask; } #endif -uint32_t glue(helper_pmovmskb, SUFFIX)(CPUX86State *env, Reg *s) -{ - uint32_t val; - - val = 0; - val |= (s->B(0) >> 7); - val |= (s->B(1) >> 6) & 0x02; - val |= (s->B(2) >> 5) & 0x04; - val |= (s->B(3) >> 4) & 0x08; - val |= (s->B(4) >> 3) & 0x10; - val |= (s->B(5) >> 2) & 0x20; - val |= (s->B(6) >> 1) & 0x40; - val |= (s->B(7)) & 0x80; -#if SHIFT == 1 - val |= (s->B(8) << 1) & 0x0100; - val |= (s->B(9) << 2) & 0x0200; - val |= (s->B(10) << 3) & 0x0400; - val |= (s->B(11) << 4) & 0x0800; - val |= (s->B(12) << 5) & 0x1000; - val |= (s->B(13) << 6) & 0x2000; - val |= (s->B(14) << 7) & 0x4000; - val |= (s->B(15) << 8) & 0x8000; -#endif - return val; +#define PACK_HELPER_B(name, F) \ +void glue(helper_pack ## name, SUFFIX)(CPUX86State *env, \ + Reg *d, Reg *v, Reg *s) \ +{ \ + uint8_t r[PACK_WIDTH * 2]; \ + int j, k; \ + for (j = 0; j < 4 << SHIFT; j += PACK_WIDTH) { \ + for (k = 0; k < PACK_WIDTH; k++) { \ + r[k] = F((int16_t)v->W(j + k)); \ + } \ + for (k = 0; k < PACK_WIDTH; k++) { \ + r[PACK_WIDTH + k] = F((int16_t)s->W(j + k)); \ + } \ + for (k = 0; k < PACK_WIDTH * 2; k++) { \ + d->B(2 * j + k) = r[k]; \ + } \ + } \ } -void glue(helper_packsswb, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +PACK_HELPER_B(sswb, satsb) +PACK_HELPER_B(uswb, satub) + +void glue(helper_packssdw, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - Reg r; + uint16_t r[PACK_WIDTH]; + int j, k; - r.B(0) = satsb((int16_t)d->W(0)); - r.B(1) = satsb((int16_t)d->W(1)); - r.B(2) = satsb((int16_t)d->W(2)); - r.B(3) = satsb((int16_t)d->W(3)); -#if SHIFT == 1 - r.B(4) = satsb((int16_t)d->W(4)); - r.B(5) = satsb((int16_t)d->W(5)); - r.B(6) = satsb((int16_t)d->W(6)); - r.B(7) = satsb((int16_t)d->W(7)); -#endif - r.B((4 << SHIFT) + 0) = satsb((int16_t)s->W(0)); - r.B((4 << SHIFT) + 1) = satsb((int16_t)s->W(1)); - r.B((4 << SHIFT) + 2) = satsb((int16_t)s->W(2)); - r.B((4 << SHIFT) + 3) = satsb((int16_t)s->W(3)); -#if SHIFT == 1 - r.B(12) = satsb((int16_t)s->W(4)); - r.B(13) = satsb((int16_t)s->W(5)); - r.B(14) = satsb((int16_t)s->W(6)); - r.B(15) = satsb((int16_t)s->W(7)); -#endif - MOVE(*d, r); -} - -void glue(helper_packuswb, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) -{ - Reg r; - - r.B(0) = satub((int16_t)d->W(0)); - r.B(1) = satub((int16_t)d->W(1)); - r.B(2) = satub((int16_t)d->W(2)); - r.B(3) = satub((int16_t)d->W(3)); -#if SHIFT == 1 - r.B(4) = satub((int16_t)d->W(4)); - r.B(5) = satub((int16_t)d->W(5)); - r.B(6) = satub((int16_t)d->W(6)); - r.B(7) = satub((int16_t)d->W(7)); -#endif - r.B((4 << SHIFT) + 0) = satub((int16_t)s->W(0)); - r.B((4 << SHIFT) + 1) = satub((int16_t)s->W(1)); - r.B((4 << SHIFT) + 2) = satub((int16_t)s->W(2)); - r.B((4 << SHIFT) + 3) = satub((int16_t)s->W(3)); -#if SHIFT == 1 - r.B(12) = satub((int16_t)s->W(4)); - r.B(13) = satub((int16_t)s->W(5)); - r.B(14) = satub((int16_t)s->W(6)); - r.B(15) = satub((int16_t)s->W(7)); -#endif - MOVE(*d, r); -} - -void glue(helper_packssdw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) -{ - Reg r; - - r.W(0) = satsw(d->L(0)); - r.W(1) = satsw(d->L(1)); -#if SHIFT == 1 - r.W(2) = satsw(d->L(2)); - r.W(3) = satsw(d->L(3)); -#endif - r.W((2 << SHIFT) + 0) = satsw(s->L(0)); - r.W((2 << SHIFT) + 1) = satsw(s->L(1)); -#if SHIFT == 1 - r.W(6) = satsw(s->L(2)); - r.W(7) = satsw(s->L(3)); -#endif - MOVE(*d, r); + for (j = 0; j < 2 << SHIFT; j += PACK_WIDTH / 2) { + for (k = 0; k < PACK_WIDTH / 2; k++) { + r[k] = satsw(v->L(j + k)); + } + for (k = 0; k < PACK_WIDTH / 2; k++) { + r[PACK_WIDTH / 2 + k] = satsw(s->L(j + k)); + } + for (k = 0; k < PACK_WIDTH; k++) { + d->W(2 * j + k) = r[k]; + } + } } #define UNPCK_OP(base_name, base) \ \ void glue(helper_punpck ## base_name ## bw, SUFFIX)(CPUX86State *env,\ - Reg *d, Reg *s) \ + Reg *d, Reg *v, Reg *s) \ { \ - Reg r; \ + uint8_t r[PACK_WIDTH * 2]; \ + int j, i; \ \ - r.B(0) = d->B((base << (SHIFT + 2)) + 0); \ - r.B(1) = s->B((base << (SHIFT + 2)) + 0); \ - r.B(2) = d->B((base << (SHIFT + 2)) + 1); \ - r.B(3) = s->B((base << (SHIFT + 2)) + 1); \ - r.B(4) = d->B((base << (SHIFT + 2)) + 2); \ - r.B(5) = s->B((base << (SHIFT + 2)) + 2); \ - r.B(6) = d->B((base << (SHIFT + 2)) + 3); \ - r.B(7) = s->B((base << (SHIFT + 2)) + 3); \ - XMM_ONLY( \ - r.B(8) = d->B((base << (SHIFT + 2)) + 4); \ - r.B(9) = s->B((base << (SHIFT + 2)) + 4); \ - r.B(10) = d->B((base << (SHIFT + 2)) + 5); \ - r.B(11) = s->B((base << (SHIFT + 2)) + 5); \ - r.B(12) = d->B((base << (SHIFT + 2)) + 6); \ - r.B(13) = s->B((base << (SHIFT + 2)) + 6); \ - r.B(14) = d->B((base << (SHIFT + 2)) + 7); \ - r.B(15) = s->B((base << (SHIFT + 2)) + 7); \ - ) \ - MOVE(*d, r); \ + for (j = 0; j < 8 << SHIFT; ) { \ + int k = j + base * PACK_WIDTH; \ + for (i = 0; i < PACK_WIDTH; i++) { \ + r[2 * i] = v->B(k + i); \ + r[2 * i + 1] = s->B(k + i); \ + } \ + for (i = 0; i < PACK_WIDTH * 2; i++, j++) { \ + d->B(j) = r[i]; \ + } \ + } \ } \ \ void glue(helper_punpck ## base_name ## wd, SUFFIX)(CPUX86State *env,\ - Reg *d, Reg *s) \ + Reg *d, Reg *v, Reg *s) \ { \ - Reg r; \ + uint16_t r[PACK_WIDTH]; \ + int j, i; \ \ - r.W(0) = d->W((base << (SHIFT + 1)) + 0); \ - r.W(1) = s->W((base << (SHIFT + 1)) + 0); \ - r.W(2) = d->W((base << (SHIFT + 1)) + 1); \ - r.W(3) = s->W((base << (SHIFT + 1)) + 1); \ - XMM_ONLY( \ - r.W(4) = d->W((base << (SHIFT + 1)) + 2); \ - r.W(5) = s->W((base << (SHIFT + 1)) + 2); \ - r.W(6) = d->W((base << (SHIFT + 1)) + 3); \ - r.W(7) = s->W((base << (SHIFT + 1)) + 3); \ - ) \ - MOVE(*d, r); \ + for (j = 0; j < 4 << SHIFT; ) { \ + int k = j + base * PACK_WIDTH / 2; \ + for (i = 0; i < PACK_WIDTH / 2; i++) { \ + r[2 * i] = v->W(k + i); \ + r[2 * i + 1] = s->W(k + i); \ + } \ + for (i = 0; i < PACK_WIDTH; i++, j++) { \ + d->W(j) = r[i]; \ + } \ + } \ } \ \ void glue(helper_punpck ## base_name ## dq, SUFFIX)(CPUX86State *env,\ - Reg *d, Reg *s) \ + Reg *d, Reg *v, Reg *s) \ { \ - Reg r; \ + uint32_t r[PACK_WIDTH / 2]; \ + int j, i; \ \ - r.L(0) = d->L((base << SHIFT) + 0); \ - r.L(1) = s->L((base << SHIFT) + 0); \ - XMM_ONLY( \ - r.L(2) = d->L((base << SHIFT) + 1); \ - r.L(3) = s->L((base << SHIFT) + 1); \ - ) \ - MOVE(*d, r); \ + for (j = 0; j < 2 << SHIFT; ) { \ + int k = j + base * PACK_WIDTH / 4; \ + for (i = 0; i < PACK_WIDTH / 4; i++) { \ + r[2 * i] = v->L(k + i); \ + r[2 * i + 1] = s->L(k + i); \ + } \ + for (i = 0; i < PACK_WIDTH / 2; i++, j++) { \ + d->L(j) = r[i]; \ + } \ + } \ } \ \ XMM_ONLY( \ - void glue(helper_punpck ## base_name ## qdq, SUFFIX)(CPUX86State \ - *env, \ - Reg *d, \ - Reg *s) \ + void glue(helper_punpck ## base_name ## qdq, SUFFIX)( \ + CPUX86State *env, Reg *d, Reg *v, Reg *s) \ { \ - Reg r; \ + uint64_t r[2]; \ + int i; \ \ - r.Q(0) = d->Q(base); \ - r.Q(1) = s->Q(base); \ - MOVE(*d, r); \ + for (i = 0; i < 1 << SHIFT; i += 2) { \ + r[0] = v->Q(base + i); \ + r[1] = s->Q(base + i); \ + d->Q(i) = r[0]; \ + d->Q(i + 1) = r[1]; \ + } \ } \ ) UNPCK_OP(l, 0) UNPCK_OP(h, 1) +#undef PACK_WIDTH +#undef PACK_HELPER_B +#undef UNPCK_OP + + /* 3DNow! float ops */ #if SHIFT == 0 void helper_pi2fd(CPUX86State *env, MMXReg *d, MMXReg *s) @@ -1327,11 +1323,11 @@ void helper_pf2iw(CPUX86State *env, MMXReg *d, MMXReg *s) void helper_pfacc(CPUX86State *env, MMXReg *d, MMXReg *s) { - MMXReg r; + float32 r; - r.MMX_S(0) = float32_add(d->MMX_S(0), d->MMX_S(1), &env->mmx_status); - r.MMX_S(1) = float32_add(s->MMX_S(0), s->MMX_S(1), &env->mmx_status); - MOVE(*d, r); + r = float32_add(d->MMX_S(0), d->MMX_S(1), &env->mmx_status); + d->MMX_S(1) = float32_add(s->MMX_S(0), s->MMX_S(1), &env->mmx_status); + d->MMX_S(0) = r; } void helper_pfadd(CPUX86State *env, MMXReg *d, MMXReg *s) @@ -1392,20 +1388,20 @@ void helper_pfmul(CPUX86State *env, MMXReg *d, MMXReg *s) void helper_pfnacc(CPUX86State *env, MMXReg *d, MMXReg *s) { - MMXReg r; + float32 r; - r.MMX_S(0) = float32_sub(d->MMX_S(0), d->MMX_S(1), &env->mmx_status); - r.MMX_S(1) = float32_sub(s->MMX_S(0), s->MMX_S(1), &env->mmx_status); - MOVE(*d, r); + r = float32_sub(d->MMX_S(0), d->MMX_S(1), &env->mmx_status); + d->MMX_S(1) = float32_sub(s->MMX_S(0), s->MMX_S(1), &env->mmx_status); + d->MMX_S(0) = r; } void helper_pfpnacc(CPUX86State *env, MMXReg *d, MMXReg *s) { - MMXReg r; + float32 r; - r.MMX_S(0) = float32_sub(d->MMX_S(0), d->MMX_S(1), &env->mmx_status); - r.MMX_S(1) = float32_add(s->MMX_S(0), s->MMX_S(1), &env->mmx_status); - MOVE(*d, r); + r = float32_sub(d->MMX_S(0), d->MMX_S(1), &env->mmx_status); + d->MMX_S(1) = float32_add(s->MMX_S(0), s->MMX_S(1), &env->mmx_status); + d->MMX_S(0) = r; } void helper_pfrcp(CPUX86State *env, MMXReg *d, MMXReg *s) @@ -1438,132 +1434,94 @@ void helper_pfsubr(CPUX86State *env, MMXReg *d, MMXReg *s) void helper_pswapd(CPUX86State *env, MMXReg *d, MMXReg *s) { - MMXReg r; + uint32_t r; - r.MMX_L(0) = s->MMX_L(1); - r.MMX_L(1) = s->MMX_L(0); - MOVE(*d, r); + r = s->MMX_L(0); + d->MMX_L(0) = s->MMX_L(1); + d->MMX_L(1) = r; } #endif /* SSSE3 op helpers */ -void glue(helper_pshufb, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_pshufb, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { int i; - Reg r; +#if SHIFT == 0 + uint8_t r[8]; - for (i = 0; i < (8 << SHIFT); i++) { - r.B(i) = (s->B(i) & 0x80) ? 0 : (d->B(s->B(i) & ((8 << SHIFT) - 1))); + for (i = 0; i < 8; i++) { + r[i] = (s->B(i) & 0x80) ? 0 : (v->B(s->B(i) & 7)); } + for (i = 0; i < 8; i++) { + d->B(i) = r[i]; + } +#else + uint8_t r[8 << SHIFT]; - MOVE(*d, r); -} - -void glue(helper_phaddw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) -{ - - Reg r; - - r.W(0) = (int16_t)d->W(0) + (int16_t)d->W(1); - r.W(1) = (int16_t)d->W(2) + (int16_t)d->W(3); - XMM_ONLY(r.W(2) = (int16_t)d->W(4) + (int16_t)d->W(5)); - XMM_ONLY(r.W(3) = (int16_t)d->W(6) + (int16_t)d->W(7)); - r.W((2 << SHIFT) + 0) = (int16_t)s->W(0) + (int16_t)s->W(1); - r.W((2 << SHIFT) + 1) = (int16_t)s->W(2) + (int16_t)s->W(3); - XMM_ONLY(r.W(6) = (int16_t)s->W(4) + (int16_t)s->W(5)); - XMM_ONLY(r.W(7) = (int16_t)s->W(6) + (int16_t)s->W(7)); - - MOVE(*d, r); -} - -void glue(helper_phaddd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) -{ - Reg r; - - r.L(0) = (int32_t)d->L(0) + (int32_t)d->L(1); - XMM_ONLY(r.L(1) = (int32_t)d->L(2) + (int32_t)d->L(3)); - r.L((1 << SHIFT) + 0) = (int32_t)s->L(0) + (int32_t)s->L(1); - XMM_ONLY(r.L(3) = (int32_t)s->L(2) + (int32_t)s->L(3)); - - MOVE(*d, r); -} - -void glue(helper_phaddsw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) -{ - Reg r; - - r.W(0) = satsw((int16_t)d->W(0) + (int16_t)d->W(1)); - r.W(1) = satsw((int16_t)d->W(2) + (int16_t)d->W(3)); - XMM_ONLY(r.W(2) = satsw((int16_t)d->W(4) + (int16_t)d->W(5))); - XMM_ONLY(r.W(3) = satsw((int16_t)d->W(6) + (int16_t)d->W(7))); - r.W((2 << SHIFT) + 0) = satsw((int16_t)s->W(0) + (int16_t)s->W(1)); - r.W((2 << SHIFT) + 1) = satsw((int16_t)s->W(2) + (int16_t)s->W(3)); - XMM_ONLY(r.W(6) = satsw((int16_t)s->W(4) + (int16_t)s->W(5))); - XMM_ONLY(r.W(7) = satsw((int16_t)s->W(6) + (int16_t)s->W(7))); - - MOVE(*d, r); -} - -void glue(helper_pmaddubsw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) -{ - d->W(0) = satsw((int8_t)s->B(0) * (uint8_t)d->B(0) + - (int8_t)s->B(1) * (uint8_t)d->B(1)); - d->W(1) = satsw((int8_t)s->B(2) * (uint8_t)d->B(2) + - (int8_t)s->B(3) * (uint8_t)d->B(3)); - d->W(2) = satsw((int8_t)s->B(4) * (uint8_t)d->B(4) + - (int8_t)s->B(5) * (uint8_t)d->B(5)); - d->W(3) = satsw((int8_t)s->B(6) * (uint8_t)d->B(6) + - (int8_t)s->B(7) * (uint8_t)d->B(7)); -#if SHIFT == 1 - d->W(4) = satsw((int8_t)s->B(8) * (uint8_t)d->B(8) + - (int8_t)s->B(9) * (uint8_t)d->B(9)); - d->W(5) = satsw((int8_t)s->B(10) * (uint8_t)d->B(10) + - (int8_t)s->B(11) * (uint8_t)d->B(11)); - d->W(6) = satsw((int8_t)s->B(12) * (uint8_t)d->B(12) + - (int8_t)s->B(13) * (uint8_t)d->B(13)); - d->W(7) = satsw((int8_t)s->B(14) * (uint8_t)d->B(14) + - (int8_t)s->B(15) * (uint8_t)d->B(15)); + for (i = 0; i < 8 << SHIFT; i++) { + int j = i & ~0xf; + r[i] = (s->B(i) & 0x80) ? 0 : v->B(j | (s->B(i) & 0xf)); + } + for (i = 0; i < 8 << SHIFT; i++) { + d->B(i) = r[i]; + } #endif } -void glue(helper_phsubw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) -{ - d->W(0) = (int16_t)d->W(0) - (int16_t)d->W(1); - d->W(1) = (int16_t)d->W(2) - (int16_t)d->W(3); - XMM_ONLY(d->W(2) = (int16_t)d->W(4) - (int16_t)d->W(5)); - XMM_ONLY(d->W(3) = (int16_t)d->W(6) - (int16_t)d->W(7)); - d->W((2 << SHIFT) + 0) = (int16_t)s->W(0) - (int16_t)s->W(1); - d->W((2 << SHIFT) + 1) = (int16_t)s->W(2) - (int16_t)s->W(3); - XMM_ONLY(d->W(6) = (int16_t)s->W(4) - (int16_t)s->W(5)); - XMM_ONLY(d->W(7) = (int16_t)s->W(6) - (int16_t)s->W(7)); +#define SSE_HELPER_HW(name, F) \ +void glue(helper_ ## name, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) \ +{ \ + uint16_t r[4 << SHIFT]; \ + int i, j, k; \ + for (k = 0; k < 4 << SHIFT; k += LANE_WIDTH / 2) { \ + for (i = j = 0; j < LANE_WIDTH / 2; i++, j += 2) { \ + r[i + k] = F(v->W(j + k), v->W(j + k + 1)); \ + } \ + for (j = 0; j < LANE_WIDTH / 2; i++, j += 2) { \ + r[i + k] = F(s->W(j + k), s->W(j + k + 1)); \ + } \ + } \ + for (i = 0; i < 4 << SHIFT; i++) { \ + d->W(i) = r[i]; \ + } \ } -void glue(helper_phsubd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) -{ - d->L(0) = (int32_t)d->L(0) - (int32_t)d->L(1); - XMM_ONLY(d->L(1) = (int32_t)d->L(2) - (int32_t)d->L(3)); - d->L((1 << SHIFT) + 0) = (int32_t)s->L(0) - (int32_t)s->L(1); - XMM_ONLY(d->L(3) = (int32_t)s->L(2) - (int32_t)s->L(3)); +#define SSE_HELPER_HL(name, F) \ +void glue(helper_ ## name, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) \ +{ \ + uint32_t r[2 << SHIFT]; \ + int i, j, k; \ + for (k = 0; k < 2 << SHIFT; k += LANE_WIDTH / 4) { \ + for (i = j = 0; j < LANE_WIDTH / 4; i++, j += 2) { \ + r[i + k] = F(v->L(j + k), v->L(j + k + 1)); \ + } \ + for (j = 0; j < LANE_WIDTH / 4; i++, j += 2) { \ + r[i + k] = F(s->L(j + k), s->L(j + k + 1)); \ + } \ + } \ + for (i = 0; i < 2 << SHIFT; i++) { \ + d->L(i) = r[i]; \ + } \ } -void glue(helper_phsubsw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) -{ - d->W(0) = satsw((int16_t)d->W(0) - (int16_t)d->W(1)); - d->W(1) = satsw((int16_t)d->W(2) - (int16_t)d->W(3)); - XMM_ONLY(d->W(2) = satsw((int16_t)d->W(4) - (int16_t)d->W(5))); - XMM_ONLY(d->W(3) = satsw((int16_t)d->W(6) - (int16_t)d->W(7))); - d->W((2 << SHIFT) + 0) = satsw((int16_t)s->W(0) - (int16_t)s->W(1)); - d->W((2 << SHIFT) + 1) = satsw((int16_t)s->W(2) - (int16_t)s->W(3)); - XMM_ONLY(d->W(6) = satsw((int16_t)s->W(4) - (int16_t)s->W(5))); - XMM_ONLY(d->W(7) = satsw((int16_t)s->W(6) - (int16_t)s->W(7))); -} +SSE_HELPER_HW(phaddw, FADD) +SSE_HELPER_HW(phsubw, FSUB) +SSE_HELPER_HW(phaddsw, FADDSW) +SSE_HELPER_HW(phsubsw, FSUBSW) +SSE_HELPER_HL(phaddd, FADD) +SSE_HELPER_HL(phsubd, FSUB) -#define FABSB(_, x) (x > INT8_MAX ? -(int8_t)x : x) -#define FABSW(_, x) (x > INT16_MAX ? -(int16_t)x : x) -#define FABSL(_, x) (x > INT32_MAX ? -(int32_t)x : x) -SSE_HELPER_B(helper_pabsb, FABSB) -SSE_HELPER_W(helper_pabsw, FABSW) -SSE_HELPER_L(helper_pabsd, FABSL) +#undef SSE_HELPER_HW +#undef SSE_HELPER_HL + +void glue(helper_pmaddubsw, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) +{ + int i; + for (i = 0; i < 4 << SHIFT; i++) { + d->W(i) = satsw((int8_t)s->B(i * 2) * (uint8_t)v->B(i * 2) + + (int8_t)s->B(i * 2 + 1) * (uint8_t)v->B(i * 2 + 1)); + } +} #define FMULHRSW(d, s) (((int16_t) d * (int16_t)s + 0x4000) >> 15) SSE_HELPER_W(helper_pmulhrsw, FMULHRSW) @@ -1575,186 +1533,146 @@ SSE_HELPER_B(helper_psignb, FSIGNB) SSE_HELPER_W(helper_psignw, FSIGNW) SSE_HELPER_L(helper_psignd, FSIGNL) -void glue(helper_palignr, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, - int32_t shift) +void glue(helper_palignr, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, + uint32_t imm) { - Reg r; + int i; /* XXX could be checked during translation */ - if (shift >= (16 << SHIFT)) { - r.Q(0) = 0; - XMM_ONLY(r.Q(1) = 0); + if (imm >= (SHIFT ? 32 : 16)) { + for (i = 0; i < (1 << SHIFT); i++) { + d->Q(i) = 0; + } } else { - shift <<= 3; + int shift = imm * 8; #define SHR(v, i) (i < 64 && i > -64 ? i > 0 ? v >> (i) : (v << -(i)) : 0) #if SHIFT == 0 - r.Q(0) = SHR(s->Q(0), shift - 0) | - SHR(d->Q(0), shift - 64); + d->Q(0) = SHR(s->Q(0), shift - 0) | + SHR(v->Q(0), shift - 64); #else - r.Q(0) = SHR(s->Q(0), shift - 0) | - SHR(s->Q(1), shift - 64) | - SHR(d->Q(0), shift - 128) | - SHR(d->Q(1), shift - 192); - r.Q(1) = SHR(s->Q(0), shift + 64) | - SHR(s->Q(1), shift - 0) | - SHR(d->Q(0), shift - 64) | - SHR(d->Q(1), shift - 128); + for (i = 0; i < (1 << SHIFT); i += 2) { + uint64_t r0, r1; + + r0 = SHR(s->Q(i), shift - 0) | + SHR(s->Q(i + 1), shift - 64) | + SHR(v->Q(i), shift - 128) | + SHR(v->Q(i + 1), shift - 192); + r1 = SHR(s->Q(i), shift + 64) | + SHR(s->Q(i + 1), shift - 0) | + SHR(v->Q(i), shift - 64) | + SHR(v->Q(i + 1), shift - 128); + d->Q(i) = r0; + d->Q(i + 1) = r1; + } #endif #undef SHR } - - MOVE(*d, r); } -#define XMM0 (env->xmm_regs[0]) +#if SHIFT >= 1 -#if SHIFT == 1 #define SSE_HELPER_V(name, elem, num, F) \ - void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) \ + void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, \ + Reg *m) \ { \ - d->elem(0) = F(d->elem(0), s->elem(0), XMM0.elem(0)); \ - d->elem(1) = F(d->elem(1), s->elem(1), XMM0.elem(1)); \ - if (num > 2) { \ - d->elem(2) = F(d->elem(2), s->elem(2), XMM0.elem(2)); \ - d->elem(3) = F(d->elem(3), s->elem(3), XMM0.elem(3)); \ - if (num > 4) { \ - d->elem(4) = F(d->elem(4), s->elem(4), XMM0.elem(4)); \ - d->elem(5) = F(d->elem(5), s->elem(5), XMM0.elem(5)); \ - d->elem(6) = F(d->elem(6), s->elem(6), XMM0.elem(6)); \ - d->elem(7) = F(d->elem(7), s->elem(7), XMM0.elem(7)); \ - if (num > 8) { \ - d->elem(8) = F(d->elem(8), s->elem(8), XMM0.elem(8)); \ - d->elem(9) = F(d->elem(9), s->elem(9), XMM0.elem(9)); \ - d->elem(10) = F(d->elem(10), s->elem(10), XMM0.elem(10)); \ - d->elem(11) = F(d->elem(11), s->elem(11), XMM0.elem(11)); \ - d->elem(12) = F(d->elem(12), s->elem(12), XMM0.elem(12)); \ - d->elem(13) = F(d->elem(13), s->elem(13), XMM0.elem(13)); \ - d->elem(14) = F(d->elem(14), s->elem(14), XMM0.elem(14)); \ - d->elem(15) = F(d->elem(15), s->elem(15), XMM0.elem(15)); \ - } \ - } \ + int i; \ + for (i = 0; i < num; i++) { \ + d->elem(i) = F(v->elem(i), s->elem(i), m->elem(i)); \ } \ } #define SSE_HELPER_I(name, elem, num, F) \ - void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t imm) \ + void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, \ + uint32_t imm) \ { \ - d->elem(0) = F(d->elem(0), s->elem(0), ((imm >> 0) & 1)); \ - d->elem(1) = F(d->elem(1), s->elem(1), ((imm >> 1) & 1)); \ - if (num > 2) { \ - d->elem(2) = F(d->elem(2), s->elem(2), ((imm >> 2) & 1)); \ - d->elem(3) = F(d->elem(3), s->elem(3), ((imm >> 3) & 1)); \ - if (num > 4) { \ - d->elem(4) = F(d->elem(4), s->elem(4), ((imm >> 4) & 1)); \ - d->elem(5) = F(d->elem(5), s->elem(5), ((imm >> 5) & 1)); \ - d->elem(6) = F(d->elem(6), s->elem(6), ((imm >> 6) & 1)); \ - d->elem(7) = F(d->elem(7), s->elem(7), ((imm >> 7) & 1)); \ - if (num > 8) { \ - d->elem(8) = F(d->elem(8), s->elem(8), ((imm >> 8) & 1)); \ - d->elem(9) = F(d->elem(9), s->elem(9), ((imm >> 9) & 1)); \ - d->elem(10) = F(d->elem(10), s->elem(10), \ - ((imm >> 10) & 1)); \ - d->elem(11) = F(d->elem(11), s->elem(11), \ - ((imm >> 11) & 1)); \ - d->elem(12) = F(d->elem(12), s->elem(12), \ - ((imm >> 12) & 1)); \ - d->elem(13) = F(d->elem(13), s->elem(13), \ - ((imm >> 13) & 1)); \ - d->elem(14) = F(d->elem(14), s->elem(14), \ - ((imm >> 14) & 1)); \ - d->elem(15) = F(d->elem(15), s->elem(15), \ - ((imm >> 15) & 1)); \ - } \ - } \ + int i; \ + for (i = 0; i < num; i++) { \ + int j = i & 7; \ + d->elem(i) = F(v->elem(i), s->elem(i), (imm >> j) & 1); \ } \ } /* SSE4.1 op helpers */ -#define FBLENDVB(d, s, m) ((m & 0x80) ? s : d) -#define FBLENDVPS(d, s, m) ((m & 0x80000000) ? s : d) -#define FBLENDVPD(d, s, m) ((m & 0x8000000000000000LL) ? s : d) -SSE_HELPER_V(helper_pblendvb, B, 16, FBLENDVB) -SSE_HELPER_V(helper_blendvps, L, 4, FBLENDVPS) -SSE_HELPER_V(helper_blendvpd, Q, 2, FBLENDVPD) +#define FBLENDVB(v, s, m) ((m & 0x80) ? s : v) +#define FBLENDVPS(v, s, m) ((m & 0x80000000) ? s : v) +#define FBLENDVPD(v, s, m) ((m & 0x8000000000000000LL) ? s : v) +SSE_HELPER_V(helper_pblendvb, B, 8 << SHIFT, FBLENDVB) +SSE_HELPER_V(helper_blendvps, L, 2 << SHIFT, FBLENDVPS) +SSE_HELPER_V(helper_blendvpd, Q, 1 << SHIFT, FBLENDVPD) void glue(helper_ptest, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) { - uint64_t zf = (s->Q(0) & d->Q(0)) | (s->Q(1) & d->Q(1)); - uint64_t cf = (s->Q(0) & ~d->Q(0)) | (s->Q(1) & ~d->Q(1)); + uint64_t zf = 0, cf = 0; + int i; + for (i = 0; i < 1 << SHIFT; i++) { + zf |= (s->Q(i) & d->Q(i)); + cf |= (s->Q(i) & ~d->Q(i)); + } CC_SRC = (zf ? 0 : CC_Z) | (cf ? 0 : CC_C); } -#define SSE_HELPER_F(name, elem, num, F) \ - void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) \ - { \ - if (num > 2) { \ - if (num > 4) { \ - d->elem(7) = F(7); \ - d->elem(6) = F(6); \ - d->elem(5) = F(5); \ - d->elem(4) = F(4); \ - } \ - d->elem(3) = F(3); \ - d->elem(2) = F(2); \ - } \ - d->elem(1) = F(1); \ - d->elem(0) = F(0); \ +#define FMOVSLDUP(i) s->L((i) & ~1) +#define FMOVSHDUP(i) s->L((i) | 1) +#define FMOVDLDUP(i) s->Q((i) & ~1) + +#define SSE_HELPER_F(name, elem, num, F) \ + void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) \ + { \ + int n = num; \ + for (int i = n; --i >= 0; ) { \ + d->elem(i) = F(i); \ + } \ } -SSE_HELPER_F(helper_pmovsxbw, W, 8, (int8_t) s->B) -SSE_HELPER_F(helper_pmovsxbd, L, 4, (int8_t) s->B) -SSE_HELPER_F(helper_pmovsxbq, Q, 2, (int8_t) s->B) -SSE_HELPER_F(helper_pmovsxwd, L, 4, (int16_t) s->W) -SSE_HELPER_F(helper_pmovsxwq, Q, 2, (int16_t) s->W) -SSE_HELPER_F(helper_pmovsxdq, Q, 2, (int32_t) s->L) -SSE_HELPER_F(helper_pmovzxbw, W, 8, s->B) -SSE_HELPER_F(helper_pmovzxbd, L, 4, s->B) -SSE_HELPER_F(helper_pmovzxbq, Q, 2, s->B) -SSE_HELPER_F(helper_pmovzxwd, L, 4, s->W) -SSE_HELPER_F(helper_pmovzxwq, Q, 2, s->W) -SSE_HELPER_F(helper_pmovzxdq, Q, 2, s->L) +#if SHIFT > 0 +SSE_HELPER_F(helper_pmovsxbw, W, 4 << SHIFT, (int8_t) s->B) +SSE_HELPER_F(helper_pmovsxbd, L, 2 << SHIFT, (int8_t) s->B) +SSE_HELPER_F(helper_pmovsxbq, Q, 1 << SHIFT, (int8_t) s->B) +SSE_HELPER_F(helper_pmovsxwd, L, 2 << SHIFT, (int16_t) s->W) +SSE_HELPER_F(helper_pmovsxwq, Q, 1 << SHIFT, (int16_t) s->W) +SSE_HELPER_F(helper_pmovsxdq, Q, 1 << SHIFT, (int32_t) s->L) +SSE_HELPER_F(helper_pmovzxbw, W, 4 << SHIFT, s->B) +SSE_HELPER_F(helper_pmovzxbd, L, 2 << SHIFT, s->B) +SSE_HELPER_F(helper_pmovzxbq, Q, 1 << SHIFT, s->B) +SSE_HELPER_F(helper_pmovzxwd, L, 2 << SHIFT, s->W) +SSE_HELPER_F(helper_pmovzxwq, Q, 1 << SHIFT, s->W) +SSE_HELPER_F(helper_pmovzxdq, Q, 1 << SHIFT, s->L) +SSE_HELPER_F(helper_pmovsldup, L, 2 << SHIFT, FMOVSLDUP) +SSE_HELPER_F(helper_pmovshdup, L, 2 << SHIFT, FMOVSHDUP) +SSE_HELPER_F(helper_pmovdldup, Q, 1 << SHIFT, FMOVDLDUP) +#endif -void glue(helper_pmuldq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_pmuldq, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - d->Q(0) = (int64_t)(int32_t) d->L(0) * (int32_t) s->L(0); - d->Q(1) = (int64_t)(int32_t) d->L(2) * (int32_t) s->L(2); + int i; + + for (i = 0; i < 1 << SHIFT; i++) { + d->Q(i) = (int64_t)(int32_t) v->L(2 * i) * (int32_t) s->L(2 * i); + } } -#define FCMPEQQ(d, s) (d == s ? -1 : 0) -SSE_HELPER_Q(helper_pcmpeqq, FCMPEQQ) - -void glue(helper_packusdw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_packusdw, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - Reg r; + uint16_t r[8]; + int i, j, k; - r.W(0) = satuw((int32_t) d->L(0)); - r.W(1) = satuw((int32_t) d->L(1)); - r.W(2) = satuw((int32_t) d->L(2)); - r.W(3) = satuw((int32_t) d->L(3)); - r.W(4) = satuw((int32_t) s->L(0)); - r.W(5) = satuw((int32_t) s->L(1)); - r.W(6) = satuw((int32_t) s->L(2)); - r.W(7) = satuw((int32_t) s->L(3)); - MOVE(*d, r); + for (i = 0, j = 0; i <= 2 << SHIFT; i += 8, j += 4) { + r[0] = satuw(v->L(j)); + r[1] = satuw(v->L(j + 1)); + r[2] = satuw(v->L(j + 2)); + r[3] = satuw(v->L(j + 3)); + r[4] = satuw(s->L(j)); + r[5] = satuw(s->L(j + 1)); + r[6] = satuw(s->L(j + 2)); + r[7] = satuw(s->L(j + 3)); + for (k = 0; k < 8; k++) { + d->W(i + k) = r[k]; + } + } } -#define FMINSB(d, s) MIN((int8_t)d, (int8_t)s) -#define FMINSD(d, s) MIN((int32_t)d, (int32_t)s) -#define FMAXSB(d, s) MAX((int8_t)d, (int8_t)s) -#define FMAXSD(d, s) MAX((int32_t)d, (int32_t)s) -SSE_HELPER_B(helper_pminsb, FMINSB) -SSE_HELPER_L(helper_pminsd, FMINSD) -SSE_HELPER_W(helper_pminuw, MIN) -SSE_HELPER_L(helper_pminud, MIN) -SSE_HELPER_B(helper_pmaxsb, FMAXSB) -SSE_HELPER_L(helper_pmaxsd, FMAXSD) -SSE_HELPER_W(helper_pmaxuw, MAX) -SSE_HELPER_L(helper_pmaxud, MAX) - -#define FMULLD(d, s) ((int32_t)d * (int32_t)s) -SSE_HELPER_L(helper_pmulld, FMULLD) - +#if SHIFT == 1 void glue(helper_phminposuw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) { int idx = 0; @@ -1786,35 +1704,23 @@ void glue(helper_phminposuw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) d->L(1) = 0; d->Q(1) = 0; } +#endif void glue(helper_roundps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t mode) { uint8_t old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; + int i; prev_rounding_mode = env->sse_status.float_rounding_mode; if (!(mode & (1 << 2))) { - switch (mode & 3) { - case 0: - set_float_rounding_mode(float_round_nearest_even, &env->sse_status); - break; - case 1: - set_float_rounding_mode(float_round_down, &env->sse_status); - break; - case 2: - set_float_rounding_mode(float_round_up, &env->sse_status); - break; - case 3: - set_float_rounding_mode(float_round_to_zero, &env->sse_status); - break; - } + set_x86_rounding_mode(mode & 3, &env->sse_status); } - d->ZMM_S(0) = float32_round_to_int(s->ZMM_S(0), &env->sse_status); - d->ZMM_S(1) = float32_round_to_int(s->ZMM_S(1), &env->sse_status); - d->ZMM_S(2) = float32_round_to_int(s->ZMM_S(2), &env->sse_status); - d->ZMM_S(3) = float32_round_to_int(s->ZMM_S(3), &env->sse_status); + for (i = 0; i < 2 << SHIFT; i++) { + d->ZMM_S(i) = float32_round_to_int(s->ZMM_S(i), &env->sse_status); + } if (mode & (1 << 3) && !(old_flags & float_flag_inexact)) { set_float_exception_flags(get_float_exception_flags(&env->sse_status) & @@ -1829,27 +1735,16 @@ void glue(helper_roundpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, { uint8_t old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; + int i; prev_rounding_mode = env->sse_status.float_rounding_mode; if (!(mode & (1 << 2))) { - switch (mode & 3) { - case 0: - set_float_rounding_mode(float_round_nearest_even, &env->sse_status); - break; - case 1: - set_float_rounding_mode(float_round_down, &env->sse_status); - break; - case 2: - set_float_rounding_mode(float_round_up, &env->sse_status); - break; - case 3: - set_float_rounding_mode(float_round_to_zero, &env->sse_status); - break; - } + set_x86_rounding_mode(mode & 3, &env->sse_status); } - d->ZMM_D(0) = float64_round_to_int(s->ZMM_D(0), &env->sse_status); - d->ZMM_D(1) = float64_round_to_int(s->ZMM_D(1), &env->sse_status); + for (i = 0; i < 1 << SHIFT; i++) { + d->ZMM_D(i) = float64_round_to_int(s->ZMM_D(i), &env->sse_status); + } if (mode & (1 << 3) && !(old_flags & float_flag_inexact)) { set_float_exception_flags(get_float_exception_flags(&env->sse_status) & @@ -1859,31 +1754,23 @@ void glue(helper_roundpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, env->sse_status.float_rounding_mode = prev_rounding_mode; } -void glue(helper_roundss, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +#if SHIFT == 1 +void glue(helper_roundss, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, uint32_t mode) { uint8_t old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; + int i; prev_rounding_mode = env->sse_status.float_rounding_mode; if (!(mode & (1 << 2))) { - switch (mode & 3) { - case 0: - set_float_rounding_mode(float_round_nearest_even, &env->sse_status); - break; - case 1: - set_float_rounding_mode(float_round_down, &env->sse_status); - break; - case 2: - set_float_rounding_mode(float_round_up, &env->sse_status); - break; - case 3: - set_float_rounding_mode(float_round_to_zero, &env->sse_status); - break; - } + set_x86_rounding_mode(mode & 3, &env->sse_status); } d->ZMM_S(0) = float32_round_to_int(s->ZMM_S(0), &env->sse_status); + for (i = 1; i < 2 << SHIFT; i++) { + d->ZMM_L(i) = v->ZMM_L(i); + } if (mode & (1 << 3) && !(old_flags & float_flag_inexact)) { set_float_exception_flags(get_float_exception_flags(&env->sse_status) & @@ -1893,31 +1780,22 @@ void glue(helper_roundss, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, env->sse_status.float_rounding_mode = prev_rounding_mode; } -void glue(helper_roundsd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +void glue(helper_roundsd, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, uint32_t mode) { uint8_t old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; + int i; prev_rounding_mode = env->sse_status.float_rounding_mode; if (!(mode & (1 << 2))) { - switch (mode & 3) { - case 0: - set_float_rounding_mode(float_round_nearest_even, &env->sse_status); - break; - case 1: - set_float_rounding_mode(float_round_down, &env->sse_status); - break; - case 2: - set_float_rounding_mode(float_round_up, &env->sse_status); - break; - case 3: - set_float_rounding_mode(float_round_to_zero, &env->sse_status); - break; - } + set_x86_rounding_mode(mode & 3, &env->sse_status); } d->ZMM_D(0) = float64_round_to_int(s->ZMM_D(0), &env->sse_status); + for (i = 1; i < 1 << SHIFT; i++) { + d->ZMM_Q(i) = v->ZMM_Q(i); + } if (mode & (1 << 3) && !(old_flags & float_flag_inexact)) { set_float_exception_flags(get_float_exception_flags(&env->sse_status) & @@ -1926,89 +1804,103 @@ void glue(helper_roundsd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, } env->sse_status.float_rounding_mode = prev_rounding_mode; } +#endif -#define FBLENDP(d, s, m) (m ? s : d) -SSE_HELPER_I(helper_blendps, L, 4, FBLENDP) -SSE_HELPER_I(helper_blendpd, Q, 2, FBLENDP) -SSE_HELPER_I(helper_pblendw, W, 8, FBLENDP) +#define FBLENDP(v, s, m) (m ? s : v) +SSE_HELPER_I(helper_blendps, L, 2 << SHIFT, FBLENDP) +SSE_HELPER_I(helper_blendpd, Q, 1 << SHIFT, FBLENDP) +SSE_HELPER_I(helper_pblendw, W, 4 << SHIFT, FBLENDP) -void glue(helper_dpps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t mask) +void glue(helper_dpps, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, + uint32_t mask) { - float32 iresult = float32_zero; + float32 prod1, prod2, temp2, temp3, temp4; + int i; - if (mask & (1 << 4)) { - iresult = float32_add(iresult, - float32_mul(d->ZMM_S(0), s->ZMM_S(0), - &env->sse_status), - &env->sse_status); + for (i = 0; i < 2 << SHIFT; i += 4) { + /* + * We must evaluate (A+B)+(C+D), not ((A+B)+C)+D + * to correctly round the intermediate results + */ + if (mask & (1 << 4)) { + prod1 = float32_mul(v->ZMM_S(i), s->ZMM_S(i), &env->sse_status); + } else { + prod1 = float32_zero; + } + if (mask & (1 << 5)) { + prod2 = float32_mul(v->ZMM_S(i+1), s->ZMM_S(i+1), &env->sse_status); + } else { + prod2 = float32_zero; + } + temp2 = float32_add(prod1, prod2, &env->sse_status); + if (mask & (1 << 6)) { + prod1 = float32_mul(v->ZMM_S(i+2), s->ZMM_S(i+2), &env->sse_status); + } else { + prod1 = float32_zero; + } + if (mask & (1 << 7)) { + prod2 = float32_mul(v->ZMM_S(i+3), s->ZMM_S(i+3), &env->sse_status); + } else { + prod2 = float32_zero; + } + temp3 = float32_add(prod1, prod2, &env->sse_status); + temp4 = float32_add(temp2, temp3, &env->sse_status); + + d->ZMM_S(i) = (mask & (1 << 0)) ? temp4 : float32_zero; + d->ZMM_S(i+1) = (mask & (1 << 1)) ? temp4 : float32_zero; + d->ZMM_S(i+2) = (mask & (1 << 2)) ? temp4 : float32_zero; + d->ZMM_S(i+3) = (mask & (1 << 3)) ? temp4 : float32_zero; } - if (mask & (1 << 5)) { - iresult = float32_add(iresult, - float32_mul(d->ZMM_S(1), s->ZMM_S(1), - &env->sse_status), - &env->sse_status); - } - if (mask & (1 << 6)) { - iresult = float32_add(iresult, - float32_mul(d->ZMM_S(2), s->ZMM_S(2), - &env->sse_status), - &env->sse_status); - } - if (mask & (1 << 7)) { - iresult = float32_add(iresult, - float32_mul(d->ZMM_S(3), s->ZMM_S(3), - &env->sse_status), - &env->sse_status); - } - d->ZMM_S(0) = (mask & (1 << 0)) ? iresult : float32_zero; - d->ZMM_S(1) = (mask & (1 << 1)) ? iresult : float32_zero; - d->ZMM_S(2) = (mask & (1 << 2)) ? iresult : float32_zero; - d->ZMM_S(3) = (mask & (1 << 3)) ? iresult : float32_zero; } -void glue(helper_dppd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t mask) +#if SHIFT == 1 +/* Oddly, there is no ymm version of dppd */ +void glue(helper_dppd, SUFFIX)(CPUX86State *env, + Reg *d, Reg *v, Reg *s, uint32_t mask) { - float64 iresult = float64_zero; + float64 prod1, prod2, temp2; if (mask & (1 << 4)) { - iresult = float64_add(iresult, - float64_mul(d->ZMM_D(0), s->ZMM_D(0), - &env->sse_status), - &env->sse_status); + prod1 = float64_mul(v->ZMM_D(0), s->ZMM_D(0), &env->sse_status); + } else { + prod1 = float64_zero; } if (mask & (1 << 5)) { - iresult = float64_add(iresult, - float64_mul(d->ZMM_D(1), s->ZMM_D(1), - &env->sse_status), - &env->sse_status); + prod2 = float64_mul(v->ZMM_D(1), s->ZMM_D(1), &env->sse_status); + } else { + prod2 = float64_zero; } - d->ZMM_D(0) = (mask & (1 << 0)) ? iresult : float64_zero; - d->ZMM_D(1) = (mask & (1 << 1)) ? iresult : float64_zero; + temp2 = float64_add(prod1, prod2, &env->sse_status); + d->ZMM_D(0) = (mask & (1 << 0)) ? temp2 : float64_zero; + d->ZMM_D(1) = (mask & (1 << 1)) ? temp2 : float64_zero; } +#endif -void glue(helper_mpsadbw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +void glue(helper_mpsadbw, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, uint32_t offset) { - int s0 = (offset & 3) << 2; - int d0 = (offset & 4) << 0; - int i; - Reg r; + int i, j; + uint16_t r[8]; - for (i = 0; i < 8; i++, d0++) { - r.W(i) = 0; - r.W(i) += abs1(d->B(d0 + 0) - s->B(s0 + 0)); - r.W(i) += abs1(d->B(d0 + 1) - s->B(s0 + 1)); - r.W(i) += abs1(d->B(d0 + 2) - s->B(s0 + 2)); - r.W(i) += abs1(d->B(d0 + 3) - s->B(s0 + 3)); + for (j = 0; j < 4 << SHIFT; ) { + int s0 = (j * 2) + ((offset & 3) << 2); + int d0 = (j * 2) + ((offset & 4) << 0); + for (i = 0; i < LANE_WIDTH / 2; i++, d0++) { + r[i] = 0; + r[i] += abs1(v->B(d0 + 0) - s->B(s0 + 0)); + r[i] += abs1(v->B(d0 + 1) - s->B(s0 + 1)); + r[i] += abs1(v->B(d0 + 2) - s->B(s0 + 2)); + r[i] += abs1(v->B(d0 + 3) - s->B(s0 + 3)); + } + for (i = 0; i < LANE_WIDTH / 2; i++, j++) { + d->W(j) = r[i]; + } + offset >>= 3; } - - MOVE(*d, r); } /* SSE4.2 op helpers */ -#define FCMPGTQ(d, s) ((int64_t)d > (int64_t)s ? -1 : 0) -SSE_HELPER_Q(helper_pcmpgtq, FCMPGTQ) - +#if SHIFT == 1 static inline int pcmp_elen(CPUX86State *env, int reg, uint32_t ctrl) { target_long val, limit; @@ -2063,7 +1955,7 @@ static inline int pcmp_val(Reg *r, uint8_t ctrl, int i) } static inline unsigned pcmpxstrx(CPUX86State *env, Reg *d, Reg *s, - int8_t ctrl, int valids, int validd) + uint8_t ctrl, int valids, int validd) { unsigned int res = 0; int v; @@ -2229,92 +2121,74 @@ target_ulong helper_crc32(uint32_t crc1, target_ulong msg, uint32_t len) return crc; } -void glue(helper_pclmulqdq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +#endif + +void glue(helper_pclmulqdq, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, uint32_t ctrl) { - uint64_t ah, al, b, resh, resl; + int a_idx = (ctrl & 1) != 0; + int b_idx = (ctrl & 16) != 0; - ah = 0; - al = d->Q((ctrl & 1) != 0); - b = s->Q((ctrl & 16) != 0); - resh = resl = 0; + for (int i = 0; i < SHIFT; i++) { + uint64_t a = v->Q(2 * i + a_idx); + uint64_t b = s->Q(2 * i + b_idx); + Int128 *r = (Int128 *)&d->ZMM_X(i); - while (b) { - if (b & 1) { - resl ^= al; - resh ^= ah; - } - ah = (ah << 1) | (al >> 63); - al <<= 1; - b >>= 1; + *r = clmul_64(a, b); } - - d->Q(0) = resl; - d->Q(1) = resh; } -void glue(helper_aesdec, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_aesdec, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - int i; - Reg st = *d; - Reg rk = *s; + for (int i = 0; i < SHIFT; i++) { + AESState *ad = (AESState *)&d->ZMM_X(i); + AESState *st = (AESState *)&v->ZMM_X(i); + AESState *rk = (AESState *)&s->ZMM_X(i); - for (i = 0 ; i < 4 ; i++) { - d->L(i) = rk.L(i) ^ bswap32(AES_Td0[st.B(AES_ishifts[4*i+0])] ^ - AES_Td1[st.B(AES_ishifts[4*i+1])] ^ - AES_Td2[st.B(AES_ishifts[4*i+2])] ^ - AES_Td3[st.B(AES_ishifts[4*i+3])]); + aesdec_ISB_ISR_IMC_AK(ad, st, rk, false); } } -void glue(helper_aesdeclast, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_aesdeclast, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - int i; - Reg st = *d; - Reg rk = *s; + for (int i = 0; i < SHIFT; i++) { + AESState *ad = (AESState *)&d->ZMM_X(i); + AESState *st = (AESState *)&v->ZMM_X(i); + AESState *rk = (AESState *)&s->ZMM_X(i); - for (i = 0; i < 16; i++) { - d->B(i) = rk.B(i) ^ (AES_isbox[st.B(AES_ishifts[i])]); + aesdec_ISB_ISR_AK(ad, st, rk, false); } } -void glue(helper_aesenc, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_aesenc, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - int i; - Reg st = *d; - Reg rk = *s; + for (int i = 0; i < SHIFT; i++) { + AESState *ad = (AESState *)&d->ZMM_X(i); + AESState *st = (AESState *)&v->ZMM_X(i); + AESState *rk = (AESState *)&s->ZMM_X(i); - for (i = 0 ; i < 4 ; i++) { - d->L(i) = rk.L(i) ^ bswap32(AES_Te0[st.B(AES_shifts[4*i+0])] ^ - AES_Te1[st.B(AES_shifts[4*i+1])] ^ - AES_Te2[st.B(AES_shifts[4*i+2])] ^ - AES_Te3[st.B(AES_shifts[4*i+3])]); + aesenc_SB_SR_MC_AK(ad, st, rk, false); } } -void glue(helper_aesenclast, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +void glue(helper_aesenclast, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - int i; - Reg st = *d; - Reg rk = *s; + for (int i = 0; i < SHIFT; i++) { + AESState *ad = (AESState *)&d->ZMM_X(i); + AESState *st = (AESState *)&v->ZMM_X(i); + AESState *rk = (AESState *)&s->ZMM_X(i); - for (i = 0; i < 16; i++) { - d->B(i) = rk.B(i) ^ (AES_sbox[st.B(AES_shifts[i])]); + aesenc_SB_SR_AK(ad, st, rk, false); } - } +#if SHIFT == 1 void glue(helper_aesimc, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) { - int i; - Reg tmp = *s; + AESState *ad = (AESState *)&d->ZMM_X(0); + AESState *st = (AESState *)&s->ZMM_X(0); - for (i = 0 ; i < 4 ; i++) { - d->L(i) = bswap32(AES_imc[tmp.B(4*i+0)][0] ^ - AES_imc[tmp.B(4*i+1)][1] ^ - AES_imc[tmp.B(4*i+2)][2] ^ - AES_imc[tmp.B(4*i+3)][3]); - } + aesdec_IMC(ad, st, false); } void glue(helper_aeskeygenassist, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, @@ -2331,7 +2205,459 @@ void glue(helper_aeskeygenassist, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, d->L(3) = (d->L(2) << 24 | d->L(2) >> 8) ^ ctrl; } #endif +#endif +#if SHIFT >= 1 +void glue(helper_vpermilpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) +{ + uint64_t r0, r1; + int i; + + for (i = 0; i < 1 << SHIFT; i += 2) { + r0 = v->Q(i + ((s->Q(i) >> 1) & 1)); + r1 = v->Q(i + ((s->Q(i+1) >> 1) & 1)); + d->Q(i) = r0; + d->Q(i+1) = r1; + } +} + +void glue(helper_vpermilps, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) +{ + uint32_t r0, r1, r2, r3; + int i; + + for (i = 0; i < 2 << SHIFT; i += 4) { + r0 = v->L(i + (s->L(i) & 3)); + r1 = v->L(i + (s->L(i+1) & 3)); + r2 = v->L(i + (s->L(i+2) & 3)); + r3 = v->L(i + (s->L(i+3) & 3)); + d->L(i) = r0; + d->L(i+1) = r1; + d->L(i+2) = r2; + d->L(i+3) = r3; + } +} + +void glue(helper_vpermilpd_imm, SUFFIX)(Reg *d, Reg *s, uint32_t order) +{ + uint64_t r0, r1; + int i; + + for (i = 0; i < 1 << SHIFT; i += 2) { + r0 = s->Q(i + ((order >> 0) & 1)); + r1 = s->Q(i + ((order >> 1) & 1)); + d->Q(i) = r0; + d->Q(i+1) = r1; + + order >>= 2; + } +} + +void glue(helper_vpermilps_imm, SUFFIX)(Reg *d, Reg *s, uint32_t order) +{ + uint32_t r0, r1, r2, r3; + int i; + + for (i = 0; i < 2 << SHIFT; i += 4) { + r0 = s->L(i + ((order >> 0) & 3)); + r1 = s->L(i + ((order >> 2) & 3)); + r2 = s->L(i + ((order >> 4) & 3)); + r3 = s->L(i + ((order >> 6) & 3)); + d->L(i) = r0; + d->L(i+1) = r1; + d->L(i+2) = r2; + d->L(i+3) = r3; + } +} + +#if SHIFT == 1 +#define FPSRLVD(x, c) (c < 32 ? ((x) >> c) : 0) +#define FPSRLVQ(x, c) (c < 64 ? ((x) >> c) : 0) +#define FPSRAVD(x, c) ((int32_t)(x) >> (c < 32 ? c : 31)) +#define FPSRAVQ(x, c) ((int64_t)(x) >> (c < 64 ? c : 63)) +#define FPSLLVD(x, c) (c < 32 ? ((x) << c) : 0) +#define FPSLLVQ(x, c) (c < 64 ? ((x) << c) : 0) +#endif + +SSE_HELPER_L(helper_vpsrlvd, FPSRLVD) +SSE_HELPER_L(helper_vpsravd, FPSRAVD) +SSE_HELPER_L(helper_vpsllvd, FPSLLVD) + +SSE_HELPER_Q(helper_vpsrlvq, FPSRLVQ) +SSE_HELPER_Q(helper_vpsravq, FPSRAVQ) +SSE_HELPER_Q(helper_vpsllvq, FPSLLVQ) + +void glue(helper_vtestps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ + uint32_t zf = 0, cf = 0; + int i; + + for (i = 0; i < 2 << SHIFT; i++) { + zf |= (s->L(i) & d->L(i)); + cf |= (s->L(i) & ~d->L(i)); + } + CC_SRC = ((zf >> 31) ? 0 : CC_Z) | ((cf >> 31) ? 0 : CC_C); +} + +void glue(helper_vtestpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ + uint64_t zf = 0, cf = 0; + int i; + + for (i = 0; i < 1 << SHIFT; i++) { + zf |= (s->Q(i) & d->Q(i)); + cf |= (s->Q(i) & ~d->Q(i)); + } + CC_SRC = ((zf >> 63) ? 0 : CC_Z) | ((cf >> 63) ? 0 : CC_C); +} + +void glue(helper_vpmaskmovd_st, SUFFIX)(CPUX86State *env, + Reg *v, Reg *s, target_ulong a0) +{ + int i; + + for (i = 0; i < (2 << SHIFT); i++) { + if (v->L(i) >> 31) { + cpu_stl_data_ra(env, a0 + i * 4, s->L(i), GETPC()); + } + } +} + +void glue(helper_vpmaskmovq_st, SUFFIX)(CPUX86State *env, + Reg *v, Reg *s, target_ulong a0) +{ + int i; + + for (i = 0; i < (1 << SHIFT); i++) { + if (v->Q(i) >> 63) { + cpu_stq_data_ra(env, a0 + i * 8, s->Q(i), GETPC()); + } + } +} + +void glue(helper_vpmaskmovd, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) +{ + int i; + + for (i = 0; i < (2 << SHIFT); i++) { + d->L(i) = (v->L(i) >> 31) ? s->L(i) : 0; + } +} + +void glue(helper_vpmaskmovq, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) +{ + int i; + + for (i = 0; i < (1 << SHIFT); i++) { + d->Q(i) = (v->Q(i) >> 63) ? s->Q(i) : 0; + } +} + +void glue(helper_vpgatherdd, SUFFIX)(CPUX86State *env, + Reg *d, Reg *v, Reg *s, target_ulong a0, unsigned scale) +{ + int i; + for (i = 0; i < (2 << SHIFT); i++) { + if (v->L(i) >> 31) { + target_ulong addr = a0 + + ((target_ulong)(int32_t)s->L(i) << scale); + d->L(i) = cpu_ldl_data_ra(env, addr, GETPC()); + } + v->L(i) = 0; + } +} + +void glue(helper_vpgatherdq, SUFFIX)(CPUX86State *env, + Reg *d, Reg *v, Reg *s, target_ulong a0, unsigned scale) +{ + int i; + for (i = 0; i < (1 << SHIFT); i++) { + if (v->Q(i) >> 63) { + target_ulong addr = a0 + + ((target_ulong)(int32_t)s->L(i) << scale); + d->Q(i) = cpu_ldq_data_ra(env, addr, GETPC()); + } + v->Q(i) = 0; + } +} + +void glue(helper_vpgatherqd, SUFFIX)(CPUX86State *env, + Reg *d, Reg *v, Reg *s, target_ulong a0, unsigned scale) +{ + int i; + for (i = 0; i < (1 << SHIFT); i++) { + if (v->L(i) >> 31) { + target_ulong addr = a0 + + ((target_ulong)(int64_t)s->Q(i) << scale); + d->L(i) = cpu_ldl_data_ra(env, addr, GETPC()); + } + v->L(i) = 0; + } + for (i /= 2; i < 1 << SHIFT; i++) { + d->Q(i) = 0; + v->Q(i) = 0; + } +} + +void glue(helper_vpgatherqq, SUFFIX)(CPUX86State *env, + Reg *d, Reg *v, Reg *s, target_ulong a0, unsigned scale) +{ + int i; + for (i = 0; i < (1 << SHIFT); i++) { + if (v->Q(i) >> 63) { + target_ulong addr = a0 + + ((target_ulong)(int64_t)s->Q(i) << scale); + d->Q(i) = cpu_ldq_data_ra(env, addr, GETPC()); + } + v->Q(i) = 0; + } +} +#endif + +#if SHIFT >= 2 +void helper_vpermdq_ymm(Reg *d, Reg *v, Reg *s, uint32_t order) +{ + uint64_t r0, r1, r2, r3; + + switch (order & 3) { + case 0: + r0 = v->Q(0); + r1 = v->Q(1); + break; + case 1: + r0 = v->Q(2); + r1 = v->Q(3); + break; + case 2: + r0 = s->Q(0); + r1 = s->Q(1); + break; + case 3: + r0 = s->Q(2); + r1 = s->Q(3); + break; + default: /* default case added to help the compiler to avoid warnings */ + g_assert_not_reached(); + } + switch ((order >> 4) & 3) { + case 0: + r2 = v->Q(0); + r3 = v->Q(1); + break; + case 1: + r2 = v->Q(2); + r3 = v->Q(3); + break; + case 2: + r2 = s->Q(0); + r3 = s->Q(1); + break; + case 3: + r2 = s->Q(2); + r3 = s->Q(3); + break; + default: /* default case added to help the compiler to avoid warnings */ + g_assert_not_reached(); + } + d->Q(0) = r0; + d->Q(1) = r1; + d->Q(2) = r2; + d->Q(3) = r3; + if (order & 0x8) { + d->Q(0) = 0; + d->Q(1) = 0; + } + if (order & 0x80) { + d->Q(2) = 0; + d->Q(3) = 0; + } +} + +void helper_vpermq_ymm(Reg *d, Reg *s, uint32_t order) +{ + uint64_t r0, r1, r2, r3; + r0 = s->Q(order & 3); + r1 = s->Q((order >> 2) & 3); + r2 = s->Q((order >> 4) & 3); + r3 = s->Q((order >> 6) & 3); + d->Q(0) = r0; + d->Q(1) = r1; + d->Q(2) = r2; + d->Q(3) = r3; +} + +void helper_vpermd_ymm(Reg *d, Reg *v, Reg *s) +{ + uint32_t r[8]; + int i; + + for (i = 0; i < 8; i++) { + r[i] = s->L(v->L(i) & 7); + } + for (i = 0; i < 8; i++) { + d->L(i) = r[i]; + } +} +#endif + +/* FMA3 op helpers */ +#if SHIFT == 1 +#define SSE_HELPER_FMAS(name, elem, F) \ + void name(CPUX86State *env, Reg *d, Reg *a, Reg *b, Reg *c, int flags) \ + { \ + d->elem(0) = F(a->elem(0), b->elem(0), c->elem(0), flags, &env->sse_status); \ + } +#define SSE_HELPER_FMAP(name, elem, num, F) \ + void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *a, Reg *b, Reg *c, \ + int flags, int flip) \ + { \ + int i; \ + for (i = 0; i < num; i++) { \ + d->elem(i) = F(a->elem(i), b->elem(i), c->elem(i), flags, &env->sse_status); \ + flags ^= flip; \ + } \ + } + +SSE_HELPER_FMAS(helper_fma4ss, ZMM_S, float32_muladd) +SSE_HELPER_FMAS(helper_fma4sd, ZMM_D, float64_muladd) +#endif + +#if SHIFT >= 1 +SSE_HELPER_FMAP(helper_fma4ps, ZMM_S, 2 << SHIFT, float32_muladd) +SSE_HELPER_FMAP(helper_fma4pd, ZMM_D, 1 << SHIFT, float64_muladd) +#endif + +#if SHIFT == 1 +#define SSE_HELPER_SHA1RNDS4(name, F, K) \ + void name(Reg *d, Reg *a, Reg *b) \ + { \ + uint32_t A, B, C, D, E, t, i; \ + \ + A = a->L(3); \ + B = a->L(2); \ + C = a->L(1); \ + D = a->L(0); \ + E = 0; \ + \ + for (i = 0; i <= 3; i++) { \ + t = F(B, C, D) + rol32(A, 5) + b->L(3 - i) + E + K; \ + E = D; \ + D = C; \ + C = rol32(B, 30); \ + B = A; \ + A = t; \ + } \ + \ + d->L(3) = A; \ + d->L(2) = B; \ + d->L(1) = C; \ + d->L(0) = D; \ + } + +#define SHA1_F0(b, c, d) (((b) & (c)) ^ (~(b) & (d))) +#define SHA1_F1(b, c, d) ((b) ^ (c) ^ (d)) +#define SHA1_F2(b, c, d) (((b) & (c)) ^ ((b) & (d)) ^ ((c) & (d))) + +SSE_HELPER_SHA1RNDS4(helper_sha1rnds4_f0, SHA1_F0, 0x5A827999) +SSE_HELPER_SHA1RNDS4(helper_sha1rnds4_f1, SHA1_F1, 0x6ED9EBA1) +SSE_HELPER_SHA1RNDS4(helper_sha1rnds4_f2, SHA1_F2, 0x8F1BBCDC) +SSE_HELPER_SHA1RNDS4(helper_sha1rnds4_f3, SHA1_F1, 0xCA62C1D6) + +void helper_sha1nexte(Reg *d, Reg *a, Reg *b) +{ + d->L(3) = b->L(3) + rol32(a->L(3), 30); + d->L(2) = b->L(2); + d->L(1) = b->L(1); + d->L(0) = b->L(0); +} + +void helper_sha1msg1(Reg *d, Reg *a, Reg *b) +{ + /* These could be overwritten by the first two assignments, save them. */ + uint32_t b3 = b->L(3); + uint32_t b2 = b->L(2); + + d->L(3) = a->L(3) ^ a->L(1); + d->L(2) = a->L(2) ^ a->L(0); + d->L(1) = a->L(1) ^ b3; + d->L(0) = a->L(0) ^ b2; +} + +void helper_sha1msg2(Reg *d, Reg *a, Reg *b) +{ + d->L(3) = rol32(a->L(3) ^ b->L(2), 1); + d->L(2) = rol32(a->L(2) ^ b->L(1), 1); + d->L(1) = rol32(a->L(1) ^ b->L(0), 1); + d->L(0) = rol32(a->L(0) ^ d->L(3), 1); +} + +#define SHA256_CH(e, f, g) (((e) & (f)) ^ (~(e) & (g))) +#define SHA256_MAJ(a, b, c) (((a) & (b)) ^ ((a) & (c)) ^ ((b) & (c))) + +#define SHA256_RNDS0(w) (ror32((w), 2) ^ ror32((w), 13) ^ ror32((w), 22)) +#define SHA256_RNDS1(w) (ror32((w), 6) ^ ror32((w), 11) ^ ror32((w), 25)) +#define SHA256_MSGS0(w) (ror32((w), 7) ^ ror32((w), 18) ^ ((w) >> 3)) +#define SHA256_MSGS1(w) (ror32((w), 17) ^ ror32((w), 19) ^ ((w) >> 10)) + +void helper_sha256rnds2(Reg *d, Reg *a, Reg *b, uint32_t wk0, uint32_t wk1) +{ + uint32_t t, AA, EE; + + uint32_t A = b->L(3); + uint32_t B = b->L(2); + uint32_t C = a->L(3); + uint32_t D = a->L(2); + uint32_t E = b->L(1); + uint32_t F = b->L(0); + uint32_t G = a->L(1); + uint32_t H = a->L(0); + + /* Even round */ + t = SHA256_CH(E, F, G) + SHA256_RNDS1(E) + wk0 + H; + AA = t + SHA256_MAJ(A, B, C) + SHA256_RNDS0(A); + EE = t + D; + + /* These will be B and F at the end of the odd round */ + d->L(2) = AA; + d->L(0) = EE; + + D = C, C = B, B = A, A = AA; + H = G, G = F, F = E, E = EE; + + /* Odd round */ + t = SHA256_CH(E, F, G) + SHA256_RNDS1(E) + wk1 + H; + AA = t + SHA256_MAJ(A, B, C) + SHA256_RNDS0(A); + EE = t + D; + + d->L(3) = AA; + d->L(1) = EE; +} + +void helper_sha256msg1(Reg *d, Reg *a, Reg *b) +{ + /* b->L(0) could be overwritten by the first assignment, save it. */ + uint32_t b0 = b->L(0); + + d->L(0) = a->L(0) + SHA256_MSGS0(a->L(1)); + d->L(1) = a->L(1) + SHA256_MSGS0(a->L(2)); + d->L(2) = a->L(2) + SHA256_MSGS0(a->L(3)); + d->L(3) = a->L(3) + SHA256_MSGS0(b0); +} + +void helper_sha256msg2(Reg *d, Reg *a, Reg *b) +{ + /* Earlier assignments cannot overwrite any of the two operands. */ + d->L(0) = a->L(0) + SHA256_MSGS1(b->L(2)); + d->L(1) = a->L(1) + SHA256_MSGS1(b->L(3)); + /* Yes, this reuses the previously computed values. */ + d->L(2) = a->L(2) + SHA256_MSGS1(d->L(0)); + d->L(3) = a->L(3) + SHA256_MSGS1(d->L(1)); +} +#endif + +#undef SSE_HELPER_S + +#undef LANE_WIDTH #undef SHIFT #undef XMM_ONLY #undef Reg @@ -2340,4 +2666,3 @@ void glue(helper_aeskeygenassist, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, #undef L #undef Q #undef SUFFIX -#undef SIZE diff --git a/target/i386/ops_sse_header.h b/target/i386/ops_sse_header.h deleted file mode 100644 index cef28f2aae..0000000000 --- a/target/i386/ops_sse_header.h +++ /dev/null @@ -1,359 +0,0 @@ -/* - * MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI support - * - * Copyright (c) 2005 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.1 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 SHIFT == 0 -#define Reg MMXReg -#define SUFFIX _mmx -#else -#define Reg ZMMReg -#define SUFFIX _xmm -#endif - -#define dh_alias_Reg ptr -#define dh_alias_ZMMReg ptr -#define dh_alias_MMXReg ptr -#define dh_ctype_Reg Reg * -#define dh_ctype_ZMMReg ZMMReg * -#define dh_ctype_MMXReg MMXReg * -#define dh_typecode_Reg dh_typecode_ptr -#define dh_typecode_ZMMReg dh_typecode_ptr -#define dh_typecode_MMXReg dh_typecode_ptr - -DEF_HELPER_3(glue(psrlw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(psraw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(psllw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(psrld, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(psrad, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pslld, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(psrlq, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(psllq, SUFFIX), void, env, Reg, Reg) - -#if SHIFT == 1 -DEF_HELPER_3(glue(psrldq, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pslldq, SUFFIX), void, env, Reg, Reg) -#endif - -#define SSE_HELPER_B(name, F)\ - DEF_HELPER_3(glue(name, SUFFIX), void, env, Reg, Reg) - -#define SSE_HELPER_W(name, F)\ - DEF_HELPER_3(glue(name, SUFFIX), void, env, Reg, Reg) - -#define SSE_HELPER_L(name, F)\ - DEF_HELPER_3(glue(name, SUFFIX), void, env, Reg, Reg) - -#define SSE_HELPER_Q(name, F)\ - DEF_HELPER_3(glue(name, SUFFIX), void, env, Reg, Reg) - -SSE_HELPER_B(paddb, FADD) -SSE_HELPER_W(paddw, FADD) -SSE_HELPER_L(paddl, FADD) -SSE_HELPER_Q(paddq, FADD) - -SSE_HELPER_B(psubb, FSUB) -SSE_HELPER_W(psubw, FSUB) -SSE_HELPER_L(psubl, FSUB) -SSE_HELPER_Q(psubq, FSUB) - -SSE_HELPER_B(paddusb, FADDUB) -SSE_HELPER_B(paddsb, FADDSB) -SSE_HELPER_B(psubusb, FSUBUB) -SSE_HELPER_B(psubsb, FSUBSB) - -SSE_HELPER_W(paddusw, FADDUW) -SSE_HELPER_W(paddsw, FADDSW) -SSE_HELPER_W(psubusw, FSUBUW) -SSE_HELPER_W(psubsw, FSUBSW) - -SSE_HELPER_B(pminub, FMINUB) -SSE_HELPER_B(pmaxub, FMAXUB) - -SSE_HELPER_W(pminsw, FMINSW) -SSE_HELPER_W(pmaxsw, FMAXSW) - -SSE_HELPER_Q(pand, FAND) -SSE_HELPER_Q(pandn, FANDN) -SSE_HELPER_Q(por, FOR) -SSE_HELPER_Q(pxor, FXOR) - -SSE_HELPER_B(pcmpgtb, FCMPGTB) -SSE_HELPER_W(pcmpgtw, FCMPGTW) -SSE_HELPER_L(pcmpgtl, FCMPGTL) - -SSE_HELPER_B(pcmpeqb, FCMPEQ) -SSE_HELPER_W(pcmpeqw, FCMPEQ) -SSE_HELPER_L(pcmpeql, FCMPEQ) - -SSE_HELPER_W(pmullw, FMULLW) -#if SHIFT == 0 -SSE_HELPER_W(pmulhrw, FMULHRW) -#endif -SSE_HELPER_W(pmulhuw, FMULHUW) -SSE_HELPER_W(pmulhw, FMULHW) - -SSE_HELPER_B(pavgb, FAVG) -SSE_HELPER_W(pavgw, FAVG) - -DEF_HELPER_3(glue(pmuludq, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmaddwd, SUFFIX), void, env, Reg, Reg) - -DEF_HELPER_3(glue(psadbw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_4(glue(maskmov, SUFFIX), void, env, Reg, Reg, tl) -DEF_HELPER_2(glue(movl_mm_T0, SUFFIX), void, Reg, i32) -#ifdef TARGET_X86_64 -DEF_HELPER_2(glue(movq_mm_T0, SUFFIX), void, Reg, i64) -#endif - -#if SHIFT == 0 -DEF_HELPER_3(glue(pshufw, SUFFIX), void, Reg, Reg, int) -#else -DEF_HELPER_3(shufps, void, Reg, Reg, int) -DEF_HELPER_3(shufpd, void, Reg, Reg, int) -DEF_HELPER_3(glue(pshufd, SUFFIX), void, Reg, Reg, int) -DEF_HELPER_3(glue(pshuflw, SUFFIX), void, Reg, Reg, int) -DEF_HELPER_3(glue(pshufhw, SUFFIX), void, Reg, Reg, int) -#endif - -#if SHIFT == 1 -/* FPU ops */ -/* XXX: not accurate */ - -#define SSE_HELPER_S(name, F) \ - DEF_HELPER_3(name ## ps, void, env, Reg, Reg) \ - DEF_HELPER_3(name ## ss, void, env, Reg, Reg) \ - DEF_HELPER_3(name ## pd, void, env, Reg, Reg) \ - DEF_HELPER_3(name ## sd, void, env, Reg, Reg) - -SSE_HELPER_S(add, FPU_ADD) -SSE_HELPER_S(sub, FPU_SUB) -SSE_HELPER_S(mul, FPU_MUL) -SSE_HELPER_S(div, FPU_DIV) -SSE_HELPER_S(min, FPU_MIN) -SSE_HELPER_S(max, FPU_MAX) -SSE_HELPER_S(sqrt, FPU_SQRT) - - -DEF_HELPER_3(cvtps2pd, void, env, Reg, Reg) -DEF_HELPER_3(cvtpd2ps, void, env, Reg, Reg) -DEF_HELPER_3(cvtss2sd, void, env, Reg, Reg) -DEF_HELPER_3(cvtsd2ss, void, env, Reg, Reg) -DEF_HELPER_3(cvtdq2ps, void, env, Reg, Reg) -DEF_HELPER_3(cvtdq2pd, void, env, Reg, Reg) -DEF_HELPER_3(cvtpi2ps, void, env, ZMMReg, MMXReg) -DEF_HELPER_3(cvtpi2pd, void, env, ZMMReg, MMXReg) -DEF_HELPER_3(cvtsi2ss, void, env, ZMMReg, i32) -DEF_HELPER_3(cvtsi2sd, void, env, ZMMReg, i32) - -#ifdef TARGET_X86_64 -DEF_HELPER_3(cvtsq2ss, void, env, ZMMReg, i64) -DEF_HELPER_3(cvtsq2sd, void, env, ZMMReg, i64) -#endif - -DEF_HELPER_3(cvtps2dq, void, env, ZMMReg, ZMMReg) -DEF_HELPER_3(cvtpd2dq, void, env, ZMMReg, ZMMReg) -DEF_HELPER_3(cvtps2pi, void, env, MMXReg, ZMMReg) -DEF_HELPER_3(cvtpd2pi, void, env, MMXReg, ZMMReg) -DEF_HELPER_2(cvtss2si, s32, env, ZMMReg) -DEF_HELPER_2(cvtsd2si, s32, env, ZMMReg) -#ifdef TARGET_X86_64 -DEF_HELPER_2(cvtss2sq, s64, env, ZMMReg) -DEF_HELPER_2(cvtsd2sq, s64, env, ZMMReg) -#endif - -DEF_HELPER_3(cvttps2dq, void, env, ZMMReg, ZMMReg) -DEF_HELPER_3(cvttpd2dq, void, env, ZMMReg, ZMMReg) -DEF_HELPER_3(cvttps2pi, void, env, MMXReg, ZMMReg) -DEF_HELPER_3(cvttpd2pi, void, env, MMXReg, ZMMReg) -DEF_HELPER_2(cvttss2si, s32, env, ZMMReg) -DEF_HELPER_2(cvttsd2si, s32, env, ZMMReg) -#ifdef TARGET_X86_64 -DEF_HELPER_2(cvttss2sq, s64, env, ZMMReg) -DEF_HELPER_2(cvttsd2sq, s64, env, ZMMReg) -#endif - -DEF_HELPER_3(rsqrtps, void, env, ZMMReg, ZMMReg) -DEF_HELPER_3(rsqrtss, void, env, ZMMReg, ZMMReg) -DEF_HELPER_3(rcpps, void, env, ZMMReg, ZMMReg) -DEF_HELPER_3(rcpss, void, env, ZMMReg, ZMMReg) -DEF_HELPER_3(extrq_r, void, env, ZMMReg, ZMMReg) -DEF_HELPER_4(extrq_i, void, env, ZMMReg, int, int) -DEF_HELPER_3(insertq_r, void, env, ZMMReg, ZMMReg) -DEF_HELPER_4(insertq_i, void, env, ZMMReg, int, int) -DEF_HELPER_3(haddps, void, env, ZMMReg, ZMMReg) -DEF_HELPER_3(haddpd, void, env, ZMMReg, ZMMReg) -DEF_HELPER_3(hsubps, void, env, ZMMReg, ZMMReg) -DEF_HELPER_3(hsubpd, void, env, ZMMReg, ZMMReg) -DEF_HELPER_3(addsubps, void, env, ZMMReg, ZMMReg) -DEF_HELPER_3(addsubpd, void, env, ZMMReg, ZMMReg) - -#define SSE_HELPER_CMP(name, F) \ - DEF_HELPER_3(name ## ps, void, env, Reg, Reg) \ - DEF_HELPER_3(name ## ss, void, env, Reg, Reg) \ - DEF_HELPER_3(name ## pd, void, env, Reg, Reg) \ - DEF_HELPER_3(name ## sd, void, env, Reg, Reg) - -SSE_HELPER_CMP(cmpeq, FPU_CMPEQ) -SSE_HELPER_CMP(cmplt, FPU_CMPLT) -SSE_HELPER_CMP(cmple, FPU_CMPLE) -SSE_HELPER_CMP(cmpunord, FPU_CMPUNORD) -SSE_HELPER_CMP(cmpneq, FPU_CMPNEQ) -SSE_HELPER_CMP(cmpnlt, FPU_CMPNLT) -SSE_HELPER_CMP(cmpnle, FPU_CMPNLE) -SSE_HELPER_CMP(cmpord, FPU_CMPORD) - -DEF_HELPER_3(ucomiss, void, env, Reg, Reg) -DEF_HELPER_3(comiss, void, env, Reg, Reg) -DEF_HELPER_3(ucomisd, void, env, Reg, Reg) -DEF_HELPER_3(comisd, void, env, Reg, Reg) -DEF_HELPER_2(movmskps, i32, env, Reg) -DEF_HELPER_2(movmskpd, i32, env, Reg) -#endif - -DEF_HELPER_2(glue(pmovmskb, SUFFIX), i32, env, Reg) -DEF_HELPER_3(glue(packsswb, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(packuswb, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(packssdw, SUFFIX), void, env, Reg, Reg) -#define UNPCK_OP(base_name, base) \ - DEF_HELPER_3(glue(punpck ## base_name ## bw, SUFFIX), void, env, Reg, Reg) \ - DEF_HELPER_3(glue(punpck ## base_name ## wd, SUFFIX), void, env, Reg, Reg) \ - DEF_HELPER_3(glue(punpck ## base_name ## dq, SUFFIX), void, env, Reg, Reg) - -UNPCK_OP(l, 0) -UNPCK_OP(h, 1) - -#if SHIFT == 1 -DEF_HELPER_3(glue(punpcklqdq, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(punpckhqdq, SUFFIX), void, env, Reg, Reg) -#endif - -/* 3DNow! float ops */ -#if SHIFT == 0 -DEF_HELPER_3(pi2fd, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pi2fw, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pf2id, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pf2iw, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pfacc, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pfadd, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pfcmpeq, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pfcmpge, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pfcmpgt, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pfmax, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pfmin, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pfmul, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pfnacc, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pfpnacc, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pfrcp, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pfrsqrt, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pfsub, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pfsubr, void, env, MMXReg, MMXReg) -DEF_HELPER_3(pswapd, void, env, MMXReg, MMXReg) -#endif - -/* SSSE3 op helpers */ -DEF_HELPER_3(glue(phaddw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(phaddd, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(phaddsw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(phsubw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(phsubd, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(phsubsw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pabsb, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pabsw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pabsd, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmaddubsw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmulhrsw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pshufb, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(psignb, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(psignw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(psignd, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_4(glue(palignr, SUFFIX), void, env, Reg, Reg, s32) - -/* SSE4.1 op helpers */ -#if SHIFT == 1 -DEF_HELPER_3(glue(pblendvb, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(blendvps, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(blendvpd, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(ptest, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmovsxbw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmovsxbd, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmovsxbq, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmovsxwd, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmovsxwq, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmovsxdq, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmovzxbw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmovzxbd, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmovzxbq, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmovzxwd, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmovzxwq, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmovzxdq, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmuldq, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pcmpeqq, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(packusdw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pminsb, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pminsd, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pminuw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pminud, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmaxsb, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmaxsd, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmaxuw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmaxud, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(pmulld, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(phminposuw, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_4(glue(roundps, SUFFIX), void, env, Reg, Reg, i32) -DEF_HELPER_4(glue(roundpd, SUFFIX), void, env, Reg, Reg, i32) -DEF_HELPER_4(glue(roundss, SUFFIX), void, env, Reg, Reg, i32) -DEF_HELPER_4(glue(roundsd, SUFFIX), void, env, Reg, Reg, i32) -DEF_HELPER_4(glue(blendps, SUFFIX), void, env, Reg, Reg, i32) -DEF_HELPER_4(glue(blendpd, SUFFIX), void, env, Reg, Reg, i32) -DEF_HELPER_4(glue(pblendw, SUFFIX), void, env, Reg, Reg, i32) -DEF_HELPER_4(glue(dpps, SUFFIX), void, env, Reg, Reg, i32) -DEF_HELPER_4(glue(dppd, SUFFIX), void, env, Reg, Reg, i32) -DEF_HELPER_4(glue(mpsadbw, SUFFIX), void, env, Reg, Reg, i32) -#endif - -/* SSE4.2 op helpers */ -#if SHIFT == 1 -DEF_HELPER_3(glue(pcmpgtq, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_4(glue(pcmpestri, SUFFIX), void, env, Reg, Reg, i32) -DEF_HELPER_4(glue(pcmpestrm, SUFFIX), void, env, Reg, Reg, i32) -DEF_HELPER_4(glue(pcmpistri, SUFFIX), void, env, Reg, Reg, i32) -DEF_HELPER_4(glue(pcmpistrm, SUFFIX), void, env, Reg, Reg, i32) -DEF_HELPER_3(crc32, tl, i32, tl, i32) -#endif - -/* AES-NI op helpers */ -#if SHIFT == 1 -DEF_HELPER_3(glue(aesdec, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(aesdeclast, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(aesenc, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(aesenclast, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_3(glue(aesimc, SUFFIX), void, env, Reg, Reg) -DEF_HELPER_4(glue(aeskeygenassist, SUFFIX), void, env, Reg, Reg, i32) -DEF_HELPER_4(glue(pclmulqdq, SUFFIX), void, env, Reg, Reg, i32) -#endif - -#undef SHIFT -#undef Reg -#undef SUFFIX - -#undef SSE_HELPER_B -#undef SSE_HELPER_W -#undef SSE_HELPER_L -#undef SSE_HELPER_Q -#undef SSE_HELPER_S -#undef SSE_HELPER_CMP -#undef UNPCK_OP diff --git a/target/i386/sev-sysemu-stub.c b/target/i386/sev-sysemu-stub.c index 7a29295d1e..96e1c15cc3 100644 --- a/target/i386/sev-sysemu-stub.c +++ b/target/i386/sev-sysemu-stub.c @@ -15,7 +15,6 @@ #include "monitor/monitor.h" #include "monitor/hmp-target.h" #include "qapi/qapi-commands-misc-target.h" -#include "qapi/qmp/qerror.h" #include "qapi/error.h" #include "sev.h" diff --git a/target/i386/sev.c b/target/i386/sev.c index 32f7dbac4e..72930ff0dc 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -23,6 +23,7 @@ #include "qemu/base64.h" #include "qemu/module.h" #include "qemu/uuid.h" +#include "qemu/error-report.h" #include "crypto/hash.h" #include "sysemu/kvm.h" #include "sev.h" @@ -34,7 +35,6 @@ #include "monitor/monitor.h" #include "monitor/hmp-target.h" #include "qapi/qapi-commands-misc-target.h" -#include "qapi/qmp/qerror.h" #include "exec/confidential-guest-support.h" #include "hw/i386/pc.h" #include "exec/address-spaces.h" @@ -167,7 +167,7 @@ sev_ioctl(int fd, int cmd, void *data, int *error) input.id = cmd; input.sev_fd = fd; - input.data = (__u64)(unsigned long)data; + input.data = (uintptr_t)data; r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, &input); @@ -240,7 +240,7 @@ sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size, return; } - range.addr = (__u64)(unsigned long)host; + range.addr = (uintptr_t)host; range.size = max_size; trace_kvm_memcrypt_register_region(host, max_size); @@ -270,7 +270,7 @@ sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size, return; } - range.addr = (__u64)(unsigned long)host; + range.addr = (uintptr_t)host; range.size = max_size; trace_kvm_memcrypt_unregister_region(host, max_size); @@ -767,7 +767,7 @@ sev_launch_update_data(SevGuestState *sev, uint8_t *addr, uint64_t len) return 1; } - update.uaddr = (__u64)(unsigned long)addr; + update.uaddr = (uintptr_t)addr; update.len = len; trace_kvm_sev_launch_update_data(addr, len); ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, @@ -891,7 +891,7 @@ sev_launch_finish(SevGuestState *sev) /* add migration blocker */ error_setg(&sev_mig_blocker, "SEV: Migration is not implemented"); - migrate_add_blocker(sev_mig_blocker, &error_fatal); + migrate_add_blocker(&sev_mig_blocker, &error_fatal); } static void @@ -932,15 +932,26 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); host_cbitpos = ebx & 0x3f; + /* + * The cbitpos value will be placed in bit positions 5:0 of the EBX + * register of CPUID 0x8000001F. No need to verify the range as the + * comparison against the host value accomplishes that. + */ if (host_cbitpos != sev->cbitpos) { error_setg(errp, "%s: cbitpos check failed, host '%d' requested '%d'", __func__, host_cbitpos, sev->cbitpos); goto err; } - if (sev->reduced_phys_bits < 1) { - error_setg(errp, "%s: reduced_phys_bits check failed, it should be >=1," - " requested '%d'", __func__, sev->reduced_phys_bits); + /* + * The reduced-phys-bits value will be placed in bit positions 11:6 of + * the EBX register of CPUID 0x8000001F, so verify the supplied value + * is in the range of 1 to 63. + */ + if (sev->reduced_phys_bits < 1 || sev->reduced_phys_bits > 63) { + error_setg(errp, "%s: reduced_phys_bits check failed," + " it should be in the range of 1 to 63, requested '%d'", + __func__, sev->reduced_phys_bits); goto err; } @@ -1033,6 +1044,7 @@ sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) int sev_inject_launch_secret(const char *packet_hdr, const char *secret, uint64_t gpa, Error **errp) { + ERRP_GUARD(); struct kvm_sev_launch_secret input; g_autofree guchar *data = NULL, *hdr = NULL; int error, ret = 1; diff --git a/target/i386/sev.h b/target/i386/sev.h index 7b1528248a..e7499c95b1 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -46,9 +46,9 @@ bool sev_es_enabled(void); #define sev_es_enabled() 0 #endif -extern uint32_t sev_get_cbit_position(void); -extern uint32_t sev_get_reduced_phys_bits(void); -extern bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); +uint32_t sev_get_cbit_position(void); +uint32_t sev_get_reduced_phys_bits(void); +bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp); int sev_inject_launch_secret(const char *hdr, const char *secret, diff --git a/target/i386/svm.h b/target/i386/svm.h index f9a785489d..1bd7844730 100644 --- a/target/i386/svm.h +++ b/target/i386/svm.h @@ -132,6 +132,7 @@ /* only included in documentation, maybe wrong */ #define SVM_EXIT_MONITOR 0x08a #define SVM_EXIT_MWAIT 0x08b +#define SVM_EXIT_XSETBV 0x08d #define SVM_EXIT_NPF 0x400 #define SVM_EXIT_ERR -1 diff --git a/target/i386/tcg/cc_helper.c b/target/i386/tcg/cc_helper.c index cc7ea9e8b9..f76e9cb8cf 100644 --- a/target/i386/tcg/cc_helper.c +++ b/target/i386/tcg/cc_helper.c @@ -58,21 +58,21 @@ const uint8_t parity_table[256] = { }; #define SHIFT 0 -#include "cc_helper_template.h" +#include "cc_helper_template.h.inc" #undef SHIFT #define SHIFT 1 -#include "cc_helper_template.h" +#include "cc_helper_template.h.inc" #undef SHIFT #define SHIFT 2 -#include "cc_helper_template.h" +#include "cc_helper_template.h.inc" #undef SHIFT #ifdef TARGET_X86_64 #define SHIFT 3 -#include "cc_helper_template.h" +#include "cc_helper_template.h.inc" #undef SHIFT #endif @@ -220,9 +220,9 @@ target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, } } -uint32_t cpu_cc_compute_all(CPUX86State *env, int op) +uint32_t cpu_cc_compute_all(CPUX86State *env) { - return helper_cc_compute_all(CC_DST, CC_SRC, CC_SRC2, op); + return helper_cc_compute_all(CC_DST, CC_SRC, CC_SRC2, CC_OP); } target_ulong helper_cc_compute_c(target_ulong dst, target_ulong src1, @@ -335,7 +335,7 @@ target_ulong helper_read_eflags(CPUX86State *env) { uint32_t eflags; - eflags = cpu_cc_compute_all(env, CC_OP); + eflags = cpu_cc_compute_all(env); eflags |= (env->df & DF_MASK); eflags |= env->eflags & ~(VM_MASK | RF_MASK); return eflags; @@ -346,44 +346,3 @@ void helper_clts(CPUX86State *env) env->cr[0] &= ~CR0_TS_MASK; env->hflags &= ~HF_TS_MASK; } - -void helper_reset_rf(CPUX86State *env) -{ - env->eflags &= ~RF_MASK; -} - -void helper_cli(CPUX86State *env) -{ - env->eflags &= ~IF_MASK; -} - -void helper_sti(CPUX86State *env) -{ - env->eflags |= IF_MASK; -} - -void helper_clac(CPUX86State *env) -{ - env->eflags &= ~AC_MASK; -} - -void helper_stac(CPUX86State *env) -{ - env->eflags |= AC_MASK; -} - -#if 0 -/* vm86plus instructions */ -void helper_cli_vm(CPUX86State *env) -{ - env->eflags &= ~VIF_MASK; -} - -void helper_sti_vm(CPUX86State *env) -{ - env->eflags |= VIF_MASK; - if (env->eflags & VIP_MASK) { - raise_exception_ra(env, EXCP0D_GPF, GETPC()); - } -} -#endif diff --git a/target/i386/tcg/cc_helper_template.h b/target/i386/tcg/cc_helper_template.h.inc similarity index 100% rename from target/i386/tcg/cc_helper_template.h rename to target/i386/tcg/cc_helper_template.h.inc diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc new file mode 100644 index 0000000000..426c459412 --- /dev/null +++ b/target/i386/tcg/decode-new.c.inc @@ -0,0 +1,2024 @@ +/* + * New-style decoder for i386 instructions + * + * Copyright (c) 2022 Red Hat, Inc. + * + * Author: Paolo Bonzini + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 . + */ + +/* + * The decoder is mostly based on tables copied from the Intel SDM. As + * a result, most operand load and writeback is done entirely in common + * table-driven code using the same operand type (X86_TYPE_*) and + * size (X86_SIZE_*) codes used in the manual. There are a few differences + * though. + * + * Operand sizes + * ------------- + * + * The manual lists d64 ("cannot encode 32-bit size in 64-bit mode") and f64 + * ("cannot encode 16-bit or 32-bit size in 64-bit mode") as modifiers of the + * "v" or "z" sizes. The decoder simply makes them separate operand sizes. + * + * Vector operands + * --------------- + * + * The main difference is that the V, U and W types are extended to + * cover MMX as well; if an instruction is like + * + * por Pq, Qq + * 66 por Vx, Hx, Wx + * + * only the second row is included and the instruction is marked as a + * valid MMX instruction. The MMX flag directs the decoder to rewrite + * the V/U/H/W types to P/N/P/Q if there is no prefix, as well as changing + * "x" to "q" if there is no prefix. + * + * In addition, the ss/ps/sd/pd types are sometimes mushed together as "x" + * if the difference is expressed via prefixes. Individual instructions + * are separated by prefix in the generator functions. + * + * There is a custom size "xh" used to address half of a SSE/AVX operand. + * This points to a 64-bit operand for SSE operations, 128-bit operand + * for 256-bit AVX operands, etc. It is used for conversion operations + * such as VCVTPH2PS or VCVTSS2SD. + * + * There are a couple cases in which instructions (e.g. MOVD) write the + * whole XMM or MM register but are established incorrectly in the manual + * as "d" or "q". These have to be fixed for the decoder to work correctly. + * + * VEX exception classes + * --------------------- + * + * Speaking about imprecisions in the manual, the decoder treats all + * exception-class 4 instructions as having an optional VEX prefix, and + * all exception-class 6 instructions as having a mandatory VEX prefix. + * This is true except for a dozen instructions; these are in exception + * class 4 but do not ignore the VEX.W bit (which does not even exist + * without a VEX prefix). These instructions are mostly listed in Intel's + * table 2-16, but with a few exceptions. + * + * The AMD manual has more precise subclasses for exceptions, and unlike Intel + * they list the VEX.W requirements in the exception classes as well (except + * when they don't). AMD describes class 6 as "AVX Mixed Memory Argument" + * without defining what a mixed memory argument is, but still use 4 as the + * primary exception class... except when they don't. + * + * The summary is: + * Intel AMD VEX.W note + * ------------------------------------------------------------------- + * vpblendd 4 4J 0 + * vpblendvb 4 4E-X 0 (*) + * vpbroadcastq 6 6D 0 (+) + * vpermd/vpermps 4 4H 0 (§) + * vpermq/vpermpd 4 4H-1 1 (§) + * vpermilpd/vpermilps 4 6E 0 (^) + * vpmaskmovd 6 4K significant (^) + * vpsllv 4 4K significant + * vpsrav 4 4J 0 + * vpsrlv 4 4K significant + * vtestps/vtestpd 4 4G 0 + * + * (*) AMD lists VPBLENDVB as related to SSE4.1 PBLENDVB, which may + * explain why it is considered exception class 4. However, + * Intel says that VEX-only instructions should be in class 6... + * + * (+) Not found in Intel's table 2-16 + * + * (§) 4H and 4H-1 do not mention VEX.W requirements, which are + * however present in the description of the instruction + * + * (^) these are the two cases in which Intel and AMD disagree on the + * primary exception class + */ + +#define X86_OP_NONE { 0 }, + +#define X86_OP_GROUP3(op, op0_, s0_, op1_, s1_, op2_, s2_, ...) { \ + .decode = glue(decode_, op), \ + .op0 = glue(X86_TYPE_, op0_), \ + .s0 = glue(X86_SIZE_, s0_), \ + .op1 = glue(X86_TYPE_, op1_), \ + .s1 = glue(X86_SIZE_, s1_), \ + .op2 = glue(X86_TYPE_, op2_), \ + .s2 = glue(X86_SIZE_, s2_), \ + .is_decode = true, \ + ## __VA_ARGS__ \ +} + +#define X86_OP_GROUP2(op, op0, s0, op1, s1, ...) \ + X86_OP_GROUP3(op, op0, s0, 2op, s0, op1, s1, ## __VA_ARGS__) +#define X86_OP_GROUP0(op, ...) \ + X86_OP_GROUP3(op, None, None, None, None, None, None, ## __VA_ARGS__) + +#define X86_OP_ENTRY3(op, op0_, s0_, op1_, s1_, op2_, s2_, ...) { \ + .gen = glue(gen_, op), \ + .op0 = glue(X86_TYPE_, op0_), \ + .s0 = glue(X86_SIZE_, s0_), \ + .op1 = glue(X86_TYPE_, op1_), \ + .s1 = glue(X86_SIZE_, s1_), \ + .op2 = glue(X86_TYPE_, op2_), \ + .s2 = glue(X86_SIZE_, s2_), \ + ## __VA_ARGS__ \ +} + +#define X86_OP_ENTRY4(op, op0_, s0_, op1_, s1_, op2_, s2_, ...) \ + X86_OP_ENTRY3(op, op0_, s0_, op1_, s1_, op2_, s2_, \ + .op3 = X86_TYPE_I, .s3 = X86_SIZE_b, \ + ## __VA_ARGS__) + +#define X86_OP_ENTRY2(op, op0, s0, op1, s1, ...) \ + X86_OP_ENTRY3(op, op0, s0, 2op, s0, op1, s1, ## __VA_ARGS__) +#define X86_OP_ENTRYw(op, op0, s0, ...) \ + X86_OP_ENTRY3(op, op0, s0, None, None, None, None, ## __VA_ARGS__) +#define X86_OP_ENTRYr(op, op0, s0, ...) \ + X86_OP_ENTRY3(op, None, None, None, None, op0, s0, ## __VA_ARGS__) +#define X86_OP_ENTRY0(op, ...) \ + X86_OP_ENTRY3(op, None, None, None, None, None, None, ## __VA_ARGS__) + +#define cpuid(feat) .cpuid = X86_FEAT_##feat, +#define xchg .special = X86_SPECIAL_Locked, +#define lock .special = X86_SPECIAL_HasLock, +#define mmx .special = X86_SPECIAL_MMX, +#define op0_Rd .special = X86_SPECIAL_Op0_Rd, +#define op2_Ry .special = X86_SPECIAL_Op2_Ry, +#define avx_movx .special = X86_SPECIAL_AVXExtMov, +#define sextT0 .special = X86_SPECIAL_SExtT0, +#define zextT0 .special = X86_SPECIAL_ZExtT0, + +#define vex1 .vex_class = 1, +#define vex1_rep3 .vex_class = 1, .vex_special = X86_VEX_REPScalar, +#define vex2 .vex_class = 2, +#define vex2_rep3 .vex_class = 2, .vex_special = X86_VEX_REPScalar, +#define vex3 .vex_class = 3, +#define vex4 .vex_class = 4, +#define vex4_unal .vex_class = 4, .vex_special = X86_VEX_SSEUnaligned, +#define vex4_rep5 .vex_class = 4, .vex_special = X86_VEX_REPScalar, +#define vex5 .vex_class = 5, +#define vex6 .vex_class = 6, +#define vex7 .vex_class = 7, +#define vex8 .vex_class = 8, +#define vex11 .vex_class = 11, +#define vex12 .vex_class = 12, +#define vex13 .vex_class = 13, + +#define chk(a) .check = X86_CHECK_##a, +#define svm(a) .intercept = SVM_EXIT_##a, + +#define avx2_256 .vex_special = X86_VEX_AVX2_256, + +#define P_00 1 +#define P_66 (1 << PREFIX_DATA) +#define P_F3 (1 << PREFIX_REPZ) +#define P_F2 (1 << PREFIX_REPNZ) + +#define p_00 .valid_prefix = P_00, +#define p_66 .valid_prefix = P_66, +#define p_f3 .valid_prefix = P_F3, +#define p_f2 .valid_prefix = P_F2, +#define p_00_66 .valid_prefix = P_00 | P_66, +#define p_00_f3 .valid_prefix = P_00 | P_F3, +#define p_66_f2 .valid_prefix = P_66 | P_F2, +#define p_00_66_f3 .valid_prefix = P_00 | P_66 | P_F3, +#define p_66_f3_f2 .valid_prefix = P_66 | P_F3 | P_F2, +#define p_00_66_f3_f2 .valid_prefix = P_00 | P_66 | P_F3 | P_F2, + +static uint8_t get_modrm(DisasContext *s, CPUX86State *env) +{ + if (!s->has_modrm) { + s->modrm = x86_ldub_code(env, s); + s->has_modrm = true; + } + return s->modrm; +} + +static inline const X86OpEntry *decode_by_prefix(DisasContext *s, const X86OpEntry entries[4]) +{ + if (s->prefix & PREFIX_REPNZ) { + return &entries[3]; + } else if (s->prefix & PREFIX_REPZ) { + return &entries[2]; + } else if (s->prefix & PREFIX_DATA) { + return &entries[1]; + } else { + return &entries[0]; + } +} + +static void decode_group15(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + /* only includes ldmxcsr and stmxcsr, because they have AVX variants. */ + static const X86OpEntry group15_reg[8] = { + }; + + static const X86OpEntry group15_mem[8] = { + [2] = X86_OP_ENTRYr(LDMXCSR, E,d, vex5 chk(VEX128)), + [3] = X86_OP_ENTRYw(STMXCSR, E,d, vex5 chk(VEX128)), + }; + + uint8_t modrm = get_modrm(s, env); + if ((modrm >> 6) == 3) { + *entry = group15_reg[(modrm >> 3) & 7]; + } else { + *entry = group15_mem[(modrm >> 3) & 7]; + } +} + +static void decode_group17(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86GenFunc group17_gen[8] = { + NULL, gen_BLSR, gen_BLSMSK, gen_BLSI, + }; + int op = (get_modrm(s, env) >> 3) & 7; + entry->gen = group17_gen[op]; +} + +static void decode_group12(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_group12[8] = { + {}, + {}, + X86_OP_ENTRY3(PSRLW_i, H,x, U,x, I,b, vex7 mmx avx2_256 p_00_66), + {}, + X86_OP_ENTRY3(PSRAW_i, H,x, U,x, I,b, vex7 mmx avx2_256 p_00_66), + {}, + X86_OP_ENTRY3(PSLLW_i, H,x, U,x, I,b, vex7 mmx avx2_256 p_00_66), + {}, + }; + + int op = (get_modrm(s, env) >> 3) & 7; + *entry = opcodes_group12[op]; +} + +static void decode_group13(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_group13[8] = { + {}, + {}, + X86_OP_ENTRY3(PSRLD_i, H,x, U,x, I,b, vex7 mmx avx2_256 p_00_66), + {}, + X86_OP_ENTRY3(PSRAD_i, H,x, U,x, I,b, vex7 mmx avx2_256 p_00_66), + {}, + X86_OP_ENTRY3(PSLLD_i, H,x, U,x, I,b, vex7 mmx avx2_256 p_00_66), + {}, + }; + + int op = (get_modrm(s, env) >> 3) & 7; + *entry = opcodes_group13[op]; +} + +static void decode_group14(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_group14[8] = { + /* grp14 */ + {}, + {}, + X86_OP_ENTRY3(PSRLQ_i, H,x, U,x, I,b, vex7 mmx avx2_256 p_00_66), + X86_OP_ENTRY3(PSRLDQ_i, H,x, U,x, I,b, vex7 avx2_256 p_66), + {}, + {}, + X86_OP_ENTRY3(PSLLQ_i, H,x, U,x, I,b, vex7 mmx avx2_256 p_00_66), + X86_OP_ENTRY3(PSLLDQ_i, H,x, U,x, I,b, vex7 avx2_256 p_66), + }; + + int op = (get_modrm(s, env) >> 3) & 7; + *entry = opcodes_group14[op]; +} + +static void decode_0F6F(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0F6F[4] = { + X86_OP_ENTRY3(MOVDQ, P,q, None,None, Q,q, vex5 mmx), /* movq */ + X86_OP_ENTRY3(MOVDQ, V,x, None,None, W,x, vex1), /* movdqa */ + X86_OP_ENTRY3(MOVDQ, V,x, None,None, W,x, vex4_unal), /* movdqu */ + {}, + }; + *entry = *decode_by_prefix(s, opcodes_0F6F); +} + +static void decode_0F70(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry pshufw[4] = { + X86_OP_ENTRY3(PSHUFW, P,q, Q,q, I,b, vex4 mmx), + X86_OP_ENTRY3(PSHUFD, V,x, W,x, I,b, vex4 avx2_256), + X86_OP_ENTRY3(PSHUFHW, V,x, W,x, I,b, vex4 avx2_256), + X86_OP_ENTRY3(PSHUFLW, V,x, W,x, I,b, vex4 avx2_256), + }; + + *entry = *decode_by_prefix(s, pshufw); +} + +static void decode_0F77(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + if (!(s->prefix & PREFIX_VEX)) { + entry->gen = gen_EMMS; + } else if (!s->vex_l) { + entry->gen = gen_VZEROUPPER; + entry->vex_class = 8; + } else { + entry->gen = gen_VZEROALL; + entry->vex_class = 8; + } +} + +static void decode_0F78(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0F78[4] = { + {}, + X86_OP_ENTRY3(EXTRQ_i, V,x, None,None, I,w, cpuid(SSE4A)), /* AMD extension */ + {}, + X86_OP_ENTRY3(INSERTQ_i, V,x, U,x, I,w, cpuid(SSE4A)), /* AMD extension */ + }; + *entry = *decode_by_prefix(s, opcodes_0F78); +} + +static void decode_0F79(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + if (s->prefix & PREFIX_REPNZ) { + entry->gen = gen_INSERTQ_r; /* AMD extension */ + } else if (s->prefix & PREFIX_DATA) { + entry->gen = gen_EXTRQ_r; /* AMD extension */ + } else { + entry->gen = NULL; + }; +} + +static void decode_0F7E(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0F7E[4] = { + X86_OP_ENTRY3(MOVD_from, E,y, None,None, P,y, vex5 mmx), + X86_OP_ENTRY3(MOVD_from, E,y, None,None, V,y, vex5), + X86_OP_ENTRY3(MOVQ, V,x, None,None, W,q, vex5), /* wrong dest Vy on SDM! */ + {}, + }; + *entry = *decode_by_prefix(s, opcodes_0F7E); +} + +static void decode_0F7F(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0F7F[4] = { + X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex5 mmx), /* movq */ + X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex1), /* movdqa */ + X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex4_unal), /* movdqu */ + {}, + }; + *entry = *decode_by_prefix(s, opcodes_0F7F); +} + +static void decode_0FD6(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry movq[4] = { + {}, + X86_OP_ENTRY3(MOVQ, W,x, None, None, V,q, vex5), + X86_OP_ENTRY3(MOVq_dq, V,dq, None, None, N,q), + X86_OP_ENTRY3(MOVq_dq, P,q, None, None, U,q), + }; + + *entry = *decode_by_prefix(s, movq); +} + +static const X86OpEntry opcodes_0F38_00toEF[240] = { + [0x00] = X86_OP_ENTRY3(PSHUFB, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + [0x01] = X86_OP_ENTRY3(PHADDW, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + [0x02] = X86_OP_ENTRY3(PHADDD, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + [0x03] = X86_OP_ENTRY3(PHADDSW, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + [0x04] = X86_OP_ENTRY3(PMADDUBSW, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + [0x05] = X86_OP_ENTRY3(PHSUBW, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + [0x06] = X86_OP_ENTRY3(PHSUBD, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + [0x07] = X86_OP_ENTRY3(PHSUBSW, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + + [0x10] = X86_OP_ENTRY2(PBLENDVB, V,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), + [0x13] = X86_OP_ENTRY2(VCVTPH2PS, V,x, W,xh, vex11 chk(W0) cpuid(F16C) p_66), + [0x14] = X86_OP_ENTRY2(BLENDVPS, V,x, W,x, vex4 cpuid(SSE41) p_66), + [0x15] = X86_OP_ENTRY2(BLENDVPD, V,x, W,x, vex4 cpuid(SSE41) p_66), + /* Listed incorrectly as type 4 */ + [0x16] = X86_OP_ENTRY3(VPERMD, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX2) p_66), /* vpermps */ + [0x17] = X86_OP_ENTRY3(VPTEST, None,None, V,x, W,x, vex4 cpuid(SSE41) p_66), + + /* + * Source operand listed as Mq/Ux and similar in the manual; incorrectly listed + * as 128-bit only in 2-17. + */ + [0x20] = X86_OP_ENTRY3(VPMOVSXBW, V,x, None,None, W,q, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), + [0x21] = X86_OP_ENTRY3(VPMOVSXBD, V,x, None,None, W,d, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), + [0x22] = X86_OP_ENTRY3(VPMOVSXBQ, V,x, None,None, W,w, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), + [0x23] = X86_OP_ENTRY3(VPMOVSXWD, V,x, None,None, W,q, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), + [0x24] = X86_OP_ENTRY3(VPMOVSXWQ, V,x, None,None, W,d, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), + [0x25] = X86_OP_ENTRY3(VPMOVSXDQ, V,x, None,None, W,q, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), + + /* Same as PMOVSX. */ + [0x30] = X86_OP_ENTRY3(VPMOVZXBW, V,x, None,None, W,q, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), + [0x31] = X86_OP_ENTRY3(VPMOVZXBD, V,x, None,None, W,d, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), + [0x32] = X86_OP_ENTRY3(VPMOVZXBQ, V,x, None,None, W,w, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), + [0x33] = X86_OP_ENTRY3(VPMOVZXWD, V,x, None,None, W,q, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), + [0x34] = X86_OP_ENTRY3(VPMOVZXWQ, V,x, None,None, W,d, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), + [0x35] = X86_OP_ENTRY3(VPMOVZXDQ, V,x, None,None, W,q, vex5 cpuid(SSE41) avx_movx avx2_256 p_66), + [0x36] = X86_OP_ENTRY3(VPERMD, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX2) p_66), + [0x37] = X86_OP_ENTRY3(PCMPGTQ, V,x, H,x, W,x, vex4 cpuid(SSE42) avx2_256 p_66), + + [0x40] = X86_OP_ENTRY3(PMULLD, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), + [0x41] = X86_OP_ENTRY3(VPHMINPOSUW, V,dq, None,None, W,dq, vex4 cpuid(SSE41) p_66), + /* Listed incorrectly as type 4 */ + [0x45] = X86_OP_ENTRY3(VPSRLV, V,x, H,x, W,x, vex6 cpuid(AVX2) p_66), + [0x46] = X86_OP_ENTRY3(VPSRAV, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX2) p_66), + [0x47] = X86_OP_ENTRY3(VPSLLV, V,x, H,x, W,x, vex6 cpuid(AVX2) p_66), + + [0x90] = X86_OP_ENTRY3(VPGATHERD, V,x, H,x, M,d, vex12 cpuid(AVX2) p_66), /* vpgatherdd/q */ + [0x91] = X86_OP_ENTRY3(VPGATHERQ, V,x, H,x, M,q, vex12 cpuid(AVX2) p_66), /* vpgatherqd/q */ + [0x92] = X86_OP_ENTRY3(VPGATHERD, V,x, H,x, M,d, vex12 cpuid(AVX2) p_66), /* vgatherdps/d */ + [0x93] = X86_OP_ENTRY3(VPGATHERQ, V,x, H,x, M,q, vex12 cpuid(AVX2) p_66), /* vgatherqps/d */ + + /* Should be exception type 2 but they do not have legacy SSE equivalents? */ + [0x96] = X86_OP_ENTRY3(VFMADDSUB132Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0x97] = X86_OP_ENTRY3(VFMSUBADD132Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + + [0xa6] = X86_OP_ENTRY3(VFMADDSUB213Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xa7] = X86_OP_ENTRY3(VFMSUBADD213Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + + [0xb6] = X86_OP_ENTRY3(VFMADDSUB231Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xb7] = X86_OP_ENTRY3(VFMSUBADD231Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + + [0x08] = X86_OP_ENTRY3(PSIGNB, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + [0x09] = X86_OP_ENTRY3(PSIGNW, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + [0x0a] = X86_OP_ENTRY3(PSIGND, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + [0x0b] = X86_OP_ENTRY3(PMULHRSW, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + /* Listed incorrectly as type 4 */ + [0x0c] = X86_OP_ENTRY3(VPERMILPS, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX) p_00_66), + [0x0d] = X86_OP_ENTRY3(VPERMILPD, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX) p_66), + [0x0e] = X86_OP_ENTRY3(VTESTPS, None,None, V,x, W,x, vex6 chk(W0) cpuid(AVX) p_66), + [0x0f] = X86_OP_ENTRY3(VTESTPD, None,None, V,x, W,x, vex6 chk(W0) cpuid(AVX) p_66), + + [0x18] = X86_OP_ENTRY3(VPBROADCASTD, V,x, None,None, W,d, vex6 chk(W0) cpuid(AVX) p_66), /* vbroadcastss */ + [0x19] = X86_OP_ENTRY3(VPBROADCASTQ, V,qq, None,None, W,q, vex6 chk(W0) cpuid(AVX) p_66), /* vbroadcastsd */ + [0x1a] = X86_OP_ENTRY3(VBROADCASTx128, V,qq, None,None, WM,dq,vex6 chk(W0) cpuid(AVX) p_66), + [0x1c] = X86_OP_ENTRY3(PABSB, V,x, None,None, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + [0x1d] = X86_OP_ENTRY3(PABSW, V,x, None,None, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + [0x1e] = X86_OP_ENTRY3(PABSD, V,x, None,None, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + + [0x28] = X86_OP_ENTRY3(PMULDQ, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), + [0x29] = X86_OP_ENTRY3(PCMPEQQ, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), + [0x2a] = X86_OP_ENTRY3(MOVDQ, V,x, None,None, WM,x, vex1 cpuid(SSE41) avx2_256 p_66), /* movntdqa */ + [0x2b] = X86_OP_ENTRY3(VPACKUSDW, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), + [0x2c] = X86_OP_ENTRY3(VMASKMOVPS, V,x, H,x, WM,x, vex6 chk(W0) cpuid(AVX) p_66), + [0x2d] = X86_OP_ENTRY3(VMASKMOVPD, V,x, H,x, WM,x, vex6 chk(W0) cpuid(AVX) p_66), + /* Incorrectly listed as Mx,Hx,Vx in the manual */ + [0x2e] = X86_OP_ENTRY3(VMASKMOVPS_st, M,x, V,x, H,x, vex6 chk(W0) cpuid(AVX) p_66), + [0x2f] = X86_OP_ENTRY3(VMASKMOVPD_st, M,x, V,x, H,x, vex6 chk(W0) cpuid(AVX) p_66), + + [0x38] = X86_OP_ENTRY3(PMINSB, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), + [0x39] = X86_OP_ENTRY3(PMINSD, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), + [0x3a] = X86_OP_ENTRY3(PMINUW, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), + [0x3b] = X86_OP_ENTRY3(PMINUD, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), + [0x3c] = X86_OP_ENTRY3(PMAXSB, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), + [0x3d] = X86_OP_ENTRY3(PMAXSD, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), + [0x3e] = X86_OP_ENTRY3(PMAXUW, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), + [0x3f] = X86_OP_ENTRY3(PMAXUD, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), + + /* VPBROADCASTQ not listed as W0 in table 2-16 */ + [0x58] = X86_OP_ENTRY3(VPBROADCASTD, V,x, None,None, W,d, vex6 chk(W0) cpuid(AVX2) p_66), + [0x59] = X86_OP_ENTRY3(VPBROADCASTQ, V,x, None,None, W,q, vex6 chk(W0) cpuid(AVX2) p_66), + [0x5a] = X86_OP_ENTRY3(VBROADCASTx128, V,qq, None,None, WM,dq,vex6 chk(W0) cpuid(AVX2) p_66), + + [0x78] = X86_OP_ENTRY3(VPBROADCASTB, V,x, None,None, W,b, vex6 chk(W0) cpuid(AVX2) p_66), + [0x79] = X86_OP_ENTRY3(VPBROADCASTW, V,x, None,None, W,w, vex6 chk(W0) cpuid(AVX2) p_66), + + [0x8c] = X86_OP_ENTRY3(VPMASKMOV, V,x, H,x, WM,x, vex6 cpuid(AVX2) p_66), + [0x8e] = X86_OP_ENTRY3(VPMASKMOV_st, M,x, V,x, H,x, vex6 cpuid(AVX2) p_66), + + /* Should be exception type 2 or 3 but they do not have legacy SSE equivalents? */ + [0x98] = X86_OP_ENTRY3(VFMADD132Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0x99] = X86_OP_ENTRY3(VFMADD132Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0x9a] = X86_OP_ENTRY3(VFMSUB132Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0x9b] = X86_OP_ENTRY3(VFMSUB132Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0x9c] = X86_OP_ENTRY3(VFNMADD132Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0x9d] = X86_OP_ENTRY3(VFNMADD132Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0x9e] = X86_OP_ENTRY3(VFNMSUB132Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0x9f] = X86_OP_ENTRY3(VFNMSUB132Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + + [0xa8] = X86_OP_ENTRY3(VFMADD213Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xa9] = X86_OP_ENTRY3(VFMADD213Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xaa] = X86_OP_ENTRY3(VFMSUB213Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xab] = X86_OP_ENTRY3(VFMSUB213Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xac] = X86_OP_ENTRY3(VFNMADD213Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xad] = X86_OP_ENTRY3(VFNMADD213Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xae] = X86_OP_ENTRY3(VFNMSUB213Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xaf] = X86_OP_ENTRY3(VFNMSUB213Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + + [0xb8] = X86_OP_ENTRY3(VFMADD231Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xb9] = X86_OP_ENTRY3(VFMADD231Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xba] = X86_OP_ENTRY3(VFMSUB231Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xbb] = X86_OP_ENTRY3(VFMSUB231Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xbc] = X86_OP_ENTRY3(VFNMADD231Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xbd] = X86_OP_ENTRY3(VFNMADD231Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xbe] = X86_OP_ENTRY3(VFNMSUB231Px, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + [0xbf] = X86_OP_ENTRY3(VFNMSUB231Sx, V,x, H,x, W,x, vex6 cpuid(FMA) p_66), + + [0xc8] = X86_OP_ENTRY2(SHA1NEXTE, V,dq, W,dq, cpuid(SHA_NI)), + [0xc9] = X86_OP_ENTRY2(SHA1MSG1, V,dq, W,dq, cpuid(SHA_NI)), + [0xca] = X86_OP_ENTRY2(SHA1MSG2, V,dq, W,dq, cpuid(SHA_NI)), + [0xcb] = X86_OP_ENTRY2(SHA256RNDS2, V,dq, W,dq, cpuid(SHA_NI)), + [0xcc] = X86_OP_ENTRY2(SHA256MSG1, V,dq, W,dq, cpuid(SHA_NI)), + [0xcd] = X86_OP_ENTRY2(SHA256MSG2, V,dq, W,dq, cpuid(SHA_NI)), + + [0xdb] = X86_OP_ENTRY3(VAESIMC, V,dq, None,None, W,dq, vex4 cpuid(AES) p_66), + [0xdc] = X86_OP_ENTRY3(VAESENC, V,x, H,x, W,x, vex4 cpuid(AES) p_66), + [0xdd] = X86_OP_ENTRY3(VAESENCLAST, V,x, H,x, W,x, vex4 cpuid(AES) p_66), + [0xde] = X86_OP_ENTRY3(VAESDEC, V,x, H,x, W,x, vex4 cpuid(AES) p_66), + [0xdf] = X86_OP_ENTRY3(VAESDECLAST, V,x, H,x, W,x, vex4 cpuid(AES) p_66), + + /* + * REG selects srcdest2 operand, VEX.vvvv selects src3. VEX class not found + * in manual, assumed to be 13 from the VEX.L0 constraint. + */ + [0xe0] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + [0xe1] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + [0xe2] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + [0xe3] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + [0xe4] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + [0xe5] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + [0xe6] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + [0xe7] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + + [0xe8] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + [0xe9] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + [0xea] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + [0xeb] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + [0xec] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + [0xed] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + [0xee] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), + [0xef] = X86_OP_ENTRY3(CMPccXADD, M,y, G,y, B,y, vex13 xchg chk(o64) cpuid(CMPCCXADD) p_66), +}; + +/* five rows for no prefix, 66, F3, F2, 66+F2 */ +static const X86OpEntry opcodes_0F38_F0toFF[16][5] = { + [0] = { + X86_OP_ENTRY3(MOVBE, G,y, M,y, None,None, cpuid(MOVBE)), + X86_OP_ENTRY3(MOVBE, G,w, M,w, None,None, cpuid(MOVBE)), + {}, + X86_OP_ENTRY2(CRC32, G,d, E,b, cpuid(SSE42)), + X86_OP_ENTRY2(CRC32, G,d, E,b, cpuid(SSE42)), + }, + [1] = { + X86_OP_ENTRY3(MOVBE, M,y, G,y, None,None, cpuid(MOVBE)), + X86_OP_ENTRY3(MOVBE, M,w, G,w, None,None, cpuid(MOVBE)), + {}, + X86_OP_ENTRY2(CRC32, G,d, E,y, cpuid(SSE42)), + X86_OP_ENTRY2(CRC32, G,d, E,w, cpuid(SSE42)), + }, + [2] = { + X86_OP_ENTRY3(ANDN, G,y, B,y, E,y, vex13 cpuid(BMI1)), + {}, + {}, + {}, + {}, + }, + [3] = { + X86_OP_GROUP3(group17, B,y, E,y, None,None, vex13 cpuid(BMI1)), + {}, + {}, + {}, + {}, + }, + [5] = { + X86_OP_ENTRY3(BZHI, G,y, E,y, B,y, vex13 cpuid(BMI1)), + {}, + X86_OP_ENTRY3(PEXT, G,y, B,y, E,y, vex13 zextT0 cpuid(BMI2)), + X86_OP_ENTRY3(PDEP, G,y, B,y, E,y, vex13 zextT0 cpuid(BMI2)), + {}, + }, + [6] = { + {}, + X86_OP_ENTRY2(ADCX, G,y, E,y, cpuid(ADX)), + X86_OP_ENTRY2(ADOX, G,y, E,y, cpuid(ADX)), + X86_OP_ENTRY3(MULX, /* B,y, */ G,y, E,y, 2,y, vex13 cpuid(BMI2)), + {}, + }, + [7] = { + X86_OP_ENTRY3(BEXTR, G,y, E,y, B,y, vex13 zextT0 cpuid(BMI1)), + X86_OP_ENTRY3(SHLX, G,y, E,y, B,y, vex13 cpuid(BMI1)), + X86_OP_ENTRY3(SARX, G,y, E,y, B,y, vex13 sextT0 cpuid(BMI1)), + X86_OP_ENTRY3(SHRX, G,y, E,y, B,y, vex13 zextT0 cpuid(BMI1)), + {}, + }, +}; + +static void decode_0F38(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + *b = x86_ldub_code(env, s); + if (*b < 0xf0) { + *entry = opcodes_0F38_00toEF[*b]; + } else { + int row = 0; + if (s->prefix & PREFIX_REPZ) { + /* The REPZ (F3) prefix has priority over 66 */ + row = 2; + } else { + row += s->prefix & PREFIX_REPNZ ? 3 : 0; + row += s->prefix & PREFIX_DATA ? 1 : 0; + } + *entry = opcodes_0F38_F0toFF[*b & 15][row]; + } +} + +static void decode_VINSERTPS(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry + vinsertps_reg = X86_OP_ENTRY4(VINSERTPS_r, V,dq, H,dq, U,dq, vex5 cpuid(SSE41) p_66), + vinsertps_mem = X86_OP_ENTRY4(VINSERTPS_m, V,dq, H,dq, M,d, vex5 cpuid(SSE41) p_66); + + int modrm = get_modrm(s, env); + *entry = (modrm >> 6) == 3 ? vinsertps_reg : vinsertps_mem; +} + +static const X86OpEntry opcodes_0F3A[256] = { + /* + * These are VEX-only, but incorrectly listed in the manual as exception type 4. + * Also the "qq" instructions are sometimes omitted by Table 2-17, but are VEX256 + * only. + */ + [0x00] = X86_OP_ENTRY3(VPERMQ, V,qq, W,qq, I,b, vex6 chk(W1) cpuid(AVX2) p_66), + [0x01] = X86_OP_ENTRY3(VPERMQ, V,qq, W,qq, I,b, vex6 chk(W1) cpuid(AVX2) p_66), /* VPERMPD */ + [0x02] = X86_OP_ENTRY4(VBLENDPS, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX2) p_66), /* VPBLENDD */ + [0x04] = X86_OP_ENTRY3(VPERMILPS_i, V,x, W,x, I,b, vex6 chk(W0) cpuid(AVX) p_66), + [0x05] = X86_OP_ENTRY3(VPERMILPD_i, V,x, W,x, I,b, vex6 chk(W0) cpuid(AVX) p_66), + [0x06] = X86_OP_ENTRY4(VPERM2x128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX) p_66), + + [0x14] = X86_OP_ENTRY3(PEXTRB, E,b, V,dq, I,b, vex5 cpuid(SSE41) op0_Rd p_66), + [0x15] = X86_OP_ENTRY3(PEXTRW, E,w, V,dq, I,b, vex5 cpuid(SSE41) op0_Rd p_66), + [0x16] = X86_OP_ENTRY3(PEXTR, E,y, V,dq, I,b, vex5 cpuid(SSE41) p_66), + [0x17] = X86_OP_ENTRY3(VEXTRACTPS, E,d, V,dq, I,b, vex5 cpuid(SSE41) p_66), + [0x1d] = X86_OP_ENTRY3(VCVTPS2PH, W,xh, V,x, I,b, vex11 chk(W0) cpuid(F16C) p_66), + + [0x20] = X86_OP_ENTRY4(PINSRB, V,dq, H,dq, E,b, vex5 cpuid(SSE41) op2_Ry p_66), + [0x21] = X86_OP_GROUP0(VINSERTPS), + [0x22] = X86_OP_ENTRY4(PINSR, V,dq, H,dq, E,y, vex5 cpuid(SSE41) p_66), + + [0x40] = X86_OP_ENTRY4(VDDPS, V,x, H,x, W,x, vex2 cpuid(SSE41) p_66), + [0x41] = X86_OP_ENTRY4(VDDPD, V,dq, H,dq, W,dq, vex2 cpuid(SSE41) p_66), + [0x42] = X86_OP_ENTRY4(VMPSADBW, V,x, H,x, W,x, vex2 cpuid(SSE41) avx2_256 p_66), + [0x44] = X86_OP_ENTRY4(PCLMULQDQ, V,dq, H,dq, W,dq, vex4 cpuid(PCLMULQDQ) p_66), + [0x46] = X86_OP_ENTRY4(VPERM2x128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX2) p_66), + + [0x60] = X86_OP_ENTRY4(PCMPESTRM, None,None, V,dq, W,dq, vex4_unal cpuid(SSE42) p_66), + [0x61] = X86_OP_ENTRY4(PCMPESTRI, None,None, V,dq, W,dq, vex4_unal cpuid(SSE42) p_66), + [0x62] = X86_OP_ENTRY4(PCMPISTRM, None,None, V,dq, W,dq, vex4_unal cpuid(SSE42) p_66), + [0x63] = X86_OP_ENTRY4(PCMPISTRI, None,None, V,dq, W,dq, vex4_unal cpuid(SSE42) p_66), + + [0x08] = X86_OP_ENTRY3(VROUNDPS, V,x, W,x, I,b, vex2 cpuid(SSE41) p_66), + [0x09] = X86_OP_ENTRY3(VROUNDPD, V,x, W,x, I,b, vex2 cpuid(SSE41) p_66), + /* + * Not listed as four operand in the manual. Also writes and reads 128-bits + * from the first two operands due to the V operand picking higher entries of + * the H operand; the "Vss,Hss,Wss" description from the manual is incorrect. + * For other unary operations such as VSQRTSx this is hidden by the "REPScalar" + * value of vex_special, because the table lists the operand types of VSQRTPx. + */ + [0x0a] = X86_OP_ENTRY4(VROUNDSS, V,x, H,x, W,ss, vex3 cpuid(SSE41) p_66), + [0x0b] = X86_OP_ENTRY4(VROUNDSD, V,x, H,x, W,sd, vex3 cpuid(SSE41) p_66), + [0x0c] = X86_OP_ENTRY4(VBLENDPS, V,x, H,x, W,x, vex4 cpuid(SSE41) p_66), + [0x0d] = X86_OP_ENTRY4(VBLENDPD, V,x, H,x, W,x, vex4 cpuid(SSE41) p_66), + [0x0e] = X86_OP_ENTRY4(VPBLENDW, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), + [0x0f] = X86_OP_ENTRY4(PALIGNR, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), + + [0x18] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX) p_66), + [0x19] = X86_OP_ENTRY3(VEXTRACTx128, W,dq, V,qq, I,b, vex6 chk(W0) cpuid(AVX) p_66), + + [0x38] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX2) p_66), + [0x39] = X86_OP_ENTRY3(VEXTRACTx128, W,dq, V,qq, I,b, vex6 chk(W0) cpuid(AVX2) p_66), + + /* Listed incorrectly as type 4 */ + [0x4a] = X86_OP_ENTRY4(VBLENDVPS, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX) p_66), + [0x4b] = X86_OP_ENTRY4(VBLENDVPD, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX) p_66), + [0x4c] = X86_OP_ENTRY4(VPBLENDVB, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX) p_66 avx2_256), + + [0xcc] = X86_OP_ENTRY3(SHA1RNDS4, V,dq, W,dq, I,b, cpuid(SHA_NI)), + + [0xdf] = X86_OP_ENTRY3(VAESKEYGEN, V,dq, W,dq, I,b, vex4 cpuid(AES) p_66), + + [0xF0] = X86_OP_ENTRY3(RORX, G,y, E,y, I,b, vex13 cpuid(BMI2) p_f2), +}; + +static void decode_0F3A(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + *b = x86_ldub_code(env, s); + *entry = opcodes_0F3A[*b]; +} + +/* + * There are some mistakes in the operands in the manual, and the load/store/register + * cases are easiest to keep separate, so the entries for 10-17 follow simplicity and + * efficiency of implementation rather than copying what the manual says. + * + * In particular: + * + * 1) "VMOVSS m32, xmm1" and "VMOVSD m64, xmm1" do not support VEX.vvvv != 1111b, + * but this is not mentioned in the tables. + * + * 2) MOVHLPS, MOVHPS, MOVHPD, MOVLPD, MOVLPS read the high quadword of one of their + * operands, which must therefore be dq; MOVLPD and MOVLPS also write the high + * quadword of the V operand. + */ +static void decode_0F10(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0F10_reg[4] = { + X86_OP_ENTRY3(MOVDQ, V,x, None,None, W,x, vex4_unal), /* MOVUPS */ + X86_OP_ENTRY3(MOVDQ, V,x, None,None, W,x, vex4_unal), /* MOVUPD */ + X86_OP_ENTRY3(VMOVSS, V,x, H,x, W,x, vex5), + X86_OP_ENTRY3(VMOVLPx, V,x, H,x, W,x, vex5), /* MOVSD */ + }; + + static const X86OpEntry opcodes_0F10_mem[4] = { + X86_OP_ENTRY3(MOVDQ, V,x, None,None, W,x, vex4_unal), /* MOVUPS */ + X86_OP_ENTRY3(MOVDQ, V,x, None,None, W,x, vex4_unal), /* MOVUPD */ + X86_OP_ENTRY3(VMOVSS_ld, V,x, H,x, M,ss, vex5), + X86_OP_ENTRY3(VMOVSD_ld, V,x, H,x, M,sd, vex5), + }; + + if ((get_modrm(s, env) >> 6) == 3) { + *entry = *decode_by_prefix(s, opcodes_0F10_reg); + } else { + *entry = *decode_by_prefix(s, opcodes_0F10_mem); + } +} + +static void decode_0F11(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0F11_reg[4] = { + X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex4), /* MOVUPS */ + X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex4), /* MOVUPD */ + X86_OP_ENTRY3(VMOVSS, W,x, H,x, V,x, vex5), + X86_OP_ENTRY3(VMOVLPx, W,x, H,x, V,q, vex5), /* MOVSD */ + }; + + static const X86OpEntry opcodes_0F11_mem[4] = { + X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex4), /* MOVUPS */ + X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex4), /* MOVUPD */ + X86_OP_ENTRY3(VMOVSS_st, M,ss, None,None, V,x, vex5), + X86_OP_ENTRY3(VMOVLPx_st, M,sd, None,None, V,x, vex5), /* MOVSD */ + }; + + if ((get_modrm(s, env) >> 6) == 3) { + *entry = *decode_by_prefix(s, opcodes_0F11_reg); + } else { + *entry = *decode_by_prefix(s, opcodes_0F11_mem); + } +} + +static void decode_0F12(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0F12_mem[4] = { + /* + * Use dq for operand for compatibility with gen_MOVSD and + * to allow VEX128 only. + */ + X86_OP_ENTRY3(VMOVLPx_ld, V,dq, H,dq, M,q, vex5), /* MOVLPS */ + X86_OP_ENTRY3(VMOVLPx_ld, V,dq, H,dq, M,q, vex5), /* MOVLPD */ + X86_OP_ENTRY3(VMOVSLDUP, V,x, None,None, W,x, vex4 cpuid(SSE3)), + X86_OP_ENTRY3(VMOVDDUP, V,x, None,None, WM,q, vex5 cpuid(SSE3)), /* qq if VEX.256 */ + }; + static const X86OpEntry opcodes_0F12_reg[4] = { + X86_OP_ENTRY3(VMOVHLPS, V,dq, H,dq, U,dq, vex7), + X86_OP_ENTRY3(VMOVLPx, W,x, H,x, U,q, vex5), /* MOVLPD */ + X86_OP_ENTRY3(VMOVSLDUP, V,x, None,None, U,x, vex4 cpuid(SSE3)), + X86_OP_ENTRY3(VMOVDDUP, V,x, None,None, U,x, vex5 cpuid(SSE3)), + }; + + if ((get_modrm(s, env) >> 6) == 3) { + *entry = *decode_by_prefix(s, opcodes_0F12_reg); + } else { + *entry = *decode_by_prefix(s, opcodes_0F12_mem); + if ((s->prefix & PREFIX_REPNZ) && s->vex_l) { + entry->s2 = X86_SIZE_qq; + } + } +} + +static void decode_0F16(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0F16_mem[4] = { + /* + * Operand 1 technically only reads the low 64 bits, but uses dq so that + * it is easier to check for op0 == op1 in an endianness-neutral manner. + */ + X86_OP_ENTRY3(VMOVHPx_ld, V,dq, H,dq, M,q, vex5), /* MOVHPS */ + X86_OP_ENTRY3(VMOVHPx_ld, V,dq, H,dq, M,q, vex5), /* MOVHPD */ + X86_OP_ENTRY3(VMOVSHDUP, V,x, None,None, W,x, vex4 cpuid(SSE3)), + {}, + }; + static const X86OpEntry opcodes_0F16_reg[4] = { + /* Same as above, operand 1 could be Hq if it wasn't for big-endian. */ + X86_OP_ENTRY3(VMOVLHPS, V,dq, H,dq, U,q, vex7), + X86_OP_ENTRY3(VMOVHPx, V,x, H,x, U,x, vex5), /* MOVHPD */ + X86_OP_ENTRY3(VMOVSHDUP, V,x, None,None, U,x, vex4 cpuid(SSE3)), + {}, + }; + + if ((get_modrm(s, env) >> 6) == 3) { + *entry = *decode_by_prefix(s, opcodes_0F16_reg); + } else { + *entry = *decode_by_prefix(s, opcodes_0F16_mem); + } +} + +static void decode_0F2A(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0F2A[4] = { + X86_OP_ENTRY3(CVTPI2Px, V,x, None,None, Q,q), + X86_OP_ENTRY3(CVTPI2Px, V,x, None,None, Q,q), + X86_OP_ENTRY3(VCVTSI2Sx, V,x, H,x, E,y, vex3), + X86_OP_ENTRY3(VCVTSI2Sx, V,x, H,x, E,y, vex3), + }; + *entry = *decode_by_prefix(s, opcodes_0F2A); +} + +static void decode_0F2B(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0F2B[4] = { + X86_OP_ENTRY3(MOVDQ, M,x, None,None, V,x, vex1), /* MOVNTPS */ + X86_OP_ENTRY3(MOVDQ, M,x, None,None, V,x, vex1), /* MOVNTPD */ + /* AMD extensions */ + X86_OP_ENTRY3(VMOVSS_st, M,ss, None,None, V,x, vex4 cpuid(SSE4A)), /* MOVNTSS */ + X86_OP_ENTRY3(VMOVLPx_st, M,sd, None,None, V,x, vex4 cpuid(SSE4A)), /* MOVNTSD */ + }; + + *entry = *decode_by_prefix(s, opcodes_0F2B); +} + +static void decode_0F2C(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0F2C[4] = { + /* Listed as ps/pd in the manual, but CVTTPS2PI only reads 64-bit. */ + X86_OP_ENTRY3(CVTTPx2PI, P,q, None,None, W,q), + X86_OP_ENTRY3(CVTTPx2PI, P,q, None,None, W,dq), + X86_OP_ENTRY3(VCVTTSx2SI, G,y, None,None, W,ss, vex3), + X86_OP_ENTRY3(VCVTTSx2SI, G,y, None,None, W,sd, vex3), + }; + *entry = *decode_by_prefix(s, opcodes_0F2C); +} + +static void decode_0F2D(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0F2D[4] = { + /* Listed as ps/pd in the manual, but CVTPS2PI only reads 64-bit. */ + X86_OP_ENTRY3(CVTPx2PI, P,q, None,None, W,q), + X86_OP_ENTRY3(CVTPx2PI, P,q, None,None, W,dq), + X86_OP_ENTRY3(VCVTSx2SI, G,y, None,None, W,ss, vex3), + X86_OP_ENTRY3(VCVTSx2SI, G,y, None,None, W,sd, vex3), + }; + *entry = *decode_by_prefix(s, opcodes_0F2D); +} + +static void decode_VxCOMISx(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + /* + * VUCOMISx and VCOMISx are different and use no-prefix and 0x66 for SS and SD + * respectively. Scalar values usually are associated with 0xF2 and 0xF3, for + * which X86_VEX_REPScalar exists, but here it has to be decoded by hand. + */ + entry->s1 = entry->s2 = (s->prefix & PREFIX_DATA ? X86_SIZE_sd : X86_SIZE_ss); + entry->gen = (*b == 0x2E ? gen_VUCOMI : gen_VCOMI); +} + +static void decode_sse_unary(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + if (!(s->prefix & (PREFIX_REPZ | PREFIX_REPNZ))) { + entry->op1 = X86_TYPE_None; + entry->s1 = X86_SIZE_None; + } + switch (*b) { + case 0x51: entry->gen = gen_VSQRT; break; + case 0x52: entry->gen = gen_VRSQRT; break; + case 0x53: entry->gen = gen_VRCP; break; + } +} + +static void decode_0F5A(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0F5A[4] = { + X86_OP_ENTRY2(VCVTPS2PD, V,x, W,xh, vex2), /* VCVTPS2PD */ + X86_OP_ENTRY2(VCVTPD2PS, V,x, W,x, vex2), /* VCVTPD2PS */ + X86_OP_ENTRY3(VCVTSS2SD, V,x, H,x, W,x, vex2_rep3), /* VCVTSS2SD */ + X86_OP_ENTRY3(VCVTSD2SS, V,x, H,x, W,x, vex2_rep3), /* VCVTSD2SS */ + }; + *entry = *decode_by_prefix(s, opcodes_0F5A); +} + +static void decode_0F5B(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0F5B[4] = { + X86_OP_ENTRY2(VCVTDQ2PS, V,x, W,x, vex2), + X86_OP_ENTRY2(VCVTPS2DQ, V,x, W,x, vex2), + X86_OP_ENTRY2(VCVTTPS2DQ, V,x, W,x, vex2), + {}, + }; + *entry = *decode_by_prefix(s, opcodes_0F5B); +} + +static void decode_0FE6(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_0FE6[4] = { + {}, + X86_OP_ENTRY2(VCVTTPD2DQ, V,x, W,x, vex2), + X86_OP_ENTRY2(VCVTDQ2PD, V,x, W,x, vex5), + X86_OP_ENTRY2(VCVTPD2DQ, V,x, W,x, vex2), + }; + *entry = *decode_by_prefix(s, opcodes_0FE6); +} + +static const X86OpEntry opcodes_0F[256] = { + [0x0E] = X86_OP_ENTRY0(EMMS, cpuid(3DNOW)), /* femms */ + /* + * 3DNow!'s opcode byte comes *after* modrm and displacements, making it + * more like an Ib operand. Dispatch to the right helper in a single gen_* + * function. + */ + [0x0F] = X86_OP_ENTRY3(3dnow, P,q, Q,q, I,b, cpuid(3DNOW)), + + [0x10] = X86_OP_GROUP0(0F10), + [0x11] = X86_OP_GROUP0(0F11), + [0x12] = X86_OP_GROUP0(0F12), + [0x13] = X86_OP_ENTRY3(VMOVLPx_st, M,q, None,None, V,q, vex5 p_00_66), + [0x14] = X86_OP_ENTRY3(VUNPCKLPx, V,x, H,x, W,x, vex4 p_00_66), + [0x15] = X86_OP_ENTRY3(VUNPCKHPx, V,x, H,x, W,x, vex4 p_00_66), + [0x16] = X86_OP_GROUP0(0F16), + /* Incorrectly listed as Mq,Vq in the manual */ + [0x17] = X86_OP_ENTRY3(VMOVHPx_st, M,q, None,None, V,dq, vex5 p_00_66), + + [0x50] = X86_OP_ENTRY3(MOVMSK, G,y, None,None, U,x, vex7 p_00_66), + [0x51] = X86_OP_GROUP3(sse_unary, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), /* sqrtps */ + [0x52] = X86_OP_GROUP3(sse_unary, V,x, H,x, W,x, vex4_rep5 p_00_f3), /* rsqrtps */ + [0x53] = X86_OP_GROUP3(sse_unary, V,x, H,x, W,x, vex4_rep5 p_00_f3), /* rcpps */ + [0x54] = X86_OP_ENTRY3(PAND, V,x, H,x, W,x, vex4 p_00_66), /* vand */ + [0x55] = X86_OP_ENTRY3(PANDN, V,x, H,x, W,x, vex4 p_00_66), /* vandn */ + [0x56] = X86_OP_ENTRY3(POR, V,x, H,x, W,x, vex4 p_00_66), /* vor */ + [0x57] = X86_OP_ENTRY3(PXOR, V,x, H,x, W,x, vex4 p_00_66), /* vxor */ + + [0x60] = X86_OP_ENTRY3(PUNPCKLBW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0x61] = X86_OP_ENTRY3(PUNPCKLWD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0x62] = X86_OP_ENTRY3(PUNPCKLDQ, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0x63] = X86_OP_ENTRY3(PACKSSWB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0x64] = X86_OP_ENTRY3(PCMPGTB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0x65] = X86_OP_ENTRY3(PCMPGTW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0x66] = X86_OP_ENTRY3(PCMPGTD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0x67] = X86_OP_ENTRY3(PACKUSWB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + + [0x70] = X86_OP_GROUP0(0F70), + [0x71] = X86_OP_GROUP0(group12), + [0x72] = X86_OP_GROUP0(group13), + [0x73] = X86_OP_GROUP0(group14), + [0x74] = X86_OP_ENTRY3(PCMPEQB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0x75] = X86_OP_ENTRY3(PCMPEQW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0x76] = X86_OP_ENTRY3(PCMPEQD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0x77] = X86_OP_GROUP0(0F77), + + [0x28] = X86_OP_ENTRY3(MOVDQ, V,x, None,None, W,x, vex1 p_00_66), /* MOVAPS */ + [0x29] = X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex1 p_00_66), /* MOVAPS */ + [0x2A] = X86_OP_GROUP0(0F2A), + [0x2B] = X86_OP_GROUP0(0F2B), + [0x2C] = X86_OP_GROUP0(0F2C), + [0x2D] = X86_OP_GROUP0(0F2D), + [0x2E] = X86_OP_GROUP3(VxCOMISx, None,None, V,x, W,x, vex3 p_00_66), /* VUCOMISS/SD */ + [0x2F] = X86_OP_GROUP3(VxCOMISx, None,None, V,x, W,x, vex3 p_00_66), /* VCOMISS/SD */ + + [0x38] = X86_OP_GROUP0(0F38), + [0x3a] = X86_OP_GROUP0(0F3A), + + [0x58] = X86_OP_ENTRY3(VADD, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), + [0x59] = X86_OP_ENTRY3(VMUL, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), + [0x5a] = X86_OP_GROUP0(0F5A), + [0x5b] = X86_OP_GROUP0(0F5B), + [0x5c] = X86_OP_ENTRY3(VSUB, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), + [0x5d] = X86_OP_ENTRY3(VMIN, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), + [0x5e] = X86_OP_ENTRY3(VDIV, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), + [0x5f] = X86_OP_ENTRY3(VMAX, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), + + [0x68] = X86_OP_ENTRY3(PUNPCKHBW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0x69] = X86_OP_ENTRY3(PUNPCKHWD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0x6a] = X86_OP_ENTRY3(PUNPCKHDQ, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0x6b] = X86_OP_ENTRY3(PACKSSDW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0x6c] = X86_OP_ENTRY3(PUNPCKLQDQ, V,x, H,x, W,x, vex4 p_66 avx2_256), + [0x6d] = X86_OP_ENTRY3(PUNPCKHQDQ, V,x, H,x, W,x, vex4 p_66 avx2_256), + [0x6e] = X86_OP_ENTRY3(MOVD_to, V,x, None,None, E,y, vex5 mmx p_00_66), /* wrong dest Vy on SDM! */ + [0x6f] = X86_OP_GROUP0(0F6F), + + [0x78] = X86_OP_GROUP0(0F78), + [0x79] = X86_OP_GROUP2(0F79, V,x, U,x, cpuid(SSE4A)), + [0x7c] = X86_OP_ENTRY3(VHADD, V,x, H,x, W,x, vex2 cpuid(SSE3) p_66_f2), + [0x7d] = X86_OP_ENTRY3(VHSUB, V,x, H,x, W,x, vex2 cpuid(SSE3) p_66_f2), + [0x7e] = X86_OP_GROUP0(0F7E), + [0x7f] = X86_OP_GROUP0(0F7F), + + [0xae] = X86_OP_GROUP0(group15), + + [0xc2] = X86_OP_ENTRY4(VCMP, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), + [0xc4] = X86_OP_ENTRY4(PINSRW, V,dq,H,dq,E,w, vex5 mmx p_00_66), + [0xc5] = X86_OP_ENTRY3(PEXTRW, G,d, U,dq,I,b, vex5 mmx p_00_66), + [0xc6] = X86_OP_ENTRY4(VSHUF, V,x, H,x, W,x, vex4 p_00_66), + + [0xd0] = X86_OP_ENTRY3(VADDSUB, V,x, H,x, W,x, vex2 cpuid(SSE3) p_66_f2), + [0xd1] = X86_OP_ENTRY3(PSRLW_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd2] = X86_OP_ENTRY3(PSRLD_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd3] = X86_OP_ENTRY3(PSRLQ_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd4] = X86_OP_ENTRY3(PADDQ, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd5] = X86_OP_ENTRY3(PMULLW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd6] = X86_OP_GROUP0(0FD6), + [0xd7] = X86_OP_ENTRY3(PMOVMSKB, G,d, None,None, U,x, vex7 mmx avx2_256 p_00_66), + + [0xe0] = X86_OP_ENTRY3(PAVGB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xe1] = X86_OP_ENTRY3(PSRAW_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xe2] = X86_OP_ENTRY3(PSRAD_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xe3] = X86_OP_ENTRY3(PAVGW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xe4] = X86_OP_ENTRY3(PMULHUW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xe5] = X86_OP_ENTRY3(PMULHW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xe6] = X86_OP_GROUP0(0FE6), + [0xe7] = X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex1 mmx p_00_66), /* MOVNTQ/MOVNTDQ */ + + [0xf0] = X86_OP_ENTRY3(MOVDQ, V,x, None,None, WM,x, vex4_unal cpuid(SSE3) p_f2), /* LDDQU */ + [0xf1] = X86_OP_ENTRY3(PSLLW_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xf2] = X86_OP_ENTRY3(PSLLD_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xf3] = X86_OP_ENTRY3(PSLLQ_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xf4] = X86_OP_ENTRY3(PMULUDQ, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xf5] = X86_OP_ENTRY3(PMADDWD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xf6] = X86_OP_ENTRY3(PSADBW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xf7] = X86_OP_ENTRY3(MASKMOV, None,None, V,dq, U,dq, vex4_unal avx2_256 mmx p_00_66), + + /* Incorrectly missing from 2-17 */ + [0xd8] = X86_OP_ENTRY3(PSUBUSB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd9] = X86_OP_ENTRY3(PSUBUSW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xda] = X86_OP_ENTRY3(PMINUB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xdb] = X86_OP_ENTRY3(PAND, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xdc] = X86_OP_ENTRY3(PADDUSB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xdd] = X86_OP_ENTRY3(PADDUSW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xde] = X86_OP_ENTRY3(PMAXUB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xdf] = X86_OP_ENTRY3(PANDN, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + + [0xe8] = X86_OP_ENTRY3(PSUBSB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xe9] = X86_OP_ENTRY3(PSUBSW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xea] = X86_OP_ENTRY3(PMINSW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xeb] = X86_OP_ENTRY3(POR, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xec] = X86_OP_ENTRY3(PADDSB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xed] = X86_OP_ENTRY3(PADDSW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xee] = X86_OP_ENTRY3(PMAXSW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xef] = X86_OP_ENTRY3(PXOR, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + + [0xf8] = X86_OP_ENTRY3(PSUBB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xf9] = X86_OP_ENTRY3(PSUBW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xfa] = X86_OP_ENTRY3(PSUBD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xfb] = X86_OP_ENTRY3(PSUBQ, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xfc] = X86_OP_ENTRY3(PADDB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xfd] = X86_OP_ENTRY3(PADDW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xfe] = X86_OP_ENTRY3(PADDD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + /* 0xff = UD0 */ +}; + +static void do_decode_0F(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + *entry = opcodes_0F[*b]; +} + +static void decode_0F(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + *b = x86_ldub_code(env, s); + do_decode_0F(s, env, entry, b); +} + +static const X86OpEntry opcodes_root[256] = { + [0x0F] = X86_OP_GROUP0(0F), +}; + +#undef mmx +#undef vex1 +#undef vex2 +#undef vex3 +#undef vex4 +#undef vex4_unal +#undef vex5 +#undef vex6 +#undef vex7 +#undef vex8 +#undef vex11 +#undef vex12 +#undef vex13 + +/* + * Decode the fixed part of the opcode and place the last + * in b. + */ +static void decode_root(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + *entry = opcodes_root[*b]; +} + + +static int decode_modrm(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + X86DecodedOp *op, X86OpType type) +{ + int modrm = get_modrm(s, env); + if ((modrm >> 6) == 3) { + op->n = (modrm & 7); + if (type != X86_TYPE_Q && type != X86_TYPE_N) { + op->n |= REX_B(s); + } + } else { + op->has_ea = true; + op->n = -1; + decode->mem = gen_lea_modrm_0(env, s, get_modrm(s, env)); + } + return modrm; +} + +static bool decode_op_size(DisasContext *s, X86OpEntry *e, X86OpSize size, MemOp *ot) +{ + switch (size) { + case X86_SIZE_b: /* byte */ + *ot = MO_8; + return true; + + case X86_SIZE_d: /* 32-bit */ + case X86_SIZE_ss: /* SSE/AVX scalar single precision */ + *ot = MO_32; + return true; + + case X86_SIZE_p: /* Far pointer, return offset size */ + case X86_SIZE_s: /* Descriptor, return offset size */ + case X86_SIZE_v: /* 16/32/64-bit, based on operand size */ + *ot = s->dflag; + return true; + + case X86_SIZE_pi: /* MMX */ + case X86_SIZE_q: /* 64-bit */ + case X86_SIZE_sd: /* SSE/AVX scalar double precision */ + *ot = MO_64; + return true; + + case X86_SIZE_w: /* 16-bit */ + *ot = MO_16; + return true; + + case X86_SIZE_y: /* 32/64-bit, based on operand size */ + *ot = s->dflag == MO_16 ? MO_32 : s->dflag; + return true; + + case X86_SIZE_z: /* 16-bit for 16-bit operand size, else 32-bit */ + *ot = s->dflag == MO_16 ? MO_16 : MO_32; + return true; + + case X86_SIZE_dq: /* SSE/AVX 128-bit */ + if (e->special == X86_SPECIAL_MMX && + !(s->prefix & (PREFIX_DATA | PREFIX_REPZ | PREFIX_REPNZ))) { + *ot = MO_64; + return true; + } + if (s->vex_l && e->s0 != X86_SIZE_qq && e->s1 != X86_SIZE_qq) { + return false; + } + *ot = MO_128; + return true; + + case X86_SIZE_qq: /* AVX 256-bit */ + if (!s->vex_l) { + return false; + } + *ot = MO_256; + return true; + + case X86_SIZE_x: /* 128/256-bit, based on operand size */ + if (e->special == X86_SPECIAL_MMX && + !(s->prefix & (PREFIX_DATA | PREFIX_REPZ | PREFIX_REPNZ))) { + *ot = MO_64; + return true; + } + /* fall through */ + case X86_SIZE_ps: /* SSE/AVX packed single precision */ + case X86_SIZE_pd: /* SSE/AVX packed double precision */ + *ot = s->vex_l ? MO_256 : MO_128; + return true; + + case X86_SIZE_xh: /* SSE/AVX packed half register */ + *ot = s->vex_l ? MO_128 : MO_64; + return true; + + case X86_SIZE_d64: /* Default to 64-bit in 64-bit mode */ + *ot = CODE64(s) && s->dflag == MO_32 ? MO_64 : s->dflag; + return true; + + case X86_SIZE_f64: /* Ignore size override prefix in 64-bit mode */ + *ot = CODE64(s) ? MO_64 : s->dflag; + return true; + + default: + *ot = -1; + return true; + } +} + +static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + X86DecodedOp *op, X86OpType type, int b) +{ + int modrm; + + switch (type) { + case X86_TYPE_None: /* Implicit or absent */ + case X86_TYPE_A: /* Implicit */ + case X86_TYPE_F: /* EFLAGS/RFLAGS */ + case X86_TYPE_X: /* string source */ + case X86_TYPE_Y: /* string destination */ + break; + + case X86_TYPE_B: /* VEX.vvvv selects a GPR */ + op->unit = X86_OP_INT; + op->n = s->vex_v; + break; + + case X86_TYPE_C: /* REG in the modrm byte selects a control register */ + op->unit = X86_OP_CR; + goto get_reg; + + case X86_TYPE_D: /* REG in the modrm byte selects a debug register */ + op->unit = X86_OP_DR; + goto get_reg; + + case X86_TYPE_G: /* REG in the modrm byte selects a GPR */ + op->unit = X86_OP_INT; + goto get_reg; + + case X86_TYPE_S: /* reg selects a segment register */ + op->unit = X86_OP_SEG; + goto get_reg; + + case X86_TYPE_P: + op->unit = X86_OP_MMX; + goto get_reg; + + case X86_TYPE_V: /* reg in the modrm byte selects an XMM/YMM register */ + if (decode->e.special == X86_SPECIAL_MMX && + !(s->prefix & (PREFIX_DATA | PREFIX_REPZ | PREFIX_REPNZ))) { + op->unit = X86_OP_MMX; + } else { + op->unit = X86_OP_SSE; + } + get_reg: + op->n = ((get_modrm(s, env) >> 3) & 7) | REX_R(s); + break; + + case X86_TYPE_E: /* ALU modrm operand */ + op->unit = X86_OP_INT; + goto get_modrm; + + case X86_TYPE_Q: /* MMX modrm operand */ + op->unit = X86_OP_MMX; + goto get_modrm; + + case X86_TYPE_W: /* XMM/YMM modrm operand */ + if (decode->e.special == X86_SPECIAL_MMX && + !(s->prefix & (PREFIX_DATA | PREFIX_REPZ | PREFIX_REPNZ))) { + op->unit = X86_OP_MMX; + } else { + op->unit = X86_OP_SSE; + } + goto get_modrm; + + case X86_TYPE_N: /* R/M in the modrm byte selects an MMX register */ + op->unit = X86_OP_MMX; + goto get_modrm_reg; + + case X86_TYPE_U: /* R/M in the modrm byte selects an XMM/YMM register */ + if (decode->e.special == X86_SPECIAL_MMX && + !(s->prefix & (PREFIX_DATA | PREFIX_REPZ | PREFIX_REPNZ))) { + op->unit = X86_OP_MMX; + } else { + op->unit = X86_OP_SSE; + } + goto get_modrm_reg; + + case X86_TYPE_R: /* R/M in the modrm byte selects a register */ + op->unit = X86_OP_INT; + get_modrm_reg: + modrm = get_modrm(s, env); + if ((modrm >> 6) != 3) { + return false; + } + goto get_modrm; + + case X86_TYPE_WM: /* modrm byte selects an XMM/YMM memory operand */ + op->unit = X86_OP_SSE; + /* fall through */ + case X86_TYPE_M: /* modrm byte selects a memory operand */ + modrm = get_modrm(s, env); + if ((modrm >> 6) == 3) { + return false; + } + get_modrm: + decode_modrm(s, env, decode, op, type); + break; + + case X86_TYPE_O: /* Absolute address encoded in the instruction */ + op->unit = X86_OP_INT; + op->has_ea = true; + op->n = -1; + decode->mem = (AddressParts) { + .def_seg = R_DS, + .base = -1, + .index = -1, + .disp = insn_get_addr(env, s, s->aflag) + }; + break; + + case X86_TYPE_H: /* For AVX, VEX.vvvv selects an XMM/YMM register */ + if ((s->prefix & PREFIX_VEX)) { + op->unit = X86_OP_SSE; + op->n = s->vex_v; + break; + } + if (op == &decode->op[0]) { + /* shifts place the destination in VEX.vvvv, use modrm */ + return decode_op(s, env, decode, op, decode->e.op1, b); + } else { + return decode_op(s, env, decode, op, decode->e.op0, b); + } + + case X86_TYPE_I: /* Immediate */ + case X86_TYPE_J: /* Relative offset for a jump */ + op->unit = X86_OP_IMM; + decode->immediate = insn_get_signed(env, s, op->ot); + break; + + case X86_TYPE_L: /* The upper 4 bits of the immediate select a 128-bit register */ + op->n = insn_get(env, s, op->ot) >> 4; + break; + + case X86_TYPE_2op: + *op = decode->op[0]; + break; + + case X86_TYPE_LoBits: + op->n = (b & 7) | REX_B(s); + op->unit = X86_OP_INT; + break; + + case X86_TYPE_0 ... X86_TYPE_7: + op->n = type - X86_TYPE_0; + op->unit = X86_OP_INT; + break; + + case X86_TYPE_ES ... X86_TYPE_GS: + op->n = type - X86_TYPE_ES; + op->unit = X86_OP_SEG; + break; + } + + return true; +} + +static bool validate_sse_prefix(DisasContext *s, X86OpEntry *e) +{ + uint16_t sse_prefixes; + + if (!e->valid_prefix) { + return true; + } + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + /* In SSE instructions, 0xF3 and 0xF2 cancel 0x66. */ + s->prefix &= ~PREFIX_DATA; + } + + /* Now, either zero or one bit is set in sse_prefixes. */ + sse_prefixes = s->prefix & (PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA); + return e->valid_prefix & (1 << sse_prefixes); +} + +static bool decode_insn(DisasContext *s, CPUX86State *env, X86DecodeFunc decode_func, + X86DecodedInsn *decode) +{ + X86OpEntry *e = &decode->e; + + decode_func(s, env, e, &decode->b); + while (e->is_decode) { + e->is_decode = false; + e->decode(s, env, e, &decode->b); + } + + if (!validate_sse_prefix(s, e)) { + return false; + } + + /* First compute size of operands in order to initialize s->rip_offset. */ + if (e->op0 != X86_TYPE_None) { + if (!decode_op_size(s, e, e->s0, &decode->op[0].ot)) { + return false; + } + if (e->op0 == X86_TYPE_I) { + s->rip_offset += 1 << decode->op[0].ot; + } + } + if (e->op1 != X86_TYPE_None) { + if (!decode_op_size(s, e, e->s1, &decode->op[1].ot)) { + return false; + } + if (e->op1 == X86_TYPE_I) { + s->rip_offset += 1 << decode->op[1].ot; + } + } + if (e->op2 != X86_TYPE_None) { + if (!decode_op_size(s, e, e->s2, &decode->op[2].ot)) { + return false; + } + if (e->op2 == X86_TYPE_I) { + s->rip_offset += 1 << decode->op[2].ot; + } + } + if (e->op3 != X86_TYPE_None) { + /* + * A couple instructions actually use the extra immediate byte for an Lx + * register operand; those are handled in the gen_* functions as one off. + */ + assert(e->op3 == X86_TYPE_I && e->s3 == X86_SIZE_b); + s->rip_offset += 1; + } + + if (e->op0 != X86_TYPE_None && + !decode_op(s, env, decode, &decode->op[0], e->op0, decode->b)) { + return false; + } + + if (e->op1 != X86_TYPE_None && + !decode_op(s, env, decode, &decode->op[1], e->op1, decode->b)) { + return false; + } + + if (e->op2 != X86_TYPE_None && + !decode_op(s, env, decode, &decode->op[2], e->op2, decode->b)) { + return false; + } + + if (e->op3 != X86_TYPE_None) { + decode->immediate = insn_get_signed(env, s, MO_8); + } + + return true; +} + +static bool has_cpuid_feature(DisasContext *s, X86CPUIDFeature cpuid) +{ + switch (cpuid) { + case X86_FEAT_None: + return true; + case X86_FEAT_F16C: + return (s->cpuid_ext_features & CPUID_EXT_F16C); + case X86_FEAT_FMA: + return (s->cpuid_ext_features & CPUID_EXT_FMA); + case X86_FEAT_MOVBE: + return (s->cpuid_ext_features & CPUID_EXT_MOVBE); + case X86_FEAT_PCLMULQDQ: + return (s->cpuid_ext_features & CPUID_EXT_PCLMULQDQ); + case X86_FEAT_SSE: + return (s->cpuid_ext_features & CPUID_SSE); + case X86_FEAT_SSE2: + return (s->cpuid_ext_features & CPUID_SSE2); + case X86_FEAT_SSE3: + return (s->cpuid_ext_features & CPUID_EXT_SSE3); + case X86_FEAT_SSSE3: + return (s->cpuid_ext_features & CPUID_EXT_SSSE3); + case X86_FEAT_SSE41: + return (s->cpuid_ext_features & CPUID_EXT_SSE41); + case X86_FEAT_SSE42: + return (s->cpuid_ext_features & CPUID_EXT_SSE42); + case X86_FEAT_AES: + if (!(s->cpuid_ext_features & CPUID_EXT_AES)) { + return false; + } else if (!(s->prefix & PREFIX_VEX)) { + return true; + } else if (!(s->cpuid_ext_features & CPUID_EXT_AVX)) { + return false; + } else { + return !s->vex_l || (s->cpuid_7_0_ecx_features & CPUID_7_0_ECX_VAES); + } + + case X86_FEAT_AVX: + return (s->cpuid_ext_features & CPUID_EXT_AVX); + + case X86_FEAT_3DNOW: + return (s->cpuid_ext2_features & CPUID_EXT2_3DNOW); + case X86_FEAT_SSE4A: + return (s->cpuid_ext3_features & CPUID_EXT3_SSE4A); + + case X86_FEAT_ADX: + return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_ADX); + case X86_FEAT_BMI1: + return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1); + case X86_FEAT_BMI2: + return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2); + case X86_FEAT_AVX2: + return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_AVX2); + case X86_FEAT_SHA_NI: + return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SHA_NI); + + case X86_FEAT_CMPCCXADD: + return (s->cpuid_7_1_eax_features & CPUID_7_1_EAX_CMPCCXADD); + } + g_assert_not_reached(); +} + +static bool validate_vex(DisasContext *s, X86DecodedInsn *decode) +{ + X86OpEntry *e = &decode->e; + + switch (e->vex_special) { + case X86_VEX_REPScalar: + /* + * Instructions which differ between 00/66 and F2/F3 in the + * exception classification and the size of the memory operand. + */ + assert(e->vex_class == 1 || e->vex_class == 2 || e->vex_class == 4); + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + e->vex_class = e->vex_class < 4 ? 3 : 5; + if (s->vex_l) { + goto illegal; + } + assert(decode->e.s2 == X86_SIZE_x); + if (decode->op[2].has_ea) { + decode->op[2].ot = s->prefix & PREFIX_REPZ ? MO_32 : MO_64; + } + } + break; + + case X86_VEX_SSEUnaligned: + /* handled in sse_needs_alignment. */ + break; + + case X86_VEX_AVX2_256: + if ((s->prefix & PREFIX_VEX) && s->vex_l && !has_cpuid_feature(s, X86_FEAT_AVX2)) { + goto illegal; + } + } + + switch (e->vex_class) { + case 0: + if (s->prefix & PREFIX_VEX) { + goto illegal; + } + return true; + case 1: + case 2: + case 3: + case 4: + case 5: + case 7: + if (s->prefix & PREFIX_VEX) { + if (!(s->flags & HF_AVX_EN_MASK)) { + goto illegal; + } + } else if (e->special != X86_SPECIAL_MMX || + (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA))) { + if (!(s->flags & HF_OSFXSR_MASK)) { + goto illegal; + } + } + break; + case 12: + /* Must have a VSIB byte and no address prefix. */ + assert(s->has_modrm); + if ((s->modrm & 7) != 4 || s->aflag == MO_16) { + goto illegal; + } + + /* Check no overlap between registers. */ + if (!decode->op[0].has_ea && + (decode->op[0].n == decode->mem.index || decode->op[0].n == decode->op[1].n)) { + goto illegal; + } + assert(!decode->op[1].has_ea); + if (decode->op[1].n == decode->mem.index) { + goto illegal; + } + if (!decode->op[2].has_ea && + (decode->op[2].n == decode->mem.index || decode->op[2].n == decode->op[1].n)) { + goto illegal; + } + /* fall through */ + case 6: + case 11: + if (!(s->prefix & PREFIX_VEX)) { + goto illegal; + } + if (!(s->flags & HF_AVX_EN_MASK)) { + goto illegal; + } + break; + case 8: + /* Non-VEX case handled in decode_0F77. */ + assert(s->prefix & PREFIX_VEX); + if (!(s->flags & HF_AVX_EN_MASK)) { + goto illegal; + } + break; + case 13: + if (!(s->prefix & PREFIX_VEX)) { + goto illegal; + } + if (s->vex_l) { + goto illegal; + } + /* All integer instructions use VEX.vvvv, so exit. */ + return true; + } + + if (s->vex_v != 0 && + e->op0 != X86_TYPE_H && e->op0 != X86_TYPE_B && + e->op1 != X86_TYPE_H && e->op1 != X86_TYPE_B && + e->op2 != X86_TYPE_H && e->op2 != X86_TYPE_B) { + goto illegal; + } + + if (s->flags & HF_TS_MASK) { + goto nm_exception; + } + if (s->flags & HF_EM_MASK) { + goto illegal; + } + + if (e->check) { + if (e->check & X86_CHECK_VEX128) { + if (s->vex_l) { + goto illegal; + } + } + if (e->check & X86_CHECK_W0) { + if (s->vex_w) { + goto illegal; + } + } + if (e->check & X86_CHECK_W1) { + if (!s->vex_w) { + goto illegal; + } + } + } + return true; + +nm_exception: + gen_NM_exception(s); + return false; +illegal: + gen_illegal_opcode(s); + return false; +} + +/* + * Convert one instruction. s->base.is_jmp is set if the translation must + * be stopped. + */ +static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) +{ + CPUX86State *env = cpu_env(cpu); + bool first = true; + X86DecodedInsn decode; + X86DecodeFunc decode_func = decode_root; + uint8_t cc_live; + + s->has_modrm = false; + + next_byte: + if (first) { + first = false; + } else { + b = x86_ldub_code(env, s); + } + /* Collect prefixes. */ + switch (b) { + case 0xf3: + s->prefix |= PREFIX_REPZ; + s->prefix &= ~PREFIX_REPNZ; + goto next_byte; + case 0xf2: + s->prefix |= PREFIX_REPNZ; + s->prefix &= ~PREFIX_REPZ; + goto next_byte; + case 0xf0: + s->prefix |= PREFIX_LOCK; + goto next_byte; + case 0x2e: + s->override = R_CS; + goto next_byte; + case 0x36: + s->override = R_SS; + goto next_byte; + case 0x3e: + s->override = R_DS; + goto next_byte; + case 0x26: + s->override = R_ES; + goto next_byte; + case 0x64: + s->override = R_FS; + goto next_byte; + case 0x65: + s->override = R_GS; + goto next_byte; + case 0x66: + s->prefix |= PREFIX_DATA; + goto next_byte; + case 0x67: + s->prefix |= PREFIX_ADR; + goto next_byte; +#ifdef TARGET_X86_64 + case 0x40 ... 0x4f: + if (CODE64(s)) { + /* REX prefix */ + s->prefix |= PREFIX_REX; + s->vex_w = (b >> 3) & 1; + s->rex_r = (b & 0x4) << 1; + s->rex_x = (b & 0x2) << 2; + s->rex_b = (b & 0x1) << 3; + goto next_byte; + } + break; +#endif + case 0xc5: /* 2-byte VEX */ + case 0xc4: /* 3-byte VEX */ + /* + * VEX prefixes cannot be used except in 32-bit mode. + * Otherwise the instruction is LES or LDS. + */ + if (CODE32(s) && !VM86(s)) { + static const int pp_prefix[4] = { + 0, PREFIX_DATA, PREFIX_REPZ, PREFIX_REPNZ + }; + int vex3, vex2 = x86_ldub_code(env, s); + + if (!CODE64(s) && (vex2 & 0xc0) != 0xc0) { + /* + * 4.1.4.6: In 32-bit mode, bits [7:6] must be 11b, + * otherwise the instruction is LES or LDS. + */ + s->pc--; /* rewind the advance_pc() x86_ldub_code() did */ + break; + } + + /* 4.1.1-4.1.3: No preceding lock, 66, f2, f3, or rex prefixes. */ + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ + | PREFIX_LOCK | PREFIX_DATA | PREFIX_REX)) { + goto illegal_op; + } +#ifdef TARGET_X86_64 + s->rex_r = (~vex2 >> 4) & 8; +#endif + if (b == 0xc5) { + /* 2-byte VEX prefix: RVVVVlpp, implied 0f leading opcode byte */ + vex3 = vex2; + decode_func = decode_0F; + } else { + /* 3-byte VEX prefix: RXBmmmmm wVVVVlpp */ + vex3 = x86_ldub_code(env, s); +#ifdef TARGET_X86_64 + s->rex_x = (~vex2 >> 3) & 8; + s->rex_b = (~vex2 >> 2) & 8; +#endif + s->vex_w = (vex3 >> 7) & 1; + switch (vex2 & 0x1f) { + case 0x01: /* Implied 0f leading opcode bytes. */ + decode_func = decode_0F; + break; + case 0x02: /* Implied 0f 38 leading opcode bytes. */ + decode_func = decode_0F38; + break; + case 0x03: /* Implied 0f 3a leading opcode bytes. */ + decode_func = decode_0F3A; + break; + default: /* Reserved for future use. */ + goto unknown_op; + } + } + s->vex_v = (~vex3 >> 3) & 0xf; + s->vex_l = (vex3 >> 2) & 1; + s->prefix |= pp_prefix[vex3 & 3] | PREFIX_VEX; + } + break; + default: + if (b >= 0x100) { + b -= 0x100; + decode_func = do_decode_0F; + } + break; + } + + /* Post-process prefixes. */ + if (CODE64(s)) { + /* + * In 64-bit mode, the default data size is 32-bit. Select 64-bit + * data with rex_w, and 16-bit data with 0x66; rex_w takes precedence + * over 0x66 if both are present. + */ + s->dflag = (REX_W(s) ? MO_64 : s->prefix & PREFIX_DATA ? MO_16 : MO_32); + /* In 64-bit mode, 0x67 selects 32-bit addressing. */ + s->aflag = (s->prefix & PREFIX_ADR ? MO_32 : MO_64); + } else { + /* In 16/32-bit mode, 0x66 selects the opposite data size. */ + if (CODE32(s) ^ ((s->prefix & PREFIX_DATA) != 0)) { + s->dflag = MO_32; + } else { + s->dflag = MO_16; + } + /* In 16/32-bit mode, 0x67 selects the opposite addressing. */ + if (CODE32(s) ^ ((s->prefix & PREFIX_ADR) != 0)) { + s->aflag = MO_32; + } else { + s->aflag = MO_16; + } + } + + memset(&decode, 0, sizeof(decode)); + decode.cc_op = -1; + decode.b = b; + if (!decode_insn(s, env, decode_func, &decode)) { + goto illegal_op; + } + if (!decode.e.gen) { + goto unknown_op; + } + + if (!has_cpuid_feature(s, decode.e.cpuid)) { + goto illegal_op; + } + + /* Checks that result in #UD come first. */ + if (decode.e.check) { + if (decode.e.check & X86_CHECK_i64) { + if (CODE64(s)) { + goto illegal_op; + } + } + if (decode.e.check & X86_CHECK_o64) { + if (!CODE64(s)) { + goto illegal_op; + } + } + if (decode.e.check & X86_CHECK_prot) { + if (!PE(s) || VM86(s)) { + goto illegal_op; + } + } + } + + switch (decode.e.special) { + case X86_SPECIAL_None: + break; + + case X86_SPECIAL_Locked: + if (decode.op[0].has_ea) { + s->prefix |= PREFIX_LOCK; + } + decode.e.special = X86_SPECIAL_HasLock; + /* fallthrough */ + case X86_SPECIAL_HasLock: + break; + + case X86_SPECIAL_Op0_Rd: + assert(decode.op[0].unit == X86_OP_INT); + if (!decode.op[0].has_ea) { + decode.op[0].ot = MO_32; + } + break; + + case X86_SPECIAL_Op2_Ry: + assert(decode.op[2].unit == X86_OP_INT); + if (!decode.op[2].has_ea) { + decode.op[2].ot = s->dflag == MO_16 ? MO_32 : s->dflag; + } + break; + + case X86_SPECIAL_AVXExtMov: + if (!decode.op[2].has_ea) { + decode.op[2].ot = s->vex_l ? MO_256 : MO_128; + } else if (s->vex_l) { + decode.op[2].ot++; + } + break; + + case X86_SPECIAL_SExtT0: + case X86_SPECIAL_ZExtT0: + /* Handled in gen_load. */ + assert(decode.op[1].unit == X86_OP_INT); + break; + + default: + break; + } + + if (s->prefix & PREFIX_LOCK) { + if (decode.e.special != X86_SPECIAL_HasLock || !decode.op[0].has_ea) { + goto illegal_op; + } + } + + if (!validate_vex(s, &decode)) { + return; + } + + /* + * Checks that result in #GP or VMEXIT come second. Intercepts are + * generally checked after non-memory exceptions (i.e. before all + * exceptions if there is no memory operand). Exceptions are + * vm86 checks (INTn, IRET, PUSHF/POPF), RSM and XSETBV (!). + * + * RSM and XSETBV will be handled in the gen_* functions + * instead of using chk(). + */ + if (decode.e.check & X86_CHECK_cpl0) { + if (CPL(s) != 0) { + goto gp_fault; + } + } + if (decode.e.intercept && unlikely(GUEST(s))) { + gen_helper_svm_check_intercept(tcg_env, + tcg_constant_i32(decode.e.intercept)); + } + if (decode.e.check) { + if ((decode.e.check & X86_CHECK_vm86_iopl) && VM86(s)) { + if (IOPL(s) < 3) { + goto gp_fault; + } + } else if (decode.e.check & X86_CHECK_cpl_iopl) { + if (IOPL(s) < CPL(s)) { + goto gp_fault; + } + } + } + + if (decode.e.special == X86_SPECIAL_MMX && + !(s->prefix & (PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA))) { + gen_helper_enter_mmx(tcg_env); + } + + if (decode.op[0].has_ea || decode.op[1].has_ea || decode.op[2].has_ea) { + gen_load_ea(s, &decode.mem, decode.e.vex_class == 12); + } + if (s->prefix & PREFIX_LOCK) { + gen_load(s, &decode, 2, s->T1); + decode.e.gen(s, env, &decode); + } else { + if (decode.op[0].unit == X86_OP_MMX) { + compute_mmx_offset(&decode.op[0]); + } else if (decode.op[0].unit == X86_OP_SSE) { + compute_xmm_offset(&decode.op[0]); + } + gen_load(s, &decode, 1, s->T0); + gen_load(s, &decode, 2, s->T1); + decode.e.gen(s, env, &decode); + gen_writeback(s, &decode, 0, s->T0); + } + + /* + * Write back flags after last memory access. Some newer ALU instructions, as + * well as SSE instructions, write flags in the gen_* function, but that can + * cause incorrect tracking of CC_OP for instructions that write to both memory + * and flags. + */ + if (decode.cc_op != -1) { + if (decode.cc_dst) { + tcg_gen_mov_tl(cpu_cc_dst, decode.cc_dst); + } + if (decode.cc_src) { + tcg_gen_mov_tl(cpu_cc_src, decode.cc_src); + } + if (decode.cc_src2) { + tcg_gen_mov_tl(cpu_cc_src2, decode.cc_src2); + } + if (decode.cc_op == CC_OP_DYNAMIC) { + tcg_gen_mov_i32(cpu_cc_op, decode.cc_op_dynamic); + } + set_cc_op(s, decode.cc_op); + cc_live = cc_op_live[decode.cc_op]; + } else { + cc_live = 0; + } + if (decode.cc_op != CC_OP_DYNAMIC) { + assert(!decode.cc_op_dynamic); + assert(!!decode.cc_dst == !!(cc_live & USES_CC_DST)); + assert(!!decode.cc_src == !!(cc_live & USES_CC_SRC)); + assert(!!decode.cc_src2 == !!(cc_live & USES_CC_SRC2)); + } + + return; + gp_fault: + gen_exception_gpf(s); + return; + illegal_op: + gen_illegal_opcode(s); + return; + unknown_op: + gen_unknown_opcode(env, s); +} diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h new file mode 100644 index 0000000000..15e6bfef4b --- /dev/null +++ b/target/i386/tcg/decode-new.h @@ -0,0 +1,293 @@ +/* + * Decode table flags, mostly based on Intel SDM. + * + * Copyright (c) 2022 Red Hat, Inc. + * + * Author: Paolo Bonzini + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 . + */ + +typedef enum X86OpType { + X86_TYPE_None, + + X86_TYPE_A, /* Implicit */ + X86_TYPE_B, /* VEX.vvvv selects a GPR */ + X86_TYPE_C, /* REG in the modrm byte selects a control register */ + X86_TYPE_D, /* REG in the modrm byte selects a debug register */ + X86_TYPE_E, /* ALU modrm operand */ + X86_TYPE_F, /* EFLAGS/RFLAGS */ + X86_TYPE_G, /* REG in the modrm byte selects a GPR */ + X86_TYPE_H, /* For AVX, VEX.vvvv selects an XMM/YMM register */ + X86_TYPE_I, /* Immediate */ + X86_TYPE_J, /* Relative offset for a jump */ + X86_TYPE_L, /* The upper 4 bits of the immediate select a 128-bit register */ + X86_TYPE_M, /* modrm byte selects a memory operand */ + X86_TYPE_N, /* R/M in the modrm byte selects an MMX register */ + X86_TYPE_O, /* Absolute address encoded in the instruction */ + X86_TYPE_P, /* reg in the modrm byte selects an MMX register */ + X86_TYPE_Q, /* MMX modrm operand */ + X86_TYPE_R, /* R/M in the modrm byte selects a register */ + X86_TYPE_S, /* reg selects a segment register */ + X86_TYPE_U, /* R/M in the modrm byte selects an XMM/YMM register */ + X86_TYPE_V, /* reg in the modrm byte selects an XMM/YMM register */ + X86_TYPE_W, /* XMM/YMM modrm operand */ + X86_TYPE_X, /* string source */ + X86_TYPE_Y, /* string destination */ + + /* Custom */ + X86_TYPE_WM, /* modrm byte selects an XMM/YMM memory operand */ + X86_TYPE_2op, /* 2-operand RMW instruction */ + X86_TYPE_LoBits, /* encoded in bits 0-2 of the operand + REX.B */ + X86_TYPE_0, /* Hard-coded GPRs (RAX..RDI) */ + X86_TYPE_1, + X86_TYPE_2, + X86_TYPE_3, + X86_TYPE_4, + X86_TYPE_5, + X86_TYPE_6, + X86_TYPE_7, + X86_TYPE_ES, /* Hard-coded segment registers */ + X86_TYPE_CS, + X86_TYPE_SS, + X86_TYPE_DS, + X86_TYPE_FS, + X86_TYPE_GS, +} X86OpType; + +typedef enum X86OpSize { + X86_SIZE_None, + + X86_SIZE_a, /* BOUND operand */ + X86_SIZE_b, /* byte */ + X86_SIZE_d, /* 32-bit */ + X86_SIZE_dq, /* SSE/AVX 128-bit */ + X86_SIZE_p, /* Far pointer */ + X86_SIZE_pd, /* SSE/AVX packed double precision */ + X86_SIZE_pi, /* MMX */ + X86_SIZE_ps, /* SSE/AVX packed single precision */ + X86_SIZE_q, /* 64-bit */ + X86_SIZE_qq, /* AVX 256-bit */ + X86_SIZE_s, /* Descriptor */ + X86_SIZE_sd, /* SSE/AVX scalar double precision */ + X86_SIZE_ss, /* SSE/AVX scalar single precision */ + X86_SIZE_si, /* 32-bit GPR */ + X86_SIZE_v, /* 16/32/64-bit, based on operand size */ + X86_SIZE_w, /* 16-bit */ + X86_SIZE_x, /* 128/256-bit, based on operand size */ + X86_SIZE_y, /* 32/64-bit, based on operand size */ + X86_SIZE_z, /* 16-bit for 16-bit operand size, else 32-bit */ + + /* Custom */ + X86_SIZE_d64, + X86_SIZE_f64, + X86_SIZE_xh, /* SSE/AVX packed half register */ +} X86OpSize; + +typedef enum X86CPUIDFeature { + X86_FEAT_None, + X86_FEAT_3DNOW, + X86_FEAT_ADX, + X86_FEAT_AES, + X86_FEAT_AVX, + X86_FEAT_AVX2, + X86_FEAT_BMI1, + X86_FEAT_BMI2, + X86_FEAT_CMPCCXADD, + X86_FEAT_F16C, + X86_FEAT_FMA, + X86_FEAT_MOVBE, + X86_FEAT_PCLMULQDQ, + X86_FEAT_SHA_NI, + X86_FEAT_SSE, + X86_FEAT_SSE2, + X86_FEAT_SSE3, + X86_FEAT_SSSE3, + X86_FEAT_SSE41, + X86_FEAT_SSE42, + X86_FEAT_SSE4A, +} X86CPUIDFeature; + +/* Execution flags */ + +typedef enum X86OpUnit { + X86_OP_SKIP, /* not valid or managed by emission function */ + X86_OP_SEG, /* segment selector */ + X86_OP_CR, /* control register */ + X86_OP_DR, /* debug register */ + X86_OP_INT, /* loaded into/stored from s->T0/T1 */ + X86_OP_IMM, /* immediate */ + X86_OP_SSE, /* address in either s->ptrX or s->A0 depending on has_ea */ + X86_OP_MMX, /* address in either s->ptrX or s->A0 depending on has_ea */ +} X86OpUnit; + +typedef enum X86InsnCheck { + /* Illegal or exclusive to 64-bit mode */ + X86_CHECK_i64 = 1, + X86_CHECK_o64 = 2, + + /* Fault outside protected mode */ + X86_CHECK_prot = 4, + + /* Privileged instruction checks */ + X86_CHECK_cpl0 = 8, + X86_CHECK_vm86_iopl = 16, + X86_CHECK_cpl_iopl = 32, + X86_CHECK_iopl = X86_CHECK_cpl_iopl | X86_CHECK_vm86_iopl, + + /* Fault if VEX.L=1 */ + X86_CHECK_VEX128 = 64, + + /* Fault if VEX.W=1 */ + X86_CHECK_W0 = 128, + + /* Fault if VEX.W=0 */ + X86_CHECK_W1 = 256, +} X86InsnCheck; + +typedef enum X86InsnSpecial { + X86_SPECIAL_None, + + /* Accepts LOCK prefix; LOCKed operations do not load or writeback operand 0 */ + X86_SPECIAL_HasLock, + + /* Always locked if it has a memory operand (XCHG) */ + X86_SPECIAL_Locked, + + /* + * Rd/Mb or Rd/Mw in the manual: register operand 0 is treated as 32 bits + * (and writeback zero-extends it to 64 bits if applicable). PREFIX_DATA + * does not trigger 16-bit writeback and, as a side effect, high-byte + * registers are never used. + */ + X86_SPECIAL_Op0_Rd, + + /* + * Ry/Mb in the manual (PINSRB). However, the high bits are never used by + * the instruction in either the register or memory cases; the *real* effect + * of this modifier is that high-byte registers are never used, even without + * a REX prefix. Therefore, PINSRW does not need it despite having Ry/Mw. + */ + X86_SPECIAL_Op2_Ry, + + /* + * Register operand 2 is extended to full width, while a memory operand + * is doubled in size if VEX.L=1. + */ + X86_SPECIAL_AVXExtMov, + + /* + * MMX instruction exists with no prefix; if there is no prefix, V/H/W/U operands + * become P/P/Q/N, and size "x" becomes "q". + */ + X86_SPECIAL_MMX, + + /* When loaded into s->T0, register operand 1 is zero/sign extended. */ + X86_SPECIAL_SExtT0, + X86_SPECIAL_ZExtT0, +} X86InsnSpecial; + +/* + * Special cases for instructions that operate on XMM/YMM registers. Intel + * retconned all of them to have VEX exception classes other than 0 and 13, so + * all these only matter for instructions that have a VEX exception class. + * Based on tables in the "AVX and SSE Instruction Exception Specification" + * section of the manual. + */ +typedef enum X86VEXSpecial { + /* Legacy SSE instructions that allow unaligned operands */ + X86_VEX_SSEUnaligned, + + /* + * Used for instructions that distinguish the XMM operand type with an + * instruction prefix; legacy SSE encodings will allow unaligned operands + * for scalar operands only (identified by a REP prefix). In this case, + * the decoding table uses "x" for the vector operands instead of specifying + * pd/ps/sd/ss individually. + */ + X86_VEX_REPScalar, + + /* + * VEX instructions that only support 256-bit operands with AVX2 (Table 2-17 + * column 3). Columns 2 and 4 (instructions limited to 256- and 127-bit + * operands respectively) are implicit in the presence of dq and qq + * operands, and thus handled by decode_op_size. + */ + X86_VEX_AVX2_256, +} X86VEXSpecial; + + +typedef struct X86OpEntry X86OpEntry; +typedef struct X86DecodedInsn X86DecodedInsn; + +/* Decode function for multibyte opcodes. */ +typedef void (*X86DecodeFunc)(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b); + +/* Code generation function. */ +typedef void (*X86GenFunc)(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode); + +struct X86OpEntry { + /* Based on the is_decode flags. */ + union { + X86GenFunc gen; + X86DecodeFunc decode; + }; + /* op0 is always written, op1 and op2 are always read. */ + X86OpType op0:8; + X86OpSize s0:8; + X86OpType op1:8; + X86OpSize s1:8; + X86OpType op2:8; + X86OpSize s2:8; + /* Must be I and b respectively if present. */ + X86OpType op3:8; + X86OpSize s3:8; + + X86InsnSpecial special:8; + X86CPUIDFeature cpuid:8; + unsigned vex_class:8; + X86VEXSpecial vex_special:8; + unsigned valid_prefix:16; + unsigned check:16; + unsigned intercept:8; + bool is_decode:1; +}; + +typedef struct X86DecodedOp { + int8_t n; + MemOp ot; /* For b/c/d/p/s/q/v/w/y/z */ + X86OpUnit unit; + bool has_ea; + int offset; /* For MMX and SSE */ + + /* + * This field is used internally by macros OP0_PTR/OP1_PTR/OP2_PTR, + * do not access directly! + */ + TCGv_ptr v_ptr; +} X86DecodedOp; + +struct X86DecodedInsn { + X86OpEntry e; + X86DecodedOp op[3]; + target_ulong immediate; + AddressParts mem; + + TCGv cc_dst, cc_src, cc_src2; + TCGv_i32 cc_op_dynamic; + int8_t cc_op; + + uint8_t b; +}; + diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc new file mode 100644 index 0000000000..6bcf88ecd7 --- /dev/null +++ b/target/i386/tcg/emit.c.inc @@ -0,0 +1,2493 @@ +/* + * New-style TCG opcode generator for i386 instructions + * + * Copyright (c) 2022 Red Hat, Inc. + * + * Author: Paolo Bonzini + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 . + */ + +#define ZMM_OFFSET(reg) offsetof(CPUX86State, xmm_regs[reg]) + +typedef void (*SSEFunc_i_ep)(TCGv_i32 val, TCGv_ptr env, TCGv_ptr reg); +typedef void (*SSEFunc_l_ep)(TCGv_i64 val, TCGv_ptr env, TCGv_ptr reg); +typedef void (*SSEFunc_0_epp)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b); +typedef void (*SSEFunc_0_eppp)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, + TCGv_ptr reg_c); +typedef void (*SSEFunc_0_epppp)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, + TCGv_ptr reg_c, TCGv_ptr reg_d); +typedef void (*SSEFunc_0_eppi)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, + TCGv_i32 val); +typedef void (*SSEFunc_0_epppi)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, + TCGv_ptr reg_c, TCGv_i32 val); +typedef void (*SSEFunc_0_ppi)(TCGv_ptr reg_a, TCGv_ptr reg_b, TCGv_i32 val); +typedef void (*SSEFunc_0_pppi)(TCGv_ptr reg_a, TCGv_ptr reg_b, TCGv_ptr reg_c, + TCGv_i32 val); +typedef void (*SSEFunc_0_eppt)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, + TCGv val); +typedef void (*SSEFunc_0_epppti)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, + TCGv_ptr reg_c, TCGv a0, TCGv_i32 scale); +typedef void (*SSEFunc_0_eppppi)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, + TCGv_ptr reg_c, TCGv_ptr reg_d, TCGv_i32 flags); +typedef void (*SSEFunc_0_eppppii)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, + TCGv_ptr reg_c, TCGv_ptr reg_d, TCGv_i32 even, + TCGv_i32 odd); + +static inline TCGv_i32 tcg_constant8u_i32(uint8_t val) +{ + return tcg_constant_i32(val); +} + +static void gen_NM_exception(DisasContext *s) +{ + gen_exception(s, EXCP07_PREX); +} + +static void gen_load_ea(DisasContext *s, AddressParts *mem, bool is_vsib) +{ + TCGv ea = gen_lea_modrm_1(s, *mem, is_vsib); + gen_lea_v_seg(s, s->aflag, ea, mem->def_seg, s->override); +} + +static inline int mmx_offset(MemOp ot) +{ + switch (ot) { + case MO_8: + return offsetof(MMXReg, MMX_B(0)); + case MO_16: + return offsetof(MMXReg, MMX_W(0)); + case MO_32: + return offsetof(MMXReg, MMX_L(0)); + case MO_64: + return offsetof(MMXReg, MMX_Q(0)); + default: + g_assert_not_reached(); + } +} + +static inline int xmm_offset(MemOp ot) +{ + switch (ot) { + case MO_8: + return offsetof(ZMMReg, ZMM_B(0)); + case MO_16: + return offsetof(ZMMReg, ZMM_W(0)); + case MO_32: + return offsetof(ZMMReg, ZMM_L(0)); + case MO_64: + return offsetof(ZMMReg, ZMM_Q(0)); + case MO_128: + return offsetof(ZMMReg, ZMM_X(0)); + case MO_256: + return offsetof(ZMMReg, ZMM_Y(0)); + default: + g_assert_not_reached(); + } +} + +static int vector_reg_offset(X86DecodedOp *op) +{ + assert(op->unit == X86_OP_MMX || op->unit == X86_OP_SSE); + + if (op->unit == X86_OP_MMX) { + return op->offset - mmx_offset(op->ot); + } else { + return op->offset - xmm_offset(op->ot); + } +} + +static int vector_elem_offset(X86DecodedOp *op, MemOp ot, int n) +{ + int base_ofs = vector_reg_offset(op); + switch(ot) { + case MO_8: + if (op->unit == X86_OP_MMX) { + return base_ofs + offsetof(MMXReg, MMX_B(n)); + } else { + return base_ofs + offsetof(ZMMReg, ZMM_B(n)); + } + case MO_16: + if (op->unit == X86_OP_MMX) { + return base_ofs + offsetof(MMXReg, MMX_W(n)); + } else { + return base_ofs + offsetof(ZMMReg, ZMM_W(n)); + } + case MO_32: + if (op->unit == X86_OP_MMX) { + return base_ofs + offsetof(MMXReg, MMX_L(n)); + } else { + return base_ofs + offsetof(ZMMReg, ZMM_L(n)); + } + case MO_64: + if (op->unit == X86_OP_MMX) { + return base_ofs; + } else { + return base_ofs + offsetof(ZMMReg, ZMM_Q(n)); + } + case MO_128: + assert(op->unit == X86_OP_SSE); + return base_ofs + offsetof(ZMMReg, ZMM_X(n)); + case MO_256: + assert(op->unit == X86_OP_SSE); + return base_ofs + offsetof(ZMMReg, ZMM_Y(n)); + default: + g_assert_not_reached(); + } +} + +static void compute_mmx_offset(X86DecodedOp *op) +{ + if (!op->has_ea) { + op->offset = offsetof(CPUX86State, fpregs[op->n].mmx) + mmx_offset(op->ot); + } else { + op->offset = offsetof(CPUX86State, mmx_t0) + mmx_offset(op->ot); + } +} + +static void compute_xmm_offset(X86DecodedOp *op) +{ + if (!op->has_ea) { + op->offset = ZMM_OFFSET(op->n) + xmm_offset(op->ot); + } else { + op->offset = offsetof(CPUX86State, xmm_t0) + xmm_offset(op->ot); + } +} + +static void gen_load_sse(DisasContext *s, TCGv temp, MemOp ot, int dest_ofs, bool aligned) +{ + switch(ot) { + case MO_8: + gen_op_ld_v(s, MO_8, temp, s->A0); + tcg_gen_st8_tl(temp, tcg_env, dest_ofs); + break; + case MO_16: + gen_op_ld_v(s, MO_16, temp, s->A0); + tcg_gen_st16_tl(temp, tcg_env, dest_ofs); + break; + case MO_32: + gen_op_ld_v(s, MO_32, temp, s->A0); + tcg_gen_st32_tl(temp, tcg_env, dest_ofs); + break; + case MO_64: + gen_ldq_env_A0(s, dest_ofs); + break; + case MO_128: + gen_ldo_env_A0(s, dest_ofs, aligned); + break; + case MO_256: + gen_ldy_env_A0(s, dest_ofs, aligned); + break; + default: + g_assert_not_reached(); + } +} + +static bool sse_needs_alignment(DisasContext *s, X86DecodedInsn *decode, MemOp ot) +{ + switch (decode->e.vex_class) { + case 2: + case 4: + if ((s->prefix & PREFIX_VEX) || + decode->e.vex_special == X86_VEX_SSEUnaligned) { + /* MOST legacy SSE instructions require aligned memory operands, but not all. */ + return false; + } + /* fall through */ + case 1: + return ot >= MO_128; + + default: + return false; + } +} + +static void gen_load(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v) +{ + X86DecodedOp *op = &decode->op[opn]; + + switch (op->unit) { + case X86_OP_SKIP: + return; + case X86_OP_SEG: + tcg_gen_ld32u_tl(v, tcg_env, + offsetof(CPUX86State,segs[op->n].selector)); + break; + case X86_OP_CR: + tcg_gen_ld_tl(v, tcg_env, offsetof(CPUX86State, cr[op->n])); + break; + case X86_OP_DR: + tcg_gen_ld_tl(v, tcg_env, offsetof(CPUX86State, dr[op->n])); + break; + case X86_OP_INT: + if (op->has_ea) { + if (v == s->T0 && decode->e.special == X86_SPECIAL_SExtT0) { + gen_op_ld_v(s, op->ot | MO_SIGN, v, s->A0); + } else { + gen_op_ld_v(s, op->ot, v, s->A0); + } + + } else if (op->ot == MO_8 && byte_reg_is_xH(s, op->n)) { + if (v == s->T0 && decode->e.special == X86_SPECIAL_SExtT0) { + tcg_gen_sextract_tl(v, cpu_regs[op->n - 4], 8, 8); + } else { + tcg_gen_extract_tl(v, cpu_regs[op->n - 4], 8, 8); + } + + } else if (op->ot < MO_TL && v == s->T0 && + (decode->e.special == X86_SPECIAL_SExtT0 || + decode->e.special == X86_SPECIAL_ZExtT0)) { + if (decode->e.special == X86_SPECIAL_SExtT0) { + tcg_gen_ext_tl(v, cpu_regs[op->n], op->ot | MO_SIGN); + } else { + tcg_gen_ext_tl(v, cpu_regs[op->n], op->ot); + } + + } else { + tcg_gen_mov_tl(v, cpu_regs[op->n]); + } + break; + case X86_OP_IMM: + tcg_gen_movi_tl(v, decode->immediate); + break; + + case X86_OP_MMX: + compute_mmx_offset(op); + goto load_vector; + + case X86_OP_SSE: + compute_xmm_offset(op); + load_vector: + if (op->has_ea) { + bool aligned = sse_needs_alignment(s, decode, op->ot); + gen_load_sse(s, v, op->ot, op->offset, aligned); + } + break; + + default: + g_assert_not_reached(); + } +} + +static TCGv_ptr op_ptr(X86DecodedInsn *decode, int opn) +{ + X86DecodedOp *op = &decode->op[opn]; + if (op->v_ptr) { + return op->v_ptr; + } + op->v_ptr = tcg_temp_new_ptr(); + + /* The temporary points to the MMXReg or ZMMReg. */ + tcg_gen_addi_ptr(op->v_ptr, tcg_env, vector_reg_offset(op)); + return op->v_ptr; +} + +#define OP_PTR0 op_ptr(decode, 0) +#define OP_PTR1 op_ptr(decode, 1) +#define OP_PTR2 op_ptr(decode, 2) + +static void gen_writeback(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v) +{ + X86DecodedOp *op = &decode->op[opn]; + switch (op->unit) { + case X86_OP_SKIP: + break; + case X86_OP_SEG: + /* Note that gen_movl_seg_T0 takes care of interrupt shadow and TF. */ + gen_movl_seg_T0(s, op->n); + break; + case X86_OP_INT: + if (op->has_ea) { + gen_op_st_v(s, op->ot, v, s->A0); + } else { + gen_op_mov_reg_v(s, op->ot, op->n, v); + } + break; + case X86_OP_MMX: + break; + case X86_OP_SSE: + if (!op->has_ea && (s->prefix & PREFIX_VEX) && op->ot <= MO_128) { + tcg_gen_gvec_dup_imm(MO_64, + offsetof(CPUX86State, xmm_regs[op->n].ZMM_X(1)), + 16, 16, 0); + } + break; + case X86_OP_CR: + case X86_OP_DR: + default: + g_assert_not_reached(); + } +} + +static inline int vector_len(DisasContext *s, X86DecodedInsn *decode) +{ + if (decode->e.special == X86_SPECIAL_MMX && + !(s->prefix & (PREFIX_DATA | PREFIX_REPZ | PREFIX_REPNZ))) { + return 8; + } + return s->vex_l ? 32 : 16; +} + +static void prepare_update1_cc(X86DecodedInsn *decode, DisasContext *s, CCOp op) +{ + decode->cc_dst = s->T0; + decode->cc_op = op; +} + +static void prepare_update2_cc(X86DecodedInsn *decode, DisasContext *s, CCOp op) +{ + decode->cc_src = s->T1; + decode->cc_dst = s->T0; + decode->cc_op = op; +} + +static void gen_store_sse(DisasContext *s, X86DecodedInsn *decode, int src_ofs) +{ + MemOp ot = decode->op[0].ot; + int vec_len = vector_len(s, decode); + bool aligned = sse_needs_alignment(s, decode, ot); + + if (!decode->op[0].has_ea) { + tcg_gen_gvec_mov(MO_64, decode->op[0].offset, src_ofs, vec_len, vec_len); + return; + } + + switch (ot) { + case MO_64: + gen_stq_env_A0(s, src_ofs); + break; + case MO_128: + gen_sto_env_A0(s, src_ofs, aligned); + break; + case MO_256: + gen_sty_env_A0(s, src_ofs, aligned); + break; + default: + g_assert_not_reached(); + } +} + +static void gen_helper_pavgusb(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b) +{ + gen_helper_pavgb_mmx(env, reg_a, reg_a, reg_b); +} + +#define FN_3DNOW_MOVE ((SSEFunc_0_epp) (uintptr_t) 1) +static const SSEFunc_0_epp fns_3dnow[] = { + [0x0c] = gen_helper_pi2fw, + [0x0d] = gen_helper_pi2fd, + [0x1c] = gen_helper_pf2iw, + [0x1d] = gen_helper_pf2id, + [0x8a] = gen_helper_pfnacc, + [0x8e] = gen_helper_pfpnacc, + [0x90] = gen_helper_pfcmpge, + [0x94] = gen_helper_pfmin, + [0x96] = gen_helper_pfrcp, + [0x97] = gen_helper_pfrsqrt, + [0x9a] = gen_helper_pfsub, + [0x9e] = gen_helper_pfadd, + [0xa0] = gen_helper_pfcmpgt, + [0xa4] = gen_helper_pfmax, + [0xa6] = FN_3DNOW_MOVE, /* PFRCPIT1; no need to actually increase precision */ + [0xa7] = FN_3DNOW_MOVE, /* PFRSQIT1 */ + [0xb6] = FN_3DNOW_MOVE, /* PFRCPIT2 */ + [0xaa] = gen_helper_pfsubr, + [0xae] = gen_helper_pfacc, + [0xb0] = gen_helper_pfcmpeq, + [0xb4] = gen_helper_pfmul, + [0xb7] = gen_helper_pmulhrw_mmx, + [0xbb] = gen_helper_pswapd, + [0xbf] = gen_helper_pavgusb, +}; + +static void gen_3dnow(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + uint8_t b = decode->immediate; + SSEFunc_0_epp fn = b < ARRAY_SIZE(fns_3dnow) ? fns_3dnow[b] : NULL; + + if (!fn) { + gen_illegal_opcode(s); + return; + } + if (s->flags & HF_TS_MASK) { + gen_NM_exception(s); + return; + } + if (s->flags & HF_EM_MASK) { + gen_illegal_opcode(s); + return; + } + + gen_helper_enter_mmx(tcg_env); + if (fn == FN_3DNOW_MOVE) { + tcg_gen_ld_i64(s->tmp1_i64, tcg_env, decode->op[1].offset); + tcg_gen_st_i64(s->tmp1_i64, tcg_env, decode->op[0].offset); + } else { + fn(tcg_env, OP_PTR0, OP_PTR1); + } +} + +/* + * 00 = v*ps Vps, Hps, Wpd + * 66 = v*pd Vpd, Hpd, Wps + * f3 = v*ss Vss, Hss, Wps + * f2 = v*sd Vsd, Hsd, Wps + */ +static inline void gen_unary_fp_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + SSEFunc_0_epp pd_xmm, SSEFunc_0_epp ps_xmm, + SSEFunc_0_epp pd_ymm, SSEFunc_0_epp ps_ymm, + SSEFunc_0_eppp sd, SSEFunc_0_eppp ss) +{ + if ((s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) != 0) { + SSEFunc_0_eppp fn = s->prefix & PREFIX_REPZ ? ss : sd; + if (!fn) { + gen_illegal_opcode(s); + return; + } + fn(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); + } else { + SSEFunc_0_epp ps, pd, fn; + ps = s->vex_l ? ps_ymm : ps_xmm; + pd = s->vex_l ? pd_ymm : pd_xmm; + fn = s->prefix & PREFIX_DATA ? pd : ps; + if (!fn) { + gen_illegal_opcode(s); + return; + } + fn(tcg_env, OP_PTR0, OP_PTR2); + } +} +#define UNARY_FP_SSE(uname, lname) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + gen_unary_fp_sse(s, env, decode, \ + gen_helper_##lname##pd_xmm, \ + gen_helper_##lname##ps_xmm, \ + gen_helper_##lname##pd_ymm, \ + gen_helper_##lname##ps_ymm, \ + gen_helper_##lname##sd, \ + gen_helper_##lname##ss); \ +} +UNARY_FP_SSE(VSQRT, sqrt) + +/* + * 00 = v*ps Vps, Hps, Wpd + * 66 = v*pd Vpd, Hpd, Wps + * f3 = v*ss Vss, Hss, Wps + * f2 = v*sd Vsd, Hsd, Wps + */ +static inline void gen_fp_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + SSEFunc_0_eppp pd_xmm, SSEFunc_0_eppp ps_xmm, + SSEFunc_0_eppp pd_ymm, SSEFunc_0_eppp ps_ymm, + SSEFunc_0_eppp sd, SSEFunc_0_eppp ss) +{ + SSEFunc_0_eppp ps, pd, fn; + if ((s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) != 0) { + fn = s->prefix & PREFIX_REPZ ? ss : sd; + } else { + ps = s->vex_l ? ps_ymm : ps_xmm; + pd = s->vex_l ? pd_ymm : pd_xmm; + fn = s->prefix & PREFIX_DATA ? pd : ps; + } + if (fn) { + fn(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); + } else { + gen_illegal_opcode(s); + } +} + +#define FP_SSE(uname, lname) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + gen_fp_sse(s, env, decode, \ + gen_helper_##lname##pd_xmm, \ + gen_helper_##lname##ps_xmm, \ + gen_helper_##lname##pd_ymm, \ + gen_helper_##lname##ps_ymm, \ + gen_helper_##lname##sd, \ + gen_helper_##lname##ss); \ +} +FP_SSE(VADD, add) +FP_SSE(VMUL, mul) +FP_SSE(VSUB, sub) +FP_SSE(VMIN, min) +FP_SSE(VDIV, div) +FP_SSE(VMAX, max) + +#define FMA_SSE_PACKED(uname, ptr0, ptr1, ptr2, even, odd) \ +static void gen_##uname##Px(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + SSEFunc_0_eppppii xmm = s->vex_w ? gen_helper_fma4pd_xmm : gen_helper_fma4ps_xmm; \ + SSEFunc_0_eppppii ymm = s->vex_w ? gen_helper_fma4pd_ymm : gen_helper_fma4ps_ymm; \ + SSEFunc_0_eppppii fn = s->vex_l ? ymm : xmm; \ + \ + fn(tcg_env, OP_PTR0, ptr0, ptr1, ptr2, \ + tcg_constant_i32(even), \ + tcg_constant_i32((even) ^ (odd))); \ +} + +#define FMA_SSE(uname, ptr0, ptr1, ptr2, flags) \ +FMA_SSE_PACKED(uname, ptr0, ptr1, ptr2, flags, flags) \ +static void gen_##uname##Sx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + SSEFunc_0_eppppi fn = s->vex_w ? gen_helper_fma4sd : gen_helper_fma4ss; \ + \ + fn(tcg_env, OP_PTR0, ptr0, ptr1, ptr2, \ + tcg_constant_i32(flags)); \ +} \ + +FMA_SSE(VFMADD231, OP_PTR1, OP_PTR2, OP_PTR0, 0) +FMA_SSE(VFMADD213, OP_PTR1, OP_PTR0, OP_PTR2, 0) +FMA_SSE(VFMADD132, OP_PTR0, OP_PTR2, OP_PTR1, 0) + +FMA_SSE(VFNMADD231, OP_PTR1, OP_PTR2, OP_PTR0, float_muladd_negate_product) +FMA_SSE(VFNMADD213, OP_PTR1, OP_PTR0, OP_PTR2, float_muladd_negate_product) +FMA_SSE(VFNMADD132, OP_PTR0, OP_PTR2, OP_PTR1, float_muladd_negate_product) + +FMA_SSE(VFMSUB231, OP_PTR1, OP_PTR2, OP_PTR0, float_muladd_negate_c) +FMA_SSE(VFMSUB213, OP_PTR1, OP_PTR0, OP_PTR2, float_muladd_negate_c) +FMA_SSE(VFMSUB132, OP_PTR0, OP_PTR2, OP_PTR1, float_muladd_negate_c) + +FMA_SSE(VFNMSUB231, OP_PTR1, OP_PTR2, OP_PTR0, float_muladd_negate_c|float_muladd_negate_product) +FMA_SSE(VFNMSUB213, OP_PTR1, OP_PTR0, OP_PTR2, float_muladd_negate_c|float_muladd_negate_product) +FMA_SSE(VFNMSUB132, OP_PTR0, OP_PTR2, OP_PTR1, float_muladd_negate_c|float_muladd_negate_product) + +FMA_SSE_PACKED(VFMADDSUB231, OP_PTR1, OP_PTR2, OP_PTR0, float_muladd_negate_c, 0) +FMA_SSE_PACKED(VFMADDSUB213, OP_PTR1, OP_PTR0, OP_PTR2, float_muladd_negate_c, 0) +FMA_SSE_PACKED(VFMADDSUB132, OP_PTR0, OP_PTR2, OP_PTR1, float_muladd_negate_c, 0) + +FMA_SSE_PACKED(VFMSUBADD231, OP_PTR1, OP_PTR2, OP_PTR0, 0, float_muladd_negate_c) +FMA_SSE_PACKED(VFMSUBADD213, OP_PTR1, OP_PTR0, OP_PTR2, 0, float_muladd_negate_c) +FMA_SSE_PACKED(VFMSUBADD132, OP_PTR0, OP_PTR2, OP_PTR1, 0, float_muladd_negate_c) + +#define FP_UNPACK_SSE(uname, lname) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + /* PS maps to the DQ integer instruction, PD maps to QDQ. */ \ + gen_fp_sse(s, env, decode, \ + gen_helper_##lname##qdq_xmm, \ + gen_helper_##lname##dq_xmm, \ + gen_helper_##lname##qdq_ymm, \ + gen_helper_##lname##dq_ymm, \ + NULL, NULL); \ +} +FP_UNPACK_SSE(VUNPCKLPx, punpckl) +FP_UNPACK_SSE(VUNPCKHPx, punpckh) + +/* + * 00 = v*ps Vps, Wpd + * f3 = v*ss Vss, Wps + */ +static inline void gen_unary_fp32_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + SSEFunc_0_epp ps_xmm, + SSEFunc_0_epp ps_ymm, + SSEFunc_0_eppp ss) +{ + if ((s->prefix & (PREFIX_DATA | PREFIX_REPNZ)) != 0) { + goto illegal_op; + } else if (s->prefix & PREFIX_REPZ) { + if (!ss) { + goto illegal_op; + } + ss(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); + } else { + SSEFunc_0_epp fn = s->vex_l ? ps_ymm : ps_xmm; + if (!fn) { + goto illegal_op; + } + fn(tcg_env, OP_PTR0, OP_PTR2); + } + return; + +illegal_op: + gen_illegal_opcode(s); +} +#define UNARY_FP32_SSE(uname, lname) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + gen_unary_fp32_sse(s, env, decode, \ + gen_helper_##lname##ps_xmm, \ + gen_helper_##lname##ps_ymm, \ + gen_helper_##lname##ss); \ +} +UNARY_FP32_SSE(VRSQRT, rsqrt) +UNARY_FP32_SSE(VRCP, rcp) + +/* + * 66 = v*pd Vpd, Hpd, Wpd + * f2 = v*ps Vps, Hps, Wps + */ +static inline void gen_horizontal_fp_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + SSEFunc_0_eppp pd_xmm, SSEFunc_0_eppp ps_xmm, + SSEFunc_0_eppp pd_ymm, SSEFunc_0_eppp ps_ymm) +{ + SSEFunc_0_eppp ps, pd, fn; + ps = s->vex_l ? ps_ymm : ps_xmm; + pd = s->vex_l ? pd_ymm : pd_xmm; + fn = s->prefix & PREFIX_DATA ? pd : ps; + fn(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); +} +#define HORIZONTAL_FP_SSE(uname, lname) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + gen_horizontal_fp_sse(s, env, decode, \ + gen_helper_##lname##pd_xmm, gen_helper_##lname##ps_xmm, \ + gen_helper_##lname##pd_ymm, gen_helper_##lname##ps_ymm); \ +} +HORIZONTAL_FP_SSE(VHADD, hadd) +HORIZONTAL_FP_SSE(VHSUB, hsub) +HORIZONTAL_FP_SSE(VADDSUB, addsub) + +static inline void gen_ternary_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + int op3, SSEFunc_0_epppp xmm, SSEFunc_0_epppp ymm) +{ + SSEFunc_0_epppp fn = s->vex_l ? ymm : xmm; + TCGv_ptr ptr3 = tcg_temp_new_ptr(); + + /* The format of the fourth input is Lx */ + tcg_gen_addi_ptr(ptr3, tcg_env, ZMM_OFFSET(op3)); + fn(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, ptr3); +} +#define TERNARY_SSE(uname, uvname, lname) \ +static void gen_##uvname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + gen_ternary_sse(s, env, decode, (uint8_t)decode->immediate >> 4, \ + gen_helper_##lname##_xmm, gen_helper_##lname##_ymm); \ +} \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + gen_ternary_sse(s, env, decode, 0, \ + gen_helper_##lname##_xmm, gen_helper_##lname##_ymm); \ +} +TERNARY_SSE(BLENDVPS, VBLENDVPS, blendvps) +TERNARY_SSE(BLENDVPD, VBLENDVPD, blendvpd) +TERNARY_SSE(PBLENDVB, VPBLENDVB, pblendvb) + +static inline void gen_binary_imm_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + SSEFunc_0_epppi xmm, SSEFunc_0_epppi ymm) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + if (!s->vex_l) { + xmm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + } else { + ymm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + } +} + +#define BINARY_IMM_SSE(uname, lname) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + gen_binary_imm_sse(s, env, decode, \ + gen_helper_##lname##_xmm, \ + gen_helper_##lname##_ymm); \ +} + +BINARY_IMM_SSE(VBLENDPD, blendpd) +BINARY_IMM_SSE(VBLENDPS, blendps) +BINARY_IMM_SSE(VPBLENDW, pblendw) +BINARY_IMM_SSE(VDDPS, dpps) +#define gen_helper_dppd_ymm NULL +BINARY_IMM_SSE(VDDPD, dppd) +BINARY_IMM_SSE(VMPSADBW, mpsadbw) +BINARY_IMM_SSE(PCLMULQDQ, pclmulqdq) + + +#define UNARY_INT_GVEC(uname, func, ...) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + int vec_len = vector_len(s, decode); \ + \ + func(__VA_ARGS__, decode->op[0].offset, \ + decode->op[2].offset, vec_len, vec_len); \ +} +UNARY_INT_GVEC(PABSB, tcg_gen_gvec_abs, MO_8) +UNARY_INT_GVEC(PABSW, tcg_gen_gvec_abs, MO_16) +UNARY_INT_GVEC(PABSD, tcg_gen_gvec_abs, MO_32) +UNARY_INT_GVEC(VBROADCASTx128, tcg_gen_gvec_dup_mem, MO_128) +UNARY_INT_GVEC(VPBROADCASTB, tcg_gen_gvec_dup_mem, MO_8) +UNARY_INT_GVEC(VPBROADCASTW, tcg_gen_gvec_dup_mem, MO_16) +UNARY_INT_GVEC(VPBROADCASTD, tcg_gen_gvec_dup_mem, MO_32) +UNARY_INT_GVEC(VPBROADCASTQ, tcg_gen_gvec_dup_mem, MO_64) + + +#define BINARY_INT_GVEC(uname, func, ...) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + int vec_len = vector_len(s, decode); \ + \ + func(__VA_ARGS__, \ + decode->op[0].offset, decode->op[1].offset, \ + decode->op[2].offset, vec_len, vec_len); \ +} + +BINARY_INT_GVEC(PADDB, tcg_gen_gvec_add, MO_8) +BINARY_INT_GVEC(PADDW, tcg_gen_gvec_add, MO_16) +BINARY_INT_GVEC(PADDD, tcg_gen_gvec_add, MO_32) +BINARY_INT_GVEC(PADDQ, tcg_gen_gvec_add, MO_64) +BINARY_INT_GVEC(PADDSB, tcg_gen_gvec_ssadd, MO_8) +BINARY_INT_GVEC(PADDSW, tcg_gen_gvec_ssadd, MO_16) +BINARY_INT_GVEC(PADDUSB, tcg_gen_gvec_usadd, MO_8) +BINARY_INT_GVEC(PADDUSW, tcg_gen_gvec_usadd, MO_16) +BINARY_INT_GVEC(PAND, tcg_gen_gvec_and, MO_64) +BINARY_INT_GVEC(PCMPEQB, tcg_gen_gvec_cmp, TCG_COND_EQ, MO_8) +BINARY_INT_GVEC(PCMPEQD, tcg_gen_gvec_cmp, TCG_COND_EQ, MO_32) +BINARY_INT_GVEC(PCMPEQW, tcg_gen_gvec_cmp, TCG_COND_EQ, MO_16) +BINARY_INT_GVEC(PCMPEQQ, tcg_gen_gvec_cmp, TCG_COND_EQ, MO_64) +BINARY_INT_GVEC(PCMPGTB, tcg_gen_gvec_cmp, TCG_COND_GT, MO_8) +BINARY_INT_GVEC(PCMPGTW, tcg_gen_gvec_cmp, TCG_COND_GT, MO_16) +BINARY_INT_GVEC(PCMPGTD, tcg_gen_gvec_cmp, TCG_COND_GT, MO_32) +BINARY_INT_GVEC(PCMPGTQ, tcg_gen_gvec_cmp, TCG_COND_GT, MO_64) +BINARY_INT_GVEC(PMAXSB, tcg_gen_gvec_smax, MO_8) +BINARY_INT_GVEC(PMAXSW, tcg_gen_gvec_smax, MO_16) +BINARY_INT_GVEC(PMAXSD, tcg_gen_gvec_smax, MO_32) +BINARY_INT_GVEC(PMAXUB, tcg_gen_gvec_umax, MO_8) +BINARY_INT_GVEC(PMAXUW, tcg_gen_gvec_umax, MO_16) +BINARY_INT_GVEC(PMAXUD, tcg_gen_gvec_umax, MO_32) +BINARY_INT_GVEC(PMINSB, tcg_gen_gvec_smin, MO_8) +BINARY_INT_GVEC(PMINSW, tcg_gen_gvec_smin, MO_16) +BINARY_INT_GVEC(PMINSD, tcg_gen_gvec_smin, MO_32) +BINARY_INT_GVEC(PMINUB, tcg_gen_gvec_umin, MO_8) +BINARY_INT_GVEC(PMINUW, tcg_gen_gvec_umin, MO_16) +BINARY_INT_GVEC(PMINUD, tcg_gen_gvec_umin, MO_32) +BINARY_INT_GVEC(PMULLW, tcg_gen_gvec_mul, MO_16) +BINARY_INT_GVEC(PMULLD, tcg_gen_gvec_mul, MO_32) +BINARY_INT_GVEC(POR, tcg_gen_gvec_or, MO_64) +BINARY_INT_GVEC(PSUBB, tcg_gen_gvec_sub, MO_8) +BINARY_INT_GVEC(PSUBW, tcg_gen_gvec_sub, MO_16) +BINARY_INT_GVEC(PSUBD, tcg_gen_gvec_sub, MO_32) +BINARY_INT_GVEC(PSUBQ, tcg_gen_gvec_sub, MO_64) +BINARY_INT_GVEC(PSUBSB, tcg_gen_gvec_sssub, MO_8) +BINARY_INT_GVEC(PSUBSW, tcg_gen_gvec_sssub, MO_16) +BINARY_INT_GVEC(PSUBUSB, tcg_gen_gvec_ussub, MO_8) +BINARY_INT_GVEC(PSUBUSW, tcg_gen_gvec_ussub, MO_16) +BINARY_INT_GVEC(PXOR, tcg_gen_gvec_xor, MO_64) + + +/* + * 00 = p* Pq, Qq (if mmx not NULL; no VEX) + * 66 = vp* Vx, Hx, Wx + * + * These are really the same encoding, because 1) V is the same as P when VEX.V + * is not present 2) P and Q are the same as H and W apart from MM/XMM + */ +static inline void gen_binary_int_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + SSEFunc_0_eppp mmx, SSEFunc_0_eppp xmm, SSEFunc_0_eppp ymm) +{ + assert(!!mmx == !!(decode->e.special == X86_SPECIAL_MMX)); + + if (mmx && (s->prefix & PREFIX_VEX) && !(s->prefix & PREFIX_DATA)) { + /* VEX encoding is not applicable to MMX instructions. */ + gen_illegal_opcode(s); + return; + } + if (!(s->prefix & PREFIX_DATA)) { + mmx(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); + } else if (!s->vex_l) { + xmm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); + } else { + ymm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); + } +} + + +#define BINARY_INT_MMX(uname, lname) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + gen_binary_int_sse(s, env, decode, \ + gen_helper_##lname##_mmx, \ + gen_helper_##lname##_xmm, \ + gen_helper_##lname##_ymm); \ +} +BINARY_INT_MMX(PUNPCKLBW, punpcklbw) +BINARY_INT_MMX(PUNPCKLWD, punpcklwd) +BINARY_INT_MMX(PUNPCKLDQ, punpckldq) +BINARY_INT_MMX(PACKSSWB, packsswb) +BINARY_INT_MMX(PACKUSWB, packuswb) +BINARY_INT_MMX(PUNPCKHBW, punpckhbw) +BINARY_INT_MMX(PUNPCKHWD, punpckhwd) +BINARY_INT_MMX(PUNPCKHDQ, punpckhdq) +BINARY_INT_MMX(PACKSSDW, packssdw) + +BINARY_INT_MMX(PAVGB, pavgb) +BINARY_INT_MMX(PAVGW, pavgw) +BINARY_INT_MMX(PMADDWD, pmaddwd) +BINARY_INT_MMX(PMULHUW, pmulhuw) +BINARY_INT_MMX(PMULHW, pmulhw) +BINARY_INT_MMX(PMULUDQ, pmuludq) +BINARY_INT_MMX(PSADBW, psadbw) + +BINARY_INT_MMX(PSLLW_r, psllw) +BINARY_INT_MMX(PSLLD_r, pslld) +BINARY_INT_MMX(PSLLQ_r, psllq) +BINARY_INT_MMX(PSRLW_r, psrlw) +BINARY_INT_MMX(PSRLD_r, psrld) +BINARY_INT_MMX(PSRLQ_r, psrlq) +BINARY_INT_MMX(PSRAW_r, psraw) +BINARY_INT_MMX(PSRAD_r, psrad) + +BINARY_INT_MMX(PHADDW, phaddw) +BINARY_INT_MMX(PHADDSW, phaddsw) +BINARY_INT_MMX(PHADDD, phaddd) +BINARY_INT_MMX(PHSUBW, phsubw) +BINARY_INT_MMX(PHSUBSW, phsubsw) +BINARY_INT_MMX(PHSUBD, phsubd) +BINARY_INT_MMX(PMADDUBSW, pmaddubsw) +BINARY_INT_MMX(PSHUFB, pshufb) +BINARY_INT_MMX(PSIGNB, psignb) +BINARY_INT_MMX(PSIGNW, psignw) +BINARY_INT_MMX(PSIGND, psignd) +BINARY_INT_MMX(PMULHRSW, pmulhrsw) + +/* Instructions with no MMX equivalent. */ +#define BINARY_INT_SSE(uname, lname) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + gen_binary_int_sse(s, env, decode, \ + NULL, \ + gen_helper_##lname##_xmm, \ + gen_helper_##lname##_ymm); \ +} + +/* Instructions with no MMX equivalent. */ +BINARY_INT_SSE(PUNPCKLQDQ, punpcklqdq) +BINARY_INT_SSE(PUNPCKHQDQ, punpckhqdq) +BINARY_INT_SSE(VPACKUSDW, packusdw) +BINARY_INT_SSE(VPERMILPS, vpermilps) +BINARY_INT_SSE(VPERMILPD, vpermilpd) +BINARY_INT_SSE(VMASKMOVPS, vpmaskmovd) +BINARY_INT_SSE(VMASKMOVPD, vpmaskmovq) + +BINARY_INT_SSE(PMULDQ, pmuldq) + +BINARY_INT_SSE(VAESDEC, aesdec) +BINARY_INT_SSE(VAESDECLAST, aesdeclast) +BINARY_INT_SSE(VAESENC, aesenc) +BINARY_INT_SSE(VAESENCLAST, aesenclast) + +#define UNARY_CMP_SSE(uname, lname) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + if (!s->vex_l) { \ + gen_helper_##lname##_xmm(tcg_env, OP_PTR1, OP_PTR2); \ + } else { \ + gen_helper_##lname##_ymm(tcg_env, OP_PTR1, OP_PTR2); \ + } \ + set_cc_op(s, CC_OP_EFLAGS); \ +} +UNARY_CMP_SSE(VPTEST, ptest) +UNARY_CMP_SSE(VTESTPS, vtestps) +UNARY_CMP_SSE(VTESTPD, vtestpd) + +static inline void gen_unary_int_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + SSEFunc_0_epp xmm, SSEFunc_0_epp ymm) +{ + if (!s->vex_l) { + xmm(tcg_env, OP_PTR0, OP_PTR2); + } else { + ymm(tcg_env, OP_PTR0, OP_PTR2); + } +} + +#define UNARY_INT_SSE(uname, lname) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + gen_unary_int_sse(s, env, decode, \ + gen_helper_##lname##_xmm, \ + gen_helper_##lname##_ymm); \ +} + +UNARY_INT_SSE(VPMOVSXBW, pmovsxbw) +UNARY_INT_SSE(VPMOVSXBD, pmovsxbd) +UNARY_INT_SSE(VPMOVSXBQ, pmovsxbq) +UNARY_INT_SSE(VPMOVSXWD, pmovsxwd) +UNARY_INT_SSE(VPMOVSXWQ, pmovsxwq) +UNARY_INT_SSE(VPMOVSXDQ, pmovsxdq) + +UNARY_INT_SSE(VPMOVZXBW, pmovzxbw) +UNARY_INT_SSE(VPMOVZXBD, pmovzxbd) +UNARY_INT_SSE(VPMOVZXBQ, pmovzxbq) +UNARY_INT_SSE(VPMOVZXWD, pmovzxwd) +UNARY_INT_SSE(VPMOVZXWQ, pmovzxwq) +UNARY_INT_SSE(VPMOVZXDQ, pmovzxdq) + +UNARY_INT_SSE(VMOVSLDUP, pmovsldup) +UNARY_INT_SSE(VMOVSHDUP, pmovshdup) +UNARY_INT_SSE(VMOVDDUP, pmovdldup) + +UNARY_INT_SSE(VCVTDQ2PD, cvtdq2pd) +UNARY_INT_SSE(VCVTPD2DQ, cvtpd2dq) +UNARY_INT_SSE(VCVTTPD2DQ, cvttpd2dq) +UNARY_INT_SSE(VCVTDQ2PS, cvtdq2ps) +UNARY_INT_SSE(VCVTPS2DQ, cvtps2dq) +UNARY_INT_SSE(VCVTTPS2DQ, cvttps2dq) +UNARY_INT_SSE(VCVTPH2PS, cvtph2ps) + + +static inline void gen_unary_imm_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + SSEFunc_0_ppi xmm, SSEFunc_0_ppi ymm) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + if (!s->vex_l) { + xmm(OP_PTR0, OP_PTR1, imm); + } else { + ymm(OP_PTR0, OP_PTR1, imm); + } +} + +#define UNARY_IMM_SSE(uname, lname) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + gen_unary_imm_sse(s, env, decode, \ + gen_helper_##lname##_xmm, \ + gen_helper_##lname##_ymm); \ +} + +UNARY_IMM_SSE(PSHUFD, pshufd) +UNARY_IMM_SSE(PSHUFHW, pshufhw) +UNARY_IMM_SSE(PSHUFLW, pshuflw) +#define gen_helper_vpermq_xmm NULL +UNARY_IMM_SSE(VPERMQ, vpermq) +UNARY_IMM_SSE(VPERMILPS_i, vpermilps_imm) +UNARY_IMM_SSE(VPERMILPD_i, vpermilpd_imm) + +static inline void gen_unary_imm_fp_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + SSEFunc_0_eppi xmm, SSEFunc_0_eppi ymm) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + if (!s->vex_l) { + xmm(tcg_env, OP_PTR0, OP_PTR1, imm); + } else { + ymm(tcg_env, OP_PTR0, OP_PTR1, imm); + } +} + +#define UNARY_IMM_FP_SSE(uname, lname) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + gen_unary_imm_fp_sse(s, env, decode, \ + gen_helper_##lname##_xmm, \ + gen_helper_##lname##_ymm); \ +} + +UNARY_IMM_FP_SSE(VROUNDPS, roundps) +UNARY_IMM_FP_SSE(VROUNDPD, roundpd) + +static inline void gen_vexw_avx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + SSEFunc_0_eppp d_xmm, SSEFunc_0_eppp q_xmm, + SSEFunc_0_eppp d_ymm, SSEFunc_0_eppp q_ymm) +{ + SSEFunc_0_eppp d = s->vex_l ? d_ymm : d_xmm; + SSEFunc_0_eppp q = s->vex_l ? q_ymm : q_xmm; + SSEFunc_0_eppp fn = s->vex_w ? q : d; + fn(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); +} + +/* VEX.W affects whether to operate on 32- or 64-bit elements. */ +#define VEXW_AVX(uname, lname) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + gen_vexw_avx(s, env, decode, \ + gen_helper_##lname##d_xmm, gen_helper_##lname##q_xmm, \ + gen_helper_##lname##d_ymm, gen_helper_##lname##q_ymm); \ +} +VEXW_AVX(VPSLLV, vpsllv) +VEXW_AVX(VPSRLV, vpsrlv) +VEXW_AVX(VPSRAV, vpsrav) +VEXW_AVX(VPMASKMOV, vpmaskmov) + +/* Same as above, but with extra arguments to the helper. */ +static inline void gen_vsib_avx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + SSEFunc_0_epppti d_xmm, SSEFunc_0_epppti q_xmm, + SSEFunc_0_epppti d_ymm, SSEFunc_0_epppti q_ymm) +{ + SSEFunc_0_epppti d = s->vex_l ? d_ymm : d_xmm; + SSEFunc_0_epppti q = s->vex_l ? q_ymm : q_xmm; + SSEFunc_0_epppti fn = s->vex_w ? q : d; + TCGv_i32 scale = tcg_constant_i32(decode->mem.scale); + TCGv_ptr index = tcg_temp_new_ptr(); + + /* Pass third input as (index, base, scale) */ + tcg_gen_addi_ptr(index, tcg_env, ZMM_OFFSET(decode->mem.index)); + fn(tcg_env, OP_PTR0, OP_PTR1, index, s->A0, scale); + + /* + * There are two output operands, so zero OP1's high 128 bits + * in the VEX.128 case. + */ + if (!s->vex_l) { + int ymmh_ofs = vector_elem_offset(&decode->op[1], MO_128, 1); + tcg_gen_gvec_dup_imm(MO_64, ymmh_ofs, 16, 16, 0); + } +} +#define VSIB_AVX(uname, lname) \ +static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +{ \ + gen_vsib_avx(s, env, decode, \ + gen_helper_##lname##d_xmm, gen_helper_##lname##q_xmm, \ + gen_helper_##lname##d_ymm, gen_helper_##lname##q_ymm); \ +} +VSIB_AVX(VPGATHERD, vpgatherd) +VSIB_AVX(VPGATHERQ, vpgatherq) + +/* ADCX/ADOX do not have memory operands and can use set_cc_op. */ +static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op) +{ + int opposite_cc_op; + TCGv carry_in = NULL; + TCGv carry_out = (cc_op == CC_OP_ADCX ? cpu_cc_dst : cpu_cc_src2); + TCGv zero; + + if (cc_op == s->cc_op || s->cc_op == CC_OP_ADCOX) { + /* Re-use the carry-out from a previous round. */ + carry_in = carry_out; + } else { + /* We don't have a carry-in, get it out of EFLAGS. */ + if (s->cc_op != CC_OP_ADCX && s->cc_op != CC_OP_ADOX) { + gen_compute_eflags(s); + } + carry_in = s->tmp0; + tcg_gen_extract_tl(carry_in, cpu_cc_src, + ctz32(cc_op == CC_OP_ADCX ? CC_C : CC_O), 1); + } + + switch (ot) { +#ifdef TARGET_X86_64 + case MO_32: + /* If TL is 64-bit just do everything in 64-bit arithmetic. */ + tcg_gen_ext32u_tl(s->T0, s->T0); + tcg_gen_ext32u_tl(s->T1, s->T1); + tcg_gen_add_i64(s->T0, s->T0, s->T1); + tcg_gen_add_i64(s->T0, s->T0, carry_in); + tcg_gen_shri_i64(carry_out, s->T0, 32); + break; +#endif + default: + zero = tcg_constant_tl(0); + tcg_gen_add2_tl(s->T0, carry_out, s->T0, zero, carry_in, zero); + tcg_gen_add2_tl(s->T0, carry_out, s->T0, carry_out, s->T1, zero); + break; + } + + opposite_cc_op = cc_op == CC_OP_ADCX ? CC_OP_ADOX : CC_OP_ADCX; + if (s->cc_op == CC_OP_ADCOX || s->cc_op == opposite_cc_op) { + /* Merge with the carry-out from the opposite instruction. */ + set_cc_op(s, CC_OP_ADCOX); + } else { + set_cc_op(s, cc_op); + } +} + +static void gen_ADCX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_ADCOX(s, env, decode->op[0].ot, CC_OP_ADCX); +} + +static void gen_ADOX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_ADCOX(s, env, decode->op[0].ot, CC_OP_ADOX); +} + +static void gen_ANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + tcg_gen_andc_tl(s->T0, s->T1, s->T0); + prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); +} + +static void gen_BEXTR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv bound = tcg_constant_tl(ot == MO_64 ? 63 : 31); + TCGv zero = tcg_constant_tl(0); + TCGv mone = tcg_constant_tl(-1); + + /* + * Extract START, and shift the operand. + * Shifts larger than operand size get zeros. + */ + tcg_gen_ext8u_tl(s->A0, s->T1); + tcg_gen_shr_tl(s->T0, s->T0, s->A0); + + tcg_gen_movcond_tl(TCG_COND_LEU, s->T0, s->A0, bound, s->T0, zero); + + /* + * Extract the LEN into an inverse mask. Lengths larger than + * operand size get all zeros, length 0 gets all ones. + */ + tcg_gen_extract_tl(s->A0, s->T1, 8, 8); + tcg_gen_shl_tl(s->T1, mone, s->A0); + tcg_gen_movcond_tl(TCG_COND_LEU, s->T1, s->A0, bound, s->T1, zero); + tcg_gen_andc_tl(s->T0, s->T0, s->T1); + + prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); +} + +/* BLSI do not have memory operands and can use set_cc_op. */ +static void gen_BLSI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + tcg_gen_mov_tl(cpu_cc_src, s->T0); + tcg_gen_neg_tl(s->T1, s->T0); + tcg_gen_and_tl(s->T0, s->T0, s->T1); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + set_cc_op(s, CC_OP_BMILGB + ot); +} + +/* BLSMSK do not have memory operands and can use set_cc_op. */ +static void gen_BLSMSK(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + tcg_gen_mov_tl(cpu_cc_src, s->T0); + tcg_gen_subi_tl(s->T1, s->T0, 1); + tcg_gen_xor_tl(s->T0, s->T0, s->T1); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + set_cc_op(s, CC_OP_BMILGB + ot); +} + +/* BLSR do not have memory operands and can use set_cc_op. */ +static void gen_BLSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + tcg_gen_mov_tl(cpu_cc_src, s->T0); + tcg_gen_subi_tl(s->T1, s->T0, 1); + tcg_gen_and_tl(s->T0, s->T0, s->T1); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + set_cc_op(s, CC_OP_BMILGB + ot); +} + +static void gen_BZHI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv bound = tcg_constant_tl(ot == MO_64 ? 63 : 31); + TCGv zero = tcg_constant_tl(0); + TCGv mone = tcg_constant_tl(-1); + + tcg_gen_ext8u_tl(s->T1, s->T1); + + tcg_gen_shl_tl(s->A0, mone, s->T1); + tcg_gen_movcond_tl(TCG_COND_LEU, s->A0, s->T1, bound, s->A0, zero); + tcg_gen_andc_tl(s->T0, s->T0, s->A0); + /* + * Note that since we're using BMILG (in order to get O + * cleared) we need to store the inverse into C. + */ + tcg_gen_setcond_tl(TCG_COND_LEU, s->T1, s->T1, bound); + prepare_update2_cc(decode, s, CC_OP_BMILGB + ot); +} + +static void gen_CMPccXADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGLabel *label_top = gen_new_label(); + TCGLabel *label_bottom = gen_new_label(); + TCGv oldv = tcg_temp_new(); + TCGv newv = tcg_temp_new(); + TCGv cmpv = tcg_temp_new(); + TCGCond cond; + + TCGv cmp_lhs, cmp_rhs; + MemOp ot, ot_full; + + int jcc_op = (decode->b >> 1) & 7; + static const TCGCond cond_table[8] = { + [JCC_O] = TCG_COND_LT, /* test sign bit by comparing against 0 */ + [JCC_B] = TCG_COND_LTU, + [JCC_Z] = TCG_COND_EQ, + [JCC_BE] = TCG_COND_LEU, + [JCC_S] = TCG_COND_LT, /* test sign bit by comparing against 0 */ + [JCC_P] = TCG_COND_EQ, /* even parity - tests low bit of popcount */ + [JCC_L] = TCG_COND_LT, + [JCC_LE] = TCG_COND_LE, + }; + + cond = cond_table[jcc_op]; + if (decode->b & 1) { + cond = tcg_invert_cond(cond); + } + + ot = decode->op[0].ot; + ot_full = ot | MO_LE; + if (jcc_op >= JCC_S) { + /* + * Sign-extend values before subtracting for S, P (zero/sign extension + * does not matter there) L, LE and their inverses. + */ + ot_full |= MO_SIGN; + } + + /* + * cmpv will be moved to cc_src *after* cpu_regs[] is written back, so use + * tcg_gen_ext_tl instead of gen_ext_tl. + */ + tcg_gen_ext_tl(cmpv, cpu_regs[decode->op[1].n], ot_full); + + /* + * Cmpxchg loop starts here. + * - s->T1: addition operand (from decoder) + * - s->A0: dest address (from decoder) + * - s->cc_srcT: memory operand (lhs for comparison) + * - cmpv: rhs for comparison + */ + gen_set_label(label_top); + gen_op_ld_v(s, ot_full, s->cc_srcT, s->A0); + tcg_gen_sub_tl(s->T0, s->cc_srcT, cmpv); + + /* Compute the comparison result by hand, to avoid clobbering cc_*. */ + switch (jcc_op) { + case JCC_O: + /* (src1 ^ src2) & (src1 ^ dst). newv is only used here for a moment */ + tcg_gen_xor_tl(newv, s->cc_srcT, s->T0); + tcg_gen_xor_tl(s->tmp0, s->cc_srcT, cmpv); + tcg_gen_and_tl(s->tmp0, s->tmp0, newv); + tcg_gen_sextract_tl(s->tmp0, s->tmp0, 0, 8 << ot); + cmp_lhs = s->tmp0, cmp_rhs = tcg_constant_tl(0); + break; + + case JCC_P: + tcg_gen_ext8u_tl(s->tmp0, s->T0); + tcg_gen_ctpop_tl(s->tmp0, s->tmp0); + tcg_gen_andi_tl(s->tmp0, s->tmp0, 1); + cmp_lhs = s->tmp0, cmp_rhs = tcg_constant_tl(0); + break; + + case JCC_S: + tcg_gen_sextract_tl(s->tmp0, s->T0, 0, 8 << ot); + cmp_lhs = s->tmp0, cmp_rhs = tcg_constant_tl(0); + break; + + default: + cmp_lhs = s->cc_srcT, cmp_rhs = cmpv; + break; + } + + /* Compute new value: if condition does not hold, just store back s->cc_srcT */ + tcg_gen_add_tl(newv, s->cc_srcT, s->T1); + tcg_gen_movcond_tl(cond, newv, cmp_lhs, cmp_rhs, newv, s->cc_srcT); + tcg_gen_atomic_cmpxchg_tl(oldv, s->A0, s->cc_srcT, newv, s->mem_index, ot_full); + + /* Exit unconditionally if cmpxchg succeeded. */ + tcg_gen_brcond_tl(TCG_COND_EQ, oldv, s->cc_srcT, label_bottom); + + /* Try again if there was actually a store to make. */ + tcg_gen_brcond_tl(cond, cmp_lhs, cmp_rhs, label_top); + gen_set_label(label_bottom); + + /* Store old value to registers only after a successful store. */ + gen_writeback(s, decode, 1, s->cc_srcT); + + decode->cc_dst = s->T0; + decode->cc_src = cmpv; + decode->cc_op = CC_OP_SUBB + ot; +} + +static void gen_CRC32(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_crc32(s->T0, s->tmp2_i32, s->T1, tcg_constant_i32(8 << ot)); +} + +static void gen_CVTPI2Px(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_enter_mmx(tcg_env); + if (s->prefix & PREFIX_DATA) { + gen_helper_cvtpi2pd(tcg_env, OP_PTR0, OP_PTR2); + } else { + gen_helper_cvtpi2ps(tcg_env, OP_PTR0, OP_PTR2); + } +} + +static void gen_CVTPx2PI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_enter_mmx(tcg_env); + if (s->prefix & PREFIX_DATA) { + gen_helper_cvtpd2pi(tcg_env, OP_PTR0, OP_PTR2); + } else { + gen_helper_cvtps2pi(tcg_env, OP_PTR0, OP_PTR2); + } +} + +static void gen_CVTTPx2PI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_enter_mmx(tcg_env); + if (s->prefix & PREFIX_DATA) { + gen_helper_cvttpd2pi(tcg_env, OP_PTR0, OP_PTR2); + } else { + gen_helper_cvttps2pi(tcg_env, OP_PTR0, OP_PTR2); + } +} + +static void gen_EMMS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_emms(tcg_env); +} + +static void gen_EXTRQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 length = tcg_constant_i32(decode->immediate & 63); + TCGv_i32 index = tcg_constant_i32((decode->immediate >> 8) & 63); + + gen_helper_extrq_i(tcg_env, OP_PTR0, index, length); +} + +static void gen_EXTRQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_extrq_r(tcg_env, OP_PTR0, OP_PTR2); +} + +static void gen_INSERTQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 length = tcg_constant_i32(decode->immediate & 63); + TCGv_i32 index = tcg_constant_i32((decode->immediate >> 8) & 63); + + gen_helper_insertq_i(tcg_env, OP_PTR0, OP_PTR1, index, length); +} + +static void gen_INSERTQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_insertq_r(tcg_env, OP_PTR0, OP_PTR2); +} + +static void gen_LDMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T1); + gen_helper_ldmxcsr(tcg_env, s->tmp2_i32); +} + +static void gen_MASKMOV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_lea_v_seg(s, s->aflag, cpu_regs[R_EDI], R_DS, s->override); + + if (s->prefix & PREFIX_DATA) { + gen_helper_maskmov_xmm(tcg_env, OP_PTR1, OP_PTR2, s->A0); + } else { + gen_helper_maskmov_mmx(tcg_env, OP_PTR1, OP_PTR2, s->A0); + } +} + +static void gen_MOVBE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + /* M operand type does not load/store */ + if (decode->e.op0 == X86_TYPE_M) { + tcg_gen_qemu_st_tl(s->T0, s->A0, s->mem_index, ot | MO_BE); + } else { + tcg_gen_qemu_ld_tl(s->T0, s->A0, s->mem_index, ot | MO_BE); + } +} + +static void gen_MOVD_from(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + + switch (ot) { + case MO_32: +#ifdef TARGET_X86_64 + tcg_gen_ld32u_tl(s->T0, tcg_env, decode->op[2].offset); + break; + case MO_64: +#endif + tcg_gen_ld_tl(s->T0, tcg_env, decode->op[2].offset); + break; + default: + abort(); + } +} + +static void gen_MOVD_to(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + int vec_len = vector_len(s, decode); + int lo_ofs = vector_elem_offset(&decode->op[0], ot, 0); + + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + + switch (ot) { + case MO_32: +#ifdef TARGET_X86_64 + tcg_gen_st32_tl(s->T1, tcg_env, lo_ofs); + break; + case MO_64: +#endif + tcg_gen_st_tl(s->T1, tcg_env, lo_ofs); + break; + default: + g_assert_not_reached(); + } +} + +static void gen_MOVDQ(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_store_sse(s, decode, decode->op[2].offset); +} + +static void gen_MOVMSK(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + typeof(gen_helper_movmskps_ymm) *ps, *pd, *fn; + ps = s->vex_l ? gen_helper_movmskps_ymm : gen_helper_movmskps_xmm; + pd = s->vex_l ? gen_helper_movmskpd_ymm : gen_helper_movmskpd_xmm; + fn = s->prefix & PREFIX_DATA ? pd : ps; + fn(s->tmp2_i32, tcg_env, OP_PTR2); + tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); +} + +static void gen_MOVQ(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + int lo_ofs = vector_elem_offset(&decode->op[0], MO_64, 0); + + tcg_gen_ld_i64(s->tmp1_i64, tcg_env, decode->op[2].offset); + if (decode->op[0].has_ea) { + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); + } else { + /* + * tcg_gen_gvec_dup_i64(MO_64, op0.offset, 8, vec_len, s->tmp1_64) would + * seem to work, but it does not on big-endian platforms; the cleared parts + * are always at higher addresses, but cross-endian emulation inverts the + * byte order so that the cleared parts need to be at *lower* addresses. + * Because oprsz is 8, we see this here even for SSE; but more in general, + * it disqualifies using oprsz < maxsz to emulate VEX128. + */ + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + tcg_gen_st_i64(s->tmp1_i64, tcg_env, lo_ofs); + } +} + +static void gen_MOVq_dq(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_enter_mmx(tcg_env); + /* Otherwise the same as any other movq. */ + return gen_MOVQ(s, env, decode); +} + +static void gen_MULX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + /* low part of result in VEX.vvvv, high in MODRM */ + switch (ot) { + case MO_32: +#ifdef TARGET_X86_64 + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); + tcg_gen_mulu2_i32(s->tmp2_i32, s->tmp3_i32, + s->tmp2_i32, s->tmp3_i32); + tcg_gen_extu_i32_tl(cpu_regs[s->vex_v], s->tmp2_i32); + tcg_gen_extu_i32_tl(s->T0, s->tmp3_i32); + break; + + case MO_64: +#endif + tcg_gen_mulu2_tl(cpu_regs[s->vex_v], s->T0, s->T0, s->T1); + break; + + default: + g_assert_not_reached(); + } +} + +static void gen_PALIGNR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + if (!(s->prefix & PREFIX_DATA)) { + gen_helper_palignr_mmx(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + } else if (!s->vex_l) { + gen_helper_palignr_xmm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + } else { + gen_helper_palignr_ymm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + } +} + +static void gen_PANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + /* Careful, operand order is reversed! */ + tcg_gen_gvec_andc(MO_64, + decode->op[0].offset, decode->op[2].offset, + decode->op[1].offset, vec_len, vec_len); +} + +static void gen_PCMPESTRI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + gen_helper_pcmpestri_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); + set_cc_op(s, CC_OP_EFLAGS); +} + +static void gen_PCMPESTRM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + gen_helper_pcmpestrm_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); + set_cc_op(s, CC_OP_EFLAGS); + if ((s->prefix & PREFIX_VEX) && !s->vex_l) { + tcg_gen_gvec_dup_imm(MO_64, offsetof(CPUX86State, xmm_regs[0].ZMM_X(1)), + 16, 16, 0); + } +} + +static void gen_PCMPISTRI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + gen_helper_pcmpistri_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); + set_cc_op(s, CC_OP_EFLAGS); +} + +static void gen_PCMPISTRM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + gen_helper_pcmpistrm_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); + set_cc_op(s, CC_OP_EFLAGS); + if ((s->prefix & PREFIX_VEX) && !s->vex_l) { + tcg_gen_gvec_dup_imm(MO_64, offsetof(CPUX86State, xmm_regs[0].ZMM_X(1)), + 16, 16, 0); + } +} + +static void gen_PDEP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_pdep(s->T0, s->T0, s->T1); +} + +static void gen_PEXT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_pext(s->T0, s->T0, s->T1); +} + +static inline void gen_pextr(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, MemOp ot) +{ + int vec_len = vector_len(s, decode); + int mask = (vec_len >> ot) - 1; + int val = decode->immediate & mask; + + switch (ot) { + case MO_8: + tcg_gen_ld8u_tl(s->T0, tcg_env, vector_elem_offset(&decode->op[1], ot, val)); + break; + case MO_16: + tcg_gen_ld16u_tl(s->T0, tcg_env, vector_elem_offset(&decode->op[1], ot, val)); + break; + case MO_32: +#ifdef TARGET_X86_64 + tcg_gen_ld32u_tl(s->T0, tcg_env, vector_elem_offset(&decode->op[1], ot, val)); + break; + case MO_64: +#endif + tcg_gen_ld_tl(s->T0, tcg_env, vector_elem_offset(&decode->op[1], ot, val)); + break; + default: + abort(); + } +} + +static void gen_PEXTRB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_pextr(s, env, decode, MO_8); +} + +static void gen_PEXTRW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_pextr(s, env, decode, MO_16); +} + +static void gen_PEXTR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + gen_pextr(s, env, decode, ot); +} + +static inline void gen_pinsr(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, MemOp ot) +{ + int vec_len = vector_len(s, decode); + int mask = (vec_len >> ot) - 1; + int val = decode->immediate & mask; + + if (decode->op[1].offset != decode->op[0].offset) { + assert(vec_len == 16); + gen_store_sse(s, decode, decode->op[1].offset); + } + + switch (ot) { + case MO_8: + tcg_gen_st8_tl(s->T1, tcg_env, vector_elem_offset(&decode->op[0], ot, val)); + break; + case MO_16: + tcg_gen_st16_tl(s->T1, tcg_env, vector_elem_offset(&decode->op[0], ot, val)); + break; + case MO_32: +#ifdef TARGET_X86_64 + tcg_gen_st32_tl(s->T1, tcg_env, vector_elem_offset(&decode->op[0], ot, val)); + break; + case MO_64: +#endif + tcg_gen_st_tl(s->T1, tcg_env, vector_elem_offset(&decode->op[0], ot, val)); + break; + default: + abort(); + } +} + +static void gen_PINSRB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_pinsr(s, env, decode, MO_8); +} + +static void gen_PINSRW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_pinsr(s, env, decode, MO_16); +} + +static void gen_PINSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_pinsr(s, env, decode, decode->op[2].ot); +} + +static void gen_pmovmskb_i64(TCGv_i64 d, TCGv_i64 s) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_andi_i64(d, s, 0x8080808080808080ull); + + /* + * After each shift+or pair: + * 0: a.......b.......c.......d.......e.......f.......g.......h....... + * 7: ab......bc......cd......de......ef......fg......gh......h....... + * 14: abcd....bcde....cdef....defg....efgh....fgh.....gh......h....... + * 28: abcdefghbcdefgh.cdefgh..defgh...efgh....fgh.....gh......h....... + * The result is left in the high bits of the word. + */ + tcg_gen_shli_i64(t, d, 7); + tcg_gen_or_i64(d, d, t); + tcg_gen_shli_i64(t, d, 14); + tcg_gen_or_i64(d, d, t); + tcg_gen_shli_i64(t, d, 28); + tcg_gen_or_i64(d, d, t); +} + +static void gen_pmovmskb_vec(unsigned vece, TCGv_vec d, TCGv_vec s) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + TCGv_vec m = tcg_constant_vec_matching(d, MO_8, 0x80); + + /* See above */ + tcg_gen_and_vec(vece, d, s, m); + tcg_gen_shli_vec(vece, t, d, 7); + tcg_gen_or_vec(vece, d, d, t); + tcg_gen_shli_vec(vece, t, d, 14); + tcg_gen_or_vec(vece, d, d, t); + tcg_gen_shli_vec(vece, t, d, 28); + tcg_gen_or_vec(vece, d, d, t); +} + +#ifdef TARGET_X86_64 +#define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i64 +#else +#define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i32 +#endif + +static void gen_PMOVMSKB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + static const TCGOpcode vecop_list[] = { INDEX_op_shli_vec, 0 }; + static const GVecGen2 g = { + .fni8 = gen_pmovmskb_i64, + .fniv = gen_pmovmskb_vec, + .opt_opc = vecop_list, + .vece = MO_64, + .prefer_i64 = TCG_TARGET_REG_BITS == 64 + }; + MemOp ot = decode->op[2].ot; + int vec_len = vector_len(s, decode); + TCGv t = tcg_temp_new(); + + tcg_gen_gvec_2(offsetof(CPUX86State, xmm_t0) + xmm_offset(ot), decode->op[2].offset, + vec_len, vec_len, &g); + tcg_gen_ld8u_tl(s->T0, tcg_env, offsetof(CPUX86State, xmm_t0.ZMM_B(vec_len - 1))); + while (vec_len > 8) { + vec_len -= 8; + if (TCG_TARGET_HAS_extract2_tl) { + /* + * Load the next byte of the result into the high byte of T. + * TCG does a similar expansion of deposit to shl+extract2; by + * loading the whole word, the shift left is avoided. + */ +#ifdef TARGET_X86_64 + tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, xmm_t0.ZMM_Q((vec_len - 1) / 8))); +#else + tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, xmm_t0.ZMM_L((vec_len - 1) / 4))); +#endif + + tcg_gen_extract2_tl(s->T0, t, s->T0, TARGET_LONG_BITS - 8); + } else { + /* + * The _previous_ value is deposited into bits 8 and higher of t. Because + * those bits are known to be zero after ld8u, this becomes a shift+or + * if deposit is not available. + */ + tcg_gen_ld8u_tl(t, tcg_env, offsetof(CPUX86State, xmm_t0.ZMM_B(vec_len - 1))); + tcg_gen_deposit_tl(s->T0, t, s->T0, 8, TARGET_LONG_BITS - 8); + } + } +} + +static void gen_PSHUFW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + gen_helper_pshufw_mmx(OP_PTR0, OP_PTR1, imm); +} + +static void gen_PSRLW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 16) { + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + } else { + tcg_gen_gvec_shri(MO_16, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); + } +} + +static void gen_PSLLW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 16) { + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + } else { + tcg_gen_gvec_shli(MO_16, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); + } +} + +static void gen_PSRAW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 16) { + decode->immediate = 15; + } + tcg_gen_gvec_sari(MO_16, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); +} + +static void gen_PSRLD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 32) { + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + } else { + tcg_gen_gvec_shri(MO_32, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); + } +} + +static void gen_PSLLD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 32) { + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + } else { + tcg_gen_gvec_shli(MO_32, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); + } +} + +static void gen_PSRAD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 32) { + decode->immediate = 31; + } + tcg_gen_gvec_sari(MO_32, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); +} + +static void gen_PSRLQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 64) { + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + } else { + tcg_gen_gvec_shri(MO_64, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); + } +} + +static void gen_PSLLQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 64) { + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + } else { + tcg_gen_gvec_shli(MO_64, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); + } +} + +static TCGv_ptr make_imm8u_xmm_vec(uint8_t imm, int vec_len) +{ + MemOp ot = vec_len == 16 ? MO_128 : MO_256; + TCGv_i32 imm_v = tcg_constant8u_i32(imm); + TCGv_ptr ptr = tcg_temp_new_ptr(); + + tcg_gen_gvec_dup_imm(MO_64, offsetof(CPUX86State, xmm_t0) + xmm_offset(ot), + vec_len, vec_len, 0); + + tcg_gen_addi_ptr(ptr, tcg_env, offsetof(CPUX86State, xmm_t0)); + tcg_gen_st_i32(imm_v, tcg_env, offsetof(CPUX86State, xmm_t0.ZMM_L(0))); + return ptr; +} + +static void gen_PSRLDQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + TCGv_ptr imm_vec = make_imm8u_xmm_vec(decode->immediate, vec_len); + + if (s->vex_l) { + gen_helper_psrldq_ymm(tcg_env, OP_PTR0, OP_PTR1, imm_vec); + } else { + gen_helper_psrldq_xmm(tcg_env, OP_PTR0, OP_PTR1, imm_vec); + } +} + +static void gen_PSLLDQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + TCGv_ptr imm_vec = make_imm8u_xmm_vec(decode->immediate, vec_len); + + if (s->vex_l) { + gen_helper_pslldq_ymm(tcg_env, OP_PTR0, OP_PTR1, imm_vec); + } else { + gen_helper_pslldq_xmm(tcg_env, OP_PTR0, OP_PTR1, imm_vec); + } +} + +static void gen_RORX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + int mask = ot == MO_64 ? 63 : 31; + int b = decode->immediate & mask; + + switch (ot) { + case MO_32: +#ifdef TARGET_X86_64 + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_rotri_i32(s->tmp2_i32, s->tmp2_i32, b); + tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); + break; + + case MO_64: +#endif + tcg_gen_rotri_tl(s->T0, s->T0, b); + break; + + default: + g_assert_not_reached(); + } +} + +static void gen_SARX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + int mask; + + mask = ot == MO_64 ? 63 : 31; + tcg_gen_andi_tl(s->T1, s->T1, mask); + tcg_gen_sar_tl(s->T0, s->T0, s->T1); +} + +static void gen_SHA1NEXTE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_sha1nexte(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA1MSG1(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_sha1msg1(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA1MSG2(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_sha1msg2(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA1RNDS4(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + switch(decode->immediate & 3) { + case 0: + gen_helper_sha1rnds4_f0(OP_PTR0, OP_PTR0, OP_PTR1); + break; + case 1: + gen_helper_sha1rnds4_f1(OP_PTR0, OP_PTR0, OP_PTR1); + break; + case 2: + gen_helper_sha1rnds4_f2(OP_PTR0, OP_PTR0, OP_PTR1); + break; + case 3: + gen_helper_sha1rnds4_f3(OP_PTR0, OP_PTR0, OP_PTR1); + break; + } +} + +static void gen_SHA256MSG1(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_sha256msg1(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA256MSG2(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_sha256msg2(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA256RNDS2(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 wk0 = tcg_temp_new_i32(); + TCGv_i32 wk1 = tcg_temp_new_i32(); + + tcg_gen_ld_i32(wk0, tcg_env, ZMM_OFFSET(0) + offsetof(ZMMReg, ZMM_L(0))); + tcg_gen_ld_i32(wk1, tcg_env, ZMM_OFFSET(0) + offsetof(ZMMReg, ZMM_L(1))); + + gen_helper_sha256rnds2(OP_PTR0, OP_PTR1, OP_PTR2, wk0, wk1); +} + +static void gen_SHLX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + int mask; + + mask = ot == MO_64 ? 63 : 31; + tcg_gen_andi_tl(s->T1, s->T1, mask); + tcg_gen_shl_tl(s->T0, s->T0, s->T1); +} + +static void gen_SHRX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + int mask; + + mask = ot == MO_64 ? 63 : 31; + tcg_gen_andi_tl(s->T1, s->T1, mask); + tcg_gen_shr_tl(s->T0, s->T0, s->T1); +} + +static void gen_VAESKEYGEN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + assert(!s->vex_l); + gen_helper_aeskeygenassist_xmm(tcg_env, OP_PTR0, OP_PTR1, imm); +} + +static void gen_STMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_update_mxcsr(tcg_env); + tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, mxcsr)); +} + +static void gen_VAESIMC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + assert(!s->vex_l); + gen_helper_aesimc_xmm(tcg_env, OP_PTR0, OP_PTR2); +} + +/* + * 00 = v*ps Vps, Hps, Wpd + * 66 = v*pd Vpd, Hpd, Wps + * f3 = v*ss Vss, Hss, Wps + * f2 = v*sd Vsd, Hsd, Wps + */ +#define SSE_CMP(x) { \ + gen_helper_ ## x ## ps ## _xmm, gen_helper_ ## x ## pd ## _xmm, \ + gen_helper_ ## x ## ss, gen_helper_ ## x ## sd, \ + gen_helper_ ## x ## ps ## _ymm, gen_helper_ ## x ## pd ## _ymm} +static const SSEFunc_0_eppp gen_helper_cmp_funcs[32][6] = { + SSE_CMP(cmpeq), + SSE_CMP(cmplt), + SSE_CMP(cmple), + SSE_CMP(cmpunord), + SSE_CMP(cmpneq), + SSE_CMP(cmpnlt), + SSE_CMP(cmpnle), + SSE_CMP(cmpord), + + SSE_CMP(cmpequ), + SSE_CMP(cmpnge), + SSE_CMP(cmpngt), + SSE_CMP(cmpfalse), + SSE_CMP(cmpnequ), + SSE_CMP(cmpge), + SSE_CMP(cmpgt), + SSE_CMP(cmptrue), + + SSE_CMP(cmpeqs), + SSE_CMP(cmpltq), + SSE_CMP(cmpleq), + SSE_CMP(cmpunords), + SSE_CMP(cmpneqq), + SSE_CMP(cmpnltq), + SSE_CMP(cmpnleq), + SSE_CMP(cmpords), + + SSE_CMP(cmpequs), + SSE_CMP(cmpngeq), + SSE_CMP(cmpngtq), + SSE_CMP(cmpfalses), + SSE_CMP(cmpnequs), + SSE_CMP(cmpgeq), + SSE_CMP(cmpgtq), + SSE_CMP(cmptrues), +}; +#undef SSE_CMP + +static void gen_VCMP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int index = decode->immediate & (s->prefix & PREFIX_VEX ? 31 : 7); + int b = + s->prefix & PREFIX_REPZ ? 2 /* ss */ : + s->prefix & PREFIX_REPNZ ? 3 /* sd */ : + !!(s->prefix & PREFIX_DATA) /* pd */ + (s->vex_l << 2); + + gen_helper_cmp_funcs[index][b](tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_VCOMI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + SSEFunc_0_epp fn; + fn = s->prefix & PREFIX_DATA ? gen_helper_comisd : gen_helper_comiss; + fn(tcg_env, OP_PTR1, OP_PTR2); + set_cc_op(s, CC_OP_EFLAGS); +} + +static void gen_VCVTPD2PS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + if (s->vex_l) { + gen_helper_cvtpd2ps_ymm(tcg_env, OP_PTR0, OP_PTR2); + } else { + gen_helper_cvtpd2ps_xmm(tcg_env, OP_PTR0, OP_PTR2); + } +} + +static void gen_VCVTPS2PD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + if (s->vex_l) { + gen_helper_cvtps2pd_ymm(tcg_env, OP_PTR0, OP_PTR2); + } else { + gen_helper_cvtps2pd_xmm(tcg_env, OP_PTR0, OP_PTR2); + } +} + +static void gen_VCVTPS2PH(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_unary_imm_fp_sse(s, env, decode, + gen_helper_cvtps2ph_xmm, + gen_helper_cvtps2ph_ymm); + /* + * VCVTPS2PH is the only instruction that performs an operation on a + * register source and then *stores* into memory. + */ + if (decode->op[0].has_ea) { + gen_store_sse(s, decode, decode->op[0].offset); + } +} + +static void gen_VCVTSD2SS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_cvtsd2ss(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_VCVTSS2SD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_helper_cvtss2sd(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_VCVTSI2Sx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + TCGv_i32 in; + + tcg_gen_gvec_mov(MO_64, decode->op[0].offset, decode->op[1].offset, vec_len, vec_len); + +#ifdef TARGET_X86_64 + MemOp ot = decode->op[2].ot; + if (ot == MO_64) { + if (s->prefix & PREFIX_REPNZ) { + gen_helper_cvtsq2sd(tcg_env, OP_PTR0, s->T1); + } else { + gen_helper_cvtsq2ss(tcg_env, OP_PTR0, s->T1); + } + return; + } + in = s->tmp2_i32; + tcg_gen_trunc_tl_i32(in, s->T1); +#else + in = s->T1; +#endif + + if (s->prefix & PREFIX_REPNZ) { + gen_helper_cvtsi2sd(tcg_env, OP_PTR0, in); + } else { + gen_helper_cvtsi2ss(tcg_env, OP_PTR0, in); + } +} + +static inline void gen_VCVTtSx2SI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + SSEFunc_i_ep ss2si, SSEFunc_l_ep ss2sq, + SSEFunc_i_ep sd2si, SSEFunc_l_ep sd2sq) +{ + TCGv_i32 out; + +#ifdef TARGET_X86_64 + MemOp ot = decode->op[0].ot; + if (ot == MO_64) { + if (s->prefix & PREFIX_REPNZ) { + sd2sq(s->T0, tcg_env, OP_PTR2); + } else { + ss2sq(s->T0, tcg_env, OP_PTR2); + } + return; + } + + out = s->tmp2_i32; +#else + out = s->T0; +#endif + if (s->prefix & PREFIX_REPNZ) { + sd2si(out, tcg_env, OP_PTR2); + } else { + ss2si(out, tcg_env, OP_PTR2); + } +#ifdef TARGET_X86_64 + tcg_gen_extu_i32_tl(s->T0, out); +#endif +} + +#ifndef TARGET_X86_64 +#define gen_helper_cvtss2sq NULL +#define gen_helper_cvtsd2sq NULL +#define gen_helper_cvttss2sq NULL +#define gen_helper_cvttsd2sq NULL +#endif + +static void gen_VCVTSx2SI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_VCVTtSx2SI(s, env, decode, + gen_helper_cvtss2si, gen_helper_cvtss2sq, + gen_helper_cvtsd2si, gen_helper_cvtsd2sq); +} + +static void gen_VCVTTSx2SI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_VCVTtSx2SI(s, env, decode, + gen_helper_cvttss2si, gen_helper_cvttss2sq, + gen_helper_cvttsd2si, gen_helper_cvttsd2sq); +} + +static void gen_VEXTRACTx128(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int mask = decode->immediate & 1; + int src_ofs = vector_elem_offset(&decode->op[1], MO_128, mask); + if (decode->op[0].has_ea) { + /* VEX-only instruction, no alignment requirements. */ + gen_sto_env_A0(s, src_ofs, false); + } else { + tcg_gen_gvec_mov(MO_64, decode->op[0].offset, src_ofs, 16, 16); + } +} + +static void gen_VEXTRACTPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_pextr(s, env, decode, MO_32); +} + +static void gen_vinsertps(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int val = decode->immediate; + int dest_word = (val >> 4) & 3; + int new_mask = (val & 15) | (1 << dest_word); + int vec_len = 16; + + assert(!s->vex_l); + + if (new_mask == 15) { + /* All zeroes except possibly for the inserted element */ + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + } else if (decode->op[1].offset != decode->op[0].offset) { + gen_store_sse(s, decode, decode->op[1].offset); + } + + if (new_mask != (val & 15)) { + tcg_gen_st_i32(s->tmp2_i32, tcg_env, + vector_elem_offset(&decode->op[0], MO_32, dest_word)); + } + + if (new_mask != 15) { + TCGv_i32 zero = tcg_constant_i32(0); /* float32_zero */ + int i; + for (i = 0; i < 4; i++) { + if ((val >> i) & 1) { + tcg_gen_st_i32(zero, tcg_env, + vector_elem_offset(&decode->op[0], MO_32, i)); + } + } + } +} + +static void gen_VINSERTPS_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int val = decode->immediate; + tcg_gen_ld_i32(s->tmp2_i32, tcg_env, + vector_elem_offset(&decode->op[2], MO_32, (val >> 6) & 3)); + gen_vinsertps(s, env, decode); +} + +static void gen_VINSERTPS_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); + gen_vinsertps(s, env, decode); +} + +static void gen_VINSERTx128(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int mask = decode->immediate & 1; + tcg_gen_gvec_mov(MO_64, + decode->op[0].offset + offsetof(YMMReg, YMM_X(mask)), + decode->op[2].offset + offsetof(YMMReg, YMM_X(0)), 16, 16); + tcg_gen_gvec_mov(MO_64, + decode->op[0].offset + offsetof(YMMReg, YMM_X(!mask)), + decode->op[1].offset + offsetof(YMMReg, YMM_X(!mask)), 16, 16); +} + +static inline void gen_maskmov(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, + SSEFunc_0_eppt xmm, SSEFunc_0_eppt ymm) +{ + if (!s->vex_l) { + xmm(tcg_env, OP_PTR2, OP_PTR1, s->A0); + } else { + ymm(tcg_env, OP_PTR2, OP_PTR1, s->A0); + } +} + +static void gen_VMASKMOVPD_st(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_maskmov(s, env, decode, gen_helper_vpmaskmovq_st_xmm, gen_helper_vpmaskmovq_st_ymm); +} + +static void gen_VMASKMOVPS_st(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_maskmov(s, env, decode, gen_helper_vpmaskmovd_st_xmm, gen_helper_vpmaskmovd_st_ymm); +} + +static void gen_VMOVHPx_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_ldq_env_A0(s, decode->op[0].offset + offsetof(XMMReg, XMM_Q(1))); + if (decode->op[0].offset != decode->op[1].offset) { + tcg_gen_ld_i64(s->tmp1_i64, tcg_env, decode->op[1].offset + offsetof(XMMReg, XMM_Q(0))); + tcg_gen_st_i64(s->tmp1_i64, tcg_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(0))); + } +} + +static void gen_VMOVHPx_st(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + gen_stq_env_A0(s, decode->op[2].offset + offsetof(XMMReg, XMM_Q(1))); +} + +static void gen_VMOVHPx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + if (decode->op[0].offset != decode->op[2].offset) { + tcg_gen_ld_i64(s->tmp1_i64, tcg_env, decode->op[2].offset + offsetof(XMMReg, XMM_Q(1))); + tcg_gen_st_i64(s->tmp1_i64, tcg_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(1))); + } + if (decode->op[0].offset != decode->op[1].offset) { + tcg_gen_ld_i64(s->tmp1_i64, tcg_env, decode->op[1].offset + offsetof(XMMReg, XMM_Q(0))); + tcg_gen_st_i64(s->tmp1_i64, tcg_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(0))); + } +} + +static void gen_VMOVHLPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + tcg_gen_ld_i64(s->tmp1_i64, tcg_env, decode->op[2].offset + offsetof(XMMReg, XMM_Q(1))); + tcg_gen_st_i64(s->tmp1_i64, tcg_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(0))); + if (decode->op[0].offset != decode->op[1].offset) { + tcg_gen_ld_i64(s->tmp1_i64, tcg_env, decode->op[1].offset + offsetof(XMMReg, XMM_Q(1))); + tcg_gen_st_i64(s->tmp1_i64, tcg_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(1))); + } +} + +static void gen_VMOVLHPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + tcg_gen_ld_i64(s->tmp1_i64, tcg_env, decode->op[2].offset); + tcg_gen_st_i64(s->tmp1_i64, tcg_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(1))); + if (decode->op[0].offset != decode->op[1].offset) { + tcg_gen_ld_i64(s->tmp1_i64, tcg_env, decode->op[1].offset + offsetof(XMMReg, XMM_Q(0))); + tcg_gen_st_i64(s->tmp1_i64, tcg_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(0))); + } +} + +/* + * Note that MOVLPx supports 256-bit operation unlike MOVHLPx, MOVLHPx, MOXHPx. + * Use a gvec move to move everything above the bottom 64 bits. + */ + +static void gen_VMOVLPx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + tcg_gen_ld_i64(s->tmp1_i64, tcg_env, decode->op[2].offset + offsetof(XMMReg, XMM_Q(0))); + tcg_gen_gvec_mov(MO_64, decode->op[0].offset, decode->op[1].offset, vec_len, vec_len); + tcg_gen_st_i64(s->tmp1_i64, tcg_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(0))); +} + +static void gen_VMOVLPx_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); + tcg_gen_gvec_mov(MO_64, decode->op[0].offset, decode->op[1].offset, vec_len, vec_len); + tcg_gen_st_i64(s->tmp1_i64, OP_PTR0, offsetof(ZMMReg, ZMM_Q(0))); +} + +static void gen_VMOVLPx_st(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + tcg_gen_ld_i64(s->tmp1_i64, OP_PTR2, offsetof(ZMMReg, ZMM_Q(0))); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); +} + +static void gen_VMOVSD_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i64 zero = tcg_constant_i64(0); + + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); + tcg_gen_st_i64(zero, OP_PTR0, offsetof(ZMMReg, ZMM_Q(1))); + tcg_gen_st_i64(s->tmp1_i64, OP_PTR0, offsetof(ZMMReg, ZMM_Q(0))); +} + +static void gen_VMOVSS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + tcg_gen_ld_i32(s->tmp2_i32, OP_PTR2, offsetof(ZMMReg, ZMM_L(0))); + tcg_gen_gvec_mov(MO_64, decode->op[0].offset, decode->op[1].offset, vec_len, vec_len); + tcg_gen_st_i32(s->tmp2_i32, OP_PTR0, offsetof(ZMMReg, ZMM_L(0))); +} + +static void gen_VMOVSS_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + tcg_gen_st_i32(s->tmp2_i32, OP_PTR0, offsetof(ZMMReg, ZMM_L(0))); +} + +static void gen_VMOVSS_st(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + tcg_gen_ld_i32(s->tmp2_i32, OP_PTR2, offsetof(ZMMReg, ZMM_L(0))); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); +} + +static void gen_VPMASKMOV_st(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + if (s->vex_w) { + gen_VMASKMOVPD_st(s, env, decode); + } else { + gen_VMASKMOVPS_st(s, env, decode); + } +} + +static void gen_VPERMD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + assert(s->vex_l); + gen_helper_vpermd_ymm(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_VPERM2x128(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + assert(s->vex_l); + gen_helper_vpermdq_ymm(OP_PTR0, OP_PTR1, OP_PTR2, imm); +} + +static void gen_VPHMINPOSUW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + assert(!s->vex_l); + gen_helper_phminposuw_xmm(tcg_env, OP_PTR0, OP_PTR2); +} + +static void gen_VROUNDSD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + assert(!s->vex_l); + gen_helper_roundsd_xmm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); +} + +static void gen_VROUNDSS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + assert(!s->vex_l); + gen_helper_roundss_xmm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); +} + +static void gen_VSHUF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant_i32(decode->immediate); + SSEFunc_0_pppi ps, pd, fn; + ps = s->vex_l ? gen_helper_shufps_ymm : gen_helper_shufps_xmm; + pd = s->vex_l ? gen_helper_shufpd_ymm : gen_helper_shufpd_xmm; + fn = s->prefix & PREFIX_DATA ? pd : ps; + fn(OP_PTR0, OP_PTR1, OP_PTR2, imm); +} + +static void gen_VUCOMI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + SSEFunc_0_epp fn; + fn = s->prefix & PREFIX_DATA ? gen_helper_ucomisd : gen_helper_ucomiss; + fn(tcg_env, OP_PTR1, OP_PTR2); + set_cc_op(s, CC_OP_EFLAGS); +} + +static void gen_VZEROALL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + TCGv_ptr ptr = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(ptr, tcg_env, offsetof(CPUX86State, xmm_regs)); + gen_helper_memset(ptr, ptr, tcg_constant_i32(0), + tcg_constant_ptr(CPU_NB_REGS * sizeof(ZMMReg))); +} + +static void gen_VZEROUPPER(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +{ + int i; + + for (i = 0; i < CPU_NB_REGS; i++) { + int offset = offsetof(CPUX86State, xmm_regs[i].ZMM_X(1)); + tcg_gen_gvec_dup_imm(MO_64, offset, 16, 16, 0); + } +} diff --git a/target/i386/tcg/excp_helper.c b/target/i386/tcg/excp_helper.c index c1ffa1c0ef..65e37ae2a0 100644 --- a/target/i386/tcg/excp_helper.c +++ b/target/i386/tcg/excp_helper.c @@ -28,7 +28,7 @@ G_NORETURN void helper_raise_interrupt(CPUX86State *env, int intno, int next_eip_addend) { - raise_interrupt(env, intno, 1, 0, next_eip_addend); + raise_interrupt(env, intno, next_eip_addend); } G_NORETURN void helper_raise_exception(CPUX86State *env, int exception_index) @@ -112,10 +112,9 @@ void raise_interrupt2(CPUX86State *env, int intno, /* shortcuts to generate exceptions */ -G_NORETURN void raise_interrupt(CPUX86State *env, int intno, int is_int, - int error_code, int next_eip_addend) +G_NORETURN void raise_interrupt(CPUX86State *env, int intno, int next_eip_addend) { - raise_interrupt2(env, intno, is_int, error_code, next_eip_addend, 0); + raise_interrupt2(env, intno, 1, 0, next_eip_addend, 0); } G_NORETURN void raise_exception_err(CPUX86State *env, int exception_index, @@ -140,3 +139,16 @@ G_NORETURN void raise_exception_ra(CPUX86State *env, int exception_index, { raise_interrupt2(env, exception_index, 0, 0, 0, retaddr); } + +G_NORETURN void handle_unaligned_access(CPUX86State *env, vaddr vaddr, + MMUAccessType access_type, + uintptr_t retaddr) +{ + /* + * Unaligned accesses are currently only triggered by SSE/AVX + * instructions that impose alignment requirements on memory + * operands. These instructions raise #GP(0) upon accessing an + * unaligned address. + */ + raise_exception_ra(env, EXCP0D_GPF, retaddr); +} diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 30bc44fcf8..4b965a5d6c 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -21,6 +21,7 @@ #include #include "cpu.h" #include "tcg-cpu.h" +#include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" #include "fpu/softfloat-macros.h" @@ -32,7 +33,8 @@ #define ST(n) (env->fpregs[(env->fpstt + (n)) & 7].d) #define ST1 ST(1) -#define FPU_RC_MASK 0xc00 +#define FPU_RC_SHIFT 10 +#define FPU_RC_MASK (3 << FPU_RC_SHIFT) #define FPU_RC_NEAR 0x000 #define FPU_RC_DOWN 0x400 #define FPU_RC_UP 0x800 @@ -482,9 +484,8 @@ void helper_fcomi_ST0_FT0(CPUX86State *env) FloatRelation ret; ret = floatx80_compare(ST0, FT0, &env->fp_status); - eflags = cpu_cc_compute_all(env, CC_OP); - eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1]; - CC_SRC = eflags; + eflags = cpu_cc_compute_all(env) & ~(CC_Z | CC_P | CC_C); + CC_SRC = eflags | fcomi_ccval[ret + 1]; merge_exception_flags(env, old_flags); } @@ -495,9 +496,8 @@ void helper_fucomi_ST0_FT0(CPUX86State *env) FloatRelation ret; ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); - eflags = cpu_cc_compute_all(env, CC_OP); - eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1]; - CC_SRC = eflags; + eflags = cpu_cc_compute_all(env) & ~(CC_Z | CC_P | CC_C); + CC_SRC = eflags | fcomi_ccval[ret + 1]; merge_exception_flags(env, old_flags); } @@ -685,28 +685,26 @@ uint32_t helper_fnstcw(CPUX86State *env) return env->fpuc; } +static void set_x86_rounding_mode(unsigned mode, float_status *status) +{ + static FloatRoundMode x86_round_mode[4] = { + float_round_nearest_even, + float_round_down, + float_round_up, + float_round_to_zero + }; + assert(mode < ARRAY_SIZE(x86_round_mode)); + set_float_rounding_mode(x86_round_mode[mode], status); +} + void update_fp_status(CPUX86State *env) { - FloatRoundMode rnd_mode; + int rnd_mode; FloatX80RoundPrec rnd_prec; /* set rounding mode */ - switch (env->fpuc & FPU_RC_MASK) { - default: - case FPU_RC_NEAR: - rnd_mode = float_round_nearest_even; - break; - case FPU_RC_DOWN: - rnd_mode = float_round_down; - break; - case FPU_RC_UP: - rnd_mode = float_round_up; - break; - case FPU_RC_CHOP: - rnd_mode = float_round_to_zero; - break; - } - set_float_rounding_mode(rnd_mode, &env->fp_status); + rnd_mode = (env->fpuc & FPU_RC_MASK) >> FPU_RC_SHIFT; + set_x86_rounding_mode(rnd_mode, &env->fp_status); switch ((env->fpuc >> 8) & 3) { case 0: @@ -2502,18 +2500,6 @@ void helper_frstor(CPUX86State *env, target_ulong ptr, int data32) do_frstor(env, ptr, data32, GETPC()); } -#if defined(CONFIG_USER_ONLY) -void cpu_x86_fsave(CPUX86State *env, target_ulong ptr, int data32) -{ - do_fsave(env, ptr, data32, 0); -} - -void cpu_x86_frstor(CPUX86State *env, target_ulong ptr, int data32) -{ - do_frstor(env, ptr, data32, 0); -} -#endif - #define XO(X) offsetof(X86XSaveArea, X) static void do_xsave_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) @@ -2571,6 +2557,22 @@ static void do_xsave_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) } } +static void do_xsave_ymmh(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + int i, nb_xmm_regs; + + if (env->hflags & HF_CS64_MASK) { + nb_xmm_regs = 16; + } else { + nb_xmm_regs = 8; + } + + for (i = 0; i < nb_xmm_regs; i++, ptr += 16) { + cpu_stq_data_ra(env, ptr, env->xmm_regs[i].ZMM_Q(2), ra); + cpu_stq_data_ra(env, ptr + 8, env->xmm_regs[i].ZMM_Q(3), ra); + } +} + static void do_xsave_bndregs(CPUX86State *env, target_ulong ptr, uintptr_t ra) { target_ulong addr = ptr + offsetof(XSaveBNDREG, bnd_regs); @@ -2663,6 +2665,9 @@ static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, if (opt & XSTATE_SSE_MASK) { do_xsave_sse(env, ptr, ra); } + if (opt & XSTATE_YMM_MASK) { + do_xsave_ymmh(env, ptr + XO(avx_state), ra); + } if (opt & XSTATE_BNDREGS_MASK) { do_xsave_bndregs(env, ptr + XO(bndreg_state), ra); } @@ -2737,6 +2742,54 @@ static void do_xrstor_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) } } +static void do_clear_sse(CPUX86State *env) +{ + int i, nb_xmm_regs; + + if (env->hflags & HF_CS64_MASK) { + nb_xmm_regs = 16; + } else { + nb_xmm_regs = 8; + } + + for (i = 0; i < nb_xmm_regs; i++) { + env->xmm_regs[i].ZMM_Q(0) = 0; + env->xmm_regs[i].ZMM_Q(1) = 0; + } +} + +static void do_xrstor_ymmh(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + int i, nb_xmm_regs; + + if (env->hflags & HF_CS64_MASK) { + nb_xmm_regs = 16; + } else { + nb_xmm_regs = 8; + } + + for (i = 0; i < nb_xmm_regs; i++, ptr += 16) { + env->xmm_regs[i].ZMM_Q(2) = cpu_ldq_data_ra(env, ptr, ra); + env->xmm_regs[i].ZMM_Q(3) = cpu_ldq_data_ra(env, ptr + 8, ra); + } +} + +static void do_clear_ymmh(CPUX86State *env) +{ + int i, nb_xmm_regs; + + if (env->hflags & HF_CS64_MASK) { + nb_xmm_regs = 16; + } else { + nb_xmm_regs = 8; + } + + for (i = 0; i < nb_xmm_regs; i++) { + env->xmm_regs[i].ZMM_Q(2) = 0; + env->xmm_regs[i].ZMM_Q(3) = 0; + } +} + static void do_xrstor_bndregs(CPUX86State *env, target_ulong ptr, uintptr_t ra) { target_ulong addr = ptr + offsetof(XSaveBNDREG, bnd_regs); @@ -2787,21 +2840,8 @@ void helper_fxrstor(CPUX86State *env, target_ulong ptr) do_fxrstor(env, ptr, GETPC()); } -#if defined(CONFIG_USER_ONLY) -void cpu_x86_fxsave(CPUX86State *env, target_ulong ptr) +static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr_t ra) { - do_fxsave(env, ptr, 0); -} - -void cpu_x86_fxrstor(CPUX86State *env, target_ulong ptr) -{ - do_fxrstor(env, ptr, 0); -} -#endif - -void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) -{ - uintptr_t ra = GETPC(); uint64_t xstate_bv, xcomp_bv, reserve0; rfbm &= env->xcr0; @@ -2856,9 +2896,14 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) if (xstate_bv & XSTATE_SSE_MASK) { do_xrstor_sse(env, ptr, ra); } else { - /* ??? When AVX is implemented, we may have to be more - selective in the clearing. */ - memset(env->xmm_regs, 0, sizeof(env->xmm_regs)); + do_clear_sse(env); + } + } + if (rfbm & XSTATE_YMM_MASK) { + if (xstate_bv & XSTATE_YMM_MASK) { + do_xrstor_ymmh(env, ptr + XO(avx_state), ra); + } else { + do_clear_ymmh(env); } } if (rfbm & XSTATE_BNDREGS_MASK) { @@ -2894,6 +2939,43 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) #undef XO +void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +{ + do_xrstor(env, ptr, rfbm, GETPC()); +} + +#if defined(CONFIG_USER_ONLY) +void cpu_x86_fsave(CPUX86State *env, target_ulong ptr, int data32) +{ + do_fsave(env, ptr, data32, 0); +} + +void cpu_x86_frstor(CPUX86State *env, target_ulong ptr, int data32) +{ + do_frstor(env, ptr, data32, 0); +} + +void cpu_x86_fxsave(CPUX86State *env, target_ulong ptr) +{ + do_fxsave(env, ptr, 0); +} + +void cpu_x86_fxrstor(CPUX86State *env, target_ulong ptr) +{ + do_fxrstor(env, ptr, 0); +} + +void cpu_x86_xsave(CPUX86State *env, target_ulong ptr) +{ + do_xsave(env, ptr, -1, get_xinuse(env), -1, 0); +} + +void cpu_x86_xrstor(CPUX86State *env, target_ulong ptr) +{ + do_xrstor(env, ptr, -1, 0); +} +#endif + uint64_t helper_xgetbv(CPUX86State *env, uint32_t ecx) { /* The OS must have enabled XSAVE. */ @@ -2943,6 +3025,7 @@ void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask) env->xcr0 = mask; cpu_sync_bndcs_hflags(env); + cpu_sync_avx_hflag(env); return; do_gpf: @@ -2953,11 +3036,8 @@ void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask) /* XXX: optimize by storing fptt and fptags in the static cpu state */ #define SSE_DAZ 0x0040 -#define SSE_RC_MASK 0x6000 -#define SSE_RC_NEAR 0x0000 -#define SSE_RC_DOWN 0x2000 -#define SSE_RC_UP 0x4000 -#define SSE_RC_CHOP 0x6000 +#define SSE_RC_SHIFT 13 +#define SSE_RC_MASK (3 << SSE_RC_SHIFT) #define SSE_FZ 0x8000 void update_mxcsr_status(CPUX86State *env) @@ -2966,22 +3046,8 @@ void update_mxcsr_status(CPUX86State *env) int rnd_type; /* set rounding mode */ - switch (mxcsr & SSE_RC_MASK) { - default: - case SSE_RC_NEAR: - rnd_type = float_round_nearest_even; - break; - case SSE_RC_DOWN: - rnd_type = float_round_down; - break; - case SSE_RC_UP: - rnd_type = float_round_up; - break; - case SSE_RC_CHOP: - rnd_type = float_round_to_zero; - break; - } - set_float_rounding_mode(rnd_type, &env->sse_status); + rnd_type = (mxcsr & SSE_RC_MASK) >> SSE_RC_SHIFT; + set_x86_rounding_mode(rnd_type, &env->sse_status); /* Set exception flags. */ set_float_exception_flags((mxcsr & FPUS_IE ? float_flag_invalid : 0) | @@ -3041,14 +3107,11 @@ void helper_emms(CPUX86State *env) *(uint32_t *)(env->fptags + 4) = 0x01010101; } -/* XXX: suppress */ -void helper_movq(CPUX86State *env, void *d, void *s) -{ - *(uint64_t *)d = *(uint64_t *)s; -} - #define SHIFT 0 #include "ops_sse.h" #define SHIFT 1 #include "ops_sse.h" + +#define SHIFT 2 +#include "ops_sse.h" diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h index 34167e2e29..effc2c1c98 100644 --- a/target/i386/tcg/helper-tcg.h +++ b/target/i386/tcg/helper-tcg.h @@ -39,20 +39,11 @@ QEMU_BUILD_BUG_ON(TCG_PHYS_ADDR_BITS > TARGET_PHYS_ADDR_SPACE_BITS); */ void x86_cpu_do_interrupt(CPUState *cpu); #ifndef CONFIG_USER_ONLY +void x86_cpu_exec_halt(CPUState *cpu); +bool x86_need_replay_interrupt(int interrupt_request); bool x86_cpu_exec_interrupt(CPUState *cpu, int int_req); #endif -/* helper.c */ -#ifdef CONFIG_USER_ONLY -void x86_cpu_record_sigsegv(CPUState *cs, vaddr addr, - MMUAccessType access_type, - bool maperr, uintptr_t ra); -#else -bool x86_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); -#endif - void breakpoint_handler(CPUState *cs); /* n must be a constant to be efficient */ @@ -76,8 +67,24 @@ G_NORETURN void raise_exception_err(CPUX86State *env, int exception_index, int error_code); G_NORETURN void raise_exception_err_ra(CPUX86State *env, int exception_index, int error_code, uintptr_t retaddr); -G_NORETURN void raise_interrupt(CPUX86State *nenv, int intno, int is_int, - int error_code, int next_eip_addend); +G_NORETURN void raise_interrupt(CPUX86State *nenv, int intno, int next_eip_addend); +G_NORETURN void handle_unaligned_access(CPUX86State *env, vaddr vaddr, + MMUAccessType access_type, + uintptr_t retaddr); +#ifdef CONFIG_USER_ONLY +void x86_cpu_record_sigsegv(CPUState *cs, vaddr addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra); +void x86_cpu_record_sigbus(CPUState *cs, vaddr addr, + MMUAccessType access_type, uintptr_t ra); +#else +bool x86_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); +G_NORETURN void x86_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr); +#endif /* cc_helper.c */ extern const uint8_t parity_table[256]; diff --git a/target/i386/tcg/int_helper.c b/target/i386/tcg/int_helper.c index 599ac968b0..ab85dc5540 100644 --- a/target/i386/tcg/int_helper.c +++ b/target/i386/tcg/int_helper.c @@ -190,7 +190,7 @@ void helper_aaa(CPUX86State *env) int al, ah, af; int eflags; - eflags = cpu_cc_compute_all(env, CC_OP); + eflags = cpu_cc_compute_all(env); af = eflags & CC_A; al = env->regs[R_EAX] & 0xff; ah = (env->regs[R_EAX] >> 8) & 0xff; @@ -214,7 +214,7 @@ void helper_aas(CPUX86State *env) int al, ah, af; int eflags; - eflags = cpu_cc_compute_all(env, CC_OP); + eflags = cpu_cc_compute_all(env); af = eflags & CC_A; al = env->regs[R_EAX] & 0xff; ah = (env->regs[R_EAX] >> 8) & 0xff; @@ -237,7 +237,7 @@ void helper_daa(CPUX86State *env) int old_al, al, af, cf; int eflags; - eflags = cpu_cc_compute_all(env, CC_OP); + eflags = cpu_cc_compute_all(env); cf = eflags & CC_C; af = eflags & CC_A; old_al = al = env->regs[R_EAX] & 0xff; @@ -264,7 +264,7 @@ void helper_das(CPUX86State *env) int al, al1, af, cf; int eflags; - eflags = cpu_cc_compute_all(env, CC_OP); + eflags = cpu_cc_compute_all(env); cf = eflags & CC_C; af = eflags & CC_A; al = env->regs[R_EAX] & 0xff; @@ -448,20 +448,20 @@ target_ulong helper_pext(target_ulong src, target_ulong mask) } #define SHIFT 0 -#include "shift_helper_template.h" +#include "shift_helper_template.h.inc" #undef SHIFT #define SHIFT 1 -#include "shift_helper_template.h" +#include "shift_helper_template.h.inc" #undef SHIFT #define SHIFT 2 -#include "shift_helper_template.h" +#include "shift_helper_template.h.inc" #undef SHIFT #ifdef TARGET_X86_64 #define SHIFT 3 -#include "shift_helper_template.h" +#include "shift_helper_template.h.inc" #undef SHIFT #endif diff --git a/target/i386/tcg/mem_helper.c b/target/i386/tcg/mem_helper.c index e3cdafd2d4..3ef84e90d9 100644 --- a/target/i386/tcg/mem_helper.c +++ b/target/i386/tcg/mem_helper.c @@ -27,132 +27,6 @@ #include "tcg/tcg.h" #include "helper-tcg.h" -void helper_cmpxchg8b_unlocked(CPUX86State *env, target_ulong a0) -{ - uintptr_t ra = GETPC(); - uint64_t oldv, cmpv, newv; - int eflags; - - eflags = cpu_cc_compute_all(env, CC_OP); - - cmpv = deposit64(env->regs[R_EAX], 32, 32, env->regs[R_EDX]); - newv = deposit64(env->regs[R_EBX], 32, 32, env->regs[R_ECX]); - - oldv = cpu_ldq_data_ra(env, a0, ra); - newv = (cmpv == oldv ? newv : oldv); - /* always do the store */ - cpu_stq_data_ra(env, a0, newv, ra); - - if (oldv == cmpv) { - eflags |= CC_Z; - } else { - env->regs[R_EAX] = (uint32_t)oldv; - env->regs[R_EDX] = (uint32_t)(oldv >> 32); - eflags &= ~CC_Z; - } - CC_SRC = eflags; -} - -void helper_cmpxchg8b(CPUX86State *env, target_ulong a0) -{ -#ifdef CONFIG_ATOMIC64 - uint64_t oldv, cmpv, newv; - int eflags; - - eflags = cpu_cc_compute_all(env, CC_OP); - - cmpv = deposit64(env->regs[R_EAX], 32, 32, env->regs[R_EDX]); - newv = deposit64(env->regs[R_EBX], 32, 32, env->regs[R_ECX]); - - { - uintptr_t ra = GETPC(); - int mem_idx = cpu_mmu_index(env, false); - MemOpIdx oi = make_memop_idx(MO_TEUQ, mem_idx); - oldv = cpu_atomic_cmpxchgq_le_mmu(env, a0, cmpv, newv, oi, ra); - } - - if (oldv == cmpv) { - eflags |= CC_Z; - } else { - env->regs[R_EAX] = (uint32_t)oldv; - env->regs[R_EDX] = (uint32_t)(oldv >> 32); - eflags &= ~CC_Z; - } - CC_SRC = eflags; -#else - cpu_loop_exit_atomic(env_cpu(env), GETPC()); -#endif /* CONFIG_ATOMIC64 */ -} - -#ifdef TARGET_X86_64 -void helper_cmpxchg16b_unlocked(CPUX86State *env, target_ulong a0) -{ - uintptr_t ra = GETPC(); - Int128 oldv, cmpv, newv; - uint64_t o0, o1; - int eflags; - bool success; - - if ((a0 & 0xf) != 0) { - raise_exception_ra(env, EXCP0D_GPF, GETPC()); - } - eflags = cpu_cc_compute_all(env, CC_OP); - - cmpv = int128_make128(env->regs[R_EAX], env->regs[R_EDX]); - newv = int128_make128(env->regs[R_EBX], env->regs[R_ECX]); - - o0 = cpu_ldq_data_ra(env, a0 + 0, ra); - o1 = cpu_ldq_data_ra(env, a0 + 8, ra); - - oldv = int128_make128(o0, o1); - success = int128_eq(oldv, cmpv); - if (!success) { - newv = oldv; - } - - cpu_stq_data_ra(env, a0 + 0, int128_getlo(newv), ra); - cpu_stq_data_ra(env, a0 + 8, int128_gethi(newv), ra); - - if (success) { - eflags |= CC_Z; - } else { - env->regs[R_EAX] = int128_getlo(oldv); - env->regs[R_EDX] = int128_gethi(oldv); - eflags &= ~CC_Z; - } - CC_SRC = eflags; -} - -void helper_cmpxchg16b(CPUX86State *env, target_ulong a0) -{ - uintptr_t ra = GETPC(); - - if ((a0 & 0xf) != 0) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } else if (HAVE_CMPXCHG128) { - int eflags = cpu_cc_compute_all(env, CC_OP); - - Int128 cmpv = int128_make128(env->regs[R_EAX], env->regs[R_EDX]); - Int128 newv = int128_make128(env->regs[R_EBX], env->regs[R_ECX]); - - int mem_idx = cpu_mmu_index(env, false); - MemOpIdx oi = make_memop_idx(MO_TE | MO_128 | MO_ALIGN, mem_idx); - Int128 oldv = cpu_atomic_cmpxchgo_le_mmu(env, a0, cmpv, newv, oi, ra); - - if (int128_eq(oldv, cmpv)) { - eflags |= CC_Z; - } else { - env->regs[R_EAX] = int128_getlo(oldv); - env->regs[R_EDX] = int128_gethi(oldv); - eflags &= ~CC_Z; - } - CC_SRC = eflags; - } else { - cpu_loop_exit_atomic(env_cpu(env), ra); - } -} -#endif - void helper_boundw(CPUX86State *env, target_ulong a0, int v) { int low, high; diff --git a/target/i386/tcg/misc_helper.c b/target/i386/tcg/misc_helper.c index 5f7a3061ca..b0f0f7b893 100644 --- a/target/i386/tcg/misc_helper.c +++ b/target/i386/tcg/misc_helper.c @@ -41,9 +41,9 @@ void helper_into(CPUX86State *env, int next_eip_addend) { int eflags; - eflags = cpu_cc_compute_all(env, CC_OP); + eflags = cpu_cc_compute_all(env); if (eflags & CC_O) { - raise_interrupt(env, EXCP04_INTO, 1, 0, next_eip_addend); + raise_interrupt(env, EXCP04_INTO, next_eip_addend); } } @@ -75,12 +75,6 @@ void helper_rdtsc(CPUX86State *env) env->regs[R_EDX] = (uint32_t)(val >> 32); } -void helper_rdtscp(CPUX86State *env) -{ - helper_rdtsc(env); - env->regs[R_ECX] = (uint32_t)(env->tsc_aux); -} - G_NORETURN void helper_rdpmc(CPUX86State *env) { if (((env->cr[4] & CR4_PCE_MASK) == 0 ) && @@ -137,3 +131,18 @@ void helper_wrpkru(CPUX86State *env, uint32_t ecx, uint64_t val) env->pkru = val; tlb_flush(cs); } + +target_ulong HELPER(rdpid)(CPUX86State *env) +{ +#if !defined CONFIG_USER_ONLY + return env->tsc_aux; +#elif defined CONFIG_LINUX && defined CONFIG_GETCPU + unsigned cpu, node; + getcpu(&cpu, &node); + return (node << 12) | (cpu & 0xfff); +#elif defined CONFIG_SCHED_GETCPU + return sched_getcpu(); +#else + return 0; +#endif +} diff --git a/target/i386/tcg/ops_sse_header.h.inc b/target/i386/tcg/ops_sse_header.h.inc new file mode 100644 index 0000000000..d92c6faf6d --- /dev/null +++ b/target/i386/tcg/ops_sse_header.h.inc @@ -0,0 +1,429 @@ +/* + * MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI support + * + * Copyright (c) 2005 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.1 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 SHIFT == 0 +#define Reg MMXReg +#define SUFFIX _mmx +#else +#define Reg ZMMReg +#if SHIFT == 1 +#define SUFFIX _xmm +#else +#define SUFFIX _ymm +#endif +#endif + +#define dh_alias_Reg ptr +#define dh_alias_ZMMReg ptr +#define dh_alias_MMXReg ptr +#define dh_ctype_Reg Reg * +#define dh_ctype_ZMMReg ZMMReg * +#define dh_ctype_MMXReg MMXReg * +#define dh_typecode_Reg dh_typecode_ptr +#define dh_typecode_ZMMReg dh_typecode_ptr +#define dh_typecode_MMXReg dh_typecode_ptr + +DEF_HELPER_4(glue(psrlw, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(psraw, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(psllw, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(psrld, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(psrad, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(pslld, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(psrlq, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(psllq, SUFFIX), void, env, Reg, Reg, Reg) + +#if SHIFT >= 1 +DEF_HELPER_4(glue(psrldq, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(pslldq, SUFFIX), void, env, Reg, Reg, Reg) +#endif + +#define SSE_HELPER_B(name, F)\ + DEF_HELPER_4(glue(name, SUFFIX), void, env, Reg, Reg, Reg) + +#define SSE_HELPER_W(name, F)\ + DEF_HELPER_4(glue(name, SUFFIX), void, env, Reg, Reg, Reg) + +#define SSE_HELPER_L(name, F)\ + DEF_HELPER_4(glue(name, SUFFIX), void, env, Reg, Reg, Reg) + +#define SSE_HELPER_Q(name, F)\ + DEF_HELPER_4(glue(name, SUFFIX), void, env, Reg, Reg, Reg) + +#if SHIFT == 0 +DEF_HELPER_3(glue(pmulhrw, SUFFIX), void, env, Reg, Reg) +#endif +SSE_HELPER_W(pmulhuw, FMULHUW) +SSE_HELPER_W(pmulhw, FMULHW) + +SSE_HELPER_B(pavgb, FAVG) +SSE_HELPER_W(pavgw, FAVG) + +DEF_HELPER_4(glue(pmuludq, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(pmaddwd, SUFFIX), void, env, Reg, Reg, Reg) + +DEF_HELPER_4(glue(psadbw, SUFFIX), void, env, Reg, Reg, Reg) +#if SHIFT < 2 +DEF_HELPER_4(glue(maskmov, SUFFIX), void, env, Reg, Reg, tl) +#endif + +#if SHIFT == 0 +DEF_HELPER_3(glue(pshufw, SUFFIX), void, Reg, Reg, int) +#else +DEF_HELPER_3(glue(pshufd, SUFFIX), void, Reg, Reg, int) +DEF_HELPER_3(glue(pshuflw, SUFFIX), void, Reg, Reg, int) +DEF_HELPER_3(glue(pshufhw, SUFFIX), void, Reg, Reg, int) +#endif + +#if SHIFT >= 1 +/* FPU ops */ +/* XXX: not accurate */ + +#define SSE_HELPER_P4(name) \ + DEF_HELPER_4(glue(name ## ps, SUFFIX), void, env, Reg, Reg, Reg) \ + DEF_HELPER_4(glue(name ## pd, SUFFIX), void, env, Reg, Reg, Reg) + +#define SSE_HELPER_P3(name, ...) \ + DEF_HELPER_3(glue(name ## ps, SUFFIX), void, env, Reg, Reg) \ + DEF_HELPER_3(glue(name ## pd, SUFFIX), void, env, Reg, Reg) + +#if SHIFT == 1 +#define SSE_HELPER_S4(name) \ + SSE_HELPER_P4(name) \ + DEF_HELPER_4(name ## ss, void, env, Reg, Reg, Reg) \ + DEF_HELPER_4(name ## sd, void, env, Reg, Reg, Reg) +#define SSE_HELPER_S3(name) \ + SSE_HELPER_P3(name) \ + DEF_HELPER_4(name ## ss, void, env, Reg, Reg, Reg) \ + DEF_HELPER_4(name ## sd, void, env, Reg, Reg, Reg) +#else +#define SSE_HELPER_S4(name, ...) SSE_HELPER_P4(name) +#define SSE_HELPER_S3(name, ...) SSE_HELPER_P3(name) +#endif + +DEF_HELPER_4(glue(shufps, SUFFIX), void, Reg, Reg, Reg, int) +DEF_HELPER_4(glue(shufpd, SUFFIX), void, Reg, Reg, Reg, int) + +SSE_HELPER_S4(add) +SSE_HELPER_S4(sub) +SSE_HELPER_S4(mul) +SSE_HELPER_S4(div) +SSE_HELPER_S4(min) +SSE_HELPER_S4(max) + +SSE_HELPER_S3(sqrt) + +DEF_HELPER_3(glue(cvtps2pd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(cvtpd2ps, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(cvtdq2ps, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(cvtdq2pd, SUFFIX), void, env, Reg, Reg) + +DEF_HELPER_3(glue(cvtps2dq, SUFFIX), void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(glue(cvtpd2dq, SUFFIX), void, env, ZMMReg, ZMMReg) + +DEF_HELPER_3(glue(cvttps2dq, SUFFIX), void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(glue(cvttpd2dq, SUFFIX), void, env, ZMMReg, ZMMReg) + +#if SHIFT == 1 +DEF_HELPER_4(cvtss2sd, void, env, Reg, Reg, Reg) +DEF_HELPER_4(cvtsd2ss, void, env, Reg, Reg, Reg) +DEF_HELPER_3(cvtpi2ps, void, env, ZMMReg, MMXReg) +DEF_HELPER_3(cvtpi2pd, void, env, ZMMReg, MMXReg) +DEF_HELPER_3(cvtsi2ss, void, env, ZMMReg, i32) +DEF_HELPER_3(cvtsi2sd, void, env, ZMMReg, i32) + +#ifdef TARGET_X86_64 +DEF_HELPER_3(cvtsq2ss, void, env, ZMMReg, i64) +DEF_HELPER_3(cvtsq2sd, void, env, ZMMReg, i64) +#endif + +DEF_HELPER_3(cvtps2pi, void, env, MMXReg, ZMMReg) +DEF_HELPER_3(cvtpd2pi, void, env, MMXReg, ZMMReg) +DEF_HELPER_2(cvtss2si, s32, env, ZMMReg) +DEF_HELPER_2(cvtsd2si, s32, env, ZMMReg) +#ifdef TARGET_X86_64 +DEF_HELPER_2(cvtss2sq, s64, env, ZMMReg) +DEF_HELPER_2(cvtsd2sq, s64, env, ZMMReg) +#endif + +DEF_HELPER_3(cvttps2pi, void, env, MMXReg, ZMMReg) +DEF_HELPER_3(cvttpd2pi, void, env, MMXReg, ZMMReg) +DEF_HELPER_2(cvttss2si, s32, env, ZMMReg) +DEF_HELPER_2(cvttsd2si, s32, env, ZMMReg) +#ifdef TARGET_X86_64 +DEF_HELPER_2(cvttss2sq, s64, env, ZMMReg) +DEF_HELPER_2(cvttsd2sq, s64, env, ZMMReg) +#endif +#endif + +DEF_HELPER_3(glue(rsqrtps, SUFFIX), void, env, ZMMReg, ZMMReg) +DEF_HELPER_3(glue(rcpps, SUFFIX), void, env, ZMMReg, ZMMReg) + +#if SHIFT == 1 +DEF_HELPER_4(rsqrtss, void, env, ZMMReg, ZMMReg, ZMMReg) +DEF_HELPER_4(rcpss, void, env, ZMMReg, ZMMReg, ZMMReg) +DEF_HELPER_3(extrq_r, void, env, ZMMReg, ZMMReg) +DEF_HELPER_4(extrq_i, void, env, ZMMReg, int, int) +DEF_HELPER_3(insertq_r, void, env, ZMMReg, ZMMReg) +DEF_HELPER_5(insertq_i, void, env, ZMMReg, ZMMReg, int, int) +#endif + +SSE_HELPER_P4(hadd) +SSE_HELPER_P4(hsub) +SSE_HELPER_P4(addsub) + +#define SSE_HELPER_CMP(name, F, C) SSE_HELPER_S4(name) + +SSE_HELPER_CMP(cmpeq, FPU_CMPQ, FPU_EQ) +SSE_HELPER_CMP(cmplt, FPU_CMPS, FPU_LT) +SSE_HELPER_CMP(cmple, FPU_CMPS, FPU_LE) +SSE_HELPER_CMP(cmpunord, FPU_CMPQ, FPU_UNORD) +SSE_HELPER_CMP(cmpneq, FPU_CMPQ, !FPU_EQ) +SSE_HELPER_CMP(cmpnlt, FPU_CMPS, !FPU_LT) +SSE_HELPER_CMP(cmpnle, FPU_CMPS, !FPU_LE) +SSE_HELPER_CMP(cmpord, FPU_CMPQ, !FPU_UNORD) + +SSE_HELPER_CMP(cmpequ, FPU_CMPQ, FPU_EQU) +SSE_HELPER_CMP(cmpnge, FPU_CMPS, !FPU_GE) +SSE_HELPER_CMP(cmpngt, FPU_CMPS, !FPU_GT) +SSE_HELPER_CMP(cmpfalse, FPU_CMPQ, FPU_FALSE) +SSE_HELPER_CMP(cmpnequ, FPU_CMPQ, !FPU_EQU) +SSE_HELPER_CMP(cmpge, FPU_CMPS, FPU_GE) +SSE_HELPER_CMP(cmpgt, FPU_CMPS, FPU_GT) +SSE_HELPER_CMP(cmptrue, FPU_CMPQ, !FPU_FALSE) + +SSE_HELPER_CMP(cmpeqs, FPU_CMPS, FPU_EQ) +SSE_HELPER_CMP(cmpltq, FPU_CMPQ, FPU_LT) +SSE_HELPER_CMP(cmpleq, FPU_CMPQ, FPU_LE) +SSE_HELPER_CMP(cmpunords, FPU_CMPS, FPU_UNORD) +SSE_HELPER_CMP(cmpneqq, FPU_CMPS, !FPU_EQ) +SSE_HELPER_CMP(cmpnltq, FPU_CMPQ, !FPU_LT) +SSE_HELPER_CMP(cmpnleq, FPU_CMPQ, !FPU_LE) +SSE_HELPER_CMP(cmpords, FPU_CMPS, !FPU_UNORD) + +SSE_HELPER_CMP(cmpequs, FPU_CMPS, FPU_EQU) +SSE_HELPER_CMP(cmpngeq, FPU_CMPQ, !FPU_GE) +SSE_HELPER_CMP(cmpngtq, FPU_CMPQ, !FPU_GT) +SSE_HELPER_CMP(cmpfalses, FPU_CMPS, FPU_FALSE) +SSE_HELPER_CMP(cmpnequs, FPU_CMPS, !FPU_EQU) +SSE_HELPER_CMP(cmpgeq, FPU_CMPQ, FPU_GE) +SSE_HELPER_CMP(cmpgtq, FPU_CMPQ, FPU_GT) +SSE_HELPER_CMP(cmptrues, FPU_CMPS, !FPU_FALSE) + +#if SHIFT == 1 +DEF_HELPER_3(ucomiss, void, env, Reg, Reg) +DEF_HELPER_3(comiss, void, env, Reg, Reg) +DEF_HELPER_3(ucomisd, void, env, Reg, Reg) +DEF_HELPER_3(comisd, void, env, Reg, Reg) +#endif + +DEF_HELPER_2(glue(movmskps, SUFFIX), i32, env, Reg) +DEF_HELPER_2(glue(movmskpd, SUFFIX), i32, env, Reg) +#endif + +DEF_HELPER_4(glue(packsswb, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(packuswb, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(packssdw, SUFFIX), void, env, Reg, Reg, Reg) +#define UNPCK_OP(name, base) \ + DEF_HELPER_4(glue(punpck ## name ## bw, SUFFIX), void, env, Reg, Reg, Reg) \ + DEF_HELPER_4(glue(punpck ## name ## wd, SUFFIX), void, env, Reg, Reg, Reg) \ + DEF_HELPER_4(glue(punpck ## name ## dq, SUFFIX), void, env, Reg, Reg, Reg) + +UNPCK_OP(l, 0) +UNPCK_OP(h, 1) + +#if SHIFT >= 1 +DEF_HELPER_4(glue(punpcklqdq, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(punpckhqdq, SUFFIX), void, env, Reg, Reg, Reg) +#endif + +/* 3DNow! float ops */ +#if SHIFT == 0 +DEF_HELPER_3(pi2fd, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pi2fw, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pf2id, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pf2iw, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfacc, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfadd, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfcmpeq, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfcmpge, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfcmpgt, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfmax, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfmin, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfmul, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfnacc, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfpnacc, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfrcp, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfrsqrt, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfsub, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfsubr, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pswapd, void, env, MMXReg, MMXReg) +#endif + +/* SSSE3 op helpers */ +DEF_HELPER_4(glue(phaddw, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(phaddd, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(phaddsw, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(phsubw, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(phsubd, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(phsubsw, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(pmaddubsw, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(pmulhrsw, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(pshufb, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(psignb, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(psignw, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(psignd, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_5(glue(palignr, SUFFIX), void, env, Reg, Reg, Reg, i32) + +/* SSE4.1 op helpers */ +#if SHIFT >= 1 +DEF_HELPER_5(glue(pblendvb, SUFFIX), void, env, Reg, Reg, Reg, Reg) +DEF_HELPER_5(glue(blendvps, SUFFIX), void, env, Reg, Reg, Reg, Reg) +DEF_HELPER_5(glue(blendvpd, SUFFIX), void, env, Reg, Reg, Reg, Reg) +DEF_HELPER_3(glue(ptest, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovsxbw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovsxbd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovsxbq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovsxwd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovsxwq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovsxdq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovzxbw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovzxbd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovzxbq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovzxwd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovzxwq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovzxdq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovsldup, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovshdup, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovdldup, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_4(glue(pmuldq, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(packusdw, SUFFIX), void, env, Reg, Reg, Reg) +#if SHIFT == 1 +DEF_HELPER_3(glue(phminposuw, SUFFIX), void, env, Reg, Reg) +#endif +DEF_HELPER_4(glue(roundps, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(roundpd, SUFFIX), void, env, Reg, Reg, i32) +#if SHIFT == 1 +DEF_HELPER_5(roundss_xmm, void, env, Reg, Reg, Reg, i32) +DEF_HELPER_5(roundsd_xmm, void, env, Reg, Reg, Reg, i32) +#endif +DEF_HELPER_5(glue(blendps, SUFFIX), void, env, Reg, Reg, Reg, i32) +DEF_HELPER_5(glue(blendpd, SUFFIX), void, env, Reg, Reg, Reg, i32) +DEF_HELPER_5(glue(pblendw, SUFFIX), void, env, Reg, Reg, Reg, i32) +DEF_HELPER_5(glue(dpps, SUFFIX), void, env, Reg, Reg, Reg, i32) +#if SHIFT == 1 +DEF_HELPER_5(glue(dppd, SUFFIX), void, env, Reg, Reg, Reg, i32) +#endif +DEF_HELPER_5(glue(mpsadbw, SUFFIX), void, env, Reg, Reg, Reg, i32) +#endif + +/* SSE4.2 op helpers */ +#if SHIFT == 1 +DEF_HELPER_4(glue(pcmpestri, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(pcmpestrm, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(pcmpistri, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(pcmpistrm, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_3(crc32, tl, i32, tl, i32) +#endif + +/* AES-NI op helpers */ +#if SHIFT >= 1 +DEF_HELPER_4(glue(aesdec, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(aesdeclast, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(aesenc, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(aesenclast, SUFFIX), void, env, Reg, Reg, Reg) +#if SHIFT == 1 +DEF_HELPER_3(glue(aesimc, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_4(glue(aeskeygenassist, SUFFIX), void, env, Reg, Reg, i32) +#endif +DEF_HELPER_5(glue(pclmulqdq, SUFFIX), void, env, Reg, Reg, Reg, i32) +#endif + +/* F16C helpers */ +#if SHIFT >= 1 +DEF_HELPER_3(glue(cvtph2ps, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_4(glue(cvtps2ph, SUFFIX), void, env, Reg, Reg, int) +#endif + +/* FMA3 helpers */ +#if SHIFT == 1 +DEF_HELPER_6(fma4ss, void, env, Reg, Reg, Reg, Reg, int) +DEF_HELPER_6(fma4sd, void, env, Reg, Reg, Reg, Reg, int) +#endif + +#if SHIFT >= 1 +DEF_HELPER_7(glue(fma4ps, SUFFIX), void, env, Reg, Reg, Reg, Reg, int, int) +DEF_HELPER_7(glue(fma4pd, SUFFIX), void, env, Reg, Reg, Reg, Reg, int, int) +#endif + +/* AVX helpers */ +#if SHIFT >= 1 +DEF_HELPER_4(glue(vpermilpd, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(vpermilps, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_3(glue(vpermilpd_imm, SUFFIX), void, Reg, Reg, i32) +DEF_HELPER_3(glue(vpermilps_imm, SUFFIX), void, Reg, Reg, i32) +DEF_HELPER_4(glue(vpsrlvd, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(vpsravd, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(vpsllvd, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(vpsrlvq, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(vpsravq, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(vpsllvq, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_3(glue(vtestps, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(vtestpd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_4(glue(vpmaskmovd_st, SUFFIX), void, env, Reg, Reg, tl) +DEF_HELPER_4(glue(vpmaskmovq_st, SUFFIX), void, env, Reg, Reg, tl) +DEF_HELPER_4(glue(vpmaskmovd, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_4(glue(vpmaskmovq, SUFFIX), void, env, Reg, Reg, Reg) +DEF_HELPER_6(glue(vpgatherdd, SUFFIX), void, env, Reg, Reg, Reg, tl, i32) +DEF_HELPER_6(glue(vpgatherdq, SUFFIX), void, env, Reg, Reg, Reg, tl, i32) +DEF_HELPER_6(glue(vpgatherqd, SUFFIX), void, env, Reg, Reg, Reg, tl, i32) +DEF_HELPER_6(glue(vpgatherqq, SUFFIX), void, env, Reg, Reg, Reg, tl, i32) +#if SHIFT == 2 +DEF_HELPER_3(vpermd_ymm, void, Reg, Reg, Reg) +DEF_HELPER_4(vpermdq_ymm, void, Reg, Reg, Reg, i32) +DEF_HELPER_3(vpermq_ymm, void, Reg, Reg, i32) +#endif +#endif + +/* SHA helpers */ +#if SHIFT == 1 +DEF_HELPER_3(sha1rnds4_f0, void, Reg, Reg, Reg) +DEF_HELPER_3(sha1rnds4_f1, void, Reg, Reg, Reg) +DEF_HELPER_3(sha1rnds4_f2, void, Reg, Reg, Reg) +DEF_HELPER_3(sha1rnds4_f3, void, Reg, Reg, Reg) +DEF_HELPER_3(sha1nexte, void, Reg, Reg, Reg) +DEF_HELPER_3(sha1msg1, void, Reg, Reg, Reg) +DEF_HELPER_3(sha1msg2, void, Reg, Reg, Reg) +DEF_HELPER_5(sha256rnds2, void, Reg, Reg, Reg, i32, i32) +DEF_HELPER_3(sha256msg1, void, Reg, Reg, Reg) +DEF_HELPER_3(sha256msg2, void, Reg, Reg, Reg) +#endif + +#undef SHIFT +#undef Reg +#undef SUFFIX + +#undef SSE_HELPER_B +#undef SSE_HELPER_W +#undef SSE_HELPER_L +#undef SSE_HELPER_Q +#undef SSE_HELPER_S3 +#undef SSE_HELPER_S4 +#undef SSE_HELPER_P3 +#undef SSE_HELPER_P4 +#undef SSE_HELPER_CMP +#undef UNPCK_OP diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index bffd82923f..34ccabd8ce 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -226,14 +226,29 @@ static void tss_load_seg(CPUX86State *env, X86Seg seg_reg, int selector, } } +static void tss_set_busy(CPUX86State *env, int tss_selector, bool value, + uintptr_t retaddr) +{ + target_ulong ptr = env->gdt.base + (tss_selector & ~7); + uint32_t e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); + + if (value) { + e2 |= DESC_TSS_BUSY_MASK; + } else { + e2 &= ~DESC_TSS_BUSY_MASK; + } + + cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr); +} + #define SWITCH_TSS_JMP 0 #define SWITCH_TSS_IRET 1 #define SWITCH_TSS_CALL 2 -/* XXX: restore CPU state in registers (PowerPC case) */ -static void switch_tss_ra(CPUX86State *env, int tss_selector, - uint32_t e1, uint32_t e2, int source, - uint32_t next_eip, uintptr_t retaddr) +/* return 0 if switching to a 16-bit selector */ +static int switch_tss_ra(CPUX86State *env, int tss_selector, + uint32_t e1, uint32_t e2, int source, + uint32_t next_eip, uintptr_t retaddr) { int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i; target_ulong tss_base; @@ -341,13 +356,7 @@ static void switch_tss_ra(CPUX86State *env, int tss_selector, /* clear busy bit (it is restartable) */ if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) { - target_ulong ptr; - uint32_t e2; - - ptr = env->gdt.base + (env->tr.selector & ~7); - e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); - e2 &= ~DESC_TSS_BUSY_MASK; - cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr); + tss_set_busy(env, env->tr.selector, 0, retaddr); } old_eflags = cpu_compute_eflags(env); if (source == SWITCH_TSS_IRET) { @@ -399,13 +408,7 @@ static void switch_tss_ra(CPUX86State *env, int tss_selector, /* set busy bit */ if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) { - target_ulong ptr; - uint32_t e2; - - ptr = env->gdt.base + (tss_selector & ~7); - e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); - e2 |= DESC_TSS_BUSY_MASK; - cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr); + tss_set_busy(env, tss_selector, 1, retaddr); } /* set the new CPU state */ @@ -499,13 +502,14 @@ static void switch_tss_ra(CPUX86State *env, int tss_selector, cpu_x86_update_dr7(env, env->dr[7] & ~DR7_LOCAL_BP_MASK); } #endif + return type >> 3; } -static void switch_tss(CPUX86State *env, int tss_selector, - uint32_t e1, uint32_t e2, int source, - uint32_t next_eip) +static int switch_tss(CPUX86State *env, int tss_selector, + uint32_t e1, uint32_t e2, int source, + uint32_t next_eip) { - switch_tss_ra(env, tss_selector, e1, e2, source, next_eip, 0); + return switch_tss_ra(env, tss_selector, e1, e2, source, next_eip, 0); } static inline unsigned int get_sp_mask(unsigned int e2) @@ -647,14 +651,11 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, if (!(e2 & DESC_P_MASK)) { raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2); } - switch_tss(env, intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip); + shift = switch_tss(env, intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip); if (has_error_code) { - int type; uint32_t mask; /* push the error code */ - type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; - shift = type >> 3; if (env->segs[R_SS].flags & DESC_B_MASK) { mask = 0xffffffff; } else { @@ -882,7 +883,7 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, dt = &env->idt; if (intno * 16 + 15 > dt->limit) { - raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2); + raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); } ptr = dt->base + intno * 16; e1 = cpu_ldl_kernel(env, ptr); @@ -895,18 +896,18 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, case 15: /* 386 trap gate */ break; default: - raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2); + raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); break; } dpl = (e2 >> DESC_DPL_SHIFT) & 3; cpl = env->hflags & HF_CPL_MASK; /* check privilege if software int */ if (is_int && dpl < cpl) { - raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2); + raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); } /* check valid bit */ if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, intno * 16 + 2); + raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2); } selector = e1 >> 16; offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff); @@ -977,6 +978,7 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, e2); env->eip = offset; } +#endif /* TARGET_X86_64 */ void helper_sysret(CPUX86State *env, int dflag) { @@ -990,6 +992,7 @@ void helper_sysret(CPUX86State *env, int dflag) raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } selector = (env->star >> 48) & 0xffff; +#ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | @@ -1015,7 +1018,9 @@ void helper_sysret(CPUX86State *env, int dflag) DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | DESC_W_MASK | DESC_A_MASK); - } else { + } else +#endif + { env->eflags |= IF_MASK; cpu_x86_load_seg_cache(env, R_CS, selector | 3, 0, 0xffffffff, @@ -1030,7 +1035,6 @@ void helper_sysret(CPUX86State *env, int dflag) DESC_W_MASK | DESC_A_MASK); } } -#endif /* TARGET_X86_64 */ /* real mode interrupt */ static void do_interrupt_real(CPUX86State *env, int intno, int is_int, @@ -1504,14 +1508,12 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, } /* real mode call */ -void helper_lcall_real(CPUX86State *env, int new_cs, target_ulong new_eip1, - int shift, int next_eip) +void helper_lcall_real(CPUX86State *env, uint32_t new_cs, uint32_t new_eip, + int shift, uint32_t next_eip) { - int new_eip; uint32_t esp, esp_mask; target_ulong ssp; - new_eip = new_eip1; esp = env->regs[R_ESP]; esp_mask = get_sp_mask(env->segs[R_SS].flags); ssp = env->segs[R_SS].base; @@ -2228,7 +2230,7 @@ target_ulong helper_lsl(CPUX86State *env, target_ulong selector1) int rpl, dpl, cpl, type; selector = selector1 & 0xffff; - eflags = cpu_cc_compute_all(env, CC_OP); + eflags = cpu_cc_compute_all(env); if ((selector & 0xfffc) == 0) { goto fail; } @@ -2275,7 +2277,7 @@ target_ulong helper_lar(CPUX86State *env, target_ulong selector1) int rpl, dpl, cpl, type; selector = selector1 & 0xffff; - eflags = cpu_cc_compute_all(env, CC_OP); + eflags = cpu_cc_compute_all(env); if ((selector & 0xfffc) == 0) { goto fail; } @@ -2324,7 +2326,7 @@ void helper_verr(CPUX86State *env, target_ulong selector1) int rpl, dpl, cpl; selector = selector1 & 0xffff; - eflags = cpu_cc_compute_all(env, CC_OP); + eflags = cpu_cc_compute_all(env); if ((selector & 0xfffc) == 0) { goto fail; } @@ -2362,7 +2364,7 @@ void helper_verw(CPUX86State *env, target_ulong selector1) int rpl, dpl, cpl; selector = selector1 & 0xffff; - eflags = cpu_cc_compute_all(env, CC_OP); + eflags = cpu_cc_compute_all(env); if ((selector & 0xfffc) == 0) { goto fail; } diff --git a/target/i386/shift_helper_template.h b/target/i386/tcg/shift_helper_template.h.inc similarity index 100% rename from target/i386/shift_helper_template.h rename to target/i386/tcg/shift_helper_template.h.inc diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/sysemu/excp_helper.c index 48feba7e75..7a57b7dd10 100644 --- a/target/i386/tcg/sysemu/excp_helper.c +++ b/target/i386/tcg/sysemu/excp_helper.c @@ -19,153 +19,275 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/cpu_ldst.h" #include "exec/exec-all.h" #include "tcg/helper-tcg.h" -#define PG_ERROR_OK (-1) +typedef struct TranslateParams { + target_ulong addr; + target_ulong cr3; + int pg_mode; + int mmu_idx; + int ptw_idx; + MMUAccessType access_type; +} TranslateParams; -typedef hwaddr (*MMUTranslateFunc)(CPUState *cs, hwaddr gphys, MMUAccessType access_type, - int *prot); +typedef struct TranslateResult { + hwaddr paddr; + int prot; + int page_size; +} TranslateResult; -#define GET_HPHYS(cs, gpa, access_type, prot) \ - (get_hphys_func ? get_hphys_func(cs, gpa, access_type, prot) : gpa) +typedef enum TranslateFaultStage2 { + S2_NONE, + S2_GPA, + S2_GPT, +} TranslateFaultStage2; -static int mmu_translate(CPUState *cs, hwaddr addr, MMUTranslateFunc get_hphys_func, - uint64_t cr3, int is_write1, int mmu_idx, int pg_mode, - hwaddr *xlat, int *page_size, int *prot) +typedef struct TranslateFault { + int exception_index; + int error_code; + target_ulong cr2; + TranslateFaultStage2 stage2; +} TranslateFault; + +typedef struct PTETranslate { + CPUX86State *env; + TranslateFault *err; + int ptw_idx; + void *haddr; + hwaddr gaddr; +} PTETranslate; + +static bool ptw_translate(PTETranslate *inout, hwaddr addr, uint64_t ra) { - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - uint64_t ptep, pte; - int32_t a20_mask; - target_ulong pde_addr, pte_addr; - int error_code = 0; - int is_dirty, is_write, is_user; - uint64_t rsvd_mask = PG_ADDRESS_MASK & ~MAKE_64BIT_MASK(0, cpu->phys_bits); - uint32_t page_offset; + CPUTLBEntryFull *full; + int flags; + + inout->gaddr = addr; + flags = probe_access_full(inout->env, addr, 0, MMU_DATA_STORE, + inout->ptw_idx, true, &inout->haddr, &full, ra); + + if (unlikely(flags & TLB_INVALID_MASK)) { + TranslateFault *err = inout->err; + + assert(inout->ptw_idx == MMU_NESTED_IDX); + *err = (TranslateFault){ + .error_code = inout->env->error_code, + .cr2 = addr, + .stage2 = S2_GPT, + }; + return false; + } + return true; +} + +static inline uint32_t ptw_ldl(const PTETranslate *in, uint64_t ra) +{ + if (likely(in->haddr)) { + return ldl_p(in->haddr); + } + return cpu_ldl_mmuidx_ra(in->env, in->gaddr, in->ptw_idx, ra); +} + +static inline uint64_t ptw_ldq(const PTETranslate *in, uint64_t ra) +{ + if (likely(in->haddr)) { + return ldq_p(in->haddr); + } + return cpu_ldq_mmuidx_ra(in->env, in->gaddr, in->ptw_idx, ra); +} + +/* + * Note that we can use a 32-bit cmpxchg for all page table entries, + * even 64-bit ones, because PG_PRESENT_MASK, PG_ACCESSED_MASK and + * PG_DIRTY_MASK are all in the low 32 bits. + */ +static bool ptw_setl_slow(const PTETranslate *in, uint32_t old, uint32_t new) +{ + uint32_t cmp; + + /* Does x86 really perform a rmw cycle on mmio for ptw? */ + start_exclusive(); + cmp = cpu_ldl_mmuidx_ra(in->env, in->gaddr, in->ptw_idx, 0); + if (cmp == old) { + cpu_stl_mmuidx_ra(in->env, in->gaddr, new, in->ptw_idx, 0); + } + end_exclusive(); + return cmp == old; +} + +static inline bool ptw_setl(const PTETranslate *in, uint32_t old, uint32_t set) +{ + if (set & ~old) { + uint32_t new = old | set; + if (likely(in->haddr)) { + old = cpu_to_le32(old); + new = cpu_to_le32(new); + return qatomic_cmpxchg((uint32_t *)in->haddr, old, new) == old; + } + return ptw_setl_slow(in, old, new); + } + return true; +} + +static bool mmu_translate(CPUX86State *env, const TranslateParams *in, + TranslateResult *out, TranslateFault *err, + uint64_t ra) +{ + const target_ulong addr = in->addr; + const int pg_mode = in->pg_mode; + const bool is_user = is_mmu_index_user(in->mmu_idx); + const MMUAccessType access_type = in->access_type; + uint64_t ptep, pte, rsvd_mask; + PTETranslate pte_trans = { + .env = env, + .err = err, + .ptw_idx = in->ptw_idx, + }; + hwaddr pte_addr, paddr; uint32_t pkr; + int page_size; + int error_code; - is_user = (mmu_idx == MMU_USER_IDX); - is_write = is_write1 & 1; - a20_mask = x86_get_a20_mask(env); - + restart_all: + rsvd_mask = ~MAKE_64BIT_MASK(0, env_archcpu(env)->phys_bits); + rsvd_mask &= PG_ADDRESS_MASK; if (!(pg_mode & PG_MODE_NXE)) { rsvd_mask |= PG_NX_MASK; } if (pg_mode & PG_MODE_PAE) { - uint64_t pde, pdpe; - target_ulong pdpe_addr; - #ifdef TARGET_X86_64 if (pg_mode & PG_MODE_LMA) { - bool la57 = pg_mode & PG_MODE_LA57; - uint64_t pml5e_addr, pml5e; - uint64_t pml4e_addr, pml4e; - - if (la57) { - pml5e_addr = ((cr3 & ~0xfff) + - (((addr >> 48) & 0x1ff) << 3)) & a20_mask; - pml5e_addr = GET_HPHYS(cs, pml5e_addr, MMU_DATA_STORE, NULL); - pml5e = x86_ldq_phys(cs, pml5e_addr); - if (!(pml5e & PG_PRESENT_MASK)) { + if (pg_mode & PG_MODE_LA57) { + /* + * Page table level 5 + */ + pte_addr = (in->cr3 & ~0xfff) + (((addr >> 48) & 0x1ff) << 3); + if (!ptw_translate(&pte_trans, pte_addr, ra)) { + return false; + } + restart_5: + pte = ptw_ldq(&pte_trans, ra); + if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } - if (pml5e & (rsvd_mask | PG_PSE_MASK)) { + if (pte & (rsvd_mask | PG_PSE_MASK)) { goto do_fault_rsvd; } - if (!(pml5e & PG_ACCESSED_MASK)) { - pml5e |= PG_ACCESSED_MASK; - x86_stl_phys_notdirty(cs, pml5e_addr, pml5e); + if (!ptw_setl(&pte_trans, pte, PG_ACCESSED_MASK)) { + goto restart_5; } - ptep = pml5e ^ PG_NX_MASK; + ptep = pte ^ PG_NX_MASK; } else { - pml5e = cr3; + pte = in->cr3; ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; } - pml4e_addr = ((pml5e & PG_ADDRESS_MASK) + - (((addr >> 39) & 0x1ff) << 3)) & a20_mask; - pml4e_addr = GET_HPHYS(cs, pml4e_addr, MMU_DATA_STORE, NULL); - pml4e = x86_ldq_phys(cs, pml4e_addr); - if (!(pml4e & PG_PRESENT_MASK)) { + /* + * Page table level 4 + */ + pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 39) & 0x1ff) << 3); + if (!ptw_translate(&pte_trans, pte_addr, ra)) { + return false; + } + restart_4: + pte = ptw_ldq(&pte_trans, ra); + if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } - if (pml4e & (rsvd_mask | PG_PSE_MASK)) { + if (pte & (rsvd_mask | PG_PSE_MASK)) { goto do_fault_rsvd; } - if (!(pml4e & PG_ACCESSED_MASK)) { - pml4e |= PG_ACCESSED_MASK; - x86_stl_phys_notdirty(cs, pml4e_addr, pml4e); + if (!ptw_setl(&pte_trans, pte, PG_ACCESSED_MASK)) { + goto restart_4; } - ptep &= pml4e ^ PG_NX_MASK; - pdpe_addr = ((pml4e & PG_ADDRESS_MASK) + (((addr >> 30) & 0x1ff) << 3)) & - a20_mask; - pdpe_addr = GET_HPHYS(cs, pdpe_addr, MMU_DATA_STORE, NULL); - pdpe = x86_ldq_phys(cs, pdpe_addr); - if (!(pdpe & PG_PRESENT_MASK)) { + ptep &= pte ^ PG_NX_MASK; + + /* + * Page table level 3 + */ + pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 30) & 0x1ff) << 3); + if (!ptw_translate(&pte_trans, pte_addr, ra)) { + return false; + } + restart_3_lma: + pte = ptw_ldq(&pte_trans, ra); + if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } - if (pdpe & rsvd_mask) { + if (pte & rsvd_mask) { goto do_fault_rsvd; } - ptep &= pdpe ^ PG_NX_MASK; - if (!(pdpe & PG_ACCESSED_MASK)) { - pdpe |= PG_ACCESSED_MASK; - x86_stl_phys_notdirty(cs, pdpe_addr, pdpe); + if (!ptw_setl(&pte_trans, pte, PG_ACCESSED_MASK)) { + goto restart_3_lma; } - if (pdpe & PG_PSE_MASK) { + ptep &= pte ^ PG_NX_MASK; + if (pte & PG_PSE_MASK) { /* 1 GB page */ - *page_size = 1024 * 1024 * 1024; - pte_addr = pdpe_addr; - pte = pdpe; + page_size = 1024 * 1024 * 1024; goto do_check_protect; } } else #endif { - /* XXX: load them when cr3 is loaded ? */ - pdpe_addr = ((cr3 & ~0x1f) + ((addr >> 27) & 0x18)) & - a20_mask; - pdpe_addr = GET_HPHYS(cs, pdpe_addr, MMU_DATA_STORE, NULL); - pdpe = x86_ldq_phys(cs, pdpe_addr); - if (!(pdpe & PG_PRESENT_MASK)) { - goto do_fault; + /* + * Page table level 3 + */ + pte_addr = (in->cr3 & 0xffffffe0ULL) + ((addr >> 27) & 0x18); + if (!ptw_translate(&pte_trans, pte_addr, ra)) { + return false; } rsvd_mask |= PG_HI_USER_MASK; - if (pdpe & (rsvd_mask | PG_NX_MASK)) { + restart_3_nolma: + pte = ptw_ldq(&pte_trans, ra); + if (!(pte & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pte & (rsvd_mask | PG_NX_MASK)) { goto do_fault_rsvd; } + if (!ptw_setl(&pte_trans, pte, PG_ACCESSED_MASK)) { + goto restart_3_nolma; + } ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; } - pde_addr = ((pdpe & PG_ADDRESS_MASK) + (((addr >> 21) & 0x1ff) << 3)) & - a20_mask; - pde_addr = GET_HPHYS(cs, pde_addr, MMU_DATA_STORE, NULL); - pde = x86_ldq_phys(cs, pde_addr); - if (!(pde & PG_PRESENT_MASK)) { + /* + * Page table level 2 + */ + pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 21) & 0x1ff) << 3); + if (!ptw_translate(&pte_trans, pte_addr, ra)) { + return false; + } + restart_2_pae: + pte = ptw_ldq(&pte_trans, ra); + if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } - if (pde & rsvd_mask) { + if (pte & rsvd_mask) { goto do_fault_rsvd; } - ptep &= pde ^ PG_NX_MASK; - if (pde & PG_PSE_MASK) { + if (pte & PG_PSE_MASK) { /* 2 MB page */ - *page_size = 2048 * 1024; - pte_addr = pde_addr; - pte = pde; + page_size = 2048 * 1024; + ptep &= pte ^ PG_NX_MASK; goto do_check_protect; } - /* 4 KB page */ - if (!(pde & PG_ACCESSED_MASK)) { - pde |= PG_ACCESSED_MASK; - x86_stl_phys_notdirty(cs, pde_addr, pde); + if (!ptw_setl(&pte_trans, pte, PG_ACCESSED_MASK)) { + goto restart_2_pae; } - pte_addr = ((pde & PG_ADDRESS_MASK) + (((addr >> 12) & 0x1ff) << 3)) & - a20_mask; - pte_addr = GET_HPHYS(cs, pte_addr, MMU_DATA_STORE, NULL); - pte = x86_ldq_phys(cs, pte_addr); + ptep &= pte ^ PG_NX_MASK; + + /* + * Page table level 1 + */ + pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 12) & 0x1ff) << 3); + if (!ptw_translate(&pte_trans, pte_addr, ra)) { + return false; + } + pte = ptw_ldq(&pte_trans, ra); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } @@ -174,54 +296,56 @@ static int mmu_translate(CPUState *cs, hwaddr addr, MMUTranslateFunc get_hphys_f } /* combine pde and pte nx, user and rw protections */ ptep &= pte ^ PG_NX_MASK; - *page_size = 4096; + page_size = 4096; } else { - uint32_t pde; - - /* page directory entry */ - pde_addr = ((cr3 & ~0xfff) + ((addr >> 20) & 0xffc)) & - a20_mask; - pde_addr = GET_HPHYS(cs, pde_addr, MMU_DATA_STORE, NULL); - pde = x86_ldl_phys(cs, pde_addr); - if (!(pde & PG_PRESENT_MASK)) { + /* + * Page table level 2 + */ + pte_addr = (in->cr3 & 0xfffff000ULL) + ((addr >> 20) & 0xffc); + if (!ptw_translate(&pte_trans, pte_addr, ra)) { + return false; + } + restart_2_nopae: + pte = ptw_ldl(&pte_trans, ra); + if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } - ptep = pde | PG_NX_MASK; + ptep = pte | PG_NX_MASK; /* if PSE bit is set, then we use a 4MB page */ - if ((pde & PG_PSE_MASK) && (pg_mode & PG_MODE_PSE)) { - *page_size = 4096 * 1024; - pte_addr = pde_addr; - - /* Bits 20-13 provide bits 39-32 of the address, bit 21 is reserved. + if ((pte & PG_PSE_MASK) && (pg_mode & PG_MODE_PSE)) { + page_size = 4096 * 1024; + /* + * Bits 20-13 provide bits 39-32 of the address, bit 21 is reserved. * Leave bits 20-13 in place for setting accessed/dirty bits below. */ - pte = pde | ((pde & 0x1fe000LL) << (32 - 13)); + pte = (uint32_t)pte | ((pte & 0x1fe000LL) << (32 - 13)); rsvd_mask = 0x200000; goto do_check_protect_pse36; } - - if (!(pde & PG_ACCESSED_MASK)) { - pde |= PG_ACCESSED_MASK; - x86_stl_phys_notdirty(cs, pde_addr, pde); + if (!ptw_setl(&pte_trans, pte, PG_ACCESSED_MASK)) { + goto restart_2_nopae; } - /* page directory entry */ - pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & - a20_mask; - pte_addr = GET_HPHYS(cs, pte_addr, MMU_DATA_STORE, NULL); - pte = x86_ldl_phys(cs, pte_addr); + /* + * Page table level 1 + */ + pte_addr = (pte & ~0xfffu) + ((addr >> 10) & 0xffc); + if (!ptw_translate(&pte_trans, pte_addr, ra)) { + return false; + } + pte = ptw_ldl(&pte_trans, ra); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } /* combine pde and pte user and rw protections */ ptep &= pte | PG_NX_MASK; - *page_size = 4096; + page_size = 4096; rsvd_mask = 0; } do_check_protect: - rsvd_mask |= (*page_size - 1) & PG_ADDRESS_MASK & ~PG_PSE_PAT_MASK; + rsvd_mask |= (page_size - 1) & PG_ADDRESS_MASK & ~PG_PSE_PAT_MASK; do_check_protect_pse36: if (pte & rsvd_mask) { goto do_fault_rsvd; @@ -233,17 +357,17 @@ do_check_protect_pse36: goto do_fault_protect; } - *prot = 0; - if (mmu_idx != MMU_KSMAP_IDX || !(ptep & PG_USER_MASK)) { - *prot |= PAGE_READ; + int prot = 0; + if (!is_mmu_index_smap(in->mmu_idx) || !(ptep & PG_USER_MASK)) { + prot |= PAGE_READ; if ((ptep & PG_RW_MASK) || !(is_user || (pg_mode & PG_MODE_WP))) { - *prot |= PAGE_WRITE; + prot |= PAGE_WRITE; } } if (!(ptep & PG_NX_MASK) && - (mmu_idx == MMU_USER_IDX || + (is_user || !((pg_mode & PG_MODE_SMEP) && (ptep & PG_USER_MASK)))) { - *prot |= PAGE_EXEC; + prot |= PAGE_EXEC; } if (ptep & PG_USER_MASK) { @@ -262,180 +386,258 @@ do_check_protect_pse36: } else if (pkr_wd && (is_user || (pg_mode & PG_MODE_WP))) { pkr_prot &= ~PAGE_WRITE; } - - *prot &= pkr_prot; - if ((pkr_prot & (1 << is_write1)) == 0) { - assert(is_write1 != 2); - error_code |= PG_ERROR_PK_MASK; - goto do_fault_protect; + if ((pkr_prot & (1 << access_type)) == 0) { + goto do_fault_pk_protect; } + prot &= pkr_prot; } - if ((*prot & (1 << is_write1)) == 0) { + if ((prot & (1 << access_type)) == 0) { goto do_fault_protect; } /* yes, it can! */ - is_dirty = is_write && !(pte & PG_DIRTY_MASK); - if (!(pte & PG_ACCESSED_MASK) || is_dirty) { - pte |= PG_ACCESSED_MASK; - if (is_dirty) { - pte |= PG_DIRTY_MASK; + { + uint32_t set = PG_ACCESSED_MASK; + if (access_type == MMU_DATA_STORE) { + set |= PG_DIRTY_MASK; + } else if (!(pte & PG_DIRTY_MASK)) { + /* + * Only set write access if already dirty... + * otherwise wait for dirty access. + */ + prot &= ~PAGE_WRITE; + } + if (!ptw_setl(&pte_trans, pte, set)) { + /* + * We can arrive here from any of 3 levels and 2 formats. + * The only safe thing is to restart the entire lookup. + */ + goto restart_all; } - x86_stl_phys_notdirty(cs, pte_addr, pte); } - if (!(pte & PG_DIRTY_MASK)) { - /* only set write access if already dirty... otherwise wait - for dirty access */ - assert(!is_write); - *prot &= ~PAGE_WRITE; + /* merge offset within page */ + paddr = (pte & PG_ADDRESS_MASK & ~(page_size - 1)) | (addr & (page_size - 1)); + + /* + * Note that NPT is walked (for both paging structures and final guest + * addresses) using the address with the A20 bit set. + */ + if (in->ptw_idx == MMU_NESTED_IDX) { + CPUTLBEntryFull *full; + int flags, nested_page_size; + + flags = probe_access_full(env, paddr, 0, access_type, + MMU_NESTED_IDX, true, + &pte_trans.haddr, &full, 0); + if (unlikely(flags & TLB_INVALID_MASK)) { + *err = (TranslateFault){ + .error_code = env->error_code, + .cr2 = paddr, + .stage2 = S2_GPA, + }; + return false; + } + + /* Merge stage1 & stage2 protection bits. */ + prot &= full->prot; + + /* Re-verify resulting protection. */ + if ((prot & (1 << access_type)) == 0) { + goto do_fault_protect; + } + + /* Merge stage1 & stage2 addresses to final physical address. */ + nested_page_size = 1 << full->lg_page_size; + paddr = (full->phys_addr & ~(nested_page_size - 1)) + | (paddr & (nested_page_size - 1)); + + /* + * Use the larger of stage1 & stage2 page sizes, so that + * invalidation works. + */ + if (nested_page_size > page_size) { + page_size = nested_page_size; + } } - pte = pte & a20_mask; - - /* align to page_size */ - pte &= PG_ADDRESS_MASK & ~(*page_size - 1); - page_offset = addr & (*page_size - 1); - *xlat = GET_HPHYS(cs, pte + page_offset, is_write1, prot); - return PG_ERROR_OK; + out->paddr = paddr & x86_get_a20_mask(env); + out->prot = prot; + out->page_size = page_size; + return true; do_fault_rsvd: - error_code |= PG_ERROR_RSVD_MASK; + error_code = PG_ERROR_RSVD_MASK; + goto do_fault_cont; do_fault_protect: - error_code |= PG_ERROR_P_MASK; + error_code = PG_ERROR_P_MASK; + goto do_fault_cont; + do_fault_pk_protect: + assert(access_type != MMU_INST_FETCH); + error_code = PG_ERROR_PK_MASK | PG_ERROR_P_MASK; + goto do_fault_cont; do_fault: - error_code |= (is_write << PG_ERROR_W_BIT); - if (is_user) + error_code = 0; + do_fault_cont: + if (is_user) { error_code |= PG_ERROR_U_MASK; - if (is_write1 == 2 && - ((pg_mode & PG_MODE_NXE) || (pg_mode & PG_MODE_SMEP))) - error_code |= PG_ERROR_I_D_MASK; - return error_code; + } + switch (access_type) { + case MMU_DATA_LOAD: + break; + case MMU_DATA_STORE: + error_code |= PG_ERROR_W_MASK; + break; + case MMU_INST_FETCH: + if (pg_mode & (PG_MODE_NXE | PG_MODE_SMEP)) { + error_code |= PG_ERROR_I_D_MASK; + } + break; + } + *err = (TranslateFault){ + .exception_index = EXCP0E_PAGE, + .error_code = error_code, + .cr2 = addr, + }; + return false; } -hwaddr get_hphys(CPUState *cs, hwaddr gphys, MMUAccessType access_type, - int *prot) +static G_NORETURN void raise_stage2(CPUX86State *env, TranslateFault *err, + uintptr_t retaddr) { - CPUX86State *env = &X86_CPU(cs)->env; - uint64_t exit_info_1; - int page_size; - int next_prot; - hwaddr hphys; + uint64_t exit_info_1 = err->error_code; - if (likely(!(env->hflags2 & HF2_NPT_MASK))) { - return gphys; - } - - exit_info_1 = mmu_translate(cs, gphys, NULL, env->nested_cr3, - access_type, MMU_USER_IDX, env->nested_pg_mode, - &hphys, &page_size, &next_prot); - if (exit_info_1 == PG_ERROR_OK) { - if (prot) { - *prot &= next_prot; - } - return hphys; - } - - x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), - gphys); - if (prot) { - exit_info_1 |= SVM_NPTEXIT_GPA; - } else { /* page table access */ + switch (err->stage2) { + case S2_GPT: exit_info_1 |= SVM_NPTEXIT_GPT; + break; + case S2_GPA: + exit_info_1 |= SVM_NPTEXIT_GPA; + break; + default: + g_assert_not_reached(); } - cpu_vmexit(env, SVM_EXIT_NPF, exit_info_1, env->retaddr); + + x86_stq_phys(env_cpu(env), + env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), + err->cr2); + cpu_vmexit(env, SVM_EXIT_NPF, exit_info_1, retaddr); } -/* return value: - * -1 = cannot handle fault - * 0 = nothing more to do - * 1 = generate PF fault - */ -static int handle_mmu_fault(CPUState *cs, vaddr addr, int size, - int is_write1, int mmu_idx) +static bool get_physical_address(CPUX86State *env, vaddr addr, + MMUAccessType access_type, int mmu_idx, + TranslateResult *out, TranslateFault *err, + uint64_t ra) { - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - int error_code = PG_ERROR_OK; - int pg_mode, prot, page_size; - int32_t a20_mask; - hwaddr paddr; - hwaddr vaddr; + TranslateParams in; + bool use_stage2 = env->hflags2 & HF2_NPT_MASK; -#if defined(DEBUG_MMU) - printf("MMU fault: addr=%" VADDR_PRIx " w=%d mmu=%d eip=" TARGET_FMT_lx "\n", - addr, is_write1, mmu_idx, env->eip); -#endif + in.addr = addr; + in.access_type = access_type; - if (!(env->cr[0] & CR0_PG_MASK)) { - a20_mask = x86_get_a20_mask(env); - paddr = addr & a20_mask; -#ifdef TARGET_X86_64 - if (!(env->hflags & HF_LMA_MASK)) { - /* Without long mode we can only address 32bits in real mode */ - paddr = (uint32_t)paddr; - } -#endif - prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - page_size = 4096; - } else { - pg_mode = get_pg_mode(env); - if (pg_mode & PG_MODE_LMA) { - int32_t sext; + switch (mmu_idx) { + case MMU_PHYS_IDX: + break; - /* test virtual address sign extension */ - sext = (int64_t)addr >> (pg_mode & PG_MODE_LA57 ? 56 : 47); - if (sext != 0 && sext != -1) { - env->error_code = 0; - cs->exception_index = EXCP0D_GPF; - return 1; + case MMU_NESTED_IDX: + if (likely(use_stage2)) { + in.cr3 = env->nested_cr3; + in.pg_mode = env->nested_pg_mode; + in.mmu_idx = + env->nested_pg_mode & PG_MODE_LMA ? MMU_USER64_IDX : MMU_USER32_IDX; + in.ptw_idx = MMU_PHYS_IDX; + + if (!mmu_translate(env, &in, out, err, ra)) { + err->stage2 = S2_GPA; + return false; } + return true; + } + break; + + default: + if (is_mmu_index_32(mmu_idx)) { + addr = (uint32_t)addr; } - error_code = mmu_translate(cs, addr, get_hphys, env->cr[3], is_write1, - mmu_idx, pg_mode, - &paddr, &page_size, &prot); - } + if (likely(env->cr[0] & CR0_PG_MASK)) { + in.cr3 = env->cr[3]; + in.mmu_idx = mmu_idx; + in.ptw_idx = use_stage2 ? MMU_NESTED_IDX : MMU_PHYS_IDX; + in.pg_mode = get_pg_mode(env); - if (error_code == PG_ERROR_OK) { - /* Even if 4MB pages, we map only one 4KB page in the cache to - avoid filling it too fast */ - vaddr = addr & TARGET_PAGE_MASK; - paddr &= TARGET_PAGE_MASK; - - assert(prot & (1 << is_write1)); - tlb_set_page_with_attrs(cs, vaddr, paddr, cpu_get_mem_attrs(env), - prot, mmu_idx, page_size); - return 0; - } else { - if (env->intercept_exceptions & (1 << EXCP0E_PAGE)) { - /* cr2 is not modified in case of exceptions */ - x86_stq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), - addr); - } else { - env->cr[2] = addr; + if (in.pg_mode & PG_MODE_LMA) { + /* test virtual address sign extension */ + int shift = in.pg_mode & PG_MODE_LA57 ? 56 : 47; + int64_t sext = (int64_t)addr >> shift; + if (sext != 0 && sext != -1) { + *err = (TranslateFault){ + .exception_index = EXCP0D_GPF, + .cr2 = addr, + }; + return false; + } + } + return mmu_translate(env, &in, out, err, ra); } - env->error_code = error_code; - cs->exception_index = EXCP0E_PAGE; - return 1; + break; } + + /* No translation needed. */ + out->paddr = addr & x86_get_a20_mask(env); + out->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + out->page_size = TARGET_PAGE_SIZE; + return true; } bool x86_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; + CPUX86State *env = cpu_env(cs); + TranslateResult out; + TranslateFault err; - env->retaddr = retaddr; - if (handle_mmu_fault(cs, addr, size, access_type, mmu_idx)) { - /* FIXME: On error in get_hphys we have already jumped out. */ - g_assert(!probe); - raise_exception_err_ra(env, cs->exception_index, - env->error_code, retaddr); + if (get_physical_address(env, addr, access_type, mmu_idx, &out, &err, + retaddr)) { + /* + * Even if 4MB pages, we map only one 4KB page in the cache to + * avoid filling it too fast. + */ + assert(out.prot & (1 << access_type)); + tlb_set_page_with_attrs(cs, addr & TARGET_PAGE_MASK, + out.paddr & TARGET_PAGE_MASK, + cpu_get_mem_attrs(env), + out.prot, mmu_idx, out.page_size); + return true; } - return true; + + if (probe) { + /* This will be used if recursing for stage2 translation. */ + env->error_code = err.error_code; + return false; + } + + if (err.stage2 != S2_NONE) { + raise_stage2(env, &err, retaddr); + } + + if (env->intercept_exceptions & (1 << err.exception_index)) { + /* cr2 is not modified in case of exceptions */ + x86_stq_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, control.exit_info_2), + err.cr2); + } else { + env->cr[2] = err.cr2; + } + raise_exception_err_ra(env, err.exception_index, err.error_code, retaddr); +} + +G_NORETURN void x86_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) +{ + X86CPU *cpu = X86_CPU(cs); + handle_unaligned_access(&cpu->env, vaddr, access_type, retaddr); } diff --git a/target/i386/tcg/sysemu/fpu_helper.c b/target/i386/tcg/sysemu/fpu_helper.c index 1c3610da3b..e0305ba234 100644 --- a/target/i386/tcg/sysemu/fpu_helper.c +++ b/target/i386/tcg/sysemu/fpu_helper.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/main-loop.h" #include "cpu.h" #include "hw/irq.h" @@ -31,7 +32,9 @@ void x86_register_ferr_irq(qemu_irq irq) void fpu_check_raise_ferr_irq(CPUX86State *env) { if (ferr_irq && !(env->hflags2 & HF2_IGNNE_MASK)) { + bql_lock(); qemu_irq_raise(ferr_irq); + bql_unlock(); return; } } @@ -45,6 +48,9 @@ void cpu_clear_ignne(void) void cpu_set_ignne(void) { CPUX86State *env = &X86_CPU(first_cpu)->env; + + assert(bql_locked()); + env->hflags2 |= HF2_IGNNE_MASK; /* * We get here in response to a write to port F0h. The chipset should diff --git a/target/i386/tcg/sysemu/meson.build b/target/i386/tcg/sysemu/meson.build index 2e444e766a..f9ac254541 100644 --- a/target/i386/tcg/sysemu/meson.build +++ b/target/i386/tcg/sysemu/meson.build @@ -1,4 +1,4 @@ -i386_softmmu_ss.add(when: ['CONFIG_TCG', 'CONFIG_SOFTMMU'], if_true: files( +i386_system_ss.add(when: ['CONFIG_TCG', 'CONFIG_SYSTEM_ONLY'], if_true: files( 'tcg-cpu.c', 'smm_helper.c', 'excp_helper.c', diff --git a/target/i386/tcg/sysemu/misc_helper.c b/target/i386/tcg/sysemu/misc_helper.c index 1328aa656f..edb7c3d894 100644 --- a/target/i386/tcg/sysemu/misc_helper.c +++ b/target/i386/tcg/sysemu/misc_helper.c @@ -25,6 +25,7 @@ #include "exec/address-spaces.h" #include "exec/exec-all.h" #include "tcg/helper-tcg.h" +#include "hw/i386/apic.h" void helper_outb(CPUX86State *env, uint32_t port, uint32_t data) { @@ -118,9 +119,9 @@ void helper_write_crN(CPUX86State *env, int reg, target_ulong t0) break; case 8: if (!(env->hflags2 & HF2_VINTR_MASK)) { - qemu_mutex_lock_iothread(); + bql_lock(); cpu_set_apic_tpr(env_archcpu(env)->apic_state, t0); - qemu_mutex_unlock_iothread(); + bql_unlock(); } env->int_ctl = (env->int_ctl & ~V_TPR_MASK) | (t0 & V_TPR_MASK); @@ -157,9 +158,19 @@ void helper_wrmsr(CPUX86State *env) case MSR_IA32_SYSENTER_EIP: env->sysenter_eip = val; break; - case MSR_IA32_APICBASE: - cpu_set_apic_base(env_archcpu(env)->apic_state, val); + case MSR_IA32_APICBASE: { + int ret; + + if (val & MSR_IA32_APICBASE_RESERVED) { + goto error; + } + + ret = cpu_set_apic_base(env_archcpu(env)->apic_state, val); + if (ret < 0) { + goto error; + } break; + } case MSR_EFER: { uint64_t update_mask; @@ -201,6 +212,9 @@ void helper_wrmsr(CPUX86State *env) tlb_flush(cs); break; case MSR_VM_HSAVE_PA: + if (val & (0xfff | ((~0ULL) << env_archcpu(env)->phys_bits))) { + goto error; + } env->vm_hsave = val; break; #ifdef TARGET_X86_64 @@ -289,6 +303,19 @@ void helper_wrmsr(CPUX86State *env) env->msr_bndcfgs = val; cpu_sync_bndcs_hflags(env); break; + case MSR_APIC_START ... MSR_APIC_END: { + int ret; + int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; + + bql_lock(); + ret = apic_msr_write(index, val); + bql_unlock(); + if (ret < 0) { + goto error; + } + + break; + } default: if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL + @@ -450,6 +477,24 @@ void helper_rdmsr(CPUX86State *env) case MSR_IA32_UCODE_REV: val = x86_cpu->ucode_rev; break; + case MSR_CORE_THREAD_COUNT: { + CPUState *cs = CPU(x86_cpu); + val = (cs->nr_threads * cs->nr_cores) | (cs->nr_cores << 16); + break; + } + case MSR_APIC_START ... MSR_APIC_END: { + int ret; + int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; + + bql_lock(); + ret = apic_msr_read(index, &val); + bql_unlock(); + if (ret < 0) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + + 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/tcg/sysemu/seg_helper.c b/target/i386/tcg/sysemu/seg_helper.c index 2c9bd007ad..2db8083748 100644 --- a/target/i386/tcg/sysemu/seg_helper.c +++ b/target/i386/tcg/sysemu/seg_helper.c @@ -20,13 +20,13 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qemu/main-loop.h" #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #include "tcg/helper-tcg.h" #include "../seg_helper.h" -#ifdef TARGET_X86_64 void helper_syscall(CPUX86State *env, int next_eip_addend) { int selector; @@ -35,6 +35,7 @@ void helper_syscall(CPUX86State *env, int next_eip_addend) raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); } selector = (env->star >> 32) & 0xffff; +#ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { int code64; @@ -61,7 +62,9 @@ void helper_syscall(CPUX86State *env, int next_eip_addend) } else { env->eip = env->cstar; } - } else { + } else +#endif + { env->regs[R_ECX] = (uint32_t)(env->eip + next_eip_addend); env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK); @@ -78,7 +81,6 @@ void helper_syscall(CPUX86State *env, int next_eip_addend) env->eip = (uint32_t)env->star; } } -#endif /* TARGET_X86_64 */ void handle_even_inj(CPUX86State *env, int intno, int is_int, int error_code, int is_hw, int rm) @@ -126,6 +128,28 @@ void x86_cpu_do_interrupt(CPUState *cs) } } +void x86_cpu_exec_halt(CPUState *cpu) +{ + if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { + X86CPU *x86_cpu = X86_CPU(cpu); + + bql_lock(); + apic_poll_irq(x86_cpu->apic_state); + cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); + bql_unlock(); + } +} + +bool x86_need_replay_interrupt(int interrupt_request) +{ + /* + * CPU_INTERRUPT_POLL is a virtual event which gets converted into a + * "real" interrupt event later. It does not need to be recorded for + * replay purposes. + */ + return !(interrupt_request & CPU_INTERRUPT_POLL); +} + bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { X86CPU *cpu = X86_CPU(cs); diff --git a/target/i386/tcg/sysemu/svm_helper.c b/target/i386/tcg/sysemu/svm_helper.c index 2b6f450af9..5d6de2294f 100644 --- a/target/i386/tcg/sysemu/svm_helper.c +++ b/target/i386/tcg/sysemu/svm_helper.c @@ -27,19 +27,19 @@ /* Secure Virtual Machine helpers */ -static inline void svm_save_seg(CPUX86State *env, hwaddr addr, - const SegmentCache *sc) +static void svm_save_seg(CPUX86State *env, int mmu_idx, hwaddr addr, + const SegmentCache *sc) { - CPUState *cs = env_cpu(env); - - x86_stw_phys(cs, addr + offsetof(struct vmcb_seg, selector), - sc->selector); - x86_stq_phys(cs, addr + offsetof(struct vmcb_seg, base), - sc->base); - x86_stl_phys(cs, addr + offsetof(struct vmcb_seg, limit), - sc->limit); - x86_stw_phys(cs, addr + offsetof(struct vmcb_seg, attrib), - ((sc->flags >> 8) & 0xff) | ((sc->flags >> 12) & 0x0f00)); + cpu_stw_mmuidx_ra(env, addr + offsetof(struct vmcb_seg, selector), + sc->selector, mmu_idx, 0); + cpu_stq_mmuidx_ra(env, addr + offsetof(struct vmcb_seg, base), + sc->base, mmu_idx, 0); + cpu_stl_mmuidx_ra(env, addr + offsetof(struct vmcb_seg, limit), + sc->limit, mmu_idx, 0); + cpu_stw_mmuidx_ra(env, addr + offsetof(struct vmcb_seg, attrib), + ((sc->flags >> 8) & 0xff) + | ((sc->flags >> 12) & 0x0f00), + mmu_idx, 0); } /* @@ -52,29 +52,36 @@ static inline void svm_canonicalization(CPUX86State *env, target_ulong *seg_base *seg_base = ((((long) *seg_base) << shift_amt) >> shift_amt); } -static inline void svm_load_seg(CPUX86State *env, hwaddr addr, - SegmentCache *sc) +static void svm_load_seg(CPUX86State *env, int mmu_idx, hwaddr addr, + SegmentCache *sc) { - CPUState *cs = env_cpu(env); unsigned int flags; - sc->selector = x86_lduw_phys(cs, - addr + offsetof(struct vmcb_seg, selector)); - sc->base = x86_ldq_phys(cs, addr + offsetof(struct vmcb_seg, base)); - sc->limit = x86_ldl_phys(cs, addr + offsetof(struct vmcb_seg, limit)); - flags = x86_lduw_phys(cs, addr + offsetof(struct vmcb_seg, attrib)); + sc->selector = + cpu_lduw_mmuidx_ra(env, addr + offsetof(struct vmcb_seg, selector), + mmu_idx, 0); + sc->base = + cpu_ldq_mmuidx_ra(env, addr + offsetof(struct vmcb_seg, base), + mmu_idx, 0); + sc->limit = + cpu_ldl_mmuidx_ra(env, addr + offsetof(struct vmcb_seg, limit), + mmu_idx, 0); + flags = + cpu_lduw_mmuidx_ra(env, addr + offsetof(struct vmcb_seg, attrib), + mmu_idx, 0); sc->flags = ((flags & 0xff) << 8) | ((flags & 0x0f00) << 12); + svm_canonicalization(env, &sc->base); } -static inline void svm_load_seg_cache(CPUX86State *env, hwaddr addr, - int seg_reg) +static void svm_load_seg_cache(CPUX86State *env, int mmu_idx, + hwaddr addr, int seg_reg) { - SegmentCache sc1, *sc = &sc1; + SegmentCache sc; - svm_load_seg(env, addr, sc); - cpu_x86_load_seg_cache(env, seg_reg, sc->selector, - sc->base, sc->limit, sc->flags); + svm_load_seg(env, mmu_idx, addr, &sc); + cpu_x86_load_seg_cache(env, seg_reg, sc.selector, + sc.base, sc.limit, sc.flags); } static inline bool is_efer_invalid_state (CPUX86State *env) @@ -157,14 +164,19 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) uint64_t new_cr3; uint64_t new_cr4; - cpu_svm_check_intercept_param(env, SVM_EXIT_VMRUN, 0, GETPC()); - if (aflag == 2) { addr = env->regs[R_EAX]; } else { addr = (uint32_t)env->regs[R_EAX]; } + /* Exceptions are checked before the intercept. */ + if (addr & (0xfff | ((~0ULL) << env_archcpu(env)->phys_bits))) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + + cpu_svm_check_intercept_param(env, SVM_EXIT_VMRUN, 0, GETPC()); + qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmrun! " TARGET_FMT_lx "\n", addr); env->vm_vmcb = addr; @@ -199,13 +211,17 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) env->vm_hsave + offsetof(struct vmcb, save.rflags), cpu_compute_eflags(env)); - svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.es), + svm_save_seg(env, MMU_PHYS_IDX, + env->vm_hsave + offsetof(struct vmcb, save.es), &env->segs[R_ES]); - svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.cs), + svm_save_seg(env, MMU_PHYS_IDX, + env->vm_hsave + offsetof(struct vmcb, save.cs), &env->segs[R_CS]); - svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.ss), + svm_save_seg(env, MMU_PHYS_IDX, + env->vm_hsave + offsetof(struct vmcb, save.ss), &env->segs[R_SS]); - svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.ds), + svm_save_seg(env, MMU_PHYS_IDX, + env->vm_hsave + offsetof(struct vmcb, save.ds), &env->segs[R_DS]); x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rip), @@ -271,6 +287,8 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) env->hflags2 |= HF2_NPT_MASK; env->nested_pg_mode = get_pg_mode(env) & PG_MODE_SVM_MASK; + + tlb_flush_by_mmuidx(cs, 1 << MMU_NESTED_IDX); } /* enable intercepts */ @@ -323,18 +341,18 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) save.rflags)), ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); - svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.es), - R_ES); - svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.cs), - R_CS); - svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.ss), - R_SS); - svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.ds), - R_DS); - svm_load_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.idtr), - &env->idt); - svm_load_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.gdtr), - &env->gdt); + svm_load_seg_cache(env, MMU_PHYS_IDX, + env->vm_vmcb + offsetof(struct vmcb, save.es), R_ES); + svm_load_seg_cache(env, MMU_PHYS_IDX, + env->vm_vmcb + offsetof(struct vmcb, save.cs), R_CS); + svm_load_seg_cache(env, MMU_PHYS_IDX, + env->vm_vmcb + offsetof(struct vmcb, save.ss), R_SS); + svm_load_seg_cache(env, MMU_PHYS_IDX, + env->vm_vmcb + offsetof(struct vmcb, save.ds), R_DS); + svm_load_seg(env, MMU_PHYS_IDX, + env->vm_vmcb + offsetof(struct vmcb, save.idtr), &env->idt); + svm_load_seg(env, MMU_PHYS_IDX, + env->vm_vmcb + offsetof(struct vmcb, save.gdtr), &env->gdt); env->eip = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rip)); @@ -374,8 +392,6 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) env->hflags2 |= HF2_GIF_MASK; if (ctl_has_irq(env)) { - CPUState *cs = env_cpu(env); - cs->interrupt_request |= CPU_INTERRUPT_VIRQ; } @@ -449,11 +465,8 @@ void helper_vmmcall(CPUX86State *env) void helper_vmload(CPUX86State *env, int aflag) { - CPUState *cs = env_cpu(env); + int mmu_idx = MMU_PHYS_IDX; target_ulong addr; - int prot; - - cpu_svm_check_intercept_param(env, SVM_EXIT_VMLOAD, 0, GETPC()); if (aflag == 2) { addr = env->regs[R_EAX]; @@ -461,46 +474,60 @@ void helper_vmload(CPUX86State *env, int aflag) addr = (uint32_t)env->regs[R_EAX]; } - if (virtual_vm_load_save_enabled(env, SVM_EXIT_VMLOAD, GETPC())) { - addr = get_hphys(cs, addr, MMU_DATA_LOAD, &prot); + /* Exceptions are checked before the intercept. */ + if (addr & (0xfff | ((~0ULL) << env_archcpu(env)->phys_bits))) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } - qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmload! " TARGET_FMT_lx - "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n", - addr, x86_ldq_phys(cs, addr + offsetof(struct vmcb, - save.fs.base)), - env->segs[R_FS].base); + cpu_svm_check_intercept_param(env, SVM_EXIT_VMLOAD, 0, GETPC()); - svm_load_seg_cache(env, addr + offsetof(struct vmcb, save.fs), R_FS); - svm_load_seg_cache(env, addr + offsetof(struct vmcb, save.gs), R_GS); - svm_load_seg(env, addr + offsetof(struct vmcb, save.tr), &env->tr); - svm_load_seg(env, addr + offsetof(struct vmcb, save.ldtr), &env->ldt); + if (virtual_vm_load_save_enabled(env, SVM_EXIT_VMLOAD, GETPC())) { + mmu_idx = MMU_NESTED_IDX; + } + + svm_load_seg_cache(env, mmu_idx, + addr + offsetof(struct vmcb, save.fs), R_FS); + svm_load_seg_cache(env, mmu_idx, + addr + offsetof(struct vmcb, save.gs), R_GS); + svm_load_seg(env, mmu_idx, + addr + offsetof(struct vmcb, save.tr), &env->tr); + svm_load_seg(env, mmu_idx, + addr + offsetof(struct vmcb, save.ldtr), &env->ldt); #ifdef TARGET_X86_64 - env->kernelgsbase = x86_ldq_phys(cs, addr + offsetof(struct vmcb, - save.kernel_gs_base)); - env->lstar = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.lstar)); - env->cstar = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.cstar)); - env->fmask = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.sfmask)); + env->kernelgsbase = + cpu_ldq_mmuidx_ra(env, + addr + offsetof(struct vmcb, save.kernel_gs_base), + mmu_idx, 0); + env->lstar = + cpu_ldq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.lstar), + mmu_idx, 0); + env->cstar = + cpu_ldq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.cstar), + mmu_idx, 0); + env->fmask = + cpu_ldq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.sfmask), + mmu_idx, 0); svm_canonicalization(env, &env->kernelgsbase); #endif - env->star = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.star)); - env->sysenter_cs = x86_ldq_phys(cs, - addr + offsetof(struct vmcb, save.sysenter_cs)); - env->sysenter_esp = x86_ldq_phys(cs, addr + offsetof(struct vmcb, - save.sysenter_esp)); - env->sysenter_eip = x86_ldq_phys(cs, addr + offsetof(struct vmcb, - save.sysenter_eip)); - + env->star = + cpu_ldq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.star), + mmu_idx, 0); + env->sysenter_cs = + cpu_ldq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.sysenter_cs), + mmu_idx, 0); + env->sysenter_esp = + cpu_ldq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.sysenter_esp), + mmu_idx, 0); + env->sysenter_eip = + cpu_ldq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.sysenter_eip), + mmu_idx, 0); } void helper_vmsave(CPUX86State *env, int aflag) { - CPUState *cs = env_cpu(env); + int mmu_idx = MMU_PHYS_IDX; target_ulong addr; - int prot; - - cpu_svm_check_intercept_param(env, SVM_EXIT_VMSAVE, 0, GETPC()); if (aflag == 2) { addr = env->regs[R_EAX]; @@ -508,39 +535,44 @@ void helper_vmsave(CPUX86State *env, int aflag) addr = (uint32_t)env->regs[R_EAX]; } - if (virtual_vm_load_save_enabled(env, SVM_EXIT_VMSAVE, GETPC())) { - addr = get_hphys(cs, addr, MMU_DATA_STORE, &prot); + /* Exceptions are checked before the intercept. */ + if (addr & (0xfff | ((~0ULL) << env_archcpu(env)->phys_bits))) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } - qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmsave! " TARGET_FMT_lx - "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n", - addr, x86_ldq_phys(cs, - addr + offsetof(struct vmcb, save.fs.base)), - env->segs[R_FS].base); + cpu_svm_check_intercept_param(env, SVM_EXIT_VMSAVE, 0, GETPC()); - svm_save_seg(env, addr + offsetof(struct vmcb, save.fs), + if (virtual_vm_load_save_enabled(env, SVM_EXIT_VMSAVE, GETPC())) { + mmu_idx = MMU_NESTED_IDX; + } + + svm_save_seg(env, mmu_idx, addr + offsetof(struct vmcb, save.fs), &env->segs[R_FS]); - svm_save_seg(env, addr + offsetof(struct vmcb, save.gs), + svm_save_seg(env, mmu_idx, addr + offsetof(struct vmcb, save.gs), &env->segs[R_GS]); - svm_save_seg(env, addr + offsetof(struct vmcb, save.tr), + svm_save_seg(env, mmu_idx, addr + offsetof(struct vmcb, save.tr), &env->tr); - svm_save_seg(env, addr + offsetof(struct vmcb, save.ldtr), + svm_save_seg(env, mmu_idx, addr + offsetof(struct vmcb, save.ldtr), &env->ldt); #ifdef TARGET_X86_64 - x86_stq_phys(cs, addr + offsetof(struct vmcb, save.kernel_gs_base), - env->kernelgsbase); - x86_stq_phys(cs, addr + offsetof(struct vmcb, save.lstar), env->lstar); - x86_stq_phys(cs, addr + offsetof(struct vmcb, save.cstar), env->cstar); - x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sfmask), env->fmask); + cpu_stq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.kernel_gs_base), + env->kernelgsbase, mmu_idx, 0); + cpu_stq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.lstar), + env->lstar, mmu_idx, 0); + cpu_stq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.cstar), + env->cstar, mmu_idx, 0); + cpu_stq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.sfmask), + env->fmask, mmu_idx, 0); #endif - x86_stq_phys(cs, addr + offsetof(struct vmcb, save.star), env->star); - x86_stq_phys(cs, - addr + offsetof(struct vmcb, save.sysenter_cs), env->sysenter_cs); - x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_esp), - env->sysenter_esp); - x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_eip), - env->sysenter_eip); + cpu_stq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.star), + env->star, mmu_idx, 0); + cpu_stq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.sysenter_cs), + env->sysenter_cs, mmu_idx, 0); + cpu_stq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.sysenter_esp), + env->sysenter_esp, mmu_idx, 0); + cpu_stq_mmuidx_ra(env, addr + offsetof(struct vmcb, save.sysenter_eip), + env->sysenter_eip, mmu_idx, 0); } void helper_stgi(CPUX86State *env) @@ -685,7 +717,7 @@ void cpu_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1, { CPUState *cs = env_cpu(env); - cpu_restore_state(cs, retaddr, true); + cpu_restore_state(cs, retaddr); qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmexit(%08x, %016" PRIx64 ", %016" PRIx64 ", " TARGET_FMT_lx ")!\n", @@ -720,15 +752,20 @@ void do_vmexit(CPUX86State *env) env->vm_vmcb + offsetof(struct vmcb, control.int_state), 0); } env->hflags2 &= ~HF2_NPT_MASK; + tlb_flush_by_mmuidx(cs, 1 << MMU_NESTED_IDX); /* Save the VM state in the vmcb */ - svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.es), + svm_save_seg(env, MMU_PHYS_IDX, + env->vm_vmcb + offsetof(struct vmcb, save.es), &env->segs[R_ES]); - svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.cs), + svm_save_seg(env, MMU_PHYS_IDX, + env->vm_vmcb + offsetof(struct vmcb, save.cs), &env->segs[R_CS]); - svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.ss), + svm_save_seg(env, MMU_PHYS_IDX, + env->vm_vmcb + offsetof(struct vmcb, save.ss), &env->segs[R_SS]); - svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.ds), + svm_save_seg(env, MMU_PHYS_IDX, + env->vm_vmcb + offsetof(struct vmcb, save.ds), &env->segs[R_DS]); x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base), @@ -809,14 +846,14 @@ void do_vmexit(CPUX86State *env) ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK | VM_MASK)); - svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.es), - R_ES); - svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.cs), - R_CS); - svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.ss), - R_SS); - svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.ds), - R_DS); + svm_load_seg_cache(env, MMU_PHYS_IDX, + env->vm_hsave + offsetof(struct vmcb, save.es), R_ES); + svm_load_seg_cache(env, MMU_PHYS_IDX, + env->vm_hsave + offsetof(struct vmcb, save.cs), R_CS); + svm_load_seg_cache(env, MMU_PHYS_IDX, + env->vm_hsave + offsetof(struct vmcb, save.ss), R_SS); + svm_load_seg_cache(env, MMU_PHYS_IDX, + env->vm_hsave + offsetof(struct vmcb, save.ds), R_DS); env->eip = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rip)); diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index 6fdfdf9598..cca19cd40e 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -49,9 +49,48 @@ static void x86_cpu_exec_exit(CPUState *cs) static void x86_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - X86CPU *cpu = X86_CPU(cs); + /* The instruction pointer is always up to date with CF_PCREL. */ + if (!(tb_cflags(tb) & CF_PCREL)) { + CPUX86State *env = cpu_env(cs); - cpu->env.eip = tb->pc - tb->cs_base; + if (tb->flags & HF_CS64_MASK) { + env->eip = tb->pc; + } else { + env->eip = (uint32_t)(tb->pc - tb->cs_base); + } + } +} + +static void x86_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + int cc_op = data[1]; + uint64_t new_pc; + + if (tb_cflags(tb) & CF_PCREL) { + /* + * data[0] in PC-relative TBs is also a linear address, i.e. an address with + * the CS base added, because it is not guaranteed that EIP bits 12 and higher + * stay the same across the translation block. Add the CS base back before + * replacing the low bits, and subtract it below just like for !CF_PCREL. + */ + uint64_t pc = env->eip + tb->cs_base; + new_pc = (pc & TARGET_PAGE_MASK) | data[0]; + } else { + new_pc = data[0]; + } + if (tb->flags & HF_CS64_MASK) { + env->eip = new_pc; + } else { + env->eip = (uint32_t)(new_pc - tb->cs_base); + } + + if (cc_op != CC_OP_DYNAMIC) { + env->cc_op = cc_op; + } } #ifndef CONFIG_USER_ONLY @@ -67,35 +106,40 @@ static bool x86_debug_check_breakpoint(CPUState *cs) #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps x86_tcg_ops = { +static const TCGCPUOps x86_tcg_ops = { .initialize = tcg_x86_init, .synchronize_from_tb = x86_cpu_synchronize_from_tb, + .restore_state_to_opc = x86_restore_state_to_opc, .cpu_exec_enter = x86_cpu_exec_enter, .cpu_exec_exit = x86_cpu_exec_exit, #ifdef CONFIG_USER_ONLY .fake_user_interrupt = x86_cpu_do_interrupt, .record_sigsegv = x86_cpu_record_sigsegv, + .record_sigbus = x86_cpu_record_sigbus, #else .tlb_fill = x86_cpu_tlb_fill, .do_interrupt = x86_cpu_do_interrupt, + .cpu_exec_halt = x86_cpu_exec_halt, .cpu_exec_interrupt = x86_cpu_exec_interrupt, + .do_unaligned_access = x86_cpu_do_unaligned_access, .debug_excp_handler = breakpoint_handler, .debug_check_breakpoint = x86_debug_check_breakpoint, + .need_replay_interrupt = x86_need_replay_interrupt, #endif /* !CONFIG_USER_ONLY */ }; -static void tcg_cpu_init_ops(AccelCPUClass *accel_cpu, CPUClass *cc) +static void x86_tcg_cpu_init_ops(AccelCPUClass *accel_cpu, CPUClass *cc) { /* for x86, all cpus use the same set of operations */ cc->tcg_ops = &x86_tcg_ops; } -static void tcg_cpu_class_init(CPUClass *cc) +static void x86_tcg_cpu_class_init(CPUClass *cc) { - cc->init_accel_cpu = tcg_cpu_init_ops; + cc->init_accel_cpu = x86_tcg_cpu_init_ops; } -static void tcg_cpu_xsave_init(void) +static void x86_tcg_cpu_xsave_init(void) { #define XO(bit, field) \ x86_ext_save_areas[bit].offset = offsetof(X86XSaveArea, field); @@ -117,44 +161,44 @@ static void tcg_cpu_xsave_init(void) * TCG-specific defaults that override cpudef models when using TCG. * Only for builtin_x86_defs models initialized with x86_register_cpudef_types. */ -static PropValue tcg_default_props[] = { +static PropValue x86_tcg_default_props[] = { { "vme", "off" }, { NULL, NULL }, }; -static void tcg_cpu_instance_init(CPUState *cs) +static void x86_tcg_cpu_instance_init(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); if (xcc->model) { /* Special cases not set in the X86CPUDefinition structs: */ - x86_cpu_apply_props(cpu, tcg_default_props); + x86_cpu_apply_props(cpu, x86_tcg_default_props); } - tcg_cpu_xsave_init(); + x86_tcg_cpu_xsave_init(); } -static void tcg_cpu_accel_class_init(ObjectClass *oc, void *data) +static void x86_tcg_cpu_accel_class_init(ObjectClass *oc, void *data) { AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); #ifndef CONFIG_USER_ONLY - acc->cpu_realizefn = tcg_cpu_realizefn; + acc->cpu_target_realize = tcg_cpu_realizefn; #endif /* CONFIG_USER_ONLY */ - acc->cpu_class_init = tcg_cpu_class_init; - acc->cpu_instance_init = tcg_cpu_instance_init; + acc->cpu_class_init = x86_tcg_cpu_class_init; + acc->cpu_instance_init = x86_tcg_cpu_instance_init; } -static const TypeInfo tcg_cpu_accel_type_info = { +static const TypeInfo x86_tcg_cpu_accel_type_info = { .name = ACCEL_CPU_NAME("tcg"), .parent = TYPE_ACCEL_CPU, - .class_init = tcg_cpu_accel_class_init, + .class_init = x86_tcg_cpu_accel_class_init, .abstract = true, }; -static void tcg_cpu_accel_register_types(void) +static void x86_tcg_cpu_accel_register_types(void) { - type_register_static(&tcg_cpu_accel_type_info); + type_register_static(&x86_tcg_cpu_accel_type_info); } -type_init(tcg_cpu_accel_register_types); +type_init(x86_tcg_cpu_accel_register_types); diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index b7972f0ff5..76a42c679c 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -23,8 +23,10 @@ #include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" #include "exec/cpu_ldst.h" #include "exec/translator.h" +#include "fpu/softfloat.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -32,6 +34,11 @@ #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + + #define PREFIX_REPZ 0x01 #define PREFIX_REPNZ 0x02 #define PREFIX_LOCK 0x04 @@ -64,20 +71,19 @@ /* global register indexes */ static TCGv cpu_cc_dst, cpu_cc_src, cpu_cc_src2; +static TCGv cpu_eip; static TCGv_i32 cpu_cc_op; static TCGv cpu_regs[CPU_NB_REGS]; static TCGv cpu_seg_base[6]; static TCGv_i64 cpu_bndl[4]; static TCGv_i64 cpu_bndu[4]; -#include "exec/gen-icount.h" - typedef struct DisasContext { DisasContextBase base; target_ulong pc; /* pc = eip + cs_base */ - target_ulong pc_start; /* pc at TB entry */ target_ulong cs_base; /* base of CS segment */ + target_ulong pc_save; MemOp aflag; MemOp dflag; @@ -85,6 +91,9 @@ typedef struct DisasContext { int8_t override; /* -1 if no override, else R_CS, R_DS, etc */ uint8_t prefix; + bool has_modrm; + uint8_t modrm; + #ifndef CONFIG_USER_ONLY uint8_t cpl; /* code priv level */ uint8_t iopl; /* i/o priv level */ @@ -98,8 +107,8 @@ typedef struct DisasContext { uint8_t rex_r; uint8_t rex_x; uint8_t rex_b; - bool rex_w; #endif + bool vex_w; /* used by AVX even on 32-bit processors */ bool jmp_opt; /* use direct block chaining for direct jumps */ bool repz_opt; /* optimize jumps within repz instructions */ bool cc_op_dirty; @@ -112,6 +121,8 @@ typedef struct DisasContext { int cpuid_ext2_features; int cpuid_ext3_features; int cpuid_7_0_ebx_features; + int cpuid_7_0_ecx_features; + int cpuid_7_1_eax_features; int cpuid_xsave_features; /* TCG local temps */ @@ -123,15 +134,20 @@ typedef struct DisasContext { /* TCG local register indexes (only used inside old micro ops) */ TCGv tmp0; TCGv tmp4; - TCGv_ptr ptr0; - TCGv_ptr ptr1; TCGv_i32 tmp2_i32; TCGv_i32 tmp3_i32; TCGv_i64 tmp1_i64; sigjmp_buf jmpbuf; + TCGOp *prev_insn_start; + TCGOp *prev_insn_end; } DisasContext; +#define DISAS_EOB_ONLY DISAS_TARGET_0 +#define DISAS_EOB_NEXT DISAS_TARGET_1 +#define DISAS_EOB_INHIBIT_IRQ DISAS_TARGET_2 +#define DISAS_JUMP DISAS_TARGET_3 + /* The environment in which user-only runs is constrained. */ #ifdef CONFIG_USER_ONLY #define PE(S) true @@ -159,18 +175,20 @@ typedef struct DisasContext { #endif #if !defined(TARGET_X86_64) #define CODE64(S) false -#define LMA(S) false #elif defined(CONFIG_USER_ONLY) #define CODE64(S) true -#define LMA(S) true #else #define CODE64(S) (((S)->flags & HF_CS64_MASK) != 0) +#endif +#if defined(CONFIG_USER_ONLY) || defined(TARGET_X86_64) #define LMA(S) (((S)->flags & HF_LMA_MASK) != 0) +#else +#define LMA(S) false #endif #ifdef TARGET_X86_64 #define REX_PREFIX(S) (((S)->prefix & PREFIX_REX) != 0) -#define REX_W(S) ((S)->rex_w) +#define REX_W(S) ((S)->vex_w) #define REX_R(S) ((S)->rex_r + 0) #define REX_X(S) ((S)->rex_x + 0) #define REX_B(S) ((S)->rex_b + 0) @@ -218,9 +236,9 @@ STUB_HELPER(wrmsr, TCGv_env env) #endif static void gen_eob(DisasContext *s); -static void gen_jr(DisasContext *s, TCGv dest); -static void gen_jmp(DisasContext *s, target_ulong eip); -static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num); +static void gen_jr(DisasContext *s); +static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num); +static void gen_jmp_rel_csize(DisasContext *s, int diff, int tb_num); static void gen_op(DisasContext *s1, int op, MemOp ot, int d); static void gen_exception_gpf(DisasContext *s); @@ -428,32 +446,51 @@ static inline MemOp mo_b_d32(int b, MemOp ot) return b & 1 ? (ot == MO_16 ? MO_16 : MO_32) : MO_8; } -static void gen_op_mov_reg_v(DisasContext *s, MemOp ot, int reg, TCGv t0) +/* Compute the result of writing t0 to the OT-sized register REG. + * + * If DEST is NULL, store the result into the register and return the + * register's TCGv. + * + * If DEST is not NULL, store the result into DEST and return the + * register's TCGv. + */ +static TCGv gen_op_deposit_reg_v(DisasContext *s, MemOp ot, int reg, TCGv dest, TCGv t0) { switch(ot) { case MO_8: - if (!byte_reg_is_xH(s, reg)) { - tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 8); - } else { - tcg_gen_deposit_tl(cpu_regs[reg - 4], cpu_regs[reg - 4], t0, 8, 8); + if (byte_reg_is_xH(s, reg)) { + dest = dest ? dest : cpu_regs[reg - 4]; + tcg_gen_deposit_tl(dest, cpu_regs[reg - 4], t0, 8, 8); + return cpu_regs[reg - 4]; } + dest = dest ? dest : cpu_regs[reg]; + tcg_gen_deposit_tl(dest, cpu_regs[reg], t0, 0, 8); break; case MO_16: - tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 16); + dest = dest ? dest : cpu_regs[reg]; + tcg_gen_deposit_tl(dest, cpu_regs[reg], t0, 0, 16); break; case MO_32: /* For x86_64, this sets the higher half of register to zero. For i386, this is equivalent to a mov. */ - tcg_gen_ext32u_tl(cpu_regs[reg], t0); + dest = dest ? dest : cpu_regs[reg]; + tcg_gen_ext32u_tl(dest, t0); break; #ifdef TARGET_X86_64 case MO_64: - tcg_gen_mov_tl(cpu_regs[reg], t0); + dest = dest ? dest : cpu_regs[reg]; + tcg_gen_mov_tl(dest, t0); break; #endif default: - tcg_abort(); + g_assert_not_reached(); } + return cpu_regs[reg]; +} + +static void gen_op_mov_reg_v(DisasContext *s, MemOp ot, int reg, TCGv t0) +{ + gen_op_deposit_reg_v(s, ot, reg, NULL, t0); } static inline @@ -474,9 +511,10 @@ static void gen_add_A0_im(DisasContext *s, int val) } } -static inline void gen_op_jmp_v(TCGv dest) +static inline void gen_op_jmp_v(DisasContext *s, TCGv dest) { - tcg_gen_st_tl(dest, cpu_env, offsetof(CPUX86State, eip)); + tcg_gen_mov_tl(cpu_eip, dest); + s->pc_save = -1; } static inline @@ -486,9 +524,9 @@ void gen_op_add_reg_im(DisasContext *s, MemOp size, int reg, int32_t val) gen_op_mov_reg_v(s, size, reg, s->tmp0); } -static inline void gen_op_add_reg_T0(DisasContext *s, MemOp size, int reg) +static inline void gen_op_add_reg(DisasContext *s, MemOp size, int reg, TCGv val) { - tcg_gen_add_tl(s->tmp0, cpu_regs[reg], s->T0); + tcg_gen_add_tl(s->tmp0, cpu_regs[reg], val); gen_op_mov_reg_v(s, size, reg, s->tmp0); } @@ -511,23 +549,105 @@ static inline void gen_op_st_rm_T0_A0(DisasContext *s, int idx, int d) } } -static inline void gen_jmp_im(DisasContext *s, target_ulong pc) +static void gen_update_eip_cur(DisasContext *s) { - tcg_gen_movi_tl(s->tmp0, pc); - gen_op_jmp_v(s->tmp0); + assert(s->pc_save != -1); + if (tb_cflags(s->base.tb) & CF_PCREL) { + tcg_gen_addi_tl(cpu_eip, cpu_eip, s->base.pc_next - s->pc_save); + } else if (CODE64(s)) { + tcg_gen_movi_tl(cpu_eip, s->base.pc_next); + } else { + tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->base.pc_next - s->cs_base)); + } + s->pc_save = s->base.pc_next; } -/* Compute SEG:REG into A0. SEG is selected from the override segment +static void gen_update_eip_next(DisasContext *s) +{ + assert(s->pc_save != -1); + if (tb_cflags(s->base.tb) & CF_PCREL) { + tcg_gen_addi_tl(cpu_eip, cpu_eip, s->pc - s->pc_save); + } else if (CODE64(s)) { + tcg_gen_movi_tl(cpu_eip, s->pc); + } else { + tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->pc - s->cs_base)); + } + s->pc_save = s->pc; +} + +static int cur_insn_len(DisasContext *s) +{ + return s->pc - s->base.pc_next; +} + +static TCGv_i32 cur_insn_len_i32(DisasContext *s) +{ + return tcg_constant_i32(cur_insn_len(s)); +} + +static TCGv_i32 eip_next_i32(DisasContext *s) +{ + assert(s->pc_save != -1); + /* + * This function has two users: lcall_real (always 16-bit mode), and + * iret_protected (16, 32, or 64-bit mode). IRET only uses the value + * when EFLAGS.NT is set, which is illegal in 64-bit mode, which is + * why passing a 32-bit value isn't broken. To avoid using this where + * we shouldn't, return -1 in 64-bit mode so that execution goes into + * the weeds quickly. + */ + if (CODE64(s)) { + return tcg_constant_i32(-1); + } + if (tb_cflags(s->base.tb) & CF_PCREL) { + TCGv_i32 ret = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(ret, cpu_eip); + tcg_gen_addi_i32(ret, ret, s->pc - s->pc_save); + return ret; + } else { + return tcg_constant_i32(s->pc - s->cs_base); + } +} + +static TCGv eip_next_tl(DisasContext *s) +{ + assert(s->pc_save != -1); + if (tb_cflags(s->base.tb) & CF_PCREL) { + TCGv ret = tcg_temp_new(); + tcg_gen_addi_tl(ret, cpu_eip, s->pc - s->pc_save); + return ret; + } else if (CODE64(s)) { + return tcg_constant_tl(s->pc); + } else { + return tcg_constant_tl((uint32_t)(s->pc - s->cs_base)); + } +} + +static TCGv eip_cur_tl(DisasContext *s) +{ + assert(s->pc_save != -1); + if (tb_cflags(s->base.tb) & CF_PCREL) { + TCGv ret = tcg_temp_new(); + tcg_gen_addi_tl(ret, cpu_eip, s->base.pc_next - s->pc_save); + return ret; + } else if (CODE64(s)) { + return tcg_constant_tl(s->base.pc_next); + } else { + return tcg_constant_tl((uint32_t)(s->base.pc_next - s->cs_base)); + } +} + +/* Compute SEG:REG into DEST. SEG is selected from the override segment (OVR_SEG) and the default segment (DEF_SEG). OVR_SEG may be -1 to indicate no override. */ -static void gen_lea_v_seg(DisasContext *s, MemOp aflag, TCGv a0, - int def_seg, int ovr_seg) +static void gen_lea_v_seg_dest(DisasContext *s, MemOp aflag, TCGv dest, TCGv a0, + int def_seg, int ovr_seg) { switch (aflag) { #ifdef TARGET_X86_64 case MO_64: if (ovr_seg < 0) { - tcg_gen_mov_tl(s->A0, a0); + tcg_gen_mov_tl(dest, a0); return; } break; @@ -538,14 +658,14 @@ static void gen_lea_v_seg(DisasContext *s, MemOp aflag, TCGv a0, ovr_seg = def_seg; } if (ovr_seg < 0) { - tcg_gen_ext32u_tl(s->A0, a0); + tcg_gen_ext32u_tl(dest, a0); return; } break; case MO_16: /* 16 bit address */ - tcg_gen_ext16u_tl(s->A0, a0); - a0 = s->A0; + tcg_gen_ext16u_tl(dest, a0); + a0 = dest; if (ovr_seg < 0) { if (ADDSEG(s)) { ovr_seg = def_seg; @@ -555,24 +675,30 @@ static void gen_lea_v_seg(DisasContext *s, MemOp aflag, TCGv a0, } break; default: - tcg_abort(); + g_assert_not_reached(); } if (ovr_seg >= 0) { TCGv seg = cpu_seg_base[ovr_seg]; if (aflag == MO_64) { - tcg_gen_add_tl(s->A0, a0, seg); + tcg_gen_add_tl(dest, a0, seg); } else if (CODE64(s)) { - tcg_gen_ext32u_tl(s->A0, a0); - tcg_gen_add_tl(s->A0, s->A0, seg); + tcg_gen_ext32u_tl(dest, a0); + tcg_gen_add_tl(dest, dest, seg); } else { - tcg_gen_add_tl(s->A0, a0, seg); - tcg_gen_ext32u_tl(s->A0, s->A0); + tcg_gen_add_tl(dest, a0, seg); + tcg_gen_ext32u_tl(dest, dest); } } } +static void gen_lea_v_seg(DisasContext *s, MemOp aflag, TCGv a0, + int def_seg, int ovr_seg) +{ + gen_lea_v_seg_dest(s, aflag, s->A0, a0, def_seg, ovr_seg); +} + static inline void gen_string_movl_A0_ESI(DisasContext *s) { gen_lea_v_seg(s, s->aflag, cpu_regs[R_ESI], R_DS, s->override); @@ -583,41 +709,24 @@ static inline void gen_string_movl_A0_EDI(DisasContext *s) gen_lea_v_seg(s, s->aflag, cpu_regs[R_EDI], R_ES, -1); } -static inline void gen_op_movl_T0_Dshift(DisasContext *s, MemOp ot) +static inline TCGv gen_compute_Dshift(DisasContext *s, MemOp ot) { - tcg_gen_ld32s_tl(s->T0, cpu_env, offsetof(CPUX86State, df)); - tcg_gen_shli_tl(s->T0, s->T0, ot); + TCGv dshift = tcg_temp_new(); + tcg_gen_ld32s_tl(dshift, tcg_env, offsetof(CPUX86State, df)); + tcg_gen_shli_tl(dshift, dshift, ot); + return dshift; }; static TCGv gen_ext_tl(TCGv dst, TCGv src, MemOp size, bool sign) { - switch (size) { - case MO_8: - if (sign) { - tcg_gen_ext8s_tl(dst, src); - } else { - tcg_gen_ext8u_tl(dst, src); - } - return dst; - case MO_16: - if (sign) { - tcg_gen_ext16s_tl(dst, src); - } else { - tcg_gen_ext16u_tl(dst, src); - } - return dst; -#ifdef TARGET_X86_64 - case MO_32: - if (sign) { - tcg_gen_ext32s_tl(dst, src); - } else { - tcg_gen_ext32u_tl(dst, src); - } - return dst; -#endif - default: + if (size == MO_TL) { return src; } + if (!dst) { + dst = tcg_temp_new(); + } + tcg_gen_ext_tl(dst, src, size | (sign ? MO_SIGN : 0)); + return dst; } static void gen_extu(MemOp ot, TCGv reg) @@ -630,36 +739,37 @@ static void gen_exts(MemOp ot, TCGv reg) gen_ext_tl(reg, reg, ot, true); } -static inline -void gen_op_jnz_ecx(DisasContext *s, MemOp size, TCGLabel *label1) +static void gen_op_j_ecx(DisasContext *s, TCGCond cond, TCGLabel *label1) { - tcg_gen_mov_tl(s->tmp0, cpu_regs[R_ECX]); - gen_extu(size, s->tmp0); - tcg_gen_brcondi_tl(TCG_COND_NE, s->tmp0, 0, label1); + TCGv tmp = gen_ext_tl(NULL, cpu_regs[R_ECX], s->aflag, false); + + tcg_gen_brcondi_tl(cond, tmp, 0, label1); } -static inline -void gen_op_jz_ecx(DisasContext *s, MemOp size, TCGLabel *label1) +static inline void gen_op_jz_ecx(DisasContext *s, TCGLabel *label1) { - tcg_gen_mov_tl(s->tmp0, cpu_regs[R_ECX]); - gen_extu(size, s->tmp0); - tcg_gen_brcondi_tl(TCG_COND_EQ, s->tmp0, 0, label1); + gen_op_j_ecx(s, TCG_COND_EQ, label1); +} + +static inline void gen_op_jnz_ecx(DisasContext *s, TCGLabel *label1) +{ + gen_op_j_ecx(s, TCG_COND_NE, label1); } static void gen_helper_in_func(MemOp ot, TCGv v, TCGv_i32 n) { switch (ot) { case MO_8: - gen_helper_inb(v, cpu_env, n); + gen_helper_inb(v, tcg_env, n); break; case MO_16: - gen_helper_inw(v, cpu_env, n); + gen_helper_inw(v, tcg_env, n); break; case MO_32: - gen_helper_inl(v, cpu_env, n); + gen_helper_inl(v, tcg_env, n); break; default: - tcg_abort(); + g_assert_not_reached(); } } @@ -667,16 +777,16 @@ static void gen_helper_out_func(MemOp ot, TCGv_i32 v, TCGv_i32 n) { switch (ot) { case MO_8: - gen_helper_outb(cpu_env, v, n); + gen_helper_outb(tcg_env, v, n); break; case MO_16: - gen_helper_outw(cpu_env, v, n); + gen_helper_outw(tcg_env, v, n); break; case MO_32: - gen_helper_outl(cpu_env, v, n); + gen_helper_outl(tcg_env, v, n); break; default: - tcg_abort(); + g_assert_not_reached(); } } @@ -696,35 +806,35 @@ static bool gen_check_io(DisasContext *s, MemOp ot, TCGv_i32 port, return false; #else if (PE(s) && (CPL(s) > IOPL(s) || VM86(s))) { - gen_helper_check_io(cpu_env, port, tcg_constant_i32(1 << ot)); + gen_helper_check_io(tcg_env, port, tcg_constant_i32(1 << ot)); } if (GUEST(s)) { - target_ulong cur_eip = s->base.pc_next - s->cs_base; - target_ulong next_eip = s->pc - s->cs_base; - gen_update_cc_op(s); - gen_jmp_im(s, cur_eip); + gen_update_eip_cur(s); if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { svm_flags |= SVM_IOIO_REP_MASK; } svm_flags |= 1 << (SVM_IOIO_SIZE_SHIFT + ot); - gen_helper_svm_check_io(cpu_env, port, + gen_helper_svm_check_io(tcg_env, port, tcg_constant_i32(svm_flags), - tcg_constant_i32(next_eip - cur_eip)); + cur_insn_len_i32(s)); } return true; #endif } -static inline void gen_movs(DisasContext *s, MemOp ot) +static void gen_movs(DisasContext *s, MemOp ot) { + TCGv dshift; + gen_string_movl_A0_ESI(s); gen_op_ld_v(s, ot, s->T0, s->A0); gen_string_movl_A0_EDI(s); gen_op_st_v(s, ot, s->T0, s->A0); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_ESI); - gen_op_add_reg_T0(s, s->aflag, R_EDI); + + dshift = gen_compute_Dshift(s, ot); + gen_op_add_reg(s, s->aflag, R_ESI, dshift); + gen_op_add_reg(s, s->aflag, R_EDI, dshift); } static void gen_op_update1_cc(DisasContext *s) @@ -757,22 +867,22 @@ static void gen_op_update_neg_cc(DisasContext *s) tcg_gen_movi_tl(s->cc_srcT, 0); } -/* compute all eflags to cc_src */ -static void gen_compute_eflags(DisasContext *s) +/* compute all eflags to reg */ +static void gen_mov_eflags(DisasContext *s, TCGv reg) { - TCGv zero, dst, src1, src2; + TCGv dst, src1, src2; + TCGv_i32 cc_op; int live, dead; if (s->cc_op == CC_OP_EFLAGS) { + tcg_gen_mov_tl(reg, cpu_cc_src); return; } if (s->cc_op == CC_OP_CLR) { - tcg_gen_movi_tl(cpu_cc_src, CC_Z | CC_P); - set_cc_op(s, CC_OP_EFLAGS); + tcg_gen_movi_tl(reg, CC_Z | CC_P); return; } - zero = NULL; dst = cpu_cc_dst; src1 = cpu_cc_src; src2 = cpu_cc_src2; @@ -781,7 +891,7 @@ static void gen_compute_eflags(DisasContext *s) live = cc_op_live[s->cc_op] & ~USES_CC_SRCT; dead = live ^ (USES_CC_DST | USES_CC_SRC | USES_CC_SRC2); if (dead) { - zero = tcg_const_tl(0); + TCGv zero = tcg_constant_tl(0); if (dead & USES_CC_DST) { dst = zero; } @@ -793,13 +903,19 @@ static void gen_compute_eflags(DisasContext *s) } } - gen_update_cc_op(s); - gen_helper_cc_compute_all(cpu_cc_src, dst, src1, src2, cpu_cc_op); - set_cc_op(s, CC_OP_EFLAGS); - - if (dead) { - tcg_temp_free(zero); + if (s->cc_op != CC_OP_DYNAMIC) { + cc_op = tcg_constant_i32(s->cc_op); + } else { + cc_op = cpu_cc_op; } + gen_helper_cc_compute_all(reg, dst, src1, src2, cc_op); +} + +/* compute all eflags to cc_src */ +static void gen_compute_eflags(DisasContext *s) +{ + gen_mov_eflags(s, cpu_cc_src); + set_cc_op(s, CC_OP_EFLAGS); } typedef struct CCPrepare { @@ -930,6 +1046,9 @@ static CCPrepare gen_prepare_eflags_o(DisasContext *s, TCGv reg) case CC_OP_CLR: case CC_OP_POPCNT: return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; + case CC_OP_MULB ... CC_OP_MULQ: + return (CCPrepare) { .cond = TCG_COND_NE, + .reg = cpu_cc_src, .mask = -1 }; default: gen_compute_eflags(s); return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, @@ -965,7 +1084,7 @@ static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg) } /* perform a conditional store into register 'reg' according to jump opcode - value 'b'. In the fast case, T0 is guaranted not to be used. */ + value 'b'. In the fast case, T0 is guaranteed not to be used. */ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) { int inv, jcc_op, cond; @@ -1036,10 +1155,9 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) if (reg == cpu_cc_src) { reg = s->tmp0; } - tcg_gen_shri_tl(reg, cpu_cc_src, 4); /* CC_O -> CC_S */ - tcg_gen_xor_tl(reg, reg, cpu_cc_src); + tcg_gen_addi_tl(reg, cpu_cc_src, CC_O - CC_S); cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, - .mask = CC_S }; + .mask = CC_O }; break; default: case JCC_LE: @@ -1047,10 +1165,9 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) if (reg == cpu_cc_src) { reg = s->tmp0; } - tcg_gen_shri_tl(reg, cpu_cc_src, 4); /* CC_O -> CC_S */ - tcg_gen_xor_tl(reg, reg, cpu_cc_src); + tcg_gen_addi_tl(reg, cpu_cc_src, CC_O - CC_S); cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, - .mask = CC_S | CC_Z }; + .mask = CC_O | CC_Z }; break; } break; @@ -1098,7 +1215,7 @@ static inline void gen_compute_eflags_c(DisasContext *s, TCGv reg) } /* generate a conditional jump to label 'l1' according to jump opcode - value 'b'. In the fast case, T0 is guaranted not to be used. */ + value 'b'. In the fast case, T0 is guaranteed not to be used. */ static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) { CCPrepare cc = gen_prepare_cc(s, b, s->T0); @@ -1115,7 +1232,7 @@ static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) } /* Generate a conditional jump to label 'l1' according to jump opcode - value 'b'. In the fast case, T0 is guaranted not to be used. + value 'b'. In the fast case, T0 is guaranteed not to be used. A translation block must end soon. */ static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) { @@ -1136,53 +1253,56 @@ static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) /* XXX: does not work with gdbstub "ice" single step - not a serious problem */ -static TCGLabel *gen_jz_ecx_string(DisasContext *s, target_ulong next_eip) +static TCGLabel *gen_jz_ecx_string(DisasContext *s) { TCGLabel *l1 = gen_new_label(); TCGLabel *l2 = gen_new_label(); - gen_op_jnz_ecx(s, s->aflag, l1); + gen_op_jnz_ecx(s, l1); gen_set_label(l2); - gen_jmp_tb(s, next_eip, 1); + gen_jmp_rel_csize(s, 0, 1); gen_set_label(l1); return l2; } -static inline void gen_stos(DisasContext *s, MemOp ot) +static void gen_stos(DisasContext *s, MemOp ot) { - gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); gen_string_movl_A0_EDI(s); gen_op_st_v(s, ot, s->T0, s->A0); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_EDI); + gen_op_add_reg(s, s->aflag, R_EDI, gen_compute_Dshift(s, ot)); } -static inline void gen_lods(DisasContext *s, MemOp ot) +static void gen_lods(DisasContext *s, MemOp ot) { gen_string_movl_A0_ESI(s); gen_op_ld_v(s, ot, s->T0, s->A0); gen_op_mov_reg_v(s, ot, R_EAX, s->T0); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_ESI); + gen_op_add_reg(s, s->aflag, R_ESI, gen_compute_Dshift(s, ot)); } -static inline void gen_scas(DisasContext *s, MemOp ot) +static void gen_scas(DisasContext *s, MemOp ot) { gen_string_movl_A0_EDI(s); gen_op_ld_v(s, ot, s->T1, s->A0); - gen_op(s, OP_CMPL, ot, R_EAX); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_EDI); + tcg_gen_mov_tl(cpu_cc_src, s->T1); + tcg_gen_mov_tl(s->cc_srcT, s->T0); + tcg_gen_sub_tl(cpu_cc_dst, s->T0, s->T1); + set_cc_op(s, CC_OP_SUBB + ot); + + gen_op_add_reg(s, s->aflag, R_EDI, gen_compute_Dshift(s, ot)); } -static inline void gen_cmps(DisasContext *s, MemOp ot) +static void gen_cmps(DisasContext *s, MemOp ot) { + TCGv dshift; + gen_string_movl_A0_EDI(s); gen_op_ld_v(s, ot, s->T1, s->A0); gen_string_movl_A0_ESI(s); gen_op(s, OP_CMPL, ot, OR_TMP0); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_ESI); - gen_op_add_reg_T0(s, s->aflag, R_EDI); + + dshift = gen_compute_Dshift(s, ot); + gen_op_add_reg(s, s->aflag, R_ESI, dshift); + gen_op_add_reg(s, s->aflag, R_EDI, dshift); } static void gen_bpt_io(DisasContext *s, TCGv_i32 t_port, int ot) @@ -1192,17 +1312,14 @@ static void gen_bpt_io(DisasContext *s, TCGv_i32 t_port, int ot) /* user-mode cpu should not be in IOBPT mode */ g_assert_not_reached(); #else - TCGv_i32 t_size = tcg_const_i32(1 << ot); - TCGv t_next = tcg_const_tl(s->pc - s->cs_base); - - gen_helper_bpt_io(cpu_env, t_port, t_size, t_next); - tcg_temp_free_i32(t_size); - tcg_temp_free(t_next); + TCGv_i32 t_size = tcg_constant_i32(1 << ot); + TCGv t_next = eip_next_tl(s); + gen_helper_bpt_io(tcg_env, t_port, t_size, t_next); #endif /* CONFIG_USER_ONLY */ } } -static inline void gen_ins(DisasContext *s, MemOp ot) +static void gen_ins(DisasContext *s, MemOp ot) { gen_string_movl_A0_EDI(s); /* Note: we must do this dummy write first to be restartable in @@ -1213,12 +1330,11 @@ static inline void gen_ins(DisasContext *s, MemOp ot) tcg_gen_andi_i32(s->tmp2_i32, s->tmp2_i32, 0xffff); gen_helper_in_func(ot, s->T0, s->tmp2_i32); gen_op_st_v(s, ot, s->T0, s->A0); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_EDI); + gen_op_add_reg(s, s->aflag, R_EDI, gen_compute_Dshift(s, ot)); gen_bpt_io(s, s->tmp2_i32, ot); } -static inline void gen_outs(DisasContext *s, MemOp ot) +static void gen_outs(DisasContext *s, MemOp ot) { gen_string_movl_A0_ESI(s); gen_op_ld_v(s, ot, s->T0, s->A0); @@ -1227,47 +1343,53 @@ static inline void gen_outs(DisasContext *s, MemOp ot) tcg_gen_andi_i32(s->tmp2_i32, s->tmp2_i32, 0xffff); tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T0); gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_ESI); + gen_op_add_reg(s, s->aflag, R_ESI, gen_compute_Dshift(s, ot)); gen_bpt_io(s, s->tmp2_i32, ot); } -/* same method as Valgrind : we generate jumps to current or next - instruction */ -#define GEN_REPZ(op) \ -static inline void gen_repz_ ## op(DisasContext *s, MemOp ot, \ - target_ulong cur_eip, target_ulong next_eip) \ -{ \ - TCGLabel *l2; \ - gen_update_cc_op(s); \ - l2 = gen_jz_ecx_string(s, next_eip); \ - gen_ ## op(s, ot); \ - gen_op_add_reg_im(s, s->aflag, R_ECX, -1); \ - /* a loop would cause two single step exceptions if ECX = 1 \ - before rep string_insn */ \ - if (s->repz_opt) \ - gen_op_jz_ecx(s, s->aflag, l2); \ - gen_jmp(s, cur_eip); \ +/* Generate jumps to current or next instruction */ +static void gen_repz(DisasContext *s, MemOp ot, + void (*fn)(DisasContext *s, MemOp ot)) +{ + TCGLabel *l2; + gen_update_cc_op(s); + l2 = gen_jz_ecx_string(s); + fn(s, ot); + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + /* + * A loop would cause two single step exceptions if ECX = 1 + * before rep string_insn + */ + if (s->repz_opt) { + gen_op_jz_ecx(s, l2); + } + gen_jmp_rel_csize(s, -cur_insn_len(s), 0); } -#define GEN_REPZ2(op) \ -static inline void gen_repz_ ## op(DisasContext *s, MemOp ot, \ - target_ulong cur_eip, \ - target_ulong next_eip, \ - int nz) \ -{ \ - TCGLabel *l2; \ - gen_update_cc_op(s); \ - l2 = gen_jz_ecx_string(s, next_eip); \ - gen_ ## op(s, ot); \ - gen_op_add_reg_im(s, s->aflag, R_ECX, -1); \ - gen_update_cc_op(s); \ - gen_jcc1(s, (JCC_Z << 1) | (nz ^ 1), l2); \ - if (s->repz_opt) \ - gen_op_jz_ecx(s, s->aflag, l2); \ - gen_jmp(s, cur_eip); \ +#define GEN_REPZ(op) \ + static inline void gen_repz_ ## op(DisasContext *s, MemOp ot) \ + { gen_repz(s, ot, gen_##op); } + +static void gen_repz2(DisasContext *s, MemOp ot, int nz, + void (*fn)(DisasContext *s, MemOp ot)) +{ + TCGLabel *l2; + gen_update_cc_op(s); + l2 = gen_jz_ecx_string(s); + fn(s, ot); + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + gen_update_cc_op(s); + gen_jcc1(s, (JCC_Z << 1) | (nz ^ 1), l2); + if (s->repz_opt) { + gen_op_jz_ecx(s, l2); + } + gen_jmp_rel_csize(s, -cur_insn_len(s), 0); } +#define GEN_REPZ2(op) \ + static inline void gen_repz_ ## op(DisasContext *s, MemOp ot, int nz) \ + { gen_repz2(s, ot, nz, gen_##op); } + GEN_REPZ(movs) GEN_REPZ(stos) GEN_REPZ(lods) @@ -1280,28 +1402,28 @@ static void gen_helper_fp_arith_ST0_FT0(int op) { switch (op) { case 0: - gen_helper_fadd_ST0_FT0(cpu_env); + gen_helper_fadd_ST0_FT0(tcg_env); break; case 1: - gen_helper_fmul_ST0_FT0(cpu_env); + gen_helper_fmul_ST0_FT0(tcg_env); break; case 2: - gen_helper_fcom_ST0_FT0(cpu_env); + gen_helper_fcom_ST0_FT0(tcg_env); break; case 3: - gen_helper_fcom_ST0_FT0(cpu_env); + gen_helper_fcom_ST0_FT0(tcg_env); break; case 4: - gen_helper_fsub_ST0_FT0(cpu_env); + gen_helper_fsub_ST0_FT0(tcg_env); break; case 5: - gen_helper_fsubr_ST0_FT0(cpu_env); + gen_helper_fsubr_ST0_FT0(tcg_env); break; case 6: - gen_helper_fdiv_ST0_FT0(cpu_env); + gen_helper_fdiv_ST0_FT0(tcg_env); break; case 7: - gen_helper_fdivr_ST0_FT0(cpu_env); + gen_helper_fdivr_ST0_FT0(tcg_env); break; } } @@ -1309,34 +1431,34 @@ static void gen_helper_fp_arith_ST0_FT0(int op) /* NOTE the exception in "r" op ordering */ static void gen_helper_fp_arith_STN_ST0(int op, int opreg) { - TCGv_i32 tmp = tcg_const_i32(opreg); + TCGv_i32 tmp = tcg_constant_i32(opreg); switch (op) { case 0: - gen_helper_fadd_STN_ST0(cpu_env, tmp); + gen_helper_fadd_STN_ST0(tcg_env, tmp); break; case 1: - gen_helper_fmul_STN_ST0(cpu_env, tmp); + gen_helper_fmul_STN_ST0(tcg_env, tmp); break; case 4: - gen_helper_fsubr_STN_ST0(cpu_env, tmp); + gen_helper_fsubr_STN_ST0(tcg_env, tmp); break; case 5: - gen_helper_fsub_STN_ST0(cpu_env, tmp); + gen_helper_fsub_STN_ST0(tcg_env, tmp); break; case 6: - gen_helper_fdivr_STN_ST0(cpu_env, tmp); + gen_helper_fdivr_STN_ST0(tcg_env, tmp); break; case 7: - gen_helper_fdiv_STN_ST0(cpu_env, tmp); + gen_helper_fdiv_STN_ST0(tcg_env, tmp); break; } } -static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip) +static void gen_exception(DisasContext *s, int trapno) { gen_update_cc_op(s); - gen_jmp_im(s, cur_eip); - gen_helper_raise_exception(cpu_env, tcg_const_i32(trapno)); + gen_update_eip_cur(s); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(trapno)); s->base.is_jmp = DISAS_NORETURN; } @@ -1344,13 +1466,13 @@ static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip) the instruction is known, but it isn't allowed in the current cpu mode. */ static void gen_illegal_opcode(DisasContext *s) { - gen_exception(s, EXCP06_ILLOP, s->pc_start - s->cs_base); + gen_exception(s, EXCP06_ILLOP); } /* Generate #GP for the current instruction. */ static void gen_exception_gpf(DisasContext *s) { - gen_exception(s, EXCP0D_GPF, s->pc_start - s->cs_base); + gen_exception(s, EXCP0D_GPF); } /* Check for cpl == 0; if not, raise #GP and return false. */ @@ -1386,12 +1508,13 @@ static bool check_iopl(DisasContext *s) /* if d == OR_TMP0, it means memory operand (address in A0) */ static void gen_op(DisasContext *s1, int op, MemOp ot, int d) { + /* Invalid lock prefix when destination is not memory or OP_CMPL. */ + if ((d != OR_TMP0 || op == OP_CMPL) && s1->prefix & PREFIX_LOCK) { + gen_illegal_opcode(s1); + return; + } + if (d != OR_TMP0) { - if (s1->prefix & PREFIX_LOCK) { - /* Lock prefix when destination is not memory. */ - gen_illegal_opcode(s1); - return; - } gen_op_mov_v_reg(s1, ot, s1->T0, d); } else if (!(s1->prefix & PREFIX_LOCK)) { gen_op_ld_v(s1, ot, s1->T0, s1->A0); @@ -1530,7 +1653,7 @@ static void gen_shift_flags(DisasContext *s, MemOp ot, TCGv result, /* Store the results into the CC variables. If we know that the variable must be dead, store unconditionally. Otherwise we'll need to not disrupt the current contents. */ - z_tl = tcg_const_tl(0); + z_tl = tcg_constant_tl(0); if (cc_op_live[s->cc_op] & USES_CC_DST) { tcg_gen_movcond_tl(TCG_COND_NE, cpu_cc_dst, count, z_tl, result, cpu_cc_dst); @@ -1543,7 +1666,6 @@ static void gen_shift_flags(DisasContext *s, MemOp ot, TCGv result, } else { tcg_gen_mov_tl(cpu_cc_src, shm1); } - tcg_temp_free(z_tl); /* Get the two potential CC_OP values into temporaries. */ tcg_gen_movi_i32(s->tmp2_i32, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); @@ -1555,12 +1677,10 @@ static void gen_shift_flags(DisasContext *s, MemOp ot, TCGv result, } /* Conditionally store the CC_OP value. */ - z32 = tcg_const_i32(0); + z32 = tcg_constant_i32(0); s32 = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(s32, count); tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, s32, z32, s->tmp2_i32, oldop); - tcg_temp_free_i32(z32); - tcg_temp_free_i32(s32); /* The CC_OP value is no longer predictable. */ set_cc_op(s, CC_OP_DYNAMIC); @@ -1713,17 +1833,15 @@ static void gen_rot_rm_T1(DisasContext *s, MemOp ot, int op1, int is_right) is 0 we keep the CC_OP_EFLAGS setting so that only CC_SRC is live. Otherwise reuse CC_OP_ADCOX which have the C and O flags split out exactly as we computed above. */ - t0 = tcg_const_i32(0); + t0 = tcg_constant_i32(0); t1 = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(t1, s->T1); tcg_gen_movi_i32(s->tmp2_i32, CC_OP_ADCOX); tcg_gen_movi_i32(s->tmp3_i32, CC_OP_EFLAGS); tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, t1, t0, s->tmp2_i32, s->tmp3_i32); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - /* The CC_OP value is no longer predictable. */ + /* The CC_OP value is no longer predictable. */ set_cc_op(s, CC_OP_DYNAMIC); } @@ -1816,44 +1934,44 @@ static void gen_rotc_rm_T1(DisasContext *s, MemOp ot, int op1, gen_op_ld_v(s, ot, s->T0, s->A0); else gen_op_mov_v_reg(s, ot, s->T0, op1); - + if (is_right) { switch (ot) { case MO_8: - gen_helper_rcrb(s->T0, cpu_env, s->T0, s->T1); + gen_helper_rcrb(s->T0, tcg_env, s->T0, s->T1); break; case MO_16: - gen_helper_rcrw(s->T0, cpu_env, s->T0, s->T1); + gen_helper_rcrw(s->T0, tcg_env, s->T0, s->T1); break; case MO_32: - gen_helper_rcrl(s->T0, cpu_env, s->T0, s->T1); + gen_helper_rcrl(s->T0, tcg_env, s->T0, s->T1); break; #ifdef TARGET_X86_64 case MO_64: - gen_helper_rcrq(s->T0, cpu_env, s->T0, s->T1); + gen_helper_rcrq(s->T0, tcg_env, s->T0, s->T1); break; #endif default: - tcg_abort(); + g_assert_not_reached(); } } else { switch (ot) { case MO_8: - gen_helper_rclb(s->T0, cpu_env, s->T0, s->T1); + gen_helper_rclb(s->T0, tcg_env, s->T0, s->T1); break; case MO_16: - gen_helper_rclw(s->T0, cpu_env, s->T0, s->T1); + gen_helper_rclw(s->T0, tcg_env, s->T0, s->T1); break; case MO_32: - gen_helper_rcll(s->T0, cpu_env, s->T0, s->T1); + gen_helper_rcll(s->T0, tcg_env, s->T0, s->T1); break; #ifdef TARGET_X86_64 case MO_64: - gen_helper_rclq(s->T0, cpu_env, s->T0, s->T1); + gen_helper_rclq(s->T0, tcg_env, s->T0, s->T1); break; #endif default: - tcg_abort(); + g_assert_not_reached(); } } /* store */ @@ -1942,7 +2060,6 @@ static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, int op1, gen_op_st_rm_T0_A0(s, ot, op1); gen_shift_flags(s, ot, s->T0, s->tmp0, count, is_right); - tcg_temp_free(count); } static void gen_shift(DisasContext *s1, int op, MemOp ot, int d, int s) @@ -2008,8 +2125,14 @@ static uint64_t advance_pc(CPUX86State *env, DisasContext *s, int num_bytes) { uint64_t pc = s->pc; + /* This is a subsequent insn that crosses a page boundary. */ + if (s->base.num_insns > 1 && + !is_same_page(&s->base, s->pc + num_bytes - 1)) { + siglongjmp(s->jmpbuf, 2); + } + s->pc += num_bytes; - if (unlikely(s->pc - s->pc_start > X86_MAX_INSN_LENGTH)) { + if (unlikely(cur_insn_len(s) > X86_MAX_INSN_LENGTH)) { /* If the instruction's 16th byte is on a different page than the 1st, a * page fault on the second page wins over the general protection fault * caused by the instruction being too long. @@ -2033,7 +2156,7 @@ static inline uint8_t x86_ldub_code(CPUX86State *env, DisasContext *s) static inline int16_t x86_ldsw_code(CPUX86State *env, DisasContext *s) { - return translator_ldsw(env, &s->base, advance_pc(env, s, 2)); + return translator_lduw(env, &s->base, advance_pc(env, s, 2)); } static inline uint16_t x86_lduw_code(CPUX86State *env, DisasContext *s) @@ -2179,7 +2302,7 @@ static AddressParts gen_lea_modrm_0(CPUX86State *env, DisasContext *s, break; default: - tcg_abort(); + g_assert_not_reached(); } done: @@ -2187,11 +2310,11 @@ static AddressParts gen_lea_modrm_0(CPUX86State *env, DisasContext *s, } /* Compute the address, with a minimum number of TCG ops. */ -static TCGv gen_lea_modrm_1(DisasContext *s, AddressParts a) +static TCGv gen_lea_modrm_1(DisasContext *s, AddressParts a, bool is_vsib) { TCGv ea = NULL; - if (a.index >= 0) { + if (a.index >= 0 && !is_vsib) { if (a.scale == 0) { ea = cpu_regs[a.index]; } else { @@ -2206,7 +2329,12 @@ static TCGv gen_lea_modrm_1(DisasContext *s, AddressParts a) ea = cpu_regs[a.base]; } if (!ea) { - tcg_gen_movi_tl(s->A0, a.disp); + if (tb_cflags(s->base.tb) & CF_PCREL && a.base == -2) { + /* With cpu_eip ~= pc_save, the expression is pc-relative. */ + tcg_gen_addi_tl(s->A0, cpu_eip, a.disp - s->pc_save); + } else { + tcg_gen_movi_tl(s->A0, a.disp); + } ea = s->A0; } else if (a.disp != 0) { tcg_gen_addi_tl(s->A0, ea, a.disp); @@ -2219,7 +2347,7 @@ static TCGv gen_lea_modrm_1(DisasContext *s, AddressParts a) static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) { AddressParts a = gen_lea_modrm_0(env, s, modrm); - TCGv ea = gen_lea_modrm_1(s, a); + TCGv ea = gen_lea_modrm_1(s, a, false); gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override); } @@ -2232,7 +2360,8 @@ static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm) static void gen_bndck(CPUX86State *env, DisasContext *s, int modrm, TCGCond cond, TCGv_i64 bndv) { - TCGv ea = gen_lea_modrm_1(s, gen_lea_modrm_0(env, s, modrm)); + AddressParts a = gen_lea_modrm_0(env, s, modrm); + TCGv ea = gen_lea_modrm_1(s, a, false); tcg_gen_extu_tl_i64(s->tmp1_i64, ea); if (!CODE64(s)) { @@ -2240,7 +2369,7 @@ static void gen_bndck(CPUX86State *env, DisasContext *s, int modrm, } tcg_gen_setcond_i64(cond, s->tmp1_i64, s->tmp1_i64, bndv); tcg_gen_extrl_i64_i32(s->tmp2_i32, s->tmp1_i64); - gen_helper_bndck(cpu_env, s->tmp2_i32); + gen_helper_bndck(tcg_env, s->tmp2_i32); } /* used for LEA and MOV AX, mem */ @@ -2282,6 +2411,31 @@ static void gen_ldst_modrm(CPUX86State *env, DisasContext *s, int modrm, } } +static target_ulong insn_get_addr(CPUX86State *env, DisasContext *s, MemOp ot) +{ + target_ulong ret; + + switch (ot) { + case MO_8: + ret = x86_ldub_code(env, s); + break; + case MO_16: + ret = x86_lduw_code(env, s); + break; + case MO_32: + ret = x86_ldl_code(env, s); + break; +#ifdef TARGET_X86_64 + case MO_64: + ret = x86_ldq_code(env, s); + break; +#endif + default: + g_assert_not_reached(); + } + return ret; +} + static inline uint32_t insn_get(CPUX86State *env, DisasContext *s, MemOp ot) { uint32_t ret; @@ -2300,7 +2454,32 @@ static inline uint32_t insn_get(CPUX86State *env, DisasContext *s, MemOp ot) ret = x86_ldl_code(env, s); break; default: - tcg_abort(); + g_assert_not_reached(); + } + return ret; +} + +static target_long insn_get_signed(CPUX86State *env, DisasContext *s, MemOp ot) +{ + target_long ret; + + switch (ot) { + case MO_8: + ret = (int8_t) x86_ldub_code(env, s); + break; + case MO_16: + ret = (int16_t) x86_lduw_code(env, s); + break; + case MO_32: + ret = (int32_t) x86_ldl_code(env, s); + break; +#ifdef TARGET_X86_64 + case MO_64: + ret = x86_ldq_code(env, s); + break; +#endif + default: + g_assert_not_reached(); } return ret; } @@ -2314,90 +2493,42 @@ static inline int insn_const_size(MemOp ot) } } -static void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip) +static void gen_jcc(DisasContext *s, int b, int diff) { - target_ulong pc = s->cs_base + eip; + TCGLabel *l1 = gen_new_label(); - if (translator_use_goto_tb(&s->base, pc)) { - /* jump to same page: we can use a direct jump */ - tcg_gen_goto_tb(tb_num); - gen_jmp_im(s, eip); - tcg_gen_exit_tb(s->base.tb, tb_num); - s->base.is_jmp = DISAS_NORETURN; - } else { - /* jump to another page */ - gen_jmp_im(s, eip); - gen_jr(s, s->tmp0); - } + gen_jcc1(s, b, l1); + gen_jmp_rel_csize(s, 0, 1); + gen_set_label(l1); + gen_jmp_rel(s, s->dflag, diff, 0); } -static inline void gen_jcc(DisasContext *s, int b, - target_ulong val, target_ulong next_eip) +static void gen_cmovcc1(DisasContext *s, int b, TCGv dest, TCGv src) { - TCGLabel *l1, *l2; + CCPrepare cc = gen_prepare_cc(s, b, s->T1); - if (s->jmp_opt) { - l1 = gen_new_label(); - gen_jcc1(s, b, l1); - - gen_goto_tb(s, 0, next_eip); - - gen_set_label(l1); - gen_goto_tb(s, 1, val); - } else { - l1 = gen_new_label(); - l2 = gen_new_label(); - gen_jcc1(s, b, l1); - - gen_jmp_im(s, next_eip); - tcg_gen_br(l2); - - gen_set_label(l1); - gen_jmp_im(s, val); - gen_set_label(l2); - gen_eob(s); - } -} - -static void gen_cmovcc1(CPUX86State *env, DisasContext *s, MemOp ot, int b, - int modrm, int reg) -{ - CCPrepare cc; - - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - - cc = gen_prepare_cc(s, b, s->T1); if (cc.mask != -1) { TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cc.reg, cc.mask); cc.reg = t0; } if (!cc.use_reg2) { - cc.reg2 = tcg_const_tl(cc.imm); + cc.reg2 = tcg_constant_tl(cc.imm); } - tcg_gen_movcond_tl(cc.cond, s->T0, cc.reg, cc.reg2, - s->T0, cpu_regs[reg]); - gen_op_mov_reg_v(s, ot, reg, s->T0); - - if (cc.mask != -1) { - tcg_temp_free(cc.reg); - } - if (!cc.use_reg2) { - tcg_temp_free(cc.reg2); - } + tcg_gen_movcond_tl(cc.cond, dest, cc.reg, cc.reg2, src, dest); } static inline void gen_op_movl_T0_seg(DisasContext *s, X86Seg seg_reg) { - tcg_gen_ld32u_tl(s->T0, cpu_env, + tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State,segs[seg_reg].selector)); } static inline void gen_op_movl_seg_T0_vm(DisasContext *s, X86Seg seg_reg) { tcg_gen_ext16u_tl(s->T0, s->T0); - tcg_gen_st32_tl(s->T0, cpu_env, + tcg_gen_st32_tl(s->T0, tcg_env, offsetof(CPUX86State,segs[seg_reg].selector)); tcg_gen_shli_tl(cpu_seg_base[seg_reg], s->T0, 4); } @@ -2408,18 +2539,20 @@ static void gen_movl_seg_T0(DisasContext *s, X86Seg seg_reg) { if (PE(s) && !VM86(s)) { tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_load_seg(cpu_env, tcg_const_i32(seg_reg), s->tmp2_i32); + gen_helper_load_seg(tcg_env, tcg_constant_i32(seg_reg), s->tmp2_i32); /* abort translation because the addseg value may change or because ss32 may change. For R_SS, translation must always stop as a special handling must be done to disable hardware interrupts for the next instruction */ - if (seg_reg == R_SS || (CODE32(s) && seg_reg < R_FS)) { - s->base.is_jmp = DISAS_TOO_MANY; + if (seg_reg == R_SS) { + s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; + } else if (CODE32(s) && seg_reg < R_FS) { + s->base.is_jmp = DISAS_EOB_NEXT; } } else { gen_op_movl_seg_T0_vm(s, seg_reg); if (seg_reg == R_SS) { - s->base.is_jmp = DISAS_TOO_MANY; + s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; } } } @@ -2430,7 +2563,7 @@ static void gen_svm_check_intercept(DisasContext *s, uint32_t type) if (likely(!GUEST(s))) { return; } - gen_helper_svm_check_intercept(cpu_env, tcg_constant_i32(type)); + gen_helper_svm_check_intercept(tcg_env, tcg_constant_i32(type)); } static inline void gen_stack_update(DisasContext *s, int addend) @@ -2450,7 +2583,7 @@ static void gen_push_v(DisasContext *s, TCGv val) if (!CODE64(s)) { if (ADDSEG(s)) { - new_esp = s->tmp4; + new_esp = tcg_temp_new(); tcg_gen_mov_tl(new_esp, s->A0); } gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); @@ -2465,8 +2598,8 @@ static MemOp gen_pop_T0(DisasContext *s) { MemOp d_ot = mo_pushpop(s, s->dflag); - gen_lea_v_seg(s, mo_stacksize(s), cpu_regs[R_ESP], R_SS, -1); - gen_op_ld_v(s, d_ot, s->T0, s->A0); + gen_lea_v_seg_dest(s, mo_stacksize(s), s->T0, cpu_regs[R_ESP], R_SS, -1); + gen_op_ld_v(s, d_ot, s->T0, s->T0); return d_ot; } @@ -2582,7 +2715,7 @@ static void gen_unknown_opcode(CPUX86State *env, DisasContext *s) if (qemu_loglevel_mask(LOG_UNIMP)) { FILE *logfile = qemu_log_trylock(); if (logfile) { - target_ulong pc = s->pc_start, end = s->pc; + target_ulong pc = s->base.pc_next, end = s->pc; fprintf(logfile, "ILLOPC: " TARGET_FMT_lx ":", pc); for (; pc < end; ++pc) { @@ -2596,13 +2729,12 @@ static void gen_unknown_opcode(CPUX86State *env, DisasContext *s) /* an interrupt is different from an exception because of the privilege checks */ -static void gen_interrupt(DisasContext *s, int intno, - target_ulong cur_eip, target_ulong next_eip) +static void gen_interrupt(DisasContext *s, int intno) { gen_update_cc_op(s); - gen_jmp_im(s, cur_eip); - gen_helper_raise_interrupt(cpu_env, tcg_const_i32(intno), - tcg_const_i32(next_eip - cur_eip)); + gen_update_eip_cur(s); + gen_helper_raise_interrupt(tcg_env, tcg_constant_i32(intno), + cur_insn_len_i32(s)); s->base.is_jmp = DISAS_NORETURN; } @@ -2610,10 +2742,9 @@ static void gen_set_hflag(DisasContext *s, uint32_t mask) { if ((s->flags & mask) == 0) { TCGv_i32 t = tcg_temp_new_i32(); - tcg_gen_ld_i32(t, cpu_env, offsetof(CPUX86State, hflags)); + tcg_gen_ld_i32(t, tcg_env, offsetof(CPUX86State, hflags)); tcg_gen_ori_i32(t, t, mask); - tcg_gen_st_i32(t, cpu_env, offsetof(CPUX86State, hflags)); - tcg_temp_free_i32(t); + tcg_gen_st_i32(t, tcg_env, offsetof(CPUX86State, hflags)); s->flags |= mask; } } @@ -2622,14 +2753,31 @@ static void gen_reset_hflag(DisasContext *s, uint32_t mask) { if (s->flags & mask) { TCGv_i32 t = tcg_temp_new_i32(); - tcg_gen_ld_i32(t, cpu_env, offsetof(CPUX86State, hflags)); + tcg_gen_ld_i32(t, tcg_env, offsetof(CPUX86State, hflags)); tcg_gen_andi_i32(t, t, ~mask); - tcg_gen_st_i32(t, cpu_env, offsetof(CPUX86State, hflags)); - tcg_temp_free_i32(t); + tcg_gen_st_i32(t, tcg_env, offsetof(CPUX86State, hflags)); s->flags &= ~mask; } } +static void gen_set_eflags(DisasContext *s, target_ulong mask) +{ + TCGv t = tcg_temp_new(); + + tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, eflags)); + tcg_gen_ori_tl(t, t, mask); + tcg_gen_st_tl(t, tcg_env, offsetof(CPUX86State, eflags)); +} + +static void gen_reset_eflags(DisasContext *s, target_ulong mask) +{ + TCGv t = tcg_temp_new(); + + tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, eflags)); + tcg_gen_andi_tl(t, t, ~mask); + tcg_gen_st_tl(t, tcg_env, offsetof(CPUX86State, eflags)); +} + /* Clear BND registers during legacy branches. */ static void gen_bnd_jmp(DisasContext *s) { @@ -2639,7 +2787,7 @@ static void gen_bnd_jmp(DisasContext *s) if ((s->prefix & PREFIX_REPNZ) == 0 && (s->flags & HF_MPX_EN_MASK) != 0 && (s->flags & HF_MPX_IU_MASK) != 0) { - gen_helper_bnd_jmp(cpu_env); + gen_helper_bnd_jmp(tcg_env); } } @@ -2660,13 +2808,13 @@ do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) } if (s->base.tb->flags & HF_RF_MASK) { - gen_helper_reset_rf(cpu_env); + gen_reset_eflags(s, RF_MASK); } if (recheck_tf) { - gen_helper_rechecking_single_step(cpu_env); + gen_helper_rechecking_single_step(tcg_env); tcg_gen_exit_tb(NULL, 0); } else if (s->flags & HF_TF_MASK) { - gen_helper_single_step(cpu_env); + gen_helper_single_step(tcg_env); } else if (jr) { tcg_gen_lookup_and_goto_ptr(); } else { @@ -2695,1872 +2843,259 @@ static void gen_eob(DisasContext *s) } /* Jump to register */ -static void gen_jr(DisasContext *s, TCGv dest) +static void gen_jr(DisasContext *s) { do_gen_eob_worker(s, false, false, true); } -/* generate a jump to eip. No segment change must happen before as a - direct call to the next block may occur */ -static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num) +/* Jump to eip+diff, truncating the result to OT. */ +static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) { + bool use_goto_tb = s->jmp_opt; + target_ulong mask = -1; + target_ulong new_pc = s->pc + diff; + target_ulong new_eip = new_pc - s->cs_base; + + /* In 64-bit mode, operand size is fixed at 64 bits. */ + if (!CODE64(s)) { + if (ot == MO_16) { + mask = 0xffff; + if (tb_cflags(s->base.tb) & CF_PCREL && CODE32(s)) { + use_goto_tb = false; + } + } else { + mask = 0xffffffff; + } + } + new_eip &= mask; + gen_update_cc_op(s); set_cc_op(s, CC_OP_DYNAMIC); - if (s->jmp_opt) { - gen_goto_tb(s, tb_num, eip); + + if (tb_cflags(s->base.tb) & CF_PCREL) { + tcg_gen_addi_tl(cpu_eip, cpu_eip, new_pc - s->pc_save); + /* + * If we can prove the branch does not leave the page and we have + * no extra masking to apply (data16 branch in code32, see above), + * then we have also proven that the addition does not wrap. + */ + if (!use_goto_tb || !is_same_page(&s->base, new_pc)) { + tcg_gen_andi_tl(cpu_eip, cpu_eip, mask); + use_goto_tb = false; + } + } else if (!CODE64(s)) { + new_pc = (uint32_t)(new_eip + s->cs_base); + } + + if (use_goto_tb && translator_use_goto_tb(&s->base, new_pc)) { + /* jump to same page: we can use a direct jump */ + tcg_gen_goto_tb(tb_num); + if (!(tb_cflags(s->base.tb) & CF_PCREL)) { + tcg_gen_movi_tl(cpu_eip, new_eip); + } + tcg_gen_exit_tb(s->base.tb, tb_num); + s->base.is_jmp = DISAS_NORETURN; } else { - gen_jmp_im(s, eip); - gen_eob(s); + if (!(tb_cflags(s->base.tb) & CF_PCREL)) { + tcg_gen_movi_tl(cpu_eip, new_eip); + } + if (s->jmp_opt) { + gen_jr(s); /* jump to another page */ + } else { + gen_eob(s); /* exit to main loop */ + } } } -static void gen_jmp(DisasContext *s, target_ulong eip) +/* Jump to eip+diff, truncating to the current code size. */ +static void gen_jmp_rel_csize(DisasContext *s, int diff, int tb_num) { - gen_jmp_tb(s, eip, 0); + /* CODE64 ignores the OT argument, so we need not consider it. */ + gen_jmp_rel(s, CODE32(s) ? MO_32 : MO_16, diff, tb_num); } static inline void gen_ldq_env_A0(DisasContext *s, int offset) { tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset); + tcg_gen_st_i64(s->tmp1_i64, tcg_env, offset); } static inline void gen_stq_env_A0(DisasContext *s, int offset) { - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset); + tcg_gen_ld_i64(s->tmp1_i64, tcg_env, offset); tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); } -static inline void gen_ldo_env_A0(DisasContext *s, int offset) +static inline void gen_ldo_env_A0(DisasContext *s, int offset, bool align) { + MemOp atom = (s->cpuid_ext_features & CPUID_EXT_AVX + ? MO_ATOM_IFALIGN : MO_ATOM_IFALIGN_PAIR); + MemOp mop = MO_128 | MO_LE | atom | (align ? MO_ALIGN_16 : 0); int mem_index = s->mem_index; - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, mem_index, MO_LEUQ); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0))); - tcg_gen_addi_tl(s->tmp0, s->A0, 8); - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEUQ); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(1))); + TCGv_i128 t = tcg_temp_new_i128(); + + tcg_gen_qemu_ld_i128(t, s->A0, mem_index, mop); + tcg_gen_st_i128(t, tcg_env, offset); } -static inline void gen_sto_env_A0(DisasContext *s, int offset) +static inline void gen_sto_env_A0(DisasContext *s, int offset, bool align) { + MemOp atom = (s->cpuid_ext_features & CPUID_EXT_AVX + ? MO_ATOM_IFALIGN : MO_ATOM_IFALIGN_PAIR); + MemOp mop = MO_128 | MO_LE | atom | (align ? MO_ALIGN_16 : 0); int mem_index = s->mem_index; - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0))); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, mem_index, MO_LEUQ); - tcg_gen_addi_tl(s->tmp0, s->A0, 8); - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(1))); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEUQ); + TCGv_i128 t = tcg_temp_new_i128(); + + tcg_gen_ld_i128(t, tcg_env, offset); + tcg_gen_qemu_st_i128(t, s->A0, mem_index, mop); } -static inline void gen_op_movo(DisasContext *s, int d_offset, int s_offset) +static void gen_ldy_env_A0(DisasContext *s, int offset, bool align) { - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, s_offset + offsetof(ZMMReg, ZMM_Q(0))); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset + offsetof(ZMMReg, ZMM_Q(0))); - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, s_offset + offsetof(ZMMReg, ZMM_Q(1))); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset + offsetof(ZMMReg, ZMM_Q(1))); + MemOp mop = MO_128 | MO_LE | MO_ATOM_IFALIGN_PAIR; + int mem_index = s->mem_index; + TCGv_i128 t0 = tcg_temp_new_i128(); + TCGv_i128 t1 = tcg_temp_new_i128(); + + tcg_gen_qemu_ld_i128(t0, s->A0, mem_index, mop | (align ? MO_ALIGN_32 : 0)); + tcg_gen_addi_tl(s->tmp0, s->A0, 16); + tcg_gen_qemu_ld_i128(t1, s->tmp0, mem_index, mop); + + tcg_gen_st_i128(t0, tcg_env, offset + offsetof(YMMReg, YMM_X(0))); + tcg_gen_st_i128(t1, tcg_env, offset + offsetof(YMMReg, YMM_X(1))); } -static inline void gen_op_movq(DisasContext *s, int d_offset, int s_offset) +static void gen_sty_env_A0(DisasContext *s, int offset, bool align) { - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, s_offset); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset); + MemOp mop = MO_128 | MO_LE | MO_ATOM_IFALIGN_PAIR; + int mem_index = s->mem_index; + TCGv_i128 t = tcg_temp_new_i128(); + + tcg_gen_ld_i128(t, tcg_env, offset + offsetof(YMMReg, YMM_X(0))); + tcg_gen_qemu_st_i128(t, s->A0, mem_index, mop | (align ? MO_ALIGN_32 : 0)); + tcg_gen_addi_tl(s->tmp0, s->A0, 16); + tcg_gen_ld_i128(t, tcg_env, offset + offsetof(YMMReg, YMM_X(1))); + tcg_gen_qemu_st_i128(t, s->tmp0, mem_index, mop); } -static inline void gen_op_movl(DisasContext *s, int d_offset, int s_offset) +#include "decode-new.h" +#include "emit.c.inc" +#include "decode-new.c.inc" + +static void gen_cmpxchg8b(DisasContext *s, CPUX86State *env, int modrm) { - tcg_gen_ld_i32(s->tmp2_i32, cpu_env, s_offset); - tcg_gen_st_i32(s->tmp2_i32, cpu_env, d_offset); -} + TCGv_i64 cmp, val, old; + TCGv Z; -static inline void gen_op_movq_env_0(DisasContext *s, int d_offset) -{ - tcg_gen_movi_i64(s->tmp1_i64, 0); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset); -} + gen_lea_modrm(env, s, modrm); -typedef void (*SSEFunc_i_ep)(TCGv_i32 val, TCGv_ptr env, TCGv_ptr reg); -typedef void (*SSEFunc_l_ep)(TCGv_i64 val, TCGv_ptr env, TCGv_ptr reg); -typedef void (*SSEFunc_0_epi)(TCGv_ptr env, TCGv_ptr reg, TCGv_i32 val); -typedef void (*SSEFunc_0_epl)(TCGv_ptr env, TCGv_ptr reg, TCGv_i64 val); -typedef void (*SSEFunc_0_epp)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b); -typedef void (*SSEFunc_0_eppi)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, - TCGv_i32 val); -typedef void (*SSEFunc_0_ppi)(TCGv_ptr reg_a, TCGv_ptr reg_b, TCGv_i32 val); -typedef void (*SSEFunc_0_eppt)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, - TCGv val); + cmp = tcg_temp_new_i64(); + val = tcg_temp_new_i64(); + old = tcg_temp_new_i64(); -#define SSE_SPECIAL ((void *)1) -#define SSE_DUMMY ((void *)2) + /* Construct the comparison values from the register pair. */ + tcg_gen_concat_tl_i64(cmp, cpu_regs[R_EAX], cpu_regs[R_EDX]); + tcg_gen_concat_tl_i64(val, cpu_regs[R_EBX], cpu_regs[R_ECX]); -#define MMX_OP2(x) { gen_helper_ ## x ## _mmx, gen_helper_ ## x ## _xmm } -#define SSE_FOP(x) { gen_helper_ ## x ## ps, gen_helper_ ## x ## pd, \ - gen_helper_ ## x ## ss, gen_helper_ ## x ## sd, } - -static const SSEFunc_0_epp sse_op_table1[256][4] = { - /* 3DNow! extensions */ - [0x0e] = { SSE_DUMMY }, /* femms */ - [0x0f] = { SSE_DUMMY }, /* pf... */ - /* pure SSE operations */ - [0x10] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movups, movupd, movss, movsd */ - [0x11] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movups, movupd, movss, movsd */ - [0x12] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movlps, movlpd, movsldup, movddup */ - [0x13] = { SSE_SPECIAL, SSE_SPECIAL }, /* movlps, movlpd */ - [0x14] = { gen_helper_punpckldq_xmm, gen_helper_punpcklqdq_xmm }, - [0x15] = { gen_helper_punpckhdq_xmm, gen_helper_punpckhqdq_xmm }, - [0x16] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movhps, movhpd, movshdup */ - [0x17] = { SSE_SPECIAL, SSE_SPECIAL }, /* movhps, movhpd */ - - [0x28] = { SSE_SPECIAL, SSE_SPECIAL }, /* movaps, movapd */ - [0x29] = { SSE_SPECIAL, SSE_SPECIAL }, /* movaps, movapd */ - [0x2a] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvtpi2ps, cvtpi2pd, cvtsi2ss, cvtsi2sd */ - [0x2b] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movntps, movntpd, movntss, movntsd */ - [0x2c] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvttps2pi, cvttpd2pi, cvttsd2si, cvttss2si */ - [0x2d] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvtps2pi, cvtpd2pi, cvtsd2si, cvtss2si */ - [0x2e] = { gen_helper_ucomiss, gen_helper_ucomisd }, - [0x2f] = { gen_helper_comiss, gen_helper_comisd }, - [0x50] = { SSE_SPECIAL, SSE_SPECIAL }, /* movmskps, movmskpd */ - [0x51] = SSE_FOP(sqrt), - [0x52] = { gen_helper_rsqrtps, NULL, gen_helper_rsqrtss, NULL }, - [0x53] = { gen_helper_rcpps, NULL, gen_helper_rcpss, NULL }, - [0x54] = { gen_helper_pand_xmm, gen_helper_pand_xmm }, /* andps, andpd */ - [0x55] = { gen_helper_pandn_xmm, gen_helper_pandn_xmm }, /* andnps, andnpd */ - [0x56] = { gen_helper_por_xmm, gen_helper_por_xmm }, /* orps, orpd */ - [0x57] = { gen_helper_pxor_xmm, gen_helper_pxor_xmm }, /* xorps, xorpd */ - [0x58] = SSE_FOP(add), - [0x59] = SSE_FOP(mul), - [0x5a] = { gen_helper_cvtps2pd, gen_helper_cvtpd2ps, - gen_helper_cvtss2sd, gen_helper_cvtsd2ss }, - [0x5b] = { gen_helper_cvtdq2ps, gen_helper_cvtps2dq, gen_helper_cvttps2dq }, - [0x5c] = SSE_FOP(sub), - [0x5d] = SSE_FOP(min), - [0x5e] = SSE_FOP(div), - [0x5f] = SSE_FOP(max), - - [0xc2] = SSE_FOP(cmpeq), - [0xc6] = { (SSEFunc_0_epp)gen_helper_shufps, - (SSEFunc_0_epp)gen_helper_shufpd }, /* XXX: casts */ - - /* SSSE3, SSE4, MOVBE, CRC32, BMI1, BMI2, ADX. */ - [0x38] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, - [0x3a] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, - - /* MMX ops and their SSE extensions */ - [0x60] = MMX_OP2(punpcklbw), - [0x61] = MMX_OP2(punpcklwd), - [0x62] = MMX_OP2(punpckldq), - [0x63] = MMX_OP2(packsswb), - [0x64] = MMX_OP2(pcmpgtb), - [0x65] = MMX_OP2(pcmpgtw), - [0x66] = MMX_OP2(pcmpgtl), - [0x67] = MMX_OP2(packuswb), - [0x68] = MMX_OP2(punpckhbw), - [0x69] = MMX_OP2(punpckhwd), - [0x6a] = MMX_OP2(punpckhdq), - [0x6b] = MMX_OP2(packssdw), - [0x6c] = { NULL, gen_helper_punpcklqdq_xmm }, - [0x6d] = { NULL, gen_helper_punpckhqdq_xmm }, - [0x6e] = { SSE_SPECIAL, SSE_SPECIAL }, /* movd mm, ea */ - [0x6f] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movq, movdqa, , movqdu */ - [0x70] = { (SSEFunc_0_epp)gen_helper_pshufw_mmx, - (SSEFunc_0_epp)gen_helper_pshufd_xmm, - (SSEFunc_0_epp)gen_helper_pshufhw_xmm, - (SSEFunc_0_epp)gen_helper_pshuflw_xmm }, /* XXX: casts */ - [0x71] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftw */ - [0x72] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftd */ - [0x73] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftq */ - [0x74] = MMX_OP2(pcmpeqb), - [0x75] = MMX_OP2(pcmpeqw), - [0x76] = MMX_OP2(pcmpeql), - [0x77] = { SSE_DUMMY }, /* emms */ - [0x78] = { NULL, SSE_SPECIAL, NULL, SSE_SPECIAL }, /* extrq_i, insertq_i */ - [0x79] = { NULL, gen_helper_extrq_r, NULL, gen_helper_insertq_r }, - [0x7c] = { NULL, gen_helper_haddpd, NULL, gen_helper_haddps }, - [0x7d] = { NULL, gen_helper_hsubpd, NULL, gen_helper_hsubps }, - [0x7e] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movd, movd, , movq */ - [0x7f] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movq, movdqa, movdqu */ - [0xc4] = { SSE_SPECIAL, SSE_SPECIAL }, /* pinsrw */ - [0xc5] = { SSE_SPECIAL, SSE_SPECIAL }, /* pextrw */ - [0xd0] = { NULL, gen_helper_addsubpd, NULL, gen_helper_addsubps }, - [0xd1] = MMX_OP2(psrlw), - [0xd2] = MMX_OP2(psrld), - [0xd3] = MMX_OP2(psrlq), - [0xd4] = MMX_OP2(paddq), - [0xd5] = MMX_OP2(pmullw), - [0xd6] = { NULL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, - [0xd7] = { SSE_SPECIAL, SSE_SPECIAL }, /* pmovmskb */ - [0xd8] = MMX_OP2(psubusb), - [0xd9] = MMX_OP2(psubusw), - [0xda] = MMX_OP2(pminub), - [0xdb] = MMX_OP2(pand), - [0xdc] = MMX_OP2(paddusb), - [0xdd] = MMX_OP2(paddusw), - [0xde] = MMX_OP2(pmaxub), - [0xdf] = MMX_OP2(pandn), - [0xe0] = MMX_OP2(pavgb), - [0xe1] = MMX_OP2(psraw), - [0xe2] = MMX_OP2(psrad), - [0xe3] = MMX_OP2(pavgw), - [0xe4] = MMX_OP2(pmulhuw), - [0xe5] = MMX_OP2(pmulhw), - [0xe6] = { NULL, gen_helper_cvttpd2dq, gen_helper_cvtdq2pd, gen_helper_cvtpd2dq }, - [0xe7] = { SSE_SPECIAL , SSE_SPECIAL }, /* movntq, movntq */ - [0xe8] = MMX_OP2(psubsb), - [0xe9] = MMX_OP2(psubsw), - [0xea] = MMX_OP2(pminsw), - [0xeb] = MMX_OP2(por), - [0xec] = MMX_OP2(paddsb), - [0xed] = MMX_OP2(paddsw), - [0xee] = MMX_OP2(pmaxsw), - [0xef] = MMX_OP2(pxor), - [0xf0] = { NULL, NULL, NULL, SSE_SPECIAL }, /* lddqu */ - [0xf1] = MMX_OP2(psllw), - [0xf2] = MMX_OP2(pslld), - [0xf3] = MMX_OP2(psllq), - [0xf4] = MMX_OP2(pmuludq), - [0xf5] = MMX_OP2(pmaddwd), - [0xf6] = MMX_OP2(psadbw), - [0xf7] = { (SSEFunc_0_epp)gen_helper_maskmov_mmx, - (SSEFunc_0_epp)gen_helper_maskmov_xmm }, /* XXX: casts */ - [0xf8] = MMX_OP2(psubb), - [0xf9] = MMX_OP2(psubw), - [0xfa] = MMX_OP2(psubl), - [0xfb] = MMX_OP2(psubq), - [0xfc] = MMX_OP2(paddb), - [0xfd] = MMX_OP2(paddw), - [0xfe] = MMX_OP2(paddl), -}; - -static const SSEFunc_0_epp sse_op_table2[3 * 8][2] = { - [0 + 2] = MMX_OP2(psrlw), - [0 + 4] = MMX_OP2(psraw), - [0 + 6] = MMX_OP2(psllw), - [8 + 2] = MMX_OP2(psrld), - [8 + 4] = MMX_OP2(psrad), - [8 + 6] = MMX_OP2(pslld), - [16 + 2] = MMX_OP2(psrlq), - [16 + 3] = { NULL, gen_helper_psrldq_xmm }, - [16 + 6] = MMX_OP2(psllq), - [16 + 7] = { NULL, gen_helper_pslldq_xmm }, -}; - -static const SSEFunc_0_epi sse_op_table3ai[] = { - gen_helper_cvtsi2ss, - gen_helper_cvtsi2sd -}; - -#ifdef TARGET_X86_64 -static const SSEFunc_0_epl sse_op_table3aq[] = { - gen_helper_cvtsq2ss, - gen_helper_cvtsq2sd -}; -#endif - -static const SSEFunc_i_ep sse_op_table3bi[] = { - gen_helper_cvttss2si, - gen_helper_cvtss2si, - gen_helper_cvttsd2si, - gen_helper_cvtsd2si -}; - -#ifdef TARGET_X86_64 -static const SSEFunc_l_ep sse_op_table3bq[] = { - gen_helper_cvttss2sq, - gen_helper_cvtss2sq, - gen_helper_cvttsd2sq, - gen_helper_cvtsd2sq -}; -#endif - -static const SSEFunc_0_epp sse_op_table4[8][4] = { - SSE_FOP(cmpeq), - SSE_FOP(cmplt), - SSE_FOP(cmple), - SSE_FOP(cmpunord), - SSE_FOP(cmpneq), - SSE_FOP(cmpnlt), - SSE_FOP(cmpnle), - SSE_FOP(cmpord), -}; - -static const SSEFunc_0_epp sse_op_table5[256] = { - [0x0c] = gen_helper_pi2fw, - [0x0d] = gen_helper_pi2fd, - [0x1c] = gen_helper_pf2iw, - [0x1d] = gen_helper_pf2id, - [0x8a] = gen_helper_pfnacc, - [0x8e] = gen_helper_pfpnacc, - [0x90] = gen_helper_pfcmpge, - [0x94] = gen_helper_pfmin, - [0x96] = gen_helper_pfrcp, - [0x97] = gen_helper_pfrsqrt, - [0x9a] = gen_helper_pfsub, - [0x9e] = gen_helper_pfadd, - [0xa0] = gen_helper_pfcmpgt, - [0xa4] = gen_helper_pfmax, - [0xa6] = gen_helper_movq, /* pfrcpit1; no need to actually increase precision */ - [0xa7] = gen_helper_movq, /* pfrsqit1 */ - [0xaa] = gen_helper_pfsubr, - [0xae] = gen_helper_pfacc, - [0xb0] = gen_helper_pfcmpeq, - [0xb4] = gen_helper_pfmul, - [0xb6] = gen_helper_movq, /* pfrcpit2 */ - [0xb7] = gen_helper_pmulhrw_mmx, - [0xbb] = gen_helper_pswapd, - [0xbf] = gen_helper_pavgb_mmx /* pavgusb */ -}; - -struct SSEOpHelper_epp { - SSEFunc_0_epp op[2]; - uint32_t ext_mask; -}; - -struct SSEOpHelper_eppi { - SSEFunc_0_eppi op[2]; - uint32_t ext_mask; -}; - -#define SSSE3_OP(x) { MMX_OP2(x), CPUID_EXT_SSSE3 } -#define SSE41_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, CPUID_EXT_SSE41 } -#define SSE42_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, CPUID_EXT_SSE42 } -#define SSE41_SPECIAL { { NULL, SSE_SPECIAL }, CPUID_EXT_SSE41 } -#define PCLMULQDQ_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, \ - CPUID_EXT_PCLMULQDQ } -#define AESNI_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, CPUID_EXT_AES } - -static const struct SSEOpHelper_epp sse_op_table6[256] = { - [0x00] = SSSE3_OP(pshufb), - [0x01] = SSSE3_OP(phaddw), - [0x02] = SSSE3_OP(phaddd), - [0x03] = SSSE3_OP(phaddsw), - [0x04] = SSSE3_OP(pmaddubsw), - [0x05] = SSSE3_OP(phsubw), - [0x06] = SSSE3_OP(phsubd), - [0x07] = SSSE3_OP(phsubsw), - [0x08] = SSSE3_OP(psignb), - [0x09] = SSSE3_OP(psignw), - [0x0a] = SSSE3_OP(psignd), - [0x0b] = SSSE3_OP(pmulhrsw), - [0x10] = SSE41_OP(pblendvb), - [0x14] = SSE41_OP(blendvps), - [0x15] = SSE41_OP(blendvpd), - [0x17] = SSE41_OP(ptest), - [0x1c] = SSSE3_OP(pabsb), - [0x1d] = SSSE3_OP(pabsw), - [0x1e] = SSSE3_OP(pabsd), - [0x20] = SSE41_OP(pmovsxbw), - [0x21] = SSE41_OP(pmovsxbd), - [0x22] = SSE41_OP(pmovsxbq), - [0x23] = SSE41_OP(pmovsxwd), - [0x24] = SSE41_OP(pmovsxwq), - [0x25] = SSE41_OP(pmovsxdq), - [0x28] = SSE41_OP(pmuldq), - [0x29] = SSE41_OP(pcmpeqq), - [0x2a] = SSE41_SPECIAL, /* movntqda */ - [0x2b] = SSE41_OP(packusdw), - [0x30] = SSE41_OP(pmovzxbw), - [0x31] = SSE41_OP(pmovzxbd), - [0x32] = SSE41_OP(pmovzxbq), - [0x33] = SSE41_OP(pmovzxwd), - [0x34] = SSE41_OP(pmovzxwq), - [0x35] = SSE41_OP(pmovzxdq), - [0x37] = SSE42_OP(pcmpgtq), - [0x38] = SSE41_OP(pminsb), - [0x39] = SSE41_OP(pminsd), - [0x3a] = SSE41_OP(pminuw), - [0x3b] = SSE41_OP(pminud), - [0x3c] = SSE41_OP(pmaxsb), - [0x3d] = SSE41_OP(pmaxsd), - [0x3e] = SSE41_OP(pmaxuw), - [0x3f] = SSE41_OP(pmaxud), - [0x40] = SSE41_OP(pmulld), - [0x41] = SSE41_OP(phminposuw), - [0xdb] = AESNI_OP(aesimc), - [0xdc] = AESNI_OP(aesenc), - [0xdd] = AESNI_OP(aesenclast), - [0xde] = AESNI_OP(aesdec), - [0xdf] = AESNI_OP(aesdeclast), -}; - -static const struct SSEOpHelper_eppi sse_op_table7[256] = { - [0x08] = SSE41_OP(roundps), - [0x09] = SSE41_OP(roundpd), - [0x0a] = SSE41_OP(roundss), - [0x0b] = SSE41_OP(roundsd), - [0x0c] = SSE41_OP(blendps), - [0x0d] = SSE41_OP(blendpd), - [0x0e] = SSE41_OP(pblendw), - [0x0f] = SSSE3_OP(palignr), - [0x14] = SSE41_SPECIAL, /* pextrb */ - [0x15] = SSE41_SPECIAL, /* pextrw */ - [0x16] = SSE41_SPECIAL, /* pextrd/pextrq */ - [0x17] = SSE41_SPECIAL, /* extractps */ - [0x20] = SSE41_SPECIAL, /* pinsrb */ - [0x21] = SSE41_SPECIAL, /* insertps */ - [0x22] = SSE41_SPECIAL, /* pinsrd/pinsrq */ - [0x40] = SSE41_OP(dpps), - [0x41] = SSE41_OP(dppd), - [0x42] = SSE41_OP(mpsadbw), - [0x44] = PCLMULQDQ_OP(pclmulqdq), - [0x60] = SSE42_OP(pcmpestrm), - [0x61] = SSE42_OP(pcmpestri), - [0x62] = SSE42_OP(pcmpistrm), - [0x63] = SSE42_OP(pcmpistri), - [0xdf] = AESNI_OP(aeskeygenassist), -}; - -static void gen_sse(CPUX86State *env, DisasContext *s, int b, - target_ulong pc_start) -{ - int b1, op1_offset, op2_offset, is_xmm, val; - int modrm, mod, rm, reg; - SSEFunc_0_epp sse_fn_epp; - SSEFunc_0_eppi sse_fn_eppi; - SSEFunc_0_ppi sse_fn_ppi; - SSEFunc_0_eppt sse_fn_eppt; - MemOp ot; - - b &= 0xff; - if (s->prefix & PREFIX_DATA) - b1 = 1; - else if (s->prefix & PREFIX_REPZ) - b1 = 2; - else if (s->prefix & PREFIX_REPNZ) - b1 = 3; - else - b1 = 0; - sse_fn_epp = sse_op_table1[b][b1]; - if (!sse_fn_epp) { - goto unknown_op; - } - if ((b <= 0x5f && b >= 0x10) || b == 0xc6 || b == 0xc2) { - is_xmm = 1; + /* Only require atomic with LOCK; non-parallel handled in generator. */ + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_cmpxchg_i64(old, s->A0, cmp, val, s->mem_index, MO_TEUQ); } else { - if (b1 == 0) { - /* MMX case */ - is_xmm = 0; - } else { - is_xmm = 1; - } - } - /* simple MMX/SSE operation */ - if (s->flags & HF_TS_MASK) { - gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); - return; - } - if (s->flags & HF_EM_MASK) { - illegal_op: - gen_illegal_opcode(s); - return; - } - if (is_xmm - && !(s->flags & HF_OSFXSR_MASK) - && (b != 0x38 && b != 0x3a)) { - goto unknown_op; - } - if (b == 0x0e) { - if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) { - /* If we were fully decoding this we might use illegal_op. */ - goto unknown_op; - } - /* femms */ - gen_helper_emms(cpu_env); - return; - } - if (b == 0x77) { - /* emms */ - gen_helper_emms(cpu_env); - return; - } - /* prepare MMX state (XXX: optimize by storing fptt and fptags in - the static cpu state) */ - if (!is_xmm) { - gen_helper_enter_mmx(cpu_env); + tcg_gen_nonatomic_cmpxchg_i64(old, s->A0, cmp, val, + s->mem_index, MO_TEUQ); } - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7); - if (is_xmm) { - reg |= REX_R(s); - } - mod = (modrm >> 6) & 3; - if (sse_fn_epp == SSE_SPECIAL) { - b |= (b1 << 8); - switch(b) { - case 0x0e7: /* movntq */ - if (mod == 3) { - goto illegal_op; - } - gen_lea_modrm(env, s, modrm); - gen_stq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); - break; - case 0x1e7: /* movntdq */ - case 0x02b: /* movntps */ - case 0x12b: /* movntps */ - if (mod == 3) - goto illegal_op; - gen_lea_modrm(env, s, modrm); - gen_sto_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); - break; - case 0x3f0: /* lddqu */ - if (mod == 3) - goto illegal_op; - gen_lea_modrm(env, s, modrm); - gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); - break; - case 0x22b: /* movntss */ - case 0x32b: /* movntsd */ - if (mod == 3) - goto illegal_op; - gen_lea_modrm(env, s, modrm); - if (b1 & 1) { - gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - } else { - tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(0))); - gen_op_st_v(s, MO_32, s->T0, s->A0); - } - break; - case 0x6e: /* movd mm, ea */ -#ifdef TARGET_X86_64 - if (s->dflag == MO_64) { - gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 0); - tcg_gen_st_tl(s->T0, cpu_env, - offsetof(CPUX86State, fpregs[reg].mmx)); - } else -#endif - { - gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 0); - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State,fpregs[reg].mmx)); - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_movl_mm_T0_mmx(s->ptr0, s->tmp2_i32); - } - break; - case 0x16e: /* movd xmm, ea */ -#ifdef TARGET_X86_64 - if (s->dflag == MO_64) { - gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 0); - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State,xmm_regs[reg])); - gen_helper_movq_mm_T0_xmm(s->ptr0, s->T0); - } else -#endif - { - gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 0); - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State,xmm_regs[reg])); - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_movl_mm_T0_xmm(s->ptr0, s->tmp2_i32); - } - break; - case 0x6f: /* movq mm, ea */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); - } else { - rm = (modrm & 7); - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, - offsetof(CPUX86State,fpregs[rm].mmx)); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, - offsetof(CPUX86State,fpregs[reg].mmx)); - } - break; - case 0x010: /* movups */ - case 0x110: /* movupd */ - case 0x028: /* movaps */ - case 0x128: /* movapd */ - case 0x16f: /* movdqa xmm, ea */ - case 0x26f: /* movdqu xmm, ea */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movo(s, offsetof(CPUX86State, xmm_regs[reg]), - offsetof(CPUX86State,xmm_regs[rm])); - } - break; - case 0x210: /* movss xmm, ea */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, MO_32, s->T0, s->A0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0))); - tcg_gen_movi_tl(s->T0, 0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_regs[reg].ZMM_L(1))); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2))); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_regs[reg].ZMM_L(3))); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_L(0))); - } - break; - case 0x310: /* movsd xmm, ea */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - tcg_gen_movi_tl(s->T0, 0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2))); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_regs[reg].ZMM_L(3))); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); - } - break; - case 0x012: /* movlps */ - case 0x112: /* movlpd */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - } else { - /* movhlps */ - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(1))); - } - break; - case 0x212: /* movsldup */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_L(0))); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_L(2))); - } - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(1)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(3)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_L(2))); - break; - case 0x312: /* movddup */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); - } - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); - break; - case 0x016: /* movhps */ - case 0x116: /* movhpd */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(1))); - } else { - /* movlhps */ - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); - } - break; - case 0x216: /* movshdup */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(1)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_L(1))); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(3)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_L(3))); - } - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_L(1))); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_L(3))); - break; - case 0x178: - case 0x378: - { - int bit_index, field_length; + /* Set tmp0 to match the required value of Z. */ + tcg_gen_setcond_i64(TCG_COND_EQ, cmp, old, cmp); + Z = tcg_temp_new(); + tcg_gen_trunc_i64_tl(Z, cmp); - if (b1 == 1 && reg != 0) - goto illegal_op; - field_length = x86_ldub_code(env, s) & 0x3F; - bit_index = x86_ldub_code(env, s) & 0x3F; - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State,xmm_regs[reg])); - if (b1 == 1) - gen_helper_extrq_i(cpu_env, s->ptr0, - tcg_const_i32(bit_index), - tcg_const_i32(field_length)); - else - gen_helper_insertq_i(cpu_env, s->ptr0, - tcg_const_i32(bit_index), - tcg_const_i32(field_length)); - } - break; - case 0x7e: /* movd ea, mm */ -#ifdef TARGET_X86_64 - if (s->dflag == MO_64) { - tcg_gen_ld_i64(s->T0, cpu_env, - offsetof(CPUX86State,fpregs[reg].mmx)); - gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 1); - } else -#endif - { - tcg_gen_ld32u_tl(s->T0, cpu_env, - offsetof(CPUX86State,fpregs[reg].mmx.MMX_L(0))); - gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 1); - } - break; - case 0x17e: /* movd ea, xmm */ -#ifdef TARGET_X86_64 - if (s->dflag == MO_64) { - tcg_gen_ld_i64(s->T0, cpu_env, - offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); - gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 1); - } else -#endif - { - tcg_gen_ld32u_tl(s->T0, cpu_env, - offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); - gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 1); - } - break; - case 0x27e: /* movq xmm, ea */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); - } - gen_op_movq_env_0(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1))); - break; - case 0x7f: /* movq ea, mm */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_stq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); - } else { - rm = (modrm & 7); - gen_op_movq(s, offsetof(CPUX86State, fpregs[rm].mmx), - offsetof(CPUX86State,fpregs[reg].mmx)); - } - break; - case 0x011: /* movups */ - case 0x111: /* movupd */ - case 0x029: /* movaps */ - case 0x129: /* movapd */ - case 0x17f: /* movdqa ea, xmm */ - case 0x27f: /* movdqu ea, xmm */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_sto_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movo(s, offsetof(CPUX86State, xmm_regs[rm]), - offsetof(CPUX86State,xmm_regs[reg])); - } - break; - case 0x211: /* movss ea, xmm */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - tcg_gen_ld32u_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0))); - gen_op_st_v(s, MO_32, s->T0, s->A0); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[rm].ZMM_L(0)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); - } - break; - case 0x311: /* movsd ea, xmm */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[rm].ZMM_Q(0)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); - } - break; - case 0x013: /* movlps */ - case 0x113: /* movlpd */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - } else { - goto illegal_op; - } - break; - case 0x017: /* movhps */ - case 0x117: /* movhpd */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(1))); - } else { - goto illegal_op; - } - break; - case 0x71: /* shift mm, im */ - case 0x72: - case 0x73: - case 0x171: /* shift xmm, im */ - case 0x172: - case 0x173: - val = x86_ldub_code(env, s); - if (is_xmm) { - tcg_gen_movi_tl(s->T0, val); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_t0.ZMM_L(0))); - tcg_gen_movi_tl(s->T0, 0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_t0.ZMM_L(1))); - op1_offset = offsetof(CPUX86State,xmm_t0); - } else { - tcg_gen_movi_tl(s->T0, val); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, mmx_t0.MMX_L(0))); - tcg_gen_movi_tl(s->T0, 0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, mmx_t0.MMX_L(1))); - op1_offset = offsetof(CPUX86State,mmx_t0); - } - assert(b1 < 2); - sse_fn_epp = sse_op_table2[((b - 1) & 3) * 8 + - (((modrm >> 3)) & 7)][b1]; - if (!sse_fn_epp) { - goto unknown_op; - } - if (is_xmm) { - rm = (modrm & 7) | REX_B(s); - op2_offset = offsetof(CPUX86State,xmm_regs[rm]); - } else { - rm = (modrm & 7); - op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); - } - tcg_gen_addi_ptr(s->ptr0, cpu_env, op2_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op1_offset); - sse_fn_epp(cpu_env, s->ptr0, s->ptr1); - break; - case 0x050: /* movmskps */ - rm = (modrm & 7) | REX_B(s); - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State,xmm_regs[rm])); - gen_helper_movmskps(s->tmp2_i32, cpu_env, s->ptr0); - tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); - break; - case 0x150: /* movmskpd */ - rm = (modrm & 7) | REX_B(s); - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State,xmm_regs[rm])); - gen_helper_movmskpd(s->tmp2_i32, cpu_env, s->ptr0); - tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); - break; - case 0x02a: /* cvtpi2ps */ - case 0x12a: /* cvtpi2pd */ - gen_helper_enter_mmx(cpu_env); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - op2_offset = offsetof(CPUX86State,mmx_t0); - gen_ldq_env_A0(s, op2_offset); - } else { - rm = (modrm & 7); - op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); - } - op1_offset = offsetof(CPUX86State,xmm_regs[reg]); - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - switch(b >> 8) { - case 0x0: - gen_helper_cvtpi2ps(cpu_env, s->ptr0, s->ptr1); - break; - default: - case 0x1: - gen_helper_cvtpi2pd(cpu_env, s->ptr0, s->ptr1); - break; - } - break; - case 0x22a: /* cvtsi2ss */ - case 0x32a: /* cvtsi2sd */ - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - op1_offset = offsetof(CPUX86State,xmm_regs[reg]); - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - if (ot == MO_32) { - SSEFunc_0_epi sse_fn_epi = sse_op_table3ai[(b >> 8) & 1]; - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - sse_fn_epi(cpu_env, s->ptr0, s->tmp2_i32); - } else { -#ifdef TARGET_X86_64 - SSEFunc_0_epl sse_fn_epl = sse_op_table3aq[(b >> 8) & 1]; - sse_fn_epl(cpu_env, s->ptr0, s->T0); -#else - goto illegal_op; -#endif - } - break; - case 0x02c: /* cvttps2pi */ - case 0x12c: /* cvttpd2pi */ - case 0x02d: /* cvtps2pi */ - case 0x12d: /* cvtpd2pi */ - gen_helper_enter_mmx(cpu_env); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - op2_offset = offsetof(CPUX86State,xmm_t0); - gen_ldo_env_A0(s, op2_offset); - } else { - rm = (modrm & 7) | REX_B(s); - op2_offset = offsetof(CPUX86State,xmm_regs[rm]); - } - op1_offset = offsetof(CPUX86State,fpregs[reg & 7].mmx); - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - switch(b) { - case 0x02c: - gen_helper_cvttps2pi(cpu_env, s->ptr0, s->ptr1); - break; - case 0x12c: - gen_helper_cvttpd2pi(cpu_env, s->ptr0, s->ptr1); - break; - case 0x02d: - gen_helper_cvtps2pi(cpu_env, s->ptr0, s->ptr1); - break; - case 0x12d: - gen_helper_cvtpd2pi(cpu_env, s->ptr0, s->ptr1); - break; - } - break; - case 0x22c: /* cvttss2si */ - case 0x32c: /* cvttsd2si */ - case 0x22d: /* cvtss2si */ - case 0x32d: /* cvtsd2si */ - ot = mo_64_32(s->dflag); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - if ((b >> 8) & 1) { - gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.ZMM_Q(0))); - } else { - gen_op_ld_v(s, MO_32, s->T0, s->A0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_t0.ZMM_L(0))); - } - op2_offset = offsetof(CPUX86State,xmm_t0); - } else { - rm = (modrm & 7) | REX_B(s); - op2_offset = offsetof(CPUX86State,xmm_regs[rm]); - } - tcg_gen_addi_ptr(s->ptr0, cpu_env, op2_offset); - if (ot == MO_32) { - SSEFunc_i_ep sse_fn_i_ep = - sse_op_table3bi[((b >> 7) & 2) | (b & 1)]; - sse_fn_i_ep(s->tmp2_i32, cpu_env, s->ptr0); - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); - } else { -#ifdef TARGET_X86_64 - SSEFunc_l_ep sse_fn_l_ep = - sse_op_table3bq[((b >> 7) & 2) | (b & 1)]; - sse_fn_l_ep(s->T0, cpu_env, s->ptr0); -#else - goto illegal_op; -#endif - } - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - case 0xc4: /* pinsrw */ - case 0x1c4: - s->rip_offset = 1; - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - val = x86_ldub_code(env, s); - if (b1) { - val &= 7; - tcg_gen_st16_tl(s->T0, cpu_env, - offsetof(CPUX86State,xmm_regs[reg].ZMM_W(val))); - } else { - val &= 3; - tcg_gen_st16_tl(s->T0, cpu_env, - offsetof(CPUX86State,fpregs[reg].mmx.MMX_W(val))); - } - break; - case 0xc5: /* pextrw */ - case 0x1c5: - if (mod != 3) - goto illegal_op; - ot = mo_64_32(s->dflag); - val = x86_ldub_code(env, s); - if (b1) { - val &= 7; - rm = (modrm & 7) | REX_B(s); - tcg_gen_ld16u_tl(s->T0, cpu_env, - offsetof(CPUX86State,xmm_regs[rm].ZMM_W(val))); - } else { - val &= 3; - rm = (modrm & 7); - tcg_gen_ld16u_tl(s->T0, cpu_env, - offsetof(CPUX86State,fpregs[rm].mmx.MMX_W(val))); - } - reg = ((modrm >> 3) & 7) | REX_R(s); - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - case 0x1d6: /* movq ea, xmm */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[rm].ZMM_Q(0)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); - gen_op_movq_env_0(s, - offsetof(CPUX86State, xmm_regs[rm].ZMM_Q(1))); - } - break; - case 0x2d6: /* movq2dq */ - gen_helper_enter_mmx(cpu_env); - rm = (modrm & 7); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), - offsetof(CPUX86State,fpregs[rm].mmx)); - gen_op_movq_env_0(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1))); - break; - case 0x3d6: /* movdq2q */ - gen_helper_enter_mmx(cpu_env); - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, fpregs[reg & 7].mmx), - offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); - break; - case 0xd7: /* pmovmskb */ - case 0x1d7: - if (mod != 3) - goto illegal_op; - if (b1) { - rm = (modrm & 7) | REX_B(s); - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State, xmm_regs[rm])); - gen_helper_pmovmskb_xmm(s->tmp2_i32, cpu_env, s->ptr0); - } else { - rm = (modrm & 7); - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State, fpregs[rm].mmx)); - gen_helper_pmovmskb_mmx(s->tmp2_i32, cpu_env, s->ptr0); - } - reg = ((modrm >> 3) & 7) | REX_R(s); - tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); - break; - - case 0x138: - case 0x038: - b = modrm; - if ((b & 0xf0) == 0xf0) { - goto do_0f_38_fx; - } - modrm = x86_ldub_code(env, s); - rm = modrm & 7; - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - - assert(b1 < 2); - sse_fn_epp = sse_op_table6[b].op[b1]; - if (!sse_fn_epp) { - goto unknown_op; - } - if (!(s->cpuid_ext_features & sse_op_table6[b].ext_mask)) - goto illegal_op; - - if (b1) { - op1_offset = offsetof(CPUX86State,xmm_regs[reg]); - if (mod == 3) { - op2_offset = offsetof(CPUX86State,xmm_regs[rm | REX_B(s)]); - } else { - op2_offset = offsetof(CPUX86State,xmm_t0); - gen_lea_modrm(env, s, modrm); - switch (b) { - case 0x20: case 0x30: /* pmovsxbw, pmovzxbw */ - case 0x23: case 0x33: /* pmovsxwd, pmovzxwd */ - case 0x25: case 0x35: /* pmovsxdq, pmovzxdq */ - gen_ldq_env_A0(s, op2_offset + - offsetof(ZMMReg, ZMM_Q(0))); - break; - case 0x21: case 0x31: /* pmovsxbd, pmovzxbd */ - case 0x24: case 0x34: /* pmovsxwq, pmovzxwq */ - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - tcg_gen_st_i32(s->tmp2_i32, cpu_env, op2_offset + - offsetof(ZMMReg, ZMM_L(0))); - break; - case 0x22: case 0x32: /* pmovsxbq, pmovzxbq */ - tcg_gen_qemu_ld_tl(s->tmp0, s->A0, - s->mem_index, MO_LEUW); - tcg_gen_st16_tl(s->tmp0, cpu_env, op2_offset + - offsetof(ZMMReg, ZMM_W(0))); - break; - case 0x2a: /* movntqda */ - gen_ldo_env_A0(s, op1_offset); - return; - default: - gen_ldo_env_A0(s, op2_offset); - } - } - } else { - op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); - if (mod == 3) { - op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); - } else { - op2_offset = offsetof(CPUX86State,mmx_t0); - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, op2_offset); - } - } - if (sse_fn_epp == SSE_SPECIAL) { - goto unknown_op; - } - - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - sse_fn_epp(cpu_env, s->ptr0, s->ptr1); - - if (b == 0x17) { - set_cc_op(s, CC_OP_EFLAGS); - } - break; - - case 0x238: - case 0x338: - do_0f_38_fx: - /* Various integer extensions at 0f 38 f[0-f]. */ - b = modrm | (b1 << 8); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - - switch (b) { - case 0x3f0: /* crc32 Gd,Eb */ - case 0x3f1: /* crc32 Gd,Ey */ - do_crc32: - if (!(s->cpuid_ext_features & CPUID_EXT_SSE42)) { - goto illegal_op; - } - if ((b & 0xff) == 0xf0) { - ot = MO_8; - } else if (s->dflag != MO_64) { - ot = (s->prefix & PREFIX_DATA ? MO_16 : MO_32); - } else { - ot = MO_64; - } - - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[reg]); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_helper_crc32(s->T0, s->tmp2_i32, - s->T0, tcg_const_i32(8 << ot)); - - ot = mo_64_32(s->dflag); - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - - case 0x1f0: /* crc32 or movbe */ - case 0x1f1: - /* For these insns, the f3 prefix is supposed to have priority - over the 66 prefix, but that's not what we implement above - setting b1. */ - if (s->prefix & PREFIX_REPNZ) { - goto do_crc32; - } - /* FALLTHRU */ - case 0x0f0: /* movbe Gy,My */ - case 0x0f1: /* movbe My,Gy */ - if (!(s->cpuid_ext_features & CPUID_EXT_MOVBE)) { - goto illegal_op; - } - if (s->dflag != MO_64) { - ot = (s->prefix & PREFIX_DATA ? MO_16 : MO_32); - } else { - ot = MO_64; - } - - gen_lea_modrm(env, s, modrm); - if ((b & 1) == 0) { - tcg_gen_qemu_ld_tl(s->T0, s->A0, - s->mem_index, ot | MO_BE); - gen_op_mov_reg_v(s, ot, reg, s->T0); - } else { - tcg_gen_qemu_st_tl(cpu_regs[reg], s->A0, - s->mem_index, ot | MO_BE); - } - break; - - case 0x0f2: /* andn Gy, By, Ey */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - tcg_gen_andc_tl(s->T0, s->T0, cpu_regs[s->vex_v]); - gen_op_mov_reg_v(s, ot, reg, s->T0); - gen_op_update1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); - break; - - case 0x0f7: /* bextr Gy, Ey, By */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - { - TCGv bound, zero; - - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - /* Extract START, and shift the operand. - Shifts larger than operand size get zeros. */ - tcg_gen_ext8u_tl(s->A0, cpu_regs[s->vex_v]); - tcg_gen_shr_tl(s->T0, s->T0, s->A0); - - bound = tcg_const_tl(ot == MO_64 ? 63 : 31); - zero = tcg_const_tl(0); - tcg_gen_movcond_tl(TCG_COND_LEU, s->T0, s->A0, bound, - s->T0, zero); - tcg_temp_free(zero); - - /* Extract the LEN into a mask. Lengths larger than - operand size get all ones. */ - tcg_gen_extract_tl(s->A0, cpu_regs[s->vex_v], 8, 8); - tcg_gen_movcond_tl(TCG_COND_LEU, s->A0, s->A0, bound, - s->A0, bound); - tcg_temp_free(bound); - tcg_gen_movi_tl(s->T1, 1); - tcg_gen_shl_tl(s->T1, s->T1, s->A0); - tcg_gen_subi_tl(s->T1, s->T1, 1); - tcg_gen_and_tl(s->T0, s->T0, s->T1); - - gen_op_mov_reg_v(s, ot, reg, s->T0); - gen_op_update1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); - } - break; - - case 0x0f5: /* bzhi Gy, Ey, By */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - tcg_gen_ext8u_tl(s->T1, cpu_regs[s->vex_v]); - { - TCGv bound = tcg_const_tl(ot == MO_64 ? 63 : 31); - /* Note that since we're using BMILG (in order to get O - cleared) we need to store the inverse into C. */ - tcg_gen_setcond_tl(TCG_COND_LT, cpu_cc_src, - s->T1, bound); - tcg_gen_movcond_tl(TCG_COND_GT, s->T1, s->T1, - bound, bound, s->T1); - tcg_temp_free(bound); - } - tcg_gen_movi_tl(s->A0, -1); - tcg_gen_shl_tl(s->A0, s->A0, s->T1); - tcg_gen_andc_tl(s->T0, s->T0, s->A0); - gen_op_mov_reg_v(s, ot, reg, s->T0); - gen_op_update1_cc(s); - set_cc_op(s, CC_OP_BMILGB + ot); - break; - - case 0x3f6: /* mulx By, Gy, rdx, Ey */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - switch (ot) { - default: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EDX]); - tcg_gen_mulu2_i32(s->tmp2_i32, s->tmp3_i32, - s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_regs[s->vex_v], s->tmp2_i32); - tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp3_i32); - break; -#ifdef TARGET_X86_64 - case MO_64: - tcg_gen_mulu2_i64(s->T0, s->T1, - s->T0, cpu_regs[R_EDX]); - tcg_gen_mov_i64(cpu_regs[s->vex_v], s->T0); - tcg_gen_mov_i64(cpu_regs[reg], s->T1); - break; -#endif - } - break; - - case 0x3f5: /* pdep Gy, By, Ey */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - /* Note that by zero-extending the source operand, we - automatically handle zero-extending the result. */ - if (ot == MO_64) { - tcg_gen_mov_tl(s->T1, cpu_regs[s->vex_v]); - } else { - tcg_gen_ext32u_tl(s->T1, cpu_regs[s->vex_v]); - } - gen_helper_pdep(cpu_regs[reg], s->T1, s->T0); - break; - - case 0x2f5: /* pext Gy, By, Ey */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - /* Note that by zero-extending the source operand, we - automatically handle zero-extending the result. */ - if (ot == MO_64) { - tcg_gen_mov_tl(s->T1, cpu_regs[s->vex_v]); - } else { - tcg_gen_ext32u_tl(s->T1, cpu_regs[s->vex_v]); - } - gen_helper_pext(cpu_regs[reg], s->T1, s->T0); - break; - - case 0x1f6: /* adcx Gy, Ey */ - case 0x2f6: /* adox Gy, Ey */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_ADX)) { - goto illegal_op; - } else { - TCGv carry_in, carry_out, zero; - int end_op; - - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - - /* Re-use the carry-out from a previous round. */ - carry_in = NULL; - carry_out = (b == 0x1f6 ? cpu_cc_dst : cpu_cc_src2); - switch (s->cc_op) { - case CC_OP_ADCX: - if (b == 0x1f6) { - carry_in = cpu_cc_dst; - end_op = CC_OP_ADCX; - } else { - end_op = CC_OP_ADCOX; - } - break; - case CC_OP_ADOX: - if (b == 0x1f6) { - end_op = CC_OP_ADCOX; - } else { - carry_in = cpu_cc_src2; - end_op = CC_OP_ADOX; - } - break; - case CC_OP_ADCOX: - end_op = CC_OP_ADCOX; - carry_in = carry_out; - break; - default: - end_op = (b == 0x1f6 ? CC_OP_ADCX : CC_OP_ADOX); - break; - } - /* If we can't reuse carry-out, get it out of EFLAGS. */ - if (!carry_in) { - if (s->cc_op != CC_OP_ADCX && s->cc_op != CC_OP_ADOX) { - gen_compute_eflags(s); - } - carry_in = s->tmp0; - tcg_gen_extract_tl(carry_in, cpu_cc_src, - ctz32(b == 0x1f6 ? CC_C : CC_O), 1); - } - - switch (ot) { -#ifdef TARGET_X86_64 - case MO_32: - /* If we know TL is 64-bit, and we want a 32-bit - result, just do everything in 64-bit arithmetic. */ - tcg_gen_ext32u_i64(cpu_regs[reg], cpu_regs[reg]); - tcg_gen_ext32u_i64(s->T0, s->T0); - tcg_gen_add_i64(s->T0, s->T0, cpu_regs[reg]); - tcg_gen_add_i64(s->T0, s->T0, carry_in); - tcg_gen_ext32u_i64(cpu_regs[reg], s->T0); - tcg_gen_shri_i64(carry_out, s->T0, 32); - break; -#endif - default: - /* Otherwise compute the carry-out in two steps. */ - zero = tcg_const_tl(0); - tcg_gen_add2_tl(s->T0, carry_out, - s->T0, zero, - carry_in, zero); - tcg_gen_add2_tl(cpu_regs[reg], carry_out, - cpu_regs[reg], carry_out, - s->T0, zero); - tcg_temp_free(zero); - break; - } - set_cc_op(s, end_op); - } - break; - - case 0x1f7: /* shlx Gy, Ey, By */ - case 0x2f7: /* sarx Gy, Ey, By */ - case 0x3f7: /* shrx Gy, Ey, By */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - if (ot == MO_64) { - tcg_gen_andi_tl(s->T1, cpu_regs[s->vex_v], 63); - } else { - tcg_gen_andi_tl(s->T1, cpu_regs[s->vex_v], 31); - } - if (b == 0x1f7) { - tcg_gen_shl_tl(s->T0, s->T0, s->T1); - } else if (b == 0x2f7) { - if (ot != MO_64) { - tcg_gen_ext32s_tl(s->T0, s->T0); - } - tcg_gen_sar_tl(s->T0, s->T0, s->T1); - } else { - if (ot != MO_64) { - tcg_gen_ext32u_tl(s->T0, s->T0); - } - tcg_gen_shr_tl(s->T0, s->T0, s->T1); - } - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - - case 0x0f3: - case 0x1f3: - case 0x2f3: - case 0x3f3: /* Group 17 */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - - tcg_gen_mov_tl(cpu_cc_src, s->T0); - switch (reg & 7) { - case 1: /* blsr By,Ey */ - tcg_gen_subi_tl(s->T1, s->T0, 1); - tcg_gen_and_tl(s->T0, s->T0, s->T1); - break; - case 2: /* blsmsk By,Ey */ - tcg_gen_subi_tl(s->T1, s->T0, 1); - tcg_gen_xor_tl(s->T0, s->T0, s->T1); - break; - case 3: /* blsi By, Ey */ - tcg_gen_neg_tl(s->T1, s->T0); - tcg_gen_and_tl(s->T0, s->T0, s->T1); - break; - default: - goto unknown_op; - } - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - gen_op_mov_reg_v(s, ot, s->vex_v, s->T0); - set_cc_op(s, CC_OP_BMILGB + ot); - break; - - default: - goto unknown_op; - } - break; - - case 0x03a: - case 0x13a: - b = modrm; - modrm = x86_ldub_code(env, s); - rm = modrm & 7; - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - - assert(b1 < 2); - sse_fn_eppi = sse_op_table7[b].op[b1]; - if (!sse_fn_eppi) { - goto unknown_op; - } - if (!(s->cpuid_ext_features & sse_op_table7[b].ext_mask)) - goto illegal_op; - - s->rip_offset = 1; - - if (sse_fn_eppi == SSE_SPECIAL) { - ot = mo_64_32(s->dflag); - rm = (modrm & 7) | REX_B(s); - if (mod != 3) - gen_lea_modrm(env, s, modrm); - reg = ((modrm >> 3) & 7) | REX_R(s); - val = x86_ldub_code(env, s); - switch (b) { - case 0x14: /* pextrb */ - tcg_gen_ld8u_tl(s->T0, cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_B(val & 15))); - if (mod == 3) { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - tcg_gen_qemu_st_tl(s->T0, s->A0, - s->mem_index, MO_UB); - } - break; - case 0x15: /* pextrw */ - tcg_gen_ld16u_tl(s->T0, cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_W(val & 7))); - if (mod == 3) { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - tcg_gen_qemu_st_tl(s->T0, s->A0, - s->mem_index, MO_LEUW); - } - break; - case 0x16: - if (ot == MO_32) { /* pextrd */ - tcg_gen_ld_i32(s->tmp2_i32, cpu_env, - offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(val & 3))); - if (mod == 3) { - tcg_gen_extu_i32_tl(cpu_regs[rm], s->tmp2_i32); - } else { - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - } - } else { /* pextrq */ -#ifdef TARGET_X86_64 - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, - offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(val & 1))); - if (mod == 3) { - tcg_gen_mov_i64(cpu_regs[rm], s->tmp1_i64); - } else { - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - } -#else - goto illegal_op; -#endif - } - break; - case 0x17: /* extractps */ - tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(val & 3))); - if (mod == 3) { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - tcg_gen_qemu_st_tl(s->T0, s->A0, - s->mem_index, MO_LEUL); - } - break; - case 0x20: /* pinsrb */ - if (mod == 3) { - gen_op_mov_v_reg(s, MO_32, s->T0, rm); - } else { - tcg_gen_qemu_ld_tl(s->T0, s->A0, - s->mem_index, MO_UB); - } - tcg_gen_st8_tl(s->T0, cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_B(val & 15))); - break; - case 0x21: /* insertps */ - if (mod == 3) { - tcg_gen_ld_i32(s->tmp2_i32, cpu_env, - offsetof(CPUX86State,xmm_regs[rm] - .ZMM_L((val >> 6) & 3))); - } else { - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - } - tcg_gen_st_i32(s->tmp2_i32, cpu_env, - offsetof(CPUX86State,xmm_regs[reg] - .ZMM_L((val >> 4) & 3))); - if ((val >> 0) & 1) - tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), - cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(0))); - if ((val >> 1) & 1) - tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), - cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(1))); - if ((val >> 2) & 1) - tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), - cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(2))); - if ((val >> 3) & 1) - tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), - cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(3))); - break; - case 0x22: - if (ot == MO_32) { /* pinsrd */ - if (mod == 3) { - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[rm]); - } else { - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - } - tcg_gen_st_i32(s->tmp2_i32, cpu_env, - offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(val & 3))); - } else { /* pinsrq */ -#ifdef TARGET_X86_64 - if (mod == 3) { - gen_op_mov_v_reg(s, ot, s->tmp1_i64, rm); - } else { - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - } - tcg_gen_st_i64(s->tmp1_i64, cpu_env, - offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(val & 1))); -#else - goto illegal_op; -#endif - } - break; - } - return; - } - - if (b1) { - op1_offset = offsetof(CPUX86State,xmm_regs[reg]); - if (mod == 3) { - op2_offset = offsetof(CPUX86State,xmm_regs[rm | REX_B(s)]); - } else { - op2_offset = offsetof(CPUX86State,xmm_t0); - gen_lea_modrm(env, s, modrm); - gen_ldo_env_A0(s, op2_offset); - } - } else { - op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); - if (mod == 3) { - op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); - } else { - op2_offset = offsetof(CPUX86State,mmx_t0); - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, op2_offset); - } - } - val = x86_ldub_code(env, s); - - if ((b & 0xfc) == 0x60) { /* pcmpXstrX */ - set_cc_op(s, CC_OP_EFLAGS); - - if (s->dflag == MO_64) { - /* The helper must use entire 64-bit gp registers */ - val |= 1 << 8; - } - } - - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - sse_fn_eppi(cpu_env, s->ptr0, s->ptr1, tcg_const_i32(val)); - break; - - case 0x33a: - /* Various integer extensions at 0f 3a f[0-f]. */ - b = modrm | (b1 << 8); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - - switch (b) { - case 0x3f0: /* rorx Gy,Ey, Ib */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - b = x86_ldub_code(env, s); - if (ot == MO_64) { - tcg_gen_rotri_tl(s->T0, s->T0, b & 63); - } else { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_rotri_i32(s->tmp2_i32, s->tmp2_i32, b & 31); - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); - } - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - - default: - goto unknown_op; - } - break; - - default: - unknown_op: - gen_unknown_opcode(env, s); - return; - } + /* + * Extract the result values for the register pair. + * For 32-bit, we may do this unconditionally, because on success (Z=1), + * the old value matches the previous value in EDX:EAX. For x86_64, + * the store must be conditional, because we must leave the source + * registers unchanged on success, and zero-extend the writeback + * on failure (Z=0). + */ + if (TARGET_LONG_BITS == 32) { + tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], old); } else { - /* generic MMX or SSE operation */ - switch(b) { - case 0x70: /* pshufx insn */ - case 0xc6: /* pshufx insn */ - case 0xc2: /* compare insns */ - s->rip_offset = 1; - break; - default: - break; - } - if (is_xmm) { - op1_offset = offsetof(CPUX86State,xmm_regs[reg]); - if (mod != 3) { - int sz = 4; + TCGv zero = tcg_constant_tl(0); - gen_lea_modrm(env, s, modrm); - op2_offset = offsetof(CPUX86State,xmm_t0); - - switch (b) { - case 0x50 ... 0x5a: - case 0x5c ... 0x5f: - case 0xc2: - /* Most sse scalar operations. */ - if (b1 == 2) { - sz = 2; - } else if (b1 == 3) { - sz = 3; - } - break; - - case 0x2e: /* ucomis[sd] */ - case 0x2f: /* comis[sd] */ - if (b1 == 0) { - sz = 2; - } else { - sz = 3; - } - break; - } - - switch (sz) { - case 2: - /* 32 bit access */ - gen_op_ld_v(s, MO_32, s->T0, s->A0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State,xmm_t0.ZMM_L(0))); - break; - case 3: - /* 64 bit access */ - gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.ZMM_D(0))); - break; - default: - /* 128 bit access */ - gen_ldo_env_A0(s, op2_offset); - break; - } - } else { - rm = (modrm & 7) | REX_B(s); - op2_offset = offsetof(CPUX86State,xmm_regs[rm]); - } - } else { - op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - op2_offset = offsetof(CPUX86State,mmx_t0); - gen_ldq_env_A0(s, op2_offset); - } else { - rm = (modrm & 7); - op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); - } - } - switch(b) { - case 0x0f: /* 3DNow! data insns */ - val = x86_ldub_code(env, s); - sse_fn_epp = sse_op_table5[val]; - if (!sse_fn_epp) { - goto unknown_op; - } - if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) { - goto illegal_op; - } - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - sse_fn_epp(cpu_env, s->ptr0, s->ptr1); - break; - case 0x70: /* pshufx insn */ - case 0xc6: /* pshufx insn */ - val = x86_ldub_code(env, s); - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - /* XXX: introduce a new table? */ - sse_fn_ppi = (SSEFunc_0_ppi)sse_fn_epp; - sse_fn_ppi(s->ptr0, s->ptr1, tcg_const_i32(val)); - break; - case 0xc2: - /* compare insns, bits 7:3 (7:5 for AVX) are ignored */ - val = x86_ldub_code(env, s) & 7; - sse_fn_epp = sse_op_table4[val][b1]; - - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - sse_fn_epp(cpu_env, s->ptr0, s->ptr1); - break; - case 0xf7: - /* maskmov : we must prepare A0 */ - if (mod != 3) - goto illegal_op; - tcg_gen_mov_tl(s->A0, cpu_regs[R_EDI]); - gen_extu(s->aflag, s->A0); - gen_add_A0_ds_seg(s); - - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - /* XXX: introduce a new table? */ - sse_fn_eppt = (SSEFunc_0_eppt)sse_fn_epp; - sse_fn_eppt(cpu_env, s->ptr0, s->ptr1, s->A0); - break; - default: - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - sse_fn_epp(cpu_env, s->ptr0, s->ptr1); - break; - } - if (b == 0x2e || b == 0x2f) { - set_cc_op(s, CC_OP_EFLAGS); - } + tcg_gen_extr_i64_tl(s->T0, s->T1, old); + tcg_gen_movcond_tl(TCG_COND_EQ, cpu_regs[R_EAX], Z, zero, + s->T0, cpu_regs[R_EAX]); + tcg_gen_movcond_tl(TCG_COND_EQ, cpu_regs[R_EDX], Z, zero, + s->T1, cpu_regs[R_EDX]); } + + /* Update Z. */ + gen_compute_eflags(s); + tcg_gen_deposit_tl(cpu_cc_src, cpu_cc_src, Z, ctz32(CC_Z), 1); } +#ifdef TARGET_X86_64 +static void gen_cmpxchg16b(DisasContext *s, CPUX86State *env, int modrm) +{ + MemOp mop = MO_TE | MO_128 | MO_ALIGN; + TCGv_i64 t0, t1; + TCGv_i128 cmp, val; + + gen_lea_modrm(env, s, modrm); + + cmp = tcg_temp_new_i128(); + val = tcg_temp_new_i128(); + tcg_gen_concat_i64_i128(cmp, cpu_regs[R_EAX], cpu_regs[R_EDX]); + tcg_gen_concat_i64_i128(val, cpu_regs[R_EBX], cpu_regs[R_ECX]); + + /* Only require atomic with LOCK; non-parallel handled in generator. */ + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_cmpxchg_i128(val, s->A0, cmp, val, s->mem_index, mop); + } else { + tcg_gen_nonatomic_cmpxchg_i128(val, s->A0, cmp, val, s->mem_index, mop); + } + + tcg_gen_extr_i128_i64(s->T0, s->T1, val); + + /* Determine success after the fact. */ + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + tcg_gen_xor_i64(t0, s->T0, cpu_regs[R_EAX]); + tcg_gen_xor_i64(t1, s->T1, cpu_regs[R_EDX]); + tcg_gen_or_i64(t0, t0, t1); + + /* Update Z. */ + gen_compute_eflags(s); + tcg_gen_setcondi_i64(TCG_COND_EQ, t0, t0, 0); + tcg_gen_deposit_tl(cpu_cc_src, cpu_cc_src, t0, ctz32(CC_Z), 1); + + /* + * Extract the result values for the register pair. We may do this + * unconditionally, because on success (Z=1), the old value matches + * the previous value in RDX:RAX. + */ + tcg_gen_mov_i64(cpu_regs[R_EAX], s->T0); + tcg_gen_mov_i64(cpu_regs[R_EDX], s->T1); +} +#endif + /* convert one instruction. s->base.is_jmp is set if the translation must be stopped. Return the next pc value */ -static target_ulong disas_insn(DisasContext *s, CPUState *cpu) +static bool disas_insn(DisasContext *s, CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; + CPUX86State *env = cpu_env(cpu); int b, prefixes; int shift; MemOp ot, aflag, dflag; int modrm, reg, rm, mod, op, opreg, val; - target_ulong next_eip, tval; - target_ulong pc_start = s->base.pc_next; + bool orig_cc_op_dirty = s->cc_op_dirty; + CCOp orig_cc_op = s->cc_op; + target_ulong orig_pc_save = s->pc_save; - s->pc_start = s->pc = pc_start; + s->pc = s->base.pc_next; s->override = -1; #ifdef TARGET_X86_64 - s->rex_w = false; s->rex_r = 0; s->rex_x = 0; s->rex_b = 0; @@ -4568,22 +3103,53 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) s->rip_offset = 0; /* for relative ip address */ s->vex_l = 0; s->vex_v = 0; - if (sigsetjmp(s->jmpbuf, 0) != 0) { + s->vex_w = false; + switch (sigsetjmp(s->jmpbuf, 0)) { + case 0: + break; + case 1: gen_exception_gpf(s); - return s->pc; + return true; + case 2: + /* Restore state that may affect the next instruction. */ + s->pc = s->base.pc_next; + /* + * TODO: These save/restore can be removed after the table-based + * decoder is complete; we will be decoding the insn completely + * before any code generation that might affect these variables. + */ + s->cc_op_dirty = orig_cc_op_dirty; + s->cc_op = orig_cc_op; + s->pc_save = orig_pc_save; + /* END TODO */ + s->base.num_insns--; + tcg_remove_ops_after(s->prev_insn_end); + s->base.insn_start = s->prev_insn_start; + s->base.is_jmp = DISAS_TOO_MANY; + return false; + default: + g_assert_not_reached(); } prefixes = 0; next_byte: + s->prefix = prefixes; b = x86_ldub_code(env, s); /* Collect prefixes. */ switch (b) { + default: + break; + case 0x0f: + b = x86_ldub_code(env, s) + 0x100; + break; case 0xf3: prefixes |= PREFIX_REPZ; + prefixes &= ~PREFIX_REPNZ; goto next_byte; case 0xf2: prefixes |= PREFIX_REPNZ; + prefixes &= ~PREFIX_REPZ; goto next_byte; case 0xf0: prefixes |= PREFIX_LOCK; @@ -4617,7 +3183,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (CODE64(s)) { /* REX prefix */ prefixes |= PREFIX_REX; - s->rex_w = (b >> 3) & 1; + s->vex_w = (b >> 3) & 1; s->rex_r = (b & 0x4) << 1; s->rex_x = (b & 0x2) << 2; s->rex_b = (b & 0x1) << 3; @@ -4627,58 +3193,17 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) #endif case 0xc5: /* 2-byte VEX */ case 0xc4: /* 3-byte VEX */ - /* VEX prefixes cannot be used except in 32-bit mode. - Otherwise the instruction is LES or LDS. */ if (CODE32(s) && !VM86(s)) { - static const int pp_prefix[4] = { - 0, PREFIX_DATA, PREFIX_REPZ, PREFIX_REPNZ - }; - int vex3, vex2 = x86_ldub_code(env, s); + int vex2 = x86_ldub_code(env, s); + s->pc--; /* rewind the advance_pc() x86_ldub_code() did */ if (!CODE64(s) && (vex2 & 0xc0) != 0xc0) { /* 4.1.4.6: In 32-bit mode, bits [7:6] must be 11b, otherwise the instruction is LES or LDS. */ - s->pc--; /* rewind the advance_pc() x86_ldub_code() did */ break; } - - /* 4.1.1-4.1.3: No preceding lock, 66, f2, f3, or rex prefixes. */ - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ - | PREFIX_LOCK | PREFIX_DATA | PREFIX_REX)) { - goto illegal_op; - } -#ifdef TARGET_X86_64 - s->rex_r = (~vex2 >> 4) & 8; -#endif - if (b == 0xc5) { - /* 2-byte VEX prefix: RVVVVlpp, implied 0f leading opcode byte */ - vex3 = vex2; - b = x86_ldub_code(env, s) | 0x100; - } else { - /* 3-byte VEX prefix: RXBmmmmm wVVVVlpp */ - vex3 = x86_ldub_code(env, s); -#ifdef TARGET_X86_64 - s->rex_x = (~vex2 >> 3) & 8; - s->rex_b = (~vex2 >> 2) & 8; - s->rex_w = (vex3 >> 7) & 1; -#endif - switch (vex2 & 0x1f) { - case 0x01: /* Implied 0f leading opcode bytes. */ - b = x86_ldub_code(env, s) | 0x100; - break; - case 0x02: /* Implied 0f 38 leading opcode bytes. */ - b = 0x138; - break; - case 0x03: /* Implied 0f 3a leading opcode bytes. */ - b = 0x13a; - break; - default: /* Reserved for future use. */ - goto unknown_op; - } - } - s->vex_v = (~vex3 >> 3) & 0xf; - s->vex_l = (vex3 >> 2) & 1; - prefixes |= pp_prefix[vex3 & 3] | PREFIX_VEX; + disas_insn_new(s, cpu, b); + return s->pc; } break; } @@ -4711,14 +3236,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) s->dflag = dflag; /* now check op code */ - reswitch: - switch(b) { - case 0x0f: - /**************************/ - /* extended op code */ - b = x86_ldub_code(env, s) | 0x100; - goto reswitch; - + switch (b) { /**************************/ /* arith & logic */ case 0x00 ... 0x05: @@ -4730,7 +3248,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x30 ... 0x35: case 0x38 ... 0x3d: { - int op, f, val; + int f; op = (b >> 3) & 7; f = (b >> 1) & 3; @@ -4790,8 +3308,6 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x81: case 0x83: { - int val; - ot = mo_b_d(b, dflag); modrm = x86_ldub_code(env, s); @@ -4890,13 +3406,10 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (mod == 3) { goto illegal_op; } - a0 = tcg_temp_local_new(); - t0 = tcg_temp_local_new(); + a0 = s->A0; + t0 = s->T0; label1 = gen_new_label(); - tcg_gen_mov_tl(a0, s->A0); - tcg_gen_mov_tl(t0, s->T0); - gen_set_label(label1); t1 = tcg_temp_new(); t2 = tcg_temp_new(); @@ -4904,13 +3417,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) tcg_gen_neg_tl(t1, t0); tcg_gen_atomic_cmpxchg_tl(t0, a0, t0, t1, s->mem_index, ot | MO_LE); - tcg_temp_free(t1); tcg_gen_brcond_tl(TCG_COND_NE, t0, t2, label1); - tcg_temp_free(t2); - tcg_temp_free(a0); - tcg_gen_mov_tl(s->T0, t0); - tcg_temp_free(t0); + tcg_gen_neg_tl(s->T0, t0); } else { tcg_gen_neg_tl(s->T0, s->T0); if (mod != 3) { @@ -5028,18 +3537,18 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 6: /* div */ switch(ot) { case MO_8: - gen_helper_divb_AL(cpu_env, s->T0); + gen_helper_divb_AL(tcg_env, s->T0); break; case MO_16: - gen_helper_divw_AX(cpu_env, s->T0); + gen_helper_divw_AX(tcg_env, s->T0); break; default: case MO_32: - gen_helper_divl_EAX(cpu_env, s->T0); + gen_helper_divl_EAX(tcg_env, s->T0); break; #ifdef TARGET_X86_64 case MO_64: - gen_helper_divq_EAX(cpu_env, s->T0); + gen_helper_divq_EAX(tcg_env, s->T0); break; #endif } @@ -5047,18 +3556,18 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 7: /* idiv */ switch(ot) { case MO_8: - gen_helper_idivb_AL(cpu_env, s->T0); + gen_helper_idivb_AL(tcg_env, s->T0); break; case MO_16: - gen_helper_idivw_AX(cpu_env, s->T0); + gen_helper_idivw_AX(tcg_env, s->T0); break; default: case MO_32: - gen_helper_idivl_EAX(cpu_env, s->T0); + gen_helper_idivl_EAX(tcg_env, s->T0); break; #ifdef TARGET_X86_64 case MO_64: - gen_helper_idivq_EAX(cpu_env, s->T0); + gen_helper_idivq_EAX(tcg_env, s->T0); break; #endif } @@ -5118,12 +3627,10 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (dflag == MO_16) { tcg_gen_ext16u_tl(s->T0, s->T0); } - next_eip = s->pc - s->cs_base; - tcg_gen_movi_tl(s->T1, next_eip); - gen_push_v(s, s->T1); - gen_op_jmp_v(s->T0); + gen_push_v(s, eip_next_tl(s)); + gen_op_jmp_v(s, s->T0); gen_bnd_jmp(s); - gen_jr(s, s->T0); + s->base.is_jmp = DISAS_JUMP; break; case 3: /* lcall Ev */ if (mod == 3) { @@ -5135,25 +3642,25 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) do_lcall: if (PE(s) && !VM86(s)) { tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_lcall_protected(cpu_env, s->tmp2_i32, s->T1, - tcg_const_i32(dflag - 1), - tcg_const_tl(s->pc - s->cs_base)); + gen_helper_lcall_protected(tcg_env, s->tmp2_i32, s->T1, + tcg_constant_i32(dflag - 1), + eip_next_tl(s)); } else { tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_lcall_real(cpu_env, s->tmp2_i32, s->T1, - tcg_const_i32(dflag - 1), - tcg_const_i32(s->pc - s->cs_base)); + tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); + gen_helper_lcall_real(tcg_env, s->tmp2_i32, s->tmp3_i32, + tcg_constant_i32(dflag - 1), + eip_next_i32(s)); } - tcg_gen_ld_tl(s->tmp4, cpu_env, offsetof(CPUX86State, eip)); - gen_jr(s, s->tmp4); + s->base.is_jmp = DISAS_JUMP; break; case 4: /* jmp Ev */ if (dflag == MO_16) { tcg_gen_ext16u_tl(s->T0, s->T0); } - gen_op_jmp_v(s->T0); + gen_op_jmp_v(s, s->T0); gen_bnd_jmp(s); - gen_jr(s, s->T0); + s->base.is_jmp = DISAS_JUMP; break; case 5: /* ljmp Ev */ if (mod == 3) { @@ -5165,14 +3672,13 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) do_ljmp: if (PE(s) && !VM86(s)) { tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_ljmp_protected(cpu_env, s->tmp2_i32, s->T1, - tcg_const_tl(s->pc - s->cs_base)); + gen_helper_ljmp_protected(tcg_env, s->tmp2_i32, s->T1, + eip_next_tl(s)); } else { gen_op_movl_seg_T0_vm(s, R_CS); - gen_op_jmp_v(s->T1); + gen_op_jmp_v(s, s->T1); } - tcg_gen_ld_tl(s->tmp4, cpu_env, offsetof(CPUX86State, eip)); - gen_jr(s, s->tmp4); + s->base.is_jmp = DISAS_JUMP; break; case 6: /* push Ev */ gen_push_v(s, s->T0); @@ -5226,7 +3732,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); break; default: - tcg_abort(); + g_assert_not_reached(); } break; case 0x99: /* CDQ/CWD */ @@ -5251,7 +3757,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); break; default: - tcg_abort(); + g_assert_not_reached(); } break; case 0x1af: /* imul Gv, Ev */ @@ -5339,7 +3845,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x1b0: case 0x1b1: /* cmpxchg Ev, Gv */ { - TCGv oldv, newv, cmpv; + TCGv oldv, newv, cmpv, dest; ot = mo_b_d(b, dflag); modrm = x86_ldub_code(env, s); @@ -5350,7 +3856,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) cmpv = tcg_temp_new(); gen_op_mov_v_reg(s, ot, newv, reg); tcg_gen_mov_tl(cmpv, cpu_regs[R_EAX]); - + gen_extu(ot, cmpv); if (s->prefix & PREFIX_LOCK) { if (mod == 3) { goto illegal_op; @@ -5358,39 +3864,47 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_lea_modrm(env, s, modrm); tcg_gen_atomic_cmpxchg_tl(oldv, s->A0, cmpv, newv, s->mem_index, ot | MO_LE); - gen_op_mov_reg_v(s, ot, R_EAX, oldv); } else { if (mod == 3) { rm = (modrm & 7) | REX_B(s); gen_op_mov_v_reg(s, ot, oldv, rm); + gen_extu(ot, oldv); + + /* + * Unlike the memory case, where "the destination operand receives + * a write cycle without regard to the result of the comparison", + * rm must not be touched altogether if the write fails, including + * not zero-extending it on 64-bit processors. So, precompute + * the result of a successful writeback and perform the movcond + * directly on cpu_regs. Also need to write accumulator first, in + * case rm is part of RAX too. + */ + dest = gen_op_deposit_reg_v(s, ot, rm, newv, newv); + tcg_gen_movcond_tl(TCG_COND_EQ, dest, oldv, cmpv, newv, dest); } else { gen_lea_modrm(env, s, modrm); gen_op_ld_v(s, ot, oldv, s->A0); - rm = 0; /* avoid warning */ - } - gen_extu(ot, oldv); - gen_extu(ot, cmpv); - /* store value = (old == cmp ? new : old); */ - tcg_gen_movcond_tl(TCG_COND_EQ, newv, oldv, cmpv, newv, oldv); - if (mod == 3) { - gen_op_mov_reg_v(s, ot, R_EAX, oldv); - gen_op_mov_reg_v(s, ot, rm, newv); - } else { - /* Perform an unconditional store cycle like physical cpu; - must be before changing accumulator to ensure - idempotency if the store faults and the instruction - is restarted */ + + /* + * Perform an unconditional store cycle like physical cpu; + * must be before changing accumulator to ensure + * idempotency if the store faults and the instruction + * is restarted + */ + tcg_gen_movcond_tl(TCG_COND_EQ, newv, oldv, cmpv, newv, oldv); gen_op_st_v(s, ot, newv, s->A0); - gen_op_mov_reg_v(s, ot, R_EAX, oldv); } } + /* + * Write EAX only if the cmpxchg fails; reuse newv as the destination, + * since it's dead here. + */ + dest = gen_op_deposit_reg_v(s, ot, R_EAX, newv, oldv); + tcg_gen_movcond_tl(TCG_COND_EQ, dest, oldv, cmpv, dest, newv); tcg_gen_mov_tl(cpu_cc_src, oldv); tcg_gen_mov_tl(s->cc_srcT, cmpv); tcg_gen_sub_tl(cpu_cc_dst, cmpv, oldv); set_cc_op(s, CC_OP_SUBB + ot); - tcg_temp_free(oldv); - tcg_temp_free(newv); - tcg_temp_free(cmpv); } break; case 0x1c7: /* cmpxchg8b */ @@ -5406,47 +3920,48 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (!(s->cpuid_ext_features & CPUID_EXT_CX16)) { goto illegal_op; } - gen_lea_modrm(env, s, modrm); - if ((s->prefix & PREFIX_LOCK) && - (tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_helper_cmpxchg16b(cpu_env, s->A0); - } else { - gen_helper_cmpxchg16b_unlocked(cpu_env, s->A0); - } - set_cc_op(s, CC_OP_EFLAGS); + gen_cmpxchg16b(s, env, modrm); break; } -#endif +#endif if (!(s->cpuid_features & CPUID_CX8)) { goto illegal_op; } - gen_lea_modrm(env, s, modrm); - if ((s->prefix & PREFIX_LOCK) && - (tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_helper_cmpxchg8b(cpu_env, s->A0); - } else { - gen_helper_cmpxchg8b_unlocked(cpu_env, s->A0); - } - set_cc_op(s, CC_OP_EFLAGS); + gen_cmpxchg8b(s, env, modrm); break; - case 7: /* RDSEED */ + case 7: /* RDSEED, RDPID with f3 prefix */ + if (mod != 3 || + (s->prefix & (PREFIX_LOCK | PREFIX_REPNZ))) { + goto illegal_op; + } + if (s->prefix & PREFIX_REPZ) { + if (!(s->cpuid_ext_features & CPUID_7_0_ECX_RDPID)) { + goto illegal_op; + } + gen_helper_rdpid(s->T0, tcg_env); + rm = (modrm & 7) | REX_B(s); + gen_op_mov_reg_v(s, dflag, rm, s->T0); + break; + } else { + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_RDSEED)) { + goto illegal_op; + } + goto do_rdrand; + } + case 6: /* RDRAND */ if (mod != 3 || (s->prefix & (PREFIX_LOCK | PREFIX_REPZ | PREFIX_REPNZ)) || !(s->cpuid_ext_features & CPUID_EXT_RDRAND)) { goto illegal_op; } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_rdrand(s->T0, cpu_env); + do_rdrand: + translator_io_start(&s->base); + gen_helper_rdrand(s->T0, tcg_env); rm = (modrm & 7) | REX_B(s); gen_op_mov_reg_v(s, dflag, rm, s->T0); set_cc_op(s, CC_OP_EFLAGS); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } break; default: @@ -5537,26 +4052,12 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) ot = gen_pop_T0(s); gen_movl_seg_T0(s, reg); gen_pop_update(s, ot); - /* Note that reg == R_SS in gen_movl_seg_T0 always sets is_jmp. */ - if (s->base.is_jmp) { - gen_jmp_im(s, s->pc - s->cs_base); - if (reg == R_SS) { - s->flags &= ~HF_TF_MASK; - gen_eob_inhibit_irq(s, true); - } else { - gen_eob(s); - } - } break; case 0x1a1: /* pop fs */ case 0x1a9: /* pop gs */ ot = gen_pop_T0(s); gen_movl_seg_T0(s, (b >> 3) & 7); gen_pop_update(s, ot); - if (s->base.is_jmp) { - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - } break; /**************************/ @@ -5603,16 +4104,6 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); gen_movl_seg_T0(s, reg); - /* Note that reg == R_SS in gen_movl_seg_T0 always sets is_jmp. */ - if (s->base.is_jmp) { - gen_jmp_im(s, s->pc - s->cs_base); - if (reg == R_SS) { - s->flags &= ~HF_TF_MASK; - gen_eob_inhibit_irq(s, true); - } else { - gen_eob(s); - } - } break; case 0x8c: /* mov Gv, seg */ modrm = x86_ldub_code(env, s); @@ -5683,7 +4174,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) reg = ((modrm >> 3) & 7) | REX_R(s); { AddressParts a = gen_lea_modrm_0(env, s, modrm); - TCGv ea = gen_lea_modrm_1(s, a); + TCGv ea = gen_lea_modrm_1(s, a, false); gen_lea_v_seg(s, s->aflag, ea, -1, -1); gen_op_mov_reg_v(s, dflag, reg, s->A0); } @@ -5697,16 +4188,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) target_ulong offset_addr; ot = mo_b_d(b, dflag); - switch (s->aflag) { -#ifdef TARGET_X86_64 - case MO_64: - offset_addr = x86_ldq_code(env, s); - break; -#endif - default: - offset_addr = insn_get(env, s, s->aflag); - break; - } + offset_addr = insn_get_addr(env, s, s->aflag); tcg_gen_movi_tl(s->A0, offset_addr); gen_add_A0_ds_seg(s); if ((b & 2) == 0) { @@ -5722,7 +4204,6 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) tcg_gen_mov_tl(s->A0, cpu_regs[R_EBX]); tcg_gen_ext8u_tl(s->T0, cpu_regs[R_EAX]); tcg_gen_add_tl(s->A0, s->A0, s->T0); - gen_extu(s->aflag, s->A0); gen_add_A0_ds_seg(s); gen_op_ld_v(s, MO_8, s->T0, s->A0); gen_op_mov_reg_v(s, MO_8, R_EAX, s->T0); @@ -5811,10 +4292,6 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_movl_seg_T0(s, op); /* then put the data */ gen_op_mov_reg_v(s, ot, reg, s->T1); - if (s->base.is_jmp) { - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - } break; /************************/ @@ -5892,9 +4369,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_op_mov_v_reg(s, ot, s->T1, reg); if (shift) { - TCGv imm = tcg_const_tl(x86_ldub_code(env, s)); + TCGv imm = tcg_constant_tl(x86_ldub_code(env, s)); gen_shiftd_rm_T1(s, ot, opreg, op, imm); - tcg_temp_free(imm); } else { gen_shiftd_rm_T1(s, ot, opreg, op, cpu_regs[R_ECX]); } @@ -5909,7 +4385,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (s->flags & (HF_EM_MASK | HF_TS_MASK)) { /* if CR0.EM or CR0.TS are set, generate an FPU exception */ /* XXX: what to do if illegal op ? */ - gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + gen_exception(s, EXCP07_PREX); break; } modrm = x86_ldub_code(env, s); @@ -5919,7 +4395,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (mod != 3) { /* memory op */ AddressParts a = gen_lea_modrm_0(env, s, modrm); - TCGv ea = gen_lea_modrm_1(s, a); + TCGv ea = gen_lea_modrm_1(s, a, false); TCGv last_addr = tcg_temp_new(); bool update_fdp = true; @@ -5939,30 +4415,30 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0: tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - gen_helper_flds_FT0(cpu_env, s->tmp2_i32); + gen_helper_flds_FT0(tcg_env, s->tmp2_i32); break; case 1: tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - gen_helper_fildl_FT0(cpu_env, s->tmp2_i32); + gen_helper_fildl_FT0(tcg_env, s->tmp2_i32); break; case 2: tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); - gen_helper_fldl_FT0(cpu_env, s->tmp1_i64); + gen_helper_fldl_FT0(tcg_env, s->tmp1_i64); break; case 3: default: tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LESW); - gen_helper_fildl_FT0(cpu_env, s->tmp2_i32); + gen_helper_fildl_FT0(tcg_env, s->tmp2_i32); break; } gen_helper_fp_arith_ST0_FT0(op1); if (op1 == 3) { /* fcomp needs pop */ - gen_helper_fpop(cpu_env); + gen_helper_fpop(tcg_env); } } break; @@ -5978,23 +4454,23 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0: tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - gen_helper_flds_ST0(cpu_env, s->tmp2_i32); + gen_helper_flds_ST0(tcg_env, s->tmp2_i32); break; case 1: tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - gen_helper_fildl_ST0(cpu_env, s->tmp2_i32); + gen_helper_fildl_ST0(tcg_env, s->tmp2_i32); break; case 2: tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); - gen_helper_fldl_ST0(cpu_env, s->tmp1_i64); + gen_helper_fldl_ST0(tcg_env, s->tmp1_i64); break; case 3: default: tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LESW); - gen_helper_fildl_ST0(cpu_env, s->tmp2_i32); + gen_helper_fildl_ST0(tcg_env, s->tmp2_i32); break; } break; @@ -6002,116 +4478,116 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) /* XXX: the corresponding CPUID bit must be tested ! */ switch (op >> 4) { case 1: - gen_helper_fisttl_ST0(s->tmp2_i32, cpu_env); + gen_helper_fisttl_ST0(s->tmp2_i32, tcg_env); tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); break; case 2: - gen_helper_fisttll_ST0(s->tmp1_i64, cpu_env); + gen_helper_fisttll_ST0(s->tmp1_i64, tcg_env); tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); break; case 3: default: - gen_helper_fistt_ST0(s->tmp2_i32, cpu_env); + gen_helper_fistt_ST0(s->tmp2_i32, tcg_env); tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUW); break; } - gen_helper_fpop(cpu_env); + gen_helper_fpop(tcg_env); break; default: switch (op >> 4) { case 0: - gen_helper_fsts_ST0(s->tmp2_i32, cpu_env); + gen_helper_fsts_ST0(s->tmp2_i32, tcg_env); tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); break; case 1: - gen_helper_fistl_ST0(s->tmp2_i32, cpu_env); + gen_helper_fistl_ST0(s->tmp2_i32, tcg_env); tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); break; case 2: - gen_helper_fstl_ST0(s->tmp1_i64, cpu_env); + gen_helper_fstl_ST0(s->tmp1_i64, tcg_env); tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); break; case 3: default: - gen_helper_fist_ST0(s->tmp2_i32, cpu_env); + gen_helper_fist_ST0(s->tmp2_i32, tcg_env); tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUW); break; } if ((op & 7) == 3) { - gen_helper_fpop(cpu_env); + gen_helper_fpop(tcg_env); } break; } break; case 0x0c: /* fldenv mem */ - gen_helper_fldenv(cpu_env, s->A0, - tcg_const_i32(dflag - 1)); + gen_helper_fldenv(tcg_env, s->A0, + tcg_constant_i32(dflag - 1)); update_fip = update_fdp = false; break; case 0x0d: /* fldcw mem */ tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUW); - gen_helper_fldcw(cpu_env, s->tmp2_i32); + gen_helper_fldcw(tcg_env, s->tmp2_i32); update_fip = update_fdp = false; break; case 0x0e: /* fnstenv mem */ - gen_helper_fstenv(cpu_env, s->A0, - tcg_const_i32(dflag - 1)); + gen_helper_fstenv(tcg_env, s->A0, + tcg_constant_i32(dflag - 1)); update_fip = update_fdp = false; break; case 0x0f: /* fnstcw mem */ - gen_helper_fnstcw(s->tmp2_i32, cpu_env); + gen_helper_fnstcw(s->tmp2_i32, tcg_env); tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUW); update_fip = update_fdp = false; break; case 0x1d: /* fldt mem */ - gen_helper_fldt_ST0(cpu_env, s->A0); + gen_helper_fldt_ST0(tcg_env, s->A0); break; case 0x1f: /* fstpt mem */ - gen_helper_fstt_ST0(cpu_env, s->A0); - gen_helper_fpop(cpu_env); + gen_helper_fstt_ST0(tcg_env, s->A0); + gen_helper_fpop(tcg_env); break; case 0x2c: /* frstor mem */ - gen_helper_frstor(cpu_env, s->A0, - tcg_const_i32(dflag - 1)); + gen_helper_frstor(tcg_env, s->A0, + tcg_constant_i32(dflag - 1)); update_fip = update_fdp = false; break; case 0x2e: /* fnsave mem */ - gen_helper_fsave(cpu_env, s->A0, - tcg_const_i32(dflag - 1)); + gen_helper_fsave(tcg_env, s->A0, + tcg_constant_i32(dflag - 1)); update_fip = update_fdp = false; break; case 0x2f: /* fnstsw mem */ - gen_helper_fnstsw(s->tmp2_i32, cpu_env); + gen_helper_fnstsw(s->tmp2_i32, tcg_env); tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUW); update_fip = update_fdp = false; break; case 0x3c: /* fbld */ - gen_helper_fbld_ST0(cpu_env, s->A0); + gen_helper_fbld_ST0(tcg_env, s->A0); break; case 0x3e: /* fbstp */ - gen_helper_fbst_ST0(cpu_env, s->A0); - gen_helper_fpop(cpu_env); + gen_helper_fbst_ST0(tcg_env, s->A0); + gen_helper_fpop(tcg_env); break; case 0x3d: /* fildll */ tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); - gen_helper_fildll_ST0(cpu_env, s->tmp1_i64); + gen_helper_fildll_ST0(tcg_env, s->tmp1_i64); break; case 0x3f: /* fistpll */ - gen_helper_fistll_ST0(s->tmp1_i64, cpu_env); + gen_helper_fistll_ST0(s->tmp1_i64, tcg_env); tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); - gen_helper_fpop(cpu_env); + gen_helper_fpop(tcg_env); break; default: goto unknown_op; @@ -6120,35 +4596,38 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (update_fdp) { int last_seg = s->override >= 0 ? s->override : a.def_seg; - tcg_gen_ld_i32(s->tmp2_i32, cpu_env, + tcg_gen_ld_i32(s->tmp2_i32, tcg_env, offsetof(CPUX86State, segs[last_seg].selector)); - tcg_gen_st16_i32(s->tmp2_i32, cpu_env, + tcg_gen_st16_i32(s->tmp2_i32, tcg_env, offsetof(CPUX86State, fpds)); - tcg_gen_st_tl(last_addr, cpu_env, + tcg_gen_st_tl(last_addr, tcg_env, offsetof(CPUX86State, fpdp)); } - tcg_temp_free(last_addr); } else { /* register float ops */ opreg = rm; switch (op) { case 0x08: /* fld sti */ - gen_helper_fpush(cpu_env); - gen_helper_fmov_ST0_STN(cpu_env, - tcg_const_i32((opreg + 1) & 7)); + gen_helper_fpush(tcg_env); + gen_helper_fmov_ST0_STN(tcg_env, + tcg_constant_i32((opreg + 1) & 7)); break; case 0x09: /* fxchg sti */ case 0x29: /* fxchg4 sti, undocumented op */ case 0x39: /* fxchg7 sti, undocumented op */ - gen_helper_fxchg_ST0_STN(cpu_env, tcg_const_i32(opreg)); + gen_helper_fxchg_ST0_STN(tcg_env, tcg_constant_i32(opreg)); break; case 0x0a: /* grp d9/2 */ switch (rm) { case 0: /* fnop */ - /* check exceptions (FreeBSD FPU probe) */ - gen_helper_fwait(cpu_env); + /* + * check exceptions (FreeBSD FPU probe) + * needs to be treated as I/O because of ferr_irq + */ + translator_io_start(&s->base); + gen_helper_fwait(tcg_env); update_fip = false; break; default: @@ -6158,17 +4637,17 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x0c: /* grp d9/4 */ switch (rm) { case 0: /* fchs */ - gen_helper_fchs_ST0(cpu_env); + gen_helper_fchs_ST0(tcg_env); break; case 1: /* fabs */ - gen_helper_fabs_ST0(cpu_env); + gen_helper_fabs_ST0(tcg_env); break; case 4: /* ftst */ - gen_helper_fldz_FT0(cpu_env); - gen_helper_fcom_ST0_FT0(cpu_env); + gen_helper_fldz_FT0(tcg_env); + gen_helper_fcom_ST0_FT0(tcg_env); break; case 5: /* fxam */ - gen_helper_fxam_ST0(cpu_env); + gen_helper_fxam_ST0(tcg_env); break; default: goto unknown_op; @@ -6178,32 +4657,32 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) { switch (rm) { case 0: - gen_helper_fpush(cpu_env); - gen_helper_fld1_ST0(cpu_env); + gen_helper_fpush(tcg_env); + gen_helper_fld1_ST0(tcg_env); break; case 1: - gen_helper_fpush(cpu_env); - gen_helper_fldl2t_ST0(cpu_env); + gen_helper_fpush(tcg_env); + gen_helper_fldl2t_ST0(tcg_env); break; case 2: - gen_helper_fpush(cpu_env); - gen_helper_fldl2e_ST0(cpu_env); + gen_helper_fpush(tcg_env); + gen_helper_fldl2e_ST0(tcg_env); break; case 3: - gen_helper_fpush(cpu_env); - gen_helper_fldpi_ST0(cpu_env); + gen_helper_fpush(tcg_env); + gen_helper_fldpi_ST0(tcg_env); break; case 4: - gen_helper_fpush(cpu_env); - gen_helper_fldlg2_ST0(cpu_env); + gen_helper_fpush(tcg_env); + gen_helper_fldlg2_ST0(tcg_env); break; case 5: - gen_helper_fpush(cpu_env); - gen_helper_fldln2_ST0(cpu_env); + gen_helper_fpush(tcg_env); + gen_helper_fldln2_ST0(tcg_env); break; case 6: - gen_helper_fpush(cpu_env); - gen_helper_fldz_ST0(cpu_env); + gen_helper_fpush(tcg_env); + gen_helper_fldz_ST0(tcg_env); break; default: goto unknown_op; @@ -6213,58 +4692,58 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x0e: /* grp d9/6 */ switch (rm) { case 0: /* f2xm1 */ - gen_helper_f2xm1(cpu_env); + gen_helper_f2xm1(tcg_env); break; case 1: /* fyl2x */ - gen_helper_fyl2x(cpu_env); + gen_helper_fyl2x(tcg_env); break; case 2: /* fptan */ - gen_helper_fptan(cpu_env); + gen_helper_fptan(tcg_env); break; case 3: /* fpatan */ - gen_helper_fpatan(cpu_env); + gen_helper_fpatan(tcg_env); break; case 4: /* fxtract */ - gen_helper_fxtract(cpu_env); + gen_helper_fxtract(tcg_env); break; case 5: /* fprem1 */ - gen_helper_fprem1(cpu_env); + gen_helper_fprem1(tcg_env); break; case 6: /* fdecstp */ - gen_helper_fdecstp(cpu_env); + gen_helper_fdecstp(tcg_env); break; default: case 7: /* fincstp */ - gen_helper_fincstp(cpu_env); + gen_helper_fincstp(tcg_env); break; } break; case 0x0f: /* grp d9/7 */ switch (rm) { case 0: /* fprem */ - gen_helper_fprem(cpu_env); + gen_helper_fprem(tcg_env); break; case 1: /* fyl2xp1 */ - gen_helper_fyl2xp1(cpu_env); + gen_helper_fyl2xp1(tcg_env); break; case 2: /* fsqrt */ - gen_helper_fsqrt(cpu_env); + gen_helper_fsqrt(tcg_env); break; case 3: /* fsincos */ - gen_helper_fsincos(cpu_env); + gen_helper_fsincos(tcg_env); break; case 5: /* fscale */ - gen_helper_fscale(cpu_env); + gen_helper_fscale(tcg_env); break; case 4: /* frndint */ - gen_helper_frndint(cpu_env); + gen_helper_frndint(tcg_env); break; case 6: /* fsin */ - gen_helper_fsin(cpu_env); + gen_helper_fsin(tcg_env); break; default: case 7: /* fcos */ - gen_helper_fcos(cpu_env); + gen_helper_fcos(tcg_env); break; } break; @@ -6278,34 +4757,34 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (op >= 0x20) { gen_helper_fp_arith_STN_ST0(op1, opreg); if (op >= 0x30) { - gen_helper_fpop(cpu_env); + gen_helper_fpop(tcg_env); } } else { - gen_helper_fmov_FT0_STN(cpu_env, - tcg_const_i32(opreg)); + gen_helper_fmov_FT0_STN(tcg_env, + tcg_constant_i32(opreg)); gen_helper_fp_arith_ST0_FT0(op1); } } break; case 0x02: /* fcom */ case 0x22: /* fcom2, undocumented op */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fcom_ST0_FT0(cpu_env); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fcom_ST0_FT0(tcg_env); break; case 0x03: /* fcomp */ case 0x23: /* fcomp3, undocumented op */ case 0x32: /* fcomp5, undocumented op */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fcom_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fcom_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); break; case 0x15: /* da/5 */ switch (rm) { case 1: /* fucompp */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(1)); - gen_helper_fucom_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); - gen_helper_fpop(cpu_env); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(1)); + gen_helper_fucom_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); + gen_helper_fpop(tcg_env); break; default: goto unknown_op; @@ -6318,11 +4797,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 1: /* fdisi (287 only, just do nop here) */ break; case 2: /* fclex */ - gen_helper_fclex(cpu_env); + gen_helper_fclex(tcg_env); update_fip = false; break; case 3: /* fninit */ - gen_helper_fninit(cpu_env); + gen_helper_fninit(tcg_env); update_fip = false; break; case 4: /* fsetpm (287 only, just do nop here) */ @@ -6336,8 +4815,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_update_cc_op(s); - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fucomi_ST0_FT0(cpu_env); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fucomi_ST0_FT0(tcg_env); set_cc_op(s, CC_OP_EFLAGS); break; case 0x1e: /* fcomi */ @@ -6345,52 +4824,52 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_update_cc_op(s); - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fcomi_ST0_FT0(cpu_env); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fcomi_ST0_FT0(tcg_env); set_cc_op(s, CC_OP_EFLAGS); break; case 0x28: /* ffree sti */ - gen_helper_ffree_STN(cpu_env, tcg_const_i32(opreg)); + gen_helper_ffree_STN(tcg_env, tcg_constant_i32(opreg)); break; case 0x2a: /* fst sti */ - gen_helper_fmov_STN_ST0(cpu_env, tcg_const_i32(opreg)); + gen_helper_fmov_STN_ST0(tcg_env, tcg_constant_i32(opreg)); break; case 0x2b: /* fstp sti */ case 0x0b: /* fstp1 sti, undocumented op */ case 0x3a: /* fstp8 sti, undocumented op */ case 0x3b: /* fstp9 sti, undocumented op */ - gen_helper_fmov_STN_ST0(cpu_env, tcg_const_i32(opreg)); - gen_helper_fpop(cpu_env); + gen_helper_fmov_STN_ST0(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fpop(tcg_env); break; case 0x2c: /* fucom st(i) */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fucom_ST0_FT0(cpu_env); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fucom_ST0_FT0(tcg_env); break; case 0x2d: /* fucomp st(i) */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fucom_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fucom_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); break; case 0x33: /* de/3 */ switch (rm) { case 1: /* fcompp */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(1)); - gen_helper_fcom_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); - gen_helper_fpop(cpu_env); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(1)); + gen_helper_fcom_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); + gen_helper_fpop(tcg_env); break; default: goto unknown_op; } break; case 0x38: /* ffreep sti, undocumented op */ - gen_helper_ffree_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fpop(cpu_env); + gen_helper_ffree_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fpop(tcg_env); break; case 0x3c: /* df/4 */ switch (rm) { case 0: - gen_helper_fnstsw(s->tmp2_i32, cpu_env); + gen_helper_fnstsw(s->tmp2_i32, tcg_env); tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); break; @@ -6403,9 +4882,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_update_cc_op(s); - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fucomi_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fucomi_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); set_cc_op(s, CC_OP_EFLAGS); break; case 0x3e: /* fcomip */ @@ -6413,9 +4892,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_update_cc_op(s); - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fcomi_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fcomi_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); set_cc_op(s, CC_OP_EFLAGS); break; case 0x10 ... 0x13: /* fcmovxx */ @@ -6436,7 +4915,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1); l1 = gen_new_label(); gen_jcc1_noeob(s, op1, l1); - gen_helper_fmov_ST0_STN(cpu_env, tcg_const_i32(opreg)); + gen_helper_fmov_ST0_STN(tcg_env, + tcg_constant_i32(opreg)); gen_set_label(l1); } break; @@ -6446,12 +4926,12 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } if (update_fip) { - tcg_gen_ld_i32(s->tmp2_i32, cpu_env, + tcg_gen_ld_i32(s->tmp2_i32, tcg_env, offsetof(CPUX86State, segs[R_CS].selector)); - tcg_gen_st16_i32(s->tmp2_i32, cpu_env, + tcg_gen_st16_i32(s->tmp2_i32, tcg_env, offsetof(CPUX86State, fpcs)); - tcg_gen_st_tl(tcg_constant_tl(pc_start - s->cs_base), - cpu_env, offsetof(CPUX86State, fpip)); + tcg_gen_st_tl(eip_cur_tl(s), + tcg_env, offsetof(CPUX86State, fpip)); } } break; @@ -6462,7 +4942,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0xa5: ot = mo_b_d(b, dflag); if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_movs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); + gen_repz_movs(s, ot); } else { gen_movs(s, ot); } @@ -6471,8 +4951,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0xaa: /* stosS */ case 0xab: ot = mo_b_d(b, dflag); + gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_stos(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); + gen_repz_stos(s, ot); } else { gen_stos(s, ot); } @@ -6481,7 +4962,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0xad: ot = mo_b_d(b, dflag); if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_lods(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); + gen_repz_lods(s, ot); } else { gen_lods(s, ot); } @@ -6489,10 +4970,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0xae: /* scasS */ case 0xaf: ot = mo_b_d(b, dflag); + gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); if (prefixes & PREFIX_REPNZ) { - gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1); + gen_repz_scas(s, ot, 1); } else if (prefixes & PREFIX_REPZ) { - gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0); + gen_repz_scas(s, ot, 0); } else { gen_scas(s, ot); } @@ -6502,9 +4984,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0xa7: ot = mo_b_d(b, dflag); if (prefixes & PREFIX_REPNZ) { - gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1); + gen_repz_cmps(s, ot, 1); } else if (prefixes & PREFIX_REPZ) { - gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0); + gen_repz_cmps(s, ot, 0); } else { gen_cmps(s, ot); } @@ -6518,17 +5000,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) SVM_IOIO_TYPE_MASK | SVM_IOIO_STR_MASK)) { break; } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&s->base); if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_ins(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); - /* jump generated by gen_repz_ins */ + gen_repz_ins(s, ot); } else { gen_ins(s, ot); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } } break; case 0x6e: /* outsS */ @@ -6539,17 +5015,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (!gen_check_io(s, ot, s->tmp2_i32, SVM_IOIO_STR_MASK)) { break; } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&s->base); if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_outs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); - /* jump generated by gen_repz_outs */ + gen_repz_outs(s, ot); } else { gen_outs(s, ot); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } } break; @@ -6564,15 +5034,10 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (!gen_check_io(s, ot, s->tmp2_i32, SVM_IOIO_TYPE_MASK)) { break; } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&s->base); gen_helper_in_func(ot, s->T1, s->tmp2_i32); gen_op_mov_reg_v(s, ot, R_EAX, s->T1); gen_bpt_io(s, s->tmp2_i32, ot); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } break; case 0xe6: case 0xe7: @@ -6582,16 +5047,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (!gen_check_io(s, ot, s->tmp2_i32, 0)) { break; } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&s->base); gen_op_mov_v_reg(s, ot, s->T1, R_EAX); tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); gen_bpt_io(s, s->tmp2_i32, ot); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } break; case 0xec: case 0xed: @@ -6601,15 +5061,10 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (!gen_check_io(s, ot, s->tmp2_i32, SVM_IOIO_TYPE_MASK)) { break; } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&s->base); gen_helper_in_func(ot, s->T1, s->tmp2_i32); gen_op_mov_reg_v(s, ot, R_EAX, s->T1); gen_bpt_io(s, s->tmp2_i32, ot); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } break; case 0xee: case 0xef: @@ -6619,16 +5074,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (!gen_check_io(s, ot, s->tmp2_i32, 0)) { break; } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&s->base); gen_op_mov_v_reg(s, ot, s->T1, R_EAX); tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); gen_bpt_io(s, s->tmp2_i32, ot); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } break; /************************/ @@ -6638,33 +5088,33 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) ot = gen_pop_T0(s); gen_stack_update(s, val + (1 << ot)); /* Note that gen_pop_T0 uses a zero-extending load. */ - gen_op_jmp_v(s->T0); + gen_op_jmp_v(s, s->T0); gen_bnd_jmp(s); - gen_jr(s, s->T0); + s->base.is_jmp = DISAS_JUMP; break; case 0xc3: /* ret */ ot = gen_pop_T0(s); gen_pop_update(s, ot); /* Note that gen_pop_T0 uses a zero-extending load. */ - gen_op_jmp_v(s->T0); + gen_op_jmp_v(s, s->T0); gen_bnd_jmp(s); - gen_jr(s, s->T0); + s->base.is_jmp = DISAS_JUMP; break; case 0xca: /* lret im */ val = x86_ldsw_code(env, s); do_lret: if (PE(s) && !VM86(s)) { gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_lret_protected(cpu_env, tcg_const_i32(dflag - 1), - tcg_const_i32(val)); + gen_update_eip_cur(s); + gen_helper_lret_protected(tcg_env, tcg_constant_i32(dflag - 1), + tcg_constant_i32(val)); } else { gen_stack_A0(s); /* pop offset */ gen_op_ld_v(s, dflag, s->T0, s->A0); /* NOTE: keeping EIP updated is not a problem in case of exception */ - gen_op_jmp_v(s->T0); + gen_op_jmp_v(s, s->T0); /* pop selector */ gen_add_A0_im(s, 1 << dflag); gen_op_ld_v(s, dflag, s->T0, s->A0); @@ -6672,7 +5122,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) /* add stack offset */ gen_stack_update(s, val + (2 << dflag)); } - gen_eob(s); + s->base.is_jmp = DISAS_EOB_ONLY; break; case 0xcb: /* lret */ val = 0; @@ -6684,32 +5134,22 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (!check_vm86_iopl(s)) { break; } - gen_helper_iret_real(cpu_env, tcg_const_i32(dflag - 1)); + gen_helper_iret_real(tcg_env, tcg_constant_i32(dflag - 1)); } else { - gen_helper_iret_protected(cpu_env, tcg_const_i32(dflag - 1), - tcg_const_i32(s->pc - s->cs_base)); + gen_helper_iret_protected(tcg_env, tcg_constant_i32(dflag - 1), + eip_next_i32(s)); } set_cc_op(s, CC_OP_EFLAGS); - gen_eob(s); + s->base.is_jmp = DISAS_EOB_ONLY; break; case 0xe8: /* call im */ { - if (dflag != MO_16) { - tval = (int32_t)insn_get(env, s, MO_32); - } else { - tval = (int16_t)insn_get(env, s, MO_16); - } - next_eip = s->pc - s->cs_base; - tval += next_eip; - if (dflag == MO_16) { - tval &= 0xffff; - } else if (!CODE64(s)) { - tval &= 0xffffffff; - } - tcg_gen_movi_tl(s->T0, next_eip); - gen_push_v(s, s->T0); + int diff = (dflag != MO_16 + ? (int32_t)insn_get(env, s, MO_32) + : (int16_t)insn_get(env, s, MO_16)); + gen_push_v(s, eip_next_tl(s)); gen_bnd_jmp(s); - gen_jmp(s, tval); + gen_jmp_rel(s, dflag, diff, 0); } break; case 0x9a: /* lcall im */ @@ -6727,19 +5167,13 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } goto do_lcall; case 0xe9: /* jmp im */ - if (dflag != MO_16) { - tval = (int32_t)insn_get(env, s, MO_32); - } else { - tval = (int16_t)insn_get(env, s, MO_16); + { + int diff = (dflag != MO_16 + ? (int32_t)insn_get(env, s, MO_32) + : (int16_t)insn_get(env, s, MO_16)); + gen_bnd_jmp(s); + gen_jmp_rel(s, dflag, diff, 0); } - tval += s->pc - s->cs_base; - if (dflag == MO_16) { - tval &= 0xffff; - } else if (!CODE64(s)) { - tval &= 0xffffffff; - } - gen_bnd_jmp(s); - gen_jmp(s, tval); break; case 0xea: /* ljmp im */ { @@ -6756,30 +5190,26 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } goto do_ljmp; case 0xeb: /* jmp Jb */ - tval = (int8_t)insn_get(env, s, MO_8); - tval += s->pc - s->cs_base; - if (dflag == MO_16) { - tval &= 0xffff; + { + int diff = (int8_t)insn_get(env, s, MO_8); + gen_jmp_rel(s, dflag, diff, 0); } - gen_jmp(s, tval); break; case 0x70 ... 0x7f: /* jcc Jb */ - tval = (int8_t)insn_get(env, s, MO_8); - goto do_jcc; + { + int diff = (int8_t)insn_get(env, s, MO_8); + gen_bnd_jmp(s); + gen_jcc(s, b, diff); + } + break; case 0x180 ... 0x18f: /* jcc Jv */ - if (dflag != MO_16) { - tval = (int32_t)insn_get(env, s, MO_32); - } else { - tval = (int16_t)insn_get(env, s, MO_16); + { + int diff = (dflag != MO_16 + ? (int32_t)insn_get(env, s, MO_32) + : (int16_t)insn_get(env, s, MO_16)); + gen_bnd_jmp(s); + gen_jcc(s, b, diff); } - do_jcc: - next_eip = s->pc - s->cs_base; - tval += next_eip; - if (dflag == MO_16) { - tval &= 0xffff; - } - gen_bnd_jmp(s); - gen_jcc(s, b, tval, next_eip); break; case 0x190 ... 0x19f: /* setcc Gv */ @@ -6794,7 +5224,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) ot = dflag; modrm = x86_ldub_code(env, s); reg = ((modrm >> 3) & 7) | REX_R(s); - gen_cmovcc1(env, s, ot, b, modrm, reg); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + gen_cmovcc1(s, b ^ 1, s->T0, cpu_regs[reg]); + gen_op_mov_reg_v(s, ot, reg, s->T0); break; /************************/ @@ -6803,70 +5235,36 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_svm_check_intercept(s, SVM_EXIT_PUSHF); if (check_vm86_iopl(s)) { gen_update_cc_op(s); - gen_helper_read_eflags(s->T0, cpu_env); + gen_helper_read_eflags(s->T0, tcg_env); gen_push_v(s, s->T0); } break; case 0x9d: /* popf */ gen_svm_check_intercept(s, SVM_EXIT_POPF); if (check_vm86_iopl(s)) { - ot = gen_pop_T0(s); + int mask = TF_MASK | AC_MASK | ID_MASK | NT_MASK; + if (CPL(s) == 0) { - if (dflag != MO_16) { - gen_helper_write_eflags(cpu_env, s->T0, - tcg_const_i32((TF_MASK | AC_MASK | - ID_MASK | NT_MASK | - IF_MASK | - IOPL_MASK))); - } else { - gen_helper_write_eflags(cpu_env, s->T0, - tcg_const_i32((TF_MASK | AC_MASK | - ID_MASK | NT_MASK | - IF_MASK | IOPL_MASK) - & 0xffff)); - } - } else { - if (CPL(s) <= IOPL(s)) { - if (dflag != MO_16) { - gen_helper_write_eflags(cpu_env, s->T0, - tcg_const_i32((TF_MASK | - AC_MASK | - ID_MASK | - NT_MASK | - IF_MASK))); - } else { - gen_helper_write_eflags(cpu_env, s->T0, - tcg_const_i32((TF_MASK | - AC_MASK | - ID_MASK | - NT_MASK | - IF_MASK) - & 0xffff)); - } - } else { - if (dflag != MO_16) { - gen_helper_write_eflags(cpu_env, s->T0, - tcg_const_i32((TF_MASK | AC_MASK | - ID_MASK | NT_MASK))); - } else { - gen_helper_write_eflags(cpu_env, s->T0, - tcg_const_i32((TF_MASK | AC_MASK | - ID_MASK | NT_MASK) - & 0xffff)); - } - } + mask |= IF_MASK | IOPL_MASK; + } else if (CPL(s) <= IOPL(s)) { + mask |= IF_MASK; } + if (dflag == MO_16) { + mask &= 0xffff; + } + + ot = gen_pop_T0(s); + gen_helper_write_eflags(tcg_env, s->T0, tcg_constant_i32(mask)); gen_pop_update(s, ot); set_cc_op(s, CC_OP_EFLAGS); /* abort translation because TF/AC flag may change */ - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); + s->base.is_jmp = DISAS_EOB_NEXT; } break; case 0x9e: /* sahf */ if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) goto illegal_op; - gen_op_mov_v_reg(s, MO_8, s->T0, R_AH); + tcg_gen_shri_tl(s->T0, cpu_regs[R_EAX], 8); gen_compute_eflags(s); tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O); tcg_gen_andi_tl(s->T0, s->T0, CC_S | CC_Z | CC_A | CC_P | CC_C); @@ -6878,7 +5276,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_compute_eflags(s); /* Note: gen_compute_eflags() only gives the condition codes */ tcg_gen_ori_tl(s->T0, cpu_cc_src, 0x02); - gen_op_mov_reg_v(s, MO_8, R_AH, s->T0); + tcg_gen_deposit_tl(cpu_regs[R_EAX], cpu_regs[R_EAX], s->T0, 8, 8); break; case 0xf5: /* cmc */ gen_compute_eflags(s); @@ -6894,11 +5292,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; case 0xfc: /* cld */ tcg_gen_movi_i32(s->tmp2_i32, 1); - tcg_gen_st_i32(s->tmp2_i32, cpu_env, offsetof(CPUX86State, df)); + tcg_gen_st_i32(s->tmp2_i32, tcg_env, offsetof(CPUX86State, df)); break; case 0xfd: /* std */ tcg_gen_movi_i32(s->tmp2_i32, -1); - tcg_gen_st_i32(s->tmp2_i32, cpu_env, offsetof(CPUX86State, df)); + tcg_gen_st_i32(s->tmp2_i32, tcg_env, offsetof(CPUX86State, df)); break; /************************/ @@ -6949,7 +5347,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_exts(ot, s->T1); tcg_gen_sari_tl(s->tmp0, s->T1, 3 + ot); tcg_gen_shli_tl(s->tmp0, s->tmp0, ot); - tcg_gen_add_tl(s->A0, gen_lea_modrm_1(s, a), s->tmp0); + tcg_gen_add_tl(s->A0, gen_lea_modrm_1(s, a, false), s->tmp0); gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); if (!(s->prefix & PREFIX_LOCK)) { gen_op_ld_v(s, ot, s->T0, s->A0); @@ -6964,7 +5362,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (s->prefix & PREFIX_LOCK) { switch (op) { case 0: /* bt */ - /* Needs no atomic ops; we surpressed the normal + /* Needs no atomic ops; we suppressed the normal memory load for LOCK above so do it now. */ gen_op_ld_v(s, ot, s->T0, s->A0); break; @@ -7096,28 +5494,28 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (CODE64(s)) goto illegal_op; gen_update_cc_op(s); - gen_helper_daa(cpu_env); + gen_helper_daa(tcg_env); set_cc_op(s, CC_OP_EFLAGS); break; case 0x2f: /* das */ if (CODE64(s)) goto illegal_op; gen_update_cc_op(s); - gen_helper_das(cpu_env); + gen_helper_das(tcg_env); set_cc_op(s, CC_OP_EFLAGS); break; case 0x37: /* aaa */ if (CODE64(s)) goto illegal_op; gen_update_cc_op(s); - gen_helper_aaa(cpu_env); + gen_helper_aaa(tcg_env); set_cc_op(s, CC_OP_EFLAGS); break; case 0x3f: /* aas */ if (CODE64(s)) goto illegal_op; gen_update_cc_op(s); - gen_helper_aas(cpu_env); + gen_helper_aas(tcg_env); set_cc_op(s, CC_OP_EFLAGS); break; case 0xd4: /* aam */ @@ -7125,9 +5523,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; val = x86_ldub_code(env, s); if (val == 0) { - gen_exception(s, EXCP00_DIVZ, pc_start - s->cs_base); + gen_exception(s, EXCP00_DIVZ); } else { - gen_helper_aam(cpu_env, tcg_const_i32(val)); + gen_helper_aam(tcg_env, tcg_constant_i32(val)); set_cc_op(s, CC_OP_LOGICB); } break; @@ -7135,7 +5533,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (CODE64(s)) goto illegal_op; val = x86_ldub_code(env, s); - gen_helper_aad(cpu_env, tcg_const_i32(val)); + gen_helper_aad(tcg_env, tcg_constant_i32(val)); set_cc_op(s, CC_OP_LOGICB); break; /************************/ @@ -7151,34 +5549,36 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } if (prefixes & PREFIX_REPZ) { gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_pause(cpu_env, tcg_const_i32(s->pc - pc_start)); + gen_update_eip_cur(s); + gen_helper_pause(tcg_env, cur_insn_len_i32(s)); s->base.is_jmp = DISAS_NORETURN; } break; case 0x9b: /* fwait */ if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) == (HF_MP_MASK | HF_TS_MASK)) { - gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + gen_exception(s, EXCP07_PREX); } else { - gen_helper_fwait(cpu_env); + /* needs to be treated as I/O because of ferr_irq */ + translator_io_start(&s->base); + gen_helper_fwait(tcg_env); } break; case 0xcc: /* int3 */ - gen_interrupt(s, EXCP03_INT3, pc_start - s->cs_base, s->pc - s->cs_base); + gen_interrupt(s, EXCP03_INT3); break; case 0xcd: /* int N */ val = x86_ldub_code(env, s); if (check_vm86_iopl(s)) { - gen_interrupt(s, val, pc_start - s->cs_base, s->pc - s->cs_base); + gen_interrupt(s, val); } break; case 0xce: /* into */ if (CODE64(s)) goto illegal_op; gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_into(cpu_env, tcg_const_i32(s->pc - pc_start)); + gen_update_eip_cur(s); + gen_helper_into(tcg_env, cur_insn_len_i32(s)); break; #ifdef WANT_ICEBP case 0xf1: /* icebp (undocumented, exits to external debugger) */ @@ -7188,14 +5588,14 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) #endif case 0xfa: /* cli */ if (check_iopl(s)) { - gen_helper_cli(cpu_env); + gen_reset_eflags(s, IF_MASK); } break; case 0xfb: /* sti */ if (check_iopl(s)) { - gen_helper_sti(cpu_env); + gen_set_eflags(s, IF_MASK); /* interruptions are enabled only the first insn after sti */ - gen_jmp_im(s, s->pc - s->cs_base); + gen_update_eip_next(s); gen_eob_inhibit_irq(s, true); } break; @@ -7212,9 +5612,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_lea_modrm(env, s, modrm); tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); if (ot == MO_16) { - gen_helper_boundw(cpu_env, s->A0, s->tmp2_i32); + gen_helper_boundw(tcg_env, s->A0, s->tmp2_i32); } else { - gen_helper_boundl(cpu_env, s->A0, s->tmp2_i32); + gen_helper_boundl(tcg_env, s->A0, s->tmp2_i32); } break; case 0x1c8 ... 0x1cf: /* bswap reg */ @@ -7239,116 +5639,108 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0xe2: /* loop */ case 0xe3: /* jecxz */ { - TCGLabel *l1, *l2, *l3; - - tval = (int8_t)insn_get(env, s, MO_8); - next_eip = s->pc - s->cs_base; - tval += next_eip; - if (dflag == MO_16) { - tval &= 0xffff; - } + TCGLabel *l1, *l2; + int diff = (int8_t)insn_get(env, s, MO_8); l1 = gen_new_label(); l2 = gen_new_label(); - l3 = gen_new_label(); gen_update_cc_op(s); b &= 3; switch(b) { case 0: /* loopnz */ case 1: /* loopz */ gen_op_add_reg_im(s, s->aflag, R_ECX, -1); - gen_op_jz_ecx(s, s->aflag, l3); + gen_op_jz_ecx(s, l2); gen_jcc1(s, (JCC_Z << 1) | (b ^ 1), l1); break; case 2: /* loop */ gen_op_add_reg_im(s, s->aflag, R_ECX, -1); - gen_op_jnz_ecx(s, s->aflag, l1); + gen_op_jnz_ecx(s, l1); break; default: case 3: /* jcxz */ - gen_op_jz_ecx(s, s->aflag, l1); + gen_op_jz_ecx(s, l1); break; } - gen_set_label(l3); - gen_jmp_im(s, next_eip); - tcg_gen_br(l2); + gen_set_label(l2); + gen_jmp_rel_csize(s, 0, 1); gen_set_label(l1); - gen_jmp_im(s, tval); - gen_set_label(l2); - gen_eob(s); + gen_jmp_rel(s, dflag, diff, 0); } break; case 0x130: /* wrmsr */ case 0x132: /* rdmsr */ if (check_cpl0(s)) { gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); + gen_update_eip_cur(s); if (b & 2) { - gen_helper_rdmsr(cpu_env); + gen_helper_rdmsr(tcg_env); } else { - gen_helper_wrmsr(cpu_env); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); + gen_helper_wrmsr(tcg_env); + s->base.is_jmp = DISAS_EOB_NEXT; } } break; case 0x131: /* rdtsc */ gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_rdtsc(cpu_env); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } + gen_update_eip_cur(s); + translator_io_start(&s->base); + gen_helper_rdtsc(tcg_env); break; case 0x133: /* rdpmc */ gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_rdpmc(cpu_env); + gen_update_eip_cur(s); + gen_helper_rdpmc(tcg_env); s->base.is_jmp = DISAS_NORETURN; break; case 0x134: /* sysenter */ - /* For Intel SYSENTER is valid on 64-bit */ - if (CODE64(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) + /* For AMD SYSENTER is not valid in long mode */ + if (LMA(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) { goto illegal_op; + } if (!PE(s)) { gen_exception_gpf(s); } else { - gen_helper_sysenter(cpu_env); - gen_eob(s); + gen_helper_sysenter(tcg_env); + s->base.is_jmp = DISAS_EOB_ONLY; } break; case 0x135: /* sysexit */ - /* For Intel SYSEXIT is valid on 64-bit */ - if (CODE64(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) + /* For AMD SYSEXIT is not valid in long mode */ + if (LMA(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) { goto illegal_op; - if (!PE(s)) { + } + if (!PE(s) || CPL(s) != 0) { gen_exception_gpf(s); } else { - gen_helper_sysexit(cpu_env, tcg_const_i32(dflag - 1)); - gen_eob(s); + gen_helper_sysexit(tcg_env, tcg_constant_i32(dflag - 1)); + s->base.is_jmp = DISAS_EOB_ONLY; } break; -#ifdef TARGET_X86_64 case 0x105: /* syscall */ - /* XXX: is it usable in real mode ? */ + /* For Intel SYSCALL is only valid in long mode */ + if (!LMA(s) && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1) { + goto illegal_op; + } gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_syscall(cpu_env, tcg_const_i32(s->pc - pc_start)); + gen_update_eip_cur(s); + gen_helper_syscall(tcg_env, cur_insn_len_i32(s)); /* TF handling for the syscall insn is different. The TF bit is checked after the syscall insn completes. This allows #DB to not be generated after one has entered CPL0 if TF is set in FMASK. */ gen_eob_worker(s, false, true); break; case 0x107: /* sysret */ - if (!PE(s)) { + /* For Intel SYSRET is only valid in long mode */ + if (!LMA(s) && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1) { + goto illegal_op; + } + if (!PE(s) || CPL(s) != 0) { gen_exception_gpf(s); } else { - gen_helper_sysret(cpu_env, tcg_const_i32(dflag - 1)); + gen_helper_sysret(tcg_env, tcg_constant_i32(dflag - 1)); /* condition codes are modified only in long mode */ if (LMA(s)) { set_cc_op(s, CC_OP_EFLAGS); @@ -7360,17 +5752,16 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_eob_worker(s, false, true); } break; -#endif case 0x1a2: /* cpuid */ gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_cpuid(cpu_env); + gen_update_eip_cur(s); + gen_helper_cpuid(tcg_env); break; case 0xf4: /* hlt */ if (check_cpl0(s)) { gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_hlt(cpu_env, tcg_const_i32(s->pc - pc_start)); + gen_update_eip_cur(s); + gen_helper_hlt(tcg_env, cur_insn_len_i32(s)); s->base.is_jmp = DISAS_NORETURN; } break; @@ -7386,7 +5777,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_svm_check_intercept(s, SVM_EXIT_LDTR_READ); - tcg_gen_ld32u_tl(s->T0, cpu_env, + tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, ldt.selector)); ot = mod == 3 ? dflag : MO_16; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); @@ -7398,7 +5789,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_svm_check_intercept(s, SVM_EXIT_LDTR_WRITE); gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_lldt(cpu_env, s->tmp2_i32); + gen_helper_lldt(tcg_env, s->tmp2_i32); } break; case 1: /* str */ @@ -7408,7 +5799,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_svm_check_intercept(s, SVM_EXIT_TR_READ); - tcg_gen_ld32u_tl(s->T0, cpu_env, + tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, tr.selector)); ot = mod == 3 ? dflag : MO_16; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); @@ -7420,7 +5811,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_svm_check_intercept(s, SVM_EXIT_TR_WRITE); gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_ltr(cpu_env, s->tmp2_i32); + gen_helper_ltr(tcg_env, s->tmp2_i32); } break; case 4: /* verr */ @@ -7430,9 +5821,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); gen_update_cc_op(s); if (op == 4) { - gen_helper_verr(cpu_env, s->T0); + gen_helper_verr(tcg_env, s->T0); } else { - gen_helper_verw(cpu_env, s->T0); + gen_helper_verw(tcg_env, s->T0); } set_cc_op(s, CC_OP_EFLAGS); break; @@ -7451,10 +5842,10 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_svm_check_intercept(s, SVM_EXIT_GDTR_READ); gen_lea_modrm(env, s, modrm); tcg_gen_ld32u_tl(s->T0, - cpu_env, offsetof(CPUX86State, gdt.limit)); + tcg_env, offsetof(CPUX86State, gdt.limit)); gen_op_st_v(s, MO_16, s->T0, s->A0); gen_add_A0_im(s, 2); - tcg_gen_ld_tl(s->T0, cpu_env, offsetof(CPUX86State, gdt.base)); + tcg_gen_ld_tl(s->T0, tcg_env, offsetof(CPUX86State, gdt.base)); if (dflag == MO_16) { tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); } @@ -7466,11 +5857,10 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); + gen_update_eip_cur(s); tcg_gen_mov_tl(s->A0, cpu_regs[R_EAX]); - gen_extu(s->aflag, s->A0); gen_add_A0_ds_seg(s); - gen_helper_monitor(cpu_env, s->A0); + gen_helper_monitor(tcg_env, s->A0); break; case 0xc9: /* mwait */ @@ -7478,8 +5868,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_mwait(cpu_env, tcg_const_i32(s->pc - pc_start)); + gen_update_eip_cur(s); + gen_helper_mwait(tcg_env, cur_insn_len_i32(s)); s->base.is_jmp = DISAS_NORETURN; break; @@ -7488,9 +5878,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) || CPL(s) != 0) { goto illegal_op; } - gen_helper_clac(cpu_env); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); + gen_reset_eflags(s, AC_MASK); + s->base.is_jmp = DISAS_EOB_NEXT; break; case 0xcb: /* stac */ @@ -7498,9 +5887,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) || CPL(s) != 0) { goto illegal_op; } - gen_helper_stac(cpu_env); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); + gen_set_eflags(s, AC_MASK); + s->base.is_jmp = DISAS_EOB_NEXT; break; CASE_MODRM_MEM_OP(1): /* sidt */ @@ -7509,10 +5897,10 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } gen_svm_check_intercept(s, SVM_EXIT_IDTR_READ); gen_lea_modrm(env, s, modrm); - tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, idt.limit)); + tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, idt.limit)); gen_op_st_v(s, MO_16, s->T0, s->A0); gen_add_A0_im(s, 2); - tcg_gen_ld_tl(s->T0, cpu_env, offsetof(CPUX86State, idt.base)); + tcg_gen_ld_tl(s->T0, tcg_env, offsetof(CPUX86State, idt.base)); if (dflag == MO_16) { tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); } @@ -7526,7 +5914,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); - gen_helper_xgetbv(s->tmp1_i64, cpu_env, s->tmp2_i32); + gen_helper_xgetbv(s->tmp1_i64, tcg_env, s->tmp2_i32); tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], s->tmp1_i64); break; @@ -7536,16 +5924,16 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) | PREFIX_REPZ | PREFIX_REPNZ))) { goto illegal_op; } + gen_svm_check_intercept(s, SVM_EXIT_XSETBV); if (!check_cpl0(s)) { break; } tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], cpu_regs[R_EDX]); tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); - gen_helper_xsetbv(cpu_env, s->tmp2_i32, s->tmp1_i64); + gen_helper_xsetbv(tcg_env, s->tmp2_i32, s->tmp1_i64); /* End TB because translation flags may change. */ - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); + s->base.is_jmp = DISAS_EOB_NEXT; break; case 0xd8: /* VMRUN */ @@ -7556,9 +5944,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_vmrun(cpu_env, tcg_const_i32(s->aflag - 1), - tcg_const_i32(s->pc - pc_start)); + gen_update_eip_cur(s); + gen_helper_vmrun(tcg_env, tcg_constant_i32(s->aflag - 1), + cur_insn_len_i32(s)); tcg_gen_exit_tb(NULL, 0); s->base.is_jmp = DISAS_NORETURN; break; @@ -7568,8 +5956,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_vmmcall(cpu_env); + gen_update_eip_cur(s); + gen_helper_vmmcall(tcg_env); break; case 0xda: /* VMLOAD */ @@ -7580,8 +5968,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_vmload(cpu_env, tcg_const_i32(s->aflag - 1)); + gen_update_eip_cur(s); + gen_helper_vmload(tcg_env, tcg_constant_i32(s->aflag - 1)); break; case 0xdb: /* VMSAVE */ @@ -7592,8 +5980,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_vmsave(cpu_env, tcg_const_i32(s->aflag - 1)); + gen_update_eip_cur(s); + gen_helper_vmsave(tcg_env, tcg_constant_i32(s->aflag - 1)); break; case 0xdc: /* STGI */ @@ -7605,9 +5993,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_update_cc_op(s); - gen_helper_stgi(cpu_env); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); + gen_helper_stgi(tcg_env); + s->base.is_jmp = DISAS_EOB_NEXT; break; case 0xdd: /* CLGI */ @@ -7618,8 +6005,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_clgi(cpu_env); + gen_update_eip_cur(s); + gen_helper_clgi(tcg_env); break; case 0xde: /* SKINIT */ @@ -7644,9 +6031,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } else { tcg_gen_ext32u_tl(s->A0, cpu_regs[R_EAX]); } - gen_helper_flush_page(cpu_env, s->A0); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); + gen_helper_flush_page(tcg_env, s->A0); + s->base.is_jmp = DISAS_EOB_NEXT; break; CASE_MODRM_MEM_OP(2): /* lgdt */ @@ -7661,8 +6047,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (dflag == MO_16) { tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); } - tcg_gen_st_tl(s->T0, cpu_env, offsetof(CPUX86State, gdt.base)); - tcg_gen_st32_tl(s->T1, cpu_env, offsetof(CPUX86State, gdt.limit)); + tcg_gen_st_tl(s->T0, tcg_env, offsetof(CPUX86State, gdt.base)); + tcg_gen_st32_tl(s->T1, tcg_env, offsetof(CPUX86State, gdt.limit)); break; CASE_MODRM_MEM_OP(3): /* lidt */ @@ -7677,8 +6063,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (dflag == MO_16) { tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); } - tcg_gen_st_tl(s->T0, cpu_env, offsetof(CPUX86State, idt.base)); - tcg_gen_st32_tl(s->T1, cpu_env, offsetof(CPUX86State, idt.limit)); + tcg_gen_st_tl(s->T0, tcg_env, offsetof(CPUX86State, idt.base)); + tcg_gen_st32_tl(s->T1, tcg_env, offsetof(CPUX86State, idt.limit)); break; CASE_MODRM_OP(4): /* smsw */ @@ -7686,7 +6072,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_svm_check_intercept(s, SVM_EXIT_READ_CR0); - tcg_gen_ld_tl(s->T0, cpu_env, offsetof(CPUX86State, cr[0])); + tcg_gen_ld_tl(s->T0, tcg_env, offsetof(CPUX86State, cr[0])); /* * In 32-bit mode, the higher 16 bits of the destination * register are undefined. In practice CR0[31:0] is stored @@ -7701,7 +6087,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); - gen_helper_rdpkru(s->tmp1_i64, cpu_env, s->tmp2_i32); + gen_helper_rdpkru(s->tmp1_i64, tcg_env, s->tmp2_i32); tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], s->tmp1_i64); break; case 0xef: /* wrpkru */ @@ -7711,7 +6097,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], cpu_regs[R_EDX]); tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); - gen_helper_wrpkru(cpu_env, s->tmp2_i32, s->tmp1_i64); + gen_helper_wrpkru(tcg_env, s->tmp2_i32, s->tmp1_i64); break; CASE_MODRM_OP(6): /* lmsw */ @@ -7724,13 +6110,12 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) * Only the 4 lower bits of CR0 are modified. * PE cannot be set to zero if already set to one. */ - tcg_gen_ld_tl(s->T1, cpu_env, offsetof(CPUX86State, cr[0])); + tcg_gen_ld_tl(s->T1, tcg_env, offsetof(CPUX86State, cr[0])); tcg_gen_andi_tl(s->T0, s->T0, 0xf); tcg_gen_andi_tl(s->T1, s->T1, ~0xe); tcg_gen_or_tl(s->T0, s->T0, s->T1); - gen_helper_write_crN(cpu_env, tcg_constant_i32(0), s->T0); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); + gen_helper_write_crN(tcg_env, tcg_constant_i32(0), s->T0); + s->base.is_jmp = DISAS_EOB_NEXT; break; CASE_MODRM_MEM_OP(7): /* invlpg */ @@ -7739,9 +6124,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } gen_svm_check_intercept(s, SVM_EXIT_INVLPG); gen_lea_modrm(env, s, modrm); - gen_helper_flush_page(cpu_env, s->A0); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); + gen_helper_flush_page(tcg_env, s->A0); + s->base.is_jmp = DISAS_EOB_NEXT; break; case 0xf8: /* swapgs */ @@ -7749,9 +6133,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (CODE64(s)) { if (check_cpl0(s)) { tcg_gen_mov_tl(s->T0, cpu_seg_base[R_GS]); - tcg_gen_ld_tl(cpu_seg_base[R_GS], cpu_env, + tcg_gen_ld_tl(cpu_seg_base[R_GS], tcg_env, offsetof(CPUX86State, kernelgsbase)); - tcg_gen_st_tl(s->T0, cpu_env, + tcg_gen_st_tl(s->T0, tcg_env, offsetof(CPUX86State, kernelgsbase)); } break; @@ -7764,14 +6148,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_rdtscp(cpu_env); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } + gen_update_eip_cur(s); + translator_io_start(&s->base); + gen_helper_rdtsc(tcg_env); + gen_helper_rdpid(s->T0, tcg_env); + gen_op_mov_reg_v(s, dflag, R_ECX, s->T0); break; default: @@ -7780,9 +6161,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; case 0x108: /* invd */ - case 0x109: /* wbinvd */ + case 0x109: /* wbinvd; wbnoinvd with REPZ prefix */ if (check_cpl0(s)) { - gen_svm_check_intercept(s, (b & 2) ? SVM_EXIT_INVD : SVM_EXIT_WBINVD); + gen_svm_check_intercept(s, (b & 1) ? SVM_EXIT_WBINVD : SVM_EXIT_INVD); /* nothing to do */ } break; @@ -7814,13 +6195,13 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) #endif { TCGLabel *label1; - TCGv t0, t1, t2, a0; + TCGv t0, t1, t2; if (!PE(s) || VM86(s)) goto illegal_op; - t0 = tcg_temp_local_new(); - t1 = tcg_temp_local_new(); - t2 = tcg_temp_local_new(); + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); ot = MO_16; modrm = x86_ldub_code(env, s); reg = (modrm >> 3) & 7; @@ -7829,11 +6210,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (mod != 3) { gen_lea_modrm(env, s, modrm); gen_op_ld_v(s, ot, t0, s->A0); - a0 = tcg_temp_local_new(); - tcg_gen_mov_tl(a0, s->A0); } else { gen_op_mov_v_reg(s, ot, t0, rm); - a0 = NULL; } gen_op_mov_v_reg(s, ot, t1, reg); tcg_gen_andi_tl(s->tmp0, t0, 3); @@ -7846,17 +6224,13 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) tcg_gen_movi_tl(t2, CC_Z); gen_set_label(label1); if (mod != 3) { - gen_op_st_v(s, ot, t0, a0); - tcg_temp_free(a0); + gen_op_st_v(s, ot, t0, s->A0); } else { gen_op_mov_reg_v(s, ot, rm, t0); } gen_compute_eflags(s); tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_Z); tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t2); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); } break; case 0x102: /* lar */ @@ -7870,12 +6244,12 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) modrm = x86_ldub_code(env, s); reg = ((modrm >> 3) & 7) | REX_R(s); gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - t0 = tcg_temp_local_new(); + t0 = tcg_temp_new(); gen_update_cc_op(s); if (b == 0x102) { - gen_helper_lar(t0, cpu_env, s->T0); + gen_helper_lar(t0, tcg_env, s->T0); } else { - gen_helper_lsl(t0, cpu_env, s->T0); + gen_helper_lsl(t0, tcg_env, s->T0); } tcg_gen_andi_tl(s->tmp0, cpu_cc_src, CC_Z); label1 = gen_new_label(); @@ -7883,7 +6257,6 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_op_mov_reg_v(s, ot, reg, t0); gen_set_label(label1); set_cc_op(s, CC_OP_EFLAGS); - tcg_temp_free(t0); } break; case 0x118: @@ -7928,7 +6301,6 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) TCGv_i64 notu = tcg_temp_new_i64(); tcg_gen_not_i64(notu, cpu_bndu[reg]); gen_bndck(env, s, modrm, TCG_COND_GTU, notu); - tcg_temp_free_i64(notu); } else if (prefixes & PREFIX_DATA) { /* bndmov -- from reg/mem */ if (reg >= 4 || s->aflag == MO_16) { @@ -7982,11 +6354,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) tcg_gen_movi_tl(s->T0, 0); } if (CODE64(s)) { - gen_helper_bndldx64(cpu_bndl[reg], cpu_env, s->A0, s->T0); - tcg_gen_ld_i64(cpu_bndu[reg], cpu_env, + gen_helper_bndldx64(cpu_bndl[reg], tcg_env, s->A0, s->T0); + tcg_gen_ld_i64(cpu_bndu[reg], tcg_env, offsetof(CPUX86State, mmx_t0.MMX_Q(0))); } else { - gen_helper_bndldx32(cpu_bndu[reg], cpu_env, s->A0, s->T0); + gen_helper_bndldx32(cpu_bndu[reg], tcg_env, s->A0, s->T0); tcg_gen_ext32u_i64(cpu_bndl[reg], cpu_bndu[reg]); tcg_gen_shri_i64(cpu_bndu[reg], cpu_bndu[reg], 32); } @@ -8020,7 +6392,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) /* rip-relative generates #ud */ goto illegal_op; } - tcg_gen_not_tl(s->A0, gen_lea_modrm_1(s, a)); + tcg_gen_not_tl(s->A0, gen_lea_modrm_1(s, a, false)); if (!CODE64(s)) { tcg_gen_ext32u_tl(s->A0, s->A0); } @@ -8087,10 +6459,10 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) tcg_gen_movi_tl(s->T0, 0); } if (CODE64(s)) { - gen_helper_bndstx64(cpu_env, s->A0, s->T0, + gen_helper_bndstx64(tcg_env, s->A0, s->T0, cpu_bndl[reg], cpu_bndu[reg]); } else { - gen_helper_bndstx32(cpu_env, s->A0, s->T0, + gen_helper_bndstx32(tcg_env, s->A0, s->T0, cpu_bndl[reg], cpu_bndu[reg]); } } @@ -8133,22 +6505,16 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } ot = (CODE64(s) ? MO_64 : MO_32); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&s->base); if (b & 2) { gen_svm_check_intercept(s, SVM_EXIT_WRITE_CR0 + reg); gen_op_mov_v_reg(s, ot, s->T0, rm); - gen_helper_write_crN(cpu_env, tcg_constant_i32(reg), s->T0); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); + gen_helper_write_crN(tcg_env, tcg_constant_i32(reg), s->T0); + s->base.is_jmp = DISAS_EOB_NEXT; } else { gen_svm_check_intercept(s, SVM_EXIT_READ_CR0 + reg); - gen_helper_read_crN(s->T0, cpu_env, tcg_constant_i32(reg)); + gen_helper_read_crN(s->T0, tcg_env, tcg_constant_i32(reg)); gen_op_mov_reg_v(s, ot, rm, s->T0); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } } break; @@ -8174,13 +6540,12 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_svm_check_intercept(s, SVM_EXIT_WRITE_DR0 + reg); gen_op_mov_v_reg(s, ot, s->T0, rm); tcg_gen_movi_i32(s->tmp2_i32, reg); - gen_helper_set_dr(cpu_env, s->tmp2_i32, s->T0); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); + gen_helper_set_dr(tcg_env, s->tmp2_i32, s->T0); + s->base.is_jmp = DISAS_EOB_NEXT; } else { gen_svm_check_intercept(s, SVM_EXIT_READ_DR0 + reg); tcg_gen_movi_i32(s->tmp2_i32, reg); - gen_helper_get_dr(s->T0, cpu_env, s->tmp2_i32); + gen_helper_get_dr(s->T0, tcg_env, s->tmp2_i32); gen_op_mov_reg_v(s, ot, rm, s->T0); } } @@ -8188,10 +6553,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x106: /* clts */ if (check_cpl0(s)) { gen_svm_check_intercept(s, SVM_EXIT_WRITE_CR0); - gen_helper_clts(cpu_env); + gen_helper_clts(tcg_env); /* abort block because static cpu state changed */ - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); + s->base.is_jmp = DISAS_EOB_NEXT; } break; /* MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4 support */ @@ -8216,11 +6580,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { - gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + gen_exception(s, EXCP07_PREX); break; } gen_lea_modrm(env, s, modrm); - gen_helper_fxsave(cpu_env, s->A0); + gen_helper_fxsave(tcg_env, s->A0); break; CASE_MODRM_MEM_OP(1): /* fxrstor */ @@ -8229,11 +6593,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { - gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + gen_exception(s, EXCP07_PREX); break; } gen_lea_modrm(env, s, modrm); - gen_helper_fxrstor(cpu_env, s->A0); + gen_helper_fxrstor(tcg_env, s->A0); break; CASE_MODRM_MEM_OP(2): /* ldmxcsr */ @@ -8241,12 +6605,12 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } if (s->flags & HF_TS_MASK) { - gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + gen_exception(s, EXCP07_PREX); break; } gen_lea_modrm(env, s, modrm); tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - gen_helper_ldmxcsr(cpu_env, s->tmp2_i32); + gen_helper_ldmxcsr(tcg_env, s->tmp2_i32); break; CASE_MODRM_MEM_OP(3): /* stmxcsr */ @@ -8254,12 +6618,12 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } if (s->flags & HF_TS_MASK) { - gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + gen_exception(s, EXCP07_PREX); break; } - gen_helper_update_mxcsr(cpu_env); + gen_helper_update_mxcsr(tcg_env); gen_lea_modrm(env, s, modrm); - tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, mxcsr)); + tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, mxcsr)); gen_op_st_v(s, MO_32, s->T0, s->A0); break; @@ -8272,7 +6636,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_lea_modrm(env, s, modrm); tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], cpu_regs[R_EDX]); - gen_helper_xsave(cpu_env, s->A0, s->tmp1_i64); + gen_helper_xsave(tcg_env, s->A0, s->tmp1_i64); break; CASE_MODRM_MEM_OP(5): /* xrstor */ @@ -8284,12 +6648,10 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_lea_modrm(env, s, modrm); tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], cpu_regs[R_EDX]); - gen_helper_xrstor(cpu_env, s->A0, s->tmp1_i64); + gen_helper_xrstor(tcg_env, s->A0, s->tmp1_i64); /* XRSTOR is how MPX is enabled, which changes how we translate. Thus we need to end the TB. */ - gen_update_cc_op(s); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); + s->base.is_jmp = DISAS_EOB_NEXT; break; CASE_MODRM_MEM_OP(6): /* xsaveopt / clwb */ @@ -8312,7 +6674,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_lea_modrm(env, s, modrm); tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], cpu_regs[R_EDX]); - gen_helper_xsaveopt(cpu_env, s->A0, s->tmp1_i64); + gen_helper_xsaveopt(tcg_env, s->A0, s->tmp1_i64); } break; @@ -8347,7 +6709,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) /* Preserve hflags bits by testing CR4 at runtime. */ tcg_gen_movi_i32(s->tmp2_i32, CR4_FSGSBASE_MASK); - gen_helper_cr4_testbit(cpu_env, s->tmp2_i32); + gen_helper_cr4_testbit(tcg_env, s->tmp2_i32); base = cpu_seg_base[modrm & 8 ? R_GS : R_FS]; treg = cpu_regs[(modrm & 7) | REX_B(s)]; @@ -8422,10 +6784,10 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) g_assert_not_reached(); #else gen_update_cc_op(s); - gen_jmp_im(s, s->pc - s->cs_base); - gen_helper_rsm(cpu_env); + gen_update_eip_next(s); + gen_helper_rsm(tcg_env); #endif /* CONFIG_USER_ONLY */ - gen_eob(s); + s->base.is_jmp = DISAS_EOB_ONLY; break; case 0x1b8: /* SSE4.2 popcnt */ if ((prefixes & (PREFIX_REPZ | PREFIX_LOCK | PREFIX_REPNZ)) != @@ -8451,11 +6813,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) set_cc_op(s, CC_OP_POPCNT); break; - case 0x10e ... 0x10f: - /* 3DNow! instructions, ignore prefixes */ - s->prefix &= ~(PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA); - /* fall through */ - case 0x110 ... 0x117: + case 0x10e ... 0x117: case 0x128 ... 0x12f: case 0x138 ... 0x13a: case 0x150 ... 0x179: @@ -8463,18 +6821,18 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x1c2: case 0x1c4 ... 0x1c6: case 0x1d0 ... 0x1fe: - gen_sse(env, s, b, pc_start); + disas_insn_new(s, cpu, b); break; default: goto unknown_op; } - return s->pc; + return true; illegal_op: gen_illegal_opcode(s); - return s->pc; + return true; unknown_op: gen_unknown_opcode(env, s); - return s->pc; + return true; } void tcg_x86_init(void) @@ -8506,6 +6864,13 @@ void tcg_x86_init(void) [R_EDI] = "edi", [R_EBP] = "ebp", [R_ESP] = "esp", +#endif + }; + static const char eip_name[] = { +#ifdef TARGET_X86_64 + "rip" +#else + "eip" #endif }; static const char seg_base_names[6][8] = { @@ -8524,35 +6889,36 @@ void tcg_x86_init(void) }; int i; - cpu_cc_op = tcg_global_mem_new_i32(cpu_env, + cpu_cc_op = tcg_global_mem_new_i32(tcg_env, offsetof(CPUX86State, cc_op), "cc_op"); - cpu_cc_dst = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_dst), + cpu_cc_dst = tcg_global_mem_new(tcg_env, offsetof(CPUX86State, cc_dst), "cc_dst"); - cpu_cc_src = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_src), + cpu_cc_src = tcg_global_mem_new(tcg_env, offsetof(CPUX86State, cc_src), "cc_src"); - cpu_cc_src2 = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_src2), + cpu_cc_src2 = tcg_global_mem_new(tcg_env, offsetof(CPUX86State, cc_src2), "cc_src2"); + cpu_eip = tcg_global_mem_new(tcg_env, offsetof(CPUX86State, eip), eip_name); for (i = 0; i < CPU_NB_REGS; ++i) { - cpu_regs[i] = tcg_global_mem_new(cpu_env, + cpu_regs[i] = tcg_global_mem_new(tcg_env, offsetof(CPUX86State, regs[i]), reg_names[i]); } for (i = 0; i < 6; ++i) { cpu_seg_base[i] - = tcg_global_mem_new(cpu_env, + = tcg_global_mem_new(tcg_env, offsetof(CPUX86State, segs[i].base), seg_base_names[i]); } for (i = 0; i < 4; ++i) { cpu_bndl[i] - = tcg_global_mem_new_i64(cpu_env, + = tcg_global_mem_new_i64(tcg_env, offsetof(CPUX86State, bnd_regs[i].lb), bnd_regl_names[i]); cpu_bndu[i] - = tcg_global_mem_new_i64(cpu_env, + = tcg_global_mem_new_i64(tcg_env, offsetof(CPUX86State, bnd_regs[i].ub), bnd_regu_names[i]); } @@ -8561,13 +6927,14 @@ void tcg_x86_init(void) static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUX86State *env = cpu->env_ptr; + CPUX86State *env = cpu_env(cpu); uint32_t flags = dc->base.tb->flags; uint32_t cflags = tb_cflags(dc->base.tb); int cpl = (flags >> HF_CPL_SHIFT) & 3; int iopl = (flags >> IOPL_SHIFT) & 3; dc->cs_base = dc->base.tb->cs_base; + dc->pc_save = dc->base.pc_next; dc->flags = flags; #ifndef CONFIG_USER_ONLY dc->cpl = cpl; @@ -8591,15 +6958,14 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) dc->cc_op_dirty = false; dc->popl_esp_hack = 0; /* select memory access functions */ - dc->mem_index = 0; -#ifdef CONFIG_SOFTMMU - dc->mem_index = cpu_mmu_index(env, false); -#endif + dc->mem_index = cpu_mmu_index(cpu, false); dc->cpuid_features = env->features[FEAT_1_EDX]; dc->cpuid_ext_features = env->features[FEAT_1_ECX]; dc->cpuid_ext2_features = env->features[FEAT_8000_0001_EDX]; dc->cpuid_ext3_features = env->features[FEAT_8000_0001_ECX]; dc->cpuid_7_0_ebx_features = env->features[FEAT_7_0_EBX]; + dc->cpuid_7_0_ecx_features = env->features[FEAT_7_0_ECX]; + dc->cpuid_7_1_eax_features = env->features[FEAT_7_1_EAX]; dc->cpuid_xsave_features = env->features[FEAT_XSAVE]; dc->jmp_opt = !((cflags & CF_NO_GOTO_TB) || (flags & (HF_TF_MASK | HF_INHIBIT_IRQ_MASK))); @@ -8619,9 +6985,7 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) dc->tmp2_i32 = tcg_temp_new_i32(); dc->tmp3_i32 = tcg_temp_new_i32(); dc->tmp4 = tcg_temp_new(); - dc->ptr0 = tcg_temp_new_ptr(); - dc->ptr1 = tcg_temp_new_ptr(); - dc->cc_srcT = tcg_temp_local_new(); + dc->cc_srcT = tcg_temp_new(); } static void i386_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -8631,61 +6995,80 @@ static void i386_tr_tb_start(DisasContextBase *db, CPUState *cpu) static void i386_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); + target_ulong pc_arg = dc->base.pc_next; - tcg_gen_insn_start(dc->base.pc_next, dc->cc_op); + dc->prev_insn_start = dc->base.insn_start; + dc->prev_insn_end = tcg_last_op(); + if (tb_cflags(dcbase->tb) & CF_PCREL) { + pc_arg &= ~TARGET_PAGE_MASK; + } + tcg_gen_insn_start(pc_arg, dc->cc_op); } static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - 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); + gen_exception(dc, EXCP_VSYSCALL); dc->base.pc_next = dc->pc + 1; return; } #endif - pc_next = disas_insn(dc, cpu); + if (disas_insn(dc, cpu)) { + target_ulong pc_next = dc->pc; + dc->base.pc_next = pc_next; - if (dc->flags & (HF_TF_MASK | HF_INHIBIT_IRQ_MASK)) { - /* if single step mode, we generate only one instruction and - generate an exception */ - /* if irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear - the flag and abort the translation to give the irqs a - chance to happen */ - dc->base.is_jmp = DISAS_TOO_MANY; - } else if ((tb_cflags(dc->base.tb) & CF_USE_ICOUNT) - && ((pc_next & TARGET_PAGE_MASK) - != ((pc_next + TARGET_MAX_INSN_SIZE - 1) - & TARGET_PAGE_MASK) - || (pc_next & ~TARGET_PAGE_MASK) == 0)) { - /* Do not cross the boundary of the pages in icount mode, - it can cause an exception. Do it only when boundary is - crossed by the first instruction in the block. - If current instruction already crossed the bound - it's ok, - because an exception hasn't stopped this code. - */ - dc->base.is_jmp = DISAS_TOO_MANY; - } else if ((pc_next - dc->base.pc_first) >= (TARGET_PAGE_SIZE - 32)) { - dc->base.is_jmp = DISAS_TOO_MANY; + if (dc->base.is_jmp == DISAS_NEXT) { + if (dc->flags & (HF_TF_MASK | HF_INHIBIT_IRQ_MASK)) { + /* + * If single step mode, we generate only one instruction and + * generate an exception. + * If irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear + * the flag and abort the translation to give the irqs a + * chance to happen. + */ + dc->base.is_jmp = DISAS_EOB_NEXT; + } else if (!is_same_page(&dc->base, pc_next)) { + dc->base.is_jmp = DISAS_TOO_MANY; + } + } } - - dc->base.pc_next = pc_next; } static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - if (dc->base.is_jmp == DISAS_TOO_MANY) { - gen_jmp_im(dc, dc->base.pc_next - dc->cs_base); + switch (dc->base.is_jmp) { + case DISAS_NORETURN: + break; + case DISAS_TOO_MANY: + gen_update_cc_op(dc); + gen_jmp_rel_csize(dc, 0, 0); + break; + case DISAS_EOB_NEXT: + gen_update_cc_op(dc); + gen_update_eip_cur(dc); + /* fall through */ + case DISAS_EOB_ONLY: gen_eob(dc); + break; + case DISAS_EOB_INHIBIT_IRQ: + gen_update_cc_op(dc); + gen_update_eip_cur(dc); + gen_eob_inhibit_irq(dc, true); + break; + case DISAS_JUMP: + gen_jr(dc); + break; + default: + g_assert_not_reached(); } } @@ -8708,19 +7091,10 @@ static const TranslatorOps i386_tr_ops = { }; /* generate intermediate code for basic block 'tb'. */ -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc; - translator_loop(&i386_tr_ops, &dc.base, cpu, tb, max_insns); -} - -void restore_state_to_opc(CPUX86State *env, TranslationBlock *tb, - target_ulong *data) -{ - int cc_op = data[1]; - env->eip = data[0] - tb->cs_base; - if (cc_op != CC_OP_DYNAMIC) { - env->cc_op = cc_op; - } + translator_loop(cpu, tb, max_insns, pc, host_pc, &i386_tr_ops, &dc.base); } diff --git a/target/i386/tcg/user/excp_helper.c b/target/i386/tcg/user/excp_helper.c index cd507e2a1b..b3bdb7831a 100644 --- a/target/i386/tcg/user/excp_helper.c +++ b/target/i386/tcg/user/excp_helper.c @@ -48,3 +48,10 @@ void x86_cpu_record_sigsegv(CPUState *cs, vaddr addr, cpu_loop_exit_restore(cs, ra); } + +void x86_cpu_record_sigbus(CPUState *cs, vaddr addr, + MMUAccessType access_type, uintptr_t ra) +{ + X86CPU *cpu = X86_CPU(cs); + handle_unaligned_access(&cpu->env, addr, access_type, ra); +} diff --git a/target/i386/tcg/user/seg_helper.c b/target/i386/tcg/user/seg_helper.c index 67481b0aa8..c45f2ac2ba 100644 --- a/target/i386/tcg/user/seg_helper.c +++ b/target/i386/tcg/user/seg_helper.c @@ -26,7 +26,6 @@ #include "tcg/helper-tcg.h" #include "tcg/seg_helper.h" -#ifdef TARGET_X86_64 void helper_syscall(CPUX86State *env, int next_eip_addend) { CPUState *cs = env_cpu(env); @@ -36,7 +35,6 @@ void helper_syscall(CPUX86State *env, int next_eip_addend) env->exception_next_eip = env->eip + next_eip_addend; cpu_loop_exit(cs); } -#endif /* TARGET_X86_64 */ /* * fake user mode interrupt. is_int is TRUE if coming from the int diff --git a/target/i386/whpx/meson.build b/target/i386/whpx/meson.build index 95fc31eb81..9c54aaad39 100644 --- a/target/i386/whpx/meson.build +++ b/target/i386/whpx/meson.build @@ -1,4 +1,4 @@ -i386_softmmu_ss.add(when: 'CONFIG_WHPX', if_true: files( +i386_system_ss.add(when: 'CONFIG_WHPX', if_true: files( 'whpx-all.c', 'whpx-apic.c', 'whpx-accel-ops.c', diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index e8dc4b3a47..189ae0f140 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -25,7 +25,7 @@ static void *whpx_cpu_thread_fn(void *arg) rcu_register_thread(); - qemu_mutex_lock_iothread(); + bql_lock(); qemu_thread_get_self(cpu->thread); cpu->thread_id = qemu_get_thread_id(); current_cpu = cpu; @@ -48,14 +48,14 @@ static void *whpx_cpu_thread_fn(void *arg) } } while (cpu_thread_is_idle(cpu)) { - qemu_cond_wait_iothread(cpu->halt_cond); + qemu_cond_wait_bql(cpu->halt_cond); } qemu_wait_io_event_common(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); whpx_destroy_vcpu(cpu); cpu_thread_signal_destroyed(cpu); - qemu_mutex_unlock_iothread(); + bql_unlock(); rcu_unregister_thread(); return NULL; } @@ -71,9 +71,6 @@ static void whpx_start_vcpu_thread(CPUState *cpu) cpu->cpu_index); qemu_thread_create(cpu->thread, thread_name, whpx_cpu_thread_fn, cpu, QEMU_THREAD_JOINABLE); -#ifdef _WIN32 - cpu->hThread = qemu_thread_get_handle(cpu->thread); -#endif } static void whpx_kick_vcpu_thread(CPUState *cpu) diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index b22a3314b4..31eec7048c 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -12,14 +12,14 @@ #include "cpu.h" #include "exec/address-spaces.h" #include "exec/ioport.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/accel.h" #include "sysemu/whpx.h" #include "sysemu/cpus.h" #include "sysemu/runstate.h" #include "qemu/main-loop.h" #include "hw/boards.h" -#include "hw/i386/ioapic.h" +#include "hw/intc/ioapic.h" #include "hw/i386/apic_internal.h" #include "qemu/error-report.h" #include "qapi/error.h" @@ -31,8 +31,8 @@ #include "whpx-internal.h" #include "whpx-accel-ops.h" -#include -#include +#include +#include #define HYPERV_APIC_BUS_FREQUENCY (200000000ULL) @@ -229,7 +229,7 @@ typedef enum WhpxStepMode { WHPX_STEP_EXCLUSIVE, } WhpxStepMode; -struct whpx_vcpu { +struct AccelCPUState { WHV_EMULATOR_HANDLE emulator; bool window_registered; bool interruptable; @@ -256,15 +256,6 @@ static bool whpx_has_xsave(void) return whpx_xsave_cap.XsaveSupport; } -/* - * VP support - */ - -static struct whpx_vcpu *get_whpx_vcpu(CPUState *cpu) -{ - return (struct whpx_vcpu *)cpu->hax_vcpu; -} - static WHV_X64_SEGMENT_REGISTER whpx_seg_q2h(const SegmentCache *qs, int v86, int r86) { @@ -309,7 +300,6 @@ static SegmentCache whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER *hs) /* X64 Extended Control Registers */ static void whpx_set_xcrs(CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; HRESULT hr; struct whpx_state *whpx = &whpx_global; WHV_REGISTER_VALUE xcr0; @@ -320,7 +310,7 @@ static void whpx_set_xcrs(CPUState *cpu) } /* Only xcr0 is supported by the hypervisor currently */ - xcr0.Reg64 = env->xcr0; + xcr0.Reg64 = cpu_env(cpu)->xcr0; hr = whp_dispatch.WHvSetVirtualProcessorRegisters( whpx->partition, cpu->cpu_index, &xcr0_name, 1, &xcr0); if (FAILED(hr)) { @@ -330,7 +320,6 @@ static void whpx_set_xcrs(CPUState *cpu) static int whpx_set_tsc(CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; WHV_REGISTER_NAME tsc_reg = WHvX64RegisterTsc; WHV_REGISTER_VALUE tsc_val; HRESULT hr; @@ -354,7 +343,7 @@ static int whpx_set_tsc(CPUState *cpu) } } - tsc_val.Reg64 = env->tsc; + tsc_val.Reg64 = cpu_env(cpu)->tsc; hr = whp_dispatch.WHvSetVirtualProcessorRegisters( whpx->partition, cpu->cpu_index, &tsc_reg, 1, &tsc_val); if (FAILED(hr)) { @@ -390,9 +379,9 @@ static uint64_t whpx_cr8_to_apic_tpr(uint64_t cr8) static void whpx_set_registers(CPUState *cpu, int level) { struct whpx_state *whpx = &whpx_global; - struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); - CPUX86State *env = cpu->env_ptr; + AccelCPUState *vcpu = cpu->accel; X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; struct whpx_register_set vcxt; HRESULT hr; int idx; @@ -565,7 +554,6 @@ static void whpx_set_registers(CPUState *cpu, int level) static int whpx_get_tsc(CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; WHV_REGISTER_NAME tsc_reg = WHvX64RegisterTsc; WHV_REGISTER_VALUE tsc_val; HRESULT hr; @@ -578,14 +566,13 @@ static int whpx_get_tsc(CPUState *cpu) return -1; } - env->tsc = tsc_val.Reg64; + cpu_env(cpu)->tsc = tsc_val.Reg64; return 0; } /* X64 Extended Control Registers */ static void whpx_get_xcrs(CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; HRESULT hr; struct whpx_state *whpx = &whpx_global; WHV_REGISTER_VALUE xcr0; @@ -603,15 +590,15 @@ static void whpx_get_xcrs(CPUState *cpu) return; } - env->xcr0 = xcr0.Reg64; + cpu_env(cpu)->xcr0 = xcr0.Reg64; } static void whpx_get_registers(CPUState *cpu) { struct whpx_state *whpx = &whpx_global; - struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); - CPUX86State *env = cpu->env_ptr; + AccelCPUState *vcpu = cpu->accel; X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; struct whpx_register_set vcxt; uint64_t tpr, apic_base; HRESULT hr; @@ -892,7 +879,7 @@ static const WHV_EMULATOR_CALLBACKS whpx_emu_callbacks = { static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx) { HRESULT hr; - struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + AccelCPUState *vcpu = cpu->accel; WHV_EMULATOR_STATUS emu_status; hr = whp_dispatch.WHvEmulatorTryMmioEmulation( @@ -917,7 +904,7 @@ static int whpx_handle_portio(CPUState *cpu, WHV_X64_IO_PORT_ACCESS_CONTEXT *ctx) { HRESULT hr; - struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + AccelCPUState *vcpu = cpu->accel; WHV_EMULATOR_STATUS emu_status; hr = whp_dispatch.WHvEmulatorTryIoEmulation( @@ -1164,9 +1151,8 @@ static void whpx_translate_cpu_breakpoints( (breakpoints->breakpoints ? breakpoints->breakpoints->used : 0); struct whpx_breakpoint_collection *new_breakpoints = - (struct whpx_breakpoint_collection *)g_malloc0( - sizeof(struct whpx_breakpoint_collection) + - max_breakpoints * sizeof(struct whpx_breakpoint)); + g_malloc0(sizeof(struct whpx_breakpoint_collection) + + max_breakpoints * sizeof(struct whpx_breakpoint)); new_breakpoints->allocated = max_breakpoints; new_breakpoints->used = 0; @@ -1225,14 +1211,12 @@ static void whpx_translate_cpu_breakpoints( } } - if (breakpoints->breakpoints) { - /* - * Free the previous breakpoint list. This can be optimized by keeping - * it as shadow buffer for the next computation instead of freeing - * it immediately. - */ - g_free(breakpoints->breakpoints); - } + /* + * Free the previous breakpoint list. This can be optimized by keeping + * it as shadow buffer for the next computation instead of freeing + * it immediately. + */ + g_free(breakpoints->breakpoints); breakpoints->breakpoints = new_breakpoints; } @@ -1336,7 +1320,7 @@ static int whpx_first_vcpu_starting(CPUState *cpu) struct whpx_state *whpx = &whpx_global; HRESULT hr; - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); if (!QTAILQ_EMPTY(&cpu->breakpoints) || (whpx->breakpoints.breakpoints && @@ -1412,15 +1396,14 @@ static vaddr whpx_vcpu_get_pc(CPUState *cpu, bool exit_context_valid) { if (cpu->vcpu_dirty) { /* The CPU registers have been modified by other parts of QEMU. */ - CPUArchState *env = (CPUArchState *)(cpu->env_ptr); - return env->eip; + return cpu_env(cpu)->eip; } else if (exit_context_valid) { /* * The CPU registers have not been modified by neither other parts * of QEMU, nor this port by calling WHvSetVirtualProcessorRegisters(). * This is the most common case. */ - struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + AccelCPUState *vcpu = cpu->accel; return vcpu->exit_ctx.VpContext.Rip; } else { /* @@ -1451,18 +1434,17 @@ static vaddr whpx_vcpu_get_pc(CPUState *cpu, bool exit_context_valid) static int whpx_handle_halt(CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; int ret = 0; - qemu_mutex_lock_iothread(); + bql_lock(); if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) && - (env->eflags & IF_MASK)) && + (cpu_env(cpu)->eflags & IF_MASK)) && !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) { cpu->exception_index = EXCP_HLT; cpu->halted = true; ret = 1; } - qemu_mutex_unlock_iothread(); + bql_unlock(); return ret; } @@ -1471,9 +1453,9 @@ static void whpx_vcpu_pre_run(CPUState *cpu) { HRESULT hr; struct whpx_state *whpx = &whpx_global; - struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); - CPUX86State *env = cpu->env_ptr; + AccelCPUState *vcpu = cpu->accel; X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; int irq; uint8_t tpr; WHV_X64_PENDING_INTERRUPTION_REGISTER new_int; @@ -1484,7 +1466,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) memset(&new_int, 0, sizeof(new_int)); memset(reg_values, 0, sizeof(reg_values)); - qemu_mutex_lock_iothread(); + bql_lock(); /* Inject NMI */ if (!vcpu->interruption_pending && @@ -1575,7 +1557,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) reg_count += 1; } - qemu_mutex_unlock_iothread(); + bql_unlock(); vcpu->ready_for_pic_interrupt = false; if (reg_count) { @@ -1593,18 +1575,18 @@ static void whpx_vcpu_pre_run(CPUState *cpu) static void whpx_vcpu_post_run(CPUState *cpu) { - struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); - CPUX86State *env = cpu->env_ptr; + AccelCPUState *vcpu = cpu->accel; X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; env->eflags = vcpu->exit_ctx.VpContext.Rflags; uint64_t tpr = vcpu->exit_ctx.VpContext.Cr8; if (vcpu->tpr != tpr) { vcpu->tpr = tpr; - qemu_mutex_lock_iothread(); + bql_lock(); cpu_set_apic_tpr(x86_cpu->apic_state, whpx_cr8_to_apic_tpr(vcpu->tpr)); - qemu_mutex_unlock_iothread(); + bql_unlock(); } vcpu->interruption_pending = @@ -1618,9 +1600,9 @@ static void whpx_vcpu_post_run(CPUState *cpu) static void whpx_vcpu_process_async_events(CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; X86CPU *x86_cpu = X86_CPU(cpu); - struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + CPUX86State *env = &x86_cpu->env; + AccelCPUState *vcpu = cpu->accel; if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { @@ -1659,12 +1641,12 @@ static int whpx_vcpu_run(CPUState *cpu) { HRESULT hr; struct whpx_state *whpx = &whpx_global; - struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + AccelCPUState *vcpu = cpu->accel; struct whpx_breakpoint *stepped_over_bp = NULL; WhpxStepMode exclusive_step_mode = WHPX_STEP_NONE; int ret; - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); if (whpx->running_cpus++ == 0) { /* Insert breakpoints into memory, update exception exit bitmap. */ @@ -1702,7 +1684,7 @@ static int whpx_vcpu_run(CPUState *cpu) } } - qemu_mutex_unlock_iothread(); + bql_unlock(); if (exclusive_step_mode != WHPX_STEP_NONE) { start_exclusive(); @@ -2040,9 +2022,9 @@ static int whpx_vcpu_run(CPUState *cpu) error_report("WHPX: Unexpected VP exit code %d", vcpu->exit_ctx.ExitReason); whpx_get_registers(cpu); - qemu_mutex_lock_iothread(); + bql_lock(); qemu_system_guest_panicked(cpu_get_crash_info(cpu)); - qemu_mutex_unlock_iothread(); + bql_unlock(); break; } @@ -2067,7 +2049,7 @@ static int whpx_vcpu_run(CPUState *cpu) cpu_exec_end(cpu); } - qemu_mutex_lock_iothread(); + bql_lock(); current_cpu = cpu; if (--whpx->running_cpus == 0) { @@ -2157,10 +2139,10 @@ int whpx_init_vcpu(CPUState *cpu) { HRESULT hr; struct whpx_state *whpx = &whpx_global; - struct whpx_vcpu *vcpu = NULL; + AccelCPUState *vcpu = NULL; Error *local_error = NULL; - CPUX86State *env = cpu->env_ptr; X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; UINT64 freq = 0; int ret; @@ -2172,21 +2154,14 @@ int whpx_init_vcpu(CPUState *cpu) "State blocked due to non-migratable CPUID feature support," "dirty memory tracking support, and XSAVE/XRSTOR support"); - if (migrate_add_blocker(whpx_migration_blocker, &local_error) < 0) { + if (migrate_add_blocker(&whpx_migration_blocker, &local_error) < 0) { error_report_err(local_error); - error_free(whpx_migration_blocker); ret = -EINVAL; goto error; } } - vcpu = g_new0(struct whpx_vcpu, 1); - - if (!vcpu) { - error_report("WHPX: Failed to allocte VCPU context."); - ret = -ENOMEM; - goto error; - } + vcpu = g_new0(AccelCPUState, 1); hr = whp_dispatch.WHvEmulatorCreateEmulator( &whpx_emu_callbacks, @@ -2261,9 +2236,9 @@ int whpx_init_vcpu(CPUState *cpu) vcpu->interruptable = true; cpu->vcpu_dirty = true; - cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu; + cpu->accel = vcpu; max_vcpu_index = max(max_vcpu_index, cpu->cpu_index); - qemu_add_vm_change_state_handler(whpx_cpu_update_state, cpu->env_ptr); + qemu_add_vm_change_state_handler(whpx_cpu_update_state, env); return 0; @@ -2299,11 +2274,11 @@ int whpx_vcpu_exec(CPUState *cpu) void whpx_destroy_vcpu(CPUState *cpu) { struct whpx_state *whpx = &whpx_global; - struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); + AccelCPUState *vcpu = cpu->accel; whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index); whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator); - g_free(cpu->hax_vcpu); + g_free(cpu->accel); return; } @@ -2430,7 +2405,7 @@ static MemoryListener whpx_memory_listener = { .region_add = whpx_region_add, .region_del = whpx_region_del, .log_sync = whpx_log_sync, - .priority = 10, + .priority = MEMORY_LISTENER_PRIORITY_ACCEL, }; static void whpx_memory_init(void) @@ -2616,8 +2591,8 @@ static int whpx_accel_init(MachineState *ms) sizeof(WHV_PARTITION_PROPERTY)); if (FAILED(hr)) { - error_report("WHPX: Failed to set partition core count to %d," - " hr=%08lx", ms->smp.cores, hr); + error_report("WHPX: Failed to set partition processor count to %u," + " hr=%08lx", prop.ProcessorCount, hr); ret = -EINVAL; goto error; } diff --git a/target/i386/whpx/whpx-apic.c b/target/i386/whpx/whpx-apic.c index c15df35ad6..7e14ded978 100644 --- a/target/i386/whpx/whpx-apic.c +++ b/target/i386/whpx/whpx-apic.c @@ -11,6 +11,7 @@ * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "cpu.h" #include "hw/i386/apic_internal.h" #include "hw/i386/apic-msidef.h" @@ -89,9 +90,10 @@ static void whpx_get_apic_state(APICCommonState *s, apic_next_timer(s, s->initial_count_load_time); } -static void whpx_apic_set_base(APICCommonState *s, uint64_t val) +static int whpx_apic_set_base(APICCommonState *s, uint64_t val) { s->apicbase = val; + return 0; } static void whpx_put_apic_base(CPUState *cpu, uint64_t val) diff --git a/target/i386/whpx/whpx-internal.h b/target/i386/whpx/whpx-internal.h index 06429d8ccd..6633e9c4ca 100644 --- a/target/i386/whpx/whpx-internal.h +++ b/target/i386/whpx/whpx-internal.h @@ -2,8 +2,8 @@ #define TARGET_I386_WHPX_INTERNAL_H #include -#include -#include +#include +#include typedef enum WhpxBreakpointState { WHPX_BP_CLEARED = 0, diff --git a/target/loongarch/README b/target/loongarch/README index 4dcd0f1682..0b9dc0d40a 100644 --- a/target/loongarch/README +++ b/target/loongarch/README @@ -11,54 +11,42 @@ - System emulation - Mainly emulate a virt 3A5000 board and ls7a bridge that is not exactly the same as the host. - 3A5000 support multiple interrupt cascading while here we just emulate the extioi interrupt - cascading. LS7A1000 host bridge support multiple devices, such as sata, gmac, uart, rtc - and so on. But we just realize the rtc. Others use the qemu common devices. It does not affect - the general use. We also introduced the emulation of devices at docs/system/loongarch/loongson3.rst. + You can reference docs/system/loongarch/loongson3.rst to get the information about system emulation of LoongArch. - This version only supports running binary files in ELF format, and does not depend on BIOS and kernel file. - You can compile the test program with 'make & make check-tcg' and run the test case with the following command: +- Linux-user emulation - 1. Install LoongArch cross-tools on X86 machines. + We already support Linux user emulation. We can use LoongArch cross-tools to build LoongArch executables on X86 machines, + and We can also use qemu-loongarch64 to run LoongArch executables. - Download cross-tools. + 1. Config cross-tools env. - wget https://github.com/loongson/build-tools/releases/latest/download/loongarch64-clfs-20211202-cross-tools.tar.xz - - tar -vxf loongarch64-clfs-20211202-cross-tools.tar.xz -C /opt - - Config cross-tools env. - - . setenv.sh - - setenv.sh: - - #!/bin/sh - set -x - CC_PREFIX=/opt/cross-tools - - export PATH=$CC_PREFIX/bin:$PATH - export LD_LIBRARY_PATH=$CC_PREFIX/lib:$LD_LIBRARY_PATH - export LD_LIBRARY_PATH=$CC_PREFIX/loongarch64-unknown-linux-gnu/lib/:$LD_LIBRARY_PATH - set +x + see System emulation. 2. Test tests/tcg/multiarch. - ./configure --disable-rdma --disable-pvrdma --prefix=/usr \ - --target-list="loongarch64-softmmu" \ - --disable-libiscsi --disable-libnfs --disable-libpmem \ - --disable-glusterfs --enable-libusb --enable-usb-redir \ - --disable-opengl --disable-xen --enable-spice --disable-werror \ - --enable-debug --disable-capstone --disable-kvm --enable-profiler + ./configure --static --prefix=/usr --disable-werror --target-list="loongarch64-linux-user" --enable-debug - cd build/ + cd build - make && make check-tcg + make && make check-tcg - or + 3. Run LoongArch system basic command with loongarch-clfs-system. - ./build/qemu-system-loongarch64 -machine virt -m 4G -cpu Loongson-3A5000 -smp 1 -kernel build/tests/tcg/loongarch64-softmmu/hello -monitor none -display none -chardev file,path=hello.out,id=output -serial chardev:output + - Config clfs env. + + wget https://github.com/loongson/build-tools/releases/download/2022.05.29/loongarch64-clfs-system-5.0.tar.bz2 + + tar -vxf loongarch64-clfs-system-5.0.tar.bz2 -C /opt/clfs + + cp /opt/clfs/lib64/ld-linux-loongarch-lp64d.so.1 /lib64 + + export LD_LIBRARY_PATH="/opt/clfs/lib64" + + - Run LoongArch system basic command. + + ./qemu-loongarch64 /opt/clfs/usr/bin/bash + ./qemu-loongarch64 /opt/clfs/usr/bin/ls + ./qemu-loongarch64 /opt/clfs/usr/bin/pwd - Note. We can get the latest LoongArch documents or LoongArch tools at https://github.com/loongson/ diff --git a/target/loongarch/cpu-csr.h b/target/loongarch/cpu-csr.h index 4c8ce7fed5..0834e91f30 100644 --- a/target/loongarch/cpu-csr.h +++ b/target/loongarch/cpu-csr.h @@ -10,7 +10,7 @@ #include "hw/registerfields.h" -/* Base on kernal definitions: arch/loongarch/include/asm/loongarch.h */ +/* Based on kernel definitions: arch/loongarch/include/asm/loongarch.h */ /* Basic CSRs */ #define LOONGARCH_CSR_CRMD 0x0 /* Current mode info */ @@ -57,7 +57,8 @@ FIELD(CSR_TLBIDX, PS, 24, 6) FIELD(CSR_TLBIDX, NE, 31, 1) #define LOONGARCH_CSR_TLBEHI 0x11 /* TLB EntryHi */ -FIELD(CSR_TLBEHI, VPPN, 13, 35) +FIELD(CSR_TLBEHI_32, VPPN, 13, 19) +FIELD(CSR_TLBEHI_64, VPPN, 13, 35) #define LOONGARCH_CSR_TLBELO0 0x12 /* TLB EntryLo0 */ #define LOONGARCH_CSR_TLBELO1 0x13 /* TLB EntryLo1 */ @@ -66,10 +67,14 @@ FIELD(TLBENTRY, D, 1, 1) FIELD(TLBENTRY, PLV, 2, 2) FIELD(TLBENTRY, MAT, 4, 2) FIELD(TLBENTRY, G, 6, 1) -FIELD(TLBENTRY, PPN, 12, 36) -FIELD(TLBENTRY, NR, 61, 1) -FIELD(TLBENTRY, NX, 62, 1) -FIELD(TLBENTRY, RPLV, 63, 1) +FIELD(TLBENTRY, HUGE, 6, 1) +FIELD(TLBENTRY, HGLOBAL, 12, 1) +FIELD(TLBENTRY, LEVEL, 13, 2) +FIELD(TLBENTRY_32, PPN, 8, 24) +FIELD(TLBENTRY_64, PPN, 12, 36) +FIELD(TLBENTRY_64, NR, 61, 1) +FIELD(TLBENTRY_64, NX, 62, 1) +FIELD(TLBENTRY_64, RPLV, 63, 1) #define LOONGARCH_CSR_ASID 0x18 /* Address space identifier */ FIELD(CSR_ASID, ASID, 0, 10) @@ -163,7 +168,8 @@ FIELD(CSR_TLBRERA, PC, 2, 62) #define LOONGARCH_CSR_TLBRELO1 0x8d /* TLB refill entrylo1 */ #define LOONGARCH_CSR_TLBREHI 0x8e /* TLB refill entryhi */ FIELD(CSR_TLBREHI, PS, 0, 6) -FIELD(CSR_TLBREHI, VPPN, 13, 35) +FIELD(CSR_TLBREHI_32, VPPN, 13, 19) +FIELD(CSR_TLBREHI_64, VPPN, 13, 35) #define LOONGARCH_CSR_TLBRPRMD 0x8f /* TLB refill mode info */ FIELD(CSR_TLBRPRMD, PPLV, 0, 2) FIELD(CSR_TLBRPRMD, PIE, 2, 1) @@ -187,10 +193,9 @@ FIELD(CSR_DMW, PLV1, 1, 1) FIELD(CSR_DMW, PLV2, 2, 1) FIELD(CSR_DMW, PLV3, 3, 1) FIELD(CSR_DMW, MAT, 4, 2) -FIELD(CSR_DMW, VSEG, 60, 4) - -#define dmw_va2pa(va) \ - (va & MAKE_64BIT_MASK(0, TARGET_VIRT_ADDR_SPACE_BITS)) +FIELD(CSR_DMW_32, PSEG, 25, 3) +FIELD(CSR_DMW_32, VSEG, 29, 3) +FIELD(CSR_DMW_64, VSEG, 60, 4) /* Debug CSRs */ #define LOONGARCH_CSR_DBG 0x500 /* debug config */ diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h index 414d8fff46..cfe195db4e 100644 --- a/target/loongarch/cpu-param.h +++ b/target/loongarch/cpu-param.h @@ -12,7 +12,6 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 48 #define TARGET_VIRT_ADDR_SPACE_BITS 48 -#define TARGET_PAGE_BITS 14 -#define NB_MMU_MODES 5 +#define TARGET_PAGE_BITS 12 #endif diff --git a/target/loongarch/cpu-qom.h b/target/loongarch/cpu-qom.h new file mode 100644 index 0000000000..fa3fcf7186 --- /dev/null +++ b/target/loongarch/cpu-qom.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch CPU QOM header (target agnostic) + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_CPU_QOM_H +#define LOONGARCH_CPU_QOM_H + +#include "hw/core/cpu.h" + +#define TYPE_LOONGARCH_CPU "loongarch-cpu" +#define TYPE_LOONGARCH32_CPU "loongarch32-cpu" +#define TYPE_LOONGARCH64_CPU "loongarch64-cpu" + +OBJECT_DECLARE_CPU_TYPE(LoongArchCPU, LoongArchCPUClass, + LOONGARCH_CPU) + +#define LOONGARCH_CPU_TYPE_SUFFIX "-" TYPE_LOONGARCH_CPU +#define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX + +#endif diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 4c8f96bc3a..203a349055 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -11,14 +11,25 @@ #include "qapi/error.h" #include "qemu/module.h" #include "sysemu/qtest.h" +#include "sysemu/tcg.h" +#include "sysemu/kvm.h" +#include "kvm/kvm_loongarch.h" #include "exec/exec-all.h" -#include "qapi/qapi-commands-machine-target.h" #include "cpu.h" #include "internals.h" #include "fpu/softfloat-helpers.h" #include "cpu-csr.h" +#ifndef CONFIG_USER_ONLY #include "sysemu/reset.h" -#include "hw/loader.h" +#endif +#include "vec.h" +#ifdef CONFIG_KVM +#include +#endif +#ifdef CONFIG_TCG +#include "exec/cpu_ldst.h" +#include "tcg/tcg.h" +#endif const char * const regnames[32] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", @@ -34,29 +45,45 @@ const char * const fregnames[32] = { "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", }; -static const char * const excp_names[] = { - [EXCCODE_INT] = "Interrupt", - [EXCCODE_PIL] = "Page invalid exception for load", - [EXCCODE_PIS] = "Page invalid exception for store", - [EXCCODE_PIF] = "Page invalid exception for fetch", - [EXCCODE_PME] = "Page modified exception", - [EXCCODE_PNR] = "Page Not Readable exception", - [EXCCODE_PNX] = "Page Not Executable exception", - [EXCCODE_PPI] = "Page Privilege error", - [EXCCODE_ADEF] = "Address error for instruction fetch", - [EXCCODE_ADEM] = "Address error for Memory access", - [EXCCODE_SYS] = "Syscall", - [EXCCODE_BRK] = "Break", - [EXCCODE_INE] = "Instruction Non-Existent", - [EXCCODE_IPE] = "Instruction privilege error", - [EXCCODE_FPE] = "Floating Point Exception", - [EXCCODE_DBP] = "Debug breakpoint", +struct TypeExcp { + int32_t exccode; + const char * const name; +}; + +static const struct TypeExcp excp_names[] = { + {EXCCODE_INT, "Interrupt"}, + {EXCCODE_PIL, "Page invalid exception for load"}, + {EXCCODE_PIS, "Page invalid exception for store"}, + {EXCCODE_PIF, "Page invalid exception for fetch"}, + {EXCCODE_PME, "Page modified exception"}, + {EXCCODE_PNR, "Page Not Readable exception"}, + {EXCCODE_PNX, "Page Not Executable exception"}, + {EXCCODE_PPI, "Page Privilege error"}, + {EXCCODE_ADEF, "Address error for instruction fetch"}, + {EXCCODE_ADEM, "Address error for Memory access"}, + {EXCCODE_SYS, "Syscall"}, + {EXCCODE_BRK, "Break"}, + {EXCCODE_INE, "Instruction Non-Existent"}, + {EXCCODE_IPE, "Instruction privilege error"}, + {EXCCODE_FPD, "Floating Point Disabled"}, + {EXCCODE_FPE, "Floating Point Exception"}, + {EXCCODE_DBP, "Debug breakpoint"}, + {EXCCODE_BCE, "Bound Check Exception"}, + {EXCCODE_SXD, "128 bit vector instructions Disable exception"}, + {EXCCODE_ASXD, "256 bit vector instructions Disable exception"}, + {EXCP_HLT, "EXCP_HLT"}, }; const char *loongarch_exception_name(int32_t exception) { - assert(excp_names[exception]); - return excp_names[exception]; + int i; + + for (i = 0; i < ARRAY_SIZE(excp_names); i++) { + if (excp_names[i].exccode == exception) { + return excp_names[i].name; + } + } + return "Unknown"; } void G_NORETURN do_raise_exception(CPULoongArchState *env, @@ -65,7 +92,7 @@ void G_NORETURN do_raise_exception(CPULoongArchState *env, { CPUState *cs = env_cpu(env); - qemu_log_mask(CPU_LOG_INT, "%s: %d (%s)\n", + qemu_log_mask(CPU_LOG_INT, "%s: expection: %d (%s)\n", __func__, exception, loongarch_exception_name(exception)); @@ -76,12 +103,15 @@ void G_NORETURN do_raise_exception(CPULoongArchState *env, static void loongarch_cpu_set_pc(CPUState *cs, vaddr value) { - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; - - env->pc = value; + set_pc(cpu_env(cs), value); } +static vaddr loongarch_cpu_get_pc(CPUState *cs) +{ + return cpu_env(cs)->pc; +} + +#ifndef CONFIG_USER_ONLY #include "hw/loongarch/virt.h" void loongarch_cpu_set_irq(void *opaque, int irq, int level) @@ -94,12 +124,15 @@ void loongarch_cpu_set_irq(void *opaque, int irq, int level) return; } - env->CSR_ESTAT = deposit64(env->CSR_ESTAT, irq, 1, level != 0); - - if (FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS)) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + if (kvm_enabled()) { + kvm_loongarch_set_interrupt(cpu, irq, level); + } else if (tcg_enabled()) { + env->CSR_ESTAT = deposit64(env->CSR_ESTAT, irq, 1, level != 0); + if (FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS)) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } } } @@ -118,37 +151,31 @@ static inline bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env) { uint32_t pending; uint32_t status; - bool r; pending = FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS); status = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, LIE); - r = (pending & status) != 0; - return r; + return (pending & status) != 0; } +#endif +#ifdef CONFIG_TCG +#ifndef CONFIG_USER_ONLY static void loongarch_cpu_do_interrupt(CPUState *cs) { - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; + CPULoongArchState *env = cpu_env(cs); bool update_badinstr = 1; int cause = -1; - const char *name; bool tlbfill = FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR); uint32_t vec_size = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, VS); if (cs->exception_index != EXCCODE_INT) { - if (cs->exception_index < 0 || - cs->exception_index > ARRAY_SIZE(excp_names)) { - name = "unknown"; - } else { - name = excp_names[cs->exception_index]; - } - qemu_log_mask(CPU_LOG_INT, "%s enter: pc " TARGET_FMT_lx " ERA " TARGET_FMT_lx - " TLBRERA " TARGET_FMT_lx " %s exception\n", __func__, - env->pc, env->CSR_ERA, env->CSR_TLBRERA, name); + " TLBRERA " TARGET_FMT_lx " exception: %d (%s)\n", + __func__, env->pc, env->CSR_ERA, env->CSR_TLBRERA, + cs->exception_index, + loongarch_exception_name(cs->exception_index)); } switch (cs->exception_index) { @@ -159,7 +186,7 @@ static void loongarch_cpu_do_interrupt(CPUState *cs) set_DERA: env->CSR_DERA = env->pc; env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DST, 1); - env->pc = env->CSR_EENTRY + 0x480; + set_pc(env, env->CSR_EENTRY + 0x480); break; case EXCCODE_INT: if (FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) { @@ -168,26 +195,33 @@ static void loongarch_cpu_do_interrupt(CPUState *cs) } QEMU_FALLTHROUGH; case EXCCODE_PIF: + case EXCCODE_ADEF: cause = cs->exception_index; update_badinstr = 0; break; - case EXCCODE_ADEM: case EXCCODE_SYS: case EXCCODE_BRK: + case EXCCODE_INE: + case EXCCODE_IPE: + case EXCCODE_FPD: + case EXCCODE_FPE: + case EXCCODE_SXD: + case EXCCODE_ASXD: + env->CSR_BADV = env->pc; + QEMU_FALLTHROUGH; + case EXCCODE_BCE: + case EXCCODE_ADEM: case EXCCODE_PIL: case EXCCODE_PIS: case EXCCODE_PME: case EXCCODE_PNR: case EXCCODE_PNX: case EXCCODE_PPI: - case EXCCODE_INE: - case EXCCODE_IPE: - case EXCCODE_FPE: cause = cs->exception_index; break; default: - qemu_log("Error: exception(%d) '%s' has not been supported\n", - cs->exception_index, excp_names[cs->exception_index]); + qemu_log("Error: exception(%d) has not been supported\n", + cs->exception_index); abort(); } @@ -208,7 +242,10 @@ static void loongarch_cpu_do_interrupt(CPUState *cs) env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, PC, (env->pc >> 2)); } else { - env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ECODE, cause); + env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ECODE, + EXCODE_MCODE(cause)); + env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ESUBCODE, + EXCODE_SUBCODE(cause)); env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PPLV, FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV)); env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PIE, @@ -219,6 +256,10 @@ static void loongarch_cpu_do_interrupt(CPUState *cs) env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0); env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0); + if (vec_size) { + vec_size = (1 << vec_size) * 4; + } + if (cs->exception_index == EXCCODE_INT) { /* Interrupt */ uint32_t vector = 0; @@ -227,7 +268,8 @@ static void loongarch_cpu_do_interrupt(CPUState *cs) /* Find the highest-priority interrupt. */ vector = 31 - clz32(pending); - env->pc = env->CSR_EENTRY + (EXCCODE_EXTERNAL_INT + vector) * vec_size; + set_pc(env, env->CSR_EENTRY + \ + (EXCCODE_EXTERNAL_INT + vector) * vec_size); qemu_log_mask(CPU_LOG_INT, "%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx " cause %d\n" " A " TARGET_FMT_lx " D " @@ -238,10 +280,9 @@ static void loongarch_cpu_do_interrupt(CPUState *cs) env->CSR_ECFG, env->CSR_ESTAT); } else { if (tlbfill) { - env->pc = env->CSR_TLBRENTRY; + set_pc(env, env->CSR_TLBRENTRY); } else { - env->pc = env->CSR_EENTRY; - env->pc += cause * vec_size; + set_pc(env, env->CSR_EENTRY + EXCODE_MCODE(cause) * vec_size); } qemu_log_mask(CPU_LOG_INT, "%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx @@ -266,8 +307,7 @@ static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, MemTxResult response, uintptr_t retaddr) { - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; + CPULoongArchState *env = cpu_env(cs); if (access_type == MMU_INST_FETCH) { do_raise_exception(env, EXCCODE_ADEF, retaddr); @@ -279,8 +319,7 @@ static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { if (interrupt_request & CPU_INTERRUPT_HARD) { - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; + CPULoongArchState *env = cpu_env(cs); if (cpu_loongarch_hw_interrupts_enabled(env) && cpu_loongarch_hw_interrupts_pending(env)) { @@ -292,30 +331,47 @@ static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } return false; } +#endif -#ifdef CONFIG_TCG static void loongarch_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; + tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + set_pc(cpu_env(cs), tb->pc); +} - env->pc = tb->pc; +static void loongarch_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + set_pc(cpu_env(cs), data[0]); } #endif /* CONFIG_TCG */ static bool loongarch_cpu_has_work(CPUState *cs) { - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; +#ifdef CONFIG_USER_ONLY + return true; +#else bool has_work = false; if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && - cpu_loongarch_hw_interrupts_pending(env)) { + cpu_loongarch_hw_interrupts_pending(cpu_env(cs))) { has_work = true; } return has_work; +#endif +} + +static int loongarch_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + CPULoongArchState *env = cpu_env(cs); + + if (FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG)) { + return FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV); + } + return MMU_DA_IDX; } static void loongarch_la464_initfn(Object *obj) @@ -328,6 +384,7 @@ static void loongarch_la464_initfn(Object *obj) env->cpucfg[i] = 0x0; } + cpu->dtb_compatible = "loongarch,Loongson-3A5000"; env->cpucfg[0] = 0x14c010; /* PRID */ uint32_t data = 0; @@ -349,8 +406,11 @@ static void loongarch_la464_initfn(Object *obj) data = FIELD_DP32(data, CPUCFG2, FP_SP, 1); data = FIELD_DP32(data, CPUCFG2, FP_DP, 1); data = FIELD_DP32(data, CPUCFG2, FP_VER, 1); + data = FIELD_DP32(data, CPUCFG2, LSX, 1), + data = FIELD_DP32(data, CPUCFG2, LASX, 1), data = FIELD_DP32(data, CPUCFG2, LLFTP, 1); data = FIELD_DP32(data, CPUCFG2, LLFTP_VER, 1); + data = FIELD_DP32(data, CPUCFG2, LSPW, 1); data = FIELD_DP32(data, CPUCFG2, LAM, 1); env->cpucfg[2] = data; @@ -393,35 +453,57 @@ static void loongarch_la464_initfn(Object *obj) data = 0; data = FIELD_DP32(data, CPUCFG20, L3IU_WAYS, 15); data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 14); - data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 6); + data = FIELD_DP32(data, CPUCFG20, L3IU_SIZE, 6); env->cpucfg[20] = data; env->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa); + loongarch_cpu_post_init(obj); } -static void loongarch_cpu_list_entry(gpointer data, gpointer user_data) +static void loongarch_la132_initfn(Object *obj) { - const char *typename = object_class_get_name(OBJECT_CLASS(data)); - - qemu_printf("%s\n", typename); -} - -void loongarch_cpu_list(void) -{ - GSList *list; - list = object_class_get_list_sorted(TYPE_LOONGARCH_CPU, false); - g_slist_foreach(list, loongarch_cpu_list_entry, NULL); - g_slist_free(list); -} - -static void loongarch_cpu_reset(DeviceState *dev) -{ - CPUState *cs = CPU(dev); - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(cpu); + LoongArchCPU *cpu = LOONGARCH_CPU(obj); CPULoongArchState *env = &cpu->env; - lacc->parent_reset(dev); + int i; + + for (i = 0; i < 21; i++) { + env->cpucfg[i] = 0x0; + } + + cpu->dtb_compatible = "loongarch,Loongson-1C103"; + env->cpucfg[0] = 0x148042; /* PRID */ + + uint32_t data = 0; + data = FIELD_DP32(data, CPUCFG1, ARCH, 1); /* LA32 */ + data = FIELD_DP32(data, CPUCFG1, PGMMU, 1); + data = FIELD_DP32(data, CPUCFG1, IOCSR, 1); + data = FIELD_DP32(data, CPUCFG1, PALEN, 0x1f); /* 32 bits */ + data = FIELD_DP32(data, CPUCFG1, VALEN, 0x1f); /* 32 bits */ + data = FIELD_DP32(data, CPUCFG1, UAL, 1); + data = FIELD_DP32(data, CPUCFG1, RI, 0); + data = FIELD_DP32(data, CPUCFG1, EP, 0); + data = FIELD_DP32(data, CPUCFG1, RPLV, 0); + data = FIELD_DP32(data, CPUCFG1, HP, 1); + data = FIELD_DP32(data, CPUCFG1, IOCSR_BRD, 1); + env->cpucfg[1] = data; +} + +static void loongarch_max_initfn(Object *obj) +{ + /* '-cpu max' for TCG: we use cpu la464. */ + loongarch_la464_initfn(obj); +} + +static void loongarch_cpu_reset_hold(Object *obj) +{ + CPUState *cs = CPU(obj); + LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(obj); + CPULoongArchState *env = cpu_env(cs); + + if (lacc->parent_phases.hold) { + lacc->parent_phases.hold(obj); + } env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3; env->fcsr0 = 0x0; @@ -447,10 +529,12 @@ static void loongarch_cpu_reset(DeviceState *dev) env->CSR_ESTAT = env->CSR_ESTAT & (~MAKE_64BIT_MASK(0, 2)); env->CSR_RVACFG = FIELD_DP64(env->CSR_RVACFG, CSR_RVACFG, RBITS, 0); + env->CSR_CPUID = cs->cpu_index; env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0); env->CSR_LLBCTL = FIELD_DP64(env->CSR_LLBCTL, CSR_LLBCTL, KLO, 0); env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0); env->CSR_MERRCTL = FIELD_DP64(env->CSR_MERRCTL, CSR_MERRCTL, ISMERR, 0); + env->CSR_TID = cs->cpu_index; env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2); env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63); @@ -464,9 +548,17 @@ static void loongarch_cpu_reset(DeviceState *dev) env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV3, 0); } +#ifndef CONFIG_USER_ONLY env->pc = 0x1c000000; + memset(env->tlb, 0, sizeof(env->tlb)); + if (kvm_enabled()) { + kvm_arch_reset_vcpu(env); + } +#endif +#ifdef CONFIG_TCG restore_fp_status(env); +#endif cs->exception_index = -1; } @@ -495,73 +587,102 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) lacc->parent_realize(dev, errp); } -static void loongarch_qemu_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) +static bool loongarch_get_lsx(Object *obj, Error **errp) { -} + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + bool ret; -static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size) -{ - switch (addr) { - case FEATURE_REG: - return 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI | - 1ULL << IOCSRF_CSRIPI; - case VENDOR_REG: - return 0x6e6f73676e6f6f4cULL; /* "Loongson" */ - case CPUNAME_REG: - return 0x303030354133ULL; /* "3A5000" */ - case MISC_FUNC_REG: - return 1ULL << IOCSRM_EXTIOI_EN; + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { + ret = true; + } else { + ret = false; } - return 0ULL; + return ret; } -static const MemoryRegionOps loongarch_qemu_ops = { - .read = loongarch_qemu_read, - .write = loongarch_qemu_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 8, - .max_access_size = 8, - }, -}; +static void loongarch_set_lsx(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + if (value) { + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 1); + } else { + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 0); + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 0); + } +} + +static bool loongarch_get_lasx(Object *obj, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + bool ret; + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { + ret = true; + } else { + ret = false; + } + return ret; +} + +static void loongarch_set_lasx(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + if (value) { + if (!FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 1); + } + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 1); + } else { + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 0); + } +} + +void loongarch_cpu_post_init(Object *obj) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { + object_property_add_bool(obj, "lsx", loongarch_get_lsx, + loongarch_set_lsx); + } + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { + object_property_add_bool(obj, "lasx", loongarch_get_lasx, + loongarch_set_lasx); + } +} static void loongarch_cpu_init(Object *obj) { +#ifndef CONFIG_USER_ONLY LoongArchCPU *cpu = LOONGARCH_CPU(obj); - CPULoongArchState *env = &cpu->env; - cpu_set_cpustate_pointers(cpu); qdev_init_gpio_in(DEVICE(cpu), loongarch_cpu_set_irq, N_IRQS); +#ifdef CONFIG_TCG timer_init_ns(&cpu->timer, QEMU_CLOCK_VIRTUAL, &loongarch_constant_timer_cb, cpu); - memory_region_init_io(&env->system_iocsr, OBJECT(cpu), NULL, - env, "iocsr", UINT64_MAX); - address_space_init(&env->address_space_iocsr, &env->system_iocsr, "IOCSR"); - memory_region_init_io(&env->iocsr_mem, OBJECT(cpu), &loongarch_qemu_ops, - NULL, "iocsr_misc", 0x428); - memory_region_add_subregion(&env->system_iocsr, 0, &env->iocsr_mem); +#endif +#endif } static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model) { ObjectClass *oc; - char *typename; - typename = g_strdup_printf(LOONGARCH_CPU_TYPE_NAME("%s"), cpu_model); - oc = object_class_by_name(typename); - g_free(typename); + oc = object_class_by_name(cpu_model); + if (!oc) { + g_autofree char *typename + = g_strdup_printf(LOONGARCH_CPU_TYPE_NAME("%s"), cpu_model); + oc = object_class_by_name(typename); + } + return oc; } void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; + CPULoongArchState *env = cpu_env(cs); int i; qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc); @@ -593,11 +714,13 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) qemu_fprintf(f, "TLBRENTRY=%016" PRIx64 "\n", env->CSR_TLBRENTRY); qemu_fprintf(f, "TLBRBADV=%016" PRIx64 "\n", env->CSR_TLBRBADV); qemu_fprintf(f, "TLBRERA=%016" PRIx64 "\n", env->CSR_TLBRERA); + qemu_fprintf(f, "TCFG=%016" PRIx64 "\n", env->CSR_TCFG); + qemu_fprintf(f, "TVAL=%016" PRIx64 "\n", env->CSR_TVAL); /* fpr */ if (flags & CPU_DUMP_FPU) { for (i = 0; i < 32; i++) { - qemu_fprintf(f, " %s %016" PRIx64, fregnames[i], env->fpr[i]); + qemu_fprintf(f, " %s %016" PRIx64, fregnames[i], env->fpr[i].vreg.D(0)); if ((i & 3) == 3) { qemu_fprintf(f, "\n"); } @@ -608,45 +731,61 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" -static struct TCGCPUOps loongarch_tcg_ops = { +static const TCGCPUOps loongarch_tcg_ops = { .initialize = loongarch_translate_init, .synchronize_from_tb = loongarch_cpu_synchronize_from_tb, + .restore_state_to_opc = loongarch_restore_state_to_opc, +#ifndef CONFIG_USER_ONLY .tlb_fill = loongarch_cpu_tlb_fill, .cpu_exec_interrupt = loongarch_cpu_exec_interrupt, .do_interrupt = loongarch_cpu_do_interrupt, .do_transaction_failed = loongarch_cpu_do_transaction_failed, +#endif }; #endif /* CONFIG_TCG */ +#ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps loongarch_sysemu_ops = { .get_phys_page_debug = loongarch_cpu_get_phys_page_debug, }; +static int64_t loongarch_cpu_get_arch_id(CPUState *cs) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + + return cpu->phy_id; +} +#endif + static void loongarch_cpu_class_init(ObjectClass *c, void *data) { LoongArchCPUClass *lacc = LOONGARCH_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); DeviceClass *dc = DEVICE_CLASS(c); + ResettableClass *rc = RESETTABLE_CLASS(c); device_class_set_parent_realize(dc, loongarch_cpu_realizefn, &lacc->parent_realize); - device_class_set_parent_reset(dc, loongarch_cpu_reset, &lacc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, loongarch_cpu_reset_hold, NULL, + &lacc->parent_phases); cc->class_by_name = loongarch_cpu_class_by_name; cc->has_work = loongarch_cpu_has_work; + cc->mmu_index = loongarch_cpu_mmu_index; cc->dump_state = loongarch_cpu_dump_state; cc->set_pc = loongarch_cpu_set_pc; + cc->get_pc = loongarch_cpu_get_pc; +#ifndef CONFIG_USER_ONLY + cc->get_arch_id = loongarch_cpu_get_arch_id; dc->vmsd = &vmstate_loongarch_cpu; cc->sysemu_ops = &loongarch_sysemu_ops; +#endif cc->disas_set_info = loongarch_cpu_disas_set_info; cc->gdb_read_register = loongarch_cpu_gdb_read_register; cc->gdb_write_register = loongarch_cpu_gdb_write_register; - cc->disas_set_info = loongarch_cpu_disas_set_info; - cc->gdb_num_core_regs = 34; - cc->gdb_core_xml_file = "loongarch-base64.xml"; cc->gdb_stop_before_watchpoint = true; #ifdef CONFIG_TCG @@ -654,9 +793,35 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data) #endif } -#define DEFINE_LOONGARCH_CPU_TYPE(model, initfn) \ +static const gchar *loongarch32_gdb_arch_name(CPUState *cs) +{ + return "loongarch32"; +} + +static void loongarch32_cpu_class_init(ObjectClass *c, void *data) +{ + CPUClass *cc = CPU_CLASS(c); + + cc->gdb_core_xml_file = "loongarch-base32.xml"; + cc->gdb_arch_name = loongarch32_gdb_arch_name; +} + +static const gchar *loongarch64_gdb_arch_name(CPUState *cs) +{ + return "loongarch64"; +} + +static void loongarch64_cpu_class_init(ObjectClass *c, void *data) +{ + CPUClass *cc = CPU_CLASS(c); + + cc->gdb_core_xml_file = "loongarch-base64.xml"; + cc->gdb_arch_name = loongarch64_gdb_arch_name; +} + +#define DEFINE_LOONGARCH_CPU_TYPE(size, model, initfn) \ { \ - .parent = TYPE_LOONGARCH_CPU, \ + .parent = TYPE_LOONGARCH##size##_CPU, \ .instance_init = initfn, \ .name = LOONGARCH_CPU_TYPE_NAME(model), \ } @@ -666,39 +831,30 @@ static const TypeInfo loongarch_cpu_type_infos[] = { .name = TYPE_LOONGARCH_CPU, .parent = TYPE_CPU, .instance_size = sizeof(LoongArchCPU), + .instance_align = __alignof(LoongArchCPU), .instance_init = loongarch_cpu_init, .abstract = true, .class_size = sizeof(LoongArchCPUClass), .class_init = loongarch_cpu_class_init, }, - DEFINE_LOONGARCH_CPU_TYPE("la464", loongarch_la464_initfn), + { + .name = TYPE_LOONGARCH32_CPU, + .parent = TYPE_LOONGARCH_CPU, + + .abstract = true, + .class_init = loongarch32_cpu_class_init, + }, + { + .name = TYPE_LOONGARCH64_CPU, + .parent = TYPE_LOONGARCH_CPU, + + .abstract = true, + .class_init = loongarch64_cpu_class_init, + }, + DEFINE_LOONGARCH_CPU_TYPE(64, "la464", loongarch_la464_initfn), + DEFINE_LOONGARCH_CPU_TYPE(32, "la132", loongarch_la132_initfn), + DEFINE_LOONGARCH_CPU_TYPE(64, "max", loongarch_max_initfn), }; DEFINE_TYPES(loongarch_cpu_type_infos) - -static void loongarch_cpu_add_definition(gpointer data, gpointer user_data) -{ - ObjectClass *oc = data; - CpuDefinitionInfoList **cpu_list = user_data; - CpuDefinitionInfo *info = g_new0(CpuDefinitionInfo, 1); - const char *typename = object_class_get_name(oc); - - info->name = g_strndup(typename, - strlen(typename) - strlen("-" TYPE_LOONGARCH_CPU)); - info->q_typename = g_strdup(typename); - - QAPI_LIST_PREPEND(*cpu_list, info); -} - -CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) -{ - CpuDefinitionInfoList *cpu_list = NULL; - GSList *list; - - list = object_class_get_list(TYPE_LOONGARCH_CPU, false); - g_slist_foreach(list, loongarch_cpu_add_definition, &cpu_list); - g_slist_free(list); - - return cpu_list; -} diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 71a5036c3c..ec37579fd6 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -8,12 +8,16 @@ #ifndef LOONGARCH_CPU_H #define LOONGARCH_CPU_H +#include "qemu/int128.h" #include "exec/cpu-defs.h" #include "fpu/softfloat-types.h" #include "hw/registerfields.h" #include "qemu/timer.h" +#ifndef CONFIG_USER_ONLY #include "exec/memory.h" -#include "hw/sysbus.h" +#endif +#include "cpu-csr.h" +#include "cpu-qom.h" #define IOCSRF_TEMP 0 #define IOCSRF_NODECNT 1 @@ -26,6 +30,7 @@ #define IOCSRF_GMOD 9 #define IOCSRF_VM 11 +#define VERSION_REG 0x0 #define FEATURE_REG 0x8 #define VENDOR_REG 0x10 #define CPUNAME_REG 0x20 @@ -47,11 +52,27 @@ FIELD(FCSR0, FLAGS, 16, 5) FIELD(FCSR0, CAUSE, 24, 5) #define GET_FP_CAUSE(REG) FIELD_EX32(REG, FCSR0, CAUSE) -#define SET_FP_CAUSE(REG, V) FIELD_DP32(REG, FCSR0, CAUSE, V) +#define SET_FP_CAUSE(REG, V) \ + do { \ + (REG) = FIELD_DP32(REG, FCSR0, CAUSE, V); \ + } while (0) +#define UPDATE_FP_CAUSE(REG, V) \ + do { \ + (REG) |= FIELD_DP32(0, FCSR0, CAUSE, V); \ + } while (0) + #define GET_FP_ENABLES(REG) FIELD_EX32(REG, FCSR0, ENABLES) -#define SET_FP_ENABLES(REG, V) FIELD_DP32(REG, FCSR0, ENABLES, V) +#define SET_FP_ENABLES(REG, V) \ + do { \ + (REG) = FIELD_DP32(REG, FCSR0, ENABLES, V); \ + } while (0) + #define GET_FP_FLAGS(REG) FIELD_EX32(REG, FCSR0, FLAGS) -#define SET_FP_FLAGS(REG, V) FIELD_DP32(REG, FCSR0, FLAGS, V) +#define SET_FP_FLAGS(REG, V) \ + do { \ + (REG) = FIELD_DP32(REG, FCSR0, FLAGS, V); \ + } while (0) + #define UPDATE_FP_FLAGS(REG, V) \ do { \ (REG) |= FIELD_DP32(0, FCSR0, FLAGS, V); \ @@ -63,33 +84,37 @@ FIELD(FCSR0, CAUSE, 24, 5) #define FP_DIV0 8 #define FP_INVALID 16 -#define EXCCODE_EXTERNAL_INT 64 /* plus external interrupt number */ -#define EXCCODE_INT 0 -#define EXCCODE_PIL 1 -#define EXCCODE_PIS 2 -#define EXCCODE_PIF 3 -#define EXCCODE_PME 4 -#define EXCCODE_PNR 5 -#define EXCCODE_PNX 6 -#define EXCCODE_PPI 7 -#define EXCCODE_ADEF 8 /* Different exception subcode */ -#define EXCCODE_ADEM 8 -#define EXCCODE_ALE 9 -#define EXCCODE_BCE 10 -#define EXCCODE_SYS 11 -#define EXCCODE_BRK 12 -#define EXCCODE_INE 13 -#define EXCCODE_IPE 14 -#define EXCCODE_FPD 15 -#define EXCCODE_SXD 16 -#define EXCCODE_ASXD 17 -#define EXCCODE_FPE 18 /* Different exception subcode */ -#define EXCCODE_VFPE 18 -#define EXCCODE_WPEF 19 /* Different exception subcode */ -#define EXCCODE_WPEM 19 -#define EXCCODE_BTD 20 -#define EXCCODE_BTE 21 -#define EXCCODE_DBP 26 /* Reserved subcode used for debug */ +#define EXCODE(code, subcode) ( ((subcode) << 6) | (code) ) +#define EXCODE_MCODE(code) ( (code) & 0x3f ) +#define EXCODE_SUBCODE(code) ( (code) >> 6 ) + +#define EXCCODE_EXTERNAL_INT 64 /* plus external interrupt number */ +#define EXCCODE_INT EXCODE(0, 0) +#define EXCCODE_PIL EXCODE(1, 0) +#define EXCCODE_PIS EXCODE(2, 0) +#define EXCCODE_PIF EXCODE(3, 0) +#define EXCCODE_PME EXCODE(4, 0) +#define EXCCODE_PNR EXCODE(5, 0) +#define EXCCODE_PNX EXCODE(6, 0) +#define EXCCODE_PPI EXCODE(7, 0) +#define EXCCODE_ADEF EXCODE(8, 0) /* Different exception subcode */ +#define EXCCODE_ADEM EXCODE(8, 1) +#define EXCCODE_ALE EXCODE(9, 0) +#define EXCCODE_BCE EXCODE(10, 0) +#define EXCCODE_SYS EXCODE(11, 0) +#define EXCCODE_BRK EXCODE(12, 0) +#define EXCCODE_INE EXCODE(13, 0) +#define EXCCODE_IPE EXCODE(14, 0) +#define EXCCODE_FPD EXCODE(15, 0) +#define EXCCODE_SXD EXCODE(16, 0) +#define EXCCODE_ASXD EXCODE(17, 0) +#define EXCCODE_FPE EXCODE(18, 0) /* Different exception subcode */ +#define EXCCODE_VFPE EXCODE(18, 1) +#define EXCCODE_WPEF EXCODE(19, 0) /* Different exception subcode */ +#define EXCCODE_WPEM EXCODE(19, 1) +#define EXCCODE_BTD EXCODE(20, 0) +#define EXCCODE_BTE EXCODE(21, 0) +#define EXCCODE_DBP EXCODE(26, 0) /* Reserved subcode used for debug */ /* cpucfg[0] bits */ FIELD(CPUCFG0, PRID, 0, 32) @@ -108,6 +133,11 @@ FIELD(CPUCFG1, HP, 24, 1) FIELD(CPUCFG1, IOCSR_BRD, 25, 1) FIELD(CPUCFG1, MSG_INT, 26, 1) +/* cpucfg[1].arch */ +#define CPUCFG1_ARCH_LA32R 0 +#define CPUCFG1_ARCH_LA32 1 +#define CPUCFG1_ARCH_LA64 2 + /* cpucfg[2] bits */ FIELD(CPUCFG2, FP, 0, 1) FIELD(CPUCFG2, FP_SP, 1, 1) @@ -222,6 +252,26 @@ FIELD(TLB_MISC, ASID, 1, 10) FIELD(TLB_MISC, VPPN, 13, 35) FIELD(TLB_MISC, PS, 48, 6) +#define LSX_LEN (128) +#define LASX_LEN (256) + +typedef union VReg { + int8_t B[LASX_LEN / 8]; + int16_t H[LASX_LEN / 16]; + int32_t W[LASX_LEN / 32]; + int64_t D[LASX_LEN / 64]; + uint8_t UB[LASX_LEN / 8]; + uint16_t UH[LASX_LEN / 16]; + uint32_t UW[LASX_LEN / 32]; + uint64_t UD[LASX_LEN / 64]; + Int128 Q[LASX_LEN / 128]; +} VReg; + +typedef union fpr_t fpr_t; +union fpr_t { + VReg vreg; +}; + struct LoongArchTLB { uint64_t tlb_misc; /* Fields corresponding to CSR_TLBELO0/1 */ @@ -234,7 +284,7 @@ typedef struct CPUArchState { uint64_t gpr[32]; uint64_t pc; - uint64_t fpr[32]; + fpr_t fpr[32]; float_status fp_status; bool cf[8]; @@ -246,8 +296,6 @@ typedef struct CPUArchState { uint64_t lladdr; /* LL virtual address compared against SC */ uint64_t llval; - uint64_t badaddr; - /* LoongArch CSRs */ uint64_t CSR_CRMD; uint64_t CSR_PRMD; @@ -271,6 +319,7 @@ typedef struct CPUArchState { uint64_t CSR_PWCH; uint64_t CSR_STLBPS; uint64_t CSR_RVACFG; + uint64_t CSR_CPUID; uint64_t CSR_PRCFG1; uint64_t CSR_PRCFG2; uint64_t CSR_PRCFG3; @@ -303,13 +352,16 @@ typedef struct CPUArchState { uint64_t CSR_DERA; uint64_t CSR_DSAVE; +#ifndef CONFIG_USER_ONLY LoongArchTLB tlb[LOONGARCH_TLB_MAX]; - AddressSpace address_space_iocsr; - MemoryRegion system_iocsr; - MemoryRegion iocsr_mem; + AddressSpace *address_space_iocsr; bool load_elf; uint64_t elf_address; + uint32_t mp_state; + /* Store ipistate to access from this struct */ + DeviceState *ipistate; +#endif } CPULoongArchState; /** @@ -319,34 +371,30 @@ typedef struct CPUArchState { * A LoongArch CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPULoongArchState env; QEMUTimer timer; + uint32_t phy_id; + + /* 'compatible' string for this CPU for Linux device trees */ + const char *dtb_compatible; + /* used by KVM_REG_LOONGARCH_COUNTER ioctl to access guest time counters */ + uint64_t kvm_state_counter; }; -#define TYPE_LOONGARCH_CPU "loongarch-cpu" - -OBJECT_DECLARE_CPU_TYPE(LoongArchCPU, LoongArchCPUClass, - LOONGARCH_CPU) - /** * LoongArchCPUClass: * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. + * @parent_phases: The parent class' reset phase handlers. * * A LoongArch CPU model. */ struct LoongArchCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; - DeviceReset parent_reset; + ResettablePhases parent_phases; }; /* @@ -354,38 +402,63 @@ struct LoongArchCPUClass { * 0 for kernel mode, 3 for user mode. * Define an extra index for DA(direct addressing) mode. */ -#define MMU_KERNEL_IDX 0 -#define MMU_USER_IDX 3 +#define MMU_PLV_KERNEL 0 +#define MMU_PLV_USER 3 +#define MMU_KERNEL_IDX MMU_PLV_KERNEL +#define MMU_USER_IDX MMU_PLV_USER #define MMU_DA_IDX 4 -static inline int cpu_mmu_index(CPULoongArchState *env, bool ifetch) +static inline bool is_la64(CPULoongArchState *env) { - uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG); - - if (!pg) { - return MMU_DA_IDX; - } - return FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV); + return FIELD_EX32(env->cpucfg[1], CPUCFG1, ARCH) == CPUCFG1_ARCH_LA64; } -static inline void cpu_get_tb_cpu_state(CPULoongArchState *env, - target_ulong *pc, - target_ulong *cs_base, - uint32_t *flags) +static inline bool is_va32(CPULoongArchState *env) +{ + /* VA32 if !LA64 or VA32L[1-3] */ + bool va32 = !is_la64(env); + uint64_t plv = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV); + if (plv >= 1 && (FIELD_EX64(env->CSR_MISC, CSR_MISC, VA32) & (1 << plv))) { + va32 = true; + } + return va32; +} + +static inline void set_pc(CPULoongArchState *env, uint64_t value) +{ + if (is_va32(env)) { + env->pc = (uint32_t)value; + } else { + env->pc = value; + } +} + +/* + * LoongArch CPUs hardware flags. + */ +#define HW_FLAGS_PLV_MASK R_CSR_CRMD_PLV_MASK /* 0x03 */ +#define HW_FLAGS_EUEN_FPE 0x04 +#define HW_FLAGS_EUEN_SXE 0x08 +#define HW_FLAGS_CRMD_PG R_CSR_CRMD_PG_MASK /* 0x10 */ +#define HW_FLAGS_VA32 0x20 +#define HW_FLAGS_EUEN_ASXE 0x40 + +static inline void cpu_get_tb_cpu_state(CPULoongArchState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { *pc = env->pc; *cs_base = 0; - *flags = cpu_mmu_index(env, false); + *flags = env->CSR_CRMD & (R_CSR_CRMD_PLV_MASK | R_CSR_CRMD_PG_MASK); + *flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, FPE) * HW_FLAGS_EUEN_FPE; + *flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE) * HW_FLAGS_EUEN_SXE; + *flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE) * HW_FLAGS_EUEN_ASXE; + *flags |= is_va32(env) * HW_FLAGS_VA32; } -void loongarch_cpu_list(void); - -#define cpu_list loongarch_cpu_list - #include "exec/cpu-all.h" -#define LOONGARCH_CPU_TYPE_SUFFIX "-" TYPE_LOONGARCH_CPU -#define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU +void loongarch_cpu_post_init(Object *obj); + #endif /* LOONGARCH_CPU_H */ diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c new file mode 100644 index 0000000000..960eec9567 --- /dev/null +++ b/target/loongarch/cpu_helper.c @@ -0,0 +1,230 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch CPU helpers for qemu + * + * Copyright (c) 2024 Loongson Technology Corporation Limited + * + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "internals.h" +#include "cpu-csr.h" + +static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + int access_type, int index, int mmu_idx) +{ + LoongArchTLB *tlb = &env->tlb[index]; + uint64_t plv = mmu_idx; + uint64_t tlb_entry, tlb_ppn; + uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; + + if (index >= LOONGARCH_STLB) { + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); + } else { + tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + } + n = (address >> tlb_ps) & 0x1;/* Odd or even */ + + tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0; + tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); + tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); + tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); + if (is_la64(env)) { + tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); + tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); + tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); + tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); + } else { + tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); + tlb_nx = 0; + tlb_nr = 0; + tlb_rplv = 0; + } + + /* Remove sw bit between bit12 -- bit PS*/ + tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1)); + + /* Check access rights */ + if (!tlb_v) { + return TLBRET_INVALID; + } + + if (access_type == MMU_INST_FETCH && tlb_nx) { + return TLBRET_XI; + } + + if (access_type == MMU_DATA_LOAD && tlb_nr) { + return TLBRET_RI; + } + + if (((tlb_rplv == 0) && (plv > tlb_plv)) || + ((tlb_rplv == 1) && (plv != tlb_plv))) { + return TLBRET_PE; + } + + if ((access_type == MMU_DATA_STORE) && !tlb_d) { + return TLBRET_DIRTY; + } + + *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | + (address & MAKE_64BIT_MASK(0, tlb_ps)); + *prot = PAGE_READ; + if (tlb_d) { + *prot |= PAGE_WRITE; + } + if (!tlb_nx) { + *prot |= PAGE_EXEC; + } + return TLBRET_MATCH; +} + +/* + * One tlb entry holds an adjacent odd/even pair, the vpn is the + * content of the virtual page number divided by 2. So the + * compare vpn is bit[47:15] for 16KiB page. while the vppn + * field in tlb entry contains bit[47:13], so need adjust. + * virt_vpn = vaddr[47:13] + */ +bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, + int *index) +{ + LoongArchTLB *tlb; + uint16_t csr_asid, tlb_asid, stlb_idx; + uint8_t tlb_e, tlb_ps, tlb_g, stlb_ps; + int i, compare_shift; + uint64_t vpn, tlb_vppn; + + csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); + stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1); + stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */ + compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; + + /* Search STLB */ + for (i = 0; i < 8; ++i) { + tlb = &env->tlb[i * 256 + stlb_idx]; + tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); + if (tlb_e) { + tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); + tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); + tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + + if ((tlb_g == 1 || tlb_asid == csr_asid) && + (vpn == (tlb_vppn >> compare_shift))) { + *index = i * 256 + stlb_idx; + return true; + } + } + } + + /* Search MTLB */ + for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) { + tlb = &env->tlb[i]; + tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); + if (tlb_e) { + tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); + tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); + tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; + vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1); + if ((tlb_g == 1 || tlb_asid == csr_asid) && + (vpn == (tlb_vppn >> compare_shift))) { + *index = i; + return true; + } + } + } + return false; +} + +static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + int index, match; + + match = loongarch_tlb_search(env, address, &index); + if (match) { + return loongarch_map_tlb_entry(env, physical, prot, + address, access_type, index, mmu_idx); + } + + return TLBRET_NOMATCH; +} + +static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, + target_ulong dmw) +{ + if (is_la64(env)) { + return va & TARGET_VIRT_MASK; + } else { + uint32_t pseg = FIELD_EX32(dmw, CSR_DMW_32, PSEG); + return (va & MAKE_64BIT_MASK(0, R_CSR_DMW_32_VSEG_SHIFT)) | \ + (pseg << R_CSR_DMW_32_VSEG_SHIFT); + } +} + +int get_physical_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + int user_mode = mmu_idx == MMU_USER_IDX; + int kernel_mode = mmu_idx == MMU_KERNEL_IDX; + uint32_t plv, base_c, base_v; + int64_t addr_high; + uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA); + uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG); + + /* Check PG and DA */ + if (da & !pg) { + *physical = address & TARGET_PHYS_MASK; + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return TLBRET_MATCH; + } + + plv = kernel_mode | (user_mode << R_CSR_DMW_PLV3_SHIFT); + if (is_la64(env)) { + base_v = address >> R_CSR_DMW_64_VSEG_SHIFT; + } else { + base_v = address >> R_CSR_DMW_32_VSEG_SHIFT; + } + /* Check direct map window */ + for (int i = 0; i < 4; i++) { + if (is_la64(env)) { + base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_64, VSEG); + } else { + base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_32, VSEG); + } + if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) { + *physical = dmw_va2pa(env, address, env->CSR_DMW[i]); + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return TLBRET_MATCH; + } + } + + /* Check valid extension */ + addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16); + if (!(addr_high == 0 || addr_high == -1)) { + return TLBRET_BADADDR; + } + + /* Mapped address */ + return loongarch_map_address(env, physical, prot, address, + access_type, mmu_idx); +} + +hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ + CPULoongArchState *env = cpu_env(cs); + hwaddr phys_addr; + int prot; + + if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, + cpu_mmu_index(cs, false)) != 0) { + return -1; + } + return phys_addr; +} diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c index 858dfcc53a..63989a6282 100644 --- a/target/loongarch/disas.c +++ b/target/loongarch/disas.c @@ -21,11 +21,21 @@ static inline int plus_1(DisasContext *ctx, int x) return x + 1; } +static inline int shl_1(DisasContext *ctx, int x) +{ + return x << 1; +} + static inline int shl_2(DisasContext *ctx, int x) { return x << 2; } +static inline int shl_3(DisasContext *ctx, int x) +{ + return x << 3; +} + #define CSR_NAME(REG) \ [LOONGARCH_CSR_##REG] = (#REG) @@ -110,10 +120,15 @@ static const char *get_csr_name(unsigned num) csr_names[num] : "Undefined CSR"; } -#define output(C, INSN, FMT, ...) \ -{ \ - (C)->info->fprintf_func((C)->info->stream, "%08x %-9s\t" FMT, \ - (C)->insn, INSN, ##__VA_ARGS__); \ +#define output(C, INSN, FMT, ...) \ + { \ + if ((C)->info->show_opcodes) { \ + (C)->info->fprintf_func((C)->info->stream, "%08x %-9s\t" FMT,\ + (C)->insn, INSN, ##__VA_ARGS__); \ + } else { \ + (C)->info->fprintf_func((C)->info->stream, "%-9s\t" FMT, \ + INSN, ##__VA_ARGS__); \ + } \ } #include "decode-insns.c.inc" @@ -180,6 +195,12 @@ static void output_hint_r_i(DisasContext *ctx, arg_hint_r_i *a, output(ctx, mnemonic, "%d, r%d, %d", a->hint, a->rj, a->imm); } +static void output_hint_rr(DisasContext *ctx, arg_hint_rr *a, + const char *mnemonic) +{ + output(ctx, mnemonic, "%d, r%d, r%d", a->hint, a->rj, a->rk); +} + static void output_i(DisasContext *ctx, arg_i *a, const char *mnemonic) { output(ctx, mnemonic, "%d", a->imm); @@ -519,10 +540,6 @@ INSN(fsel, fffc) INSN(addu16i_d, rr_i) INSN(lu12i_w, r_i) INSN(lu32i_d, r_i) -INSN(pcaddi, r_i) -INSN(pcalau12i, r_i) -INSN(pcaddu12i, r_i) -INSN(pcaddu18i, r_i) INSN(ll_w, rr_i) INSN(sc_w, rr_i) INSN(ll_d, rr_i) @@ -543,6 +560,7 @@ INSN(ld_bu, rr_i) INSN(ld_hu, rr_i) INSN(ld_wu, rr_i) INSN(preld, hint_r_i) +INSN(preldx, hint_rr) INSN(fld_s, fr_i) INSN(fst_s, fr_i) INSN(fld_d, fr_i) @@ -628,7 +646,7 @@ INSN(beqz, r_offs) INSN(bnez, r_offs) INSN(bceqz, c_offs) INSN(bcnez, c_offs) -INSN(jirl, rr_offs) +INSN(jirl, rr_i) INSN(b, offs) INSN(bl, offs) INSN(beq, rr_offs) @@ -755,3 +773,1861 @@ static bool trans_fcmp_cond_##suffix(DisasContext *ctx, \ FCMP_INSN(s) FCMP_INSN(d) + +#define PCADD_INSN(name) \ +static bool trans_##name(DisasContext *ctx, arg_##name *a) \ +{ \ + output(ctx, #name, "r%d, %d # 0x%" PRIx64, \ + a->rd, a->imm, gen_##name(ctx->pc, a->imm)); \ + return true; \ +} + +static uint64_t gen_pcaddi(uint64_t pc, int imm) +{ + return pc + (imm << 2); +} + +static uint64_t gen_pcalau12i(uint64_t pc, int imm) +{ + return (pc + (imm << 12)) & ~0xfff; +} + +static uint64_t gen_pcaddu12i(uint64_t pc, int imm) +{ + return pc + (imm << 12); +} + +static uint64_t gen_pcaddu18i(uint64_t pc, int imm) +{ + return pc + ((uint64_t)(imm) << 18); +} + +PCADD_INSN(pcaddi) +PCADD_INSN(pcalau12i) +PCADD_INSN(pcaddu12i) +PCADD_INSN(pcaddu18i) + +#define INSN_LSX(insn, type) \ +static bool trans_##insn(DisasContext *ctx, arg_##type * a) \ +{ \ + output_##type(ctx, a, #insn); \ + return true; \ +} + +static void output_cv(DisasContext *ctx, arg_cv *a, + const char *mnemonic) +{ + output(ctx, mnemonic, "fcc%d, v%d", a->cd, a->vj); +} + +static void output_vvv(DisasContext *ctx, arg_vvv *a, const char *mnemonic) +{ + output(ctx, mnemonic, "v%d, v%d, v%d", a->vd, a->vj, a->vk); +} + +static void output_vv_i(DisasContext *ctx, arg_vv_i *a, const char *mnemonic) +{ + output(ctx, mnemonic, "v%d, v%d, 0x%x", a->vd, a->vj, a->imm); +} + +static void output_vv(DisasContext *ctx, arg_vv *a, const char *mnemonic) +{ + output(ctx, mnemonic, "v%d, v%d", a->vd, a->vj); +} + +static void output_vvvv(DisasContext *ctx, arg_vvvv *a, const char *mnemonic) +{ + output(ctx, mnemonic, "v%d, v%d, v%d, v%d", a->vd, a->vj, a->vk, a->va); +} + +static void output_vr_i(DisasContext *ctx, arg_vr_i *a, const char *mnemonic) +{ + output(ctx, mnemonic, "v%d, r%d, 0x%x", a->vd, a->rj, a->imm); +} + +static void output_vr_ii(DisasContext *ctx, arg_vr_ii *a, const char *mnemonic) +{ + output(ctx, mnemonic, "v%d, r%d, 0x%x, 0x%x", a->vd, a->rj, a->imm, a->imm2); +} + +static void output_rv_i(DisasContext *ctx, arg_rv_i *a, const char *mnemonic) +{ + output(ctx, mnemonic, "r%d, v%d, 0x%x", a->rd, a->vj, a->imm); +} + +static void output_vr(DisasContext *ctx, arg_vr *a, const char *mnemonic) +{ + output(ctx, mnemonic, "v%d, r%d", a->vd, a->rj); +} + +static void output_vvr(DisasContext *ctx, arg_vvr *a, const char *mnemonic) +{ + output(ctx, mnemonic, "v%d, v%d, r%d", a->vd, a->vj, a->rk); +} + +static void output_vrr(DisasContext *ctx, arg_vrr *a, const char *mnemonic) +{ + output(ctx, mnemonic, "v%d, r%d, r%d", a->vd, a->rj, a->rk); +} + +static void output_v_i(DisasContext *ctx, arg_v_i *a, const char *mnemonic) +{ + output(ctx, mnemonic, "v%d, 0x%x", a->vd, a->imm); +} + +INSN_LSX(vadd_b, vvv) +INSN_LSX(vadd_h, vvv) +INSN_LSX(vadd_w, vvv) +INSN_LSX(vadd_d, vvv) +INSN_LSX(vadd_q, vvv) +INSN_LSX(vsub_b, vvv) +INSN_LSX(vsub_h, vvv) +INSN_LSX(vsub_w, vvv) +INSN_LSX(vsub_d, vvv) +INSN_LSX(vsub_q, vvv) + +INSN_LSX(vaddi_bu, vv_i) +INSN_LSX(vaddi_hu, vv_i) +INSN_LSX(vaddi_wu, vv_i) +INSN_LSX(vaddi_du, vv_i) +INSN_LSX(vsubi_bu, vv_i) +INSN_LSX(vsubi_hu, vv_i) +INSN_LSX(vsubi_wu, vv_i) +INSN_LSX(vsubi_du, vv_i) + +INSN_LSX(vneg_b, vv) +INSN_LSX(vneg_h, vv) +INSN_LSX(vneg_w, vv) +INSN_LSX(vneg_d, vv) + +INSN_LSX(vsadd_b, vvv) +INSN_LSX(vsadd_h, vvv) +INSN_LSX(vsadd_w, vvv) +INSN_LSX(vsadd_d, vvv) +INSN_LSX(vsadd_bu, vvv) +INSN_LSX(vsadd_hu, vvv) +INSN_LSX(vsadd_wu, vvv) +INSN_LSX(vsadd_du, vvv) +INSN_LSX(vssub_b, vvv) +INSN_LSX(vssub_h, vvv) +INSN_LSX(vssub_w, vvv) +INSN_LSX(vssub_d, vvv) +INSN_LSX(vssub_bu, vvv) +INSN_LSX(vssub_hu, vvv) +INSN_LSX(vssub_wu, vvv) +INSN_LSX(vssub_du, vvv) + +INSN_LSX(vhaddw_h_b, vvv) +INSN_LSX(vhaddw_w_h, vvv) +INSN_LSX(vhaddw_d_w, vvv) +INSN_LSX(vhaddw_q_d, vvv) +INSN_LSX(vhaddw_hu_bu, vvv) +INSN_LSX(vhaddw_wu_hu, vvv) +INSN_LSX(vhaddw_du_wu, vvv) +INSN_LSX(vhaddw_qu_du, vvv) +INSN_LSX(vhsubw_h_b, vvv) +INSN_LSX(vhsubw_w_h, vvv) +INSN_LSX(vhsubw_d_w, vvv) +INSN_LSX(vhsubw_q_d, vvv) +INSN_LSX(vhsubw_hu_bu, vvv) +INSN_LSX(vhsubw_wu_hu, vvv) +INSN_LSX(vhsubw_du_wu, vvv) +INSN_LSX(vhsubw_qu_du, vvv) + +INSN_LSX(vaddwev_h_b, vvv) +INSN_LSX(vaddwev_w_h, vvv) +INSN_LSX(vaddwev_d_w, vvv) +INSN_LSX(vaddwev_q_d, vvv) +INSN_LSX(vaddwod_h_b, vvv) +INSN_LSX(vaddwod_w_h, vvv) +INSN_LSX(vaddwod_d_w, vvv) +INSN_LSX(vaddwod_q_d, vvv) +INSN_LSX(vsubwev_h_b, vvv) +INSN_LSX(vsubwev_w_h, vvv) +INSN_LSX(vsubwev_d_w, vvv) +INSN_LSX(vsubwev_q_d, vvv) +INSN_LSX(vsubwod_h_b, vvv) +INSN_LSX(vsubwod_w_h, vvv) +INSN_LSX(vsubwod_d_w, vvv) +INSN_LSX(vsubwod_q_d, vvv) + +INSN_LSX(vaddwev_h_bu, vvv) +INSN_LSX(vaddwev_w_hu, vvv) +INSN_LSX(vaddwev_d_wu, vvv) +INSN_LSX(vaddwev_q_du, vvv) +INSN_LSX(vaddwod_h_bu, vvv) +INSN_LSX(vaddwod_w_hu, vvv) +INSN_LSX(vaddwod_d_wu, vvv) +INSN_LSX(vaddwod_q_du, vvv) +INSN_LSX(vsubwev_h_bu, vvv) +INSN_LSX(vsubwev_w_hu, vvv) +INSN_LSX(vsubwev_d_wu, vvv) +INSN_LSX(vsubwev_q_du, vvv) +INSN_LSX(vsubwod_h_bu, vvv) +INSN_LSX(vsubwod_w_hu, vvv) +INSN_LSX(vsubwod_d_wu, vvv) +INSN_LSX(vsubwod_q_du, vvv) + +INSN_LSX(vaddwev_h_bu_b, vvv) +INSN_LSX(vaddwev_w_hu_h, vvv) +INSN_LSX(vaddwev_d_wu_w, vvv) +INSN_LSX(vaddwev_q_du_d, vvv) +INSN_LSX(vaddwod_h_bu_b, vvv) +INSN_LSX(vaddwod_w_hu_h, vvv) +INSN_LSX(vaddwod_d_wu_w, vvv) +INSN_LSX(vaddwod_q_du_d, vvv) + +INSN_LSX(vavg_b, vvv) +INSN_LSX(vavg_h, vvv) +INSN_LSX(vavg_w, vvv) +INSN_LSX(vavg_d, vvv) +INSN_LSX(vavg_bu, vvv) +INSN_LSX(vavg_hu, vvv) +INSN_LSX(vavg_wu, vvv) +INSN_LSX(vavg_du, vvv) +INSN_LSX(vavgr_b, vvv) +INSN_LSX(vavgr_h, vvv) +INSN_LSX(vavgr_w, vvv) +INSN_LSX(vavgr_d, vvv) +INSN_LSX(vavgr_bu, vvv) +INSN_LSX(vavgr_hu, vvv) +INSN_LSX(vavgr_wu, vvv) +INSN_LSX(vavgr_du, vvv) + +INSN_LSX(vabsd_b, vvv) +INSN_LSX(vabsd_h, vvv) +INSN_LSX(vabsd_w, vvv) +INSN_LSX(vabsd_d, vvv) +INSN_LSX(vabsd_bu, vvv) +INSN_LSX(vabsd_hu, vvv) +INSN_LSX(vabsd_wu, vvv) +INSN_LSX(vabsd_du, vvv) + +INSN_LSX(vadda_b, vvv) +INSN_LSX(vadda_h, vvv) +INSN_LSX(vadda_w, vvv) +INSN_LSX(vadda_d, vvv) + +INSN_LSX(vmax_b, vvv) +INSN_LSX(vmax_h, vvv) +INSN_LSX(vmax_w, vvv) +INSN_LSX(vmax_d, vvv) +INSN_LSX(vmin_b, vvv) +INSN_LSX(vmin_h, vvv) +INSN_LSX(vmin_w, vvv) +INSN_LSX(vmin_d, vvv) +INSN_LSX(vmax_bu, vvv) +INSN_LSX(vmax_hu, vvv) +INSN_LSX(vmax_wu, vvv) +INSN_LSX(vmax_du, vvv) +INSN_LSX(vmin_bu, vvv) +INSN_LSX(vmin_hu, vvv) +INSN_LSX(vmin_wu, vvv) +INSN_LSX(vmin_du, vvv) +INSN_LSX(vmaxi_b, vv_i) +INSN_LSX(vmaxi_h, vv_i) +INSN_LSX(vmaxi_w, vv_i) +INSN_LSX(vmaxi_d, vv_i) +INSN_LSX(vmini_b, vv_i) +INSN_LSX(vmini_h, vv_i) +INSN_LSX(vmini_w, vv_i) +INSN_LSX(vmini_d, vv_i) +INSN_LSX(vmaxi_bu, vv_i) +INSN_LSX(vmaxi_hu, vv_i) +INSN_LSX(vmaxi_wu, vv_i) +INSN_LSX(vmaxi_du, vv_i) +INSN_LSX(vmini_bu, vv_i) +INSN_LSX(vmini_hu, vv_i) +INSN_LSX(vmini_wu, vv_i) +INSN_LSX(vmini_du, vv_i) + +INSN_LSX(vmul_b, vvv) +INSN_LSX(vmul_h, vvv) +INSN_LSX(vmul_w, vvv) +INSN_LSX(vmul_d, vvv) +INSN_LSX(vmuh_b, vvv) +INSN_LSX(vmuh_h, vvv) +INSN_LSX(vmuh_w, vvv) +INSN_LSX(vmuh_d, vvv) +INSN_LSX(vmuh_bu, vvv) +INSN_LSX(vmuh_hu, vvv) +INSN_LSX(vmuh_wu, vvv) +INSN_LSX(vmuh_du, vvv) + +INSN_LSX(vmulwev_h_b, vvv) +INSN_LSX(vmulwev_w_h, vvv) +INSN_LSX(vmulwev_d_w, vvv) +INSN_LSX(vmulwev_q_d, vvv) +INSN_LSX(vmulwod_h_b, vvv) +INSN_LSX(vmulwod_w_h, vvv) +INSN_LSX(vmulwod_d_w, vvv) +INSN_LSX(vmulwod_q_d, vvv) +INSN_LSX(vmulwev_h_bu, vvv) +INSN_LSX(vmulwev_w_hu, vvv) +INSN_LSX(vmulwev_d_wu, vvv) +INSN_LSX(vmulwev_q_du, vvv) +INSN_LSX(vmulwod_h_bu, vvv) +INSN_LSX(vmulwod_w_hu, vvv) +INSN_LSX(vmulwod_d_wu, vvv) +INSN_LSX(vmulwod_q_du, vvv) +INSN_LSX(vmulwev_h_bu_b, vvv) +INSN_LSX(vmulwev_w_hu_h, vvv) +INSN_LSX(vmulwev_d_wu_w, vvv) +INSN_LSX(vmulwev_q_du_d, vvv) +INSN_LSX(vmulwod_h_bu_b, vvv) +INSN_LSX(vmulwod_w_hu_h, vvv) +INSN_LSX(vmulwod_d_wu_w, vvv) +INSN_LSX(vmulwod_q_du_d, vvv) + +INSN_LSX(vmadd_b, vvv) +INSN_LSX(vmadd_h, vvv) +INSN_LSX(vmadd_w, vvv) +INSN_LSX(vmadd_d, vvv) +INSN_LSX(vmsub_b, vvv) +INSN_LSX(vmsub_h, vvv) +INSN_LSX(vmsub_w, vvv) +INSN_LSX(vmsub_d, vvv) + +INSN_LSX(vmaddwev_h_b, vvv) +INSN_LSX(vmaddwev_w_h, vvv) +INSN_LSX(vmaddwev_d_w, vvv) +INSN_LSX(vmaddwev_q_d, vvv) +INSN_LSX(vmaddwod_h_b, vvv) +INSN_LSX(vmaddwod_w_h, vvv) +INSN_LSX(vmaddwod_d_w, vvv) +INSN_LSX(vmaddwod_q_d, vvv) +INSN_LSX(vmaddwev_h_bu, vvv) +INSN_LSX(vmaddwev_w_hu, vvv) +INSN_LSX(vmaddwev_d_wu, vvv) +INSN_LSX(vmaddwev_q_du, vvv) +INSN_LSX(vmaddwod_h_bu, vvv) +INSN_LSX(vmaddwod_w_hu, vvv) +INSN_LSX(vmaddwod_d_wu, vvv) +INSN_LSX(vmaddwod_q_du, vvv) +INSN_LSX(vmaddwev_h_bu_b, vvv) +INSN_LSX(vmaddwev_w_hu_h, vvv) +INSN_LSX(vmaddwev_d_wu_w, vvv) +INSN_LSX(vmaddwev_q_du_d, vvv) +INSN_LSX(vmaddwod_h_bu_b, vvv) +INSN_LSX(vmaddwod_w_hu_h, vvv) +INSN_LSX(vmaddwod_d_wu_w, vvv) +INSN_LSX(vmaddwod_q_du_d, vvv) + +INSN_LSX(vdiv_b, vvv) +INSN_LSX(vdiv_h, vvv) +INSN_LSX(vdiv_w, vvv) +INSN_LSX(vdiv_d, vvv) +INSN_LSX(vdiv_bu, vvv) +INSN_LSX(vdiv_hu, vvv) +INSN_LSX(vdiv_wu, vvv) +INSN_LSX(vdiv_du, vvv) +INSN_LSX(vmod_b, vvv) +INSN_LSX(vmod_h, vvv) +INSN_LSX(vmod_w, vvv) +INSN_LSX(vmod_d, vvv) +INSN_LSX(vmod_bu, vvv) +INSN_LSX(vmod_hu, vvv) +INSN_LSX(vmod_wu, vvv) +INSN_LSX(vmod_du, vvv) + +INSN_LSX(vsat_b, vv_i) +INSN_LSX(vsat_h, vv_i) +INSN_LSX(vsat_w, vv_i) +INSN_LSX(vsat_d, vv_i) +INSN_LSX(vsat_bu, vv_i) +INSN_LSX(vsat_hu, vv_i) +INSN_LSX(vsat_wu, vv_i) +INSN_LSX(vsat_du, vv_i) + +INSN_LSX(vexth_h_b, vv) +INSN_LSX(vexth_w_h, vv) +INSN_LSX(vexth_d_w, vv) +INSN_LSX(vexth_q_d, vv) +INSN_LSX(vexth_hu_bu, vv) +INSN_LSX(vexth_wu_hu, vv) +INSN_LSX(vexth_du_wu, vv) +INSN_LSX(vexth_qu_du, vv) + +INSN_LSX(vsigncov_b, vvv) +INSN_LSX(vsigncov_h, vvv) +INSN_LSX(vsigncov_w, vvv) +INSN_LSX(vsigncov_d, vvv) + +INSN_LSX(vmskltz_b, vv) +INSN_LSX(vmskltz_h, vv) +INSN_LSX(vmskltz_w, vv) +INSN_LSX(vmskltz_d, vv) +INSN_LSX(vmskgez_b, vv) +INSN_LSX(vmsknz_b, vv) + +INSN_LSX(vldi, v_i) + +INSN_LSX(vand_v, vvv) +INSN_LSX(vor_v, vvv) +INSN_LSX(vxor_v, vvv) +INSN_LSX(vnor_v, vvv) +INSN_LSX(vandn_v, vvv) +INSN_LSX(vorn_v, vvv) + +INSN_LSX(vandi_b, vv_i) +INSN_LSX(vori_b, vv_i) +INSN_LSX(vxori_b, vv_i) +INSN_LSX(vnori_b, vv_i) + +INSN_LSX(vsll_b, vvv) +INSN_LSX(vsll_h, vvv) +INSN_LSX(vsll_w, vvv) +INSN_LSX(vsll_d, vvv) +INSN_LSX(vslli_b, vv_i) +INSN_LSX(vslli_h, vv_i) +INSN_LSX(vslli_w, vv_i) +INSN_LSX(vslli_d, vv_i) + +INSN_LSX(vsrl_b, vvv) +INSN_LSX(vsrl_h, vvv) +INSN_LSX(vsrl_w, vvv) +INSN_LSX(vsrl_d, vvv) +INSN_LSX(vsrli_b, vv_i) +INSN_LSX(vsrli_h, vv_i) +INSN_LSX(vsrli_w, vv_i) +INSN_LSX(vsrli_d, vv_i) + +INSN_LSX(vsra_b, vvv) +INSN_LSX(vsra_h, vvv) +INSN_LSX(vsra_w, vvv) +INSN_LSX(vsra_d, vvv) +INSN_LSX(vsrai_b, vv_i) +INSN_LSX(vsrai_h, vv_i) +INSN_LSX(vsrai_w, vv_i) +INSN_LSX(vsrai_d, vv_i) + +INSN_LSX(vrotr_b, vvv) +INSN_LSX(vrotr_h, vvv) +INSN_LSX(vrotr_w, vvv) +INSN_LSX(vrotr_d, vvv) +INSN_LSX(vrotri_b, vv_i) +INSN_LSX(vrotri_h, vv_i) +INSN_LSX(vrotri_w, vv_i) +INSN_LSX(vrotri_d, vv_i) + +INSN_LSX(vsllwil_h_b, vv_i) +INSN_LSX(vsllwil_w_h, vv_i) +INSN_LSX(vsllwil_d_w, vv_i) +INSN_LSX(vextl_q_d, vv) +INSN_LSX(vsllwil_hu_bu, vv_i) +INSN_LSX(vsllwil_wu_hu, vv_i) +INSN_LSX(vsllwil_du_wu, vv_i) +INSN_LSX(vextl_qu_du, vv) + +INSN_LSX(vsrlr_b, vvv) +INSN_LSX(vsrlr_h, vvv) +INSN_LSX(vsrlr_w, vvv) +INSN_LSX(vsrlr_d, vvv) +INSN_LSX(vsrlri_b, vv_i) +INSN_LSX(vsrlri_h, vv_i) +INSN_LSX(vsrlri_w, vv_i) +INSN_LSX(vsrlri_d, vv_i) + +INSN_LSX(vsrar_b, vvv) +INSN_LSX(vsrar_h, vvv) +INSN_LSX(vsrar_w, vvv) +INSN_LSX(vsrar_d, vvv) +INSN_LSX(vsrari_b, vv_i) +INSN_LSX(vsrari_h, vv_i) +INSN_LSX(vsrari_w, vv_i) +INSN_LSX(vsrari_d, vv_i) + +INSN_LSX(vsrln_b_h, vvv) +INSN_LSX(vsrln_h_w, vvv) +INSN_LSX(vsrln_w_d, vvv) +INSN_LSX(vsran_b_h, vvv) +INSN_LSX(vsran_h_w, vvv) +INSN_LSX(vsran_w_d, vvv) + +INSN_LSX(vsrlni_b_h, vv_i) +INSN_LSX(vsrlni_h_w, vv_i) +INSN_LSX(vsrlni_w_d, vv_i) +INSN_LSX(vsrlni_d_q, vv_i) +INSN_LSX(vsrani_b_h, vv_i) +INSN_LSX(vsrani_h_w, vv_i) +INSN_LSX(vsrani_w_d, vv_i) +INSN_LSX(vsrani_d_q, vv_i) + +INSN_LSX(vsrlrn_b_h, vvv) +INSN_LSX(vsrlrn_h_w, vvv) +INSN_LSX(vsrlrn_w_d, vvv) +INSN_LSX(vsrarn_b_h, vvv) +INSN_LSX(vsrarn_h_w, vvv) +INSN_LSX(vsrarn_w_d, vvv) + +INSN_LSX(vsrlrni_b_h, vv_i) +INSN_LSX(vsrlrni_h_w, vv_i) +INSN_LSX(vsrlrni_w_d, vv_i) +INSN_LSX(vsrlrni_d_q, vv_i) +INSN_LSX(vsrarni_b_h, vv_i) +INSN_LSX(vsrarni_h_w, vv_i) +INSN_LSX(vsrarni_w_d, vv_i) +INSN_LSX(vsrarni_d_q, vv_i) + +INSN_LSX(vssrln_b_h, vvv) +INSN_LSX(vssrln_h_w, vvv) +INSN_LSX(vssrln_w_d, vvv) +INSN_LSX(vssran_b_h, vvv) +INSN_LSX(vssran_h_w, vvv) +INSN_LSX(vssran_w_d, vvv) +INSN_LSX(vssrln_bu_h, vvv) +INSN_LSX(vssrln_hu_w, vvv) +INSN_LSX(vssrln_wu_d, vvv) +INSN_LSX(vssran_bu_h, vvv) +INSN_LSX(vssran_hu_w, vvv) +INSN_LSX(vssran_wu_d, vvv) + +INSN_LSX(vssrlni_b_h, vv_i) +INSN_LSX(vssrlni_h_w, vv_i) +INSN_LSX(vssrlni_w_d, vv_i) +INSN_LSX(vssrlni_d_q, vv_i) +INSN_LSX(vssrani_b_h, vv_i) +INSN_LSX(vssrani_h_w, vv_i) +INSN_LSX(vssrani_w_d, vv_i) +INSN_LSX(vssrani_d_q, vv_i) +INSN_LSX(vssrlni_bu_h, vv_i) +INSN_LSX(vssrlni_hu_w, vv_i) +INSN_LSX(vssrlni_wu_d, vv_i) +INSN_LSX(vssrlni_du_q, vv_i) +INSN_LSX(vssrani_bu_h, vv_i) +INSN_LSX(vssrani_hu_w, vv_i) +INSN_LSX(vssrani_wu_d, vv_i) +INSN_LSX(vssrani_du_q, vv_i) + +INSN_LSX(vssrlrn_b_h, vvv) +INSN_LSX(vssrlrn_h_w, vvv) +INSN_LSX(vssrlrn_w_d, vvv) +INSN_LSX(vssrarn_b_h, vvv) +INSN_LSX(vssrarn_h_w, vvv) +INSN_LSX(vssrarn_w_d, vvv) +INSN_LSX(vssrlrn_bu_h, vvv) +INSN_LSX(vssrlrn_hu_w, vvv) +INSN_LSX(vssrlrn_wu_d, vvv) +INSN_LSX(vssrarn_bu_h, vvv) +INSN_LSX(vssrarn_hu_w, vvv) +INSN_LSX(vssrarn_wu_d, vvv) + +INSN_LSX(vssrlrni_b_h, vv_i) +INSN_LSX(vssrlrni_h_w, vv_i) +INSN_LSX(vssrlrni_w_d, vv_i) +INSN_LSX(vssrlrni_d_q, vv_i) +INSN_LSX(vssrlrni_bu_h, vv_i) +INSN_LSX(vssrlrni_hu_w, vv_i) +INSN_LSX(vssrlrni_wu_d, vv_i) +INSN_LSX(vssrlrni_du_q, vv_i) +INSN_LSX(vssrarni_b_h, vv_i) +INSN_LSX(vssrarni_h_w, vv_i) +INSN_LSX(vssrarni_w_d, vv_i) +INSN_LSX(vssrarni_d_q, vv_i) +INSN_LSX(vssrarni_bu_h, vv_i) +INSN_LSX(vssrarni_hu_w, vv_i) +INSN_LSX(vssrarni_wu_d, vv_i) +INSN_LSX(vssrarni_du_q, vv_i) + +INSN_LSX(vclo_b, vv) +INSN_LSX(vclo_h, vv) +INSN_LSX(vclo_w, vv) +INSN_LSX(vclo_d, vv) +INSN_LSX(vclz_b, vv) +INSN_LSX(vclz_h, vv) +INSN_LSX(vclz_w, vv) +INSN_LSX(vclz_d, vv) + +INSN_LSX(vpcnt_b, vv) +INSN_LSX(vpcnt_h, vv) +INSN_LSX(vpcnt_w, vv) +INSN_LSX(vpcnt_d, vv) + +INSN_LSX(vbitclr_b, vvv) +INSN_LSX(vbitclr_h, vvv) +INSN_LSX(vbitclr_w, vvv) +INSN_LSX(vbitclr_d, vvv) +INSN_LSX(vbitclri_b, vv_i) +INSN_LSX(vbitclri_h, vv_i) +INSN_LSX(vbitclri_w, vv_i) +INSN_LSX(vbitclri_d, vv_i) +INSN_LSX(vbitset_b, vvv) +INSN_LSX(vbitset_h, vvv) +INSN_LSX(vbitset_w, vvv) +INSN_LSX(vbitset_d, vvv) +INSN_LSX(vbitseti_b, vv_i) +INSN_LSX(vbitseti_h, vv_i) +INSN_LSX(vbitseti_w, vv_i) +INSN_LSX(vbitseti_d, vv_i) +INSN_LSX(vbitrev_b, vvv) +INSN_LSX(vbitrev_h, vvv) +INSN_LSX(vbitrev_w, vvv) +INSN_LSX(vbitrev_d, vvv) +INSN_LSX(vbitrevi_b, vv_i) +INSN_LSX(vbitrevi_h, vv_i) +INSN_LSX(vbitrevi_w, vv_i) +INSN_LSX(vbitrevi_d, vv_i) + +INSN_LSX(vfrstp_b, vvv) +INSN_LSX(vfrstp_h, vvv) +INSN_LSX(vfrstpi_b, vv_i) +INSN_LSX(vfrstpi_h, vv_i) + +INSN_LSX(vfadd_s, vvv) +INSN_LSX(vfadd_d, vvv) +INSN_LSX(vfsub_s, vvv) +INSN_LSX(vfsub_d, vvv) +INSN_LSX(vfmul_s, vvv) +INSN_LSX(vfmul_d, vvv) +INSN_LSX(vfdiv_s, vvv) +INSN_LSX(vfdiv_d, vvv) + +INSN_LSX(vfmadd_s, vvvv) +INSN_LSX(vfmadd_d, vvvv) +INSN_LSX(vfmsub_s, vvvv) +INSN_LSX(vfmsub_d, vvvv) +INSN_LSX(vfnmadd_s, vvvv) +INSN_LSX(vfnmadd_d, vvvv) +INSN_LSX(vfnmsub_s, vvvv) +INSN_LSX(vfnmsub_d, vvvv) + +INSN_LSX(vfmax_s, vvv) +INSN_LSX(vfmax_d, vvv) +INSN_LSX(vfmin_s, vvv) +INSN_LSX(vfmin_d, vvv) + +INSN_LSX(vfmaxa_s, vvv) +INSN_LSX(vfmaxa_d, vvv) +INSN_LSX(vfmina_s, vvv) +INSN_LSX(vfmina_d, vvv) + +INSN_LSX(vflogb_s, vv) +INSN_LSX(vflogb_d, vv) + +INSN_LSX(vfclass_s, vv) +INSN_LSX(vfclass_d, vv) + +INSN_LSX(vfsqrt_s, vv) +INSN_LSX(vfsqrt_d, vv) +INSN_LSX(vfrecip_s, vv) +INSN_LSX(vfrecip_d, vv) +INSN_LSX(vfrsqrt_s, vv) +INSN_LSX(vfrsqrt_d, vv) + +INSN_LSX(vfcvtl_s_h, vv) +INSN_LSX(vfcvth_s_h, vv) +INSN_LSX(vfcvtl_d_s, vv) +INSN_LSX(vfcvth_d_s, vv) +INSN_LSX(vfcvt_h_s, vvv) +INSN_LSX(vfcvt_s_d, vvv) + +INSN_LSX(vfrint_s, vv) +INSN_LSX(vfrint_d, vv) +INSN_LSX(vfrintrm_s, vv) +INSN_LSX(vfrintrm_d, vv) +INSN_LSX(vfrintrp_s, vv) +INSN_LSX(vfrintrp_d, vv) +INSN_LSX(vfrintrz_s, vv) +INSN_LSX(vfrintrz_d, vv) +INSN_LSX(vfrintrne_s, vv) +INSN_LSX(vfrintrne_d, vv) + +INSN_LSX(vftint_w_s, vv) +INSN_LSX(vftint_l_d, vv) +INSN_LSX(vftintrm_w_s, vv) +INSN_LSX(vftintrm_l_d, vv) +INSN_LSX(vftintrp_w_s, vv) +INSN_LSX(vftintrp_l_d, vv) +INSN_LSX(vftintrz_w_s, vv) +INSN_LSX(vftintrz_l_d, vv) +INSN_LSX(vftintrne_w_s, vv) +INSN_LSX(vftintrne_l_d, vv) +INSN_LSX(vftint_wu_s, vv) +INSN_LSX(vftint_lu_d, vv) +INSN_LSX(vftintrz_wu_s, vv) +INSN_LSX(vftintrz_lu_d, vv) +INSN_LSX(vftint_w_d, vvv) +INSN_LSX(vftintrm_w_d, vvv) +INSN_LSX(vftintrp_w_d, vvv) +INSN_LSX(vftintrz_w_d, vvv) +INSN_LSX(vftintrne_w_d, vvv) +INSN_LSX(vftintl_l_s, vv) +INSN_LSX(vftinth_l_s, vv) +INSN_LSX(vftintrml_l_s, vv) +INSN_LSX(vftintrmh_l_s, vv) +INSN_LSX(vftintrpl_l_s, vv) +INSN_LSX(vftintrph_l_s, vv) +INSN_LSX(vftintrzl_l_s, vv) +INSN_LSX(vftintrzh_l_s, vv) +INSN_LSX(vftintrnel_l_s, vv) +INSN_LSX(vftintrneh_l_s, vv) + +INSN_LSX(vffint_s_w, vv) +INSN_LSX(vffint_s_wu, vv) +INSN_LSX(vffint_d_l, vv) +INSN_LSX(vffint_d_lu, vv) +INSN_LSX(vffintl_d_w, vv) +INSN_LSX(vffinth_d_w, vv) +INSN_LSX(vffint_s_l, vvv) + +INSN_LSX(vseq_b, vvv) +INSN_LSX(vseq_h, vvv) +INSN_LSX(vseq_w, vvv) +INSN_LSX(vseq_d, vvv) +INSN_LSX(vseqi_b, vv_i) +INSN_LSX(vseqi_h, vv_i) +INSN_LSX(vseqi_w, vv_i) +INSN_LSX(vseqi_d, vv_i) + +INSN_LSX(vsle_b, vvv) +INSN_LSX(vsle_h, vvv) +INSN_LSX(vsle_w, vvv) +INSN_LSX(vsle_d, vvv) +INSN_LSX(vslei_b, vv_i) +INSN_LSX(vslei_h, vv_i) +INSN_LSX(vslei_w, vv_i) +INSN_LSX(vslei_d, vv_i) +INSN_LSX(vsle_bu, vvv) +INSN_LSX(vsle_hu, vvv) +INSN_LSX(vsle_wu, vvv) +INSN_LSX(vsle_du, vvv) +INSN_LSX(vslei_bu, vv_i) +INSN_LSX(vslei_hu, vv_i) +INSN_LSX(vslei_wu, vv_i) +INSN_LSX(vslei_du, vv_i) + +INSN_LSX(vslt_b, vvv) +INSN_LSX(vslt_h, vvv) +INSN_LSX(vslt_w, vvv) +INSN_LSX(vslt_d, vvv) +INSN_LSX(vslti_b, vv_i) +INSN_LSX(vslti_h, vv_i) +INSN_LSX(vslti_w, vv_i) +INSN_LSX(vslti_d, vv_i) +INSN_LSX(vslt_bu, vvv) +INSN_LSX(vslt_hu, vvv) +INSN_LSX(vslt_wu, vvv) +INSN_LSX(vslt_du, vvv) +INSN_LSX(vslti_bu, vv_i) +INSN_LSX(vslti_hu, vv_i) +INSN_LSX(vslti_wu, vv_i) +INSN_LSX(vslti_du, vv_i) + +#define output_vfcmp(C, PREFIX, SUFFIX) \ +{ \ + (C)->info->fprintf_func((C)->info->stream, "%08x %s%s\t%d, f%d, f%d", \ + (C)->insn, PREFIX, SUFFIX, a->vd, \ + a->vj, a->vk); \ +} + +static bool output_vvv_fcond(DisasContext *ctx, arg_vvv_fcond * a, + const char *suffix) +{ + bool ret = true; + switch (a->fcond) { + case 0x0: + output_vfcmp(ctx, "vfcmp_caf_", suffix); + break; + case 0x1: + output_vfcmp(ctx, "vfcmp_saf_", suffix); + break; + case 0x2: + output_vfcmp(ctx, "vfcmp_clt_", suffix); + break; + case 0x3: + output_vfcmp(ctx, "vfcmp_slt_", suffix); + break; + case 0x4: + output_vfcmp(ctx, "vfcmp_ceq_", suffix); + break; + case 0x5: + output_vfcmp(ctx, "vfcmp_seq_", suffix); + break; + case 0x6: + output_vfcmp(ctx, "vfcmp_cle_", suffix); + break; + case 0x7: + output_vfcmp(ctx, "vfcmp_sle_", suffix); + break; + case 0x8: + output_vfcmp(ctx, "vfcmp_cun_", suffix); + break; + case 0x9: + output_vfcmp(ctx, "vfcmp_sun_", suffix); + break; + case 0xA: + output_vfcmp(ctx, "vfcmp_cult_", suffix); + break; + case 0xB: + output_vfcmp(ctx, "vfcmp_sult_", suffix); + break; + case 0xC: + output_vfcmp(ctx, "vfcmp_cueq_", suffix); + break; + case 0xD: + output_vfcmp(ctx, "vfcmp_sueq_", suffix); + break; + case 0xE: + output_vfcmp(ctx, "vfcmp_cule_", suffix); + break; + case 0xF: + output_vfcmp(ctx, "vfcmp_sule_", suffix); + break; + case 0x10: + output_vfcmp(ctx, "vfcmp_cne_", suffix); + break; + case 0x11: + output_vfcmp(ctx, "vfcmp_sne_", suffix); + break; + case 0x14: + output_vfcmp(ctx, "vfcmp_cor_", suffix); + break; + case 0x15: + output_vfcmp(ctx, "vfcmp_sor_", suffix); + break; + case 0x18: + output_vfcmp(ctx, "vfcmp_cune_", suffix); + break; + case 0x19: + output_vfcmp(ctx, "vfcmp_sune_", suffix); + break; + default: + ret = false; + } + return ret; +} + +#define LSX_FCMP_INSN(suffix) \ +static bool trans_vfcmp_cond_##suffix(DisasContext *ctx, \ + arg_vvv_fcond * a) \ +{ \ + return output_vvv_fcond(ctx, a, #suffix); \ +} + +LSX_FCMP_INSN(s) +LSX_FCMP_INSN(d) + +INSN_LSX(vbitsel_v, vvvv) +INSN_LSX(vbitseli_b, vv_i) + +INSN_LSX(vseteqz_v, cv) +INSN_LSX(vsetnez_v, cv) +INSN_LSX(vsetanyeqz_b, cv) +INSN_LSX(vsetanyeqz_h, cv) +INSN_LSX(vsetanyeqz_w, cv) +INSN_LSX(vsetanyeqz_d, cv) +INSN_LSX(vsetallnez_b, cv) +INSN_LSX(vsetallnez_h, cv) +INSN_LSX(vsetallnez_w, cv) +INSN_LSX(vsetallnez_d, cv) + +INSN_LSX(vinsgr2vr_b, vr_i) +INSN_LSX(vinsgr2vr_h, vr_i) +INSN_LSX(vinsgr2vr_w, vr_i) +INSN_LSX(vinsgr2vr_d, vr_i) +INSN_LSX(vpickve2gr_b, rv_i) +INSN_LSX(vpickve2gr_h, rv_i) +INSN_LSX(vpickve2gr_w, rv_i) +INSN_LSX(vpickve2gr_d, rv_i) +INSN_LSX(vpickve2gr_bu, rv_i) +INSN_LSX(vpickve2gr_hu, rv_i) +INSN_LSX(vpickve2gr_wu, rv_i) +INSN_LSX(vpickve2gr_du, rv_i) + +INSN_LSX(vreplgr2vr_b, vr) +INSN_LSX(vreplgr2vr_h, vr) +INSN_LSX(vreplgr2vr_w, vr) +INSN_LSX(vreplgr2vr_d, vr) + +INSN_LSX(vreplve_b, vvr) +INSN_LSX(vreplve_h, vvr) +INSN_LSX(vreplve_w, vvr) +INSN_LSX(vreplve_d, vvr) +INSN_LSX(vreplvei_b, vv_i) +INSN_LSX(vreplvei_h, vv_i) +INSN_LSX(vreplvei_w, vv_i) +INSN_LSX(vreplvei_d, vv_i) + +INSN_LSX(vbsll_v, vv_i) +INSN_LSX(vbsrl_v, vv_i) + +INSN_LSX(vpackev_b, vvv) +INSN_LSX(vpackev_h, vvv) +INSN_LSX(vpackev_w, vvv) +INSN_LSX(vpackev_d, vvv) +INSN_LSX(vpackod_b, vvv) +INSN_LSX(vpackod_h, vvv) +INSN_LSX(vpackod_w, vvv) +INSN_LSX(vpackod_d, vvv) + +INSN_LSX(vpickev_b, vvv) +INSN_LSX(vpickev_h, vvv) +INSN_LSX(vpickev_w, vvv) +INSN_LSX(vpickev_d, vvv) +INSN_LSX(vpickod_b, vvv) +INSN_LSX(vpickod_h, vvv) +INSN_LSX(vpickod_w, vvv) +INSN_LSX(vpickod_d, vvv) + +INSN_LSX(vilvl_b, vvv) +INSN_LSX(vilvl_h, vvv) +INSN_LSX(vilvl_w, vvv) +INSN_LSX(vilvl_d, vvv) +INSN_LSX(vilvh_b, vvv) +INSN_LSX(vilvh_h, vvv) +INSN_LSX(vilvh_w, vvv) +INSN_LSX(vilvh_d, vvv) + +INSN_LSX(vshuf_b, vvvv) +INSN_LSX(vshuf_h, vvv) +INSN_LSX(vshuf_w, vvv) +INSN_LSX(vshuf_d, vvv) +INSN_LSX(vshuf4i_b, vv_i) +INSN_LSX(vshuf4i_h, vv_i) +INSN_LSX(vshuf4i_w, vv_i) +INSN_LSX(vshuf4i_d, vv_i) + +INSN_LSX(vpermi_w, vv_i) + +INSN_LSX(vextrins_d, vv_i) +INSN_LSX(vextrins_w, vv_i) +INSN_LSX(vextrins_h, vv_i) +INSN_LSX(vextrins_b, vv_i) + +INSN_LSX(vld, vr_i) +INSN_LSX(vst, vr_i) +INSN_LSX(vldx, vrr) +INSN_LSX(vstx, vrr) + +INSN_LSX(vldrepl_d, vr_i) +INSN_LSX(vldrepl_w, vr_i) +INSN_LSX(vldrepl_h, vr_i) +INSN_LSX(vldrepl_b, vr_i) +INSN_LSX(vstelm_d, vr_ii) +INSN_LSX(vstelm_w, vr_ii) +INSN_LSX(vstelm_h, vr_ii) +INSN_LSX(vstelm_b, vr_ii) + +#define INSN_LASX(insn, type) \ +static bool trans_##insn(DisasContext *ctx, arg_##type * a) \ +{ \ + output_##type ## _x(ctx, a, #insn); \ + return true; \ +} + +static void output_cv_x(DisasContext *ctx, arg_cv *a, const char *mnemonic) +{ + output(ctx, mnemonic, "fcc%d, x%d", a->cd, a->vj); +} + +static void output_v_i_x(DisasContext *ctx, arg_v_i *a, const char *mnemonic) +{ + output(ctx, mnemonic, "x%d, 0x%x", a->vd, a->imm); +} + +static void output_vvvv_x(DisasContext *ctx, arg_vvvv *a, const char *mnemonic) +{ + output(ctx, mnemonic, "x%d, x%d, x%d, x%d", a->vd, a->vj, a->vk, a->va); +} + +static void output_vvv_x(DisasContext *ctx, arg_vvv * a, const char *mnemonic) +{ + output(ctx, mnemonic, "x%d, x%d, x%d", a->vd, a->vj, a->vk); +} + +static void output_vr_x(DisasContext *ctx, arg_vr *a, const char *mnemonic) +{ + output(ctx, mnemonic, "x%d, r%d", a->vd, a->rj); +} + +static void output_vv_i_x(DisasContext *ctx, arg_vv_i *a, const char *mnemonic) +{ + output(ctx, mnemonic, "x%d, x%d, 0x%x", a->vd, a->vj, a->imm); +} + +static void output_vv_x(DisasContext *ctx, arg_vv *a, const char *mnemonic) +{ + output(ctx, mnemonic, "x%d, x%d", a->vd, a->vj); +} + +static void output_vr_i_x(DisasContext *ctx, arg_vr_i *a, const char *mnemonic) +{ + output(ctx, mnemonic, "x%d, r%d, 0x%x", a->vd, a->rj, a->imm); +} + +static void output_rv_i_x(DisasContext *ctx, arg_rv_i *a, const char *mnemonic) +{ + output(ctx, mnemonic, "r%d, x%d, 0x%x", a->rd, a->vj, a->imm); +} + +static void output_vvr_x(DisasContext *ctx, arg_vvr *a, const char *mnemonic) +{ + output(ctx, mnemonic, "x%d, x%d, r%d", a->vd, a->vj, a->rk); +} + +static void output_vrr_x(DisasContext *ctx, arg_vrr *a, const char *mnemonic) +{ + output(ctx, mnemonic, "x%d, r%d, r%d", a->vd, a->rj, a->rk); +} + +static void output_vr_ii_x(DisasContext *ctx, arg_vr_ii *a, const char *mnemonic) +{ + output(ctx, mnemonic, "x%d, r%d, 0x%x, 0x%x", a->vd, a->rj, a->imm, a->imm2); +} + +INSN_LASX(xvadd_b, vvv) +INSN_LASX(xvadd_h, vvv) +INSN_LASX(xvadd_w, vvv) +INSN_LASX(xvadd_d, vvv) +INSN_LASX(xvadd_q, vvv) +INSN_LASX(xvsub_b, vvv) +INSN_LASX(xvsub_h, vvv) +INSN_LASX(xvsub_w, vvv) +INSN_LASX(xvsub_d, vvv) +INSN_LASX(xvsub_q, vvv) + +INSN_LASX(xvaddi_bu, vv_i) +INSN_LASX(xvaddi_hu, vv_i) +INSN_LASX(xvaddi_wu, vv_i) +INSN_LASX(xvaddi_du, vv_i) +INSN_LASX(xvsubi_bu, vv_i) +INSN_LASX(xvsubi_hu, vv_i) +INSN_LASX(xvsubi_wu, vv_i) +INSN_LASX(xvsubi_du, vv_i) + +INSN_LASX(xvneg_b, vv) +INSN_LASX(xvneg_h, vv) +INSN_LASX(xvneg_w, vv) +INSN_LASX(xvneg_d, vv) + +INSN_LASX(xvsadd_b, vvv) +INSN_LASX(xvsadd_h, vvv) +INSN_LASX(xvsadd_w, vvv) +INSN_LASX(xvsadd_d, vvv) +INSN_LASX(xvsadd_bu, vvv) +INSN_LASX(xvsadd_hu, vvv) +INSN_LASX(xvsadd_wu, vvv) +INSN_LASX(xvsadd_du, vvv) +INSN_LASX(xvssub_b, vvv) +INSN_LASX(xvssub_h, vvv) +INSN_LASX(xvssub_w, vvv) +INSN_LASX(xvssub_d, vvv) +INSN_LASX(xvssub_bu, vvv) +INSN_LASX(xvssub_hu, vvv) +INSN_LASX(xvssub_wu, vvv) +INSN_LASX(xvssub_du, vvv) + +INSN_LASX(xvhaddw_h_b, vvv) +INSN_LASX(xvhaddw_w_h, vvv) +INSN_LASX(xvhaddw_d_w, vvv) +INSN_LASX(xvhaddw_q_d, vvv) +INSN_LASX(xvhaddw_hu_bu, vvv) +INSN_LASX(xvhaddw_wu_hu, vvv) +INSN_LASX(xvhaddw_du_wu, vvv) +INSN_LASX(xvhaddw_qu_du, vvv) +INSN_LASX(xvhsubw_h_b, vvv) +INSN_LASX(xvhsubw_w_h, vvv) +INSN_LASX(xvhsubw_d_w, vvv) +INSN_LASX(xvhsubw_q_d, vvv) +INSN_LASX(xvhsubw_hu_bu, vvv) +INSN_LASX(xvhsubw_wu_hu, vvv) +INSN_LASX(xvhsubw_du_wu, vvv) +INSN_LASX(xvhsubw_qu_du, vvv) + +INSN_LASX(xvaddwev_h_b, vvv) +INSN_LASX(xvaddwev_w_h, vvv) +INSN_LASX(xvaddwev_d_w, vvv) +INSN_LASX(xvaddwev_q_d, vvv) +INSN_LASX(xvaddwod_h_b, vvv) +INSN_LASX(xvaddwod_w_h, vvv) +INSN_LASX(xvaddwod_d_w, vvv) +INSN_LASX(xvaddwod_q_d, vvv) +INSN_LASX(xvsubwev_h_b, vvv) +INSN_LASX(xvsubwev_w_h, vvv) +INSN_LASX(xvsubwev_d_w, vvv) +INSN_LASX(xvsubwev_q_d, vvv) +INSN_LASX(xvsubwod_h_b, vvv) +INSN_LASX(xvsubwod_w_h, vvv) +INSN_LASX(xvsubwod_d_w, vvv) +INSN_LASX(xvsubwod_q_d, vvv) + +INSN_LASX(xvaddwev_h_bu, vvv) +INSN_LASX(xvaddwev_w_hu, vvv) +INSN_LASX(xvaddwev_d_wu, vvv) +INSN_LASX(xvaddwev_q_du, vvv) +INSN_LASX(xvaddwod_h_bu, vvv) +INSN_LASX(xvaddwod_w_hu, vvv) +INSN_LASX(xvaddwod_d_wu, vvv) +INSN_LASX(xvaddwod_q_du, vvv) +INSN_LASX(xvsubwev_h_bu, vvv) +INSN_LASX(xvsubwev_w_hu, vvv) +INSN_LASX(xvsubwev_d_wu, vvv) +INSN_LASX(xvsubwev_q_du, vvv) +INSN_LASX(xvsubwod_h_bu, vvv) +INSN_LASX(xvsubwod_w_hu, vvv) +INSN_LASX(xvsubwod_d_wu, vvv) +INSN_LASX(xvsubwod_q_du, vvv) + +INSN_LASX(xvaddwev_h_bu_b, vvv) +INSN_LASX(xvaddwev_w_hu_h, vvv) +INSN_LASX(xvaddwev_d_wu_w, vvv) +INSN_LASX(xvaddwev_q_du_d, vvv) +INSN_LASX(xvaddwod_h_bu_b, vvv) +INSN_LASX(xvaddwod_w_hu_h, vvv) +INSN_LASX(xvaddwod_d_wu_w, vvv) +INSN_LASX(xvaddwod_q_du_d, vvv) + +INSN_LASX(xvavg_b, vvv) +INSN_LASX(xvavg_h, vvv) +INSN_LASX(xvavg_w, vvv) +INSN_LASX(xvavg_d, vvv) +INSN_LASX(xvavg_bu, vvv) +INSN_LASX(xvavg_hu, vvv) +INSN_LASX(xvavg_wu, vvv) +INSN_LASX(xvavg_du, vvv) +INSN_LASX(xvavgr_b, vvv) +INSN_LASX(xvavgr_h, vvv) +INSN_LASX(xvavgr_w, vvv) +INSN_LASX(xvavgr_d, vvv) +INSN_LASX(xvavgr_bu, vvv) +INSN_LASX(xvavgr_hu, vvv) +INSN_LASX(xvavgr_wu, vvv) +INSN_LASX(xvavgr_du, vvv) + +INSN_LASX(xvabsd_b, vvv) +INSN_LASX(xvabsd_h, vvv) +INSN_LASX(xvabsd_w, vvv) +INSN_LASX(xvabsd_d, vvv) +INSN_LASX(xvabsd_bu, vvv) +INSN_LASX(xvabsd_hu, vvv) +INSN_LASX(xvabsd_wu, vvv) +INSN_LASX(xvabsd_du, vvv) + +INSN_LASX(xvadda_b, vvv) +INSN_LASX(xvadda_h, vvv) +INSN_LASX(xvadda_w, vvv) +INSN_LASX(xvadda_d, vvv) + +INSN_LASX(xvmax_b, vvv) +INSN_LASX(xvmax_h, vvv) +INSN_LASX(xvmax_w, vvv) +INSN_LASX(xvmax_d, vvv) +INSN_LASX(xvmin_b, vvv) +INSN_LASX(xvmin_h, vvv) +INSN_LASX(xvmin_w, vvv) +INSN_LASX(xvmin_d, vvv) +INSN_LASX(xvmax_bu, vvv) +INSN_LASX(xvmax_hu, vvv) +INSN_LASX(xvmax_wu, vvv) +INSN_LASX(xvmax_du, vvv) +INSN_LASX(xvmin_bu, vvv) +INSN_LASX(xvmin_hu, vvv) +INSN_LASX(xvmin_wu, vvv) +INSN_LASX(xvmin_du, vvv) + +INSN_LASX(xvmaxi_b, vv_i) +INSN_LASX(xvmaxi_h, vv_i) +INSN_LASX(xvmaxi_w, vv_i) +INSN_LASX(xvmaxi_d, vv_i) +INSN_LASX(xvmini_b, vv_i) +INSN_LASX(xvmini_h, vv_i) +INSN_LASX(xvmini_w, vv_i) +INSN_LASX(xvmini_d, vv_i) +INSN_LASX(xvmaxi_bu, vv_i) +INSN_LASX(xvmaxi_hu, vv_i) +INSN_LASX(xvmaxi_wu, vv_i) +INSN_LASX(xvmaxi_du, vv_i) +INSN_LASX(xvmini_bu, vv_i) +INSN_LASX(xvmini_hu, vv_i) +INSN_LASX(xvmini_wu, vv_i) +INSN_LASX(xvmini_du, vv_i) + +INSN_LASX(xvmul_b, vvv) +INSN_LASX(xvmul_h, vvv) +INSN_LASX(xvmul_w, vvv) +INSN_LASX(xvmul_d, vvv) +INSN_LASX(xvmuh_b, vvv) +INSN_LASX(xvmuh_h, vvv) +INSN_LASX(xvmuh_w, vvv) +INSN_LASX(xvmuh_d, vvv) +INSN_LASX(xvmuh_bu, vvv) +INSN_LASX(xvmuh_hu, vvv) +INSN_LASX(xvmuh_wu, vvv) +INSN_LASX(xvmuh_du, vvv) + +INSN_LASX(xvmulwev_h_b, vvv) +INSN_LASX(xvmulwev_w_h, vvv) +INSN_LASX(xvmulwev_d_w, vvv) +INSN_LASX(xvmulwev_q_d, vvv) +INSN_LASX(xvmulwod_h_b, vvv) +INSN_LASX(xvmulwod_w_h, vvv) +INSN_LASX(xvmulwod_d_w, vvv) +INSN_LASX(xvmulwod_q_d, vvv) +INSN_LASX(xvmulwev_h_bu, vvv) +INSN_LASX(xvmulwev_w_hu, vvv) +INSN_LASX(xvmulwev_d_wu, vvv) +INSN_LASX(xvmulwev_q_du, vvv) +INSN_LASX(xvmulwod_h_bu, vvv) +INSN_LASX(xvmulwod_w_hu, vvv) +INSN_LASX(xvmulwod_d_wu, vvv) +INSN_LASX(xvmulwod_q_du, vvv) +INSN_LASX(xvmulwev_h_bu_b, vvv) +INSN_LASX(xvmulwev_w_hu_h, vvv) +INSN_LASX(xvmulwev_d_wu_w, vvv) +INSN_LASX(xvmulwev_q_du_d, vvv) +INSN_LASX(xvmulwod_h_bu_b, vvv) +INSN_LASX(xvmulwod_w_hu_h, vvv) +INSN_LASX(xvmulwod_d_wu_w, vvv) +INSN_LASX(xvmulwod_q_du_d, vvv) + +INSN_LASX(xvmadd_b, vvv) +INSN_LASX(xvmadd_h, vvv) +INSN_LASX(xvmadd_w, vvv) +INSN_LASX(xvmadd_d, vvv) +INSN_LASX(xvmsub_b, vvv) +INSN_LASX(xvmsub_h, vvv) +INSN_LASX(xvmsub_w, vvv) +INSN_LASX(xvmsub_d, vvv) + +INSN_LASX(xvmaddwev_h_b, vvv) +INSN_LASX(xvmaddwev_w_h, vvv) +INSN_LASX(xvmaddwev_d_w, vvv) +INSN_LASX(xvmaddwev_q_d, vvv) +INSN_LASX(xvmaddwod_h_b, vvv) +INSN_LASX(xvmaddwod_w_h, vvv) +INSN_LASX(xvmaddwod_d_w, vvv) +INSN_LASX(xvmaddwod_q_d, vvv) +INSN_LASX(xvmaddwev_h_bu, vvv) +INSN_LASX(xvmaddwev_w_hu, vvv) +INSN_LASX(xvmaddwev_d_wu, vvv) +INSN_LASX(xvmaddwev_q_du, vvv) +INSN_LASX(xvmaddwod_h_bu, vvv) +INSN_LASX(xvmaddwod_w_hu, vvv) +INSN_LASX(xvmaddwod_d_wu, vvv) +INSN_LASX(xvmaddwod_q_du, vvv) +INSN_LASX(xvmaddwev_h_bu_b, vvv) +INSN_LASX(xvmaddwev_w_hu_h, vvv) +INSN_LASX(xvmaddwev_d_wu_w, vvv) +INSN_LASX(xvmaddwev_q_du_d, vvv) +INSN_LASX(xvmaddwod_h_bu_b, vvv) +INSN_LASX(xvmaddwod_w_hu_h, vvv) +INSN_LASX(xvmaddwod_d_wu_w, vvv) +INSN_LASX(xvmaddwod_q_du_d, vvv) + +INSN_LASX(xvdiv_b, vvv) +INSN_LASX(xvdiv_h, vvv) +INSN_LASX(xvdiv_w, vvv) +INSN_LASX(xvdiv_d, vvv) +INSN_LASX(xvdiv_bu, vvv) +INSN_LASX(xvdiv_hu, vvv) +INSN_LASX(xvdiv_wu, vvv) +INSN_LASX(xvdiv_du, vvv) +INSN_LASX(xvmod_b, vvv) +INSN_LASX(xvmod_h, vvv) +INSN_LASX(xvmod_w, vvv) +INSN_LASX(xvmod_d, vvv) +INSN_LASX(xvmod_bu, vvv) +INSN_LASX(xvmod_hu, vvv) +INSN_LASX(xvmod_wu, vvv) +INSN_LASX(xvmod_du, vvv) + +INSN_LASX(xvsat_b, vv_i) +INSN_LASX(xvsat_h, vv_i) +INSN_LASX(xvsat_w, vv_i) +INSN_LASX(xvsat_d, vv_i) +INSN_LASX(xvsat_bu, vv_i) +INSN_LASX(xvsat_hu, vv_i) +INSN_LASX(xvsat_wu, vv_i) +INSN_LASX(xvsat_du, vv_i) + +INSN_LASX(xvexth_h_b, vv) +INSN_LASX(xvexth_w_h, vv) +INSN_LASX(xvexth_d_w, vv) +INSN_LASX(xvexth_q_d, vv) +INSN_LASX(xvexth_hu_bu, vv) +INSN_LASX(xvexth_wu_hu, vv) +INSN_LASX(xvexth_du_wu, vv) +INSN_LASX(xvexth_qu_du, vv) + +INSN_LASX(vext2xv_h_b, vv) +INSN_LASX(vext2xv_w_b, vv) +INSN_LASX(vext2xv_d_b, vv) +INSN_LASX(vext2xv_w_h, vv) +INSN_LASX(vext2xv_d_h, vv) +INSN_LASX(vext2xv_d_w, vv) +INSN_LASX(vext2xv_hu_bu, vv) +INSN_LASX(vext2xv_wu_bu, vv) +INSN_LASX(vext2xv_du_bu, vv) +INSN_LASX(vext2xv_wu_hu, vv) +INSN_LASX(vext2xv_du_hu, vv) +INSN_LASX(vext2xv_du_wu, vv) + +INSN_LASX(xvsigncov_b, vvv) +INSN_LASX(xvsigncov_h, vvv) +INSN_LASX(xvsigncov_w, vvv) +INSN_LASX(xvsigncov_d, vvv) + +INSN_LASX(xvmskltz_b, vv) +INSN_LASX(xvmskltz_h, vv) +INSN_LASX(xvmskltz_w, vv) +INSN_LASX(xvmskltz_d, vv) +INSN_LASX(xvmskgez_b, vv) +INSN_LASX(xvmsknz_b, vv) + +INSN_LASX(xvldi, v_i) + +INSN_LASX(xvand_v, vvv) +INSN_LASX(xvor_v, vvv) +INSN_LASX(xvxor_v, vvv) +INSN_LASX(xvnor_v, vvv) +INSN_LASX(xvandn_v, vvv) +INSN_LASX(xvorn_v, vvv) + +INSN_LASX(xvandi_b, vv_i) +INSN_LASX(xvori_b, vv_i) +INSN_LASX(xvxori_b, vv_i) +INSN_LASX(xvnori_b, vv_i) + +INSN_LASX(xvsll_b, vvv) +INSN_LASX(xvsll_h, vvv) +INSN_LASX(xvsll_w, vvv) +INSN_LASX(xvsll_d, vvv) +INSN_LASX(xvslli_b, vv_i) +INSN_LASX(xvslli_h, vv_i) +INSN_LASX(xvslli_w, vv_i) +INSN_LASX(xvslli_d, vv_i) + +INSN_LASX(xvsrl_b, vvv) +INSN_LASX(xvsrl_h, vvv) +INSN_LASX(xvsrl_w, vvv) +INSN_LASX(xvsrl_d, vvv) +INSN_LASX(xvsrli_b, vv_i) +INSN_LASX(xvsrli_h, vv_i) +INSN_LASX(xvsrli_w, vv_i) +INSN_LASX(xvsrli_d, vv_i) + +INSN_LASX(xvsra_b, vvv) +INSN_LASX(xvsra_h, vvv) +INSN_LASX(xvsra_w, vvv) +INSN_LASX(xvsra_d, vvv) +INSN_LASX(xvsrai_b, vv_i) +INSN_LASX(xvsrai_h, vv_i) +INSN_LASX(xvsrai_w, vv_i) +INSN_LASX(xvsrai_d, vv_i) + +INSN_LASX(xvrotr_b, vvv) +INSN_LASX(xvrotr_h, vvv) +INSN_LASX(xvrotr_w, vvv) +INSN_LASX(xvrotr_d, vvv) +INSN_LASX(xvrotri_b, vv_i) +INSN_LASX(xvrotri_h, vv_i) +INSN_LASX(xvrotri_w, vv_i) +INSN_LASX(xvrotri_d, vv_i) + +INSN_LASX(xvsllwil_h_b, vv_i) +INSN_LASX(xvsllwil_w_h, vv_i) +INSN_LASX(xvsllwil_d_w, vv_i) +INSN_LASX(xvextl_q_d, vv) +INSN_LASX(xvsllwil_hu_bu, vv_i) +INSN_LASX(xvsllwil_wu_hu, vv_i) +INSN_LASX(xvsllwil_du_wu, vv_i) +INSN_LASX(xvextl_qu_du, vv) + +INSN_LASX(xvsrlr_b, vvv) +INSN_LASX(xvsrlr_h, vvv) +INSN_LASX(xvsrlr_w, vvv) +INSN_LASX(xvsrlr_d, vvv) +INSN_LASX(xvsrlri_b, vv_i) +INSN_LASX(xvsrlri_h, vv_i) +INSN_LASX(xvsrlri_w, vv_i) +INSN_LASX(xvsrlri_d, vv_i) + +INSN_LASX(xvsrar_b, vvv) +INSN_LASX(xvsrar_h, vvv) +INSN_LASX(xvsrar_w, vvv) +INSN_LASX(xvsrar_d, vvv) +INSN_LASX(xvsrari_b, vv_i) +INSN_LASX(xvsrari_h, vv_i) +INSN_LASX(xvsrari_w, vv_i) +INSN_LASX(xvsrari_d, vv_i) + +INSN_LASX(xvsrln_b_h, vvv) +INSN_LASX(xvsrln_h_w, vvv) +INSN_LASX(xvsrln_w_d, vvv) +INSN_LASX(xvsran_b_h, vvv) +INSN_LASX(xvsran_h_w, vvv) +INSN_LASX(xvsran_w_d, vvv) + +INSN_LASX(xvsrlni_b_h, vv_i) +INSN_LASX(xvsrlni_h_w, vv_i) +INSN_LASX(xvsrlni_w_d, vv_i) +INSN_LASX(xvsrlni_d_q, vv_i) +INSN_LASX(xvsrani_b_h, vv_i) +INSN_LASX(xvsrani_h_w, vv_i) +INSN_LASX(xvsrani_w_d, vv_i) +INSN_LASX(xvsrani_d_q, vv_i) + +INSN_LASX(xvsrlrn_b_h, vvv) +INSN_LASX(xvsrlrn_h_w, vvv) +INSN_LASX(xvsrlrn_w_d, vvv) +INSN_LASX(xvsrarn_b_h, vvv) +INSN_LASX(xvsrarn_h_w, vvv) +INSN_LASX(xvsrarn_w_d, vvv) + +INSN_LASX(xvsrlrni_b_h, vv_i) +INSN_LASX(xvsrlrni_h_w, vv_i) +INSN_LASX(xvsrlrni_w_d, vv_i) +INSN_LASX(xvsrlrni_d_q, vv_i) +INSN_LASX(xvsrarni_b_h, vv_i) +INSN_LASX(xvsrarni_h_w, vv_i) +INSN_LASX(xvsrarni_w_d, vv_i) +INSN_LASX(xvsrarni_d_q, vv_i) + +INSN_LASX(xvssrln_b_h, vvv) +INSN_LASX(xvssrln_h_w, vvv) +INSN_LASX(xvssrln_w_d, vvv) +INSN_LASX(xvssran_b_h, vvv) +INSN_LASX(xvssran_h_w, vvv) +INSN_LASX(xvssran_w_d, vvv) +INSN_LASX(xvssrln_bu_h, vvv) +INSN_LASX(xvssrln_hu_w, vvv) +INSN_LASX(xvssrln_wu_d, vvv) +INSN_LASX(xvssran_bu_h, vvv) +INSN_LASX(xvssran_hu_w, vvv) +INSN_LASX(xvssran_wu_d, vvv) + +INSN_LASX(xvssrlni_b_h, vv_i) +INSN_LASX(xvssrlni_h_w, vv_i) +INSN_LASX(xvssrlni_w_d, vv_i) +INSN_LASX(xvssrlni_d_q, vv_i) +INSN_LASX(xvssrani_b_h, vv_i) +INSN_LASX(xvssrani_h_w, vv_i) +INSN_LASX(xvssrani_w_d, vv_i) +INSN_LASX(xvssrani_d_q, vv_i) +INSN_LASX(xvssrlni_bu_h, vv_i) +INSN_LASX(xvssrlni_hu_w, vv_i) +INSN_LASX(xvssrlni_wu_d, vv_i) +INSN_LASX(xvssrlni_du_q, vv_i) +INSN_LASX(xvssrani_bu_h, vv_i) +INSN_LASX(xvssrani_hu_w, vv_i) +INSN_LASX(xvssrani_wu_d, vv_i) +INSN_LASX(xvssrani_du_q, vv_i) + +INSN_LASX(xvssrlrn_b_h, vvv) +INSN_LASX(xvssrlrn_h_w, vvv) +INSN_LASX(xvssrlrn_w_d, vvv) +INSN_LASX(xvssrarn_b_h, vvv) +INSN_LASX(xvssrarn_h_w, vvv) +INSN_LASX(xvssrarn_w_d, vvv) +INSN_LASX(xvssrlrn_bu_h, vvv) +INSN_LASX(xvssrlrn_hu_w, vvv) +INSN_LASX(xvssrlrn_wu_d, vvv) +INSN_LASX(xvssrarn_bu_h, vvv) +INSN_LASX(xvssrarn_hu_w, vvv) +INSN_LASX(xvssrarn_wu_d, vvv) + +INSN_LASX(xvssrlrni_b_h, vv_i) +INSN_LASX(xvssrlrni_h_w, vv_i) +INSN_LASX(xvssrlrni_w_d, vv_i) +INSN_LASX(xvssrlrni_d_q, vv_i) +INSN_LASX(xvssrlrni_bu_h, vv_i) +INSN_LASX(xvssrlrni_hu_w, vv_i) +INSN_LASX(xvssrlrni_wu_d, vv_i) +INSN_LASX(xvssrlrni_du_q, vv_i) +INSN_LASX(xvssrarni_b_h, vv_i) +INSN_LASX(xvssrarni_h_w, vv_i) +INSN_LASX(xvssrarni_w_d, vv_i) +INSN_LASX(xvssrarni_d_q, vv_i) +INSN_LASX(xvssrarni_bu_h, vv_i) +INSN_LASX(xvssrarni_hu_w, vv_i) +INSN_LASX(xvssrarni_wu_d, vv_i) +INSN_LASX(xvssrarni_du_q, vv_i) + +INSN_LASX(xvclo_b, vv) +INSN_LASX(xvclo_h, vv) +INSN_LASX(xvclo_w, vv) +INSN_LASX(xvclo_d, vv) +INSN_LASX(xvclz_b, vv) +INSN_LASX(xvclz_h, vv) +INSN_LASX(xvclz_w, vv) +INSN_LASX(xvclz_d, vv) + +INSN_LASX(xvpcnt_b, vv) +INSN_LASX(xvpcnt_h, vv) +INSN_LASX(xvpcnt_w, vv) +INSN_LASX(xvpcnt_d, vv) + +INSN_LASX(xvbitclr_b, vvv) +INSN_LASX(xvbitclr_h, vvv) +INSN_LASX(xvbitclr_w, vvv) +INSN_LASX(xvbitclr_d, vvv) +INSN_LASX(xvbitclri_b, vv_i) +INSN_LASX(xvbitclri_h, vv_i) +INSN_LASX(xvbitclri_w, vv_i) +INSN_LASX(xvbitclri_d, vv_i) +INSN_LASX(xvbitset_b, vvv) +INSN_LASX(xvbitset_h, vvv) +INSN_LASX(xvbitset_w, vvv) +INSN_LASX(xvbitset_d, vvv) +INSN_LASX(xvbitseti_b, vv_i) +INSN_LASX(xvbitseti_h, vv_i) +INSN_LASX(xvbitseti_w, vv_i) +INSN_LASX(xvbitseti_d, vv_i) +INSN_LASX(xvbitrev_b, vvv) +INSN_LASX(xvbitrev_h, vvv) +INSN_LASX(xvbitrev_w, vvv) +INSN_LASX(xvbitrev_d, vvv) +INSN_LASX(xvbitrevi_b, vv_i) +INSN_LASX(xvbitrevi_h, vv_i) +INSN_LASX(xvbitrevi_w, vv_i) +INSN_LASX(xvbitrevi_d, vv_i) + +INSN_LASX(xvfrstp_b, vvv) +INSN_LASX(xvfrstp_h, vvv) +INSN_LASX(xvfrstpi_b, vv_i) +INSN_LASX(xvfrstpi_h, vv_i) + +INSN_LASX(xvfadd_s, vvv) +INSN_LASX(xvfadd_d, vvv) +INSN_LASX(xvfsub_s, vvv) +INSN_LASX(xvfsub_d, vvv) +INSN_LASX(xvfmul_s, vvv) +INSN_LASX(xvfmul_d, vvv) +INSN_LASX(xvfdiv_s, vvv) +INSN_LASX(xvfdiv_d, vvv) + +INSN_LASX(xvfmadd_s, vvvv) +INSN_LASX(xvfmadd_d, vvvv) +INSN_LASX(xvfmsub_s, vvvv) +INSN_LASX(xvfmsub_d, vvvv) +INSN_LASX(xvfnmadd_s, vvvv) +INSN_LASX(xvfnmadd_d, vvvv) +INSN_LASX(xvfnmsub_s, vvvv) +INSN_LASX(xvfnmsub_d, vvvv) + +INSN_LASX(xvfmax_s, vvv) +INSN_LASX(xvfmax_d, vvv) +INSN_LASX(xvfmin_s, vvv) +INSN_LASX(xvfmin_d, vvv) + +INSN_LASX(xvfmaxa_s, vvv) +INSN_LASX(xvfmaxa_d, vvv) +INSN_LASX(xvfmina_s, vvv) +INSN_LASX(xvfmina_d, vvv) + +INSN_LASX(xvflogb_s, vv) +INSN_LASX(xvflogb_d, vv) + +INSN_LASX(xvfclass_s, vv) +INSN_LASX(xvfclass_d, vv) + +INSN_LASX(xvfsqrt_s, vv) +INSN_LASX(xvfsqrt_d, vv) +INSN_LASX(xvfrecip_s, vv) +INSN_LASX(xvfrecip_d, vv) +INSN_LASX(xvfrsqrt_s, vv) +INSN_LASX(xvfrsqrt_d, vv) + +INSN_LASX(xvfcvtl_s_h, vv) +INSN_LASX(xvfcvth_s_h, vv) +INSN_LASX(xvfcvtl_d_s, vv) +INSN_LASX(xvfcvth_d_s, vv) +INSN_LASX(xvfcvt_h_s, vvv) +INSN_LASX(xvfcvt_s_d, vvv) + +INSN_LASX(xvfrint_s, vv) +INSN_LASX(xvfrint_d, vv) +INSN_LASX(xvfrintrm_s, vv) +INSN_LASX(xvfrintrm_d, vv) +INSN_LASX(xvfrintrp_s, vv) +INSN_LASX(xvfrintrp_d, vv) +INSN_LASX(xvfrintrz_s, vv) +INSN_LASX(xvfrintrz_d, vv) +INSN_LASX(xvfrintrne_s, vv) +INSN_LASX(xvfrintrne_d, vv) + +INSN_LASX(xvftint_w_s, vv) +INSN_LASX(xvftint_l_d, vv) +INSN_LASX(xvftintrm_w_s, vv) +INSN_LASX(xvftintrm_l_d, vv) +INSN_LASX(xvftintrp_w_s, vv) +INSN_LASX(xvftintrp_l_d, vv) +INSN_LASX(xvftintrz_w_s, vv) +INSN_LASX(xvftintrz_l_d, vv) +INSN_LASX(xvftintrne_w_s, vv) +INSN_LASX(xvftintrne_l_d, vv) +INSN_LASX(xvftint_wu_s, vv) +INSN_LASX(xvftint_lu_d, vv) +INSN_LASX(xvftintrz_wu_s, vv) +INSN_LASX(xvftintrz_lu_d, vv) +INSN_LASX(xvftint_w_d, vvv) +INSN_LASX(xvftintrm_w_d, vvv) +INSN_LASX(xvftintrp_w_d, vvv) +INSN_LASX(xvftintrz_w_d, vvv) +INSN_LASX(xvftintrne_w_d, vvv) +INSN_LASX(xvftintl_l_s, vv) +INSN_LASX(xvftinth_l_s, vv) +INSN_LASX(xvftintrml_l_s, vv) +INSN_LASX(xvftintrmh_l_s, vv) +INSN_LASX(xvftintrpl_l_s, vv) +INSN_LASX(xvftintrph_l_s, vv) +INSN_LASX(xvftintrzl_l_s, vv) +INSN_LASX(xvftintrzh_l_s, vv) +INSN_LASX(xvftintrnel_l_s, vv) +INSN_LASX(xvftintrneh_l_s, vv) + +INSN_LASX(xvffint_s_w, vv) +INSN_LASX(xvffint_s_wu, vv) +INSN_LASX(xvffint_d_l, vv) +INSN_LASX(xvffint_d_lu, vv) +INSN_LASX(xvffintl_d_w, vv) +INSN_LASX(xvffinth_d_w, vv) +INSN_LASX(xvffint_s_l, vvv) + +INSN_LASX(xvseq_b, vvv) +INSN_LASX(xvseq_h, vvv) +INSN_LASX(xvseq_w, vvv) +INSN_LASX(xvseq_d, vvv) +INSN_LASX(xvseqi_b, vv_i) +INSN_LASX(xvseqi_h, vv_i) +INSN_LASX(xvseqi_w, vv_i) +INSN_LASX(xvseqi_d, vv_i) + +INSN_LASX(xvsle_b, vvv) +INSN_LASX(xvsle_h, vvv) +INSN_LASX(xvsle_w, vvv) +INSN_LASX(xvsle_d, vvv) +INSN_LASX(xvslei_b, vv_i) +INSN_LASX(xvslei_h, vv_i) +INSN_LASX(xvslei_w, vv_i) +INSN_LASX(xvslei_d, vv_i) +INSN_LASX(xvsle_bu, vvv) +INSN_LASX(xvsle_hu, vvv) +INSN_LASX(xvsle_wu, vvv) +INSN_LASX(xvsle_du, vvv) +INSN_LASX(xvslei_bu, vv_i) +INSN_LASX(xvslei_hu, vv_i) +INSN_LASX(xvslei_wu, vv_i) +INSN_LASX(xvslei_du, vv_i) + +INSN_LASX(xvslt_b, vvv) +INSN_LASX(xvslt_h, vvv) +INSN_LASX(xvslt_w, vvv) +INSN_LASX(xvslt_d, vvv) +INSN_LASX(xvslti_b, vv_i) +INSN_LASX(xvslti_h, vv_i) +INSN_LASX(xvslti_w, vv_i) +INSN_LASX(xvslti_d, vv_i) +INSN_LASX(xvslt_bu, vvv) +INSN_LASX(xvslt_hu, vvv) +INSN_LASX(xvslt_wu, vvv) +INSN_LASX(xvslt_du, vvv) +INSN_LASX(xvslti_bu, vv_i) +INSN_LASX(xvslti_hu, vv_i) +INSN_LASX(xvslti_wu, vv_i) +INSN_LASX(xvslti_du, vv_i) + +#define output_xvfcmp(C, PREFIX, SUFFIX) \ +{ \ + (C)->info->fprintf_func((C)->info->stream, "%08x %s%s\tx%d, x%d, x%d", \ + (C)->insn, PREFIX, SUFFIX, a->vd, \ + a->vj, a->vk); \ +} +static bool output_xxx_fcond(DisasContext *ctx, arg_vvv_fcond * a, + const char *suffix) +{ + bool ret = true; + switch (a->fcond) { + case 0x0: + output_xvfcmp(ctx, "xvfcmp_caf_", suffix); + break; + case 0x1: + output_xvfcmp(ctx, "xvfcmp_saf_", suffix); + break; + case 0x2: + output_xvfcmp(ctx, "xvfcmp_clt_", suffix); + break; + case 0x3: + output_xvfcmp(ctx, "xvfcmp_slt_", suffix); + break; + case 0x4: + output_xvfcmp(ctx, "xvfcmp_ceq_", suffix); + break; + case 0x5: + output_xvfcmp(ctx, "xvfcmp_seq_", suffix); + break; + case 0x6: + output_xvfcmp(ctx, "xvfcmp_cle_", suffix); + break; + case 0x7: + output_xvfcmp(ctx, "xvfcmp_sle_", suffix); + break; + case 0x8: + output_xvfcmp(ctx, "xvfcmp_cun_", suffix); + break; + case 0x9: + output_xvfcmp(ctx, "xvfcmp_sun_", suffix); + break; + case 0xA: + output_xvfcmp(ctx, "xvfcmp_cult_", suffix); + break; + case 0xB: + output_xvfcmp(ctx, "xvfcmp_sult_", suffix); + break; + case 0xC: + output_xvfcmp(ctx, "xvfcmp_cueq_", suffix); + break; + case 0xD: + output_xvfcmp(ctx, "xvfcmp_sueq_", suffix); + break; + case 0xE: + output_xvfcmp(ctx, "xvfcmp_cule_", suffix); + break; + case 0xF: + output_xvfcmp(ctx, "xvfcmp_sule_", suffix); + break; + case 0x10: + output_xvfcmp(ctx, "xvfcmp_cne_", suffix); + break; + case 0x11: + output_xvfcmp(ctx, "xvfcmp_sne_", suffix); + break; + case 0x14: + output_xvfcmp(ctx, "xvfcmp_cor_", suffix); + break; + case 0x15: + output_xvfcmp(ctx, "xvfcmp_sor_", suffix); + break; + case 0x18: + output_xvfcmp(ctx, "xvfcmp_cune_", suffix); + break; + case 0x19: + output_xvfcmp(ctx, "xvfcmp_sune_", suffix); + break; + default: + ret = false; + } + return ret; +} + +#define LASX_FCMP_INSN(suffix) \ +static bool trans_xvfcmp_cond_##suffix(DisasContext *ctx, \ + arg_vvv_fcond * a) \ +{ \ + return output_xxx_fcond(ctx, a, #suffix); \ +} + +LASX_FCMP_INSN(s) +LASX_FCMP_INSN(d) + +INSN_LASX(xvbitsel_v, vvvv) +INSN_LASX(xvbitseli_b, vv_i) + +INSN_LASX(xvseteqz_v, cv) +INSN_LASX(xvsetnez_v, cv) +INSN_LASX(xvsetanyeqz_b, cv) +INSN_LASX(xvsetanyeqz_h, cv) +INSN_LASX(xvsetanyeqz_w, cv) +INSN_LASX(xvsetanyeqz_d, cv) +INSN_LASX(xvsetallnez_b, cv) +INSN_LASX(xvsetallnez_h, cv) +INSN_LASX(xvsetallnez_w, cv) +INSN_LASX(xvsetallnez_d, cv) + +INSN_LASX(xvinsgr2vr_w, vr_i) +INSN_LASX(xvinsgr2vr_d, vr_i) +INSN_LASX(xvpickve2gr_w, rv_i) +INSN_LASX(xvpickve2gr_d, rv_i) +INSN_LASX(xvpickve2gr_wu, rv_i) +INSN_LASX(xvpickve2gr_du, rv_i) + +INSN_LASX(xvreplgr2vr_b, vr) +INSN_LASX(xvreplgr2vr_h, vr) +INSN_LASX(xvreplgr2vr_w, vr) +INSN_LASX(xvreplgr2vr_d, vr) + +INSN_LASX(xvreplve_b, vvr) +INSN_LASX(xvreplve_h, vvr) +INSN_LASX(xvreplve_w, vvr) +INSN_LASX(xvreplve_d, vvr) +INSN_LASX(xvrepl128vei_b, vv_i) +INSN_LASX(xvrepl128vei_h, vv_i) +INSN_LASX(xvrepl128vei_w, vv_i) +INSN_LASX(xvrepl128vei_d, vv_i) + +INSN_LASX(xvreplve0_b, vv) +INSN_LASX(xvreplve0_h, vv) +INSN_LASX(xvreplve0_w, vv) +INSN_LASX(xvreplve0_d, vv) +INSN_LASX(xvreplve0_q, vv) + +INSN_LASX(xvinsve0_w, vv_i) +INSN_LASX(xvinsve0_d, vv_i) + +INSN_LASX(xvpickve_w, vv_i) +INSN_LASX(xvpickve_d, vv_i) + +INSN_LASX(xvbsll_v, vv_i) +INSN_LASX(xvbsrl_v, vv_i) + +INSN_LASX(xvpackev_b, vvv) +INSN_LASX(xvpackev_h, vvv) +INSN_LASX(xvpackev_w, vvv) +INSN_LASX(xvpackev_d, vvv) +INSN_LASX(xvpackod_b, vvv) +INSN_LASX(xvpackod_h, vvv) +INSN_LASX(xvpackod_w, vvv) +INSN_LASX(xvpackod_d, vvv) + +INSN_LASX(xvpickev_b, vvv) +INSN_LASX(xvpickev_h, vvv) +INSN_LASX(xvpickev_w, vvv) +INSN_LASX(xvpickev_d, vvv) +INSN_LASX(xvpickod_b, vvv) +INSN_LASX(xvpickod_h, vvv) +INSN_LASX(xvpickod_w, vvv) +INSN_LASX(xvpickod_d, vvv) + +INSN_LASX(xvilvl_b, vvv) +INSN_LASX(xvilvl_h, vvv) +INSN_LASX(xvilvl_w, vvv) +INSN_LASX(xvilvl_d, vvv) +INSN_LASX(xvilvh_b, vvv) +INSN_LASX(xvilvh_h, vvv) +INSN_LASX(xvilvh_w, vvv) +INSN_LASX(xvilvh_d, vvv) + +INSN_LASX(xvshuf_b, vvvv) +INSN_LASX(xvshuf_h, vvv) +INSN_LASX(xvshuf_w, vvv) +INSN_LASX(xvshuf_d, vvv) + +INSN_LASX(xvperm_w, vvv) + +INSN_LASX(xvshuf4i_b, vv_i) +INSN_LASX(xvshuf4i_h, vv_i) +INSN_LASX(xvshuf4i_w, vv_i) +INSN_LASX(xvshuf4i_d, vv_i) + +INSN_LASX(xvpermi_w, vv_i) +INSN_LASX(xvpermi_d, vv_i) +INSN_LASX(xvpermi_q, vv_i) + +INSN_LASX(xvextrins_d, vv_i) +INSN_LASX(xvextrins_w, vv_i) +INSN_LASX(xvextrins_h, vv_i) +INSN_LASX(xvextrins_b, vv_i) + +INSN_LASX(xvld, vr_i) +INSN_LASX(xvst, vr_i) +INSN_LASX(xvldx, vrr) +INSN_LASX(xvstx, vrr) + +INSN_LASX(xvldrepl_d, vr_i) +INSN_LASX(xvldrepl_w, vr_i) +INSN_LASX(xvldrepl_h, vr_i) +INSN_LASX(xvldrepl_b, vr_i) +INSN_LASX(xvstelm_d, vr_ii) +INSN_LASX(xvstelm_w, vr_ii) +INSN_LASX(xvstelm_h, vr_ii) +INSN_LASX(xvstelm_b, vr_ii) diff --git a/target/loongarch/gdbstub.c b/target/loongarch/gdbstub.c index 0c48834201..a0e1439bd0 100644 --- a/target/loongarch/gdbstub.c +++ b/target/loongarch/gdbstub.c @@ -10,44 +10,85 @@ #include "cpu.h" #include "internals.h" #include "exec/gdbstub.h" +#include "gdbstub/helpers.h" +#include "vec.h" + +uint64_t read_fcc(CPULoongArchState *env) +{ + uint64_t ret = 0; + + for (int i = 0; i < 8; ++i) { + ret |= (uint64_t)env->cf[i] << (i * 8); + } + + return ret; +} + +void write_fcc(CPULoongArchState *env, uint64_t val) +{ + for (int i = 0; i < 8; ++i) { + env->cf[i] = (val >> (i * 8)) & 1; + } +} int loongarch_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; + CPULoongArchState *env = cpu_env(cs); + uint64_t val; if (0 <= n && n < 32) { - return gdb_get_regl(mem_buf, env->gpr[n]); + val = env->gpr[n]; } else if (n == 32) { - return gdb_get_regl(mem_buf, env->pc); + /* orig_a0 */ + val = 0; } else if (n == 33) { - return gdb_get_regl(mem_buf, env->badaddr); + val = env->pc; + } else if (n == 34) { + val = env->CSR_BADV; + } + + if (0 <= n && n <= 34) { + if (is_la64(env)) { + return gdb_get_reg64(mem_buf, val); + } else { + return gdb_get_reg32(mem_buf, val); + } } return 0; } int loongarch_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; - target_ulong tmp = ldtul_p(mem_buf); + CPULoongArchState *env = cpu_env(cs); + target_ulong tmp; + int read_length; int length = 0; + if (is_la64(env)) { + tmp = ldq_p(mem_buf); + read_length = 8; + } else { + tmp = ldl_p(mem_buf); + read_length = 4; + } + if (0 <= n && n < 32) { env->gpr[n] = tmp; - length = sizeof(target_ulong); - } else if (n == 32) { - env->pc = tmp; - length = sizeof(target_ulong); + length = read_length; + } else if (n == 33) { + set_pc(env, tmp); + length = read_length; } return length; } -static int loongarch_gdb_get_fpu(CPULoongArchState *env, - GByteArray *mem_buf, int n) +static int loongarch_gdb_get_fpu(CPUState *cs, GByteArray *mem_buf, int n) { + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = &cpu->env; + if (0 <= n && n < 32) { - return gdb_get_reg64(mem_buf, env->fpr[n]); + return gdb_get_reg64(mem_buf, env->fpr[n].vreg.D(0)); } else if (32 <= n && n < 40) { return gdb_get_reg8(mem_buf, env->cf[n - 32]); } else if (n == 40) { @@ -56,13 +97,14 @@ static int loongarch_gdb_get_fpu(CPULoongArchState *env, return 0; } -static int loongarch_gdb_set_fpu(CPULoongArchState *env, - uint8_t *mem_buf, int n) +static int loongarch_gdb_set_fpu(CPUState *cs, uint8_t *mem_buf, int n) { + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = &cpu->env; int length = 0; if (0 <= n && n < 32) { - env->fpr[n] = ldq_p(mem_buf); + env->fpr[n].vreg.D(0) = ldq_p(mem_buf); length = 8; } else if (32 <= n && n < 40) { env->cf[n - 32] = ldub_p(mem_buf); @@ -77,5 +119,5 @@ static int loongarch_gdb_set_fpu(CPULoongArchState *env, void loongarch_cpu_register_gdb_regs_for_features(CPUState *cs) { gdb_register_coprocessor(cs, loongarch_gdb_get_fpu, loongarch_gdb_set_fpu, - 41, "loongarch-fpu64.xml", 0); + gdb_find_static_feature("loongarch-fpu.xml"), 0); } diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h index 85c11a60d4..b3b64a0215 100644 --- a/target/loongarch/helper.h +++ b/target/loongarch/helper.h @@ -91,12 +91,14 @@ DEF_HELPER_2(ftint_w_d, i64, env, i64) DEF_HELPER_2(frint_s, i64, env, i64) DEF_HELPER_2(frint_d, i64, env, i64) -DEF_HELPER_FLAGS_2(set_rounding_mode, TCG_CALL_NO_RWG, void, env, i32) +DEF_HELPER_FLAGS_1(set_rounding_mode, TCG_CALL_NO_RWG, void, env) DEF_HELPER_1(rdtime_d, i64, env) +#ifndef CONFIG_USER_ONLY /* CSRs helper */ DEF_HELPER_1(csrrd_pgd, i64, env) +DEF_HELPER_1(csrrd_cpuid, i64, env) DEF_HELPER_1(csrrd_tval, i64, env) DEF_HELPER_2(csrwr_estat, i64, env, tl) DEF_HELPER_2(csrwr_asid, i64, env, tl) @@ -128,3 +130,591 @@ DEF_HELPER_4(lddir, tl, env, tl, tl, i32) DEF_HELPER_4(ldpte, void, env, tl, tl, i32) DEF_HELPER_1(ertn, void, env) DEF_HELPER_1(idle, void, env) +#endif + +/* LoongArch LSX */ +DEF_HELPER_FLAGS_4(vhaddw_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vaddwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsubwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vaddwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsubwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vaddwev_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_q_du_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_q_du_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vavg_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vavgr_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vabsd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vadda_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vadda_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vadda_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vadda_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmini_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vmaxi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vmuh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmulwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmulwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmulwev_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmadd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmadd_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmsub_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmsub_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmaddwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmaddwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmaddwev_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vdiv_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsat_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_3(vexth_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(vext2xv_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_w_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_d_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_d_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_wu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_du_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_du_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsigncov_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsigncov_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsigncov_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsigncov_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(vmskltz_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmskltz_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmskltz_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmskltz_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmskgez_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmsknz_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vnori_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vsllwil_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsllwil_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsllwil_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_3(vextl_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsllwil_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsllwil_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsllwil_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_3(vextl_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsrlr_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlr_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlr_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlr_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlri_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlri_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vsrar_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrar_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrar_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrar_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrari_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrari_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrari_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrari_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vsrln_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrln_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrln_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsran_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsran_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsran_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsrlni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrani_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrani_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrani_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrani_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vsrlrn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlrn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlrn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrarn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrarn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrarn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsrlrni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlrni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlrni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlrni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrarni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrarni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrarni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrarni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vssrln_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vssrlni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vssrlrn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vssrlrni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_3(vclo_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclo_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclo_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclo_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclz_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclz_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclz_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclz_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(vpcnt_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vpcnt_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vpcnt_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vpcnt_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vbitclr_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitclr_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitclr_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitclr_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitclri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitclri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitclri_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitclri_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vbitset_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitset_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitset_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitset_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitseti_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitseti_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitseti_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitseti_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vbitrev_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitrev_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitrev_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitrev_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitrevi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitrevi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitrevi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitrevi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vfrstp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vfrstp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vfrstpi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vfrstpi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_5(vfadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfdiv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_6(vfmadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfmadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfmsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfmsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfnmadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfnmadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfnmsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfnmsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_5(vfmax_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmin_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_5(vfmaxa_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmaxa_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmina_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmina_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vflogb_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vflogb_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vfclass_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfclass_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vfsqrt_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfsqrt_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrecip_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrecip_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrsqrt_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrsqrt_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vfcvtl_s_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfcvth_s_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfcvtl_d_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfcvth_d_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfcvt_h_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfcvt_s_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vfrintrne_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrne_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrz_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrz_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrp_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrp_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrm_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrm_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrint_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrint_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vftintrne_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrne_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrz_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrz_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrp_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrp_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrm_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrm_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftint_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftint_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrz_wu_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrz_lu_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftint_wu_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftint_lu_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftintrne_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftintrz_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftintrp_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftintrm_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftint_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrnel_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrneh_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrzl_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrzh_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrpl_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrph_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrml_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrmh_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintl_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftinth_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vffint_s_w, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffint_d_l, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffint_s_wu, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffint_d_lu, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffintl_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffinth_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vffint_s_l, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vseqi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vseqi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vseqi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vseqi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vslei_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vslti_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_6(vfcmp_c_s, void, env, i32, i32, i32, i32, i32) +DEF_HELPER_6(vfcmp_s_s, void, env, i32, i32, i32, i32, i32) +DEF_HELPER_6(vfcmp_c_d, void, env, i32, i32, i32, i32, i32) +DEF_HELPER_6(vfcmp_s_d, void, env, i32, i32, i32, i32, i32) + +DEF_HELPER_FLAGS_4(vbitseli_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_4(vsetanyeqz_b, void, env, i32, i32, i32) +DEF_HELPER_4(vsetanyeqz_h, void, env, i32, i32, i32) +DEF_HELPER_4(vsetanyeqz_w, void, env, i32, i32, i32) +DEF_HELPER_4(vsetanyeqz_d, void, env, i32, i32, i32) +DEF_HELPER_4(vsetallnez_b, void, env, i32, i32, i32) +DEF_HELPER_4(vsetallnez_h, void, env, i32, i32, i32) +DEF_HELPER_4(vsetallnez_w, void, env, i32, i32, i32) +DEF_HELPER_4(vsetallnez_d, void, env, i32, i32, i32) + +DEF_HELPER_FLAGS_4(xvinsve0_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(xvinsve0_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(xvpickve_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(xvpickve_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vpackev_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackev_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackev_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackev_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackod_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackod_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackod_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackod_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vpickev_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickev_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickev_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickev_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickod_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickod_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickod_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickod_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vilvl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvl_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvh_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(vshuf_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vshuf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vshuf_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vshuf_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vshuf4i_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vshuf4i_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vshuf4i_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vshuf4i_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vperm_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpermi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vpermi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vpermi_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vextrins_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vextrins_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vextrins_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vextrins_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) diff --git a/target/loongarch/insn_trans/trans_atomic.c.inc b/target/loongarch/insn_trans/trans_atomic.c.inc deleted file mode 100644 index 6763c1c301..0000000000 --- a/target/loongarch/insn_trans/trans_atomic.c.inc +++ /dev/null @@ -1,113 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -static bool gen_ll(DisasContext *ctx, arg_rr_i *a, MemOp mop) -{ - TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); - TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - TCGv t0 = tcg_temp_new(); - - tcg_gen_addi_tl(t0, src1, a->imm); - tcg_gen_qemu_ld_i64(dest, t0, ctx->mem_idx, mop); - tcg_gen_st_tl(t0, cpu_env, offsetof(CPULoongArchState, lladdr)); - tcg_gen_st_tl(dest, cpu_env, offsetof(CPULoongArchState, llval)); - gen_set_gpr(a->rd, dest, EXT_NONE); - tcg_temp_free(t0); - - return true; -} - -static bool gen_sc(DisasContext *ctx, arg_rr_i *a, MemOp mop) -{ - TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); - TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - TCGv src2 = gpr_src(ctx, a->rd, EXT_NONE); - TCGv t0 = tcg_temp_new(); - TCGv val = tcg_temp_new(); - - TCGLabel *l1 = gen_new_label(); - TCGLabel *done = gen_new_label(); - - tcg_gen_addi_tl(t0, src1, a->imm); - tcg_gen_brcond_tl(TCG_COND_EQ, t0, cpu_lladdr, l1); - tcg_gen_movi_tl(dest, 0); - tcg_gen_br(done); - - gen_set_label(l1); - tcg_gen_mov_tl(val, src2); - /* generate cmpxchg */ - tcg_gen_atomic_cmpxchg_tl(t0, cpu_lladdr, cpu_llval, - val, ctx->mem_idx, mop); - tcg_gen_setcond_tl(TCG_COND_EQ, dest, t0, cpu_llval); - gen_set_label(done); - gen_set_gpr(a->rd, dest, EXT_NONE); - tcg_temp_free(t0); - tcg_temp_free(val); - - return true; -} - -static bool gen_am(DisasContext *ctx, arg_rrr *a, - void (*func)(TCGv, TCGv, TCGv, TCGArg, MemOp), - MemOp mop) -{ - TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); - TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); - TCGv val = gpr_src(ctx, a->rk, EXT_NONE); - - if (a->rd != 0 && (a->rj == a->rd || a->rk == a->rd)) { - qemu_log_mask(LOG_GUEST_ERROR, - "Warning: source register overlaps destination register" - "in atomic insn at pc=0x" TARGET_FMT_lx "\n", - ctx->base.pc_next - 4); - return false; - } - - func(dest, addr, val, ctx->mem_idx, mop); - gen_set_gpr(a->rd, dest, EXT_NONE); - - return true; -} - -TRANS(ll_w, gen_ll, MO_TESL) -TRANS(sc_w, gen_sc, MO_TESL) -TRANS(ll_d, gen_ll, MO_TEUQ) -TRANS(sc_d, gen_sc, MO_TEUQ) -TRANS(amswap_w, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL) -TRANS(amswap_d, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) -TRANS(amadd_w, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL) -TRANS(amadd_d, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) -TRANS(amand_w, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL) -TRANS(amand_d, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) -TRANS(amor_w, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL) -TRANS(amor_d, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) -TRANS(amxor_w, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL) -TRANS(amxor_d, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) -TRANS(ammax_w, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL) -TRANS(ammax_d, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) -TRANS(ammin_w, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL) -TRANS(ammin_d, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) -TRANS(ammax_wu, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL) -TRANS(ammax_du, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) -TRANS(ammin_wu, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL) -TRANS(ammin_du, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) -TRANS(amswap_db_w, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL) -TRANS(amswap_db_d, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) -TRANS(amadd_db_w, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL) -TRANS(amadd_db_d, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) -TRANS(amand_db_w, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL) -TRANS(amand_db_d, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) -TRANS(amor_db_w, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL) -TRANS(amor_db_d, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) -TRANS(amxor_db_w, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL) -TRANS(amxor_db_d, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) -TRANS(ammax_db_w, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL) -TRANS(ammax_db_d, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) -TRANS(ammin_db_w, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL) -TRANS(ammin_db_d, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) -TRANS(ammax_db_wu, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL) -TRANS(ammax_db_du, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) -TRANS(ammin_db_wu, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL) -TRANS(ammin_db_du, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) diff --git a/target/loongarch/insn_trans/trans_farith.c.inc b/target/loongarch/insn_trans/trans_farith.c.inc deleted file mode 100644 index 65ad2ffab8..0000000000 --- a/target/loongarch/insn_trans/trans_farith.c.inc +++ /dev/null @@ -1,105 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -static bool gen_fff(DisasContext *ctx, arg_fff *a, - void (*func)(TCGv, TCGv_env, TCGv, TCGv)) -{ - func(cpu_fpr[a->fd], cpu_env, cpu_fpr[a->fj], cpu_fpr[a->fk]); - return true; -} - -static bool gen_ff(DisasContext *ctx, arg_ff *a, - void (*func)(TCGv, TCGv_env, TCGv)) -{ - func(cpu_fpr[a->fd], cpu_env, cpu_fpr[a->fj]); - return true; -} - -static bool gen_muladd(DisasContext *ctx, arg_ffff *a, - void (*func)(TCGv, TCGv_env, TCGv, TCGv, TCGv, TCGv_i32), - int flag) -{ - TCGv_i32 tflag = tcg_constant_i32(flag); - func(cpu_fpr[a->fd], cpu_env, cpu_fpr[a->fj], - cpu_fpr[a->fk], cpu_fpr[a->fa], tflag); - return true; -} - -static bool trans_fcopysign_s(DisasContext *ctx, arg_fcopysign_s *a) -{ - tcg_gen_deposit_i64(cpu_fpr[a->fd], cpu_fpr[a->fk], cpu_fpr[a->fj], 0, 31); - return true; -} - -static bool trans_fcopysign_d(DisasContext *ctx, arg_fcopysign_d *a) -{ - tcg_gen_deposit_i64(cpu_fpr[a->fd], cpu_fpr[a->fk], cpu_fpr[a->fj], 0, 63); - return true; -} - -static bool trans_fabs_s(DisasContext *ctx, arg_fabs_s *a) -{ - tcg_gen_andi_i64(cpu_fpr[a->fd], cpu_fpr[a->fj], MAKE_64BIT_MASK(0, 31)); - gen_nanbox_s(cpu_fpr[a->fd], cpu_fpr[a->fd]); - return true; -} - -static bool trans_fabs_d(DisasContext *ctx, arg_fabs_d *a) -{ - tcg_gen_andi_i64(cpu_fpr[a->fd], cpu_fpr[a->fj], MAKE_64BIT_MASK(0, 63)); - return true; -} - -static bool trans_fneg_s(DisasContext *ctx, arg_fneg_s *a) -{ - tcg_gen_xori_i64(cpu_fpr[a->fd], cpu_fpr[a->fj], 0x80000000); - gen_nanbox_s(cpu_fpr[a->fd], cpu_fpr[a->fd]); - return true; -} - -static bool trans_fneg_d(DisasContext *ctx, arg_fneg_d *a) -{ - tcg_gen_xori_i64(cpu_fpr[a->fd], cpu_fpr[a->fj], 0x8000000000000000LL); - return true; -} - -TRANS(fadd_s, gen_fff, gen_helper_fadd_s) -TRANS(fadd_d, gen_fff, gen_helper_fadd_d) -TRANS(fsub_s, gen_fff, gen_helper_fsub_s) -TRANS(fsub_d, gen_fff, gen_helper_fsub_d) -TRANS(fmul_s, gen_fff, gen_helper_fmul_s) -TRANS(fmul_d, gen_fff, gen_helper_fmul_d) -TRANS(fdiv_s, gen_fff, gen_helper_fdiv_s) -TRANS(fdiv_d, gen_fff, gen_helper_fdiv_d) -TRANS(fmax_s, gen_fff, gen_helper_fmax_s) -TRANS(fmax_d, gen_fff, gen_helper_fmax_d) -TRANS(fmin_s, gen_fff, gen_helper_fmin_s) -TRANS(fmin_d, gen_fff, gen_helper_fmin_d) -TRANS(fmaxa_s, gen_fff, gen_helper_fmaxa_s) -TRANS(fmaxa_d, gen_fff, gen_helper_fmaxa_d) -TRANS(fmina_s, gen_fff, gen_helper_fmina_s) -TRANS(fmina_d, gen_fff, gen_helper_fmina_d) -TRANS(fscaleb_s, gen_fff, gen_helper_fscaleb_s) -TRANS(fscaleb_d, gen_fff, gen_helper_fscaleb_d) -TRANS(fsqrt_s, gen_ff, gen_helper_fsqrt_s) -TRANS(fsqrt_d, gen_ff, gen_helper_fsqrt_d) -TRANS(frecip_s, gen_ff, gen_helper_frecip_s) -TRANS(frecip_d, gen_ff, gen_helper_frecip_d) -TRANS(frsqrt_s, gen_ff, gen_helper_frsqrt_s) -TRANS(frsqrt_d, gen_ff, gen_helper_frsqrt_d) -TRANS(flogb_s, gen_ff, gen_helper_flogb_s) -TRANS(flogb_d, gen_ff, gen_helper_flogb_d) -TRANS(fclass_s, gen_ff, gen_helper_fclass_s) -TRANS(fclass_d, gen_ff, gen_helper_fclass_d) -TRANS(fmadd_s, gen_muladd, gen_helper_fmuladd_s, 0) -TRANS(fmadd_d, gen_muladd, gen_helper_fmuladd_d, 0) -TRANS(fmsub_s, gen_muladd, gen_helper_fmuladd_s, float_muladd_negate_c) -TRANS(fmsub_d, gen_muladd, gen_helper_fmuladd_d, float_muladd_negate_c) -TRANS(fnmadd_s, gen_muladd, gen_helper_fmuladd_s, - float_muladd_negate_product | float_muladd_negate_c) -TRANS(fnmadd_d, gen_muladd, gen_helper_fmuladd_d, - float_muladd_negate_product | float_muladd_negate_c) -TRANS(fnmsub_s, gen_muladd, gen_helper_fmuladd_s, float_muladd_negate_product) -TRANS(fnmsub_d, gen_muladd, gen_helper_fmuladd_d, float_muladd_negate_product) diff --git a/target/loongarch/insn_trans/trans_fcnv.c.inc b/target/loongarch/insn_trans/trans_fcnv.c.inc deleted file mode 100644 index c1c6918ad1..0000000000 --- a/target/loongarch/insn_trans/trans_fcnv.c.inc +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -TRANS(fcvt_s_d, gen_ff, gen_helper_fcvt_s_d) -TRANS(fcvt_d_s, gen_ff, gen_helper_fcvt_d_s) -TRANS(ftintrm_w_s, gen_ff, gen_helper_ftintrm_w_s) -TRANS(ftintrm_w_d, gen_ff, gen_helper_ftintrm_w_d) -TRANS(ftintrm_l_s, gen_ff, gen_helper_ftintrm_l_s) -TRANS(ftintrm_l_d, gen_ff, gen_helper_ftintrm_l_d) -TRANS(ftintrp_w_s, gen_ff, gen_helper_ftintrp_w_s) -TRANS(ftintrp_w_d, gen_ff, gen_helper_ftintrp_w_d) -TRANS(ftintrp_l_s, gen_ff, gen_helper_ftintrp_l_s) -TRANS(ftintrp_l_d, gen_ff, gen_helper_ftintrp_l_d) -TRANS(ftintrz_w_s, gen_ff, gen_helper_ftintrz_w_s) -TRANS(ftintrz_w_d, gen_ff, gen_helper_ftintrz_w_d) -TRANS(ftintrz_l_s, gen_ff, gen_helper_ftintrz_l_s) -TRANS(ftintrz_l_d, gen_ff, gen_helper_ftintrz_l_d) -TRANS(ftintrne_w_s, gen_ff, gen_helper_ftintrne_w_s) -TRANS(ftintrne_w_d, gen_ff, gen_helper_ftintrne_w_d) -TRANS(ftintrne_l_s, gen_ff, gen_helper_ftintrne_l_s) -TRANS(ftintrne_l_d, gen_ff, gen_helper_ftintrne_l_d) -TRANS(ftint_w_s, gen_ff, gen_helper_ftint_w_s) -TRANS(ftint_w_d, gen_ff, gen_helper_ftint_w_d) -TRANS(ftint_l_s, gen_ff, gen_helper_ftint_l_s) -TRANS(ftint_l_d, gen_ff, gen_helper_ftint_l_d) -TRANS(ffint_s_w, gen_ff, gen_helper_ffint_s_w) -TRANS(ffint_s_l, gen_ff, gen_helper_ffint_s_l) -TRANS(ffint_d_w, gen_ff, gen_helper_ffint_d_w) -TRANS(ffint_d_l, gen_ff, gen_helper_ffint_d_l) -TRANS(frint_s, gen_ff, gen_helper_frint_s) -TRANS(frint_d, gen_ff, gen_helper_frint_d) diff --git a/target/loongarch/insn_trans/trans_fmemory.c.inc b/target/loongarch/insn_trans/trans_fmemory.c.inc deleted file mode 100644 index 74ee98f63a..0000000000 --- a/target/loongarch/insn_trans/trans_fmemory.c.inc +++ /dev/null @@ -1,153 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -static void maybe_nanbox_load(TCGv freg, MemOp mop) -{ - if ((mop & MO_SIZE) == MO_32) { - gen_nanbox_s(freg, freg); - } -} - -static bool gen_fload_i(DisasContext *ctx, arg_fr_i *a, MemOp mop) -{ - TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); - TCGv temp = NULL; - - if (a->imm) { - temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, addr, a->imm); - addr = temp; - } - - tcg_gen_qemu_ld_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop); - maybe_nanbox_load(cpu_fpr[a->fd], mop); - - if (temp) { - tcg_temp_free(temp); - } - - return true; -} - -static bool gen_fstore_i(DisasContext *ctx, arg_fr_i *a, MemOp mop) -{ - TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); - TCGv temp = NULL; - - if (a->imm) { - temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, addr, a->imm); - addr = temp; - } - - tcg_gen_qemu_st_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop); - - if (temp) { - tcg_temp_free(temp); - } - return true; -} - -static bool gen_floadx(DisasContext *ctx, arg_frr *a, MemOp mop) -{ - TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - TCGv addr = tcg_temp_new(); - - tcg_gen_add_tl(addr, src1, src2); - tcg_gen_qemu_ld_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop); - maybe_nanbox_load(cpu_fpr[a->fd], mop); - tcg_temp_free(addr); - - return true; -} - -static bool gen_fstorex(DisasContext *ctx, arg_frr *a, MemOp mop) -{ - TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - TCGv addr = tcg_temp_new(); - - tcg_gen_add_tl(addr, src1, src2); - tcg_gen_qemu_st_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop); - tcg_temp_free(addr); - - return true; -} - -static bool gen_fload_gt(DisasContext *ctx, arg_frr *a, MemOp mop) -{ - TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - TCGv addr = tcg_temp_new(); - - gen_helper_asrtgt_d(cpu_env, src1, src2); - tcg_gen_add_tl(addr, src1, src2); - tcg_gen_qemu_ld_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop); - maybe_nanbox_load(cpu_fpr[a->fd], mop); - tcg_temp_free(addr); - - return true; -} - -static bool gen_fstore_gt(DisasContext *ctx, arg_frr *a, MemOp mop) -{ - TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - TCGv addr = tcg_temp_new(); - - gen_helper_asrtgt_d(cpu_env, src1, src2); - tcg_gen_add_tl(addr, src1, src2); - tcg_gen_qemu_st_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop); - tcg_temp_free(addr); - - return true; -} - -static bool gen_fload_le(DisasContext *ctx, arg_frr *a, MemOp mop) -{ - TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - TCGv addr = tcg_temp_new(); - - gen_helper_asrtle_d(cpu_env, src1, src2); - tcg_gen_add_tl(addr, src1, src2); - tcg_gen_qemu_ld_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop); - maybe_nanbox_load(cpu_fpr[a->fd], mop); - tcg_temp_free(addr); - - return true; -} - -static bool gen_fstore_le(DisasContext *ctx, arg_frr *a, MemOp mop) -{ - TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - TCGv addr = tcg_temp_new(); - - gen_helper_asrtle_d(cpu_env, src1, src2); - tcg_gen_add_tl(addr, src1, src2); - tcg_gen_qemu_st_tl(cpu_fpr[a->fd], addr, ctx->mem_idx, mop); - tcg_temp_free(addr); - - return true; -} - -TRANS(fld_s, gen_fload_i, MO_TEUL) -TRANS(fst_s, gen_fstore_i, MO_TEUL) -TRANS(fld_d, gen_fload_i, MO_TEUQ) -TRANS(fst_d, gen_fstore_i, MO_TEUQ) -TRANS(fldx_s, gen_floadx, MO_TEUL) -TRANS(fldx_d, gen_floadx, MO_TEUQ) -TRANS(fstx_s, gen_fstorex, MO_TEUL) -TRANS(fstx_d, gen_fstorex, MO_TEUQ) -TRANS(fldgt_s, gen_fload_gt, MO_TEUL) -TRANS(fldgt_d, gen_fload_gt, MO_TEUQ) -TRANS(fldle_s, gen_fload_le, MO_TEUL) -TRANS(fldle_d, gen_fload_le, MO_TEUQ) -TRANS(fstgt_s, gen_fstore_gt, MO_TEUL) -TRANS(fstgt_d, gen_fstore_gt, MO_TEUQ) -TRANS(fstle_s, gen_fstore_le, MO_TEUL) -TRANS(fstle_d, gen_fstore_le, MO_TEUQ) diff --git a/target/loongarch/insn_trans/trans_fmov.c.inc b/target/loongarch/insn_trans/trans_fmov.c.inc deleted file mode 100644 index 24753d4568..0000000000 --- a/target/loongarch/insn_trans/trans_fmov.c.inc +++ /dev/null @@ -1,157 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -static const uint32_t fcsr_mask[4] = { - UINT32_MAX, FCSR0_M1, FCSR0_M2, FCSR0_M3 -}; - -static bool trans_fsel(DisasContext *ctx, arg_fsel *a) -{ - TCGv zero = tcg_constant_tl(0); - TCGv cond = tcg_temp_new(); - - tcg_gen_ld8u_tl(cond, cpu_env, offsetof(CPULoongArchState, cf[a->ca])); - tcg_gen_movcond_tl(TCG_COND_EQ, cpu_fpr[a->fd], cond, zero, - cpu_fpr[a->fj], cpu_fpr[a->fk]); - tcg_temp_free(cond); - - return true; -} - -static bool gen_f2f(DisasContext *ctx, arg_ff *a, - void (*func)(TCGv, TCGv), bool nanbox) -{ - TCGv dest = cpu_fpr[a->fd]; - TCGv src = cpu_fpr[a->fj]; - - func(dest, src); - if (nanbox) { - gen_nanbox_s(cpu_fpr[a->fd], cpu_fpr[a->fd]); - } - - return true; -} - -static bool gen_r2f(DisasContext *ctx, arg_fr *a, - void (*func)(TCGv, TCGv)) -{ - TCGv src = gpr_src(ctx, a->rj, EXT_NONE); - - func(cpu_fpr[a->fd], src); - return true; -} - -static bool gen_f2r(DisasContext *ctx, arg_rf *a, - void (*func)(TCGv, TCGv)) -{ - TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); - - func(dest, cpu_fpr[a->fj]); - gen_set_gpr(a->rd, dest, EXT_NONE); - - return true; -} - -static bool trans_movgr2fcsr(DisasContext *ctx, arg_movgr2fcsr *a) -{ - uint32_t mask = fcsr_mask[a->fcsrd]; - TCGv Rj = gpr_src(ctx, a->rj, EXT_NONE); - - if (mask == UINT32_MAX) { - tcg_gen_extrl_i64_i32(cpu_fcsr0, Rj); - } else { - TCGv_i32 temp = tcg_temp_new_i32(); - - tcg_gen_extrl_i64_i32(temp, Rj); - tcg_gen_andi_i32(temp, temp, mask); - tcg_gen_andi_i32(cpu_fcsr0, cpu_fcsr0, ~mask); - tcg_gen_or_i32(cpu_fcsr0, cpu_fcsr0, temp); - tcg_temp_free_i32(temp); - - /* - * Install the new rounding mode to fpu_status, if changed. - * Note that FCSR3 is exactly the rounding mode field. - */ - if (mask != FCSR0_M3) { - return true; - } - } - gen_helper_set_rounding_mode(cpu_env, cpu_fcsr0); - - return true; -} - -static bool trans_movfcsr2gr(DisasContext *ctx, arg_movfcsr2gr *a) -{ - TCGv_i32 temp = tcg_temp_new_i32(); - TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); - - tcg_gen_andi_i32(temp, cpu_fcsr0, fcsr_mask[a->fcsrs]); - tcg_gen_ext_i32_i64(dest, temp); - gen_set_gpr(a->rd, dest, EXT_NONE); - tcg_temp_free_i32(temp); - - return true; -} - -static void gen_movgr2fr_w(TCGv dest, TCGv src) -{ - tcg_gen_deposit_i64(dest, dest, src, 0, 32); -} - -static void gen_movgr2frh_w(TCGv dest, TCGv src) -{ - tcg_gen_deposit_i64(dest, dest, src, 32, 32); -} - -static void gen_movfrh2gr_s(TCGv dest, TCGv src) -{ - tcg_gen_sextract_tl(dest, src, 32, 32); -} - -static bool trans_movfr2cf(DisasContext *ctx, arg_movfr2cf *a) -{ - TCGv t0 = tcg_temp_new(); - - tcg_gen_andi_tl(t0, cpu_fpr[a->fj], 0x1); - tcg_gen_st8_tl(t0, cpu_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); - tcg_temp_free(t0); - - return true; -} - -static bool trans_movcf2fr(DisasContext *ctx, arg_movcf2fr *a) -{ - tcg_gen_ld8u_tl(cpu_fpr[a->fd], cpu_env, - offsetof(CPULoongArchState, cf[a->cj & 0x7])); - return true; -} - -static bool trans_movgr2cf(DisasContext *ctx, arg_movgr2cf *a) -{ - TCGv t0 = tcg_temp_new(); - - tcg_gen_andi_tl(t0, gpr_src(ctx, a->rj, EXT_NONE), 0x1); - tcg_gen_st8_tl(t0, cpu_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); - tcg_temp_free(t0); - - return true; -} - -static bool trans_movcf2gr(DisasContext *ctx, arg_movcf2gr *a) -{ - tcg_gen_ld8u_tl(gpr_dst(ctx, a->rd, EXT_NONE), cpu_env, - offsetof(CPULoongArchState, cf[a->cj & 0x7])); - return true; -} - -TRANS(fmov_s, gen_f2f, tcg_gen_mov_tl, true) -TRANS(fmov_d, gen_f2f, tcg_gen_mov_tl, false) -TRANS(movgr2fr_w, gen_r2f, gen_movgr2fr_w) -TRANS(movgr2fr_d, gen_r2f, tcg_gen_mov_tl) -TRANS(movgr2frh_w, gen_r2f, gen_movgr2frh_w) -TRANS(movfr2gr_s, gen_f2r, tcg_gen_ext32s_tl) -TRANS(movfr2gr_d, gen_f2r, tcg_gen_mov_tl) -TRANS(movfrh2gr_s, gen_f2r, gen_movfrh2gr_s) diff --git a/target/loongarch/insns.decode b/target/loongarch/insns.decode index 3fdc6e148c..62f58cc541 100644 --- a/target/loongarch/insns.decode +++ b/target/loongarch/insns.decode @@ -24,6 +24,7 @@ &rrr rd rj rk &rr_i rd rj imm &hint_r_i hint rj imm +&hint_rr hint rj rk &rrr_sa rd rj rk sa &rr_ms_ls rd rj ms ls &ff fd fj @@ -67,7 +68,9 @@ @rr_ui12 .... ...... imm:12 rj:5 rd:5 &rr_i @rr_i14s2 .... .... .............. rj:5 rd:5 &rr_i imm=%i14s2 @rr_i16 .... .. imm:s16 rj:5 rd:5 &rr_i +@rr_i16s2 .... .. ................ rj:5 rd:5 &rr_i imm=%offs16 @hint_r_i12 .... ...... imm:s12 rj:5 hint:5 &hint_r_i +@hint_rr .... ........ ..... rk:5 rj:5 hint:5 &hint_rr @rrr_sa2p1 .... ........ ... .. rk:5 rj:5 rd:5 &rrr_sa sa=%sa2p1 @rrr_sa2 .... ........ ... sa:2 rk:5 rj:5 rd:5 &rrr_sa @rrr_sa3 .... ........ .. sa:3 rk:5 rj:5 rd:5 &rrr_sa @@ -227,6 +230,7 @@ ldx_bu 0011 10000010 00000 ..... ..... ..... @rrr ldx_hu 0011 10000010 01000 ..... ..... ..... @rrr ldx_wu 0011 10000010 10000 ..... ..... ..... @rrr preld 0010 101011 ............ ..... ..... @hint_r_i12 +preldx 0011 10000010 11000 ..... ..... ..... @hint_rr dbar 0011 10000111 00100 ............... @i15 ibar 0011 10000111 00101 ............... @i15 ldptr_w 0010 0100 .............. ..... ..... @rr_i14s2 @@ -444,7 +448,7 @@ beqz 0100 00 ................ ..... ..... @r_offs21 bnez 0100 01 ................ ..... ..... @r_offs21 bceqz 0100 10 ................ 00 ... ..... @c_offs21 bcnez 0100 10 ................ 01 ... ..... @c_offs21 -jirl 0100 11 ................ ..... ..... @rr_offs16 +jirl 0100 11 ................ ..... ..... @rr_i16s2 b 0101 00 .......................... @offs26 bl 0101 01 .......................... @offs26 beq 0101 10 ................ ..... ..... @rr_offs16 @@ -484,3 +488,1596 @@ ldpte 0000 01100100 01 ........ ..... 00000 @j_i ertn 0000 01100100 10000 01110 00000 00000 @empty idle 0000 01100100 10001 ............... @i15 dbcl 0000 00000010 10101 ............... @i15 + +# +# LSX Fields +# + +%i9s3 10:s9 !function=shl_3 +%i10s2 10:s10 !function=shl_2 +%i11s1 10:s11 !function=shl_1 +%i8s3 10:s8 !function=shl_3 +%i8s2 10:s8 !function=shl_2 +%i8s1 10:s8 !function=shl_1 + +# +# LSX Argument sets +# + +&vv vd vj +&cv cd vj +&vvv vd vj vk +&vv_i vd vj imm +&vvvv vd vj vk va +&vvv_fcond vd vj vk fcond +&vr_i vd rj imm +&rv_i rd vj imm +&vr vd rj +&vvr vd vj rk +&vrr vd rj rk +&vr_ii vd rj imm imm2 +&v_i vd imm + +# +# LSX Formats +# +@vv .... ........ ..... ..... vj:5 vd:5 &vv +@cv .... ........ ..... ..... vj:5 .. cd:3 &cv +@vvv .... ........ ..... vk:5 vj:5 vd:5 &vvv +@vv_ui1 .... ........ ..... .... imm:1 vj:5 vd:5 &vv_i +@vv_ui2 .... ........ ..... ... imm:2 vj:5 vd:5 &vv_i +@vv_ui3 .... ........ ..... .. imm:3 vj:5 vd:5 &vv_i +@vv_ui4 .... ........ ..... . imm:4 vj:5 vd:5 &vv_i +@vv_ui5 .... ........ ..... imm:5 vj:5 vd:5 &vv_i +@vv_ui6 .... ........ .... imm:6 vj:5 vd:5 &vv_i +@vv_ui7 .... ........ ... imm:7 vj:5 vd:5 &vv_i +@vv_ui8 .... ........ .. imm:8 vj:5 vd:5 &vv_i +@vv_i5 .... ........ ..... imm:s5 vj:5 vd:5 &vv_i +@vvvv .... ........ va:5 vk:5 vj:5 vd:5 &vvvv +@vvv_fcond .... ........ fcond:5 vk:5 vj:5 vd:5 &vvv_fcond +@vr_ui4 .... ........ ..... . imm:4 rj:5 vd:5 &vr_i +@vr_ui3 .... ........ ..... .. imm:3 rj:5 vd:5 &vr_i +@vr_ui2 .... ........ ..... ... imm:2 rj:5 vd:5 &vr_i +@vr_ui1 .... ........ ..... .... imm:1 rj:5 vd:5 &vr_i +@rv_ui4 .... ........ ..... . imm:4 vj:5 rd:5 &rv_i +@rv_ui3 .... ........ ..... .. imm:3 vj:5 rd:5 &rv_i +@rv_ui2 .... ........ ..... ... imm:2 vj:5 rd:5 &rv_i +@rv_ui1 .... ........ ..... .... imm:1 vj:5 rd:5 &rv_i +@vr .... ........ ..... ..... rj:5 vd:5 &vr +@vvr .... ........ ..... rk:5 vj:5 vd:5 &vvr +@vr_i9 .... ........ . ......... rj:5 vd:5 &vr_i imm=%i9s3 +@vr_i10 .... ........ .......... rj:5 vd:5 &vr_i imm=%i10s2 +@vr_i11 .... ....... ........... rj:5 vd:5 &vr_i imm=%i11s1 +@vr_i12 .... ...... imm:s12 rj:5 vd:5 &vr_i +@vr_i8i1 .... ........ . imm2:1 ........ rj:5 vd:5 &vr_ii imm=%i8s3 +@vr_i8i2 .... ........ imm2:2 ........ rj:5 vd:5 &vr_ii imm=%i8s2 +@vr_i8i3 .... ....... imm2:3 ........ rj:5 vd:5 &vr_ii imm=%i8s1 +@vr_i8i4 .... ...... imm2:4 imm:s8 rj:5 vd:5 &vr_ii +@vr_i8i2x .... ........ imm2:2 ........ rj:5 vd:5 &vr_ii imm=%i8s3 +@vr_i8i3x .... ....... imm2:3 ........ rj:5 vd:5 &vr_ii imm=%i8s2 +@vr_i8i4x .... ...... imm2:4 ........ rj:5 vd:5 &vr_ii imm=%i8s1 +@vr_i8i5x .... ..... imm2:5 imm:s8 rj:5 vd:5 &vr_ii +@vrr .... ........ ..... rk:5 rj:5 vd:5 &vrr +@v_i13 .... ........ .. imm:13 vd:5 &v_i + +vadd_b 0111 00000000 10100 ..... ..... ..... @vvv +vadd_h 0111 00000000 10101 ..... ..... ..... @vvv +vadd_w 0111 00000000 10110 ..... ..... ..... @vvv +vadd_d 0111 00000000 10111 ..... ..... ..... @vvv +vadd_q 0111 00010010 11010 ..... ..... ..... @vvv +vsub_b 0111 00000000 11000 ..... ..... ..... @vvv +vsub_h 0111 00000000 11001 ..... ..... ..... @vvv +vsub_w 0111 00000000 11010 ..... ..... ..... @vvv +vsub_d 0111 00000000 11011 ..... ..... ..... @vvv +vsub_q 0111 00010010 11011 ..... ..... ..... @vvv + +vaddi_bu 0111 00101000 10100 ..... ..... ..... @vv_ui5 +vaddi_hu 0111 00101000 10101 ..... ..... ..... @vv_ui5 +vaddi_wu 0111 00101000 10110 ..... ..... ..... @vv_ui5 +vaddi_du 0111 00101000 10111 ..... ..... ..... @vv_ui5 +vsubi_bu 0111 00101000 11000 ..... ..... ..... @vv_ui5 +vsubi_hu 0111 00101000 11001 ..... ..... ..... @vv_ui5 +vsubi_wu 0111 00101000 11010 ..... ..... ..... @vv_ui5 +vsubi_du 0111 00101000 11011 ..... ..... ..... @vv_ui5 + +vneg_b 0111 00101001 11000 01100 ..... ..... @vv +vneg_h 0111 00101001 11000 01101 ..... ..... @vv +vneg_w 0111 00101001 11000 01110 ..... ..... @vv +vneg_d 0111 00101001 11000 01111 ..... ..... @vv + +vsadd_b 0111 00000100 01100 ..... ..... ..... @vvv +vsadd_h 0111 00000100 01101 ..... ..... ..... @vvv +vsadd_w 0111 00000100 01110 ..... ..... ..... @vvv +vsadd_d 0111 00000100 01111 ..... ..... ..... @vvv +vsadd_bu 0111 00000100 10100 ..... ..... ..... @vvv +vsadd_hu 0111 00000100 10101 ..... ..... ..... @vvv +vsadd_wu 0111 00000100 10110 ..... ..... ..... @vvv +vsadd_du 0111 00000100 10111 ..... ..... ..... @vvv +vssub_b 0111 00000100 10000 ..... ..... ..... @vvv +vssub_h 0111 00000100 10001 ..... ..... ..... @vvv +vssub_w 0111 00000100 10010 ..... ..... ..... @vvv +vssub_d 0111 00000100 10011 ..... ..... ..... @vvv +vssub_bu 0111 00000100 11000 ..... ..... ..... @vvv +vssub_hu 0111 00000100 11001 ..... ..... ..... @vvv +vssub_wu 0111 00000100 11010 ..... ..... ..... @vvv +vssub_du 0111 00000100 11011 ..... ..... ..... @vvv + +vhaddw_h_b 0111 00000101 01000 ..... ..... ..... @vvv +vhaddw_w_h 0111 00000101 01001 ..... ..... ..... @vvv +vhaddw_d_w 0111 00000101 01010 ..... ..... ..... @vvv +vhaddw_q_d 0111 00000101 01011 ..... ..... ..... @vvv +vhaddw_hu_bu 0111 00000101 10000 ..... ..... ..... @vvv +vhaddw_wu_hu 0111 00000101 10001 ..... ..... ..... @vvv +vhaddw_du_wu 0111 00000101 10010 ..... ..... ..... @vvv +vhaddw_qu_du 0111 00000101 10011 ..... ..... ..... @vvv +vhsubw_h_b 0111 00000101 01100 ..... ..... ..... @vvv +vhsubw_w_h 0111 00000101 01101 ..... ..... ..... @vvv +vhsubw_d_w 0111 00000101 01110 ..... ..... ..... @vvv +vhsubw_q_d 0111 00000101 01111 ..... ..... ..... @vvv +vhsubw_hu_bu 0111 00000101 10100 ..... ..... ..... @vvv +vhsubw_wu_hu 0111 00000101 10101 ..... ..... ..... @vvv +vhsubw_du_wu 0111 00000101 10110 ..... ..... ..... @vvv +vhsubw_qu_du 0111 00000101 10111 ..... ..... ..... @vvv + +vaddwev_h_b 0111 00000001 11100 ..... ..... ..... @vvv +vaddwev_w_h 0111 00000001 11101 ..... ..... ..... @vvv +vaddwev_d_w 0111 00000001 11110 ..... ..... ..... @vvv +vaddwev_q_d 0111 00000001 11111 ..... ..... ..... @vvv +vaddwod_h_b 0111 00000010 00100 ..... ..... ..... @vvv +vaddwod_w_h 0111 00000010 00101 ..... ..... ..... @vvv +vaddwod_d_w 0111 00000010 00110 ..... ..... ..... @vvv +vaddwod_q_d 0111 00000010 00111 ..... ..... ..... @vvv +vsubwev_h_b 0111 00000010 00000 ..... ..... ..... @vvv +vsubwev_w_h 0111 00000010 00001 ..... ..... ..... @vvv +vsubwev_d_w 0111 00000010 00010 ..... ..... ..... @vvv +vsubwev_q_d 0111 00000010 00011 ..... ..... ..... @vvv +vsubwod_h_b 0111 00000010 01000 ..... ..... ..... @vvv +vsubwod_w_h 0111 00000010 01001 ..... ..... ..... @vvv +vsubwod_d_w 0111 00000010 01010 ..... ..... ..... @vvv +vsubwod_q_d 0111 00000010 01011 ..... ..... ..... @vvv + +vaddwev_h_bu 0111 00000010 11100 ..... ..... ..... @vvv +vaddwev_w_hu 0111 00000010 11101 ..... ..... ..... @vvv +vaddwev_d_wu 0111 00000010 11110 ..... ..... ..... @vvv +vaddwev_q_du 0111 00000010 11111 ..... ..... ..... @vvv +vaddwod_h_bu 0111 00000011 00100 ..... ..... ..... @vvv +vaddwod_w_hu 0111 00000011 00101 ..... ..... ..... @vvv +vaddwod_d_wu 0111 00000011 00110 ..... ..... ..... @vvv +vaddwod_q_du 0111 00000011 00111 ..... ..... ..... @vvv +vsubwev_h_bu 0111 00000011 00000 ..... ..... ..... @vvv +vsubwev_w_hu 0111 00000011 00001 ..... ..... ..... @vvv +vsubwev_d_wu 0111 00000011 00010 ..... ..... ..... @vvv +vsubwev_q_du 0111 00000011 00011 ..... ..... ..... @vvv +vsubwod_h_bu 0111 00000011 01000 ..... ..... ..... @vvv +vsubwod_w_hu 0111 00000011 01001 ..... ..... ..... @vvv +vsubwod_d_wu 0111 00000011 01010 ..... ..... ..... @vvv +vsubwod_q_du 0111 00000011 01011 ..... ..... ..... @vvv + +vaddwev_h_bu_b 0111 00000011 11100 ..... ..... ..... @vvv +vaddwev_w_hu_h 0111 00000011 11101 ..... ..... ..... @vvv +vaddwev_d_wu_w 0111 00000011 11110 ..... ..... ..... @vvv +vaddwev_q_du_d 0111 00000011 11111 ..... ..... ..... @vvv +vaddwod_h_bu_b 0111 00000100 00000 ..... ..... ..... @vvv +vaddwod_w_hu_h 0111 00000100 00001 ..... ..... ..... @vvv +vaddwod_d_wu_w 0111 00000100 00010 ..... ..... ..... @vvv +vaddwod_q_du_d 0111 00000100 00011 ..... ..... ..... @vvv + +vavg_b 0111 00000110 01000 ..... ..... ..... @vvv +vavg_h 0111 00000110 01001 ..... ..... ..... @vvv +vavg_w 0111 00000110 01010 ..... ..... ..... @vvv +vavg_d 0111 00000110 01011 ..... ..... ..... @vvv +vavg_bu 0111 00000110 01100 ..... ..... ..... @vvv +vavg_hu 0111 00000110 01101 ..... ..... ..... @vvv +vavg_wu 0111 00000110 01110 ..... ..... ..... @vvv +vavg_du 0111 00000110 01111 ..... ..... ..... @vvv +vavgr_b 0111 00000110 10000 ..... ..... ..... @vvv +vavgr_h 0111 00000110 10001 ..... ..... ..... @vvv +vavgr_w 0111 00000110 10010 ..... ..... ..... @vvv +vavgr_d 0111 00000110 10011 ..... ..... ..... @vvv +vavgr_bu 0111 00000110 10100 ..... ..... ..... @vvv +vavgr_hu 0111 00000110 10101 ..... ..... ..... @vvv +vavgr_wu 0111 00000110 10110 ..... ..... ..... @vvv +vavgr_du 0111 00000110 10111 ..... ..... ..... @vvv + +vabsd_b 0111 00000110 00000 ..... ..... ..... @vvv +vabsd_h 0111 00000110 00001 ..... ..... ..... @vvv +vabsd_w 0111 00000110 00010 ..... ..... ..... @vvv +vabsd_d 0111 00000110 00011 ..... ..... ..... @vvv +vabsd_bu 0111 00000110 00100 ..... ..... ..... @vvv +vabsd_hu 0111 00000110 00101 ..... ..... ..... @vvv +vabsd_wu 0111 00000110 00110 ..... ..... ..... @vvv +vabsd_du 0111 00000110 00111 ..... ..... ..... @vvv + +vadda_b 0111 00000101 11000 ..... ..... ..... @vvv +vadda_h 0111 00000101 11001 ..... ..... ..... @vvv +vadda_w 0111 00000101 11010 ..... ..... ..... @vvv +vadda_d 0111 00000101 11011 ..... ..... ..... @vvv + +vmax_b 0111 00000111 00000 ..... ..... ..... @vvv +vmax_h 0111 00000111 00001 ..... ..... ..... @vvv +vmax_w 0111 00000111 00010 ..... ..... ..... @vvv +vmax_d 0111 00000111 00011 ..... ..... ..... @vvv +vmaxi_b 0111 00101001 00000 ..... ..... ..... @vv_i5 +vmaxi_h 0111 00101001 00001 ..... ..... ..... @vv_i5 +vmaxi_w 0111 00101001 00010 ..... ..... ..... @vv_i5 +vmaxi_d 0111 00101001 00011 ..... ..... ..... @vv_i5 +vmax_bu 0111 00000111 01000 ..... ..... ..... @vvv +vmax_hu 0111 00000111 01001 ..... ..... ..... @vvv +vmax_wu 0111 00000111 01010 ..... ..... ..... @vvv +vmax_du 0111 00000111 01011 ..... ..... ..... @vvv +vmaxi_bu 0111 00101001 01000 ..... ..... ..... @vv_ui5 +vmaxi_hu 0111 00101001 01001 ..... ..... ..... @vv_ui5 +vmaxi_wu 0111 00101001 01010 ..... ..... ..... @vv_ui5 +vmaxi_du 0111 00101001 01011 ..... ..... ..... @vv_ui5 + +vmin_b 0111 00000111 00100 ..... ..... ..... @vvv +vmin_h 0111 00000111 00101 ..... ..... ..... @vvv +vmin_w 0111 00000111 00110 ..... ..... ..... @vvv +vmin_d 0111 00000111 00111 ..... ..... ..... @vvv +vmini_b 0111 00101001 00100 ..... ..... ..... @vv_i5 +vmini_h 0111 00101001 00101 ..... ..... ..... @vv_i5 +vmini_w 0111 00101001 00110 ..... ..... ..... @vv_i5 +vmini_d 0111 00101001 00111 ..... ..... ..... @vv_i5 +vmin_bu 0111 00000111 01100 ..... ..... ..... @vvv +vmin_hu 0111 00000111 01101 ..... ..... ..... @vvv +vmin_wu 0111 00000111 01110 ..... ..... ..... @vvv +vmin_du 0111 00000111 01111 ..... ..... ..... @vvv +vmini_bu 0111 00101001 01100 ..... ..... ..... @vv_ui5 +vmini_hu 0111 00101001 01101 ..... ..... ..... @vv_ui5 +vmini_wu 0111 00101001 01110 ..... ..... ..... @vv_ui5 +vmini_du 0111 00101001 01111 ..... ..... ..... @vv_ui5 + +vmul_b 0111 00001000 01000 ..... ..... ..... @vvv +vmul_h 0111 00001000 01001 ..... ..... ..... @vvv +vmul_w 0111 00001000 01010 ..... ..... ..... @vvv +vmul_d 0111 00001000 01011 ..... ..... ..... @vvv +vmuh_b 0111 00001000 01100 ..... ..... ..... @vvv +vmuh_h 0111 00001000 01101 ..... ..... ..... @vvv +vmuh_w 0111 00001000 01110 ..... ..... ..... @vvv +vmuh_d 0111 00001000 01111 ..... ..... ..... @vvv +vmuh_bu 0111 00001000 10000 ..... ..... ..... @vvv +vmuh_hu 0111 00001000 10001 ..... ..... ..... @vvv +vmuh_wu 0111 00001000 10010 ..... ..... ..... @vvv +vmuh_du 0111 00001000 10011 ..... ..... ..... @vvv + +vmulwev_h_b 0111 00001001 00000 ..... ..... ..... @vvv +vmulwev_w_h 0111 00001001 00001 ..... ..... ..... @vvv +vmulwev_d_w 0111 00001001 00010 ..... ..... ..... @vvv +vmulwev_q_d 0111 00001001 00011 ..... ..... ..... @vvv +vmulwod_h_b 0111 00001001 00100 ..... ..... ..... @vvv +vmulwod_w_h 0111 00001001 00101 ..... ..... ..... @vvv +vmulwod_d_w 0111 00001001 00110 ..... ..... ..... @vvv +vmulwod_q_d 0111 00001001 00111 ..... ..... ..... @vvv +vmulwev_h_bu 0111 00001001 10000 ..... ..... ..... @vvv +vmulwev_w_hu 0111 00001001 10001 ..... ..... ..... @vvv +vmulwev_d_wu 0111 00001001 10010 ..... ..... ..... @vvv +vmulwev_q_du 0111 00001001 10011 ..... ..... ..... @vvv +vmulwod_h_bu 0111 00001001 10100 ..... ..... ..... @vvv +vmulwod_w_hu 0111 00001001 10101 ..... ..... ..... @vvv +vmulwod_d_wu 0111 00001001 10110 ..... ..... ..... @vvv +vmulwod_q_du 0111 00001001 10111 ..... ..... ..... @vvv +vmulwev_h_bu_b 0111 00001010 00000 ..... ..... ..... @vvv +vmulwev_w_hu_h 0111 00001010 00001 ..... ..... ..... @vvv +vmulwev_d_wu_w 0111 00001010 00010 ..... ..... ..... @vvv +vmulwev_q_du_d 0111 00001010 00011 ..... ..... ..... @vvv +vmulwod_h_bu_b 0111 00001010 00100 ..... ..... ..... @vvv +vmulwod_w_hu_h 0111 00001010 00101 ..... ..... ..... @vvv +vmulwod_d_wu_w 0111 00001010 00110 ..... ..... ..... @vvv +vmulwod_q_du_d 0111 00001010 00111 ..... ..... ..... @vvv + +vmadd_b 0111 00001010 10000 ..... ..... ..... @vvv +vmadd_h 0111 00001010 10001 ..... ..... ..... @vvv +vmadd_w 0111 00001010 10010 ..... ..... ..... @vvv +vmadd_d 0111 00001010 10011 ..... ..... ..... @vvv +vmsub_b 0111 00001010 10100 ..... ..... ..... @vvv +vmsub_h 0111 00001010 10101 ..... ..... ..... @vvv +vmsub_w 0111 00001010 10110 ..... ..... ..... @vvv +vmsub_d 0111 00001010 10111 ..... ..... ..... @vvv + +vmaddwev_h_b 0111 00001010 11000 ..... ..... ..... @vvv +vmaddwev_w_h 0111 00001010 11001 ..... ..... ..... @vvv +vmaddwev_d_w 0111 00001010 11010 ..... ..... ..... @vvv +vmaddwev_q_d 0111 00001010 11011 ..... ..... ..... @vvv +vmaddwod_h_b 0111 00001010 11100 ..... ..... ..... @vvv +vmaddwod_w_h 0111 00001010 11101 ..... ..... ..... @vvv +vmaddwod_d_w 0111 00001010 11110 ..... ..... ..... @vvv +vmaddwod_q_d 0111 00001010 11111 ..... ..... ..... @vvv +vmaddwev_h_bu 0111 00001011 01000 ..... ..... ..... @vvv +vmaddwev_w_hu 0111 00001011 01001 ..... ..... ..... @vvv +vmaddwev_d_wu 0111 00001011 01010 ..... ..... ..... @vvv +vmaddwev_q_du 0111 00001011 01011 ..... ..... ..... @vvv +vmaddwod_h_bu 0111 00001011 01100 ..... ..... ..... @vvv +vmaddwod_w_hu 0111 00001011 01101 ..... ..... ..... @vvv +vmaddwod_d_wu 0111 00001011 01110 ..... ..... ..... @vvv +vmaddwod_q_du 0111 00001011 01111 ..... ..... ..... @vvv +vmaddwev_h_bu_b 0111 00001011 11000 ..... ..... ..... @vvv +vmaddwev_w_hu_h 0111 00001011 11001 ..... ..... ..... @vvv +vmaddwev_d_wu_w 0111 00001011 11010 ..... ..... ..... @vvv +vmaddwev_q_du_d 0111 00001011 11011 ..... ..... ..... @vvv +vmaddwod_h_bu_b 0111 00001011 11100 ..... ..... ..... @vvv +vmaddwod_w_hu_h 0111 00001011 11101 ..... ..... ..... @vvv +vmaddwod_d_wu_w 0111 00001011 11110 ..... ..... ..... @vvv +vmaddwod_q_du_d 0111 00001011 11111 ..... ..... ..... @vvv + +vdiv_b 0111 00001110 00000 ..... ..... ..... @vvv +vdiv_h 0111 00001110 00001 ..... ..... ..... @vvv +vdiv_w 0111 00001110 00010 ..... ..... ..... @vvv +vdiv_d 0111 00001110 00011 ..... ..... ..... @vvv +vdiv_bu 0111 00001110 01000 ..... ..... ..... @vvv +vdiv_hu 0111 00001110 01001 ..... ..... ..... @vvv +vdiv_wu 0111 00001110 01010 ..... ..... ..... @vvv +vdiv_du 0111 00001110 01011 ..... ..... ..... @vvv +vmod_b 0111 00001110 00100 ..... ..... ..... @vvv +vmod_h 0111 00001110 00101 ..... ..... ..... @vvv +vmod_w 0111 00001110 00110 ..... ..... ..... @vvv +vmod_d 0111 00001110 00111 ..... ..... ..... @vvv +vmod_bu 0111 00001110 01100 ..... ..... ..... @vvv +vmod_hu 0111 00001110 01101 ..... ..... ..... @vvv +vmod_wu 0111 00001110 01110 ..... ..... ..... @vvv +vmod_du 0111 00001110 01111 ..... ..... ..... @vvv + +vsat_b 0111 00110010 01000 01 ... ..... ..... @vv_ui3 +vsat_h 0111 00110010 01000 1 .... ..... ..... @vv_ui4 +vsat_w 0111 00110010 01001 ..... ..... ..... @vv_ui5 +vsat_d 0111 00110010 0101 ...... ..... ..... @vv_ui6 +vsat_bu 0111 00110010 10000 01 ... ..... ..... @vv_ui3 +vsat_hu 0111 00110010 10000 1 .... ..... ..... @vv_ui4 +vsat_wu 0111 00110010 10001 ..... ..... ..... @vv_ui5 +vsat_du 0111 00110010 1001 ...... ..... ..... @vv_ui6 + +vexth_h_b 0111 00101001 11101 11000 ..... ..... @vv +vexth_w_h 0111 00101001 11101 11001 ..... ..... @vv +vexth_d_w 0111 00101001 11101 11010 ..... ..... @vv +vexth_q_d 0111 00101001 11101 11011 ..... ..... @vv +vexth_hu_bu 0111 00101001 11101 11100 ..... ..... @vv +vexth_wu_hu 0111 00101001 11101 11101 ..... ..... @vv +vexth_du_wu 0111 00101001 11101 11110 ..... ..... @vv +vexth_qu_du 0111 00101001 11101 11111 ..... ..... @vv + +vsigncov_b 0111 00010010 11100 ..... ..... ..... @vvv +vsigncov_h 0111 00010010 11101 ..... ..... ..... @vvv +vsigncov_w 0111 00010010 11110 ..... ..... ..... @vvv +vsigncov_d 0111 00010010 11111 ..... ..... ..... @vvv + +vmskltz_b 0111 00101001 11000 10000 ..... ..... @vv +vmskltz_h 0111 00101001 11000 10001 ..... ..... @vv +vmskltz_w 0111 00101001 11000 10010 ..... ..... @vv +vmskltz_d 0111 00101001 11000 10011 ..... ..... @vv +vmskgez_b 0111 00101001 11000 10100 ..... ..... @vv +vmsknz_b 0111 00101001 11000 11000 ..... ..... @vv + +vldi 0111 00111110 00 ............. ..... @v_i13 + +vand_v 0111 00010010 01100 ..... ..... ..... @vvv +vor_v 0111 00010010 01101 ..... ..... ..... @vvv +vxor_v 0111 00010010 01110 ..... ..... ..... @vvv +vnor_v 0111 00010010 01111 ..... ..... ..... @vvv +vandn_v 0111 00010010 10000 ..... ..... ..... @vvv +vorn_v 0111 00010010 10001 ..... ..... ..... @vvv + +vandi_b 0111 00111101 00 ........ ..... ..... @vv_ui8 +vori_b 0111 00111101 01 ........ ..... ..... @vv_ui8 +vxori_b 0111 00111101 10 ........ ..... ..... @vv_ui8 +vnori_b 0111 00111101 11 ........ ..... ..... @vv_ui8 + +vsll_b 0111 00001110 10000 ..... ..... ..... @vvv +vsll_h 0111 00001110 10001 ..... ..... ..... @vvv +vsll_w 0111 00001110 10010 ..... ..... ..... @vvv +vsll_d 0111 00001110 10011 ..... ..... ..... @vvv +vslli_b 0111 00110010 11000 01 ... ..... ..... @vv_ui3 +vslli_h 0111 00110010 11000 1 .... ..... ..... @vv_ui4 +vslli_w 0111 00110010 11001 ..... ..... ..... @vv_ui5 +vslli_d 0111 00110010 1101 ...... ..... ..... @vv_ui6 + +vsrl_b 0111 00001110 10100 ..... ..... ..... @vvv +vsrl_h 0111 00001110 10101 ..... ..... ..... @vvv +vsrl_w 0111 00001110 10110 ..... ..... ..... @vvv +vsrl_d 0111 00001110 10111 ..... ..... ..... @vvv +vsrli_b 0111 00110011 00000 01 ... ..... ..... @vv_ui3 +vsrli_h 0111 00110011 00000 1 .... ..... ..... @vv_ui4 +vsrli_w 0111 00110011 00001 ..... ..... ..... @vv_ui5 +vsrli_d 0111 00110011 0001 ...... ..... ..... @vv_ui6 + +vsra_b 0111 00001110 11000 ..... ..... ..... @vvv +vsra_h 0111 00001110 11001 ..... ..... ..... @vvv +vsra_w 0111 00001110 11010 ..... ..... ..... @vvv +vsra_d 0111 00001110 11011 ..... ..... ..... @vvv +vsrai_b 0111 00110011 01000 01 ... ..... ..... @vv_ui3 +vsrai_h 0111 00110011 01000 1 .... ..... ..... @vv_ui4 +vsrai_w 0111 00110011 01001 ..... ..... ..... @vv_ui5 +vsrai_d 0111 00110011 0101 ...... ..... ..... @vv_ui6 + +vrotr_b 0111 00001110 11100 ..... ..... ..... @vvv +vrotr_h 0111 00001110 11101 ..... ..... ..... @vvv +vrotr_w 0111 00001110 11110 ..... ..... ..... @vvv +vrotr_d 0111 00001110 11111 ..... ..... ..... @vvv +vrotri_b 0111 00101010 00000 01 ... ..... ..... @vv_ui3 +vrotri_h 0111 00101010 00000 1 .... ..... ..... @vv_ui4 +vrotri_w 0111 00101010 00001 ..... ..... ..... @vv_ui5 +vrotri_d 0111 00101010 0001 ...... ..... ..... @vv_ui6 + +vsllwil_h_b 0111 00110000 10000 01 ... ..... ..... @vv_ui3 +vsllwil_w_h 0111 00110000 10000 1 .... ..... ..... @vv_ui4 +vsllwil_d_w 0111 00110000 10001 ..... ..... ..... @vv_ui5 +vextl_q_d 0111 00110000 10010 00000 ..... ..... @vv +vsllwil_hu_bu 0111 00110000 11000 01 ... ..... ..... @vv_ui3 +vsllwil_wu_hu 0111 00110000 11000 1 .... ..... ..... @vv_ui4 +vsllwil_du_wu 0111 00110000 11001 ..... ..... ..... @vv_ui5 +vextl_qu_du 0111 00110000 11010 00000 ..... ..... @vv + +vsrlr_b 0111 00001111 00000 ..... ..... ..... @vvv +vsrlr_h 0111 00001111 00001 ..... ..... ..... @vvv +vsrlr_w 0111 00001111 00010 ..... ..... ..... @vvv +vsrlr_d 0111 00001111 00011 ..... ..... ..... @vvv +vsrlri_b 0111 00101010 01000 01 ... ..... ..... @vv_ui3 +vsrlri_h 0111 00101010 01000 1 .... ..... ..... @vv_ui4 +vsrlri_w 0111 00101010 01001 ..... ..... ..... @vv_ui5 +vsrlri_d 0111 00101010 0101 ...... ..... ..... @vv_ui6 + +vsrar_b 0111 00001111 00100 ..... ..... ..... @vvv +vsrar_h 0111 00001111 00101 ..... ..... ..... @vvv +vsrar_w 0111 00001111 00110 ..... ..... ..... @vvv +vsrar_d 0111 00001111 00111 ..... ..... ..... @vvv +vsrari_b 0111 00101010 10000 01 ... ..... ..... @vv_ui3 +vsrari_h 0111 00101010 10000 1 .... ..... ..... @vv_ui4 +vsrari_w 0111 00101010 10001 ..... ..... ..... @vv_ui5 +vsrari_d 0111 00101010 1001 ...... ..... ..... @vv_ui6 + +vsrln_b_h 0111 00001111 01001 ..... ..... ..... @vvv +vsrln_h_w 0111 00001111 01010 ..... ..... ..... @vvv +vsrln_w_d 0111 00001111 01011 ..... ..... ..... @vvv +vsran_b_h 0111 00001111 01101 ..... ..... ..... @vvv +vsran_h_w 0111 00001111 01110 ..... ..... ..... @vvv +vsran_w_d 0111 00001111 01111 ..... ..... ..... @vvv + +vsrlni_b_h 0111 00110100 00000 1 .... ..... ..... @vv_ui4 +vsrlni_h_w 0111 00110100 00001 ..... ..... ..... @vv_ui5 +vsrlni_w_d 0111 00110100 0001 ...... ..... ..... @vv_ui6 +vsrlni_d_q 0111 00110100 001 ....... ..... ..... @vv_ui7 +vsrani_b_h 0111 00110101 10000 1 .... ..... ..... @vv_ui4 +vsrani_h_w 0111 00110101 10001 ..... ..... ..... @vv_ui5 +vsrani_w_d 0111 00110101 1001 ...... ..... ..... @vv_ui6 +vsrani_d_q 0111 00110101 101 ....... ..... ..... @vv_ui7 + +vsrlrn_b_h 0111 00001111 10001 ..... ..... ..... @vvv +vsrlrn_h_w 0111 00001111 10010 ..... ..... ..... @vvv +vsrlrn_w_d 0111 00001111 10011 ..... ..... ..... @vvv +vsrarn_b_h 0111 00001111 10101 ..... ..... ..... @vvv +vsrarn_h_w 0111 00001111 10110 ..... ..... ..... @vvv +vsrarn_w_d 0111 00001111 10111 ..... ..... ..... @vvv + +vsrlrni_b_h 0111 00110100 01000 1 .... ..... ..... @vv_ui4 +vsrlrni_h_w 0111 00110100 01001 ..... ..... ..... @vv_ui5 +vsrlrni_w_d 0111 00110100 0101 ...... ..... ..... @vv_ui6 +vsrlrni_d_q 0111 00110100 011 ....... ..... ..... @vv_ui7 +vsrarni_b_h 0111 00110101 11000 1 .... ..... ..... @vv_ui4 +vsrarni_h_w 0111 00110101 11001 ..... ..... ..... @vv_ui5 +vsrarni_w_d 0111 00110101 1101 ...... ..... ..... @vv_ui6 +vsrarni_d_q 0111 00110101 111 ....... ..... ..... @vv_ui7 + +vssrln_b_h 0111 00001111 11001 ..... ..... ..... @vvv +vssrln_h_w 0111 00001111 11010 ..... ..... ..... @vvv +vssrln_w_d 0111 00001111 11011 ..... ..... ..... @vvv +vssran_b_h 0111 00001111 11101 ..... ..... ..... @vvv +vssran_h_w 0111 00001111 11110 ..... ..... ..... @vvv +vssran_w_d 0111 00001111 11111 ..... ..... ..... @vvv +vssrln_bu_h 0111 00010000 01001 ..... ..... ..... @vvv +vssrln_hu_w 0111 00010000 01010 ..... ..... ..... @vvv +vssrln_wu_d 0111 00010000 01011 ..... ..... ..... @vvv +vssran_bu_h 0111 00010000 01101 ..... ..... ..... @vvv +vssran_hu_w 0111 00010000 01110 ..... ..... ..... @vvv +vssran_wu_d 0111 00010000 01111 ..... ..... ..... @vvv + +vssrlni_b_h 0111 00110100 10000 1 .... ..... ..... @vv_ui4 +vssrlni_h_w 0111 00110100 10001 ..... ..... ..... @vv_ui5 +vssrlni_w_d 0111 00110100 1001 ...... ..... ..... @vv_ui6 +vssrlni_d_q 0111 00110100 101 ....... ..... ..... @vv_ui7 +vssrani_b_h 0111 00110110 00000 1 .... ..... ..... @vv_ui4 +vssrani_h_w 0111 00110110 00001 ..... ..... ..... @vv_ui5 +vssrani_w_d 0111 00110110 0001 ...... ..... ..... @vv_ui6 +vssrani_d_q 0111 00110110 001 ....... ..... ..... @vv_ui7 +vssrlni_bu_h 0111 00110100 11000 1 .... ..... ..... @vv_ui4 +vssrlni_hu_w 0111 00110100 11001 ..... ..... ..... @vv_ui5 +vssrlni_wu_d 0111 00110100 1101 ...... ..... ..... @vv_ui6 +vssrlni_du_q 0111 00110100 111 ....... ..... ..... @vv_ui7 +vssrani_bu_h 0111 00110110 01000 1 .... ..... ..... @vv_ui4 +vssrani_hu_w 0111 00110110 01001 ..... ..... ..... @vv_ui5 +vssrani_wu_d 0111 00110110 0101 ...... ..... ..... @vv_ui6 +vssrani_du_q 0111 00110110 011 ....... ..... ..... @vv_ui7 + +vssrlrn_b_h 0111 00010000 00001 ..... ..... ..... @vvv +vssrlrn_h_w 0111 00010000 00010 ..... ..... ..... @vvv +vssrlrn_w_d 0111 00010000 00011 ..... ..... ..... @vvv +vssrarn_b_h 0111 00010000 00101 ..... ..... ..... @vvv +vssrarn_h_w 0111 00010000 00110 ..... ..... ..... @vvv +vssrarn_w_d 0111 00010000 00111 ..... ..... ..... @vvv +vssrlrn_bu_h 0111 00010000 10001 ..... ..... ..... @vvv +vssrlrn_hu_w 0111 00010000 10010 ..... ..... ..... @vvv +vssrlrn_wu_d 0111 00010000 10011 ..... ..... ..... @vvv +vssrarn_bu_h 0111 00010000 10101 ..... ..... ..... @vvv +vssrarn_hu_w 0111 00010000 10110 ..... ..... ..... @vvv +vssrarn_wu_d 0111 00010000 10111 ..... ..... ..... @vvv + +vssrlrni_b_h 0111 00110101 00000 1 .... ..... ..... @vv_ui4 +vssrlrni_h_w 0111 00110101 00001 ..... ..... ..... @vv_ui5 +vssrlrni_w_d 0111 00110101 0001 ...... ..... ..... @vv_ui6 +vssrlrni_d_q 0111 00110101 001 ....... ..... ..... @vv_ui7 +vssrarni_b_h 0111 00110110 10000 1 .... ..... ..... @vv_ui4 +vssrarni_h_w 0111 00110110 10001 ..... ..... ..... @vv_ui5 +vssrarni_w_d 0111 00110110 1001 ...... ..... ..... @vv_ui6 +vssrarni_d_q 0111 00110110 101 ....... ..... ..... @vv_ui7 +vssrlrni_bu_h 0111 00110101 01000 1 .... ..... ..... @vv_ui4 +vssrlrni_hu_w 0111 00110101 01001 ..... ..... ..... @vv_ui5 +vssrlrni_wu_d 0111 00110101 0101 ...... ..... ..... @vv_ui6 +vssrlrni_du_q 0111 00110101 011 ....... ..... ..... @vv_ui7 +vssrarni_bu_h 0111 00110110 11000 1 .... ..... ..... @vv_ui4 +vssrarni_hu_w 0111 00110110 11001 ..... ..... ..... @vv_ui5 +vssrarni_wu_d 0111 00110110 1101 ...... ..... ..... @vv_ui6 +vssrarni_du_q 0111 00110110 111 ....... ..... ..... @vv_ui7 + +vclo_b 0111 00101001 11000 00000 ..... ..... @vv +vclo_h 0111 00101001 11000 00001 ..... ..... @vv +vclo_w 0111 00101001 11000 00010 ..... ..... @vv +vclo_d 0111 00101001 11000 00011 ..... ..... @vv +vclz_b 0111 00101001 11000 00100 ..... ..... @vv +vclz_h 0111 00101001 11000 00101 ..... ..... @vv +vclz_w 0111 00101001 11000 00110 ..... ..... @vv +vclz_d 0111 00101001 11000 00111 ..... ..... @vv + +vpcnt_b 0111 00101001 11000 01000 ..... ..... @vv +vpcnt_h 0111 00101001 11000 01001 ..... ..... @vv +vpcnt_w 0111 00101001 11000 01010 ..... ..... @vv +vpcnt_d 0111 00101001 11000 01011 ..... ..... @vv + +vbitclr_b 0111 00010000 11000 ..... ..... ..... @vvv +vbitclr_h 0111 00010000 11001 ..... ..... ..... @vvv +vbitclr_w 0111 00010000 11010 ..... ..... ..... @vvv +vbitclr_d 0111 00010000 11011 ..... ..... ..... @vvv +vbitclri_b 0111 00110001 00000 01 ... ..... ..... @vv_ui3 +vbitclri_h 0111 00110001 00000 1 .... ..... ..... @vv_ui4 +vbitclri_w 0111 00110001 00001 ..... ..... ..... @vv_ui5 +vbitclri_d 0111 00110001 0001 ...... ..... ..... @vv_ui6 +vbitset_b 0111 00010000 11100 ..... ..... ..... @vvv +vbitset_h 0111 00010000 11101 ..... ..... ..... @vvv +vbitset_w 0111 00010000 11110 ..... ..... ..... @vvv +vbitset_d 0111 00010000 11111 ..... ..... ..... @vvv +vbitseti_b 0111 00110001 01000 01 ... ..... ..... @vv_ui3 +vbitseti_h 0111 00110001 01000 1 .... ..... ..... @vv_ui4 +vbitseti_w 0111 00110001 01001 ..... ..... ..... @vv_ui5 +vbitseti_d 0111 00110001 0101 ...... ..... ..... @vv_ui6 +vbitrev_b 0111 00010001 00000 ..... ..... ..... @vvv +vbitrev_h 0111 00010001 00001 ..... ..... ..... @vvv +vbitrev_w 0111 00010001 00010 ..... ..... ..... @vvv +vbitrev_d 0111 00010001 00011 ..... ..... ..... @vvv +vbitrevi_b 0111 00110001 10000 01 ... ..... ..... @vv_ui3 +vbitrevi_h 0111 00110001 10000 1 .... ..... ..... @vv_ui4 +vbitrevi_w 0111 00110001 10001 ..... ..... ..... @vv_ui5 +vbitrevi_d 0111 00110001 1001 ...... ..... ..... @vv_ui6 + +vfrstp_b 0111 00010010 10110 ..... ..... ..... @vvv +vfrstp_h 0111 00010010 10111 ..... ..... ..... @vvv +vfrstpi_b 0111 00101001 10100 ..... ..... ..... @vv_ui5 +vfrstpi_h 0111 00101001 10101 ..... ..... ..... @vv_ui5 + +vfadd_s 0111 00010011 00001 ..... ..... ..... @vvv +vfadd_d 0111 00010011 00010 ..... ..... ..... @vvv +vfsub_s 0111 00010011 00101 ..... ..... ..... @vvv +vfsub_d 0111 00010011 00110 ..... ..... ..... @vvv +vfmul_s 0111 00010011 10001 ..... ..... ..... @vvv +vfmul_d 0111 00010011 10010 ..... ..... ..... @vvv +vfdiv_s 0111 00010011 10101 ..... ..... ..... @vvv +vfdiv_d 0111 00010011 10110 ..... ..... ..... @vvv + +vfmadd_s 0000 10010001 ..... ..... ..... ..... @vvvv +vfmadd_d 0000 10010010 ..... ..... ..... ..... @vvvv +vfmsub_s 0000 10010101 ..... ..... ..... ..... @vvvv +vfmsub_d 0000 10010110 ..... ..... ..... ..... @vvvv +vfnmadd_s 0000 10011001 ..... ..... ..... ..... @vvvv +vfnmadd_d 0000 10011010 ..... ..... ..... ..... @vvvv +vfnmsub_s 0000 10011101 ..... ..... ..... ..... @vvvv +vfnmsub_d 0000 10011110 ..... ..... ..... ..... @vvvv + +vfmax_s 0111 00010011 11001 ..... ..... ..... @vvv +vfmax_d 0111 00010011 11010 ..... ..... ..... @vvv +vfmin_s 0111 00010011 11101 ..... ..... ..... @vvv +vfmin_d 0111 00010011 11110 ..... ..... ..... @vvv + +vfmaxa_s 0111 00010100 00001 ..... ..... ..... @vvv +vfmaxa_d 0111 00010100 00010 ..... ..... ..... @vvv +vfmina_s 0111 00010100 00101 ..... ..... ..... @vvv +vfmina_d 0111 00010100 00110 ..... ..... ..... @vvv + +vflogb_s 0111 00101001 11001 10001 ..... ..... @vv +vflogb_d 0111 00101001 11001 10010 ..... ..... @vv + +vfclass_s 0111 00101001 11001 10101 ..... ..... @vv +vfclass_d 0111 00101001 11001 10110 ..... ..... @vv + +vfsqrt_s 0111 00101001 11001 11001 ..... ..... @vv +vfsqrt_d 0111 00101001 11001 11010 ..... ..... @vv +vfrecip_s 0111 00101001 11001 11101 ..... ..... @vv +vfrecip_d 0111 00101001 11001 11110 ..... ..... @vv +vfrsqrt_s 0111 00101001 11010 00001 ..... ..... @vv +vfrsqrt_d 0111 00101001 11010 00010 ..... ..... @vv + +vfcvtl_s_h 0111 00101001 11011 11010 ..... ..... @vv +vfcvth_s_h 0111 00101001 11011 11011 ..... ..... @vv +vfcvtl_d_s 0111 00101001 11011 11100 ..... ..... @vv +vfcvth_d_s 0111 00101001 11011 11101 ..... ..... @vv +vfcvt_h_s 0111 00010100 01100 ..... ..... ..... @vvv +vfcvt_s_d 0111 00010100 01101 ..... ..... ..... @vvv + +vfrint_s 0111 00101001 11010 01101 ..... ..... @vv +vfrint_d 0111 00101001 11010 01110 ..... ..... @vv +vfrintrm_s 0111 00101001 11010 10001 ..... ..... @vv +vfrintrm_d 0111 00101001 11010 10010 ..... ..... @vv +vfrintrp_s 0111 00101001 11010 10101 ..... ..... @vv +vfrintrp_d 0111 00101001 11010 10110 ..... ..... @vv +vfrintrz_s 0111 00101001 11010 11001 ..... ..... @vv +vfrintrz_d 0111 00101001 11010 11010 ..... ..... @vv +vfrintrne_s 0111 00101001 11010 11101 ..... ..... @vv +vfrintrne_d 0111 00101001 11010 11110 ..... ..... @vv + +vftint_w_s 0111 00101001 11100 01100 ..... ..... @vv +vftint_l_d 0111 00101001 11100 01101 ..... ..... @vv +vftintrm_w_s 0111 00101001 11100 01110 ..... ..... @vv +vftintrm_l_d 0111 00101001 11100 01111 ..... ..... @vv +vftintrp_w_s 0111 00101001 11100 10000 ..... ..... @vv +vftintrp_l_d 0111 00101001 11100 10001 ..... ..... @vv +vftintrz_w_s 0111 00101001 11100 10010 ..... ..... @vv +vftintrz_l_d 0111 00101001 11100 10011 ..... ..... @vv +vftintrne_w_s 0111 00101001 11100 10100 ..... ..... @vv +vftintrne_l_d 0111 00101001 11100 10101 ..... ..... @vv +vftint_wu_s 0111 00101001 11100 10110 ..... ..... @vv +vftint_lu_d 0111 00101001 11100 10111 ..... ..... @vv +vftintrz_wu_s 0111 00101001 11100 11100 ..... ..... @vv +vftintrz_lu_d 0111 00101001 11100 11101 ..... ..... @vv +vftint_w_d 0111 00010100 10011 ..... ..... ..... @vvv +vftintrm_w_d 0111 00010100 10100 ..... ..... ..... @vvv +vftintrp_w_d 0111 00010100 10101 ..... ..... ..... @vvv +vftintrz_w_d 0111 00010100 10110 ..... ..... ..... @vvv +vftintrne_w_d 0111 00010100 10111 ..... ..... ..... @vvv +vftintl_l_s 0111 00101001 11101 00000 ..... ..... @vv +vftinth_l_s 0111 00101001 11101 00001 ..... ..... @vv +vftintrml_l_s 0111 00101001 11101 00010 ..... ..... @vv +vftintrmh_l_s 0111 00101001 11101 00011 ..... ..... @vv +vftintrpl_l_s 0111 00101001 11101 00100 ..... ..... @vv +vftintrph_l_s 0111 00101001 11101 00101 ..... ..... @vv +vftintrzl_l_s 0111 00101001 11101 00110 ..... ..... @vv +vftintrzh_l_s 0111 00101001 11101 00111 ..... ..... @vv +vftintrnel_l_s 0111 00101001 11101 01000 ..... ..... @vv +vftintrneh_l_s 0111 00101001 11101 01001 ..... ..... @vv + +vffint_s_w 0111 00101001 11100 00000 ..... ..... @vv +vffint_s_wu 0111 00101001 11100 00001 ..... ..... @vv +vffint_d_l 0111 00101001 11100 00010 ..... ..... @vv +vffint_d_lu 0111 00101001 11100 00011 ..... ..... @vv +vffintl_d_w 0111 00101001 11100 00100 ..... ..... @vv +vffinth_d_w 0111 00101001 11100 00101 ..... ..... @vv +vffint_s_l 0111 00010100 10000 ..... ..... ..... @vvv + +vseq_b 0111 00000000 00000 ..... ..... ..... @vvv +vseq_h 0111 00000000 00001 ..... ..... ..... @vvv +vseq_w 0111 00000000 00010 ..... ..... ..... @vvv +vseq_d 0111 00000000 00011 ..... ..... ..... @vvv +vseqi_b 0111 00101000 00000 ..... ..... ..... @vv_i5 +vseqi_h 0111 00101000 00001 ..... ..... ..... @vv_i5 +vseqi_w 0111 00101000 00010 ..... ..... ..... @vv_i5 +vseqi_d 0111 00101000 00011 ..... ..... ..... @vv_i5 + +vsle_b 0111 00000000 00100 ..... ..... ..... @vvv +vsle_h 0111 00000000 00101 ..... ..... ..... @vvv +vsle_w 0111 00000000 00110 ..... ..... ..... @vvv +vsle_d 0111 00000000 00111 ..... ..... ..... @vvv +vslei_b 0111 00101000 00100 ..... ..... ..... @vv_i5 +vslei_h 0111 00101000 00101 ..... ..... ..... @vv_i5 +vslei_w 0111 00101000 00110 ..... ..... ..... @vv_i5 +vslei_d 0111 00101000 00111 ..... ..... ..... @vv_i5 +vsle_bu 0111 00000000 01000 ..... ..... ..... @vvv +vsle_hu 0111 00000000 01001 ..... ..... ..... @vvv +vsle_wu 0111 00000000 01010 ..... ..... ..... @vvv +vsle_du 0111 00000000 01011 ..... ..... ..... @vvv +vslei_bu 0111 00101000 01000 ..... ..... ..... @vv_ui5 +vslei_hu 0111 00101000 01001 ..... ..... ..... @vv_ui5 +vslei_wu 0111 00101000 01010 ..... ..... ..... @vv_ui5 +vslei_du 0111 00101000 01011 ..... ..... ..... @vv_ui5 + +vslt_b 0111 00000000 01100 ..... ..... ..... @vvv +vslt_h 0111 00000000 01101 ..... ..... ..... @vvv +vslt_w 0111 00000000 01110 ..... ..... ..... @vvv +vslt_d 0111 00000000 01111 ..... ..... ..... @vvv +vslti_b 0111 00101000 01100 ..... ..... ..... @vv_i5 +vslti_h 0111 00101000 01101 ..... ..... ..... @vv_i5 +vslti_w 0111 00101000 01110 ..... ..... ..... @vv_i5 +vslti_d 0111 00101000 01111 ..... ..... ..... @vv_i5 +vslt_bu 0111 00000000 10000 ..... ..... ..... @vvv +vslt_hu 0111 00000000 10001 ..... ..... ..... @vvv +vslt_wu 0111 00000000 10010 ..... ..... ..... @vvv +vslt_du 0111 00000000 10011 ..... ..... ..... @vvv +vslti_bu 0111 00101000 10000 ..... ..... ..... @vv_ui5 +vslti_hu 0111 00101000 10001 ..... ..... ..... @vv_ui5 +vslti_wu 0111 00101000 10010 ..... ..... ..... @vv_ui5 +vslti_du 0111 00101000 10011 ..... ..... ..... @vv_ui5 + +vfcmp_cond_s 0000 11000101 ..... ..... ..... ..... @vvv_fcond +vfcmp_cond_d 0000 11000110 ..... ..... ..... ..... @vvv_fcond + +vbitsel_v 0000 11010001 ..... ..... ..... ..... @vvvv + +vbitseli_b 0111 00111100 01 ........ ..... ..... @vv_ui8 + +vseteqz_v 0111 00101001 11001 00110 ..... 00 ... @cv +vsetnez_v 0111 00101001 11001 00111 ..... 00 ... @cv +vsetanyeqz_b 0111 00101001 11001 01000 ..... 00 ... @cv +vsetanyeqz_h 0111 00101001 11001 01001 ..... 00 ... @cv +vsetanyeqz_w 0111 00101001 11001 01010 ..... 00 ... @cv +vsetanyeqz_d 0111 00101001 11001 01011 ..... 00 ... @cv +vsetallnez_b 0111 00101001 11001 01100 ..... 00 ... @cv +vsetallnez_h 0111 00101001 11001 01101 ..... 00 ... @cv +vsetallnez_w 0111 00101001 11001 01110 ..... 00 ... @cv +vsetallnez_d 0111 00101001 11001 01111 ..... 00 ... @cv + +vinsgr2vr_b 0111 00101110 10111 0 .... ..... ..... @vr_ui4 +vinsgr2vr_h 0111 00101110 10111 10 ... ..... ..... @vr_ui3 +vinsgr2vr_w 0111 00101110 10111 110 .. ..... ..... @vr_ui2 +vinsgr2vr_d 0111 00101110 10111 1110 . ..... ..... @vr_ui1 +vpickve2gr_b 0111 00101110 11111 0 .... ..... ..... @rv_ui4 +vpickve2gr_h 0111 00101110 11111 10 ... ..... ..... @rv_ui3 +vpickve2gr_w 0111 00101110 11111 110 .. ..... ..... @rv_ui2 +vpickve2gr_d 0111 00101110 11111 1110 . ..... ..... @rv_ui1 +vpickve2gr_bu 0111 00101111 00111 0 .... ..... ..... @rv_ui4 +vpickve2gr_hu 0111 00101111 00111 10 ... ..... ..... @rv_ui3 +vpickve2gr_wu 0111 00101111 00111 110 .. ..... ..... @rv_ui2 +vpickve2gr_du 0111 00101111 00111 1110 . ..... ..... @rv_ui1 + +vreplgr2vr_b 0111 00101001 11110 00000 ..... ..... @vr +vreplgr2vr_h 0111 00101001 11110 00001 ..... ..... @vr +vreplgr2vr_w 0111 00101001 11110 00010 ..... ..... @vr +vreplgr2vr_d 0111 00101001 11110 00011 ..... ..... @vr + +vreplve_b 0111 00010010 00100 ..... ..... ..... @vvr +vreplve_h 0111 00010010 00101 ..... ..... ..... @vvr +vreplve_w 0111 00010010 00110 ..... ..... ..... @vvr +vreplve_d 0111 00010010 00111 ..... ..... ..... @vvr +vreplvei_b 0111 00101111 01111 0 .... ..... ..... @vv_ui4 +vreplvei_h 0111 00101111 01111 10 ... ..... ..... @vv_ui3 +vreplvei_w 0111 00101111 01111 110 .. ..... ..... @vv_ui2 +vreplvei_d 0111 00101111 01111 1110 . ..... ..... @vv_ui1 + +vbsll_v 0111 00101000 11100 ..... ..... ..... @vv_ui5 +vbsrl_v 0111 00101000 11101 ..... ..... ..... @vv_ui5 + +vpackev_b 0111 00010001 01100 ..... ..... ..... @vvv +vpackev_h 0111 00010001 01101 ..... ..... ..... @vvv +vpackev_w 0111 00010001 01110 ..... ..... ..... @vvv +vpackev_d 0111 00010001 01111 ..... ..... ..... @vvv +vpackod_b 0111 00010001 10000 ..... ..... ..... @vvv +vpackod_h 0111 00010001 10001 ..... ..... ..... @vvv +vpackod_w 0111 00010001 10010 ..... ..... ..... @vvv +vpackod_d 0111 00010001 10011 ..... ..... ..... @vvv + +vpickev_b 0111 00010001 11100 ..... ..... ..... @vvv +vpickev_h 0111 00010001 11101 ..... ..... ..... @vvv +vpickev_w 0111 00010001 11110 ..... ..... ..... @vvv +vpickev_d 0111 00010001 11111 ..... ..... ..... @vvv +vpickod_b 0111 00010010 00000 ..... ..... ..... @vvv +vpickod_h 0111 00010010 00001 ..... ..... ..... @vvv +vpickod_w 0111 00010010 00010 ..... ..... ..... @vvv +vpickod_d 0111 00010010 00011 ..... ..... ..... @vvv + +vilvl_b 0111 00010001 10100 ..... ..... ..... @vvv +vilvl_h 0111 00010001 10101 ..... ..... ..... @vvv +vilvl_w 0111 00010001 10110 ..... ..... ..... @vvv +vilvl_d 0111 00010001 10111 ..... ..... ..... @vvv +vilvh_b 0111 00010001 11000 ..... ..... ..... @vvv +vilvh_h 0111 00010001 11001 ..... ..... ..... @vvv +vilvh_w 0111 00010001 11010 ..... ..... ..... @vvv +vilvh_d 0111 00010001 11011 ..... ..... ..... @vvv + +vshuf_b 0000 11010101 ..... ..... ..... ..... @vvvv +vshuf_h 0111 00010111 10101 ..... ..... ..... @vvv +vshuf_w 0111 00010111 10110 ..... ..... ..... @vvv +vshuf_d 0111 00010111 10111 ..... ..... ..... @vvv +vshuf4i_b 0111 00111001 00 ........ ..... ..... @vv_ui8 +vshuf4i_h 0111 00111001 01 ........ ..... ..... @vv_ui8 +vshuf4i_w 0111 00111001 10 ........ ..... ..... @vv_ui8 +vshuf4i_d 0111 00111001 11 ........ ..... ..... @vv_ui8 + +vpermi_w 0111 00111110 01 ........ ..... ..... @vv_ui8 + +vextrins_d 0111 00111000 00 ........ ..... ..... @vv_ui8 +vextrins_w 0111 00111000 01 ........ ..... ..... @vv_ui8 +vextrins_h 0111 00111000 10 ........ ..... ..... @vv_ui8 +vextrins_b 0111 00111000 11 ........ ..... ..... @vv_ui8 + +vld 0010 110000 ............ ..... ..... @vr_i12 +vst 0010 110001 ............ ..... ..... @vr_i12 +vldx 0011 10000100 00000 ..... ..... ..... @vrr +vstx 0011 10000100 01000 ..... ..... ..... @vrr + +vldrepl_d 0011 00000001 0 ......... ..... ..... @vr_i9 +vldrepl_w 0011 00000010 .......... ..... ..... @vr_i10 +vldrepl_h 0011 0000010 ........... ..... ..... @vr_i11 +vldrepl_b 0011 000010 ............ ..... ..... @vr_i12 +vstelm_d 0011 00010001 0 . ........ ..... ..... @vr_i8i1 +vstelm_w 0011 00010010 .. ........ ..... ..... @vr_i8i2 +vstelm_h 0011 0001010 ... ........ ..... ..... @vr_i8i3 +vstelm_b 0011 000110 .... ........ ..... ..... @vr_i8i4 + +# +# LoongArch LASX instructions +# +xvadd_b 0111 01000000 10100 ..... ..... ..... @vvv +xvadd_h 0111 01000000 10101 ..... ..... ..... @vvv +xvadd_w 0111 01000000 10110 ..... ..... ..... @vvv +xvadd_d 0111 01000000 10111 ..... ..... ..... @vvv +xvadd_q 0111 01010010 11010 ..... ..... ..... @vvv +xvsub_b 0111 01000000 11000 ..... ..... ..... @vvv +xvsub_h 0111 01000000 11001 ..... ..... ..... @vvv +xvsub_w 0111 01000000 11010 ..... ..... ..... @vvv +xvsub_d 0111 01000000 11011 ..... ..... ..... @vvv +xvsub_q 0111 01010010 11011 ..... ..... ..... @vvv + +xvaddi_bu 0111 01101000 10100 ..... ..... ..... @vv_ui5 +xvaddi_hu 0111 01101000 10101 ..... ..... ..... @vv_ui5 +xvaddi_wu 0111 01101000 10110 ..... ..... ..... @vv_ui5 +xvaddi_du 0111 01101000 10111 ..... ..... ..... @vv_ui5 +xvsubi_bu 0111 01101000 11000 ..... ..... ..... @vv_ui5 +xvsubi_hu 0111 01101000 11001 ..... ..... ..... @vv_ui5 +xvsubi_wu 0111 01101000 11010 ..... ..... ..... @vv_ui5 +xvsubi_du 0111 01101000 11011 ..... ..... ..... @vv_ui5 + +xvneg_b 0111 01101001 11000 01100 ..... ..... @vv +xvneg_h 0111 01101001 11000 01101 ..... ..... @vv +xvneg_w 0111 01101001 11000 01110 ..... ..... @vv +xvneg_d 0111 01101001 11000 01111 ..... ..... @vv + +xvsadd_b 0111 01000100 01100 ..... ..... ..... @vvv +xvsadd_h 0111 01000100 01101 ..... ..... ..... @vvv +xvsadd_w 0111 01000100 01110 ..... ..... ..... @vvv +xvsadd_d 0111 01000100 01111 ..... ..... ..... @vvv +xvsadd_bu 0111 01000100 10100 ..... ..... ..... @vvv +xvsadd_hu 0111 01000100 10101 ..... ..... ..... @vvv +xvsadd_wu 0111 01000100 10110 ..... ..... ..... @vvv +xvsadd_du 0111 01000100 10111 ..... ..... ..... @vvv + +xvssub_b 0111 01000100 10000 ..... ..... ..... @vvv +xvssub_h 0111 01000100 10001 ..... ..... ..... @vvv +xvssub_w 0111 01000100 10010 ..... ..... ..... @vvv +xvssub_d 0111 01000100 10011 ..... ..... ..... @vvv +xvssub_bu 0111 01000100 11000 ..... ..... ..... @vvv +xvssub_hu 0111 01000100 11001 ..... ..... ..... @vvv +xvssub_wu 0111 01000100 11010 ..... ..... ..... @vvv +xvssub_du 0111 01000100 11011 ..... ..... ..... @vvv + +xvhaddw_h_b 0111 01000101 01000 ..... ..... ..... @vvv +xvhaddw_w_h 0111 01000101 01001 ..... ..... ..... @vvv +xvhaddw_d_w 0111 01000101 01010 ..... ..... ..... @vvv +xvhaddw_q_d 0111 01000101 01011 ..... ..... ..... @vvv +xvhaddw_hu_bu 0111 01000101 10000 ..... ..... ..... @vvv +xvhaddw_wu_hu 0111 01000101 10001 ..... ..... ..... @vvv +xvhaddw_du_wu 0111 01000101 10010 ..... ..... ..... @vvv +xvhaddw_qu_du 0111 01000101 10011 ..... ..... ..... @vvv + +xvhsubw_h_b 0111 01000101 01100 ..... ..... ..... @vvv +xvhsubw_w_h 0111 01000101 01101 ..... ..... ..... @vvv +xvhsubw_d_w 0111 01000101 01110 ..... ..... ..... @vvv +xvhsubw_q_d 0111 01000101 01111 ..... ..... ..... @vvv +xvhsubw_hu_bu 0111 01000101 10100 ..... ..... ..... @vvv +xvhsubw_wu_hu 0111 01000101 10101 ..... ..... ..... @vvv +xvhsubw_du_wu 0111 01000101 10110 ..... ..... ..... @vvv +xvhsubw_qu_du 0111 01000101 10111 ..... ..... ..... @vvv + +xvaddwev_h_b 0111 01000001 11100 ..... ..... ..... @vvv +xvaddwev_w_h 0111 01000001 11101 ..... ..... ..... @vvv +xvaddwev_d_w 0111 01000001 11110 ..... ..... ..... @vvv +xvaddwev_q_d 0111 01000001 11111 ..... ..... ..... @vvv +xvaddwod_h_b 0111 01000010 00100 ..... ..... ..... @vvv +xvaddwod_w_h 0111 01000010 00101 ..... ..... ..... @vvv +xvaddwod_d_w 0111 01000010 00110 ..... ..... ..... @vvv +xvaddwod_q_d 0111 01000010 00111 ..... ..... ..... @vvv + +xvsubwev_h_b 0111 01000010 00000 ..... ..... ..... @vvv +xvsubwev_w_h 0111 01000010 00001 ..... ..... ..... @vvv +xvsubwev_d_w 0111 01000010 00010 ..... ..... ..... @vvv +xvsubwev_q_d 0111 01000010 00011 ..... ..... ..... @vvv +xvsubwod_h_b 0111 01000010 01000 ..... ..... ..... @vvv +xvsubwod_w_h 0111 01000010 01001 ..... ..... ..... @vvv +xvsubwod_d_w 0111 01000010 01010 ..... ..... ..... @vvv +xvsubwod_q_d 0111 01000010 01011 ..... ..... ..... @vvv + +xvaddwev_h_bu 0111 01000010 11100 ..... ..... ..... @vvv +xvaddwev_w_hu 0111 01000010 11101 ..... ..... ..... @vvv +xvaddwev_d_wu 0111 01000010 11110 ..... ..... ..... @vvv +xvaddwev_q_du 0111 01000010 11111 ..... ..... ..... @vvv +xvaddwod_h_bu 0111 01000011 00100 ..... ..... ..... @vvv +xvaddwod_w_hu 0111 01000011 00101 ..... ..... ..... @vvv +xvaddwod_d_wu 0111 01000011 00110 ..... ..... ..... @vvv +xvaddwod_q_du 0111 01000011 00111 ..... ..... ..... @vvv + +xvsubwev_h_bu 0111 01000011 00000 ..... ..... ..... @vvv +xvsubwev_w_hu 0111 01000011 00001 ..... ..... ..... @vvv +xvsubwev_d_wu 0111 01000011 00010 ..... ..... ..... @vvv +xvsubwev_q_du 0111 01000011 00011 ..... ..... ..... @vvv +xvsubwod_h_bu 0111 01000011 01000 ..... ..... ..... @vvv +xvsubwod_w_hu 0111 01000011 01001 ..... ..... ..... @vvv +xvsubwod_d_wu 0111 01000011 01010 ..... ..... ..... @vvv +xvsubwod_q_du 0111 01000011 01011 ..... ..... ..... @vvv + +xvaddwev_h_bu_b 0111 01000011 11100 ..... ..... ..... @vvv +xvaddwev_w_hu_h 0111 01000011 11101 ..... ..... ..... @vvv +xvaddwev_d_wu_w 0111 01000011 11110 ..... ..... ..... @vvv +xvaddwev_q_du_d 0111 01000011 11111 ..... ..... ..... @vvv +xvaddwod_h_bu_b 0111 01000100 00000 ..... ..... ..... @vvv +xvaddwod_w_hu_h 0111 01000100 00001 ..... ..... ..... @vvv +xvaddwod_d_wu_w 0111 01000100 00010 ..... ..... ..... @vvv +xvaddwod_q_du_d 0111 01000100 00011 ..... ..... ..... @vvv + +xvavg_b 0111 01000110 01000 ..... ..... ..... @vvv +xvavg_h 0111 01000110 01001 ..... ..... ..... @vvv +xvavg_w 0111 01000110 01010 ..... ..... ..... @vvv +xvavg_d 0111 01000110 01011 ..... ..... ..... @vvv +xvavg_bu 0111 01000110 01100 ..... ..... ..... @vvv +xvavg_hu 0111 01000110 01101 ..... ..... ..... @vvv +xvavg_wu 0111 01000110 01110 ..... ..... ..... @vvv +xvavg_du 0111 01000110 01111 ..... ..... ..... @vvv +xvavgr_b 0111 01000110 10000 ..... ..... ..... @vvv +xvavgr_h 0111 01000110 10001 ..... ..... ..... @vvv +xvavgr_w 0111 01000110 10010 ..... ..... ..... @vvv +xvavgr_d 0111 01000110 10011 ..... ..... ..... @vvv +xvavgr_bu 0111 01000110 10100 ..... ..... ..... @vvv +xvavgr_hu 0111 01000110 10101 ..... ..... ..... @vvv +xvavgr_wu 0111 01000110 10110 ..... ..... ..... @vvv +xvavgr_du 0111 01000110 10111 ..... ..... ..... @vvv + +xvabsd_b 0111 01000110 00000 ..... ..... ..... @vvv +xvabsd_h 0111 01000110 00001 ..... ..... ..... @vvv +xvabsd_w 0111 01000110 00010 ..... ..... ..... @vvv +xvabsd_d 0111 01000110 00011 ..... ..... ..... @vvv +xvabsd_bu 0111 01000110 00100 ..... ..... ..... @vvv +xvabsd_hu 0111 01000110 00101 ..... ..... ..... @vvv +xvabsd_wu 0111 01000110 00110 ..... ..... ..... @vvv +xvabsd_du 0111 01000110 00111 ..... ..... ..... @vvv + +xvadda_b 0111 01000101 11000 ..... ..... ..... @vvv +xvadda_h 0111 01000101 11001 ..... ..... ..... @vvv +xvadda_w 0111 01000101 11010 ..... ..... ..... @vvv +xvadda_d 0111 01000101 11011 ..... ..... ..... @vvv + +xvmax_b 0111 01000111 00000 ..... ..... ..... @vvv +xvmax_h 0111 01000111 00001 ..... ..... ..... @vvv +xvmax_w 0111 01000111 00010 ..... ..... ..... @vvv +xvmax_d 0111 01000111 00011 ..... ..... ..... @vvv +xvmax_bu 0111 01000111 01000 ..... ..... ..... @vvv +xvmax_hu 0111 01000111 01001 ..... ..... ..... @vvv +xvmax_wu 0111 01000111 01010 ..... ..... ..... @vvv +xvmax_du 0111 01000111 01011 ..... ..... ..... @vvv + +xvmaxi_b 0111 01101001 00000 ..... ..... ..... @vv_i5 +xvmaxi_h 0111 01101001 00001 ..... ..... ..... @vv_i5 +xvmaxi_w 0111 01101001 00010 ..... ..... ..... @vv_i5 +xvmaxi_d 0111 01101001 00011 ..... ..... ..... @vv_i5 +xvmaxi_bu 0111 01101001 01000 ..... ..... ..... @vv_ui5 +xvmaxi_hu 0111 01101001 01001 ..... ..... ..... @vv_ui5 +xvmaxi_wu 0111 01101001 01010 ..... ..... ..... @vv_ui5 +xvmaxi_du 0111 01101001 01011 ..... ..... ..... @vv_ui5 + +xvmin_b 0111 01000111 00100 ..... ..... ..... @vvv +xvmin_h 0111 01000111 00101 ..... ..... ..... @vvv +xvmin_w 0111 01000111 00110 ..... ..... ..... @vvv +xvmin_d 0111 01000111 00111 ..... ..... ..... @vvv +xvmin_bu 0111 01000111 01100 ..... ..... ..... @vvv +xvmin_hu 0111 01000111 01101 ..... ..... ..... @vvv +xvmin_wu 0111 01000111 01110 ..... ..... ..... @vvv +xvmin_du 0111 01000111 01111 ..... ..... ..... @vvv + +xvmini_b 0111 01101001 00100 ..... ..... ..... @vv_i5 +xvmini_h 0111 01101001 00101 ..... ..... ..... @vv_i5 +xvmini_w 0111 01101001 00110 ..... ..... ..... @vv_i5 +xvmini_d 0111 01101001 00111 ..... ..... ..... @vv_i5 +xvmini_bu 0111 01101001 01100 ..... ..... ..... @vv_ui5 +xvmini_hu 0111 01101001 01101 ..... ..... ..... @vv_ui5 +xvmini_wu 0111 01101001 01110 ..... ..... ..... @vv_ui5 +xvmini_du 0111 01101001 01111 ..... ..... ..... @vv_ui5 + +xvmul_b 0111 01001000 01000 ..... ..... ..... @vvv +xvmul_h 0111 01001000 01001 ..... ..... ..... @vvv +xvmul_w 0111 01001000 01010 ..... ..... ..... @vvv +xvmul_d 0111 01001000 01011 ..... ..... ..... @vvv +xvmuh_b 0111 01001000 01100 ..... ..... ..... @vvv +xvmuh_h 0111 01001000 01101 ..... ..... ..... @vvv +xvmuh_w 0111 01001000 01110 ..... ..... ..... @vvv +xvmuh_d 0111 01001000 01111 ..... ..... ..... @vvv +xvmuh_bu 0111 01001000 10000 ..... ..... ..... @vvv +xvmuh_hu 0111 01001000 10001 ..... ..... ..... @vvv +xvmuh_wu 0111 01001000 10010 ..... ..... ..... @vvv +xvmuh_du 0111 01001000 10011 ..... ..... ..... @vvv + +xvmulwev_h_b 0111 01001001 00000 ..... ..... ..... @vvv +xvmulwev_w_h 0111 01001001 00001 ..... ..... ..... @vvv +xvmulwev_d_w 0111 01001001 00010 ..... ..... ..... @vvv +xvmulwev_q_d 0111 01001001 00011 ..... ..... ..... @vvv +xvmulwod_h_b 0111 01001001 00100 ..... ..... ..... @vvv +xvmulwod_w_h 0111 01001001 00101 ..... ..... ..... @vvv +xvmulwod_d_w 0111 01001001 00110 ..... ..... ..... @vvv +xvmulwod_q_d 0111 01001001 00111 ..... ..... ..... @vvv +xvmulwev_h_bu 0111 01001001 10000 ..... ..... ..... @vvv +xvmulwev_w_hu 0111 01001001 10001 ..... ..... ..... @vvv +xvmulwev_d_wu 0111 01001001 10010 ..... ..... ..... @vvv +xvmulwev_q_du 0111 01001001 10011 ..... ..... ..... @vvv +xvmulwod_h_bu 0111 01001001 10100 ..... ..... ..... @vvv +xvmulwod_w_hu 0111 01001001 10101 ..... ..... ..... @vvv +xvmulwod_d_wu 0111 01001001 10110 ..... ..... ..... @vvv +xvmulwod_q_du 0111 01001001 10111 ..... ..... ..... @vvv +xvmulwev_h_bu_b 0111 01001010 00000 ..... ..... ..... @vvv +xvmulwev_w_hu_h 0111 01001010 00001 ..... ..... ..... @vvv +xvmulwev_d_wu_w 0111 01001010 00010 ..... ..... ..... @vvv +xvmulwev_q_du_d 0111 01001010 00011 ..... ..... ..... @vvv +xvmulwod_h_bu_b 0111 01001010 00100 ..... ..... ..... @vvv +xvmulwod_w_hu_h 0111 01001010 00101 ..... ..... ..... @vvv +xvmulwod_d_wu_w 0111 01001010 00110 ..... ..... ..... @vvv +xvmulwod_q_du_d 0111 01001010 00111 ..... ..... ..... @vvv + +xvmadd_b 0111 01001010 10000 ..... ..... ..... @vvv +xvmadd_h 0111 01001010 10001 ..... ..... ..... @vvv +xvmadd_w 0111 01001010 10010 ..... ..... ..... @vvv +xvmadd_d 0111 01001010 10011 ..... ..... ..... @vvv +xvmsub_b 0111 01001010 10100 ..... ..... ..... @vvv +xvmsub_h 0111 01001010 10101 ..... ..... ..... @vvv +xvmsub_w 0111 01001010 10110 ..... ..... ..... @vvv +xvmsub_d 0111 01001010 10111 ..... ..... ..... @vvv + +xvmaddwev_h_b 0111 01001010 11000 ..... ..... ..... @vvv +xvmaddwev_w_h 0111 01001010 11001 ..... ..... ..... @vvv +xvmaddwev_d_w 0111 01001010 11010 ..... ..... ..... @vvv +xvmaddwev_q_d 0111 01001010 11011 ..... ..... ..... @vvv +xvmaddwod_h_b 0111 01001010 11100 ..... ..... ..... @vvv +xvmaddwod_w_h 0111 01001010 11101 ..... ..... ..... @vvv +xvmaddwod_d_w 0111 01001010 11110 ..... ..... ..... @vvv +xvmaddwod_q_d 0111 01001010 11111 ..... ..... ..... @vvv +xvmaddwev_h_bu 0111 01001011 01000 ..... ..... ..... @vvv +xvmaddwev_w_hu 0111 01001011 01001 ..... ..... ..... @vvv +xvmaddwev_d_wu 0111 01001011 01010 ..... ..... ..... @vvv +xvmaddwev_q_du 0111 01001011 01011 ..... ..... ..... @vvv +xvmaddwod_h_bu 0111 01001011 01100 ..... ..... ..... @vvv +xvmaddwod_w_hu 0111 01001011 01101 ..... ..... ..... @vvv +xvmaddwod_d_wu 0111 01001011 01110 ..... ..... ..... @vvv +xvmaddwod_q_du 0111 01001011 01111 ..... ..... ..... @vvv +xvmaddwev_h_bu_b 0111 01001011 11000 ..... ..... ..... @vvv +xvmaddwev_w_hu_h 0111 01001011 11001 ..... ..... ..... @vvv +xvmaddwev_d_wu_w 0111 01001011 11010 ..... ..... ..... @vvv +xvmaddwev_q_du_d 0111 01001011 11011 ..... ..... ..... @vvv +xvmaddwod_h_bu_b 0111 01001011 11100 ..... ..... ..... @vvv +xvmaddwod_w_hu_h 0111 01001011 11101 ..... ..... ..... @vvv +xvmaddwod_d_wu_w 0111 01001011 11110 ..... ..... ..... @vvv +xvmaddwod_q_du_d 0111 01001011 11111 ..... ..... ..... @vvv + +xvdiv_b 0111 01001110 00000 ..... ..... ..... @vvv +xvdiv_h 0111 01001110 00001 ..... ..... ..... @vvv +xvdiv_w 0111 01001110 00010 ..... ..... ..... @vvv +xvdiv_d 0111 01001110 00011 ..... ..... ..... @vvv +xvmod_b 0111 01001110 00100 ..... ..... ..... @vvv +xvmod_h 0111 01001110 00101 ..... ..... ..... @vvv +xvmod_w 0111 01001110 00110 ..... ..... ..... @vvv +xvmod_d 0111 01001110 00111 ..... ..... ..... @vvv +xvdiv_bu 0111 01001110 01000 ..... ..... ..... @vvv +xvdiv_hu 0111 01001110 01001 ..... ..... ..... @vvv +xvdiv_wu 0111 01001110 01010 ..... ..... ..... @vvv +xvdiv_du 0111 01001110 01011 ..... ..... ..... @vvv +xvmod_bu 0111 01001110 01100 ..... ..... ..... @vvv +xvmod_hu 0111 01001110 01101 ..... ..... ..... @vvv +xvmod_wu 0111 01001110 01110 ..... ..... ..... @vvv +xvmod_du 0111 01001110 01111 ..... ..... ..... @vvv + +xvsat_b 0111 01110010 01000 01 ... ..... ..... @vv_ui3 +xvsat_h 0111 01110010 01000 1 .... ..... ..... @vv_ui4 +xvsat_w 0111 01110010 01001 ..... ..... ..... @vv_ui5 +xvsat_d 0111 01110010 0101 ...... ..... ..... @vv_ui6 +xvsat_bu 0111 01110010 10000 01 ... ..... ..... @vv_ui3 +xvsat_hu 0111 01110010 10000 1 .... ..... ..... @vv_ui4 +xvsat_wu 0111 01110010 10001 ..... ..... ..... @vv_ui5 +xvsat_du 0111 01110010 1001 ...... ..... ..... @vv_ui6 + +xvexth_h_b 0111 01101001 11101 11000 ..... ..... @vv +xvexth_w_h 0111 01101001 11101 11001 ..... ..... @vv +xvexth_d_w 0111 01101001 11101 11010 ..... ..... @vv +xvexth_q_d 0111 01101001 11101 11011 ..... ..... @vv +xvexth_hu_bu 0111 01101001 11101 11100 ..... ..... @vv +xvexth_wu_hu 0111 01101001 11101 11101 ..... ..... @vv +xvexth_du_wu 0111 01101001 11101 11110 ..... ..... @vv +xvexth_qu_du 0111 01101001 11101 11111 ..... ..... @vv + +vext2xv_h_b 0111 01101001 11110 00100 ..... ..... @vv +vext2xv_w_b 0111 01101001 11110 00101 ..... ..... @vv +vext2xv_d_b 0111 01101001 11110 00110 ..... ..... @vv +vext2xv_w_h 0111 01101001 11110 00111 ..... ..... @vv +vext2xv_d_h 0111 01101001 11110 01000 ..... ..... @vv +vext2xv_d_w 0111 01101001 11110 01001 ..... ..... @vv +vext2xv_hu_bu 0111 01101001 11110 01010 ..... ..... @vv +vext2xv_wu_bu 0111 01101001 11110 01011 ..... ..... @vv +vext2xv_du_bu 0111 01101001 11110 01100 ..... ..... @vv +vext2xv_wu_hu 0111 01101001 11110 01101 ..... ..... @vv +vext2xv_du_hu 0111 01101001 11110 01110 ..... ..... @vv +vext2xv_du_wu 0111 01101001 11110 01111 ..... ..... @vv + +xvsigncov_b 0111 01010010 11100 ..... ..... ..... @vvv +xvsigncov_h 0111 01010010 11101 ..... ..... ..... @vvv +xvsigncov_w 0111 01010010 11110 ..... ..... ..... @vvv +xvsigncov_d 0111 01010010 11111 ..... ..... ..... @vvv + +xvmskltz_b 0111 01101001 11000 10000 ..... ..... @vv +xvmskltz_h 0111 01101001 11000 10001 ..... ..... @vv +xvmskltz_w 0111 01101001 11000 10010 ..... ..... @vv +xvmskltz_d 0111 01101001 11000 10011 ..... ..... @vv +xvmskgez_b 0111 01101001 11000 10100 ..... ..... @vv +xvmsknz_b 0111 01101001 11000 11000 ..... ..... @vv + +xvldi 0111 01111110 00 ............. ..... @v_i13 + +xvand_v 0111 01010010 01100 ..... ..... ..... @vvv +xvor_v 0111 01010010 01101 ..... ..... ..... @vvv +xvxor_v 0111 01010010 01110 ..... ..... ..... @vvv +xvnor_v 0111 01010010 01111 ..... ..... ..... @vvv +xvandn_v 0111 01010010 10000 ..... ..... ..... @vvv +xvorn_v 0111 01010010 10001 ..... ..... ..... @vvv + +xvandi_b 0111 01111101 00 ........ ..... ..... @vv_ui8 +xvori_b 0111 01111101 01 ........ ..... ..... @vv_ui8 +xvxori_b 0111 01111101 10 ........ ..... ..... @vv_ui8 +xvnori_b 0111 01111101 11 ........ ..... ..... @vv_ui8 + +xvsll_b 0111 01001110 10000 ..... ..... ..... @vvv +xvsll_h 0111 01001110 10001 ..... ..... ..... @vvv +xvsll_w 0111 01001110 10010 ..... ..... ..... @vvv +xvsll_d 0111 01001110 10011 ..... ..... ..... @vvv +xvslli_b 0111 01110010 11000 01 ... ..... ..... @vv_ui3 +xvslli_h 0111 01110010 11000 1 .... ..... ..... @vv_ui4 +xvslli_w 0111 01110010 11001 ..... ..... ..... @vv_ui5 +xvslli_d 0111 01110010 1101 ...... ..... ..... @vv_ui6 +xvsrl_b 0111 01001110 10100 ..... ..... ..... @vvv +xvsrl_h 0111 01001110 10101 ..... ..... ..... @vvv +xvsrl_w 0111 01001110 10110 ..... ..... ..... @vvv +xvsrl_d 0111 01001110 10111 ..... ..... ..... @vvv +xvsrli_b 0111 01110011 00000 01 ... ..... ..... @vv_ui3 +xvsrli_h 0111 01110011 00000 1 .... ..... ..... @vv_ui4 +xvsrli_w 0111 01110011 00001 ..... ..... ..... @vv_ui5 +xvsrli_d 0111 01110011 0001 ...... ..... ..... @vv_ui6 +xvsra_b 0111 01001110 11000 ..... ..... ..... @vvv +xvsra_h 0111 01001110 11001 ..... ..... ..... @vvv +xvsra_w 0111 01001110 11010 ..... ..... ..... @vvv +xvsra_d 0111 01001110 11011 ..... ..... ..... @vvv +xvsrai_b 0111 01110011 01000 01 ... ..... ..... @vv_ui3 +xvsrai_h 0111 01110011 01000 1 .... ..... ..... @vv_ui4 +xvsrai_w 0111 01110011 01001 ..... ..... ..... @vv_ui5 +xvsrai_d 0111 01110011 0101 ...... ..... ..... @vv_ui6 +xvrotr_b 0111 01001110 11100 ..... ..... ..... @vvv +xvrotr_h 0111 01001110 11101 ..... ..... ..... @vvv +xvrotr_w 0111 01001110 11110 ..... ..... ..... @vvv +xvrotr_d 0111 01001110 11111 ..... ..... ..... @vvv +xvrotri_b 0111 01101010 00000 01 ... ..... ..... @vv_ui3 +xvrotri_h 0111 01101010 00000 1 .... ..... ..... @vv_ui4 +xvrotri_w 0111 01101010 00001 ..... ..... ..... @vv_ui5 +xvrotri_d 0111 01101010 0001 ...... ..... ..... @vv_ui6 + +xvsllwil_h_b 0111 01110000 10000 01 ... ..... ..... @vv_ui3 +xvsllwil_w_h 0111 01110000 10000 1 .... ..... ..... @vv_ui4 +xvsllwil_d_w 0111 01110000 10001 ..... ..... ..... @vv_ui5 +xvextl_q_d 0111 01110000 10010 00000 ..... ..... @vv +xvsllwil_hu_bu 0111 01110000 11000 01 ... ..... ..... @vv_ui3 +xvsllwil_wu_hu 0111 01110000 11000 1 .... ..... ..... @vv_ui4 +xvsllwil_du_wu 0111 01110000 11001 ..... ..... ..... @vv_ui5 +xvextl_qu_du 0111 01110000 11010 00000 ..... ..... @vv + +xvsrlr_b 0111 01001111 00000 ..... ..... ..... @vvv +xvsrlr_h 0111 01001111 00001 ..... ..... ..... @vvv +xvsrlr_w 0111 01001111 00010 ..... ..... ..... @vvv +xvsrlr_d 0111 01001111 00011 ..... ..... ..... @vvv +xvsrlri_b 0111 01101010 01000 01 ... ..... ..... @vv_ui3 +xvsrlri_h 0111 01101010 01000 1 .... ..... ..... @vv_ui4 +xvsrlri_w 0111 01101010 01001 ..... ..... ..... @vv_ui5 +xvsrlri_d 0111 01101010 0101 ...... ..... ..... @vv_ui6 +xvsrar_b 0111 01001111 00100 ..... ..... ..... @vvv +xvsrar_h 0111 01001111 00101 ..... ..... ..... @vvv +xvsrar_w 0111 01001111 00110 ..... ..... ..... @vvv +xvsrar_d 0111 01001111 00111 ..... ..... ..... @vvv +xvsrari_b 0111 01101010 10000 01 ... ..... ..... @vv_ui3 +xvsrari_h 0111 01101010 10000 1 .... ..... ..... @vv_ui4 +xvsrari_w 0111 01101010 10001 ..... ..... ..... @vv_ui5 +xvsrari_d 0111 01101010 1001 ...... ..... ..... @vv_ui6 + +xvsrln_b_h 0111 01001111 01001 ..... ..... ..... @vvv +xvsrln_h_w 0111 01001111 01010 ..... ..... ..... @vvv +xvsrln_w_d 0111 01001111 01011 ..... ..... ..... @vvv +xvsran_b_h 0111 01001111 01101 ..... ..... ..... @vvv +xvsran_h_w 0111 01001111 01110 ..... ..... ..... @vvv +xvsran_w_d 0111 01001111 01111 ..... ..... ..... @vvv + +xvsrlni_b_h 0111 01110100 00000 1 .... ..... ..... @vv_ui4 +xvsrlni_h_w 0111 01110100 00001 ..... ..... ..... @vv_ui5 +xvsrlni_w_d 0111 01110100 0001 ...... ..... ..... @vv_ui6 +xvsrlni_d_q 0111 01110100 001 ....... ..... ..... @vv_ui7 +xvsrani_b_h 0111 01110101 10000 1 .... ..... ..... @vv_ui4 +xvsrani_h_w 0111 01110101 10001 ..... ..... ..... @vv_ui5 +xvsrani_w_d 0111 01110101 1001 ...... ..... ..... @vv_ui6 +xvsrani_d_q 0111 01110101 101 ....... ..... ..... @vv_ui7 + +xvsrlrn_b_h 0111 01001111 10001 ..... ..... ..... @vvv +xvsrlrn_h_w 0111 01001111 10010 ..... ..... ..... @vvv +xvsrlrn_w_d 0111 01001111 10011 ..... ..... ..... @vvv +xvsrarn_b_h 0111 01001111 10101 ..... ..... ..... @vvv +xvsrarn_h_w 0111 01001111 10110 ..... ..... ..... @vvv +xvsrarn_w_d 0111 01001111 10111 ..... ..... ..... @vvv + +xvsrlrni_b_h 0111 01110100 01000 1 .... ..... ..... @vv_ui4 +xvsrlrni_h_w 0111 01110100 01001 ..... ..... ..... @vv_ui5 +xvsrlrni_w_d 0111 01110100 0101 ...... ..... ..... @vv_ui6 +xvsrlrni_d_q 0111 01110100 011 ....... ..... ..... @vv_ui7 +xvsrarni_b_h 0111 01110101 11000 1 .... ..... ..... @vv_ui4 +xvsrarni_h_w 0111 01110101 11001 ..... ..... ..... @vv_ui5 +xvsrarni_w_d 0111 01110101 1101 ...... ..... ..... @vv_ui6 +xvsrarni_d_q 0111 01110101 111 ....... ..... ..... @vv_ui7 + +xvssrln_b_h 0111 01001111 11001 ..... ..... ..... @vvv +xvssrln_h_w 0111 01001111 11010 ..... ..... ..... @vvv +xvssrln_w_d 0111 01001111 11011 ..... ..... ..... @vvv +xvssran_b_h 0111 01001111 11101 ..... ..... ..... @vvv +xvssran_h_w 0111 01001111 11110 ..... ..... ..... @vvv +xvssran_w_d 0111 01001111 11111 ..... ..... ..... @vvv +xvssrln_bu_h 0111 01010000 01001 ..... ..... ..... @vvv +xvssrln_hu_w 0111 01010000 01010 ..... ..... ..... @vvv +xvssrln_wu_d 0111 01010000 01011 ..... ..... ..... @vvv +xvssran_bu_h 0111 01010000 01101 ..... ..... ..... @vvv +xvssran_hu_w 0111 01010000 01110 ..... ..... ..... @vvv +xvssran_wu_d 0111 01010000 01111 ..... ..... ..... @vvv + +xvssrlni_b_h 0111 01110100 10000 1 .... ..... ..... @vv_ui4 +xvssrlni_h_w 0111 01110100 10001 ..... ..... ..... @vv_ui5 +xvssrlni_w_d 0111 01110100 1001 ...... ..... ..... @vv_ui6 +xvssrlni_d_q 0111 01110100 101 ....... ..... ..... @vv_ui7 +xvssrani_b_h 0111 01110110 00000 1 .... ..... ..... @vv_ui4 +xvssrani_h_w 0111 01110110 00001 ..... ..... ..... @vv_ui5 +xvssrani_w_d 0111 01110110 0001 ...... ..... ..... @vv_ui6 +xvssrani_d_q 0111 01110110 001 ....... ..... ..... @vv_ui7 +xvssrlni_bu_h 0111 01110100 11000 1 .... ..... ..... @vv_ui4 +xvssrlni_hu_w 0111 01110100 11001 ..... ..... ..... @vv_ui5 +xvssrlni_wu_d 0111 01110100 1101 ...... ..... ..... @vv_ui6 +xvssrlni_du_q 0111 01110100 111 ....... ..... ..... @vv_ui7 +xvssrani_bu_h 0111 01110110 01000 1 .... ..... ..... @vv_ui4 +xvssrani_hu_w 0111 01110110 01001 ..... ..... ..... @vv_ui5 +xvssrani_wu_d 0111 01110110 0101 ...... ..... ..... @vv_ui6 +xvssrani_du_q 0111 01110110 011 ....... ..... ..... @vv_ui7 + +xvssrlrn_b_h 0111 01010000 00001 ..... ..... ..... @vvv +xvssrlrn_h_w 0111 01010000 00010 ..... ..... ..... @vvv +xvssrlrn_w_d 0111 01010000 00011 ..... ..... ..... @vvv +xvssrarn_b_h 0111 01010000 00101 ..... ..... ..... @vvv +xvssrarn_h_w 0111 01010000 00110 ..... ..... ..... @vvv +xvssrarn_w_d 0111 01010000 00111 ..... ..... ..... @vvv +xvssrlrn_bu_h 0111 01010000 10001 ..... ..... ..... @vvv +xvssrlrn_hu_w 0111 01010000 10010 ..... ..... ..... @vvv +xvssrlrn_wu_d 0111 01010000 10011 ..... ..... ..... @vvv +xvssrarn_bu_h 0111 01010000 10101 ..... ..... ..... @vvv +xvssrarn_hu_w 0111 01010000 10110 ..... ..... ..... @vvv +xvssrarn_wu_d 0111 01010000 10111 ..... ..... ..... @vvv + +xvssrlrni_b_h 0111 01110101 00000 1 .... ..... ..... @vv_ui4 +xvssrlrni_h_w 0111 01110101 00001 ..... ..... ..... @vv_ui5 +xvssrlrni_w_d 0111 01110101 0001 ...... ..... ..... @vv_ui6 +xvssrlrni_d_q 0111 01110101 001 ....... ..... ..... @vv_ui7 +xvssrarni_b_h 0111 01110110 10000 1 .... ..... ..... @vv_ui4 +xvssrarni_h_w 0111 01110110 10001 ..... ..... ..... @vv_ui5 +xvssrarni_w_d 0111 01110110 1001 ...... ..... ..... @vv_ui6 +xvssrarni_d_q 0111 01110110 101 ....... ..... ..... @vv_ui7 +xvssrlrni_bu_h 0111 01110101 01000 1 .... ..... ..... @vv_ui4 +xvssrlrni_hu_w 0111 01110101 01001 ..... ..... ..... @vv_ui5 +xvssrlrni_wu_d 0111 01110101 0101 ...... ..... ..... @vv_ui6 +xvssrlrni_du_q 0111 01110101 011 ....... ..... ..... @vv_ui7 +xvssrarni_bu_h 0111 01110110 11000 1 .... ..... ..... @vv_ui4 +xvssrarni_hu_w 0111 01110110 11001 ..... ..... ..... @vv_ui5 +xvssrarni_wu_d 0111 01110110 1101 ...... ..... ..... @vv_ui6 +xvssrarni_du_q 0111 01110110 111 ....... ..... ..... @vv_ui7 + +xvclo_b 0111 01101001 11000 00000 ..... ..... @vv +xvclo_h 0111 01101001 11000 00001 ..... ..... @vv +xvclo_w 0111 01101001 11000 00010 ..... ..... @vv +xvclo_d 0111 01101001 11000 00011 ..... ..... @vv +xvclz_b 0111 01101001 11000 00100 ..... ..... @vv +xvclz_h 0111 01101001 11000 00101 ..... ..... @vv +xvclz_w 0111 01101001 11000 00110 ..... ..... @vv +xvclz_d 0111 01101001 11000 00111 ..... ..... @vv + +xvpcnt_b 0111 01101001 11000 01000 ..... ..... @vv +xvpcnt_h 0111 01101001 11000 01001 ..... ..... @vv +xvpcnt_w 0111 01101001 11000 01010 ..... ..... @vv +xvpcnt_d 0111 01101001 11000 01011 ..... ..... @vv + +xvbitclr_b 0111 01010000 11000 ..... ..... ..... @vvv +xvbitclr_h 0111 01010000 11001 ..... ..... ..... @vvv +xvbitclr_w 0111 01010000 11010 ..... ..... ..... @vvv +xvbitclr_d 0111 01010000 11011 ..... ..... ..... @vvv +xvbitclri_b 0111 01110001 00000 01 ... ..... ..... @vv_ui3 +xvbitclri_h 0111 01110001 00000 1 .... ..... ..... @vv_ui4 +xvbitclri_w 0111 01110001 00001 ..... ..... ..... @vv_ui5 +xvbitclri_d 0111 01110001 0001 ...... ..... ..... @vv_ui6 + +xvbitset_b 0111 01010000 11100 ..... ..... ..... @vvv +xvbitset_h 0111 01010000 11101 ..... ..... ..... @vvv +xvbitset_w 0111 01010000 11110 ..... ..... ..... @vvv +xvbitset_d 0111 01010000 11111 ..... ..... ..... @vvv +xvbitseti_b 0111 01110001 01000 01 ... ..... ..... @vv_ui3 +xvbitseti_h 0111 01110001 01000 1 .... ..... ..... @vv_ui4 +xvbitseti_w 0111 01110001 01001 ..... ..... ..... @vv_ui5 +xvbitseti_d 0111 01110001 0101 ...... ..... ..... @vv_ui6 + +xvbitrev_b 0111 01010001 00000 ..... ..... ..... @vvv +xvbitrev_h 0111 01010001 00001 ..... ..... ..... @vvv +xvbitrev_w 0111 01010001 00010 ..... ..... ..... @vvv +xvbitrev_d 0111 01010001 00011 ..... ..... ..... @vvv +xvbitrevi_b 0111 01110001 10000 01 ... ..... ..... @vv_ui3 +xvbitrevi_h 0111 01110001 10000 1 .... ..... ..... @vv_ui4 +xvbitrevi_w 0111 01110001 10001 ..... ..... ..... @vv_ui5 +xvbitrevi_d 0111 01110001 1001 ...... ..... ..... @vv_ui6 + +xvfrstp_b 0111 01010010 10110 ..... ..... ..... @vvv +xvfrstp_h 0111 01010010 10111 ..... ..... ..... @vvv +xvfrstpi_b 0111 01101001 10100 ..... ..... ..... @vv_ui5 +xvfrstpi_h 0111 01101001 10101 ..... ..... ..... @vv_ui5 + +xvfadd_s 0111 01010011 00001 ..... ..... ..... @vvv +xvfadd_d 0111 01010011 00010 ..... ..... ..... @vvv +xvfsub_s 0111 01010011 00101 ..... ..... ..... @vvv +xvfsub_d 0111 01010011 00110 ..... ..... ..... @vvv +xvfmul_s 0111 01010011 10001 ..... ..... ..... @vvv +xvfmul_d 0111 01010011 10010 ..... ..... ..... @vvv +xvfdiv_s 0111 01010011 10101 ..... ..... ..... @vvv +xvfdiv_d 0111 01010011 10110 ..... ..... ..... @vvv + +xvfmadd_s 0000 10100001 ..... ..... ..... ..... @vvvv +xvfmadd_d 0000 10100010 ..... ..... ..... ..... @vvvv +xvfmsub_s 0000 10100101 ..... ..... ..... ..... @vvvv +xvfmsub_d 0000 10100110 ..... ..... ..... ..... @vvvv +xvfnmadd_s 0000 10101001 ..... ..... ..... ..... @vvvv +xvfnmadd_d 0000 10101010 ..... ..... ..... ..... @vvvv +xvfnmsub_s 0000 10101101 ..... ..... ..... ..... @vvvv +xvfnmsub_d 0000 10101110 ..... ..... ..... ..... @vvvv + +xvfmax_s 0111 01010011 11001 ..... ..... ..... @vvv +xvfmax_d 0111 01010011 11010 ..... ..... ..... @vvv +xvfmin_s 0111 01010011 11101 ..... ..... ..... @vvv +xvfmin_d 0111 01010011 11110 ..... ..... ..... @vvv + +xvfmaxa_s 0111 01010100 00001 ..... ..... ..... @vvv +xvfmaxa_d 0111 01010100 00010 ..... ..... ..... @vvv +xvfmina_s 0111 01010100 00101 ..... ..... ..... @vvv +xvfmina_d 0111 01010100 00110 ..... ..... ..... @vvv + +xvflogb_s 0111 01101001 11001 10001 ..... ..... @vv +xvflogb_d 0111 01101001 11001 10010 ..... ..... @vv + +xvfclass_s 0111 01101001 11001 10101 ..... ..... @vv +xvfclass_d 0111 01101001 11001 10110 ..... ..... @vv + +xvfsqrt_s 0111 01101001 11001 11001 ..... ..... @vv +xvfsqrt_d 0111 01101001 11001 11010 ..... ..... @vv +xvfrecip_s 0111 01101001 11001 11101 ..... ..... @vv +xvfrecip_d 0111 01101001 11001 11110 ..... ..... @vv +xvfrsqrt_s 0111 01101001 11010 00001 ..... ..... @vv +xvfrsqrt_d 0111 01101001 11010 00010 ..... ..... @vv + +xvfcvtl_s_h 0111 01101001 11011 11010 ..... ..... @vv +xvfcvth_s_h 0111 01101001 11011 11011 ..... ..... @vv +xvfcvtl_d_s 0111 01101001 11011 11100 ..... ..... @vv +xvfcvth_d_s 0111 01101001 11011 11101 ..... ..... @vv +xvfcvt_h_s 0111 01010100 01100 ..... ..... ..... @vvv +xvfcvt_s_d 0111 01010100 01101 ..... ..... ..... @vvv + +xvfrintrne_s 0111 01101001 11010 11101 ..... ..... @vv +xvfrintrne_d 0111 01101001 11010 11110 ..... ..... @vv +xvfrintrz_s 0111 01101001 11010 11001 ..... ..... @vv +xvfrintrz_d 0111 01101001 11010 11010 ..... ..... @vv +xvfrintrp_s 0111 01101001 11010 10101 ..... ..... @vv +xvfrintrp_d 0111 01101001 11010 10110 ..... ..... @vv +xvfrintrm_s 0111 01101001 11010 10001 ..... ..... @vv +xvfrintrm_d 0111 01101001 11010 10010 ..... ..... @vv +xvfrint_s 0111 01101001 11010 01101 ..... ..... @vv +xvfrint_d 0111 01101001 11010 01110 ..... ..... @vv + +xvftintrne_w_s 0111 01101001 11100 10100 ..... ..... @vv +xvftintrne_l_d 0111 01101001 11100 10101 ..... ..... @vv +xvftintrz_w_s 0111 01101001 11100 10010 ..... ..... @vv +xvftintrz_l_d 0111 01101001 11100 10011 ..... ..... @vv +xvftintrp_w_s 0111 01101001 11100 10000 ..... ..... @vv +xvftintrp_l_d 0111 01101001 11100 10001 ..... ..... @vv +xvftintrm_w_s 0111 01101001 11100 01110 ..... ..... @vv +xvftintrm_l_d 0111 01101001 11100 01111 ..... ..... @vv +xvftint_w_s 0111 01101001 11100 01100 ..... ..... @vv +xvftint_l_d 0111 01101001 11100 01101 ..... ..... @vv +xvftintrz_wu_s 0111 01101001 11100 11100 ..... ..... @vv +xvftintrz_lu_d 0111 01101001 11100 11101 ..... ..... @vv +xvftint_wu_s 0111 01101001 11100 10110 ..... ..... @vv +xvftint_lu_d 0111 01101001 11100 10111 ..... ..... @vv + +xvftintrne_w_d 0111 01010100 10111 ..... ..... ..... @vvv +xvftintrz_w_d 0111 01010100 10110 ..... ..... ..... @vvv +xvftintrp_w_d 0111 01010100 10101 ..... ..... ..... @vvv +xvftintrm_w_d 0111 01010100 10100 ..... ..... ..... @vvv +xvftint_w_d 0111 01010100 10011 ..... ..... ..... @vvv + +xvftintrnel_l_s 0111 01101001 11101 01000 ..... ..... @vv +xvftintrneh_l_s 0111 01101001 11101 01001 ..... ..... @vv +xvftintrzl_l_s 0111 01101001 11101 00110 ..... ..... @vv +xvftintrzh_l_s 0111 01101001 11101 00111 ..... ..... @vv +xvftintrpl_l_s 0111 01101001 11101 00100 ..... ..... @vv +xvftintrph_l_s 0111 01101001 11101 00101 ..... ..... @vv +xvftintrml_l_s 0111 01101001 11101 00010 ..... ..... @vv +xvftintrmh_l_s 0111 01101001 11101 00011 ..... ..... @vv +xvftintl_l_s 0111 01101001 11101 00000 ..... ..... @vv +xvftinth_l_s 0111 01101001 11101 00001 ..... ..... @vv + +xvffint_s_w 0111 01101001 11100 00000 ..... ..... @vv +xvffint_d_l 0111 01101001 11100 00010 ..... ..... @vv +xvffint_s_wu 0111 01101001 11100 00001 ..... ..... @vv +xvffint_d_lu 0111 01101001 11100 00011 ..... ..... @vv +xvffintl_d_w 0111 01101001 11100 00100 ..... ..... @vv +xvffinth_d_w 0111 01101001 11100 00101 ..... ..... @vv +xvffint_s_l 0111 01010100 10000 ..... ..... ..... @vvv + +xvseq_b 0111 01000000 00000 ..... ..... ..... @vvv +xvseq_h 0111 01000000 00001 ..... ..... ..... @vvv +xvseq_w 0111 01000000 00010 ..... ..... ..... @vvv +xvseq_d 0111 01000000 00011 ..... ..... ..... @vvv +xvseqi_b 0111 01101000 00000 ..... ..... ..... @vv_i5 +xvseqi_h 0111 01101000 00001 ..... ..... ..... @vv_i5 +xvseqi_w 0111 01101000 00010 ..... ..... ..... @vv_i5 +xvseqi_d 0111 01101000 00011 ..... ..... ..... @vv_i5 + +xvsle_b 0111 01000000 00100 ..... ..... ..... @vvv +xvsle_h 0111 01000000 00101 ..... ..... ..... @vvv +xvsle_w 0111 01000000 00110 ..... ..... ..... @vvv +xvsle_d 0111 01000000 00111 ..... ..... ..... @vvv +xvslei_b 0111 01101000 00100 ..... ..... ..... @vv_i5 +xvslei_h 0111 01101000 00101 ..... ..... ..... @vv_i5 +xvslei_w 0111 01101000 00110 ..... ..... ..... @vv_i5 +xvslei_d 0111 01101000 00111 ..... ..... ..... @vv_i5 +xvsle_bu 0111 01000000 01000 ..... ..... ..... @vvv +xvsle_hu 0111 01000000 01001 ..... ..... ..... @vvv +xvsle_wu 0111 01000000 01010 ..... ..... ..... @vvv +xvsle_du 0111 01000000 01011 ..... ..... ..... @vvv +xvslei_bu 0111 01101000 01000 ..... ..... ..... @vv_ui5 +xvslei_hu 0111 01101000 01001 ..... ..... ..... @vv_ui5 +xvslei_wu 0111 01101000 01010 ..... ..... ..... @vv_ui5 +xvslei_du 0111 01101000 01011 ..... ..... ..... @vv_ui5 + +xvslt_b 0111 01000000 01100 ..... ..... ..... @vvv +xvslt_h 0111 01000000 01101 ..... ..... ..... @vvv +xvslt_w 0111 01000000 01110 ..... ..... ..... @vvv +xvslt_d 0111 01000000 01111 ..... ..... ..... @vvv +xvslti_b 0111 01101000 01100 ..... ..... ..... @vv_i5 +xvslti_h 0111 01101000 01101 ..... ..... ..... @vv_i5 +xvslti_w 0111 01101000 01110 ..... ..... ..... @vv_i5 +xvslti_d 0111 01101000 01111 ..... ..... ..... @vv_i5 +xvslt_bu 0111 01000000 10000 ..... ..... ..... @vvv +xvslt_hu 0111 01000000 10001 ..... ..... ..... @vvv +xvslt_wu 0111 01000000 10010 ..... ..... ..... @vvv +xvslt_du 0111 01000000 10011 ..... ..... ..... @vvv +xvslti_bu 0111 01101000 10000 ..... ..... ..... @vv_ui5 +xvslti_hu 0111 01101000 10001 ..... ..... ..... @vv_ui5 +xvslti_wu 0111 01101000 10010 ..... ..... ..... @vv_ui5 +xvslti_du 0111 01101000 10011 ..... ..... ..... @vv_ui5 + +xvfcmp_cond_s 0000 11001001 ..... ..... ..... ..... @vvv_fcond +xvfcmp_cond_d 0000 11001010 ..... ..... ..... ..... @vvv_fcond + +xvbitsel_v 0000 11010010 ..... ..... ..... ..... @vvvv + +xvbitseli_b 0111 01111100 01 ........ ..... ..... @vv_ui8 + +xvseteqz_v 0111 01101001 11001 00110 ..... 00 ... @cv +xvsetnez_v 0111 01101001 11001 00111 ..... 00 ... @cv +xvsetanyeqz_b 0111 01101001 11001 01000 ..... 00 ... @cv +xvsetanyeqz_h 0111 01101001 11001 01001 ..... 00 ... @cv +xvsetanyeqz_w 0111 01101001 11001 01010 ..... 00 ... @cv +xvsetanyeqz_d 0111 01101001 11001 01011 ..... 00 ... @cv +xvsetallnez_b 0111 01101001 11001 01100 ..... 00 ... @cv +xvsetallnez_h 0111 01101001 11001 01101 ..... 00 ... @cv +xvsetallnez_w 0111 01101001 11001 01110 ..... 00 ... @cv +xvsetallnez_d 0111 01101001 11001 01111 ..... 00 ... @cv + +xvinsgr2vr_w 0111 01101110 10111 10 ... ..... ..... @vr_ui3 +xvinsgr2vr_d 0111 01101110 10111 110 .. ..... ..... @vr_ui2 +xvpickve2gr_w 0111 01101110 11111 10 ... ..... ..... @rv_ui3 +xvpickve2gr_d 0111 01101110 11111 110 .. ..... ..... @rv_ui2 +xvpickve2gr_wu 0111 01101111 00111 10 ... ..... ..... @rv_ui3 +xvpickve2gr_du 0111 01101111 00111 110 .. ..... ..... @rv_ui2 + +xvreplgr2vr_b 0111 01101001 11110 00000 ..... ..... @vr +xvreplgr2vr_h 0111 01101001 11110 00001 ..... ..... @vr +xvreplgr2vr_w 0111 01101001 11110 00010 ..... ..... @vr +xvreplgr2vr_d 0111 01101001 11110 00011 ..... ..... @vr + +xvreplve_b 0111 01010010 00100 ..... ..... ..... @vvr +xvreplve_h 0111 01010010 00101 ..... ..... ..... @vvr +xvreplve_w 0111 01010010 00110 ..... ..... ..... @vvr +xvreplve_d 0111 01010010 00111 ..... ..... ..... @vvr + +xvrepl128vei_b 0111 01101111 01111 0 .... ..... ..... @vv_ui4 +xvrepl128vei_h 0111 01101111 01111 10 ... ..... ..... @vv_ui3 +xvrepl128vei_w 0111 01101111 01111 110 .. ..... ..... @vv_ui2 +xvrepl128vei_d 0111 01101111 01111 1110 . ..... ..... @vv_ui1 + +xvreplve0_b 0111 01110000 01110 00000 ..... ..... @vv +xvreplve0_h 0111 01110000 01111 00000 ..... ..... @vv +xvreplve0_w 0111 01110000 01111 10000 ..... ..... @vv +xvreplve0_d 0111 01110000 01111 11000 ..... ..... @vv +xvreplve0_q 0111 01110000 01111 11100 ..... ..... @vv + +xvinsve0_w 0111 01101111 11111 10 ... ..... ..... @vv_ui3 +xvinsve0_d 0111 01101111 11111 110 .. ..... ..... @vv_ui2 + +xvpickve_w 0111 01110000 00111 10 ... ..... ..... @vv_ui3 +xvpickve_d 0111 01110000 00111 110 .. ..... ..... @vv_ui2 + +xvbsll_v 0111 01101000 11100 ..... ..... ..... @vv_ui5 +xvbsrl_v 0111 01101000 11101 ..... ..... ..... @vv_ui5 + +xvpackev_b 0111 01010001 01100 ..... ..... ..... @vvv +xvpackev_h 0111 01010001 01101 ..... ..... ..... @vvv +xvpackev_w 0111 01010001 01110 ..... ..... ..... @vvv +xvpackev_d 0111 01010001 01111 ..... ..... ..... @vvv +xvpackod_b 0111 01010001 10000 ..... ..... ..... @vvv +xvpackod_h 0111 01010001 10001 ..... ..... ..... @vvv +xvpackod_w 0111 01010001 10010 ..... ..... ..... @vvv +xvpackod_d 0111 01010001 10011 ..... ..... ..... @vvv + +xvpickev_b 0111 01010001 11100 ..... ..... ..... @vvv +xvpickev_h 0111 01010001 11101 ..... ..... ..... @vvv +xvpickev_w 0111 01010001 11110 ..... ..... ..... @vvv +xvpickev_d 0111 01010001 11111 ..... ..... ..... @vvv +xvpickod_b 0111 01010010 00000 ..... ..... ..... @vvv +xvpickod_h 0111 01010010 00001 ..... ..... ..... @vvv +xvpickod_w 0111 01010010 00010 ..... ..... ..... @vvv +xvpickod_d 0111 01010010 00011 ..... ..... ..... @vvv + +xvilvl_b 0111 01010001 10100 ..... ..... ..... @vvv +xvilvl_h 0111 01010001 10101 ..... ..... ..... @vvv +xvilvl_w 0111 01010001 10110 ..... ..... ..... @vvv +xvilvl_d 0111 01010001 10111 ..... ..... ..... @vvv +xvilvh_b 0111 01010001 11000 ..... ..... ..... @vvv +xvilvh_h 0111 01010001 11001 ..... ..... ..... @vvv +xvilvh_w 0111 01010001 11010 ..... ..... ..... @vvv +xvilvh_d 0111 01010001 11011 ..... ..... ..... @vvv + +xvshuf_b 0000 11010110 ..... ..... ..... ..... @vvvv +xvshuf_h 0111 01010111 10101 ..... ..... ..... @vvv +xvshuf_w 0111 01010111 10110 ..... ..... ..... @vvv +xvshuf_d 0111 01010111 10111 ..... ..... ..... @vvv + +xvperm_w 0111 01010111 11010 ..... ..... ..... @vvv + +xvshuf4i_b 0111 01111001 00 ........ ..... ..... @vv_ui8 +xvshuf4i_h 0111 01111001 01 ........ ..... ..... @vv_ui8 +xvshuf4i_w 0111 01111001 10 ........ ..... ..... @vv_ui8 +xvshuf4i_d 0111 01111001 11 ........ ..... ..... @vv_ui8 + +xvpermi_w 0111 01111110 01 ........ ..... ..... @vv_ui8 +xvpermi_d 0111 01111110 10 ........ ..... ..... @vv_ui8 +xvpermi_q 0111 01111110 11 ........ ..... ..... @vv_ui8 + +xvextrins_d 0111 01111000 00 ........ ..... ..... @vv_ui8 +xvextrins_w 0111 01111000 01 ........ ..... ..... @vv_ui8 +xvextrins_h 0111 01111000 10 ........ ..... ..... @vv_ui8 +xvextrins_b 0111 01111000 11 ........ ..... ..... @vv_ui8 + +xvld 0010 110010 ............ ..... ..... @vr_i12 +xvst 0010 110011 ............ ..... ..... @vr_i12 +xvldx 0011 10000100 10000 ..... ..... ..... @vrr +xvstx 0011 10000100 11000 ..... ..... ..... @vrr + +xvldrepl_d 0011 00100001 0 ......... ..... ..... @vr_i9 +xvldrepl_w 0011 00100010 .......... ..... ..... @vr_i10 +xvldrepl_h 0011 0010010 ........... ..... ..... @vr_i11 +xvldrepl_b 0011 001010 ............ ..... ..... @vr_i12 +xvstelm_d 0011 00110001 .. ........ ..... ..... @vr_i8i2x +xvstelm_w 0011 0011001 ... ........ ..... ..... @vr_i8i3x +xvstelm_h 0011 001101 .... ........ ..... ..... @vr_i8i4x +xvstelm_b 0011 00111 ..... ........ ..... ..... @vr_i8i5x diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index 9d50fbdd81..944153b180 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -16,11 +16,6 @@ #define TARGET_PHYS_MASK MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS) #define TARGET_VIRT_MASK MAKE_64BIT_MASK(0, TARGET_VIRT_ADDR_SPACE_BITS) -/* Global bit used for lddir/ldpte */ -#define LOONGARCH_PAGE_HUGE_SHIFT 6 -/* Global bit for huge page */ -#define LOONGARCH_HGLOBAL_SHIFT 12 - void loongarch_translate_init(void); void loongarch_cpu_dump_state(CPUState *cpu, FILE *f, int flags); @@ -31,7 +26,22 @@ void G_NORETURN do_raise_exception(CPULoongArchState *env, const char *loongarch_exception_name(int32_t exception); +#ifdef CONFIG_TCG +int ieee_ex_to_loongarch(int xcpt); void restore_fp_status(CPULoongArchState *env); +#endif + +#ifndef CONFIG_USER_ONLY +enum { + TLBRET_MATCH = 0, + TLBRET_BADADDR = 1, + TLBRET_NOMATCH = 2, + TLBRET_INVALID = 3, + TLBRET_DIRTY = 4, + TLBRET_RI = 5, + TLBRET_XI = 6, + TLBRET_PE = 7, +}; extern const VMStateDescription vmstate_loongarch_cpu; @@ -42,12 +52,22 @@ uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu); uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu); void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, uint64_t value); +bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, + int *index); +int get_physical_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx); +hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +#ifdef CONFIG_TCG bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +#endif +#endif /* !CONFIG_USER_ONLY */ -hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +uint64_t read_fcc(CPULoongArchState *env); +void write_fcc(CPULoongArchState *env, uint64_t val); int loongarch_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n); int loongarch_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n); diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c new file mode 100644 index 0000000000..d630cc39cb --- /dev/null +++ b/target/loongarch/kvm/kvm.c @@ -0,0 +1,784 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch KVM + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include +#include + +#include "qemu/timer.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "sysemu/kvm_int.h" +#include "hw/pci/pci.h" +#include "exec/memattrs.h" +#include "exec/address-spaces.h" +#include "hw/boards.h" +#include "hw/irq.h" +#include "qemu/log.h" +#include "hw/loader.h" +#include "sysemu/runstate.h" +#include "cpu-csr.h" +#include "kvm_loongarch.h" +#include "trace.h" + +static bool cap_has_mp_state; +const KVMCapabilityInfo kvm_arch_required_capabilities[] = { + KVM_CAP_LAST_INFO +}; + +static int kvm_loongarch_get_regs_core(CPUState *cs) +{ + int ret = 0; + int i; + struct kvm_regs regs; + CPULoongArchState *env = cpu_env(cs); + + /* Get the current register set as KVM seems it */ + ret = kvm_vcpu_ioctl(cs, KVM_GET_REGS, ®s); + if (ret < 0) { + trace_kvm_failed_get_regs_core(strerror(errno)); + return ret; + } + /* gpr[0] value is always 0 */ + env->gpr[0] = 0; + for (i = 1; i < 32; i++) { + env->gpr[i] = regs.gpr[i]; + } + + env->pc = regs.pc; + return ret; +} + +static int kvm_loongarch_put_regs_core(CPUState *cs) +{ + int ret = 0; + int i; + struct kvm_regs regs; + CPULoongArchState *env = cpu_env(cs); + + /* Set the registers based on QEMU's view of things */ + for (i = 0; i < 32; i++) { + regs.gpr[i] = env->gpr[i]; + } + + regs.pc = env->pc; + ret = kvm_vcpu_ioctl(cs, KVM_SET_REGS, ®s); + if (ret < 0) { + trace_kvm_failed_put_regs_core(strerror(errno)); + } + + return ret; +} + +static int kvm_loongarch_get_csr(CPUState *cs) +{ + int ret = 0; + CPULoongArchState *env = cpu_env(cs); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_CRMD), + &env->CSR_CRMD); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRMD), + &env->CSR_PRMD); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_EUEN), + &env->CSR_EUEN); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MISC), + &env->CSR_MISC); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ECFG), + &env->CSR_ECFG); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ESTAT), + &env->CSR_ESTAT); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ERA), + &env->CSR_ERA); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_BADV), + &env->CSR_BADV); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_BADI), + &env->CSR_BADI); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_EENTRY), + &env->CSR_EENTRY); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBIDX), + &env->CSR_TLBIDX); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBEHI), + &env->CSR_TLBEHI); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBELO0), + &env->CSR_TLBELO0); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBELO1), + &env->CSR_TLBELO1); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ASID), + &env->CSR_ASID); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PGDL), + &env->CSR_PGDL); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PGDH), + &env->CSR_PGDH); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PGD), + &env->CSR_PGD); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PWCL), + &env->CSR_PWCL); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PWCH), + &env->CSR_PWCH); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_STLBPS), + &env->CSR_STLBPS); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_RVACFG), + &env->CSR_RVACFG); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_CPUID), + &env->CSR_CPUID); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRCFG1), + &env->CSR_PRCFG1); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRCFG2), + &env->CSR_PRCFG2); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRCFG3), + &env->CSR_PRCFG3); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(0)), + &env->CSR_SAVE[0]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(1)), + &env->CSR_SAVE[1]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(2)), + &env->CSR_SAVE[2]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(3)), + &env->CSR_SAVE[3]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(4)), + &env->CSR_SAVE[4]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(5)), + &env->CSR_SAVE[5]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(6)), + &env->CSR_SAVE[6]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(7)), + &env->CSR_SAVE[7]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TID), + &env->CSR_TID); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_CNTC), + &env->CSR_CNTC); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TICLR), + &env->CSR_TICLR); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_LLBCTL), + &env->CSR_LLBCTL); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_IMPCTL1), + &env->CSR_IMPCTL1); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_IMPCTL2), + &env->CSR_IMPCTL2); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRENTRY), + &env->CSR_TLBRENTRY); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRBADV), + &env->CSR_TLBRBADV); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRERA), + &env->CSR_TLBRERA); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRSAVE), + &env->CSR_TLBRSAVE); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRELO0), + &env->CSR_TLBRELO0); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRELO1), + &env->CSR_TLBRELO1); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBREHI), + &env->CSR_TLBREHI); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRPRMD), + &env->CSR_TLBRPRMD); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(0)), + &env->CSR_DMW[0]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(1)), + &env->CSR_DMW[1]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(2)), + &env->CSR_DMW[2]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(3)), + &env->CSR_DMW[3]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TVAL), + &env->CSR_TVAL); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TCFG), + &env->CSR_TCFG); + + return ret; +} + +static int kvm_loongarch_put_csr(CPUState *cs, int level) +{ + int ret = 0; + CPULoongArchState *env = cpu_env(cs); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_CRMD), + &env->CSR_CRMD); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRMD), + &env->CSR_PRMD); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_EUEN), + &env->CSR_EUEN); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MISC), + &env->CSR_MISC); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ECFG), + &env->CSR_ECFG); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ESTAT), + &env->CSR_ESTAT); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ERA), + &env->CSR_ERA); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_BADV), + &env->CSR_BADV); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_BADI), + &env->CSR_BADI); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_EENTRY), + &env->CSR_EENTRY); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBIDX), + &env->CSR_TLBIDX); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBEHI), + &env->CSR_TLBEHI); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBELO0), + &env->CSR_TLBELO0); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBELO1), + &env->CSR_TLBELO1); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ASID), + &env->CSR_ASID); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PGDL), + &env->CSR_PGDL); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PGDH), + &env->CSR_PGDH); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PGD), + &env->CSR_PGD); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PWCL), + &env->CSR_PWCL); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PWCH), + &env->CSR_PWCH); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_STLBPS), + &env->CSR_STLBPS); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_RVACFG), + &env->CSR_RVACFG); + + /* CPUID is constant after poweron, it should be set only once */ + if (level >= KVM_PUT_FULL_STATE) { + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_CPUID), + &env->CSR_CPUID); + } + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRCFG1), + &env->CSR_PRCFG1); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRCFG2), + &env->CSR_PRCFG2); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRCFG3), + &env->CSR_PRCFG3); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(0)), + &env->CSR_SAVE[0]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(1)), + &env->CSR_SAVE[1]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(2)), + &env->CSR_SAVE[2]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(3)), + &env->CSR_SAVE[3]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(4)), + &env->CSR_SAVE[4]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(5)), + &env->CSR_SAVE[5]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(6)), + &env->CSR_SAVE[6]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(7)), + &env->CSR_SAVE[7]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TID), + &env->CSR_TID); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_CNTC), + &env->CSR_CNTC); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TICLR), + &env->CSR_TICLR); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_LLBCTL), + &env->CSR_LLBCTL); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_IMPCTL1), + &env->CSR_IMPCTL1); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_IMPCTL2), + &env->CSR_IMPCTL2); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRENTRY), + &env->CSR_TLBRENTRY); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRBADV), + &env->CSR_TLBRBADV); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRERA), + &env->CSR_TLBRERA); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRSAVE), + &env->CSR_TLBRSAVE); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRELO0), + &env->CSR_TLBRELO0); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRELO1), + &env->CSR_TLBRELO1); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBREHI), + &env->CSR_TLBREHI); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRPRMD), + &env->CSR_TLBRPRMD); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(0)), + &env->CSR_DMW[0]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(1)), + &env->CSR_DMW[1]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(2)), + &env->CSR_DMW[2]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(3)), + &env->CSR_DMW[3]); + /* + * timer cfg must be put at last since it is used to enable + * guest timer + */ + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TVAL), + &env->CSR_TVAL); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TCFG), + &env->CSR_TCFG); + return ret; +} + +static int kvm_loongarch_get_regs_fp(CPUState *cs) +{ + int ret, i; + struct kvm_fpu fpu; + CPULoongArchState *env = cpu_env(cs); + + ret = kvm_vcpu_ioctl(cs, KVM_GET_FPU, &fpu); + if (ret < 0) { + trace_kvm_failed_get_fpu(strerror(errno)); + return ret; + } + + env->fcsr0 = fpu.fcsr; + for (i = 0; i < 32; i++) { + env->fpr[i].vreg.UD[0] = fpu.fpr[i].val64[0]; + } + for (i = 0; i < 8; i++) { + env->cf[i] = fpu.fcc & 0xFF; + fpu.fcc = fpu.fcc >> 8; + } + + return ret; +} + +static int kvm_loongarch_put_regs_fp(CPUState *cs) +{ + int ret, i; + struct kvm_fpu fpu; + CPULoongArchState *env = cpu_env(cs); + + fpu.fcsr = env->fcsr0; + fpu.fcc = 0; + for (i = 0; i < 32; i++) { + fpu.fpr[i].val64[0] = env->fpr[i].vreg.UD[0]; + } + + for (i = 0; i < 8; i++) { + fpu.fcc |= env->cf[i] << (8 * i); + } + + ret = kvm_vcpu_ioctl(cs, KVM_SET_FPU, &fpu); + if (ret < 0) { + trace_kvm_failed_put_fpu(strerror(errno)); + } + + return ret; +} + +void kvm_arch_reset_vcpu(CPULoongArchState *env) +{ + env->mp_state = KVM_MP_STATE_RUNNABLE; +} + +static int kvm_loongarch_get_mpstate(CPUState *cs) +{ + int ret = 0; + struct kvm_mp_state mp_state; + CPULoongArchState *env = cpu_env(cs); + + if (cap_has_mp_state) { + ret = kvm_vcpu_ioctl(cs, KVM_GET_MP_STATE, &mp_state); + if (ret) { + trace_kvm_failed_get_mpstate(strerror(errno)); + return ret; + } + env->mp_state = mp_state.mp_state; + } + + return ret; +} + +static int kvm_loongarch_put_mpstate(CPUState *cs) +{ + int ret = 0; + struct kvm_mp_state mp_state = { + .mp_state = cpu_env(cs)->mp_state + }; + + if (cap_has_mp_state) { + ret = kvm_vcpu_ioctl(cs, KVM_SET_MP_STATE, &mp_state); + if (ret) { + trace_kvm_failed_put_mpstate(strerror(errno)); + } + } + + return ret; +} + +static int kvm_loongarch_get_cpucfg(CPUState *cs) +{ + int i, ret = 0; + uint64_t val; + CPULoongArchState *env = cpu_env(cs); + + for (i = 0; i < 21; i++) { + ret = kvm_get_one_reg(cs, KVM_IOC_CPUCFG(i), &val); + if (ret < 0) { + trace_kvm_failed_get_cpucfg(strerror(errno)); + } + env->cpucfg[i] = (uint32_t)val; + } + return ret; +} + +static int kvm_check_cpucfg2(CPUState *cs) +{ + int ret; + uint64_t val; + struct kvm_device_attr attr = { + .group = KVM_LOONGARCH_VCPU_CPUCFG, + .attr = 2, + .addr = (uint64_t)&val, + }; + CPULoongArchState *env = cpu_env(cs); + + ret = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, &attr); + + if (!ret) { + kvm_vcpu_ioctl(cs, KVM_GET_DEVICE_ATTR, &attr); + env->cpucfg[2] &= val; + + if (FIELD_EX32(env->cpucfg[2], CPUCFG2, FP)) { + /* The FP minimal version is 1. */ + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, FP_VER, 1); + } + + if (FIELD_EX32(env->cpucfg[2], CPUCFG2, LLFTP)) { + /* The LLFTP minimal version is 1. */ + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LLFTP_VER, 1); + } + } + + return ret; +} + +static int kvm_loongarch_put_cpucfg(CPUState *cs) +{ + int i, ret = 0; + CPULoongArchState *env = cpu_env(cs); + uint64_t val; + + for (i = 0; i < 21; i++) { + if (i == 2) { + ret = kvm_check_cpucfg2(cs); + if (ret) { + return ret; + } + } + val = env->cpucfg[i]; + ret = kvm_set_one_reg(cs, KVM_IOC_CPUCFG(i), &val); + if (ret < 0) { + trace_kvm_failed_put_cpucfg(strerror(errno)); + } + } + return ret; +} + +int kvm_arch_get_registers(CPUState *cs) +{ + int ret; + + ret = kvm_loongarch_get_regs_core(cs); + if (ret) { + return ret; + } + + ret = kvm_loongarch_get_csr(cs); + if (ret) { + return ret; + } + + ret = kvm_loongarch_get_regs_fp(cs); + if (ret) { + return ret; + } + + ret = kvm_loongarch_get_mpstate(cs); + if (ret) { + return ret; + } + + ret = kvm_loongarch_get_cpucfg(cs); + return ret; +} + +int kvm_arch_put_registers(CPUState *cs, int level) +{ + int ret; + + ret = kvm_loongarch_put_regs_core(cs); + if (ret) { + return ret; + } + + ret = kvm_loongarch_put_csr(cs, level); + if (ret) { + return ret; + } + + ret = kvm_loongarch_put_regs_fp(cs); + if (ret) { + return ret; + } + + ret = kvm_loongarch_put_mpstate(cs); + if (ret) { + return ret; + } + + ret = kvm_loongarch_put_cpucfg(cs); + return ret; +} + +static void kvm_loongarch_vm_stage_change(void *opaque, bool running, + RunState state) +{ + int ret; + CPUState *cs = opaque; + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + + if (running) { + ret = kvm_set_one_reg(cs, KVM_REG_LOONGARCH_COUNTER, + &cpu->kvm_state_counter); + if (ret < 0) { + trace_kvm_failed_put_counter(strerror(errno)); + } + } else { + ret = kvm_get_one_reg(cs, KVM_REG_LOONGARCH_COUNTER, + &cpu->kvm_state_counter); + if (ret < 0) { + trace_kvm_failed_get_counter(strerror(errno)); + } + } +} + +int kvm_arch_init_vcpu(CPUState *cs) +{ + qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs); + return 0; +} + +int kvm_arch_destroy_vcpu(CPUState *cs) +{ + return 0; +} + +unsigned long kvm_arch_vcpu_id(CPUState *cs) +{ + return cs->cpu_index; +} + +int kvm_arch_release_virq_post(int virq) +{ + return 0; +} + +int kvm_arch_msi_data_to_gsi(uint32_t data) +{ + abort(); +} + +int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, + uint64_t address, uint32_t data, PCIDevice *dev) +{ + return 0; +} + +int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route, + int vector, PCIDevice *dev) +{ + return 0; +} + +void kvm_arch_init_irq_routing(KVMState *s) +{ +} + +int kvm_arch_get_default_type(MachineState *ms) +{ + return 0; +} + +int kvm_arch_init(MachineState *ms, KVMState *s) +{ + cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE); + return 0; +} + +int kvm_arch_irqchip_create(KVMState *s) +{ + return 0; +} + +void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) +{ +} + +MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) +{ + return MEMTXATTRS_UNSPECIFIED; +} + +int kvm_arch_process_async_events(CPUState *cs) +{ + return cs->halted; +} + +bool kvm_arch_stop_on_emulation_error(CPUState *cs) +{ + return true; +} + +bool kvm_arch_cpu_check_are_resettable(void) +{ + return true; +} + +int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) +{ + int ret = 0; + CPULoongArchState *env = cpu_env(cs); + MemTxAttrs attrs = {}; + + attrs.requester_id = env_cpu(env)->cpu_index; + + trace_kvm_arch_handle_exit(run->exit_reason); + switch (run->exit_reason) { + case KVM_EXIT_LOONGARCH_IOCSR: + address_space_rw(env->address_space_iocsr, + run->iocsr_io.phys_addr, + attrs, + run->iocsr_io.data, + run->iocsr_io.len, + run->iocsr_io.is_write); + break; + default: + ret = -1; + warn_report("KVM: unknown exit reason %d", run->exit_reason); + break; + } + return ret; +} + +int kvm_loongarch_set_interrupt(LoongArchCPU *cpu, int irq, int level) +{ + struct kvm_interrupt intr; + CPUState *cs = CPU(cpu); + + if (level) { + intr.irq = irq; + } else { + intr.irq = -irq; + } + + trace_kvm_set_intr(irq, level); + return kvm_vcpu_ioctl(cs, KVM_INTERRUPT, &intr); +} + +void kvm_arch_accel_class_init(ObjectClass *oc) +{ +} diff --git a/target/loongarch/kvm/kvm_loongarch.h b/target/loongarch/kvm/kvm_loongarch.h new file mode 100644 index 0000000000..d945b6bb82 --- /dev/null +++ b/target/loongarch/kvm/kvm_loongarch.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch kvm interface + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + */ + +#include "cpu.h" + +#ifndef QEMU_KVM_LOONGARCH_H +#define QEMU_KVM_LOONGARCH_H + +int kvm_loongarch_set_interrupt(LoongArchCPU *cpu, int irq, int level); +void kvm_arch_reset_vcpu(CPULoongArchState *env); + +#endif diff --git a/target/loongarch/kvm/meson.build b/target/loongarch/kvm/meson.build new file mode 100644 index 0000000000..2266de6ca9 --- /dev/null +++ b/target/loongarch/kvm/meson.build @@ -0,0 +1 @@ +loongarch_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c')) diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c new file mode 100644 index 0000000000..8721a5eb13 --- /dev/null +++ b/target/loongarch/loongarch-qmp-cmds.c @@ -0,0 +1,116 @@ +/* + * QEMU LoongArch CPU (monitor definitions) + * + * SPDX-FileCopyrightText: 2021 Loongson Technology Corporation Limited + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine-target.h" +#include "cpu.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qobject-input-visitor.h" +#include "qom/qom-qobject.h" + +static void loongarch_cpu_add_definition(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CpuDefinitionInfoList **cpu_list = user_data; + CpuDefinitionInfo *info = g_new0(CpuDefinitionInfo, 1); + const char *typename = object_class_get_name(oc); + + info->name = cpu_model_from_type(typename); + info->q_typename = g_strdup(typename); + + QAPI_LIST_PREPEND(*cpu_list, info); +} + +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) +{ + CpuDefinitionInfoList *cpu_list = NULL; + GSList *list; + + list = object_class_get_list(TYPE_LOONGARCH_CPU, false); + g_slist_foreach(list, loongarch_cpu_add_definition, &cpu_list); + g_slist_free(list); + + return cpu_list; +} + +static const char *cpu_model_advertised_features[] = { + "lsx", "lasx", NULL +}; + +CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + Visitor *visitor; + bool ok; + CpuModelExpansionInfo *expansion_info; + QDict *qdict_out; + ObjectClass *oc; + Object *obj; + const char *name; + int i; + + if (type != CPU_MODEL_EXPANSION_TYPE_STATIC) { + error_setg(errp, "The requested expansion type is not supported"); + return NULL; + } + + if (model->props) { + visitor = qobject_input_visitor_new(model->props); + if (!visit_start_struct(visitor, "model.props", NULL, 0, errp)) { + visit_free(visitor); + return NULL; + } + + ok = visit_check_struct(visitor, errp); + visit_end_struct(visitor, NULL); + visit_free(visitor); + if (!ok) { + return NULL; + } + } + + oc = cpu_class_by_name(TYPE_LOONGARCH_CPU, model->name); + if (!oc) { + error_setg(errp, "The CPU type '%s' is not a recognized LoongArch CPU type", + model->name); + return NULL; + } + + obj = object_new(object_class_get_name(oc)); + + expansion_info = g_new0(CpuModelExpansionInfo, 1); + expansion_info->model = g_malloc0(sizeof(*expansion_info->model)); + expansion_info->model->name = g_strdup(model->name); + + qdict_out = qdict_new(); + + i = 0; + while ((name = cpu_model_advertised_features[i++]) != NULL) { + ObjectProperty *prop = object_property_find(obj, name); + if (prop) { + QObject *value; + + assert(prop->get); + value = object_property_get_qobject(obj, name, &error_abort); + + qdict_put_obj(qdict_out, name, value); + } + } + + if (!qdict_size(qdict_out)) { + qobject_unref(qdict_out); + } else { + expansion_info->model->props = QOBJECT(qdict_out); + } + + object_unref(obj); + + return expansion_info; +} diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c index b1e523ea72..c7029fb9b4 100644 --- a/target/loongarch/machine.c +++ b/target/loongarch/machine.c @@ -8,14 +8,113 @@ #include "qemu/osdep.h" #include "cpu.h" #include "migration/cpu.h" -#include "internals.h" +#include "vec.h" + +static const VMStateDescription vmstate_fpu_reg = { + .name = "fpu_reg", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(UD(0), VReg), + VMSTATE_END_OF_LIST() + } +}; + +#define VMSTATE_FPU_REGS(_field, _state, _start) \ + VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, 32, 0, \ + vmstate_fpu_reg, fpr_t) + +static bool fpu_needed(void *opaque) +{ + LoongArchCPU *cpu = opaque; + + return FIELD_EX64(cpu->env.cpucfg[2], CPUCFG2, FP); +} + +static const VMStateDescription vmstate_fpu = { + .name = "cpu/fpu", + .version_id = 1, + .minimum_version_id = 1, + .needed = fpu_needed, + .fields = (const VMStateField[]) { + VMSTATE_FPU_REGS(env.fpr, LoongArchCPU, 0), + VMSTATE_UINT32(env.fcsr0, LoongArchCPU), + VMSTATE_BOOL_ARRAY(env.cf, LoongArchCPU, 8), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_lsxh_reg = { + .name = "lsxh_reg", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(UD(1), VReg), + VMSTATE_END_OF_LIST() + } +}; + +#define VMSTATE_LSXH_REGS(_field, _state, _start) \ + VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, 32, 0, \ + vmstate_lsxh_reg, fpr_t) + +static bool lsx_needed(void *opaque) +{ + LoongArchCPU *cpu = opaque; + + return FIELD_EX64(cpu->env.cpucfg[2], CPUCFG2, LSX); +} + +static const VMStateDescription vmstate_lsx = { + .name = "cpu/lsx", + .version_id = 1, + .minimum_version_id = 1, + .needed = lsx_needed, + .fields = (const VMStateField[]) { + VMSTATE_LSXH_REGS(env.fpr, LoongArchCPU, 0), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_lasxh_reg = { + .name = "lasxh_reg", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(UD(2), VReg), + VMSTATE_UINT64(UD(3), VReg), + VMSTATE_END_OF_LIST() + } +}; + +#define VMSTATE_LASXH_REGS(_field, _state, _start) \ + VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, 32, 0, \ + vmstate_lasxh_reg, fpr_t) + +static bool lasx_needed(void *opaque) +{ + LoongArchCPU *cpu = opaque; + + return FIELD_EX64(cpu->env.cpucfg[2], CPUCFG2, LASX); +} + +static const VMStateDescription vmstate_lasx = { + .name = "cpu/lasx", + .version_id = 1, + .minimum_version_id = 1, + .needed = lasx_needed, + .fields = (const VMStateField[]) { + VMSTATE_LASXH_REGS(env.fpr, LoongArchCPU, 0), + VMSTATE_END_OF_LIST() + }, +}; /* TLB state */ const VMStateDescription vmstate_tlb = { .name = "cpu/tlb", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(tlb_misc, LoongArchTLB), VMSTATE_UINT64(tlb_entry0, LoongArchTLB), VMSTATE_UINT64(tlb_entry1, LoongArchTLB), @@ -24,18 +123,13 @@ const VMStateDescription vmstate_tlb = { }; /* LoongArch CPU state */ - const VMStateDescription vmstate_loongarch_cpu = { .name = "cpu", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.gpr, LoongArchCPU, 32), VMSTATE_UINTTL(env.pc, LoongArchCPU), - VMSTATE_UINT64_ARRAY(env.fpr, LoongArchCPU, 32), - VMSTATE_UINT32(env.fcsr0, LoongArchCPU), - VMSTATE_BOOL_ARRAY(env.cf, LoongArchCPU, 8), /* Remaining CSRs */ VMSTATE_UINT64(env.CSR_CRMD, LoongArchCPU), @@ -99,4 +193,10 @@ const VMStateDescription vmstate_loongarch_cpu = { VMSTATE_END_OF_LIST() }, + .subsections = (const VMStateDescription * const []) { + &vmstate_fpu, + &vmstate_lsx, + &vmstate_lasx, + NULL + } }; diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build index 6376f9e84b..e002e9aaf6 100644 --- a/target/loongarch/meson.build +++ b/target/loongarch/meson.build @@ -3,28 +3,20 @@ gen = decodetree.process('insns.decode') loongarch_ss = ss.source_set() loongarch_ss.add(files( 'cpu.c', - 'disas.c', -)) -loongarch_tcg_ss = ss.source_set() -loongarch_tcg_ss.add(gen) -loongarch_tcg_ss.add(files( - 'fpu_helper.c', - 'op_helper.c', - 'translate.c', 'gdbstub.c', )) -loongarch_tcg_ss.add(zlib) -loongarch_softmmu_ss = ss.source_set() -loongarch_softmmu_ss.add(files( +loongarch_system_ss = ss.source_set() +loongarch_system_ss.add(files( + 'cpu_helper.c', + 'loongarch-qmp-cmds.c', 'machine.c', - 'tlb_helper.c', - 'constant_timer.c', - 'csr_helper.c', - 'iocsr_helper.c', )) -loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss]) +common_ss.add(when: 'CONFIG_LOONGARCH_DIS', if_true: [files('disas.c'), gen]) + +subdir('tcg') target_arch += {'loongarch': loongarch_ss} -target_softmmu_arch += {'loongarch': loongarch_softmmu_ss} +target_system_arch += {'loongarch': loongarch_system_ss} +subdir('kvm') diff --git a/target/loongarch/constant_timer.c b/target/loongarch/tcg/constant_timer.c similarity index 100% rename from target/loongarch/constant_timer.c rename to target/loongarch/tcg/constant_timer.c diff --git a/target/loongarch/csr_helper.c b/target/loongarch/tcg/csr_helper.c similarity index 89% rename from target/loongarch/csr_helper.c rename to target/loongarch/tcg/csr_helper.c index 24a9389364..15f94caefa 100644 --- a/target/loongarch/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -15,7 +15,6 @@ #include "exec/cpu_ldst.h" #include "hw/irq.h" #include "cpu-csr.h" -#include "tcg/tcg-ldst.h" target_ulong helper_csrrd_pgd(CPULoongArchState *env) { @@ -36,6 +35,15 @@ target_ulong helper_csrrd_pgd(CPULoongArchState *env) return v; } +target_ulong helper_csrrd_cpuid(CPULoongArchState *env) +{ + LoongArchCPU *lac = env_archcpu(env); + + env->CSR_CPUID = CPU(lac)->cpu_index; + + return env->CSR_CPUID; +} + target_ulong helper_csrrd_tval(CPULoongArchState *env) { LoongArchCPU *cpu = env_archcpu(env); @@ -81,7 +89,9 @@ target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val) int64_t old_v = 0; if (val & 0x1) { + bql_lock(); loongarch_cpu_set_irq(cpu, IRQ_TIMER, 0); + bql_unlock(); } return old_v; } diff --git a/target/loongarch/fpu_helper.c b/target/loongarch/tcg/fpu_helper.c similarity index 86% rename from target/loongarch/fpu_helper.c rename to target/loongarch/tcg/fpu_helper.c index 3d0cb8dd0d..f6753c5875 100644 --- a/target/loongarch/fpu_helper.c +++ b/target/loongarch/tcg/fpu_helper.c @@ -13,9 +13,6 @@ #include "fpu/softfloat.h" #include "internals.h" -#define FLOAT_TO_INT32_OVERFLOW 0x7fffffff -#define FLOAT_TO_INT64_OVERFLOW 0x7fffffffffffffffULL - static inline uint64_t nanbox_s(float32 fp) { return fp | MAKE_64BIT_MASK(32, 32); @@ -36,7 +33,7 @@ void restore_fp_status(CPULoongArchState *env) set_flush_to_zero(0, &env->fp_status); } -static int ieee_ex_to_loongarch(int xcpt) +int ieee_ex_to_loongarch(int xcpt) { int ret = 0; if (xcpt & float_flag_invalid) { @@ -544,9 +541,10 @@ uint64_t helper_ftintrm_l_d(CPULoongArchState *env, uint64_t fj) fd = float64_to_int64(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -561,9 +559,10 @@ uint64_t helper_ftintrm_l_s(CPULoongArchState *env, uint64_t fj) fd = float32_to_int64((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -578,9 +577,10 @@ uint64_t helper_ftintrm_w_d(CPULoongArchState *env, uint64_t fj) fd = (uint64_t)float64_to_int32(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -595,9 +595,10 @@ uint64_t helper_ftintrm_w_s(CPULoongArchState *env, uint64_t fj) fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -612,9 +613,10 @@ uint64_t helper_ftintrp_l_d(CPULoongArchState *env, uint64_t fj) fd = float64_to_int64(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -629,9 +631,10 @@ uint64_t helper_ftintrp_l_s(CPULoongArchState *env, uint64_t fj) fd = float32_to_int64((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -646,9 +649,10 @@ uint64_t helper_ftintrp_w_d(CPULoongArchState *env, uint64_t fj) fd = (uint64_t)float64_to_int32(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -663,9 +667,10 @@ uint64_t helper_ftintrp_w_s(CPULoongArchState *env, uint64_t fj) fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -679,9 +684,10 @@ uint64_t helper_ftintrz_l_d(CPULoongArchState *env, uint64_t fj) fd = float64_to_int64_round_to_zero(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -695,9 +701,10 @@ uint64_t helper_ftintrz_l_s(CPULoongArchState *env, uint64_t fj) fd = float32_to_int64_round_to_zero((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -711,9 +718,10 @@ uint64_t helper_ftintrz_w_d(CPULoongArchState *env, uint64_t fj) fd = (uint64_t)float64_to_int32_round_to_zero(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -727,9 +735,10 @@ uint64_t helper_ftintrz_w_s(CPULoongArchState *env, uint64_t fj) fd = float32_to_int32_round_to_zero((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return (uint64_t)fd; @@ -744,9 +753,10 @@ uint64_t helper_ftintrne_l_d(CPULoongArchState *env, uint64_t fj) fd = float64_to_int64(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -761,9 +771,10 @@ uint64_t helper_ftintrne_l_s(CPULoongArchState *env, uint64_t fj) fd = float32_to_int64((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -778,9 +789,10 @@ uint64_t helper_ftintrne_w_d(CPULoongArchState *env, uint64_t fj) fd = (uint64_t)float64_to_int32(fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -795,9 +807,10 @@ uint64_t helper_ftintrne_w_s(CPULoongArchState *env, uint64_t fj) fd = float32_to_int32((uint32_t)fj, &env->fp_status); set_float_rounding_mode(old_mode, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return (uint64_t)fd; @@ -808,9 +821,10 @@ uint64_t helper_ftint_l_d(CPULoongArchState *env, uint64_t fj) uint64_t fd; fd = float64_to_int64(fj, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -821,9 +835,10 @@ uint64_t helper_ftint_l_s(CPULoongArchState *env, uint64_t fj) uint64_t fd; fd = float32_to_int64((uint32_t)fj, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & - (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT64_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -834,9 +849,10 @@ uint64_t helper_ftint_w_s(CPULoongArchState *env, uint64_t fj) uint64_t fd; fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) - & (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; @@ -847,16 +863,17 @@ uint64_t helper_ftint_w_d(CPULoongArchState *env, uint64_t fj) uint64_t fd; fd = (uint64_t)float64_to_int32(fj, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) - & (float_flag_invalid | float_flag_overflow)) { - fd = FLOAT_TO_INT32_OVERFLOW; + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } } update_fcsr0(env, GETPC()); return fd; } -void helper_set_rounding_mode(CPULoongArchState *env, uint32_t fcsr0) +void helper_set_rounding_mode(CPULoongArchState *env) { - set_float_rounding_mode(ieee_rm[(fcsr0 >> FCSR0_RM) & 0x3], + set_float_rounding_mode(ieee_rm[(env->fcsr0 >> FCSR0_RM) & 0x3], &env->fp_status); } diff --git a/target/loongarch/insn_trans/trans_arith.c.inc b/target/loongarch/tcg/insn_trans/trans_arith.c.inc similarity index 68% rename from target/loongarch/insn_trans/trans_arith.c.inc rename to target/loongarch/tcg/insn_trans/trans_arith.c.inc index 8e45eadbc8..2be057e932 100644 --- a/target/loongarch/insn_trans/trans_arith.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_arith.c.inc @@ -72,7 +72,7 @@ static bool gen_pc(DisasContext *ctx, arg_r_i *a, target_ulong (*func)(target_ulong, int)) { TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); - target_ulong addr = func(ctx->base.pc_next, a->imm); + target_ulong addr = make_address_pc(ctx, func(ctx->base.pc_next, a->imm)); tcg_gen_movi_tl(dest, addr); gen_set_gpr(a->rd, dest, EXT_NONE); @@ -100,14 +100,12 @@ static void gen_mulh_d(TCGv dest, TCGv src1, TCGv src2) { TCGv discard = tcg_temp_new(); tcg_gen_muls2_tl(discard, dest, src1, src2); - tcg_temp_free(discard); } static void gen_mulh_du(TCGv dest, TCGv src1, TCGv src2) { TCGv discard = tcg_temp_new(); tcg_gen_mulu2_tl(discard, dest, src1, src2); - tcg_temp_free(discard); } static void prep_divisor_d(TCGv ret, TCGv src1, TCGv src2) @@ -129,9 +127,6 @@ static void prep_divisor_d(TCGv ret, TCGv src1, TCGv src2) tcg_gen_and_tl(ret, ret, t0); tcg_gen_or_tl(ret, ret, t1); tcg_gen_movcond_tl(TCG_COND_NE, ret, ret, zero, ret, src2); - - tcg_temp_free(t0); - tcg_temp_free(t1); } static void prep_divisor_du(TCGv ret, TCGv src2) @@ -152,7 +147,6 @@ static void gen_div_d(TCGv dest, TCGv src1, TCGv src2) TCGv t0 = tcg_temp_new(); prep_divisor_d(t0, src1, src2); tcg_gen_div_tl(dest, src1, t0); - tcg_temp_free(t0); } static void gen_rem_d(TCGv dest, TCGv src1, TCGv src2) @@ -160,7 +154,6 @@ static void gen_rem_d(TCGv dest, TCGv src1, TCGv src2) TCGv t0 = tcg_temp_new(); prep_divisor_d(t0, src1, src2); tcg_gen_rem_tl(dest, src1, t0); - tcg_temp_free(t0); } static void gen_div_du(TCGv dest, TCGv src1, TCGv src2) @@ -168,7 +161,6 @@ static void gen_div_du(TCGv dest, TCGv src1, TCGv src2) TCGv t0 = tcg_temp_new(); prep_divisor_du(t0, src2); tcg_gen_divu_tl(dest, src1, t0); - tcg_temp_free(t0); } static void gen_rem_du(TCGv dest, TCGv src1, TCGv src2) @@ -176,7 +168,6 @@ static void gen_rem_du(TCGv dest, TCGv src1, TCGv src2) TCGv t0 = tcg_temp_new(); prep_divisor_du(t0, src2); tcg_gen_remu_tl(dest, src1, t0); - tcg_temp_free(t0); } static void gen_div_w(TCGv dest, TCGv src1, TCGv src2) @@ -185,7 +176,6 @@ static void gen_div_w(TCGv dest, TCGv src1, TCGv src2) /* We need not check for integer overflow for div_w. */ prep_divisor_du(t0, src2); tcg_gen_div_tl(dest, src1, t0); - tcg_temp_free(t0); } static void gen_rem_w(TCGv dest, TCGv src1, TCGv src2) @@ -194,7 +184,6 @@ static void gen_rem_w(TCGv dest, TCGv src1, TCGv src2) /* We need not check for integer overflow for rem_w. */ prep_divisor_du(t0, src2); tcg_gen_rem_tl(dest, src1, t0); - tcg_temp_free(t0); } static void gen_alsl(TCGv dest, TCGv src1, TCGv src2, target_long sa) @@ -202,7 +191,6 @@ static void gen_alsl(TCGv dest, TCGv src1, TCGv src2, target_long sa) TCGv t0 = tcg_temp_new(); tcg_gen_shli_tl(t0, src1, sa); tcg_gen_add_tl(dest, t0, src2); - tcg_temp_free(t0); } static bool trans_lu32i_d(DisasContext *ctx, arg_lu32i_d *a) @@ -211,6 +199,10 @@ static bool trans_lu32i_d(DisasContext *ctx, arg_lu32i_d *a) TCGv src1 = gpr_src(ctx, a->rd, EXT_NONE); TCGv src2 = tcg_constant_tl(a->imm); + if (!avail_64(ctx)) { + return false; + } + tcg_gen_deposit_tl(dest, src1, src2, 32, 32); gen_set_gpr(a->rd, dest, EXT_NONE); @@ -223,6 +215,10 @@ static bool trans_lu52i_d(DisasContext *ctx, arg_lu52i_d *a) TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); TCGv src2 = tcg_constant_tl(a->imm); + if (!avail_64(ctx)) { + return false; + } + tcg_gen_deposit_tl(dest, src1, src2, 52, 12); gen_set_gpr(a->rd, dest, EXT_NONE); @@ -254,51 +250,55 @@ static bool trans_addu16i_d(DisasContext *ctx, arg_addu16i_d *a) TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + if (!avail_64(ctx)) { + return false; + } + tcg_gen_addi_tl(dest, src1, a->imm << 16); gen_set_gpr(a->rd, dest, EXT_NONE); return true; } -TRANS(add_w, gen_rrr, EXT_NONE, EXT_NONE, EXT_SIGN, tcg_gen_add_tl) -TRANS(add_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_add_tl) -TRANS(sub_w, gen_rrr, EXT_NONE, EXT_NONE, EXT_SIGN, tcg_gen_sub_tl) -TRANS(sub_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_sub_tl) -TRANS(and, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_and_tl) -TRANS(or, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_or_tl) -TRANS(xor, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_xor_tl) -TRANS(nor, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_nor_tl) -TRANS(andn, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_andc_tl) -TRANS(orn, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_orc_tl) -TRANS(slt, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_slt) -TRANS(sltu, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sltu) -TRANS(mul_w, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, tcg_gen_mul_tl) -TRANS(mul_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_mul_tl) -TRANS(mulh_w, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_NONE, gen_mulh_w) -TRANS(mulh_wu, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_NONE, gen_mulh_w) -TRANS(mulh_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_mulh_d) -TRANS(mulh_du, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_mulh_du) -TRANS(mulw_d_w, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_NONE, tcg_gen_mul_tl) -TRANS(mulw_d_wu, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_NONE, tcg_gen_mul_tl) -TRANS(div_w, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, gen_div_w) -TRANS(mod_w, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, gen_rem_w) -TRANS(div_wu, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_SIGN, gen_div_du) -TRANS(mod_wu, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_SIGN, gen_rem_du) -TRANS(div_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_div_d) -TRANS(mod_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rem_d) -TRANS(div_du, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_div_du) -TRANS(mod_du, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rem_du) -TRANS(slti, gen_rri_v, EXT_NONE, EXT_NONE, gen_slt) -TRANS(sltui, gen_rri_v, EXT_NONE, EXT_NONE, gen_sltu) -TRANS(addi_w, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_addi_tl) -TRANS(addi_d, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_addi_tl) -TRANS(alsl_w, gen_rrr_sa, EXT_NONE, EXT_SIGN, gen_alsl) -TRANS(alsl_wu, gen_rrr_sa, EXT_NONE, EXT_ZERO, gen_alsl) -TRANS(alsl_d, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_alsl) -TRANS(pcaddi, gen_pc, gen_pcaddi) -TRANS(pcalau12i, gen_pc, gen_pcalau12i) -TRANS(pcaddu12i, gen_pc, gen_pcaddu12i) -TRANS(pcaddu18i, gen_pc, gen_pcaddu18i) -TRANS(andi, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_andi_tl) -TRANS(ori, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_ori_tl) -TRANS(xori, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_xori_tl) +TRANS(add_w, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_SIGN, tcg_gen_add_tl) +TRANS(add_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_add_tl) +TRANS(sub_w, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_SIGN, tcg_gen_sub_tl) +TRANS(sub_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_sub_tl) +TRANS(and, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_and_tl) +TRANS(or, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_or_tl) +TRANS(xor, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_xor_tl) +TRANS(nor, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_nor_tl) +TRANS(andn, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_andc_tl) +TRANS(orn, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_orc_tl) +TRANS(slt, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_slt) +TRANS(sltu, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sltu) +TRANS(mul_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, tcg_gen_mul_tl) +TRANS(mul_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_mul_tl) +TRANS(mulh_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_NONE, gen_mulh_w) +TRANS(mulh_wu, ALL, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_NONE, gen_mulh_w) +TRANS(mulh_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_mulh_d) +TRANS(mulh_du, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_mulh_du) +TRANS(mulw_d_w, 64, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_NONE, tcg_gen_mul_tl) +TRANS(mulw_d_wu, 64, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_NONE, tcg_gen_mul_tl) +TRANS(div_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, gen_div_w) +TRANS(mod_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, gen_rem_w) +TRANS(div_wu, ALL, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_SIGN, gen_div_du) +TRANS(mod_wu, ALL, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_SIGN, gen_rem_du) +TRANS(div_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_div_d) +TRANS(mod_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rem_d) +TRANS(div_du, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_div_du) +TRANS(mod_du, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rem_du) +TRANS(slti, ALL, gen_rri_v, EXT_NONE, EXT_NONE, gen_slt) +TRANS(sltui, ALL, gen_rri_v, EXT_NONE, EXT_NONE, gen_sltu) +TRANS(addi_w, ALL, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_addi_tl) +TRANS(addi_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_addi_tl) +TRANS(alsl_w, ALL, gen_rrr_sa, EXT_NONE, EXT_SIGN, gen_alsl) +TRANS(alsl_wu, 64, gen_rrr_sa, EXT_NONE, EXT_ZERO, gen_alsl) +TRANS(alsl_d, 64, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_alsl) +TRANS(pcaddi, ALL, gen_pc, gen_pcaddi) +TRANS(pcalau12i, ALL, gen_pc, gen_pcalau12i) +TRANS(pcaddu12i, ALL, gen_pc, gen_pcaddu12i) +TRANS(pcaddu18i, 64, gen_pc, gen_pcaddu18i) +TRANS(andi, ALL, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_andi_tl) +TRANS(ori, ALL, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_ori_tl) +TRANS(xori, ALL, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_xori_tl) diff --git a/target/loongarch/tcg/insn_trans/trans_atomic.c.inc b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc new file mode 100644 index 0000000000..974bc2a70f --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +static bool gen_ll(DisasContext *ctx, arg_rr_i *a, MemOp mop) +{ + TCGv t1 = tcg_temp_new(); + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + TCGv t0 = make_address_i(ctx, src1, a->imm); + + tcg_gen_qemu_ld_i64(t1, t0, ctx->mem_idx, mop); + tcg_gen_st_tl(t0, tcg_env, offsetof(CPULoongArchState, lladdr)); + tcg_gen_st_tl(t1, tcg_env, offsetof(CPULoongArchState, llval)); + gen_set_gpr(a->rd, t1, EXT_NONE); + + return true; +} + +static bool gen_sc(DisasContext *ctx, arg_rr_i *a, MemOp mop) +{ + TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + TCGv src2 = gpr_src(ctx, a->rd, EXT_NONE); + TCGv t0 = tcg_temp_new(); + TCGv val = tcg_temp_new(); + + TCGLabel *l1 = gen_new_label(); + TCGLabel *done = gen_new_label(); + + tcg_gen_addi_tl(t0, src1, a->imm); + tcg_gen_brcond_tl(TCG_COND_EQ, t0, cpu_lladdr, l1); + tcg_gen_movi_tl(dest, 0); + tcg_gen_br(done); + + gen_set_label(l1); + tcg_gen_mov_tl(val, src2); + /* generate cmpxchg */ + tcg_gen_atomic_cmpxchg_tl(t0, cpu_lladdr, cpu_llval, + val, ctx->mem_idx, mop); + tcg_gen_setcond_tl(TCG_COND_EQ, dest, t0, cpu_llval); + gen_set_label(done); + gen_set_gpr(a->rd, dest, EXT_NONE); + + return true; +} + +static bool gen_am(DisasContext *ctx, arg_rrr *a, + void (*func)(TCGv, TCGv, TCGv, TCGArg, MemOp), + MemOp mop) +{ + TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); + TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); + TCGv val = gpr_src(ctx, a->rk, EXT_NONE); + + if (a->rd != 0 && (a->rj == a->rd || a->rk == a->rd)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Warning: source register overlaps destination register" + "in atomic insn at pc=0x" TARGET_FMT_lx "\n", + ctx->base.pc_next - 4); + return false; + } + + addr = make_address_i(ctx, addr, 0); + + func(dest, addr, val, ctx->mem_idx, mop); + gen_set_gpr(a->rd, dest, EXT_NONE); + + return true; +} + +TRANS(ll_w, ALL, gen_ll, MO_TESL) +TRANS(sc_w, ALL, gen_sc, MO_TESL) +TRANS(ll_d, 64, gen_ll, MO_TEUQ) +TRANS(sc_d, 64, gen_sc, MO_TEUQ) +TRANS(amswap_w, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL) +TRANS(amswap_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) +TRANS(amadd_w, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL) +TRANS(amadd_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) +TRANS(amand_w, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL) +TRANS(amand_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) +TRANS(amor_w, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL) +TRANS(amor_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) +TRANS(amxor_w, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL) +TRANS(amxor_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) +TRANS(ammax_w, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL) +TRANS(ammax_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) +TRANS(ammin_w, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL) +TRANS(ammin_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) +TRANS(ammax_wu, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL) +TRANS(ammax_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) +TRANS(ammin_wu, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL) +TRANS(ammin_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) +TRANS(amswap_db_w, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL) +TRANS(amswap_db_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) +TRANS(amadd_db_w, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL) +TRANS(amadd_db_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) +TRANS(amand_db_w, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL) +TRANS(amand_db_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) +TRANS(amor_db_w, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL) +TRANS(amor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) +TRANS(amxor_db_w, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL) +TRANS(amxor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) +TRANS(ammax_db_w, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL) +TRANS(ammax_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) +TRANS(ammin_db_w, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL) +TRANS(ammin_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) +TRANS(ammax_db_wu, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL) +TRANS(ammax_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) +TRANS(ammin_db_wu, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL) +TRANS(ammin_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) diff --git a/target/loongarch/insn_trans/trans_bit.c.inc b/target/loongarch/tcg/insn_trans/trans_bit.c.inc similarity index 61% rename from target/loongarch/insn_trans/trans_bit.c.inc rename to target/loongarch/tcg/insn_trans/trans_bit.c.inc index 9337714ec4..ee5fa003ce 100644 --- a/target/loongarch/insn_trans/trans_bit.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_bit.c.inc @@ -27,26 +27,34 @@ static void gen_bytepick_d(TCGv dest, TCGv src1, TCGv src2, target_long sa) tcg_gen_extract2_i64(dest, src1, src2, (64 - sa * 8)); } -static void gen_bstrins(TCGv dest, TCGv src1, - unsigned int ls, unsigned int len) +static bool gen_bstrins(DisasContext *ctx, arg_rr_ms_ls *a, + DisasExtend dst_ext) { - tcg_gen_deposit_tl(dest, dest, src1, ls, len); -} - -static bool gen_rr_ms_ls(DisasContext *ctx, arg_rr_ms_ls *a, - DisasExtend src_ext, DisasExtend dst_ext, - void (*func)(TCGv, TCGv, unsigned int, unsigned int)) -{ - TCGv dest = gpr_dst(ctx, a->rd, dst_ext); - TCGv src1 = gpr_src(ctx, a->rj, src_ext); + TCGv src1 = gpr_src(ctx, a->rd, EXT_NONE); + TCGv src2 = gpr_src(ctx, a->rj, EXT_NONE); + TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); if (a->ls > a->ms) { return false; } - func(dest, src1, a->ls, a->ms - a->ls + 1); + tcg_gen_deposit_tl(dest, src1, src2, a->ls, a->ms - a->ls + 1); gen_set_gpr(a->rd, dest, dst_ext); + return true; +} +static bool gen_bstrpick(DisasContext *ctx, arg_rr_ms_ls *a, + DisasExtend dst_ext) +{ + TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + + if (a->ls > a->ms) { + return false; + } + + tcg_gen_extract_tl(dest, src1, a->ls, a->ms - a->ls + 1); + gen_set_gpr(a->rd, dest, dst_ext); return true; } @@ -114,9 +122,6 @@ static void gen_revb_2h(TCGv dest, TCGv src1) tcg_gen_and_tl(t1, src1, mask); tcg_gen_shli_tl(t1, t1, 8); tcg_gen_or_tl(dest, t0, t1); - - tcg_temp_free(t0); - tcg_temp_free(t1); } static void gen_revb_4h(TCGv dest, TCGv src1) @@ -130,9 +135,6 @@ static void gen_revb_4h(TCGv dest, TCGv src1) tcg_gen_and_tl(t1, src1, mask); tcg_gen_shli_tl(t1, t1, 8); tcg_gen_or_tl(dest, t0, t1); - - tcg_temp_free(t0); - tcg_temp_free(t1); } static void gen_revh_2w(TCGv dest, TCGv src1) @@ -146,9 +148,6 @@ static void gen_revh_2w(TCGv dest, TCGv src1) tcg_gen_and_i64(t0, t0, mask); tcg_gen_shli_i64(t1, t1, 16); tcg_gen_or_i64(dest, t1, t0); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } static void gen_revh_d(TCGv dest, TCGv src1) @@ -163,9 +162,6 @@ static void gen_revh_d(TCGv dest, TCGv src1) tcg_gen_shli_tl(t0, t0, 16); tcg_gen_or_tl(t0, t0, t1); tcg_gen_rotri_tl(dest, t0, 32); - - tcg_temp_free(t0); - tcg_temp_free(t1); } static void gen_maskeqz(TCGv dest, TCGv src1, TCGv src2) @@ -182,31 +178,31 @@ static void gen_masknez(TCGv dest, TCGv src1, TCGv src2) tcg_gen_movcond_tl(TCG_COND_NE, dest, src2, zero, zero, src1); } -TRANS(ext_w_h, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_ext16s_tl) -TRANS(ext_w_b, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_ext8s_tl) -TRANS(clo_w, gen_rr, EXT_NONE, EXT_NONE, gen_clo_w) -TRANS(clz_w, gen_rr, EXT_ZERO, EXT_NONE, gen_clz_w) -TRANS(cto_w, gen_rr, EXT_NONE, EXT_NONE, gen_cto_w) -TRANS(ctz_w, gen_rr, EXT_NONE, EXT_NONE, gen_ctz_w) -TRANS(clo_d, gen_rr, EXT_NONE, EXT_NONE, gen_clo_d) -TRANS(clz_d, gen_rr, EXT_NONE, EXT_NONE, gen_clz_d) -TRANS(cto_d, gen_rr, EXT_NONE, EXT_NONE, gen_cto_d) -TRANS(ctz_d, gen_rr, EXT_NONE, EXT_NONE, gen_ctz_d) -TRANS(revb_2h, gen_rr, EXT_NONE, EXT_SIGN, gen_revb_2h) -TRANS(revb_4h, gen_rr, EXT_NONE, EXT_NONE, gen_revb_4h) -TRANS(revb_2w, gen_rr, EXT_NONE, EXT_NONE, gen_revb_2w) -TRANS(revb_d, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_bswap64_i64) -TRANS(revh_2w, gen_rr, EXT_NONE, EXT_NONE, gen_revh_2w) -TRANS(revh_d, gen_rr, EXT_NONE, EXT_NONE, gen_revh_d) -TRANS(bitrev_4b, gen_rr, EXT_ZERO, EXT_SIGN, gen_helper_bitswap) -TRANS(bitrev_8b, gen_rr, EXT_NONE, EXT_NONE, gen_helper_bitswap) -TRANS(bitrev_w, gen_rr, EXT_NONE, EXT_SIGN, gen_helper_bitrev_w) -TRANS(bitrev_d, gen_rr, EXT_NONE, EXT_NONE, gen_helper_bitrev_d) -TRANS(maskeqz, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_maskeqz) -TRANS(masknez, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_masknez) -TRANS(bytepick_w, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_bytepick_w) -TRANS(bytepick_d, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_bytepick_d) -TRANS(bstrins_w, gen_rr_ms_ls, EXT_NONE, EXT_NONE, gen_bstrins) -TRANS(bstrins_d, gen_rr_ms_ls, EXT_NONE, EXT_NONE, gen_bstrins) -TRANS(bstrpick_w, gen_rr_ms_ls, EXT_NONE, EXT_SIGN, tcg_gen_extract_tl) -TRANS(bstrpick_d, gen_rr_ms_ls, EXT_NONE, EXT_NONE, tcg_gen_extract_tl) +TRANS(ext_w_h, ALL, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_ext16s_tl) +TRANS(ext_w_b, ALL, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_ext8s_tl) +TRANS(clo_w, ALL, gen_rr, EXT_NONE, EXT_NONE, gen_clo_w) +TRANS(clz_w, ALL, gen_rr, EXT_ZERO, EXT_NONE, gen_clz_w) +TRANS(cto_w, ALL, gen_rr, EXT_NONE, EXT_NONE, gen_cto_w) +TRANS(ctz_w, ALL, gen_rr, EXT_NONE, EXT_NONE, gen_ctz_w) +TRANS(clo_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_clo_d) +TRANS(clz_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_clz_d) +TRANS(cto_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_cto_d) +TRANS(ctz_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_ctz_d) +TRANS(revb_2h, ALL, gen_rr, EXT_NONE, EXT_SIGN, gen_revb_2h) +TRANS(revb_4h, 64, gen_rr, EXT_NONE, EXT_NONE, gen_revb_4h) +TRANS(revb_2w, 64, gen_rr, EXT_NONE, EXT_NONE, gen_revb_2w) +TRANS(revb_d, 64, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_bswap64_i64) +TRANS(revh_2w, 64, gen_rr, EXT_NONE, EXT_NONE, gen_revh_2w) +TRANS(revh_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_revh_d) +TRANS(bitrev_4b, ALL, gen_rr, EXT_ZERO, EXT_SIGN, gen_helper_bitswap) +TRANS(bitrev_8b, 64, gen_rr, EXT_NONE, EXT_NONE, gen_helper_bitswap) +TRANS(bitrev_w, ALL, gen_rr, EXT_NONE, EXT_SIGN, gen_helper_bitrev_w) +TRANS(bitrev_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_helper_bitrev_d) +TRANS(maskeqz, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_maskeqz) +TRANS(masknez, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_masknez) +TRANS(bytepick_w, ALL, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_bytepick_w) +TRANS(bytepick_d, 64, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_bytepick_d) +TRANS(bstrins_w, ALL, gen_bstrins, EXT_SIGN) +TRANS(bstrins_d, 64, gen_bstrins, EXT_NONE) +TRANS(bstrpick_w, ALL, gen_bstrpick, EXT_SIGN) +TRANS(bstrpick_d, 64, gen_bstrpick, EXT_NONE) diff --git a/target/loongarch/insn_trans/trans_branch.c.inc b/target/loongarch/tcg/insn_trans/trans_branch.c.inc similarity index 72% rename from target/loongarch/insn_trans/trans_branch.c.inc rename to target/loongarch/tcg/insn_trans/trans_branch.c.inc index 65dbdff41e..221e5159db 100644 --- a/target/loongarch/insn_trans/trans_branch.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_branch.c.inc @@ -12,7 +12,7 @@ static bool trans_b(DisasContext *ctx, arg_b *a) static bool trans_bl(DisasContext *ctx, arg_bl *a) { - tcg_gen_movi_tl(cpu_gpr[1], ctx->base.pc_next + 4); + tcg_gen_movi_tl(cpu_gpr[1], make_address_pc(ctx, ctx->base.pc_next + 4)); gen_goto_tb(ctx, 0, ctx->base.pc_next + a->offs); ctx->base.is_jmp = DISAS_NORETURN; return true; @@ -23,8 +23,9 @@ static bool trans_jirl(DisasContext *ctx, arg_jirl *a) TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - tcg_gen_addi_tl(cpu_pc, src1, a->offs); - tcg_gen_movi_tl(dest, ctx->base.pc_next + 4); + TCGv addr = make_address_i(ctx, src1, a->imm); + tcg_gen_mov_tl(cpu_pc, addr); + tcg_gen_movi_tl(dest, make_address_pc(ctx, ctx->base.pc_next + 4)); gen_set_gpr(a->rd, dest, EXT_NONE); tcg_gen_lookup_and_goto_ptr(); ctx->base.is_jmp = DISAS_NORETURN; @@ -65,19 +66,19 @@ static bool gen_cz_bc(DisasContext *ctx, arg_c_offs *a, TCGCond cond) TCGv src1 = tcg_temp_new(); TCGv src2 = tcg_constant_tl(0); - tcg_gen_ld8u_tl(src1, cpu_env, + tcg_gen_ld8u_tl(src1, tcg_env, offsetof(CPULoongArchState, cf[a->cj])); gen_bc(ctx, src1, src2, a->offs, cond); return true; } -TRANS(beq, gen_rr_bc, TCG_COND_EQ) -TRANS(bne, gen_rr_bc, TCG_COND_NE) -TRANS(blt, gen_rr_bc, TCG_COND_LT) -TRANS(bge, gen_rr_bc, TCG_COND_GE) -TRANS(bltu, gen_rr_bc, TCG_COND_LTU) -TRANS(bgeu, gen_rr_bc, TCG_COND_GEU) -TRANS(beqz, gen_rz_bc, TCG_COND_EQ) -TRANS(bnez, gen_rz_bc, TCG_COND_NE) -TRANS(bceqz, gen_cz_bc, TCG_COND_EQ) -TRANS(bcnez, gen_cz_bc, TCG_COND_NE) +TRANS(beq, ALL, gen_rr_bc, TCG_COND_EQ) +TRANS(bne, ALL, gen_rr_bc, TCG_COND_NE) +TRANS(blt, ALL, gen_rr_bc, TCG_COND_LT) +TRANS(bge, ALL, gen_rr_bc, TCG_COND_GE) +TRANS(bltu, ALL, gen_rr_bc, TCG_COND_LTU) +TRANS(bgeu, ALL, gen_rr_bc, TCG_COND_GEU) +TRANS(beqz, ALL, gen_rz_bc, TCG_COND_EQ) +TRANS(bnez, ALL, gen_rz_bc, TCG_COND_NE) +TRANS(bceqz, 64, gen_cz_bc, TCG_COND_EQ) +TRANS(bcnez, 64, gen_cz_bc, TCG_COND_NE) diff --git a/target/loongarch/insn_trans/trans_extra.c.inc b/target/loongarch/tcg/insn_trans/trans_extra.c.inc similarity index 67% rename from target/loongarch/insn_trans/trans_extra.c.inc rename to target/loongarch/tcg/insn_trans/trans_extra.c.inc index ad713cd61e..cfa361fecf 100644 --- a/target/loongarch/insn_trans/trans_extra.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_extra.c.inc @@ -20,7 +20,11 @@ static bool trans_asrtle_d(DisasContext *ctx, arg_asrtle_d * a) TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - gen_helper_asrtle_d(cpu_env, src1, src2); + if (!avail_64(ctx)) { + return false; + } + + gen_helper_asrtle_d(tcg_env, src1, src2); return true; } @@ -29,7 +33,11 @@ static bool trans_asrtgt_d(DisasContext *ctx, arg_asrtgt_d * a) TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - gen_helper_asrtgt_d(cpu_env, src1, src2); + if (!avail_64(ctx)) { + return false; + } + + gen_helper_asrtgt_d(tcg_env, src1, src2); return true; } @@ -39,14 +47,12 @@ static bool gen_rdtime(DisasContext *ctx, arg_rr *a, TCGv dst1 = gpr_dst(ctx, a->rd, EXT_NONE); TCGv dst2 = gpr_dst(ctx, a->rj, EXT_NONE); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_rdtime_d(dst1, cpu_env); + translator_io_start(&ctx->base); + gen_helper_rdtime_d(dst1, tcg_env); if (word) { tcg_gen_sextract_tl(dst1, dst1, high ? 32 : 0, 32); } - tcg_gen_ld_i64(dst2, cpu_env, offsetof(CPULoongArchState, CSR_TID)); + tcg_gen_ld_i64(dst2, tcg_env, offsetof(CPULoongArchState, CSR_TID)); return true; } @@ -71,7 +77,7 @@ static bool trans_cpucfg(DisasContext *ctx, arg_cpucfg *a) TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - gen_helper_cpucfg(dest, cpu_env, src1); + gen_helper_cpucfg(dest, tcg_env, src1); gen_set_gpr(a->rd, dest, EXT_NONE); return true; @@ -91,11 +97,11 @@ static bool gen_crc(DisasContext *ctx, arg_rrr *a, return true; } -TRANS(crc_w_b_w, gen_crc, gen_helper_crc32, tcg_constant_tl(1)) -TRANS(crc_w_h_w, gen_crc, gen_helper_crc32, tcg_constant_tl(2)) -TRANS(crc_w_w_w, gen_crc, gen_helper_crc32, tcg_constant_tl(4)) -TRANS(crc_w_d_w, gen_crc, gen_helper_crc32, tcg_constant_tl(8)) -TRANS(crcc_w_b_w, gen_crc, gen_helper_crc32c, tcg_constant_tl(1)) -TRANS(crcc_w_h_w, gen_crc, gen_helper_crc32c, tcg_constant_tl(2)) -TRANS(crcc_w_w_w, gen_crc, gen_helper_crc32c, tcg_constant_tl(4)) -TRANS(crcc_w_d_w, gen_crc, gen_helper_crc32c, tcg_constant_tl(8)) +TRANS(crc_w_b_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(1)) +TRANS(crc_w_h_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(2)) +TRANS(crc_w_w_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(4)) +TRANS(crc_w_d_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(8)) +TRANS(crcc_w_b_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(1)) +TRANS(crcc_w_h_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(2)) +TRANS(crcc_w_w_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(4)) +TRANS(crcc_w_d_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(8)) diff --git a/target/loongarch/tcg/insn_trans/trans_farith.c.inc b/target/loongarch/tcg/insn_trans/trans_farith.c.inc new file mode 100644 index 0000000000..f4a0dea727 --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_farith.c.inc @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef CONFIG_USER_ONLY +#define CHECK_FPE do { \ + if ((ctx->base.tb->flags & HW_FLAGS_EUEN_FPE) == 0) { \ + generate_exception(ctx, EXCCODE_FPD); \ + return true; \ + } \ +} while (0) +#else +#define CHECK_FPE +#endif + +static bool gen_fff(DisasContext *ctx, arg_fff *a, + void (*func)(TCGv, TCGv_env, TCGv, TCGv)) +{ + TCGv dest = get_fpr(ctx, a->fd); + TCGv src1 = get_fpr(ctx, a->fj); + TCGv src2 = get_fpr(ctx, a->fk); + + CHECK_FPE; + + func(dest, tcg_env, src1, src2); + set_fpr(a->fd, dest); + + return true; +} + +static bool gen_ff(DisasContext *ctx, arg_ff *a, + void (*func)(TCGv, TCGv_env, TCGv)) +{ + TCGv dest = get_fpr(ctx, a->fd); + TCGv src = get_fpr(ctx, a->fj); + + CHECK_FPE; + + func(dest, tcg_env, src); + set_fpr(a->fd, dest); + + return true; +} + +static bool gen_muladd(DisasContext *ctx, arg_ffff *a, + void (*func)(TCGv, TCGv_env, TCGv, TCGv, TCGv, TCGv_i32), + int flag) +{ + TCGv_i32 tflag = tcg_constant_i32(flag); + TCGv dest = get_fpr(ctx, a->fd); + TCGv src1 = get_fpr(ctx, a->fj); + TCGv src2 = get_fpr(ctx, a->fk); + TCGv src3 = get_fpr(ctx, a->fa); + + CHECK_FPE; + + func(dest, tcg_env, src1, src2, src3, tflag); + set_fpr(a->fd, dest); + + return true; +} + +static bool trans_fcopysign_s(DisasContext *ctx, arg_fcopysign_s *a) +{ + TCGv dest = get_fpr(ctx, a->fd); + TCGv src1 = get_fpr(ctx, a->fk); + TCGv src2 = get_fpr(ctx, a->fj); + + if (!avail_FP_SP(ctx)) { + return false; + } + + CHECK_FPE; + + tcg_gen_deposit_i64(dest, src1, src2, 0, 31); + set_fpr(a->fd, dest); + + return true; +} + +static bool trans_fcopysign_d(DisasContext *ctx, arg_fcopysign_d *a) +{ + TCGv dest = get_fpr(ctx, a->fd); + TCGv src1 = get_fpr(ctx, a->fk); + TCGv src2 = get_fpr(ctx, a->fj); + + if (!avail_FP_DP(ctx)) { + return false; + } + + CHECK_FPE; + + tcg_gen_deposit_i64(dest, src1, src2, 0, 63); + set_fpr(a->fd, dest); + + return true; +} + +static bool trans_fabs_s(DisasContext *ctx, arg_fabs_s *a) +{ + TCGv dest = get_fpr(ctx, a->fd); + TCGv src = get_fpr(ctx, a->fj); + + if (!avail_FP_SP(ctx)) { + return false; + } + + CHECK_FPE; + + tcg_gen_andi_i64(dest, src, MAKE_64BIT_MASK(0, 31)); + gen_nanbox_s(dest, dest); + set_fpr(a->fd, dest); + + return true; +} + +static bool trans_fabs_d(DisasContext *ctx, arg_fabs_d *a) +{ + TCGv dest = get_fpr(ctx, a->fd); + TCGv src = get_fpr(ctx, a->fj); + + if (!avail_FP_DP(ctx)) { + return false; + } + + CHECK_FPE; + + tcg_gen_andi_i64(dest, src, MAKE_64BIT_MASK(0, 63)); + set_fpr(a->fd, dest); + + return true; +} + +static bool trans_fneg_s(DisasContext *ctx, arg_fneg_s *a) +{ + TCGv dest = get_fpr(ctx, a->fd); + TCGv src = get_fpr(ctx, a->fj); + + if (!avail_FP_SP(ctx)) { + return false; + } + + CHECK_FPE; + + tcg_gen_xori_i64(dest, src, 0x80000000); + gen_nanbox_s(dest, dest); + set_fpr(a->fd, dest); + + return true; +} + +static bool trans_fneg_d(DisasContext *ctx, arg_fneg_d *a) +{ + TCGv dest = get_fpr(ctx, a->fd); + TCGv src = get_fpr(ctx, a->fj); + + if (!avail_FP_DP(ctx)) { + return false; + } + + CHECK_FPE; + + tcg_gen_xori_i64(dest, src, 0x8000000000000000LL); + set_fpr(a->fd, dest); + + return true; +} + +TRANS(fadd_s, FP_SP, gen_fff, gen_helper_fadd_s) +TRANS(fadd_d, FP_DP, gen_fff, gen_helper_fadd_d) +TRANS(fsub_s, FP_SP, gen_fff, gen_helper_fsub_s) +TRANS(fsub_d, FP_DP, gen_fff, gen_helper_fsub_d) +TRANS(fmul_s, FP_SP, gen_fff, gen_helper_fmul_s) +TRANS(fmul_d, FP_DP, gen_fff, gen_helper_fmul_d) +TRANS(fdiv_s, FP_SP, gen_fff, gen_helper_fdiv_s) +TRANS(fdiv_d, FP_DP, gen_fff, gen_helper_fdiv_d) +TRANS(fmax_s, FP_SP, gen_fff, gen_helper_fmax_s) +TRANS(fmax_d, FP_DP, gen_fff, gen_helper_fmax_d) +TRANS(fmin_s, FP_SP, gen_fff, gen_helper_fmin_s) +TRANS(fmin_d, FP_DP, gen_fff, gen_helper_fmin_d) +TRANS(fmaxa_s, FP_SP, gen_fff, gen_helper_fmaxa_s) +TRANS(fmaxa_d, FP_DP, gen_fff, gen_helper_fmaxa_d) +TRANS(fmina_s, FP_SP, gen_fff, gen_helper_fmina_s) +TRANS(fmina_d, FP_DP, gen_fff, gen_helper_fmina_d) +TRANS(fscaleb_s, FP_SP, gen_fff, gen_helper_fscaleb_s) +TRANS(fscaleb_d, FP_DP, gen_fff, gen_helper_fscaleb_d) +TRANS(fsqrt_s, FP_SP, gen_ff, gen_helper_fsqrt_s) +TRANS(fsqrt_d, FP_DP, gen_ff, gen_helper_fsqrt_d) +TRANS(frecip_s, FP_SP, gen_ff, gen_helper_frecip_s) +TRANS(frecip_d, FP_DP, gen_ff, gen_helper_frecip_d) +TRANS(frsqrt_s, FP_SP, gen_ff, gen_helper_frsqrt_s) +TRANS(frsqrt_d, FP_DP, gen_ff, gen_helper_frsqrt_d) +TRANS(flogb_s, FP_SP, gen_ff, gen_helper_flogb_s) +TRANS(flogb_d, FP_DP, gen_ff, gen_helper_flogb_d) +TRANS(fclass_s, FP_SP, gen_ff, gen_helper_fclass_s) +TRANS(fclass_d, FP_DP, gen_ff, gen_helper_fclass_d) +TRANS(fmadd_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, 0) +TRANS(fmadd_d, FP_DP, gen_muladd, gen_helper_fmuladd_d, 0) +TRANS(fmsub_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, float_muladd_negate_c) +TRANS(fmsub_d, FP_DP, gen_muladd, gen_helper_fmuladd_d, float_muladd_negate_c) +TRANS(fnmadd_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, float_muladd_negate_result) +TRANS(fnmadd_d, FP_DP, gen_muladd, gen_helper_fmuladd_d, float_muladd_negate_result) +TRANS(fnmsub_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, + float_muladd_negate_c | float_muladd_negate_result) +TRANS(fnmsub_d, FP_DP, gen_muladd, gen_helper_fmuladd_d, + float_muladd_negate_c | float_muladd_negate_result) diff --git a/target/loongarch/insn_trans/trans_fcmp.c.inc b/target/loongarch/tcg/insn_trans/trans_fcmp.c.inc similarity index 63% rename from target/loongarch/insn_trans/trans_fcmp.c.inc rename to target/loongarch/tcg/insn_trans/trans_fcmp.c.inc index 93a6a2230f..3babf69e4a 100644 --- a/target/loongarch/insn_trans/trans_fcmp.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_fcmp.c.inc @@ -25,32 +25,48 @@ static uint32_t get_fcmp_flags(int cond) static bool trans_fcmp_cond_s(DisasContext *ctx, arg_fcmp_cond_s *a) { - TCGv var = tcg_temp_new(); + TCGv var, src1, src2; uint32_t flags; void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32); + if (!avail_FP_SP(ctx)) { + return false; + } + + CHECK_FPE; + + var = tcg_temp_new(); + src1 = get_fpr(ctx, a->fj); + src2 = get_fpr(ctx, a->fk); fn = (a->fcond & 1 ? gen_helper_fcmp_s_s : gen_helper_fcmp_c_s); flags = get_fcmp_flags(a->fcond >> 1); - fn(var, cpu_env, cpu_fpr[a->fj], cpu_fpr[a->fk], tcg_constant_i32(flags)); + fn(var, tcg_env, src1, src2, tcg_constant_i32(flags)); - tcg_gen_st8_tl(var, cpu_env, offsetof(CPULoongArchState, cf[a->cd])); - tcg_temp_free(var); + tcg_gen_st8_tl(var, tcg_env, offsetof(CPULoongArchState, cf[a->cd])); return true; } static bool trans_fcmp_cond_d(DisasContext *ctx, arg_fcmp_cond_d *a) { - TCGv var = tcg_temp_new(); + TCGv var, src1, src2; uint32_t flags; void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32); + + if (!avail_FP_DP(ctx)) { + return false; + } + + CHECK_FPE; + + var = tcg_temp_new(); + src1 = get_fpr(ctx, a->fj); + src2 = get_fpr(ctx, a->fk); fn = (a->fcond & 1 ? gen_helper_fcmp_s_d : gen_helper_fcmp_c_d); flags = get_fcmp_flags(a->fcond >> 1); - fn(var, cpu_env, cpu_fpr[a->fj], cpu_fpr[a->fk], tcg_constant_i32(flags)); + fn(var, tcg_env, src1, src2, tcg_constant_i32(flags)); - tcg_gen_st8_tl(var, cpu_env, offsetof(CPULoongArchState, cf[a->cd])); - - tcg_temp_free(var); + tcg_gen_st8_tl(var, tcg_env, offsetof(CPULoongArchState, cf[a->cd])); return true; } diff --git a/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc b/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc new file mode 100644 index 0000000000..833c059d6d --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +TRANS(fcvt_s_d, FP_DP, gen_ff, gen_helper_fcvt_s_d) +TRANS(fcvt_d_s, FP_DP, gen_ff, gen_helper_fcvt_d_s) +TRANS(ftintrm_w_s, FP_SP, gen_ff, gen_helper_ftintrm_w_s) +TRANS(ftintrm_w_d, FP_DP, gen_ff, gen_helper_ftintrm_w_d) +TRANS(ftintrm_l_s, FP_SP, gen_ff, gen_helper_ftintrm_l_s) +TRANS(ftintrm_l_d, FP_DP, gen_ff, gen_helper_ftintrm_l_d) +TRANS(ftintrp_w_s, FP_SP, gen_ff, gen_helper_ftintrp_w_s) +TRANS(ftintrp_w_d, FP_DP, gen_ff, gen_helper_ftintrp_w_d) +TRANS(ftintrp_l_s, FP_SP, gen_ff, gen_helper_ftintrp_l_s) +TRANS(ftintrp_l_d, FP_DP, gen_ff, gen_helper_ftintrp_l_d) +TRANS(ftintrz_w_s, FP_SP, gen_ff, gen_helper_ftintrz_w_s) +TRANS(ftintrz_w_d, FP_DP, gen_ff, gen_helper_ftintrz_w_d) +TRANS(ftintrz_l_s, FP_SP, gen_ff, gen_helper_ftintrz_l_s) +TRANS(ftintrz_l_d, FP_DP, gen_ff, gen_helper_ftintrz_l_d) +TRANS(ftintrne_w_s, FP_SP, gen_ff, gen_helper_ftintrne_w_s) +TRANS(ftintrne_w_d, FP_DP, gen_ff, gen_helper_ftintrne_w_d) +TRANS(ftintrne_l_s, FP_SP, gen_ff, gen_helper_ftintrne_l_s) +TRANS(ftintrne_l_d, FP_DP, gen_ff, gen_helper_ftintrne_l_d) +TRANS(ftint_w_s, FP_SP, gen_ff, gen_helper_ftint_w_s) +TRANS(ftint_w_d, FP_DP, gen_ff, gen_helper_ftint_w_d) +TRANS(ftint_l_s, FP_SP, gen_ff, gen_helper_ftint_l_s) +TRANS(ftint_l_d, FP_DP, gen_ff, gen_helper_ftint_l_d) +TRANS(ffint_s_w, FP_SP, gen_ff, gen_helper_ffint_s_w) +TRANS(ffint_s_l, FP_SP, gen_ff, gen_helper_ffint_s_l) +TRANS(ffint_d_w, FP_DP, gen_ff, gen_helper_ffint_d_w) +TRANS(ffint_d_l, FP_DP, gen_ff, gen_helper_ffint_d_l) +TRANS(frint_s, FP_SP, gen_ff, gen_helper_frint_s) +TRANS(frint_d, FP_DP, gen_ff, gen_helper_frint_d) diff --git a/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc b/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc new file mode 100644 index 0000000000..13452bc7e5 --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +static void maybe_nanbox_load(TCGv freg, MemOp mop) +{ + if ((mop & MO_SIZE) == MO_32) { + gen_nanbox_s(freg, freg); + } +} + +static bool gen_fload_i(DisasContext *ctx, arg_fr_i *a, MemOp mop) +{ + TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); + TCGv dest = get_fpr(ctx, a->fd); + + CHECK_FPE; + + addr = make_address_i(ctx, addr, a->imm); + + tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); + maybe_nanbox_load(dest, mop); + set_fpr(a->fd, dest); + + return true; +} + +static bool gen_fstore_i(DisasContext *ctx, arg_fr_i *a, MemOp mop) +{ + TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); + TCGv src = get_fpr(ctx, a->fd); + + CHECK_FPE; + + addr = make_address_i(ctx, addr, a->imm); + + tcg_gen_qemu_st_tl(src, addr, ctx->mem_idx, mop); + + return true; +} + +static bool gen_floadx(DisasContext *ctx, arg_frr *a, MemOp mop) +{ + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); + TCGv dest = get_fpr(ctx, a->fd); + TCGv addr; + + CHECK_FPE; + + addr = make_address_x(ctx, src1, src2); + tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); + maybe_nanbox_load(dest, mop); + set_fpr(a->fd, dest); + + return true; +} + +static bool gen_fstorex(DisasContext *ctx, arg_frr *a, MemOp mop) +{ + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); + TCGv src3 = get_fpr(ctx, a->fd); + TCGv addr; + + CHECK_FPE; + + addr = make_address_x(ctx, src1, src2); + tcg_gen_qemu_st_tl(src3, addr, ctx->mem_idx, mop); + + return true; +} + +static bool gen_fload_gt(DisasContext *ctx, arg_frr *a, MemOp mop) +{ + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); + TCGv dest = get_fpr(ctx, a->fd); + TCGv addr; + + CHECK_FPE; + + gen_helper_asrtgt_d(tcg_env, src1, src2); + addr = make_address_x(ctx, src1, src2); + tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); + maybe_nanbox_load(dest, mop); + set_fpr(a->fd, dest); + + return true; +} + +static bool gen_fstore_gt(DisasContext *ctx, arg_frr *a, MemOp mop) +{ + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); + TCGv src3 = get_fpr(ctx, a->fd); + TCGv addr; + + CHECK_FPE; + + gen_helper_asrtgt_d(tcg_env, src1, src2); + addr = make_address_x(ctx, src1, src2); + tcg_gen_qemu_st_tl(src3, addr, ctx->mem_idx, mop); + + return true; +} + +static bool gen_fload_le(DisasContext *ctx, arg_frr *a, MemOp mop) +{ + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); + TCGv dest = get_fpr(ctx, a->fd); + TCGv addr; + + CHECK_FPE; + + gen_helper_asrtle_d(tcg_env, src1, src2); + addr = make_address_x(ctx, src1, src2); + tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); + maybe_nanbox_load(dest, mop); + set_fpr(a->fd, dest); + + return true; +} + +static bool gen_fstore_le(DisasContext *ctx, arg_frr *a, MemOp mop) +{ + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); + TCGv src3 = get_fpr(ctx, a->fd); + TCGv addr; + + CHECK_FPE; + + gen_helper_asrtle_d(tcg_env, src1, src2); + addr = make_address_x(ctx, src1, src2); + tcg_gen_qemu_st_tl(src3, addr, ctx->mem_idx, mop); + + return true; +} + +TRANS(fld_s, FP_SP, gen_fload_i, MO_TEUL) +TRANS(fst_s, FP_SP, gen_fstore_i, MO_TEUL) +TRANS(fld_d, FP_DP, gen_fload_i, MO_TEUQ) +TRANS(fst_d, FP_DP, gen_fstore_i, MO_TEUQ) +TRANS(fldx_s, FP_SP, gen_floadx, MO_TEUL) +TRANS(fldx_d, FP_DP, gen_floadx, MO_TEUQ) +TRANS(fstx_s, FP_SP, gen_fstorex, MO_TEUL) +TRANS(fstx_d, FP_DP, gen_fstorex, MO_TEUQ) +TRANS(fldgt_s, FP_SP, gen_fload_gt, MO_TEUL) +TRANS(fldgt_d, FP_DP, gen_fload_gt, MO_TEUQ) +TRANS(fldle_s, FP_SP, gen_fload_le, MO_TEUL) +TRANS(fldle_d, FP_DP, gen_fload_le, MO_TEUQ) +TRANS(fstgt_s, FP_SP, gen_fstore_gt, MO_TEUL) +TRANS(fstgt_d, FP_DP, gen_fstore_gt, MO_TEUQ) +TRANS(fstle_s, FP_SP, gen_fstore_le, MO_TEUL) +TRANS(fstle_d, FP_DP, gen_fstore_le, MO_TEUQ) diff --git a/target/loongarch/tcg/insn_trans/trans_fmov.c.inc b/target/loongarch/tcg/insn_trans/trans_fmov.c.inc new file mode 100644 index 0000000000..5cbd9d3f34 --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_fmov.c.inc @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +static const uint32_t fcsr_mask[4] = { + UINT32_MAX, FCSR0_M1, FCSR0_M2, FCSR0_M3 +}; + +static bool trans_fsel(DisasContext *ctx, arg_fsel *a) +{ + TCGv zero = tcg_constant_tl(0); + TCGv dest = get_fpr(ctx, a->fd); + TCGv src1 = get_fpr(ctx, a->fj); + TCGv src2 = get_fpr(ctx, a->fk); + TCGv cond; + + if (!avail_FP(ctx)) { + return false; + } + + CHECK_FPE; + + cond = tcg_temp_new(); + tcg_gen_ld8u_tl(cond, tcg_env, offsetof(CPULoongArchState, cf[a->ca])); + tcg_gen_movcond_tl(TCG_COND_EQ, dest, cond, zero, src1, src2); + set_fpr(a->fd, dest); + + return true; +} + +static bool gen_f2f(DisasContext *ctx, arg_ff *a, + void (*func)(TCGv, TCGv), bool nanbox) +{ + TCGv dest = get_fpr(ctx, a->fd); + TCGv src = get_fpr(ctx, a->fj); + + CHECK_FPE; + + func(dest, src); + if (nanbox) { + gen_nanbox_s(dest, dest); + } + set_fpr(a->fd, dest); + + return true; +} + +static bool gen_r2f(DisasContext *ctx, arg_fr *a, + void (*func)(TCGv, TCGv)) +{ + TCGv src = gpr_src(ctx, a->rj, EXT_NONE); + TCGv dest = get_fpr(ctx, a->fd); + + if (!avail_FP(ctx)) { + return false; + } + + CHECK_FPE; + + func(dest, src); + set_fpr(a->fd, dest); + + return true; +} + +static bool gen_f2r(DisasContext *ctx, arg_rf *a, + void (*func)(TCGv, TCGv)) +{ + TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); + TCGv src = get_fpr(ctx, a->fj); + + if (!avail_FP(ctx)) { + return false; + } + + CHECK_FPE; + + func(dest, src); + gen_set_gpr(a->rd, dest, EXT_NONE); + + return true; +} + +static bool trans_movgr2fcsr(DisasContext *ctx, arg_movgr2fcsr *a) +{ + uint32_t mask = fcsr_mask[a->fcsrd]; + TCGv Rj = gpr_src(ctx, a->rj, EXT_NONE); + + if (!avail_FP(ctx)) { + return false; + } + + CHECK_FPE; + + if (mask == UINT32_MAX) { + tcg_gen_st32_i64(Rj, tcg_env, offsetof(CPULoongArchState, fcsr0)); + } else { + TCGv_i32 fcsr0 = tcg_temp_new_i32(); + TCGv_i32 temp = tcg_temp_new_i32(); + + tcg_gen_ld_i32(fcsr0, tcg_env, offsetof(CPULoongArchState, fcsr0)); + tcg_gen_extrl_i64_i32(temp, Rj); + tcg_gen_andi_i32(temp, temp, mask); + tcg_gen_andi_i32(fcsr0, fcsr0, ~mask); + tcg_gen_or_i32(fcsr0, fcsr0, temp); + tcg_gen_st_i32(fcsr0, tcg_env, offsetof(CPULoongArchState, fcsr0)); + } + + /* + * Install the new rounding mode to fpu_status, if changed. + * Note that FCSR3 is exactly the rounding mode field. + */ + if (mask & FCSR0_M3) { + gen_helper_set_rounding_mode(tcg_env); + } + return true; +} + +static bool trans_movfcsr2gr(DisasContext *ctx, arg_movfcsr2gr *a) +{ + TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); + + if (!avail_FP(ctx)) { + return false; + } + + CHECK_FPE; + + tcg_gen_ld32u_i64(dest, tcg_env, offsetof(CPULoongArchState, fcsr0)); + tcg_gen_andi_i64(dest, dest, fcsr_mask[a->fcsrs]); + gen_set_gpr(a->rd, dest, EXT_NONE); + + return true; +} + +static void gen_movgr2fr_w(TCGv dest, TCGv src) +{ + tcg_gen_deposit_i64(dest, dest, src, 0, 32); +} + +static void gen_movgr2frh_w(TCGv dest, TCGv src) +{ + tcg_gen_deposit_i64(dest, dest, src, 32, 32); +} + +static void gen_movfrh2gr_s(TCGv dest, TCGv src) +{ + tcg_gen_sextract_tl(dest, src, 32, 32); +} + +static bool trans_movfr2cf(DisasContext *ctx, arg_movfr2cf *a) +{ + TCGv t0; + TCGv src = get_fpr(ctx, a->fj); + + if (!avail_FP(ctx)) { + return false; + } + + CHECK_FPE; + + t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, src, 0x1); + tcg_gen_st8_tl(t0, tcg_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); + + return true; +} + +static bool trans_movcf2fr(DisasContext *ctx, arg_movcf2fr *a) +{ + TCGv dest = get_fpr(ctx, a->fd); + + if (!avail_FP(ctx)) { + return false; + } + + CHECK_FPE; + + tcg_gen_ld8u_tl(dest, tcg_env, + offsetof(CPULoongArchState, cf[a->cj & 0x7])); + set_fpr(a->fd, dest); + + return true; +} + +static bool trans_movgr2cf(DisasContext *ctx, arg_movgr2cf *a) +{ + TCGv t0; + + if (!avail_FP(ctx)) { + return false; + } + + CHECK_FPE; + + t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, gpr_src(ctx, a->rj, EXT_NONE), 0x1); + tcg_gen_st8_tl(t0, tcg_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); + + return true; +} + +static bool trans_movcf2gr(DisasContext *ctx, arg_movcf2gr *a) +{ + if (!avail_FP(ctx)) { + return false; + } + + CHECK_FPE; + + tcg_gen_ld8u_tl(gpr_dst(ctx, a->rd, EXT_NONE), tcg_env, + offsetof(CPULoongArchState, cf[a->cj & 0x7])); + return true; +} + +TRANS(fmov_s, FP_SP, gen_f2f, tcg_gen_mov_tl, true) +TRANS(fmov_d, FP_DP, gen_f2f, tcg_gen_mov_tl, false) +TRANS(movgr2fr_w, FP_SP, gen_r2f, gen_movgr2fr_w) +TRANS(movgr2fr_d, 64, gen_r2f, tcg_gen_mov_tl) +TRANS(movgr2frh_w, FP_DP, gen_r2f, gen_movgr2frh_w) +TRANS(movfr2gr_s, FP_SP, gen_f2r, tcg_gen_ext32s_tl) +TRANS(movfr2gr_d, 64, gen_f2r, tcg_gen_mov_tl) +TRANS(movfrh2gr_s, FP_DP, gen_f2r, gen_movfrh2gr_s) diff --git a/target/loongarch/insn_trans/trans_memory.c.inc b/target/loongarch/tcg/insn_trans/trans_memory.c.inc similarity index 56% rename from target/loongarch/insn_trans/trans_memory.c.inc rename to target/loongarch/tcg/insn_trans/trans_memory.c.inc index d5eb31147c..42f4e74012 100644 --- a/target/loongarch/insn_trans/trans_memory.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_memory.c.inc @@ -7,21 +7,11 @@ static bool gen_load(DisasContext *ctx, arg_rr_i *a, MemOp mop) { TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); - TCGv temp = NULL; - if (a->imm) { - temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, addr, a->imm); - addr = temp; - } + addr = make_address_i(ctx, addr, a->imm); tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); gen_set_gpr(a->rd, dest, EXT_NONE); - - if (temp) { - tcg_temp_free(temp); - } - return true; } @@ -29,20 +19,10 @@ static bool gen_store(DisasContext *ctx, arg_rr_i *a, MemOp mop) { TCGv data = gpr_src(ctx, a->rd, EXT_NONE); TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); - TCGv temp = NULL; - if (a->imm) { - temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, addr, a->imm); - addr = temp; - } + addr = make_address_i(ctx, addr, a->imm); tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop); - - if (temp) { - tcg_temp_free(temp); - } - return true; } @@ -51,12 +31,10 @@ static bool gen_loadx(DisasContext *ctx, arg_rrr *a, MemOp mop) TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - TCGv addr = tcg_temp_new(); + TCGv addr = make_address_x(ctx, src1, src2); - tcg_gen_add_tl(addr, src1, src2); tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); gen_set_gpr(a->rd, dest, EXT_NONE); - tcg_temp_free(addr); return true; } @@ -66,11 +44,9 @@ static bool gen_storex(DisasContext *ctx, arg_rrr *a, MemOp mop) TCGv data = gpr_src(ctx, a->rd, EXT_NONE); TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - TCGv addr = tcg_temp_new(); + TCGv addr = make_address_x(ctx, src1, src2); - tcg_gen_add_tl(addr, src1, src2); tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop); - tcg_temp_free(addr); return true; } @@ -81,7 +57,8 @@ static bool gen_load_gt(DisasContext *ctx, arg_rrr *a, MemOp mop) TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - gen_helper_asrtgt_d(cpu_env, src1, src2); + gen_helper_asrtgt_d(tcg_env, src1, src2); + src1 = make_address_i(ctx, src1, 0); tcg_gen_qemu_ld_tl(dest, src1, ctx->mem_idx, mop); gen_set_gpr(a->rd, dest, EXT_NONE); @@ -94,7 +71,8 @@ static bool gen_load_le(DisasContext *ctx, arg_rrr *a, MemOp mop) TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - gen_helper_asrtle_d(cpu_env, src1, src2); + gen_helper_asrtle_d(tcg_env, src1, src2); + src1 = make_address_i(ctx, src1, 0); tcg_gen_qemu_ld_tl(dest, src1, ctx->mem_idx, mop); gen_set_gpr(a->rd, dest, EXT_NONE); @@ -107,7 +85,8 @@ static bool gen_store_gt(DisasContext *ctx, arg_rrr *a, MemOp mop) TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - gen_helper_asrtgt_d(cpu_env, src1, src2); + gen_helper_asrtgt_d(tcg_env, src1, src2); + src1 = make_address_i(ctx, src1, 0); tcg_gen_qemu_st_tl(data, src1, ctx->mem_idx, mop); return true; @@ -119,7 +98,8 @@ static bool gen_store_le(DisasContext *ctx, arg_rrr *a, MemOp mop) TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - gen_helper_asrtle_d(cpu_env, src1, src2); + gen_helper_asrtle_d(tcg_env, src1, src2); + src1 = make_address_i(ctx, src1, 0); tcg_gen_qemu_st_tl(data, src1, ctx->mem_idx, mop); return true; @@ -130,6 +110,11 @@ static bool trans_preld(DisasContext *ctx, arg_preld *a) return true; } +static bool trans_preldx(DisasContext *ctx, arg_preldx * a) +{ + return true; +} + static bool trans_dbar(DisasContext *ctx, arg_dbar * a) { tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); @@ -146,21 +131,11 @@ static bool gen_ldptr(DisasContext *ctx, arg_rr_i *a, MemOp mop) { TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); - TCGv temp = NULL; - if (a->imm) { - temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, addr, a->imm); - addr = temp; - } + addr = make_address_i(ctx, addr, a->imm); tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); gen_set_gpr(a->rd, dest, EXT_NONE); - - if (temp) { - tcg_temp_free(temp); - } - return true; } @@ -168,62 +143,52 @@ static bool gen_stptr(DisasContext *ctx, arg_rr_i *a, MemOp mop) { TCGv data = gpr_src(ctx, a->rd, EXT_NONE); TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); - TCGv temp = NULL; - if (a->imm) { - temp = tcg_temp_new(); - tcg_gen_addi_tl(temp, addr, a->imm); - addr = temp; - } + addr = make_address_i(ctx, addr, a->imm); tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop); - - if (temp) { - tcg_temp_free(temp); - } - return true; } -TRANS(ld_b, gen_load, MO_SB) -TRANS(ld_h, gen_load, MO_TESW) -TRANS(ld_w, gen_load, MO_TESL) -TRANS(ld_d, gen_load, MO_TEUQ) -TRANS(st_b, gen_store, MO_UB) -TRANS(st_h, gen_store, MO_TEUW) -TRANS(st_w, gen_store, MO_TEUL) -TRANS(st_d, gen_store, MO_TEUQ) -TRANS(ld_bu, gen_load, MO_UB) -TRANS(ld_hu, gen_load, MO_TEUW) -TRANS(ld_wu, gen_load, MO_TEUL) -TRANS(ldx_b, gen_loadx, MO_SB) -TRANS(ldx_h, gen_loadx, MO_TESW) -TRANS(ldx_w, gen_loadx, MO_TESL) -TRANS(ldx_d, gen_loadx, MO_TEUQ) -TRANS(stx_b, gen_storex, MO_UB) -TRANS(stx_h, gen_storex, MO_TEUW) -TRANS(stx_w, gen_storex, MO_TEUL) -TRANS(stx_d, gen_storex, MO_TEUQ) -TRANS(ldx_bu, gen_loadx, MO_UB) -TRANS(ldx_hu, gen_loadx, MO_TEUW) -TRANS(ldx_wu, gen_loadx, MO_TEUL) -TRANS(ldptr_w, gen_ldptr, MO_TESL) -TRANS(stptr_w, gen_stptr, MO_TEUL) -TRANS(ldptr_d, gen_ldptr, MO_TEUQ) -TRANS(stptr_d, gen_stptr, MO_TEUQ) -TRANS(ldgt_b, gen_load_gt, MO_SB) -TRANS(ldgt_h, gen_load_gt, MO_TESW) -TRANS(ldgt_w, gen_load_gt, MO_TESL) -TRANS(ldgt_d, gen_load_gt, MO_TEUQ) -TRANS(ldle_b, gen_load_le, MO_SB) -TRANS(ldle_h, gen_load_le, MO_TESW) -TRANS(ldle_w, gen_load_le, MO_TESL) -TRANS(ldle_d, gen_load_le, MO_TEUQ) -TRANS(stgt_b, gen_store_gt, MO_UB) -TRANS(stgt_h, gen_store_gt, MO_TEUW) -TRANS(stgt_w, gen_store_gt, MO_TEUL) -TRANS(stgt_d, gen_store_gt, MO_TEUQ) -TRANS(stle_b, gen_store_le, MO_UB) -TRANS(stle_h, gen_store_le, MO_TEUW) -TRANS(stle_w, gen_store_le, MO_TEUL) -TRANS(stle_d, gen_store_le, MO_TEUQ) +TRANS(ld_b, ALL, gen_load, MO_SB) +TRANS(ld_h, ALL, gen_load, MO_TESW) +TRANS(ld_w, ALL, gen_load, MO_TESL) +TRANS(ld_d, 64, gen_load, MO_TEUQ) +TRANS(st_b, ALL, gen_store, MO_UB) +TRANS(st_h, ALL, gen_store, MO_TEUW) +TRANS(st_w, ALL, gen_store, MO_TEUL) +TRANS(st_d, 64, gen_store, MO_TEUQ) +TRANS(ld_bu, ALL, gen_load, MO_UB) +TRANS(ld_hu, ALL, gen_load, MO_TEUW) +TRANS(ld_wu, 64, gen_load, MO_TEUL) +TRANS(ldx_b, 64, gen_loadx, MO_SB) +TRANS(ldx_h, 64, gen_loadx, MO_TESW) +TRANS(ldx_w, 64, gen_loadx, MO_TESL) +TRANS(ldx_d, 64, gen_loadx, MO_TEUQ) +TRANS(stx_b, 64, gen_storex, MO_UB) +TRANS(stx_h, 64, gen_storex, MO_TEUW) +TRANS(stx_w, 64, gen_storex, MO_TEUL) +TRANS(stx_d, 64, gen_storex, MO_TEUQ) +TRANS(ldx_bu, 64, gen_loadx, MO_UB) +TRANS(ldx_hu, 64, gen_loadx, MO_TEUW) +TRANS(ldx_wu, 64, gen_loadx, MO_TEUL) +TRANS(ldptr_w, 64, gen_ldptr, MO_TESL) +TRANS(stptr_w, 64, gen_stptr, MO_TEUL) +TRANS(ldptr_d, 64, gen_ldptr, MO_TEUQ) +TRANS(stptr_d, 64, gen_stptr, MO_TEUQ) +TRANS(ldgt_b, 64, gen_load_gt, MO_SB) +TRANS(ldgt_h, 64, gen_load_gt, MO_TESW) +TRANS(ldgt_w, 64, gen_load_gt, MO_TESL) +TRANS(ldgt_d, 64, gen_load_gt, MO_TEUQ) +TRANS(ldle_b, 64, gen_load_le, MO_SB) +TRANS(ldle_h, 64, gen_load_le, MO_TESW) +TRANS(ldle_w, 64, gen_load_le, MO_TESL) +TRANS(ldle_d, 64, gen_load_le, MO_TEUQ) +TRANS(stgt_b, 64, gen_store_gt, MO_UB) +TRANS(stgt_h, 64, gen_store_gt, MO_TEUW) +TRANS(stgt_w, 64, gen_store_gt, MO_TEUL) +TRANS(stgt_d, 64, gen_store_gt, MO_TEUQ) +TRANS(stle_b, 64, gen_store_le, MO_UB) +TRANS(stle_h, 64, gen_store_le, MO_TEUW) +TRANS(stle_w, 64, gen_store_le, MO_TEUL) +TRANS(stle_d, 64, gen_store_le, MO_TEUQ) diff --git a/target/loongarch/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc similarity index 77% rename from target/loongarch/insn_trans/trans_privileged.c.inc rename to target/loongarch/tcg/insn_trans/trans_privileged.c.inc index 53596c4f77..7e4ec93edb 100644 --- a/target/loongarch/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -7,6 +7,41 @@ #include "cpu-csr.h" +#ifdef CONFIG_USER_ONLY + +#define GEN_FALSE_TRANS(name) \ +static bool trans_##name(DisasContext *ctx, arg_##name * a) \ +{ \ + return false; \ +} + +GEN_FALSE_TRANS(csrrd) +GEN_FALSE_TRANS(csrwr) +GEN_FALSE_TRANS(csrxchg) +GEN_FALSE_TRANS(iocsrrd_b) +GEN_FALSE_TRANS(iocsrrd_h) +GEN_FALSE_TRANS(iocsrrd_w) +GEN_FALSE_TRANS(iocsrrd_d) +GEN_FALSE_TRANS(iocsrwr_b) +GEN_FALSE_TRANS(iocsrwr_h) +GEN_FALSE_TRANS(iocsrwr_w) +GEN_FALSE_TRANS(iocsrwr_d) +GEN_FALSE_TRANS(tlbsrch) +GEN_FALSE_TRANS(tlbrd) +GEN_FALSE_TRANS(tlbwr) +GEN_FALSE_TRANS(tlbfill) +GEN_FALSE_TRANS(tlbclr) +GEN_FALSE_TRANS(tlbflush) +GEN_FALSE_TRANS(invtlb) +GEN_FALSE_TRANS(cacop) +GEN_FALSE_TRANS(ldpte) +GEN_FALSE_TRANS(lddir) +GEN_FALSE_TRANS(ertn) +GEN_FALSE_TRANS(dbcl) +GEN_FALSE_TRANS(idle) + +#else + typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env); typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src); @@ -64,13 +99,7 @@ static const CSRInfo csr_info[] = { CSR_OFF(PWCH), CSR_OFF(STLBPS), CSR_OFF(RVACFG), - [LOONGARCH_CSR_CPUID] = { - .offset = (int)offsetof(CPUState, cpu_index) - - (int)offsetof(LoongArchCPU, env), - .flags = CSRFL_READONLY, - .readfn = NULL, - .writefn = NULL - }, + CSR_OFF_FUNCS(CPUID, CSRFL_READONLY, gen_helper_csrrd_cpuid, NULL), CSR_OFF_FLAGS(PRCFG1, CSRFL_READONLY), CSR_OFF_FLAGS(PRCFG2, CSRFL_READONLY), CSR_OFF_FLAGS(PRCFG3, CSRFL_READONLY), @@ -124,7 +153,7 @@ static const CSRInfo csr_info[] = { static bool check_plv(DisasContext *ctx) { - if (ctx->base.tb->flags == MMU_USER_IDX) { + if (ctx->plv == MMU_PLV_USER) { generate_exception(ctx, EXCCODE_IPE); return true; } @@ -150,9 +179,7 @@ static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write) if ((csr->flags & CSRFL_READONLY) && write) { return false; } - if ((csr->flags & CSRFL_IO) && - (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT)) { - gen_io_start(); + if ((csr->flags & CSRFL_IO) && translator_io_start(&ctx->base)) { ctx->base.is_jmp = DISAS_EXIT_UPDATE; } else if ((csr->flags & CSRFL_EXITTB) && write) { ctx->base.is_jmp = DISAS_EXIT_UPDATE; @@ -176,9 +203,9 @@ static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a) check_csr_flags(ctx, csr, false); dest = gpr_dst(ctx, a->rd, EXT_NONE); if (csr->readfn) { - csr->readfn(dest, cpu_env); + csr->readfn(dest, tcg_env); } else { - tcg_gen_ld_tl(dest, cpu_env, csr->offset); + tcg_gen_ld_tl(dest, tcg_env, csr->offset); } } gen_set_gpr(a->rd, dest, EXT_NONE); @@ -206,11 +233,11 @@ static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a) src1 = gpr_src(ctx, a->rd, EXT_NONE); if (csr->writefn) { dest = gpr_dst(ctx, a->rd, EXT_NONE); - csr->writefn(dest, cpu_env, src1); + csr->writefn(dest, tcg_env, src1); } else { - dest = temp_new(ctx); - tcg_gen_ld_tl(dest, cpu_env, csr->offset); - tcg_gen_st_tl(src1, cpu_env, csr->offset); + dest = tcg_temp_new(); + tcg_gen_ld_tl(dest, tcg_env, csr->offset); + tcg_gen_st_tl(src1, tcg_env, csr->offset); } gen_set_gpr(a->rd, dest, EXT_NONE); return true; @@ -245,21 +272,17 @@ static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a) newv = tcg_temp_new(); temp = tcg_temp_new(); - tcg_gen_ld_tl(oldv, cpu_env, csr->offset); + tcg_gen_ld_tl(oldv, tcg_env, csr->offset); tcg_gen_and_tl(newv, src1, mask); tcg_gen_andc_tl(temp, oldv, mask); tcg_gen_or_tl(newv, newv, temp); if (csr->writefn) { - csr->writefn(oldv, cpu_env, newv); + csr->writefn(oldv, tcg_env, newv); } else { - tcg_gen_st_tl(newv, cpu_env, csr->offset); + tcg_gen_st_tl(newv, tcg_env, csr->offset); } gen_set_gpr(a->rd, oldv, EXT_NONE); - - tcg_temp_free(temp); - tcg_temp_free(newv); - tcg_temp_free(oldv); return true; } @@ -272,7 +295,7 @@ static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a, if (check_plv(ctx)) { return false; } - func(dest, cpu_env, src1); + func(dest, tcg_env, src1); return true; } @@ -285,18 +308,18 @@ static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a, if (check_plv(ctx)) { return false; } - func(cpu_env, addr, val); + func(tcg_env, addr, val); return true; } -TRANS(iocsrrd_b, gen_iocsrrd, gen_helper_iocsrrd_b) -TRANS(iocsrrd_h, gen_iocsrrd, gen_helper_iocsrrd_h) -TRANS(iocsrrd_w, gen_iocsrrd, gen_helper_iocsrrd_w) -TRANS(iocsrrd_d, gen_iocsrrd, gen_helper_iocsrrd_d) -TRANS(iocsrwr_b, gen_iocsrwr, gen_helper_iocsrwr_b) -TRANS(iocsrwr_h, gen_iocsrwr, gen_helper_iocsrwr_h) -TRANS(iocsrwr_w, gen_iocsrwr, gen_helper_iocsrwr_w) -TRANS(iocsrwr_d, gen_iocsrwr, gen_helper_iocsrwr_d) +TRANS(iocsrrd_b, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_b) +TRANS(iocsrrd_h, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_h) +TRANS(iocsrrd_w, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_w) +TRANS(iocsrrd_d, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_d) +TRANS(iocsrwr_b, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_b) +TRANS(iocsrwr_h, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_h) +TRANS(iocsrwr_w, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_w) +TRANS(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d) static void check_mmu_idx(DisasContext *ctx) { @@ -311,7 +334,7 @@ static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a) if (check_plv(ctx)) { return false; } - gen_helper_tlbsrch(cpu_env); + gen_helper_tlbsrch(tcg_env); return true; } @@ -320,7 +343,7 @@ static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a) if (check_plv(ctx)) { return false; } - gen_helper_tlbrd(cpu_env); + gen_helper_tlbrd(tcg_env); return true; } @@ -329,7 +352,7 @@ static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a) if (check_plv(ctx)) { return false; } - gen_helper_tlbwr(cpu_env); + gen_helper_tlbwr(tcg_env); check_mmu_idx(ctx); return true; } @@ -339,7 +362,7 @@ static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a) if (check_plv(ctx)) { return false; } - gen_helper_tlbfill(cpu_env); + gen_helper_tlbfill(tcg_env); check_mmu_idx(ctx); return true; } @@ -349,7 +372,7 @@ static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a) if (check_plv(ctx)) { return false; } - gen_helper_tlbclr(cpu_env); + gen_helper_tlbclr(tcg_env); check_mmu_idx(ctx); return true; } @@ -359,7 +382,7 @@ static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a) if (check_plv(ctx)) { return false; } - gen_helper_tlbflush(cpu_env); + gen_helper_tlbflush(tcg_env); check_mmu_idx(ctx); return true; } @@ -376,22 +399,22 @@ static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a) switch (a->imm) { case 0: case 1: - gen_helper_invtlb_all(cpu_env); + gen_helper_invtlb_all(tcg_env); break; case 2: - gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(1)); + gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(1)); break; case 3: - gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(0)); + gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(0)); break; case 4: - gen_helper_invtlb_all_asid(cpu_env, rj); + gen_helper_invtlb_all_asid(tcg_env, rj); break; case 5: - gen_helper_invtlb_page_asid(cpu_env, rj, rk); + gen_helper_invtlb_page_asid(tcg_env, rj, rk); break; case 6: - gen_helper_invtlb_page_asid_or_g(cpu_env, rj, rk); + gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk); break; default: return false; @@ -414,10 +437,14 @@ static bool trans_ldpte(DisasContext *ctx, arg_ldpte *a) TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx); TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + if (!avail_LSPW(ctx)) { + return true; + } + if (check_plv(ctx)) { return false; } - gen_helper_ldpte(cpu_env, src1, tcg_constant_tl(a->imm), mem_idx); + gen_helper_ldpte(tcg_env, src1, tcg_constant_tl(a->imm), mem_idx); return true; } @@ -427,10 +454,14 @@ static bool trans_lddir(DisasContext *ctx, arg_lddir *a) TCGv src = gpr_src(ctx, a->rj, EXT_NONE); TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); + if (!avail_LSPW(ctx)) { + return true; + } + if (check_plv(ctx)) { return false; } - gen_helper_lddir(dest, cpu_env, src, tcg_constant_tl(a->imm), mem_idx); + gen_helper_lddir(dest, tcg_env, src, tcg_constant_tl(a->imm), mem_idx); return true; } @@ -439,7 +470,7 @@ static bool trans_ertn(DisasContext *ctx, arg_ertn *a) if (check_plv(ctx)) { return false; } - gen_helper_ertn(cpu_env); + gen_helper_ertn(tcg_env); ctx->base.is_jmp = DISAS_EXIT; return true; } @@ -460,7 +491,8 @@ static bool trans_idle(DisasContext *ctx, arg_idle *a) } tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4); - gen_helper_idle(cpu_env); + gen_helper_idle(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; return true; } +#endif diff --git a/target/loongarch/insn_trans/trans_shift.c.inc b/target/loongarch/tcg/insn_trans/trans_shift.c.inc similarity index 61% rename from target/loongarch/insn_trans/trans_shift.c.inc rename to target/loongarch/tcg/insn_trans/trans_shift.c.inc index 5260af2337..2f4bd6ff28 100644 --- a/target/loongarch/insn_trans/trans_shift.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_shift.c.inc @@ -8,7 +8,6 @@ static void gen_sll_w(TCGv dest, TCGv src1, TCGv src2) TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, src2, 0x1f); tcg_gen_shl_tl(dest, src1, t0); - tcg_temp_free(t0); } static void gen_srl_w(TCGv dest, TCGv src1, TCGv src2) @@ -16,7 +15,6 @@ static void gen_srl_w(TCGv dest, TCGv src1, TCGv src2) TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, src2, 0x1f); tcg_gen_shr_tl(dest, src1, t0); - tcg_temp_free(t0); } static void gen_sra_w(TCGv dest, TCGv src1, TCGv src2) @@ -24,7 +22,6 @@ static void gen_sra_w(TCGv dest, TCGv src1, TCGv src2) TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, src2, 0x1f); tcg_gen_sar_tl(dest, src1, t0); - tcg_temp_free(t0); } static void gen_sll_d(TCGv dest, TCGv src1, TCGv src2) @@ -32,7 +29,6 @@ static void gen_sll_d(TCGv dest, TCGv src1, TCGv src2) TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, src2, 0x3f); tcg_gen_shl_tl(dest, src1, t0); - tcg_temp_free(t0); } static void gen_srl_d(TCGv dest, TCGv src1, TCGv src2) @@ -40,7 +36,6 @@ static void gen_srl_d(TCGv dest, TCGv src1, TCGv src2) TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, src2, 0x3f); tcg_gen_shr_tl(dest, src1, t0); - tcg_temp_free(t0); } static void gen_sra_d(TCGv dest, TCGv src1, TCGv src2) @@ -48,7 +43,6 @@ static void gen_sra_d(TCGv dest, TCGv src1, TCGv src2) TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, src2, 0x3f); tcg_gen_sar_tl(dest, src1, t0); - tcg_temp_free(t0); } static void gen_rotr_w(TCGv dest, TCGv src1, TCGv src2) @@ -64,10 +58,6 @@ static void gen_rotr_w(TCGv dest, TCGv src1, TCGv src2) tcg_gen_rotr_i32(t1, t1, t2); tcg_gen_ext_i32_tl(dest, t1); - - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); - tcg_temp_free(t0); } static void gen_rotr_d(TCGv dest, TCGv src1, TCGv src2) @@ -75,7 +65,6 @@ static void gen_rotr_d(TCGv dest, TCGv src1, TCGv src2) TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, src2, 0x3f); tcg_gen_rotr_tl(dest, src1, t0); - tcg_temp_free(t0); } static bool trans_srai_w(DisasContext *ctx, arg_srai_w *a) @@ -83,24 +72,28 @@ static bool trans_srai_w(DisasContext *ctx, arg_srai_w *a) TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); TCGv src1 = gpr_src(ctx, a->rj, EXT_ZERO); + if (!avail_64(ctx)) { + return false; + } + tcg_gen_sextract_tl(dest, src1, a->imm, 32 - a->imm); gen_set_gpr(a->rd, dest, EXT_NONE); return true; } -TRANS(sll_w, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_sll_w) -TRANS(srl_w, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_srl_w) -TRANS(sra_w, gen_rrr, EXT_SIGN, EXT_NONE, EXT_SIGN, gen_sra_w) -TRANS(sll_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sll_d) -TRANS(srl_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_srl_d) -TRANS(sra_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sra_d) -TRANS(rotr_w, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_rotr_w) -TRANS(rotr_d, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rotr_d) -TRANS(slli_w, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_shli_tl) -TRANS(slli_d, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shli_tl) -TRANS(srli_w, gen_rri_c, EXT_ZERO, EXT_SIGN, tcg_gen_shri_tl) -TRANS(srli_d, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shri_tl) -TRANS(srai_d, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_sari_tl) -TRANS(rotri_w, gen_rri_v, EXT_NONE, EXT_NONE, gen_rotr_w) -TRANS(rotri_d, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_rotri_tl) +TRANS(sll_w, ALL, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_sll_w) +TRANS(srl_w, ALL, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_srl_w) +TRANS(sra_w, ALL, gen_rrr, EXT_SIGN, EXT_NONE, EXT_SIGN, gen_sra_w) +TRANS(sll_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sll_d) +TRANS(srl_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_srl_d) +TRANS(sra_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sra_d) +TRANS(rotr_w, 64, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_rotr_w) +TRANS(rotr_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rotr_d) +TRANS(slli_w, ALL, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_shli_tl) +TRANS(slli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shli_tl) +TRANS(srli_w, ALL, gen_rri_c, EXT_ZERO, EXT_SIGN, tcg_gen_shri_tl) +TRANS(srli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shri_tl) +TRANS(srai_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_sari_tl) +TRANS(rotri_w, 64, gen_rri_v, EXT_NONE, EXT_NONE, gen_rotr_w) +TRANS(rotri_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_rotri_tl) diff --git a/target/loongarch/tcg/insn_trans/trans_vec.c.inc b/target/loongarch/tcg/insn_trans/trans_vec.c.inc new file mode 100644 index 0000000000..92b1d22e28 --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_vec.c.inc @@ -0,0 +1,5511 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch vector translate functions + * Copyright (c) 2022-2023 Loongson Technology Corporation Limited + */ + +static bool check_vec(DisasContext *ctx, uint32_t oprsz) +{ + if ((oprsz == 16) && ((ctx->base.tb->flags & HW_FLAGS_EUEN_SXE) == 0)) { + generate_exception(ctx, EXCCODE_SXD); + return false; + } + + if ((oprsz == 32) && ((ctx->base.tb->flags & HW_FLAGS_EUEN_ASXE) == 0)) { + generate_exception(ctx, EXCCODE_ASXD); + return false; + } + + return true; +} + +static bool gen_vvvv_ptr_vl(DisasContext *ctx, arg_vvvv *a, uint32_t oprsz, + gen_helper_gvec_4_ptr *fn) +{ + if (!check_vec(ctx, oprsz)) { + return true; + } + + tcg_gen_gvec_4_ptr(vec_full_offset(a->vd), + vec_full_offset(a->vj), + vec_full_offset(a->vk), + vec_full_offset(a->va), + tcg_env, + oprsz, ctx->vl / 8, 0, fn); + return true; +} + +static bool gen_vvvv_ptr(DisasContext *ctx, arg_vvvv *a, + gen_helper_gvec_4_ptr *fn) +{ + return gen_vvvv_ptr_vl(ctx, a, 16, fn); +} + +static bool gen_xxxx_ptr(DisasContext *ctx, arg_vvvv *a, + gen_helper_gvec_4_ptr *fn) +{ + return gen_vvvv_ptr_vl(ctx, a, 32, fn); +} + +static bool gen_vvvv_vl(DisasContext *ctx, arg_vvvv *a, uint32_t oprsz, + gen_helper_gvec_4 *fn) +{ + if (!check_vec(ctx, oprsz)) { + return true; + } + + tcg_gen_gvec_4_ool(vec_full_offset(a->vd), + vec_full_offset(a->vj), + vec_full_offset(a->vk), + vec_full_offset(a->va), + oprsz, ctx->vl / 8, 0, fn); + return true; +} + +static bool gen_vvvv(DisasContext *ctx, arg_vvvv *a, + gen_helper_gvec_4 *fn) +{ + return gen_vvvv_vl(ctx, a, 16, fn); +} + +static bool gen_xxxx(DisasContext *ctx, arg_vvvv *a, + gen_helper_gvec_4 *fn) +{ + return gen_vvvv_vl(ctx, a, 32, fn); +} + +static bool gen_vvv_ptr_vl(DisasContext *ctx, arg_vvv *a, uint32_t oprsz, + gen_helper_gvec_3_ptr *fn) +{ + if (!check_vec(ctx, oprsz)) { + return true; + } + tcg_gen_gvec_3_ptr(vec_full_offset(a->vd), + vec_full_offset(a->vj), + vec_full_offset(a->vk), + tcg_env, + oprsz, ctx->vl / 8, 0, fn); + return true; +} + +static bool gen_vvv_ptr(DisasContext *ctx, arg_vvv *a, + gen_helper_gvec_3_ptr *fn) +{ + return gen_vvv_ptr_vl(ctx, a, 16, fn); +} + +static bool gen_xxx_ptr(DisasContext *ctx, arg_vvv *a, + gen_helper_gvec_3_ptr *fn) +{ + return gen_vvv_ptr_vl(ctx, a, 32, fn); +} + +static bool gen_vvv_vl(DisasContext *ctx, arg_vvv *a, uint32_t oprsz, + gen_helper_gvec_3 *fn) +{ + if (!check_vec(ctx, oprsz)) { + return true; + } + + tcg_gen_gvec_3_ool(vec_full_offset(a->vd), + vec_full_offset(a->vj), + vec_full_offset(a->vk), + oprsz, ctx->vl / 8, 0, fn); + return true; +} + +static bool gen_vvv(DisasContext *ctx, arg_vvv *a, gen_helper_gvec_3 *fn) +{ + return gen_vvv_vl(ctx, a, 16, fn); +} + +static bool gen_xxx(DisasContext *ctx, arg_vvv *a, gen_helper_gvec_3 *fn) +{ + return gen_vvv_vl(ctx, a, 32, fn); +} + +static bool gen_vv_ptr_vl(DisasContext *ctx, arg_vv *a, uint32_t oprsz, + gen_helper_gvec_2_ptr *fn) +{ + if (!check_vec(ctx, oprsz)) { + return true; + } + + tcg_gen_gvec_2_ptr(vec_full_offset(a->vd), + vec_full_offset(a->vj), + tcg_env, + oprsz, ctx->vl / 8, 0, fn); + return true; +} + +static bool gen_vv_ptr(DisasContext *ctx, arg_vv *a, + gen_helper_gvec_2_ptr *fn) +{ + return gen_vv_ptr_vl(ctx, a, 16, fn); +} + +static bool gen_xx_ptr(DisasContext *ctx, arg_vv *a, + gen_helper_gvec_2_ptr *fn) +{ + return gen_vv_ptr_vl(ctx, a, 32, fn); +} + +static bool gen_vv_vl(DisasContext *ctx, arg_vv *a, uint32_t oprsz, + gen_helper_gvec_2 *fn) +{ + if (!check_vec(ctx, oprsz)) { + return true; + } + + tcg_gen_gvec_2_ool(vec_full_offset(a->vd), + vec_full_offset(a->vj), + oprsz, ctx->vl / 8, 0, fn); + return true; +} + +static bool gen_vv(DisasContext *ctx, arg_vv *a, gen_helper_gvec_2 *fn) +{ + return gen_vv_vl(ctx, a, 16, fn); +} + +static bool gen_xx(DisasContext *ctx, arg_vv *a, gen_helper_gvec_2 *fn) +{ + return gen_vv_vl(ctx, a, 32, fn); +} + +static bool gen_vv_i_vl(DisasContext *ctx, arg_vv_i *a, uint32_t oprsz, + gen_helper_gvec_2i *fn) +{ + if (!check_vec(ctx, oprsz)) { + return true; + } + + tcg_gen_gvec_2i_ool(vec_full_offset(a->vd), + vec_full_offset(a->vj), + tcg_constant_i64(a->imm), + oprsz, ctx->vl / 8, 0, fn); + return true; +} + +static bool gen_vv_i(DisasContext *ctx, arg_vv_i *a, gen_helper_gvec_2i *fn) +{ + return gen_vv_i_vl(ctx, a, 16, fn); +} + +static bool gen_xx_i(DisasContext *ctx, arg_vv_i *a, gen_helper_gvec_2i *fn) +{ + return gen_vv_i_vl(ctx, a, 32, fn); +} + +static bool gen_cv_vl(DisasContext *ctx, arg_cv *a, uint32_t sz, + void (*func)(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32)) +{ + if (!check_vec(ctx, sz)) { + return true; + } + + TCGv_i32 vj = tcg_constant_i32(a->vj); + TCGv_i32 cd = tcg_constant_i32(a->cd); + TCGv_i32 oprsz = tcg_constant_i32(sz); + + func(tcg_env, oprsz, cd, vj); + return true; +} + +static bool gen_cv(DisasContext *ctx, arg_cv *a, + void (*func)(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32)) +{ + return gen_cv_vl(ctx, a, 16, func); +} + +static bool gen_cx(DisasContext *ctx, arg_cv *a, + void (*func)(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32)) +{ + return gen_cv_vl(ctx, a, 32, func); +} + +static bool gvec_vvv_vl(DisasContext *ctx, arg_vvv *a, + uint32_t oprsz, MemOp mop, + void (*func)(unsigned, uint32_t, uint32_t, + uint32_t, uint32_t, uint32_t)) +{ + uint32_t vd_ofs = vec_full_offset(a->vd); + uint32_t vj_ofs = vec_full_offset(a->vj); + uint32_t vk_ofs = vec_full_offset(a->vk); + + if (!check_vec(ctx, oprsz)) { + return true; + } + + func(mop, vd_ofs, vj_ofs, vk_ofs, oprsz, ctx->vl / 8); + return true; +} + +static bool gvec_vvv(DisasContext *ctx, arg_vvv *a, MemOp mop, + void (*func)(unsigned, uint32_t, uint32_t, + uint32_t, uint32_t, uint32_t)) +{ + return gvec_vvv_vl(ctx, a, 16, mop, func); +} + +static bool gvec_xxx(DisasContext *ctx, arg_vvv *a, MemOp mop, + void (*func)(unsigned, uint32_t, uint32_t, + uint32_t, uint32_t, uint32_t)) +{ + return gvec_vvv_vl(ctx, a, 32, mop, func); +} + +static bool gvec_vv_vl(DisasContext *ctx, arg_vv *a, + uint32_t oprsz, MemOp mop, + void (*func)(unsigned, uint32_t, uint32_t, + uint32_t, uint32_t)) +{ + uint32_t vd_ofs = vec_full_offset(a->vd); + uint32_t vj_ofs = vec_full_offset(a->vj); + + if (!check_vec(ctx, oprsz)) { + return true; + } + + func(mop, vd_ofs, vj_ofs, oprsz, ctx->vl / 8); + return true; +} + + +static bool gvec_vv(DisasContext *ctx, arg_vv *a, MemOp mop, + void (*func)(unsigned, uint32_t, uint32_t, + uint32_t, uint32_t)) +{ + return gvec_vv_vl(ctx, a, 16, mop, func); +} + +static bool gvec_xx(DisasContext *ctx, arg_vv *a, MemOp mop, + void (*func)(unsigned, uint32_t, uint32_t, + uint32_t, uint32_t)) +{ + return gvec_vv_vl(ctx, a, 32, mop, func); +} + +static bool gvec_vv_i_vl(DisasContext *ctx, arg_vv_i *a, + uint32_t oprsz, MemOp mop, + void (*func)(unsigned, uint32_t, uint32_t, + int64_t, uint32_t, uint32_t)) +{ + uint32_t vd_ofs = vec_full_offset(a->vd); + uint32_t vj_ofs = vec_full_offset(a->vj); + + if (!check_vec(ctx, oprsz)) { + return true; + } + + func(mop, vd_ofs, vj_ofs, a->imm, oprsz, ctx->vl / 8); + return true; +} + +static bool gvec_vv_i(DisasContext *ctx, arg_vv_i *a, MemOp mop, + void (*func)(unsigned, uint32_t, uint32_t, + int64_t, uint32_t, uint32_t)) +{ + return gvec_vv_i_vl(ctx, a, 16, mop, func); +} + +static bool gvec_xx_i(DisasContext *ctx, arg_vv_i *a, MemOp mop, + void (*func)(unsigned, uint32_t, uint32_t, + int64_t, uint32_t, uint32_t)) +{ + return gvec_vv_i_vl(ctx,a, 32, mop, func); +} + +static bool gvec_subi_vl(DisasContext *ctx, arg_vv_i *a, + uint32_t oprsz, MemOp mop) +{ + uint32_t vd_ofs = vec_full_offset(a->vd); + uint32_t vj_ofs = vec_full_offset(a->vj); + + if (!check_vec(ctx, oprsz)) { + return true; + } + + tcg_gen_gvec_addi(mop, vd_ofs, vj_ofs, -a->imm, oprsz, ctx->vl / 8); + return true; +} + +static bool gvec_subi(DisasContext *ctx, arg_vv_i *a, MemOp mop) +{ + return gvec_subi_vl(ctx, a, 16, mop); +} + +static bool gvec_xsubi(DisasContext *ctx, arg_vv_i *a, MemOp mop) +{ + return gvec_subi_vl(ctx, a, 32, mop); +} + +TRANS(vadd_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_add) +TRANS(vadd_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_add) +TRANS(vadd_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_add) +TRANS(vadd_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_add) +TRANS(xvadd_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_add) +TRANS(xvadd_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_add) +TRANS(xvadd_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_add) +TRANS(xvadd_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_add) + +static bool gen_vaddsub_q_vl(DisasContext *ctx, arg_vvv *a, uint32_t oprsz, + void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, + TCGv_i64, TCGv_i64, TCGv_i64)) +{ + int i; + TCGv_i64 rh, rl, ah, al, bh, bl; + + if (!check_vec(ctx, oprsz)) { + return true; + } + + rh = tcg_temp_new_i64(); + rl = tcg_temp_new_i64(); + ah = tcg_temp_new_i64(); + al = tcg_temp_new_i64(); + bh = tcg_temp_new_i64(); + bl = tcg_temp_new_i64(); + + for (i = 0; i < oprsz / 16; i++) { + get_vreg64(ah, a->vj, 1 + i * 2); + get_vreg64(al, a->vj, i * 2); + get_vreg64(bh, a->vk, 1 + i * 2); + get_vreg64(bl, a->vk, i * 2); + + func(rl, rh, al, ah, bl, bh); + + set_vreg64(rh, a->vd, 1 + i * 2); + set_vreg64(rl, a->vd, i * 2); + } + return true; +} + +static bool gen_vaddsub_q(DisasContext *ctx, arg_vvv *a, + void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, + TCGv_i64, TCGv_i64, TCGv_i64)) +{ + return gen_vaddsub_q_vl(ctx, a, 16, func); +} + +static bool gen_xvaddsub_q(DisasContext *ctx, arg_vvv *a, + void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, + TCGv_i64, TCGv_i64, TCGv_i64)) +{ + return gen_vaddsub_q_vl(ctx, a, 32, func); +} + +TRANS(vsub_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_sub) +TRANS(vsub_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_sub) +TRANS(vsub_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_sub) +TRANS(vsub_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_sub) +TRANS(xvsub_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_sub) +TRANS(xvsub_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_sub) +TRANS(xvsub_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_sub) +TRANS(xvsub_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_sub) + +TRANS(vadd_q, LSX, gen_vaddsub_q, tcg_gen_add2_i64) +TRANS(vsub_q, LSX, gen_vaddsub_q, tcg_gen_sub2_i64) +TRANS(xvadd_q, LASX, gen_xvaddsub_q, tcg_gen_add2_i64) +TRANS(xvsub_q, LASX, gen_xvaddsub_q, tcg_gen_sub2_i64) + +TRANS(vaddi_bu, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_addi) +TRANS(vaddi_hu, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_addi) +TRANS(vaddi_wu, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_addi) +TRANS(vaddi_du, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_addi) +TRANS(vsubi_bu, LSX, gvec_subi, MO_8) +TRANS(vsubi_hu, LSX, gvec_subi, MO_16) +TRANS(vsubi_wu, LSX, gvec_subi, MO_32) +TRANS(vsubi_du, LSX, gvec_subi, MO_64) +TRANS(xvaddi_bu, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_addi) +TRANS(xvaddi_hu, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_addi) +TRANS(xvaddi_wu, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_addi) +TRANS(xvaddi_du, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_addi) +TRANS(xvsubi_bu, LASX, gvec_xsubi, MO_8) +TRANS(xvsubi_hu, LASX, gvec_xsubi, MO_16) +TRANS(xvsubi_wu, LASX, gvec_xsubi, MO_32) +TRANS(xvsubi_du, LASX, gvec_xsubi, MO_64) + +TRANS(vneg_b, LSX, gvec_vv, MO_8, tcg_gen_gvec_neg) +TRANS(vneg_h, LSX, gvec_vv, MO_16, tcg_gen_gvec_neg) +TRANS(vneg_w, LSX, gvec_vv, MO_32, tcg_gen_gvec_neg) +TRANS(vneg_d, LSX, gvec_vv, MO_64, tcg_gen_gvec_neg) +TRANS(xvneg_b, LASX, gvec_xx, MO_8, tcg_gen_gvec_neg) +TRANS(xvneg_h, LASX, gvec_xx, MO_16, tcg_gen_gvec_neg) +TRANS(xvneg_w, LASX, gvec_xx, MO_32, tcg_gen_gvec_neg) +TRANS(xvneg_d, LASX, gvec_xx, MO_64, tcg_gen_gvec_neg) + +TRANS(vsadd_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_ssadd) +TRANS(vsadd_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_ssadd) +TRANS(vsadd_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_ssadd) +TRANS(vsadd_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_ssadd) +TRANS(vsadd_bu, LSX, gvec_vvv, MO_8, tcg_gen_gvec_usadd) +TRANS(vsadd_hu, LSX, gvec_vvv, MO_16, tcg_gen_gvec_usadd) +TRANS(vsadd_wu, LSX, gvec_vvv, MO_32, tcg_gen_gvec_usadd) +TRANS(vsadd_du, LSX, gvec_vvv, MO_64, tcg_gen_gvec_usadd) +TRANS(vssub_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_sssub) +TRANS(vssub_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_sssub) +TRANS(vssub_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_sssub) +TRANS(vssub_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_sssub) +TRANS(vssub_bu, LSX, gvec_vvv, MO_8, tcg_gen_gvec_ussub) +TRANS(vssub_hu, LSX, gvec_vvv, MO_16, tcg_gen_gvec_ussub) +TRANS(vssub_wu, LSX, gvec_vvv, MO_32, tcg_gen_gvec_ussub) +TRANS(vssub_du, LSX, gvec_vvv, MO_64, tcg_gen_gvec_ussub) + +TRANS(xvsadd_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_ssadd) +TRANS(xvsadd_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_ssadd) +TRANS(xvsadd_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_ssadd) +TRANS(xvsadd_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_ssadd) +TRANS(xvsadd_bu, LASX, gvec_xxx, MO_8, tcg_gen_gvec_usadd) +TRANS(xvsadd_hu, LASX, gvec_xxx, MO_16, tcg_gen_gvec_usadd) +TRANS(xvsadd_wu, LASX, gvec_xxx, MO_32, tcg_gen_gvec_usadd) +TRANS(xvsadd_du, LASX, gvec_xxx, MO_64, tcg_gen_gvec_usadd) +TRANS(xvssub_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_sssub) +TRANS(xvssub_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_sssub) +TRANS(xvssub_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_sssub) +TRANS(xvssub_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_sssub) +TRANS(xvssub_bu, LASX, gvec_xxx, MO_8, tcg_gen_gvec_ussub) +TRANS(xvssub_hu, LASX, gvec_xxx, MO_16, tcg_gen_gvec_ussub) +TRANS(xvssub_wu, LASX, gvec_xxx, MO_32, tcg_gen_gvec_ussub) +TRANS(xvssub_du, LASX, gvec_xxx, MO_64, tcg_gen_gvec_ussub) + +TRANS(vhaddw_h_b, LSX, gen_vvv, gen_helper_vhaddw_h_b) +TRANS(vhaddw_w_h, LSX, gen_vvv, gen_helper_vhaddw_w_h) +TRANS(vhaddw_d_w, LSX, gen_vvv, gen_helper_vhaddw_d_w) +TRANS(vhaddw_q_d, LSX, gen_vvv, gen_helper_vhaddw_q_d) +TRANS(vhaddw_hu_bu, LSX, gen_vvv, gen_helper_vhaddw_hu_bu) +TRANS(vhaddw_wu_hu, LSX, gen_vvv, gen_helper_vhaddw_wu_hu) +TRANS(vhaddw_du_wu, LSX, gen_vvv, gen_helper_vhaddw_du_wu) +TRANS(vhaddw_qu_du, LSX, gen_vvv, gen_helper_vhaddw_qu_du) +TRANS(vhsubw_h_b, LSX, gen_vvv, gen_helper_vhsubw_h_b) +TRANS(vhsubw_w_h, LSX, gen_vvv, gen_helper_vhsubw_w_h) +TRANS(vhsubw_d_w, LSX, gen_vvv, gen_helper_vhsubw_d_w) +TRANS(vhsubw_q_d, LSX, gen_vvv, gen_helper_vhsubw_q_d) +TRANS(vhsubw_hu_bu, LSX, gen_vvv, gen_helper_vhsubw_hu_bu) +TRANS(vhsubw_wu_hu, LSX, gen_vvv, gen_helper_vhsubw_wu_hu) +TRANS(vhsubw_du_wu, LSX, gen_vvv, gen_helper_vhsubw_du_wu) +TRANS(vhsubw_qu_du, LSX, gen_vvv, gen_helper_vhsubw_qu_du) + +TRANS(xvhaddw_h_b, LASX, gen_xxx, gen_helper_vhaddw_h_b) +TRANS(xvhaddw_w_h, LASX, gen_xxx, gen_helper_vhaddw_w_h) +TRANS(xvhaddw_d_w, LASX, gen_xxx, gen_helper_vhaddw_d_w) +TRANS(xvhaddw_q_d, LASX, gen_xxx, gen_helper_vhaddw_q_d) +TRANS(xvhaddw_hu_bu, LASX, gen_xxx, gen_helper_vhaddw_hu_bu) +TRANS(xvhaddw_wu_hu, LASX, gen_xxx, gen_helper_vhaddw_wu_hu) +TRANS(xvhaddw_du_wu, LASX, gen_xxx, gen_helper_vhaddw_du_wu) +TRANS(xvhaddw_qu_du, LASX, gen_xxx, gen_helper_vhaddw_qu_du) +TRANS(xvhsubw_h_b, LASX, gen_xxx, gen_helper_vhsubw_h_b) +TRANS(xvhsubw_w_h, LASX, gen_xxx, gen_helper_vhsubw_w_h) +TRANS(xvhsubw_d_w, LASX, gen_xxx, gen_helper_vhsubw_d_w) +TRANS(xvhsubw_q_d, LASX, gen_xxx, gen_helper_vhsubw_q_d) +TRANS(xvhsubw_hu_bu, LASX, gen_xxx, gen_helper_vhsubw_hu_bu) +TRANS(xvhsubw_wu_hu, LASX, gen_xxx, gen_helper_vhsubw_wu_hu) +TRANS(xvhsubw_du_wu, LASX, gen_xxx, gen_helper_vhsubw_du_wu) +TRANS(xvhsubw_qu_du, LASX, gen_xxx, gen_helper_vhsubw_qu_du) + +static void gen_vaddwev_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2; + + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + + /* Sign-extend the even elements from a */ + tcg_gen_shli_vec(vece, t1, a, halfbits); + tcg_gen_sari_vec(vece, t1, t1, halfbits); + + /* Sign-extend the even elements from b */ + tcg_gen_shli_vec(vece, t2, b, halfbits); + tcg_gen_sari_vec(vece, t2, t2, halfbits); + + tcg_gen_add_vec(vece, t, t1, t2); +} + +static void gen_vaddwev_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_ext16s_i32(t1, a); + tcg_gen_ext16s_i32(t2, b); + tcg_gen_add_i32(t, t1, t2); +} + +static void gen_vaddwev_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_ext32s_i64(t1, a); + tcg_gen_ext32s_i64(t2, b); + tcg_gen_add_i64(t, t1, t2); +} + +static void do_vaddwev_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vaddwev_s, + .fno = gen_helper_vaddwev_h_b, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vaddwev_w_h, + .fniv = gen_vaddwev_s, + .fno = gen_helper_vaddwev_w_h, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vaddwev_d_w, + .fniv = gen_vaddwev_s, + .fno = gen_helper_vaddwev_d_w, + .opt_opc = vecop_list, + .vece = MO_64 + }, + { + .fno = gen_helper_vaddwev_q_d, + .vece = MO_128 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vaddwev_h_b, LSX, gvec_vvv, MO_8, do_vaddwev_s) +TRANS(vaddwev_w_h, LSX, gvec_vvv, MO_16, do_vaddwev_s) +TRANS(vaddwev_d_w, LSX, gvec_vvv, MO_32, do_vaddwev_s) +TRANS(vaddwev_q_d, LSX, gvec_vvv, MO_64, do_vaddwev_s) +TRANS(xvaddwev_h_b, LASX, gvec_xxx, MO_8, do_vaddwev_s) +TRANS(xvaddwev_w_h, LASX, gvec_xxx, MO_16, do_vaddwev_s) +TRANS(xvaddwev_d_w, LASX, gvec_xxx, MO_32, do_vaddwev_s) +TRANS(xvaddwev_q_d, LASX, gvec_xxx, MO_64, do_vaddwev_s) + +static void gen_vaddwod_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_sari_i32(t1, a, 16); + tcg_gen_sari_i32(t2, b, 16); + tcg_gen_add_i32(t, t1, t2); +} + +static void gen_vaddwod_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_sari_i64(t1, a, 32); + tcg_gen_sari_i64(t2, b, 32); + tcg_gen_add_i64(t, t1, t2); +} + +static void gen_vaddwod_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2; + + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + + /* Sign-extend the odd elements for vector */ + tcg_gen_sari_vec(vece, t1, a, halfbits); + tcg_gen_sari_vec(vece, t2, b, halfbits); + + tcg_gen_add_vec(vece, t, t1, t2); +} + +static void do_vaddwod_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vaddwod_s, + .fno = gen_helper_vaddwod_h_b, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vaddwod_w_h, + .fniv = gen_vaddwod_s, + .fno = gen_helper_vaddwod_w_h, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vaddwod_d_w, + .fniv = gen_vaddwod_s, + .fno = gen_helper_vaddwod_d_w, + .opt_opc = vecop_list, + .vece = MO_64 + }, + { + .fno = gen_helper_vaddwod_q_d, + .vece = MO_128 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vaddwod_h_b, LSX, gvec_vvv, MO_8, do_vaddwod_s) +TRANS(vaddwod_w_h, LSX, gvec_vvv, MO_16, do_vaddwod_s) +TRANS(vaddwod_d_w, LSX, gvec_vvv, MO_32, do_vaddwod_s) +TRANS(vaddwod_q_d, LSX, gvec_vvv, MO_64, do_vaddwod_s) +TRANS(xvaddwod_h_b, LASX, gvec_xxx, MO_8, do_vaddwod_s) +TRANS(xvaddwod_w_h, LASX, gvec_xxx, MO_16, do_vaddwod_s) +TRANS(xvaddwod_d_w, LASX, gvec_xxx, MO_32, do_vaddwod_s) +TRANS(xvaddwod_q_d, LASX, gvec_xxx, MO_64, do_vaddwod_s) + + +static void gen_vsubwev_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2; + + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + + /* Sign-extend the even elements from a */ + tcg_gen_shli_vec(vece, t1, a, halfbits); + tcg_gen_sari_vec(vece, t1, t1, halfbits); + + /* Sign-extend the even elements from b */ + tcg_gen_shli_vec(vece, t2, b, halfbits); + tcg_gen_sari_vec(vece, t2, t2, halfbits); + + tcg_gen_sub_vec(vece, t, t1, t2); +} + +static void gen_vsubwev_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_ext16s_i32(t1, a); + tcg_gen_ext16s_i32(t2, b); + tcg_gen_sub_i32(t, t1, t2); +} + +static void gen_vsubwev_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_ext32s_i64(t1, a); + tcg_gen_ext32s_i64(t2, b); + tcg_gen_sub_i64(t, t1, t2); +} + +static void do_vsubwev_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vsubwev_s, + .fno = gen_helper_vsubwev_h_b, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vsubwev_w_h, + .fniv = gen_vsubwev_s, + .fno = gen_helper_vsubwev_w_h, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vsubwev_d_w, + .fniv = gen_vsubwev_s, + .fno = gen_helper_vsubwev_d_w, + .opt_opc = vecop_list, + .vece = MO_64 + }, + { + .fno = gen_helper_vsubwev_q_d, + .vece = MO_128 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vsubwev_h_b, LSX, gvec_vvv, MO_8, do_vsubwev_s) +TRANS(vsubwev_w_h, LSX, gvec_vvv, MO_16, do_vsubwev_s) +TRANS(vsubwev_d_w, LSX, gvec_vvv, MO_32, do_vsubwev_s) +TRANS(vsubwev_q_d, LSX, gvec_vvv, MO_64, do_vsubwev_s) +TRANS(xvsubwev_h_b, LASX, gvec_xxx, MO_8, do_vsubwev_s) +TRANS(xvsubwev_w_h, LASX, gvec_xxx, MO_16, do_vsubwev_s) +TRANS(xvsubwev_d_w, LASX, gvec_xxx, MO_32, do_vsubwev_s) +TRANS(xvsubwev_q_d, LASX, gvec_xxx, MO_64, do_vsubwev_s) + +static void gen_vsubwod_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2; + + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + + /* Sign-extend the odd elements for vector */ + tcg_gen_sari_vec(vece, t1, a, halfbits); + tcg_gen_sari_vec(vece, t2, b, halfbits); + + tcg_gen_sub_vec(vece, t, t1, t2); +} + +static void gen_vsubwod_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_sari_i32(t1, a, 16); + tcg_gen_sari_i32(t2, b, 16); + tcg_gen_sub_i32(t, t1, t2); +} + +static void gen_vsubwod_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_sari_i64(t1, a, 32); + tcg_gen_sari_i64(t2, b, 32); + tcg_gen_sub_i64(t, t1, t2); +} + +static void do_vsubwod_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vsubwod_s, + .fno = gen_helper_vsubwod_h_b, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vsubwod_w_h, + .fniv = gen_vsubwod_s, + .fno = gen_helper_vsubwod_w_h, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vsubwod_d_w, + .fniv = gen_vsubwod_s, + .fno = gen_helper_vsubwod_d_w, + .opt_opc = vecop_list, + .vece = MO_64 + }, + { + .fno = gen_helper_vsubwod_q_d, + .vece = MO_128 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vsubwod_h_b, LSX, gvec_vvv, MO_8, do_vsubwod_s) +TRANS(vsubwod_w_h, LSX, gvec_vvv, MO_16, do_vsubwod_s) +TRANS(vsubwod_d_w, LSX, gvec_vvv, MO_32, do_vsubwod_s) +TRANS(vsubwod_q_d, LSX, gvec_vvv, MO_64, do_vsubwod_s) +TRANS(xvsubwod_h_b, LASX, gvec_xxx, MO_8, do_vsubwod_s) +TRANS(xvsubwod_w_h, LASX, gvec_xxx, MO_16, do_vsubwod_s) +TRANS(xvsubwod_d_w, LASX, gvec_xxx, MO_32, do_vsubwod_s) +TRANS(xvsubwod_q_d, LASX, gvec_xxx, MO_64, do_vsubwod_s) + +static void gen_vaddwev_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2, t3; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + t3 = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); + tcg_gen_and_vec(vece, t1, a, t3); + tcg_gen_and_vec(vece, t2, b, t3); + tcg_gen_add_vec(vece, t, t1, t2); +} + +static void gen_vaddwev_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_ext16u_i32(t1, a); + tcg_gen_ext16u_i32(t2, b); + tcg_gen_add_i32(t, t1, t2); +} + +static void gen_vaddwev_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(t1, a); + tcg_gen_ext32u_i64(t2, b); + tcg_gen_add_i64(t, t1, t2); +} + +static void do_vaddwev_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vaddwev_u, + .fno = gen_helper_vaddwev_h_bu, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vaddwev_w_hu, + .fniv = gen_vaddwev_u, + .fno = gen_helper_vaddwev_w_hu, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vaddwev_d_wu, + .fniv = gen_vaddwev_u, + .fno = gen_helper_vaddwev_d_wu, + .opt_opc = vecop_list, + .vece = MO_64 + }, + { + .fno = gen_helper_vaddwev_q_du, + .vece = MO_128 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vaddwev_h_bu, LSX, gvec_vvv, MO_8, do_vaddwev_u) +TRANS(vaddwev_w_hu, LSX, gvec_vvv, MO_16, do_vaddwev_u) +TRANS(vaddwev_d_wu, LSX, gvec_vvv, MO_32, do_vaddwev_u) +TRANS(vaddwev_q_du, LSX, gvec_vvv, MO_64, do_vaddwev_u) +TRANS(xvaddwev_h_bu, LASX, gvec_xxx, MO_8, do_vaddwev_u) +TRANS(xvaddwev_w_hu, LASX, gvec_xxx, MO_16, do_vaddwev_u) +TRANS(xvaddwev_d_wu, LASX, gvec_xxx, MO_32, do_vaddwev_u) +TRANS(xvaddwev_q_du, LASX, gvec_xxx, MO_64, do_vaddwev_u) + +static void gen_vaddwod_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2; + + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + + /* Zero-extend the odd elements for vector */ + tcg_gen_shri_vec(vece, t1, a, halfbits); + tcg_gen_shri_vec(vece, t2, b, halfbits); + + tcg_gen_add_vec(vece, t, t1, t2); +} + +static void gen_vaddwod_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_shri_i32(t1, a, 16); + tcg_gen_shri_i32(t2, b, 16); + tcg_gen_add_i32(t, t1, t2); +} + +static void gen_vaddwod_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_shri_i64(t1, a, 32); + tcg_gen_shri_i64(t2, b, 32); + tcg_gen_add_i64(t, t1, t2); +} + +static void do_vaddwod_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vaddwod_u, + .fno = gen_helper_vaddwod_h_bu, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vaddwod_w_hu, + .fniv = gen_vaddwod_u, + .fno = gen_helper_vaddwod_w_hu, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vaddwod_d_wu, + .fniv = gen_vaddwod_u, + .fno = gen_helper_vaddwod_d_wu, + .opt_opc = vecop_list, + .vece = MO_64 + }, + { + .fno = gen_helper_vaddwod_q_du, + .vece = MO_128 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vaddwod_h_bu, LSX, gvec_vvv, MO_8, do_vaddwod_u) +TRANS(vaddwod_w_hu, LSX, gvec_vvv, MO_16, do_vaddwod_u) +TRANS(vaddwod_d_wu, LSX, gvec_vvv, MO_32, do_vaddwod_u) +TRANS(vaddwod_q_du, LSX, gvec_vvv, MO_64, do_vaddwod_u) +TRANS(xvaddwod_h_bu, LASX, gvec_xxx, MO_8, do_vaddwod_u) +TRANS(xvaddwod_w_hu, LASX, gvec_xxx, MO_16, do_vaddwod_u) +TRANS(xvaddwod_d_wu, LASX, gvec_xxx, MO_32, do_vaddwod_u) +TRANS(xvaddwod_q_du, LASX, gvec_xxx, MO_64, do_vaddwod_u) + +static void gen_vsubwev_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2, t3; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + t3 = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); + tcg_gen_and_vec(vece, t1, a, t3); + tcg_gen_and_vec(vece, t2, b, t3); + tcg_gen_sub_vec(vece, t, t1, t2); +} + +static void gen_vsubwev_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_ext16u_i32(t1, a); + tcg_gen_ext16u_i32(t2, b); + tcg_gen_sub_i32(t, t1, t2); +} + +static void gen_vsubwev_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(t1, a); + tcg_gen_ext32u_i64(t2, b); + tcg_gen_sub_i64(t, t1, t2); +} + +static void do_vsubwev_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sub_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vsubwev_u, + .fno = gen_helper_vsubwev_h_bu, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vsubwev_w_hu, + .fniv = gen_vsubwev_u, + .fno = gen_helper_vsubwev_w_hu, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vsubwev_d_wu, + .fniv = gen_vsubwev_u, + .fno = gen_helper_vsubwev_d_wu, + .opt_opc = vecop_list, + .vece = MO_64 + }, + { + .fno = gen_helper_vsubwev_q_du, + .vece = MO_128 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vsubwev_h_bu, LSX, gvec_vvv, MO_8, do_vsubwev_u) +TRANS(vsubwev_w_hu, LSX, gvec_vvv, MO_16, do_vsubwev_u) +TRANS(vsubwev_d_wu, LSX, gvec_vvv, MO_32, do_vsubwev_u) +TRANS(vsubwev_q_du, LSX, gvec_vvv, MO_64, do_vsubwev_u) +TRANS(xvsubwev_h_bu, LASX, gvec_xxx, MO_8, do_vsubwev_u) +TRANS(xvsubwev_w_hu, LASX, gvec_xxx, MO_16, do_vsubwev_u) +TRANS(xvsubwev_d_wu, LASX, gvec_xxx, MO_32, do_vsubwev_u) +TRANS(xvsubwev_q_du, LASX, gvec_xxx, MO_64, do_vsubwev_u) + +static void gen_vsubwod_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2; + + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + + /* Zero-extend the odd elements for vector */ + tcg_gen_shri_vec(vece, t1, a, halfbits); + tcg_gen_shri_vec(vece, t2, b, halfbits); + + tcg_gen_sub_vec(vece, t, t1, t2); +} + +static void gen_vsubwod_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_shri_i32(t1, a, 16); + tcg_gen_shri_i32(t2, b, 16); + tcg_gen_sub_i32(t, t1, t2); +} + +static void gen_vsubwod_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_shri_i64(t1, a, 32); + tcg_gen_shri_i64(t2, b, 32); + tcg_gen_sub_i64(t, t1, t2); +} + +static void do_vsubwod_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vsubwod_u, + .fno = gen_helper_vsubwod_h_bu, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vsubwod_w_hu, + .fniv = gen_vsubwod_u, + .fno = gen_helper_vsubwod_w_hu, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vsubwod_d_wu, + .fniv = gen_vsubwod_u, + .fno = gen_helper_vsubwod_d_wu, + .opt_opc = vecop_list, + .vece = MO_64 + }, + { + .fno = gen_helper_vsubwod_q_du, + .vece = MO_128 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vsubwod_h_bu, LSX, gvec_vvv, MO_8, do_vsubwod_u) +TRANS(vsubwod_w_hu, LSX, gvec_vvv, MO_16, do_vsubwod_u) +TRANS(vsubwod_d_wu, LSX, gvec_vvv, MO_32, do_vsubwod_u) +TRANS(vsubwod_q_du, LSX, gvec_vvv, MO_64, do_vsubwod_u) +TRANS(xvsubwod_h_bu, LASX, gvec_xxx, MO_8, do_vsubwod_u) +TRANS(xvsubwod_w_hu, LASX, gvec_xxx, MO_16, do_vsubwod_u) +TRANS(xvsubwod_d_wu, LASX, gvec_xxx, MO_32, do_vsubwod_u) +TRANS(xvsubwod_q_du, LASX, gvec_xxx, MO_64, do_vsubwod_u) + +static void gen_vaddwev_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2, t3; + + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + t3 = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, halfbits)); + + /* Zero-extend the even elements from a */ + tcg_gen_and_vec(vece, t1, a, t3); + + /* Sign-extend the even elements from b */ + tcg_gen_shli_vec(vece, t2, b, halfbits); + tcg_gen_sari_vec(vece, t2, t2, halfbits); + + tcg_gen_add_vec(vece, t, t1, t2); +} + +static void gen_vaddwev_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_ext16u_i32(t1, a); + tcg_gen_ext16s_i32(t2, b); + tcg_gen_add_i32(t, t1, t2); +} + +static void gen_vaddwev_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(t1, a); + tcg_gen_ext32s_i64(t2, b); + tcg_gen_add_i64(t, t1, t2); +} + +static void do_vaddwev_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vaddwev_u_s, + .fno = gen_helper_vaddwev_h_bu_b, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vaddwev_w_hu_h, + .fniv = gen_vaddwev_u_s, + .fno = gen_helper_vaddwev_w_hu_h, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vaddwev_d_wu_w, + .fniv = gen_vaddwev_u_s, + .fno = gen_helper_vaddwev_d_wu_w, + .opt_opc = vecop_list, + .vece = MO_64 + }, + { + .fno = gen_helper_vaddwev_q_du_d, + .vece = MO_128 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vaddwev_h_bu_b, LSX, gvec_vvv, MO_8, do_vaddwev_u_s) +TRANS(vaddwev_w_hu_h, LSX, gvec_vvv, MO_16, do_vaddwev_u_s) +TRANS(vaddwev_d_wu_w, LSX, gvec_vvv, MO_32, do_vaddwev_u_s) +TRANS(vaddwev_q_du_d, LSX, gvec_vvv, MO_64, do_vaddwev_u_s) +TRANS(xvaddwev_h_bu_b, LASX, gvec_xxx, MO_8, do_vaddwev_u_s) +TRANS(xvaddwev_w_hu_h, LASX, gvec_xxx, MO_16, do_vaddwev_u_s) +TRANS(xvaddwev_d_wu_w, LASX, gvec_xxx, MO_32, do_vaddwev_u_s) +TRANS(xvaddwev_q_du_d, LASX, gvec_xxx, MO_64, do_vaddwev_u_s) + +static void gen_vaddwod_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2; + + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + + /* Zero-extend the odd elements from a */ + tcg_gen_shri_vec(vece, t1, a, halfbits); + /* Sign-extend the odd elements from b */ + tcg_gen_sari_vec(vece, t2, b, halfbits); + + tcg_gen_add_vec(vece, t, t1, t2); +} + +static void gen_vaddwod_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_shri_i32(t1, a, 16); + tcg_gen_sari_i32(t2, b, 16); + tcg_gen_add_i32(t, t1, t2); +} + +static void gen_vaddwod_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_shri_i64(t1, a, 32); + tcg_gen_sari_i64(t2, b, 32); + tcg_gen_add_i64(t, t1, t2); +} + +static void do_vaddwod_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vaddwod_u_s, + .fno = gen_helper_vaddwod_h_bu_b, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vaddwod_w_hu_h, + .fniv = gen_vaddwod_u_s, + .fno = gen_helper_vaddwod_w_hu_h, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vaddwod_d_wu_w, + .fniv = gen_vaddwod_u_s, + .fno = gen_helper_vaddwod_d_wu_w, + .opt_opc = vecop_list, + .vece = MO_64 + }, + { + .fno = gen_helper_vaddwod_q_du_d, + .vece = MO_128 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vaddwod_h_bu_b, LSX, gvec_vvv, MO_8, do_vaddwod_u_s) +TRANS(vaddwod_w_hu_h, LSX, gvec_vvv, MO_16, do_vaddwod_u_s) +TRANS(vaddwod_d_wu_w, LSX, gvec_vvv, MO_32, do_vaddwod_u_s) +TRANS(vaddwod_q_du_d, LSX, gvec_vvv, MO_64, do_vaddwod_u_s) +TRANS(xvaddwod_h_bu_b, LSX, gvec_xxx, MO_8, do_vaddwod_u_s) +TRANS(xvaddwod_w_hu_h, LSX, gvec_xxx, MO_16, do_vaddwod_u_s) +TRANS(xvaddwod_d_wu_w, LSX, gvec_xxx, MO_32, do_vaddwod_u_s) +TRANS(xvaddwod_q_du_d, LSX, gvec_xxx, MO_64, do_vaddwod_u_s) + +static void do_vavg(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, + void (*gen_shr_vec)(unsigned, TCGv_vec, + TCGv_vec, int64_t), + void (*gen_round_vec)(unsigned, TCGv_vec, + TCGv_vec, TCGv_vec)) +{ + TCGv_vec tmp = tcg_temp_new_vec_matching(t); + gen_round_vec(vece, tmp, a, b); + tcg_gen_and_vec(vece, tmp, tmp, tcg_constant_vec_matching(t, vece, 1)); + gen_shr_vec(vece, a, a, 1); + gen_shr_vec(vece, b, b, 1); + tcg_gen_add_vec(vece, t, a, b); + tcg_gen_add_vec(vece, t, t, tmp); +} + +static void gen_vavg_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + do_vavg(vece, t, a, b, tcg_gen_sari_vec, tcg_gen_and_vec); +} + +static void gen_vavg_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + do_vavg(vece, t, a, b, tcg_gen_shri_vec, tcg_gen_and_vec); +} + +static void gen_vavgr_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + do_vavg(vece, t, a, b, tcg_gen_sari_vec, tcg_gen_or_vec); +} + +static void gen_vavgr_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + do_vavg(vece, t, a, b, tcg_gen_shri_vec, tcg_gen_or_vec); +} + +static void do_vavg_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vavg_s, + .fno = gen_helper_vavg_b, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vavg_s, + .fno = gen_helper_vavg_h, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vavg_s, + .fno = gen_helper_vavg_w, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vavg_s, + .fno = gen_helper_vavg_d, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +static void do_vavg_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vavg_u, + .fno = gen_helper_vavg_bu, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vavg_u, + .fno = gen_helper_vavg_hu, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vavg_u, + .fno = gen_helper_vavg_wu, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vavg_u, + .fno = gen_helper_vavg_du, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vavg_b, LSX, gvec_vvv, MO_8, do_vavg_s) +TRANS(vavg_h, LSX, gvec_vvv, MO_16, do_vavg_s) +TRANS(vavg_w, LSX, gvec_vvv, MO_32, do_vavg_s) +TRANS(vavg_d, LSX, gvec_vvv, MO_64, do_vavg_s) +TRANS(vavg_bu, LSX, gvec_vvv, MO_8, do_vavg_u) +TRANS(vavg_hu, LSX, gvec_vvv, MO_16, do_vavg_u) +TRANS(vavg_wu, LSX, gvec_vvv, MO_32, do_vavg_u) +TRANS(vavg_du, LSX, gvec_vvv, MO_64, do_vavg_u) +TRANS(xvavg_b, LASX, gvec_xxx, MO_8, do_vavg_s) +TRANS(xvavg_h, LASX, gvec_xxx, MO_16, do_vavg_s) +TRANS(xvavg_w, LASX, gvec_xxx, MO_32, do_vavg_s) +TRANS(xvavg_d, LASX, gvec_xxx, MO_64, do_vavg_s) +TRANS(xvavg_bu, LASX, gvec_xxx, MO_8, do_vavg_u) +TRANS(xvavg_hu, LASX, gvec_xxx, MO_16, do_vavg_u) +TRANS(xvavg_wu, LASX, gvec_xxx, MO_32, do_vavg_u) +TRANS(xvavg_du, LASX, gvec_xxx, MO_64, do_vavg_u) + +static void do_vavgr_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vavgr_s, + .fno = gen_helper_vavgr_b, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vavgr_s, + .fno = gen_helper_vavgr_h, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vavgr_s, + .fno = gen_helper_vavgr_w, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vavgr_s, + .fno = gen_helper_vavgr_d, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +static void do_vavgr_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vavgr_u, + .fno = gen_helper_vavgr_bu, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vavgr_u, + .fno = gen_helper_vavgr_hu, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vavgr_u, + .fno = gen_helper_vavgr_wu, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vavgr_u, + .fno = gen_helper_vavgr_du, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vavgr_b, LSX, gvec_vvv, MO_8, do_vavgr_s) +TRANS(vavgr_h, LSX, gvec_vvv, MO_16, do_vavgr_s) +TRANS(vavgr_w, LSX, gvec_vvv, MO_32, do_vavgr_s) +TRANS(vavgr_d, LSX, gvec_vvv, MO_64, do_vavgr_s) +TRANS(vavgr_bu, LSX, gvec_vvv, MO_8, do_vavgr_u) +TRANS(vavgr_hu, LSX, gvec_vvv, MO_16, do_vavgr_u) +TRANS(vavgr_wu, LSX, gvec_vvv, MO_32, do_vavgr_u) +TRANS(vavgr_du, LSX, gvec_vvv, MO_64, do_vavgr_u) +TRANS(xvavgr_b, LASX, gvec_xxx, MO_8, do_vavgr_s) +TRANS(xvavgr_h, LASX, gvec_xxx, MO_16, do_vavgr_s) +TRANS(xvavgr_w, LASX, gvec_xxx, MO_32, do_vavgr_s) +TRANS(xvavgr_d, LASX, gvec_xxx, MO_64, do_vavgr_s) +TRANS(xvavgr_bu, LASX, gvec_xxx, MO_8, do_vavgr_u) +TRANS(xvavgr_hu, LASX, gvec_xxx, MO_16, do_vavgr_u) +TRANS(xvavgr_wu, LASX, gvec_xxx, MO_32, do_vavgr_u) +TRANS(xvavgr_du, LASX, gvec_xxx, MO_64, do_vavgr_u) + +static void gen_vabsd_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_smax_vec(vece, t, a, b); + tcg_gen_smin_vec(vece, a, a, b); + tcg_gen_sub_vec(vece, t, t, a); +} + +static void do_vabsd_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_smax_vec, INDEX_op_smin_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vabsd_s, + .fno = gen_helper_vabsd_b, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vabsd_s, + .fno = gen_helper_vabsd_h, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vabsd_s, + .fno = gen_helper_vabsd_w, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vabsd_s, + .fno = gen_helper_vabsd_d, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +static void gen_vabsd_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_umax_vec(vece, t, a, b); + tcg_gen_umin_vec(vece, a, a, b); + tcg_gen_sub_vec(vece, t, t, a); +} + +static void do_vabsd_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_umax_vec, INDEX_op_umin_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vabsd_u, + .fno = gen_helper_vabsd_bu, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vabsd_u, + .fno = gen_helper_vabsd_hu, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vabsd_u, + .fno = gen_helper_vabsd_wu, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vabsd_u, + .fno = gen_helper_vabsd_du, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vabsd_b, LSX, gvec_vvv, MO_8, do_vabsd_s) +TRANS(vabsd_h, LSX, gvec_vvv, MO_16, do_vabsd_s) +TRANS(vabsd_w, LSX, gvec_vvv, MO_32, do_vabsd_s) +TRANS(vabsd_d, LSX, gvec_vvv, MO_64, do_vabsd_s) +TRANS(vabsd_bu, LSX, gvec_vvv, MO_8, do_vabsd_u) +TRANS(vabsd_hu, LSX, gvec_vvv, MO_16, do_vabsd_u) +TRANS(vabsd_wu, LSX, gvec_vvv, MO_32, do_vabsd_u) +TRANS(vabsd_du, LSX, gvec_vvv, MO_64, do_vabsd_u) +TRANS(xvabsd_b, LASX, gvec_xxx, MO_8, do_vabsd_s) +TRANS(xvabsd_h, LASX, gvec_xxx, MO_16, do_vabsd_s) +TRANS(xvabsd_w, LASX, gvec_xxx, MO_32, do_vabsd_s) +TRANS(xvabsd_d, LASX, gvec_xxx, MO_64, do_vabsd_s) +TRANS(xvabsd_bu, LASX, gvec_xxx, MO_8, do_vabsd_u) +TRANS(xvabsd_hu, LASX, gvec_xxx, MO_16, do_vabsd_u) +TRANS(xvabsd_wu, LASX, gvec_xxx, MO_32, do_vabsd_u) +TRANS(xvabsd_du, LASX, gvec_xxx, MO_64, do_vabsd_u) + +static void gen_vadda(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + + tcg_gen_abs_vec(vece, t1, a); + tcg_gen_abs_vec(vece, t2, b); + tcg_gen_add_vec(vece, t, t1, t2); +} + +static void do_vadda(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_abs_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vadda, + .fno = gen_helper_vadda_b, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vadda, + .fno = gen_helper_vadda_h, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vadda, + .fno = gen_helper_vadda_w, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vadda, + .fno = gen_helper_vadda_d, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vadda_b, LSX, gvec_vvv, MO_8, do_vadda) +TRANS(vadda_h, LSX, gvec_vvv, MO_16, do_vadda) +TRANS(vadda_w, LSX, gvec_vvv, MO_32, do_vadda) +TRANS(vadda_d, LSX, gvec_vvv, MO_64, do_vadda) +TRANS(xvadda_b, LASX, gvec_xxx, MO_8, do_vadda) +TRANS(xvadda_h, LASX, gvec_xxx, MO_16, do_vadda) +TRANS(xvadda_w, LASX, gvec_xxx, MO_32, do_vadda) +TRANS(xvadda_d, LASX, gvec_xxx, MO_64, do_vadda) + +TRANS(vmax_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_smax) +TRANS(vmax_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_smax) +TRANS(vmax_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_smax) +TRANS(vmax_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_smax) +TRANS(vmax_bu, LSX, gvec_vvv, MO_8, tcg_gen_gvec_umax) +TRANS(vmax_hu, LSX, gvec_vvv, MO_16, tcg_gen_gvec_umax) +TRANS(vmax_wu, LSX, gvec_vvv, MO_32, tcg_gen_gvec_umax) +TRANS(vmax_du, LSX, gvec_vvv, MO_64, tcg_gen_gvec_umax) +TRANS(xvmax_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_smax) +TRANS(xvmax_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_smax) +TRANS(xvmax_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_smax) +TRANS(xvmax_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_smax) +TRANS(xvmax_bu, LASX, gvec_xxx, MO_8, tcg_gen_gvec_umax) +TRANS(xvmax_hu, LASX, gvec_xxx, MO_16, tcg_gen_gvec_umax) +TRANS(xvmax_wu, LASX, gvec_xxx, MO_32, tcg_gen_gvec_umax) +TRANS(xvmax_du, LASX, gvec_xxx, MO_64, tcg_gen_gvec_umax) + +TRANS(vmin_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_smin) +TRANS(vmin_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_smin) +TRANS(vmin_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_smin) +TRANS(vmin_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_smin) +TRANS(vmin_bu, LSX, gvec_vvv, MO_8, tcg_gen_gvec_umin) +TRANS(vmin_hu, LSX, gvec_vvv, MO_16, tcg_gen_gvec_umin) +TRANS(vmin_wu, LSX, gvec_vvv, MO_32, tcg_gen_gvec_umin) +TRANS(vmin_du, LSX, gvec_vvv, MO_64, tcg_gen_gvec_umin) +TRANS(xvmin_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_smin) +TRANS(xvmin_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_smin) +TRANS(xvmin_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_smin) +TRANS(xvmin_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_smin) +TRANS(xvmin_bu, LASX, gvec_xxx, MO_8, tcg_gen_gvec_umin) +TRANS(xvmin_hu, LASX, gvec_xxx, MO_16, tcg_gen_gvec_umin) +TRANS(xvmin_wu, LASX, gvec_xxx, MO_32, tcg_gen_gvec_umin) +TRANS(xvmin_du, LASX, gvec_xxx, MO_64, tcg_gen_gvec_umin) + +static void gen_vmini_s(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +{ + tcg_gen_smin_vec(vece, t, a, tcg_constant_vec_matching(t, vece, imm)); +} + +static void gen_vmini_u(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +{ + tcg_gen_umin_vec(vece, t, a, tcg_constant_vec_matching(t, vece, imm)); +} + +static void gen_vmaxi_s(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +{ + tcg_gen_smax_vec(vece, t, a, tcg_constant_vec_matching(t, vece, imm)); +} + +static void gen_vmaxi_u(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +{ + tcg_gen_umax_vec(vece, t, a, tcg_constant_vec_matching(t, vece, imm)); +} + +static void do_vmini_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + int64_t imm, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_smin_vec, 0 + }; + static const GVecGen2i op[4] = { + { + .fniv = gen_vmini_s, + .fnoi = gen_helper_vmini_b, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vmini_s, + .fnoi = gen_helper_vmini_h, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vmini_s, + .fnoi = gen_helper_vmini_w, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vmini_s, + .fnoi = gen_helper_vmini_d, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); +} + +static void do_vmini_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + int64_t imm, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_umin_vec, 0 + }; + static const GVecGen2i op[4] = { + { + .fniv = gen_vmini_u, + .fnoi = gen_helper_vmini_bu, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vmini_u, + .fnoi = gen_helper_vmini_hu, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vmini_u, + .fnoi = gen_helper_vmini_wu, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vmini_u, + .fnoi = gen_helper_vmini_du, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); +} + +TRANS(vmini_b, LSX, gvec_vv_i, MO_8, do_vmini_s) +TRANS(vmini_h, LSX, gvec_vv_i, MO_16, do_vmini_s) +TRANS(vmini_w, LSX, gvec_vv_i, MO_32, do_vmini_s) +TRANS(vmini_d, LSX, gvec_vv_i, MO_64, do_vmini_s) +TRANS(vmini_bu, LSX, gvec_vv_i, MO_8, do_vmini_u) +TRANS(vmini_hu, LSX, gvec_vv_i, MO_16, do_vmini_u) +TRANS(vmini_wu, LSX, gvec_vv_i, MO_32, do_vmini_u) +TRANS(vmini_du, LSX, gvec_vv_i, MO_64, do_vmini_u) +TRANS(xvmini_b, LASX, gvec_xx_i, MO_8, do_vmini_s) +TRANS(xvmini_h, LASX, gvec_xx_i, MO_16, do_vmini_s) +TRANS(xvmini_w, LASX, gvec_xx_i, MO_32, do_vmini_s) +TRANS(xvmini_d, LASX, gvec_xx_i, MO_64, do_vmini_s) +TRANS(xvmini_bu, LASX, gvec_xx_i, MO_8, do_vmini_u) +TRANS(xvmini_hu, LASX, gvec_xx_i, MO_16, do_vmini_u) +TRANS(xvmini_wu, LASX, gvec_xx_i, MO_32, do_vmini_u) +TRANS(xvmini_du, LASX, gvec_xx_i, MO_64, do_vmini_u) + +static void do_vmaxi_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + int64_t imm, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_smax_vec, 0 + }; + static const GVecGen2i op[4] = { + { + .fniv = gen_vmaxi_s, + .fnoi = gen_helper_vmaxi_b, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vmaxi_s, + .fnoi = gen_helper_vmaxi_h, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vmaxi_s, + .fnoi = gen_helper_vmaxi_w, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vmaxi_s, + .fnoi = gen_helper_vmaxi_d, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); +} + +static void do_vmaxi_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + int64_t imm, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_umax_vec, 0 + }; + static const GVecGen2i op[4] = { + { + .fniv = gen_vmaxi_u, + .fnoi = gen_helper_vmaxi_bu, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vmaxi_u, + .fnoi = gen_helper_vmaxi_hu, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vmaxi_u, + .fnoi = gen_helper_vmaxi_wu, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vmaxi_u, + .fnoi = gen_helper_vmaxi_du, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); +} + +TRANS(vmaxi_b, LSX, gvec_vv_i, MO_8, do_vmaxi_s) +TRANS(vmaxi_h, LSX, gvec_vv_i, MO_16, do_vmaxi_s) +TRANS(vmaxi_w, LSX, gvec_vv_i, MO_32, do_vmaxi_s) +TRANS(vmaxi_d, LSX, gvec_vv_i, MO_64, do_vmaxi_s) +TRANS(vmaxi_bu, LSX, gvec_vv_i, MO_8, do_vmaxi_u) +TRANS(vmaxi_hu, LSX, gvec_vv_i, MO_16, do_vmaxi_u) +TRANS(vmaxi_wu, LSX, gvec_vv_i, MO_32, do_vmaxi_u) +TRANS(vmaxi_du, LSX, gvec_vv_i, MO_64, do_vmaxi_u) +TRANS(xvmaxi_b, LASX, gvec_xx_i, MO_8, do_vmaxi_s) +TRANS(xvmaxi_h, LASX, gvec_xx_i, MO_16, do_vmaxi_s) +TRANS(xvmaxi_w, LASX, gvec_xx_i, MO_32, do_vmaxi_s) +TRANS(xvmaxi_d, LASX, gvec_xx_i, MO_64, do_vmaxi_s) +TRANS(xvmaxi_bu, LASX, gvec_xx_i, MO_8, do_vmaxi_u) +TRANS(xvmaxi_hu, LASX, gvec_xx_i, MO_16, do_vmaxi_u) +TRANS(xvmaxi_wu, LASX, gvec_xx_i, MO_32, do_vmaxi_u) +TRANS(xvmaxi_du, LASX, gvec_xx_i, MO_64, do_vmaxi_u) + +TRANS(vmul_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_mul) +TRANS(vmul_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_mul) +TRANS(vmul_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_mul) +TRANS(vmul_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_mul) +TRANS(xvmul_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_mul) +TRANS(xvmul_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_mul) +TRANS(xvmul_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_mul) +TRANS(xvmul_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_mul) + +static void gen_vmuh_w(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 discard = tcg_temp_new_i32(); + tcg_gen_muls2_i32(discard, t, a, b); +} + +static void gen_vmuh_d(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 discard = tcg_temp_new_i64(); + tcg_gen_muls2_i64(discard, t, a, b); +} + +static void do_vmuh_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const GVecGen3 op[4] = { + { + .fno = gen_helper_vmuh_b, + .vece = MO_8 + }, + { + .fno = gen_helper_vmuh_h, + .vece = MO_16 + }, + { + .fni4 = gen_vmuh_w, + .fno = gen_helper_vmuh_w, + .vece = MO_32 + }, + { + .fni8 = gen_vmuh_d, + .fno = gen_helper_vmuh_d, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmuh_b, LSX, gvec_vvv, MO_8, do_vmuh_s) +TRANS(vmuh_h, LSX, gvec_vvv, MO_16, do_vmuh_s) +TRANS(vmuh_w, LSX, gvec_vvv, MO_32, do_vmuh_s) +TRANS(vmuh_d, LSX, gvec_vvv, MO_64, do_vmuh_s) +TRANS(xvmuh_b, LASX, gvec_xxx, MO_8, do_vmuh_s) +TRANS(xvmuh_h, LASX, gvec_xxx, MO_16, do_vmuh_s) +TRANS(xvmuh_w, LASX, gvec_xxx, MO_32, do_vmuh_s) +TRANS(xvmuh_d, LASX, gvec_xxx, MO_64, do_vmuh_s) + +static void gen_vmuh_wu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 discard = tcg_temp_new_i32(); + tcg_gen_mulu2_i32(discard, t, a, b); +} + +static void gen_vmuh_du(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 discard = tcg_temp_new_i64(); + tcg_gen_mulu2_i64(discard, t, a, b); +} + +static void do_vmuh_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const GVecGen3 op[4] = { + { + .fno = gen_helper_vmuh_bu, + .vece = MO_8 + }, + { + .fno = gen_helper_vmuh_hu, + .vece = MO_16 + }, + { + .fni4 = gen_vmuh_wu, + .fno = gen_helper_vmuh_wu, + .vece = MO_32 + }, + { + .fni8 = gen_vmuh_du, + .fno = gen_helper_vmuh_du, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmuh_bu, LSX, gvec_vvv, MO_8, do_vmuh_u) +TRANS(vmuh_hu, LSX, gvec_vvv, MO_16, do_vmuh_u) +TRANS(vmuh_wu, LSX, gvec_vvv, MO_32, do_vmuh_u) +TRANS(vmuh_du, LSX, gvec_vvv, MO_64, do_vmuh_u) +TRANS(xvmuh_bu, LASX, gvec_xxx, MO_8, do_vmuh_u) +TRANS(xvmuh_hu, LASX, gvec_xxx, MO_16, do_vmuh_u) +TRANS(xvmuh_wu, LASX, gvec_xxx, MO_32, do_vmuh_u) +TRANS(xvmuh_du, LASX, gvec_xxx, MO_64, do_vmuh_u) + +static void gen_vmulwev_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2; + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + tcg_gen_shli_vec(vece, t1, a, halfbits); + tcg_gen_sari_vec(vece, t1, t1, halfbits); + tcg_gen_shli_vec(vece, t2, b, halfbits); + tcg_gen_sari_vec(vece, t2, t2, halfbits); + tcg_gen_mul_vec(vece, t, t1, t2); +} + +static void gen_vmulwev_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_ext16s_i32(t1, a); + tcg_gen_ext16s_i32(t2, b); + tcg_gen_mul_i32(t, t1, t2); +} + +static void gen_vmulwev_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_ext32s_i64(t1, a); + tcg_gen_ext32s_i64(t2, b); + tcg_gen_mul_i64(t, t1, t2); +} + +static void do_vmulwev_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_mul_vec, 0 + }; + static const GVecGen3 op[3] = { + { + .fniv = gen_vmulwev_s, + .fno = gen_helper_vmulwev_h_b, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vmulwev_w_h, + .fniv = gen_vmulwev_s, + .fno = gen_helper_vmulwev_w_h, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vmulwev_d_w, + .fniv = gen_vmulwev_s, + .fno = gen_helper_vmulwev_d_w, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmulwev_h_b, LSX, gvec_vvv, MO_8, do_vmulwev_s) +TRANS(vmulwev_w_h, LSX, gvec_vvv, MO_16, do_vmulwev_s) +TRANS(vmulwev_d_w, LSX, gvec_vvv, MO_32, do_vmulwev_s) +TRANS(xvmulwev_h_b, LASX, gvec_xxx, MO_8, do_vmulwev_s) +TRANS(xvmulwev_w_h, LASX, gvec_xxx, MO_16, do_vmulwev_s) +TRANS(xvmulwev_d_w, LASX, gvec_xxx, MO_32, do_vmulwev_s) + +static void tcg_gen_mulus2_i64(TCGv_i64 rl, TCGv_i64 rh, + TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_mulsu2_i64(rl, rh, arg2, arg1); +} + +static bool gen_vmul_q_vl(DisasContext *ctx, + arg_vvv *a, uint32_t oprsz, int idx1, int idx2, + void (*func)(TCGv_i64, TCGv_i64, + TCGv_i64, TCGv_i64)) +{ + TCGv_i64 rh, rl, arg1, arg2; + int i; + + if (!check_vec(ctx, oprsz)) { + return true; + } + + rh = tcg_temp_new_i64(); + rl = tcg_temp_new_i64(); + arg1 = tcg_temp_new_i64(); + arg2 = tcg_temp_new_i64(); + + for (i = 0; i < oprsz / 16; i++) { + get_vreg64(arg1, a->vj, 2 * i + idx1); + get_vreg64(arg2, a->vk, 2 * i + idx2); + + func(rl, rh, arg1, arg2); + + set_vreg64(rh, a->vd, 2 * i + 1); + set_vreg64(rl, a->vd, 2 * i); + } + + return true; +} + +static bool gen_vmul_q(DisasContext *ctx, arg_vvv *a, int idx1, int idx2, + void (*func)(TCGv_i64, TCGv_i64, + TCGv_i64, TCGv_i64)) +{ + return gen_vmul_q_vl(ctx, a, 16, idx1, idx2, func); +} + +static bool gen_xvmul_q(DisasContext *ctx, arg_vvv *a, int idx1, int idx2, + void (*func)(TCGv_i64, TCGv_i64, + TCGv_i64, TCGv_i64)) +{ + return gen_vmul_q_vl(ctx, a, 32, idx1, idx2, func); +} + +TRANS(vmulwev_q_d, LSX, gen_vmul_q, 0, 0, tcg_gen_muls2_i64) +TRANS(vmulwod_q_d, LSX, gen_vmul_q, 1, 1, tcg_gen_muls2_i64) +TRANS(vmulwev_q_du, LSX, gen_vmul_q, 0, 0, tcg_gen_mulu2_i64) +TRANS(vmulwod_q_du, LSX, gen_vmul_q, 1, 1, tcg_gen_mulu2_i64) +TRANS(vmulwev_q_du_d, LSX, gen_vmul_q, 0, 0, tcg_gen_mulus2_i64) +TRANS(vmulwod_q_du_d, LSX, gen_vmul_q, 1, 1, tcg_gen_mulus2_i64) +TRANS(xvmulwev_q_d, LASX, gen_xvmul_q, 0, 0, tcg_gen_muls2_i64) +TRANS(xvmulwod_q_d, LASX, gen_xvmul_q, 1, 1, tcg_gen_muls2_i64) +TRANS(xvmulwev_q_du, LASX, gen_xvmul_q, 0, 0, tcg_gen_mulu2_i64) +TRANS(xvmulwod_q_du, LASX, gen_xvmul_q, 1, 1, tcg_gen_mulu2_i64) +TRANS(xvmulwev_q_du_d, LASX, gen_xvmul_q, 0, 0, tcg_gen_mulus2_i64) +TRANS(xvmulwod_q_du_d, LASX, gen_xvmul_q, 1, 1, tcg_gen_mulus2_i64) + +static void gen_vmulwod_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2; + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + tcg_gen_sari_vec(vece, t1, a, halfbits); + tcg_gen_sari_vec(vece, t2, b, halfbits); + tcg_gen_mul_vec(vece, t, t1, t2); +} + +static void gen_vmulwod_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_sari_i32(t1, a, 16); + tcg_gen_sari_i32(t2, b, 16); + tcg_gen_mul_i32(t, t1, t2); +} + +static void gen_vmulwod_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_sari_i64(t1, a, 32); + tcg_gen_sari_i64(t2, b, 32); + tcg_gen_mul_i64(t, t1, t2); +} + +static void do_vmulwod_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_mul_vec, 0 + }; + static const GVecGen3 op[3] = { + { + .fniv = gen_vmulwod_s, + .fno = gen_helper_vmulwod_h_b, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vmulwod_w_h, + .fniv = gen_vmulwod_s, + .fno = gen_helper_vmulwod_w_h, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vmulwod_d_w, + .fniv = gen_vmulwod_s, + .fno = gen_helper_vmulwod_d_w, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmulwod_h_b, LSX, gvec_vvv, MO_8, do_vmulwod_s) +TRANS(vmulwod_w_h, LSX, gvec_vvv, MO_16, do_vmulwod_s) +TRANS(vmulwod_d_w, LSX, gvec_vvv, MO_32, do_vmulwod_s) +TRANS(xvmulwod_h_b, LASX, gvec_xxx, MO_8, do_vmulwod_s) +TRANS(xvmulwod_w_h, LASX, gvec_xxx, MO_16, do_vmulwod_s) +TRANS(xvmulwod_d_w, LASX, gvec_xxx, MO_32, do_vmulwod_s) + +static void gen_vmulwev_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2, mask; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + mask = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); + tcg_gen_and_vec(vece, t1, a, mask); + tcg_gen_and_vec(vece, t2, b, mask); + tcg_gen_mul_vec(vece, t, t1, t2); +} + +static void gen_vmulwev_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_ext16u_i32(t1, a); + tcg_gen_ext16u_i32(t2, b); + tcg_gen_mul_i32(t, t1, t2); +} + +static void gen_vmulwev_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(t1, a); + tcg_gen_ext32u_i64(t2, b); + tcg_gen_mul_i64(t, t1, t2); +} + +static void do_vmulwev_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_mul_vec, 0 + }; + static const GVecGen3 op[3] = { + { + .fniv = gen_vmulwev_u, + .fno = gen_helper_vmulwev_h_bu, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vmulwev_w_hu, + .fniv = gen_vmulwev_u, + .fno = gen_helper_vmulwev_w_hu, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vmulwev_d_wu, + .fniv = gen_vmulwev_u, + .fno = gen_helper_vmulwev_d_wu, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmulwev_h_bu, LSX, gvec_vvv, MO_8, do_vmulwev_u) +TRANS(vmulwev_w_hu, LSX, gvec_vvv, MO_16, do_vmulwev_u) +TRANS(vmulwev_d_wu, LSX, gvec_vvv, MO_32, do_vmulwev_u) +TRANS(xvmulwev_h_bu, LASX, gvec_xxx, MO_8, do_vmulwev_u) +TRANS(xvmulwev_w_hu, LASX, gvec_xxx, MO_16, do_vmulwev_u) +TRANS(xvmulwev_d_wu, LASX, gvec_xxx, MO_32, do_vmulwev_u) + +static void gen_vmulwod_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2; + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + tcg_gen_shri_vec(vece, t1, a, halfbits); + tcg_gen_shri_vec(vece, t2, b, halfbits); + tcg_gen_mul_vec(vece, t, t1, t2); +} + +static void gen_vmulwod_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_shri_i32(t1, a, 16); + tcg_gen_shri_i32(t2, b, 16); + tcg_gen_mul_i32(t, t1, t2); +} + +static void gen_vmulwod_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_shri_i64(t1, a, 32); + tcg_gen_shri_i64(t2, b, 32); + tcg_gen_mul_i64(t, t1, t2); +} + +static void do_vmulwod_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_mul_vec, 0 + }; + static const GVecGen3 op[3] = { + { + .fniv = gen_vmulwod_u, + .fno = gen_helper_vmulwod_h_bu, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vmulwod_w_hu, + .fniv = gen_vmulwod_u, + .fno = gen_helper_vmulwod_w_hu, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vmulwod_d_wu, + .fniv = gen_vmulwod_u, + .fno = gen_helper_vmulwod_d_wu, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmulwod_h_bu, LSX, gvec_vvv, MO_8, do_vmulwod_u) +TRANS(vmulwod_w_hu, LSX, gvec_vvv, MO_16, do_vmulwod_u) +TRANS(vmulwod_d_wu, LSX, gvec_vvv, MO_32, do_vmulwod_u) +TRANS(xvmulwod_h_bu, LASX, gvec_xxx, MO_8, do_vmulwod_u) +TRANS(xvmulwod_w_hu, LASX, gvec_xxx, MO_16, do_vmulwod_u) +TRANS(xvmulwod_d_wu, LASX, gvec_xxx, MO_32, do_vmulwod_u) + +static void gen_vmulwev_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2, mask; + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + mask = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); + tcg_gen_and_vec(vece, t1, a, mask); + tcg_gen_shli_vec(vece, t2, b, halfbits); + tcg_gen_sari_vec(vece, t2, t2, halfbits); + tcg_gen_mul_vec(vece, t, t1, t2); +} + +static void gen_vmulwev_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_ext16u_i32(t1, a); + tcg_gen_ext16s_i32(t2, b); + tcg_gen_mul_i32(t, t1, t2); +} + +static void gen_vmulwev_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(t1, a); + tcg_gen_ext32s_i64(t2, b); + tcg_gen_mul_i64(t, t1, t2); +} + +static void do_vmulwev_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_mul_vec, 0 + }; + static const GVecGen3 op[3] = { + { + .fniv = gen_vmulwev_u_s, + .fno = gen_helper_vmulwev_h_bu_b, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vmulwev_w_hu_h, + .fniv = gen_vmulwev_u_s, + .fno = gen_helper_vmulwev_w_hu_h, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vmulwev_d_wu_w, + .fniv = gen_vmulwev_u_s, + .fno = gen_helper_vmulwev_d_wu_w, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmulwev_h_bu_b, LSX, gvec_vvv, MO_8, do_vmulwev_u_s) +TRANS(vmulwev_w_hu_h, LSX, gvec_vvv, MO_16, do_vmulwev_u_s) +TRANS(vmulwev_d_wu_w, LSX, gvec_vvv, MO_32, do_vmulwev_u_s) +TRANS(xvmulwev_h_bu_b, LASX, gvec_xxx, MO_8, do_vmulwev_u_s) +TRANS(xvmulwev_w_hu_h, LASX, gvec_xxx, MO_16, do_vmulwev_u_s) +TRANS(xvmulwev_d_wu_w, LASX, gvec_xxx, MO_32, do_vmulwev_u_s) + +static void gen_vmulwod_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2; + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + tcg_gen_shri_vec(vece, t1, a, halfbits); + tcg_gen_sari_vec(vece, t2, b, halfbits); + tcg_gen_mul_vec(vece, t, t1, t2); +} + +static void gen_vmulwod_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1, t2; + + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + tcg_gen_shri_i32(t1, a, 16); + tcg_gen_sari_i32(t2, b, 16); + tcg_gen_mul_i32(t, t1, t2); +} +static void gen_vmulwod_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1, t2; + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_shri_i64(t1, a, 32); + tcg_gen_sari_i64(t2, b, 32); + tcg_gen_mul_i64(t, t1, t2); +} + +static void do_vmulwod_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_mul_vec, 0 + }; + static const GVecGen3 op[3] = { + { + .fniv = gen_vmulwod_u_s, + .fno = gen_helper_vmulwod_h_bu_b, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vmulwod_w_hu_h, + .fniv = gen_vmulwod_u_s, + .fno = gen_helper_vmulwod_w_hu_h, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vmulwod_d_wu_w, + .fniv = gen_vmulwod_u_s, + .fno = gen_helper_vmulwod_d_wu_w, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmulwod_h_bu_b, LSX, gvec_vvv, MO_8, do_vmulwod_u_s) +TRANS(vmulwod_w_hu_h, LSX, gvec_vvv, MO_16, do_vmulwod_u_s) +TRANS(vmulwod_d_wu_w, LSX, gvec_vvv, MO_32, do_vmulwod_u_s) +TRANS(xvmulwod_h_bu_b, LASX, gvec_xxx, MO_8, do_vmulwod_u_s) +TRANS(xvmulwod_w_hu_h, LASX, gvec_xxx, MO_16, do_vmulwod_u_s) +TRANS(xvmulwod_d_wu_w, LASX, gvec_xxx, MO_32, do_vmulwod_u_s) + +static void gen_vmadd(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1; + + t1 = tcg_temp_new_vec_matching(t); + tcg_gen_mul_vec(vece, t1, a, b); + tcg_gen_add_vec(vece, t, t, t1); +} + +static void gen_vmadd_w(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1; + + t1 = tcg_temp_new_i32(); + tcg_gen_mul_i32(t1, a, b); + tcg_gen_add_i32(t, t, t1); +} + +static void gen_vmadd_d(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1; + + t1 = tcg_temp_new_i64(); + tcg_gen_mul_i64(t1, a, b); + tcg_gen_add_i64(t, t, t1); +} + +static void do_vmadd(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_mul_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vmadd, + .fno = gen_helper_vmadd_b, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vmadd, + .fno = gen_helper_vmadd_h, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vmadd_w, + .fniv = gen_vmadd, + .fno = gen_helper_vmadd_w, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vmadd_d, + .fniv = gen_vmadd, + .fno = gen_helper_vmadd_d, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmadd_b, LSX, gvec_vvv, MO_8, do_vmadd) +TRANS(vmadd_h, LSX, gvec_vvv, MO_16, do_vmadd) +TRANS(vmadd_w, LSX, gvec_vvv, MO_32, do_vmadd) +TRANS(vmadd_d, LSX, gvec_vvv, MO_64, do_vmadd) +TRANS(xvmadd_b, LASX, gvec_xxx, MO_8, do_vmadd) +TRANS(xvmadd_h, LASX, gvec_xxx, MO_16, do_vmadd) +TRANS(xvmadd_w, LASX, gvec_xxx, MO_32, do_vmadd) +TRANS(xvmadd_d, LASX, gvec_xxx, MO_64, do_vmadd) + +static void gen_vmsub(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1; + + t1 = tcg_temp_new_vec_matching(t); + tcg_gen_mul_vec(vece, t1, a, b); + tcg_gen_sub_vec(vece, t, t, t1); +} + +static void gen_vmsub_w(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1; + + t1 = tcg_temp_new_i32(); + tcg_gen_mul_i32(t1, a, b); + tcg_gen_sub_i32(t, t, t1); +} + +static void gen_vmsub_d(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1; + + t1 = tcg_temp_new_i64(); + tcg_gen_mul_i64(t1, a, b); + tcg_gen_sub_i64(t, t, t1); +} + +static void do_vmsub(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_mul_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vmsub, + .fno = gen_helper_vmsub_b, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vmsub, + .fno = gen_helper_vmsub_h, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vmsub_w, + .fniv = gen_vmsub, + .fno = gen_helper_vmsub_w, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vmsub_d, + .fniv = gen_vmsub, + .fno = gen_helper_vmsub_d, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmsub_b, LSX, gvec_vvv, MO_8, do_vmsub) +TRANS(vmsub_h, LSX, gvec_vvv, MO_16, do_vmsub) +TRANS(vmsub_w, LSX, gvec_vvv, MO_32, do_vmsub) +TRANS(vmsub_d, LSX, gvec_vvv, MO_64, do_vmsub) +TRANS(xvmsub_b, LASX, gvec_xxx, MO_8, do_vmsub) +TRANS(xvmsub_h, LASX, gvec_xxx, MO_16, do_vmsub) +TRANS(xvmsub_w, LASX, gvec_xxx, MO_32, do_vmsub) +TRANS(xvmsub_d, LASX, gvec_xxx, MO_64, do_vmsub) + +static void gen_vmaddwev_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2, t3; + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + t3 = tcg_temp_new_vec_matching(t); + tcg_gen_shli_vec(vece, t1, a, halfbits); + tcg_gen_sari_vec(vece, t1, t1, halfbits); + tcg_gen_shli_vec(vece, t2, b, halfbits); + tcg_gen_sari_vec(vece, t2, t2, halfbits); + tcg_gen_mul_vec(vece, t3, t1, t2); + tcg_gen_add_vec(vece, t, t, t3); +} + +static void gen_vmaddwev_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1; + + t1 = tcg_temp_new_i32(); + gen_vmulwev_w_h(t1, a, b); + tcg_gen_add_i32(t, t, t1); +} + +static void gen_vmaddwev_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1; + + t1 = tcg_temp_new_i64(); + gen_vmulwev_d_w(t1, a, b); + tcg_gen_add_i64(t, t, t1); +} + +static void do_vmaddwev_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shli_vec, INDEX_op_sari_vec, + INDEX_op_mul_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[3] = { + { + .fniv = gen_vmaddwev_s, + .fno = gen_helper_vmaddwev_h_b, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vmaddwev_w_h, + .fniv = gen_vmaddwev_s, + .fno = gen_helper_vmaddwev_w_h, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vmaddwev_d_w, + .fniv = gen_vmaddwev_s, + .fno = gen_helper_vmaddwev_d_w, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmaddwev_h_b, LSX, gvec_vvv, MO_8, do_vmaddwev_s) +TRANS(vmaddwev_w_h, LSX, gvec_vvv, MO_16, do_vmaddwev_s) +TRANS(vmaddwev_d_w, LSX, gvec_vvv, MO_32, do_vmaddwev_s) +TRANS(xvmaddwev_h_b, LASX, gvec_xxx, MO_8, do_vmaddwev_s) +TRANS(xvmaddwev_w_h, LASX, gvec_xxx, MO_16, do_vmaddwev_s) +TRANS(xvmaddwev_d_w, LASX, gvec_xxx, MO_32, do_vmaddwev_s) + +static bool gen_vmadd_q_vl(DisasContext * ctx, + arg_vvv *a, uint32_t oprsz, int idx1, int idx2, + void (*func)(TCGv_i64, TCGv_i64, + TCGv_i64, TCGv_i64)) +{ + TCGv_i64 rh, rl, arg1, arg2, th, tl; + int i; + + if (!check_vec(ctx, oprsz)) { + return true; + } + + rh = tcg_temp_new_i64(); + rl = tcg_temp_new_i64(); + arg1 = tcg_temp_new_i64(); + arg2 = tcg_temp_new_i64(); + th = tcg_temp_new_i64(); + tl = tcg_temp_new_i64(); + + for (i = 0; i < oprsz / 16; i++) { + get_vreg64(arg1, a->vj, 2 * i + idx1); + get_vreg64(arg2, a->vk, 2 * i + idx2); + get_vreg64(rh, a->vd, 2 * i + 1); + get_vreg64(rl, a->vd, 2 * i); + + func(tl, th, arg1, arg2); + tcg_gen_add2_i64(rl, rh, rl, rh, tl, th); + + set_vreg64(rh, a->vd, 2 * i + 1); + set_vreg64(rl, a->vd, 2 * i); + } + + return true; +} + +static bool gen_vmadd_q(DisasContext *ctx, arg_vvv *a, int idx1, int idx2, + void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) +{ + return gen_vmadd_q_vl(ctx, a, 16, idx1, idx2, func); +} + +static bool gen_xvmadd_q(DisasContext *ctx, arg_vvv *a, int idx1, int idx2, + void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) +{ + return gen_vmadd_q_vl(ctx, a, 32, idx1, idx2, func); +} + +TRANS(vmaddwev_q_d, LSX, gen_vmadd_q, 0, 0, tcg_gen_muls2_i64) +TRANS(vmaddwod_q_d, LSX, gen_vmadd_q, 1, 1, tcg_gen_muls2_i64) +TRANS(vmaddwev_q_du, LSX, gen_vmadd_q, 0, 0, tcg_gen_mulu2_i64) +TRANS(vmaddwod_q_du, LSX, gen_vmadd_q, 1, 1, tcg_gen_mulu2_i64) +TRANS(vmaddwev_q_du_d, LSX, gen_vmadd_q, 0, 0, tcg_gen_mulus2_i64) +TRANS(vmaddwod_q_du_d, LSX, gen_vmadd_q, 1, 1, tcg_gen_mulus2_i64) +TRANS(xvmaddwev_q_d, LASX, gen_xvmadd_q, 0, 0, tcg_gen_muls2_i64) +TRANS(xvmaddwod_q_d, LASX, gen_xvmadd_q, 1, 1, tcg_gen_muls2_i64) +TRANS(xvmaddwev_q_du, LASX, gen_xvmadd_q, 0, 0, tcg_gen_mulu2_i64) +TRANS(xvmaddwod_q_du, LASX, gen_xvmadd_q, 1, 1, tcg_gen_mulu2_i64) +TRANS(xvmaddwev_q_du_d, LASX, gen_xvmadd_q, 0, 0, tcg_gen_mulus2_i64) +TRANS(xvmaddwod_q_du_d, LASX, gen_xvmadd_q, 1, 1, tcg_gen_mulus2_i64) + +static void gen_vmaddwod_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2, t3; + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + t3 = tcg_temp_new_vec_matching(t); + tcg_gen_sari_vec(vece, t1, a, halfbits); + tcg_gen_sari_vec(vece, t2, b, halfbits); + tcg_gen_mul_vec(vece, t3, t1, t2); + tcg_gen_add_vec(vece, t, t, t3); +} + +static void gen_vmaddwod_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1; + + t1 = tcg_temp_new_i32(); + gen_vmulwod_w_h(t1, a, b); + tcg_gen_add_i32(t, t, t1); +} + +static void gen_vmaddwod_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1; + + t1 = tcg_temp_new_i64(); + gen_vmulwod_d_w(t1, a, b); + tcg_gen_add_i64(t, t, t1); +} + +static void do_vmaddwod_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_mul_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[3] = { + { + .fniv = gen_vmaddwod_s, + .fno = gen_helper_vmaddwod_h_b, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vmaddwod_w_h, + .fniv = gen_vmaddwod_s, + .fno = gen_helper_vmaddwod_w_h, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vmaddwod_d_w, + .fniv = gen_vmaddwod_s, + .fno = gen_helper_vmaddwod_d_w, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmaddwod_h_b, LSX, gvec_vvv, MO_8, do_vmaddwod_s) +TRANS(vmaddwod_w_h, LSX, gvec_vvv, MO_16, do_vmaddwod_s) +TRANS(vmaddwod_d_w, LSX, gvec_vvv, MO_32, do_vmaddwod_s) +TRANS(xvmaddwod_h_b, LASX, gvec_xxx, MO_8, do_vmaddwod_s) +TRANS(xvmaddwod_w_h, LASX, gvec_xxx, MO_16, do_vmaddwod_s) +TRANS(xvmaddwod_d_w, LASX, gvec_xxx, MO_32, do_vmaddwod_s) + +static void gen_vmaddwev_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2, mask; + + t1 = tcg_temp_new_vec_matching(t); + t2 = tcg_temp_new_vec_matching(b); + mask = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); + tcg_gen_and_vec(vece, t1, a, mask); + tcg_gen_and_vec(vece, t2, b, mask); + tcg_gen_mul_vec(vece, t1, t1, t2); + tcg_gen_add_vec(vece, t, t, t1); +} + +static void gen_vmaddwev_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1; + + t1 = tcg_temp_new_i32(); + gen_vmulwev_w_hu(t1, a, b); + tcg_gen_add_i32(t, t, t1); +} + +static void gen_vmaddwev_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1; + + t1 = tcg_temp_new_i64(); + gen_vmulwev_d_wu(t1, a, b); + tcg_gen_add_i64(t, t, t1); +} + +static void do_vmaddwev_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_mul_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[3] = { + { + .fniv = gen_vmaddwev_u, + .fno = gen_helper_vmaddwev_h_bu, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vmaddwev_w_hu, + .fniv = gen_vmaddwev_u, + .fno = gen_helper_vmaddwev_w_hu, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vmaddwev_d_wu, + .fniv = gen_vmaddwev_u, + .fno = gen_helper_vmaddwev_d_wu, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmaddwev_h_bu, LSX, gvec_vvv, MO_8, do_vmaddwev_u) +TRANS(vmaddwev_w_hu, LSX, gvec_vvv, MO_16, do_vmaddwev_u) +TRANS(vmaddwev_d_wu, LSX, gvec_vvv, MO_32, do_vmaddwev_u) +TRANS(xvmaddwev_h_bu, LASX, gvec_xxx, MO_8, do_vmaddwev_u) +TRANS(xvmaddwev_w_hu, LASX, gvec_xxx, MO_16, do_vmaddwev_u) +TRANS(xvmaddwev_d_wu, LASX, gvec_xxx, MO_32, do_vmaddwev_u) + +static void gen_vmaddwod_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2, t3; + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + t3 = tcg_temp_new_vec_matching(t); + tcg_gen_shri_vec(vece, t1, a, halfbits); + tcg_gen_shri_vec(vece, t2, b, halfbits); + tcg_gen_mul_vec(vece, t3, t1, t2); + tcg_gen_add_vec(vece, t, t, t3); +} + +static void gen_vmaddwod_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1; + + t1 = tcg_temp_new_i32(); + gen_vmulwod_w_hu(t1, a, b); + tcg_gen_add_i32(t, t, t1); +} + +static void gen_vmaddwod_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1; + + t1 = tcg_temp_new_i64(); + gen_vmulwod_d_wu(t1, a, b); + tcg_gen_add_i64(t, t, t1); +} + +static void do_vmaddwod_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_mul_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[3] = { + { + .fniv = gen_vmaddwod_u, + .fno = gen_helper_vmaddwod_h_bu, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vmaddwod_w_hu, + .fniv = gen_vmaddwod_u, + .fno = gen_helper_vmaddwod_w_hu, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vmaddwod_d_wu, + .fniv = gen_vmaddwod_u, + .fno = gen_helper_vmaddwod_d_wu, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmaddwod_h_bu, LSX, gvec_vvv, MO_8, do_vmaddwod_u) +TRANS(vmaddwod_w_hu, LSX, gvec_vvv, MO_16, do_vmaddwod_u) +TRANS(vmaddwod_d_wu, LSX, gvec_vvv, MO_32, do_vmaddwod_u) +TRANS(xvmaddwod_h_bu, LASX, gvec_xxx, MO_8, do_vmaddwod_u) +TRANS(xvmaddwod_w_hu, LASX, gvec_xxx, MO_16, do_vmaddwod_u) +TRANS(xvmaddwod_d_wu, LASX, gvec_xxx, MO_32, do_vmaddwod_u) + +static void gen_vmaddwev_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2, mask; + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + mask = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); + tcg_gen_and_vec(vece, t1, a, mask); + tcg_gen_shli_vec(vece, t2, b, halfbits); + tcg_gen_sari_vec(vece, t2, t2, halfbits); + tcg_gen_mul_vec(vece, t1, t1, t2); + tcg_gen_add_vec(vece, t, t, t1); +} + +static void gen_vmaddwev_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1; + + t1 = tcg_temp_new_i32(); + gen_vmulwev_w_hu_h(t1, a, b); + tcg_gen_add_i32(t, t, t1); +} + +static void gen_vmaddwev_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1; + + t1 = tcg_temp_new_i64(); + gen_vmulwev_d_wu_w(t1, a, b); + tcg_gen_add_i64(t, t, t1); +} + +static void do_vmaddwev_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shli_vec, INDEX_op_sari_vec, + INDEX_op_mul_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[3] = { + { + .fniv = gen_vmaddwev_u_s, + .fno = gen_helper_vmaddwev_h_bu_b, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vmaddwev_w_hu_h, + .fniv = gen_vmaddwev_u_s, + .fno = gen_helper_vmaddwev_w_hu_h, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vmaddwev_d_wu_w, + .fniv = gen_vmaddwev_u_s, + .fno = gen_helper_vmaddwev_d_wu_w, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmaddwev_h_bu_b, LSX, gvec_vvv, MO_8, do_vmaddwev_u_s) +TRANS(vmaddwev_w_hu_h, LSX, gvec_vvv, MO_16, do_vmaddwev_u_s) +TRANS(vmaddwev_d_wu_w, LSX, gvec_vvv, MO_32, do_vmaddwev_u_s) +TRANS(xvmaddwev_h_bu_b, LASX, gvec_xxx, MO_8, do_vmaddwev_u_s) +TRANS(xvmaddwev_w_hu_h, LASX, gvec_xxx, MO_16, do_vmaddwev_u_s) +TRANS(xvmaddwev_d_wu_w, LASX, gvec_xxx, MO_32, do_vmaddwev_u_s) + +static void gen_vmaddwod_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, t2, t3; + int halfbits = 4 << vece; + + t1 = tcg_temp_new_vec_matching(a); + t2 = tcg_temp_new_vec_matching(b); + t3 = tcg_temp_new_vec_matching(t); + tcg_gen_shri_vec(vece, t1, a, halfbits); + tcg_gen_sari_vec(vece, t2, b, halfbits); + tcg_gen_mul_vec(vece, t3, t1, t2); + tcg_gen_add_vec(vece, t, t, t3); +} + +static void gen_vmaddwod_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t1; + + t1 = tcg_temp_new_i32(); + gen_vmulwod_w_hu_h(t1, a, b); + tcg_gen_add_i32(t, t, t1); +} + +static void gen_vmaddwod_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t1; + + t1 = tcg_temp_new_i64(); + gen_vmulwod_d_wu_w(t1, a, b); + tcg_gen_add_i64(t, t, t1); +} + +static void do_vmaddwod_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_sari_vec, + INDEX_op_mul_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 op[3] = { + { + .fniv = gen_vmaddwod_u_s, + .fno = gen_helper_vmaddwod_h_bu_b, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fni4 = gen_vmaddwod_w_hu_h, + .fniv = gen_vmaddwod_u_s, + .fno = gen_helper_vmaddwod_w_hu_h, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = gen_vmaddwod_d_wu_w, + .fniv = gen_vmaddwod_u_s, + .fno = gen_helper_vmaddwod_d_wu_w, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vmaddwod_h_bu_b, LSX, gvec_vvv, MO_8, do_vmaddwod_u_s) +TRANS(vmaddwod_w_hu_h, LSX, gvec_vvv, MO_16, do_vmaddwod_u_s) +TRANS(vmaddwod_d_wu_w, LSX, gvec_vvv, MO_32, do_vmaddwod_u_s) +TRANS(xvmaddwod_h_bu_b, LASX, gvec_xxx, MO_8, do_vmaddwod_u_s) +TRANS(xvmaddwod_w_hu_h, LASX, gvec_xxx, MO_16, do_vmaddwod_u_s) +TRANS(xvmaddwod_d_wu_w, LASX, gvec_xxx, MO_32, do_vmaddwod_u_s) + +TRANS(vdiv_b, LSX, gen_vvv, gen_helper_vdiv_b) +TRANS(vdiv_h, LSX, gen_vvv, gen_helper_vdiv_h) +TRANS(vdiv_w, LSX, gen_vvv, gen_helper_vdiv_w) +TRANS(vdiv_d, LSX, gen_vvv, gen_helper_vdiv_d) +TRANS(vdiv_bu, LSX, gen_vvv, gen_helper_vdiv_bu) +TRANS(vdiv_hu, LSX, gen_vvv, gen_helper_vdiv_hu) +TRANS(vdiv_wu, LSX, gen_vvv, gen_helper_vdiv_wu) +TRANS(vdiv_du, LSX, gen_vvv, gen_helper_vdiv_du) +TRANS(vmod_b, LSX, gen_vvv, gen_helper_vmod_b) +TRANS(vmod_h, LSX, gen_vvv, gen_helper_vmod_h) +TRANS(vmod_w, LSX, gen_vvv, gen_helper_vmod_w) +TRANS(vmod_d, LSX, gen_vvv, gen_helper_vmod_d) +TRANS(vmod_bu, LSX, gen_vvv, gen_helper_vmod_bu) +TRANS(vmod_hu, LSX, gen_vvv, gen_helper_vmod_hu) +TRANS(vmod_wu, LSX, gen_vvv, gen_helper_vmod_wu) +TRANS(vmod_du, LSX, gen_vvv, gen_helper_vmod_du) +TRANS(xvdiv_b, LASX, gen_xxx, gen_helper_vdiv_b) +TRANS(xvdiv_h, LASX, gen_xxx, gen_helper_vdiv_h) +TRANS(xvdiv_w, LASX, gen_xxx, gen_helper_vdiv_w) +TRANS(xvdiv_d, LASX, gen_xxx, gen_helper_vdiv_d) +TRANS(xvdiv_bu, LASX, gen_xxx, gen_helper_vdiv_bu) +TRANS(xvdiv_hu, LASX, gen_xxx, gen_helper_vdiv_hu) +TRANS(xvdiv_wu, LASX, gen_xxx, gen_helper_vdiv_wu) +TRANS(xvdiv_du, LASX, gen_xxx, gen_helper_vdiv_du) +TRANS(xvmod_b, LASX, gen_xxx, gen_helper_vmod_b) +TRANS(xvmod_h, LASX, gen_xxx, gen_helper_vmod_h) +TRANS(xvmod_w, LASX, gen_xxx, gen_helper_vmod_w) +TRANS(xvmod_d, LASX, gen_xxx, gen_helper_vmod_d) +TRANS(xvmod_bu, LASX, gen_xxx, gen_helper_vmod_bu) +TRANS(xvmod_hu, LASX, gen_xxx, gen_helper_vmod_hu) +TRANS(xvmod_wu, LASX, gen_xxx, gen_helper_vmod_wu) +TRANS(xvmod_du, LASX, gen_xxx, gen_helper_vmod_du) + +static void gen_vsat_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec max) +{ + TCGv_vec min; + + min = tcg_temp_new_vec_matching(t); + tcg_gen_not_vec(vece, min, max); + tcg_gen_smax_vec(vece, t, a, min); + tcg_gen_smin_vec(vece, t, t, max); +} + +static void do_vsat_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + int64_t imm, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_smax_vec, INDEX_op_smin_vec, 0 + }; + static const GVecGen2s op[4] = { + { + .fniv = gen_vsat_s, + .fno = gen_helper_vsat_b, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vsat_s, + .fno = gen_helper_vsat_h, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vsat_s, + .fno = gen_helper_vsat_w, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vsat_s, + .fno = gen_helper_vsat_d, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_2s(vd_ofs, vj_ofs, oprsz, maxsz, + tcg_constant_i64((1ll<< imm) -1), &op[vece]); +} + +TRANS(vsat_b, LSX, gvec_vv_i, MO_8, do_vsat_s) +TRANS(vsat_h, LSX, gvec_vv_i, MO_16, do_vsat_s) +TRANS(vsat_w, LSX, gvec_vv_i, MO_32, do_vsat_s) +TRANS(vsat_d, LSX, gvec_vv_i, MO_64, do_vsat_s) +TRANS(xvsat_b, LASX, gvec_xx_i, MO_8, do_vsat_s) +TRANS(xvsat_h, LASX, gvec_xx_i, MO_16, do_vsat_s) +TRANS(xvsat_w, LASX, gvec_xx_i, MO_32, do_vsat_s) +TRANS(xvsat_d, LASX, gvec_xx_i, MO_64, do_vsat_s) + +static void gen_vsat_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec max) +{ + tcg_gen_umin_vec(vece, t, a, max); +} + +static void do_vsat_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + int64_t imm, uint32_t oprsz, uint32_t maxsz) +{ + uint64_t max; + static const TCGOpcode vecop_list[] = { + INDEX_op_umin_vec, 0 + }; + static const GVecGen2s op[4] = { + { + .fniv = gen_vsat_u, + .fno = gen_helper_vsat_bu, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vsat_u, + .fno = gen_helper_vsat_hu, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vsat_u, + .fno = gen_helper_vsat_wu, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vsat_u, + .fno = gen_helper_vsat_du, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + max = (imm == 0x3f) ? UINT64_MAX : (1ull << (imm + 1)) - 1; + tcg_gen_gvec_2s(vd_ofs, vj_ofs, oprsz, maxsz, + tcg_constant_i64(max), &op[vece]); +} + +TRANS(vsat_bu, LSX, gvec_vv_i, MO_8, do_vsat_u) +TRANS(vsat_hu, LSX, gvec_vv_i, MO_16, do_vsat_u) +TRANS(vsat_wu, LSX, gvec_vv_i, MO_32, do_vsat_u) +TRANS(vsat_du, LSX, gvec_vv_i, MO_64, do_vsat_u) +TRANS(xvsat_bu, LASX, gvec_xx_i, MO_8, do_vsat_u) +TRANS(xvsat_hu, LASX, gvec_xx_i, MO_16, do_vsat_u) +TRANS(xvsat_wu, LASX, gvec_xx_i, MO_32, do_vsat_u) +TRANS(xvsat_du, LASX, gvec_xx_i, MO_64, do_vsat_u) + +TRANS(vexth_h_b, LSX, gen_vv, gen_helper_vexth_h_b) +TRANS(vexth_w_h, LSX, gen_vv, gen_helper_vexth_w_h) +TRANS(vexth_d_w, LSX, gen_vv, gen_helper_vexth_d_w) +TRANS(vexth_q_d, LSX, gen_vv, gen_helper_vexth_q_d) +TRANS(vexth_hu_bu, LSX, gen_vv, gen_helper_vexth_hu_bu) +TRANS(vexth_wu_hu, LSX, gen_vv, gen_helper_vexth_wu_hu) +TRANS(vexth_du_wu, LSX, gen_vv, gen_helper_vexth_du_wu) +TRANS(vexth_qu_du, LSX, gen_vv, gen_helper_vexth_qu_du) +TRANS(xvexth_h_b, LASX, gen_xx, gen_helper_vexth_h_b) +TRANS(xvexth_w_h, LASX, gen_xx, gen_helper_vexth_w_h) +TRANS(xvexth_d_w, LASX, gen_xx, gen_helper_vexth_d_w) +TRANS(xvexth_q_d, LASX, gen_xx, gen_helper_vexth_q_d) +TRANS(xvexth_hu_bu, LASX, gen_xx, gen_helper_vexth_hu_bu) +TRANS(xvexth_wu_hu, LASX, gen_xx, gen_helper_vexth_wu_hu) +TRANS(xvexth_du_wu, LASX, gen_xx, gen_helper_vexth_du_wu) +TRANS(xvexth_qu_du, LASX, gen_xx, gen_helper_vexth_qu_du) + +TRANS(vext2xv_h_b, LASX, gen_xx, gen_helper_vext2xv_h_b) +TRANS(vext2xv_w_b, LASX, gen_xx, gen_helper_vext2xv_w_b) +TRANS(vext2xv_d_b, LASX, gen_xx, gen_helper_vext2xv_d_b) +TRANS(vext2xv_w_h, LASX, gen_xx, gen_helper_vext2xv_w_h) +TRANS(vext2xv_d_h, LASX, gen_xx, gen_helper_vext2xv_d_h) +TRANS(vext2xv_d_w, LASX, gen_xx, gen_helper_vext2xv_d_w) +TRANS(vext2xv_hu_bu, LASX, gen_xx, gen_helper_vext2xv_hu_bu) +TRANS(vext2xv_wu_bu, LASX, gen_xx, gen_helper_vext2xv_wu_bu) +TRANS(vext2xv_du_bu, LASX, gen_xx, gen_helper_vext2xv_du_bu) +TRANS(vext2xv_wu_hu, LASX, gen_xx, gen_helper_vext2xv_wu_hu) +TRANS(vext2xv_du_hu, LASX, gen_xx, gen_helper_vext2xv_du_hu) +TRANS(vext2xv_du_wu, LASX, gen_xx, gen_helper_vext2xv_du_wu) + +static void gen_vsigncov(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t1, zero; + + t1 = tcg_temp_new_vec_matching(t); + zero = tcg_constant_vec_matching(t, vece, 0); + + tcg_gen_neg_vec(vece, t1, b); + tcg_gen_cmpsel_vec(TCG_COND_LT, vece, t, a, zero, t1, b); + tcg_gen_cmpsel_vec(TCG_COND_EQ, vece, t, a, zero, zero, t); +} + +static void do_vsigncov(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_neg_vec, INDEX_op_cmpsel_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vsigncov, + .fno = gen_helper_vsigncov_b, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vsigncov, + .fno = gen_helper_vsigncov_h, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vsigncov, + .fno = gen_helper_vsigncov_w, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vsigncov, + .fno = gen_helper_vsigncov_d, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vsigncov_b, LSX, gvec_vvv, MO_8, do_vsigncov) +TRANS(vsigncov_h, LSX, gvec_vvv, MO_16, do_vsigncov) +TRANS(vsigncov_w, LSX, gvec_vvv, MO_32, do_vsigncov) +TRANS(vsigncov_d, LSX, gvec_vvv, MO_64, do_vsigncov) +TRANS(xvsigncov_b, LASX, gvec_xxx, MO_8, do_vsigncov) +TRANS(xvsigncov_h, LASX, gvec_xxx, MO_16, do_vsigncov) +TRANS(xvsigncov_w, LASX, gvec_xxx, MO_32, do_vsigncov) +TRANS(xvsigncov_d, LASX, gvec_xxx, MO_64, do_vsigncov) + +TRANS(vmskltz_b, LSX, gen_vv, gen_helper_vmskltz_b) +TRANS(vmskltz_h, LSX, gen_vv, gen_helper_vmskltz_h) +TRANS(vmskltz_w, LSX, gen_vv, gen_helper_vmskltz_w) +TRANS(vmskltz_d, LSX, gen_vv, gen_helper_vmskltz_d) +TRANS(vmskgez_b, LSX, gen_vv, gen_helper_vmskgez_b) +TRANS(vmsknz_b, LSX, gen_vv, gen_helper_vmsknz_b) +TRANS(xvmskltz_b, LASX, gen_xx, gen_helper_vmskltz_b) +TRANS(xvmskltz_h, LASX, gen_xx, gen_helper_vmskltz_h) +TRANS(xvmskltz_w, LASX, gen_xx, gen_helper_vmskltz_w) +TRANS(xvmskltz_d, LASX, gen_xx, gen_helper_vmskltz_d) +TRANS(xvmskgez_b, LASX, gen_xx, gen_helper_vmskgez_b) +TRANS(xvmsknz_b, LASX, gen_xx, gen_helper_vmsknz_b) + +#define EXPAND_BYTE(bit) ((uint64_t)(bit ? 0xff : 0)) + +static uint64_t vldi_get_value(DisasContext *ctx, uint32_t imm) +{ + int mode; + uint64_t data, t; + + /* + * imm bit [11:8] is mode, mode value is 0-12. + * other values are invalid. + */ + mode = (imm >> 8) & 0xf; + t = imm & 0xff; + switch (mode) { + case 0: + /* data: {2{24'0, imm[7:0]}} */ + data = (t << 32) | t ; + break; + case 1: + /* data: {2{16'0, imm[7:0], 8'0}} */ + data = (t << 24) | (t << 8); + break; + case 2: + /* data: {2{8'0, imm[7:0], 16'0}} */ + data = (t << 48) | (t << 16); + break; + case 3: + /* data: {2{imm[7:0], 24'0}} */ + data = (t << 56) | (t << 24); + break; + case 4: + /* data: {4{8'0, imm[7:0]}} */ + data = (t << 48) | (t << 32) | (t << 16) | t; + break; + case 5: + /* data: {4{imm[7:0], 8'0}} */ + data = (t << 56) |(t << 40) | (t << 24) | (t << 8); + break; + case 6: + /* data: {2{16'0, imm[7:0], 8'1}} */ + data = (t << 40) | ((uint64_t)0xff << 32) | (t << 8) | 0xff; + break; + case 7: + /* data: {2{8'0, imm[7:0], 16'1}} */ + data = (t << 48) | ((uint64_t)0xffff << 32) | (t << 16) | 0xffff; + break; + case 8: + /* data: {8{imm[7:0]}} */ + data =(t << 56) | (t << 48) | (t << 40) | (t << 32) | + (t << 24) | (t << 16) | (t << 8) | t; + break; + case 9: + /* data: {{8{imm[7]}, ..., 8{imm[0]}}} */ + { + uint64_t b0,b1,b2,b3,b4,b5,b6,b7; + b0 = t& 0x1; + b1 = (t & 0x2) >> 1; + b2 = (t & 0x4) >> 2; + b3 = (t & 0x8) >> 3; + b4 = (t & 0x10) >> 4; + b5 = (t & 0x20) >> 5; + b6 = (t & 0x40) >> 6; + b7 = (t & 0x80) >> 7; + data = (EXPAND_BYTE(b7) << 56) | + (EXPAND_BYTE(b6) << 48) | + (EXPAND_BYTE(b5) << 40) | + (EXPAND_BYTE(b4) << 32) | + (EXPAND_BYTE(b3) << 24) | + (EXPAND_BYTE(b2) << 16) | + (EXPAND_BYTE(b1) << 8) | + EXPAND_BYTE(b0); + } + break; + case 10: + /* data: {2{imm[7], ~imm[6], {5{imm[6]}}, imm[5:0], 19'0}} */ + { + uint64_t b6, b7; + uint64_t t0, t1; + b6 = (imm & 0x40) >> 6; + b7 = (imm & 0x80) >> 7; + t0 = (imm & 0x3f); + t1 = (b7 << 6) | ((1-b6) << 5) | (uint64_t)(b6 ? 0x1f : 0); + data = (t1 << 57) | (t0 << 51) | (t1 << 25) | (t0 << 19); + } + break; + case 11: + /* data: {32'0, imm[7], ~{imm[6]}, 5{imm[6]}, imm[5:0], 19'0} */ + { + uint64_t b6,b7; + uint64_t t0, t1; + b6 = (imm & 0x40) >> 6; + b7 = (imm & 0x80) >> 7; + t0 = (imm & 0x3f); + t1 = (b7 << 6) | ((1-b6) << 5) | (b6 ? 0x1f : 0); + data = (t1 << 25) | (t0 << 19); + } + break; + case 12: + /* data: {imm[7], ~imm[6], 8{imm[6]}, imm[5:0], 48'0} */ + { + uint64_t b6,b7; + uint64_t t0, t1; + b6 = (imm & 0x40) >> 6; + b7 = (imm & 0x80) >> 7; + t0 = (imm & 0x3f); + t1 = (b7 << 9) | ((1-b6) << 8) | (b6 ? 0xff : 0); + data = (t1 << 54) | (t0 << 48); + } + break; + default: + generate_exception(ctx, EXCCODE_INE); + g_assert_not_reached(); + } + return data; +} + +static bool gen_vldi(DisasContext *ctx, arg_vldi *a, uint32_t oprsz) +{ + int sel, vece; + uint64_t value; + + if (!check_vec(ctx, oprsz)) { + return true; + } + + sel = (a->imm >> 12) & 0x1; + + if (sel) { + value = vldi_get_value(ctx, a->imm); + vece = MO_64; + } else { + value = ((int32_t)(a->imm << 22)) >> 22; + vece = (a->imm >> 10) & 0x3; + } + + tcg_gen_gvec_dup_i64(vece, vec_full_offset(a->vd), oprsz, ctx->vl/8, + tcg_constant_i64(value)); + return true; +} + +TRANS(vldi, LSX, gen_vldi, 16) +TRANS(xvldi, LASX, gen_vldi, 32) + +static bool gen_vandn_v(DisasContext *ctx, arg_vvv *a, uint32_t oprsz) +{ + uint32_t vd_ofs, vj_ofs, vk_ofs; + + if (!check_vec(ctx, oprsz)) { + return true; + } + + vd_ofs = vec_full_offset(a->vd); + vj_ofs = vec_full_offset(a->vj); + vk_ofs = vec_full_offset(a->vk); + + tcg_gen_gvec_andc(MO_64, vd_ofs, vk_ofs, vj_ofs, oprsz, ctx->vl / 8); + return true; +} + +static void gen_vnori(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +{ + TCGv_vec t1; + + t1 = tcg_constant_vec_matching(t, vece, imm); + tcg_gen_nor_vec(vece, t, a, t1); +} + +static void gen_vnori_b(TCGv_i64 t, TCGv_i64 a, int64_t imm) +{ + tcg_gen_movi_i64(t, dup_const(MO_8, imm)); + tcg_gen_nor_i64(t, a, t); +} + +static void do_vnori_b(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + int64_t imm, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_nor_vec, 0 + }; + static const GVecGen2i op = { + .fni8 = gen_vnori_b, + .fniv = gen_vnori, + .fnoi = gen_helper_vnori_b, + .opt_opc = vecop_list, + .vece = MO_8 + }; + + tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op); +} + +TRANS(vand_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_and) +TRANS(vor_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_or) +TRANS(vxor_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_xor) +TRANS(vnor_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_nor) +TRANS(vandn_v, LSX, gen_vandn_v, 16) +TRANS(vorn_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_orc) +TRANS(vandi_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_andi) +TRANS(vori_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_ori) +TRANS(vxori_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_xori) +TRANS(vnori_b, LSX, gvec_vv_i, MO_8, do_vnori_b) +TRANS(xvand_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_and) +TRANS(xvor_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_or) +TRANS(xvxor_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_xor) +TRANS(xvnor_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_nor) +TRANS(xvandn_v, LASX, gen_vandn_v, 32) +TRANS(xvorn_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_orc) +TRANS(xvandi_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_andi) +TRANS(xvori_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_ori) +TRANS(xvxori_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_xori) +TRANS(xvnori_b, LASX, gvec_xx_i, MO_8, do_vnori_b) + +TRANS(vsll_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_shlv) +TRANS(vsll_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_shlv) +TRANS(vsll_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_shlv) +TRANS(vsll_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_shlv) +TRANS(vslli_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_shli) +TRANS(vslli_h, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_shli) +TRANS(vslli_w, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_shli) +TRANS(vslli_d, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_shli) +TRANS(xvsll_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_shlv) +TRANS(xvsll_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_shlv) +TRANS(xvsll_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_shlv) +TRANS(xvsll_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_shlv) +TRANS(xvslli_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_shli) +TRANS(xvslli_h, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_shli) +TRANS(xvslli_w, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_shli) +TRANS(xvslli_d, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_shli) + +TRANS(vsrl_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_shrv) +TRANS(vsrl_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_shrv) +TRANS(vsrl_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_shrv) +TRANS(vsrl_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_shrv) +TRANS(vsrli_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_shri) +TRANS(vsrli_h, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_shri) +TRANS(vsrli_w, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_shri) +TRANS(vsrli_d, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_shri) +TRANS(xvsrl_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_shrv) +TRANS(xvsrl_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_shrv) +TRANS(xvsrl_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_shrv) +TRANS(xvsrl_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_shrv) +TRANS(xvsrli_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_shri) +TRANS(xvsrli_h, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_shri) +TRANS(xvsrli_w, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_shri) +TRANS(xvsrli_d, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_shri) + +TRANS(vsra_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_sarv) +TRANS(vsra_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_sarv) +TRANS(vsra_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_sarv) +TRANS(vsra_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_sarv) +TRANS(vsrai_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_sari) +TRANS(vsrai_h, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_sari) +TRANS(vsrai_w, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_sari) +TRANS(vsrai_d, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_sari) +TRANS(xvsra_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_sarv) +TRANS(xvsra_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_sarv) +TRANS(xvsra_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_sarv) +TRANS(xvsra_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_sarv) +TRANS(xvsrai_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_sari) +TRANS(xvsrai_h, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_sari) +TRANS(xvsrai_w, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_sari) +TRANS(xvsrai_d, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_sari) + +TRANS(vrotr_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_rotrv) +TRANS(vrotr_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_rotrv) +TRANS(vrotr_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_rotrv) +TRANS(vrotr_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_rotrv) +TRANS(vrotri_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_rotri) +TRANS(vrotri_h, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_rotri) +TRANS(vrotri_w, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_rotri) +TRANS(vrotri_d, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_rotri) +TRANS(xvrotr_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_rotrv) +TRANS(xvrotr_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_rotrv) +TRANS(xvrotr_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_rotrv) +TRANS(xvrotr_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_rotrv) +TRANS(xvrotri_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_rotri) +TRANS(xvrotri_h, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_rotri) +TRANS(xvrotri_w, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_rotri) +TRANS(xvrotri_d, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_rotri) + +TRANS(vsllwil_h_b, LSX, gen_vv_i, gen_helper_vsllwil_h_b) +TRANS(vsllwil_w_h, LSX, gen_vv_i, gen_helper_vsllwil_w_h) +TRANS(vsllwil_d_w, LSX, gen_vv_i, gen_helper_vsllwil_d_w) +TRANS(vextl_q_d, LSX, gen_vv, gen_helper_vextl_q_d) +TRANS(vsllwil_hu_bu, LSX, gen_vv_i, gen_helper_vsllwil_hu_bu) +TRANS(vsllwil_wu_hu, LSX, gen_vv_i, gen_helper_vsllwil_wu_hu) +TRANS(vsllwil_du_wu, LSX, gen_vv_i, gen_helper_vsllwil_du_wu) +TRANS(vextl_qu_du, LSX, gen_vv, gen_helper_vextl_qu_du) +TRANS(xvsllwil_h_b, LASX, gen_xx_i, gen_helper_vsllwil_h_b) +TRANS(xvsllwil_w_h, LASX, gen_xx_i, gen_helper_vsllwil_w_h) +TRANS(xvsllwil_d_w, LASX, gen_xx_i, gen_helper_vsllwil_d_w) +TRANS(xvextl_q_d, LASX, gen_xx, gen_helper_vextl_q_d) +TRANS(xvsllwil_hu_bu, LASX, gen_xx_i, gen_helper_vsllwil_hu_bu) +TRANS(xvsllwil_wu_hu, LASX, gen_xx_i, gen_helper_vsllwil_wu_hu) +TRANS(xvsllwil_du_wu, LASX, gen_xx_i, gen_helper_vsllwil_du_wu) +TRANS(xvextl_qu_du, LASX, gen_xx, gen_helper_vextl_qu_du) + +TRANS(vsrlr_b, LSX, gen_vvv, gen_helper_vsrlr_b) +TRANS(vsrlr_h, LSX, gen_vvv, gen_helper_vsrlr_h) +TRANS(vsrlr_w, LSX, gen_vvv, gen_helper_vsrlr_w) +TRANS(vsrlr_d, LSX, gen_vvv, gen_helper_vsrlr_d) +TRANS(vsrlri_b, LSX, gen_vv_i, gen_helper_vsrlri_b) +TRANS(vsrlri_h, LSX, gen_vv_i, gen_helper_vsrlri_h) +TRANS(vsrlri_w, LSX, gen_vv_i, gen_helper_vsrlri_w) +TRANS(vsrlri_d, LSX, gen_vv_i, gen_helper_vsrlri_d) +TRANS(xvsrlr_b, LASX, gen_xxx, gen_helper_vsrlr_b) +TRANS(xvsrlr_h, LASX, gen_xxx, gen_helper_vsrlr_h) +TRANS(xvsrlr_w, LASX, gen_xxx, gen_helper_vsrlr_w) +TRANS(xvsrlr_d, LASX, gen_xxx, gen_helper_vsrlr_d) +TRANS(xvsrlri_b, LASX, gen_xx_i, gen_helper_vsrlri_b) +TRANS(xvsrlri_h, LASX, gen_xx_i, gen_helper_vsrlri_h) +TRANS(xvsrlri_w, LASX, gen_xx_i, gen_helper_vsrlri_w) +TRANS(xvsrlri_d, LASX, gen_xx_i, gen_helper_vsrlri_d) + +TRANS(vsrar_b, LSX, gen_vvv, gen_helper_vsrar_b) +TRANS(vsrar_h, LSX, gen_vvv, gen_helper_vsrar_h) +TRANS(vsrar_w, LSX, gen_vvv, gen_helper_vsrar_w) +TRANS(vsrar_d, LSX, gen_vvv, gen_helper_vsrar_d) +TRANS(vsrari_b, LSX, gen_vv_i, gen_helper_vsrari_b) +TRANS(vsrari_h, LSX, gen_vv_i, gen_helper_vsrari_h) +TRANS(vsrari_w, LSX, gen_vv_i, gen_helper_vsrari_w) +TRANS(vsrari_d, LSX, gen_vv_i, gen_helper_vsrari_d) +TRANS(xvsrar_b, LASX, gen_xxx, gen_helper_vsrar_b) +TRANS(xvsrar_h, LASX, gen_xxx, gen_helper_vsrar_h) +TRANS(xvsrar_w, LASX, gen_xxx, gen_helper_vsrar_w) +TRANS(xvsrar_d, LASX, gen_xxx, gen_helper_vsrar_d) +TRANS(xvsrari_b, LASX, gen_xx_i, gen_helper_vsrari_b) +TRANS(xvsrari_h, LASX, gen_xx_i, gen_helper_vsrari_h) +TRANS(xvsrari_w, LASX, gen_xx_i, gen_helper_vsrari_w) +TRANS(xvsrari_d, LASX, gen_xx_i, gen_helper_vsrari_d) + +TRANS(vsrln_b_h, LSX, gen_vvv, gen_helper_vsrln_b_h) +TRANS(vsrln_h_w, LSX, gen_vvv, gen_helper_vsrln_h_w) +TRANS(vsrln_w_d, LSX, gen_vvv, gen_helper_vsrln_w_d) +TRANS(vsran_b_h, LSX, gen_vvv, gen_helper_vsran_b_h) +TRANS(vsran_h_w, LSX, gen_vvv, gen_helper_vsran_h_w) +TRANS(vsran_w_d, LSX, gen_vvv, gen_helper_vsran_w_d) +TRANS(xvsrln_b_h, LASX, gen_xxx, gen_helper_vsrln_b_h) +TRANS(xvsrln_h_w, LASX, gen_xxx, gen_helper_vsrln_h_w) +TRANS(xvsrln_w_d, LASX, gen_xxx, gen_helper_vsrln_w_d) +TRANS(xvsran_b_h, LASX, gen_xxx, gen_helper_vsran_b_h) +TRANS(xvsran_h_w, LASX, gen_xxx, gen_helper_vsran_h_w) +TRANS(xvsran_w_d, LASX, gen_xxx, gen_helper_vsran_w_d) + +TRANS(vsrlni_b_h, LSX, gen_vv_i, gen_helper_vsrlni_b_h) +TRANS(vsrlni_h_w, LSX, gen_vv_i, gen_helper_vsrlni_h_w) +TRANS(vsrlni_w_d, LSX, gen_vv_i, gen_helper_vsrlni_w_d) +TRANS(vsrlni_d_q, LSX, gen_vv_i, gen_helper_vsrlni_d_q) +TRANS(vsrani_b_h, LSX, gen_vv_i, gen_helper_vsrani_b_h) +TRANS(vsrani_h_w, LSX, gen_vv_i, gen_helper_vsrani_h_w) +TRANS(vsrani_w_d, LSX, gen_vv_i, gen_helper_vsrani_w_d) +TRANS(vsrani_d_q, LSX, gen_vv_i, gen_helper_vsrani_d_q) +TRANS(xvsrlni_b_h, LASX, gen_xx_i, gen_helper_vsrlni_b_h) +TRANS(xvsrlni_h_w, LASX, gen_xx_i, gen_helper_vsrlni_h_w) +TRANS(xvsrlni_w_d, LASX, gen_xx_i, gen_helper_vsrlni_w_d) +TRANS(xvsrlni_d_q, LASX, gen_xx_i, gen_helper_vsrlni_d_q) +TRANS(xvsrani_b_h, LASX, gen_xx_i, gen_helper_vsrani_b_h) +TRANS(xvsrani_h_w, LASX, gen_xx_i, gen_helper_vsrani_h_w) +TRANS(xvsrani_w_d, LASX, gen_xx_i, gen_helper_vsrani_w_d) +TRANS(xvsrani_d_q, LASX, gen_xx_i, gen_helper_vsrani_d_q) + +TRANS(vsrlrn_b_h, LSX, gen_vvv, gen_helper_vsrlrn_b_h) +TRANS(vsrlrn_h_w, LSX, gen_vvv, gen_helper_vsrlrn_h_w) +TRANS(vsrlrn_w_d, LSX, gen_vvv, gen_helper_vsrlrn_w_d) +TRANS(vsrarn_b_h, LSX, gen_vvv, gen_helper_vsrarn_b_h) +TRANS(vsrarn_h_w, LSX, gen_vvv, gen_helper_vsrarn_h_w) +TRANS(vsrarn_w_d, LSX, gen_vvv, gen_helper_vsrarn_w_d) +TRANS(xvsrlrn_b_h, LASX, gen_xxx, gen_helper_vsrlrn_b_h) +TRANS(xvsrlrn_h_w, LASX, gen_xxx, gen_helper_vsrlrn_h_w) +TRANS(xvsrlrn_w_d, LASX, gen_xxx, gen_helper_vsrlrn_w_d) +TRANS(xvsrarn_b_h, LASX, gen_xxx, gen_helper_vsrarn_b_h) +TRANS(xvsrarn_h_w, LASX, gen_xxx, gen_helper_vsrarn_h_w) +TRANS(xvsrarn_w_d, LASX, gen_xxx, gen_helper_vsrarn_w_d) + +TRANS(vsrlrni_b_h, LSX, gen_vv_i, gen_helper_vsrlrni_b_h) +TRANS(vsrlrni_h_w, LSX, gen_vv_i, gen_helper_vsrlrni_h_w) +TRANS(vsrlrni_w_d, LSX, gen_vv_i, gen_helper_vsrlrni_w_d) +TRANS(vsrlrni_d_q, LSX, gen_vv_i, gen_helper_vsrlrni_d_q) +TRANS(vsrarni_b_h, LSX, gen_vv_i, gen_helper_vsrarni_b_h) +TRANS(vsrarni_h_w, LSX, gen_vv_i, gen_helper_vsrarni_h_w) +TRANS(vsrarni_w_d, LSX, gen_vv_i, gen_helper_vsrarni_w_d) +TRANS(vsrarni_d_q, LSX, gen_vv_i, gen_helper_vsrarni_d_q) +TRANS(xvsrlrni_b_h, LASX, gen_xx_i, gen_helper_vsrlrni_b_h) +TRANS(xvsrlrni_h_w, LASX, gen_xx_i, gen_helper_vsrlrni_h_w) +TRANS(xvsrlrni_w_d, LASX, gen_xx_i, gen_helper_vsrlrni_w_d) +TRANS(xvsrlrni_d_q, LASX, gen_xx_i, gen_helper_vsrlrni_d_q) +TRANS(xvsrarni_b_h, LASX, gen_xx_i, gen_helper_vsrarni_b_h) +TRANS(xvsrarni_h_w, LASX, gen_xx_i, gen_helper_vsrarni_h_w) +TRANS(xvsrarni_w_d, LASX, gen_xx_i, gen_helper_vsrarni_w_d) +TRANS(xvsrarni_d_q, LASX, gen_xx_i, gen_helper_vsrarni_d_q) + +TRANS(vssrln_b_h, LSX, gen_vvv, gen_helper_vssrln_b_h) +TRANS(vssrln_h_w, LSX, gen_vvv, gen_helper_vssrln_h_w) +TRANS(vssrln_w_d, LSX, gen_vvv, gen_helper_vssrln_w_d) +TRANS(vssran_b_h, LSX, gen_vvv, gen_helper_vssran_b_h) +TRANS(vssran_h_w, LSX, gen_vvv, gen_helper_vssran_h_w) +TRANS(vssran_w_d, LSX, gen_vvv, gen_helper_vssran_w_d) +TRANS(vssrln_bu_h, LSX, gen_vvv, gen_helper_vssrln_bu_h) +TRANS(vssrln_hu_w, LSX, gen_vvv, gen_helper_vssrln_hu_w) +TRANS(vssrln_wu_d, LSX, gen_vvv, gen_helper_vssrln_wu_d) +TRANS(vssran_bu_h, LSX, gen_vvv, gen_helper_vssran_bu_h) +TRANS(vssran_hu_w, LSX, gen_vvv, gen_helper_vssran_hu_w) +TRANS(vssran_wu_d, LSX, gen_vvv, gen_helper_vssran_wu_d) +TRANS(xvssrln_b_h, LASX, gen_xxx, gen_helper_vssrln_b_h) +TRANS(xvssrln_h_w, LASX, gen_xxx, gen_helper_vssrln_h_w) +TRANS(xvssrln_w_d, LASX, gen_xxx, gen_helper_vssrln_w_d) +TRANS(xvssran_b_h, LASX, gen_xxx, gen_helper_vssran_b_h) +TRANS(xvssran_h_w, LASX, gen_xxx, gen_helper_vssran_h_w) +TRANS(xvssran_w_d, LASX, gen_xxx, gen_helper_vssran_w_d) +TRANS(xvssrln_bu_h, LASX, gen_xxx, gen_helper_vssrln_bu_h) +TRANS(xvssrln_hu_w, LASX, gen_xxx, gen_helper_vssrln_hu_w) +TRANS(xvssrln_wu_d, LASX, gen_xxx, gen_helper_vssrln_wu_d) +TRANS(xvssran_bu_h, LASX, gen_xxx, gen_helper_vssran_bu_h) +TRANS(xvssran_hu_w, LASX, gen_xxx, gen_helper_vssran_hu_w) +TRANS(xvssran_wu_d, LASX, gen_xxx, gen_helper_vssran_wu_d) + +TRANS(vssrlni_b_h, LSX, gen_vv_i, gen_helper_vssrlni_b_h) +TRANS(vssrlni_h_w, LSX, gen_vv_i, gen_helper_vssrlni_h_w) +TRANS(vssrlni_w_d, LSX, gen_vv_i, gen_helper_vssrlni_w_d) +TRANS(vssrlni_d_q, LSX, gen_vv_i, gen_helper_vssrlni_d_q) +TRANS(vssrani_b_h, LSX, gen_vv_i, gen_helper_vssrani_b_h) +TRANS(vssrani_h_w, LSX, gen_vv_i, gen_helper_vssrani_h_w) +TRANS(vssrani_w_d, LSX, gen_vv_i, gen_helper_vssrani_w_d) +TRANS(vssrani_d_q, LSX, gen_vv_i, gen_helper_vssrani_d_q) +TRANS(vssrlni_bu_h, LSX, gen_vv_i, gen_helper_vssrlni_bu_h) +TRANS(vssrlni_hu_w, LSX, gen_vv_i, gen_helper_vssrlni_hu_w) +TRANS(vssrlni_wu_d, LSX, gen_vv_i, gen_helper_vssrlni_wu_d) +TRANS(vssrlni_du_q, LSX, gen_vv_i, gen_helper_vssrlni_du_q) +TRANS(vssrani_bu_h, LSX, gen_vv_i, gen_helper_vssrani_bu_h) +TRANS(vssrani_hu_w, LSX, gen_vv_i, gen_helper_vssrani_hu_w) +TRANS(vssrani_wu_d, LSX, gen_vv_i, gen_helper_vssrani_wu_d) +TRANS(vssrani_du_q, LSX, gen_vv_i, gen_helper_vssrani_du_q) +TRANS(xvssrlni_b_h, LASX, gen_xx_i, gen_helper_vssrlni_b_h) +TRANS(xvssrlni_h_w, LASX, gen_xx_i, gen_helper_vssrlni_h_w) +TRANS(xvssrlni_w_d, LASX, gen_xx_i, gen_helper_vssrlni_w_d) +TRANS(xvssrlni_d_q, LASX, gen_xx_i, gen_helper_vssrlni_d_q) +TRANS(xvssrani_b_h, LASX, gen_xx_i, gen_helper_vssrani_b_h) +TRANS(xvssrani_h_w, LASX, gen_xx_i, gen_helper_vssrani_h_w) +TRANS(xvssrani_w_d, LASX, gen_xx_i, gen_helper_vssrani_w_d) +TRANS(xvssrani_d_q, LASX, gen_xx_i, gen_helper_vssrani_d_q) +TRANS(xvssrlni_bu_h, LASX, gen_xx_i, gen_helper_vssrlni_bu_h) +TRANS(xvssrlni_hu_w, LASX, gen_xx_i, gen_helper_vssrlni_hu_w) +TRANS(xvssrlni_wu_d, LASX, gen_xx_i, gen_helper_vssrlni_wu_d) +TRANS(xvssrlni_du_q, LASX, gen_xx_i, gen_helper_vssrlni_du_q) +TRANS(xvssrani_bu_h, LASX, gen_xx_i, gen_helper_vssrani_bu_h) +TRANS(xvssrani_hu_w, LASX, gen_xx_i, gen_helper_vssrani_hu_w) +TRANS(xvssrani_wu_d, LASX, gen_xx_i, gen_helper_vssrani_wu_d) +TRANS(xvssrani_du_q, LASX, gen_xx_i, gen_helper_vssrani_du_q) + +TRANS(vssrlrn_b_h, LSX, gen_vvv, gen_helper_vssrlrn_b_h) +TRANS(vssrlrn_h_w, LSX, gen_vvv, gen_helper_vssrlrn_h_w) +TRANS(vssrlrn_w_d, LSX, gen_vvv, gen_helper_vssrlrn_w_d) +TRANS(vssrarn_b_h, LSX, gen_vvv, gen_helper_vssrarn_b_h) +TRANS(vssrarn_h_w, LSX, gen_vvv, gen_helper_vssrarn_h_w) +TRANS(vssrarn_w_d, LSX, gen_vvv, gen_helper_vssrarn_w_d) +TRANS(vssrlrn_bu_h, LSX, gen_vvv, gen_helper_vssrlrn_bu_h) +TRANS(vssrlrn_hu_w, LSX, gen_vvv, gen_helper_vssrlrn_hu_w) +TRANS(vssrlrn_wu_d, LSX, gen_vvv, gen_helper_vssrlrn_wu_d) +TRANS(vssrarn_bu_h, LSX, gen_vvv, gen_helper_vssrarn_bu_h) +TRANS(vssrarn_hu_w, LSX, gen_vvv, gen_helper_vssrarn_hu_w) +TRANS(vssrarn_wu_d, LSX, gen_vvv, gen_helper_vssrarn_wu_d) +TRANS(xvssrlrn_b_h, LASX, gen_xxx, gen_helper_vssrlrn_b_h) +TRANS(xvssrlrn_h_w, LASX, gen_xxx, gen_helper_vssrlrn_h_w) +TRANS(xvssrlrn_w_d, LASX, gen_xxx, gen_helper_vssrlrn_w_d) +TRANS(xvssrarn_b_h, LASX, gen_xxx, gen_helper_vssrarn_b_h) +TRANS(xvssrarn_h_w, LASX, gen_xxx, gen_helper_vssrarn_h_w) +TRANS(xvssrarn_w_d, LASX, gen_xxx, gen_helper_vssrarn_w_d) +TRANS(xvssrlrn_bu_h, LASX, gen_xxx, gen_helper_vssrlrn_bu_h) +TRANS(xvssrlrn_hu_w, LASX, gen_xxx, gen_helper_vssrlrn_hu_w) +TRANS(xvssrlrn_wu_d, LASX, gen_xxx, gen_helper_vssrlrn_wu_d) +TRANS(xvssrarn_bu_h, LASX, gen_xxx, gen_helper_vssrarn_bu_h) +TRANS(xvssrarn_hu_w, LASX, gen_xxx, gen_helper_vssrarn_hu_w) +TRANS(xvssrarn_wu_d, LASX, gen_xxx, gen_helper_vssrarn_wu_d) + +TRANS(vssrlrni_b_h, LSX, gen_vv_i, gen_helper_vssrlrni_b_h) +TRANS(vssrlrni_h_w, LSX, gen_vv_i, gen_helper_vssrlrni_h_w) +TRANS(vssrlrni_w_d, LSX, gen_vv_i, gen_helper_vssrlrni_w_d) +TRANS(vssrlrni_d_q, LSX, gen_vv_i, gen_helper_vssrlrni_d_q) +TRANS(vssrarni_b_h, LSX, gen_vv_i, gen_helper_vssrarni_b_h) +TRANS(vssrarni_h_w, LSX, gen_vv_i, gen_helper_vssrarni_h_w) +TRANS(vssrarni_w_d, LSX, gen_vv_i, gen_helper_vssrarni_w_d) +TRANS(vssrarni_d_q, LSX, gen_vv_i, gen_helper_vssrarni_d_q) +TRANS(vssrlrni_bu_h, LSX, gen_vv_i, gen_helper_vssrlrni_bu_h) +TRANS(vssrlrni_hu_w, LSX, gen_vv_i, gen_helper_vssrlrni_hu_w) +TRANS(vssrlrni_wu_d, LSX, gen_vv_i, gen_helper_vssrlrni_wu_d) +TRANS(vssrlrni_du_q, LSX, gen_vv_i, gen_helper_vssrlrni_du_q) +TRANS(vssrarni_bu_h, LSX, gen_vv_i, gen_helper_vssrarni_bu_h) +TRANS(vssrarni_hu_w, LSX, gen_vv_i, gen_helper_vssrarni_hu_w) +TRANS(vssrarni_wu_d, LSX, gen_vv_i, gen_helper_vssrarni_wu_d) +TRANS(vssrarni_du_q, LSX, gen_vv_i, gen_helper_vssrarni_du_q) +TRANS(xvssrlrni_b_h, LASX, gen_xx_i, gen_helper_vssrlrni_b_h) +TRANS(xvssrlrni_h_w, LASX, gen_xx_i, gen_helper_vssrlrni_h_w) +TRANS(xvssrlrni_w_d, LASX, gen_xx_i, gen_helper_vssrlrni_w_d) +TRANS(xvssrlrni_d_q, LASX, gen_xx_i, gen_helper_vssrlrni_d_q) +TRANS(xvssrarni_b_h, LASX, gen_xx_i, gen_helper_vssrarni_b_h) +TRANS(xvssrarni_h_w, LASX, gen_xx_i, gen_helper_vssrarni_h_w) +TRANS(xvssrarni_w_d, LASX, gen_xx_i, gen_helper_vssrarni_w_d) +TRANS(xvssrarni_d_q, LASX, gen_xx_i, gen_helper_vssrarni_d_q) +TRANS(xvssrlrni_bu_h, LASX, gen_xx_i, gen_helper_vssrlrni_bu_h) +TRANS(xvssrlrni_hu_w, LASX, gen_xx_i, gen_helper_vssrlrni_hu_w) +TRANS(xvssrlrni_wu_d, LASX, gen_xx_i, gen_helper_vssrlrni_wu_d) +TRANS(xvssrlrni_du_q, LASX, gen_xx_i, gen_helper_vssrlrni_du_q) +TRANS(xvssrarni_bu_h, LASX, gen_xx_i, gen_helper_vssrarni_bu_h) +TRANS(xvssrarni_hu_w, LASX, gen_xx_i, gen_helper_vssrarni_hu_w) +TRANS(xvssrarni_wu_d, LASX, gen_xx_i, gen_helper_vssrarni_wu_d) +TRANS(xvssrarni_du_q, LASX, gen_xx_i, gen_helper_vssrarni_du_q) + +TRANS(vclo_b, LSX, gen_vv, gen_helper_vclo_b) +TRANS(vclo_h, LSX, gen_vv, gen_helper_vclo_h) +TRANS(vclo_w, LSX, gen_vv, gen_helper_vclo_w) +TRANS(vclo_d, LSX, gen_vv, gen_helper_vclo_d) +TRANS(vclz_b, LSX, gen_vv, gen_helper_vclz_b) +TRANS(vclz_h, LSX, gen_vv, gen_helper_vclz_h) +TRANS(vclz_w, LSX, gen_vv, gen_helper_vclz_w) +TRANS(vclz_d, LSX, gen_vv, gen_helper_vclz_d) +TRANS(xvclo_b, LASX, gen_xx, gen_helper_vclo_b) +TRANS(xvclo_h, LASX, gen_xx, gen_helper_vclo_h) +TRANS(xvclo_w, LASX, gen_xx, gen_helper_vclo_w) +TRANS(xvclo_d, LASX, gen_xx, gen_helper_vclo_d) +TRANS(xvclz_b, LASX, gen_xx, gen_helper_vclz_b) +TRANS(xvclz_h, LASX, gen_xx, gen_helper_vclz_h) +TRANS(xvclz_w, LASX, gen_xx, gen_helper_vclz_w) +TRANS(xvclz_d, LASX, gen_xx, gen_helper_vclz_d) + +TRANS(vpcnt_b, LSX, gen_vv, gen_helper_vpcnt_b) +TRANS(vpcnt_h, LSX, gen_vv, gen_helper_vpcnt_h) +TRANS(vpcnt_w, LSX, gen_vv, gen_helper_vpcnt_w) +TRANS(vpcnt_d, LSX, gen_vv, gen_helper_vpcnt_d) +TRANS(xvpcnt_b, LASX, gen_xx, gen_helper_vpcnt_b) +TRANS(xvpcnt_h, LASX, gen_xx, gen_helper_vpcnt_h) +TRANS(xvpcnt_w, LASX, gen_xx, gen_helper_vpcnt_w) +TRANS(xvpcnt_d, LASX, gen_xx, gen_helper_vpcnt_d) + +static void do_vbit(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, + void (*func)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) +{ + TCGv_vec mask, lsh, t1, one; + + lsh = tcg_temp_new_vec_matching(t); + t1 = tcg_temp_new_vec_matching(t); + mask = tcg_constant_vec_matching(t, vece, (8 << vece) - 1); + one = tcg_constant_vec_matching(t, vece, 1); + + tcg_gen_and_vec(vece, lsh, b, mask); + tcg_gen_shlv_vec(vece, t1, one, lsh); + func(vece, t, a, t1); +} + +static void gen_vbitclr(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + do_vbit(vece, t, a, b, tcg_gen_andc_vec); +} + +static void gen_vbitset(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + do_vbit(vece, t, a, b, tcg_gen_or_vec); +} + +static void gen_vbitrev(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + do_vbit(vece, t, a, b, tcg_gen_xor_vec); +} + +static void do_vbitclr(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shlv_vec, INDEX_op_andc_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vbitclr, + .fno = gen_helper_vbitclr_b, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vbitclr, + .fno = gen_helper_vbitclr_h, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vbitclr, + .fno = gen_helper_vbitclr_w, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vbitclr, + .fno = gen_helper_vbitclr_d, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vbitclr_b, LSX, gvec_vvv, MO_8, do_vbitclr) +TRANS(vbitclr_h, LSX, gvec_vvv, MO_16, do_vbitclr) +TRANS(vbitclr_w, LSX, gvec_vvv, MO_32, do_vbitclr) +TRANS(vbitclr_d, LSX, gvec_vvv, MO_64, do_vbitclr) +TRANS(xvbitclr_b, LASX, gvec_xxx, MO_8, do_vbitclr) +TRANS(xvbitclr_h, LASX, gvec_xxx, MO_16, do_vbitclr) +TRANS(xvbitclr_w, LASX, gvec_xxx, MO_32, do_vbitclr) +TRANS(xvbitclr_d, LASX, gvec_xxx, MO_64, do_vbitclr) + +static void do_vbiti(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm, + void (*func)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) +{ + int lsh; + TCGv_vec t1, one; + + lsh = imm & ((8 << vece) -1); + t1 = tcg_temp_new_vec_matching(t); + one = tcg_constant_vec_matching(t, vece, 1); + + tcg_gen_shli_vec(vece, t1, one, lsh); + func(vece, t, a, t1); +} + +static void gen_vbitclri(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +{ + do_vbiti(vece, t, a, imm, tcg_gen_andc_vec); +} + +static void gen_vbitseti(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +{ + do_vbiti(vece, t, a, imm, tcg_gen_or_vec); +} + +static void gen_vbitrevi(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +{ + do_vbiti(vece, t, a, imm, tcg_gen_xor_vec); +} + +static void do_vbitclri(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + int64_t imm, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shli_vec, INDEX_op_andc_vec, 0 + }; + static const GVecGen2i op[4] = { + { + .fniv = gen_vbitclri, + .fnoi = gen_helper_vbitclri_b, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vbitclri, + .fnoi = gen_helper_vbitclri_h, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vbitclri, + .fnoi = gen_helper_vbitclri_w, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vbitclri, + .fnoi = gen_helper_vbitclri_d, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); +} + +TRANS(vbitclri_b, LSX, gvec_vv_i, MO_8, do_vbitclri) +TRANS(vbitclri_h, LSX, gvec_vv_i, MO_16, do_vbitclri) +TRANS(vbitclri_w, LSX, gvec_vv_i, MO_32, do_vbitclri) +TRANS(vbitclri_d, LSX, gvec_vv_i, MO_64, do_vbitclri) +TRANS(xvbitclri_b, LASX, gvec_xx_i, MO_8, do_vbitclri) +TRANS(xvbitclri_h, LASX, gvec_xx_i, MO_16, do_vbitclri) +TRANS(xvbitclri_w, LASX, gvec_xx_i, MO_32, do_vbitclri) +TRANS(xvbitclri_d, LASX, gvec_xx_i, MO_64, do_vbitclri) + +static void do_vbitset(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shlv_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vbitset, + .fno = gen_helper_vbitset_b, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vbitset, + .fno = gen_helper_vbitset_h, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vbitset, + .fno = gen_helper_vbitset_w, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vbitset, + .fno = gen_helper_vbitset_d, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vbitset_b, LSX, gvec_vvv, MO_8, do_vbitset) +TRANS(vbitset_h, LSX, gvec_vvv, MO_16, do_vbitset) +TRANS(vbitset_w, LSX, gvec_vvv, MO_32, do_vbitset) +TRANS(vbitset_d, LSX, gvec_vvv, MO_64, do_vbitset) +TRANS(xvbitset_b, LASX, gvec_xxx, MO_8, do_vbitset) +TRANS(xvbitset_h, LASX, gvec_xxx, MO_16, do_vbitset) +TRANS(xvbitset_w, LASX, gvec_xxx, MO_32, do_vbitset) +TRANS(xvbitset_d, LASX, gvec_xxx, MO_64, do_vbitset) + +static void do_vbitseti(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + int64_t imm, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shli_vec, 0 + }; + static const GVecGen2i op[4] = { + { + .fniv = gen_vbitseti, + .fnoi = gen_helper_vbitseti_b, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vbitseti, + .fnoi = gen_helper_vbitseti_h, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vbitseti, + .fnoi = gen_helper_vbitseti_w, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vbitseti, + .fnoi = gen_helper_vbitseti_d, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); +} + +TRANS(vbitseti_b, LSX, gvec_vv_i, MO_8, do_vbitseti) +TRANS(vbitseti_h, LSX, gvec_vv_i, MO_16, do_vbitseti) +TRANS(vbitseti_w, LSX, gvec_vv_i, MO_32, do_vbitseti) +TRANS(vbitseti_d, LSX, gvec_vv_i, MO_64, do_vbitseti) +TRANS(xvbitseti_b, LASX, gvec_xx_i, MO_8, do_vbitseti) +TRANS(xvbitseti_h, LASX, gvec_xx_i, MO_16, do_vbitseti) +TRANS(xvbitseti_w, LASX, gvec_xx_i, MO_32, do_vbitseti) +TRANS(xvbitseti_d, LASX, gvec_xx_i, MO_64, do_vbitseti) + +static void do_vbitrev(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shlv_vec, 0 + }; + static const GVecGen3 op[4] = { + { + .fniv = gen_vbitrev, + .fno = gen_helper_vbitrev_b, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vbitrev, + .fno = gen_helper_vbitrev_h, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vbitrev, + .fno = gen_helper_vbitrev_w, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vbitrev, + .fno = gen_helper_vbitrev_d, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +} + +TRANS(vbitrev_b, LSX, gvec_vvv, MO_8, do_vbitrev) +TRANS(vbitrev_h, LSX, gvec_vvv, MO_16, do_vbitrev) +TRANS(vbitrev_w, LSX, gvec_vvv, MO_32, do_vbitrev) +TRANS(vbitrev_d, LSX, gvec_vvv, MO_64, do_vbitrev) +TRANS(xvbitrev_b, LASX, gvec_xxx, MO_8, do_vbitrev) +TRANS(xvbitrev_h, LASX, gvec_xxx, MO_16, do_vbitrev) +TRANS(xvbitrev_w, LASX, gvec_xxx, MO_32, do_vbitrev) +TRANS(xvbitrev_d, LASX, gvec_xxx, MO_64, do_vbitrev) + +static void do_vbitrevi(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, + int64_t imm, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shli_vec, 0 + }; + static const GVecGen2i op[4] = { + { + .fniv = gen_vbitrevi, + .fnoi = gen_helper_vbitrevi_b, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vbitrevi, + .fnoi = gen_helper_vbitrevi_h, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vbitrevi, + .fnoi = gen_helper_vbitrevi_w, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vbitrevi, + .fnoi = gen_helper_vbitrevi_d, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); +} + +TRANS(vbitrevi_b, LSX, gvec_vv_i, MO_8, do_vbitrevi) +TRANS(vbitrevi_h, LSX, gvec_vv_i, MO_16, do_vbitrevi) +TRANS(vbitrevi_w, LSX, gvec_vv_i, MO_32, do_vbitrevi) +TRANS(vbitrevi_d, LSX, gvec_vv_i, MO_64, do_vbitrevi) +TRANS(xvbitrevi_b, LASX, gvec_xx_i, MO_8, do_vbitrevi) +TRANS(xvbitrevi_h, LASX, gvec_xx_i, MO_16, do_vbitrevi) +TRANS(xvbitrevi_w, LASX, gvec_xx_i, MO_32, do_vbitrevi) +TRANS(xvbitrevi_d, LASX, gvec_xx_i, MO_64, do_vbitrevi) + +TRANS(vfrstp_b, LSX, gen_vvv, gen_helper_vfrstp_b) +TRANS(vfrstp_h, LSX, gen_vvv, gen_helper_vfrstp_h) +TRANS(vfrstpi_b, LSX, gen_vv_i, gen_helper_vfrstpi_b) +TRANS(vfrstpi_h, LSX, gen_vv_i, gen_helper_vfrstpi_h) +TRANS(xvfrstp_b, LASX, gen_xxx, gen_helper_vfrstp_b) +TRANS(xvfrstp_h, LASX, gen_xxx, gen_helper_vfrstp_h) +TRANS(xvfrstpi_b, LASX, gen_xx_i, gen_helper_vfrstpi_b) +TRANS(xvfrstpi_h, LASX, gen_xx_i, gen_helper_vfrstpi_h) + +TRANS(vfadd_s, LSX, gen_vvv_ptr, gen_helper_vfadd_s) +TRANS(vfadd_d, LSX, gen_vvv_ptr, gen_helper_vfadd_d) +TRANS(vfsub_s, LSX, gen_vvv_ptr, gen_helper_vfsub_s) +TRANS(vfsub_d, LSX, gen_vvv_ptr, gen_helper_vfsub_d) +TRANS(vfmul_s, LSX, gen_vvv_ptr, gen_helper_vfmul_s) +TRANS(vfmul_d, LSX, gen_vvv_ptr, gen_helper_vfmul_d) +TRANS(vfdiv_s, LSX, gen_vvv_ptr, gen_helper_vfdiv_s) +TRANS(vfdiv_d, LSX, gen_vvv_ptr, gen_helper_vfdiv_d) +TRANS(xvfadd_s, LASX, gen_xxx_ptr, gen_helper_vfadd_s) +TRANS(xvfadd_d, LASX, gen_xxx_ptr, gen_helper_vfadd_d) +TRANS(xvfsub_s, LASX, gen_xxx_ptr, gen_helper_vfsub_s) +TRANS(xvfsub_d, LASX, gen_xxx_ptr, gen_helper_vfsub_d) +TRANS(xvfmul_s, LASX, gen_xxx_ptr, gen_helper_vfmul_s) +TRANS(xvfmul_d, LASX, gen_xxx_ptr, gen_helper_vfmul_d) +TRANS(xvfdiv_s, LASX, gen_xxx_ptr, gen_helper_vfdiv_s) +TRANS(xvfdiv_d, LASX, gen_xxx_ptr, gen_helper_vfdiv_d) + +TRANS(vfmadd_s, LSX, gen_vvvv_ptr, gen_helper_vfmadd_s) +TRANS(vfmadd_d, LSX, gen_vvvv_ptr, gen_helper_vfmadd_d) +TRANS(vfmsub_s, LSX, gen_vvvv_ptr, gen_helper_vfmsub_s) +TRANS(vfmsub_d, LSX, gen_vvvv_ptr, gen_helper_vfmsub_d) +TRANS(vfnmadd_s, LSX, gen_vvvv_ptr, gen_helper_vfnmadd_s) +TRANS(vfnmadd_d, LSX, gen_vvvv_ptr, gen_helper_vfnmadd_d) +TRANS(vfnmsub_s, LSX, gen_vvvv_ptr, gen_helper_vfnmsub_s) +TRANS(vfnmsub_d, LSX, gen_vvvv_ptr, gen_helper_vfnmsub_d) +TRANS(xvfmadd_s, LASX, gen_xxxx_ptr, gen_helper_vfmadd_s) +TRANS(xvfmadd_d, LASX, gen_xxxx_ptr, gen_helper_vfmadd_d) +TRANS(xvfmsub_s, LASX, gen_xxxx_ptr, gen_helper_vfmsub_s) +TRANS(xvfmsub_d, LASX, gen_xxxx_ptr, gen_helper_vfmsub_d) +TRANS(xvfnmadd_s, LASX, gen_xxxx_ptr, gen_helper_vfnmadd_s) +TRANS(xvfnmadd_d, LASX, gen_xxxx_ptr, gen_helper_vfnmadd_d) +TRANS(xvfnmsub_s, LASX, gen_xxxx_ptr, gen_helper_vfnmsub_s) +TRANS(xvfnmsub_d, LASX, gen_xxxx_ptr, gen_helper_vfnmsub_d) + +TRANS(vfmax_s, LSX, gen_vvv_ptr, gen_helper_vfmax_s) +TRANS(vfmax_d, LSX, gen_vvv_ptr, gen_helper_vfmax_d) +TRANS(vfmin_s, LSX, gen_vvv_ptr, gen_helper_vfmin_s) +TRANS(vfmin_d, LSX, gen_vvv_ptr, gen_helper_vfmin_d) +TRANS(xvfmax_s, LASX, gen_xxx_ptr, gen_helper_vfmax_s) +TRANS(xvfmax_d, LASX, gen_xxx_ptr, gen_helper_vfmax_d) +TRANS(xvfmin_s, LASX, gen_xxx_ptr, gen_helper_vfmin_s) +TRANS(xvfmin_d, LASX, gen_xxx_ptr, gen_helper_vfmin_d) + +TRANS(vfmaxa_s, LSX, gen_vvv_ptr, gen_helper_vfmaxa_s) +TRANS(vfmaxa_d, LSX, gen_vvv_ptr, gen_helper_vfmaxa_d) +TRANS(vfmina_s, LSX, gen_vvv_ptr, gen_helper_vfmina_s) +TRANS(vfmina_d, LSX, gen_vvv_ptr, gen_helper_vfmina_d) +TRANS(xvfmaxa_s, LASX, gen_xxx_ptr, gen_helper_vfmaxa_s) +TRANS(xvfmaxa_d, LASX, gen_xxx_ptr, gen_helper_vfmaxa_d) +TRANS(xvfmina_s, LASX, gen_xxx_ptr, gen_helper_vfmina_s) +TRANS(xvfmina_d, LASX, gen_xxx_ptr, gen_helper_vfmina_d) + +TRANS(vflogb_s, LSX, gen_vv_ptr, gen_helper_vflogb_s) +TRANS(vflogb_d, LSX, gen_vv_ptr, gen_helper_vflogb_d) +TRANS(xvflogb_s, LASX, gen_xx_ptr, gen_helper_vflogb_s) +TRANS(xvflogb_d, LASX, gen_xx_ptr, gen_helper_vflogb_d) + +TRANS(vfclass_s, LSX, gen_vv_ptr, gen_helper_vfclass_s) +TRANS(vfclass_d, LSX, gen_vv_ptr, gen_helper_vfclass_d) +TRANS(xvfclass_s, LASX, gen_xx_ptr, gen_helper_vfclass_s) +TRANS(xvfclass_d, LASX, gen_xx_ptr, gen_helper_vfclass_d) + +TRANS(vfsqrt_s, LSX, gen_vv_ptr, gen_helper_vfsqrt_s) +TRANS(vfsqrt_d, LSX, gen_vv_ptr, gen_helper_vfsqrt_d) +TRANS(vfrecip_s, LSX, gen_vv_ptr, gen_helper_vfrecip_s) +TRANS(vfrecip_d, LSX, gen_vv_ptr, gen_helper_vfrecip_d) +TRANS(vfrsqrt_s, LSX, gen_vv_ptr, gen_helper_vfrsqrt_s) +TRANS(vfrsqrt_d, LSX, gen_vv_ptr, gen_helper_vfrsqrt_d) +TRANS(xvfsqrt_s, LASX, gen_xx_ptr, gen_helper_vfsqrt_s) +TRANS(xvfsqrt_d, LASX, gen_xx_ptr, gen_helper_vfsqrt_d) +TRANS(xvfrecip_s, LASX, gen_xx_ptr, gen_helper_vfrecip_s) +TRANS(xvfrecip_d, LASX, gen_xx_ptr, gen_helper_vfrecip_d) +TRANS(xvfrsqrt_s, LASX, gen_xx_ptr, gen_helper_vfrsqrt_s) +TRANS(xvfrsqrt_d, LASX, gen_xx_ptr, gen_helper_vfrsqrt_d) + +TRANS(vfcvtl_s_h, LSX, gen_vv_ptr, gen_helper_vfcvtl_s_h) +TRANS(vfcvth_s_h, LSX, gen_vv_ptr, gen_helper_vfcvth_s_h) +TRANS(vfcvtl_d_s, LSX, gen_vv_ptr, gen_helper_vfcvtl_d_s) +TRANS(vfcvth_d_s, LSX, gen_vv_ptr, gen_helper_vfcvth_d_s) +TRANS(vfcvt_h_s, LSX, gen_vvv_ptr, gen_helper_vfcvt_h_s) +TRANS(vfcvt_s_d, LSX, gen_vvv_ptr, gen_helper_vfcvt_s_d) +TRANS(xvfcvtl_s_h, LASX, gen_xx_ptr, gen_helper_vfcvtl_s_h) +TRANS(xvfcvth_s_h, LASX, gen_xx_ptr, gen_helper_vfcvth_s_h) +TRANS(xvfcvtl_d_s, LASX, gen_xx_ptr, gen_helper_vfcvtl_d_s) +TRANS(xvfcvth_d_s, LASX, gen_xx_ptr, gen_helper_vfcvth_d_s) +TRANS(xvfcvt_h_s, LASX, gen_xxx_ptr, gen_helper_vfcvt_h_s) +TRANS(xvfcvt_s_d, LASX, gen_xxx_ptr, gen_helper_vfcvt_s_d) + +TRANS(vfrintrne_s, LSX, gen_vv_ptr, gen_helper_vfrintrne_s) +TRANS(vfrintrne_d, LSX, gen_vv_ptr, gen_helper_vfrintrne_d) +TRANS(vfrintrz_s, LSX, gen_vv_ptr, gen_helper_vfrintrz_s) +TRANS(vfrintrz_d, LSX, gen_vv_ptr, gen_helper_vfrintrz_d) +TRANS(vfrintrp_s, LSX, gen_vv_ptr, gen_helper_vfrintrp_s) +TRANS(vfrintrp_d, LSX, gen_vv_ptr, gen_helper_vfrintrp_d) +TRANS(vfrintrm_s, LSX, gen_vv_ptr, gen_helper_vfrintrm_s) +TRANS(vfrintrm_d, LSX, gen_vv_ptr, gen_helper_vfrintrm_d) +TRANS(vfrint_s, LSX, gen_vv_ptr, gen_helper_vfrint_s) +TRANS(vfrint_d, LSX, gen_vv_ptr, gen_helper_vfrint_d) +TRANS(xvfrintrne_s, LASX, gen_xx_ptr, gen_helper_vfrintrne_s) +TRANS(xvfrintrne_d, LASX, gen_xx_ptr, gen_helper_vfrintrne_d) +TRANS(xvfrintrz_s, LASX, gen_xx_ptr, gen_helper_vfrintrz_s) +TRANS(xvfrintrz_d, LASX, gen_xx_ptr, gen_helper_vfrintrz_d) +TRANS(xvfrintrp_s, LASX, gen_xx_ptr, gen_helper_vfrintrp_s) +TRANS(xvfrintrp_d, LASX, gen_xx_ptr, gen_helper_vfrintrp_d) +TRANS(xvfrintrm_s, LASX, gen_xx_ptr, gen_helper_vfrintrm_s) +TRANS(xvfrintrm_d, LASX, gen_xx_ptr, gen_helper_vfrintrm_d) +TRANS(xvfrint_s, LASX, gen_xx_ptr, gen_helper_vfrint_s) +TRANS(xvfrint_d, LASX, gen_xx_ptr, gen_helper_vfrint_d) + +TRANS(vftintrne_w_s, LSX, gen_vv_ptr, gen_helper_vftintrne_w_s) +TRANS(vftintrne_l_d, LSX, gen_vv_ptr, gen_helper_vftintrne_l_d) +TRANS(vftintrz_w_s, LSX, gen_vv_ptr, gen_helper_vftintrz_w_s) +TRANS(vftintrz_l_d, LSX, gen_vv_ptr, gen_helper_vftintrz_l_d) +TRANS(vftintrp_w_s, LSX, gen_vv_ptr, gen_helper_vftintrp_w_s) +TRANS(vftintrp_l_d, LSX, gen_vv_ptr, gen_helper_vftintrp_l_d) +TRANS(vftintrm_w_s, LSX, gen_vv_ptr, gen_helper_vftintrm_w_s) +TRANS(vftintrm_l_d, LSX, gen_vv_ptr, gen_helper_vftintrm_l_d) +TRANS(vftint_w_s, LSX, gen_vv_ptr, gen_helper_vftint_w_s) +TRANS(vftint_l_d, LSX, gen_vv_ptr, gen_helper_vftint_l_d) +TRANS(vftintrz_wu_s, LSX, gen_vv_ptr, gen_helper_vftintrz_wu_s) +TRANS(vftintrz_lu_d, LSX, gen_vv_ptr, gen_helper_vftintrz_lu_d) +TRANS(vftint_wu_s, LSX, gen_vv_ptr, gen_helper_vftint_wu_s) +TRANS(vftint_lu_d, LSX, gen_vv_ptr, gen_helper_vftint_lu_d) +TRANS(vftintrne_w_d, LSX, gen_vvv_ptr, gen_helper_vftintrne_w_d) +TRANS(vftintrz_w_d, LSX, gen_vvv_ptr, gen_helper_vftintrz_w_d) +TRANS(vftintrp_w_d, LSX, gen_vvv_ptr, gen_helper_vftintrp_w_d) +TRANS(vftintrm_w_d, LSX, gen_vvv_ptr, gen_helper_vftintrm_w_d) +TRANS(vftint_w_d, LSX, gen_vvv_ptr, gen_helper_vftint_w_d) +TRANS(vftintrnel_l_s, LSX, gen_vv_ptr, gen_helper_vftintrnel_l_s) +TRANS(vftintrneh_l_s, LSX, gen_vv_ptr, gen_helper_vftintrneh_l_s) +TRANS(vftintrzl_l_s, LSX, gen_vv_ptr, gen_helper_vftintrzl_l_s) +TRANS(vftintrzh_l_s, LSX, gen_vv_ptr, gen_helper_vftintrzh_l_s) +TRANS(vftintrpl_l_s, LSX, gen_vv_ptr, gen_helper_vftintrpl_l_s) +TRANS(vftintrph_l_s, LSX, gen_vv_ptr, gen_helper_vftintrph_l_s) +TRANS(vftintrml_l_s, LSX, gen_vv_ptr, gen_helper_vftintrml_l_s) +TRANS(vftintrmh_l_s, LSX, gen_vv_ptr, gen_helper_vftintrmh_l_s) +TRANS(vftintl_l_s, LSX, gen_vv_ptr, gen_helper_vftintl_l_s) +TRANS(vftinth_l_s, LSX, gen_vv_ptr, gen_helper_vftinth_l_s) +TRANS(xvftintrne_w_s, LASX, gen_xx_ptr, gen_helper_vftintrne_w_s) +TRANS(xvftintrne_l_d, LASX, gen_xx_ptr, gen_helper_vftintrne_l_d) +TRANS(xvftintrz_w_s, LASX, gen_xx_ptr, gen_helper_vftintrz_w_s) +TRANS(xvftintrz_l_d, LASX, gen_xx_ptr, gen_helper_vftintrz_l_d) +TRANS(xvftintrp_w_s, LASX, gen_xx_ptr, gen_helper_vftintrp_w_s) +TRANS(xvftintrp_l_d, LASX, gen_xx_ptr, gen_helper_vftintrp_l_d) +TRANS(xvftintrm_w_s, LASX, gen_xx_ptr, gen_helper_vftintrm_w_s) +TRANS(xvftintrm_l_d, LASX, gen_xx_ptr, gen_helper_vftintrm_l_d) +TRANS(xvftint_w_s, LASX, gen_xx_ptr, gen_helper_vftint_w_s) +TRANS(xvftint_l_d, LASX, gen_xx_ptr, gen_helper_vftint_l_d) +TRANS(xvftintrz_wu_s, LASX, gen_xx_ptr, gen_helper_vftintrz_wu_s) +TRANS(xvftintrz_lu_d, LASX, gen_xx_ptr, gen_helper_vftintrz_lu_d) +TRANS(xvftint_wu_s, LASX, gen_xx_ptr, gen_helper_vftint_wu_s) +TRANS(xvftint_lu_d, LASX, gen_xx_ptr, gen_helper_vftint_lu_d) +TRANS(xvftintrne_w_d, LASX, gen_xxx_ptr, gen_helper_vftintrne_w_d) +TRANS(xvftintrz_w_d, LASX, gen_xxx_ptr, gen_helper_vftintrz_w_d) +TRANS(xvftintrp_w_d, LASX, gen_xxx_ptr, gen_helper_vftintrp_w_d) +TRANS(xvftintrm_w_d, LASX, gen_xxx_ptr, gen_helper_vftintrm_w_d) +TRANS(xvftint_w_d, LASX, gen_xxx_ptr, gen_helper_vftint_w_d) +TRANS(xvftintrnel_l_s, LASX, gen_xx_ptr, gen_helper_vftintrnel_l_s) +TRANS(xvftintrneh_l_s, LASX, gen_xx_ptr, gen_helper_vftintrneh_l_s) +TRANS(xvftintrzl_l_s, LASX, gen_xx_ptr, gen_helper_vftintrzl_l_s) +TRANS(xvftintrzh_l_s, LASX, gen_xx_ptr, gen_helper_vftintrzh_l_s) +TRANS(xvftintrpl_l_s, LASX, gen_xx_ptr, gen_helper_vftintrpl_l_s) +TRANS(xvftintrph_l_s, LASX, gen_xx_ptr, gen_helper_vftintrph_l_s) +TRANS(xvftintrml_l_s, LASX, gen_xx_ptr, gen_helper_vftintrml_l_s) +TRANS(xvftintrmh_l_s, LASX, gen_xx_ptr, gen_helper_vftintrmh_l_s) +TRANS(xvftintl_l_s, LASX, gen_xx_ptr, gen_helper_vftintl_l_s) +TRANS(xvftinth_l_s, LASX, gen_xx_ptr, gen_helper_vftinth_l_s) + +TRANS(vffint_s_w, LSX, gen_vv_ptr, gen_helper_vffint_s_w) +TRANS(vffint_d_l, LSX, gen_vv_ptr, gen_helper_vffint_d_l) +TRANS(vffint_s_wu, LSX, gen_vv_ptr, gen_helper_vffint_s_wu) +TRANS(vffint_d_lu, LSX, gen_vv_ptr, gen_helper_vffint_d_lu) +TRANS(vffintl_d_w, LSX, gen_vv_ptr, gen_helper_vffintl_d_w) +TRANS(vffinth_d_w, LSX, gen_vv_ptr, gen_helper_vffinth_d_w) +TRANS(vffint_s_l, LSX, gen_vvv_ptr, gen_helper_vffint_s_l) +TRANS(xvffint_s_w, LASX, gen_xx_ptr, gen_helper_vffint_s_w) +TRANS(xvffint_d_l, LASX, gen_xx_ptr, gen_helper_vffint_d_l) +TRANS(xvffint_s_wu, LASX, gen_xx_ptr, gen_helper_vffint_s_wu) +TRANS(xvffint_d_lu, LASX, gen_xx_ptr, gen_helper_vffint_d_lu) +TRANS(xvffintl_d_w, LASX, gen_xx_ptr, gen_helper_vffintl_d_w) +TRANS(xvffinth_d_w, LASX, gen_xx_ptr, gen_helper_vffinth_d_w) +TRANS(xvffint_s_l, LASX, gen_xxx_ptr, gen_helper_vffint_s_l) + +static bool do_cmp_vl(DisasContext *ctx, arg_vvv *a, + uint32_t oprsz, MemOp mop, TCGCond cond) +{ + uint32_t vd_ofs, vj_ofs, vk_ofs; + + if (!check_vec(ctx, oprsz)) { + return true; + } + + vd_ofs = vec_full_offset(a->vd); + vj_ofs = vec_full_offset(a->vj); + vk_ofs = vec_full_offset(a->vk); + + tcg_gen_gvec_cmp(cond, mop, vd_ofs, vj_ofs, vk_ofs, oprsz, ctx->vl / 8); + return true; +} + +static bool do_cmp(DisasContext *ctx, arg_vvv *a, + MemOp mop, TCGCond cond) +{ + return do_cmp_vl(ctx, a, 16, mop, cond); +} + +static bool do_xcmp(DisasContext *ctx, arg_vvv *a, + MemOp mop, TCGCond cond) +{ + return do_cmp_vl(ctx, a, 32, mop, cond); +} + +static bool do_cmpi_vl(DisasContext *ctx, arg_vv_i *a, + uint32_t oprsz, MemOp mop, TCGCond cond) +{ + uint32_t vd_ofs, vj_ofs; + + if (!check_vec(ctx, oprsz)) { + return true; + } + + vd_ofs = vec_full_offset(a->vd); + vj_ofs = vec_full_offset(a->vj); + + tcg_gen_gvec_cmpi(cond, mop, vd_ofs, vj_ofs, a->imm, oprsz, ctx->vl / 8); + return true; +} + +static bool do_cmpi(DisasContext *ctx, arg_vv_i *a, + MemOp mop, TCGCond cond) +{ + return do_cmpi_vl(ctx, a, 16, mop, cond); +} + +static bool do_xcmpi(DisasContext *ctx, arg_vv_i *a, + MemOp mop, TCGCond cond) +{ + return do_cmpi_vl(ctx, a, 32, mop, cond); +} + +TRANS(vseq_b, LSX, do_cmp, MO_8, TCG_COND_EQ) +TRANS(vseq_h, LSX, do_cmp, MO_16, TCG_COND_EQ) +TRANS(vseq_w, LSX, do_cmp, MO_32, TCG_COND_EQ) +TRANS(vseq_d, LSX, do_cmp, MO_64, TCG_COND_EQ) +TRANS(vseqi_b, LSX, do_cmpi, MO_8, TCG_COND_EQ) +TRANS(vseqi_h, LSX, do_cmpi, MO_16, TCG_COND_EQ) +TRANS(vseqi_w, LSX, do_cmpi, MO_32, TCG_COND_EQ) +TRANS(vseqi_d, LSX, do_cmpi, MO_64, TCG_COND_EQ) +TRANS(xvseq_b, LASX, do_xcmp, MO_8, TCG_COND_EQ) +TRANS(xvseq_h, LASX, do_xcmp, MO_16, TCG_COND_EQ) +TRANS(xvseq_w, LASX, do_xcmp, MO_32, TCG_COND_EQ) +TRANS(xvseq_d, LASX, do_xcmp, MO_64, TCG_COND_EQ) +TRANS(xvseqi_b, LASX, do_xcmpi, MO_8, TCG_COND_EQ) +TRANS(xvseqi_h, LASX, do_xcmpi, MO_16, TCG_COND_EQ) +TRANS(xvseqi_w, LASX, do_xcmpi, MO_32, TCG_COND_EQ) +TRANS(xvseqi_d, LASX, do_xcmpi, MO_64, TCG_COND_EQ) + +TRANS(vsle_b, LSX, do_cmp, MO_8, TCG_COND_LE) +TRANS(vsle_h, LSX, do_cmp, MO_16, TCG_COND_LE) +TRANS(vsle_w, LSX, do_cmp, MO_32, TCG_COND_LE) +TRANS(vsle_d, LSX, do_cmp, MO_64, TCG_COND_LE) +TRANS(vslei_b, LSX, do_cmpi, MO_8, TCG_COND_LE) +TRANS(vslei_h, LSX, do_cmpi, MO_16, TCG_COND_LE) +TRANS(vslei_w, LSX, do_cmpi, MO_32, TCG_COND_LE) +TRANS(vslei_d, LSX, do_cmpi, MO_64, TCG_COND_LE) +TRANS(vsle_bu, LSX, do_cmp, MO_8, TCG_COND_LEU) +TRANS(vsle_hu, LSX, do_cmp, MO_16, TCG_COND_LEU) +TRANS(vsle_wu, LSX, do_cmp, MO_32, TCG_COND_LEU) +TRANS(vsle_du, LSX, do_cmp, MO_64, TCG_COND_LEU) +TRANS(vslei_bu, LSX, do_cmpi, MO_8, TCG_COND_LEU) +TRANS(vslei_hu, LSX, do_cmpi, MO_16, TCG_COND_LEU) +TRANS(vslei_wu, LSX, do_cmpi, MO_32, TCG_COND_LEU) +TRANS(vslei_du, LSX, do_cmpi, MO_64, TCG_COND_LEU) +TRANS(xvsle_b, LASX, do_xcmp, MO_8, TCG_COND_LE) +TRANS(xvsle_h, LASX, do_xcmp, MO_16, TCG_COND_LE) +TRANS(xvsle_w, LASX, do_xcmp, MO_32, TCG_COND_LE) +TRANS(xvsle_d, LASX, do_xcmp, MO_64, TCG_COND_LE) +TRANS(xvslei_b, LASX, do_xcmpi, MO_8, TCG_COND_LE) +TRANS(xvslei_h, LASX, do_xcmpi, MO_16, TCG_COND_LE) +TRANS(xvslei_w, LASX, do_xcmpi, MO_32, TCG_COND_LE) +TRANS(xvslei_d, LASX, do_xcmpi, MO_64, TCG_COND_LE) +TRANS(xvsle_bu, LASX, do_xcmp, MO_8, TCG_COND_LEU) +TRANS(xvsle_hu, LASX, do_xcmp, MO_16, TCG_COND_LEU) +TRANS(xvsle_wu, LASX, do_xcmp, MO_32, TCG_COND_LEU) +TRANS(xvsle_du, LASX, do_xcmp, MO_64, TCG_COND_LEU) +TRANS(xvslei_bu, LASX, do_xcmpi, MO_8, TCG_COND_LEU) +TRANS(xvslei_hu, LASX, do_xcmpi, MO_16, TCG_COND_LEU) +TRANS(xvslei_wu, LASX, do_xcmpi, MO_32, TCG_COND_LEU) +TRANS(xvslei_du, LASX, do_xcmpi, MO_64, TCG_COND_LEU) + +TRANS(vslt_b, LSX, do_cmp, MO_8, TCG_COND_LT) +TRANS(vslt_h, LSX, do_cmp, MO_16, TCG_COND_LT) +TRANS(vslt_w, LSX, do_cmp, MO_32, TCG_COND_LT) +TRANS(vslt_d, LSX, do_cmp, MO_64, TCG_COND_LT) +TRANS(vslti_b, LSX, do_cmpi, MO_8, TCG_COND_LT) +TRANS(vslti_h, LSX, do_cmpi, MO_16, TCG_COND_LT) +TRANS(vslti_w, LSX, do_cmpi, MO_32, TCG_COND_LT) +TRANS(vslti_d, LSX, do_cmpi, MO_64, TCG_COND_LT) +TRANS(vslt_bu, LSX, do_cmp, MO_8, TCG_COND_LTU) +TRANS(vslt_hu, LSX, do_cmp, MO_16, TCG_COND_LTU) +TRANS(vslt_wu, LSX, do_cmp, MO_32, TCG_COND_LTU) +TRANS(vslt_du, LSX, do_cmp, MO_64, TCG_COND_LTU) +TRANS(vslti_bu, LSX, do_cmpi, MO_8, TCG_COND_LTU) +TRANS(vslti_hu, LSX, do_cmpi, MO_16, TCG_COND_LTU) +TRANS(vslti_wu, LSX, do_cmpi, MO_32, TCG_COND_LTU) +TRANS(vslti_du, LSX, do_cmpi, MO_64, TCG_COND_LTU) +TRANS(xvslt_b, LASX, do_xcmp, MO_8, TCG_COND_LT) +TRANS(xvslt_h, LASX, do_xcmp, MO_16, TCG_COND_LT) +TRANS(xvslt_w, LASX, do_xcmp, MO_32, TCG_COND_LT) +TRANS(xvslt_d, LASX, do_xcmp, MO_64, TCG_COND_LT) +TRANS(xvslti_b, LASX, do_xcmpi, MO_8, TCG_COND_LT) +TRANS(xvslti_h, LASX, do_xcmpi, MO_16, TCG_COND_LT) +TRANS(xvslti_w, LASX, do_xcmpi, MO_32, TCG_COND_LT) +TRANS(xvslti_d, LASX, do_xcmpi, MO_64, TCG_COND_LT) +TRANS(xvslt_bu, LASX, do_xcmp, MO_8, TCG_COND_LTU) +TRANS(xvslt_hu, LASX, do_xcmp, MO_16, TCG_COND_LTU) +TRANS(xvslt_wu, LASX, do_xcmp, MO_32, TCG_COND_LTU) +TRANS(xvslt_du, LASX, do_xcmp, MO_64, TCG_COND_LTU) +TRANS(xvslti_bu, LASX, do_xcmpi, MO_8, TCG_COND_LTU) +TRANS(xvslti_hu, LASX, do_xcmpi, MO_16, TCG_COND_LTU) +TRANS(xvslti_wu, LASX, do_xcmpi, MO_32, TCG_COND_LTU) +TRANS(xvslti_du, LASX, do_xcmpi, MO_64, TCG_COND_LTU) + +static bool do_vfcmp_cond_s(DisasContext *ctx, arg_vvv_fcond *a, uint32_t sz) +{ + uint32_t flags; + void (*fn)(TCGv_env, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); + TCGv_i32 vd = tcg_constant_i32(a->vd); + TCGv_i32 vj = tcg_constant_i32(a->vj); + TCGv_i32 vk = tcg_constant_i32(a->vk); + TCGv_i32 oprsz = tcg_constant_i32(sz); + + if (!check_vec(ctx, sz)) { + return true; + } + + fn = (a->fcond & 1 ? gen_helper_vfcmp_s_s : gen_helper_vfcmp_c_s); + flags = get_fcmp_flags(a->fcond >> 1); + fn(tcg_env, oprsz, vd, vj, vk, tcg_constant_i32(flags)); + + return true; +} + +static bool do_vfcmp_cond_d(DisasContext *ctx, arg_vvv_fcond *a, uint32_t sz) +{ + uint32_t flags; + void (*fn)(TCGv_env, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); + TCGv_i32 vd = tcg_constant_i32(a->vd); + TCGv_i32 vj = tcg_constant_i32(a->vj); + TCGv_i32 vk = tcg_constant_i32(a->vk); + TCGv_i32 oprsz = tcg_constant_i32(sz); + + if (!check_vec(ctx, sz)) { + return true; + } + + fn = (a->fcond & 1 ? gen_helper_vfcmp_s_d : gen_helper_vfcmp_c_d); + flags = get_fcmp_flags(a->fcond >> 1); + fn(tcg_env, oprsz, vd, vj, vk, tcg_constant_i32(flags)); + + return true; +} + +TRANS(vfcmp_cond_s, LSX, do_vfcmp_cond_s, 16) +TRANS(vfcmp_cond_d, LSX, do_vfcmp_cond_d, 16) +TRANS(xvfcmp_cond_s, LASX, do_vfcmp_cond_s, 32) +TRANS(xvfcmp_cond_d, LASX, do_vfcmp_cond_d, 32) + +static bool do_vbitsel_v(DisasContext *ctx, arg_vvvv *a, uint32_t oprsz) +{ + if (!check_vec(ctx, oprsz)) { + return true; + } + + tcg_gen_gvec_bitsel(MO_64, vec_full_offset(a->vd), vec_full_offset(a->va), + vec_full_offset(a->vk), vec_full_offset(a->vj), + oprsz, ctx->vl / 8); + return true; +} + +TRANS(vbitsel_v, LSX, do_vbitsel_v, 16) +TRANS(xvbitsel_v, LASX, do_vbitsel_v, 32) + +static void gen_vbitseli(unsigned vece, TCGv_vec a, TCGv_vec b, int64_t imm) +{ + tcg_gen_bitsel_vec(vece, a, a, tcg_constant_vec_matching(a, vece, imm), b); +} + +static bool do_vbitseli_b(DisasContext *ctx, arg_vv_i *a, uint32_t oprsz) +{ + static const GVecGen2i op = { + .fniv = gen_vbitseli, + .fnoi = gen_helper_vbitseli_b, + .vece = MO_8, + .load_dest = true + }; + + if (!check_vec(ctx, oprsz)) { + return true; + } + + tcg_gen_gvec_2i(vec_full_offset(a->vd), vec_full_offset(a->vj), + oprsz, ctx->vl / 8, a->imm , &op); + return true; +} + +TRANS(vbitseli_b, LSX, do_vbitseli_b, 16) +TRANS(xvbitseli_b, LASX, do_vbitseli_b, 32) + +#define VSET(NAME, COND) \ +static bool trans_## NAME (DisasContext *ctx, arg_cv *a) \ +{ \ + TCGv_i64 t1, al, ah; \ + \ + al = tcg_temp_new_i64(); \ + ah = tcg_temp_new_i64(); \ + t1 = tcg_temp_new_i64(); \ + \ + get_vreg64(ah, a->vj, 1); \ + get_vreg64(al, a->vj, 0); \ + \ + if (!avail_LSX(ctx)) { \ + return false; \ + } \ + \ + if (!check_vec(ctx, 16)) { \ + return true; \ + } \ + \ + tcg_gen_or_i64(t1, al, ah); \ + tcg_gen_setcondi_i64(COND, t1, t1, 0); \ + tcg_gen_st8_tl(t1, tcg_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); \ + \ + return true; \ +} + +VSET(vseteqz_v, TCG_COND_EQ) +VSET(vsetnez_v, TCG_COND_NE) + +TRANS(vsetanyeqz_b, LSX, gen_cv, gen_helper_vsetanyeqz_b) +TRANS(vsetanyeqz_h, LSX, gen_cv, gen_helper_vsetanyeqz_h) +TRANS(vsetanyeqz_w, LSX, gen_cv, gen_helper_vsetanyeqz_w) +TRANS(vsetanyeqz_d, LSX, gen_cv, gen_helper_vsetanyeqz_d) +TRANS(vsetallnez_b, LSX, gen_cv, gen_helper_vsetallnez_b) +TRANS(vsetallnez_h, LSX, gen_cv, gen_helper_vsetallnez_h) +TRANS(vsetallnez_w, LSX, gen_cv, gen_helper_vsetallnez_w) +TRANS(vsetallnez_d, LSX, gen_cv, gen_helper_vsetallnez_d) + +#define XVSET(NAME, COND) \ +static bool trans_## NAME(DisasContext *ctx, arg_cv * a) \ +{ \ + TCGv_i64 t1, t2, d[4]; \ + \ + d[0] = tcg_temp_new_i64(); \ + d[1] = tcg_temp_new_i64(); \ + d[2] = tcg_temp_new_i64(); \ + d[3] = tcg_temp_new_i64(); \ + t1 = tcg_temp_new_i64(); \ + t2 = tcg_temp_new_i64(); \ + \ + get_vreg64(d[0], a->vj, 0); \ + get_vreg64(d[1], a->vj, 1); \ + get_vreg64(d[2], a->vj, 2); \ + get_vreg64(d[3], a->vj, 3); \ + \ + if (!avail_LASX(ctx)) { \ + return false; \ + } \ + \ + if (!check_vec(ctx, 32)) { \ + return true; \ + } \ + \ + tcg_gen_or_i64(t1, d[0], d[1]); \ + tcg_gen_or_i64(t2, d[2], d[3]); \ + tcg_gen_or_i64(t1, t2, t1); \ + tcg_gen_setcondi_i64(COND, t1, t1, 0); \ + tcg_gen_st8_tl(t1, tcg_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); \ + \ + return true; \ +} + +XVSET(xvseteqz_v, TCG_COND_EQ) +XVSET(xvsetnez_v, TCG_COND_NE) + +TRANS(xvsetanyeqz_b, LASX, gen_cx, gen_helper_vsetanyeqz_b) +TRANS(xvsetanyeqz_h, LASX, gen_cx, gen_helper_vsetanyeqz_h) +TRANS(xvsetanyeqz_w, LASX, gen_cx, gen_helper_vsetanyeqz_w) +TRANS(xvsetanyeqz_d, LASX, gen_cx, gen_helper_vsetanyeqz_d) +TRANS(xvsetallnez_b, LASX, gen_cx, gen_helper_vsetallnez_b) +TRANS(xvsetallnez_h, LASX, gen_cx, gen_helper_vsetallnez_h) +TRANS(xvsetallnez_w, LASX, gen_cx, gen_helper_vsetallnez_w) +TRANS(xvsetallnez_d, LASX, gen_cx, gen_helper_vsetallnez_d) + +static bool gen_g2v_vl(DisasContext *ctx, arg_vr_i *a, uint32_t oprsz, MemOp mop, + void (*func)(TCGv, TCGv_ptr, tcg_target_long)) +{ + TCGv src = gpr_src(ctx, a->rj, EXT_NONE); + + if (!check_vec(ctx, oprsz)) { + return true; + } + + func(src, tcg_env, vec_reg_offset(a->vd, a->imm, mop)); + + return true; +} + +static bool gen_g2v(DisasContext *ctx, arg_vr_i *a, MemOp mop, + void (*func)(TCGv, TCGv_ptr, tcg_target_long)) +{ + return gen_g2v_vl(ctx, a, 16, mop, func); +} + +static bool gen_g2x(DisasContext *ctx, arg_vr_i *a, MemOp mop, + void (*func)(TCGv, TCGv_ptr, tcg_target_long)) +{ + return gen_g2v_vl(ctx, a, 32, mop, func); +} + +TRANS(vinsgr2vr_b, LSX, gen_g2v, MO_8, tcg_gen_st8_i64) +TRANS(vinsgr2vr_h, LSX, gen_g2v, MO_16, tcg_gen_st16_i64) +TRANS(vinsgr2vr_w, LSX, gen_g2v, MO_32, tcg_gen_st32_i64) +TRANS(vinsgr2vr_d, LSX, gen_g2v, MO_64, tcg_gen_st_i64) +TRANS(xvinsgr2vr_w, LASX, gen_g2x, MO_32, tcg_gen_st32_i64) +TRANS(xvinsgr2vr_d, LASX, gen_g2x, MO_64, tcg_gen_st_i64) + +static bool gen_v2g_vl(DisasContext *ctx, arg_rv_i *a, uint32_t oprsz, MemOp mop, + void (*func)(TCGv, TCGv_ptr, tcg_target_long)) +{ + TCGv dst = gpr_dst(ctx, a->rd, EXT_NONE); + + if (!check_vec(ctx, oprsz)) { + return true; + } + + func(dst, tcg_env, vec_reg_offset(a->vj, a->imm, mop)); + + return true; +} + +static bool gen_v2g(DisasContext *ctx, arg_rv_i *a, MemOp mop, + void (*func)(TCGv, TCGv_ptr, tcg_target_long)) +{ + return gen_v2g_vl(ctx, a, 16, mop, func); +} + +static bool gen_x2g(DisasContext *ctx, arg_rv_i *a, MemOp mop, + void (*func)(TCGv, TCGv_ptr, tcg_target_long)) +{ + return gen_v2g_vl(ctx, a, 32, mop, func); +} + +TRANS(vpickve2gr_b, LSX, gen_v2g, MO_8, tcg_gen_ld8s_i64) +TRANS(vpickve2gr_h, LSX, gen_v2g, MO_16, tcg_gen_ld16s_i64) +TRANS(vpickve2gr_w, LSX, gen_v2g, MO_32, tcg_gen_ld32s_i64) +TRANS(vpickve2gr_d, LSX, gen_v2g, MO_64, tcg_gen_ld_i64) +TRANS(vpickve2gr_bu, LSX, gen_v2g, MO_8, tcg_gen_ld8u_i64) +TRANS(vpickve2gr_hu, LSX, gen_v2g, MO_16, tcg_gen_ld16u_i64) +TRANS(vpickve2gr_wu, LSX, gen_v2g, MO_32, tcg_gen_ld32u_i64) +TRANS(vpickve2gr_du, LSX, gen_v2g, MO_64, tcg_gen_ld_i64) +TRANS(xvpickve2gr_w, LASX, gen_x2g, MO_32, tcg_gen_ld32s_i64) +TRANS(xvpickve2gr_d, LASX, gen_x2g, MO_64, tcg_gen_ld_i64) +TRANS(xvpickve2gr_wu, LASX, gen_x2g, MO_32, tcg_gen_ld32u_i64) +TRANS(xvpickve2gr_du, LASX, gen_x2g, MO_64, tcg_gen_ld_i64) + +static bool gvec_dup_vl(DisasContext *ctx, arg_vr *a, + uint32_t oprsz, MemOp mop) +{ + TCGv src = gpr_src(ctx, a->rj, EXT_NONE); + + if (!check_vec(ctx, oprsz)) { + return true; + } + + tcg_gen_gvec_dup_i64(mop, vec_full_offset(a->vd), + oprsz, ctx->vl/8, src); + return true; +} + +static bool gvec_dup(DisasContext *ctx, arg_vr *a, MemOp mop) +{ + return gvec_dup_vl(ctx, a, 16, mop); +} + +static bool gvec_dupx(DisasContext *ctx, arg_vr *a, MemOp mop) +{ + return gvec_dup_vl(ctx, a, 32, mop); +} + +TRANS(vreplgr2vr_b, LSX, gvec_dup, MO_8) +TRANS(vreplgr2vr_h, LSX, gvec_dup, MO_16) +TRANS(vreplgr2vr_w, LSX, gvec_dup, MO_32) +TRANS(vreplgr2vr_d, LSX, gvec_dup, MO_64) +TRANS(xvreplgr2vr_b, LASX, gvec_dupx, MO_8) +TRANS(xvreplgr2vr_h, LASX, gvec_dupx, MO_16) +TRANS(xvreplgr2vr_w, LASX, gvec_dupx, MO_32) +TRANS(xvreplgr2vr_d, LASX, gvec_dupx, MO_64) + +static bool trans_vreplvei_b(DisasContext *ctx, arg_vv_i *a) +{ + if (!avail_LSX(ctx)) { + return false; + } + + if (!check_vec(ctx, 16)) { + return true; + } + + tcg_gen_gvec_dup_mem(MO_8,vec_full_offset(a->vd), + offsetof(CPULoongArchState, + fpr[a->vj].vreg.B((a->imm))), + 16, ctx->vl/8); + return true; +} + +static bool trans_vreplvei_h(DisasContext *ctx, arg_vv_i *a) +{ + if (!avail_LSX(ctx)) { + return false; + } + + if (!check_vec(ctx, 16)) { + return true; + } + + tcg_gen_gvec_dup_mem(MO_16, vec_full_offset(a->vd), + offsetof(CPULoongArchState, + fpr[a->vj].vreg.H((a->imm))), + 16, ctx->vl/8); + return true; +} +static bool trans_vreplvei_w(DisasContext *ctx, arg_vv_i *a) +{ + if (!avail_LSX(ctx)) { + return false; + } + + if (!check_vec(ctx, 16)) { + return true; + } + + tcg_gen_gvec_dup_mem(MO_32, vec_full_offset(a->vd), + offsetof(CPULoongArchState, + fpr[a->vj].vreg.W((a->imm))), + 16, ctx->vl/8); + return true; +} +static bool trans_vreplvei_d(DisasContext *ctx, arg_vv_i *a) +{ + if (!avail_LSX(ctx)) { + return false; + } + + if (!check_vec(ctx, 16)) { + return true; + } + + tcg_gen_gvec_dup_mem(MO_64, vec_full_offset(a->vd), + offsetof(CPULoongArchState, + fpr[a->vj].vreg.D((a->imm))), + 16, ctx->vl/8); + return true; +} + +static bool gen_vreplve_vl(DisasContext *ctx, arg_vvr *a, + uint32_t oprsz, int vece, int bit, + void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) +{ + int i; + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_ptr t1 = tcg_temp_new_ptr(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + if (!check_vec(ctx, oprsz)) { + return true; + } + + tcg_gen_andi_i64(t0, gpr_src(ctx, a->rk, EXT_NONE), (LSX_LEN / bit) - 1); + tcg_gen_shli_i64(t0, t0, vece); + if (HOST_BIG_ENDIAN) { + tcg_gen_xori_i64(t0, t0, vece << ((LSX_LEN / bit) - 1)); + } + + tcg_gen_trunc_i64_ptr(t1, t0); + tcg_gen_add_ptr(t1, t1, tcg_env); + + for (i = 0; i < oprsz; i += 16) { + func(t2, t1, vec_full_offset(a->vj) + i); + tcg_gen_gvec_dup_i64(vece, vec_full_offset(a->vd) + i, 16, 16, t2); + } + + return true; +} + +static bool gen_vreplve(DisasContext *ctx, arg_vvr *a, int vece, int bit, + void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) +{ + return gen_vreplve_vl(ctx, a, 16, vece, bit, func); +} + +static bool gen_xvreplve(DisasContext *ctx, arg_vvr *a, int vece, int bit, + void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) +{ + return gen_vreplve_vl(ctx, a, 32, vece, bit, func); +} + +TRANS(vreplve_b, LSX, gen_vreplve, MO_8, 8, tcg_gen_ld8u_i64) +TRANS(vreplve_h, LSX, gen_vreplve, MO_16, 16, tcg_gen_ld16u_i64) +TRANS(vreplve_w, LSX, gen_vreplve, MO_32, 32, tcg_gen_ld32u_i64) +TRANS(vreplve_d, LSX, gen_vreplve, MO_64, 64, tcg_gen_ld_i64) +TRANS(xvreplve_b, LASX, gen_xvreplve, MO_8, 8, tcg_gen_ld8u_i64) +TRANS(xvreplve_h, LASX, gen_xvreplve, MO_16, 16, tcg_gen_ld16u_i64) +TRANS(xvreplve_w, LASX, gen_xvreplve, MO_32, 32, tcg_gen_ld32u_i64) +TRANS(xvreplve_d, LASX, gen_xvreplve, MO_64, 64, tcg_gen_ld_i64) + +static bool gen_xvrepl128(DisasContext *ctx, arg_vv_i *a, MemOp mop) +{ + int i; + + if (!check_vec(ctx, 32)) { + return true; + } + + for (i = 0; i < 32; i += 16) { + tcg_gen_gvec_dup_mem(mop, vec_full_offset(a->vd) + i, + vec_reg_offset(a->vj, a->imm, mop) + i, 16, 16); + + } + return true; +} + +TRANS(xvrepl128vei_b, LASX, gen_xvrepl128, MO_8) +TRANS(xvrepl128vei_h, LASX, gen_xvrepl128, MO_16) +TRANS(xvrepl128vei_w, LASX, gen_xvrepl128, MO_32) +TRANS(xvrepl128vei_d, LASX, gen_xvrepl128, MO_64) + +static bool gen_xvreplve0(DisasContext *ctx, arg_vv *a, MemOp mop) +{ + if (!check_vec(ctx, 32)) { + return true; + } + + tcg_gen_gvec_dup_mem(mop, vec_full_offset(a->vd), + vec_full_offset(a->vj), 32, 32); + return true; +} + +TRANS(xvreplve0_b, LASX, gen_xvreplve0, MO_8) +TRANS(xvreplve0_h, LASX, gen_xvreplve0, MO_16) +TRANS(xvreplve0_w, LASX, gen_xvreplve0, MO_32) +TRANS(xvreplve0_d, LASX, gen_xvreplve0, MO_64) +TRANS(xvreplve0_q, LASX, gen_xvreplve0, MO_128) + +TRANS(xvinsve0_w, LASX, gen_xx_i, gen_helper_xvinsve0_w) +TRANS(xvinsve0_d, LASX, gen_xx_i, gen_helper_xvinsve0_d) + +TRANS(xvpickve_w, LASX, gen_xx_i, gen_helper_xvpickve_w) +TRANS(xvpickve_d, LASX, gen_xx_i, gen_helper_xvpickve_d) + +static bool do_vbsll_v(DisasContext *ctx, arg_vv_i *a, uint32_t oprsz) +{ + int i, ofs; + + if (!check_vec(ctx, oprsz)) { + return true; + } + + for (i = 0; i < oprsz / 16; i++) { + TCGv desthigh = tcg_temp_new_i64(); + TCGv destlow = tcg_temp_new_i64(); + TCGv high = tcg_temp_new_i64(); + TCGv low = tcg_temp_new_i64(); + + get_vreg64(low, a->vj, 2 * i); + + ofs = ((a->imm) & 0xf) * 8; + if (ofs < 64) { + get_vreg64(high, a->vj, 2 * i + 1); + tcg_gen_extract2_i64(desthigh, low, high, 64 - ofs); + tcg_gen_shli_i64(destlow, low, ofs); + } else { + tcg_gen_shli_i64(desthigh, low, ofs - 64); + destlow = tcg_constant_i64(0); + } + set_vreg64(desthigh, a->vd, 2 * i + 1); + set_vreg64(destlow, a->vd, 2 * i); + } + + return true; +} + +static bool do_vbsrl_v(DisasContext *ctx, arg_vv_i *a, uint32_t oprsz) +{ + int i, ofs; + + if (!check_vec(ctx, 32)) { + return true; + } + + for (i = 0; i < oprsz / 16; i++) { + TCGv desthigh = tcg_temp_new_i64(); + TCGv destlow = tcg_temp_new_i64(); + TCGv high = tcg_temp_new_i64(); + TCGv low = tcg_temp_new_i64(); + get_vreg64(high, a->vj, 2 * i + 1); + + ofs = ((a->imm) & 0xf) * 8; + if (ofs < 64) { + get_vreg64(low, a->vj, 2 * i); + tcg_gen_extract2_i64(destlow, low, high, ofs); + tcg_gen_shri_i64(desthigh, high, ofs); + } else { + tcg_gen_shri_i64(destlow, high, ofs - 64); + desthigh = tcg_constant_i64(0); + } + set_vreg64(desthigh, a->vd, 2 * i + 1); + set_vreg64(destlow, a->vd, 2 * i); + } + + return true; +} + +TRANS(vbsll_v, LSX, do_vbsll_v, 16) +TRANS(vbsrl_v, LSX, do_vbsrl_v, 16) +TRANS(xvbsll_v, LASX, do_vbsll_v, 32) +TRANS(xvbsrl_v, LASX, do_vbsrl_v, 32) + +TRANS(vpackev_b, LSX, gen_vvv, gen_helper_vpackev_b) +TRANS(vpackev_h, LSX, gen_vvv, gen_helper_vpackev_h) +TRANS(vpackev_w, LSX, gen_vvv, gen_helper_vpackev_w) +TRANS(vpackev_d, LSX, gen_vvv, gen_helper_vpackev_d) +TRANS(vpackod_b, LSX, gen_vvv, gen_helper_vpackod_b) +TRANS(vpackod_h, LSX, gen_vvv, gen_helper_vpackod_h) +TRANS(vpackod_w, LSX, gen_vvv, gen_helper_vpackod_w) +TRANS(vpackod_d, LSX, gen_vvv, gen_helper_vpackod_d) +TRANS(xvpackev_b, LASX, gen_xxx, gen_helper_vpackev_b) +TRANS(xvpackev_h, LASX, gen_xxx, gen_helper_vpackev_h) +TRANS(xvpackev_w, LASX, gen_xxx, gen_helper_vpackev_w) +TRANS(xvpackev_d, LASX, gen_xxx, gen_helper_vpackev_d) +TRANS(xvpackod_b, LASX, gen_xxx, gen_helper_vpackod_b) +TRANS(xvpackod_h, LASX, gen_xxx, gen_helper_vpackod_h) +TRANS(xvpackod_w, LASX, gen_xxx, gen_helper_vpackod_w) +TRANS(xvpackod_d, LASX, gen_xxx, gen_helper_vpackod_d) + +TRANS(vpickev_b, LSX, gen_vvv, gen_helper_vpickev_b) +TRANS(vpickev_h, LSX, gen_vvv, gen_helper_vpickev_h) +TRANS(vpickev_w, LSX, gen_vvv, gen_helper_vpickev_w) +TRANS(vpickev_d, LSX, gen_vvv, gen_helper_vpickev_d) +TRANS(vpickod_b, LSX, gen_vvv, gen_helper_vpickod_b) +TRANS(vpickod_h, LSX, gen_vvv, gen_helper_vpickod_h) +TRANS(vpickod_w, LSX, gen_vvv, gen_helper_vpickod_w) +TRANS(vpickod_d, LSX, gen_vvv, gen_helper_vpickod_d) +TRANS(xvpickev_b, LASX, gen_xxx, gen_helper_vpickev_b) +TRANS(xvpickev_h, LASX, gen_xxx, gen_helper_vpickev_h) +TRANS(xvpickev_w, LASX, gen_xxx, gen_helper_vpickev_w) +TRANS(xvpickev_d, LASX, gen_xxx, gen_helper_vpickev_d) +TRANS(xvpickod_b, LASX, gen_xxx, gen_helper_vpickod_b) +TRANS(xvpickod_h, LASX, gen_xxx, gen_helper_vpickod_h) +TRANS(xvpickod_w, LASX, gen_xxx, gen_helper_vpickod_w) +TRANS(xvpickod_d, LASX, gen_xxx, gen_helper_vpickod_d) + +TRANS(vilvl_b, LSX, gen_vvv, gen_helper_vilvl_b) +TRANS(vilvl_h, LSX, gen_vvv, gen_helper_vilvl_h) +TRANS(vilvl_w, LSX, gen_vvv, gen_helper_vilvl_w) +TRANS(vilvl_d, LSX, gen_vvv, gen_helper_vilvl_d) +TRANS(vilvh_b, LSX, gen_vvv, gen_helper_vilvh_b) +TRANS(vilvh_h, LSX, gen_vvv, gen_helper_vilvh_h) +TRANS(vilvh_w, LSX, gen_vvv, gen_helper_vilvh_w) +TRANS(vilvh_d, LSX, gen_vvv, gen_helper_vilvh_d) +TRANS(xvilvl_b, LASX, gen_xxx, gen_helper_vilvl_b) +TRANS(xvilvl_h, LASX, gen_xxx, gen_helper_vilvl_h) +TRANS(xvilvl_w, LASX, gen_xxx, gen_helper_vilvl_w) +TRANS(xvilvl_d, LASX, gen_xxx, gen_helper_vilvl_d) +TRANS(xvilvh_b, LASX, gen_xxx, gen_helper_vilvh_b) +TRANS(xvilvh_h, LASX, gen_xxx, gen_helper_vilvh_h) +TRANS(xvilvh_w, LASX, gen_xxx, gen_helper_vilvh_w) +TRANS(xvilvh_d, LASX, gen_xxx, gen_helper_vilvh_d) + +TRANS(vshuf_b, LSX, gen_vvvv, gen_helper_vshuf_b) +TRANS(vshuf_h, LSX, gen_vvv, gen_helper_vshuf_h) +TRANS(vshuf_w, LSX, gen_vvv, gen_helper_vshuf_w) +TRANS(vshuf_d, LSX, gen_vvv, gen_helper_vshuf_d) +TRANS(xvshuf_b, LASX, gen_xxxx, gen_helper_vshuf_b) +TRANS(xvshuf_h, LASX, gen_xxx, gen_helper_vshuf_h) +TRANS(xvshuf_w, LASX, gen_xxx, gen_helper_vshuf_w) +TRANS(xvshuf_d, LASX, gen_xxx, gen_helper_vshuf_d) +TRANS(vshuf4i_b, LSX, gen_vv_i, gen_helper_vshuf4i_b) +TRANS(vshuf4i_h, LSX, gen_vv_i, gen_helper_vshuf4i_h) +TRANS(vshuf4i_w, LSX, gen_vv_i, gen_helper_vshuf4i_w) +TRANS(vshuf4i_d, LSX, gen_vv_i, gen_helper_vshuf4i_d) +TRANS(xvshuf4i_b, LASX, gen_xx_i, gen_helper_vshuf4i_b) +TRANS(xvshuf4i_h, LASX, gen_xx_i, gen_helper_vshuf4i_h) +TRANS(xvshuf4i_w, LASX, gen_xx_i, gen_helper_vshuf4i_w) +TRANS(xvshuf4i_d, LASX, gen_xx_i, gen_helper_vshuf4i_d) + +TRANS(xvperm_w, LASX, gen_xxx, gen_helper_vperm_w) +TRANS(vpermi_w, LSX, gen_vv_i, gen_helper_vpermi_w) +TRANS(xvpermi_w, LASX, gen_xx_i, gen_helper_vpermi_w) +TRANS(xvpermi_d, LASX, gen_xx_i, gen_helper_vpermi_d) +TRANS(xvpermi_q, LASX, gen_xx_i, gen_helper_vpermi_q) + +TRANS(vextrins_b, LSX, gen_vv_i, gen_helper_vextrins_b) +TRANS(vextrins_h, LSX, gen_vv_i, gen_helper_vextrins_h) +TRANS(vextrins_w, LSX, gen_vv_i, gen_helper_vextrins_w) +TRANS(vextrins_d, LSX, gen_vv_i, gen_helper_vextrins_d) +TRANS(xvextrins_b, LASX, gen_xx_i, gen_helper_vextrins_b) +TRANS(xvextrins_h, LASX, gen_xx_i, gen_helper_vextrins_h) +TRANS(xvextrins_w, LASX, gen_xx_i, gen_helper_vextrins_w) +TRANS(xvextrins_d, LASX, gen_xx_i, gen_helper_vextrins_d) + +static bool trans_vld(DisasContext *ctx, arg_vr_i *a) +{ + TCGv addr; + TCGv_i64 rl, rh; + TCGv_i128 val; + + if (!avail_LSX(ctx)) { + return false; + } + + if (!check_vec(ctx, 16)) { + return true; + } + + addr = gpr_src(ctx, a->rj, EXT_NONE); + val = tcg_temp_new_i128(); + rl = tcg_temp_new_i64(); + rh = tcg_temp_new_i64(); + + addr = make_address_i(ctx, addr, a->imm); + + tcg_gen_qemu_ld_i128(val, addr, ctx->mem_idx, MO_128 | MO_TE); + tcg_gen_extr_i128_i64(rl, rh, val); + set_vreg64(rh, a->vd, 1); + set_vreg64(rl, a->vd, 0); + + return true; +} + +static bool trans_vst(DisasContext *ctx, arg_vr_i *a) +{ + TCGv addr; + TCGv_i128 val; + TCGv_i64 ah, al; + + if (!avail_LSX(ctx)) { + return false; + } + + if (!check_vec(ctx, 16)) { + return true; + } + + addr = gpr_src(ctx, a->rj, EXT_NONE); + val = tcg_temp_new_i128(); + ah = tcg_temp_new_i64(); + al = tcg_temp_new_i64(); + + addr = make_address_i(ctx, addr, a->imm); + + get_vreg64(ah, a->vd, 1); + get_vreg64(al, a->vd, 0); + tcg_gen_concat_i64_i128(val, al, ah); + tcg_gen_qemu_st_i128(val, addr, ctx->mem_idx, MO_128 | MO_TE); + + return true; +} + +static bool trans_vldx(DisasContext *ctx, arg_vrr *a) +{ + TCGv addr, src1, src2; + TCGv_i64 rl, rh; + TCGv_i128 val; + + if (!avail_LSX(ctx)) { + return false; + } + + if (!check_vec(ctx, 16)) { + return true; + } + + src1 = gpr_src(ctx, a->rj, EXT_NONE); + src2 = gpr_src(ctx, a->rk, EXT_NONE); + val = tcg_temp_new_i128(); + rl = tcg_temp_new_i64(); + rh = tcg_temp_new_i64(); + + addr = make_address_x(ctx, src1, src2); + tcg_gen_qemu_ld_i128(val, addr, ctx->mem_idx, MO_128 | MO_TE); + tcg_gen_extr_i128_i64(rl, rh, val); + set_vreg64(rh, a->vd, 1); + set_vreg64(rl, a->vd, 0); + + return true; +} + +static bool trans_vstx(DisasContext *ctx, arg_vrr *a) +{ + TCGv addr, src1, src2; + TCGv_i64 ah, al; + TCGv_i128 val; + + if (!avail_LSX(ctx)) { + return false; + } + + if (!check_vec(ctx, 16)) { + return true; + } + + src1 = gpr_src(ctx, a->rj, EXT_NONE); + src2 = gpr_src(ctx, a->rk, EXT_NONE); + val = tcg_temp_new_i128(); + ah = tcg_temp_new_i64(); + al = tcg_temp_new_i64(); + + addr = make_address_x(ctx, src1, src2); + get_vreg64(ah, a->vd, 1); + get_vreg64(al, a->vd, 0); + tcg_gen_concat_i64_i128(val, al, ah); + tcg_gen_qemu_st_i128(val, addr, ctx->mem_idx, MO_128 | MO_TE); + + return true; +} + +static bool do_vldrepl_vl(DisasContext *ctx, arg_vr_i *a, + uint32_t oprsz, MemOp mop) +{ + TCGv addr; + TCGv_i64 val; + + if (!check_vec(ctx, oprsz)) { + return true; + } + + addr = gpr_src(ctx, a->rj, EXT_NONE); + val = tcg_temp_new_i64(); + + addr = make_address_i(ctx, addr, a->imm); + + tcg_gen_qemu_ld_i64(val, addr, ctx->mem_idx, mop); + tcg_gen_gvec_dup_i64(mop, vec_full_offset(a->vd), oprsz, ctx->vl / 8, val); + + return true; +} + +static bool do_vldrepl(DisasContext *ctx, arg_vr_i *a, MemOp mop) +{ + return do_vldrepl_vl(ctx, a, 16, mop); +} + +static bool do_xvldrepl(DisasContext *ctx, arg_vr_i *a, MemOp mop) +{ + return do_vldrepl_vl(ctx, a, 32, mop); +} + +TRANS(vldrepl_b, LSX, do_vldrepl, MO_8) +TRANS(vldrepl_h, LSX, do_vldrepl, MO_16) +TRANS(vldrepl_w, LSX, do_vldrepl, MO_32) +TRANS(vldrepl_d, LSX, do_vldrepl, MO_64) +TRANS(xvldrepl_b, LASX, do_xvldrepl, MO_8) +TRANS(xvldrepl_h, LASX, do_xvldrepl, MO_16) +TRANS(xvldrepl_w, LASX, do_xvldrepl, MO_32) +TRANS(xvldrepl_d, LASX, do_xvldrepl, MO_64) + +static bool do_vstelm_vl(DisasContext *ctx, + arg_vr_ii *a, uint32_t oprsz, MemOp mop) +{ + TCGv addr; + TCGv_i64 val; + + if (!check_vec(ctx, oprsz)) { + return true; + } + + addr = gpr_src(ctx, a->rj, EXT_NONE); + val = tcg_temp_new_i64(); + + addr = make_address_i(ctx, addr, a->imm); + tcg_gen_ld_i64(val, tcg_env, vec_reg_offset(a->vd, a->imm2, mop)); + tcg_gen_qemu_st_i64(val, addr, ctx->mem_idx, mop); + return true; +} + +static bool do_vstelm(DisasContext *ctx, arg_vr_ii *a, MemOp mop) +{ + return do_vstelm_vl(ctx, a, 16, mop); +} + +static bool do_xvstelm(DisasContext *ctx, arg_vr_ii *a, MemOp mop) +{ + return do_vstelm_vl(ctx, a, 32, mop); +} + +TRANS(vstelm_b, LSX, do_vstelm, MO_8) +TRANS(vstelm_h, LSX, do_vstelm, MO_16) +TRANS(vstelm_w, LSX, do_vstelm, MO_32) +TRANS(vstelm_d, LSX, do_vstelm, MO_64) +TRANS(xvstelm_b, LASX, do_xvstelm, MO_8) +TRANS(xvstelm_h, LASX, do_xvstelm, MO_16) +TRANS(xvstelm_w, LASX, do_xvstelm, MO_32) +TRANS(xvstelm_d, LASX, do_xvstelm, MO_64) + +static bool gen_lasx_memory(DisasContext *ctx, arg_vr_i *a, + void (*func)(DisasContext *, int, TCGv)) +{ + TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); + TCGv temp = NULL; + + if (!check_vec(ctx, 32)) { + return true; + } + + if (a->imm) { + temp = tcg_temp_new(); + tcg_gen_addi_tl(temp, addr, a->imm); + addr = temp; + } + + func(ctx, a->vd, addr); + return true; +} + +static void gen_xvld(DisasContext *ctx, int vreg, TCGv addr) +{ + int i; + TCGv temp = tcg_temp_new(); + TCGv dest = tcg_temp_new(); + + tcg_gen_qemu_ld_i64(dest, addr, ctx->mem_idx, MO_TEUQ); + set_vreg64(dest, vreg, 0); + + for (i = 1; i < 4; i++) { + tcg_gen_addi_tl(temp, addr, 8 * i); + tcg_gen_qemu_ld_i64(dest, temp, ctx->mem_idx, MO_TEUQ); + set_vreg64(dest, vreg, i); + } +} + +static void gen_xvst(DisasContext * ctx, int vreg, TCGv addr) +{ + int i; + TCGv temp = tcg_temp_new(); + TCGv dest = tcg_temp_new(); + + get_vreg64(dest, vreg, 0); + tcg_gen_qemu_st_i64(dest, addr, ctx->mem_idx, MO_TEUQ); + + for (i = 1; i < 4; i++) { + tcg_gen_addi_tl(temp, addr, 8 * i); + get_vreg64(dest, vreg, i); + tcg_gen_qemu_st_i64(dest, temp, ctx->mem_idx, MO_TEUQ); + } +} + +TRANS(xvld, LASX, gen_lasx_memory, gen_xvld) +TRANS(xvst, LASX, gen_lasx_memory, gen_xvst) + +static bool gen_lasx_memoryx(DisasContext *ctx, arg_vrr *a, + void (*func)(DisasContext*, int, TCGv)) +{ + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); + TCGv addr = tcg_temp_new(); + + if (!check_vec(ctx, 32)) { + return true; + } + + tcg_gen_add_tl(addr, src1, src2); + func(ctx, a->vd, addr); + + return true; +} + +TRANS(xvldx, LASX, gen_lasx_memoryx, gen_xvld) +TRANS(xvstx, LASX, gen_lasx_memoryx, gen_xvst) diff --git a/target/loongarch/iocsr_helper.c b/target/loongarch/tcg/iocsr_helper.c similarity index 50% rename from target/loongarch/iocsr_helper.c rename to target/loongarch/tcg/iocsr_helper.c index 0e9c537dc7..b6916f53d2 100644 --- a/target/loongarch/iocsr_helper.c +++ b/target/loongarch/tcg/iocsr_helper.c @@ -6,62 +6,63 @@ */ #include "qemu/osdep.h" -#include "qemu/main-loop.h" #include "cpu.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" -#include "tcg/tcg-ldst.h" + +#define GET_MEMTXATTRS(cas) \ + ((MemTxAttrs){.requester_id = env_cpu(cas)->cpu_index}) uint64_t helper_iocsrrd_b(CPULoongArchState *env, target_ulong r_addr) { - return address_space_ldub(&env->address_space_iocsr, r_addr, - MEMTXATTRS_UNSPECIFIED, NULL); + return address_space_ldub(env->address_space_iocsr, r_addr, + GET_MEMTXATTRS(env), NULL); } uint64_t helper_iocsrrd_h(CPULoongArchState *env, target_ulong r_addr) { - return address_space_lduw(&env->address_space_iocsr, r_addr, - MEMTXATTRS_UNSPECIFIED, NULL); + return address_space_lduw(env->address_space_iocsr, r_addr, + GET_MEMTXATTRS(env), NULL); } uint64_t helper_iocsrrd_w(CPULoongArchState *env, target_ulong r_addr) { - return address_space_ldl(&env->address_space_iocsr, r_addr, - MEMTXATTRS_UNSPECIFIED, NULL); + return address_space_ldl(env->address_space_iocsr, r_addr, + GET_MEMTXATTRS(env), NULL); } uint64_t helper_iocsrrd_d(CPULoongArchState *env, target_ulong r_addr) { - return address_space_ldq(&env->address_space_iocsr, r_addr, - MEMTXATTRS_UNSPECIFIED, NULL); + return address_space_ldq(env->address_space_iocsr, r_addr, + GET_MEMTXATTRS(env), NULL); } void helper_iocsrwr_b(CPULoongArchState *env, target_ulong w_addr, target_ulong val) { - address_space_stb(&env->address_space_iocsr, w_addr, - val, MEMTXATTRS_UNSPECIFIED, NULL); + address_space_stb(env->address_space_iocsr, w_addr, + val, GET_MEMTXATTRS(env), NULL); } void helper_iocsrwr_h(CPULoongArchState *env, target_ulong w_addr, target_ulong val) { - address_space_stw(&env->address_space_iocsr, w_addr, - val, MEMTXATTRS_UNSPECIFIED, NULL); + address_space_stw(env->address_space_iocsr, w_addr, + val, GET_MEMTXATTRS(env), NULL); } void helper_iocsrwr_w(CPULoongArchState *env, target_ulong w_addr, target_ulong val) { - address_space_stl(&env->address_space_iocsr, w_addr, - val, MEMTXATTRS_UNSPECIFIED, NULL); + address_space_stl(env->address_space_iocsr, w_addr, + val, GET_MEMTXATTRS(env), NULL); } void helper_iocsrwr_d(CPULoongArchState *env, target_ulong w_addr, target_ulong val) { - address_space_stq(&env->address_space_iocsr, w_addr, - val, MEMTXATTRS_UNSPECIFIED, NULL); + address_space_stq(env->address_space_iocsr, w_addr, + val, GET_MEMTXATTRS(env), NULL); } diff --git a/target/loongarch/tcg/meson.build b/target/loongarch/tcg/meson.build new file mode 100644 index 0000000000..bdf34f9673 --- /dev/null +++ b/target/loongarch/tcg/meson.build @@ -0,0 +1,19 @@ +if 'CONFIG_TCG' not in config_all_accel + subdir_done() +endif + +loongarch_ss.add([zlib, gen]) + +loongarch_ss.add(files( + 'fpu_helper.c', + 'op_helper.c', + 'translate.c', + 'vec_helper.c', +)) + +loongarch_system_ss.add(files( + 'constant_timer.c', + 'csr_helper.c', + 'iocsr_helper.c', + 'tlb_helper.c', +)) diff --git a/target/loongarch/op_helper.c b/target/loongarch/tcg/op_helper.c similarity index 89% rename from target/loongarch/op_helper.c rename to target/loongarch/tcg/op_helper.c index d87049851f..fe79c62fa4 100644 --- a/target/loongarch/op_helper.c +++ b/target/loongarch/tcg/op_helper.c @@ -7,7 +7,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "qemu/main-loop.h" #include "cpu.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" @@ -49,14 +48,16 @@ target_ulong helper_bitswap(target_ulong v) void helper_asrtle_d(CPULoongArchState *env, target_ulong rj, target_ulong rk) { if (rj > rk) { - do_raise_exception(env, EXCCODE_ADEM, GETPC()); + env->CSR_BADV = rj; + do_raise_exception(env, EXCCODE_BCE, GETPC()); } } void helper_asrtgt_d(CPULoongArchState *env, target_ulong rj, target_ulong rk) { if (rj <= rk) { - do_raise_exception(env, EXCCODE_ADEM, GETPC()); + env->CSR_BADV = rj; + do_raise_exception(env, EXCCODE_BCE, GETPC()); } } @@ -81,11 +82,14 @@ target_ulong helper_crc32c(target_ulong val, target_ulong m, uint64_t sz) target_ulong helper_cpucfg(CPULoongArchState *env, target_ulong rj) { - return rj > 21 ? 0 : env->cpucfg[rj]; + return rj >= ARRAY_SIZE(env->cpucfg) ? 0 : env->cpucfg[rj]; } uint64_t helper_rdtime_d(CPULoongArchState *env) { +#ifdef CONFIG_USER_ONLY + return cpu_get_host_ticks(); +#else uint64_t plv; LoongArchCPU *cpu = env_archcpu(env); @@ -95,8 +99,10 @@ uint64_t helper_rdtime_d(CPULoongArchState *env) } return cpu_loongarch_get_constant_timer_counter(cpu); +#endif } +#ifndef CONFIG_USER_ONLY void helper_ertn(CPULoongArchState *env) { uint64_t csr_pplv, csr_pie; @@ -107,14 +113,14 @@ void helper_ertn(CPULoongArchState *env) env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0); env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 0); env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 1); - env->pc = env->CSR_TLBRERA; + set_pc(env, env->CSR_TLBRERA); qemu_log_mask(CPU_LOG_INT, "%s: TLBRERA " TARGET_FMT_lx "\n", __func__, env->CSR_TLBRERA); } else { csr_pplv = FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PPLV); csr_pie = FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PIE); - env->pc = env->CSR_ERA; + set_pc(env, env->CSR_ERA); qemu_log_mask(CPU_LOG_INT, "%s: ERA " TARGET_FMT_lx "\n", __func__, env->CSR_ERA); } @@ -131,3 +137,4 @@ void helper_idle(CPULoongArchState *env) cs->halted = 1; do_raise_exception(env, EXCP_HLT, 0); } +#endif diff --git a/target/loongarch/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c similarity index 66% rename from target/loongarch/tlb_helper.c rename to target/loongarch/tcg/tlb_helper.c index bab19c7e05..57f5308632 100644 --- a/target/loongarch/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -17,208 +17,32 @@ #include "exec/log.h" #include "cpu-csr.h" -enum { - TLBRET_MATCH = 0, - TLBRET_BADADDR = 1, - TLBRET_NOMATCH = 2, - TLBRET_INVALID = 3, - TLBRET_DIRTY = 4, - TLBRET_RI = 5, - TLBRET_XI = 6, - TLBRET_PE = 7, -}; - -static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - int access_type, int index, int mmu_idx) +static void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, + uint64_t *dir_width, target_ulong level) { - LoongArchTLB *tlb = &env->tlb[index]; - uint64_t plv = mmu_idx; - uint64_t tlb_entry, tlb_ppn; - uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; - - if (index >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + switch (level) { + case 1: + *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE); + *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH); + break; + case 2: + *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE); + *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH); + break; + case 3: + *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE); + *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH); + break; + case 4: + *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE); + *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH); + break; + default: + /* level may be zero for ldpte */ + *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE); + *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH); + break; } - n = (address >> tlb_ps) & 0x1;/* Odd or even */ - - tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0; - tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); - tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); - tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY, PPN); - tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY, NX); - tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY, NR); - tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY, RPLV); - - /* Check access rights */ - if (!tlb_v) { - return TLBRET_INVALID; - } - - if (access_type == MMU_INST_FETCH && tlb_nx) { - return TLBRET_XI; - } - - if (access_type == MMU_DATA_LOAD && tlb_nr) { - return TLBRET_RI; - } - - if (((tlb_rplv == 0) && (plv > tlb_plv)) || - ((tlb_rplv == 1) && (plv != tlb_plv))) { - return TLBRET_PE; - } - - if ((access_type == MMU_DATA_STORE) && !tlb_d) { - return TLBRET_DIRTY; - } - - /* - * tlb_entry contains ppn[47:12] while 16KiB ppn is [47:15] - * need adjust. - */ - *physical = (tlb_ppn << R_TLBENTRY_PPN_SHIFT) | - (address & MAKE_64BIT_MASK(0, tlb_ps)); - *prot = PAGE_READ; - if (tlb_d) { - *prot |= PAGE_WRITE; - } - if (!tlb_nx) { - *prot |= PAGE_EXEC; - } - return TLBRET_MATCH; -} - -/* - * One tlb entry holds an adjacent odd/even pair, the vpn is the - * content of the virtual page number divided by 2. So the - * compare vpn is bit[47:15] for 16KiB page. while the vppn - * field in tlb entry contains bit[47:13], so need adjust. - * virt_vpn = vaddr[47:13] - */ -static bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, - int *index) -{ - LoongArchTLB *tlb; - uint16_t csr_asid, tlb_asid, stlb_idx; - uint8_t tlb_e, tlb_ps, tlb_g, stlb_ps; - int i, compare_shift; - uint64_t vpn, tlb_vppn; - - csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); - stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1); - stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */ - compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; - - /* Search STLB */ - for (i = 0; i < 8; ++i) { - tlb = &env->tlb[i * 256 + stlb_idx]; - tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); - if (tlb_e) { - tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); - tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - - if ((tlb_g == 1 || tlb_asid == csr_asid) && - (vpn == (tlb_vppn >> compare_shift))) { - *index = i * 256 + stlb_idx; - return true; - } - } - } - - /* Search MTLB */ - for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) { - tlb = &env->tlb[i]; - tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); - if (tlb_e) { - tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; - vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1); - if ((tlb_g == 1 || tlb_asid == csr_asid) && - (vpn == (tlb_vppn >> compare_shift))) { - *index = i; - return true; - } - } - } - return false; -} - -static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - int index, match; - - match = loongarch_tlb_search(env, address, &index); - if (match) { - return loongarch_map_tlb_entry(env, physical, prot, - address, access_type, index, mmu_idx); - } - - return TLBRET_NOMATCH; -} - -static int get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - int user_mode = mmu_idx == MMU_USER_IDX; - int kernel_mode = mmu_idx == MMU_KERNEL_IDX; - uint32_t plv, base_c, base_v; - int64_t addr_high; - uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA); - uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG); - - /* Check PG and DA */ - if (da & !pg) { - *physical = address & TARGET_PHYS_MASK; - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return TLBRET_MATCH; - } - - plv = kernel_mode | (user_mode << R_CSR_DMW_PLV3_SHIFT); - base_v = address >> TARGET_VIRT_ADDR_SPACE_BITS; - /* Check direct map window */ - for (int i = 0; i < 4; i++) { - base_c = env->CSR_DMW[i] >> TARGET_VIRT_ADDR_SPACE_BITS; - if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) { - *physical = dmw_va2pa(address); - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return TLBRET_MATCH; - } - } - - /* Check valid extension */ - addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16); - if (!(addr_high == 0 || addr_high == -1)) { - return TLBRET_BADADDR; - } - - /* Mapped address */ - return loongarch_map_address(env, physical, prot, address, - access_type, mmu_idx); -} - -hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; - hwaddr phys_addr; - int prot; - - if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, - cpu_mmu_index(env, false)) != 0) { - return -1; - } - return phys_addr; } static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, @@ -229,7 +53,8 @@ static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, switch (tlb_error) { default: case TLBRET_BADADDR: - cs->exception_index = EXCCODE_ADEM; + cs->exception_index = access_type == MMU_INST_FETCH + ? EXCCODE_ADEF : EXCCODE_ADEM; break; case TLBRET_NOMATCH: /* No TLB match for a mapped address */ @@ -272,8 +97,13 @@ static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, if (tlb_error == TLBRET_NOMATCH) { env->CSR_TLBRBADV = address; - env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, VPPN, - extract64(address, 13, 35)); + if (is_la64(env)) { + env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI_64, + VPPN, extract64(address, 13, 35)); + } else { + env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI_32, + VPPN, extract64(address, 13, 19)); + } } else { if (!FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) { env->CSR_BADV = address; @@ -288,7 +118,7 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index) uint8_t tlb_ps; LoongArchTLB *tlb = &env->tlb[index]; - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = cpu_mmu_index(env_cpu(env), false); uint8_t tlb_v0 = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, V); uint8_t tlb_v1 = FIELD_EX64(tlb->tlb_entry1, TLBENTRY, V); uint64_t tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); @@ -298,7 +128,7 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index) } else { tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); } - pagesize = 1 << tlb_ps; + pagesize = MAKE_64BIT_MASK(tlb_ps, 1); mask = MAKE_64BIT_MASK(0, tlb_ps + 1); if (tlb_v0) { @@ -338,12 +168,20 @@ static void fill_tlb_entry(CPULoongArchState *env, int index) if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { csr_ps = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS); - csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, VPPN); + if (is_la64(env)) { + csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI_64, VPPN); + } else { + csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI_32, VPPN); + } lo0 = env->CSR_TLBRELO0; lo1 = env->CSR_TLBRELO1; } else { csr_ps = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS); - csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI, VPPN); + if (is_la64(env)) { + csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI_64, VPPN); + } else { + csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI_32, VPPN); + } lo0 = env->CSR_TLBELO0; lo1 = env->CSR_TLBELO1; } @@ -463,7 +301,7 @@ void helper_tlbfill(CPULoongArchState *env) if (pagesize == stlb_ps) { /* Only write into STLB bits [47:13] */ - address = entryhi & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_VPPN_SHIFT); + address = entryhi & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_64_VPPN_SHIFT); /* Choose one set ramdomly */ set = get_random_tlb(0, 7); @@ -639,11 +477,10 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; + CPULoongArchState *env = cpu_env(cs); hwaddr physical; int prot; - int ret = TLBRET_BADADDR; + int ret; /* Data access */ ret = get_physical_address(env, &physical, &prot, address, @@ -654,7 +491,7 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, physical & TARGET_PAGE_MASK, prot, mmu_idx, TARGET_PAGE_SIZE); qemu_log_mask(CPU_LOG_MMU, - "%s address=%" VADDR_PRIx " physical " TARGET_FMT_plx + "%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx " prot %d\n", __func__, address, physical, prot); return true; } else { @@ -676,7 +513,25 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, target_ulong badvaddr, index, phys, ret; int shift; uint64_t dir_base, dir_width; - bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1; + + if (unlikely((level == 0) || (level > 4))) { + qemu_log_mask(LOG_GUEST_ERROR, + "Attepted LDDIR with level %"PRId64"\n", level); + return base; + } + + if (FIELD_EX64(base, TLBENTRY, HUGE)) { + if (unlikely(level == 4)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Attempted use of level 4 huge page\n"); + } + + if (FIELD_EX64(base, TLBENTRY, LEVEL)) { + return base; + } else { + return FIELD_DP64(base, TLBENTRY, LEVEL, level); + } + } badvaddr = env->CSR_TLBRBADV; base = base & TARGET_PHYS_MASK; @@ -685,30 +540,7 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); shift = (shift + 1) * 3; - if (huge) { - return base; - } - switch (level) { - case 1: - dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE); - dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH); - break; - case 2: - dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE); - dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH); - break; - case 3: - dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE); - dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH); - break; - case 4: - dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE); - dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH); - break; - default: - do_raise_exception(env, EXCCODE_INE, GETPC()); - return 0; - } + get_dir_base_width(env, &dir_base, &dir_width, level); index = (badvaddr >> dir_base) & ((1 << dir_width) - 1); phys = base | index << shift; ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; @@ -721,22 +553,44 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, CPUState *cs = env_cpu(env); target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv; int shift; - bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1; uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE); uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH); + uint64_t dir_base, dir_width; + /* + * The parameter "base" has only two types, + * one is the page table base address, + * whose bit 6 should be 0, + * and the other is the huge page entry, + * whose bit 6 should be 1. + */ base = base & TARGET_PHYS_MASK; + if (FIELD_EX64(base, TLBENTRY, HUGE)) { + /* + * Gets the huge page level and Gets huge page size. + * Clears the huge page level information in the entry. + * Clears huge page bit. + * Move HGLOBAL bit to GLOBAL bit. + */ + get_dir_base_width(env, &dir_base, &dir_width, + FIELD_EX64(base, TLBENTRY, LEVEL)); - if (huge) { - /* Huge Page. base is paddr */ - tmp0 = base ^ (1 << LOONGARCH_PAGE_HUGE_SHIFT); - /* Move Global bit */ - tmp0 = ((tmp0 & (1 << LOONGARCH_HGLOBAL_SHIFT)) >> - LOONGARCH_HGLOBAL_SHIFT) << R_TLBENTRY_G_SHIFT | - (tmp0 & (~(1 << R_TLBENTRY_G_SHIFT))); - ps = ptbase + ptwidth - 1; + base = FIELD_DP64(base, TLBENTRY, LEVEL, 0); + base = FIELD_DP64(base, TLBENTRY, HUGE, 0); + if (FIELD_EX64(base, TLBENTRY, HGLOBAL)) { + base = FIELD_DP64(base, TLBENTRY, HGLOBAL, 0); + base = FIELD_DP64(base, TLBENTRY, G, 1); + } + + ps = dir_base + dir_width - 1; + /* + * Huge pages are evenly split into parity pages + * when loaded into the tlb, + * so the tlb page size needs to be divided by 2. + */ + tmp0 = base; if (odd) { - tmp0 += (1 << ps); + tmp0 += MAKE_64BIT_MASK(ps, 1); } } else { /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ diff --git a/target/loongarch/translate.c b/target/loongarch/tcg/translate.c similarity index 65% rename from target/loongarch/translate.c rename to target/loongarch/tcg/translate.c index c9afd11420..7567712655 100644 --- a/target/loongarch/translate.c +++ b/target/loongarch/tcg/translate.c @@ -8,39 +8,79 @@ #include "qemu/osdep.h" #include "cpu.h" #include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" +#include "exec/translation-block.h" #include "exec/translator.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" - -#include "exec/translator.h" #include "exec/log.h" #include "qemu/qemu-print.h" #include "fpu/softfloat.h" #include "translate.h" #include "internals.h" +#include "vec.h" /* Global register indices */ TCGv cpu_gpr[32], cpu_pc; static TCGv cpu_lladdr, cpu_llval; -TCGv_i32 cpu_fcsr0; -TCGv_i64 cpu_fpr[32]; -#include "exec/gen-icount.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H #define DISAS_STOP DISAS_TARGET_0 #define DISAS_EXIT DISAS_TARGET_1 #define DISAS_EXIT_UPDATE DISAS_TARGET_2 +static inline int vec_full_offset(int regno) +{ + return offsetof(CPULoongArchState, fpr[regno]); +} + +static inline int vec_reg_offset(int regno, int index, MemOp mop) +{ + const uint8_t size = 1 << mop; + int offs = index * size; + + if (HOST_BIG_ENDIAN && size < 8 ) { + offs ^= (8 - size); + } + + return offs + vec_full_offset(regno); +} + +static inline void get_vreg64(TCGv_i64 dest, int regno, int index) +{ + tcg_gen_ld_i64(dest, tcg_env, + offsetof(CPULoongArchState, fpr[regno].vreg.D(index))); +} + +static inline void set_vreg64(TCGv_i64 src, int regno, int index) +{ + tcg_gen_st_i64(src, tcg_env, + offsetof(CPULoongArchState, fpr[regno].vreg.D(index))); +} + static inline int plus_1(DisasContext *ctx, int x) { return x + 1; } +static inline int shl_1(DisasContext *ctx, int x) +{ + return x << 1; +} + static inline int shl_2(DisasContext *ctx, int x) { return x << 2; } +static inline int shl_3(DisasContext *ctx, int x) +{ + return x << 3; +} + /* * LoongArch the upper 32 bits are undefined ("can be any value"). * QEMU chooses to nanbox, because it is most likely to show guest bugs early. @@ -53,12 +93,16 @@ static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in) void generate_exception(DisasContext *ctx, int excp) { tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); - gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp)); ctx->base.is_jmp = DISAS_NORETURN; } static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) { + if (ctx->va32) { + dest = (uint32_t) dest; + } + if (translator_use_goto_tb(&ctx->base, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(cpu_pc, dest); @@ -73,19 +117,36 @@ static void loongarch_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { int64_t bound; + CPULoongArchState *env = cpu_env(cs); DisasContext *ctx = container_of(dcbase, DisasContext, base); ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; - ctx->mem_idx = ctx->base.tb->flags; + ctx->plv = ctx->base.tb->flags & HW_FLAGS_PLV_MASK; + if (ctx->base.tb->flags & HW_FLAGS_CRMD_PG) { + ctx->mem_idx = ctx->plv; + } else { + ctx->mem_idx = MMU_DA_IDX; + } /* Bound the number of insns to execute to those left on the page. */ bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; ctx->base.max_insns = MIN(ctx->base.max_insns, bound); - ctx->ntemp = 0; - memset(ctx->temp, 0, sizeof(ctx->temp)); + if (FIELD_EX64(env->cpucfg[2], CPUCFG2, LSX)) { + ctx->vl = LSX_LEN; + } + + if (FIELD_EX64(env->cpucfg[2], CPUCFG2, LASX)) { + ctx->vl = LASX_LEN; + } + + ctx->la64 = is_la64(env); + ctx->va32 = (ctx->base.tb->flags & HW_FLAGS_VA32) != 0; ctx->zero = tcg_constant_tl(0); + + ctx->cpucfg1 = env->cpucfg[1]; + ctx->cpucfg2 = env->cpucfg[2]; } static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) @@ -107,12 +168,6 @@ static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) * * Further, we may provide an extension for word operations. */ -static TCGv temp_new(DisasContext *ctx) -{ - assert(ctx->ntemp < ARRAY_SIZE(ctx->temp)); - return ctx->temp[ctx->ntemp++] = tcg_temp_new(); -} - static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext) { TCGv t; @@ -125,11 +180,11 @@ static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext) case EXT_NONE: return cpu_gpr[reg_num]; case EXT_SIGN: - t = temp_new(ctx); + t = tcg_temp_new(); tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); return t; case EXT_ZERO: - t = temp_new(ctx); + t = tcg_temp_new(); tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); return t; } @@ -139,7 +194,7 @@ static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext) static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext) { if (reg_num == 0 || dst_ext) { - return temp_new(ctx); + return tcg_temp_new(); } return cpu_gpr[reg_num]; } @@ -163,6 +218,52 @@ static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext) } } +static TCGv get_fpr(DisasContext *ctx, int reg_num) +{ + TCGv t = tcg_temp_new(); + tcg_gen_ld_i64(t, tcg_env, + offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0))); + return t; +} + +static void set_fpr(int reg_num, TCGv val) +{ + tcg_gen_st_i64(val, tcg_env, + offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0))); +} + +static TCGv make_address_x(DisasContext *ctx, TCGv base, TCGv addend) +{ + TCGv temp = NULL; + + if (addend || ctx->va32) { + temp = tcg_temp_new(); + } + if (addend) { + tcg_gen_add_tl(temp, base, addend); + base = temp; + } + if (ctx->va32) { + tcg_gen_ext32u_tl(temp, base); + base = temp; + } + return base; +} + +static TCGv make_address_i(DisasContext *ctx, TCGv base, target_long ofs) +{ + TCGv addend = ofs ? tcg_constant_tl(ofs) : NULL; + return make_address_x(ctx, base, addend); +} + +static uint64_t make_address_pc(DisasContext *ctx, uint64_t addr) +{ + if (ctx->va32) { + addr = (int32_t)addr; + } + return addr; +} + #include "decode-insns.c.inc" #include "insn_trans/trans_arith.c.inc" #include "insn_trans/trans_shift.c.inc" @@ -177,13 +278,13 @@ static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext) #include "insn_trans/trans_fmemory.c.inc" #include "insn_trans/trans_branch.c.inc" #include "insn_trans/trans_privileged.c.inc" +#include "insn_trans/trans_vec.c.inc" static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) { - CPULoongArchState *env = cs->env_ptr; DisasContext *ctx = container_of(dcbase, DisasContext, base); - ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); + ctx->opcode = translator_ldl(cpu_env(cs), &ctx->base, ctx->base.pc_next); if (!decode(ctx, ctx->opcode)) { qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. " @@ -192,13 +293,11 @@ static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) generate_exception(ctx, EXCCODE_INE); } - for (int i = ctx->ntemp - 1; i >= 0; --i) { - tcg_temp_free(ctx->temp[i]); - ctx->temp[i] = NULL; - } - ctx->ntemp = 0; - ctx->base.pc_next += 4; + + if (ctx->va32) { + ctx->base.pc_next = (uint32_t)ctx->base.pc_next; + } } static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) @@ -242,11 +341,13 @@ static const TranslatorOps loongarch_tr_ops = { .disas_log = loongarch_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; - translator_loop(&loongarch_tr_ops, &ctx.base, cs, tb, max_insns); + translator_loop(cs, tb, max_insns, pc, host_pc, + &loongarch_tr_ops, &ctx.base); } void loongarch_translate_init(void) @@ -255,27 +356,14 @@ void loongarch_translate_init(void) cpu_gpr[0] = NULL; for (i = 1; i < 32; i++) { - cpu_gpr[i] = tcg_global_mem_new(cpu_env, + cpu_gpr[i] = tcg_global_mem_new(tcg_env, offsetof(CPULoongArchState, gpr[i]), regnames[i]); } - for (i = 0; i < 32; i++) { - int off = offsetof(CPULoongArchState, fpr[i]); - cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, off, fregnames[i]); - } - - cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPULoongArchState, pc), "pc"); - cpu_fcsr0 = tcg_global_mem_new_i32(cpu_env, - offsetof(CPULoongArchState, fcsr0), "fcsr0"); - cpu_lladdr = tcg_global_mem_new(cpu_env, + cpu_pc = tcg_global_mem_new(tcg_env, offsetof(CPULoongArchState, pc), "pc"); + cpu_lladdr = tcg_global_mem_new(tcg_env, offsetof(CPULoongArchState, lladdr), "lladdr"); - cpu_llval = tcg_global_mem_new(cpu_env, + cpu_llval = tcg_global_mem_new(tcg_env, offsetof(CPULoongArchState, llval), "llval"); } - -void restore_state_to_opc(CPULoongArchState *env, TranslationBlock *tb, - target_ulong *data) -{ - env->pc = data[0]; -} diff --git a/target/loongarch/tcg/vec_helper.c b/target/loongarch/tcg/vec_helper.c new file mode 100644 index 0000000000..3faf52cbc4 --- /dev/null +++ b/target/loongarch/tcg/vec_helper.c @@ -0,0 +1,3494 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch vector helper functions. + * + * Copyright (c) 2022-2023 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "fpu/softfloat.h" +#include "internals.h" +#include "tcg/tcg.h" +#include "vec.h" +#include "tcg/tcg-gvec-desc.h" + +#define DO_ODD_EVEN(NAME, BIT, E1, E2, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + typedef __typeof(Vd->E1(0)) TD; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E1(i) = DO_OP((TD)Vj->E2(2 * i + 1), (TD)Vk->E2(2 * i)); \ + } \ +} + +DO_ODD_EVEN(vhaddw_h_b, 16, H, B, DO_ADD) +DO_ODD_EVEN(vhaddw_w_h, 32, W, H, DO_ADD) +DO_ODD_EVEN(vhaddw_d_w, 64, D, W, DO_ADD) + +void HELPER(vhaddw_q_d)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16 ; i++) { + Vd->Q(i) = int128_add(int128_makes64(Vj->D(2 * i + 1)), + int128_makes64(Vk->D(2 * i))); + } +} + +DO_ODD_EVEN(vhsubw_h_b, 16, H, B, DO_SUB) +DO_ODD_EVEN(vhsubw_w_h, 32, W, H, DO_SUB) +DO_ODD_EVEN(vhsubw_d_w, 64, D, W, DO_SUB) + +void HELPER(vhsubw_q_d)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_sub(int128_makes64(Vj->D(2 * i + 1)), + int128_makes64(Vk->D(2 * i))); + } +} + +DO_ODD_EVEN(vhaddw_hu_bu, 16, UH, UB, DO_ADD) +DO_ODD_EVEN(vhaddw_wu_hu, 32, UW, UH, DO_ADD) +DO_ODD_EVEN(vhaddw_du_wu, 64, UD, UW, DO_ADD) + +void HELPER(vhaddw_qu_du)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i ++) { + Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i + 1)), + int128_make64(Vk->UD(2 * i))); + } +} + +DO_ODD_EVEN(vhsubw_hu_bu, 16, UH, UB, DO_SUB) +DO_ODD_EVEN(vhsubw_wu_hu, 32, UW, UH, DO_SUB) +DO_ODD_EVEN(vhsubw_du_wu, 64, UD, UW, DO_SUB) + +void HELPER(vhsubw_qu_du)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_sub(int128_make64(Vj->UD(2 * i + 1)), + int128_make64(Vk->UD(2 * i))); + } +} + +#define DO_EVEN(NAME, BIT, E1, E2, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + typedef __typeof(Vd->E1(0)) TD; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E1(i) = DO_OP((TD)Vj->E2(2 * i) ,(TD)Vk->E2(2 * i)); \ + } \ +} + +#define DO_ODD(NAME, BIT, E1, E2, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + typedef __typeof(Vd->E1(0)) TD; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E1(i) = DO_OP((TD)Vj->E2(2 * i + 1), (TD)Vk->E2(2 * i + 1)); \ + } \ +} + +void HELPER(vaddwev_q_d)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_add(int128_makes64(Vj->D(2 * i)), + int128_makes64(Vk->D(2 * i))); + } +} + +DO_EVEN(vaddwev_h_b, 16, H, B, DO_ADD) +DO_EVEN(vaddwev_w_h, 32, W, H, DO_ADD) +DO_EVEN(vaddwev_d_w, 64, D, W, DO_ADD) + +void HELPER(vaddwod_q_d)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_add(int128_makes64(Vj->D(2 * i +1)), + int128_makes64(Vk->D(2 * i +1))); + } +} + +DO_ODD(vaddwod_h_b, 16, H, B, DO_ADD) +DO_ODD(vaddwod_w_h, 32, W, H, DO_ADD) +DO_ODD(vaddwod_d_w, 64, D, W, DO_ADD) + +void HELPER(vsubwev_q_d)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_sub(int128_makes64(Vj->D(2 * i)), + int128_makes64(Vk->D(2 * i))); + } +} + +DO_EVEN(vsubwev_h_b, 16, H, B, DO_SUB) +DO_EVEN(vsubwev_w_h, 32, W, H, DO_SUB) +DO_EVEN(vsubwev_d_w, 64, D, W, DO_SUB) + +void HELPER(vsubwod_q_d)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_sub(int128_makes64(Vj->D(2 * i + 1)), + int128_makes64(Vk->D(2 * i + 1))); + } +} + +DO_ODD(vsubwod_h_b, 16, H, B, DO_SUB) +DO_ODD(vsubwod_w_h, 32, W, H, DO_SUB) +DO_ODD(vsubwod_d_w, 64, D, W, DO_SUB) + +void HELPER(vaddwev_q_du)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i)), + int128_make64(Vk->UD(2 * i))); + } +} + +DO_EVEN(vaddwev_h_bu, 16, UH, UB, DO_ADD) +DO_EVEN(vaddwev_w_hu, 32, UW, UH, DO_ADD) +DO_EVEN(vaddwev_d_wu, 64, UD, UW, DO_ADD) + +void HELPER(vaddwod_q_du)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i + 1)), + int128_make64(Vk->UD(2 * i + 1))); + } +} + +DO_ODD(vaddwod_h_bu, 16, UH, UB, DO_ADD) +DO_ODD(vaddwod_w_hu, 32, UW, UH, DO_ADD) +DO_ODD(vaddwod_d_wu, 64, UD, UW, DO_ADD) + +void HELPER(vsubwev_q_du)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_sub(int128_make64(Vj->UD(2 * i)), + int128_make64(Vk->UD(2 * i))); + } +} + +DO_EVEN(vsubwev_h_bu, 16, UH, UB, DO_SUB) +DO_EVEN(vsubwev_w_hu, 32, UW, UH, DO_SUB) +DO_EVEN(vsubwev_d_wu, 64, UD, UW, DO_SUB) + +void HELPER(vsubwod_q_du)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_sub(int128_make64(Vj->UD(2 * i + 1)), + int128_make64(Vk->UD(2 * i + 1))); + } +} + +DO_ODD(vsubwod_h_bu, 16, UH, UB, DO_SUB) +DO_ODD(vsubwod_w_hu, 32, UW, UH, DO_SUB) +DO_ODD(vsubwod_d_wu, 64, UD, UW, DO_SUB) + +#define DO_EVEN_U_S(NAME, BIT, ES1, EU1, ES2, EU2, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + typedef __typeof(Vd->ES1(0)) TDS; \ + typedef __typeof(Vd->EU1(0)) TDU; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->ES1(i) = DO_OP((TDU)Vj->EU2(2 * i) ,(TDS)Vk->ES2(2 * i)); \ + } \ +} + +#define DO_ODD_U_S(NAME, BIT, ES1, EU1, ES2, EU2, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + typedef __typeof(Vd->ES1(0)) TDS; \ + typedef __typeof(Vd->EU1(0)) TDU; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->ES1(i) = DO_OP((TDU)Vj->EU2(2 * i + 1), (TDS)Vk->ES2(2 * i + 1)); \ + } \ +} + +void HELPER(vaddwev_q_du_d)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i)), + int128_makes64(Vk->D(2 * i))); + } +} + +DO_EVEN_U_S(vaddwev_h_bu_b, 16, H, UH, B, UB, DO_ADD) +DO_EVEN_U_S(vaddwev_w_hu_h, 32, W, UW, H, UH, DO_ADD) +DO_EVEN_U_S(vaddwev_d_wu_w, 64, D, UD, W, UW, DO_ADD) + +void HELPER(vaddwod_q_du_d)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i + 1)), + int128_makes64(Vk->D(2 * i + 1))); + } +} + +DO_ODD_U_S(vaddwod_h_bu_b, 16, H, UH, B, UB, DO_ADD) +DO_ODD_U_S(vaddwod_w_hu_h, 32, W, UW, H, UH, DO_ADD) +DO_ODD_U_S(vaddwod_d_wu_w, 64, D, UD, W, UW, DO_ADD) + +#define DO_3OP(NAME, BIT, E, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = DO_OP(Vj->E(i), Vk->E(i)); \ + } \ +} + +DO_3OP(vavg_b, 8, B, DO_VAVG) +DO_3OP(vavg_h, 16, H, DO_VAVG) +DO_3OP(vavg_w, 32, W, DO_VAVG) +DO_3OP(vavg_d, 64, D, DO_VAVG) +DO_3OP(vavgr_b, 8, B, DO_VAVGR) +DO_3OP(vavgr_h, 16, H, DO_VAVGR) +DO_3OP(vavgr_w, 32, W, DO_VAVGR) +DO_3OP(vavgr_d, 64, D, DO_VAVGR) +DO_3OP(vavg_bu, 8, UB, DO_VAVG) +DO_3OP(vavg_hu, 16, UH, DO_VAVG) +DO_3OP(vavg_wu, 32, UW, DO_VAVG) +DO_3OP(vavg_du, 64, UD, DO_VAVG) +DO_3OP(vavgr_bu, 8, UB, DO_VAVGR) +DO_3OP(vavgr_hu, 16, UH, DO_VAVGR) +DO_3OP(vavgr_wu, 32, UW, DO_VAVGR) +DO_3OP(vavgr_du, 64, UD, DO_VAVGR) + +DO_3OP(vabsd_b, 8, B, DO_VABSD) +DO_3OP(vabsd_h, 16, H, DO_VABSD) +DO_3OP(vabsd_w, 32, W, DO_VABSD) +DO_3OP(vabsd_d, 64, D, DO_VABSD) +DO_3OP(vabsd_bu, 8, UB, DO_VABSD) +DO_3OP(vabsd_hu, 16, UH, DO_VABSD) +DO_3OP(vabsd_wu, 32, UW, DO_VABSD) +DO_3OP(vabsd_du, 64, UD, DO_VABSD) + +#define DO_VADDA(NAME, BIT, E) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = DO_VABS(Vj->E(i)) + DO_VABS(Vk->E(i)); \ + } \ +} + +DO_VADDA(vadda_b, 8, B) +DO_VADDA(vadda_h, 16, H) +DO_VADDA(vadda_w, 32, W) +DO_VADDA(vadda_d, 64, D) + +#define VMINMAXI(NAME, BIT, E, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + typedef __typeof(Vd->E(0)) TD; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = DO_OP(Vj->E(i), (TD)imm); \ + } \ +} + +VMINMAXI(vmini_b, 8, B, DO_MIN) +VMINMAXI(vmini_h, 16, H, DO_MIN) +VMINMAXI(vmini_w, 32, W, DO_MIN) +VMINMAXI(vmini_d, 64, D, DO_MIN) +VMINMAXI(vmaxi_b, 8, B, DO_MAX) +VMINMAXI(vmaxi_h, 16, H, DO_MAX) +VMINMAXI(vmaxi_w, 32, W, DO_MAX) +VMINMAXI(vmaxi_d, 64, D, DO_MAX) +VMINMAXI(vmini_bu, 8, UB, DO_MIN) +VMINMAXI(vmini_hu, 16, UH, DO_MIN) +VMINMAXI(vmini_wu, 32, UW, DO_MIN) +VMINMAXI(vmini_du, 64, UD, DO_MIN) +VMINMAXI(vmaxi_bu, 8, UB, DO_MAX) +VMINMAXI(vmaxi_hu, 16, UH, DO_MAX) +VMINMAXI(vmaxi_wu, 32, UW, DO_MAX) +VMINMAXI(vmaxi_du, 64, UD, DO_MAX) + +#define DO_VMUH(NAME, BIT, E1, E2, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + typedef __typeof(Vd->E1(0)) T; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E2(i) = ((T)Vj->E2(i)) * ((T)Vk->E2(i)) >> BIT; \ + } \ +} + +void HELPER(vmuh_d)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + uint64_t l, h; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 8; i++) { + muls64(&l, &h, Vj->D(i), Vk->D(i)); + Vd->D(i) = h; + } +} + +DO_VMUH(vmuh_b, 8, H, B, DO_MUH) +DO_VMUH(vmuh_h, 16, W, H, DO_MUH) +DO_VMUH(vmuh_w, 32, D, W, DO_MUH) + +void HELPER(vmuh_du)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i; + uint64_t l, h; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 8; i++) { + mulu64(&l, &h, Vj->D(i), Vk->D(i)); + Vd->D(i) = h; + } +} + +DO_VMUH(vmuh_bu, 8, UH, UB, DO_MUH) +DO_VMUH(vmuh_hu, 16, UW, UH, DO_MUH) +DO_VMUH(vmuh_wu, 32, UD, UW, DO_MUH) + +DO_EVEN(vmulwev_h_b, 16, H, B, DO_MUL) +DO_EVEN(vmulwev_w_h, 32, W, H, DO_MUL) +DO_EVEN(vmulwev_d_w, 64, D, W, DO_MUL) + +DO_ODD(vmulwod_h_b, 16, H, B, DO_MUL) +DO_ODD(vmulwod_w_h, 32, W, H, DO_MUL) +DO_ODD(vmulwod_d_w, 64, D, W, DO_MUL) + +DO_EVEN(vmulwev_h_bu, 16, UH, UB, DO_MUL) +DO_EVEN(vmulwev_w_hu, 32, UW, UH, DO_MUL) +DO_EVEN(vmulwev_d_wu, 64, UD, UW, DO_MUL) + +DO_ODD(vmulwod_h_bu, 16, UH, UB, DO_MUL) +DO_ODD(vmulwod_w_hu, 32, UW, UH, DO_MUL) +DO_ODD(vmulwod_d_wu, 64, UD, UW, DO_MUL) + +DO_EVEN_U_S(vmulwev_h_bu_b, 16, H, UH, B, UB, DO_MUL) +DO_EVEN_U_S(vmulwev_w_hu_h, 32, W, UW, H, UH, DO_MUL) +DO_EVEN_U_S(vmulwev_d_wu_w, 64, D, UD, W, UW, DO_MUL) + +DO_ODD_U_S(vmulwod_h_bu_b, 16, H, UH, B, UB, DO_MUL) +DO_ODD_U_S(vmulwod_w_hu_h, 32, W, UW, H, UH, DO_MUL) +DO_ODD_U_S(vmulwod_d_wu_w, 64, D, UD, W, UW, DO_MUL) + +#define VMADDSUB(NAME, BIT, E, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = DO_OP(Vd->E(i), Vj->E(i) ,Vk->E(i)); \ + } \ +} + +VMADDSUB(vmadd_b, 8, B, DO_MADD) +VMADDSUB(vmadd_h, 16, H, DO_MADD) +VMADDSUB(vmadd_w, 32, W, DO_MADD) +VMADDSUB(vmadd_d, 64, D, DO_MADD) +VMADDSUB(vmsub_b, 8, B, DO_MSUB) +VMADDSUB(vmsub_h, 16, H, DO_MSUB) +VMADDSUB(vmsub_w, 32, W, DO_MSUB) +VMADDSUB(vmsub_d, 64, D, DO_MSUB) + +#define VMADDWEV(NAME, BIT, E1, E2, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + typedef __typeof(Vd->E1(0)) TD; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E1(i) += DO_OP((TD)Vj->E2(2 * i), (TD)Vk->E2(2 * i)); \ + } \ +} + +VMADDWEV(vmaddwev_h_b, 16, H, B, DO_MUL) +VMADDWEV(vmaddwev_w_h, 32, W, H, DO_MUL) +VMADDWEV(vmaddwev_d_w, 64, D, W, DO_MUL) +VMADDWEV(vmaddwev_h_bu, 16, UH, UB, DO_MUL) +VMADDWEV(vmaddwev_w_hu, 32, UW, UH, DO_MUL) +VMADDWEV(vmaddwev_d_wu, 64, UD, UW, DO_MUL) + +#define VMADDWOD(NAME, BIT, E1, E2, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + typedef __typeof(Vd->E1(0)) TD; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E1(i) += DO_OP((TD)Vj->E2(2 * i + 1), \ + (TD)Vk->E2(2 * i + 1)); \ + } \ +} + +VMADDWOD(vmaddwod_h_b, 16, H, B, DO_MUL) +VMADDWOD(vmaddwod_w_h, 32, W, H, DO_MUL) +VMADDWOD(vmaddwod_d_w, 64, D, W, DO_MUL) +VMADDWOD(vmaddwod_h_bu, 16, UH, UB, DO_MUL) +VMADDWOD(vmaddwod_w_hu, 32, UW, UH, DO_MUL) +VMADDWOD(vmaddwod_d_wu, 64, UD, UW, DO_MUL) + +#define VMADDWEV_U_S(NAME, BIT, ES1, EU1, ES2, EU2, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + typedef __typeof(Vd->ES1(0)) TS1; \ + typedef __typeof(Vd->EU1(0)) TU1; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->ES1(i) += DO_OP((TU1)Vj->EU2(2 * i), \ + (TS1)Vk->ES2(2 * i)); \ + } \ +} + +VMADDWEV_U_S(vmaddwev_h_bu_b, 16, H, UH, B, UB, DO_MUL) +VMADDWEV_U_S(vmaddwev_w_hu_h, 32, W, UW, H, UH, DO_MUL) +VMADDWEV_U_S(vmaddwev_d_wu_w, 64, D, UD, W, UW, DO_MUL) + +#define VMADDWOD_U_S(NAME, BIT, ES1, EU1, ES2, EU2, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + typedef __typeof(Vd->ES1(0)) TS1; \ + typedef __typeof(Vd->EU1(0)) TU1; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->ES1(i) += DO_OP((TU1)Vj->EU2(2 * i + 1), \ + (TS1)Vk->ES2(2 * i + 1)); \ + } \ +} + +VMADDWOD_U_S(vmaddwod_h_bu_b, 16, H, UH, B, UB, DO_MUL) +VMADDWOD_U_S(vmaddwod_w_hu_h, 32, W, UW, H, UH, DO_MUL) +VMADDWOD_U_S(vmaddwod_d_wu_w, 64, D, UD, W, UW, DO_MUL) + +#define VDIV(NAME, BIT, E, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = DO_OP(Vj->E(i), Vk->E(i)); \ + } \ +} + +VDIV(vdiv_b, 8, B, DO_DIV) +VDIV(vdiv_h, 16, H, DO_DIV) +VDIV(vdiv_w, 32, W, DO_DIV) +VDIV(vdiv_d, 64, D, DO_DIV) +VDIV(vdiv_bu, 8, UB, DO_DIVU) +VDIV(vdiv_hu, 16, UH, DO_DIVU) +VDIV(vdiv_wu, 32, UW, DO_DIVU) +VDIV(vdiv_du, 64, UD, DO_DIVU) +VDIV(vmod_b, 8, B, DO_REM) +VDIV(vmod_h, 16, H, DO_REM) +VDIV(vmod_w, 32, W, DO_REM) +VDIV(vmod_d, 64, D, DO_REM) +VDIV(vmod_bu, 8, UB, DO_REMU) +VDIV(vmod_hu, 16, UH, DO_REMU) +VDIV(vmod_wu, 32, UW, DO_REMU) +VDIV(vmod_du, 64, UD, DO_REMU) + +#define VSAT_S(NAME, BIT, E) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t max, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + typedef __typeof(Vd->E(0)) TD; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = Vj->E(i) > (TD)max ? (TD)max : \ + Vj->E(i) < (TD)~max ? (TD)~max: Vj->E(i); \ + } \ +} + +VSAT_S(vsat_b, 8, B) +VSAT_S(vsat_h, 16, H) +VSAT_S(vsat_w, 32, W) +VSAT_S(vsat_d, 64, D) + +#define VSAT_U(NAME, BIT, E) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t max, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + typedef __typeof(Vd->E(0)) TD; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = Vj->E(i) > (TD)max ? (TD)max : Vj->E(i); \ + } \ +} + +VSAT_U(vsat_bu, 8, UB) +VSAT_U(vsat_hu, 16, UH) +VSAT_U(vsat_wu, 32, UW) +VSAT_U(vsat_du, 64, UD) + +#define VEXTH(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + Vd->E1(j + i * ofs) = Vj->E2(j + ofs + ofs * 2 * i); \ + } \ + } \ +} + +void HELPER(vexth_q_d)(void *vd, void *vj, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_makes64(Vj->D(2 * i + 1)); + } +} + +void HELPER(vexth_qu_du)(void *vd, void *vj, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_make64(Vj->UD(2 * i + 1)); + } +} + +VEXTH(vexth_h_b, 16, H, B) +VEXTH(vexth_w_h, 32, W, H) +VEXTH(vexth_d_w, 64, D, W) +VEXTH(vexth_hu_bu, 16, UH, UB) +VEXTH(vexth_wu_hu, 32, UW, UH) +VEXTH(vexth_du_wu, 64, UD, UW) + +#define VEXT2XV(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint32_t desc) \ +{ \ + int i; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + temp.E1(i) = Vj->E2(i); \ + } \ + *Vd = temp; \ +} + +VEXT2XV(vext2xv_h_b, 16, H, B) +VEXT2XV(vext2xv_w_b, 32, W, B) +VEXT2XV(vext2xv_d_b, 64, D, B) +VEXT2XV(vext2xv_w_h, 32, W, H) +VEXT2XV(vext2xv_d_h, 64, D, H) +VEXT2XV(vext2xv_d_w, 64, D, W) +VEXT2XV(vext2xv_hu_bu, 16, UH, UB) +VEXT2XV(vext2xv_wu_bu, 32, UW, UB) +VEXT2XV(vext2xv_du_bu, 64, UD, UB) +VEXT2XV(vext2xv_wu_hu, 32, UW, UH) +VEXT2XV(vext2xv_du_hu, 64, UD, UH) +VEXT2XV(vext2xv_du_wu, 64, UD, UW) + +DO_3OP(vsigncov_b, 8, B, DO_SIGNCOV) +DO_3OP(vsigncov_h, 16, H, DO_SIGNCOV) +DO_3OP(vsigncov_w, 32, W, DO_SIGNCOV) +DO_3OP(vsigncov_d, 64, D, DO_SIGNCOV) + +static uint64_t do_vmskltz_b(int64_t val) +{ + uint64_t m = 0x8080808080808080ULL; + uint64_t c = val & m; + c |= c << 7; + c |= c << 14; + c |= c << 28; + return c >> 56; +} + +void HELPER(vmskltz_b)(void *vd, void *vj, uint32_t desc) +{ + int i; + uint16_t temp = 0; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + temp = 0; + temp = do_vmskltz_b(Vj->D(2 * i)); + temp |= (do_vmskltz_b(Vj->D(2 * i + 1)) << 8); + Vd->D(2 * i) = temp; + Vd->D(2 * i + 1) = 0; + } +} + +static uint64_t do_vmskltz_h(int64_t val) +{ + uint64_t m = 0x8000800080008000ULL; + uint64_t c = val & m; + c |= c << 15; + c |= c << 30; + return c >> 60; +} + +void HELPER(vmskltz_h)(void *vd, void *vj, uint32_t desc) +{ + int i; + uint16_t temp = 0; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + temp = 0; + temp = do_vmskltz_h(Vj->D(2 * i)); + temp |= (do_vmskltz_h(Vj->D(2 * i + 1)) << 4); + Vd->D(2 * i) = temp; + Vd->D(2 * i + 1) = 0; + } +} + +static uint64_t do_vmskltz_w(int64_t val) +{ + uint64_t m = 0x8000000080000000ULL; + uint64_t c = val & m; + c |= c << 31; + return c >> 62; +} + +void HELPER(vmskltz_w)(void *vd, void *vj, uint32_t desc) +{ + int i; + uint16_t temp = 0; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + temp = 0; + temp = do_vmskltz_w(Vj->D(2 * i)); + temp |= (do_vmskltz_w(Vj->D(2 * i + 1)) << 2); + Vd->D(2 * i) = temp; + Vd->D(2 * i + 1) = 0; + } +} + +static uint64_t do_vmskltz_d(int64_t val) +{ + return (uint64_t)val >> 63; +} +void HELPER(vmskltz_d)(void *vd, void *vj, uint32_t desc) +{ + int i; + uint16_t temp = 0; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + temp = 0; + temp = do_vmskltz_d(Vj->D(2 * i)); + temp |= (do_vmskltz_d(Vj->D(2 * i + 1)) << 1); + Vd->D(2 * i) = temp; + Vd->D(2 * i + 1) = 0; + } +} + +void HELPER(vmskgez_b)(void *vd, void *vj, uint32_t desc) +{ + int i; + uint16_t temp = 0; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + temp = 0; + temp = do_vmskltz_b(Vj->D(2 * i)); + temp |= (do_vmskltz_b(Vj->D(2 * i + 1)) << 8); + Vd->D(2 * i) = (uint16_t)(~temp); + Vd->D(2 * i + 1) = 0; + } +} + +static uint64_t do_vmskez_b(uint64_t a) +{ + uint64_t m = 0x7f7f7f7f7f7f7f7fULL; + uint64_t c = ~(((a & m) + m) | a | m); + c |= c << 7; + c |= c << 14; + c |= c << 28; + return c >> 56; +} + +void HELPER(vmsknz_b)(void *vd, void *vj, uint32_t desc) +{ + int i; + uint16_t temp = 0; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + temp = 0; + temp = do_vmskez_b(Vj->D(2 * i)); + temp |= (do_vmskez_b(Vj->D(2 * i + 1)) << 8); + Vd->D(2 * i) = (uint16_t)(~temp); + Vd->D(2 * i + 1) = 0; + } +} + +void HELPER(vnori_b)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + + for (i = 0; i < simd_oprsz(desc); i++) { + Vd->B(i) = ~(Vj->B(i) | (uint8_t)imm); + } +} + +#define VSLLWIL(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + typedef __typeof(temp.E1(0)) TD; \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E1(j + ofs * i) = (TD)Vj->E2(j + ofs * 2 * i) << (imm % BIT); \ + } \ + } \ + *Vd = temp; \ +} + + +void HELPER(vextl_q_d)(void *vd, void *vj, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_makes64(Vj->D(2 * i)); + } +} + +void HELPER(vextl_qu_du)(void *vd, void *vj, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + Vd->Q(i) = int128_make64(Vj->UD(2 * i)); + } +} + +VSLLWIL(vsllwil_h_b, 16, H, B) +VSLLWIL(vsllwil_w_h, 32, W, H) +VSLLWIL(vsllwil_d_w, 64, D, W) +VSLLWIL(vsllwil_hu_bu, 16, UH, UB) +VSLLWIL(vsllwil_wu_hu, 32, UW, UH) +VSLLWIL(vsllwil_du_wu, 64, UD, UW) + +#define do_vsrlr(E, T) \ +static T do_vsrlr_ ##E(T s1, int sh) \ +{ \ + if (sh == 0) { \ + return s1; \ + } else { \ + return (s1 >> sh) + ((s1 >> (sh - 1)) & 0x1); \ + } \ +} + +do_vsrlr(B, uint8_t) +do_vsrlr(H, uint16_t) +do_vsrlr(W, uint32_t) +do_vsrlr(D, uint64_t) + +#define VSRLR(NAME, BIT, T, E) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = do_vsrlr_ ## E(Vj->E(i), ((T)Vk->E(i))%BIT); \ + } \ +} + +VSRLR(vsrlr_b, 8, uint8_t, B) +VSRLR(vsrlr_h, 16, uint16_t, H) +VSRLR(vsrlr_w, 32, uint32_t, W) +VSRLR(vsrlr_d, 64, uint64_t, D) + +#define VSRLRI(NAME, BIT, E) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = do_vsrlr_ ## E(Vj->E(i), imm); \ + } \ +} + +VSRLRI(vsrlri_b, 8, B) +VSRLRI(vsrlri_h, 16, H) +VSRLRI(vsrlri_w, 32, W) +VSRLRI(vsrlri_d, 64, D) + +#define do_vsrar(E, T) \ +static T do_vsrar_ ##E(T s1, int sh) \ +{ \ + if (sh == 0) { \ + return s1; \ + } else { \ + return (s1 >> sh) + ((s1 >> (sh - 1)) & 0x1); \ + } \ +} + +do_vsrar(B, int8_t) +do_vsrar(H, int16_t) +do_vsrar(W, int32_t) +do_vsrar(D, int64_t) + +#define VSRAR(NAME, BIT, T, E) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = do_vsrar_ ## E(Vj->E(i), ((T)Vk->E(i))%BIT); \ + } \ +} + +VSRAR(vsrar_b, 8, uint8_t, B) +VSRAR(vsrar_h, 16, uint16_t, H) +VSRAR(vsrar_w, 32, uint32_t, W) +VSRAR(vsrar_d, 64, uint64_t, D) + +#define VSRARI(NAME, BIT, E) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = do_vsrar_ ## E(Vj->E(i), imm); \ + } \ +} + +VSRARI(vsrari_b, 8, B) +VSRARI(vsrari_h, 16, H) +VSRARI(vsrari_w, 32, W) +VSRARI(vsrari_d, 64, D) + +#define VSRLN(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + Vd->E1(j + ofs * 2 * i) = R_SHIFT(Vj->E2(j + ofs * i), \ + Vk->E2(j + ofs * i) % BIT); \ + } \ + Vd->D(2 * i + 1) = 0; \ + } \ +} + +VSRLN(vsrln_b_h, 16, B, UH) +VSRLN(vsrln_h_w, 32, H, UW) +VSRLN(vsrln_w_d, 64, W, UD) + +#define VSRAN(NAME, BIT, E1, E2, E3) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + Vd->E1(j + ofs * 2 * i) = R_SHIFT(Vj->E2(j + ofs * i), \ + Vk->E3(j + ofs * i) % BIT); \ + } \ + Vd->D(2 * i + 1) = 0; \ + } \ +} + +VSRAN(vsran_b_h, 16, B, H, UH) +VSRAN(vsran_h_w, 32, H, W, UW) +VSRAN(vsran_w_d, 64, W, D, UD) + +#define VSRLNI(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E1(j + ofs * 2 * i) = R_SHIFT(Vj->E2(j + ofs * i), imm); \ + temp.E1(j + ofs * (2 * i + 1)) = R_SHIFT(Vd->E2(j + ofs * i), \ + imm); \ + } \ + } \ + *Vd = temp; \ +} + +void HELPER(vsrlni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + + for (i = 0; i < 2; i++) { + temp.D(2 * i) = int128_getlo(int128_urshift(Vj->Q(i), imm % 128)); + temp.D(2 * i +1) = int128_getlo(int128_urshift(Vd->Q(i), imm % 128)); + } + *Vd = temp; +} + +VSRLNI(vsrlni_b_h, 16, B, UH) +VSRLNI(vsrlni_h_w, 32, H, UW) +VSRLNI(vsrlni_w_d, 64, W, UD) + +#define VSRANI(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E1(j + ofs * 2 * i) = R_SHIFT(Vj->E2(j + ofs * i), imm); \ + temp.E1(j + ofs * (2 * i + 1)) = R_SHIFT(Vd->E2(j + ofs * i), \ + imm); \ + } \ + } \ + *Vd = temp; \ +} + +void HELPER(vsrani_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + + for (i = 0; i < 2; i++) { + temp.D(2 * i) = int128_getlo(int128_rshift(Vj->Q(i), imm % 128)); + temp.D(2 * i + 1) = int128_getlo(int128_rshift(Vd->Q(i), imm % 128)); + } + *Vd = temp; +} + +VSRANI(vsrani_b_h, 16, B, H) +VSRANI(vsrani_h_w, 32, H, W) +VSRANI(vsrani_w_d, 64, W, D) + +#define VSRLRN(NAME, BIT, E1, E2, E3) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + Vd->E1(j + ofs * 2 * i) = do_vsrlr_ ##E2(Vj->E2(j + ofs * i), \ + Vk->E3(j + ofs * i) % BIT); \ + } \ + Vd->D(2 * i + 1) = 0; \ + } \ +} + +VSRLRN(vsrlrn_b_h, 16, B, H, UH) +VSRLRN(vsrlrn_h_w, 32, H, W, UW) +VSRLRN(vsrlrn_w_d, 64, W, D, UD) + +#define VSRARN(NAME, BIT, E1, E2, E3) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + Vd->E1(j + ofs * 2 * i) = do_vsrar_ ## E2(Vj->E2(j + ofs * i), \ + Vk->E3(j + ofs * i) % BIT); \ + } \ + Vd->D(2 * i + 1) = 0; \ + } \ +} + +VSRARN(vsrarn_b_h, 16, B, H, UH) +VSRARN(vsrarn_h_w, 32, H, W, UW) +VSRARN(vsrarn_w_d, 64, W, D, UD) + +#define VSRLRNI(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E1(j + ofs * 2 * i) = do_vsrlr_ ## E2(Vj->E2(j + ofs * i), imm); \ + temp.E1(j + ofs * (2 * i + 1)) = do_vsrlr_ ## E2(Vd->E2(j + ofs * i), \ + imm); \ + } \ + } \ + *Vd = temp; \ +} + +void HELPER(vsrlrni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + Int128 r[4]; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + if (imm == 0) { + temp.D(2 * i) = int128_getlo(Vj->Q(i)); + temp.D(2 * i + 1) = int128_getlo(Vd->Q(i)); + } else { + r[2 * i] = int128_and(int128_urshift(Vj->Q(i), (imm - 1)), + int128_one()); + r[2 * i + 1] = int128_and(int128_urshift(Vd->Q(i), (imm - 1)), + int128_one()); + temp.D(2 * i) = int128_getlo(int128_add(int128_urshift(Vj->Q(i), + imm), r[2 * i])); + temp.D(2 * i + 1) = int128_getlo(int128_add(int128_urshift(Vd->Q(i), + imm), r[ 2 * i + 1])); + } + } + *Vd = temp; +} + +VSRLRNI(vsrlrni_b_h, 16, B, H) +VSRLRNI(vsrlrni_h_w, 32, H, W) +VSRLRNI(vsrlrni_w_d, 64, W, D) + +#define VSRARNI(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E1(j + ofs * 2 * i) = do_vsrar_ ## E2(Vj->E2(j + ofs * i), imm); \ + temp.E1(j + ofs * (2 * i + 1)) = do_vsrar_ ## E2(Vd->E2(j + ofs * i), \ + imm); \ + } \ + } \ + *Vd = temp; \ +} + +void HELPER(vsrarni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + Int128 r[4]; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + if (imm == 0) { + temp.D(2 * i) = int128_getlo(Vj->Q(i)); + temp.D(2 * i + 1) = int128_getlo(Vd->Q(i)); + } else { + r[2 * i] = int128_and(int128_rshift(Vj->Q(i), (imm - 1)), + int128_one()); + r[2 * i + 1] = int128_and(int128_rshift(Vd->Q(i), (imm - 1)), + int128_one()); + temp.D(2 * i) = int128_getlo(int128_add(int128_rshift(Vj->Q(i), + imm), r[2 * i])); + temp.D(2 * i + 1) = int128_getlo(int128_add(int128_rshift(Vd->Q(i), + imm), r[2 * i + 1])); + } + } + *Vd = temp; +} + +VSRARNI(vsrarni_b_h, 16, B, H) +VSRARNI(vsrarni_h_w, 32, H, W) +VSRARNI(vsrarni_w_d, 64, W, D) + +#define SSRLNS(NAME, T1, T2, T3) \ +static T1 do_ssrlns_ ## NAME(T2 e2, int sa, int sh) \ +{ \ + T1 shft_res; \ + if (sa == 0) { \ + shft_res = e2; \ + } else { \ + shft_res = (((T1)e2) >> sa); \ + } \ + T3 mask; \ + mask = (1ull << sh) -1; \ + if (shft_res > mask) { \ + return mask; \ + } else { \ + return shft_res; \ + } \ +} + +SSRLNS(B, uint16_t, int16_t, uint8_t) +SSRLNS(H, uint32_t, int32_t, uint16_t) +SSRLNS(W, uint64_t, int64_t, uint32_t) + +#define VSSRLN(NAME, BIT, E1, E2, E3) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + Vd->E1(j + ofs * 2 * i) = do_ssrlns_ ## E1(Vj->E2(j + ofs * i), \ + Vk->E3(j + ofs * i) % BIT, \ + BIT / 2 - 1); \ + } \ + Vd->D(2 * i + 1) = 0; \ + } \ +} + +VSSRLN(vssrln_b_h, 16, B, H, UH) +VSSRLN(vssrln_h_w, 32, H, W, UW) +VSSRLN(vssrln_w_d, 64, W, D, UD) + +#define SSRANS(E, T1, T2) \ +static T1 do_ssrans_ ## E(T1 e2, int sa, int sh) \ +{ \ + T1 shft_res; \ + if (sa == 0) { \ + shft_res = e2; \ + } else { \ + shft_res = e2 >> sa; \ + } \ + T2 mask; \ + mask = (1ll << sh) - 1; \ + if (shft_res > mask) { \ + return mask; \ + } else if (shft_res < -(mask + 1)) { \ + return ~mask; \ + } else { \ + return shft_res; \ + } \ +} + +SSRANS(B, int16_t, int8_t) +SSRANS(H, int32_t, int16_t) +SSRANS(W, int64_t, int32_t) + +#define VSSRAN(NAME, BIT, E1, E2, E3) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + Vd->E1(j + ofs * 2 * i) = do_ssrans_ ## E1(Vj->E2(j + ofs * i), \ + Vk->E3(j + ofs * i) % BIT, \ + BIT / 2 - 1); \ + } \ + Vd->D(2 * i + 1) = 0; \ + } \ +} + +VSSRAN(vssran_b_h, 16, B, H, UH) +VSSRAN(vssran_h_w, 32, H, W, UW) +VSSRAN(vssran_w_d, 64, W, D, UD) + +#define SSRLNU(E, T1, T2, T3) \ +static T1 do_ssrlnu_ ## E(T3 e2, int sa, int sh) \ +{ \ + T1 shft_res; \ + if (sa == 0) { \ + shft_res = e2; \ + } else { \ + shft_res = (((T1)e2) >> sa); \ + } \ + T2 mask; \ + mask = (1ull << sh) - 1; \ + if (shft_res > mask) { \ + return mask; \ + } else { \ + return shft_res; \ + } \ +} + +SSRLNU(B, uint16_t, uint8_t, int16_t) +SSRLNU(H, uint32_t, uint16_t, int32_t) +SSRLNU(W, uint64_t, uint32_t, int64_t) + +#define VSSRLNU(NAME, BIT, E1, E2, E3) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + Vd->E1(j + ofs * 2 * i) = do_ssrlnu_ ## E1(Vj->E2(j + ofs * i), \ + Vk->E3(j + ofs * i) % BIT, \ + BIT / 2); \ + } \ + Vd->D(2 * i + 1) = 0; \ + } \ +} + +VSSRLNU(vssrln_bu_h, 16, B, H, UH) +VSSRLNU(vssrln_hu_w, 32, H, W, UW) +VSSRLNU(vssrln_wu_d, 64, W, D, UD) + +#define SSRANU(E, T1, T2, T3) \ +static T1 do_ssranu_ ## E(T3 e2, int sa, int sh) \ +{ \ + T1 shft_res; \ + if (sa == 0) { \ + shft_res = e2; \ + } else { \ + shft_res = e2 >> sa; \ + } \ + if (e2 < 0) { \ + shft_res = 0; \ + } \ + T2 mask; \ + mask = (1ull << sh) - 1; \ + if (shft_res > mask) { \ + return mask; \ + } else { \ + return shft_res; \ + } \ +} + +SSRANU(B, uint16_t, uint8_t, int16_t) +SSRANU(H, uint32_t, uint16_t, int32_t) +SSRANU(W, uint64_t, uint32_t, int64_t) + +#define VSSRANU(NAME, BIT, E1, E2, E3) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + Vd->E1(j + ofs * 2 * i) = do_ssranu_ ## E1(Vj->E2(j + ofs * i), \ + Vk->E3(j + ofs * i) % BIT, \ + BIT / 2); \ + } \ + Vd->D(2 * i + 1) = 0; \ + } \ +} + +VSSRANU(vssran_bu_h, 16, B, H, UH) +VSSRANU(vssran_hu_w, 32, H, W, UW) +VSSRANU(vssran_wu_d, 64, W, D, UD) + +#define VSSRLNI(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E1(j + ofs * 2 * i) = do_ssrlns_ ## E1(Vj->E2(j + ofs * i), \ + imm, BIT / 2 - 1); \ + temp.E1(j + ofs * (2 * i + 1)) = do_ssrlns_ ## E1(Vd->E2(j + ofs * i), \ + imm, BIT / 2 - 1); \ + } \ + } \ + *Vd = temp; \ +} + +static void do_vssrlni_q(VReg *Vd, VReg *Vj, + uint64_t imm, int idx, Int128 mask) +{ + Int128 shft_res1, shft_res2; + + if (imm == 0) { + shft_res1 = Vj->Q(idx); + shft_res2 = Vd->Q(idx); + } else { + shft_res1 = int128_urshift(Vj->Q(idx), imm); + shft_res2 = int128_urshift(Vd->Q(idx), imm); + } + + if (int128_ult(mask, shft_res1)) { + Vd->D(idx * 2) = int128_getlo(mask); + }else { + Vd->D(idx * 2) = int128_getlo(shft_res1); + } + + if (int128_ult(mask, shft_res2)) { + Vd->D(idx * 2 + 1) = int128_getlo(mask); + }else { + Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); + } +} + +void HELPER(vssrlni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + Int128 mask; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + mask = int128_sub(int128_lshift(int128_one(), 63), int128_one()); + + for (i = 0; i < oprsz / 16; i++) { + do_vssrlni_q(Vd, Vj, imm, i, mask); + } +} + +VSSRLNI(vssrlni_b_h, 16, B, H) +VSSRLNI(vssrlni_h_w, 32, H, W) +VSSRLNI(vssrlni_w_d, 64, W, D) + +#define VSSRANI(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E1(j + ofs * 2 * i) = do_ssrans_ ## E1(Vj->E2(j + ofs * i), \ + imm, BIT / 2 - 1); \ + temp.E1(j + ofs * (2 * i + 1)) = do_ssrans_ ## E1(Vd->E2(j + ofs * i), \ + imm, BIT / 2 - 1); \ + } \ + } \ + *Vd = temp; \ +} + +static void do_vssrani_d_q(VReg *Vd, VReg *Vj, + uint64_t imm, int idx, Int128 mask, Int128 min) +{ + Int128 shft_res1, shft_res2; + + if (imm == 0) { + shft_res1 = Vj->Q(idx); + shft_res2 = Vd->Q(idx); + } else { + shft_res1 = int128_rshift(Vj->Q(idx), imm); + shft_res2 = int128_rshift(Vd->Q(idx), imm); + } + + if (int128_gt(shft_res1, mask)) { + Vd->D(idx * 2) = int128_getlo(mask); + } else if (int128_lt(shft_res1, int128_neg(min))) { + Vd->D(idx * 2) = int128_getlo(min); + } else { + Vd->D(idx * 2) = int128_getlo(shft_res1); + } + + if (int128_gt(shft_res2, mask)) { + Vd->D(idx * 2 + 1) = int128_getlo(mask); + } else if (int128_lt(shft_res2, int128_neg(min))) { + Vd->D(idx * 2 + 1) = int128_getlo(min); + } else { + Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); + } +} + +void HELPER(vssrani_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + Int128 mask, min; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + mask = int128_sub(int128_lshift(int128_one(), 63), int128_one()); + min = int128_lshift(int128_one(), 63); + + for (i = 0; i < oprsz / 16; i++) { + do_vssrani_d_q(Vd, Vj, imm, i, mask, min); + } +} + + +VSSRANI(vssrani_b_h, 16, B, H) +VSSRANI(vssrani_h_w, 32, H, W) +VSSRANI(vssrani_w_d, 64, W, D) + +#define VSSRLNUI(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E1(j + ofs * 2 * i) = do_ssrlnu_ ## E1(Vj->E2(j + ofs * i), \ + imm, BIT / 2); \ + temp.E1(j + ofs * (2 * i + 1)) = do_ssrlnu_ ## E1(Vd->E2(j + ofs * i), \ + imm, BIT / 2); \ + } \ + } \ + *Vd = temp; \ +} + +void HELPER(vssrlni_du_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + Int128 mask; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + mask = int128_sub(int128_lshift(int128_one(), 64), int128_one()); + + for (i = 0; i < oprsz / 16; i++) { + do_vssrlni_q(Vd, Vj, imm, i, mask); + } +} + +VSSRLNUI(vssrlni_bu_h, 16, B, H) +VSSRLNUI(vssrlni_hu_w, 32, H, W) +VSSRLNUI(vssrlni_wu_d, 64, W, D) + +#define VSSRANUI(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E1(j + ofs * 2 * i) = do_ssranu_ ## E1(Vj->E2(j + ofs * i), \ + imm, BIT / 2); \ + temp.E1(j + ofs * (2 * i + 1)) = do_ssranu_ ## E1(Vd->E2(j + ofs * i), \ + imm, BIT / 2); \ + } \ + } \ + *Vd = temp; \ +} + +static void do_vssrani_du_q(VReg *Vd, VReg *Vj, + uint64_t imm, int idx, Int128 mask) +{ + Int128 shft_res1, shft_res2; + + if (imm == 0) { + shft_res1 = Vj->Q(idx); + shft_res2 = Vd->Q(idx); + } else { + shft_res1 = int128_rshift(Vj->Q(idx), imm); + shft_res2 = int128_rshift(Vd->Q(idx), imm); + } + + if (int128_lt(Vj->Q(idx), int128_zero())) { + shft_res1 = int128_zero(); + } + + if (int128_lt(Vd->Q(idx), int128_zero())) { + shft_res2 = int128_zero(); + } + if (int128_ult(mask, shft_res1)) { + Vd->D(idx * 2) = int128_getlo(mask); + }else { + Vd->D(idx * 2) = int128_getlo(shft_res1); + } + + if (int128_ult(mask, shft_res2)) { + Vd->D(idx * 2 + 1) = int128_getlo(mask); + }else { + Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); + } + +} + +void HELPER(vssrani_du_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + Int128 mask; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + mask = int128_sub(int128_lshift(int128_one(), 64), int128_one()); + + for (i = 0; i < oprsz / 16; i++) { + do_vssrani_du_q(Vd, Vj, imm, i, mask); + } +} + +VSSRANUI(vssrani_bu_h, 16, B, H) +VSSRANUI(vssrani_hu_w, 32, H, W) +VSSRANUI(vssrani_wu_d, 64, W, D) + +#define SSRLRNS(E1, E2, T1, T2, T3) \ +static T1 do_ssrlrns_ ## E1(T2 e2, int sa, int sh) \ +{ \ + T1 shft_res; \ + \ + shft_res = do_vsrlr_ ## E2(e2, sa); \ + T1 mask; \ + mask = (1ull << sh) - 1; \ + if (shft_res > mask) { \ + return mask; \ + } else { \ + return shft_res; \ + } \ +} + +SSRLRNS(B, H, uint16_t, int16_t, uint8_t) +SSRLRNS(H, W, uint32_t, int32_t, uint16_t) +SSRLRNS(W, D, uint64_t, int64_t, uint32_t) + +#define VSSRLRN(NAME, BIT, E1, E2, E3) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + Vd->E1(j + ofs * 2 * i) = do_ssrlrns_ ## E1(Vj->E2(j + ofs * i), \ + Vk->E3(j + ofs * i) % BIT, \ + BIT / 2 - 1); \ + } \ + Vd->D(2 * i + 1) = 0; \ + } \ +} + +VSSRLRN(vssrlrn_b_h, 16, B, H, UH) +VSSRLRN(vssrlrn_h_w, 32, H, W, UW) +VSSRLRN(vssrlrn_w_d, 64, W, D, UD) + +#define SSRARNS(E1, E2, T1, T2) \ +static T1 do_ssrarns_ ## E1(T1 e2, int sa, int sh) \ +{ \ + T1 shft_res; \ + \ + shft_res = do_vsrar_ ## E2(e2, sa); \ + T2 mask; \ + mask = (1ll << sh) - 1; \ + if (shft_res > mask) { \ + return mask; \ + } else if (shft_res < -(mask +1)) { \ + return ~mask; \ + } else { \ + return shft_res; \ + } \ +} + +SSRARNS(B, H, int16_t, int8_t) +SSRARNS(H, W, int32_t, int16_t) +SSRARNS(W, D, int64_t, int32_t) + +#define VSSRARN(NAME, BIT, E1, E2, E3) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + Vd->E1(j + ofs * 2 * i) = do_ssrarns_ ## E1(Vj->E2(j + ofs * i), \ + Vk->E3(j + ofs * i) % BIT, \ + BIT/ 2 - 1); \ + } \ + Vd->D(2 * i + 1) = 0; \ + } \ +} + +VSSRARN(vssrarn_b_h, 16, B, H, UH) +VSSRARN(vssrarn_h_w, 32, H, W, UW) +VSSRARN(vssrarn_w_d, 64, W, D, UD) + +#define SSRLRNU(E1, E2, T1, T2, T3) \ +static T1 do_ssrlrnu_ ## E1(T3 e2, int sa, int sh) \ +{ \ + T1 shft_res; \ + \ + shft_res = do_vsrlr_ ## E2(e2, sa); \ + \ + T2 mask; \ + mask = (1ull << sh) - 1; \ + if (shft_res > mask) { \ + return mask; \ + } else { \ + return shft_res; \ + } \ +} + +SSRLRNU(B, H, uint16_t, uint8_t, int16_t) +SSRLRNU(H, W, uint32_t, uint16_t, int32_t) +SSRLRNU(W, D, uint64_t, uint32_t, int64_t) + +#define VSSRLRNU(NAME, BIT, E1, E2, E3) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + Vd->E1(j + ofs * 2 * i) = do_ssrlrnu_ ## E1(Vj->E2(j + ofs * i), \ + Vk->E3(j + ofs * i) % BIT, \ + BIT / 2); \ + } \ + Vd->D(2 * i + 1) = 0; \ + } \ +} + +VSSRLRNU(vssrlrn_bu_h, 16, B, H, UH) +VSSRLRNU(vssrlrn_hu_w, 32, H, W, UW) +VSSRLRNU(vssrlrn_wu_d, 64, W, D, UD) + +#define SSRARNU(E1, E2, T1, T2, T3) \ +static T1 do_ssrarnu_ ## E1(T3 e2, int sa, int sh) \ +{ \ + T1 shft_res; \ + \ + if (e2 < 0) { \ + shft_res = 0; \ + } else { \ + shft_res = do_vsrar_ ## E2(e2, sa); \ + } \ + T2 mask; \ + mask = (1ull << sh) - 1; \ + if (shft_res > mask) { \ + return mask; \ + } else { \ + return shft_res; \ + } \ +} + +SSRARNU(B, H, uint16_t, uint8_t, int16_t) +SSRARNU(H, W, uint32_t, uint16_t, int32_t) +SSRARNU(W, D, uint64_t, uint32_t, int64_t) + +#define VSSRARNU(NAME, BIT, E1, E2, E3) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + Vd->E1(j + ofs * 2 * i) = do_ssrarnu_ ## E1(Vj->E2(j + ofs * i), \ + Vk->E3(j + ofs * i) % BIT, \ + BIT / 2); \ + } \ + Vd->D(2 * i + 1) = 0; \ + } \ +} + +VSSRARNU(vssrarn_bu_h, 16, B, H, UH) +VSSRARNU(vssrarn_hu_w, 32, H, W, UW) +VSSRARNU(vssrarn_wu_d, 64, W, D, UD) + +#define VSSRLRNI(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E1(j + ofs * 2 * i) = do_ssrlrns_ ## E1(Vj->E2(j + ofs * i), \ + imm, BIT / 2 - 1); \ + temp.E1(j + ofs * (2 * i + 1)) = do_ssrlrns_ ## E1(Vd->E2(j + ofs * i), \ + imm, BIT / 2 - 1); \ + } \ + } \ + *Vd = temp; \ +} + +static void do_vssrlrni_q(VReg *Vd, VReg * Vj, + uint64_t imm, int idx, Int128 mask) +{ + Int128 shft_res1, shft_res2, r1, r2; + if (imm == 0) { + shft_res1 = Vj->Q(idx); + shft_res2 = Vd->Q(idx); + } else { + r1 = int128_and(int128_urshift(Vj->Q(idx), (imm - 1)), int128_one()); + r2 = int128_and(int128_urshift(Vd->Q(idx), (imm - 1)), int128_one()); + shft_res1 = (int128_add(int128_urshift(Vj->Q(idx), imm), r1)); + shft_res2 = (int128_add(int128_urshift(Vd->Q(idx), imm), r2)); + } + + if (int128_ult(mask, shft_res1)) { + Vd->D(idx * 2) = int128_getlo(mask); + }else { + Vd->D(idx * 2) = int128_getlo(shft_res1); + } + + if (int128_ult(mask, shft_res2)) { + Vd->D(idx * 2 + 1) = int128_getlo(mask); + }else { + Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); + } +} + +void HELPER(vssrlrni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + Int128 mask; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + mask = int128_sub(int128_lshift(int128_one(), 63), int128_one()); + + for (i = 0; i < oprsz / 16; i++) { + do_vssrlrni_q(Vd, Vj, imm, i, mask); + } +} + +VSSRLRNI(vssrlrni_b_h, 16, B, H) +VSSRLRNI(vssrlrni_h_w, 32, H, W) +VSSRLRNI(vssrlrni_w_d, 64, W, D) + +#define VSSRARNI(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E1(j + ofs * 2 * i) = do_ssrarns_ ## E1(Vj->E2(j + ofs * i), \ + imm, BIT / 2 - 1); \ + temp.E1(j + ofs * (2 * i + 1)) = do_ssrarns_ ## E1(Vd->E2(j + ofs * i), \ + imm, BIT / 2 - 1); \ + } \ + } \ + *Vd = temp; \ +} + +static void do_vssrarni_d_q(VReg *Vd, VReg *Vj, + uint64_t imm, int idx, Int128 mask1, Int128 mask2) +{ + Int128 shft_res1, shft_res2, r1, r2; + + if (imm == 0) { + shft_res1 = Vj->Q(idx); + shft_res2 = Vd->Q(idx); + } else { + r1 = int128_and(int128_rshift(Vj->Q(idx), (imm - 1)), int128_one()); + r2 = int128_and(int128_rshift(Vd->Q(idx), (imm - 1)), int128_one()); + shft_res1 = int128_add(int128_rshift(Vj->Q(idx), imm), r1); + shft_res2 = int128_add(int128_rshift(Vd->Q(idx), imm), r2); + } + if (int128_gt(shft_res1, mask1)) { + Vd->D(idx * 2) = int128_getlo(mask1); + } else if (int128_lt(shft_res1, int128_neg(mask2))) { + Vd->D(idx * 2) = int128_getlo(mask2); + } else { + Vd->D(idx * 2) = int128_getlo(shft_res1); + } + + if (int128_gt(shft_res2, mask1)) { + Vd->D(idx * 2 + 1) = int128_getlo(mask1); + } else if (int128_lt(shft_res2, int128_neg(mask2))) { + Vd->D(idx * 2 + 1) = int128_getlo(mask2); + } else { + Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); + } +} + +void HELPER(vssrarni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + Int128 mask1, mask2; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + mask1 = int128_sub(int128_lshift(int128_one(), 63), int128_one()); + mask2 = int128_lshift(int128_one(), 63); + + for (i = 0; i < oprsz / 16; i++) { + do_vssrarni_d_q(Vd, Vj, imm, i, mask1, mask2); + } +} + +VSSRARNI(vssrarni_b_h, 16, B, H) +VSSRARNI(vssrarni_h_w, 32, H, W) +VSSRARNI(vssrarni_w_d, 64, W, D) + +#define VSSRLRNUI(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E1(j + ofs * 2 * i) = do_ssrlrnu_ ## E1(Vj->E2(j + ofs * i), \ + imm, BIT / 2); \ + temp.E1(j + ofs * (2 * i + 1)) = do_ssrlrnu_ ## E1(Vd->E2(j + ofs * i), \ + imm, BIT / 2); \ + } \ + } \ + *Vd = temp; \ +} + +void HELPER(vssrlrni_du_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + Int128 mask; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + mask = int128_sub(int128_lshift(int128_one(), 64), int128_one()); + + for (i = 0; i < oprsz / 16; i++) { + do_vssrlrni_q(Vd, Vj, imm, i, mask); + } +} + +VSSRLRNUI(vssrlrni_bu_h, 16, B, H) +VSSRLRNUI(vssrlrni_hu_w, 32, H, W) +VSSRLRNUI(vssrlrni_wu_d, 64, W, D) + +#define VSSRARNUI(NAME, BIT, E1, E2) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E1(j + ofs * 2 * i) = do_ssrarnu_ ## E1(Vj->E2(j + ofs * i), \ + imm, BIT / 2); \ + temp.E1(j + ofs * (2 * i + 1)) = do_ssrarnu_ ## E1(Vd->E2(j + ofs * i), \ + imm, BIT / 2); \ + } \ + } \ + *Vd = temp; \ +} + +static void do_vssrarni_du_q(VReg *Vd, VReg *Vj, + uint64_t imm, int idx, Int128 mask1, Int128 mask2) +{ + Int128 shft_res1, shft_res2, r1, r2; + + if (imm == 0) { + shft_res1 = Vj->Q(idx); + shft_res2 = Vd->Q(idx); + } else { + r1 = int128_and(int128_rshift(Vj->Q(idx), (imm - 1)), int128_one()); + r2 = int128_and(int128_rshift(Vd->Q(idx), (imm - 1)), int128_one()); + shft_res1 = int128_add(int128_rshift(Vj->Q(idx), imm), r1); + shft_res2 = int128_add(int128_rshift(Vd->Q(idx), imm), r2); + } + + if (int128_lt(Vj->Q(idx), int128_zero())) { + shft_res1 = int128_zero(); + } + if (int128_lt(Vd->Q(idx), int128_zero())) { + shft_res2 = int128_zero(); + } + + if (int128_gt(shft_res1, mask1)) { + Vd->D(idx * 2) = int128_getlo(mask1); + } else if (int128_lt(shft_res1, int128_neg(mask2))) { + Vd->D(idx * 2) = int128_getlo(mask2); + } else { + Vd->D(idx * 2) = int128_getlo(shft_res1); + } + + if (int128_gt(shft_res2, mask1)) { + Vd->D(idx * 2 + 1) = int128_getlo(mask1); + } else if (int128_lt(shft_res2, int128_neg(mask2))) { + Vd->D(idx * 2 + 1) = int128_getlo(mask2); + } else { + Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); + } +} + +void HELPER(vssrarni_du_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + Int128 mask1, mask2; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + mask1 = int128_sub(int128_lshift(int128_one(), 64), int128_one()); + mask2 = int128_lshift(int128_one(), 64); + + for (i = 0; i < oprsz / 16; i++) { + do_vssrarni_du_q(Vd, Vj, imm, i, mask1, mask2); + } +} + +VSSRARNUI(vssrarni_bu_h, 16, B, H) +VSSRARNUI(vssrarni_hu_w, 32, H, W) +VSSRARNUI(vssrarni_wu_d, 64, W, D) + +#define DO_2OP(NAME, BIT, E, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) \ + { \ + Vd->E(i) = DO_OP(Vj->E(i)); \ + } \ +} + +DO_2OP(vclo_b, 8, UB, DO_CLO_B) +DO_2OP(vclo_h, 16, UH, DO_CLO_H) +DO_2OP(vclo_w, 32, UW, DO_CLO_W) +DO_2OP(vclo_d, 64, UD, DO_CLO_D) +DO_2OP(vclz_b, 8, UB, DO_CLZ_B) +DO_2OP(vclz_h, 16, UH, DO_CLZ_H) +DO_2OP(vclz_w, 32, UW, DO_CLZ_W) +DO_2OP(vclz_d, 64, UD, DO_CLZ_D) + +#define VPCNT(NAME, BIT, E, FN) \ +void HELPER(NAME)(void *vd, void *vj, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) \ + { \ + Vd->E(i) = FN(Vj->E(i)); \ + } \ +} + +VPCNT(vpcnt_b, 8, UB, ctpop8) +VPCNT(vpcnt_h, 16, UH, ctpop16) +VPCNT(vpcnt_w, 32, UW, ctpop32) +VPCNT(vpcnt_d, 64, UD, ctpop64) + +#define DO_BIT(NAME, BIT, E, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = DO_OP(Vj->E(i), Vk->E(i)%BIT); \ + } \ +} + +DO_BIT(vbitclr_b, 8, UB, DO_BITCLR) +DO_BIT(vbitclr_h, 16, UH, DO_BITCLR) +DO_BIT(vbitclr_w, 32, UW, DO_BITCLR) +DO_BIT(vbitclr_d, 64, UD, DO_BITCLR) +DO_BIT(vbitset_b, 8, UB, DO_BITSET) +DO_BIT(vbitset_h, 16, UH, DO_BITSET) +DO_BIT(vbitset_w, 32, UW, DO_BITSET) +DO_BIT(vbitset_d, 64, UD, DO_BITSET) +DO_BIT(vbitrev_b, 8, UB, DO_BITREV) +DO_BIT(vbitrev_h, 16, UH, DO_BITREV) +DO_BIT(vbitrev_w, 32, UW, DO_BITREV) +DO_BIT(vbitrev_d, 64, UD, DO_BITREV) + +#define DO_BITI(NAME, BIT, E, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = DO_OP(Vj->E(i), imm); \ + } \ +} + +DO_BITI(vbitclri_b, 8, UB, DO_BITCLR) +DO_BITI(vbitclri_h, 16, UH, DO_BITCLR) +DO_BITI(vbitclri_w, 32, UW, DO_BITCLR) +DO_BITI(vbitclri_d, 64, UD, DO_BITCLR) +DO_BITI(vbitseti_b, 8, UB, DO_BITSET) +DO_BITI(vbitseti_h, 16, UH, DO_BITSET) +DO_BITI(vbitseti_w, 32, UW, DO_BITSET) +DO_BITI(vbitseti_d, 64, UD, DO_BITSET) +DO_BITI(vbitrevi_b, 8, UB, DO_BITREV) +DO_BITI(vbitrevi_h, 16, UH, DO_BITREV) +DO_BITI(vbitrevi_w, 32, UW, DO_BITREV) +DO_BITI(vbitrevi_d, 64, UD, DO_BITREV) + +#define VFRSTP(NAME, BIT, MASK, E) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, m, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + m = Vk->E(i * ofs) & MASK; \ + for (j = 0; j < ofs; j++) { \ + if (Vj->E(j + ofs * i) < 0) { \ + break; \ + } \ + } \ + Vd->E(m + i * ofs) = j; \ + } \ +} + +VFRSTP(vfrstp_b, 8, 0xf, B) +VFRSTP(vfrstp_h, 16, 0x7, H) + +#define VFRSTPI(NAME, BIT, E) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, m, ofs; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + m = imm % ofs; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + if (Vj->E(j + ofs * i) < 0) { \ + break; \ + } \ + } \ + Vd->E(m + i * ofs) = j; \ + } \ +} + +VFRSTPI(vfrstpi_b, 8, B) +VFRSTPI(vfrstpi_h, 16, H) + +static void vec_update_fcsr0_mask(CPULoongArchState *env, + uintptr_t pc, int mask) +{ + int flags = get_float_exception_flags(&env->fp_status); + + set_float_exception_flags(0, &env->fp_status); + + flags &= ~mask; + + if (flags) { + flags = ieee_ex_to_loongarch(flags); + UPDATE_FP_CAUSE(env->fcsr0, flags); + } + + if (GET_FP_ENABLES(env->fcsr0) & flags) { + do_raise_exception(env, EXCCODE_FPE, pc); + } else { + UPDATE_FP_FLAGS(env->fcsr0, flags); + } +} + +static void vec_update_fcsr0(CPULoongArchState *env, uintptr_t pc) +{ + vec_update_fcsr0_mask(env, pc, 0); +} + +static inline void vec_clear_cause(CPULoongArchState *env) +{ + SET_FP_CAUSE(env->fcsr0, 0); +} + +#define DO_3OP_F(NAME, BIT, E, FN) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, \ + CPULoongArchState *env, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + vec_clear_cause(env); \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = FN(Vj->E(i), Vk->E(i), &env->fp_status); \ + vec_update_fcsr0(env, GETPC()); \ + } \ +} + +DO_3OP_F(vfadd_s, 32, UW, float32_add) +DO_3OP_F(vfadd_d, 64, UD, float64_add) +DO_3OP_F(vfsub_s, 32, UW, float32_sub) +DO_3OP_F(vfsub_d, 64, UD, float64_sub) +DO_3OP_F(vfmul_s, 32, UW, float32_mul) +DO_3OP_F(vfmul_d, 64, UD, float64_mul) +DO_3OP_F(vfdiv_s, 32, UW, float32_div) +DO_3OP_F(vfdiv_d, 64, UD, float64_div) +DO_3OP_F(vfmax_s, 32, UW, float32_maxnum) +DO_3OP_F(vfmax_d, 64, UD, float64_maxnum) +DO_3OP_F(vfmin_s, 32, UW, float32_minnum) +DO_3OP_F(vfmin_d, 64, UD, float64_minnum) +DO_3OP_F(vfmaxa_s, 32, UW, float32_maxnummag) +DO_3OP_F(vfmaxa_d, 64, UD, float64_maxnummag) +DO_3OP_F(vfmina_s, 32, UW, float32_minnummag) +DO_3OP_F(vfmina_d, 64, UD, float64_minnummag) + +#define DO_4OP_F(NAME, BIT, E, FN, flags) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, void *va, \ + CPULoongArchState *env, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + VReg *Va = (VReg *)va; \ + int oprsz = simd_oprsz(desc); \ + \ + vec_clear_cause(env); \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = FN(Vj->E(i), Vk->E(i), Va->E(i), flags, &env->fp_status); \ + vec_update_fcsr0(env, GETPC()); \ + } \ +} + +DO_4OP_F(vfmadd_s, 32, UW, float32_muladd, 0) +DO_4OP_F(vfmadd_d, 64, UD, float64_muladd, 0) +DO_4OP_F(vfmsub_s, 32, UW, float32_muladd, float_muladd_negate_c) +DO_4OP_F(vfmsub_d, 64, UD, float64_muladd, float_muladd_negate_c) +DO_4OP_F(vfnmadd_s, 32, UW, float32_muladd, float_muladd_negate_result) +DO_4OP_F(vfnmadd_d, 64, UD, float64_muladd, float_muladd_negate_result) +DO_4OP_F(vfnmsub_s, 32, UW, float32_muladd, + float_muladd_negate_c | float_muladd_negate_result) +DO_4OP_F(vfnmsub_d, 64, UD, float64_muladd, + float_muladd_negate_c | float_muladd_negate_result) + +#define DO_2OP_F(NAME, BIT, E, FN) \ +void HELPER(NAME)(void *vd, void *vj, \ + CPULoongArchState *env, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + vec_clear_cause(env); \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = FN(env, Vj->E(i)); \ + } \ +} + +#define FLOGB(BIT, T) \ +static T do_flogb_## BIT(CPULoongArchState *env, T fj) \ +{ \ + T fp, fd; \ + float_status *status = &env->fp_status; \ + FloatRoundMode old_mode = get_float_rounding_mode(status); \ + \ + set_float_rounding_mode(float_round_down, status); \ + fp = float ## BIT ##_log2(fj, status); \ + fd = float ## BIT ##_round_to_int(fp, status); \ + set_float_rounding_mode(old_mode, status); \ + vec_update_fcsr0_mask(env, GETPC(), float_flag_inexact); \ + return fd; \ +} + +FLOGB(32, uint32_t) +FLOGB(64, uint64_t) + +#define FCLASS(NAME, BIT, E, FN) \ +void HELPER(NAME)(void *vd, void *vj, \ + CPULoongArchState *env, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = FN(env, Vj->E(i)); \ + } \ +} + +FCLASS(vfclass_s, 32, UW, helper_fclass_s) +FCLASS(vfclass_d, 64, UD, helper_fclass_d) + +#define FSQRT(BIT, T) \ +static T do_fsqrt_## BIT(CPULoongArchState *env, T fj) \ +{ \ + T fd; \ + fd = float ## BIT ##_sqrt(fj, &env->fp_status); \ + vec_update_fcsr0(env, GETPC()); \ + return fd; \ +} + +FSQRT(32, uint32_t) +FSQRT(64, uint64_t) + +#define FRECIP(BIT, T) \ +static T do_frecip_## BIT(CPULoongArchState *env, T fj) \ +{ \ + T fd; \ + fd = float ## BIT ##_div(float ## BIT ##_one, fj, &env->fp_status); \ + vec_update_fcsr0(env, GETPC()); \ + return fd; \ +} + +FRECIP(32, uint32_t) +FRECIP(64, uint64_t) + +#define FRSQRT(BIT, T) \ +static T do_frsqrt_## BIT(CPULoongArchState *env, T fj) \ +{ \ + T fd, fp; \ + fp = float ## BIT ##_sqrt(fj, &env->fp_status); \ + fd = float ## BIT ##_div(float ## BIT ##_one, fp, &env->fp_status); \ + vec_update_fcsr0(env, GETPC()); \ + return fd; \ +} + +FRSQRT(32, uint32_t) +FRSQRT(64, uint64_t) + +DO_2OP_F(vflogb_s, 32, UW, do_flogb_32) +DO_2OP_F(vflogb_d, 64, UD, do_flogb_64) +DO_2OP_F(vfsqrt_s, 32, UW, do_fsqrt_32) +DO_2OP_F(vfsqrt_d, 64, UD, do_fsqrt_64) +DO_2OP_F(vfrecip_s, 32, UW, do_frecip_32) +DO_2OP_F(vfrecip_d, 64, UD, do_frecip_64) +DO_2OP_F(vfrsqrt_s, 32, UW, do_frsqrt_32) +DO_2OP_F(vfrsqrt_d, 64, UD, do_frsqrt_64) + +static uint32_t float16_cvt_float32(uint16_t h, float_status *status) +{ + return float16_to_float32(h, true, status); +} +static uint64_t float32_cvt_float64(uint32_t s, float_status *status) +{ + return float32_to_float64(s, status); +} + +static uint16_t float32_cvt_float16(uint32_t s, float_status *status) +{ + return float32_to_float16(s, true, status); +} +static uint32_t float64_cvt_float32(uint64_t d, float_status *status) +{ + return float64_to_float32(d, status); +} + +void HELPER(vfcvtl_s_h)(void *vd, void *vj, + CPULoongArchState *env, uint32_t desc) +{ + int i, j, ofs; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + ofs = LSX_LEN / 32; + vec_clear_cause(env); + for (i = 0; i < oprsz / 16; i++) { + for (j = 0; j < ofs; j++) { + temp.UW(j + ofs * i) =float16_cvt_float32(Vj->UH(j + ofs * 2 * i), + &env->fp_status); + } + vec_update_fcsr0(env, GETPC()); + } + *Vd = temp; +} + +void HELPER(vfcvtl_d_s)(void *vd, void *vj, + CPULoongArchState *env, uint32_t desc) +{ + int i, j, ofs; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + ofs = LSX_LEN / 64; + vec_clear_cause(env); + for (i = 0; i < oprsz / 16; i++) { + for (j = 0; j < ofs; j++) { + temp.UD(j + ofs * i) = float32_cvt_float64(Vj->UW(j + ofs * 2 * i), + &env->fp_status); + } + vec_update_fcsr0(env, GETPC()); + } + *Vd = temp; +} + +void HELPER(vfcvth_s_h)(void *vd, void *vj, + CPULoongArchState *env, uint32_t desc) +{ + int i, j, ofs; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + ofs = LSX_LEN / 32; + vec_clear_cause(env); + for (i = 0; i < oprsz / 16; i++) { + for (j = 0; j < ofs; j++) { + temp.UW(j + ofs * i) = float16_cvt_float32(Vj->UH(j + ofs * (2 * i + 1)), + &env->fp_status); + } + vec_update_fcsr0(env, GETPC()); + } + *Vd = temp; +} + +void HELPER(vfcvth_d_s)(void *vd, void *vj, + CPULoongArchState *env, uint32_t desc) +{ + int i, j, ofs; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + ofs = LSX_LEN / 64; + vec_clear_cause(env); + for (i = 0; i < oprsz / 16; i++) { + for (j = 0; j < ofs; j++) { + temp.UD(j + ofs * i) = float32_cvt_float64(Vj->UW(j + ofs * (2 * i + 1)), + &env->fp_status); + } + vec_update_fcsr0(env, GETPC()); + } + *Vd = temp; +} + +void HELPER(vfcvt_h_s)(void *vd, void *vj, void *vk, + CPULoongArchState *env, uint32_t desc) +{ + int i, j, ofs; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + ofs = LSX_LEN / 32; + vec_clear_cause(env); + for(i = 0; i < oprsz / 16; i++) { + for (j = 0; j < ofs; j++) { + temp.UH(j + ofs * (2 * i + 1)) = float32_cvt_float16(Vj->UW(j + ofs * i), + &env->fp_status); + temp.UH(j + ofs * 2 * i) = float32_cvt_float16(Vk->UW(j + ofs * i), + &env->fp_status); + } + vec_update_fcsr0(env, GETPC()); + } + *Vd = temp; +} + +void HELPER(vfcvt_s_d)(void *vd, void *vj, void *vk, + CPULoongArchState *env, uint32_t desc) +{ + int i, j, ofs; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + ofs = LSX_LEN / 64; + vec_clear_cause(env); + for(i = 0; i < oprsz / 16; i++) { + for (j = 0; j < ofs; j++) { + temp.UW(j + ofs * (2 * i + 1)) = float64_cvt_float32(Vj->UD(j + ofs * i), + &env->fp_status); + temp.UW(j + ofs * 2 * i) = float64_cvt_float32(Vk->UD(j + ofs * i), + &env->fp_status); + } + vec_update_fcsr0(env, GETPC()); + } + *Vd = temp; +} + +void HELPER(vfrint_s)(void *vd, void *vj, + CPULoongArchState *env, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + vec_clear_cause(env); + for (i = 0; i < oprsz / 4; i++) { + Vd->W(i) = float32_round_to_int(Vj->UW(i), &env->fp_status); + vec_update_fcsr0(env, GETPC()); + } +} + +void HELPER(vfrint_d)(void *vd, void *vj, + CPULoongArchState *env, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + vec_clear_cause(env); + for (i = 0; i < oprsz / 8; i++) { + Vd->D(i) = float64_round_to_int(Vj->UD(i), &env->fp_status); + vec_update_fcsr0(env, GETPC()); + } +} + +#define FCVT_2OP(NAME, BIT, E, MODE) \ +void HELPER(NAME)(void *vd, void *vj, \ + CPULoongArchState *env, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + vec_clear_cause(env); \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); \ + set_float_rounding_mode(MODE, &env->fp_status); \ + Vd->E(i) = float## BIT ## _round_to_int(Vj->E(i), &env->fp_status); \ + set_float_rounding_mode(old_mode, &env->fp_status); \ + vec_update_fcsr0(env, GETPC()); \ + } \ +} + +FCVT_2OP(vfrintrne_s, 32, UW, float_round_nearest_even) +FCVT_2OP(vfrintrne_d, 64, UD, float_round_nearest_even) +FCVT_2OP(vfrintrz_s, 32, UW, float_round_to_zero) +FCVT_2OP(vfrintrz_d, 64, UD, float_round_to_zero) +FCVT_2OP(vfrintrp_s, 32, UW, float_round_up) +FCVT_2OP(vfrintrp_d, 64, UD, float_round_up) +FCVT_2OP(vfrintrm_s, 32, UW, float_round_down) +FCVT_2OP(vfrintrm_d, 64, UD, float_round_down) + +#define FTINT(NAME, FMT1, FMT2, T1, T2, MODE) \ +static T2 do_ftint ## NAME(CPULoongArchState *env, T1 fj) \ +{ \ + T2 fd; \ + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); \ + \ + set_float_rounding_mode(MODE, &env->fp_status); \ + fd = do_## FMT1 ##_to_## FMT2(env, fj); \ + set_float_rounding_mode(old_mode, &env->fp_status); \ + return fd; \ +} + +#define DO_FTINT(FMT1, FMT2, T1, T2) \ +static T2 do_## FMT1 ##_to_## FMT2(CPULoongArchState *env, T1 fj) \ +{ \ + T2 fd; \ + \ + fd = FMT1 ##_to_## FMT2(fj, &env->fp_status); \ + if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { \ + if (FMT1 ##_is_any_nan(fj)) { \ + fd = 0; \ + } \ + } \ + vec_update_fcsr0(env, GETPC()); \ + return fd; \ +} + +DO_FTINT(float32, int32, uint32_t, uint32_t) +DO_FTINT(float64, int64, uint64_t, uint64_t) +DO_FTINT(float32, uint32, uint32_t, uint32_t) +DO_FTINT(float64, uint64, uint64_t, uint64_t) +DO_FTINT(float64, int32, uint64_t, uint32_t) +DO_FTINT(float32, int64, uint32_t, uint64_t) + +FTINT(rne_w_s, float32, int32, uint32_t, uint32_t, float_round_nearest_even) +FTINT(rne_l_d, float64, int64, uint64_t, uint64_t, float_round_nearest_even) +FTINT(rp_w_s, float32, int32, uint32_t, uint32_t, float_round_up) +FTINT(rp_l_d, float64, int64, uint64_t, uint64_t, float_round_up) +FTINT(rz_w_s, float32, int32, uint32_t, uint32_t, float_round_to_zero) +FTINT(rz_l_d, float64, int64, uint64_t, uint64_t, float_round_to_zero) +FTINT(rm_w_s, float32, int32, uint32_t, uint32_t, float_round_down) +FTINT(rm_l_d, float64, int64, uint64_t, uint64_t, float_round_down) + +DO_2OP_F(vftintrne_w_s, 32, UW, do_ftintrne_w_s) +DO_2OP_F(vftintrne_l_d, 64, UD, do_ftintrne_l_d) +DO_2OP_F(vftintrp_w_s, 32, UW, do_ftintrp_w_s) +DO_2OP_F(vftintrp_l_d, 64, UD, do_ftintrp_l_d) +DO_2OP_F(vftintrz_w_s, 32, UW, do_ftintrz_w_s) +DO_2OP_F(vftintrz_l_d, 64, UD, do_ftintrz_l_d) +DO_2OP_F(vftintrm_w_s, 32, UW, do_ftintrm_w_s) +DO_2OP_F(vftintrm_l_d, 64, UD, do_ftintrm_l_d) +DO_2OP_F(vftint_w_s, 32, UW, do_float32_to_int32) +DO_2OP_F(vftint_l_d, 64, UD, do_float64_to_int64) + +FTINT(rz_wu_s, float32, uint32, uint32_t, uint32_t, float_round_to_zero) +FTINT(rz_lu_d, float64, uint64, uint64_t, uint64_t, float_round_to_zero) + +DO_2OP_F(vftintrz_wu_s, 32, UW, do_ftintrz_wu_s) +DO_2OP_F(vftintrz_lu_d, 64, UD, do_ftintrz_lu_d) +DO_2OP_F(vftint_wu_s, 32, UW, do_float32_to_uint32) +DO_2OP_F(vftint_lu_d, 64, UD, do_float64_to_uint64) + +FTINT(rm_w_d, float64, int32, uint64_t, uint32_t, float_round_down) +FTINT(rp_w_d, float64, int32, uint64_t, uint32_t, float_round_up) +FTINT(rz_w_d, float64, int32, uint64_t, uint32_t, float_round_to_zero) +FTINT(rne_w_d, float64, int32, uint64_t, uint32_t, float_round_nearest_even) + +#define FTINT_W_D(NAME, FN) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, \ + CPULoongArchState *env, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / 64; \ + vec_clear_cause(env); \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.W(j + ofs * (2 * i + 1)) = FN(env, Vj->UD(j + ofs * i)); \ + temp.W(j + ofs * 2 * i) = FN(env, Vk->UD(j + ofs * i)); \ + } \ + } \ + *Vd = temp; \ +} + +FTINT_W_D(vftint_w_d, do_float64_to_int32) +FTINT_W_D(vftintrm_w_d, do_ftintrm_w_d) +FTINT_W_D(vftintrp_w_d, do_ftintrp_w_d) +FTINT_W_D(vftintrz_w_d, do_ftintrz_w_d) +FTINT_W_D(vftintrne_w_d, do_ftintrne_w_d) + +FTINT(rml_l_s, float32, int64, uint32_t, uint64_t, float_round_down) +FTINT(rpl_l_s, float32, int64, uint32_t, uint64_t, float_round_up) +FTINT(rzl_l_s, float32, int64, uint32_t, uint64_t, float_round_to_zero) +FTINT(rnel_l_s, float32, int64, uint32_t, uint64_t, float_round_nearest_even) +FTINT(rmh_l_s, float32, int64, uint32_t, uint64_t, float_round_down) +FTINT(rph_l_s, float32, int64, uint32_t, uint64_t, float_round_up) +FTINT(rzh_l_s, float32, int64, uint32_t, uint64_t, float_round_to_zero) +FTINT(rneh_l_s, float32, int64, uint32_t, uint64_t, float_round_nearest_even) + +#define FTINTL_L_S(NAME, FN) \ +void HELPER(NAME)(void *vd, void *vj, \ + CPULoongArchState *env, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / 64; \ + vec_clear_cause(env); \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.D(j + ofs * i) = FN(env, Vj->UW(j + ofs * 2 * i)); \ + } \ + } \ + *Vd = temp; \ +} + +FTINTL_L_S(vftintl_l_s, do_float32_to_int64) +FTINTL_L_S(vftintrml_l_s, do_ftintrml_l_s) +FTINTL_L_S(vftintrpl_l_s, do_ftintrpl_l_s) +FTINTL_L_S(vftintrzl_l_s, do_ftintrzl_l_s) +FTINTL_L_S(vftintrnel_l_s, do_ftintrnel_l_s) + +#define FTINTH_L_S(NAME, FN) \ +void HELPER(NAME)(void *vd, void *vj, \ + CPULoongArchState *env, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / 64; \ + vec_clear_cause(env); \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.D(j + ofs * i) = FN(env, Vj->UW(j + ofs * (2 * i + 1))); \ + } \ + } \ + *Vd = temp; \ +} + +FTINTH_L_S(vftinth_l_s, do_float32_to_int64) +FTINTH_L_S(vftintrmh_l_s, do_ftintrmh_l_s) +FTINTH_L_S(vftintrph_l_s, do_ftintrph_l_s) +FTINTH_L_S(vftintrzh_l_s, do_ftintrzh_l_s) +FTINTH_L_S(vftintrneh_l_s, do_ftintrneh_l_s) + +#define FFINT(NAME, FMT1, FMT2, T1, T2) \ +static T2 do_ffint_ ## NAME(CPULoongArchState *env, T1 fj) \ +{ \ + T2 fd; \ + \ + fd = FMT1 ##_to_## FMT2(fj, &env->fp_status); \ + vec_update_fcsr0(env, GETPC()); \ + return fd; \ +} + +FFINT(s_w, int32, float32, int32_t, uint32_t) +FFINT(d_l, int64, float64, int64_t, uint64_t) +FFINT(s_wu, uint32, float32, uint32_t, uint32_t) +FFINT(d_lu, uint64, float64, uint64_t, uint64_t) + +DO_2OP_F(vffint_s_w, 32, W, do_ffint_s_w) +DO_2OP_F(vffint_d_l, 64, D, do_ffint_d_l) +DO_2OP_F(vffint_s_wu, 32, UW, do_ffint_s_wu) +DO_2OP_F(vffint_d_lu, 64, UD, do_ffint_d_lu) + +void HELPER(vffintl_d_w)(void *vd, void *vj, + CPULoongArchState *env, uint32_t desc) +{ + int i, j, ofs; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + ofs = LSX_LEN / 64; + vec_clear_cause(env); + for (i = 0; i < oprsz / 16; i++) { + for (j = 0; j < ofs; j++) { + temp.D(j + ofs * i) = int32_to_float64(Vj->W(j + ofs * 2 * i), + &env->fp_status); + } + vec_update_fcsr0(env, GETPC()); + } + *Vd = temp; +} + +void HELPER(vffinth_d_w)(void *vd, void *vj, + CPULoongArchState *env, uint32_t desc) +{ + int i, j, ofs; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + ofs = LSX_LEN / 64; + vec_clear_cause(env); + for (i = 0; i < oprsz /16; i++) { + for (j = 0; j < ofs; j++) { + temp.D(j + ofs * i) = int32_to_float64(Vj->W(j + ofs * (2 * i + 1)), + &env->fp_status); + } + vec_update_fcsr0(env, GETPC()); + } + *Vd = temp; +} + +void HELPER(vffint_s_l)(void *vd, void *vj, void *vk, + CPULoongArchState *env, uint32_t desc) +{ + int i, j, ofs; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + int oprsz = simd_oprsz(desc); + + ofs = LSX_LEN / 64; + vec_clear_cause(env); + for (i = 0; i < oprsz / 16; i++) { + for (j = 0; j < ofs; j++) { + temp.W(j + ofs * (2 * i + 1)) = int64_to_float32(Vj->D(j + ofs * i), + &env->fp_status); + temp.W(j + ofs * 2 * i) = int64_to_float32(Vk->D(j + ofs * i), + &env->fp_status); + } + vec_update_fcsr0(env, GETPC()); + } + *Vd = temp; +} + +#define VCMPI(NAME, BIT, E, DO_OP) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + typedef __typeof(Vd->E(0)) TD; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = DO_OP(Vj->E(i), (TD)imm); \ + } \ +} + +VCMPI(vseqi_b, 8, B, VSEQ) +VCMPI(vseqi_h, 16, H, VSEQ) +VCMPI(vseqi_w, 32, W, VSEQ) +VCMPI(vseqi_d, 64, D, VSEQ) +VCMPI(vslei_b, 8, B, VSLE) +VCMPI(vslei_h, 16, H, VSLE) +VCMPI(vslei_w, 32, W, VSLE) +VCMPI(vslei_d, 64, D, VSLE) +VCMPI(vslei_bu, 8, UB, VSLE) +VCMPI(vslei_hu, 16, UH, VSLE) +VCMPI(vslei_wu, 32, UW, VSLE) +VCMPI(vslei_du, 64, UD, VSLE) +VCMPI(vslti_b, 8, B, VSLT) +VCMPI(vslti_h, 16, H, VSLT) +VCMPI(vslti_w, 32, W, VSLT) +VCMPI(vslti_d, 64, D, VSLT) +VCMPI(vslti_bu, 8, UB, VSLT) +VCMPI(vslti_hu, 16, UH, VSLT) +VCMPI(vslti_wu, 32, UW, VSLT) +VCMPI(vslti_du, 64, UD, VSLT) + +static uint64_t vfcmp_common(CPULoongArchState *env, + FloatRelation cmp, uint32_t flags) +{ + uint64_t ret = 0; + + switch (cmp) { + case float_relation_less: + ret = (flags & FCMP_LT); + break; + case float_relation_equal: + ret = (flags & FCMP_EQ); + break; + case float_relation_greater: + ret = (flags & FCMP_GT); + break; + case float_relation_unordered: + ret = (flags & FCMP_UN); + break; + default: + g_assert_not_reached(); + } + + if (ret) { + ret = -1; + } + + return ret; +} + +#define VFCMP(NAME, BIT, E, FN) \ +void HELPER(NAME)(CPULoongArchState *env, uint32_t oprsz, \ + uint32_t vd, uint32_t vj, uint32_t vk, uint32_t flags) \ +{ \ + int i; \ + VReg t; \ + VReg *Vd = &(env->fpr[vd].vreg); \ + VReg *Vj = &(env->fpr[vj].vreg); \ + VReg *Vk = &(env->fpr[vk].vreg); \ + \ + vec_clear_cause(env); \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + FloatRelation cmp; \ + cmp = FN(Vj->E(i), Vk->E(i), &env->fp_status); \ + t.E(i) = vfcmp_common(env, cmp, flags); \ + vec_update_fcsr0(env, GETPC()); \ + } \ + *Vd = t; \ +} + +VFCMP(vfcmp_c_s, 32, UW, float32_compare_quiet) +VFCMP(vfcmp_s_s, 32, UW, float32_compare) +VFCMP(vfcmp_c_d, 64, UD, float64_compare_quiet) +VFCMP(vfcmp_s_d, 64, UD, float64_compare) + +void HELPER(vbitseli_b)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + + for (i = 0; i < simd_oprsz(desc); i++) { + Vd->B(i) = (~Vd->B(i) & Vj->B(i)) | (Vd->B(i) & imm); + } +} + +/* Copy from target/arm/tcg/sve_helper.c */ +static inline bool do_match2(uint64_t n, uint64_t m0, uint64_t m1, int esz) +{ + int bits = 8 << esz; + uint64_t ones = dup_const(esz, 1); + uint64_t signs = ones << (bits - 1); + uint64_t cmp0, cmp1; + + cmp1 = dup_const(esz, n); + cmp0 = cmp1 ^ m0; + cmp1 = cmp1 ^ m1; + cmp0 = (cmp0 - ones) & ~cmp0; + cmp1 = (cmp1 - ones) & ~cmp1; + return (cmp0 | cmp1) & signs; +} + +#define SETANYEQZ(NAME, MO) \ +void HELPER(NAME)(CPULoongArchState *env, \ + uint32_t oprsz, uint32_t cd, uint32_t vj) \ +{ \ + VReg *Vj = &(env->fpr[vj].vreg); \ + \ + env->cf[cd & 0x7] = do_match2(0, Vj->D(0), Vj->D(1), MO); \ + if (oprsz == 32) { \ + env->cf[cd & 0x7] = env->cf[cd & 0x7] || \ + do_match2(0, Vj->D(2), Vj->D(3), MO); \ + } \ +} + +SETANYEQZ(vsetanyeqz_b, MO_8) +SETANYEQZ(vsetanyeqz_h, MO_16) +SETANYEQZ(vsetanyeqz_w, MO_32) +SETANYEQZ(vsetanyeqz_d, MO_64) + +#define SETALLNEZ(NAME, MO) \ +void HELPER(NAME)(CPULoongArchState *env, \ + uint32_t oprsz, uint32_t cd, uint32_t vj) \ +{ \ + VReg *Vj = &(env->fpr[vj].vreg); \ + \ + env->cf[cd & 0x7]= !do_match2(0, Vj->D(0), Vj->D(1), MO); \ + if (oprsz == 32) { \ + env->cf[cd & 0x7] = env->cf[cd & 0x7] && \ + !do_match2(0, Vj->D(2), Vj->D(3), MO); \ + } \ +} + +SETALLNEZ(vsetallnez_b, MO_8) +SETALLNEZ(vsetallnez_h, MO_16) +SETALLNEZ(vsetallnez_w, MO_32) +SETALLNEZ(vsetallnez_d, MO_64) + +#define XVINSVE0(NAME, E, MASK) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + Vd->E(imm & MASK) = Vj->E(0); \ +} + +XVINSVE0(xvinsve0_w, W, 0x7) +XVINSVE0(xvinsve0_d, D, 0x3) + +#define XVPICKVE(NAME, E, BIT, MASK) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + Vd->E(0) = Vj->E(imm & MASK); \ + for (i = 1; i < oprsz / (BIT / 8); i++) { \ + Vd->E(i) = 0; \ + } \ +} + +XVPICKVE(xvpickve_w, W, 32, 0x7) +XVPICKVE(xvpickve_d, D, 64, 0x3) + +#define VPACKEV(NAME, BIT, E) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + temp.E(2 * i + 1) = Vj->E(2 * i); \ + temp.E(2 *i) = Vk->E(2 * i); \ + } \ + *Vd = temp; \ +} + +VPACKEV(vpackev_b, 16, B) +VPACKEV(vpackev_h, 32, H) +VPACKEV(vpackev_w, 64, W) +VPACKEV(vpackev_d, 128, D) + +#define VPACKOD(NAME, BIT, E) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + temp.E(2 * i + 1) = Vj->E(2 * i + 1); \ + temp.E(2 * i) = Vk->E(2 * i + 1); \ + } \ + *Vd = temp; \ +} + +VPACKOD(vpackod_b, 16, B) +VPACKOD(vpackod_h, 32, H) +VPACKOD(vpackod_w, 64, W) +VPACKOD(vpackod_d, 128, D) + +#define VPICKEV(NAME, BIT, E) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E(j + ofs * (2 * i + 1)) = Vj->E(2 * (j + ofs * i)); \ + temp.E(j + ofs * 2 * i) = Vk->E(2 * (j + ofs * i)); \ + } \ + } \ + *Vd = temp; \ +} + +VPICKEV(vpickev_b, 16, B) +VPICKEV(vpickev_h, 32, H) +VPICKEV(vpickev_w, 64, W) +VPICKEV(vpickev_d, 128, D) + +#define VPICKOD(NAME, BIT, E) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E(j + ofs * (2 * i + 1)) = Vj->E(2 * (j + ofs * i) + 1); \ + temp.E(j + ofs * 2 * i) = Vk->E(2 * (j + ofs * i) + 1); \ + } \ + } \ + *Vd = temp; \ +} + +VPICKOD(vpickod_b, 16, B) +VPICKOD(vpickod_h, 32, H) +VPICKOD(vpickod_w, 64, W) +VPICKOD(vpickod_d, 128, D) + +#define VILVL(NAME, BIT, E) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E(2 * (j + ofs * i) + 1) = Vj->E(j + ofs * 2 * i); \ + temp.E(2 * (j + ofs * i)) = Vk->E(j + ofs * 2 * i); \ + } \ + } \ + *Vd = temp; \ +} + +VILVL(vilvl_b, 16, B) +VILVL(vilvl_h, 32, H) +VILVL(vilvl_w, 64, W) +VILVL(vilvl_d, 128, D) + +#define VILVH(NAME, BIT, E) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, ofs; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + ofs = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / 16; i++) { \ + for (j = 0; j < ofs; j++) { \ + temp.E(2 * (j + ofs * i) + 1) = Vj->E(j + ofs * (2 * i + 1)); \ + temp.E(2 * (j + ofs * i)) = Vk->E(j + ofs * (2 * i + 1)); \ + } \ + } \ + *Vd = temp; \ +} + +VILVH(vilvh_b, 16, B) +VILVH(vilvh_h, 32, H) +VILVH(vilvh_w, 64, W) +VILVH(vilvh_d, 128, D) + +void HELPER(vshuf_b)(void *vd, void *vj, void *vk, void *va, uint32_t desc) +{ + int i, j, m; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + VReg *Va = (VReg *)va; + int oprsz = simd_oprsz(desc); + + m = LSX_LEN / 8; + for (i = 0; i < (oprsz / 16) * m; i++) { + j = i < m ? 0 : 1; + uint64_t k = (uint8_t)Va->B(i) % (2 * m); + temp.B(i) = k < m ? Vk->B(k + j * m): Vj->B(k + (j - 1) * m); + } + *Vd = temp; +} + +#define VSHUF(NAME, BIT, E) \ +void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +{ \ + int i, j, m; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + VReg *Vk = (VReg *)vk; \ + int oprsz = simd_oprsz(desc); \ + \ + m = LSX_LEN / BIT; \ + for (i = 0; i < (oprsz / 16) * m; i++) { \ + j = i < m ? 0 : 1; \ + uint64_t k = ((uint8_t)Vd->E(i)) % (2 * m); \ + temp.E(i) = k < m ? Vk->E(k + j * m) : Vj->E(k + (j - 1) * m); \ + } \ + *Vd = temp; \ +} + +VSHUF(vshuf_h, 16, H) +VSHUF(vshuf_w, 32, W) +VSHUF(vshuf_d, 64, D) + +#define VSHUF4I(NAME, BIT, E) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, j, max; \ + VReg temp = {}; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + max = LSX_LEN / BIT; \ + for (i = 0; i < oprsz / (BIT / 8); i++) { \ + j = i < max ? 1 : 2; \ + temp.E(i) = Vj->E(SHF_POS(i - ((j -1)* max), imm) + (j - 1) * max); \ + } \ + *Vd = temp; \ +} + +VSHUF4I(vshuf4i_b, 8, B) +VSHUF4I(vshuf4i_h, 16, H) +VSHUF4I(vshuf4i_w, 32, W) + +void HELPER(vshuf4i_d)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + temp.D(2 * i) = (imm & 2 ? Vj : Vd)->D((imm & 1) + 2 * i); + temp.D(2 * i + 1) = (imm & 8 ? Vj : Vd)->D(((imm >> 2) & 1) + 2 * i); + } + *Vd = temp; +} + +void HELPER(vperm_w)(void *vd, void *vj, void *vk, uint32_t desc) +{ + int i, m; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + VReg *Vk = (VReg *)vk; + + m = LASX_LEN / 32; + for (i = 0; i < m ; i++) { + uint64_t k = (uint8_t)Vk->W(i) % 8; + temp.W(i) = Vj->W(k); + } + *Vd = temp; +} + +void HELPER(vpermi_w)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + int oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz / 16; i++) { + temp.W(4 * i) = Vj->W((imm & 0x3) + 4 * i); + temp.W(4 * i + 1) = Vj->W(((imm >> 2) & 0x3) + 4 * i); + temp.W(4 * i + 2) = Vd->W(((imm >> 4) & 0x3) + 4 * i); + temp.W(4 * i + 3) = Vd->W(((imm >> 6) & 0x3) + 4 * i); + } + *Vd = temp; +} + +void HELPER(vpermi_d)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + VReg temp = {}; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + + temp.D(0) = Vj->D(imm & 0x3); + temp.D(1) = Vj->D((imm >> 2) & 0x3); + temp.D(2) = Vj->D((imm >> 4) & 0x3); + temp.D(3) = Vj->D((imm >> 6) & 0x3); + *Vd = temp; +} + +void HELPER(vpermi_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +{ + int i; + VReg temp; + VReg *Vd = (VReg *)vd; + VReg *Vj = (VReg *)vj; + + for (i = 0; i < 2; i++, imm >>= 4) { + temp.Q(i) = (imm & 2 ? Vd: Vj)->Q(imm & 1); + } + *Vd = temp; +} + +#define VEXTRINS(NAME, BIT, E, MASK) \ +void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +{ \ + int i, ins, extr, max; \ + VReg *Vd = (VReg *)vd; \ + VReg *Vj = (VReg *)vj; \ + int oprsz = simd_oprsz(desc); \ + \ + max = LSX_LEN / BIT; \ + ins = (imm >> 4) & MASK; \ + extr = imm & MASK; \ + for (i = 0; i < oprsz / 16; i++) { \ + Vd->E(ins + i * max) = Vj->E(extr + i * max); \ + } \ +} + +VEXTRINS(vextrins_b, 8, B, 0xf) +VEXTRINS(vextrins_h, 16, H, 0x7) +VEXTRINS(vextrins_w, 32, W, 0x3) +VEXTRINS(vextrins_d, 64, D, 0x1) diff --git a/target/loongarch/trace-events b/target/loongarch/trace-events new file mode 100644 index 0000000000..dea11edc0f --- /dev/null +++ b/target/loongarch/trace-events @@ -0,0 +1,15 @@ +# See docs/devel/tracing.rst for syntax documentation. + +#kvm.c +kvm_failed_get_regs_core(const char *msg) "Failed to get core regs from KVM: %s" +kvm_failed_put_regs_core(const char *msg) "Failed to put core regs into KVM: %s" +kvm_failed_get_fpu(const char *msg) "Failed to get fpu from KVM: %s" +kvm_failed_put_fpu(const char *msg) "Failed to put fpu into KVM: %s" +kvm_failed_get_mpstate(const char *msg) "Failed to get mp_state from KVM: %s" +kvm_failed_put_mpstate(const char *msg) "Failed to put mp_state into KVM: %s" +kvm_failed_get_counter(const char *msg) "Failed to get counter from KVM: %s" +kvm_failed_put_counter(const char *msg) "Failed to put counter into KVM: %s" +kvm_failed_get_cpucfg(const char *msg) "Failed to get cpucfg from KVM: %s" +kvm_failed_put_cpucfg(const char *msg) "Failed to put cpucfg into KVM: %s" +kvm_arch_handle_exit(int num) "kvm arch handle exit, the reason number: %d" +kvm_set_intr(int irq, int level) "kvm set interrupt, irq num: %d, level: %d" diff --git a/target/loongarch/trace.h b/target/loongarch/trace.h new file mode 100644 index 0000000000..c2ecb78f08 --- /dev/null +++ b/target/loongarch/trace.h @@ -0,0 +1 @@ +#include "trace/trace-target_loongarch.h" diff --git a/target/loongarch/translate.h b/target/loongarch/translate.h index 9cc12512d1..195f53573a 100644 --- a/target/loongarch/translate.h +++ b/target/loongarch/translate.h @@ -10,9 +10,21 @@ #include "exec/translator.h" -#define TRANS(NAME, FUNC, ...) \ +#define TRANS(NAME, AVAIL, FUNC, ...) \ static bool trans_##NAME(DisasContext *ctx, arg_##NAME * a) \ - { return FUNC(ctx, a, __VA_ARGS__); } + { return avail_##AVAIL(ctx) && FUNC(ctx, a, __VA_ARGS__); } + +#define avail_ALL(C) true +#define avail_64(C) (FIELD_EX32((C)->cpucfg1, CPUCFG1, ARCH) == \ + CPUCFG1_ARCH_LA64) +#define avail_FP(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, FP)) +#define avail_FP_SP(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, FP_SP)) +#define avail_FP_DP(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, FP_DP)) +#define avail_LSPW(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, LSPW)) +#define avail_LAM(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, LAM)) +#define avail_LSX(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, LSX)) +#define avail_LASX(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, LASX)) +#define avail_IOCSR(C) (FIELD_EX32((C)->cpucfg1, CPUCFG1, IOCSR)) /* * If an operation is being performed on less than TARGET_LONG_BITS, @@ -29,11 +41,14 @@ typedef struct DisasContext { DisasContextBase base; target_ulong page_start; uint32_t opcode; - int mem_idx; + uint16_t mem_idx; + uint16_t plv; + int vl; /* Vector length */ TCGv zero; - /* Space for 3 operands plus 1 extra for address computation. */ - TCGv temp[4]; - uint8_t ntemp; + bool la64; /* LoongArch64 mode */ + bool va32; /* 32-bit virtual address */ + uint32_t cpucfg1; + uint32_t cpucfg2; } DisasContext; void generate_exception(DisasContext *ctx, int excp); diff --git a/target/loongarch/vec.h b/target/loongarch/vec.h new file mode 100644 index 0000000000..3c9adf8427 --- /dev/null +++ b/target/loongarch/vec.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch vector utilitites + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_VEC_H +#define LOONGARCH_VEC_H + +#if HOST_BIG_ENDIAN +#define B(x) B[(x) ^ 15] +#define H(x) H[(x) ^ 7] +#define W(x) W[(x) ^ 3] +#define D(x) D[(x) ^ 1] +#define UB(x) UB[(x) ^ 15] +#define UH(x) UH[(x) ^ 7] +#define UW(x) UW[(x) ^ 3] +#define UD(x) UD[(x) ^ 1] +#define Q(x) Q[x] +#else +#define B(x) B[x] +#define H(x) H[x] +#define W(x) W[x] +#define D(x) D[x] +#define UB(x) UB[x] +#define UH(x) UH[x] +#define UW(x) UW[x] +#define UD(x) UD[x] +#define Q(x) Q[x] +#endif /* HOST_BIG_ENDIAN */ + +#define DO_ADD(a, b) (a + b) +#define DO_SUB(a, b) (a - b) +#define DO_VAVG(a, b) ((a >> 1) + (b >> 1) + (a & b & 1)) +#define DO_VAVGR(a, b) ((a >> 1) + (b >> 1) + ((a | b) & 1)) +#define DO_VABSD(a, b) ((a > b) ? (a -b) : (b-a)) +#define DO_VABS(a) ((a < 0) ? (-a) : (a)) +#define DO_MIN(a, b) (a < b ? a : b) +#define DO_MAX(a, b) (a > b ? a : b) +#define DO_MUL(a, b) (a * b) +#define DO_MADD(a, b, c) (a + b * c) +#define DO_MSUB(a, b, c) (a - b * c) + +#define DO_DIVU(N, M) (unlikely(M == 0) ? 0 : N / M) +#define DO_REMU(N, M) (unlikely(M == 0) ? 0 : N % M) +#define DO_DIV(N, M) (unlikely(M == 0) ? 0 :\ + unlikely((N == -N) && (M == (__typeof(N))(-1))) ? N : N / M) +#define DO_REM(N, M) (unlikely(M == 0) ? 0 :\ + unlikely((N == -N) && (M == (__typeof(N))(-1))) ? 0 : N % M) + +#define DO_SIGNCOV(a, b) (a == 0 ? 0 : a < 0 ? -b : b) + +#define R_SHIFT(a, b) (a >> b) + +#define DO_CLO_B(N) (clz32(~N & 0xff) - 24) +#define DO_CLO_H(N) (clz32(~N & 0xffff) - 16) +#define DO_CLO_W(N) (clz32(~N)) +#define DO_CLO_D(N) (clz64(~N)) +#define DO_CLZ_B(N) (clz32(N) - 24) +#define DO_CLZ_H(N) (clz32(N) - 16) +#define DO_CLZ_W(N) (clz32(N)) +#define DO_CLZ_D(N) (clz64(N)) + +#define DO_BITCLR(a, bit) (a & ~(1ull << bit)) +#define DO_BITSET(a, bit) (a | 1ull << bit) +#define DO_BITREV(a, bit) (a ^ (1ull << bit)) + +#define VSEQ(a, b) (a == b ? -1 : 0) +#define VSLE(a, b) (a <= b ? -1 : 0) +#define VSLT(a, b) (a < b ? -1 : 0) + +#define SHF_POS(i, imm) (((i) & 0xfc) + (((imm) >> (2 * ((i) & 0x03))) & 0x03)) + +#endif /* LOONGARCH_VEC_H */ diff --git a/target/m68k/Kconfig b/target/m68k/Kconfig index 23debad519..9eae71486f 100644 --- a/target/m68k/Kconfig +++ b/target/m68k/Kconfig @@ -1,2 +1,3 @@ config M68K bool + select SEMIHOSTING diff --git a/target/m68k/cpu-param.h b/target/m68k/cpu-param.h index 44a8d193f0..39dcbcece8 100644 --- a/target/m68k/cpu-param.h +++ b/target/m68k/cpu-param.h @@ -17,6 +17,5 @@ #define TARGET_PAGE_BITS 12 #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -#define NB_MMU_MODES 2 #endif diff --git a/target/m68k/cpu-qom.h b/target/m68k/cpu-qom.h index cd9687192c..273e8eae41 100644 --- a/target/m68k/cpu-qom.h +++ b/target/m68k/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU Motorola 68k CPU + * QEMU Motorola 68k CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -21,27 +21,12 @@ #define QEMU_M68K_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_M68K_CPU "m68k-cpu" OBJECT_DECLARE_CPU_TYPE(M68kCPU, M68kCPUClass, M68K_CPU) -/* - * M68kCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * - * A Motorola 68k CPU model. - */ -struct M68kCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceReset parent_reset; -}; - +#define M68K_CPU_TYPE_SUFFIX "-" TYPE_M68K_CPU +#define M68K_CPU_TYPE_NAME(model) model M68K_CPU_TYPE_SUFFIX #endif diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 5bbefda575..df49ff1880 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -31,37 +31,63 @@ static void m68k_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.pc = value; } +static vaddr m68k_cpu_get_pc(CPUState *cs) +{ + M68kCPU *cpu = M68K_CPU(cs); + + return cpu->env.pc; +} + +static void m68k_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + M68kCPU *cpu = M68K_CPU(cs); + int cc_op = data[1]; + + cpu->env.pc = data[0]; + if (cc_op != CC_OP_DYNAMIC) { + cpu->env.cc_op = cc_op; + } +} + static bool m68k_cpu_has_work(CPUState *cs) { return cs->interrupt_request & CPU_INTERRUPT_HARD; } +static int m68k_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return cpu_env(cs)->sr & SR_S ? MMU_KERNEL_IDX : MMU_USER_IDX; +} + static void m68k_set_feature(CPUM68KState *env, int feature) { - env->features |= (1u << feature); + env->features |= BIT_ULL(feature); } static void m68k_unset_feature(CPUM68KState *env, int feature) { - env->features &= (-1u - (1u << feature)); + env->features &= ~BIT_ULL(feature); } -static void m68k_cpu_reset(DeviceState *dev) +static void m68k_cpu_reset_hold(Object *obj) { - CPUState *s = CPU(dev); - M68kCPU *cpu = M68K_CPU(s); - M68kCPUClass *mcc = M68K_CPU_GET_CLASS(cpu); - CPUM68KState *env = &cpu->env; + CPUState *cs = CPU(obj); + M68kCPUClass *mcc = M68K_CPU_GET_CLASS(obj); + CPUM68KState *env = cpu_env(cs); floatx80 nan = floatx80_default_nan(NULL); int i; - mcc->parent_reset(dev); + if (mcc->parent_phases.hold) { + mcc->parent_phases.hold(obj); + } memset(env, 0, offsetof(CPUM68KState, end_reset_fields)); -#ifdef CONFIG_SOFTMMU - cpu_m68k_set_sr(env, SR_S | SR_I); -#else +#ifdef CONFIG_USER_ONLY cpu_m68k_set_sr(env, 0); +#else + cpu_m68k_set_sr(env, SR_S | SR_I); #endif for (i = 0; i < 8; i++) { env->fregs[i].d = nan; @@ -89,46 +115,44 @@ static ObjectClass *m68k_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf(M68K_CPU_TYPE_NAME("%s"), cpu_model); oc = object_class_by_name(typename); g_free(typename); - if (oc != NULL && (object_class_dynamic_cast(oc, TYPE_M68K_CPU) == NULL || - object_class_is_abstract(oc))) { - return NULL; - } + return oc; } static void m5206_cpu_initfn(Object *obj) { - M68kCPU *cpu = M68K_CPU(obj); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(CPU(obj)); m68k_set_feature(env, M68K_FEATURE_CF_ISA_A); + m68k_set_feature(env, M68K_FEATURE_MOVEFROMSR_PRIV); } /* Base feature set, including isns. for m68k family */ static void m68000_cpu_initfn(Object *obj) { - M68kCPU *cpu = M68K_CPU(obj); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(CPU(obj)); - m68k_set_feature(env, M68K_FEATURE_M68000); + m68k_set_feature(env, M68K_FEATURE_M68K); m68k_set_feature(env, M68K_FEATURE_USP); m68k_set_feature(env, M68K_FEATURE_WORD_INDEX); m68k_set_feature(env, M68K_FEATURE_MOVEP); } /* - * Adds BKPT, MOVE-from-SR *now priv instr, and MOVEC, MOVES, RTD + * Adds BKPT, MOVE-from-SR *now priv instr, and MOVEC, MOVES, RTD, + * format+vector in exception frame. */ static void m68010_cpu_initfn(Object *obj) { - M68kCPU *cpu = M68K_CPU(obj); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(CPU(obj)); m68000_cpu_initfn(obj); m68k_set_feature(env, M68K_FEATURE_M68010); m68k_set_feature(env, M68K_FEATURE_RTD); m68k_set_feature(env, M68K_FEATURE_BKPT); m68k_set_feature(env, M68K_FEATURE_MOVEC); + m68k_set_feature(env, M68K_FEATURE_MOVEFROMSR_PRIV); + m68k_set_feature(env, M68K_FEATURE_EXCEPTION_FORMAT_VEC); } /* @@ -140,8 +164,7 @@ static void m68010_cpu_initfn(Object *obj) */ static void m68020_cpu_initfn(Object *obj) { - M68kCPU *cpu = M68K_CPU(obj); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(CPU(obj)); m68010_cpu_initfn(obj); m68k_unset_feature(env, M68K_FEATURE_M68010); @@ -171,8 +194,7 @@ static void m68020_cpu_initfn(Object *obj) */ static void m68030_cpu_initfn(Object *obj) { - M68kCPU *cpu = M68K_CPU(obj); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(CPU(obj)); m68020_cpu_initfn(obj); m68k_unset_feature(env, M68K_FEATURE_M68020); @@ -198,8 +220,7 @@ static void m68030_cpu_initfn(Object *obj) */ static void m68040_cpu_initfn(Object *obj) { - M68kCPU *cpu = M68K_CPU(obj); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(CPU(obj)); m68030_cpu_initfn(obj); m68k_unset_feature(env, M68K_FEATURE_M68030); @@ -219,8 +240,7 @@ static void m68040_cpu_initfn(Object *obj) */ static void m68060_cpu_initfn(Object *obj) { - M68kCPU *cpu = M68K_CPU(obj); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(CPU(obj)); m68040_cpu_initfn(obj); m68k_unset_feature(env, M68K_FEATURE_M68040); @@ -233,20 +253,19 @@ static void m68060_cpu_initfn(Object *obj) static void m5208_cpu_initfn(Object *obj) { - M68kCPU *cpu = M68K_CPU(obj); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(CPU(obj)); m68k_set_feature(env, M68K_FEATURE_CF_ISA_A); m68k_set_feature(env, M68K_FEATURE_CF_ISA_APLUSC); m68k_set_feature(env, M68K_FEATURE_BRAL); m68k_set_feature(env, M68K_FEATURE_CF_EMAC); m68k_set_feature(env, M68K_FEATURE_USP); + m68k_set_feature(env, M68K_FEATURE_MOVEFROMSR_PRIV); } static void cfv4e_cpu_initfn(Object *obj) { - M68kCPU *cpu = M68K_CPU(obj); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(CPU(obj)); m68k_set_feature(env, M68K_FEATURE_CF_ISA_A); m68k_set_feature(env, M68K_FEATURE_CF_ISA_B); @@ -254,12 +273,12 @@ static void cfv4e_cpu_initfn(Object *obj) m68k_set_feature(env, M68K_FEATURE_CF_FPU); m68k_set_feature(env, M68K_FEATURE_CF_EMAC); m68k_set_feature(env, M68K_FEATURE_USP); + m68k_set_feature(env, M68K_FEATURE_MOVEFROMSR_PRIV); } static void any_cpu_initfn(Object *obj) { - M68kCPU *cpu = M68K_CPU(obj); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(CPU(obj)); m68k_set_feature(env, M68K_FEATURE_CF_ISA_A); m68k_set_feature(env, M68K_FEATURE_CF_ISA_B); @@ -275,6 +294,7 @@ static void any_cpu_initfn(Object *obj) m68k_set_feature(env, M68K_FEATURE_USP); m68k_set_feature(env, M68K_FEATURE_EXT_FULL); m68k_set_feature(env, M68K_FEATURE_WORD_INDEX); + m68k_set_feature(env, M68K_FEATURE_MOVEFROMSR_PRIV); } static void m68k_cpu_realizefn(DeviceState *dev, Error **errp) @@ -300,14 +320,7 @@ static void m68k_cpu_realizefn(DeviceState *dev, Error **errp) mcc->parent_realize(dev, errp); } -static void m68k_cpu_initfn(Object *obj) -{ - M68kCPU *cpu = M68K_CPU(obj); - - cpu_set_cpustate_pointers(cpu); -} - -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) static bool fpu_needed(void *opaque) { M68kCPU *s = opaque; @@ -362,7 +375,7 @@ static const VMStateDescription vmstate_freg_tmp = { .name = "freg_tmp", .post_load = freg_post_load, .pre_save = freg_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(tmp_mant, m68k_FPReg_tmp), VMSTATE_UINT16(tmp_exp, m68k_FPReg_tmp), VMSTATE_END_OF_LIST() @@ -371,18 +384,25 @@ static const VMStateDescription vmstate_freg_tmp = { static const VMStateDescription vmstate_freg = { .name = "freg", - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_WITH_TMP(FPReg, m68k_FPReg_tmp, vmstate_freg_tmp), VMSTATE_END_OF_LIST() } }; +static int fpu_pre_save(void *opaque) +{ + M68kCPU *s = opaque; + + s->env.fpsr = cpu_m68k_get_fpsr(&s->env); + return 0; +} + static int fpu_post_load(void *opaque, int version) { M68kCPU *s = opaque; - cpu_m68k_restore_fp_status(&s->env); - + cpu_m68k_set_fpsr(&s->env, s->env.fpsr); return 0; } @@ -391,8 +411,9 @@ const VMStateDescription vmmstate_fpu = { .version_id = 1, .minimum_version_id = 1, .needed = fpu_needed, + .pre_save = fpu_pre_save, .post_load = fpu_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.fpcr, M68kCPU), VMSTATE_UINT32(env.fpsr, M68kCPU), VMSTATE_STRUCT_ARRAY(env.fregs, M68kCPU, 8, 0, vmstate_freg, FPReg), @@ -413,7 +434,7 @@ const VMStateDescription vmstate_cf_spregs = { .version_id = 1, .minimum_version_id = 1, .needed = cf_spregs_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.macc, M68kCPU, 4), VMSTATE_UINT32(env.macsr, M68kCPU), VMSTATE_UINT32(env.mac_mask, M68kCPU), @@ -435,7 +456,7 @@ const VMStateDescription vmstate_68040_mmu = { .version_id = 1, .minimum_version_id = 1, .needed = cpu_68040_mmu_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.mmu.ar, M68kCPU), VMSTATE_UINT32(env.mmu.ssw, M68kCPU), VMSTATE_UINT16(env.mmu.tcr, M68kCPU), @@ -460,7 +481,7 @@ const VMStateDescription vmstate_68040_spregs = { .version_id = 1, .minimum_version_id = 1, .needed = cpu_68040_spregs_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(env.vbr, M68kCPU), VMSTATE_UINT32(env.cacr, M68kCPU), VMSTATE_UINT32(env.sfc, M68kCPU), @@ -473,7 +494,7 @@ static const VMStateDescription vmstate_m68k_cpu = { .name = "cpu", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(env.dregs, M68kCPU, 8), VMSTATE_UINT32_ARRAY(env.aregs, M68kCPU, 8), VMSTATE_UINT32(env.pc, M68kCPU), @@ -490,7 +511,7 @@ static const VMStateDescription vmstate_m68k_cpu = { VMSTATE_INT32(env.pending_level, M68kCPU), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmmstate_fpu, &vmstate_cf_spregs, &vmstate_68040_mmu, @@ -498,20 +519,19 @@ static const VMStateDescription vmstate_m68k_cpu = { NULL }, }; -#endif -#ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps m68k_sysemu_ops = { .get_phys_page_debug = m68k_cpu_get_phys_page_debug, }; -#endif +#endif /* !CONFIG_USER_ONLY */ #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps m68k_tcg_ops = { +static const TCGCPUOps m68k_tcg_ops = { .initialize = m68k_tcg_init, + .restore_state_to_opc = m68k_restore_state_to_opc, #ifndef CONFIG_USER_ONLY .tlb_fill = m68k_cpu_tlb_fill, @@ -526,24 +546,27 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data) M68kCPUClass *mcc = M68K_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); DeviceClass *dc = DEVICE_CLASS(c); + ResettableClass *rc = RESETTABLE_CLASS(c); device_class_set_parent_realize(dc, m68k_cpu_realizefn, &mcc->parent_realize); - device_class_set_parent_reset(dc, m68k_cpu_reset, &mcc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, m68k_cpu_reset_hold, NULL, + &mcc->parent_phases); cc->class_by_name = m68k_cpu_class_by_name; cc->has_work = m68k_cpu_has_work; + cc->mmu_index = m68k_cpu_mmu_index; cc->dump_state = m68k_cpu_dump_state; cc->set_pc = m68k_cpu_set_pc; + cc->get_pc = m68k_cpu_get_pc; cc->gdb_read_register = m68k_cpu_gdb_read_register; cc->gdb_write_register = m68k_cpu_gdb_write_register; -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) dc->vmsd = &vmstate_m68k_cpu; cc->sysemu_ops = &m68k_sysemu_ops; #endif cc->disas_set_info = m68k_cpu_disas_set_info; - cc->gdb_num_core_regs = 18; cc->tcg_ops = &m68k_tcg_ops; } @@ -582,7 +605,7 @@ static const TypeInfo m68k_cpus_type_infos[] = { .name = TYPE_M68K_CPU, .parent = TYPE_CPU, .instance_size = sizeof(M68kCPU), - .instance_init = m68k_cpu_initfn, + .instance_align = __alignof(M68kCPU), .abstract = true, .class_size = sizeof(M68kCPUClass), .class_init = m68k_cpu_class_init, diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 4d8f48e8c7..e184239a81 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -154,7 +154,7 @@ typedef struct CPUArchState { struct {} end_reset_fields; /* Fields from here on are preserved across CPU reset. */ - uint32_t features; + uint64_t features; } CPUM68KState; /* @@ -164,21 +164,31 @@ typedef struct CPUArchState { * A Motorola 68k CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUM68KState env; }; +/* + * M68kCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A Motorola 68k CPU model. + */ +struct M68kCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; #ifndef CONFIG_USER_ONLY void m68k_cpu_do_interrupt(CPUState *cpu); bool m68k_cpu_exec_interrupt(CPUState *cpu, int int_req); +hwaddr m68k_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #endif /* !CONFIG_USER_ONLY */ 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, GByteArray *buf, int reg); int m68k_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); @@ -189,7 +199,8 @@ void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t); void cpu_m68k_set_sr(CPUM68KState *env, uint32_t); void cpu_m68k_restore_fp_status(CPUM68KState *env); void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val); - +uint32_t cpu_m68k_get_fpsr(CPUM68KState *env); +void cpu_m68k_set_fpsr(CPUM68KState *env, uint32_t val); /* * Instead of computing the condition codes after each m68k instruction, @@ -468,10 +479,11 @@ void do_m68k_semihosting(CPUM68KState *env, int nr); * The 68000 family is defined in six main CPU classes, the 680[012346]0. * Generally each successive CPU adds enhanced data/stack/instructions. * However, some features are only common to one, or a few classes. - * The features covers those subsets of instructons. + * The features cover those subsets of instructions. * - * CPU32/32+ are basically 680010 compatible with some 68020 class instructons, - * and some additional CPU32 instructions. Mostly Supervisor state differences. + * CPU32/32+ are basically 680010 compatible with some 68020 class + * instructions, and some additional CPU32 instructions. Mostly Supervisor + * state differences. * * The ColdFire core ISA is a RISC-style reduction of the 68000 series cpu. * There are 4 ColdFire core ISA revisions: A, A+, B and C. @@ -480,8 +492,9 @@ void do_m68k_semihosting(CPUM68KState *env, int nr); */ enum m68k_features { - /* Base m68k instruction set */ - M68K_FEATURE_M68000, + /* Base Motorola CPU set (not set for Coldfire CPUs) */ + M68K_FEATURE_M68K, + /* Motorola CPU feature sets */ M68K_FEATURE_M68010, M68K_FEATURE_M68020, M68K_FEATURE_M68030, @@ -536,15 +549,17 @@ enum m68k_features { M68K_FEATURE_UNALIGNED_DATA, /* TRAPcc insn. (680[2346]0, and CPU32) */ M68K_FEATURE_TRAPCC, + /* MOVE from SR privileged (from 68010) */ + M68K_FEATURE_MOVEFROMSR_PRIV, + /* Exception frame with format+vector (from 68010) */ + M68K_FEATURE_EXCEPTION_FORMAT_VEC, }; -static inline int m68k_feature(CPUM68KState *env, int feature) +static inline bool m68k_feature(CPUM68KState *env, int feature) { - return (env->features & (1u << feature)) != 0; + return (env->features & BIT_ULL(feature)) != 0; } -void m68k_cpu_list(void); - void register_m68k_insns (CPUM68KState *env); enum { @@ -561,27 +576,21 @@ enum { ACCESS_DATA = 0x20, /* Data load/store access */ }; -#define M68K_CPU_TYPE_SUFFIX "-" TYPE_M68K_CPU -#define M68K_CPU_TYPE_NAME(model) model M68K_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_M68K_CPU -#define cpu_list m68k_cpu_list - /* MMU modes definitions */ #define MMU_KERNEL_IDX 0 #define MMU_USER_IDX 1 -static inline int cpu_mmu_index (CPUM68KState *env, bool ifetch) -{ - return (env->sr & SR_S) == 0 ? 1 : 0; -} bool m68k_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +#ifndef CONFIG_USER_ONLY void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, unsigned size, MMUAccessType access_type, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr); +#endif #include "exec/cpu-all.h" @@ -596,8 +605,8 @@ void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, #define TB_FLAGS_TRACE 16 #define TB_FLAGS_TRACE_BIT (1 << TB_FLAGS_TRACE) -static inline void cpu_get_tb_cpu_state(CPUM68KState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) +static inline void cpu_get_tb_cpu_state(CPUM68KState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { *pc = env->pc; *cs_base = 0; diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index fdc4937e29..8314791f50 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -164,6 +164,78 @@ void HELPER(set_fpcr)(CPUM68KState *env, uint32_t val) cpu_m68k_set_fpcr(env, val); } +/* Convert host exception flags to cpu_m68k form. */ +static int cpu_m68k_exceptbits_from_host(int host_bits) +{ + int target_bits = 0; + + if (host_bits & float_flag_invalid) { + target_bits |= 0x80; + } + if (host_bits & float_flag_overflow) { + target_bits |= 0x40; + } + if (host_bits & (float_flag_underflow | float_flag_output_denormal)) { + target_bits |= 0x20; + } + if (host_bits & float_flag_divbyzero) { + target_bits |= 0x10; + } + if (host_bits & float_flag_inexact) { + target_bits |= 0x08; + } + return target_bits; +} + +/* Convert cpu_m68k exception flags to target form. */ +static int cpu_m68k_exceptbits_to_host(int target_bits) +{ + int host_bits = 0; + + if (target_bits & 0x80) { + host_bits |= float_flag_invalid; + } + if (target_bits & 0x40) { + host_bits |= float_flag_overflow; + } + if (target_bits & 0x20) { + host_bits |= float_flag_underflow; + } + if (target_bits & 0x10) { + host_bits |= float_flag_divbyzero; + } + if (target_bits & 0x08) { + host_bits |= float_flag_inexact; + } + return host_bits; +} + +uint32_t cpu_m68k_get_fpsr(CPUM68KState *env) +{ + int host_flags = get_float_exception_flags(&env->fp_status); + int target_flags = cpu_m68k_exceptbits_from_host(host_flags); + int except = (env->fpsr & ~(0xf8)) | target_flags; + return except; +} + +uint32_t HELPER(get_fpsr)(CPUM68KState *env) +{ + return cpu_m68k_get_fpsr(env); +} + +void cpu_m68k_set_fpsr(CPUM68KState *env, uint32_t val) +{ + env->fpsr = val; + + int host_flags = cpu_m68k_exceptbits_to_host((int) env->fpsr); + set_float_exception_flags(host_flags, &env->fp_status); +} + +void HELPER(set_fpsr)(CPUM68KState *env, uint32_t val) +{ + cpu_m68k_set_fpsr(env, val); +} + #define PREC_BEGIN(prec) \ do { \ FloatX80RoundPrec old = \ @@ -349,7 +421,7 @@ void HELPER(fsgldiv)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) PREC_END(); } -static int float_comp_to_cc(int float_compare) +static int float_comp_to_cc(FloatRelation float_compare) { switch (float_compare) { case float_relation_equal: @@ -367,7 +439,7 @@ static int float_comp_to_cc(int float_compare) void HELPER(fcmp)(CPUM68KState *env, FPReg *val0, FPReg *val1) { - int float_compare; + FloatRelation float_compare; float_compare = floatx80_compare(val1->d, val0->d, &env->fp_status); env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | float_comp_to_cc(float_compare); @@ -515,37 +587,50 @@ uint32_t HELPER(fmovemd_ld_postinc)(CPUM68KState *env, uint32_t addr, return fmovem_postinc(env, addr, mask, cpu_ld_float64_ra); } -static void make_quotient(CPUM68KState *env, floatx80 val) +static void make_quotient(CPUM68KState *env, int sign, uint32_t quotient) { - int32_t quotient; - int sign; - - if (floatx80_is_any_nan(val)) { - return; - } - - quotient = floatx80_to_int32(val, &env->fp_status); - sign = quotient < 0; - if (sign) { - quotient = -quotient; - } - quotient = (sign << 7) | (quotient & 0x7f); env->fpsr = (env->fpsr & ~FPSR_QT_MASK) | (quotient << FPSR_QT_SHIFT); } void HELPER(fmod)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) { - res->d = floatx80_mod(val1->d, val0->d, &env->fp_status); + uint64_t quotient; + int sign = extractFloatx80Sign(val1->d) ^ extractFloatx80Sign(val0->d); - make_quotient(env, res->d); + res->d = floatx80_modrem(val1->d, val0->d, true, "ient, + &env->fp_status); + + if (floatx80_is_any_nan(res->d)) { + return; + } + + make_quotient(env, sign, quotient); } void HELPER(frem)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) { - res->d = floatx80_rem(val1->d, val0->d, &env->fp_status); + FPReg fp_quot; + floatx80 fp_rem; - make_quotient(env, res->d); + fp_rem = floatx80_rem(val1->d, val0->d, &env->fp_status); + if (!floatx80_is_any_nan(fp_rem)) { + float_status fp_status = { }; + uint32_t quotient; + int sign; + + /* Calculate quotient directly using round to nearest mode */ + set_float_rounding_mode(float_round_nearest_even, &fp_status); + set_floatx80_rounding_precision( + get_floatx80_rounding_precision(&env->fp_status), &fp_status); + fp_quot.d = floatx80_div(val1->d, val0->d, &fp_status); + + sign = extractFloatx80Sign(fp_quot.d); + quotient = floatx80_to_int32(floatx80_abs(fp_quot.d), &env->fp_status); + make_quotient(env, sign, quotient); + } + + res->d = fp_rem; } void HELPER(fgetexp)(CPUM68KState *env, FPReg *res, FPReg *val) diff --git a/target/m68k/gdbstub.c b/target/m68k/gdbstub.c index eb2d030e14..15547e2313 100644 --- a/target/m68k/gdbstub.c +++ b/target/m68k/gdbstub.c @@ -19,12 +19,11 @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" int m68k_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(cs); if (n < 8) { /* D0-D7 */ @@ -50,8 +49,7 @@ int m68k_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int m68k_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(cs); uint32_t tmp; tmp = ldl_p(mem_buf); diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 5728e48585..7a91f33b17 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -23,53 +23,17 @@ #include "exec/exec-all.h" #include "exec/gdbstub.h" #include "exec/helper-proto.h" +#include "gdbstub/helpers.h" #include "fpu/softfloat.h" #include "qemu/qemu-print.h" #define SIGNBIT (1u << 31) -/* Sort alphabetically, except for "any". */ -static gint m68k_cpu_list_compare(gconstpointer a, gconstpointer b) +static int cf_fpu_gdb_get_reg(CPUState *cs, GByteArray *mem_buf, int n) { - ObjectClass *class_a = (ObjectClass *)a; - ObjectClass *class_b = (ObjectClass *)b; - const char *name_a, *name_b; + M68kCPU *cpu = M68K_CPU(cs); + CPUM68KState *env = &cpu->env; - name_a = object_class_get_name(class_a); - name_b = object_class_get_name(class_b); - if (strcmp(name_a, "any-" TYPE_M68K_CPU) == 0) { - return 1; - } else if (strcmp(name_b, "any-" TYPE_M68K_CPU) == 0) { - return -1; - } else { - return strcasecmp(name_a, name_b); - } -} - -static void m68k_cpu_list_entry(gpointer data, gpointer user_data) -{ - ObjectClass *c = data; - const char *typename; - char *name; - - typename = object_class_get_name(c); - name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_M68K_CPU)); - qemu_printf("%s\n", name); - g_free(name); -} - -void m68k_cpu_list(void) -{ - GSList *list; - - list = object_class_get_list(TYPE_M68K_CPU, false); - list = g_slist_sort(list, m68k_cpu_list_compare); - g_slist_foreach(list, m68k_cpu_list_entry, NULL); - g_slist_free(list); -} - -static int cf_fpu_gdb_get_reg(CPUM68KState *env, GByteArray *mem_buf, int n) -{ if (n < 8) { float_status s; return gdb_get_reg64(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); @@ -85,8 +49,11 @@ static int cf_fpu_gdb_get_reg(CPUM68KState *env, GByteArray *mem_buf, int n) return 0; } -static int cf_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) +static int cf_fpu_gdb_set_reg(CPUState *cs, uint8_t *mem_buf, int n) { + M68kCPU *cpu = M68K_CPU(cs); + CPUM68KState *env = &cpu->env; + if (n < 8) { float_status s; env->fregs[n].d = float64_to_floatx80(ldq_p(mem_buf), &s); @@ -105,8 +72,11 @@ 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, GByteArray *mem_buf, int n) +static int m68k_fpu_gdb_get_reg(CPUState *cs, GByteArray *mem_buf, int n) { + M68kCPU *cpu = M68K_CPU(cs); + CPUM68KState *env = &cpu->env; + if (n < 8) { int len = gdb_get_reg16(mem_buf, env->fregs[n].l.upper); len += gdb_get_reg16(mem_buf, 0); @@ -117,15 +87,18 @@ static int m68k_fpu_gdb_get_reg(CPUM68KState *env, GByteArray *mem_buf, int n) case 8: /* fpcontrol */ return gdb_get_reg32(mem_buf, env->fpcr); case 9: /* fpstatus */ - return gdb_get_reg32(mem_buf, env->fpsr); + return gdb_get_reg32(mem_buf, cpu_m68k_get_fpsr(env)); case 10: /* fpiar, not implemented */ return gdb_get_reg32(mem_buf, 0); } return 0; } -static int m68k_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) +static int m68k_fpu_gdb_set_reg(CPUState *cs, uint8_t *mem_buf, int n) { + M68kCPU *cpu = M68K_CPU(cs); + CPUM68KState *env = &cpu->env; + if (n < 8) { env->fregs[n].l.upper = lduw_be_p(mem_buf); env->fregs[n].l.lower = ldq_be_p(mem_buf + 4); @@ -136,7 +109,7 @@ static int m68k_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) cpu_m68k_set_fpcr(env, ldl_p(mem_buf)); return 4; case 9: /* fpstatus */ - env->fpsr = ldl_p(mem_buf); + cpu_m68k_set_fpsr(env, ldl_p(mem_buf)); return 4; case 10: /* fpiar, not implemented */ return 4; @@ -151,10 +124,10 @@ void m68k_cpu_init_gdb(M68kCPU *cpu) if (m68k_feature(env, M68K_FEATURE_CF_FPU)) { gdb_register_coprocessor(cs, cf_fpu_gdb_get_reg, cf_fpu_gdb_set_reg, - 11, "cf-fp.xml", 18); + gdb_find_static_feature("cf-fp.xml"), 18); } else if (m68k_feature(env, M68K_FEATURE_FPU)) { - gdb_register_coprocessor(cs, m68k_fpu_gdb_get_reg, - m68k_fpu_gdb_set_reg, 11, "m68k-fp.xml", 18); + gdb_register_coprocessor(cs, m68k_fpu_gdb_get_reg, m68k_fpu_gdb_set_reg, + gdb_find_static_feature("m68k-fp.xml"), 18); } /* TODO: Add [E]MAC registers. */ } @@ -460,7 +433,7 @@ void m68k_switch_sp(CPUM68KState *env) int new_sp; env->sp[env->current_sp] = env->aregs[7]; - if (m68k_feature(env, M68K_FEATURE_M68000)) { + if (m68k_feature(env, M68K_FEATURE_M68K)) { if (env->sr & SR_S) { /* SR:Master-Mode bit unimplemented then ISP is not available */ if (!m68k_feature(env, M68K_FEATURE_MSP) || env->sr & SR_M) { @@ -589,10 +562,10 @@ static void dump_address_map(CPUM68KState *env, uint32_t root_pointer) #define DUMP_CACHEFLAGS(a) \ switch (a & M68K_DESC_CACHEMODE) { \ - case M68K_DESC_CM_WRTHRU: /* cachable, write-through */ \ + case M68K_DESC_CM_WRTHRU: /* cacheable, write-through */ \ qemu_printf("T"); \ break; \ - case M68K_DESC_CM_COPYBK: /* cachable, copyback */ \ + case M68K_DESC_CM_COPYBK: /* cacheable, copyback */ \ qemu_printf("C"); \ break; \ case M68K_DESC_CM_SERIAL: /* noncachable, serialized */ \ @@ -933,8 +906,7 @@ txfail: hwaddr m68k_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(cs); hwaddr phys_addr; int prot; int access_type; @@ -982,8 +954,7 @@ bool m68k_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType qemu_access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(cs); hwaddr physical; int prot; int access_type; @@ -1011,7 +982,7 @@ bool m68k_cpu_tlb_fill(CPUState *cs, vaddr address, int size, access_type |= ACCESS_SUPER; } - ret = get_physical_address(&cpu->env, &physical, &prot, + ret = get_physical_address(env, &physical, &prot, address, access_type, &page_size); if (likely(ret == 0)) { tlb_set_page(cs, address & TARGET_PAGE_MASK, @@ -1479,7 +1450,7 @@ void HELPER(set_mac_extu)(CPUM68KState *env, uint32_t val, uint32_t acc) env->macc[acc + 1] = res; } -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) void HELPER(ptest)(CPUM68KState *env, uint32_t addr, uint32_t is_read) { hwaddr physical; @@ -1533,4 +1504,4 @@ void HELPER(reset)(CPUM68KState *env) { /* FIXME: reset all except CPU */ } -#endif +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/m68k/helper.h b/target/m68k/helper.h index c9bed2b884..95aa5e53bb 100644 --- a/target/m68k/helper.h +++ b/target/m68k/helper.h @@ -54,6 +54,8 @@ DEF_HELPER_4(fsdiv, void, env, fp, fp, fp) DEF_HELPER_4(fddiv, void, env, fp, fp, fp) DEF_HELPER_4(fsgldiv, void, env, fp, fp, fp) DEF_HELPER_FLAGS_3(fcmp, TCG_CALL_NO_RWG, void, env, fp, fp) +DEF_HELPER_2(set_fpsr, void, env, i32) +DEF_HELPER_1(get_fpsr, i32, env) DEF_HELPER_FLAGS_2(set_fpcr, TCG_CALL_NO_RWG, void, env, i32) DEF_HELPER_FLAGS_2(ftst, TCG_CALL_NO_RWG, void, env, fp) DEF_HELPER_3(fconst, void, env, fp, i32) @@ -124,7 +126,7 @@ DEF_HELPER_FLAGS_4(bfffo_mem, TCG_CALL_NO_WG, i64, env, i32, s32, i32) DEF_HELPER_3(chk, void, env, s32, s32) DEF_HELPER_4(chk2, void, env, s32, s32, s32) -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) DEF_HELPER_3(ptest, void, env, i32, i32) DEF_HELPER_3(pflush, void, env, i32, i32) DEF_HELPER_FLAGS_1(reset, TCG_CALL_NO_RWG, void, env) diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c index 37343d47e2..546cff2246 100644 --- a/target/m68k/m68k-semi.c +++ b/target/m68k/m68k-semi.c @@ -15,19 +15,20 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see . + * + * The semihosting protocol implemented here is described in the + * libgloss sources: + * https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=libgloss/m68k/m68k-semi.txt;hb=HEAD */ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" -#if defined(CONFIG_USER_ONLY) -#include "qemu.h" -#define SEMIHOSTING_HEAP_SIZE (128 * 1024 * 1024) -#else -#include "exec/softmmu-semi.h" +#include "gdbstub/syscalls.h" +#include "gdbstub/helpers.h" +#include "semihosting/syscalls.h" +#include "semihosting/uaccess.h" #include "hw/boards.h" -#endif #include "qemu/log.h" #define HOSTED_EXIT 0 @@ -45,91 +46,42 @@ #define HOSTED_ISATTY 12 #define HOSTED_SYSTEM 13 -typedef uint32_t gdb_mode_t; -typedef uint32_t gdb_time_t; - -struct m68k_gdb_stat { - uint32_t gdb_st_dev; /* device */ - uint32_t gdb_st_ino; /* inode */ - gdb_mode_t gdb_st_mode; /* protection */ - uint32_t gdb_st_nlink; /* number of hard links */ - uint32_t gdb_st_uid; /* user ID of owner */ - uint32_t gdb_st_gid; /* group ID of owner */ - uint32_t gdb_st_rdev; /* device type (if inode device) */ - uint64_t gdb_st_size; /* total size, in bytes */ - uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */ - uint64_t gdb_st_blocks; /* number of blocks allocated */ - gdb_time_t gdb_st_atime; /* time of last access */ - gdb_time_t gdb_st_mtime; /* time of last modification */ - gdb_time_t gdb_st_ctime; /* time of last change */ -} QEMU_PACKED; - -struct gdb_timeval { - gdb_time_t tv_sec; /* second */ - uint64_t tv_usec; /* microsecond */ -} QEMU_PACKED; - -#define GDB_O_RDONLY 0x0 -#define GDB_O_WRONLY 0x1 -#define GDB_O_RDWR 0x2 -#define GDB_O_APPEND 0x8 -#define GDB_O_CREAT 0x200 -#define GDB_O_TRUNC 0x400 -#define GDB_O_EXCL 0x800 - -static int translate_openflags(int flags) +static int host_to_gdb_errno(int err) { - int hf; - - if (flags & GDB_O_WRONLY) - hf = O_WRONLY; - else if (flags & GDB_O_RDWR) - hf = O_RDWR; - else - hf = O_RDONLY; - - if (flags & GDB_O_APPEND) hf |= O_APPEND; - if (flags & GDB_O_CREAT) hf |= O_CREAT; - if (flags & GDB_O_TRUNC) hf |= O_TRUNC; - if (flags & GDB_O_EXCL) hf |= O_EXCL; - - return hf; +#define E(X) case E##X: return GDB_E##X + switch (err) { + E(PERM); + E(NOENT); + E(INTR); + E(BADF); + E(ACCES); + E(FAULT); + E(BUSY); + E(EXIST); + E(NODEV); + E(NOTDIR); + E(ISDIR); + E(INVAL); + E(NFILE); + E(MFILE); + E(FBIG); + E(NOSPC); + E(SPIPE); + E(ROFS); + E(NAMETOOLONG); + default: + return GDB_EUNKNOWN; + } +#undef E } -static void translate_stat(CPUM68KState *env, target_ulong addr, struct stat *s) +static void m68k_semi_u32_cb(CPUState *cs, uint64_t ret, int err) { - struct m68k_gdb_stat *p; + CPUM68KState *env = cpu_env(cs); - if (!(p = lock_user(VERIFY_WRITE, addr, sizeof(struct m68k_gdb_stat), 0))) - /* FIXME - should this return an error code? */ - return; - p->gdb_st_dev = cpu_to_be32(s->st_dev); - p->gdb_st_ino = cpu_to_be32(s->st_ino); - p->gdb_st_mode = cpu_to_be32(s->st_mode); - p->gdb_st_nlink = cpu_to_be32(s->st_nlink); - p->gdb_st_uid = cpu_to_be32(s->st_uid); - p->gdb_st_gid = cpu_to_be32(s->st_gid); - p->gdb_st_rdev = cpu_to_be32(s->st_rdev); - p->gdb_st_size = cpu_to_be64(s->st_size); -#ifdef _WIN32 - /* Windows stat is missing some fields. */ - p->gdb_st_blksize = 0; - p->gdb_st_blocks = 0; -#else - p->gdb_st_blksize = cpu_to_be64(s->st_blksize); - p->gdb_st_blocks = cpu_to_be64(s->st_blocks); -#endif - p->gdb_st_atime = cpu_to_be32(s->st_atime); - p->gdb_st_mtime = cpu_to_be32(s->st_mtime); - p->gdb_st_ctime = cpu_to_be32(s->st_ctime); - unlock_user(p, addr, sizeof(struct m68k_gdb_stat)); -} - -static void m68k_semi_return_u32(CPUM68KState *env, uint32_t ret, uint32_t err) -{ target_ulong args = env->dregs[1]; if (put_user_u32(ret, args) || - put_user_u32(err, args + 4)) { + put_user_u32(host_to_gdb_errno(err), args + 4)) { /* * The m68k semihosting ABI does not provide any way to report this * error to the guest, so the best we can do is log it in qemu. @@ -140,328 +92,146 @@ static void m68k_semi_return_u32(CPUM68KState *env, uint32_t ret, uint32_t err) } } -static void m68k_semi_return_u64(CPUM68KState *env, uint64_t ret, uint32_t err) +static void m68k_semi_u64_cb(CPUState *cs, uint64_t ret, int err) { + CPUM68KState *env = cpu_env(cs); + target_ulong args = env->dregs[1]; if (put_user_u32(ret >> 32, args) || put_user_u32(ret, args + 4) || - put_user_u32(err, args + 8)) { + put_user_u32(host_to_gdb_errno(err), args + 8)) { /* No way to report this via m68k semihosting ABI; just log it */ qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value " "discarded because argument block not writable\n"); } } -static int m68k_semi_is_fseek; - -static void m68k_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) -{ - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; - - if (m68k_semi_is_fseek) { - /* - * FIXME: We've already lost the high bits of the fseek - * return value. - */ - m68k_semi_return_u64(env, ret, err); - m68k_semi_is_fseek = 0; - } else { - m68k_semi_return_u32(env, ret, err); - } -} - /* * Read the input value from the argument block; fail the semihosting * call if the memory read fails. */ #define GET_ARG(n) do { \ if (get_user_ual(arg ## n, args + (n) * 4)) { \ - result = -1; \ - errno = EFAULT; \ goto failed; \ } \ } while (0) +#define GET_ARG64(n) do { \ + if (get_user_ual(arg ## n, args + (n) * 4)) { \ + goto failed64; \ + } \ +} while (0) + + void do_m68k_semihosting(CPUM68KState *env, int nr) { + CPUState *cs = env_cpu(env); uint32_t args; target_ulong arg0, arg1, arg2, arg3; - void *p; - void *q; - uint32_t len; - uint32_t result; args = env->dregs[1]; switch (nr) { case HOSTED_EXIT: gdb_exit(env->dregs[0]); exit(env->dregs[0]); + case HOSTED_OPEN: GET_ARG(0); GET_ARG(1); GET_ARG(2); GET_ARG(3); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "open,%s,%x,%x", arg0, (int)arg1, - arg2, arg3); - return; - } else { - p = lock_user_string(arg0); - if (!p) { - /* FIXME - check error code? */ - result = -1; - } else { - result = open(p, translate_openflags(arg2), arg3); - unlock_user(p, arg0, 0); - } - } + semihost_sys_open(cs, m68k_semi_u32_cb, arg0, arg1, arg2, arg3); break; + case HOSTED_CLOSE: - { - /* Ignore attempts to close stdin/out/err. */ - GET_ARG(0); - int fd = arg0; - if (fd > 2) { - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "close,%x", arg0); - return; - } else { - result = close(fd); - } - } else { - result = 0; - } - break; - } + GET_ARG(0); + semihost_sys_close(cs, m68k_semi_u32_cb, arg0); + break; + case HOSTED_READ: GET_ARG(0); GET_ARG(1); GET_ARG(2); - len = arg2; - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "read,%x,%x,%x", - arg0, arg1, len); - return; - } else { - p = lock_user(VERIFY_WRITE, arg1, len, 0); - if (!p) { - /* FIXME - check error code? */ - result = -1; - } else { - result = read(arg0, p, len); - unlock_user(p, arg1, len); - } - } + semihost_sys_read(cs, m68k_semi_u32_cb, arg0, arg1, arg2); break; + case HOSTED_WRITE: GET_ARG(0); GET_ARG(1); GET_ARG(2); - len = arg2; - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "write,%x,%x,%x", - arg0, arg1, len); - return; - } else { - p = lock_user(VERIFY_READ, arg1, len, 1); - if (!p) { - /* FIXME - check error code? */ - result = -1; - } else { - result = write(arg0, p, len); - unlock_user(p, arg0, 0); - } - } + semihost_sys_write(cs, m68k_semi_u32_cb, arg0, arg1, arg2); break; + case HOSTED_LSEEK: - { - uint64_t off; - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - GET_ARG(3); - off = (uint32_t)arg2 | ((uint64_t)arg1 << 32); - if (use_gdb_syscalls()) { - m68k_semi_is_fseek = 1; - gdb_do_syscall(m68k_semi_cb, "fseek,%x,%lx,%x", - arg0, off, arg3); - } else { - off = lseek(arg0, off, arg3); - m68k_semi_return_u64(env, off, errno); - } - return; - } + GET_ARG64(0); + GET_ARG64(1); + GET_ARG64(2); + GET_ARG64(3); + semihost_sys_lseek(cs, m68k_semi_u64_cb, arg0, + deposit64(arg2, 32, 32, arg1), arg3); + break; + case HOSTED_RENAME: GET_ARG(0); GET_ARG(1); GET_ARG(2); GET_ARG(3); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "rename,%s,%s", - arg0, (int)arg1, arg2, (int)arg3); - return; - } else { - p = lock_user_string(arg0); - q = lock_user_string(arg2); - if (!p || !q) { - /* FIXME - check error code? */ - result = -1; - } else { - result = rename(p, q); - } - unlock_user(p, arg0, 0); - unlock_user(q, arg2, 0); - } + semihost_sys_rename(cs, m68k_semi_u32_cb, arg0, arg1, arg2, arg3); break; + case HOSTED_UNLINK: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "unlink,%s", - arg0, (int)arg1); - return; - } else { - p = lock_user_string(arg0); - if (!p) { - /* FIXME - check error code? */ - result = -1; - } else { - result = unlink(p); - unlock_user(p, arg0, 0); - } - } + semihost_sys_remove(cs, m68k_semi_u32_cb, arg0, arg1); break; + case HOSTED_STAT: GET_ARG(0); GET_ARG(1); GET_ARG(2); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "stat,%s,%x", - arg0, (int)arg1, arg2); - return; - } else { - struct stat s; - p = lock_user_string(arg0); - if (!p) { - /* FIXME - check error code? */ - result = -1; - } else { - result = stat(p, &s); - unlock_user(p, arg0, 0); - } - if (result == 0) { - translate_stat(env, arg2, &s); - } - } + semihost_sys_stat(cs, m68k_semi_u32_cb, arg0, arg1, arg2); break; + case HOSTED_FSTAT: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "fstat,%x,%x", - arg0, arg1); - return; - } else { - struct stat s; - result = fstat(arg0, &s); - if (result == 0) { - translate_stat(env, arg1, &s); - } - } + semihost_sys_fstat(cs, m68k_semi_u32_cb, arg0, arg1); break; + case HOSTED_GETTIMEOFDAY: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "gettimeofday,%x,%x", - arg0, arg1); - return; - } else { - struct gdb_timeval *p; - int64_t rt = g_get_real_time(); - p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval), 0); - if (!p) { - /* FIXME - check error code? */ - result = -1; - } else { - result = 0; - p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC); - p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC); - unlock_user(p, arg0, sizeof(struct gdb_timeval)); - } - } + semihost_sys_gettimeofday(cs, m68k_semi_u32_cb, arg0, arg1); break; + case HOSTED_ISATTY: GET_ARG(0); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "isatty,%x", arg0); - return; - } else { - result = isatty(arg0); - } + semihost_sys_isatty(cs, m68k_semi_u32_cb, arg0); break; + case HOSTED_SYSTEM: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(m68k_semi_cb, "system,%s", - arg0, (int)arg1); - return; - } else { - p = lock_user_string(arg0); - if (!p) { - /* FIXME - check error code? */ - result = -1; - } else { - result = system(p); - unlock_user(p, arg0, 0); - } - } + semihost_sys_system(cs, m68k_semi_u32_cb, arg0, arg1); break; - case HOSTED_INIT_SIM: -#if defined(CONFIG_USER_ONLY) - { - CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; - /* Allocate the heap using sbrk. */ - if (!ts->heap_limit) { - abi_ulong ret; - uint32_t size; - uint32_t base; - base = do_brk(0); - size = SEMIHOSTING_HEAP_SIZE; - /* Try a big heap, and reduce the size if that fails. */ - for (;;) { - ret = do_brk(base + size); - if (ret >= (base + size)) { - break; - } - size >>= 1; - } - ts->heap_limit = base + size; - } - /* - * This call may happen before we have writable memory, so return - * values directly in registers. - */ - env->dregs[1] = ts->heap_limit; - env->aregs[7] = ts->stack_base; - } -#else + case HOSTED_INIT_SIM: /* * FIXME: This is wrong for boards where RAM does not start at * address zero. */ env->dregs[1] = current_machine->ram_size; env->aregs[7] = current_machine->ram_size; -#endif return; + default: cpu_abort(env_cpu(env), "Unsupported semihosting syscall %d\n", nr); - result = 0; + + failed: + m68k_semi_u32_cb(cs, -1, EFAULT); + break; + failed64: + m68k_semi_u64_cb(cs, -1, EFAULT); + break; } -failed: - m68k_semi_return_u32(env, result, errno); } diff --git a/target/m68k/meson.build b/target/m68k/meson.build index 05cd9fbd1e..8d3f9ce288 100644 --- a/target/m68k/meson.build +++ b/target/m68k/meson.build @@ -4,14 +4,16 @@ m68k_ss.add(files( 'fpu_helper.c', 'gdbstub.c', 'helper.c', - 'm68k-semi.c', 'op_helper.c', 'softfloat.c', 'translate.c', )) -m68k_softmmu_ss = ss.source_set() -m68k_softmmu_ss.add(files('monitor.c')) +m68k_system_ss = ss.source_set() +m68k_system_ss.add(files( + 'm68k-semi.c', + 'monitor.c' +)) target_arch += {'m68k': m68k_ss} -target_softmmu_arch += {'m68k': m68k_softmmu_ss} +target_system_arch += {'m68k': m68k_system_ss} diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c index d9937ca8dc..125f6c1b08 100644 --- a/target/m68k/op_helper.c +++ b/target/m68k/op_helper.c @@ -52,7 +52,7 @@ throwaway: sp += 2; env->pc = cpu_ldl_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0); sp += 4; - if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) { + if (m68k_feature(env, M68K_FEATURE_EXCEPTION_FORMAT_VEC)) { /* all except 68000 */ fmt = cpu_lduw_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0); sp += 2; @@ -203,8 +203,7 @@ static void cf_interrupt_all(CPUM68KState *env, int is_hw) cf_rte(env); return; case EXCP_HALT_INSN: - if (semihosting_enabled() - && (env->sr & SR_S) != 0 + if (semihosting_enabled((env->sr & SR_S) == 0) && (env->pc & 3) == 0 && cpu_lduw_code(env, env->pc - 4) == 0x4e71 && cpu_ldl_code(env, env->pc) == 0x4e7bf000) { @@ -257,7 +256,7 @@ static inline void do_stack_frame(CPUM68KState *env, uint32_t *sp, uint16_t format, uint16_t sr, uint32_t addr, uint32_t retaddr) { - if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) { + if (m68k_feature(env, M68K_FEATURE_EXCEPTION_FORMAT_VEC)) { /* all except 68000 */ CPUState *cs = env_cpu(env); switch (format) { @@ -433,7 +432,7 @@ static void m68k_interrupt_all(CPUM68KState *env, int is_hw) static void do_interrupt_all(CPUM68KState *env, int is_hw) { - if (m68k_feature(env, M68K_FEATURE_M68000)) { + if (m68k_feature(env, M68K_FEATURE_M68K)) { m68k_interrupt_all(env, is_hw); return; } @@ -442,10 +441,7 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw) void m68k_cpu_do_interrupt(CPUState *cs) { - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; - - do_interrupt_all(env, 0); + do_interrupt_all(cpu_env(cs), 0); } static inline void do_interrupt_m68k_hardirq(CPUM68KState *env) @@ -458,10 +454,9 @@ void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr) { - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(cs); - cpu_restore_state(cs, retaddr, true); + cpu_restore_state(cs, retaddr); if (m68k_feature(env, M68K_FEATURE_M68040)) { env->mmu.mmusr = 0; @@ -512,8 +507,7 @@ void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(cs); if (interrupt_request & CPU_INTERRUPT_HARD && ((env->sr & SR_I) >> SR_I_SHIFT) < env->pending_level) { @@ -559,7 +553,7 @@ raise_exception_format2(CPUM68KState *env, int tt, int ilen, uintptr_t raddr) cs->exception_index = tt; /* Recover PC and CC_OP for the beginning of the insn. */ - cpu_restore_state(cs, raddr, true); + cpu_restore_state(cs, raddr); /* Flags are current in env->cc_*, or are undefined. */ env->cc_op = CC_OP_FLAGS; @@ -812,7 +806,7 @@ static void do_cas2l(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2, uint32_t l1, l2; uintptr_t ra = GETPC(); #if defined(CONFIG_ATOMIC64) - int mmu_idx = cpu_mmu_index(env, 0); + int mmu_idx = cpu_mmu_index(env_cpu(env), 0); MemOpIdx oi = make_memop_idx(MO_BEUQ, mmu_idx); #endif diff --git a/target/m68k/qregs.def b/target/m68k/qregs.h.inc similarity index 100% rename from target/m68k/qregs.def rename to target/m68k/qregs.h.inc diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 8f3c298ad0..8a194f2f21 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -25,7 +25,6 @@ #include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/qemu-print.h" -#include "exec/cpu_ldst.h" #include "exec/translator.h" #include "exec/helper-proto.h" @@ -34,12 +33,15 @@ #include "exec/log.h" #include "fpu/softfloat.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H //#define DEBUG_DISPATCH 1 #define DEFO32(name, offset) static TCGv QREG_##name; #define DEFO64(name, offset) static TCGv_i64 QREG_##name; -#include "qregs.def" +#include "qregs.h.inc" #undef DEFO32 #undef DEFO64 @@ -62,27 +64,25 @@ static TCGv NULL_QREG; /* Used to distinguish stores from bad addressing modes. */ static TCGv store_dummy; -#include "exec/gen-icount.h" - void m68k_tcg_init(void) { char *p; int i; #define DEFO32(name, offset) \ - QREG_##name = tcg_global_mem_new_i32(cpu_env, \ + QREG_##name = tcg_global_mem_new_i32(tcg_env, \ offsetof(CPUM68KState, offset), #name); #define DEFO64(name, offset) \ - QREG_##name = tcg_global_mem_new_i64(cpu_env, \ + QREG_##name = tcg_global_mem_new_i64(tcg_env, \ offsetof(CPUM68KState, offset), #name); -#include "qregs.def" +#include "qregs.h.inc" #undef DEFO32 #undef DEFO64 - cpu_halted = tcg_global_mem_new_i32(cpu_env, + cpu_halted = tcg_global_mem_new_i32(tcg_env, -offsetof(M68kCPU, env) + offsetof(CPUState, halted), "HALTED"); - cpu_exception_index = tcg_global_mem_new_i32(cpu_env, + cpu_exception_index = tcg_global_mem_new_i32(tcg_env, -offsetof(M68kCPU, env) + offsetof(CPUState, exception_index), "EXCEPTION"); @@ -90,23 +90,23 @@ void m68k_tcg_init(void) p = cpu_reg_names; for (i = 0; i < 8; i++) { sprintf(p, "D%d", i); - cpu_dregs[i] = tcg_global_mem_new(cpu_env, + cpu_dregs[i] = tcg_global_mem_new(tcg_env, offsetof(CPUM68KState, dregs[i]), p); p += 3; sprintf(p, "A%d", i); - cpu_aregs[i] = tcg_global_mem_new(cpu_env, + cpu_aregs[i] = tcg_global_mem_new(tcg_env, offsetof(CPUM68KState, aregs[i]), p); p += 3; } for (i = 0; i < 4; i++) { sprintf(p, "ACC%d", i); - cpu_macc[i] = tcg_global_mem_new_i64(cpu_env, + cpu_macc[i] = tcg_global_mem_new_i64(tcg_env, offsetof(CPUM68KState, macc[i]), p); p += 5; } - NULL_QREG = tcg_global_mem_new(cpu_env, -4, "NULL"); - store_dummy = tcg_global_mem_new(cpu_env, -8, "NULL"); + NULL_QREG = tcg_global_mem_new(tcg_env, -4, "NULL"); + store_dummy = tcg_global_mem_new(tcg_env, -8, "NULL"); } /* internal defines */ @@ -121,35 +121,9 @@ typedef struct DisasContext { int done_mac; int writeback_mask; TCGv writeback[8]; -#define MAX_TO_RELEASE 8 - int release_count; - TCGv release[MAX_TO_RELEASE]; bool ss_active; } DisasContext; -static void init_release_array(DisasContext *s) -{ -#ifdef CONFIG_DEBUG_TCG - memset(s->release, 0, sizeof(s->release)); -#endif - s->release_count = 0; -} - -static void do_release(DisasContext *s) -{ - int i; - for (i = 0; i < s->release_count; i++) { - tcg_temp_free(s->release[i]); - } - init_release_array(s); -} - -static TCGv mark_to_release(DisasContext *s, TCGv tmp) -{ - g_assert(s->release_count < MAX_TO_RELEASE); - return s->release[s->release_count++] = tmp; -} - static TCGv get_areg(DisasContext *s, unsigned regno) { if (s->writeback_mask & (1 << regno)) { @@ -164,7 +138,6 @@ static void delay_set_areg(DisasContext *s, unsigned regno, { if (s->writeback_mask & (1 << regno)) { if (give_temp) { - tcg_temp_free(s->writeback[regno]); s->writeback[regno] = val; } else { tcg_gen_mov_i32(s->writeback[regno], val); @@ -189,7 +162,6 @@ static void do_writebacks(DisasContext *s) do { unsigned regno = ctz32(mask); tcg_gen_mov_i32(cpu_aregs[regno], s->writeback[regno]); - tcg_temp_free(s->writeback[regno]); mask &= mask - 1; } while (mask); } @@ -292,11 +264,7 @@ static void gen_jmp(DisasContext *s, TCGv dest) static void gen_raise_exception(int nr) { - TCGv_i32 tmp; - - tmp = tcg_const_i32(nr); - gen_helper_raise_exception(cpu_env, tmp); - tcg_temp_free_i32(tmp); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(nr)); } static void gen_raise_exception_format2(DisasContext *s, int nr, @@ -308,7 +276,7 @@ static void gen_raise_exception_format2(DisasContext *s, int nr, * Re-use mmu.ar for the purpose, since that's only valid * after tlb_fill. */ - tcg_gen_st_i32(tcg_constant_i32(this_pc), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(this_pc), tcg_env, offsetof(CPUM68KState, mmu.ar)); gen_raise_exception(nr); s->base.is_jmp = DISAS_NORETURN; @@ -336,23 +304,14 @@ static inline void gen_addr_fault(DisasContext *s) static inline TCGv gen_load(DisasContext *s, int opsize, TCGv addr, int sign, int index) { - TCGv tmp; - tmp = tcg_temp_new_i32(); - switch(opsize) { + TCGv tmp = tcg_temp_new_i32(); + + switch (opsize) { case OS_BYTE: - if (sign) - tcg_gen_qemu_ld8s(tmp, addr, index); - else - tcg_gen_qemu_ld8u(tmp, addr, index); - break; case OS_WORD: - if (sign) - tcg_gen_qemu_ld16s(tmp, addr, index); - else - tcg_gen_qemu_ld16u(tmp, addr, index); - break; case OS_LONG: - tcg_gen_qemu_ld32u(tmp, addr, index); + tcg_gen_qemu_ld_tl(tmp, addr, index, + opsize | (sign ? MO_SIGN : 0) | MO_TE); break; default: g_assert_not_reached(); @@ -364,15 +323,11 @@ static inline TCGv gen_load(DisasContext *s, int opsize, TCGv addr, static inline void gen_store(DisasContext *s, int opsize, TCGv addr, TCGv val, int index) { - switch(opsize) { + switch (opsize) { case OS_BYTE: - tcg_gen_qemu_st8(val, addr, index); - break; case OS_WORD: - tcg_gen_qemu_st16(val, addr, index); - break; case OS_LONG: - tcg_gen_qemu_st32(val, addr, index); + tcg_gen_qemu_st_tl(val, addr, index, opsize | MO_TE); break; default: g_assert_not_reached(); @@ -396,8 +351,7 @@ static TCGv gen_ldst(DisasContext *s, int opsize, TCGv addr, TCGv val, gen_store(s, opsize, addr, val, index); return store_dummy; } else { - return mark_to_release(s, gen_load(s, opsize, addr, - what == EA_LOADS, index)); + return gen_load(s, opsize, addr, what == EA_LOADS, index); } } @@ -471,7 +425,7 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base) if ((ext & 0x800) == 0 && !m68k_feature(s->env, M68K_FEATURE_WORD_INDEX)) return NULL_QREG; - if (m68k_feature(s->env, M68K_FEATURE_M68000) && + if (m68k_feature(s->env, M68K_FEATURE_M68K) && !m68k_feature(s->env, M68K_FEATURE_SCALED_INDEX)) { ext &= ~(3 << 9); } @@ -491,7 +445,7 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base) } else { bd = 0; } - tmp = mark_to_release(s, tcg_temp_new()); + tmp = tcg_temp_new(); if ((ext & 0x44) == 0) { /* pre-index */ add = gen_addr_index(s, ext, tmp); @@ -501,7 +455,7 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base) if ((ext & 0x80) == 0) { /* base not suppressed */ if (IS_NULL_QREG(base)) { - base = mark_to_release(s, tcg_const_i32(offset + bd)); + base = tcg_constant_i32(offset + bd); bd = 0; } if (!IS_NULL_QREG(add)) { @@ -517,11 +471,11 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base) add = tmp; } } else { - add = mark_to_release(s, tcg_const_i32(bd)); + add = tcg_constant_i32(bd); } if ((ext & 3) != 0) { /* memory indirect */ - base = mark_to_release(s, gen_load(s, OS_LONG, add, 0, IS_USER(s))); + base = gen_load(s, OS_LONG, add, 0, IS_USER(s)); if ((ext & 0x44) == 4) { add = gen_addr_index(s, ext, tmp); tcg_gen_add_i32(tmp, add, base); @@ -546,7 +500,7 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base) } } else { /* brief extension word format */ - tmp = mark_to_release(s, tcg_temp_new()); + tmp = tcg_temp_new(); add = gen_addr_index(s, ext, tmp); if (!IS_NULL_QREG(base)) { tcg_gen_add_i32(tmp, add, base); @@ -566,21 +520,9 @@ static inline void gen_ext(TCGv res, TCGv val, int opsize, int sign) { switch (opsize) { case OS_BYTE: - if (sign) { - tcg_gen_ext8s_i32(res, val); - } else { - tcg_gen_ext8u_i32(res, val); - } - break; case OS_WORD: - if (sign) { - tcg_gen_ext16s_i32(res, val); - } else { - tcg_gen_ext16u_i32(res, val); - } - break; case OS_LONG: - tcg_gen_mov_i32(res, val); + tcg_gen_ext_i32(res, val, opsize | (sign ? MO_SIGN : 0)); break; default: g_assert_not_reached(); @@ -609,9 +551,7 @@ static void gen_flush_flags(DisasContext *s) gen_ext(t0, t0, s->cc_op - CC_OP_ADDB, 1); tcg_gen_xor_i32(t1, QREG_CC_N, QREG_CC_V); tcg_gen_xor_i32(QREG_CC_V, QREG_CC_V, t0); - tcg_temp_free(t0); tcg_gen_andc_i32(QREG_CC_V, t1, QREG_CC_V); - tcg_temp_free(t1); break; case CC_OP_SUBB: @@ -626,9 +566,7 @@ static void gen_flush_flags(DisasContext *s) gen_ext(t0, t0, s->cc_op - CC_OP_SUBB, 1); tcg_gen_xor_i32(t1, QREG_CC_N, t0); tcg_gen_xor_i32(QREG_CC_V, QREG_CC_V, t0); - tcg_temp_free(t0); tcg_gen_and_i32(QREG_CC_V, QREG_CC_V, t1); - tcg_temp_free(t1); break; case CC_OP_CMPB: @@ -642,7 +580,6 @@ static void gen_flush_flags(DisasContext *s) tcg_gen_xor_i32(t0, QREG_CC_Z, QREG_CC_N); tcg_gen_xor_i32(QREG_CC_V, QREG_CC_V, QREG_CC_N); tcg_gen_and_i32(QREG_CC_V, QREG_CC_V, t0); - tcg_temp_free(t0); tcg_gen_mov_i32(QREG_CC_N, QREG_CC_Z); break; @@ -653,14 +590,12 @@ static void gen_flush_flags(DisasContext *s) break; case CC_OP_DYNAMIC: - gen_helper_flush_flags(cpu_env, QREG_CC_OP); + gen_helper_flush_flags(tcg_env, QREG_CC_OP); s->cc_op_synced = 1; break; default: - t0 = tcg_const_i32(s->cc_op); - gen_helper_flush_flags(cpu_env, t0); - tcg_temp_free(t0); + gen_helper_flush_flags(tcg_env, tcg_constant_i32(s->cc_op)); s->cc_op_synced = 1; break; } @@ -676,7 +611,7 @@ static inline TCGv gen_extend(DisasContext *s, TCGv val, int opsize, int sign) if (opsize == OS_LONG) { tmp = val; } else { - tmp = mark_to_release(s, tcg_temp_new()); + tmp = tcg_temp_new(); gen_ext(tmp, val, opsize, sign); } @@ -749,21 +684,12 @@ static inline int ext_opsize(int ext, int pos) */ static void gen_partset_reg(int opsize, TCGv reg, TCGv val) { - TCGv tmp; switch (opsize) { case OS_BYTE: - tcg_gen_andi_i32(reg, reg, 0xffffff00); - tmp = tcg_temp_new(); - tcg_gen_ext8u_i32(tmp, val); - tcg_gen_or_i32(reg, reg, tmp); - tcg_temp_free(tmp); + tcg_gen_deposit_i32(reg, reg, val, 0, 8); break; case OS_WORD: - tcg_gen_andi_i32(reg, reg, 0xffff0000); - tmp = tcg_temp_new(); - tcg_gen_ext16u_i32(tmp, val); - tcg_gen_or_i32(reg, reg, tmp); - tcg_temp_free(tmp); + tcg_gen_deposit_i32(reg, reg, val, 0, 16); break; case OS_LONG: case OS_SINGLE: @@ -802,9 +728,9 @@ static TCGv gen_lea_mode(CPUM68KState *env, DisasContext *s, return NULL_QREG; } reg = get_areg(s, reg0); - tmp = mark_to_release(s, tcg_temp_new()); + tmp = tcg_temp_new(); if (reg0 == 7 && opsize == OS_BYTE && - m68k_feature(s->env, M68K_FEATURE_M68000)) { + m68k_feature(s->env, M68K_FEATURE_M68K)) { tcg_gen_subi_i32(tmp, reg, 2); } else { tcg_gen_subi_i32(tmp, reg, opsize_bytes(opsize)); @@ -812,7 +738,7 @@ static TCGv gen_lea_mode(CPUM68KState *env, DisasContext *s, return tmp; case 5: /* Indirect displacement. */ reg = get_areg(s, reg0); - tmp = mark_to_release(s, tcg_temp_new()); + tmp = tcg_temp_new(); ext = read_im16(env, s); tcg_gen_addi_i32(tmp, reg, (int16_t)ext); return tmp; @@ -823,14 +749,14 @@ static TCGv gen_lea_mode(CPUM68KState *env, DisasContext *s, switch (reg0) { case 0: /* Absolute short. */ offset = (int16_t)read_im16(env, s); - return mark_to_release(s, tcg_const_i32(offset)); + return tcg_constant_i32(offset); case 1: /* Absolute long. */ offset = read_im32(env, s); - return mark_to_release(s, tcg_const_i32(offset)); + return tcg_constant_i32(offset); case 2: /* pc displacement */ offset = s->pc; offset += (int16_t)read_im16(env, s); - return mark_to_release(s, tcg_const_i32(offset)); + return tcg_constant_i32(offset); case 3: /* pc index+displacement. */ return gen_lea_indexed(env, s, NULL_QREG); case 4: /* Immediate. */ @@ -886,9 +812,9 @@ static TCGv gen_ea_mode(CPUM68KState *env, DisasContext *s, int mode, int reg0, reg = get_areg(s, reg0); result = gen_ldst(s, opsize, reg, val, what, index); if (what == EA_STORE || !addrp) { - TCGv tmp = tcg_temp_new(); + tmp = tcg_temp_new(); if (reg0 == 7 && opsize == OS_BYTE && - m68k_feature(s->env, M68K_FEATURE_M68000)) { + m68k_feature(s->env, M68K_FEATURE_M68K)) { tcg_gen_addi_i32(tmp, reg, 2); } else { tcg_gen_addi_i32(tmp, reg, opsize_bytes(opsize)); @@ -958,7 +884,7 @@ static TCGv gen_ea_mode(CPUM68KState *env, DisasContext *s, int mode, int reg0, default: g_assert_not_reached(); } - return mark_to_release(s, tcg_const_i32(offset)); + return tcg_constant_i32(offset); default: return NULL_QREG; } @@ -978,14 +904,14 @@ static TCGv gen_ea(CPUM68KState *env, DisasContext *s, uint16_t insn, static TCGv_ptr gen_fp_ptr(int freg) { TCGv_ptr fp = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(fp, cpu_env, offsetof(CPUM68KState, fregs[freg])); + tcg_gen_addi_ptr(fp, tcg_env, offsetof(CPUM68KState, fregs[freg])); return fp; } static TCGv_ptr gen_fp_result_ptr(void) { TCGv_ptr fp = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(fp, cpu_env, offsetof(CPUM68KState, fp_result)); + tcg_gen_addi_ptr(fp, tcg_env, offsetof(CPUM68KState, fp_result)); return fp; } @@ -997,12 +923,10 @@ static void gen_fp_move(TCGv_ptr dest, TCGv_ptr src) t32 = tcg_temp_new(); tcg_gen_ld16u_i32(t32, src, offsetof(FPReg, l.upper)); tcg_gen_st16_i32(t32, dest, offsetof(FPReg, l.upper)); - tcg_temp_free(t32); t64 = tcg_temp_new_i64(); tcg_gen_ld_i64(t64, src, offsetof(FPReg, l.lower)); tcg_gen_st_i64(t64, dest, offsetof(FPReg, l.lower)); - tcg_temp_free_i64(t64); } static void gen_load_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, @@ -1015,35 +939,29 @@ static void gen_load_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, tmp = tcg_temp_new(); switch (opsize) { case OS_BYTE: - tcg_gen_qemu_ld8s(tmp, addr, index); - gen_helper_exts32(cpu_env, fp, tmp); - break; case OS_WORD: - tcg_gen_qemu_ld16s(tmp, addr, index); - gen_helper_exts32(cpu_env, fp, tmp); - break; case OS_LONG: - tcg_gen_qemu_ld32u(tmp, addr, index); - gen_helper_exts32(cpu_env, fp, tmp); + tcg_gen_qemu_ld_tl(tmp, addr, index, opsize | MO_SIGN | MO_TE); + gen_helper_exts32(tcg_env, fp, tmp); break; case OS_SINGLE: - tcg_gen_qemu_ld32u(tmp, addr, index); - gen_helper_extf32(cpu_env, fp, tmp); + tcg_gen_qemu_ld_tl(tmp, addr, index, MO_TEUL); + gen_helper_extf32(tcg_env, fp, tmp); break; case OS_DOUBLE: - tcg_gen_qemu_ld64(t64, addr, index); - gen_helper_extf64(cpu_env, fp, t64); + tcg_gen_qemu_ld_i64(t64, addr, index, MO_TEUQ); + gen_helper_extf64(tcg_env, fp, t64); break; case OS_EXTENDED: if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP); break; } - tcg_gen_qemu_ld32u(tmp, addr, index); + tcg_gen_qemu_ld_i32(tmp, addr, index, MO_TEUL); tcg_gen_shri_i32(tmp, tmp, 16); tcg_gen_st16_i32(tmp, fp, offsetof(FPReg, l.upper)); tcg_gen_addi_i32(tmp, addr, 4); - tcg_gen_qemu_ld64(t64, tmp, index); + tcg_gen_qemu_ld_i64(t64, tmp, index, MO_TEUQ); tcg_gen_st_i64(t64, fp, offsetof(FPReg, l.lower)); break; case OS_PACKED: @@ -1056,8 +974,6 @@ static void gen_load_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, default: g_assert_not_reached(); } - tcg_temp_free(tmp); - tcg_temp_free_i64(t64); } static void gen_store_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, @@ -1070,24 +986,18 @@ static void gen_store_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, tmp = tcg_temp_new(); switch (opsize) { case OS_BYTE: - gen_helper_reds32(tmp, cpu_env, fp); - tcg_gen_qemu_st8(tmp, addr, index); - break; case OS_WORD: - gen_helper_reds32(tmp, cpu_env, fp); - tcg_gen_qemu_st16(tmp, addr, index); - break; case OS_LONG: - gen_helper_reds32(tmp, cpu_env, fp); - tcg_gen_qemu_st32(tmp, addr, index); + gen_helper_reds32(tmp, tcg_env, fp); + tcg_gen_qemu_st_tl(tmp, addr, index, opsize | MO_TE); break; case OS_SINGLE: - gen_helper_redf32(tmp, cpu_env, fp); - tcg_gen_qemu_st32(tmp, addr, index); + gen_helper_redf32(tmp, tcg_env, fp); + tcg_gen_qemu_st_tl(tmp, addr, index, MO_TEUL); break; case OS_DOUBLE: - gen_helper_redf64(t64, cpu_env, fp); - tcg_gen_qemu_st64(t64, addr, index); + gen_helper_redf64(t64, tcg_env, fp); + tcg_gen_qemu_st_i64(t64, addr, index, MO_TEUQ); break; case OS_EXTENDED: if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { @@ -1096,10 +1006,10 @@ static void gen_store_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, } tcg_gen_ld16u_i32(tmp, fp, offsetof(FPReg, l.upper)); tcg_gen_shli_i32(tmp, tmp, 16); - tcg_gen_qemu_st32(tmp, addr, index); + tcg_gen_qemu_st_i32(tmp, addr, index, MO_TEUL); tcg_gen_addi_i32(tmp, addr, 4); tcg_gen_ld_i64(t64, fp, offsetof(FPReg, l.lower)); - tcg_gen_qemu_st64(t64, tmp, index); + tcg_gen_qemu_st_i64(t64, tmp, index, MO_TEUQ); break; case OS_PACKED: /* @@ -1111,8 +1021,6 @@ static void gen_store_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, default: g_assert_not_reached(); } - tcg_temp_free(tmp); - tcg_temp_free_i64(t64); } static void gen_ldst_fp(DisasContext *s, int opsize, TCGv addr, @@ -1140,10 +1048,10 @@ static int gen_ea_mode_fp(CPUM68KState *env, DisasContext *s, int mode, case OS_BYTE: case OS_WORD: case OS_LONG: - gen_helper_reds32(reg, cpu_env, fp); + gen_helper_reds32(reg, tcg_env, fp); break; case OS_SINGLE: - gen_helper_redf32(reg, cpu_env, fp); + gen_helper_redf32(reg, tcg_env, fp); break; default: g_assert_not_reached(); @@ -1152,23 +1060,17 @@ static int gen_ea_mode_fp(CPUM68KState *env, DisasContext *s, int mode, tmp = tcg_temp_new(); switch (opsize) { case OS_BYTE: - tcg_gen_ext8s_i32(tmp, reg); - gen_helper_exts32(cpu_env, fp, tmp); - break; case OS_WORD: - tcg_gen_ext16s_i32(tmp, reg); - gen_helper_exts32(cpu_env, fp, tmp); - break; case OS_LONG: - gen_helper_exts32(cpu_env, fp, reg); + tcg_gen_ext_i32(tmp, reg, opsize | MO_SIGN); + gen_helper_exts32(tcg_env, fp, tmp); break; case OS_SINGLE: - gen_helper_extf32(cpu_env, fp, reg); + gen_helper_extf32(tcg_env, fp, reg); break; default: g_assert_not_reached(); } - tcg_temp_free(tmp); } return 0; case 1: /* Address register direct. */ @@ -1212,41 +1114,34 @@ static int gen_ea_mode_fp(CPUM68KState *env, DisasContext *s, int mode, } switch (opsize) { case OS_BYTE: - tmp = tcg_const_i32((int8_t)read_im8(env, s)); - gen_helper_exts32(cpu_env, fp, tmp); - tcg_temp_free(tmp); + tmp = tcg_constant_i32((int8_t)read_im8(env, s)); + gen_helper_exts32(tcg_env, fp, tmp); break; case OS_WORD: - tmp = tcg_const_i32((int16_t)read_im16(env, s)); - gen_helper_exts32(cpu_env, fp, tmp); - tcg_temp_free(tmp); + tmp = tcg_constant_i32((int16_t)read_im16(env, s)); + gen_helper_exts32(tcg_env, fp, tmp); break; case OS_LONG: - tmp = tcg_const_i32(read_im32(env, s)); - gen_helper_exts32(cpu_env, fp, tmp); - tcg_temp_free(tmp); + tmp = tcg_constant_i32(read_im32(env, s)); + gen_helper_exts32(tcg_env, fp, tmp); break; case OS_SINGLE: - tmp = tcg_const_i32(read_im32(env, s)); - gen_helper_extf32(cpu_env, fp, tmp); - tcg_temp_free(tmp); + tmp = tcg_constant_i32(read_im32(env, s)); + gen_helper_extf32(tcg_env, fp, tmp); break; case OS_DOUBLE: - t64 = tcg_const_i64(read_im64(env, s)); - gen_helper_extf64(cpu_env, fp, t64); - tcg_temp_free_i64(t64); + t64 = tcg_constant_i64(read_im64(env, s)); + gen_helper_extf64(tcg_env, fp, t64); break; case OS_EXTENDED: if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP); break; } - tmp = tcg_const_i32(read_im32(env, s) >> 16); + tmp = tcg_constant_i32(read_im32(env, s) >> 16); tcg_gen_st16_i32(tmp, fp, offsetof(FPReg, l.upper)); - tcg_temp_free(tmp); - t64 = tcg_const_i64(read_im64(env, s)); + t64 = tcg_constant_i64(read_im64(env, s)); tcg_gen_st_i64(t64, fp, offsetof(FPReg, l.lower)); - tcg_temp_free_i64(t64); break; case OS_PACKED: /* @@ -1276,8 +1171,6 @@ static int gen_ea_fp(CPUM68KState *env, DisasContext *s, uint16_t insn, typedef struct { TCGCond tcond; - bool g1; - bool g2; TCGv v1; TCGv v2; } DisasCompare; @@ -1290,7 +1183,6 @@ static void gen_cc_cond(DisasCompare *c, DisasContext *s, int cond) /* The CC_OP_CMP form can handle most normal comparisons directly. */ if (op == CC_OP_CMPB || op == CC_OP_CMPW || op == CC_OP_CMPL) { - c->g1 = c->g2 = 1; c->v1 = QREG_CC_N; c->v2 = QREG_CC_V; switch (cond) { @@ -1308,8 +1200,7 @@ static void gen_cc_cond(DisasCompare *c, DisasContext *s, int cond) goto done; case 10: /* PL */ case 11: /* MI */ - c->g1 = c->g2 = 0; - c->v2 = tcg_const_i32(0); + c->v2 = tcg_constant_i32(0); c->v1 = tmp = tcg_temp_new(); tcg_gen_sub_i32(tmp, QREG_CC_N, QREG_CC_V); gen_ext(tmp, tmp, op - CC_OP_CMPB, 1); @@ -1325,9 +1216,7 @@ static void gen_cc_cond(DisasCompare *c, DisasContext *s, int cond) } } - c->g1 = 1; - c->g2 = 0; - c->v2 = tcg_const_i32(0); + c->v2 = tcg_constant_i32(0); switch (cond) { case 0: /* T */ @@ -1410,7 +1299,6 @@ static void gen_cc_cond(DisasCompare *c, DisasContext *s, int cond) case 2: /* HI (!C && !Z) -> !(C || Z)*/ case 3: /* LS (C || Z) */ c->v1 = tmp = tcg_temp_new(); - c->g1 = 0; tcg_gen_setcond_i32(TCG_COND_EQ, tmp, QREG_CC_Z, c->v2); tcg_gen_or_i32(tmp, tmp, QREG_CC_C); tcond = TCG_COND_NE; @@ -1438,20 +1326,16 @@ static void gen_cc_cond(DisasCompare *c, DisasContext *s, int cond) case 12: /* GE (!(N ^ V)) */ case 13: /* LT (N ^ V) */ c->v1 = tmp = tcg_temp_new(); - c->g1 = 0; tcg_gen_xor_i32(tmp, QREG_CC_N, QREG_CC_V); tcond = TCG_COND_LT; break; case 14: /* GT (!(Z || (N ^ V))) */ case 15: /* LE (Z || (N ^ V)) */ c->v1 = tmp = tcg_temp_new(); - c->g1 = 0; - tcg_gen_setcond_i32(TCG_COND_EQ, tmp, QREG_CC_Z, c->v2); - tcg_gen_neg_i32(tmp, tmp); + tcg_gen_negsetcond_i32(TCG_COND_EQ, tmp, QREG_CC_Z, c->v2); tmp2 = tcg_temp_new(); tcg_gen_xor_i32(tmp2, QREG_CC_N, QREG_CC_V); tcg_gen_or_i32(tmp, tmp, tmp2); - tcg_temp_free(tmp2); tcond = TCG_COND_LT; break; } @@ -1463,16 +1347,6 @@ static void gen_cc_cond(DisasCompare *c, DisasContext *s, int cond) c->tcond = tcond; } -static void free_cond(DisasCompare *c) -{ - if (!c->g1) { - tcg_temp_free(c->v1); - } - if (!c->g2) { - tcg_temp_free(c->v2); - } -} - static void gen_jmpcc(DisasContext *s, int cond, TCGLabel *l1) { DisasCompare c; @@ -1480,7 +1354,6 @@ static void gen_jmpcc(DisasContext *s, int cond, TCGLabel *l1) gen_cc_cond(&c, s, cond); update_cc_op(s); tcg_gen_brcond_i32(c.tcond, c.v1, c.v2, l1); - free_cond(&c); } /* Force a TB lookup after an instruction that changes the CPU state. */ @@ -1538,12 +1411,9 @@ DISAS_INSN(scc) gen_cc_cond(&c, s, cond); tmp = tcg_temp_new(); - tcg_gen_setcond_i32(c.tcond, tmp, c.v1, c.v2); - free_cond(&c); + tcg_gen_negsetcond_i32(c.tcond, tmp, c.v1, c.v2); - tcg_gen_neg_i32(tmp, tmp); DEST_EA(env, insn, OS_BYTE, tmp, NULL); - tcg_temp_free(tmp); } DISAS_INSN(dbcc) @@ -1587,7 +1457,7 @@ DISAS_INSN(undef) * for the 680x0 series, as well as those that are implemented * but actually illegal for CPU32 or pre-68020. */ - qemu_log_mask(LOG_UNIMP, "Illegal instruction: %04x @ %08x\n", + qemu_log_mask(LOG_UNIMP, "Illegal instruction: %04x @ %" VADDR_PRIx "\n", insn, s->base.pc_next); gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); } @@ -1610,7 +1480,6 @@ DISAS_INSN(mulw) tcg_gen_mul_i32(tmp, tmp, src); tcg_gen_mov_i32(reg, tmp); gen_logic_cc(s, tmp, OS_LONG); - tcg_temp_free(tmp); } DISAS_INSN(divw) @@ -1630,9 +1499,9 @@ DISAS_INSN(divw) destr = tcg_constant_i32(REG(insn, 9)); ilen = tcg_constant_i32(s->pc - s->base.pc_next); if (sign) { - gen_helper_divsw(cpu_env, destr, src, ilen); + gen_helper_divsw(tcg_env, destr, src, ilen); } else { - gen_helper_divuw(cpu_env, destr, src, ilen); + gen_helper_divuw(tcg_env, destr, src, ilen); } set_cc_op(s, CC_OP_FLAGS); @@ -1661,9 +1530,9 @@ DISAS_INSN(divl) reg = tcg_constant_i32(REG(ext, 0)); ilen = tcg_constant_i32(s->pc - s->base.pc_next); if (sign) { - gen_helper_divsll(cpu_env, num, reg, den, ilen); + gen_helper_divsll(tcg_env, num, reg, den, ilen); } else { - gen_helper_divull(cpu_env, num, reg, den, ilen); + gen_helper_divull(tcg_env, num, reg, den, ilen); } set_cc_op(s, CC_OP_FLAGS); return; @@ -1677,9 +1546,9 @@ DISAS_INSN(divl) reg = tcg_constant_i32(REG(ext, 0)); ilen = tcg_constant_i32(s->pc - s->base.pc_next); if (sign) { - gen_helper_divsl(cpu_env, num, reg, den, ilen); + gen_helper_divsl(tcg_env, num, reg, den, ilen); } else { - gen_helper_divul(cpu_env, num, reg, den, ilen); + gen_helper_divul(tcg_env, num, reg, den, ilen); } set_cc_op(s, CC_OP_FLAGS); @@ -1707,8 +1576,8 @@ static void bcd_add(TCGv dest, TCGv src) * = result with some possible exceeding 0x6 */ - t0 = tcg_const_i32(0x066); - tcg_gen_add_i32(t0, t0, src); + t0 = tcg_temp_new(); + tcg_gen_addi_i32(t0, src, 0x066); t1 = tcg_temp_new(); tcg_gen_add_i32(t1, t0, dest); @@ -1741,7 +1610,6 @@ static void bcd_add(TCGv dest, TCGv src) tcg_gen_andi_i32(t0, t0, 0x22); tcg_gen_add_i32(dest, t0, t0); tcg_gen_add_i32(dest, dest, t0); - tcg_temp_free(t0); /* * remove the exceeding 0x6 @@ -1749,7 +1617,6 @@ static void bcd_add(TCGv dest, TCGv src) */ tcg_gen_sub_i32(dest, t1, dest); - tcg_temp_free(t1); } static void bcd_sub(TCGv dest, TCGv src) @@ -1798,13 +1665,10 @@ static void bcd_sub(TCGv dest, TCGv src) tcg_gen_andi_i32(t2, t2, 0x22); tcg_gen_add_i32(t0, t2, t2); tcg_gen_add_i32(t0, t0, t2); - tcg_temp_free(t2); /* return t1 - t0 */ tcg_gen_sub_i32(dest, t1, t0); - tcg_temp_free(t0); - tcg_temp_free(t1); } static void bcd_flags(TCGv val) @@ -1899,14 +1763,13 @@ DISAS_INSN(nbcd) SRC_EA(env, src, OS_BYTE, 0, &addr); - dest = tcg_const_i32(0); + dest = tcg_temp_new(); + tcg_gen_movi_i32(dest, 0); bcd_sub(dest, src); DEST_EA(env, insn, OS_BYTE, dest, &addr); bcd_flags(dest); - - tcg_temp_free(dest); } DISAS_INSN(addsub) @@ -1945,7 +1808,6 @@ DISAS_INSN(addsub) } else { gen_partset_reg(opsize, DREG(insn, 9), dest); } - tcg_temp_free(dest); } /* Reverse the order of the bits in REG. */ @@ -1980,9 +1842,8 @@ DISAS_INSN(bitop_reg) else tcg_gen_andi_i32(src2, DREG(insn, 9), 31); - tmp = tcg_const_i32(1); - tcg_gen_shl_i32(tmp, tmp, src2); - tcg_temp_free(src2); + tmp = tcg_temp_new(); + tcg_gen_shl_i32(tmp, tcg_constant_i32(1), src2); tcg_gen_and_i32(QREG_CC_Z, src1, tmp); @@ -2000,11 +1861,9 @@ DISAS_INSN(bitop_reg) default: /* btst */ break; } - tcg_temp_free(tmp); if (op) { DEST_EA(env, insn, opsize, dest, &addr); } - tcg_temp_free(dest); } DISAS_INSN(sats) @@ -2024,7 +1883,6 @@ static void gen_push(DisasContext *s, TCGv val) tcg_gen_subi_i32(tmp, QREG_SP, 4); gen_store(s, OS_LONG, tmp, val, IS_USER(s)); tcg_gen_mov_i32(QREG_SP, tmp); - tcg_temp_free(tmp); } static TCGv mreg(int reg) @@ -2087,7 +1945,7 @@ DISAS_INSN(movem) addr = tcg_temp_new(); tcg_gen_mov_i32(addr, tmp); - incr = tcg_const_i32(opsize_bytes(opsize)); + incr = tcg_constant_i32(opsize_bytes(opsize)); if (is_load) { /* memory to register */ @@ -2100,7 +1958,6 @@ DISAS_INSN(movem) for (i = 0; i < 16; i++) { if (mask & (1 << i)) { tcg_gen_mov_i32(mreg(i), r[i]); - tcg_temp_free(r[i]); } } if (mode == 3) { @@ -2127,7 +1984,6 @@ DISAS_INSN(movem) tmp = tcg_temp_new(); tcg_gen_sub_i32(tmp, cpu_aregs[reg0], incr); gen_store(s, opsize, addr, tmp, IS_USER(s)); - tcg_temp_free(tmp); } else { gen_store(s, opsize, addr, mreg(i), IS_USER(s)); } @@ -2143,9 +1999,6 @@ DISAS_INSN(movem) } } } - - tcg_temp_free(incr); - tcg_temp_free(addr); } DISAS_INSN(movep) @@ -2175,22 +2028,20 @@ DISAS_INSN(movep) if (insn & 0x80) { for ( ; i > 0 ; i--) { tcg_gen_shri_i32(dbuf, reg, (i - 1) * 8); - tcg_gen_qemu_st8(dbuf, abuf, IS_USER(s)); + tcg_gen_qemu_st_i32(dbuf, abuf, IS_USER(s), MO_UB); if (i > 1) { tcg_gen_addi_i32(abuf, abuf, 2); } } } else { for ( ; i > 0 ; i--) { - tcg_gen_qemu_ld8u(dbuf, abuf, IS_USER(s)); + tcg_gen_qemu_ld_tl(dbuf, abuf, IS_USER(s), MO_UB); tcg_gen_deposit_i32(reg, reg, dbuf, (i - 1) * 8, 8); if (i > 1) { tcg_gen_addi_i32(abuf, abuf, 2); } } } - tcg_temp_free(abuf); - tcg_temp_free(dbuf); } DISAS_INSN(bitop_im) @@ -2210,7 +2061,7 @@ DISAS_INSN(bitop_im) op = (insn >> 6) & 3; bitnum = read_im16(env, s); - if (m68k_feature(s->env, M68K_FEATURE_M68000)) { + if (m68k_feature(s->env, M68K_FEATURE_M68K)) { if (bitnum & 0xfe00) { disas_undef(env, s, insn); return; @@ -2249,7 +2100,6 @@ DISAS_INSN(bitop_im) break; } DEST_EA(env, insn, opsize, tmp, &addr); - tcg_temp_free(tmp); } } @@ -2259,7 +2109,7 @@ static TCGv gen_get_ccr(DisasContext *s) update_cc_op(s); dest = tcg_temp_new(); - gen_helper_get_ccr(dest, cpu_env); + gen_helper_get_ccr(dest, tcg_env); return dest; } @@ -2272,7 +2122,6 @@ static TCGv gen_get_sr(DisasContext *s) sr = tcg_temp_new(); tcg_gen_andi_i32(sr, QREG_SR, 0xffe0); tcg_gen_or_i32(sr, sr, ccr); - tcg_temp_free(ccr); return sr; } @@ -2285,9 +2134,9 @@ static void gen_set_sr_im(DisasContext *s, uint16_t val, int ccr_only) tcg_gen_movi_i32(QREG_CC_N, val & CCF_N ? -1 : 0); tcg_gen_movi_i32(QREG_CC_X, val & CCF_X ? 1 : 0); } else { - TCGv sr = tcg_const_i32(val); - gen_helper_set_sr(cpu_env, sr); - tcg_temp_free(sr); + /* Must writeback before changing security state. */ + do_writebacks(s); + gen_helper_set_sr(tcg_env, tcg_constant_i32(val)); } set_cc_op(s, CC_OP_FLAGS); } @@ -2295,9 +2144,11 @@ static void gen_set_sr_im(DisasContext *s, uint16_t val, int ccr_only) static void gen_set_sr(DisasContext *s, TCGv val, int ccr_only) { if (ccr_only) { - gen_helper_set_ccr(cpu_env, val); + gen_helper_set_ccr(tcg_env, val); } else { - gen_helper_set_sr(cpu_env, val); + /* Must writeback before changing security state. */ + do_writebacks(s); + gen_helper_set_sr(tcg_env, val); } set_cc_op(s, CC_OP_FLAGS); } @@ -2330,13 +2181,13 @@ DISAS_INSN(arith_im) opsize = insn_opsize(insn); switch (opsize) { case OS_BYTE: - im = tcg_const_i32((int8_t)read_im8(env, s)); + im = tcg_constant_i32((int8_t)read_im8(env, s)); break; case OS_WORD: - im = tcg_const_i32((int16_t)read_im16(env, s)); + im = tcg_constant_i32((int16_t)read_im16(env, s)); break; case OS_LONG: - im = tcg_const_i32(read_im32(env, s)); + im = tcg_constant_i32(read_im32(env, s)); break; default: g_assert_not_reached(); @@ -2373,6 +2224,7 @@ DISAS_INSN(arith_im) tcg_gen_or_i32(dest, src1, im); if (with_SR) { gen_set_sr(s, dest, opsize == OS_BYTE); + gen_exit_tb(s); } else { DEST_EA(env, insn, opsize, dest, &addr); gen_logic_cc(s, dest, opsize); @@ -2382,6 +2234,7 @@ DISAS_INSN(arith_im) tcg_gen_and_i32(dest, src1, im); if (with_SR) { gen_set_sr(s, dest, opsize == OS_BYTE); + gen_exit_tb(s); } else { DEST_EA(env, insn, opsize, dest, &addr); gen_logic_cc(s, dest, opsize); @@ -2405,6 +2258,7 @@ DISAS_INSN(arith_im) tcg_gen_xor_i32(dest, src1, im); if (with_SR) { gen_set_sr(s, dest, opsize == OS_BYTE); + gen_exit_tb(s); } else { DEST_EA(env, insn, opsize, dest, &addr); gen_logic_cc(s, dest, opsize); @@ -2416,8 +2270,6 @@ DISAS_INSN(arith_im) default: abort(); } - tcg_temp_free(im); - tcg_temp_free(dest); } DISAS_INSN(cas) @@ -2473,8 +2325,6 @@ DISAS_INSN(cas) gen_update_cc_cmp(s, load, cmp, opsize); gen_partset_reg(opsize, DREG(ext, 0), load); - tcg_temp_free(load); - switch (extract32(insn, 3, 3)) { case 3: /* Indirect postincrement. */ tcg_gen_addi_i32(AREG(insn, 0), addr, opsize_bytes(opsize)); @@ -2489,7 +2339,6 @@ DISAS_INSN(cas2w) { uint16_t ext1, ext2; TCGv addr1, addr2; - TCGv regs; /* cas2 Dc1:Dc2,Du1:Du2,(Rn1):(Rn2) */ @@ -2521,16 +2370,15 @@ DISAS_INSN(cas2w) * Dc2 = (R2) */ - regs = tcg_const_i32(REG(ext2, 6) | - (REG(ext1, 6) << 3) | - (REG(ext2, 0) << 6) | - (REG(ext1, 0) << 9)); if (tb_cflags(s->base.tb) & CF_PARALLEL) { - gen_helper_exit_atomic(cpu_env); + gen_helper_exit_atomic(tcg_env); } else { - gen_helper_cas2w(cpu_env, regs, addr1, addr2); + TCGv regs = tcg_constant_i32(REG(ext2, 6) | + (REG(ext1, 6) << 3) | + (REG(ext2, 0) << 6) | + (REG(ext1, 0) << 9)); + gen_helper_cas2w(tcg_env, regs, addr1, addr2); } - tcg_temp_free(regs); /* Note that cas2w also assigned to env->cc_op. */ s->cc_op = CC_OP_CMPW; @@ -2572,16 +2420,15 @@ DISAS_INSN(cas2l) * Dc2 = (R2) */ - regs = tcg_const_i32(REG(ext2, 6) | - (REG(ext1, 6) << 3) | - (REG(ext2, 0) << 6) | - (REG(ext1, 0) << 9)); + regs = tcg_constant_i32(REG(ext2, 6) | + (REG(ext1, 6) << 3) | + (REG(ext2, 0) << 6) | + (REG(ext1, 0) << 9)); if (tb_cflags(s->base.tb) & CF_PARALLEL) { - gen_helper_cas2l_parallel(cpu_env, regs, addr1, addr2); + gen_helper_cas2l_parallel(tcg_env, regs, addr1, addr2); } else { - gen_helper_cas2l(cpu_env, regs, addr1, addr2); + gen_helper_cas2l(tcg_env, regs, addr1, addr2); } - tcg_temp_free(regs); /* Note that cas2l also assigned to env->cc_op. */ s->cc_op = CC_OP_CMPL; @@ -2650,10 +2497,9 @@ DISAS_INSN(negx) * (X, N) = -(src + X); */ - z = tcg_const_i32(0); + z = tcg_constant_i32(0); tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, src, z, QREG_CC_X, z); tcg_gen_sub2_i32(QREG_CC_N, QREG_CC_X, z, z, QREG_CC_N, QREG_CC_X); - tcg_temp_free(z); gen_ext(QREG_CC_N, QREG_CC_N, opsize, 1); tcg_gen_andi_i32(QREG_CC_X, QREG_CC_X, 1); @@ -2696,12 +2542,10 @@ DISAS_INSN(clr) int opsize; TCGv zero; - zero = tcg_const_i32(0); - + zero = tcg_constant_i32(0); opsize = insn_opsize(insn); DEST_EA(env, insn, opsize, zero, NULL); gen_logic_cc(s, zero, opsize); - tcg_temp_free(zero); } DISAS_INSN(move_from_ccr) @@ -2727,7 +2571,6 @@ DISAS_INSN(neg) gen_update_cc_add(dest, src1, opsize); tcg_gen_setcondi_i32(TCG_COND_NE, QREG_CC_X, dest, 0); DEST_EA(env, insn, opsize, dest, &addr); - tcg_temp_free(dest); } DISAS_INSN(move_to_ccr) @@ -2762,14 +2605,16 @@ DISAS_INSN(swap) tcg_gen_shli_i32(src1, reg, 16); tcg_gen_shri_i32(src2, reg, 16); tcg_gen_or_i32(reg, src1, src2); - tcg_temp_free(src2); - tcg_temp_free(src1); gen_logic_cc(s, reg, OS_LONG); } DISAS_INSN(bkpt) { +#if defined(CONFIG_USER_ONLY) gen_exception(s, s->base.pc_next, EXCP_DEBUG); +#else + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); +#endif } DISAS_INSN(pea) @@ -2802,7 +2647,6 @@ DISAS_INSN(ext) else tcg_gen_mov_i32(reg, tmp); gen_logic_cc(s, tmp, OS_LONG); - tcg_temp_free(tmp); } DISAS_INSN(tst) @@ -2825,19 +2669,38 @@ DISAS_INSN(illegal) gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); } -/* ??? This should be atomic. */ DISAS_INSN(tas) { - TCGv dest; - TCGv src1; - TCGv addr; + int mode = extract32(insn, 3, 3); + int reg0 = REG(insn, 0); - dest = tcg_temp_new(); - SRC_EA(env, src1, OS_BYTE, 1, &addr); - gen_logic_cc(s, src1, OS_BYTE); - tcg_gen_ori_i32(dest, src1, 0x80); - DEST_EA(env, insn, OS_BYTE, dest, &addr); - tcg_temp_free(dest); + if (mode == 0) { + /* data register direct */ + TCGv dest = cpu_dregs[reg0]; + gen_logic_cc(s, dest, OS_BYTE); + tcg_gen_ori_tl(dest, dest, 0x80); + } else { + TCGv src1, addr; + + addr = gen_lea_mode(env, s, mode, reg0, OS_BYTE); + if (IS_NULL_QREG(addr)) { + gen_addr_fault(s); + return; + } + src1 = tcg_temp_new(); + tcg_gen_atomic_fetch_or_tl(src1, addr, tcg_constant_tl(0x80), + IS_USER(s), MO_SB); + gen_logic_cc(s, src1, OS_BYTE); + + switch (mode) { + case 3: /* Indirect postincrement. */ + tcg_gen_addi_i32(AREG(insn, 0), addr, 1); + break; + case 4: /* Indirect predecrememnt. */ + tcg_gen_mov_i32(AREG(insn, 0), addr); + break; + } + } } DISAS_INSN(mull) @@ -2875,19 +2738,20 @@ DISAS_INSN(mull) return; } SRC_EA(env, src1, OS_LONG, 0, NULL); - if (m68k_feature(s->env, M68K_FEATURE_M68000)) { + if (m68k_feature(s->env, M68K_FEATURE_M68K)) { tcg_gen_movi_i32(QREG_CC_C, 0); if (sign) { tcg_gen_muls2_i32(QREG_CC_N, QREG_CC_V, src1, DREG(ext, 12)); /* QREG_CC_V is -(QREG_CC_V != (QREG_CC_N >> 31)) */ tcg_gen_sari_i32(QREG_CC_Z, QREG_CC_N, 31); - tcg_gen_setcond_i32(TCG_COND_NE, QREG_CC_V, QREG_CC_V, QREG_CC_Z); + tcg_gen_negsetcond_i32(TCG_COND_NE, QREG_CC_V, + QREG_CC_V, QREG_CC_Z); } else { tcg_gen_mulu2_i32(QREG_CC_N, QREG_CC_V, src1, DREG(ext, 12)); /* QREG_CC_V is -(QREG_CC_V != 0), use QREG_CC_C as 0 */ - tcg_gen_setcond_i32(TCG_COND_NE, QREG_CC_V, QREG_CC_V, QREG_CC_C); + tcg_gen_negsetcond_i32(TCG_COND_NE, QREG_CC_V, + QREG_CC_V, QREG_CC_C); } - tcg_gen_neg_i32(QREG_CC_V, QREG_CC_V); tcg_gen_mov_i32(DREG(ext, 12), QREG_CC_N); tcg_gen_mov_i32(QREG_CC_Z, QREG_CC_N); @@ -2916,7 +2780,6 @@ static void gen_link(DisasContext *s, uint16_t insn, int32_t offset) tcg_gen_mov_i32(reg, tmp); } tcg_gen_addi_i32(QREG_SP, tmp, offset); - tcg_temp_free(tmp); } DISAS_INSN(link) @@ -2947,11 +2810,9 @@ DISAS_INSN(unlk) tmp = gen_load(s, OS_LONG, src, 0, IS_USER(s)); tcg_gen_mov_i32(reg, tmp); tcg_gen_addi_i32(QREG_SP, src, 4); - tcg_temp_free(src); - tcg_temp_free(tmp); } -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) DISAS_INSN(reset) { if (IS_USER(s)) { @@ -2959,7 +2820,7 @@ DISAS_INSN(reset) return; } - gen_helper_reset(cpu_env); + gen_helper_reset(tcg_env); } #endif @@ -2988,10 +2849,8 @@ DISAS_INSN(rtr) tcg_gen_addi_i32(sp, QREG_SP, 2); tmp = gen_load(s, OS_LONG, sp, 0, IS_USER(s)); tcg_gen_addi_i32(QREG_SP, sp, 4); - tcg_temp_free(sp); gen_set_sr(s, ccr, true); - tcg_temp_free(ccr); gen_jmp(s, tmp); } @@ -3020,7 +2879,7 @@ DISAS_INSN(jump) } if ((insn & 0x40) == 0) { /* jsr */ - gen_push(s, tcg_const_i32(s->pc)); + gen_push(s, tcg_constant_i32(s->pc)); } gen_jmp(s, tmp); } @@ -3045,7 +2904,7 @@ DISAS_INSN(addsubq) if (imm == 0) { imm = 8; } - val = tcg_const_i32(imm); + val = tcg_constant_i32(imm); dest = tcg_temp_new(); tcg_gen_mov_i32(dest, src); if ((insn & 0x38) == 0x08) { @@ -3070,9 +2929,7 @@ DISAS_INSN(addsubq) } gen_update_cc_add(dest, val, opsize); } - tcg_temp_free(val); DEST_EA(env, insn, opsize, dest, &addr); - tcg_temp_free(dest); } DISAS_INSN(branch) @@ -3091,7 +2948,7 @@ DISAS_INSN(branch) } if (op == 1) { /* bsr */ - gen_push(s, tcg_const_i32(s->pc)); + gen_push(s, tcg_constant_i32(s->pc)); } if (op > 1) { /* Bcc */ @@ -3150,7 +3007,6 @@ DISAS_INSN(or) gen_partset_reg(opsize, DREG(insn, 9), dest); } gen_logic_cc(s, dest, opsize); - tcg_temp_free(dest); } DISAS_INSN(suba) @@ -3165,7 +3021,7 @@ DISAS_INSN(suba) static inline void gen_subx(DisasContext *s, TCGv src, TCGv dest, int opsize) { - TCGv tmp; + TCGv tmp, zero; gen_flush_flags(s); /* compute old Z */ @@ -3174,18 +3030,18 @@ static inline void gen_subx(DisasContext *s, TCGv src, TCGv dest, int opsize) * (X, N) = dest - (src + X); */ - tmp = tcg_const_i32(0); - tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, src, tmp, QREG_CC_X, tmp); - tcg_gen_sub2_i32(QREG_CC_N, QREG_CC_X, dest, tmp, QREG_CC_N, QREG_CC_X); + zero = tcg_constant_i32(0); + tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, src, zero, QREG_CC_X, zero); + tcg_gen_sub2_i32(QREG_CC_N, QREG_CC_X, dest, zero, QREG_CC_N, QREG_CC_X); gen_ext(QREG_CC_N, QREG_CC_N, opsize, 1); tcg_gen_andi_i32(QREG_CC_X, QREG_CC_X, 1); /* Compute signed-overflow for subtract. */ + tmp = tcg_temp_new(); tcg_gen_xor_i32(QREG_CC_V, QREG_CC_N, dest); tcg_gen_xor_i32(tmp, dest, src); tcg_gen_and_i32(QREG_CC_V, QREG_CC_V, tmp); - tcg_temp_free(tmp); /* Copy the rest of the results into place. */ tcg_gen_or_i32(QREG_CC_Z, QREG_CC_Z, QREG_CC_N); /* !Z is sticky */ @@ -3233,9 +3089,6 @@ DISAS_INSN(subx_mem) gen_subx(s, src, dest, opsize); gen_store(s, opsize, addr_dest, QREG_CC_N, IS_USER(s)); - - tcg_temp_free(dest); - tcg_temp_free(src); } DISAS_INSN(mov3q) @@ -3244,12 +3097,12 @@ DISAS_INSN(mov3q) int val; val = (insn >> 9) & 7; - if (val == 0) + if (val == 0) { val = -1; - src = tcg_const_i32(val); + } + src = tcg_constant_i32(val); gen_logic_cc(s, src, OS_LONG); DEST_EA(env, insn, OS_LONG, src, NULL); - tcg_temp_free(src); } DISAS_INSN(cmp) @@ -3309,7 +3162,6 @@ DISAS_INSN(eor) tcg_gen_xor_i32(dest, src, DREG(insn, 9)); gen_logic_cc(s, dest, opsize); DEST_EA(env, insn, opsize, dest, &addr); - tcg_temp_free(dest); } static void do_exg(TCGv reg1, TCGv reg2) @@ -3318,7 +3170,6 @@ static void do_exg(TCGv reg1, TCGv reg2) tcg_gen_mov_i32(temp, reg1); tcg_gen_mov_i32(reg1, reg2); tcg_gen_mov_i32(reg2, temp); - tcg_temp_free(temp); } DISAS_INSN(exg_dd) @@ -3361,7 +3212,6 @@ DISAS_INSN(and) gen_partset_reg(opsize, reg, dest); } gen_logic_cc(s, dest, opsize); - tcg_temp_free(dest); } DISAS_INSN(adda) @@ -3376,7 +3226,7 @@ DISAS_INSN(adda) static inline void gen_addx(DisasContext *s, TCGv src, TCGv dest, int opsize) { - TCGv tmp; + TCGv tmp, zero; gen_flush_flags(s); /* compute old Z */ @@ -3385,17 +3235,17 @@ static inline void gen_addx(DisasContext *s, TCGv src, TCGv dest, int opsize) * (X, N) = src + dest + X; */ - tmp = tcg_const_i32(0); - tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, QREG_CC_X, tmp, dest, tmp); - tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, QREG_CC_N, QREG_CC_X, src, tmp); + zero = tcg_constant_i32(0); + tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, QREG_CC_X, zero, dest, zero); + tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, QREG_CC_N, QREG_CC_X, src, zero); gen_ext(QREG_CC_N, QREG_CC_N, opsize, 1); /* Compute signed-overflow for addition. */ + tmp = tcg_temp_new(); tcg_gen_xor_i32(QREG_CC_V, QREG_CC_N, src); tcg_gen_xor_i32(tmp, dest, src); tcg_gen_andc_i32(QREG_CC_V, QREG_CC_V, tmp); - tcg_temp_free(tmp); /* Copy the rest of the results into place. */ tcg_gen_or_i32(QREG_CC_Z, QREG_CC_Z, QREG_CC_N); /* !Z is sticky */ @@ -3443,9 +3293,6 @@ DISAS_INSN(addx_mem) gen_addx(s, src, dest, opsize); gen_store(s, opsize, addr_dest, QREG_CC_N, IS_USER(s)); - - tcg_temp_free(dest); - tcg_temp_free(src); } static inline void shift_im(DisasContext *s, uint16_t insn, int opsize) @@ -3470,18 +3317,16 @@ static inline void shift_im(DisasContext *s, uint16_t insn, int opsize) * while M68000 sets if the most significant bit is changed at * any time during the shift operation. */ - if (!logical && m68k_feature(s->env, M68K_FEATURE_M68000)) { + if (!logical && m68k_feature(s->env, M68K_FEATURE_M68K)) { /* if shift count >= bits, V is (reg != 0) */ if (count >= bits) { - tcg_gen_setcond_i32(TCG_COND_NE, QREG_CC_V, reg, QREG_CC_V); + tcg_gen_negsetcond_i32(TCG_COND_NE, QREG_CC_V, reg, QREG_CC_V); } else { TCGv t0 = tcg_temp_new(); tcg_gen_sari_i32(QREG_CC_V, reg, bits - 1); tcg_gen_sari_i32(t0, reg, bits - count - 1); - tcg_gen_setcond_i32(TCG_COND_NE, QREG_CC_V, QREG_CC_V, t0); - tcg_temp_free(t0); + tcg_gen_negsetcond_i32(TCG_COND_NE, QREG_CC_V, QREG_CC_V, t0); } - tcg_gen_neg_i32(QREG_CC_V, QREG_CC_V); } } else { tcg_gen_shri_i32(QREG_CC_C, reg, count - 1); @@ -3532,12 +3377,11 @@ static inline void shift_reg(DisasContext *s, uint16_t insn, int opsize) tcg_gen_extr_i64_i32(QREG_CC_N, QREG_CC_C, t64); /* Note that C=0 if shift count is 0, and we get that for free. */ } else { - TCGv zero = tcg_const_i32(0); + TCGv zero = tcg_constant_i32(0); tcg_gen_extrl_i64_i32(QREG_CC_N, t64); tcg_gen_shri_i32(QREG_CC_C, QREG_CC_N, bits); tcg_gen_movcond_i32(TCG_COND_EQ, QREG_CC_C, s32, zero, zero, QREG_CC_C); - tcg_temp_free(zero); } tcg_gen_andi_i32(QREG_CC_C, QREG_CC_C, 1); @@ -3554,11 +3398,10 @@ static inline void shift_reg(DisasContext *s, uint16_t insn, int opsize) * int64_t t = (int64_t)(intN_t)reg << count; * V = ((s ^ t) & (-1 << (bits - 1))) != 0 */ - if (!logical && m68k_feature(s->env, M68K_FEATURE_M68000)) { - TCGv_i64 tt = tcg_const_i64(32); + if (!logical && m68k_feature(s->env, M68K_FEATURE_M68K)) { + TCGv_i64 tt = tcg_constant_i64(32); /* if shift is greater than 32, use 32 */ tcg_gen_movcond_i64(TCG_COND_GT, s64, s64, tt, tt, s64); - tcg_temp_free_i64(tt); /* Sign extend the input to 64 bits; re-do the shift. */ tcg_gen_ext_i32_i64(t64, reg); tcg_gen_shl_i64(s64, t64, s64); @@ -3567,9 +3410,8 @@ static inline void shift_reg(DisasContext *s, uint16_t insn, int opsize) /* Ignore the bits below the sign bit. */ tcg_gen_andi_i64(t64, t64, -1ULL << (bits - 1)); /* If any bits remain set, we have overflow. */ - tcg_gen_setcondi_i64(TCG_COND_NE, t64, t64, 0); + tcg_gen_negsetcond_i64(TCG_COND_NE, t64, t64, tcg_constant_i64(0)); tcg_gen_extrl_i64_i32(QREG_CC_V, t64); - tcg_gen_neg_i32(QREG_CC_V, QREG_CC_V); } } else { tcg_gen_shli_i64(t64, t64, 32); @@ -3590,10 +3432,6 @@ static inline void shift_reg(DisasContext *s, uint16_t insn, int opsize) gen_ext(QREG_CC_N, QREG_CC_N, opsize, 1); tcg_gen_mov_i32(QREG_CC_Z, QREG_CC_N); - tcg_temp_free(s32); - tcg_temp_free_i64(s64); - tcg_temp_free_i64(t64); - /* Write back the result. */ gen_partset_reg(opsize, DREG(insn, 0), QREG_CC_N); set_cc_op(s, CC_OP_FLAGS); @@ -3647,7 +3485,7 @@ DISAS_INSN(shift_mem) * while M68000 sets if the most significant bit is changed at * any time during the shift operation */ - if (!logical && m68k_feature(s->env, M68K_FEATURE_M68000)) { + if (!logical && m68k_feature(s->env, M68K_FEATURE_M68K)) { src = gen_extend(s, src, OS_WORD, 1); tcg_gen_xor_i32(QREG_CC_V, QREG_CC_N, src); } @@ -3741,7 +3579,7 @@ static TCGv rotate_x(TCGv reg, TCGv shift, int left, int size) { TCGv X, shl, shr, shx, sz, zero; - sz = tcg_const_i32(size); + sz = tcg_constant_i32(size); shr = tcg_temp_new(); shl = tcg_temp_new(); @@ -3752,27 +3590,22 @@ static TCGv rotate_x(TCGv reg, TCGv shift, int left, int size) tcg_gen_sub_i32(shr, shr, shift); /* shr = size + 1 - shift */ tcg_gen_subi_i32(shx, shift, 1); /* shx = shift - 1 */ /* shx = shx < 0 ? size : shx; */ - zero = tcg_const_i32(0); + zero = tcg_constant_i32(0); tcg_gen_movcond_i32(TCG_COND_LT, shx, shx, zero, sz, shx); - tcg_temp_free(zero); } else { tcg_gen_mov_i32(shr, shift); /* shr = shift */ tcg_gen_movi_i32(shl, size + 1); tcg_gen_sub_i32(shl, shl, shift); /* shl = size + 1 - shift */ tcg_gen_sub_i32(shx, sz, shift); /* shx = size - shift */ } - tcg_temp_free_i32(sz); /* reg = (reg << shl) | (reg >> shr) | (x << shx); */ tcg_gen_shl_i32(shl, reg, shl); tcg_gen_shr_i32(shr, reg, shr); tcg_gen_or_i32(reg, shl, shr); - tcg_temp_free(shl); - tcg_temp_free(shr); tcg_gen_shl_i32(shx, QREG_CC_X, shx); tcg_gen_or_i32(reg, reg, shx); - tcg_temp_free(shx); /* X = (reg >> size) & 1 */ @@ -3806,7 +3639,6 @@ static TCGv rotate32_x(TCGv reg, TCGv shift, int left) /* rotate */ tcg_gen_rotl_i64(t0, t0, shift64); - tcg_temp_free_i64(shift64); /* result is [reg:..:reg:X] */ @@ -3820,7 +3652,6 @@ static TCGv rotate32_x(TCGv reg, TCGv shift, int left) tcg_gen_concat_i32_i64(t0, reg, QREG_CC_X); tcg_gen_rotr_i64(t0, t0, shift64); - tcg_temp_free_i64(shift64); /* result is value: [X:reg:..:reg] */ @@ -3834,17 +3665,13 @@ static TCGv rotate32_x(TCGv reg, TCGv shift, int left) tcg_gen_shli_i32(hi, hi, 1); } - tcg_temp_free_i64(t0); tcg_gen_or_i32(lo, lo, hi); - tcg_temp_free(hi); /* if shift == 0, register and X are not affected */ - zero = tcg_const_i32(0); + zero = tcg_constant_i32(0); tcg_gen_movcond_i32(TCG_COND_EQ, X, shift, zero, QREG_CC_X, X); tcg_gen_movcond_i32(TCG_COND_EQ, reg, shift, zero, reg, lo); - tcg_temp_free(zero); - tcg_temp_free(lo); return X; } @@ -3860,15 +3687,13 @@ DISAS_INSN(rotate_im) tmp = 8; } - shift = tcg_const_i32(tmp); + shift = tcg_constant_i32(tmp); if (insn & 8) { rotate(DREG(insn, 0), shift, left, 32); } else { TCGv X = rotate32_x(DREG(insn, 0), shift, left); rotate_x_flags(DREG(insn, 0), X, 32); - tcg_temp_free(X); } - tcg_temp_free(shift); set_cc_op(s, CC_OP_FLAGS); } @@ -3887,15 +3712,13 @@ DISAS_INSN(rotate8_im) tmp = 8; } - shift = tcg_const_i32(tmp); + shift = tcg_constant_i32(tmp); if (insn & 8) { rotate(reg, shift, left, 8); } else { TCGv X = rotate_x(reg, shift, left, 8); rotate_x_flags(reg, X, 8); - tcg_temp_free(X); } - tcg_temp_free(shift); gen_partset_reg(OS_BYTE, DREG(insn, 0), reg); set_cc_op(s, CC_OP_FLAGS); } @@ -3913,15 +3736,13 @@ DISAS_INSN(rotate16_im) tmp = 8; } - shift = tcg_const_i32(tmp); + shift = tcg_constant_i32(tmp); if (insn & 8) { rotate(reg, shift, left, 16); } else { TCGv X = rotate_x(reg, shift, left, 16); rotate_x_flags(reg, X, 16); - tcg_temp_free(X); } - tcg_temp_free(shift); gen_partset_reg(OS_WORD, DREG(insn, 0), reg); set_cc_op(s, CC_OP_FLAGS); } @@ -3953,10 +3774,7 @@ DISAS_INSN(rotate_reg) tcg_gen_remu_i32(t1, t0, t1); X = rotate32_x(DREG(insn, 0), t1, left); rotate_x_flags(DREG(insn, 0), X, 32); - tcg_temp_free(X); } - tcg_temp_free(t1); - tcg_temp_free(t0); set_cc_op(s, CC_OP_FLAGS); } @@ -3987,10 +3805,7 @@ DISAS_INSN(rotate8_reg) tcg_gen_remu_i32(t1, t0, t1); X = rotate_x(reg, t1, left, 8); rotate_x_flags(reg, X, 8); - tcg_temp_free(X); } - tcg_temp_free(t1); - tcg_temp_free(t0); gen_partset_reg(OS_BYTE, DREG(insn, 0), reg); set_cc_op(s, CC_OP_FLAGS); } @@ -4022,10 +3837,7 @@ DISAS_INSN(rotate16_reg) tcg_gen_remu_i32(t1, t0, t1); X = rotate_x(reg, t1, left, 16); rotate_x_flags(reg, X, 16); - tcg_temp_free(X); } - tcg_temp_free(t1); - tcg_temp_free(t0); gen_partset_reg(OS_WORD, DREG(insn, 0), reg); set_cc_op(s, CC_OP_FLAGS); } @@ -4039,15 +3851,13 @@ DISAS_INSN(rotate_mem) SRC_EA(env, src, OS_WORD, 0, &addr); - shift = tcg_const_i32(1); + shift = tcg_constant_i32(1); if (insn & 0x0200) { rotate(src, shift, left, 16); } else { TCGv X = rotate_x(src, shift, left, 16); rotate_x_flags(src, X, 16); - tcg_temp_free(X); } - tcg_temp_free(shift); DEST_EA(env, insn, OS_WORD, src, &addr); set_cc_op(s, CC_OP_FLAGS); } @@ -4088,7 +3898,6 @@ DISAS_INSN(bfext_reg) } else { tcg_gen_shr_i32(dst, tmp, shift); } - tcg_temp_free(shift); } else { /* Immediate width. */ if (ext & 0x800) { @@ -4117,7 +3926,6 @@ DISAS_INSN(bfext_reg) } } - tcg_temp_free(tmp); set_cc_op(s, CC_OP_LOGIC); } @@ -4137,31 +3945,23 @@ DISAS_INSN(bfext_mem) if (ext & 0x20) { len = DREG(ext, 0); } else { - len = tcg_const_i32(extract32(ext, 0, 5)); + len = tcg_constant_i32(extract32(ext, 0, 5)); } if (ext & 0x800) { ofs = DREG(ext, 6); } else { - ofs = tcg_const_i32(extract32(ext, 6, 5)); + ofs = tcg_constant_i32(extract32(ext, 6, 5)); } if (is_sign) { - gen_helper_bfexts_mem(dest, cpu_env, addr, ofs, len); + gen_helper_bfexts_mem(dest, tcg_env, addr, ofs, len); tcg_gen_mov_i32(QREG_CC_N, dest); } else { TCGv_i64 tmp = tcg_temp_new_i64(); - gen_helper_bfextu_mem(tmp, cpu_env, addr, ofs, len); + gen_helper_bfextu_mem(tmp, tcg_env, addr, ofs, len); tcg_gen_extr_i64_i32(dest, QREG_CC_N, tmp); - tcg_temp_free_i64(tmp); } set_cc_op(s, CC_OP_LOGIC); - - if (!(ext & 0x20)) { - tcg_temp_free(len); - } - if (!(ext & 0x800)) { - tcg_temp_free(ofs); - } } DISAS_INSN(bfop_reg) @@ -4170,14 +3970,8 @@ DISAS_INSN(bfop_reg) TCGv src = DREG(insn, 0); int len = ((extract32(ext, 0, 5) - 1) & 31) + 1; int ofs = extract32(ext, 6, 5); /* big bit-endian */ - TCGv mask, tofs, tlen; - - tofs = NULL; - tlen = NULL; - if ((insn & 0x0f00) == 0x0d00) { /* bfffo */ - tofs = tcg_temp_new(); - tlen = tcg_temp_new(); - } + TCGv mask, tofs = NULL, tlen = NULL; + bool is_bfffo = (insn & 0x0f00) == 0x0d00; if ((ext & 0x820) == 0) { /* Immediate width and offset. */ @@ -4188,48 +3982,51 @@ DISAS_INSN(bfop_reg) tcg_gen_rotli_i32(QREG_CC_N, src, ofs); } tcg_gen_andi_i32(QREG_CC_N, QREG_CC_N, ~maski); - mask = tcg_const_i32(ror32(maski, ofs)); - if (tofs) { - tcg_gen_movi_i32(tofs, ofs); - tcg_gen_movi_i32(tlen, len); + + mask = tcg_constant_i32(ror32(maski, ofs)); + if (is_bfffo) { + tofs = tcg_constant_i32(ofs); + tlen = tcg_constant_i32(len); } } else { TCGv tmp = tcg_temp_new(); + + mask = tcg_temp_new(); if (ext & 0x20) { /* Variable width */ tcg_gen_subi_i32(tmp, DREG(ext, 0), 1); tcg_gen_andi_i32(tmp, tmp, 31); - mask = tcg_const_i32(0x7fffffffu); - tcg_gen_shr_i32(mask, mask, tmp); - if (tlen) { + tcg_gen_shr_i32(mask, tcg_constant_i32(0x7fffffffu), tmp); + if (is_bfffo) { + tlen = tcg_temp_new(); tcg_gen_addi_i32(tlen, tmp, 1); } } else { /* Immediate width */ - mask = tcg_const_i32(0x7fffffffu >> (len - 1)); - if (tlen) { - tcg_gen_movi_i32(tlen, len); + tcg_gen_movi_i32(mask, 0x7fffffffu >> (len - 1)); + if (is_bfffo) { + tlen = tcg_constant_i32(len); } } + if (ext & 0x800) { /* Variable offset */ tcg_gen_andi_i32(tmp, DREG(ext, 6), 31); tcg_gen_rotl_i32(QREG_CC_N, src, tmp); tcg_gen_andc_i32(QREG_CC_N, QREG_CC_N, mask); tcg_gen_rotr_i32(mask, mask, tmp); - if (tofs) { - tcg_gen_mov_i32(tofs, tmp); + if (is_bfffo) { + tofs = tmp; } } else { /* Immediate offset (and variable width) */ tcg_gen_rotli_i32(QREG_CC_N, src, ofs); tcg_gen_andc_i32(QREG_CC_N, QREG_CC_N, mask); tcg_gen_rotri_i32(mask, mask, ofs); - if (tofs) { - tcg_gen_movi_i32(tofs, ofs); + if (is_bfffo) { + tofs = tcg_constant_i32(ofs); } } - tcg_temp_free(tmp); } set_cc_op(s, CC_OP_LOGIC); @@ -4242,8 +4039,6 @@ DISAS_INSN(bfop_reg) break; case 0x0d00: /* bfffo */ gen_helper_bfffo_reg(DREG(ext, 12), QREG_CC_N, tofs, tlen); - tcg_temp_free(tlen); - tcg_temp_free(tofs); break; case 0x0e00: /* bfset */ tcg_gen_orc_i32(src, src, mask); @@ -4254,7 +4049,6 @@ DISAS_INSN(bfop_reg) default: g_assert_not_reached(); } - tcg_temp_free(mask); } DISAS_INSN(bfop_mem) @@ -4272,44 +4066,36 @@ DISAS_INSN(bfop_mem) if (ext & 0x20) { len = DREG(ext, 0); } else { - len = tcg_const_i32(extract32(ext, 0, 5)); + len = tcg_constant_i32(extract32(ext, 0, 5)); } if (ext & 0x800) { ofs = DREG(ext, 6); } else { - ofs = tcg_const_i32(extract32(ext, 6, 5)); + ofs = tcg_constant_i32(extract32(ext, 6, 5)); } switch (insn & 0x0f00) { case 0x0a00: /* bfchg */ - gen_helper_bfchg_mem(QREG_CC_N, cpu_env, addr, ofs, len); + gen_helper_bfchg_mem(QREG_CC_N, tcg_env, addr, ofs, len); break; case 0x0c00: /* bfclr */ - gen_helper_bfclr_mem(QREG_CC_N, cpu_env, addr, ofs, len); + gen_helper_bfclr_mem(QREG_CC_N, tcg_env, addr, ofs, len); break; case 0x0d00: /* bfffo */ t64 = tcg_temp_new_i64(); - gen_helper_bfffo_mem(t64, cpu_env, addr, ofs, len); + gen_helper_bfffo_mem(t64, tcg_env, addr, ofs, len); tcg_gen_extr_i64_i32(DREG(ext, 12), QREG_CC_N, t64); - tcg_temp_free_i64(t64); break; case 0x0e00: /* bfset */ - gen_helper_bfset_mem(QREG_CC_N, cpu_env, addr, ofs, len); + gen_helper_bfset_mem(QREG_CC_N, tcg_env, addr, ofs, len); break; case 0x0800: /* bftst */ - gen_helper_bfexts_mem(QREG_CC_N, cpu_env, addr, ofs, len); + gen_helper_bfexts_mem(QREG_CC_N, tcg_env, addr, ofs, len); break; default: g_assert_not_reached(); } set_cc_op(s, CC_OP_LOGIC); - - if (!(ext & 0x20)) { - tcg_temp_free(len); - } - if (!(ext & 0x800)) { - tcg_temp_free(ofs); - } } DISAS_INSN(bfins_reg) @@ -4379,11 +4165,7 @@ DISAS_INSN(bfins_reg) tcg_gen_rotr_i32(tmp, tmp, rot); tcg_gen_and_i32(dst, dst, mask); tcg_gen_or_i32(dst, dst, tmp); - - tcg_temp_free(rot); - tcg_temp_free(mask); } - tcg_temp_free(tmp); } DISAS_INSN(bfins_mem) @@ -4401,23 +4183,16 @@ DISAS_INSN(bfins_mem) if (ext & 0x20) { len = DREG(ext, 0); } else { - len = tcg_const_i32(extract32(ext, 0, 5)); + len = tcg_constant_i32(extract32(ext, 0, 5)); } if (ext & 0x800) { ofs = DREG(ext, 6); } else { - ofs = tcg_const_i32(extract32(ext, 6, 5)); + ofs = tcg_constant_i32(extract32(ext, 6, 5)); } - gen_helper_bfins_mem(QREG_CC_N, cpu_env, addr, src, ofs, len); + gen_helper_bfins_mem(QREG_CC_N, tcg_env, addr, src, ofs, len); set_cc_op(s, CC_OP_LOGIC); - - if (!(ext & 0x20)) { - tcg_temp_free(len); - } - if (!(ext & 0x800)) { - tcg_temp_free(ofs); - } } DISAS_INSN(ff1) @@ -4451,7 +4226,7 @@ DISAS_INSN(chk) reg = gen_extend(s, DREG(insn, 9), opsize, 1); gen_flush_flags(s); - gen_helper_chk(cpu_env, reg, src); + gen_helper_chk(tcg_env, reg, src); } DISAS_INSN(chk2) @@ -4486,9 +4261,7 @@ DISAS_INSN(chk2) tcg_gen_addi_i32(addr2, addr1, opsize_bytes(opsize)); bound1 = gen_load(s, opsize, addr1, 1, IS_USER(s)); - tcg_temp_free(addr1); bound2 = gen_load(s, opsize, addr2, 1, IS_USER(s)); - tcg_temp_free(addr2); reg = tcg_temp_new(); if (ext & 0x8000) { @@ -4498,10 +4271,7 @@ DISAS_INSN(chk2) } gen_flush_flags(s); - gen_helper_chk2(cpu_env, reg, bound1, bound2); - tcg_temp_free(reg); - tcg_temp_free(bound1); - tcg_temp_free(bound2); + gen_helper_chk2(tcg_env, reg, bound1, bound2); } static void m68k_copy_line(TCGv dst, TCGv src, int index) @@ -4515,18 +4285,14 @@ static void m68k_copy_line(TCGv dst, TCGv src, int index) t1 = tcg_temp_new_i64(); tcg_gen_andi_i32(addr, src, ~15); - tcg_gen_qemu_ld64(t0, addr, index); + tcg_gen_qemu_ld_i64(t0, addr, index, MO_TEUQ); tcg_gen_addi_i32(addr, addr, 8); - tcg_gen_qemu_ld64(t1, addr, index); + tcg_gen_qemu_ld_i64(t1, addr, index, MO_TEUQ); tcg_gen_andi_i32(addr, dst, ~15); - tcg_gen_qemu_st64(t0, addr, index); + tcg_gen_qemu_st_i64(t0, addr, index, MO_TEUQ); tcg_gen_addi_i32(addr, addr, 8); - tcg_gen_qemu_st64(t1, addr, index); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free(addr); + tcg_gen_qemu_st_i64(t1, addr, index, MO_TEUQ); } DISAS_INSN(move16_reg) @@ -4547,7 +4313,6 @@ DISAS_INSN(move16_reg) tcg_gen_mov_i32(tmp, AREG(ext, 12)); tcg_gen_addi_i32(AREG(insn, 0), AREG(insn, 0), 16); tcg_gen_addi_i32(AREG(ext, 12), tmp, 16); - tcg_temp_free(tmp); } DISAS_INSN(move16_mem) @@ -4556,7 +4321,7 @@ DISAS_INSN(move16_mem) TCGv reg, addr; reg = AREG(insn, 0); - addr = tcg_const_i32(read_im32(env, s)); + addr = tcg_constant_i32(read_im32(env, s)); if ((insn >> 3) & 1) { /* MOVE16 (xxx).L, (Ay) */ @@ -4566,8 +4331,6 @@ DISAS_INSN(move16_mem) m68k_copy_line(addr, reg, index); } - tcg_temp_free(addr); - if (((insn >> 3) & 2) == 0) { /* (Ay)+ */ tcg_gen_addi_i32(reg, reg, 16); @@ -4592,13 +4355,14 @@ DISAS_INSN(strldsr) } gen_push(s, gen_get_sr(s)); gen_set_sr_im(s, ext, 0); + gen_exit_tb(s); } DISAS_INSN(move_from_sr) { TCGv sr; - if (IS_USER(s) && !m68k_feature(env, M68K_FEATURE_M68000)) { + if (IS_USER(s) && m68k_feature(env, M68K_FEATURE_MOVEFROMSR_PRIV)) { gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -4606,7 +4370,7 @@ DISAS_INSN(move_from_sr) DEST_EA(env, insn, OS_WORD, sr, NULL); } -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) DISAS_INSN(moves) { int opsize; @@ -4651,7 +4415,6 @@ DISAS_INSN(moves) } else { gen_partset_reg(opsize, reg, tmp); } - tcg_temp_free(tmp); } switch (extract32(insn, 3, 3)) { case 3: /* Indirect postincrement. */ @@ -4682,7 +4445,7 @@ DISAS_INSN(move_from_usp) gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } - tcg_gen_ld_i32(AREG(insn, 0), cpu_env, + tcg_gen_ld_i32(AREG(insn, 0), tcg_env, offsetof(CPUM68KState, sp[M68K_USP])); } @@ -4692,7 +4455,7 @@ DISAS_INSN(move_to_usp) gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } - tcg_gen_st_i32(AREG(insn, 0), cpu_env, + tcg_gen_st_i32(AREG(insn, 0), tcg_env, offsetof(CPUM68KState, sp[M68K_USP])); } @@ -4748,14 +4511,14 @@ DISAS_INSN(cf_movec) } else { reg = DREG(ext, 12); } - gen_helper_cf_movec_to(cpu_env, tcg_const_i32(ext & 0xfff), reg); + gen_helper_cf_movec_to(tcg_env, tcg_constant_i32(ext & 0xfff), reg); gen_exit_tb(s); } DISAS_INSN(m68k_movec) { uint16_t ext; - TCGv reg; + TCGv reg, creg; if (IS_USER(s)) { gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); @@ -4769,10 +4532,11 @@ DISAS_INSN(m68k_movec) } else { reg = DREG(ext, 12); } + creg = tcg_constant_i32(ext & 0xfff); if (insn & 1) { - gen_helper_m68k_movec_to(cpu_env, tcg_const_i32(ext & 0xfff), reg); + gen_helper_m68k_movec_to(tcg_env, creg, reg); } else { - gen_helper_m68k_movec_from(reg, cpu_env, tcg_const_i32(ext & 0xfff)); + gen_helper_m68k_movec_from(reg, tcg_env, creg); } gen_exit_tb(s); } @@ -4813,7 +4577,7 @@ DISAS_INSN(cinv) /* Invalidate cache line. Implement as no-op. */ } -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) DISAS_INSN(pflush) { TCGv opmode; @@ -4823,9 +4587,8 @@ DISAS_INSN(pflush) return; } - opmode = tcg_const_i32((insn >> 3) & 3); - gen_helper_pflush(cpu_env, AREG(insn, 0), opmode); - tcg_temp_free(opmode); + opmode = tcg_constant_i32((insn >> 3) & 3); + gen_helper_pflush(tcg_env, AREG(insn, 0), opmode); } DISAS_INSN(ptest) @@ -4836,9 +4599,8 @@ DISAS_INSN(ptest) gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } - is_read = tcg_const_i32((insn >> 5) & 1); - gen_helper_ptest(cpu_env, AREG(insn, 0), is_read); - tcg_temp_free(is_read); + is_read = tcg_constant_i32((insn >> 5) & 1); + gen_helper_ptest(tcg_env, AREG(insn, 0), is_read); } #endif @@ -4884,7 +4646,6 @@ static void do_trapcc(DisasContext *s, DisasCompare *c) s->base.is_jmp = DISAS_NEXT; } } - free_cond(c); } DISAS_INSN(trapcc) @@ -4925,10 +4686,10 @@ static void gen_load_fcr(DisasContext *s, TCGv res, int reg) tcg_gen_movi_i32(res, 0); break; case M68K_FPSR: - tcg_gen_ld_i32(res, cpu_env, offsetof(CPUM68KState, fpsr)); + gen_helper_get_fpsr(res, tcg_env); break; case M68K_FPCR: - tcg_gen_ld_i32(res, cpu_env, offsetof(CPUM68KState, fpcr)); + tcg_gen_ld_i32(res, tcg_env, offsetof(CPUM68KState, fpcr)); break; } } @@ -4939,10 +4700,10 @@ static void gen_store_fcr(DisasContext *s, TCGv val, int reg) case M68K_FPIAR: break; case M68K_FPSR: - tcg_gen_st_i32(val, cpu_env, offsetof(CPUM68KState, fpsr)); + gen_helper_set_fpsr(tcg_env, val); break; case M68K_FPCR: - gen_helper_set_fpcr(cpu_env, val); + gen_helper_set_fpcr(tcg_env, val); break; } } @@ -4954,8 +4715,7 @@ static void gen_qemu_store_fcr(DisasContext *s, TCGv addr, int reg) tmp = tcg_temp_new(); gen_load_fcr(s, tmp, reg); - tcg_gen_qemu_st32(tmp, addr, index); - tcg_temp_free(tmp); + tcg_gen_qemu_st_tl(tmp, addr, index, MO_TEUL); } static void gen_qemu_load_fcr(DisasContext *s, TCGv addr, int reg) @@ -4964,9 +4724,8 @@ static void gen_qemu_load_fcr(DisasContext *s, TCGv addr, int reg) TCGv tmp; tmp = tcg_temp_new(); - tcg_gen_qemu_ld32u(tmp, addr, index); + tcg_gen_qemu_ld_tl(tmp, addr, index, MO_TEUL); gen_store_fcr(s, tmp, reg); - tcg_temp_free(tmp); } @@ -5010,9 +4769,8 @@ static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s, gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); return; } - tmp = tcg_const_i32(read_im32(env, s)); + tmp = tcg_constant_i32(read_im32(env, s)); gen_store_fcr(s, tmp, mask); - tcg_temp_free(tmp); return; } break; @@ -5065,7 +4823,6 @@ static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s, tcg_gen_mov_i32(AREG(insn, 0), addr); } } - tcg_temp_free_i32(addr); } static void gen_op_fmovem(CPUM68KState *env, DisasContext *s, @@ -5103,30 +4860,29 @@ static void gen_op_fmovem(CPUM68KState *env, DisasContext *s, * only available to store register to memory */ if (opsize == OS_EXTENDED) { - gen_helper_fmovemx_st_predec(tmp, cpu_env, addr, tmp); + gen_helper_fmovemx_st_predec(tmp, tcg_env, addr, tmp); } else { - gen_helper_fmovemd_st_predec(tmp, cpu_env, addr, tmp); + gen_helper_fmovemd_st_predec(tmp, tcg_env, addr, tmp); } } else { /* postincrement addressing mode */ if (opsize == OS_EXTENDED) { if (is_load) { - gen_helper_fmovemx_ld_postinc(tmp, cpu_env, addr, tmp); + gen_helper_fmovemx_ld_postinc(tmp, tcg_env, addr, tmp); } else { - gen_helper_fmovemx_st_postinc(tmp, cpu_env, addr, tmp); + gen_helper_fmovemx_st_postinc(tmp, tcg_env, addr, tmp); } } else { if (is_load) { - gen_helper_fmovemd_ld_postinc(tmp, cpu_env, addr, tmp); + gen_helper_fmovemd_ld_postinc(tmp, tcg_env, addr, tmp); } else { - gen_helper_fmovemd_st_postinc(tmp, cpu_env, addr, tmp); + gen_helper_fmovemd_st_postinc(tmp, tcg_env, addr, tmp); } } } if ((insn & 070) == 030 || (insn & 070) == 040) { tcg_gen_mov_i32(AREG(insn, 0), tmp); } - tcg_temp_free(tmp); } /* @@ -5150,11 +4906,9 @@ DISAS_INSN(fpu) case 2: if (insn == 0xf200 && (ext & 0xfc00) == 0x5c00) { /* fmovecr */ - TCGv rom_offset = tcg_const_i32(opmode); + TCGv rom_offset = tcg_constant_i32(opmode); cpu_dest = gen_fp_ptr(REG(ext, 7)); - gen_helper_fconst(cpu_env, cpu_dest, rom_offset); - tcg_temp_free_ptr(cpu_dest); - tcg_temp_free(rom_offset); + gen_helper_fconst(tcg_env, cpu_dest, rom_offset); return; } break; @@ -5165,8 +4919,7 @@ DISAS_INSN(fpu) EA_STORE, IS_USER(s)) == -1) { gen_addr_fault(s); } - gen_helper_ftst(cpu_env, cpu_src); - tcg_temp_free_ptr(cpu_src); + gen_helper_ftst(tcg_env, cpu_src); return; case 4: /* fmove to control register. */ case 5: /* fmove from control register. */ @@ -5200,175 +4953,172 @@ DISAS_INSN(fpu) gen_fp_move(cpu_dest, cpu_src); break; case 0x40: /* fsmove */ - gen_helper_fsround(cpu_env, cpu_dest, cpu_src); + gen_helper_fsround(tcg_env, cpu_dest, cpu_src); break; case 0x44: /* fdmove */ - gen_helper_fdround(cpu_env, cpu_dest, cpu_src); + gen_helper_fdround(tcg_env, cpu_dest, cpu_src); break; case 1: /* fint */ - gen_helper_firound(cpu_env, cpu_dest, cpu_src); + gen_helper_firound(tcg_env, cpu_dest, cpu_src); break; case 2: /* fsinh */ - gen_helper_fsinh(cpu_env, cpu_dest, cpu_src); + gen_helper_fsinh(tcg_env, cpu_dest, cpu_src); break; case 3: /* fintrz */ - gen_helper_fitrunc(cpu_env, cpu_dest, cpu_src); + gen_helper_fitrunc(tcg_env, cpu_dest, cpu_src); break; case 4: /* fsqrt */ - gen_helper_fsqrt(cpu_env, cpu_dest, cpu_src); + gen_helper_fsqrt(tcg_env, cpu_dest, cpu_src); break; case 0x41: /* fssqrt */ - gen_helper_fssqrt(cpu_env, cpu_dest, cpu_src); + gen_helper_fssqrt(tcg_env, cpu_dest, cpu_src); break; case 0x45: /* fdsqrt */ - gen_helper_fdsqrt(cpu_env, cpu_dest, cpu_src); + gen_helper_fdsqrt(tcg_env, cpu_dest, cpu_src); break; case 0x06: /* flognp1 */ - gen_helper_flognp1(cpu_env, cpu_dest, cpu_src); + gen_helper_flognp1(tcg_env, cpu_dest, cpu_src); break; case 0x08: /* fetoxm1 */ - gen_helper_fetoxm1(cpu_env, cpu_dest, cpu_src); + gen_helper_fetoxm1(tcg_env, cpu_dest, cpu_src); break; case 0x09: /* ftanh */ - gen_helper_ftanh(cpu_env, cpu_dest, cpu_src); + gen_helper_ftanh(tcg_env, cpu_dest, cpu_src); break; case 0x0a: /* fatan */ - gen_helper_fatan(cpu_env, cpu_dest, cpu_src); + gen_helper_fatan(tcg_env, cpu_dest, cpu_src); break; case 0x0c: /* fasin */ - gen_helper_fasin(cpu_env, cpu_dest, cpu_src); + gen_helper_fasin(tcg_env, cpu_dest, cpu_src); break; case 0x0d: /* fatanh */ - gen_helper_fatanh(cpu_env, cpu_dest, cpu_src); + gen_helper_fatanh(tcg_env, cpu_dest, cpu_src); break; case 0x0e: /* fsin */ - gen_helper_fsin(cpu_env, cpu_dest, cpu_src); + gen_helper_fsin(tcg_env, cpu_dest, cpu_src); break; case 0x0f: /* ftan */ - gen_helper_ftan(cpu_env, cpu_dest, cpu_src); + gen_helper_ftan(tcg_env, cpu_dest, cpu_src); break; case 0x10: /* fetox */ - gen_helper_fetox(cpu_env, cpu_dest, cpu_src); + gen_helper_fetox(tcg_env, cpu_dest, cpu_src); break; case 0x11: /* ftwotox */ - gen_helper_ftwotox(cpu_env, cpu_dest, cpu_src); + gen_helper_ftwotox(tcg_env, cpu_dest, cpu_src); break; case 0x12: /* ftentox */ - gen_helper_ftentox(cpu_env, cpu_dest, cpu_src); + gen_helper_ftentox(tcg_env, cpu_dest, cpu_src); break; case 0x14: /* flogn */ - gen_helper_flogn(cpu_env, cpu_dest, cpu_src); + gen_helper_flogn(tcg_env, cpu_dest, cpu_src); break; case 0x15: /* flog10 */ - gen_helper_flog10(cpu_env, cpu_dest, cpu_src); + gen_helper_flog10(tcg_env, cpu_dest, cpu_src); break; case 0x16: /* flog2 */ - gen_helper_flog2(cpu_env, cpu_dest, cpu_src); + gen_helper_flog2(tcg_env, cpu_dest, cpu_src); break; case 0x18: /* fabs */ - gen_helper_fabs(cpu_env, cpu_dest, cpu_src); + gen_helper_fabs(tcg_env, cpu_dest, cpu_src); break; case 0x58: /* fsabs */ - gen_helper_fsabs(cpu_env, cpu_dest, cpu_src); + gen_helper_fsabs(tcg_env, cpu_dest, cpu_src); break; case 0x5c: /* fdabs */ - gen_helper_fdabs(cpu_env, cpu_dest, cpu_src); + gen_helper_fdabs(tcg_env, cpu_dest, cpu_src); break; case 0x19: /* fcosh */ - gen_helper_fcosh(cpu_env, cpu_dest, cpu_src); + gen_helper_fcosh(tcg_env, cpu_dest, cpu_src); break; case 0x1a: /* fneg */ - gen_helper_fneg(cpu_env, cpu_dest, cpu_src); + gen_helper_fneg(tcg_env, cpu_dest, cpu_src); break; case 0x5a: /* fsneg */ - gen_helper_fsneg(cpu_env, cpu_dest, cpu_src); + gen_helper_fsneg(tcg_env, cpu_dest, cpu_src); break; case 0x5e: /* fdneg */ - gen_helper_fdneg(cpu_env, cpu_dest, cpu_src); + gen_helper_fdneg(tcg_env, cpu_dest, cpu_src); break; case 0x1c: /* facos */ - gen_helper_facos(cpu_env, cpu_dest, cpu_src); + gen_helper_facos(tcg_env, cpu_dest, cpu_src); break; case 0x1d: /* fcos */ - gen_helper_fcos(cpu_env, cpu_dest, cpu_src); + gen_helper_fcos(tcg_env, cpu_dest, cpu_src); break; case 0x1e: /* fgetexp */ - gen_helper_fgetexp(cpu_env, cpu_dest, cpu_src); + gen_helper_fgetexp(tcg_env, cpu_dest, cpu_src); break; case 0x1f: /* fgetman */ - gen_helper_fgetman(cpu_env, cpu_dest, cpu_src); + gen_helper_fgetman(tcg_env, cpu_dest, cpu_src); break; case 0x20: /* fdiv */ - gen_helper_fdiv(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fdiv(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x60: /* fsdiv */ - gen_helper_fsdiv(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fsdiv(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x64: /* fddiv */ - gen_helper_fddiv(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fddiv(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x21: /* fmod */ - gen_helper_fmod(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fmod(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x22: /* fadd */ - gen_helper_fadd(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fadd(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x62: /* fsadd */ - gen_helper_fsadd(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fsadd(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x66: /* fdadd */ - gen_helper_fdadd(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fdadd(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x23: /* fmul */ - gen_helper_fmul(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fmul(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x63: /* fsmul */ - gen_helper_fsmul(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fsmul(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x67: /* fdmul */ - gen_helper_fdmul(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fdmul(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x24: /* fsgldiv */ - gen_helper_fsgldiv(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fsgldiv(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x25: /* frem */ - gen_helper_frem(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_frem(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x26: /* fscale */ - gen_helper_fscale(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fscale(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x27: /* fsglmul */ - gen_helper_fsglmul(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fsglmul(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x28: /* fsub */ - gen_helper_fsub(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fsub(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x68: /* fssub */ - gen_helper_fssub(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fssub(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x6c: /* fdsub */ - gen_helper_fdsub(cpu_env, cpu_dest, cpu_src, cpu_dest); + gen_helper_fdsub(tcg_env, cpu_dest, cpu_src, cpu_dest); break; case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: { TCGv_ptr cpu_dest2 = gen_fp_ptr(REG(ext, 0)); - gen_helper_fsincos(cpu_env, cpu_dest, cpu_dest2, cpu_src); - tcg_temp_free_ptr(cpu_dest2); + gen_helper_fsincos(tcg_env, cpu_dest, cpu_dest2, cpu_src); } break; case 0x38: /* fcmp */ - gen_helper_fcmp(cpu_env, cpu_src, cpu_dest); + gen_helper_fcmp(tcg_env, cpu_src, cpu_dest); return; case 0x3a: /* ftst */ - gen_helper_ftst(cpu_env, cpu_src); + gen_helper_ftst(tcg_env, cpu_src); return; default: goto undef; } - tcg_temp_free_ptr(cpu_src); - gen_helper_ftst(cpu_env, cpu_dest); - tcg_temp_free_ptr(cpu_dest); + gen_helper_ftst(tcg_env, cpu_dest); return; undef: /* FIXME: Is this right for offset addressing modes? */ @@ -5379,141 +5129,116 @@ undef: static void gen_fcc_cond(DisasCompare *c, DisasContext *s, int cond) { TCGv fpsr; + int imm = 0; - c->g1 = 1; - c->v2 = tcg_const_i32(0); - c->g2 = 0; /* TODO: Raise BSUN exception. */ fpsr = tcg_temp_new(); gen_load_fcr(s, fpsr, M68K_FPSR); + c->v1 = fpsr; + switch (cond) { case 0: /* False */ case 16: /* Signaling False */ - c->v1 = c->v2; c->tcond = TCG_COND_NEVER; break; case 1: /* EQual Z */ case 17: /* Signaling EQual Z */ - c->v1 = tcg_temp_new(); - c->g1 = 0; - tcg_gen_andi_i32(c->v1, fpsr, FPSR_CC_Z); - c->tcond = TCG_COND_NE; + imm = FPSR_CC_Z; + c->tcond = TCG_COND_TSTNE; break; case 2: /* Ordered Greater Than !(A || Z || N) */ case 18: /* Greater Than !(A || Z || N) */ - c->v1 = tcg_temp_new(); - c->g1 = 0; - tcg_gen_andi_i32(c->v1, fpsr, - FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N); - c->tcond = TCG_COND_EQ; + imm = FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N; + c->tcond = TCG_COND_TSTEQ; break; case 3: /* Ordered Greater than or Equal Z || !(A || N) */ case 19: /* Greater than or Equal Z || !(A || N) */ c->v1 = tcg_temp_new(); - c->g1 = 0; tcg_gen_andi_i32(c->v1, fpsr, FPSR_CC_A); tcg_gen_shli_i32(c->v1, c->v1, ctz32(FPSR_CC_N) - ctz32(FPSR_CC_A)); - tcg_gen_andi_i32(fpsr, fpsr, FPSR_CC_Z | FPSR_CC_N); tcg_gen_or_i32(c->v1, c->v1, fpsr); tcg_gen_xori_i32(c->v1, c->v1, FPSR_CC_N); - c->tcond = TCG_COND_NE; + imm = FPSR_CC_Z | FPSR_CC_N; + c->tcond = TCG_COND_TSTNE; break; case 4: /* Ordered Less Than !(!N || A || Z); */ case 20: /* Less Than !(!N || A || Z); */ c->v1 = tcg_temp_new(); - c->g1 = 0; tcg_gen_xori_i32(c->v1, fpsr, FPSR_CC_N); - tcg_gen_andi_i32(c->v1, c->v1, FPSR_CC_N | FPSR_CC_A | FPSR_CC_Z); - c->tcond = TCG_COND_EQ; + imm = FPSR_CC_N | FPSR_CC_A | FPSR_CC_Z; + c->tcond = TCG_COND_TSTEQ; break; case 5: /* Ordered Less than or Equal Z || (N && !A) */ case 21: /* Less than or Equal Z || (N && !A) */ c->v1 = tcg_temp_new(); - c->g1 = 0; tcg_gen_andi_i32(c->v1, fpsr, FPSR_CC_A); tcg_gen_shli_i32(c->v1, c->v1, ctz32(FPSR_CC_N) - ctz32(FPSR_CC_A)); tcg_gen_andc_i32(c->v1, fpsr, c->v1); - tcg_gen_andi_i32(c->v1, c->v1, FPSR_CC_Z | FPSR_CC_N); - c->tcond = TCG_COND_NE; + imm = FPSR_CC_Z | FPSR_CC_N; + c->tcond = TCG_COND_TSTNE; break; case 6: /* Ordered Greater or Less than !(A || Z) */ case 22: /* Greater or Less than !(A || Z) */ - c->v1 = tcg_temp_new(); - c->g1 = 0; - tcg_gen_andi_i32(c->v1, fpsr, FPSR_CC_A | FPSR_CC_Z); - c->tcond = TCG_COND_EQ; + imm = FPSR_CC_A | FPSR_CC_Z; + c->tcond = TCG_COND_TSTEQ; break; case 7: /* Ordered !A */ case 23: /* Greater, Less or Equal !A */ - c->v1 = tcg_temp_new(); - c->g1 = 0; - tcg_gen_andi_i32(c->v1, fpsr, FPSR_CC_A); - c->tcond = TCG_COND_EQ; + imm = FPSR_CC_A; + c->tcond = TCG_COND_TSTEQ; break; case 8: /* Unordered A */ case 24: /* Not Greater, Less or Equal A */ - c->v1 = tcg_temp_new(); - c->g1 = 0; - tcg_gen_andi_i32(c->v1, fpsr, FPSR_CC_A); - c->tcond = TCG_COND_NE; + imm = FPSR_CC_A; + c->tcond = TCG_COND_TSTNE; break; case 9: /* Unordered or Equal A || Z */ case 25: /* Not Greater or Less then A || Z */ - c->v1 = tcg_temp_new(); - c->g1 = 0; - tcg_gen_andi_i32(c->v1, fpsr, FPSR_CC_A | FPSR_CC_Z); - c->tcond = TCG_COND_NE; + imm = FPSR_CC_A | FPSR_CC_Z; + c->tcond = TCG_COND_TSTNE; break; case 10: /* Unordered or Greater Than A || !(N || Z)) */ case 26: /* Not Less or Equal A || !(N || Z)) */ c->v1 = tcg_temp_new(); - c->g1 = 0; tcg_gen_andi_i32(c->v1, fpsr, FPSR_CC_Z); tcg_gen_shli_i32(c->v1, c->v1, ctz32(FPSR_CC_N) - ctz32(FPSR_CC_Z)); - tcg_gen_andi_i32(fpsr, fpsr, FPSR_CC_A | FPSR_CC_N); tcg_gen_or_i32(c->v1, c->v1, fpsr); tcg_gen_xori_i32(c->v1, c->v1, FPSR_CC_N); - c->tcond = TCG_COND_NE; + imm = FPSR_CC_A | FPSR_CC_N; + c->tcond = TCG_COND_TSTNE; break; case 11: /* Unordered or Greater or Equal A || Z || !N */ case 27: /* Not Less Than A || Z || !N */ c->v1 = tcg_temp_new(); - c->g1 = 0; - tcg_gen_andi_i32(c->v1, fpsr, FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N); - tcg_gen_xori_i32(c->v1, c->v1, FPSR_CC_N); - c->tcond = TCG_COND_NE; + tcg_gen_xori_i32(c->v1, fpsr, FPSR_CC_N); + imm = FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N; + c->tcond = TCG_COND_TSTNE; break; case 12: /* Unordered or Less Than A || (N && !Z) */ case 28: /* Not Greater than or Equal A || (N && !Z) */ c->v1 = tcg_temp_new(); - c->g1 = 0; tcg_gen_andi_i32(c->v1, fpsr, FPSR_CC_Z); tcg_gen_shli_i32(c->v1, c->v1, ctz32(FPSR_CC_N) - ctz32(FPSR_CC_Z)); tcg_gen_andc_i32(c->v1, fpsr, c->v1); - tcg_gen_andi_i32(c->v1, c->v1, FPSR_CC_A | FPSR_CC_N); - c->tcond = TCG_COND_NE; + imm = FPSR_CC_A | FPSR_CC_N; + c->tcond = TCG_COND_TSTNE; break; case 13: /* Unordered or Less or Equal A || Z || N */ case 29: /* Not Greater Than A || Z || N */ - c->v1 = tcg_temp_new(); - c->g1 = 0; - tcg_gen_andi_i32(c->v1, fpsr, FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N); - c->tcond = TCG_COND_NE; + imm = FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N; + c->tcond = TCG_COND_TSTNE; break; case 14: /* Not Equal !Z */ case 30: /* Signaling Not Equal !Z */ - c->v1 = tcg_temp_new(); - c->g1 = 0; - tcg_gen_andi_i32(c->v1, fpsr, FPSR_CC_Z); - c->tcond = TCG_COND_EQ; + imm = FPSR_CC_Z; + c->tcond = TCG_COND_TSTEQ; break; case 15: /* True */ case 31: /* Signaling True */ - c->v1 = c->v2; c->tcond = TCG_COND_ALWAYS; break; } - tcg_temp_free(fpsr); + c->v2 = tcg_constant_i32(imm); } static void gen_fjmpcc(DisasContext *s, int cond, TCGLabel *l1) @@ -5523,7 +5248,6 @@ static void gen_fjmpcc(DisasContext *s, int cond, TCGLabel *l1) gen_fcc_cond(&c, s, cond); update_cc_op(s); tcg_gen_brcond_i32(c.tcond, c.v1, c.v2, l1); - free_cond(&c); } DISAS_INSN(fbcc) @@ -5558,12 +5282,9 @@ DISAS_INSN(fscc) gen_fcc_cond(&c, s, cond); tmp = tcg_temp_new(); - tcg_gen_setcond_i32(c.tcond, tmp, c.v1, c.v2); - free_cond(&c); + tcg_gen_negsetcond_i32(c.tcond, tmp, c.v1, c.v2); - tcg_gen_neg_i32(tmp, tmp); DEST_EA(env, insn, OS_BYTE, tmp, NULL); - tcg_temp_free(tmp); } DISAS_INSN(ftrapcc) @@ -5594,7 +5315,7 @@ DISAS_INSN(ftrapcc) do_trapcc(s, &c); } -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) DISAS_INSN(frestore) { TCGv addr; @@ -5620,9 +5341,8 @@ DISAS_INSN(fsave) if (m68k_feature(s->env, M68K_FEATURE_M68040)) { /* always write IDLE */ - TCGv idle = tcg_const_i32(0x41000000); + TCGv idle = tcg_constant_i32(0x41000000); DEST_EA(env, insn, OS_LONG, idle, NULL); - tcg_temp_free(idle); } else { disas_undef(env, s, insn); } @@ -5721,12 +5441,12 @@ DISAS_INSN(mac) ry = gen_mac_extract_word(s, ry, (ext & 0x40) != 0); } if (s->env->macsr & MACSR_FI) { - gen_helper_macmulf(s->mactmp, cpu_env, rx, ry); + gen_helper_macmulf(s->mactmp, tcg_env, rx, ry); } else { if (s->env->macsr & MACSR_SU) - gen_helper_macmuls(s->mactmp, cpu_env, rx, ry); + gen_helper_macmuls(s->mactmp, tcg_env, rx, ry); else - gen_helper_macmulu(s->mactmp, cpu_env, rx, ry); + gen_helper_macmulu(s->mactmp, tcg_env, rx, ry); switch ((ext >> 9) & 3) { case 1: tcg_gen_shli_i64(s->mactmp, s->mactmp, 1); @@ -5751,7 +5471,7 @@ DISAS_INSN(mac) /* Skip the accumulate if the value is already saturated. */ l1 = gen_new_label(); tmp = tcg_temp_new(); - gen_op_and32(tmp, QREG_MACSR, tcg_const_i32(MACSR_PAV0 << acc)); + gen_op_and32(tmp, QREG_MACSR, tcg_constant_i32(MACSR_PAV0 << acc)); gen_op_jmp_nz32(tmp, l1); } #endif @@ -5762,11 +5482,11 @@ DISAS_INSN(mac) tcg_gen_add_i64(MACREG(acc), MACREG(acc), s->mactmp); if (s->env->macsr & MACSR_FI) - gen_helper_macsatf(cpu_env, tcg_const_i32(acc)); + gen_helper_macsatf(tcg_env, tcg_constant_i32(acc)); else if (s->env->macsr & MACSR_SU) - gen_helper_macsats(cpu_env, tcg_const_i32(acc)); + gen_helper_macsats(tcg_env, tcg_constant_i32(acc)); else - gen_helper_macsatu(cpu_env, tcg_const_i32(acc)); + gen_helper_macsatu(tcg_env, tcg_constant_i32(acc)); #if 0 /* Disabled because conditional branches clobber temporary vars. */ @@ -5785,7 +5505,7 @@ DISAS_INSN(mac) /* Skip the accumulate if the value is already saturated. */ l1 = gen_new_label(); tmp = tcg_temp_new(); - gen_op_and32(tmp, QREG_MACSR, tcg_const_i32(MACSR_PAV0 << acc)); + gen_op_and32(tmp, QREG_MACSR, tcg_constant_i32(MACSR_PAV0 << acc)); gen_op_jmp_nz32(tmp, l1); } #endif @@ -5794,18 +5514,18 @@ DISAS_INSN(mac) else tcg_gen_add_i64(MACREG(acc), MACREG(acc), s->mactmp); if (s->env->macsr & MACSR_FI) - gen_helper_macsatf(cpu_env, tcg_const_i32(acc)); + gen_helper_macsatf(tcg_env, tcg_constant_i32(acc)); else if (s->env->macsr & MACSR_SU) - gen_helper_macsats(cpu_env, tcg_const_i32(acc)); + gen_helper_macsats(tcg_env, tcg_constant_i32(acc)); else - gen_helper_macsatu(cpu_env, tcg_const_i32(acc)); + gen_helper_macsatu(tcg_env, tcg_constant_i32(acc)); #if 0 /* Disabled because conditional branches clobber temporary vars. */ if (l1 != -1) gen_set_label(l1); #endif } - gen_helper_mac_set_flags(cpu_env, tcg_const_i32(acc)); + gen_helper_mac_set_flags(tcg_env, tcg_constant_i32(acc)); if (insn & 0x30) { TCGv rw; @@ -5822,7 +5542,6 @@ DISAS_INSN(mac) case 4: /* Pre-decrement. */ tcg_gen_mov_i32(AREG(insn, 0), addr); } - tcg_temp_free(loadval); } } @@ -5836,7 +5555,7 @@ DISAS_INSN(from_mac) accnum = (insn >> 9) & 3; acc = MACREG(accnum); if (s->env->macsr & MACSR_FI) { - gen_helper_get_macf(rx, cpu_env, acc); + gen_helper_get_macf(rx, tcg_env, acc); } else if ((s->env->macsr & MACSR_OMC) == 0) { tcg_gen_extrl_i64_i32(rx, acc); } else if (s->env->macsr & MACSR_SU) { @@ -5856,10 +5575,10 @@ DISAS_INSN(move_mac) int src; TCGv dest; src = insn & 3; - dest = tcg_const_i32((insn >> 9) & 3); - gen_helper_mac_move(cpu_env, dest, tcg_const_i32(src)); + dest = tcg_constant_i32((insn >> 9) & 3); + gen_helper_mac_move(tcg_env, dest, tcg_constant_i32(src)); gen_mac_clear_flags(); - gen_helper_mac_set_flags(cpu_env, dest); + gen_helper_mac_set_flags(tcg_env, dest); } DISAS_INSN(from_macsr) @@ -5882,19 +5601,20 @@ DISAS_INSN(from_mext) TCGv reg; TCGv acc; reg = (insn & 8) ? AREG(insn, 0) : DREG(insn, 0); - acc = tcg_const_i32((insn & 0x400) ? 2 : 0); + acc = tcg_constant_i32((insn & 0x400) ? 2 : 0); if (s->env->macsr & MACSR_FI) - gen_helper_get_mac_extf(reg, cpu_env, acc); + gen_helper_get_mac_extf(reg, tcg_env, acc); else - gen_helper_get_mac_exti(reg, cpu_env, acc); + gen_helper_get_mac_exti(reg, tcg_env, acc); } DISAS_INSN(macsr_to_ccr) { TCGv tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_MACSR, 0xf); - gen_helper_set_sr(cpu_env, tmp); - tcg_temp_free(tmp); + + /* Note that X and C are always cleared. */ + tcg_gen_andi_i32(tmp, QREG_MACSR, CCF_N | CCF_Z | CCF_V); + gen_helper_set_ccr(tcg_env, tmp); set_cc_op(s, CC_OP_FLAGS); } @@ -5916,14 +5636,14 @@ DISAS_INSN(to_mac) } tcg_gen_andi_i32(QREG_MACSR, QREG_MACSR, ~(MACSR_PAV0 << accnum)); gen_mac_clear_flags(); - gen_helper_mac_set_flags(cpu_env, tcg_const_i32(accnum)); + gen_helper_mac_set_flags(tcg_env, tcg_constant_i32(accnum)); } DISAS_INSN(to_macsr) { TCGv val; SRC_EA(env, val, OS_LONG, 0, NULL); - gen_helper_set_macsr(cpu_env, val); + gen_helper_set_macsr(tcg_env, val); gen_exit_tb(s); } @@ -5939,13 +5659,13 @@ DISAS_INSN(to_mext) TCGv val; TCGv acc; SRC_EA(env, val, OS_LONG, 0, NULL); - acc = tcg_const_i32((insn & 0x400) ? 2 : 0); + acc = tcg_constant_i32((insn & 0x400) ? 2 : 0); if (s->env->macsr & MACSR_FI) - gen_helper_set_mac_extf(cpu_env, val, acc); + gen_helper_set_mac_extf(tcg_env, val, acc); else if (s->env->macsr & MACSR_SU) - gen_helper_set_mac_exts(cpu_env, val, acc); + gen_helper_set_mac_exts(tcg_env, val, acc); else - gen_helper_set_mac_extu(cpu_env, val, acc); + gen_helper_set_mac_extu(tcg_env, val, acc); } static disas_proc opcode_table[65536]; @@ -6011,7 +5731,7 @@ void register_m68k_insns (CPUM68KState *env) } while(0) BASE(undef, 0000, 0000); INSN(arith_im, 0080, fff8, CF_ISA_A); - INSN(arith_im, 0000, ff00, M68000); + INSN(arith_im, 0000, ff00, M68K); INSN(chk2, 00c0, f9c0, CHK2); INSN(bitrev, 00c0, fff8, CF_ISA_APLUSC); BASE(bitop_reg, 0100, f1c0); @@ -6020,26 +5740,26 @@ void register_m68k_insns (CPUM68KState *env) BASE(bitop_reg, 01c0, f1c0); INSN(movep, 0108, f138, MOVEP); INSN(arith_im, 0280, fff8, CF_ISA_A); - INSN(arith_im, 0200, ff00, M68000); - INSN(undef, 02c0, ffc0, M68000); + INSN(arith_im, 0200, ff00, M68K); + INSN(undef, 02c0, ffc0, M68K); INSN(byterev, 02c0, fff8, CF_ISA_APLUSC); INSN(arith_im, 0480, fff8, CF_ISA_A); - INSN(arith_im, 0400, ff00, M68000); - INSN(undef, 04c0, ffc0, M68000); - INSN(arith_im, 0600, ff00, M68000); - INSN(undef, 06c0, ffc0, M68000); + INSN(arith_im, 0400, ff00, M68K); + INSN(undef, 04c0, ffc0, M68K); + INSN(arith_im, 0600, ff00, M68K); + INSN(undef, 06c0, ffc0, M68K); INSN(ff1, 04c0, fff8, CF_ISA_APLUSC); INSN(arith_im, 0680, fff8, CF_ISA_A); INSN(arith_im, 0c00, ff38, CF_ISA_A); - INSN(arith_im, 0c00, ff00, M68000); + INSN(arith_im, 0c00, ff00, M68K); BASE(bitop_im, 0800, ffc0); BASE(bitop_im, 0840, ffc0); BASE(bitop_im, 0880, ffc0); BASE(bitop_im, 08c0, ffc0); INSN(arith_im, 0a80, fff8, CF_ISA_A); - INSN(arith_im, 0a00, ff00, M68000); -#if defined(CONFIG_SOFTMMU) - INSN(moves, 0e00, ff00, M68000); + INSN(arith_im, 0a00, ff00, M68K); +#if !defined(CONFIG_USER_ONLY) + INSN(moves, 0e00, ff00, M68K); #endif INSN(cas, 0ac0, ffc0, CAS); INSN(cas, 0cc0, ffc0, CAS); @@ -6049,44 +5769,44 @@ void register_m68k_insns (CPUM68KState *env) BASE(move, 1000, f000); BASE(move, 2000, f000); BASE(move, 3000, f000); - INSN(chk, 4000, f040, M68000); + INSN(chk, 4000, f040, M68K); INSN(strldsr, 40e7, ffff, CF_ISA_APLUSC); INSN(negx, 4080, fff8, CF_ISA_A); - INSN(negx, 4000, ff00, M68000); - INSN(undef, 40c0, ffc0, M68000); + INSN(negx, 4000, ff00, M68K); + INSN(undef, 40c0, ffc0, M68K); INSN(move_from_sr, 40c0, fff8, CF_ISA_A); - INSN(move_from_sr, 40c0, ffc0, M68000); + INSN(move_from_sr, 40c0, ffc0, M68K); BASE(lea, 41c0, f1c0); BASE(clr, 4200, ff00); BASE(undef, 42c0, ffc0); INSN(move_from_ccr, 42c0, fff8, CF_ISA_A); - INSN(move_from_ccr, 42c0, ffc0, M68000); + INSN(move_from_ccr, 42c0, ffc0, M68K); INSN(neg, 4480, fff8, CF_ISA_A); - INSN(neg, 4400, ff00, M68000); - INSN(undef, 44c0, ffc0, M68000); + INSN(neg, 4400, ff00, M68K); + INSN(undef, 44c0, ffc0, M68K); BASE(move_to_ccr, 44c0, ffc0); INSN(not, 4680, fff8, CF_ISA_A); - INSN(not, 4600, ff00, M68000); -#if defined(CONFIG_SOFTMMU) + INSN(not, 4600, ff00, M68K); +#if !defined(CONFIG_USER_ONLY) BASE(move_to_sr, 46c0, ffc0); #endif - INSN(nbcd, 4800, ffc0, M68000); - INSN(linkl, 4808, fff8, M68000); + INSN(nbcd, 4800, ffc0, M68K); + INSN(linkl, 4808, fff8, M68K); BASE(pea, 4840, ffc0); BASE(swap, 4840, fff8); INSN(bkpt, 4848, fff8, BKPT); INSN(movem, 48d0, fbf8, CF_ISA_A); INSN(movem, 48e8, fbf8, CF_ISA_A); - INSN(movem, 4880, fb80, M68000); + INSN(movem, 4880, fb80, M68K); BASE(ext, 4880, fff8); BASE(ext, 48c0, fff8); BASE(ext, 49c0, fff8); BASE(tst, 4a00, ff00); INSN(tas, 4ac0, ffc0, CF_ISA_B); - INSN(tas, 4ac0, ffc0, M68000); -#if defined(CONFIG_SOFTMMU) + INSN(tas, 4ac0, ffc0, M68K); +#if !defined(CONFIG_USER_ONLY) INSN(halt, 4ac8, ffff, CF_ISA_A); - INSN(halt, 4ac8, ffff, M68060); + INSN(halt, 4ac8, ffff, M68K); #endif INSN(pulse, 4acc, ffff, CF_ISA_A); BASE(illegal, 4afc, ffff); @@ -6098,10 +5818,10 @@ void register_m68k_insns (CPUM68KState *env) BASE(trap, 4e40, fff0); BASE(link, 4e50, fff8); BASE(unlk, 4e58, fff8); -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) INSN(move_to_usp, 4e60, fff8, USP); INSN(move_from_usp, 4e68, fff8, USP); - INSN(reset, 4e70, ffff, M68000); + INSN(reset, 4e70, ffff, M68K); BASE(stop, 4e72, ffff); BASE(rte, 4e73, ffff); INSN(cf_movec, 4e7b, ffff, CF_ISA_A); @@ -6110,15 +5830,15 @@ void register_m68k_insns (CPUM68KState *env) BASE(nop, 4e71, ffff); INSN(rtd, 4e74, ffff, RTD); BASE(rts, 4e75, ffff); - INSN(trapv, 4e76, ffff, M68000); - INSN(rtr, 4e77, ffff, M68000); + INSN(trapv, 4e76, ffff, M68K); + INSN(rtr, 4e77, ffff, M68K); BASE(jump, 4e80, ffc0); BASE(jump, 4ec0, ffc0); - INSN(addsubq, 5000, f080, M68000); + INSN(addsubq, 5000, f080, M68K); BASE(addsubq, 5080, f0c0); INSN(scc, 50c0, f0f8, CF_ISA_A); /* Scc.B Dx */ - INSN(scc, 50c0, f0c0, M68000); /* Scc.B */ - INSN(dbcc, 50c8, f0f8, M68000); + INSN(scc, 50c0, f0c0, M68K); /* Scc.B */ + INSN(dbcc, 50c8, f0f8, M68K); INSN(trapcc, 50fa, f0fe, TRAPCC); /* opmode 010, 011 */ INSN(trapcc, 50fc, f0ff, TRAPCC); /* opmode 100 */ INSN(trapcc, 51fa, fffe, CF_ISA_A); /* TPF (trapf) opmode 010, 011 */ @@ -6137,15 +5857,15 @@ void register_m68k_insns (CPUM68KState *env) INSN(mvzs, 7100, f100, CF_ISA_B); BASE(or, 8000, f000); BASE(divw, 80c0, f0c0); - INSN(sbcd_reg, 8100, f1f8, M68000); - INSN(sbcd_mem, 8108, f1f8, M68000); + INSN(sbcd_reg, 8100, f1f8, M68K); + INSN(sbcd_mem, 8108, f1f8, M68K); BASE(addsub, 9000, f000); INSN(undef, 90c0, f0c0, CF_ISA_A); INSN(subx_reg, 9180, f1f8, CF_ISA_A); - INSN(subx_reg, 9100, f138, M68000); - INSN(subx_mem, 9108, f138, M68000); + INSN(subx_reg, 9100, f138, M68K); + INSN(subx_mem, 9108, f138, M68K); INSN(suba, 91c0, f1c0, CF_ISA_A); - INSN(suba, 90c0, f0c0, M68000); + INSN(suba, 90c0, f0c0, M68K); BASE(undef_mac, a000, f000); INSN(mac, a000, f100, CF_EMAC); @@ -6166,41 +5886,41 @@ void register_m68k_insns (CPUM68KState *env) INSN(cmpa, b0c0, f1c0, CF_ISA_B); /* cmpa.w */ INSN(cmp, b080, f1c0, CF_ISA_A); INSN(cmpa, b1c0, f1c0, CF_ISA_A); - INSN(cmp, b000, f100, M68000); - INSN(eor, b100, f100, M68000); - INSN(cmpm, b108, f138, M68000); - INSN(cmpa, b0c0, f0c0, M68000); + INSN(cmp, b000, f100, M68K); + INSN(eor, b100, f100, M68K); + INSN(cmpm, b108, f138, M68K); + INSN(cmpa, b0c0, f0c0, M68K); INSN(eor, b180, f1c0, CF_ISA_A); BASE(and, c000, f000); - INSN(exg_dd, c140, f1f8, M68000); - INSN(exg_aa, c148, f1f8, M68000); - INSN(exg_da, c188, f1f8, M68000); + INSN(exg_dd, c140, f1f8, M68K); + INSN(exg_aa, c148, f1f8, M68K); + INSN(exg_da, c188, f1f8, M68K); BASE(mulw, c0c0, f0c0); - INSN(abcd_reg, c100, f1f8, M68000); - INSN(abcd_mem, c108, f1f8, M68000); + INSN(abcd_reg, c100, f1f8, M68K); + INSN(abcd_mem, c108, f1f8, M68K); BASE(addsub, d000, f000); INSN(undef, d0c0, f0c0, CF_ISA_A); INSN(addx_reg, d180, f1f8, CF_ISA_A); - INSN(addx_reg, d100, f138, M68000); - INSN(addx_mem, d108, f138, M68000); + INSN(addx_reg, d100, f138, M68K); + INSN(addx_mem, d108, f138, M68K); INSN(adda, d1c0, f1c0, CF_ISA_A); - INSN(adda, d0c0, f0c0, M68000); + INSN(adda, d0c0, f0c0, M68K); INSN(shift_im, e080, f0f0, CF_ISA_A); INSN(shift_reg, e0a0, f0f0, CF_ISA_A); - INSN(shift8_im, e000, f0f0, M68000); - INSN(shift16_im, e040, f0f0, M68000); - INSN(shift_im, e080, f0f0, M68000); - INSN(shift8_reg, e020, f0f0, M68000); - INSN(shift16_reg, e060, f0f0, M68000); - INSN(shift_reg, e0a0, f0f0, M68000); - INSN(shift_mem, e0c0, fcc0, M68000); - INSN(rotate_im, e090, f0f0, M68000); - INSN(rotate8_im, e010, f0f0, M68000); - INSN(rotate16_im, e050, f0f0, M68000); - INSN(rotate_reg, e0b0, f0f0, M68000); - INSN(rotate8_reg, e030, f0f0, M68000); - INSN(rotate16_reg, e070, f0f0, M68000); - INSN(rotate_mem, e4c0, fcc0, M68000); + INSN(shift8_im, e000, f0f0, M68K); + INSN(shift16_im, e040, f0f0, M68K); + INSN(shift_im, e080, f0f0, M68K); + INSN(shift8_reg, e020, f0f0, M68K); + INSN(shift16_reg, e060, f0f0, M68K); + INSN(shift_reg, e0a0, f0f0, M68K); + INSN(shift_mem, e0c0, fcc0, M68K); + INSN(rotate_im, e090, f0f0, M68K); + INSN(rotate8_im, e010, f0f0, M68K); + INSN(rotate16_im, e050, f0f0, M68K); + INSN(rotate_reg, e0b0, f0f0, M68K); + INSN(rotate8_reg, e030, f0f0, M68K); + INSN(rotate16_reg, e070, f0f0, M68K); + INSN(rotate_mem, e4c0, fcc0, M68K); INSN(bfext_mem, e9c0, fdc0, BITFIELD); /* bfextu & bfexts */ INSN(bfext_reg, e9c0, fdf8, BITFIELD); INSN(bfins_mem, efc0, ffc0, BITFIELD); @@ -6223,7 +5943,7 @@ void register_m68k_insns (CPUM68KState *env) INSN(ftrapcc, f27a, fffe, FPU); /* opmode 010, 011 */ INSN(ftrapcc, f27c, ffff, FPU); /* opmode 100 */ INSN(fbcc, f280, ff80, FPU); -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) INSN(frestore, f340, ffc0, CF_FPU); INSN(fsave, f300, ffc0, CF_FPU); INSN(frestore, f340, ffc0, FPU); @@ -6245,7 +5965,7 @@ void register_m68k_insns (CPUM68KState *env) static void m68k_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUM68KState *env = cpu->env_ptr; + CPUM68KState *env = cpu_env(cpu); dc->env = env; dc->pc = dc->base.pc_first; @@ -6255,7 +5975,6 @@ static void m68k_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) dc->cc_op_synced = 1; dc->done_mac = 0; dc->writeback_mask = 0; - init_release_array(dc); dc->ss_active = (M68K_SR_TRACE(env->sr) == M68K_SR_TRACE_ANY_INS); /* If architectural single step active, limit to 1 */ @@ -6277,12 +5996,11 @@ static void m68k_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) static void m68k_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUM68KState *env = cpu->env_ptr; + CPUM68KState *env = cpu_env(cpu); uint16_t insn = read_im16(env, dc); opcode_table[insn](env, dc, insn); do_writebacks(dc); - do_release(dc); dc->pc_prev = dc->base.pc_next; dc->base.pc_next = dc->pc; @@ -6361,10 +6079,11 @@ static const TranslatorOps m68k_tr_ops = { .disas_log = m68k_tr_disas_log, }; -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc; - translator_loop(&m68k_tr_ops, &dc.base, cpu, tb, max_insns); + translator_loop(cpu, tb, max_insns, pc, host_pc, &m68k_tr_ops, &dc.base); } static double floatx80_to_double(CPUM68KState *env, uint16_t high, uint64_t low) @@ -6381,8 +6100,7 @@ static double floatx80_to_double(CPUM68KState *env, uint16_t high, uint64_t low) void m68k_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(cs); int i; uint16_t sr; for (i = 0; i < 8; i++) { @@ -6434,7 +6152,7 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, int flags) break; } qemu_fprintf(f, "\n"); -#ifdef CONFIG_SOFTMMU +#ifndef CONFIG_USER_ONLY qemu_fprintf(f, "%sA7(MSP) = %08x %sA7(USP) = %08x %sA7(ISP) = %08x\n", env->current_sp == M68K_SSP ? "->" : " ", env->sp[M68K_SSP], env->current_sp == M68K_USP ? "->" : " ", env->sp[M68K_USP], @@ -6448,15 +6166,5 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, int flags) env->mmu.ttr[M68K_ITTR0], env->mmu.ttr[M68K_ITTR1]); qemu_fprintf(f, "MMUSR %08x, fault at %08x\n", env->mmu.mmusr, env->mmu.ar); -#endif -} - -void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb, - target_ulong *data) -{ - int cc_op = data[1]; - env->pc = data[0]; - if (cc_op != CC_OP_DYNAMIC) { - env->cc_op = cc_op; - } +#endif /* !CONFIG_USER_ONLY */ } diff --git a/target/meson.build b/target/meson.build index 26474c9fe3..eea1802453 100644 --- a/target/meson.build +++ b/target/meson.build @@ -20,3 +20,5 @@ subdir('sh4') subdir('sparc') subdir('tricore') subdir('xtensa') + +specific_ss.add(files('target-common.c')) diff --git a/target/microblaze/cpu-param.h b/target/microblaze/cpu-param.h index 5e54ea0108..9770b0eb52 100644 --- a/target/microblaze/cpu-param.h +++ b/target/microblaze/cpu-param.h @@ -28,6 +28,5 @@ /* FIXME: MB uses variable pages down to 1K but linux only uses 4k. */ #define TARGET_PAGE_BITS 12 -#define NB_MMU_MODES 3 #endif diff --git a/target/microblaze/cpu-qom.h b/target/microblaze/cpu-qom.h index 255b39a45d..92e539fb2f 100644 --- a/target/microblaze/cpu-qom.h +++ b/target/microblaze/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU MicroBlaze CPU + * QEMU MicroBlaze CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -21,27 +21,9 @@ #define QEMU_MICROBLAZE_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_MICROBLAZE_CPU "microblaze-cpu" OBJECT_DECLARE_CPU_TYPE(MicroBlazeCPU, MicroBlazeCPUClass, MICROBLAZE_CPU) -/** - * MicroBlazeCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * - * A MicroBlaze CPU model. - */ -struct MicroBlazeCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceReset parent_reset; -}; - - #endif diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index aed200dcff..96c2b71f7f 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -28,7 +28,9 @@ #include "qemu/module.h" #include "hw/qdev-properties.h" #include "exec/exec-all.h" +#include "exec/gdbstub.h" #include "fpu/softfloat-helpers.h" +#include "tcg/tcg.h" static const struct { const char *name; @@ -84,20 +86,54 @@ static void mb_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.iflags = 0; } +static vaddr mb_cpu_get_pc(CPUState *cs) +{ + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + + return cpu->env.pc; +} + static void mb_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); cpu->env.pc = tb->pc; cpu->env.iflags = tb->flags & IFLAGS_TB_MASK; } +static void mb_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + + cpu->env.pc = data[0]; + cpu->env.iflags = data[1]; +} + static bool mb_cpu_has_work(CPUState *cs) { return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } +static int mb_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + CPUMBState *env = cpu_env(cs); + MicroBlazeCPU *cpu = env_archcpu(env); + + /* Are we in nommu mode?. */ + if (!(env->msr & MSR_VM) || !cpu->cfg.use_mmu) { + return MMU_NOMMU_IDX; + } + + if (env->msr & MSR_UM) { + return MMU_USER_IDX; + } + return MMU_KERNEL_IDX; +} + #ifndef CONFIG_USER_ONLY static void mb_cpu_ns_axi_dp(void *opaque, int irq, int level) { @@ -145,14 +181,16 @@ static void microblaze_cpu_set_irq(void *opaque, int irq, int level) } #endif -static void mb_cpu_reset(DeviceState *dev) +static void mb_cpu_reset_hold(Object *obj) { - CPUState *s = CPU(dev); - MicroBlazeCPU *cpu = MICROBLAZE_CPU(s); - MicroBlazeCPUClass *mcc = MICROBLAZE_CPU_GET_CLASS(cpu); + CPUState *cs = CPU(obj); + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + MicroBlazeCPUClass *mcc = MICROBLAZE_CPU_GET_CLASS(obj); CPUMBState *env = &cpu->env; - mcc->parent_reset(dev); + if (mcc->parent_phases.hold) { + mcc->parent_phases.hold(obj); + } memset(env, 0, offsetof(CPUMBState, end_reset_fields)); env->res_addr = RES_ADDR_NONE; @@ -274,7 +312,10 @@ static void mb_cpu_initfn(Object *obj) MicroBlazeCPU *cpu = MICROBLAZE_CPU(obj); CPUMBState *env = &cpu->env; - cpu_set_cpustate_pointers(cpu); + gdb_register_coprocessor(CPU(cpu), mb_cpu_gdb_read_stack_protect, + mb_cpu_gdb_write_stack_protect, + gdb_find_static_feature("microblaze-stack-protect.xml"), + 0); set_float_rounding_mode(float_round_nearest_even, &env->fp_status); @@ -363,9 +404,10 @@ static const struct SysemuCPUOps mb_sysemu_ops = { #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps mb_tcg_ops = { +static const TCGCPUOps mb_tcg_ops = { .initialize = mb_tcg_init, .synchronize_from_tb = mb_cpu_synchronize_from_tb, + .restore_state_to_opc = mb_restore_state_to_opc, #ifndef CONFIG_USER_ONLY .tlb_fill = mb_cpu_tlb_fill, @@ -381,16 +423,19 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); MicroBlazeCPUClass *mcc = MICROBLAZE_CPU_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); device_class_set_parent_realize(dc, mb_cpu_realizefn, &mcc->parent_realize); - device_class_set_parent_reset(dc, mb_cpu_reset, &mcc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, mb_cpu_reset_hold, NULL, + &mcc->parent_phases); cc->class_by_name = mb_cpu_class_by_name; cc->has_work = mb_cpu_has_work; - + cc->mmu_index = mb_cpu_mmu_index; cc->dump_state = mb_cpu_dump_state; cc->set_pc = mb_cpu_set_pc; + cc->get_pc = mb_cpu_get_pc; cc->gdb_read_register = mb_cpu_gdb_read_register; cc->gdb_write_register = mb_cpu_gdb_write_register; @@ -399,7 +444,7 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) cc->sysemu_ops = &mb_sysemu_ops; #endif device_class_set_props(dc, mb_properties); - cc->gdb_num_core_regs = 32 + 27; + cc->gdb_core_xml_file = "microblaze-core.xml"; cc->disas_set_info = mb_disas_set_info; cc->tcg_ops = &mb_tcg_ops; @@ -409,6 +454,7 @@ static const TypeInfo mb_cpu_type_info = { .name = TYPE_MICROBLAZE_CPU, .parent = TYPE_CPU, .instance_size = sizeof(MicroBlazeCPU), + .instance_align = __alignof(MicroBlazeCPU), .instance_init = mb_cpu_initfn, .class_size = sizeof(MicroBlazeCPUClass), .class_init = mb_cpu_class_init, diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 1e84dd8f47..c0c7574dbd 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -24,6 +24,9 @@ #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" +/* MicroBlaze is always in-order. */ +#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL + typedef struct CPUArchState CPUMBState; #if !defined(CONFIG_USER_ONLY) #include "mmu.h" @@ -202,7 +205,7 @@ typedef struct CPUArchState CPUMBState; #define PVR10_TARGET_FAMILY_MASK 0xFF000000 #define PVR10_ASIZE_SHIFT 18 -/* MMU descrtiption */ +/* MMU description */ #define PVR11_USE_MMU 0xC0000000 #define PVR11_MMU_ITLB_SIZE 0x38000000 #define PVR11_MMU_DTLB_SIZE 0x07000000 @@ -340,33 +343,46 @@ typedef struct { * A MicroBlaze CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ + CPUMBState env; + bool ns_axi_dp; bool ns_axi_ip; bool ns_axi_dc; bool ns_axi_ic; - CPUNegativeOffsetState neg; - CPUMBState env; MicroBlazeCPUConfig cfg; }; +/** + * MicroBlazeCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A MicroBlaze CPU model. + */ +struct MicroBlazeCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; #ifndef CONFIG_USER_ONLY void mb_cpu_do_interrupt(CPUState *cs); bool mb_cpu_exec_interrupt(CPUState *cs, int int_req); +hwaddr mb_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, + MemTxAttrs *attrs); #endif /* !CONFIG_USER_ONLY */ G_NORETURN void mb_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); void mb_cpu_dump_state(CPUState *cpu, FILE *f, int flags); -hwaddr mb_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, - MemTxAttrs *attrs); 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); +int mb_cpu_gdb_read_stack_protect(CPUState *cs, GByteArray *buf, int reg); +int mb_cpu_gdb_write_stack_protect(CPUState *cs, uint8_t *buf, int reg); static inline uint32_t mb_cpu_read_msr(const CPUMBState *env) { @@ -392,15 +408,15 @@ void mb_tcg_init(void); #define MMU_NOMMU_IDX 0 #define MMU_KERNEL_IDX 1 #define MMU_USER_IDX 2 -/* See NB_MMU_MODES further up the file. */ +/* See NB_MMU_MODES in cpu-defs.h. */ #include "exec/cpu-all.h" /* Ensure there is no overlap between the two masks. */ QEMU_BUILD_BUG_ON(MSR_TB_MASK & IFLAGS_TB_MASK); -static inline void cpu_get_tb_cpu_state(CPUMBState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) +static inline void cpu_get_tb_cpu_state(CPUMBState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { *pc = env->pc; *flags = (env->iflags & IFLAGS_TB_MASK) | (env->msr & MSR_TB_MASK); @@ -418,21 +434,6 @@ void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, MemTxResult response, uintptr_t retaddr); #endif -static inline int cpu_mmu_index(CPUMBState *env, bool ifetch) -{ - MicroBlazeCPU *cpu = env_archcpu(env); - - /* Are we in nommu mode?. */ - if (!(env->msr & MSR_VM) || !cpu->cfg.use_mmu) { - return MMU_NOMMU_IDX; - } - - if (env->msr & MSR_UM) { - return MMU_USER_IDX; - } - return MMU_KERNEL_IDX; -} - #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_mb_cpu; #endif diff --git a/target/microblaze/gdbstub.c b/target/microblaze/gdbstub.c index 2e6e070051..09d74e164d 100644 --- a/target/microblaze/gdbstub.c +++ b/target/microblaze/gdbstub.c @@ -19,7 +19,7 @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" /* * GDB expects SREGs in the following order: @@ -39,21 +39,19 @@ enum { GDB_PVR0 = 32 + 6, GDB_PVR11 = 32 + 17, GDB_EDR = 32 + 18, - GDB_SLR = 32 + 25, - GDB_SHR = 32 + 26, +}; + +enum { + GDB_SP_SHL, + GDB_SP_SHR, }; int mb_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - CPUClass *cc = CPU_GET_CLASS(cs); CPUMBState *env = &cpu->env; uint32_t val; - if (n > cc->gdb_num_core_regs) { - return 0; - } - switch (n) { case 1 ... 31: val = env->regs[n]; @@ -83,12 +81,6 @@ int mb_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) case GDB_EDR: val = env->edr; break; - case GDB_SLR: - val = env->slr; - break; - case GDB_SHR: - val = env->shr; - break; default: /* Other SRegs aren't modeled, so report a value of 0 */ val = 0; @@ -97,11 +89,29 @@ int mb_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) return gdb_get_reg32(mem_buf, val); } -int mb_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +int mb_cpu_gdb_read_stack_protect(CPUState *cs, GByteArray *mem_buf, int n) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - CPUClass *cc = CPU_GET_CLASS(cs); CPUMBState *env = &cpu->env; + uint32_t val; + + switch (n) { + case GDB_SP_SHL: + val = env->slr; + break; + case GDB_SP_SHR: + val = env->shr; + break; + default: + return 0; + } + return gdb_get_reg32(mem_buf, val); +} + +int mb_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + CPUClass *cc = CPU_GET_CLASS(cs); + CPUMBState *env = cpu_env(cs); uint32_t tmp; if (n > cc->gdb_num_core_regs) { @@ -135,12 +145,24 @@ int mb_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) case GDB_EDR: env->edr = tmp; break; - case GDB_SLR: - env->slr = tmp; - break; - case GDB_SHR: - env->shr = tmp; - break; + } + return 4; +} + +int mb_cpu_gdb_write_stack_protect(CPUState *cs, uint8_t *mem_buf, int n) +{ + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + CPUMBState *env = &cpu->env; + + switch (n) { + case GDB_SP_SHL: + env->slr = ldl_p(mem_buf); + break; + case GDB_SP_SHR: + env->shr = ldl_p(mem_buf); + break; + default: + return 0; } return 4; } diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index a607fe68e5..d25c9eb4d3 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -228,10 +228,9 @@ hwaddr mb_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, MemTxAttrs *attrs) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - CPUMBState *env = &cpu->env; target_ulong vaddr, paddr = 0; MicroBlazeMMULookup lu; - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = cpu_mmu_index(cs, false); unsigned int hit; /* Caller doesn't initialize */ @@ -253,8 +252,7 @@ hwaddr mb_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, bool mb_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { - MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - CPUMBState *env = &cpu->env; + CPUMBState *env = cpu_env(cs); if ((interrupt_request & CPU_INTERRUPT_HARD) && (env->msr & MSR_IE) @@ -277,7 +275,7 @@ void mb_cpu_do_unaligned_access(CPUState *cs, vaddr addr, uint32_t esr, iflags; /* Recover the pc and iflags from the corresponding insn_start. */ - cpu_restore_state(cs, retaddr, true); + cpu_restore_state(cs, retaddr); iflags = cpu->env.iflags; qemu_log_mask(CPU_LOG_INT, diff --git a/target/microblaze/machine.c b/target/microblaze/machine.c index d24def3992..51705e4f5c 100644 --- a/target/microblaze/machine.c +++ b/target/microblaze/machine.c @@ -22,7 +22,7 @@ #include "migration/cpu.h" -static VMStateField vmstate_mmu_fields[] = { +static const VMStateField vmstate_mmu_fields[] = { VMSTATE_UINT64_2DARRAY(rams, MicroBlazeMMU, 2, TLB_ENTRIES), VMSTATE_UINT8_ARRAY(tids, MicroBlazeMMU, TLB_ENTRIES), VMSTATE_UINT32_ARRAY(regs, MicroBlazeMMU, 3), @@ -60,7 +60,7 @@ static const VMStateInfo vmstate_msr = { .put = put_msr, }; -static VMStateField vmstate_env_fields[] = { +static const VMStateField vmstate_env_fields[] = { VMSTATE_UINT32_ARRAY(regs, CPUMBState, 32), VMSTATE_UINT32(pc, CPUMBState), @@ -92,7 +92,7 @@ static const VMStateDescription vmstate_env = { .fields = vmstate_env_fields, }; -static VMStateField vmstate_cpu_fields[] = { +static const VMStateField vmstate_cpu_fields[] = { VMSTATE_CPU(), VMSTATE_STRUCT(env, MicroBlazeCPU, 1, vmstate_env, CPUMBState), VMSTATE_END_OF_LIST() diff --git a/target/microblaze/meson.build b/target/microblaze/meson.build index 05ee0ec163..3ed4fbb67a 100644 --- a/target/microblaze/meson.build +++ b/target/microblaze/meson.build @@ -10,11 +10,11 @@ microblaze_ss.add(files( 'translate.c', )) -microblaze_softmmu_ss = ss.source_set() -microblaze_softmmu_ss.add(files( +microblaze_system_ss = ss.source_set() +microblaze_system_ss.add(files( 'mmu.c', 'machine.c', )) target_arch += {'microblaze': microblaze_ss} -target_softmmu_arch += {'microblaze': microblaze_softmmu_ss} +target_system_arch += {'microblaze': microblaze_system_ss} diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 75651979a9..234006634e 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -305,7 +305,7 @@ void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) } hit = mmu_translate(cpu, &lu, v & TLB_EPN_MASK, - 0, cpu_mmu_index(env, false)); + 0, cpu_mmu_index(env_cpu(env), false)); if (hit) { env->mmu.regs[MMU_R_TLBX] = lu.idx; } else { diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index 5b745d0928..f6378030b7 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -403,7 +403,7 @@ void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, CPUMBState *env = &cpu->env; qemu_log_mask(CPU_LOG_INT, "Transaction failed: vaddr 0x%" VADDR_PRIx - " physaddr 0x" TARGET_FMT_plx " size %d access type %s\n", + " physaddr 0x" HWADDR_FMT_plx " size %d access type %s\n", addr, physaddr, size, access_type == MMU_INST_FETCH ? "INST_FETCH" : (access_type == MMU_DATA_LOAD ? "DATA_LOAD" : "DATA_STORE")); diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index bf01384d33..fc451befae 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -24,13 +24,16 @@ #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" #include "exec/helper-gen.h" #include "exec/translator.h" #include "qemu/qemu-print.h" #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + #define EXTRACT_FIELD(src, start, end) \ (((src) >> start) & ((1 << (end - start + 1)) - 1)) @@ -54,16 +57,11 @@ static TCGv_i32 cpu_iflags; static TCGv cpu_res_addr; static TCGv_i32 cpu_res_val; -#include "exec/gen-icount.h" - /* This is the state at translation time. */ typedef struct DisasContext { DisasContextBase base; const MicroBlazeCPUConfig *cfg; - /* TCG op of the current insn_start. */ - TCGOp *insn_start; - TCGv_i32 r0; bool r0_set; @@ -101,10 +99,7 @@ static void t_sync_flags(DisasContext *dc) static void gen_raise_exception(DisasContext *dc, uint32_t index) { - TCGv_i32 tmp = tcg_const_i32(index); - - gen_helper_raise_exception(cpu_env, tmp); - tcg_temp_free_i32(tmp); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(index)); dc->base.is_jmp = DISAS_NORETURN; } @@ -117,9 +112,8 @@ static void gen_raise_exception_sync(DisasContext *dc, uint32_t index) static void gen_raise_hw_excp(DisasContext *dc, uint32_t esr_ec) { - TCGv_i32 tmp = tcg_const_i32(esr_ec); - tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUMBState, esr)); - tcg_temp_free_i32(tmp); + TCGv_i32 tmp = tcg_constant_i32(esr_ec); + tcg_gen_st_i32(tmp, tcg_env, offsetof(CPUMBState, esr)); gen_raise_exception_sync(dc, EXCP_HW_EXCP); } @@ -262,11 +256,9 @@ static bool do_typeb_val(DisasContext *dc, arg_typeb *arg, bool side_effects, rd = reg_for_write(dc, arg->rd); ra = reg_for_read(dc, arg->ra); - imm = tcg_const_i32(arg->imm); + imm = tcg_constant_i32(arg->imm); fn(rd, ra, imm); - - tcg_temp_free_i32(imm); return true; } @@ -300,33 +292,28 @@ static bool do_typeb_val(DisasContext *dc, arg_typeb *arg, bool side_effects, #define ENV_WRAPPER2(NAME, HELPER) \ static void NAME(TCGv_i32 out, TCGv_i32 ina) \ - { HELPER(out, cpu_env, ina); } + { HELPER(out, tcg_env, ina); } #define ENV_WRAPPER3(NAME, HELPER) \ static void NAME(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) \ - { HELPER(out, cpu_env, ina, inb); } + { HELPER(out, tcg_env, ina, inb); } /* No input carry, but output carry. */ static void gen_add(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 zero = tcg_constant_i32(0); tcg_gen_add2_i32(out, cpu_msr_c, ina, zero, inb, zero); - - tcg_temp_free_i32(zero); } /* Input and output carry. */ static void gen_addc(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 zero = tcg_constant_i32(0); TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_add2_i32(tmp, cpu_msr_c, ina, zero, cpu_msr_c, zero); tcg_gen_add2_i32(out, cpu_msr_c, tmp, cpu_msr_c, inb, zero); - - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(zero); } /* Input carry, but no output carry. */ @@ -361,7 +348,6 @@ static void gen_bsra(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_andi_i32(tmp, inb, 31); tcg_gen_sar_i32(out, ina, tmp); - tcg_temp_free_i32(tmp); } static void gen_bsrl(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) @@ -369,7 +355,6 @@ static void gen_bsrl(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_andi_i32(tmp, inb, 31); tcg_gen_shr_i32(out, ina, tmp); - tcg_temp_free_i32(tmp); } static void gen_bsll(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) @@ -377,7 +362,6 @@ static void gen_bsll(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_andi_i32(tmp, inb, 31); tcg_gen_shl_i32(out, ina, tmp); - tcg_temp_free_i32(tmp); } static void gen_bsefi(TCGv_i32 out, TCGv_i32 ina, int32_t imm) @@ -436,7 +420,6 @@ static void gen_cmp(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) tcg_gen_setcond_i32(TCG_COND_LT, lt, inb, ina); tcg_gen_sub_i32(out, inb, ina); tcg_gen_deposit_i32(out, out, lt, 31, 1); - tcg_temp_free_i32(lt); } static void gen_cmpu(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) @@ -446,7 +429,6 @@ static void gen_cmpu(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) tcg_gen_setcond_i32(TCG_COND_LTU, lt, inb, ina); tcg_gen_sub_i32(out, inb, ina); tcg_gen_deposit_i32(out, out, lt, 31, 1); - tcg_temp_free_i32(lt); } DO_TYPEA(cmp, false, gen_cmp) @@ -487,12 +469,12 @@ DO_TYPEA0_CFG(fsqrt, use_fpu >= 2, true, gen_fsqrt) /* Does not use ENV_WRAPPER3, because arguments are swapped as well. */ static void gen_idiv(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - gen_helper_divs(out, cpu_env, inb, ina); + gen_helper_divs(out, tcg_env, inb, ina); } static void gen_idivu(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - gen_helper_divu(out, cpu_env, inb, ina); + gen_helper_divu(out, tcg_env, inb, ina); } DO_TYPEA_CFG(idiv, use_div, true, gen_idiv) @@ -513,21 +495,18 @@ static void gen_mulh(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_muls2_i32(tmp, out, ina, inb); - tcg_temp_free_i32(tmp); } static void gen_mulhu(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_mulu2_i32(tmp, out, ina, inb); - tcg_temp_free_i32(tmp); } static void gen_mulhsu(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_mulsu2_i32(tmp, out, ina, inb); - tcg_temp_free_i32(tmp); } DO_TYPEA_CFG(mul, use_hw_mul, false, tcg_gen_mul_i32) @@ -563,15 +542,12 @@ static void gen_rsub(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) /* Input and output carry. */ static void gen_rsubc(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 zero = tcg_constant_i32(0); TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_not_i32(tmp, ina); tcg_gen_add2_i32(tmp, cpu_msr_c, tmp, zero, cpu_msr_c, zero); tcg_gen_add2_i32(out, cpu_msr_c, tmp, cpu_msr_c, inb, zero); - - tcg_temp_free_i32(zero); - tcg_temp_free_i32(tmp); } /* No input or output carry. */ @@ -588,8 +564,6 @@ static void gen_rsubkc(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) tcg_gen_not_i32(nota, ina); tcg_gen_add_i32(out, inb, nota); tcg_gen_add_i32(out, out, cpu_msr_c); - - tcg_temp_free_i32(nota); } DO_TYPEA(rsub, true, gen_rsub) @@ -618,8 +592,6 @@ static void gen_src(TCGv_i32 out, TCGv_i32 ina) tcg_gen_mov_i32(tmp, cpu_msr_c); tcg_gen_andi_i32(cpu_msr_c, ina, 1); tcg_gen_extract2_i32(out, ina, tmp, 1); - - tcg_temp_free_i32(tmp); } static void gen_srl(TCGv_i32 out, TCGv_i32 ina) @@ -659,7 +631,6 @@ static TCGv compute_ldst_addr_typea(DisasContext *dc, int ra, int rb) TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_add_i32(tmp, cpu_R[ra], cpu_R[rb]); tcg_gen_extu_i32_tl(ret, tmp); - tcg_temp_free_i32(tmp); } else if (ra) { tcg_gen_extu_i32_tl(ret, cpu_R[ra]); } else if (rb) { @@ -669,7 +640,7 @@ static TCGv compute_ldst_addr_typea(DisasContext *dc, int ra, int rb) } if ((ra == 1 || rb == 1) && dc->cfg->stackprot) { - gen_helper_stackprot(cpu_env, ret); + gen_helper_stackprot(tcg_env, ret); } return ret; } @@ -683,13 +654,12 @@ static TCGv compute_ldst_addr_typeb(DisasContext *dc, int ra, int imm) TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_addi_i32(tmp, cpu_R[ra], imm); tcg_gen_extu_i32_tl(ret, tmp); - tcg_temp_free_i32(tmp); } else { tcg_gen_movi_tl(ret, (uint32_t)imm); } if (ra == 1 && dc->cfg->stackprot) { - gen_helper_stackprot(cpu_env, ret); + gen_helper_stackprot(tcg_env, ret); } return ret; } @@ -726,14 +696,14 @@ static TCGv compute_ldst_addr_ea(DisasContext *dc, int ra, int rb) static void record_unaligned_ess(DisasContext *dc, int rd, MemOp size, bool store) { - uint32_t iflags = tcg_get_insn_start_param(dc->insn_start, 1); + uint32_t iflags = tcg_get_insn_start_param(dc->base.insn_start, 1); iflags |= ESR_ESS_FLAG; iflags |= rd << 5; iflags |= store * ESR_S; iflags |= (size == MO_32) * ESR_W; - tcg_set_insn_start_param(dc->insn_start, 1, iflags); + tcg_set_insn_start_param(dc->base.insn_start, 1, iflags); } #endif @@ -772,8 +742,6 @@ static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop, #endif tcg_gen_qemu_ld_i32(reg_for_write(dc, rd), addr, mem_index, mop); - - tcg_temp_free(addr); return true; } @@ -879,7 +847,6 @@ static bool trans_lwx(DisasContext *dc, arg_typea *arg) tcg_gen_qemu_ld_i32(cpu_res_val, addr, dc->mem_index, MO_TEUL); tcg_gen_mov_tl(cpu_res_addr, addr); - tcg_temp_free(addr); if (arg->rd) { tcg_gen_mov_i32(cpu_R[arg->rd], cpu_res_val); @@ -925,8 +892,6 @@ static bool do_store(DisasContext *dc, int rd, TCGv addr, MemOp mop, #endif tcg_gen_qemu_st_i32(reg_for_read(dc, rd), addr, mem_index, mop); - - tcg_temp_free(addr); return true; } @@ -1040,7 +1005,6 @@ static bool trans_swx(DisasContext *dc, arg_typea *arg) * In either case, addr is no longer needed. */ tcg_gen_brcond_tl(TCG_COND_NE, cpu_res_addr, addr, swx_fail); - tcg_temp_free(addr); /* * Compare the value loaded during lwx with current contents of @@ -1053,7 +1017,6 @@ static bool trans_swx(DisasContext *dc, arg_typea *arg) dc->mem_index, MO_TEUL); tcg_gen_brcond_i32(TCG_COND_NE, cpu_res_val, tval, swx_fail); - tcg_temp_free_i32(tval); /* Success */ tcg_gen_movi_i32(cpu_msr_c, 0); @@ -1150,13 +1113,11 @@ static bool do_bcc(DisasContext *dc, int dest_rb, int dest_imm, } /* Compute the final destination into btarget. */ - zero = tcg_const_i32(0); - next = tcg_const_i32(dc->base.pc_next + (delay + 1) * 4); + zero = tcg_constant_i32(0); + next = tcg_constant_i32(dc->base.pc_next + (delay + 1) * 4); tcg_gen_movcond_i32(dc->jmp_cond, cpu_btarget, reg_for_read(dc, ra), zero, cpu_btarget, next); - tcg_temp_free_i32(zero); - tcg_temp_free_i32(next); return true; } @@ -1261,8 +1222,6 @@ static bool trans_mbar(DisasContext *dc, arg_mbar *arg) /* Sleep. */ if (mbar_imm & 16) { - TCGv_i32 tmp_1; - if (trap_userspace(dc, true)) { /* Sleep is a privileged instruction. */ return true; @@ -1270,11 +1229,9 @@ static bool trans_mbar(DisasContext *dc, arg_mbar *arg) t_sync_flags(dc); - tmp_1 = tcg_const_i32(1); - tcg_gen_st_i32(tmp_1, cpu_env, + tcg_gen_st_i32(tcg_constant_i32(1), tcg_env, -offsetof(MicroBlazeCPU, env) +offsetof(CPUState, halted)); - tcg_temp_free_i32(tmp_1); tcg_gen_movi_i32(cpu_pc, dc->base.pc_next + 4); @@ -1345,7 +1302,6 @@ static void msr_read(DisasContext *dc, TCGv_i32 d) t = tcg_temp_new_i32(); tcg_gen_muli_i32(t, cpu_msr_c, MSR_C | MSR_CC); tcg_gen_or_i32(d, cpu_msr, t); - tcg_temp_free_i32(t); } static bool do_msrclrset(DisasContext *dc, arg_type_msr *arg, bool set) @@ -1422,13 +1378,13 @@ static bool trans_mts(DisasContext *dc, arg_mts *arg) tcg_gen_andi_i32(cpu_msr, src, ~(MSR_C | MSR_CC | MSR_PVR)); break; case SR_FSR: - tcg_gen_st_i32(src, cpu_env, offsetof(CPUMBState, fsr)); + tcg_gen_st_i32(src, tcg_env, offsetof(CPUMBState, fsr)); break; case 0x800: - tcg_gen_st_i32(src, cpu_env, offsetof(CPUMBState, slr)); + tcg_gen_st_i32(src, tcg_env, offsetof(CPUMBState, slr)); break; case 0x802: - tcg_gen_st_i32(src, cpu_env, offsetof(CPUMBState, shr)); + tcg_gen_st_i32(src, tcg_env, offsetof(CPUMBState, shr)); break; case 0x1000: /* PID */ @@ -1438,12 +1394,10 @@ static bool trans_mts(DisasContext *dc, arg_mts *arg) case 0x1004: /* TLBHI */ case 0x1005: /* TLBSX */ { - TCGv_i32 tmp_ext = tcg_const_i32(arg->e); - TCGv_i32 tmp_reg = tcg_const_i32(arg->rs & 7); + TCGv_i32 tmp_ext = tcg_constant_i32(arg->e); + TCGv_i32 tmp_reg = tcg_constant_i32(arg->rs & 7); - gen_helper_mmu_write(cpu_env, tmp_ext, tmp_reg, src); - tcg_temp_free_i32(tmp_reg); - tcg_temp_free_i32(tmp_ext); + gen_helper_mmu_write(tcg_env, tmp_ext, tmp_reg, src); } break; @@ -1465,9 +1419,8 @@ static bool trans_mfs(DisasContext *dc, arg_mfs *arg) case SR_EAR: { TCGv_i64 t64 = tcg_temp_new_i64(); - tcg_gen_ld_i64(t64, cpu_env, offsetof(CPUMBState, ear)); + tcg_gen_ld_i64(t64, tcg_env, offsetof(CPUMBState, ear)); tcg_gen_extrh_i64_i32(dest, t64); - tcg_temp_free_i64(t64); } return true; #ifndef CONFIG_USER_ONLY @@ -1496,28 +1449,27 @@ static bool trans_mfs(DisasContext *dc, arg_mfs *arg) case SR_EAR: { TCGv_i64 t64 = tcg_temp_new_i64(); - tcg_gen_ld_i64(t64, cpu_env, offsetof(CPUMBState, ear)); + tcg_gen_ld_i64(t64, tcg_env, offsetof(CPUMBState, ear)); tcg_gen_extrl_i64_i32(dest, t64); - tcg_temp_free_i64(t64); } break; case SR_ESR: - tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, esr)); + tcg_gen_ld_i32(dest, tcg_env, offsetof(CPUMBState, esr)); break; case SR_FSR: - tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, fsr)); + tcg_gen_ld_i32(dest, tcg_env, offsetof(CPUMBState, fsr)); break; case SR_BTR: - tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, btr)); + tcg_gen_ld_i32(dest, tcg_env, offsetof(CPUMBState, btr)); break; case SR_EDR: - tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, edr)); + tcg_gen_ld_i32(dest, tcg_env, offsetof(CPUMBState, edr)); break; case 0x800: - tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, slr)); + tcg_gen_ld_i32(dest, tcg_env, offsetof(CPUMBState, slr)); break; case 0x802: - tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, shr)); + tcg_gen_ld_i32(dest, tcg_env, offsetof(CPUMBState, shr)); break; #ifndef CONFIG_USER_ONLY @@ -1528,18 +1480,16 @@ static bool trans_mfs(DisasContext *dc, arg_mfs *arg) case 0x1004: /* TLBHI */ case 0x1005: /* TLBSX */ { - TCGv_i32 tmp_ext = tcg_const_i32(arg->e); - TCGv_i32 tmp_reg = tcg_const_i32(arg->rs & 7); + TCGv_i32 tmp_ext = tcg_constant_i32(arg->e); + TCGv_i32 tmp_reg = tcg_constant_i32(arg->rs & 7); - gen_helper_mmu_read(dest, cpu_env, tmp_ext, tmp_reg); - tcg_temp_free_i32(tmp_reg); - tcg_temp_free_i32(tmp_ext); + gen_helper_mmu_read(dest, tcg_env, tmp_ext, tmp_reg); } break; #endif case 0x2000 ... 0x200c: - tcg_gen_ld_i32(dest, cpu_env, + tcg_gen_ld_i32(dest, tcg_env, offsetof(MicroBlazeCPU, cfg.pvr_regs[arg->rs - 0x2000]) - offsetof(MicroBlazeCPU, env)); break; @@ -1559,8 +1509,6 @@ static void do_rti(DisasContext *dc) tcg_gen_andi_i32(tmp, tmp, MSR_VM | MSR_UM); tcg_gen_andi_i32(cpu_msr, cpu_msr, ~(MSR_VM | MSR_UM)); tcg_gen_or_i32(cpu_msr, cpu_msr, tmp); - - tcg_temp_free_i32(tmp); } static void do_rtb(DisasContext *dc) @@ -1571,8 +1519,6 @@ static void do_rtb(DisasContext *dc) tcg_gen_andi_i32(cpu_msr, cpu_msr, ~(MSR_VM | MSR_UM | MSR_BIP)); tcg_gen_andi_i32(tmp, tmp, (MSR_VM | MSR_UM)); tcg_gen_or_i32(cpu_msr, cpu_msr, tmp); - - tcg_temp_free_i32(tmp); } static void do_rte(DisasContext *dc) @@ -1584,8 +1530,6 @@ static void do_rte(DisasContext *dc) tcg_gen_andi_i32(tmp, tmp, (MSR_VM | MSR_UM)); tcg_gen_andi_i32(cpu_msr, cpu_msr, ~(MSR_VM | MSR_UM | MSR_EIP)); tcg_gen_or_i32(cpu_msr, cpu_msr, tmp); - - tcg_temp_free_i32(tmp); } /* Insns connected to FSL or AXI stream attached devices. */ @@ -1604,10 +1548,8 @@ static bool do_get(DisasContext *dc, int rd, int rb, int imm, int ctrl) tcg_gen_movi_i32(t_id, imm); } - t_ctrl = tcg_const_i32(ctrl); + t_ctrl = tcg_constant_i32(ctrl); gen_helper_get(reg_for_write(dc, rd), t_id, t_ctrl); - tcg_temp_free_i32(t_id); - tcg_temp_free_i32(t_ctrl); return true; } @@ -1636,10 +1578,8 @@ static bool do_put(DisasContext *dc, int ra, int rb, int imm, int ctrl) tcg_gen_movi_i32(t_id, imm); } - t_ctrl = tcg_const_i32(ctrl); + t_ctrl = tcg_constant_i32(ctrl); gen_helper_put(t_id, t_ctrl, reg_for_read(dc, ra)); - tcg_temp_free_i32(t_id); - tcg_temp_free_i32(t_ctrl); return true; } @@ -1664,7 +1604,7 @@ static void mb_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs) dc->ext_imm = dc->base.tb->cs_base; dc->r0 = NULL; dc->r0_set = false; - dc->mem_index = cpu_mmu_index(&cpu->env, false); + dc->mem_index = cpu_mmu_index(cs, false); dc->jmp_cond = dc->tb_flags & D_FLAG ? TCG_COND_ALWAYS : TCG_COND_NEVER; dc->jmp_dest = -1; @@ -1681,13 +1621,11 @@ static void mb_tr_insn_start(DisasContextBase *dcb, CPUState *cs) DisasContext *dc = container_of(dcb, DisasContext, base); tcg_gen_insn_start(dc->base.pc_next, dc->tb_flags & ~MSR_TB_MASK); - dc->insn_start = tcg_last_op(); } static void mb_tr_translate_insn(DisasContextBase *dcb, CPUState *cs) { DisasContext *dc = container_of(dcb, DisasContext, base); - CPUMBState *env = cs->env_ptr; uint32_t ir; /* TODO: This should raise an exception, not terminate qemu. */ @@ -1698,13 +1636,12 @@ static void mb_tr_translate_insn(DisasContextBase *dcb, CPUState *cs) dc->tb_flags_to_set = 0; - ir = cpu_ldl_code(env, dc->base.pc_next); + ir = cpu_ldl_code(cpu_env(cs), dc->base.pc_next); if (!decode(dc, ir)) { trap_illegal(dc, true); } if (dc->r0) { - tcg_temp_free_i32(dc->r0); dc->r0 = NULL; dc->r0_set = false; } @@ -1849,16 +1786,16 @@ static const TranslatorOps mb_tr_ops = { .disas_log = mb_tr_disas_log, }; -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc; - translator_loop(&mb_tr_ops, &dc.base, cpu, tb, max_insns); + translator_loop(cpu, tb, max_insns, pc, host_pc, &mb_tr_ops, &dc.base); } void mb_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - CPUMBState *env = &cpu->env; + CPUMBState *env = cpu_env(cs); uint32_t iflags; int i; @@ -1939,16 +1876,9 @@ void mb_tcg_init(void) for (int i = 0; i < ARRAY_SIZE(i32s); ++i) { *i32s[i].var = - tcg_global_mem_new_i32(cpu_env, i32s[i].ofs, i32s[i].name); + tcg_global_mem_new_i32(tcg_env, i32s[i].ofs, i32s[i].name); } cpu_res_addr = - tcg_global_mem_new(cpu_env, offsetof(CPUMBState, res_addr), "res_addr"); -} - -void restore_state_to_opc(CPUMBState *env, TranslationBlock *tb, - target_ulong *data) -{ - env->pc = data[0]; - env->iflags = data[1]; + tcg_global_mem_new(tcg_env, offsetof(CPUMBState, res_addr), "res_addr"); } diff --git a/target/mips/Kconfig b/target/mips/Kconfig index 6adf145354..eb19c94c7d 100644 --- a/target/mips/Kconfig +++ b/target/mips/Kconfig @@ -1,5 +1,6 @@ config MIPS bool + select SEMIHOSTING config MIPS64 bool diff --git a/target/mips/cpu-defs.c.inc b/target/mips/cpu-defs.c.inc index 582f940070..fbf787d8ce 100644 --- a/target/mips/cpu-defs.c.inc +++ b/target/mips/cpu-defs.c.inc @@ -117,6 +117,26 @@ const mips_def_t mips_defs[] = .insn_flags = CPU_MIPS32R1, .mmu_type = MMU_TYPE_R4000, }, + { + .name = "XBurstR1", + .CP0_PRid = 0x1ed0024f, + .CP0_Config0 = MIPS_CONFIG0 | (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) | + (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | + (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | + (0 << CP0C1_CA), + .CP0_Config2 = MIPS_CONFIG2, + .CP0_Config3 = MIPS_CONFIG3, + .CP0_LLAddr_rw_bitmask = 0, + .CP0_LLAddr_shift = 4, + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x1278FF17, + .SEGBITS = 32, + .PABITS = 32, + .insn_flags = CPU_MIPS32R1 | ASE_MXU, + .mmu_type = MMU_TYPE_R4000, + }, { .name = "4KEmR1", .CP0_PRid = 0x00018500, @@ -323,6 +343,32 @@ const mips_def_t mips_defs[] = .insn_flags = CPU_MIPS32R2 | ASE_MIPS16 | ASE_DSP | ASE_DSP_R2, .mmu_type = MMU_TYPE_R4000, }, + { + .name = "XBurstR2", + .CP0_PRid = 0x2ed1024f, + .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (15 << CP0C1_MMU) | + (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | + (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | + (1 << CP0C1_CA), + .CP0_Config2 = MIPS_CONFIG2, + .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_DSP2P) | (1 << CP0C3_DSPP) | + (1 << CP0C3_VInt), + .CP0_LLAddr_rw_bitmask = 0, + .CP0_LLAddr_shift = 4, + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x3778FF1F, + .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | + (1 << FCR0_D) | (1 << FCR0_S) | (0x93 << FCR0_PRID), + .CP1_fcr31 = 0, + .CP1_fcr31_rw_bitmask = 0xFF83FFFF, + .SEGBITS = 32, + .PABITS = 32, + .insn_flags = CPU_MIPS32R2 | ASE_MXU, + .mmu_type = MMU_TYPE_R4000, + }, { .name = "M14K", .CP0_PRid = 0x00019b00, @@ -332,7 +378,11 @@ const mips_def_t mips_defs[] = (0x1 << CP0C0_AR) | (MMU_TYPE_FMT << CP0C0_MT), .CP0_Config1 = MIPS_CONFIG1, .CP0_Config2 = MIPS_CONFIG2, - .CP0_Config3 = MIPS_CONFIG3 | (0x2 << CP0C3_ISA) | (1 << CP0C3_VInt), + .CP0_Config3 = MIPS_CONFIG3 | (0x2 << CP0C3_ISA) | (1 << CP0C3_VInt) | + (1 << CP0C3_M), + .CP0_Config4 = MIPS_CONFIG4 | (1 << CP0C4_M), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_NFExists), + .CP0_Config7 = 1 << CP0C7_WII, .CP0_LLAddr_rw_bitmask = 0, .CP0_LLAddr_shift = 4, .SYNCI_Step = 32, @@ -353,7 +403,11 @@ const mips_def_t mips_defs[] = (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA), .CP0_Config2 = MIPS_CONFIG2, - .CP0_Config3 = MIPS_CONFIG3 | (0x2 << CP0C3_ISA) | (0 << CP0C3_VInt), + .CP0_Config3 = MIPS_CONFIG3 | (0x2 << CP0C3_ISA) | (0 << CP0C3_VInt) | + (1 << CP0C3_M), + .CP0_Config4 = MIPS_CONFIG4 | (1 << CP0C4_M), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_NFExists), + .CP0_Config7 = 1 << CP0C7_WII, .CP0_LLAddr_rw_bitmask = 0, .CP0_LLAddr_shift = 4, .SYNCI_Step = 32, @@ -392,6 +446,7 @@ const mips_def_t mips_defs[] = .CP0_Config5_rw_bitmask = (1 << CP0C5_K) | (1 << CP0C5_CV) | (1 << CP0C5_MSAEn) | (1 << CP0C5_UFE) | (1 << CP0C5_FRE) | (1 << CP0C5_UFR), + .CP0_Config7 = 1 << CP0C7_WII, .CP0_LLAddr_rw_bitmask = 0, .CP0_LLAddr_shift = 0, .SYNCI_Step = 32, @@ -700,7 +755,7 @@ const mips_def_t mips_defs[] = .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) | (1 << CP0C4_AE) | (0xfc << CP0C4_KScrExist), .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_XNP) | (1 << CP0C5_VP) | - (1 << CP0C5_LLB) | (1 << CP0C5_MRP), + (1 << CP0C5_LLB) | (1 << CP0C5_MRP) | (3 << CP0C5_GI), .CP0_Config5_rw_bitmask = (1 << CP0C5_MSAEn) | (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | (1 << CP0C5_UFE), .CP0_LLAddr_rw_bitmask = 0, @@ -740,7 +795,7 @@ const mips_def_t mips_defs[] = .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) | (1 << CP0C4_AE) | (0xfc << CP0C4_KScrExist), .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_XNP) | (1 << CP0C5_VP) | - (1 << CP0C5_LLB) | (1 << CP0C5_MRP), + (1 << CP0C5_LLB) | (1 << CP0C5_MRP) | (3 << CP0C5_GI), .CP0_Config5_rw_bitmask = (1 << CP0C5_MSAEn) | (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | (1 << CP0C5_UFE), .CP0_LLAddr_rw_bitmask = 0, @@ -886,6 +941,15 @@ const mips_def_t mips_defs[] = .CP1_fcr31 = 0, .CP1_fcr31_rw_bitmask = 0xFF83FFFF, .MSAIR = (0x01 << MSAIR_ProcID) | (0x40 << MSAIR_Rev), + .lcsr_cpucfg1 = (1 << CPUCFG1_FP) | (2 << CPUCFG1_FPREV) | + (1 << CPUCFG1_MSA1) | (1 << CPUCFG1_LSLDR0) | + (1 << CPUCFG1_LSPERF) | (1 << CPUCFG1_LSPERFX) | + (1 << CPUCFG1_LSSYNCI) | (1 << CPUCFG1_LLEXC) | + (1 << CPUCFG1_SCRAND) | (1 << CPUCFG1_MUALP) | + (1 << CPUCFG1_KMUALEN) | (1 << CPUCFG1_ITLBT) | + (1 << CPUCFG1_SFBP) | (1 << CPUCFG1_CDMAP), + .lcsr_cpucfg2 = (1 << CPUCFG2_LEXT1) | (1 << CPUCFG2_LCSRP) | + (1 << CPUCFG2_LDISBLIKELY), .SEGBITS = 48, .PABITS = 48, .insn_flags = CPU_MIPS64R2 | INSN_LOONGSON3A | @@ -921,20 +985,39 @@ const mips_def_t mips_defs[] = .insn_flags = CPU_MIPS64R2 | ASE_DSP | ASE_DSP_R2, .mmu_type = MMU_TYPE_R4000, }, + { + /* + * Octeon 68xx with MIPS64 Cavium Octeon features. + */ + .name = "Octeon68XX", + .CP0_PRid = 0x000D9100, + .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1 | (0x3F << CP0C1_MMU) | + (1 << CP0C1_IS) | (4 << CP0C1_IL) | (1 << CP0C1_IA) | + (1 << CP0C1_DS) | (4 << CP0C1_DL) | (1 << CP0C1_DA) | + (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), + .CP0_Config2 = MIPS_CONFIG2, + .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_LPA), + .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | + (0x3c << CP0C4_KScrExist) | (1U << CP0C4_MMUExtDef) | + (3U << CP0C4_MMUSizeExt), + .CP0_LLAddr_rw_bitmask = 0, + .CP0_LLAddr_shift = 4, + .CP0_PageGrain = (1 << CP0PG_ELPA), + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x12F8FFFF, + .SEGBITS = 42, + .PABITS = 49, + .insn_flags = CPU_MIPS64R2 | INSN_OCTEON, + .mmu_type = MMU_TYPE_R4000, + }, #endif }; const int mips_defs_number = ARRAY_SIZE(mips_defs); -void mips_cpu_list(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(mips_defs); i++) { - qemu_printf("MIPS '%s'\n", mips_defs[i].name); - } -} - static void fpu_init (CPUMIPSState *env, const mips_def_t *def) { int i; @@ -953,7 +1036,7 @@ static void mvp_init(CPUMIPSState *env) return; } - /* MVPConf1 implemented, TLB sharable, no gating storage support, + /* MVPConf1 implemented, TLB shareable, no gating storage support, programmable cache partitioning implemented, number of allocatable and shareable TLB entries, MVP has allocatable TCs, 2 VPEs implemented, 5 TCs implemented. */ diff --git a/target/mips/cpu-param.h b/target/mips/cpu-param.h index f4c76994ea..594c91a156 100644 --- a/target/mips/cpu-param.h +++ b/target/mips/cpu-param.h @@ -29,6 +29,5 @@ #define TARGET_PAGE_BITS_VARY #define TARGET_PAGE_BITS_MIN 12 #endif -#define NB_MMU_MODES 4 #endif diff --git a/target/mips/cpu-qom.h b/target/mips/cpu-qom.h index e28b529607..0eea2a2598 100644 --- a/target/mips/cpu-qom.h +++ b/target/mips/cpu-qom.h @@ -21,7 +21,6 @@ #define QEMU_MIPS_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #ifdef TARGET_MIPS64 #define TYPE_MIPS_CPU "mips64-cpu" @@ -31,25 +30,7 @@ OBJECT_DECLARE_CPU_TYPE(MIPSCPU, MIPSCPUClass, MIPS_CPU) -/** - * MIPSCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * - * A MIPS CPU model. - */ -struct MIPSCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceReset parent_reset; - const struct mips_def_t *cpu_def; - - /* Used for the jazz board to modify mips_cpu_do_transaction_failed. */ - bool no_data_aborts; -}; - +#define MIPS_CPU_TYPE_SUFFIX "-" TYPE_MIPS_CPU +#define MIPS_CPU_TYPE_NAME(model) model MIPS_CPU_TYPE_SUFFIX #endif diff --git a/target/mips/cpu.c b/target/mips/cpu.c index ad74fbe636..8d8f690a53 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/qemu-print.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "cpu.h" #include "internal.h" @@ -32,7 +33,6 @@ #include "hw/qdev-properties.h" #include "hw/qdev-clock.h" #include "semihosting/semihost.h" -#include "qapi/qapi-commands-machine-target.h" #include "fpu_helper.h" const char regnames[32][3] = { @@ -80,8 +80,7 @@ static void fpu_dump_state(CPUMIPSState *env, FILE *f, int flags) static void mips_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); int i; qemu_fprintf(f, "pc=0x" TARGET_FMT_lx " HI=0x" TARGET_FMT_lx @@ -122,26 +121,32 @@ void cpu_set_exception_base(int vp_index, target_ulong address) } static void mips_cpu_set_pc(CPUState *cs, vaddr value) +{ + mips_env_set_pc(cpu_env(cs), value); +} + +static vaddr mips_cpu_get_pc(CPUState *cs) { MIPSCPU *cpu = MIPS_CPU(cs); - mips_env_set_pc(&cpu->env, value); + return cpu->env.active_tc.PC; } static bool mips_cpu_has_work(CPUState *cs) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); bool has_work = false; /* * Prior to MIPS Release 6 it is implementation dependent if non-enabled * interrupts wake-up the CPU, however most of the implementations only - * check for interrupts that can be taken. + * check for interrupts that can be taken. For pre-release 6 CPUs, + * check for CP0 Config7 'Wait IE ignore' bit. */ if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && cpu_mips_hw_interrupts_pending(env)) { if (cpu_mips_hw_interrupts_enabled(env) || + (env->CP0_Config7 & (1 << CP0C7_WII)) || (env->insn_flags & ISA_MIPS_R6)) { has_work = true; } @@ -173,16 +178,23 @@ static bool mips_cpu_has_work(CPUState *cs) return has_work; } +static int mips_cpu_mmu_index(CPUState *cs, bool ifunc) +{ + return mips_env_mmu_index(cpu_env(cs)); +} + #include "cpu-defs.c.inc" -static void mips_cpu_reset(DeviceState *dev) +static void mips_cpu_reset_hold(Object *obj) { - CPUState *cs = CPU(dev); + CPUState *cs = CPU(obj); MIPSCPU *cpu = MIPS_CPU(cs); - MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(cpu); + MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(obj); CPUMIPSState *env = &cpu->env; - mcc->parent_reset(dev); + if (mcc->parent_phases.hold) { + mcc->parent_phases.hold(obj); + } memset(env, 0, offsetof(CPUMIPSState, end_reset_fields)); @@ -233,6 +245,8 @@ static void mips_cpu_reset(DeviceState *dev) env->CP0_PageGrain_rw_bitmask = env->cpu_model->CP0_PageGrain_rw_bitmask; env->CP0_PageGrain = env->cpu_model->CP0_PageGrain; env->CP0_EBaseWG_rw_bitmask = env->cpu_model->CP0_EBaseWG_rw_bitmask; + env->lcsr_cpucfg1 = env->cpu_model->lcsr_cpucfg1; + env->lcsr_cpucfg2 = env->cpu_model->lcsr_cpucfg2; env->active_fpu.fcr0 = env->cpu_model->CP1_fcr0; env->active_fpu.fcr31_rw_bitmask = env->cpu_model->CP1_fcr31_rw_bitmask; env->active_fpu.fcr31 = env->cpu_model->CP1_fcr31; @@ -283,18 +297,19 @@ static void mips_cpu_reset(DeviceState *dev) env->tlb->tlb_in_use = env->tlb->nb_tlb; env->CP0_Wired = 0; env->CP0_GlobalNumber = (cs->cpu_index & 0xFF) << CP0GN_VPId; - env->CP0_EBase = (cs->cpu_index & 0x3FF); - if (mips_um_ksegs_enabled()) { - env->CP0_EBase |= 0x40000000; - } else { - env->CP0_EBase |= (int32_t)0x80000000; - } + env->CP0_EBase = KSEG0_BASE | (cs->cpu_index & 0x3FF); if (env->CP0_Config3 & (1 << CP0C3_CMGCR)) { env->CP0_CMGCRBase = 0x1fbf8000 >> 4; } 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); + if (env->insn_flags & INSN_LOONGSON2F) { + /* Loongson-2F has those bits hardcoded to 1 */ + env->CP0_Status |= (1 << CP0St_KX) | (1 << CP0St_SX) | + (1 << CP0St_UX); + } + /* * Vectored interrupts not implemented, timer on int 7, * no performance counters. @@ -305,7 +320,7 @@ static void mips_cpu_reset(DeviceState *dev) for (i = 0; i < 7; i++) { env->CP0_WatchLo[i] = 0; - env->CP0_WatchHi[i] = 0x80000000; + env->CP0_WatchHi[i] = 1 << CP0WH_M; } env->CP0_WatchLo[7] = 0; env->CP0_WatchHi[7] = 0; @@ -414,19 +429,14 @@ static void mips_cpu_reset(DeviceState *dev) static void mips_cpu_disas_set_info(CPUState *s, disassemble_info *info) { - MIPSCPU *cpu = MIPS_CPU(s); - CPUMIPSState *env = &cpu->env; - - if (!(env->insn_flags & ISA_NANOMIPS32)) { + if (!(cpu_env(s)->insn_flags & ISA_NANOMIPS32)) { #if TARGET_BIG_ENDIAN info->print_insn = print_insn_big_mips; #else info->print_insn = print_insn_little_mips; #endif } else { -#if defined(CONFIG_NANOMIPS_DIS) info->print_insn = print_insn_nanomips; -#endif } } @@ -439,9 +449,9 @@ static void mips_cp0_period_set(MIPSCPU *cpu) { CPUMIPSState *env = &cpu->env; - env->cp0_count_ns = clock_ticks_to_ns(MIPS_CPU(cpu)->clock, - env->cpu_model->CCRes); - assert(env->cp0_count_ns); + clock_set_mul_div(cpu->count_div, env->cpu_model->CCRes, 1); + clock_set_source(cpu->count_div, cpu->clock); + clock_set_source(env->count_clock, cpu->count_div); } static void mips_cpu_realizefn(DeviceState *dev, Error **errp) @@ -492,9 +502,18 @@ static void mips_cpu_initfn(Object *obj) CPUMIPSState *env = &cpu->env; MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(obj); - cpu_set_cpustate_pointers(cpu); cpu->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, cpu, 0); + cpu->count_div = clock_new(OBJECT(obj), "clk-div-count"); + env->count_clock = clock_new(OBJECT(obj), "clk-count"); env->cpu_model = mcc->cpu_def; +#ifndef CONFIG_USER_ONLY + if (mcc->cpu_def->lcsr_cpucfg2 & (1 << CPUCFG2_LCSRP)) { + memory_region_init_io(&env->iocsr.mr, OBJECT(cpu), NULL, + env, "iocsr", UINT64_MAX); + address_space_init(&env->iocsr.as, + &env->iocsr.mr, "IOCSR"); + } +#endif } static char *mips_cpu_type_name(const char *cpu_model) @@ -528,9 +547,10 @@ static const struct SysemuCPUOps mips_sysemu_ops = { * NB: cannot be const, as some elements are changed for specific * mips hardware (see hw/mips/jazz.c). */ -static const struct TCGCPUOps mips_tcg_ops = { +static const TCGCPUOps mips_tcg_ops = { .initialize = mips_tcg_init, .synchronize_from_tb = mips_cpu_synchronize_from_tb, + .restore_state_to_opc = mips_restore_state_to_opc, #if !defined(CONFIG_USER_ONLY) .tlb_fill = mips_cpu_tlb_fill, @@ -548,15 +568,19 @@ static void mips_cpu_class_init(ObjectClass *c, void *data) MIPSCPUClass *mcc = MIPS_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); DeviceClass *dc = DEVICE_CLASS(c); + ResettableClass *rc = RESETTABLE_CLASS(c); device_class_set_parent_realize(dc, mips_cpu_realizefn, &mcc->parent_realize); - device_class_set_parent_reset(dc, mips_cpu_reset, &mcc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, mips_cpu_reset_hold, NULL, + &mcc->parent_phases); cc->class_by_name = mips_cpu_class_by_name; cc->has_work = mips_cpu_has_work; + cc->mmu_index = mips_cpu_mmu_index; cc->dump_state = mips_cpu_dump_state; cc->set_pc = mips_cpu_set_pc; + cc->get_pc = mips_cpu_get_pc; cc->gdb_read_register = mips_cpu_gdb_read_register; cc->gdb_write_register = mips_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY @@ -574,6 +598,7 @@ static const TypeInfo mips_cpu_type_info = { .name = TYPE_MIPS_CPU, .parent = TYPE_CPU, .instance_size = sizeof(MIPSCPU), + .instance_align = __alignof(MIPSCPU), .instance_init = mips_cpu_initfn, .abstract = true, .class_size = sizeof(MIPSCPUClass), @@ -612,34 +637,6 @@ static void mips_cpu_register_types(void) type_init(mips_cpu_register_types) -static void mips_cpu_add_definition(gpointer data, gpointer user_data) -{ - ObjectClass *oc = data; - CpuDefinitionInfoList **cpu_list = user_data; - CpuDefinitionInfo *info; - const char *typename; - - typename = object_class_get_name(oc); - info = g_malloc0(sizeof(*info)); - info->name = g_strndup(typename, - strlen(typename) - strlen("-" TYPE_MIPS_CPU)); - info->q_typename = g_strdup(typename); - - QAPI_LIST_PREPEND(*cpu_list, info); -} - -CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) -{ - CpuDefinitionInfoList *cpu_list = NULL; - GSList *list; - - list = object_class_get_list(TYPE_MIPS_CPU, false); - g_slist_foreach(list, mips_cpu_add_definition, &cpu_list); - g_slist_free(list); - - return cpu_list; -} - /* Could be used by generic CPU object */ MIPSCPU *mips_cpu_create_with_clock(const char *cpu_type, Clock *cpu_refclk) { diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 5335ac10a3..7329226d39 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -3,6 +3,9 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#ifndef CONFIG_USER_ONLY +#include "exec/memory.h" +#endif #include "fpu/softfloat-types.h" #include "hw/clock.h" #include "mips-defs.h" @@ -744,9 +747,7 @@ typedef struct CPUArchState { * CP0 Register 9 */ int32_t CP0_Count; - uint32_t CP0_SAARI; #define CP0SAARI_TARGET 0 /* 5..0 */ - uint64_t CP0_SAAR[2]; #define CP0SAAR_BASE 12 /* 43..12 */ #define CP0SAAR_SIZE 1 /* 5..1 */ #define CP0SAAR_EN 0 @@ -980,6 +981,7 @@ typedef struct CPUArchState { #define CP0C6_DATAPREF 0 int32_t CP0_Config7; int64_t CP0_Config7_rw_bitmask; +#define CP0C7_WII 31 #define CP0C7_NAPCGEN 2 #define CP0C7_UNIMUEN 1 #define CP0C7_VFPUCGEN 0 @@ -1005,6 +1007,7 @@ typedef struct CPUArchState { */ uint64_t CP0_WatchHi[8]; #define CP0WH_ASID 16 +#define CP0WH_M 31 /* * CP0 Register 20 */ @@ -1066,6 +1069,33 @@ typedef struct CPUArchState { */ int32_t CP0_DESAVE; target_ulong CP0_KScratch[MIPS_KSCRATCH_NUM]; +/* + * Loongson CSR CPUCFG registers + */ + uint32_t lcsr_cpucfg1; +#define CPUCFG1_FP 0 +#define CPUCFG1_FPREV 1 +#define CPUCFG1_MMI 4 +#define CPUCFG1_MSA1 5 +#define CPUCFG1_MSA2 6 +#define CPUCFG1_LSLDR0 16 +#define CPUCFG1_LSPERF 17 +#define CPUCFG1_LSPERFX 18 +#define CPUCFG1_LSSYNCI 19 +#define CPUCFG1_LLEXC 20 +#define CPUCFG1_SCRAND 21 +#define CPUCFG1_MUALP 25 +#define CPUCFG1_KMUALEN 26 +#define CPUCFG1_ITLBT 27 +#define CPUCFG1_SFBP 29 +#define CPUCFG1_CDMAP 30 + uint32_t lcsr_cpucfg2; +#define CPUCFG2_LEXT1 0 +#define CPUCFG2_LEXT2 1 +#define CPUCFG2_LEXT3 2 +#define CPUCFG2_LSPW 3 +#define CPUCFG2_LCSRP 27 +#define CPUCFG2_LDISBLIKELY 28 /* We waste some space so we can handle shadow registers like TCs. */ TCState tcs[MIPS_SHADOW_SET_MAX]; @@ -1076,7 +1106,7 @@ typedef struct CPUArchState { #define EXCP_INST_NOTAVAIL 0x2 /* No valid instruction word for BadInstr */ uint32_t hflags; /* CPU State */ /* TMASK defines different execution modes */ -#define MIPS_HFLAG_TMASK 0x1F5807FF +#define MIPS_HFLAG_TMASK 0x3F5807FF #define MIPS_HFLAG_MODE 0x00007 /* execution modes */ /* * The KSU flags must be the lowest bits in hflags. The flag order @@ -1142,7 +1172,6 @@ typedef struct CPUArchState { uint32_t CP0_Status_rw_bitmask; /* Read/write bits in CP0_Status */ uint32_t CP0_TCStatus_rw_bitmask; /* Read/write bits in CP0_TCStatus */ uint64_t insn_flags; /* Supported instruction set */ - int saarp; /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; @@ -1151,15 +1180,20 @@ typedef struct CPUArchState { CPUMIPSMVPContext *mvp; #if !defined(CONFIG_USER_ONLY) CPUMIPSTLBContext *tlb; - void *irq[8]; - struct MIPSITUState *itu; + qemu_irq irq[8]; MemoryRegion *itc_tag; /* ITC Configuration Tags */ + + /* Loongson IOCSR memory */ + struct { + AddressSpace as; + MemoryRegion mr; + } iocsr; #endif const mips_def_t *cpu_model; QEMUTimer *timer; /* Internal timer */ + Clock *count_clock; /* CP0_Count clock */ target_ulong exception_base; /* ExceptionBase input to the core */ - uint64_t cp0_count_ns; /* CP0_Count clock period (in nanoseconds) */ } CPUMIPSState; /** @@ -1171,39 +1205,53 @@ typedef struct CPUArchState { * A MIPS CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ + + CPUMIPSState env; Clock *clock; - CPUNegativeOffsetState neg; - CPUMIPSState env; + Clock *count_div; /* Divider for CP0_Count clock */ }; +/** + * MIPSCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A MIPS CPU model. + */ +struct MIPSCPUClass { + CPUClass parent_class; -void mips_cpu_list(void); + DeviceRealize parent_realize; + ResettablePhases parent_phases; + const struct mips_def_t *cpu_def; -#define cpu_list mips_cpu_list + /* Used for the jazz board to modify mips_cpu_do_transaction_failed. */ + bool no_data_aborts; +}; -extern void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env); -extern uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env); +void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env); +uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env); /* * MMU modes definitions. We carefully match the indices with our * hflags layout. */ +#define MMU_KERNEL_IDX 0 #define MMU_USER_IDX 2 +#define MMU_ERL_IDX 3 static inline int hflags_mmu_index(uint32_t hflags) { if (hflags & MIPS_HFLAG_ERL) { - return 3; /* ERL */ + return MMU_ERL_IDX; } else { return hflags & MIPS_HFLAG_KSU; } } -static inline int cpu_mmu_index(CPUMIPSState *env, bool ifetch) +static inline int mips_env_mmu_index(CPUMIPSState *env) { return hflags_mmu_index(env->hflags); } @@ -1251,8 +1299,9 @@ enum { EXCP_MSAFPE, EXCP_TLBXI, EXCP_TLBRI, + EXCP_SEMIHOST, - EXCP_LAST = EXCP_TLBRI, + EXCP_LAST = EXCP_SEMIHOST, }; /* @@ -1263,8 +1312,6 @@ enum { */ #define CPU_INTERRUPT_WAKE CPU_INTERRUPT_TGT_INT_0 -#define MIPS_CPU_TYPE_SUFFIX "-" TYPE_MIPS_CPU -#define MIPS_CPU_TYPE_NAME(model) model MIPS_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_MIPS_CPU bool cpu_type_supports_cps_smp(const char *cpu_type); @@ -1277,6 +1324,12 @@ static inline bool ase_msa_available(CPUMIPSState *env) return env->CP0_Config3 & (1 << CP0C3_MSAP); } +/* Check presence of Loongson CSR instructions */ +static inline bool ase_lcsr_available(CPUMIPSState *env) +{ + return env->lcsr_cpucfg2 & (1 << CPUCFG2_LCSRP); +} + /* Check presence of multi-threading ASE implementation */ static inline bool ase_mt_available(CPUMIPSState *env) { @@ -1294,27 +1347,23 @@ void cpu_set_exception_base(int vp_index, target_ulong address); uint64_t cpu_mips_kseg0_to_phys(void *opaque, uint64_t addr); uint64_t cpu_mips_phys_to_kseg0(void *opaque, uint64_t addr); -uint64_t cpu_mips_kvm_um_phys_to_kseg0(void *opaque, uint64_t addr); uint64_t cpu_mips_kseg1_to_phys(void *opaque, uint64_t addr); uint64_t cpu_mips_phys_to_kseg1(void *opaque, uint64_t addr); -bool mips_um_ksegs_enabled(void); -void mips_um_ksegs_enable(void); #if !defined(CONFIG_USER_ONLY) -/* mips_int.c */ +/* HW declaration specific to the MIPS target */ void cpu_mips_soft_irq(CPUMIPSState *env, int irq, int level); - -/* mips_itu.c */ -void itc_reconfigure(struct MIPSITUState *tag); +void cpu_mips_irq_init_cpu(MIPSCPU *cpu); +void cpu_mips_clock_init(MIPSCPU *cpu); #endif /* !CONFIG_USER_ONLY */ /* helper.c */ target_ulong exception_resume_pc(CPUMIPSState *env); -static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) +static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { *pc = env->active_tc.PC; *cs_base = 0; diff --git a/target/mips/gdbstub.c b/target/mips/gdbstub.c index f1c2a2cf6d..169d47416a 100644 --- a/target/mips/gdbstub.c +++ b/target/mips/gdbstub.c @@ -20,13 +20,12 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "fpu_helper.h" int mips_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); if (n < 32) { return gdb_get_regl(mem_buf, env->active_tc.gpr[n]); @@ -78,8 +77,7 @@ int mips_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int mips_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); target_ulong tmp; tmp = ldtul_p(mem_buf); diff --git a/target/mips/helper.h b/target/mips/helper.h index de32d82e98..0f8462febb 100644 --- a/target/mips/helper.h +++ b/target/mips/helper.h @@ -196,6 +196,10 @@ DEF_HELPER_1(rdhwr_xnp, tl, env) DEF_HELPER_2(pmon, void, env, int) DEF_HELPER_1(wait, void, env) +#ifdef TARGET_MIPS64 +DEF_HELPER_FLAGS_2(lcsr_cpucfg, TCG_CALL_NO_RWG_SE, tl, env, tl) +#endif + /* Loongson multimedia functions. */ DEF_HELPER_FLAGS_2(paddsh, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(paddush, TCG_CALL_NO_RWG_SE, i64, i64, i64) diff --git a/target/mips/internal.h b/target/mips/internal.h index 57b312689a..a9a22ea00e 100644 --- a/target/mips/internal.h +++ b/target/mips/internal.h @@ -79,9 +79,10 @@ struct mips_def_t { int32_t CP0_PageGrain_rw_bitmask; int32_t CP0_PageGrain; target_ulong CP0_EBaseWG_rw_bitmask; + uint32_t lcsr_cpucfg1; + uint32_t lcsr_cpucfg2; uint64_t insn_flags; enum mips_mmu_types mmu_type; - int32_t SAARP; }; extern const char regnames[32][3]; @@ -99,9 +100,6 @@ int mips_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); #define KSEG2_BASE ((target_ulong)(int32_t)0xC0000000UL) #define KSEG3_BASE ((target_ulong)(int32_t)0xE0000000UL) -#define KVM_KSEG0_BASE ((target_ulong)(int32_t)0x40000000UL) -#define KVM_KSEG2_BASE ((target_ulong)(int32_t)0x60000000UL) - #if !defined(CONFIG_USER_ONLY) enum { diff --git a/target/mips/kvm.c b/target/mips/kvm.c index caf70decd2..6c52e59f55 100644 --- a/target/mips/kvm.c +++ b/target/mips/kvm.c @@ -63,8 +63,7 @@ int kvm_arch_irqchip_create(KVMState *s) int kvm_arch_init_vcpu(CPUState *cs) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); int ret = 0; qemu_add_vm_change_state_handler(kvm_mips_update_state, cs); @@ -138,7 +137,7 @@ void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) int r; struct kvm_mips_interrupt intr; - qemu_mutex_lock_iothread(); + bql_lock(); if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && cpu_mips_io_interrupts_pending(cpu)) { @@ -151,7 +150,7 @@ void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) } } - qemu_mutex_unlock_iothread(); + bql_unlock(); } MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) @@ -460,8 +459,7 @@ static inline int kvm_mips_change_one_reg(CPUState *cs, uint64_t reg_id, */ static int kvm_mips_save_count(CPUState *cs) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); uint64_t count_ctl; int err, ret = 0; @@ -502,8 +500,7 @@ static int kvm_mips_save_count(CPUState *cs) */ static int kvm_mips_restore_count(CPUState *cs) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); uint64_t count_ctl; int err_dc, err, ret = 0; @@ -590,8 +587,7 @@ static void kvm_mips_update_state(void *opaque, bool running, RunState state) static int kvm_mips_put_fpu_registers(CPUState *cs, int level) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); int err, ret = 0; unsigned int i; @@ -670,8 +666,7 @@ static int kvm_mips_put_fpu_registers(CPUState *cs, int level) static int kvm_mips_get_fpu_registers(CPUState *cs) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); int err, ret = 0; unsigned int i; @@ -751,8 +746,7 @@ static int kvm_mips_get_fpu_registers(CPUState *cs) static int kvm_mips_put_cp0_registers(CPUState *cs, int level) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); int err, ret = 0; (void)level; @@ -974,8 +968,7 @@ static int kvm_mips_put_cp0_registers(CPUState *cs, int level) static int kvm_mips_get_cp0_registers(CPUState *cs) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); int err, ret = 0; err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_INDEX, &env->CP0_Index); @@ -1181,8 +1174,7 @@ static int kvm_mips_get_cp0_registers(CPUState *cs) int kvm_arch_put_registers(CPUState *cs, int level) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); struct kvm_regs regs; int ret; int i; @@ -1217,8 +1209,7 @@ int kvm_arch_put_registers(CPUState *cs, int level) int kvm_arch_get_registers(CPUState *cs) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); int ret = 0; struct kvm_regs regs; int i; @@ -1266,27 +1257,19 @@ int kvm_arch_msi_data_to_gsi(uint32_t data) abort(); } -int mips_kvm_type(MachineState *machine, const char *vm_type) +int kvm_arch_get_default_type(MachineState *machine) { -#if defined(KVM_CAP_MIPS_VZ) || defined(KVM_CAP_MIPS_TE) +#if defined(KVM_CAP_MIPS_VZ) int r; KVMState *s = KVM_STATE(machine->accelerator); -#endif -#if defined(KVM_CAP_MIPS_VZ) r = kvm_check_extension(s, KVM_CAP_MIPS_VZ); if (r > 0) { return KVM_VM_MIPS_VZ; } #endif -#if defined(KVM_CAP_MIPS_TE) - r = kvm_check_extension(s, KVM_CAP_MIPS_TE); - if (r > 0) { - return KVM_VM_MIPS_TE; - } -#endif - + error_report("KVM_VM_MIPS_VZ type is not available"); return -1; } @@ -1294,3 +1277,7 @@ bool kvm_arch_cpu_check_are_resettable(void) { return true; } + +void kvm_arch_accel_class_init(ObjectClass *oc) +{ +} diff --git a/target/mips/kvm_mips.h b/target/mips/kvm_mips.h index 171d53dbe1..c711269d0a 100644 --- a/target/mips/kvm_mips.h +++ b/target/mips/kvm_mips.h @@ -25,13 +25,4 @@ void kvm_mips_reset_vcpu(MIPSCPU *cpu); int kvm_mips_set_interrupt(MIPSCPU *cpu, int irq, int level); int kvm_mips_set_ipi_interrupt(MIPSCPU *cpu, int irq, int level); -#ifdef CONFIG_KVM -int mips_kvm_type(MachineState *machine, const char *vm_type); -#else -static inline int mips_kvm_type(MachineState *machine, const char *vm_type) -{ - return 0; -} -#endif - #endif /* KVM_MIPS_H */ diff --git a/target/mips/meson.build b/target/mips/meson.build index 2407a05d4c..a26d1e1f79 100644 --- a/target/mips/meson.build +++ b/target/mips/meson.build @@ -1,5 +1,5 @@ mips_user_ss = ss.source_set() -mips_softmmu_ss = ss.source_set() +mips_system_ss = ss.source_set() mips_ss = ss.source_set() mips_ss.add(files( 'cpu.c', @@ -12,12 +12,12 @@ if have_system subdir('sysemu') endif -if 'CONFIG_TCG' in config_all +if 'CONFIG_TCG' in config_all_accel subdir('tcg') endif mips_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c')) target_arch += {'mips': mips_ss} -target_softmmu_arch += {'mips': mips_softmmu_ss} +target_system_arch += {'mips': mips_system_ss} target_user_arch += {'mips': mips_user_ss} diff --git a/target/mips/mips-defs.h b/target/mips/mips-defs.h index 0a12d982a7..a6cebe0265 100644 --- a/target/mips/mips-defs.h +++ b/target/mips/mips-defs.h @@ -42,6 +42,7 @@ #define INSN_LOONGSON2E 0x0000040000000000ULL #define INSN_LOONGSON2F 0x0000080000000000ULL #define INSN_LOONGSON3A 0x0000100000000000ULL +#define INSN_OCTEON 0x0000200000000000ULL /* * bits 52-63: vendor-specific ASEs */ diff --git a/target/mips/sysemu/addr.c b/target/mips/sysemu/addr.c index 86f1c129c9..4f025be44a 100644 --- a/target/mips/sysemu/addr.c +++ b/target/mips/sysemu/addr.c @@ -23,8 +23,6 @@ #include "qemu/osdep.h" #include "cpu.h" -static int mips_um_ksegs; - uint64_t cpu_mips_kseg0_to_phys(void *opaque, uint64_t addr) { return addr & 0x1fffffffll; @@ -35,11 +33,6 @@ uint64_t cpu_mips_phys_to_kseg0(void *opaque, uint64_t addr) return addr | ~0x7fffffffll; } -uint64_t cpu_mips_kvm_um_phys_to_kseg0(void *opaque, uint64_t addr) -{ - return addr | 0x40000000ll; -} - uint64_t cpu_mips_kseg1_to_phys(void *opaque, uint64_t addr) { return addr & 0x1fffffffll; @@ -49,13 +42,3 @@ uint64_t cpu_mips_phys_to_kseg1(void *opaque, uint64_t addr) { return (addr & 0x1fffffffll) | 0xffffffffa0000000ll; } - -bool mips_um_ksegs_enabled(void) -{ - return mips_um_ksegs; -} - -void mips_um_ksegs_enable(void) -{ - mips_um_ksegs = 1; -} diff --git a/target/mips/sysemu/cp0_timer.c b/target/mips/sysemu/cp0_timer.c index 70de95d338..62de502caa 100644 --- a/target/mips/sysemu/cp0_timer.c +++ b/target/mips/sysemu/cp0_timer.c @@ -22,21 +22,31 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/mips/cpudevs.h" #include "qemu/timer.h" #include "sysemu/kvm.h" #include "internal.h" /* MIPS R4K timer */ +static uint32_t cpu_mips_get_count_val(CPUMIPSState *env) +{ + int64_t now_ns; + now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + return env->CP0_Count + + (uint32_t)clock_ns_to_ticks(env->count_clock, now_ns); +} + static void cpu_mips_timer_update(CPUMIPSState *env) { uint64_t now_ns, next_ns; uint32_t wait; now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - wait = env->CP0_Compare - env->CP0_Count - - (uint32_t)(now_ns / env->cp0_count_ns); - next_ns = now_ns + (uint64_t)wait * env->cp0_count_ns; + wait = env->CP0_Compare - cpu_mips_get_count_val(env); + /* Clamp interval to overflow if virtual time had not progressed */ + if (!wait) { + wait = UINT32_MAX; + } + next_ns = now_ns + clock_ticks_to_ns(env->count_clock, wait); timer_mod(env->timer, next_ns); } @@ -64,7 +74,7 @@ uint32_t cpu_mips_get_count(CPUMIPSState *env) cpu_mips_timer_expire(env); } - return env->CP0_Count + (uint32_t)(now_ns / env->cp0_count_ns); + return cpu_mips_get_count_val(env); } } @@ -79,9 +89,8 @@ void cpu_mips_store_count(CPUMIPSState *env, uint32_t count) env->CP0_Count = count; } else { /* Store new count register */ - env->CP0_Count = count - - (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / - env->cp0_count_ns); + env->CP0_Count = count - (uint32_t)clock_ns_to_ticks(env->count_clock, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); /* Update timer timer */ cpu_mips_timer_update(env); } @@ -107,8 +116,8 @@ void cpu_mips_start_count(CPUMIPSState *env) void cpu_mips_stop_count(CPUMIPSState *env) { /* Store the current value */ - env->CP0_Count += (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / - env->cp0_count_ns); + env->CP0_Count += (uint32_t)clock_ns_to_ticks(env->count_clock, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); } static void mips_timer_cb(void *opaque) @@ -121,14 +130,7 @@ static void mips_timer_cb(void *opaque) return; } - /* - * ??? This callback should occur when the counter is exactly equal to - * the comparator value. Offset the count by one to avoid immediately - * retriggering the callback before any virtual time has passed. - */ - env->CP0_Count++; cpu_mips_timer_expire(env); - env->CP0_Count--; } void cpu_mips_clock_init(MIPSCPU *cpu) diff --git a/target/mips/sysemu/machine.c b/target/mips/sysemu/machine.c index 80d37f9c2f..213fd637fc 100644 --- a/target/mips/sysemu/machine.c +++ b/target/mips/sysemu/machine.c @@ -44,7 +44,7 @@ static int put_fpr(QEMUFile *f, void *pv, size_t size, return 0; } -const VMStateInfo vmstate_info_fpr = { +static const VMStateInfo vmstate_info_fpr = { .name = "fpr", .get = get_fpr, .put = put_fpr, @@ -56,21 +56,21 @@ const VMStateInfo vmstate_info_fpr = { #define VMSTATE_FPR_ARRAY(_f, _s, _n) \ VMSTATE_FPR_ARRAY_V(_f, _s, _n, 0) -static VMStateField vmstate_fpu_fields[] = { +static const VMStateField vmstate_fpu_fields[] = { VMSTATE_FPR_ARRAY(fpr, CPUMIPSFPUContext, 32), VMSTATE_UINT32(fcr0, CPUMIPSFPUContext), VMSTATE_UINT32(fcr31, CPUMIPSFPUContext), VMSTATE_END_OF_LIST() }; -const VMStateDescription vmstate_fpu = { +static const VMStateDescription vmstate_fpu = { .name = "cpu/fpu", .version_id = 1, .minimum_version_id = 1, .fields = vmstate_fpu_fields }; -const VMStateDescription vmstate_inactive_fpu = { +static const VMStateDescription vmstate_inactive_fpu = { .name = "cpu/inactive_fpu", .version_id = 1, .minimum_version_id = 1, @@ -79,7 +79,7 @@ const VMStateDescription vmstate_inactive_fpu = { /* TC state */ -static VMStateField vmstate_tc_fields[] = { +static const VMStateField vmstate_tc_fields[] = { VMSTATE_UINTTL_ARRAY(gpr, TCState, 32), #if defined(TARGET_MIPS64) VMSTATE_UINT64_ARRAY(gpr_hi, TCState, 32), @@ -103,14 +103,14 @@ static VMStateField vmstate_tc_fields[] = { VMSTATE_END_OF_LIST() }; -const VMStateDescription vmstate_tc = { +static const VMStateDescription vmstate_tc = { .name = "cpu/tc", .version_id = 2, .minimum_version_id = 2, .fields = vmstate_tc_fields }; -const VMStateDescription vmstate_inactive_tc = { +static const VMStateDescription vmstate_inactive_tc = { .name = "cpu/inactive_tc", .version_id = 2, .minimum_version_id = 2, @@ -119,11 +119,11 @@ const VMStateDescription vmstate_inactive_tc = { /* MVP state */ -const VMStateDescription vmstate_mvp = { +static const VMStateDescription vmstate_mvp = { .name = "cpu/mvp", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(CP0_MVPControl, CPUMIPSMVPContext), VMSTATE_INT32(CP0_MVPConf0, CPUMIPSMVPContext), VMSTATE_INT32(CP0_MVPConf1, CPUMIPSMVPContext), @@ -190,7 +190,7 @@ static int put_tlb(QEMUFile *f, void *pv, size_t size, return 0; } -const VMStateInfo vmstate_info_tlb = { +static const VMStateInfo vmstate_info_tlb = { .name = "tlb_entry", .get = get_tlb, .put = put_tlb, @@ -202,11 +202,11 @@ const VMStateInfo vmstate_info_tlb = { #define VMSTATE_TLB_ARRAY(_f, _s, _n) \ VMSTATE_TLB_ARRAY_V(_f, _s, _n, 0) -const VMStateDescription vmstate_tlb = { +static const VMStateDescription vmstate_tlb = { .name = "cpu/tlb", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(nb_tlb, CPUMIPSTLBContext), VMSTATE_UINT32(tlb_in_use, CPUMIPSTLBContext), VMSTATE_TLB_ARRAY(mmu.r4k.tlb, CPUMIPSTLBContext, MIPS_TLB_MAX), @@ -221,7 +221,7 @@ const VMStateDescription vmstate_mips_cpu = { .version_id = 21, .minimum_version_id = 21, .post_load = cpu_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { /* Active TC */ VMSTATE_STRUCT(env.active_tc, MIPSCPU, 1, vmstate_tc, TCState), @@ -281,8 +281,8 @@ const VMStateDescription vmstate_mips_cpu = { VMSTATE_UINT32(env.CP0_BadInstrP, MIPSCPU), VMSTATE_UINT32(env.CP0_BadInstrX, MIPSCPU), VMSTATE_INT32(env.CP0_Count, MIPSCPU), - VMSTATE_UINT32(env.CP0_SAARI, MIPSCPU), - VMSTATE_UINT64_ARRAY(env.CP0_SAAR, MIPSCPU, 2), + VMSTATE_UNUSED(sizeof(uint32_t)), /* was CP0_SAARI */ + VMSTATE_UNUSED(2 * sizeof(uint64_t)), /* was CP0_SAAR[2] */ VMSTATE_UINTTL(env.CP0_EntryHi, MIPSCPU), VMSTATE_INT32(env.CP0_Compare, MIPSCPU), VMSTATE_INT32(env.CP0_Status, MIPSCPU), diff --git a/target/mips/sysemu/meson.build b/target/mips/sysemu/meson.build index cefc227582..498cf289d6 100644 --- a/target/mips/sysemu/meson.build +++ b/target/mips/sysemu/meson.build @@ -1,7 +1,8 @@ -mips_softmmu_ss.add(files( +mips_system_ss.add(files( 'addr.c', 'cp0.c', 'cp0_timer.c', 'machine.c', + 'mips-qmp-cmds.c', 'physaddr.c', )) diff --git a/target/mips/sysemu/mips-qmp-cmds.c b/target/mips/sysemu/mips-qmp-cmds.c new file mode 100644 index 0000000000..7340ac70ba --- /dev/null +++ b/target/mips/sysemu/mips-qmp-cmds.c @@ -0,0 +1,38 @@ +/* + * QEMU MIPS CPU (monitor definitions) + * + * SPDX-FileCopyrightText: 2012 SUSE LINUX Products GmbH + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/qapi-commands-machine-target.h" +#include "cpu.h" + +static void mips_cpu_add_definition(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CpuDefinitionInfoList **cpu_list = user_data; + CpuDefinitionInfo *info; + const char *typename; + + typename = object_class_get_name(oc); + info = g_malloc0(sizeof(*info)); + info->name = cpu_model_from_type(typename); + info->q_typename = g_strdup(typename); + + QAPI_LIST_PREPEND(*cpu_list, info); +} + +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) +{ + CpuDefinitionInfoList *cpu_list = NULL; + GSList *list; + + list = object_class_get_list(TYPE_MIPS_CPU, false); + g_slist_foreach(list, mips_cpu_add_definition, &cpu_list); + g_slist_free(list); + + return cpu_list; +} diff --git a/target/mips/sysemu/physaddr.c b/target/mips/sysemu/physaddr.c index 1918633aa1..5c5184e136 100644 --- a/target/mips/sysemu/physaddr.c +++ b/target/mips/sysemu/physaddr.c @@ -70,8 +70,7 @@ static int is_seg_am_mapped(unsigned int am, bool eu, int mmu_idx) /* is this AM mapped in current execution mode */ return ((adetlb_mask << am) < 0); default: - assert(0); - return TLBRET_BADADDR; + g_assert_not_reached(); }; } @@ -130,19 +129,6 @@ int get_physical_address(CPUMIPSState *env, hwaddr *physical, /* effective address (modified for KVM T&E kernel segments) */ target_ulong address = real_address; - if (mips_um_ksegs_enabled()) { - /* KVM T&E adds guest kernel segments in useg */ - if (real_address >= KVM_KSEG0_BASE) { - if (real_address < KVM_KSEG2_BASE) { - /* kseg0 */ - address += KSEG0_BASE - KVM_KSEG0_BASE; - } else if (real_address <= USEG_LIMIT) { - /* kseg2/3 */ - address += KSEG2_BASE - KVM_KSEG2_BASE; - } - } - } - if (address <= USEG_LIMIT) { /* useg */ uint16_t segctl; @@ -244,13 +230,12 @@ int get_physical_address(CPUMIPSState *env, hwaddr *physical, hwaddr mips_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); hwaddr phys_addr; int prot; if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, - cpu_mmu_index(env, false)) != 0) { + mips_env_mmu_index(env)) != 0) { return -1; } return phys_addr; diff --git a/target/mips/tcg/dsp_helper.c b/target/mips/tcg/dsp_helper.c index 09b6e5fb15..7a4362c8ef 100644 --- a/target/mips/tcg/dsp_helper.c +++ b/target/mips/tcg/dsp_helper.c @@ -3281,15 +3281,12 @@ target_ulong helper_dextr_l(target_ulong ac, target_ulong shift, CPUMIPSState *env) { uint64_t temp[3]; - target_ulong ret; shift = shift & 0x3F; mipsdsp_rndrashift_acc(temp, ac, shift, env); - ret = (temp[1] << 63) | (temp[0] >> 1); - - return ret; + return (temp[1] << 63) | (temp[0] >> 1); } target_ulong helper_dextr_r_l(target_ulong ac, target_ulong shift, @@ -3297,7 +3294,6 @@ target_ulong helper_dextr_r_l(target_ulong ac, target_ulong shift, { uint64_t temp[3]; uint32_t temp128; - target_ulong ret; shift = shift & 0x3F; mipsdsp_rndrashift_acc(temp, ac, shift, env); @@ -3317,9 +3313,7 @@ target_ulong helper_dextr_r_l(target_ulong ac, target_ulong shift, set_DSPControl_overflow_flag(1, 23, env); } - ret = (temp[1] << 63) | (temp[0] >> 1); - - return ret; + return (temp[1] << 63) | (temp[0] >> 1); } target_ulong helper_dextr_rs_l(target_ulong ac, target_ulong shift, @@ -3327,7 +3321,6 @@ target_ulong helper_dextr_rs_l(target_ulong ac, target_ulong shift, { uint64_t temp[3]; uint32_t temp128; - target_ulong ret; shift = shift & 0x3F; mipsdsp_rndrashift_acc(temp, ac, shift, env); @@ -3354,9 +3347,7 @@ target_ulong helper_dextr_rs_l(target_ulong ac, target_ulong shift, set_DSPControl_overflow_flag(1, 23, env); } - ret = (temp[1] << 63) | (temp[0] >> 1); - - return ret; + return (temp[1] << 63) | (temp[0] >> 1); } #endif diff --git a/target/mips/tcg/exception.c b/target/mips/tcg/exception.c index 0b21e0872b..13275d1ded 100644 --- a/target/mips/tcg/exception.c +++ b/target/mips/tcg/exception.c @@ -79,9 +79,9 @@ void helper_wait(CPUMIPSState *env) void mips_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); + tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); env->active_tc.PC = tb->pc; env->hflags &= ~MIPS_HFLAG_BMASK; env->hflags |= tb->flags & MIPS_HFLAG_BMASK; @@ -125,6 +125,7 @@ static const char * const excp_names[EXCP_LAST + 1] = { [EXCP_TLBRI] = "TLB read-inhibit", [EXCP_MSADIS] = "MSA disabled", [EXCP_MSAFPE] = "MSA floating point", + [EXCP_SEMIHOST] = "Semihosting", }; const char *mips_exception_name(int32_t exception) diff --git a/target/mips/tcg/fpu_helper.c b/target/mips/tcg/fpu_helper.c index 8ce56ed7c8..45d593de48 100644 --- a/target/mips/tcg/fpu_helper.c +++ b/target/mips/tcg/fpu_helper.c @@ -25,7 +25,6 @@ #include "internal.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" #include "fpu/softfloat.h" #include "fpu_helper.h" diff --git a/target/mips/tcg/lcsr.decode b/target/mips/tcg/lcsr.decode new file mode 100644 index 0000000000..960ef8b6f9 --- /dev/null +++ b/target/mips/tcg/lcsr.decode @@ -0,0 +1,17 @@ +# Loongson CSR instructions +# +# Copyright (C) 2023 Jiaxun Yang +# +# SPDX-License-Identifier: LGPL-2.1-or-later +# + +&r rs rt rd sa + +@rs_rd ...... rs:5 ..... rd:5 ..... ...... &r rt=0 sa=0 + +CPUCFG 110010 ..... 01000 ..... 00100 011000 @rs_rd + +RDCSR 110010 ..... 00000 ..... 00100 011000 @rs_rd +WRCSR 110010 ..... 00001 ..... 00100 011000 @rs_rd +DRDCSR 110010 ..... 00010 ..... 00100 011000 @rs_rd +DWRCSR 110010 ..... 00011 ..... 00100 011000 @rs_rd diff --git a/target/mips/tcg/lcsr_translate.c b/target/mips/tcg/lcsr_translate.c new file mode 100644 index 0000000000..352b0f4328 --- /dev/null +++ b/target/mips/tcg/lcsr_translate.c @@ -0,0 +1,75 @@ +/* + * Loongson CSR instructions translation routines + * + * Copyright (c) 2023 Jiaxun Yang + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" +#include "exec/helper-gen.h" +#include "translate.h" + +/* Include the auto-generated decoder. */ +#include "decode-lcsr.c.inc" + +static bool trans_CPUCFG(DisasContext *ctx, arg_CPUCFG *a) +{ + TCGv dest = tcg_temp_new(); + TCGv src1 = tcg_temp_new(); + + gen_load_gpr(src1, a->rs); + gen_helper_lcsr_cpucfg(dest, tcg_env, src1); + gen_store_gpr(dest, a->rd); + + return true; +} + +#ifndef CONFIG_USER_ONLY +static bool gen_rdcsr(DisasContext *ctx, arg_r *a, + void (*func)(TCGv, TCGv_ptr, TCGv)) +{ + TCGv dest = tcg_temp_new(); + TCGv src1 = tcg_temp_new(); + + check_cp0_enabled(ctx); + gen_load_gpr(src1, a->rs); + func(dest, tcg_env, src1); + gen_store_gpr(dest, a->rd); + + return true; +} + +static bool gen_wrcsr(DisasContext *ctx, arg_r *a, + void (*func)(TCGv_ptr, TCGv, TCGv)) +{ + TCGv val = tcg_temp_new(); + TCGv addr = tcg_temp_new(); + + check_cp0_enabled(ctx); + gen_load_gpr(addr, a->rs); + gen_load_gpr(val, a->rd); + func(tcg_env, addr, val); + + return true; +} + +TRANS(RDCSR, gen_rdcsr, gen_helper_lcsr_rdcsr) +TRANS(DRDCSR, gen_rdcsr, gen_helper_lcsr_drdcsr) +TRANS(WRCSR, gen_wrcsr, gen_helper_lcsr_wrcsr) +TRANS(DWRCSR, gen_wrcsr, gen_helper_lcsr_dwrcsr) +#else +#define GEN_FALSE_TRANS(name) \ +static bool trans_##name(DisasContext *ctx, arg_##name * a) \ +{ \ + return false; \ +} + +GEN_FALSE_TRANS(RDCSR) +GEN_FALSE_TRANS(DRDCSR) +GEN_FALSE_TRANS(WRCSR) +GEN_FALSE_TRANS(DWRCSR) +#endif diff --git a/target/mips/tcg/ldst_helper.c b/target/mips/tcg/ldst_helper.c index d0bd0267b2..97056d00a2 100644 --- a/target/mips/tcg/ldst_helper.c +++ b/target/mips/tcg/ldst_helper.c @@ -24,6 +24,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" +#include "exec/cpu_ldst.h" #include "exec/memop.h" #include "internal.h" @@ -248,14 +249,14 @@ void helper_swm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, target_ulong i; for (i = 0; i < base_reglist; i++) { - cpu_stw_mmuidx_ra(env, addr, env->active_tc.gpr[multiple_regs[i]], + cpu_stl_mmuidx_ra(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx, GETPC()); addr += 4; } } if (do_r31) { - cpu_stw_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC()); + cpu_stl_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC()); } } diff --git a/target/mips/tcg/meson.build b/target/mips/tcg/meson.build index 98003779ae..ea7fb582f2 100644 --- a/target/mips/tcg/meson.build +++ b/target/mips/tcg/meson.build @@ -3,6 +3,8 @@ gen = [ decodetree.process('msa.decode', extra_args: '--decode=decode_ase_msa'), decodetree.process('tx79.decode', extra_args: '--static-decode=decode_tx79'), decodetree.process('vr54xx.decode', extra_args: '--decode=decode_ext_vr54xx'), + decodetree.process('octeon.decode', extra_args: '--decode=decode_ext_octeon'), + decodetree.process('lcsr.decode', extra_args: '--decode=decode_ase_lcsr'), ] mips_ss.add(gen) @@ -24,6 +26,8 @@ mips_ss.add(files( )) mips_ss.add(when: 'TARGET_MIPS64', if_true: files( 'tx79_translate.c', + 'octeon_translate.c', + 'lcsr_translate.c', ), if_false: files( 'mxu_translate.c', )) diff --git a/target/mips/tcg/micromips_translate.c.inc b/target/mips/tcg/micromips_translate.c.inc index fc6ede75b8..7510831701 100644 --- a/target/mips/tcg/micromips_translate.c.inc +++ b/target/mips/tcg/micromips_translate.c.inc @@ -704,29 +704,26 @@ static void gen_ldst_multiple(DisasContext *ctx, uint32_t opc, int reglist, gen_base_offset_addr(ctx, t0, base, offset); - t1 = tcg_const_tl(reglist); - t2 = tcg_const_i32(ctx->mem_idx); + t1 = tcg_constant_tl(reglist); + t2 = tcg_constant_i32(ctx->mem_idx); save_cpu_state(ctx, 1); switch (opc) { case LWM32: - gen_helper_lwm(cpu_env, t0, t1, t2); + gen_helper_lwm(tcg_env, t0, t1, t2); break; case SWM32: - gen_helper_swm(cpu_env, t0, t1, t2); + gen_helper_swm(tcg_env, t0, t1, t2); break; #ifdef TARGET_MIPS64 case LDM: - gen_helper_ldm(cpu_env, t0, t1, t2); + gen_helper_ldm(tcg_env, t0, t1, t2); break; case SDM: - gen_helper_sdm(cpu_env, t0, t1, t2); + gen_helper_sdm(tcg_env, t0, t1, t2); break; #endif } - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free_i32(t2); } @@ -825,8 +822,8 @@ static void gen_pool16c_insn(DisasContext *ctx) generate_exception_break(ctx, extract32(ctx->opcode, 0, 4)); break; case SDBBP16: - if (is_uhi(extract32(ctx->opcode, 0, 4))) { - gen_helper_do_semihosting(cpu_env); + if (is_uhi(ctx, extract32(ctx->opcode, 0, 4))) { + ctx->base.is_jmp = DISAS_SEMIHOST; } else { /* * XXX: not clear which exception should be raised @@ -941,8 +938,8 @@ static void gen_pool16c_r6_insn(DisasContext *ctx) break; case R6_SDBBP16: /* SDBBP16 */ - if (is_uhi(extract32(ctx->opcode, 6, 4))) { - gen_helper_do_semihosting(cpu_env); + if (is_uhi(ctx, extract32(ctx->opcode, 6, 4))) { + ctx->base.is_jmp = DISAS_SEMIHOST; } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { generate_exception(ctx, EXCP_RI); @@ -980,20 +977,24 @@ static void gen_ldst_pair(DisasContext *ctx, uint32_t opc, int rd, gen_reserved_instruction(ctx); return; } - tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL); + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL | + ctx->default_tcg_memop_mask); gen_store_gpr(t1, rd); tcg_gen_movi_tl(t1, 4); gen_op_addr_add(ctx, t0, t0, t1); - tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL); + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL | + ctx->default_tcg_memop_mask); gen_store_gpr(t1, rd + 1); break; case SWP: gen_load_gpr(t1, rd); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL | + ctx->default_tcg_memop_mask); tcg_gen_movi_tl(t1, 4); gen_op_addr_add(ctx, t0, t0, t1); gen_load_gpr(t1, rd + 1); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL | + ctx->default_tcg_memop_mask); break; #ifdef TARGET_MIPS64 case LDP: @@ -1001,25 +1002,27 @@ static void gen_ldst_pair(DisasContext *ctx, uint32_t opc, int rd, gen_reserved_instruction(ctx); return; } - tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEUQ | + ctx->default_tcg_memop_mask); gen_store_gpr(t1, rd); tcg_gen_movi_tl(t1, 8); gen_op_addr_add(ctx, t0, t0, t1); - tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEUQ | + ctx->default_tcg_memop_mask); gen_store_gpr(t1, rd + 1); break; case SDP: gen_load_gpr(t1, rd); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUQ | + ctx->default_tcg_memop_mask); tcg_gen_movi_tl(t1, 8); gen_op_addr_add(ctx, t0, t0, t1); gen_load_gpr(t1, rd + 1); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUQ | + ctx->default_tcg_memop_mask); break; #endif } - tcg_temp_free(t0); - tcg_temp_free(t1); } static void gen_pool32axf(CPUMIPSState *env, DisasContext *ctx, int rt, int rs) @@ -1067,7 +1070,6 @@ static void gen_pool32axf(CPUMIPSState *env, DisasContext *ctx, int rt, int rs) gen_load_gpr(t0, rt); gen_mtc0(ctx, t0, rs, (ctx->opcode >> 11) & 0x7); - tcg_temp_free(t0); } break; #endif @@ -1269,14 +1271,13 @@ static void gen_pool32axf(CPUMIPSState *env, DisasContext *ctx, int rt, int rs) TCGv t0 = tcg_temp_new(); save_cpu_state(ctx, 1); - gen_helper_di(t0, cpu_env); + gen_helper_di(t0, tcg_env); gen_store_gpr(t0, rs); /* * Stop translation as we may have switched the execution * mode. */ ctx->base.is_jmp = DISAS_STOP; - tcg_temp_free(t0); } break; case EI: @@ -1285,7 +1286,7 @@ static void gen_pool32axf(CPUMIPSState *env, DisasContext *ctx, int rt, int rs) TCGv t0 = tcg_temp_new(); save_cpu_state(ctx, 1); - gen_helper_ei(t0, cpu_env); + gen_helper_ei(t0, tcg_env); gen_store_gpr(t0, rs); /* * DISAS_STOP isn't sufficient, we need to ensure we break out @@ -1293,7 +1294,6 @@ static void gen_pool32axf(CPUMIPSState *env, DisasContext *ctx, int rt, int rs) */ gen_save_pc(ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; - tcg_temp_free(t0); } break; default: @@ -1310,8 +1310,8 @@ static void gen_pool32axf(CPUMIPSState *env, DisasContext *ctx, int rt, int rs) generate_exception_end(ctx, EXCP_SYSCALL); break; case SDBBP: - if (is_uhi(extract32(ctx->opcode, 16, 10))) { - gen_helper_do_semihosting(cpu_env); + if (is_uhi(ctx, extract32(ctx->opcode, 16, 10))) { + ctx->base.is_jmp = DISAS_SEMIHOST; } else { check_insn(ctx, ISA_MIPS_R1); if (ctx->hflags & MIPS_HFLAG_SBRI) { diff --git a/target/mips/tcg/mips16e_translate.c.inc b/target/mips/tcg/mips16e_translate.c.inc index f57e0a5f2a..5cffe0e412 100644 --- a/target/mips/tcg/mips16e_translate.c.inc +++ b/target/mips/tcg/mips16e_translate.c.inc @@ -172,22 +172,26 @@ static void gen_mips16_save(DisasContext *ctx, case 4: gen_base_offset_addr(ctx, t0, 29, 12); gen_load_gpr(t1, 7); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL | + ctx->default_tcg_memop_mask); /* Fall through */ case 3: gen_base_offset_addr(ctx, t0, 29, 8); gen_load_gpr(t1, 6); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL | + ctx->default_tcg_memop_mask); /* Fall through */ case 2: gen_base_offset_addr(ctx, t0, 29, 4); gen_load_gpr(t1, 5); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL | + ctx->default_tcg_memop_mask); /* Fall through */ case 1: gen_base_offset_addr(ctx, t0, 29, 0); gen_load_gpr(t1, 4); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL | + ctx->default_tcg_memop_mask); } gen_load_gpr(t0, 29); @@ -196,7 +200,8 @@ static void gen_mips16_save(DisasContext *ctx, tcg_gen_movi_tl(t2, -4); \ gen_op_addr_add(ctx, t0, t0, t2); \ gen_load_gpr(t1, reg); \ - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); \ + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL | \ + ctx->default_tcg_memop_mask); \ } while (0) if (do_ra) { @@ -280,9 +285,6 @@ static void gen_mips16_save(DisasContext *ctx, tcg_gen_movi_tl(t2, -framesize); gen_op_addr_add(ctx, cpu_gpr[29], cpu_gpr[29], t2); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); } static void gen_mips16_restore(DisasContext *ctx, @@ -301,7 +303,8 @@ static void gen_mips16_restore(DisasContext *ctx, #define DECR_AND_LOAD(reg) do { \ tcg_gen_movi_tl(t2, -4); \ gen_op_addr_add(ctx, t0, t0, t2); \ - tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL); \ + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL | \ + ctx->default_tcg_memop_mask); \ gen_store_gpr(t1, reg); \ } while (0) @@ -386,9 +389,6 @@ static void gen_mips16_restore(DisasContext *ctx, tcg_gen_movi_tl(t2, framesize); gen_op_addr_add(ctx, cpu_gpr[29], cpu_gpr[29], t2); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); } #if defined(TARGET_MIPS64) @@ -951,8 +951,8 @@ static int decode_ase_mips16e(CPUMIPSState *env, DisasContext *ctx) } break; case RR_SDBBP: - if (is_uhi(extract32(ctx->opcode, 5, 6))) { - gen_helper_do_semihosting(cpu_env); + if (is_uhi(ctx, extract32(ctx->opcode, 5, 6))) { + ctx->base.is_jmp = DISAS_SEMIHOST; } else { /* * XXX: not clear which exception should be raised diff --git a/target/mips/tcg/msa.decode b/target/mips/tcg/msa.decode index 9575289195..4410e2a02e 100644 --- a/target/mips/tcg/msa.decode +++ b/target/mips/tcg/msa.decode @@ -31,8 +31,8 @@ @lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &r @ldst ...... sa:s10 ws:5 wd:5 .... df:2 &msa_i -@bz_v ...... ... .. wt:5 sa:16 &msa_bz df=3 -@bz ...... ... df:2 wt:5 sa:16 &msa_bz +@bz_v ...... ... .. wt:5 sa:s16 &msa_bz df=3 +@bz ...... ... df:2 wt:5 sa:s16 &msa_bz @elm_df ...... .... ...... ws:5 wd:5 ...... &msa_elm_df df=%elm_df n=%elm_n @elm ...... .......... ws:5 wd:5 ...... &msa_elm @vec ...... ..... wt:5 ws:5 wd:5 ...... &msa_r df=0 diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index 4dde5d639a..d2181763e7 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -22,6 +22,7 @@ #include "internal.h" #include "tcg/tcg.h" #include "exec/exec-all.h" +#include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "exec/memop.h" #include "fpu/softfloat.h" @@ -802,9 +803,9 @@ void helper_msa_bset_d(CPUMIPSState *env, uint32_t wd, uint32_t ws, uint32_t wt) * | HADD_S.H | Vector Signed Horizontal Add (halfword) | * | HADD_S.W | Vector Signed Horizontal Add (word) | * | HADD_S.D | Vector Signed Horizontal Add (doubleword) | - * | HADD_U.H | Vector Unigned Horizontal Add (halfword) | - * | HADD_U.W | Vector Unigned Horizontal Add (word) | - * | HADD_U.D | Vector Unigned Horizontal Add (doubleword) | + * | HADD_U.H | Vector Unsigned Horizontal Add (halfword) | + * | HADD_U.W | Vector Unsigned Horizontal Add (word) | + * | HADD_U.D | Vector Unsigned Horizontal Add (doubleword) | * +---------------+----------------------------------------------------------+ */ @@ -3451,9 +3452,9 @@ void helper_msa_mulv_d(CPUMIPSState *env, * | HSUB_S.H | Vector Signed Horizontal Subtract (halfword) | * | HSUB_S.W | Vector Signed Horizontal Subtract (word) | * | HSUB_S.D | Vector Signed Horizontal Subtract (doubleword) | - * | HSUB_U.H | Vector Unigned Horizontal Subtract (halfword) | - * | HSUB_U.W | Vector Unigned Horizontal Subtract (word) | - * | HSUB_U.D | Vector Unigned Horizontal Subtract (doubleword) | + * | HSUB_U.H | Vector Unsigned Horizontal Subtract (halfword) | + * | HSUB_U.W | Vector Unsigned Horizontal Subtract (word) | + * | HSUB_U.D | Vector Unsigned Horizontal Subtract (doubleword) | * | SUBS_S.B | Vector Signed Saturated Subtract (of Signed) (byte) | * | SUBS_S.H | Vector Signed Saturated Subtract (of Signed) (halfword) | * | SUBS_S.W | Vector Signed Saturated Subtract (of Signed) (word) | @@ -5333,7 +5334,7 @@ void helper_msa_shf_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } msa_move_v(pwd, pwx); } @@ -5368,7 +5369,7 @@ void helper_msa_ ## helper ## _df(CPUMIPSState *env, uint32_t df, \ } \ break; \ default: \ - assert(0); \ + g_assert_not_reached(); \ } \ } @@ -5413,7 +5414,7 @@ void helper_msa_ldi_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } } @@ -5461,7 +5462,7 @@ void helper_msa_ ## helper ## _df(CPUMIPSState *env, uint32_t df, uint32_t wd, \ } \ break; \ default: \ - assert(0); \ + g_assert_not_reached(); \ } \ } @@ -5511,7 +5512,7 @@ void helper_msa_ ## helper ## _df(CPUMIPSState *env, uint32_t df, \ } \ break; \ default: \ - assert(0); \ + g_assert_not_reached(); \ } \ } @@ -5557,7 +5558,7 @@ static inline void msa_sld_df(uint32_t df, wr_t *pwd, } break; default: - assert(0); + g_assert_not_reached(); } } @@ -5632,7 +5633,7 @@ void helper_msa_ ## func ## _df(CPUMIPSState *env, uint32_t df, \ pwd->d[1] = msa_ ## func ## _df(df, pws->d[1], pwt->d[1]); \ break; \ default: \ - assert(0); \ + g_assert_not_reached(); \ } \ } @@ -5771,7 +5772,7 @@ void helper_msa_ ## func ## _df(CPUMIPSState *env, uint32_t df, uint32_t wd, \ pwd->d[1] = msa_ ## func ## _df(df, pwd->d[1], pws->d[1], pwt->d[1]); \ break; \ default: \ - assert(0); \ + g_assert_not_reached(); \ } \ } @@ -5811,7 +5812,7 @@ static inline void msa_splat_df(uint32_t df, wr_t *pwd, } break; default: - assert(0); + g_assert_not_reached(); } } @@ -5869,7 +5870,7 @@ void helper_msa_##FUNC(CPUMIPSState *env, uint32_t df, uint32_t wd, \ MSA_LOOP_D; \ break; \ default: \ - assert(0); \ + g_assert_not_reached(); \ } \ msa_move_v(pwd, pwx); \ } @@ -6090,7 +6091,7 @@ void helper_msa_insve_df(CPUMIPSState *env, uint32_t df, uint32_t wd, pwd->d[n] = (int64_t)pws->d[0]; break; default: - assert(0); + g_assert_not_reached(); } } @@ -6150,7 +6151,7 @@ void helper_msa_fill_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } } @@ -6565,7 +6566,7 @@ static inline void compare_af(CPUMIPSState *env, wr_t *pwd, wr_t *pws, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, retaddr); @@ -6596,7 +6597,7 @@ static inline void compare_un(CPUMIPSState *env, wr_t *pwd, wr_t *pws, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, retaddr); @@ -6625,7 +6626,7 @@ static inline void compare_eq(CPUMIPSState *env, wr_t *pwd, wr_t *pws, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, retaddr); @@ -6654,7 +6655,7 @@ static inline void compare_ueq(CPUMIPSState *env, wr_t *pwd, wr_t *pws, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, retaddr); @@ -6683,7 +6684,7 @@ static inline void compare_lt(CPUMIPSState *env, wr_t *pwd, wr_t *pws, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, retaddr); @@ -6712,7 +6713,7 @@ static inline void compare_ult(CPUMIPSState *env, wr_t *pwd, wr_t *pws, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, retaddr); @@ -6741,7 +6742,7 @@ static inline void compare_le(CPUMIPSState *env, wr_t *pwd, wr_t *pws, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, retaddr); @@ -6770,7 +6771,7 @@ static inline void compare_ule(CPUMIPSState *env, wr_t *pwd, wr_t *pws, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, retaddr); @@ -6799,7 +6800,7 @@ static inline void compare_or(CPUMIPSState *env, wr_t *pwd, wr_t *pws, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, retaddr); @@ -6828,7 +6829,7 @@ static inline void compare_une(CPUMIPSState *env, wr_t *pwd, wr_t *pws, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, retaddr); @@ -6857,7 +6858,7 @@ static inline void compare_ne(CPUMIPSState *env, wr_t *pwd, wr_t *pws, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, retaddr); @@ -7107,7 +7108,7 @@ void helper_msa_fadd_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7137,7 +7138,7 @@ void helper_msa_fsub_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7167,7 +7168,7 @@ void helper_msa_fmul_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7198,7 +7199,7 @@ void helper_msa_fdiv_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7245,7 +7246,7 @@ void helper_msa_fmadd_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7280,7 +7281,7 @@ void helper_msa_fmsub_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7317,7 +7318,7 @@ void helper_msa_fexp2_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7371,7 +7372,7 @@ void helper_msa_fexdo_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7417,7 +7418,7 @@ void helper_msa_ftq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7431,15 +7432,15 @@ void helper_msa_ftq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, #define MSA_FLOAT_MAXOP(DEST, OP, ARG1, ARG2, BITS) \ do { \ - float_status *status = &env->active_tc.msa_fp_status; \ + float_status *status_ = &env->active_tc.msa_fp_status; \ int c; \ \ - set_float_exception_flags(0, status); \ - DEST = float ## BITS ## _ ## OP(ARG1, ARG2, status); \ + set_float_exception_flags(0, status_); \ + DEST = float ## BITS ## _ ## OP(ARG1, ARG2, status_); \ c = update_msacsr(env, 0, 0); \ \ if (get_enabled_exceptions(env, c)) { \ - DEST = ((FLOAT_SNAN ## BITS(status) >> 6) << 6) | c; \ + DEST = ((FLOAT_SNAN ## BITS(status_) >> 6) << 6) | c; \ } \ } while (0) @@ -7526,7 +7527,7 @@ void helper_msa_fmin_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } else { - assert(0); + g_assert_not_reached(); } @@ -7555,7 +7556,7 @@ void helper_msa_fmin_a_df(CPUMIPSState *env, uint32_t df, uint32_t wd, FMAXMIN_A(min, max, pwx->d[0], pws->d[0], pwt->d[0], 64, status); FMAXMIN_A(min, max, pwx->d[1], pws->d[1], pwt->d[1], 64, status); } else { - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7628,7 +7629,7 @@ void helper_msa_fmax_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } else { - assert(0); + g_assert_not_reached(); } @@ -7657,7 +7658,7 @@ void helper_msa_fmax_a_df(CPUMIPSState *env, uint32_t df, uint32_t wd, FMAXMIN_A(max, min, pwx->d[0], pws->d[0], pwt->d[0], 64, status); FMAXMIN_A(max, min, pwx->d[1], pws->d[1], pwt->d[1], 64, status); } else { - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7681,7 +7682,7 @@ void helper_msa_fclass_df(CPUMIPSState *env, uint32_t df, pwd->d[0] = float_class_d(pws->d[0], status); pwd->d[1] = float_class_d(pws->d[1], status); } else { - assert(0); + g_assert_not_reached(); } } @@ -7723,7 +7724,7 @@ void helper_msa_ftrunc_s_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7753,7 +7754,7 @@ void helper_msa_ftrunc_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7783,7 +7784,7 @@ void helper_msa_fsqrt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7832,7 +7833,7 @@ void helper_msa_frsqrt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7862,7 +7863,7 @@ void helper_msa_frcp_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7892,7 +7893,7 @@ void helper_msa_frint_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7946,7 +7947,7 @@ void helper_msa_flog2_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -7983,7 +7984,7 @@ void helper_msa_fexupl_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -8019,7 +8020,7 @@ void helper_msa_fexupr_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -8046,7 +8047,7 @@ void helper_msa_ffql_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } msa_move_v(pwd, pwx); @@ -8072,7 +8073,7 @@ void helper_msa_ffqr_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } msa_move_v(pwd, pwx); @@ -8100,7 +8101,7 @@ void helper_msa_ftint_s_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -8130,7 +8131,7 @@ void helper_msa_ftint_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -8166,7 +8167,7 @@ void helper_msa_ffint_s_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -8196,7 +8197,7 @@ void helper_msa_ffint_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, } break; default: - assert(0); + g_assert_not_reached(); } check_msacsr_cause(env, GETPC()); @@ -8213,7 +8214,7 @@ void helper_msa_ffint_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, #if !defined(CONFIG_USER_ONLY) #define MEMOP_IDX(DF) \ MemOpIdx oi = make_memop_idx(MO_TE | DF | MO_UNALN, \ - cpu_mmu_index(env, false)); + mips_env_mmu_index(env)); #else #define MEMOP_IDX(DF) #endif @@ -8322,21 +8323,21 @@ void helper_msa_st_b(CPUMIPSState *env, uint32_t wd, target_ulong addr) { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = mips_env_mmu_index(env); uintptr_t ra = GETPC(); ensure_writable_pages(env, addr, mmu_idx, ra); /* Store 8 bytes at a time. Vector element ordering makes this LE. */ cpu_stq_le_data_ra(env, addr + 0, pwd->d[0], ra); - cpu_stq_le_data_ra(env, addr + 0, pwd->d[1], ra); + cpu_stq_le_data_ra(env, addr + 8, pwd->d[1], ra); } void helper_msa_st_h(CPUMIPSState *env, uint32_t wd, target_ulong addr) { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = mips_env_mmu_index(env); uintptr_t ra = GETPC(); uint64_t d0, d1; @@ -8357,7 +8358,7 @@ void helper_msa_st_w(CPUMIPSState *env, uint32_t wd, target_ulong addr) { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = mips_env_mmu_index(env); uintptr_t ra = GETPC(); uint64_t d0, d1; @@ -8378,7 +8379,7 @@ void helper_msa_st_d(CPUMIPSState *env, uint32_t wd, target_ulong addr) { wr_t *pwd = &(env->active_fpu.fpr[wd].wr); - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = mips_env_mmu_index(env); uintptr_t ra = GETPC(); ensure_writable_pages(env, addr, mmu_idx, GETPC()); diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 7576b3ed86..75cf80a20e 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -11,11 +11,8 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "exec/helper-gen.h" #include "translate.h" #include "fpu_helper.h" -#include "internal.h" static int elm_n(DisasContext *ctx, int x); static int elm_df(DisasContext *ctx, int x); @@ -68,8 +65,8 @@ struct dfe { static int df_extract_val(DisasContext *ctx, int x, const struct dfe *s) { for (unsigned i = 0; i < 4; i++) { - if (extract32(x, s->start, s->length) == s->mask) { - return extract32(x, 0, s->start); + if (extract32(x, s[i].start, s[i].length) == s[i].mask) { + return extract32(x, 0, s[i].start); } } return -1; @@ -82,7 +79,7 @@ static int df_extract_val(DisasContext *ctx, int x, const struct dfe *s) static int df_extract_df(DisasContext *ctx, int x, const struct dfe *s) { for (unsigned i = 0; i < 4; i++) { - if (extract32(x, s->start, s->length) == s->mask) { + if (extract32(x, s[i].start, s[i].length) == s[i].mask) { return i; } } @@ -143,7 +140,7 @@ void msa_translate_init(void) off = offsetof(CPUMIPSState, active_fpu.fpr[i].wr.d[1]); msa_wr_d[i * 2 + 1] = - tcg_global_mem_new_i64(cpu_env, off, msaregnames[i * 2 + 1]); + tcg_global_mem_new_i64(tcg_env, off, msaregnames[i * 2 + 1]); } } @@ -217,8 +214,6 @@ static void gen_check_zero_element(TCGv tresult, uint8_t df, uint8_t wt, /* if some bit is non-zero then some element is zero */ tcg_gen_setcondi_i64(cond, t0, t0, 0); tcg_gen_trunc_i64_tl(tresult, t0); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } static bool gen_msa_BxZ_V(DisasContext *ctx, int wt, int sa, TCGCond cond) @@ -237,7 +232,6 @@ static bool gen_msa_BxZ_V(DisasContext *ctx, int wt, int sa, TCGCond cond) tcg_gen_or_i64(t0, msa_wr_d[wt << 1], msa_wr_d[(wt << 1) + 1]); tcg_gen_setcondi_i64(cond, t0, t0, 0); tcg_gen_trunc_i64_tl(bcond, t0); - tcg_temp_free_i64(t0); ctx->btarget = ctx->base.pc_next + (sa << 2) + 4; @@ -294,7 +288,7 @@ static bool trans_msa_i8(DisasContext *ctx, arg_msa_i *a, return true; } - gen_msa_i8(cpu_env, + gen_msa_i8(tcg_env, tcg_constant_i32(a->wd), tcg_constant_i32(a->ws), tcg_constant_i32(a->sa)); @@ -320,7 +314,7 @@ static bool trans_SHF(DisasContext *ctx, arg_msa_i *a) return true; } - gen_helper_msa_shf_df(cpu_env, + gen_helper_msa_shf_df(tcg_env, tcg_constant_i32(a->df), tcg_constant_i32(a->wd), tcg_constant_i32(a->ws), @@ -336,7 +330,7 @@ static bool trans_msa_i5(DisasContext *ctx, arg_msa_i *a, return true; } - gen_msa_i5(cpu_env, + gen_msa_i5(tcg_env, tcg_constant_i32(a->df), tcg_constant_i32(a->wd), tcg_constant_i32(a->ws), @@ -363,7 +357,7 @@ static bool trans_LDI(DisasContext *ctx, arg_msa_ldi *a) return true; } - gen_helper_msa_ldi_df(cpu_env, + gen_helper_msa_ldi_df(tcg_env, tcg_constant_i32(a->df), tcg_constant_i32(a->wd), tcg_constant_i32(a->sa)); @@ -382,7 +376,7 @@ static bool trans_msa_bit(DisasContext *ctx, arg_msa_bit *a, return true; } - gen_msa_bit(cpu_env, + gen_msa_bit(tcg_env, tcg_constant_i32(a->df), tcg_constant_i32(a->wd), tcg_constant_i32(a->ws), @@ -399,7 +393,7 @@ TRANS(BSETI, trans_msa_bit, gen_helper_msa_bseti_df); TRANS(BNEGI, trans_msa_bit, gen_helper_msa_bnegi_df); TRANS(BINSLI, trans_msa_bit, gen_helper_msa_binsli_df); TRANS(BINSRI, trans_msa_bit, gen_helper_msa_binsri_df); -TRANS(SAT_S, trans_msa_bit, gen_helper_msa_sat_u_df); +TRANS(SAT_S, trans_msa_bit, gen_helper_msa_sat_s_df); TRANS(SAT_U, trans_msa_bit, gen_helper_msa_sat_u_df); TRANS(SRARI, trans_msa_bit, gen_helper_msa_srari_df); TRANS(SRLRI, trans_msa_bit, gen_helper_msa_srlri_df); @@ -411,7 +405,7 @@ static bool trans_msa_3rf(DisasContext *ctx, arg_msa_r *a, return true; } - gen_msa_3rf(cpu_env, + gen_msa_3rf(tcg_env, tcg_constant_i32(a->df), tcg_constant_i32(a->wd), tcg_constant_i32(a->ws), @@ -431,7 +425,7 @@ static bool trans_msa_3r(DisasContext *ctx, arg_msa_r *a, return true; } - gen_msa_3r(cpu_env, + gen_msa_3r(tcg_env, tcg_constant_i32(a->wd), tcg_constant_i32(a->ws), tcg_constant_i32(a->wt)); @@ -525,7 +519,7 @@ static bool trans_MOVE_V(DisasContext *ctx, arg_msa_elm *a) return true; } - gen_helper_msa_move_v(cpu_env, + gen_helper_msa_move_v(tcg_env, tcg_constant_i32(a->wd), tcg_constant_i32(a->ws)); @@ -543,9 +537,7 @@ static bool trans_CTCMSA(DisasContext *ctx, arg_msa_elm *a) telm = tcg_temp_new(); gen_load_gpr(telm, a->ws); - gen_helper_msa_ctcmsa(cpu_env, telm, tcg_constant_i32(a->wd)); - - tcg_temp_free(telm); + gen_helper_msa_ctcmsa(tcg_env, telm, tcg_constant_i32(a->wd)); return true; } @@ -560,11 +552,9 @@ static bool trans_CFCMSA(DisasContext *ctx, arg_msa_elm *a) telm = tcg_temp_new(); - gen_helper_msa_cfcmsa(telm, cpu_env, tcg_constant_i32(a->ws)); + gen_helper_msa_cfcmsa(telm, tcg_env, tcg_constant_i32(a->ws)); gen_store_gpr(telm, a->wd); - tcg_temp_free(telm); - return true; } @@ -579,7 +569,7 @@ static bool trans_msa_elm(DisasContext *ctx, arg_msa_elm_df *a, return true; } - gen_msa_elm_df(cpu_env, + gen_msa_elm_df(tcg_env, tcg_constant_i32(a->df), tcg_constant_i32(a->wd), tcg_constant_i32(a->ws), @@ -599,16 +589,11 @@ static bool trans_msa_elm_fn(DisasContext *ctx, arg_msa_elm_df *a, return false; } - if (check_msa_enabled(ctx)) { + if (!check_msa_enabled(ctx)) { return true; } - if (a->wd == 0) { - /* Treat as NOP. */ - return true; - } - - gen_msa_elm[a->df](cpu_env, + gen_msa_elm[a->df](tcg_env, tcg_constant_i32(a->wd), tcg_constant_i32(a->ws), tcg_constant_i32(a->n)); @@ -624,6 +609,11 @@ static bool trans_msa_elm_fn(DisasContext *ctx, arg_msa_elm_df *a, static bool trans_COPY_U(DisasContext *ctx, arg_msa_elm_df *a) { + if (a->wd == 0) { + /* Treat as NOP. */ + return true; + } + static gen_helper_piii * const gen_msa_copy_u[4] = { gen_helper_msa_copy_u_b, gen_helper_msa_copy_u_h, NULL_IF_MIPS32(gen_helper_msa_copy_u_w), NULL @@ -634,6 +624,11 @@ static bool trans_COPY_U(DisasContext *ctx, arg_msa_elm_df *a) static bool trans_COPY_S(DisasContext *ctx, arg_msa_elm_df *a) { + if (a->wd == 0) { + /* Treat as NOP. */ + return true; + } + static gen_helper_piii * const gen_msa_copy_s[4] = { gen_helper_msa_copy_s_b, gen_helper_msa_copy_s_h, gen_helper_msa_copy_s_w, NULL_IF_MIPS32(gen_helper_msa_copy_s_d) @@ -703,7 +698,7 @@ static bool trans_msa_2r(DisasContext *ctx, arg_msa_r *a, return true; } - gen_msa_2r(cpu_env, tcg_constant_i32(a->wd), tcg_constant_i32(a->ws)); + gen_msa_2r(tcg_env, tcg_constant_i32(a->wd), tcg_constant_i32(a->ws)); return true; } @@ -723,7 +718,7 @@ static bool trans_FILL(DisasContext *ctx, arg_msa_r *a) return true; } - gen_helper_msa_fill_df(cpu_env, + gen_helper_msa_fill_df(tcg_env, tcg_constant_i32(a->df), tcg_constant_i32(a->wd), tcg_constant_i32(a->ws)); @@ -738,7 +733,7 @@ static bool trans_msa_2rf(DisasContext *ctx, arg_msa_r *a, return true; } - gen_msa_2rf(cpu_env, + gen_msa_2rf(tcg_env, tcg_constant_i32(a->df), tcg_constant_i32(a->wd), tcg_constant_i32(a->ws)); @@ -747,8 +742,8 @@ static bool trans_msa_2rf(DisasContext *ctx, arg_msa_r *a, } TRANS(FCLASS, trans_msa_2rf, gen_helper_msa_fclass_df); -TRANS(FTRUNC_S, trans_msa_2rf, gen_helper_msa_fclass_df); -TRANS(FTRUNC_U, trans_msa_2rf, gen_helper_msa_ftrunc_s_df); +TRANS(FTRUNC_S, trans_msa_2rf, gen_helper_msa_ftrunc_s_df); +TRANS(FTRUNC_U, trans_msa_2rf, gen_helper_msa_ftrunc_u_df); TRANS(FSQRT, trans_msa_2rf, gen_helper_msa_fsqrt_df); TRANS(FRSQRT, trans_msa_2rf, gen_helper_msa_frsqrt_df); TRANS(FRCP, trans_msa_2rf, gen_helper_msa_frcp_df); @@ -775,9 +770,7 @@ static bool trans_msa_ldst(DisasContext *ctx, arg_msa_i *a, taddr = tcg_temp_new(); gen_base_offset_addr(ctx, taddr, a->ws, a->sa << a->df); - gen_msa_ldst(cpu_env, tcg_constant_i32(a->wd), taddr); - - tcg_temp_free(taddr); + gen_msa_ldst(tcg_env, tcg_constant_i32(a->wd), taddr); return true; } diff --git a/target/mips/tcg/mxu_translate.c b/target/mips/tcg/mxu_translate.c index f52244e1b2..c517258ac5 100644 --- a/target/mips/tcg/mxu_translate.c +++ b/target/mips/tcg/mxu_translate.c @@ -16,8 +16,6 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "exec/helper-gen.h" #include "translate.h" /* @@ -239,11 +237,11 @@ * ├─ 001100 ─ OPC_MXU_D16MADL * ├─ 001101 ─ OPC_MXU_S16MAD * ├─ 001110 ─ OPC_MXU_Q16ADD - * ├─ 001111 ─ OPC_MXU_D16MACE 23 + * ├─ 001111 ─ OPC_MXU_D16MACE 20 (13..10 don't care) * │ ┌─ 0 ─ OPC_MXU_S32LDD * ├─ 010000 ─ OPC_MXU__POOL04 ─┴─ 1 ─ OPC_MXU_S32LDDR * │ - * │ 23 + * │ 20 (13..10 don't care) * ├─ 010001 ─ OPC_MXU__POOL05 ─┬─ 0 ─ OPC_MXU_S32STD * │ └─ 1 ─ OPC_MXU_S32STDR * │ @@ -255,11 +253,11 @@ * ├─ 010011 ─ OPC_MXU__POOL07 ─┬─ 0000 ─ OPC_MXU_S32STDV * │ └─ 0001 ─ OPC_MXU_S32STDVR * │ - * │ 23 + * │ 20 (13..10 don't care) * ├─ 010100 ─ OPC_MXU__POOL08 ─┬─ 0 ─ OPC_MXU_S32LDI * │ └─ 1 ─ OPC_MXU_S32LDIR * │ - * │ 23 + * │ 20 (13..10 don't care) * ├─ 010101 ─ OPC_MXU__POOL09 ─┬─ 0 ─ OPC_MXU_S32SDI * │ └─ 1 ─ OPC_MXU_S32SDIR * │ @@ -270,7 +268,7 @@ * │ 13..10 * ├─ 010111 ─ OPC_MXU__POOL11 ─┬─ 0000 ─ OPC_MXU_S32SDIV * │ └─ 0001 ─ OPC_MXU_S32SDIVR - * ├─ 011000 ─ OPC_MXU_D32ADD + * ├─ 011000 ─ OPC_MXU_D32ADD (catches D32ADDC too) * │ 23..22 * MXU ├─ 011001 ─ OPC_MXU__POOL12 ─┬─ 00 ─ OPC_MXU_D32ACC * opcodes ─┤ ├─ 01 ─ OPC_MXU_D32ACCM @@ -279,7 +277,7 @@ * │ 23..22 * ├─ 011011 ─ OPC_MXU__POOL13 ─┬─ 00 ─ OPC_MXU_Q16ACC * │ ├─ 01 ─ OPC_MXU_Q16ACCM - * │ └─ 10 ─ OPC_MXU_Q16ASUM + * │ └─ 10 ─ OPC_MXU_D16ASUM * │ * │ 23..22 * ├─ 011100 ─ OPC_MXU__POOL14 ─┬─ 00 ─ OPC_MXU_Q8ADDE @@ -292,9 +290,9 @@ * ├─ 100010 ─ OPC_MXU_S8LDD * ├─ 100011 ─ OPC_MXU_S8STD 15..14 * ├─ 100100 ─ OPC_MXU_S8LDI ┌─ 00 ─ OPC_MXU_S32MUL - * ├─ 100101 ─ OPC_MXU_S8SDI ├─ 00 ─ OPC_MXU_S32MULU - * │ ├─ 00 ─ OPC_MXU_S32EXTR - * ├─ 100110 ─ OPC_MXU__POOL15 ─┴─ 00 ─ OPC_MXU_S32EXTRV + * ├─ 100101 ─ OPC_MXU_S8SDI ├─ 01 ─ OPC_MXU_S32MULU + * │ ├─ 10 ─ OPC_MXU_S32EXTR + * ├─ 100110 ─ OPC_MXU__POOL15 ─┴─ 11 ─ OPC_MXU_S32EXTRV * │ * │ 20..18 * ├─ 100111 ─ OPC_MXU__POOL16 ─┬─ 000 ─ OPC_MXU_D32SARW @@ -306,7 +304,7 @@ * │ ├─ 110 ─ OPC_MXU_S32OR * │ └─ 111 ─ OPC_MXU_S32XOR * │ - * │ 7..5 + * │ 8..6 * ├─ 101000 ─ OPC_MXU__POOL17 ─┬─ 000 ─ OPC_MXU_LXB * │ ├─ 001 ─ OPC_MXU_LXH * ├─ 101001 ─ ├─ 011 ─ OPC_MXU_LXW @@ -320,15 +318,15 @@ * ├─ 110001 ─ OPC_MXU_D32SLR 20..18 * ├─ 110010 ─ OPC_MXU_D32SARL ┌─ 000 ─ OPC_MXU_D32SLLV * ├─ 110011 ─ OPC_MXU_D32SAR ├─ 001 ─ OPC_MXU_D32SLRV - * ├─ 110100 ─ OPC_MXU_Q16SLL ├─ 010 ─ OPC_MXU_D32SARV - * ├─ 110101 ─ OPC_MXU_Q16SLR ├─ 011 ─ OPC_MXU_Q16SLLV - * │ ├─ 100 ─ OPC_MXU_Q16SLRV - * ├─ 110110 ─ OPC_MXU__POOL18 ─┴─ 101 ─ OPC_MXU_Q16SARV + * ├─ 110100 ─ OPC_MXU_Q16SLL ├─ 011 ─ OPC_MXU_D32SARV + * ├─ 110101 ─ OPC_MXU_Q16SLR ├─ 100 ─ OPC_MXU_Q16SLLV + * │ ├─ 101 ─ OPC_MXU_Q16SLRV + * ├─ 110110 ─ OPC_MXU__POOL18 ─┴─ 111 ─ OPC_MXU_Q16SARV * │ * ├─ 110111 ─ OPC_MXU_Q16SAR * │ 23..22 * ├─ 111000 ─ OPC_MXU__POOL19 ─┬─ 00 ─ OPC_MXU_Q8MUL - * │ └─ 01 ─ OPC_MXU_Q8MULSU + * │ └─ 10 ─ OPC_MXU_Q8MULSU * │ * │ 20..18 * ├─ 111001 ─ OPC_MXU__POOL20 ─┬─ 000 ─ OPC_MXU_Q8MOVZ @@ -355,15 +353,62 @@ */ enum { + OPC_MXU_S32MADD = 0x00, + OPC_MXU_S32MADDU = 0x01, OPC_MXU__POOL00 = 0x03, + OPC_MXU_S32MSUB = 0x04, + OPC_MXU_S32MSUBU = 0x05, + OPC_MXU__POOL01 = 0x06, + OPC_MXU__POOL02 = 0x07, OPC_MXU_D16MUL = 0x08, + OPC_MXU__POOL03 = 0x09, OPC_MXU_D16MAC = 0x0A, + OPC_MXU_D16MACF = 0x0B, + OPC_MXU_D16MADL = 0x0C, + OPC_MXU_S16MAD = 0x0D, + OPC_MXU_Q16ADD = 0x0E, + OPC_MXU_D16MACE = 0x0F, OPC_MXU__POOL04 = 0x10, + OPC_MXU__POOL05 = 0x11, + OPC_MXU__POOL06 = 0x12, + OPC_MXU__POOL07 = 0x13, + OPC_MXU__POOL08 = 0x14, + OPC_MXU__POOL09 = 0x15, + OPC_MXU__POOL10 = 0x16, + OPC_MXU__POOL11 = 0x17, + OPC_MXU_D32ADD = 0x18, + OPC_MXU__POOL12 = 0x19, + OPC_MXU__POOL13 = 0x1B, + OPC_MXU__POOL14 = 0x1C, + OPC_MXU_Q8ACCE = 0x1D, OPC_MXU_S8LDD = 0x22, + OPC_MXU_S8STD = 0x23, + OPC_MXU_S8LDI = 0x24, + OPC_MXU_S8SDI = 0x25, + OPC_MXU__POOL15 = 0x26, OPC_MXU__POOL16 = 0x27, + OPC_MXU__POOL17 = 0x28, + OPC_MXU_S16LDD = 0x2A, + OPC_MXU_S16STD = 0x2B, + OPC_MXU_S16LDI = 0x2C, + OPC_MXU_S16SDI = 0x2D, OPC_MXU_S32M2I = 0x2E, OPC_MXU_S32I2M = 0x2F, + OPC_MXU_D32SLL = 0x30, + OPC_MXU_D32SLR = 0x31, + OPC_MXU_D32SARL = 0x32, + OPC_MXU_D32SAR = 0x33, + OPC_MXU_Q16SLL = 0x34, + OPC_MXU_Q16SLR = 0x35, + OPC_MXU__POOL18 = 0x36, + OPC_MXU_Q16SAR = 0x37, OPC_MXU__POOL19 = 0x38, + OPC_MXU__POOL20 = 0x39, + OPC_MXU__POOL21 = 0x3A, + OPC_MXU_Q16SCOP = 0x3B, + OPC_MXU_Q8MADL = 0x3C, + OPC_MXU_S32SFL = 0x3D, + OPC_MXU_Q8SAD = 0x3E, }; @@ -377,35 +422,152 @@ enum { OPC_MXU_D16MIN = 0x03, OPC_MXU_Q8MAX = 0x04, OPC_MXU_Q8MIN = 0x05, + OPC_MXU_Q8SLT = 0x06, + OPC_MXU_Q8SLTU = 0x07, }; /* - * MXU pool 04 + * MXU pool 01 */ enum { - OPC_MXU_S32LDD = 0x00, - OPC_MXU_S32LDDR = 0x01, + OPC_MXU_S32SLT = 0x00, + OPC_MXU_D16SLT = 0x01, + OPC_MXU_D16AVG = 0x02, + OPC_MXU_D16AVGR = 0x03, + OPC_MXU_Q8AVG = 0x04, + OPC_MXU_Q8AVGR = 0x05, + OPC_MXU_Q8ADD = 0x07, +}; + +/* + * MXU pool 02 + */ +enum { + OPC_MXU_S32CPS = 0x00, + OPC_MXU_D16CPS = 0x02, + OPC_MXU_Q8ABD = 0x04, + OPC_MXU_Q16SAT = 0x06, +}; + +/* + * MXU pool 03 + */ +enum { + OPC_MXU_D16MULF = 0x00, + OPC_MXU_D16MULE = 0x01, +}; + +/* + * MXU pool 04 05 06 07 08 09 10 11 + */ +enum { + OPC_MXU_S32LDST = 0x00, + OPC_MXU_S32LDSTR = 0x01, +}; + +/* + * MXU pool 12 + */ +enum { + OPC_MXU_D32ACC = 0x00, + OPC_MXU_D32ACCM = 0x01, + OPC_MXU_D32ASUM = 0x02, +}; + +/* + * MXU pool 13 + */ +enum { + OPC_MXU_Q16ACC = 0x00, + OPC_MXU_Q16ACCM = 0x01, + OPC_MXU_D16ASUM = 0x02, +}; + +/* + * MXU pool 14 + */ +enum { + OPC_MXU_Q8ADDE = 0x00, + OPC_MXU_D8SUM = 0x01, + OPC_MXU_D8SUMC = 0x02, +}; + +/* + * MXU pool 15 + */ +enum { + OPC_MXU_S32MUL = 0x00, + OPC_MXU_S32MULU = 0x01, + OPC_MXU_S32EXTR = 0x02, + OPC_MXU_S32EXTRV = 0x03, }; /* * MXU pool 16 */ enum { + OPC_MXU_D32SARW = 0x00, + OPC_MXU_S32ALN = 0x01, OPC_MXU_S32ALNI = 0x02, + OPC_MXU_S32LUI = 0x03, OPC_MXU_S32NOR = 0x04, OPC_MXU_S32AND = 0x05, OPC_MXU_S32OR = 0x06, OPC_MXU_S32XOR = 0x07, }; +/* + * MXU pool 17 + */ +enum { + OPC_MXU_LXB = 0x00, + OPC_MXU_LXH = 0x01, + OPC_MXU_LXW = 0x03, + OPC_MXU_LXBU = 0x04, + OPC_MXU_LXHU = 0x05, +}; + +/* + * MXU pool 18 + */ +enum { + OPC_MXU_D32SLLV = 0x00, + OPC_MXU_D32SLRV = 0x01, + OPC_MXU_D32SARV = 0x03, + OPC_MXU_Q16SLLV = 0x04, + OPC_MXU_Q16SLRV = 0x05, + OPC_MXU_Q16SARV = 0x07, +}; + /* * MXU pool 19 */ enum { OPC_MXU_Q8MUL = 0x00, - OPC_MXU_Q8MULSU = 0x01, + OPC_MXU_Q8MULSU = 0x02, }; +/* + * MXU pool 20 + */ +enum { + OPC_MXU_Q8MOVZ = 0x00, + OPC_MXU_Q8MOVN = 0x01, + OPC_MXU_D16MOVZ = 0x02, + OPC_MXU_D16MOVN = 0x03, + OPC_MXU_S32MOVZ = 0x04, + OPC_MXU_S32MOVN = 0x05, +}; + +/* + * MXU pool 21 + */ +enum { + OPC_MXU_Q8MAC = 0x00, + OPC_MXU_Q8MACSU = 0x02, +}; + + /* MXU accumulate add/subtract 1-bit pattern 'aptn1' */ #define MXU_APTN1_A 0 #define MXU_APTN1_S 1 @@ -447,7 +609,7 @@ enum { static TCGv mxu_gpr[NUMBER_OF_MXU_REGISTERS - 1]; static TCGv mxu_CR; -static const char mxuregnames[][4] = { +static const char mxuregnames[NUMBER_OF_MXU_REGISTERS][4] = { "XR1", "XR2", "XR3", "XR4", "XR5", "XR6", "XR7", "XR8", "XR9", "XR10", "XR11", "XR12", "XR13", "XR14", "XR15", "XCR", }; @@ -455,12 +617,12 @@ static const char mxuregnames[][4] = { void mxu_translate_init(void) { for (unsigned i = 0; i < NUMBER_OF_MXU_REGISTERS - 1; i++) { - mxu_gpr[i] = tcg_global_mem_new(cpu_env, + mxu_gpr[i] = tcg_global_mem_new(tcg_env, offsetof(CPUMIPSState, active_tc.mxu_gpr[i]), mxuregnames[i]); } - mxu_CR = tcg_global_mem_new(cpu_env, + mxu_CR = tcg_global_mem_new(tcg_env, offsetof(CPUMIPSState, active_tc.mxu_cr), mxuregnames[NUMBER_OF_MXU_REGISTERS - 1]); } @@ -482,6 +644,16 @@ static inline void gen_store_mxu_gpr(TCGv t, unsigned int reg) } } +static inline void gen_extract_mxu_gpr(TCGv t, unsigned int reg, + unsigned int ofs, unsigned int len) +{ + if (reg == 0) { + tcg_gen_movi_tl(t, 0); + } else if (reg <= 15) { + tcg_gen_extract_tl(t, mxu_gpr[reg - 1], ofs, len); + } +} + /* MXU control register moves. */ static inline void gen_load_mxu_cr(TCGv t) { @@ -513,8 +685,6 @@ static void gen_mxu_s32i2m(DisasContext *ctx) } else if (XRa == 16) { gen_store_mxu_cr(t0); } - - tcg_temp_free(t0); } /* @@ -537,14 +707,15 @@ static void gen_mxu_s32m2i(DisasContext *ctx) } gen_store_gpr(t0, Rb); - - tcg_temp_free(t0); } /* * S8LDD XRa, Rb, s8, optn3 - Load a byte from memory to XRF + * + * S8LDI XRa, Rb, s8, optn3 - Load a byte from memory to XRF, + * post modify address register */ -static void gen_mxu_s8ldd(DisasContext *ctx) +static void gen_mxu_s8ldd(DisasContext *ctx, bool postmodify) { TCGv t0, t1; uint32_t XRa, Rb, s8, optn3; @@ -559,6 +730,9 @@ static void gen_mxu_s8ldd(DisasContext *ctx) gen_load_gpr(t0, Rb); tcg_gen_addi_tl(t0, t0, (int8_t)s8); + if (postmodify) { + gen_store_gpr(t0, Rb); + } switch (optn3) { /* XRa[7:0] = tmp8 */ @@ -613,15 +787,211 @@ static void gen_mxu_s8ldd(DisasContext *ctx) } gen_store_mxu_gpr(t0, XRa); - - tcg_temp_free(t0); - tcg_temp_free(t1); } /* - * D16MUL XRa, XRb, XRc, XRd, optn2 - Signed 16 bit pattern multiplication + * S8STD XRa, Rb, s8, optn3 - Store a byte from XRF to memory + * + * S8SDI XRa, Rb, s8, optn3 - Store a byte from XRF to memory, + * post modify address register */ -static void gen_mxu_d16mul(DisasContext *ctx) +static void gen_mxu_s8std(DisasContext *ctx, bool postmodify) +{ + TCGv t0, t1; + uint32_t XRa, Rb, s8, optn3; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + s8 = extract32(ctx->opcode, 10, 8); + optn3 = extract32(ctx->opcode, 18, 3); + Rb = extract32(ctx->opcode, 21, 5); + + if (optn3 > 3) { + /* reserved, do nothing */ + return; + } + + gen_load_gpr(t0, Rb); + tcg_gen_addi_tl(t0, t0, (int8_t)s8); + if (postmodify) { + gen_store_gpr(t0, Rb); + } + gen_load_mxu_gpr(t1, XRa); + + switch (optn3) { + /* XRa[7:0] => tmp8 */ + case MXU_OPTN3_PTN0: + tcg_gen_extract_tl(t1, t1, 0, 8); + break; + /* XRa[15:8] => tmp8 */ + case MXU_OPTN3_PTN1: + tcg_gen_extract_tl(t1, t1, 8, 8); + break; + /* XRa[23:16] => tmp8 */ + case MXU_OPTN3_PTN2: + tcg_gen_extract_tl(t1, t1, 16, 8); + break; + /* XRa[31:24] => tmp8 */ + case MXU_OPTN3_PTN3: + tcg_gen_extract_tl(t1, t1, 24, 8); + break; + } + + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_UB); +} + +/* + * S16LDD XRa, Rb, s10, optn2 - Load a halfword from memory to XRF + * + * S16LDI XRa, Rb, s10, optn2 - Load a halfword from memory to XRF, + * post modify address register + */ +static void gen_mxu_s16ldd(DisasContext *ctx, bool postmodify) +{ + TCGv t0, t1; + uint32_t XRa, Rb, optn2; + int32_t s10; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + s10 = sextract32(ctx->opcode, 10, 9) * 2; + optn2 = extract32(ctx->opcode, 19, 2); + Rb = extract32(ctx->opcode, 21, 5); + + gen_load_gpr(t0, Rb); + tcg_gen_addi_tl(t0, t0, s10); + if (postmodify) { + gen_store_gpr(t0, Rb); + } + + switch (optn2) { + /* XRa[15:0] = tmp16 */ + case MXU_OPTN2_PTN0: + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UW); + gen_load_mxu_gpr(t0, XRa); + tcg_gen_deposit_tl(t0, t0, t1, 0, 16); + break; + /* XRa[31:16] = tmp16 */ + case MXU_OPTN2_PTN1: + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UW); + gen_load_mxu_gpr(t0, XRa); + tcg_gen_deposit_tl(t0, t0, t1, 16, 16); + break; + /* XRa = sign_extend(tmp16) */ + case MXU_OPTN2_PTN2: + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_SW); + break; + /* XRa = {tmp16, tmp16} */ + case MXU_OPTN2_PTN3: + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UW); + tcg_gen_deposit_tl(t0, t1, t1, 0, 16); + tcg_gen_deposit_tl(t0, t1, t1, 16, 16); + break; + } + + gen_store_mxu_gpr(t0, XRa); +} + +/* + * S16STD XRa, Rb, s8, optn2 - Store a byte from XRF to memory + * + * S16SDI XRa, Rb, s8, optn2 - Store a byte from XRF to memory, + * post modify address register + */ +static void gen_mxu_s16std(DisasContext *ctx, bool postmodify) +{ + TCGv t0, t1; + uint32_t XRa, Rb, optn2; + int32_t s10; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + s10 = sextract32(ctx->opcode, 10, 9) * 2; + optn2 = extract32(ctx->opcode, 19, 2); + Rb = extract32(ctx->opcode, 21, 5); + + if (optn2 > 1) { + /* reserved, do nothing */ + return; + } + + gen_load_gpr(t0, Rb); + tcg_gen_addi_tl(t0, t0, s10); + if (postmodify) { + gen_store_gpr(t0, Rb); + } + gen_load_mxu_gpr(t1, XRa); + + switch (optn2) { + /* XRa[15:0] => tmp16 */ + case MXU_OPTN2_PTN0: + tcg_gen_extract_tl(t1, t1, 0, 16); + break; + /* XRa[31:16] => tmp16 */ + case MXU_OPTN2_PTN1: + tcg_gen_extract_tl(t1, t1, 16, 16); + break; + } + + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_UW); +} + +/* + * S32MUL XRa, XRd, rs, rt - Signed 32x32=>64 bit multiplication + * of GPR's and stores result into pair of MXU registers. + * It strains HI and LO registers. + * + * S32MULU XRa, XRd, rs, rt - Unsigned 32x32=>64 bit multiplication + * of GPR's and stores result into pair of MXU registers. + * It strains HI and LO registers. + */ +static void gen_mxu_s32mul(DisasContext *ctx, bool mulu) +{ + TCGv t0, t1; + uint32_t XRa, XRd, rs, rt; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + XRd = extract32(ctx->opcode, 10, 4); + rs = extract32(ctx->opcode, 16, 5); + rt = extract32(ctx->opcode, 21, 5); + + if (unlikely(rs == 0 || rt == 0)) { + tcg_gen_movi_tl(t0, 0); + tcg_gen_movi_tl(t1, 0); + } else { + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + + if (mulu) { + tcg_gen_mulu2_tl(t0, t1, t0, t1); + } else { + tcg_gen_muls2_tl(t0, t1, t0, t1); + } + } + tcg_gen_mov_tl(cpu_HI[0], t1); + tcg_gen_mov_tl(cpu_LO[0], t0); + gen_store_mxu_gpr(t1, XRa); + gen_store_mxu_gpr(t0, XRd); +} + +/* + * D16MUL XRa, XRb, XRc, XRd, optn2 - Signed 16 bit pattern multiplication + * D16MULF XRa, XRb, XRc, optn2 - Signed Q15 fraction pattern multiplication + * with rounding and packing result + * D16MULE XRa, XRb, XRc, XRd, optn2 - Signed Q15 fraction pattern + * multiplication with rounding + */ +static void gen_mxu_d16mul(DisasContext *ctx, bool fractional, + bool packed_result) { TCGv t0, t1, t2, t3; uint32_t XRa, XRb, XRc, XRd, optn2; @@ -637,6 +1007,12 @@ static void gen_mxu_d16mul(DisasContext *ctx) XRd = extract32(ctx->opcode, 18, 4); optn2 = extract32(ctx->opcode, 22, 2); + /* + * TODO: XRd field isn't used for D16MULF + * There's no knowledge how this field affect + * instruction decoding/behavior + */ + gen_load_mxu_gpr(t1, XRb); tcg_gen_sextract_tl(t0, t1, 0, 16); tcg_gen_sextract_tl(t1, t1, 16, 16); @@ -662,20 +1038,64 @@ static void gen_mxu_d16mul(DisasContext *ctx) tcg_gen_mul_tl(t2, t1, t2); break; } - gen_store_mxu_gpr(t3, XRa); - gen_store_mxu_gpr(t2, XRd); + if (fractional) { + TCGLabel *l_done = gen_new_label(); + TCGv rounding = tcg_temp_new(); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - tcg_temp_free(t3); + tcg_gen_shli_tl(t3, t3, 1); + tcg_gen_shli_tl(t2, t2, 1); + tcg_gen_andi_tl(rounding, mxu_CR, 0x2); + tcg_gen_brcondi_tl(TCG_COND_EQ, rounding, 0, l_done); + if (packed_result) { + TCGLabel *l_apply_bias_l = gen_new_label(); + TCGLabel *l_apply_bias_r = gen_new_label(); + TCGLabel *l_half_done = gen_new_label(); + TCGv bias = tcg_temp_new(); + + /* + * D16MULF supports unbiased rounding aka "bankers rounding", + * "round to even", "convergent rounding" + */ + tcg_gen_andi_tl(bias, mxu_CR, 0x4); + tcg_gen_brcondi_tl(TCG_COND_NE, bias, 0, l_apply_bias_l); + tcg_gen_andi_tl(t0, t3, 0x1ffff); + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0x8000, l_half_done); + gen_set_label(l_apply_bias_l); + tcg_gen_addi_tl(t3, t3, 0x8000); + gen_set_label(l_half_done); + tcg_gen_brcondi_tl(TCG_COND_NE, bias, 0, l_apply_bias_r); + tcg_gen_andi_tl(t0, t2, 0x1ffff); + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0x8000, l_done); + gen_set_label(l_apply_bias_r); + tcg_gen_addi_tl(t2, t2, 0x8000); + } else { + /* D16MULE doesn't support unbiased rounding */ + tcg_gen_addi_tl(t3, t3, 0x8000); + tcg_gen_addi_tl(t2, t2, 0x8000); + } + gen_set_label(l_done); + } + if (!packed_result) { + gen_store_mxu_gpr(t3, XRa); + gen_store_mxu_gpr(t2, XRd); + } else { + tcg_gen_andi_tl(t3, t3, 0xffff0000); + tcg_gen_shri_tl(t2, t2, 16); + tcg_gen_or_tl(t3, t3, t2); + gen_store_mxu_gpr(t3, XRa); + } } /* - * D16MAC XRa, XRb, XRc, XRd, aptn2, optn2 - Signed 16 bit pattern multiply - * and accumulate + * D16MAC XRa, XRb, XRc, XRd, aptn2, optn2 + * Signed 16 bit pattern multiply and accumulate + * D16MACF XRa, XRb, XRc, aptn2, optn2 + * Signed Q15 fraction pattern multiply accumulate and pack + * D16MACE XRa, XRb, XRc, XRd, aptn2, optn2 + * Signed Q15 fraction pattern multiply and accumulate */ -static void gen_mxu_d16mac(DisasContext *ctx) +static void gen_mxu_d16mac(DisasContext *ctx, bool fractional, + bool packed_result) { TCGv t0, t1, t2, t3; uint32_t XRa, XRb, XRc, XRd, optn2, aptn2; @@ -718,6 +1138,11 @@ static void gen_mxu_d16mac(DisasContext *ctx) tcg_gen_mul_tl(t2, t1, t2); break; } + + if (fractional) { + tcg_gen_shli_tl(t3, t3, 1); + tcg_gen_shli_tl(t2, t2, 1); + } gen_load_mxu_gpr(t0, XRa); gen_load_mxu_gpr(t1, XRd); @@ -739,23 +1164,205 @@ static void gen_mxu_d16mac(DisasContext *ctx) tcg_gen_sub_tl(t2, t1, t2); break; } - gen_store_mxu_gpr(t3, XRa); - gen_store_mxu_gpr(t2, XRd); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - tcg_temp_free(t3); + if (fractional) { + TCGLabel *l_done = gen_new_label(); + TCGv rounding = tcg_temp_new(); + + tcg_gen_andi_tl(rounding, mxu_CR, 0x2); + tcg_gen_brcondi_tl(TCG_COND_EQ, rounding, 0, l_done); + if (packed_result) { + TCGLabel *l_apply_bias_l = gen_new_label(); + TCGLabel *l_apply_bias_r = gen_new_label(); + TCGLabel *l_half_done = gen_new_label(); + TCGv bias = tcg_temp_new(); + + /* + * D16MACF supports unbiased rounding aka "bankers rounding", + * "round to even", "convergent rounding" + */ + tcg_gen_andi_tl(bias, mxu_CR, 0x4); + tcg_gen_brcondi_tl(TCG_COND_NE, bias, 0, l_apply_bias_l); + tcg_gen_andi_tl(t0, t3, 0x1ffff); + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0x8000, l_half_done); + gen_set_label(l_apply_bias_l); + tcg_gen_addi_tl(t3, t3, 0x8000); + gen_set_label(l_half_done); + tcg_gen_brcondi_tl(TCG_COND_NE, bias, 0, l_apply_bias_r); + tcg_gen_andi_tl(t0, t2, 0x1ffff); + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0x8000, l_done); + gen_set_label(l_apply_bias_r); + tcg_gen_addi_tl(t2, t2, 0x8000); + } else { + /* D16MACE doesn't support unbiased rounding */ + tcg_gen_addi_tl(t3, t3, 0x8000); + tcg_gen_addi_tl(t2, t2, 0x8000); + } + gen_set_label(l_done); + } + + if (!packed_result) { + gen_store_mxu_gpr(t3, XRa); + gen_store_mxu_gpr(t2, XRd); + } else { + tcg_gen_andi_tl(t3, t3, 0xffff0000); + tcg_gen_shri_tl(t2, t2, 16); + tcg_gen_or_tl(t3, t3, t2); + gen_store_mxu_gpr(t3, XRa); + } } /* - * Q8MUL XRa, XRb, XRc, XRd - Parallel unsigned 8 bit pattern multiply - * Q8MULSU XRa, XRb, XRc, XRd - Parallel signed 8 bit pattern multiply + * D16MADL XRa, XRb, XRc, XRd, aptn2, optn2 - Double packed + * unsigned 16 bit pattern multiply and add/subtract. */ -static void gen_mxu_q8mul_q8mulsu(DisasContext *ctx) +static void gen_mxu_d16madl(DisasContext *ctx) +{ + TCGv t0, t1, t2, t3; + uint32_t XRa, XRb, XRc, XRd, optn2, aptn2; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + t3 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRd = extract32(ctx->opcode, 18, 4); + optn2 = extract32(ctx->opcode, 22, 2); + aptn2 = extract32(ctx->opcode, 24, 2); + + gen_load_mxu_gpr(t1, XRb); + tcg_gen_sextract_tl(t0, t1, 0, 16); + tcg_gen_sextract_tl(t1, t1, 16, 16); + + gen_load_mxu_gpr(t3, XRc); + tcg_gen_sextract_tl(t2, t3, 0, 16); + tcg_gen_sextract_tl(t3, t3, 16, 16); + + switch (optn2) { + case MXU_OPTN2_WW: /* XRB.H*XRC.H == lop, XRB.L*XRC.L == rop */ + tcg_gen_mul_tl(t3, t1, t3); + tcg_gen_mul_tl(t2, t0, t2); + break; + case MXU_OPTN2_LW: /* XRB.L*XRC.H == lop, XRB.L*XRC.L == rop */ + tcg_gen_mul_tl(t3, t0, t3); + tcg_gen_mul_tl(t2, t0, t2); + break; + case MXU_OPTN2_HW: /* XRB.H*XRC.H == lop, XRB.H*XRC.L == rop */ + tcg_gen_mul_tl(t3, t1, t3); + tcg_gen_mul_tl(t2, t1, t2); + break; + case MXU_OPTN2_XW: /* XRB.L*XRC.H == lop, XRB.H*XRC.L == rop */ + tcg_gen_mul_tl(t3, t0, t3); + tcg_gen_mul_tl(t2, t1, t2); + break; + } + tcg_gen_extract_tl(t2, t2, 0, 16); + tcg_gen_extract_tl(t3, t3, 0, 16); + + gen_load_mxu_gpr(t1, XRa); + tcg_gen_extract_tl(t0, t1, 0, 16); + tcg_gen_extract_tl(t1, t1, 16, 16); + + switch (aptn2) { + case MXU_APTN2_AA: + tcg_gen_add_tl(t3, t1, t3); + tcg_gen_add_tl(t2, t0, t2); + break; + case MXU_APTN2_AS: + tcg_gen_add_tl(t3, t1, t3); + tcg_gen_sub_tl(t2, t0, t2); + break; + case MXU_APTN2_SA: + tcg_gen_sub_tl(t3, t1, t3); + tcg_gen_add_tl(t2, t0, t2); + break; + case MXU_APTN2_SS: + tcg_gen_sub_tl(t3, t1, t3); + tcg_gen_sub_tl(t2, t0, t2); + break; + } + + tcg_gen_andi_tl(t2, t2, 0xffff); + tcg_gen_shli_tl(t3, t3, 16); + tcg_gen_or_tl(mxu_gpr[XRd - 1], t3, t2); +} + +/* + * S16MAD XRa, XRb, XRc, XRd, aptn2, optn2 - Single packed + * signed 16 bit pattern multiply and 32-bit add/subtract. + */ +static void gen_mxu_s16mad(DisasContext *ctx) +{ + TCGv t0, t1; + uint32_t XRa, XRb, XRc, XRd, optn2, aptn1, pad; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRd = extract32(ctx->opcode, 18, 4); + optn2 = extract32(ctx->opcode, 22, 2); + aptn1 = extract32(ctx->opcode, 24, 1); + pad = extract32(ctx->opcode, 25, 1); + + if (pad) { + /* FIXME check if it influence the result */ + } + + gen_load_mxu_gpr(t0, XRb); + gen_load_mxu_gpr(t1, XRc); + + switch (optn2) { + case MXU_OPTN2_WW: /* XRB.H*XRC.H */ + tcg_gen_sextract_tl(t0, t0, 16, 16); + tcg_gen_sextract_tl(t1, t1, 16, 16); + break; + case MXU_OPTN2_LW: /* XRB.L*XRC.L */ + tcg_gen_sextract_tl(t0, t0, 0, 16); + tcg_gen_sextract_tl(t1, t1, 0, 16); + break; + case MXU_OPTN2_HW: /* XRB.H*XRC.L */ + tcg_gen_sextract_tl(t0, t0, 16, 16); + tcg_gen_sextract_tl(t1, t1, 0, 16); + break; + case MXU_OPTN2_XW: /* XRB.L*XRC.H */ + tcg_gen_sextract_tl(t0, t0, 0, 16); + tcg_gen_sextract_tl(t1, t1, 16, 16); + break; + } + tcg_gen_mul_tl(t0, t0, t1); + + gen_load_mxu_gpr(t1, XRa); + + switch (aptn1) { + case MXU_APTN1_A: + tcg_gen_add_tl(t1, t1, t0); + break; + case MXU_APTN1_S: + tcg_gen_sub_tl(t1, t1, t0); + break; + } + + gen_store_mxu_gpr(t1, XRd); +} + +/* + * Q8MUL XRa, XRb, XRc, XRd - Parallel quad unsigned 8 bit multiply + * Q8MULSU XRa, XRb, XRc, XRd - Parallel quad signed 8 bit multiply + * Q8MAC XRa, XRb, XRc, XRd - Parallel quad unsigned 8 bit multiply + * and accumulate + * Q8MACSU XRa, XRb, XRc, XRd - Parallel quad signed 8 bit multiply + * and accumulate + */ +static void gen_mxu_q8mul_mac(DisasContext *ctx, bool su, bool mac) { TCGv t0, t1, t2, t3, t4, t5, t6, t7; - uint32_t XRa, XRb, XRc, XRd, sel; + uint32_t XRa, XRb, XRc, XRd, aptn2; t0 = tcg_temp_new(); t1 = tcg_temp_new(); @@ -770,101 +1377,311 @@ static void gen_mxu_q8mul_q8mulsu(DisasContext *ctx) XRb = extract32(ctx->opcode, 10, 4); XRc = extract32(ctx->opcode, 14, 4); XRd = extract32(ctx->opcode, 18, 4); - sel = extract32(ctx->opcode, 22, 2); + aptn2 = extract32(ctx->opcode, 24, 2); gen_load_mxu_gpr(t3, XRb); gen_load_mxu_gpr(t7, XRc); - if (sel == 0x2) { - /* Q8MULSU */ - tcg_gen_ext8s_tl(t0, t3); - tcg_gen_shri_tl(t3, t3, 8); - tcg_gen_ext8s_tl(t1, t3); - tcg_gen_shri_tl(t3, t3, 8); - tcg_gen_ext8s_tl(t2, t3); - tcg_gen_shri_tl(t3, t3, 8); - tcg_gen_ext8s_tl(t3, t3); + if (su) { + /* Q8MULSU / Q8MACSU */ + tcg_gen_sextract_tl(t0, t3, 0, 8); + tcg_gen_sextract_tl(t1, t3, 8, 8); + tcg_gen_sextract_tl(t2, t3, 16, 8); + tcg_gen_sextract_tl(t3, t3, 24, 8); } else { - /* Q8MUL */ - tcg_gen_ext8u_tl(t0, t3); - tcg_gen_shri_tl(t3, t3, 8); - tcg_gen_ext8u_tl(t1, t3); - tcg_gen_shri_tl(t3, t3, 8); - tcg_gen_ext8u_tl(t2, t3); - tcg_gen_shri_tl(t3, t3, 8); - tcg_gen_ext8u_tl(t3, t3); + /* Q8MUL / Q8MAC */ + tcg_gen_extract_tl(t0, t3, 0, 8); + tcg_gen_extract_tl(t1, t3, 8, 8); + tcg_gen_extract_tl(t2, t3, 16, 8); + tcg_gen_extract_tl(t3, t3, 24, 8); } - tcg_gen_ext8u_tl(t4, t7); - tcg_gen_shri_tl(t7, t7, 8); - tcg_gen_ext8u_tl(t5, t7); - tcg_gen_shri_tl(t7, t7, 8); - tcg_gen_ext8u_tl(t6, t7); - tcg_gen_shri_tl(t7, t7, 8); - tcg_gen_ext8u_tl(t7, t7); + tcg_gen_extract_tl(t4, t7, 0, 8); + tcg_gen_extract_tl(t5, t7, 8, 8); + tcg_gen_extract_tl(t6, t7, 16, 8); + tcg_gen_extract_tl(t7, t7, 24, 8); tcg_gen_mul_tl(t0, t0, t4); tcg_gen_mul_tl(t1, t1, t5); tcg_gen_mul_tl(t2, t2, t6); tcg_gen_mul_tl(t3, t3, t7); - tcg_gen_andi_tl(t0, t0, 0xFFFF); - tcg_gen_andi_tl(t1, t1, 0xFFFF); - tcg_gen_andi_tl(t2, t2, 0xFFFF); - tcg_gen_andi_tl(t3, t3, 0xFFFF); + if (mac) { + gen_load_mxu_gpr(t4, XRd); + gen_load_mxu_gpr(t5, XRa); + tcg_gen_extract_tl(t6, t4, 0, 16); + tcg_gen_extract_tl(t7, t4, 16, 16); + if (aptn2 & 1) { + tcg_gen_sub_tl(t0, t6, t0); + tcg_gen_sub_tl(t1, t7, t1); + } else { + tcg_gen_add_tl(t0, t6, t0); + tcg_gen_add_tl(t1, t7, t1); + } + tcg_gen_extract_tl(t6, t5, 0, 16); + tcg_gen_extract_tl(t7, t5, 16, 16); + if (aptn2 & 2) { + tcg_gen_sub_tl(t2, t6, t2); + tcg_gen_sub_tl(t3, t7, t3); + } else { + tcg_gen_add_tl(t2, t6, t2); + tcg_gen_add_tl(t3, t7, t3); + } + } - tcg_gen_shli_tl(t1, t1, 16); - tcg_gen_shli_tl(t3, t3, 16); - - tcg_gen_or_tl(t0, t0, t1); - tcg_gen_or_tl(t1, t2, t3); + tcg_gen_deposit_tl(t0, t0, t1, 16, 16); + tcg_gen_deposit_tl(t1, t2, t3, 16, 16); gen_store_mxu_gpr(t0, XRd); gen_store_mxu_gpr(t1, XRa); +} - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - tcg_temp_free(t3); - tcg_temp_free(t4); - tcg_temp_free(t5); - tcg_temp_free(t6); - tcg_temp_free(t7); +/* + * Q8MADL XRd, XRa, XRb, XRc + * Parallel quad unsigned 8 bit multiply and accumulate. + * e.g. XRd[0..3] = XRa[0..3] + XRb[0..3] * XRc[0..3] + */ +static void gen_mxu_q8madl(DisasContext *ctx) +{ + TCGv t0, t1, t2, t3, t4, t5, t6, t7; + uint32_t XRa, XRb, XRc, XRd, aptn2; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + t3 = tcg_temp_new(); + t4 = tcg_temp_new(); + t5 = tcg_temp_new(); + t6 = tcg_temp_new(); + t7 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRd = extract32(ctx->opcode, 18, 4); + aptn2 = extract32(ctx->opcode, 24, 2); + + gen_load_mxu_gpr(t3, XRb); + gen_load_mxu_gpr(t7, XRc); + + tcg_gen_extract_tl(t0, t3, 0, 8); + tcg_gen_extract_tl(t1, t3, 8, 8); + tcg_gen_extract_tl(t2, t3, 16, 8); + tcg_gen_extract_tl(t3, t3, 24, 8); + + tcg_gen_extract_tl(t4, t7, 0, 8); + tcg_gen_extract_tl(t5, t7, 8, 8); + tcg_gen_extract_tl(t6, t7, 16, 8); + tcg_gen_extract_tl(t7, t7, 24, 8); + + tcg_gen_mul_tl(t0, t0, t4); + tcg_gen_mul_tl(t1, t1, t5); + tcg_gen_mul_tl(t2, t2, t6); + tcg_gen_mul_tl(t3, t3, t7); + + gen_load_mxu_gpr(t4, XRa); + tcg_gen_extract_tl(t6, t4, 0, 8); + tcg_gen_extract_tl(t7, t4, 8, 8); + if (aptn2 & 1) { + tcg_gen_sub_tl(t0, t6, t0); + tcg_gen_sub_tl(t1, t7, t1); + } else { + tcg_gen_add_tl(t0, t6, t0); + tcg_gen_add_tl(t1, t7, t1); + } + tcg_gen_extract_tl(t6, t4, 16, 8); + tcg_gen_extract_tl(t7, t4, 24, 8); + if (aptn2 & 2) { + tcg_gen_sub_tl(t2, t6, t2); + tcg_gen_sub_tl(t3, t7, t3); + } else { + tcg_gen_add_tl(t2, t6, t2); + tcg_gen_add_tl(t3, t7, t3); + } + + tcg_gen_andi_tl(t5, t0, 0xff); + tcg_gen_deposit_tl(t5, t5, t1, 8, 8); + tcg_gen_deposit_tl(t5, t5, t2, 16, 8); + tcg_gen_deposit_tl(t5, t5, t3, 24, 8); + + gen_store_mxu_gpr(t5, XRd); } /* * S32LDD XRa, Rb, S12 - Load a word from memory to XRF - * S32LDDR XRa, Rb, S12 - Load a word from memory to XRF, reversed byte seq. + * S32LDDR XRa, Rb, S12 - Load a word from memory to XRF + * in reversed byte seq. + * S32LDI XRa, Rb, S12 - Load a word from memory to XRF, + * post modify base address GPR. + * S32LDIR XRa, Rb, S12 - Load a word from memory to XRF, + * post modify base address GPR and load in reversed byte seq. */ -static void gen_mxu_s32ldd_s32lddr(DisasContext *ctx) +static void gen_mxu_s32ldxx(DisasContext *ctx, bool reversed, bool postinc) { TCGv t0, t1; - uint32_t XRa, Rb, s12, sel; + uint32_t XRa, Rb, s12; t0 = tcg_temp_new(); t1 = tcg_temp_new(); XRa = extract32(ctx->opcode, 6, 4); - s12 = extract32(ctx->opcode, 10, 10); - sel = extract32(ctx->opcode, 20, 1); + s12 = sextract32(ctx->opcode, 10, 10); Rb = extract32(ctx->opcode, 21, 5); gen_load_gpr(t0, Rb); + tcg_gen_movi_tl(t1, s12 * 4); + tcg_gen_add_tl(t0, t0, t1); - tcg_gen_movi_tl(t1, s12); - tcg_gen_shli_tl(t1, t1, 2); - if (s12 & 0x200) { - tcg_gen_ori_tl(t1, t1, 0xFFFFF000); - } - tcg_gen_add_tl(t1, t0, t1); - tcg_gen_qemu_ld_tl(t1, t1, ctx->mem_idx, MO_TESL ^ (sel * MO_BSWAP)); - + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, + (MO_TESL ^ (reversed ? MO_BSWAP : 0)) | + ctx->default_tcg_memop_mask); gen_store_mxu_gpr(t1, XRa); - tcg_temp_free(t0); - tcg_temp_free(t1); + if (postinc) { + gen_store_gpr(t0, Rb); + } } +/* + * S32STD XRa, Rb, S12 - Store a word from XRF to memory + * S32STDR XRa, Rb, S12 - Store a word from XRF to memory + * in reversed byte seq. + * S32SDI XRa, Rb, S12 - Store a word from XRF to memory, + * post modify base address GPR. + * S32SDIR XRa, Rb, S12 - Store a word from XRF to memory, + * post modify base address GPR and store in reversed byte seq. + */ +static void gen_mxu_s32stxx(DisasContext *ctx, bool reversed, bool postinc) +{ + TCGv t0, t1; + uint32_t XRa, Rb, s12; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + s12 = sextract32(ctx->opcode, 10, 10); + Rb = extract32(ctx->opcode, 21, 5); + + gen_load_gpr(t0, Rb); + tcg_gen_movi_tl(t1, s12 * 4); + tcg_gen_add_tl(t0, t0, t1); + + gen_load_mxu_gpr(t1, XRa); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, + (MO_TESL ^ (reversed ? MO_BSWAP : 0)) | + ctx->default_tcg_memop_mask); + + if (postinc) { + gen_store_gpr(t0, Rb); + } +} + +/* + * S32LDDV XRa, Rb, Rc, STRD2 - Load a word from memory to XRF + * S32LDDVR XRa, Rb, Rc, STRD2 - Load a word from memory to XRF + * in reversed byte seq. + * S32LDIV XRa, Rb, Rc, STRD2 - Load a word from memory to XRF, + * post modify base address GPR. + * S32LDIVR XRa, Rb, Rc, STRD2 - Load a word from memory to XRF, + * post modify base address GPR and load in reversed byte seq. + */ +static void gen_mxu_s32ldxvx(DisasContext *ctx, bool reversed, + bool postinc, uint32_t strd2) +{ + TCGv t0, t1; + uint32_t XRa, Rb, Rc; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + Rc = extract32(ctx->opcode, 16, 5); + Rb = extract32(ctx->opcode, 21, 5); + + gen_load_gpr(t0, Rb); + gen_load_gpr(t1, Rc); + tcg_gen_shli_tl(t1, t1, strd2); + tcg_gen_add_tl(t0, t0, t1); + + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, + (MO_TESL ^ (reversed ? MO_BSWAP : 0)) | + ctx->default_tcg_memop_mask); + gen_store_mxu_gpr(t1, XRa); + + if (postinc) { + gen_store_gpr(t0, Rb); + } +} + +/* + * LXW Ra, Rb, Rc, STRD2 - Load a word from memory to GPR + * LXB Ra, Rb, Rc, STRD2 - Load a byte from memory to GPR, + * sign extending to GPR size. + * LXH Ra, Rb, Rc, STRD2 - Load a byte from memory to GPR, + * sign extending to GPR size. + * LXBU Ra, Rb, Rc, STRD2 - Load a halfword from memory to GPR, + * zero extending to GPR size. + * LXHU Ra, Rb, Rc, STRD2 - Load a halfword from memory to GPR, + * zero extending to GPR size. + */ +static void gen_mxu_lxx(DisasContext *ctx, uint32_t strd2, MemOp mop) +{ + TCGv t0, t1; + uint32_t Ra, Rb, Rc; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + Ra = extract32(ctx->opcode, 11, 5); + Rc = extract32(ctx->opcode, 16, 5); + Rb = extract32(ctx->opcode, 21, 5); + + gen_load_gpr(t0, Rb); + gen_load_gpr(t1, Rc); + tcg_gen_shli_tl(t1, t1, strd2); + tcg_gen_add_tl(t0, t0, t1); + + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, mop | ctx->default_tcg_memop_mask); + gen_store_gpr(t1, Ra); +} + +/* + * S32STDV XRa, Rb, Rc, STRD2 - Load a word from memory to XRF + * S32STDVR XRa, Rb, Rc, STRD2 - Load a word from memory to XRF + * in reversed byte seq. + * S32SDIV XRa, Rb, Rc, STRD2 - Load a word from memory to XRF, + * post modify base address GPR. + * S32SDIVR XRa, Rb, Rc, STRD2 - Load a word from memory to XRF, + * post modify base address GPR and store in reversed byte seq. + */ +static void gen_mxu_s32stxvx(DisasContext *ctx, bool reversed, + bool postinc, uint32_t strd2) +{ + TCGv t0, t1; + uint32_t XRa, Rb, Rc; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + Rc = extract32(ctx->opcode, 16, 5); + Rb = extract32(ctx->opcode, 21, 5); + + gen_load_gpr(t0, Rb); + gen_load_gpr(t1, Rc); + tcg_gen_shli_tl(t1, t1, strd2); + tcg_gen_add_tl(t0, t0, t1); + + gen_load_mxu_gpr(t1, XRa); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, + (MO_TESL ^ (reversed ? MO_BSWAP : 0)) | + ctx->default_tcg_memop_mask); + + if (postinc) { + gen_store_gpr(t0, Rb); + } +} /* * MXU instruction category: logic @@ -1011,13 +1828,291 @@ static void gen_mxu_S32XOR(DisasContext *ctx) } } +/* + * MXU instruction category: shift + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * D32SLL D32SLR D32SAR D32SARL + * D32SLLV D32SLRV D32SARV D32SARW + * Q16SLL Q16SLR Q16SAR + * Q16SLLV Q16SLRV Q16SARV + */ /* - * MXU instruction category max/min + * D32SLL XRa, XRd, XRb, XRc, SFT4 + * Dual 32-bit shift left from XRb and XRc to SFT4 + * bits (0..15). Store to XRa and XRd respectively. + * D32SLR XRa, XRd, XRb, XRc, SFT4 + * Dual 32-bit shift logic right from XRb and XRc + * to SFT4 bits (0..15). Store to XRa and XRd respectively. + * D32SAR XRa, XRd, XRb, XRc, SFT4 + * Dual 32-bit shift arithmetic right from XRb and XRc + * to SFT4 bits (0..15). Store to XRa and XRd respectively. + */ +static void gen_mxu_d32sxx(DisasContext *ctx, bool right, bool arithmetic) +{ + uint32_t XRa, XRb, XRc, XRd, sft4; + + XRa = extract32(ctx->opcode, 6, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRd = extract32(ctx->opcode, 18, 4); + sft4 = extract32(ctx->opcode, 22, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + gen_load_mxu_gpr(t0, XRb); + gen_load_mxu_gpr(t1, XRc); + + if (right) { + if (arithmetic) { + tcg_gen_sari_tl(t0, t0, sft4); + tcg_gen_sari_tl(t1, t1, sft4); + } else { + tcg_gen_shri_tl(t0, t0, sft4); + tcg_gen_shri_tl(t1, t1, sft4); + } + } else { + tcg_gen_shli_tl(t0, t0, sft4); + tcg_gen_shli_tl(t1, t1, sft4); + } + gen_store_mxu_gpr(t0, XRa); + gen_store_mxu_gpr(t1, XRd); +} + +/* + * D32SLLV XRa, XRd, rs + * Dual 32-bit shift left from XRa and XRd to rs[3:0] + * bits. Store back to XRa and XRd respectively. + * D32SLRV XRa, XRd, rs + * Dual 32-bit shift logic right from XRa and XRd to rs[3:0] + * bits. Store back to XRa and XRd respectively. + * D32SARV XRa, XRd, rs + * Dual 32-bit shift arithmetic right from XRa and XRd to rs[3:0] + * bits. Store back to XRa and XRd respectively. + */ +static void gen_mxu_d32sxxv(DisasContext *ctx, bool right, bool arithmetic) +{ + uint32_t XRa, XRd, rs; + + XRa = extract32(ctx->opcode, 10, 4); + XRd = extract32(ctx->opcode, 14, 4); + rs = extract32(ctx->opcode, 21, 5); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + gen_load_mxu_gpr(t0, XRa); + gen_load_mxu_gpr(t1, XRd); + gen_load_gpr(t2, rs); + tcg_gen_andi_tl(t2, t2, 0x0f); + + if (right) { + if (arithmetic) { + tcg_gen_sar_tl(t0, t0, t2); + tcg_gen_sar_tl(t1, t1, t2); + } else { + tcg_gen_shr_tl(t0, t0, t2); + tcg_gen_shr_tl(t1, t1, t2); + } + } else { + tcg_gen_shl_tl(t0, t0, t2); + tcg_gen_shl_tl(t1, t1, t2); + } + gen_store_mxu_gpr(t0, XRa); + gen_store_mxu_gpr(t1, XRd); +} + +/* + * D32SARL XRa, XRb, XRc, SFT4 + * Dual shift arithmetic right 32-bit integers in XRb and XRc + * to SFT4 bits (0..15). Pack 16 LSBs of each into XRa. + * + * D32SARW XRa, XRb, XRc, rb + * Dual shift arithmetic right 32-bit integers in XRb and XRc + * to rb[3:0] bits. Pack 16 LSBs of each into XRa. + */ +static void gen_mxu_d32sarl(DisasContext *ctx, bool sarw) +{ + uint32_t XRa, XRb, XRc, rb; + + XRa = extract32(ctx->opcode, 6, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRc = extract32(ctx->opcode, 14, 4); + rb = extract32(ctx->opcode, 21, 5); + + if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else { + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + if (!sarw) { + /* Make SFT4 from rb field */ + tcg_gen_movi_tl(t2, rb >> 1); + } else { + gen_load_gpr(t2, rb); + tcg_gen_andi_tl(t2, t2, 0x0f); + } + gen_load_mxu_gpr(t0, XRb); + gen_load_mxu_gpr(t1, XRc); + tcg_gen_sar_tl(t0, t0, t2); + tcg_gen_sar_tl(t1, t1, t2); + tcg_gen_extract_tl(t2, t1, 0, 16); + tcg_gen_deposit_tl(t2, t2, t0, 16, 16); + gen_store_mxu_gpr(t2, XRa); + } +} + +/* + * Q16SLL XRa, XRd, XRb, XRc, SFT4 + * Quad 16-bit shift left from XRb and XRc to SFT4 + * bits (0..15). Store to XRa and XRd respectively. + * Q16SLR XRa, XRd, XRb, XRc, SFT4 + * Quad 16-bit shift logic right from XRb and XRc + * to SFT4 bits (0..15). Store to XRa and XRd respectively. + * Q16SAR XRa, XRd, XRb, XRc, SFT4 + * Quad 16-bit shift arithmetic right from XRb and XRc + * to SFT4 bits (0..15). Store to XRa and XRd respectively. + */ +static void gen_mxu_q16sxx(DisasContext *ctx, bool right, bool arithmetic) +{ + uint32_t XRa, XRb, XRc, XRd, sft4; + + XRa = extract32(ctx->opcode, 6, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRd = extract32(ctx->opcode, 18, 4); + sft4 = extract32(ctx->opcode, 22, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + + gen_load_mxu_gpr(t0, XRb); + gen_load_mxu_gpr(t2, XRc); + + if (arithmetic) { + tcg_gen_sextract_tl(t1, t0, 16, 16); + tcg_gen_sextract_tl(t0, t0, 0, 16); + tcg_gen_sextract_tl(t3, t2, 16, 16); + tcg_gen_sextract_tl(t2, t2, 0, 16); + } else { + tcg_gen_extract_tl(t1, t0, 16, 16); + tcg_gen_extract_tl(t0, t0, 0, 16); + tcg_gen_extract_tl(t3, t2, 16, 16); + tcg_gen_extract_tl(t2, t2, 0, 16); + } + + if (right) { + if (arithmetic) { + tcg_gen_sari_tl(t0, t0, sft4); + tcg_gen_sari_tl(t1, t1, sft4); + tcg_gen_sari_tl(t2, t2, sft4); + tcg_gen_sari_tl(t3, t3, sft4); + } else { + tcg_gen_shri_tl(t0, t0, sft4); + tcg_gen_shri_tl(t1, t1, sft4); + tcg_gen_shri_tl(t2, t2, sft4); + tcg_gen_shri_tl(t3, t3, sft4); + } + } else { + tcg_gen_shli_tl(t0, t0, sft4); + tcg_gen_shli_tl(t1, t1, sft4); + tcg_gen_shli_tl(t2, t2, sft4); + tcg_gen_shli_tl(t3, t3, sft4); + } + tcg_gen_deposit_tl(t0, t0, t1, 16, 16); + tcg_gen_deposit_tl(t2, t2, t3, 16, 16); + + gen_store_mxu_gpr(t0, XRa); + gen_store_mxu_gpr(t2, XRd); +} + +/* + * Q16SLLV XRa, XRd, rs + * Quad 16-bit shift left from XRa and XRd to rs[3:0] + * bits. Store to XRa and XRd respectively. + * Q16SLRV XRa, XRd, rs + * Quad 16-bit shift logic right from XRa and XRd to rs[3:0] + * bits. Store to XRa and XRd respectively. + * Q16SARV XRa, XRd, rs + * Quad 16-bit shift arithmetic right from XRa and XRd to rs[3:0] + * bits. Store to XRa and XRd respectively. + */ +static void gen_mxu_q16sxxv(DisasContext *ctx, bool right, bool arithmetic) +{ + uint32_t XRa, XRd, rs; + + XRa = extract32(ctx->opcode, 10, 4); + XRd = extract32(ctx->opcode, 14, 4); + rs = extract32(ctx->opcode, 21, 5); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t5 = tcg_temp_new(); + + gen_load_mxu_gpr(t0, XRa); + gen_load_mxu_gpr(t2, XRd); + gen_load_gpr(t5, rs); + tcg_gen_andi_tl(t5, t5, 0x0f); + + + if (arithmetic) { + tcg_gen_sextract_tl(t1, t0, 16, 16); + tcg_gen_sextract_tl(t0, t0, 0, 16); + tcg_gen_sextract_tl(t3, t2, 16, 16); + tcg_gen_sextract_tl(t2, t2, 0, 16); + } else { + tcg_gen_extract_tl(t1, t0, 16, 16); + tcg_gen_extract_tl(t0, t0, 0, 16); + tcg_gen_extract_tl(t3, t2, 16, 16); + tcg_gen_extract_tl(t2, t2, 0, 16); + } + + if (right) { + if (arithmetic) { + tcg_gen_sar_tl(t0, t0, t5); + tcg_gen_sar_tl(t1, t1, t5); + tcg_gen_sar_tl(t2, t2, t5); + tcg_gen_sar_tl(t3, t3, t5); + } else { + tcg_gen_shr_tl(t0, t0, t5); + tcg_gen_shr_tl(t1, t1, t5); + tcg_gen_shr_tl(t2, t2, t5); + tcg_gen_shr_tl(t3, t3, t5); + } + } else { + tcg_gen_shl_tl(t0, t0, t5); + tcg_gen_shl_tl(t1, t1, t5); + tcg_gen_shl_tl(t2, t2, t5); + tcg_gen_shl_tl(t3, t3, t5); + } + tcg_gen_deposit_tl(t0, t0, t1, 16, 16); + tcg_gen_deposit_tl(t2, t2, t3, 16, 16); + + gen_store_mxu_gpr(t0, XRa); + gen_store_mxu_gpr(t2, XRd); +} + +/* + * MXU instruction category max/min/avg * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * S32MAX D16MAX Q8MAX * S32MIN D16MIN Q8MIN + * S32SLT D16SLT Q8SLT + * Q8SLTU + * D16AVG Q8AVG + * D16AVGR Q8AVGR + * S32MOVZ D16MOVZ Q8MOVZ + * S32MOVN D16MOVN Q8MOVN */ /* @@ -1101,14 +2196,15 @@ static void gen_mxu_D16MAX_D16MIN(DisasContext *ctx) uint32_t XRx = XRb ? XRb : XRc; /* ...and do half-word-wise max/min with one operand 0 */ TCGv_i32 t0 = tcg_temp_new(); - TCGv_i32 t1 = tcg_const_i32(0); + TCGv_i32 t1 = tcg_constant_i32(0); + TCGv_i32 t2 = tcg_temp_new(); /* the left half-word first */ tcg_gen_andi_i32(t0, mxu_gpr[XRx - 1], 0xFFFF0000); if (opc == OPC_MXU_D16MAX) { - tcg_gen_smax_i32(mxu_gpr[XRa - 1], t0, t1); + tcg_gen_smax_i32(t2, t0, t1); } else { - tcg_gen_smin_i32(mxu_gpr[XRa - 1], t0, t1); + tcg_gen_smin_i32(t2, t0, t1); } /* the right half-word */ @@ -1124,10 +2220,7 @@ static void gen_mxu_D16MAX_D16MIN(DisasContext *ctx) /* return resulting half-words to its original position */ tcg_gen_shri_i32(t0, t0, 16); /* finally update the destination */ - tcg_gen_or_i32(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0); - - tcg_temp_free(t1); - tcg_temp_free(t0); + tcg_gen_or_i32(mxu_gpr[XRa - 1], t2, t0); } else if (unlikely(XRb == XRc)) { /* both operands same -> just set destination to one of them */ tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); @@ -1135,14 +2228,15 @@ static void gen_mxu_D16MAX_D16MIN(DisasContext *ctx) /* the most general case */ TCGv_i32 t0 = tcg_temp_new(); TCGv_i32 t1 = tcg_temp_new(); + TCGv_i32 t2 = tcg_temp_new(); /* the left half-word first */ tcg_gen_andi_i32(t0, mxu_gpr[XRb - 1], 0xFFFF0000); tcg_gen_andi_i32(t1, mxu_gpr[XRc - 1], 0xFFFF0000); if (opc == OPC_MXU_D16MAX) { - tcg_gen_smax_i32(mxu_gpr[XRa - 1], t0, t1); + tcg_gen_smax_i32(t2, t0, t1); } else { - tcg_gen_smin_i32(mxu_gpr[XRa - 1], t0, t1); + tcg_gen_smin_i32(t2, t0, t1); } /* the right half-word */ @@ -1160,10 +2254,7 @@ static void gen_mxu_D16MAX_D16MIN(DisasContext *ctx) /* return resulting half-words to its original position */ tcg_gen_shri_i32(t0, t0, 16); /* finally update the destination */ - tcg_gen_or_i32(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0); - - tcg_temp_free(t1); - tcg_temp_free(t0); + tcg_gen_or_i32(mxu_gpr[XRa - 1], t2, t0); } } @@ -1198,15 +2289,16 @@ static void gen_mxu_Q8MAX_Q8MIN(DisasContext *ctx) uint32_t XRx = XRb ? XRb : XRc; /* ...and do byte-wise max/min with one operand 0 */ TCGv_i32 t0 = tcg_temp_new(); - TCGv_i32 t1 = tcg_const_i32(0); + TCGv_i32 t1 = tcg_constant_i32(0); + TCGv_i32 t2 = tcg_temp_new(); int32_t i; /* the leftmost byte (byte 3) first */ tcg_gen_andi_i32(t0, mxu_gpr[XRx - 1], 0xFF000000); if (opc == OPC_MXU_Q8MAX) { - tcg_gen_smax_i32(mxu_gpr[XRa - 1], t0, t1); + tcg_gen_smax_i32(t2, t0, t1); } else { - tcg_gen_smin_i32(mxu_gpr[XRa - 1], t0, t1); + tcg_gen_smin_i32(t2, t0, t1); } /* bytes 2, 1, 0 */ @@ -1224,11 +2316,9 @@ static void gen_mxu_Q8MAX_Q8MIN(DisasContext *ctx) /* return resulting byte to its original position */ tcg_gen_shri_i32(t0, t0, 8 * (3 - i)); /* finally update the destination */ - tcg_gen_or_i32(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0); + tcg_gen_or_i32(t2, t2, t0); } - - tcg_temp_free(t1); - tcg_temp_free(t0); + gen_store_mxu_gpr(t2, XRa); } else if (unlikely(XRb == XRc)) { /* both operands same -> just set destination to one of them */ tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); @@ -1236,15 +2326,16 @@ static void gen_mxu_Q8MAX_Q8MIN(DisasContext *ctx) /* the most general case */ TCGv_i32 t0 = tcg_temp_new(); TCGv_i32 t1 = tcg_temp_new(); + TCGv_i32 t2 = tcg_temp_new(); int32_t i; /* the leftmost bytes (bytes 3) first */ tcg_gen_andi_i32(t0, mxu_gpr[XRb - 1], 0xFF000000); tcg_gen_andi_i32(t1, mxu_gpr[XRc - 1], 0xFF000000); if (opc == OPC_MXU_Q8MAX) { - tcg_gen_smax_i32(mxu_gpr[XRa - 1], t0, t1); + tcg_gen_smax_i32(t2, t0, t1); } else { - tcg_gen_smin_i32(mxu_gpr[XRa - 1], t0, t1); + tcg_gen_smin_i32(t2, t0, t1); } /* bytes 2, 1, 0 */ @@ -1264,14 +2355,1745 @@ static void gen_mxu_Q8MAX_Q8MIN(DisasContext *ctx) /* return resulting byte to its original position */ tcg_gen_shri_i32(t0, t0, 8 * (3 - i)); /* finally update the destination */ - tcg_gen_or_i32(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0); + tcg_gen_or_i32(t2, t2, t0); } - - tcg_temp_free(t1); - tcg_temp_free(t0); + gen_store_mxu_gpr(t2, XRa); } } +/* + * Q8SLT + * Update XRa with the signed "set less than" comparison of XRb and XRc + * on per-byte basis. + * a.k.a. XRa[0..3] = XRb[0..3] < XRc[0..3] ? 1 : 0; + * + * Q8SLTU + * Update XRa with the unsigned "set less than" comparison of XRb and XRc + * on per-byte basis. + * a.k.a. XRa[0..3] = XRb[0..3] < XRc[0..3] ? 1 : 0; + */ +static void gen_mxu_q8slt(DisasContext *ctx, bool sltu) +{ + uint32_t pad, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 5); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to zero */ + tcg_gen_movi_tl(mxu_gpr[XRa - 1], 0); + } else if (unlikely(XRb == XRc)) { + /* both operands same registers -> just set destination to zero */ + tcg_gen_movi_tl(mxu_gpr[XRa - 1], 0); + } else { + /* the most general case */ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + + gen_load_mxu_gpr(t3, XRb); + gen_load_mxu_gpr(t4, XRc); + tcg_gen_movi_tl(t2, 0); + + for (int i = 0; i < 4; i++) { + if (sltu) { + tcg_gen_extract_tl(t0, t3, 8 * i, 8); + tcg_gen_extract_tl(t1, t4, 8 * i, 8); + } else { + tcg_gen_sextract_tl(t0, t3, 8 * i, 8); + tcg_gen_sextract_tl(t1, t4, 8 * i, 8); + } + tcg_gen_setcond_tl(TCG_COND_LT, t0, t0, t1); + tcg_gen_deposit_tl(t2, t2, t0, 8 * i, 8); + } + gen_store_mxu_gpr(t2, XRa); + } +} + +/* + * S32SLT + * Update XRa with the signed "set less than" comparison of XRb and XRc. + * a.k.a. XRa = XRb < XRc ? 1 : 0; + */ +static void gen_mxu_S32SLT(DisasContext *ctx) +{ + uint32_t pad, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 5); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to zero */ + tcg_gen_movi_tl(mxu_gpr[XRa - 1], 0); + } else if (unlikely(XRb == XRc)) { + /* both operands same registers -> just set destination to zero */ + tcg_gen_movi_tl(mxu_gpr[XRa - 1], 0); + } else { + /* the most general case */ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + gen_load_mxu_gpr(t0, XRb); + gen_load_mxu_gpr(t1, XRc); + tcg_gen_setcond_tl(TCG_COND_LT, mxu_gpr[XRa - 1], t0, t1); + } +} + +/* + * D16SLT + * Update XRa with the signed "set less than" comparison of XRb and XRc + * on per-word basis. + * a.k.a. XRa[0..1] = XRb[0..1] < XRc[0..1] ? 1 : 0; + */ +static void gen_mxu_D16SLT(DisasContext *ctx) +{ + uint32_t pad, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 5); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to zero */ + tcg_gen_movi_tl(mxu_gpr[XRa - 1], 0); + } else if (unlikely(XRb == XRc)) { + /* both operands same registers -> just set destination to zero */ + tcg_gen_movi_tl(mxu_gpr[XRa - 1], 0); + } else { + /* the most general case */ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + + gen_load_mxu_gpr(t3, XRb); + gen_load_mxu_gpr(t4, XRc); + tcg_gen_sextract_tl(t0, t3, 16, 16); + tcg_gen_sextract_tl(t1, t4, 16, 16); + tcg_gen_setcond_tl(TCG_COND_LT, t0, t0, t1); + tcg_gen_shli_tl(t2, t0, 16); + tcg_gen_sextract_tl(t0, t3, 0, 16); + tcg_gen_sextract_tl(t1, t4, 0, 16); + tcg_gen_setcond_tl(TCG_COND_LT, t0, t0, t1); + tcg_gen_or_tl(mxu_gpr[XRa - 1], t2, t0); + } +} + +/* + * D16AVG + * Update XRa with the signed average of XRb and XRc + * on per-word basis, rounding down. + * a.k.a. XRa[0..1] = (XRb[0..1] + XRc[0..1]) >> 1; + * + * D16AVGR + * Update XRa with the signed average of XRb and XRc + * on per-word basis, math rounding 4/5. + * a.k.a. XRa[0..1] = (XRb[0..1] + XRc[0..1] + 1) >> 1; + */ +static void gen_mxu_d16avg(DisasContext *ctx, bool round45) +{ + uint32_t pad, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 5); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to zero */ + tcg_gen_movi_tl(mxu_gpr[XRa - 1], 0); + } else if (unlikely(XRb == XRc)) { + /* both operands same registers -> just set destination to same */ + tcg_gen_mov_tl(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + } else { + /* the most general case */ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + + gen_load_mxu_gpr(t3, XRb); + gen_load_mxu_gpr(t4, XRc); + tcg_gen_sextract_tl(t0, t3, 16, 16); + tcg_gen_sextract_tl(t1, t4, 16, 16); + tcg_gen_add_tl(t0, t0, t1); + if (round45) { + tcg_gen_addi_tl(t0, t0, 1); + } + tcg_gen_shli_tl(t2, t0, 15); + tcg_gen_andi_tl(t2, t2, 0xffff0000); + tcg_gen_sextract_tl(t0, t3, 0, 16); + tcg_gen_sextract_tl(t1, t4, 0, 16); + tcg_gen_add_tl(t0, t0, t1); + if (round45) { + tcg_gen_addi_tl(t0, t0, 1); + } + tcg_gen_shri_tl(t0, t0, 1); + tcg_gen_deposit_tl(t2, t2, t0, 0, 16); + gen_store_mxu_gpr(t2, XRa); + } +} + +/* + * Q8AVG + * Update XRa with the signed average of XRb and XRc + * on per-byte basis, rounding down. + * a.k.a. XRa[0..3] = (XRb[0..3] + XRc[0..3]) >> 1; + * + * Q8AVGR + * Update XRa with the signed average of XRb and XRc + * on per-word basis, math rounding 4/5. + * a.k.a. XRa[0..3] = (XRb[0..3] + XRc[0..3] + 1) >> 1; + */ +static void gen_mxu_q8avg(DisasContext *ctx, bool round45) +{ + uint32_t pad, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 5); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to zero */ + tcg_gen_movi_tl(mxu_gpr[XRa - 1], 0); + } else if (unlikely(XRb == XRc)) { + /* both operands same registers -> just set destination to same */ + tcg_gen_mov_tl(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + } else { + /* the most general case */ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + + gen_load_mxu_gpr(t3, XRb); + gen_load_mxu_gpr(t4, XRc); + tcg_gen_movi_tl(t2, 0); + + for (int i = 0; i < 4; i++) { + tcg_gen_extract_tl(t0, t3, 8 * i, 8); + tcg_gen_extract_tl(t1, t4, 8 * i, 8); + tcg_gen_add_tl(t0, t0, t1); + if (round45) { + tcg_gen_addi_tl(t0, t0, 1); + } + tcg_gen_shri_tl(t0, t0, 1); + tcg_gen_deposit_tl(t2, t2, t0, 8 * i, 8); + } + gen_store_mxu_gpr(t2, XRa); + } +} + +/* + * Q8MOVZ + * Quadruple 8-bit packed conditional move where + * XRb contains conditions, XRc what to move and + * XRa is the destination. + * a.k.a. if (XRb[0..3] == 0) { XRa[0..3] = XRc[0..3] } + * + * Q8MOVN + * Quadruple 8-bit packed conditional move where + * XRb contains conditions, XRc what to move and + * XRa is the destination. + * a.k.a. if (XRb[0..3] != 0) { XRa[0..3] = XRc[0..3] } + */ +static void gen_mxu_q8movzn(DisasContext *ctx, TCGCond cond) +{ + uint32_t XRc, XRb, XRa; + + XRa = extract32(ctx->opcode, 6, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRc = extract32(ctx->opcode, 14, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGLabel *l_quarterdone = gen_new_label(); + TCGLabel *l_halfdone = gen_new_label(); + TCGLabel *l_quarterrest = gen_new_label(); + TCGLabel *l_done = gen_new_label(); + + gen_load_mxu_gpr(t0, XRc); + gen_load_mxu_gpr(t1, XRb); + gen_load_mxu_gpr(t2, XRa); + + tcg_gen_extract_tl(t3, t1, 24, 8); + tcg_gen_brcondi_tl(cond, t3, 0, l_quarterdone); + tcg_gen_extract_tl(t3, t0, 24, 8); + tcg_gen_deposit_tl(t2, t2, t3, 24, 8); + + gen_set_label(l_quarterdone); + tcg_gen_extract_tl(t3, t1, 16, 8); + tcg_gen_brcondi_tl(cond, t3, 0, l_halfdone); + tcg_gen_extract_tl(t3, t0, 16, 8); + tcg_gen_deposit_tl(t2, t2, t3, 16, 8); + + gen_set_label(l_halfdone); + tcg_gen_extract_tl(t3, t1, 8, 8); + tcg_gen_brcondi_tl(cond, t3, 0, l_quarterrest); + tcg_gen_extract_tl(t3, t0, 8, 8); + tcg_gen_deposit_tl(t2, t2, t3, 8, 8); + + gen_set_label(l_quarterrest); + tcg_gen_extract_tl(t3, t1, 0, 8); + tcg_gen_brcondi_tl(cond, t3, 0, l_done); + tcg_gen_extract_tl(t3, t0, 0, 8); + tcg_gen_deposit_tl(t2, t2, t3, 0, 8); + + gen_set_label(l_done); + gen_store_mxu_gpr(t2, XRa); +} + +/* + * D16MOVZ + * Double 16-bit packed conditional move where + * XRb contains conditions, XRc what to move and + * XRa is the destination. + * a.k.a. if (XRb[0..1] == 0) { XRa[0..1] = XRc[0..1] } + * + * D16MOVN + * Double 16-bit packed conditional move where + * XRb contains conditions, XRc what to move and + * XRa is the destination. + * a.k.a. if (XRb[0..3] != 0) { XRa[0..1] = XRc[0..1] } + */ +static void gen_mxu_d16movzn(DisasContext *ctx, TCGCond cond) +{ + uint32_t XRc, XRb, XRa; + + XRa = extract32(ctx->opcode, 6, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRc = extract32(ctx->opcode, 14, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGLabel *l_halfdone = gen_new_label(); + TCGLabel *l_done = gen_new_label(); + + gen_load_mxu_gpr(t0, XRc); + gen_load_mxu_gpr(t1, XRb); + gen_load_mxu_gpr(t2, XRa); + + tcg_gen_extract_tl(t3, t1, 16, 16); + tcg_gen_brcondi_tl(cond, t3, 0, l_halfdone); + tcg_gen_extract_tl(t3, t0, 16, 16); + tcg_gen_deposit_tl(t2, t2, t3, 16, 16); + + gen_set_label(l_halfdone); + tcg_gen_extract_tl(t3, t1, 0, 16); + tcg_gen_brcondi_tl(cond, t3, 0, l_done); + tcg_gen_extract_tl(t3, t0, 0, 16); + tcg_gen_deposit_tl(t2, t2, t3, 0, 16); + + gen_set_label(l_done); + gen_store_mxu_gpr(t2, XRa); +} + +/* + * S32MOVZ + * Quadruple 32-bit conditional move where + * XRb contains conditions, XRc what to move and + * XRa is the destination. + * a.k.a. if (XRb == 0) { XRa = XRc } + * + * S32MOVN + * Single 32-bit conditional move where + * XRb contains conditions, XRc what to move and + * XRa is the destination. + * a.k.a. if (XRb != 0) { XRa = XRc } + */ +static void gen_mxu_s32movzn(DisasContext *ctx, TCGCond cond) +{ + uint32_t XRc, XRb, XRa; + + XRa = extract32(ctx->opcode, 6, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRc = extract32(ctx->opcode, 14, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGLabel *l_done = gen_new_label(); + + gen_load_mxu_gpr(t0, XRc); + gen_load_mxu_gpr(t1, XRb); + + tcg_gen_brcondi_tl(cond, t1, 0, l_done); + gen_store_mxu_gpr(t0, XRa); + gen_set_label(l_done); +} + +/* + * MXU instruction category: Addition and subtraction + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * S32CPS D16CPS + * Q8ADD + */ + +/* + * S32CPS + * Update XRa if XRc < 0 by value of 0 - XRb + * else XRa = XRb + */ +static void gen_mxu_S32CPS(DisasContext *ctx) +{ + uint32_t pad, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 5); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely(XRb == 0)) { + /* XRc make no sense 0 - 0 = 0 -> just set destination to zero */ + tcg_gen_movi_tl(mxu_gpr[XRa - 1], 0); + } else if (unlikely(XRc == 0)) { + /* condition always false -> just move XRb to XRa */ + tcg_gen_mov_tl(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + } else { + /* the most general case */ + TCGv t0 = tcg_temp_new(); + TCGLabel *l_not_less = gen_new_label(); + TCGLabel *l_done = gen_new_label(); + + tcg_gen_brcondi_tl(TCG_COND_GE, mxu_gpr[XRc - 1], 0, l_not_less); + tcg_gen_neg_tl(t0, mxu_gpr[XRb - 1]); + tcg_gen_br(l_done); + gen_set_label(l_not_less); + gen_load_mxu_gpr(t0, XRb); + gen_set_label(l_done); + gen_store_mxu_gpr(t0, XRa); + } +} + +/* + * D16CPS + * Update XRa[0..1] if XRc[0..1] < 0 by value of 0 - XRb[0..1] + * else XRa[0..1] = XRb[0..1] + */ +static void gen_mxu_D16CPS(DisasContext *ctx) +{ + uint32_t pad, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 5); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely(XRb == 0)) { + /* XRc make no sense 0 - 0 = 0 -> just set destination to zero */ + tcg_gen_movi_tl(mxu_gpr[XRa - 1], 0); + } else if (unlikely(XRc == 0)) { + /* condition always false -> just move XRb to XRa */ + tcg_gen_mov_tl(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + } else { + /* the most general case */ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGLabel *l_done_hi = gen_new_label(); + TCGLabel *l_not_less_lo = gen_new_label(); + TCGLabel *l_done_lo = gen_new_label(); + + tcg_gen_sextract_tl(t0, mxu_gpr[XRc - 1], 16, 16); + tcg_gen_sextract_tl(t1, mxu_gpr[XRb - 1], 16, 16); + tcg_gen_brcondi_tl(TCG_COND_GE, t0, 0, l_done_hi); + tcg_gen_subfi_tl(t1, 0, t1); + + gen_set_label(l_done_hi); + tcg_gen_shli_i32(t1, t1, 16); + + tcg_gen_sextract_tl(t0, mxu_gpr[XRc - 1], 0, 16); + tcg_gen_brcondi_tl(TCG_COND_GE, t0, 0, l_not_less_lo); + tcg_gen_sextract_tl(t0, mxu_gpr[XRb - 1], 0, 16); + tcg_gen_subfi_tl(t0, 0, t0); + tcg_gen_br(l_done_lo); + + gen_set_label(l_not_less_lo); + tcg_gen_extract_tl(t0, mxu_gpr[XRb - 1], 0, 16); + + gen_set_label(l_done_lo); + tcg_gen_deposit_tl(mxu_gpr[XRa - 1], t1, t0, 0, 16); + } +} + +/* + * Q8ABD XRa, XRb, XRc + * Gets absolute difference for quadruple of 8-bit + * packed in XRb to another one in XRc, + * put the result in XRa. + * a.k.a. XRa[0..3] = abs(XRb[0..3] - XRc[0..3]); + */ +static void gen_mxu_Q8ABD(DisasContext *ctx) +{ + uint32_t pad, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 3); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to zero */ + tcg_gen_movi_tl(mxu_gpr[XRa - 1], 0); + } else { + /* the most general case */ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + + gen_load_mxu_gpr(t3, XRb); + gen_load_mxu_gpr(t4, XRc); + tcg_gen_movi_tl(t2, 0); + + for (int i = 0; i < 4; i++) { + tcg_gen_extract_tl(t0, t3, 8 * i, 8); + tcg_gen_extract_tl(t1, t4, 8 * i, 8); + + tcg_gen_sub_tl(t0, t0, t1); + tcg_gen_abs_tl(t0, t0); + + tcg_gen_deposit_tl(t2, t2, t0, 8 * i, 8); + } + gen_store_mxu_gpr(t2, XRa); + } +} + +/* + * Q8ADD XRa, XRb, XRc, ptn2 + * Add/subtract quadruple of 8-bit packed in XRb + * to another one in XRc, put the result in XRa. + */ +static void gen_mxu_Q8ADD(DisasContext *ctx) +{ + uint32_t aptn2, pad, XRc, XRb, XRa; + + aptn2 = extract32(ctx->opcode, 24, 2); + pad = extract32(ctx->opcode, 21, 3); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to zero */ + tcg_gen_movi_i32(mxu_gpr[XRa - 1], 0); + } else { + /* the most general case */ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + + gen_load_mxu_gpr(t3, XRb); + gen_load_mxu_gpr(t4, XRc); + + for (int i = 0; i < 4; i++) { + tcg_gen_andi_tl(t0, t3, 0xff); + tcg_gen_andi_tl(t1, t4, 0xff); + + if (i < 2) { + if (aptn2 & 0x01) { + tcg_gen_sub_tl(t0, t0, t1); + } else { + tcg_gen_add_tl(t0, t0, t1); + } + } else { + if (aptn2 & 0x02) { + tcg_gen_sub_tl(t0, t0, t1); + } else { + tcg_gen_add_tl(t0, t0, t1); + } + } + if (i < 3) { + tcg_gen_shri_tl(t3, t3, 8); + tcg_gen_shri_tl(t4, t4, 8); + } + if (i > 0) { + tcg_gen_deposit_tl(t2, t2, t0, 8 * i, 8); + } else { + tcg_gen_andi_tl(t0, t0, 0xff); + tcg_gen_mov_tl(t2, t0); + } + } + gen_store_mxu_gpr(t2, XRa); + } +} + +/* + * Q8ADDE XRa, XRb, XRc, XRd, aptn2 + * Add/subtract quadruple of 8-bit packed in XRb + * to another one in XRc, with zero extending + * to 16-bit and put results as packed 16-bit data + * into XRa and XRd. + * aptn2 manages action add or subtract of pairs of data. + * + * Q8ACCE XRa, XRb, XRc, XRd, aptn2 + * Add/subtract quadruple of 8-bit packed in XRb + * to another one in XRc, with zero extending + * to 16-bit and accumulate results as packed 16-bit data + * into XRa and XRd. + * aptn2 manages action add or subtract of pairs of data. + */ +static void gen_mxu_q8adde(DisasContext *ctx, bool accumulate) +{ + uint32_t aptn2, XRd, XRc, XRb, XRa; + + aptn2 = extract32(ctx->opcode, 24, 2); + XRd = extract32(ctx->opcode, 18, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to zero */ + if (XRa != 0) { + tcg_gen_movi_tl(mxu_gpr[XRa - 1], 0); + } + if (XRd != 0) { + tcg_gen_movi_tl(mxu_gpr[XRd - 1], 0); + } + } else { + /* the most general case */ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + TCGv t5 = tcg_temp_new(); + + if (XRa != 0) { + gen_extract_mxu_gpr(t0, XRb, 16, 8); + gen_extract_mxu_gpr(t1, XRc, 16, 8); + gen_extract_mxu_gpr(t2, XRb, 24, 8); + gen_extract_mxu_gpr(t3, XRc, 24, 8); + if (aptn2 & 2) { + tcg_gen_sub_tl(t0, t0, t1); + tcg_gen_sub_tl(t2, t2, t3); + } else { + tcg_gen_add_tl(t0, t0, t1); + tcg_gen_add_tl(t2, t2, t3); + } + if (accumulate) { + gen_load_mxu_gpr(t5, XRa); + tcg_gen_extract_tl(t1, t5, 0, 16); + tcg_gen_extract_tl(t3, t5, 16, 16); + tcg_gen_add_tl(t0, t0, t1); + tcg_gen_add_tl(t2, t2, t3); + } + tcg_gen_shli_tl(t2, t2, 16); + tcg_gen_extract_tl(t0, t0, 0, 16); + tcg_gen_or_tl(t4, t2, t0); + } + if (XRd != 0) { + gen_extract_mxu_gpr(t0, XRb, 0, 8); + gen_extract_mxu_gpr(t1, XRc, 0, 8); + gen_extract_mxu_gpr(t2, XRb, 8, 8); + gen_extract_mxu_gpr(t3, XRc, 8, 8); + if (aptn2 & 1) { + tcg_gen_sub_tl(t0, t0, t1); + tcg_gen_sub_tl(t2, t2, t3); + } else { + tcg_gen_add_tl(t0, t0, t1); + tcg_gen_add_tl(t2, t2, t3); + } + if (accumulate) { + gen_load_mxu_gpr(t5, XRd); + tcg_gen_extract_tl(t1, t5, 0, 16); + tcg_gen_extract_tl(t3, t5, 16, 16); + tcg_gen_add_tl(t0, t0, t1); + tcg_gen_add_tl(t2, t2, t3); + } + tcg_gen_shli_tl(t2, t2, 16); + tcg_gen_extract_tl(t0, t0, 0, 16); + tcg_gen_or_tl(t5, t2, t0); + } + + gen_store_mxu_gpr(t4, XRa); + gen_store_mxu_gpr(t5, XRd); + } +} + +/* + * D8SUM XRa, XRb, XRc + * Double parallel add of quadruple unsigned 8-bit together + * with zero extending to 16-bit data. + * D8SUMC XRa, XRb, XRc + * Double parallel add of quadruple unsigned 8-bit together + * with zero extending to 16-bit data and adding 2 to each + * parallel result. + */ +static void gen_mxu_d8sum(DisasContext *ctx, bool sumc) +{ + uint32_t pad, pad2, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 24, 2); + pad2 = extract32(ctx->opcode, 18, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0 || pad2 != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to zero */ + tcg_gen_movi_tl(mxu_gpr[XRa - 1], 0); + } else { + /* the most general case */ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + TCGv t5 = tcg_temp_new(); + + if (XRb != 0) { + tcg_gen_extract_tl(t0, mxu_gpr[XRb - 1], 0, 8); + tcg_gen_extract_tl(t1, mxu_gpr[XRb - 1], 8, 8); + tcg_gen_extract_tl(t2, mxu_gpr[XRb - 1], 16, 8); + tcg_gen_extract_tl(t3, mxu_gpr[XRb - 1], 24, 8); + tcg_gen_add_tl(t4, t0, t1); + tcg_gen_add_tl(t4, t4, t2); + tcg_gen_add_tl(t4, t4, t3); + } else { + tcg_gen_mov_tl(t4, 0); + } + if (XRc != 0) { + tcg_gen_extract_tl(t0, mxu_gpr[XRc - 1], 0, 8); + tcg_gen_extract_tl(t1, mxu_gpr[XRc - 1], 8, 8); + tcg_gen_extract_tl(t2, mxu_gpr[XRc - 1], 16, 8); + tcg_gen_extract_tl(t3, mxu_gpr[XRc - 1], 24, 8); + tcg_gen_add_tl(t5, t0, t1); + tcg_gen_add_tl(t5, t5, t2); + tcg_gen_add_tl(t5, t5, t3); + } else { + tcg_gen_mov_tl(t5, 0); + } + + if (sumc) { + tcg_gen_addi_tl(t4, t4, 2); + tcg_gen_addi_tl(t5, t5, 2); + } + tcg_gen_shli_tl(t4, t4, 16); + + tcg_gen_or_tl(mxu_gpr[XRa - 1], t4, t5); + } +} + +/* + * Q16ADD XRa, XRb, XRc, XRd, aptn2, optn2 - Quad packed + * 16-bit pattern addition. + */ +static void gen_mxu_q16add(DisasContext *ctx) +{ + uint32_t aptn2, optn2, XRc, XRb, XRa, XRd; + + aptn2 = extract32(ctx->opcode, 24, 2); + optn2 = extract32(ctx->opcode, 22, 2); + XRd = extract32(ctx->opcode, 18, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + TCGv t5 = tcg_temp_new(); + + gen_load_mxu_gpr(t1, XRb); + tcg_gen_extract_tl(t0, t1, 0, 16); + tcg_gen_extract_tl(t1, t1, 16, 16); + + gen_load_mxu_gpr(t3, XRc); + tcg_gen_extract_tl(t2, t3, 0, 16); + tcg_gen_extract_tl(t3, t3, 16, 16); + + switch (optn2) { + case MXU_OPTN2_WW: /* XRB.H+XRC.H == lop, XRB.L+XRC.L == rop */ + tcg_gen_mov_tl(t4, t1); + tcg_gen_mov_tl(t5, t0); + break; + case MXU_OPTN2_LW: /* XRB.L+XRC.H == lop, XRB.L+XRC.L == rop */ + tcg_gen_mov_tl(t4, t0); + tcg_gen_mov_tl(t5, t0); + break; + case MXU_OPTN2_HW: /* XRB.H+XRC.H == lop, XRB.H+XRC.L == rop */ + tcg_gen_mov_tl(t4, t1); + tcg_gen_mov_tl(t5, t1); + break; + case MXU_OPTN2_XW: /* XRB.L+XRC.H == lop, XRB.H+XRC.L == rop */ + tcg_gen_mov_tl(t4, t0); + tcg_gen_mov_tl(t5, t1); + break; + } + + switch (aptn2) { + case MXU_APTN2_AA: /* lop +, rop + */ + tcg_gen_add_tl(t0, t4, t3); + tcg_gen_add_tl(t1, t5, t2); + tcg_gen_add_tl(t4, t4, t3); + tcg_gen_add_tl(t5, t5, t2); + break; + case MXU_APTN2_AS: /* lop +, rop + */ + tcg_gen_sub_tl(t0, t4, t3); + tcg_gen_sub_tl(t1, t5, t2); + tcg_gen_add_tl(t4, t4, t3); + tcg_gen_add_tl(t5, t5, t2); + break; + case MXU_APTN2_SA: /* lop +, rop + */ + tcg_gen_add_tl(t0, t4, t3); + tcg_gen_add_tl(t1, t5, t2); + tcg_gen_sub_tl(t4, t4, t3); + tcg_gen_sub_tl(t5, t5, t2); + break; + case MXU_APTN2_SS: /* lop +, rop + */ + tcg_gen_sub_tl(t0, t4, t3); + tcg_gen_sub_tl(t1, t5, t2); + tcg_gen_sub_tl(t4, t4, t3); + tcg_gen_sub_tl(t5, t5, t2); + break; + } + + tcg_gen_shli_tl(t0, t0, 16); + tcg_gen_extract_tl(t1, t1, 0, 16); + tcg_gen_shli_tl(t4, t4, 16); + tcg_gen_extract_tl(t5, t5, 0, 16); + + tcg_gen_or_tl(mxu_gpr[XRa - 1], t4, t5); + tcg_gen_or_tl(mxu_gpr[XRd - 1], t0, t1); +} + +/* + * Q16ACC XRa, XRb, XRc, XRd, aptn2 - Quad packed + * 16-bit addition/subtraction with accumulate. + */ +static void gen_mxu_q16acc(DisasContext *ctx) +{ + uint32_t aptn2, XRc, XRb, XRa, XRd; + + aptn2 = extract32(ctx->opcode, 24, 2); + XRd = extract32(ctx->opcode, 18, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv s3 = tcg_temp_new(); + TCGv s2 = tcg_temp_new(); + TCGv s1 = tcg_temp_new(); + TCGv s0 = tcg_temp_new(); + + gen_load_mxu_gpr(t1, XRb); + tcg_gen_extract_tl(t0, t1, 0, 16); + tcg_gen_extract_tl(t1, t1, 16, 16); + + gen_load_mxu_gpr(t3, XRc); + tcg_gen_extract_tl(t2, t3, 0, 16); + tcg_gen_extract_tl(t3, t3, 16, 16); + + switch (aptn2) { + case MXU_APTN2_AA: /* lop +, rop + */ + tcg_gen_add_tl(s3, t1, t3); + tcg_gen_add_tl(s2, t0, t2); + tcg_gen_add_tl(s1, t1, t3); + tcg_gen_add_tl(s0, t0, t2); + break; + case MXU_APTN2_AS: /* lop +, rop - */ + tcg_gen_sub_tl(s3, t1, t3); + tcg_gen_sub_tl(s2, t0, t2); + tcg_gen_add_tl(s1, t1, t3); + tcg_gen_add_tl(s0, t0, t2); + break; + case MXU_APTN2_SA: /* lop -, rop + */ + tcg_gen_add_tl(s3, t1, t3); + tcg_gen_add_tl(s2, t0, t2); + tcg_gen_sub_tl(s1, t1, t3); + tcg_gen_sub_tl(s0, t0, t2); + break; + case MXU_APTN2_SS: /* lop -, rop - */ + tcg_gen_sub_tl(s3, t1, t3); + tcg_gen_sub_tl(s2, t0, t2); + tcg_gen_sub_tl(s1, t1, t3); + tcg_gen_sub_tl(s0, t0, t2); + break; + } + + if (XRa != 0) { + tcg_gen_add_tl(t0, mxu_gpr[XRa - 1], s0); + tcg_gen_extract_tl(t0, t0, 0, 16); + tcg_gen_extract_tl(t1, mxu_gpr[XRa - 1], 16, 16); + tcg_gen_add_tl(t1, t1, s1); + tcg_gen_shli_tl(t1, t1, 16); + tcg_gen_or_tl(mxu_gpr[XRa - 1], t1, t0); + } + + if (XRd != 0) { + tcg_gen_add_tl(t0, mxu_gpr[XRd - 1], s2); + tcg_gen_extract_tl(t0, t0, 0, 16); + tcg_gen_extract_tl(t1, mxu_gpr[XRd - 1], 16, 16); + tcg_gen_add_tl(t1, t1, s3); + tcg_gen_shli_tl(t1, t1, 16); + tcg_gen_or_tl(mxu_gpr[XRd - 1], t1, t0); + } +} + +/* + * Q16ACCM XRa, XRb, XRc, XRd, aptn2 - Quad packed + * 16-bit accumulate. + */ +static void gen_mxu_q16accm(DisasContext *ctx) +{ + uint32_t aptn2, XRc, XRb, XRa, XRd; + + aptn2 = extract32(ctx->opcode, 24, 2); + XRd = extract32(ctx->opcode, 18, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + + gen_load_mxu_gpr(t2, XRb); + gen_load_mxu_gpr(t3, XRc); + + if (XRa != 0) { + TCGv a0 = tcg_temp_new(); + TCGv a1 = tcg_temp_new(); + + tcg_gen_extract_tl(t0, t2, 0, 16); + tcg_gen_extract_tl(t1, t2, 16, 16); + + gen_load_mxu_gpr(a1, XRa); + tcg_gen_extract_tl(a0, a1, 0, 16); + tcg_gen_extract_tl(a1, a1, 16, 16); + + if (aptn2 & 2) { + tcg_gen_sub_tl(a0, a0, t0); + tcg_gen_sub_tl(a1, a1, t1); + } else { + tcg_gen_add_tl(a0, a0, t0); + tcg_gen_add_tl(a1, a1, t1); + } + tcg_gen_extract_tl(a0, a0, 0, 16); + tcg_gen_shli_tl(a1, a1, 16); + tcg_gen_or_tl(mxu_gpr[XRa - 1], a1, a0); + } + + if (XRd != 0) { + TCGv a0 = tcg_temp_new(); + TCGv a1 = tcg_temp_new(); + + tcg_gen_extract_tl(t0, t3, 0, 16); + tcg_gen_extract_tl(t1, t3, 16, 16); + + gen_load_mxu_gpr(a1, XRd); + tcg_gen_extract_tl(a0, a1, 0, 16); + tcg_gen_extract_tl(a1, a1, 16, 16); + + if (aptn2 & 1) { + tcg_gen_sub_tl(a0, a0, t0); + tcg_gen_sub_tl(a1, a1, t1); + } else { + tcg_gen_add_tl(a0, a0, t0); + tcg_gen_add_tl(a1, a1, t1); + } + tcg_gen_extract_tl(a0, a0, 0, 16); + tcg_gen_shli_tl(a1, a1, 16); + tcg_gen_or_tl(mxu_gpr[XRd - 1], a1, a0); + } +} + + +/* + * D16ASUM XRa, XRb, XRc, XRd, aptn2 - Double packed + * 16-bit sign extended addition and accumulate. + */ +static void gen_mxu_d16asum(DisasContext *ctx) +{ + uint32_t aptn2, XRc, XRb, XRa, XRd; + + aptn2 = extract32(ctx->opcode, 24, 2); + XRd = extract32(ctx->opcode, 18, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + + gen_load_mxu_gpr(t2, XRb); + gen_load_mxu_gpr(t3, XRc); + + if (XRa != 0) { + tcg_gen_sextract_tl(t0, t2, 0, 16); + tcg_gen_sextract_tl(t1, t2, 16, 16); + tcg_gen_add_tl(t0, t0, t1); + if (aptn2 & 2) { + tcg_gen_sub_tl(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0); + } else { + tcg_gen_add_tl(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0); + } + } + + if (XRd != 0) { + tcg_gen_sextract_tl(t0, t3, 0, 16); + tcg_gen_sextract_tl(t1, t3, 16, 16); + tcg_gen_add_tl(t0, t0, t1); + if (aptn2 & 1) { + tcg_gen_sub_tl(mxu_gpr[XRd - 1], mxu_gpr[XRd - 1], t0); + } else { + tcg_gen_add_tl(mxu_gpr[XRd - 1], mxu_gpr[XRd - 1], t0); + } + } +} + +/* + * D32ADD XRa, XRb, XRc, XRd, aptn2 - Double + * 32 bit pattern addition/subtraction, set carry. + * + * D32ADDC XRa, XRb, XRc, XRd, aptn2 - Double + * 32 bit pattern addition/subtraction with carry. + */ +static void gen_mxu_d32add(DisasContext *ctx) +{ + uint32_t aptn2, addc, XRc, XRb, XRa, XRd; + + aptn2 = extract32(ctx->opcode, 24, 2); + addc = extract32(ctx->opcode, 22, 2); + XRd = extract32(ctx->opcode, 18, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv cr = tcg_temp_new(); + + if (unlikely(addc > 1)) { + /* opcode incorrect -> do nothing */ + } else if (addc == 1) { + if (unlikely(XRa == 0 && XRd == 0)) { + /* destinations are zero register -> do nothing */ + } else { + /* FIXME ??? What if XRa == XRd ??? */ + /* aptn2 is unused here */ + gen_load_mxu_gpr(t0, XRb); + gen_load_mxu_gpr(t1, XRc); + gen_load_mxu_cr(cr); + if (XRa != 0) { + tcg_gen_extract_tl(t2, cr, 31, 1); + tcg_gen_add_tl(t0, t0, t2); + tcg_gen_add_tl(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0); + } + if (XRd != 0) { + tcg_gen_extract_tl(t2, cr, 30, 1); + tcg_gen_add_tl(t1, t1, t2); + tcg_gen_add_tl(mxu_gpr[XRd - 1], mxu_gpr[XRd - 1], t1); + } + } + } else if (unlikely(XRa == 0 && XRd == 0)) { + /* destinations are zero register -> do nothing */ + } else { + /* common case */ + /* FIXME ??? What if XRa == XRd ??? */ + TCGv carry = tcg_temp_new(); + + gen_load_mxu_gpr(t0, XRb); + gen_load_mxu_gpr(t1, XRc); + gen_load_mxu_cr(cr); + if (XRa != 0) { + if (aptn2 & 2) { + tcg_gen_sub_i32(t2, t0, t1); + tcg_gen_setcond_tl(TCG_COND_GTU, carry, t0, t1); + } else { + tcg_gen_add_i32(t2, t0, t1); + tcg_gen_setcond_tl(TCG_COND_GTU, carry, t0, t2); + } + tcg_gen_andi_tl(cr, cr, 0x7fffffff); + tcg_gen_shli_tl(carry, carry, 31); + tcg_gen_or_tl(cr, cr, carry); + gen_store_mxu_gpr(t2, XRa); + } + if (XRd != 0) { + if (aptn2 & 1) { + tcg_gen_sub_i32(t2, t0, t1); + tcg_gen_setcond_tl(TCG_COND_GTU, carry, t0, t1); + } else { + tcg_gen_add_i32(t2, t0, t1); + tcg_gen_setcond_tl(TCG_COND_GTU, carry, t0, t2); + } + tcg_gen_andi_tl(cr, cr, 0xbfffffff); + tcg_gen_shli_tl(carry, carry, 30); + tcg_gen_or_tl(cr, cr, carry); + gen_store_mxu_gpr(t2, XRd); + } + gen_store_mxu_cr(cr); + } +} + +/* + * D32ACC XRa, XRb, XRc, XRd, aptn2 - Double + * 32 bit pattern addition/subtraction and accumulate. + */ +static void gen_mxu_d32acc(DisasContext *ctx) +{ + uint32_t aptn2, XRc, XRb, XRa, XRd; + + aptn2 = extract32(ctx->opcode, 24, 2); + XRd = extract32(ctx->opcode, 18, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + if (unlikely(XRa == 0 && XRd == 0)) { + /* destinations are zero register -> do nothing */ + } else { + /* common case */ + gen_load_mxu_gpr(t0, XRb); + gen_load_mxu_gpr(t1, XRc); + if (XRa != 0) { + if (aptn2 & 2) { + tcg_gen_sub_tl(t2, t0, t1); + } else { + tcg_gen_add_tl(t2, t0, t1); + } + tcg_gen_add_tl(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t2); + } + if (XRd != 0) { + if (aptn2 & 1) { + tcg_gen_sub_tl(t2, t0, t1); + } else { + tcg_gen_add_tl(t2, t0, t1); + } + tcg_gen_add_tl(mxu_gpr[XRd - 1], mxu_gpr[XRd - 1], t2); + } + } +} + +/* + * D32ACCM XRa, XRb, XRc, XRd, aptn2 - Double + * 32 bit pattern addition/subtraction and accumulate. + */ +static void gen_mxu_d32accm(DisasContext *ctx) +{ + uint32_t aptn2, XRc, XRb, XRa, XRd; + + aptn2 = extract32(ctx->opcode, 24, 2); + XRd = extract32(ctx->opcode, 18, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + if (unlikely(XRa == 0 && XRd == 0)) { + /* destinations are zero register -> do nothing */ + } else { + /* common case */ + gen_load_mxu_gpr(t0, XRb); + gen_load_mxu_gpr(t1, XRc); + if (XRa != 0) { + tcg_gen_add_tl(t2, t0, t1); + if (aptn2 & 2) { + tcg_gen_sub_tl(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t2); + } else { + tcg_gen_add_tl(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t2); + } + } + if (XRd != 0) { + tcg_gen_sub_tl(t2, t0, t1); + if (aptn2 & 1) { + tcg_gen_sub_tl(mxu_gpr[XRd - 1], mxu_gpr[XRd - 1], t2); + } else { + tcg_gen_add_tl(mxu_gpr[XRd - 1], mxu_gpr[XRd - 1], t2); + } + } + } +} + +/* + * D32ASUM XRa, XRb, XRc, XRd, aptn2 - Double + * 32 bit pattern addition/subtraction. + */ +static void gen_mxu_d32asum(DisasContext *ctx) +{ + uint32_t aptn2, XRc, XRb, XRa, XRd; + + aptn2 = extract32(ctx->opcode, 24, 2); + XRd = extract32(ctx->opcode, 18, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + if (unlikely(XRa == 0 && XRd == 0)) { + /* destinations are zero register -> do nothing */ + } else { + /* common case */ + gen_load_mxu_gpr(t0, XRb); + gen_load_mxu_gpr(t1, XRc); + if (XRa != 0) { + if (aptn2 & 2) { + tcg_gen_sub_tl(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0); + } else { + tcg_gen_add_tl(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0); + } + } + if (XRd != 0) { + if (aptn2 & 1) { + tcg_gen_sub_tl(mxu_gpr[XRd - 1], mxu_gpr[XRd - 1], t1); + } else { + tcg_gen_add_tl(mxu_gpr[XRd - 1], mxu_gpr[XRd - 1], t1); + } + } + } +} + +/* + * MXU instruction category: Miscellaneous + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * S32EXTR S32LUI + * S32EXTRV + * Q16SAT + * Q16SCOP + */ + +/* + * S32EXTR XRa, XRd, rs, bits5 + * Extract bits5 bits from 64-bit pair {XRa:XRd} + * starting from rs[4:0] offset and put to the XRa. + */ +static void gen_mxu_s32extr(DisasContext *ctx) +{ + TCGv t0, t1, t2, t3; + uint32_t XRa, XRd, rs, bits5; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + t3 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + XRd = extract32(ctx->opcode, 10, 4); + bits5 = extract32(ctx->opcode, 16, 5); + rs = extract32(ctx->opcode, 21, 5); + + /* {tmp} = {XRa:XRd} >> (64 - rt - bits5); */ + /* {XRa} = extract({tmp}, 0, bits5); */ + if (bits5 > 0) { + TCGLabel *l_xra_only = gen_new_label(); + TCGLabel *l_done = gen_new_label(); + + gen_load_mxu_gpr(t0, XRd); + gen_load_mxu_gpr(t1, XRa); + gen_load_gpr(t2, rs); + tcg_gen_andi_tl(t2, t2, 0x1f); + tcg_gen_subfi_tl(t2, 32, t2); + tcg_gen_brcondi_tl(TCG_COND_GE, t2, bits5, l_xra_only); + tcg_gen_subfi_tl(t2, bits5, t2); + tcg_gen_subfi_tl(t3, 32, t2); + tcg_gen_shr_tl(t0, t0, t3); + tcg_gen_shl_tl(t1, t1, t2); + tcg_gen_or_tl(t0, t0, t1); + tcg_gen_br(l_done); + gen_set_label(l_xra_only); + tcg_gen_subi_tl(t2, t2, bits5); + tcg_gen_shr_tl(t0, t1, t2); + gen_set_label(l_done); + tcg_gen_extract_tl(t0, t0, 0, bits5); + } else { + /* unspecified behavior but matches tests on real hardware*/ + tcg_gen_movi_tl(t0, 0); + } + gen_store_mxu_gpr(t0, XRa); +} + +/* + * S32EXTRV XRa, XRd, rs, rt + * Extract rt[4:0] bits from 64-bit pair {XRa:XRd} + * starting from rs[4:0] offset and put to the XRa. + */ +static void gen_mxu_s32extrv(DisasContext *ctx) +{ + TCGv t0, t1, t2, t3, t4; + uint32_t XRa, XRd, rs, rt; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + t3 = tcg_temp_new(); + t4 = tcg_temp_new(); + TCGLabel *l_xra_only = gen_new_label(); + TCGLabel *l_done = gen_new_label(); + TCGLabel *l_zero = gen_new_label(); + TCGLabel *l_extract = gen_new_label(); + + XRa = extract32(ctx->opcode, 6, 4); + XRd = extract32(ctx->opcode, 10, 4); + rt = extract32(ctx->opcode, 16, 5); + rs = extract32(ctx->opcode, 21, 5); + + /* {tmp} = {XRa:XRd} >> (64 - rs - rt) */ + gen_load_mxu_gpr(t0, XRd); + gen_load_mxu_gpr(t1, XRa); + gen_load_gpr(t2, rs); + gen_load_gpr(t4, rt); + tcg_gen_brcondi_tl(TCG_COND_EQ, t4, 0, l_zero); + tcg_gen_andi_tl(t2, t2, 0x1f); + tcg_gen_subfi_tl(t2, 32, t2); + tcg_gen_brcond_tl(TCG_COND_GE, t2, t4, l_xra_only); + tcg_gen_sub_tl(t2, t4, t2); + tcg_gen_subfi_tl(t3, 32, t2); + tcg_gen_shr_tl(t0, t0, t3); + tcg_gen_shl_tl(t1, t1, t2); + tcg_gen_or_tl(t0, t0, t1); + tcg_gen_br(l_extract); + + gen_set_label(l_xra_only); + tcg_gen_sub_tl(t2, t2, t4); + tcg_gen_shr_tl(t0, t1, t2); + tcg_gen_br(l_extract); + + /* unspecified behavior but matches tests on real hardware*/ + gen_set_label(l_zero); + tcg_gen_movi_tl(t0, 0); + tcg_gen_br(l_done); + + /* {XRa} = extract({tmp}, 0, rt) */ + gen_set_label(l_extract); + tcg_gen_subfi_tl(t4, 32, t4); + tcg_gen_shl_tl(t0, t0, t4); + tcg_gen_shr_tl(t0, t0, t4); + + gen_set_label(l_done); + gen_store_mxu_gpr(t0, XRa); +} + +/* + * S32LUI XRa, S8, optn3 + * Permutate the immediate S8 value to form a word + * to update XRa. + */ +static void gen_mxu_s32lui(DisasContext *ctx) +{ + uint32_t XRa, s8, optn3, pad; + + XRa = extract32(ctx->opcode, 6, 4); + s8 = extract32(ctx->opcode, 10, 8); + pad = extract32(ctx->opcode, 21, 2); + optn3 = extract32(ctx->opcode, 23, 3); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else { + uint32_t s16; + TCGv t0 = tcg_temp_new(); + + switch (optn3) { + case 0: + tcg_gen_movi_tl(t0, s8); + break; + case 1: + tcg_gen_movi_tl(t0, s8 << 8); + break; + case 2: + tcg_gen_movi_tl(t0, s8 << 16); + break; + case 3: + tcg_gen_movi_tl(t0, s8 << 24); + break; + case 4: + tcg_gen_movi_tl(t0, (s8 << 16) | s8); + break; + case 5: + tcg_gen_movi_tl(t0, (s8 << 24) | (s8 << 8)); + break; + case 6: + s16 = (uint16_t)(int16_t)(int8_t)s8; + tcg_gen_movi_tl(t0, (s16 << 16) | s16); + break; + case 7: + tcg_gen_movi_tl(t0, (s8 << 24) | (s8 << 16) | (s8 << 8) | s8); + break; + } + gen_store_mxu_gpr(t0, XRa); + } +} + +/* + * Q16SAT XRa, XRb, XRc + * Packs four 16-bit signed integers in XRb and XRc to + * four saturated unsigned 8-bit into XRa. + * + */ +static void gen_mxu_Q16SAT(DisasContext *ctx) +{ + uint32_t pad, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 3); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else { + /* the most general case */ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + tcg_gen_movi_tl(t2, 0); + if (XRb != 0) { + TCGLabel *l_less_hi = gen_new_label(); + TCGLabel *l_less_lo = gen_new_label(); + TCGLabel *l_lo = gen_new_label(); + TCGLabel *l_greater_hi = gen_new_label(); + TCGLabel *l_greater_lo = gen_new_label(); + TCGLabel *l_done = gen_new_label(); + + tcg_gen_sari_tl(t0, mxu_gpr[XRb - 1], 16); + tcg_gen_brcondi_tl(TCG_COND_LT, t0, 0, l_less_hi); + tcg_gen_brcondi_tl(TCG_COND_GT, t0, 255, l_greater_hi); + tcg_gen_br(l_lo); + gen_set_label(l_less_hi); + tcg_gen_movi_tl(t0, 0); + tcg_gen_br(l_lo); + gen_set_label(l_greater_hi); + tcg_gen_movi_tl(t0, 255); + + gen_set_label(l_lo); + tcg_gen_shli_tl(t1, mxu_gpr[XRb - 1], 16); + tcg_gen_sari_tl(t1, t1, 16); + tcg_gen_brcondi_tl(TCG_COND_LT, t1, 0, l_less_lo); + tcg_gen_brcondi_tl(TCG_COND_GT, t1, 255, l_greater_lo); + tcg_gen_br(l_done); + gen_set_label(l_less_lo); + tcg_gen_movi_tl(t1, 0); + tcg_gen_br(l_done); + gen_set_label(l_greater_lo); + tcg_gen_movi_tl(t1, 255); + + gen_set_label(l_done); + tcg_gen_shli_tl(t2, t0, 24); + tcg_gen_shli_tl(t1, t1, 16); + tcg_gen_or_tl(t2, t2, t1); + } + + if (XRc != 0) { + TCGLabel *l_less_hi = gen_new_label(); + TCGLabel *l_less_lo = gen_new_label(); + TCGLabel *l_lo = gen_new_label(); + TCGLabel *l_greater_hi = gen_new_label(); + TCGLabel *l_greater_lo = gen_new_label(); + TCGLabel *l_done = gen_new_label(); + + tcg_gen_sari_tl(t0, mxu_gpr[XRc - 1], 16); + tcg_gen_brcondi_tl(TCG_COND_LT, t0, 0, l_less_hi); + tcg_gen_brcondi_tl(TCG_COND_GT, t0, 255, l_greater_hi); + tcg_gen_br(l_lo); + gen_set_label(l_less_hi); + tcg_gen_movi_tl(t0, 0); + tcg_gen_br(l_lo); + gen_set_label(l_greater_hi); + tcg_gen_movi_tl(t0, 255); + + gen_set_label(l_lo); + tcg_gen_shli_tl(t1, mxu_gpr[XRc - 1], 16); + tcg_gen_sari_tl(t1, t1, 16); + tcg_gen_brcondi_tl(TCG_COND_LT, t1, 0, l_less_lo); + tcg_gen_brcondi_tl(TCG_COND_GT, t1, 255, l_greater_lo); + tcg_gen_br(l_done); + gen_set_label(l_less_lo); + tcg_gen_movi_tl(t1, 0); + tcg_gen_br(l_done); + gen_set_label(l_greater_lo); + tcg_gen_movi_tl(t1, 255); + + gen_set_label(l_done); + tcg_gen_shli_tl(t0, t0, 8); + tcg_gen_or_tl(t2, t2, t0); + tcg_gen_or_tl(t2, t2, t1); + } + gen_store_mxu_gpr(t2, XRa); + } +} + +/* + * Q16SCOP XRa, XRd, XRb, XRc + * Determine sign of quad packed 16-bit signed values + * in XRb and XRc put result in XRa and XRd respectively. + */ +static void gen_mxu_q16scop(DisasContext *ctx) +{ + uint32_t XRd, XRc, XRb, XRa; + + XRd = extract32(ctx->opcode, 18, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + + TCGLabel *l_b_hi_lt = gen_new_label(); + TCGLabel *l_b_hi_gt = gen_new_label(); + TCGLabel *l_b_lo = gen_new_label(); + TCGLabel *l_b_lo_lt = gen_new_label(); + TCGLabel *l_c_hi = gen_new_label(); + TCGLabel *l_c_hi_lt = gen_new_label(); + TCGLabel *l_c_hi_gt = gen_new_label(); + TCGLabel *l_c_lo = gen_new_label(); + TCGLabel *l_c_lo_lt = gen_new_label(); + TCGLabel *l_done = gen_new_label(); + + gen_load_mxu_gpr(t0, XRb); + gen_load_mxu_gpr(t1, XRc); + + tcg_gen_sextract_tl(t2, t0, 16, 16); + tcg_gen_brcondi_tl(TCG_COND_LT, t2, 0, l_b_hi_lt); + tcg_gen_brcondi_tl(TCG_COND_GT, t2, 0, l_b_hi_gt); + tcg_gen_movi_tl(t3, 0); + tcg_gen_br(l_b_lo); + gen_set_label(l_b_hi_lt); + tcg_gen_movi_tl(t3, 0xffff0000); + tcg_gen_br(l_b_lo); + gen_set_label(l_b_hi_gt); + tcg_gen_movi_tl(t3, 0x00010000); + + gen_set_label(l_b_lo); + tcg_gen_sextract_tl(t2, t0, 0, 16); + tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, l_c_hi); + tcg_gen_brcondi_tl(TCG_COND_LT, t2, 0, l_b_lo_lt); + tcg_gen_ori_tl(t3, t3, 0x00000001); + tcg_gen_br(l_c_hi); + gen_set_label(l_b_lo_lt); + tcg_gen_ori_tl(t3, t3, 0x0000ffff); + tcg_gen_br(l_c_hi); + + gen_set_label(l_c_hi); + tcg_gen_sextract_tl(t2, t1, 16, 16); + tcg_gen_brcondi_tl(TCG_COND_LT, t2, 0, l_c_hi_lt); + tcg_gen_brcondi_tl(TCG_COND_GT, t2, 0, l_c_hi_gt); + tcg_gen_movi_tl(t4, 0); + tcg_gen_br(l_c_lo); + gen_set_label(l_c_hi_lt); + tcg_gen_movi_tl(t4, 0xffff0000); + tcg_gen_br(l_c_lo); + gen_set_label(l_c_hi_gt); + tcg_gen_movi_tl(t4, 0x00010000); + + gen_set_label(l_c_lo); + tcg_gen_sextract_tl(t2, t1, 0, 16); + tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, l_done); + tcg_gen_brcondi_tl(TCG_COND_LT, t2, 0, l_c_lo_lt); + tcg_gen_ori_tl(t4, t4, 0x00000001); + tcg_gen_br(l_done); + gen_set_label(l_c_lo_lt); + tcg_gen_ori_tl(t4, t4, 0x0000ffff); + + gen_set_label(l_done); + gen_store_mxu_gpr(t3, XRa); + gen_store_mxu_gpr(t4, XRd); +} + +/* + * S32SFL XRa, XRd, XRb, XRc + * Shuffle bytes according to one of four patterns. + */ +static void gen_mxu_s32sfl(DisasContext *ctx) +{ + uint32_t XRd, XRc, XRb, XRa, ptn2; + + XRd = extract32(ctx->opcode, 18, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + ptn2 = extract32(ctx->opcode, 24, 2); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + + gen_load_mxu_gpr(t0, XRb); + gen_load_mxu_gpr(t1, XRc); + + switch (ptn2) { + case 0: + tcg_gen_andi_tl(t2, t0, 0xff000000); + tcg_gen_andi_tl(t3, t1, 0x000000ff); + tcg_gen_deposit_tl(t3, t3, t0, 8, 8); + tcg_gen_shri_tl(t0, t0, 8); + tcg_gen_shri_tl(t1, t1, 8); + tcg_gen_deposit_tl(t3, t3, t0, 24, 8); + tcg_gen_deposit_tl(t3, t3, t1, 16, 8); + tcg_gen_shri_tl(t0, t0, 8); + tcg_gen_shri_tl(t1, t1, 8); + tcg_gen_deposit_tl(t2, t2, t0, 8, 8); + tcg_gen_deposit_tl(t2, t2, t1, 0, 8); + tcg_gen_shri_tl(t1, t1, 8); + tcg_gen_deposit_tl(t2, t2, t1, 16, 8); + break; + case 1: + tcg_gen_andi_tl(t2, t0, 0xff000000); + tcg_gen_andi_tl(t3, t1, 0x000000ff); + tcg_gen_deposit_tl(t3, t3, t0, 16, 8); + tcg_gen_shri_tl(t0, t0, 8); + tcg_gen_shri_tl(t1, t1, 8); + tcg_gen_deposit_tl(t2, t2, t0, 16, 8); + tcg_gen_deposit_tl(t2, t2, t1, 0, 8); + tcg_gen_shri_tl(t0, t0, 8); + tcg_gen_shri_tl(t1, t1, 8); + tcg_gen_deposit_tl(t3, t3, t0, 24, 8); + tcg_gen_deposit_tl(t3, t3, t1, 8, 8); + tcg_gen_shri_tl(t1, t1, 8); + tcg_gen_deposit_tl(t2, t2, t1, 8, 8); + break; + case 2: + tcg_gen_andi_tl(t2, t0, 0xff00ff00); + tcg_gen_andi_tl(t3, t1, 0x00ff00ff); + tcg_gen_deposit_tl(t3, t3, t0, 8, 8); + tcg_gen_shri_tl(t0, t0, 16); + tcg_gen_shri_tl(t1, t1, 8); + tcg_gen_deposit_tl(t2, t2, t1, 0, 8); + tcg_gen_deposit_tl(t3, t3, t0, 24, 8); + tcg_gen_shri_tl(t1, t1, 16); + tcg_gen_deposit_tl(t2, t2, t1, 16, 8); + break; + case 3: + tcg_gen_andi_tl(t2, t0, 0xffff0000); + tcg_gen_andi_tl(t3, t1, 0x0000ffff); + tcg_gen_shri_tl(t1, t1, 16); + tcg_gen_deposit_tl(t2, t2, t1, 0, 16); + tcg_gen_deposit_tl(t3, t3, t0, 16, 16); + break; + } + + gen_store_mxu_gpr(t2, XRa); + gen_store_mxu_gpr(t3, XRd); +} + +/* + * Q8SAD XRa, XRd, XRb, XRc + * Typical SAD operation for motion estimation. + */ +static void gen_mxu_q8sad(DisasContext *ctx) +{ + uint32_t XRd, XRc, XRb, XRa; + + XRd = extract32(ctx->opcode, 18, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGv t4 = tcg_temp_new(); + TCGv t5 = tcg_temp_new(); + + gen_load_mxu_gpr(t2, XRb); + gen_load_mxu_gpr(t3, XRc); + gen_load_mxu_gpr(t5, XRd); + tcg_gen_movi_tl(t4, 0); + + for (int i = 0; i < 4; i++) { + tcg_gen_andi_tl(t0, t2, 0xff); + tcg_gen_andi_tl(t1, t3, 0xff); + tcg_gen_sub_tl(t0, t0, t1); + tcg_gen_abs_tl(t0, t0); + tcg_gen_add_tl(t4, t4, t0); + if (i < 3) { + tcg_gen_shri_tl(t2, t2, 8); + tcg_gen_shri_tl(t3, t3, 8); + } + } + tcg_gen_add_tl(t5, t5, t4); + gen_store_mxu_gpr(t4, XRa); + gen_store_mxu_gpr(t5, XRd); +} /* * MXU instruction category: align @@ -1384,9 +4206,6 @@ static void gen_mxu_S32ALNI(DisasContext *ctx) tcg_gen_shri_i32(t1, t1, 24); tcg_gen_or_i32(mxu_gpr[XRa - 1], t0, t1); - - tcg_temp_free(t1); - tcg_temp_free(t0); } break; case MXU_OPTN3_PTN2: @@ -1410,9 +4229,6 @@ static void gen_mxu_S32ALNI(DisasContext *ctx) tcg_gen_shri_i32(t1, t1, 16); tcg_gen_or_i32(mxu_gpr[XRa - 1], t0, t1); - - tcg_temp_free(t1); - tcg_temp_free(t0); } break; case MXU_OPTN3_PTN3: @@ -1436,9 +4252,6 @@ static void gen_mxu_S32ALNI(DisasContext *ctx) tcg_gen_shri_i32(t1, t1, 8); tcg_gen_or_i32(mxu_gpr[XRa - 1], t0, t1); - - tcg_temp_free(t1); - tcg_temp_free(t0); } break; case MXU_OPTN3_PTN4: @@ -1459,6 +4272,129 @@ static void gen_mxu_S32ALNI(DisasContext *ctx) } } +/* + * S32ALN XRc, XRb, XRa, rs + * Arrange bytes from XRb and XRc according to one of five sets of + * rules determined by rs[2:0], and place the result in XRa. + */ +static void gen_mxu_S32ALN(DisasContext *ctx) +{ + uint32_t rs, XRc, XRb, XRa; + + rs = extract32(ctx->opcode, 21, 5); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to all 0s */ + tcg_gen_movi_tl(mxu_gpr[XRa - 1], 0); + } else { + /* the most general case */ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + TCGLabel *l_exit = gen_new_label(); + TCGLabel *l_b_only = gen_new_label(); + TCGLabel *l_c_only = gen_new_label(); + + gen_load_mxu_gpr(t0, XRb); + gen_load_mxu_gpr(t1, XRc); + gen_load_gpr(t2, rs); + tcg_gen_andi_tl(t2, t2, 0x07); + + /* do nothing for undefined cases */ + tcg_gen_brcondi_tl(TCG_COND_GE, t2, 5, l_exit); + + tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, l_b_only); + tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 4, l_c_only); + + tcg_gen_shli_tl(t2, t2, 3); + tcg_gen_subfi_tl(t3, 32, t2); + + tcg_gen_shl_tl(t0, t0, t2); + tcg_gen_shr_tl(t1, t1, t3); + tcg_gen_or_tl(mxu_gpr[XRa - 1], t0, t1); + tcg_gen_br(l_exit); + + gen_set_label(l_b_only); + gen_store_mxu_gpr(t0, XRa); + tcg_gen_br(l_exit); + + gen_set_label(l_c_only); + gen_store_mxu_gpr(t1, XRa); + + gen_set_label(l_exit); + } +} + +/* + * S32MADD XRa, XRd, rb, rc + * 32 to 64 bit signed multiply with subsequent add + * result stored in {XRa, XRd} pair, stain HI/LO. + * S32MADDU XRa, XRd, rb, rc + * 32 to 64 bit unsigned multiply with subsequent add + * result stored in {XRa, XRd} pair, stain HI/LO. + * S32MSUB XRa, XRd, rb, rc + * 32 to 64 bit signed multiply with subsequent subtract + * result stored in {XRa, XRd} pair, stain HI/LO. + * S32MSUBU XRa, XRd, rb, rc + * 32 to 64 bit unsigned multiply with subsequent subtract + * result stored in {XRa, XRd} pair, stain HI/LO. + */ +static void gen_mxu_s32madd_sub(DisasContext *ctx, bool sub, bool uns) +{ + uint32_t XRa, XRd, Rb, Rc; + + XRa = extract32(ctx->opcode, 6, 4); + XRd = extract32(ctx->opcode, 10, 4); + Rb = extract32(ctx->opcode, 16, 5); + Rc = extract32(ctx->opcode, 21, 5); + + if (unlikely(Rb == 0 || Rc == 0)) { + /* do nothing because x + 0 * y => x */ + } else if (unlikely(XRa == 0 && XRd == 0)) { + /* do nothing because result just dropped */ + } else { + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + gen_load_gpr(t0, Rb); + gen_load_gpr(t1, Rc); + + if (uns) { + tcg_gen_extu_tl_i64(t2, t0); + tcg_gen_extu_tl_i64(t3, t1); + } else { + tcg_gen_ext_tl_i64(t2, t0); + tcg_gen_ext_tl_i64(t3, t1); + } + tcg_gen_mul_i64(t2, t2, t3); + + gen_load_mxu_gpr(t0, XRa); + gen_load_mxu_gpr(t1, XRd); + + tcg_gen_concat_tl_i64(t3, t1, t0); + if (sub) { + tcg_gen_sub_i64(t3, t3, t2); + } else { + tcg_gen_add_i64(t3, t3, t2); + } + gen_move_low32(t1, t3); + gen_move_high32(t0, t3); + + tcg_gen_mov_tl(cpu_HI[0], t0); + tcg_gen_mov_tl(cpu_LO[0], t1); + + gen_store_mxu_gpr(t1, XRd); + gen_store_mxu_gpr(t0, XRa); + } +} /* * Decoding engine for MXU @@ -1482,6 +4418,116 @@ static void decode_opc_mxu__pool00(DisasContext *ctx) case OPC_MXU_Q8MIN: gen_mxu_Q8MAX_Q8MIN(ctx); break; + case OPC_MXU_Q8SLT: + gen_mxu_q8slt(ctx, false); + break; + case OPC_MXU_Q8SLTU: + gen_mxu_q8slt(ctx, true); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + break; + } +} + +static bool decode_opc_mxu_s32madd_sub(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 0, 6); + uint32_t pad = extract32(ctx->opcode, 14, 2); + + if (pad != 2) { + /* MIPS32R1 MADD/MADDU/MSUB/MSUBU are on pad == 0 */ + return false; + } + + switch (opcode) { + case OPC_MXU_S32MADD: + gen_mxu_s32madd_sub(ctx, false, false); + break; + case OPC_MXU_S32MADDU: + gen_mxu_s32madd_sub(ctx, false, true); + break; + case OPC_MXU_S32MSUB: + gen_mxu_s32madd_sub(ctx, true, false); + break; + case OPC_MXU_S32MSUBU: + gen_mxu_s32madd_sub(ctx, true, true); + break; + default: + return false; + } + return true; +} + +static void decode_opc_mxu__pool01(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 18, 3); + + switch (opcode) { + case OPC_MXU_S32SLT: + gen_mxu_S32SLT(ctx); + break; + case OPC_MXU_D16SLT: + gen_mxu_D16SLT(ctx); + break; + case OPC_MXU_D16AVG: + gen_mxu_d16avg(ctx, false); + break; + case OPC_MXU_D16AVGR: + gen_mxu_d16avg(ctx, true); + break; + case OPC_MXU_Q8AVG: + gen_mxu_q8avg(ctx, false); + break; + case OPC_MXU_Q8AVGR: + gen_mxu_q8avg(ctx, true); + break; + case OPC_MXU_Q8ADD: + gen_mxu_Q8ADD(ctx); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + break; + } +} + +static void decode_opc_mxu__pool02(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 18, 3); + + switch (opcode) { + case OPC_MXU_S32CPS: + gen_mxu_S32CPS(ctx); + break; + case OPC_MXU_D16CPS: + gen_mxu_D16CPS(ctx); + break; + case OPC_MXU_Q8ABD: + gen_mxu_Q8ABD(ctx); + break; + case OPC_MXU_Q16SAT: + gen_mxu_Q16SAT(ctx); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + break; + } +} + +static void decode_opc_mxu__pool03(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 24, 2); + + switch (opcode) { + case OPC_MXU_D16MULF: + gen_mxu_d16mul(ctx, true, true); + break; + case OPC_MXU_D16MULE: + gen_mxu_d16mul(ctx, true, false); + break; default: MIPS_INVAL("decode_opc_mxu"); gen_reserved_instruction(ctx); @@ -1491,12 +4537,215 @@ static void decode_opc_mxu__pool00(DisasContext *ctx) static void decode_opc_mxu__pool04(DisasContext *ctx) { - uint32_t opcode = extract32(ctx->opcode, 20, 1); + uint32_t reversed = extract32(ctx->opcode, 20, 1); + uint32_t opcode = extract32(ctx->opcode, 10, 4); + + /* Don't care about opcode bits as their meaning is unknown yet */ + switch (opcode) { + default: + gen_mxu_s32ldxx(ctx, reversed, false); + break; + } +} + +static void decode_opc_mxu__pool05(DisasContext *ctx) +{ + uint32_t reversed = extract32(ctx->opcode, 20, 1); + uint32_t opcode = extract32(ctx->opcode, 10, 4); + + /* Don't care about opcode bits as their meaning is unknown yet */ + switch (opcode) { + default: + gen_mxu_s32stxx(ctx, reversed, false); + break; + } +} + +static void decode_opc_mxu__pool06(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 10, 4); + uint32_t strd2 = extract32(ctx->opcode, 14, 2); switch (opcode) { - case OPC_MXU_S32LDD: - case OPC_MXU_S32LDDR: - gen_mxu_s32ldd_s32lddr(ctx); + case OPC_MXU_S32LDST: + case OPC_MXU_S32LDSTR: + if (strd2 <= 2) { + gen_mxu_s32ldxvx(ctx, opcode, false, strd2); + break; + } + /* fallthrough */ + default: + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + break; + } +} + +static void decode_opc_mxu__pool07(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 10, 4); + uint32_t strd2 = extract32(ctx->opcode, 14, 2); + + switch (opcode) { + case OPC_MXU_S32LDST: + case OPC_MXU_S32LDSTR: + if (strd2 <= 2) { + gen_mxu_s32stxvx(ctx, opcode, false, strd2); + break; + } + /* fallthrough */ + default: + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + break; + } +} + +static void decode_opc_mxu__pool08(DisasContext *ctx) +{ + uint32_t reversed = extract32(ctx->opcode, 20, 1); + uint32_t opcode = extract32(ctx->opcode, 10, 4); + + /* Don't care about opcode bits as their meaning is unknown yet */ + switch (opcode) { + default: + gen_mxu_s32ldxx(ctx, reversed, true); + break; + } +} + +static void decode_opc_mxu__pool09(DisasContext *ctx) +{ + uint32_t reversed = extract32(ctx->opcode, 20, 1); + uint32_t opcode = extract32(ctx->opcode, 10, 4); + + /* Don't care about opcode bits as their meaning is unknown yet */ + switch (opcode) { + default: + gen_mxu_s32stxx(ctx, reversed, true); + break; + } +} + +static void decode_opc_mxu__pool10(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 10, 4); + uint32_t strd2 = extract32(ctx->opcode, 14, 2); + + switch (opcode) { + case OPC_MXU_S32LDST: + case OPC_MXU_S32LDSTR: + if (strd2 <= 2) { + gen_mxu_s32ldxvx(ctx, opcode, true, strd2); + break; + } + /* fallthrough */ + default: + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + break; + } +} + +static void decode_opc_mxu__pool11(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 10, 4); + uint32_t strd2 = extract32(ctx->opcode, 14, 2); + + switch (opcode) { + case OPC_MXU_S32LDST: + case OPC_MXU_S32LDSTR: + if (strd2 <= 2) { + gen_mxu_s32stxvx(ctx, opcode, true, strd2); + break; + } + /* fallthrough */ + default: + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + break; + } +} + +static void decode_opc_mxu__pool12(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 22, 2); + + switch (opcode) { + case OPC_MXU_D32ACC: + gen_mxu_d32acc(ctx); + break; + case OPC_MXU_D32ACCM: + gen_mxu_d32accm(ctx); + break; + case OPC_MXU_D32ASUM: + gen_mxu_d32asum(ctx); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + break; + } +} + +static void decode_opc_mxu__pool13(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 22, 2); + + switch (opcode) { + case OPC_MXU_Q16ACC: + gen_mxu_q16acc(ctx); + break; + case OPC_MXU_Q16ACCM: + gen_mxu_q16accm(ctx); + break; + case OPC_MXU_D16ASUM: + gen_mxu_d16asum(ctx); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + break; + } +} + +static void decode_opc_mxu__pool14(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 22, 2); + + switch (opcode) { + case OPC_MXU_Q8ADDE: + gen_mxu_q8adde(ctx, false); + break; + case OPC_MXU_D8SUM: + gen_mxu_d8sum(ctx, false); + break; + case OPC_MXU_D8SUMC: + gen_mxu_d8sum(ctx, true); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + break; + } +} + +static void decode_opc_mxu__pool15(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 14, 2); + + switch (opcode) { + case OPC_MXU_S32MUL: + gen_mxu_s32mul(ctx, false); + break; + case OPC_MXU_S32MULU: + gen_mxu_s32mul(ctx, true); + break; + case OPC_MXU_S32EXTR: + gen_mxu_s32extr(ctx); + break; + case OPC_MXU_S32EXTRV: + gen_mxu_s32extrv(ctx); break; default: MIPS_INVAL("decode_opc_mxu"); @@ -1510,9 +4759,18 @@ static void decode_opc_mxu__pool16(DisasContext *ctx) uint32_t opcode = extract32(ctx->opcode, 18, 3); switch (opcode) { + case OPC_MXU_D32SARW: + gen_mxu_d32sarl(ctx, true); + break; + case OPC_MXU_S32ALN: + gen_mxu_S32ALN(ctx); + break; case OPC_MXU_S32ALNI: gen_mxu_S32ALNI(ctx); break; + case OPC_MXU_S32LUI: + gen_mxu_s32lui(ctx); + break; case OPC_MXU_S32NOR: gen_mxu_S32NOR(ctx); break; @@ -1532,14 +4790,62 @@ static void decode_opc_mxu__pool16(DisasContext *ctx) } } -static void decode_opc_mxu__pool19(DisasContext *ctx) +static void decode_opc_mxu__pool17(DisasContext *ctx) { - uint32_t opcode = extract32(ctx->opcode, 22, 2); + uint32_t opcode = extract32(ctx->opcode, 6, 3); + uint32_t strd2 = extract32(ctx->opcode, 9, 2); + + if (strd2 > 2) { + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + return; + } switch (opcode) { - case OPC_MXU_Q8MUL: - case OPC_MXU_Q8MULSU: - gen_mxu_q8mul_q8mulsu(ctx); + case OPC_MXU_LXW: + gen_mxu_lxx(ctx, strd2, MO_TE | MO_UL); + break; + case OPC_MXU_LXB: + gen_mxu_lxx(ctx, strd2, MO_TE | MO_SB); + break; + case OPC_MXU_LXH: + gen_mxu_lxx(ctx, strd2, MO_TE | MO_SW); + break; + case OPC_MXU_LXBU: + gen_mxu_lxx(ctx, strd2, MO_TE | MO_UB); + break; + case OPC_MXU_LXHU: + gen_mxu_lxx(ctx, strd2, MO_TE | MO_UW); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + break; + } +} + +static void decode_opc_mxu__pool18(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 18, 3); + + switch (opcode) { + case OPC_MXU_D32SLLV: + gen_mxu_d32sxxv(ctx, false, false); + break; + case OPC_MXU_D32SLRV: + gen_mxu_d32sxxv(ctx, true, false); + break; + case OPC_MXU_D32SARV: + gen_mxu_d32sxxv(ctx, true, true); + break; + case OPC_MXU_Q16SLLV: + gen_mxu_q16sxxv(ctx, false, false); + break; + case OPC_MXU_Q16SLRV: + gen_mxu_q16sxxv(ctx, true, false); + break; + case OPC_MXU_Q16SARV: + gen_mxu_q16sxxv(ctx, true, true); break; default: MIPS_INVAL("decode_opc_mxu"); @@ -1548,6 +4854,73 @@ static void decode_opc_mxu__pool19(DisasContext *ctx) } } +static void decode_opc_mxu__pool19(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 22, 4); + + switch (opcode) { + case OPC_MXU_Q8MUL: + gen_mxu_q8mul_mac(ctx, false, false); + break; + case OPC_MXU_Q8MULSU: + gen_mxu_q8mul_mac(ctx, true, false); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + break; + } +} + +static void decode_opc_mxu__pool20(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 18, 3); + + switch (opcode) { + case OPC_MXU_Q8MOVZ: + gen_mxu_q8movzn(ctx, TCG_COND_NE); + break; + case OPC_MXU_Q8MOVN: + gen_mxu_q8movzn(ctx, TCG_COND_EQ); + break; + case OPC_MXU_D16MOVZ: + gen_mxu_d16movzn(ctx, TCG_COND_NE); + break; + case OPC_MXU_D16MOVN: + gen_mxu_d16movzn(ctx, TCG_COND_EQ); + break; + case OPC_MXU_S32MOVZ: + gen_mxu_s32movzn(ctx, TCG_COND_NE); + break; + case OPC_MXU_S32MOVN: + gen_mxu_s32movzn(ctx, TCG_COND_EQ); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + break; + } +} + +static void decode_opc_mxu__pool21(DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 22, 2); + + switch (opcode) { + case OPC_MXU_Q8MAC: + gen_mxu_q8mul_mac(ctx, false, true); + break; + case OPC_MXU_Q8MACSU: + gen_mxu_q8mul_mac(ctx, true, true); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + gen_reserved_instruction(ctx); + break; + } +} + + bool decode_ase_mxu(DisasContext *ctx, uint32_t insn) { uint32_t opcode = extract32(insn, 0, 6); @@ -1571,34 +4944,166 @@ bool decode_ase_mxu(DisasContext *ctx, uint32_t insn) tcg_gen_brcondi_tl(TCG_COND_NE, t_mxu_cr, MXU_CR_MXU_EN, l_exit); switch (opcode) { + case OPC_MXU_S32MADD: + case OPC_MXU_S32MADDU: + case OPC_MXU_S32MSUB: + case OPC_MXU_S32MSUBU: + return decode_opc_mxu_s32madd_sub(ctx); case OPC_MXU__POOL00: decode_opc_mxu__pool00(ctx); break; case OPC_MXU_D16MUL: - gen_mxu_d16mul(ctx); + gen_mxu_d16mul(ctx, false, false); break; case OPC_MXU_D16MAC: - gen_mxu_d16mac(ctx); + gen_mxu_d16mac(ctx, false, false); + break; + case OPC_MXU_D16MACF: + gen_mxu_d16mac(ctx, true, true); + break; + case OPC_MXU_D16MADL: + gen_mxu_d16madl(ctx); + break; + case OPC_MXU_S16MAD: + gen_mxu_s16mad(ctx); + break; + case OPC_MXU_Q16ADD: + gen_mxu_q16add(ctx); + break; + case OPC_MXU_D16MACE: + gen_mxu_d16mac(ctx, true, false); + break; + case OPC_MXU__POOL01: + decode_opc_mxu__pool01(ctx); + break; + case OPC_MXU__POOL02: + decode_opc_mxu__pool02(ctx); + break; + case OPC_MXU__POOL03: + decode_opc_mxu__pool03(ctx); break; case OPC_MXU__POOL04: decode_opc_mxu__pool04(ctx); break; + case OPC_MXU__POOL05: + decode_opc_mxu__pool05(ctx); + break; + case OPC_MXU__POOL06: + decode_opc_mxu__pool06(ctx); + break; + case OPC_MXU__POOL07: + decode_opc_mxu__pool07(ctx); + break; + case OPC_MXU__POOL08: + decode_opc_mxu__pool08(ctx); + break; + case OPC_MXU__POOL09: + decode_opc_mxu__pool09(ctx); + break; + case OPC_MXU__POOL10: + decode_opc_mxu__pool10(ctx); + break; + case OPC_MXU__POOL11: + decode_opc_mxu__pool11(ctx); + break; + case OPC_MXU_D32ADD: + gen_mxu_d32add(ctx); + break; + case OPC_MXU__POOL12: + decode_opc_mxu__pool12(ctx); + break; + case OPC_MXU__POOL13: + decode_opc_mxu__pool13(ctx); + break; + case OPC_MXU__POOL14: + decode_opc_mxu__pool14(ctx); + break; + case OPC_MXU_Q8ACCE: + gen_mxu_q8adde(ctx, true); + break; case OPC_MXU_S8LDD: - gen_mxu_s8ldd(ctx); + gen_mxu_s8ldd(ctx, false); + break; + case OPC_MXU_S8STD: + gen_mxu_s8std(ctx, false); + break; + case OPC_MXU_S8LDI: + gen_mxu_s8ldd(ctx, true); + break; + case OPC_MXU_S8SDI: + gen_mxu_s8std(ctx, true); + break; + case OPC_MXU__POOL15: + decode_opc_mxu__pool15(ctx); break; case OPC_MXU__POOL16: decode_opc_mxu__pool16(ctx); break; + case OPC_MXU__POOL17: + decode_opc_mxu__pool17(ctx); + break; + case OPC_MXU_S16LDD: + gen_mxu_s16ldd(ctx, false); + break; + case OPC_MXU_S16STD: + gen_mxu_s16std(ctx, false); + break; + case OPC_MXU_S16LDI: + gen_mxu_s16ldd(ctx, true); + break; + case OPC_MXU_S16SDI: + gen_mxu_s16std(ctx, true); + break; + case OPC_MXU_D32SLL: + gen_mxu_d32sxx(ctx, false, false); + break; + case OPC_MXU_D32SLR: + gen_mxu_d32sxx(ctx, true, false); + break; + case OPC_MXU_D32SARL: + gen_mxu_d32sarl(ctx, false); + break; + case OPC_MXU_D32SAR: + gen_mxu_d32sxx(ctx, true, true); + break; + case OPC_MXU_Q16SLL: + gen_mxu_q16sxx(ctx, false, false); + break; + case OPC_MXU__POOL18: + decode_opc_mxu__pool18(ctx); + break; + case OPC_MXU_Q16SLR: + gen_mxu_q16sxx(ctx, true, false); + break; + case OPC_MXU_Q16SAR: + gen_mxu_q16sxx(ctx, true, true); + break; case OPC_MXU__POOL19: decode_opc_mxu__pool19(ctx); break; + case OPC_MXU__POOL20: + decode_opc_mxu__pool20(ctx); + break; + case OPC_MXU__POOL21: + decode_opc_mxu__pool21(ctx); + break; + case OPC_MXU_Q16SCOP: + gen_mxu_q16scop(ctx); + break; + case OPC_MXU_Q8MADL: + gen_mxu_q8madl(ctx); + break; + case OPC_MXU_S32SFL: + gen_mxu_s32sfl(ctx); + break; + case OPC_MXU_Q8SAD: + gen_mxu_q8sad(ctx); + break; default: - MIPS_INVAL("decode_opc_mxu"); - gen_reserved_instruction(ctx); + return false; } gen_set_label(l_exit); - tcg_temp_free(t_mxu_cr); } return true; diff --git a/target/mips/tcg/nanomips_translate.c.inc b/target/mips/tcg/nanomips_translate.c.inc index 916cece4d2..b4b746d418 100644 --- a/target/mips/tcg/nanomips_translate.c.inc +++ b/target/mips/tcg/nanomips_translate.c.inc @@ -998,27 +998,23 @@ static void gen_llwp(DisasContext *ctx, uint32_t base, int16_t offset, TCGv tmp2 = tcg_temp_new(); gen_base_offset_addr(ctx, taddr, base, offset); - tcg_gen_qemu_ld64(tval, taddr, ctx->mem_idx); + tcg_gen_qemu_ld_i64(tval, taddr, ctx->mem_idx, MO_TEUQ | MO_ALIGN); if (cpu_is_bigendian(ctx)) { tcg_gen_extr_i64_tl(tmp2, tmp1, tval); } else { tcg_gen_extr_i64_tl(tmp1, tmp2, tval); } gen_store_gpr(tmp1, reg1); - tcg_temp_free(tmp1); gen_store_gpr(tmp2, reg2); - tcg_temp_free(tmp2); - tcg_gen_st_i64(tval, cpu_env, offsetof(CPUMIPSState, llval_wp)); - tcg_temp_free_i64(tval); - tcg_gen_st_tl(taddr, cpu_env, offsetof(CPUMIPSState, lladdr)); - tcg_temp_free(taddr); + tcg_gen_st_i64(tval, tcg_env, offsetof(CPUMIPSState, llval_wp)); + tcg_gen_st_tl(taddr, tcg_env, offsetof(CPUMIPSState, lladdr)); } static void gen_scwp(DisasContext *ctx, uint32_t base, int16_t offset, uint32_t reg1, uint32_t reg2, bool eva) { - TCGv taddr = tcg_temp_local_new(); - TCGv lladdr = tcg_temp_local_new(); + TCGv taddr = tcg_temp_new(); + TCGv lladdr = tcg_temp_new(); TCGv_i64 tval = tcg_temp_new_i64(); TCGv_i64 llval = tcg_temp_new_i64(); TCGv_i64 val = tcg_temp_new_i64(); @@ -1029,7 +1025,7 @@ static void gen_scwp(DisasContext *ctx, uint32_t base, int16_t offset, gen_base_offset_addr(ctx, taddr, base, offset); - tcg_gen_ld_tl(lladdr, cpu_env, offsetof(CPUMIPSState, lladdr)); + tcg_gen_ld_tl(lladdr, tcg_env, offsetof(CPUMIPSState, lladdr)); tcg_gen_brcond_tl(TCG_COND_NE, taddr, lladdr, lab_fail); gen_load_gpr(tmp1, reg1); @@ -1041,9 +1037,10 @@ static void gen_scwp(DisasContext *ctx, uint32_t base, int16_t offset, tcg_gen_concat_tl_i64(tval, tmp1, tmp2); } - tcg_gen_ld_i64(llval, cpu_env, offsetof(CPUMIPSState, llval_wp)); + tcg_gen_ld_i64(llval, tcg_env, offsetof(CPUMIPSState, llval_wp)); tcg_gen_atomic_cmpxchg_i64(val, taddr, llval, tval, - eva ? MIPS_HFLAG_UM : ctx->mem_idx, MO_64); + eva ? MIPS_HFLAG_UM : ctx->mem_idx, + MO_64 | MO_ALIGN); if (reg1 != 0) { tcg_gen_movi_tl(cpu_gpr[reg1], 1); } @@ -1056,7 +1053,7 @@ static void gen_scwp(DisasContext *ctx, uint32_t base, int16_t offset, } gen_set_label(lab_done); tcg_gen_movi_tl(lladdr, -1); - tcg_gen_st_tl(lladdr, cpu_env, offsetof(CPUMIPSState, lladdr)); + tcg_gen_st_tl(lladdr, tcg_env, offsetof(CPUMIPSState, lladdr)); } static void gen_adjust_sp(DisasContext *ctx, int u) @@ -1084,9 +1081,6 @@ static void gen_save(DisasContext *ctx, uint8_t rt, uint8_t count, /* adjust stack pointer */ gen_adjust_sp(ctx, -u); - - tcg_temp_free(t0); - tcg_temp_free(va); } static void gen_restore(DisasContext *ctx, uint8_t rt, uint8_t count, @@ -1110,9 +1104,6 @@ static void gen_restore(DisasContext *ctx, uint8_t rt, uint8_t count, /* adjust stack pointer */ gen_adjust_sp(ctx, u); - - tcg_temp_free(t0); - tcg_temp_free(va); } static void gen_compute_branch_nm(DisasContext *ctx, uint32_t opc, @@ -1232,8 +1223,6 @@ static void gen_compute_branch_nm(DisasContext *ctx, uint32_t opc, if (insn_bytes == 2) { ctx->hflags |= MIPS_HFLAG_B16; } - tcg_temp_free(t0); - tcg_temp_free(t1); } static void gen_pool16c_nanomips_insn(DisasContext *ctx) @@ -1346,19 +1335,18 @@ static void gen_pool32a0_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) case NM_DVP: if (ctx->vp) { check_cp0_enabled(ctx); - gen_helper_dvp(t0, cpu_env); + gen_helper_dvp(t0, tcg_env); gen_store_gpr(t0, rt); } break; case NM_EVP: if (ctx->vp) { check_cp0_enabled(ctx); - gen_helper_evp(t0, cpu_env); + gen_helper_evp(t0, tcg_env); gen_store_gpr(t0, rt); } break; } - tcg_temp_free(t0); #endif } else { gen_slt(ctx, OPC_SLTU, rd, rs, rt); @@ -1381,10 +1369,6 @@ static void gen_pool32a0_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) /* operands of same sign, result different sign */ tcg_gen_setcondi_tl(TCG_COND_LT, t0, t1, 0); gen_store_gpr(t0, rd); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); } break; case NM_MUL: @@ -1427,7 +1411,6 @@ static void gen_pool32a0_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) gen_load_gpr(t0, rt); gen_mtc0(ctx, t0, rs, extract32(ctx->opcode, 11, 3)); - tcg_temp_free(t0); } break; case NM_D_E_MT_VPE: @@ -1445,7 +1428,7 @@ static void gen_pool32a0_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) } else if (rs == 0) { /* DVPE */ check_cp0_mt(ctx); - gen_helper_dvpe(t0, cpu_env); + gen_helper_dvpe(t0, tcg_env); gen_store_gpr(t0, rt); } else { gen_reserved_instruction(ctx); @@ -1460,15 +1443,13 @@ static void gen_pool32a0_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) } else if (rs == 0) { /* EVPE */ check_cp0_mt(ctx); - gen_helper_evpe(t0, cpu_env); + gen_helper_evpe(t0, tcg_env); gen_store_gpr(t0, rt); } else { gen_reserved_instruction(ctx); } break; } - - tcg_temp_free(t0); } break; case NM_FORK: @@ -1480,8 +1461,6 @@ static void gen_pool32a0_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) gen_load_gpr(t0, rt); gen_load_gpr(t1, rs); gen_helper_fork(t0, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); } break; case NM_MFTR: @@ -1506,9 +1485,8 @@ static void gen_pool32a0_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) TCGv t0 = tcg_temp_new(); gen_load_gpr(t0, rs); - gen_helper_yield(t0, cpu_env, t0); + gen_helper_yield(t0, tcg_env, t0); gen_store_gpr(t0, rt); - tcg_temp_free(t0); } break; #endif @@ -1539,29 +1517,24 @@ static void gen_pool32axf_1_5_nanomips_insn(DisasContext *ctx, uint32_t opc, switch (opc) { case NM_MAQ_S_W_PHR: check_dsp(ctx); - gen_helper_maq_s_w_phr(t0, v1_t, v0_t, cpu_env); + gen_helper_maq_s_w_phr(t0, v1_t, v0_t, tcg_env); break; case NM_MAQ_S_W_PHL: check_dsp(ctx); - gen_helper_maq_s_w_phl(t0, v1_t, v0_t, cpu_env); + gen_helper_maq_s_w_phl(t0, v1_t, v0_t, tcg_env); break; case NM_MAQ_SA_W_PHR: check_dsp(ctx); - gen_helper_maq_sa_w_phr(t0, v1_t, v0_t, cpu_env); + gen_helper_maq_sa_w_phr(t0, v1_t, v0_t, tcg_env); break; case NM_MAQ_SA_W_PHL: check_dsp(ctx); - gen_helper_maq_sa_w_phl(t0, v1_t, v0_t, cpu_env); + gen_helper_maq_sa_w_phl(t0, v1_t, v0_t, tcg_env); break; default: gen_reserved_instruction(ctx); break; } - - tcg_temp_free_i32(t0); - - tcg_temp_free(v0_t); - tcg_temp_free(v1_t); } @@ -1597,12 +1570,12 @@ static void gen_pool32axf_1_nanomips_insn(DisasContext *ctx, uint32_t opc, check_dsp(ctx); switch (extract32(ctx->opcode, 12, 2)) { case NM_MTHLIP: - tcg_gen_movi_tl(t0, v2); - gen_helper_mthlip(t0, v0_t, cpu_env); + tcg_gen_movi_tl(t0, v2 >> 3); + gen_helper_mthlip(t0, v0_t, tcg_env); break; case NM_SHILOV: tcg_gen_movi_tl(t0, v2 >> 3); - gen_helper_shilo(t0, v0_t, cpu_env); + gen_helper_shilo(t0, v0_t, tcg_env); break; default: gen_reserved_instruction(ctx); @@ -1615,24 +1588,24 @@ static void gen_pool32axf_1_nanomips_insn(DisasContext *ctx, uint32_t opc, switch (extract32(ctx->opcode, 12, 2)) { case NM_RDDSP: tcg_gen_movi_tl(t0, imm); - gen_helper_rddsp(t0, t0, cpu_env); + gen_helper_rddsp(t0, t0, tcg_env); gen_store_gpr(t0, ret); break; case NM_WRDSP: gen_load_gpr(t0, ret); tcg_gen_movi_tl(t1, imm); - gen_helper_wrdsp(t0, t1, cpu_env); + gen_helper_wrdsp(t0, t1, tcg_env); break; case NM_EXTP: tcg_gen_movi_tl(t0, v2 >> 3); tcg_gen_movi_tl(t1, v1); - gen_helper_extp(t0, t0, t1, cpu_env); + gen_helper_extp(t0, t0, t1, tcg_env); gen_store_gpr(t0, ret); break; case NM_EXTPDP: tcg_gen_movi_tl(t0, v2 >> 3); tcg_gen_movi_tl(t1, v1); - gen_helper_extpdp(t0, t0, t1, cpu_env); + gen_helper_extpdp(t0, t0, t1, tcg_env); gen_store_gpr(t0, ret); break; } @@ -1642,7 +1615,7 @@ static void gen_pool32axf_1_nanomips_insn(DisasContext *ctx, uint32_t opc, tcg_gen_movi_tl(t0, v2 >> 2); switch (extract32(ctx->opcode, 12, 1)) { case NM_SHLL_QB: - gen_helper_shll_qb(t0, t0, v0_t, cpu_env); + gen_helper_shll_qb(t0, t0, v0_t, tcg_env); gen_store_gpr(t0, ret); break; case NM_SHRL_QB: @@ -1661,19 +1634,19 @@ static void gen_pool32axf_1_nanomips_insn(DisasContext *ctx, uint32_t opc, tcg_gen_movi_tl(t1, v1); switch (extract32(ctx->opcode, 12, 2)) { case NM_EXTR_W: - gen_helper_extr_w(t0, t0, t1, cpu_env); + gen_helper_extr_w(t0, t0, t1, tcg_env); gen_store_gpr(t0, ret); break; case NM_EXTR_R_W: - gen_helper_extr_r_w(t0, t0, t1, cpu_env); + gen_helper_extr_r_w(t0, t0, t1, tcg_env); gen_store_gpr(t0, ret); break; case NM_EXTR_RS_W: - gen_helper_extr_rs_w(t0, t0, t1, cpu_env); + gen_helper_extr_rs_w(t0, t0, t1, tcg_env); gen_store_gpr(t0, ret); break; case NM_EXTR_S_H: - gen_helper_extr_s_h(t0, t0, t1, cpu_env); + gen_helper_extr_s_h(t0, t0, t1, tcg_env); gen_store_gpr(t0, ret); break; } @@ -1682,10 +1655,6 @@ static void gen_pool32axf_1_nanomips_insn(DisasContext *ctx, uint32_t opc, gen_reserved_instruction(ctx); break; } - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(v0_t); } static void gen_pool32axf_2_multiply(DisasContext *ctx, uint32_t opc, @@ -1702,19 +1671,19 @@ static void gen_pool32axf_2_multiply(DisasContext *ctx, uint32_t opc, switch (extract32(ctx->opcode, 9, 3)) { case NM_DPA_W_PH: check_dsp_r2(ctx); - gen_helper_dpa_w_ph(t0, v1, v0, cpu_env); + gen_helper_dpa_w_ph(t0, v1, v0, tcg_env); break; case NM_DPAQ_S_W_PH: check_dsp(ctx); - gen_helper_dpaq_s_w_ph(t0, v1, v0, cpu_env); + gen_helper_dpaq_s_w_ph(t0, v1, v0, tcg_env); break; case NM_DPS_W_PH: check_dsp_r2(ctx); - gen_helper_dps_w_ph(t0, v1, v0, cpu_env); + gen_helper_dps_w_ph(t0, v1, v0, tcg_env); break; case NM_DPSQ_S_W_PH: check_dsp(ctx); - gen_helper_dpsq_s_w_ph(t0, v1, v0, cpu_env); + gen_helper_dpsq_s_w_ph(t0, v1, v0, tcg_env); break; default: gen_reserved_instruction(ctx); @@ -1725,19 +1694,19 @@ static void gen_pool32axf_2_multiply(DisasContext *ctx, uint32_t opc, switch (extract32(ctx->opcode, 9, 3)) { case NM_DPAX_W_PH: check_dsp_r2(ctx); - gen_helper_dpax_w_ph(t0, v0, v1, cpu_env); + gen_helper_dpax_w_ph(t0, v0, v1, tcg_env); break; case NM_DPAQ_SA_L_W: check_dsp(ctx); - gen_helper_dpaq_sa_l_w(t0, v0, v1, cpu_env); + gen_helper_dpaq_sa_l_w(t0, v0, v1, tcg_env); break; case NM_DPSX_W_PH: check_dsp_r2(ctx); - gen_helper_dpsx_w_ph(t0, v0, v1, cpu_env); + gen_helper_dpsx_w_ph(t0, v0, v1, tcg_env); break; case NM_DPSQ_SA_L_W: check_dsp(ctx); - gen_helper_dpsq_sa_l_w(t0, v0, v1, cpu_env); + gen_helper_dpsq_sa_l_w(t0, v0, v1, tcg_env); break; default: gen_reserved_instruction(ctx); @@ -1748,23 +1717,23 @@ static void gen_pool32axf_2_multiply(DisasContext *ctx, uint32_t opc, switch (extract32(ctx->opcode, 9, 3)) { case NM_DPAU_H_QBL: check_dsp(ctx); - gen_helper_dpau_h_qbl(t0, v0, v1, cpu_env); + gen_helper_dpau_h_qbl(t0, v0, v1, tcg_env); break; case NM_DPAQX_S_W_PH: check_dsp_r2(ctx); - gen_helper_dpaqx_s_w_ph(t0, v0, v1, cpu_env); + gen_helper_dpaqx_s_w_ph(t0, v0, v1, tcg_env); break; case NM_DPSU_H_QBL: check_dsp(ctx); - gen_helper_dpsu_h_qbl(t0, v0, v1, cpu_env); + gen_helper_dpsu_h_qbl(t0, v0, v1, tcg_env); break; case NM_DPSQX_S_W_PH: check_dsp_r2(ctx); - gen_helper_dpsqx_s_w_ph(t0, v0, v1, cpu_env); + gen_helper_dpsqx_s_w_ph(t0, v0, v1, tcg_env); break; case NM_MULSA_W_PH: check_dsp_r2(ctx); - gen_helper_mulsa_w_ph(t0, v0, v1, cpu_env); + gen_helper_mulsa_w_ph(t0, v0, v1, tcg_env); break; default: gen_reserved_instruction(ctx); @@ -1775,23 +1744,23 @@ static void gen_pool32axf_2_multiply(DisasContext *ctx, uint32_t opc, switch (extract32(ctx->opcode, 9, 3)) { case NM_DPAU_H_QBR: check_dsp(ctx); - gen_helper_dpau_h_qbr(t0, v1, v0, cpu_env); + gen_helper_dpau_h_qbr(t0, v1, v0, tcg_env); break; case NM_DPAQX_SA_W_PH: check_dsp_r2(ctx); - gen_helper_dpaqx_sa_w_ph(t0, v1, v0, cpu_env); + gen_helper_dpaqx_sa_w_ph(t0, v1, v0, tcg_env); break; case NM_DPSU_H_QBR: check_dsp(ctx); - gen_helper_dpsu_h_qbr(t0, v1, v0, cpu_env); + gen_helper_dpsu_h_qbr(t0, v1, v0, tcg_env); break; case NM_DPSQX_SA_W_PH: check_dsp_r2(ctx); - gen_helper_dpsqx_sa_w_ph(t0, v1, v0, cpu_env); + gen_helper_dpsqx_sa_w_ph(t0, v1, v0, tcg_env); break; case NM_MULSAQ_S_W_PH: check_dsp(ctx); - gen_helper_mulsaq_s_w_ph(t0, v1, v0, cpu_env); + gen_helper_mulsaq_s_w_ph(t0, v1, v0, tcg_env); break; default: gen_reserved_instruction(ctx); @@ -1802,8 +1771,6 @@ static void gen_pool32axf_2_multiply(DisasContext *ctx, uint32_t opc, gen_reserved_instruction(ctx); break; } - - tcg_temp_free_i32(t0); } static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, @@ -1855,10 +1822,8 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, tcg_gen_mul_i64(t2, t2, t3); tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); tcg_gen_add_i64(t2, t2, t3); - tcg_temp_free_i64(t3); gen_move_low32(cpu_LO[acc], t2); gen_move_high32(cpu_HI[acc], t2); - tcg_temp_free_i64(t2); } break; case NM_MULT: @@ -1878,15 +1843,13 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, tcg_gen_muls2_i32(t2, t3, t2, t3); tcg_gen_ext_i32_tl(cpu_LO[acc], t2); tcg_gen_ext_i32_tl(cpu_HI[acc], t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); } break; case NM_EXTRV_W: check_dsp(ctx); gen_load_gpr(v1_t, rs); tcg_gen_movi_tl(t0, rd >> 3); - gen_helper_extr_w(t0, t0, v1_t, cpu_env); + gen_helper_extr_w(t0, t0, v1_t, tcg_env); gen_store_gpr(t0, ret); break; } @@ -1915,10 +1878,8 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, tcg_gen_mul_i64(t2, t2, t3); tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); tcg_gen_add_i64(t2, t2, t3); - tcg_temp_free_i64(t3); gen_move_low32(cpu_LO[acc], t2); gen_move_high32(cpu_HI[acc], t2); - tcg_temp_free_i64(t2); } break; case NM_MULTU: @@ -1938,14 +1899,12 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, tcg_gen_mulu2_i32(t2, t3, t2, t3); tcg_gen_ext_i32_tl(cpu_LO[acc], t2); tcg_gen_ext_i32_tl(cpu_HI[acc], t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); } break; case NM_EXTRV_R_W: check_dsp(ctx); tcg_gen_movi_tl(t0, rd >> 3); - gen_helper_extr_r_w(t0, t0, v1_t, cpu_env); + gen_helper_extr_r_w(t0, t0, v1_t, tcg_env); gen_store_gpr(t0, ret); break; default: @@ -1965,7 +1924,7 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, case NM_EXTPV: check_dsp(ctx); tcg_gen_movi_tl(t0, rd >> 3); - gen_helper_extp(t0, t0, v1_t, cpu_env); + gen_helper_extp(t0, t0, v1_t, tcg_env); gen_store_gpr(t0, ret); break; case NM_MSUB: @@ -1982,16 +1941,14 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, tcg_gen_mul_i64(t2, t2, t3); tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); tcg_gen_sub_i64(t2, t3, t2); - tcg_temp_free_i64(t3); gen_move_low32(cpu_LO[acc], t2); gen_move_high32(cpu_HI[acc], t2); - tcg_temp_free_i64(t2); } break; case NM_EXTRV_RS_W: check_dsp(ctx); tcg_gen_movi_tl(t0, rd >> 3); - gen_helper_extr_rs_w(t0, t0, v1_t, cpu_env); + gen_helper_extr_rs_w(t0, t0, v1_t, tcg_env); gen_store_gpr(t0, ret); break; } @@ -2008,7 +1965,7 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, case NM_EXTPDPV: check_dsp(ctx); tcg_gen_movi_tl(t0, rd >> 3); - gen_helper_extpdp(t0, t0, v1_t, cpu_env); + gen_helper_extpdp(t0, t0, v1_t, tcg_env); gen_store_gpr(t0, ret); break; case NM_MSUBU: @@ -2027,16 +1984,14 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, tcg_gen_mul_i64(t2, t2, t3); tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); tcg_gen_sub_i64(t2, t3, t2); - tcg_temp_free_i64(t3); gen_move_low32(cpu_LO[acc], t2); gen_move_high32(cpu_HI[acc], t2); - tcg_temp_free_i64(t2); } break; case NM_EXTRV_S_H: check_dsp(ctx); tcg_gen_movi_tl(t0, rd >> 3); - gen_helper_extr_s_h(t0, t0, v0_t, cpu_env); + gen_helper_extr_s_h(t0, t0, v1_t, tcg_env); gen_store_gpr(t0, ret); break; } @@ -2045,12 +2000,6 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, gen_reserved_instruction(ctx); break; } - - tcg_temp_free(t0); - tcg_temp_free(t1); - - tcg_temp_free(v0_t); - tcg_temp_free(v1_t); } static void gen_pool32axf_4_nanomips_insn(DisasContext *ctx, uint32_t opc, @@ -2065,17 +2014,17 @@ static void gen_pool32axf_4_nanomips_insn(DisasContext *ctx, uint32_t opc, switch (opc) { case NM_ABSQ_S_QB: check_dsp_r2(ctx); - gen_helper_absq_s_qb(v0_t, v0_t, cpu_env); + gen_helper_absq_s_qb(v0_t, v0_t, tcg_env); gen_store_gpr(v0_t, ret); break; case NM_ABSQ_S_PH: check_dsp(ctx); - gen_helper_absq_s_ph(v0_t, v0_t, cpu_env); + gen_helper_absq_s_ph(v0_t, v0_t, tcg_env); gen_store_gpr(v0_t, ret); break; case NM_ABSQ_S_W: check_dsp(ctx); - gen_helper_absq_s_w(v0_t, v0_t, cpu_env); + gen_helper_absq_s_w(v0_t, v0_t, tcg_env); gen_store_gpr(v0_t, ret); break; case NM_PRECEQ_W_PHL: @@ -2160,9 +2109,8 @@ static void gen_pool32axf_4_nanomips_insn(DisasContext *ctx, uint32_t opc, TCGv tv0 = tcg_temp_new(); gen_load_gpr(tv0, rt); - gen_helper_insv(v0_t, cpu_env, v0_t, tv0); + gen_helper_insv(v0_t, tcg_env, v0_t, tv0); gen_store_gpr(v0_t, ret); - tcg_temp_free(tv0); } break; case NM_RADDU_W_QB: @@ -2188,9 +2136,6 @@ static void gen_pool32axf_4_nanomips_insn(DisasContext *ctx, uint32_t opc, gen_reserved_instruction(ctx); break; } - - tcg_temp_free(v0_t); - tcg_temp_free(t0); } static void gen_pool32axf_7_nanomips_insn(DisasContext *ctx, uint32_t opc, @@ -2243,8 +2188,6 @@ static void gen_pool32axf_7_nanomips_insn(DisasContext *ctx, uint32_t opc, gen_reserved_instruction(ctx); break; } - tcg_temp_free(t0); - tcg_temp_free(rs_t); } @@ -2300,11 +2243,10 @@ static void gen_pool32axf_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) TCGv t0 = tcg_temp_new(); save_cpu_state(ctx, 1); - gen_helper_di(t0, cpu_env); + gen_helper_di(t0, tcg_env); gen_store_gpr(t0, rt); /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; - tcg_temp_free(t0); } break; case NM_EI: @@ -2313,11 +2255,10 @@ static void gen_pool32axf_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) TCGv t0 = tcg_temp_new(); save_cpu_state(ctx, 1); - gen_helper_ei(t0, cpu_env); + gen_helper_ei(t0, tcg_env); gen_store_gpr(t0, rt); /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; - tcg_temp_free(t0); } break; case NM_RDPGPR: @@ -2374,7 +2315,7 @@ static void gen_compute_imm_branch(DisasContext *ctx, uint32_t opc, /* Unconditional branch */ } else if (rt == 0 && imm != 0) { /* Treat as NOP */ - goto out; + return; } else { cond = TCG_COND_EQ; } @@ -2384,12 +2325,12 @@ static void gen_compute_imm_branch(DisasContext *ctx, uint32_t opc, check_nms(ctx); if (imm >= 32 && !(ctx->hflags & MIPS_HFLAG_64)) { gen_reserved_instruction(ctx); - goto out; + return; } else if (rt == 0 && opc == NM_BBEQZC) { /* Unconditional branch */ } else if (rt == 0 && opc == NM_BBNEZC) { /* Treat as NOP */ - goto out; + return; } else { tcg_gen_shri_tl(t0, t0, imm); tcg_gen_andi_tl(t0, t0, 1); @@ -2404,7 +2345,7 @@ static void gen_compute_imm_branch(DisasContext *ctx, uint32_t opc, case NM_BNEIC: if (rt == 0 && imm == 0) { /* Treat as NOP */ - goto out; + return; } else if (rt == 0 && imm != 0) { /* Unconditional branch */ } else { @@ -2434,7 +2375,7 @@ static void gen_compute_imm_branch(DisasContext *ctx, uint32_t opc, default: MIPS_INVAL("Immediate Value Compact branch"); gen_reserved_instruction(ctx); - goto out; + return; } /* branch completion */ @@ -2455,10 +2396,6 @@ static void gen_compute_imm_branch(DisasContext *ctx, uint32_t opc, gen_goto_tb(ctx, 0, ctx->base.pc_next + 4); } - -out: - tcg_temp_free(t0); - tcg_temp_free(t1); } /* P.BALRSC type nanoMIPS R6 branches: BALRSC and BRSC */ @@ -2488,9 +2425,6 @@ static void gen_compute_nanomips_pbalrsc_branch(DisasContext *ctx, int rs, /* unconditional branch to register */ tcg_gen_mov_tl(cpu_PC, btarget); tcg_gen_lookup_and_goto_ptr(); - - tcg_temp_free(t0); - tcg_temp_free(t1); } /* nanoMIPS Branches */ @@ -2540,14 +2474,12 @@ static void gen_compute_compact_branch_nm(DisasContext *ctx, uint32_t opc, gen_load_gpr(tbase, rt); tcg_gen_movi_tl(toffset, offset); gen_op_addr_add(ctx, btarget, tbase, toffset); - tcg_temp_free(tbase); - tcg_temp_free(toffset); } break; default: MIPS_INVAL("Compact branch/jump"); gen_reserved_instruction(ctx); - goto out; + return; } if (bcond_compute == 0) { @@ -2559,7 +2491,7 @@ static void gen_compute_compact_branch_nm(DisasContext *ctx, uint32_t opc, default: MIPS_INVAL("Compact branch/jump"); gen_reserved_instruction(ctx); - goto out; + return; } } else { /* Conditional compact branch */ @@ -2620,7 +2552,7 @@ static void gen_compute_compact_branch_nm(DisasContext *ctx, uint32_t opc, default: MIPS_INVAL("Compact conditional branch/jump"); gen_reserved_instruction(ctx); - goto out; + return; } /* branch completion */ @@ -2633,10 +2565,6 @@ static void gen_compute_compact_branch_nm(DisasContext *ctx, uint32_t opc, gen_goto_tb(ctx, 0, ctx->base.pc_next + 4); } - -out: - tcg_temp_free(t0); - tcg_temp_free(t1); } @@ -2664,15 +2592,12 @@ static void gen_compute_branch_cp1_nm(DisasContext *ctx, uint32_t op, default: MIPS_INVAL("cp1 cond branch"); gen_reserved_instruction(ctx); - goto out; + return; } tcg_gen_trunc_i64_tl(bcond, t0); ctx->btarget = btarget; - -out: - tcg_temp_free_i64(t0); } @@ -2707,58 +2632,58 @@ static void gen_p_lsx(DisasContext *ctx, int rd, int rs, int rt) case NM_SDC1XS: tcg_gen_shli_tl(t0, t0, 3); break; + default: + gen_reserved_instruction(ctx); + return; } } gen_op_addr_add(ctx, t0, t0, t1); switch (extract32(ctx->opcode, 7, 4)) { case NM_LBX: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, - MO_SB); + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_SB); gen_store_gpr(t0, rd); break; case NM_LHX: /*case NM_LHXS:*/ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, - MO_TESW); + MO_TESW | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rd); break; case NM_LWX: /*case NM_LWXS:*/ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, - MO_TESL); + MO_TESL | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rd); break; case NM_LBUX: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, - MO_UB); + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_UB); gen_store_gpr(t0, rd); break; case NM_LHUX: /*case NM_LHUXS:*/ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, - MO_TEUW); + MO_TEUW | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rd); break; case NM_SBX: check_nms(ctx); gen_load_gpr(t1, rd); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, - MO_8); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_8); break; case NM_SHX: /*case NM_SHXS:*/ check_nms(ctx); gen_load_gpr(t1, rd); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, - MO_TEUW); + MO_TEUW | ctx->default_tcg_memop_mask); break; case NM_SWX: /*case NM_SWXS:*/ check_nms(ctx); gen_load_gpr(t1, rd); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, - MO_TEUL); + MO_TEUL | ctx->default_tcg_memop_mask); break; case NM_LWC1X: /*case NM_LWC1XS:*/ @@ -2796,9 +2721,6 @@ static void gen_p_lsx(DisasContext *ctx, int rd, int rs, int rt) gen_reserved_instruction(ctx); break; } - - tcg_temp_free(t0); - tcg_temp_free(t1); } static void gen_pool32f_nanomips_insn(DisasContext *ctx) @@ -3114,27 +3036,27 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, switch (opc) { case NM_CMP_EQ_PH: check_dsp(ctx); - gen_helper_cmp_eq_ph(v1_t, v2_t, cpu_env); + gen_helper_cmp_eq_ph(v1_t, v2_t, tcg_env); break; case NM_CMP_LT_PH: check_dsp(ctx); - gen_helper_cmp_lt_ph(v1_t, v2_t, cpu_env); + gen_helper_cmp_lt_ph(v1_t, v2_t, tcg_env); break; case NM_CMP_LE_PH: check_dsp(ctx); - gen_helper_cmp_le_ph(v1_t, v2_t, cpu_env); + gen_helper_cmp_le_ph(v1_t, v2_t, tcg_env); break; case NM_CMPU_EQ_QB: check_dsp(ctx); - gen_helper_cmpu_eq_qb(v1_t, v2_t, cpu_env); + gen_helper_cmpu_eq_qb(v1_t, v2_t, tcg_env); break; case NM_CMPU_LT_QB: check_dsp(ctx); - gen_helper_cmpu_lt_qb(v1_t, v2_t, cpu_env); + gen_helper_cmpu_lt_qb(v1_t, v2_t, tcg_env); break; case NM_CMPU_LE_QB: check_dsp(ctx); - gen_helper_cmpu_le_qb(v1_t, v2_t, cpu_env); + gen_helper_cmpu_le_qb(v1_t, v2_t, tcg_env); break; case NM_CMPGU_EQ_QB: check_dsp(ctx); @@ -3176,32 +3098,32 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, break; case NM_PICK_QB: check_dsp(ctx); - gen_helper_pick_qb(v1_t, v1_t, v2_t, cpu_env); + gen_helper_pick_qb(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_PICK_PH: check_dsp(ctx); - gen_helper_pick_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_pick_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_ADDQ_S_W: check_dsp(ctx); - gen_helper_addq_s_w(v1_t, v1_t, v2_t, cpu_env); + gen_helper_addq_s_w(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_SUBQ_S_W: check_dsp(ctx); - gen_helper_subq_s_w(v1_t, v1_t, v2_t, cpu_env); + gen_helper_subq_s_w(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_ADDSC: check_dsp(ctx); - gen_helper_addsc(v1_t, v1_t, v2_t, cpu_env); + gen_helper_addsc(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_ADDWC: check_dsp(ctx); - gen_helper_addwc(v1_t, v1_t, v2_t, cpu_env); + gen_helper_addwc(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_ADDQ_S_PH: @@ -3209,12 +3131,12 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, switch (extract32(ctx->opcode, 10, 1)) { case 0: /* ADDQ_PH */ - gen_helper_addq_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_addq_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case 1: /* ADDQ_S_PH */ - gen_helper_addq_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_addq_s_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; } @@ -3254,12 +3176,12 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, switch (extract32(ctx->opcode, 10, 1)) { case 0: /* ADDU_QB */ - gen_helper_addu_qb(v1_t, v1_t, v2_t, cpu_env); + gen_helper_addu_qb(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case 1: /* ADDU_S_QB */ - gen_helper_addu_s_qb(v1_t, v1_t, v2_t, cpu_env); + gen_helper_addu_s_qb(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; } @@ -3269,12 +3191,12 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, switch (extract32(ctx->opcode, 10, 1)) { case 0: /* ADDU_PH */ - gen_helper_addu_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_addu_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case 1: /* ADDU_S_PH */ - gen_helper_addu_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_addu_s_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; } @@ -3329,12 +3251,12 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, switch (extract32(ctx->opcode, 10, 1)) { case 0: /* SUBQ_PH */ - gen_helper_subq_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_subq_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case 1: /* SUBQ_S_PH */ - gen_helper_subq_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_subq_s_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; } @@ -3374,12 +3296,12 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, switch (extract32(ctx->opcode, 10, 1)) { case 0: /* SUBU_QB */ - gen_helper_subu_qb(v1_t, v1_t, v2_t, cpu_env); + gen_helper_subu_qb(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case 1: /* SUBU_S_QB */ - gen_helper_subu_s_qb(v1_t, v1_t, v2_t, cpu_env); + gen_helper_subu_s_qb(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; } @@ -3389,12 +3311,12 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, switch (extract32(ctx->opcode, 10, 1)) { case 0: /* SUBU_PH */ - gen_helper_subu_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_subu_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case 1: /* SUBU_S_PH */ - gen_helper_subu_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_subu_s_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; } @@ -3419,12 +3341,12 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, switch (extract32(ctx->opcode, 10, 1)) { case 0: /* SHLLV_PH */ - gen_helper_shll_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_shll_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case 1: /* SHLLV_S_PH */ - gen_helper_shll_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_shll_s_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; } @@ -3435,53 +3357,51 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, case 0: /* PRECR_SRA_PH_W */ { - TCGv_i32 sa_t = tcg_const_i32(rd); + TCGv_i32 sa_t = tcg_constant_i32(rd); gen_helper_precr_sra_ph_w(v1_t, sa_t, v1_t, cpu_gpr[rt]); gen_store_gpr(v1_t, rt); - tcg_temp_free_i32(sa_t); } break; case 1: /* PRECR_SRA_R_PH_W */ { - TCGv_i32 sa_t = tcg_const_i32(rd); + TCGv_i32 sa_t = tcg_constant_i32(rd); gen_helper_precr_sra_r_ph_w(v1_t, sa_t, v1_t, cpu_gpr[rt]); gen_store_gpr(v1_t, rt); - tcg_temp_free_i32(sa_t); } break; } break; case NM_MULEU_S_PH_QBL: check_dsp(ctx); - gen_helper_muleu_s_ph_qbl(v1_t, v1_t, v2_t, cpu_env); + gen_helper_muleu_s_ph_qbl(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_MULEU_S_PH_QBR: check_dsp(ctx); - gen_helper_muleu_s_ph_qbr(v1_t, v1_t, v2_t, cpu_env); + gen_helper_muleu_s_ph_qbr(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_MULQ_RS_PH: check_dsp(ctx); - gen_helper_mulq_rs_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_mulq_rs_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_MULQ_S_PH: check_dsp_r2(ctx); - gen_helper_mulq_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_mulq_s_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_MULQ_RS_W: check_dsp_r2(ctx); - gen_helper_mulq_rs_w(v1_t, v1_t, v2_t, cpu_env); + gen_helper_mulq_rs_w(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_MULQ_S_W: check_dsp_r2(ctx); - gen_helper_mulq_s_w(v1_t, v1_t, v2_t, cpu_env); + gen_helper_mulq_s_w(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_APPEND: @@ -3514,12 +3434,12 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, break; case NM_SHLLV_QB: check_dsp(ctx); - gen_helper_shll_qb(v1_t, v1_t, v2_t, cpu_env); + gen_helper_shll_qb(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_SHLLV_S_W: check_dsp(ctx); - gen_helper_shll_s_w(v1_t, v1_t, v2_t, cpu_env); + gen_helper_shll_s_w(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_SHILO: @@ -3531,19 +3451,17 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, tcg_gen_movi_tl(tv0, rd >> 3); tcg_gen_movi_tl(tv1, imm); - gen_helper_shilo(tv0, tv1, cpu_env); - tcg_temp_free(tv1); - tcg_temp_free(tv0); + gen_helper_shilo(tv0, tv1, tcg_env); } break; case NM_MULEQ_S_W_PHL: check_dsp(ctx); - gen_helper_muleq_s_w_phl(v1_t, v1_t, v2_t, cpu_env); + gen_helper_muleq_s_w_phl(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_MULEQ_S_W_PHR: check_dsp(ctx); - gen_helper_muleq_s_w_phr(v1_t, v1_t, v2_t, cpu_env); + gen_helper_muleq_s_w_phr(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_MUL_S_PH: @@ -3551,12 +3469,12 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, switch (extract32(ctx->opcode, 10, 1)) { case 0: /* MUL_PH */ - gen_helper_mul_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_mul_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case 1: /* MUL_S_PH */ - gen_helper_mul_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_mul_s_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; } @@ -3578,12 +3496,12 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, break; case NM_PRECRQ_RS_PH_W: check_dsp(ctx); - gen_helper_precrq_rs_ph_w(v1_t, v1_t, v2_t, cpu_env); + gen_helper_precrq_rs_ph_w(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_PRECRQU_S_QB_PH: check_dsp(ctx); - gen_helper_precrqu_s_qb_ph(v1_t, v1_t, v2_t, cpu_env); + gen_helper_precrqu_s_qb_ph(v1_t, v1_t, v2_t, tcg_env); gen_store_gpr(v1_t, ret); break; case NM_SHRA_R_W: @@ -3614,12 +3532,12 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, switch (extract32(ctx->opcode, 10, 2)) { case 0: /* SHLL_PH */ - gen_helper_shll_ph(v1_t, t0, v1_t, cpu_env); + gen_helper_shll_ph(v1_t, t0, v1_t, tcg_env); gen_store_gpr(v1_t, rt); break; case 2: /* SHLL_S_PH */ - gen_helper_shll_s_ph(v1_t, t0, v1_t, cpu_env); + gen_helper_shll_s_ph(v1_t, t0, v1_t, tcg_env); gen_store_gpr(v1_t, rt); break; default: @@ -3630,7 +3548,7 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, case NM_SHLL_S_W: check_dsp(ctx); tcg_gen_movi_tl(t0, rd); - gen_helper_shll_s_w(v1_t, t0, v1_t, cpu_env); + gen_helper_shll_s_w(v1_t, t0, v1_t, tcg_env); gen_store_gpr(v1_t, rt); break; case NM_REPL_PH: @@ -3648,10 +3566,6 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, gen_reserved_instruction(ctx); break; } - - tcg_temp_free(v2_t); - tcg_temp_free(v1_t); - tcg_temp_free(t0); } static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) @@ -3690,8 +3604,8 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) generate_exception_end(ctx, EXCP_BREAK); break; case NM_SDBBP: - if (is_uhi(extract32(ctx->opcode, 0, 19))) { - gen_helper_do_semihosting(cpu_env); + if (is_uhi(ctx, extract32(ctx->opcode, 0, 19))) { + ctx->base.is_jmp = DISAS_SEMIHOST; } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { gen_reserved_instruction(ctx); @@ -3822,8 +3736,8 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) addr_off); tcg_gen_movi_tl(t0, addr); - tcg_gen_qemu_ld_tl(cpu_gpr[rt], t0, ctx->mem_idx, MO_TESL); - tcg_temp_free(t0); + tcg_gen_qemu_ld_tl(cpu_gpr[rt], t0, ctx->mem_idx, + MO_TESL | ctx->default_tcg_memop_mask); } break; case NM_SWPC48: @@ -3839,10 +3753,8 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) tcg_gen_movi_tl(t0, addr); gen_load_gpr(t1, rt); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); - - tcg_temp_free(t0); - tcg_temp_free(t1); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, + MO_TEUL | ctx->default_tcg_memop_mask); } break; default: @@ -3904,8 +3816,6 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) gen_load_gpr(t0, rs); tcg_gen_setcondi_tl(TCG_COND_EQ, t0, t0, imm); gen_store_gpr(t0, rt); - - tcg_temp_free(t0); } break; case NM_ADDIUNEG: @@ -3944,6 +3854,9 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) gen_shift_imm(ctx, OPC_ROTR, rt, rs, extract32(ctx->opcode, 0, 5)); break; + default: + gen_reserved_instruction(ctx); + break; } } break; @@ -3951,18 +3864,15 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) check_nms(ctx); if (rt != 0) { TCGv t0 = tcg_temp_new(); - TCGv_i32 shift = tcg_const_i32(extract32(ctx->opcode, 0, 5)); - TCGv_i32 shiftx = tcg_const_i32(extract32(ctx->opcode, 7, 4) - << 1); - TCGv_i32 stripe = tcg_const_i32(extract32(ctx->opcode, 6, 1)); + TCGv_i32 shift = + tcg_constant_i32(extract32(ctx->opcode, 0, 5)); + TCGv_i32 shiftx = + tcg_constant_i32(extract32(ctx->opcode, 7, 4) << 1); + TCGv_i32 stripe = + tcg_constant_i32(extract32(ctx->opcode, 6, 1)); gen_load_gpr(t0, rs); gen_helper_rotx(cpu_gpr[rt], t0, shift, shiftx, stripe); - tcg_temp_free(t0); - - tcg_temp_free_i32(shift); - tcg_temp_free_i32(shiftx); - tcg_temp_free_i32(stripe); } break; case NM_P_INS: @@ -4232,8 +4142,6 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) MO_UNALN); break; } - tcg_temp_free(t0); - tcg_temp_free(t1); } break; case NM_P_LL: @@ -4245,6 +4153,9 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) check_xnp(ctx); gen_llwp(ctx, rs, 0, rt, extract32(ctx->opcode, 3, 5)); break; + default: + gen_reserved_instruction(ctx); + break; } break; case NM_P_SC: @@ -4257,6 +4168,9 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) gen_scwp(ctx, rs, 0, rt, extract32(ctx->opcode, 3, 5), false); break; + default: + gen_reserved_instruction(ctx); + break; } break; case NM_CACHE: @@ -4265,6 +4179,9 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) gen_cache_operation(ctx, rt, rs, s); } break; + default: + gen_reserved_instruction(ctx); + break; } break; case NM_P_LS_E0: @@ -4371,6 +4288,9 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) break; } break; + default: + gen_reserved_instruction(ctx); + break; } break; case NM_P_LS_WM: @@ -4385,7 +4305,7 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) TCGv va = tcg_temp_new(); TCGv t1 = tcg_temp_new(); MemOp memop = (extract32(ctx->opcode, 8, 3)) == - NM_P_LS_UAWM ? MO_UNALN : 0; + NM_P_LS_UAWM ? MO_UNALN : MO_ALIGN; count = (count == 0) ? 8 : count; while (counter != count) { @@ -4413,8 +4333,6 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) } counter++; } - tcg_temp_free(va); - tcg_temp_free(t1); } break; default: @@ -4435,7 +4353,6 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) gen_load_gpr(t0, rt); tcg_gen_mov_tl(cpu_gpr[rd], t0); gen_compute_branch_nm(ctx, OPC_BGEZAL, 4, 0, 0, s); - tcg_temp_free(t0); } break; case NM_P_BAL: @@ -4478,19 +4395,20 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) case NM_P_BR3A: s = sextract32(ctx->opcode, 0, 1) << 14 | extract32(ctx->opcode, 1, 13) << 1; - check_cp1_enabled(ctx); switch (extract32(ctx->opcode, 16, 5)) { case NM_BC1EQZC: + check_cp1_enabled(ctx); gen_compute_branch_cp1_nm(ctx, OPC_BC1EQZ, rt, s); break; case NM_BC1NEZC: + check_cp1_enabled(ctx); gen_compute_branch_cp1_nm(ctx, OPC_BC1NEZ, rt, s); break; case NM_BPOSGE32C: check_dsp_r3(ctx); { - int32_t imm = extract32(ctx->opcode, 1, 13) | - extract32(ctx->opcode, 0, 1) << 13; + imm = extract32(ctx->opcode, 1, 13) + | extract32(ctx->opcode, 0, 1) << 13; gen_compute_branch_nm(ctx, OPC_BPOSGE32, 4, -1, -2, imm << 1); @@ -4527,7 +4445,12 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) switch (extract32(ctx->opcode, 14, 2)) { case NM_BNEC: check_nms(ctx); - gen_compute_branch_nm(ctx, OPC_BNE, 4, rs, rt, s); + if (rs == rt) { + /* NOP */ + ctx->hflags |= MIPS_HFLAG_FBNSLOT; + } else { + gen_compute_branch_nm(ctx, OPC_BNE, 4, rs, rt, s); + } break; case NM_BLTC: if (rs != 0 && rt != 0 && rs == rt) { @@ -4579,9 +4502,8 @@ static int decode_isa_nanomips(CPUMIPSState *env, DisasContext *ctx) /* make sure instructions are on a halfword boundary */ if (ctx->base.pc_next & 0x1) { - TCGv tmp = tcg_const_tl(ctx->base.pc_next); - tcg_gen_st_tl(tmp, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr)); - tcg_temp_free(tmp); + TCGv tmp = tcg_constant_tl(ctx->base.pc_next); + tcg_gen_st_tl(tmp, tcg_env, offsetof(CPUMIPSState, CP0_BadVAddr)); generate_exception_end(ctx, EXCP_AdEL); return 2; } @@ -4608,8 +4530,8 @@ static int decode_isa_nanomips(CPUMIPSState *env, DisasContext *ctx) generate_exception_end(ctx, EXCP_BREAK); break; case NM_SDBBP16: - if (is_uhi(extract32(ctx->opcode, 0, 3))) { - gen_helper_do_semihosting(cpu_env); + if (is_uhi(ctx, extract32(ctx->opcode, 0, 3))) { + ctx->base.is_jmp = DISAS_SEMIHOST; } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { gen_reserved_instruction(ctx); @@ -4713,7 +4635,7 @@ static int decode_isa_nanomips(CPUMIPSState *env, DisasContext *ctx) break; case NM_LI16: { - int imm = extract32(ctx->opcode, 0, 7); + imm = extract32(ctx->opcode, 0, 7); imm = (imm == 0x7f ? -1 : imm); if (rt != 0) { tcg_gen_movi_tl(cpu_gpr[rt], imm); @@ -4916,8 +4838,6 @@ static int decode_isa_nanomips(CPUMIPSState *env, DisasContext *ctx) gen_load_gpr(t1, rt); tcg_gen_mov_tl(cpu_gpr[rd], t0); tcg_gen_mov_tl(cpu_gpr[re], t1); - tcg_temp_free(t0); - tcg_temp_free(t1); } break; default: diff --git a/target/mips/tcg/octeon.decode b/target/mips/tcg/octeon.decode new file mode 100644 index 0000000000..0c787cb498 --- /dev/null +++ b/target/mips/tcg/octeon.decode @@ -0,0 +1,41 @@ +# Octeon Architecture Module instruction set +# +# Copyright (C) 2022 Pavel Dovgalyuk +# +# SPDX-License-Identifier: LGPL-2.1-or-later +# + +# Branch on bit set or clear +# BBIT0 110010 ..... ..... ................ +# BBIT032 110110 ..... ..... ................ +# BBIT1 111010 ..... ..... ................ +# BBIT132 111110 ..... ..... ................ + +%bbit_p 28:1 16:5 +BBIT 11 set:1 . 10 rs:5 ..... offset:s16 p=%bbit_p + +# Arithmetic +# BADDU rd, rs, rt +# DMUL rd, rs, rt +# EXTS rt, rs, p, lenm1 +# EXTS32 rt, rs, p, lenm1 +# CINS rt, rs, p, lenm1 +# CINS32 rt, rs, p, lenm1 +# DPOP rd, rs +# POP rd, rs +# SEQ rd, rs, rt +# SEQI rt, rs, immediate +# SNE rd, rs, rt +# SNEI rt, rs, immediate + +@r3 ...... rs:5 rt:5 rd:5 ..... ...... +%bitfield_p 0:1 6:5 +@bitfield ...... rs:5 rt:5 lenm1:5 ..... ..... . p=%bitfield_p + +BADDU 011100 ..... ..... ..... 00000 101000 @r3 +DMUL 011100 ..... ..... ..... 00000 000011 @r3 +EXTS 011100 ..... ..... ..... ..... 11101 . @bitfield +CINS 011100 ..... ..... ..... ..... 11001 . @bitfield +POP 011100 rs:5 00000 rd:5 00000 10110 dw:1 +SEQNE 011100 rs:5 rt:5 rd:5 00000 10101 ne:1 +SEQNEI 011100 rs:5 rt:5 imm:s10 10111 ne:1 diff --git a/target/mips/tcg/octeon_translate.c b/target/mips/tcg/octeon_translate.c new file mode 100644 index 0000000000..e25c4cbaa0 --- /dev/null +++ b/target/mips/tcg/octeon_translate.c @@ -0,0 +1,176 @@ +/* + * Octeon-specific instructions translation routines + * + * Copyright (c) 2022 Pavel Dovgalyuk + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "translate.h" +#include "tcg/tcg-op-gvec.h" + +/* Include the auto-generated decoder. */ +#include "decode-octeon.c.inc" + +static bool trans_BBIT(DisasContext *ctx, arg_BBIT *a) +{ + TCGv p; + + if (ctx->hflags & MIPS_HFLAG_BMASK) { + LOG_DISAS("Branch in delay / forbidden slot at PC 0x" + TARGET_FMT_lx "\n", ctx->base.pc_next); + generate_exception_end(ctx, EXCP_RI); + return true; + } + + /* Load needed operands */ + TCGv t0 = tcg_temp_new(); + gen_load_gpr(t0, a->rs); + + p = tcg_constant_tl(1ULL << a->p); + if (a->set) { + tcg_gen_and_tl(bcond, p, t0); + } else { + tcg_gen_andc_tl(bcond, p, t0); + } + + ctx->hflags |= MIPS_HFLAG_BC; + ctx->btarget = ctx->base.pc_next + 4 + a->offset * 4; + ctx->hflags |= MIPS_HFLAG_BDS32; + return true; +} + +static bool trans_BADDU(DisasContext *ctx, arg_BADDU *a) +{ + TCGv t0, t1; + + if (a->rt == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + gen_load_gpr(t0, a->rs); + gen_load_gpr(t1, a->rt); + + tcg_gen_add_tl(t0, t0, t1); + tcg_gen_andi_i64(cpu_gpr[a->rd], t0, 0xff); + return true; +} + +static bool trans_DMUL(DisasContext *ctx, arg_DMUL *a) +{ + TCGv t0, t1; + + if (a->rt == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + gen_load_gpr(t0, a->rs); + gen_load_gpr(t1, a->rt); + + tcg_gen_mul_i64(cpu_gpr[a->rd], t0, t1); + return true; +} + +static bool trans_EXTS(DisasContext *ctx, arg_EXTS *a) +{ + TCGv t0; + + if (a->rt == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new(); + gen_load_gpr(t0, a->rs); + tcg_gen_sextract_tl(t0, t0, a->p, a->lenm1 + 1); + gen_store_gpr(t0, a->rt); + return true; +} + +static bool trans_CINS(DisasContext *ctx, arg_CINS *a) +{ + TCGv t0; + + if (a->rt == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new(); + gen_load_gpr(t0, a->rs); + tcg_gen_deposit_z_tl(t0, t0, a->p, a->lenm1 + 1); + gen_store_gpr(t0, a->rt); + return true; +} + +static bool trans_POP(DisasContext *ctx, arg_POP *a) +{ + TCGv t0; + + if (a->rd == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new(); + gen_load_gpr(t0, a->rs); + if (!a->dw) { + tcg_gen_andi_i64(t0, t0, 0xffffffff); + } + tcg_gen_ctpop_tl(t0, t0); + gen_store_gpr(t0, a->rd); + return true; +} + +static bool trans_SEQNE(DisasContext *ctx, arg_SEQNE *a) +{ + TCGv t0, t1; + + if (a->rd == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + gen_load_gpr(t0, a->rs); + gen_load_gpr(t1, a->rt); + + if (a->ne) { + tcg_gen_setcond_tl(TCG_COND_NE, cpu_gpr[a->rd], t1, t0); + } else { + tcg_gen_setcond_tl(TCG_COND_EQ, cpu_gpr[a->rd], t1, t0); + } + return true; +} + +static bool trans_SEQNEI(DisasContext *ctx, arg_SEQNEI *a) +{ + TCGv t0; + + if (a->rt == 0) { + /* nop */ + return true; + } + + t0 = tcg_temp_new(); + + gen_load_gpr(t0, a->rs); + + /* Sign-extend to 64 bit value */ + target_ulong imm = a->imm; + if (a->ne) { + tcg_gen_setcondi_tl(TCG_COND_NE, cpu_gpr[a->rt], t0, imm); + } else { + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_gpr[a->rt], t0, imm); + } + return true; +} diff --git a/target/mips/tcg/op_helper.c b/target/mips/tcg/op_helper.c index ef3dafcbb3..65403f1a87 100644 --- a/target/mips/tcg/op_helper.c +++ b/target/mips/tcg/op_helper.c @@ -257,14 +257,29 @@ void helper_pmon(CPUMIPSState *env, int function) } } +#ifdef TARGET_MIPS64 +target_ulong helper_lcsr_cpucfg(CPUMIPSState *env, target_ulong rs) +{ + switch (rs) { + case 0: + return env->CP0_PRid; + case 1: + return env->lcsr_cpucfg1; + case 2: + return env->lcsr_cpucfg2; + default: + return 0; + } +} +#endif + #if !defined(CONFIG_USER_ONLY) void mips_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); int error_code = 0; int excp; @@ -290,9 +305,8 @@ void mips_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr) { - MIPSCPU *cpu = MIPS_CPU(cs); - MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(cpu); - CPUMIPSState *env = &cpu->env; + MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(cs); + CPUMIPSState *env = cpu_env(cs); if (access_type == MMU_INST_FETCH) { do_raise_exception(env, EXCP_IBE, retaddr); diff --git a/target/mips/tcg/rel6_translate.c b/target/mips/tcg/rel6_translate.c index d631851258..59f237ba3b 100644 --- a/target/mips/tcg/rel6_translate.c +++ b/target/mips/tcg/rel6_translate.c @@ -9,8 +9,6 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "exec/helper-gen.h" #include "translate.h" /* Include the auto-generated decoders. */ diff --git a/target/mips/tcg/sysemu/cp0_helper.c b/target/mips/tcg/sysemu/cp0_helper.c index aae2af6ecc..ded6c78e9a 100644 --- a/target/mips/tcg/sysemu/cp0_helper.c +++ b/target/mips/tcg/sysemu/cp0_helper.c @@ -28,6 +28,7 @@ #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" +#include "hw/misc/mips_itu.h" /* SMP helpers. */ @@ -58,9 +59,9 @@ static inline void mips_vpe_wake(MIPSCPU *c) * because there might be other conditions that state that c should * be sleeping. */ - qemu_mutex_lock_iothread(); + bql_lock(); cpu_interrupt(CPU(c), CPU_INTERRUPT_WAKE); - qemu_mutex_unlock_iothread(); + bql_unlock(); } static inline void mips_vpe_sleep(MIPSCPU *cpu) @@ -370,22 +371,6 @@ 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); @@ -513,13 +498,6 @@ 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) @@ -1099,46 +1077,6 @@ 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; @@ -1201,7 +1139,7 @@ void helper_mtc0_status(CPUMIPSState *env, target_ulong arg1) 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)) { + switch (mips_env_mmu_index(env)) { case 3: qemu_log(", ERL\n"); break; @@ -1396,10 +1334,11 @@ void helper_mtc0_watchlo(CPUMIPSState *env, target_ulong arg1, uint32_t sel) void helper_mtc0_watchhi(CPUMIPSState *env, target_ulong arg1, uint32_t sel) { uint64_t mask = 0x40000FF8 | (env->CP0_EntryHi_ASID_mask << CP0WH_ASID); + uint64_t m_bit = env->CP0_WatchHi[sel] & (1 << CP0WH_M); /* read-only */ if ((env->CP0_Config5 >> CP0C5_MI) & 1) { mask |= 0xFFFFFFFF00000000ULL; /* MMID */ } - env->CP0_WatchHi[sel] = arg1 & mask; + env->CP0_WatchHi[sel] = m_bit | (arg1 & mask); env->CP0_WatchHi[sel] &= ~(env->CP0_WatchHi[sel] & arg1 & 0x7); } diff --git a/target/mips/tcg/sysemu/lcsr_helper.c b/target/mips/tcg/sysemu/lcsr_helper.c new file mode 100644 index 0000000000..25e03572fe --- /dev/null +++ b/target/mips/tcg/sysemu/lcsr_helper.c @@ -0,0 +1,40 @@ +/* + * Loongson CSR instructions translation routines + * + * Copyright (c) 2023 Jiaxun Yang + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" + +#define GET_MEMTXATTRS(cas) \ + ((MemTxAttrs){.requester_id = env_cpu(cas)->cpu_index}) + +uint64_t helper_lcsr_rdcsr(CPUMIPSState *env, target_ulong r_addr) +{ + return address_space_ldl(&env->iocsr.as, r_addr, + GET_MEMTXATTRS(env), NULL); +} + +uint64_t helper_lcsr_drdcsr(CPUMIPSState *env, target_ulong r_addr) +{ + return address_space_ldq(&env->iocsr.as, r_addr, + GET_MEMTXATTRS(env), NULL); +} + +void helper_lcsr_wrcsr(CPUMIPSState *env, target_ulong w_addr, + target_ulong val) +{ + address_space_stl(&env->iocsr.as, w_addr, + val, GET_MEMTXATTRS(env), NULL); +} + +void helper_lcsr_dwrcsr(CPUMIPSState *env, target_ulong w_addr, + target_ulong val) +{ + address_space_stq(&env->iocsr.as, w_addr, + val, GET_MEMTXATTRS(env), NULL); +} diff --git a/target/mips/tcg/sysemu/meson.build b/target/mips/tcg/sysemu/meson.build index 4da2c577b2..ec665a4b1e 100644 --- a/target/mips/tcg/sysemu/meson.build +++ b/target/mips/tcg/sysemu/meson.build @@ -1,6 +1,10 @@ -mips_softmmu_ss.add(files( +mips_system_ss.add(files( 'cp0_helper.c', 'mips-semi.c', 'special_helper.c', 'tlb_helper.c', )) + +mips_system_ss.add(when: 'TARGET_MIPS64', if_true: files( + 'lcsr_helper.c', +)) diff --git a/target/mips/tcg/sysemu/mips-semi.c b/target/mips/tcg/sysemu/mips-semi.c index b4a383ae90..5ba06e9573 100644 --- a/target/mips/tcg/sysemu/mips-semi.c +++ b/target/mips/tcg/sysemu/mips-semi.c @@ -20,10 +20,13 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/log.h" -#include "exec/helper-proto.h" -#include "exec/softmmu-semi.h" +#include "gdbstub/syscalls.h" +#include "gdbstub/helpers.h" +#include "semihosting/uaccess.h" #include "semihosting/semihost.h" #include "semihosting/console.h" +#include "semihosting/syscalls.h" +#include "internal.h" typedef enum UHIOp { UHI_exit = 1, @@ -74,293 +77,284 @@ enum UHIOpenFlags { UHIOpen_EXCL = 0x800 }; -static int errno_mips(int host_errno) +enum UHIErrno { + UHI_EACCESS = 13, + UHI_EAGAIN = 11, + UHI_EBADF = 9, + UHI_EBADMSG = 77, + UHI_EBUSY = 16, + UHI_ECONNRESET = 104, + UHI_EEXIST = 17, + UHI_EFBIG = 27, + UHI_EINTR = 4, + UHI_EINVAL = 22, + UHI_EIO = 5, + UHI_EISDIR = 21, + UHI_ELOOP = 92, + UHI_EMFILE = 24, + UHI_EMLINK = 31, + UHI_ENAMETOOLONG = 91, + UHI_ENETDOWN = 115, + UHI_ENETUNREACH = 114, + UHI_ENFILE = 23, + UHI_ENOBUFS = 105, + UHI_ENOENT = 2, + UHI_ENOMEM = 12, + UHI_ENOSPC = 28, + UHI_ENOSR = 63, + UHI_ENOTCONN = 128, + UHI_ENOTDIR = 20, + UHI_ENXIO = 6, + UHI_EOVERFLOW = 139, + UHI_EPERM = 1, + UHI_EPIPE = 32, + UHI_ERANGE = 34, + UHI_EROFS = 30, + UHI_ESPIPE = 29, + UHI_ETIMEDOUT = 116, + UHI_ETXTBSY = 26, + UHI_EWOULDBLOCK = 11, + UHI_EXDEV = 18, +}; + +static void report_fault(CPUMIPSState *env) { - /* Errno values taken from asm-mips/errno.h */ - switch (host_errno) { - case 0: return 0; - case ENAMETOOLONG: return 78; -#ifdef EOVERFLOW - case EOVERFLOW: return 79; -#endif -#ifdef ELOOP - case ELOOP: return 90; -#endif - default: return EINVAL; - } + int op = env->active_tc.gpr[25]; + error_report("Fault during UHI operation %d", op); + abort(); } -static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src, - target_ulong vaddr) +static void uhi_cb(CPUState *cs, uint64_t ret, int err) { - hwaddr len = sizeof(struct UHIStat); - UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); - if (!dst) { - errno = EFAULT; - return -1; + CPUMIPSState *env = cpu_env(cs); + +#define E(N) case E##N: err = UHI_E##N; break + + switch (err) { + case 0: + break; + E(PERM); + E(NOENT); + E(INTR); + E(BADF); + E(BUSY); + E(EXIST); + E(NOTDIR); + E(ISDIR); + E(INVAL); + E(NFILE); + E(MFILE); + E(FBIG); + E(NOSPC); + E(SPIPE); + E(ROFS); + E(NAMETOOLONG); + default: + err = UHI_EINVAL; + break; + case EFAULT: + report_fault(env); } - dst->uhi_st_dev = tswap16(src->st_dev); - dst->uhi_st_ino = tswap16(src->st_ino); - dst->uhi_st_mode = tswap32(src->st_mode); - dst->uhi_st_nlink = tswap16(src->st_nlink); - dst->uhi_st_uid = tswap16(src->st_uid); - dst->uhi_st_gid = tswap16(src->st_gid); - dst->uhi_st_rdev = tswap16(src->st_rdev); - dst->uhi_st_size = tswap64(src->st_size); - dst->uhi_st_atime = tswap64(src->st_atime); - dst->uhi_st_mtime = tswap64(src->st_mtime); - dst->uhi_st_ctime = tswap64(src->st_ctime); -#ifdef _WIN32 - dst->uhi_st_blksize = 0; - dst->uhi_st_blocks = 0; -#else - dst->uhi_st_blksize = tswap64(src->st_blksize); - dst->uhi_st_blocks = tswap64(src->st_blocks); -#endif - unlock_user(dst, vaddr, len); - return 0; +#undef E + + env->active_tc.gpr[2] = ret; + env->active_tc.gpr[3] = err; } -static int get_open_flags(target_ulong target_flags) +static void uhi_fstat_cb(CPUState *cs, uint64_t ret, int err) { - int open_flags = 0; + QEMU_BUILD_BUG_ON(sizeof(UHIStat) < sizeof(struct gdb_stat)); - if (target_flags & UHIOpen_RDWR) { - open_flags |= O_RDWR; - } else if (target_flags & UHIOpen_WRONLY) { - open_flags |= O_WRONLY; - } else { - open_flags |= O_RDONLY; + if (!err) { + CPUMIPSState *env = cpu_env(cs); + target_ulong addr = env->active_tc.gpr[5]; + UHIStat *dst = lock_user(VERIFY_WRITE, addr, sizeof(UHIStat), 1); + struct gdb_stat s; + + if (!dst) { + report_fault(env); + } + + memcpy(&s, dst, sizeof(struct gdb_stat)); + memset(dst, 0, sizeof(UHIStat)); + + dst->uhi_st_dev = tswap16(be32_to_cpu(s.gdb_st_dev)); + dst->uhi_st_ino = tswap16(be32_to_cpu(s.gdb_st_ino)); + dst->uhi_st_mode = tswap32(be32_to_cpu(s.gdb_st_mode)); + dst->uhi_st_nlink = tswap16(be32_to_cpu(s.gdb_st_nlink)); + dst->uhi_st_uid = tswap16(be32_to_cpu(s.gdb_st_uid)); + dst->uhi_st_gid = tswap16(be32_to_cpu(s.gdb_st_gid)); + dst->uhi_st_rdev = tswap16(be32_to_cpu(s.gdb_st_rdev)); + dst->uhi_st_size = tswap64(be64_to_cpu(s.gdb_st_size)); + dst->uhi_st_atime = tswap64(be32_to_cpu(s.gdb_st_atime)); + dst->uhi_st_mtime = tswap64(be32_to_cpu(s.gdb_st_mtime)); + dst->uhi_st_ctime = tswap64(be32_to_cpu(s.gdb_st_ctime)); + dst->uhi_st_blksize = tswap64(be64_to_cpu(s.gdb_st_blksize)); + dst->uhi_st_blocks = tswap64(be64_to_cpu(s.gdb_st_blocks)); + + unlock_user(dst, addr, sizeof(UHIStat)); } - open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0; - open_flags |= (target_flags & UHIOpen_CREAT) ? O_CREAT : 0; - open_flags |= (target_flags & UHIOpen_TRUNC) ? O_TRUNC : 0; - open_flags |= (target_flags & UHIOpen_EXCL) ? O_EXCL : 0; - - return open_flags; + uhi_cb(cs, ret, err); } -static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr, - target_ulong len, target_ulong offset) -{ - int num_of_bytes; - void *dst = lock_user(VERIFY_READ, vaddr, len, 1); - if (!dst) { - errno = EFAULT; - return -1; - } - - if (offset) { -#ifdef _WIN32 - num_of_bytes = 0; -#else - num_of_bytes = pwrite(fd, dst, len, offset); -#endif - } else { - num_of_bytes = write(fd, dst, len); - } - - unlock_user(dst, vaddr, 0); - return num_of_bytes; -} - -static int read_from_file(CPUMIPSState *env, target_ulong fd, - target_ulong vaddr, target_ulong len, - target_ulong offset) -{ - int num_of_bytes; - void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); - if (!dst) { - errno = EFAULT; - return -1; - } - - if (offset) { -#ifdef _WIN32 - num_of_bytes = 0; -#else - num_of_bytes = pread(fd, dst, len, offset); -#endif - } else { - num_of_bytes = read(fd, dst, len); - } - - unlock_user(dst, vaddr, len); - return num_of_bytes; -} - -static int copy_argn_to_target(CPUMIPSState *env, int arg_num, - target_ulong vaddr) -{ - int strsize = strlen(semihosting_get_arg(arg_num)) + 1; - char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0); - if (!dst) { - return -1; - } - - strcpy(dst, semihosting_get_arg(arg_num)); - - unlock_user(dst, vaddr, strsize); - return 0; -} - -#define GET_TARGET_STRING(p, addr) \ - do { \ - p = lock_user_string(addr); \ - if (!p) { \ - gpr[2] = -1; \ - gpr[3] = EFAULT; \ - return; \ - } \ - } while (0) - -#define GET_TARGET_STRINGS_2(p, addr, p2, addr2) \ - do { \ - p = lock_user_string(addr); \ - if (!p) { \ - gpr[2] = -1; \ - gpr[3] = EFAULT; \ - return; \ - } \ - p2 = lock_user_string(addr2); \ - if (!p2) { \ - unlock_user(p, addr, 0); \ - gpr[2] = -1; \ - gpr[3] = EFAULT; \ - return; \ - } \ - } while (0) - -#define FREE_TARGET_STRING(p, gpr) \ - do { \ - unlock_user(p, gpr, 0); \ - } while (0) - -void helper_do_semihosting(CPUMIPSState *env) +void mips_semihosting(CPUMIPSState *env) { + CPUState *cs = env_cpu(env); target_ulong *gpr = env->active_tc.gpr; const UHIOp op = gpr[25]; - char *p, *p2; + char *p; switch (op) { case UHI_exit: - qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]); + gdb_exit(gpr[4]); exit(gpr[4]); + case UHI_open: - GET_TARGET_STRING(p, gpr[4]); - if (!strcmp("/dev/stdin", p)) { - gpr[2] = 0; - } else if (!strcmp("/dev/stdout", p)) { - gpr[2] = 1; - } else if (!strcmp("/dev/stderr", p)) { - gpr[2] = 2; - } else { - gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]); - gpr[3] = errno_mips(errno); + { + target_ulong fname = gpr[4]; + int ret = -1; + + p = lock_user_string(fname); + if (!p) { + report_fault(env); + } + if (!strcmp("/dev/stdin", p)) { + ret = 0; + } else if (!strcmp("/dev/stdout", p)) { + ret = 1; + } else if (!strcmp("/dev/stderr", p)) { + ret = 2; + } + unlock_user(p, fname, 0); + + /* FIXME: reusing a guest fd doesn't seem correct. */ + if (ret >= 0) { + gpr[2] = ret; + break; + } + + semihost_sys_open(cs, uhi_cb, fname, 0, gpr[5], gpr[6]); } - FREE_TARGET_STRING(p, gpr[4]); break; + case UHI_close: - if (gpr[4] < 3) { - /* ignore closing stdin/stdout/stderr */ - gpr[2] = 0; - return; - } - gpr[2] = close(gpr[4]); - gpr[3] = errno_mips(errno); + semihost_sys_close(cs, uhi_cb, gpr[4]); break; case UHI_read: - gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0); - gpr[3] = errno_mips(errno); + semihost_sys_read(cs, uhi_cb, gpr[4], gpr[5], gpr[6]); break; case UHI_write: - gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0); - gpr[3] = errno_mips(errno); + semihost_sys_write(cs, uhi_cb, gpr[4], gpr[5], gpr[6]); break; case UHI_lseek: - gpr[2] = lseek(gpr[4], gpr[5], gpr[6]); - gpr[3] = errno_mips(errno); + semihost_sys_lseek(cs, uhi_cb, gpr[4], gpr[5], gpr[6]); break; case UHI_unlink: - GET_TARGET_STRING(p, gpr[4]); - gpr[2] = remove(p); - gpr[3] = errno_mips(errno); - FREE_TARGET_STRING(p, gpr[4]); + semihost_sys_remove(cs, uhi_cb, gpr[4], 0); break; case UHI_fstat: - { - struct stat sbuf; - memset(&sbuf, 0, sizeof(sbuf)); - gpr[2] = fstat(gpr[4], &sbuf); - gpr[3] = errno_mips(errno); - if (gpr[2]) { - return; - } - gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]); - gpr[3] = errno_mips(errno); - } + semihost_sys_fstat(cs, uhi_fstat_cb, gpr[4], gpr[5]); break; + case UHI_argc: gpr[2] = semihosting_get_argc(); break; case UHI_argnlen: - if (gpr[4] >= semihosting_get_argc()) { - gpr[2] = -1; - return; + { + const char *s = semihosting_get_arg(gpr[4]); + gpr[2] = s ? strlen(s) : -1; } - gpr[2] = strlen(semihosting_get_arg(gpr[4])); break; case UHI_argn: - if (gpr[4] >= semihosting_get_argc()) { - gpr[2] = -1; - return; + { + const char *s = semihosting_get_arg(gpr[4]); + target_ulong addr; + size_t len; + + if (!s) { + gpr[2] = -1; + break; + } + len = strlen(s) + 1; + addr = gpr[5]; + p = lock_user(VERIFY_WRITE, addr, len, 0); + if (!p) { + report_fault(env); + } + memcpy(p, s, len); + unlock_user(p, addr, len); + gpr[2] = 0; } - gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]); break; + case UHI_plog: - GET_TARGET_STRING(p, gpr[4]); - p2 = strstr(p, "%d"); - if (p2) { - int char_num = p2 - p; - GString *s = g_string_new_len(p, char_num); - g_string_append_printf(s, "%d%s", (int)gpr[5], p2 + 2); - gpr[2] = qemu_semihosting_log_out(s->str, s->len); - g_string_free(s, true); - } else { - gpr[2] = qemu_semihosting_log_out(p, strlen(p)); + { + target_ulong addr = gpr[4]; + ssize_t len = target_strlen(addr); + GString *str; + char *pct_d; + + if (len < 0) { + report_fault(env); + } + p = lock_user(VERIFY_READ, addr, len, 1); + if (!p) { + report_fault(env); + } + + pct_d = strstr(p, "%d"); + if (!pct_d) { + unlock_user(p, addr, 0); + semihost_sys_write(cs, uhi_cb, 2, addr, len); + break; + } + + str = g_string_new_len(p, pct_d - p); + g_string_append_printf(str, "%d%s", (int)gpr[5], pct_d + 2); + unlock_user(p, addr, 0); + + /* + * When we're using gdb, we need a guest address, so + * drop the string onto the stack below the stack pointer. + */ + if (use_gdb_syscalls()) { + addr = gpr[29] - str->len; + p = lock_user(VERIFY_WRITE, addr, str->len, 0); + if (!p) { + report_fault(env); + } + memcpy(p, str->str, str->len); + unlock_user(p, addr, str->len); + semihost_sys_write(cs, uhi_cb, 2, addr, str->len); + } else { + gpr[2] = qemu_semihosting_console_write(str->str, str->len); + } + g_string_free(str, true); } - FREE_TARGET_STRING(p, gpr[4]); break; + case UHI_assert: - GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]); - printf("assertion '"); - printf("\"%s\"", p); - printf("': file \"%s\", line %d\n", p2, (int)gpr[6]); - FREE_TARGET_STRING(p2, gpr[5]); - FREE_TARGET_STRING(p, gpr[4]); - abort(); - break; - case UHI_pread: - gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]); - gpr[3] = errno_mips(errno); - break; - case UHI_pwrite: - gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]); - gpr[3] = errno_mips(errno); - break; -#ifndef _WIN32 - case UHI_link: - GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]); - gpr[2] = link(p, p2); - gpr[3] = errno_mips(errno); - FREE_TARGET_STRING(p2, gpr[5]); - FREE_TARGET_STRING(p, gpr[4]); - break; -#endif + { + const char *msg, *file; + + msg = lock_user_string(gpr[4]); + if (!msg) { + msg = ""; + } + file = lock_user_string(gpr[5]); + if (!file) { + file = ""; + } + + error_report("UHI assertion \"%s\": file \"%s\", line %d", + msg, file, (int)gpr[6]); + abort(); + } + default: - fprintf(stderr, "Unknown UHI operation %d\n", op); + error_report("Unknown UHI operation %d", op); abort(); } return; diff --git a/target/mips/tcg/sysemu/special_helper.c b/target/mips/tcg/sysemu/special_helper.c index f4f8fe8afc..5baa25348e 100644 --- a/target/mips/tcg/sysemu/special_helper.c +++ b/target/mips/tcg/sysemu/special_helper.c @@ -68,7 +68,7 @@ static void debug_post_eret(CPUMIPSState *env) if (env->hflags & MIPS_HFLAG_DM) { qemu_log(" DEPC " TARGET_FMT_lx, env->CP0_DEPC); } - switch (cpu_mmu_index(env, false)) { + switch (mips_env_mmu_index(env)) { case 3: qemu_log(", ERL\n"); break; @@ -90,11 +90,10 @@ static void debug_post_eret(CPUMIPSState *env) bool mips_io_recompile_replay_branch(CPUState *cs, const TranslationBlock *tb) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); if ((env->hflags & MIPS_HFLAG_BMASK) != 0 - && env->active_tc.PC != tb->pc) { + && !(cs->tcg_cflags & CF_PCREL) && env->active_tc.PC != tb->pc) { env->active_tc.PC -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4); env->hflags &= ~MIPS_HFLAG_BMASK; return true; diff --git a/target/mips/tcg/sysemu/tlb_helper.c b/target/mips/tcg/sysemu/tlb_helper.c index 73254d1929..119eae771e 100644 --- a/target/mips/tcg/sysemu/tlb_helper.c +++ b/target/mips/tcg/sysemu/tlb_helper.c @@ -24,7 +24,6 @@ #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "exec/log.h" -#include "hw/mips/cpudevs.h" #include "exec/helper-proto.h" /* TLB management */ @@ -623,18 +622,13 @@ static uint64_t get_tlb_entry_layout(CPUMIPSState *env, uint64_t entry, static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, int directory_index, bool *huge_page, bool *hgpg_directory_hit, - uint64_t *pw_entrylo0, uint64_t *pw_entrylo1) + uint64_t *pw_entrylo0, uint64_t *pw_entrylo1, + unsigned directory_shift, unsigned leaf_shift, int ptw_mmu_idx) { int dph = (env->CP0_PWCtl >> CP0PC_DPH) & 0x1; int psn = (env->CP0_PWCtl >> CP0PC_PSN) & 0x3F; int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; int pf_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; - int ptew = (env->CP0_PWSize >> CP0PS_PTEW) & 0x3F; - int native_shift = (((env->CP0_PWSize >> CP0PS_PS) & 1) == 0) ? 2 : 3; - int directory_shift = (ptew > 1) ? -1 : - (hugepg && (ptew == 1)) ? native_shift + 1 : native_shift; - int leaf_shift = (ptew > 1) ? -1 : - (ptew == 1) ? native_shift + 1 : native_shift; uint32_t direntry_size = 1 << (directory_shift + 3); uint32_t leafentry_size = 1 << (leaf_shift + 3); uint64_t entry; @@ -644,8 +638,7 @@ static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, uint64_t w = 0; if (get_physical_address(env, &paddr, &prot, *vaddr, MMU_DATA_LOAD, - cpu_mmu_index(env, false)) != - TLBRET_MATCH) { + ptw_mmu_idx) != TLBRET_MATCH) { /* wrong base address */ return 0; } @@ -672,8 +665,7 @@ static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, *pw_entrylo0 = entry; } if (get_physical_address(env, &paddr, &prot, vaddr2, MMU_DATA_LOAD, - cpu_mmu_index(env, false)) != - TLBRET_MATCH) { + ptw_mmu_idx) != TLBRET_MATCH) { return 0; } if (!get_pte(env, vaddr2, leafentry_size, &entry)) { @@ -696,7 +688,7 @@ static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, } static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, - int mmu_idx) + int ptw_mmu_idx) { int gdw = (env->CP0_PWSize >> CP0PS_GDW) & 0x3F; int udw = (env->CP0_PWSize >> CP0PS_UDW) & 0x3F; @@ -735,21 +727,11 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, /* Other HTW configs */ int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; - - /* HTW Shift values (depend on entry size) */ - int directory_shift = (ptew > 1) ? -1 : - (hugepg && (ptew == 1)) ? native_shift + 1 : native_shift; - int leaf_shift = (ptew > 1) ? -1 : - (ptew == 1) ? native_shift + 1 : native_shift; + unsigned directory_shift, leaf_shift; /* Offsets into tables */ - int goffset = gindex << directory_shift; - int uoffset = uindex << directory_shift; - int moffset = mindex << directory_shift; - int ptoffset0 = (ptindex >> 1) << (leaf_shift + 1); - int ptoffset1 = ptoffset0 | (1 << (leaf_shift)); - - uint32_t leafentry_size = 1 << (leaf_shift + 3); + unsigned goffset, uoffset, moffset, ptoffset0, ptoffset1; + uint32_t leafentry_size; /* Starting address - Page Table Base */ uint64_t vaddr = env->CP0_PWBase; @@ -771,15 +753,28 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, /* no structure to walk */ return false; } - if ((directory_shift == -1) || (leaf_shift == -1)) { + if (ptew > 1) { return false; } + /* HTW Shift values (depend on entry size) */ + directory_shift = (hugepg && (ptew == 1)) ? native_shift + 1 : native_shift; + leaf_shift = (ptew == 1) ? native_shift + 1 : native_shift; + + goffset = gindex << directory_shift; + uoffset = uindex << directory_shift; + moffset = mindex << directory_shift; + ptoffset0 = (ptindex >> 1) << (leaf_shift + 1); + ptoffset1 = ptoffset0 | (1 << (leaf_shift)); + + leafentry_size = 1 << (leaf_shift + 3); + /* Global Directory */ if (gdw > 0) { vaddr |= goffset; switch (walk_directory(env, &vaddr, pf_gdw, &huge_page, &hgpg_gdhit, - &pw_entrylo0, &pw_entrylo1)) + &pw_entrylo0, &pw_entrylo1, + directory_shift, leaf_shift, ptw_mmu_idx)) { case 0: return false; @@ -795,7 +790,8 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, if (udw > 0) { vaddr |= uoffset; switch (walk_directory(env, &vaddr, pf_udw, &huge_page, &hgpg_udhit, - &pw_entrylo0, &pw_entrylo1)) + &pw_entrylo0, &pw_entrylo1, + directory_shift, leaf_shift, ptw_mmu_idx)) { case 0: return false; @@ -811,7 +807,8 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, if (mdw > 0) { vaddr |= moffset; switch (walk_directory(env, &vaddr, pf_mdw, &huge_page, &hgpg_mdhit, - &pw_entrylo0, &pw_entrylo1)) + &pw_entrylo0, &pw_entrylo1, + directory_shift, leaf_shift, ptw_mmu_idx)) { case 0: return false; @@ -826,8 +823,7 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, /* Leaf Level Page Table - First half of PTE pair */ vaddr |= ptoffset0; if (get_physical_address(env, &paddr, &prot, vaddr, MMU_DATA_LOAD, - cpu_mmu_index(env, false)) != - TLBRET_MATCH) { + ptw_mmu_idx) != TLBRET_MATCH) { return false; } if (!get_pte(env, vaddr, leafentry_size, &dir_entry)) { @@ -839,8 +835,7 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, /* Leaf Level Page Table - Second half of PTE pair */ vaddr |= ptoffset1; if (get_physical_address(env, &paddr, &prot, vaddr, MMU_DATA_LOAD, - cpu_mmu_index(env, false)) != - TLBRET_MATCH) { + ptw_mmu_idx) != TLBRET_MATCH) { return false; } if (!get_pte(env, vaddr, leafentry_size, &dir_entry)) { @@ -911,8 +906,7 @@ bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); hwaddr physical; int prot; int ret = TLBRET_BADADDR; @@ -924,7 +918,7 @@ bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size, switch (ret) { case TLBRET_MATCH: qemu_log_mask(CPU_LOG_MMU, - "%s address=%" VADDR_PRIx " physical " TARGET_FMT_plx + "%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx " prot %d\n", __func__, address, physical, prot); break; default: @@ -945,12 +939,10 @@ bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size, * Memory reads during hardware page table walking are performed * as if they were kernel-mode load instructions. */ - int mode = (env->hflags & MIPS_HFLAG_KSU); - bool ret_walker; - env->hflags &= ~MIPS_HFLAG_KSU; - ret_walker = page_table_walk_refill(env, address, mmu_idx); - env->hflags |= mode; - if (ret_walker) { + int ptw_mmu_idx = (env->hflags & MIPS_HFLAG_ERL ? + MMU_ERL_IDX : MMU_KERNEL_IDX); + + if (page_table_walk_refill(env, address, ptw_mmu_idx)) { ret = get_physical_address(env, &physical, &prot, address, access_type, mmu_idx); if (ret == TLBRET_MATCH) { @@ -980,7 +972,7 @@ hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, /* data access */ ret = get_physical_address(env, &physical, &prot, address, access_type, - cpu_mmu_index(env, false)); + mips_env_mmu_index(env)); if (ret == TLBRET_MATCH) { return physical; } @@ -1053,6 +1045,11 @@ void mips_cpu_do_interrupt(CPUState *cs) } offset = 0x180; switch (cs->exception_index) { + case EXCP_SEMIHOST: + cs->exception_index = EXCP_NONE; + mips_semihosting(env); + env->active_tc.PC += env->error_code; + return; case EXCP_DSS: env->CP0_Debug |= 1 << CP0DB_DSS; /* @@ -1342,8 +1339,7 @@ void mips_cpu_do_interrupt(CPUState *cs) bool mips_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { if (interrupt_request & CPU_INTERRUPT_HARD) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); if (cpu_mips_hw_interrupts_enabled(env) && cpu_mips_hw_interrupts_pending(env)) { diff --git a/target/mips/tcg/sysemu_helper.h.inc b/target/mips/tcg/sysemu_helper.h.inc index 4353a966f9..1861d538de 100644 --- a/target/mips/tcg/sysemu_helper.h.inc +++ b/target/mips/tcg/sysemu_helper.h.inc @@ -9,8 +9,6 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ -DEF_HELPER_1(do_semihosting, void, env) - /* CP0 helpers */ DEF_HELPER_1(mfc0_mvpcontrol, tl, env) DEF_HELPER_1(mfc0_mvpconf0, tl, env) @@ -33,8 +31,6 @@ DEF_HELPER_1(mftc0_tcschedule, tl, env) DEF_HELPER_1(mfc0_tcschefback, tl, env) DEF_HELPER_1(mftc0_tcschefback, tl, env) DEF_HELPER_1(mfc0_count, tl, env) -DEF_HELPER_1(mfc0_saar, tl, env) -DEF_HELPER_1(mfhc0_saar, tl, env) DEF_HELPER_1(mftc0_entryhi, tl, env) DEF_HELPER_1(mftc0_status, tl, env) DEF_HELPER_1(mftc0_cause, tl, env) @@ -59,7 +55,6 @@ 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 */ DEF_HELPER_2(mtc0_index, void, env, tl) @@ -105,9 +100,6 @@ DEF_HELPER_2(mtc0_srsconf4, void, env, tl) DEF_HELPER_2(mtc0_hwrena, void, env, tl) DEF_HELPER_2(mtc0_pwctl, void, env, tl) DEF_HELPER_2(mtc0_count, void, env, tl) -DEF_HELPER_2(mtc0_saari, void, env, tl) -DEF_HELPER_2(mtc0_saar, void, env, tl) -DEF_HELPER_2(mthc0_saar, void, env, tl) DEF_HELPER_2(mtc0_entryhi, void, env, tl) DEF_HELPER_2(mttc0_entryhi, void, env, tl) DEF_HELPER_2(mtc0_compare, void, env, tl) @@ -183,3 +175,11 @@ DEF_HELPER_1(eret, void, env) DEF_HELPER_1(eretnc, void, env) DEF_HELPER_1(deret, void, env) DEF_HELPER_3(cache, void, env, tl, i32) + +#ifdef TARGET_MIPS64 +/* Longson CSR */ +DEF_HELPER_2(lcsr_rdcsr, i64, env, tl) +DEF_HELPER_2(lcsr_drdcsr, i64, env, tl) +DEF_HELPER_3(lcsr_wrcsr, void, env, tl, tl) +DEF_HELPER_3(lcsr_dwrcsr, void, env, tl, tl) +#endif diff --git a/target/mips/tcg/tcg-internal.h b/target/mips/tcg/tcg-internal.h index 993720b00c..aef032c48d 100644 --- a/target/mips/tcg/tcg-internal.h +++ b/target/mips/tcg/tcg-internal.h @@ -21,6 +21,9 @@ void mips_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb); G_NORETURN void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); +void mips_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data); const char *mips_exception_name(int32_t exception); @@ -62,6 +65,8 @@ bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +void mips_semihosting(CPUMIPSState *env); + #endif /* !CONFIG_USER_ONLY */ #endif diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 6de5b66650..06c108cc9c 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -23,20 +23,19 @@ */ #include "qemu/osdep.h" -#include "cpu.h" -#include "internal.h" -#include "tcg/tcg-op.h" -#include "exec/translator.h" -#include "exec/helper-proto.h" -#include "exec/helper-gen.h" -#include "semihosting/semihost.h" - -#include "trace.h" -#include "exec/translator.h" -#include "exec/log.h" -#include "qemu/qemu-print.h" -#include "fpu_helper.h" #include "translate.h" +#include "internal.h" +#include "exec/helper-proto.h" +#include "exec/translation-block.h" +#include "semihosting/semihost.h" +#include "trace.h" +#include "disas/disas.h" +#include "fpu_helper.h" + +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + /* * Many sysemu-only helpers are not reachable for user-only. @@ -1211,11 +1210,6 @@ static TCGv_i32 hflags; TCGv_i32 fpu_fcr0, fpu_fcr31; TCGv_i64 fpu_f64[32]; -#include "exec/gen-icount.h" - -#define DISAS_STOP DISAS_TARGET_0 -#define DISAS_EXIT DISAS_TARGET_1 - static const char regnames_HI[][4] = { "HI0", "HI1", "HI2", "HI3", }; @@ -1227,6 +1221,7 @@ static const char regnames_LO[][4] = { /* General purpose registers moves. */ void gen_load_gpr(TCGv t, int reg) { + assert(reg >= 0 && reg <= ARRAY_SIZE(cpu_gpr)); if (reg == 0) { tcg_gen_movi_tl(t, 0); } else { @@ -1236,6 +1231,7 @@ void gen_load_gpr(TCGv t, int reg) void gen_store_gpr(TCGv t, int reg) { + assert(reg >= 0 && reg <= ARRAY_SIZE(cpu_gpr)); if (reg != 0) { tcg_gen_mov_tl(cpu_gpr[reg], t); } @@ -1244,6 +1240,7 @@ void gen_store_gpr(TCGv t, int reg) #if defined(TARGET_MIPS64) void gen_load_gpr_hi(TCGv_i64 t, int reg) { + assert(reg >= 0 && reg <= ARRAY_SIZE(cpu_gpr_hi)); if (reg == 0) { tcg_gen_movi_i64(t, 0); } else { @@ -1253,6 +1250,7 @@ void gen_load_gpr_hi(TCGv_i64 t, int reg) void gen_store_gpr_hi(TCGv_i64 t, int reg) { + assert(reg >= 0 && reg <= ARRAY_SIZE(cpu_gpr_hi)); if (reg != 0) { tcg_gen_mov_i64(cpu_gpr_hi[reg], t); } @@ -1270,19 +1268,16 @@ static inline void gen_load_srsgpr(int from, int to) TCGv_i32 t2 = tcg_temp_new_i32(); TCGv_ptr addr = tcg_temp_new_ptr(); - tcg_gen_ld_i32(t2, cpu_env, offsetof(CPUMIPSState, CP0_SRSCtl)); + tcg_gen_ld_i32(t2, tcg_env, offsetof(CPUMIPSState, CP0_SRSCtl)); tcg_gen_shri_i32(t2, t2, CP0SRSCtl_PSS); tcg_gen_andi_i32(t2, t2, 0xf); tcg_gen_muli_i32(t2, t2, sizeof(target_ulong) * 32); tcg_gen_ext_i32_ptr(addr, t2); - tcg_gen_add_ptr(addr, cpu_env, addr); + tcg_gen_add_ptr(addr, tcg_env, addr); tcg_gen_ld_tl(t0, addr, sizeof(target_ulong) * from); - tcg_temp_free_ptr(addr); - tcg_temp_free_i32(t2); } gen_store_gpr(t0, to); - tcg_temp_free(t0); } static inline void gen_store_srsgpr(int from, int to) @@ -1293,17 +1288,14 @@ static inline void gen_store_srsgpr(int from, int to) TCGv_ptr addr = tcg_temp_new_ptr(); gen_load_gpr(t0, from); - tcg_gen_ld_i32(t2, cpu_env, offsetof(CPUMIPSState, CP0_SRSCtl)); + tcg_gen_ld_i32(t2, tcg_env, offsetof(CPUMIPSState, CP0_SRSCtl)); tcg_gen_shri_i32(t2, t2, CP0SRSCtl_PSS); tcg_gen_andi_i32(t2, t2, 0xf); tcg_gen_muli_i32(t2, t2, sizeof(target_ulong) * 32); tcg_gen_ext_i32_ptr(addr, t2); - tcg_gen_add_ptr(addr, cpu_env, addr); + tcg_gen_add_ptr(addr, tcg_env, addr); tcg_gen_st_tl(t0, addr, sizeof(target_ulong) * to); - tcg_temp_free_ptr(addr); - tcg_temp_free_i32(t2); - tcg_temp_free(t0); } } @@ -1352,14 +1344,14 @@ static inline void restore_cpu_state(CPUMIPSState *env, DisasContext *ctx) void generate_exception_err(DisasContext *ctx, int excp, int err) { save_cpu_state(ctx, 1); - gen_helper_raise_exception_err(cpu_env, tcg_constant_i32(excp), + gen_helper_raise_exception_err(tcg_env, tcg_constant_i32(excp), tcg_constant_i32(err)); ctx->base.is_jmp = DISAS_NORETURN; } void generate_exception(DisasContext *ctx, int excp) { - gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp)); } void generate_exception_end(DisasContext *ctx, int excp) @@ -1371,7 +1363,7 @@ void generate_exception_break(DisasContext *ctx, int code) { #ifdef CONFIG_USER_ONLY /* Pass the break code along to cpu_loop. */ - tcg_gen_st_i32(tcg_constant_i32(code), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(code), tcg_env, offsetof(CPUMIPSState, error_code)); #endif generate_exception_end(ctx, EXCP_BREAK); @@ -1400,7 +1392,6 @@ void gen_store_fpr32(DisasContext *ctx, TCGv_i32 t, int reg) t64 = tcg_temp_new_i64(); tcg_gen_extu_i32_i64(t64, t); tcg_gen_deposit_i64(fpu_f64[reg], fpu_f64[reg], t64, 0, 32); - tcg_temp_free_i64(t64); } static void gen_load_fpr32h(DisasContext *ctx, TCGv_i32 t, int reg) @@ -1418,7 +1409,6 @@ static void gen_store_fpr32h(DisasContext *ctx, TCGv_i32 t, int reg) TCGv_i64 t64 = tcg_temp_new_i64(); tcg_gen_extu_i32_i64(t64, t); tcg_gen_deposit_i64(fpu_f64[reg], fpu_f64[reg], t64, 32, 32); - tcg_temp_free_i64(t64); } else { gen_store_fpr32(ctx, t, reg | 1); } @@ -1443,7 +1433,6 @@ void gen_store_fpr64(DisasContext *ctx, TCGv_i64 t, int reg) t0 = tcg_temp_new_i64(); tcg_gen_shri_i64(t0, t, 32); tcg_gen_deposit_i64(fpu_f64[reg | 1], fpu_f64[reg | 1], t0, 0, 32); - tcg_temp_free_i64(t0); } } @@ -1548,7 +1537,7 @@ void check_cop1x(DisasContext *ctx) */ void check_cp1_64bitmode(DisasContext *ctx) { - if (unlikely(~ctx->hflags & (MIPS_HFLAG_F64 | MIPS_HFLAG_COP1X))) { + if (unlikely(~ctx->hflags & MIPS_HFLAG_F64)) { gen_reserved_instruction(ctx); } } @@ -1856,8 +1845,6 @@ static inline void gen_cmp ## type ## _ ## fmt(DisasContext *ctx, int n, \ default: \ abort(); \ } \ - tcg_temp_free_i##bits(fp0); \ - tcg_temp_free_i##bits(fp1); \ } FOP_CONDS(, 0, d, FMT_D, 64) @@ -1881,77 +1868,75 @@ static inline void gen_r6_cmp_ ## fmt(DisasContext *ctx, int n, \ gen_ldcmp_fpr ## bits(ctx, fp1, ft); \ switch (n) { \ case 0: \ - gen_helper_r6_cmp_ ## fmt ## _af(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _af(fp0, tcg_env, fp0, fp1); \ break; \ case 1: \ - gen_helper_r6_cmp_ ## fmt ## _un(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _un(fp0, tcg_env, fp0, fp1); \ break; \ case 2: \ - gen_helper_r6_cmp_ ## fmt ## _eq(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _eq(fp0, tcg_env, fp0, fp1); \ break; \ case 3: \ - gen_helper_r6_cmp_ ## fmt ## _ueq(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _ueq(fp0, tcg_env, fp0, fp1); \ break; \ case 4: \ - gen_helper_r6_cmp_ ## fmt ## _lt(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _lt(fp0, tcg_env, fp0, fp1); \ break; \ case 5: \ - gen_helper_r6_cmp_ ## fmt ## _ult(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _ult(fp0, tcg_env, fp0, fp1); \ break; \ case 6: \ - gen_helper_r6_cmp_ ## fmt ## _le(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _le(fp0, tcg_env, fp0, fp1); \ break; \ case 7: \ - gen_helper_r6_cmp_ ## fmt ## _ule(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _ule(fp0, tcg_env, fp0, fp1); \ break; \ case 8: \ - gen_helper_r6_cmp_ ## fmt ## _saf(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _saf(fp0, tcg_env, fp0, fp1); \ break; \ case 9: \ - gen_helper_r6_cmp_ ## fmt ## _sun(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _sun(fp0, tcg_env, fp0, fp1); \ break; \ case 10: \ - gen_helper_r6_cmp_ ## fmt ## _seq(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _seq(fp0, tcg_env, fp0, fp1); \ break; \ case 11: \ - gen_helper_r6_cmp_ ## fmt ## _sueq(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _sueq(fp0, tcg_env, fp0, fp1); \ break; \ case 12: \ - gen_helper_r6_cmp_ ## fmt ## _slt(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _slt(fp0, tcg_env, fp0, fp1); \ break; \ case 13: \ - gen_helper_r6_cmp_ ## fmt ## _sult(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _sult(fp0, tcg_env, fp0, fp1); \ break; \ case 14: \ - gen_helper_r6_cmp_ ## fmt ## _sle(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _sle(fp0, tcg_env, fp0, fp1); \ break; \ case 15: \ - gen_helper_r6_cmp_ ## fmt ## _sule(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _sule(fp0, tcg_env, fp0, fp1); \ break; \ case 17: \ - gen_helper_r6_cmp_ ## fmt ## _or(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _or(fp0, tcg_env, fp0, fp1); \ break; \ case 18: \ - gen_helper_r6_cmp_ ## fmt ## _une(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _une(fp0, tcg_env, fp0, fp1); \ break; \ case 19: \ - gen_helper_r6_cmp_ ## fmt ## _ne(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _ne(fp0, tcg_env, fp0, fp1); \ break; \ case 25: \ - gen_helper_r6_cmp_ ## fmt ## _sor(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _sor(fp0, tcg_env, fp0, fp1); \ break; \ case 26: \ - gen_helper_r6_cmp_ ## fmt ## _sune(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _sune(fp0, tcg_env, fp0, fp1); \ break; \ case 27: \ - gen_helper_r6_cmp_ ## fmt ## _sne(fp0, cpu_env, fp0, fp1); \ + gen_helper_r6_cmp_ ## fmt ## _sne(fp0, tcg_env, fp0, fp1); \ break; \ default: \ abort(); \ } \ STORE; \ - tcg_temp_free_i ## bits(fp0); \ - tcg_temp_free_i ## bits(fp1); \ } FOP_CONDNS(d, FMT_D, 64, gen_store_fpr64(ctx, fp0, fd)) @@ -1962,28 +1947,27 @@ FOP_CONDNS(s, FMT_S, 32, gen_store_fpr32(ctx, fp0, fd)) /* load/store instructions. */ #ifdef CONFIG_USER_ONLY -#define OP_LD_ATOMIC(insn, fname) \ +#define OP_LD_ATOMIC(insn, memop) \ static inline void op_ld_##insn(TCGv ret, TCGv arg1, int mem_idx, \ DisasContext *ctx) \ { \ TCGv t0 = tcg_temp_new(); \ tcg_gen_mov_tl(t0, arg1); \ - tcg_gen_qemu_##fname(ret, arg1, ctx->mem_idx); \ - tcg_gen_st_tl(t0, cpu_env, offsetof(CPUMIPSState, lladdr)); \ - tcg_gen_st_tl(ret, cpu_env, offsetof(CPUMIPSState, llval)); \ - tcg_temp_free(t0); \ + tcg_gen_qemu_ld_tl(ret, arg1, ctx->mem_idx, memop); \ + tcg_gen_st_tl(t0, tcg_env, offsetof(CPUMIPSState, lladdr)); \ + tcg_gen_st_tl(ret, tcg_env, offsetof(CPUMIPSState, llval)); \ } #else #define OP_LD_ATOMIC(insn, fname) \ static inline void op_ld_##insn(TCGv ret, TCGv arg1, int mem_idx, \ DisasContext *ctx) \ { \ - gen_helper_##insn(ret, cpu_env, arg1, tcg_constant_i32(mem_idx)); \ + gen_helper_##insn(ret, tcg_env, arg1, tcg_constant_i32(mem_idx)); \ } #endif -OP_LD_ATOMIC(ll, ld32s); +OP_LD_ATOMIC(ll, MO_TESL); #if defined(TARGET_MIPS64) -OP_LD_ATOMIC(lld, ld64); +OP_LD_ATOMIC(lld, MO_TEUQ); #endif #undef OP_LD_ATOMIC @@ -2013,11 +1997,65 @@ static target_ulong pc_relative_pc(DisasContext *ctx) return pc; } +/* LWL or LDL, depending on MemOp. */ +static void gen_lxl(DisasContext *ctx, TCGv reg, TCGv addr, + int mem_idx, MemOp mop) +{ + int sizem1 = memop_size(mop) - 1; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + /* + * Do a byte access to possibly trigger a page + * fault with the unaligned address. + */ + tcg_gen_qemu_ld_tl(t1, addr, mem_idx, MO_UB); + tcg_gen_andi_tl(t1, addr, sizem1); + if (!cpu_is_bigendian(ctx)) { + tcg_gen_xori_tl(t1, t1, sizem1); + } + tcg_gen_shli_tl(t1, t1, 3); + tcg_gen_andi_tl(t0, addr, ~sizem1); + tcg_gen_qemu_ld_tl(t0, t0, mem_idx, mop); + tcg_gen_shl_tl(t0, t0, t1); + tcg_gen_shl_tl(t1, tcg_constant_tl(-1), t1); + tcg_gen_andc_tl(t1, reg, t1); + tcg_gen_or_tl(reg, t0, t1); +} + +/* LWR or LDR, depending on MemOp. */ +static void gen_lxr(DisasContext *ctx, TCGv reg, TCGv addr, + int mem_idx, MemOp mop) +{ + int size = memop_size(mop); + int sizem1 = size - 1; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + /* + * Do a byte access to possibly trigger a page + * fault with the unaligned address. + */ + tcg_gen_qemu_ld_tl(t1, addr, mem_idx, MO_UB); + tcg_gen_andi_tl(t1, addr, sizem1); + if (cpu_is_bigendian(ctx)) { + tcg_gen_xori_tl(t1, t1, sizem1); + } + tcg_gen_shli_tl(t1, t1, 3); + tcg_gen_andi_tl(t0, addr, ~sizem1); + tcg_gen_qemu_ld_tl(t0, t0, mem_idx, mop); + tcg_gen_shr_tl(t0, t0, t1); + tcg_gen_xori_tl(t1, t1, size * 8 - 1); + tcg_gen_shl_tl(t1, tcg_constant_tl(~1), t1); + tcg_gen_and_tl(t1, reg, t1); + tcg_gen_or_tl(reg, t0, t1); +} + /* Load */ static void gen_ld(DisasContext *ctx, uint32_t opc, int rt, int base, int offset) { - TCGv t0, t1, t2; + TCGv t0, t1; int mem_idx = ctx->mem_idx; if (rt == 0 && ctx->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F | @@ -2052,65 +2090,26 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, break; case OPC_LDL: t1 = tcg_temp_new(); - /* - * Do a byte access to possibly trigger a page - * fault with the unaligned address. - */ - tcg_gen_qemu_ld_tl(t1, t0, mem_idx, MO_UB); - tcg_gen_andi_tl(t1, t0, 7); - if (!cpu_is_bigendian(ctx)) { - tcg_gen_xori_tl(t1, t1, 7); - } - tcg_gen_shli_tl(t1, t1, 3); - tcg_gen_andi_tl(t0, t0, ~7); - tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TEUQ); - tcg_gen_shl_tl(t0, t0, t1); - t2 = tcg_const_tl(-1); - tcg_gen_shl_tl(t2, t2, t1); gen_load_gpr(t1, rt); - tcg_gen_andc_tl(t1, t1, t2); - tcg_temp_free(t2); - tcg_gen_or_tl(t0, t0, t1); - tcg_temp_free(t1); - gen_store_gpr(t0, rt); + gen_lxl(ctx, t1, t0, mem_idx, MO_TEUQ); + gen_store_gpr(t1, rt); break; case OPC_LDR: t1 = tcg_temp_new(); - /* - * Do a byte access to possibly trigger a page - * fault with the unaligned address. - */ - tcg_gen_qemu_ld_tl(t1, t0, mem_idx, MO_UB); - tcg_gen_andi_tl(t1, t0, 7); - if (cpu_is_bigendian(ctx)) { - tcg_gen_xori_tl(t1, t1, 7); - } - tcg_gen_shli_tl(t1, t1, 3); - tcg_gen_andi_tl(t0, t0, ~7); - tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TEUQ); - tcg_gen_shr_tl(t0, t0, t1); - tcg_gen_xori_tl(t1, t1, 63); - t2 = tcg_const_tl(0xfffffffffffffffeull); - tcg_gen_shl_tl(t2, t2, t1); gen_load_gpr(t1, rt); - tcg_gen_and_tl(t1, t1, t2); - tcg_temp_free(t2); - tcg_gen_or_tl(t0, t0, t1); - tcg_temp_free(t1); - gen_store_gpr(t0, rt); + gen_lxr(ctx, t1, t0, mem_idx, MO_TEUQ); + gen_store_gpr(t1, rt); break; case OPC_LDPC: - t1 = tcg_const_tl(pc_relative_pc(ctx)); + t1 = tcg_constant_tl(pc_relative_pc(ctx)); gen_op_addr_add(ctx, t0, t0, t1); - tcg_temp_free(t1); tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TEUQ); gen_store_gpr(t0, rt); break; #endif case OPC_LWPC: - t1 = tcg_const_tl(pc_relative_pc(ctx)); + t1 = tcg_constant_tl(pc_relative_pc(ctx)); gen_op_addr_add(ctx, t0, t0, t1); - tcg_temp_free(t1); tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TESL); gen_store_gpr(t0, rt); break; @@ -2157,57 +2156,20 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, /* fall through */ case OPC_LWL: t1 = tcg_temp_new(); - /* - * Do a byte access to possibly trigger a page - * fault with the unaligned address. - */ - tcg_gen_qemu_ld_tl(t1, t0, mem_idx, MO_UB); - tcg_gen_andi_tl(t1, t0, 3); - if (!cpu_is_bigendian(ctx)) { - tcg_gen_xori_tl(t1, t1, 3); - } - tcg_gen_shli_tl(t1, t1, 3); - tcg_gen_andi_tl(t0, t0, ~3); - tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TEUL); - tcg_gen_shl_tl(t0, t0, t1); - t2 = tcg_const_tl(-1); - tcg_gen_shl_tl(t2, t2, t1); gen_load_gpr(t1, rt); - tcg_gen_andc_tl(t1, t1, t2); - tcg_temp_free(t2); - tcg_gen_or_tl(t0, t0, t1); - tcg_temp_free(t1); - tcg_gen_ext32s_tl(t0, t0); - gen_store_gpr(t0, rt); + gen_lxl(ctx, t1, t0, mem_idx, MO_TEUL); + tcg_gen_ext32s_tl(t1, t1); + gen_store_gpr(t1, rt); break; case OPC_LWRE: mem_idx = MIPS_HFLAG_UM; /* fall through */ case OPC_LWR: t1 = tcg_temp_new(); - /* - * Do a byte access to possibly trigger a page - * fault with the unaligned address. - */ - tcg_gen_qemu_ld_tl(t1, t0, mem_idx, MO_UB); - tcg_gen_andi_tl(t1, t0, 3); - if (cpu_is_bigendian(ctx)) { - tcg_gen_xori_tl(t1, t1, 3); - } - tcg_gen_shli_tl(t1, t1, 3); - tcg_gen_andi_tl(t0, t0, ~3); - tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TEUL); - tcg_gen_shr_tl(t0, t0, t1); - tcg_gen_xori_tl(t1, t1, 31); - t2 = tcg_const_tl(0xfffffffeull); - tcg_gen_shl_tl(t2, t2, t1); gen_load_gpr(t1, rt); - tcg_gen_and_tl(t1, t1, t2); - tcg_temp_free(t2); - tcg_gen_or_tl(t0, t0, t1); - tcg_temp_free(t1); - tcg_gen_ext32s_tl(t0, t0); - gen_store_gpr(t0, rt); + gen_lxr(ctx, t1, t0, mem_idx, MO_TEUL); + tcg_gen_ext32s_tl(t1, t1); + gen_store_gpr(t1, rt); break; case OPC_LLE: mem_idx = MIPS_HFLAG_UM; @@ -2218,7 +2180,6 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, gen_store_gpr(t0, rt); break; } - tcg_temp_free(t0); } /* Store */ @@ -2277,8 +2238,6 @@ static void gen_st(DisasContext *ctx, uint32_t opc, int rt, gen_helper_0e2i(swr, t1, t0, mem_idx); break; } - tcg_temp_free(t0); - tcg_temp_free(t1); } @@ -2295,7 +2254,6 @@ static void gen_st_cond(DisasContext *ctx, int rt, int base, int offset, /* compare the address against that of the preceding LL */ gen_base_offset_addr(ctx, addr, base, offset); tcg_gen_brcond_tl(TCG_COND_EQ, addr, cpu_lladdr, l1); - tcg_temp_free(addr); tcg_gen_movi_tl(t0, 0); gen_store_gpr(t0, rt); tcg_gen_br(done); @@ -2308,10 +2266,8 @@ static void gen_st_cond(DisasContext *ctx, int rt, int base, int offset, eva ? MIPS_HFLAG_UM : ctx->mem_idx, tcg_mo); tcg_gen_setcond_tl(TCG_COND_EQ, t0, t0, cpu_llval); gen_store_gpr(t0, rt); - tcg_temp_free(val); gen_set_label(done); - tcg_temp_free(t0); } /* Load and store */ @@ -2329,7 +2285,6 @@ static void gen_flt_ldst(DisasContext *ctx, uint32_t opc, int ft, tcg_gen_qemu_ld_i32(fp0, t0, ctx->mem_idx, MO_TESL | ctx->default_tcg_memop_mask); gen_store_fpr32(ctx, fp0, ft); - tcg_temp_free_i32(fp0); } break; case OPC_SWC1: @@ -2338,7 +2293,6 @@ static void gen_flt_ldst(DisasContext *ctx, uint32_t opc, int ft, gen_load_fpr32(ctx, fp0, ft); tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL | ctx->default_tcg_memop_mask); - tcg_temp_free_i32(fp0); } break; case OPC_LDC1: @@ -2347,7 +2301,6 @@ static void gen_flt_ldst(DisasContext *ctx, uint32_t opc, int ft, tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEUQ | ctx->default_tcg_memop_mask); gen_store_fpr64(ctx, fp0, ft); - tcg_temp_free_i64(fp0); } break; case OPC_SDC1: @@ -2356,7 +2309,6 @@ static void gen_flt_ldst(DisasContext *ctx, uint32_t opc, int ft, gen_load_fpr64(ctx, fp0, ft); tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEUQ | ctx->default_tcg_memop_mask); - tcg_temp_free_i64(fp0); } break; default: @@ -2385,7 +2337,6 @@ static void gen_cop1_ldst(DisasContext *ctx, uint32_t op, int rt, } else { generate_exception_err(ctx, EXCP_CpU, 1); } - tcg_temp_free(t0); } /* Arithmetic with immediate operand */ @@ -2404,7 +2355,7 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, switch (opc) { case OPC_ADDI: { - TCGv t0 = tcg_temp_local_new(); + TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); TCGLabel *l1 = gen_new_label(); @@ -2416,15 +2367,12 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, tcg_gen_xori_tl(t1, t1, ~uimm); tcg_gen_xori_tl(t2, t0, uimm); tcg_gen_and_tl(t1, t1, t2); - tcg_temp_free(t2); tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); - tcg_temp_free(t1); /* operands of same sign, result different sign */ generate_exception(ctx, EXCP_OVERFLOW); gen_set_label(l1); tcg_gen_ext32s_tl(t0, t0); gen_store_gpr(t0, rt); - tcg_temp_free(t0); } break; case OPC_ADDIU: @@ -2438,7 +2386,7 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, #if defined(TARGET_MIPS64) case OPC_DADDI: { - TCGv t0 = tcg_temp_local_new(); + TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); TCGLabel *l1 = gen_new_label(); @@ -2449,14 +2397,11 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, tcg_gen_xori_tl(t1, t1, ~uimm); tcg_gen_xori_tl(t2, t0, uimm); tcg_gen_and_tl(t1, t1, t2); - tcg_temp_free(t2); tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); - tcg_temp_free(t1); /* operands of same sign, result different sign */ generate_exception(ctx, EXCP_OVERFLOW); gen_set_label(l1); gen_store_gpr(t0, rt); - tcg_temp_free(t0); } break; case OPC_DADDIU: @@ -2539,7 +2484,6 @@ static void gen_slt_imm(DisasContext *ctx, uint32_t opc, tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_gpr[rt], t0, uimm); break; } - tcg_temp_free(t0); } /* Shifts with immediate operand */ @@ -2579,7 +2523,6 @@ static void gen_shift_imm(DisasContext *ctx, uint32_t opc, tcg_gen_trunc_tl_i32(t1, t0); tcg_gen_rotri_i32(t1, t1, uimm); tcg_gen_ext_i32_tl(cpu_gpr[rt], t1); - tcg_temp_free_i32(t1); } else { tcg_gen_ext32s_tl(cpu_gpr[rt], t0); } @@ -2615,7 +2558,6 @@ static void gen_shift_imm(DisasContext *ctx, uint32_t opc, break; #endif } - tcg_temp_free(t0); } /* Arithmetic */ @@ -2634,7 +2576,7 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, switch (opc) { case OPC_ADD: { - TCGv t0 = tcg_temp_local_new(); + TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); TCGLabel *l1 = gen_new_label(); @@ -2646,14 +2588,11 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, tcg_gen_xor_tl(t1, t1, t2); tcg_gen_xor_tl(t2, t0, t2); tcg_gen_andc_tl(t1, t2, t1); - tcg_temp_free(t2); tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); - tcg_temp_free(t1); /* operands of same sign, result different sign */ generate_exception(ctx, EXCP_OVERFLOW); gen_set_label(l1); gen_store_gpr(t0, rd); - tcg_temp_free(t0); } break; case OPC_ADDU: @@ -2670,7 +2609,7 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, break; case OPC_SUB: { - TCGv t0 = tcg_temp_local_new(); + TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); TCGLabel *l1 = gen_new_label(); @@ -2682,9 +2621,7 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, tcg_gen_xor_tl(t2, t1, t2); tcg_gen_xor_tl(t1, t0, t1); tcg_gen_and_tl(t1, t1, t2); - tcg_temp_free(t2); tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); - tcg_temp_free(t1); /* * operands of different sign, first operand and the result * of different sign @@ -2692,7 +2629,6 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, generate_exception(ctx, EXCP_OVERFLOW); gen_set_label(l1); gen_store_gpr(t0, rd); - tcg_temp_free(t0); } break; case OPC_SUBU: @@ -2711,7 +2647,7 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, #if defined(TARGET_MIPS64) case OPC_DADD: { - TCGv t0 = tcg_temp_local_new(); + TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); TCGLabel *l1 = gen_new_label(); @@ -2722,14 +2658,11 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, tcg_gen_xor_tl(t1, t1, t2); tcg_gen_xor_tl(t2, t0, t2); tcg_gen_andc_tl(t1, t2, t1); - tcg_temp_free(t2); tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); - tcg_temp_free(t1); /* operands of same sign, result different sign */ generate_exception(ctx, EXCP_OVERFLOW); gen_set_label(l1); gen_store_gpr(t0, rd); - tcg_temp_free(t0); } break; case OPC_DADDU: @@ -2745,7 +2678,7 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, break; case OPC_DSUB: { - TCGv t0 = tcg_temp_local_new(); + TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); TCGLabel *l1 = gen_new_label(); @@ -2756,9 +2689,7 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, tcg_gen_xor_tl(t2, t1, t2); tcg_gen_xor_tl(t1, t0, t1); tcg_gen_and_tl(t1, t1, t2); - tcg_temp_free(t2); tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); - tcg_temp_free(t1); /* * Operands of different sign, first operand and result different * sign. @@ -2766,7 +2697,6 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, generate_exception(ctx, EXCP_OVERFLOW); gen_set_label(l1); gen_store_gpr(t0, rd); - tcg_temp_free(t0); } break; case OPC_DSUBU: @@ -2805,7 +2735,7 @@ static void gen_cond_move(DisasContext *ctx, uint32_t opc, t0 = tcg_temp_new(); gen_load_gpr(t0, rt); - t1 = tcg_const_tl(0); + t1 = tcg_constant_tl(0); t2 = tcg_temp_new(); gen_load_gpr(t2, rs); switch (opc) { @@ -2822,9 +2752,6 @@ static void gen_cond_move(DisasContext *ctx, uint32_t opc, tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr[rd], t0, t1, t2, t1); break; } - tcg_temp_free(t2); - tcg_temp_free(t1); - tcg_temp_free(t0); } /* Logic */ @@ -2903,8 +2830,6 @@ static void gen_slt(DisasContext *ctx, uint32_t opc, tcg_gen_setcond_tl(TCG_COND_LTU, cpu_gpr[rd], t0, t1); break; } - tcg_temp_free(t0); - tcg_temp_free(t1); } /* Shifts */ @@ -2951,8 +2876,6 @@ static void gen_shift(DisasContext *ctx, uint32_t opc, tcg_gen_andi_i32(t2, t2, 0x1f); tcg_gen_rotr_i32(t2, t3, t2); tcg_gen_ext_i32_tl(cpu_gpr[rd], t2); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); } break; #if defined(TARGET_MIPS64) @@ -2974,8 +2897,6 @@ static void gen_shift(DisasContext *ctx, uint32_t opc, break; #endif } - tcg_temp_free(t0); - tcg_temp_free(t1); } /* Arithmetic on HI/LO registers */ @@ -3045,10 +2966,9 @@ static void gen_HILO(DisasContext *ctx, uint32_t opc, int acc, int reg) static inline void gen_r6_ld(target_long addr, int reg, int memidx, MemOp memop) { - TCGv t0 = tcg_const_tl(addr); - tcg_gen_qemu_ld_tl(t0, t0, memidx, memop); + TCGv t0 = tcg_temp_new(); + tcg_gen_qemu_ld_tl(t0, tcg_constant_tl(addr), memidx, memop); gen_store_gpr(t0, reg); - tcg_temp_free(t0); } static inline void gen_pcrel(DisasContext *ctx, int opc, target_ulong pc, @@ -3145,8 +3065,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); tcg_gen_div_tl(cpu_gpr[rd], t0, t1); tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case R6_OPC_MOD: @@ -3164,34 +3082,28 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case R6_OPC_DIVU: { - TCGv t2 = tcg_const_tl(0); - TCGv t3 = tcg_const_tl(1); + TCGv t2 = tcg_constant_tl(0); + TCGv t3 = tcg_constant_tl(1); tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); tcg_gen_divu_tl(cpu_gpr[rd], t0, t1); tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case R6_OPC_MODU: { - TCGv t2 = tcg_const_tl(0); - TCGv t3 = tcg_const_tl(1); + TCGv t2 = tcg_constant_tl(0); + TCGv t3 = tcg_constant_tl(1); tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); tcg_gen_remu_tl(cpu_gpr[rd], t0, t1); tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case R6_OPC_MUL: @@ -3202,8 +3114,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_gen_trunc_tl_i32(t3, t1); tcg_gen_mul_i32(t2, t2, t3); tcg_gen_ext_i32_tl(cpu_gpr[rd], t2); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); } break; case R6_OPC_MUH: @@ -3214,8 +3124,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_gen_trunc_tl_i32(t3, t1); tcg_gen_muls2_i32(t2, t3, t2, t3); tcg_gen_ext_i32_tl(cpu_gpr[rd], t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); } break; case R6_OPC_MULU: @@ -3226,8 +3134,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_gen_trunc_tl_i32(t3, t1); tcg_gen_mul_i32(t2, t2, t3); tcg_gen_ext_i32_tl(cpu_gpr[rd], t2); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); } break; case R6_OPC_MUHU: @@ -3238,8 +3144,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_gen_trunc_tl_i32(t3, t1); tcg_gen_mulu2_i32(t2, t3, t2, t3); tcg_gen_ext_i32_tl(cpu_gpr[rd], t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); } break; #if defined(TARGET_MIPS64) @@ -3255,8 +3159,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_gen_movi_tl(t3, 0); tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); tcg_gen_div_tl(cpu_gpr[rd], t0, t1); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case R6_OPC_DMOD: @@ -3271,28 +3173,22 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_gen_movi_tl(t3, 0); tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case R6_OPC_DDIVU: { - TCGv t2 = tcg_const_tl(0); - TCGv t3 = tcg_const_tl(1); + TCGv t2 = tcg_constant_tl(0); + TCGv t3 = tcg_constant_tl(1); tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); tcg_gen_divu_i64(cpu_gpr[rd], t0, t1); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case R6_OPC_DMODU: { - TCGv t2 = tcg_const_tl(0); - TCGv t3 = tcg_const_tl(1); + TCGv t2 = tcg_constant_tl(0); + TCGv t3 = tcg_constant_tl(1); tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); tcg_gen_remu_i64(cpu_gpr[rd], t0, t1); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case R6_OPC_DMUL: @@ -3302,7 +3198,6 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) { TCGv t2 = tcg_temp_new(); tcg_gen_muls2_i64(t2, cpu_gpr[rd], t0, t1); - tcg_temp_free(t2); } break; case R6_OPC_DMULU: @@ -3312,18 +3207,14 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) { TCGv t2 = tcg_temp_new(); tcg_gen_mulu2_i64(t2, cpu_gpr[rd], t0, t1); - tcg_temp_free(t2); } break; #endif default: MIPS_INVAL("r6 mul/div"); gen_reserved_instruction(ctx); - goto out; + break; } - out: - tcg_temp_free(t0); - tcg_temp_free(t1); } #if defined(TARGET_MIPS64) @@ -3355,14 +3246,12 @@ static void gen_div1_tx79(DisasContext *ctx, uint32_t opc, int rs, int rt) tcg_gen_rem_tl(cpu_HI[1], t0, t1); tcg_gen_ext32s_tl(cpu_LO[1], cpu_LO[1]); tcg_gen_ext32s_tl(cpu_HI[1], cpu_HI[1]); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case MMI_OPC_DIVU1: { - TCGv t2 = tcg_const_tl(0); - TCGv t3 = tcg_const_tl(1); + TCGv t2 = tcg_constant_tl(0); + TCGv t3 = tcg_constant_tl(1); tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); @@ -3370,18 +3259,13 @@ static void gen_div1_tx79(DisasContext *ctx, uint32_t opc, int rs, int rt) tcg_gen_remu_tl(cpu_HI[1], t0, t1); tcg_gen_ext32s_tl(cpu_LO[1], cpu_LO[1]); tcg_gen_ext32s_tl(cpu_HI[1], cpu_HI[1]); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; default: MIPS_INVAL("div1 TX79"); gen_reserved_instruction(ctx); - goto out; + break; } - out: - tcg_temp_free(t0); - tcg_temp_free(t1); } #endif @@ -3418,14 +3302,12 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_rem_tl(cpu_HI[acc], t0, t1); tcg_gen_ext32s_tl(cpu_LO[acc], cpu_LO[acc]); tcg_gen_ext32s_tl(cpu_HI[acc], cpu_HI[acc]); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case OPC_DIVU: { - TCGv t2 = tcg_const_tl(0); - TCGv t3 = tcg_const_tl(1); + TCGv t2 = tcg_constant_tl(0); + TCGv t3 = tcg_constant_tl(1); tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); @@ -3433,8 +3315,6 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_remu_tl(cpu_HI[acc], t0, t1); tcg_gen_ext32s_tl(cpu_LO[acc], cpu_LO[acc]); tcg_gen_ext32s_tl(cpu_HI[acc], cpu_HI[acc]); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case OPC_MULT: @@ -3446,8 +3326,6 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_muls2_i32(t2, t3, t2, t3); tcg_gen_ext_i32_tl(cpu_LO[acc], t2); tcg_gen_ext_i32_tl(cpu_HI[acc], t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); } break; case OPC_MULTU: @@ -3459,8 +3337,6 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_mulu2_i32(t2, t3, t2, t3); tcg_gen_ext_i32_tl(cpu_LO[acc], t2); tcg_gen_ext_i32_tl(cpu_HI[acc], t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); } break; #if defined(TARGET_MIPS64) @@ -3477,19 +3353,15 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); tcg_gen_div_tl(cpu_LO[acc], t0, t1); tcg_gen_rem_tl(cpu_HI[acc], t0, t1); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case OPC_DDIVU: { - TCGv t2 = tcg_const_tl(0); - TCGv t3 = tcg_const_tl(1); + TCGv t2 = tcg_constant_tl(0); + TCGv t3 = tcg_constant_tl(1); tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); tcg_gen_divu_i64(cpu_LO[acc], t0, t1); tcg_gen_remu_i64(cpu_HI[acc], t0, t1); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case OPC_DMULT: @@ -3509,10 +3381,8 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_mul_i64(t2, t2, t3); tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); tcg_gen_add_i64(t2, t2, t3); - tcg_temp_free_i64(t3); gen_move_low32(cpu_LO[acc], t2); gen_move_high32(cpu_HI[acc], t2); - tcg_temp_free_i64(t2); } break; case OPC_MADDU: @@ -3527,10 +3397,8 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_mul_i64(t2, t2, t3); tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); tcg_gen_add_i64(t2, t2, t3); - tcg_temp_free_i64(t3); gen_move_low32(cpu_LO[acc], t2); gen_move_high32(cpu_HI[acc], t2); - tcg_temp_free_i64(t2); } break; case OPC_MSUB: @@ -3543,10 +3411,8 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_mul_i64(t2, t2, t3); tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); tcg_gen_sub_i64(t2, t3, t2); - tcg_temp_free_i64(t3); gen_move_low32(cpu_LO[acc], t2); gen_move_high32(cpu_HI[acc], t2); - tcg_temp_free_i64(t2); } break; case OPC_MSUBU: @@ -3561,20 +3427,15 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_mul_i64(t2, t2, t3); tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); tcg_gen_sub_i64(t2, t3, t2); - tcg_temp_free_i64(t3); gen_move_low32(cpu_LO[acc], t2); gen_move_high32(cpu_HI[acc], t2); - tcg_temp_free_i64(t2); } break; default: MIPS_INVAL("mul/div"); gen_reserved_instruction(ctx); - goto out; + break; } - out: - tcg_temp_free(t0); - tcg_temp_free(t1); } /* @@ -3629,8 +3490,6 @@ static void gen_mul_txx9(DisasContext *ctx, uint32_t opc, } tcg_gen_ext_i32_tl(cpu_LO[acc], t2); tcg_gen_ext_i32_tl(cpu_HI[acc], t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); } break; case MMI_OPC_MULTU1: @@ -3648,8 +3507,6 @@ static void gen_mul_txx9(DisasContext *ctx, uint32_t opc, } tcg_gen_ext_i32_tl(cpu_LO[acc], t2); tcg_gen_ext_i32_tl(cpu_HI[acc], t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); } break; case MMI_OPC_MADD1: @@ -3665,13 +3522,11 @@ static void gen_mul_txx9(DisasContext *ctx, uint32_t opc, tcg_gen_mul_i64(t2, t2, t3); tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); tcg_gen_add_i64(t2, t2, t3); - tcg_temp_free_i64(t3); gen_move_low32(cpu_LO[acc], t2); gen_move_high32(cpu_HI[acc], t2); if (rd) { gen_move_low32(cpu_gpr[rd], t2); } - tcg_temp_free_i64(t2); } break; case MMI_OPC_MADDU1: @@ -3689,24 +3544,18 @@ static void gen_mul_txx9(DisasContext *ctx, uint32_t opc, tcg_gen_mul_i64(t2, t2, t3); tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); tcg_gen_add_i64(t2, t2, t3); - tcg_temp_free_i64(t3); gen_move_low32(cpu_LO[acc], t2); gen_move_high32(cpu_HI[acc], t2); if (rd) { gen_move_low32(cpu_gpr[rd], t2); } - tcg_temp_free_i64(t2); } break; default: MIPS_INVAL("mul/madd TXx9"); gen_reserved_instruction(ctx); - goto out; + break; } - - out: - tcg_temp_free(t0); - tcg_temp_free(t1); } static void gen_cl(DisasContext *ctx, uint32_t opc, @@ -3763,26 +3612,8 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, return; } - switch (opc) { - case OPC_MULT_G_2E: - case OPC_MULT_G_2F: - case OPC_MULTU_G_2E: - case OPC_MULTU_G_2F: -#if defined(TARGET_MIPS64) - case OPC_DMULT_G_2E: - case OPC_DMULT_G_2F: - case OPC_DMULTU_G_2E: - case OPC_DMULTU_G_2F: -#endif - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); - break; - default: - t0 = tcg_temp_local_new(); - t1 = tcg_temp_local_new(); - break; - } - + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); @@ -3946,9 +3777,6 @@ static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, break; #endif } - - tcg_temp_free(t0); - tcg_temp_free(t1); } /* Loongson multimedia instructions */ @@ -3959,21 +3787,10 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) TCGCond cond; opc = MASK_LMMI(ctx->opcode); - switch (opc) { - case OPC_ADD_CP2: - case OPC_SUB_CP2: - case OPC_DADD_CP2: - case OPC_DSUB_CP2: - t0 = tcg_temp_local_new_i64(); - t1 = tcg_temp_local_new_i64(); - break; - default: - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - break; - } - check_cp1_enabled(ctx); + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); gen_load_fpr64(ctx, t0, rs); gen_load_fpr64(ctx, t1, rt); @@ -4254,7 +4071,6 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) tcg_gen_xor_i64(t1, t1, t2); tcg_gen_xor_i64(t2, t2, t0); tcg_gen_andc_i64(t1, t2, t1); - tcg_temp_free_i64(t2); tcg_gen_brcondi_i64(TCG_COND_GE, t1, 0, lab); generate_exception(ctx, EXCP_OVERFLOW); gen_set_label(lab); @@ -4275,7 +4091,6 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) tcg_gen_xor_i64(t1, t1, t2); tcg_gen_xor_i64(t2, t2, t0); tcg_gen_and_i64(t1, t1, t2); - tcg_temp_free_i64(t2); tcg_gen_brcondi_i64(TCG_COND_GE, t1, 0, lab); generate_exception(ctx, EXCP_OVERFLOW); gen_set_label(lab); @@ -4317,12 +4132,8 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) 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; + return; default: MIPS_INVAL("loongson_cp2"); gen_reserved_instruction(ctx); @@ -4330,16 +4141,12 @@ 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); } static void gen_loongson_lswc2(DisasContext *ctx, int rt, int rs, int rd) { - TCGv t0, t1, t2; + TCGv t0, t1; TCGv_i32 fp0; #if defined(TARGET_MIPS64) int lsq_rt1 = ctx->opcode & 0x1f; @@ -4361,7 +4168,6 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, ctx->default_tcg_memop_mask); gen_store_gpr(t1, rt); gen_store_gpr(t0, lsq_rt1); - tcg_temp_free(t1); break; case OPC_GSLQC1: check_cp1_enabled(ctx); @@ -4374,7 +4180,6 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, ctx->default_tcg_memop_mask); gen_store_fpr64(ctx, t1, rt); gen_store_fpr64(ctx, t0, lsq_rt1); - tcg_temp_free(t1); break; case OPC_GSSQ: t1 = tcg_temp_new(); @@ -4386,7 +4191,6 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, gen_load_gpr(t1, lsq_rt1); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUQ | ctx->default_tcg_memop_mask); - tcg_temp_free(t1); break; case OPC_GSSQC1: check_cp1_enabled(ctx); @@ -4399,7 +4203,6 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, gen_load_fpr64(ctx, t1, lsq_rt1); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUQ | ctx->default_tcg_memop_mask); - tcg_temp_free(t1); break; #endif case OPC_GSSHFL: @@ -4407,109 +4210,41 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, case OPC_GSLWLC1: check_cp1_enabled(ctx); gen_base_offset_addr(ctx, t0, rs, shf_offset); - t1 = tcg_temp_new(); - tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); - tcg_gen_andi_tl(t1, t0, 3); - if (!cpu_is_bigendian(ctx)) { - tcg_gen_xori_tl(t1, t1, 3); - } - tcg_gen_shli_tl(t1, t1, 3); - tcg_gen_andi_tl(t0, t0, ~3); - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL); - tcg_gen_shl_tl(t0, t0, t1); - t2 = tcg_const_tl(-1); - tcg_gen_shl_tl(t2, t2, t1); fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, rt); + t1 = tcg_temp_new(); tcg_gen_ext_i32_tl(t1, fp0); - tcg_gen_andc_tl(t1, t1, t2); - tcg_temp_free(t2); - tcg_gen_or_tl(t0, t0, t1); - tcg_temp_free(t1); -#if defined(TARGET_MIPS64) - tcg_gen_extrl_i64_i32(fp0, t0); -#else - tcg_gen_ext32s_tl(fp0, t0); -#endif + gen_lxl(ctx, t1, t0, ctx->mem_idx, MO_TEUL); + tcg_gen_trunc_tl_i32(fp0, t1); gen_store_fpr32(ctx, fp0, rt); - tcg_temp_free_i32(fp0); break; case OPC_GSLWRC1: check_cp1_enabled(ctx); gen_base_offset_addr(ctx, t0, rs, shf_offset); - t1 = tcg_temp_new(); - tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); - tcg_gen_andi_tl(t1, t0, 3); - if (cpu_is_bigendian(ctx)) { - tcg_gen_xori_tl(t1, t1, 3); - } - tcg_gen_shli_tl(t1, t1, 3); - tcg_gen_andi_tl(t0, t0, ~3); - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL); - tcg_gen_shr_tl(t0, t0, t1); - tcg_gen_xori_tl(t1, t1, 31); - t2 = tcg_const_tl(0xfffffffeull); - tcg_gen_shl_tl(t2, t2, t1); fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, rt); + t1 = tcg_temp_new(); tcg_gen_ext_i32_tl(t1, fp0); - tcg_gen_and_tl(t1, t1, t2); - tcg_temp_free(t2); - tcg_gen_or_tl(t0, t0, t1); - tcg_temp_free(t1); -#if defined(TARGET_MIPS64) - tcg_gen_extrl_i64_i32(fp0, t0); -#else - tcg_gen_ext32s_tl(fp0, t0); -#endif + gen_lxr(ctx, t1, t0, ctx->mem_idx, MO_TEUL); + tcg_gen_trunc_tl_i32(fp0, t1); gen_store_fpr32(ctx, fp0, rt); - tcg_temp_free_i32(fp0); break; #if defined(TARGET_MIPS64) case OPC_GSLDLC1: check_cp1_enabled(ctx); gen_base_offset_addr(ctx, t0, rs, shf_offset); t1 = tcg_temp_new(); - tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); - tcg_gen_andi_tl(t1, t0, 7); - if (!cpu_is_bigendian(ctx)) { - tcg_gen_xori_tl(t1, t1, 7); - } - tcg_gen_shli_tl(t1, t1, 3); - tcg_gen_andi_tl(t0, t0, ~7); - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUQ); - tcg_gen_shl_tl(t0, t0, t1); - t2 = tcg_const_tl(-1); - tcg_gen_shl_tl(t2, t2, t1); gen_load_fpr64(ctx, t1, rt); - tcg_gen_andc_tl(t1, t1, t2); - tcg_temp_free(t2); - tcg_gen_or_tl(t0, t0, t1); - tcg_temp_free(t1); - gen_store_fpr64(ctx, t0, rt); + gen_lxl(ctx, t1, t0, ctx->mem_idx, MO_TEUQ); + gen_store_fpr64(ctx, t1, rt); break; case OPC_GSLDRC1: check_cp1_enabled(ctx); gen_base_offset_addr(ctx, t0, rs, shf_offset); t1 = tcg_temp_new(); - tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); - tcg_gen_andi_tl(t1, t0, 7); - if (cpu_is_bigendian(ctx)) { - tcg_gen_xori_tl(t1, t1, 7); - } - tcg_gen_shli_tl(t1, t1, 3); - tcg_gen_andi_tl(t0, t0, ~7); - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUQ); - tcg_gen_shr_tl(t0, t0, t1); - tcg_gen_xori_tl(t1, t1, 63); - t2 = tcg_const_tl(0xfffffffffffffffeull); - tcg_gen_shl_tl(t2, t2, t1); gen_load_fpr64(ctx, t1, rt); - tcg_gen_and_tl(t1, t1, t2); - tcg_temp_free(t2); - tcg_gen_or_tl(t0, t0, t1); - tcg_temp_free(t1); - gen_store_fpr64(ctx, t0, rt); + gen_lxr(ctx, t1, t0, ctx->mem_idx, MO_TEUQ); + gen_store_fpr64(ctx, t1, rt); break; #endif default: @@ -4528,8 +4263,6 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, gen_load_fpr32(ctx, fp0, rt); tcg_gen_ext_i32_tl(t1, fp0); gen_helper_0e2i(swl, t1, t0, ctx->mem_idx); - tcg_temp_free_i32(fp0); - tcg_temp_free(t1); break; case OPC_GSSWRC1: check_cp1_enabled(ctx); @@ -4539,8 +4272,6 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, gen_load_fpr32(ctx, fp0, rt); tcg_gen_ext_i32_tl(t1, fp0); gen_helper_0e2i(swr, t1, t0, ctx->mem_idx); - tcg_temp_free_i32(fp0); - tcg_temp_free(t1); break; #if defined(TARGET_MIPS64) case OPC_GSSDLC1: @@ -4549,7 +4280,6 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, gen_base_offset_addr(ctx, t0, rs, shf_offset); gen_load_fpr64(ctx, t1, rt); gen_helper_0e2i(sdl, t1, t0, ctx->mem_idx); - tcg_temp_free(t1); break; case OPC_GSSDRC1: check_cp1_enabled(ctx); @@ -4557,7 +4287,6 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, gen_base_offset_addr(ctx, t0, rs, shf_offset); gen_load_fpr64(ctx, t1, rt); gen_helper_0e2i(sdr, t1, t0, ctx->mem_idx); - tcg_temp_free(t1); break; #endif default: @@ -4571,7 +4300,6 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, gen_reserved_instruction(ctx); break; } - tcg_temp_free(t0); } /* Loongson EXT LDC2/SDC2 */ @@ -4666,7 +4394,6 @@ static void gen_loongson_lsdc2(DisasContext *ctx, int rt, tcg_gen_qemu_ld_i32(fp0, t0, ctx->mem_idx, MO_TESL | ctx->default_tcg_memop_mask); gen_store_fpr32(ctx, fp0, rt); - tcg_temp_free_i32(fp0); break; #if defined(TARGET_MIPS64) case OPC_GSLDXC1: @@ -4683,21 +4410,18 @@ static void gen_loongson_lsdc2(DisasContext *ctx, int rt, t1 = tcg_temp_new(); gen_load_gpr(t1, rt); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_SB); - tcg_temp_free(t1); break; case OPC_GSSHX: t1 = tcg_temp_new(); gen_load_gpr(t1, rt); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUW | ctx->default_tcg_memop_mask); - tcg_temp_free(t1); break; case OPC_GSSWX: t1 = tcg_temp_new(); gen_load_gpr(t1, rt); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL | ctx->default_tcg_memop_mask); - tcg_temp_free(t1); break; #if defined(TARGET_MIPS64) case OPC_GSSDX: @@ -4705,7 +4429,6 @@ static void gen_loongson_lsdc2(DisasContext *ctx, int rt, gen_load_gpr(t1, rt); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUQ | ctx->default_tcg_memop_mask); - tcg_temp_free(t1); break; #endif case OPC_GSSWXC1: @@ -4713,7 +4436,6 @@ static void gen_loongson_lsdc2(DisasContext *ctx, int rt, gen_load_fpr32(ctx, fp0, rt); tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL | ctx->default_tcg_memop_mask); - tcg_temp_free_i32(fp0); break; #if defined(TARGET_MIPS64) case OPC_GSSDXC1: @@ -4721,14 +4443,11 @@ static void gen_loongson_lsdc2(DisasContext *ctx, int rt, gen_load_fpr64(ctx, t1, rt); tcg_gen_qemu_st_i64(t1, t0, ctx->mem_idx, MO_TEUQ | ctx->default_tcg_memop_mask); - tcg_temp_free(t1); break; #endif default: break; } - - tcg_temp_free(t0); } /* Traps */ @@ -4780,7 +4499,7 @@ static void gen_trap(DisasContext *ctx, uint32_t opc, /* Always trap */ #ifdef CONFIG_USER_ONLY /* Pass the break code along to cpu_loop. */ - tcg_gen_st_i32(tcg_constant_i32(code), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(code), tcg_env, offsetof(CPUMIPSState, error_code)); #endif generate_exception_end(ctx, EXCP_TRAP); @@ -4825,7 +4544,7 @@ static void gen_trap(DisasContext *ctx, uint32_t opc, } #ifdef CONFIG_USER_ONLY /* Pass the break code along to cpu_loop. */ - tcg_gen_st_i32(tcg_constant_i32(code), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(code), tcg_env, offsetof(CPUMIPSState, error_code)); #endif /* Like save_cpu_state, only don't update saved values. */ @@ -4838,8 +4557,6 @@ static void gen_trap(DisasContext *ctx, uint32_t opc, generate_exception(ctx, EXCP_TRAP); gen_set_label(l1); } - tcg_temp_free(t0); - tcg_temp_free(t1); } static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) @@ -4868,8 +4585,8 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, if (ctx->hflags & MIPS_HFLAG_BMASK) { #ifdef MIPS_DEBUG_DISAS - LOG_DISAS("Branch in delay / forbidden slot at PC 0x" - TARGET_FMT_lx "\n", ctx->base.pc_next); + LOG_DISAS("Branch in delay / forbidden slot at PC 0x%016" + VADDR_PRIx "\n", ctx->base.pc_next); #endif gen_reserved_instruction(ctx); goto out; @@ -4920,6 +4637,14 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, break; case OPC_J: case OPC_JAL: + { + /* Jump to immediate */ + int jal_mask = ctx->hflags & MIPS_HFLAG_M16 ? 0xF8000000 + : 0xF0000000; + btgt = ((ctx->base.pc_next + insn_bytes) & jal_mask) + | (uint32_t)offset; + break; + } case OPC_JALX: /* Jump to immediate */ btgt = ((ctx->base.pc_next + insn_bytes) & (int32_t)0xF0000000) | @@ -5105,8 +4830,6 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, if (insn_bytes == 2) { ctx->hflags |= MIPS_HFLAG_B16; } - tcg_temp_free(t0); - tcg_temp_free(t1); } @@ -5175,13 +4898,9 @@ static void gen_bitops(DisasContext *ctx, uint32_t opc, int rt, fail: MIPS_INVAL("bitops"); gen_reserved_instruction(ctx); - tcg_temp_free(t0); - tcg_temp_free(t1); return; } gen_store_gpr(t0, rt); - tcg_temp_free(t0); - tcg_temp_free(t1); } static void gen_bshfl(DisasContext *ctx, uint32_t op2, int rt, int rd) @@ -5199,15 +4918,13 @@ static void gen_bshfl(DisasContext *ctx, uint32_t op2, int rt, int rd) case OPC_WSBH: { TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_const_tl(0x00FF00FF); + TCGv t2 = tcg_constant_tl(0x00FF00FF); tcg_gen_shri_tl(t1, t0, 8); tcg_gen_and_tl(t1, t1, t2); tcg_gen_and_tl(t0, t0, t2); tcg_gen_shli_tl(t0, t0, 8); tcg_gen_or_tl(t0, t0, t1); - tcg_temp_free(t2); - tcg_temp_free(t1); tcg_gen_ext32s_tl(cpu_gpr[rd], t0); } break; @@ -5221,21 +4938,19 @@ static void gen_bshfl(DisasContext *ctx, uint32_t op2, int rt, int rd) case OPC_DSBH: { TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_const_tl(0x00FF00FF00FF00FFULL); + TCGv t2 = tcg_constant_tl(0x00FF00FF00FF00FFULL); tcg_gen_shri_tl(t1, t0, 8); tcg_gen_and_tl(t1, t1, t2); tcg_gen_and_tl(t0, t0, t2); tcg_gen_shli_tl(t0, t0, 8); tcg_gen_or_tl(cpu_gpr[rd], t0, t1); - tcg_temp_free(t2); - tcg_temp_free(t1); } break; case OPC_DSHD: { TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_const_tl(0x0000FFFF0000FFFFULL); + TCGv t2 = tcg_constant_tl(0x0000FFFF0000FFFFULL); tcg_gen_shri_tl(t1, t0, 16); tcg_gen_and_tl(t1, t1, t2); @@ -5245,18 +4960,14 @@ static void gen_bshfl(DisasContext *ctx, uint32_t op2, int rt, int rd) tcg_gen_shri_tl(t1, t0, 32); tcg_gen_shli_tl(t0, t0, 32); tcg_gen_or_tl(cpu_gpr[rd], t0, t1); - tcg_temp_free(t2); - tcg_temp_free(t1); } break; #endif default: MIPS_INVAL("bsfhl"); gen_reserved_instruction(ctx); - tcg_temp_free(t0); return; } - tcg_temp_free(t0); } static void gen_align_bits(DisasContext *ctx, int wordsz, int rd, int rs, @@ -5295,7 +5006,6 @@ static void gen_align_bits(DisasContext *ctx, int wordsz, int rd, int rs, tcg_gen_concat_tl_i64(t2, t1, t0); tcg_gen_shri_i64(t2, t2, 32 - bits); gen_move_low32(cpu_gpr[rd], t2); - tcg_temp_free_i64(t2); } break; #if defined(TARGET_MIPS64) @@ -5306,10 +5016,7 @@ static void gen_align_bits(DisasContext *ctx, int wordsz, int rd, int rs, break; #endif } - tcg_temp_free(t1); } - - tcg_temp_free(t0); } void gen_align(DisasContext *ctx, int wordsz, int rd, int rs, int rt, int bp) @@ -5336,7 +5043,6 @@ static void gen_bitswap(DisasContext *ctx, int opc, int rd, int rt) break; #endif } - tcg_temp_free(t0); } #ifndef CONFIG_USER_ONLY @@ -5347,15 +5053,13 @@ static inline void gen_mthc0_entrylo(TCGv arg, target_ulong off) TCGv_i64 t1 = tcg_temp_new_i64(); tcg_gen_ext_tl_i64(t0, arg); - tcg_gen_ld_i64(t1, cpu_env, off); + tcg_gen_ld_i64(t1, tcg_env, off); #if defined(TARGET_MIPS64) tcg_gen_deposit_i64(t1, t1, t0, 30, 32); #else tcg_gen_concat32_i64(t1, t1, t0); #endif - tcg_gen_st_i64(t1, cpu_env, off); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t0); + tcg_gen_st_i64(t1, tcg_env, off); } static inline void gen_mthc0_store64(TCGv arg, target_ulong off) @@ -5364,49 +5068,44 @@ static inline void gen_mthc0_store64(TCGv arg, target_ulong off) TCGv_i64 t1 = tcg_temp_new_i64(); tcg_gen_ext_tl_i64(t0, arg); - tcg_gen_ld_i64(t1, cpu_env, off); + tcg_gen_ld_i64(t1, tcg_env, off); tcg_gen_concat32_i64(t1, t1, t0); - tcg_gen_st_i64(t1, cpu_env, off); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t0); + tcg_gen_st_i64(t1, tcg_env, off); } static inline void gen_mfhc0_entrylo(TCGv arg, target_ulong off) { TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_ld_i64(t0, cpu_env, off); + tcg_gen_ld_i64(t0, tcg_env, off); #if defined(TARGET_MIPS64) tcg_gen_shri_i64(t0, t0, 30); #else tcg_gen_shri_i64(t0, t0, 32); #endif gen_move_low32(arg, t0); - tcg_temp_free_i64(t0); } static inline void gen_mfhc0_load64(TCGv arg, target_ulong off, int shift) { TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_ld_i64(t0, cpu_env, off); + tcg_gen_ld_i64(t0, tcg_env, off); tcg_gen_shri_i64(t0, t0, 32 + shift); gen_move_low32(arg, t0); - tcg_temp_free_i64(t0); } static inline void gen_mfc0_load32(TCGv arg, target_ulong off) { TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_ld_i32(t0, cpu_env, off); + tcg_gen_ld_i32(t0, tcg_env, off); tcg_gen_ext_i32_tl(arg, t0); - tcg_temp_free_i32(t0); } static inline void gen_mfc0_load64(TCGv arg, target_ulong off) { - tcg_gen_ld_tl(arg, cpu_env, off); + tcg_gen_ld_tl(arg, tcg_env, off); tcg_gen_ext32s_tl(arg, arg); } @@ -5415,8 +5114,7 @@ static inline void gen_mtc0_store32(TCGv arg, target_ulong off) TCGv_i32 t0 = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(t0, arg); - tcg_gen_st_i32(t0, cpu_env, off); - tcg_temp_free_i32(t0); + tcg_gen_st_i32(t0, tcg_env, off); } #define CP0_CHECK(c) \ @@ -5453,17 +5151,6 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel) goto cp0_unimplemented; } break; - case CP0_REGISTER_09: - switch (sel) { - case CP0_REG09__SAAR: - CP0_CHECK(ctx->saar); - gen_helper_mfhc0_saar(arg, cpu_env); - register_name = "SAAR"; - break; - default: - goto cp0_unimplemented; - } - break; case CP0_REGISTER_17: switch (sel) { case CP0_REG17__LLADDR: @@ -5473,7 +5160,7 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG17__MAAR: CP0_CHECK(ctx->mrp); - gen_helper_mfhc0_maar(arg, cpu_env); + gen_helper_mfhc0_maar(arg, tcg_env); register_name = "MAAR"; break; default: @@ -5554,17 +5241,6 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) goto cp0_unimplemented; } break; - case CP0_REGISTER_09: - switch (sel) { - case CP0_REG09__SAAR: - CP0_CHECK(ctx->saar); - gen_helper_mthc0_saar(cpu_env, arg); - register_name = "SAAR"; - break; - default: - goto cp0_unimplemented; - } - break; case CP0_REGISTER_17: switch (sel) { case CP0_REG17__LLADDR: @@ -5578,7 +5254,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG17__MAAR: CP0_CHECK(ctx->mrp); - gen_helper_mthc0_maar(cpu_env, arg); + gen_helper_mthc0_maar(tcg_env, arg); register_name = "MAAR"; break; default: @@ -5655,17 +5331,17 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG00__MVPCONTROL: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_mvpcontrol(arg, cpu_env); + gen_helper_mfc0_mvpcontrol(arg, tcg_env); register_name = "MVPControl"; break; case CP0_REG00__MVPCONF0: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_mvpconf0(arg, cpu_env); + gen_helper_mfc0_mvpconf0(arg, tcg_env); register_name = "MVPConf0"; break; case CP0_REG00__MVPCONF1: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_mvpconf1(arg, cpu_env); + gen_helper_mfc0_mvpconf1(arg, tcg_env); register_name = "MVPConf1"; break; case CP0_REG00__VPCONTROL: @@ -5681,7 +5357,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG01__RANDOM: CP0_CHECK(!(ctx->insn_flags & ISA_MIPS_R6)); - gen_helper_mfc0_random(arg, cpu_env); + gen_helper_mfc0_random(arg, tcg_env); register_name = "Random"; break; case CP0_REG01__VPECONTROL: @@ -5728,7 +5404,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG02__ENTRYLO0: { TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_ld_i64(tmp, cpu_env, + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUMIPSState, CP0_EntryLo0)); #if defined(TARGET_MIPS64) if (ctx->rxi) { @@ -5738,43 +5414,42 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) } #endif gen_move_low32(arg, tmp); - tcg_temp_free_i64(tmp); } register_name = "EntryLo0"; break; case CP0_REG02__TCSTATUS: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_tcstatus(arg, cpu_env); + gen_helper_mfc0_tcstatus(arg, tcg_env); register_name = "TCStatus"; break; case CP0_REG02__TCBIND: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_tcbind(arg, cpu_env); + gen_helper_mfc0_tcbind(arg, tcg_env); register_name = "TCBind"; break; case CP0_REG02__TCRESTART: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_tcrestart(arg, cpu_env); + gen_helper_mfc0_tcrestart(arg, tcg_env); register_name = "TCRestart"; break; case CP0_REG02__TCHALT: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_tchalt(arg, cpu_env); + gen_helper_mfc0_tchalt(arg, tcg_env); register_name = "TCHalt"; break; case CP0_REG02__TCCONTEXT: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_tccontext(arg, cpu_env); + gen_helper_mfc0_tccontext(arg, tcg_env); register_name = "TCContext"; break; case CP0_REG02__TCSCHEDULE: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_tcschedule(arg, cpu_env); + gen_helper_mfc0_tcschedule(arg, tcg_env); register_name = "TCSchedule"; break; case CP0_REG02__TCSCHEFBACK: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_tcschefback(arg, cpu_env); + gen_helper_mfc0_tcschefback(arg, tcg_env); register_name = "TCScheFBack"; break; default: @@ -5786,7 +5461,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG03__ENTRYLO1: { TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_ld_i64(tmp, cpu_env, + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUMIPSState, CP0_EntryLo1)); #if defined(TARGET_MIPS64) if (ctx->rxi) { @@ -5796,7 +5471,6 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) } #endif gen_move_low32(arg, tmp); - tcg_temp_free_i64(tmp); } register_name = "EntryLo1"; break; @@ -5812,7 +5486,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_04: switch (sel) { case CP0_REG04__CONTEXT: - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_Context)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_Context)); tcg_gen_ext32s_tl(arg, arg); register_name = "Context"; break; @@ -5823,14 +5497,14 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) goto cp0_unimplemented; case CP0_REG04__USERLOCAL: CP0_CHECK(ctx->ulri); - tcg_gen_ld_tl(arg, cpu_env, + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); 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); + gen_helper_mtc0_memorymapid(tcg_env, arg); register_name = "MMID"; break; default: @@ -5850,19 +5524,19 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG05__SEGCTL0: CP0_CHECK(ctx->sc); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl0)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_SegCtl0)); tcg_gen_ext32s_tl(arg, arg); register_name = "SegCtl0"; break; case CP0_REG05__SEGCTL1: CP0_CHECK(ctx->sc); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl1)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_SegCtl1)); tcg_gen_ext32s_tl(arg, arg); register_name = "SegCtl1"; break; case CP0_REG05__SEGCTL2: CP0_CHECK(ctx->sc); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl2)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_SegCtl2)); tcg_gen_ext32s_tl(arg, arg); register_name = "SegCtl2"; break; @@ -5939,7 +5613,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_08: switch (sel) { case CP0_REG08__BADVADDR: - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_BadVAddr)); tcg_gen_ext32s_tl(arg, arg); register_name = "BadVAddr"; break; @@ -5967,10 +5641,9 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG09__COUNT: /* Mark as an IO operation because we read the time. */ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_mfc0_count(arg, cpu_env); + translator_io_start(&ctx->base); + + gen_helper_mfc0_count(arg, tcg_env); /* * Break the TB to be able to take timer interrupts immediately * after reading count. DISAS_STOP isn't sufficient, we need to @@ -5980,16 +5653,6 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) ctx->base.is_jmp = DISAS_EXIT; register_name = "Count"; break; - case CP0_REG09__SAARI: - CP0_CHECK(ctx->saar); - gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SAARI)); - register_name = "SAARI"; - break; - case CP0_REG09__SAAR: - CP0_CHECK(ctx->saar); - gen_helper_mfc0_saar(arg, cpu_env); - register_name = "SAAR"; - break; default: goto cp0_unimplemented; } @@ -5997,7 +5660,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_10: switch (sel) { case CP0_REG10__ENTRYHI: - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryHi)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_EntryHi)); tcg_gen_ext32s_tl(arg, arg); register_name = "EntryHi"; break; @@ -6054,7 +5717,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_14: switch (sel) { case CP0_REG14__EPC: - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_EPC)); tcg_gen_ext32s_tl(arg, arg); register_name = "EPC"; break; @@ -6070,14 +5733,14 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG15__EBASE: check_insn(ctx, ISA_MIPS_R2); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EBase)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_EBase)); tcg_gen_ext32s_tl(arg, arg); register_name = "EBase"; break; case CP0_REG15__CMGCRBASE: check_insn(ctx, ISA_MIPS_R2); CP0_CHECK(ctx->cmgcr); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_CMGCRBase)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_CMGCRBase)); tcg_gen_ext32s_tl(arg, arg); register_name = "CMGCRBase"; break; @@ -6127,12 +5790,12 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_17: switch (sel) { case CP0_REG17__LLADDR: - gen_helper_mfc0_lladdr(arg, cpu_env); + gen_helper_mfc0_lladdr(arg, tcg_env); register_name = "LLAddr"; break; case CP0_REG17__MAAR: CP0_CHECK(ctx->mrp); - gen_helper_mfc0_maar(arg, cpu_env); + gen_helper_mfc0_maar(arg, tcg_env); register_name = "MAAR"; break; case CP0_REG17__MAARI: @@ -6185,7 +5848,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG20__XCONTEXT: #if defined(TARGET_MIPS64) check_insn(ctx, ISA_MIPS3); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_XContext)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_XContext)); tcg_gen_ext32s_tl(arg, arg); register_name = "XContext"; break; @@ -6213,7 +5876,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_23: switch (sel) { case CP0_REG23__DEBUG: - gen_helper_mfc0_debug(arg, cpu_env); /* EJTAG support */ + gen_helper_mfc0_debug(arg, tcg_env); /* EJTAG support */ register_name = "Debug"; break; case CP0_REG23__TRACECONTROL: @@ -6249,7 +5912,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG24__DEPC: /* EJTAG support */ - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_DEPC)); tcg_gen_ext32s_tl(arg, arg); register_name = "DEPC"; break; @@ -6323,9 +5986,8 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG28__TAGLO3: { TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_ld_i64(tmp, cpu_env, offsetof(CPUMIPSState, CP0_TagLo)); + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPUMIPSState, CP0_TagLo)); gen_move_low32(arg, tmp); - tcg_temp_free_i64(tmp); } register_name = "TagLo"; break; @@ -6363,7 +6025,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_30: switch (sel) { case CP0_REG30__ERROREPC: - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); tcg_gen_ext32s_tl(arg, arg); register_name = "ErrorEPC"; break; @@ -6385,7 +6047,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG31__KSCRATCH5: case CP0_REG31__KSCRATCH6: CP0_CHECK(ctx->kscrexist & (1 << sel)); - tcg_gen_ld_tl(arg, cpu_env, + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_KScratch[sel - 2])); tcg_gen_ext32s_tl(arg, arg); register_name = "KScratch"; @@ -6409,25 +6071,24 @@ cp0_unimplemented: static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *register_name = "invalid"; + bool icount; if (sel != 0) { check_insn(ctx, ISA_MIPS_R1); } - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + icount = translator_io_start(&ctx->base); switch (reg) { case CP0_REGISTER_00: switch (sel) { case CP0_REG00__INDEX: - gen_helper_mtc0_index(cpu_env, arg); + gen_helper_mtc0_index(tcg_env, arg); register_name = "Index"; break; case CP0_REG00__MVPCONTROL: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_mvpcontrol(cpu_env, arg); + gen_helper_mtc0_mvpcontrol(tcg_env, arg); register_name = "MVPControl"; break; case CP0_REG00__MVPCONF0: @@ -6457,39 +6118,39 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG01__VPECONTROL: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_vpecontrol(cpu_env, arg); + gen_helper_mtc0_vpecontrol(tcg_env, arg); register_name = "VPEControl"; break; case CP0_REG01__VPECONF0: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_vpeconf0(cpu_env, arg); + gen_helper_mtc0_vpeconf0(tcg_env, arg); register_name = "VPEConf0"; break; case CP0_REG01__VPECONF1: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_vpeconf1(cpu_env, arg); + gen_helper_mtc0_vpeconf1(tcg_env, arg); register_name = "VPEConf1"; break; case CP0_REG01__YQMASK: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_yqmask(cpu_env, arg); + gen_helper_mtc0_yqmask(tcg_env, arg); register_name = "YQMask"; break; case CP0_REG01__VPESCHEDULE: CP0_CHECK(ctx->insn_flags & ASE_MT); - tcg_gen_st_tl(arg, cpu_env, + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_VPESchedule)); register_name = "VPESchedule"; break; case CP0_REG01__VPESCHEFBACK: CP0_CHECK(ctx->insn_flags & ASE_MT); - tcg_gen_st_tl(arg, cpu_env, + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); register_name = "VPEScheFBack"; break; case CP0_REG01__VPEOPT: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_vpeopt(cpu_env, arg); + gen_helper_mtc0_vpeopt(tcg_env, arg); register_name = "VPEOpt"; break; default: @@ -6499,42 +6160,42 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_02: switch (sel) { case CP0_REG02__ENTRYLO0: - gen_helper_mtc0_entrylo0(cpu_env, arg); + gen_helper_mtc0_entrylo0(tcg_env, arg); register_name = "EntryLo0"; break; case CP0_REG02__TCSTATUS: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_tcstatus(cpu_env, arg); + gen_helper_mtc0_tcstatus(tcg_env, arg); register_name = "TCStatus"; break; case CP0_REG02__TCBIND: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_tcbind(cpu_env, arg); + gen_helper_mtc0_tcbind(tcg_env, arg); register_name = "TCBind"; break; case CP0_REG02__TCRESTART: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_tcrestart(cpu_env, arg); + gen_helper_mtc0_tcrestart(tcg_env, arg); register_name = "TCRestart"; break; case CP0_REG02__TCHALT: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_tchalt(cpu_env, arg); + gen_helper_mtc0_tchalt(tcg_env, arg); register_name = "TCHalt"; break; case CP0_REG02__TCCONTEXT: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_tccontext(cpu_env, arg); + gen_helper_mtc0_tccontext(tcg_env, arg); register_name = "TCContext"; break; case CP0_REG02__TCSCHEDULE: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_tcschedule(cpu_env, arg); + gen_helper_mtc0_tcschedule(tcg_env, arg); register_name = "TCSchedule"; break; case CP0_REG02__TCSCHEFBACK: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_tcschefback(cpu_env, arg); + gen_helper_mtc0_tcschefback(tcg_env, arg); register_name = "TCScheFBack"; break; default: @@ -6544,7 +6205,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_03: switch (sel) { case CP0_REG03__ENTRYLO1: - gen_helper_mtc0_entrylo1(cpu_env, arg); + gen_helper_mtc0_entrylo1(tcg_env, arg); register_name = "EntryLo1"; break; case CP0_REG03__GLOBALNUM: @@ -6559,7 +6220,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_04: switch (sel) { case CP0_REG04__CONTEXT: - gen_helper_mtc0_context(cpu_env, arg); + gen_helper_mtc0_context(tcg_env, arg); register_name = "Context"; break; case CP0_REG04__CONTEXTCONFIG: @@ -6569,7 +6230,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) goto cp0_unimplemented; case CP0_REG04__USERLOCAL: CP0_CHECK(ctx->ulri); - tcg_gen_st_tl(arg, cpu_env, + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); register_name = "UserLocal"; break; @@ -6585,28 +6246,28 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_05: switch (sel) { case CP0_REG05__PAGEMASK: - gen_helper_mtc0_pagemask(cpu_env, arg); + gen_helper_mtc0_pagemask(tcg_env, arg); register_name = "PageMask"; break; case CP0_REG05__PAGEGRAIN: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_pagegrain(cpu_env, arg); + gen_helper_mtc0_pagegrain(tcg_env, arg); register_name = "PageGrain"; ctx->base.is_jmp = DISAS_STOP; break; case CP0_REG05__SEGCTL0: CP0_CHECK(ctx->sc); - gen_helper_mtc0_segctl0(cpu_env, arg); + gen_helper_mtc0_segctl0(tcg_env, arg); register_name = "SegCtl0"; break; case CP0_REG05__SEGCTL1: CP0_CHECK(ctx->sc); - gen_helper_mtc0_segctl1(cpu_env, arg); + gen_helper_mtc0_segctl1(tcg_env, arg); register_name = "SegCtl1"; break; case CP0_REG05__SEGCTL2: CP0_CHECK(ctx->sc); - gen_helper_mtc0_segctl2(cpu_env, arg); + gen_helper_mtc0_segctl2(tcg_env, arg); register_name = "SegCtl2"; break; case CP0_REG05__PWBASE: @@ -6616,12 +6277,12 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG05__PWFIELD: check_pw(ctx); - gen_helper_mtc0_pwfield(cpu_env, arg); + gen_helper_mtc0_pwfield(tcg_env, arg); register_name = "PWField"; break; case CP0_REG05__PWSIZE: check_pw(ctx); - gen_helper_mtc0_pwsize(cpu_env, arg); + gen_helper_mtc0_pwsize(tcg_env, arg); register_name = "PWSize"; break; default: @@ -6631,37 +6292,37 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_06: switch (sel) { case CP0_REG06__WIRED: - gen_helper_mtc0_wired(cpu_env, arg); + gen_helper_mtc0_wired(tcg_env, arg); register_name = "Wired"; break; case CP0_REG06__SRSCONF0: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_srsconf0(cpu_env, arg); + gen_helper_mtc0_srsconf0(tcg_env, arg); register_name = "SRSConf0"; break; case CP0_REG06__SRSCONF1: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_srsconf1(cpu_env, arg); + gen_helper_mtc0_srsconf1(tcg_env, arg); register_name = "SRSConf1"; break; case CP0_REG06__SRSCONF2: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_srsconf2(cpu_env, arg); + gen_helper_mtc0_srsconf2(tcg_env, arg); register_name = "SRSConf2"; break; case CP0_REG06__SRSCONF3: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_srsconf3(cpu_env, arg); + gen_helper_mtc0_srsconf3(tcg_env, arg); register_name = "SRSConf3"; break; case CP0_REG06__SRSCONF4: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_srsconf4(cpu_env, arg); + gen_helper_mtc0_srsconf4(tcg_env, arg); register_name = "SRSConf4"; break; case CP0_REG06__PWCTL: check_pw(ctx); - gen_helper_mtc0_pwctl(cpu_env, arg); + gen_helper_mtc0_pwctl(tcg_env, arg); register_name = "PWCtl"; break; default: @@ -6672,7 +6333,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG07__HWRENA: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_hwrena(cpu_env, arg); + gen_helper_mtc0_hwrena(tcg_env, arg); ctx->base.is_jmp = DISAS_STOP; register_name = "HWREna"; break; @@ -6705,19 +6366,9 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_09: switch (sel) { case CP0_REG09__COUNT: - gen_helper_mtc0_count(cpu_env, arg); + gen_helper_mtc0_count(tcg_env, arg); register_name = "Count"; break; - case CP0_REG09__SAARI: - CP0_CHECK(ctx->saar); - gen_helper_mtc0_saari(cpu_env, arg); - register_name = "SAARI"; - break; - case CP0_REG09__SAAR: - CP0_CHECK(ctx->saar); - gen_helper_mtc0_saar(cpu_env, arg); - register_name = "SAAR"; - break; default: goto cp0_unimplemented; } @@ -6725,7 +6376,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_10: switch (sel) { case CP0_REG10__ENTRYHI: - gen_helper_mtc0_entryhi(cpu_env, arg); + gen_helper_mtc0_entryhi(tcg_env, arg); register_name = "EntryHi"; break; default: @@ -6735,7 +6386,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_11: switch (sel) { case CP0_REG11__COMPARE: - gen_helper_mtc0_compare(cpu_env, arg); + gen_helper_mtc0_compare(tcg_env, arg); register_name = "Compare"; break; /* 6,7 are implementation dependent */ @@ -6747,7 +6398,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG12__STATUS: save_cpu_state(ctx, 1); - gen_helper_mtc0_status(cpu_env, arg); + gen_helper_mtc0_status(tcg_env, arg); /* DISAS_STOP isn't good enough here, hflags may have changed. */ gen_save_pc(ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; @@ -6755,14 +6406,14 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG12__INTCTL: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_intctl(cpu_env, arg); + gen_helper_mtc0_intctl(tcg_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; register_name = "IntCtl"; break; case CP0_REG12__SRSCTL: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_srsctl(cpu_env, arg); + gen_helper_mtc0_srsctl(tcg_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; register_name = "SRSCtl"; @@ -6782,7 +6433,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG13__CAUSE: save_cpu_state(ctx, 1); - gen_helper_mtc0_cause(cpu_env, arg); + gen_helper_mtc0_cause(tcg_env, arg); /* * Stop translation as we may have triggered an interrupt. * DISAS_STOP isn't sufficient, we need to ensure we break out of @@ -6799,7 +6450,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_14: switch (sel) { case CP0_REG14__EPC: - tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC)); + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_EPC)); register_name = "EPC"; break; default: @@ -6814,7 +6465,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG15__EBASE: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_ebase(cpu_env, arg); + gen_helper_mtc0_ebase(tcg_env, arg); register_name = "EBase"; break; default: @@ -6824,7 +6475,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_16: switch (sel) { case CP0_REG16__CONFIG: - gen_helper_mtc0_config0(cpu_env, arg); + gen_helper_mtc0_config0(tcg_env, arg); register_name = "Config"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; @@ -6834,24 +6485,24 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) register_name = "Config1"; break; case CP0_REG16__CONFIG2: - gen_helper_mtc0_config2(cpu_env, arg); + gen_helper_mtc0_config2(tcg_env, arg); register_name = "Config2"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; break; case CP0_REG16__CONFIG3: - gen_helper_mtc0_config3(cpu_env, arg); + gen_helper_mtc0_config3(tcg_env, arg); register_name = "Config3"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; break; case CP0_REG16__CONFIG4: - gen_helper_mtc0_config4(cpu_env, arg); + gen_helper_mtc0_config4(tcg_env, arg); register_name = "Config4"; ctx->base.is_jmp = DISAS_STOP; break; case CP0_REG16__CONFIG5: - gen_helper_mtc0_config5(cpu_env, arg); + gen_helper_mtc0_config5(tcg_env, arg); register_name = "Config5"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; @@ -6873,17 +6524,17 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_17: switch (sel) { case CP0_REG17__LLADDR: - gen_helper_mtc0_lladdr(cpu_env, arg); + gen_helper_mtc0_lladdr(tcg_env, arg); register_name = "LLAddr"; break; case CP0_REG17__MAAR: CP0_CHECK(ctx->mrp); - gen_helper_mtc0_maar(cpu_env, arg); + gen_helper_mtc0_maar(tcg_env, arg); register_name = "MAAR"; break; case CP0_REG17__MAARI: CP0_CHECK(ctx->mrp); - gen_helper_mtc0_maari(cpu_env, arg); + gen_helper_mtc0_maari(tcg_env, arg); register_name = "MAARI"; break; default: @@ -6931,7 +6582,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG20__XCONTEXT: #if defined(TARGET_MIPS64) check_insn(ctx, ISA_MIPS3); - gen_helper_mtc0_xcontext(cpu_env, arg); + gen_helper_mtc0_xcontext(tcg_env, arg); register_name = "XContext"; break; #endif @@ -6944,7 +6595,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) CP0_CHECK(!(ctx->insn_flags & ISA_MIPS_R6)); switch (sel) { case 0: - gen_helper_mtc0_framemask(cpu_env, arg); + gen_helper_mtc0_framemask(tcg_env, arg); register_name = "Framemask"; break; default: @@ -6958,7 +6609,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_23: switch (sel) { case CP0_REG23__DEBUG: - gen_helper_mtc0_debug(cpu_env, arg); /* EJTAG support */ + gen_helper_mtc0_debug(tcg_env, arg); /* EJTAG support */ /* DISAS_STOP isn't good enough here, hflags may have changed. */ gen_save_pc(ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; @@ -6966,14 +6617,14 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG23__TRACECONTROL: /* PDtrace support */ - /* gen_helper_mtc0_tracecontrol(cpu_env, arg); */ + /* gen_helper_mtc0_tracecontrol(tcg_env, arg); */ register_name = "TraceControl"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; goto cp0_unimplemented; case CP0_REG23__TRACECONTROL2: /* PDtrace support */ - /* gen_helper_mtc0_tracecontrol2(cpu_env, arg); */ + /* gen_helper_mtc0_tracecontrol2(tcg_env, arg); */ register_name = "TraceControl2"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; @@ -6982,21 +6633,21 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; /* PDtrace support */ - /* gen_helper_mtc0_usertracedata1(cpu_env, arg);*/ + /* gen_helper_mtc0_usertracedata1(tcg_env, arg);*/ register_name = "UserTraceData"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; goto cp0_unimplemented; case CP0_REG23__TRACEIBPC: /* PDtrace support */ - /* gen_helper_mtc0_traceibpc(cpu_env, arg); */ + /* gen_helper_mtc0_traceibpc(tcg_env, arg); */ /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; register_name = "TraceIBPC"; goto cp0_unimplemented; case CP0_REG23__TRACEDBPC: /* PDtrace support */ - /* gen_helper_mtc0_tracedbpc(cpu_env, arg); */ + /* gen_helper_mtc0_tracedbpc(tcg_env, arg); */ /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; register_name = "TraceDBPC"; @@ -7009,7 +6660,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG24__DEPC: /* EJTAG support */ - tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC)); + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_DEPC)); register_name = "DEPC"; break; default: @@ -7019,7 +6670,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_25: switch (sel) { case CP0_REG25__PERFCTL0: - gen_helper_mtc0_performance0(cpu_env, arg); + gen_helper_mtc0_performance0(tcg_env, arg); register_name = "Performance0"; break; case CP0_REG25__PERFCNT0: @@ -7057,7 +6708,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_26: switch (sel) { case CP0_REG26__ERRCTL: - gen_helper_mtc0_errctl(cpu_env, arg); + gen_helper_mtc0_errctl(tcg_env, arg); ctx->base.is_jmp = DISAS_STOP; register_name = "ErrCtl"; break; @@ -7081,14 +6732,14 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG28__TAGLO1: case CP0_REG28__TAGLO2: case CP0_REG28__TAGLO3: - gen_helper_mtc0_taglo(cpu_env, arg); + gen_helper_mtc0_taglo(tcg_env, arg); register_name = "TagLo"; break; case CP0_REG28__DATALO: case CP0_REG28__DATALO1: case CP0_REG28__DATALO2: case CP0_REG28__DATALO3: - gen_helper_mtc0_datalo(cpu_env, arg); + gen_helper_mtc0_datalo(tcg_env, arg); register_name = "DataLo"; break; default: @@ -7101,14 +6752,14 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG29__TAGHI1: case CP0_REG29__TAGHI2: case CP0_REG29__TAGHI3: - gen_helper_mtc0_taghi(cpu_env, arg); + gen_helper_mtc0_taghi(tcg_env, arg); register_name = "TagHi"; break; case CP0_REG29__DATAHI: case CP0_REG29__DATAHI1: case CP0_REG29__DATAHI2: case CP0_REG29__DATAHI3: - gen_helper_mtc0_datahi(cpu_env, arg); + gen_helper_mtc0_datahi(tcg_env, arg); register_name = "DataHi"; break; default: @@ -7119,7 +6770,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_30: switch (sel) { case CP0_REG30__ERROREPC: - tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); register_name = "ErrorEPC"; break; default: @@ -7140,7 +6791,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG31__KSCRATCH5: case CP0_REG31__KSCRATCH6: CP0_CHECK(ctx->kscrexist & (1 << sel)); - tcg_gen_st_tl(arg, cpu_env, + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_KScratch[sel - 2])); register_name = "KScratch"; break; @@ -7154,7 +6805,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) trace_mips_translate_c0("mtc0", register_name, reg, sel); /* For simplicity assume that all writes can cause interrupts. */ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + if (icount) { /* * DISAS_STOP isn't sufficient, we need to ensure we break out of * translated code to check for pending interrupts. @@ -7187,17 +6838,17 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG00__MVPCONTROL: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_mvpcontrol(arg, cpu_env); + gen_helper_mfc0_mvpcontrol(arg, tcg_env); register_name = "MVPControl"; break; case CP0_REG00__MVPCONF0: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_mvpconf0(arg, cpu_env); + gen_helper_mfc0_mvpconf0(arg, tcg_env); register_name = "MVPConf0"; break; case CP0_REG00__MVPCONF1: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_mvpconf1(arg, cpu_env); + gen_helper_mfc0_mvpconf1(arg, tcg_env); register_name = "MVPConf1"; break; case CP0_REG00__VPCONTROL: @@ -7213,7 +6864,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG01__RANDOM: CP0_CHECK(!(ctx->insn_flags & ISA_MIPS_R6)); - gen_helper_mfc0_random(arg, cpu_env); + gen_helper_mfc0_random(arg, tcg_env); register_name = "Random"; break; case CP0_REG01__VPECONTROL: @@ -7233,19 +6884,19 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG01__YQMASK: CP0_CHECK(ctx->insn_flags & ASE_MT); - tcg_gen_ld_tl(arg, cpu_env, + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_YQMask)); register_name = "YQMask"; break; case CP0_REG01__VPESCHEDULE: CP0_CHECK(ctx->insn_flags & ASE_MT); - tcg_gen_ld_tl(arg, cpu_env, + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_VPESchedule)); register_name = "VPESchedule"; break; case CP0_REG01__VPESCHEFBACK: CP0_CHECK(ctx->insn_flags & ASE_MT); - tcg_gen_ld_tl(arg, cpu_env, + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); register_name = "VPEScheFBack"; break; @@ -7261,43 +6912,43 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_02: switch (sel) { case CP0_REG02__ENTRYLO0: - tcg_gen_ld_tl(arg, cpu_env, + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_EntryLo0)); register_name = "EntryLo0"; break; case CP0_REG02__TCSTATUS: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_tcstatus(arg, cpu_env); + gen_helper_mfc0_tcstatus(arg, tcg_env); register_name = "TCStatus"; break; case CP0_REG02__TCBIND: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_tcbind(arg, cpu_env); + gen_helper_mfc0_tcbind(arg, tcg_env); register_name = "TCBind"; break; case CP0_REG02__TCRESTART: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_dmfc0_tcrestart(arg, cpu_env); + gen_helper_dmfc0_tcrestart(arg, tcg_env); register_name = "TCRestart"; break; case CP0_REG02__TCHALT: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_dmfc0_tchalt(arg, cpu_env); + gen_helper_dmfc0_tchalt(arg, tcg_env); register_name = "TCHalt"; break; case CP0_REG02__TCCONTEXT: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_dmfc0_tccontext(arg, cpu_env); + gen_helper_dmfc0_tccontext(arg, tcg_env); register_name = "TCContext"; break; case CP0_REG02__TCSCHEDULE: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_dmfc0_tcschedule(arg, cpu_env); + gen_helper_dmfc0_tcschedule(arg, tcg_env); register_name = "TCSchedule"; break; case CP0_REG02__TCSCHEFBACK: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_dmfc0_tcschefback(arg, cpu_env); + gen_helper_dmfc0_tcschefback(arg, tcg_env); register_name = "TCScheFBack"; break; default: @@ -7307,7 +6958,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_03: switch (sel) { case CP0_REG03__ENTRYLO1: - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo1)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_EntryLo1)); register_name = "EntryLo1"; break; case CP0_REG03__GLOBALNUM: @@ -7322,7 +6973,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_04: switch (sel) { case CP0_REG04__CONTEXT: - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_Context)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_Context)); register_name = "Context"; break; case CP0_REG04__CONTEXTCONFIG: @@ -7332,13 +6983,13 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) goto cp0_unimplemented; case CP0_REG04__USERLOCAL: CP0_CHECK(ctx->ulri); - tcg_gen_ld_tl(arg, cpu_env, + tcg_gen_ld_tl(arg, tcg_env, 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); + gen_helper_mtc0_memorymapid(tcg_env, arg); register_name = "MMID"; break; default: @@ -7358,32 +7009,32 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG05__SEGCTL0: CP0_CHECK(ctx->sc); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl0)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_SegCtl0)); register_name = "SegCtl0"; break; case CP0_REG05__SEGCTL1: CP0_CHECK(ctx->sc); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl1)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_SegCtl1)); register_name = "SegCtl1"; break; case CP0_REG05__SEGCTL2: CP0_CHECK(ctx->sc); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl2)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_SegCtl2)); register_name = "SegCtl2"; break; case CP0_REG05__PWBASE: check_pw(ctx); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_PWBase)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_PWBase)); register_name = "PWBase"; break; case CP0_REG05__PWFIELD: check_pw(ctx); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_PWField)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_PWField)); register_name = "PWField"; break; case CP0_REG05__PWSIZE: check_pw(ctx); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_PWSize)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_PWSize)); register_name = "PWSize"; break; default: @@ -7444,7 +7095,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_08: switch (sel) { case CP0_REG08__BADVADDR: - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_BadVAddr)); register_name = "BadVAddr"; break; case CP0_REG08__BADINSTR: @@ -7471,10 +7122,8 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG09__COUNT: /* Mark as an IO operation because we read the time. */ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_mfc0_count(arg, cpu_env); + translator_io_start(&ctx->base); + gen_helper_mfc0_count(arg, tcg_env); /* * Break the TB to be able to take timer interrupts immediately * after reading count. DISAS_STOP isn't sufficient, we need to @@ -7484,16 +7133,6 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) ctx->base.is_jmp = DISAS_EXIT; register_name = "Count"; break; - case CP0_REG09__SAARI: - CP0_CHECK(ctx->saar); - gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SAARI)); - register_name = "SAARI"; - break; - case CP0_REG09__SAAR: - CP0_CHECK(ctx->saar); - gen_helper_dmfc0_saar(arg, cpu_env); - register_name = "SAAR"; - break; default: goto cp0_unimplemented; } @@ -7501,7 +7140,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_10: switch (sel) { case CP0_REG10__ENTRYHI: - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryHi)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_EntryHi)); register_name = "EntryHi"; break; default: @@ -7557,7 +7196,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_14: switch (sel) { case CP0_REG14__EPC: - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_EPC)); register_name = "EPC"; break; default: @@ -7572,13 +7211,13 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG15__EBASE: check_insn(ctx, ISA_MIPS_R2); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EBase)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_EBase)); register_name = "EBase"; break; case CP0_REG15__CMGCRBASE: check_insn(ctx, ISA_MIPS_R2); CP0_CHECK(ctx->cmgcr); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_CMGCRBase)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_CMGCRBase)); register_name = "CMGCRBase"; break; default: @@ -7627,12 +7266,12 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_17: switch (sel) { case CP0_REG17__LLADDR: - gen_helper_dmfc0_lladdr(arg, cpu_env); + gen_helper_dmfc0_lladdr(arg, tcg_env); register_name = "LLAddr"; break; case CP0_REG17__MAAR: CP0_CHECK(ctx->mrp); - gen_helper_dmfc0_maar(arg, cpu_env); + gen_helper_dmfc0_maar(arg, tcg_env); register_name = "MAAR"; break; case CP0_REG17__MAARI: @@ -7684,7 +7323,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG20__XCONTEXT: check_insn(ctx, ISA_MIPS3); - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_XContext)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_XContext)); register_name = "XContext"; break; default: @@ -7710,32 +7349,32 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_23: switch (sel) { case CP0_REG23__DEBUG: - gen_helper_mfc0_debug(arg, cpu_env); /* EJTAG support */ + gen_helper_mfc0_debug(arg, tcg_env); /* EJTAG support */ register_name = "Debug"; break; case CP0_REG23__TRACECONTROL: /* PDtrace support */ - /* gen_helper_dmfc0_tracecontrol(arg, cpu_env); */ + /* gen_helper_dmfc0_tracecontrol(arg, tcg_env); */ register_name = "TraceControl"; goto cp0_unimplemented; case CP0_REG23__TRACECONTROL2: /* PDtrace support */ - /* gen_helper_dmfc0_tracecontrol2(arg, cpu_env); */ + /* gen_helper_dmfc0_tracecontrol2(arg, tcg_env); */ register_name = "TraceControl2"; goto cp0_unimplemented; case CP0_REG23__USERTRACEDATA1: /* PDtrace support */ - /* gen_helper_dmfc0_usertracedata1(arg, cpu_env);*/ + /* gen_helper_dmfc0_usertracedata1(arg, tcg_env);*/ register_name = "UserTraceData1"; goto cp0_unimplemented; case CP0_REG23__TRACEIBPC: /* PDtrace support */ - /* gen_helper_dmfc0_traceibpc(arg, cpu_env); */ + /* gen_helper_dmfc0_traceibpc(arg, tcg_env); */ register_name = "TraceIBPC"; goto cp0_unimplemented; case CP0_REG23__TRACEDBPC: /* PDtrace support */ - /* gen_helper_dmfc0_tracedbpc(arg, cpu_env); */ + /* gen_helper_dmfc0_tracedbpc(arg, tcg_env); */ register_name = "TraceDBPC"; goto cp0_unimplemented; default: @@ -7746,7 +7385,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG24__DEPC: /* EJTAG support */ - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_DEPC)); register_name = "DEPC"; break; default: @@ -7855,7 +7494,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_30: switch (sel) { case CP0_REG30__ERROREPC: - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); register_name = "ErrorEPC"; break; default: @@ -7876,7 +7515,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG31__KSCRATCH5: case CP0_REG31__KSCRATCH6: CP0_CHECK(ctx->kscrexist & (1 << sel)); - tcg_gen_ld_tl(arg, cpu_env, + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_KScratch[sel - 2])); register_name = "KScratch"; break; @@ -7899,25 +7538,24 @@ cp0_unimplemented: static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *register_name = "invalid"; + bool icount; if (sel != 0) { check_insn(ctx, ISA_MIPS_R1); } - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + icount = translator_io_start(&ctx->base); switch (reg) { case CP0_REGISTER_00: switch (sel) { case CP0_REG00__INDEX: - gen_helper_mtc0_index(cpu_env, arg); + gen_helper_mtc0_index(tcg_env, arg); register_name = "Index"; break; case CP0_REG00__MVPCONTROL: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_mvpcontrol(cpu_env, arg); + gen_helper_mtc0_mvpcontrol(tcg_env, arg); register_name = "MVPControl"; break; case CP0_REG00__MVPCONF0: @@ -7947,39 +7585,39 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG01__VPECONTROL: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_vpecontrol(cpu_env, arg); + gen_helper_mtc0_vpecontrol(tcg_env, arg); register_name = "VPEControl"; break; case CP0_REG01__VPECONF0: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_vpeconf0(cpu_env, arg); + gen_helper_mtc0_vpeconf0(tcg_env, arg); register_name = "VPEConf0"; break; case CP0_REG01__VPECONF1: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_vpeconf1(cpu_env, arg); + gen_helper_mtc0_vpeconf1(tcg_env, arg); register_name = "VPEConf1"; break; case CP0_REG01__YQMASK: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_yqmask(cpu_env, arg); + gen_helper_mtc0_yqmask(tcg_env, arg); register_name = "YQMask"; break; case CP0_REG01__VPESCHEDULE: CP0_CHECK(ctx->insn_flags & ASE_MT); - tcg_gen_st_tl(arg, cpu_env, + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_VPESchedule)); register_name = "VPESchedule"; break; case CP0_REG01__VPESCHEFBACK: CP0_CHECK(ctx->insn_flags & ASE_MT); - tcg_gen_st_tl(arg, cpu_env, + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); register_name = "VPEScheFBack"; break; case CP0_REG01__VPEOPT: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_vpeopt(cpu_env, arg); + gen_helper_mtc0_vpeopt(tcg_env, arg); register_name = "VPEOpt"; break; default: @@ -7989,42 +7627,42 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_02: switch (sel) { case CP0_REG02__ENTRYLO0: - gen_helper_dmtc0_entrylo0(cpu_env, arg); + gen_helper_dmtc0_entrylo0(tcg_env, arg); register_name = "EntryLo0"; break; case CP0_REG02__TCSTATUS: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_tcstatus(cpu_env, arg); + gen_helper_mtc0_tcstatus(tcg_env, arg); register_name = "TCStatus"; break; case CP0_REG02__TCBIND: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_tcbind(cpu_env, arg); + gen_helper_mtc0_tcbind(tcg_env, arg); register_name = "TCBind"; break; case CP0_REG02__TCRESTART: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_tcrestart(cpu_env, arg); + gen_helper_mtc0_tcrestart(tcg_env, arg); register_name = "TCRestart"; break; case CP0_REG02__TCHALT: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_tchalt(cpu_env, arg); + gen_helper_mtc0_tchalt(tcg_env, arg); register_name = "TCHalt"; break; case CP0_REG02__TCCONTEXT: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_tccontext(cpu_env, arg); + gen_helper_mtc0_tccontext(tcg_env, arg); register_name = "TCContext"; break; case CP0_REG02__TCSCHEDULE: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_tcschedule(cpu_env, arg); + gen_helper_mtc0_tcschedule(tcg_env, arg); register_name = "TCSchedule"; break; case CP0_REG02__TCSCHEFBACK: CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_tcschefback(cpu_env, arg); + gen_helper_mtc0_tcschefback(tcg_env, arg); register_name = "TCScheFBack"; break; default: @@ -8034,7 +7672,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_03: switch (sel) { case CP0_REG03__ENTRYLO1: - gen_helper_dmtc0_entrylo1(cpu_env, arg); + gen_helper_dmtc0_entrylo1(tcg_env, arg); register_name = "EntryLo1"; break; case CP0_REG03__GLOBALNUM: @@ -8049,7 +7687,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_04: switch (sel) { case CP0_REG04__CONTEXT: - gen_helper_mtc0_context(cpu_env, arg); + gen_helper_mtc0_context(tcg_env, arg); register_name = "Context"; break; case CP0_REG04__CONTEXTCONFIG: @@ -8059,7 +7697,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) goto cp0_unimplemented; case CP0_REG04__USERLOCAL: CP0_CHECK(ctx->ulri); - tcg_gen_st_tl(arg, cpu_env, + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); register_name = "UserLocal"; break; @@ -8075,42 +7713,42 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_05: switch (sel) { case CP0_REG05__PAGEMASK: - gen_helper_mtc0_pagemask(cpu_env, arg); + gen_helper_mtc0_pagemask(tcg_env, arg); register_name = "PageMask"; break; case CP0_REG05__PAGEGRAIN: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_pagegrain(cpu_env, arg); + gen_helper_mtc0_pagegrain(tcg_env, arg); register_name = "PageGrain"; break; case CP0_REG05__SEGCTL0: CP0_CHECK(ctx->sc); - gen_helper_mtc0_segctl0(cpu_env, arg); + gen_helper_mtc0_segctl0(tcg_env, arg); register_name = "SegCtl0"; break; case CP0_REG05__SEGCTL1: CP0_CHECK(ctx->sc); - gen_helper_mtc0_segctl1(cpu_env, arg); + gen_helper_mtc0_segctl1(tcg_env, arg); register_name = "SegCtl1"; break; case CP0_REG05__SEGCTL2: CP0_CHECK(ctx->sc); - gen_helper_mtc0_segctl2(cpu_env, arg); + gen_helper_mtc0_segctl2(tcg_env, arg); register_name = "SegCtl2"; break; case CP0_REG05__PWBASE: check_pw(ctx); - tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_PWBase)); + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_PWBase)); register_name = "PWBase"; break; case CP0_REG05__PWFIELD: check_pw(ctx); - gen_helper_mtc0_pwfield(cpu_env, arg); + gen_helper_mtc0_pwfield(tcg_env, arg); register_name = "PWField"; break; case CP0_REG05__PWSIZE: check_pw(ctx); - gen_helper_mtc0_pwsize(cpu_env, arg); + gen_helper_mtc0_pwsize(tcg_env, arg); register_name = "PWSize"; break; default: @@ -8120,37 +7758,37 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_06: switch (sel) { case CP0_REG06__WIRED: - gen_helper_mtc0_wired(cpu_env, arg); + gen_helper_mtc0_wired(tcg_env, arg); register_name = "Wired"; break; case CP0_REG06__SRSCONF0: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_srsconf0(cpu_env, arg); + gen_helper_mtc0_srsconf0(tcg_env, arg); register_name = "SRSConf0"; break; case CP0_REG06__SRSCONF1: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_srsconf1(cpu_env, arg); + gen_helper_mtc0_srsconf1(tcg_env, arg); register_name = "SRSConf1"; break; case CP0_REG06__SRSCONF2: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_srsconf2(cpu_env, arg); + gen_helper_mtc0_srsconf2(tcg_env, arg); register_name = "SRSConf2"; break; case CP0_REG06__SRSCONF3: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_srsconf3(cpu_env, arg); + gen_helper_mtc0_srsconf3(tcg_env, arg); register_name = "SRSConf3"; break; case CP0_REG06__SRSCONF4: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_srsconf4(cpu_env, arg); + gen_helper_mtc0_srsconf4(tcg_env, arg); register_name = "SRSConf4"; break; case CP0_REG06__PWCTL: check_pw(ctx); - gen_helper_mtc0_pwctl(cpu_env, arg); + gen_helper_mtc0_pwctl(tcg_env, arg); register_name = "PWCtl"; break; default: @@ -8161,7 +7799,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG07__HWRENA: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_hwrena(cpu_env, arg); + gen_helper_mtc0_hwrena(tcg_env, arg); ctx->base.is_jmp = DISAS_STOP; register_name = "HWREna"; break; @@ -8194,19 +7832,9 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_09: switch (sel) { case CP0_REG09__COUNT: - gen_helper_mtc0_count(cpu_env, arg); + gen_helper_mtc0_count(tcg_env, arg); register_name = "Count"; break; - case CP0_REG09__SAARI: - CP0_CHECK(ctx->saar); - gen_helper_mtc0_saari(cpu_env, arg); - register_name = "SAARI"; - break; - case CP0_REG09__SAAR: - CP0_CHECK(ctx->saar); - gen_helper_mtc0_saar(cpu_env, arg); - register_name = "SAAR"; - break; default: goto cp0_unimplemented; } @@ -8216,7 +7844,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_10: switch (sel) { case CP0_REG10__ENTRYHI: - gen_helper_mtc0_entryhi(cpu_env, arg); + gen_helper_mtc0_entryhi(tcg_env, arg); register_name = "EntryHi"; break; default: @@ -8226,7 +7854,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_11: switch (sel) { case CP0_REG11__COMPARE: - gen_helper_mtc0_compare(cpu_env, arg); + gen_helper_mtc0_compare(tcg_env, arg); register_name = "Compare"; break; /* 6,7 are implementation dependent */ @@ -8240,7 +7868,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG12__STATUS: save_cpu_state(ctx, 1); - gen_helper_mtc0_status(cpu_env, arg); + gen_helper_mtc0_status(tcg_env, arg); /* DISAS_STOP isn't good enough here, hflags may have changed. */ gen_save_pc(ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; @@ -8248,14 +7876,14 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG12__INTCTL: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_intctl(cpu_env, arg); + gen_helper_mtc0_intctl(tcg_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; register_name = "IntCtl"; break; case CP0_REG12__SRSCTL: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_srsctl(cpu_env, arg); + gen_helper_mtc0_srsctl(tcg_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; register_name = "SRSCtl"; @@ -8275,7 +7903,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG13__CAUSE: save_cpu_state(ctx, 1); - gen_helper_mtc0_cause(cpu_env, arg); + gen_helper_mtc0_cause(tcg_env, arg); /* * Stop translation as we may have triggered an interrupt. * DISAS_STOP isn't sufficient, we need to ensure we break out of @@ -8292,7 +7920,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_14: switch (sel) { case CP0_REG14__EPC: - tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC)); + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_EPC)); register_name = "EPC"; break; default: @@ -8307,7 +7935,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG15__EBASE: check_insn(ctx, ISA_MIPS_R2); - gen_helper_mtc0_ebase(cpu_env, arg); + gen_helper_mtc0_ebase(tcg_env, arg); register_name = "EBase"; break; default: @@ -8317,7 +7945,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_16: switch (sel) { case CP0_REG16__CONFIG: - gen_helper_mtc0_config0(cpu_env, arg); + gen_helper_mtc0_config0(tcg_env, arg); register_name = "Config"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; @@ -8327,13 +7955,13 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) register_name = "Config1"; break; case CP0_REG16__CONFIG2: - gen_helper_mtc0_config2(cpu_env, arg); + gen_helper_mtc0_config2(tcg_env, arg); register_name = "Config2"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; break; case CP0_REG16__CONFIG3: - gen_helper_mtc0_config3(cpu_env, arg); + gen_helper_mtc0_config3(tcg_env, arg); register_name = "Config3"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; @@ -8343,7 +7971,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) register_name = "Config4"; break; case CP0_REG16__CONFIG5: - gen_helper_mtc0_config5(cpu_env, arg); + gen_helper_mtc0_config5(tcg_env, arg); register_name = "Config5"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; @@ -8357,17 +7985,17 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_17: switch (sel) { case CP0_REG17__LLADDR: - gen_helper_mtc0_lladdr(cpu_env, arg); + gen_helper_mtc0_lladdr(tcg_env, arg); register_name = "LLAddr"; break; case CP0_REG17__MAAR: CP0_CHECK(ctx->mrp); - gen_helper_mtc0_maar(cpu_env, arg); + gen_helper_mtc0_maar(tcg_env, arg); register_name = "MAAR"; break; case CP0_REG17__MAARI: CP0_CHECK(ctx->mrp); - gen_helper_mtc0_maari(cpu_env, arg); + gen_helper_mtc0_maari(tcg_env, arg); register_name = "MAARI"; break; default: @@ -8414,7 +8042,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG20__XCONTEXT: check_insn(ctx, ISA_MIPS3); - gen_helper_mtc0_xcontext(cpu_env, arg); + gen_helper_mtc0_xcontext(tcg_env, arg); register_name = "XContext"; break; default: @@ -8426,7 +8054,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) CP0_CHECK(!(ctx->insn_flags & ISA_MIPS_R6)); switch (sel) { case 0: - gen_helper_mtc0_framemask(cpu_env, arg); + gen_helper_mtc0_framemask(tcg_env, arg); register_name = "Framemask"; break; default: @@ -8440,7 +8068,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_23: switch (sel) { case CP0_REG23__DEBUG: - gen_helper_mtc0_debug(cpu_env, arg); /* EJTAG support */ + gen_helper_mtc0_debug(tcg_env, arg); /* EJTAG support */ /* DISAS_STOP isn't good enough here, hflags may have changed. */ gen_save_pc(ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; @@ -8448,35 +8076,35 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; case CP0_REG23__TRACECONTROL: /* PDtrace support */ - /* gen_helper_mtc0_tracecontrol(cpu_env, arg); */ + /* gen_helper_mtc0_tracecontrol(tcg_env, arg); */ /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; register_name = "TraceControl"; goto cp0_unimplemented; case CP0_REG23__TRACECONTROL2: /* PDtrace support */ - /* gen_helper_mtc0_tracecontrol2(cpu_env, arg); */ + /* gen_helper_mtc0_tracecontrol2(tcg_env, arg); */ /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; register_name = "TraceControl2"; goto cp0_unimplemented; case CP0_REG23__USERTRACEDATA1: /* PDtrace support */ - /* gen_helper_mtc0_usertracedata1(cpu_env, arg);*/ + /* gen_helper_mtc0_usertracedata1(tcg_env, arg);*/ /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; register_name = "UserTraceData1"; goto cp0_unimplemented; case CP0_REG23__TRACEIBPC: /* PDtrace support */ - /* gen_helper_mtc0_traceibpc(cpu_env, arg); */ + /* gen_helper_mtc0_traceibpc(tcg_env, arg); */ /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; register_name = "TraceIBPC"; goto cp0_unimplemented; case CP0_REG23__TRACEDBPC: /* PDtrace support */ - /* gen_helper_mtc0_tracedbpc(cpu_env, arg); */ + /* gen_helper_mtc0_tracedbpc(tcg_env, arg); */ /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; register_name = "TraceDBPC"; @@ -8489,7 +8117,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case CP0_REG24__DEPC: /* EJTAG support */ - tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC)); + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_DEPC)); register_name = "DEPC"; break; default: @@ -8499,35 +8127,35 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_25: switch (sel) { case CP0_REG25__PERFCTL0: - gen_helper_mtc0_performance0(cpu_env, arg); + gen_helper_mtc0_performance0(tcg_env, arg); register_name = "Performance0"; break; case CP0_REG25__PERFCNT0: - /* gen_helper_mtc0_performance1(cpu_env, arg); */ + /* gen_helper_mtc0_performance1(tcg_env, arg); */ register_name = "Performance1"; goto cp0_unimplemented; case CP0_REG25__PERFCTL1: - /* gen_helper_mtc0_performance2(cpu_env, arg); */ + /* gen_helper_mtc0_performance2(tcg_env, arg); */ register_name = "Performance2"; goto cp0_unimplemented; case CP0_REG25__PERFCNT1: - /* gen_helper_mtc0_performance3(cpu_env, arg); */ + /* gen_helper_mtc0_performance3(tcg_env, arg); */ register_name = "Performance3"; goto cp0_unimplemented; case CP0_REG25__PERFCTL2: - /* gen_helper_mtc0_performance4(cpu_env, arg); */ + /* gen_helper_mtc0_performance4(tcg_env, arg); */ register_name = "Performance4"; goto cp0_unimplemented; case CP0_REG25__PERFCNT2: - /* gen_helper_mtc0_performance5(cpu_env, arg); */ + /* gen_helper_mtc0_performance5(tcg_env, arg); */ register_name = "Performance5"; goto cp0_unimplemented; case CP0_REG25__PERFCTL3: - /* gen_helper_mtc0_performance6(cpu_env, arg); */ + /* gen_helper_mtc0_performance6(tcg_env, arg); */ register_name = "Performance6"; goto cp0_unimplemented; case CP0_REG25__PERFCNT3: - /* gen_helper_mtc0_performance7(cpu_env, arg); */ + /* gen_helper_mtc0_performance7(tcg_env, arg); */ register_name = "Performance7"; goto cp0_unimplemented; default: @@ -8537,7 +8165,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_26: switch (sel) { case CP0_REG26__ERRCTL: - gen_helper_mtc0_errctl(cpu_env, arg); + gen_helper_mtc0_errctl(tcg_env, arg); ctx->base.is_jmp = DISAS_STOP; register_name = "ErrCtl"; break; @@ -8561,14 +8189,14 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG28__TAGLO1: case CP0_REG28__TAGLO2: case CP0_REG28__TAGLO3: - gen_helper_mtc0_taglo(cpu_env, arg); + gen_helper_mtc0_taglo(tcg_env, arg); register_name = "TagLo"; break; case CP0_REG28__DATALO: case CP0_REG28__DATALO1: case CP0_REG28__DATALO2: case CP0_REG28__DATALO3: - gen_helper_mtc0_datalo(cpu_env, arg); + gen_helper_mtc0_datalo(tcg_env, arg); register_name = "DataLo"; break; default: @@ -8581,14 +8209,14 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG29__TAGHI1: case CP0_REG29__TAGHI2: case CP0_REG29__TAGHI3: - gen_helper_mtc0_taghi(cpu_env, arg); + gen_helper_mtc0_taghi(tcg_env, arg); register_name = "TagHi"; break; case CP0_REG29__DATAHI: case CP0_REG29__DATAHI1: case CP0_REG29__DATAHI2: case CP0_REG29__DATAHI3: - gen_helper_mtc0_datahi(cpu_env, arg); + gen_helper_mtc0_datahi(tcg_env, arg); register_name = "DataHi"; break; default: @@ -8599,7 +8227,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REGISTER_30: switch (sel) { case CP0_REG30__ERROREPC: - tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); register_name = "ErrorEPC"; break; default: @@ -8620,7 +8248,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG31__KSCRATCH5: case CP0_REG31__KSCRATCH6: CP0_CHECK(ctx->kscrexist & (1 << sel)); - tcg_gen_st_tl(arg, cpu_env, + tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_KScratch[sel - 2])); register_name = "KScratch"; break; @@ -8634,7 +8262,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) trace_mips_translate_c0("dmtc0", register_name, reg, sel); /* For simplicity assume that all writes can cause interrupts. */ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + if (icount) { /* * DISAS_STOP isn't sufficient, we need to ensure we break out of * translated code to check for pending interrupts. @@ -8654,7 +8282,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, int u, int sel, int h) { int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - TCGv t0 = tcg_temp_local_new(); + TCGv t0 = tcg_temp_new(); if ((env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) == 0 && ((env->tcs[other_tc].CP0_TCBind & (0xf << CP0TCBd_CurVPE)) != @@ -8668,10 +8296,10 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, case 1: switch (sel) { case 1: - gen_helper_mftc0_vpecontrol(t0, cpu_env); + gen_helper_mftc0_vpecontrol(t0, tcg_env); break; case 2: - gen_helper_mftc0_vpeconf0(t0, cpu_env); + gen_helper_mftc0_vpeconf0(t0, tcg_env); break; default: goto die; @@ -8681,25 +8309,25 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, case 2: switch (sel) { case 1: - gen_helper_mftc0_tcstatus(t0, cpu_env); + gen_helper_mftc0_tcstatus(t0, tcg_env); break; case 2: - gen_helper_mftc0_tcbind(t0, cpu_env); + gen_helper_mftc0_tcbind(t0, tcg_env); break; case 3: - gen_helper_mftc0_tcrestart(t0, cpu_env); + gen_helper_mftc0_tcrestart(t0, tcg_env); break; case 4: - gen_helper_mftc0_tchalt(t0, cpu_env); + gen_helper_mftc0_tchalt(t0, tcg_env); break; case 5: - gen_helper_mftc0_tccontext(t0, cpu_env); + gen_helper_mftc0_tccontext(t0, tcg_env); break; case 6: - gen_helper_mftc0_tcschedule(t0, cpu_env); + gen_helper_mftc0_tcschedule(t0, tcg_env); break; case 7: - gen_helper_mftc0_tcschefback(t0, cpu_env); + gen_helper_mftc0_tcschefback(t0, tcg_env); break; default: gen_mfc0(ctx, t0, rt, sel); @@ -8709,7 +8337,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, case 10: switch (sel) { case 0: - gen_helper_mftc0_entryhi(t0, cpu_env); + gen_helper_mftc0_entryhi(t0, tcg_env); break; default: gen_mfc0(ctx, t0, rt, sel); @@ -8719,7 +8347,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, case 12: switch (sel) { case 0: - gen_helper_mftc0_status(t0, cpu_env); + gen_helper_mftc0_status(t0, tcg_env); break; default: gen_mfc0(ctx, t0, rt, sel); @@ -8729,7 +8357,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, case 13: switch (sel) { case 0: - gen_helper_mftc0_cause(t0, cpu_env); + gen_helper_mftc0_cause(t0, tcg_env); break; default: goto die; @@ -8739,7 +8367,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, case 14: switch (sel) { case 0: - gen_helper_mftc0_epc(t0, cpu_env); + gen_helper_mftc0_epc(t0, tcg_env); break; default: goto die; @@ -8749,7 +8377,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, case 15: switch (sel) { case 1: - gen_helper_mftc0_ebase(t0, cpu_env); + gen_helper_mftc0_ebase(t0, tcg_env); break; default: goto die; @@ -8766,7 +8394,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, case 5: case 6: case 7: - gen_helper_mftc0_configx(t0, cpu_env, tcg_const_tl(sel)); + gen_helper_mftc0_configx(t0, tcg_env, tcg_constant_tl(sel)); break; default: goto die; @@ -8776,7 +8404,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, case 23: switch (sel) { case 0: - gen_helper_mftc0_debug(t0, cpu_env); + gen_helper_mftc0_debug(t0, tcg_env); break; default: gen_mfc0(ctx, t0, rt, sel); @@ -8832,7 +8460,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_helper_1e0i(mftacx, t0, 3); break; case 16: - gen_helper_mftdsp(t0, cpu_env); + gen_helper_mftdsp(t0, tcg_env); break; default: goto die; @@ -8846,13 +8474,11 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_load_fpr32(ctx, fp0, rt); tcg_gen_ext_i32_tl(t0, fp0); - tcg_temp_free_i32(fp0); } else { TCGv_i32 fp0 = tcg_temp_new_i32(); gen_load_fpr32h(ctx, fp0, rt); tcg_gen_ext_i32_tl(t0, fp0); - tcg_temp_free_i32(fp0); } break; case 3: @@ -8869,11 +8495,9 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, } trace_mips_translate_tr("mftr", rt, u, sel, h); gen_store_gpr(t0, rd); - tcg_temp_free(t0); return; die: - tcg_temp_free(t0); LOG_DISAS("mftr (reg %d u %d sel %d h %d)\n", rt, u, sel, h); gen_reserved_instruction(ctx); } @@ -8882,7 +8506,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, int u, int sel, int h) { int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - TCGv t0 = tcg_temp_local_new(); + TCGv t0 = tcg_temp_new(); gen_load_gpr(t0, rt); if ((env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) == 0 && @@ -8899,10 +8523,10 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, case 1: switch (sel) { case 1: - gen_helper_mttc0_vpecontrol(cpu_env, t0); + gen_helper_mttc0_vpecontrol(tcg_env, t0); break; case 2: - gen_helper_mttc0_vpeconf0(cpu_env, t0); + gen_helper_mttc0_vpeconf0(tcg_env, t0); break; default: goto die; @@ -8912,25 +8536,25 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, case 2: switch (sel) { case 1: - gen_helper_mttc0_tcstatus(cpu_env, t0); + gen_helper_mttc0_tcstatus(tcg_env, t0); break; case 2: - gen_helper_mttc0_tcbind(cpu_env, t0); + gen_helper_mttc0_tcbind(tcg_env, t0); break; case 3: - gen_helper_mttc0_tcrestart(cpu_env, t0); + gen_helper_mttc0_tcrestart(tcg_env, t0); break; case 4: - gen_helper_mttc0_tchalt(cpu_env, t0); + gen_helper_mttc0_tchalt(tcg_env, t0); break; case 5: - gen_helper_mttc0_tccontext(cpu_env, t0); + gen_helper_mttc0_tccontext(tcg_env, t0); break; case 6: - gen_helper_mttc0_tcschedule(cpu_env, t0); + gen_helper_mttc0_tcschedule(tcg_env, t0); break; case 7: - gen_helper_mttc0_tcschefback(cpu_env, t0); + gen_helper_mttc0_tcschefback(tcg_env, t0); break; default: gen_mtc0(ctx, t0, rd, sel); @@ -8940,7 +8564,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, case 10: switch (sel) { case 0: - gen_helper_mttc0_entryhi(cpu_env, t0); + gen_helper_mttc0_entryhi(tcg_env, t0); break; default: gen_mtc0(ctx, t0, rd, sel); @@ -8950,7 +8574,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, case 12: switch (sel) { case 0: - gen_helper_mttc0_status(cpu_env, t0); + gen_helper_mttc0_status(tcg_env, t0); break; default: gen_mtc0(ctx, t0, rd, sel); @@ -8960,7 +8584,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, case 13: switch (sel) { case 0: - gen_helper_mttc0_cause(cpu_env, t0); + gen_helper_mttc0_cause(tcg_env, t0); break; default: goto die; @@ -8970,7 +8594,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, case 15: switch (sel) { case 1: - gen_helper_mttc0_ebase(cpu_env, t0); + gen_helper_mttc0_ebase(tcg_env, t0); break; default: goto die; @@ -8980,7 +8604,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, case 23: switch (sel) { case 0: - gen_helper_mttc0_debug(cpu_env, t0); + gen_helper_mttc0_debug(tcg_env, t0); break; default: gen_mtc0(ctx, t0, rd, sel); @@ -9036,7 +8660,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_helper_0e1i(mttacx, t0, 3); break; case 16: - gen_helper_mttdsp(cpu_env, t0); + gen_helper_mttdsp(tcg_env, t0); break; default: goto die; @@ -9050,13 +8674,11 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, tcg_gen_trunc_tl_i32(fp0, t0); gen_store_fpr32(ctx, fp0, rd); - tcg_temp_free_i32(fp0); } else { TCGv_i32 fp0 = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(fp0, t0); gen_store_fpr32h(ctx, fp0, rd); - tcg_temp_free_i32(fp0); } break; case 3: @@ -9074,11 +8696,9 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, } } trace_mips_translate_tr("mttr", rd, u, sel, h); - tcg_temp_free(t0); return; die: - tcg_temp_free(t0); LOG_DISAS("mttr (reg %d u %d sel %d h %d)\n", rd, u, sel, h); gen_reserved_instruction(ctx); } @@ -9104,7 +8724,6 @@ static void gen_cp0(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, gen_load_gpr(t0, rt); gen_mtc0(ctx, t0, rd, ctx->opcode & 0x7); - tcg_temp_free(t0); } opn = "mtc0"; break; @@ -9125,7 +8744,6 @@ static void gen_cp0(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, gen_load_gpr(t0, rt); gen_dmtc0(ctx, t0, rd, ctx->opcode & 0x7); - tcg_temp_free(t0); } opn = "dmtc0"; break; @@ -9145,7 +8763,6 @@ static void gen_cp0(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, TCGv t0 = tcg_temp_new(); gen_load_gpr(t0, rt); gen_mthc0(ctx, t0, rd, ctx->opcode & 0x7); - tcg_temp_free(t0); } opn = "mthc0"; break; @@ -9170,7 +8787,7 @@ static void gen_cp0(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, if (!env->tlb->helper_tlbwi) { goto die; } - gen_helper_tlbwi(cpu_env); + gen_helper_tlbwi(tcg_env); break; case OPC_TLBINV: opn = "tlbinv"; @@ -9178,7 +8795,7 @@ static void gen_cp0(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, if (!env->tlb->helper_tlbinv) { goto die; } - gen_helper_tlbinv(cpu_env); + gen_helper_tlbinv(tcg_env); } /* treat as nop if TLBINV not supported */ break; case OPC_TLBINVF: @@ -9187,7 +8804,7 @@ static void gen_cp0(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, if (!env->tlb->helper_tlbinvf) { goto die; } - gen_helper_tlbinvf(cpu_env); + gen_helper_tlbinvf(tcg_env); } /* treat as nop if TLBINV not supported */ break; case OPC_TLBWR: @@ -9195,21 +8812,21 @@ static void gen_cp0(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, if (!env->tlb->helper_tlbwr) { goto die; } - gen_helper_tlbwr(cpu_env); + gen_helper_tlbwr(tcg_env); break; case OPC_TLBP: opn = "tlbp"; if (!env->tlb->helper_tlbp) { goto die; } - gen_helper_tlbp(cpu_env); + gen_helper_tlbp(tcg_env); break; case OPC_TLBR: opn = "tlbr"; if (!env->tlb->helper_tlbr) { goto die; } - gen_helper_tlbr(cpu_env); + gen_helper_tlbr(tcg_env); break; case OPC_ERET: /* OPC_ERETNC */ if ((ctx->insn_flags & ISA_MIPS_R6) && @@ -9221,12 +8838,12 @@ static void gen_cp0(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, /* OPC_ERETNC */ opn = "eretnc"; check_insn(ctx, ISA_MIPS_R5); - gen_helper_eretnc(cpu_env); + gen_helper_eretnc(tcg_env); } else { /* OPC_ERET */ opn = "eret"; check_insn(ctx, ISA_MIPS2); - gen_helper_eret(cpu_env); + gen_helper_eret(tcg_env); } ctx->base.is_jmp = DISAS_EXIT; } @@ -9242,7 +8859,7 @@ static void gen_cp0(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, MIPS_INVAL(opn); gen_reserved_instruction(ctx); } else { - gen_helper_deret(cpu_env); + gen_helper_deret(tcg_env); ctx->base.is_jmp = DISAS_EXIT; } break; @@ -9257,7 +8874,7 @@ static void gen_cp0(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, ctx->base.pc_next += 4; save_cpu_state(ctx, 1); ctx->base.pc_next -= 4; - gen_helper_wait(cpu_env); + gen_helper_wait(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; break; default: @@ -9279,7 +8896,7 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, if ((ctx->insn_flags & ISA_MIPS_R6) && (ctx->hflags & MIPS_HFLAG_BMASK)) { gen_reserved_instruction(ctx); - goto out; + return; } if (cc != 0) { @@ -9319,7 +8936,6 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc)); tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc + 1)); tcg_gen_nand_i32(t0, t0, t1); - tcg_temp_free_i32(t1); tcg_gen_andi_i32(t0, t0, 1); tcg_gen_extu_i32_tl(bcond, t0); } @@ -9330,7 +8946,6 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc)); tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc + 1)); tcg_gen_or_i32(t0, t0, t1); - tcg_temp_free_i32(t1); tcg_gen_andi_i32(t0, t0, 1); tcg_gen_extu_i32_tl(bcond, t0); } @@ -9345,7 +8960,6 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, tcg_gen_and_i32(t0, t0, t1); tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc + 3)); tcg_gen_nand_i32(t0, t0, t1); - tcg_temp_free_i32(t1); tcg_gen_andi_i32(t0, t0, 1); tcg_gen_extu_i32_tl(bcond, t0); } @@ -9360,7 +8974,6 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, tcg_gen_or_i32(t0, t0, t1); tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc + 3)); tcg_gen_or_i32(t0, t0, t1); - tcg_temp_free_i32(t1); tcg_gen_andi_i32(t0, t0, 1); tcg_gen_extu_i32_tl(bcond, t0); } @@ -9370,12 +8983,10 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, default: MIPS_INVAL("cp1 cond branch"); gen_reserved_instruction(ctx); - goto out; + return; } ctx->btarget = btarget; ctx->hflags |= MIPS_HFLAG_BDS32; - out: - tcg_temp_free_i32(t0); } /* R6 CP1 Branches */ @@ -9388,11 +8999,11 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, if (ctx->hflags & MIPS_HFLAG_BMASK) { #ifdef MIPS_DEBUG_DISAS - LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx - "\n", ctx->base.pc_next); + LOG_DISAS("Branch in delay / forbidden slot at PC 0x%016" + VADDR_PRIx "\n", ctx->base.pc_next); #endif gen_reserved_instruction(ctx); - goto out; + return; } gen_load_fpr64(ctx, t0, ft); @@ -9412,7 +9023,7 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, default: MIPS_INVAL("cp1 cond branch"); gen_reserved_instruction(ctx); - goto out; + return; } tcg_gen_trunc_i64_tl(bcond, t0); @@ -9427,9 +9038,6 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, ctx->hflags |= MIPS_HFLAG_BDS32; break; } - -out: - tcg_temp_free_i64(t0); } /* Coprocessor 1 (FPU) */ @@ -9657,7 +9265,6 @@ static void gen_cp1(DisasContext *ctx, uint32_t opc, int rt, int fs) gen_load_fpr32(ctx, fp0, fs); tcg_gen_ext_i32_tl(t0, fp0); - tcg_temp_free_i32(fp0); } gen_store_gpr(t0, rt); break; @@ -9668,7 +9275,6 @@ static void gen_cp1(DisasContext *ctx, uint32_t opc, int rt, int fs) tcg_gen_trunc_tl_i32(fp0, t0); gen_store_fpr32(ctx, fp0, fs); - tcg_temp_free_i32(fp0); } break; case OPC_CFC1: @@ -9698,7 +9304,6 @@ static void gen_cp1(DisasContext *ctx, uint32_t opc, int rt, int fs) gen_load_fpr32h(ctx, fp0, fs); tcg_gen_ext_i32_tl(t0, fp0); - tcg_temp_free_i32(fp0); } gen_store_gpr(t0, rt); break; @@ -9709,17 +9314,13 @@ static void gen_cp1(DisasContext *ctx, uint32_t opc, int rt, int fs) tcg_gen_trunc_tl_i32(fp0, t0); gen_store_fpr32h(ctx, fp0, fs); - tcg_temp_free_i32(fp0); } break; default: MIPS_INVAL("cp1 move"); gen_reserved_instruction(ctx); - goto out; + return; } - - out: - tcg_temp_free(t0); } static void gen_movci(DisasContext *ctx, int rd, int rs, int cc, int tf) @@ -9743,7 +9344,6 @@ static void gen_movci(DisasContext *ctx, int rd, int rs, int cc, int tf) t0 = tcg_temp_new_i32(); tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc)); tcg_gen_brcondi_i32(cond, t0, 0, l1); - tcg_temp_free_i32(t0); gen_load_gpr(cpu_gpr[rd], rs); gen_set_label(l1); } @@ -9766,7 +9366,6 @@ static inline void gen_movcf_s(DisasContext *ctx, int fs, int fd, int cc, gen_load_fpr32(ctx, t0, fs); gen_store_fpr32(ctx, t0, fd); gen_set_label(l1); - tcg_temp_free_i32(t0); } static inline void gen_movcf_d(DisasContext *ctx, int fs, int fd, int cc, @@ -9785,11 +9384,9 @@ static inline void gen_movcf_d(DisasContext *ctx, int fs, int fd, int cc, tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc)); tcg_gen_brcondi_i32(cond, t0, 0, l1); - tcg_temp_free_i32(t0); fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); gen_set_label(l1); } @@ -9817,14 +9414,13 @@ static inline void gen_movcf_ps(DisasContext *ctx, int fs, int fd, tcg_gen_brcondi_i32(cond, t0, 0, l2); gen_load_fpr32h(ctx, t0, fs); gen_store_fpr32h(ctx, t0, fd); - tcg_temp_free_i32(t0); gen_set_label(l2); } static void gen_sel_s(DisasContext *ctx, enum fopcode op1, int fd, int ft, int fs) { - TCGv_i32 t1 = tcg_const_i32(0); + TCGv_i32 t1 = tcg_constant_i32(0); TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); TCGv_i32 fp2 = tcg_temp_new_i32(); @@ -9852,16 +9448,12 @@ static void gen_sel_s(DisasContext *ctx, enum fopcode op1, int fd, int ft, } gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp2); - tcg_temp_free_i32(fp1); - tcg_temp_free_i32(fp0); - tcg_temp_free_i32(t1); } static void gen_sel_d(DisasContext *ctx, enum fopcode op1, int fd, int ft, int fs) { - TCGv_i64 t1 = tcg_const_i64(0); + TCGv_i64 t1 = tcg_constant_i64(0); TCGv_i64 fp0 = tcg_temp_new_i64(); TCGv_i64 fp1 = tcg_temp_new_i64(); TCGv_i64 fp2 = tcg_temp_new_i64(); @@ -9889,10 +9481,6 @@ static void gen_sel_d(DisasContext *ctx, enum fopcode op1, int fd, int ft, } gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp2); - tcg_temp_free_i64(fp1); - tcg_temp_free_i64(fp0); - tcg_temp_free_i64(t1); } static void gen_farith(DisasContext *ctx, enum fopcode op1, @@ -9907,10 +9495,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); - gen_helper_float_add_s(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i32(fp1); + gen_helper_float_add_s(fp0, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_SUB_S: @@ -9920,10 +9506,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); - gen_helper_float_sub_s(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i32(fp1); + gen_helper_float_sub_s(fp0, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_MUL_S: @@ -9933,10 +9517,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); - gen_helper_float_mul_s(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i32(fp1); + gen_helper_float_mul_s(fp0, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_DIV_S: @@ -9946,10 +9528,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); - gen_helper_float_div_s(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i32(fp1); + gen_helper_float_div_s(fp0, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_SQRT_S: @@ -9957,9 +9537,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); - gen_helper_float_sqrt_s(fp0, cpu_env, fp0); + gen_helper_float_sqrt_s(fp0, tcg_env, fp0); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_ABS_S: @@ -9973,7 +9552,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_helper_float_abs_s(fp0, fp0); } gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_MOV_S: @@ -9982,7 +9560,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp0, fs); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_NEG_S: @@ -9996,7 +9573,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_helper_float_chs_s(fp0, fp0); } gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_ROUND_L_S: @@ -10007,13 +9583,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp32, fs); if (ctx->nan2008) { - gen_helper_float_round_2008_l_s(fp64, cpu_env, fp32); + gen_helper_float_round_2008_l_s(fp64, tcg_env, fp32); } else { - gen_helper_float_round_l_s(fp64, cpu_env, fp32); + gen_helper_float_round_l_s(fp64, tcg_env, fp32); } - tcg_temp_free_i32(fp32); gen_store_fpr64(ctx, fp64, fd); - tcg_temp_free_i64(fp64); } break; case OPC_TRUNC_L_S: @@ -10024,13 +9598,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp32, fs); if (ctx->nan2008) { - gen_helper_float_trunc_2008_l_s(fp64, cpu_env, fp32); + gen_helper_float_trunc_2008_l_s(fp64, tcg_env, fp32); } else { - gen_helper_float_trunc_l_s(fp64, cpu_env, fp32); + gen_helper_float_trunc_l_s(fp64, tcg_env, fp32); } - tcg_temp_free_i32(fp32); gen_store_fpr64(ctx, fp64, fd); - tcg_temp_free_i64(fp64); } break; case OPC_CEIL_L_S: @@ -10041,13 +9613,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp32, fs); if (ctx->nan2008) { - gen_helper_float_ceil_2008_l_s(fp64, cpu_env, fp32); + gen_helper_float_ceil_2008_l_s(fp64, tcg_env, fp32); } else { - gen_helper_float_ceil_l_s(fp64, cpu_env, fp32); + gen_helper_float_ceil_l_s(fp64, tcg_env, fp32); } - tcg_temp_free_i32(fp32); gen_store_fpr64(ctx, fp64, fd); - tcg_temp_free_i64(fp64); } break; case OPC_FLOOR_L_S: @@ -10058,13 +9628,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp32, fs); if (ctx->nan2008) { - gen_helper_float_floor_2008_l_s(fp64, cpu_env, fp32); + gen_helper_float_floor_2008_l_s(fp64, tcg_env, fp32); } else { - gen_helper_float_floor_l_s(fp64, cpu_env, fp32); + gen_helper_float_floor_l_s(fp64, tcg_env, fp32); } - tcg_temp_free_i32(fp32); gen_store_fpr64(ctx, fp64, fd); - tcg_temp_free_i64(fp64); } break; case OPC_ROUND_W_S: @@ -10073,12 +9641,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp0, fs); if (ctx->nan2008) { - gen_helper_float_round_2008_w_s(fp0, cpu_env, fp0); + gen_helper_float_round_2008_w_s(fp0, tcg_env, fp0); } else { - gen_helper_float_round_w_s(fp0, cpu_env, fp0); + gen_helper_float_round_w_s(fp0, tcg_env, fp0); } gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_TRUNC_W_S: @@ -10087,12 +9654,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp0, fs); if (ctx->nan2008) { - gen_helper_float_trunc_2008_w_s(fp0, cpu_env, fp0); + gen_helper_float_trunc_2008_w_s(fp0, tcg_env, fp0); } else { - gen_helper_float_trunc_w_s(fp0, cpu_env, fp0); + gen_helper_float_trunc_w_s(fp0, tcg_env, fp0); } gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_CEIL_W_S: @@ -10101,12 +9667,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp0, fs); if (ctx->nan2008) { - gen_helper_float_ceil_2008_w_s(fp0, cpu_env, fp0); + gen_helper_float_ceil_2008_w_s(fp0, tcg_env, fp0); } else { - gen_helper_float_ceil_w_s(fp0, cpu_env, fp0); + gen_helper_float_ceil_w_s(fp0, tcg_env, fp0); } gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_FLOOR_W_S: @@ -10115,12 +9680,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp0, fs); if (ctx->nan2008) { - gen_helper_float_floor_2008_w_s(fp0, cpu_env, fp0); + gen_helper_float_floor_2008_w_s(fp0, tcg_env, fp0); } else { - gen_helper_float_floor_w_s(fp0, cpu_env, fp0); + gen_helper_float_floor_w_s(fp0, tcg_env, fp0); } gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_SEL_S: @@ -10151,7 +9715,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); gen_set_label(l1); } break; @@ -10166,7 +9729,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); gen_set_label(l1); } } @@ -10176,9 +9738,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); - gen_helper_float_recip_s(fp0, cpu_env, fp0); + gen_helper_float_recip_s(fp0, tcg_env, fp0); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_RSQRT_S: @@ -10186,9 +9747,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); - gen_helper_float_rsqrt_s(fp0, cpu_env, fp0); + gen_helper_float_rsqrt_s(fp0, tcg_env, fp0); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_MADDF_S: @@ -10200,11 +9760,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); gen_load_fpr32(ctx, fp2, fd); - gen_helper_float_maddf_s(fp2, cpu_env, fp0, fp1, fp2); + gen_helper_float_maddf_s(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr32(ctx, fp2, fd); - tcg_temp_free_i32(fp2); - tcg_temp_free_i32(fp1); - tcg_temp_free_i32(fp0); } break; case OPC_MSUBF_S: @@ -10216,11 +9773,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); gen_load_fpr32(ctx, fp2, fd); - gen_helper_float_msubf_s(fp2, cpu_env, fp0, fp1, fp2); + gen_helper_float_msubf_s(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr32(ctx, fp2, fd); - tcg_temp_free_i32(fp2); - tcg_temp_free_i32(fp1); - tcg_temp_free_i32(fp0); } break; case OPC_RINT_S: @@ -10228,9 +9782,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); - gen_helper_float_rint_s(fp0, cpu_env, fp0); + gen_helper_float_rint_s(fp0, tcg_env, fp0); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_CLASS_S: @@ -10238,9 +9791,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); - gen_helper_float_class_s(fp0, cpu_env, fp0); + gen_helper_float_class_s(fp0, tcg_env, fp0); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_MIN_S: /* OPC_RECIP2_S */ @@ -10251,11 +9803,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i32 fp2 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); - gen_helper_float_min_s(fp2, cpu_env, fp0, fp1); + gen_helper_float_min_s(fp2, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp2, fd); - tcg_temp_free_i32(fp2); - tcg_temp_free_i32(fp1); - tcg_temp_free_i32(fp0); } else { /* OPC_RECIP2_S */ check_cp1_64bitmode(ctx); @@ -10265,10 +9814,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); - gen_helper_float_recip2_s(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i32(fp1); + gen_helper_float_recip2_s(fp0, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } } break; @@ -10280,11 +9827,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i32 fp2 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); - gen_helper_float_mina_s(fp2, cpu_env, fp0, fp1); + gen_helper_float_mina_s(fp2, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp2, fd); - tcg_temp_free_i32(fp2); - tcg_temp_free_i32(fp1); - tcg_temp_free_i32(fp0); } else { /* OPC_RECIP1_S */ check_cp1_64bitmode(ctx); @@ -10292,9 +9836,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); - gen_helper_float_recip1_s(fp0, cpu_env, fp0); + gen_helper_float_recip1_s(fp0, tcg_env, fp0); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } } break; @@ -10305,10 +9848,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i32 fp1 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); - gen_helper_float_max_s(fp1, cpu_env, fp0, fp1); + gen_helper_float_max_s(fp1, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp1, fd); - tcg_temp_free_i32(fp1); - tcg_temp_free_i32(fp0); } else { /* OPC_RSQRT1_S */ check_cp1_64bitmode(ctx); @@ -10316,9 +9857,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); - gen_helper_float_rsqrt1_s(fp0, cpu_env, fp0); + gen_helper_float_rsqrt1_s(fp0, tcg_env, fp0); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } } break; @@ -10329,10 +9869,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i32 fp1 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); - gen_helper_float_maxa_s(fp1, cpu_env, fp0, fp1); + gen_helper_float_maxa_s(fp1, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp1, fd); - tcg_temp_free_i32(fp1); - tcg_temp_free_i32(fp0); } else { /* OPC_RSQRT2_S */ check_cp1_64bitmode(ctx); @@ -10342,10 +9880,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); - gen_helper_float_rsqrt2_s(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i32(fp1); + gen_helper_float_rsqrt2_s(fp0, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } } break; @@ -10356,10 +9892,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp64 = tcg_temp_new_i64(); gen_load_fpr32(ctx, fp32, fs); - gen_helper_float_cvtd_s(fp64, cpu_env, fp32); - tcg_temp_free_i32(fp32); + gen_helper_float_cvtd_s(fp64, tcg_env, fp32); gen_store_fpr64(ctx, fp64, fd); - tcg_temp_free_i64(fp64); } break; case OPC_CVT_W_S: @@ -10368,12 +9902,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp0, fs); if (ctx->nan2008) { - gen_helper_float_cvt_2008_w_s(fp0, cpu_env, fp0); + gen_helper_float_cvt_2008_w_s(fp0, tcg_env, fp0); } else { - gen_helper_float_cvt_w_s(fp0, cpu_env, fp0); + gen_helper_float_cvt_w_s(fp0, tcg_env, fp0); } gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_CVT_L_S: @@ -10384,13 +9917,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp32, fs); if (ctx->nan2008) { - gen_helper_float_cvt_2008_l_s(fp64, cpu_env, fp32); + gen_helper_float_cvt_2008_l_s(fp64, tcg_env, fp32); } else { - gen_helper_float_cvt_l_s(fp64, cpu_env, fp32); + gen_helper_float_cvt_l_s(fp64, tcg_env, fp32); } - tcg_temp_free_i32(fp32); gen_store_fpr64(ctx, fp64, fd); - tcg_temp_free_i64(fp64); } break; case OPC_CVT_PS_S: @@ -10403,10 +9934,7 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp32_0, fs); gen_load_fpr32(ctx, fp32_1, ft); tcg_gen_concat_i32_i64(fp64, fp32_1, fp32_0); - tcg_temp_free_i32(fp32_1); - tcg_temp_free_i32(fp32_0); gen_store_fpr64(ctx, fp64, fd); - tcg_temp_free_i64(fp64); } break; case OPC_CMP_F_S: @@ -10440,10 +9968,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_add_d(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i64(fp1); + gen_helper_float_add_d(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_SUB_D: @@ -10454,10 +9980,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_sub_d(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i64(fp1); + gen_helper_float_sub_d(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_MUL_D: @@ -10468,10 +9992,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_mul_d(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i64(fp1); + gen_helper_float_mul_d(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_DIV_D: @@ -10482,10 +10004,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_div_d(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i64(fp1); + gen_helper_float_div_d(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_SQRT_D: @@ -10494,9 +10014,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); - gen_helper_float_sqrt_d(fp0, cpu_env, fp0); + gen_helper_float_sqrt_d(fp0, tcg_env, fp0); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_ABS_D: @@ -10511,7 +10030,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_helper_float_abs_d(fp0, fp0); } gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_MOV_D: @@ -10521,7 +10039,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_NEG_D: @@ -10536,7 +10053,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_helper_float_chs_d(fp0, fp0); } gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_ROUND_L_D: @@ -10546,12 +10062,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); if (ctx->nan2008) { - gen_helper_float_round_2008_l_d(fp0, cpu_env, fp0); + gen_helper_float_round_2008_l_d(fp0, tcg_env, fp0); } else { - gen_helper_float_round_l_d(fp0, cpu_env, fp0); + gen_helper_float_round_l_d(fp0, tcg_env, fp0); } gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_TRUNC_L_D: @@ -10561,12 +10076,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); if (ctx->nan2008) { - gen_helper_float_trunc_2008_l_d(fp0, cpu_env, fp0); + gen_helper_float_trunc_2008_l_d(fp0, tcg_env, fp0); } else { - gen_helper_float_trunc_l_d(fp0, cpu_env, fp0); + gen_helper_float_trunc_l_d(fp0, tcg_env, fp0); } gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_CEIL_L_D: @@ -10576,12 +10090,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); if (ctx->nan2008) { - gen_helper_float_ceil_2008_l_d(fp0, cpu_env, fp0); + gen_helper_float_ceil_2008_l_d(fp0, tcg_env, fp0); } else { - gen_helper_float_ceil_l_d(fp0, cpu_env, fp0); + gen_helper_float_ceil_l_d(fp0, tcg_env, fp0); } gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_FLOOR_L_D: @@ -10591,12 +10104,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); if (ctx->nan2008) { - gen_helper_float_floor_2008_l_d(fp0, cpu_env, fp0); + gen_helper_float_floor_2008_l_d(fp0, tcg_env, fp0); } else { - gen_helper_float_floor_l_d(fp0, cpu_env, fp0); + gen_helper_float_floor_l_d(fp0, tcg_env, fp0); } gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_ROUND_W_D: @@ -10607,13 +10119,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp64, fs); if (ctx->nan2008) { - gen_helper_float_round_2008_w_d(fp32, cpu_env, fp64); + gen_helper_float_round_2008_w_d(fp32, tcg_env, fp64); } else { - gen_helper_float_round_w_d(fp32, cpu_env, fp64); + gen_helper_float_round_w_d(fp32, tcg_env, fp64); } - tcg_temp_free_i64(fp64); gen_store_fpr32(ctx, fp32, fd); - tcg_temp_free_i32(fp32); } break; case OPC_TRUNC_W_D: @@ -10624,13 +10134,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp64, fs); if (ctx->nan2008) { - gen_helper_float_trunc_2008_w_d(fp32, cpu_env, fp64); + gen_helper_float_trunc_2008_w_d(fp32, tcg_env, fp64); } else { - gen_helper_float_trunc_w_d(fp32, cpu_env, fp64); + gen_helper_float_trunc_w_d(fp32, tcg_env, fp64); } - tcg_temp_free_i64(fp64); gen_store_fpr32(ctx, fp32, fd); - tcg_temp_free_i32(fp32); } break; case OPC_CEIL_W_D: @@ -10641,13 +10149,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp64, fs); if (ctx->nan2008) { - gen_helper_float_ceil_2008_w_d(fp32, cpu_env, fp64); + gen_helper_float_ceil_2008_w_d(fp32, tcg_env, fp64); } else { - gen_helper_float_ceil_w_d(fp32, cpu_env, fp64); + gen_helper_float_ceil_w_d(fp32, tcg_env, fp64); } - tcg_temp_free_i64(fp64); gen_store_fpr32(ctx, fp32, fd); - tcg_temp_free_i32(fp32); } break; case OPC_FLOOR_W_D: @@ -10658,13 +10164,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp64, fs); if (ctx->nan2008) { - gen_helper_float_floor_2008_w_d(fp32, cpu_env, fp64); + gen_helper_float_floor_2008_w_d(fp32, tcg_env, fp64); } else { - gen_helper_float_floor_w_d(fp32, cpu_env, fp64); + gen_helper_float_floor_w_d(fp32, tcg_env, fp64); } - tcg_temp_free_i64(fp64); gen_store_fpr32(ctx, fp32, fd); - tcg_temp_free_i32(fp32); } break; case OPC_SEL_D: @@ -10695,7 +10199,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); gen_set_label(l1); } break; @@ -10710,7 +10213,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); gen_set_label(l1); } } @@ -10721,9 +10223,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); - gen_helper_float_recip_d(fp0, cpu_env, fp0); + gen_helper_float_recip_d(fp0, tcg_env, fp0); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_RSQRT_D: @@ -10732,9 +10233,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); - gen_helper_float_rsqrt_d(fp0, cpu_env, fp0); + gen_helper_float_rsqrt_d(fp0, tcg_env, fp0); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_MADDF_D: @@ -10746,11 +10246,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); gen_load_fpr64(ctx, fp2, fd); - gen_helper_float_maddf_d(fp2, cpu_env, fp0, fp1, fp2); + gen_helper_float_maddf_d(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); - tcg_temp_free_i64(fp2); - tcg_temp_free_i64(fp1); - tcg_temp_free_i64(fp0); } break; case OPC_MSUBF_D: @@ -10762,11 +10259,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); gen_load_fpr64(ctx, fp2, fd); - gen_helper_float_msubf_d(fp2, cpu_env, fp0, fp1, fp2); + gen_helper_float_msubf_d(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); - tcg_temp_free_i64(fp2); - tcg_temp_free_i64(fp1); - tcg_temp_free_i64(fp0); } break; case OPC_RINT_D: @@ -10774,9 +10268,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, { TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); - gen_helper_float_rint_d(fp0, cpu_env, fp0); + gen_helper_float_rint_d(fp0, tcg_env, fp0); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_CLASS_D: @@ -10784,9 +10277,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, { TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); - gen_helper_float_class_d(fp0, cpu_env, fp0); + gen_helper_float_class_d(fp0, tcg_env, fp0); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_MIN_D: /* OPC_RECIP2_D */ @@ -10796,10 +10288,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp1 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_min_d(fp1, cpu_env, fp0, fp1); + gen_helper_float_min_d(fp1, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp1, fd); - tcg_temp_free_i64(fp1); - tcg_temp_free_i64(fp0); } else { /* OPC_RECIP2_D */ check_cp1_64bitmode(ctx); @@ -10809,10 +10299,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_recip2_d(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i64(fp1); + gen_helper_float_recip2_d(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } } break; @@ -10823,10 +10311,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp1 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_mina_d(fp1, cpu_env, fp0, fp1); + gen_helper_float_mina_d(fp1, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp1, fd); - tcg_temp_free_i64(fp1); - tcg_temp_free_i64(fp0); } else { /* OPC_RECIP1_D */ check_cp1_64bitmode(ctx); @@ -10834,9 +10320,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); - gen_helper_float_recip1_d(fp0, cpu_env, fp0); + gen_helper_float_recip1_d(fp0, tcg_env, fp0); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } } break; @@ -10847,10 +10332,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp1 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_max_d(fp1, cpu_env, fp0, fp1); + gen_helper_float_max_d(fp1, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp1, fd); - tcg_temp_free_i64(fp1); - tcg_temp_free_i64(fp0); } else { /* OPC_RSQRT1_D */ check_cp1_64bitmode(ctx); @@ -10858,9 +10341,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); - gen_helper_float_rsqrt1_d(fp0, cpu_env, fp0); + gen_helper_float_rsqrt1_d(fp0, tcg_env, fp0); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } } break; @@ -10871,10 +10353,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp1 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_maxa_d(fp1, cpu_env, fp0, fp1); + gen_helper_float_maxa_d(fp1, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp1, fd); - tcg_temp_free_i64(fp1); - tcg_temp_free_i64(fp0); } else { /* OPC_RSQRT2_D */ check_cp1_64bitmode(ctx); @@ -10884,10 +10364,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_rsqrt2_d(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i64(fp1); + gen_helper_float_rsqrt2_d(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } } break; @@ -10921,10 +10399,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp64 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp64, fs); - gen_helper_float_cvts_d(fp32, cpu_env, fp64); - tcg_temp_free_i64(fp64); + gen_helper_float_cvts_d(fp32, tcg_env, fp64); gen_store_fpr32(ctx, fp32, fd); - tcg_temp_free_i32(fp32); } break; case OPC_CVT_W_D: @@ -10935,13 +10411,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp64, fs); if (ctx->nan2008) { - gen_helper_float_cvt_2008_w_d(fp32, cpu_env, fp64); + gen_helper_float_cvt_2008_w_d(fp32, tcg_env, fp64); } else { - gen_helper_float_cvt_w_d(fp32, cpu_env, fp64); + gen_helper_float_cvt_w_d(fp32, tcg_env, fp64); } - tcg_temp_free_i64(fp64); gen_store_fpr32(ctx, fp32, fd); - tcg_temp_free_i32(fp32); } break; case OPC_CVT_L_D: @@ -10951,12 +10425,11 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); if (ctx->nan2008) { - gen_helper_float_cvt_2008_l_d(fp0, cpu_env, fp0); + gen_helper_float_cvt_2008_l_d(fp0, tcg_env, fp0); } else { - gen_helper_float_cvt_l_d(fp0, cpu_env, fp0); + gen_helper_float_cvt_l_d(fp0, tcg_env, fp0); } gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_CVT_S_W: @@ -10964,9 +10437,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); - gen_helper_float_cvts_w(fp0, cpu_env, fp0); + gen_helper_float_cvts_w(fp0, tcg_env, fp0); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_CVT_D_W: @@ -10976,10 +10448,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp64 = tcg_temp_new_i64(); gen_load_fpr32(ctx, fp32, fs); - gen_helper_float_cvtd_w(fp64, cpu_env, fp32); - tcg_temp_free_i32(fp32); + gen_helper_float_cvtd_w(fp64, tcg_env, fp32); gen_store_fpr64(ctx, fp64, fd); - tcg_temp_free_i64(fp64); } break; case OPC_CVT_S_L: @@ -10989,10 +10459,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp64 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp64, fs); - gen_helper_float_cvts_l(fp32, cpu_env, fp64); - tcg_temp_free_i64(fp64); + gen_helper_float_cvts_l(fp32, tcg_env, fp64); gen_store_fpr32(ctx, fp32, fd); - tcg_temp_free_i32(fp32); } break; case OPC_CVT_D_L: @@ -11001,9 +10469,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); - gen_helper_float_cvtd_l(fp0, cpu_env, fp0); + gen_helper_float_cvtd_l(fp0, tcg_env, fp0); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_CVT_PS_PW: @@ -11012,9 +10479,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); - gen_helper_float_cvtps_pw(fp0, cpu_env, fp0); + gen_helper_float_cvtps_pw(fp0, tcg_env, fp0); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_ADD_PS: @@ -11025,10 +10491,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_add_ps(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i64(fp1); + gen_helper_float_add_ps(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_SUB_PS: @@ -11039,10 +10503,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_sub_ps(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i64(fp1); + gen_helper_float_sub_ps(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_MUL_PS: @@ -11053,10 +10515,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_mul_ps(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i64(fp1); + gen_helper_float_mul_ps(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_ABS_PS: @@ -11067,7 +10527,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_helper_float_abs_ps(fp0, fp0); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_MOV_PS: @@ -11077,7 +10536,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_NEG_PS: @@ -11088,7 +10546,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_helper_float_chs_ps(fp0, fp0); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_MOVCF_PS: @@ -11107,7 +10564,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); gen_set_label(l1); } break; @@ -11122,7 +10578,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); gen_set_label(l1); } } @@ -11135,10 +10590,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, ft); gen_load_fpr64(ctx, fp1, fs); - gen_helper_float_addr_ps(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i64(fp1); + gen_helper_float_addr_ps(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_MULR_PS: @@ -11149,10 +10602,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, ft); gen_load_fpr64(ctx, fp1, fs); - gen_helper_float_mulr_ps(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i64(fp1); + gen_helper_float_mulr_ps(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_RECIP2_PS: @@ -11163,10 +10614,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_recip2_ps(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i64(fp1); + gen_helper_float_recip2_ps(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_RECIP1_PS: @@ -11175,9 +10624,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); - gen_helper_float_recip1_ps(fp0, cpu_env, fp0); + gen_helper_float_recip1_ps(fp0, tcg_env, fp0); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_RSQRT1_PS: @@ -11186,9 +10634,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); - gen_helper_float_rsqrt1_ps(fp0, cpu_env, fp0); + gen_helper_float_rsqrt1_ps(fp0, tcg_env, fp0); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_RSQRT2_PS: @@ -11199,10 +10646,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); - gen_helper_float_rsqrt2_ps(fp0, cpu_env, fp0, fp1); - tcg_temp_free_i64(fp1); + gen_helper_float_rsqrt2_ps(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_CVT_S_PU: @@ -11211,9 +10656,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); gen_load_fpr32h(ctx, fp0, fs); - gen_helper_float_cvts_pu(fp0, cpu_env, fp0); + gen_helper_float_cvts_pu(fp0, tcg_env, fp0); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_CVT_PW_PS: @@ -11222,9 +10666,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); - gen_helper_float_cvtpw_ps(fp0, cpu_env, fp0); + gen_helper_float_cvtpw_ps(fp0, tcg_env, fp0); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_CVT_S_PL: @@ -11233,9 +10676,8 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); - gen_helper_float_cvts_pl(fp0, cpu_env, fp0); + gen_helper_float_cvts_pl(fp0, tcg_env, fp0); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_PLL_PS: @@ -11248,8 +10690,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp1, ft); gen_store_fpr32h(ctx, fp0, fd); gen_store_fpr32(ctx, fp1, fd); - tcg_temp_free_i32(fp0); - tcg_temp_free_i32(fp1); } break; case OPC_PLU_PS: @@ -11262,8 +10702,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32h(ctx, fp1, ft); gen_store_fpr32(ctx, fp1, fd); gen_store_fpr32h(ctx, fp0, fd); - tcg_temp_free_i32(fp0); - tcg_temp_free_i32(fp1); } break; case OPC_PUL_PS: @@ -11276,8 +10714,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32(ctx, fp1, ft); gen_store_fpr32(ctx, fp1, fd); gen_store_fpr32h(ctx, fp0, fd); - tcg_temp_free_i32(fp0); - tcg_temp_free_i32(fp1); } break; case OPC_PUU_PS: @@ -11290,8 +10726,6 @@ static void gen_farith(DisasContext *ctx, enum fopcode op1, gen_load_fpr32h(ctx, fp1, ft); gen_store_fpr32(ctx, fp1, fd); gen_store_fpr32h(ctx, fp0, fd); - tcg_temp_free_i32(fp0); - tcg_temp_free_i32(fp1); } break; case OPC_CMP_F_PS: @@ -11349,7 +10783,6 @@ static void gen_flt3_ldst(DisasContext *ctx, uint32_t opc, tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL); tcg_gen_trunc_tl_i32(fp0, t0); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_LDXC1: @@ -11359,7 +10792,6 @@ static void gen_flt3_ldst(DisasContext *ctx, uint32_t opc, TCGv_i64 fp0 = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEUQ); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_LUXC1: @@ -11370,7 +10802,6 @@ static void gen_flt3_ldst(DisasContext *ctx, uint32_t opc, tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEUQ); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_SWXC1: @@ -11379,7 +10810,6 @@ static void gen_flt3_ldst(DisasContext *ctx, uint32_t opc, TCGv_i32 fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, fs); tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL); - tcg_temp_free_i32(fp0); } break; case OPC_SDXC1: @@ -11389,7 +10819,6 @@ static void gen_flt3_ldst(DisasContext *ctx, uint32_t opc, TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEUQ); - tcg_temp_free_i64(fp0); } break; case OPC_SUXC1: @@ -11399,11 +10828,9 @@ static void gen_flt3_ldst(DisasContext *ctx, uint32_t opc, TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, fs); tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEUQ); - tcg_temp_free_i64(fp0); } break; } - tcg_temp_free(t0); } static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, @@ -11413,7 +10840,7 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, case OPC_ALNV_PS: check_ps(ctx); { - TCGv t0 = tcg_temp_local_new(); + TCGv t0 = tcg_temp_new(); TCGv_i32 fp = tcg_temp_new_i32(); TCGv_i32 fph = tcg_temp_new_i32(); TCGLabel *l1 = gen_new_label(); @@ -11430,7 +10857,6 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, tcg_gen_br(l2); gen_set_label(l1); tcg_gen_brcondi_tl(TCG_COND_NE, t0, 4, l2); - tcg_temp_free(t0); if (cpu_is_bigendian(ctx)) { gen_load_fpr32(ctx, fp, fs); gen_load_fpr32h(ctx, fph, ft); @@ -11443,8 +10869,6 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, gen_store_fpr32h(ctx, fp, fd); } gen_set_label(l2); - tcg_temp_free_i32(fp); - tcg_temp_free_i32(fph); } break; case OPC_MADD_S: @@ -11457,11 +10881,8 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); gen_load_fpr32(ctx, fp2, fr); - gen_helper_float_madd_s(fp2, cpu_env, fp0, fp1, fp2); - tcg_temp_free_i32(fp0); - tcg_temp_free_i32(fp1); + gen_helper_float_madd_s(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr32(ctx, fp2, fd); - tcg_temp_free_i32(fp2); } break; case OPC_MADD_D: @@ -11475,11 +10896,8 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); gen_load_fpr64(ctx, fp2, fr); - gen_helper_float_madd_d(fp2, cpu_env, fp0, fp1, fp2); - tcg_temp_free_i64(fp0); - tcg_temp_free_i64(fp1); + gen_helper_float_madd_d(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); - tcg_temp_free_i64(fp2); } break; case OPC_MADD_PS: @@ -11492,11 +10910,8 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); gen_load_fpr64(ctx, fp2, fr); - gen_helper_float_madd_ps(fp2, cpu_env, fp0, fp1, fp2); - tcg_temp_free_i64(fp0); - tcg_temp_free_i64(fp1); + gen_helper_float_madd_ps(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); - tcg_temp_free_i64(fp2); } break; case OPC_MSUB_S: @@ -11509,11 +10924,8 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); gen_load_fpr32(ctx, fp2, fr); - gen_helper_float_msub_s(fp2, cpu_env, fp0, fp1, fp2); - tcg_temp_free_i32(fp0); - tcg_temp_free_i32(fp1); + gen_helper_float_msub_s(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr32(ctx, fp2, fd); - tcg_temp_free_i32(fp2); } break; case OPC_MSUB_D: @@ -11527,11 +10939,8 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); gen_load_fpr64(ctx, fp2, fr); - gen_helper_float_msub_d(fp2, cpu_env, fp0, fp1, fp2); - tcg_temp_free_i64(fp0); - tcg_temp_free_i64(fp1); + gen_helper_float_msub_d(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); - tcg_temp_free_i64(fp2); } break; case OPC_MSUB_PS: @@ -11544,11 +10953,8 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); gen_load_fpr64(ctx, fp2, fr); - gen_helper_float_msub_ps(fp2, cpu_env, fp0, fp1, fp2); - tcg_temp_free_i64(fp0); - tcg_temp_free_i64(fp1); + gen_helper_float_msub_ps(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); - tcg_temp_free_i64(fp2); } break; case OPC_NMADD_S: @@ -11561,11 +10967,8 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); gen_load_fpr32(ctx, fp2, fr); - gen_helper_float_nmadd_s(fp2, cpu_env, fp0, fp1, fp2); - tcg_temp_free_i32(fp0); - tcg_temp_free_i32(fp1); + gen_helper_float_nmadd_s(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr32(ctx, fp2, fd); - tcg_temp_free_i32(fp2); } break; case OPC_NMADD_D: @@ -11579,11 +10982,8 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); gen_load_fpr64(ctx, fp2, fr); - gen_helper_float_nmadd_d(fp2, cpu_env, fp0, fp1, fp2); - tcg_temp_free_i64(fp0); - tcg_temp_free_i64(fp1); + gen_helper_float_nmadd_d(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); - tcg_temp_free_i64(fp2); } break; case OPC_NMADD_PS: @@ -11596,11 +10996,8 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); gen_load_fpr64(ctx, fp2, fr); - gen_helper_float_nmadd_ps(fp2, cpu_env, fp0, fp1, fp2); - tcg_temp_free_i64(fp0); - tcg_temp_free_i64(fp1); + gen_helper_float_nmadd_ps(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); - tcg_temp_free_i64(fp2); } break; case OPC_NMSUB_S: @@ -11613,11 +11010,8 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32(ctx, fp1, ft); gen_load_fpr32(ctx, fp2, fr); - gen_helper_float_nmsub_s(fp2, cpu_env, fp0, fp1, fp2); - tcg_temp_free_i32(fp0); - tcg_temp_free_i32(fp1); + gen_helper_float_nmsub_s(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr32(ctx, fp2, fd); - tcg_temp_free_i32(fp2); } break; case OPC_NMSUB_D: @@ -11631,11 +11025,8 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); gen_load_fpr64(ctx, fp2, fr); - gen_helper_float_nmsub_d(fp2, cpu_env, fp0, fp1, fp2); - tcg_temp_free_i64(fp0); - tcg_temp_free_i64(fp1); + gen_helper_float_nmsub_d(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); - tcg_temp_free_i64(fp2); } break; case OPC_NMSUB_PS: @@ -11648,11 +11039,8 @@ static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, gen_load_fpr64(ctx, fp0, fs); gen_load_fpr64(ctx, fp1, ft); gen_load_fpr64(ctx, fp2, fr); - gen_helper_float_nmsub_ps(fp2, cpu_env, fp0, fp1, fp2); - tcg_temp_free_i64(fp0); - tcg_temp_free_i64(fp1); + gen_helper_float_nmsub_ps(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); - tcg_temp_free_i64(fp2); } break; default: @@ -11677,18 +11065,16 @@ void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel) switch (rd) { case 0: - gen_helper_rdhwr_cpunum(t0, cpu_env); + gen_helper_rdhwr_cpunum(t0, tcg_env); gen_store_gpr(t0, rt); break; case 1: - gen_helper_rdhwr_synci_step(t0, cpu_env); + gen_helper_rdhwr_synci_step(t0, tcg_env); gen_store_gpr(t0, rt); break; case 2: - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_rdhwr_cc(t0, cpu_env); + translator_io_start(&ctx->base); + gen_helper_rdhwr_cc(t0, tcg_env); gen_store_gpr(t0, rt); /* * Break the TB to be able to take timer interrupts immediately @@ -11699,7 +11085,7 @@ void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel) ctx->base.is_jmp = DISAS_EXIT; break; case 3: - gen_helper_rdhwr_ccres(t0, cpu_env); + gen_helper_rdhwr_ccres(t0, tcg_env); gen_store_gpr(t0, rt); break; case 4: @@ -11711,24 +11097,24 @@ void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel) */ generate_exception(ctx, EXCP_RI); } - gen_helper_rdhwr_performance(t0, cpu_env); + gen_helper_rdhwr_performance(t0, tcg_env); gen_store_gpr(t0, rt); break; case 5: check_insn(ctx, ISA_MIPS_R6); - gen_helper_rdhwr_xnp(t0, cpu_env); + gen_helper_rdhwr_xnp(t0, tcg_env); gen_store_gpr(t0, rt); break; case 29: #if defined(CONFIG_USER_ONLY) - tcg_gen_ld_tl(t0, cpu_env, + tcg_gen_ld_tl(t0, tcg_env, offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); gen_store_gpr(t0, rt); break; #else if ((ctx->hflags & MIPS_HFLAG_CP0) || (ctx->hflags & MIPS_HFLAG_HWRENA_ULR)) { - tcg_gen_ld_tl(t0, cpu_env, + tcg_gen_ld_tl(t0, tcg_env, offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); gen_store_gpr(t0, rt); } else { @@ -11741,7 +11127,6 @@ void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel) gen_reserved_instruction(ctx); break; } - tcg_temp_free(t0); } static inline void clear_branch_hflags(DisasContext *ctx) @@ -11765,7 +11150,6 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) /* Branches completion */ clear_branch_hflags(ctx); ctx->base.is_jmp = DISAS_NORETURN; - /* FIXME: Need to clear can_do_io. */ switch (proc_hflags & MIPS_HFLAG_BMASK_BASE) { case MIPS_HFLAG_FBNSLOT: gen_goto_tb(ctx, 0, ctx->base.pc_next + insn_bytes); @@ -11800,11 +11184,9 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) tcg_gen_andi_tl(t0, btarget, 0x1); tcg_gen_trunc_tl_i32(t1, t0); - tcg_temp_free(t0); tcg_gen_andi_i32(hflags, hflags, ~(uint32_t)MIPS_HFLAG_M16); tcg_gen_shli_i32(t1, t1, MIPS_HFLAG_M16_SHIFT); tcg_gen_or_i32(hflags, hflags, t1); - tcg_temp_free_i32(t1); tcg_gen_andi_tl(cpu_PC, btarget, ~(target_ulong)0x1); } else { @@ -11830,11 +11212,11 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, if (ctx->hflags & MIPS_HFLAG_BMASK) { #ifdef MIPS_DEBUG_DISAS - LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx - "\n", ctx->base.pc_next); + LOG_DISAS("Branch in delay / forbidden slot at PC 0x%016" + VADDR_PRIx "\n", ctx->base.pc_next); #endif gen_reserved_instruction(ctx); - goto out; + return; } /* Load needed operands and calculate btarget */ @@ -11888,13 +11270,12 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, gen_load_gpr(tbase, rt); gen_op_addr_add(ctx, btarget, tbase, toffset); - tcg_temp_free(tbase); } break; default: MIPS_INVAL("Compact branch/jump"); gen_reserved_instruction(ctx); - goto out; + return; } if (bcond_compute == 0) { @@ -11915,7 +11296,7 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, default: MIPS_INVAL("Compact branch/jump"); gen_reserved_instruction(ctx); - goto out; + return; } /* Generating branch here as compact branches don't have delay slot */ @@ -12005,10 +11386,6 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, /* OPC_BNVC */ tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t4, 0, fs); } - tcg_temp_free(input_overflow); - tcg_temp_free(t4); - tcg_temp_free(t3); - tcg_temp_free(t2); } else if (rs < rt && rs == 0) { /* OPC_BEQZALC, OPC_BNEZALC */ if (opc == OPC_BEQZALC) { @@ -12038,7 +11415,7 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, default: MIPS_INVAL("Compact conditional branch/jump"); gen_reserved_instruction(ctx); - goto out; + return; } /* Generating branch here as compact branches don't have delay slot */ @@ -12047,10 +11424,6 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, ctx->hflags |= MIPS_HFLAG_FBNSLOT; } - -out: - tcg_temp_free(t0); - tcg_temp_free(t1); } void gen_addiupc(DisasContext *ctx, int rx, int imm, @@ -12070,38 +11443,27 @@ void gen_addiupc(DisasContext *ctx, int rx, int imm, if (!is_64_bit) { tcg_gen_ext32s_tl(cpu_gpr[rx], cpu_gpr[rx]); } - - tcg_temp_free(t0); } static void gen_cache_operation(DisasContext *ctx, uint32_t op, int base, int16_t offset) { - TCGv_i32 t0 = tcg_const_i32(op); + TCGv_i32 t0 = tcg_constant_i32(op); TCGv t1 = tcg_temp_new(); gen_base_offset_addr(ctx, t1, base, offset); - gen_helper_cache(cpu_env, t1, t0); - tcg_temp_free(t1); - tcg_temp_free_i32(t0); + gen_helper_cache(tcg_env, t1, t0); } -static inline bool is_uhi(int sdbbp_code) +static inline bool is_uhi(DisasContext *ctx, int sdbbp_code) { #ifdef CONFIG_USER_ONLY return false; #else - return semihosting_enabled() && sdbbp_code == 1; + bool is_user = (ctx->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_UM; + return semihosting_enabled(is_user) && sdbbp_code == 1; #endif } -#ifdef CONFIG_USER_ONLY -/* The above should dead-code away any calls to this..*/ -static inline void gen_helper_do_semihosting(void *env) -{ - g_assert_not_reached(); -} -#endif - void gen_ldxs(DisasContext *ctx, int base, int index, int rd) { TCGv t0 = tcg_temp_new(); @@ -12117,9 +11479,6 @@ void gen_ldxs(DisasContext *ctx, int base, int index, int rd) tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL); gen_store_gpr(t1, rd); - - tcg_temp_free(t0); - tcg_temp_free(t1); } static void gen_sync(int stype) @@ -12183,12 +11542,16 @@ enum { #include "nanomips_translate.c.inc" /* MIPSDSP functions. */ -static void gen_mipsdsp_ld(DisasContext *ctx, uint32_t opc, - int rd, int base, int offset) + +/* Indexed load is not for DSP only */ +static void gen_mips_lx(DisasContext *ctx, uint32_t opc, + int rd, int base, int offset) { TCGv t0; - check_dsp(ctx); + if (!(ctx->insn_flags & INSN_OCTEON)) { + check_dsp(ctx); + } t0 = tcg_temp_new(); if (base == 0) { @@ -12219,7 +11582,6 @@ static void gen_mipsdsp_ld(DisasContext *ctx, uint32_t opc, break; #endif } - tcg_temp_free(t0); } static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, @@ -12286,15 +11648,15 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, switch (op2) { case OPC_ABSQ_S_QB: check_dsp_r2(ctx); - gen_helper_absq_s_qb(cpu_gpr[ret], v2_t, cpu_env); + gen_helper_absq_s_qb(cpu_gpr[ret], v2_t, tcg_env); break; case OPC_ABSQ_S_PH: check_dsp(ctx); - gen_helper_absq_s_ph(cpu_gpr[ret], v2_t, cpu_env); + gen_helper_absq_s_ph(cpu_gpr[ret], v2_t, tcg_env); break; case OPC_ABSQ_S_W: check_dsp(ctx); - gen_helper_absq_s_w(cpu_gpr[ret], v2_t, cpu_env); + gen_helper_absq_s_w(cpu_gpr[ret], v2_t, tcg_env); break; case OPC_PRECEQ_W_PHL: check_dsp(ctx); @@ -12345,67 +11707,67 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, switch (op2) { case OPC_ADDQ_PH: check_dsp(ctx); - gen_helper_addq_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addq_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDQ_S_PH: check_dsp(ctx); - gen_helper_addq_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addq_s_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDQ_S_W: check_dsp(ctx); - gen_helper_addq_s_w(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addq_s_w(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDU_QB: check_dsp(ctx); - gen_helper_addu_qb(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addu_qb(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDU_S_QB: check_dsp(ctx); - gen_helper_addu_s_qb(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addu_s_qb(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDU_PH: check_dsp_r2(ctx); - gen_helper_addu_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addu_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDU_S_PH: check_dsp_r2(ctx); - gen_helper_addu_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addu_s_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBQ_PH: check_dsp(ctx); - gen_helper_subq_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subq_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBQ_S_PH: check_dsp(ctx); - gen_helper_subq_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subq_s_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBQ_S_W: check_dsp(ctx); - gen_helper_subq_s_w(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subq_s_w(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBU_QB: check_dsp(ctx); - gen_helper_subu_qb(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subu_qb(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBU_S_QB: check_dsp(ctx); - gen_helper_subu_s_qb(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subu_s_qb(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBU_PH: check_dsp_r2(ctx); - gen_helper_subu_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subu_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBU_S_PH: check_dsp_r2(ctx); - gen_helper_subu_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subu_s_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDSC: check_dsp(ctx); - gen_helper_addsc(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addsc(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDWC: check_dsp(ctx); - gen_helper_addwc(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addwc(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_MODSUB: check_dsp(ctx); @@ -12430,19 +11792,17 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, case OPC_PRECR_SRA_PH_W: check_dsp_r2(ctx); { - TCGv_i32 sa_t = tcg_const_i32(v2); + TCGv_i32 sa_t = tcg_constant_i32(v2); gen_helper_precr_sra_ph_w(cpu_gpr[ret], sa_t, v1_t, cpu_gpr[ret]); - tcg_temp_free_i32(sa_t); break; } case OPC_PRECR_SRA_R_PH_W: check_dsp_r2(ctx); { - TCGv_i32 sa_t = tcg_const_i32(v2); + TCGv_i32 sa_t = tcg_constant_i32(v2); gen_helper_precr_sra_r_ph_w(cpu_gpr[ret], sa_t, v1_t, cpu_gpr[ret]); - tcg_temp_free_i32(sa_t); break; } case OPC_PRECRQ_PH_W: @@ -12451,11 +11811,11 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, break; case OPC_PRECRQ_RS_PH_W: check_dsp(ctx); - gen_helper_precrq_rs_ph_w(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_precrq_rs_ph_w(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_PRECRQU_S_QB_PH: check_dsp(ctx); - gen_helper_precrqu_s_qb_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_precrqu_s_qb_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; } break; @@ -12520,15 +11880,15 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, break; case OPC_ABSQ_S_OB: check_dsp_r2(ctx); - gen_helper_absq_s_ob(cpu_gpr[ret], v2_t, cpu_env); + gen_helper_absq_s_ob(cpu_gpr[ret], v2_t, tcg_env); break; case OPC_ABSQ_S_PW: check_dsp(ctx); - gen_helper_absq_s_pw(cpu_gpr[ret], v2_t, cpu_env); + gen_helper_absq_s_pw(cpu_gpr[ret], v2_t, tcg_env); break; case OPC_ABSQ_S_QH: check_dsp(ctx); - gen_helper_absq_s_qh(cpu_gpr[ret], v2_t, cpu_env); + gen_helper_absq_s_qh(cpu_gpr[ret], v2_t, tcg_env); break; } break; @@ -12540,35 +11900,35 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, break; case OPC_SUBQ_PW: check_dsp(ctx); - gen_helper_subq_pw(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subq_pw(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBQ_S_PW: check_dsp(ctx); - gen_helper_subq_s_pw(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subq_s_pw(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBQ_QH: check_dsp(ctx); - gen_helper_subq_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subq_qh(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBQ_S_QH: check_dsp(ctx); - gen_helper_subq_s_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subq_s_qh(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBU_OB: check_dsp(ctx); - gen_helper_subu_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subu_ob(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBU_S_OB: check_dsp(ctx); - gen_helper_subu_s_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subu_s_ob(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBU_QH: check_dsp_r2(ctx); - gen_helper_subu_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subu_qh(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBU_S_QH: check_dsp_r2(ctx); - gen_helper_subu_s_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_subu_s_qh(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SUBUH_OB: check_dsp_r2(ctx); @@ -12580,35 +11940,35 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, break; case OPC_ADDQ_PW: check_dsp(ctx); - gen_helper_addq_pw(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addq_pw(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDQ_S_PW: check_dsp(ctx); - gen_helper_addq_s_pw(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addq_s_pw(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDQ_QH: check_dsp(ctx); - gen_helper_addq_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addq_qh(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDQ_S_QH: check_dsp(ctx); - gen_helper_addq_s_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addq_s_qh(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDU_OB: check_dsp(ctx); - gen_helper_addu_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addu_ob(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDU_S_OB: check_dsp(ctx); - gen_helper_addu_s_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addu_s_ob(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDU_QH: check_dsp_r2(ctx); - gen_helper_addu_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addu_qh(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDU_S_QH: check_dsp_r2(ctx); - gen_helper_addu_s_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_addu_s_qh(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_ADDUH_OB: check_dsp_r2(ctx); @@ -12629,17 +11989,15 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, case OPC_PRECR_SRA_QH_PW: check_dsp_r2(ctx); { - TCGv_i32 ret_t = tcg_const_i32(ret); + TCGv_i32 ret_t = tcg_constant_i32(ret); gen_helper_precr_sra_qh_pw(v2_t, v1_t, v2_t, ret_t); - tcg_temp_free_i32(ret_t); break; } case OPC_PRECR_SRA_R_QH_PW: check_dsp_r2(ctx); { - TCGv_i32 sa_v = tcg_const_i32(ret); + TCGv_i32 sa_v = tcg_constant_i32(ret); gen_helper_precr_sra_r_qh_pw(v2_t, v1_t, v2_t, sa_v); - tcg_temp_free_i32(sa_v); break; } case OPC_PRECRQ_OB_QH: @@ -12656,19 +12014,16 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, break; case OPC_PRECRQ_RS_QH_PW: check_dsp(ctx); - gen_helper_precrq_rs_qh_pw(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_precrq_rs_qh_pw(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_PRECRQU_S_OB_QH: check_dsp(ctx); - gen_helper_precrqu_s_ob_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_precrqu_s_ob_qh(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; } break; #endif } - - tcg_temp_free(v1_t); - tcg_temp_free(v2_t); } static void gen_mipsdsp_shift(DisasContext *ctx, uint32_t opc, @@ -12699,35 +12054,35 @@ static void gen_mipsdsp_shift(DisasContext *ctx, uint32_t opc, switch (op2) { case OPC_SHLL_QB: check_dsp(ctx); - gen_helper_shll_qb(cpu_gpr[ret], t0, v2_t, cpu_env); + gen_helper_shll_qb(cpu_gpr[ret], t0, v2_t, tcg_env); break; case OPC_SHLLV_QB: check_dsp(ctx); - gen_helper_shll_qb(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_shll_qb(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SHLL_PH: check_dsp(ctx); - gen_helper_shll_ph(cpu_gpr[ret], t0, v2_t, cpu_env); + gen_helper_shll_ph(cpu_gpr[ret], t0, v2_t, tcg_env); break; case OPC_SHLLV_PH: check_dsp(ctx); - gen_helper_shll_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_shll_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SHLL_S_PH: check_dsp(ctx); - gen_helper_shll_s_ph(cpu_gpr[ret], t0, v2_t, cpu_env); + gen_helper_shll_s_ph(cpu_gpr[ret], t0, v2_t, tcg_env); break; case OPC_SHLLV_S_PH: check_dsp(ctx); - gen_helper_shll_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_shll_s_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SHLL_S_W: check_dsp(ctx); - gen_helper_shll_s_w(cpu_gpr[ret], t0, v2_t, cpu_env); + gen_helper_shll_s_w(cpu_gpr[ret], t0, v2_t, tcg_env); break; case OPC_SHLLV_S_W: check_dsp(ctx); - gen_helper_shll_s_w(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_shll_s_w(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_SHRL_QB: check_dsp(ctx); @@ -12798,43 +12153,43 @@ static void gen_mipsdsp_shift(DisasContext *ctx, uint32_t opc, switch (op2) { case OPC_SHLL_PW: check_dsp(ctx); - gen_helper_shll_pw(cpu_gpr[ret], v2_t, t0, cpu_env); + gen_helper_shll_pw(cpu_gpr[ret], v2_t, t0, tcg_env); break; case OPC_SHLLV_PW: check_dsp(ctx); - gen_helper_shll_pw(cpu_gpr[ret], v2_t, v1_t, cpu_env); + gen_helper_shll_pw(cpu_gpr[ret], v2_t, v1_t, tcg_env); break; case OPC_SHLL_S_PW: check_dsp(ctx); - gen_helper_shll_s_pw(cpu_gpr[ret], v2_t, t0, cpu_env); + gen_helper_shll_s_pw(cpu_gpr[ret], v2_t, t0, tcg_env); break; case OPC_SHLLV_S_PW: check_dsp(ctx); - gen_helper_shll_s_pw(cpu_gpr[ret], v2_t, v1_t, cpu_env); + gen_helper_shll_s_pw(cpu_gpr[ret], v2_t, v1_t, tcg_env); break; case OPC_SHLL_OB: check_dsp(ctx); - gen_helper_shll_ob(cpu_gpr[ret], v2_t, t0, cpu_env); + gen_helper_shll_ob(cpu_gpr[ret], v2_t, t0, tcg_env); break; case OPC_SHLLV_OB: check_dsp(ctx); - gen_helper_shll_ob(cpu_gpr[ret], v2_t, v1_t, cpu_env); + gen_helper_shll_ob(cpu_gpr[ret], v2_t, v1_t, tcg_env); break; case OPC_SHLL_QH: check_dsp(ctx); - gen_helper_shll_qh(cpu_gpr[ret], v2_t, t0, cpu_env); + gen_helper_shll_qh(cpu_gpr[ret], v2_t, t0, tcg_env); break; case OPC_SHLLV_QH: check_dsp(ctx); - gen_helper_shll_qh(cpu_gpr[ret], v2_t, v1_t, cpu_env); + gen_helper_shll_qh(cpu_gpr[ret], v2_t, v1_t, tcg_env); break; case OPC_SHLL_S_QH: check_dsp(ctx); - gen_helper_shll_s_qh(cpu_gpr[ret], v2_t, t0, cpu_env); + gen_helper_shll_s_qh(cpu_gpr[ret], v2_t, t0, tcg_env); break; case OPC_SHLLV_S_QH: check_dsp(ctx); - gen_helper_shll_s_qh(cpu_gpr[ret], v2_t, v1_t, cpu_env); + gen_helper_shll_s_qh(cpu_gpr[ret], v2_t, v1_t, tcg_env); break; case OPC_SHRA_OB: check_dsp_r2(ctx); @@ -12908,10 +12263,6 @@ static void gen_mipsdsp_shift(DisasContext *ctx, uint32_t opc, break; #endif } - - tcg_temp_free(t0); - tcg_temp_free(v1_t); - tcg_temp_free(v2_t); } static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, @@ -12943,16 +12294,16 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, check_dsp_r2(ctx); switch (op2) { case OPC_MUL_PH: - gen_helper_mul_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_mul_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_MUL_S_PH: - gen_helper_mul_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_mul_s_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_MULQ_S_W: - gen_helper_mulq_s_w(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_mulq_s_w(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_MULQ_RS_W: - gen_helper_mulq_rs_w(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_mulq_rs_w(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; } break; @@ -12960,91 +12311,91 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, switch (op2) { case OPC_DPAU_H_QBL: check_dsp(ctx); - gen_helper_dpau_h_qbl(t0, v1_t, v2_t, cpu_env); + gen_helper_dpau_h_qbl(t0, v1_t, v2_t, tcg_env); break; case OPC_DPAU_H_QBR: check_dsp(ctx); - gen_helper_dpau_h_qbr(t0, v1_t, v2_t, cpu_env); + gen_helper_dpau_h_qbr(t0, v1_t, v2_t, tcg_env); break; case OPC_DPSU_H_QBL: check_dsp(ctx); - gen_helper_dpsu_h_qbl(t0, v1_t, v2_t, cpu_env); + gen_helper_dpsu_h_qbl(t0, v1_t, v2_t, tcg_env); break; case OPC_DPSU_H_QBR: check_dsp(ctx); - gen_helper_dpsu_h_qbr(t0, v1_t, v2_t, cpu_env); + gen_helper_dpsu_h_qbr(t0, v1_t, v2_t, tcg_env); break; case OPC_DPA_W_PH: check_dsp_r2(ctx); - gen_helper_dpa_w_ph(t0, v1_t, v2_t, cpu_env); + gen_helper_dpa_w_ph(t0, v1_t, v2_t, tcg_env); break; case OPC_DPAX_W_PH: check_dsp_r2(ctx); - gen_helper_dpax_w_ph(t0, v1_t, v2_t, cpu_env); + gen_helper_dpax_w_ph(t0, v1_t, v2_t, tcg_env); break; case OPC_DPAQ_S_W_PH: check_dsp(ctx); - gen_helper_dpaq_s_w_ph(t0, v1_t, v2_t, cpu_env); + gen_helper_dpaq_s_w_ph(t0, v1_t, v2_t, tcg_env); break; case OPC_DPAQX_S_W_PH: check_dsp_r2(ctx); - gen_helper_dpaqx_s_w_ph(t0, v1_t, v2_t, cpu_env); + gen_helper_dpaqx_s_w_ph(t0, v1_t, v2_t, tcg_env); break; case OPC_DPAQX_SA_W_PH: check_dsp_r2(ctx); - gen_helper_dpaqx_sa_w_ph(t0, v1_t, v2_t, cpu_env); + gen_helper_dpaqx_sa_w_ph(t0, v1_t, v2_t, tcg_env); break; case OPC_DPS_W_PH: check_dsp_r2(ctx); - gen_helper_dps_w_ph(t0, v1_t, v2_t, cpu_env); + gen_helper_dps_w_ph(t0, v1_t, v2_t, tcg_env); break; case OPC_DPSX_W_PH: check_dsp_r2(ctx); - gen_helper_dpsx_w_ph(t0, v1_t, v2_t, cpu_env); + gen_helper_dpsx_w_ph(t0, v1_t, v2_t, tcg_env); break; case OPC_DPSQ_S_W_PH: check_dsp(ctx); - gen_helper_dpsq_s_w_ph(t0, v1_t, v2_t, cpu_env); + gen_helper_dpsq_s_w_ph(t0, v1_t, v2_t, tcg_env); break; case OPC_DPSQX_S_W_PH: check_dsp_r2(ctx); - gen_helper_dpsqx_s_w_ph(t0, v1_t, v2_t, cpu_env); + gen_helper_dpsqx_s_w_ph(t0, v1_t, v2_t, tcg_env); break; case OPC_DPSQX_SA_W_PH: check_dsp_r2(ctx); - gen_helper_dpsqx_sa_w_ph(t0, v1_t, v2_t, cpu_env); + gen_helper_dpsqx_sa_w_ph(t0, v1_t, v2_t, tcg_env); break; case OPC_MULSAQ_S_W_PH: check_dsp(ctx); - gen_helper_mulsaq_s_w_ph(t0, v1_t, v2_t, cpu_env); + gen_helper_mulsaq_s_w_ph(t0, v1_t, v2_t, tcg_env); break; case OPC_DPAQ_SA_L_W: check_dsp(ctx); - gen_helper_dpaq_sa_l_w(t0, v1_t, v2_t, cpu_env); + gen_helper_dpaq_sa_l_w(t0, v1_t, v2_t, tcg_env); break; case OPC_DPSQ_SA_L_W: check_dsp(ctx); - gen_helper_dpsq_sa_l_w(t0, v1_t, v2_t, cpu_env); + gen_helper_dpsq_sa_l_w(t0, v1_t, v2_t, tcg_env); break; case OPC_MAQ_S_W_PHL: check_dsp(ctx); - gen_helper_maq_s_w_phl(t0, v1_t, v2_t, cpu_env); + gen_helper_maq_s_w_phl(t0, v1_t, v2_t, tcg_env); break; case OPC_MAQ_S_W_PHR: check_dsp(ctx); - gen_helper_maq_s_w_phr(t0, v1_t, v2_t, cpu_env); + gen_helper_maq_s_w_phr(t0, v1_t, v2_t, tcg_env); break; case OPC_MAQ_SA_W_PHL: check_dsp(ctx); - gen_helper_maq_sa_w_phl(t0, v1_t, v2_t, cpu_env); + gen_helper_maq_sa_w_phl(t0, v1_t, v2_t, tcg_env); break; case OPC_MAQ_SA_W_PHR: check_dsp(ctx); - gen_helper_maq_sa_w_phr(t0, v1_t, v2_t, cpu_env); + gen_helper_maq_sa_w_phr(t0, v1_t, v2_t, tcg_env); break; case OPC_MULSA_W_PH: check_dsp_r2(ctx); - gen_helper_mulsa_w_ph(t0, v1_t, v2_t, cpu_env); + gen_helper_mulsa_w_ph(t0, v1_t, v2_t, tcg_env); break; } break; @@ -13057,107 +12408,107 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, switch (op2) { case OPC_DMADD: check_dsp(ctx); - gen_helper_dmadd(v1_t, v2_t, t0, cpu_env); + gen_helper_dmadd(v1_t, v2_t, t0, tcg_env); break; case OPC_DMADDU: check_dsp(ctx); - gen_helper_dmaddu(v1_t, v2_t, t0, cpu_env); + gen_helper_dmaddu(v1_t, v2_t, t0, tcg_env); break; case OPC_DMSUB: check_dsp(ctx); - gen_helper_dmsub(v1_t, v2_t, t0, cpu_env); + gen_helper_dmsub(v1_t, v2_t, t0, tcg_env); break; case OPC_DMSUBU: check_dsp(ctx); - gen_helper_dmsubu(v1_t, v2_t, t0, cpu_env); + gen_helper_dmsubu(v1_t, v2_t, t0, tcg_env); break; case OPC_DPA_W_QH: check_dsp_r2(ctx); - gen_helper_dpa_w_qh(v1_t, v2_t, t0, cpu_env); + gen_helper_dpa_w_qh(v1_t, v2_t, t0, tcg_env); break; case OPC_DPAQ_S_W_QH: check_dsp(ctx); - gen_helper_dpaq_s_w_qh(v1_t, v2_t, t0, cpu_env); + gen_helper_dpaq_s_w_qh(v1_t, v2_t, t0, tcg_env); break; case OPC_DPAQ_SA_L_PW: check_dsp(ctx); - gen_helper_dpaq_sa_l_pw(v1_t, v2_t, t0, cpu_env); + gen_helper_dpaq_sa_l_pw(v1_t, v2_t, t0, tcg_env); break; case OPC_DPAU_H_OBL: check_dsp(ctx); - gen_helper_dpau_h_obl(v1_t, v2_t, t0, cpu_env); + gen_helper_dpau_h_obl(v1_t, v2_t, t0, tcg_env); break; case OPC_DPAU_H_OBR: check_dsp(ctx); - gen_helper_dpau_h_obr(v1_t, v2_t, t0, cpu_env); + gen_helper_dpau_h_obr(v1_t, v2_t, t0, tcg_env); break; case OPC_DPS_W_QH: check_dsp_r2(ctx); - gen_helper_dps_w_qh(v1_t, v2_t, t0, cpu_env); + gen_helper_dps_w_qh(v1_t, v2_t, t0, tcg_env); break; case OPC_DPSQ_S_W_QH: check_dsp(ctx); - gen_helper_dpsq_s_w_qh(v1_t, v2_t, t0, cpu_env); + gen_helper_dpsq_s_w_qh(v1_t, v2_t, t0, tcg_env); break; case OPC_DPSQ_SA_L_PW: check_dsp(ctx); - gen_helper_dpsq_sa_l_pw(v1_t, v2_t, t0, cpu_env); + gen_helper_dpsq_sa_l_pw(v1_t, v2_t, t0, tcg_env); break; case OPC_DPSU_H_OBL: check_dsp(ctx); - gen_helper_dpsu_h_obl(v1_t, v2_t, t0, cpu_env); + gen_helper_dpsu_h_obl(v1_t, v2_t, t0, tcg_env); break; case OPC_DPSU_H_OBR: check_dsp(ctx); - gen_helper_dpsu_h_obr(v1_t, v2_t, t0, cpu_env); + gen_helper_dpsu_h_obr(v1_t, v2_t, t0, tcg_env); break; case OPC_MAQ_S_L_PWL: check_dsp(ctx); - gen_helper_maq_s_l_pwl(v1_t, v2_t, t0, cpu_env); + gen_helper_maq_s_l_pwl(v1_t, v2_t, t0, tcg_env); break; case OPC_MAQ_S_L_PWR: check_dsp(ctx); - gen_helper_maq_s_l_pwr(v1_t, v2_t, t0, cpu_env); + gen_helper_maq_s_l_pwr(v1_t, v2_t, t0, tcg_env); break; case OPC_MAQ_S_W_QHLL: check_dsp(ctx); - gen_helper_maq_s_w_qhll(v1_t, v2_t, t0, cpu_env); + gen_helper_maq_s_w_qhll(v1_t, v2_t, t0, tcg_env); break; case OPC_MAQ_SA_W_QHLL: check_dsp(ctx); - gen_helper_maq_sa_w_qhll(v1_t, v2_t, t0, cpu_env); + gen_helper_maq_sa_w_qhll(v1_t, v2_t, t0, tcg_env); break; case OPC_MAQ_S_W_QHLR: check_dsp(ctx); - gen_helper_maq_s_w_qhlr(v1_t, v2_t, t0, cpu_env); + gen_helper_maq_s_w_qhlr(v1_t, v2_t, t0, tcg_env); break; case OPC_MAQ_SA_W_QHLR: check_dsp(ctx); - gen_helper_maq_sa_w_qhlr(v1_t, v2_t, t0, cpu_env); + gen_helper_maq_sa_w_qhlr(v1_t, v2_t, t0, tcg_env); break; case OPC_MAQ_S_W_QHRL: check_dsp(ctx); - gen_helper_maq_s_w_qhrl(v1_t, v2_t, t0, cpu_env); + gen_helper_maq_s_w_qhrl(v1_t, v2_t, t0, tcg_env); break; case OPC_MAQ_SA_W_QHRL: check_dsp(ctx); - gen_helper_maq_sa_w_qhrl(v1_t, v2_t, t0, cpu_env); + gen_helper_maq_sa_w_qhrl(v1_t, v2_t, t0, tcg_env); break; case OPC_MAQ_S_W_QHRR: check_dsp(ctx); - gen_helper_maq_s_w_qhrr(v1_t, v2_t, t0, cpu_env); + gen_helper_maq_s_w_qhrr(v1_t, v2_t, t0, tcg_env); break; case OPC_MAQ_SA_W_QHRR: check_dsp(ctx); - gen_helper_maq_sa_w_qhrr(v1_t, v2_t, t0, cpu_env); + gen_helper_maq_sa_w_qhrr(v1_t, v2_t, t0, tcg_env); break; case OPC_MULSAQ_S_L_PW: check_dsp(ctx); - gen_helper_mulsaq_s_l_pw(v1_t, v2_t, t0, cpu_env); + gen_helper_mulsaq_s_l_pw(v1_t, v2_t, t0, tcg_env); break; case OPC_MULSAQ_S_W_QH: check_dsp(ctx); - gen_helper_mulsaq_s_w_qh(v1_t, v2_t, t0, cpu_env); + gen_helper_mulsaq_s_w_qh(v1_t, v2_t, t0, tcg_env); break; } } @@ -13167,27 +12518,27 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, switch (op2) { case OPC_MULEU_S_PH_QBL: check_dsp(ctx); - gen_helper_muleu_s_ph_qbl(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_muleu_s_ph_qbl(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_MULEU_S_PH_QBR: check_dsp(ctx); - gen_helper_muleu_s_ph_qbr(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_muleu_s_ph_qbr(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_MULQ_RS_PH: check_dsp(ctx); - gen_helper_mulq_rs_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_mulq_rs_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_MULEQ_S_W_PHL: check_dsp(ctx); - gen_helper_muleq_s_w_phl(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_muleq_s_w_phl(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_MULEQ_S_W_PHR: check_dsp(ctx); - gen_helper_muleq_s_w_phr(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_muleq_s_w_phr(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_MULQ_S_PH: check_dsp_r2(ctx); - gen_helper_mulq_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_mulq_s_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; } break; @@ -13196,32 +12547,28 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, switch (op2) { case OPC_MULEQ_S_PW_QHL: check_dsp(ctx); - gen_helper_muleq_s_pw_qhl(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_muleq_s_pw_qhl(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_MULEQ_S_PW_QHR: check_dsp(ctx); - gen_helper_muleq_s_pw_qhr(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_muleq_s_pw_qhr(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_MULEU_S_QH_OBL: check_dsp(ctx); - gen_helper_muleu_s_qh_obl(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_muleu_s_qh_obl(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_MULEU_S_QH_OBR: check_dsp(ctx); - gen_helper_muleu_s_qh_obr(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_muleu_s_qh_obr(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_MULQ_RS_QH: check_dsp(ctx); - gen_helper_mulq_rs_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_mulq_rs_qh(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; } break; #endif } - - tcg_temp_free_i32(t0); - tcg_temp_free(v1_t); - tcg_temp_free(v2_t); } static void gen_mipsdsp_bitinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, @@ -13358,8 +12705,6 @@ static void gen_mipsdsp_bitinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, break; #endif } - tcg_temp_free(t0); - tcg_temp_free(val_t); } static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, @@ -13387,15 +12732,15 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, switch (op2) { case OPC_CMPU_EQ_QB: check_dsp(ctx); - gen_helper_cmpu_eq_qb(v1_t, v2_t, cpu_env); + gen_helper_cmpu_eq_qb(v1_t, v2_t, tcg_env); break; case OPC_CMPU_LT_QB: check_dsp(ctx); - gen_helper_cmpu_lt_qb(v1_t, v2_t, cpu_env); + gen_helper_cmpu_lt_qb(v1_t, v2_t, tcg_env); break; case OPC_CMPU_LE_QB: check_dsp(ctx); - gen_helper_cmpu_le_qb(v1_t, v2_t, cpu_env); + gen_helper_cmpu_le_qb(v1_t, v2_t, tcg_env); break; case OPC_CMPGU_EQ_QB: check_dsp(ctx); @@ -13435,23 +12780,23 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, break; case OPC_CMP_EQ_PH: check_dsp(ctx); - gen_helper_cmp_eq_ph(v1_t, v2_t, cpu_env); + gen_helper_cmp_eq_ph(v1_t, v2_t, tcg_env); break; case OPC_CMP_LT_PH: check_dsp(ctx); - gen_helper_cmp_lt_ph(v1_t, v2_t, cpu_env); + gen_helper_cmp_lt_ph(v1_t, v2_t, tcg_env); break; case OPC_CMP_LE_PH: check_dsp(ctx); - gen_helper_cmp_le_ph(v1_t, v2_t, cpu_env); + gen_helper_cmp_le_ph(v1_t, v2_t, tcg_env); break; case OPC_PICK_QB: check_dsp(ctx); - gen_helper_pick_qb(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_pick_qb(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_PICK_PH: check_dsp(ctx); - gen_helper_pick_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_pick_ph(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_PACKRL_PH: check_dsp(ctx); @@ -13464,39 +12809,39 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, switch (op2) { case OPC_CMP_EQ_PW: check_dsp(ctx); - gen_helper_cmp_eq_pw(v1_t, v2_t, cpu_env); + gen_helper_cmp_eq_pw(v1_t, v2_t, tcg_env); break; case OPC_CMP_LT_PW: check_dsp(ctx); - gen_helper_cmp_lt_pw(v1_t, v2_t, cpu_env); + gen_helper_cmp_lt_pw(v1_t, v2_t, tcg_env); break; case OPC_CMP_LE_PW: check_dsp(ctx); - gen_helper_cmp_le_pw(v1_t, v2_t, cpu_env); + gen_helper_cmp_le_pw(v1_t, v2_t, tcg_env); break; case OPC_CMP_EQ_QH: check_dsp(ctx); - gen_helper_cmp_eq_qh(v1_t, v2_t, cpu_env); + gen_helper_cmp_eq_qh(v1_t, v2_t, tcg_env); break; case OPC_CMP_LT_QH: check_dsp(ctx); - gen_helper_cmp_lt_qh(v1_t, v2_t, cpu_env); + gen_helper_cmp_lt_qh(v1_t, v2_t, tcg_env); break; case OPC_CMP_LE_QH: check_dsp(ctx); - gen_helper_cmp_le_qh(v1_t, v2_t, cpu_env); + gen_helper_cmp_le_qh(v1_t, v2_t, tcg_env); break; case OPC_CMPGDU_EQ_OB: check_dsp_r2(ctx); - gen_helper_cmpgdu_eq_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_cmpgdu_eq_ob(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_CMPGDU_LT_OB: check_dsp_r2(ctx); - gen_helper_cmpgdu_lt_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_cmpgdu_lt_ob(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_CMPGDU_LE_OB: check_dsp_r2(ctx); - gen_helper_cmpgdu_le_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_cmpgdu_le_ob(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_CMPGU_EQ_OB: check_dsp(ctx); @@ -13512,15 +12857,15 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, break; case OPC_CMPU_EQ_OB: check_dsp(ctx); - gen_helper_cmpu_eq_ob(v1_t, v2_t, cpu_env); + gen_helper_cmpu_eq_ob(v1_t, v2_t, tcg_env); break; case OPC_CMPU_LT_OB: check_dsp(ctx); - gen_helper_cmpu_lt_ob(v1_t, v2_t, cpu_env); + gen_helper_cmpu_lt_ob(v1_t, v2_t, tcg_env); break; case OPC_CMPU_LE_OB: check_dsp(ctx); - gen_helper_cmpu_le_ob(v1_t, v2_t, cpu_env); + gen_helper_cmpu_le_ob(v1_t, v2_t, tcg_env); break; case OPC_PACKRL_PW: check_dsp(ctx); @@ -13528,24 +12873,20 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, break; case OPC_PICK_OB: check_dsp(ctx); - gen_helper_pick_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_pick_ob(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_PICK_PW: check_dsp(ctx); - gen_helper_pick_pw(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_pick_pw(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; case OPC_PICK_QH: check_dsp(ctx); - gen_helper_pick_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); + gen_helper_pick_qh(cpu_gpr[ret], v1_t, v2_t, tcg_env); break; } break; #endif } - - tcg_temp_free(t1); - tcg_temp_free(v1_t); - tcg_temp_free(v2_t); } static void gen_mipsdsp_append(CPUMIPSState *env, DisasContext *ctx, @@ -13633,7 +12974,6 @@ static void gen_mipsdsp_append(CPUMIPSState *env, DisasContext *ctx, break; #endif } - tcg_temp_free(t0); } static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, @@ -13663,80 +13003,80 @@ static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, case OPC_EXTR_W: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_extr_w(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_extr_w(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_EXTR_R_W: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_extr_r_w(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_extr_r_w(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_EXTR_RS_W: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_extr_rs_w(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_extr_rs_w(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_EXTR_S_H: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_extr_s_h(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_extr_s_h(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_EXTRV_S_H: tcg_gen_movi_tl(t0, v2); - gen_helper_extr_s_h(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_extr_s_h(cpu_gpr[ret], t0, v1_t, tcg_env); break; case OPC_EXTRV_W: tcg_gen_movi_tl(t0, v2); - gen_helper_extr_w(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_extr_w(cpu_gpr[ret], t0, v1_t, tcg_env); break; case OPC_EXTRV_R_W: tcg_gen_movi_tl(t0, v2); - gen_helper_extr_r_w(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_extr_r_w(cpu_gpr[ret], t0, v1_t, tcg_env); break; case OPC_EXTRV_RS_W: tcg_gen_movi_tl(t0, v2); - gen_helper_extr_rs_w(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_extr_rs_w(cpu_gpr[ret], t0, v1_t, tcg_env); break; case OPC_EXTP: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_extp(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_extp(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_EXTPV: tcg_gen_movi_tl(t0, v2); - gen_helper_extp(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_extp(cpu_gpr[ret], t0, v1_t, tcg_env); break; case OPC_EXTPDP: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_extpdp(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_extpdp(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_EXTPDPV: tcg_gen_movi_tl(t0, v2); - gen_helper_extpdp(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_extpdp(cpu_gpr[ret], t0, v1_t, tcg_env); break; case OPC_SHILO: imm = (ctx->opcode >> 20) & 0x3F; tcg_gen_movi_tl(t0, ret); tcg_gen_movi_tl(t1, imm); - gen_helper_shilo(t0, t1, cpu_env); + gen_helper_shilo(t0, t1, tcg_env); break; case OPC_SHILOV: tcg_gen_movi_tl(t0, ret); - gen_helper_shilo(t0, v1_t, cpu_env); + gen_helper_shilo(t0, v1_t, tcg_env); break; case OPC_MTHLIP: tcg_gen_movi_tl(t0, ret); - gen_helper_mthlip(t0, v1_t, cpu_env); + gen_helper_mthlip(t0, v1_t, tcg_env); break; case OPC_WRDSP: imm = (ctx->opcode >> 11) & 0x3FF; tcg_gen_movi_tl(t0, imm); - gen_helper_wrdsp(v1_t, t0, cpu_env); + gen_helper_wrdsp(v1_t, t0, tcg_env); break; case OPC_RDDSP: imm = (ctx->opcode >> 16) & 0x03FF; tcg_gen_movi_tl(t0, imm); - gen_helper_rddsp(cpu_gpr[ret], t0, cpu_env); + gen_helper_rddsp(cpu_gpr[ret], t0, tcg_env); break; } break; @@ -13746,7 +13086,7 @@ static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, switch (op2) { case OPC_DMTHLIP: tcg_gen_movi_tl(t0, ret); - gen_helper_dmthlip(v1_t, t0, cpu_env); + gen_helper_dmthlip(v1_t, t0, tcg_env); break; case OPC_DSHILO: { @@ -13754,106 +13094,102 @@ static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, int ac = (ctx->opcode >> 11) & 0x03; tcg_gen_movi_tl(t0, shift); tcg_gen_movi_tl(t1, ac); - gen_helper_dshilo(t0, t1, cpu_env); + gen_helper_dshilo(t0, t1, tcg_env); break; } case OPC_DSHILOV: { int ac = (ctx->opcode >> 11) & 0x03; tcg_gen_movi_tl(t0, ac); - gen_helper_dshilo(v1_t, t0, cpu_env); + gen_helper_dshilo(v1_t, t0, tcg_env); break; } case OPC_DEXTP: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_dextp(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_dextp(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_DEXTPV: tcg_gen_movi_tl(t0, v2); - gen_helper_dextp(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_dextp(cpu_gpr[ret], t0, v1_t, tcg_env); break; case OPC_DEXTPDP: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_dextpdp(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_dextpdp(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_DEXTPDPV: tcg_gen_movi_tl(t0, v2); - gen_helper_dextpdp(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_dextpdp(cpu_gpr[ret], t0, v1_t, tcg_env); break; case OPC_DEXTR_L: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_dextr_l(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_dextr_l(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_DEXTR_R_L: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_dextr_r_l(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_dextr_r_l(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_DEXTR_RS_L: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_dextr_rs_l(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_dextr_rs_l(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_DEXTR_W: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_dextr_w(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_dextr_w(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_DEXTR_R_W: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_dextr_r_w(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_dextr_r_w(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_DEXTR_RS_W: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_dextr_rs_w(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_dextr_rs_w(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_DEXTR_S_H: tcg_gen_movi_tl(t0, v2); tcg_gen_movi_tl(t1, v1); - gen_helper_dextr_s_h(cpu_gpr[ret], t0, t1, cpu_env); + gen_helper_dextr_s_h(cpu_gpr[ret], t0, t1, tcg_env); break; case OPC_DEXTRV_S_H: tcg_gen_movi_tl(t0, v2); - gen_helper_dextr_s_h(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_dextr_s_h(cpu_gpr[ret], t0, v1_t, tcg_env); break; case OPC_DEXTRV_L: tcg_gen_movi_tl(t0, v2); - gen_helper_dextr_l(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_dextr_l(cpu_gpr[ret], t0, v1_t, tcg_env); break; case OPC_DEXTRV_R_L: tcg_gen_movi_tl(t0, v2); - gen_helper_dextr_r_l(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_dextr_r_l(cpu_gpr[ret], t0, v1_t, tcg_env); break; case OPC_DEXTRV_RS_L: tcg_gen_movi_tl(t0, v2); - gen_helper_dextr_rs_l(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_dextr_rs_l(cpu_gpr[ret], t0, v1_t, tcg_env); break; case OPC_DEXTRV_W: tcg_gen_movi_tl(t0, v2); - gen_helper_dextr_w(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_dextr_w(cpu_gpr[ret], t0, v1_t, tcg_env); break; case OPC_DEXTRV_R_W: tcg_gen_movi_tl(t0, v2); - gen_helper_dextr_r_w(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_dextr_r_w(cpu_gpr[ret], t0, v1_t, tcg_env); break; case OPC_DEXTRV_RS_W: tcg_gen_movi_tl(t0, v2); - gen_helper_dextr_rs_w(cpu_gpr[ret], t0, v1_t, cpu_env); + gen_helper_dextr_rs_w(cpu_gpr[ret], t0, v1_t, tcg_env); break; } break; #endif } - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(v1_t); } /* End MIPSDSP functions. */ @@ -13909,8 +13245,8 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) } break; case R6_OPC_SDBBP: - if (is_uhi(extract32(ctx->opcode, 6, 20))) { - gen_helper_do_semihosting(cpu_env); + if (is_uhi(ctx, extract32(ctx->opcode, 6, 20))) { + ctx->base.is_jmp = DISAS_SEMIHOST; } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { gen_reserved_instruction(ctx); @@ -14180,7 +13516,7 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) MIPS_INVAL("PMON / selsl"); gen_reserved_instruction(ctx); #else - gen_helper_pmon(cpu_env, tcg_constant_i32(sa)); + gen_helper_pmon(tcg_env, tcg_constant_i32(sa)); #endif break; case OPC_SYSCALL: @@ -14321,8 +13657,8 @@ static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) gen_cl(ctx, op1, rd, rs); break; case OPC_SDBBP: - if (is_uhi(extract32(ctx->opcode, 6, 20))) { - gen_helper_do_semihosting(cpu_env); + if (is_uhi(ctx, extract32(ctx->opcode, 6, 20))) { + ctx->base.is_jmp = DISAS_SEMIHOST; } else { /* * XXX: not clear which exception should be raised @@ -14533,7 +13869,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) case OPC_LBUX: case OPC_LHX: case OPC_LWX: - gen_mipsdsp_ld(ctx, op2, rd, rs, rt); + gen_mips_lx(ctx, op2, rd, rs, rt); break; default: /* Invalid */ MIPS_INVAL("MASK LX"); @@ -14703,10 +14039,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) gen_load_gpr(t0, rt); gen_load_gpr(t1, rs); - gen_helper_insv(cpu_gpr[rt], cpu_env, t1, t0); - - tcg_temp_free(t0); - tcg_temp_free(t1); + gen_helper_insv(cpu_gpr[rt], tcg_env, t1, t0); break; } default: /* Invalid */ @@ -14975,10 +14308,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) gen_load_gpr(t0, rt); gen_load_gpr(t1, rs); - gen_helper_dinsv(cpu_gpr[rt], cpu_env, t1, t0); - - tcg_temp_free(t0); - tcg_temp_free(t1); + gen_helper_dinsv(cpu_gpr[rt], tcg_env, t1, t0); break; } default: /* Invalid */ @@ -15205,8 +14535,6 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) gen_load_gpr(t0, rt); gen_load_gpr(t1, rs); gen_helper_fork(t0, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); } break; case OPC_YIELD: @@ -15215,9 +14543,8 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) TCGv t0 = tcg_temp_new(); gen_load_gpr(t0, rs); - gen_helper_yield(t0, cpu_env, t0); + gen_helper_yield(t0, tcg_env, t0); gen_store_gpr(t0, rd); - tcg_temp_free(t0); } break; default: @@ -15254,12 +14581,9 @@ static bool decode_opc_legacy(CPUMIPSState *env, DisasContext *ctx) } #endif if (TARGET_LONG_BITS == 32 && (ctx->insn_flags & ASE_MXU)) { - if (MASK_SPECIAL2(ctx->opcode) == OPC_MUL) { - gen_arith(ctx, OPC_MUL, rd, rs, rt); - } else { - decode_ase_mxu(ctx, ctx->opcode); + if (decode_ase_mxu(ctx, ctx->opcode)) { + break; } - break; } decode_opc_special2_legacy(env, ctx); break; @@ -15410,32 +14734,32 @@ static bool decode_opc_legacy(CPUMIPSState *env, DisasContext *ctx) break; case OPC_DVPE: check_cp0_mt(ctx); - gen_helper_dvpe(t0, cpu_env); + gen_helper_dvpe(t0, tcg_env); gen_store_gpr(t0, rt); break; case OPC_EVPE: check_cp0_mt(ctx); - gen_helper_evpe(t0, cpu_env); + gen_helper_evpe(t0, tcg_env); gen_store_gpr(t0, rt); break; case OPC_DVP: check_insn(ctx, ISA_MIPS_R6); if (ctx->vp) { - gen_helper_dvp(t0, cpu_env); + gen_helper_dvp(t0, tcg_env); gen_store_gpr(t0, rt); } break; case OPC_EVP: check_insn(ctx, ISA_MIPS_R6); if (ctx->vp) { - gen_helper_evp(t0, cpu_env); + gen_helper_evp(t0, tcg_env); gen_store_gpr(t0, rt); } break; case OPC_DI: check_insn(ctx, ISA_MIPS_R2); save_cpu_state(ctx, 1); - gen_helper_di(t0, cpu_env); + gen_helper_di(t0, tcg_env); gen_store_gpr(t0, rt); /* * Stop translation as we may have switched @@ -15446,7 +14770,7 @@ static bool decode_opc_legacy(CPUMIPSState *env, DisasContext *ctx) case OPC_EI: check_insn(ctx, ISA_MIPS_R2); save_cpu_state(ctx, 1); - gen_helper_ei(t0, cpu_env); + gen_helper_ei(t0, tcg_env); gen_store_gpr(t0, rt); /* * DISAS_STOP isn't sufficient, we need to ensure we break @@ -15460,7 +14784,6 @@ static bool decode_opc_legacy(CPUMIPSState *env, DisasContext *ctx) gen_reserved_instruction(ctx); break; } - tcg_temp_free(t0); } #endif /* !CONFIG_USER_ONLY */ break; @@ -15908,7 +15231,6 @@ static bool decode_opc_legacy(CPUMIPSState *env, DisasContext *ctx) TCGv t0 = tcg_temp_new(); gen_load_gpr(t0, rs); tcg_gen_addi_tl(cpu_gpr[rt], t0, imm << 16); - tcg_temp_free(t0); } #else gen_reserved_instruction(ctx); @@ -15963,6 +15285,14 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) if (cpu_supports_isa(env, INSN_VR54XX) && decode_ext_vr54xx(ctx, ctx->opcode)) { return; } +#if defined(TARGET_MIPS64) + if (ase_lcsr_available(env) && decode_ase_lcsr(ctx, ctx->opcode)) { + return; + } + if (cpu_supports_isa(env, INSN_OCTEON) && decode_ext_octeon(ctx, ctx->opcode)) { + return; + } +#endif /* ISA extensions */ if (ase_msa_available(env) && decode_ase_msa(ctx, ctx->opcode)) { @@ -15984,7 +15314,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - CPUMIPSState *env = cs->env_ptr; + CPUMIPSState *env = cpu_env(cs); ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; ctx->saved_pc = -1; @@ -16023,8 +15353,9 @@ static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #else ctx->mem_idx = hflags_mmu_index(ctx->hflags); #endif - ctx->default_tcg_memop_mask = (ctx->insn_flags & (ISA_MIPS_R6 | - INSN_LOONGSON3A)) ? MO_UNALN : MO_ALIGN; + ctx->default_tcg_memop_mask = (!(ctx->insn_flags & ISA_NANOMIPS32) && + (ctx->insn_flags & (ISA_MIPS_R6 | + INSN_LOONGSON3A))) ? MO_UNALN : MO_ALIGN; /* * Execute a branch and its delay slot as a single instruction. @@ -16054,7 +15385,7 @@ static void mips_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) static void mips_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) { - CPUMIPSState *env = cs->env_ptr; + CPUMIPSState *env = cpu_env(cs); DisasContext *ctx = container_of(dcbase, DisasContext, base); int insn_bytes; int is_slot; @@ -16100,6 +15431,9 @@ static void mips_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) if (is_slot) { gen_branch(ctx, insn_bytes); } + if (ctx->base.is_jmp == DISAS_SEMIHOST) { + generate_exception_err(ctx, EXCP_SEMIHOST, insn_bytes); + } ctx->base.pc_next += insn_bytes; if (ctx->base.is_jmp != DISAS_NEXT) { @@ -16157,20 +15491,19 @@ static const TranslatorOps mips_tr_ops = { .disas_log = mips_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; - translator_loop(&mips_tr_ops, &ctx.base, cs, tb, max_insns); + translator_loop(cs, tb, max_insns, pc, host_pc, &mips_tr_ops, &ctx.base); } void mips_tcg_init(void) { - int i; - cpu_gpr[0] = NULL; - for (i = 1; i < 32; i++) - cpu_gpr[i] = tcg_global_mem_new(cpu_env, + for (unsigned i = 1; i < 32; i++) + cpu_gpr[i] = tcg_global_mem_new(tcg_env, offsetof(CPUMIPSState, active_tc.gpr[i]), regnames[i]); @@ -16180,48 +15513,48 @@ void mips_tcg_init(void) for (unsigned i = 1; i < 32; i++) { g_autofree char *rname = g_strdup_printf("%s[hi]", regnames[i]); - cpu_gpr_hi[i] = tcg_global_mem_new_i64(cpu_env, + cpu_gpr_hi[i] = tcg_global_mem_new_i64(tcg_env, offsetof(CPUMIPSState, active_tc.gpr_hi[i]), rname); } #endif /* !TARGET_MIPS64 */ - for (i = 0; i < 32; i++) { + for (unsigned i = 0; i < 32; i++) { int off = offsetof(CPUMIPSState, active_fpu.fpr[i].wr.d[0]); - fpu_f64[i] = tcg_global_mem_new_i64(cpu_env, off, fregnames[i]); + fpu_f64[i] = tcg_global_mem_new_i64(tcg_env, off, fregnames[i]); } msa_translate_init(); - cpu_PC = tcg_global_mem_new(cpu_env, + cpu_PC = tcg_global_mem_new(tcg_env, offsetof(CPUMIPSState, active_tc.PC), "PC"); - for (i = 0; i < MIPS_DSP_ACC; i++) { - cpu_HI[i] = tcg_global_mem_new(cpu_env, + for (unsigned i = 0; i < MIPS_DSP_ACC; i++) { + cpu_HI[i] = tcg_global_mem_new(tcg_env, offsetof(CPUMIPSState, active_tc.HI[i]), regnames_HI[i]); - cpu_LO[i] = tcg_global_mem_new(cpu_env, + cpu_LO[i] = tcg_global_mem_new(tcg_env, offsetof(CPUMIPSState, active_tc.LO[i]), regnames_LO[i]); } - cpu_dspctrl = tcg_global_mem_new(cpu_env, + cpu_dspctrl = tcg_global_mem_new(tcg_env, offsetof(CPUMIPSState, active_tc.DSPControl), "DSPControl"); - bcond = tcg_global_mem_new(cpu_env, + bcond = tcg_global_mem_new(tcg_env, offsetof(CPUMIPSState, bcond), "bcond"); - btarget = tcg_global_mem_new(cpu_env, + btarget = tcg_global_mem_new(tcg_env, offsetof(CPUMIPSState, btarget), "btarget"); - hflags = tcg_global_mem_new_i32(cpu_env, + hflags = tcg_global_mem_new_i32(tcg_env, offsetof(CPUMIPSState, hflags), "hflags"); - fpu_fcr0 = tcg_global_mem_new_i32(cpu_env, + fpu_fcr0 = tcg_global_mem_new_i32(tcg_env, offsetof(CPUMIPSState, active_fpu.fcr0), "fcr0"); - fpu_fcr31 = tcg_global_mem_new_i32(cpu_env, + fpu_fcr31 = tcg_global_mem_new_i32(tcg_env, offsetof(CPUMIPSState, active_fpu.fcr31), "fcr31"); - cpu_lladdr = tcg_global_mem_new(cpu_env, offsetof(CPUMIPSState, lladdr), + cpu_lladdr = tcg_global_mem_new(tcg_env, offsetof(CPUMIPSState, lladdr), "lladdr"); - cpu_llval = tcg_global_mem_new(cpu_env, offsetof(CPUMIPSState, llval), + cpu_llval = tcg_global_mem_new(tcg_env, offsetof(CPUMIPSState, llval), "llval"); if (TARGET_LONG_BITS == 32) { @@ -16229,9 +15562,12 @@ void mips_tcg_init(void) } } -void restore_state_to_opc(CPUMIPSState *env, TranslationBlock *tb, - target_ulong *data) +void mips_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) { + CPUMIPSState *env = cpu_env(cs); + env->active_tc.PC = data[0]; env->hflags &= ~MIPS_HFLAG_BMASK; env->hflags |= data[1]; diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h index 9997fe2f3c..2b6646b339 100644 --- a/target/mips/tcg/translate.h +++ b/target/mips/tcg/translate.h @@ -8,8 +8,11 @@ #ifndef TARGET_MIPS_TRANSLATE_H #define TARGET_MIPS_TRANSLATE_H -#include "qemu/log.h" +#include "cpu.h" +#include "tcg/tcg-op.h" #include "exec/translator.h" +#include "exec/helper-gen.h" +#include "qemu/log.h" #define MIPS_DEBUG_DISAS 0 @@ -46,11 +49,14 @@ typedef struct DisasContext { bool mrp; bool nan2008; bool abs2008; - bool saar; bool mi; int gi; } DisasContext; +#define DISAS_STOP DISAS_TARGET_0 +#define DISAS_EXIT DISAS_TARGET_1 +#define DISAS_SEMIHOST DISAS_TARGET_2 + /* MIPS major opcodes */ #define MASK_OP_MAJOR(op) (op & (0x3F << 26)) @@ -116,15 +122,15 @@ enum { }; #define gen_helper_0e1i(name, arg1, arg2) do { \ - gen_helper_##name(cpu_env, arg1, tcg_constant_i32(arg2)); \ + gen_helper_##name(tcg_env, arg1, tcg_constant_i32(arg2)); \ } while (0) #define gen_helper_1e0i(name, ret, arg1) do { \ - gen_helper_##name(ret, cpu_env, tcg_constant_i32(arg1)); \ + gen_helper_##name(ret, tcg_env, tcg_constant_i32(arg1)); \ } while (0) #define gen_helper_0e2i(name, arg1, arg2, arg3) do { \ - gen_helper_##name(cpu_env, arg1, arg2, tcg_constant_i32(arg3));\ + gen_helper_##name(tcg_env, arg1, arg2, tcg_constant_i32(arg3));\ } while (0) void generate_exception(DisasContext *ctx, int excp); @@ -195,7 +201,8 @@ extern TCGv bcond; do { \ if (MIPS_DEBUG_DISAS) { \ qemu_log_mask(CPU_LOG_TB_IN_ASM, \ - TARGET_FMT_lx ": %08x Invalid %s %03x %03x %03x\n", \ + "%016" VADDR_PRIx \ + ": %08x Invalid %s %03x %03x %03x\n", \ ctx->base.pc_next, ctx->opcode, op, \ ctx->opcode >> 26, ctx->opcode & 0x3F, \ ((ctx->opcode >> 16) & 0x1F)); \ @@ -214,7 +221,9 @@ bool decode_isa_rel6(DisasContext *ctx, uint32_t insn); bool decode_ase_msa(DisasContext *ctx, uint32_t insn); bool decode_ext_txx9(DisasContext *ctx, uint32_t insn); #if defined(TARGET_MIPS64) +bool decode_ase_lcsr(DisasContext *ctx, uint32_t insn); bool decode_ext_tx79(DisasContext *ctx, uint32_t insn); +bool decode_ext_octeon(DisasContext *ctx, uint32_t insn); #endif bool decode_ext_vr54xx(DisasContext *ctx, uint32_t insn); diff --git a/target/mips/tcg/translate_addr_const.c b/target/mips/tcg/translate_addr_const.c index 96f483418e..6f4b39f715 100644 --- a/target/mips/tcg/translate_addr_const.c +++ b/target/mips/tcg/translate_addr_const.c @@ -11,7 +11,6 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" #include "translate.h" bool gen_lsa(DisasContext *ctx, int rd, int rt, int rs, int sa) @@ -30,10 +29,6 @@ bool gen_lsa(DisasContext *ctx, int rd, int rt, int rs, int sa) tcg_gen_shli_tl(t0, t0, sa + 1); tcg_gen_add_tl(cpu_gpr[rd], t0, t1); tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); - - tcg_temp_free(t1); - tcg_temp_free(t0); - return true; } @@ -54,8 +49,5 @@ bool gen_dlsa(DisasContext *ctx, int rd, int rt, int rs, int sa) gen_load_gpr(t1, rt); tcg_gen_shli_tl(t0, t0, sa + 1); tcg_gen_add_tl(cpu_gpr[rd], t0, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); - return true; } diff --git a/target/mips/tcg/tx79.decode b/target/mips/tcg/tx79.decode index 57d87a2076..578b8c54c0 100644 --- a/target/mips/tcg/tx79.decode +++ b/target/mips/tcg/tx79.decode @@ -24,7 +24,7 @@ @rs ...... rs:5 ..... .......... ...... &r sa=0 rt=0 rd=0 @rd ...... .......... rd:5 ..... ...... &r sa=0 rs=0 rt=0 -@ldst ...... base:5 rt:5 offset:16 &i +@ldst ...... base:5 rt:5 offset:s16 &i ########################################################################### diff --git a/target/mips/tcg/tx79_translate.c b/target/mips/tcg/tx79_translate.c index 4e479c2d10..dd6fb8a7bd 100644 --- a/target/mips/tcg/tx79_translate.c +++ b/target/mips/tcg/tx79_translate.c @@ -8,10 +8,8 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "exec/helper-gen.h" #include "translate.h" +#include "tcg/tcg-op-gvec.h" /* Include the auto-generated decoder. */ #include "decode-tx79.c.inc" @@ -138,10 +136,6 @@ static bool trans_parallel_arith(DisasContext *ctx, arg_r *a, gen_load_gpr_hi(ax, a->rs); gen_load_gpr_hi(bx, a->rt); gen_logic_i64(cpu_gpr_hi[a->rd], ax, bx); - - tcg_temp_free(bx); - tcg_temp_free(ax); - return true; } @@ -247,8 +241,8 @@ static bool trans_parallel_compare(DisasContext *ctx, arg_r *a, return true; } - c0 = tcg_const_tl(0); - c1 = tcg_const_tl(0xffffffff); + c0 = tcg_constant_tl(0); + c1 = tcg_constant_tl(0xffffffff); ax = tcg_temp_new_i64(); bx = tcg_temp_new_i64(); t0 = tcg_temp_new_i64(); @@ -273,15 +267,6 @@ static bool trans_parallel_compare(DisasContext *ctx, arg_r *a, tcg_gen_movcond_i64(cond, t2, t1, t0, c1, c0); tcg_gen_deposit_i64(cpu_gpr_hi[a->rd], cpu_gpr_hi[a->rd], t2, wlen * i, wlen); } - - tcg_temp_free(t2); - tcg_temp_free(t1); - tcg_temp_free(t0); - tcg_temp_free(bx); - tcg_temp_free(ax); - tcg_temp_free(c1); - tcg_temp_free(c0); - return true; } @@ -362,10 +347,6 @@ static bool trans_LQ(DisasContext *ctx, arg_i *a) tcg_gen_addi_i64(addr, addr, 8); tcg_gen_qemu_ld_i64(t0, addr, ctx->mem_idx, MO_TEUQ); gen_store_gpr_hi(t0, a->rt); - - tcg_temp_free(t0); - tcg_temp_free(addr); - return true; } @@ -389,10 +370,6 @@ static bool trans_SQ(DisasContext *ctx, arg_i *a) tcg_gen_addi_i64(addr, addr, 8); gen_load_gpr_hi(t0, a->rt); tcg_gen_qemu_st_i64(t0, addr, ctx->mem_idx, MO_TEUQ); - - tcg_temp_free(addr); - tcg_temp_free(t0); - return true; } @@ -458,11 +435,6 @@ static bool trans_PPACW(DisasContext *ctx, arg_r *a) gen_load_gpr_hi(t0, a->rs); /* a1 */ tcg_gen_deposit_i64(cpu_gpr_hi[a->rd], a0, t0, 32, 32); - - tcg_temp_free(t0); - tcg_temp_free(b0); - tcg_temp_free(a0); - return true; } @@ -506,10 +478,6 @@ static bool trans_PEXTLx(DisasContext *ctx, arg_r *a, unsigned wlen) tcg_gen_shri_i64(bx, bx, wlen); tcg_gen_shri_i64(ax, ax, wlen); } - - tcg_temp_free(bx); - tcg_temp_free(ax); - return true; } @@ -541,10 +509,6 @@ static bool trans_PEXTLW(DisasContext *ctx, arg_r *a) gen_load_gpr(ax, a->rs); gen_load_gpr(bx, a->rt); gen_pextw(cpu_gpr[a->rd], cpu_gpr_hi[a->rd], ax, bx); - - tcg_temp_free(bx); - tcg_temp_free(ax); - return true; } @@ -564,10 +528,6 @@ static bool trans_PEXTUW(DisasContext *ctx, arg_r *a) gen_load_gpr_hi(ax, a->rs); gen_load_gpr_hi(bx, a->rt); gen_pextw(cpu_gpr[a->rd], cpu_gpr_hi[a->rd], ax, bx); - - tcg_temp_free(bx); - tcg_temp_free(ax); - return true; } @@ -678,8 +638,5 @@ static bool trans_PROT3W(DisasContext *ctx, arg_r *a) tcg_gen_deposit_i64(cpu_gpr[a->rd], cpu_gpr[a->rt], ax, 0, 32); tcg_gen_rotri_i64(cpu_gpr[a->rd], cpu_gpr[a->rd], 32); - - tcg_temp_free(ax); - return true; } diff --git a/target/mips/tcg/vr54xx_translate.c b/target/mips/tcg/vr54xx_translate.c index 3e2c98f2c6..c877ede76e 100644 --- a/target/mips/tcg/vr54xx_translate.c +++ b/target/mips/tcg/vr54xx_translate.c @@ -10,10 +10,7 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg-op.h" -#include "exec/helper-gen.h" #include "translate.h" -#include "internal.h" /* Include the auto-generated decoder. */ #include "decode-vr54xx.c.inc" @@ -46,14 +43,10 @@ static bool trans_mult_acc(DisasContext *ctx, arg_r *a, gen_load_gpr(t0, a->rs); gen_load_gpr(t1, a->rt); - gen_helper_mult_acc(t0, cpu_env, t0, t1); + gen_helper_mult_acc(t0, tcg_env, t0, t1); gen_store_gpr(t0, a->rd); - - tcg_temp_free(t0); - tcg_temp_free(t1); - - return false; + return true; } TRANS(MACC, trans_mult_acc, gen_helper_macc); diff --git a/target/nios2/Kconfig b/target/nios2/Kconfig index 1529ab8950..c65550c861 100644 --- a/target/nios2/Kconfig +++ b/target/nios2/Kconfig @@ -1,2 +1,3 @@ config NIOS2 bool + select SEMIHOSTING diff --git a/target/nios2/cpu-param.h b/target/nios2/cpu-param.h index 177d720864..767bba4b7b 100644 --- a/target/nios2/cpu-param.h +++ b/target/nios2/cpu-param.h @@ -16,6 +16,5 @@ #else # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif -#define NB_MMU_MODES 2 #endif diff --git a/target/nios2/cpu-qom.h b/target/nios2/cpu-qom.h new file mode 100644 index 0000000000..2fd9121540 --- /dev/null +++ b/target/nios2/cpu-qom.h @@ -0,0 +1,18 @@ +/* + * QEMU Nios II CPU QOM header (target agnostic) + * + * Copyright (c) 2012 Chris Wulff + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef QEMU_NIOS2_CPU_QOM_H +#define QEMU_NIOS2_CPU_QOM_H + +#include "hw/core/cpu.h" + +#define TYPE_NIOS2_CPU "nios2-cpu" + +OBJECT_DECLARE_CPU_TYPE(Nios2CPU, Nios2CPUClass, NIOS2_CPU) + +#endif diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c index 19b2409974..679aff5730 100644 --- a/target/nios2/cpu.c +++ b/target/nios2/cpu.c @@ -23,15 +23,24 @@ #include "qapi/error.h" #include "cpu.h" #include "exec/log.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "hw/qdev-properties.h" static void nios2_cpu_set_pc(CPUState *cs, vaddr value) { - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; + cpu_env(cs)->pc = value; +} - env->pc = value; +static vaddr nios2_cpu_get_pc(CPUState *cs) +{ + return cpu_env(cs)->pc; +} + +static void nios2_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + cpu_env(cs)->pc = data[0]; } static bool nios2_cpu_has_work(CPUState *cs) @@ -39,14 +48,22 @@ static bool nios2_cpu_has_work(CPUState *cs) return cs->interrupt_request & CPU_INTERRUPT_HARD; } -static void nios2_cpu_reset(DeviceState *dev) +static int nios2_cpu_mmu_index(CPUState *cs, bool ifetch) { - CPUState *cs = CPU(dev); + return (cpu_env(cs)->ctrl[CR_STATUS] & CR_STATUS_U + ? MMU_USER_IDX : MMU_SUPERVISOR_IDX); +} + +static void nios2_cpu_reset_hold(Object *obj) +{ + CPUState *cs = CPU(obj); Nios2CPU *cpu = NIOS2_CPU(cs); - Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(cpu); + Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(obj); CPUNios2State *env = &cpu->env; - ncc->parent_reset(dev); + if (ncc->parent_phases.hold) { + ncc->parent_phases.hold(obj); + } memset(env->ctrl, 0, sizeof(env->ctrl)); env->pc = cpu->reset_addr; @@ -93,11 +110,9 @@ static void iic_set_irq(void *opaque, int irq, int level) static void nios2_cpu_initfn(Object *obj) { +#if !defined(CONFIG_USER_ONLY) Nios2CPU *cpu = NIOS2_CPU(obj); - cpu_set_cpustate_pointers(cpu); - -#if !defined(CONFIG_USER_ONLY) mmu_init(&cpu->env); #endif } @@ -181,14 +196,6 @@ static void nios2_cpu_realizefn(DeviceState *dev, Error **errp) Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev); Error *local_err = NULL; -#ifndef CONFIG_USER_ONLY - if (cpu->eic_present) { - qdev_init_gpio_in_named(DEVICE(cpu), eic_set_irq, "EIC", 1); - } else { - qdev_init_gpio_in_named(DEVICE(cpu), iic_set_irq, "IRQ", 32); - } -#endif - cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -202,6 +209,14 @@ static void nios2_cpu_realizefn(DeviceState *dev, Error **errp) /* We have reserved storage for cpuid; might as well use it. */ cpu->env.ctrl[CR_CPUID] = cs->cpu_index; +#ifndef CONFIG_USER_ONLY + if (cpu->eic_present) { + qdev_init_gpio_in_named(DEVICE(cpu), eic_set_irq, "EIC", 1); + } else { + qdev_init_gpio_in_named(DEVICE(cpu), iic_set_irq, "IRQ", 32); + } +#endif + ncc->parent_realize(dev, errp); } @@ -336,8 +351,9 @@ static const struct SysemuCPUOps nios2_sysemu_ops = { #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps nios2_tcg_ops = { +static const TCGCPUOps nios2_tcg_ops = { .initialize = nios2_tcg_init, + .restore_state_to_opc = nios2_restore_state_to_opc, #ifndef CONFIG_USER_ONLY .tlb_fill = nios2_cpu_tlb_fill, @@ -352,16 +368,20 @@ static void nios2_cpu_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); Nios2CPUClass *ncc = NIOS2_CPU_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); device_class_set_parent_realize(dc, nios2_cpu_realizefn, &ncc->parent_realize); device_class_set_props(dc, nios2_properties); - device_class_set_parent_reset(dc, nios2_cpu_reset, &ncc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, nios2_cpu_reset_hold, NULL, + &ncc->parent_phases); cc->class_by_name = nios2_cpu_class_by_name; cc->has_work = nios2_cpu_has_work; + cc->mmu_index = nios2_cpu_mmu_index; cc->dump_state = nios2_cpu_dump_state; cc->set_pc = nios2_cpu_set_pc; + cc->get_pc = nios2_cpu_get_pc; cc->disas_set_info = nios2_cpu_disas_set_info; #ifndef CONFIG_USER_ONLY cc->sysemu_ops = &nios2_sysemu_ops; @@ -376,6 +396,7 @@ static const TypeInfo nios2_cpu_type_info = { .name = TYPE_NIOS2_CPU, .parent = TYPE_CPU, .instance_size = sizeof(Nios2CPU), + .instance_align = __alignof(Nios2CPU), .instance_init = nios2_cpu_initfn, .class_size = sizeof(Nios2CPUClass), .class_init = nios2_cpu_class_init, diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h index f85581ee56..4164a3432e 100644 --- a/target/nios2/cpu.h +++ b/target/nios2/cpu.h @@ -21,33 +21,26 @@ #ifndef NIOS2_CPU_H #define NIOS2_CPU_H +#include "cpu-qom.h" #include "exec/cpu-defs.h" -#include "hw/core/cpu.h" #include "hw/registerfields.h" -#include "qom/object.h" typedef struct CPUArchState CPUNios2State; #if !defined(CONFIG_USER_ONLY) #include "mmu.h" #endif -#define TYPE_NIOS2_CPU "nios2-cpu" - -OBJECT_DECLARE_CPU_TYPE(Nios2CPU, Nios2CPUClass, NIOS2_CPU) - /** * Nios2CPUClass: - * @parent_reset: The parent class' reset handler. + * @parent_phases: The parent class' reset phase handlers. * * A Nios2 CPU model. */ struct Nios2CPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; - DeviceReset parent_reset; + ResettablePhases parent_phases; }; #define TARGET_HAS_ICE 1 @@ -214,11 +207,8 @@ typedef struct { * A Nios2 CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUNios2State env; bool diverr_present; @@ -262,7 +252,6 @@ void nios2_tcg_init(void); void nios2_cpu_do_interrupt(CPUState *cs); void dump_mmu(CPUNios2State *env); void nios2_cpu_dump_state(CPUState *cpu, FILE *f, int flags); -hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); G_NORETURN void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); @@ -281,13 +270,8 @@ void do_nios2_semihosting(CPUNios2State *env); #define MMU_SUPERVISOR_IDX 0 #define MMU_USER_IDX 1 -static inline int cpu_mmu_index(CPUNios2State *env, bool ifetch) -{ - return (env->ctrl[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX : - MMU_SUPERVISOR_IDX; -} - #ifndef CONFIG_USER_ONLY +hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); @@ -302,8 +286,8 @@ FIELD(TBFLAGS, CRS0, 0, 1) /* Set if CRS == 0. */ FIELD(TBFLAGS, U, 1, 1) /* Overlaps CR_STATUS_U */ FIELD(TBFLAGS, R0_0, 2, 1) /* Set if R0 == 0. */ -static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) +static inline void cpu_get_tb_cpu_state(CPUNios2State *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS); diff --git a/target/nios2/helper.c b/target/nios2/helper.c index bb3b09e5a7..ac57121afc 100644 --- a/target/nios2/helper.c +++ b/target/nios2/helper.c @@ -287,8 +287,7 @@ void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; + CPUNios2State *env = cpu_env(cs); env->ctrl[CR_BADADDR] = addr; cs->exception_index = EXCP_UNALIGN; diff --git a/target/nios2/meson.build b/target/nios2/meson.build index 2bd60ba306..12d8abf0bd 100644 --- a/target/nios2/meson.build +++ b/target/nios2/meson.build @@ -1,17 +1,17 @@ nios2_ss = ss.source_set() nios2_ss.add(files( 'cpu.c', - 'nios2-semi.c', 'op_helper.c', 'translate.c', )) -nios2_softmmu_ss = ss.source_set() -nios2_softmmu_ss.add(files( +nios2_system_ss = ss.source_set() +nios2_system_ss.add(files( 'helper.c', 'monitor.c', - 'mmu.c' + 'mmu.c', + 'nios2-semi.c', )) target_arch += {'nios2': nios2_ss} -target_softmmu_arch += {'nios2': nios2_softmmu_ss} +target_system_arch += {'nios2': nios2_system_ss} diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c index ec88474a73..420702e293 100644 --- a/target/nios2/nios2-semi.c +++ b/target/nios2/nios2-semi.c @@ -22,14 +22,11 @@ */ #include "qemu/osdep.h" - #include "cpu.h" -#include "exec/gdbstub.h" -#if defined(CONFIG_USER_ONLY) -#include "qemu.h" -#else -#include "exec/softmmu-semi.h" -#endif +#include "gdbstub/syscalls.h" +#include "gdbstub/helpers.h" +#include "semihosting/syscalls.h" +#include "semihosting/uaccess.h" #include "qemu/log.h" #define HOSTED_EXIT 0 @@ -47,105 +44,42 @@ #define HOSTED_ISATTY 12 #define HOSTED_SYSTEM 13 -typedef uint32_t gdb_mode_t; -typedef uint32_t gdb_time_t; - -struct nios2_gdb_stat { - uint32_t gdb_st_dev; /* device */ - uint32_t gdb_st_ino; /* inode */ - gdb_mode_t gdb_st_mode; /* protection */ - uint32_t gdb_st_nlink; /* number of hard links */ - uint32_t gdb_st_uid; /* user ID of owner */ - uint32_t gdb_st_gid; /* group ID of owner */ - uint32_t gdb_st_rdev; /* device type (if inode device) */ - uint64_t gdb_st_size; /* total size, in bytes */ - uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */ - uint64_t gdb_st_blocks; /* number of blocks allocated */ - gdb_time_t gdb_st_atime; /* time of last access */ - gdb_time_t gdb_st_mtime; /* time of last modification */ - gdb_time_t gdb_st_ctime; /* time of last change */ -} QEMU_PACKED; - -struct gdb_timeval { - gdb_time_t tv_sec; /* second */ - uint64_t tv_usec; /* microsecond */ -} QEMU_PACKED; - -#define GDB_O_RDONLY 0x0 -#define GDB_O_WRONLY 0x1 -#define GDB_O_RDWR 0x2 -#define GDB_O_APPEND 0x8 -#define GDB_O_CREAT 0x200 -#define GDB_O_TRUNC 0x400 -#define GDB_O_EXCL 0x800 - -static int translate_openflags(int flags) +static int host_to_gdb_errno(int err) { - int hf; - - if (flags & GDB_O_WRONLY) { - hf = O_WRONLY; - } else if (flags & GDB_O_RDWR) { - hf = O_RDWR; - } else { - hf = O_RDONLY; +#define E(X) case E##X: return GDB_E##X + switch (err) { + E(PERM); + E(NOENT); + E(INTR); + E(BADF); + E(ACCES); + E(FAULT); + E(BUSY); + E(EXIST); + E(NODEV); + E(NOTDIR); + E(ISDIR); + E(INVAL); + E(NFILE); + E(MFILE); + E(FBIG); + E(NOSPC); + E(SPIPE); + E(ROFS); + E(NAMETOOLONG); + default: + return GDB_EUNKNOWN; } - - if (flags & GDB_O_APPEND) { - hf |= O_APPEND; - } - if (flags & GDB_O_CREAT) { - hf |= O_CREAT; - } - if (flags & GDB_O_TRUNC) { - hf |= O_TRUNC; - } - if (flags & GDB_O_EXCL) { - hf |= O_EXCL; - } - - return hf; +#undef E } -static bool translate_stat(CPUNios2State *env, target_ulong addr, - struct stat *s) -{ - struct nios2_gdb_stat *p; - - p = lock_user(VERIFY_WRITE, addr, sizeof(struct nios2_gdb_stat), 0); - - if (!p) { - return false; - } - p->gdb_st_dev = cpu_to_be32(s->st_dev); - p->gdb_st_ino = cpu_to_be32(s->st_ino); - p->gdb_st_mode = cpu_to_be32(s->st_mode); - p->gdb_st_nlink = cpu_to_be32(s->st_nlink); - p->gdb_st_uid = cpu_to_be32(s->st_uid); - p->gdb_st_gid = cpu_to_be32(s->st_gid); - p->gdb_st_rdev = cpu_to_be32(s->st_rdev); - p->gdb_st_size = cpu_to_be64(s->st_size); -#ifdef _WIN32 - /* Windows stat is missing some fields. */ - p->gdb_st_blksize = 0; - p->gdb_st_blocks = 0; -#else - p->gdb_st_blksize = cpu_to_be64(s->st_blksize); - p->gdb_st_blocks = cpu_to_be64(s->st_blocks); -#endif - p->gdb_st_atime = cpu_to_be32(s->st_atime); - p->gdb_st_mtime = cpu_to_be32(s->st_mtime); - p->gdb_st_ctime = cpu_to_be32(s->st_ctime); - unlock_user(p, addr, sizeof(struct nios2_gdb_stat)); - return true; -} - -static void nios2_semi_return_u32(CPUNios2State *env, uint32_t ret, - uint32_t err) +static void nios2_semi_u32_cb(CPUState *cs, uint64_t ret, int err) { + CPUNios2State *env = cpu_env(cs); target_ulong args = env->regs[R_ARG1]; + if (put_user_u32(ret, args) || - put_user_u32(err, args + 4)) { + put_user_u32(host_to_gdb_errno(err), args + 4)) { /* * The nios2 semihosting ABI does not provide any way to report this * error to the guest, so the best we can do is log it in qemu. @@ -156,295 +90,141 @@ static void nios2_semi_return_u32(CPUNios2State *env, uint32_t ret, } } -static void nios2_semi_return_u64(CPUNios2State *env, uint64_t ret, - uint32_t err) +static void nios2_semi_u64_cb(CPUState *cs, uint64_t ret, int err) { + CPUNios2State *env = cpu_env(cs); target_ulong args = env->regs[R_ARG1]; + if (put_user_u32(ret >> 32, args) || put_user_u32(ret, args + 4) || - put_user_u32(err, args + 8)) { + put_user_u32(host_to_gdb_errno(err), args + 8)) { /* No way to report this via nios2 semihosting ABI; just log it */ qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value " "discarded because argument block not writable\n"); } } -static int nios2_semi_is_lseek; - -static void nios2_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - - if (nios2_semi_is_lseek) { - /* - * FIXME: We've already lost the high bits of the lseek - * return value. - */ - nios2_semi_return_u64(env, ret, err); - nios2_semi_is_lseek = 0; - } else { - nios2_semi_return_u32(env, ret, err); - } -} - /* * Read the input value from the argument block; fail the semihosting * call if the memory read fails. */ #define GET_ARG(n) do { \ if (get_user_ual(arg ## n, args + (n) * 4)) { \ - result = -1; \ - errno = EFAULT; \ goto failed; \ } \ } while (0) +#define GET_ARG64(n) do { \ + if (get_user_ual(arg ## n, args + (n) * 4)) { \ + goto failed64; \ + } \ +} while (0) + void do_nios2_semihosting(CPUNios2State *env) { + CPUState *cs = env_cpu(env); int nr; uint32_t args; target_ulong arg0, arg1, arg2, arg3; - void *p; - void *q; - uint32_t len; - uint32_t result; nr = env->regs[R_ARG0]; args = env->regs[R_ARG1]; switch (nr) { case HOSTED_EXIT: - gdb_exit(env->regs[R_ARG0]); - exit(env->regs[R_ARG0]); + gdb_exit(env->regs[R_ARG1]); + exit(env->regs[R_ARG1]); + case HOSTED_OPEN: GET_ARG(0); GET_ARG(1); GET_ARG(2); GET_ARG(3); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "open,%s,%x,%x", arg0, (int)arg1, - arg2, arg3); - return; - } else { - p = lock_user_string(arg0); - if (!p) { - result = -1; - errno = EFAULT; - } else { - result = open(p, translate_openflags(arg2), arg3); - unlock_user(p, arg0, 0); - } - } + semihost_sys_open(cs, nios2_semi_u32_cb, arg0, arg1, arg2, arg3); break; + case HOSTED_CLOSE: - { - /* Ignore attempts to close stdin/out/err. */ - GET_ARG(0); - int fd = arg0; - if (fd > 2) { - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "close,%x", arg0); - return; - } else { - result = close(fd); - } - } else { - result = 0; - } - break; - } + GET_ARG(0); + semihost_sys_close(cs, nios2_semi_u32_cb, arg0); + break; + case HOSTED_READ: GET_ARG(0); GET_ARG(1); GET_ARG(2); - len = arg2; - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "read,%x,%x,%x", - arg0, arg1, len); - return; - } else { - p = lock_user(VERIFY_WRITE, arg1, len, 0); - if (!p) { - result = -1; - errno = EFAULT; - } else { - result = read(arg0, p, len); - unlock_user(p, arg1, len); - } - } + semihost_sys_read(cs, nios2_semi_u32_cb, arg0, arg1, arg2); break; + case HOSTED_WRITE: GET_ARG(0); GET_ARG(1); GET_ARG(2); - len = arg2; - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "write,%x,%x,%x", - arg0, arg1, len); - return; - } else { - p = lock_user(VERIFY_READ, arg1, len, 1); - if (!p) { - result = -1; - errno = EFAULT; - } else { - result = write(arg0, p, len); - unlock_user(p, arg0, 0); - } - } + semihost_sys_write(cs, nios2_semi_u32_cb, arg0, arg1, arg2); break; + case HOSTED_LSEEK: - { - uint64_t off; - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - GET_ARG(3); - off = (uint32_t)arg2 | ((uint64_t)arg1 << 32); - if (use_gdb_syscalls()) { - nios2_semi_is_lseek = 1; - gdb_do_syscall(nios2_semi_cb, "lseek,%x,%lx,%x", - arg0, off, arg3); - } else { - off = lseek(arg0, off, arg3); - nios2_semi_return_u64(env, off, errno); - } - return; - } + GET_ARG64(0); + GET_ARG64(1); + GET_ARG64(2); + GET_ARG64(3); + semihost_sys_lseek(cs, nios2_semi_u64_cb, arg0, + deposit64(arg2, 32, 32, arg1), arg3); + break; + case HOSTED_RENAME: GET_ARG(0); GET_ARG(1); GET_ARG(2); GET_ARG(3); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "rename,%s,%s", - arg0, (int)arg1, arg2, (int)arg3); - return; - } else { - p = lock_user_string(arg0); - q = lock_user_string(arg2); - if (!p || !q) { - result = -1; - errno = EFAULT; - } else { - result = rename(p, q); - } - unlock_user(p, arg0, 0); - unlock_user(q, arg2, 0); - } + semihost_sys_rename(cs, nios2_semi_u32_cb, arg0, arg1, arg2, arg3); break; + case HOSTED_UNLINK: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "unlink,%s", - arg0, (int)arg1); - return; - } else { - p = lock_user_string(arg0); - if (!p) { - result = -1; - errno = EFAULT; - } else { - result = unlink(p); - unlock_user(p, arg0, 0); - } - } + semihost_sys_remove(cs, nios2_semi_u32_cb, arg0, arg1); break; + case HOSTED_STAT: GET_ARG(0); GET_ARG(1); GET_ARG(2); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "stat,%s,%x", - arg0, (int)arg1, arg2); - return; - } else { - struct stat s; - p = lock_user_string(arg0); - if (!p) { - result = -1; - errno = EFAULT; - } else { - result = stat(p, &s); - unlock_user(p, arg0, 0); - } - if (result == 0 && !translate_stat(env, arg2, &s)) { - result = -1; - errno = EFAULT; - } - } + semihost_sys_stat(cs, nios2_semi_u32_cb, arg0, arg1, arg2); break; + case HOSTED_FSTAT: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "fstat,%x,%x", - arg0, arg1); - return; - } else { - struct stat s; - result = fstat(arg0, &s); - if (result == 0 && !translate_stat(env, arg1, &s)) { - result = -1; - errno = EFAULT; - } - } + semihost_sys_fstat(cs, nios2_semi_u32_cb, arg0, arg1); break; + case HOSTED_GETTIMEOFDAY: - /* Only the tv parameter is used. tz is assumed NULL. */ GET_ARG(0); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "gettimeofday,%x,%x", - arg0, 0); - return; - } else { - struct gdb_timeval *p; - int64_t rt = g_get_real_time(); - p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval), 0); - if (!p) { - result = -1; - errno = EFAULT; - } else { - result = 0; - p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC); - p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC); - unlock_user(p, arg0, sizeof(struct gdb_timeval)); - } - } + GET_ARG(1); + semihost_sys_gettimeofday(cs, nios2_semi_u32_cb, arg0, arg1); break; + case HOSTED_ISATTY: GET_ARG(0); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "isatty,%x", arg0); - return; - } else { - result = isatty(arg0); - } + semihost_sys_isatty(cs, nios2_semi_u32_cb, arg0); break; + case HOSTED_SYSTEM: GET_ARG(0); GET_ARG(1); - if (use_gdb_syscalls()) { - gdb_do_syscall(nios2_semi_cb, "system,%s", - arg0, (int)arg1); - return; - } else { - p = lock_user_string(arg0); - if (!p) { - result = -1; - errno = EFAULT; - } else { - result = system(p); - unlock_user(p, arg0, 0); - } - } + semihost_sys_system(cs, nios2_semi_u32_cb, arg0, arg1); break; + default: qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: unsupported " "semihosting syscall %d\n", nr); - result = 0; + nios2_semi_u32_cb(cs, -1, ENOSYS); + break; + + failed: + nios2_semi_u32_cb(cs, -1, EFAULT); + break; + failed64: + nios2_semi_u64_cb(cs, -1, EFAULT); + break; } -failed: - nios2_semi_return_u32(env, result, errno); } diff --git a/target/nios2/op_helper.c b/target/nios2/op_helper.c index 2e30d0a908..5017457c5e 100644 --- a/target/nios2/op_helper.c +++ b/target/nios2/op_helper.c @@ -22,7 +22,6 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "qemu/main-loop.h" void helper_raise_exception(CPUNios2State *env, uint32_t index) { @@ -40,7 +39,7 @@ void nios2_cpu_loop_exit_advance(CPUNios2State *env, uintptr_t retaddr) * Do this here, rather than in restore_state_to_opc(), * lest we affect QEMU internal exceptions, like EXCP_DEBUG. */ - cpu_restore_state(cs, retaddr, true); + cpu_restore_state(cs, retaddr); env->pc += 4; cpu_loop_exit(cs); } diff --git a/target/nios2/translate.c b/target/nios2/translate.c index 3a037a68cc..7ddc6ac1a2 100644 --- a/target/nios2/translate.c +++ b/target/nios2/translate.c @@ -29,12 +29,15 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/log.h" -#include "exec/cpu_ldst.h" #include "exec/translator.h" #include "qemu/qemu-print.h" -#include "exec/gen-icount.h" #include "semihosting/semihost.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + + /* is_jmp field values */ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ @@ -206,7 +209,7 @@ static void t_gen_helper_raise_exception(DisasContext *dc, uint32_t index) { /* Note that PC is advanced for all hardware exceptions. */ tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); - gen_helper_raise_exception(cpu_env, tcg_constant_i32(index)); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(index)); dc->base.is_jmp = DISAS_NORETURN; } @@ -233,7 +236,6 @@ static void gen_jumpr(DisasContext *dc, int regno, bool is_call) tcg_gen_andi_tl(test, dest, 3); tcg_gen_brcondi_tl(TCG_COND_NE, test, 0, l); - tcg_temp_free(test); tcg_gen_mov_tl(cpu_pc, dest); if (is_call) { @@ -242,7 +244,7 @@ static void gen_jumpr(DisasContext *dc, int regno, bool is_call) tcg_gen_lookup_and_goto_ptr(); gen_set_label(l); - tcg_gen_st_tl(dest, cpu_env, offsetof(CPUNios2State, ctrl[CR_BADADDR])); + tcg_gen_st_tl(dest, tcg_env, offsetof(CPUNios2State, ctrl[CR_BADADDR])); t_gen_helper_raise_exception(dc, EXCP_UNALIGND); dc->base.is_jmp = DISAS_NORETURN; @@ -299,8 +301,12 @@ static void gen_ldx(DisasContext *dc, uint32_t code, uint32_t flags) TCGv data = dest_gpr(dc, instr.b); tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); +#ifdef CONFIG_USER_ONLY + flags |= MO_UNALN; +#else + flags |= MO_ALIGN; +#endif tcg_gen_qemu_ld_tl(data, addr, dc->mem_idx, flags); - tcg_temp_free(addr); } /* Store instructions */ @@ -311,8 +317,12 @@ static void gen_stx(DisasContext *dc, uint32_t code, uint32_t flags) TCGv addr = tcg_temp_new(); tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); +#ifdef CONFIG_USER_ONLY + flags |= MO_UNALN; +#else + flags |= MO_ALIGN; +#endif tcg_gen_qemu_st_tl(val, addr, dc->mem_idx, flags); - tcg_temp_free(addr); } /* Branch instructions */ @@ -404,7 +414,7 @@ static void rdprs(DisasContext *dc, uint32_t code, uint32_t flags) #else I_TYPE(instr, code); TCGv dest = dest_gpr(dc, instr.b); - gen_helper_rdprs(dest, cpu_env, tcg_constant_i32(instr.a)); + gen_helper_rdprs(dest, tcg_env, tcg_constant_i32(instr.a)); tcg_gen_addi_tl(dest, dest, instr.imm16.s); #endif } @@ -425,19 +435,19 @@ static const Nios2Instruction i_type_instructions[] = { INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_GE), /* cmpgei */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_ldx, MO_UW), /* ldhu */ + INSTRUCTION_FLG(gen_ldx, MO_TEUW), /* ldhu */ INSTRUCTION(andi), /* andi */ - INSTRUCTION_FLG(gen_stx, MO_UW), /* sth */ + INSTRUCTION_FLG(gen_stx, MO_TEUW), /* sth */ INSTRUCTION_FLG(gen_bxx, TCG_COND_GE), /* bge */ - INSTRUCTION_FLG(gen_ldx, MO_SW), /* ldh */ + INSTRUCTION_FLG(gen_ldx, MO_TESW), /* ldh */ INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_LT), /* cmplti */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION_NOP(), /* initda */ INSTRUCTION(ori), /* ori */ - INSTRUCTION_FLG(gen_stx, MO_UL), /* stw */ + INSTRUCTION_FLG(gen_stx, MO_TEUL), /* stw */ INSTRUCTION_FLG(gen_bxx, TCG_COND_LT), /* blt */ - INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldw */ + INSTRUCTION_FLG(gen_ldx, MO_TEUL), /* ldw */ INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_NE), /* cmpnei */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), @@ -457,19 +467,19 @@ static const Nios2Instruction i_type_instructions[] = { INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_GEU), /* cmpgeui */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_ldx, MO_UW), /* ldhuio */ + INSTRUCTION_FLG(gen_ldx, MO_TEUW), /* ldhuio */ INSTRUCTION(andhi), /* andhi */ - INSTRUCTION_FLG(gen_stx, MO_UW), /* sthio */ + INSTRUCTION_FLG(gen_stx, MO_TEUW), /* sthio */ INSTRUCTION_FLG(gen_bxx, TCG_COND_GEU), /* bgeu */ - INSTRUCTION_FLG(gen_ldx, MO_SW), /* ldhio */ + INSTRUCTION_FLG(gen_ldx, MO_TESW), /* ldhio */ INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_LTU), /* cmpltui */ INSTRUCTION_ILLEGAL(), INSTRUCTION_UNIMPLEMENTED(), /* custom */ INSTRUCTION_NOP(), /* initd */ INSTRUCTION(orhi), /* orhi */ - INSTRUCTION_FLG(gen_stx, MO_SL), /* stwio */ + INSTRUCTION_FLG(gen_stx, MO_TESL), /* stwio */ INSTRUCTION_FLG(gen_bxx, TCG_COND_LTU), /* bltu */ - INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldwio */ + INSTRUCTION_FLG(gen_ldx, MO_TEUL), /* ldwio */ INSTRUCTION(rdprs), /* rdprs */ INSTRUCTION_ILLEGAL(), INSTRUCTION_FLG(handle_r_type_instr, 0), /* R-Type */ @@ -498,11 +508,10 @@ static void eret(DisasContext *dc, uint32_t code, uint32_t flags) #else if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { TCGv tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPUNios2State, ctrl[CR_ESTATUS])); - gen_helper_eret(cpu_env, tmp, load_gpr(dc, R_EA)); - tcg_temp_free(tmp); + tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPUNios2State, ctrl[CR_ESTATUS])); + gen_helper_eret(tcg_env, tmp, load_gpr(dc, R_EA)); } else { - gen_helper_eret(cpu_env, load_gpr(dc, R_SSTATUS), load_gpr(dc, R_EA)); + gen_helper_eret(tcg_env, load_gpr(dc, R_SSTATUS), load_gpr(dc, R_EA)); } dc->base.is_jmp = DISAS_NORETURN; #endif @@ -528,9 +537,8 @@ static void bret(DisasContext *dc, uint32_t code, uint32_t flags) g_assert_not_reached(); #else TCGv tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPUNios2State, ctrl[CR_BSTATUS])); - gen_helper_eret(cpu_env, tmp, load_gpr(dc, R_BA)); - tcg_temp_free(tmp); + tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPUNios2State, ctrl[CR_BSTATUS])); + gen_helper_eret(tcg_env, tmp, load_gpr(dc, R_BA)); dc->base.is_jmp = DISAS_NORETURN; #endif @@ -594,14 +602,12 @@ static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags) */ t1 = tcg_temp_new(); t2 = tcg_temp_new(); - tcg_gen_ld_tl(t1, cpu_env, offsetof(CPUNios2State, ctrl[CR_IPENDING])); - tcg_gen_ld_tl(t2, cpu_env, offsetof(CPUNios2State, ctrl[CR_IENABLE])); + tcg_gen_ld_tl(t1, tcg_env, offsetof(CPUNios2State, ctrl[CR_IPENDING])); + tcg_gen_ld_tl(t2, tcg_env, offsetof(CPUNios2State, ctrl[CR_IENABLE])); tcg_gen_and_tl(dest, t1, t2); - tcg_temp_free(t1); - tcg_temp_free(t2); break; default: - tcg_gen_ld_tl(dest, cpu_env, + tcg_gen_ld_tl(dest, tcg_env, offsetof(CPUNios2State, ctrl[instr.imm5])); break; } @@ -631,13 +637,13 @@ static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) switch (instr.imm5) { case CR_PTEADDR: - gen_helper_mmu_write_pteaddr(cpu_env, v); + gen_helper_mmu_write_pteaddr(tcg_env, v); break; case CR_TLBACC: - gen_helper_mmu_write_tlbacc(cpu_env, v); + gen_helper_mmu_write_tlbacc(tcg_env, v); break; case CR_TLBMISC: - gen_helper_mmu_write_tlbmisc(cpu_env, v); + gen_helper_mmu_write_tlbmisc(tcg_env, v); break; case CR_STATUS: case CR_IENABLE: @@ -647,7 +653,7 @@ static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) default: if (wr == -1) { /* The register is entirely writable. */ - tcg_gen_st_tl(v, cpu_env, ofs); + tcg_gen_st_tl(v, tcg_env, ofs); } else { /* * The register is partially read-only or reserved: @@ -659,14 +665,12 @@ static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) if (ro != 0) { TCGv o = tcg_temp_new(); - tcg_gen_ld_tl(o, cpu_env, ofs); + tcg_gen_ld_tl(o, tcg_env, ofs); tcg_gen_andi_tl(o, o, ro); tcg_gen_or_tl(n, n, o); - tcg_temp_free(o); } - tcg_gen_st_tl(n, cpu_env, ofs); - tcg_temp_free(n); + tcg_gen_st_tl(n, tcg_env, ofs); } break; } @@ -688,7 +692,7 @@ static void wrprs(DisasContext *dc, uint32_t code, uint32_t flags) g_assert_not_reached(); #else R_TYPE(instr, code); - gen_helper_wrprs(cpu_env, tcg_constant_i32(instr.c), + gen_helper_wrprs(tcg_env, tcg_constant_i32(instr.c), load_gpr(dc, instr.a)); /* * The expected write to PRS[r0] is 0, from CRS[r0]. @@ -753,7 +757,6 @@ static void do_rr_mul_high(DisasContext *dc, uint32_t insn, GenFn4 *fn) fn(discard, dest_gpr(dc, instr.c), load_gpr(dc, instr.a), load_gpr(dc, instr.b)); - tcg_temp_free(discard); } #define gen_rr_mul_high(fname, insn) \ @@ -771,7 +774,6 @@ static void do_rr_shift(DisasContext *dc, uint32_t insn, GenFn3 *fn) tcg_gen_andi_tl(sh, load_gpr(dc, instr.b), 31); fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), sh); - tcg_temp_free(sh); } #define gen_rr_shift(fname, insn) \ @@ -787,14 +789,14 @@ gen_rr_shift(ror, rotr) static void divs(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, (code)); - gen_helper_divs(dest_gpr(dc, instr.c), cpu_env, + gen_helper_divs(dest_gpr(dc, instr.c), tcg_env, load_gpr(dc, instr.a), load_gpr(dc, instr.b)); } static void divu(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, (code)); - gen_helper_divu(dest_gpr(dc, instr.c), cpu_env, + gen_helper_divu(dest_gpr(dc, instr.c), tcg_env, load_gpr(dc, instr.a), load_gpr(dc, instr.b)); } @@ -807,7 +809,7 @@ static void trap(DisasContext *dc, uint32_t code, uint32_t flags) * things easier for cpu_loop if we pop this into env->error_code. */ R_TYPE(instr, code); - tcg_gen_st_i32(tcg_constant_i32(instr.imm5), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(instr.imm5), tcg_env, offsetof(CPUNios2State, error_code)); #endif t_gen_helper_raise_exception(dc, EXCP_TRAP); @@ -817,8 +819,9 @@ static void gen_break(DisasContext *dc, uint32_t code, uint32_t flags) { #ifndef CONFIG_USER_ONLY /* The semihosting instruction is "break 1". */ + bool is_user = FIELD_EX32(dc->tb_flags, TBFLAGS, U); R_TYPE(instr, code); - if (semihosting_enabled() && instr.imm5 == 1) { + if (semihosting_enabled(is_user) && instr.imm5 == 1) { t_gen_helper_raise_exception(dc, EXCP_SEMIHOST); return; } @@ -937,17 +940,15 @@ static const char * const cr_regnames[NUM_CR_REGS] = { }; #endif -#include "exec/gen-icount.h" - /* generate intermediate code for basic block 'tb'. */ static void nios2_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUNios2State *env = cs->env_ptr; + CPUNios2State *env = cpu_env(cs); Nios2CPU *cpu = env_archcpu(env); int page_insns; - dc->mem_idx = cpu_mmu_index(env, false); + dc->mem_idx = cpu_mmu_index(cs, false); dc->cr_state = cpu->cr_state; dc->tb_flags = dc->base.tb->flags; dc->eic_present = cpu->eic_present; @@ -969,7 +970,6 @@ static void nios2_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) static void nios2_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUNios2State *env = cs->env_ptr; const Nios2Instruction *instr; uint32_t code, pc; uint8_t op; @@ -979,7 +979,7 @@ static void nios2_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) dc->base.pc_next = pc + 4; /* Decode an instruction */ - code = cpu_ldl_code(env, pc); + code = cpu_ldl_code(cpu_env(cs), pc); op = get_opcode(code); if (unlikely(op >= ARRAY_SIZE(i_type_instructions))) { @@ -991,10 +991,6 @@ static void nios2_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) instr = &i_type_instructions[op]; instr->handler(dc, code, instr->flags); - - if (dc->sink) { - tcg_temp_free(dc->sink); - } } static void nios2_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) @@ -1038,10 +1034,11 @@ static const TranslatorOps nios2_tr_ops = { .disas_log = nios2_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc; - translator_loop(&nios2_tr_ops, &dc.base, cs, tb, max_insns); + translator_loop(cs, tb, max_insns, pc, host_pc, &nios2_tr_ops, &dc.base); } void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags) @@ -1086,7 +1083,7 @@ void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags) void nios2_tcg_init(void) { #ifndef CONFIG_USER_ONLY - TCGv_ptr crs = tcg_global_mem_new_ptr(cpu_env, + TCGv_ptr crs = tcg_global_mem_new_ptr(tcg_env, offsetof(CPUNios2State, regs), "crs"); for (int i = 0; i < NUM_GP_REGS; i++) { @@ -1099,18 +1096,12 @@ void nios2_tcg_init(void) #endif for (int i = 0; i < NUM_GP_REGS; i++) { - cpu_R[i] = tcg_global_mem_new(cpu_env, offsetof_regs0(i), + cpu_R[i] = tcg_global_mem_new(tcg_env, offsetof_regs0(i), gr_regnames[i]); } #undef offsetof_regs0 - cpu_pc = tcg_global_mem_new(cpu_env, + cpu_pc = tcg_global_mem_new(tcg_env, offsetof(CPUNios2State, pc), "pc"); } - -void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, - target_ulong *data) -{ - env->pc = data[0]; -} diff --git a/target/openrisc/cpu-param.h b/target/openrisc/cpu-param.h index 73be699f36..3f08207485 100644 --- a/target/openrisc/cpu-param.h +++ b/target/openrisc/cpu-param.h @@ -12,6 +12,5 @@ #define TARGET_PAGE_BITS 13 #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -#define NB_MMU_MODES 3 #endif diff --git a/target/openrisc/cpu-qom.h b/target/openrisc/cpu-qom.h new file mode 100644 index 0000000000..14bac33312 --- /dev/null +++ b/target/openrisc/cpu-qom.h @@ -0,0 +1,21 @@ +/* + * QEMU OpenRISC CPU QOM header (target agnostic) + * + * Copyright (c) 2011-2012 Jia Liu + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef QEMU_OPENRISC_CPU_QOM_H +#define QEMU_OPENRISC_CPU_QOM_H + +#include "hw/core/cpu.h" + +#define TYPE_OPENRISC_CPU "or1k-cpu" + +OBJECT_DECLARE_CPU_TYPE(OpenRISCCPU, OpenRISCCPUClass, OPENRISC_CPU) + +#define OPENRISC_CPU_TYPE_SUFFIX "-" TYPE_OPENRISC_CPU +#define OPENRISC_CPU_TYPE_NAME(model) model OPENRISC_CPU_TYPE_SUFFIX + +#endif diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 41d1b2a24a..33c45dbf04 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -22,6 +22,8 @@ #include "qemu/qemu-print.h" #include "cpu.h" #include "exec/exec-all.h" +#include "fpu/softfloat-helpers.h" +#include "tcg/tcg.h" static void openrisc_cpu_set_pc(CPUState *cs, vaddr value) { @@ -31,14 +33,34 @@ static void openrisc_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.dflag = 0; } +static vaddr openrisc_cpu_get_pc(CPUState *cs) +{ + OpenRISCCPU *cpu = OPENRISC_CPU(cs); + + return cpu->env.pc; +} + static void openrisc_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { OpenRISCCPU *cpu = OPENRISC_CPU(cs); + tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); cpu->env.pc = tb->pc; } +static void openrisc_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + OpenRISCCPU *cpu = OPENRISC_CPU(cs); + + cpu->env.pc = data[0]; + cpu->env.dflag = data[1] & 1; + if (data[1] & 2) { + cpu->env.ppc = cpu->env.pc - 4; + } +} static bool openrisc_cpu_has_work(CPUState *cs) { @@ -46,27 +68,44 @@ static bool openrisc_cpu_has_work(CPUState *cs) CPU_INTERRUPT_TIMER); } +static int openrisc_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + CPUOpenRISCState *env = cpu_env(cs); + + if (env->sr & (ifetch ? SR_IME : SR_DME)) { + /* The mmu is enabled; test supervisor state. */ + return env->sr & SR_SM ? MMU_SUPERVISOR_IDX : MMU_USER_IDX; + } + + return MMU_NOMMU_IDX; /* mmu is disabled */ +} + static void openrisc_disas_set_info(CPUState *cpu, disassemble_info *info) { info->print_insn = print_insn_or1k; } -static void openrisc_cpu_reset(DeviceState *dev) +static void openrisc_cpu_reset_hold(Object *obj) { - CPUState *s = CPU(dev); - OpenRISCCPU *cpu = OPENRISC_CPU(s); - OpenRISCCPUClass *occ = OPENRISC_CPU_GET_CLASS(cpu); + CPUState *cs = CPU(obj); + OpenRISCCPU *cpu = OPENRISC_CPU(cs); + OpenRISCCPUClass *occ = OPENRISC_CPU_GET_CLASS(obj); - occ->parent_reset(dev); + if (occ->parent_phases.hold) { + occ->parent_phases.hold(obj); + } memset(&cpu->env, 0, offsetof(CPUOpenRISCState, end_reset_fields)); cpu->env.pc = 0x100; cpu->env.sr = SR_FO | SR_SM; cpu->env.lock_addr = -1; - s->exception_index = -1; + cs->exception_index = -1; cpu_set_fpcsr(&cpu->env, 0); + set_float_detect_tininess(float_tininess_before_rounding, + &cpu->env.fp_status); + #ifndef CONFIG_USER_ONLY cpu->env.picmr = 0x00000000; cpu->env.picsr = 0x00000000; @@ -98,7 +137,6 @@ static void openrisc_cpu_set_irq(void *opaque, int irq, int level) cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - cpu->env.picsr = 0; } } #endif @@ -123,12 +161,8 @@ static void openrisc_cpu_realizefn(DeviceState *dev, Error **errp) static void openrisc_cpu_initfn(Object *obj) { - OpenRISCCPU *cpu = OPENRISC_CPU(obj); - - cpu_set_cpustate_pointers(cpu); - #ifndef CONFIG_USER_ONLY - qdev_init_gpio_in_named(DEVICE(cpu), openrisc_cpu_set_irq, "IRQ", NR_IRQS); + qdev_init_gpio_in_named(DEVICE(obj), openrisc_cpu_set_irq, "IRQ", NR_IRQS); #endif } @@ -142,10 +176,7 @@ static ObjectClass *openrisc_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf(OPENRISC_CPU_TYPE_NAME("%s"), cpu_model); oc = object_class_by_name(typename); g_free(typename); - if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_OPENRISC_CPU) || - object_class_is_abstract(oc))) { - return NULL; - } + return oc; } @@ -194,9 +225,10 @@ static const struct SysemuCPUOps openrisc_sysemu_ops = { #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps openrisc_tcg_ops = { +static const TCGCPUOps openrisc_tcg_ops = { .initialize = openrisc_translate_init, .synchronize_from_tb = openrisc_cpu_synchronize_from_tb, + .restore_state_to_opc = openrisc_restore_state_to_opc, #ifndef CONFIG_USER_ONLY .tlb_fill = openrisc_cpu_tlb_fill, @@ -210,15 +242,19 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) OpenRISCCPUClass *occ = OPENRISC_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(occ); DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); device_class_set_parent_realize(dc, openrisc_cpu_realizefn, &occ->parent_realize); - device_class_set_parent_reset(dc, openrisc_cpu_reset, &occ->parent_reset); + resettable_class_set_parent_phases(rc, NULL, openrisc_cpu_reset_hold, NULL, + &occ->parent_phases); cc->class_by_name = openrisc_cpu_class_by_name; cc->has_work = openrisc_cpu_has_work; + cc->mmu_index = openrisc_cpu_mmu_index; cc->dump_state = openrisc_cpu_dump_state; cc->set_pc = openrisc_cpu_set_pc; + cc->get_pc = openrisc_cpu_get_pc; cc->gdb_read_register = openrisc_cpu_gdb_read_register; cc->gdb_write_register = openrisc_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY @@ -230,48 +266,6 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) cc->tcg_ops = &openrisc_tcg_ops; } -/* Sort alphabetically by type name, except for "any". */ -static gint openrisc_cpu_list_compare(gconstpointer a, gconstpointer b) -{ - ObjectClass *class_a = (ObjectClass *)a; - ObjectClass *class_b = (ObjectClass *)b; - const char *name_a, *name_b; - - name_a = object_class_get_name(class_a); - name_b = object_class_get_name(class_b); - if (strcmp(name_a, "any-" TYPE_OPENRISC_CPU) == 0) { - return 1; - } else if (strcmp(name_b, "any-" TYPE_OPENRISC_CPU) == 0) { - return -1; - } else { - return strcmp(name_a, name_b); - } -} - -static void openrisc_cpu_list_entry(gpointer data, gpointer user_data) -{ - ObjectClass *oc = data; - const char *typename; - char *name; - - typename = object_class_get_name(oc); - name = g_strndup(typename, - strlen(typename) - strlen("-" TYPE_OPENRISC_CPU)); - qemu_printf(" %s\n", name); - g_free(name); -} - -void cpu_openrisc_list(void) -{ - GSList *list; - - list = object_class_get_list(TYPE_OPENRISC_CPU, false); - list = g_slist_sort(list, openrisc_cpu_list_compare); - qemu_printf("Available CPUs:\n"); - g_slist_foreach(list, openrisc_cpu_list_entry, NULL); - g_slist_free(list); -} - #define DEFINE_OPENRISC_CPU_TYPE(cpu_model, initfn) \ { \ .parent = TYPE_OPENRISC_CPU, \ @@ -284,6 +278,7 @@ static const TypeInfo openrisc_cpus_type_infos[] = { .name = TYPE_OPENRISC_CPU, .parent = TYPE_CPU, .instance_size = sizeof(OpenRISCCPU), + .instance_align = __alignof(OpenRISCCPU), .instance_init = openrisc_cpu_initfn, .abstract = true, .class_size = sizeof(OpenRISCCPUClass), diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index b9584f10d4..b1b7db5cbd 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -20,29 +20,24 @@ #ifndef OPENRISC_CPU_H #define OPENRISC_CPU_H +#include "cpu-qom.h" #include "exec/cpu-defs.h" #include "fpu/softfloat-types.h" -#include "hw/core/cpu.h" -#include "qom/object.h" -#define TYPE_OPENRISC_CPU "or1k-cpu" - -OBJECT_DECLARE_CPU_TYPE(OpenRISCCPU, OpenRISCCPUClass, OPENRISC_CPU) +#define TCG_GUEST_DEFAULT_MO (0) /** * OpenRISCCPUClass: * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. + * @parent_phases: The parent class' reset phase handlers. * * A OpenRISC CPU model. */ struct OpenRISCCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; - DeviceReset parent_reset; + ResettablePhases parent_phases; }; #define TARGET_INSN_START_EXTRA_WORDS 1 @@ -288,7 +283,7 @@ typedef struct CPUArchState { int is_counting; uint32_t picmr; /* Interrupt mask register */ - uint32_t picsr; /* Interrupt contrl register*/ + uint32_t picsr; /* Interrupt control register */ #endif } CPUOpenRISCState; @@ -299,26 +294,20 @@ typedef struct CPUArchState { * A OpenRISC CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUOpenRISCState env; }; - -void cpu_openrisc_list(void); 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, GByteArray *buf, int reg); int openrisc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void openrisc_translate_init(void); int print_insn_or1k(bfd_vma addr, disassemble_info *info); -#define cpu_list cpu_openrisc_list - #ifndef CONFIG_USER_ONLY +hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); + bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); @@ -341,8 +330,6 @@ void cpu_openrisc_count_start(OpenRISCCPU *cpu); void cpu_openrisc_count_stop(OpenRISCCPU *cpu); #endif -#define OPENRISC_CPU_TYPE_SUFFIX "-" TYPE_OPENRISC_CPU -#define OPENRISC_CPU_TYPE_NAME(model) model OPENRISC_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_OPENRISC_CPU #include "exec/cpu-all.h" @@ -364,9 +351,8 @@ static inline void cpu_set_gpr(CPUOpenRISCState *env, int i, uint32_t val) env->shadow_gpr[0][i] = val; } -static inline void cpu_get_tb_cpu_state(CPUOpenRISCState *env, - target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) +static inline void cpu_get_tb_cpu_state(CPUOpenRISCState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { *pc = env->pc; *cs_base = 0; @@ -375,18 +361,6 @@ static inline void cpu_get_tb_cpu_state(CPUOpenRISCState *env, | (env->sr & (SR_SM | SR_DME | SR_IME | SR_OVE)); } -static inline int cpu_mmu_index(CPUOpenRISCState *env, bool ifetch) -{ - int ret = MMU_NOMMU_IDX; /* mmu is disabled */ - - if (env->sr & (ifetch ? SR_IME : SR_DME)) { - /* The mmu is enabled; test supervisor state. */ - ret = env->sr & SR_SM ? MMU_SUPERVISOR_IDX : MMU_USER_IDX; - } - - return ret; -} - static inline uint32_t cpu_get_sr(const CPUOpenRISCState *env) { return (env->sr diff --git a/target/openrisc/fpu_helper.c b/target/openrisc/fpu_helper.c index f9e34fa2cc..8b81d2f62f 100644 --- a/target/openrisc/fpu_helper.c +++ b/target/openrisc/fpu_helper.c @@ -20,8 +20,8 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/exec-all.h" #include "exec/helper-proto.h" -#include "exception.h" #include "fpu/softfloat.h" static int ieee_ex_to_openrisc(int fexcp) @@ -45,6 +45,15 @@ static int ieee_ex_to_openrisc(int fexcp) return ret; } +static G_NORETURN +void do_fpe(CPUOpenRISCState *env, uintptr_t pc) +{ + CPUState *cs = env_cpu(env); + + cs->exception_index = EXCP_FPE; + cpu_loop_exit_restore(cs, pc); +} + void HELPER(update_fpcsr)(CPUOpenRISCState *env) { int tmp = get_float_exception_flags(&env->fp_status); @@ -55,7 +64,7 @@ void HELPER(update_fpcsr)(CPUOpenRISCState *env) if (tmp) { env->fpcsr |= tmp; if (env->fpcsr & FPCSR_FPEE) { - helper_exception(env, EXCP_FPE); + do_fpe(env, GETPC()); } } } diff --git a/target/openrisc/gdbstub.c b/target/openrisc/gdbstub.c index 095bf76c12..c2a77d5d4d 100644 --- a/target/openrisc/gdbstub.c +++ b/target/openrisc/gdbstub.c @@ -19,12 +19,11 @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" int openrisc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - OpenRISCCPU *cpu = OPENRISC_CPU(cs); - CPUOpenRISCState *env = &cpu->env; + CPUOpenRISCState *env = cpu_env(cs); if (n < 32) { return gdb_get_reg32(mem_buf, cpu_get_gpr(env, n)); @@ -48,9 +47,8 @@ int openrisc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int openrisc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - OpenRISCCPU *cpu = OPENRISC_CPU(cs); CPUClass *cc = CPU_GET_CLASS(cs); - CPUOpenRISCState *env = &cpu->env; + CPUOpenRISCState *env = cpu_env(cs); uint32_t tmp; if (n > cc->gdb_num_core_regs) { diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c index e5724f5371..b3b5b40577 100644 --- a/target/openrisc/interrupt.c +++ b/target/openrisc/interrupt.c @@ -21,7 +21,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/host-utils.h" #ifndef CONFIG_USER_ONLY #include "hw/loader.h" @@ -29,14 +29,11 @@ void openrisc_cpu_do_interrupt(CPUState *cs) { - OpenRISCCPU *cpu = OPENRISC_CPU(cs); - CPUOpenRISCState *env = &cpu->env; + CPUOpenRISCState *env = cpu_env(cs); int exception = cs->exception_index; env->epcr = env->pc; - if (exception == EXCP_SYSCALL) { - env->epcr += 4; - } + /* When we have an illegal instruction the error effective address shall be set to the illegal instruction address. */ if (exception == EXCP_ILLEGAL) { @@ -63,6 +60,9 @@ void openrisc_cpu_do_interrupt(CPUState *cs) env->epcr -= 4; } else { env->sr &= ~SR_DSX; + if (exception == EXCP_SYSCALL || exception == EXCP_FPE) { + env->epcr += 4; + } } if (exception > 0 && exception < EXCP_NR) { @@ -83,7 +83,9 @@ void openrisc_cpu_do_interrupt(CPUState *cs) [EXCP_TRAP] = "TRAP", }; - qemu_log_mask(CPU_LOG_INT, "INT: %s\n", int_name[exception]); + qemu_log_mask(CPU_LOG_INT, "CPU: %d INT: %s\n", + cs->cpu_index, + int_name[exception]); hwaddr vect_pc = exception << 8; if (env->cpucfgr & CPUCFGR_EVBARP) { @@ -102,8 +104,7 @@ void openrisc_cpu_do_interrupt(CPUState *cs) bool openrisc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { - OpenRISCCPU *cpu = OPENRISC_CPU(cs); - CPUOpenRISCState *env = &cpu->env; + CPUOpenRISCState *env = cpu_env(cs); int idx = -1; if ((interrupt_request & CPU_INTERRUPT_HARD) && (env->sr & SR_IEE)) { diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index b7d7388640..3574e571cb 100644 --- a/target/openrisc/machine.c +++ b/target/openrisc/machine.c @@ -25,7 +25,7 @@ static const VMStateDescription vmstate_tlb_entry = { .name = "tlb_entry", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINTTL(mr, OpenRISCTLBEntry), VMSTATE_UINTTL(tr, OpenRISCTLBEntry), VMSTATE_END_OF_LIST() @@ -36,7 +36,7 @@ static const VMStateDescription vmstate_cpu_tlb = { .name = "cpu_tlb", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(itlb, CPUOpenRISCTLBContext, TLB_SIZE, 0, vmstate_tlb_entry, OpenRISCTLBEntry), VMSTATE_STRUCT_ARRAY(dtlb, CPUOpenRISCTLBContext, TLB_SIZE, 0, @@ -71,7 +71,7 @@ static const VMStateDescription vmstate_env = { .name = "env", .version_id = 6, .minimum_version_id = 6, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINTTL_2DARRAY(shadow_gpr, CPUOpenRISCState, 16, 32), VMSTATE_UINTTL(pc, CPUOpenRISCState), VMSTATE_UINTTL(ppc, CPUOpenRISCState), @@ -135,7 +135,7 @@ const VMStateDescription vmstate_openrisc_cpu = { .version_id = 1, .minimum_version_id = 1, .post_load = cpu_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_CPU(), VMSTATE_STRUCT(env, OpenRISCCPU, 1, vmstate_env, CPUOpenRISCState), VMSTATE_END_OF_LIST() diff --git a/target/openrisc/meson.build b/target/openrisc/meson.build index 84322086ec..31608b6dc7 100644 --- a/target/openrisc/meson.build +++ b/target/openrisc/meson.build @@ -14,12 +14,12 @@ openrisc_ss.add(files( 'translate.c', )) -openrisc_softmmu_ss = ss.source_set() -openrisc_softmmu_ss.add(files( +openrisc_system_ss = ss.source_set() +openrisc_system_ss.add(files( 'interrupt.c', 'machine.c', 'mmu.c', )) target_arch += {'openrisc': openrisc_ss} -target_softmmu_arch += {'openrisc': openrisc_softmmu_ss} +target_system_arch += {'openrisc': openrisc_system_ss} diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index d7e1320998..603c26715e 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -22,7 +22,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "hw/loader.h" @@ -148,7 +148,13 @@ hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) case SR_DME | SR_IME: /* The mmu is definitely enabled. */ excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, - PAGE_EXEC | PAGE_READ | PAGE_WRITE, + PAGE_READ, + (sr & SR_SM) != 0); + if (!excp) { + return phys_addr; + } + excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, + PAGE_EXEC, (sr & SR_SM) != 0); return excp ? -1 : phys_addr; diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index 48674231e7..77567afba4 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -26,33 +26,54 @@ #ifndef CONFIG_USER_ONLY #include "hw/boards.h" #endif +#include "tcg/insn-start-words.h" #define TO_SPR(group, number) (((group) << 11) + (number)) +static inline bool is_user(CPUOpenRISCState *env) +{ +#ifdef CONFIG_USER_ONLY + return true; +#else + return (env->sr & SR_SM) == 0; +#endif +} + void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) { -#ifndef CONFIG_USER_ONLY OpenRISCCPU *cpu = env_archcpu(env); +#ifndef CONFIG_USER_ONLY CPUState *cs = env_cpu(env); target_ulong mr; int idx; #endif + /* Handle user accessible SPRs first. */ switch (spr) { + case TO_SPR(0, 20): /* FPCSR */ + cpu_set_fpcsr(env, rb); + return; + } + + if (is_user(env)) { + raise_exception(cpu, EXCP_ILLEGAL); + } + #ifndef CONFIG_USER_ONLY + switch (spr) { case TO_SPR(0, 11): /* EVBAR */ env->evbar = rb; break; case TO_SPR(0, 16): /* NPC */ - cpu_restore_state(cs, GETPC(), true); + cpu_restore_state(cs, GETPC()); /* ??? Mirror or1ksim in not trashing delayed branch state when "jumping" to the current instruction. */ if (env->pc != rb) { env->pc = rb; env->dflag = 0; - cpu_loop_exit(cs); } + cpu_loop_exit(cs); break; case TO_SPR(0, 17): /* SR */ @@ -131,7 +152,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) case TO_SPR(8, 0): /* PMR */ env->pmr = rb; if (env->pmr & PMR_DME || env->pmr & PMR_SME) { - cpu_restore_state(cs, GETPC(), true); + cpu_restore_state(cs, GETPC()); env->pc += 4; cs->halted = 1; raise_exception(cpu, EXCP_HALTED); @@ -139,12 +160,20 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) break; case TO_SPR(9, 0): /* PICMR */ env->picmr = rb; + bql_lock(); + if (env->picsr & env->picmr) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + bql_unlock(); break; case TO_SPR(9, 2): /* PICSR */ env->picsr &= ~rb; break; case TO_SPR(10, 0): /* TTMR */ { + bql_lock(); if ((env->ttmr & TTMR_M) ^ (rb & TTMR_M)) { switch (rb & TTMR_M) { case TIMER_NONE: @@ -168,35 +197,44 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) env->ttmr = rb & ~TTMR_IP; cs->interrupt_request &= ~CPU_INTERRUPT_TIMER; } - cpu_openrisc_timer_update(cpu); + bql_unlock(); } break; case TO_SPR(10, 1): /* TTCR */ + bql_lock(); cpu_openrisc_count_set(cpu, rb); cpu_openrisc_timer_update(cpu); - break; -#endif - - case TO_SPR(0, 20): /* FPCSR */ - cpu_set_fpcsr(env, rb); + bql_unlock(); break; } +#endif } target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, target_ulong spr) { -#ifndef CONFIG_USER_ONLY - MachineState *ms = MACHINE(qdev_get_machine()); OpenRISCCPU *cpu = env_archcpu(env); +#ifndef CONFIG_USER_ONLY + uint64_t data[TARGET_INSN_START_WORDS]; + MachineState *ms = MACHINE(qdev_get_machine()); CPUState *cs = env_cpu(env); int idx; #endif + /* Handle user accessible SPRs first. */ switch (spr) { + case TO_SPR(0, 20): /* FPCSR */ + return env->fpcsr; + } + + if (is_user(env)) { + raise_exception(cpu, EXCP_ILLEGAL); + } + #ifndef CONFIG_USER_ONLY + switch (spr) { case TO_SPR(0, 0): /* VR */ return env->vr; @@ -222,14 +260,20 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, return env->evbar; case TO_SPR(0, 16): /* NPC (equals PC) */ - cpu_restore_state(cs, GETPC(), false); + if (cpu_unwind_state_data(cs, GETPC(), data)) { + return data[0]; + } return env->pc; case TO_SPR(0, 17): /* SR */ return cpu_get_sr(env); case TO_SPR(0, 18): /* PPC */ - cpu_restore_state(cs, GETPC(), false); + if (cpu_unwind_state_data(cs, GETPC(), data)) { + if (data[1] & 2) { + return data[0] - 4; + } + } return env->ppc; case TO_SPR(0, 32): /* EPCR */ @@ -303,13 +347,12 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, return env->ttmr; case TO_SPR(10, 1): /* TTCR */ + bql_lock(); cpu_openrisc_count_update(cpu); + bql_unlock(); return cpu_openrisc_count_get(cpu); -#endif - - case TO_SPR(0, 20): /* FPCSR */ - return env->fpcsr; } +#endif /* for rd is passed in, if rd unchanged, just keep it back. */ return rd; diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 7b8ad43d5f..23fff46084 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -26,15 +26,18 @@ #include "qemu/log.h" #include "qemu/bitops.h" #include "qemu/qemu-print.h" -#include "exec/cpu_ldst.h" #include "exec/translator.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" -#include "exec/gen-icount.h" #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + + /* is_jmp field values */ #define DISAS_EXIT DISAS_TARGET_0 /* force exit to main loop */ #define DISAS_JUMP DISAS_TARGET_1 /* exit via jmp_pc/jmp_pc_imm */ @@ -92,37 +95,37 @@ void openrisc_translate_init(void) }; int i; - cpu_sr = tcg_global_mem_new(cpu_env, + cpu_sr = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, sr), "sr"); - cpu_dflag = tcg_global_mem_new_i32(cpu_env, + cpu_dflag = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, dflag), "dflag"); - cpu_pc = tcg_global_mem_new(cpu_env, + cpu_pc = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, pc), "pc"); - cpu_ppc = tcg_global_mem_new(cpu_env, + cpu_ppc = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, ppc), "ppc"); - jmp_pc = tcg_global_mem_new(cpu_env, + jmp_pc = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, jmp_pc), "jmp_pc"); - cpu_sr_f = tcg_global_mem_new(cpu_env, + cpu_sr_f = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, sr_f), "sr_f"); - cpu_sr_cy = tcg_global_mem_new(cpu_env, + cpu_sr_cy = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, sr_cy), "sr_cy"); - cpu_sr_ov = tcg_global_mem_new(cpu_env, + cpu_sr_ov = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, sr_ov), "sr_ov"); - cpu_lock_addr = tcg_global_mem_new(cpu_env, + cpu_lock_addr = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, lock_addr), "lock_addr"); - cpu_lock_value = tcg_global_mem_new(cpu_env, + cpu_lock_value = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, lock_value), "lock_value"); - fpcsr = tcg_global_mem_new_i32(cpu_env, + fpcsr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, fpcsr), "fpcsr"); - cpu_mac = tcg_global_mem_new_i64(cpu_env, + cpu_mac = tcg_global_mem_new_i64(tcg_env, offsetof(CPUOpenRISCState, mac), "mac"); for (i = 0; i < 32; i++) { - cpu_regs[i] = tcg_global_mem_new(cpu_env, + cpu_regs[i] = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, shadow_gpr[0][i]), regnames[i]); @@ -131,7 +134,7 @@ void openrisc_translate_init(void) static void gen_exception(DisasContext *dc, unsigned int excp) { - gen_helper_exception(cpu_env, tcg_constant_i32(excp)); + gen_helper_exception(tcg_env, tcg_constant_i32(excp)); } static void gen_illegal_exception(DisasContext *dc) @@ -179,21 +182,21 @@ static void check_r0_write(DisasContext *dc, int reg) static void gen_ove_cy(DisasContext *dc) { if (dc->tb_flags & SR_OVE) { - gen_helper_ove_cy(cpu_env); + gen_helper_ove_cy(tcg_env); } } static void gen_ove_ov(DisasContext *dc) { if (dc->tb_flags & SR_OVE) { - gen_helper_ove_ov(cpu_env); + gen_helper_ove_ov(tcg_env); } } static void gen_ove_cyov(DisasContext *dc) { if (dc->tb_flags & SR_OVE) { - gen_helper_ove_cyov(cpu_env); + gen_helper_ove_cyov(tcg_env); } } @@ -206,10 +209,8 @@ static void gen_add(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) tcg_gen_xor_tl(cpu_sr_ov, srca, srcb); tcg_gen_xor_tl(t0, res, srcb); tcg_gen_andc_tl(cpu_sr_ov, t0, cpu_sr_ov); - tcg_temp_free(t0); tcg_gen_mov_tl(dest, res); - tcg_temp_free(res); gen_ove_cyov(dc); } @@ -224,10 +225,8 @@ static void gen_addc(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) tcg_gen_xor_tl(cpu_sr_ov, srca, srcb); tcg_gen_xor_tl(t0, res, srcb); tcg_gen_andc_tl(cpu_sr_ov, t0, cpu_sr_ov); - tcg_temp_free(t0); tcg_gen_mov_tl(dest, res); - tcg_temp_free(res); gen_ove_cyov(dc); } @@ -243,7 +242,6 @@ static void gen_sub(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) tcg_gen_setcond_tl(TCG_COND_LTU, cpu_sr_cy, srca, srcb); tcg_gen_mov_tl(dest, res); - tcg_temp_free(res); gen_ove_cyov(dc); } @@ -254,10 +252,8 @@ static void gen_mul(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) tcg_gen_muls2_tl(dest, cpu_sr_ov, srca, srcb); tcg_gen_sari_tl(t0, dest, TARGET_LONG_BITS - 1); - tcg_gen_setcond_tl(TCG_COND_NE, cpu_sr_ov, cpu_sr_ov, t0); - tcg_temp_free(t0); + tcg_gen_negsetcond_tl(TCG_COND_NE, cpu_sr_ov, cpu_sr_ov, t0); - tcg_gen_neg_tl(cpu_sr_ov, cpu_sr_ov); gen_ove_ov(dc); } @@ -275,10 +271,9 @@ static void gen_div(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_ov, srcb, 0); /* The result of divide-by-zero is undefined. - Supress the host-side exception by dividing by 1. */ + Suppress the host-side exception by dividing by 1. */ tcg_gen_or_tl(t0, srcb, cpu_sr_ov); tcg_gen_div_tl(dest, srca, t0); - tcg_temp_free(t0); tcg_gen_neg_tl(cpu_sr_ov, cpu_sr_ov); gen_ove_ov(dc); @@ -290,10 +285,9 @@ static void gen_divu(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_cy, srcb, 0); /* The result of divide-by-zero is undefined. - Supress the host-side exception by dividing by 1. */ + Suppress the host-side exception by dividing by 1. */ tcg_gen_or_tl(t0, srcb, cpu_sr_cy); tcg_gen_divu_tl(dest, srca, t0); - tcg_temp_free(t0); gen_ove_cy(dc); } @@ -313,15 +307,11 @@ static void gen_muld(DisasContext *dc, TCGv srca, TCGv srcb) tcg_gen_muls2_i64(cpu_mac, high, t1, t2); tcg_gen_sari_i64(t1, cpu_mac, 63); - tcg_gen_setcond_i64(TCG_COND_NE, t1, t1, high); - tcg_temp_free_i64(high); + tcg_gen_negsetcond_i64(TCG_COND_NE, t1, t1, high); tcg_gen_trunc_i64_tl(cpu_sr_ov, t1); - tcg_gen_neg_tl(cpu_sr_ov, cpu_sr_ov); gen_ove_ov(dc); } - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); } static void gen_muldu(DisasContext *dc, TCGv srca, TCGv srcb) @@ -340,12 +330,9 @@ static void gen_muldu(DisasContext *dc, TCGv srca, TCGv srcb) tcg_gen_mulu2_i64(cpu_mac, high, t1, t2); tcg_gen_setcondi_i64(TCG_COND_NE, high, high, 0); tcg_gen_trunc_i64_tl(cpu_sr_cy, high); - tcg_temp_free_i64(high); gen_ove_cy(dc); } - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); } static void gen_mac(DisasContext *dc, TCGv srca, TCGv srcb) @@ -362,14 +349,12 @@ static void gen_mac(DisasContext *dc, TCGv srca, TCGv srcb) tcg_gen_add_i64(cpu_mac, cpu_mac, t1); tcg_gen_xor_i64(t1, t1, cpu_mac); tcg_gen_andc_i64(t1, t1, t2); - tcg_temp_free_i64(t2); #if TARGET_LONG_BITS == 32 tcg_gen_extrh_i64_i32(cpu_sr_ov, t1); #else tcg_gen_mov_i64(cpu_sr_ov, t1); #endif - tcg_temp_free_i64(t1); gen_ove_ov(dc); } @@ -382,13 +367,11 @@ static void gen_macu(DisasContext *dc, TCGv srca, TCGv srcb) tcg_gen_extu_tl_i64(t1, srca); tcg_gen_extu_tl_i64(t2, srcb); tcg_gen_mul_i64(t1, t1, t2); - tcg_temp_free_i64(t2); /* Note that overflow is only computed during addition stage. */ tcg_gen_add_i64(cpu_mac, cpu_mac, t1); tcg_gen_setcond_i64(TCG_COND_LTU, t1, cpu_mac, t1); tcg_gen_trunc_i64_tl(cpu_sr_cy, t1); - tcg_temp_free_i64(t1); gen_ove_cy(dc); } @@ -407,14 +390,12 @@ static void gen_msb(DisasContext *dc, TCGv srca, TCGv srcb) tcg_gen_sub_i64(cpu_mac, cpu_mac, t1); tcg_gen_xor_i64(t1, t1, cpu_mac); tcg_gen_and_i64(t1, t1, t2); - tcg_temp_free_i64(t2); #if TARGET_LONG_BITS == 32 tcg_gen_extrh_i64_i32(cpu_sr_ov, t1); #else tcg_gen_mov_i64(cpu_sr_ov, t1); #endif - tcg_temp_free_i64(t1); gen_ove_ov(dc); } @@ -432,8 +413,6 @@ static void gen_msbu(DisasContext *dc, TCGv srca, TCGv srcb) tcg_gen_setcond_i64(TCG_COND_LTU, t2, cpu_mac, t1); tcg_gen_sub_i64(cpu_mac, cpu_mac, t1); tcg_gen_trunc_i64_tl(cpu_sr_cy, t2); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t1); gen_ove_cy(dc); } @@ -672,7 +651,6 @@ static bool trans_l_lwa(DisasContext *dc, arg_load *a) tcg_gen_qemu_ld_tl(cpu_R(dc, a->d), ea, dc->mem_idx, MO_TEUL); tcg_gen_mov_tl(cpu_lock_addr, ea); tcg_gen_mov_tl(cpu_lock_value, cpu_R(dc, a->d)); - tcg_temp_free(ea); return true; } @@ -684,7 +662,6 @@ static void do_load(DisasContext *dc, arg_load *a, MemOp mop) ea = tcg_temp_new(); tcg_gen_addi_tl(ea, cpu_R(dc, a->a), a->i); tcg_gen_qemu_ld_tl(cpu_R(dc, a->d), ea, dc->mem_idx, mop); - tcg_temp_free(ea); } static bool trans_l_lwz(DisasContext *dc, arg_load *a) @@ -734,13 +711,11 @@ static bool trans_l_swa(DisasContext *dc, arg_store *a) lab_fail = gen_new_label(); lab_done = gen_new_label(); tcg_gen_brcond_tl(TCG_COND_NE, ea, cpu_lock_addr, lab_fail); - tcg_temp_free(ea); val = tcg_temp_new(); tcg_gen_atomic_cmpxchg_tl(val, cpu_lock_addr, cpu_lock_value, cpu_R(dc, a->b), dc->mem_idx, MO_TEUL); tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, val, cpu_lock_value); - tcg_temp_free(val); tcg_gen_br(lab_done); @@ -757,7 +732,6 @@ static void do_store(DisasContext *dc, arg_store *a, MemOp mop) TCGv t0 = tcg_temp_new(); tcg_gen_addi_tl(t0, cpu_R(dc, a->a), a->i); tcg_gen_qemu_st_tl(cpu_R(dc, a->b), t0, dc->mem_idx, mop); - tcg_temp_free(t0); } static bool trans_l_sw(DisasContext *dc, arg_store *a) @@ -846,46 +820,11 @@ static bool trans_l_xori(DisasContext *dc, arg_rri *a) static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a) { + TCGv spr = tcg_temp_new(); + check_r0_write(dc, a->d); - if (is_user(dc)) { - gen_illegal_exception(dc); - } else { - TCGv spr = tcg_temp_new(); - - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - if (dc->delayed_branch) { - tcg_gen_mov_tl(cpu_pc, jmp_pc); - tcg_gen_discard_tl(jmp_pc); - } else { - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next + 4); - } - dc->base.is_jmp = DISAS_EXIT; - } - - tcg_gen_ori_tl(spr, cpu_R(dc, a->a), a->k); - gen_helper_mfspr(cpu_R(dc, a->d), cpu_env, cpu_R(dc, a->d), spr); - tcg_temp_free(spr); - } - return true; -} - -static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a) -{ - if (is_user(dc)) { - gen_illegal_exception(dc); - } else { - TCGv spr; - - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - /* For SR, we will need to exit the TB to recognize the new - * exception state. For NPC, in theory this counts as a branch - * (although the SPR only exists for use by an ICE). Save all - * of the cpu state first, allowing it to be overwritten. - */ + if (translator_io_start(&dc->base)) { if (dc->delayed_branch) { tcg_gen_mov_tl(cpu_pc, jmp_pc); tcg_gen_discard_tl(jmp_pc); @@ -893,12 +832,35 @@ static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a) tcg_gen_movi_tl(cpu_pc, dc->base.pc_next + 4); } dc->base.is_jmp = DISAS_EXIT; - - spr = tcg_temp_new(); - tcg_gen_ori_tl(spr, cpu_R(dc, a->a), a->k); - gen_helper_mtspr(cpu_env, spr, cpu_R(dc, a->b)); - tcg_temp_free(spr); } + + tcg_gen_ori_tl(spr, cpu_R(dc, a->a), a->k); + gen_helper_mfspr(cpu_R(dc, a->d), tcg_env, cpu_R(dc, a->d), spr); + return true; +} + +static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a) +{ + TCGv spr = tcg_temp_new(); + + translator_io_start(&dc->base); + + /* + * For SR, we will need to exit the TB to recognize the new + * exception state. For NPC, in theory this counts as a branch + * (although the SPR only exists for use by an ICE). Save all + * of the cpu state first, allowing it to be overwritten. + */ + if (dc->delayed_branch) { + tcg_gen_mov_tl(cpu_pc, jmp_pc); + tcg_gen_discard_tl(jmp_pc); + } else { + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next + 4); + } + dc->base.is_jmp = DISAS_EXIT; + + tcg_gen_ori_tl(spr, cpu_R(dc, a->a), a->k); + gen_helper_mtspr(tcg_env, spr, cpu_R(dc, a->b)); return true; } @@ -1140,7 +1102,7 @@ static bool trans_l_rfe(DisasContext *dc, arg_l_rfe *a) if (is_user(dc)) { gen_illegal_exception(dc); } else { - gen_helper_rfe(cpu_env); + gen_helper_rfe(tcg_env); dc->base.is_jmp = DISAS_EXIT; } return true; @@ -1153,8 +1115,8 @@ static bool do_fp2(DisasContext *dc, arg_da *a, return false; } check_r0_write(dc, a->d); - fn(cpu_R(dc, a->d), cpu_env, cpu_R(dc, a->a)); - gen_helper_update_fpcsr(cpu_env); + fn(cpu_R(dc, a->d), tcg_env, cpu_R(dc, a->a)); + gen_helper_update_fpcsr(tcg_env); return true; } @@ -1165,8 +1127,8 @@ static bool do_fp3(DisasContext *dc, arg_dab *a, return false; } check_r0_write(dc, a->d); - fn(cpu_R(dc, a->d), cpu_env, cpu_R(dc, a->a), cpu_R(dc, a->b)); - gen_helper_update_fpcsr(cpu_env); + fn(cpu_R(dc, a->d), tcg_env, cpu_R(dc, a->a), cpu_R(dc, a->b)); + gen_helper_update_fpcsr(tcg_env); return true; } @@ -1178,14 +1140,14 @@ static bool do_fpcmp(DisasContext *dc, arg_ab *a, return false; } if (swap) { - fn(cpu_sr_f, cpu_env, cpu_R(dc, a->b), cpu_R(dc, a->a)); + fn(cpu_sr_f, tcg_env, cpu_R(dc, a->b), cpu_R(dc, a->a)); } else { - fn(cpu_sr_f, cpu_env, cpu_R(dc, a->a), cpu_R(dc, a->b)); + fn(cpu_sr_f, tcg_env, cpu_R(dc, a->a), cpu_R(dc, a->b)); } if (inv) { tcg_gen_xori_tl(cpu_sr_f, cpu_sr_f, 1); } - gen_helper_update_fpcsr(cpu_env); + gen_helper_update_fpcsr(tcg_env); return true; } @@ -1231,9 +1193,9 @@ static bool trans_lf_madd_s(DisasContext *dc, arg_dab *a) return false; } check_r0_write(dc, a->d); - gen_helper_float_madd_s(cpu_R(dc, a->d), cpu_env, cpu_R(dc, a->d), + gen_helper_float_madd_s(cpu_R(dc, a->d), tcg_env, cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b)); - gen_helper_update_fpcsr(cpu_env); + gen_helper_update_fpcsr(tcg_env); return true; } @@ -1347,12 +1309,10 @@ static bool do_dp3(DisasContext *dc, arg_dab_pair *a, t1 = tcg_temp_new_i64(); load_pair(dc, t0, a->a, a->ap); load_pair(dc, t1, a->b, a->bp); - fn(t0, cpu_env, t0, t1); + fn(t0, tcg_env, t0, t1); save_pair(dc, t0, a->d, a->dp); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - gen_helper_update_fpcsr(cpu_env); + gen_helper_update_fpcsr(tcg_env); return true; } @@ -1370,11 +1330,10 @@ static bool do_dp2(DisasContext *dc, arg_da_pair *a, t0 = tcg_temp_new_i64(); load_pair(dc, t0, a->a, a->ap); - fn(t0, cpu_env, t0); + fn(t0, tcg_env, t0); save_pair(dc, t0, a->d, a->dp); - tcg_temp_free_i64(t0); - gen_helper_update_fpcsr(cpu_env); + gen_helper_update_fpcsr(tcg_env); return true; } @@ -1395,17 +1354,15 @@ static bool do_dpcmp(DisasContext *dc, arg_ab_pair *a, load_pair(dc, t0, a->a, a->ap); load_pair(dc, t1, a->b, a->bp); if (swap) { - fn(cpu_sr_f, cpu_env, t1, t0); + fn(cpu_sr_f, tcg_env, t1, t0); } else { - fn(cpu_sr_f, cpu_env, t0, t1); + fn(cpu_sr_f, tcg_env, t0, t1); } - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); if (inv) { tcg_gen_xori_tl(cpu_sr_f, cpu_sr_f, 1); } - gen_helper_update_fpcsr(cpu_env); + gen_helper_update_fpcsr(tcg_env); return true; } @@ -1455,11 +1412,10 @@ static bool trans_lf_stod_d(DisasContext *dc, arg_lf_stod_d *a) check_r0_write(dc, a->d); t0 = tcg_temp_new_i64(); - gen_helper_stod(t0, cpu_env, cpu_R(dc, a->a)); + gen_helper_stod(t0, tcg_env, cpu_R(dc, a->a)); save_pair(dc, t0, a->d, a->dp); - tcg_temp_free_i64(t0); - gen_helper_update_fpcsr(cpu_env); + gen_helper_update_fpcsr(tcg_env); return true; } @@ -1475,10 +1431,9 @@ static bool trans_lf_dtos_d(DisasContext *dc, arg_lf_dtos_d *a) t0 = tcg_temp_new_i64(); load_pair(dc, t0, a->a, a->ap); - gen_helper_dtos(cpu_R(dc, a->d), cpu_env, t0); - tcg_temp_free_i64(t0); + gen_helper_dtos(cpu_R(dc, a->d), tcg_env, t0); - gen_helper_update_fpcsr(cpu_env); + gen_helper_update_fpcsr(tcg_env); return true; } @@ -1500,13 +1455,10 @@ static bool trans_lf_madd_d(DisasContext *dc, arg_dab_pair *a) load_pair(dc, t0, a->d, a->dp); load_pair(dc, t1, a->a, a->ap); load_pair(dc, t2, a->b, a->bp); - gen_helper_float_madd_d(t0, cpu_env, t0, t1, t2); + gen_helper_float_madd_d(t0, tcg_env, t0, t1, t2); save_pair(dc, t0, a->d, a->dp); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - gen_helper_update_fpcsr(cpu_env); + gen_helper_update_fpcsr(tcg_env); return true; } @@ -1573,10 +1525,10 @@ static bool trans_lf_sfun_d(DisasContext *dc, arg_ab_pair *a) static void openrisc_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs) { DisasContext *dc = container_of(dcb, DisasContext, base); - CPUOpenRISCState *env = cs->env_ptr; + CPUOpenRISCState *env = cpu_env(cs); int bound; - dc->mem_idx = cpu_mmu_index(env, false); + dc->mem_idx = cpu_mmu_index(cs, false); dc->tb_flags = dc->base.tb->flags; dc->delayed_branch = (dc->tb_flags & TB_FLAGS_DFLAG) != 0; dc->cpucfgr = env->cpucfgr; @@ -1612,8 +1564,7 @@ static void openrisc_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) static void openrisc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); - OpenRISCCPU *cpu = OPENRISC_CPU(cs); - uint32_t insn = translator_ldl(&cpu->env, &dc->base, dc->base.pc_next); + uint32_t insn = translator_ldl(cpu_env(cs), &dc->base, dc->base.pc_next); if (!decode(dc, insn)) { gen_illegal_exception(dc); @@ -1705,17 +1656,18 @@ static const TranslatorOps openrisc_tr_ops = { .disas_log = openrisc_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; - translator_loop(&openrisc_tr_ops, &ctx.base, cs, tb, max_insns); + translator_loop(cs, tb, max_insns, pc, host_pc, + &openrisc_tr_ops, &ctx.base); } void openrisc_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - OpenRISCCPU *cpu = OPENRISC_CPU(cs); - CPUOpenRISCState *env = &cpu->env; + CPUOpenRISCState *env = cpu_env(cs); int i; qemu_fprintf(f, "PC=%08x\n", env->pc); @@ -1724,13 +1676,3 @@ void openrisc_cpu_dump_state(CPUState *cs, FILE *f, int flags) (i % 4) == 3 ? '\n' : ' '); } } - -void restore_state_to_opc(CPUOpenRISCState *env, TranslationBlock *tb, - target_ulong *data) -{ - env->pc = data[0]; - env->dflag = data[1] & 1; - if (data[1] & 2) { - env->ppc = env->pc - 4; - } -} diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c index 1139cead9f..a8315659d9 100644 --- a/target/ppc/arch_dump.c +++ b/target/ppc/arch_dump.c @@ -237,7 +237,7 @@ int cpu_get_dump_info(ArchDumpInfo *info, info->d_machine = PPC_ELF_MACHINE; info->d_class = ELFCLASS; - if (ppc_interrupts_little_endian(cpu, cpu->env.has_hv_mode)) { + if (ppc_interrupts_little_endian(cpu, !!(cpu->env.msr_mask & MSR_HVB))) { info->d_endian = ELFDATA2LSB; } else { info->d_endian = ELFDATA2MSB; @@ -270,23 +270,23 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) static int ppc_write_all_elf_notes(const char *note_name, WriteCoreDumpFunction f, PowerPCCPU *cpu, int id, - void *opaque) + DumpState *s) { - NoteFuncArg arg = { .state = opaque }; + NoteFuncArg arg = { .state = s }; int ret = -1; int note_size; const NoteFuncDesc *nf; for (nf = note_func; nf->note_contents_func; nf++) { - arg.note.hdr.n_namesz = cpu_to_dump32(opaque, sizeof(arg.note.name)); - arg.note.hdr.n_descsz = cpu_to_dump32(opaque, nf->contents_size); + arg.note.hdr.n_namesz = cpu_to_dump32(s, sizeof(arg.note.name)); + arg.note.hdr.n_descsz = cpu_to_dump32(s, nf->contents_size); strncpy(arg.note.name, note_name, sizeof(arg.note.name)); (*nf->note_contents_func)(&arg, cpu); note_size = sizeof(arg.note) - sizeof(arg.note.contents) + nf->contents_size; - ret = f(&arg.note, note_size, opaque); + ret = f(&arg.note, note_size, s); if (ret < 0) { return -1; } @@ -295,15 +295,15 @@ static int ppc_write_all_elf_notes(const char *note_name, } int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque) + int cpuid, DumpState *s) { PowerPCCPU *cpu = POWERPC_CPU(cs); - return ppc_write_all_elf_notes("CORE", f, cpu, cpuid, opaque); + return ppc_write_all_elf_notes("CORE", f, cpu, cpuid, s); } int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque) + int cpuid, DumpState *s) { PowerPCCPU *cpu = POWERPC_CPU(cs); - return ppc_write_all_elf_notes("CORE", f, cpu, cpuid, opaque); + return ppc_write_all_elf_notes("CORE", f, cpu, cpuid, s); } diff --git a/target/ppc/compat.c b/target/ppc/compat.c index 7949a24f5a..ebef2cccec 100644 --- a/target/ppc/compat.c +++ b/target/ppc/compat.c @@ -229,6 +229,25 @@ int ppc_set_compat_all(uint32_t compat_pvr, Error **errp) return 0; } +/* To be used when the machine is not running */ +int ppc_init_compat_all(uint32_t compat_pvr, Error **errp) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + int ret; + + ret = ppc_set_compat(cpu, compat_pvr, errp); + + if (ret < 0) { + return ret; + } + } + + return 0; +} + int ppc_compat_max_vthreads(PowerPCCPU *cpu) { const CompatInfo *compat = compat_by_pvr(cpu->compat_pvr); diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c index 976be5e0d1..f2301b43f7 100644 --- a/target/ppc/cpu-models.c +++ b/target/ppc/cpu-models.c @@ -385,19 +385,19 @@ POWERPC_DEF_SVR("mpc8548e_v21", "MPC8548E v2.1", CPU_POWERPC_MPC8548E_v21, POWERPC_SVR_8548E_v21, e500v2) POWERPC_DEF_SVR("mpc8555_v10", "MPC8555 v1.0", - CPU_POWERPC_MPC8555_v10, POWERPC_SVR_8555_v10, e500v2) + CPU_POWERPC_MPC8555_v10, POWERPC_SVR_8555_v10, e500v1) POWERPC_DEF_SVR("mpc8555_v11", "MPC8555 v1.1", - CPU_POWERPC_MPC8555_v11, POWERPC_SVR_8555_v11, e500v2) + CPU_POWERPC_MPC8555_v11, POWERPC_SVR_8555_v11, e500v1) POWERPC_DEF_SVR("mpc8555e_v10", "MPC8555E v1.0", - CPU_POWERPC_MPC8555E_v10, POWERPC_SVR_8555E_v10, e500v2) + CPU_POWERPC_MPC8555E_v10, POWERPC_SVR_8555E_v10, e500v1) POWERPC_DEF_SVR("mpc8555e_v11", "MPC8555E v1.1", - CPU_POWERPC_MPC8555E_v11, POWERPC_SVR_8555E_v11, e500v2) + CPU_POWERPC_MPC8555E_v11, POWERPC_SVR_8555E_v11, e500v1) POWERPC_DEF_SVR("mpc8560_v10", "MPC8560 v1.0", - CPU_POWERPC_MPC8560_v10, POWERPC_SVR_8560_v10, e500v2) + CPU_POWERPC_MPC8560_v10, POWERPC_SVR_8560_v10, e500v1) POWERPC_DEF_SVR("mpc8560_v20", "MPC8560 v2.0", - CPU_POWERPC_MPC8560_v20, POWERPC_SVR_8560_v20, e500v2) + CPU_POWERPC_MPC8560_v20, POWERPC_SVR_8560_v20, e500v1) POWERPC_DEF_SVR("mpc8560_v21", "MPC8560 v2.1", - CPU_POWERPC_MPC8560_v21, POWERPC_SVR_8560_v21, e500v2) + CPU_POWERPC_MPC8560_v21, POWERPC_SVR_8560_v21, e500v1) POWERPC_DEF_SVR("mpc8567", "MPC8567", CPU_POWERPC_MPC8567, POWERPC_SVR_8567, e500v2) POWERPC_DEF_SVR("mpc8567e", "MPC8567E", @@ -716,11 +716,11 @@ "PowerPC 970MP v1.0") POWERPC_DEF("970mp_v1.1", CPU_POWERPC_970MP_v11, 970, "PowerPC 970MP v1.1") - POWERPC_DEF("power5+_v2.1", CPU_POWERPC_POWER5P_v21, POWER5P, + POWERPC_DEF("power5p_v2.1", CPU_POWERPC_POWER5P_v21, POWER5P, "POWER5+ v2.1") POWERPC_DEF("power7_v2.3", CPU_POWERPC_POWER7_v23, POWER7, "POWER7 v2.3") - POWERPC_DEF("power7+_v2.1", CPU_POWERPC_POWER7P_v21, POWER7, + POWERPC_DEF("power7p_v2.1", CPU_POWERPC_POWER7P_v21, POWER7, "POWER7+ v2.1") POWERPC_DEF("power8e_v2.1", CPU_POWERPC_POWER8E_v21, POWER8, "POWER8E v2.1") @@ -728,12 +728,10 @@ "POWER8 v2.0") POWERPC_DEF("power8nvl_v1.0", CPU_POWERPC_POWER8NVL_v10, POWER8, "POWER8NVL v1.0") - POWERPC_DEF("power9_v1.0", CPU_POWERPC_POWER9_DD1, POWER9, - "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") + POWERPC_DEF("power9_v2.2", CPU_POWERPC_POWER9_DD22, POWER9, + "POWER9 v2.2") POWERPC_DEF("power10_v2.0", CPU_POWERPC_POWER10_DD20, POWER10, "POWER10 v2.0") #endif /* defined (TARGET_PPC64) */ @@ -879,7 +877,6 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { { "755", "755_v2.8" }, { "goldfinger", "755_v2.8" }, { "7400", "7400_v2.9" }, - { "max", "7400_v2.9" }, { "g4", "7400_v2.9" }, { "7410", "7410_v1.4" }, { "nitro", "7410_v1.4" }, @@ -901,14 +898,16 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { { "970", "970_v2.2" }, { "970fx", "970fx_v3.1" }, { "970mp", "970mp_v1.1" }, - { "power5+", "power5+_v2.1" }, + { "power5+", "power5p_v2.1" }, + { "power5+_v2.1", "power5p_v2.1" }, { "power5gs", "power5+_v2.1" }, { "power7", "power7_v2.3" }, - { "power7+", "power7+_v2.1" }, + { "power7+", "power7p_v2.1" }, + { "power7+_v2.1", "power7p_v2.1" }, { "power8e", "power8e_v2.1" }, { "power8", "power8_v2.0" }, { "power8nvl", "power8nvl_v1.0" }, - { "power9", "power9_v2.0" }, + { "power9", "power9_v2.2" }, { "power10", "power10_v2.0" }, #endif @@ -918,6 +917,6 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { #endif { "ppc32", "604" }, { "ppc", "604" }, - { "default", "604" }, + { NULL, NULL } }; diff --git a/target/ppc/cpu-models.h b/target/ppc/cpu-models.h index 76775a74a9..0229ef3a9a 100644 --- a/target/ppc/cpu-models.h +++ b/target/ppc/cpu-models.h @@ -44,7 +44,7 @@ enum { /* PowerPC 405 cores */ CPU_POWERPC_405D2 = 0x20010000, CPU_POWERPC_405D4 = 0x41810000, - /* PowerPC 405 microcontrolers */ + /* PowerPC 405 microcontrollers */ /* XXX: missing 0x200108a0 */ CPU_POWERPC_405CRa = 0x40110041, CPU_POWERPC_405CRb = 0x401100C5, @@ -74,7 +74,7 @@ enum { #define CPU_POWERPC_440 CPU_POWERPC_440GXf /* PowerPC 440 cores */ CPU_POWERPC_440_XILINX = 0x7ff21910, - /* PowerPC 440 microcontrolers */ + /* PowerPC 440 microcontrollers */ CPU_POWERPC_440EPa = 0x42221850, CPU_POWERPC_440EPb = 0x422218D3, CPU_POWERPC_440GPb = 0x40120440, @@ -184,13 +184,13 @@ enum { #define CPU_POWERPC_MPC8548E_v11 CPU_POWERPC_e500v2_v11 #define CPU_POWERPC_MPC8548E_v20 CPU_POWERPC_e500v2_v20 #define CPU_POWERPC_MPC8548E_v21 CPU_POWERPC_e500v2_v21 -#define CPU_POWERPC_MPC8555_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8555_v11 CPU_POWERPC_e500v2_v11 -#define CPU_POWERPC_MPC8555E_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8555E_v11 CPU_POWERPC_e500v2_v11 -#define CPU_POWERPC_MPC8560_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8560_v20 CPU_POWERPC_e500v2_v20 -#define CPU_POWERPC_MPC8560_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8555_v10 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8555_v11 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8555E_v10 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8555E_v11 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8560_v10 CPU_POWERPC_e500v1_v10 +#define CPU_POWERPC_MPC8560_v20 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8560_v21 CPU_POWERPC_e500v1_v20 #define CPU_POWERPC_MPC8567 CPU_POWERPC_e500v2_v22 #define CPU_POWERPC_MPC8567E CPU_POWERPC_e500v2_v22 #define CPU_POWERPC_MPC8568 CPU_POWERPC_e500v2_v22 @@ -348,11 +348,12 @@ enum { CPU_POWERPC_POWER8NVL_BASE = 0x004C0000, CPU_POWERPC_POWER8NVL_v10 = 0x004C0100, CPU_POWERPC_POWER9_BASE = 0x004E0000, - CPU_POWERPC_POWER9_DD1 = 0x004E0100, + CPU_POWERPC_POWER9_DD1 = 0x004E1100, CPU_POWERPC_POWER9_DD20 = 0x004E1200, + CPU_POWERPC_POWER9_DD22 = 0x004E1202, CPU_POWERPC_POWER10_BASE = 0x00800000, - CPU_POWERPC_POWER10_DD1 = 0x00800100, - CPU_POWERPC_POWER10_DD20 = 0x00800200, + CPU_POWERPC_POWER10_DD1 = 0x00801100, + CPU_POWERPC_POWER10_DD20 = 0x00801200, CPU_POWERPC_970_v22 = 0x00390202, CPU_POWERPC_970FX_v10 = 0x00391100, CPU_POWERPC_970FX_v20 = 0x003C0200, diff --git a/target/ppc/cpu-param.h b/target/ppc/cpu-param.h index ea377b7d06..b7ad52de03 100644 --- a/target/ppc/cpu-param.h +++ b/target/ppc/cpu-param.h @@ -31,7 +31,13 @@ # define TARGET_PHYS_ADDR_SPACE_BITS 36 # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif -#define TARGET_PAGE_BITS 12 -#define NB_MMU_MODES 10 + +#ifdef CONFIG_USER_ONLY +/* Allow user-only to vary page size from 4k */ +# define TARGET_PAGE_BITS_VARY +# define TARGET_PAGE_BITS_MIN 12 +#else +# define TARGET_PAGE_BITS 12 +#endif #endif diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index ad7e3c3db9..8247fa2336 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU PowerPC CPU + * QEMU PowerPC CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -20,8 +20,8 @@ #ifndef QEMU_PPC_CPU_QOM_H #define QEMU_PPC_CPU_QOM_H +#include "exec/gdbstub.h" #include "hw/core/cpu.h" -#include "qom/object.h" #ifdef TARGET_PPC64 #define TYPE_POWERPC_CPU "powerpc64-cpu" @@ -31,159 +31,10 @@ OBJECT_DECLARE_CPU_TYPE(PowerPCCPU, PowerPCCPUClass, POWERPC_CPU) -typedef struct CPUArchState CPUPPCState; -typedef struct ppc_tb_t ppc_tb_t; -typedef struct ppc_dcr_t ppc_dcr_t; +#define POWERPC_CPU_TYPE_SUFFIX "-" TYPE_POWERPC_CPU +#define POWERPC_CPU_TYPE_NAME(model) model POWERPC_CPU_TYPE_SUFFIX -/*****************************************************************************/ -/* MMU model */ -typedef enum powerpc_mmu_t powerpc_mmu_t; -enum powerpc_mmu_t { - POWERPC_MMU_UNKNOWN = 0x00000000, - /* Standard 32 bits PowerPC MMU */ - POWERPC_MMU_32B = 0x00000001, - /* PowerPC 6xx MMU with software TLB */ - POWERPC_MMU_SOFT_6xx = 0x00000002, - /* - * PowerPC 74xx MMU with software TLB (this has been - * disabled, see git history for more information. - * keywords: tlbld tlbli TLBMISS PTEHI PTELO) - */ - POWERPC_MMU_SOFT_74xx = 0x00000003, - /* PowerPC 4xx MMU with software TLB */ - POWERPC_MMU_SOFT_4xx = 0x00000004, - /* PowerPC MMU in real mode only */ - POWERPC_MMU_REAL = 0x00000006, - /* Freescale MPC8xx MMU model */ - POWERPC_MMU_MPC8xx = 0x00000007, - /* BookE MMU model */ - POWERPC_MMU_BOOKE = 0x00000008, - /* BookE 2.06 MMU model */ - POWERPC_MMU_BOOKE206 = 0x00000009, -#define POWERPC_MMU_64 0x00010000 - /* 64 bits PowerPC MMU */ - POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, - /* Architecture 2.03 and later (has LPCR) */ - POWERPC_MMU_2_03 = POWERPC_MMU_64 | 0x00000002, - /* Architecture 2.06 variant */ - POWERPC_MMU_2_06 = POWERPC_MMU_64 | 0x00000003, - /* Architecture 2.07 variant */ - POWERPC_MMU_2_07 = POWERPC_MMU_64 | 0x00000004, - /* Architecture 3.00 variant */ - POWERPC_MMU_3_00 = POWERPC_MMU_64 | 0x00000005, -}; - -static inline bool mmu_is_64bit(powerpc_mmu_t mmu_model) -{ - return mmu_model & POWERPC_MMU_64; -} - -/*****************************************************************************/ -/* Exception model */ -typedef enum powerpc_excp_t powerpc_excp_t; -enum powerpc_excp_t { - POWERPC_EXCP_UNKNOWN = 0, - /* Standard PowerPC exception model */ - POWERPC_EXCP_STD, - /* PowerPC 40x exception model */ - POWERPC_EXCP_40x, - /* PowerPC 603/604/G2 exception model */ - POWERPC_EXCP_6xx, - /* PowerPC 7xx exception model */ - POWERPC_EXCP_7xx, - /* PowerPC 74xx exception model */ - POWERPC_EXCP_74xx, - /* BookE exception model */ - POWERPC_EXCP_BOOKE, - /* PowerPC 970 exception model */ - POWERPC_EXCP_970, - /* POWER7 exception model */ - POWERPC_EXCP_POWER7, - /* POWER8 exception model */ - POWERPC_EXCP_POWER8, - /* POWER9 exception model */ - POWERPC_EXCP_POWER9, - /* POWER10 exception model */ - POWERPC_EXCP_POWER10, -}; - -/*****************************************************************************/ -/* PM instructions */ -typedef enum { - PPC_PM_DOZE, - PPC_PM_NAP, - PPC_PM_SLEEP, - PPC_PM_RVWINKLE, - PPC_PM_STOP, -} powerpc_pm_insn_t; - -/*****************************************************************************/ -/* Input pins model */ -typedef enum powerpc_input_t powerpc_input_t; -enum powerpc_input_t { - PPC_FLAGS_INPUT_UNKNOWN = 0, - /* PowerPC 6xx bus */ - PPC_FLAGS_INPUT_6xx, - /* BookE bus */ - PPC_FLAGS_INPUT_BookE, - /* PowerPC 405 bus */ - PPC_FLAGS_INPUT_405, - /* PowerPC 970 bus */ - PPC_FLAGS_INPUT_970, - /* PowerPC POWER7 bus */ - PPC_FLAGS_INPUT_POWER7, - /* PowerPC POWER9 bus */ - PPC_FLAGS_INPUT_POWER9, - /* Freescale RCPU bus */ - PPC_FLAGS_INPUT_RCPU, -}; - -typedef struct PPCHash64Options PPCHash64Options; - -/** - * PowerPCCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * - * A PowerPC CPU model. - */ -struct PowerPCCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceUnrealize parent_unrealize; - DeviceReset parent_reset; - void (*parent_parse_features)(const char *type, char *str, Error **errp); - - uint32_t pvr; - bool (*pvr_match)(struct PowerPCCPUClass *pcc, uint32_t pvr); - uint64_t pcr_mask; /* Available bits in PCR register */ - uint64_t pcr_supported; /* Bits for supported PowerISA versions */ - uint32_t svr; - 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; - powerpc_input_t bus_model; - uint32_t flags; - int bfd_mach; - uint32_t l1_dcache_size, l1_icache_size; -#ifndef CONFIG_USER_ONLY - unsigned int gdb_num_sprs; - const char *gdb_spr_xml; -#endif - const PPCHash64Options *hash64_opts; - struct ppc_radix_page_info *radix_page_info; - uint32_t lrg_decr_bits; - int n_host_threads; - void (*init_proc)(CPUPPCState *env); - int (*check_pow)(CPUPPCState *env); -}; +#define TYPE_HOST_POWERPC_CPU POWERPC_CPU_TYPE_NAME("host") #ifndef CONFIG_USER_ONLY typedef struct PPCTimebase { diff --git a/target/ppc/cpu.c b/target/ppc/cpu.c index 401b6f9e63..e3ad8e0c27 100644 --- a/target/ppc/cpu.c +++ b/target/ppc/cpu.c @@ -59,6 +59,7 @@ void ppc_store_vscr(CPUPPCState *env, uint32_t vscr) env->vscr_sat.u64[0] = vscr & (1u << VSCR_SAT); env->vscr_sat.u64[1] = 0; set_flush_to_zero((vscr >> VSCR_NJ) & 1, &env->vec_status); + set_flush_inputs_to_zero((vscr >> VSCR_NJ) & 1, &env->vec_status); } uint32_t ppc_get_vscr(CPUPPCState *env) @@ -67,12 +68,30 @@ uint32_t ppc_get_vscr(CPUPPCState *env) return env->vscr | (sat << VSCR_SAT); } +void ppc_set_cr(CPUPPCState *env, uint64_t cr) +{ + for (int i = 7; i >= 0; i--) { + env->crf[i] = cr & 0xf; + cr >>= 4; + } +} + +uint64_t ppc_get_cr(const CPUPPCState *env) +{ + uint64_t cr = 0; + for (int i = 0; i < 8; i++) { + cr |= (env->crf[i] & 0xf) << (4 * (7 - i)); + } + return cr; +} + /* GDBstub can read and write MSR... */ void ppc_store_msr(CPUPPCState *env, target_ulong value) { hreg_store_msr(env, value, 0); } +#if !defined(CONFIG_USER_ONLY) void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val) { PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); @@ -81,8 +100,97 @@ void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val) env->spr[SPR_LPCR] = val & pcc->lpcr_mask; /* The gtse bit affects hflags */ hreg_compute_hflags(env); + + ppc_maybe_interrupt(env); } +#if defined(TARGET_PPC64) +void ppc_update_ciabr(CPUPPCState *env) +{ + CPUState *cs = env_cpu(env); + target_ulong ciabr = env->spr[SPR_CIABR]; + target_ulong ciea, priv; + + ciea = ciabr & PPC_BITMASK(0, 61); + priv = ciabr & PPC_BITMASK(62, 63); + + if (env->ciabr_breakpoint) { + cpu_breakpoint_remove_by_ref(cs, env->ciabr_breakpoint); + env->ciabr_breakpoint = NULL; + } + + if (priv) { + cpu_breakpoint_insert(cs, ciea, BP_CPU, &env->ciabr_breakpoint); + } +} + +void ppc_store_ciabr(CPUPPCState *env, target_ulong val) +{ + env->spr[SPR_CIABR] = val; + ppc_update_ciabr(env); +} + +void ppc_update_daw0(CPUPPCState *env) +{ + CPUState *cs = env_cpu(env); + target_ulong deaw = env->spr[SPR_DAWR0] & PPC_BITMASK(0, 60); + uint32_t dawrx = env->spr[SPR_DAWRX0]; + int mrd = extract32(dawrx, PPC_BIT_NR(48), 54 - 48); + bool dw = extract32(dawrx, PPC_BIT_NR(57), 1); + bool dr = extract32(dawrx, PPC_BIT_NR(58), 1); + bool hv = extract32(dawrx, PPC_BIT_NR(61), 1); + bool sv = extract32(dawrx, PPC_BIT_NR(62), 1); + bool pr = extract32(dawrx, PPC_BIT_NR(62), 1); + vaddr len; + int flags; + + if (env->dawr0_watchpoint) { + cpu_watchpoint_remove_by_ref(cs, env->dawr0_watchpoint); + env->dawr0_watchpoint = NULL; + } + + if (!dr && !dw) { + return; + } + + if (!hv && !sv && !pr) { + return; + } + + len = (mrd + 1) * 8; + flags = BP_CPU | BP_STOP_BEFORE_ACCESS; + if (dr) { + flags |= BP_MEM_READ; + } + if (dw) { + flags |= BP_MEM_WRITE; + } + + cpu_watchpoint_insert(cs, deaw, len, flags, &env->dawr0_watchpoint); +} + +void ppc_store_dawr0(CPUPPCState *env, target_ulong val) +{ + env->spr[SPR_DAWR0] = val; + ppc_update_daw0(env); +} + +void ppc_store_dawrx0(CPUPPCState *env, uint32_t val) +{ + int hrammc = extract32(val, PPC_BIT_NR(56), 1); + + if (hrammc) { + /* This might be done with a second watchpoint at the xor of DEAW[0] */ + qemu_log_mask(LOG_UNIMP, "%s: DAWRX0[HRAMMC] is unimplemented\n", + __func__); + } + + env->spr[SPR_DAWRX0] = val; + ppc_update_daw0(env); +} +#endif +#endif + static inline void fpscr_set_rounding_mode(CPUPPCState *env) { int rnd_type; @@ -120,6 +228,8 @@ void ppc_store_fpscr(CPUPPCState *env, target_ulong val) val |= FP_FEX; } env->fpscr = val; + env->fp_status.rebias_overflow = (FP_OE & env->fpscr) ? true : false; + env->fp_status.rebias_underflow = (FP_UE & env->fpscr) ? true : false; if (tcg_enabled()) { fpscr_set_rounding_mode(env); } diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 6d78078f37..67e6b2effd 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -27,6 +27,8 @@ #include "qom/object.h" #include "hw/registerfields.h" +#define CPU_RESOLVING_TYPE TYPE_POWERPC_CPU + #define TCG_GUEST_DEFAULT_MO 0 #define TARGET_PAGE_BITS_64K 16 @@ -47,6 +49,18 @@ PPC_BIT32(bs)) #define PPC_BITMASK8(bs, be) ((PPC_BIT8(bs) - PPC_BIT8(be)) | PPC_BIT8(bs)) +/* + * QEMU version of the GETFIELD/SETFIELD macros from skiboot + * + * It might be better to use the existing extract64() and + * deposit64() but this means that all the register definitions will + * change and become incompatible with the ones found in skiboot. + */ +#define MASK_TO_LSH(m) (__builtin_ffsll(m) - 1) +#define GETFIELD(m, v) (((v) & (m)) >> MASK_TO_LSH(m)) +#define SETFIELD(m, v, val) \ + (((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) & (m))) + /*****************************************************************************/ /* Exception vectors definitions */ enum { @@ -178,6 +192,95 @@ enum { POWERPC_EXCP_TRAP = 0x40, }; +/* Exception model */ +typedef enum powerpc_excp_t { + POWERPC_EXCP_UNKNOWN = 0, + /* Standard PowerPC exception model */ + POWERPC_EXCP_STD, + /* PowerPC 40x exception model */ + POWERPC_EXCP_40x, + /* PowerPC 603/604/G2 exception model */ + POWERPC_EXCP_6xx, + /* PowerPC 7xx exception model */ + POWERPC_EXCP_7xx, + /* PowerPC 74xx exception model */ + POWERPC_EXCP_74xx, + /* BookE exception model */ + POWERPC_EXCP_BOOKE, + /* PowerPC 970 exception model */ + POWERPC_EXCP_970, + /* POWER7 exception model */ + POWERPC_EXCP_POWER7, + /* POWER8 exception model */ + POWERPC_EXCP_POWER8, + /* POWER9 exception model */ + POWERPC_EXCP_POWER9, + /* POWER10 exception model */ + POWERPC_EXCP_POWER10, +} powerpc_excp_t; + +/*****************************************************************************/ +/* MMU model */ +typedef enum powerpc_mmu_t { + POWERPC_MMU_UNKNOWN = 0x00000000, + /* Standard 32 bits PowerPC MMU */ + POWERPC_MMU_32B = 0x00000001, + /* PowerPC 6xx MMU with software TLB */ + POWERPC_MMU_SOFT_6xx = 0x00000002, + /* + * PowerPC 74xx MMU with software TLB (this has been + * disabled, see git history for more information. + * keywords: tlbld tlbli TLBMISS PTEHI PTELO) + */ + POWERPC_MMU_SOFT_74xx = 0x00000003, + /* PowerPC 4xx MMU with software TLB */ + POWERPC_MMU_SOFT_4xx = 0x00000004, + /* PowerPC MMU in real mode only */ + POWERPC_MMU_REAL = 0x00000006, + /* Freescale MPC8xx MMU model */ + POWERPC_MMU_MPC8xx = 0x00000007, + /* BookE MMU model */ + POWERPC_MMU_BOOKE = 0x00000008, + /* BookE 2.06 MMU model */ + POWERPC_MMU_BOOKE206 = 0x00000009, +#define POWERPC_MMU_64 0x00010000 + /* 64 bits PowerPC MMU */ + POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, + /* Architecture 2.03 and later (has LPCR) */ + POWERPC_MMU_2_03 = POWERPC_MMU_64 | 0x00000002, + /* Architecture 2.06 variant */ + POWERPC_MMU_2_06 = POWERPC_MMU_64 | 0x00000003, + /* Architecture 2.07 variant */ + POWERPC_MMU_2_07 = POWERPC_MMU_64 | 0x00000004, + /* Architecture 3.00 variant */ + POWERPC_MMU_3_00 = POWERPC_MMU_64 | 0x00000005, +} powerpc_mmu_t; + +static inline bool mmu_is_64bit(powerpc_mmu_t mmu_model) +{ + return mmu_model & POWERPC_MMU_64; +} + +/*****************************************************************************/ +/* Input pins model */ +typedef enum powerpc_input_t { + PPC_FLAGS_INPUT_UNKNOWN = 0, + /* PowerPC 6xx bus */ + PPC_FLAGS_INPUT_6xx, + /* BookE bus */ + PPC_FLAGS_INPUT_BookE, + /* PowerPC 405 bus */ + PPC_FLAGS_INPUT_405, + /* PowerPC 970 bus */ + PPC_FLAGS_INPUT_970, + /* PowerPC POWER7 bus */ + PPC_FLAGS_INPUT_POWER7, + /* PowerPC POWER9 bus */ + PPC_FLAGS_INPUT_POWER9, + /* Freescale RCPU bus */ + PPC_FLAGS_INPUT_RCPU, +} powerpc_input_t; + #define PPC_INPUT(env) ((env)->bus_model) /*****************************************************************************/ @@ -186,9 +289,14 @@ typedef struct opc_handler_t opc_handler_t; /*****************************************************************************/ /* Types used to describe some PowerPC registers etc. */ typedef struct DisasContext DisasContext; +typedef struct ppc_dcr_t ppc_dcr_t; typedef struct ppc_spr_t ppc_spr_t; +typedef struct ppc_tb_t ppc_tb_t; typedef union ppc_tlb_t ppc_tlb_t; typedef struct ppc_hash_pte64 ppc_hash_pte64_t; +typedef struct PPCHash64Options PPCHash64Options; + +typedef struct CPUArchState CPUPPCState; /* SPR access micro-ops generations callbacks */ struct ppc_spr_t { @@ -234,7 +342,7 @@ typedef union _ppc_vsr_t { #ifdef CONFIG_INT128 __uint128_t u128; #endif - Int128 s128; + Int128 s128; } ppc_vsr_t; typedef ppc_vsr_t ppc_avr_t; @@ -416,7 +524,7 @@ FIELD(MSR, LE, MSR_LE, 1) /* PMU bits */ #define MMCR0_FC PPC_BIT(32) /* Freeze Counters */ -#define MMCR0_PMAO PPC_BIT(56) /* Perf Monitor Alert Ocurred */ +#define MMCR0_PMAO PPC_BIT(56) /* Perf Monitor Alert Occurred */ #define MMCR0_PMAE PPC_BIT(37) /* Perf Monitor Alert Enable */ #define MMCR0_EBE PPC_BIT(43) /* Perf Monitor EBB Enable */ #define MMCR0_FCECE PPC_BIT(38) /* FC on Enabled Cond or Event */ @@ -660,6 +768,10 @@ enum { POWERPC_FLAG_TM = 0x00100000, /* Has SCV (ISA 3.00) */ POWERPC_FLAG_SCV = 0x00200000, + /* Has >1 thread per core */ + POWERPC_FLAG_SMT = 0x00400000, + /* Using "LPAR per core" mode (as opposed to per-thread) */ + POWERPC_FLAG_SMT_1LPAR = 0x00800000, }; /* @@ -684,7 +796,9 @@ enum { HFLAGS_PR = 14, /* MSR_PR */ HFLAGS_PMCC0 = 15, /* MMCR0 PMCC bit 0 */ HFLAGS_PMCC1 = 16, /* MMCR0 PMCC bit 1 */ - HFLAGS_INSN_CNT = 17, /* PMU instruction count enabled */ + HFLAGS_PMCJCE = 17, /* MMCR0 PMCjCE bit */ + HFLAGS_PMC_OTHER = 18, /* PMC other than PMC5-6 is enabled */ + HFLAGS_INSN_CNT = 19, /* PMU instruction count enabled */ HFLAGS_VSX = 23, /* MSR_VSX if cpu has VSX */ HFLAGS_VR = 25, /* MSR_VR if cpu has VRE */ @@ -694,42 +808,42 @@ enum { /*****************************************************************************/ /* Floating point status and control register */ -#define FPSCR_DRN2 34 /* Decimal Floating-Point rounding control */ -#define FPSCR_DRN1 33 /* Decimal Floating-Point rounding control */ -#define FPSCR_DRN0 32 /* Decimal Floating-Point rounding control */ -#define FPSCR_FX 31 /* Floating-point exception summary */ -#define FPSCR_FEX 30 /* Floating-point enabled exception summary */ -#define FPSCR_VX 29 /* Floating-point invalid operation exception summ. */ -#define FPSCR_OX 28 /* Floating-point overflow exception */ -#define FPSCR_UX 27 /* Floating-point underflow exception */ -#define FPSCR_ZX 26 /* Floating-point zero divide exception */ -#define FPSCR_XX 25 /* Floating-point inexact exception */ -#define FPSCR_VXSNAN 24 /* Floating-point invalid operation exception (sNan) */ -#define FPSCR_VXISI 23 /* Floating-point invalid operation exception (inf) */ -#define FPSCR_VXIDI 22 /* Floating-point invalid operation exception (inf) */ -#define FPSCR_VXZDZ 21 /* Floating-point invalid operation exception (zero) */ -#define FPSCR_VXIMZ 20 /* Floating-point invalid operation exception (inf) */ -#define FPSCR_VXVC 19 /* Floating-point invalid operation exception (comp) */ -#define FPSCR_FR 18 /* Floating-point fraction rounded */ -#define FPSCR_FI 17 /* Floating-point fraction inexact */ -#define FPSCR_C 16 /* Floating-point result class descriptor */ -#define FPSCR_FL 15 /* Floating-point less than or negative */ -#define FPSCR_FG 14 /* Floating-point greater than or negative */ -#define FPSCR_FE 13 /* Floating-point equal or zero */ -#define FPSCR_FU 12 /* Floating-point unordered or NaN */ -#define FPSCR_FPCC 12 /* Floating-point condition code */ -#define FPSCR_FPRF 12 /* Floating-point result flags */ -#define FPSCR_VXSOFT 10 /* Floating-point invalid operation exception (soft) */ -#define FPSCR_VXSQRT 9 /* Floating-point invalid operation exception (sqrt) */ -#define FPSCR_VXCVI 8 /* Floating-point invalid operation exception (int) */ -#define FPSCR_VE 7 /* Floating-point invalid operation exception enable */ -#define FPSCR_OE 6 /* Floating-point overflow exception enable */ -#define FPSCR_UE 5 /* Floating-point underflow exception enable */ -#define FPSCR_ZE 4 /* Floating-point zero divide exception enable */ -#define FPSCR_XE 3 /* Floating-point inexact exception enable */ -#define FPSCR_NI 2 /* Floating-point non-IEEE mode */ -#define FPSCR_RN1 1 -#define FPSCR_RN0 0 /* Floating-point rounding control */ +#define FPSCR_DRN2 PPC_BIT_NR(29) /* Decimal Floating-Point rounding ctrl. */ +#define FPSCR_DRN1 PPC_BIT_NR(30) /* Decimal Floating-Point rounding ctrl. */ +#define FPSCR_DRN0 PPC_BIT_NR(31) /* Decimal Floating-Point rounding ctrl. */ +#define FPSCR_FX PPC_BIT_NR(32) /* Floating-point exception summary */ +#define FPSCR_FEX PPC_BIT_NR(33) /* Floating-point enabled exception summ.*/ +#define FPSCR_VX PPC_BIT_NR(34) /* Floating-point invalid op. excp. summ.*/ +#define FPSCR_OX PPC_BIT_NR(35) /* Floating-point overflow exception */ +#define FPSCR_UX PPC_BIT_NR(36) /* Floating-point underflow exception */ +#define FPSCR_ZX PPC_BIT_NR(37) /* Floating-point zero divide exception */ +#define FPSCR_XX PPC_BIT_NR(38) /* Floating-point inexact exception */ +#define FPSCR_VXSNAN PPC_BIT_NR(39) /* Floating-point invalid op. excp (sNan)*/ +#define FPSCR_VXISI PPC_BIT_NR(40) /* Floating-point invalid op. excp (inf) */ +#define FPSCR_VXIDI PPC_BIT_NR(41) /* Floating-point invalid op. excp (inf) */ +#define FPSCR_VXZDZ PPC_BIT_NR(42) /* Floating-point invalid op. excp (zero)*/ +#define FPSCR_VXIMZ PPC_BIT_NR(43) /* Floating-point invalid op. excp (inf) */ +#define FPSCR_VXVC PPC_BIT_NR(44) /* Floating-point invalid op. excp (comp)*/ +#define FPSCR_FR PPC_BIT_NR(45) /* Floating-point fraction rounded */ +#define FPSCR_FI PPC_BIT_NR(46) /* Floating-point fraction inexact */ +#define FPSCR_C PPC_BIT_NR(47) /* Floating-point result class descriptor*/ +#define FPSCR_FL PPC_BIT_NR(48) /* Floating-point less than or negative */ +#define FPSCR_FG PPC_BIT_NR(49) /* Floating-point greater than or neg. */ +#define FPSCR_FE PPC_BIT_NR(50) /* Floating-point equal or zero */ +#define FPSCR_FU PPC_BIT_NR(51) /* Floating-point unordered or NaN */ +#define FPSCR_FPCC PPC_BIT_NR(51) /* Floating-point condition code */ +#define FPSCR_FPRF PPC_BIT_NR(51) /* Floating-point result flags */ +#define FPSCR_VXSOFT PPC_BIT_NR(53) /* Floating-point invalid op. excp (soft)*/ +#define FPSCR_VXSQRT PPC_BIT_NR(54) /* Floating-point invalid op. excp (sqrt)*/ +#define FPSCR_VXCVI PPC_BIT_NR(55) /* Floating-point invalid op. excp (int) */ +#define FPSCR_VE PPC_BIT_NR(56) /* Floating-point invalid op. excp enable*/ +#define FPSCR_OE PPC_BIT_NR(57) /* Floating-point overflow excp. enable */ +#define FPSCR_UE PPC_BIT_NR(58) /* Floating-point underflow excp. enable */ +#define FPSCR_ZE PPC_BIT_NR(59) /* Floating-point zero divide excp enable*/ +#define FPSCR_XE PPC_BIT_NR(60) /* Floating-point inexact excp. enable */ +#define FPSCR_NI PPC_BIT_NR(61) /* Floating-point non-IEEE mode */ +#define FPSCR_RN1 PPC_BIT_NR(62) +#define FPSCR_RN0 PPC_BIT_NR(63) /* Floating-point rounding control */ /* Invalid operation exception summary */ #define FPSCR_IX ((1 << FPSCR_VXSNAN) | (1 << FPSCR_VXISI) | \ (1 << FPSCR_VXIDI) | (1 << FPSCR_VXZDZ) | \ @@ -1054,6 +1168,36 @@ struct ppc_radix_page_info { uint32_t entries[PPC_PAGE_SIZES_MAX_SZ]; }; +/*****************************************************************************/ +/* Dynamic Execution Control Register */ + +#define DEXCR_ASPECT(name, num) \ +FIELD(DEXCR, PNH_##name, PPC_BIT_NR(num), 1) \ +FIELD(DEXCR, PRO_##name, PPC_BIT_NR(num + 32), 1) \ +FIELD(HDEXCR, HNU_##name, PPC_BIT_NR(num), 1) \ +FIELD(HDEXCR, ENF_##name, PPC_BIT_NR(num + 32), 1) \ + +DEXCR_ASPECT(SBHE, 0) +DEXCR_ASPECT(IBRTPD, 1) +DEXCR_ASPECT(SRAPD, 4) +DEXCR_ASPECT(NPHIE, 5) +DEXCR_ASPECT(PHIE, 6) + +/*****************************************************************************/ +/* PowerNV ChipTOD and TimeBase State Machine */ +struct pnv_tod_tbst { + int tb_ready_for_tod; /* core TB ready to receive TOD from chiptod */ + int tod_sent_to_tb; /* chiptod sent TOD to the core TB */ + + /* + * "Timers" for async TBST events are simulated by mfTFAC because TFAC + * is polled for such events. These are just used to ensure firmware + * performs the polling at least a few times. + */ + int tb_state_timer; + int tb_sync_pulse_timer; +}; + /*****************************************************************************/ /* The whole PowerPC CPU context */ @@ -1085,9 +1229,12 @@ struct CPUArchState { target_ulong ov32; target_ulong ca32; - target_ulong reserve_addr; /* Reservation address */ - target_ulong reserve_val; /* Reservation value */ + target_ulong reserve_addr; /* Reservation address */ + target_ulong reserve_length; /* Reservation larx op size (bytes) */ + target_ulong reserve_val; /* Reservation value */ +#if defined(TARGET_PPC64) target_ulong reserve_val2; +#endif /* These are used in supervisor mode only */ target_ulong msr; /* machine state register */ @@ -1095,7 +1242,6 @@ struct CPUArchState { /* used to speed-up TLB assist handlers */ 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; @@ -1104,6 +1250,8 @@ struct CPUArchState { /* MMU context, only relevant for full system emulation */ #if defined(TARGET_PPC64) ppc_slb_t slb[MAX_SLB_ENTRIES]; /* PowerPC 64 SLB area */ + struct CPUBreakpoint *ciabr_breakpoint; + struct CPUWatchpoint *dawr0_watchpoint; #endif target_ulong sr[32]; /* segment registers */ uint32_t nb_BATs; /* number of BATs */ @@ -1118,11 +1266,19 @@ struct CPUArchState { 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 */ +#ifdef CONFIG_KVM bool tlb_dirty; /* Set to non-zero when modifying TLB */ bool kvm_sw_tlb; /* non-zero if KVM SW TLB API is active */ +#endif /* CONFIG_KVM */ uint32_t tlb_need_flush; /* Delayed flush needed */ #define TLB_NEED_LOCAL_FLUSH 0x1 #define TLB_NEED_GLOBAL_FLUSH 0x2 + +#if defined(TARGET_PPC64) + /* PowerNV chiptod / timebase facility state. */ + /* Would be nice to put these into PnvCore */ + struct pnv_tod_tbst pnv_tod_tbst; +#endif #endif /* Other registers */ @@ -1166,13 +1322,13 @@ struct CPUArchState { int error_code; uint32_t pending_interrupts; #if !defined(CONFIG_USER_ONLY) + uint64_t excp_stats[POWERPC_EXCP_NB]; /* * 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; target_ulong excp_vectors[POWERPC_EXCP_NB]; /* Exception vectors */ target_ulong excp_prefix; @@ -1239,6 +1395,13 @@ struct CPUArchState { uint64_t pmu_base_time; }; +#define _CORE_ID(cs) \ + (POWERPC_CPU(cs)->env.spr_cb[SPR_PIR].default_value & ~(cs->nr_threads - 1)) + +#define THREAD_SIBLING_FOREACH(cs, cs_sibling) \ + CPU_FOREACH(cs_sibling) \ + if (_CORE_ID(cs) == _CORE_ID(cs_sibling)) + #define SET_FIT_PERIOD(a_, b_, c_, d_) \ do { \ env->fit_period[0] = (a_); \ @@ -1267,11 +1430,8 @@ typedef struct PPCVirtualHypervisorClass PPCVirtualHypervisorClass; * A PowerPC CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUPPCState env; int vcpu_id; @@ -1296,7 +1456,53 @@ struct ArchCPU { int32_t mig_slb_nr; }; +/** + * PowerPCCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A PowerPC CPU model. + */ +struct PowerPCCPUClass { + CPUClass parent_class; + DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; + ResettablePhases parent_phases; + void (*parent_parse_features)(const char *type, char *str, Error **errp); + + uint32_t pvr; + /* + * If @best is false, match if pcc is in the family of pvr + * Else match only if pcc is the best match for pvr in this family. + */ + bool (*pvr_match)(struct PowerPCCPUClass *pcc, uint32_t pvr, bool best); + uint64_t pcr_mask; /* Available bits in PCR register */ + uint64_t pcr_supported; /* Bits for supported PowerISA versions */ + uint32_t svr; + 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; + powerpc_input_t bus_model; + uint32_t flags; + int bfd_mach; + uint32_t l1_dcache_size, l1_icache_size; +#ifndef CONFIG_USER_ONLY + GDBFeature gdb_spr; +#endif + const PPCHash64Options *hash64_opts; + struct ppc_radix_page_info *radix_page_info; + uint32_t lrg_decr_bits; + int n_host_threads; + void (*init_proc)(CPUPPCState *env); + int (*check_pow)(CPUPPCState *env); +}; + +ObjectClass *ppc_cpu_class_by_name(const char *name); 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); @@ -1333,20 +1539,19 @@ static inline bool vhyp_cpu_in_nested(PowerPCCPU *cpu) #endif /* CONFIG_USER_ONLY */ void ppc_cpu_dump_state(CPUState *cpu, FILE *f, int flags); -hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); 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 -void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu); -const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name); +hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #endif int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque); + int cpuid, DumpState *s); int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque); + int cpuid, DumpState *s); #ifndef CONFIG_USER_ONLY +void ppc_maybe_interrupt(CPUPPCState *env); void ppc_cpu_do_interrupt(CPUState *cpu); bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req); void ppc_cpu_do_system_reset(CPUState *cs); @@ -1359,14 +1564,18 @@ void ppc_translate_init(void); #if !defined(CONFIG_USER_ONLY) void ppc_store_sdr1(CPUPPCState *env, target_ulong value); +void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val); +void ppc_update_ciabr(CPUPPCState *env); +void ppc_store_ciabr(CPUPPCState *env, target_ulong value); +void ppc_update_daw0(CPUPPCState *env); +void ppc_store_dawr0(CPUPPCState *env, target_ulong value); +void ppc_store_dawrx0(CPUPPCState *env, uint32_t value); #endif /* !defined(CONFIG_USER_ONLY) */ void ppc_store_msr(CPUPPCState *env, target_ulong value); -void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val); void ppc_cpu_list(void); /* Time-base and decrementer management */ -#ifndef NO_CPU_IO_DEFS uint64_t cpu_ppc_load_tbl(CPUPPCState *env); uint32_t cpu_ppc_load_tbu(CPUPPCState *env); void cpu_ppc_store_tbu(CPUPPCState *env, uint32_t value); @@ -1375,6 +1584,8 @@ 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); +void cpu_ppc_increase_tb_by_offset(CPUPPCState *env, int64_t offset); +void cpu_ppc_decrease_tb_by_offset(CPUPPCState *env, int64_t offset); 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); @@ -1397,16 +1608,11 @@ void store_booke_tsr(CPUPPCState *env, target_ulong val); void ppc_tlb_invalidate_all(CPUPPCState *env); void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr); void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp); -int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, - hwaddr *raddrp, target_ulong address, - uint32_t pid); -int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddrp, - target_ulong address, uint32_t pid, int ext, - int i); -hwaddr booke206_tlb_to_page_size(CPUPPCState *env, - ppcmas_tlb_t *tlb); -#endif +void cpu_ppc_set_1lpar(PowerPCCPU *cpu); +int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, + target_ulong address, uint32_t pid); +int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid); +hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb); #endif void ppc_store_fpscr(CPUPPCState *env, target_ulong val); @@ -1434,15 +1640,11 @@ static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn) int ppc_dcr_read(ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp); int ppc_dcr_write(ppc_dcr_t *dcr_env, int dcrn, uint32_t val); -#define POWERPC_CPU_TYPE_SUFFIX "-" TYPE_POWERPC_CPU -#define POWERPC_CPU_TYPE_NAME(model) model POWERPC_CPU_TYPE_SUFFIX -#define CPU_RESOLVING_TYPE TYPE_POWERPC_CPU - #define cpu_list ppc_cpu_list /* MMU modes definitions */ #define MMU_USER_IDX 0 -static inline int cpu_mmu_index(CPUPPCState *env, bool ifetch) +static inline int ppc_env_mmu_index(CPUPPCState *env, bool ifetch) { #ifdef CONFIG_USER_ONLY return MMU_USER_IDX; @@ -1462,6 +1664,7 @@ int ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp); #if !defined(CONFIG_USER_ONLY) int ppc_set_compat_all(uint32_t compat_pvr, Error **errp); +int ppc_init_compat_all(uint32_t compat_pvr, Error **errp); #endif int ppc_compat_max_vthreads(PowerPCCPU *cpu); void ppc_compat_add_property(Object *obj, const char *name, @@ -1495,10 +1698,6 @@ void ppc_compat_add_property(Object *obj, const char *name, #define XER_CMP 8 #define XER_BC 0 #define xer_so (env->so) -#define xer_ov (env->ov) -#define xer_ca (env->ca) -#define xer_ov32 (env->ov) -#define xer_ca32 (env->ca) #define xer_cmp ((env->xer >> XER_CMP) & 0xFF) #define xer_bc ((env->xer >> XER_BC) & 0x7F) @@ -1558,9 +1757,11 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_PSPB (0x09F) #define SPR_DPDES (0x0B0) #define SPR_DAWR0 (0x0B4) +#define SPR_DAWR1 (0x0B5) #define SPR_RPR (0x0BA) #define SPR_CIABR (0x0BB) #define SPR_DAWRX0 (0x0BC) +#define SPR_DAWRX1 (0x0BD) #define SPR_HFSCR (0x0BE) #define SPR_VRSAVE (0x100) #define SPR_USPRG0 (0x100) @@ -1571,8 +1772,8 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_USPRG5 (0x105) #define SPR_USPRG6 (0x106) #define SPR_USPRG7 (0x107) -#define SPR_VTBL (0x10C) -#define SPR_VTBU (0x10D) +#define SPR_TBL (0x10C) +#define SPR_TBU (0x10D) #define SPR_SPRG0 (0x110) #define SPR_SPRG1 (0x111) #define SPR_SPRG2 (0x112) @@ -1585,8 +1786,8 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_SPRG7 (0x117) #define SPR_ASR (0x118) #define SPR_EAR (0x11A) -#define SPR_TBL (0x11C) -#define SPR_TBU (0x11D) +#define SPR_WR_TBL (0x11C) +#define SPR_WR_TBU (0x11D) #define SPR_TBU40 (0x11E) #define SPR_SVR (0x11E) #define SPR_BOOKE_PIR (0x11E) @@ -1628,6 +1829,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_HMER (0x150) #define SPR_HMEER (0x151) #define SPR_PCR (0x152) +#define SPR_HEIR (0x153) #define SPR_BOOKE_LPIDR (0x152) #define SPR_BOOKE_TCR (0x154) #define SPR_BOOKE_TLB0PS (0x158) @@ -1664,7 +1866,11 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_BOOKE_GIVOR13 (0x1BC) #define SPR_BOOKE_GIVOR14 (0x1BD) #define SPR_TIR (0x1BE) +#define SPR_UHDEXCR (0x1C7) #define SPR_PTCR (0x1D0) +#define SPR_HASHKEYR (0x1D4) +#define SPR_HASHPKEYR (0x1D5) +#define SPR_HDEXCR (0x1D7) #define SPR_BOOKE_SPEFSCR (0x200) #define SPR_Exxx_BBEAR (0x201) #define SPR_Exxx_BBTAR (0x202) @@ -1749,6 +1955,12 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_BOOKE_TLB2CFG (0x2B2) #define SPR_BOOKE_TLB3CFG (0x2B3) #define SPR_BOOKE_EPR (0x2BE) +#define SPR_POWER_USIER2 (0x2E0) +#define SPR_POWER_USIER3 (0x2E1) +#define SPR_POWER_UMMCR3 (0x2E2) +#define SPR_POWER_SIER2 (0x2F0) +#define SPR_POWER_SIER3 (0x2F1) +#define SPR_POWER_MMCR3 (0x2F2) #define SPR_PERF0 (0x300) #define SPR_RCPU_MI_RBA0 (0x300) #define SPR_MPC_MI_CTR (0x300) @@ -1853,15 +2065,19 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_RCPU_L2U_RA2 (0x32A) #define SPR_MPC_MD_DBRAM1 (0x32A) #define SPR_RCPU_L2U_RA3 (0x32B) +#define SPR_UDEXCR (0x32C) #define SPR_TAR (0x32F) #define SPR_ASDR (0x330) +#define SPR_DEXCR (0x33C) #define SPR_IC (0x350) #define SPR_VTB (0x351) #define SPR_MMCRC (0x353) #define SPR_PSSCR (0x357) #define SPR_440_INV0 (0x370) #define SPR_440_INV1 (0x371) +#define SPR_TRIG1 (0x371) #define SPR_440_INV2 (0x372) +#define SPR_TRIG2 (0x372) #define SPR_440_INV3 (0x373) #define SPR_440_ITV0 (0x374) #define SPR_440_ITV1 (0x375) @@ -2203,8 +2419,6 @@ enum { PPC_DCR = 0x1000000000000000ULL, /* DCR extended accesse */ PPC_DCRX = 0x2000000000000000ULL, - /* user-mode DCR access, implemented in PowerPC 460 */ - PPC_DCRUX = 0x4000000000000000ULL, /* popcntw and popcntd instructions */ PPC_POPCNTWD = 0x8000000000000000ULL, @@ -2228,8 +2442,8 @@ enum { | PPC_405_MAC | PPC_440_SPEC | PPC_BOOKE \ | PPC_MFAPIDI | PPC_TLBIVA | PPC_TLBIVAX \ | PPC_4xx_COMMON | PPC_40x_ICBT | PPC_RFMCI \ - | PPC_RFDI | PPC_DCR | PPC_DCRX | PPC_DCRUX \ - | PPC_POPCNTWD | PPC_CILDST) + | PPC_RFDI | PPC_DCR | PPC_DCRX | PPC_POPCNTWD \ + | PPC_CILDST) /* extended type values */ @@ -2277,6 +2491,8 @@ enum { PPC2_ISA310 = 0x0000000000100000ULL, /* lwsync instruction */ PPC2_MEM_LWSYNC = 0x0000000000200000ULL, + /* ISA 2.06 BCD assist instructions */ + PPC2_BCDA_ISA206 = 0x0000000000400000ULL, #define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \ PPC2_ISA205 | PPC2_VSX207 | PPC2_PERM_ISA206 | \ @@ -2285,7 +2501,8 @@ enum { PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | \ PPC2_ALTIVEC_207 | PPC2_ISA207S | PPC2_DFP | \ PPC2_FP_CVT_S64 | PPC2_TM | PPC2_PM_ISA206 | \ - PPC2_ISA300 | PPC2_ISA310 | PPC2_MEM_LWSYNC) + PPC2_ISA300 | PPC2_ISA310 | PPC2_MEM_LWSYNC | \ + PPC2_BCDA_ISA206) }; /*****************************************************************************/ @@ -2406,27 +2623,27 @@ enum { /* Hardware exceptions definitions */ enum { /* External hardware exception sources */ - PPC_INTERRUPT_RESET = 0, /* Reset exception */ - PPC_INTERRUPT_WAKEUP, /* Wakeup exception */ - PPC_INTERRUPT_MCK, /* Machine check exception */ - PPC_INTERRUPT_EXT, /* External interrupt */ - PPC_INTERRUPT_SMI, /* System management interrupt */ - PPC_INTERRUPT_CEXT, /* Critical external interrupt */ - PPC_INTERRUPT_DEBUG, /* External debug exception */ - PPC_INTERRUPT_THERM, /* Thermal exception */ + PPC_INTERRUPT_RESET = 0x00001, /* Reset exception */ + PPC_INTERRUPT_WAKEUP = 0x00002, /* Wakeup exception */ + PPC_INTERRUPT_MCK = 0x00004, /* Machine check exception */ + PPC_INTERRUPT_EXT = 0x00008, /* External interrupt */ + PPC_INTERRUPT_SMI = 0x00010, /* System management interrupt */ + PPC_INTERRUPT_CEXT = 0x00020, /* Critical external interrupt */ + PPC_INTERRUPT_DEBUG = 0x00040, /* External debug exception */ + PPC_INTERRUPT_THERM = 0x00080, /* Thermal exception */ /* Internal hardware exception sources */ - PPC_INTERRUPT_DECR, /* Decrementer exception */ - PPC_INTERRUPT_HDECR, /* Hypervisor decrementer exception */ - PPC_INTERRUPT_PIT, /* Programmable interval timer interrupt */ - PPC_INTERRUPT_FIT, /* Fixed interval timer interrupt */ - PPC_INTERRUPT_WDT, /* Watchdog timer interrupt */ - PPC_INTERRUPT_CDOORBELL, /* Critical doorbell interrupt */ - PPC_INTERRUPT_DOORBELL, /* Doorbell interrupt */ - PPC_INTERRUPT_PERFM, /* Performance monitor interrupt */ - PPC_INTERRUPT_HMI, /* Hypervisor Maintenance interrupt */ - PPC_INTERRUPT_HDOORBELL, /* Hypervisor Doorbell interrupt */ - PPC_INTERRUPT_HVIRT, /* Hypervisor virtualization interrupt */ - PPC_INTERRUPT_EBB, /* Event-based Branch exception */ + PPC_INTERRUPT_DECR = 0x00100, /* Decrementer exception */ + PPC_INTERRUPT_HDECR = 0x00200, /* Hypervisor decrementer exception */ + PPC_INTERRUPT_PIT = 0x00400, /* Programmable interval timer int. */ + PPC_INTERRUPT_FIT = 0x00800, /* Fixed interval timer interrupt */ + PPC_INTERRUPT_WDT = 0x01000, /* Watchdog timer interrupt */ + PPC_INTERRUPT_CDOORBELL = 0x02000, /* Critical doorbell interrupt */ + PPC_INTERRUPT_DOORBELL = 0x04000, /* Doorbell interrupt */ + PPC_INTERRUPT_PERFM = 0x08000, /* Performance monitor interrupt */ + PPC_INTERRUPT_HMI = 0x10000, /* Hypervisor Maintenance interrupt */ + PPC_INTERRUPT_HDOORBELL = 0x20000, /* Hypervisor Doorbell interrupt */ + PPC_INTERRUPT_HVIRT = 0x40000, /* Hypervisor virtualization interrupt */ + PPC_INTERRUPT_EBB = 0x80000, /* Event-based Branch exception */ }; /* Processor Compatibility mask (PCR) */ @@ -2459,6 +2676,34 @@ enum { HMER_XSCOM_STATUS_MASK = PPC_BITMASK(21, 23), }; +/* TFMR */ +enum { + TFMR_CONTROL_MASK = PPC_BITMASK(0, 24), + TFMR_MASK_HMI = PPC_BIT(10), + TFMR_TB_ECLIPZ = PPC_BIT(14), + TFMR_LOAD_TOD_MOD = PPC_BIT(16), + TFMR_MOVE_CHIP_TOD_TO_TB = PPC_BIT(18), + TFMR_CLEAR_TB_ERRORS = PPC_BIT(24), + TFMR_STATUS_MASK = PPC_BITMASK(25, 63), + TFMR_TBST_ENCODED = PPC_BITMASK(28, 31), /* TBST = TB State */ + TFMR_TBST_LAST = PPC_BITMASK(32, 35), /* Previous TBST */ + TFMR_TB_ENABLED = PPC_BIT(40), + TFMR_TB_VALID = PPC_BIT(41), + TFMR_TB_SYNC_OCCURED = PPC_BIT(42), + TFMR_FIRMWARE_CONTROL_ERROR = PPC_BIT(46), +}; + +/* TFMR TBST (Time Base State Machine). */ +enum { + TBST_RESET = 0x0, + TBST_SEND_TOD_MOD = 0x1, + TBST_NOT_SET = 0x2, + TBST_SYNC_WAIT = 0x6, + TBST_GET_TOD = 0x7, + TBST_TB_RUNNING = 0x8, + TBST_TB_ERROR = 0x9, +}; + /*****************************************************************************/ #define is_isa300(ctx) (!!(ctx->insns_flags2 & PPC2_ISA300)) @@ -2472,11 +2717,11 @@ void cpu_write_xer(CPUPPCState *env, target_ulong xer); #define is_book3s_arch2x(ctx) (!!((ctx)->insns_flags & PPC_SEGMENT_64B)) #ifdef CONFIG_DEBUG_TCG -void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags); +void cpu_get_tb_cpu_state(CPUPPCState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags); #else -static inline void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) +static inline void cpu_get_tb_cpu_state(CPUPPCState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { *pc = env->nip; *cs_base = 0; @@ -2713,6 +2958,7 @@ static inline bool ppc_has_spr(PowerPCCPU *cpu, int spr) } #if !defined(CONFIG_USER_ONLY) +/* Sort out endianness of interrupt. Depends on the CPU, HV mode, etc. */ static inline bool ppc_interrupts_little_endian(PowerPCCPU *cpu, bool hv) { PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); @@ -2741,6 +2987,8 @@ void dump_mmu(CPUPPCState *env); void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len); void ppc_store_vscr(CPUPPCState *env, uint32_t vscr); uint32_t ppc_get_vscr(CPUPPCState *env); +void ppc_set_cr(CPUPPCState *env, uint64_t cr); +uint64_t ppc_get_cr(const CPUPPCState *env); /*****************************************************************************/ /* Power management enable checks */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 0f891afa04..6241de62ce 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -20,8 +20,7 @@ #include "qemu/osdep.h" #include "disas/dis-asm.h" -#include "exec/gdbstub.h" -#include "kvm_ppc.h" +#include "gdbstub/helpers.h" #include "sysemu/cpus.h" #include "sysemu/hw_accel.h" #include "sysemu/tcg.h" @@ -40,13 +39,18 @@ #include "qemu/cutils.h" #include "disas/capstone.h" #include "fpu/softfloat.h" -#include "qapi/qapi-commands-machine-target.h" #include "helper_regs.h" #include "internal.h" #include "spr_common.h" #include "power8-pmu.h" +#ifndef CONFIG_USER_ONLY +#include "hw/boards.h" +#include "hw/intc/intc.h" +#include "kvm_ppc.h" +#endif + /* #define PPC_DEBUG_SPR */ /* #define USE_APPLE_GDB */ @@ -1626,6 +1630,7 @@ static void register_8xx_sprs(CPUPPCState *env) * HSRR0 => SPR 314 (Power 2.04 hypv) * HSRR1 => SPR 315 (Power 2.04 hypv) * LPIDR => SPR 317 (970) + * HEIR => SPR 339 (Power 2.05 hypv) (64-bit reg from 3.1) * EPR => SPR 702 (Power 2.04 emb) * perf => 768-783 (Power 2.04) * perf => 784-799 (Power 2.04) @@ -1637,7 +1642,7 @@ static void register_8xx_sprs(CPUPPCState *env) /*****************************************************************************/ /* Exception vectors models */ -static void init_excp_4xx_softmmu(CPUPPCState *env) +static void init_excp_4xx(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000100; @@ -2115,7 +2120,7 @@ static void init_proc_405(CPUPPCState *env) env->id_tlbs = 0; env->tlb_type = TLB_EMB; #endif - init_excp_4xx_softmmu(env); + init_excp_4xx(env); env->dcache_line_size = 32; env->icache_line_size = 32; /* Allocate hardware IRQ controller */ @@ -5057,7 +5062,7 @@ static void register_970_hid_sprs(CPUPPCState *env) static void register_970_hior_sprs(CPUPPCState *env) { - spr_register(env, SPR_HIOR, "SPR_HIOR", + spr_register(env, SPR_HIOR, "HIOR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_hior, &spr_write_hior, 0x00000000); @@ -5065,11 +5070,11 @@ static void register_970_hior_sprs(CPUPPCState *env) static void register_book3s_ctrl_sprs(CPUPPCState *env) { - spr_register(env, SPR_CTRL, "SPR_CTRL", + spr_register(env, SPR_CTRL, "CTRL", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, &spr_write_CTRL, 0x00000000); - spr_register(env, SPR_UCTRL, "SPR_UCTRL", + spr_register(env, SPR_UCTRL, "UCTRL", &spr_read_ureg, SPR_NOACCESS, &spr_read_ureg, SPR_NOACCESS, 0x00000000); @@ -5082,8 +5087,8 @@ static void register_book3s_altivec_sprs(CPUPPCState *env) } spr_register_kvm(env, SPR_VRSAVE, "VRSAVE", - &spr_read_generic, &spr_write_generic, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic32, + &spr_read_generic, &spr_write_generic32, KVM_REG_PPC_VRSAVE, 0x00000000); } @@ -5112,17 +5117,17 @@ static void register_book3s_207_dbg_sprs(CPUPPCState *env) spr_register_kvm_hv(env, SPR_DAWR0, "DAWR0", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_dawr0, KVM_REG_PPC_DAWR, 0x00000000); spr_register_kvm_hv(env, SPR_DAWRX0, "DAWRX0", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_dawrx0, KVM_REG_PPC_DAWRX, 0x00000000); spr_register_kvm_hv(env, SPR_CIABR, "CIABR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_ciabr, KVM_REG_PPC_CIABR, 0x00000000); } @@ -5303,6 +5308,38 @@ static void register_power8_pmu_user_sprs(CPUPPCState *env) 0x00000000); } +static void register_power10_pmu_sup_sprs(CPUPPCState *env) +{ + spr_register_kvm(env, SPR_POWER_MMCR3, "MMCR3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_MMCR3, 0x00000000); + spr_register_kvm(env, SPR_POWER_SIER2, "SIER2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_SIER2, 0x00000000); + spr_register_kvm(env, SPR_POWER_SIER3, "SIER3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_SIER3, 0x00000000); +} + +static void register_power10_pmu_user_sprs(CPUPPCState *env) +{ + spr_register(env, SPR_POWER_UMMCR3, "UMMCR3", + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_POWER_USIER2, "USIER2", + &spr_read_generic, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_POWER_USIER3, "USIER3", + &spr_read_generic, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + static void register_power5p_ear_sprs(CPUPPCState *env) { /* External access control */ @@ -5342,7 +5379,7 @@ static void register_970_lpar_sprs(CPUPPCState *env) static void register_power5p_lpar_sprs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) - /* Logical partitionning */ + /* Logical partitioning */ spr_register_kvm_hv(env, SPR_LPCR, "LPCR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, @@ -5365,31 +5402,6 @@ static void register_book3s_ids_sprs(CPUPPCState *env) &spr_read_generic, SPR_NOACCESS, &spr_read_generic, NULL, 0x00000000); - spr_register_hv(env, SPR_HID0, "HID0", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - spr_register_hv(env, SPR_TSCR, "TSCR", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - spr_register_hv(env, SPR_HMER, "HMER", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_hmer, - 0x00000000); - spr_register_hv(env, SPR_HMEER, "HMEER", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - spr_register_hv(env, SPR_TFMR, "TFMR", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); spr_register_hv(env, SPR_LPIDR, "LPIDR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, @@ -5403,7 +5415,7 @@ static void register_book3s_ids_sprs(CPUPPCState *env) spr_register_hv(env, SPR_MMCRC, "MMCRC", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic32, 0x00000000); spr_register_hv(env, SPR_MMCRH, "MMCRH", SPR_NOACCESS, SPR_NOACCESS, @@ -5438,7 +5450,7 @@ static void register_book3s_ids_sprs(CPUPPCState *env) spr_register_hv(env, SPR_HDSISR, "HDSISR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic32, 0x00000000); spr_register_hv(env, SPR_HRMOR, "HRMOR", SPR_NOACCESS, SPR_NOACCESS, @@ -5485,7 +5497,7 @@ static void register_book3s_purr_sprs(CPUPPCState *env) static void register_power6_dbg_sprs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) - spr_register(env, SPR_CFAR, "SPR_CFAR", + spr_register(env, SPR_CFAR, "CFAR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_cfar, &spr_write_cfar, 0x00000000); @@ -5503,7 +5515,7 @@ static void register_power5p_common_sprs(CPUPPCState *env) static void register_power6_common_sprs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) - spr_register_kvm(env, SPR_DSCR, "SPR_DSCR", + spr_register_kvm(env, SPR_DSCR, "DSCR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_DSCR, 0x00000000); @@ -5519,6 +5531,24 @@ static void register_power6_common_sprs(CPUPPCState *env) 0x00000000); } +static void register_HEIR32_spr(CPUPPCState *env) +{ + spr_register_hv(env, SPR_HEIR, "HEIR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic32, + 0x00000000); +} + +static void register_HEIR64_spr(CPUPPCState *env) +{ + spr_register_hv(env, SPR_HEIR, "HEIR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + static void register_power8_tce_address_control_sprs(CPUPPCState *env) { spr_register_kvm(env, SPR_TAR, "TAR", @@ -5633,15 +5663,71 @@ static void register_power8_ic_sprs(CPUPPCState *env) #endif } +/* SPRs specific to IBM POWER CPUs */ +static void register_power_common_book4_sprs(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + spr_register_hv(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_core_write_generic, + 0x00000000); + spr_register_hv(env, SPR_TSCR, "TSCR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic32, + 0x00000000); + spr_register_hv(env, SPR_HMER, "HMER", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_hmer, + 0x00000000); + spr_register_hv(env, SPR_HMEER, "HMEER", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_TFMR, "TFMR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_tfmr, &spr_write_tfmr, + 0x00000000); + spr_register_hv(env, SPR_TRIG1, "TRIG1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_access_nop, &spr_write_generic, + &spr_access_nop, &spr_write_generic, + 0x00000000); + spr_register_hv(env, SPR_TRIG2, "TRIG2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_access_nop, &spr_write_generic, + &spr_access_nop, &spr_write_generic, + 0x00000000); +#endif +} + +static void register_power9_book4_sprs(CPUPPCState *env) +{ + /* Add a number of P9 book4 registers */ + register_power_common_book4_sprs(env); +#if !defined(CONFIG_USER_ONLY) + spr_register_kvm(env, SPR_WORT, "WORT", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_WORT, 0); +#endif +} + static void register_power8_book4_sprs(CPUPPCState *env) { /* Add a number of P8 book4 registers */ + register_power_common_book4_sprs(env); #if !defined(CONFIG_USER_ONLY) spr_register_kvm(env, SPR_ACOP, "ACOP", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_ACOP, 0); - spr_register_kvm(env, SPR_BOOKS_PID, "PID", + /* PID is only in BookE in ISA v2.07 */ + spr_register_kvm(env, SPR_BOOKS_PID, "PIDR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_pidr, KVM_REG_PPC_PID, 0); @@ -5656,13 +5742,15 @@ static void register_power7_book4_sprs(CPUPPCState *env) { /* Add a number of P7 book4 registers */ #if !defined(CONFIG_USER_ONLY) + register_power_common_book4_sprs(env); spr_register_kvm(env, SPR_ACOP, "ACOP", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_ACOP, 0); - spr_register_kvm(env, SPR_BOOKS_PID, "PID", + /* PID is only in BookE in ISA v2.06 */ + spr_register_kvm(env, SPR_BOOKS_PID, "PIDR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic32, KVM_REG_PPC_PID, 0); #endif } @@ -5693,9 +5781,65 @@ static void register_power9_mmu_sprs(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x0000000000000000); + /* PID is part of the BookS ISA from v3.0 */ + spr_register_kvm(env, SPR_BOOKS_PID, "PIDR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pidr, + KVM_REG_PPC_PID, 0); #endif } +static void register_power10_hash_sprs(CPUPPCState *env) +{ + /* + * it's the OS responsibility to generate a random value for the registers + * in each process' context. So, initialize it with 0 here. + */ + uint64_t hashkeyr_initial_value = 0, hashpkeyr_initial_value = 0; +#if defined(CONFIG_USER_ONLY) + /* in linux-user, setup the hash register with a random value */ + GRand *rand = g_rand_new(); + hashkeyr_initial_value = + ((uint64_t)g_rand_int(rand) << 32) | (uint64_t)g_rand_int(rand); + hashpkeyr_initial_value = + ((uint64_t)g_rand_int(rand) << 32) | (uint64_t)g_rand_int(rand); + g_rand_free(rand); +#endif + spr_register(env, SPR_HASHKEYR, "HASHKEYR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + hashkeyr_initial_value); + spr_register_hv(env, SPR_HASHPKEYR, "HASHPKEYR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + hashpkeyr_initial_value); +} + +static void register_power10_dexcr_sprs(CPUPPCState *env) +{ + spr_register(env, SPR_DEXCR, "DEXCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0); + + spr_register(env, SPR_UDEXCR, "UDEXCR", + &spr_read_dexcr_ureg, SPR_NOACCESS, + &spr_read_dexcr_ureg, SPR_NOACCESS, + 0); + + spr_register_hv(env, SPR_HDEXCR, "HDEXCR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0); + + spr_register(env, SPR_UHDEXCR, "UHDEXCR", + &spr_read_dexcr_ureg, SPR_NOACCESS, + &spr_read_dexcr_ureg, SPR_NOACCESS, + 0); +} + /* * Initialize PMU counter overflow timers for Power8 and * newer Power chips when using TCG. @@ -5786,7 +5930,7 @@ POWERPC_FAMILY(970)(ObjectClass *oc, void *data) (1ull << MSR_PMM) | (1ull << MSR_RI); pcc->mmu_model = POWERPC_MMU_64B; -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) pcc->hash64_opts = &ppc_hash64_opts_basic; #endif pcc->excp_model = POWERPC_EXCP_970; @@ -5865,7 +6009,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) 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) +#if !defined(CONFIG_USER_ONLY) pcc->hash64_opts = &ppc_hash64_opts_basic; pcc->lrg_decr_bits = 32; #endif @@ -5896,6 +6040,7 @@ static void init_proc_POWER7(CPUPPCState *env) register_power5p_ear_sprs(env); register_power5p_tb_sprs(env); register_power6_common_sprs(env); + register_HEIR32_spr(env); register_power6_dbg_sprs(env); register_power7_book4_sprs(env); @@ -5908,57 +6053,31 @@ static void init_proc_POWER7(CPUPPCState *env) ppcPOWER7_irq_init(env_archcpu(env)); } -static bool ppc_pvr_match_power7(PowerPCCPUClass *pcc, uint32_t pvr) +static bool ppc_pvr_match_power7(PowerPCCPUClass *pcc, uint32_t pvr, bool best) { - if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER7P_BASE) { - return true; - } - if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER7_BASE) { - return true; - } - return false; -} + uint32_t base = pvr & CPU_POWERPC_POWER_SERVER_MASK; + uint32_t pcc_base = pcc->pvr & CPU_POWERPC_POWER_SERVER_MASK; -static bool cpu_has_work_POWER7(CPUState *cs) -{ - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + if (!best) { + if (base == CPU_POWERPC_POWER7_BASE) { + return true; + } + if (base == CPU_POWERPC_POWER7P_BASE) { + return true; + } + } - if (cs->halted) { - if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { - return false; - } - if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) && - (env->spr[SPR_LPCR] & LPCR_P7_PECE0)) { - return true; - } - if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) && - (env->spr[SPR_LPCR] & LPCR_P7_PECE1)) { - return true; - } - if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) && - (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) { - return true; - } - if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) && - (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) { - return true; - } - if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) { - return true; - } + if (base != pcc_base) { return false; - } else { - return FIELD_EX64(env->msr, MSR, EE) && - (cs->interrupt_request & CPU_INTERRUPT_HARD); } + + return true; } POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); - CPUClass *cc = CPU_CLASS(oc); dc->fw_name = "PowerPC,POWER7"; dc->desc = "POWER7"; @@ -5967,7 +6086,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->pcr_supported = PCR_COMPAT_2_06 | PCR_COMPAT_2_05; pcc->init_proc = init_proc_POWER7; pcc->check_pow = check_pow_nocheck; - cc->has_work = cpu_has_work_POWER7; 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 | @@ -5985,7 +6103,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | PPC2_FP_CVT_S64 | - PPC2_PM_ISA206 | PPC2_MEM_LWSYNC; + PPC2_PM_ISA206 | PPC2_MEM_LWSYNC | PPC2_BCDA_ISA206; pcc->msr_mask = (1ull << MSR_SF) | (1ull << MSR_VR) | (1ull << MSR_VSX) | @@ -6009,7 +6127,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) 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) +#if !defined(CONFIG_USER_ONLY) pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->lrg_decr_bits = 32; #endif @@ -6045,6 +6163,7 @@ static void init_proc_POWER8(CPUPPCState *env) register_power5p_ear_sprs(env); register_power5p_tb_sprs(env); register_power6_common_sprs(env); + register_HEIR32_spr(env); register_power6_dbg_sprs(env); register_power8_tce_address_control_sprs(env); register_power8_ids_sprs(env); @@ -6069,68 +6188,33 @@ static void init_proc_POWER8(CPUPPCState *env) ppcPOWER7_irq_init(env_archcpu(env)); } -static bool ppc_pvr_match_power8(PowerPCCPUClass *pcc, uint32_t pvr) +static bool ppc_pvr_match_power8(PowerPCCPUClass *pcc, uint32_t pvr, bool best) { - if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER8NVL_BASE) { - return true; - } - if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER8E_BASE) { - return true; - } - if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER8_BASE) { - return true; - } - return false; -} + uint32_t base = pvr & CPU_POWERPC_POWER_SERVER_MASK; + uint32_t pcc_base = pcc->pvr & CPU_POWERPC_POWER_SERVER_MASK; -static bool cpu_has_work_POWER8(CPUState *cs) -{ - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - - if (cs->halted) { - if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { - return false; - } - if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) && - (env->spr[SPR_LPCR] & LPCR_P8_PECE2)) { + if (!best) { + if (base == CPU_POWERPC_POWER8_BASE) { return true; } - if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) && - (env->spr[SPR_LPCR] & LPCR_P8_PECE3)) { + if (base == CPU_POWERPC_POWER8E_BASE) { return true; } - if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) && - (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) { - return true; - } - if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) && - (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) { - return true; - } - if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DOORBELL)) && - (env->spr[SPR_LPCR] & LPCR_P8_PECE0)) { - return true; - } - if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HDOORBELL)) && - (env->spr[SPR_LPCR] & LPCR_P8_PECE1)) { - return true; - } - if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) { + if (base == CPU_POWERPC_POWER8NVL_BASE) { return true; } + } + if (base != pcc_base) { return false; - } else { - return FIELD_EX64(env->msr, MSR, EE) && - (cs->interrupt_request & CPU_INTERRUPT_HARD); } + + return true; } POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); - CPUClass *cc = CPU_CLASS(oc); dc->fw_name = "PowerPC,POWER8"; dc->desc = "POWER8"; @@ -6139,7 +6223,6 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->pcr_supported = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05; pcc->init_proc = init_proc_POWER8; pcc->check_pow = check_pow_nocheck; - cc->has_work = cpu_has_work_POWER8; 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 | @@ -6159,7 +6242,8 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | - PPC2_TM | PPC2_PM_ISA206 | PPC2_MEM_LWSYNC; + PPC2_TM | PPC2_PM_ISA206 | PPC2_MEM_LWSYNC | + PPC2_BCDA_ISA206; pcc->msr_mask = (1ull << MSR_SF) | (1ull << MSR_HV) | (1ull << MSR_TM) | @@ -6188,7 +6272,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) 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) +#if !defined(CONFIG_USER_ONLY) pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->lrg_decr_bits = 32; pcc->n_host_threads = 8; @@ -6204,7 +6288,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x8000; } -#ifdef CONFIG_SOFTMMU +#ifndef CONFIG_USER_ONLY /* * Radix pg sizes and AP encodings for dt node ibm,processor-radix-AP-encodings * Encoded as array of int_32s in the form: @@ -6221,7 +6305,7 @@ static struct ppc_radix_page_info POWER9_radix_page_info = { 0x4000001e /* 1G - enc: 0x2 */ } }; -#endif /* CONFIG_SOFTMMU */ +#endif /* CONFIG_USER_ONLY */ static void init_proc_POWER9(CPUPPCState *env) { @@ -6242,6 +6326,7 @@ static void init_proc_POWER9(CPUPPCState *env) register_power5p_ear_sprs(env); register_power5p_tb_sprs(env); register_power6_common_sprs(env); + register_HEIR32_spr(env); register_power6_dbg_sprs(env); register_power8_tce_address_control_sprs(env); register_power8_ids_sprs(env); @@ -6254,7 +6339,7 @@ static void init_proc_POWER9(CPUPPCState *env) register_power8_dpdes_sprs(env); register_vtb_sprs(env); register_power8_ic_sprs(env); - register_power8_book4_sprs(env); + register_power9_book4_sprs(env); register_power8_rpr_sprs(env); register_power9_mmu_sprs(env); @@ -6277,79 +6362,47 @@ static void init_proc_POWER9(CPUPPCState *env) ppcPOWER9_irq_init(env_archcpu(env)); } -static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr) +static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr, bool best) { - if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER9_BASE) { - return true; - } - return false; -} + uint32_t base = pvr & CPU_POWERPC_POWER_SERVER_MASK; + uint32_t pcc_base = pcc->pvr & CPU_POWERPC_POWER_SERVER_MASK; -static bool cpu_has_work_POWER9(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)) { + if (!best) { + if (base == CPU_POWERPC_POWER9_BASE) { 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 || !FIELD_EX64_HV(env->msr) || - FIELD_EX64(env->msr, MSR, PR)) { + } + + if (base != pcc_base) { + return false; + } + + if ((pvr & 0x0f00) != (pcc->pvr & 0x0f00)) { + /* Major DD version does not match */ + return false; + } + + if ((pvr & 0x0f00) == 0x200) { + if ((pvr & 0xf) < 2) { + /* DD2.0, DD2.1 match power9_v2.0 */ + if ((pcc->pvr & 0xf) == 0) { + return true; + } + } else { + /* DD2.2, DD2.3 match power9_v2.2 */ + if ((pcc->pvr & 0xf) == 2) { 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 FIELD_EX64(env->msr, MSR, EE) && - (cs->interrupt_request & CPU_INTERRUPT_HARD); } + + return false; } POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); - CPUClass *cc = CPU_CLASS(oc); dc->fw_name = "PowerPC,POWER9"; dc->desc = "POWER9"; @@ -6359,7 +6412,6 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) PCR_COMPAT_2_05; pcc->init_proc = init_proc_POWER9; pcc->check_pow = check_pow_nocheck; - cc->has_work = cpu_has_work_POWER9; 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 | @@ -6368,7 +6420,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) PPC_FLOAT_EXT | PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | - PPC_MEM_TLBSYNC | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD | @@ -6379,7 +6431,8 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) 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 | PPC2_MEM_LWSYNC; + PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL | PPC2_MEM_LWSYNC | + PPC2_BCDA_ISA206; pcc->msr_mask = (1ull << MSR_SF) | (1ull << MSR_HV) | (1ull << MSR_TM) | @@ -6407,7 +6460,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) 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) +#if !defined(CONFIG_USER_ONLY) /* segment page size remain the same */ pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->radix_page_info = &POWER9_radix_page_info; @@ -6425,7 +6478,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x8000; } -#ifdef CONFIG_SOFTMMU +#ifndef CONFIG_USER_ONLY /* * Radix pg sizes and AP encodings for dt node ibm,processor-radix-AP-encodings * Encoded as array of int_32s in the form: @@ -6442,7 +6495,7 @@ static struct ppc_radix_page_info POWER10_radix_page_info = { 0x4000001e /* 1G - enc: 0x2 */ } }; -#endif /* CONFIG_SOFTMMU */ +#endif /* !CONFIG_USER_ONLY */ static void init_proc_POWER10(CPUPPCState *env) { @@ -6463,6 +6516,7 @@ static void init_proc_POWER10(CPUPPCState *env) register_power5p_ear_sprs(env); register_power5p_tb_sprs(env); register_power6_common_sprs(env); + register_HEIR64_spr(env); register_power6_dbg_sprs(env); register_power8_tce_address_control_sprs(env); register_power8_ids_sprs(env); @@ -6475,9 +6529,13 @@ static void init_proc_POWER10(CPUPPCState *env) register_power8_dpdes_sprs(env); register_vtb_sprs(env); register_power8_ic_sprs(env); - register_power8_book4_sprs(env); + register_power9_book4_sprs(env); register_power8_rpr_sprs(env); register_power9_mmu_sprs(env); + register_power10_hash_sprs(env); + register_power10_dexcr_sprs(env); + register_power10_pmu_sup_sprs(env); + register_power10_pmu_user_sprs(env); /* FIXME: Filter fields properly based on privilege level */ spr_register_kvm_hv(env, SPR_PSSCR, "PSSCR", NULL, NULL, NULL, NULL, @@ -6493,79 +6551,33 @@ static void init_proc_POWER10(CPUPPCState *env) ppcPOWER9_irq_init(env_archcpu(env)); } -static bool ppc_pvr_match_power10(PowerPCCPUClass *pcc, uint32_t pvr) +static bool ppc_pvr_match_power10(PowerPCCPUClass *pcc, uint32_t pvr, bool best) { - if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER10_BASE) { + uint32_t base = pvr & CPU_POWERPC_POWER_SERVER_MASK; + uint32_t pcc_base = pcc->pvr & CPU_POWERPC_POWER_SERVER_MASK; + + if (!best) { + if (base == CPU_POWERPC_POWER10_BASE) { + return true; + } + } + + if (base != pcc_base) { + return false; + } + + if ((pvr & 0x0f00) == (pcc->pvr & 0x0f00)) { + /* Major DD version matches power10_v2.0 */ 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 || !FIELD_EX64_HV(env->msr) || - FIELD_EX64(env->msr, 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 FIELD_EX64(env->msr, 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"; @@ -6576,7 +6588,6 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) 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 | @@ -6585,7 +6596,7 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) PPC_FLOAT_EXT | PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | - PPC_MEM_TLBSYNC | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD | @@ -6596,11 +6607,10 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) 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 | PPC2_ISA310 | - PPC2_MEM_LWSYNC; + PPC2_ISA300 | PPC2_PRCNTL | PPC2_ISA310 | + PPC2_MEM_LWSYNC | PPC2_BCDA_ISA206; pcc->msr_mask = (1ull << MSR_SF) | (1ull << MSR_HV) | - (1ull << MSR_TM) | (1ull << MSR_VR) | (1ull << MSR_VSX) | (1ull << MSR_EE) | @@ -6628,7 +6638,7 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; pcc->mmu_model = POWERPC_MMU_3_00; -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) /* segment page size remain the same */ pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->radix_page_info = &POWER10_radix_page_info; @@ -6640,7 +6650,7 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) 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 | POWERPC_FLAG_SCV; + POWERPC_FLAG_VSX | POWERPC_FLAG_SCV; pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; } @@ -6659,6 +6669,18 @@ void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) env->msr_mask &= ~MSR_HVB; } +void cpu_ppc_set_1lpar(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + + /* + * pseries SMT means "LPAR per core" mode, e.g., msgsndp is usable + * between threads. + */ + if (env->flags & POWERPC_FLAG_SMT) { + env->flags |= POWERPC_FLAG_SMT_1LPAR; + } +} #endif /* !defined(CONFIG_USER_ONLY) */ #endif /* defined(TARGET_PPC64) */ @@ -6672,7 +6694,6 @@ static void init_ppc_proc(PowerPCCPU *cpu) #if !defined(CONFIG_USER_ONLY) int i; - env->irq_inputs = NULL; /* Set all exception vectors to an invalid address */ for (i = 0; i < POWERPC_EXCP_NB; i++) { env->excp_vectors[i] = (target_ulong)(-1ULL); @@ -6691,10 +6712,6 @@ static void init_ppc_proc(PowerPCCPU *cpu) /* PowerPC implementation specific initialisations (SPRs, timers, ...) */ (*pcc->init_proc)(env); -#if !defined(CONFIG_USER_ONLY) - ppc_gdb_gen_spr_xml(cpu); -#endif - /* MSR bits & flags consistency checks */ if (env->msr_mask & (1 << 25)) { switch (env->flags & (POWERPC_FLAG_SPE | POWERPC_FLAG_VRE)) { @@ -6802,10 +6819,6 @@ static void init_ppc_proc(PowerPCCPU *cpu) /* Pre-compute some useful values */ env->tlb_per_way = env->nb_tlb / env->nb_ways; } - if (env->irq_inputs == NULL) { - warn_report("no internal IRQ controller registered." - " Attempt QEMU to crash very soon !"); - } #endif if (env->check_pow == NULL) { warn_report("no power management check handler registered." @@ -6818,6 +6831,7 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); PowerPCCPU *cpu = POWERPC_CPU(dev); + CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); Error *local_err = NULL; @@ -6849,6 +6863,10 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp) pcc->parent_realize(dev, errp); + if (env_cpu(env)->nr_threads > 1) { + env->flags |= POWERPC_FLAG_SMT; + } + return; unrealize: @@ -6909,7 +6927,7 @@ static gint ppc_cpu_compare_class_pvr_mask(gconstpointer a, gconstpointer b) return -1; } - if (pcc->pvr_match(pcc, pvr)) { + if (pcc->pvr_match(pcc, pvr, true)) { return 0; } @@ -6944,7 +6962,7 @@ static const char *ppc_cpu_lookup_alias(const char *alias) return NULL; } -static ObjectClass *ppc_cpu_class_by_name(const char *name) +ObjectClass *ppc_cpu_class_by_name(const char *name) { char *cpu_model, *typename; ObjectClass *oc; @@ -6963,6 +6981,21 @@ static ObjectClass *ppc_cpu_class_by_name(const char *name) } } + /* + * All ppc CPUs represent hardware that exists in the real world, i.e.: we + * do not have a "max" CPU with all possible emulated features enabled. + * Return the default CPU type for the machine because that has greater + * chance of being useful as the "max" CPU. + */ +#if !defined(CONFIG_USER_ONLY) + if (strcmp(name, "max") == 0) { + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + if (mc) { + return object_class_by_name(mc->default_cpu_type); + } + } +#endif + cpu_model = g_ascii_strdown(name, -1); p = ppc_cpu_lookup_alias(cpu_model); if (p) { @@ -7029,8 +7062,7 @@ static void ppc_cpu_list_entry(gpointer data, gpointer user_data) return; } - name = g_strndup(typename, - strlen(typename) - strlen(POWERPC_CPU_TYPE_SUFFIX)); + name = cpu_model_from_type(typename); qemu_printf("PowerPC %-16s PVR %08x\n", name, pcc->pvr); for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) { PowerPCCPUAlias *alias = &ppc_cpu_aliases[i]; @@ -7069,51 +7101,6 @@ void ppc_cpu_list(void) #endif } -static void ppc_cpu_defs_entry(gpointer data, gpointer user_data) -{ - ObjectClass *oc = data; - CpuDefinitionInfoList **first = user_data; - const char *typename; - CpuDefinitionInfo *info; - - typename = object_class_get_name(oc); - info = g_malloc0(sizeof(*info)); - info->name = g_strndup(typename, - strlen(typename) - strlen(POWERPC_CPU_TYPE_SUFFIX)); - - QAPI_LIST_PREPEND(*first, info); -} - -CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) -{ - CpuDefinitionInfoList *cpu_list = NULL; - GSList *list; - int i; - - list = object_class_get_list(TYPE_POWERPC_CPU, false); - g_slist_foreach(list, ppc_cpu_defs_entry, &cpu_list); - g_slist_free(list); - - for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) { - PowerPCCPUAlias *alias = &ppc_cpu_aliases[i]; - ObjectClass *oc; - CpuDefinitionInfo *info; - - oc = ppc_cpu_class_by_name(alias->model); - if (oc == NULL) { - continue; - } - - info = g_malloc0(sizeof(*info)); - info->name = g_strdup(alias->alias); - info->q_typename = g_strdup(object_class_get_name(oc)); - - QAPI_LIST_PREPEND(cpu_list, info); - } - - return cpu_list; -} - static void ppc_cpu_set_pc(CPUState *cs, vaddr value) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -7121,25 +7108,46 @@ static void ppc_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.nip = value; } -static bool ppc_cpu_has_work(CPUState *cs) +static vaddr ppc_cpu_get_pc(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - return FIELD_EX64(env->msr, MSR, EE) && - (cs->interrupt_request & CPU_INTERRUPT_HARD); + return cpu->env.nip; } -static void ppc_cpu_reset(DeviceState *dev) +#ifdef CONFIG_TCG +static void ppc_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) { - CPUState *s = CPU(dev); - PowerPCCPU *cpu = POWERPC_CPU(s); - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + PowerPCCPU *cpu = POWERPC_CPU(cs); + + cpu->env.nip = data[0]; +} +#endif /* CONFIG_TCG */ + +static bool ppc_cpu_has_work(CPUState *cs) +{ + return cs->interrupt_request & CPU_INTERRUPT_HARD; +} + +static int ppc_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return ppc_env_mmu_index(cpu_env(cs), ifetch); +} + +static void ppc_cpu_reset_hold(Object *obj) +{ + CPUState *cs = CPU(obj); + PowerPCCPU *cpu = POWERPC_CPU(cs); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(obj); CPUPPCState *env = &cpu->env; target_ulong msr; int i; - pcc->parent_reset(dev); + if (pcc->parent_phases.hold) { + pcc->parent_phases.hold(obj); + } msr = (target_ulong)0; msr |= (target_ulong)MSR_HVB; @@ -7181,17 +7189,22 @@ static void ppc_cpu_reset(DeviceState *dev) env->nip = env->hreset_vector | env->excp_prefix; if (tcg_enabled()) { + cpu_breakpoint_remove_all(cs, BP_CPU); + cpu_watchpoint_remove_all(cs, BP_CPU); if (env->mmu_model != POWERPC_MMU_REAL) { ppc_tlb_invalidate_all(env); } - pmu_update_summaries(env); + pmu_mmcr01_updated(env); } + + /* clean any pending stop state */ + env->resume_as_sreset = 0; #endif hreg_compute_hflags(env); env->reserve_addr = (target_ulong)-1ULL; /* Be sure no exception or interrupt is pending */ env->pending_interrupts = 0; - s->exception_index = POWERPC_EXCP_NONE; + cs->exception_index = POWERPC_EXCP_NONE; env->error_code = 0; ppc_irq_reset(cpu); @@ -7213,12 +7226,19 @@ static void ppc_cpu_reset(DeviceState *dev) static bool ppc_cpu_is_big_endian(CPUState *cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - cpu_synchronize_state(cs); - return !FIELD_EX64(env->msr, MSR, LE); + return !FIELD_EX64(cpu_env(cs)->msr, MSR, LE); +} + +static bool ppc_get_irq_stats(InterruptStatsProvider *obj, + uint64_t **irq_counts, unsigned int *nb_irqs) +{ + CPUPPCState *env = &POWERPC_CPU(obj)->env; + + *irq_counts = env->excp_stats; + *nb_irqs = ARRAY_SIZE(env->excp_stats); + return true; } #ifdef CONFIG_TCG @@ -7253,7 +7273,6 @@ static void ppc_cpu_instance_init(Object *obj) PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; - cpu_set_cpustate_pointers(cpu); cpu->vcpu_id = UNASSIGNED_CPU_INDEX; env->msr_mask = pcc->msr_mask; @@ -7289,15 +7308,14 @@ static void ppc_cpu_instance_finalize(Object *obj) ppc_hash64_finalize(cpu); } -static bool ppc_pvr_match_default(PowerPCCPUClass *pcc, uint32_t pvr) +static bool ppc_pvr_match_default(PowerPCCPUClass *pcc, uint32_t pvr, bool best) { return pcc->pvr == pvr; } static void ppc_disas_set_info(CPUState *cs, disassemble_info *info) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); if ((env->hflags >> MSR_LE) & 1) { info->endian = BFD_ENDIAN_LITTLE; @@ -7341,8 +7359,9 @@ static const struct SysemuCPUOps ppc_sysemu_ops = { #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps ppc_tcg_ops = { +static const TCGCPUOps ppc_tcg_ops = { .initialize = ppc_translate_init, + .restore_state_to_opc = ppc_restore_state_to_opc, #ifdef CONFIG_USER_ONLY .record_sigsegv = ppc_cpu_record_sigsegv, @@ -7353,6 +7372,10 @@ static const struct TCGCPUOps ppc_tcg_ops = { .cpu_exec_enter = ppc_cpu_exec_enter, .cpu_exec_exit = ppc_cpu_exec_exit, .do_unaligned_access = ppc_cpu_do_unaligned_access, + .do_transaction_failed = ppc_cpu_do_transaction_failed, + .debug_excp_handler = ppc_cpu_debug_excp_handler, + .debug_check_breakpoint = ppc_cpu_debug_check_breakpoint, + .debug_check_watchpoint = ppc_cpu_debug_check_watchpoint, #endif /* !CONFIG_USER_ONLY */ }; #endif /* CONFIG_TCG */ @@ -7362,6 +7385,7 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); device_class_set_parent_realize(dc, ppc_cpu_realize, &pcc->parent_realize); @@ -7370,22 +7394,23 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) pcc->pvr_match = ppc_pvr_match_default; device_class_set_props(dc, ppc_cpu_properties); - device_class_set_parent_reset(dc, ppc_cpu_reset, &pcc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, ppc_cpu_reset_hold, NULL, + &pcc->parent_phases); cc->class_by_name = ppc_cpu_class_by_name; cc->has_work = ppc_cpu_has_work; + cc->mmu_index = ppc_cpu_mmu_index; cc->dump_state = ppc_cpu_dump_state; cc->set_pc = ppc_cpu_set_pc; + cc->get_pc = ppc_cpu_get_pc; cc->gdb_read_register = ppc_cpu_gdb_read_register; cc->gdb_write_register = ppc_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY cc->sysemu_ops = &ppc_sysemu_ops; + INTERRUPT_STATS_PROVIDER_CLASS(oc)->get_statistics = ppc_get_irq_stats; #endif cc->gdb_num_core_regs = 71; -#ifndef CONFIG_USER_ONLY - cc->gdb_get_dynamic_xml = ppc_gdb_get_dynamic_xml; -#endif #ifdef USE_APPLE_GDB cc->gdb_read_register = ppc_cpu_gdb_read_register_apple; cc->gdb_write_register = ppc_cpu_gdb_write_register_apple; @@ -7417,6 +7442,12 @@ static const TypeInfo ppc_cpu_type_info = { .abstract = true, .class_size = sizeof(PowerPCCPUClass), .class_init = ppc_cpu_class_init, +#ifndef CONFIG_USER_ONLY + .interfaces = (InterfaceInfo[]) { + { TYPE_INTERRUPT_STATS_PROVIDER }, + { } + }, +#endif }; #ifndef CONFIG_USER_ONLY @@ -7440,8 +7471,7 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags) #define RGPL 4 #define RFPL 4 - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); int i; qemu_fprintf(f, "NIP " TARGET_FMT_lx " LR " TARGET_FMT_lx " CTR " @@ -7451,18 +7481,16 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags) qemu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx " HF " "%08x iidx %d didx %d\n", env->msr, env->spr[SPR_HID0], env->hflags, - cpu_mmu_index(env, true), cpu_mmu_index(env, false)); -#if !defined(NO_TIMER_DUMP) - qemu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 + ppc_env_mmu_index(env, true), ppc_env_mmu_index(env, false)); #if !defined(CONFIG_USER_ONLY) - " DECR " TARGET_FMT_lu -#endif - "\n", - cpu_ppc_load_tbu(env), cpu_ppc_load_tbl(env) -#if !defined(CONFIG_USER_ONLY) - , cpu_ppc_load_decr(env) -#endif - ); + if (env->tb_env) { + qemu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 + " DECR " TARGET_FMT_lu "\n", cpu_ppc_load_tbu(env), + cpu_ppc_load_tbl(env), cpu_ppc_load_decr(env)); + } +#else + qemu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 "\n", cpu_ppc_load_tbu(env), + cpu_ppc_load_tbl(env)); #endif for (i = 0; i < 32; i++) { if ((i & (RGPL - 1)) == 0) { @@ -7488,8 +7516,8 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags) } qemu_fprintf(f, " %c%c", a, env->crf[i] & 0x01 ? 'O' : ' '); } - qemu_fprintf(f, " ] RES " TARGET_FMT_lx "\n", - env->reserve_addr); + qemu_fprintf(f, " ] RES %03x@" TARGET_FMT_lx "\n", + (int)env->reserve_length, env->reserve_addr); if (flags & CPU_DUMP_FPU) { for (i = 0; i < 32; i++) { diff --git a/target/ppc/dfp_helper.c b/target/ppc/dfp_helper.c index 0d01ac3de0..5967ea07a9 100644 --- a/target/ppc/dfp_helper.c +++ b/target/ppc/dfp_helper.c @@ -42,13 +42,16 @@ static void get_dfp128(ppc_vsr_t *dst, ppc_fprp_t *dfp) static void set_dfp64(ppc_fprp_t *dfp, ppc_vsr_t *src) { - dfp->VsrD(0) = src->VsrD(1); + dfp[0].VsrD(0) = src->VsrD(1); + dfp[0].VsrD(1) = 0ULL; } static void set_dfp128(ppc_fprp_t *dfp, ppc_vsr_t *src) { dfp[0].VsrD(0) = src->VsrD(0); dfp[1].VsrD(0) = src->VsrD(1); + dfp[0].VsrD(1) = 0ULL; + dfp[1].VsrD(1) = 0ULL; } static void set_dfp128_to_avr(ppc_avr_t *dst, ppc_vsr_t *src) @@ -118,7 +121,7 @@ static void dfp_set_round_mode_from_immediate(uint8_t r, uint8_t rmc, case 3: /* use FPSCR rounding mode */ return; default: - assert(0); /* cannot get here */ + g_assert_not_reached(); } } else { /* r == 1 */ switch (rmc & 3) { @@ -135,7 +138,7 @@ static void dfp_set_round_mode_from_immediate(uint8_t r, uint8_t rmc, rnd = DEC_ROUND_HALF_DOWN; break; default: - assert(0); /* cannot get here */ + g_assert_not_reached(); } } decContextSetRounding(&dfp->context, rnd); @@ -1144,6 +1147,26 @@ static inline uint8_t dfp_get_bcd_digit_128(ppc_vsr_t *t, unsigned n) return t->VsrD((n & 0x10) ? 0 : 1) >> ((n << 2) & 63) & 15; } +static inline void dfp_invalid_op_vxcvi_64(struct PPC_DFP *dfp) +{ + /* TODO: fpscr is incorrectly not being saved to env */ + dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXCVI, FPSCR_VE); + if ((dfp->env->fpscr & FP_VE) == 0) { + dfp->vt.VsrD(1) = 0x7c00000000000000; /* QNaN */ + } +} + + +static inline void dfp_invalid_op_vxcvi_128(struct PPC_DFP *dfp) +{ + /* TODO: fpscr is incorrectly not being saved to env */ + dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXCVI, FPSCR_VE); + if ((dfp->env->fpscr & FP_VE) == 0) { + dfp->vt.VsrD(0) = 0x7c00000000000000; /* QNaN */ + dfp->vt.VsrD(1) = 0x0; + } +} + #define DFP_HELPER_ENBCD(op, size) \ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b, \ uint32_t s) \ @@ -1170,7 +1193,8 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b, \ sgn = 0; \ break; \ default: \ - dfp_set_FPSCR_flag(&dfp, FP_VX | FP_VXCVI, FPSCR_VE); \ + dfp_invalid_op_vxcvi_##size(&dfp); \ + set_dfp##size(t, &dfp.vt); \ return; \ } \ } \ @@ -1180,7 +1204,8 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b, \ digits[(size) / 4 - n] = dfp_get_bcd_digit_##size(&dfp.vb, \ offset++); \ if (digits[(size) / 4 - n] > 10) { \ - dfp_set_FPSCR_flag(&dfp, FP_VX | FP_VXCVI, FPSCR_VE); \ + dfp_invalid_op_vxcvi_##size(&dfp); \ + set_dfp##size(t, &dfp.vt); \ return; \ } else { \ nonzero |= (digits[(size) / 4 - n] > 0); \ @@ -1391,3 +1416,68 @@ DFP_HELPER_SHIFT(DSCLI, 64, 1) DFP_HELPER_SHIFT(DSCLIQ, 128, 1) DFP_HELPER_SHIFT(DSCRI, 64, 0) DFP_HELPER_SHIFT(DSCRIQ, 128, 0) + +target_ulong helper_CDTBCD(target_ulong s) +{ + uint64_t res = 0; + uint32_t dec32, declets; + uint8_t bcd[6]; + int i, w, sh; + decNumber a; + + for (w = 1; w >= 0; w--) { + res <<= 32; + declets = extract64(s, 32 * w, 20); + if (declets) { + /* decimal32 with zero exponent and word "w" declets */ + dec32 = (0x225ULL << 20) | declets; + decimal32ToNumber((decimal32 *)&dec32, &a); + decNumberGetBCD(&a, bcd); + for (i = 0; i < a.digits; i++) { + sh = 4 * (a.digits - 1 - i); + res |= (uint64_t)bcd[i] << sh; + } + } + } + + return res; +} + +target_ulong helper_CBCDTD(target_ulong s) +{ + uint64_t res = 0; + uint32_t dec32; + uint8_t bcd[6]; + int w, i, offs; + decNumber a; + decContext context; + + decContextDefault(&context, DEC_INIT_DECIMAL32); + + for (w = 1; w >= 0; w--) { + res <<= 32; + decNumberZero(&a); + /* Extract each BCD field of word "w" */ + for (i = 5; i >= 0; i--) { + offs = 4 * (5 - i) + 32 * w; + bcd[i] = extract64(s, offs, 4); + if (bcd[i] > 9) { + /* + * If the field value is greater than 9, the results are + * undefined. We could use a fixed value like 0 or 9, but + * an and with 9 seems to better match the hardware behavior. + */ + bcd[i] &= 9; + } + } + + /* Create a decNumber with the BCD values and convert to decimal32 */ + decNumberSetBCD(&a, bcd, 6); + decimal32FromNumber((decimal32 *)&dec32, &a, &context); + + /* Extract the two declets from the decimal32 value */ + res |= dec32 & 0xfffff; + } + + return res; +} diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index cb752b184a..674c05a2ce 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -23,17 +23,19 @@ #include "exec/exec-all.h" #include "internal.h" #include "helper_regs.h" +#include "hw/ppc/ppc.h" #include "trace.h" #ifdef CONFIG_TCG +#include "sysemu/tcg.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #endif /*****************************************************************************/ /* Exception processing */ -#if !defined(CONFIG_USER_ONLY) +#ifndef CONFIG_USER_ONLY static const char *powerpc_excp_name(int excp) { @@ -132,6 +134,26 @@ static void dump_hcall(CPUPPCState *env) env->nip); } +#ifdef CONFIG_TCG +/* Return true iff byteswap is needed to load instruction */ +static inline bool insn_need_byteswap(CPUArchState *env) +{ + /* SYSTEM builds TARGET_BIG_ENDIAN. Need to swap when MSR[LE] is set */ + return !!(env->msr & ((target_ulong)1 << MSR_LE)); +} + +static uint32_t ppc_ldl_code(CPUArchState *env, abi_ptr addr) +{ + uint32_t insn = cpu_ldl_code(env, addr); + + if (insn_need_byteswap(env)) { + insn = bswap32(insn); + } + + return insn; +} +#endif + static void ppc_excp_debug_sw_tlb(CPUPPCState *env, int excp) { const char *es; @@ -164,9 +186,8 @@ static void ppc_excp_debug_sw_tlb(CPUPPCState *env, int excp) env->error_code); } -#if defined(TARGET_PPC64) -static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp, - target_ulong *msr) +#ifdef TARGET_PPC64 +static int powerpc_reset_wakeup(CPUPPCState *env, int excp, target_ulong *msr) { /* We no longer are in a PM state */ env->resume_as_sreset = false; @@ -201,8 +222,8 @@ static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp, *msr |= SRR1_WAKEHVI; break; default: - cpu_abort(cs, "Unsupported exception %d in Power Save mode\n", - excp); + cpu_abort(env_cpu(env), + "Unsupported exception %d in Power Save mode\n", excp); } return POWERPC_EXCP_RESET; } @@ -359,7 +380,7 @@ static void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp, target_ulong msr, } } } -#endif +#endif /* TARGET_PPC64 */ static void powerpc_reset_excp_state(PowerPCCPU *cpu) { @@ -382,13 +403,13 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector, * 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. + * 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->nip = vector; env->msr = msr; hreg_compute_hflags(env); + ppc_maybe_interrupt(env); powerpc_reset_excp_state(cpu); @@ -402,40 +423,47 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector, env->reserve_addr = -1; } +static void powerpc_mcheck_checkstop(CPUPPCState *env) +{ + CPUState *cs = env_cpu(env); + + if (FIELD_EX64(env->msr, MSR, ME)) { + return; + } + + /* Machine check exception is not enabled. Enter checkstop state. */ + fprintf(stderr, "Machine check while not allowed. " + "Entering checkstop state\n"); + if (qemu_log_separate()) { + qemu_log("Machine check while not allowed. " + "Entering checkstop state\n"); + } + cs->halted = 1; + cpu_interrupt_exittb(cs); +} + static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) { - CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; - int srr0, srr1; + int srr0 = SPR_SRR0, srr1 = SPR_SRR1; /* new srr1 value excluding must-be-zero bits */ msr = env->msr & ~0x783f0000ULL; - /* - * new interrupt handler msr preserves existing ME unless - * explicitly overriden. - */ + /* new interrupt handler msr preserves ME unless explicitly overridden */ new_msr = env->msr & (((target_ulong)1 << MSR_ME)); - /* target registers */ - srr0 = SPR_SRR0; - srr1 = SPR_SRR1; - - /* - * Hypervisor emulation assistance interrupt only exists on server - * arch 2.05 server or later. - */ + /* HV emu assistance interrupt only exists on server arch 2.05 or later */ if (excp == POWERPC_EXCP_HV_EMU) { excp = POWERPC_EXCP_PROGRAM; } vector = env->excp_vectors[excp]; if (vector == (target_ulong)-1ULL) { - cpu_abort(cs, "Raised an exception without defined vector %d\n", - excp); + cpu_abort(env_cpu(env), + "Raised an exception without defined vector %d\n", excp); } - vector |= env->excp_prefix; switch (excp) { @@ -444,24 +472,9 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) srr1 = SPR_40x_SRR3; break; case POWERPC_EXCP_MCHECK: /* Machine check exception */ - if (!FIELD_EX64(env->msr, MSR, ME)) { - /* - * Machine check exception is not enabled. Enter - * checkstop state. - */ - fprintf(stderr, "Machine check while not allowed. " - "Entering checkstop state\n"); - if (qemu_log_separate()) { - qemu_log("Machine check while not allowed. " - "Entering checkstop state\n"); - } - cs->halted = 1; - cpu_interrupt_exittb(cs); - } - + powerpc_mcheck_checkstop(env); /* machine check exceptions don't have ME set */ new_msr &= ~((target_ulong)1 << MSR_ME); - srr0 = SPR_40x_SRR2; srr1 = SPR_40x_SRR3; break; @@ -496,7 +509,7 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) env->spr[SPR_40x_ESR] = ESR_PTR; break; default: - cpu_abort(cs, "Invalid program exception %d. Aborting\n", + cpu_abort(env_cpu(env), "Invalid program exception %d. Aborting\n", env->error_code); break; } @@ -523,76 +536,50 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) trace_ppc_excp_print("PIT"); break; case POWERPC_EXCP_DEBUG: /* Debug interrupt */ - cpu_abort(cs, "%s exception not implemented\n", + cpu_abort(env_cpu(env), "%s exception not implemented\n", powerpc_excp_name(excp)); break; default: - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + cpu_abort(env_cpu(env), "Invalid PowerPC exception %d. Aborting\n", + excp); break; } - /* Save PC */ env->spr[srr0] = env->nip; - - /* Save MSR */ env->spr[srr1] = msr; - powerpc_set_excp_state(cpu, vector, new_msr); } static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) { - CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; /* new srr1 value excluding must-be-zero bits */ msr = env->msr & ~0x783f0000ULL; - /* - * new interrupt handler msr preserves existing ME unless - * explicitly overriden - */ + /* new interrupt handler msr preserves ME unless explicitly overridden */ new_msr = env->msr & ((target_ulong)1 << MSR_ME); - /* - * Hypervisor emulation assistance interrupt only exists on server - * arch 2.05 server or later. - */ + /* HV emu assistance interrupt only exists on server arch 2.05 or later */ if (excp == POWERPC_EXCP_HV_EMU) { excp = POWERPC_EXCP_PROGRAM; } vector = env->excp_vectors[excp]; if (vector == (target_ulong)-1ULL) { - cpu_abort(cs, "Raised an exception without defined vector %d\n", - excp); + cpu_abort(env_cpu(env), + "Raised an exception without defined vector %d\n", excp); } - vector |= env->excp_prefix; switch (excp) { case POWERPC_EXCP_CRITICAL: /* Critical input */ break; case POWERPC_EXCP_MCHECK: /* Machine check exception */ - if (!FIELD_EX64(env->msr, MSR, ME)) { - /* - * Machine check exception is not enabled. Enter - * checkstop state. - */ - fprintf(stderr, "Machine check while not allowed. " - "Entering checkstop state\n"); - if (qemu_log_separate()) { - qemu_log("Machine check while not allowed. " - "Entering checkstop state\n"); - } - cs->halted = 1; - cpu_interrupt_exittb(cs); - } - + powerpc_mcheck_checkstop(env); /* machine check exceptions don't have ME set */ new_msr &= ~((target_ulong)1 << MSR_ME); - break; case POWERPC_EXCP_DSI: /* Data storage exception */ trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); @@ -620,11 +607,9 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) powerpc_reset_excp_state(cpu); return; } - /* - * FP exceptions always have NIP pointing to the faulting - * instruction, so always use store_next and claim we are - * precise in the MSR. + * NIP always points to the faulting instruction for FP exceptions, + * so always use store_next and claim we are precise in the MSR. */ msr |= 0x00100000; break; @@ -640,7 +625,7 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) break; default: /* Should never occur */ - cpu_abort(cs, "Invalid program exception %d. Aborting\n", + cpu_abort(env_cpu(env), "Invalid program exception %d. Aborting\n", env->error_code); break; } @@ -662,8 +647,9 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) break; case POWERPC_EXCP_RESET: /* System reset exception */ if (FIELD_EX64(env->msr, MSR, POW)) { - cpu_abort(cs, "Trying to deliver power-saving system reset " - "exception %d with no HV support\n", excp); + cpu_abort(env_cpu(env), + "Trying to deliver power-saving system reset exception " + "%d with no HV support\n", excp); } break; case POWERPC_EXCP_TRACE: /* Trace exception */ @@ -690,82 +676,51 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_SMI: /* System management interrupt */ case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ - cpu_abort(cs, "%s exception not implemented\n", + cpu_abort(env_cpu(env), "%s exception not implemented\n", powerpc_excp_name(excp)); break; default: - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + cpu_abort(env_cpu(env), "Invalid PowerPC exception %d. Aborting\n", + excp); break; } - /* - * Sort out endianness of interrupt, this differs depending on the - * CPU, the HV mode, etc... - */ if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) { new_msr |= (target_ulong)1 << MSR_LE; } - - /* Save PC */ env->spr[SPR_SRR0] = env->nip; - - /* Save MSR */ env->spr[SPR_SRR1] = msr; - powerpc_set_excp_state(cpu, vector, new_msr); } static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) { - CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; /* new srr1 value excluding must-be-zero bits */ msr = env->msr & ~0x783f0000ULL; - /* - * new interrupt handler msr preserves existing ME unless - * explicitly overriden - */ + /* new interrupt handler msr preserves ME unless explicitly overridden */ new_msr = env->msr & ((target_ulong)1 << MSR_ME); - /* - * Hypervisor emulation assistance interrupt only exists on server - * arch 2.05 server or later. - */ + /* HV emu assistance interrupt only exists on server arch 2.05 or later */ if (excp == POWERPC_EXCP_HV_EMU) { excp = POWERPC_EXCP_PROGRAM; } vector = env->excp_vectors[excp]; if (vector == (target_ulong)-1ULL) { - cpu_abort(cs, "Raised an exception without defined vector %d\n", - excp); + cpu_abort(env_cpu(env), + "Raised an exception without defined vector %d\n", excp); } - vector |= env->excp_prefix; switch (excp) { case POWERPC_EXCP_MCHECK: /* Machine check exception */ - if (!FIELD_EX64(env->msr, MSR, ME)) { - /* - * Machine check exception is not enabled. Enter - * checkstop state. - */ - fprintf(stderr, "Machine check while not allowed. " - "Entering checkstop state\n"); - if (qemu_log_separate()) { - qemu_log("Machine check while not allowed. " - "Entering checkstop state\n"); - } - cs->halted = 1; - cpu_interrupt_exittb(cs); - } - + powerpc_mcheck_checkstop(env); /* machine check exceptions don't have ME set */ new_msr &= ~((target_ulong)1 << MSR_ME); - break; case POWERPC_EXCP_DSI: /* Data storage exception */ trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); @@ -793,11 +748,9 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) powerpc_reset_excp_state(cpu); return; } - /* - * FP exceptions always have NIP pointing to the faulting - * instruction, so always use store_next and claim we are - * precise in the MSR. + * NIP always points to the faulting instruction for FP exceptions, + * so always use store_next and claim we are precise in the MSR. */ msr |= 0x00100000; break; @@ -813,7 +766,7 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) break; default: /* Should never occur */ - cpu_abort(cs, "Invalid program exception %d. Aborting\n", + cpu_abort(env_cpu(env), "Invalid program exception %d. Aborting\n", env->error_code); break; } @@ -844,6 +797,7 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); vhc->hypercall(cpu->vhyp, cpu); + powerpc_reset_excp_state(cpu); return; } @@ -854,8 +808,9 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) break; case POWERPC_EXCP_RESET: /* System reset exception */ if (FIELD_EX64(env->msr, MSR, POW)) { - cpu_abort(cs, "Trying to deliver power-saving system reset " - "exception %d with no HV support\n", excp); + cpu_abort(env_cpu(env), + "Trying to deliver power-saving system reset exception " + "%d with no HV support\n", excp); } break; case POWERPC_EXCP_TRACE: /* Trace exception */ @@ -864,93 +819,60 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ ppc_excp_debug_sw_tlb(env, excp); - msr |= env->crf[0] << 28; msr |= env->error_code; /* key, D/I, S/L bits */ /* Set way using a LRU mechanism */ msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; - break; case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ case POWERPC_EXCP_SMI: /* System management interrupt */ case POWERPC_EXCP_THERM: /* Thermal interrupt */ case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ - cpu_abort(cs, "%s exception not implemented\n", + cpu_abort(env_cpu(env), "%s exception not implemented\n", powerpc_excp_name(excp)); break; default: - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + cpu_abort(env_cpu(env), "Invalid PowerPC exception %d. Aborting\n", + excp); break; } - /* - * Sort out endianness of interrupt, this differs depending on the - * CPU, the HV mode, etc... - */ if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) { new_msr |= (target_ulong)1 << MSR_LE; } - - /* Save PC */ env->spr[SPR_SRR0] = env->nip; - - /* Save MSR */ env->spr[SPR_SRR1] = msr; - powerpc_set_excp_state(cpu, vector, new_msr); } static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) { - CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; /* new srr1 value excluding must-be-zero bits */ msr = env->msr & ~0x783f0000ULL; - /* - * new interrupt handler msr preserves existing ME unless - * explicitly overriden - */ + /* new interrupt handler msr preserves ME unless explicitly overridden */ new_msr = env->msr & ((target_ulong)1 << MSR_ME); - /* - * Hypervisor emulation assistance interrupt only exists on server - * arch 2.05 server or later. - */ + /* HV emu assistance interrupt only exists on server arch 2.05 or later */ if (excp == POWERPC_EXCP_HV_EMU) { excp = POWERPC_EXCP_PROGRAM; } vector = env->excp_vectors[excp]; if (vector == (target_ulong)-1ULL) { - cpu_abort(cs, "Raised an exception without defined vector %d\n", - excp); + cpu_abort(env_cpu(env), + "Raised an exception without defined vector %d\n", excp); } - vector |= env->excp_prefix; switch (excp) { case POWERPC_EXCP_MCHECK: /* Machine check exception */ - if (!FIELD_EX64(env->msr, MSR, ME)) { - /* - * Machine check exception is not enabled. Enter - * checkstop state. - */ - fprintf(stderr, "Machine check while not allowed. " - "Entering checkstop state\n"); - if (qemu_log_separate()) { - qemu_log("Machine check while not allowed. " - "Entering checkstop state\n"); - } - cs->halted = 1; - cpu_interrupt_exittb(cs); - } - + powerpc_mcheck_checkstop(env); /* machine check exceptions don't have ME set */ new_msr &= ~((target_ulong)1 << MSR_ME); - break; case POWERPC_EXCP_DSI: /* Data storage exception */ trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); @@ -978,11 +900,9 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) powerpc_reset_excp_state(cpu); return; } - /* - * FP exceptions always have NIP pointing to the faulting - * instruction, so always use store_next and claim we are - * precise in the MSR. + * NIP always points to the faulting instruction for FP exceptions, + * so always use store_next and claim we are precise in the MSR. */ msr |= 0x00100000; break; @@ -998,7 +918,7 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) break; default: /* Should never occur */ - cpu_abort(cs, "Invalid program exception %d. Aborting\n", + cpu_abort(env_cpu(env), "Invalid program exception %d. Aborting\n", env->error_code); break; } @@ -1007,7 +927,7 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) { int lev = env->error_code; - if ((lev == 1) && cpu->vhyp) { + if (lev == 1 && cpu->vhyp) { dump_hcall(env); } else { dump_syscall(env); @@ -1025,10 +945,11 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) * uses VOF and the 74xx CPUs, so although the 74xx don't have * HV mode, we need to keep hypercall support. */ - if ((lev == 1) && cpu->vhyp) { + if (lev == 1 && cpu->vhyp) { PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); vhc->hypercall(cpu->vhyp, cpu); + powerpc_reset_excp_state(cpu); return; } @@ -1039,7 +960,8 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) break; case POWERPC_EXCP_RESET: /* System reset exception */ if (FIELD_EX64(env->msr, MSR, POW)) { - cpu_abort(cs, "Trying to deliver power-saving system reset " + cpu_abort(env_cpu(env), + "Trying to deliver power-saving system reset " "exception %d with no HV support\n", excp); } break; @@ -1052,54 +974,39 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_THERM: /* Thermal interrupt */ case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ case POWERPC_EXCP_VPUA: /* Vector assist exception */ - cpu_abort(cs, "%s exception not implemented\n", + cpu_abort(env_cpu(env), "%s exception not implemented\n", powerpc_excp_name(excp)); break; default: - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + cpu_abort(env_cpu(env), "Invalid PowerPC exception %d. Aborting\n", + excp); break; } - /* - * Sort out endianness of interrupt, this differs depending on the - * CPU, the HV mode, etc... - */ if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) { new_msr |= (target_ulong)1 << MSR_LE; } - - /* Save PC */ env->spr[SPR_SRR0] = env->nip; - - /* Save MSR */ env->spr[SPR_SRR1] = msr; - powerpc_set_excp_state(cpu, vector, new_msr); } static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) { - CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; - int srr0, srr1; + int srr0 = SPR_SRR0, srr1 = SPR_SRR1; + /* + * Book E does not play games with certain bits of xSRR1 being MSR save + * bits and others being error status. xSRR1 is the old MSR, period. + */ msr = env->msr; - /* - * new interrupt handler msr preserves existing ME unless - * explicitly overriden - */ + /* new interrupt handler msr preserves ME unless explicitly overridden */ new_msr = env->msr & ((target_ulong)1 << MSR_ME); - /* target registers */ - srr0 = SPR_SRR0; - srr1 = SPR_SRR1; - - /* - * Hypervisor emulation assistance interrupt only exists on server - * arch 2.05 server or later. - */ + /* HV emu assistance interrupt only exists on server arch 2.05 or later */ if (excp == POWERPC_EXCP_HV_EMU) { excp = POWERPC_EXCP_PROGRAM; } @@ -1116,10 +1023,9 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) vector = env->excp_vectors[excp]; if (vector == (target_ulong)-1ULL) { - cpu_abort(cs, "Raised an exception without defined vector %d\n", - excp); + cpu_abort(env_cpu(env), + "Raised an exception without defined vector %d\n", excp); } - vector |= env->excp_prefix; switch (excp) { @@ -1128,21 +1034,7 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) srr1 = SPR_BOOKE_CSRR1; break; case POWERPC_EXCP_MCHECK: /* Machine check exception */ - if (!FIELD_EX64(env->msr, MSR, ME)) { - /* - * Machine check exception is not enabled. Enter - * checkstop state. - */ - fprintf(stderr, "Machine check while not allowed. " - "Entering checkstop state\n"); - if (qemu_log_separate()) { - qemu_log("Machine check while not allowed. " - "Entering checkstop state\n"); - } - cs->halted = 1; - cpu_interrupt_exittb(cs); - } - + powerpc_mcheck_checkstop(env); /* machine check exceptions don't have ME set */ new_msr &= ~((target_ulong)1 << MSR_ME); @@ -1162,6 +1054,7 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) break; case POWERPC_EXCP_EXTERNAL: /* External input */ if (env->mpic_proxy) { + CPUState *cs = env_cpu(env); /* IACK the IRQ on delivery */ env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); } @@ -1176,11 +1069,9 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) powerpc_reset_excp_state(cpu); return; } - /* - * FP exceptions always have NIP pointing to the faulting - * instruction, so always use store_next and claim we are - * precise in the MSR. + * NIP always points to the faulting instruction for FP exceptions, + * so always use store_next and claim we are precise in the MSR. */ msr |= 0x00100000; env->spr[SPR_BOOKE_ESR] = ESR_FP; @@ -1200,7 +1091,7 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) break; default: /* Should never occur */ - cpu_abort(cs, "Invalid program exception %d. Aborting\n", + cpu_abort(env_cpu(env), "Invalid program exception %d. Aborting\n", env->error_code); break; } @@ -1241,29 +1132,38 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) /* DBSR already modified by caller */ } else { - cpu_abort(cs, "Debug exception triggered on unsupported model\n"); + cpu_abort(env_cpu(env), + "Debug exception triggered on unsupported model\n"); } break; case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */ env->spr[SPR_BOOKE_ESR] = ESR_SPV; break; + case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ + break; + case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; case POWERPC_EXCP_RESET: /* System reset exception */ if (FIELD_EX64(env->msr, MSR, POW)) { - cpu_abort(cs, "Trying to deliver power-saving system reset " + cpu_abort(env_cpu(env), + "Trying to deliver power-saving system reset " "exception %d with no HV support\n", excp); } break; case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ - cpu_abort(cs, "%s exception not implemented\n", + cpu_abort(env_cpu(env), "%s exception not implemented\n", powerpc_excp_name(excp)); break; default: - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + cpu_abort(env_cpu(env), "Invalid PowerPC exception %d. Aborting\n", + excp); break; } -#if defined(TARGET_PPC64) +#ifdef TARGET_PPC64 if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ new_msr |= (target_ulong)1 << MSR_CM; @@ -1272,12 +1172,8 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) } #endif - /* Save PC */ env->spr[srr0] = env->nip; - - /* Save MSR */ env->spr[srr1] = msr; - powerpc_set_excp_state(cpu, vector, new_msr); } @@ -1309,7 +1205,7 @@ static bool books_vhyp_handles_hcall(PowerPCCPU *cpu) /* * When running a nested KVM HV guest under vhyp, HV exceptions are not * delivered to the guest (because there is no concept of HV support), but - * rather they are sent tothe vhyp to exit from the L2 back to the L1 and + * rather they are sent to the vhyp to exit from the L2 back to the L1 and * return from the H_ENTER_NESTED hypercall. */ static bool books_vhyp_handles_hv_excp(PowerPCCPU *cpu) @@ -1320,78 +1216,143 @@ static bool books_vhyp_handles_hv_excp(PowerPCCPU *cpu) return false; } +#ifdef CONFIG_TCG +static bool is_prefix_insn(CPUPPCState *env, uint32_t insn) +{ + if (!(env->insns_flags2 & PPC2_ISA310)) { + return false; + } + return ((insn & 0xfc000000) == 0x04000000); +} + +static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp) +{ + CPUPPCState *env = &cpu->env; + + if (!(env->insns_flags2 & PPC2_ISA310)) { + return false; + } + + if (!tcg_enabled()) { + /* + * This does not load instructions and set the prefix bit correctly + * for injected interrupts with KVM. That may have to be discovered + * and set by the KVM layer before injecting. + */ + return false; + } + + switch (excp) { + case POWERPC_EXCP_MCHECK: + if (!(env->error_code & PPC_BIT(42))) { + /* + * Fetch attempt caused a machine check, so attempting to fetch + * again would cause a recursive machine check. + */ + return false; + } + break; + case POWERPC_EXCP_HDSI: + /* HDSI PRTABLE_FAULT has the originating access type in error_code */ + if ((env->spr[SPR_HDSISR] & DSISR_PRTABLE_FAULT) && + (env->error_code == MMU_INST_FETCH)) { + /* + * Fetch failed due to partition scope translation, so prefix + * indication is not relevant (and attempting to load the + * instruction at NIP would cause recursive faults with the same + * translation). + */ + return false; + } + break; + + case POWERPC_EXCP_DSI: + case POWERPC_EXCP_DSEG: + case POWERPC_EXCP_ALIGN: + case POWERPC_EXCP_PROGRAM: + case POWERPC_EXCP_FPU: + case POWERPC_EXCP_TRACE: + case POWERPC_EXCP_HV_EMU: + case POWERPC_EXCP_VPU: + case POWERPC_EXCP_VSXU: + case POWERPC_EXCP_FU: + case POWERPC_EXCP_HV_FU: + break; + default: + return false; + } + + return is_prefix_insn(env, ppc_ldl_code(env, env->nip)); +} +#else +static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp) +{ + return false; +} +#endif + static void powerpc_excp_books(PowerPCCPU *cpu, int excp) { - CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; - int srr0, srr1, lev = -1; + int srr0 = SPR_SRR0, srr1 = SPR_SRR1, lev = -1; /* new srr1 value excluding must-be-zero bits */ msr = env->msr & ~0x783f0000ULL; /* - * new interrupt handler msr preserves existing HV and ME unless - * explicitly overriden + * new interrupt handler msr preserves HV and ME unless explicitly + * overridden */ new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); - /* target registers */ - srr0 = SPR_SRR0; - srr1 = SPR_SRR1; - /* * check for special resume at 0x100 from doze/nap/sleep/winkle on * P7/P8/P9 */ if (env->resume_as_sreset) { - excp = powerpc_reset_wakeup(cs, env, excp, &msr); + excp = powerpc_reset_wakeup(env, excp, &msr); } /* * We don't want to generate a Hypervisor Emulation Assistance - * Interrupt if we don't have HVB in msr_mask (PAPR mode). + * Interrupt if we don't have HVB in msr_mask (PAPR mode), + * unless running a nested-hv guest, in which case the L1 + * kernel wants the interrupt. */ - if (excp == POWERPC_EXCP_HV_EMU && !(env->msr_mask & MSR_HVB)) { + if (excp == POWERPC_EXCP_HV_EMU && !(env->msr_mask & MSR_HVB) && + !books_vhyp_handles_hv_excp(cpu)) { excp = POWERPC_EXCP_PROGRAM; } vector = env->excp_vectors[excp]; if (vector == (target_ulong)-1ULL) { - cpu_abort(cs, "Raised an exception without defined vector %d\n", - excp); + cpu_abort(env_cpu(env), + "Raised an exception without defined vector %d\n", excp); } - vector |= env->excp_prefix; + if (is_prefix_insn_excp(cpu, excp)) { + msr |= PPC_BIT(34); + } + switch (excp) { case POWERPC_EXCP_MCHECK: /* Machine check exception */ - if (!FIELD_EX64(env->msr, MSR, ME)) { - /* - * Machine check exception is not enabled. Enter - * checkstop state. - */ - fprintf(stderr, "Machine check while not allowed. " - "Entering checkstop state\n"); - if (qemu_log_separate()) { - qemu_log("Machine check while not allowed. " - "Entering checkstop state\n"); - } - cs->halted = 1; - cpu_interrupt_exittb(cs); - } + powerpc_mcheck_checkstop(env); if (env->msr_mask & MSR_HVB) { /* * ISA specifies HV, but can be delivered to guest with HV * clear (e.g., see FWNMI in PAPR). */ new_msr |= (target_ulong)MSR_HVB; + + /* HV machine check exceptions don't have ME set */ + new_msr &= ~((target_ulong)1 << MSR_ME); } - /* machine check exceptions don't have ME set */ - new_msr &= ~((target_ulong)1 << MSR_ME); - + msr |= env->error_code; break; + case POWERPC_EXCP_DSI: /* Data storage exception */ trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); break; @@ -1403,33 +1364,30 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) { bool lpes0; - /* - * LPES0 is only taken into consideration if we support HV - * mode for this CPU. - */ + /* LPES0 is only taken into consideration if we support HV mode */ if (!env->has_hv_mode) { break; } - lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); - if (!lpes0) { new_msr |= (target_ulong)MSR_HVB; new_msr |= env->msr & ((target_ulong)1 << MSR_RI); srr0 = SPR_HSRR0; srr1 = SPR_HSRR1; } - break; } case POWERPC_EXCP_ALIGN: /* Alignment exception */ - /* Get rS/rD and rA from faulting opcode */ - /* - * Note: the opcode fields will not be set properly for a - * direct store load/store, but nobody cares as nobody - * actually uses direct store segments. - */ - env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16; + /* Optional DSISR update was removed from ISA v3.0 */ + if (!(env->insns_flags2 & PPC2_ISA300)) { + /* Get rS/rD and rA from faulting opcode */ + /* + * Note: the opcode fields will not be set properly for a + * direct store load/store, but nobody cares as nobody + * actually uses direct store segments. + */ + env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16; + } break; case POWERPC_EXCP_PROGRAM: /* Program exception */ switch (env->error_code & ~0xF) { @@ -1439,11 +1397,9 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) powerpc_reset_excp_state(cpu); return; } - /* - * FP exceptions always have NIP pointing to the faulting - * instruction, so always use store_next and claim we are - * precise in the MSR. + * NIP always points to the faulting instruction for FP exceptions, + * so always use store_next and claim we are precise in the MSR. */ msr |= 0x00100000; break; @@ -1459,7 +1415,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) break; default: /* Should never occur */ - cpu_abort(cs, "Invalid program exception %d. Aborting\n", + cpu_abort(env_cpu(env), "Invalid program exception %d. Aborting\n", env->error_code); break; } @@ -1467,7 +1423,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_SYSCALL: /* System call exception */ lev = env->error_code; - if ((lev == 1) && cpu->vhyp) { + if (lev == 1 && cpu->vhyp) { dump_hcall(env); } else { dump_syscall(env); @@ -1480,12 +1436,17 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) env->nip += 4; /* "PAPR mode" built-in hypercall emulation */ - if ((lev == 1) && books_vhyp_handles_hcall(cpu)) { + if (lev == 1 && books_vhyp_handles_hcall(cpu)) { PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); vhc->hypercall(cpu->vhyp, cpu); + powerpc_reset_excp_state(cpu); return; } + if (env->insns_flags2 & PPC2_ISA310) { + /* ISAv3.1 puts LEV into SRR1 */ + msr |= lev << 20; + } if (lev == 1) { new_msr |= (target_ulong)MSR_HVB; } @@ -1520,14 +1481,19 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) new_msr |= (target_ulong)MSR_HVB; } else { if (FIELD_EX64(env->msr, MSR, POW)) { - cpu_abort(cs, "Trying to deliver power-saving system reset " + cpu_abort(env_cpu(env), + "Trying to deliver power-saving system reset " "exception %d with no HV support\n", excp); } } break; + case POWERPC_EXCP_TRACE: /* Trace exception */ + msr |= env->error_code; + /* fall through */ case POWERPC_EXCP_DSEG: /* Data segment exception */ case POWERPC_EXCP_ISEG: /* Instruction segment exception */ - case POWERPC_EXCP_TRACE: /* Trace exception */ + case POWERPC_EXCP_SDOOR: /* Doorbell interrupt */ + case POWERPC_EXCP_PERFM: /* Performance monitor interrupt */ break; case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ msr |= env->error_code; @@ -1535,13 +1501,28 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ - case POWERPC_EXCP_HV_EMU: case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ srr0 = SPR_HSRR0; srr1 = SPR_HSRR1; new_msr |= (target_ulong)MSR_HVB; new_msr |= env->msr & ((target_ulong)1 << MSR_RI); break; +#ifdef CONFIG_TCG + case POWERPC_EXCP_HV_EMU: { + uint32_t insn = ppc_ldl_code(env, env->nip); + env->spr[SPR_HEIR] = insn; + if (is_prefix_insn(env, insn)) { + uint32_t insn2 = ppc_ldl_code(env, env->nip + 4); + env->spr[SPR_HEIR] <<= 32; + env->spr[SPR_HEIR] |= insn2; + } + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + break; + } +#endif case POWERPC_EXCP_VPU: /* Vector unavailable exception */ case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ case POWERPC_EXCP_FU: /* Facility unavailable exception */ @@ -1570,34 +1551,25 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) */ return; case POWERPC_EXCP_THERM: /* Thermal interrupt */ - case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ case POWERPC_EXCP_VPUA: /* Vector assist exception */ case POWERPC_EXCP_MAINT: /* Maintenance exception */ - case POWERPC_EXCP_SDOOR: /* Doorbell interrupt */ case POWERPC_EXCP_HV_MAINT: /* Hypervisor Maintenance exception */ - cpu_abort(cs, "%s exception not implemented\n", + cpu_abort(env_cpu(env), "%s exception not implemented\n", powerpc_excp_name(excp)); break; default: - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + cpu_abort(env_cpu(env), "Invalid PowerPC exception %d. Aborting\n", + excp); break; } - /* - * Sort out endianness of interrupt, this differs depending on the - * CPU, the HV mode, etc... - */ if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) { new_msr |= (target_ulong)1 << MSR_LE; } - new_msr |= (target_ulong)1 << MSR_SF; if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { - /* Save PC */ env->spr[srr0] = env->nip; - - /* Save MSR */ env->spr[srr1] = msr; } @@ -1606,19 +1578,15 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); /* Deliver interrupt to L1 by returning from the H_ENTER_NESTED call */ vhc->deliver_hv_excp(cpu, excp); - powerpc_reset_excp_state(cpu); - } else { /* Sanity check */ if (!(env->msr_mask & MSR_HVB) && srr0 == SPR_HSRR0) { - cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " - "no HV support\n", excp); + cpu_abort(env_cpu(env), "Trying to deliver HV exception (HSRR) %d " + "with no HV support\n", excp); } - /* This can update new_msr and vector if AIL applies */ ppc_excp_apply_ail(cpu, excp, msr, &new_msr, &vector); - powerpc_set_excp_state(cpu, vector, new_msr); } } @@ -1627,20 +1595,21 @@ static inline void powerpc_excp_books(PowerPCCPU *cpu, int excp) { g_assert_not_reached(); } -#endif +#endif /* TARGET_PPC64 */ static void powerpc_excp(PowerPCCPU *cpu, int excp) { - CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + cpu_abort(env_cpu(env), "Invalid PowerPC exception %d. Aborting\n", + excp); } qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), excp, env->error_code); + env->excp_stats[excp]++; switch (env->excp_model) { case POWERPC_EXCP_40x: @@ -1677,29 +1646,368 @@ void ppc_cpu_do_interrupt(CPUState *cs) powerpc_excp(cpu, cs->exception_index); } -static void ppc_hw_interrupt(CPUPPCState *env) +#ifdef TARGET_PPC64 +#define P7_UNUSED_INTERRUPTS \ + (PPC_INTERRUPT_RESET | PPC_INTERRUPT_HVIRT | PPC_INTERRUPT_CEXT | \ + PPC_INTERRUPT_WDT | PPC_INTERRUPT_CDOORBELL | PPC_INTERRUPT_FIT | \ + PPC_INTERRUPT_PIT | PPC_INTERRUPT_DOORBELL | PPC_INTERRUPT_HDOORBELL | \ + PPC_INTERRUPT_THERM | PPC_INTERRUPT_EBB) + +static int p7_interrupt_powersave(CPUPPCState *env) { - PowerPCCPU *cpu = env_archcpu(env); + if ((env->pending_interrupts & PPC_INTERRUPT_EXT) && + (env->spr[SPR_LPCR] & LPCR_P7_PECE0)) { + return PPC_INTERRUPT_EXT; + } + if ((env->pending_interrupts & PPC_INTERRUPT_DECR) && + (env->spr[SPR_LPCR] & LPCR_P7_PECE1)) { + return PPC_INTERRUPT_DECR; + } + if ((env->pending_interrupts & PPC_INTERRUPT_MCK) && + (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) { + return PPC_INTERRUPT_MCK; + } + if ((env->pending_interrupts & PPC_INTERRUPT_HMI) && + (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) { + return PPC_INTERRUPT_HMI; + } + if (env->pending_interrupts & PPC_INTERRUPT_RESET) { + return PPC_INTERRUPT_RESET; + } + return 0; +} + +static int p7_next_unmasked_interrupt(CPUPPCState *env) +{ + CPUState *cs = env_cpu(env); + + /* Ignore MSR[EE] when coming out of some power management states */ + bool msr_ee = FIELD_EX64(env->msr, MSR, EE) || env->resume_as_sreset; + + assert((env->pending_interrupts & P7_UNUSED_INTERRUPTS) == 0); + + if (cs->halted) { + /* LPCR[PECE] controls which interrupts can exit power-saving mode */ + return p7_interrupt_powersave(env); + } + + /* Machine check exception */ + if (env->pending_interrupts & PPC_INTERRUPT_MCK) { + return PPC_INTERRUPT_MCK; + } + + /* Hypervisor decrementer exception */ + if (env->pending_interrupts & PPC_INTERRUPT_HDECR) { + /* LPCR will be clear when not supported so this will work */ + bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE); + if ((msr_ee || !FIELD_EX64_HV(env->msr)) && hdice) { + /* HDEC clears on delivery */ + return PPC_INTERRUPT_HDECR; + } + } + + /* External interrupt can ignore MSR:EE under some circumstances */ + if (env->pending_interrupts & PPC_INTERRUPT_EXT) { + bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); + bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC); + /* HEIC blocks delivery to the hypervisor */ + if ((msr_ee && !(heic && FIELD_EX64_HV(env->msr) && + !FIELD_EX64(env->msr, MSR, PR))) || + (env->has_hv_mode && !FIELD_EX64_HV(env->msr) && !lpes0)) { + return PPC_INTERRUPT_EXT; + } + } + if (msr_ee != 0) { + /* Decrementer exception */ + if (env->pending_interrupts & PPC_INTERRUPT_DECR) { + return PPC_INTERRUPT_DECR; + } + if (env->pending_interrupts & PPC_INTERRUPT_PERFM) { + return PPC_INTERRUPT_PERFM; + } + } + + return 0; +} + +#define P8_UNUSED_INTERRUPTS \ + (PPC_INTERRUPT_RESET | PPC_INTERRUPT_DEBUG | PPC_INTERRUPT_HVIRT | \ + PPC_INTERRUPT_CEXT | PPC_INTERRUPT_WDT | PPC_INTERRUPT_CDOORBELL | \ + PPC_INTERRUPT_FIT | PPC_INTERRUPT_PIT | PPC_INTERRUPT_THERM) + +static int p8_interrupt_powersave(CPUPPCState *env) +{ + if ((env->pending_interrupts & PPC_INTERRUPT_EXT) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE2)) { + return PPC_INTERRUPT_EXT; + } + if ((env->pending_interrupts & PPC_INTERRUPT_DECR) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE3)) { + return PPC_INTERRUPT_DECR; + } + if ((env->pending_interrupts & PPC_INTERRUPT_MCK) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) { + return PPC_INTERRUPT_MCK; + } + if ((env->pending_interrupts & PPC_INTERRUPT_HMI) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) { + return PPC_INTERRUPT_HMI; + } + if ((env->pending_interrupts & PPC_INTERRUPT_DOORBELL) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE0)) { + return PPC_INTERRUPT_DOORBELL; + } + if ((env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) && + (env->spr[SPR_LPCR] & LPCR_P8_PECE1)) { + return PPC_INTERRUPT_HDOORBELL; + } + if (env->pending_interrupts & PPC_INTERRUPT_RESET) { + return PPC_INTERRUPT_RESET; + } + return 0; +} + +static int p8_next_unmasked_interrupt(CPUPPCState *env) +{ + CPUState *cs = env_cpu(env); + + /* Ignore MSR[EE] when coming out of some power management states */ + bool msr_ee = FIELD_EX64(env->msr, MSR, EE) || env->resume_as_sreset; + + assert((env->pending_interrupts & P8_UNUSED_INTERRUPTS) == 0); + + if (cs->halted) { + /* LPCR[PECE] controls which interrupts can exit power-saving mode */ + return p8_interrupt_powersave(env); + } + + /* Machine check exception */ + if (env->pending_interrupts & PPC_INTERRUPT_MCK) { + return PPC_INTERRUPT_MCK; + } + + /* Hypervisor decrementer exception */ + if (env->pending_interrupts & PPC_INTERRUPT_HDECR) { + /* LPCR will be clear when not supported so this will work */ + bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE); + if ((msr_ee || !FIELD_EX64_HV(env->msr)) && hdice) { + /* HDEC clears on delivery */ + return PPC_INTERRUPT_HDECR; + } + } + + /* External interrupt can ignore MSR:EE under some circumstances */ + if (env->pending_interrupts & PPC_INTERRUPT_EXT) { + bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); + bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC); + /* HEIC blocks delivery to the hypervisor */ + if ((msr_ee && !(heic && FIELD_EX64_HV(env->msr) && + !FIELD_EX64(env->msr, MSR, PR))) || + (env->has_hv_mode && !FIELD_EX64_HV(env->msr) && !lpes0)) { + return PPC_INTERRUPT_EXT; + } + } + if (msr_ee != 0) { + /* Decrementer exception */ + if (env->pending_interrupts & PPC_INTERRUPT_DECR) { + return PPC_INTERRUPT_DECR; + } + if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) { + return PPC_INTERRUPT_DOORBELL; + } + if (env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) { + return PPC_INTERRUPT_HDOORBELL; + } + if (env->pending_interrupts & PPC_INTERRUPT_PERFM) { + return PPC_INTERRUPT_PERFM; + } + /* EBB exception */ + if (env->pending_interrupts & PPC_INTERRUPT_EBB) { + /* + * EBB exception must be taken in problem state and + * with BESCR_GE set. + */ + if (FIELD_EX64(env->msr, MSR, PR) && + (env->spr[SPR_BESCR] & BESCR_GE)) { + return PPC_INTERRUPT_EBB; + } + } + } + + return 0; +} + +#define P9_UNUSED_INTERRUPTS \ + (PPC_INTERRUPT_RESET | PPC_INTERRUPT_DEBUG | PPC_INTERRUPT_CEXT | \ + PPC_INTERRUPT_WDT | PPC_INTERRUPT_CDOORBELL | PPC_INTERRUPT_FIT | \ + PPC_INTERRUPT_PIT | PPC_INTERRUPT_THERM) + +static int p9_interrupt_powersave(CPUPPCState *env) +{ + /* External Exception */ + if ((env->pending_interrupts & PPC_INTERRUPT_EXT) && + (env->spr[SPR_LPCR] & LPCR_EEE)) { + bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC); + if (!heic || !FIELD_EX64_HV(env->msr) || + FIELD_EX64(env->msr, MSR, PR)) { + return PPC_INTERRUPT_EXT; + } + } + /* Decrementer Exception */ + if ((env->pending_interrupts & PPC_INTERRUPT_DECR) && + (env->spr[SPR_LPCR] & LPCR_DEE)) { + return PPC_INTERRUPT_DECR; + } + /* Machine Check or Hypervisor Maintenance Exception */ + if (env->spr[SPR_LPCR] & LPCR_OEE) { + if (env->pending_interrupts & PPC_INTERRUPT_MCK) { + return PPC_INTERRUPT_MCK; + } + if (env->pending_interrupts & PPC_INTERRUPT_HMI) { + return PPC_INTERRUPT_HMI; + } + } + /* Privileged Doorbell Exception */ + if ((env->pending_interrupts & PPC_INTERRUPT_DOORBELL) && + (env->spr[SPR_LPCR] & LPCR_PDEE)) { + return PPC_INTERRUPT_DOORBELL; + } + /* Hypervisor Doorbell Exception */ + if ((env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) && + (env->spr[SPR_LPCR] & LPCR_HDEE)) { + return PPC_INTERRUPT_HDOORBELL; + } + /* Hypervisor virtualization exception */ + if ((env->pending_interrupts & PPC_INTERRUPT_HVIRT) && + (env->spr[SPR_LPCR] & LPCR_HVEE)) { + return PPC_INTERRUPT_HVIRT; + } + if (env->pending_interrupts & PPC_INTERRUPT_RESET) { + return PPC_INTERRUPT_RESET; + } + return 0; +} + +static int p9_next_unmasked_interrupt(CPUPPCState *env) +{ + CPUState *cs = env_cpu(env); + + /* Ignore MSR[EE] when coming out of some power management states */ + bool msr_ee = FIELD_EX64(env->msr, MSR, EE) || env->resume_as_sreset; + + assert((env->pending_interrupts & P9_UNUSED_INTERRUPTS) == 0); + + if (cs->halted) { + if (env->spr[SPR_PSSCR] & PSSCR_EC) { + /* + * When PSSCR[EC] is set, LPCR[PECE] controls which interrupts can + * wakeup the processor + */ + return p9_interrupt_powersave(env); + } else { + /* + * When it's clear, any system-caused exception exits power-saving + * mode, even the ones that gate on MSR[EE]. + */ + msr_ee = true; + } + } + + /* Machine check exception */ + if (env->pending_interrupts & PPC_INTERRUPT_MCK) { + return PPC_INTERRUPT_MCK; + } + + /* Hypervisor decrementer exception */ + if (env->pending_interrupts & PPC_INTERRUPT_HDECR) { + /* LPCR will be clear when not supported so this will work */ + bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE); + if ((msr_ee || !FIELD_EX64_HV(env->msr)) && hdice) { + /* HDEC clears on delivery */ + return PPC_INTERRUPT_HDECR; + } + } + + /* Hypervisor virtualization interrupt */ + if (env->pending_interrupts & PPC_INTERRUPT_HVIRT) { + /* LPCR will be clear when not supported so this will work */ + bool hvice = !!(env->spr[SPR_LPCR] & LPCR_HVICE); + if ((msr_ee || !FIELD_EX64_HV(env->msr)) && hvice) { + return PPC_INTERRUPT_HVIRT; + } + } + + /* External interrupt can ignore MSR:EE under some circumstances */ + if (env->pending_interrupts & PPC_INTERRUPT_EXT) { + bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); + bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC); + /* HEIC blocks delivery to the hypervisor */ + if ((msr_ee && !(heic && FIELD_EX64_HV(env->msr) && + !FIELD_EX64(env->msr, MSR, PR))) || + (env->has_hv_mode && !FIELD_EX64_HV(env->msr) && !lpes0)) { + return PPC_INTERRUPT_EXT; + } + } + if (msr_ee != 0) { + /* Decrementer exception */ + if (env->pending_interrupts & PPC_INTERRUPT_DECR) { + return PPC_INTERRUPT_DECR; + } + if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) { + return PPC_INTERRUPT_DOORBELL; + } + if (env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) { + return PPC_INTERRUPT_HDOORBELL; + } + if (env->pending_interrupts & PPC_INTERRUPT_PERFM) { + return PPC_INTERRUPT_PERFM; + } + /* EBB exception */ + if (env->pending_interrupts & PPC_INTERRUPT_EBB) { + /* + * EBB exception must be taken in problem state and + * with BESCR_GE set. + */ + if (FIELD_EX64(env->msr, MSR, PR) && + (env->spr[SPR_BESCR] & BESCR_GE)) { + return PPC_INTERRUPT_EBB; + } + } + } + + return 0; +} +#endif /* TARGET_PPC64 */ + +static int ppc_next_unmasked_interrupt(CPUPPCState *env) +{ +#ifdef TARGET_PPC64 + switch (env->excp_model) { + case POWERPC_EXCP_POWER7: + return p7_next_unmasked_interrupt(env); + case POWERPC_EXCP_POWER8: + return p8_next_unmasked_interrupt(env); + case POWERPC_EXCP_POWER9: + case POWERPC_EXCP_POWER10: + return p9_next_unmasked_interrupt(env); + default: + break; + } +#endif bool async_deliver; /* External reset */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET); - powerpc_excp(cpu, POWERPC_EXCP_RESET); - return; + if (env->pending_interrupts & PPC_INTERRUPT_RESET) { + return PPC_INTERRUPT_RESET; } /* Machine check exception */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK); - powerpc_excp(cpu, POWERPC_EXCP_MCHECK); - return; + if (env->pending_interrupts & PPC_INTERRUPT_MCK) { + return PPC_INTERRUPT_MCK; } #if 0 /* TODO */ /* External debug exception */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG); - powerpc_excp(cpu, POWERPC_EXCP_DEBUG); - return; + if (env->pending_interrupts & PPC_INTERRUPT_DEBUG) { + return PPC_INTERRUPT_DEBUG; } #endif @@ -1712,129 +2020,148 @@ static void ppc_hw_interrupt(CPUPPCState *env) async_deliver = FIELD_EX64(env->msr, MSR, EE) || env->resume_as_sreset; /* Hypervisor decrementer exception */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { + if (env->pending_interrupts & PPC_INTERRUPT_HDECR) { /* LPCR will be clear when not supported so this will work */ bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE); if ((async_deliver || !FIELD_EX64_HV(env->msr)) && hdice) { /* HDEC clears on delivery */ - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); - powerpc_excp(cpu, POWERPC_EXCP_HDECR); - return; + return PPC_INTERRUPT_HDECR; } } /* Hypervisor virtualization interrupt */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_HVIRT)) { + if (env->pending_interrupts & PPC_INTERRUPT_HVIRT) { /* LPCR will be clear when not supported so this will work */ bool hvice = !!(env->spr[SPR_LPCR] & LPCR_HVICE); if ((async_deliver || !FIELD_EX64_HV(env->msr)) && hvice) { - powerpc_excp(cpu, POWERPC_EXCP_HVIRT); - return; + return PPC_INTERRUPT_HVIRT; } } /* External interrupt can ignore MSR:EE under some circumstances */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { + if (env->pending_interrupts & PPC_INTERRUPT_EXT) { bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC); /* HEIC blocks delivery to the hypervisor */ if ((async_deliver && !(heic && FIELD_EX64_HV(env->msr) && !FIELD_EX64(env->msr, MSR, PR))) || (env->has_hv_mode && !FIELD_EX64_HV(env->msr) && !lpes0)) { - if (books_vhyp_promotes_external_to_hvirt(cpu)) { - powerpc_excp(cpu, POWERPC_EXCP_HVIRT); - } else { - powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL); - } - return; + return PPC_INTERRUPT_EXT; } } if (FIELD_EX64(env->msr, MSR, CE)) { /* External critical interrupt */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) { - powerpc_excp(cpu, POWERPC_EXCP_CRITICAL); - return; + if (env->pending_interrupts & PPC_INTERRUPT_CEXT) { + return PPC_INTERRUPT_CEXT; } } if (async_deliver != 0) { /* Watchdog timer on embedded PowerPC */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT); - powerpc_excp(cpu, POWERPC_EXCP_WDT); - return; + if (env->pending_interrupts & PPC_INTERRUPT_WDT) { + return PPC_INTERRUPT_WDT; } - if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL); - powerpc_excp(cpu, POWERPC_EXCP_DOORCI); - return; + if (env->pending_interrupts & PPC_INTERRUPT_CDOORBELL) { + return PPC_INTERRUPT_CDOORBELL; } /* Fixed interval timer on embedded PowerPC */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT); - powerpc_excp(cpu, POWERPC_EXCP_FIT); - return; + if (env->pending_interrupts & PPC_INTERRUPT_FIT) { + return PPC_INTERRUPT_FIT; } /* Programmable interval timer on embedded PowerPC */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT); - powerpc_excp(cpu, POWERPC_EXCP_PIT); - return; + if (env->pending_interrupts & PPC_INTERRUPT_PIT) { + return PPC_INTERRUPT_PIT; } /* Decrementer exception */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_DECR)) { - if (ppc_decr_clear_on_delivery(env)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR); - } - powerpc_excp(cpu, POWERPC_EXCP_DECR); - return; + if (env->pending_interrupts & PPC_INTERRUPT_DECR) { + return PPC_INTERRUPT_DECR; } - if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); - if (is_book3s_arch2x(env)) { - powerpc_excp(cpu, POWERPC_EXCP_SDOOR); - } else { - powerpc_excp(cpu, POWERPC_EXCP_DOORI); - } - return; + if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) { + return PPC_INTERRUPT_DOORBELL; } - if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDOORBELL)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDOORBELL); - powerpc_excp(cpu, POWERPC_EXCP_SDOOR_HV); - return; + if (env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) { + return PPC_INTERRUPT_HDOORBELL; } - if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM); - powerpc_excp(cpu, POWERPC_EXCP_PERFM); - return; + if (env->pending_interrupts & PPC_INTERRUPT_PERFM) { + return PPC_INTERRUPT_PERFM; } /* Thermal interrupt */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM); - powerpc_excp(cpu, POWERPC_EXCP_THERM); - return; + if (env->pending_interrupts & PPC_INTERRUPT_THERM) { + return PPC_INTERRUPT_THERM; } /* EBB exception */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_EBB)) { + if (env->pending_interrupts & PPC_INTERRUPT_EBB) { /* * EBB exception must be taken in problem state and * with BESCR_GE set. */ if (FIELD_EX64(env->msr, MSR, PR) && (env->spr[SPR_BESCR] & BESCR_GE)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EBB); - - if (env->spr[SPR_BESCR] & BESCR_PMEO) { - powerpc_excp(cpu, POWERPC_EXCP_PERFM_EBB); - } else if (env->spr[SPR_BESCR] & BESCR_EEO) { - powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL_EBB); - } - - return; + return PPC_INTERRUPT_EBB; } } } - if (env->resume_as_sreset) { + return 0; +} + +/* + * Sets CPU_INTERRUPT_HARD if there is at least one unmasked interrupt to be + * delivered and clears CPU_INTERRUPT_HARD otherwise. + * + * This method is called by ppc_set_interrupt when an interrupt is raised or + * lowered, and should also be called whenever an interrupt masking condition + * is changed, e.g.: + * - When relevant bits of MSR are altered, like EE, HV, PR, etc.; + * - When relevant bits of LPCR are altered, like PECE, HDICE, HVICE, etc.; + * - When PSSCR[EC] or env->resume_as_sreset are changed; + * - When cs->halted is changed and the CPU has a different interrupt masking + * logic in power-saving mode (e.g., POWER7/8/9/10); + */ +void ppc_maybe_interrupt(CPUPPCState *env) +{ + CPUState *cs = env_cpu(env); + BQL_LOCK_GUARD(); + + if (ppc_next_unmasked_interrupt(env)) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } +} + +#ifdef TARGET_PPC64 +static void p7_deliver_interrupt(CPUPPCState *env, int interrupt) +{ + PowerPCCPU *cpu = env_archcpu(env); + + switch (interrupt) { + case PPC_INTERRUPT_MCK: /* Machine check exception */ + env->pending_interrupts &= ~PPC_INTERRUPT_MCK; + powerpc_excp(cpu, POWERPC_EXCP_MCHECK); + break; + + case PPC_INTERRUPT_HDECR: /* Hypervisor decrementer exception */ + /* HDEC clears on delivery */ + env->pending_interrupts &= ~PPC_INTERRUPT_HDECR; + powerpc_excp(cpu, POWERPC_EXCP_HDECR); + break; + + case PPC_INTERRUPT_EXT: + if (books_vhyp_promotes_external_to_hvirt(cpu)) { + powerpc_excp(cpu, POWERPC_EXCP_HVIRT); + } else { + powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL); + } + break; + + case PPC_INTERRUPT_DECR: /* Decrementer exception */ + powerpc_excp(cpu, POWERPC_EXCP_DECR); + break; + case PPC_INTERRUPT_PERFM: + env->pending_interrupts &= ~PPC_INTERRUPT_PERFM; + powerpc_excp(cpu, POWERPC_EXCP_PERFM); + break; + case 0: /* * This is a bug ! It means that has_work took us out of halt without * anything to deliver while in a PM state that requires getting @@ -1846,8 +2173,280 @@ static void ppc_hw_interrupt(CPUPPCState *env) * It generally means a discrepancy between the wakeup conditions in the * processor has_work implementation and the logic in this function. */ - cpu_abort(env_cpu(env), - "Wakeup from PM state but interrupt Undelivered"); + assert(!env->resume_as_sreset); + break; + default: + cpu_abort(env_cpu(env), "Invalid PowerPC interrupt %d. Aborting\n", + interrupt); + } +} + +static void p8_deliver_interrupt(CPUPPCState *env, int interrupt) +{ + PowerPCCPU *cpu = env_archcpu(env); + + switch (interrupt) { + case PPC_INTERRUPT_MCK: /* Machine check exception */ + env->pending_interrupts &= ~PPC_INTERRUPT_MCK; + powerpc_excp(cpu, POWERPC_EXCP_MCHECK); + break; + + case PPC_INTERRUPT_HDECR: /* Hypervisor decrementer exception */ + /* HDEC clears on delivery */ + env->pending_interrupts &= ~PPC_INTERRUPT_HDECR; + powerpc_excp(cpu, POWERPC_EXCP_HDECR); + break; + + case PPC_INTERRUPT_EXT: + if (books_vhyp_promotes_external_to_hvirt(cpu)) { + powerpc_excp(cpu, POWERPC_EXCP_HVIRT); + } else { + powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL); + } + break; + + case PPC_INTERRUPT_DECR: /* Decrementer exception */ + powerpc_excp(cpu, POWERPC_EXCP_DECR); + break; + case PPC_INTERRUPT_DOORBELL: + env->pending_interrupts &= ~PPC_INTERRUPT_DOORBELL; + if (is_book3s_arch2x(env)) { + powerpc_excp(cpu, POWERPC_EXCP_SDOOR); + } else { + powerpc_excp(cpu, POWERPC_EXCP_DOORI); + } + break; + case PPC_INTERRUPT_HDOORBELL: + env->pending_interrupts &= ~PPC_INTERRUPT_HDOORBELL; + powerpc_excp(cpu, POWERPC_EXCP_SDOOR_HV); + break; + case PPC_INTERRUPT_PERFM: + env->pending_interrupts &= ~PPC_INTERRUPT_PERFM; + powerpc_excp(cpu, POWERPC_EXCP_PERFM); + break; + case PPC_INTERRUPT_EBB: /* EBB exception */ + env->pending_interrupts &= ~PPC_INTERRUPT_EBB; + if (env->spr[SPR_BESCR] & BESCR_PMEO) { + powerpc_excp(cpu, POWERPC_EXCP_PERFM_EBB); + } else if (env->spr[SPR_BESCR] & BESCR_EEO) { + powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL_EBB); + } + break; + case 0: + /* + * This is a bug ! It means that has_work took us out of halt without + * anything to deliver while in a PM state that requires getting + * out via a 0x100 + * + * This means we will incorrectly execute past the power management + * instruction instead of triggering a reset. + * + * It generally means a discrepancy between the wakeup conditions in the + * processor has_work implementation and the logic in this function. + */ + assert(!env->resume_as_sreset); + break; + default: + cpu_abort(env_cpu(env), "Invalid PowerPC interrupt %d. Aborting\n", + interrupt); + } +} + +static void p9_deliver_interrupt(CPUPPCState *env, int interrupt) +{ + PowerPCCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + + if (cs->halted && !(env->spr[SPR_PSSCR] & PSSCR_EC) && + !FIELD_EX64(env->msr, MSR, EE)) { + /* + * A pending interrupt took us out of power-saving, but MSR[EE] says + * that we should return to NIP+4 instead of delivering it. + */ + return; + } + + switch (interrupt) { + case PPC_INTERRUPT_MCK: /* Machine check exception */ + env->pending_interrupts &= ~PPC_INTERRUPT_MCK; + powerpc_excp(cpu, POWERPC_EXCP_MCHECK); + break; + + case PPC_INTERRUPT_HDECR: /* Hypervisor decrementer exception */ + /* HDEC clears on delivery */ + env->pending_interrupts &= ~PPC_INTERRUPT_HDECR; + powerpc_excp(cpu, POWERPC_EXCP_HDECR); + break; + case PPC_INTERRUPT_HVIRT: /* Hypervisor virtualization interrupt */ + powerpc_excp(cpu, POWERPC_EXCP_HVIRT); + break; + + case PPC_INTERRUPT_EXT: + if (books_vhyp_promotes_external_to_hvirt(cpu)) { + powerpc_excp(cpu, POWERPC_EXCP_HVIRT); + } else { + powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL); + } + break; + + case PPC_INTERRUPT_DECR: /* Decrementer exception */ + powerpc_excp(cpu, POWERPC_EXCP_DECR); + break; + case PPC_INTERRUPT_DOORBELL: + env->pending_interrupts &= ~PPC_INTERRUPT_DOORBELL; + powerpc_excp(cpu, POWERPC_EXCP_SDOOR); + break; + case PPC_INTERRUPT_HDOORBELL: + env->pending_interrupts &= ~PPC_INTERRUPT_HDOORBELL; + powerpc_excp(cpu, POWERPC_EXCP_SDOOR_HV); + break; + case PPC_INTERRUPT_PERFM: + env->pending_interrupts &= ~PPC_INTERRUPT_PERFM; + powerpc_excp(cpu, POWERPC_EXCP_PERFM); + break; + case PPC_INTERRUPT_EBB: /* EBB exception */ + env->pending_interrupts &= ~PPC_INTERRUPT_EBB; + if (env->spr[SPR_BESCR] & BESCR_PMEO) { + powerpc_excp(cpu, POWERPC_EXCP_PERFM_EBB); + } else if (env->spr[SPR_BESCR] & BESCR_EEO) { + powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL_EBB); + } + break; + case 0: + /* + * This is a bug ! It means that has_work took us out of halt without + * anything to deliver while in a PM state that requires getting + * out via a 0x100 + * + * This means we will incorrectly execute past the power management + * instruction instead of triggering a reset. + * + * It generally means a discrepancy between the wakeup conditions in the + * processor has_work implementation and the logic in this function. + */ + assert(!env->resume_as_sreset); + break; + default: + cpu_abort(env_cpu(env), "Invalid PowerPC interrupt %d. Aborting\n", + interrupt); + } +} +#endif /* TARGET_PPC64 */ + +static void ppc_deliver_interrupt(CPUPPCState *env, int interrupt) +{ +#ifdef TARGET_PPC64 + switch (env->excp_model) { + case POWERPC_EXCP_POWER7: + return p7_deliver_interrupt(env, interrupt); + case POWERPC_EXCP_POWER8: + return p8_deliver_interrupt(env, interrupt); + case POWERPC_EXCP_POWER9: + case POWERPC_EXCP_POWER10: + return p9_deliver_interrupt(env, interrupt); + default: + break; + } +#endif + PowerPCCPU *cpu = env_archcpu(env); + + switch (interrupt) { + case PPC_INTERRUPT_RESET: /* External reset */ + env->pending_interrupts &= ~PPC_INTERRUPT_RESET; + powerpc_excp(cpu, POWERPC_EXCP_RESET); + break; + case PPC_INTERRUPT_MCK: /* Machine check exception */ + env->pending_interrupts &= ~PPC_INTERRUPT_MCK; + powerpc_excp(cpu, POWERPC_EXCP_MCHECK); + break; + + case PPC_INTERRUPT_HDECR: /* Hypervisor decrementer exception */ + /* HDEC clears on delivery */ + env->pending_interrupts &= ~PPC_INTERRUPT_HDECR; + powerpc_excp(cpu, POWERPC_EXCP_HDECR); + break; + case PPC_INTERRUPT_HVIRT: /* Hypervisor virtualization interrupt */ + powerpc_excp(cpu, POWERPC_EXCP_HVIRT); + break; + + case PPC_INTERRUPT_EXT: + if (books_vhyp_promotes_external_to_hvirt(cpu)) { + powerpc_excp(cpu, POWERPC_EXCP_HVIRT); + } else { + powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL); + } + break; + case PPC_INTERRUPT_CEXT: /* External critical interrupt */ + powerpc_excp(cpu, POWERPC_EXCP_CRITICAL); + break; + + case PPC_INTERRUPT_WDT: /* Watchdog timer on embedded PowerPC */ + env->pending_interrupts &= ~PPC_INTERRUPT_WDT; + powerpc_excp(cpu, POWERPC_EXCP_WDT); + break; + case PPC_INTERRUPT_CDOORBELL: + env->pending_interrupts &= ~PPC_INTERRUPT_CDOORBELL; + powerpc_excp(cpu, POWERPC_EXCP_DOORCI); + break; + case PPC_INTERRUPT_FIT: /* Fixed interval timer on embedded PowerPC */ + env->pending_interrupts &= ~PPC_INTERRUPT_FIT; + powerpc_excp(cpu, POWERPC_EXCP_FIT); + break; + case PPC_INTERRUPT_PIT: /* Programmable interval timer on embedded ppc */ + env->pending_interrupts &= ~PPC_INTERRUPT_PIT; + powerpc_excp(cpu, POWERPC_EXCP_PIT); + break; + case PPC_INTERRUPT_DECR: /* Decrementer exception */ + if (ppc_decr_clear_on_delivery(env)) { + env->pending_interrupts &= ~PPC_INTERRUPT_DECR; + } + powerpc_excp(cpu, POWERPC_EXCP_DECR); + break; + case PPC_INTERRUPT_DOORBELL: + env->pending_interrupts &= ~PPC_INTERRUPT_DOORBELL; + if (is_book3s_arch2x(env)) { + powerpc_excp(cpu, POWERPC_EXCP_SDOOR); + } else { + powerpc_excp(cpu, POWERPC_EXCP_DOORI); + } + break; + case PPC_INTERRUPT_HDOORBELL: + env->pending_interrupts &= ~PPC_INTERRUPT_HDOORBELL; + powerpc_excp(cpu, POWERPC_EXCP_SDOOR_HV); + break; + case PPC_INTERRUPT_PERFM: + env->pending_interrupts &= ~PPC_INTERRUPT_PERFM; + powerpc_excp(cpu, POWERPC_EXCP_PERFM); + break; + case PPC_INTERRUPT_THERM: /* Thermal interrupt */ + env->pending_interrupts &= ~PPC_INTERRUPT_THERM; + powerpc_excp(cpu, POWERPC_EXCP_THERM); + break; + case PPC_INTERRUPT_EBB: /* EBB exception */ + env->pending_interrupts &= ~PPC_INTERRUPT_EBB; + if (env->spr[SPR_BESCR] & BESCR_PMEO) { + powerpc_excp(cpu, POWERPC_EXCP_PERFM_EBB); + } else if (env->spr[SPR_BESCR] & BESCR_EEO) { + powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL_EBB); + } + break; + case 0: + /* + * This is a bug ! It means that has_work took us out of halt without + * anything to deliver while in a PM state that requires getting + * out via a 0x100 + * + * This means we will incorrectly execute past the power management + * instruction instead of triggering a reset. + * + * It generally means a discrepancy between the wakeup conditions in the + * processor has_work implementation and the logic in this function. + */ + assert(!env->resume_as_sreset); + break; + default: + cpu_abort(env_cpu(env), "Invalid PowerPC interrupt %d. Aborting\n", + interrupt); } } @@ -1881,17 +2480,23 @@ void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector) bool ppc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); + int interrupt; - if (interrupt_request & CPU_INTERRUPT_HARD) { - ppc_hw_interrupt(env); - if (env->pending_interrupts == 0) { - cs->interrupt_request &= ~CPU_INTERRUPT_HARD; - } - return true; + if ((interrupt_request & CPU_INTERRUPT_HARD) == 0) { + return false; } - return false; + + interrupt = ppc_next_unmasked_interrupt(env); + if (interrupt == 0) { + return false; + } + + ppc_deliver_interrupt(env, interrupt); + if (env->pending_interrupts == 0) { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + return true; } #endif /* !CONFIG_USER_ONLY */ @@ -1937,22 +2542,24 @@ void helper_raise_exception(CPUPPCState *env, uint32_t exception) { raise_exception_err_ra(env, exception, 0, 0); } -#endif -#if !defined(CONFIG_USER_ONLY) -#ifdef CONFIG_TCG +#ifndef CONFIG_USER_ONLY void helper_store_msr(CPUPPCState *env, target_ulong val) { uint32_t excp = hreg_store_msr(env, val, 0); if (excp != 0) { - CPUState *cs = env_cpu(env); - cpu_interrupt_exittb(cs); + cpu_interrupt_exittb(env_cpu(env)); raise_exception(env, excp); } } -#if defined(TARGET_PPC64) +void helper_ppc_maybe_interrupt(CPUPPCState *env) +{ + ppc_maybe_interrupt(env); +} + +#ifdef TARGET_PPC64 void helper_scv(CPUPPCState *env, uint32_t lev) { if (env->spr[SPR_FSCR] & (1ull << FSCR_SCV)) { @@ -1962,23 +2569,28 @@ void helper_scv(CPUPPCState *env, uint32_t lev) } } -void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn) +void helper_pminsn(CPUPPCState *env, uint32_t insn) { - CPUState *cs; + CPUState *cs = env_cpu(env); - cs = env_cpu(env); cs->halted = 1; /* Condition for waking up at 0x100 */ env->resume_as_sreset = (insn != PPC_PM_STOP) || (env->spr[SPR_PSSCR] & PSSCR_EC); + + /* HDECR is not to wake from PM state, it may have already fired */ + if (env->resume_as_sreset) { + PowerPCCPU *cpu = env_archcpu(env); + ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 0); + } + + ppc_maybe_interrupt(env); } -#endif /* defined(TARGET_PPC64) */ +#endif /* TARGET_PPC64 */ static void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) { - CPUState *cs = env_cpu(env); - /* MSR:POW cannot be set by any form of rfi */ msr &= ~(1ULL << MSR_POW); @@ -1986,7 +2598,7 @@ static void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) if (env->flags & POWERPC_FLAG_TGPR) msr &= ~(1ULL << MSR_TGPR); -#if defined(TARGET_PPC64) +#ifdef TARGET_PPC64 /* Switching to 32-bit ? Crop the nip */ if (!msr_is_64bit(env, msr)) { nip = (uint32_t)nip; @@ -2002,7 +2614,7 @@ static void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) * No need to raise an exception here, as rfi is always the last * insn of a TB */ - cpu_interrupt_exittb(cs); + cpu_interrupt_exittb(env_cpu(env)); /* Reset the reservation */ env->reserve_addr = -1; @@ -2015,8 +2627,7 @@ void helper_rfi(CPUPPCState *env) do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful); } -#define MSR_BOOK3S_MASK -#if defined(TARGET_PPC64) +#ifdef TARGET_PPC64 void helper_rfid(CPUPPCState *env) { /* @@ -2037,9 +2648,7 @@ void helper_hrfid(CPUPPCState *env) { do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]); } -#endif -#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) void helper_rfebb(CPUPPCState *env, target_ulong s) { target_ulong msr = env->msr; @@ -2081,7 +2690,6 @@ void helper_rfebb(CPUPPCState *env, target_ulong s) static void do_ebb(CPUPPCState *env, int ebb_excp) { PowerPCCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); /* * FSCR_EBB and FSCR_IC_EBB are the same bits used with @@ -2099,8 +2707,7 @@ static void do_ebb(CPUPPCState *env, int ebb_excp) if (FIELD_EX64(env->msr, MSR, PR)) { powerpc_excp(cpu, ebb_excp); } else { - env->pending_interrupts |= 1 << PPC_INTERRUPT_EBB; - cpu_interrupt(cs, CPU_INTERRUPT_HARD); + ppc_set_irq(cpu, PPC_INTERRUPT_EBB, 1); } } @@ -2116,7 +2723,7 @@ void raise_ebb_perfm_exception(CPUPPCState *env) do_ebb(env, POWERPC_EXCP_PERFM_EBB); } -#endif +#endif /* TARGET_PPC64 */ /*****************************************************************************/ /* Embedded PowerPC specific helpers */ @@ -2141,10 +2748,8 @@ void helper_rfmci(CPUPPCState *env) /* FIXME: choose CSRR1 or MCSRR1 based on cpu type */ do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]); } -#endif /* CONFIG_TCG */ -#endif /* !defined(CONFIG_USER_ONLY) */ +#endif /* !CONFIG_USER_ONLY */ -#ifdef CONFIG_TCG void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2, uint32_t flags) { @@ -2158,7 +2763,7 @@ void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2, } } -#if defined(TARGET_PPC64) +#ifdef TARGET_PPC64 void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2, uint32_t flags) { @@ -2171,13 +2776,120 @@ void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2, POWERPC_EXCP_TRAP, GETPC()); } } -#endif -#endif +#endif /* TARGET_PPC64 */ -#if !defined(CONFIG_USER_ONLY) +static uint32_t helper_SIMON_LIKE_32_64(uint32_t x, uint64_t key, uint32_t lane) +{ + const uint16_t c = 0xfffc; + const uint64_t z0 = 0xfa2561cdf44ac398ULL; + uint16_t z = 0, temp; + uint16_t k[32], eff_k[32], xleft[33], xright[33], fxleft[32]; -#ifdef CONFIG_TCG + for (int i = 3; i >= 0; i--) { + k[i] = key & 0xffff; + key >>= 16; + } + xleft[0] = x & 0xffff; + xright[0] = (x >> 16) & 0xffff; + for (int i = 0; i < 28; i++) { + z = (z0 >> (63 - i)) & 1; + temp = ror16(k[i + 3], 3) ^ k[i + 1]; + k[i + 4] = c ^ z ^ k[i] ^ temp ^ ror16(temp, 1); + } + + for (int i = 0; i < 8; i++) { + eff_k[4 * i + 0] = k[4 * i + ((0 + lane) % 4)]; + eff_k[4 * i + 1] = k[4 * i + ((1 + lane) % 4)]; + eff_k[4 * i + 2] = k[4 * i + ((2 + lane) % 4)]; + eff_k[4 * i + 3] = k[4 * i + ((3 + lane) % 4)]; + } + + for (int i = 0; i < 32; i++) { + fxleft[i] = (rol16(xleft[i], 1) & + rol16(xleft[i], 8)) ^ rol16(xleft[i], 2); + xleft[i + 1] = xright[i] ^ fxleft[i] ^ eff_k[i]; + xright[i + 1] = xleft[i]; + } + + return (((uint32_t)xright[32]) << 16) | xleft[32]; +} + +static uint64_t hash_digest(uint64_t ra, uint64_t rb, uint64_t key) +{ + uint64_t stage0_h = 0ULL, stage0_l = 0ULL; + uint64_t stage1_h, stage1_l; + + for (int i = 0; i < 4; i++) { + stage0_h |= ror64(rb & 0xff, 8 * (2 * i + 1)); + stage0_h |= ((ra >> 32) & 0xff) << (8 * 2 * i); + stage0_l |= ror64((rb >> 32) & 0xff, 8 * (2 * i + 1)); + stage0_l |= (ra & 0xff) << (8 * 2 * i); + rb >>= 8; + ra >>= 8; + } + + stage1_h = (uint64_t)helper_SIMON_LIKE_32_64(stage0_h >> 32, key, 0) << 32; + stage1_h |= helper_SIMON_LIKE_32_64(stage0_h, key, 1); + stage1_l = (uint64_t)helper_SIMON_LIKE_32_64(stage0_l >> 32, key, 2) << 32; + stage1_l |= helper_SIMON_LIKE_32_64(stage0_l, key, 3); + + return stage1_h ^ stage1_l; +} + +static void do_hash(CPUPPCState *env, target_ulong ea, target_ulong ra, + target_ulong rb, uint64_t key, bool store) +{ + uint64_t calculated_hash = hash_digest(ra, rb, key), loaded_hash; + + if (store) { + cpu_stq_data_ra(env, ea, calculated_hash, GETPC()); + } else { + loaded_hash = cpu_ldq_data_ra(env, ea, GETPC()); + if (loaded_hash != calculated_hash) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_TRAP, GETPC()); + } + } +} + +#include "qemu/guest-random.h" + +#ifdef TARGET_PPC64 +#define HELPER_HASH(op, key, store, dexcr_aspect) \ +void helper_##op(CPUPPCState *env, target_ulong ea, target_ulong ra, \ + target_ulong rb) \ +{ \ + if (env->msr & R_MSR_PR_MASK) { \ + if (!(env->spr[SPR_DEXCR] & R_DEXCR_PRO_##dexcr_aspect##_MASK || \ + env->spr[SPR_HDEXCR] & R_HDEXCR_ENF_##dexcr_aspect##_MASK)) \ + return; \ + } else if (!(env->msr & R_MSR_HV_MASK)) { \ + if (!(env->spr[SPR_DEXCR] & R_DEXCR_PNH_##dexcr_aspect##_MASK || \ + env->spr[SPR_HDEXCR] & R_HDEXCR_ENF_##dexcr_aspect##_MASK)) \ + return; \ + } else if (!(env->msr & R_MSR_S_MASK)) { \ + if (!(env->spr[SPR_HDEXCR] & R_HDEXCR_HNU_##dexcr_aspect##_MASK)) \ + return; \ + } \ + \ + do_hash(env, ea, ra, rb, key, store); \ +} +#else +#define HELPER_HASH(op, key, store, dexcr_aspect) \ +void helper_##op(CPUPPCState *env, target_ulong ea, target_ulong ra, \ + target_ulong rb) \ +{ \ + do_hash(env, ea, ra, rb, key, store); \ +} +#endif /* TARGET_PPC64 */ + +HELPER_HASH(HASHST, env->spr[SPR_HASHKEYR], true, NPHIE) +HELPER_HASH(HASHCHK, env->spr[SPR_HASHKEYR], false, NPHIE) +HELPER_HASH(HASHSTP, env->spr[SPR_HASHPKEYR], true, PHIE) +HELPER_HASH(HASHCHKP, env->spr[SPR_HASHPKEYR], false, PHIE) + +#ifndef CONFIG_USER_ONLY /* Embedded.Processor Control */ static int dbell2irq(target_ulong rb) { @@ -2210,7 +2922,7 @@ void helper_msgclr(CPUPPCState *env, target_ulong rb) return; } - env->pending_interrupts &= ~(1 << irq); + ppc_set_irq(env_archcpu(env), irq, 0); } void helper_msgsnd(target_ulong rb) @@ -2223,17 +2935,16 @@ void helper_msgsnd(target_ulong rb) return; } - qemu_mutex_lock_iothread(); + bql_lock(); CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *cenv = &cpu->env; if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { - cenv->pending_interrupts |= 1 << irq; - cpu_interrupt(cs, CPU_INTERRUPT_HARD); + ppc_set_irq(cpu, irq, 1); } } - qemu_mutex_unlock_iothread(); + bql_unlock(); } /* Server Processor Control */ @@ -2254,25 +2965,24 @@ void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) return; } - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDOORBELL); + ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_HDOORBELL, 0); } static void book3s_msgsnd_common(int pir, int irq) { CPUState *cs; - qemu_mutex_lock_iothread(); + bql_lock(); CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *cenv = &cpu->env; /* TODO: broadcast message to all threads of the same processor */ if (cenv->spr_cb[SPR_PIR].default_value == pir) { - cenv->pending_interrupts |= 1 << irq; - cpu_interrupt(cs, CPU_INTERRUPT_HARD); + ppc_set_irq(cpu, irq, 1); } } - qemu_mutex_unlock_iothread(); + bql_unlock(); } void helper_book3s_msgsnd(target_ulong rb) @@ -2286,7 +2996,7 @@ void helper_book3s_msgsnd(target_ulong rb) book3s_msgsnd_common(pir, PPC_INTERRUPT_HDOORBELL); } -#if defined(TARGET_PPC64) +#ifdef TARGET_PPC64 void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb) { helper_hfscr_facility_check(env, HFSCR_MSGP, "msgclrp", HFSCR_IC_MSGP); @@ -2295,39 +3005,75 @@ void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb) return; } - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); + ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_DOORBELL, 0); } /* - * sends a message to other threads that are on the same + * sends a message to another thread on the same * multi-threaded processor */ void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb) { - int pir = env->spr_cb[SPR_PIR].default_value; + CPUState *cs = env_cpu(env); + PowerPCCPU *cpu = env_archcpu(env); + CPUState *ccs; + uint32_t nr_threads = cs->nr_threads; + int ttir = rb & PPC_BITMASK(57, 63); helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP); - if (!dbell_type_server(rb)) { + if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + nr_threads = 1; /* msgsndp behaves as 1-thread in LPAR-per-thread mode*/ + } + + if (!dbell_type_server(rb) || ttir >= nr_threads) { return; } - /* TODO: TCG supports only one thread */ + if (nr_threads == 1) { + ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, 1); + return; + } - book3s_msgsnd_common(pir, PPC_INTERRUPT_DOORBELL); + /* Does iothread need to be locked for walking CPU list? */ + bql_lock(); + THREAD_SIBLING_FOREACH(cs, ccs) { + PowerPCCPU *ccpu = POWERPC_CPU(ccs); + uint32_t thread_id = ppc_cpu_tir(ccpu); + + if (ttir == thread_id) { + ppc_set_irq(ccpu, PPC_INTERRUPT_DOORBELL, 1); + bql_unlock(); + return; + } + } + + g_assert_not_reached(); } #endif /* TARGET_PPC64 */ +/* Single-step tracing */ +void helper_book3s_trace(CPUPPCState *env, target_ulong prev_ip) +{ + uint32_t error_code = 0; + if (env->insns_flags2 & PPC2_ISA207S) { + /* Load/store reporting, SRR1[35, 36] and SDAR, are not implemented. */ + env->spr[SPR_POWER_SIAR] = prev_ip; + error_code = PPC_BIT(33); + } + raise_exception_err(env, POWERPC_EXCP_TRACE, error_code); +} + void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { - CPUPPCState *env = cs->env_ptr; + CPUPPCState *env = cpu_env(cs); uint32_t insn; /* Restore state and reload the insn we executed, for filling in DSISR. */ - cpu_restore_state(cs, retaddr, true); - insn = cpu_ldl_code(env, env->nip); + cpu_restore_state(cs, retaddr); + insn = ppc_ldl_code(env, env->nip); switch (env->mmu_model) { case POWERPC_MMU_SOFT_4xx: @@ -2346,5 +3092,149 @@ void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, env->error_code = insn & 0x03FF0000; cpu_loop_exit(cs); } -#endif /* CONFIG_TCG */ + +void ppc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, + vaddr vaddr, unsigned size, + MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, uintptr_t retaddr) +{ + CPUPPCState *env = cpu_env(cs); + + switch (env->excp_model) { +#if defined(TARGET_PPC64) + case POWERPC_EXCP_POWER8: + case POWERPC_EXCP_POWER9: + case POWERPC_EXCP_POWER10: + /* + * Machine check codes can be found in processor User Manual or + * Linux or skiboot source. + */ + if (access_type == MMU_DATA_LOAD) { + env->spr[SPR_DAR] = vaddr; + env->spr[SPR_DSISR] = PPC_BIT(57); + env->error_code = PPC_BIT(42); + + } else if (access_type == MMU_DATA_STORE) { + /* + * MCE for stores in POWER is asynchronous so hardware does + * not set DAR, but QEMU can do better. + */ + env->spr[SPR_DAR] = vaddr; + env->error_code = PPC_BIT(36) | PPC_BIT(43) | PPC_BIT(45); + env->error_code |= PPC_BIT(42); + + } else { /* Fetch */ + /* + * is_prefix_insn_excp() tests !PPC_BIT(42) to avoid fetching + * the instruction, so that must always be clear for fetches. + */ + env->error_code = PPC_BIT(36) | PPC_BIT(44) | PPC_BIT(45); + } + break; +#endif + default: + /* + * TODO: Check behaviour for other CPUs, for now do nothing. + * Could add a basic MCE even if real hardware ignores. + */ + return; + } + + cs->exception_index = POWERPC_EXCP_MCHECK; + cpu_loop_exit_restore(cs, retaddr); +} + +void ppc_cpu_debug_excp_handler(CPUState *cs) +{ +#if defined(TARGET_PPC64) + CPUPPCState *env = cpu_env(cs); + + if (env->insns_flags2 & PPC2_ISA207S) { + if (cs->watchpoint_hit) { + if (cs->watchpoint_hit->flags & BP_CPU) { + env->spr[SPR_DAR] = cs->watchpoint_hit->hitaddr; + env->spr[SPR_DSISR] = PPC_BIT(41); + cs->watchpoint_hit = NULL; + raise_exception(env, POWERPC_EXCP_DSI); + } + cs->watchpoint_hit = NULL; + } else if (cpu_breakpoint_test(cs, env->nip, BP_CPU)) { + raise_exception_err(env, POWERPC_EXCP_TRACE, + PPC_BIT(33) | PPC_BIT(43)); + } + } +#endif +} + +bool ppc_cpu_debug_check_breakpoint(CPUState *cs) +{ +#if defined(TARGET_PPC64) + CPUPPCState *env = cpu_env(cs); + + if (env->insns_flags2 & PPC2_ISA207S) { + target_ulong priv; + + priv = env->spr[SPR_CIABR] & PPC_BITMASK(62, 63); + switch (priv) { + case 0x1: /* problem */ + return env->msr & ((target_ulong)1 << MSR_PR); + case 0x2: /* supervisor */ + return (!(env->msr & ((target_ulong)1 << MSR_PR)) && + !(env->msr & ((target_ulong)1 << MSR_HV))); + case 0x3: /* hypervisor */ + return (!(env->msr & ((target_ulong)1 << MSR_PR)) && + (env->msr & ((target_ulong)1 << MSR_HV))); + default: + g_assert_not_reached(); + } + } +#endif + + return false; +} + +bool ppc_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) +{ +#if defined(TARGET_PPC64) + CPUPPCState *env = cpu_env(cs); + + if (env->insns_flags2 & PPC2_ISA207S) { + if (wp == env->dawr0_watchpoint) { + uint32_t dawrx = env->spr[SPR_DAWRX0]; + bool wt = extract32(dawrx, PPC_BIT_NR(59), 1); + bool wti = extract32(dawrx, PPC_BIT_NR(60), 1); + bool hv = extract32(dawrx, PPC_BIT_NR(61), 1); + bool sv = extract32(dawrx, PPC_BIT_NR(62), 1); + bool pr = extract32(dawrx, PPC_BIT_NR(62), 1); + + if ((env->msr & ((target_ulong)1 << MSR_PR)) && !pr) { + return false; + } else if ((env->msr & ((target_ulong)1 << MSR_HV)) && !hv) { + return false; + } else if (!sv) { + return false; + } + + if (!wti) { + if (env->msr & ((target_ulong)1 << MSR_DR)) { + if (!wt) { + return false; + } + } else { + if (wt) { + return false; + } + } + } + + return true; + } + } +#endif + + return false; +} + #endif /* !CONFIG_USER_ONLY */ +#endif /* CONFIG_TCG */ diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index fed0ce420a..4b3dcad5d1 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -141,62 +141,28 @@ static inline int ppc_float64_get_unbiased_exp(float64 f) return ((f >> 52) & 0x7FF) - 1023; } -/* Classify a floating-point number. */ -enum { - is_normal = 1, - is_zero = 2, - is_denormal = 4, - is_inf = 8, - is_qnan = 16, - is_snan = 32, - is_neg = 64, -}; - -#define COMPUTE_CLASS(tp) \ -static int tp##_classify(tp arg) \ -{ \ - int ret = tp##_is_neg(arg) * is_neg; \ - if (unlikely(tp##_is_any_nan(arg))) { \ - float_status dummy = { }; /* snan_bit_is_one = 0 */ \ - ret |= (tp##_is_signaling_nan(arg, &dummy) \ - ? is_snan : is_qnan); \ - } else if (unlikely(tp##_is_infinity(arg))) { \ - ret |= is_inf; \ - } else if (tp##_is_zero(arg)) { \ - ret |= is_zero; \ - } else if (tp##_is_zero_or_denormal(arg)) { \ - ret |= is_denormal; \ - } else { \ - ret |= is_normal; \ - } \ - return ret; \ -} - -COMPUTE_CLASS(float16) -COMPUTE_CLASS(float32) -COMPUTE_CLASS(float64) -COMPUTE_CLASS(float128) - -static void set_fprf_from_class(CPUPPCState *env, int class) -{ - static const uint8_t fprf[6][2] = { - { 0x04, 0x08 }, /* normalized */ - { 0x02, 0x12 }, /* zero */ - { 0x14, 0x18 }, /* denormalized */ - { 0x05, 0x09 }, /* infinity */ - { 0x11, 0x11 }, /* qnan */ - { 0x00, 0x00 }, /* snan -- flags are undefined */ - }; - bool isneg = class & is_neg; - - env->fpscr &= ~FP_FPRF; - env->fpscr |= fprf[ctz32(class)][isneg] << FPSCR_FPRF; -} - -#define COMPUTE_FPRF(tp) \ -void helper_compute_fprf_##tp(CPUPPCState *env, tp arg) \ -{ \ - set_fprf_from_class(env, tp##_classify(arg)); \ +#define COMPUTE_FPRF(tp) \ +void helper_compute_fprf_##tp(CPUPPCState *env, tp arg) \ +{ \ + bool neg = tp##_is_neg(arg); \ + target_ulong fprf; \ + if (likely(tp##_is_normal(arg))) { \ + fprf = neg ? 0x08 << FPSCR_FPRF : 0x04 << FPSCR_FPRF; \ + } else if (tp##_is_zero(arg)) { \ + fprf = neg ? 0x12 << FPSCR_FPRF : 0x02 << FPSCR_FPRF; \ + } else if (tp##_is_zero_or_denormal(arg)) { \ + fprf = neg ? 0x18 << FPSCR_FPRF : 0x14 << FPSCR_FPRF; \ + } else if (tp##_is_infinity(arg)) { \ + fprf = neg ? 0x09 << FPSCR_FPRF : 0x05 << FPSCR_FPRF; \ + } else { \ + float_status dummy = { }; /* snan_bit_is_one = 0 */ \ + if (tp##_is_signaling_nan(arg, &dummy)) { \ + fprf = 0x00 << FPSCR_FPRF; \ + } else { \ + fprf = 0x11 << FPSCR_FPRF; \ + } \ + } \ + env->fpscr = (env->fpscr & ~FP_FPRF) | fprf; \ } COMPUTE_FPRF(float16) @@ -348,7 +314,6 @@ static inline int float_overflow_excp(CPUPPCState *env) bool overflow_enabled = !!(env->fpscr & FP_OE); if (overflow_enabled) { - /* XXX: should adjust the result */ /* Update the floating-point enabled exception summary */ env->fpscr |= FP_FEX; /* We must update the target FPR before raising the exception */ @@ -367,7 +332,6 @@ static inline void float_underflow_excp(CPUPPCState *env) /* Update the floating-point exception summary */ env->fpscr |= FP_FX; if (env->fpscr & FP_UE) { - /* XXX: should adjust the result */ /* Update the floating-point enabled exception summary */ env->fpscr |= FP_FEX; /* We must update the target FPR before raising the exception */ @@ -464,7 +428,7 @@ static void do_fpscr_check_status(CPUPPCState *env, uintptr_t raddr) } cs->exception_index = POWERPC_EXCP_PROGRAM; env->error_code = error | POWERPC_EXCP_FP; - env->fpscr |= error ? FP_FEX : 0; + env->fpscr |= FP_FEX; /* Deferred floating-point exception after target FPSCR update */ if (fp_exceptions_enabled(env)) { raise_exception_err_ra(env, cs->exception_index, @@ -832,30 +796,21 @@ static void float_invalid_op_sqrt(CPUPPCState *env, int flags, } } -/* fsqrt - fsqrt. */ -float64 helper_fsqrt(CPUPPCState *env, float64 arg) -{ - float64 ret = float64_sqrt(arg, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_sqrt(env, flags, 1, GETPC()); - } - - return ret; +#define FPU_FSQRT(name, op) \ +float64 helper_##name(CPUPPCState *env, float64 arg) \ +{ \ + float64 ret = op(arg, &env->fp_status); \ + int flags = get_float_exception_flags(&env->fp_status); \ + \ + if (unlikely(flags & float_flag_invalid)) { \ + float_invalid_op_sqrt(env, flags, 1, GETPC()); \ + } \ + \ + return ret; \ } -/* fsqrts - fsqrts. */ -float64 helper_fsqrts(CPUPPCState *env, float64 arg) -{ - float64 ret = float64r32_sqrt(arg, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_sqrt(env, flags, 1, GETPC()); - } - return ret; -} +FPU_FSQRT(FSQRT, float64_sqrt) +FPU_FSQRT(FSQRTS, float64r32_sqrt) /* fre - fre. */ float64 helper_fre(CPUPPCState *env, float64 arg) @@ -2178,7 +2133,7 @@ VSX_TSQRT(xvtsqrtsp, 4, float32, VsrW(i), -126, 23) void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ ppc_vsr_t *s1, ppc_vsr_t *s2, ppc_vsr_t *s3) \ { \ - ppc_vsr_t t = *xt; \ + ppc_vsr_t t = { }; \ int i; \ \ helper_reset_fpstatus(env); \ @@ -2639,6 +2594,8 @@ uint32_t helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ int all_true = 1; \ int all_false = 1; \ \ + helper_reset_fpstatus(env); \ + \ for (i = 0; i < nels; i++) { \ if (unlikely(tp##_is_any_nan(xa->fld) || \ tp##_is_any_nan(xb->fld))) { \ @@ -2692,6 +2649,8 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ ppc_vsr_t t = { }; \ int i; \ \ + helper_reset_fpstatus(env); \ + \ for (i = 0; i < nels; i++) { \ t.tfld = stp##_to_##ttp(xb->sfld, &env->fp_status); \ if (unlikely(stp##_is_signaling_nan(xb->sfld, \ @@ -2717,6 +2676,8 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ ppc_vsr_t t = { }; \ int i; \ \ + helper_reset_fpstatus(env); \ + \ for (i = 0; i < nels; i++) { \ t.VsrW(2 * i) = stp##_to_##ttp(xb->VsrD(i), &env->fp_status); \ if (unlikely(stp##_is_signaling_nan(xb->VsrD(i), \ @@ -2754,6 +2715,8 @@ void helper_##op(CPUPPCState *env, uint32_t opcode, \ ppc_vsr_t t = *xt; \ int i; \ \ + helper_reset_fpstatus(env); \ + \ for (i = 0; i < nels; i++) { \ t.tfld = stp##_to_##ttp(xb->sfld, &env->fp_status); \ if (unlikely(stp##_is_signaling_nan(xb->sfld, \ @@ -2789,6 +2752,8 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ ppc_vsr_t t = { }; \ int i; \ \ + helper_reset_fpstatus(env); \ + \ for (i = 0; i < nels; i++) { \ t.tfld = stp##_to_##ttp(xb->sfld, 1, &env->fp_status); \ if (unlikely(stp##_is_signaling_nan(xb->sfld, \ @@ -2836,6 +2801,8 @@ void helper_XSCVQPDP(CPUPPCState *env, uint32_t ro, ppc_vsr_t *xt, ppc_vsr_t t = { }; float_status tstat; + helper_reset_fpstatus(env); + tstat = env->fp_status; if (ro != 0) { tstat.float_rounding_mode = float_round_to_odd; @@ -2857,6 +2824,7 @@ uint64_t helper_xscvdpspn(CPUPPCState *env, uint64_t xb) { uint64_t result, sign, exp, frac; + helper_reset_fpstatus(env); float_status tstat = env->fp_status; set_float_exception_flags(0, &tstat); @@ -2912,18 +2880,18 @@ uint64_t helper_XSCVSPDPN(uint64_t xb) #define VSX_CVT_FP_TO_INT(op, nels, stp, ttp, sfld, tfld, sfi, rnan) \ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ { \ - int all_flags = env->fp_status.float_exception_flags, flags; \ + int all_flags = 0; \ ppc_vsr_t t = { }; \ - int i; \ + int i, flags; \ \ for (i = 0; i < nels; i++) { \ - env->fp_status.float_exception_flags = 0; \ + helper_reset_fpstatus(env); \ t.tfld = stp##_to_##ttp##_round_to_zero(xb->sfld, &env->fp_status); \ flags = env->fp_status.float_exception_flags; \ + all_flags |= flags; \ if (unlikely(flags & float_flag_invalid)) { \ t.tfld = float_invalid_cvt(env, flags, t.tfld, rnan, 0, GETPC());\ } \ - all_flags |= flags; \ } \ \ *xt = t; \ @@ -2979,21 +2947,21 @@ VSX_CVT_FP_TO_INT128(XSCVQPSQZ, int128, 0x8000000000000000ULL); #define VSX_CVT_FP_TO_INT2(op, nels, stp, ttp, sfi, rnan) \ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ { \ - int all_flags = env->fp_status.float_exception_flags, flags; \ + int all_flags = 0; \ ppc_vsr_t t = { }; \ - int i; \ + int i, flags; \ \ for (i = 0; i < nels; i++) { \ - env->fp_status.float_exception_flags = 0; \ + helper_reset_fpstatus(env); \ t.VsrW(2 * i) = stp##_to_##ttp##_round_to_zero(xb->VsrD(i), \ &env->fp_status); \ flags = env->fp_status.float_exception_flags; \ + all_flags |= flags; \ if (unlikely(flags & float_flag_invalid)) { \ t.VsrW(2 * i) = float_invalid_cvt(env, flags, t.VsrW(2 * i), \ rnan, 0, GETPC()); \ } \ t.VsrW(2 * i + 1) = t.VsrW(2 * i); \ - all_flags |= flags; \ } \ \ *xt = t; \ @@ -3022,6 +2990,8 @@ void helper_##op(CPUPPCState *env, uint32_t opcode, \ ppc_vsr_t t = { }; \ int flags; \ \ + helper_reset_fpstatus(env); \ + \ t.tfld = stp##_to_##ttp##_round_to_zero(xb->sfld, &env->fp_status); \ flags = get_float_exception_flags(&env->fp_status); \ if (flags & float_flag_invalid) { \ @@ -3034,7 +3004,6 @@ void helper_##op(CPUPPCState *env, uint32_t opcode, \ VSX_CVT_FP_TO_INT_VECTOR(xscvqpsdz, float128, int64, f128, VsrD(0), \ 0x8000000000000000ULL) - VSX_CVT_FP_TO_INT_VECTOR(xscvqpswz, float128, int32, f128, VsrD(0), \ 0xffffffff80000000ULL) VSX_CVT_FP_TO_INT_VECTOR(xscvqpudz, float128, uint64, f128, VsrD(0), 0x0ULL) @@ -3057,6 +3026,8 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ ppc_vsr_t t = { }; \ int i; \ \ + helper_reset_fpstatus(env); \ + \ for (i = 0; i < nels; i++) { \ t.tfld = stp##_to_##ttp(xb->sfld, &env->fp_status); \ if (r2sp) { \ @@ -3126,6 +3097,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode, \ { \ ppc_vsr_t t = *xt; \ \ + helper_reset_fpstatus(env); \ t.tfld = stp##_to_##ttp(xb->sfld, &env->fp_status); \ helper_compute_fprf_##ttp(env, t.tfld); \ \ @@ -3159,6 +3131,8 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ int i; \ FloatRoundMode curr_rounding_mode; \ \ + helper_reset_fpstatus(env); \ + \ if (rmode != FLOAT_ROUND_CURRENT) { \ curr_rounding_mode = get_float_rounding_mode(&env->fp_status); \ set_float_rounding_mode(rmode, &env->fp_status); \ @@ -3237,93 +3211,82 @@ void helper_XVXSIGSP(ppc_vsr_t *xt, ppc_vsr_t *xb) *xt = t; } -/* - * VSX_TEST_DC - VSX floating point test data class - * op - instruction mnemonic - * nels - number of elements (1, 2 or 4) - * xbn - VSR register number - * tp - type (float32 or float64) - * fld - vsr_t field (VsrD(*) or VsrW(*)) - * tfld - target vsr_t field (VsrD(*) or VsrW(*)) - * fld_max - target field max - * scrf - set result in CR and FPCC - */ -#define VSX_TEST_DC(op, nels, xbn, tp, fld, tfld, fld_max, scrf) \ -void helper_##op(CPUPPCState *env, uint32_t opcode) \ +#define VSX_TSTDC(tp) \ +static int32_t tp##_tstdc(tp b, uint32_t dcmx) \ { \ - ppc_vsr_t *xt = &env->vsr[xT(opcode)]; \ - ppc_vsr_t *xb = &env->vsr[xbn]; \ - ppc_vsr_t t = { }; \ - uint32_t i, sign, dcmx; \ - uint32_t cc, match = 0; \ - \ - if (!scrf) { \ - dcmx = DCMX_XV(opcode); \ - } else { \ - t = *xt; \ - dcmx = DCMX(opcode); \ - } \ - \ - for (i = 0; i < nels; i++) { \ - sign = tp##_is_neg(xb->fld); \ - if (tp##_is_any_nan(xb->fld)) { \ - match = extract32(dcmx, 6, 1); \ - } else if (tp##_is_infinity(xb->fld)) { \ - match = extract32(dcmx, 4 + !sign, 1); \ - } else if (tp##_is_zero(xb->fld)) { \ - match = extract32(dcmx, 2 + !sign, 1); \ - } else if (tp##_is_zero_or_denormal(xb->fld)) { \ - match = extract32(dcmx, 0 + !sign, 1); \ - } \ - \ - if (scrf) { \ - cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT; \ - env->fpscr &= ~FP_FPCC; \ - env->fpscr |= cc << FPSCR_FPCC; \ - env->crf[BF(opcode)] = cc; \ - } else { \ - t.tfld = match ? fld_max : 0; \ - } \ - match = 0; \ - } \ - if (!scrf) { \ - *xt = t; \ + uint32_t match = 0; \ + uint32_t sign = tp##_is_neg(b); \ + if (tp##_is_any_nan(b)) { \ + match = extract32(dcmx, 6, 1); \ + } else if (tp##_is_infinity(b)) { \ + match = extract32(dcmx, 4 + !sign, 1); \ + } else if (tp##_is_zero(b)) { \ + match = extract32(dcmx, 2 + !sign, 1); \ + } else if (tp##_is_zero_or_denormal(b)) { \ + match = extract32(dcmx, 0 + !sign, 1); \ } \ + return (match != 0); \ } -VSX_TEST_DC(xvtstdcdp, 2, xB(opcode), float64, VsrD(i), VsrD(i), UINT64_MAX, 0) -VSX_TEST_DC(xvtstdcsp, 4, xB(opcode), float32, VsrW(i), VsrW(i), UINT32_MAX, 0) -VSX_TEST_DC(xststdcdp, 1, xB(opcode), float64, VsrD(0), VsrD(0), 0, 1) -VSX_TEST_DC(xststdcqp, 1, (rB(opcode) + 32), float128, f128, VsrD(0), 0, 1) +VSX_TSTDC(float32) +VSX_TSTDC(float64) +VSX_TSTDC(float128) +#undef VSX_TSTDC -void helper_xststdcsp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xb) +void helper_XVTSTDCDP(ppc_vsr_t *t, ppc_vsr_t *b, uint64_t dcmx, uint32_t v) { - uint32_t dcmx, sign, exp; - uint32_t cc, match = 0, not_sp = 0; - float64 arg = xb->VsrD(0); - float64 arg_sp; + int i; + for (i = 0; i < 2; i++) { + t->s64[i] = (int64_t)-float64_tstdc(b->f64[i], dcmx); + } +} - dcmx = DCMX(opcode); - exp = (arg >> 52) & 0x7FF; - sign = float64_is_neg(arg); +void helper_XVTSTDCSP(ppc_vsr_t *t, ppc_vsr_t *b, uint64_t dcmx, uint32_t v) +{ + int i; + for (i = 0; i < 4; i++) { + t->s32[i] = (int32_t)-float32_tstdc(b->f32[i], dcmx); + } +} - if (float64_is_any_nan(arg)) { - match = extract32(dcmx, 6, 1); - } else if (float64_is_infinity(arg)) { - match = extract32(dcmx, 4 + !sign, 1); - } else if (float64_is_zero(arg)) { - match = extract32(dcmx, 2 + !sign, 1); - } else if (float64_is_zero_or_denormal(arg) || (exp > 0 && exp < 0x381)) { - match = extract32(dcmx, 0 + !sign, 1); +static bool not_SP_value(float64 val) +{ + return val != helper_todouble(helper_tosingle(val)); +} + +/* + * VSX_XS_TSTDC - VSX Scalar Test Data Class + * NAME - instruction name + * FLD - vsr_t field (VsrD(0) or f128) + * TP - type (float64 or float128) + */ +#define VSX_XS_TSTDC(NAME, FLD, TP) \ + void helper_##NAME(CPUPPCState *env, uint32_t bf, \ + uint32_t dcmx, ppc_vsr_t *b) \ + { \ + uint32_t cc, match, sign = TP##_is_neg(b->FLD); \ + match = TP##_tstdc(b->FLD, dcmx); \ + cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT; \ + env->fpscr &= ~FP_FPCC; \ + env->fpscr |= cc << FPSCR_FPCC; \ + env->crf[bf] = cc; \ } - arg_sp = helper_todouble(helper_tosingle(arg)); - not_sp = arg != arg_sp; +VSX_XS_TSTDC(XSTSTDCDP, VsrD(0), float64) +VSX_XS_TSTDC(XSTSTDCQP, f128, float128) +#undef VSX_XS_TSTDC +void helper_XSTSTDCSP(CPUPPCState *env, uint32_t bf, + uint32_t dcmx, ppc_vsr_t *b) +{ + uint32_t cc, match, sign = float64_is_neg(b->VsrD(0)); + uint32_t exp = (b->VsrD(0) >> 52) & 0x7FF; + int not_sp = (int)not_SP_value(b->VsrD(0)); + match = float64_tstdc(b->VsrD(0), dcmx) || (exp > 0 && exp < 0x381); cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT | not_sp << CRF_SO_BIT; env->fpscr &= ~FP_FPCC; env->fpscr |= cc << FPSCR_FPCC; - env->crf[BF(opcode)] = cc; + env->crf[bf] = cc; } void helper_xsrqpi(CPUPPCState *env, uint32_t opcode, diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c index 1a0b9ca82c..3b28d4e21c 100644 --- a/target/ppc/gdbstub.c +++ b/target/ppc/gdbstub.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "internal.h" static int ppc_gdb_register_len_apple(int n) @@ -53,12 +54,6 @@ static int ppc_gdb_register_len(int n) case 0 ... 31: /* gprs */ return sizeof(target_ulong); - case 32 ... 63: - /* fprs */ - if (gdb_has_xml) { - return 0; - } - return 8; case 66: /* cr */ case 69: @@ -73,12 +68,6 @@ static int ppc_gdb_register_len(int n) case 68: /* ctr */ return sizeof(target_ulong); - case 70: - /* fpscr */ - if (gdb_has_xml) { - return 0; - } - return sizeof(target_ulong); default: return 0; } @@ -119,8 +108,7 @@ void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len) int ppc_cpu_gdb_read_register(CPUState *cs, GByteArray *buf, int n) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); uint8_t *mem_buf; int r = ppc_gdb_register_len(n); @@ -131,9 +119,6 @@ int ppc_cpu_gdb_read_register(CPUState *cs, GByteArray *buf, int n) if (n < 32) { /* gprs */ gdb_get_regl(buf, env->gpr[n]); - } else if (n < 64) { - /* fprs */ - gdb_get_reg64(buf, *cpu_fpr_ptr(env, n - 32)); } else { switch (n) { case 64: @@ -144,11 +129,7 @@ int ppc_cpu_gdb_read_register(CPUState *cs, GByteArray *buf, int n) break; case 66: { - uint32_t cr = 0; - int i; - for (i = 0; i < 8; i++) { - cr |= env->crf[i] << (32 - ((i + 1) * 4)); - } + uint32_t cr = ppc_get_cr(env); gdb_get_reg32(buf, cr); break; } @@ -161,9 +142,6 @@ int ppc_cpu_gdb_read_register(CPUState *cs, GByteArray *buf, int n) case 69: gdb_get_reg32(buf, cpu_read_xer(env)); break; - case 70: - gdb_get_reg32(buf, env->fpscr); - break; } } mem_buf = buf->data + buf->len - r; @@ -173,8 +151,7 @@ int ppc_cpu_gdb_read_register(CPUState *cs, GByteArray *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; + CPUPPCState *env = cpu_env(cs); uint8_t *mem_buf; int r = ppc_gdb_register_len_apple(n); @@ -202,11 +179,7 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, GByteArray *buf, int n) break; case 66 + 32: { - uint32_t cr = 0; - int i; - for (i = 0; i < 8; i++) { - cr |= env->crf[i] << (32 - ((i + 1) * 4)); - } + uint32_t cr = ppc_get_cr(env); gdb_get_reg32(buf, cr); break; } @@ -231,8 +204,7 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, GByteArray *buf, int n) int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); int r = ppc_gdb_register_len(n); if (!r) { @@ -256,10 +228,7 @@ int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) case 66: { uint32_t cr = ldl_p(mem_buf); - int i; - for (i = 0; i < 8; i++) { - env->crf[i] = (cr >> (32 - ((i + 1) * 4))) & 0xF; - } + ppc_set_cr(env, cr); break; } case 67: @@ -281,8 +250,7 @@ int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) } int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); int r = ppc_gdb_register_len_apple(n); if (!r) { @@ -306,10 +274,7 @@ int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n) case 66 + 32: { uint32_t cr = ldl_p(mem_buf); - int i; - for (i = 0; i < 8; i++) { - env->crf[i] = (cr >> (32 - ((i + 1) * 4))) & 0xF; - } + ppc_set_cr(env, cr); break; } case 67 + 32: @@ -331,23 +296,15 @@ int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n) } #ifndef CONFIG_USER_ONLY -void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu) +static void gdb_gen_spr_feature(CPUState *cs) { - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); + PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; - GString *xml; - char *spr_name; + GDBFeatureBuilder builder; unsigned int num_regs = 0; int i; - if (pcc->gdb_spr_xml) { - return; - } - - xml = g_string_new(""); - g_string_append(xml, ""); - g_string_append(xml, ""); - for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) { ppc_spr_t *spr = &env->spr_cb[i]; @@ -355,13 +312,6 @@ void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu) continue; } - spr_name = g_ascii_strdown(spr->name, -1); - g_string_append_printf(xml, ""); - /* * GDB identifies registers based on the order they are * presented in the XML. These ids will not match QEMU's @@ -374,20 +324,27 @@ void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu) num_regs++; } - g_string_append(xml, ""); - - pcc->gdb_num_sprs = num_regs; - pcc->gdb_spr_xml = g_string_free(xml, false); -} - -const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name) -{ - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); - - if (strcmp(xml_name, "power-spr.xml") == 0) { - return pcc->gdb_spr_xml; + if (pcc->gdb_spr.xml) { + return; } - return NULL; + + gdb_feature_builder_init(&builder, &pcc->gdb_spr, + "org.qemu.power.spr", "power-spr.xml", + cs->gdb_num_regs); + + for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) { + ppc_spr_t *spr = &env->spr_cb[i]; + + if (!spr->name) { + continue; + } + + gdb_feature_builder_append_reg(&builder, g_ascii_strdown(spr->name, -1), + TARGET_LONG_BITS, spr->gdb_id, + "int", "spr"); + } + + gdb_feature_builder_end(&builder); } #endif @@ -406,8 +363,10 @@ static int gdb_find_spr_idx(CPUPPCState *env, int n) return -1; } -static int gdb_get_spr_reg(CPUPPCState *env, GByteArray *buf, int n) +static int gdb_get_spr_reg(CPUState *cs, GByteArray *buf, int n) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; int reg; int len; @@ -417,13 +376,40 @@ static int gdb_get_spr_reg(CPUPPCState *env, GByteArray *buf, int n) } len = TARGET_LONG_SIZE; - gdb_get_regl(buf, env->spr[reg]); + + /* Handle those SPRs that are not part of the env->spr[] array */ + target_ulong val; + switch (reg) { +#if defined(TARGET_PPC64) + case SPR_CFAR: + val = env->cfar; + break; +#endif + case SPR_HDEC: + val = cpu_ppc_load_hdecr(env); + break; + case SPR_TBL: + val = cpu_ppc_load_tbl(env); + break; + case SPR_TBU: + val = cpu_ppc_load_tbu(env); + break; + case SPR_DECR: + val = cpu_ppc_load_decr(env); + break; + default: + val = env->spr[reg]; + } + gdb_get_regl(buf, val); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, len), len); return len; } -static int gdb_set_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_set_spr_reg(CPUState *cs, uint8_t *mem_buf, int n) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; int reg; int len; @@ -434,14 +420,27 @@ static int gdb_set_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) len = TARGET_LONG_SIZE; ppc_maybe_bswap_register(env, mem_buf, len); - env->spr[reg] = ldn_p(mem_buf, len); + + /* Handle those SPRs that are not part of the env->spr[] array */ + target_ulong val = ldn_p(mem_buf, len); + switch (reg) { +#if defined(TARGET_PPC64) + case SPR_CFAR: + env->cfar = val; + break; +#endif + default: + env->spr[reg] = val; + } return len; } #endif -static int gdb_get_float_reg(CPUPPCState *env, GByteArray *buf, int n) +static int gdb_get_float_reg(CPUState *cs, GByteArray *buf, int n) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; uint8_t *mem_buf; if (n < 32) { gdb_get_reg64(buf, *cpu_fpr_ptr(env, n)); @@ -458,8 +457,11 @@ static int gdb_get_float_reg(CPUPPCState *env, GByteArray *buf, int n) return 0; } -static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_set_float_reg(CPUState *cs, uint8_t *mem_buf, int n) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + if (n < 32) { ppc_maybe_bswap_register(env, mem_buf, 8); *cpu_fpr_ptr(env, n) = ldq_p(mem_buf); @@ -473,8 +475,10 @@ static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 0; } -static int gdb_get_avr_reg(CPUPPCState *env, GByteArray *buf, int n) +static int gdb_get_avr_reg(CPUState *cs, GByteArray *buf, int n) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; uint8_t *mem_buf; if (n < 32) { @@ -499,8 +503,11 @@ static int gdb_get_avr_reg(CPUPPCState *env, GByteArray *buf, int n) return 0; } -static int gdb_set_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_set_avr_reg(CPUState *cs, uint8_t *mem_buf, int n) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + if (n < 32) { ppc_avr_t *avr = cpu_avr_ptr(env, n); ppc_maybe_bswap_register(env, mem_buf, 16); @@ -521,8 +528,11 @@ static int gdb_set_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 0; } -static int gdb_get_spe_reg(CPUPPCState *env, GByteArray *buf, int n) +static int gdb_get_spe_reg(CPUState *cs, GByteArray *buf, int n) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + if (n < 32) { #if defined(TARGET_PPC64) gdb_get_reg32(buf, env->gpr[n] >> 32); @@ -545,8 +555,11 @@ static int gdb_get_spe_reg(CPUPPCState *env, GByteArray *buf, int n) return 0; } -static int gdb_set_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_set_spe_reg(CPUState *cs, uint8_t *mem_buf, int n) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + if (n < 32) { #if defined(TARGET_PPC64) target_ulong lo = (uint32_t)env->gpr[n]; @@ -574,8 +587,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, GByteArray *buf, int n) +static int gdb_get_vsx_reg(CPUState *cs, GByteArray *buf, int n) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + if (n < 32) { gdb_get_reg64(buf, *cpu_vsrl_ptr(env, n)); ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8); @@ -584,8 +600,11 @@ static int gdb_get_vsx_reg(CPUPPCState *env, GByteArray *buf, int n) return 0; } -static int gdb_set_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_set_vsx_reg(CPUState *cs, uint8_t *mem_buf, int n) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + if (n < 32) { ppc_maybe_bswap_register(env, mem_buf, 8); *cpu_vsrl_ptr(env, n) = ldq_p(mem_buf); @@ -594,12 +613,12 @@ static int gdb_set_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 0; } -gchar *ppc_gdb_arch_name(CPUState *cs) +const gchar *ppc_gdb_arch_name(CPUState *cs) { #if defined(TARGET_PPC64) - return g_strdup("powerpc:common64"); + return "powerpc:common64"; #else - return g_strdup("powerpc:common"); + return "powerpc:common"; #endif } @@ -607,22 +626,24 @@ void ppc_gdb_init(CPUState *cs, PowerPCCPUClass *pcc) { if (pcc->insns_flags & PPC_FLOAT) { gdb_register_coprocessor(cs, gdb_get_float_reg, gdb_set_float_reg, - 33, "power-fpu.xml", 0); + gdb_find_static_feature("power-fpu.xml"), 0); } if (pcc->insns_flags & PPC_ALTIVEC) { gdb_register_coprocessor(cs, gdb_get_avr_reg, gdb_set_avr_reg, - 34, "power-altivec.xml", 0); + gdb_find_static_feature("power-altivec.xml"), + 0); } if (pcc->insns_flags & PPC_SPE) { gdb_register_coprocessor(cs, gdb_get_spe_reg, gdb_set_spe_reg, - 34, "power-spe.xml", 0); + gdb_find_static_feature("power-spe.xml"), 0); } if (pcc->insns_flags2 & PPC2_VSX) { gdb_register_coprocessor(cs, gdb_get_vsx_reg, gdb_set_vsx_reg, - 32, "power-vsx.xml", 0); + gdb_find_static_feature("power-vsx.xml"), 0); } #ifndef CONFIG_USER_ONLY + gdb_gen_spr_feature(cs); gdb_register_coprocessor(cs, gdb_get_spr_reg, gdb_set_spr_reg, - pcc->gdb_num_sprs, "power-spr.xml", 0); + &pcc->gdb_spr, 0); #endif } diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 6233e28d85..86f97ee1e7 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -4,8 +4,13 @@ DEF_HELPER_FLAGS_4(tw, TCG_CALL_NO_WG, void, env, tl, tl, i32) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_4(td, TCG_CALL_NO_WG, void, env, tl, tl, i32) #endif +DEF_HELPER_4(HASHST, void, env, tl, tl, tl) +DEF_HELPER_4(HASHCHK, void, env, tl, tl, tl) +DEF_HELPER_4(HASHSTP, void, env, tl, tl, tl) +DEF_HELPER_4(HASHCHKP, void, env, tl, tl, tl) #if !defined(CONFIG_USER_ONLY) DEF_HELPER_2(store_msr, void, env, tl) +DEF_HELPER_1(ppc_maybe_interrupt, void, env) DEF_HELPER_1(rfi, void, env) DEF_HELPER_1(40x_rfci, void, env) DEF_HELPER_1(rfci, void, env) @@ -20,12 +25,17 @@ DEF_HELPER_1(hrfid, void, env) DEF_HELPER_2(rfebb, void, env, tl) DEF_HELPER_2(store_lpcr, void, env, tl) DEF_HELPER_2(store_pcr, void, env, tl) +DEF_HELPER_2(store_ciabr, void, env, tl) +DEF_HELPER_2(store_dawr0, void, env, tl) +DEF_HELPER_2(store_dawrx0, void, env, tl) DEF_HELPER_2(store_mmcr0, void, env, tl) DEF_HELPER_2(store_mmcr1, void, env, tl) DEF_HELPER_3(store_pmc, void, env, i32, i64) DEF_HELPER_2(read_pmc, tl, env, i32) DEF_HELPER_2(insns_inc, void, env, i32) +DEF_HELPER_1(handle_pmc5_overflow, void, env) #endif +DEF_HELPER_2(book3s_trace, void, env, tl) DEF_HELPER_1(check_tlb_flush_local, void, env) DEF_HELPER_1(check_tlb_flush_global, void, env) #endif @@ -54,6 +64,8 @@ DEF_HELPER_3(sraw, tl, env, tl, tl) DEF_HELPER_FLAGS_2(CFUGED, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(PDEPD, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(PEXTD, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_1(CDTBCD, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(CBCDTD, TCG_CALL_NO_RWG_SE, tl, tl) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_2(cmpeqb, TCG_CALL_NO_RWG_SE, i32, tl, tl) DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_NO_RWG_SE, tl, tl) @@ -114,8 +126,8 @@ DEF_HELPER_4(fmadds, i64, env, i64, i64, i64) DEF_HELPER_4(fmsubs, i64, env, i64, i64, i64) DEF_HELPER_4(fnmadds, i64, env, i64, i64, i64) DEF_HELPER_4(fnmsubs, i64, env, i64, i64, i64) -DEF_HELPER_2(fsqrt, f64, env, f64) -DEF_HELPER_2(fsqrts, f64, env, f64) +DEF_HELPER_2(FSQRT, f64, env, f64) +DEF_HELPER_2(FSQRTS, f64, env, f64) DEF_HELPER_2(fre, i64, env, i64) DEF_HELPER_2(fres, i64, env, i64) DEF_HELPER_2(frsqrte, i64, env, i64) @@ -137,15 +149,15 @@ DEF_HELPER_FLAGS_1(ftsqrt, TCG_CALL_NO_RWG_SE, i32, i64) #define dh_ctype_acc ppc_acc_t * #define dh_typecode_acc dh_typecode_ptr -DEF_HELPER_FLAGS_3(vavgub, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_3(vavguh, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_3(vavguw, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_3(vabsdub, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_3(vabsduh, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_3(vabsduw, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_3(vavgsb, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_3(vavgsh, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_3(vavgsw, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_4(VAVGUB, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) +DEF_HELPER_FLAGS_4(VAVGUH, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) +DEF_HELPER_FLAGS_4(VAVGUW, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) +DEF_HELPER_FLAGS_4(VABSDUB, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) +DEF_HELPER_FLAGS_4(VABSDUH, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) +DEF_HELPER_FLAGS_4(VABSDUW, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) +DEF_HELPER_FLAGS_4(VAVGSB, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) +DEF_HELPER_FLAGS_4(VAVGSH, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) +DEF_HELPER_FLAGS_4(VAVGSW, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) DEF_HELPER_4(vcmpeqfp, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgefp, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtfp, void, env, avr, avr, avr) @@ -175,15 +187,19 @@ DEF_HELPER_FLAGS_3(VMULOSW, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(VMULOUB, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(VMULOUH, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(VMULOUW, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VDIVSQ, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VDIVUQ, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VDIVESD, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VDIVEUD, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VDIVESQ, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VDIVEUQ, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMODSQ, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VMODUQ, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vslo, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vsro, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vsrv, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vslv, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_3(vaddcuw, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_2(vprtybw, TCG_CALL_NO_RWG, void, avr, avr) -DEF_HELPER_FLAGS_2(vprtybd, TCG_CALL_NO_RWG, void, avr, avr) -DEF_HELPER_FLAGS_2(vprtybq, TCG_CALL_NO_RWG, void, avr, avr) -DEF_HELPER_FLAGS_3(vsubcuw, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VPRTYBQ, TCG_CALL_NO_RWG, void, avr, avr, i32) DEF_HELPER_FLAGS_5(vaddsbs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) DEF_HELPER_FLAGS_5(vaddshs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) DEF_HELPER_FLAGS_5(vaddsws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) @@ -196,14 +212,14 @@ DEF_HELPER_FLAGS_5(vadduws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) DEF_HELPER_FLAGS_5(vsububs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) DEF_HELPER_FLAGS_5(vsubuhs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) DEF_HELPER_FLAGS_5(vsubuws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_3(vadduqm, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_4(vaddecuq, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) -DEF_HELPER_FLAGS_4(vaddeuqm, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) -DEF_HELPER_FLAGS_3(vaddcuq, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_3(vsubuqm, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_4(vsubecuq, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) -DEF_HELPER_FLAGS_4(vsubeuqm, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) -DEF_HELPER_FLAGS_3(vsubcuq, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VADDUQM, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_4(VADDECUQ, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) +DEF_HELPER_FLAGS_4(VADDEUQM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) +DEF_HELPER_FLAGS_3(VADDCUQ, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VSUBUQM, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_4(VSUBECUQ, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) +DEF_HELPER_FLAGS_4(VSUBEUQM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) +DEF_HELPER_FLAGS_3(VSUBCUQ, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_4(vsldoi, TCG_CALL_NO_RWG, void, avr, avr, avr, i32) DEF_HELPER_FLAGS_3(vextractub, TCG_CALL_NO_RWG, void, avr, avr, i32) DEF_HELPER_FLAGS_3(vextractuh, TCG_CALL_NO_RWG, void, avr, avr, i32) @@ -217,8 +233,6 @@ DEF_HELPER_FLAGS_2(VSTRIBL, TCG_CALL_NO_RWG, i32, avr, avr) DEF_HELPER_FLAGS_2(VSTRIBR, TCG_CALL_NO_RWG, i32, avr, avr) DEF_HELPER_FLAGS_2(VSTRIHL, TCG_CALL_NO_RWG, i32, avr, avr) DEF_HELPER_FLAGS_2(VSTRIHR, TCG_CALL_NO_RWG, i32, avr, avr) -DEF_HELPER_FLAGS_2(vnegw, TCG_CALL_NO_RWG, void, avr, avr) -DEF_HELPER_FLAGS_2(vnegd, TCG_CALL_NO_RWG, void, avr, avr) DEF_HELPER_FLAGS_2(vupkhpx, TCG_CALL_NO_RWG, void, avr, avr) DEF_HELPER_FLAGS_2(vupklpx, TCG_CALL_NO_RWG, void, avr, avr) DEF_HELPER_FLAGS_2(vupkhsb, TCG_CALL_NO_RWG, void, avr, avr) @@ -244,13 +258,13 @@ DEF_HELPER_4(vpkuhum, void, env, avr, avr, avr) DEF_HELPER_4(vpkuwum, void, env, avr, avr, avr) DEF_HELPER_4(vpkudum, void, env, avr, avr, avr) DEF_HELPER_FLAGS_3(vpkpx, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_5(vmhaddshs, void, env, avr, avr, avr, avr) -DEF_HELPER_5(vmhraddshs, void, env, avr, avr, avr, avr) +DEF_HELPER_5(VMHADDSHS, void, env, avr, avr, avr, avr) +DEF_HELPER_5(VMHRADDSHS, void, env, avr, avr, avr, avr) DEF_HELPER_FLAGS_4(VMSUMUHM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) DEF_HELPER_5(VMSUMUHS, void, env, avr, avr, avr, avr) DEF_HELPER_FLAGS_4(VMSUMSHM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) DEF_HELPER_5(VMSUMSHS, void, env, avr, avr, avr, avr) -DEF_HELPER_FLAGS_4(vmladduhm, TCG_CALL_NO_RWG, void, avr, avr, avr, avr) +DEF_HELPER_FLAGS_5(VMLADDUHM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) DEF_HELPER_FLAGS_2(mtvscr, TCG_CALL_NO_RWG, void, env, i32) DEF_HELPER_FLAGS_1(mfvscr, TCG_CALL_NO_RWG, i32, env) DEF_HELPER_3(lvebx, void, env, avr, tl) @@ -310,7 +324,7 @@ DEF_HELPER_FLAGS_3(vbpermq, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vpmsumb, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vpmsumh, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_3(vpmsumw, TCG_CALL_NO_RWG, void, avr, avr, avr) -DEF_HELPER_FLAGS_3(vpmsumd, TCG_CALL_NO_RWG, void, avr, avr, avr) +DEF_HELPER_FLAGS_3(VPMSUMD, TCG_CALL_NO_RWG, void, avr, avr, avr) DEF_HELPER_FLAGS_2(vextublx, TCG_CALL_NO_RWG, tl, tl, avr) DEF_HELPER_FLAGS_2(vextuhlx, TCG_CALL_NO_RWG, tl, tl, avr) DEF_HELPER_FLAGS_2(vextuwlx, TCG_CALL_NO_RWG, tl, tl, avr) @@ -409,9 +423,9 @@ DEF_HELPER_3(xscvuxdsp, void, env, vsr, vsr) DEF_HELPER_3(xscvsxdsp, void, env, vsr, vsr) DEF_HELPER_4(xscvudqp, void, env, i32, vsr, vsr) DEF_HELPER_3(xscvuxddp, void, env, vsr, vsr) -DEF_HELPER_3(xststdcsp, void, env, i32, vsr) -DEF_HELPER_2(xststdcdp, void, env, i32) -DEF_HELPER_2(xststdcqp, void, env, i32) +DEF_HELPER_4(XSTSTDCSP, void, env, i32, i32, vsr) +DEF_HELPER_4(XSTSTDCDP, void, env, i32, i32, vsr) +DEF_HELPER_4(XSTSTDCQP, void, env, i32, i32, vsr) DEF_HELPER_3(xsrdpi, void, env, vsr, vsr) DEF_HELPER_3(xsrdpic, void, env, vsr, vsr) DEF_HELPER_3(xsrdpim, void, env, vsr, vsr) @@ -509,8 +523,8 @@ DEF_HELPER_3(xvcvsxdsp, void, env, vsr, vsr) DEF_HELPER_3(xvcvuxdsp, void, env, vsr, vsr) DEF_HELPER_3(xvcvsxwsp, void, env, vsr, vsr) DEF_HELPER_3(xvcvuxwsp, void, env, vsr, vsr) -DEF_HELPER_2(xvtstdcsp, void, env, i32) -DEF_HELPER_2(xvtstdcdp, void, env, i32) +DEF_HELPER_FLAGS_4(XVTSTDCSP, TCG_CALL_NO_RWG, void, vsr, vsr, i64, i32) +DEF_HELPER_FLAGS_4(XVTSTDCDP, TCG_CALL_NO_RWG, void, vsr, vsr, i64, i32) DEF_HELPER_3(xvrspi, void, env, vsr, vsr) DEF_HELPER_3(xvrspic, void, env, vsr, vsr) DEF_HELPER_3(xvrspim, void, env, vsr, vsr) @@ -664,13 +678,16 @@ DEF_HELPER_FLAGS_1(tlbia, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(tlbie, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(tlbiva, TCG_CALL_NO_RWG, void, env, tl) #if defined(TARGET_PPC64) -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_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) +DEF_HELPER_FLAGS_4(tlbie_isa300, TCG_CALL_NO_WG, void, \ + env, tl, tl, i32) +DEF_HELPER_FLAGS_3(SLBMTE, TCG_CALL_NO_RWG, void, env, tl, tl) +DEF_HELPER_2(SLBMFEE, tl, env, tl) +DEF_HELPER_2(SLBMFEV, tl, env, tl) +DEF_HELPER_2(SLBFEE, tl, env, tl) +DEF_HELPER_FLAGS_2(SLBIA, TCG_CALL_NO_RWG, void, env, i32) +DEF_HELPER_FLAGS_3(SLBIAG, TCG_CALL_NO_RWG, void, env, tl, 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 DEF_HELPER_FLAGS_2(load_sr, TCG_CALL_NO_RWG, tl, env, tl) DEF_HELPER_FLAGS_3(store_sr, TCG_CALL_NO_RWG, void, env, tl, tl) @@ -684,13 +701,16 @@ DEF_HELPER_2(book3s_msgclr, void, env, tl) DEF_HELPER_4(dlmzb, tl, env, tl, tl, i32) #if !defined(CONFIG_USER_ONLY) DEF_HELPER_2(rac, tl, env, tl) -#endif DEF_HELPER_2(load_dcr, tl, env, tl) DEF_HELPER_3(store_dcr, void, env, tl, tl) +#endif DEF_HELPER_2(load_dump_spr, void, env, i32) DEF_HELPER_2(store_dump_spr, void, env, i32) +DEF_HELPER_3(spr_core_write_generic, void, env, i32, tl) +DEF_HELPER_3(spr_write_CTRL, void, env, i32, tl) + DEF_HELPER_4(fscr_facility_check, void, env, i32, i32, i32) DEF_HELPER_4(msr_facility_check, void, env, i32, i32, i32) DEF_HELPER_FLAGS_1(load_tbl, TCG_CALL_NO_RWG, tl, env) @@ -707,6 +727,8 @@ 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) +DEF_HELPER_1(load_tfmr, tl, env) +DEF_HELPER_2(store_tfmr, void, env, tl) #endif DEF_HELPER_2(store_sdr1, void, env, tl) DEF_HELPER_2(store_pidr, void, env, tl) @@ -797,14 +819,3 @@ DEF_HELPER_4(DSCLIQ, void, env, fprp, fprp, i32) DEF_HELPER_1(tbegin, void, env) DEF_HELPER_FLAGS_1(fixup_thrm, TCG_CALL_NO_RWG, void, env) - -#ifdef TARGET_PPC64 -DEF_HELPER_FLAGS_3(lq_le_parallel, TCG_CALL_NO_WG, i64, env, tl, i32) -DEF_HELPER_FLAGS_3(lq_be_parallel, TCG_CALL_NO_WG, i64, env, tl, i32) -DEF_HELPER_FLAGS_5(stq_le_parallel, TCG_CALL_NO_WG, - void, env, tl, i64, i64, i32) -DEF_HELPER_FLAGS_5(stq_be_parallel, TCG_CALL_NO_WG, - void, env, tl, i64, i64, i32) -DEF_HELPER_5(stqcx_le_parallel, i32, env, tl, i64, i64, i32) -DEF_HELPER_5(stqcx_be_parallel, i32, env, tl, i64, i64, i32) -#endif diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 12235ea2e9..25258986e3 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -22,6 +22,7 @@ #include "qemu/main-loop.h" #include "exec/exec-all.h" #include "sysemu/kvm.h" +#include "sysemu/tcg.h" #include "helper_regs.h" #include "power8-pmu.h" #include "cpu-models.h" @@ -46,6 +47,48 @@ void hreg_swap_gpr_tgpr(CPUPPCState *env) env->tgpr[3] = tmp; } +static uint32_t hreg_compute_pmu_hflags_value(CPUPPCState *env) +{ + uint32_t hflags = 0; + +#if defined(TARGET_PPC64) + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC0) { + hflags |= 1 << HFLAGS_PMCC0; + } + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) { + hflags |= 1 << HFLAGS_PMCC1; + } + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE) { + hflags |= 1 << HFLAGS_PMCJCE; + } + +#ifndef CONFIG_USER_ONLY + if (env->pmc_ins_cnt) { + hflags |= 1 << HFLAGS_INSN_CNT; + } + if (env->pmc_ins_cnt & 0x1e) { + hflags |= 1 << HFLAGS_PMC_OTHER; + } +#endif +#endif + + return hflags; +} + +/* Mask of all PMU hflags */ +static uint32_t hreg_compute_pmu_hflags_mask(CPUPPCState *env) +{ + uint32_t hflags_mask = 0; +#if defined(TARGET_PPC64) + hflags_mask |= 1 << HFLAGS_PMCC0; + hflags_mask |= 1 << HFLAGS_PMCC1; + hflags_mask |= 1 << HFLAGS_PMCJCE; + hflags_mask |= 1 << HFLAGS_INSN_CNT; + hflags_mask |= 1 << HFLAGS_PMC_OTHER; +#endif + return hflags_mask; +} + static uint32_t hreg_compute_hflags_value(CPUPPCState *env) { target_ulong msr = env->msr; @@ -103,24 +146,12 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) if (env->spr[SPR_LPCR] & LPCR_HR) { hflags |= 1 << HFLAGS_HR; } - if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC0) { - hflags |= 1 << HFLAGS_PMCC0; - } - if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) { - hflags |= 1 << HFLAGS_PMCC1; - } #ifndef CONFIG_USER_ONLY if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) { hflags |= 1 << HFLAGS_HV; } -#if defined(TARGET_PPC64) - if (env->pmc_ins_cnt) { - hflags |= 1 << HFLAGS_INSN_CNT; - } -#endif - /* * This is our encoding for server processors. The architecture * specifies that there is no such thing as userspace with @@ -165,6 +196,8 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) hflags |= dmmu_idx << HFLAGS_DMMU_IDX; #endif + hflags |= hreg_compute_pmu_hflags_value(env); + return hflags | (msr & msr_mask); } @@ -173,9 +206,20 @@ void hreg_compute_hflags(CPUPPCState *env) env->hflags = hreg_compute_hflags_value(env); } +/* + * This can be used as a lighter-weight alternative to hreg_compute_hflags + * when PMU MMCR0 or pmc_ins_cnt changes. pmc_ins_cnt is changed by + * pmu_update_summaries. + */ +void hreg_update_pmu_hflags(CPUPPCState *env) +{ + env->hflags &= ~hreg_compute_pmu_hflags_mask(env); + env->hflags |= hreg_compute_pmu_hflags_value(env); +} + #ifdef CONFIG_DEBUG_TCG -void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) +void cpu_get_tb_cpu_state(CPUPPCState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { uint32_t hflags_current = env->hflags; uint32_t hflags_rebuilt; @@ -197,17 +241,10 @@ void cpu_interrupt_exittb(CPUState *cs) { /* * We don't need to worry about translation blocks - * when running with KVM. + * unless running with TCG. */ - if (kvm_enabled()) { - return; - } - - if (!qemu_mutex_iothread_locked()) { - qemu_mutex_lock_iothread(); - cpu_interrupt(cs, CPU_INTERRUPT_EXITTB); - qemu_mutex_unlock_iothread(); - } else { + if (tcg_enabled()) { + BQL_LOCK_GUARD(); cpu_interrupt(cs, CPU_INTERRUPT_EXITTB); } } @@ -227,6 +264,11 @@ int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv) value &= ~MSR_HVB; value |= env->msr & MSR_HVB; } + /* Attempt to modify MSR[ME] in guest state is ignored */ + if (is_book3s_arch2x(env) && !(env->msr & MSR_HVB)) { + value &= ~(1 << MSR_ME); + value |= env->msr & (1 << MSR_ME); + } if ((value ^ env->msr) & (R_MSR_IR_MASK | R_MSR_DR_MASK)) { cpu_interrupt_exittb(cs); } @@ -260,6 +302,8 @@ int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv) env->msr = value; hreg_compute_hflags(env); #if !defined(CONFIG_USER_ONLY) + ppc_maybe_interrupt(env); + if (unlikely(FIELD_EX64(env->msr, MSR, POW))) { if (!env->pending_interrupts && (*env->check_pow)(env)) { cs->halted = 1; @@ -271,7 +315,7 @@ int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv) return excp; } -#ifdef CONFIG_SOFTMMU +#ifndef CONFIG_USER_ONLY void store_40x_sler(CPUPPCState *env, uint32_t val) { /* XXX: TO BE FIXED */ @@ -281,9 +325,7 @@ void store_40x_sler(CPUPPCState *env, uint32_t val) } env->spr[SPR_405_SLER] = val; } -#endif /* CONFIG_SOFTMMU */ -#ifndef CONFIG_USER_ONLY void check_tlb_flush(CPUPPCState *env, bool global) { CPUState *cs = env_cpu(env); @@ -302,7 +344,7 @@ void check_tlb_flush(CPUPPCState *env, bool global) tlb_flush(cs); } } -#endif +#endif /* !CONFIG_USER_ONLY */ /** * _spr_register @@ -423,22 +465,41 @@ void register_generic_sprs(PowerPCCPU *cpu) } /* Time base */ - spr_register(env, SPR_VTBL, "TBL", +#if defined(TARGET_PPC64) + spr_register(env, SPR_TBL, "TB", +#else + spr_register(env, SPR_TBL, "TBL", +#endif &spr_read_tbl, SPR_NOACCESS, &spr_read_tbl, SPR_NOACCESS, 0x00000000); - spr_register(env, SPR_TBL, "TBL", - &spr_read_tbl, SPR_NOACCESS, - &spr_read_tbl, &spr_write_tbl, - 0x00000000); - spr_register(env, SPR_VTBU, "TBU", + spr_register(env, SPR_TBU, "TBU", &spr_read_tbu, SPR_NOACCESS, &spr_read_tbu, SPR_NOACCESS, 0x00000000); - spr_register(env, SPR_TBU, "TBU", - &spr_read_tbu, SPR_NOACCESS, - &spr_read_tbu, &spr_write_tbu, - 0x00000000); +#ifndef CONFIG_USER_ONLY + if (env->has_hv_mode) { + spr_register_hv(env, SPR_WR_TBL, "TBL", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, &spr_write_tbl, + 0x00000000); + spr_register_hv(env, SPR_WR_TBU, "TBU", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, &spr_write_tbu, + 0x00000000); + } else { + spr_register(env, SPR_WR_TBL, "TBL", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, &spr_write_tbl, + 0x00000000); + spr_register(env, SPR_WR_TBU, "TBU", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, &spr_write_tbu, + 0x00000000); + } +#endif } void register_non_embedded_sprs(CPUPPCState *env) @@ -446,14 +507,14 @@ void register_non_embedded_sprs(CPUPPCState *env) /* Exception processing */ spr_register_kvm(env, SPR_DSISR, "DSISR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic32, KVM_REG_PPC_DSISR, 0x00000000); spr_register_kvm(env, SPR_DAR, "DAR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_DAR, 0x00000000); /* Timer */ - spr_register(env, SPR_DECR, "DECR", + spr_register(env, SPR_DECR, "DEC", SPR_NOACCESS, SPR_NOACCESS, &spr_read_decr, &spr_write_decr, 0x00000000); diff --git a/target/ppc/helper_regs.h b/target/ppc/helper_regs.h index 42f26870b9..8196c1346d 100644 --- a/target/ppc/helper_regs.h +++ b/target/ppc/helper_regs.h @@ -22,6 +22,7 @@ void hreg_swap_gpr_tgpr(CPUPPCState *env); void hreg_compute_hflags(CPUPPCState *env); +void hreg_update_pmu_hflags(CPUPPCState *env); void cpu_interrupt_exittb(CPUState *cs); int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv); diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 18a94fa3b5..eada59f59f 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -20,12 +20,15 @@ &A frt fra frb frc rc:bool @A ...... frt:5 fra:5 frb:5 frc:5 ..... rc:1 &A +&A_tb frt frb rc:bool +@A_tb ...... frt:5 ..... frb:5 ..... ..... rc:1 &A_tb + &D rt ra si:int64_t -@D ...... rt:5 ra:5 si:s16 &D +@D ...... rt:5 ra:5 si:s16 &D &D_bf bf l:bool ra imm -@D_bfs ...... bf:3 - l:1 ra:5 imm:s16 &D_bf -@D_bfu ...... bf:3 - l:1 ra:5 imm:16 &D_bf +@D_bfs ...... bf:3 . l:1 ra:5 imm:s16 &D_bf +@D_bfu ...... bf:3 . l:1 ra:5 imm:16 &D_bf %dq_si 4:s12 !function=times_16 %dq_rtp 22:4 !function=times_2 @@ -38,7 +41,7 @@ @DQ_TSXP ...... ..... ra:5 ............ .... &D si=%dq_si rt=%rt_tsxp %ds_si 2:s14 !function=times_4 -@DS ...... rt:5 ra:5 .............. .. &D si=%ds_si +@DS ...... rt:5 ra:5 .............. .. &D si=%ds_si %ds_rtp 22:4 !function=times_2 @DS_rtp ...... ....0 ra:5 .............. .. &D rt=%ds_rtp si=%ds_si @@ -49,10 +52,10 @@ &DX rt d %dx_d 6:s10 16:5 0:1 -@DX ...... rt:5 ..... .......... ..... . &DX d=%dx_d +@DX ...... rt:5 ..... .......... ..... . &DX d=%dx_d &VA vrt vra vrb rc -@VA ...... vrt:5 vra:5 vrb:5 rc:5 ...... &VA +@VA ...... vrt:5 vra:5 vrb:5 rc:5 ...... &VA &VC vrt vra vrb rc:bool @VC ...... vrt:5 vra:5 vrb:5 rc:1 .......... &VC @@ -61,7 +64,7 @@ @VN ...... vrt:5 vra:5 vrb:5 .. sh:3 ...... &VN &VX vrt vra vrb -@VX ...... vrt:5 vra:5 vrb:5 .......... . &VX +@VX ...... vrt:5 vra:5 vrb:5 .......... . &VX &VX_bf bf vra vrb @VX_bf ...... bf:3 .. vra:5 vrb:5 ........... &VX_bf @@ -76,17 +79,20 @@ @VX_tb_rc ...... vrt:5 ..... vrb:5 rc:1 .......... &VX_tb_rc &VX_uim4 vrt uim vrb -@VX_uim4 ...... vrt:5 . uim:4 vrb:5 ........... &VX_uim4 +@VX_uim4 ...... vrt:5 . uim:4 vrb:5 ........... &VX_uim4 &VX_tb vrt vrb -@VX_tb ...... vrt:5 ..... vrb:5 ........... &VX_tb +@VX_tb ...... vrt:5 ..... vrb:5 ........... &VX_tb &X rt ra rb -@X ...... rt:5 ra:5 rb:5 .......... . &X +@X ...... rt:5 ra:5 rb:5 .......... . &X &X_rc rt ra rb rc:bool @X_rc ...... rt:5 ra:5 rb:5 .......... rc:1 &X_rc +&X_sa rs ra +@X_sa ...... rs:5 ra:5 ..... .......... . &X_sa + %x_frtp 22:4 !function=times_2 %x_frap 17:4 !function=times_2 %x_frbp 12:4 !function=times_2 @@ -94,9 +100,15 @@ @X_tp_a_bp_rc ...... ....0 ra:5 ....0 .......... rc:1 &X_rc rt=%x_frtp rb=%x_frbp +&X_t rt +@X_t ...... rt:5 ..... ..... .......... . &X_t + &X_tb rt rb @X_tb ...... rt:5 ..... rb:5 .......... . &X_tb +&X_t_rc rt rc:bool +@X_t_rc ...... rt:5 ..... ..... .......... rc:1 &X_t_rc + &X_tb_rc rt rb rc:bool @X_tb_rc ...... rt:5 ..... rb:5 .......... rc:1 &X_tb_rc @@ -107,7 +119,7 @@ @X_t_bp_rc ...... rt:5 ..... ....0 .......... rc:1 &X_tb_rc rb=%x_frbp &X_bi rt bi -@X_bi ...... rt:5 bi:5 ----- .......... - &X_bi +@X_bi ...... rt:5 bi:5 ..... .......... . &X_bi &X_bf bf ra rb @X_bf ...... bf:3 .. ra:5 rb:5 .......... . &X_bf @@ -122,7 +134,13 @@ @X_bf_uim_bp ...... bf:3 . uim:6 ....0 .......... . &X_bf_uim rb=%x_frbp &X_bfl bf l:bool ra rb -@X_bfl ...... bf:3 - l:1 ra:5 rb:5 ..........- &X_bfl +@X_bfl ...... bf:3 . l:1 ra:5 rb:5 .......... . &X_bfl + +&X_imm2 rt imm +@X_imm2 ...... rt:5 ..... ... imm:2 .......... . &X_imm2 + +&X_imm3 rt imm +@X_imm3 ...... rt:5 ..... .. imm:3 .......... . &X_imm3 %x_xt 0:1 21:5 &X_imm5 xt imm:uint8_t vrb @@ -131,6 +149,15 @@ &X_imm8 xt imm:uint8_t @X_imm8 ...... ..... .. imm:8 .......... . &X_imm8 xt=%x_xt +&X_ih ih:uint8_t +@X_ih ...... .. ih:3 ..... ..... .......... . &X_ih + +&X_rb rb +@X_rb ...... ..... ..... rb:5 .......... . &X_rb + +&X_rs_l rs l:bool +@X_rs_l ...... rs:5 .... l:1 ..... .......... . &X_rs_l + &X_uim5 xt uim:uint8_t @X_uim5 ...... ..... ..... uim:5 .......... . &X_uim5 xt=%x_xt @@ -148,6 +175,9 @@ @X_TSX ...... ..... ra:5 rb:5 .......... . &X rt=%x_rt_tsx @X_TSXP ...... ..... ra:5 rb:5 .......... . &X rt=%rt_tsxp +%x_dw 0:1 21:5 !function=dw_compose_ea +@X_DW ...... ..... ra:5 rb:5 .......... . &X rt=%x_dw + &X_frtp_vrb frtp vrb @X_frtp_vrb ...... ....0 ..... vrb:5 .......... . &X_frtp_vrb frtp=%x_frtp @@ -157,6 +187,12 @@ &X_a ra @X_a ...... ra:3 .. ..... ..... .......... . &X_a +&XO rt ra rb oe:bool rc:bool +@XO ...... rt:5 ra:5 rb:5 oe:1 ......... rc:1 &XO + +&XO_ta rt ra oe:bool rc:bool +@XO_ta ...... rt:5 ra:5 ..... oe:1 ......... rc:1 &XO_ta + %xx_xt 0:1 21:5 %xx_xb 1:1 11:5 %xx_xa 2:1 16:5 @@ -169,6 +205,12 @@ @XX2_uim4 ...... ..... . uim:4 ..... ......... .. &XX2_uim xt=%xx_xt xb=%xx_xb +%xx_uim7 6:1 2:1 16:5 +@XX2_uim7 ...... ..... ..... ..... .... . ... . .. &XX2_uim xt=%xx_xt xb=%xx_xb uim=%xx_uim7 + +&XX2_bf_uim bf xb uim +@XX2_bf_uim ...... bf:3 uim:7 ..... ......... . . &XX2_bf_uim + &XX2_bf_xb bf xb @XX2_bf_xb ...... bf:3 .. ..... ..... ......... . . &XX2_bf_xb xb=%xx_xb @@ -286,10 +328,30 @@ CMPLI 001010 ... - . ..... ................ @D_bfu ### Fixed-Point Arithmetic Instructions +ADD 011111 ..... ..... ..... . 100001010 . @XO +ADDC 011111 ..... ..... ..... . 000001010 . @XO +ADDE 011111 ..... ..... ..... . 010001010 . @XO + +# ADDEX is Z23-form, with CY=0; all other values for CY are reserved. +# This works out the same as X-form. +ADDEX 011111 ..... ..... ..... 00 10101010 - @X + ADDI 001110 ..... ..... ................ @D ADDIS 001111 ..... ..... ................ @D +ADDIC 001100 ..... ..... ................ @D +ADDIC_ 001101 ..... ..... ................ @D ADDPCIS 010011 ..... ..... .......... 00010 . @DX +ADDME 011111 ..... ..... ----- . 011101010 . @XO_ta +ADDZE 011111 ..... ..... ----- . 011001010 . @XO_ta + +SUBF 011111 ..... ..... ..... . 000101000 . @XO +SUBFIC 001000 ..... ..... ................ @D +SUBFC 011111 ..... ..... ..... . 000001000 . @XO +SUBFE 011111 ..... ..... ..... . 010001000 . @XO + +SUBFME 011111 ..... ..... ----- . 011101000 . @XO_ta +SUBFZE 011111 ..... ..... ----- . 011001000 . @XO_ta ## Fixed-Point Logical Instructions @@ -299,6 +361,19 @@ CNTTZDM 011111 ..... ..... ..... 1000111011 - @X PDEPD 011111 ..... ..... ..... 0010011100 - @X PEXTD 011111 ..... ..... ..... 0010111100 - @X +# Fixed-Point Hash Instructions + +HASHST 011111 ..... ..... ..... 1011010010 . @X_DW +HASHCHK 011111 ..... ..... ..... 1011110010 . @X_DW +HASHSTP 011111 ..... ..... ..... 1010010010 . @X_DW +HASHCHKP 011111 ..... ..... ..... 1010110010 . @X_DW + +## BCD Assist + +ADDG6S 011111 ..... ..... ..... - 001001010 - @X +CDTBCD 011111 ..... ..... ----- 0100011010 - @X_sa +CBCDTD 011111 ..... ..... ----- 0100111010 - @X_sa + ### Float-Point Load Instructions LFS 110000 ..... ..... ................ @D @@ -323,6 +398,11 @@ STFDU 110111 ..... ...... ............... @D STFDX 011111 ..... ...... .... 1011010111 - @X STFDUX 011111 ..... ...... .... 1011110111 - @X +### Floating-Point Arithmetic Instructions + +FSQRT 111111 ..... ----- ..... ----- 10110 . @A_tb +FSQRTS 111011 ..... ----- ..... ----- 10110 . @A_tb + ### Floating-Point Select Instruction FSEL 111111 ..... ..... ..... ..... 10111 . @A @@ -334,6 +414,22 @@ SETBCR 011111 ..... ..... ----- 0110100000 - @X_bi SETNBC 011111 ..... ..... ----- 0111000000 - @X_bi SETNBCR 011111 ..... ..... ----- 0111100000 - @X_bi +### Move To/From FPSCR + +{ + # Before Power ISA v3.0, MFFS bits 11~15 were reserved and should be ignored + MFFS_ISA207 111111 ..... ----- ----- 1001000111 . @X_t_rc + [ + MFFS 111111 ..... 00000 ----- 1001000111 . @X_t_rc + MFFSCE 111111 ..... 00001 ----- 1001000111 - @X_t + MFFSCRN 111111 ..... 10110 ..... 1001000111 - @X_tb + MFFSCDRN 111111 ..... 10100 ..... 1001000111 - @X_tb + MFFSCRNI 111111 ..... 10111 ---.. 1001000111 - @X_imm2 + MFFSCDRNI 111111 ..... 10101 --... 1001000111 - @X_imm3 + MFFSL 111111 ..... 11000 ----- 1001000111 - @X_t + ] +} + ### Decimal Floating-Point Arithmetic Instructions DADD 111011 ..... ..... ..... 0000000010 . @X_rc @@ -426,6 +522,10 @@ DSCLIQ 111111 ..... ..... ...... 001000010 . @Z22_tap_sh_rc DSCRI 111011 ..... ..... ...... 001100010 . @Z22_ta_sh_rc DSCRIQ 111111 ..... ..... ...... 001100010 . @Z22_tap_sh_rc +## Vector Exclusive-OR-based Instructions + +VPMSUMD 000100 ..... ..... ..... 10011001000 @VX + ## Vector Integer Instructions VCMPEQUB 000100 ..... ..... ..... . 0000000110 @VC @@ -457,6 +557,21 @@ VCMPNEZW 000100 ..... ..... ..... . 0110000111 @VC VCMPSQ 000100 ... -- ..... ..... 00101000001 @VX_bf VCMPUQ 000100 ... -- ..... ..... 00100000001 @VX_bf +## Vector Integer Average Instructions + +VAVGSB 000100 ..... ..... ..... 10100000010 @VX +VAVGSH 000100 ..... ..... ..... 10101000010 @VX +VAVGSW 000100 ..... ..... ..... 10110000010 @VX +VAVGUB 000100 ..... ..... ..... 10000000010 @VX +VAVGUH 000100 ..... ..... ..... 10001000010 @VX +VAVGUW 000100 ..... ..... ..... 10010000010 @VX + +## Vector Integer Absolute Difference Instructions + +VABSDUB 000100 ..... ..... ..... 10000000011 @VX +VABSDUH 000100 ..... ..... ..... 10001000011 @VX +VABSDUW 000100 ..... ..... ..... 10010000011 @VX + ## Vector Bit Manipulation Instruction VGNB 000100 ..... -- ... ..... 10011001100 @VX_n @@ -467,6 +582,10 @@ VCTZDM 000100 ..... ..... ..... 11111000100 @VX VPDEPD 000100 ..... ..... ..... 10111001101 @VX VPEXTD 000100 ..... ..... ..... 10110001101 @VX +VPRTYBD 000100 ..... 01001 ..... 11000000010 @VX_tb +VPRTYBQ 000100 ..... 01010 ..... 11000000010 @VX_tb +VPRTYBW 000100 ..... 01000 ..... 11000000010 @VX_tb + ## Vector Permute and Formatting Instruction VEXTDUBVLX 000100 ..... ..... ..... ..... 011000 @VA @@ -546,6 +665,20 @@ VRLQNM 000100 ..... ..... ..... 00101000101 @VX ## Vector Integer Arithmetic Instructions +VADDCUW 000100 ..... ..... ..... 00110000000 @VX +VADDCUQ 000100 ..... ..... ..... 00101000000 @VX +VADDUQM 000100 ..... ..... ..... 00100000000 @VX + +VADDEUQM 000100 ..... ..... ..... ..... 111100 @VA +VADDECUQ 000100 ..... ..... ..... ..... 111101 @VA + +VSUBCUW 000100 ..... ..... ..... 10110000000 @VX +VSUBCUQ 000100 ..... ..... ..... 10101000000 @VX +VSUBUQM 000100 ..... ..... ..... 10100000000 @VX + +VSUBECUQ 000100 ..... ..... ..... ..... 111111 @VA +VSUBEUQM 000100 ..... ..... ..... ..... 111110 @VA + VEXTSB2W 000100 ..... 10000 ..... 11000000010 @VX_tb VEXTSH2W 000100 ..... 10001 ..... 11000000010 @VX_tb VEXTSB2D 000100 ..... 11000 ..... 11000000010 @VX_tb @@ -553,6 +686,9 @@ VEXTSH2D 000100 ..... 11001 ..... 11000000010 @VX_tb VEXTSW2D 000100 ..... 11010 ..... 11000000010 @VX_tb VEXTSD2Q 000100 ..... 11011 ..... 11000000010 @VX_tb +VNEGD 000100 ..... 00111 ..... 11000000010 @VX_tb +VNEGW 000100 ..... 00110 ..... 11000000010 @VX_tb + ## Vector Mask Manipulation Instructions MTVSRBM 000100 ..... 10000 ..... 11001000010 @VX_tb @@ -619,6 +755,10 @@ VMSUMUHS 000100 ..... ..... ..... ..... 100111 @VA VMSUMCUD 000100 ..... ..... ..... ..... 010111 @VA VMSUMUDM 000100 ..... ..... ..... ..... 100011 @VA +VMLADDUHM 000100 ..... ..... ..... ..... 100010 @VA +VMHADDSHS 000100 ..... ..... ..... ..... 100000 @VA +VMHRADDSHS 000100 ..... ..... ..... ..... 100001 @VA + ## Vector String Instructions VSTRIBL 000100 ..... 00000 ..... . 0000001101 @VX_tb_rc @@ -652,6 +792,17 @@ STXVRHX 011111 ..... ..... ..... 0010101101 . @X_TSX STXVRWX 011111 ..... ..... ..... 0011001101 . @X_TSX STXVRDX 011111 ..... ..... ..... 0011101101 . @X_TSX +## VSX Vector Binary Floating-Point Sign Manipulation Instructions + +XVABSDP 111100 ..... 00000 ..... 111011001 .. @XX2 +XVABSSP 111100 ..... 00000 ..... 110011001 .. @XX2 +XVNABSDP 111100 ..... 00000 ..... 111101001 .. @XX2 +XVNABSSP 111100 ..... 00000 ..... 110101001 .. @XX2 +XVNEGDP 111100 ..... 00000 ..... 111111001 .. @XX2 +XVNEGSP 111100 ..... 00000 ..... 110111001 .. @XX2 +XVCPSGNDP 111100 ..... ..... ..... 11110000 ... @XX3 +XVCPSGNSP 111100 ..... ..... ..... 11010000 ... @XX3 + ## VSX Scalar Multiply-Add Instructions XSMADDADP 111100 ..... ..... ..... 00100001 . . . @XX3 @@ -735,6 +886,11 @@ XSCVSPDPN 111100 ..... ----- ..... 101001011 .. @XX2 ## VSX Binary Floating-Point Math Support Instructions XVXSIGSP 111100 ..... 01001 ..... 111011011 .. @XX2 +XVTSTDCDP 111100 ..... ..... ..... 1111 . 101 ... @XX2_uim7 +XVTSTDCSP 111100 ..... ..... ..... 1101 . 101 ... @XX2_uim7 +XSTSTDCSP 111100 ... ....... ..... 100101010 . - @XX2_bf_uim xb=%xx_xb +XSTSTDCDP 111100 ... ....... ..... 101101010 . - @XX2_bf_uim xb=%xx_xb +XSTSTDCQP 111111 ... ....... xb:5 1011000100 - @XX2_bf_uim ## VSX Vector Test Least-Significant Bit by Byte Instruction @@ -786,3 +942,59 @@ XVF64GERPP 111011 ... -- .... 0 ..... 00111010 ..- @XX3_at xa=%xx_xa_pair XVF64GERPN 111011 ... -- .... 0 ..... 10111010 ..- @XX3_at xa=%xx_xa_pair XVF64GERNP 111011 ... -- .... 0 ..... 01111010 ..- @XX3_at xa=%xx_xa_pair XVF64GERNN 111011 ... -- .... 0 ..... 11111010 ..- @XX3_at xa=%xx_xa_pair + +## Vector Division Instructions + +VDIVSW 000100 ..... ..... ..... 00110001011 @VX +VDIVUW 000100 ..... ..... ..... 00010001011 @VX +VDIVSD 000100 ..... ..... ..... 00111001011 @VX +VDIVUD 000100 ..... ..... ..... 00011001011 @VX +VDIVSQ 000100 ..... ..... ..... 00100001011 @VX +VDIVUQ 000100 ..... ..... ..... 00000001011 @VX + +VDIVESW 000100 ..... ..... ..... 01110001011 @VX +VDIVEUW 000100 ..... ..... ..... 01010001011 @VX +VDIVESD 000100 ..... ..... ..... 01111001011 @VX +VDIVEUD 000100 ..... ..... ..... 01011001011 @VX +VDIVESQ 000100 ..... ..... ..... 01100001011 @VX +VDIVEUQ 000100 ..... ..... ..... 01000001011 @VX + +VMODSW 000100 ..... ..... ..... 11110001011 @VX +VMODUW 000100 ..... ..... ..... 11010001011 @VX +VMODSD 000100 ..... ..... ..... 11111001011 @VX +VMODUD 000100 ..... ..... ..... 11011001011 @VX +VMODSQ 000100 ..... ..... ..... 11100001011 @VX +VMODUQ 000100 ..... ..... ..... 11000001011 @VX + +## SLB Management Instructions + +SLBIE 011111 ----- ----- ..... 0110110010 - @X_rb +SLBIEG 011111 ..... ----- ..... 0111010010 - @X_tb + +SLBIA 011111 --... ----- ----- 0111110010 - @X_ih +SLBIAG 011111 ..... ----. ----- 1101010010 - @X_rs_l + +SLBMTE 011111 ..... ----- ..... 0110010010 - @X_tb + +SLBMFEV 011111 ..... ----- ..... 1101010011 - @X_tb +SLBMFEE 011111 ..... ----- ..... 1110010011 - @X_tb + +SLBFEE 011111 ..... ----- ..... 1111010011 1 @X_tb + +SLBSYNC 011111 ----- ----- ----- 0101010010 - + +## TLB Management Instructions + +&X_tlbie rb rs ric prs:bool r:bool +@X_tlbie ...... rs:5 - ric:2 prs:1 r:1 rb:5 .......... - &X_tlbie + +TLBIE 011111 ..... - .. . . ..... 0100110010 - @X_tlbie +TLBIEL 011111 ..... - .. . . ..... 0100010010 - @X_tlbie + +# Processor Control Instructions + +MSGCLR 011111 ----- ----- ..... 0011101110 - @X_rb +MSGSND 011111 ----- ----- ..... 0011001110 - @X_rb +MSGCLRP 011111 ----- ----- ..... 0010101110 - @X_rb +MSGSNDP 011111 ----- ----- ..... 0010001110 - @X_rb +MSGSYNC 011111 ----- ----- ----- 1101110110 - diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 105b626d1b..0a5c3e78a4 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -21,10 +21,11 @@ #include "cpu.h" #include "internal.h" #include "qemu/host-utils.h" -#include "qemu/main-loop.h" #include "qemu/log.h" #include "exec/helper-proto.h" #include "crypto/aes.h" +#include "crypto/aes-round.h" +#include "crypto/clmul.h" #include "fpu/softfloat.h" #include "qapi/error.h" #include "qemu/guest-random.h" @@ -37,9 +38,9 @@ static inline void helper_update_ov_legacy(CPUPPCState *env, int ov) { if (unlikely(ov)) { - env->so = env->ov = 1; + env->so = env->ov = env->ov32 = 1; } else { - env->ov = 0; + env->ov = env->ov32 = 0; } } @@ -492,40 +493,8 @@ static inline void set_vscr_sat(CPUPPCState *env) env->vscr_sat.u32[0] = 1; } -void helper_vaddcuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(r->u32); i++) { - r->u32[i] = ~a->u32[i] < b->u32[i]; - } -} - -/* vprtybw */ -void helper_vprtybw(ppc_avr_t *r, ppc_avr_t *b) -{ - int i; - for (i = 0; i < ARRAY_SIZE(r->u32); i++) { - uint64_t res = b->u32[i] ^ (b->u32[i] >> 16); - res ^= res >> 8; - r->u32[i] = res & 1; - } -} - -/* vprtybd */ -void helper_vprtybd(ppc_avr_t *r, ppc_avr_t *b) -{ - int i; - for (i = 0; i < ARRAY_SIZE(r->u64); i++) { - uint64_t res = b->u64[i] ^ (b->u64[i] >> 32); - res ^= res >> 16; - res ^= res >> 8; - r->u64[i] = res & 1; - } -} - /* vprtybq */ -void helper_vprtybq(ppc_avr_t *r, ppc_avr_t *b) +void helper_VPRTYBQ(ppc_avr_t *r, ppc_avr_t *b, uint32_t v) { uint64_t res = b->u64[0] ^ b->u64[1]; res ^= res >> 32; @@ -602,29 +571,27 @@ VARITHSAT_UNSIGNED(w, u32, uint64_t, cvtsduw) #undef VARITHSAT_SIGNED #undef VARITHSAT_UNSIGNED -#define VAVG_DO(name, element, etype) \ - void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - int i; \ - \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - etype x = (etype)a->element[i] + (etype)b->element[i] + 1; \ - r->element[i] = x >> 1; \ - } \ +#define VAVG(name, element, etype) \ + void helper_##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t v)\ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + etype x = (etype)a->element[i] + (etype)b->element[i] + 1; \ + r->element[i] = x >> 1; \ + } \ } -#define VAVG(type, signed_element, signed_type, unsigned_element, \ - unsigned_type) \ - VAVG_DO(avgs##type, signed_element, signed_type) \ - VAVG_DO(avgu##type, unsigned_element, unsigned_type) -VAVG(b, s8, int16_t, u8, uint16_t) -VAVG(h, s16, int32_t, u16, uint32_t) -VAVG(w, s32, int64_t, u32, uint64_t) -#undef VAVG_DO +VAVG(VAVGSB, s8, int16_t) +VAVG(VAVGUB, u8, uint16_t) +VAVG(VAVGSH, s16, int32_t) +VAVG(VAVGUH, u16, uint32_t) +VAVG(VAVGSW, s32, int64_t) +VAVG(VAVGUW, u32, uint64_t) #undef VAVG -#define VABSDU_DO(name, element) \ -void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ +#define VABSDU(name, element) \ +void helper_##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t v)\ { \ int i; \ \ @@ -640,12 +607,9 @@ void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ * name - instruction mnemonic suffix (b: byte, h: halfword, w: word) * element - element type to access from vector */ -#define VABSDU(type, element) \ - VABSDU_DO(absdu##type, element) -VABSDU(b, u8) -VABSDU(h, u16) -VABSDU(w, u32) -#undef VABSDU_DO +VABSDU(VABSDUB, u8) +VABSDU(VABSDUH, u16) +VABSDU(VABSDUW, u32) #undef VABSDU #define VCF(suffix, cvt, element) \ @@ -789,7 +753,7 @@ static int64_t ger_rank8(uint32_t a, uint32_t b, uint32_t mask) int64_t psum = 0; for (int i = 0; i < 8; i++, mask >>= 1) { if (mask & 1) { - psum += sextract32(a, 4 * i, 4) * sextract32(b, 4 * i, 4); + psum += (int64_t)sextract32(a, 4 * i, 4) * sextract32(b, 4 * i, 4); } } return psum; @@ -811,7 +775,8 @@ static int64_t ger_rank2(uint32_t a, uint32_t b, uint32_t mask) int64_t psum = 0; for (int i = 0; i < 2; i++, mask >>= 1) { if (mask & 1) { - psum += sextract32(a, 16 * i, 16) * sextract32(b, 16 * i, 16); + psum += (int64_t)sextract32(a, 16 * i, 16) * + sextract32(b, 16 * i, 16); } } return psum; @@ -938,7 +903,7 @@ target_ulong helper_vctzlsbb(ppc_avr_t *r) return count; } -void helper_vmhaddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, +void helper_VMHADDSHS(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) { int sat = 0; @@ -956,7 +921,7 @@ void helper_vmhaddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, } } -void helper_vmhraddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, +void helper_VMHRADDSHS(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) { int sat = 0; @@ -973,7 +938,8 @@ void helper_vmhraddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, } } -void helper_vmladduhm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +void helper_VMLADDUHM(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c, + uint32_t v) { int i; @@ -1162,6 +1128,112 @@ void helper_XXPERMX(ppc_vsr_t *t, ppc_vsr_t *s0, ppc_vsr_t *s1, ppc_vsr_t *pcv, *t = tmp; } +void helper_VDIVSQ(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + Int128 neg1 = int128_makes64(-1); + Int128 int128_min = int128_make128(0, INT64_MIN); + if (likely(int128_nz(b->s128) && + (int128_ne(a->s128, int128_min) || int128_ne(b->s128, neg1)))) { + t->s128 = int128_divs(a->s128, b->s128); + } else { + t->s128 = a->s128; /* Undefined behavior */ + } +} + +void helper_VDIVUQ(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + if (int128_nz(b->s128)) { + t->s128 = int128_divu(a->s128, b->s128); + } else { + t->s128 = a->s128; /* Undefined behavior */ + } +} + +void helper_VDIVESD(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + int64_t high; + uint64_t low; + for (i = 0; i < 2; i++) { + high = a->s64[i]; + low = 0; + if (unlikely((high == INT64_MIN && b->s64[i] == -1) || !b->s64[i])) { + t->s64[i] = a->s64[i]; /* Undefined behavior */ + } else { + divs128(&low, &high, b->s64[i]); + t->s64[i] = low; + } + } +} + +void helper_VDIVEUD(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + uint64_t high, low; + for (i = 0; i < 2; i++) { + high = a->u64[i]; + low = 0; + if (unlikely(!b->u64[i])) { + t->u64[i] = a->u64[i]; /* Undefined behavior */ + } else { + divu128(&low, &high, b->u64[i]); + t->u64[i] = low; + } + } +} + +void helper_VDIVESQ(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + Int128 high, low; + Int128 int128_min = int128_make128(0, INT64_MIN); + Int128 neg1 = int128_makes64(-1); + + high = a->s128; + low = int128_zero(); + if (unlikely(!int128_nz(b->s128) || + (int128_eq(b->s128, neg1) && int128_eq(high, int128_min)))) { + t->s128 = a->s128; /* Undefined behavior */ + } else { + divs256(&low, &high, b->s128); + t->s128 = low; + } +} + +void helper_VDIVEUQ(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + Int128 high, low; + + high = a->s128; + low = int128_zero(); + if (unlikely(!int128_nz(b->s128))) { + t->s128 = a->s128; /* Undefined behavior */ + } else { + divu256(&low, &high, b->s128); + t->s128 = low; + } +} + +void helper_VMODSQ(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + Int128 neg1 = int128_makes64(-1); + Int128 int128_min = int128_make128(0, INT64_MIN); + if (likely(int128_nz(b->s128) && + (int128_ne(a->s128, int128_min) || int128_ne(b->s128, neg1)))) { + t->s128 = int128_rems(a->s128, b->s128); + } else { + t->s128 = int128_zero(); /* Undefined behavior */ + } +} + +void helper_VMODUQ(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b) +{ + if (likely(int128_nz(b->s128))) { + t->s128 = int128_remu(a->s128, b->s128); + } else { + t->s128 = int128_zero(); /* Undefined behavior */ + } +} + void helper_VPERM(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) { ppc_avr_t result; @@ -1307,14 +1379,13 @@ XXGENPCV(XXGENPCVDM, 8) #define VBPERMQ_INDEX(avr, i) ((avr)->u8[(i)]) #define VBPERMD_INDEX(i) (i) #define VBPERMQ_DW(index) (((index) & 0x40) != 0) -#define EXTRACT_BIT(avr, i, index) (extract64((avr)->u64[i], index, 1)) #else #define VBPERMQ_INDEX(avr, i) ((avr)->u8[15 - (i)]) #define VBPERMD_INDEX(i) (1 - i) #define VBPERMQ_DW(index) (((index) & 0x40) == 0) -#define EXTRACT_BIT(avr, i, index) \ - (extract64((avr)->u64[1 - i], 63 - index, 1)) #endif +#define EXTRACT_BIT(avr, i, index) \ + (extract64((avr)->VsrD(i), 63 - index, 1)) void helper_vbpermd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { @@ -1354,75 +1425,40 @@ void helper_vbpermq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) #undef VBPERMQ_INDEX #undef VBPERMQ_DW -#define PMSUM(name, srcfld, trgfld, trgtyp) \ -void helper_##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ -{ \ - int i, j; \ - trgtyp prod[sizeof(ppc_avr_t) / sizeof(a->srcfld[0])]; \ - \ - VECTOR_FOR_INORDER_I(i, srcfld) { \ - prod[i] = 0; \ - for (j = 0; j < sizeof(a->srcfld[0]) * 8; j++) { \ - if (a->srcfld[i] & (1ull << j)) { \ - prod[i] ^= ((trgtyp)b->srcfld[i] << j); \ - } \ - } \ - } \ - \ - VECTOR_FOR_INORDER_I(i, trgfld) { \ - r->trgfld[i] = prod[2 * i] ^ prod[2 * i + 1]; \ - } \ -} - -PMSUM(vpmsumb, u8, u16, uint16_t) -PMSUM(vpmsumh, u16, u32, uint32_t) -PMSUM(vpmsumw, u32, u64, uint64_t) - -void helper_vpmsumd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +/* + * There is no carry across the two doublewords, so their order does + * not matter. Nor is there partial overlap between registers. + */ +void helper_vpmsumb(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { - -#ifdef CONFIG_INT128 - int i, j; - __uint128_t prod[2]; - - VECTOR_FOR_INORDER_I(i, u64) { - prod[i] = 0; - for (j = 0; j < 64; j++) { - if (a->u64[i] & (1ull << j)) { - prod[i] ^= (((__uint128_t)b->u64[i]) << j); - } - } + for (int i = 0; i < 2; ++i) { + uint64_t aa = a->u64[i], bb = b->u64[i]; + r->u64[i] = clmul_8x4_even(aa, bb) ^ clmul_8x4_odd(aa, bb); } - - r->u128 = prod[0] ^ prod[1]; - -#else - int i, j; - ppc_avr_t prod[2]; - - VECTOR_FOR_INORDER_I(i, u64) { - prod[i].VsrD(1) = prod[i].VsrD(0) = 0; - for (j = 0; j < 64; j++) { - if (a->u64[i] & (1ull << j)) { - ppc_avr_t bshift; - if (j == 0) { - bshift.VsrD(0) = 0; - bshift.VsrD(1) = b->u64[i]; - } else { - bshift.VsrD(0) = b->u64[i] >> (64 - j); - bshift.VsrD(1) = b->u64[i] << j; - } - prod[i].VsrD(1) ^= bshift.VsrD(1); - prod[i].VsrD(0) ^= bshift.VsrD(0); - } - } - } - - r->VsrD(1) = prod[0].VsrD(1) ^ prod[1].VsrD(1); - r->VsrD(0) = prod[0].VsrD(0) ^ prod[1].VsrD(0); -#endif } +void helper_vpmsumh(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + for (int i = 0; i < 2; ++i) { + uint64_t aa = a->u64[i], bb = b->u64[i]; + r->u64[i] = clmul_16x2_even(aa, bb) ^ clmul_16x2_odd(aa, bb); + } +} + +void helper_vpmsumw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + for (int i = 0; i < 2; ++i) { + uint64_t aa = a->u64[i], bb = b->u64[i]; + r->u64[i] = clmul_32(aa, bb) ^ clmul_32(aa >> 32, bb >> 32); + } +} + +void helper_VPMSUMD(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + Int128 e = clmul_64(a->u64[0], b->u64[0]); + Int128 o = clmul_64(a->u64[1], b->u64[1]); + r->s128 = int128_xor(e, o); +} #if HOST_BIG_ENDIAN #define PKBIG 1 @@ -1858,18 +1894,6 @@ XXBLEND(W, 32) XXBLEND(D, 64) #undef XXBLEND -#define VNEG(name, element) \ -void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \ -{ \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - r->element[i] = -b->element[i]; \ - } \ -} -VNEG(vnegw, s32) -VNEG(vnegd, s64) -#undef VNEG - void helper_vsro(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { int sh = (b->VsrB(0xf) >> 3) & 0xf; @@ -1883,15 +1907,6 @@ void helper_vsro(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) #endif } -void helper_vsubcuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(r->u32); i++) { - r->u32[i] = a->u32[i] >= b->u32[i]; - } -} - void helper_vsumsws(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { int64_t t; @@ -2005,13 +2020,13 @@ void helper_vsum4ubs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) ppc_avr_t result; \ \ for (i = 0; i < ARRAY_SIZE(r->u32); i++) { \ - uint16_t e = b->u16[hi ? i : i + 4]; \ - uint8_t a = (e >> 15) ? 0xff : 0; \ - uint8_t r = (e >> 10) & 0x1f; \ - uint8_t g = (e >> 5) & 0x1f; \ - uint8_t b = e & 0x1f; \ + uint16_t _e = b->u16[hi ? i : i + 4]; \ + uint8_t _a = (_e >> 15) ? 0xff : 0; \ + uint8_t _r = (_e >> 10) & 0x1f; \ + uint8_t _g = (_e >> 5) & 0x1f; \ + uint8_t _b = _e & 0x1f; \ \ - result.u32[i] = (a << 24) | (r << 16) | (g << 8) | b; \ + result.u32[i] = (_a << 24) | (_r << 16) | (_g << 8) | _b; \ } \ *r = result; \ } @@ -2098,189 +2113,66 @@ VGENERIC_DO(popcntd, u64) #undef VGENERIC_DO -#if HOST_BIG_ENDIAN -#define QW_ONE { .u64 = { 0, 1 } } -#else -#define QW_ONE { .u64 = { 1, 0 } } -#endif - -#ifndef CONFIG_INT128 - -static inline void avr_qw_not(ppc_avr_t *t, ppc_avr_t a) +void helper_VADDUQM(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { - t->u64[0] = ~a.u64[0]; - t->u64[1] = ~a.u64[1]; + r->s128 = int128_add(a->s128, b->s128); } -static int avr_qw_cmpu(ppc_avr_t a, ppc_avr_t b) +void helper_VADDEUQM(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) { - if (a.VsrD(0) < b.VsrD(0)) { - return -1; - } else if (a.VsrD(0) > b.VsrD(0)) { - return 1; - } else if (a.VsrD(1) < b.VsrD(1)) { - return -1; - } else if (a.VsrD(1) > b.VsrD(1)) { - return 1; - } else { - return 0; - } + r->s128 = int128_add(int128_add(a->s128, b->s128), + int128_make64(int128_getlo(c->s128) & 1)); } -static void avr_qw_add(ppc_avr_t *t, ppc_avr_t a, ppc_avr_t b) +void helper_VADDCUQ(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { - t->VsrD(1) = a.VsrD(1) + b.VsrD(1); - t->VsrD(0) = a.VsrD(0) + b.VsrD(0) + - (~a.VsrD(1) < b.VsrD(1)); -} - -static int avr_qw_addc(ppc_avr_t *t, ppc_avr_t a, ppc_avr_t b) -{ - ppc_avr_t not_a; - t->VsrD(1) = a.VsrD(1) + b.VsrD(1); - t->VsrD(0) = a.VsrD(0) + b.VsrD(0) + - (~a.VsrD(1) < b.VsrD(1)); - avr_qw_not(¬_a, a); - return avr_qw_cmpu(not_a, b) < 0; -} - -#endif - -void helper_vadduqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ -#ifdef CONFIG_INT128 - r->u128 = a->u128 + b->u128; -#else - avr_qw_add(r, *a, *b); -#endif -} - -void helper_vaddeuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ -#ifdef CONFIG_INT128 - r->u128 = a->u128 + b->u128 + (c->u128 & 1); -#else - - if (c->VsrD(1) & 1) { - ppc_avr_t tmp; - - tmp.VsrD(0) = 0; - tmp.VsrD(1) = c->VsrD(1) & 1; - avr_qw_add(&tmp, *a, tmp); - avr_qw_add(r, tmp, *b); - } else { - avr_qw_add(r, *a, *b); - } -#endif -} - -void helper_vaddcuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ -#ifdef CONFIG_INT128 - r->u128 = (~a->u128 < b->u128); -#else - ppc_avr_t not_a; - - avr_qw_not(¬_a, *a); - + r->VsrD(1) = int128_ult(int128_not(a->s128), b->s128); r->VsrD(0) = 0; - r->VsrD(1) = (avr_qw_cmpu(not_a, *b) < 0); -#endif } -void helper_vaddecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +void helper_VADDECUQ(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) { -#ifdef CONFIG_INT128 - int carry_out = (~a->u128 < b->u128); - if (!carry_out && (c->u128 & 1)) { - carry_out = ((a->u128 + b->u128 + 1) == 0) && - ((a->u128 != 0) || (b->u128 != 0)); - } - r->u128 = carry_out; -#else - - int carry_in = c->VsrD(1) & 1; - int carry_out = 0; - ppc_avr_t tmp; - - carry_out = avr_qw_addc(&tmp, *a, *b); + bool carry_out = int128_ult(int128_not(a->s128), b->s128), + carry_in = int128_getlo(c->s128) & 1; if (!carry_out && carry_in) { - ppc_avr_t one = QW_ONE; - carry_out = avr_qw_addc(&tmp, tmp, one); - } - r->VsrD(0) = 0; - r->VsrD(1) = carry_out; -#endif -} - -void helper_vsubuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ -#ifdef CONFIG_INT128 - r->u128 = a->u128 - b->u128; -#else - ppc_avr_t tmp; - ppc_avr_t one = QW_ONE; - - avr_qw_not(&tmp, *b); - avr_qw_add(&tmp, *a, tmp); - avr_qw_add(r, tmp, one); -#endif -} - -void helper_vsubeuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ -#ifdef CONFIG_INT128 - r->u128 = a->u128 + ~b->u128 + (c->u128 & 1); -#else - ppc_avr_t tmp, sum; - - avr_qw_not(&tmp, *b); - avr_qw_add(&sum, *a, tmp); - - tmp.VsrD(0) = 0; - tmp.VsrD(1) = c->VsrD(1) & 1; - avr_qw_add(r, sum, tmp); -#endif -} - -void helper_vsubcuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ -#ifdef CONFIG_INT128 - r->u128 = (~a->u128 < ~b->u128) || - (a->u128 + ~b->u128 == (__uint128_t)-1); -#else - int carry = (avr_qw_cmpu(*a, *b) > 0); - if (!carry) { - ppc_avr_t tmp; - avr_qw_not(&tmp, *b); - avr_qw_add(&tmp, *a, tmp); - carry = ((tmp.VsrSD(0) == -1ull) && (tmp.VsrSD(1) == -1ull)); - } - r->VsrD(0) = 0; - r->VsrD(1) = carry; -#endif -} - -void helper_vsubecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ -#ifdef CONFIG_INT128 - r->u128 = - (~a->u128 < ~b->u128) || - ((c->u128 & 1) && (a->u128 + ~b->u128 == (__uint128_t)-1)); -#else - int carry_in = c->VsrD(1) & 1; - int carry_out = (avr_qw_cmpu(*a, *b) > 0); - if (!carry_out && carry_in) { - ppc_avr_t tmp; - avr_qw_not(&tmp, *b); - avr_qw_add(&tmp, *a, tmp); - carry_out = ((tmp.VsrD(0) == -1ull) && (tmp.VsrD(1) == -1ull)); + carry_out = (int128_nz(a->s128) || int128_nz(b->s128)) && + int128_eq(int128_add(a->s128, b->s128), int128_makes64(-1)); } r->VsrD(0) = 0; r->VsrD(1) = carry_out; -#endif +} + +void helper_VSUBUQM(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + r->s128 = int128_sub(a->s128, b->s128); +} + +void helper_VSUBEUQM(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ + r->s128 = int128_add(int128_add(a->s128, int128_not(b->s128)), + int128_make64(int128_getlo(c->s128) & 1)); +} + +void helper_VSUBCUQ(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + Int128 tmp = int128_not(b->s128); + + r->VsrD(1) = int128_ult(int128_not(a->s128), tmp) || + int128_eq(int128_add(a->s128, tmp), int128_makes64(-1)); + r->VsrD(0) = 0; +} + +void helper_VSUBECUQ(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ + Int128 tmp = int128_not(b->s128); + bool carry_out = int128_ult(int128_not(a->s128), tmp), + carry_in = int128_getlo(c->s128) & 1; + + r->VsrD(1) = carry_out || (carry_in && int128_eq(int128_add(a->s128, tmp), + int128_makes64(-1))); + r->VsrD(0) = 0; } #define BCD_PLUS_PREF_1 0xC @@ -3034,59 +2926,30 @@ void helper_vsbox(ppc_avr_t *r, ppc_avr_t *a) void helper_vcipher(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { - ppc_avr_t result; - int i; + AESState *ad = (AESState *)r; + AESState *st = (AESState *)a; + AESState *rk = (AESState *)b; - VECTOR_FOR_INORDER_I(i, u32) { - result.VsrW(i) = b->VsrW(i) ^ - (AES_Te0[a->VsrB(AES_shifts[4 * i + 0])] ^ - AES_Te1[a->VsrB(AES_shifts[4 * i + 1])] ^ - AES_Te2[a->VsrB(AES_shifts[4 * i + 2])] ^ - AES_Te3[a->VsrB(AES_shifts[4 * i + 3])]); - } - *r = result; + aesenc_SB_SR_MC_AK(ad, st, rk, true); } void helper_vcipherlast(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { - ppc_avr_t result; - int i; - - VECTOR_FOR_INORDER_I(i, u8) { - result.VsrB(i) = b->VsrB(i) ^ (AES_sbox[a->VsrB(AES_shifts[i])]); - } - *r = result; + aesenc_SB_SR_AK((AESState *)r, (AESState *)a, (AESState *)b, true); } void helper_vncipher(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { - /* This differs from what is written in ISA V2.07. The RTL is */ - /* incorrect and will be fixed in V2.07B. */ - int i; - ppc_avr_t tmp; + AESState *ad = (AESState *)r; + AESState *st = (AESState *)a; + AESState *rk = (AESState *)b; - VECTOR_FOR_INORDER_I(i, u8) { - tmp.VsrB(i) = b->VsrB(i) ^ AES_isbox[a->VsrB(AES_ishifts[i])]; - } - - VECTOR_FOR_INORDER_I(i, u32) { - r->VsrW(i) = - AES_imc[tmp.VsrB(4 * i + 0)][0] ^ - AES_imc[tmp.VsrB(4 * i + 1)][1] ^ - AES_imc[tmp.VsrB(4 * i + 2)][2] ^ - AES_imc[tmp.VsrB(4 * i + 3)][3]; - } + aesdec_ISB_ISR_AK_IMC(ad, st, rk, true); } void helper_vncipherlast(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { - ppc_avr_t result; - int i; - - VECTOR_FOR_INORDER_I(i, u8) { - result.VsrB(i) = b->VsrB(i) ^ (AES_isbox[a->VsrB(AES_ishifts[i])]); - } - *r = result; + aesdec_ISB_ISR_AK((AESState *)r, (AESState *)a, (AESState *)b, true); } void helper_vshasigmaw(ppc_avr_t *r, ppc_avr_t *a, uint32_t st_six) diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 2add128cd1..5b20ecbd33 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -20,6 +20,15 @@ #include "hw/registerfields.h" +/* PM instructions */ +typedef enum { + PPC_PM_DOZE, + PPC_PM_NAP, + PPC_PM_SLEEP, + PPC_PM_RVWINKLE, + PPC_PM_STOP, +} powerpc_pm_insn_t; + #define FUNC_MASK(name, ret_type, size, max_val) \ static inline ret_type name(uint##size##_t start, \ uint##size##_t end) \ @@ -159,15 +168,15 @@ EXTRACT_HELPER(FPL, 25, 1); EXTRACT_HELPER(FPFLM, 17, 8); EXTRACT_HELPER(FPW, 16, 1); -/* mffscrni */ -EXTRACT_HELPER(RM, 11, 2); - /* addpcis */ EXTRACT_HELPER_SPLIT_3(DX, 10, 6, 6, 5, 16, 1, 1, 0, 0) #if defined(TARGET_PPC64) /* darn */ EXTRACT_HELPER(L, 16, 2); #endif +/* wait */ +EXTRACT_HELPER(WC, 21, 2); +EXTRACT_HELPER(PL, 16, 2); /*** Jump target decoding ***/ /* Immediate address */ @@ -221,7 +230,7 @@ void destroy_ppc_opcodes(PowerPCCPU *cpu); /* gdbstub.c */ void ppc_gdb_init(CPUState *cs, PowerPCCPUClass *ppc); -gchar *ppc_gdb_arch_name(CPUState *cs); +const gchar *ppc_gdb_arch_name(CPUState *cs); /** * prot_for_access_type: @@ -242,9 +251,12 @@ static inline int prot_for_access_type(MMUAccessType access_type) g_assert_not_reached(); } +#ifndef CONFIG_USER_ONLY + /* PowerPC MMU emulation */ typedef struct mmu_ctx_t mmu_ctx_t; + bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible); @@ -266,6 +278,8 @@ struct mmu_ctx_t { int nx; /* Non-execute area */ }; +#endif /* !CONFIG_USER_ONLY */ + /* Common routines used by software and hardware TLBs emulation */ static inline int pte_is_valid(target_ulong pte0) { @@ -291,6 +305,14 @@ bool ppc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, G_NORETURN void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); +void ppc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, + vaddr addr, unsigned size, + MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, uintptr_t retaddr); +void ppc_cpu_debug_excp_handler(CPUState *cs); +bool ppc_cpu_debug_check_breakpoint(CPUState *cs); +bool ppc_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp); #endif FIELD(GER_MSK, XMSK, 0, 4) diff --git a/target/ppc/kvm-stub.c b/target/ppc/kvm-stub.c deleted file mode 100644 index b98e1d404f..0000000000 --- a/target/ppc/kvm-stub.c +++ /dev/null @@ -1,19 +0,0 @@ -/* - * QEMU KVM PPC specific function stubs - * - * Copyright Freescale Inc. 2013 - * - * Author: Alexander Graf - * - * 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 "cpu.h" -#include "hw/ppc/openpic_kvm.h" - -int kvm_openpic_connect_vcpu(DeviceState *d, CPUState *cs) -{ - return -EINVAL; -} diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 6eed466f80..8231feb2d4 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -32,7 +32,6 @@ #include "sysemu/device_tree.h" #include "mmu-hash64.h" -#include "hw/sysbus.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_cpu_core.h" #include "hw/hw.h" @@ -89,6 +88,7 @@ static int cap_ppc_nested_kvm_hv; static int cap_large_decr; static int cap_fwnmi; static int cap_rpt_invalidate; +static int cap_ail_mode_3; static uint32_t debug_inst_opcode; @@ -108,6 +108,11 @@ static int kvm_ppc_register_host_cpu_type(void); static void kvmppc_get_cpu_characteristics(KVMState *s); static int kvmppc_get_dec_bits(void); +int kvm_arch_get_default_type(MachineState *ms) +{ + return 0; +} + int kvm_arch_init(MachineState *ms, KVMState *s) { cap_interrupt_unset = kvm_check_extension(s, KVM_CAP_PPC_UNSET_IRQ); @@ -153,6 +158,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } cap_rpt_invalidate = kvm_vm_check_extension(s, KVM_CAP_PPC_RPT_INVALIDATE); + cap_ail_mode_3 = kvm_vm_check_extension(s, KVM_CAP_PPC_AIL_MODE_3); kvm_ppc_register_host_cpu_type(); return 0; @@ -262,7 +268,7 @@ static void kvm_get_smmu_info(struct kvm_ppc_smmu_info *info, Error **errp) "KVM failed to provide the MMU features it supports"); } -struct ppc_radix_page_info *kvm_get_radix_page_info(void) +static struct ppc_radix_page_info *kvmppc_get_radix_page_info(void) { KVMState *s = KVM_STATE(current_accel()); struct ppc_radix_page_info *radix_page_info; @@ -540,8 +546,7 @@ static void kvm_sw_tlb_put(PowerPCCPU *cpu) static void kvm_get_one_spr(CPUState *cs, uint64_t id, int spr) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); /* Init 'val' to avoid "uninitialised value" Valgrind warnings */ union { uint32_t u32; @@ -575,8 +580,7 @@ static void kvm_get_one_spr(CPUState *cs, uint64_t id, int spr) static void kvm_put_one_spr(CPUState *cs, uint64_t id, int spr) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); union { uint32_t u32; uint64_t u64; @@ -609,8 +613,7 @@ static void kvm_put_one_spr(CPUState *cs, uint64_t id, int spr) static int kvm_put_fp(CPUState *cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); struct kvm_one_reg reg; int i; int ret; @@ -629,8 +632,8 @@ static int kvm_put_fp(CPUState *cs) for (i = 0; i < 32; i++) { uint64_t vsr[2]; - uint64_t *fpr = cpu_fpr_ptr(&cpu->env, i); - uint64_t *vsrl = cpu_vsrl_ptr(&cpu->env, i); + uint64_t *fpr = cpu_fpr_ptr(env, i); + uint64_t *vsrl = cpu_vsrl_ptr(env, i); #if HOST_BIG_ENDIAN vsr[0] = float64_val(*fpr); @@ -676,8 +679,7 @@ static int kvm_put_fp(CPUState *cs) static int kvm_get_fp(CPUState *cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); struct kvm_one_reg reg; int i; int ret; @@ -698,8 +700,8 @@ static int kvm_get_fp(CPUState *cs) for (i = 0; i < 32; i++) { uint64_t vsr[2]; - uint64_t *fpr = cpu_fpr_ptr(&cpu->env, i); - uint64_t *vsrl = cpu_vsrl_ptr(&cpu->env, i); + uint64_t *fpr = cpu_fpr_ptr(env, i); + uint64_t *vsrl = cpu_vsrl_ptr(env, i); reg.addr = (uintptr_t) &vsr; reg.id = vsx ? KVM_REG_PPC_VSR(i) : KVM_REG_PPC_FPR(i); @@ -928,10 +930,7 @@ int kvm_arch_put_registers(CPUState *cs, int level) regs.gpr[i] = env->gpr[i]; } - regs.cr = 0; - for (i = 0; i < 8; i++) { - regs.cr |= (env->crf[i] & 15) << (4 * (7 - i)); - } + regs.cr = ppc_get_cr(env); ret = kvm_vcpu_ioctl(cs, KVM_SET_REGS, ®s); if (ret < 0) { @@ -957,8 +956,6 @@ int kvm_arch_put_registers(CPUState *cs, int level) } if (cap_one_reg) { - int i; - /* * We deliberately ignore errors here, for kernels which have * the ONE_REG calls, but don't support the specific @@ -1206,7 +1203,6 @@ int kvm_arch_get_registers(CPUState *cs) PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; struct kvm_regs regs; - uint32_t cr; int i, ret; ret = kvm_vcpu_ioctl(cs, KVM_GET_REGS, ®s); @@ -1214,12 +1210,7 @@ int kvm_arch_get_registers(CPUState *cs) return ret; } - cr = regs.cr; - for (i = 7; i >= 0; i--) { - env->crf[i] = cr & 15; - cr >>= 4; - } - + ppc_set_cr(env, regs.cr); env->ctr = regs.ctr; env->lr = regs.lr; cpu_write_xer(env, regs.xer); @@ -1265,8 +1256,6 @@ int kvm_arch_get_registers(CPUState *cs) } if (cap_one_reg) { - int i; - /* * We deliberately ignore errors here, for kernels which have * the ONE_REG calls, but don't support the specific @@ -1323,7 +1312,7 @@ int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level) return 0; } - if (!kvm_enabled() || !cap_interrupt_unset) { + if (!cap_interrupt_unset) { return 0; } @@ -1452,15 +1441,15 @@ static int find_hw_watchpoint(target_ulong addr, int *flag) return -1; } -int kvm_arch_insert_hw_breakpoint(target_ulong addr, - target_ulong len, int type) +int kvm_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type) { - if ((nb_hw_breakpoint + nb_hw_watchpoint) >= ARRAY_SIZE(hw_debug_points)) { + const unsigned breakpoint_index = nb_hw_breakpoint + nb_hw_watchpoint; + if (breakpoint_index >= ARRAY_SIZE(hw_debug_points)) { return -ENOBUFS; } - hw_debug_points[nb_hw_breakpoint + nb_hw_watchpoint].addr = addr; - hw_debug_points[nb_hw_breakpoint + nb_hw_watchpoint].type = type; + hw_debug_points[breakpoint_index].addr = addr; + hw_debug_points[breakpoint_index].type = type; switch (type) { case GDB_BREAKPOINT_HW: @@ -1496,8 +1485,7 @@ int kvm_arch_insert_hw_breakpoint(target_ulong addr, return 0; } -int kvm_arch_remove_hw_breakpoint(target_ulong addr, - target_ulong len, int type) +int kvm_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type) { int n; @@ -1664,7 +1652,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) CPUPPCState *env = &cpu->env; int ret; - qemu_mutex_lock_iothread(); + bql_lock(); switch (run->exit_reason) { case KVM_EXIT_DCR: @@ -1723,7 +1711,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) break; } - qemu_mutex_unlock_iothread(); + bql_unlock(); return ret; } @@ -1736,6 +1724,10 @@ int kvmppc_or_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits) .addr = (uintptr_t) &bits, }; + if (!kvm_enabled()) { + return 0; + } + return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); } @@ -1749,6 +1741,10 @@ int kvmppc_clear_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits) .addr = (uintptr_t) &bits, }; + if (!kvm_enabled()) { + return 0; + } + return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); } @@ -1763,6 +1759,10 @@ int kvmppc_set_tcr(PowerPCCPU *cpu) .addr = (uintptr_t) &tcr, }; + if (!kvm_enabled()) { + return 0; + } + return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); } @@ -1877,6 +1877,12 @@ static int kvmppc_find_cpu_dt(char *buf, int buf_len) buf[0] = '\0'; while ((dirp = readdir(dp)) != NULL) { FILE *f; + + /* Don't accidentally read from the current and parent directories */ + if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0) { + continue; + } + snprintf(buf, buf_len, "%s%s/clock-frequency", PROC_DEVTREE_CPU, dirp->d_name); f = fopen(buf, "r"); @@ -2358,18 +2364,7 @@ static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) } #if defined(TARGET_PPC64) - pcc->radix_page_info = kvm_get_radix_page_info(); - - if ((pcc->pvr & 0xffffff00) == CPU_POWERPC_POWER9_DD1) { - /* - * POWER9 DD1 has some bugs which make it not really ISA 3.00 - * compliant. More importantly, advertising ISA 3.00 - * architected mode may prevent guests from activating - * necessary DD1 workarounds. - */ - pcc->pcr_supported &= ~(PCR_COMPAT_3_00 | PCR_COMPAT_2_07 - | PCR_COMPAT_2_06 | PCR_COMPAT_2_05); - } + pcc->radix_page_info = kvmppc_get_radix_page_info(); #endif /* defined(TARGET_PPC64) */ } @@ -2564,6 +2559,11 @@ int kvmppc_has_cap_rpt_invalidate(void) return cap_rpt_invalidate; } +bool kvmppc_supports_ail_3(void) +{ + return cap_ail_mode_3; +} + PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void) { uint32_t host_pvr = mfpvr(); @@ -2673,7 +2673,7 @@ int kvmppc_get_htab_fd(bool write, uint64_t index, Error **errp) int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns) { int64_t starttime = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - uint8_t buf[bufsize]; + g_autofree uint8_t *buf = g_malloc(bufsize); ssize_t rc; do { @@ -2755,9 +2755,9 @@ void kvmppc_read_hptes(ppc_hash_pte64_t *hptes, hwaddr ptex, int n) while (i < n) { struct kvm_get_htab_header *hdr; int m = n < HPTES_PER_GROUP ? n : HPTES_PER_GROUP; - char buf[sizeof(*hdr) + m * HASH_PTE_SIZE_64]; + char buf[sizeof(*hdr) + HPTES_PER_GROUP * HASH_PTE_SIZE_64]; - rc = read(fd, buf, sizeof(buf)); + rc = read(fd, buf, sizeof(*hdr) + m * HASH_PTE_SIZE_64); if (rc < 0) { hw_error("kvmppc_read_hptes: Unable to read HPTEs"); } @@ -2960,3 +2960,7 @@ bool kvm_arch_cpu_check_are_resettable(void) { return true; } + +void kvm_arch_accel_class_init(ObjectClass *oc) +{ +} diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index ee9325bf9a..1975fb5ee6 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -9,7 +9,13 @@ #ifndef KVM_PPC_H #define KVM_PPC_H -#define TYPE_HOST_POWERPC_CPU POWERPC_CPU_TYPE_NAME("host") +#include "sysemu/kvm.h" +#include "exec/hwaddr.h" +#include "cpu.h" + +#ifdef CONFIG_USER_ONLY +#error Cannot include kvm_ppc.h from user emulation +#endif #ifdef CONFIG_KVM @@ -40,7 +46,6 @@ int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu); target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, bool radix, bool gtse, uint64_t proc_tbl); -#ifndef CONFIG_USER_ONLY bool kvmppc_spapr_use_multitce(void); int kvmppc_spapr_enable_inkernel_multitce(void); void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift, @@ -50,7 +55,6 @@ int kvmppc_remove_spapr_tce(void *table, int pfd, uint32_t window_size); int kvmppc_reset_htab(int shift_hint); 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); int kvmppc_define_rtas_kernel_token(uint32_t token, const char *function); int kvmppc_get_htab_fd(bool write, uint64_t index, Error **errp); @@ -73,6 +77,7 @@ int kvmppc_set_cap_nested_kvm_hv(int enable); int kvmppc_get_cap_large_decr(void); int kvmppc_enable_cap_large_decr(PowerPCCPU *cpu, int enable); int kvmppc_has_cap_rpt_invalidate(void); +bool kvmppc_supports_ail_3(void); int kvmppc_enable_hwrng(void); int kvmppc_put_books_sregs(PowerPCCPU *cpu); PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void); @@ -88,7 +93,34 @@ void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset); int kvm_handle_nmi(PowerPCCPU *cpu, struct kvm_run *run); -#else +#define kvmppc_eieio() \ + do { \ + if (kvm_enabled()) { \ + asm volatile("eieio" : : : "memory"); \ + } \ + } while (0) + +/* Store data cache blocks back to memory */ +static inline void kvmppc_dcbst_range(PowerPCCPU *cpu, uint8_t *addr, int len) +{ + uint8_t *p; + + for (p = addr; p < addr + len; p += cpu->env.dcache_line_size) { + asm volatile("dcbst 0,%0" : : "r"(p) : "memory"); + } +} + +/* Invalidate instruction cache blocks */ +static inline void kvmppc_icbi_range(PowerPCCPU *cpu, uint8_t *addr, int len) +{ + uint8_t *p; + + for (p = addr; p < addr + len; p += cpu->env.icache_line_size) { + asm volatile("icbi 0,%0" : : "r"(p)); + } +} + +#else /* !CONFIG_KVM */ static inline uint32_t kvmppc_get_tbfreq(void) { @@ -232,7 +264,6 @@ static inline void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset) { } -#ifndef CONFIG_USER_ONLY static inline bool kvmppc_spapr_use_multitce(void) { return false; @@ -292,8 +323,6 @@ static inline void kvmppc_write_hpte(hwaddr ptex, uint64_t pte0, uint64_t pte1) abort(); } -#endif /* !CONFIG_USER_ONLY */ - static inline bool kvmppc_has_cap_epr(void) { return false; @@ -393,6 +422,11 @@ static inline int kvmppc_has_cap_rpt_invalidate(void) return false; } +static inline bool kvmppc_supports_ail_3(void) +{ + return false; +} + static inline int kvmppc_enable_hwrng(void) { return -1; @@ -430,10 +464,6 @@ static inline bool kvmppc_pvr_workaround_required(PowerPCCPU *cpu) return false; } -#endif - -#ifndef CONFIG_KVM - #define kvmppc_eieio() do { } while (0) static inline void kvmppc_dcbst_range(PowerPCCPU *cpu, uint8_t *addr, int len) @@ -444,35 +474,6 @@ static inline void kvmppc_icbi_range(PowerPCCPU *cpu, uint8_t *addr, int len) { } -#else /* CONFIG_KVM */ - -#define kvmppc_eieio() \ - do { \ - if (kvm_enabled()) { \ - asm volatile("eieio" : : : "memory"); \ - } \ - } while (0) - -/* Store data cache blocks back to memory */ -static inline void kvmppc_dcbst_range(PowerPCCPU *cpu, uint8_t *addr, int len) -{ - uint8_t *p; - - for (p = addr; p < addr + len; p += cpu->env.dcache_line_size) { - asm volatile("dcbst 0,%0" : : "r"(p) : "memory"); - } -} - -/* Invalidate instruction cache blocks */ -static inline void kvmppc_icbi_range(PowerPCCPU *cpu, uint8_t *addr, int len) -{ - uint8_t *p; - - for (p = addr; p < addr + len; p += cpu->env.icache_line_size) { - asm volatile("icbi 0,%0" : : "r"(p)); - } -} - #endif /* CONFIG_KVM */ #endif /* KVM_PPC_H */ diff --git a/target/ppc/machine.c b/target/ppc/machine.c index a7d9036c09..203fe28e01 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -7,9 +7,9 @@ #include "mmu-hash64.h" #include "migration/cpu.h" #include "qapi/error.h" -#include "qemu/main-loop.h" #include "kvm_ppc.h" #include "power8-pmu.h" +#include "sysemu/replay.h" static void post_load_update_msr(CPUPPCState *env) { @@ -21,10 +21,6 @@ static void post_load_update_msr(CPUPPCState *env) */ env->msr ^= env->msr_mask & ~((1ULL << MSR_TGPR) | MSR_HVB); ppc_store_msr(env, msr); - - if (tcg_enabled()) { - pmu_update_summaries(env); - } } static int get_avr(QEMUFile *f, void *pv, size_t size, @@ -213,6 +209,14 @@ static int cpu_pre_save(void *opaque) /* Used to retain migration compatibility for pre 6.0 for 601 machines. */ env->hflags_compat_nmsr = 0; + if (tcg_enabled()) { + /* + * TCG does not maintain the DECR spr (unlike KVM) so have to save + * it here. + */ + env->spr[SPR_DECR] = cpu_ppc_load_decr(env); + } + return 0; } @@ -234,7 +238,7 @@ static bool pvr_match(PowerPCCPU *cpu, uint32_t pvr) if (pvr == pcc->pvr) { return true; } - return pcc->pvr_match(pcc, pvr); + return pcc->pvr_match(pcc, pvr, true); } static int cpu_post_load(void *opaque, int version_id) @@ -317,6 +321,21 @@ static int cpu_post_load(void *opaque, int version_id) post_load_update_msr(env); + if (tcg_enabled()) { + /* Re-set breaks based on regs */ +#if defined(TARGET_PPC64) + ppc_update_ciabr(env); + ppc_update_daw0(env); +#endif + /* + * TCG needs to re-start the decrementer timer and/or raise the + * interrupt. This works for level-triggered decrementer. Edge + * triggered types (including HDEC) would need to carry more state. + */ + cpu_ppc_store_decr(env, env->spr[SPR_DECR]); + pmu_mmcr01_updated(env); + } + return 0; } @@ -332,7 +351,7 @@ static const VMStateDescription vmstate_fpu = { .version_id = 1, .minimum_version_id = 1, .needed = fpu_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_FPR_ARRAY(env.vsr, PowerPCCPU, 32), VMSTATE_UINTTL(env.fpscr, PowerPCCPU), VMSTATE_END_OF_LIST() @@ -373,7 +392,7 @@ static const VMStateDescription vmstate_altivec = { .version_id = 1, .minimum_version_id = 1, .needed = altivec_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_AVR_ARRAY(env.vsr, PowerPCCPU, 32), /* * Save the architecture value of the vscr, not the internally @@ -406,7 +425,7 @@ static const VMStateDescription vmstate_vsx = { .version_id = 1, .minimum_version_id = 1, .needed = vsx_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VSR_ARRAY(env.vsr, PowerPCCPU, 32), VMSTATE_END_OF_LIST() }, @@ -426,7 +445,7 @@ static const VMStateDescription vmstate_tm = { .version_id = 1, .minimum_version_id = 1, .needed = tm_needed, - .fields = (VMStateField []) { + .fields = (const VMStateField []) { VMSTATE_UINTTL_ARRAY(env.tm_gpr, PowerPCCPU, 32), VMSTATE_AVR_ARRAY(env.tm_vsr, PowerPCCPU, 64), VMSTATE_UINT64(env.tm_cr, PowerPCCPU), @@ -460,7 +479,7 @@ static const VMStateDescription vmstate_sr = { .version_id = 1, .minimum_version_id = 1, .needed = sr_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.sr, PowerPCCPU, 32), VMSTATE_END_OF_LIST() }, @@ -534,7 +553,7 @@ static const VMStateDescription vmstate_slb = { .minimum_version_id = 1, .needed = slb_needed, .post_load = slb_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32_TEST(mig_slb_nr, PowerPCCPU, cpu_pre_3_0_migration), VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, MAX_SLB_ENTRIES), VMSTATE_END_OF_LIST() @@ -546,7 +565,7 @@ static const VMStateDescription vmstate_tlb6xx_entry = { .name = "cpu/tlb6xx_entry", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINTTL(pte0, ppc6xx_tlb_t), VMSTATE_UINTTL(pte1, ppc6xx_tlb_t), VMSTATE_UINTTL(EPN, ppc6xx_tlb_t), @@ -567,7 +586,7 @@ static const VMStateDescription vmstate_tlb6xx = { .version_id = 1, .minimum_version_id = 1, .needed = tlb6xx_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32_EQUAL(env.nb_tlb, PowerPCCPU, NULL), VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlb6, PowerPCCPU, env.nb_tlb, @@ -582,7 +601,7 @@ static const VMStateDescription vmstate_tlbemb_entry = { .name = "cpu/tlbemb_entry", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(RPN, ppcemb_tlb_t), VMSTATE_UINTTL(EPN, ppcemb_tlb_t), VMSTATE_UINTTL(PID, ppcemb_tlb_t), @@ -606,7 +625,7 @@ static const VMStateDescription vmstate_tlbemb = { .version_id = 1, .minimum_version_id = 1, .needed = tlbemb_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32_EQUAL(env.nb_tlb, PowerPCCPU, NULL), VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlbe, PowerPCCPU, env.nb_tlb, @@ -620,7 +639,7 @@ static const VMStateDescription vmstate_tlbmas_entry = { .name = "cpu/tlbmas_entry", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(mas8, ppcmas_tlb_t), VMSTATE_UINT32(mas1, ppcmas_tlb_t), VMSTATE_UINT64(mas2, ppcmas_tlb_t), @@ -642,7 +661,7 @@ static const VMStateDescription vmstate_tlbmas = { .version_id = 1, .minimum_version_id = 1, .needed = tlbmas_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32_EQUAL(env.nb_tlb, PowerPCCPU, NULL), VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlbm, PowerPCCPU, env.nb_tlb, @@ -665,19 +684,40 @@ static const VMStateDescription vmstate_compat = { .version_id = 1, .minimum_version_id = 1, .needed = compat_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(compat_pvr, PowerPCCPU), VMSTATE_END_OF_LIST() } }; +static bool reservation_needed(void *opaque) +{ + return (replay_mode != REPLAY_MODE_NONE); +} + +static const VMStateDescription vmstate_reservation = { + .name = "cpu/reservation", + .version_id = 1, + .minimum_version_id = 1, + .needed = reservation_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINTTL(env.reserve_addr, PowerPCCPU), + VMSTATE_UINTTL(env.reserve_length, PowerPCCPU), + VMSTATE_UINTTL(env.reserve_val, PowerPCCPU), +#if defined(TARGET_PPC64) + VMSTATE_UINTTL(env.reserve_val2, PowerPCCPU), +#endif + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_ppc_cpu = { .name = "cpu", .version_id = 5, .minimum_version_id = 5, .pre_save = cpu_pre_save, .post_load = cpu_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UNUSED(sizeof(target_ulong)), /* was _EQUAL(env.spr[SPR_PVR]) */ /* User mode architected state */ @@ -692,8 +732,7 @@ const VMStateDescription vmstate_ppc_cpu = { VMSTATE_UINTTL_ARRAY(env.spr, PowerPCCPU, 1024), VMSTATE_UINT64(env.spe_acc, PowerPCCPU), - /* Reservation */ - VMSTATE_UINTTL(env.reserve_addr, PowerPCCPU), + VMSTATE_UNUSED(sizeof(target_ulong)), /* was env.reserve_addr */ /* Supervisor mode architected state */ VMSTATE_UINTTL(env.msr, PowerPCCPU), @@ -709,7 +748,7 @@ const VMStateDescription vmstate_ppc_cpu = { VMSTATE_UINT32_TEST(mig_nb_BATs, PowerPCCPU, cpu_pre_2_8_migration), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_fpu, &vmstate_altivec, &vmstate_vsx, @@ -722,6 +761,7 @@ const VMStateDescription vmstate_ppc_cpu = { &vmstate_tlbemb, &vmstate_tlbmas, &vmstate_compat, + &vmstate_reservation, NULL } }; diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index d1163f316c..ea7e8443a8 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -21,7 +21,6 @@ #include "cpu.h" #include "exec/exec-all.h" #include "qemu/host-utils.h" -#include "qemu/main-loop.h" #include "exec/helper-proto.h" #include "helper_regs.h" #include "exec/cpu_ldst.h" @@ -84,7 +83,7 @@ static void *probe_contiguous(CPUPPCState *env, target_ulong addr, uint32_t nb, void helper_lmw(CPUPPCState *env, target_ulong addr, uint32_t reg) { uintptr_t raddr = GETPC(); - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = ppc_env_mmu_index(env, false); void *host = probe_contiguous(env, addr, (32 - reg) * 4, MMU_DATA_LOAD, mmu_idx, raddr); @@ -106,7 +105,7 @@ void helper_lmw(CPUPPCState *env, target_ulong addr, uint32_t reg) void helper_stmw(CPUPPCState *env, target_ulong addr, uint32_t reg) { uintptr_t raddr = GETPC(); - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = ppc_env_mmu_index(env, false); void *host = probe_contiguous(env, addr, (32 - reg) * 4, MMU_DATA_STORE, mmu_idx, raddr); @@ -136,7 +135,7 @@ static void do_lsw(CPUPPCState *env, target_ulong addr, uint32_t nb, return; } - mmu_idx = cpu_mmu_index(env, false); + mmu_idx = ppc_env_mmu_index(env, false); host = probe_contiguous(env, addr, nb, MMU_DATA_LOAD, mmu_idx, raddr); if (likely(host)) { @@ -225,7 +224,7 @@ void helper_stsw(CPUPPCState *env, target_ulong addr, uint32_t nb, return; } - mmu_idx = cpu_mmu_index(env, false); + mmu_idx = ppc_env_mmu_index(env, false); host = probe_contiguous(env, addr, nb, MMU_DATA_STORE, mmu_idx, raddr); if (likely(host)) { @@ -277,7 +276,7 @@ static void dcbz_common(CPUPPCState *env, target_ulong addr, target_ulong mask, dcbz_size = env->dcache_line_size; uint32_t i; void *haddr; - int mmu_idx = epid ? PPC_TLB_EPID_STORE : cpu_mmu_index(env, false); + int mmu_idx = epid ? PPC_TLB_EPID_STORE : ppc_env_mmu_index(env, false); #if defined(TARGET_PPC64) /* Check for dcbz vs dcbzl on 970 */ @@ -367,98 +366,6 @@ target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, return i; } -#ifdef TARGET_PPC64 -uint64_t helper_lq_le_parallel(CPUPPCState *env, target_ulong addr, - uint32_t opidx) -{ - Int128 ret; - - /* We will have raised EXCP_ATOMIC from the translator. */ - assert(HAVE_ATOMIC128); - ret = cpu_atomic_ldo_le_mmu(env, addr, opidx, GETPC()); - env->retxh = int128_gethi(ret); - return int128_getlo(ret); -} - -uint64_t helper_lq_be_parallel(CPUPPCState *env, target_ulong addr, - uint32_t opidx) -{ - Int128 ret; - - /* We will have raised EXCP_ATOMIC from the translator. */ - assert(HAVE_ATOMIC128); - ret = cpu_atomic_ldo_be_mmu(env, addr, opidx, GETPC()); - env->retxh = int128_gethi(ret); - return int128_getlo(ret); -} - -void helper_stq_le_parallel(CPUPPCState *env, target_ulong addr, - uint64_t lo, uint64_t hi, uint32_t opidx) -{ - Int128 val; - - /* We will have raised EXCP_ATOMIC from the translator. */ - assert(HAVE_ATOMIC128); - val = int128_make128(lo, hi); - cpu_atomic_sto_le_mmu(env, addr, val, opidx, GETPC()); -} - -void helper_stq_be_parallel(CPUPPCState *env, target_ulong addr, - uint64_t lo, uint64_t hi, uint32_t opidx) -{ - Int128 val; - - /* We will have raised EXCP_ATOMIC from the translator. */ - assert(HAVE_ATOMIC128); - val = int128_make128(lo, hi); - cpu_atomic_sto_be_mmu(env, addr, val, opidx, GETPC()); -} - -uint32_t helper_stqcx_le_parallel(CPUPPCState *env, target_ulong addr, - uint64_t new_lo, uint64_t new_hi, - uint32_t opidx) -{ - bool success = false; - - /* We will have raised EXCP_ATOMIC from the translator. */ - assert(HAVE_CMPXCHG128); - - if (likely(addr == env->reserve_addr)) { - Int128 oldv, cmpv, newv; - - cmpv = int128_make128(env->reserve_val2, env->reserve_val); - newv = int128_make128(new_lo, new_hi); - oldv = cpu_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv, - opidx, GETPC()); - success = int128_eq(oldv, cmpv); - } - env->reserve_addr = -1; - return env->so + success * CRF_EQ_BIT; -} - -uint32_t helper_stqcx_be_parallel(CPUPPCState *env, target_ulong addr, - uint64_t new_lo, uint64_t new_hi, - uint32_t opidx) -{ - bool success = false; - - /* We will have raised EXCP_ATOMIC from the translator. */ - assert(HAVE_CMPXCHG128); - - if (likely(addr == env->reserve_addr)) { - Int128 oldv, cmpv, newv; - - cmpv = int128_make128(env->reserve_val2, env->reserve_val); - newv = int128_make128(new_lo, new_hi); - oldv = cpu_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, - opidx, GETPC()); - success = int128_eq(oldv, cmpv); - } - env->reserve_addr = -1; - return env->so + success * CRF_EQ_BIT; -} -#endif - /*****************************************************************************/ /* Altivec extension helpers */ #if HOST_BIG_ENDIAN diff --git a/target/ppc/meson.build b/target/ppc/meson.build index 79beaff147..0b89f9b89f 100644 --- a/target/ppc/meson.build +++ b/target/ppc/meson.build @@ -28,26 +28,26 @@ gen = [ extra_args: ['--static-decode=decode_insn64', '--insnwidth=64']), ] -ppc_ss.add(gen) +ppc_ss.add(when: 'CONFIG_TCG', if_true: gen) -ppc_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c')) ppc_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user_only_helper.c')) -ppc_softmmu_ss = ss.source_set() -ppc_softmmu_ss.add(files( +ppc_system_ss = ss.source_set() +ppc_system_ss.add(files( 'arch_dump.c', 'machine.c', 'mmu-hash32.c', 'mmu_common.c', - 'monitor.c', + 'ppc-qmp-cmds.c', )) -ppc_softmmu_ss.add(when: 'CONFIG_TCG', if_true: files( +ppc_system_ss.add(when: 'CONFIG_TCG', if_true: files( 'mmu_helper.c', ), if_false: files( 'tcg-stub.c', )) +ppc_system_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c')) -ppc_softmmu_ss.add(when: 'TARGET_PPC64', if_true: files( +ppc_system_ss.add(when: 'TARGET_PPC64', if_true: files( 'compat.c', 'mmu-book3s-v3.c', 'mmu-hash64.c', @@ -55,4 +55,4 @@ ppc_softmmu_ss.add(when: 'TARGET_PPC64', if_true: files( )) target_arch += {'ppc': ppc_ss} -target_softmmu_arch += {'ppc': ppc_softmmu_ss} +target_system_arch += {'ppc': ppc_system_ss} diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index b0a5e7ce76..58e808dc96 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -25,6 +25,7 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "mmu-book3s-v3.h" +#include "hw/ppc/ppc.h" #include "helper_regs.h" @@ -42,6 +43,49 @@ void helper_store_dump_spr(CPUPPCState *env, uint32_t sprn) env->spr[sprn]); } +void helper_spr_core_write_generic(CPUPPCState *env, uint32_t sprn, + target_ulong val) +{ + CPUState *cs = env_cpu(env); + CPUState *ccs; + uint32_t nr_threads = cs->nr_threads; + + if (nr_threads == 1) { + env->spr[sprn] = val; + return; + } + + THREAD_SIBLING_FOREACH(cs, ccs) { + CPUPPCState *cenv = &POWERPC_CPU(ccs)->env; + cenv->spr[sprn] = val; + } +} + +void helper_spr_write_CTRL(CPUPPCState *env, uint32_t sprn, + target_ulong val) +{ + CPUState *cs = env_cpu(env); + CPUState *ccs; + uint32_t run = val & 1; + uint32_t ts, ts_mask; + + assert(sprn == SPR_CTRL); + + env->spr[sprn] &= ~1U; + env->spr[sprn] |= run; + + ts_mask = ~(1U << (8 + env->spr[SPR_TIR])); + ts = run << (8 + env->spr[SPR_TIR]); + + THREAD_SIBLING_FOREACH(cs, ccs) { + CPUPPCState *cenv = &POWERPC_CPU(ccs)->env; + + cenv->spr[sprn] &= ts_mask; + cenv->spr[sprn] |= ts; + } +} + + #ifdef TARGET_PPC64 static void raise_hv_fu_exception(CPUPPCState *env, uint32_t bit, const char *caller, uint32_t cause, @@ -152,56 +196,105 @@ void helper_store_pcr(CPUPPCState *env, target_ulong value) env->spr[SPR_PCR] = value & pcc->pcr_mask; } +void helper_store_ciabr(CPUPPCState *env, target_ulong value) +{ + ppc_store_ciabr(env, value); +} + +void helper_store_dawr0(CPUPPCState *env, target_ulong value) +{ + ppc_store_dawr0(env, value); +} + +void helper_store_dawrx0(CPUPPCState *env, target_ulong value) +{ + ppc_store_dawrx0(env, value); +} + /* * 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) { + CPUState *cs = env_cpu(env); + CPUState *ccs; + uint32_t nr_threads = cs->nr_threads; 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; + if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + nr_threads = 1; /* DPDES behaves as 1-thread in LPAR-per-thread mode */ } + if (nr_threads == 1) { + if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) { + dpdes = 1; + } + return dpdes; + } + + bql_lock(); + THREAD_SIBLING_FOREACH(cs, ccs) { + PowerPCCPU *ccpu = POWERPC_CPU(ccs); + CPUPPCState *cenv = &ccpu->env; + uint32_t thread_id = ppc_cpu_tir(ccpu); + + if (cenv->pending_interrupts & PPC_INTERRUPT_DOORBELL) { + dpdes |= (0x1 << thread_id); + } + } + bql_unlock(); + return dpdes; } void helper_store_dpdes(CPUPPCState *env, target_ulong val) { PowerPCCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); + CPUState *cs = env_cpu(env); + CPUState *ccs; + uint32_t nr_threads = cs->nr_threads; helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP); - /* TODO: TCG supports only one thread */ - if (val & ~0x1) { + if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + nr_threads = 1; /* DPDES behaves as 1-thread in LPAR-per-thread mode */ + } + + if (val & ~(nr_threads - 1)) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid DPDES register value " TARGET_FMT_lx"\n", val); + val &= (nr_threads - 1); /* Ignore the invalid bits */ + } + + if (nr_threads == 1) { + ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & 0x1); 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); + /* Does iothread need to be locked for walking CPU list? */ + bql_lock(); + THREAD_SIBLING_FOREACH(cs, ccs) { + PowerPCCPU *ccpu = POWERPC_CPU(ccs); + uint32_t thread_id = ppc_cpu_tir(ccpu); + + ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & (0x1 << thread_id)); } + bql_unlock(); } #endif /* defined(TARGET_PPC64) */ void helper_store_pidr(CPUPPCState *env, target_ulong val) { - env->spr[SPR_BOOKS_PID] = val; + env->spr[SPR_BOOKS_PID] = (uint32_t)val; tlb_flush(env_cpu(env)); } void helper_store_lpidr(CPUPPCState *env, target_ulong val) { - env->spr[SPR_LPIDR] = val; + env->spr[SPR_LPIDR] = (uint32_t)val; /* * We need to flush the TLB on LPID changes as we only tag HV vs diff --git a/target/ppc/mmu-book3s-v3.c b/target/ppc/mmu-book3s-v3.c index f4985bae78..c8f69b3df9 100644 --- a/target/ppc/mmu-book3s-v3.c +++ b/target/ppc/mmu-book3s-v3.c @@ -28,6 +28,11 @@ bool ppc64_v3_get_pate(PowerPCCPU *cpu, target_ulong lpid, ppc_v3_pate_t *entry) uint64_t patb = cpu->env.spr[SPR_PTCR] & PTCR_PATB; uint64_t pats = cpu->env.spr[SPR_PTCR] & PTCR_PATS; + /* Check if partition table is properly aligned */ + if (patb & MAKE_64BIT_MASK(0, pats + 12)) { + return false; + } + /* Calculate number of entries */ pats = 1ull << (pats + 12 - 4); if (pats <= lpid) { diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index d6d5ed8f8e..674377a19e 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -50,6 +50,21 @@ struct prtb_entry { #ifdef TARGET_PPC64 +/* + * tlbie[l] helper flags + * + * RIC, PRS, R and local are passed as flags in the last argument. + */ +#define TLBIE_F_RIC_SHIFT 0 +#define TLBIE_F_PRS_SHIFT 2 +#define TLBIE_F_R_SHIFT 3 +#define TLBIE_F_LOCAL_SHIFT 4 + +#define TLBIE_F_RIC_MASK (3 << TLBIE_F_RIC_SHIFT) +#define TLBIE_F_PRS (1 << TLBIE_F_PRS_SHIFT) +#define TLBIE_F_R (1 << TLBIE_F_R_SHIFT) +#define TLBIE_F_LOCAL (1 << TLBIE_F_LOCAL_SHIFT) + static inline bool ppc64_use_proc_tbl(PowerPCCPU *cpu) { return !!(cpu->env.spr[SPR_LPCR] & LPCR_UPRT); diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index cc091c3e62..3976416840 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -346,24 +346,24 @@ static hwaddr ppc_hash32_htab_lookup(PowerPCCPU *cpu, ptem = (vsid << 7) | (pgidx >> 10); /* Page address translation */ - qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx - " htab_mask " TARGET_FMT_plx - " hash " TARGET_FMT_plx "\n", + qemu_log_mask(CPU_LOG_MMU, "htab_base " HWADDR_FMT_plx + " htab_mask " HWADDR_FMT_plx + " hash " HWADDR_FMT_plx "\n", ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); /* Primary PTEG lookup */ - qemu_log_mask(CPU_LOG_MMU, "0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, "0 htab=" HWADDR_FMT_plx "/" HWADDR_FMT_plx " vsid=%" PRIx32 " ptem=%" PRIx32 - " hash=" TARGET_FMT_plx "\n", + " hash=" HWADDR_FMT_plx "\n", ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), vsid, ptem, hash); pteg_off = get_pteg_offset32(cpu, hash); pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 0, ptem, pte); if (pte_offset == -1) { /* Secondary PTEG lookup */ - qemu_log_mask(CPU_LOG_MMU, "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, "1 htab=" HWADDR_FMT_plx "/" HWADDR_FMT_plx " vsid=%" PRIx32 " api=%" PRIx32 - " hash=" TARGET_FMT_plx "\n", ppc_hash32_hpt_base(cpu), + " hash=" HWADDR_FMT_plx "\n", ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), vsid, ptem, ~hash); pteg_off = get_pteg_offset32(cpu, ~hash); pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 1, ptem, pte); diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index da9fe99ff8..d645c0bb94 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -101,7 +101,7 @@ void dump_slb(PowerPCCPU *cpu) } #ifdef CONFIG_TCG -void helper_slbia(CPUPPCState *env, uint32_t ih) +void helper_SLBIA(CPUPPCState *env, uint32_t ih) { PowerPCCPU *cpu = env_archcpu(env); int starting_entry; @@ -173,6 +173,33 @@ void helper_slbia(CPUPPCState *env, uint32_t ih) } } +#if defined(TARGET_PPC64) +void helper_SLBIAG(CPUPPCState *env, target_ulong rs, uint32_t l) +{ + PowerPCCPU *cpu = env_archcpu(env); + int n; + + /* + * slbiag 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 slbiag would + * not have to flush unless it evicts a valid SLB entry. However it is + * expected that slbmte is more common than slbiag, and slbiag is usually + * going to evict valid SLB entries, so that tradeoff is unlikely to be a + * good one. + */ + env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; + + for (n = 0; n < cpu->hash64_opts->slb_size; n++) { + ppc_slb_t *slb = &env->slb[n]; + slb->esid &= ~SLB_ESID_V; + } +} +#endif + static void __helper_slbie(CPUPPCState *env, target_ulong addr, target_ulong global) { @@ -197,12 +224,12 @@ static void __helper_slbie(CPUPPCState *env, target_ulong addr, } } -void helper_slbie(CPUPPCState *env, target_ulong addr) +void helper_SLBIE(CPUPPCState *env, target_ulong addr) { __helper_slbie(env, addr, false); } -void helper_slbieg(CPUPPCState *env, target_ulong addr) +void helper_SLBIEG(CPUPPCState *env, target_ulong addr) { __helper_slbie(env, addr, true); } @@ -309,7 +336,7 @@ static int ppc_find_slb_vsid(PowerPCCPU *cpu, target_ulong rb, return 0; } -void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) +void helper_SLBMTE(CPUPPCState *env, target_ulong rb, target_ulong rs) { PowerPCCPU *cpu = env_archcpu(env); @@ -319,7 +346,7 @@ void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) } } -target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb) +target_ulong helper_SLBMFEE(CPUPPCState *env, target_ulong rb) { PowerPCCPU *cpu = env_archcpu(env); target_ulong rt = 0; @@ -331,7 +358,7 @@ target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb) return rt; } -target_ulong helper_find_slb_vsid(CPUPPCState *env, target_ulong rb) +target_ulong helper_SLBFEE(CPUPPCState *env, target_ulong rb) { PowerPCCPU *cpu = env_archcpu(env); target_ulong rt = 0; @@ -343,7 +370,7 @@ target_ulong helper_find_slb_vsid(CPUPPCState *env, target_ulong rb) return rt; } -target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) +target_ulong helper_SLBMFEV(CPUPPCState *env, target_ulong rb) { PowerPCCPU *cpu = env_archcpu(env); target_ulong rt = 0; @@ -670,15 +697,15 @@ static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu, /* Page address translation */ qemu_log_mask(CPU_LOG_MMU, - "htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx - " hash " TARGET_FMT_plx "\n", + "htab_base " HWADDR_FMT_plx " htab_mask " HWADDR_FMT_plx + " hash " HWADDR_FMT_plx "\n", ppc_hash64_hpt_base(cpu), ppc_hash64_hpt_mask(cpu), hash); /* Primary PTEG lookup */ qemu_log_mask(CPU_LOG_MMU, - "0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + "0 htab=" HWADDR_FMT_plx "/" HWADDR_FMT_plx " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", + " hash=" HWADDR_FMT_plx "\n", ppc_hash64_hpt_base(cpu), ppc_hash64_hpt_mask(cpu), vsid, ptem, hash); ptex = ppc_hash64_pteg_search(cpu, hash, sps, ptem, pte, pshift); @@ -687,9 +714,9 @@ static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu, /* Secondary PTEG lookup */ ptem |= HPTE64_V_SECONDARY; qemu_log_mask(CPU_LOG_MMU, - "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + "1 htab=" HWADDR_FMT_plx "/" HWADDR_FMT_plx " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", ppc_hash64_hpt_base(cpu), + " hash=" HWADDR_FMT_plx "\n", ppc_hash64_hpt_base(cpu), ppc_hash64_hpt_mask(cpu), vsid, ptem, ~hash); ptex = ppc_hash64_pteg_search(cpu, ~hash, sps, ptem, pte, pshift); @@ -743,7 +770,8 @@ static bool ppc_hash64_use_vrma(CPUPPCState *env) } } -static void ppc_hash64_set_isi(CPUState *cs, int mmu_idx, uint64_t error_code) +static void ppc_hash64_set_isi(CPUState *cs, int mmu_idx, uint64_t slb_vsid, + uint64_t error_code) { CPUPPCState *env = &POWERPC_CPU(cs)->env; bool vpm; @@ -755,13 +783,15 @@ static void ppc_hash64_set_isi(CPUState *cs, int mmu_idx, uint64_t error_code) } if (vpm && !mmuidx_hv(mmu_idx)) { cs->exception_index = POWERPC_EXCP_HISI; + env->spr[SPR_ASDR] = slb_vsid; } else { cs->exception_index = POWERPC_EXCP_ISI; } env->error_code = error_code; } -static void ppc_hash64_set_dsi(CPUState *cs, int mmu_idx, uint64_t dar, uint64_t dsisr) +static void ppc_hash64_set_dsi(CPUState *cs, int mmu_idx, uint64_t slb_vsid, + uint64_t dar, uint64_t dsisr) { CPUPPCState *env = &POWERPC_CPU(cs)->env; bool vpm; @@ -775,6 +805,7 @@ static void ppc_hash64_set_dsi(CPUState *cs, int mmu_idx, uint64_t dar, uint64_t cs->exception_index = POWERPC_EXCP_HDSI; env->spr[SPR_HDAR] = dar; env->spr[SPR_HDSISR] = dsisr; + env->spr[SPR_ASDR] = slb_vsid; } else { cs->exception_index = POWERPC_EXCP_DSI; env->spr[SPR_DAR] = dar; @@ -843,12 +874,46 @@ static target_ulong rmls_limit(PowerPCCPU *cpu) return rma_sizes[rmls]; } -static int build_vrma_slbe(PowerPCCPU *cpu, ppc_slb_t *slb) +/* Return the LLP in SLB_VSID format */ +static uint64_t get_vrma_llp(PowerPCCPU *cpu) { 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); + uint64_t llp; + + if (env->mmu_model == POWERPC_MMU_3_00) { + ppc_v3_pate_t pate; + uint64_t ps, l, lp; + + /* + * ISA v3.0 removes the LPCR[VRMASD] field and puts the VRMA base + * page size (L||LP equivalent) in the PS field in the HPT partition + * table entry. + */ + if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { + error_report("Bad VRMA with no partition table entry"); + return 0; + } + ps = PATE0_GET_PS(pate.dw0); + /* PS has L||LP in 3 consecutive bits, put them into SLB LLP format */ + l = (ps >> 2) & 0x1; + lp = ps & 0x3; + llp = (l << SLB_VSID_L_SHIFT) | (lp << SLB_VSID_LP_SHIFT); + + } else { + uint64_t lpcr = env->spr[SPR_LPCR]; + target_ulong vrmasd = (lpcr & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT; + + /* VRMASD LLP matches SLB format, just shift and mask it */ + llp = (vrmasd << SLB_VSID_LP_SHIFT) & SLB_VSID_LLP_MASK; + } + + return llp; +} + +static int build_vrma_slbe(PowerPCCPU *cpu, ppc_slb_t *slb) +{ + uint64_t llp = get_vrma_llp(cpu); + target_ulong vsid = SLB_VSID_VRMA | llp; int i; for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { @@ -866,8 +931,7 @@ static int build_vrma_slbe(PowerPCCPU *cpu, ppc_slb_t *slb) } } - error_report("Bad page size encoding in LPCR[VRMASD]; LPCR=0x" - TARGET_FMT_lx, lpcr); + error_report("Bad VRMA page size encoding 0x" TARGET_FMT_lx, llp); return -1; } @@ -936,13 +1000,13 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, } switch (access_type) { case MMU_INST_FETCH: - ppc_hash64_set_isi(cs, mmu_idx, SRR1_PROTFAULT); + ppc_hash64_set_isi(cs, mmu_idx, 0, SRR1_PROTFAULT); break; case MMU_DATA_LOAD: - ppc_hash64_set_dsi(cs, mmu_idx, eaddr, DSISR_PROTFAULT); + ppc_hash64_set_dsi(cs, mmu_idx, 0, eaddr, DSISR_PROTFAULT); break; case MMU_DATA_STORE: - ppc_hash64_set_dsi(cs, mmu_idx, eaddr, + ppc_hash64_set_dsi(cs, mmu_idx, 0, eaddr, DSISR_PROTFAULT | DSISR_ISSTORE); break; default: @@ -995,7 +1059,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, /* 3. Check for segment level no-execute violation */ if (access_type == MMU_INST_FETCH && (slb->vsid & SLB_VSID_N)) { if (guest_visible) { - ppc_hash64_set_isi(cs, mmu_idx, SRR1_NOEXEC_GUARD); + ppc_hash64_set_isi(cs, mmu_idx, slb->vsid, SRR1_NOEXEC_GUARD); } return false; } @@ -1008,13 +1072,14 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, } switch (access_type) { case MMU_INST_FETCH: - ppc_hash64_set_isi(cs, mmu_idx, SRR1_NOPTE); + ppc_hash64_set_isi(cs, mmu_idx, slb->vsid, SRR1_NOPTE); break; case MMU_DATA_LOAD: - ppc_hash64_set_dsi(cs, mmu_idx, eaddr, DSISR_NOPTE); + ppc_hash64_set_dsi(cs, mmu_idx, slb->vsid, eaddr, DSISR_NOPTE); break; case MMU_DATA_STORE: - ppc_hash64_set_dsi(cs, mmu_idx, eaddr, DSISR_NOPTE | DSISR_ISSTORE); + ppc_hash64_set_dsi(cs, mmu_idx, slb->vsid, eaddr, + DSISR_NOPTE | DSISR_ISSTORE); break; default: g_assert_not_reached(); @@ -1048,7 +1113,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, if (PAGE_EXEC & ~amr_prot) { srr1 |= SRR1_IAMR; /* Access violates virt pg class key prot */ } - ppc_hash64_set_isi(cs, mmu_idx, srr1); + ppc_hash64_set_isi(cs, mmu_idx, slb->vsid, srr1); } else { int dsisr = 0; if (need_prot & ~pp_prot) { @@ -1060,7 +1125,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, if (need_prot & ~amr_prot) { dsisr |= DSISR_AMR; } - ppc_hash64_set_dsi(cs, mmu_idx, eaddr, dsisr); + ppc_hash64_set_dsi(cs, mmu_idx, slb->vsid, eaddr, dsisr); } return false; } diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index 1496955d38..de653fcae5 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -41,8 +41,10 @@ void ppc_hash64_finalize(PowerPCCPU *cpu); #define SLB_VSID_KP 0x0000000000000400ULL #define SLB_VSID_N 0x0000000000000200ULL /* no-execute */ #define SLB_VSID_L 0x0000000000000100ULL +#define SLB_VSID_L_SHIFT PPC_BIT_NR(55) #define SLB_VSID_C 0x0000000000000080ULL /* class */ #define SLB_VSID_LP 0x0000000000000030ULL +#define SLB_VSID_LP_SHIFT PPC_BIT_NR(59) #define SLB_VSID_ATTR 0x0000000000000FFFULL #define SLB_VSID_LLP_MASK (SLB_VSID_L | SLB_VSID_LP) #define SLB_VSID_4K 0x0000000000000000ULL @@ -58,6 +60,9 @@ void ppc_hash64_finalize(PowerPCCPU *cpu); #define SDR_64_HTABSIZE 0x000000000000001FULL #define PATE0_HTABORG 0x0FFFFFFFFFFC0000ULL +#define PATE0_PS PPC_BITMASK(56, 58) +#define PATE0_GET_PS(dw0) (((dw0) & PATE0_PS) >> PPC_BIT_NR(58)) + #define HPTES_PER_GROUP 8 #define HASH_PTE_SIZE_64 16 #define HASH_PTEG_SIZE_64 (HASH_PTE_SIZE_64 * HPTES_PER_GROUP) diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 21ac958e48..690dff7a49 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -145,6 +145,13 @@ static void ppc_radix64_raise_hsi(PowerPCCPU *cpu, MMUAccessType access_type, CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; + env->error_code = 0; + if (cause & DSISR_PRTABLE_FAULT) { + /* HDSI PRTABLE_FAULT gets the originating access type in error_code */ + env->error_code = access_type; + access_type = MMU_DATA_LOAD; + } + qemu_log_mask(CPU_LOG_MMU, "%s for %s @0x%"VADDR_PRIx" 0x%" HWADDR_PRIx" cause %08x\n", __func__, access_str(access_type), @@ -166,7 +173,6 @@ static void ppc_radix64_raise_hsi(PowerPCCPU *cpu, MMUAccessType access_type, env->spr[SPR_HDSISR] = cause; env->spr[SPR_HDAR] = eaddr; env->spr[SPR_ASDR] = g_raddr; - env->error_code = 0; break; default: g_assert_not_reached(); @@ -213,39 +219,68 @@ static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, return false; } -static void ppc_radix64_set_rc(PowerPCCPU *cpu, MMUAccessType access_type, - uint64_t pte, hwaddr pte_addr, int *prot) +static int ppc_radix64_check_rc(MMUAccessType access_type, uint64_t pte) { - CPUState *cs = CPU(cpu); - uint64_t npte; + switch (access_type) { + case MMU_DATA_STORE: + if (!(pte & R_PTE_C)) { + break; + } + /* fall through */ + case MMU_INST_FETCH: + case MMU_DATA_LOAD: + if (!(pte & R_PTE_R)) { + break; + } - npte = pte | R_PTE_R; /* Always set reference bit */ - - if (access_type == MMU_DATA_STORE) { /* Store/Write */ - npte |= R_PTE_C; /* Set change bit */ - } else { - /* - * Treat the page as read-only for now, so that a later write - * will pass through this function again to set the C bit. - */ - *prot &= ~PAGE_WRITE; + /* R/C bits are already set appropriately for this access */ + return 0; } - if (pte ^ npte) { /* If pte has changed then write it back */ - stq_phys(cs->as, pte_addr, npte); + return 1; +} + +static bool ppc_radix64_is_valid_level(int level, int psize, uint64_t nls) +{ + bool ret; + + /* + * Check if this is a valid level, according to POWER9 and POWER10 + * Processor User's Manuals, sections 4.10.4.1 and 5.10.6.1, respectively: + * Supported Radix Tree Configurations and Resulting Page Sizes. + * + * Note: these checks are specific to POWER9 and POWER10 CPUs. Any future + * CPUs that supports a different Radix MMU configuration will need their + * own implementation. + */ + switch (level) { + case 0: /* Root Page Dir */ + ret = psize == 52 && nls == 13; + break; + case 1: + case 2: + ret = nls == 9; + break; + case 3: + ret = nls == 9 || nls == 5; + break; + default: + ret = false; } + + if (unlikely(!ret)) { + qemu_log_mask(LOG_GUEST_ERROR, "invalid radix configuration: " + "level %d size %d nls %"PRIu64"\n", + level, psize, nls); + } + return ret; } static int ppc_radix64_next_level(AddressSpace *as, vaddr eaddr, uint64_t *pte_addr, uint64_t *nls, int *psize, uint64_t *pte, int *fault_cause) { - uint64_t index, pde; - - if (*nls < 5) { /* Directory maps less than 2**5 entries */ - *fault_cause |= DSISR_R_BADCONFIG; - return 1; - } + uint64_t index, mask, nlb, pde; /* Read page entry from guest address space */ pde = ldq_phys(as, *pte_addr); @@ -260,7 +295,17 @@ static int ppc_radix64_next_level(AddressSpace *as, vaddr eaddr, *nls = pde & R_PDE_NLS; index = eaddr >> (*psize - *nls); /* Shift */ index &= ((1UL << *nls) - 1); /* Mask */ - *pte_addr = (pde & R_PDE_NLB) + (index * sizeof(pde)); + nlb = pde & R_PDE_NLB; + mask = MAKE_64BIT_MASK(0, *nls + 3); + + if (nlb & mask) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: misaligned page dir/table base: 0x%" PRIx64 + " page dir size: 0x%" PRIx64 "\n", + __func__, nlb, mask + 1); + nlb &= ~mask; + } + *pte_addr = nlb + index * sizeof(pde); } return 0; } @@ -270,19 +315,30 @@ static int ppc_radix64_walk_tree(AddressSpace *as, vaddr eaddr, hwaddr *raddr, int *psize, uint64_t *pte, int *fault_cause, hwaddr *pte_addr) { - uint64_t index, pde, rpn , mask; - - if (nls < 5) { /* Directory maps less than 2**5 entries */ - *fault_cause |= DSISR_R_BADCONFIG; - return 1; - } + uint64_t index, pde, rpn, mask; + int level = 0; index = eaddr >> (*psize - nls); /* Shift */ - index &= ((1UL << nls) - 1); /* Mask */ - *pte_addr = base_addr + (index * sizeof(pde)); + index &= ((1UL << nls) - 1); /* Mask */ + mask = MAKE_64BIT_MASK(0, nls + 3); + + if (base_addr & mask) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: misaligned page dir base: 0x%" PRIx64 + " page dir size: 0x%" PRIx64 "\n", + __func__, base_addr, mask + 1); + base_addr &= ~mask; + } + *pte_addr = base_addr + index * sizeof(pde); + do { int ret; + if (!ppc_radix64_is_valid_level(level++, *psize, nls)) { + *fault_cause |= DSISR_R_BADCONFIG; + return 1; + } + ret = ppc_radix64_next_level(as, eaddr, pte_addr, &nls, psize, &pde, fault_cause); if (ret) { @@ -317,17 +373,27 @@ static bool validate_pate(PowerPCCPU *cpu, uint64_t lpid, ppc_v3_pate_t *pate) } static int ppc_radix64_partition_scoped_xlate(PowerPCCPU *cpu, - MMUAccessType access_type, + MMUAccessType orig_access_type, vaddr eaddr, hwaddr g_raddr, ppc_v3_pate_t pate, hwaddr *h_raddr, int *h_prot, int *h_page_size, bool pde_addr, - int mmu_idx, bool guest_visible) + int mmu_idx, uint64_t lpid, + bool guest_visible) { + MMUAccessType access_type = orig_access_type; int fault_cause = 0; hwaddr pte_addr; uint64_t pte; + if (pde_addr) { + /* + * Translation of process-scoped tables/directories is performed as + * a read-access. + */ + access_type = MMU_DATA_LOAD; + } + qemu_log_mask(CPU_LOG_MMU, "%s for %s @0x%"VADDR_PRIx " mmu_idx %u 0x%"HWADDR_PRIx"\n", __func__, access_str(access_type), @@ -344,13 +410,31 @@ static int ppc_radix64_partition_scoped_xlate(PowerPCCPU *cpu, fault_cause |= DSISR_PRTABLE_FAULT; } if (guest_visible) { - ppc_radix64_raise_hsi(cpu, access_type, eaddr, g_raddr, fault_cause); + ppc_radix64_raise_hsi(cpu, orig_access_type, + eaddr, g_raddr, fault_cause); } return 1; } if (guest_visible) { - ppc_radix64_set_rc(cpu, access_type, pte, pte_addr, h_prot); + if (ppc_radix64_check_rc(access_type, pte)) { + /* + * Per ISA 3.1 Book III, 7.5.3 and 7.5.5, failure to set R/C during + * partition-scoped translation when effLPID = 0 results in normal + * (non-Hypervisor) Data and Instruction Storage Interrupts + * respectively. + * + * ISA 3.0 is ambiguous about this, but tests on POWER9 hardware + * seem to exhibit the same behavior. + */ + if (lpid > 0) { + ppc_radix64_raise_hsi(cpu, access_type, eaddr, g_raddr, + DSISR_ATOMIC_RC); + } else { + ppc_radix64_raise_si(cpu, access_type, eaddr, DSISR_ATOMIC_RC); + } + return 1; + } } return 0; @@ -379,11 +463,12 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, vaddr eaddr, uint64_t pid, ppc_v3_pate_t pate, hwaddr *g_raddr, int *g_prot, int *g_page_size, - int mmu_idx, bool guest_visible) + int mmu_idx, uint64_t lpid, + bool guest_visible) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - uint64_t offset, size, prtbe_addr, prtbe0, base_addr, nls, index, pte; + uint64_t offset, size, prtb, prtbe_addr, prtbe0, base_addr, nls, index, pte; int fault_cause = 0, h_page_size, h_prot; hwaddr h_raddr, pte_addr; int ret; @@ -393,9 +478,18 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, __func__, access_str(access_type), eaddr, mmu_idx, pid); + prtb = (pate.dw1 & PATE1_R_PRTB); + size = 1ULL << ((pate.dw1 & PATE1_R_PRTS) + 12); + if (prtb & (size - 1)) { + /* Process Table not properly aligned */ + if (guest_visible) { + ppc_radix64_raise_si(cpu, access_type, eaddr, DSISR_R_BADCONFIG); + } + return 1; + } + /* Index Process Table by PID to Find Corresponding Process Table Entry */ offset = pid * sizeof(struct prtb_entry); - size = 1ULL << ((pate.dw1 & PATE1_R_PRTS) + 12); if (offset >= size) { /* offset exceeds size of the process table */ if (guest_visible) { @@ -403,7 +497,7 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, } return 1; } - prtbe_addr = (pate.dw1 & PATE1_R_PRTB) + offset; + prtbe_addr = prtb + offset; if (vhyp_flat_addressing(cpu)) { prtbe0 = ldq_phys(cs->as, prtbe_addr); @@ -416,11 +510,11 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, * is only used to translate the effective addresses of the * process table entries. */ - ret = ppc_radix64_partition_scoped_xlate(cpu, 0, eaddr, prtbe_addr, - pate, &h_raddr, &h_prot, - &h_page_size, true, - /* mmu_idx is 5 because we're translating from hypervisor scope */ - 5, guest_visible); + /* mmu_idx is 5 because we're translating from hypervisor scope */ + ret = ppc_radix64_partition_scoped_xlate(cpu, access_type, eaddr, + prtbe_addr, pate, &h_raddr, + &h_prot, &h_page_size, true, + 5, lpid, guest_visible); if (ret) { return ret; } @@ -447,6 +541,7 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, } } else { uint64_t rpn, mask; + int level = 0; index = (eaddr & R_EADDR_MASK) >> (*g_page_size - nls); /* Shift */ index &= ((1UL << nls) - 1); /* Mask */ @@ -457,17 +552,25 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, * translation */ do { - ret = ppc_radix64_partition_scoped_xlate(cpu, 0, eaddr, pte_addr, - pate, &h_raddr, &h_prot, - &h_page_size, true, /* mmu_idx is 5 because we're translating from hypervisor scope */ - 5, guest_visible); + ret = ppc_radix64_partition_scoped_xlate(cpu, access_type, eaddr, + pte_addr, pate, &h_raddr, + &h_prot, &h_page_size, + true, 5, lpid, + guest_visible); if (ret) { return ret; } - ret = ppc_radix64_next_level(cs->as, eaddr & R_EADDR_MASK, &h_raddr, - &nls, g_page_size, &pte, &fault_cause); + if (!ppc_radix64_is_valid_level(level++, *g_page_size, nls)) { + fault_cause |= DSISR_R_BADCONFIG; + ret = 1; + } else { + ret = ppc_radix64_next_level(cs->as, eaddr & R_EADDR_MASK, + &h_raddr, &nls, g_page_size, + &pte, &fault_cause); + } + if (ret) { /* No valid pte */ if (guest_visible) { @@ -495,7 +598,11 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, } if (guest_visible) { - ppc_radix64_set_rc(cpu, access_type, pte, pte_addr, g_prot); + /* R/C bits not appropriately set for access */ + if (ppc_radix64_check_rc(access_type, pte)) { + ppc_radix64_raise_si(cpu, access_type, eaddr, DSISR_ATOMIC_RC); + return 1; + } } return 0; @@ -568,7 +675,7 @@ static bool ppc_radix64_xlate_impl(PowerPCCPU *cpu, vaddr eaddr, return false; } - /* Get Process Table */ + /* Get Partition Table */ if (cpu->vhyp) { PPCVirtualHypervisorClass *vhc; vhc = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); @@ -610,7 +717,8 @@ static bool ppc_radix64_xlate_impl(PowerPCCPU *cpu, vaddr eaddr, if (relocation) { int ret = ppc_radix64_process_scoped_xlate(cpu, access_type, eaddr, pid, pate, &g_raddr, &prot, - &psize, mmu_idx, guest_visible); + &psize, mmu_idx, lpid, + guest_visible); if (ret) { return false; } @@ -634,7 +742,8 @@ static bool ppc_radix64_xlate_impl(PowerPCCPU *cpu, vaddr eaddr, ret = ppc_radix64_partition_scoped_xlate(cpu, access_type, eaddr, g_raddr, pate, raddr, &prot, &psize, false, - mmu_idx, guest_visible); + mmu_idx, lpid, + guest_visible); if (ret) { return false; } diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 89107a6af2..751403f1c8 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -28,7 +28,6 @@ #include "exec/log.h" #include "helper_regs.h" #include "qemu/error-report.h" -#include "qemu/main-loop.h" #include "qemu/qemu-print.h" #include "internal.h" #include "mmu-book3s-v3.h" @@ -252,7 +251,7 @@ static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, } if (best != -1) { done: - qemu_log_mask(CPU_LOG_MMU, "found TLB at addr " TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, "found TLB at addr " HWADDR_FMT_plx " prot=%01x ret=%d\n", ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); /* Update page flags */ @@ -328,7 +327,7 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, ctx->prot = prot; ret = check_prot(ctx->prot, access_type); if (ret == 0) { - qemu_log_mask(CPU_LOG_MMU, "BAT %d match: r " TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, "BAT %d match: r " HWADDR_FMT_plx " prot=%c%c\n", i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', ctx->prot & PAGE_WRITE ? 'W' : '-'); @@ -403,9 +402,9 @@ static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, /* Check if instruction fetch is allowed, if needed */ if (type != ACCESS_CODE || ctx->nx == 0) { /* Page address translation */ - qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx - " htab_mask " TARGET_FMT_plx - " hash " TARGET_FMT_plx "\n", + qemu_log_mask(CPU_LOG_MMU, "htab_base " HWADDR_FMT_plx + " htab_mask " HWADDR_FMT_plx + " hash " HWADDR_FMT_plx "\n", ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); ctx->hash[0] = hash; ctx->hash[1] = ~hash; @@ -420,7 +419,7 @@ static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, hwaddr curaddr; uint32_t a0, a1, a2, a3; - qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx + qemu_log("Page table: " HWADDR_FMT_plx " len " HWADDR_FMT_plx "\n", ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu) + 0x80); for (curaddr = ppc_hash32_hpt_base(cpu); @@ -432,7 +431,7 @@ static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, a2 = ldl_phys(cs->as, curaddr + 8); a3 = ldl_phys(cs->as, curaddr + 12); if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { - qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n", + qemu_log(HWADDR_FMT_plx ": %08x %08x %08x %08x\n", curaddr, a0, a1, a2, a3); } } @@ -489,16 +488,15 @@ static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, } /* Generic TLB check function for embedded PowerPC implementations */ -int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddrp, - target_ulong address, uint32_t pid, int ext, - int i) +static bool ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddrp, + target_ulong address, uint32_t pid, int i) { target_ulong mask; /* Check valid flag */ if (!(tlb->prot & PAGE_VALID)) { - return -1; + return false; } mask = ~(tlb->size - 1); qemu_log_mask(CPU_LOG_MMU, "%s: TLB %d address " TARGET_FMT_lx @@ -507,19 +505,30 @@ int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, mask, (uint32_t)tlb->PID, tlb->prot); /* Check PID */ if (tlb->PID != 0 && tlb->PID != pid) { - return -1; + return false; } /* Check effective address */ if ((address & mask) != tlb->EPN) { - return -1; + return false; } *raddrp = (tlb->RPN & mask) | (address & ~mask); - if (ext) { - /* Extend the physical address to 36 bits */ - *raddrp |= (uint64_t)(tlb->RPN & 0xF) << 32; - } + return true; +} - return 0; +/* Generic TLB search function for PowerPC embedded implementations */ +int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid) +{ + ppcemb_tlb_t *tlb; + hwaddr raddr; + int i; + + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, i)) { + return i; + } + } + return -1; } static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, @@ -535,8 +544,8 @@ static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, pr = FIELD_EX64(env->msr, MSR, PR); for (i = 0; i < env->nb_tlb; i++) { tlb = &env->tlb.tlbe[i]; - if (ppcemb_tlb_check(env, tlb, &raddr, address, - env->spr[SPR_40x_PID], 0, i) < 0) { + if (!ppcemb_tlb_check(env, tlb, &raddr, address, + env->spr[SPR_40x_PID], i)) { continue; } zsel = (tlb->attr >> 4) & 0xF; @@ -578,48 +587,53 @@ static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, if (ret >= 0) { ctx->raddr = raddr; qemu_log_mask(CPU_LOG_MMU, "%s: access granted " TARGET_FMT_lx - " => " TARGET_FMT_plx + " => " HWADDR_FMT_plx " %d %d\n", __func__, address, ctx->raddr, ctx->prot, ret); return 0; } } qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " TARGET_FMT_plx + " => " HWADDR_FMT_plx " %d %d\n", __func__, address, raddr, ctx->prot, ret); return ret; } +static bool mmubooke_check_pid(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddr, target_ulong addr, int i) +{ + if (ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID], i)) { + if (!env->nb_pids) { + /* Extend the physical address to 36 bits */ + *raddr |= (uint64_t)(tlb->RPN & 0xF) << 32; + } + return true; + } else if (!env->nb_pids) { + return false; + } + if (env->spr[SPR_BOOKE_PID1] && + ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID1], i)) { + return true; + } + if (env->spr[SPR_BOOKE_PID2] && + ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID2], i)) { + return true; + } + return false; +} + static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, hwaddr *raddr, int *prot, target_ulong address, MMUAccessType access_type, int i) { int prot2; - if (ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID], - !env->nb_pids, i) >= 0) { - goto found_tlb; + if (!mmubooke_check_pid(env, tlb, raddr, address, i)) { + qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__); + return -1; } - if (env->spr[SPR_BOOKE_PID1] && - ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID1], 0, i) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID2] && - ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID2], 0, i) >= 0) { - goto found_tlb; - } - - qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__); - return -1; - -found_tlb: - if (FIELD_EX64(env->msr, MSR, PR)) { prot2 = tlb->prot & 0xF; } else { @@ -666,19 +680,18 @@ static int mmubooke_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, if (ret >= 0) { ctx->raddr = raddr; qemu_log_mask(CPU_LOG_MMU, "%s: access granted " TARGET_FMT_lx - " => " TARGET_FMT_plx " %d %d\n", __func__, + " => " HWADDR_FMT_plx " %d %d\n", __func__, address, ctx->raddr, ctx->prot, ret); } else { qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " TARGET_FMT_plx " %d %d\n", __func__, + " => " HWADDR_FMT_plx " %d %d\n", __func__, address, raddr, ctx->prot, ret); } return ret; } -hwaddr booke206_tlb_to_page_size(CPUPPCState *env, - ppcmas_tlb_t *tlb) +hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb) { int tlbm_size; @@ -688,9 +701,8 @@ hwaddr booke206_tlb_to_page_size(CPUPPCState *env, } /* TLB check function for MAS based SoftTLBs */ -int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, - hwaddr *raddrp, target_ulong address, - uint32_t pid) +int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, + target_ulong address, uint32_t pid) { hwaddr mask; uint32_t tlb_pid; @@ -811,7 +823,8 @@ static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, } } - qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__); + qemu_log_mask(CPU_LOG_MMU, "%s: No TLB entry found for effective address " + "0x" TARGET_FMT_lx "\n", __func__, address); return -1; found_tlb: @@ -893,11 +906,11 @@ found_tlb: if (ret >= 0) { ctx->raddr = raddr; qemu_log_mask(CPU_LOG_MMU, "%s: access granted " TARGET_FMT_lx - " => " TARGET_FMT_plx " %d %d\n", __func__, address, + " => " HWADDR_FMT_plx " %d %d\n", __func__, address, ctx->raddr, ctx->prot, ret); } else { qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " TARGET_FMT_plx " %d %d\n", __func__, address, + " => " HWADDR_FMT_plx " %d %d\n", __func__, address, raddr, ctx->prot, ret); } @@ -916,10 +929,12 @@ static void mmubooke_dump_mmu(CPUPPCState *env) ppcemb_tlb_t *entry; int i; +#ifdef CONFIG_KVM if (kvm_enabled() && !env->kvm_sw_tlb) { qemu_printf("Cannot access KVM TLB\n"); return; } +#endif qemu_printf("\nTLB:\n"); qemu_printf("Effective Physical Size PID Prot " @@ -979,7 +994,7 @@ static void mmubooke206_dump_one_tlb(CPUPPCState *env, int tlbn, int offset, pa = entry->mas7_3 & ~(size - 1); qemu_printf("0x%016" PRIx64 " 0x%016" PRIx64 " %4s %-5u %1u S%c%c%c" - "U%c%c%c %c%c%c%c%c U%c%c%c%c\n", + " U%c%c%c %c%c%c%c%c U%c%c%c%c\n", (uint64_t)ea, (uint64_t)pa, book3e_tsize_to_str[tsize], (entry->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT, @@ -1007,10 +1022,12 @@ static void mmubooke206_dump_mmu(CPUPPCState *env) int offset = 0; int i; +#ifdef CONFIG_KVM if (kvm_enabled() && !env->kvm_sw_tlb) { qemu_printf("Cannot access KVM TLB\n"); return; } +#endif for (i = 0; i < BOOKE206_MAX_TLBN; i++) { int size = booke206_tlb_size(env, i); @@ -1544,9 +1561,9 @@ hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) * mapped by code TLBs, so we also try a MMU_INST_FETCH. */ if (ppc_xlate(cpu, addr, MMU_DATA_LOAD, &raddr, &s, &p, - cpu_mmu_index(&cpu->env, false), false) || + ppc_env_mmu_index(&cpu->env, false), false) || ppc_xlate(cpu, addr, MMU_INST_FETCH, &raddr, &s, &p, - cpu_mmu_index(&cpu->env, true), false)) { + ppc_env_mmu_index(&cpu->env, true), false)) { return raddr & TARGET_PAGE_MASK; } return -1; diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 15239dc95b..c071b4d5e2 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -28,7 +28,6 @@ #include "exec/log.h" #include "helper_regs.h" #include "qemu/error-report.h" -#include "qemu/main-loop.h" #include "qemu/qemu-print.h" #include "internal.h" #include "mmu-book3s-v3.h" @@ -112,27 +111,6 @@ static void ppc6xx_tlb_store(CPUPPCState *env, target_ulong EPN, int way, env->last_way = way; } -/* Generic TLB search function for PowerPC embedded implementations */ -static int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, - uint32_t pid) -{ - ppcemb_tlb_t *tlb; - hwaddr raddr; - int i, ret; - - /* Default return value is no match */ - ret = -1; - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, 0, i) == 0) { - ret = i; - break; - } - } - - return ret; -} - /* Helpers specific to PowerPC 40x implementations */ static inline void ppc4xx_tlb_invalidate_all(CPUPPCState *env) { @@ -168,15 +146,6 @@ static void booke206_flush_tlb(CPUPPCState *env, int flags, tlb_flush(env_cpu(env)); } -static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, MMUAccessType access_type, - int type) -{ - return get_physical_address_wtlb(env, ctx, eaddr, access_type, type, 0); -} - - - /*****************************************************************************/ /* BATs management */ #if !defined(FLUSH_ALL_TLBS) @@ -429,6 +398,160 @@ void helper_tlbie(CPUPPCState *env, target_ulong addr) ppc_tlb_invalidate_one(env, addr); } +#if defined(TARGET_PPC64) + +/* Invalidation Selector */ +#define TLBIE_IS_VA 0 +#define TLBIE_IS_PID 1 +#define TLBIE_IS_LPID 2 +#define TLBIE_IS_ALL 3 + +/* Radix Invalidation Control */ +#define TLBIE_RIC_TLB 0 +#define TLBIE_RIC_PWC 1 +#define TLBIE_RIC_ALL 2 +#define TLBIE_RIC_GRP 3 + +/* Radix Actual Page sizes */ +#define TLBIE_R_AP_4K 0 +#define TLBIE_R_AP_64K 5 +#define TLBIE_R_AP_2M 1 +#define TLBIE_R_AP_1G 2 + +/* RB field masks */ +#define TLBIE_RB_EPN_MASK PPC_BITMASK(0, 51) +#define TLBIE_RB_IS_MASK PPC_BITMASK(52, 53) +#define TLBIE_RB_AP_MASK PPC_BITMASK(56, 58) + +void helper_tlbie_isa300(CPUPPCState *env, target_ulong rb, target_ulong rs, + uint32_t flags) +{ + unsigned ric = (flags & TLBIE_F_RIC_MASK) >> TLBIE_F_RIC_SHIFT; + /* + * With the exception of the checks for invalid instruction forms, + * PRS is currently ignored, because we don't know if a given TLB entry + * is process or partition scoped. + */ + bool prs = flags & TLBIE_F_PRS; + bool r = flags & TLBIE_F_R; + bool local = flags & TLBIE_F_LOCAL; + bool effR; + unsigned is = extract64(rb, PPC_BIT_NR(53), 2); + unsigned ap; /* actual page size */ + target_ulong addr, pgoffs_mask; + + qemu_log_mask(CPU_LOG_MMU, + "%s: local=%d addr=" TARGET_FMT_lx " ric=%u prs=%d r=%d is=%u\n", + __func__, local, rb & TARGET_PAGE_MASK, ric, prs, r, is); + + effR = FIELD_EX64(env->msr, MSR, HV) ? r : env->spr[SPR_LPCR] & LPCR_HR; + + /* Partial TLB invalidation is supported for Radix only for now. */ + if (!effR) { + goto inval_all; + } + + /* Check for invalid instruction forms (effR=1). */ + if (unlikely(ric == TLBIE_RIC_GRP || + ((ric == TLBIE_RIC_PWC || ric == TLBIE_RIC_ALL) && + is == TLBIE_IS_VA) || + (!prs && is == TLBIE_IS_PID))) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid instruction form: ric=%u prs=%d r=%d is=%u\n", + __func__, ric, prs, r, is); + goto invalid; + } + + /* We don't cache Page Walks. */ + if (ric == TLBIE_RIC_PWC) { + if (local) { + unsigned set = extract64(rb, PPC_BIT_NR(51), 12); + if (set != 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid set: %d\n", + __func__, set); + goto invalid; + } + } + return; + } + + /* + * Invalidation by LPID or PID is not supported, so fallback + * to full TLB flush in these cases. + */ + if (is != TLBIE_IS_VA) { + goto inval_all; + } + + /* + * The results of an attempt to invalidate a translation outside of + * quadrant 0 for Radix Tree translation (effR=1, RIC=0, PRS=1, IS=0, + * and EA 0:1 != 0b00) are boundedly undefined. + */ + if (unlikely(ric == TLBIE_RIC_TLB && prs && is == TLBIE_IS_VA && + (rb & R_EADDR_QUADRANT) != R_EADDR_QUADRANT0)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: attempt to invalidate a translation outside of quadrant 0\n", + __func__); + goto inval_all; + } + + assert(is == TLBIE_IS_VA); + assert(ric == TLBIE_RIC_TLB || ric == TLBIE_RIC_ALL); + + ap = extract64(rb, PPC_BIT_NR(58), 3); + switch (ap) { + case TLBIE_R_AP_4K: + pgoffs_mask = 0xfffull; + break; + + case TLBIE_R_AP_64K: + pgoffs_mask = 0xffffull; + break; + + case TLBIE_R_AP_2M: + pgoffs_mask = 0x1fffffull; + break; + + case TLBIE_R_AP_1G: + pgoffs_mask = 0x3fffffffull; + break; + + default: + /* + * If the value specified in RS 0:31, RS 32:63, RB 54:55, RB 56:58, + * RB 44:51, or RB 56:63, when it is needed to perform the specified + * operation, is not supported by the implementation, the instruction + * is treated as if the instruction form were invalid. + */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid AP: %d\n", __func__, ap); + goto invalid; + } + + addr = rb & TLBIE_RB_EPN_MASK & ~pgoffs_mask; + + if (local) { + tlb_flush_page(env_cpu(env), addr); + } else { + tlb_flush_page_all_cpus(env_cpu(env), addr); + } + return; + +inval_all: + env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; + if (!local) { + env->tlb_need_flush |= TLB_NEED_GLOBAL_FLUSH; + } + return; + +invalid: + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_INVAL_INVAL, GETPC()); +} + +#endif + void helper_tlbiva(CPUPPCState *env, target_ulong addr) { /* tlbiva instruction only exists on BookE */ @@ -489,7 +612,7 @@ target_ulong helper_rac(CPUPPCState *env, target_ulong addr) */ nb_BATs = env->nb_BATs; env->nb_BATs = 0; - if (get_physical_address(env, &ctx, addr, 0, ACCESS_INT) == 0) { + if (get_physical_address_wtlb(env, &ctx, addr, 0, ACCESS_INT, 0) == 0) { ret = ctx.raddr; } env->nb_BATs = nb_BATs; @@ -626,12 +749,29 @@ target_ulong helper_4xx_tlbre_lo(CPUPPCState *env, target_ulong entry) return ret; } +static void ppcemb_tlb_flush(CPUState *cs, ppcemb_tlb_t *tlb) +{ + unsigned mmu_idx = 0; + + if (tlb->prot & 0xf) { + mmu_idx |= 0x1; + } + if ((tlb->prot >> 4) & 0xf) { + mmu_idx |= 0x2; + } + if (tlb->attr & 1) { + mmu_idx <<= 2; + } + + tlb_flush_range_by_mmuidx(cs, tlb->EPN, tlb->size, mmu_idx, + TARGET_LONG_BITS); +} + void helper_4xx_tlbwe_hi(CPUPPCState *env, target_ulong entry, target_ulong val) { CPUState *cs = env_cpu(env); ppcemb_tlb_t *tlb; - target_ulong page, end; qemu_log_mask(CPU_LOG_MMU, "%s entry %d val " TARGET_FMT_lx "\n", __func__, (int)entry, @@ -639,14 +779,11 @@ void helper_4xx_tlbwe_hi(CPUPPCState *env, target_ulong entry, entry &= PPC4XX_TLB_ENTRY_MASK; tlb = &env->tlb.tlbe[entry]; /* Invalidate previous TLB (if it's valid) */ - if (tlb->prot & PAGE_VALID) { - end = tlb->EPN + tlb->size; + if ((tlb->prot & PAGE_VALID) && tlb->PID == env->spr[SPR_40x_PID]) { qemu_log_mask(CPU_LOG_MMU, "%s: invalidate old TLB %d start " TARGET_FMT_lx " end " TARGET_FMT_lx "\n", __func__, - (int)entry, tlb->EPN, end); - for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) { - tlb_flush_page(cs, page); - } + (int)entry, tlb->EPN, tlb->EPN + tlb->size); + ppcemb_tlb_flush(cs, tlb); } tlb->size = booke_tlb_to_page_size((val >> PPC4XX_TLBHI_SIZE_SHIFT) & PPC4XX_TLBHI_SIZE_MASK); @@ -672,7 +809,7 @@ void helper_4xx_tlbwe_hi(CPUPPCState *env, target_ulong entry, tlb->prot &= ~PAGE_VALID; } tlb->PID = env->spr[SPR_40x_PID]; /* PID */ - qemu_log_mask(CPU_LOG_MMU, "%s: set up TLB %d RPN " TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, "%s: set up TLB %d RPN " HWADDR_FMT_plx " EPN " TARGET_FMT_lx " size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__, (int)entry, tlb->RPN, tlb->EPN, tlb->size, @@ -680,27 +817,25 @@ void helper_4xx_tlbwe_hi(CPUPPCState *env, target_ulong entry, tlb->prot & PAGE_WRITE ? 'w' : '-', tlb->prot & PAGE_EXEC ? 'x' : '-', tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID); - /* Invalidate new TLB (if valid) */ - if (tlb->prot & PAGE_VALID) { - end = tlb->EPN + tlb->size; - qemu_log_mask(CPU_LOG_MMU, "%s: invalidate TLB %d start " - TARGET_FMT_lx " end " TARGET_FMT_lx "\n", __func__, - (int)entry, tlb->EPN, end); - for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) { - tlb_flush_page(cs, page); - } - } } void helper_4xx_tlbwe_lo(CPUPPCState *env, target_ulong entry, target_ulong val) { + CPUState *cs = env_cpu(env); ppcemb_tlb_t *tlb; qemu_log_mask(CPU_LOG_MMU, "%s entry %i val " TARGET_FMT_lx "\n", __func__, (int)entry, val); entry &= PPC4XX_TLB_ENTRY_MASK; tlb = &env->tlb.tlbe[entry]; + /* Invalidate previous TLB (if it's valid) */ + if ((tlb->prot & PAGE_VALID) && tlb->PID == env->spr[SPR_40x_PID]) { + qemu_log_mask(CPU_LOG_MMU, "%s: invalidate old TLB %d start " + TARGET_FMT_lx " end " TARGET_FMT_lx "\n", __func__, + (int)entry, tlb->EPN, tlb->EPN + tlb->size); + ppcemb_tlb_flush(cs, tlb); + } tlb->attr = val & PPC4XX_TLBLO_ATTR_MASK; tlb->RPN = val & PPC4XX_TLBLO_RPN_MASK; tlb->prot = PAGE_READ; @@ -710,7 +845,7 @@ void helper_4xx_tlbwe_lo(CPUPPCState *env, target_ulong entry, if (val & PPC4XX_TLBLO_WR) { tlb->prot |= PAGE_WRITE; } - qemu_log_mask(CPU_LOG_MMU, "%s: set up TLB %d RPN " TARGET_FMT_plx + qemu_log_mask(CPU_LOG_MMU, "%s: set up TLB %d RPN " HWADDR_FMT_plx " EPN " TARGET_FMT_lx " size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__, (int)entry, tlb->RPN, tlb->EPN, tlb->size, @@ -718,8 +853,6 @@ void helper_4xx_tlbwe_lo(CPUPPCState *env, target_ulong entry, tlb->prot & PAGE_WRITE ? 'w' : '-', tlb->prot & PAGE_EXEC ? 'x' : '-', tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID); - - env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; } target_ulong helper_4xx_tlbsx(CPUPPCState *env, target_ulong address) @@ -727,54 +860,61 @@ target_ulong helper_4xx_tlbsx(CPUPPCState *env, target_ulong address) return ppcemb_tlb_search(env, address, env->spr[SPR_40x_PID]); } +static bool mmubooke_pid_match(CPUPPCState *env, ppcemb_tlb_t *tlb) +{ + if (tlb->PID == env->spr[SPR_BOOKE_PID]) { + return true; + } + if (!env->nb_pids) { + return false; + } + + if (env->spr[SPR_BOOKE_PID1] && tlb->PID == env->spr[SPR_BOOKE_PID1]) { + return true; + } + if (env->spr[SPR_BOOKE_PID2] && tlb->PID == env->spr[SPR_BOOKE_PID2]) { + return true; + } + + return false; +} + /* PowerPC 440 TLB management */ void helper_440_tlbwe(CPUPPCState *env, uint32_t word, target_ulong entry, target_ulong value) { ppcemb_tlb_t *tlb; - target_ulong EPN, RPN, size; - int do_flush_tlbs; qemu_log_mask(CPU_LOG_MMU, "%s word %d entry %d value " TARGET_FMT_lx "\n", __func__, word, (int)entry, value); - do_flush_tlbs = 0; entry &= 0x3F; tlb = &env->tlb.tlbe[entry]; + + /* Invalidate previous TLB (if it's valid) */ + if ((tlb->prot & PAGE_VALID) && mmubooke_pid_match(env, tlb)) { + qemu_log_mask(CPU_LOG_MMU, "%s: invalidate old TLB %d start " + TARGET_FMT_lx " end " TARGET_FMT_lx "\n", __func__, + (int)entry, tlb->EPN, tlb->EPN + tlb->size); + ppcemb_tlb_flush(env_cpu(env), tlb); + } + switch (word) { default: /* Just here to please gcc */ case 0: - EPN = value & 0xFFFFFC00; - if ((tlb->prot & PAGE_VALID) && EPN != tlb->EPN) { - do_flush_tlbs = 1; - } - tlb->EPN = EPN; - size = booke_tlb_to_page_size((value >> 4) & 0xF); - if ((tlb->prot & PAGE_VALID) && tlb->size < size) { - do_flush_tlbs = 1; - } - tlb->size = size; + tlb->EPN = value & 0xFFFFFC00; + tlb->size = booke_tlb_to_page_size((value >> 4) & 0xF); tlb->attr &= ~0x1; tlb->attr |= (value >> 8) & 1; if (value & 0x200) { tlb->prot |= PAGE_VALID; } else { - if (tlb->prot & PAGE_VALID) { - tlb->prot &= ~PAGE_VALID; - do_flush_tlbs = 1; - } + tlb->prot &= ~PAGE_VALID; } tlb->PID = env->spr[SPR_440_MMUCR] & 0x000000FF; - if (do_flush_tlbs) { - tlb_flush(env_cpu(env)); - } break; case 1: - RPN = value & 0xFFFFFC0F; - if ((tlb->prot & PAGE_VALID) && tlb->RPN != RPN) { - tlb_flush(env_cpu(env)); - } - tlb->RPN = RPN; + tlb->RPN = value & 0xFFFFFC0F; break; case 2: tlb->attr = (tlb->attr & 0x1) | (value & 0x0000FF00); diff --git a/target/ppc/power8-pmu-regs.c.inc b/target/ppc/power8-pmu-regs.c.inc index 2bab6cece7..4956a8b350 100644 --- a/target/ppc/power8-pmu-regs.c.inc +++ b/target/ppc/power8-pmu-regs.c.inc @@ -16,13 +16,13 @@ * Checks whether the Group A SPR (MMCR0, MMCR2, MMCRA, and the * PMCs) has problem state read access. * - * Read acccess is granted for all PMCC values but 0b01, where a + * Read access is granted for all PMCC values but 0b01, where a * Facility Unavailable Interrupt will occur. */ static bool spr_groupA_read_allowed(DisasContext *ctx) { if (!ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { - gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_PMU); return false; } @@ -33,7 +33,7 @@ static bool spr_groupA_read_allowed(DisasContext *ctx) * Checks whether the Group A SPR (MMCR0, MMCR2, MMCRA, and the * PMCs) has problem state write access. * - * Write acccess is granted for PMCC values 0b10 and 0b11. Userspace + * Write access is granted for PMCC values 0b10 and 0b11. Userspace * writing with PMCC 0b00 will generate a Hypervisor Emulation * Assistance Interrupt. Userspace writing with PMCC 0b01 will * generate a Facility Unavailable Interrupt. @@ -46,10 +46,10 @@ static bool spr_groupA_write_allowed(DisasContext *ctx) if (ctx->mmcr0_pmcc1) { /* PMCC = 0b01 */ - gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_PMU); } else { /* PMCC = 0b00 */ - gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + gen_hvpriv_exception(ctx, POWERPC_EXCP_PRIV_REG); } return false; @@ -58,8 +58,6 @@ static bool spr_groupA_write_allowed(DisasContext *ctx) /* * Helper function to avoid code repetition between MMCR0 and * MMCR2 problem state write functions. - * - * 'ret' must be tcg_temp_freed() by the caller. */ static TCGv masked_gprn_for_spr_write(int gprn, int sprn, uint64_t spr_mask) @@ -77,8 +75,6 @@ static TCGv masked_gprn_for_spr_write(int gprn, int sprn, /* Add the masked gprn bits into 'ret' */ tcg_gen_or_tl(ret, ret, t0); - tcg_temp_free(t0); - return ret; } @@ -100,8 +96,6 @@ void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn) gen_load_spr(t0, SPR_POWER_MMCR0); tcg_gen_andi_tl(t0, t0, MMCR0_UREG_MASK); tcg_gen_mov_tl(cpu_gpr[gprn], t0); - - tcg_temp_free(t0); } static void write_MMCR0_common(DisasContext *ctx, TCGv val) @@ -109,10 +103,10 @@ static void write_MMCR0_common(DisasContext *ctx, TCGv val) /* * helper_store_mmcr0 will make clock based operations that * will cause 'bad icount read' errors if we do not execute - * gen_icount_io_start() beforehand. + * translator_io_start() beforehand. */ - gen_icount_io_start(ctx); - gen_helper_store_mmcr0(cpu_env, val); + translator_io_start(&ctx->base); + gen_helper_store_mmcr0(tcg_env, val); /* * End the translation block because MMCR0 writes can change @@ -137,8 +131,6 @@ void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn) masked_gprn = masked_gprn_for_spr_write(gprn, SPR_POWER_MMCR0, MMCR0_UREG_MASK); write_MMCR0_common(ctx, masked_gprn); - - tcg_temp_free(masked_gprn); } void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn) @@ -164,8 +156,6 @@ void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn) gen_load_spr(t0, SPR_POWER_MMCR2); tcg_gen_andi_tl(t0, t0, MMCR2_UREG_MASK); tcg_gen_mov_tl(cpu_gpr[gprn], t0); - - tcg_temp_free(t0); } void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn) @@ -183,18 +173,14 @@ void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn) masked_gprn = masked_gprn_for_spr_write(gprn, SPR_POWER_MMCR2, MMCR2_UREG_MASK); gen_store_spr(SPR_POWER_MMCR2, masked_gprn); - - tcg_temp_free(masked_gprn); } void spr_read_PMC(DisasContext *ctx, int gprn, int sprn) { - TCGv_i32 t_sprn = tcg_const_i32(sprn); + TCGv_i32 t_sprn = tcg_constant_i32(sprn); - gen_icount_io_start(ctx); - gen_helper_read_pmc(cpu_gpr[gprn], cpu_env, t_sprn); - - tcg_temp_free_i32(t_sprn); + translator_io_start(&ctx->base); + gen_helper_read_pmc(cpu_gpr[gprn], tcg_env, t_sprn); } void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn) @@ -214,7 +200,7 @@ void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn) * Interrupt. */ if (ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { - gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_PMU); return; } @@ -224,12 +210,10 @@ void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn) void spr_write_PMC(DisasContext *ctx, int sprn, int gprn) { - TCGv_i32 t_sprn = tcg_const_i32(sprn); + TCGv_i32 t_sprn = tcg_constant_i32(sprn); - gen_icount_io_start(ctx); - gen_helper_store_pmc(cpu_env, t_sprn, cpu_gpr[gprn]); - - tcg_temp_free_i32(t_sprn); + translator_io_start(&ctx->base); + gen_helper_store_pmc(tcg_env, t_sprn, cpu_gpr[gprn]); } void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn) @@ -249,7 +233,7 @@ void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn) * Interrupt. */ if (ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { - gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_PMU); return; } @@ -264,8 +248,8 @@ void spr_write_MMCR0(DisasContext *ctx, int sprn, int gprn) void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); - gen_helper_store_mmcr1(cpu_env, cpu_gpr[gprn]); + translator_io_start(&ctx->base); + gen_helper_store_mmcr1(tcg_env, cpu_gpr[gprn]); } #else void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn) diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c index beeab5c494..cbc5889d91 100644 --- a/target/ppc/power8-pmu.c +++ b/target/ppc/power8-pmu.c @@ -16,14 +16,12 @@ #include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qemu/error-report.h" -#include "qemu/main-loop.h" +#include "qemu/timer.h" #include "hw/ppc/ppc.h" #include "power8-pmu.h" #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) -#define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL - static bool pmc_has_overflow_enabled(CPUPPCState *env, int sprn) { if (sprn == SPR_POWER_PMC1) { @@ -33,7 +31,11 @@ static bool pmc_has_overflow_enabled(CPUPPCState *env, int sprn) return env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE; } -void pmu_update_summaries(CPUPPCState *env) +/* + * Called after MMCR0 or MMCR1 changes to update pmc_ins_cnt and pmc_cyc_cnt. + * hflags must subsequently be updated. + */ +static void pmu_update_summaries(CPUPPCState *env) { target_ulong mmcr0 = env->spr[SPR_POWER_MMCR0]; target_ulong mmcr1 = env->spr[SPR_POWER_MMCR1]; @@ -41,7 +43,7 @@ void pmu_update_summaries(CPUPPCState *env) int cyc_cnt = 0; if (mmcr0 & MMCR0_FC) { - goto hflags_calc; + goto out; } if (!(mmcr0 & MMCR0_FC14) && mmcr1 != 0) { @@ -75,10 +77,28 @@ void pmu_update_summaries(CPUPPCState *env) ins_cnt |= !(mmcr0 & MMCR0_FC56) << 5; cyc_cnt |= !(mmcr0 & MMCR0_FC56) << 6; - hflags_calc: + out: env->pmc_ins_cnt = ins_cnt; env->pmc_cyc_cnt = cyc_cnt; - env->hflags = deposit32(env->hflags, HFLAGS_INSN_CNT, 1, ins_cnt != 0); +} + +void pmu_mmcr01_updated(CPUPPCState *env) +{ + PowerPCCPU *cpu = env_archcpu(env); + + pmu_update_summaries(env); + hreg_update_pmu_hflags(env); + + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMAO) { + ppc_set_irq(cpu, PPC_INTERRUPT_PERFM, 1); + } else { + ppc_set_irq(cpu, PPC_INTERRUPT_PERFM, 0); + } + + /* + * Should this update overflow timers (if mmcr0 is updated) so they + * get set in cpu_post_load? + */ } static bool pmu_increment_insns(CPUPPCState *env, uint32_t num_insns) @@ -88,49 +108,47 @@ static bool pmu_increment_insns(CPUPPCState *env, uint32_t num_insns) bool overflow_triggered = false; target_ulong tmp; - if (unlikely(ins_cnt & 0x1e)) { - if (ins_cnt & (1 << 1)) { - tmp = env->spr[SPR_POWER_PMC1]; - tmp += num_insns; - if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMC1CE)) { - tmp = PMC_COUNTER_NEGATIVE_VAL; - overflow_triggered = true; - } - env->spr[SPR_POWER_PMC1] = tmp; + if (ins_cnt & (1 << 1)) { + tmp = env->spr[SPR_POWER_PMC1]; + tmp += num_insns; + if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMC1CE)) { + tmp = PMC_COUNTER_NEGATIVE_VAL; + overflow_triggered = true; } + env->spr[SPR_POWER_PMC1] = tmp; + } - if (ins_cnt & (1 << 2)) { - tmp = env->spr[SPR_POWER_PMC2]; + if (ins_cnt & (1 << 2)) { + tmp = env->spr[SPR_POWER_PMC2]; + tmp += num_insns; + if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) { + tmp = PMC_COUNTER_NEGATIVE_VAL; + overflow_triggered = true; + } + env->spr[SPR_POWER_PMC2] = tmp; + } + + if (ins_cnt & (1 << 3)) { + tmp = env->spr[SPR_POWER_PMC3]; + tmp += num_insns; + if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) { + tmp = PMC_COUNTER_NEGATIVE_VAL; + overflow_triggered = true; + } + env->spr[SPR_POWER_PMC3] = tmp; + } + + if (ins_cnt & (1 << 4)) { + target_ulong mmcr1 = env->spr[SPR_POWER_MMCR1]; + int sel = extract64(mmcr1, MMCR1_PMC4EVT_EXTR, MMCR1_EVT_SIZE); + if (sel == 0x02 || (env->spr[SPR_CTRL] & CTRL_RUN)) { + tmp = env->spr[SPR_POWER_PMC4]; tmp += num_insns; if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) { tmp = PMC_COUNTER_NEGATIVE_VAL; overflow_triggered = true; } - env->spr[SPR_POWER_PMC2] = tmp; - } - - if (ins_cnt & (1 << 3)) { - tmp = env->spr[SPR_POWER_PMC3]; - tmp += num_insns; - if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) { - tmp = PMC_COUNTER_NEGATIVE_VAL; - overflow_triggered = true; - } - env->spr[SPR_POWER_PMC3] = tmp; - } - - if (ins_cnt & (1 << 4)) { - target_ulong mmcr1 = env->spr[SPR_POWER_MMCR1]; - int sel = extract64(mmcr1, MMCR1_PMC4EVT_EXTR, MMCR1_EVT_SIZE); - if (sel == 0x02 || (env->spr[SPR_CTRL] & CTRL_RUN)) { - tmp = env->spr[SPR_POWER_PMC4]; - tmp += num_insns; - if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) { - tmp = PMC_COUNTER_NEGATIVE_VAL; - overflow_triggered = true; - } - env->spr[SPR_POWER_PMC4] = tmp; - } + env->spr[SPR_POWER_PMC4] = tmp; } } @@ -238,18 +256,11 @@ static void pmu_delete_timers(CPUPPCState *env) void helper_store_mmcr0(CPUPPCState *env, target_ulong value) { - bool hflags_pmcc0 = (value & MMCR0_PMCC0) != 0; - bool hflags_pmcc1 = (value & MMCR0_PMCC1) != 0; - pmu_update_cycles(env); env->spr[SPR_POWER_MMCR0] = value; - /* MMCR0 writes can change HFLAGS_PMCC[01] and HFLAGS_INSN_CNT */ - env->hflags = deposit32(env->hflags, HFLAGS_PMCC0, 1, hflags_pmcc0); - env->hflags = deposit32(env->hflags, HFLAGS_PMCC1, 1, hflags_pmcc1); - - pmu_update_summaries(env); + pmu_mmcr01_updated(env); /* Update cycle overflow timers with the current MMCR0 state */ pmu_update_overflow_timers(env); @@ -261,8 +272,7 @@ void helper_store_mmcr1(CPUPPCState *env, uint64_t value) env->spr[SPR_POWER_MMCR1] = value; - /* MMCR1 writes can change HFLAGS_INSN_CNT */ - pmu_update_summaries(env); + pmu_mmcr01_updated(env); } target_ulong helper_read_pmc(CPUPPCState *env, uint32_t sprn) @@ -276,23 +286,22 @@ void helper_store_pmc(CPUPPCState *env, uint32_t sprn, uint64_t value) { pmu_update_cycles(env); - env->spr[sprn] = value; + env->spr[sprn] = (uint32_t)value; pmc_update_overflow_timer(env, sprn); } -static void fire_PMC_interrupt(PowerPCCPU *cpu) +static void perfm_alert(PowerPCCPU *cpu) { CPUPPCState *env = &cpu->env; pmu_update_cycles(env); if (env->spr[SPR_POWER_MMCR0] & MMCR0_FCECE) { - env->spr[SPR_POWER_MMCR0] &= ~MMCR0_FCECE; env->spr[SPR_POWER_MMCR0] |= MMCR0_FC; - /* Changing MMCR0_FC requires a new HFLAGS_INSN_CNT calc */ - pmu_update_summaries(env); + /* Changing MMCR0_FC requires summaries and hflags update */ + pmu_mmcr01_updated(env); /* * Delete all pending timers if we need to freeze @@ -303,24 +312,29 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu) } if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMAE) { + /* These MMCR0 bits do not require summaries or hflags update. */ env->spr[SPR_POWER_MMCR0] &= ~MMCR0_PMAE; env->spr[SPR_POWER_MMCR0] |= MMCR0_PMAO; + ppc_set_irq(cpu, PPC_INTERRUPT_PERFM, 1); } raise_ebb_perfm_exception(env); } +void helper_handle_pmc5_overflow(CPUPPCState *env) +{ + env->spr[SPR_POWER_PMC5] = PMC_COUNTER_NEGATIVE_VAL; + perfm_alert(env_archcpu(env)); +} + /* This helper assumes that the PMC is running. */ void helper_insns_inc(CPUPPCState *env, uint32_t num_insns) { bool overflow_triggered; - PowerPCCPU *cpu; overflow_triggered = pmu_increment_insns(env, num_insns); - if (overflow_triggered) { - cpu = env_archcpu(env); - fire_PMC_interrupt(cpu); + perfm_alert(env_archcpu(env)); } } @@ -328,7 +342,7 @@ static void cpu_ppc_pmu_timer_cb(void *opaque) { PowerPCCPU *cpu = opaque; - fire_PMC_interrupt(cpu); + perfm_alert(cpu); } void cpu_ppc_pmu_init(CPUPPCState *env) diff --git a/target/ppc/power8-pmu.h b/target/ppc/power8-pmu.h index 9692dd765e..775e640053 100644 --- a/target/ppc/power8-pmu.h +++ b/target/ppc/power8-pmu.h @@ -14,11 +14,14 @@ #define POWER8_PMU_H #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) + +#define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL + void cpu_ppc_pmu_init(CPUPPCState *env); -void pmu_update_summaries(CPUPPCState *env); +void pmu_mmcr01_updated(CPUPPCState *env); #else static inline void cpu_ppc_pmu_init(CPUPPCState *env) { } -static inline void pmu_update_summaries(CPUPPCState *env) { } +static inline void pmu_mmcr01_updated(CPUPPCState *env) { } #endif #endif diff --git a/target/ppc/monitor.c b/target/ppc/ppc-qmp-cmds.c similarity index 74% rename from target/ppc/monitor.c rename to target/ppc/ppc-qmp-cmds.c index 0b805ef6e9..a25d86a8d1 100644 --- a/target/ppc/monitor.c +++ b/target/ppc/ppc-qmp-cmds.c @@ -1,5 +1,5 @@ /* - * QEMU monitor + * QEMU PPC (monitor definitions) * * Copyright (c) 2003-2004 Fabrice Bellard * @@ -28,18 +28,17 @@ #include "qemu/ctype.h" #include "monitor/hmp-target.h" #include "monitor/hmp.h" +#include "qapi/qapi-commands-machine-target.h" +#include "cpu-models.h" +#include "cpu-qom.h" static target_long monitor_get_ccr(Monitor *mon, const struct MonitorDef *md, int val) { CPUArchState *env = mon_get_cpu_env(mon); unsigned int u; - int i; - u = 0; - for (i = 0; i < 8; i++) { - u |= env->crf[i] << (32 - (4 * (i + 1))); - } + u = ppc_get_cr(env); return u; } @@ -55,6 +54,9 @@ static target_long monitor_get_decr(Monitor *mon, const struct MonitorDef *md, int val) { CPUArchState *env = mon_get_cpu_env(mon); + if (!env->tb_env) { + return 0; + } return cpu_ppc_load_decr(env); } @@ -62,6 +64,9 @@ static target_long monitor_get_tbu(Monitor *mon, const struct MonitorDef *md, int val) { CPUArchState *env = mon_get_cpu_env(mon); + if (!env->tb_env) { + return 0; + } return cpu_ppc_load_tbu(env); } @@ -69,6 +74,9 @@ static target_long monitor_get_tbl(Monitor *mon, const struct MonitorDef *md, int val) { CPUArchState *env = mon_get_cpu_env(mon); + if (!env->tb_env) { + return 0; + } return cpu_ppc_load_tbl(env); } @@ -95,7 +103,11 @@ const MonitorDef monitor_defs[] = { { "xer", 0, &monitor_get_xer }, { "msr", offsetof(CPUPPCState, msr) }, { "tbu", 0, &monitor_get_tbu, }, +#if defined(TARGET_PPC64) + { "tb", 0, &monitor_get_tbl, }, +#else { "tbl", 0, &monitor_get_tbl, }, +#endif { NULL }, }; @@ -125,8 +137,7 @@ static int ppc_cpu_get_reg_num(const char *numstr, int maxnum, int *pregnum) int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval) { int i, regnum; - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); /* General purpose registers */ if ((qemu_tolower(name[0]) == 'r') && @@ -163,3 +174,47 @@ int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval) return -EINVAL; } + +static void ppc_cpu_defs_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CpuDefinitionInfoList **first = user_data; + const char *typename; + CpuDefinitionInfo *info; + + typename = object_class_get_name(oc); + info = g_malloc0(sizeof(*info)); + info->name = cpu_model_from_type(typename); + + QAPI_LIST_PREPEND(*first, info); +} + +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) +{ + CpuDefinitionInfoList *cpu_list = NULL; + GSList *list; + int i; + + list = object_class_get_list(TYPE_POWERPC_CPU, false); + g_slist_foreach(list, ppc_cpu_defs_entry, &cpu_list); + g_slist_free(list); + + for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) { + PowerPCCPUAlias *alias = &ppc_cpu_aliases[i]; + ObjectClass *oc; + CpuDefinitionInfo *info; + + oc = ppc_cpu_class_by_name(alias->model); + if (oc == NULL) { + continue; + } + + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(alias->alias); + info->q_typename = g_strdup(object_class_get_name(oc)); + + QAPI_LIST_PREPEND(cpu_list, info); + } + + return cpu_list; +} diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h index b5a5bc6895..8a9d6cd994 100644 --- a/target/ppc/spr_common.h +++ b/target/ppc/spr_common.h @@ -81,6 +81,8 @@ void _spr_register(CPUPPCState *env, int num, const char *name, void spr_noaccess(DisasContext *ctx, int gprn, int sprn); void spr_read_generic(DisasContext *ctx, int gprn, int sprn); void spr_write_generic(DisasContext *ctx, int sprn, int gprn); +void spr_write_generic32(DisasContext *ctx, int sprn, int gprn); +void spr_core_write_generic(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCR0(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn); void spr_write_PMC(DisasContext *ctx, int sprn, int gprn); @@ -109,7 +111,6 @@ void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn); void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn); #ifndef CONFIG_USER_ONLY -void spr_write_generic32(DisasContext *ctx, int sprn, int gprn); void spr_write_clear(DisasContext *ctx, int sprn, int gprn); void spr_access_nop(DisasContext *ctx, int sprn, int gprn); void spr_read_decr(DisasContext *ctx, int gprn, int sprn); @@ -158,6 +159,9 @@ void spr_read_mas73(DisasContext *ctx, int gprn, int sprn); #ifdef TARGET_PPC64 void spr_read_cfar(DisasContext *ctx, int gprn, int sprn); void spr_write_cfar(DisasContext *ctx, int sprn, int gprn); +void spr_write_ciabr(DisasContext *ctx, int sprn, int gprn); +void spr_write_dawr0(DisasContext *ctx, int sprn, int gprn); +void spr_write_dawrx0(DisasContext *ctx, int sprn, int gprn); void spr_write_ureg(DisasContext *ctx, int sprn, int gprn); void spr_read_purr(DisasContext *ctx, int gprn, int sprn); void spr_write_purr(DisasContext *ctx, int sprn, int gprn); @@ -194,7 +198,10 @@ void spr_write_ebb(DisasContext *ctx, int sprn, int gprn); void spr_read_ebb_upper32(DisasContext *ctx, int gprn, int sprn); void spr_write_ebb_upper32(DisasContext *ctx, int sprn, int gprn); void spr_write_hmer(DisasContext *ctx, int sprn, int gprn); +void spr_read_tfmr(DisasContext *ctx, int gprn, int sprn); +void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn); void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn); +void spr_read_dexcr_ureg(DisasContext *ctx, int gprn, int sprn); #endif void register_low_BATs(CPUPPCState *env); diff --git a/target/ppc/tcg-stub.c b/target/ppc/tcg-stub.c index aadcf59d26..740d796b98 100644 --- a/target/ppc/tcg-stub.c +++ b/target/ppc/tcg-stub.c @@ -28,18 +28,3 @@ void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp) void destroy_ppc_opcodes(PowerPCCPU *cpu) { } - -target_ulong softmmu_resize_hpt_prepare(PowerPCCPU *cpu, - SpaprMachineState *spapr, - target_ulong shift) -{ - g_assert_not_reached(); -} - -target_ulong softmmu_resize_hpt_commit(PowerPCCPU *cpu, - SpaprMachineState *spapr, - target_ulong flags, - target_ulong shift) -{ - g_assert_not_reached(); -} diff --git a/target/ppc/timebase_helper.c b/target/ppc/timebase_helper.c index 86d01d6e4e..39d397416e 100644 --- a/target/ppc/timebase_helper.c +++ b/target/ppc/timebase_helper.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" #include "cpu.h" +#include "hw/ppc/ppc.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "qemu/log.h" @@ -59,19 +60,55 @@ target_ulong helper_load_purr(CPUPPCState *env) void helper_store_purr(CPUPPCState *env, target_ulong val) { - cpu_ppc_store_purr(env, val); + CPUState *cs = env_cpu(env); + CPUState *ccs; + uint32_t nr_threads = cs->nr_threads; + + if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + cpu_ppc_store_purr(env, val); + return; + } + + THREAD_SIBLING_FOREACH(cs, ccs) { + CPUPPCState *cenv = &POWERPC_CPU(ccs)->env; + cpu_ppc_store_purr(cenv, val); + } } #endif #if !defined(CONFIG_USER_ONLY) void helper_store_tbl(CPUPPCState *env, target_ulong val) { - cpu_ppc_store_tbl(env, val); + CPUState *cs = env_cpu(env); + CPUState *ccs; + uint32_t nr_threads = cs->nr_threads; + + if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + cpu_ppc_store_tbl(env, val); + return; + } + + THREAD_SIBLING_FOREACH(cs, ccs) { + CPUPPCState *cenv = &POWERPC_CPU(ccs)->env; + cpu_ppc_store_tbl(cenv, val); + } } void helper_store_tbu(CPUPPCState *env, target_ulong val) { - cpu_ppc_store_tbu(env, val); + CPUState *cs = env_cpu(env); + CPUState *ccs; + uint32_t nr_threads = cs->nr_threads; + + if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + cpu_ppc_store_tbu(env, val); + return; + } + + THREAD_SIBLING_FOREACH(cs, ccs) { + CPUPPCState *cenv = &POWERPC_CPU(ccs)->env; + cpu_ppc_store_tbu(cenv, val); + } } void helper_store_atbl(CPUPPCState *env, target_ulong val) @@ -101,17 +138,53 @@ target_ulong helper_load_hdecr(CPUPPCState *env) void helper_store_hdecr(CPUPPCState *env, target_ulong val) { - cpu_ppc_store_hdecr(env, val); + CPUState *cs = env_cpu(env); + CPUState *ccs; + uint32_t nr_threads = cs->nr_threads; + + if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + cpu_ppc_store_hdecr(env, val); + return; + } + + THREAD_SIBLING_FOREACH(cs, ccs) { + CPUPPCState *cenv = &POWERPC_CPU(ccs)->env; + cpu_ppc_store_hdecr(cenv, val); + } } void helper_store_vtb(CPUPPCState *env, target_ulong val) { - cpu_ppc_store_vtb(env, val); + CPUState *cs = env_cpu(env); + CPUState *ccs; + uint32_t nr_threads = cs->nr_threads; + + if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + cpu_ppc_store_vtb(env, val); + return; + } + + THREAD_SIBLING_FOREACH(cs, ccs) { + CPUPPCState *cenv = &POWERPC_CPU(ccs)->env; + cpu_ppc_store_vtb(cenv, val); + } } void helper_store_tbu40(CPUPPCState *env, target_ulong val) { - cpu_ppc_store_tbu40(env, val); + CPUState *cs = env_cpu(env); + CPUState *ccs; + uint32_t nr_threads = cs->nr_threads; + + if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + cpu_ppc_store_tbu40(env, val); + return; + } + + THREAD_SIBLING_FOREACH(cs, ccs) { + CPUPPCState *cenv = &POWERPC_CPU(ccs)->env; + cpu_ppc_store_tbu40(cenv, val); + } } target_ulong helper_load_40x_pit(CPUPPCState *env) @@ -143,6 +216,236 @@ void helper_store_booke_tsr(CPUPPCState *env, target_ulong val) { store_booke_tsr(env, val); } + +#if defined(TARGET_PPC64) +/* + * POWER processor Timebase Facility + */ + +/* + * The TBST is the timebase state machine, which is a per-core machine that + * is used to synchronize the core TB with the ChipTOD. States 3,4,5 are + * not used in POWER8/9/10. + * + * The state machine gets driven by writes to TFMR SPR from the core, and + * by signals from the ChipTOD. The state machine table for common + * transitions is as follows (according to hardware specs, not necessarily + * this implementation): + * + * | Cur | Event | New | + * +----------------+----------------------------------+-----+ + * | 0 RESET | TFMR |= LOAD_TOD_MOD | 1 | + * | 1 SEND_TOD_MOD | "immediate transition" | 2 | + * | 2 NOT_SET | mttbu/mttbu40/mttbl | 2 | + * | 2 NOT_SET | TFMR |= MOVE_CHIP_TOD_TO_TB | 6 | + * | 6 SYNC_WAIT | "sync pulse from ChipTOD" | 7 | + * | 7 GET_TOD | ChipTOD xscom MOVE_TOD_TO_TB_REG | 8 | + * | 8 TB_RUNNING | mttbu/mttbu40 | 8 | + * | 8 TB_RUNNING | TFMR |= LOAD_TOD_MOD | 1 | + * | 8 TB_RUNNING | mttbl | 9 | + * | 9 TB_ERROR | TFMR |= CLEAR_TB_ERRORS | 0 | + * + * - LOAD_TOD_MOD will also move states 2,6 to state 1, omitted from table + * because it's not a typical init flow. + * + * - The ERROR state can be entered from most/all other states on invalid + * states (e.g., if some TFMR control bit is set from a state where it's + * not listed to cause a transition away from), omitted to avoid clutter. + * + * Note: mttbl causes a timebase error because this inevitably causes + * ticks to be lost and TB to become unsynchronized, whereas TB can be + * adjusted using mttbu* without losing ticks. mttbl behaviour is not + * modelled. + * + * Note: the TB state machine does not actually cause any real TB adjustment! + * TB starts out synchronized across all vCPUs (hardware threads) in + * QMEU, so for now the purpose of the TBST and ChipTOD model is simply + * to step through firmware initialisation sequences. + */ +static unsigned int tfmr_get_tb_state(uint64_t tfmr) +{ + return (tfmr & TFMR_TBST_ENCODED) >> (63 - 31); +} + +static uint64_t tfmr_new_tb_state(uint64_t tfmr, unsigned int tbst) +{ + tfmr &= ~TFMR_TBST_LAST; + tfmr |= (tfmr & TFMR_TBST_ENCODED) >> 4; /* move state to last state */ + tfmr &= ~TFMR_TBST_ENCODED; + tfmr |= (uint64_t)tbst << (63 - 31); /* move new state to state */ + + if (tbst == TBST_TB_RUNNING) { + tfmr |= TFMR_TB_VALID; + } else { + tfmr &= ~TFMR_TB_VALID; + } + + return tfmr; +} + +static void write_tfmr(CPUPPCState *env, target_ulong val) +{ + CPUState *cs = env_cpu(env); + + if (cs->nr_threads == 1) { + env->spr[SPR_TFMR] = val; + } else { + CPUState *ccs; + THREAD_SIBLING_FOREACH(cs, ccs) { + CPUPPCState *cenv = &POWERPC_CPU(ccs)->env; + cenv->spr[SPR_TFMR] = val; + } + } +} + +static void tb_state_machine_step(CPUPPCState *env) +{ + uint64_t tfmr = env->spr[SPR_TFMR]; + unsigned int tbst = tfmr_get_tb_state(tfmr); + + if (!(tfmr & TFMR_TB_ECLIPZ) || tbst == TBST_TB_ERROR) { + return; + } + + if (env->pnv_tod_tbst.tb_sync_pulse_timer) { + env->pnv_tod_tbst.tb_sync_pulse_timer--; + } else { + tfmr |= TFMR_TB_SYNC_OCCURED; + write_tfmr(env, tfmr); + } + + if (env->pnv_tod_tbst.tb_state_timer) { + env->pnv_tod_tbst.tb_state_timer--; + return; + } + + if (tfmr & TFMR_LOAD_TOD_MOD) { + tfmr &= ~TFMR_LOAD_TOD_MOD; + if (tbst == TBST_GET_TOD) { + tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR); + tfmr |= TFMR_FIRMWARE_CONTROL_ERROR; + } else { + tfmr = tfmr_new_tb_state(tfmr, TBST_SEND_TOD_MOD); + /* State seems to transition immediately */ + tfmr = tfmr_new_tb_state(tfmr, TBST_NOT_SET); + } + } else if (tfmr & TFMR_MOVE_CHIP_TOD_TO_TB) { + if (tbst == TBST_SYNC_WAIT) { + tfmr = tfmr_new_tb_state(tfmr, TBST_GET_TOD); + env->pnv_tod_tbst.tb_state_timer = 3; + } else if (tbst == TBST_GET_TOD) { + if (env->pnv_tod_tbst.tod_sent_to_tb) { + tfmr = tfmr_new_tb_state(tfmr, TBST_TB_RUNNING); + tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB; + env->pnv_tod_tbst.tb_ready_for_tod = 0; + env->pnv_tod_tbst.tod_sent_to_tb = 0; + } + } else { + qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: MOVE_CHIP_TOD_TO_TB " + "state machine in invalid state 0x%x\n", tbst); + tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR); + tfmr |= TFMR_FIRMWARE_CONTROL_ERROR; + env->pnv_tod_tbst.tb_ready_for_tod = 0; + } + } + + write_tfmr(env, tfmr); +} + +target_ulong helper_load_tfmr(CPUPPCState *env) +{ + tb_state_machine_step(env); + + return env->spr[SPR_TFMR] | TFMR_TB_ECLIPZ; +} + +void helper_store_tfmr(CPUPPCState *env, target_ulong val) +{ + uint64_t tfmr = env->spr[SPR_TFMR]; + uint64_t clear_on_write; + unsigned int tbst = tfmr_get_tb_state(tfmr); + + if (!(val & TFMR_TB_ECLIPZ)) { + qemu_log_mask(LOG_UNIMP, "TFMR non-ECLIPZ mode not implemented\n"); + tfmr &= ~TFMR_TBST_ENCODED; + tfmr &= ~TFMR_TBST_LAST; + goto out; + } + + /* Update control bits */ + tfmr = (tfmr & ~TFMR_CONTROL_MASK) | (val & TFMR_CONTROL_MASK); + + /* Several bits are clear-on-write, only one is implemented so far */ + clear_on_write = val & TFMR_FIRMWARE_CONTROL_ERROR; + tfmr &= ~clear_on_write; + + /* + * mtspr always clears this. The sync pulse timer makes it come back + * after the second mfspr. + */ + tfmr &= ~TFMR_TB_SYNC_OCCURED; + env->pnv_tod_tbst.tb_sync_pulse_timer = 1; + + if (ppc_cpu_tir(env_archcpu(env)) != 0 && + (val & (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB))) { + qemu_log_mask(LOG_UNIMP, "TFMR timebase state machine can only be " + "driven by thread 0\n"); + goto out; + } + + if (((tfmr | val) & (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB)) == + (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB)) { + qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: LOAD_TOD_MOD and " + "MOVE_CHIP_TOD_TO_TB both set\n"); + tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR); + tfmr |= TFMR_FIRMWARE_CONTROL_ERROR; + env->pnv_tod_tbst.tb_ready_for_tod = 0; + goto out; + } + + if (tfmr & TFMR_CLEAR_TB_ERRORS) { + /* + * Workbook says TFMR_CLEAR_TB_ERRORS should be written twice. + * This is not simulated/required here. + */ + tfmr = tfmr_new_tb_state(tfmr, TBST_RESET); + tfmr &= ~TFMR_CLEAR_TB_ERRORS; + tfmr &= ~TFMR_LOAD_TOD_MOD; + tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB; + tfmr &= ~TFMR_FIRMWARE_CONTROL_ERROR; /* XXX: should this be cleared? */ + env->pnv_tod_tbst.tb_ready_for_tod = 0; + env->pnv_tod_tbst.tod_sent_to_tb = 0; + goto out; + } + + if (tbst == TBST_TB_ERROR) { + qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: mtspr TFMR in TB_ERROR" + " state\n"); + tfmr |= TFMR_FIRMWARE_CONTROL_ERROR; + return; + } + + if (tfmr & TFMR_LOAD_TOD_MOD) { + /* Wait for an arbitrary 3 mfspr until the next state transition. */ + env->pnv_tod_tbst.tb_state_timer = 3; + } else if (tfmr & TFMR_MOVE_CHIP_TOD_TO_TB) { + if (tbst == TBST_NOT_SET) { + tfmr = tfmr_new_tb_state(tfmr, TBST_SYNC_WAIT); + env->pnv_tod_tbst.tb_ready_for_tod = 1; + env->pnv_tod_tbst.tb_state_timer = 3; /* arbitrary */ + } else { + qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: MOVE_CHIP_TOD_TO_TB " + "not in TB not set state 0x%x\n", + tbst); + tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR); + tfmr |= TFMR_FIRMWARE_CONTROL_ERROR; + env->pnv_tod_tbst.tb_ready_for_tod = 0; + } + } + +out: + write_tfmr(env, tfmr); +} #endif /*****************************************************************************/ @@ -161,15 +464,15 @@ target_ulong helper_load_dcr(CPUPPCState *env, target_ulong dcrn) } else { int ret; - qemu_mutex_lock_iothread(); + bql_lock(); ret = ppc_dcr_read(env->dcr_env, (uint32_t)dcrn, &val); - qemu_mutex_unlock_iothread(); + bql_unlock(); 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()); + POWERPC_EXCP_INVAL_INVAL, GETPC()); } } return val; @@ -184,15 +487,16 @@ void helper_store_dcr(CPUPPCState *env, target_ulong dcrn, target_ulong val) POWERPC_EXCP_INVAL_INVAL, GETPC()); } else { int ret; - qemu_mutex_lock_iothread(); + bql_lock(); ret = ppc_dcr_write(env->dcr_env, (uint32_t)dcrn, (uint32_t)val); - qemu_mutex_unlock_iothread(); + bql_unlock(); 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()); + POWERPC_EXCP_INVAL_INVAL, GETPC()); } } } +#endif diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 1d6daa4608..93ffec787c 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -26,8 +26,6 @@ #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" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -36,10 +34,15 @@ #include "exec/log.h" #include "qemu/atomic128.h" #include "spr_common.h" +#include "power8-pmu.h" #include "qemu/qemu-print.h" #include "qapi/error.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + #define CPU_SINGLE_STEP 0x1 #define CPU_BRANCH_STEP 0x2 @@ -70,12 +73,14 @@ static TCGv cpu_cfar; #endif static TCGv cpu_xer, cpu_so, cpu_ov, cpu_ca, cpu_ov32, cpu_ca32; static TCGv cpu_reserve; +static TCGv cpu_reserve_length; static TCGv cpu_reserve_val; +#if defined(TARGET_PPC64) +static TCGv cpu_reserve_val2; +#endif static TCGv cpu_fpscr; static TCGv_i32 cpu_access_type; -#include "exec/gen-icount.h" - void ppc_translate_init(void) { int i; @@ -87,7 +92,7 @@ void ppc_translate_init(void) for (i = 0; i < 8; i++) { snprintf(p, cpu_reg_names_size, "crf%d", i); - cpu_crf[i] = tcg_global_mem_new_i32(cpu_env, + cpu_crf[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUPPCState, crf[i]), p); p += 5; cpu_reg_names_size -= 5; @@ -95,58 +100,67 @@ void ppc_translate_init(void) for (i = 0; i < 32; i++) { snprintf(p, cpu_reg_names_size, "r%d", i); - cpu_gpr[i] = tcg_global_mem_new(cpu_env, + cpu_gpr[i] = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, gpr[i]), p); p += (i < 10) ? 3 : 4; cpu_reg_names_size -= (i < 10) ? 3 : 4; snprintf(p, cpu_reg_names_size, "r%dH", i); - cpu_gprh[i] = tcg_global_mem_new(cpu_env, + cpu_gprh[i] = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, gprh[i]), p); p += (i < 10) ? 4 : 5; cpu_reg_names_size -= (i < 10) ? 4 : 5; } - cpu_nip = tcg_global_mem_new(cpu_env, + cpu_nip = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, nip), "nip"); - cpu_msr = tcg_global_mem_new(cpu_env, + cpu_msr = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, msr), "msr"); - cpu_ctr = tcg_global_mem_new(cpu_env, + cpu_ctr = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, ctr), "ctr"); - cpu_lr = tcg_global_mem_new(cpu_env, + cpu_lr = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, lr), "lr"); #if defined(TARGET_PPC64) - cpu_cfar = tcg_global_mem_new(cpu_env, + cpu_cfar = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, cfar), "cfar"); #endif - cpu_xer = tcg_global_mem_new(cpu_env, + cpu_xer = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, xer), "xer"); - cpu_so = tcg_global_mem_new(cpu_env, + cpu_so = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, so), "SO"); - cpu_ov = tcg_global_mem_new(cpu_env, + cpu_ov = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, ov), "OV"); - cpu_ca = tcg_global_mem_new(cpu_env, + cpu_ca = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, ca), "CA"); - cpu_ov32 = tcg_global_mem_new(cpu_env, + cpu_ov32 = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, ov32), "OV32"); - cpu_ca32 = tcg_global_mem_new(cpu_env, + cpu_ca32 = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, ca32), "CA32"); - cpu_reserve = tcg_global_mem_new(cpu_env, + cpu_reserve = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, reserve_addr), "reserve_addr"); - cpu_reserve_val = tcg_global_mem_new(cpu_env, - offsetof(CPUPPCState, reserve_val), - "reserve_val"); + cpu_reserve_length = tcg_global_mem_new(tcg_env, + offsetof(CPUPPCState, + reserve_length), + "reserve_length"); + cpu_reserve_val = tcg_global_mem_new(tcg_env, + offsetof(CPUPPCState, reserve_val), + "reserve_val"); +#if defined(TARGET_PPC64) + cpu_reserve_val2 = tcg_global_mem_new(tcg_env, + offsetof(CPUPPCState, reserve_val2), + "reserve_val2"); +#endif - cpu_fpscr = tcg_global_mem_new(cpu_env, + cpu_fpscr = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, fpscr), "fpscr"); - cpu_access_type = tcg_global_mem_new_i32(cpu_env, + cpu_access_type = tcg_global_mem_new_i32(tcg_env, offsetof(CPUPPCState, access_type), "access_type"); } @@ -177,6 +191,8 @@ struct DisasContext { bool hr; bool mmcr0_pmcc0; bool mmcr0_pmcc1; + bool mmcr0_pmcjce; + bool pmc_other; bool pmu_insn_cnt; ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */ int singlestep_enabled; @@ -220,15 +236,48 @@ struct opc_handler_t { void (*handler)(DisasContext *ctx); }; +static inline bool gen_serialize(DisasContext *ctx) +{ + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(tcg_env); + ctx->base.is_jmp = DISAS_NORETURN; + return false; + } + return true; +} + +#if !defined(CONFIG_USER_ONLY) +#if defined(TARGET_PPC64) +static inline bool gen_serialize_core(DisasContext *ctx) +{ + if (ctx->flags & POWERPC_FLAG_SMT) { + return gen_serialize(ctx); + } + return true; +} +#endif + +static inline bool gen_serialize_core_lpar(DisasContext *ctx) +{ +#if defined(TARGET_PPC64) + if (ctx->flags & POWERPC_FLAG_SMT_1LPAR) { + return gen_serialize(ctx); + } +#endif + return true; +} +#endif + /* SPR load/store helpers */ static inline void gen_load_spr(TCGv t, int reg) { - tcg_gen_ld_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg])); + tcg_gen_ld_tl(t, tcg_env, offsetof(CPUPPCState, spr[reg])); } static inline void gen_store_spr(int reg, TCGv t) { - tcg_gen_st_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg])); + tcg_gen_st_tl(t, tcg_env, offsetof(CPUPPCState, spr[reg])); } static inline void gen_set_access_type(DisasContext *ctx, int access_type) @@ -247,36 +296,26 @@ static inline void gen_update_nip(DisasContext *ctx, target_ulong nip) tcg_gen_movi_tl(cpu_nip, nip); } -static void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error) +static void gen_exception_err_nip(DisasContext *ctx, uint32_t excp, + uint32_t error, target_ulong nip) { TCGv_i32 t0, t1; - /* - * These are all synchronous exceptions, we set the PC back to the - * faulting instruction - */ - gen_update_nip(ctx, ctx->cia); - t0 = tcg_const_i32(excp); - t1 = tcg_const_i32(error); - gen_helper_raise_exception_err(cpu_env, t0, t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); + gen_update_nip(ctx, nip); + t0 = tcg_constant_i32(excp); + t1 = tcg_constant_i32(error); + gen_helper_raise_exception_err(tcg_env, t0, t1); ctx->base.is_jmp = DISAS_NORETURN; } -static void gen_exception(DisasContext *ctx, uint32_t excp) +static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, + uint32_t error) { - TCGv_i32 t0; - /* * These are all synchronous exceptions, we set the PC back to the * faulting instruction */ - gen_update_nip(ctx, ctx->cia); - t0 = tcg_const_i32(excp); - gen_helper_raise_exception(cpu_env, t0); - tcg_temp_free_i32(t0); - ctx->base.is_jmp = DISAS_NORETURN; + gen_exception_err_nip(ctx, excp, error, ctx->cia); } static void gen_exception_nip(DisasContext *ctx, uint32_t excp, @@ -285,26 +324,28 @@ static void gen_exception_nip(DisasContext *ctx, uint32_t excp, TCGv_i32 t0; gen_update_nip(ctx, nip); - t0 = tcg_const_i32(excp); - gen_helper_raise_exception(cpu_env, t0); - tcg_temp_free_i32(t0); + t0 = tcg_constant_i32(excp); + gen_helper_raise_exception(tcg_env, t0); ctx->base.is_jmp = DISAS_NORETURN; } -static void gen_icount_io_start(DisasContext *ctx) +static inline void gen_exception(DisasContext *ctx, uint32_t excp) { - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - /* - * An I/O instruction must be last in the TB. - * Chain to the next TB, and let the code from gen_tb_start - * decide if we need to return to the main loop. - * Doing this first also allows this value to be overridden. - */ - ctx->base.is_jmp = DISAS_TOO_MANY; - } + /* + * These are all synchronous exceptions, we set the PC back to the + * faulting instruction + */ + gen_exception_nip(ctx, excp, ctx->cia); } +#if !defined(CONFIG_USER_ONLY) +static void gen_ppc_maybe_interrupt(DisasContext *ctx) +{ + translator_io_start(&ctx->base); + gen_helper_ppc_maybe_interrupt(tcg_env); +} +#endif + /* * Tells the caller what is the appropriate exception to generate and prepares * SPR registers for this exception. @@ -312,8 +353,9 @@ static void gen_icount_io_start(DisasContext *ctx) * The exception can be either POWERPC_EXCP_TRACE (on most PowerPCs) or * POWERPC_EXCP_DEBUG (on BookE). */ -static uint32_t gen_prep_dbgex(DisasContext *ctx) +static void gen_debug_exception(DisasContext *ctx, bool rfi_type) { +#if !defined(CONFIG_USER_ONLY) if (ctx->flags & POWERPC_FLAG_DE) { target_ulong dbsr = 0; if (ctx->singlestep_enabled & CPU_SINGLE_STEP) { @@ -326,17 +368,18 @@ static uint32_t gen_prep_dbgex(DisasContext *ctx) gen_load_spr(t0, SPR_BOOKE_DBSR); tcg_gen_ori_tl(t0, t0, dbsr); gen_store_spr(SPR_BOOKE_DBSR, t0); - tcg_temp_free(t0); - return POWERPC_EXCP_DEBUG; + gen_helper_raise_exception(tcg_env, + tcg_constant_i32(POWERPC_EXCP_DEBUG)); + ctx->base.is_jmp = DISAS_NORETURN; } else { - return POWERPC_EXCP_TRACE; + if (!rfi_type) { /* BookS does not single step rfi type instructions */ + TCGv t0 = tcg_temp_new(); + tcg_gen_movi_tl(t0, ctx->cia); + gen_helper_book3s_trace(tcg_env, t0); + ctx->base.is_jmp = DISAS_NORETURN; + } } -} - -static void gen_debug_exception(DisasContext *ctx) -{ - gen_helper_raise_exception(cpu_env, tcg_constant_i32(gen_prep_dbgex(ctx))); - ctx->base.is_jmp = DISAS_NORETURN; +#endif } static inline void gen_inval_exception(DisasContext *ctx, uint32_t error) @@ -376,9 +419,8 @@ void spr_noaccess(DisasContext *ctx, int gprn, int sprn) static void spr_load_dump_spr(int sprn) { #ifdef PPC_DUMP_SPR_ACCESSES - TCGv_i32 t0 = tcg_const_i32(sprn); - gen_helper_load_dump_spr(cpu_env, t0); - tcg_temp_free_i32(t0); + TCGv_i32 t0 = tcg_constant_i32(sprn); + gen_helper_load_dump_spr(tcg_env, t0); #endif } @@ -391,9 +433,8 @@ void spr_read_generic(DisasContext *ctx, int gprn, int sprn) static void spr_store_dump_spr(int sprn) { #ifdef PPC_DUMP_SPR_ACCESSES - TCGv_i32 t0 = tcg_const_i32(sprn); - gen_helper_store_dump_spr(cpu_env, t0); - tcg_temp_free_i32(t0); + TCGv_i32 t0 = tcg_constant_i32(sprn); + gen_helper_store_dump_spr(tcg_env, t0); #endif } @@ -403,9 +444,61 @@ void spr_write_generic(DisasContext *ctx, int sprn, int gprn) spr_store_dump_spr(sprn); } +void spr_write_generic32(DisasContext *ctx, int sprn, int gprn) +{ +#ifdef TARGET_PPC64 + TCGv t0 = tcg_temp_new(); + tcg_gen_ext32u_tl(t0, cpu_gpr[gprn]); + gen_store_spr(sprn, t0); + spr_store_dump_spr(sprn); +#else + spr_write_generic(ctx, sprn, gprn); +#endif +} + +void spr_core_write_generic(DisasContext *ctx, int sprn, int gprn) +{ + if (!(ctx->flags & POWERPC_FLAG_SMT)) { + spr_write_generic(ctx, sprn, gprn); + return; + } + + if (!gen_serialize(ctx)) { + return; + } + + gen_helper_spr_core_write_generic(tcg_env, tcg_constant_i32(sprn), + cpu_gpr[gprn]); + spr_store_dump_spr(sprn); +} + +static void spr_write_CTRL_ST(DisasContext *ctx, int sprn, int gprn) +{ + /* This does not implement >1 thread */ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_extract_tl(t0, cpu_gpr[gprn], 0, 1); /* Extract RUN field */ + tcg_gen_shli_tl(t1, t0, 8); /* Duplicate the bit in TS */ + tcg_gen_or_tl(t1, t1, t0); + gen_store_spr(sprn, t1); +} + void spr_write_CTRL(DisasContext *ctx, int sprn, int gprn) { - spr_write_generic(ctx, sprn, gprn); + if (!(ctx->flags & POWERPC_FLAG_SMT_1LPAR)) { + /* CTRL behaves as 1-thread in LPAR-per-thread mode */ + spr_write_CTRL_ST(ctx, sprn, gprn); + goto out; + } + + if (!gen_serialize(ctx)) { + return; + } + + gen_helper_spr_write_CTRL(tcg_env, tcg_constant_i32(sprn), + cpu_gpr[gprn]); +out: + spr_store_dump_spr(sprn); /* * SPR_CTRL writes must force a new translation block, @@ -416,19 +509,6 @@ void spr_write_CTRL(DisasContext *ctx, int sprn, int gprn) } #if !defined(CONFIG_USER_ONLY) -void spr_write_generic32(DisasContext *ctx, int sprn, int gprn) -{ -#ifdef TARGET_PPC64 - TCGv t0 = tcg_temp_new(); - tcg_gen_ext32u_tl(t0, cpu_gpr[gprn]); - gen_store_spr(sprn, t0); - tcg_temp_free(t0); - spr_store_dump_spr(sprn); -#else - spr_write_generic(ctx, sprn, gprn); -#endif -} - void spr_write_clear(DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); @@ -437,8 +517,6 @@ void spr_write_clear(DisasContext *ctx, int sprn, int gprn) tcg_gen_neg_tl(t1, cpu_gpr[gprn]); tcg_gen_and_tl(t0, t0, t1); gen_store_spr(sprn, t0); - tcg_temp_free(t0); - tcg_temp_free(t1); } void spr_access_nop(DisasContext *ctx, int sprn, int gprn) @@ -468,9 +546,6 @@ void spr_read_xer(DisasContext *ctx, int gprn, int sprn) tcg_gen_shli_tl(t0, cpu_ca32, XER_CA32); tcg_gen_or_tl(dst, dst, t0); } - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); } void spr_write_xer(DisasContext *ctx, int sprn, int gprn) @@ -499,8 +574,9 @@ void spr_write_lr(DisasContext *ctx, int sprn, int gprn) tcg_gen_mov_tl(cpu_lr, cpu_gpr[gprn]); } -/* CFAR */ #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +/* Debug facilities */ +/* CFAR */ void spr_read_cfar(DisasContext *ctx, int gprn, int sprn) { tcg_gen_mov_tl(cpu_gpr[gprn], cpu_cfar); @@ -510,6 +586,26 @@ void spr_write_cfar(DisasContext *ctx, int sprn, int gprn) { tcg_gen_mov_tl(cpu_cfar, cpu_gpr[gprn]); } + +/* Breakpoint */ +void spr_write_ciabr(DisasContext *ctx, int sprn, int gprn) +{ + translator_io_start(&ctx->base); + gen_helper_store_ciabr(tcg_env, cpu_gpr[gprn]); +} + +/* Watchpoint */ +void spr_write_dawr0(DisasContext *ctx, int sprn, int gprn) +{ + translator_io_start(&ctx->base); + gen_helper_store_dawr0(tcg_env, cpu_gpr[gprn]); +} + +void spr_write_dawrx0(DisasContext *ctx, int sprn, int gprn) +{ + translator_io_start(&ctx->base); + gen_helper_store_dawrx0(tcg_env, cpu_gpr[gprn]); +} #endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ /* CTR */ @@ -546,14 +642,14 @@ void spr_write_ureg(DisasContext *ctx, int sprn, int gprn) #if !defined(CONFIG_USER_ONLY) void spr_read_decr(DisasContext *ctx, int gprn, int sprn) { - gen_icount_io_start(ctx); - gen_helper_load_decr(cpu_gpr[gprn], cpu_env); + translator_io_start(&ctx->base); + gen_helper_load_decr(cpu_gpr[gprn], tcg_env); } void spr_write_decr(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); - gen_helper_store_decr(cpu_env, cpu_gpr[gprn]); + translator_io_start(&ctx->base); + gen_helper_store_decr(tcg_env, cpu_gpr[gprn]); } #endif @@ -561,91 +657,111 @@ void spr_write_decr(DisasContext *ctx, int sprn, int gprn) /* Time base */ void spr_read_tbl(DisasContext *ctx, int gprn, int sprn) { - gen_icount_io_start(ctx); - gen_helper_load_tbl(cpu_gpr[gprn], cpu_env); + translator_io_start(&ctx->base); + gen_helper_load_tbl(cpu_gpr[gprn], tcg_env); } void spr_read_tbu(DisasContext *ctx, int gprn, int sprn) { - gen_icount_io_start(ctx); - gen_helper_load_tbu(cpu_gpr[gprn], cpu_env); + translator_io_start(&ctx->base); + gen_helper_load_tbu(cpu_gpr[gprn], tcg_env); } void spr_read_atbl(DisasContext *ctx, int gprn, int sprn) { - gen_helper_load_atbl(cpu_gpr[gprn], cpu_env); + gen_helper_load_atbl(cpu_gpr[gprn], tcg_env); } void spr_read_atbu(DisasContext *ctx, int gprn, int sprn) { - gen_helper_load_atbu(cpu_gpr[gprn], cpu_env); + gen_helper_load_atbu(cpu_gpr[gprn], tcg_env); } #if !defined(CONFIG_USER_ONLY) void spr_write_tbl(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); - gen_helper_store_tbl(cpu_env, cpu_gpr[gprn]); + if (!gen_serialize_core_lpar(ctx)) { + return; + } + + translator_io_start(&ctx->base); + gen_helper_store_tbl(tcg_env, cpu_gpr[gprn]); } void spr_write_tbu(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); - gen_helper_store_tbu(cpu_env, cpu_gpr[gprn]); + if (!gen_serialize_core_lpar(ctx)) { + return; + } + + translator_io_start(&ctx->base); + gen_helper_store_tbu(tcg_env, cpu_gpr[gprn]); } void spr_write_atbl(DisasContext *ctx, int sprn, int gprn) { - gen_helper_store_atbl(cpu_env, cpu_gpr[gprn]); + gen_helper_store_atbl(tcg_env, cpu_gpr[gprn]); } void spr_write_atbu(DisasContext *ctx, int sprn, int gprn) { - gen_helper_store_atbu(cpu_env, cpu_gpr[gprn]); + gen_helper_store_atbu(tcg_env, cpu_gpr[gprn]); } #if defined(TARGET_PPC64) void spr_read_purr(DisasContext *ctx, int gprn, int sprn) { - gen_icount_io_start(ctx); - gen_helper_load_purr(cpu_gpr[gprn], cpu_env); + translator_io_start(&ctx->base); + gen_helper_load_purr(cpu_gpr[gprn], tcg_env); } void spr_write_purr(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); - gen_helper_store_purr(cpu_env, cpu_gpr[gprn]); + if (!gen_serialize_core_lpar(ctx)) { + return; + } + translator_io_start(&ctx->base); + gen_helper_store_purr(tcg_env, cpu_gpr[gprn]); } /* HDECR */ void spr_read_hdecr(DisasContext *ctx, int gprn, int sprn) { - gen_icount_io_start(ctx); - gen_helper_load_hdecr(cpu_gpr[gprn], cpu_env); + translator_io_start(&ctx->base); + gen_helper_load_hdecr(cpu_gpr[gprn], tcg_env); } void spr_write_hdecr(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); - gen_helper_store_hdecr(cpu_env, cpu_gpr[gprn]); + if (!gen_serialize_core_lpar(ctx)) { + return; + } + translator_io_start(&ctx->base); + gen_helper_store_hdecr(tcg_env, cpu_gpr[gprn]); } void spr_read_vtb(DisasContext *ctx, int gprn, int sprn) { - gen_icount_io_start(ctx); - gen_helper_load_vtb(cpu_gpr[gprn], cpu_env); + translator_io_start(&ctx->base); + gen_helper_load_vtb(cpu_gpr[gprn], tcg_env); } void spr_write_vtb(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); - gen_helper_store_vtb(cpu_env, cpu_gpr[gprn]); + if (!gen_serialize_core_lpar(ctx)) { + return; + } + translator_io_start(&ctx->base); + gen_helper_store_vtb(tcg_env, cpu_gpr[gprn]); } void spr_write_tbu40(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); - gen_helper_store_tbu40(cpu_env, cpu_gpr[gprn]); + if (!gen_serialize_core_lpar(ctx)) { + return; + } + translator_io_start(&ctx->base); + gen_helper_store_tbu40(tcg_env, cpu_gpr[gprn]); } #endif @@ -656,94 +772,86 @@ void spr_write_tbu40(DisasContext *ctx, int sprn, int gprn) /* IBAT0L...IBAT7L */ void spr_read_ibat(DisasContext *ctx, int gprn, int sprn) { - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, + tcg_gen_ld_tl(cpu_gpr[gprn], tcg_env, offsetof(CPUPPCState, IBAT[sprn & 1][(sprn - SPR_IBAT0U) / 2])); } void spr_read_ibat_h(DisasContext *ctx, int gprn, int sprn) { - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, + tcg_gen_ld_tl(cpu_gpr[gprn], tcg_env, offsetof(CPUPPCState, IBAT[sprn & 1][((sprn - SPR_IBAT4U) / 2) + 4])); } void spr_write_ibatu(DisasContext *ctx, int sprn, int gprn) { - TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); - gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); + TCGv_i32 t0 = tcg_constant_i32((sprn - SPR_IBAT0U) / 2); + gen_helper_store_ibatu(tcg_env, t0, cpu_gpr[gprn]); } void spr_write_ibatu_h(DisasContext *ctx, int sprn, int gprn) { - TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4U) / 2) + 4); - gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); + TCGv_i32 t0 = tcg_constant_i32(((sprn - SPR_IBAT4U) / 2) + 4); + gen_helper_store_ibatu(tcg_env, t0, cpu_gpr[gprn]); } void spr_write_ibatl(DisasContext *ctx, int sprn, int gprn) { - TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0L) / 2); - gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); + TCGv_i32 t0 = tcg_constant_i32((sprn - SPR_IBAT0L) / 2); + gen_helper_store_ibatl(tcg_env, t0, cpu_gpr[gprn]); } void spr_write_ibatl_h(DisasContext *ctx, int sprn, int gprn) { - TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4L) / 2) + 4); - gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); + TCGv_i32 t0 = tcg_constant_i32(((sprn - SPR_IBAT4L) / 2) + 4); + gen_helper_store_ibatl(tcg_env, t0, cpu_gpr[gprn]); } /* DBAT0U...DBAT7U */ /* DBAT0L...DBAT7L */ void spr_read_dbat(DisasContext *ctx, int gprn, int sprn) { - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, + tcg_gen_ld_tl(cpu_gpr[gprn], tcg_env, offsetof(CPUPPCState, DBAT[sprn & 1][(sprn - SPR_DBAT0U) / 2])); } void spr_read_dbat_h(DisasContext *ctx, int gprn, int sprn) { - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, + tcg_gen_ld_tl(cpu_gpr[gprn], tcg_env, offsetof(CPUPPCState, DBAT[sprn & 1][((sprn - SPR_DBAT4U) / 2) + 4])); } void spr_write_dbatu(DisasContext *ctx, int sprn, int gprn) { - TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0U) / 2); - gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); + TCGv_i32 t0 = tcg_constant_i32((sprn - SPR_DBAT0U) / 2); + gen_helper_store_dbatu(tcg_env, t0, cpu_gpr[gprn]); } void spr_write_dbatu_h(DisasContext *ctx, int sprn, int gprn) { - TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4U) / 2) + 4); - gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); + TCGv_i32 t0 = tcg_constant_i32(((sprn - SPR_DBAT4U) / 2) + 4); + gen_helper_store_dbatu(tcg_env, t0, cpu_gpr[gprn]); } void spr_write_dbatl(DisasContext *ctx, int sprn, int gprn) { - TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0L) / 2); - gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); + TCGv_i32 t0 = tcg_constant_i32((sprn - SPR_DBAT0L) / 2); + gen_helper_store_dbatl(tcg_env, t0, cpu_gpr[gprn]); } void spr_write_dbatl_h(DisasContext *ctx, int sprn, int gprn) { - TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4L) / 2) + 4); - gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); + TCGv_i32 t0 = tcg_constant_i32(((sprn - SPR_DBAT4L) / 2) + 4); + gen_helper_store_dbatl(tcg_env, t0, cpu_gpr[gprn]); } /* SDR1 */ void spr_write_sdr1(DisasContext *ctx, int sprn, int gprn) { - gen_helper_store_sdr1(cpu_env, cpu_gpr[gprn]); + gen_helper_store_sdr1(tcg_env, cpu_gpr[gprn]); } #if defined(TARGET_PPC64) @@ -751,45 +859,52 @@ void spr_write_sdr1(DisasContext *ctx, int sprn, int gprn) /* PIDR */ void spr_write_pidr(DisasContext *ctx, int sprn, int gprn) { - gen_helper_store_pidr(cpu_env, cpu_gpr[gprn]); + gen_helper_store_pidr(tcg_env, cpu_gpr[gprn]); } void spr_write_lpidr(DisasContext *ctx, int sprn, int gprn) { - gen_helper_store_lpidr(cpu_env, cpu_gpr[gprn]); + gen_helper_store_lpidr(tcg_env, cpu_gpr[gprn]); } void spr_read_hior(DisasContext *ctx, int gprn, int sprn) { - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, excp_prefix)); + tcg_gen_ld_tl(cpu_gpr[gprn], tcg_env, offsetof(CPUPPCState, excp_prefix)); } void spr_write_hior(DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0x3FFFFF00000ULL); - tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_prefix)); - tcg_temp_free(t0); + tcg_gen_st_tl(t0, tcg_env, offsetof(CPUPPCState, excp_prefix)); } void spr_write_ptcr(DisasContext *ctx, int sprn, int gprn) { - gen_helper_store_ptcr(cpu_env, cpu_gpr[gprn]); + gen_helper_store_ptcr(tcg_env, cpu_gpr[gprn]); } void spr_write_pcr(DisasContext *ctx, int sprn, int gprn) { - gen_helper_store_pcr(cpu_env, cpu_gpr[gprn]); + gen_helper_store_pcr(tcg_env, cpu_gpr[gprn]); } /* DPDES */ void spr_read_dpdes(DisasContext *ctx, int gprn, int sprn) { - gen_helper_load_dpdes(cpu_gpr[gprn], cpu_env); + if (!gen_serialize_core_lpar(ctx)) { + return; + } + + gen_helper_load_dpdes(cpu_gpr[gprn], tcg_env); } void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn) { - gen_helper_store_dpdes(cpu_env, cpu_gpr[gprn]); + if (!gen_serialize_core_lpar(ctx)) { + return; + } + + gen_helper_store_dpdes(tcg_env, cpu_gpr[gprn]); } #endif #endif @@ -798,61 +913,60 @@ void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn) #if !defined(CONFIG_USER_ONLY) void spr_read_40x_pit(DisasContext *ctx, int gprn, int sprn) { - gen_icount_io_start(ctx); - gen_helper_load_40x_pit(cpu_gpr[gprn], cpu_env); + translator_io_start(&ctx->base); + gen_helper_load_40x_pit(cpu_gpr[gprn], tcg_env); } void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); - gen_helper_store_40x_pit(cpu_env, cpu_gpr[gprn]); + translator_io_start(&ctx->base); + gen_helper_store_40x_pit(tcg_env, cpu_gpr[gprn]); } void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_store_spr(sprn, cpu_gpr[gprn]); - gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]); + gen_helper_store_40x_dbcr0(tcg_env, cpu_gpr[gprn]); /* We must stop translation as we may have rebooted */ ctx->base.is_jmp = DISAS_EXIT_UPDATE; } void spr_write_40x_sler(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); - gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]); + translator_io_start(&ctx->base); + gen_helper_store_40x_sler(tcg_env, cpu_gpr[gprn]); } void spr_write_40x_tcr(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); - gen_helper_store_40x_tcr(cpu_env, cpu_gpr[gprn]); + translator_io_start(&ctx->base); + gen_helper_store_40x_tcr(tcg_env, cpu_gpr[gprn]); } void spr_write_40x_tsr(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); - gen_helper_store_40x_tsr(cpu_env, cpu_gpr[gprn]); + translator_io_start(&ctx->base); + gen_helper_store_40x_tsr(tcg_env, cpu_gpr[gprn]); } void spr_write_40x_pid(DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0xFF); - gen_helper_store_40x_pid(cpu_env, t0); - tcg_temp_free(t0); + gen_helper_store_40x_pid(tcg_env, t0); } void spr_write_booke_tcr(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); - gen_helper_store_booke_tcr(cpu_env, cpu_gpr[gprn]); + translator_io_start(&ctx->base); + gen_helper_store_booke_tcr(tcg_env, cpu_gpr[gprn]); } void spr_write_booke_tsr(DisasContext *ctx, int sprn, int gprn) { - gen_icount_io_start(ctx); - gen_helper_store_booke_tsr(cpu_env, cpu_gpr[gprn]); + translator_io_start(&ctx->base); + gen_helper_store_booke_tsr(tcg_env, cpu_gpr[gprn]); } #endif @@ -863,7 +977,6 @@ void spr_write_pir(DisasContext *ctx, int sprn, int gprn) TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0xF); gen_store_spr(SPR_PIR, t0); - tcg_temp_free(t0); } #endif @@ -871,17 +984,15 @@ void spr_write_pir(DisasContext *ctx, int sprn, int gprn) void spr_read_spefscr(DisasContext *ctx, int gprn, int sprn) { TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_ld_i32(t0, cpu_env, offsetof(CPUPPCState, spe_fscr)); + tcg_gen_ld_i32(t0, tcg_env, offsetof(CPUPPCState, spe_fscr)); tcg_gen_extu_i32_tl(cpu_gpr[gprn], t0); - tcg_temp_free_i32(t0); } void spr_write_spefscr(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(t0, cpu_gpr[gprn]); - tcg_gen_st_i32(t0, cpu_env, offsetof(CPUPPCState, spe_fscr)); - tcg_temp_free_i32(t0); + tcg_gen_st_i32(t0, tcg_env, offsetof(CPUPPCState, spe_fscr)); } #if !defined(CONFIG_USER_ONLY) @@ -889,11 +1000,10 @@ void spr_write_spefscr(DisasContext *ctx, int sprn, int gprn) void spr_write_excp_prefix(DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); - tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUPPCState, ivpr_mask)); + tcg_gen_ld_tl(t0, tcg_env, offsetof(CPUPPCState, ivpr_mask)); tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]); - tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_prefix)); + tcg_gen_st_tl(t0, tcg_env, offsetof(CPUPPCState, excp_prefix)); gen_store_spr(sprn, t0); - tcg_temp_free(t0); } void spr_write_excp_vector(DisasContext *ctx, int sprn, int gprn) @@ -907,18 +1017,17 @@ void spr_write_excp_vector(DisasContext *ctx, int sprn, int gprn) } else if (sprn >= SPR_BOOKE_IVOR38 && sprn <= SPR_BOOKE_IVOR42) { sprn_offs = sprn - SPR_BOOKE_IVOR38 + 38; } else { - printf("Trying to write an unknown exception vector %d %03x\n", - sprn, sprn); - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + qemu_log_mask(LOG_GUEST_ERROR, "Trying to write an unknown exception" + " vector 0x%03x\n", sprn); + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); return; } TCGv t0 = tcg_temp_new(); - tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUPPCState, ivor_mask)); + tcg_gen_ld_tl(t0, tcg_env, offsetof(CPUPPCState, ivor_mask)); tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]); - tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_vectors[sprn_offs])); + tcg_gen_st_tl(t0, tcg_env, offsetof(CPUPPCState, excp_vectors[sprn_offs])); gen_store_spr(sprn, t0); - tcg_temp_free(t0); } #endif @@ -953,10 +1062,6 @@ void spr_write_amr(DisasContext *ctx, int sprn, int gprn) tcg_gen_or_tl(t0, t0, t2); gen_store_spr(SPR_AMR, t0); spr_store_dump_spr(SPR_AMR); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); } void spr_write_uamor(DisasContext *ctx, int sprn, int gprn) @@ -984,10 +1089,6 @@ void spr_write_uamor(DisasContext *ctx, int sprn, int gprn) tcg_gen_or_tl(t0, t0, t2); gen_store_spr(SPR_UAMOR, t0); spr_store_dump_spr(SPR_UAMOR); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); } void spr_write_iamr(DisasContext *ctx, int sprn, int gprn) @@ -1015,10 +1116,6 @@ void spr_write_iamr(DisasContext *ctx, int sprn, int gprn) tcg_gen_or_tl(t0, t0, t2); gen_store_spr(SPR_IAMR, t0); spr_store_dump_spr(SPR_IAMR); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); } #endif #endif @@ -1026,7 +1123,7 @@ void spr_write_iamr(DisasContext *ctx, int sprn, int gprn) #ifndef CONFIG_USER_ONLY void spr_read_thrm(DisasContext *ctx, int gprn, int sprn) { - gen_helper_fixup_thrm(cpu_env); + gen_helper_fixup_thrm(tcg_env); gen_load_spr(cpu_gpr[gprn], sprn); spr_load_dump_spr(sprn); } @@ -1039,7 +1136,6 @@ void spr_write_e500_l1csr0(DisasContext *ctx, int sprn, int gprn) tcg_gen_andi_tl(t0, cpu_gpr[gprn], L1CSR0_DCE | L1CSR0_CPE); gen_store_spr(sprn, t0); - tcg_temp_free(t0); } void spr_write_e500_l1csr1(DisasContext *ctx, int sprn, int gprn) @@ -1048,7 +1144,6 @@ void spr_write_e500_l1csr1(DisasContext *ctx, int sprn, int gprn) tcg_gen_andi_tl(t0, cpu_gpr[gprn], L1CSR1_ICE | L1CSR1_CPE); gen_store_spr(sprn, t0); - tcg_temp_free(t0); } void spr_write_e500_l2csr0(DisasContext *ctx, int sprn, int gprn) @@ -1058,27 +1153,27 @@ void spr_write_e500_l2csr0(DisasContext *ctx, int sprn, int gprn) tcg_gen_andi_tl(t0, cpu_gpr[gprn], ~(E500_L2CSR0_L2FI | E500_L2CSR0_L2FL | E500_L2CSR0_L2LFC)); gen_store_spr(sprn, t0); - tcg_temp_free(t0); } void spr_write_booke206_mmucsr0(DisasContext *ctx, int sprn, int gprn) { - gen_helper_booke206_tlbflush(cpu_env, cpu_gpr[gprn]); + gen_helper_booke206_tlbflush(tcg_env, cpu_gpr[gprn]); } void spr_write_booke_pid(DisasContext *ctx, int sprn, int gprn) { - TCGv_i32 t0 = tcg_const_i32(sprn); - gen_helper_booke_setpid(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); + TCGv_i32 t0 = tcg_constant_i32(sprn); + gen_helper_booke_setpid(tcg_env, t0, cpu_gpr[gprn]); } + void spr_write_eplc(DisasContext *ctx, int sprn, int gprn) { - gen_helper_booke_set_eplc(cpu_env, cpu_gpr[gprn]); + gen_helper_booke_set_eplc(tcg_env, cpu_gpr[gprn]); } + void spr_write_epsc(DisasContext *ctx, int sprn, int gprn) { - gen_helper_booke_set_epsc(cpu_env, cpu_gpr[gprn]); + gen_helper_booke_set_epsc(tcg_env, cpu_gpr[gprn]); } #endif @@ -1091,7 +1186,6 @@ void spr_write_mas73(DisasContext *ctx, int sprn, int gprn) gen_store_spr(SPR_BOOKE_MAS3, val); tcg_gen_shri_tl(val, cpu_gpr[gprn], 32); gen_store_spr(SPR_BOOKE_MAS7, val); - tcg_temp_free(val); } void spr_read_mas73(DisasContext *ctx, int gprn, int sprn) @@ -1102,8 +1196,6 @@ void spr_read_mas73(DisasContext *ctx, int gprn, int sprn) tcg_gen_shli_tl(mas7, mas7, 32); gen_load_spr(mas3, SPR_BOOKE_MAS3); tcg_gen_or_tl(cpu_gpr[gprn], mas3, mas7); - tcg_temp_free(mas3); - tcg_temp_free(mas7); } #endif @@ -1112,29 +1204,21 @@ void spr_read_mas73(DisasContext *ctx, int gprn, int sprn) static void gen_fscr_facility_check(DisasContext *ctx, int facility_sprn, int bit, int sprn, int cause) { - TCGv_i32 t1 = tcg_const_i32(bit); - TCGv_i32 t2 = tcg_const_i32(sprn); - TCGv_i32 t3 = tcg_const_i32(cause); + TCGv_i32 t1 = tcg_constant_i32(bit); + TCGv_i32 t2 = tcg_constant_i32(sprn); + TCGv_i32 t3 = tcg_constant_i32(cause); - gen_helper_fscr_facility_check(cpu_env, t1, t2, t3); - - tcg_temp_free_i32(t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t1); + gen_helper_fscr_facility_check(tcg_env, t1, t2, t3); } static void gen_msr_facility_check(DisasContext *ctx, int facility_sprn, int bit, int sprn, int cause) { - TCGv_i32 t1 = tcg_const_i32(bit); - TCGv_i32 t2 = tcg_const_i32(sprn); - TCGv_i32 t3 = tcg_const_i32(cause); + TCGv_i32 t1 = tcg_constant_i32(bit); + TCGv_i32 t2 = tcg_constant_i32(sprn); + TCGv_i32 t3 = tcg_constant_i32(cause); - gen_helper_msr_facility_check(cpu_env, t1, t2, t3); - - tcg_temp_free_i32(t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t1); + gen_helper_msr_facility_check(tcg_env, t1, t2, t3); } void spr_read_prev_upper32(DisasContext *ctx, int gprn, int sprn) @@ -1145,9 +1229,6 @@ void spr_read_prev_upper32(DisasContext *ctx, int gprn, int sprn) gen_load_spr(spr, sprn - 1); tcg_gen_shri_tl(spr_up, spr, 32); tcg_gen_ext32u_tl(cpu_gpr[gprn], spr_up); - - tcg_temp_free(spr); - tcg_temp_free(spr_up); } void spr_write_prev_upper32(DisasContext *ctx, int sprn, int gprn) @@ -1157,8 +1238,6 @@ void spr_write_prev_upper32(DisasContext *ctx, int sprn, int gprn) gen_load_spr(spr, sprn - 1); tcg_gen_deposit_tl(spr, spr, cpu_gpr[gprn], 32, 32); gen_store_spr(sprn - 1, spr); - - tcg_temp_free(spr); } #if !defined(CONFIG_USER_ONLY) @@ -1170,12 +1249,29 @@ void spr_write_hmer(DisasContext *ctx, int sprn, int gprn) tcg_gen_and_tl(hmer, cpu_gpr[gprn], hmer); gen_store_spr(sprn, hmer); spr_store_dump_spr(sprn); - tcg_temp_free(hmer); +} + +void spr_read_tfmr(DisasContext *ctx, int gprn, int sprn) +{ + /* Reading TFMR can cause it to be updated, so serialize threads here too */ + if (!gen_serialize_core(ctx)) { + return; + } + gen_helper_load_tfmr(cpu_gpr[gprn], tcg_env); +} + +void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn) +{ + if (!gen_serialize_core(ctx)) { + return; + } + gen_helper_store_tfmr(tcg_env, cpu_gpr[gprn]); } void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) { - gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); + translator_io_start(&ctx->base); + gen_helper_store_lpcr(tcg_env, cpu_gpr[gprn]); } #endif /* !defined(CONFIG_USER_ONLY) */ @@ -1238,6 +1334,23 @@ void spr_write_ebb_upper32(DisasContext *ctx, int sprn, int gprn) gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); spr_write_prev_upper32(ctx, sprn, gprn); } + +void spr_read_dexcr_ureg(DisasContext *ctx, int gprn, int sprn) +{ + TCGv t0 = tcg_temp_new(); + + /* + * Access to the (H)DEXCR in problem state is done using separated + * SPR indexes which are 16 below the SPR indexes which have full + * access to the (H)DEXCR in privileged state. Problem state can + * only read bits 32:63, bits 0:31 return 0. + * + * See section 9.3.1-9.3.2 of PowerISA v3.1B + */ + + gen_load_spr(t0, sprn + 16); + tcg_gen_ext32u_tl(cpu_gpr[gprn], t0); +} #endif #define GEN_HANDLER(name, opc1, opc2, opc3, inval, type) \ @@ -1267,38 +1380,43 @@ typedef struct opcode_t { const char *oname; } opcode_t; +static void gen_priv_opc(DisasContext *ctx) +{ + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); +} + /* Helpers for priv. check */ -#define GEN_PRIV \ - do { \ - gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; \ +#define GEN_PRIV(CTX) \ + do { \ + gen_priv_opc(CTX); return; \ } while (0) #if defined(CONFIG_USER_ONLY) -#define CHK_HV GEN_PRIV -#define CHK_SV GEN_PRIV -#define CHK_HVRM GEN_PRIV +#define CHK_HV(CTX) GEN_PRIV(CTX) +#define CHK_SV(CTX) GEN_PRIV(CTX) +#define CHK_HVRM(CTX) GEN_PRIV(CTX) #else -#define CHK_HV \ - do { \ - if (unlikely(ctx->pr || !ctx->hv)) { \ - GEN_PRIV; \ - } \ +#define CHK_HV(CTX) \ + do { \ + if (unlikely(ctx->pr || !ctx->hv)) {\ + GEN_PRIV(CTX); \ + } \ } while (0) -#define CHK_SV \ +#define CHK_SV(CTX) \ do { \ if (unlikely(ctx->pr)) { \ - GEN_PRIV; \ + GEN_PRIV(CTX); \ } \ } while (0) -#define CHK_HVRM \ - do { \ - if (unlikely(ctx->pr || !ctx->hv || ctx->dr)) { \ - GEN_PRIV; \ - } \ +#define CHK_HVRM(CTX) \ + do { \ + if (unlikely(ctx->pr || !ctx->hv || ctx->dr)) { \ + GEN_PRIV(CTX); \ + } \ } while (0) #endif -#define CHK_NONE +#define CHK_NONE(CTX) /*****************************************************************************/ /* PowerPC instructions table */ @@ -1408,17 +1526,12 @@ static inline void gen_op_cmp(TCGv arg0, TCGv arg1, int s, int crf) tcg_gen_trunc_tl_i32(t, t0); tcg_gen_trunc_tl_i32(cpu_crf[crf], cpu_so); tcg_gen_or_i32(cpu_crf[crf], cpu_crf[crf], t); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free_i32(t); } static inline void gen_op_cmpi(TCGv arg0, target_ulong arg1, int s, int crf) { - TCGv t0 = tcg_const_tl(arg1); + TCGv t0 = tcg_constant_tl(arg1); gen_op_cmp(arg0, t0, s, crf); - tcg_temp_free(t0); } static inline void gen_op_cmp32(TCGv arg0, TCGv arg1, int s, int crf) @@ -1434,15 +1547,12 @@ static inline void gen_op_cmp32(TCGv arg0, TCGv arg1, int s, int crf) tcg_gen_ext32u_tl(t1, arg1); } gen_op_cmp(t0, t1, s, crf); - tcg_temp_free(t1); - tcg_temp_free(t0); } static inline void gen_op_cmpi32(TCGv arg0, target_ulong arg1, int s, int crf) { - TCGv t0 = tcg_const_tl(arg1); + TCGv t0 = tcg_constant_tl(arg1); gen_op_cmp32(arg0, t0, s, crf); - tcg_temp_free(t0); } static inline void gen_set_Rc0(DisasContext *ctx, TCGv reg) @@ -1486,10 +1596,6 @@ static void gen_cmprb(DisasContext *ctx) tcg_gen_or_i32(crf, crf, src2lo); } tcg_gen_shli_i32(crf, crf, CRF_GT_BIT); - tcg_temp_free_i32(src1); - tcg_temp_free_i32(src2); - tcg_temp_free_i32(src2lo); - tcg_temp_free_i32(src2hi); } #if defined(TARGET_PPC64) @@ -1512,12 +1618,10 @@ static void gen_isel(DisasContext *ctx) tcg_gen_extu_i32_tl(t0, cpu_crf[bi >> 2]); tcg_gen_andi_tl(t0, t0, mask); - zr = tcg_const_tl(0); + zr = tcg_constant_tl(0); tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr[rD(ctx->opcode)], t0, zr, rA(ctx->opcode) ? cpu_gpr[rA(ctx->opcode)] : zr, cpu_gpr[rB(ctx->opcode)]); - tcg_temp_free(zr); - tcg_temp_free(t0); } /* cmpb: PowerPC 2.05 specification */ @@ -1541,7 +1645,6 @@ static inline void gen_op_arith_compute_ov(DisasContext *ctx, TCGv arg0, } else { tcg_gen_andc_tl(cpu_ov, cpu_ov, t0); } - tcg_temp_free(t0); if (NARROW_MODE(ctx)) { tcg_gen_extract_tl(cpu_ov, cpu_ov, 31, 1); if (is_isa300(ctx)) { @@ -1574,7 +1677,6 @@ static inline void gen_op_arith_compute_ca32(DisasContext *ctx, } tcg_gen_xor_tl(t0, t0, res); tcg_gen_extract_tl(ca32, t0, 32, 1); - tcg_temp_free(t0); } /* Common add function */ @@ -1603,13 +1705,12 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_add_tl(t0, t0, ca); } tcg_gen_xor_tl(ca, t0, t1); /* bits changed w/ carry */ - tcg_temp_free(t1); tcg_gen_extract_tl(ca, ca, 32, 1); if (is_isa300(ctx)) { tcg_gen_mov_tl(ca32, ca); } } else { - TCGv zero = tcg_const_tl(0); + TCGv zero = tcg_constant_tl(0); if (add_ca) { tcg_gen_add2_tl(t0, ca, arg1, zero, ca, zero); tcg_gen_add2_tl(t0, ca, t0, ca, arg2, zero); @@ -1617,7 +1718,6 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_add2_tl(t0, ca, arg1, zero, arg2, zero); } gen_op_arith_compute_ca32(ctx, t0, arg1, arg2, ca32, 0); - tcg_temp_free(zero); } } else { tcg_gen_add_tl(t0, arg1, arg2); @@ -1635,66 +1735,8 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, if (t0 != ret) { tcg_gen_mov_tl(ret, t0); - tcg_temp_free(t0); } } -/* Add functions with two operands */ -#define GEN_INT_ARITH_ADD(name, opc3, ca, add_ca, compute_ca, compute_ov) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - ca, glue(ca, 32), \ - add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \ -} -/* Add functions with one operand and one immediate */ -#define GEN_INT_ARITH_ADD_CONST(name, opc3, const_val, ca, \ - add_ca, compute_ca, compute_ov) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - TCGv t0 = tcg_const_tl(const_val); \ - gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], t0, \ - ca, glue(ca, 32), \ - add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \ - tcg_temp_free(t0); \ -} - -/* add add. addo addo. */ -GEN_INT_ARITH_ADD(add, 0x08, cpu_ca, 0, 0, 0) -GEN_INT_ARITH_ADD(addo, 0x18, cpu_ca, 0, 0, 1) -/* addc addc. addco addco. */ -GEN_INT_ARITH_ADD(addc, 0x00, cpu_ca, 0, 1, 0) -GEN_INT_ARITH_ADD(addco, 0x10, cpu_ca, 0, 1, 1) -/* adde adde. addeo addeo. */ -GEN_INT_ARITH_ADD(adde, 0x04, cpu_ca, 1, 1, 0) -GEN_INT_ARITH_ADD(addeo, 0x14, cpu_ca, 1, 1, 1) -/* addme addme. addmeo addmeo. */ -GEN_INT_ARITH_ADD_CONST(addme, 0x07, -1LL, cpu_ca, 1, 1, 0) -GEN_INT_ARITH_ADD_CONST(addmeo, 0x17, -1LL, cpu_ca, 1, 1, 1) -/* addex */ -GEN_INT_ARITH_ADD(addex, 0x05, cpu_ov, 1, 1, 0); -/* addze addze. addzeo addzeo.*/ -GEN_INT_ARITH_ADD_CONST(addze, 0x06, 0, cpu_ca, 1, 1, 0) -GEN_INT_ARITH_ADD_CONST(addzeo, 0x16, 0, cpu_ca, 1, 1, 1) -/* addic addic.*/ -static inline void gen_op_addic(DisasContext *ctx, bool compute_rc0) -{ - TCGv c = tcg_const_tl(SIMM(ctx->opcode)); - gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - c, cpu_ca, cpu_ca32, 0, 1, 0, compute_rc0); - tcg_temp_free(c); -} - -static void gen_addic(DisasContext *ctx) -{ - gen_op_addic(ctx, 0); -} - -static void gen_addic_(DisasContext *ctx) -{ - gen_op_addic(ctx, 1); -} static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, TCGv arg1, TCGv arg2, int sign, int compute_ov) @@ -1730,10 +1772,6 @@ static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, TCGv arg1, } tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); } - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, ret); @@ -1758,10 +1796,9 @@ GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1); #define GEN_DIVE(name, hlpr, compute_ov) \ static void gen_##name(DisasContext *ctx) \ { \ - TCGv_i32 t0 = tcg_const_i32(compute_ov); \ - gen_helper_##hlpr(cpu_gpr[rD(ctx->opcode)], cpu_env, \ + TCGv_i32 t0 = tcg_constant_i32(compute_ov); \ + gen_helper_##hlpr(cpu_gpr[rD(ctx->opcode)], tcg_env, \ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); \ - tcg_temp_free_i32(t0); \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); \ } \ @@ -1805,10 +1842,6 @@ static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, } tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); } - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, ret); @@ -1855,19 +1888,13 @@ static inline void gen_op_arith_modw(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_movcond_i32(TCG_COND_NE, t1, t2, t3, t2, t1); tcg_gen_rem_i32(t3, t0, t1); tcg_gen_ext_i32_tl(ret, t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); } else { - TCGv_i32 t2 = tcg_const_i32(1); - TCGv_i32 t3 = tcg_const_i32(0); + TCGv_i32 t2 = tcg_constant_i32(1); + TCGv_i32 t3 = tcg_constant_i32(0); tcg_gen_movcond_i32(TCG_COND_EQ, t1, t1, t3, t2, t1); - tcg_gen_remu_i32(t3, t0, t1); - tcg_gen_extu_i32_tl(ret, t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); + tcg_gen_remu_i32(t0, t0, t1); + tcg_gen_extu_i32_tl(ret, t0); } - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); } #define GEN_INT_ARITH_MODW(name, opc3, sign) \ @@ -1901,18 +1928,12 @@ static inline void gen_op_arith_modd(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_movi_i64(t3, 0); tcg_gen_movcond_i64(TCG_COND_NE, t1, t2, t3, t2, t1); tcg_gen_rem_i64(ret, t0, t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); } else { - TCGv_i64 t2 = tcg_const_i64(1); - TCGv_i64 t3 = tcg_const_i64(0); + TCGv_i64 t2 = tcg_constant_i64(1); + TCGv_i64 t3 = tcg_constant_i64(0); tcg_gen_movcond_i64(TCG_COND_EQ, t1, t1, t3, t2, t1); tcg_gen_remu_i64(ret, t0, t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); } - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } #define GEN_INT_ARITH_MODD(name, opc3, sign) \ @@ -1937,8 +1958,6 @@ static void gen_mulhw(DisasContext *ctx) tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); tcg_gen_muls2_i32(t0, t1, t0, t1); tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -1954,8 +1973,6 @@ static void gen_mulhwu(DisasContext *ctx) tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); tcg_gen_mulu2_i32(t0, t1, t0, t1); tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -1971,8 +1988,6 @@ static void gen_mullw(DisasContext *ctx) tcg_gen_ext32s_tl(t0, cpu_gpr[rA(ctx->opcode)]); tcg_gen_ext32s_tl(t1, cpu_gpr[rB(ctx->opcode)]); tcg_gen_mul_i64(cpu_gpr[rD(ctx->opcode)], t0, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); #else tcg_gen_mul_i32(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); @@ -2005,8 +2020,6 @@ static void gen_mullwo(DisasContext *ctx) } tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -2026,7 +2039,6 @@ static void gen_mulhd(DisasContext *ctx) TCGv lo = tcg_temp_new(); tcg_gen_muls2_tl(lo, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); - tcg_temp_free(lo); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -2038,7 +2050,6 @@ static void gen_mulhdu(DisasContext *ctx) TCGv lo = tcg_temp_new(); tcg_gen_mulu2_tl(lo, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); - tcg_temp_free(lo); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -2071,9 +2082,6 @@ static void gen_mulldo(DisasContext *ctx) } tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -2109,9 +2117,7 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, } tcg_gen_xor_tl(t1, arg2, inv1); /* add without carry */ tcg_gen_add_tl(t0, t0, inv1); - tcg_temp_free(inv1); tcg_gen_xor_tl(cpu_ca, t0, t1); /* bits changes w/ carry */ - tcg_temp_free(t1); tcg_gen_extract_tl(cpu_ca, cpu_ca, 32, 1); if (is_isa300(ctx)) { tcg_gen_mov_tl(cpu_ca32, cpu_ca); @@ -2119,12 +2125,10 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, } else if (add_ca) { TCGv zero, inv1 = tcg_temp_new(); tcg_gen_not_tl(inv1, arg1); - zero = tcg_const_tl(0); + zero = tcg_constant_tl(0); tcg_gen_add2_tl(t0, cpu_ca, arg2, zero, cpu_ca, zero); tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, inv1, zero); gen_op_arith_compute_ca32(ctx, t0, inv1, arg2, cpu_ca32, 0); - tcg_temp_free(zero); - tcg_temp_free(inv1); } else { tcg_gen_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1); tcg_gen_sub_tl(t0, arg2, arg1); @@ -2151,60 +2155,15 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, if (t0 != ret) { tcg_gen_mov_tl(ret, t0); - tcg_temp_free(t0); } } -/* Sub functions with Two operands functions */ -#define GEN_INT_ARITH_SUBF(name, opc3, add_ca, compute_ca, compute_ov) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \ -} -/* Sub functions with one operand and one immediate */ -#define GEN_INT_ARITH_SUBF_CONST(name, opc3, const_val, \ - add_ca, compute_ca, compute_ov) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - TCGv t0 = tcg_const_tl(const_val); \ - gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], t0, \ - add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \ - tcg_temp_free(t0); \ -} -/* subf subf. subfo subfo. */ -GEN_INT_ARITH_SUBF(subf, 0x01, 0, 0, 0) -GEN_INT_ARITH_SUBF(subfo, 0x11, 0, 0, 1) -/* subfc subfc. subfco subfco. */ -GEN_INT_ARITH_SUBF(subfc, 0x00, 0, 1, 0) -GEN_INT_ARITH_SUBF(subfco, 0x10, 0, 1, 1) -/* subfe subfe. subfeo subfo. */ -GEN_INT_ARITH_SUBF(subfe, 0x04, 1, 1, 0) -GEN_INT_ARITH_SUBF(subfeo, 0x14, 1, 1, 1) -/* subfme subfme. subfmeo subfmeo. */ -GEN_INT_ARITH_SUBF_CONST(subfme, 0x07, -1LL, 1, 1, 0) -GEN_INT_ARITH_SUBF_CONST(subfmeo, 0x17, -1LL, 1, 1, 1) -/* subfze subfze. subfzeo subfzeo.*/ -GEN_INT_ARITH_SUBF_CONST(subfze, 0x06, 0, 1, 1, 0) -GEN_INT_ARITH_SUBF_CONST(subfzeo, 0x16, 0, 1, 1, 1) - -/* subfic */ -static void gen_subfic(DisasContext *ctx) -{ - TCGv c = tcg_const_tl(SIMM(ctx->opcode)); - gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - c, 0, 1, 0, 0); - tcg_temp_free(c); -} /* neg neg. nego nego. */ static inline void gen_op_arith_neg(DisasContext *ctx, bool compute_ov) { - TCGv zero = tcg_const_tl(0); + TCGv zero = tcg_constant_tl(0); gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], zero, 0, 0, compute_ov, Rc(ctx->opcode)); - tcg_temp_free(zero); } static void gen_neg(DisasContext *ctx) @@ -2267,7 +2226,6 @@ static void gen_cntlzw(DisasContext *ctx) tcg_gen_trunc_tl_i32(t, cpu_gpr[rS(ctx->opcode)]); tcg_gen_clzi_i32(t, t, 32); tcg_gen_extu_i32_tl(cpu_gpr[rA(ctx->opcode)], t); - tcg_temp_free_i32(t); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); @@ -2282,7 +2240,6 @@ static void gen_cnttzw(DisasContext *ctx) tcg_gen_trunc_tl_i32(t, cpu_gpr[rS(ctx->opcode)]); tcg_gen_ctzi_i32(t, t, 32); tcg_gen_extu_i32_tl(cpu_gpr[rA(ctx->opcode)], t); - tcg_temp_free_i32(t); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); @@ -2303,10 +2260,9 @@ GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER); #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) static void gen_pause(DisasContext *ctx) { - TCGv_i32 t0 = tcg_const_i32(0); - tcg_gen_st_i32(t0, cpu_env, + TCGv_i32 t0 = tcg_constant_i32(0); + tcg_gen_st_i32(t0, tcg_env, -offsetof(PowerPCCPU, env) + offsetof(CPUState, halted)); - tcg_temp_free_i32(t0); /* Stop translation, this gives other CPUs a chance to run */ gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); @@ -2385,7 +2341,6 @@ static void gen_or(DisasContext *ctx) tcg_gen_andi_tl(t0, t0, ~0x001C000000000000ULL); tcg_gen_ori_tl(t0, t0, ((uint64_t)prio) << 50); gen_store_spr(SPR_PPR, t0); - tcg_temp_free(t0); } #if !defined(CONFIG_USER_ONLY) /* @@ -2500,7 +2455,6 @@ static void gen_prtyw(DisasContext *ctx) tcg_gen_shri_tl(t0, ra, 8); tcg_gen_xor_tl(ra, ra, t0); tcg_gen_andi_tl(ra, ra, (target_ulong)0x100000001ULL); - tcg_temp_free(t0); } #if defined(TARGET_PPC64) @@ -2517,7 +2471,6 @@ static void gen_prtyd(DisasContext *ctx) tcg_gen_shri_tl(t0, ra, 8); tcg_gen_xor_tl(ra, ra, t0); tcg_gen_andi_tl(ra, ra, 1); - tcg_temp_free(t0); } #endif @@ -2560,7 +2513,7 @@ static void gen_darn(DisasContext *ctx) if (l > 2) { tcg_gen_movi_i64(cpu_gpr[rD(ctx->opcode)], -1); } else { - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); if (l == 0) { gen_helper_darn32(cpu_gpr[rD(ctx->opcode)]); } else { @@ -2606,7 +2559,6 @@ static void gen_rlwimi(DisasContext *ctx) tcg_gen_trunc_tl_i32(t0, t_rs); tcg_gen_rotli_i32(t0, t0, sh); tcg_gen_extu_i32_tl(t1, t0); - tcg_temp_free_i32(t0); } else { #if defined(TARGET_PPC64) tcg_gen_deposit_i64(t1, t_rs, t_rs, 32, 32); @@ -2619,7 +2571,6 @@ static void gen_rlwimi(DisasContext *ctx) tcg_gen_andi_tl(t1, t1, mask); tcg_gen_andi_tl(t_ra, t_ra, ~mask); tcg_gen_or_tl(t_ra, t_ra, t1); - tcg_temp_free(t1); } if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, t_ra); @@ -2663,7 +2614,6 @@ static void gen_rlwinm(DisasContext *ctx) 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) @@ -2710,15 +2660,12 @@ static void gen_rlwnm(DisasContext *ctx) tcg_gen_andi_i32(t0, t0, 0x1f); tcg_gen_rotl_i32(t1, t1, t0); tcg_gen_extu_i32_tl(t_ra, t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); } else { #if defined(TARGET_PPC64) TCGv_i64 t0 = tcg_temp_new_i64(); tcg_gen_andi_i64(t0, t_rb, 0x1f); tcg_gen_deposit_i64(t_ra, t_rs, t_rs, 32, 32); tcg_gen_rotl_i64(t_ra, t_ra, t0); - tcg_temp_free_i64(t0); #else g_assert_not_reached(); #endif @@ -2826,7 +2773,6 @@ static void gen_rldnm(DisasContext *ctx, int mb, int me) t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, t_rb, 0x3f); tcg_gen_rotl_tl(t_ra, t_rs, t0); - tcg_temp_free(t0); tcg_gen_andi_tl(t_ra, t_ra, MASK(mb, me)); if (unlikely(Rc(ctx->opcode) != 0)) { @@ -2873,7 +2819,6 @@ static void gen_rldimi(DisasContext *ctx, int mbn, int shn) tcg_gen_andi_tl(t1, t1, mask); tcg_gen_andi_tl(t_ra, t_ra, ~mask); tcg_gen_or_tl(t_ra, t_ra, t1); - tcg_temp_free(t1); } if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, t_ra); @@ -2902,8 +2847,6 @@ static void gen_slw(DisasContext *ctx) t1 = tcg_temp_new(); tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1f); tcg_gen_shl_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); tcg_gen_ext32u_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); @@ -2913,7 +2856,7 @@ static void gen_slw(DisasContext *ctx) /* sraw & sraw. */ static void gen_sraw(DisasContext *ctx) { - gen_helper_sraw(cpu_gpr[rA(ctx->opcode)], cpu_env, + gen_helper_sraw(cpu_gpr[rA(ctx->opcode)], tcg_env, cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); @@ -2939,7 +2882,6 @@ static void gen_srawi(DisasContext *ctx) t0 = tcg_temp_new(); tcg_gen_sari_tl(t0, dst, TARGET_LONG_BITS - 1); tcg_gen_and_tl(cpu_ca, cpu_ca, t0); - tcg_temp_free(t0); tcg_gen_setcondi_tl(TCG_COND_NE, cpu_ca, cpu_ca, 0); if (is_isa300(ctx)) { tcg_gen_mov_tl(cpu_ca32, cpu_ca); @@ -2970,8 +2912,6 @@ static void gen_srw(DisasContext *ctx) t1 = tcg_temp_new(); tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1f); tcg_gen_shr_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); } @@ -2991,8 +2931,6 @@ static void gen_sld(DisasContext *ctx) t1 = tcg_temp_new(); tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x3f); tcg_gen_shl_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); } @@ -3001,7 +2939,7 @@ static void gen_sld(DisasContext *ctx) /* srad & srad. */ static void gen_srad(DisasContext *ctx) { - gen_helper_srad(cpu_gpr[rA(ctx->opcode)], cpu_env, + gen_helper_srad(cpu_gpr[rA(ctx->opcode)], tcg_env, cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); @@ -3025,7 +2963,6 @@ static inline void gen_sradi(DisasContext *ctx, int n) t0 = tcg_temp_new(); tcg_gen_sari_tl(t0, src, TARGET_LONG_BITS - 1); tcg_gen_and_tl(cpu_ca, cpu_ca, t0); - tcg_temp_free(t0); tcg_gen_setcondi_tl(TCG_COND_NE, cpu_ca, cpu_ca, 0); if (is_isa300(ctx)) { tcg_gen_mov_tl(cpu_ca32, cpu_ca); @@ -3084,8 +3021,6 @@ static void gen_srd(DisasContext *ctx) t1 = tcg_temp_new(); tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x3f); tcg_gen_shr_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); } @@ -3252,12 +3187,11 @@ GEN_QEMU_STORE_64(st64r, BSWAP_MEMOP(MO_UQ)) static void glue(gen_, name##x)(DisasContext *ctx) \ { \ TCGv EA; \ - chk; \ + chk(ctx); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ gen_qemu_##ldop(ctx, cpu_gpr[rD(ctx->opcode)], EA); \ - tcg_temp_free(EA); \ } #define GEN_LDX(name, ldop, opc2, opc3, type) \ @@ -3270,12 +3204,11 @@ static void glue(gen_, name##x)(DisasContext *ctx) \ static void glue(gen_, name##epx)(DisasContext *ctx) \ { \ TCGv EA; \ - CHK_SV; \ + CHK_SV(ctx); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ tcg_gen_qemu_ld_tl(cpu_gpr[rD(ctx->opcode)], EA, PPC_TLB_EPID_LOAD, ldop);\ - tcg_temp_free(EA); \ } GEN_LDEPX(lb, DEF_MEMOP(MO_UB), 0x1F, 0x02) @@ -3298,12 +3231,11 @@ GEN_LDX_HVRM(lbzcix, ld8u, 0x15, 0x1a, PPC_CILDST) static void glue(gen_, name##x)(DisasContext *ctx) \ { \ TCGv EA; \ - chk; \ + chk(ctx); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ gen_qemu_##stop(ctx, cpu_gpr[rS(ctx->opcode)], EA); \ - tcg_temp_free(EA); \ } #define GEN_STX(name, stop, opc2, opc3, type) \ GEN_STX_E(name, stop, opc2, opc3, type, PPC_NONE, CHK_NONE) @@ -3315,13 +3247,12 @@ static void glue(gen_, name##x)(DisasContext *ctx) \ static void glue(gen_, name##epx)(DisasContext *ctx) \ { \ TCGv EA; \ - CHK_SV; \ + CHK_SV(ctx); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ tcg_gen_qemu_st_tl( \ cpu_gpr[rD(ctx->opcode)], EA, PPC_TLB_EPID_STORE, stop); \ - tcg_temp_free(EA); \ } GEN_STEPX(stb, DEF_MEMOP(MO_UB), 0x1F, 0x06) @@ -3371,11 +3302,9 @@ static void gen_lmw(DisasContext *ctx) } gen_set_access_type(ctx, ACCESS_INT); t0 = tcg_temp_new(); - t1 = tcg_const_i32(rD(ctx->opcode)); + t1 = tcg_constant_i32(rD(ctx->opcode)); gen_addr_imm_index(ctx, t0, 0); - gen_helper_lmw(cpu_env, t0, t1); - tcg_temp_free(t0); - tcg_temp_free_i32(t1); + gen_helper_lmw(tcg_env, t0, t1); } /* stmw */ @@ -3390,11 +3319,9 @@ static void gen_stmw(DisasContext *ctx) } gen_set_access_type(ctx, ACCESS_INT); t0 = tcg_temp_new(); - t1 = tcg_const_i32(rS(ctx->opcode)); + t1 = tcg_constant_i32(rS(ctx->opcode)); gen_addr_imm_index(ctx, t0, 0); - gen_helper_stmw(cpu_env, t0, t1); - tcg_temp_free(t0); - tcg_temp_free_i32(t1); + gen_helper_stmw(tcg_env, t0, t1); } /*** Integer load and store strings ***/ @@ -3430,12 +3357,9 @@ static void gen_lswi(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_INT); t0 = tcg_temp_new(); gen_addr_register(ctx, t0); - t1 = tcg_const_i32(nb); - t2 = tcg_const_i32(start); - gen_helper_lsw(cpu_env, t0, t1, t2); - tcg_temp_free(t0); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); + t1 = tcg_constant_i32(nb); + t2 = tcg_constant_i32(start); + gen_helper_lsw(tcg_env, t0, t1, t2); } /* lswx */ @@ -3451,14 +3375,10 @@ static void gen_lswx(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_INT); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - t1 = tcg_const_i32(rD(ctx->opcode)); - t2 = tcg_const_i32(rA(ctx->opcode)); - t3 = tcg_const_i32(rB(ctx->opcode)); - gen_helper_lswx(cpu_env, t0, t1, t2, t3); - tcg_temp_free(t0); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); + t1 = tcg_constant_i32(rD(ctx->opcode)); + t2 = tcg_constant_i32(rA(ctx->opcode)); + t3 = tcg_constant_i32(rB(ctx->opcode)); + gen_helper_lswx(tcg_env, t0, t1, t2, t3); } /* stswi */ @@ -3478,12 +3398,9 @@ static void gen_stswi(DisasContext *ctx) if (nb == 0) { nb = 32; } - t1 = tcg_const_i32(nb); - t2 = tcg_const_i32(rS(ctx->opcode)); - gen_helper_stsw(cpu_env, t0, t1, t2); - tcg_temp_free(t0); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); + t1 = tcg_constant_i32(nb); + t2 = tcg_constant_i32(rS(ctx->opcode)); + gen_helper_stsw(tcg_env, t0, t1, t2); } /* stswx */ @@ -3502,11 +3419,8 @@ static void gen_stswx(DisasContext *ctx) t1 = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(t1, cpu_xer); tcg_gen_andi_i32(t1, t1, 0x7F); - t2 = tcg_const_i32(rS(ctx->opcode)); - gen_helper_stsw(cpu_env, t0, t1, t2); - tcg_temp_free(t0); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); + t2 = tcg_constant_i32(rS(ctx->opcode)); + gen_helper_stsw(tcg_env, t0, t1, t2); } /*** Memory synchronisation ***/ @@ -3573,15 +3487,14 @@ static inline void gen_check_tlb_flush(DisasContext *ctx, bool global) } l = gen_new_label(); t = tcg_temp_new_i32(); - tcg_gen_ld_i32(t, cpu_env, offsetof(CPUPPCState, tlb_need_flush)); + tcg_gen_ld_i32(t, tcg_env, offsetof(CPUPPCState, tlb_need_flush)); tcg_gen_brcondi_i32(TCG_COND_EQ, t, 0, l); if (global) { - gen_helper_check_tlb_flush_global(cpu_env); + gen_helper_check_tlb_flush_global(tcg_env); } else { - gen_helper_check_tlb_flush_local(cpu_env); + gen_helper_check_tlb_flush_local(tcg_env); } gen_set_label(l); - tcg_temp_free_i32(t); } #else static inline void gen_check_tlb_flush(DisasContext *ctx, bool global) { } @@ -3612,9 +3525,8 @@ static void gen_load_locked(DisasContext *ctx, MemOp memop) gen_addr_reg_index(ctx, t0); tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, memop | MO_ALIGN); tcg_gen_mov_tl(cpu_reserve, t0); + tcg_gen_movi_tl(cpu_reserve_length, memop_size(memop)); tcg_gen_mov_tl(cpu_reserve_val, gpr); - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); - tcg_temp_free(t0); } #define LARX(name, memop) \ @@ -3648,10 +3560,6 @@ static void gen_fetch_inc_conditional(DisasContext *ctx, MemOp memop, /* RT = (t != t2 ? t : u = 1<<(s*8-1)) */ tcg_gen_movi_tl(u, 1 << (MEMOP_GET_SIZE(memop) * 8 - 1)); tcg_gen_movcond_tl(cond, cpu_gpr[rD(ctx->opcode)], t, t2, t, u); - - tcg_temp_free(t); - tcg_temp_free(t2); - tcg_temp_free(u); } static void gen_ld_atomic(DisasContext *ctx, MemOp memop) @@ -3714,9 +3622,6 @@ static void gen_ld_atomic(DisasContext *ctx, MemOp memop) cpu_gpr[(rt + 2) & 31], t0); tcg_gen_qemu_st_tl(t1, EA, ctx->mem_idx, memop); tcg_gen_mov_tl(dst, t0); - - tcg_temp_free(t0); - tcg_temp_free(t1); } break; @@ -3746,11 +3651,10 @@ static void gen_ld_atomic(DisasContext *ctx, MemOp memop) /* invoke data storage error handler */ gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL); } - tcg_temp_free(EA); if (need_serial) { /* Restart with exclusive lock. */ - gen_helper_exit_atomic(cpu_env); + gen_helper_exit_atomic(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; } } @@ -3806,7 +3710,7 @@ static void gen_st_atomic(DisasContext *ctx, MemOp memop) case 24: /* Store twin */ if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { /* Restart with exclusive lock. */ - gen_helper_exit_atomic(cpu_env); + gen_helper_exit_atomic(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; } else { TCGv t = tcg_temp_new(); @@ -3822,20 +3726,12 @@ static void gen_st_atomic(DisasContext *ctx, MemOp memop) tcg_gen_movcond_tl(TCG_COND_EQ, s2, t, t2, src, t2); tcg_gen_qemu_st_tl(s, EA, ctx->mem_idx, memop); tcg_gen_qemu_st_tl(s2, ea_plus_s, ctx->mem_idx, memop); - - tcg_temp_free(ea_plus_s); - tcg_temp_free(s2); - tcg_temp_free(s); - tcg_temp_free(t2); - tcg_temp_free(t); } break; default: /* invoke data storage error handler */ gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL); } - tcg_temp_free(discard); - tcg_temp_free(EA); } static void gen_stwat(DisasContext *ctx) @@ -3852,37 +3748,32 @@ static void gen_stdat(DisasContext *ctx) static void gen_conditional_store(DisasContext *ctx, MemOp memop) { - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - TCGv t0 = tcg_temp_new(); - int reg = rS(ctx->opcode); - - gen_set_access_type(ctx, ACCESS_RES); - gen_addr_reg_index(ctx, t0); - tcg_gen_brcond_tl(TCG_COND_NE, t0, cpu_reserve, l1); - tcg_temp_free(t0); + TCGLabel *lfail; + TCGv EA; + TCGv cr0; + TCGv t0; + int rs = rS(ctx->opcode); + lfail = gen_new_label(); + EA = tcg_temp_new(); + cr0 = tcg_temp_new(); t0 = tcg_temp_new(); + + tcg_gen_mov_tl(cr0, cpu_so); + gen_set_access_type(ctx, ACCESS_RES); + gen_addr_reg_index(ctx, EA); + tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, lfail); + tcg_gen_brcondi_tl(TCG_COND_NE, cpu_reserve_length, memop_size(memop), lfail); + tcg_gen_atomic_cmpxchg_tl(t0, cpu_reserve, cpu_reserve_val, - cpu_gpr[reg], ctx->mem_idx, + cpu_gpr[rs], ctx->mem_idx, DEF_MEMOP(memop) | MO_ALIGN); tcg_gen_setcond_tl(TCG_COND_EQ, t0, t0, cpu_reserve_val); tcg_gen_shli_tl(t0, t0, CRF_EQ_BIT); - tcg_gen_or_tl(t0, t0, cpu_so); - tcg_gen_trunc_tl_i32(cpu_crf[0], t0); - tcg_temp_free(t0); - tcg_gen_br(l2); + tcg_gen_or_tl(cr0, cr0, t0); - gen_set_label(l1); - - /* - * Address mismatch implies failure. But we still need to provide - * the memory barrier semantics of the instruction. - */ - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); - - gen_set_label(l2); + gen_set_label(lfail); + tcg_gen_trunc_tl_i32(cpu_crf[0], cr0); tcg_gen_movi_tl(cpu_reserve, -1); } @@ -3907,6 +3798,7 @@ static void gen_lqarx(DisasContext *ctx) { int rd = rD(ctx->opcode); TCGv EA, hi, lo; + TCGv_i128 t16; if (unlikely((rd & 1) || (rd == rA(ctx->opcode)) || (rd == rB(ctx->opcode)))) { @@ -3922,119 +3814,66 @@ static void gen_lqarx(DisasContext *ctx) lo = cpu_gpr[rd + 1]; hi = cpu_gpr[rd]; - if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { - if (HAVE_ATOMIC128) { - TCGv_i32 oi = tcg_temp_new_i32(); - if (ctx->le_mode) { - tcg_gen_movi_i32(oi, make_memop_idx(MO_LE | MO_128 | MO_ALIGN, - ctx->mem_idx)); - gen_helper_lq_le_parallel(lo, cpu_env, EA, oi); - } else { - tcg_gen_movi_i32(oi, make_memop_idx(MO_BE | MO_128 | MO_ALIGN, - ctx->mem_idx)); - gen_helper_lq_be_parallel(lo, cpu_env, EA, oi); - } - tcg_temp_free_i32(oi); - tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh)); - } else { - /* Restart with exclusive lock. */ - gen_helper_exit_atomic(cpu_env); - ctx->base.is_jmp = DISAS_NORETURN; - tcg_temp_free(EA); - return; - } - } else if (ctx->le_mode) { - tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_LEUQ | MO_ALIGN_16); - tcg_gen_mov_tl(cpu_reserve, EA); - gen_addr_add(ctx, EA, EA, 8); - tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_LEUQ); - } else { - tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_BEUQ | MO_ALIGN_16); - tcg_gen_mov_tl(cpu_reserve, EA); - gen_addr_add(ctx, EA, EA, 8); - tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_BEUQ); - } - tcg_temp_free(EA); + t16 = tcg_temp_new_i128(); + tcg_gen_qemu_ld_i128(t16, EA, ctx->mem_idx, DEF_MEMOP(MO_128 | MO_ALIGN)); + tcg_gen_extr_i128_i64(lo, hi, t16); - tcg_gen_st_tl(hi, cpu_env, offsetof(CPUPPCState, reserve_val)); - tcg_gen_st_tl(lo, cpu_env, offsetof(CPUPPCState, reserve_val2)); + tcg_gen_mov_tl(cpu_reserve, EA); + tcg_gen_movi_tl(cpu_reserve_length, 16); + tcg_gen_st_tl(hi, tcg_env, offsetof(CPUPPCState, reserve_val)); + tcg_gen_st_tl(lo, tcg_env, offsetof(CPUPPCState, reserve_val2)); } /* stqcx. */ static void gen_stqcx_(DisasContext *ctx) { + TCGLabel *lfail; + TCGv EA, t0, t1; + TCGv cr0; + TCGv_i128 cmp, val; int rs = rS(ctx->opcode); - TCGv EA, hi, lo; if (unlikely(rs & 1)) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); return; } - gen_set_access_type(ctx, ACCESS_RES); + lfail = gen_new_label(); EA = tcg_temp_new(); + cr0 = tcg_temp_new(); + + tcg_gen_mov_tl(cr0, cpu_so); + gen_set_access_type(ctx, ACCESS_RES); gen_addr_reg_index(ctx, EA); + tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, lfail); + tcg_gen_brcondi_tl(TCG_COND_NE, cpu_reserve_length, 16, lfail); + + cmp = tcg_temp_new_i128(); + val = tcg_temp_new_i128(); + + tcg_gen_concat_i64_i128(cmp, cpu_reserve_val2, cpu_reserve_val); /* Note that the low part is always in RS+1, even in LE mode. */ - lo = cpu_gpr[rs + 1]; - hi = cpu_gpr[rs]; + tcg_gen_concat_i64_i128(val, cpu_gpr[rs + 1], cpu_gpr[rs]); - if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { - if (HAVE_CMPXCHG128) { - TCGv_i32 oi = tcg_const_i32(DEF_MEMOP(MO_128) | MO_ALIGN); - if (ctx->le_mode) { - gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, - EA, lo, hi, oi); - } else { - gen_helper_stqcx_be_parallel(cpu_crf[0], cpu_env, - EA, lo, hi, oi); - } - tcg_temp_free_i32(oi); - } else { - /* Restart with exclusive lock. */ - gen_helper_exit_atomic(cpu_env); - ctx->base.is_jmp = DISAS_NORETURN; - } - tcg_temp_free(EA); - } else { - TCGLabel *lab_fail = gen_new_label(); - TCGLabel *lab_over = gen_new_label(); - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_atomic_cmpxchg_i128(val, cpu_reserve, cmp, val, ctx->mem_idx, + DEF_MEMOP(MO_128 | MO_ALIGN)); - tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, lab_fail); - tcg_temp_free(EA); + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + tcg_gen_extr_i128_i64(t1, t0, val); - gen_qemu_ld64_i64(ctx, t0, cpu_reserve); - tcg_gen_ld_i64(t1, cpu_env, (ctx->le_mode - ? offsetof(CPUPPCState, reserve_val2) - : offsetof(CPUPPCState, reserve_val))); - tcg_gen_brcond_i64(TCG_COND_NE, t0, t1, lab_fail); + tcg_gen_xor_tl(t1, t1, cpu_reserve_val2); + tcg_gen_xor_tl(t0, t0, cpu_reserve_val); + tcg_gen_or_tl(t0, t0, t1); - tcg_gen_addi_i64(t0, cpu_reserve, 8); - gen_qemu_ld64_i64(ctx, t0, t0); - tcg_gen_ld_i64(t1, cpu_env, (ctx->le_mode - ? offsetof(CPUPPCState, reserve_val) - : offsetof(CPUPPCState, reserve_val2))); - tcg_gen_brcond_i64(TCG_COND_NE, t0, t1, lab_fail); + tcg_gen_setcondi_tl(TCG_COND_EQ, t0, t0, 0); + tcg_gen_shli_tl(t0, t0, CRF_EQ_BIT); + tcg_gen_or_tl(cr0, cr0, t0); - /* Success */ - gen_qemu_st64_i64(ctx, ctx->le_mode ? lo : hi, cpu_reserve); - tcg_gen_addi_i64(t0, cpu_reserve, 8); - gen_qemu_st64_i64(ctx, ctx->le_mode ? hi : lo, t0); - - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); - tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ); - tcg_gen_br(lab_over); - - gen_set_label(lab_fail); - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); - - gen_set_label(lab_over); - tcg_gen_movi_tl(cpu_reserve, -1); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - } + gen_set_label(lfail); + tcg_gen_trunc_tl_i32(cpu_crf[0], cr0); + tcg_gen_movi_tl(cpu_reserve, -1); } #endif /* defined(TARGET_PPC64) */ @@ -4066,26 +3905,104 @@ static void gen_sync(DisasContext *ctx) /* wait */ static void gen_wait(DisasContext *ctx) { - TCGv_i32 t0 = tcg_const_i32(1); - tcg_gen_st_i32(t0, cpu_env, - -offsetof(PowerPCCPU, env) + offsetof(CPUState, halted)); - tcg_temp_free_i32(t0); - /* Stop translation, as the CPU is supposed to sleep from now */ - gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); + uint32_t wc; + + if (ctx->insns_flags & PPC_WAIT) { + /* v2.03-v2.07 define an older incompatible 'wait' encoding. */ + + if (ctx->insns_flags2 & PPC2_PM_ISA206) { + /* v2.06 introduced the WC field. WC > 0 may be treated as no-op. */ + wc = WC(ctx->opcode); + } else { + wc = 0; + } + + } else if (ctx->insns_flags2 & PPC2_ISA300) { + /* v3.0 defines a new 'wait' encoding. */ + wc = WC(ctx->opcode); + if (ctx->insns_flags2 & PPC2_ISA310) { + uint32_t pl = PL(ctx->opcode); + + /* WC 1,2 may be treated as no-op. WC 3 is reserved. */ + if (wc == 3) { + gen_invalid(ctx); + return; + } + + /* PL 1-3 are reserved. If WC=2 then the insn is treated as noop. */ + if (pl > 0 && wc != 2) { + gen_invalid(ctx); + return; + } + + } else { /* ISA300 */ + /* WC 1-3 are reserved */ + if (wc > 0) { + gen_invalid(ctx); + return; + } + } + + } else { + warn_report("wait instruction decoded with wrong ISA flags."); + gen_invalid(ctx); + return; + } + + /* + * wait without WC field or with WC=0 waits for an exception / interrupt + * to occur. + */ + if (wc == 0) { + TCGv_i32 t0 = tcg_constant_i32(1); + tcg_gen_st_i32(t0, tcg_env, + -offsetof(PowerPCCPU, env) + offsetof(CPUState, halted)); + /* Stop translation, as the CPU is supposed to sleep from now */ + gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); + } + + /* + * Other wait types must not just wait until an exception occurs because + * ignoring their other wake-up conditions could cause a hang. + * + * For v2.06 and 2.07, wc=1,2,3 are architected but may be implemented as + * no-ops. + * + * wc=1 and wc=3 explicitly allow the instruction to be treated as a no-op. + * + * wc=2 waits for an implementation-specific condition, such could be + * always true, so it can be implemented as a no-op. + * + * For v3.1, wc=1,2 are architected but may be implemented as no-ops. + * + * wc=1 (waitrsv) waits for an exception or a reservation to be lost. + * Reservation-loss may have implementation-specific conditions, so it + * can be implemented as a no-op. + * + * wc=2 waits for an exception or an amount of time to pass. This + * amount is implementation-specific so it can be implemented as a + * no-op. + * + * ISA v3.1 allows for execution to resume "in the rare case of + * an implementation-dependent event", so in any case software must + * not depend on the architected resumption condition to become + * true, so no-op implementations should be architecturally correct + * (if suboptimal). + */ } #if defined(TARGET_PPC64) static void gen_doze(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv_i32 t; - CHK_HV; - t = tcg_const_i32(PPC_PM_DOZE); - gen_helper_pminsn(cpu_env, t); - tcg_temp_free_i32(t); + CHK_HV(ctx); + translator_io_start(&ctx->base); + t = tcg_constant_i32(PPC_PM_DOZE); + gen_helper_pminsn(tcg_env, t); /* Stop translation, as the CPU is supposed to sleep from now */ gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); #endif /* defined(CONFIG_USER_ONLY) */ @@ -4094,14 +4011,14 @@ static void gen_doze(DisasContext *ctx) static void gen_nap(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv_i32 t; - CHK_HV; - t = tcg_const_i32(PPC_PM_NAP); - gen_helper_pminsn(cpu_env, t); - tcg_temp_free_i32(t); + CHK_HV(ctx); + translator_io_start(&ctx->base); + t = tcg_constant_i32(PPC_PM_NAP); + gen_helper_pminsn(tcg_env, t); /* Stop translation, as the CPU is supposed to sleep from now */ gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); #endif /* defined(CONFIG_USER_ONLY) */ @@ -4110,14 +4027,14 @@ static void gen_nap(DisasContext *ctx) static void gen_stop(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv_i32 t; - CHK_HV; - t = tcg_const_i32(PPC_PM_STOP); - gen_helper_pminsn(cpu_env, t); - tcg_temp_free_i32(t); + CHK_HV(ctx); + translator_io_start(&ctx->base); + t = tcg_constant_i32(PPC_PM_STOP); + gen_helper_pminsn(tcg_env, t); /* Stop translation, as the CPU is supposed to sleep from now */ gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); #endif /* defined(CONFIG_USER_ONLY) */ @@ -4126,14 +4043,14 @@ static void gen_stop(DisasContext *ctx) static void gen_sleep(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv_i32 t; - CHK_HV; - t = tcg_const_i32(PPC_PM_SLEEP); - gen_helper_pminsn(cpu_env, t); - tcg_temp_free_i32(t); + CHK_HV(ctx); + translator_io_start(&ctx->base); + t = tcg_constant_i32(PPC_PM_SLEEP); + gen_helper_pminsn(tcg_env, t); /* Stop translation, as the CPU is supposed to sleep from now */ gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); #endif /* defined(CONFIG_USER_ONLY) */ @@ -4142,14 +4059,14 @@ static void gen_sleep(DisasContext *ctx) static void gen_rvwinkle(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv_i32 t; - CHK_HV; - t = tcg_const_i32(PPC_PM_RVWINKLE); - gen_helper_pminsn(cpu_env, t); - tcg_temp_free_i32(t); + CHK_HV(ctx); + translator_io_start(&ctx->base); + t = tcg_constant_i32(PPC_PM_RVWINKLE); + gen_helper_pminsn(tcg_env, t); /* Stop translation, as the CPU is supposed to sleep from now */ gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); #endif /* defined(CONFIG_USER_ONLY) */ @@ -4177,16 +4094,36 @@ static void pmu_count_insns(DisasContext *ctx) } #if !defined(CONFIG_USER_ONLY) + TCGLabel *l; + TCGv t0; + /* * The PMU insns_inc() helper stops the internal PMU timer if a * counter overflows happens. In that case, if the guest is * running with icount and we do not handle it beforehand, * the helper can trigger a 'bad icount read'. */ - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); - gen_helper_insns_inc(cpu_env, tcg_constant_i32(ctx->base.num_insns)); -#else + /* Avoid helper calls when only PMC5-6 are enabled. */ + if (!ctx->pmc_other) { + l = gen_new_label(); + t0 = tcg_temp_new(); + + gen_load_spr(t0, SPR_POWER_PMC5); + tcg_gen_addi_tl(t0, t0, ctx->base.num_insns); + gen_store_spr(SPR_POWER_PMC5, t0); + /* Check for overflow, if it's enabled */ + if (ctx->mmcr0_pmcjce) { + tcg_gen_brcondi_tl(TCG_COND_LT, t0, PMC_COUNTER_NEGATIVE_VAL, l); + gen_helper_handle_pmc5_overflow(tcg_env); + } + + gen_set_label(l); + } else { + gen_helper_insns_inc(tcg_env, tcg_constant_i32(ctx->base.num_insns)); + } + #else /* * User mode can read (but not write) PMC5 and start/stop * the PMU via MMCR0_FC. In this case just increment @@ -4197,9 +4134,7 @@ static void pmu_count_insns(DisasContext *ctx) gen_load_spr(t0, SPR_POWER_PMC5); tcg_gen_addi_tl(t0, t0, ctx->base.num_insns); gen_store_spr(SPR_POWER_PMC5, t0); - - tcg_temp_free(t0); -#endif /* #if !defined(CONFIG_USER_ONLY) */ + #endif /* #if !defined(CONFIG_USER_ONLY) */ } #else static void pmu_count_insns(DisasContext *ctx) @@ -4210,13 +4145,16 @@ static void pmu_count_insns(DisasContext *ctx) static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) { + if (unlikely(ctx->singlestep_enabled)) { + return false; + } return translator_use_goto_tb(&ctx->base, dest); } static void gen_lookup_and_goto_ptr(DisasContext *ctx) { if (unlikely(ctx->singlestep_enabled)) { - gen_debug_exception(ctx); + gen_debug_exception(ctx, false); } else { /* * tcg_gen_lookup_and_goto_ptr will exit the TB if @@ -4288,7 +4226,7 @@ static void gen_bcond(DisasContext *ctx, int type) TCGv target; if (type == BCOND_LR || type == BCOND_CTR || type == BCOND_TAR) { - target = tcg_temp_local_new(); + target = tcg_temp_new(); if (type == BCOND_CTR) { tcg_gen_mov_tl(target, cpu_ctr); } else if (type == BCOND_TAR) { @@ -4324,8 +4262,6 @@ static void gen_bcond(DisasContext *ctx, int type) */ if (unlikely(!is_book3s_arch2x(ctx))) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); - tcg_temp_free(temp); - tcg_temp_free(target); return; } @@ -4353,7 +4289,6 @@ static void gen_bcond(DisasContext *ctx, int type) tcg_gen_brcondi_tl(TCG_COND_EQ, temp, 0, l1); } } - tcg_temp_free(temp); } if ((bo & 0x10) == 0) { /* Test CR */ @@ -4368,7 +4303,6 @@ static void gen_bcond(DisasContext *ctx, int type) tcg_gen_andi_i32(temp, cpu_crf[bi >> 2], mask); tcg_gen_brcondi_i32(TCG_COND_NE, temp, 0, l1); } - tcg_temp_free_i32(temp); } gen_update_cfar(ctx, ctx->cia); if (type == BCOND_IM) { @@ -4385,7 +4319,6 @@ static void gen_bcond(DisasContext *ctx, int type) tcg_gen_andi_tl(cpu_nip, target, ~3); } gen_lookup_and_goto_ptr(ctx); - tcg_temp_free(target); } if ((bo & 0x14) != 0x14) { /* fallthrough case */ @@ -4443,8 +4376,6 @@ static void glue(gen_, name)(DisasContext *ctx) \ tcg_gen_andi_i32(t0, t0, bitmask); \ tcg_gen_andi_i32(t1, cpu_crf[crbD(ctx->opcode) >> 2], ~bitmask); \ tcg_gen_or_i32(cpu_crf[crbD(ctx->opcode) >> 2], t0, t1); \ - tcg_temp_free_i32(t0); \ - tcg_temp_free_i32(t1); \ } /* crand */ @@ -4476,7 +4407,7 @@ static void gen_mcrf(DisasContext *ctx) static void gen_rfi(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else /* * This instruction doesn't exist anymore on 64-bit server @@ -4487,10 +4418,10 @@ static void gen_rfi(DisasContext *ctx) return; } /* Restore CPU state */ - CHK_SV; - gen_icount_io_start(ctx); + CHK_SV(ctx); + translator_io_start(&ctx->base); gen_update_cfar(ctx, ctx->cia); - gen_helper_rfi(cpu_env); + gen_helper_rfi(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif } @@ -4499,13 +4430,13 @@ static void gen_rfi(DisasContext *ctx) static void gen_rfid(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else /* Restore CPU state */ - CHK_SV; - gen_icount_io_start(ctx); + CHK_SV(ctx); + translator_io_start(&ctx->base); gen_update_cfar(ctx, ctx->cia); - gen_helper_rfid(cpu_env); + gen_helper_rfid(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif } @@ -4514,13 +4445,13 @@ static void gen_rfid(DisasContext *ctx) static void gen_rfscv(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else /* Restore CPU state */ - CHK_SV; - gen_icount_io_start(ctx); + CHK_SV(ctx); + translator_io_start(&ctx->base); gen_update_cfar(ctx, ctx->cia); - gen_helper_rfscv(cpu_env); + gen_helper_rfscv(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif } @@ -4529,11 +4460,12 @@ static void gen_rfscv(DisasContext *ctx) static void gen_hrfid(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else /* Restore CPU state */ - CHK_HV; - gen_helper_hrfid(cpu_env); + CHK_HV(ctx); + translator_io_start(&ctx->base); + gen_helper_hrfid(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif } @@ -4544,13 +4476,17 @@ static void gen_hrfid(DisasContext *ctx) #define POWERPC_SYSCALL POWERPC_EXCP_SYSCALL_USER #else #define POWERPC_SYSCALL POWERPC_EXCP_SYSCALL -#define POWERPC_SYSCALL_VECTORED POWERPC_EXCP_SYSCALL_VECTORED #endif static void gen_sc(DisasContext *ctx) { uint32_t lev; - lev = (ctx->opcode >> 5) & 0x7F; + /* + * LEV is a 7-bit field, but the top 6 bits are treated as a reserved + * field (i.e., ignored). ISA v3.1 changes that to 5 bits, but that is + * for Ultravisor which TCG does not support, so just ignore the top 6. + */ + lev = (ctx->opcode >> 5) & 0x1; gen_exception_err(ctx, POWERPC_SYSCALL, lev); } @@ -4562,7 +4498,7 @@ static void gen_scv(DisasContext *ctx) /* Set the PC back to the faulting instruction. */ gen_update_nip(ctx, ctx->cia); - gen_helper_scv(cpu_env, tcg_constant_i32(lev)); + gen_helper_scv(tcg_env, tcg_constant_i32(lev)); ctx->base.is_jmp = DISAS_NORETURN; } @@ -4594,10 +4530,9 @@ static void gen_tw(DisasContext *ctx) if (check_unconditional_trap(ctx)) { return; } - t0 = tcg_const_i32(TO(ctx->opcode)); - gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], + t0 = tcg_constant_i32(TO(ctx->opcode)); + gen_helper_tw(tcg_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); - tcg_temp_free_i32(t0); } /* twi */ @@ -4609,11 +4544,9 @@ static void gen_twi(DisasContext *ctx) if (check_unconditional_trap(ctx)) { return; } - t0 = tcg_const_tl(SIMM(ctx->opcode)); - t1 = tcg_const_i32(TO(ctx->opcode)); - gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, t1); - tcg_temp_free(t0); - tcg_temp_free_i32(t1); + t0 = tcg_constant_tl(SIMM(ctx->opcode)); + t1 = tcg_constant_i32(TO(ctx->opcode)); + gen_helper_tw(tcg_env, cpu_gpr[rA(ctx->opcode)], t0, t1); } #if defined(TARGET_PPC64) @@ -4625,10 +4558,9 @@ static void gen_td(DisasContext *ctx) if (check_unconditional_trap(ctx)) { return; } - t0 = tcg_const_i32(TO(ctx->opcode)); - gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], + t0 = tcg_constant_i32(TO(ctx->opcode)); + gen_helper_td(tcg_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); - tcg_temp_free_i32(t0); } /* tdi */ @@ -4640,11 +4572,9 @@ static void gen_tdi(DisasContext *ctx) if (check_unconditional_trap(ctx)) { return; } - t0 = tcg_const_tl(SIMM(ctx->opcode)); - t1 = tcg_const_i32(TO(ctx->opcode)); - gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, t1); - tcg_temp_free(t0); - tcg_temp_free_i32(t1); + t0 = tcg_constant_tl(SIMM(ctx->opcode)); + t1 = tcg_constant_i32(TO(ctx->opcode)); + gen_helper_td(tcg_env, cpu_gpr[rA(ctx->opcode)], t0, t1); } #endif @@ -4665,8 +4595,6 @@ static void gen_mcrxr(DisasContext *ctx) tcg_gen_shli_i32(dst, dst, 1); tcg_gen_or_i32(dst, dst, t0); tcg_gen_or_i32(dst, dst, t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); tcg_gen_movi_tl(cpu_so, 0); tcg_gen_movi_tl(cpu_ov, 0); @@ -4690,8 +4618,6 @@ static void gen_mcrxrx(DisasContext *ctx) tcg_gen_or_tl(t1, t1, cpu_ca32); tcg_gen_or_tl(t0, t0, t1); tcg_gen_trunc_tl_i32(dst, t0); - tcg_temp_free(t0); - tcg_temp_free(t1); } #endif @@ -4726,14 +4652,13 @@ static void gen_mfcr(DisasContext *ctx) tcg_gen_shli_i32(t0, t0, 4); tcg_gen_or_i32(t0, t0, cpu_crf[7]); tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); - tcg_temp_free_i32(t0); } } /* mfmsr */ static void gen_mfmsr(DisasContext *ctx) { - CHK_SV; + CHK_SV(ctx); tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_msr); } @@ -4789,11 +4714,11 @@ static inline void gen_op_mfspr(DisasContext *ctx) */ if (sprn & 0x10) { if (ctx->pr) { - gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); } } else { if (ctx->pr || sprn == 0 || sprn == 4 || sprn == 5 || sprn == 6) { - gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + gen_hvpriv_exception(ctx, POWERPC_EXCP_PRIV_REG); } } } @@ -4823,7 +4748,6 @@ static void gen_mtcrf(DisasContext *ctx) tcg_gen_trunc_tl_i32(temp, cpu_gpr[rS(ctx->opcode)]); tcg_gen_shri_i32(temp, temp, crn * 4); tcg_gen_andi_i32(cpu_crf[7 - crn], temp, 0xf); - tcg_temp_free_i32(temp); } } else { TCGv_i32 temp = tcg_temp_new_i32(); @@ -4834,7 +4758,6 @@ static void gen_mtcrf(DisasContext *ctx) tcg_gen_andi_i32(cpu_crf[7 - crn], cpu_crf[7 - crn], 0xf); } } - tcg_temp_free_i32(temp); } } @@ -4847,7 +4770,7 @@ static void gen_mtmsrd(DisasContext *ctx) return; } - CHK_SV; + CHK_SV(ctx); #if !defined(CONFIG_USER_ONLY) TCGv t0, t1; @@ -4856,7 +4779,7 @@ static void gen_mtmsrd(DisasContext *ctx) t0 = tcg_temp_new(); t1 = tcg_temp_new(); - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); if (ctx->opcode & 0x00010000) { /* L=1 form only updates EE and RI */ @@ -4877,20 +4800,17 @@ static void gen_mtmsrd(DisasContext *ctx) tcg_gen_andi_tl(t1, cpu_msr, ~mask); tcg_gen_or_tl(t0, t0, t1); - gen_helper_store_msr(cpu_env, t0); + gen_helper_store_msr(tcg_env, t0); /* Must stop the translation as machine state (may have) changed */ ctx->base.is_jmp = DISAS_EXIT_UPDATE; - - tcg_temp_free(t0); - tcg_temp_free(t1); #endif /* !defined(CONFIG_USER_ONLY) */ } #endif /* defined(TARGET_PPC64) */ static void gen_mtmsr(DisasContext *ctx) { - CHK_SV; + CHK_SV(ctx); #if !defined(CONFIG_USER_ONLY) TCGv t0, t1; @@ -4899,7 +4819,7 @@ static void gen_mtmsr(DisasContext *ctx) t0 = tcg_temp_new(); t1 = tcg_temp_new(); - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); if (ctx->opcode & 0x00010000) { /* L=1 form only updates EE and RI */ mask &= (1ULL << MSR_RI) | (1ULL << MSR_EE); @@ -4919,13 +4839,10 @@ static void gen_mtmsr(DisasContext *ctx) tcg_gen_andi_tl(t1, cpu_msr, ~mask); tcg_gen_or_tl(t0, t0, t1); - gen_helper_store_msr(cpu_env, t0); + gen_helper_store_msr(tcg_env, t0); /* Must stop the translation as machine state (may have) changed */ ctx->base.is_jmp = DISAS_EXIT_UPDATE; - - tcg_temp_free(t0); - tcg_temp_free(t1); #endif } @@ -4976,11 +4893,11 @@ static void gen_mtspr(DisasContext *ctx) */ if (sprn & 0x10) { if (ctx->pr) { - gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); } } else { if (ctx->pr || sprn == 0) { - gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + gen_hvpriv_exception(ctx, POWERPC_EXCP_PRIV_REG); } } } @@ -4998,8 +4915,6 @@ static void gen_setb(DisasContext *ctx) tcg_gen_setcondi_i32(TCG_COND_GEU, t0, cpu_crf[crf], 4); tcg_gen_movcond_i32(TCG_COND_GEU, t0, cpu_crf[crf], t8, tm1, t0); tcg_gen_ext_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); - - tcg_temp_free_i32(t0); } #endif @@ -5014,7 +4929,6 @@ static void gen_dcbf(DisasContext *ctx) t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); gen_qemu_ld8u(ctx, t0, t0); - tcg_temp_free(t0); } /* dcbfep (external PID dcbf) */ @@ -5022,23 +4936,22 @@ static void gen_dcbfep(DisasContext *ctx) { /* XXX: specification says this is treated as a load by the MMU */ TCGv t0; - CHK_SV; + CHK_SV(ctx); gen_set_access_type(ctx, ACCESS_CACHE); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); tcg_gen_qemu_ld_tl(t0, t0, PPC_TLB_EPID_LOAD, DEF_MEMOP(MO_UB)); - tcg_temp_free(t0); } /* dcbi (Supervisor only) */ static void gen_dcbi(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv EA, val; - CHK_SV; + CHK_SV(ctx); EA = tcg_temp_new(); gen_set_access_type(ctx, ACCESS_CACHE); gen_addr_reg_index(ctx, EA); @@ -5046,8 +4959,6 @@ static void gen_dcbi(DisasContext *ctx) /* XXX: specification says this should be treated as a store by the MMU */ gen_qemu_ld8u(ctx, val, EA); gen_qemu_st8(ctx, val, EA); - tcg_temp_free(val); - tcg_temp_free(EA); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5060,7 +4971,6 @@ static void gen_dcbst(DisasContext *ctx) t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); gen_qemu_ld8u(ctx, t0, t0); - tcg_temp_free(t0); } /* dcbstep (dcbstep External PID version) */ @@ -5072,7 +4982,6 @@ static void gen_dcbstep(DisasContext *ctx) t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); tcg_gen_qemu_ld_tl(t0, t0, PPC_TLB_EPID_LOAD, DEF_MEMOP(MO_UB)); - tcg_temp_free(t0); } /* dcbt */ @@ -5123,7 +5032,14 @@ static void gen_dcbtls(DisasContext *ctx) gen_load_spr(t0, SPR_Exxx_L1CSR0); tcg_gen_ori_tl(t0, t0, L1CSR0_CUL); gen_store_spr(SPR_Exxx_L1CSR0, t0); - tcg_temp_free(t0); +} + +/* dcblc */ +static void gen_dcblc(DisasContext *ctx) +{ + /* + * interpreted as no-op + */ } /* dcbz */ @@ -5134,11 +5050,9 @@ static void gen_dcbz(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_CACHE); tcgv_addr = tcg_temp_new(); - tcgv_op = tcg_const_i32(ctx->opcode & 0x03FF000); + tcgv_op = tcg_constant_i32(ctx->opcode & 0x03FF000); gen_addr_reg_index(ctx, tcgv_addr); - gen_helper_dcbz(cpu_env, tcgv_addr, tcgv_op); - tcg_temp_free(tcgv_addr); - tcg_temp_free_i32(tcgv_op); + gen_helper_dcbz(tcg_env, tcgv_addr, tcgv_op); } /* dcbzep */ @@ -5149,11 +5063,9 @@ static void gen_dcbzep(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_CACHE); tcgv_addr = tcg_temp_new(); - tcgv_op = tcg_const_i32(ctx->opcode & 0x03FF000); + tcgv_op = tcg_constant_i32(ctx->opcode & 0x03FF000); gen_addr_reg_index(ctx, tcgv_addr); - gen_helper_dcbzep(cpu_env, tcgv_addr, tcgv_op); - tcg_temp_free(tcgv_addr); - tcg_temp_free_i32(tcgv_op); + gen_helper_dcbzep(tcg_env, tcgv_addr, tcgv_op); } /* dst / dstt */ @@ -5190,8 +5102,7 @@ static void gen_icbi(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_CACHE); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_icbi(cpu_env, t0); - tcg_temp_free(t0); + gen_helper_icbi(tcg_env, t0); } /* icbiep */ @@ -5201,8 +5112,7 @@ static void gen_icbiep(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_CACHE); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_icbiep(cpu_env, t0); - tcg_temp_free(t0); + gen_helper_icbiep(tcg_env, t0); } /* Optional: */ @@ -5223,14 +5133,13 @@ static void gen_dcba(DisasContext *ctx) static void gen_mfsr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; - t0 = tcg_const_tl(SR(ctx->opcode)); - gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); - tcg_temp_free(t0); + CHK_SV(ctx); + t0 = tcg_constant_tl(SR(ctx->opcode)); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], tcg_env, t0); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5238,15 +5147,14 @@ static void gen_mfsr(DisasContext *ctx) static void gen_mfsrin(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); tcg_gen_extract_tl(t0, cpu_gpr[rB(ctx->opcode)], 28, 4); - gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); - tcg_temp_free(t0); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], tcg_env, t0); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5254,14 +5162,13 @@ static void gen_mfsrin(DisasContext *ctx) static void gen_mtsr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; - t0 = tcg_const_tl(SR(ctx->opcode)); - gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); - tcg_temp_free(t0); + CHK_SV(ctx); + t0 = tcg_constant_tl(SR(ctx->opcode)); + gen_helper_store_sr(tcg_env, t0, cpu_gpr[rS(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5269,15 +5176,14 @@ static void gen_mtsr(DisasContext *ctx) static void gen_mtsrin(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); tcg_gen_extract_tl(t0, cpu_gpr[rB(ctx->opcode)], 28, 4); - gen_helper_store_sr(cpu_env, t0, cpu_gpr[rD(ctx->opcode)]); - tcg_temp_free(t0); + gen_helper_store_sr(tcg_env, t0, cpu_gpr[rD(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5288,14 +5194,13 @@ static void gen_mtsrin(DisasContext *ctx) static void gen_mfsr_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; - t0 = tcg_const_tl(SR(ctx->opcode)); - gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); - tcg_temp_free(t0); + CHK_SV(ctx); + t0 = tcg_constant_tl(SR(ctx->opcode)); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], tcg_env, t0); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5303,15 +5208,14 @@ static void gen_mfsr_64b(DisasContext *ctx) static void gen_mfsrin_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); tcg_gen_extract_tl(t0, cpu_gpr[rB(ctx->opcode)], 28, 4); - gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); - tcg_temp_free(t0); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], tcg_env, t0); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5319,14 +5223,13 @@ static void gen_mfsrin_64b(DisasContext *ctx) static void gen_mtsr_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; - t0 = tcg_const_tl(SR(ctx->opcode)); - gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); - tcg_temp_free(t0); + CHK_SV(ctx); + t0 = tcg_constant_tl(SR(ctx->opcode)); + gen_helper_store_sr(tcg_env, t0, cpu_gpr[rS(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5334,79 +5237,17 @@ static void gen_mtsr_64b(DisasContext *ctx) static void gen_mtsrin_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); tcg_gen_extract_tl(t0, cpu_gpr[rB(ctx->opcode)], 28, 4); - gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); - tcg_temp_free(t0); + gen_helper_store_sr(tcg_env, t0, cpu_gpr[rS(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } -/* slbmte */ -static void gen_slbmte(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_SV; - - gen_helper_store_slb(cpu_env, cpu_gpr[rB(ctx->opcode)], - cpu_gpr[rS(ctx->opcode)]); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -static void gen_slbmfee(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_SV; - - gen_helper_load_slb_esid(cpu_gpr[rS(ctx->opcode)], cpu_env, - cpu_gpr[rB(ctx->opcode)]); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -static void gen_slbmfev(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_SV; - - gen_helper_load_slb_vsid(cpu_gpr[rS(ctx->opcode)], cpu_env, - cpu_gpr[rB(ctx->opcode)]); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -static void gen_slbfee_(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); -#else - TCGLabel *l1, *l2; - - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } - gen_helper_find_slb_vsid(cpu_gpr[rS(ctx->opcode)], cpu_env, - cpu_gpr[rB(ctx->opcode)]); - l1 = gen_new_label(); - l2 = gen_new_label(); - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rS(ctx->opcode)], -1, l1); - tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_movi_tl(cpu_gpr[rS(ctx->opcode)], 0); - gen_set_label(l2); -#endif -} #endif /* defined(TARGET_PPC64) */ /*** Lookaside buffer management ***/ @@ -5416,83 +5257,25 @@ static void gen_slbfee_(DisasContext *ctx) static void gen_tlbia(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_HV; + CHK_HV(ctx); - gen_helper_tlbia(cpu_env); + gen_helper_tlbia(tcg_env); #endif /* defined(CONFIG_USER_ONLY) */ } -/* tlbiel */ -static void gen_tlbiel(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - bool psr = (ctx->opcode >> 17) & 0x1; - - if (ctx->pr || (!ctx->hv && !psr && ctx->hr)) { - /* - * tlbiel is privileged except when PSR=0 and HR=1, making it - * hypervisor privileged. - */ - GEN_PRIV; - } - - gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -/* tlbie */ -static void gen_tlbie(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - bool psr = (ctx->opcode >> 17) & 0x1; - TCGv_i32 t1; - - if (ctx->pr) { - /* tlbie is privileged... */ - GEN_PRIV; - } else if (!ctx->hv) { - if (!ctx->gtse || (!psr && ctx->hr)) { - /* - * ... except when GTSE=0 or when PSR=0 and HR=1, making it - * hypervisor privileged. - */ - GEN_PRIV; - } - } - - if (NARROW_MODE(ctx)) { - TCGv t0 = tcg_temp_new(); - tcg_gen_ext32u_tl(t0, cpu_gpr[rB(ctx->opcode)]); - gen_helper_tlbie(cpu_env, t0); - tcg_temp_free(t0); - } else { - gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); - } - t1 = tcg_temp_new_i32(); - tcg_gen_ld_i32(t1, cpu_env, offsetof(CPUPPCState, tlb_need_flush)); - tcg_gen_ori_i32(t1, t1, TLB_NEED_GLOBAL_FLUSH); - tcg_gen_st_i32(t1, cpu_env, offsetof(CPUPPCState, tlb_need_flush)); - tcg_temp_free_i32(t1); -#endif /* defined(CONFIG_USER_ONLY) */ -} - /* tlbsync */ static void gen_tlbsync(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else if (ctx->gtse) { - CHK_SV; /* If gtse is set then tlbsync is supervisor privileged */ + CHK_SV(ctx); /* If gtse is set then tlbsync is supervisor privileged */ } else { - CHK_HV; /* Else hypervisor privileged */ + CHK_HV(ctx); /* Else hypervisor privileged */ } /* BookS does both ptesync and tlbsync make tlbsync a nop for server */ @@ -5502,60 +5285,6 @@ static void gen_tlbsync(DisasContext *ctx) #endif /* defined(CONFIG_USER_ONLY) */ } -#if defined(TARGET_PPC64) -/* slbia */ -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, t0); - tcg_temp_free_i32(t0); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -/* slbie */ -static void gen_slbie(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_SV; - - gen_helper_slbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -/* slbieg */ -static void gen_slbieg(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_SV; - - gen_helper_slbieg(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -/* slbsync */ -static void gen_slbsync(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_SV; - gen_check_tlb_flush(ctx, true); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -#endif /* defined(TARGET_PPC64) */ - /*** External control ***/ /* Optional: */ @@ -5569,7 +5298,6 @@ static void gen_eciwx(DisasContext *ctx) gen_addr_reg_index(ctx, t0); tcg_gen_qemu_ld_tl(cpu_gpr[rD(ctx->opcode)], t0, ctx->mem_idx, DEF_MEMOP(MO_UL | MO_ALIGN)); - tcg_temp_free(t0); } /* ecowx */ @@ -5582,7 +5310,6 @@ static void gen_ecowx(DisasContext *ctx) gen_addr_reg_index(ctx, t0); tcg_gen_qemu_st_tl(cpu_gpr[rD(ctx->opcode)], t0, ctx->mem_idx, DEF_MEMOP(MO_UL | MO_ALIGN)); - tcg_temp_free(t0); } /* 602 - 603 - G2 TLB management */ @@ -5591,10 +5318,10 @@ static void gen_ecowx(DisasContext *ctx) static void gen_tlbld_6xx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; - gen_helper_6xx_tlbd(cpu_env, cpu_gpr[rB(ctx->opcode)]); + CHK_SV(ctx); + gen_helper_6xx_tlbd(tcg_env, cpu_gpr[rB(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5602,10 +5329,10 @@ static void gen_tlbld_6xx(DisasContext *ctx) static void gen_tlbli_6xx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; - gen_helper_6xx_tlbi(cpu_env, cpu_gpr[rB(ctx->opcode)]); + CHK_SV(ctx); + gen_helper_6xx_tlbi(tcg_env, cpu_gpr[rB(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5622,15 +5349,14 @@ static void gen_mfapidi(DisasContext *ctx) static void gen_tlbiva(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_tlbiva(cpu_env, cpu_gpr[rB(ctx->opcode)]); - tcg_temp_free(t0); + gen_helper_tlbiva(tcg_env, cpu_gpr[rB(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5640,8 +5366,8 @@ static inline void gen_405_mulladd_insn(DisasContext *ctx, int opc2, int opc3, { TCGv t0, t1; - t0 = tcg_temp_local_new(); - t1 = tcg_temp_local_new(); + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); switch (opc3 & 0x0D) { case 0x05: @@ -5748,8 +5474,6 @@ static inline void gen_405_mulladd_insn(DisasContext *ctx, int opc2, int opc3, } else { tcg_gen_mul_tl(cpu_gpr[rt], t0, t1); } - tcg_temp_free(t0); - tcg_temp_free(t1); if (unlikely(Rc) != 0) { /* Update Rc0 */ gen_set_Rc0(ctx, cpu_gpr[rt]); @@ -5853,14 +5577,13 @@ GEN_MAC_HANDLER(mullhwu, 0x08, 0x0C); static void gen_mfdcr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv dcrn; - CHK_SV; - dcrn = tcg_const_tl(SPR(ctx->opcode)); - gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, dcrn); - tcg_temp_free(dcrn); + CHK_SV(ctx); + dcrn = tcg_constant_tl(SPR(ctx->opcode)); + gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], tcg_env, dcrn); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5868,14 +5591,13 @@ static void gen_mfdcr(DisasContext *ctx) static void gen_mtdcr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv dcrn; - CHK_SV; - dcrn = tcg_const_tl(SPR(ctx->opcode)); - gen_helper_store_dcr(cpu_env, dcrn, cpu_gpr[rS(ctx->opcode)]); - tcg_temp_free(dcrn); + CHK_SV(ctx); + dcrn = tcg_constant_tl(SPR(ctx->opcode)); + gen_helper_store_dcr(tcg_env, dcrn, cpu_gpr[rS(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5884,10 +5606,10 @@ static void gen_mtdcr(DisasContext *ctx) static void gen_mfdcrx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; - gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, + CHK_SV(ctx); + gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], tcg_env, cpu_gpr[rA(ctx->opcode)]); /* Note: Rc update flag set leads to undefined state of Rc0 */ #endif /* defined(CONFIG_USER_ONLY) */ @@ -5898,35 +5620,19 @@ static void gen_mfdcrx(DisasContext *ctx) static void gen_mtdcrx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; - gen_helper_store_dcr(cpu_env, cpu_gpr[rA(ctx->opcode)], + CHK_SV(ctx); + gen_helper_store_dcr(tcg_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); /* Note: Rc update flag set leads to undefined state of Rc0 */ #endif /* defined(CONFIG_USER_ONLY) */ } -/* mfdcrux (PPC 460) : user-mode access to DCR */ -static void gen_mfdcrux(DisasContext *ctx) -{ - gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, - cpu_gpr[rA(ctx->opcode)]); - /* Note: Rc update flag set leads to undefined state of Rc0 */ -} - -/* mtdcrux (PPC 460) : user-mode access to DCR */ -static void gen_mtdcrux(DisasContext *ctx) -{ - gen_helper_store_dcr(cpu_env, cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rS(ctx->opcode)]); - /* Note: Rc update flag set leads to undefined state of Rc0 */ -} - /* dccci */ static void gen_dccci(DisasContext *ctx) { - CHK_SV; + CHK_SV(ctx); /* interpreted as no-op */ } @@ -5934,19 +5640,17 @@ static void gen_dccci(DisasContext *ctx) static void gen_dcread(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv EA, val; - CHK_SV; + CHK_SV(ctx); gen_set_access_type(ctx, ACCESS_CACHE); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); val = tcg_temp_new(); gen_qemu_ld32u(ctx, val, EA); - tcg_temp_free(val); tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], EA); - tcg_temp_free(EA); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5963,14 +5667,14 @@ static void gen_icbt_40x(DisasContext *ctx) /* iccci */ static void gen_iccci(DisasContext *ctx) { - CHK_SV; + CHK_SV(ctx); /* interpreted as no-op */ } /* icread */ static void gen_icread(DisasContext *ctx) { - CHK_SV; + CHK_SV(ctx); /* interpreted as no-op */ } @@ -5978,11 +5682,11 @@ static void gen_icread(DisasContext *ctx) static void gen_rfci_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); /* Restore CPU state */ - gen_helper_40x_rfci(cpu_env); + gen_helper_40x_rfci(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5990,11 +5694,11 @@ static void gen_rfci_40x(DisasContext *ctx) static void gen_rfci(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); /* Restore CPU state */ - gen_helper_rfci(cpu_env); + gen_helper_rfci(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6005,11 +5709,11 @@ static void gen_rfci(DisasContext *ctx) static void gen_rfdi(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); /* Restore CPU state */ - gen_helper_rfdi(cpu_env); + gen_helper_rfdi(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6018,11 +5722,11 @@ static void gen_rfdi(DisasContext *ctx) static void gen_rfmci(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); /* Restore CPU state */ - gen_helper_rfmci(cpu_env); + gen_helper_rfmci(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6033,16 +5737,16 @@ static void gen_rfmci(DisasContext *ctx) static void gen_tlbre_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); switch (rB(ctx->opcode)) { case 0: - gen_helper_4xx_tlbre_hi(cpu_gpr[rD(ctx->opcode)], cpu_env, + gen_helper_4xx_tlbre_hi(cpu_gpr[rD(ctx->opcode)], tcg_env, cpu_gpr[rA(ctx->opcode)]); break; case 1: - gen_helper_4xx_tlbre_lo(cpu_gpr[rD(ctx->opcode)], cpu_env, + gen_helper_4xx_tlbre_lo(cpu_gpr[rD(ctx->opcode)], tcg_env, cpu_gpr[rA(ctx->opcode)]); break; default: @@ -6056,15 +5760,14 @@ static void gen_tlbre_40x(DisasContext *ctx) static void gen_tlbsx_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_4xx_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); - tcg_temp_free(t0); + gen_helper_4xx_tlbsx(cpu_gpr[rD(ctx->opcode)], tcg_env, t0); if (Rc(ctx->opcode)) { TCGLabel *l1 = gen_new_label(); tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); @@ -6079,17 +5782,17 @@ static void gen_tlbsx_40x(DisasContext *ctx) static void gen_tlbwe_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); switch (rB(ctx->opcode)) { case 0: - gen_helper_4xx_tlbwe_hi(cpu_env, cpu_gpr[rA(ctx->opcode)], + gen_helper_4xx_tlbwe_hi(tcg_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); break; case 1: - gen_helper_4xx_tlbwe_lo(cpu_env, cpu_gpr[rA(ctx->opcode)], + gen_helper_4xx_tlbwe_lo(tcg_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); break; default: @@ -6105,19 +5808,18 @@ static void gen_tlbwe_40x(DisasContext *ctx) static void gen_tlbre_440(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); switch (rB(ctx->opcode)) { case 0: case 1: case 2: { - TCGv_i32 t0 = tcg_const_i32(rB(ctx->opcode)); - gen_helper_440_tlbre(cpu_gpr[rD(ctx->opcode)], cpu_env, + TCGv_i32 t0 = tcg_constant_i32(rB(ctx->opcode)); + gen_helper_440_tlbre(cpu_gpr[rD(ctx->opcode)], tcg_env, t0, cpu_gpr[rA(ctx->opcode)]); - tcg_temp_free_i32(t0); } break; default: @@ -6131,15 +5833,14 @@ static void gen_tlbre_440(DisasContext *ctx) static void gen_tlbsx_440(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_440_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); - tcg_temp_free(t0); + gen_helper_440_tlbsx(cpu_gpr[rD(ctx->opcode)], tcg_env, t0); if (Rc(ctx->opcode)) { TCGLabel *l1 = gen_new_label(); tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); @@ -6154,18 +5855,17 @@ static void gen_tlbsx_440(DisasContext *ctx) static void gen_tlbwe_440(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); switch (rB(ctx->opcode)) { case 0: case 1: case 2: { - TCGv_i32 t0 = tcg_const_i32(rB(ctx->opcode)); - gen_helper_440_tlbwe(cpu_env, t0, cpu_gpr[rA(ctx->opcode)], + TCGv_i32 t0 = tcg_constant_i32(rB(ctx->opcode)); + gen_helper_440_tlbwe(tcg_env, t0, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); - tcg_temp_free_i32(t0); } break; default: @@ -6181,10 +5881,10 @@ static void gen_tlbwe_440(DisasContext *ctx) static void gen_tlbre_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; - gen_helper_booke206_tlbre(cpu_env); + CHK_SV(ctx); + gen_helper_booke206_tlbre(tcg_env); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6192,21 +5892,18 @@ static void gen_tlbre_booke206(DisasContext *ctx) static void gen_tlbsx_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); if (rA(ctx->opcode)) { t0 = tcg_temp_new(); - tcg_gen_mov_tl(t0, cpu_gpr[rD(ctx->opcode)]); + tcg_gen_add_tl(t0, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); } else { - t0 = tcg_const_tl(0); + t0 = cpu_gpr[rB(ctx->opcode)]; } - - tcg_gen_add_tl(t0, t0, cpu_gpr[rB(ctx->opcode)]); - gen_helper_booke206_tlbsx(cpu_env, t0); - tcg_temp_free(t0); + gen_helper_booke206_tlbsx(tcg_env, t0); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6214,73 +5911,69 @@ static void gen_tlbsx_booke206(DisasContext *ctx) static void gen_tlbwe_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; - gen_helper_booke206_tlbwe(cpu_env); + CHK_SV(ctx); + gen_helper_booke206_tlbwe(tcg_env); #endif /* defined(CONFIG_USER_ONLY) */ } static void gen_tlbivax_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_booke206_tlbivax(cpu_env, t0); - tcg_temp_free(t0); + gen_helper_booke206_tlbivax(tcg_env, t0); #endif /* defined(CONFIG_USER_ONLY) */ } static void gen_tlbilx_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); switch ((ctx->opcode >> 21) & 0x3) { case 0: - gen_helper_booke206_tlbilx0(cpu_env, t0); + gen_helper_booke206_tlbilx0(tcg_env, t0); break; case 1: - gen_helper_booke206_tlbilx1(cpu_env, t0); + gen_helper_booke206_tlbilx1(tcg_env, t0); break; case 3: - gen_helper_booke206_tlbilx3(cpu_env, t0); + gen_helper_booke206_tlbilx3(tcg_env, t0); break; default: gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); break; } - - tcg_temp_free(t0); #endif /* defined(CONFIG_USER_ONLY) */ } - /* wrtee */ static void gen_wrtee(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else TCGv t0; - CHK_SV; + CHK_SV(ctx); t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[rD(ctx->opcode)], (1 << MSR_EE)); tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE)); tcg_gen_or_tl(cpu_msr, cpu_msr, t0); - tcg_temp_free(t0); + gen_ppc_maybe_interrupt(ctx); /* * Stop translation to have a chance to raise an exception if we * just set msr_ee to 1 @@ -6293,11 +5986,12 @@ static void gen_wrtee(DisasContext *ctx) static void gen_wrteei(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - GEN_PRIV; + GEN_PRIV(ctx); #else - CHK_SV; + CHK_SV(ctx); if (ctx->opcode & 0x00008000) { tcg_gen_ori_tl(cpu_msr, cpu_msr, (1 << MSR_EE)); + gen_ppc_maybe_interrupt(ctx); /* Stop translation to have a chance to raise an exception */ ctx->base.is_jmp = DISAS_EXIT_UPDATE; } else { @@ -6311,10 +6005,9 @@ static void gen_wrteei(DisasContext *ctx) /* dlmzb */ static void gen_dlmzb(DisasContext *ctx) { - TCGv_i32 t0 = tcg_const_i32(Rc(ctx->opcode)); - gen_helper_dlmzb(cpu_gpr[rA(ctx->opcode)], cpu_env, + TCGv_i32 t0 = tcg_constant_i32(Rc(ctx->opcode)); + gen_helper_dlmzb(cpu_gpr[rA(ctx->opcode)], tcg_env, cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); - tcg_temp_free_i32(t0); } /* mbar replaces eieio on 440 */ @@ -6344,68 +6037,6 @@ static void gen_icbt_440(DisasContext *ctx) */ } -/* Embedded.Processor Control */ - -static void gen_msgclr(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_HV; - if (is_book3s_arch2x(ctx)) { - gen_helper_book3s_msgclr(cpu_env, cpu_gpr[rB(ctx->opcode)]); - } else { - gen_helper_msgclr(cpu_env, cpu_gpr[rB(ctx->opcode)]); - } -#endif /* defined(CONFIG_USER_ONLY) */ -} - -static void gen_msgsnd(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_HV; - if (is_book3s_arch2x(ctx)) { - gen_helper_book3s_msgsnd(cpu_gpr[rB(ctx->opcode)]); - } else { - gen_helper_msgsnd(cpu_gpr[rB(ctx->opcode)]); - } -#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) - GEN_PRIV; -#else - CHK_HV; -#endif /* defined(CONFIG_USER_ONLY) */ - /* interpreted as no-op */ -} - #if defined(TARGET_PPC64) static void gen_maddld(DisasContext *ctx) { @@ -6413,7 +6044,6 @@ static void gen_maddld(DisasContext *ctx) tcg_gen_mul_i64(t1, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); tcg_gen_add_i64(cpu_gpr[rD(ctx->opcode)], t1, cpu_gpr[rC(ctx->opcode)]); - tcg_temp_free_i64(t1); } /* maddhd maddhdu */ @@ -6434,9 +6064,6 @@ static void gen_maddhd_maddhdu(DisasContext *ctx) } tcg_gen_add2_i64(t1, cpu_gpr[rD(ctx->opcode)], lo, hi, cpu_gpr[rC(ctx->opcode)], t1); - tcg_temp_free_i64(lo); - tcg_temp_free_i64(hi); - tcg_temp_free_i64(t1); } #endif /* defined(TARGET_PPC64) */ @@ -6446,7 +6073,7 @@ static void gen_tbegin(DisasContext *ctx) gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); return; } - gen_helper_tbegin(cpu_env); + gen_helper_tbegin(tcg_env); } #define GEN_TM_NOOP(name) \ @@ -6512,7 +6139,7 @@ static void gen_tcheck(DisasContext *ctx) #define GEN_TM_PRIV_NOOP(name) \ static inline void gen_##name(DisasContext *ctx) \ { \ - gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); \ + gen_priv_opc(ctx); \ } #else @@ -6520,7 +6147,7 @@ static inline void gen_##name(DisasContext *ctx) \ #define GEN_TM_PRIV_NOOP(name) \ static inline void gen_##name(DisasContext *ctx) \ { \ - CHK_SV; \ + CHK_SV(ctx); \ if (unlikely(!ctx->tm_enabled)) { \ gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); \ return; \ @@ -6542,22 +6169,30 @@ GEN_TM_PRIV_NOOP(trechkpt); static inline void get_fpr(TCGv_i64 dst, int regno) { - tcg_gen_ld_i64(dst, cpu_env, fpr_offset(regno)); + tcg_gen_ld_i64(dst, tcg_env, fpr_offset(regno)); } static inline void set_fpr(int regno, TCGv_i64 src) { - tcg_gen_st_i64(src, cpu_env, fpr_offset(regno)); + tcg_gen_st_i64(src, tcg_env, fpr_offset(regno)); + /* + * Before PowerISA v3.1 the result of doubleword 1 of the VSR + * corresponding to the target FPR was undefined. However, + * most (if not all) real hardware were setting the result to 0. + * Starting at ISA v3.1, the result for doubleword 1 is now defined + * to be 0. + */ + tcg_gen_st_i64(tcg_constant_i64(0), tcg_env, vsr64_offset(regno, false)); } static inline void get_avr64(TCGv_i64 dst, int regno, bool high) { - tcg_gen_ld_i64(dst, cpu_env, avr64_offset(regno, high)); + tcg_gen_ld_i64(dst, tcg_env, avr64_offset(regno, high)); } static inline void set_avr64(int regno, TCGv_i64 src, bool high) { - tcg_gen_st_i64(src, cpu_env, avr64_offset(regno, high)); + tcg_gen_st_i64(src, tcg_env, avr64_offset(regno, high)); } /* @@ -6578,6 +6213,11 @@ static int times_16(DisasContext *ctx, int x) return x * 16; } +static int64_t dw_compose_ea(DisasContext *ctx, int x) +{ + return deposit64(0xfffffffffffffe00, 3, 6, x); +} + /* * Helpers for trans_* functions to check for specific insns flags. * Use token pasting to ensure that we use the proper flag with the @@ -6628,6 +6268,27 @@ static int times_16(DisasContext *ctx, int x) } \ } while (0) +#if !defined(CONFIG_USER_ONLY) +#define REQUIRE_SV(CTX) \ + do { \ + if (unlikely((CTX)->pr)) { \ + gen_priv_opc(CTX); \ + return true; \ + } \ + } while (0) + +#define REQUIRE_HV(CTX) \ + do { \ + if (unlikely((CTX)->pr || !(CTX)->hv)) { \ + gen_priv_opc(CTX); \ + return true; \ + } \ + } while (0) +#else +#define REQUIRE_SV(CTX) do { gen_priv_opc(CTX); return true; } while (0) +#define REQUIRE_HV(CTX) do { gen_priv_opc(CTX); return true; } while (0) +#endif + /* * Helpers for implementing sets of trans_* functions. * Defer the implementation of NAME to FUNC, with optional extra arguments. @@ -6699,6 +6360,10 @@ static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a) #include "translate/branch-impl.c.inc" +#include "translate/processor-ctrl-impl.c.inc" + +#include "translate/storage-ctrl-impl.c.inc" + /* Handles lfdp */ static void gen_dform39(DisasContext *ctx) { @@ -6749,9 +6414,6 @@ static void gen_brh(DisasContext *ctx) tcg_gen_and_i64(t1, cpu_gpr[rS(ctx->opcode)], mask); tcg_gen_shli_i64(t1, t1, 8); tcg_gen_or_i64(cpu_gpr[rA(ctx->opcode)], t1, t2); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); } #endif @@ -6768,8 +6430,6 @@ GEN_HANDLER_E(cmpeqb, 0x1F, 0x00, 0x07, 0x00600000, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(cmpb, 0x1F, 0x1C, 0x0F, 0x00000001, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(cmprb, 0x1F, 0x00, 0x06, 0x00400001, PPC_NONE, PPC2_ISA300), GEN_HANDLER(isel, 0x1F, 0x0F, 0xFF, 0x00000001, PPC_ISEL), -GEN_HANDLER(addic, 0x0C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER2(addic_, "addic.", 0x0D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(mulhw, 0x1F, 0x0B, 0x02, 0x00000400, PPC_INTEGER), GEN_HANDLER(mulhwu, 0x1F, 0x0B, 0x00, 0x00000400, PPC_INTEGER), GEN_HANDLER(mullw, 0x1F, 0x0B, 0x07, 0x00000000, PPC_INTEGER), @@ -6780,7 +6440,6 @@ GEN_HANDLER(mulld, 0x1F, 0x09, 0x07, 0x00000000, PPC_64B), #endif GEN_HANDLER(neg, 0x1F, 0x08, 0x03, 0x0000F800, PPC_INTEGER), GEN_HANDLER(nego, 0x1F, 0x08, 0x13, 0x0000F800, PPC_INTEGER), -GEN_HANDLER(subfic, 0x08, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER2(andi_, "andi.", 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER2(andis_, "andis.", 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(cntlzw, 0x1F, 0x1A, 0x00, 0x00000000, PPC_INTEGER), @@ -6852,8 +6511,9 @@ GEN_HANDLER2(stdcx_, "stdcx.", 0x1F, 0x16, 0x06, 0x00000000, PPC_64B), GEN_HANDLER_E(stqcx_, 0x1F, 0x16, 0x05, 0, PPC_NONE, PPC2_LSQ_ISA207), #endif GEN_HANDLER(sync, 0x1F, 0x16, 0x12, 0x039FF801, PPC_MEM_SYNC), -GEN_HANDLER(wait, 0x1F, 0x1E, 0x01, 0x03FFF801, PPC_WAIT), -GEN_HANDLER_E(wait, 0x1F, 0x1E, 0x00, 0x039FF801, PPC_NONE, PPC2_ISA300), +/* ISA v3.0 changed the extended opcode from 62 to 30 */ +GEN_HANDLER(wait, 0x1F, 0x1E, 0x01, 0x039FF801, PPC_WAIT), +GEN_HANDLER_E(wait, 0x1F, 0x1E, 0x00, 0x039CF801, PPC_NONE, PPC2_ISA300), GEN_HANDLER(b, 0x12, 0xFF, 0xFF, 0x00000000, PPC_FLOW), GEN_HANDLER(bc, 0x10, 0xFF, 0xFF, 0x00000000, PPC_FLOW), GEN_HANDLER(bcctr, 0x13, 0x10, 0x10, 0x00000000, PPC_FLOW), @@ -6908,6 +6568,7 @@ GEN_HANDLER_E(dcbtep, 0x1F, 0x1F, 0x09, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x00000001, PPC_CACHE), GEN_HANDLER_E(dcbtstep, 0x1F, 0x1F, 0x07, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER_E(dcbtls, 0x1F, 0x06, 0x05, 0x02000001, PPC_BOOKE, PPC2_BOOKE206), +GEN_HANDLER_E(dcblc, 0x1F, 0x06, 0x0c, 0x02000001, PPC_BOOKE, PPC2_BOOKE206), GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03C00001, PPC_CACHE_DCBZ), GEN_HANDLER_E(dcbzep, 0x1F, 0x1F, 0x1F, 0x03C00001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER(dst, 0x1F, 0x16, 0x0A, 0x01800001, PPC_ALTIVEC), @@ -6927,27 +6588,13 @@ GEN_HANDLER2(mfsrin_64b, "mfsrin", 0x1F, 0x13, 0x14, 0x001F0001, GEN_HANDLER2(mtsr_64b, "mtsr", 0x1F, 0x12, 0x06, 0x0010F801, PPC_SEGMENT_64B), GEN_HANDLER2(mtsrin_64b, "mtsrin", 0x1F, 0x12, 0x07, 0x001F0001, PPC_SEGMENT_64B), -GEN_HANDLER2(slbmte, "slbmte", 0x1F, 0x12, 0x0C, 0x001F0001, PPC_SEGMENT_64B), -GEN_HANDLER2(slbmfee, "slbmfee", 0x1F, 0x13, 0x1C, 0x001F0001, PPC_SEGMENT_64B), -GEN_HANDLER2(slbmfev, "slbmfev", 0x1F, 0x13, 0x1A, 0x001F0001, PPC_SEGMENT_64B), -GEN_HANDLER2(slbfee_, "slbfee.", 0x1F, 0x13, 0x1E, 0x001F0000, PPC_SEGMENT_64B), #endif GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_TLBIA), /* * XXX Those instructions will need to be handled differently for * different ISA versions */ -GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x001F0001, PPC_MEM_TLBIE), -GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x001F0001, PPC_MEM_TLBIE), -GEN_HANDLER_E(tlbiel, 0x1F, 0x12, 0x08, 0x00100001, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(tlbie, 0x1F, 0x12, 0x09, 0x00100001, PPC_NONE, PPC2_ISA300), GEN_HANDLER(tlbsync, 0x1F, 0x16, 0x11, 0x03FFF801, PPC_MEM_TLBSYNC), -#if defined(TARGET_PPC64) -GEN_HANDLER(slbia, 0x1F, 0x12, 0x0F, 0x031FFC01, PPC_SLBI), -GEN_HANDLER(slbie, 0x1F, 0x12, 0x0D, 0x03FF0001, PPC_SLBI), -GEN_HANDLER_E(slbieg, 0x1F, 0x12, 0x0E, 0x001F0001, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(slbsync, 0x1F, 0x12, 0x0A, 0x03FFF801, PPC_NONE, PPC2_ISA300), -#endif GEN_HANDLER(eciwx, 0x1F, 0x16, 0x0D, 0x00000001, PPC_EXTERN), GEN_HANDLER(ecowx, 0x1F, 0x16, 0x09, 0x00000001, PPC_EXTERN), GEN_HANDLER2(tlbld_6xx, "tlbld", 0x1F, 0x12, 0x1E, 0x03FF0001, PPC_6xx_TLB), @@ -6958,8 +6605,6 @@ GEN_HANDLER(mfdcr, 0x1F, 0x03, 0x0A, 0x00000001, PPC_DCR), GEN_HANDLER(mtdcr, 0x1F, 0x03, 0x0E, 0x00000001, PPC_DCR), GEN_HANDLER(mfdcrx, 0x1F, 0x03, 0x08, 0x00000000, PPC_DCRX), GEN_HANDLER(mtdcrx, 0x1F, 0x03, 0x0C, 0x00000000, PPC_DCRX), -GEN_HANDLER(mfdcrux, 0x1F, 0x03, 0x09, 0x00000000, PPC_DCRUX), -GEN_HANDLER(mtdcrux, 0x1F, 0x03, 0x0D, 0x00000000, PPC_DCRUX), GEN_HANDLER(dccci, 0x1F, 0x06, 0x0E, 0x03E00001, PPC_4xx_COMMON), GEN_HANDLER(dcread, 0x1F, 0x06, 0x0F, 0x00000001, PPC_4xx_COMMON), GEN_HANDLER2(icbt_40x, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, PPC_40x_ICBT), @@ -6985,12 +6630,6 @@ GEN_HANDLER2_E(tlbivax_booke206, "tlbivax", 0x1F, 0x12, 0x18, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER2_E(tlbilx_booke206, "tlbilx", 0x1F, 0x12, 0x00, 0x03800001, PPC_NONE, PPC2_BOOKE206), -GEN_HANDLER2_E(msgsnd, "msgsnd", 0x1F, 0x0E, 0x06, 0x03ff0001, - PPC_NONE, PPC2_PRCNTL), -GEN_HANDLER2_E(msgclr, "msgclr", 0x1F, 0x0E, 0x07, 0x03ff0001, - PPC_NONE, PPC2_PRCNTL), -GEN_HANDLER2_E(msgsync, "msgsync", 0x1F, 0x16, 0x1B, 0x00000000, - PPC_NONE, PPC2_PRCNTL), GEN_HANDLER(wrtee, 0x1F, 0x03, 0x04, 0x000FFC01, PPC_WRTEE), GEN_HANDLER(wrteei, 0x1F, 0x03, 0x05, 0x000E7C01, PPC_WRTEE), GEN_HANDLER(dlmzb, 0x1F, 0x0E, 0x02, 0x00000000, PPC_440_SPEC), @@ -7005,36 +6644,12 @@ GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC), -GEN_HANDLER(vmladduhm, 0x04, 0x11, 0xFF, 0x00000000, PPC_ALTIVEC), #if defined(TARGET_PPC64) 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 -#undef GEN_INT_ARITH_ADD_CONST -#define GEN_INT_ARITH_ADD(name, opc3, add_ca, compute_ca, compute_ov) \ -GEN_HANDLER(name, 0x1F, 0x0A, opc3, 0x00000000, PPC_INTEGER), -#define GEN_INT_ARITH_ADD_CONST(name, opc3, const_val, \ - add_ca, compute_ca, compute_ov) \ -GEN_HANDLER(name, 0x1F, 0x0A, opc3, 0x0000F800, PPC_INTEGER), -GEN_INT_ARITH_ADD(add, 0x08, 0, 0, 0) -GEN_INT_ARITH_ADD(addo, 0x18, 0, 0, 1) -GEN_INT_ARITH_ADD(addc, 0x00, 0, 1, 0) -GEN_INT_ARITH_ADD(addco, 0x10, 0, 1, 1) -GEN_INT_ARITH_ADD(adde, 0x04, 1, 1, 0) -GEN_INT_ARITH_ADD(addeo, 0x14, 1, 1, 1) -GEN_INT_ARITH_ADD_CONST(addme, 0x07, -1LL, 1, 1, 0) -GEN_INT_ARITH_ADD_CONST(addmeo, 0x17, -1LL, 1, 1, 1) -GEN_HANDLER_E(addex, 0x1F, 0x0A, 0x05, 0x00000000, PPC_NONE, PPC2_ISA300), -GEN_INT_ARITH_ADD_CONST(addze, 0x06, 0, 1, 1, 0) -GEN_INT_ARITH_ADD_CONST(addzeo, 0x16, 0, 1, 1, 1) - #undef GEN_INT_ARITH_DIVW #define GEN_INT_ARITH_DIVW(name, opc3, sign, compute_ov) \ GEN_HANDLER(name, 0x1F, 0x0B, opc3, 0x00000000, PPC_INTEGER) @@ -7073,24 +6688,6 @@ GEN_INT_ARITH_MUL_HELPER(mulhd, 0x02), GEN_INT_ARITH_MUL_HELPER(mulldo, 0x17), #endif -#undef GEN_INT_ARITH_SUBF -#undef GEN_INT_ARITH_SUBF_CONST -#define GEN_INT_ARITH_SUBF(name, opc3, add_ca, compute_ca, compute_ov) \ -GEN_HANDLER(name, 0x1F, 0x08, opc3, 0x00000000, PPC_INTEGER), -#define GEN_INT_ARITH_SUBF_CONST(name, opc3, const_val, \ - add_ca, compute_ca, compute_ov) \ -GEN_HANDLER(name, 0x1F, 0x08, opc3, 0x0000F800, PPC_INTEGER), -GEN_INT_ARITH_SUBF(subf, 0x01, 0, 0, 0) -GEN_INT_ARITH_SUBF(subfo, 0x11, 0, 0, 1) -GEN_INT_ARITH_SUBF(subfc, 0x00, 0, 1, 0) -GEN_INT_ARITH_SUBF(subfco, 0x10, 0, 1, 1) -GEN_INT_ARITH_SUBF(subfe, 0x04, 1, 1, 0) -GEN_INT_ARITH_SUBF(subfeo, 0x14, 1, 1, 1) -GEN_INT_ARITH_SUBF_CONST(subfme, 0x07, -1LL, 1, 1, 0) -GEN_INT_ARITH_SUBF_CONST(subfmeo, 0x17, -1LL, 1, 1, 1) -GEN_INT_ARITH_SUBF_CONST(subfze, 0x06, 0, 1, 1, 0) -GEN_INT_ARITH_SUBF_CONST(subfzeo, 0x16, 0, 1, 1, 1) - #undef GEN_LOGICAL1 #undef GEN_LOGICAL2 #define GEN_LOGICAL2(name, tcg_op, opc, type) \ @@ -7465,7 +7062,7 @@ static int test_opcode_table(opc_handler_t **table, int len) tmp = test_opcode_table(ind_table(table[i]), PPC_CPU_INDIRECT_OPCODES_LEN); if (tmp == 0) { - free(table[i]); + g_free(table[i]); table[i] = &invalid_handler; } else { count++; @@ -7627,7 +7224,7 @@ static bool decode_legacy(PowerPCCPU *cpu, DisasContext *ctx, uint32_t insn) static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - CPUPPCState *env = cs->env_ptr; + CPUPPCState *env = cpu_env(cs); uint32_t hflags = ctx->base.tb->flags; ctx->spr_cb = env->spr_cb; @@ -7658,6 +7255,8 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->hr = (hflags >> HFLAGS_HR) & 1; ctx->mmcr0_pmcc0 = (hflags >> HFLAGS_PMCC0) & 1; ctx->mmcr0_pmcc1 = (hflags >> HFLAGS_PMCC1) & 1; + ctx->mmcr0_pmcjce = (hflags >> HFLAGS_PMCJCE) & 1; + ctx->pmc_other = (hflags >> HFLAGS_PMC_OTHER) & 1; ctx->pmu_insn_cnt = (hflags >> HFLAGS_INSN_CNT) & 1; ctx->singlestep_enabled = 0; @@ -7689,7 +7288,7 @@ static void ppc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = cs->env_ptr; + CPUPPCState *env = cpu_env(cs); target_ulong pc; uint32_t insn; bool ok; @@ -7727,8 +7326,6 @@ static void ppc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) if (ctx->base.is_jmp == DISAS_NEXT && !(pc & ~TARGET_PAGE_MASK)) { ctx->base.is_jmp = DISAS_TOO_MANY; } - - translator_loop_temp_check(&ctx->base); } static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) @@ -7743,8 +7340,9 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } /* Honor single stepping. */ - if (unlikely(ctx->singlestep_enabled & CPU_SINGLE_STEP) - && (nip <= 0x100 || nip > 0xf00)) { + if (unlikely(ctx->singlestep_enabled & CPU_SINGLE_STEP)) { + bool rfi_type = false; + switch (is_jmp) { case DISAS_TOO_MANY: case DISAS_EXIT_UPDATE: @@ -7753,12 +7351,19 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) break; case DISAS_EXIT: case DISAS_CHAIN: + /* + * This is a heuristic, to put it kindly. The rfi class of + * instructions are among the few outside branches that change + * NIP without taking an interrupt. Single step trace interrupts + * do not fire on completion of these instructions. + */ + rfi_type = true; break; default: g_assert_not_reached(); } - gen_debug_exception(ctx); + gen_debug_exception(ctx, rfi_type); return; } @@ -7816,15 +7421,10 @@ static const TranslatorOps ppc_tr_ops = { .disas_log = ppc_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; - translator_loop(&ppc_tr_ops, &ctx.base, cs, tb, max_insns); -} - -void restore_state_to_opc(CPUPPCState *env, TranslationBlock *tb, - target_ulong *data) -{ - env->nip = data[0]; + translator_loop(cs, tb, max_insns, pc, host_pc, &ppc_tr_ops, &ctx.base); } diff --git a/target/ppc/translate/branch-impl.c.inc b/target/ppc/translate/branch-impl.c.inc index 29cfa11854..fb0fcf30cc 100644 --- a/target/ppc/translate/branch-impl.c.inc +++ b/target/ppc/translate/branch-impl.c.inc @@ -16,9 +16,9 @@ static bool trans_RFEBB(DisasContext *ctx, arg_XL_s *arg) { REQUIRE_INSNS_FLAGS2(ctx, ISA207S); - gen_icount_io_start(ctx); + translator_io_start(&ctx->base); gen_update_cfar(ctx, ctx->cia); - gen_helper_rfebb(cpu_env, cpu_gpr[arg->s]); + gen_helper_rfebb(tcg_env, cpu_gpr[arg->s]); ctx->base.is_jmp = DISAS_CHAIN; diff --git a/target/ppc/translate/dfp-impl.c.inc b/target/ppc/translate/dfp-impl.c.inc index f9f1d58d44..371076582b 100644 --- a/target/ppc/translate/dfp-impl.c.inc +++ b/target/ppc/translate/dfp-impl.c.inc @@ -3,7 +3,7 @@ static inline TCGv_ptr gen_fprp_ptr(int reg) { TCGv_ptr r = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(r, cpu_env, offsetof(CPUPPCState, vsr[reg].u64[0])); + tcg_gen_addi_ptr(r, tcg_env, offsetof(CPUPPCState, vsr[reg].u64[0])); return r; } @@ -16,13 +16,10 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ rt = gen_fprp_ptr(a->rt); \ ra = gen_fprp_ptr(a->ra); \ rb = gen_fprp_ptr(a->rb); \ - gen_helper_##NAME(cpu_env, rt, ra, rb); \ + gen_helper_##NAME(tcg_env, rt, ra, rb); \ if (unlikely(a->rc)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ - tcg_temp_free_ptr(rt); \ - tcg_temp_free_ptr(ra); \ - tcg_temp_free_ptr(rb); \ return true; \ } @@ -35,9 +32,7 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ ra = gen_fprp_ptr(a->ra); \ rb = gen_fprp_ptr(a->rb); \ gen_helper_##NAME(cpu_crf[a->bf], \ - cpu_env, ra, rb); \ - tcg_temp_free_ptr(ra); \ - tcg_temp_free_ptr(rb); \ + tcg_env, ra, rb); \ return true; \ } @@ -49,8 +44,7 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ REQUIRE_FPU(ctx); \ rb = gen_fprp_ptr(a->rb); \ gen_helper_##NAME(cpu_crf[a->bf], \ - cpu_env, tcg_constant_i32(a->uim), rb);\ - tcg_temp_free_ptr(rb); \ + tcg_env, tcg_constant_i32(a->uim), rb);\ return true; \ } @@ -62,8 +56,7 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ REQUIRE_FPU(ctx); \ ra = gen_fprp_ptr(a->fra); \ gen_helper_##NAME(cpu_crf[a->bf], \ - cpu_env, ra, tcg_constant_i32(a->dm)); \ - tcg_temp_free_ptr(ra); \ + tcg_env, ra, tcg_constant_i32(a->dm)); \ return true; \ } @@ -75,14 +68,12 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ REQUIRE_FPU(ctx); \ rt = gen_fprp_ptr(a->frt); \ rb = gen_fprp_ptr(a->frb); \ - gen_helper_##NAME(cpu_env, rt, rb, \ + gen_helper_##NAME(tcg_env, rt, rb, \ tcg_constant_i32(a->U32F1), \ tcg_constant_i32(a->U32F2)); \ if (unlikely(a->rc)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ - tcg_temp_free_ptr(rt); \ - tcg_temp_free_ptr(rb); \ return true; \ } @@ -95,14 +86,11 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ rt = gen_fprp_ptr(a->frt); \ ra = gen_fprp_ptr(a->fra); \ rb = gen_fprp_ptr(a->frb); \ - gen_helper_##NAME(cpu_env, rt, ra, rb, \ + gen_helper_##NAME(tcg_env, rt, ra, rb, \ tcg_constant_i32(a->I32FLD)); \ if (unlikely(a->rc)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ - tcg_temp_free_ptr(rt); \ - tcg_temp_free_ptr(ra); \ - tcg_temp_free_ptr(rb); \ return true; \ } @@ -114,12 +102,10 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ REQUIRE_FPU(ctx); \ rt = gen_fprp_ptr(a->rt); \ rb = gen_fprp_ptr(a->rb); \ - gen_helper_##NAME(cpu_env, rt, rb); \ + gen_helper_##NAME(tcg_env, rt, rb); \ if (unlikely(a->rc)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ - tcg_temp_free_ptr(rt); \ - tcg_temp_free_ptr(rb); \ return true; \ } @@ -131,13 +117,11 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ REQUIRE_FPU(ctx); \ rt = gen_fprp_ptr(a->rt); \ rx = gen_fprp_ptr(a->FPRFLD); \ - gen_helper_##NAME(cpu_env, rt, rx, \ + gen_helper_##NAME(tcg_env, rt, rx, \ tcg_constant_i32(a->I32FLD)); \ if (unlikely(a->rc)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ - tcg_temp_free_ptr(rt); \ - tcg_temp_free_ptr(rx); \ return true; \ } @@ -204,9 +188,7 @@ static bool trans_DCFFIXQQ(DisasContext *ctx, arg_DCFFIXQQ *a) rt = gen_fprp_ptr(a->frtp); rb = gen_avr_ptr(a->vrb); - gen_helper_DCFFIXQQ(cpu_env, rt, rb); - tcg_temp_free_ptr(rt); - tcg_temp_free_ptr(rb); + gen_helper_DCFFIXQQ(tcg_env, rt, rb); return true; } @@ -221,9 +203,7 @@ static bool trans_DCTFIXQQ(DisasContext *ctx, arg_DCTFIXQQ *a) rt = gen_avr_ptr(a->vrt); rb = gen_fprp_ptr(a->frbp); - gen_helper_DCTFIXQQ(cpu_env, rt, rb); - tcg_temp_free_ptr(rt); - tcg_temp_free_ptr(rb); + gen_helper_DCTFIXQQ(tcg_env, rt, rb); return true; } diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 1aab32be03..0c66465d96 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -42,8 +42,6 @@ static bool do_ldst(DisasContext *ctx, int rt, int ra, TCGv displ, bool update, if (update) { tcg_gen_mov_tl(cpu_gpr[ra], ea); } - tcg_temp_free(ea); - return true; } @@ -73,17 +71,14 @@ static bool do_ldst_quad(DisasContext *ctx, arg_D *a, bool store, bool prefixed) { #if defined(TARGET_PPC64) TCGv ea; - TCGv_i64 low_addr_gpr, high_addr_gpr; - MemOp mop; + TCGv_i64 lo, hi; + TCGv_i128 t16; REQUIRE_INSNS_FLAGS(ctx, 64BX); if (!prefixed && !(ctx->insns_flags2 & PPC2_LSQ_ISA207)) { - if (ctx->pr) { - /* lq and stq were privileged prior to V. 2.07 */ - gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return true; - } + /* lq and stq were privileged prior to V. 2.07 */ + REQUIRE_SV(ctx); if (ctx->le_mode) { gen_align_no_le(ctx); @@ -99,60 +94,22 @@ static bool do_ldst_quad(DisasContext *ctx, arg_D *a, bool store, bool prefixed) gen_set_access_type(ctx, ACCESS_INT); ea = do_ea_calc(ctx, a->ra, tcg_constant_tl(a->si)); - if (prefixed || !ctx->le_mode) { - low_addr_gpr = cpu_gpr[a->rt]; - high_addr_gpr = cpu_gpr[a->rt + 1]; + if (ctx->le_mode && prefixed) { + lo = cpu_gpr[a->rt]; + hi = cpu_gpr[a->rt + 1]; } else { - low_addr_gpr = cpu_gpr[a->rt + 1]; - high_addr_gpr = cpu_gpr[a->rt]; + lo = cpu_gpr[a->rt + 1]; + hi = cpu_gpr[a->rt]; } + t16 = tcg_temp_new_i128(); - if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { - if (HAVE_ATOMIC128) { - mop = DEF_MEMOP(MO_128); - TCGv_i32 oi = tcg_constant_i32(make_memop_idx(mop, ctx->mem_idx)); - if (store) { - if (ctx->le_mode) { - gen_helper_stq_le_parallel(cpu_env, ea, low_addr_gpr, - high_addr_gpr, oi); - } else { - gen_helper_stq_be_parallel(cpu_env, ea, high_addr_gpr, - low_addr_gpr, oi); - - } - } else { - if (ctx->le_mode) { - gen_helper_lq_le_parallel(low_addr_gpr, cpu_env, ea, oi); - tcg_gen_ld_i64(high_addr_gpr, cpu_env, - offsetof(CPUPPCState, retxh)); - } else { - gen_helper_lq_be_parallel(high_addr_gpr, cpu_env, ea, oi); - tcg_gen_ld_i64(low_addr_gpr, cpu_env, - offsetof(CPUPPCState, retxh)); - } - } - } else { - /* Restart with exclusive lock. */ - gen_helper_exit_atomic(cpu_env); - ctx->base.is_jmp = DISAS_NORETURN; - } + if (store) { + tcg_gen_concat_i64_i128(t16, lo, hi); + tcg_gen_qemu_st_i128(t16, ea, ctx->mem_idx, DEF_MEMOP(MO_128)); } else { - mop = DEF_MEMOP(MO_UQ); - if (store) { - tcg_gen_qemu_st_i64(low_addr_gpr, ea, ctx->mem_idx, mop); - } else { - tcg_gen_qemu_ld_i64(low_addr_gpr, ea, ctx->mem_idx, mop); - } - - gen_addr_add(ctx, ea, ea, 8); - - if (store) { - tcg_gen_qemu_st_i64(high_addr_gpr, ea, ctx->mem_idx, mop); - } else { - tcg_gen_qemu_ld_i64(high_addr_gpr, ea, ctx->mem_idx, mop); - } + tcg_gen_qemu_ld_i128(t16, ea, ctx->mem_idx, DEF_MEMOP(MO_128)); + tcg_gen_extr_i128_i64(lo, hi, t16); } - tcg_temp_free(ea); #else qemu_build_not_reached(); #endif @@ -368,6 +325,76 @@ static bool trans_ADDPCIS(DisasContext *ctx, arg_DX *a) return true; } +static bool trans_ADDEX(DisasContext *ctx, arg_X *a) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + gen_op_arith_add(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + cpu_ov, cpu_ov32, true, true, false, false); + return true; +} + +static bool do_add_D(DisasContext *ctx, arg_D *a, bool add_ca, bool compute_ca, + bool compute_ov, bool compute_rc0) +{ + gen_op_arith_add(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], + tcg_constant_tl(a->si), cpu_ca, cpu_ca32, + add_ca, compute_ca, compute_ov, compute_rc0); + return true; +} + +static bool do_add_XO(DisasContext *ctx, arg_XO *a, bool add_ca, + bool compute_ca) +{ + gen_op_arith_add(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + cpu_ca, cpu_ca32, add_ca, compute_ca, a->oe, a->rc); + return true; +} + +static bool do_add_const_XO(DisasContext *ctx, arg_XO_ta *a, TCGv const_val, + bool add_ca, bool compute_ca) +{ + gen_op_arith_add(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], const_val, + cpu_ca, cpu_ca32, add_ca, compute_ca, a->oe, a->rc); + return true; +} + +TRANS(ADD, do_add_XO, false, false); +TRANS(ADDC, do_add_XO, false, true); +TRANS(ADDE, do_add_XO, true, true); +TRANS(ADDME, do_add_const_XO, tcg_constant_tl(-1LL), true, true); +TRANS(ADDZE, do_add_const_XO, tcg_constant_tl(0), true, true); +TRANS(ADDIC, do_add_D, false, true, false, false); +TRANS(ADDIC_, do_add_D, false, true, false, true); + +static bool trans_SUBFIC(DisasContext *ctx, arg_D *a) +{ + gen_op_arith_subf(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], + tcg_constant_tl(a->si), false, true, false, false); + return true; +} + +static bool do_subf_XO(DisasContext *ctx, arg_XO *a, bool add_ca, + bool compute_ca) +{ + gen_op_arith_subf(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + add_ca, compute_ca, a->oe, a->rc); + return true; +} + +static bool do_subf_const_XO(DisasContext *ctx, arg_XO_ta *a, TCGv const_val, + bool add_ca, bool compute_ca) +{ + gen_op_arith_subf(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], const_val, + add_ca, compute_ca, a->oe, a->rc); + return true; +} + +TRANS(SUBF, do_subf_XO, false, false) +TRANS(SUBFC, do_subf_XO, false, true) +TRANS(SUBFE, do_subf_XO, true, true) +TRANS(SUBFME, do_subf_const_XO, tcg_constant_tl(-1LL), true, true) +TRANS(SUBFZE, do_subf_const_XO, tcg_constant_tl(0), true, true) + static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a) { gen_invalid(ctx); @@ -385,15 +412,15 @@ static bool do_set_bool_cond(DisasContext *ctx, arg_X_bi *a, bool neg, bool rev) uint32_t mask = 0x08 >> (a->bi & 0x03); TCGCond cond = rev ? TCG_COND_EQ : TCG_COND_NE; TCGv temp = tcg_temp_new(); + TCGv zero = tcg_constant_tl(0); tcg_gen_extu_i32_tl(temp, cpu_crf[a->bi >> 2]); tcg_gen_andi_tl(temp, temp, mask); - tcg_gen_setcondi_tl(cond, cpu_gpr[a->rt], temp, 0); if (neg) { - tcg_gen_neg_tl(cpu_gpr[a->rt], cpu_gpr[a->rt]); + tcg_gen_negsetcond_tl(cond, cpu_gpr[a->rt], temp, zero); + } else { + tcg_gen_setcond_tl(cond, cpu_gpr[a->rt], temp, zero); } - tcg_temp_free(temp); - return true; } @@ -440,9 +467,6 @@ static void do_cntzdm(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 mask, int64_t trail) } tcg_gen_ctpop_i64(dst, t0); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } static bool trans_CNTLZDM(DisasContext *ctx, arg_X *a) @@ -492,3 +516,82 @@ static bool trans_PEXTD(DisasContext *ctx, arg_X *a) #endif return true; } + +static bool trans_ADDG6S(DisasContext *ctx, arg_X *a) +{ + const target_ulong carry_bits = (target_ulong)-1 / 0xf; + TCGv in1, in2, carryl, carryh, tmp; + TCGv zero = tcg_constant_tl(0); + + REQUIRE_INSNS_FLAGS2(ctx, BCDA_ISA206); + + in1 = cpu_gpr[a->ra]; + in2 = cpu_gpr[a->rb]; + tmp = tcg_temp_new(); + carryl = tcg_temp_new(); + carryh = tcg_temp_new(); + + /* Addition with carry. */ + tcg_gen_add2_tl(carryl, carryh, in1, zero, in2, zero); + /* Addition without carry. */ + tcg_gen_xor_tl(tmp, in1, in2); + /* Difference between the two is carry in to each bit. */ + tcg_gen_xor_tl(carryl, carryl, tmp); + + /* + * The carry-out that we're looking for is the carry-in to + * the next nibble. Shift the double-word down one nibble, + * which puts all of the bits back into one word. + */ + tcg_gen_extract2_tl(carryl, carryl, carryh, 4); + + /* Invert, isolate the carry bits, and produce 6's. */ + tcg_gen_andc_tl(carryl, tcg_constant_tl(carry_bits), carryl); + tcg_gen_muli_tl(cpu_gpr[a->rt], carryl, 6); + return true; +} + +static bool trans_CDTBCD(DisasContext *ctx, arg_X_sa *a) +{ + REQUIRE_INSNS_FLAGS2(ctx, BCDA_ISA206); + gen_helper_CDTBCD(cpu_gpr[a->ra], cpu_gpr[a->rs]); + return true; +} + +static bool trans_CBCDTD(DisasContext *ctx, arg_X_sa *a) +{ + REQUIRE_INSNS_FLAGS2(ctx, BCDA_ISA206); + gen_helper_CBCDTD(cpu_gpr[a->ra], cpu_gpr[a->rs]); + return true; +} + +static bool do_hash(DisasContext *ctx, arg_X *a, bool priv, + void (*helper)(TCGv_ptr, TCGv, TCGv, TCGv)) +{ + TCGv ea; + + if (!(ctx->insns_flags2 & PPC2_ISA310)) { + /* if version is before v3.1, this operation is a nop */ + return true; + } + + if (priv) { + /* if instruction is privileged but the context is in user space */ + REQUIRE_SV(ctx); + } + + if (unlikely(a->ra == 0)) { + /* if RA=0, the instruction form is invalid */ + gen_invalid(ctx); + return true; + } + + ea = do_ea_calc(ctx, a->ra, tcg_constant_tl(a->rt)); + helper(tcg_env, ea, cpu_gpr[a->ra], cpu_gpr[a->rb]); + return true; +} + +TRANS(HASHST, do_hash, false, gen_helper_HASHST) +TRANS(HASHCHK, do_hash, false, gen_helper_HASHCHK) +TRANS(HASHSTP, do_hash, true, gen_helper_HASHSTP) +TRANS(HASHCHKP, do_hash, true, gen_helper_HASHCHKP) diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc index f9b58b844e..189cd8c979 100644 --- a/target/ppc/translate/fp-impl.c.inc +++ b/target/ppc/translate/fp-impl.c.inc @@ -6,13 +6,13 @@ static inline void gen_reset_fpstatus(void) { - gen_helper_reset_fpstatus(cpu_env); + gen_helper_reset_fpstatus(tcg_env); } static inline void gen_compute_fprf_float64(TCGv_i64 arg) { - gen_helper_compute_fprf_float64(cpu_env, arg); - gen_helper_float_check_status(cpu_env); + gen_helper_compute_fprf_float64(tcg_env, arg); + gen_helper_float_check_status(tcg_env); } #if defined(TARGET_PPC64) @@ -21,7 +21,6 @@ static void gen_set_cr1_from_fpscr(DisasContext *ctx) TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(tmp, cpu_fpscr); tcg_gen_shri_i32(cpu_crf[1], tmp, 28); - tcg_temp_free_i32(tmp); } #else static void gen_set_cr1_from_fpscr(DisasContext *ctx) @@ -50,7 +49,7 @@ static void gen_f##name(DisasContext *ctx) \ get_fpr(t0, rA(ctx->opcode)); \ get_fpr(t1, rC(ctx->opcode)); \ get_fpr(t2, rB(ctx->opcode)); \ - gen_helper_f##name(t3, cpu_env, t0, t1, t2); \ + gen_helper_f##name(t3, tcg_env, t0, t1, t2); \ set_fpr(rD(ctx->opcode), t3); \ if (set_fprf) { \ gen_compute_fprf_float64(t3); \ @@ -58,10 +57,6 @@ static void gen_f##name(DisasContext *ctx) \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ - tcg_temp_free_i64(t0); \ - tcg_temp_free_i64(t1); \ - tcg_temp_free_i64(t2); \ - tcg_temp_free_i64(t3); \ } #define GEN_FLOAT_ACB(name, op2, set_fprf, type) \ @@ -84,7 +79,7 @@ static void gen_f##name(DisasContext *ctx) \ gen_reset_fpstatus(); \ get_fpr(t0, rA(ctx->opcode)); \ get_fpr(t1, rB(ctx->opcode)); \ - gen_helper_f##name(t2, cpu_env, t0, t1); \ + gen_helper_f##name(t2, tcg_env, t0, t1); \ set_fpr(rD(ctx->opcode), t2); \ if (set_fprf) { \ gen_compute_fprf_float64(t2); \ @@ -92,9 +87,6 @@ static void gen_f##name(DisasContext *ctx) \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ - tcg_temp_free_i64(t0); \ - tcg_temp_free_i64(t1); \ - tcg_temp_free_i64(t2); \ } #define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \ _GEN_FLOAT_AB(name, 0x3F, op2, inval, set_fprf, type); \ @@ -116,7 +108,7 @@ static void gen_f##name(DisasContext *ctx) \ gen_reset_fpstatus(); \ get_fpr(t0, rA(ctx->opcode)); \ get_fpr(t1, rC(ctx->opcode)); \ - gen_helper_f##name(t2, cpu_env, t0, t1); \ + gen_helper_f##name(t2, tcg_env, t0, t1); \ set_fpr(rD(ctx->opcode), t2); \ if (set_fprf) { \ gen_compute_fprf_float64(t2); \ @@ -124,9 +116,6 @@ static void gen_f##name(DisasContext *ctx) \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ - tcg_temp_free_i64(t0); \ - tcg_temp_free_i64(t1); \ - tcg_temp_free_i64(t2); \ } #define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \ _GEN_FLOAT_AC(name, 0x3F, op2, inval, set_fprf, type); \ @@ -145,17 +134,15 @@ static void gen_f##name(DisasContext *ctx) \ t1 = tcg_temp_new_i64(); \ gen_reset_fpstatus(); \ get_fpr(t0, rB(ctx->opcode)); \ - gen_helper_f##name(t1, cpu_env, t0); \ + gen_helper_f##name(t1, tcg_env, t0); \ set_fpr(rD(ctx->opcode), t1); \ if (set_fprf) { \ - gen_helper_compute_fprf_float64(cpu_env, t1); \ + gen_helper_compute_fprf_float64(tcg_env, t1); \ } \ - gen_helper_float_check_status(cpu_env); \ + gen_helper_float_check_status(tcg_env); \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ - tcg_temp_free_i64(t0); \ - tcg_temp_free_i64(t1); \ } #define GEN_FLOAT_BS(name, op1, op2, set_fprf, type) \ @@ -171,7 +158,7 @@ static void gen_f##name(DisasContext *ctx) \ t1 = tcg_temp_new_i64(); \ gen_reset_fpstatus(); \ get_fpr(t0, rB(ctx->opcode)); \ - gen_helper_f##name(t1, cpu_env, t0); \ + gen_helper_f##name(t1, tcg_env, t0); \ set_fpr(rD(ctx->opcode), t1); \ if (set_fprf) { \ gen_compute_fprf_float64(t1); \ @@ -179,8 +166,6 @@ static void gen_f##name(DisasContext *ctx) \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ - tcg_temp_free_i64(t0); \ - tcg_temp_free_i64(t1); \ } /* fadd - fadds */ @@ -212,14 +197,12 @@ static void gen_frsqrtes(DisasContext *ctx) t1 = tcg_temp_new_i64(); gen_reset_fpstatus(); get_fpr(t0, rB(ctx->opcode)); - gen_helper_frsqrtes(t1, cpu_env, t0); + gen_helper_frsqrtes(t1, tcg_env, t0); set_fpr(rD(ctx->opcode), t1); gen_compute_fprf_float64(t1); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_cr1_from_fpscr(ctx); } - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } static bool trans_FSEL(DisasContext *ctx, arg_A *a) @@ -242,11 +225,6 @@ static bool trans_FSEL(DisasContext *ctx, arg_A *a) if (a->rc) { gen_set_cr1_from_fpscr(ctx); } - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - return true; } @@ -254,50 +232,30 @@ static bool trans_FSEL(DisasContext *ctx, arg_A *a) GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT); /* Optional: */ -/* fsqrt */ -static void gen_fsqrt(DisasContext *ctx) +static bool do_helper_fsqrt(DisasContext *ctx, arg_A_tb *a, + void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64)) { - TCGv_i64 t0; - TCGv_i64 t1; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + TCGv_i64 t0, t1; + + REQUIRE_INSNS_FLAGS(ctx, FLOAT_FSQRT); + REQUIRE_FPU(ctx); + t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); + gen_reset_fpstatus(); - get_fpr(t0, rB(ctx->opcode)); - gen_helper_fsqrt(t1, cpu_env, t0); - set_fpr(rD(ctx->opcode), t1); + get_fpr(t0, a->frb); + helper(t1, tcg_env, t0); + set_fpr(a->frt, t1); gen_compute_fprf_float64(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { + if (unlikely(a->rc != 0)) { gen_set_cr1_from_fpscr(ctx); } - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); + return true; } -static void gen_fsqrts(DisasContext *ctx) -{ - TCGv_i64 t0; - TCGv_i64 t1; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - gen_reset_fpstatus(); - get_fpr(t0, rB(ctx->opcode)); - gen_helper_fsqrts(t1, cpu_env, t0); - set_fpr(rD(ctx->opcode), t1); - gen_compute_fprf_float64(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_cr1_from_fpscr(ctx); - } - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); -} +TRANS(FSQRT, do_helper_fsqrt, gen_helper_FSQRT); +TRANS(FSQRTS, do_helper_fsqrt, gen_helper_FSQRTS); /*** Floating-Point multiply-and-add ***/ /* fmadd - fmadds */ @@ -359,8 +317,6 @@ static void gen_ftdiv(DisasContext *ctx) get_fpr(t0, rA(ctx->opcode)); get_fpr(t1, rB(ctx->opcode)); gen_helper_ftdiv(cpu_crf[crfD(ctx->opcode)], t0, t1); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } static void gen_ftsqrt(DisasContext *ctx) @@ -373,7 +329,6 @@ static void gen_ftsqrt(DisasContext *ctx) t0 = tcg_temp_new_i64(); get_fpr(t0, rB(ctx->opcode)); gen_helper_ftsqrt(cpu_crf[crfD(ctx->opcode)], t0); - tcg_temp_free_i64(t0); } @@ -393,14 +348,11 @@ static void gen_fcmpo(DisasContext *ctx) t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); gen_reset_fpstatus(); - crf = tcg_const_i32(crfD(ctx->opcode)); + crf = tcg_constant_i32(crfD(ctx->opcode)); get_fpr(t0, rA(ctx->opcode)); get_fpr(t1, rB(ctx->opcode)); - gen_helper_fcmpo(cpu_env, t0, t1, crf); - tcg_temp_free_i32(crf); - gen_helper_float_check_status(cpu_env); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); + gen_helper_fcmpo(tcg_env, t0, t1, crf); + gen_helper_float_check_status(tcg_env); } /* fcmpu */ @@ -416,14 +368,11 @@ static void gen_fcmpu(DisasContext *ctx) t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); gen_reset_fpstatus(); - crf = tcg_const_i32(crfD(ctx->opcode)); + crf = tcg_constant_i32(crfD(ctx->opcode)); get_fpr(t0, rA(ctx->opcode)); get_fpr(t1, rB(ctx->opcode)); - gen_helper_fcmpu(cpu_env, t0, t1, crf); - tcg_temp_free_i32(crf); - gen_helper_float_check_status(cpu_env); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); + gen_helper_fcmpu(tcg_env, t0, t1, crf); + gen_helper_float_check_status(tcg_env); } /*** Floating-point move ***/ @@ -445,8 +394,6 @@ static void gen_fabs(DisasContext *ctx) if (unlikely(Rc(ctx->opcode))) { gen_set_cr1_from_fpscr(ctx); } - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } /* fmr - fmr. */ @@ -464,7 +411,6 @@ static void gen_fmr(DisasContext *ctx) if (unlikely(Rc(ctx->opcode))) { gen_set_cr1_from_fpscr(ctx); } - tcg_temp_free_i64(t0); } /* fnabs */ @@ -485,8 +431,6 @@ static void gen_fnabs(DisasContext *ctx) if (unlikely(Rc(ctx->opcode))) { gen_set_cr1_from_fpscr(ctx); } - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } /* fneg */ @@ -507,8 +451,6 @@ static void gen_fneg(DisasContext *ctx) if (unlikely(Rc(ctx->opcode))) { gen_set_cr1_from_fpscr(ctx); } - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } /* fcpsgn: PowerPC 2.05 specification */ @@ -532,9 +474,6 @@ static void gen_fcpsgn(DisasContext *ctx) if (unlikely(Rc(ctx->opcode))) { gen_set_cr1_from_fpscr(ctx); } - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); } static void gen_fmrgew(DisasContext *ctx) @@ -554,9 +493,6 @@ static void gen_fmrgew(DisasContext *ctx) get_fpr(t0, rA(ctx->opcode)); tcg_gen_deposit_i64(t1, t0, b0, 0, 32); set_fpr(rD(ctx->opcode), t1); - tcg_temp_free_i64(b0); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } static void gen_fmrgow(DisasContext *ctx) @@ -575,9 +511,6 @@ static void gen_fmrgow(DisasContext *ctx) get_fpr(t1, rA(ctx->opcode)); tcg_gen_deposit_i64(t2, t0, t1, 32, 32); set_fpr(rD(ctx->opcode), t2); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); } /*** Floating-Point status & ctrl register ***/ @@ -603,153 +536,147 @@ static void gen_mcrfs(DisasContext *ctx) tcg_gen_trunc_tl_i32(cpu_crf[crfD(ctx->opcode)], tmp); tcg_gen_andi_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfD(ctx->opcode)], 0xf); - tcg_temp_free(tmp); tcg_gen_extu_tl_i64(tnew_fpscr, cpu_fpscr); /* Only the exception bits (including FX) should be cleared if read */ tcg_gen_andi_i64(tnew_fpscr, tnew_fpscr, ~((0xF << shift) & FP_EX_CLEAR_BITS)); /* FEX and VX need to be updated, so don't set fpscr directly */ - tmask = tcg_const_i32(1 << nibble); - gen_helper_store_fpscr(cpu_env, tnew_fpscr, tmask); - tcg_temp_free_i32(tmask); - tcg_temp_free_i64(tnew_fpscr); + tmask = tcg_constant_i32(1 << nibble); + gen_helper_store_fpscr(tcg_env, tnew_fpscr, tmask); } -/* mffs */ -static void gen_mffs(DisasContext *ctx) +static TCGv_i64 place_from_fpscr(int rt, uint64_t mask) { - TCGv_i64 t0; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; + TCGv_i64 fpscr = tcg_temp_new_i64(); + TCGv_i64 fpscr_masked = tcg_temp_new_i64(); + + tcg_gen_extu_tl_i64(fpscr, cpu_fpscr); + tcg_gen_andi_i64(fpscr_masked, fpscr, mask); + set_fpr(rt, fpscr_masked); + + return fpscr; +} + +static void store_fpscr_masked(TCGv_i64 fpscr, uint64_t clear_mask, + TCGv_i64 set_mask, uint32_t store_mask) +{ + TCGv_i64 fpscr_masked = tcg_temp_new_i64(); + TCGv_i32 st_mask = tcg_constant_i32(store_mask); + + tcg_gen_andi_i64(fpscr_masked, fpscr, ~clear_mask); + tcg_gen_or_i64(fpscr_masked, fpscr_masked, set_mask); + gen_helper_store_fpscr(tcg_env, fpscr_masked, st_mask); +} + +static bool trans_MFFS_ISA207(DisasContext *ctx, arg_X_t_rc *a) +{ + if (!(ctx->insns_flags2 & PPC2_ISA300)) { + /* + * Before Power ISA v3.0, MFFS bits 11~15 were reserved, any instruction + * with OPCD=63 and XO=583 should be decoded as MFFS. + */ + return trans_MFFS(ctx, a); } - t0 = tcg_temp_new_i64(); + /* + * For Power ISA v3.0+, return false and let the pattern group + * select the correct instruction. + */ + return false; +} + +static bool trans_MFFS(DisasContext *ctx, arg_X_t_rc *a) +{ + REQUIRE_FPU(ctx); + gen_reset_fpstatus(); - tcg_gen_extu_tl_i64(t0, cpu_fpscr); - set_fpr(rD(ctx->opcode), t0); - if (unlikely(Rc(ctx->opcode))) { + place_from_fpscr(a->rt, UINT64_MAX); + if (a->rc) { gen_set_cr1_from_fpscr(ctx); } - tcg_temp_free_i64(t0); + return true; } -/* mffsl */ -static void gen_mffsl(DisasContext *ctx) +static bool trans_MFFSCE(DisasContext *ctx, arg_X_t *a) { - TCGv_i64 t0; + TCGv_i64 fpscr; - if (unlikely(!(ctx->insns_flags2 & PPC2_ISA300))) { - return gen_mffs(ctx); - } - - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } - t0 = tcg_temp_new_i64(); - gen_reset_fpstatus(); - tcg_gen_extu_tl_i64(t0, cpu_fpscr); - /* Mask everything except mode, status, and enables. */ - tcg_gen_andi_i64(t0, t0, FP_DRN | FP_STATUS | FP_ENABLES | FP_RN); - set_fpr(rD(ctx->opcode), t0); - tcg_temp_free_i64(t0); -} - -/* mffsce */ -static void gen_mffsce(DisasContext *ctx) -{ - TCGv_i64 t0; - TCGv_i32 mask; - - if (unlikely(!(ctx->insns_flags2 & PPC2_ISA300))) { - return gen_mffs(ctx); - } - - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } - - t0 = tcg_temp_new_i64(); + REQUIRE_FPU(ctx); gen_reset_fpstatus(); - tcg_gen_extu_tl_i64(t0, cpu_fpscr); - set_fpr(rD(ctx->opcode), t0); - - /* Clear exception enable bits in the FPSCR. */ - tcg_gen_andi_i64(t0, t0, ~FP_ENABLES); - mask = tcg_const_i32(0x0003); - gen_helper_store_fpscr(cpu_env, t0, mask); - - tcg_temp_free_i32(mask); - tcg_temp_free_i64(t0); + fpscr = place_from_fpscr(a->rt, UINT64_MAX); + store_fpscr_masked(fpscr, FP_ENABLES, tcg_constant_i64(0), 0x0003); + return true; } -static void gen_helper_mffscrn(DisasContext *ctx, TCGv_i64 t1) +static bool trans_MFFSCRN(DisasContext *ctx, arg_X_tb *a) { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i32 mask = tcg_const_i32(0x0001); + TCGv_i64 t1, fpscr; - gen_reset_fpstatus(); - tcg_gen_extu_tl_i64(t0, cpu_fpscr); - tcg_gen_andi_i64(t0, t0, FP_DRN | FP_ENABLES | FP_RN); - set_fpr(rD(ctx->opcode), t0); - - /* Mask FPSCR value to clear RN. */ - tcg_gen_andi_i64(t0, t0, ~FP_RN); - - /* Merge RN into FPSCR value. */ - tcg_gen_or_i64(t0, t0, t1); - - gen_helper_store_fpscr(cpu_env, t0, mask); - - tcg_temp_free_i32(mask); - tcg_temp_free_i64(t0); -} - -/* mffscrn */ -static void gen_mffscrn(DisasContext *ctx) -{ - TCGv_i64 t1; - - if (unlikely(!(ctx->insns_flags2 & PPC2_ISA300))) { - return gen_mffs(ctx); - } - - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + REQUIRE_FPU(ctx); t1 = tcg_temp_new_i64(); - get_fpr(t1, rB(ctx->opcode)); - /* Mask FRB to get just RN. */ + get_fpr(t1, a->rb); tcg_gen_andi_i64(t1, t1, FP_RN); - gen_helper_mffscrn(ctx, t1); - - tcg_temp_free_i64(t1); + gen_reset_fpstatus(); + fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN); + store_fpscr_masked(fpscr, FP_RN, t1, 0x0001); + return true; } -/* mffscrni */ -static void gen_mffscrni(DisasContext *ctx) +static bool trans_MFFSCDRN(DisasContext *ctx, arg_X_tb *a) { - TCGv_i64 t1; + TCGv_i64 t1, fpscr; - if (unlikely(!(ctx->insns_flags2 & PPC2_ISA300))) { - return gen_mffs(ctx); - } + REQUIRE_FPU(ctx); - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + t1 = tcg_temp_new_i64(); + get_fpr(t1, a->rb); + tcg_gen_andi_i64(t1, t1, FP_DRN); - t1 = tcg_const_i64((uint64_t)RM(ctx->opcode)); + gen_reset_fpstatus(); + fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN); + store_fpscr_masked(fpscr, FP_DRN, t1, 0x0100); + return true; +} - gen_helper_mffscrn(ctx, t1); +static bool trans_MFFSCRNI(DisasContext *ctx, arg_X_imm2 *a) +{ + TCGv_i64 t1, fpscr; - tcg_temp_free_i64(t1); + REQUIRE_FPU(ctx); + + t1 = tcg_temp_new_i64(); + tcg_gen_movi_i64(t1, a->imm); + + gen_reset_fpstatus(); + fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN); + store_fpscr_masked(fpscr, FP_RN, t1, 0x0001); + return true; +} + +static bool trans_MFFSCDRNI(DisasContext *ctx, arg_X_imm3 *a) +{ + TCGv_i64 t1, fpscr; + + REQUIRE_FPU(ctx); + + t1 = tcg_temp_new_i64(); + tcg_gen_movi_i64(t1, (uint64_t)a->imm << FPSCR_DRN0); + + gen_reset_fpstatus(); + fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN); + store_fpscr_masked(fpscr, FP_DRN, t1, 0x0100); + return true; +} + +static bool trans_MFFSL(DisasContext *ctx, arg_X_t *a) +{ + REQUIRE_FPU(ctx); + + gen_reset_fpstatus(); + place_from_fpscr(a->rt, FP_DRN | FP_STATUS | FP_ENABLES | FP_NI | FP_RN); + return true; } /* mtfsb0 */ @@ -764,10 +691,7 @@ static void gen_mtfsb0(DisasContext *ctx) crb = 31 - crbD(ctx->opcode); gen_reset_fpstatus(); if (likely(crb != FPSCR_FEX && crb != FPSCR_VX)) { - TCGv_i32 t0; - t0 = tcg_const_i32(crb); - gen_helper_fpscr_clrbit(cpu_env, t0); - tcg_temp_free_i32(t0); + gen_helper_fpscr_clrbit(tcg_env, tcg_constant_i32(crb)); } if (unlikely(Rc(ctx->opcode) != 0)) { tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr); @@ -787,17 +711,14 @@ static void gen_mtfsb1(DisasContext *ctx) crb = 31 - crbD(ctx->opcode); /* XXX: we pretend we can only do IEEE floating-point computations */ if (likely(crb != FPSCR_FEX && crb != FPSCR_VX && crb != FPSCR_NI)) { - TCGv_i32 t0; - t0 = tcg_const_i32(crb); - gen_helper_fpscr_setbit(cpu_env, t0); - tcg_temp_free_i32(t0); + gen_helper_fpscr_setbit(tcg_env, tcg_constant_i32(crb)); } if (unlikely(Rc(ctx->opcode) != 0)) { 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 deferred exception */ - gen_helper_fpscr_check_status(cpu_env); + gen_helper_fpscr_check_status(tcg_env); } /* mtfsf */ @@ -818,22 +739,22 @@ static void gen_mtfsf(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); return; } - if (l) { - t0 = tcg_const_i32((ctx->insns_flags2 & PPC2_ISA205) ? 0xffff : 0xff); + if (!l) { + t0 = tcg_constant_i32(flm << (w * 8)); + } else if (ctx->insns_flags2 & PPC2_ISA205) { + t0 = tcg_constant_i32(0xffff); } else { - t0 = tcg_const_i32(flm << (w * 8)); + t0 = tcg_constant_i32(0xff); } t1 = tcg_temp_new_i64(); get_fpr(t1, rB(ctx->opcode)); - gen_helper_store_fpscr(cpu_env, t1, t0); - tcg_temp_free_i32(t0); + gen_helper_store_fpscr(tcg_env, t1, t0); if (unlikely(Rc(ctx->opcode) != 0)) { 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 deferred exception */ - gen_helper_fpscr_check_status(cpu_env); - tcg_temp_free_i64(t1); + gen_helper_fpscr_check_status(tcg_env); } /* mtfsfi */ @@ -854,17 +775,15 @@ static void gen_mtfsfi(DisasContext *ctx) return; } sh = (8 * w) + 7 - bf; - t0 = tcg_const_i64(((uint64_t)FPIMM(ctx->opcode)) << (4 * sh)); - t1 = tcg_const_i32(1 << sh); - gen_helper_store_fpscr(cpu_env, t0, t1); - tcg_temp_free_i64(t0); - tcg_temp_free_i32(t1); + t0 = tcg_constant_i64(((uint64_t)FPIMM(ctx->opcode)) << (4 * sh)); + t1 = tcg_constant_i32(1 << sh); + gen_helper_store_fpscr(tcg_env, t0, t1); if (unlikely(Rc(ctx->opcode) != 0)) { 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 deferred exception */ - gen_helper_fpscr_check_status(cpu_env); + gen_helper_fpscr_check_status(tcg_env); } static void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 dest, TCGv addr) @@ -872,7 +791,6 @@ static void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 dest, TCGv addr) TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_qemu_ld_i32(tmp, addr, ctx->mem_idx, DEF_MEMOP(MO_UL)); gen_helper_todouble(dest, tmp); - tcg_temp_free_i32(tmp); } /* lfdepx (external PID lfdx) */ @@ -880,7 +798,7 @@ static void gen_lfdepx(DisasContext *ctx) { TCGv EA; TCGv_i64 t0; - CHK_SV; + CHK_SV(ctx); if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; @@ -891,8 +809,6 @@ static void gen_lfdepx(DisasContext *ctx) gen_addr_reg_index(ctx, EA); tcg_gen_qemu_ld_i64(t0, EA, PPC_TLB_EPID_LOAD, DEF_MEMOP(MO_UQ)); set_fpr(rD(ctx->opcode), t0); - tcg_temp_free(EA); - tcg_temp_free_i64(t0); } /* lfdp */ @@ -925,8 +841,6 @@ static void gen_lfdp(DisasContext *ctx) gen_qemu_ld64_i64(ctx, t0, EA); set_fpr(rD(ctx->opcode) + 1, t0); } - tcg_temp_free(EA); - tcg_temp_free_i64(t0); } /* lfdpx */ @@ -959,8 +873,6 @@ static void gen_lfdpx(DisasContext *ctx) gen_qemu_ld64_i64(ctx, t0, EA); set_fpr(rD(ctx->opcode) + 1, t0); } - tcg_temp_free(EA); - tcg_temp_free_i64(t0); } /* lfiwax */ @@ -981,9 +893,6 @@ static void gen_lfiwax(DisasContext *ctx) gen_qemu_ld32s(ctx, t0, EA); tcg_gen_ext_tl_i64(t1, t0); set_fpr(rD(ctx->opcode), t1); - tcg_temp_free(EA); - tcg_temp_free(t0); - tcg_temp_free_i64(t1); } /* lfiwzx */ @@ -1001,8 +910,6 @@ static void gen_lfiwzx(DisasContext *ctx) gen_addr_reg_index(ctx, EA); gen_qemu_ld32u_i64(ctx, t0, EA); set_fpr(rD(ctx->opcode), t0); - tcg_temp_free(EA); - tcg_temp_free_i64(t0); } #define GEN_STXF(name, stop, opc2, opc3, type) \ @@ -1020,8 +927,6 @@ static void glue(gen_, name##x)(DisasContext *ctx) \ gen_addr_reg_index(ctx, EA); \ get_fpr(t0, rS(ctx->opcode)); \ gen_qemu_##stop(ctx, t0, EA); \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(t0); \ } static void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 src, TCGv addr) @@ -1029,7 +934,6 @@ static void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 src, TCGv addr) TCGv_i32 tmp = tcg_temp_new_i32(); gen_helper_tosingle(tmp, src); tcg_gen_qemu_st_i32(tmp, addr, ctx->mem_idx, DEF_MEMOP(MO_UL)); - tcg_temp_free_i32(tmp); } /* stfdepx (external PID lfdx) */ @@ -1037,7 +941,7 @@ static void gen_stfdepx(DisasContext *ctx) { TCGv EA; TCGv_i64 t0; - CHK_SV; + CHK_SV(ctx); if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; @@ -1048,8 +952,6 @@ static void gen_stfdepx(DisasContext *ctx) gen_addr_reg_index(ctx, EA); get_fpr(t0, rD(ctx->opcode)); tcg_gen_qemu_st_i64(t0, EA, PPC_TLB_EPID_STORE, DEF_MEMOP(MO_UQ)); - tcg_temp_free(EA); - tcg_temp_free_i64(t0); } /* stfdp */ @@ -1082,8 +984,6 @@ static void gen_stfdp(DisasContext *ctx) get_fpr(t0, rD(ctx->opcode) + 1); gen_qemu_st64_i64(ctx, t0, EA); } - tcg_temp_free(EA); - tcg_temp_free_i64(t0); } /* stfdpx */ @@ -1116,8 +1016,6 @@ static void gen_stfdpx(DisasContext *ctx) get_fpr(t0, rD(ctx->opcode) + 1); gen_qemu_st64_i64(ctx, t0, EA); } - tcg_temp_free(EA); - tcg_temp_free_i64(t0); } /* Optional: */ @@ -1126,7 +1024,6 @@ static inline void gen_qemu_st32fiw(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) TCGv t0 = tcg_temp_new(); tcg_gen_trunc_i64_tl(t0, arg1), gen_qemu_st32(ctx, t0, arg2); - tcg_temp_free(t0); } /* stfiwx */ GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX); @@ -1164,8 +1061,6 @@ static bool do_lsfpsd(DisasContext *ctx, int rt, int ra, TCGv displ, if (update) { tcg_gen_mov_tl(cpu_gpr[ra], ea); } - tcg_temp_free_i64(t0); - tcg_temp_free(ea); return true; } diff --git a/target/ppc/translate/fp-ops.c.inc b/target/ppc/translate/fp-ops.c.inc index 0538ab2d2d..d4c6c4bed1 100644 --- a/target/ppc/translate/fp-ops.c.inc +++ b/target/ppc/translate/fp-ops.c.inc @@ -62,8 +62,6 @@ GEN_HANDLER_E(stfdepx, 0x1F, 0x1F, 0x16, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER_E(stfdpx, 0x1F, 0x17, 0x1C, 0x00200001, PPC_NONE, PPC2_ISA205), GEN_HANDLER(frsqrtes, 0x3B, 0x1A, 0xFF, 0x001F07C0, PPC_FLOAT_FRSQRTES), -GEN_HANDLER(fsqrt, 0x3F, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_FSQRT), -GEN_HANDLER(fsqrts, 0x3B, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_FSQRT), GEN_HANDLER(fcmpo, 0x3F, 0x00, 0x01, 0x00600001, PPC_FLOAT), GEN_HANDLER(fcmpu, 0x3F, 0x00, 0x00, 0x00600001, PPC_FLOAT), GEN_HANDLER(fabs, 0x3F, 0x08, 0x08, 0x001F0000, PPC_FLOAT), @@ -74,15 +72,6 @@ GEN_HANDLER_E(fcpsgn, 0x3F, 0x08, 0x00, 0x00000000, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(fmrgew, 0x3F, 0x06, 0x1E, 0x00000001, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(fmrgow, 0x3F, 0x06, 0x1A, 0x00000001, PPC_NONE, PPC2_VSX207), GEN_HANDLER(mcrfs, 0x3F, 0x00, 0x02, 0x0063F801, PPC_FLOAT), -GEN_HANDLER_E_2(mffs, 0x3F, 0x07, 0x12, 0x00, 0x00000000, PPC_FLOAT, PPC_NONE), -GEN_HANDLER_E_2(mffsce, 0x3F, 0x07, 0x12, 0x01, 0x00000000, PPC_FLOAT, - PPC2_ISA300), -GEN_HANDLER_E_2(mffsl, 0x3F, 0x07, 0x12, 0x18, 0x00000000, PPC_FLOAT, - PPC2_ISA300), -GEN_HANDLER_E_2(mffscrn, 0x3F, 0x07, 0x12, 0x16, 0x00000000, PPC_FLOAT, - PPC_NONE), -GEN_HANDLER_E_2(mffscrni, 0x3F, 0x07, 0x12, 0x17, 0x00000000, PPC_FLOAT, - PPC_NONE), GEN_HANDLER(mtfsb0, 0x3F, 0x06, 0x02, 0x001FF800, PPC_FLOAT), GEN_HANDLER(mtfsb1, 0x3F, 0x06, 0x01, 0x001FF800, PPC_FLOAT), GEN_HANDLER(mtfsf, 0x3F, 0x07, 0x16, 0x00000000, PPC_FLOAT), diff --git a/target/ppc/translate/processor-ctrl-impl.c.inc b/target/ppc/translate/processor-ctrl-impl.c.inc new file mode 100644 index 0000000000..0142801985 --- /dev/null +++ b/target/ppc/translate/processor-ctrl-impl.c.inc @@ -0,0 +1,105 @@ +/* + * Power ISA decode for Storage Control instructions + * + * Copyright (c) 2022 Instituto de Pesquisas Eldorado (eldorado.org.br) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 . + */ + +/* + * Processor Control Instructions + */ + +static bool trans_MSGCLR(DisasContext *ctx, arg_X_rb *a) +{ + if (!(ctx->insns_flags2 & PPC2_ISA207S)) { + /* + * Before Power ISA 2.07, processor control instructions were only + * implemented in the "Embedded.Processor Control" category. + */ + REQUIRE_INSNS_FLAGS2(ctx, PRCNTL); + } + + REQUIRE_HV(ctx); + +#if !defined(CONFIG_USER_ONLY) + if (is_book3s_arch2x(ctx)) { + gen_helper_book3s_msgclr(tcg_env, cpu_gpr[a->rb]); + } else { + gen_helper_msgclr(tcg_env, cpu_gpr[a->rb]); + } +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_MSGSND(DisasContext *ctx, arg_X_rb *a) +{ + if (!(ctx->insns_flags2 & PPC2_ISA207S)) { + /* + * Before Power ISA 2.07, processor control instructions were only + * implemented in the "Embedded.Processor Control" category. + */ + REQUIRE_INSNS_FLAGS2(ctx, PRCNTL); + } + + REQUIRE_HV(ctx); + +#if !defined(CONFIG_USER_ONLY) + if (is_book3s_arch2x(ctx)) { + gen_helper_book3s_msgsnd(cpu_gpr[a->rb]); + } else { + gen_helper_msgsnd(cpu_gpr[a->rb]); + } +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_MSGCLRP(DisasContext *ctx, arg_X_rb *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA207S); + REQUIRE_SV(ctx); +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_book3s_msgclrp(tcg_env, cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_MSGSNDP(DisasContext *ctx, arg_X_rb *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA207S); + REQUIRE_SV(ctx); +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_book3s_msgsndp(tcg_env, cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_MSGSYNC(DisasContext *ctx, arg_MSGSYNC *a) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_HV(ctx); + + /* interpreted as no-op */ + return true; +} diff --git a/target/ppc/translate/spe-impl.c.inc b/target/ppc/translate/spe-impl.c.inc index 2e6e799a25..454dac823e 100644 --- a/target/ppc/translate/spe-impl.c.inc +++ b/target/ppc/translate/spe-impl.c.inc @@ -22,8 +22,7 @@ static inline void gen_evmra(DisasContext *ctx) cpu_gprh[rA(ctx->opcode)]); /* spe_acc := tmp */ - tcg_gen_st_i64(tmp, cpu_env, offsetof(CPUPPCState, spe_acc)); - tcg_temp_free_i64(tmp); + tcg_gen_st_i64(tmp, tcg_env, offsetof(CPUPPCState, spe_acc)); /* rD := rA */ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); @@ -96,8 +95,6 @@ static inline void gen_##name(DisasContext *ctx) \ tcg_gen_trunc_tl_i32(t0, cpu_gprh[rA(ctx->opcode)]); \ tcg_opi(t0, t0, rB(ctx->opcode)); \ tcg_gen_extu_i32_tl(cpu_gprh[rD(ctx->opcode)], t0); \ - \ - tcg_temp_free_i32(t0); \ } GEN_SPEOP_TCG_LOGIC_IMM2(evslwi, tcg_gen_shli_i32); GEN_SPEOP_TCG_LOGIC_IMM2(evsrwiu, tcg_gen_shri_i32); @@ -122,8 +119,6 @@ static inline void gen_##name(DisasContext *ctx) \ tcg_gen_trunc_tl_i32(t0, cpu_gprh[rA(ctx->opcode)]); \ tcg_op(t0, t0); \ tcg_gen_extu_i32_tl(cpu_gprh[rD(ctx->opcode)], t0); \ - \ - tcg_temp_free_i32(t0); \ } GEN_SPEOP_ARITH1(evabs, tcg_gen_abs_i32); @@ -159,16 +154,13 @@ static inline void gen_##name(DisasContext *ctx) \ tcg_gen_trunc_tl_i32(t1, cpu_gprh[rB(ctx->opcode)]); \ tcg_op(t0, t0, t1); \ tcg_gen_extu_i32_tl(cpu_gprh[rD(ctx->opcode)], t0); \ - \ - tcg_temp_free_i32(t0); \ - tcg_temp_free_i32(t1); \ } static inline void gen_op_evsrwu(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { TCGLabel *l1 = gen_new_label(); TCGLabel *l2 = gen_new_label(); - TCGv_i32 t0 = tcg_temp_local_new_i32(); + TCGv_i32 t0 = tcg_temp_new_i32(); /* No error here: 6 bits are used */ tcg_gen_andi_i32(t0, arg2, 0x3F); @@ -178,14 +170,13 @@ static inline void gen_op_evsrwu(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) gen_set_label(l1); tcg_gen_movi_i32(ret, 0); gen_set_label(l2); - tcg_temp_free_i32(t0); } GEN_SPEOP_ARITH2(evsrwu, gen_op_evsrwu); static inline void gen_op_evsrws(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { TCGLabel *l1 = gen_new_label(); TCGLabel *l2 = gen_new_label(); - TCGv_i32 t0 = tcg_temp_local_new_i32(); + TCGv_i32 t0 = tcg_temp_new_i32(); /* No error here: 6 bits are used */ tcg_gen_andi_i32(t0, arg2, 0x3F); @@ -195,14 +186,13 @@ static inline void gen_op_evsrws(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) gen_set_label(l1); tcg_gen_movi_i32(ret, 0); gen_set_label(l2); - tcg_temp_free_i32(t0); } GEN_SPEOP_ARITH2(evsrws, gen_op_evsrws); static inline void gen_op_evslw(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { TCGLabel *l1 = gen_new_label(); TCGLabel *l2 = gen_new_label(); - TCGv_i32 t0 = tcg_temp_local_new_i32(); + TCGv_i32 t0 = tcg_temp_new_i32(); /* No error here: 6 bits are used */ tcg_gen_andi_i32(t0, arg2, 0x3F); @@ -212,7 +202,6 @@ static inline void gen_op_evslw(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) gen_set_label(l1); tcg_gen_movi_i32(ret, 0); gen_set_label(l2); - tcg_temp_free_i32(t0); } GEN_SPEOP_ARITH2(evslw, gen_op_evslw); static inline void gen_op_evrlw(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) @@ -220,7 +209,6 @@ static inline void gen_op_evrlw(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) TCGv_i32 t0 = tcg_temp_new_i32(); tcg_gen_andi_i32(t0, arg2, 0x1F); tcg_gen_rotl_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); } GEN_SPEOP_ARITH2(evrlw, gen_op_evrlw); static inline void gen_evmergehi(DisasContext *ctx) @@ -257,8 +245,6 @@ static inline void gen_##name(DisasContext *ctx) \ tcg_gen_trunc_tl_i32(t0, cpu_gprh[rB(ctx->opcode)]); \ tcg_op(t0, t0, rA(ctx->opcode)); \ tcg_gen_extu_i32_tl(cpu_gprh[rD(ctx->opcode)], t0); \ - \ - tcg_temp_free_i32(t0); \ } GEN_SPEOP_ARITH_IMM2(evaddiw, tcg_gen_addi_i32); GEN_SPEOP_ARITH_IMM2(evsubifw, tcg_gen_subi_i32); @@ -341,7 +327,6 @@ static inline void gen_evmergelohi(DisasContext *ctx) tcg_gen_mov_tl(tmp, cpu_gpr[rA(ctx->opcode)]); tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gprh[rB(ctx->opcode)]); tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], tmp); - tcg_temp_free(tmp); } else { tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gprh[rB(ctx->opcode)]); tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); @@ -378,7 +363,7 @@ static inline void gen_evsel(DisasContext *ctx) TCGLabel *l2 = gen_new_label(); TCGLabel *l3 = gen_new_label(); TCGLabel *l4 = gen_new_label(); - TCGv_i32 t0 = tcg_temp_local_new_i32(); + TCGv_i32 t0 = tcg_temp_new_i32(); tcg_gen_andi_i32(t0, cpu_crf[ctx->opcode & 0x07], 1 << 3); tcg_gen_brcondi_i32(TCG_COND_EQ, t0, 0, l1); @@ -394,7 +379,6 @@ static inline void gen_evsel(DisasContext *ctx) gen_set_label(l3); tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); gen_set_label(l4); - tcg_temp_free_i32(t0); } static void gen_evsel0(DisasContext *ctx) @@ -456,9 +440,6 @@ static inline void gen_evmwumi(DisasContext *ctx) tcg_gen_mul_i64(t0, t0, t1); /* t0 := rA * rB */ gen_store_gpr64(rD(ctx->opcode), t0); /* rD := t0 */ - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } static inline void gen_evmwumia(DisasContext *ctx) @@ -476,8 +457,7 @@ static inline void gen_evmwumia(DisasContext *ctx) /* acc := rD */ gen_load_gpr64(tmp, rD(ctx->opcode)); - tcg_gen_st_i64(tmp, cpu_env, offsetof(CPUPPCState, spe_acc)); - tcg_temp_free_i64(tmp); + tcg_gen_st_i64(tmp, tcg_env, offsetof(CPUPPCState, spe_acc)); } static inline void gen_evmwumiaa(DisasContext *ctx) @@ -499,19 +479,16 @@ static inline void gen_evmwumiaa(DisasContext *ctx) gen_load_gpr64(tmp, rD(ctx->opcode)); /* Load acc */ - tcg_gen_ld_i64(acc, cpu_env, offsetof(CPUPPCState, spe_acc)); + tcg_gen_ld_i64(acc, tcg_env, offsetof(CPUPPCState, spe_acc)); /* acc := tmp + acc */ tcg_gen_add_i64(acc, acc, tmp); /* Store acc */ - tcg_gen_st_i64(acc, cpu_env, offsetof(CPUPPCState, spe_acc)); + tcg_gen_st_i64(acc, tcg_env, offsetof(CPUPPCState, spe_acc)); /* rD := acc */ gen_store_gpr64(rD(ctx->opcode), acc); - - tcg_temp_free_i64(acc); - tcg_temp_free_i64(tmp); } static inline void gen_evmwsmi(DisasContext *ctx) @@ -535,9 +512,6 @@ static inline void gen_evmwsmi(DisasContext *ctx) tcg_gen_mul_i64(t0, t0, t1); /* t0 := rA * rB */ gen_store_gpr64(rD(ctx->opcode), t0); /* rD := t0 */ - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } static inline void gen_evmwsmia(DisasContext *ctx) @@ -555,9 +529,7 @@ static inline void gen_evmwsmia(DisasContext *ctx) /* acc := rD */ gen_load_gpr64(tmp, rD(ctx->opcode)); - tcg_gen_st_i64(tmp, cpu_env, offsetof(CPUPPCState, spe_acc)); - - tcg_temp_free_i64(tmp); + tcg_gen_st_i64(tmp, tcg_env, offsetof(CPUPPCState, spe_acc)); } static inline void gen_evmwsmiaa(DisasContext *ctx) @@ -579,19 +551,16 @@ static inline void gen_evmwsmiaa(DisasContext *ctx) gen_load_gpr64(tmp, rD(ctx->opcode)); /* Load acc */ - tcg_gen_ld_i64(acc, cpu_env, offsetof(CPUPPCState, spe_acc)); + tcg_gen_ld_i64(acc, tcg_env, offsetof(CPUPPCState, spe_acc)); /* acc := tmp + acc */ tcg_gen_add_i64(acc, acc, tmp); /* Store acc */ - tcg_gen_st_i64(acc, cpu_env, offsetof(CPUPPCState, spe_acc)); + tcg_gen_st_i64(acc, tcg_env, offsetof(CPUPPCState, spe_acc)); /* rD := acc */ gen_store_gpr64(rD(ctx->opcode), acc); - - tcg_temp_free_i64(acc); - tcg_temp_free_i64(tmp); } GEN_SPE(evaddw, speundef, 0x00, 0x08, 0x00000000, 0xFFFFFFFF, PPC_SPE); //// @@ -644,7 +613,6 @@ static inline void gen_op_evldd(DisasContext *ctx, TCGv addr) TCGv_i64 t0 = tcg_temp_new_i64(); gen_qemu_ld64_i64(ctx, t0, addr); gen_store_gpr64(rD(ctx->opcode), t0); - tcg_temp_free_i64(t0); } static inline void gen_op_evldw(DisasContext *ctx, TCGv addr) @@ -668,7 +636,6 @@ static inline void gen_op_evldh(DisasContext *ctx, TCGv addr) gen_addr_add(ctx, addr, addr, 2); gen_qemu_ld16u(ctx, t0, addr); tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0); - tcg_temp_free(t0); } static inline void gen_op_evlhhesplat(DisasContext *ctx, TCGv addr) @@ -678,7 +645,6 @@ static inline void gen_op_evlhhesplat(DisasContext *ctx, TCGv addr) tcg_gen_shli_tl(t0, t0, 16); tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], t0); tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0); - tcg_temp_free(t0); } static inline void gen_op_evlhhousplat(DisasContext *ctx, TCGv addr) @@ -687,7 +653,6 @@ static inline void gen_op_evlhhousplat(DisasContext *ctx, TCGv addr) gen_qemu_ld16u(ctx, t0, addr); tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], t0); tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0); - tcg_temp_free(t0); } static inline void gen_op_evlhhossplat(DisasContext *ctx, TCGv addr) @@ -696,7 +661,6 @@ static inline void gen_op_evlhhossplat(DisasContext *ctx, TCGv addr) gen_qemu_ld16s(ctx, t0, addr); tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], t0); tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0); - tcg_temp_free(t0); } static inline void gen_op_evlwhe(DisasContext *ctx, TCGv addr) @@ -707,7 +671,6 @@ static inline void gen_op_evlwhe(DisasContext *ctx, TCGv addr) gen_addr_add(ctx, addr, addr, 2); gen_qemu_ld16u(ctx, t0, addr); tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], t0, 16); - tcg_temp_free(t0); } static inline void gen_op_evlwhou(DisasContext *ctx, TCGv addr) @@ -730,7 +693,6 @@ static inline void gen_op_evlwwsplat(DisasContext *ctx, TCGv addr) gen_qemu_ld32u(ctx, t0, addr); tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], t0); tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0); - tcg_temp_free(t0); } static inline void gen_op_evlwhsplat(DisasContext *ctx, TCGv addr) @@ -743,7 +705,6 @@ static inline void gen_op_evlwhsplat(DisasContext *ctx, TCGv addr) gen_qemu_ld16u(ctx, t0, addr); tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], t0, 16); tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gprh[rD(ctx->opcode)], t0); - tcg_temp_free(t0); } static inline void gen_op_evstdd(DisasContext *ctx, TCGv addr) @@ -751,7 +712,6 @@ static inline void gen_op_evstdd(DisasContext *ctx, TCGv addr) TCGv_i64 t0 = tcg_temp_new_i64(); gen_load_gpr64(t0, rS(ctx->opcode)); gen_qemu_st64_i64(ctx, t0, addr); - tcg_temp_free_i64(t0); } static inline void gen_op_evstdw(DisasContext *ctx, TCGv addr) @@ -771,7 +731,6 @@ static inline void gen_op_evstdh(DisasContext *ctx, TCGv addr) gen_addr_add(ctx, addr, addr, 2); tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], 16); gen_qemu_st16(ctx, t0, addr); - tcg_temp_free(t0); gen_addr_add(ctx, addr, addr, 2); gen_qemu_st16(ctx, cpu_gpr[rS(ctx->opcode)], addr); } @@ -784,7 +743,6 @@ static inline void gen_op_evstwhe(DisasContext *ctx, TCGv addr) gen_addr_add(ctx, addr, addr, 2); tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], 16); gen_qemu_st16(ctx, t0, addr); - tcg_temp_free(t0); } static inline void gen_op_evstwho(DisasContext *ctx, TCGv addr) @@ -820,7 +778,6 @@ static void glue(gen_, name)(DisasContext *ctx) \ gen_addr_reg_index(ctx, t0); \ } \ gen_op_##name(ctx, t0); \ - tcg_temp_free(t0); \ } GEN_SPEOP_LDST(evldd, 0x00, 3); @@ -921,9 +878,8 @@ static inline void gen_##name(DisasContext *ctx) \ { \ TCGv_i32 t0 = tcg_temp_new_i32(); \ tcg_gen_trunc_tl_i32(t0, cpu_gpr[rB(ctx->opcode)]); \ - gen_helper_##name(t0, cpu_env, t0); \ + gen_helper_##name(t0, tcg_env, t0); \ tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); \ - tcg_temp_free_i32(t0); \ } #define GEN_SPEFPUOP_CONV_32_64(name) \ static inline void gen_##name(DisasContext *ctx) \ @@ -937,10 +893,8 @@ static inline void gen_##name(DisasContext *ctx) \ t0 = tcg_temp_new_i64(); \ t1 = tcg_temp_new_i32(); \ gen_load_gpr64(t0, rB(ctx->opcode)); \ - gen_helper_##name(t1, cpu_env, t0); \ + gen_helper_##name(t1, tcg_env, t0); \ tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t1); \ - tcg_temp_free_i64(t0); \ - tcg_temp_free_i32(t1); \ } #define GEN_SPEFPUOP_CONV_64_32(name) \ static inline void gen_##name(DisasContext *ctx) \ @@ -954,10 +908,8 @@ static inline void gen_##name(DisasContext *ctx) \ t0 = tcg_temp_new_i64(); \ t1 = tcg_temp_new_i32(); \ tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); \ - gen_helper_##name(t0, cpu_env, t1); \ + gen_helper_##name(t0, tcg_env, t1); \ gen_store_gpr64(rD(ctx->opcode), t0); \ - tcg_temp_free_i64(t0); \ - tcg_temp_free_i32(t1); \ } #define GEN_SPEFPUOP_CONV_64_64(name) \ static inline void gen_##name(DisasContext *ctx) \ @@ -969,9 +921,8 @@ static inline void gen_##name(DisasContext *ctx) \ } \ t0 = tcg_temp_new_i64(); \ gen_load_gpr64(t0, rB(ctx->opcode)); \ - gen_helper_##name(t0, cpu_env, t0); \ + gen_helper_##name(t0, tcg_env, t0); \ gen_store_gpr64(rD(ctx->opcode), t0); \ - tcg_temp_free_i64(t0); \ } #define GEN_SPEFPUOP_ARITH2_32_32(name) \ static inline void gen_##name(DisasContext *ctx) \ @@ -980,11 +931,8 @@ static inline void gen_##name(DisasContext *ctx) \ TCGv_i32 t1 = tcg_temp_new_i32(); \ tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); \ tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); \ - gen_helper_##name(t0, cpu_env, t0, t1); \ + gen_helper_##name(t0, tcg_env, t0, t1); \ tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); \ - \ - tcg_temp_free_i32(t0); \ - tcg_temp_free_i32(t1); \ } #define GEN_SPEFPUOP_ARITH2_64_64(name) \ static inline void gen_##name(DisasContext *ctx) \ @@ -998,10 +946,8 @@ static inline void gen_##name(DisasContext *ctx) \ t1 = tcg_temp_new_i64(); \ gen_load_gpr64(t0, rA(ctx->opcode)); \ gen_load_gpr64(t1, rB(ctx->opcode)); \ - gen_helper_##name(t0, cpu_env, t0, t1); \ + gen_helper_##name(t0, tcg_env, t0, t1); \ gen_store_gpr64(rD(ctx->opcode), t0); \ - tcg_temp_free_i64(t0); \ - tcg_temp_free_i64(t1); \ } #define GEN_SPEFPUOP_COMP_32(name) \ static inline void gen_##name(DisasContext *ctx) \ @@ -1011,10 +957,7 @@ static inline void gen_##name(DisasContext *ctx) \ \ tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); \ tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); \ - gen_helper_##name(cpu_crf[crfD(ctx->opcode)], cpu_env, t0, t1); \ - \ - tcg_temp_free_i32(t0); \ - tcg_temp_free_i32(t1); \ + gen_helper_##name(cpu_crf[crfD(ctx->opcode)], tcg_env, t0, t1); \ } #define GEN_SPEFPUOP_COMP_64(name) \ static inline void gen_##name(DisasContext *ctx) \ @@ -1028,9 +971,7 @@ static inline void gen_##name(DisasContext *ctx) \ t1 = tcg_temp_new_i64(); \ gen_load_gpr64(t0, rA(ctx->opcode)); \ gen_load_gpr64(t1, rB(ctx->opcode)); \ - gen_helper_##name(cpu_crf[crfD(ctx->opcode)], cpu_env, t0, t1); \ - tcg_temp_free_i64(t0); \ - tcg_temp_free_i64(t1); \ + gen_helper_##name(cpu_crf[crfD(ctx->opcode)], tcg_env, t0, t1); \ } /* Single precision floating-point vectors operations */ diff --git a/target/ppc/translate/storage-ctrl-impl.c.inc b/target/ppc/translate/storage-ctrl-impl.c.inc new file mode 100644 index 0000000000..74c23a4191 --- /dev/null +++ b/target/ppc/translate/storage-ctrl-impl.c.inc @@ -0,0 +1,248 @@ +/* + * Power ISA decode for Storage Control instructions + * + * Copyright (c) 2022 Instituto de Pesquisas Eldorado (eldorado.org.br) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 . + */ + +/* + * Store Control Instructions + */ + +#include "mmu-book3s-v3.h" + +static bool trans_SLBIE(DisasContext *ctx, arg_SLBIE *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, SLBI); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_SLBIE(tcg_env, cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_SLBIEG(DisasContext *ctx, arg_SLBIEG *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_SLBIEG(tcg_env, cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_SLBIA(DisasContext *ctx, arg_SLBIA *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, SLBI); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_SLBIA(tcg_env, tcg_constant_i32(a->ih)); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_SLBIAG(DisasContext *ctx, arg_SLBIAG *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_SLBIAG(tcg_env, cpu_gpr[a->rs], tcg_constant_i32(a->l)); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_SLBMTE(DisasContext *ctx, arg_SLBMTE *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, SEGMENT_64B); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_SLBMTE(tcg_env, cpu_gpr[a->rb], cpu_gpr[a->rt]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_SLBMFEV(DisasContext *ctx, arg_SLBMFEV *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, SEGMENT_64B); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_SLBMFEV(cpu_gpr[a->rt], tcg_env, cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_SLBMFEE(DisasContext *ctx, arg_SLBMFEE *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, SEGMENT_64B); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_helper_SLBMFEE(cpu_gpr[a->rt], tcg_env, cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_SLBFEE(DisasContext *ctx, arg_SLBFEE *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, SEGMENT_64B); + +#if defined(CONFIG_USER_ONLY) + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); +#else + +#if defined(TARGET_PPC64) + TCGLabel *l1, *l2; + + if (unlikely(ctx->pr)) { + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + return true; + } + gen_helper_SLBFEE(cpu_gpr[a->rt], tcg_env, + cpu_gpr[a->rb]); + l1 = gen_new_label(); + l2 = gen_new_label(); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[a->rt], -1, l1); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_movi_tl(cpu_gpr[a->rt], 0); + gen_set_label(l2); +#else + qemu_build_not_reached(); +#endif +#endif + return true; +} + +static bool trans_SLBSYNC(DisasContext *ctx, arg_SLBSYNC *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_SV(ctx); + +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + gen_check_tlb_flush(ctx, true); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool do_tlbie(DisasContext *ctx, arg_X_tlbie *a, bool local) +{ +#if defined(CONFIG_USER_ONLY) + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); + return true; +#else + TCGv_i32 t1; + int rb; + + rb = a->rb; + + if ((ctx->insns_flags2 & PPC2_ISA300) == 0) { + /* + * Before Power ISA 3.0, the corresponding bits of RIC, PRS, and R + * (and RS for tlbiel) were reserved fields and should be ignored. + */ + a->ric = 0; + a->prs = false; + a->r = false; + if (local) { + a->rs = 0; + } + } + + if (ctx->pr) { + /* tlbie[l] is privileged... */ + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); + return true; + } else if (!ctx->hv) { + if ((!a->prs && ctx->hr) || (!local && !ctx->gtse)) { + /* + * ... except when PRS=0 and HR=1, or when GTSE=0 for tlbie, + * making it hypervisor privileged. + */ + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); + return true; + } + } + + if (!local && NARROW_MODE(ctx)) { + TCGv t0 = tcg_temp_new(); + tcg_gen_ext32u_tl(t0, cpu_gpr[rb]); + gen_helper_tlbie(tcg_env, t0); + +#if defined(TARGET_PPC64) + /* + * ISA 3.1B says that MSR SF must be 1 when this instruction is executed; + * otherwise the results are undefined. + */ + } else if (a->r) { + gen_helper_tlbie_isa300(tcg_env, cpu_gpr[rb], cpu_gpr[a->rs], + tcg_constant_i32(a->ric << TLBIE_F_RIC_SHIFT | + a->prs << TLBIE_F_PRS_SHIFT | + a->r << TLBIE_F_R_SHIFT | + local << TLBIE_F_LOCAL_SHIFT)); + return true; +#endif + + } else { + gen_helper_tlbie(tcg_env, cpu_gpr[rb]); + } + + if (local) { + return true; + } + + t1 = tcg_temp_new_i32(); + tcg_gen_ld_i32(t1, tcg_env, offsetof(CPUPPCState, tlb_need_flush)); + tcg_gen_ori_i32(t1, t1, TLB_NEED_GLOBAL_FLUSH); + tcg_gen_st_i32(t1, tcg_env, offsetof(CPUPPCState, tlb_need_flush)); + + return true; +#endif +} + +TRANS_FLAGS(MEM_TLBIE, TLBIE, do_tlbie, false) +TRANS_FLAGS(MEM_TLBIE, TLBIEL, do_tlbie, true) diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index d7524c3204..b56e615c24 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -10,7 +10,7 @@ static inline TCGv_ptr gen_avr_ptr(int reg) { TCGv_ptr r = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(r, cpu_env, avr_full_offset(reg)); + tcg_gen_addi_ptr(r, tcg_env, avr_full_offset(reg)); return r; } @@ -45,8 +45,6 @@ static void glue(gen_, name)(DisasContext *ctx) \ gen_qemu_ld64_i64(ctx, avr, EA); \ set_avr64(rD(ctx->opcode), avr, false); \ } \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(avr); \ } #define GEN_VR_STX(name, opc2, opc3) \ @@ -80,8 +78,6 @@ static void gen_st##name(DisasContext *ctx) \ get_avr64(avr, rD(ctx->opcode), false); \ gen_qemu_st64_i64(ctx, avr, EA); \ } \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(avr); \ } #define GEN_VR_LVE(name, opc2, opc3, size) \ @@ -100,9 +96,7 @@ static void gen_lve##name(DisasContext *ctx) \ tcg_gen_andi_tl(EA, EA, ~(size - 1)); \ } \ rs = gen_avr_ptr(rS(ctx->opcode)); \ - gen_helper_lve##name(cpu_env, rs, EA); \ - tcg_temp_free(EA); \ - tcg_temp_free_ptr(rs); \ + gen_helper_lve##name(tcg_env, rs, EA); \ } #define GEN_VR_STVE(name, opc2, opc3, size) \ @@ -121,13 +115,11 @@ static void gen_stve##name(DisasContext *ctx) \ tcg_gen_andi_tl(EA, EA, ~(size - 1)); \ } \ rs = gen_avr_ptr(rS(ctx->opcode)); \ - gen_helper_stve##name(cpu_env, rs, EA); \ - tcg_temp_free(EA); \ - tcg_temp_free_ptr(rs); \ + gen_helper_stve##name(tcg_env, rs, EA); \ } GEN_VR_LDX(lvx, 0x07, 0x03); -/* As we don't emulate the cache, lvxl is stricly equivalent to lvx */ +/* As we don't emulate the cache, lvxl is strictly equivalent to lvx */ GEN_VR_LDX(lvxl, 0x07, 0x0B); GEN_VR_LVE(bx, 0x07, 0x00, 1); @@ -135,7 +127,7 @@ GEN_VR_LVE(hx, 0x07, 0x01, 2); GEN_VR_LVE(wx, 0x07, 0x02, 4); GEN_VR_STX(svx, 0x07, 0x07); -/* As we don't emulate the cache, stvxl is stricly equivalent to stvx */ +/* As we don't emulate the cache, stvxl is strictly equivalent to stvx */ GEN_VR_STX(svxl, 0x07, 0x0F); GEN_VR_STVE(bx, 0x07, 0x04, 1); @@ -154,11 +146,9 @@ static void gen_mfvscr(DisasContext *ctx) tcg_gen_movi_i64(avr, 0); set_avr64(rD(ctx->opcode), avr, true); t = tcg_temp_new_i32(); - gen_helper_mfvscr(t, cpu_env); + gen_helper_mfvscr(t, tcg_env); tcg_gen_extu_i32_i64(avr, t); set_avr64(rD(ctx->opcode), avr, false); - tcg_temp_free_i32(t); - tcg_temp_free_i64(avr); } static void gen_mtvscr(DisasContext *ctx) @@ -177,65 +167,60 @@ static void gen_mtvscr(DisasContext *ctx) bofs += 3 * 4; #endif - tcg_gen_ld_i32(val, cpu_env, bofs); - gen_helper_mtvscr(cpu_env, val); - tcg_temp_free_i32(val); + tcg_gen_ld_i32(val, tcg_env, bofs); + gen_helper_mtvscr(tcg_env, val); +} + +static void gen_vx_vmul10(DisasContext *ctx, bool add_cin, bool ret_carry) +{ + TCGv_i64 t0; + TCGv_i64 t1; + TCGv_i64 t2; + TCGv_i64 avr; + TCGv_i64 ten, z; + + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + avr = tcg_temp_new_i64(); + ten = tcg_constant_i64(10); + z = tcg_constant_i64(0); + + if (add_cin) { + get_avr64(avr, rA(ctx->opcode), false); + tcg_gen_mulu2_i64(t0, t1, avr, ten); + get_avr64(avr, rB(ctx->opcode), false); + tcg_gen_andi_i64(t2, avr, 0xF); + tcg_gen_add2_i64(avr, t2, t0, t1, t2, z); + set_avr64(rD(ctx->opcode), avr, false); + } else { + get_avr64(avr, rA(ctx->opcode), false); + tcg_gen_mulu2_i64(avr, t2, avr, ten); + set_avr64(rD(ctx->opcode), avr, false); + } + + if (ret_carry) { + get_avr64(avr, rA(ctx->opcode), true); + tcg_gen_mulu2_i64(t0, t1, avr, ten); + tcg_gen_add2_i64(t0, avr, t0, t1, t2, z); + set_avr64(rD(ctx->opcode), avr, false); + set_avr64(rD(ctx->opcode), z, true); + } else { + get_avr64(avr, rA(ctx->opcode), true); + tcg_gen_mul_i64(t0, avr, ten); + tcg_gen_add_i64(avr, t0, t2); + set_avr64(rD(ctx->opcode), avr, true); + } } #define GEN_VX_VMUL10(name, add_cin, ret_carry) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - TCGv_i64 t0; \ - TCGv_i64 t1; \ - TCGv_i64 t2; \ - TCGv_i64 avr; \ - TCGv_i64 ten, z; \ - \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - \ - t0 = tcg_temp_new_i64(); \ - t1 = tcg_temp_new_i64(); \ - t2 = tcg_temp_new_i64(); \ - avr = tcg_temp_new_i64(); \ - ten = tcg_const_i64(10); \ - z = tcg_const_i64(0); \ - \ - if (add_cin) { \ - get_avr64(avr, rA(ctx->opcode), false); \ - tcg_gen_mulu2_i64(t0, t1, avr, ten); \ - get_avr64(avr, rB(ctx->opcode), false); \ - tcg_gen_andi_i64(t2, avr, 0xF); \ - tcg_gen_add2_i64(avr, t2, t0, t1, t2, z); \ - set_avr64(rD(ctx->opcode), avr, false); \ - } else { \ - get_avr64(avr, rA(ctx->opcode), false); \ - tcg_gen_mulu2_i64(avr, t2, avr, ten); \ - set_avr64(rD(ctx->opcode), avr, false); \ - } \ - \ - if (ret_carry) { \ - get_avr64(avr, rA(ctx->opcode), true); \ - tcg_gen_mulu2_i64(t0, t1, avr, ten); \ - tcg_gen_add2_i64(t0, avr, t0, t1, t2, z); \ - set_avr64(rD(ctx->opcode), avr, false); \ - set_avr64(rD(ctx->opcode), z, true); \ - } else { \ - get_avr64(avr, rA(ctx->opcode), true); \ - tcg_gen_mul_i64(t0, avr, ten); \ - tcg_gen_add_i64(avr, t0, t2); \ - set_avr64(rD(ctx->opcode), avr, true); \ - } \ - \ - tcg_temp_free_i64(t0); \ - tcg_temp_free_i64(t1); \ - tcg_temp_free_i64(t2); \ - tcg_temp_free_i64(avr); \ - tcg_temp_free_i64(ten); \ - tcg_temp_free_i64(z); \ -} \ + static void glue(gen_, name)(DisasContext *ctx) \ + { gen_vx_vmul10(ctx, add_cin, ret_carry); } GEN_VX_VMUL10(vmul10uq, 0, 0); GEN_VX_VMUL10(vmul10euq, 1, 0); @@ -279,9 +264,6 @@ static void glue(gen_, name)(DisasContext *ctx) \ rb = gen_avr_ptr(rB(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ gen_helper_##name(rd, ra, rb); \ - tcg_temp_free_ptr(ra); \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_ptr(rd); \ } #define GEN_VXFORM_TRANS(name, opc2, opc3) \ @@ -305,10 +287,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ ra = gen_avr_ptr(rA(ctx->opcode)); \ rb = gen_avr_ptr(rB(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ - gen_helper_##name(cpu_env, rd, ra, rb); \ - tcg_temp_free_ptr(ra); \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_ptr(rd); \ + gen_helper_##name(tcg_env, rd, ra, rb); \ } #define GEN_VXFORM3(name, opc2, opc3) \ @@ -324,10 +303,6 @@ static void glue(gen_, name)(DisasContext *ctx) \ rc = gen_avr_ptr(rC(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ gen_helper_##name(rd, ra, rb, rc); \ - tcg_temp_free_ptr(ra); \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_ptr(rc); \ - tcg_temp_free_ptr(rd); \ } /* @@ -400,7 +375,6 @@ static void glue(gen_, name)(DisasContext *ctx) \ } \ rb = gen_avr_ptr(rB(ctx->opcode)); \ gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], rb); \ - tcg_temp_free_ptr(rb); \ } GEN_VXFORM_V(vaddubm, MO_8, tcg_gen_gvec_add, 0, 0); @@ -431,21 +405,6 @@ GEN_VXFORM_V(vminsb, MO_8, tcg_gen_gvec_smin, 1, 12); GEN_VXFORM_V(vminsh, MO_16, tcg_gen_gvec_smin, 1, 13); GEN_VXFORM_V(vminsw, MO_32, tcg_gen_gvec_smin, 1, 14); GEN_VXFORM_V(vminsd, MO_64, tcg_gen_gvec_smin, 1, 15); -GEN_VXFORM(vavgub, 1, 16); -GEN_VXFORM(vabsdub, 1, 16); -GEN_VXFORM_DUAL(vavgub, PPC_ALTIVEC, PPC_NONE, \ - vabsdub, PPC_NONE, PPC2_ISA300) -GEN_VXFORM(vavguh, 1, 17); -GEN_VXFORM(vabsduh, 1, 17); -GEN_VXFORM_DUAL(vavguh, PPC_ALTIVEC, PPC_NONE, \ - vabsduh, PPC_NONE, PPC2_ISA300) -GEN_VXFORM(vavguw, 1, 18); -GEN_VXFORM(vabsduw, 1, 18); -GEN_VXFORM_DUAL(vavguw, PPC_ALTIVEC, PPC_NONE, \ - vabsduw, PPC_NONE, PPC2_ISA300) -GEN_VXFORM(vavgsb, 1, 20); -GEN_VXFORM(vavgsh, 1, 21); -GEN_VXFORM(vavgsw, 1, 22); GEN_VXFORM(vmrghb, 6, 0); GEN_VXFORM(vmrghh, 6, 1); GEN_VXFORM(vmrghw, 6, 2); @@ -472,9 +431,6 @@ static void trans_vmrgew(DisasContext *ctx) get_avr64(avr, VA, false); tcg_gen_deposit_i64(avr, avr, tmp, 0, 32); set_avr64(VT, avr, false); - - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(avr); } static void trans_vmrgow(DisasContext *ctx) @@ -495,10 +451,6 @@ static void trans_vmrgow(DisasContext *ctx) get_avr64(t1, VA, false); tcg_gen_deposit_i64(avr, t0, t1, 32, 32); set_avr64(VT, avr, false); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(avr); } /* @@ -533,10 +485,6 @@ static void trans_lvsl(DisasContext *ctx) */ tcg_gen_addi_i64(result, sh, 0x08090a0b0c0d0e0fULL); set_avr64(VT, result, false); - - tcg_temp_free_i64(result); - tcg_temp_free_i64(sh); - tcg_temp_free(EA); } /* @@ -572,10 +520,6 @@ static void trans_lvsr(DisasContext *ctx) */ tcg_gen_subfi_i64(result, 0x18191a1b1c1d1e1fULL, sh); set_avr64(VT, result, false); - - tcg_temp_free_i64(result); - tcg_temp_free_i64(sh); - tcg_temp_free(EA); } /* @@ -618,11 +562,6 @@ static void trans_vsl(DisasContext *ctx) tcg_gen_shl_i64(avr, avr, sh); tcg_gen_or_i64(avr, avr, carry); set_avr64(VT, avr, true); - - tcg_temp_free_i64(avr); - tcg_temp_free_i64(sh); - tcg_temp_free_i64(carry); - tcg_temp_free_i64(tmp); } /* @@ -664,11 +603,6 @@ static void trans_vsr(DisasContext *ctx) tcg_gen_shr_i64(avr, avr, sh); tcg_gen_or_i64(avr, avr, carry); set_avr64(VT, avr, false); - - tcg_temp_free_i64(avr); - tcg_temp_free_i64(sh); - tcg_temp_free_i64(carry); - tcg_temp_free_i64(tmp); } /* @@ -737,13 +671,6 @@ static void trans_vgbbd(DisasContext *ctx) for (j = 0; j < 2; j++) { set_avr64(VT, result[j], j); } - - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tcg_mask); - tcg_temp_free_i64(result[0]); - tcg_temp_free_i64(result[1]); - tcg_temp_free_i64(avr[0]); - tcg_temp_free_i64(avr[1]); } /* @@ -762,14 +689,12 @@ static void trans_vclzw(DisasContext *ctx) /* Perform count for every word element using tcg_gen_clzi_i32. */ for (i = 0; i < 4; i++) { - tcg_gen_ld_i32(tmp, cpu_env, + tcg_gen_ld_i32(tmp, tcg_env, offsetof(CPUPPCState, vsr[32 + VB].u64[0]) + i * 4); tcg_gen_clzi_i32(tmp, tmp, 32); - tcg_gen_st_i32(tmp, cpu_env, + tcg_gen_st_i32(tmp, tcg_env, offsetof(CPUPPCState, vsr[32 + VT].u64[0]) + i * 4); } - - tcg_temp_free_i32(tmp); } /* @@ -794,8 +719,6 @@ static void trans_vclzd(DisasContext *ctx) get_avr64(avr, VB, false); tcg_gen_clzi_i64(avr, avr, 64); set_avr64(VT, avr, false); - - tcg_temp_free_i64(avr); } GEN_VXFORM_V(vmuluwm, MO_32, tcg_gen_gvec_mul, 4, 2); @@ -803,8 +726,6 @@ GEN_VXFORM(vsrv, 2, 28); GEN_VXFORM(vslv, 2, 29); GEN_VXFORM(vslo, 6, 16); GEN_VXFORM(vsro, 6, 17); -GEN_VXFORM(vaddcuw, 0, 6); -GEN_VXFORM(vsubcuw, 0, 22); static bool do_vector_gvec3_VX(DisasContext *ctx, arg_VX *a, int vece, void (*gen_gvec)(unsigned, uint32_t, uint32_t, @@ -866,9 +787,6 @@ static TCGv_vec do_vrl_mask_vec(unsigned vece, TCGv_vec vrb) /* negate the mask */ tcg_gen_xor_vec(vece, t0, t0, t2); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t2); - return t0; } @@ -887,9 +805,6 @@ static void gen_vrlnm_vec(unsigned vece, TCGv_vec vrt, TCGv_vec vra, /* Rotate and mask */ tcg_gen_rotlv_vec(vece, vrt, vra, n); tcg_gen_and_vec(vece, vrt, vrt, mask); - - tcg_temp_free_vec(n); - tcg_temp_free_vec(mask); } static bool do_vrlnm(DisasContext *ctx, arg_VX *a, int vece) @@ -943,10 +858,6 @@ static void gen_vrlmi_vec(unsigned vece, TCGv_vec vrt, TCGv_vec vra, /* Rotate and insert */ tcg_gen_rotlv_vec(vece, tmp, vra, n); tcg_gen_bitsel_vec(vece, vrt, mask, tmp, vrt); - - tcg_temp_free_vec(n); - tcg_temp_free_vec(tmp); - tcg_temp_free_vec(mask); } static bool do_vrlmi(DisasContext *ctx, arg_VX *a, int vece) @@ -995,7 +906,6 @@ static bool do_vector_shift_quad(DisasContext *ctx, arg_VX *a, bool right, hi = tcg_temp_new_i64(); lo = tcg_temp_new_i64(); t0 = tcg_temp_new_i64(); - t1 = tcg_const_i64(0); get_avr64(lo, a->vra, false); get_avr64(hi, a->vra, true); @@ -1006,7 +916,10 @@ static bool do_vector_shift_quad(DisasContext *ctx, arg_VX *a, bool right, if (right) { tcg_gen_movcond_i64(TCG_COND_NE, lo, t0, zero, hi, lo); if (alg) { + t1 = tcg_temp_new_i64(); tcg_gen_sari_i64(t1, lo, 63); + } else { + t1 = zero; } tcg_gen_movcond_i64(TCG_COND_NE, hi, t0, zero, t1, hi); } else { @@ -1041,13 +954,6 @@ static bool do_vector_shift_quad(DisasContext *ctx, arg_VX *a, bool right, } tcg_gen_or_i64(hi, hi, lo); set_avr64(a->vrt, hi, !right); - - tcg_temp_free_i64(hi); - tcg_temp_free_i64(lo); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(n); - return true; } @@ -1100,11 +1006,6 @@ static void do_vrlq_mask(TCGv_i64 mh, TCGv_i64 ml, TCGv_i64 b, TCGv_i64 e) tcg_gen_xor_i64(mh, mh, t0); tcg_gen_xor_i64(ml, ml, t0); - - tcg_temp_free_i64(th); - tcg_temp_free_i64(tl); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } static bool do_vector_rotl_quad(DisasContext *ctx, arg_VX *a, bool mask, @@ -1166,14 +1067,6 @@ static bool do_vector_rotl_quad(DisasContext *ctx, arg_VX *a, bool mask, set_avr64(a->vrt, t0, true); set_avr64(a->vrt, t1, false); - - tcg_temp_free_i64(ah); - tcg_temp_free_i64(al); - tcg_temp_free_i64(vrb); - tcg_temp_free_i64(n); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - return true; } @@ -1191,7 +1084,6 @@ static void glue(glue(gen_, NAME), _vec)(unsigned vece, TCGv_vec t, \ glue(glue(tcg_gen_, SAT), _vec)(VECE, t, a, b); \ tcg_gen_cmp_vec(TCG_COND_NE, VECE, x, x, t); \ tcg_gen_or_vec(VECE, sat, sat, x); \ - tcg_temp_free_vec(x); \ } \ static void glue(gen_, NAME)(DisasContext *ctx) \ { \ @@ -1234,18 +1126,6 @@ GEN_VXFORM_SAT(vsubuws, MO_32, sub, ussub, 0, 26); GEN_VXFORM_SAT(vsubsbs, MO_8, sub, sssub, 0, 28); GEN_VXFORM_SAT(vsubshs, MO_16, sub, sssub, 0, 29); GEN_VXFORM_SAT(vsubsws, MO_32, sub, sssub, 0, 30); -GEN_VXFORM(vadduqm, 0, 4); -GEN_VXFORM(vaddcuq, 0, 5); -GEN_VXFORM3(vaddeuqm, 30, 0); -GEN_VXFORM3(vaddecuq, 30, 0); -GEN_VXFORM_DUAL(vaddeuqm, PPC_NONE, PPC2_ALTIVEC_207, \ - vaddecuq, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXFORM(vsubuqm, 0, 20); -GEN_VXFORM(vsubcuq, 0, 21); -GEN_VXFORM3(vsubeuqm, 31, 0); -GEN_VXFORM3(vsubecuq, 31, 0); -GEN_VXFORM_DUAL(vsubeuqm, PPC_NONE, PPC2_ALTIVEC_207, \ - vsubecuq, PPC_NONE, PPC2_ALTIVEC_207) GEN_VXFORM_TRANS(vsl, 2, 7); GEN_VXFORM_TRANS(vsr, 2, 11); GEN_VXFORM_ENV(vpkuhum, 7, 0); @@ -1294,10 +1174,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ ra = gen_avr_ptr(rA(ctx->opcode)); \ rb = gen_avr_ptr(rB(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ - gen_helper_##opname(cpu_env, rd, ra, rb); \ - tcg_temp_free_ptr(ra); \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_ptr(rd); \ + gen_helper_##opname(tcg_env, rd, ra, rb); \ } #define GEN_VXRFORM(name, opc2, opc3) \ @@ -1306,7 +1183,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ /* * Support for Altivec instructions that use bit 31 (Rc) as an opcode - * bit but also use bit 21 as an actual Rc bit. In general, thse pairs + * bit but also use bit 21 as an actual Rc bit. In general, these pairs * come from different versions of the ISA, so we must also support a * pair of flags for each instruction. */ @@ -1354,10 +1231,6 @@ static void do_vcmp_rc(int vrt) tcg_gen_or_i64(tmp, set, clr); tcg_gen_extrl_i64_i32(cpu_crf[6], tmp); - - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(set); - tcg_temp_free_i64(clr); } static bool do_vcmp(DisasContext *ctx, arg_VC *a, TCGCond cond, int vece) @@ -1406,9 +1279,6 @@ static void gen_vcmpnez_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) tcg_gen_or_vec(vece, t, t, t0); tcg_gen_or_vec(vece, t, t, t1); - - tcg_temp_free_vec(t0); - tcg_temp_free_vec(t1); } static bool do_vcmpnez(DisasContext *ctx, arg_VC *a, int vece) @@ -1471,8 +1341,7 @@ static bool trans_VCMPEQUQ(DisasContext *ctx, arg_VC *a) tcg_gen_xor_i64(t1, t0, t1); tcg_gen_or_i64(t1, t1, t2); - tcg_gen_setcondi_i64(TCG_COND_EQ, t1, t1, 0); - tcg_gen_neg_i64(t1, t1); + tcg_gen_negsetcond_i64(TCG_COND_EQ, t1, t1, tcg_constant_i64(0)); set_avr64(a->vrt, t1, true); set_avr64(a->vrt, t1, false); @@ -1482,11 +1351,6 @@ static bool trans_VCMPEQUQ(DisasContext *ctx, arg_VC *a) tcg_gen_andi_i32(cpu_crf[6], cpu_crf[6], 0xa); tcg_gen_xori_i32(cpu_crf[6], cpu_crf[6], 0x2); } - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - return true; } @@ -1500,15 +1364,14 @@ static bool do_vcmpgtq(DisasContext *ctx, arg_VC *a, bool sign) get_avr64(t0, a->vra, false); get_avr64(t1, a->vrb, false); - tcg_gen_setcond_i64(TCG_COND_GTU, t2, t0, t1); + tcg_gen_negsetcond_i64(TCG_COND_GTU, t2, t0, t1); get_avr64(t0, a->vra, true); get_avr64(t1, a->vrb, true); tcg_gen_movcond_i64(TCG_COND_EQ, t2, t0, t1, t2, tcg_constant_i64(0)); - tcg_gen_setcond_i64(sign ? TCG_COND_GT : TCG_COND_GTU, t1, t0, t1); + tcg_gen_negsetcond_i64(sign ? TCG_COND_GT : TCG_COND_GTU, t1, t0, t1); tcg_gen_or_i64(t1, t1, t2); - tcg_gen_neg_i64(t1, t1); set_avr64(a->vrt, t1, true); set_avr64(a->vrt, t1, false); @@ -1518,11 +1381,6 @@ static bool do_vcmpgtq(DisasContext *ctx, arg_VC *a, bool sign) tcg_gen_andi_i32(cpu_crf[6], cpu_crf[6], 0xa); tcg_gen_xori_i32(cpu_crf[6], cpu_crf[6], 0x2); } - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - return true; } @@ -1537,8 +1395,8 @@ static bool do_vcmpq(DisasContext *ctx, arg_VX_bf *a, bool sign) REQUIRE_INSNS_FLAGS2(ctx, ISA310); REQUIRE_VECTOR(ctx); - vra = tcg_temp_local_new_i64(); - vrb = tcg_temp_local_new_i64(); + vra = tcg_temp_new_i64(); + vrb = tcg_temp_new_i64(); gt = gen_new_label(); lt = gen_new_label(); done = gen_new_label(); @@ -1565,9 +1423,6 @@ static bool do_vcmpq(DisasContext *ctx, arg_VX_bf *a, bool sign) tcg_gen_br(done); gen_set_label(done); - tcg_temp_free_i64(vra); - tcg_temp_free_i64(vrb); - return true; } @@ -1610,8 +1465,6 @@ static void glue(gen_, name)(DisasContext *ctx) \ rb = gen_avr_ptr(rB(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ gen_helper_##name(rd, rb); \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_ptr(rd); \ } #define GEN_VXFORM_NOA_ENV(name, opc2, opc3) \ @@ -1625,9 +1478,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ } \ rb = gen_avr_ptr(rB(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ - gen_helper_##name(cpu_env, rd, rb); \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_ptr(rd); \ + gen_helper_##name(tcg_env, rd, rb); \ } #define GEN_VXFORM_NOA_2(name, opc2, opc3, opc4) \ @@ -1641,8 +1492,6 @@ static void glue(gen_, name)(DisasContext *ctx) \ rb = gen_avr_ptr(rB(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ gen_helper_##name(rd, rb); \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_ptr(rd); \ } #define GEN_VXFORM_NOA_3(name, opc2, opc3, opc4) \ @@ -1655,7 +1504,6 @@ static void glue(gen_, name)(DisasContext *ctx) \ } \ rb = gen_avr_ptr(rB(ctx->opcode)); \ gen_helper_##name(cpu_gpr[rD(ctx->opcode)], rb); \ - tcg_temp_free_ptr(rb); \ } GEN_VXFORM_NOA(vupkhsb, 7, 8); GEN_VXFORM_NOA(vupkhsh, 7, 9); @@ -1673,9 +1521,70 @@ GEN_VXFORM_NOA_ENV(vrfim, 5, 11); GEN_VXFORM_NOA_ENV(vrfin, 5, 8); GEN_VXFORM_NOA_ENV(vrfip, 5, 10); GEN_VXFORM_NOA_ENV(vrfiz, 5, 9); -GEN_VXFORM_NOA(vprtybw, 1, 24); -GEN_VXFORM_NOA(vprtybd, 1, 24); -GEN_VXFORM_NOA(vprtybq, 1, 24); + +static void gen_vprtyb_vec(unsigned vece, TCGv_vec t, TCGv_vec b) +{ + int i; + TCGv_vec tmp = tcg_temp_new_vec_matching(b); + /* MO_32 is 2, so 2 iterations for MO_32 and 3 for MO_64 */ + for (i = 0; i < vece; i++) { + tcg_gen_shri_vec(vece, tmp, b, (4 << (vece - i))); + tcg_gen_xor_vec(vece, b, tmp, b); + } + tcg_gen_and_vec(vece, t, b, tcg_constant_vec_matching(t, vece, 1)); +} + +/* vprtybw */ +static void gen_vprtyb_i32(TCGv_i32 t, TCGv_i32 b) +{ + tcg_gen_ctpop_i32(t, b); + tcg_gen_and_i32(t, t, tcg_constant_i32(1)); +} + +/* vprtybd */ +static void gen_vprtyb_i64(TCGv_i64 t, TCGv_i64 b) +{ + tcg_gen_ctpop_i64(t, b); + tcg_gen_and_i64(t, t, tcg_constant_i64(1)); +} + +static bool do_vx_vprtyb(DisasContext *ctx, arg_VX_tb *a, unsigned vece) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, 0 + }; + + static const GVecGen2 op[] = { + { + .fniv = gen_vprtyb_vec, + .fni4 = gen_vprtyb_i32, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_vprtyb_vec, + .fni8 = gen_vprtyb_i64, + .opt_opc = vecop_list, + .vece = MO_64 + }, + { + .fno = gen_helper_VPRTYBQ, + .vece = MO_128 + }, + }; + + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_2(avr_full_offset(a->vrt), avr_full_offset(a->vrb), + 16, 16, &op[vece - MO_32]); + + return true; +} + +TRANS(VPRTYBW, do_vx_vprtyb, MO_32) +TRANS(VPRTYBD, do_vx_vprtyb, MO_64) +TRANS(VPRTYBQ, do_vx_vprtyb, MO_128) static void gen_vsplt(DisasContext *ctx, int vece) { @@ -1713,13 +1622,10 @@ static void glue(gen_, name)(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_VPU); \ return; \ } \ - uimm = tcg_const_i32(UIMM5(ctx->opcode)); \ + uimm = tcg_constant_i32(UIMM5(ctx->opcode)); \ rb = gen_avr_ptr(rB(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ - gen_helper_##name(cpu_env, rd, rb, uimm); \ - tcg_temp_free_i32(uimm); \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_ptr(rd); \ + gen_helper_##name(tcg_env, rd, rb, uimm); \ } #define GEN_VXFORM_UIMM_SPLAT(name, opc2, opc3, splat_max) \ @@ -1740,9 +1646,6 @@ static void glue(gen_, name)(DisasContext *ctx) \ rb = gen_avr_ptr(rB(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ gen_helper_##name(rd, rb, t0); \ - tcg_temp_free_i32(t0); \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_ptr(rd); \ } GEN_VXFORM_VSPLT(vspltb, MO_8, 6, 8); @@ -1889,12 +1792,6 @@ static bool trans_VGNB(DisasContext *ctx, arg_VX_n *a) tcg_gen_shri_i64(lo, lo, nbits); tcg_gen_or_i64(hi, hi, lo); tcg_gen_trunc_i64_tl(cpu_gpr[a->rt], hi); - - tcg_temp_free_i64(hi); - tcg_temp_free_i64(lo); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - return true; } @@ -1916,12 +1813,7 @@ static bool do_vextdx(DisasContext *ctx, arg_VA *a, int size, bool right, if (right) { tcg_gen_subfi_tl(rc, 32 - size, rc); } - gen_helper(cpu_env, vrt, vra, vrb, rc); - - tcg_temp_free_ptr(vrt); - tcg_temp_free_ptr(vra); - tcg_temp_free_ptr(vrb); - tcg_temp_free(rc); + gen_helper(tcg_env, vrt, vra, vrb, rc); return true; } @@ -1949,32 +1841,23 @@ static bool do_vinsx(DisasContext *ctx, int vrt, int size, bool right, TCGv ra, tcg_gen_subfi_tl(idx, 16 - size, idx); } - gen_helper(cpu_env, t, rb, idx); - - tcg_temp_free_ptr(t); - tcg_temp_free(idx); - + gen_helper(tcg_env, t, rb, idx); return true; } static bool do_vinsvx(DisasContext *ctx, int vrt, int size, bool right, TCGv ra, int vrb, void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv)) { - bool ok; TCGv_i64 val; val = tcg_temp_new_i64(); get_avr64(val, vrb, true); - ok = do_vinsx(ctx, vrt, size, right, ra, val, gen_helper); - - tcg_temp_free_i64(val); - return ok; + return do_vinsx(ctx, vrt, size, right, ra, val, gen_helper); } static bool do_vinsx_VX(DisasContext *ctx, arg_VX *a, int size, bool right, void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv)) { - bool ok; TCGv_i64 val; REQUIRE_INSNS_FLAGS2(ctx, ISA310); @@ -1983,10 +1866,7 @@ static bool do_vinsx_VX(DisasContext *ctx, arg_VX *a, int size, bool right, val = tcg_temp_new_i64(); tcg_gen_extu_tl_i64(val, cpu_gpr[a->vrb]); - ok = do_vinsx(ctx, a->vrt, size, right, cpu_gpr[a->vra], val, gen_helper); - - tcg_temp_free_i64(val); - return ok; + return do_vinsx(ctx, a->vrt, size, right, cpu_gpr[a->vra], val, gen_helper); } static bool do_vinsvx_VX(DisasContext *ctx, arg_VX *a, int size, bool right, @@ -2002,7 +1882,6 @@ static bool do_vinsvx_VX(DisasContext *ctx, arg_VX *a, int size, bool right, static bool do_vins_VX_uim4(DisasContext *ctx, arg_VX_uim4 *a, int size, void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv)) { - bool ok; TCGv_i64 val; REQUIRE_INSNS_FLAGS2(ctx, ISA310); @@ -2026,11 +1905,8 @@ static bool do_vins_VX_uim4(DisasContext *ctx, arg_VX_uim4 *a, int size, val = tcg_temp_new_i64(); tcg_gen_extu_tl_i64(val, cpu_gpr[a->vrb]); - ok = do_vinsx(ctx, a->vrt, size, false, tcg_constant_tl(a->uim), val, - gen_helper); - - tcg_temp_free_i64(val); - return ok; + return do_vinsx(ctx, a->vrt, size, false, tcg_constant_tl(a->uim), val, + gen_helper); } static bool do_vinsert_VX_uim4(DisasContext *ctx, arg_VX_uim4 *a, int size, @@ -2087,12 +1963,8 @@ static void gen_vsldoi(DisasContext *ctx) ra = gen_avr_ptr(rA(ctx->opcode)); rb = gen_avr_ptr(rB(ctx->opcode)); rd = gen_avr_ptr(rD(ctx->opcode)); - sh = tcg_const_i32(VSH(ctx->opcode)); + sh = tcg_constant_i32(VSH(ctx->opcode)); gen_helper_vsldoi(rd, ra, rb, sh); - tcg_temp_free_ptr(ra); - tcg_temp_free_ptr(rb); - tcg_temp_free_ptr(rd); - tcg_temp_free_i32(sh); } static bool trans_VSLDBI(DisasContext *ctx, arg_VN *a) @@ -2115,16 +1987,10 @@ static bool trans_VSLDBI(DisasContext *ctx, arg_VN *a) tcg_gen_extract2_i64(t0, t1, t0, 64 - a->sh); tcg_gen_extract2_i64(t1, t2, t1, 64 - a->sh); - - tcg_temp_free_i64(t2); } set_avr64(a->vrt, t0, true); set_avr64(a->vrt, t1, false); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - return true; } @@ -2148,16 +2014,10 @@ static bool trans_VSRDBI(DisasContext *ctx, arg_VN *a) tcg_gen_extract2_i64(t0, t0, t1, a->sh); tcg_gen_extract2_i64(t1, t1, t2, a->sh); - - tcg_temp_free_i64(t2); } set_avr64(a->vrt, t0, false); set_avr64(a->vrt, t1, true); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - return true; } @@ -2190,15 +2050,13 @@ static bool trans_VEXPANDQM(DisasContext *ctx, arg_VX_tb *a) tcg_gen_sari_i64(tmp, tmp, 63); set_avr64(a->vrt, tmp, false); set_avr64(a->vrt, tmp, true); - - tcg_temp_free_i64(tmp); return true; } static bool do_vextractm(DisasContext *ctx, arg_VX_tb *a, unsigned vece) { const uint64_t elem_width = 8 << vece, elem_count_half = 8 >> vece, - mask = dup_const(vece, 1 << (elem_width - 1)); + mask = dup_const(vece, 1ULL << (elem_width - 1)); uint64_t i, j; TCGv_i64 lo, hi, t0, t1; @@ -2245,12 +2103,6 @@ static bool do_vextractm(DisasContext *ctx, arg_VX_tb *a, unsigned vece) tcg_gen_shri_i64(hi, hi, 64 - elem_count_half); tcg_gen_extract2_i64(lo, lo, hi, 64 - elem_count_half); tcg_gen_trunc_i64_tl(cpu_gpr[a->vrt], lo); - - tcg_temp_free_i64(hi); - tcg_temp_free_i64(lo); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - return true; } @@ -2271,9 +2123,6 @@ static bool trans_VEXTRACTQM(DisasContext *ctx, arg_VX_tb *a) get_avr64(tmp, a->vrb, true); tcg_gen_shri_i64(tmp, tmp, 63); tcg_gen_trunc_i64_tl(cpu_gpr[a->vrt], tmp); - - tcg_temp_free_i64(tmp); - return true; } @@ -2334,12 +2183,6 @@ static bool do_mtvsrm(DisasContext *ctx, arg_VX_tb *a, unsigned vece) set_avr64(a->vrt, lo, false); set_avr64(a->vrt, hi, true); - - tcg_temp_free_i64(hi); - tcg_temp_free_i64(lo); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - return true; } @@ -2361,9 +2204,6 @@ static bool trans_MTVSRQM(DisasContext *ctx, arg_VX_tb *a) tcg_gen_sextract_i64(tmp, tmp, 0, 1); set_avr64(a->vrt, tmp, false); set_avr64(a->vrt, tmp, true); - - tcg_temp_free_i64(tmp); - return true; } @@ -2394,28 +2234,25 @@ static bool trans_MTVSRBMI(DisasContext *ctx, arg_DX_b *a) static bool do_vcntmb(DisasContext *ctx, arg_VX_mp *a, int vece) { - TCGv_i64 rt, vrb, mask; - rt = tcg_const_i64(0); - vrb = tcg_temp_new_i64(); + TCGv_i64 r[2], mask; + + r[0] = tcg_temp_new_i64(); + r[1] = tcg_temp_new_i64(); mask = tcg_constant_i64(dup_const(vece, 1ULL << ((8 << vece) - 1))); for (int i = 0; i < 2; i++) { - get_avr64(vrb, a->vrb, i); + get_avr64(r[i], a->vrb, i); if (a->mp) { - tcg_gen_and_i64(vrb, mask, vrb); + tcg_gen_and_i64(r[i], mask, r[i]); } else { - tcg_gen_andc_i64(vrb, mask, vrb); + tcg_gen_andc_i64(r[i], mask, r[i]); } - tcg_gen_ctpop_i64(vrb, vrb); - tcg_gen_add_i64(rt, rt, vrb); + tcg_gen_ctpop_i64(r[i], r[i]); } - tcg_gen_shli_i64(rt, rt, TARGET_LONG_BITS - 8 + vece); - tcg_gen_trunc_i64_tl(cpu_gpr[a->rt], rt); - - tcg_temp_free_i64(vrb); - tcg_temp_free_i64(rt); - + tcg_gen_add_i64(r[0], r[0], r[1]); + tcg_gen_shli_i64(r[0], r[0], TARGET_LONG_BITS - 8 + vece); + tcg_gen_trunc_i64_tl(cpu_gpr[a->rt], r[0]); return true; } @@ -2440,12 +2277,7 @@ static bool do_vstri(DisasContext *ctx, arg_VX_tb_rc *a, } else { TCGv_i32 discard = tcg_temp_new_i32(); gen_helper(discard, vrt, vrb); - tcg_temp_free_i32(discard); } - - tcg_temp_free_ptr(vrt); - tcg_temp_free_ptr(vrb); - return true; } @@ -2498,12 +2330,6 @@ static bool do_vclrb(DisasContext *ctx, arg_VX *a, bool right) get_avr64(tmp, a->vra, false); tcg_gen_and_i64(tmp, tmp, ml); set_avr64(a->vrt, tmp, false); - - tcg_temp_free_i64(rb); - tcg_temp_free_i64(mh); - tcg_temp_free_i64(ml); - tcg_temp_free_i64(tmp); - return true; } @@ -2523,35 +2349,13 @@ static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ rc = gen_avr_ptr(rC(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ if (Rc(ctx->opcode)) { \ - gen_helper_##name1(cpu_env, rd, ra, rb, rc); \ + gen_helper_##name1(tcg_env, rd, ra, rb, rc); \ } else { \ - gen_helper_##name0(cpu_env, rd, ra, rb, rc); \ + gen_helper_##name0(tcg_env, rd, ra, rb, rc); \ } \ - tcg_temp_free_ptr(ra); \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_ptr(rc); \ - tcg_temp_free_ptr(rd); \ } -GEN_VAFORM_PAIRED(vmhaddshs, vmhraddshs, 16) - -static void gen_vmladduhm(DisasContext *ctx) -{ - TCGv_ptr ra, rb, rc, rd; - if (unlikely(!ctx->altivec_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VPU); - return; - } - ra = gen_avr_ptr(rA(ctx->opcode)); - rb = gen_avr_ptr(rB(ctx->opcode)); - rc = gen_avr_ptr(rC(ctx->opcode)); - rd = gen_avr_ptr(rD(ctx->opcode)); - gen_helper_vmladduhm(rd, ra, rb, rc); - tcg_temp_free_ptr(ra); - tcg_temp_free_ptr(rb); - tcg_temp_free_ptr(rc); - tcg_temp_free_ptr(rd); -} +GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23) static bool do_va_helper(DisasContext *ctx, arg_VA *a, void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) @@ -2564,17 +2368,48 @@ static bool do_va_helper(DisasContext *ctx, arg_VA *a, vrb = gen_avr_ptr(a->vrb); vrc = gen_avr_ptr(a->rc); gen_helper(vrt, vra, vrb, vrc); - tcg_temp_free_ptr(vrt); - tcg_temp_free_ptr(vra); - tcg_temp_free_ptr(vrb); - tcg_temp_free_ptr(vrc); - return true; } +TRANS_FLAGS2(ALTIVEC_207, VADDECUQ, do_va_helper, gen_helper_VADDECUQ) +TRANS_FLAGS2(ALTIVEC_207, VADDEUQM, do_va_helper, gen_helper_VADDEUQM) + +TRANS_FLAGS2(ALTIVEC_207, VSUBEUQM, do_va_helper, gen_helper_VSUBEUQM) +TRANS_FLAGS2(ALTIVEC_207, VSUBECUQ, do_va_helper, gen_helper_VSUBECUQ) + TRANS_FLAGS(ALTIVEC, VPERM, do_va_helper, gen_helper_VPERM) TRANS_FLAGS2(ISA300, VPERMR, do_va_helper, gen_helper_VPERMR) +static void gen_vmladduhm_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, + TCGv_vec c) +{ + tcg_gen_mul_vec(vece, t, a, b); + tcg_gen_add_vec(vece, t, t, c); +} + +static bool trans_VMLADDUHM(DisasContext *ctx, arg_VA *a) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_add_vec, INDEX_op_mul_vec, 0 + }; + + static const GVecGen4 op = { + .fno = gen_helper_VMLADDUHM, + .fniv = gen_vmladduhm_vec, + .opt_opc = vecop_list, + .vece = MO_16 + }; + + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_4(avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), avr_full_offset(a->rc), + 16, 16, &op); + + return true; +} + static bool trans_VSEL(DisasContext *ctx, arg_VA *a) { REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); @@ -2602,26 +2437,33 @@ static bool do_va_env_helper(DisasContext *ctx, arg_VA *a, vra = gen_avr_ptr(a->vra); vrb = gen_avr_ptr(a->vrb); vrc = gen_avr_ptr(a->rc); - gen_helper(cpu_env, vrt, vra, vrb, vrc); - tcg_temp_free_ptr(vrt); - tcg_temp_free_ptr(vra); - tcg_temp_free_ptr(vrb); - tcg_temp_free_ptr(vrc); - + gen_helper(tcg_env, vrt, vra, vrb, vrc); return true; } TRANS_FLAGS(ALTIVEC, VMSUMUHS, do_va_env_helper, gen_helper_VMSUMUHS) TRANS_FLAGS(ALTIVEC, VMSUMSHS, do_va_env_helper, gen_helper_VMSUMSHS) -GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23) +TRANS_FLAGS(ALTIVEC, VMHADDSHS, do_va_env_helper, gen_helper_VMHADDSHS) +TRANS_FLAGS(ALTIVEC, VMHRADDSHS, do_va_env_helper, gen_helper_VMHRADDSHS) GEN_VXFORM_NOA(vclzb, 1, 28) GEN_VXFORM_NOA(vclzh, 1, 29) GEN_VXFORM_TRANS(vclzw, 1, 30) GEN_VXFORM_TRANS(vclzd, 1, 31) -GEN_VXFORM_NOA_2(vnegw, 1, 24, 6) -GEN_VXFORM_NOA_2(vnegd, 1, 24, 7) + +static bool do_vneg(DisasContext *ctx, arg_VX_tb *a, unsigned vece) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_neg(vece, avr_full_offset(a->vrt), avr_full_offset(a->vrb), + 16, 16); + return true; +} + +TRANS(VNEGW, do_vneg, MO_32) +TRANS(VNEGD, do_vneg, MO_64) static void gen_vexts_i64(TCGv_i64 t, TCGv_i64 b, int64_t s) { @@ -2688,8 +2530,6 @@ static bool trans_VEXTSD2Q(DisasContext *ctx, arg_VX_tb *a) set_avr64(a->vrt, tmp, false); tcg_gen_sari_i64(tmp, tmp, 63); set_avr64(a->vrt, tmp, true); - - tcg_temp_free_i64(tmp); return true; } @@ -2717,7 +2557,6 @@ GEN_VXFORM_TRANS(vgbbd, 6, 20); GEN_VXFORM(vpmsumb, 4, 16) GEN_VXFORM(vpmsumh, 4, 17) GEN_VXFORM(vpmsumw, 4, 18) -GEN_VXFORM(vpmsumd, 4, 19) #define GEN_BCD(op) \ static void gen_##op(DisasContext *ctx) \ @@ -2734,14 +2573,9 @@ static void gen_##op(DisasContext *ctx) \ rb = gen_avr_ptr(rB(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ \ - ps = tcg_const_i32((ctx->opcode & 0x200) != 0); \ + ps = tcg_constant_i32((ctx->opcode & 0x200) != 0); \ \ gen_helper_##op(cpu_crf[6], rd, ra, rb, ps); \ - \ - tcg_temp_free_ptr(ra); \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_ptr(rd); \ - tcg_temp_free_i32(ps); \ } #define GEN_BCD2(op) \ @@ -2758,13 +2592,9 @@ static void gen_##op(DisasContext *ctx) \ rb = gen_avr_ptr(rB(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ \ - ps = tcg_const_i32((ctx->opcode & 0x200) != 0); \ + ps = tcg_constant_i32((ctx->opcode & 0x200) != 0); \ \ gen_helper_##op(cpu_crf[6], rd, rb, ps); \ - \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_ptr(rd); \ - tcg_temp_free_i32(ps); \ } GEN_BCD(bcdadd) @@ -2841,8 +2671,6 @@ static void gen_xpnd04_2(DisasContext *ctx) } -GEN_VXFORM_DUAL(vsubcuw, PPC_ALTIVEC, PPC_NONE, \ - xpnd04_1, PPC_NONE, PPC2_ISA300) GEN_VXFORM_DUAL(vsubsws, PPC_ALTIVEC, PPC_NONE, \ xpnd04_2, PPC_NONE, PPC2_ISA300) @@ -2862,11 +2690,6 @@ GEN_VXFORM_DUAL(vsubuwm, PPC_ALTIVEC, PPC_NONE, \ bcdus, PPC_NONE, PPC2_ISA300) GEN_VXFORM_DUAL(vsubsbs, PPC_ALTIVEC, PPC_NONE, \ bcdtrunc, PPC_NONE, PPC2_ISA300) -GEN_VXFORM_DUAL(vsubuqm, PPC2_ALTIVEC_207, PPC_NONE, \ - bcdtrunc, PPC_NONE, PPC2_ISA300) -GEN_VXFORM_DUAL(vsubcuq, PPC2_ALTIVEC_207, PPC_NONE, \ - bcdutrunc, PPC_NONE, PPC2_ISA300) - static void gen_vsbox(DisasContext *ctx) { @@ -2878,8 +2701,6 @@ static void gen_vsbox(DisasContext *ctx) ra = gen_avr_ptr(rA(ctx->opcode)); rd = gen_avr_ptr(rD(ctx->opcode)); gen_helper_vsbox(rd, ra); - tcg_temp_free_ptr(ra); - tcg_temp_free_ptr(rd); } GEN_VXFORM(vcipher, 4, 20) @@ -2903,11 +2724,8 @@ static void gen_##op(DisasContext *ctx) \ } \ ra = gen_avr_ptr(rA(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ - st_six = tcg_const_i32(rB(ctx->opcode)); \ + st_six = tcg_constant_i32(rB(ctx->opcode)); \ gen_helper_##op(rd, ra, st_six); \ - tcg_temp_free_ptr(ra); \ - tcg_temp_free_ptr(rd); \ - tcg_temp_free_i32(st_six); \ } VSHASIGMA(vshasigmaw) @@ -3022,12 +2840,6 @@ static bool trans_VMSUMUDM(DisasContext *ctx, arg_VA *a) set_avr64(a->vrt, rl, false); set_avr64(a->vrt, rh, true); - - tcg_temp_free_i64(rl); - tcg_temp_free_i64(rh); - tcg_temp_free_i64(src1); - tcg_temp_free_i64(src2); - return true; } @@ -3073,14 +2885,6 @@ static bool trans_VMSUMCUD(DisasContext *ctx, arg_VA *a) /* Discard 64 more bits to complete the CHOP128(temp >> 128) */ set_avr64(a->vrt, tmp0, false); set_avr64(a->vrt, zero, true); - - tcg_temp_free_i64(tmp0); - tcg_temp_free_i64(tmp1); - tcg_temp_free_i64(prod1h); - tcg_temp_free_i64(prod1l); - tcg_temp_free_i64(prod0h); - tcg_temp_free_i64(prod0l); - return true; } @@ -3094,13 +2898,74 @@ static bool do_vx_helper(DisasContext *ctx, arg_VX *a, rb = gen_avr_ptr(a->vrb); rd = gen_avr_ptr(a->vrt); gen_helper(rd, ra, rb); - tcg_temp_free_ptr(ra); - tcg_temp_free_ptr(rb); - tcg_temp_free_ptr(rd); + return true; +} + +TRANS_FLAGS2(ALTIVEC_207, VADDCUQ, do_vx_helper, gen_helper_VADDCUQ) +TRANS_FLAGS2(ALTIVEC_207, VADDUQM, do_vx_helper, gen_helper_VADDUQM) + +TRANS_FLAGS2(ALTIVEC_207, VPMSUMD, do_vx_helper, gen_helper_VPMSUMD) + +TRANS_FLAGS2(ALTIVEC_207, VSUBCUQ, do_vx_helper, gen_helper_VSUBCUQ) +TRANS_FLAGS2(ALTIVEC_207, VSUBUQM, do_vx_helper, gen_helper_VSUBUQM) + +static void gen_VADDCUW_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_not_vec(vece, a, a); + tcg_gen_cmp_vec(TCG_COND_LTU, vece, t, a, b); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(t, vece, 1)); +} + +static void gen_VADDCUW_i32(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_not_i32(a, a); + tcg_gen_setcond_i32(TCG_COND_LTU, t, a, b); +} + +static void gen_VSUBCUW_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_cmp_vec(TCG_COND_GEU, vece, t, a, b); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(t, vece, 1)); +} + +static void gen_VSUBCUW_i32(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_setcond_i32(TCG_COND_GEU, t, a, b); +} + +static bool do_vx_vaddsubcuw(DisasContext *ctx, arg_VX *a, int add) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_cmp_vec, 0 + }; + + static const GVecGen3 op[] = { + { + .fniv = gen_VSUBCUW_vec, + .fni4 = gen_VSUBCUW_i32, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fniv = gen_VADDCUW_vec, + .fni4 = gen_VADDCUW_i32, + .opt_opc = vecop_list, + .vece = MO_32 + }, + }; + + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16, &op[add]); return true; } +TRANS(VSUBCUW, do_vx_vaddsubcuw, 0) +TRANS(VADDCUW, do_vx_vaddsubcuw, 1) + static bool do_vx_vmuleo(DisasContext *ctx, arg_VX *a, bool even, void (*gen_mul)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) { @@ -3117,12 +2982,6 @@ static bool do_vx_vmuleo(DisasContext *ctx, arg_VX *a, bool even, gen_mul(vrt0, vrt1, vra, vrb); set_avr64(a->vrt, vrt0, false); set_avr64(a->vrt, vrt1, true); - - tcg_temp_free_i64(vra); - tcg_temp_free_i64(vrb); - tcg_temp_free_i64(vrt0); - tcg_temp_free_i64(vrt1); - return true; } @@ -3182,10 +3041,6 @@ static void do_vx_vmulhw_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b, bool sign) tcg_gen_shri_i64(lh, lh, 32); tcg_gen_deposit_i64(t, hh, lh, 0, 32); - - tcg_temp_free_i64(hh); - tcg_temp_free_i64(lh); - tcg_temp_free_i64(temp); } static void do_vx_vmulhd_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b, bool sign) @@ -3198,8 +3053,6 @@ static void do_vx_vmulhd_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b, bool sign) } else { tcg_gen_mulu2_i64(tlow, t, a, b); } - - tcg_temp_free_i64(tlow); } static bool do_vx_mulh(DisasContext *ctx, arg_VX *a, bool sign, @@ -3224,13 +3077,7 @@ static bool do_vx_mulh(DisasContext *ctx, arg_VX *a, bool sign, set_avr64(a->vrt, vrt, i); } - - tcg_temp_free_i64(vra); - tcg_temp_free_i64(vrb); - tcg_temp_free_i64(vrt); - return true; - } TRANS(VMULHSW, do_vx_mulh, true , do_vx_vmulhw_i64) @@ -3238,6 +3085,286 @@ TRANS(VMULHSD, do_vx_mulh, true , do_vx_vmulhd_i64) TRANS(VMULHUW, do_vx_mulh, false, do_vx_vmulhw_i64) TRANS(VMULHUD, do_vx_mulh, false, do_vx_vmulhd_i64) +static void do_vavg(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, + void (*gen_shr_vec)(unsigned, TCGv_vec, TCGv_vec, int64_t)) +{ + TCGv_vec tmp = tcg_temp_new_vec_matching(t); + tcg_gen_or_vec(vece, tmp, a, b); + tcg_gen_and_vec(vece, tmp, tmp, tcg_constant_vec_matching(t, vece, 1)); + gen_shr_vec(vece, a, a, 1); + gen_shr_vec(vece, b, b, 1); + tcg_gen_add_vec(vece, t, a, b); + tcg_gen_add_vec(vece, t, t, tmp); +} + +QEMU_FLATTEN +static void gen_vavgu(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + do_vavg(vece, t, a, b, tcg_gen_shri_vec); +} + +QEMU_FLATTEN +static void gen_vavgs(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + do_vavg(vece, t, a, b, tcg_gen_sari_vec); +} + +static bool do_vx_vavg(DisasContext *ctx, arg_VX *a, int sign, int vece) +{ + static const TCGOpcode vecop_list_s[] = { + INDEX_op_add_vec, INDEX_op_sari_vec, 0 + }; + static const TCGOpcode vecop_list_u[] = { + INDEX_op_add_vec, INDEX_op_shri_vec, 0 + }; + + static const GVecGen3 op[2][3] = { + { + { + .fniv = gen_vavgu, + .fno = gen_helper_VAVGUB, + .opt_opc = vecop_list_u, + .vece = MO_8 + }, + { + .fniv = gen_vavgu, + .fno = gen_helper_VAVGUH, + .opt_opc = vecop_list_u, + .vece = MO_16 + }, + { + .fniv = gen_vavgu, + .fno = gen_helper_VAVGUW, + .opt_opc = vecop_list_u, + .vece = MO_32 + }, + }, + { + { + .fniv = gen_vavgs, + .fno = gen_helper_VAVGSB, + .opt_opc = vecop_list_s, + .vece = MO_8 + }, + { + .fniv = gen_vavgs, + .fno = gen_helper_VAVGSH, + .opt_opc = vecop_list_s, + .vece = MO_16 + }, + { + .fniv = gen_vavgs, + .fno = gen_helper_VAVGSW, + .opt_opc = vecop_list_s, + .vece = MO_32 + }, + }, + }; + + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16, &op[sign][vece]); + + + return true; +} + + +TRANS_FLAGS(ALTIVEC, VAVGSB, do_vx_vavg, 1, MO_8) +TRANS_FLAGS(ALTIVEC, VAVGSH, do_vx_vavg, 1, MO_16) +TRANS_FLAGS(ALTIVEC, VAVGSW, do_vx_vavg, 1, MO_32) +TRANS_FLAGS(ALTIVEC, VAVGUB, do_vx_vavg, 0, MO_8) +TRANS_FLAGS(ALTIVEC, VAVGUH, do_vx_vavg, 0, MO_16) +TRANS_FLAGS(ALTIVEC, VAVGUW, do_vx_vavg, 0, MO_32) + +static void gen_vabsdu(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_umax_vec(vece, t, a, b); + tcg_gen_umin_vec(vece, a, a, b); + tcg_gen_sub_vec(vece, t, t, a); +} + +static bool do_vabsdu(DisasContext *ctx, arg_VX *a, const int vece) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_umax_vec, INDEX_op_umin_vec, INDEX_op_sub_vec, 0 + }; + + static const GVecGen3 op[] = { + { + .fniv = gen_vabsdu, + .fno = gen_helper_VABSDUB, + .opt_opc = vecop_list, + .vece = MO_8 + }, + { + .fniv = gen_vabsdu, + .fno = gen_helper_VABSDUH, + .opt_opc = vecop_list, + .vece = MO_16 + }, + { + .fniv = gen_vabsdu, + .fno = gen_helper_VABSDUW, + .opt_opc = vecop_list, + .vece = MO_32 + }, + }; + + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16, &op[vece]); + + return true; +} + +TRANS_FLAGS2(ISA300, VABSDUB, do_vabsdu, MO_8) +TRANS_FLAGS2(ISA300, VABSDUH, do_vabsdu, MO_16) +TRANS_FLAGS2(ISA300, VABSDUW, do_vabsdu, MO_32) + +static bool do_vdiv_vmod(DisasContext *ctx, arg_VX *a, const int vece, + void (*func_32)(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b), + void (*func_64)(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b)) +{ + const GVecGen3 op = { + .fni4 = func_32, + .fni8 = func_64, + .vece = vece + }; + + REQUIRE_VECTOR(ctx); + + tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra), + avr_full_offset(a->vrb), 16, 16, &op); + + return true; +} + +#define DIVU32(NAME, DIV) \ +static void NAME(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) \ +{ \ + TCGv_i32 zero = tcg_constant_i32(0); \ + TCGv_i32 one = tcg_constant_i32(1); \ + tcg_gen_movcond_i32(TCG_COND_EQ, b, b, zero, one, b); \ + DIV(t, a, b); \ +} + +#define DIVS32(NAME, DIV) \ +static void NAME(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) \ +{ \ + TCGv_i32 t0 = tcg_temp_new_i32(); \ + TCGv_i32 t1 = tcg_temp_new_i32(); \ + tcg_gen_setcondi_i32(TCG_COND_EQ, t0, a, INT32_MIN); \ + tcg_gen_setcondi_i32(TCG_COND_EQ, t1, b, -1); \ + tcg_gen_and_i32(t0, t0, t1); \ + tcg_gen_setcondi_i32(TCG_COND_EQ, t1, b, 0); \ + tcg_gen_or_i32(t0, t0, t1); \ + tcg_gen_movi_i32(t1, 0); \ + tcg_gen_movcond_i32(TCG_COND_NE, b, t0, t1, t0, b); \ + DIV(t, a, b); \ +} + +#define DIVU64(NAME, DIV) \ +static void NAME(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) \ +{ \ + TCGv_i64 zero = tcg_constant_i64(0); \ + TCGv_i64 one = tcg_constant_i64(1); \ + tcg_gen_movcond_i64(TCG_COND_EQ, b, b, zero, one, b); \ + DIV(t, a, b); \ +} + +#define DIVS64(NAME, DIV) \ +static void NAME(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) \ +{ \ + TCGv_i64 t0 = tcg_temp_new_i64(); \ + TCGv_i64 t1 = tcg_temp_new_i64(); \ + tcg_gen_setcondi_i64(TCG_COND_EQ, t0, a, INT64_MIN); \ + tcg_gen_setcondi_i64(TCG_COND_EQ, t1, b, -1); \ + tcg_gen_and_i64(t0, t0, t1); \ + tcg_gen_setcondi_i64(TCG_COND_EQ, t1, b, 0); \ + tcg_gen_or_i64(t0, t0, t1); \ + tcg_gen_movi_i64(t1, 0); \ + tcg_gen_movcond_i64(TCG_COND_NE, b, t0, t1, t0, b); \ + DIV(t, a, b); \ +} + +DIVS32(do_divsw, tcg_gen_div_i32) +DIVU32(do_divuw, tcg_gen_divu_i32) +DIVS64(do_divsd, tcg_gen_div_i64) +DIVU64(do_divud, tcg_gen_divu_i64) + +TRANS_FLAGS2(ISA310, VDIVSW, do_vdiv_vmod, MO_32, do_divsw, NULL) +TRANS_FLAGS2(ISA310, VDIVUW, do_vdiv_vmod, MO_32, do_divuw, NULL) +TRANS_FLAGS2(ISA310, VDIVSD, do_vdiv_vmod, MO_64, NULL, do_divsd) +TRANS_FLAGS2(ISA310, VDIVUD, do_vdiv_vmod, MO_64, NULL, do_divud) +TRANS_FLAGS2(ISA310, VDIVSQ, do_vx_helper, gen_helper_VDIVSQ) +TRANS_FLAGS2(ISA310, VDIVUQ, do_vx_helper, gen_helper_VDIVUQ) + +static void do_dives_i32(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i64 val1, val2; + + val1 = tcg_temp_new_i64(); + val2 = tcg_temp_new_i64(); + + tcg_gen_ext_i32_i64(val1, a); + tcg_gen_ext_i32_i64(val2, b); + + /* (a << 32)/b */ + tcg_gen_shli_i64(val1, val1, 32); + tcg_gen_div_i64(val1, val1, val2); + + /* if quotient doesn't fit in 32 bits the result is undefined */ + tcg_gen_extrl_i64_i32(t, val1); +} + +static void do_diveu_i32(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i64 val1, val2; + + val1 = tcg_temp_new_i64(); + val2 = tcg_temp_new_i64(); + + tcg_gen_extu_i32_i64(val1, a); + tcg_gen_extu_i32_i64(val2, b); + + /* (a << 32)/b */ + tcg_gen_shli_i64(val1, val1, 32); + tcg_gen_divu_i64(val1, val1, val2); + + /* if quotient doesn't fit in 32 bits the result is undefined */ + tcg_gen_extrl_i64_i32(t, val1); +} + +DIVS32(do_divesw, do_dives_i32) +DIVU32(do_diveuw, do_diveu_i32) + +DIVS32(do_modsw, tcg_gen_rem_i32) +DIVU32(do_moduw, tcg_gen_remu_i32) +DIVS64(do_modsd, tcg_gen_rem_i64) +DIVU64(do_modud, tcg_gen_remu_i64) + +TRANS_FLAGS2(ISA310, VDIVESW, do_vdiv_vmod, MO_32, do_divesw, NULL) +TRANS_FLAGS2(ISA310, VDIVEUW, do_vdiv_vmod, MO_32, do_diveuw, NULL) +TRANS_FLAGS2(ISA310, VDIVESD, do_vx_helper, gen_helper_VDIVESD) +TRANS_FLAGS2(ISA310, VDIVEUD, do_vx_helper, gen_helper_VDIVEUD) +TRANS_FLAGS2(ISA310, VDIVESQ, do_vx_helper, gen_helper_VDIVESQ) +TRANS_FLAGS2(ISA310, VDIVEUQ, do_vx_helper, gen_helper_VDIVEUQ) + +TRANS_FLAGS2(ISA310, VMODSW, do_vdiv_vmod, MO_32, do_modsw , NULL) +TRANS_FLAGS2(ISA310, VMODUW, do_vdiv_vmod, MO_32, do_moduw, NULL) +TRANS_FLAGS2(ISA310, VMODSD, do_vdiv_vmod, MO_64, NULL, do_modsd) +TRANS_FLAGS2(ISA310, VMODUD, do_vdiv_vmod, MO_64, NULL, do_modud) +TRANS_FLAGS2(ISA310, VMODSQ, do_vx_helper, gen_helper_VMODSQ) +TRANS_FLAGS2(ISA310, VMODUQ, do_vx_helper, gen_helper_VMODUQ) + +#undef DIVS32 +#undef DIVU32 +#undef DIVS64 +#undef DIVU64 + #undef GEN_VR_LDX #undef GEN_VR_STX #undef GEN_VR_LVE diff --git a/target/ppc/translate/vmx-ops.c.inc b/target/ppc/translate/vmx-ops.c.inc index d7cc57868e..33fec8aca4 100644 --- a/target/ppc/translate/vmx-ops.c.inc +++ b/target/ppc/translate/vmx-ops.c.inc @@ -83,12 +83,6 @@ GEN_VXFORM(vminsb, 1, 12), GEN_VXFORM(vminsh, 1, 13), GEN_VXFORM(vminsw, 1, 14), GEN_VXFORM_207(vminsd, 1, 15), -GEN_VXFORM_DUAL(vavgub, vabsdub, 1, 16, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM_DUAL(vavguh, vabsduh, 1, 17, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM_DUAL(vavguw, vabsduw, 1, 18, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM(vavgsb, 1, 20), -GEN_VXFORM(vavgsh, 1, 21), -GEN_VXFORM(vavgsw, 1, 22), GEN_VXFORM(vmrghb, 6, 0), GEN_VXFORM(vmrghh, 6, 1), GEN_VXFORM(vmrghw, 6, 2), @@ -106,12 +100,8 @@ GEN_VXFORM_300(vsrv, 2, 28), GEN_VXFORM_300(vslv, 2, 29), GEN_VXFORM(vslo, 6, 16), GEN_VXFORM(vsro, 6, 17), -GEN_VXFORM(vaddcuw, 0, 6), -GEN_HANDLER_E_2(vprtybw, 0x4, 0x1, 0x18, 8, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E_2(vprtybd, 0x4, 0x1, 0x18, 9, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E_2(vprtybq, 0x4, 0x1, 0x18, 10, 0, PPC_NONE, PPC2_ISA300), -GEN_VXFORM_DUAL(vsubcuw, xpnd04_1, 0, 22, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM(xpnd04_1, 0, 22), GEN_VXFORM_300(bcdsr, 0, 23), GEN_VXFORM_300(bcdsr, 0, 31), GEN_VXFORM_DUAL(vaddubs, vmul10uq, 0, 8, PPC_ALTIVEC, PPC_NONE), @@ -126,12 +116,8 @@ GEN_VXFORM(vsubuws, 0, 26), GEN_VXFORM_DUAL(vsubsbs, bcdtrunc, 0, 28, PPC_ALTIVEC, PPC2_ISA300), GEN_VXFORM(vsubshs, 0, 29), GEN_VXFORM_DUAL(vsubsws, xpnd04_2, 0, 30, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM_207(vadduqm, 0, 4), -GEN_VXFORM_207(vaddcuq, 0, 5), -GEN_VXFORM_DUAL(vaddeuqm, vaddecuq, 30, 0xFF, PPC_NONE, PPC2_ALTIVEC_207), -GEN_VXFORM_DUAL(vsubuqm, bcdtrunc, 0, 20, PPC2_ALTIVEC_207, PPC2_ISA300), -GEN_VXFORM_DUAL(vsubcuq, bcdutrunc, 0, 21, PPC2_ALTIVEC_207, PPC2_ISA300), -GEN_VXFORM_DUAL(vsubeuqm, vsubecuq, 31, 0xFF, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_300(bcdtrunc, 0, 20), +GEN_VXFORM_300(bcdutrunc, 0, 21), GEN_VXFORM(vsl, 2, 7), GEN_VXFORM(vsr, 2, 11), GEN_VXFORM(vpkuhum, 7, 0), @@ -186,8 +172,6 @@ GEN_VXFORM_300_EXT(vextractd, 6, 11, 0x100000), GEN_VXFORM(vspltisb, 6, 12), GEN_VXFORM(vspltish, 6, 13), GEN_VXFORM(vspltisw, 6, 14), -GEN_VXFORM_300_EO(vnegw, 0x01, 0x18, 0x06), -GEN_VXFORM_300_EO(vnegd, 0x01, 0x18, 0x07), GEN_VXFORM_300_EO(vctzb, 0x01, 0x18, 0x1C), GEN_VXFORM_300_EO(vctzh, 0x01, 0x18, 0x1D), GEN_VXFORM_300_EO(vctzw, 0x01, 0x18, 0x1E), @@ -223,7 +207,6 @@ GEN_VXFORM_UIMM(vctsxs, 5, 15), #define GEN_VAFORM_PAIRED(name0, name1, opc2) \ GEN_HANDLER(name0##_##name1, 0x04, opc2, 0xFF, 0x00000000, PPC_ALTIVEC) -GEN_VAFORM_PAIRED(vmhaddshs, vmhraddshs, 16), GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23), GEN_VXFORM_DUAL(vclzb, vpopcntb, 1, 28, PPC_NONE, PPC2_ALTIVEC_207), @@ -237,7 +220,6 @@ GEN_VXFORM_207(vgbbd, 6, 20), GEN_VXFORM_207(vpmsumb, 4, 16), GEN_VXFORM_207(vpmsumh, 4, 17), GEN_VXFORM_207(vpmsumw, 4, 18), -GEN_VXFORM_207(vpmsumd, 4, 19), GEN_VXFORM_207(vsbox, 4, 23), diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 7acdbceec4..0266f09119 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -2,25 +2,25 @@ static inline void get_cpu_vsr(TCGv_i64 dst, int n, bool high) { - tcg_gen_ld_i64(dst, cpu_env, vsr64_offset(n, high)); + tcg_gen_ld_i64(dst, tcg_env, vsr64_offset(n, high)); } static inline void set_cpu_vsr(int n, TCGv_i64 src, bool high) { - tcg_gen_st_i64(src, cpu_env, vsr64_offset(n, high)); + tcg_gen_st_i64(src, tcg_env, vsr64_offset(n, high)); } static inline TCGv_ptr gen_vsr_ptr(int reg) { TCGv_ptr r = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(r, cpu_env, vsr_full_offset(reg)); + tcg_gen_addi_ptr(r, tcg_env, vsr_full_offset(reg)); return r; } static inline TCGv_ptr gen_acc_ptr(int reg) { TCGv_ptr r = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(r, cpu_env, acc_full_offset(reg)); + tcg_gen_addi_ptr(r, tcg_env, acc_full_offset(reg)); return r; } @@ -40,8 +40,6 @@ static void gen_##name(DisasContext *ctx) \ gen_qemu_##operation(ctx, t0, EA); \ set_cpu_vsr(xT(ctx->opcode), t0, true); \ /* NOTE: cpu_vsrl is undefined */ \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(t0); \ } VSX_LOAD_SCALAR(lxsdx, ld64_i64) @@ -68,8 +66,6 @@ static void gen_lxvd2x(DisasContext *ctx) tcg_gen_addi_tl(EA, EA, 8); gen_qemu_ld64_i64(ctx, t0, EA); set_cpu_vsr(xT(ctx->opcode), t0, false); - tcg_temp_free(EA); - tcg_temp_free_i64(t0); } static void gen_lxvw4x(DisasContext *ctx) @@ -99,8 +95,6 @@ static void gen_lxvw4x(DisasContext *ctx) tcg_gen_qemu_ld_i64(t0, EA, ctx->mem_idx, MO_LEUQ); tcg_gen_shri_i64(t1, t0, 32); tcg_gen_deposit_i64(xtl, t1, t0, 32, 32); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } else { tcg_gen_qemu_ld_i64(xth, EA, ctx->mem_idx, MO_BEUQ); tcg_gen_addi_tl(EA, EA, 8); @@ -108,9 +102,6 @@ static void gen_lxvw4x(DisasContext *ctx) } set_cpu_vsr(xT(ctx->opcode), xth, true); set_cpu_vsr(xT(ctx->opcode), xtl, false); - tcg_temp_free(EA); - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); } static void gen_lxvwsx(DisasContext *ctx) @@ -138,9 +129,6 @@ static void gen_lxvwsx(DisasContext *ctx) data = tcg_temp_new_i32(); tcg_gen_qemu_ld_i32(data, EA, ctx->mem_idx, DEF_MEMOP(MO_UL)); tcg_gen_gvec_dup_i32(MO_UL, vsr_full_offset(xT(ctx->opcode)), 16, 16, data); - - tcg_temp_free(EA); - tcg_temp_free_i32(data); } static void gen_lxvdsx(DisasContext *ctx) @@ -161,15 +149,12 @@ static void gen_lxvdsx(DisasContext *ctx) data = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(data, EA, ctx->mem_idx, DEF_MEMOP(MO_UQ)); tcg_gen_gvec_dup_i64(MO_UQ, vsr_full_offset(xT(ctx->opcode)), 16, 16, data); - - tcg_temp_free(EA); - tcg_temp_free_i64(data); } static void gen_bswap16x8(TCGv_i64 outh, TCGv_i64 outl, TCGv_i64 inh, TCGv_i64 inl) { - TCGv_i64 mask = tcg_const_i64(0x00FF00FF00FF00FF); + TCGv_i64 mask = tcg_constant_i64(0x00FF00FF00FF00FF); TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); @@ -186,10 +171,6 @@ static void gen_bswap16x8(TCGv_i64 outh, TCGv_i64 outl, tcg_gen_shri_i64(t1, inl, 8); tcg_gen_and_i64(t1, t1, mask); tcg_gen_or_i64(outl, t0, t1); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(mask); } static void gen_bswap32x4(TCGv_i64 outh, TCGv_i64 outl, @@ -204,10 +185,8 @@ static void gen_bswap32x4(TCGv_i64 outh, TCGv_i64 outl, tcg_gen_deposit_i64(outh, outh, hi, 32, 32); tcg_gen_shri_i64(outl, lo, 32); tcg_gen_deposit_i64(outl, outl, lo, 32, 32); - - tcg_temp_free_i64(hi); - tcg_temp_free_i64(lo); } + static void gen_lxvh8x(DisasContext *ctx) { TCGv EA; @@ -232,9 +211,6 @@ static void gen_lxvh8x(DisasContext *ctx) } set_cpu_vsr(xT(ctx->opcode), xth, true); set_cpu_vsr(xT(ctx->opcode), xtl, false); - tcg_temp_free(EA); - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); } static void gen_lxvb16x(DisasContext *ctx) @@ -257,9 +233,6 @@ static void gen_lxvb16x(DisasContext *ctx) tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEUQ); set_cpu_vsr(xT(ctx->opcode), xth, true); set_cpu_vsr(xT(ctx->opcode), xtl, false); - tcg_temp_free(EA); - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); } #ifdef TARGET_PPC64 @@ -284,9 +257,7 @@ static void gen_##name(DisasContext *ctx) \ xt = gen_vsr_ptr(xT(ctx->opcode)); \ gen_set_access_type(ctx, ACCESS_INT); \ gen_addr_register(ctx, EA); \ - gen_helper_##name(cpu_env, EA, xt, cpu_gpr[rB(ctx->opcode)]); \ - tcg_temp_free(EA); \ - tcg_temp_free_ptr(xt); \ + gen_helper_##name(tcg_env, EA, xt, cpu_gpr[rB(ctx->opcode)]); \ } VSX_VECTOR_LOAD_STORE_LENGTH(lxvl) @@ -310,8 +281,6 @@ static void gen_##name(DisasContext *ctx) \ gen_addr_reg_index(ctx, EA); \ get_cpu_vsr(t0, xS(ctx->opcode), true); \ gen_qemu_##operation(ctx, t0, EA); \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(t0); \ } VSX_STORE_SCALAR(stxsdx, st64_i64) @@ -338,8 +307,6 @@ static void gen_stxvd2x(DisasContext *ctx) tcg_gen_addi_tl(EA, EA, 8); get_cpu_vsr(t0, xS(ctx->opcode), false); gen_qemu_st64_i64(ctx, t0, EA); - tcg_temp_free(EA); - tcg_temp_free_i64(t0); } static void gen_stxvw4x(DisasContext *ctx) @@ -370,16 +337,11 @@ static void gen_stxvw4x(DisasContext *ctx) tcg_gen_shri_i64(t0, xsl, 32); tcg_gen_deposit_i64(t1, t0, xsl, 32, 32); tcg_gen_qemu_st_i64(t1, EA, ctx->mem_idx, MO_LEUQ); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } else { tcg_gen_qemu_st_i64(xsh, EA, ctx->mem_idx, MO_BEUQ); tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEUQ); } - tcg_temp_free(EA); - tcg_temp_free_i64(xsh); - tcg_temp_free_i64(xsl); } static void gen_stxvh8x(DisasContext *ctx) @@ -407,16 +369,11 @@ static void gen_stxvh8x(DisasContext *ctx) tcg_gen_qemu_st_i64(outh, EA, ctx->mem_idx, MO_BEUQ); tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_st_i64(outl, EA, ctx->mem_idx, MO_BEUQ); - tcg_temp_free_i64(outh); - tcg_temp_free_i64(outl); } else { tcg_gen_qemu_st_i64(xsh, EA, ctx->mem_idx, MO_BEUQ); tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEUQ); } - tcg_temp_free(EA); - tcg_temp_free_i64(xsh); - tcg_temp_free_i64(xsl); } static void gen_stxvb16x(DisasContext *ctx) @@ -439,9 +396,6 @@ static void gen_stxvb16x(DisasContext *ctx) tcg_gen_qemu_st_i64(xsh, EA, ctx->mem_idx, MO_BEUQ); tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEUQ); - tcg_temp_free(EA); - tcg_temp_free_i64(xsh); - tcg_temp_free_i64(xsl); } static void gen_mfvsrwz(DisasContext *ctx) @@ -462,8 +416,6 @@ static void gen_mfvsrwz(DisasContext *ctx) get_cpu_vsr(xsh, xS(ctx->opcode), true); tcg_gen_ext32u_i64(tmp, xsh); tcg_gen_trunc_i64_tl(cpu_gpr[rA(ctx->opcode)], tmp); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(xsh); } static void gen_mtvsrwa(DisasContext *ctx) @@ -484,8 +436,6 @@ static void gen_mtvsrwa(DisasContext *ctx) tcg_gen_extu_tl_i64(tmp, cpu_gpr[rA(ctx->opcode)]); tcg_gen_ext32s_i64(xsh, tmp); set_cpu_vsr(xT(ctx->opcode), xsh, true); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(xsh); } static void gen_mtvsrwz(DisasContext *ctx) @@ -506,8 +456,6 @@ static void gen_mtvsrwz(DisasContext *ctx) tcg_gen_extu_tl_i64(tmp, cpu_gpr[rA(ctx->opcode)]); tcg_gen_ext32u_i64(xsh, tmp); set_cpu_vsr(xT(ctx->opcode), xsh, true); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(xsh); } #if defined(TARGET_PPC64) @@ -528,7 +476,6 @@ static void gen_mfvsrd(DisasContext *ctx) t0 = tcg_temp_new_i64(); get_cpu_vsr(t0, xS(ctx->opcode), true); tcg_gen_mov_i64(cpu_gpr[rA(ctx->opcode)], t0); - tcg_temp_free_i64(t0); } static void gen_mtvsrd(DisasContext *ctx) @@ -548,7 +495,6 @@ static void gen_mtvsrd(DisasContext *ctx) t0 = tcg_temp_new_i64(); tcg_gen_mov_i64(t0, cpu_gpr[rA(ctx->opcode)]); set_cpu_vsr(xT(ctx->opcode), t0, true); - tcg_temp_free_i64(t0); } static void gen_mfvsrld(DisasContext *ctx) @@ -568,7 +514,6 @@ static void gen_mfvsrld(DisasContext *ctx) t0 = tcg_temp_new_i64(); get_cpu_vsr(t0, xS(ctx->opcode), false); tcg_gen_mov_i64(cpu_gpr[rA(ctx->opcode)], t0); - tcg_temp_free_i64(t0); } static void gen_mtvsrdd(DisasContext *ctx) @@ -596,7 +541,6 @@ static void gen_mtvsrdd(DisasContext *ctx) tcg_gen_mov_i64(t0, cpu_gpr[rB(ctx->opcode)]); set_cpu_vsr(xT(ctx->opcode), t0, false); - tcg_temp_free_i64(t0); } static void gen_mtvsrws(DisasContext *ctx) @@ -619,7 +563,6 @@ static void gen_mtvsrws(DisasContext *ctx) cpu_gpr[rA(ctx->opcode)], 32, 32); set_cpu_vsr(xT(ctx->opcode), t0, false); set_cpu_vsr(xT(ctx->opcode), t0, true); - tcg_temp_free_i64(t0); } #endif @@ -630,6 +573,10 @@ static void gen_mtvsrws(DisasContext *ctx) #define OP_CPSGN 4 #define SGN_MASK_DP 0x8000000000000000ull #define SGN_MASK_SP 0x8000000080000000ull +#define EXP_MASK_DP 0x7FF0000000000000ull +#define EXP_MASK_SP 0x7F8000007F800000ull +#define FRC_MASK_DP (~(SGN_MASK_DP | EXP_MASK_DP)) +#define FRC_MASK_SP (~(SGN_MASK_SP | EXP_MASK_SP)) #define VSX_SCALAR_MOVE(name, op, sgn_mask) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -662,14 +609,11 @@ static void glue(gen_, name)(DisasContext *ctx) \ tcg_gen_and_i64(xa, xa, sgm); \ tcg_gen_andc_i64(xb, xb, sgm); \ tcg_gen_or_i64(xb, xb, xa); \ - tcg_temp_free_i64(xa); \ break; \ } \ } \ set_cpu_vsr(xT(ctx->opcode), xb, true); \ set_cpu_vsr(xT(ctx->opcode), tcg_constant_i64(0), false); \ - tcg_temp_free_i64(xb); \ - tcg_temp_free_i64(sgm); \ } VSX_SCALAR_MOVE(xsabsdp, OP_ABS, SGN_MASK_DP) @@ -713,15 +657,10 @@ static void glue(gen_, name)(DisasContext *ctx) \ tcg_gen_and_i64(xah, tmp, sgm); \ tcg_gen_andc_i64(xbh, xbh, sgm); \ tcg_gen_or_i64(xbh, xbh, xah); \ - tcg_temp_free_i64(xah); \ break; \ } \ set_cpu_vsr(xt, xbh, true); \ set_cpu_vsr(xt, xbl, false); \ - tcg_temp_free_i64(xbl); \ - tcg_temp_free_i64(xbh); \ - tcg_temp_free_i64(sgm); \ - tcg_temp_free_i64(tmp); \ } VSX_SCALAR_MOVE_QP(xsabsqp, OP_ABS, SGN_MASK_DP) @@ -729,67 +668,125 @@ VSX_SCALAR_MOVE_QP(xsnabsqp, OP_NABS, SGN_MASK_DP) VSX_SCALAR_MOVE_QP(xsnegqp, OP_NEG, SGN_MASK_DP) VSX_SCALAR_MOVE_QP(xscpsgnqp, OP_CPSGN, SGN_MASK_DP) -#define VSX_VECTOR_MOVE(name, op, sgn_mask) \ -static void glue(gen_, name)(DisasContext *ctx) \ - { \ - TCGv_i64 xbh, xbl, sgm; \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - xbh = tcg_temp_new_i64(); \ - xbl = tcg_temp_new_i64(); \ - sgm = tcg_temp_new_i64(); \ - get_cpu_vsr(xbh, xB(ctx->opcode), true); \ - get_cpu_vsr(xbl, xB(ctx->opcode), false); \ - tcg_gen_movi_i64(sgm, sgn_mask); \ - switch (op) { \ - case OP_ABS: { \ - tcg_gen_andc_i64(xbh, xbh, sgm); \ - tcg_gen_andc_i64(xbl, xbl, sgm); \ - break; \ - } \ - case OP_NABS: { \ - tcg_gen_or_i64(xbh, xbh, sgm); \ - tcg_gen_or_i64(xbl, xbl, sgm); \ - break; \ - } \ - case OP_NEG: { \ - tcg_gen_xor_i64(xbh, xbh, sgm); \ - tcg_gen_xor_i64(xbl, xbl, sgm); \ - break; \ - } \ - case OP_CPSGN: { \ - TCGv_i64 xah = tcg_temp_new_i64(); \ - TCGv_i64 xal = tcg_temp_new_i64(); \ - get_cpu_vsr(xah, xA(ctx->opcode), true); \ - get_cpu_vsr(xal, xA(ctx->opcode), false); \ - tcg_gen_and_i64(xah, xah, sgm); \ - tcg_gen_and_i64(xal, xal, sgm); \ - tcg_gen_andc_i64(xbh, xbh, sgm); \ - tcg_gen_andc_i64(xbl, xbl, sgm); \ - tcg_gen_or_i64(xbh, xbh, xah); \ - tcg_gen_or_i64(xbl, xbl, xal); \ - tcg_temp_free_i64(xah); \ - tcg_temp_free_i64(xal); \ - break; \ - } \ - } \ - set_cpu_vsr(xT(ctx->opcode), xbh, true); \ - set_cpu_vsr(xT(ctx->opcode), xbl, false); \ - tcg_temp_free_i64(xbh); \ - tcg_temp_free_i64(xbl); \ - tcg_temp_free_i64(sgm); \ +#define TCG_OP_IMM_i64(FUNC, OP, IMM) \ + static void FUNC(TCGv_i64 t, TCGv_i64 b) \ + { \ + OP(t, b, IMM); \ } -VSX_VECTOR_MOVE(xvabsdp, OP_ABS, SGN_MASK_DP) -VSX_VECTOR_MOVE(xvnabsdp, OP_NABS, SGN_MASK_DP) -VSX_VECTOR_MOVE(xvnegdp, OP_NEG, SGN_MASK_DP) -VSX_VECTOR_MOVE(xvcpsgndp, OP_CPSGN, SGN_MASK_DP) -VSX_VECTOR_MOVE(xvabssp, OP_ABS, SGN_MASK_SP) -VSX_VECTOR_MOVE(xvnabssp, OP_NABS, SGN_MASK_SP) -VSX_VECTOR_MOVE(xvnegsp, OP_NEG, SGN_MASK_SP) -VSX_VECTOR_MOVE(xvcpsgnsp, OP_CPSGN, SGN_MASK_SP) +TCG_OP_IMM_i64(do_xvabssp_i64, tcg_gen_andi_i64, ~SGN_MASK_SP) +TCG_OP_IMM_i64(do_xvnabssp_i64, tcg_gen_ori_i64, SGN_MASK_SP) +TCG_OP_IMM_i64(do_xvnegsp_i64, tcg_gen_xori_i64, SGN_MASK_SP) +TCG_OP_IMM_i64(do_xvabsdp_i64, tcg_gen_andi_i64, ~SGN_MASK_DP) +TCG_OP_IMM_i64(do_xvnabsdp_i64, tcg_gen_ori_i64, SGN_MASK_DP) +TCG_OP_IMM_i64(do_xvnegdp_i64, tcg_gen_xori_i64, SGN_MASK_DP) +#undef TCG_OP_IMM_i64 + +static void xv_msb_op1(unsigned vece, TCGv_vec t, TCGv_vec b, + void (*tcg_gen_op_vec)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) +{ + uint64_t msb = (vece == MO_32) ? SGN_MASK_SP : SGN_MASK_DP; + tcg_gen_op_vec(vece, t, b, tcg_constant_vec_matching(t, vece, msb)); +} + +static void do_xvabs_vec(unsigned vece, TCGv_vec t, TCGv_vec b) +{ + xv_msb_op1(vece, t, b, tcg_gen_andc_vec); +} + +static void do_xvnabs_vec(unsigned vece, TCGv_vec t, TCGv_vec b) +{ + xv_msb_op1(vece, t, b, tcg_gen_or_vec); +} + +static void do_xvneg_vec(unsigned vece, TCGv_vec t, TCGv_vec b) +{ + xv_msb_op1(vece, t, b, tcg_gen_xor_vec); +} + +static bool do_vsx_msb_op(DisasContext *ctx, arg_XX2 *a, unsigned vece, + void (*vec)(unsigned, TCGv_vec, TCGv_vec), + void (*i64)(TCGv_i64, TCGv_i64)) +{ + static const TCGOpcode vecop_list[] = { + 0 + }; + + const GVecGen2 op = { + .fni8 = i64, + .fniv = vec, + .opt_opc = vecop_list, + .vece = vece + }; + + REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); + + tcg_gen_gvec_2(vsr_full_offset(a->xt), vsr_full_offset(a->xb), + 16, 16, &op); + + return true; +} + +TRANS(XVABSDP, do_vsx_msb_op, MO_64, do_xvabs_vec, do_xvabsdp_i64) +TRANS(XVNABSDP, do_vsx_msb_op, MO_64, do_xvnabs_vec, do_xvnabsdp_i64) +TRANS(XVNEGDP, do_vsx_msb_op, MO_64, do_xvneg_vec, do_xvnegdp_i64) +TRANS(XVABSSP, do_vsx_msb_op, MO_32, do_xvabs_vec, do_xvabssp_i64) +TRANS(XVNABSSP, do_vsx_msb_op, MO_32, do_xvnabs_vec, do_xvnabssp_i64) +TRANS(XVNEGSP, do_vsx_msb_op, MO_32, do_xvneg_vec, do_xvnegsp_i64) + +static void do_xvcpsgndp_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_andi_i64(a, a, SGN_MASK_DP); + tcg_gen_andi_i64(b, b, ~SGN_MASK_DP); + tcg_gen_or_i64(t, a, b); +} + +static void do_xvcpsgnsp_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_andi_i64(a, a, SGN_MASK_SP); + tcg_gen_andi_i64(b, b, ~SGN_MASK_SP); + tcg_gen_or_i64(t, a, b); +} + +static void do_xvcpsgn_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +{ + uint64_t msb = (vece == MO_32) ? SGN_MASK_SP : SGN_MASK_DP; + tcg_gen_bitsel_vec(vece, t, tcg_constant_vec_matching(t, vece, msb), a, b); +} + +static bool do_xvcpsgn(DisasContext *ctx, arg_XX3 *a, unsigned vece) +{ + static const TCGOpcode vecop_list[] = { + 0 + }; + + static const GVecGen3 op[] = { + { + .fni8 = do_xvcpsgnsp_i64, + .fniv = do_xvcpsgn_vec, + .opt_opc = vecop_list, + .vece = MO_32 + }, + { + .fni8 = do_xvcpsgndp_i64, + .fniv = do_xvcpsgn_vec, + .opt_opc = vecop_list, + .vece = MO_64 + }, + }; + + REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); + + tcg_gen_gvec_3(vsr_full_offset(a->xt), vsr_full_offset(a->xa), + vsr_full_offset(a->xb), 16, 16, &op[vece - MO_32]); + + return true; +} + +TRANS(XVCPSGNSP, do_xvcpsgn, MO_32) +TRANS(XVCPSGNDP, do_xvcpsgn, MO_64) #define VSX_CMP(name, op1, op2, inval, type) \ static void gen_##name(DisasContext *ctx) \ @@ -804,16 +801,11 @@ static void gen_##name(DisasContext *ctx) \ xa = gen_vsr_ptr(xA(ctx->opcode)); \ xb = gen_vsr_ptr(xB(ctx->opcode)); \ if ((ctx->opcode >> (31 - 21)) & 1) { \ - gen_helper_##name(cpu_crf[6], cpu_env, xt, xa, xb); \ + gen_helper_##name(cpu_crf[6], tcg_env, xt, xa, xb); \ } else { \ ignored = tcg_temp_new_i32(); \ - gen_helper_##name(ignored, cpu_env, xt, xa, xb); \ - tcg_temp_free_i32(ignored); \ + gen_helper_##name(ignored, tcg_env, xt, xa, xb); \ } \ - gen_helper_float_check_status(cpu_env); \ - tcg_temp_free_ptr(xt); \ - tcg_temp_free_ptr(xa); \ - tcg_temp_free_ptr(xb); \ } VSX_CMP(xvcmpeqdp, 0x0C, 0x0C, 0, PPC2_VSX) @@ -833,15 +825,11 @@ static bool trans_XSCVQPDP(DisasContext *ctx, arg_X_tb_rc *a) REQUIRE_INSNS_FLAGS2(ctx, ISA300); REQUIRE_VSX(ctx); - ro = tcg_const_i32(a->rc); + ro = tcg_constant_i32(a->rc); xt = gen_avr_ptr(a->rt); xb = gen_avr_ptr(a->rb); - gen_helper_XSCVQPDP(cpu_env, ro, xt, xb); - tcg_temp_free_i32(ro); - tcg_temp_free_ptr(xt); - tcg_temp_free_ptr(xb); - + gen_helper_XSCVQPDP(tcg_env, ro, xt, xb); return true; } @@ -855,10 +843,7 @@ static bool do_helper_env_X_tb(DisasContext *ctx, arg_X_tb *a, xt = gen_avr_ptr(a->rt); xb = gen_avr_ptr(a->rb); - gen_helper(cpu_env, xt, xb); - tcg_temp_free_ptr(xt); - tcg_temp_free_ptr(xb); - + gen_helper(tcg_env, xt, xb); return true; } @@ -875,9 +860,8 @@ static void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_VSXU); \ return; \ } \ - opc = tcg_const_i32(ctx->opcode); \ - gen_helper_##name(cpu_env, opc); \ - tcg_temp_free_i32(opc); \ + opc = tcg_constant_i32(ctx->opcode); \ + gen_helper_##name(tcg_env, opc); \ } #define GEN_VSX_HELPER_X3(name, op1, op2, inval, type) \ @@ -891,10 +875,7 @@ static void gen_##name(DisasContext *ctx) \ xt = gen_vsr_ptr(xT(ctx->opcode)); \ xa = gen_vsr_ptr(xA(ctx->opcode)); \ xb = gen_vsr_ptr(xB(ctx->opcode)); \ - gen_helper_##name(cpu_env, xt, xa, xb); \ - tcg_temp_free_ptr(xt); \ - tcg_temp_free_ptr(xa); \ - tcg_temp_free_ptr(xb); \ + gen_helper_##name(tcg_env, xt, xa, xb); \ } #define GEN_VSX_HELPER_X2(name, op1, op2, inval, type) \ @@ -907,9 +888,7 @@ static void gen_##name(DisasContext *ctx) \ } \ xt = gen_vsr_ptr(xT(ctx->opcode)); \ xb = gen_vsr_ptr(xB(ctx->opcode)); \ - gen_helper_##name(cpu_env, xt, xb); \ - tcg_temp_free_ptr(xt); \ - tcg_temp_free_ptr(xb); \ + gen_helper_##name(tcg_env, xt, xb); \ } #define GEN_VSX_HELPER_X2_AB(name, op1, op2, inval, type) \ @@ -921,13 +900,10 @@ static void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_VSXU); \ return; \ } \ - opc = tcg_const_i32(ctx->opcode); \ + opc = tcg_constant_i32(ctx->opcode); \ xa = gen_vsr_ptr(xA(ctx->opcode)); \ xb = gen_vsr_ptr(xB(ctx->opcode)); \ - gen_helper_##name(cpu_env, opc, xa, xb); \ - tcg_temp_free_i32(opc); \ - tcg_temp_free_ptr(xa); \ - tcg_temp_free_ptr(xb); \ + gen_helper_##name(tcg_env, opc, xa, xb); \ } #define GEN_VSX_HELPER_X1(name, op1, op2, inval, type) \ @@ -939,11 +915,9 @@ static void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_VSXU); \ return; \ } \ - opc = tcg_const_i32(ctx->opcode); \ + opc = tcg_constant_i32(ctx->opcode); \ xb = gen_vsr_ptr(xB(ctx->opcode)); \ - gen_helper_##name(cpu_env, opc, xb); \ - tcg_temp_free_i32(opc); \ - tcg_temp_free_ptr(xb); \ + gen_helper_##name(tcg_env, opc, xb); \ } #define GEN_VSX_HELPER_R3(name, op1, op2, inval, type) \ @@ -955,15 +929,11 @@ static void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_VSXU); \ return; \ } \ - opc = tcg_const_i32(ctx->opcode); \ + opc = tcg_constant_i32(ctx->opcode); \ xt = gen_vsr_ptr(rD(ctx->opcode) + 32); \ xa = gen_vsr_ptr(rA(ctx->opcode) + 32); \ xb = gen_vsr_ptr(rB(ctx->opcode) + 32); \ - gen_helper_##name(cpu_env, opc, xt, xa, xb); \ - tcg_temp_free_i32(opc); \ - tcg_temp_free_ptr(xt); \ - tcg_temp_free_ptr(xa); \ - tcg_temp_free_ptr(xb); \ + gen_helper_##name(tcg_env, opc, xt, xa, xb); \ } #define GEN_VSX_HELPER_R2(name, op1, op2, inval, type) \ @@ -975,13 +945,10 @@ static void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_VSXU); \ return; \ } \ - opc = tcg_const_i32(ctx->opcode); \ + opc = tcg_constant_i32(ctx->opcode); \ xt = gen_vsr_ptr(rD(ctx->opcode) + 32); \ xb = gen_vsr_ptr(rB(ctx->opcode) + 32); \ - gen_helper_##name(cpu_env, opc, xt, xb); \ - tcg_temp_free_i32(opc); \ - tcg_temp_free_ptr(xt); \ - tcg_temp_free_ptr(xb); \ + gen_helper_##name(tcg_env, opc, xt, xb); \ } #define GEN_VSX_HELPER_R2_AB(name, op1, op2, inval, type) \ @@ -993,13 +960,10 @@ static void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_VSXU); \ return; \ } \ - opc = tcg_const_i32(ctx->opcode); \ + opc = tcg_constant_i32(ctx->opcode); \ xa = gen_vsr_ptr(rA(ctx->opcode) + 32); \ xb = gen_vsr_ptr(rB(ctx->opcode) + 32); \ - gen_helper_##name(cpu_env, opc, xa, xb); \ - tcg_temp_free_i32(opc); \ - tcg_temp_free_ptr(xa); \ - tcg_temp_free_ptr(xb); \ + gen_helper_##name(tcg_env, opc, xa, xb); \ } #define GEN_VSX_HELPER_XT_XB_ENV(name, op1, op2, inval, type) \ @@ -1014,11 +978,9 @@ static void gen_##name(DisasContext *ctx) \ t0 = tcg_temp_new_i64(); \ t1 = tcg_temp_new_i64(); \ get_cpu_vsr(t0, xB(ctx->opcode), true); \ - gen_helper_##name(t1, cpu_env, t0); \ + gen_helper_##name(t1, tcg_env, t0); \ set_cpu_vsr(xT(ctx->opcode), t1, true); \ set_cpu_vsr(xT(ctx->opcode), tcg_constant_i64(0), false); \ - tcg_temp_free_i64(t0); \ - tcg_temp_free_i64(t1); \ } GEN_VSX_HELPER_X3(xsadddp, 0x00, 0x04, 0, PPC2_VSX) @@ -1053,6 +1015,190 @@ GEN_VSX_HELPER_X2(xscvhpdp, 0x16, 0x15, 0x10, PPC2_ISA300) GEN_VSX_HELPER_R2(xscvsdqp, 0x04, 0x1A, 0x0A, PPC2_ISA300) GEN_VSX_HELPER_X2(xscvspdp, 0x12, 0x14, 0, PPC2_VSX) +/* test if +Inf */ +static void gen_is_pos_inf(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v) +{ + uint64_t exp_msk = (vece == MO_32) ? (uint32_t)EXP_MASK_SP : EXP_MASK_DP; + tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b, + tcg_constant_vec_matching(t, vece, exp_msk)); +} + +/* test if -Inf */ +static void gen_is_neg_inf(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v) +{ + uint64_t exp_msk = (vece == MO_32) ? (uint32_t)EXP_MASK_SP : EXP_MASK_DP; + uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP; + tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b, + tcg_constant_vec_matching(t, vece, sgn_msk | exp_msk)); +} + +/* test if +Inf or -Inf */ +static void gen_is_any_inf(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v) +{ + uint64_t exp_msk = (vece == MO_32) ? (uint32_t)EXP_MASK_SP : EXP_MASK_DP; + uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP; + tcg_gen_andc_vec(vece, b, b, tcg_constant_vec_matching(t, vece, sgn_msk)); + tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b, + tcg_constant_vec_matching(t, vece, exp_msk)); +} + +/* test if +0 */ +static void gen_is_pos_zero(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v) +{ + tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b, + tcg_constant_vec_matching(t, vece, 0)); +} + +/* test if -0 */ +static void gen_is_neg_zero(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v) +{ + uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP; + tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b, + tcg_constant_vec_matching(t, vece, sgn_msk)); +} + +/* test if +0 or -0 */ +static void gen_is_any_zero(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v) +{ + uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP; + tcg_gen_andc_vec(vece, b, b, tcg_constant_vec_matching(t, vece, sgn_msk)); + tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b, + tcg_constant_vec_matching(t, vece, 0)); +} + +/* test if +Denormal */ +static void gen_is_pos_denormal(unsigned vece, TCGv_vec t, + TCGv_vec b, int64_t v) +{ + uint64_t frc_msk = (vece == MO_32) ? (uint32_t)FRC_MASK_SP : FRC_MASK_DP; + tcg_gen_cmp_vec(TCG_COND_LEU, vece, t, b, + tcg_constant_vec_matching(t, vece, frc_msk)); + tcg_gen_cmp_vec(TCG_COND_NE, vece, b, b, + tcg_constant_vec_matching(t, vece, 0)); + tcg_gen_and_vec(vece, t, t, b); +} + +/* test if -Denormal */ +static void gen_is_neg_denormal(unsigned vece, TCGv_vec t, + TCGv_vec b, int64_t v) +{ + uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP; + uint64_t frc_msk = (vece == MO_32) ? (uint32_t)FRC_MASK_SP : FRC_MASK_DP; + tcg_gen_cmp_vec(TCG_COND_LEU, vece, t, b, + tcg_constant_vec_matching(t, vece, sgn_msk | frc_msk)); + tcg_gen_cmp_vec(TCG_COND_GTU, vece, b, b, + tcg_constant_vec_matching(t, vece, sgn_msk)); + tcg_gen_and_vec(vece, t, t, b); +} + +/* test if +Denormal or -Denormal */ +static void gen_is_any_denormal(unsigned vece, TCGv_vec t, + TCGv_vec b, int64_t v) +{ + uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP; + uint64_t frc_msk = (vece == MO_32) ? (uint32_t)FRC_MASK_SP : FRC_MASK_DP; + tcg_gen_andc_vec(vece, b, b, tcg_constant_vec_matching(t, vece, sgn_msk)); + tcg_gen_cmp_vec(TCG_COND_LE, vece, t, b, + tcg_constant_vec_matching(t, vece, frc_msk)); + tcg_gen_cmp_vec(TCG_COND_NE, vece, b, b, + tcg_constant_vec_matching(t, vece, 0)); + tcg_gen_and_vec(vece, t, t, b); +} + +/* test if NaN */ +static void gen_is_nan(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v) +{ + uint64_t exp_msk = (vece == MO_32) ? (uint32_t)EXP_MASK_SP : EXP_MASK_DP; + uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP; + tcg_gen_and_vec(vece, b, b, tcg_constant_vec_matching(t, vece, ~sgn_msk)); + tcg_gen_cmp_vec(TCG_COND_GT, vece, t, b, + tcg_constant_vec_matching(t, vece, exp_msk)); +} + +static bool do_xvtstdc(DisasContext *ctx, arg_XX2_uim *a, unsigned vece) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_cmp_vec, 0 + }; + + GVecGen2i op = { + .fnoi = (vece == MO_32) ? gen_helper_XVTSTDCSP : gen_helper_XVTSTDCDP, + .vece = vece, + .opt_opc = vecop_list + }; + + REQUIRE_VSX(ctx); + + switch (a->uim) { + case 0: + set_cpu_vsr(a->xt, tcg_constant_i64(0), true); + set_cpu_vsr(a->xt, tcg_constant_i64(0), false); + return true; + case ((1 << 0) | (1 << 1)): + /* test if +Denormal or -Denormal */ + op.fniv = gen_is_any_denormal; + break; + case (1 << 0): + /* test if -Denormal */ + op.fniv = gen_is_neg_denormal; + break; + case (1 << 1): + /* test if +Denormal */ + op.fniv = gen_is_pos_denormal; + break; + case ((1 << 2) | (1 << 3)): + /* test if +0 or -0 */ + op.fniv = gen_is_any_zero; + break; + case (1 << 2): + /* test if -0 */ + op.fniv = gen_is_neg_zero; + break; + case (1 << 3): + /* test if +0 */ + op.fniv = gen_is_pos_zero; + break; + case ((1 << 4) | (1 << 5)): + /* test if +Inf or -Inf */ + op.fniv = gen_is_any_inf; + break; + case (1 << 4): + /* test if -Inf */ + op.fniv = gen_is_neg_inf; + break; + case (1 << 5): + /* test if +Inf */ + op.fniv = gen_is_pos_inf; + break; + case (1 << 6): + /* test if NaN */ + op.fniv = gen_is_nan; + break; + } + tcg_gen_gvec_2i(vsr_full_offset(a->xt), vsr_full_offset(a->xb), + 16, 16, a->uim, &op); + + return true; +} + +TRANS_FLAGS2(VSX, XVTSTDCSP, do_xvtstdc, MO_32) +TRANS_FLAGS2(VSX, XVTSTDCDP, do_xvtstdc, MO_64) + +static bool do_XX2_bf_uim(DisasContext *ctx, arg_XX2_bf_uim *a, bool vsr, + void (*gen_helper)(TCGv_env, TCGv_i32, TCGv_i32, TCGv_ptr)) +{ + TCGv_ptr xb; + + REQUIRE_VSX(ctx); + xb = vsr ? gen_vsr_ptr(a->xb) : gen_avr_ptr(a->xb); + gen_helper(tcg_env, tcg_constant_i32(a->bf), tcg_constant_i32(a->uim), xb); + return true; +} + +TRANS_FLAGS2(ISA300, XSTSTDCSP, do_XX2_bf_uim, true, gen_helper_XSTSTDCSP) +TRANS_FLAGS2(ISA300, XSTSTDCDP, do_XX2_bf_uim, true, gen_helper_XSTSTDCDP) +TRANS_FLAGS2(ISA300, XSTSTDCQP, do_XX2_bf_uim, false, gen_helper_XSTSTDCQP) + bool trans_XSCVSPDPN(DisasContext *ctx, arg_XX2 *a) { TCGv_i64 tmp; @@ -1067,9 +1213,6 @@ bool trans_XSCVSPDPN(DisasContext *ctx, arg_XX2 *a) set_cpu_vsr(a->xt, tmp, true); set_cpu_vsr(a->xt, tcg_constant_i64(0), false); - - tcg_temp_free_i64(tmp); - return true; } @@ -1099,9 +1242,6 @@ GEN_VSX_HELPER_X2(xssqrtsp, 0x16, 0x00, 0, PPC2_VSX207) GEN_VSX_HELPER_X2(xsrsqrtesp, 0x14, 0x00, 0, PPC2_VSX207) GEN_VSX_HELPER_X2(xscvsxdsp, 0x10, 0x13, 0, PPC2_VSX207) GEN_VSX_HELPER_X2(xscvuxdsp, 0x10, 0x12, 0, PPC2_VSX207) -GEN_VSX_HELPER_X1(xststdcsp, 0x14, 0x12, 0, PPC2_ISA300) -GEN_VSX_HELPER_2(xststdcdp, 0x14, 0x16, 0, PPC2_ISA300) -GEN_VSX_HELPER_2(xststdcqp, 0x04, 0x16, 0, PPC2_ISA300) GEN_VSX_HELPER_X3(xvadddp, 0x00, 0x0C, 0, PPC2_VSX) GEN_VSX_HELPER_X3(xvsubdp, 0x00, 0x0D, 0, PPC2_VSX) @@ -1156,8 +1296,6 @@ GEN_VSX_HELPER_X2(xvrspic, 0x16, 0x0A, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrspim, 0x12, 0x0B, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrspip, 0x12, 0x0A, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrspiz, 0x12, 0x09, 0, PPC2_VSX) -GEN_VSX_HELPER_2(xvtstdcsp, 0x14, 0x1A, 0, PPC2_VSX) -GEN_VSX_HELPER_2(xvtstdcdp, 0x14, 0x1E, 0, PPC2_VSX) static bool trans_XXPERM(DisasContext *ctx, arg_XX3 *a) { @@ -1171,11 +1309,6 @@ static bool trans_XXPERM(DisasContext *ctx, arg_XX3 *a) xb = gen_vsr_ptr(a->xb); gen_helper_VPERM(xt, xa, xt, xb); - - tcg_temp_free_ptr(xt); - tcg_temp_free_ptr(xa); - tcg_temp_free_ptr(xb); - return true; } @@ -1191,11 +1324,6 @@ static bool trans_XXPERMR(DisasContext *ctx, arg_XX3 *a) xb = gen_vsr_ptr(a->xb); gen_helper_VPERMR(xt, xa, xt, xb); - - tcg_temp_free_ptr(xt); - tcg_temp_free_ptr(xa); - tcg_temp_free_ptr(xb); - return true; } @@ -1216,8 +1344,6 @@ static bool trans_XXPERMDI(DisasContext *ctx, arg_XX3_dm *a) set_cpu_vsr(a->xt, t0, true); set_cpu_vsr(a->xt, t1, false); - - tcg_temp_free_i64(t1); } else { get_cpu_vsr(t0, a->xa, (a->dm & 2) == 0); set_cpu_vsr(a->xt, t0, true); @@ -1225,9 +1351,6 @@ static bool trans_XXPERMDI(DisasContext *ctx, arg_XX3_dm *a) get_cpu_vsr(t0, a->xb, (a->dm & 1) == 0); set_cpu_vsr(a->xt, t0, false); } - - tcg_temp_free_i64(t0); - return true; } @@ -1244,12 +1367,6 @@ static bool trans_XXPERMX(DisasContext *ctx, arg_8RR_XX4_uim3 *a) xc = gen_vsr_ptr(a->xc); gen_helper_XXPERMX(xt, xa, xb, xc, tcg_constant_tl(a->uim3)); - - tcg_temp_free_ptr(xt); - tcg_temp_free_ptr(xa); - tcg_temp_free_ptr(xb); - tcg_temp_free_ptr(xc); - return true; } @@ -1272,10 +1389,6 @@ static bool do_xxgenpcv(DisasContext *ctx, arg_X_imm5 *a, vrb = gen_avr_ptr(a->vrb); fn[a->imm](xt, vrb); - - tcg_temp_free_ptr(xt); - tcg_temp_free_ptr(vrb); - return true; } @@ -1307,13 +1420,7 @@ static bool do_xsmadd(DisasContext *ctx, int tgt, int src1, int src2, int src3, s2 = gen_vsr_ptr(src2); s3 = gen_vsr_ptr(src3); - gen_helper(cpu_env, t, s1, s2, s3); - - tcg_temp_free_ptr(t); - tcg_temp_free_ptr(s1); - tcg_temp_free_ptr(s2); - tcg_temp_free_ptr(s3); - + gen_helper(tcg_env, t, s1, s2, s3); return true; } @@ -1393,11 +1500,7 @@ static void gen_##name(DisasContext *ctx) \ s2 = gen_vsr_ptr(xT(ctx->opcode)); \ s3 = gen_vsr_ptr(xB(ctx->opcode)); \ } \ - gen_helper_##name(cpu_env, xt, s1, s2, s3); \ - tcg_temp_free_ptr(xt); \ - tcg_temp_free_ptr(s1); \ - tcg_temp_free_ptr(s2); \ - tcg_temp_free_ptr(s3); \ + gen_helper_##name(tcg_env, xt, s1, s2, s3); \ } GEN_VSX_HELPER_VSX_MADD(xvmadddp, 0x04, 0x0C, 0x0D, 0, PPC2_VSX) @@ -1431,11 +1534,6 @@ static void gen_xxbrd(DisasContext *ctx) tcg_gen_bswap64_i64(xtl, xbl); set_cpu_vsr(xT(ctx->opcode), xth, true); set_cpu_vsr(xT(ctx->opcode), xtl, false); - - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); - tcg_temp_free_i64(xbh); - tcg_temp_free_i64(xbl); } static void gen_xxbrh(DisasContext *ctx) @@ -1459,11 +1557,6 @@ static void gen_xxbrh(DisasContext *ctx) gen_bswap16x8(xth, xtl, xbh, xbl); set_cpu_vsr(xT(ctx->opcode), xth, true); set_cpu_vsr(xT(ctx->opcode), xtl, false); - - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); - tcg_temp_free_i64(xbh); - tcg_temp_free_i64(xbl); } static void gen_xxbrq(DisasContext *ctx) @@ -1491,12 +1584,6 @@ static void gen_xxbrq(DisasContext *ctx) set_cpu_vsr(xT(ctx->opcode), xtl, false); tcg_gen_mov_i64(xth, t0); set_cpu_vsr(xT(ctx->opcode), xth, true); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); - tcg_temp_free_i64(xbh); - tcg_temp_free_i64(xbl); } static void gen_xxbrw(DisasContext *ctx) @@ -1520,11 +1607,6 @@ static void gen_xxbrw(DisasContext *ctx) gen_bswap32x4(xth, xtl, xbh, xbl); set_cpu_vsr(xT(ctx->opcode), xth, true); set_cpu_vsr(xT(ctx->opcode), xtl, false); - - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); - tcg_temp_free_i64(xbh); - tcg_temp_free_i64(xbl); } #define VSX_LOGICAL(name, vece, tcg_op) \ @@ -1571,11 +1653,6 @@ static void glue(gen_, name)(DisasContext *ctx) \ set_cpu_vsr(xT(ctx->opcode), tmp, true); \ tcg_gen_deposit_i64(tmp, b1, a1, 32, 32); \ set_cpu_vsr(xT(ctx->opcode), tmp, false); \ - tcg_temp_free_i64(a0); \ - tcg_temp_free_i64(a1); \ - tcg_temp_free_i64(b0); \ - tcg_temp_free_i64(b1); \ - tcg_temp_free_i64(tmp); \ } VSX_XXMRG(xxmrghw, 1) @@ -1651,9 +1728,9 @@ static bool trans_XXSPLTI32DX(DisasContext *ctx, arg_8RR_D_IX *a) imm = tcg_constant_i32(a->si); - tcg_gen_st_i32(imm, cpu_env, + tcg_gen_st_i32(imm, tcg_env, offsetof(CPUPPCState, vsr[a->xt].VsrW(0 + a->ix))); - tcg_gen_st_i32(imm, cpu_env, + tcg_gen_st_i32(imm, tcg_env, offsetof(CPUPPCState, vsr[a->xt].VsrW(2 + a->ix))); return true; @@ -1732,13 +1809,6 @@ static bool trans_XVTLSBB(DisasContext *ctx, arg_XX2_bf_xb *a) tcg_gen_or_i64(t0, all_false, all_true); tcg_gen_extrl_i64_i32(cpu_crf[a->bf], t0); - - tcg_temp_free_i64(xb); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(all_true); - tcg_temp_free_i64(all_false); - return true; } @@ -1770,7 +1840,6 @@ static void gen_xxsldwi(DisasContext *ctx) get_cpu_vsr(t0, xB(ctx->opcode), true); tcg_gen_shri_i64(t0, t0, 32); tcg_gen_or_i64(xtl, xtl, t0); - tcg_temp_free_i64(t0); break; } case 2: { @@ -1790,16 +1859,12 @@ static void gen_xxsldwi(DisasContext *ctx) get_cpu_vsr(t0, xB(ctx->opcode), false); tcg_gen_shri_i64(t0, t0, 32); tcg_gen_or_i64(xtl, xtl, t0); - tcg_temp_free_i64(t0); break; } } set_cpu_vsr(xT(ctx->opcode), xth, true); set_cpu_vsr(xT(ctx->opcode), xtl, false); - - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); } static bool do_vsx_extract_insert(DisasContext *ctx, arg_XX2_uim *a, @@ -1822,10 +1887,7 @@ static bool do_vsx_extract_insert(DisasContext *ctx, arg_XX2_uim *a, xt = gen_vsr_ptr(a->xt); xb = gen_vsr_ptr(a->xb); gen_helper(xt, xb, tcg_constant_i32(a->uim)); - tcg_temp_free_ptr(xb); - tcg_temp_free_ptr(xt); } - return true; } @@ -1844,7 +1906,6 @@ static void gen_xsxexpdp(DisasContext *ctx) t0 = tcg_temp_new_i64(); get_cpu_vsr(t0, xB(ctx->opcode), true); tcg_gen_extract_i64(rt, t0, 52, 11); - tcg_temp_free_i64(t0); } static void gen_xsxexpqp(DisasContext *ctx) @@ -1866,10 +1927,6 @@ static void gen_xsxexpqp(DisasContext *ctx) set_cpu_vsr(rD(ctx->opcode) + 32, xth, true); tcg_gen_movi_i64(xtl, 0); set_cpu_vsr(rD(ctx->opcode) + 32, xtl, false); - - tcg_temp_free_i64(xbh); - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); } static void gen_xsiexpdp(DisasContext *ctx) @@ -1891,8 +1948,6 @@ static void gen_xsiexpdp(DisasContext *ctx) tcg_gen_or_i64(xth, xth, t0); set_cpu_vsr(xT(ctx->opcode), xth, true); set_cpu_vsr(xT(ctx->opcode), tcg_constant_i64(0), false); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(xth); } static void gen_xsiexpqp(DisasContext *ctx) @@ -1925,13 +1980,6 @@ static void gen_xsiexpqp(DisasContext *ctx) set_cpu_vsr(rD(ctx->opcode) + 32, xth, true); tcg_gen_mov_i64(xtl, xal); set_cpu_vsr(rD(ctx->opcode) + 32, xtl, false); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); - tcg_temp_free_i64(xah); - tcg_temp_free_i64(xal); - tcg_temp_free_i64(xbh); } static void gen_xsxsigdp(DisasContext *ctx) @@ -1946,8 +1994,8 @@ static void gen_xsxsigdp(DisasContext *ctx) exp = tcg_temp_new_i64(); t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); - zr = tcg_const_i64(0); - nan = tcg_const_i64(2047); + zr = tcg_constant_i64(0); + nan = tcg_constant_i64(2047); get_cpu_vsr(t1, xB(ctx->opcode), true); tcg_gen_extract_i64(exp, t1, 52, 11); @@ -1956,12 +2004,6 @@ static void gen_xsxsigdp(DisasContext *ctx) tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0); get_cpu_vsr(t1, xB(ctx->opcode), true); tcg_gen_deposit_i64(rt, t0, t1, 0, 52); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(exp); - tcg_temp_free_i64(zr); - tcg_temp_free_i64(nan); } static void gen_xsxsigqp(DisasContext *ctx) @@ -1984,8 +2026,8 @@ static void gen_xsxsigqp(DisasContext *ctx) get_cpu_vsr(xbl, rB(ctx->opcode) + 32, false); exp = tcg_temp_new_i64(); t0 = tcg_temp_new_i64(); - zr = tcg_const_i64(0); - nan = tcg_const_i64(32767); + zr = tcg_constant_i64(0); + nan = tcg_constant_i64(32767); tcg_gen_extract_i64(exp, xbh, 48, 15); tcg_gen_movi_i64(t0, 0x0001000000000000); @@ -1995,15 +2037,6 @@ static void gen_xsxsigqp(DisasContext *ctx) set_cpu_vsr(rD(ctx->opcode) + 32, xth, true); tcg_gen_mov_i64(xtl, xbl); set_cpu_vsr(rD(ctx->opcode) + 32, xtl, false); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(exp); - tcg_temp_free_i64(zr); - tcg_temp_free_i64(nan); - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); - tcg_temp_free_i64(xbh); - tcg_temp_free_i64(xbl); } #endif @@ -2043,14 +2076,6 @@ static void gen_xviexpsp(DisasContext *ctx) tcg_gen_shli_i64(t0, t0, 23); tcg_gen_or_i64(xtl, xtl, t0); set_cpu_vsr(xT(ctx->opcode), xtl, false); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); - tcg_temp_free_i64(xah); - tcg_temp_free_i64(xal); - tcg_temp_free_i64(xbh); - tcg_temp_free_i64(xbl); } static void gen_xviexpdp(DisasContext *ctx) @@ -2082,13 +2107,6 @@ static void gen_xviexpdp(DisasContext *ctx) tcg_gen_deposit_i64(xtl, xal, xbl, 52, 11); set_cpu_vsr(xT(ctx->opcode), xtl, false); - - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); - tcg_temp_free_i64(xah); - tcg_temp_free_i64(xal); - tcg_temp_free_i64(xbh); - tcg_temp_free_i64(xbl); } static void gen_xvxexpsp(DisasContext *ctx) @@ -2115,11 +2133,6 @@ static void gen_xvxexpsp(DisasContext *ctx) tcg_gen_shri_i64(xtl, xbl, 23); tcg_gen_andi_i64(xtl, xtl, 0xFF000000FF); set_cpu_vsr(xT(ctx->opcode), xtl, false); - - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); - tcg_temp_free_i64(xbh); - tcg_temp_free_i64(xbl); } static void gen_xvxexpdp(DisasContext *ctx) @@ -2144,11 +2157,6 @@ static void gen_xvxexpdp(DisasContext *ctx) set_cpu_vsr(xT(ctx->opcode), xth, true); tcg_gen_extract_i64(xtl, xbl, 52, 11); set_cpu_vsr(xT(ctx->opcode), xtl, false); - - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); - tcg_temp_free_i64(xbh); - tcg_temp_free_i64(xbl); } static bool trans_XVXSIGSP(DisasContext *ctx, arg_XX2 *a) @@ -2162,10 +2170,6 @@ static bool trans_XVXSIGSP(DisasContext *ctx, arg_XX2 *a) b = gen_vsr_ptr(a->xb); gen_helper_XVXSIGSP(t, b); - - tcg_temp_free_ptr(t); - tcg_temp_free_ptr(b); - return true; } @@ -2189,8 +2193,8 @@ static void gen_xvxsigdp(DisasContext *ctx) get_cpu_vsr(xbl, xB(ctx->opcode), false); exp = tcg_temp_new_i64(); t0 = tcg_temp_new_i64(); - zr = tcg_const_i64(0); - nan = tcg_const_i64(2047); + zr = tcg_constant_i64(0); + nan = tcg_constant_i64(2047); tcg_gen_extract_i64(exp, xbh, 52, 11); tcg_gen_movi_i64(t0, 0x0010000000000000); @@ -2205,15 +2209,6 @@ static void gen_xvxsigdp(DisasContext *ctx) tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0); tcg_gen_deposit_i64(xtl, t0, xbl, 0, 52); set_cpu_vsr(xT(ctx->opcode), xtl, false); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(exp); - tcg_temp_free_i64(zr); - tcg_temp_free_i64(nan); - tcg_temp_free_i64(xth); - tcg_temp_free_i64(xtl); - tcg_temp_free_i64(xbh); - tcg_temp_free_i64(xbl); } static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ, @@ -2268,15 +2263,12 @@ static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ, set_cpu_vsr(rt2, xt, ctx->le_mode); } } - - tcg_temp_free(ea); - tcg_temp_free_i64(xt); return true; } static bool do_lstxv_D(DisasContext *ctx, arg_D *a, bool store, bool paired) { - if (paired || a->rt >= 32) { + if (paired || a->rt < 32) { REQUIRE_VSX(ctx); } else { REQUIRE_VECTOR(ctx); @@ -2335,10 +2327,6 @@ static bool do_lstxsd(DisasContext *ctx, int rt, int ra, TCGv displ, bool store) set_cpu_vsr(rt + 32, xt, true); set_cpu_vsr(rt + 32, tcg_constant_i64(0), false); } - - tcg_temp_free(ea); - tcg_temp_free_i64(xt); - return true; } @@ -2378,10 +2366,6 @@ static bool do_lstxssp(DisasContext *ctx, int rt, int ra, TCGv displ, bool store set_cpu_vsr(rt + 32, xt, true); set_cpu_vsr(rt + 32, tcg_constant_i64(0), false); } - - tcg_temp_free(ea); - tcg_temp_free_i64(xt); - return true; } @@ -2442,9 +2426,6 @@ static bool do_lstrm(DisasContext *ctx, arg_X *a, MemOp mop, bool store) set_cpu_vsr(a->rt, xt, false); set_cpu_vsr(a->rt, tcg_constant_i64(0), true); } - - tcg_temp_free(ea); - tcg_temp_free_i64(xt); return true; } @@ -2468,7 +2449,8 @@ static void gen_xxeval_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b, TCGv_i64 c, TCGv_i64 conj, disj; conj = tcg_temp_new_i64(); - disj = tcg_const_i64(0); + disj = tcg_temp_new_i64(); + tcg_gen_movi_i64(disj, 0); /* Iterate over set bits from the least to the most significant bit */ while (imm) { @@ -2499,9 +2481,6 @@ static void gen_xxeval_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b, TCGv_i64 c, } tcg_gen_mov_i64(t, disj); - - tcg_temp_free_i64(conj); - tcg_temp_free_i64(disj); } static void gen_xxeval_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, @@ -2514,8 +2493,9 @@ static void gen_xxeval_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, int bit; TCGv_vec disj, conj; - disj = tcg_const_zeros_vec_matching(t); conj = tcg_temp_new_vec_matching(t); + disj = tcg_temp_new_vec_matching(t); + tcg_gen_dupi_vec(vece, disj, 0); /* Iterate over set bits from the least to the most significant bit */ while (imm) { @@ -2546,9 +2526,6 @@ static void gen_xxeval_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, } tcg_gen_mov_vec(t, disj); - - tcg_temp_free_vec(disj); - tcg_temp_free_vec(conj); } static bool trans_XXEVAL(DisasContext *ctx, arg_8RR_XX4_imm *a) @@ -2571,7 +2548,7 @@ static bool trans_XXEVAL(DisasContext *ctx, arg_8RR_XX4_imm *a) /* Equivalent functions that can be implemented with a single gen_gvec */ switch (a->imm) { - case 0b00000000: /* true */ + case 0b00000000: /* false */ set_cpu_vsr(a->xt, tcg_constant_i64(0), true); set_cpu_vsr(a->xt, tcg_constant_i64(0), false); break; @@ -2683,7 +2660,6 @@ static void gen_xxblendv_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, TCGv_vec tmp = tcg_temp_new_vec_matching(c); tcg_gen_sari_vec(vece, tmp, c, (8 << vece) - 1); tcg_gen_bitsel_vec(vece, t, tmp, b, a); - tcg_temp_free_vec(tmp); } static bool do_xxblendv(DisasContext *ctx, arg_8RR_XX4 *a, unsigned vece) @@ -2744,12 +2720,7 @@ static bool do_helper_XX3(DisasContext *ctx, arg_XX3 *a, xa = gen_vsr_ptr(a->xa); xb = gen_vsr_ptr(a->xb); - helper(cpu_env, xt, xa, xb); - - tcg_temp_free_ptr(xt); - tcg_temp_free_ptr(xa); - tcg_temp_free_ptr(xb); - + helper(tcg_env, xt, xa, xb); return true; } @@ -2770,12 +2741,7 @@ static bool do_helper_X(arg_X *a, ra = gen_avr_ptr(a->ra); rb = gen_avr_ptr(a->rb); - helper(cpu_env, rt, ra, rb); - - tcg_temp_free_ptr(rt); - tcg_temp_free_ptr(ra); - tcg_temp_free_ptr(rb); - + helper(tcg_env, rt, ra, rb); return true; } @@ -2804,11 +2770,7 @@ static bool trans_XVCVSPBF16(DisasContext *ctx, arg_XX2 *a) xt = gen_vsr_ptr(a->xt); xb = gen_vsr_ptr(a->xb); - gen_helper_XVCVSPBF16(cpu_env, xt, xb); - - tcg_temp_free_ptr(xt); - tcg_temp_free_ptr(xb); - + gen_helper_XVCVSPBF16(tcg_env, xt, xb); return true; } @@ -2871,10 +2833,7 @@ static bool do_ger(DisasContext *ctx, arg_MMIRR_XX3 *a, xb = gen_vsr_ptr(a->xb); mask = ger_pack_masks(a->pmsk, a->ymsk, a->xmsk); - helper(cpu_env, xa, xb, xt, tcg_constant_i32(mask)); - tcg_temp_free_ptr(xt); - tcg_temp_free_ptr(xa); - tcg_temp_free_ptr(xb); + helper(tcg_env, xa, xb, xt, tcg_constant_i32(mask)); return true; } diff --git a/target/ppc/translate/vsx-ops.c.inc b/target/ppc/translate/vsx-ops.c.inc index bff14bbece..a3ba094d62 100644 --- a/target/ppc/translate/vsx-ops.c.inc +++ b/target/ppc/translate/vsx-ops.c.inc @@ -147,33 +147,12 @@ GEN_HANDLER_E(xsiexpdp, 0x3C, 0x16, 0x1C, 0, PPC_NONE, PPC2_ISA300), GEN_VSX_XFORM_300(xsiexpqp, 0x4, 0x1B, 0x00000001), #endif -GEN_XX2FORM(xststdcdp, 0x14, 0x16, PPC2_ISA300), -GEN_XX2FORM(xststdcsp, 0x14, 0x12, PPC2_ISA300), -GEN_VSX_XFORM_300(xststdcqp, 0x04, 0x16, 0x00000001), - GEN_XX3FORM(xviexpsp, 0x00, 0x1B, PPC2_ISA300), GEN_XX3FORM(xviexpdp, 0x00, 0x1F, PPC2_ISA300), GEN_XX2FORM_EO(xvxexpdp, 0x16, 0x1D, 0x00, PPC2_ISA300), GEN_XX2FORM_EO(xvxsigdp, 0x16, 0x1D, 0x01, PPC2_ISA300), GEN_XX2FORM_EO(xvxexpsp, 0x16, 0x1D, 0x08, PPC2_ISA300), -/* DCMX = bit[25] << 6 | bit[29] << 5 | bit[11:15] */ -#define GEN_XX2FORM_DCMX(name, opc2, opc3, fl2) \ -GEN_XX3FORM(name, opc2, opc3 | 0, fl2), \ -GEN_XX3FORM(name, opc2, opc3 | 1, fl2) - -GEN_XX2FORM_DCMX(xvtstdcdp, 0x14, 0x1E, PPC2_ISA300), -GEN_XX2FORM_DCMX(xvtstdcsp, 0x14, 0x1A, PPC2_ISA300), - -GEN_XX2FORM(xvabsdp, 0x12, 0x1D, PPC2_VSX), -GEN_XX2FORM(xvnabsdp, 0x12, 0x1E, PPC2_VSX), -GEN_XX2FORM(xvnegdp, 0x12, 0x1F, PPC2_VSX), -GEN_XX3FORM(xvcpsgndp, 0x00, 0x1E, PPC2_VSX), -GEN_XX2FORM(xvabssp, 0x12, 0x19, PPC2_VSX), -GEN_XX2FORM(xvnabssp, 0x12, 0x1A, PPC2_VSX), -GEN_XX2FORM(xvnegsp, 0x12, 0x1B, PPC2_VSX), -GEN_XX3FORM(xvcpsgnsp, 0x00, 0x1A, PPC2_VSX), - GEN_XX3FORM(xsadddp, 0x00, 0x04, PPC2_VSX), GEN_VSX_XFORM_300(xsaddqp, 0x04, 0x00, 0x0), GEN_XX3FORM(xssubdp, 0x00, 0x05, PPC2_VSX), diff --git a/target/ppc/user_only_helper.c b/target/ppc/user_only_helper.c index 7ff76f7a06..a4d07a0d0d 100644 --- a/target/ppc/user_only_helper.c +++ b/target/ppc/user_only_helper.c @@ -27,8 +27,7 @@ void ppc_cpu_record_sigsegv(CPUState *cs, vaddr address, MMUAccessType access_type, bool maperr, uintptr_t retaddr) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); int exception, error_code; /* diff --git a/target/riscv/Kconfig b/target/riscv/Kconfig index b9e5932f13..adb7de3f37 100644 --- a/target/riscv/Kconfig +++ b/target/riscv/Kconfig @@ -1,5 +1,7 @@ config RISCV32 bool + select ARM_COMPATIBLE_SEMIHOSTING # for do_common_semihosting() config RISCV64 bool + select ARM_COMPATIBLE_SEMIHOSTING # for do_common_semihosting() diff --git a/target/riscv/arch_dump.c b/target/riscv/arch_dump.c index 709f621d82..434c8a3dbb 100644 --- a/target/riscv/arch_dump.c +++ b/target/riscv/arch_dump.c @@ -1,4 +1,5 @@ -/* Support for writing ELF notes for RISC-V architectures +/* + * Support for writing ELF notes for RISC-V architectures * * Copyright (C) 2021 Huawei Technologies Co., Ltd * @@ -64,12 +65,11 @@ static void riscv64_note_init(struct riscv64_note *note, DumpState *s, } int riscv_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque) + int cpuid, DumpState *s) { struct riscv64_note note; RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - DumpState *s = opaque; int ret, i = 0; const char name[] = "CORE"; @@ -134,12 +134,11 @@ static void riscv32_note_init(struct riscv32_note *note, DumpState *s, } int riscv_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque) + int cpuid, DumpState *s) { struct riscv32_note note; RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - DumpState *s = opaque; int ret, i; const char name[] = "CORE"; @@ -182,8 +181,8 @@ int cpu_get_dump_info(ArchDumpInfo *info, info->d_class = ELFCLASS32; #endif - info->d_endian = (env->mstatus & MSTATUS_UBE) != 0 - ? ELFDATA2MSB : ELFDATA2LSB; + info->d_endian = (env->mstatus & MSTATUS_UBE) != 0 ? + ELFDATA2MSB : ELFDATA2LSB; return 0; } diff --git a/target/riscv/common-semi-target.h b/target/riscv/common-semi-target.h new file mode 100644 index 0000000000..7c8a59e0cc --- /dev/null +++ b/target/riscv/common-semi-target.h @@ -0,0 +1,50 @@ +/* + * Target-specific parts of semihosting/arm-compat-semi.c. + * + * Copyright (c) 2005, 2007 CodeSourcery. + * Copyright (c) 2019, 2022 Linaro + * Copyright © 2020 by Keith Packard + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TARGET_RISCV_COMMON_SEMI_TARGET_H +#define TARGET_RISCV_COMMON_SEMI_TARGET_H + +static inline target_ulong common_semi_arg(CPUState *cs, int argno) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + return env->gpr[xA0 + argno]; +} + +static inline void common_semi_set_ret(CPUState *cs, target_ulong ret) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + env->gpr[xA0] = ret; +} + +static inline bool common_semi_sys_exit_extended(CPUState *cs, int nr) +{ + return (nr == TARGET_SYS_EXIT_EXTENDED || sizeof(target_ulong) == 8); +} + +static inline bool is_64bit_semihosting(CPUArchState *env) +{ + return riscv_cpu_mxl(env) != MXL_RV32; +} + +static inline target_ulong common_semi_stack_bottom(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + return env->gpr[xSP]; +} + +static inline bool common_semi_has_synccache(CPUArchState *env) +{ + return true; +} + +#endif diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h index ebaf26d26d..b2a9396dec 100644 --- a/target/riscv/cpu-param.h +++ b/target/riscv/cpu-param.h @@ -27,6 +27,5 @@ * - S mode HLV/HLVX/HSV 0b101 * - M mode HLV/HLVX/HSV 0b111 */ -#define NB_MMU_MODES 8 #endif diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h new file mode 100644 index 0000000000..3670cfe6d9 --- /dev/null +++ b/target/riscv/cpu-qom.h @@ -0,0 +1,56 @@ +/* + * QEMU RISC-V CPU QOM header (target agnostic) + * + * Copyright (c) 2023 Ventana Micro Systems 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 RISCV_CPU_QOM_H +#define RISCV_CPU_QOM_H + +#include "hw/core/cpu.h" + +#define TYPE_RISCV_CPU "riscv-cpu" +#define TYPE_RISCV_DYNAMIC_CPU "riscv-dynamic-cpu" +#define TYPE_RISCV_VENDOR_CPU "riscv-vendor-cpu" +#define TYPE_RISCV_BARE_CPU "riscv-bare-cpu" + +#define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU +#define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX) + +#define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any") +#define TYPE_RISCV_CPU_MAX RISCV_CPU_TYPE_NAME("max") +#define TYPE_RISCV_CPU_BASE32 RISCV_CPU_TYPE_NAME("rv32") +#define TYPE_RISCV_CPU_BASE64 RISCV_CPU_TYPE_NAME("rv64") +#define TYPE_RISCV_CPU_BASE128 RISCV_CPU_TYPE_NAME("x-rv128") +#define TYPE_RISCV_CPU_RV32I RISCV_CPU_TYPE_NAME("rv32i") +#define TYPE_RISCV_CPU_RV32E RISCV_CPU_TYPE_NAME("rv32e") +#define TYPE_RISCV_CPU_RV64I RISCV_CPU_TYPE_NAME("rv64i") +#define TYPE_RISCV_CPU_RV64E RISCV_CPU_TYPE_NAME("rv64e") +#define TYPE_RISCV_CPU_RVA22U64 RISCV_CPU_TYPE_NAME("rva22u64") +#define TYPE_RISCV_CPU_RVA22S64 RISCV_CPU_TYPE_NAME("rva22s64") +#define TYPE_RISCV_CPU_IBEX RISCV_CPU_TYPE_NAME("lowrisc-ibex") +#define TYPE_RISCV_CPU_SHAKTI_C RISCV_CPU_TYPE_NAME("shakti-c") +#define TYPE_RISCV_CPU_SIFIVE_E31 RISCV_CPU_TYPE_NAME("sifive-e31") +#define TYPE_RISCV_CPU_SIFIVE_E34 RISCV_CPU_TYPE_NAME("sifive-e34") +#define TYPE_RISCV_CPU_SIFIVE_E51 RISCV_CPU_TYPE_NAME("sifive-e51") +#define TYPE_RISCV_CPU_SIFIVE_U34 RISCV_CPU_TYPE_NAME("sifive-u34") +#define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54") +#define TYPE_RISCV_CPU_THEAD_C906 RISCV_CPU_TYPE_NAME("thead-c906") +#define TYPE_RISCV_CPU_VEYRON_V1 RISCV_CPU_TYPE_NAME("veyron-v1") +#define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host") + +OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU) + +#endif /* RISCV_CPU_QOM_H */ diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index a91253d4bd..36e3e5fdaf 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -22,54 +22,241 @@ #include "qemu/ctype.h" #include "qemu/log.h" #include "cpu.h" +#include "cpu_vendorid.h" #include "internals.h" #include "exec/exec-all.h" #include "qapi/error.h" +#include "qapi/visitor.h" #include "qemu/error-report.h" #include "hw/qdev-properties.h" +#include "hw/core/qdev-prop-internal.h" #include "migration/vmstate.h" #include "fpu/softfloat-helpers.h" +#include "sysemu/device_tree.h" #include "sysemu/kvm.h" -#include "kvm_riscv.h" +#include "sysemu/tcg.h" +#include "kvm/kvm_riscv.h" +#include "tcg/tcg-cpu.h" +#include "tcg/tcg.h" /* RISC-V CPU definitions */ +static const char riscv_single_letter_exts[] = "IEMAFDQCBPVH"; +const uint32_t misa_bits[] = {RVI, RVE, RVM, RVA, RVF, RVD, RVV, + RVC, RVS, RVU, RVH, RVJ, RVG, RVB, 0}; -#define RISCV_CPU_MARCHID ((QEMU_VERSION_MAJOR << 16) | \ - (QEMU_VERSION_MINOR << 8) | \ - (QEMU_VERSION_MICRO)) -#define RISCV_CPU_MIMPID RISCV_CPU_MARCHID +/* + * From vector_helper.c + * Note that vector data is stored in host-endian 64-bit chunks, + * so addressing bytes needs a host-endian fixup. + */ +#if HOST_BIG_ENDIAN +#define BYTE(x) ((x) ^ 7) +#else +#define BYTE(x) (x) +#endif -static const char riscv_single_letter_exts[] = "IEMAFDQCPVH"; +bool riscv_cpu_is_32bit(RISCVCPU *cpu) +{ + return riscv_cpu_mxl(&cpu->env) == MXL_RV32; +} -struct isa_ext_data { - const char *name; - bool enabled; +/* Hash that stores general user set numeric options */ +static GHashTable *general_user_opts; + +static void cpu_option_add_user_setting(const char *optname, uint32_t value) +{ + g_hash_table_insert(general_user_opts, (gpointer)optname, + GUINT_TO_POINTER(value)); +} + +bool riscv_cpu_option_set(const char *optname) +{ + return g_hash_table_contains(general_user_opts, optname); +} + +#define ISA_EXT_DATA_ENTRY(_name, _min_ver, _prop) \ + {#_name, _min_ver, CPU_CFG_OFFSET(_prop)} + +/* + * Here are the ordering rules of extension naming defined by RISC-V + * specification : + * 1. All extensions should be separated from other multi-letter extensions + * by an underscore. + * 2. The first letter following the 'Z' conventionally indicates the most + * closely related alphabetical extension category, IMAFDQLCBKJTPVH. + * If multiple 'Z' extensions are named, they should be ordered first + * by category, then alphabetically within a category. + * 3. Standard supervisor-level extensions (starts with 'S') should be + * listed after standard unprivileged extensions. If multiple + * supervisor-level extensions are listed, they should be ordered + * alphabetically. + * 4. Non-standard extensions (starts with 'X') must be listed after all + * standard extensions. They must be separated from other multi-letter + * extensions by an underscore. + * + * Single letter extensions are checked in riscv_cpu_validate_misa_priv() + * instead. + */ +const RISCVIsaExtData isa_edata_arr[] = { + ISA_EXT_DATA_ENTRY(zic64b, PRIV_VERSION_1_12_0, ext_zic64b), + ISA_EXT_DATA_ENTRY(zicbom, PRIV_VERSION_1_12_0, ext_zicbom), + ISA_EXT_DATA_ENTRY(zicbop, PRIV_VERSION_1_12_0, ext_zicbop), + ISA_EXT_DATA_ENTRY(zicboz, PRIV_VERSION_1_12_0, ext_zicboz), + ISA_EXT_DATA_ENTRY(ziccamoa, PRIV_VERSION_1_11_0, has_priv_1_11), + ISA_EXT_DATA_ENTRY(ziccif, PRIV_VERSION_1_11_0, has_priv_1_11), + ISA_EXT_DATA_ENTRY(zicclsm, PRIV_VERSION_1_11_0, has_priv_1_11), + ISA_EXT_DATA_ENTRY(ziccrse, PRIV_VERSION_1_11_0, has_priv_1_11), + ISA_EXT_DATA_ENTRY(zicond, PRIV_VERSION_1_12_0, ext_zicond), + ISA_EXT_DATA_ENTRY(zicntr, PRIV_VERSION_1_12_0, ext_zicntr), + ISA_EXT_DATA_ENTRY(zicsr, PRIV_VERSION_1_10_0, ext_zicsr), + ISA_EXT_DATA_ENTRY(zifencei, PRIV_VERSION_1_10_0, ext_zifencei), + ISA_EXT_DATA_ENTRY(zihintntl, PRIV_VERSION_1_10_0, ext_zihintntl), + ISA_EXT_DATA_ENTRY(zihintpause, PRIV_VERSION_1_10_0, ext_zihintpause), + ISA_EXT_DATA_ENTRY(zihpm, PRIV_VERSION_1_12_0, ext_zihpm), + ISA_EXT_DATA_ENTRY(zmmul, PRIV_VERSION_1_12_0, ext_zmmul), + ISA_EXT_DATA_ENTRY(za64rs, PRIV_VERSION_1_12_0, has_priv_1_11), + ISA_EXT_DATA_ENTRY(zaamo, PRIV_VERSION_1_12_0, ext_zaamo), + ISA_EXT_DATA_ENTRY(zacas, PRIV_VERSION_1_12_0, ext_zacas), + ISA_EXT_DATA_ENTRY(zalrsc, PRIV_VERSION_1_12_0, ext_zalrsc), + ISA_EXT_DATA_ENTRY(zawrs, PRIV_VERSION_1_12_0, ext_zawrs), + ISA_EXT_DATA_ENTRY(zfa, PRIV_VERSION_1_12_0, ext_zfa), + ISA_EXT_DATA_ENTRY(zfbfmin, PRIV_VERSION_1_12_0, ext_zfbfmin), + ISA_EXT_DATA_ENTRY(zfh, PRIV_VERSION_1_11_0, ext_zfh), + ISA_EXT_DATA_ENTRY(zfhmin, PRIV_VERSION_1_11_0, ext_zfhmin), + ISA_EXT_DATA_ENTRY(zfinx, PRIV_VERSION_1_12_0, ext_zfinx), + ISA_EXT_DATA_ENTRY(zdinx, PRIV_VERSION_1_12_0, ext_zdinx), + ISA_EXT_DATA_ENTRY(zca, PRIV_VERSION_1_12_0, ext_zca), + ISA_EXT_DATA_ENTRY(zcb, PRIV_VERSION_1_12_0, ext_zcb), + ISA_EXT_DATA_ENTRY(zcf, PRIV_VERSION_1_12_0, ext_zcf), + ISA_EXT_DATA_ENTRY(zcd, PRIV_VERSION_1_12_0, ext_zcd), + ISA_EXT_DATA_ENTRY(zce, PRIV_VERSION_1_12_0, ext_zce), + ISA_EXT_DATA_ENTRY(zcmp, PRIV_VERSION_1_12_0, ext_zcmp), + ISA_EXT_DATA_ENTRY(zcmt, PRIV_VERSION_1_12_0, ext_zcmt), + ISA_EXT_DATA_ENTRY(zba, PRIV_VERSION_1_12_0, ext_zba), + ISA_EXT_DATA_ENTRY(zbb, PRIV_VERSION_1_12_0, ext_zbb), + ISA_EXT_DATA_ENTRY(zbc, PRIV_VERSION_1_12_0, ext_zbc), + ISA_EXT_DATA_ENTRY(zbkb, PRIV_VERSION_1_12_0, ext_zbkb), + ISA_EXT_DATA_ENTRY(zbkc, PRIV_VERSION_1_12_0, ext_zbkc), + ISA_EXT_DATA_ENTRY(zbkx, PRIV_VERSION_1_12_0, ext_zbkx), + ISA_EXT_DATA_ENTRY(zbs, PRIV_VERSION_1_12_0, ext_zbs), + ISA_EXT_DATA_ENTRY(zk, PRIV_VERSION_1_12_0, ext_zk), + ISA_EXT_DATA_ENTRY(zkn, PRIV_VERSION_1_12_0, ext_zkn), + ISA_EXT_DATA_ENTRY(zknd, PRIV_VERSION_1_12_0, ext_zknd), + ISA_EXT_DATA_ENTRY(zkne, PRIV_VERSION_1_12_0, ext_zkne), + ISA_EXT_DATA_ENTRY(zknh, PRIV_VERSION_1_12_0, ext_zknh), + ISA_EXT_DATA_ENTRY(zkr, PRIV_VERSION_1_12_0, ext_zkr), + ISA_EXT_DATA_ENTRY(zks, PRIV_VERSION_1_12_0, ext_zks), + ISA_EXT_DATA_ENTRY(zksed, PRIV_VERSION_1_12_0, ext_zksed), + ISA_EXT_DATA_ENTRY(zksh, PRIV_VERSION_1_12_0, ext_zksh), + ISA_EXT_DATA_ENTRY(zkt, PRIV_VERSION_1_12_0, ext_zkt), + ISA_EXT_DATA_ENTRY(ztso, PRIV_VERSION_1_12_0, ext_ztso), + ISA_EXT_DATA_ENTRY(zvbb, PRIV_VERSION_1_12_0, ext_zvbb), + ISA_EXT_DATA_ENTRY(zvbc, PRIV_VERSION_1_12_0, ext_zvbc), + ISA_EXT_DATA_ENTRY(zve32f, PRIV_VERSION_1_10_0, ext_zve32f), + ISA_EXT_DATA_ENTRY(zve64f, PRIV_VERSION_1_10_0, ext_zve64f), + ISA_EXT_DATA_ENTRY(zve64d, PRIV_VERSION_1_10_0, ext_zve64d), + ISA_EXT_DATA_ENTRY(zvfbfmin, PRIV_VERSION_1_12_0, ext_zvfbfmin), + ISA_EXT_DATA_ENTRY(zvfbfwma, PRIV_VERSION_1_12_0, ext_zvfbfwma), + ISA_EXT_DATA_ENTRY(zvfh, PRIV_VERSION_1_12_0, ext_zvfh), + ISA_EXT_DATA_ENTRY(zvfhmin, PRIV_VERSION_1_12_0, ext_zvfhmin), + ISA_EXT_DATA_ENTRY(zvkb, PRIV_VERSION_1_12_0, ext_zvkb), + ISA_EXT_DATA_ENTRY(zvkg, PRIV_VERSION_1_12_0, ext_zvkg), + ISA_EXT_DATA_ENTRY(zvkn, PRIV_VERSION_1_12_0, ext_zvkn), + ISA_EXT_DATA_ENTRY(zvknc, PRIV_VERSION_1_12_0, ext_zvknc), + ISA_EXT_DATA_ENTRY(zvkned, PRIV_VERSION_1_12_0, ext_zvkned), + ISA_EXT_DATA_ENTRY(zvkng, PRIV_VERSION_1_12_0, ext_zvkng), + ISA_EXT_DATA_ENTRY(zvknha, PRIV_VERSION_1_12_0, ext_zvknha), + ISA_EXT_DATA_ENTRY(zvknhb, PRIV_VERSION_1_12_0, ext_zvknhb), + ISA_EXT_DATA_ENTRY(zvks, PRIV_VERSION_1_12_0, ext_zvks), + ISA_EXT_DATA_ENTRY(zvksc, PRIV_VERSION_1_12_0, ext_zvksc), + ISA_EXT_DATA_ENTRY(zvksed, PRIV_VERSION_1_12_0, ext_zvksed), + ISA_EXT_DATA_ENTRY(zvksg, PRIV_VERSION_1_12_0, ext_zvksg), + ISA_EXT_DATA_ENTRY(zvksh, PRIV_VERSION_1_12_0, ext_zvksh), + ISA_EXT_DATA_ENTRY(zvkt, PRIV_VERSION_1_12_0, ext_zvkt), + ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), + ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), + ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), + ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp), + ISA_EXT_DATA_ENTRY(smstateen, PRIV_VERSION_1_12_0, ext_smstateen), + ISA_EXT_DATA_ENTRY(ssaia, PRIV_VERSION_1_12_0, ext_ssaia), + ISA_EXT_DATA_ENTRY(ssccptr, PRIV_VERSION_1_11_0, has_priv_1_11), + ISA_EXT_DATA_ENTRY(sscofpmf, PRIV_VERSION_1_12_0, ext_sscofpmf), + ISA_EXT_DATA_ENTRY(sscounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc), + ISA_EXT_DATA_ENTRY(sstvala, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(sstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(svade, PRIV_VERSION_1_11_0, ext_svade), + ISA_EXT_DATA_ENTRY(svadu, PRIV_VERSION_1_12_0, ext_svadu), + ISA_EXT_DATA_ENTRY(svinval, PRIV_VERSION_1_12_0, ext_svinval), + ISA_EXT_DATA_ENTRY(svnapot, PRIV_VERSION_1_12_0, ext_svnapot), + ISA_EXT_DATA_ENTRY(svpbmt, PRIV_VERSION_1_12_0, ext_svpbmt), + ISA_EXT_DATA_ENTRY(xtheadba, PRIV_VERSION_1_11_0, ext_xtheadba), + ISA_EXT_DATA_ENTRY(xtheadbb, PRIV_VERSION_1_11_0, ext_xtheadbb), + ISA_EXT_DATA_ENTRY(xtheadbs, PRIV_VERSION_1_11_0, ext_xtheadbs), + ISA_EXT_DATA_ENTRY(xtheadcmo, PRIV_VERSION_1_11_0, ext_xtheadcmo), + ISA_EXT_DATA_ENTRY(xtheadcondmov, PRIV_VERSION_1_11_0, ext_xtheadcondmov), + ISA_EXT_DATA_ENTRY(xtheadfmemidx, PRIV_VERSION_1_11_0, ext_xtheadfmemidx), + ISA_EXT_DATA_ENTRY(xtheadfmv, PRIV_VERSION_1_11_0, ext_xtheadfmv), + ISA_EXT_DATA_ENTRY(xtheadmac, PRIV_VERSION_1_11_0, ext_xtheadmac), + ISA_EXT_DATA_ENTRY(xtheadmemidx, PRIV_VERSION_1_11_0, ext_xtheadmemidx), + ISA_EXT_DATA_ENTRY(xtheadmempair, PRIV_VERSION_1_11_0, ext_xtheadmempair), + ISA_EXT_DATA_ENTRY(xtheadsync, PRIV_VERSION_1_11_0, ext_xtheadsync), + ISA_EXT_DATA_ENTRY(xventanacondops, PRIV_VERSION_1_12_0, ext_XVentanaCondOps), + + DEFINE_PROP_END_OF_LIST(), }; +bool isa_ext_is_enabled(RISCVCPU *cpu, uint32_t ext_offset) +{ + bool *ext_enabled = (void *)&cpu->cfg + ext_offset; + + return *ext_enabled; +} + +void isa_ext_update_enabled(RISCVCPU *cpu, uint32_t ext_offset, bool en) +{ + bool *ext_enabled = (void *)&cpu->cfg + ext_offset; + + *ext_enabled = en; +} + +bool riscv_cpu_is_vendor(Object *cpu_obj) +{ + return object_dynamic_cast(cpu_obj, TYPE_RISCV_VENDOR_CPU) != NULL; +} + const char * const riscv_int_regnames[] = { - "x0/zero", "x1/ra", "x2/sp", "x3/gp", "x4/tp", "x5/t0", "x6/t1", - "x7/t2", "x8/s0", "x9/s1", "x10/a0", "x11/a1", "x12/a2", "x13/a3", - "x14/a4", "x15/a5", "x16/a6", "x17/a7", "x18/s2", "x19/s3", "x20/s4", - "x21/s5", "x22/s6", "x23/s7", "x24/s8", "x25/s9", "x26/s10", "x27/s11", - "x28/t3", "x29/t4", "x30/t5", "x31/t6" + "x0/zero", "x1/ra", "x2/sp", "x3/gp", "x4/tp", "x5/t0", "x6/t1", + "x7/t2", "x8/s0", "x9/s1", "x10/a0", "x11/a1", "x12/a2", "x13/a3", + "x14/a4", "x15/a5", "x16/a6", "x17/a7", "x18/s2", "x19/s3", "x20/s4", + "x21/s5", "x22/s6", "x23/s7", "x24/s8", "x25/s9", "x26/s10", "x27/s11", + "x28/t3", "x29/t4", "x30/t5", "x31/t6" }; const char * const riscv_int_regnamesh[] = { - "x0h/zeroh", "x1h/rah", "x2h/sph", "x3h/gph", "x4h/tph", "x5h/t0h", - "x6h/t1h", "x7h/t2h", "x8h/s0h", "x9h/s1h", "x10h/a0h", "x11h/a1h", - "x12h/a2h", "x13h/a3h", "x14h/a4h", "x15h/a5h", "x16h/a6h", "x17h/a7h", - "x18h/s2h", "x19h/s3h", "x20h/s4h", "x21h/s5h", "x22h/s6h", "x23h/s7h", - "x24h/s8h", "x25h/s9h", "x26h/s10h", "x27h/s11h", "x28h/t3h", "x29h/t4h", - "x30h/t5h", "x31h/t6h" + "x0h/zeroh", "x1h/rah", "x2h/sph", "x3h/gph", "x4h/tph", "x5h/t0h", + "x6h/t1h", "x7h/t2h", "x8h/s0h", "x9h/s1h", "x10h/a0h", "x11h/a1h", + "x12h/a2h", "x13h/a3h", "x14h/a4h", "x15h/a5h", "x16h/a6h", "x17h/a7h", + "x18h/s2h", "x19h/s3h", "x20h/s4h", "x21h/s5h", "x22h/s6h", "x23h/s7h", + "x24h/s8h", "x25h/s9h", "x26h/s10h", "x27h/s11h", "x28h/t3h", "x29h/t4h", + "x30h/t5h", "x31h/t6h" }; const char * const riscv_fpr_regnames[] = { - "f0/ft0", "f1/ft1", "f2/ft2", "f3/ft3", "f4/ft4", "f5/ft5", - "f6/ft6", "f7/ft7", "f8/fs0", "f9/fs1", "f10/fa0", "f11/fa1", - "f12/fa2", "f13/fa3", "f14/fa4", "f15/fa5", "f16/fa6", "f17/fa7", - "f18/fs2", "f19/fs3", "f20/fs4", "f21/fs5", "f22/fs6", "f23/fs7", - "f24/fs8", "f25/fs9", "f26/fs10", "f27/fs11", "f28/ft8", "f29/ft9", - "f30/ft10", "f31/ft11" + "f0/ft0", "f1/ft1", "f2/ft2", "f3/ft3", "f4/ft4", "f5/ft5", + "f6/ft6", "f7/ft7", "f8/fs0", "f9/fs1", "f10/fa0", "f11/fa1", + "f12/fa2", "f13/fa3", "f14/fa4", "f15/fa5", "f16/fa6", "f17/fa7", + "f18/fs2", "f19/fs3", "f20/fs4", "f21/fs5", "f22/fs6", "f23/fs7", + "f24/fs8", "f25/fs9", "f26/fs10", "f27/fs11", "f28/ft8", "f29/ft9", + "f30/ft10", "f31/ft11" +}; + +const char * const riscv_rvv_regnames[] = { + "v0", "v1", "v2", "v3", "v4", "v5", "v6", + "v7", "v8", "v9", "v10", "v11", "v12", "v13", + "v14", "v15", "v16", "v17", "v18", "v19", "v20", + "v21", "v22", "v23", "v24", "v25", "v26", "v27", + "v28", "v29", "v30", "v31" }; static const char * const riscv_excp_names[] = { @@ -129,126 +316,408 @@ const char *riscv_cpu_get_trap_name(target_ulong cause, bool async) } } -static void set_misa(CPURISCVState *env, RISCVMXL mxl, uint32_t ext) +void riscv_cpu_set_misa_ext(CPURISCVState *env, uint32_t ext) { - env->misa_mxl_max = env->misa_mxl = mxl; env->misa_ext_mask = env->misa_ext = ext; } -static void set_priv_version(CPURISCVState *env, int priv_ver) +int riscv_cpu_max_xlen(RISCVCPUClass *mcc) { - env->priv_ver = priv_ver; + return 16 << mcc->misa_mxl_max; } -static void set_vext_version(CPURISCVState *env, int vext_ver) -{ - env->vext_ver = vext_ver; -} - -static void set_resetvec(CPURISCVState *env, target_ulong resetvec) -{ #ifndef CONFIG_USER_ONLY - env->resetvec = resetvec; -#endif +static uint8_t satp_mode_from_str(const char *satp_mode_str) +{ + if (!strncmp(satp_mode_str, "mbare", 5)) { + return VM_1_10_MBARE; + } + + if (!strncmp(satp_mode_str, "sv32", 4)) { + return VM_1_10_SV32; + } + + if (!strncmp(satp_mode_str, "sv39", 4)) { + return VM_1_10_SV39; + } + + if (!strncmp(satp_mode_str, "sv48", 4)) { + return VM_1_10_SV48; + } + + if (!strncmp(satp_mode_str, "sv57", 4)) { + return VM_1_10_SV57; + } + + if (!strncmp(satp_mode_str, "sv64", 4)) { + return VM_1_10_SV64; + } + + g_assert_not_reached(); } +uint8_t satp_mode_max_from_map(uint32_t map) +{ + /* + * 'map = 0' will make us return (31 - 32), which C will + * happily overflow to UINT_MAX. There's no good result to + * return if 'map = 0' (e.g. returning 0 will be ambiguous + * with the result for 'map = 1'). + * + * Assert out if map = 0. Callers will have to deal with + * it outside of this function. + */ + g_assert(map > 0); + + /* map here has at least one bit set, so no problem with clz */ + return 31 - __builtin_clz(map); +} + +const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit) +{ + if (is_32_bit) { + switch (satp_mode) { + case VM_1_10_SV32: + return "sv32"; + case VM_1_10_MBARE: + return "none"; + } + } else { + switch (satp_mode) { + case VM_1_10_SV64: + return "sv64"; + case VM_1_10_SV57: + return "sv57"; + case VM_1_10_SV48: + return "sv48"; + case VM_1_10_SV39: + return "sv39"; + case VM_1_10_MBARE: + return "none"; + } + } + + g_assert_not_reached(); +} + +static void set_satp_mode_max_supported(RISCVCPU *cpu, + uint8_t satp_mode) +{ + bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32; + const bool *valid_vm = rv32 ? valid_vm_1_10_32 : valid_vm_1_10_64; + + for (int i = 0; i <= satp_mode; ++i) { + if (valid_vm[i]) { + cpu->cfg.satp_mode.supported |= (1 << i); + } + } +} + +/* Set the satp mode to the max supported */ +static void set_satp_mode_default_map(RISCVCPU *cpu) +{ + /* + * Bare CPUs do not default to the max available. + * Users must set a valid satp_mode in the command + * line. + */ + if (object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_BARE_CPU) != NULL) { + warn_report("No satp mode set. Defaulting to 'bare'"); + cpu->cfg.satp_mode.map = (1 << VM_1_10_MBARE); + return; + } + + cpu->cfg.satp_mode.map = cpu->cfg.satp_mode.supported; +} +#endif + static void riscv_any_cpu_init(Object *obj) { - CPURISCVState *env = &RISCV_CPU(obj)->env; -#if defined(TARGET_RISCV32) - set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVD | RVC | RVU); -#elif defined(TARGET_RISCV64) - set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVU); + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + riscv_cpu_set_misa_ext(env, RVI | RVM | RVA | RVF | RVD | RVC | RVU); + +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(RISCV_CPU(obj), + riscv_cpu_mxl(&RISCV_CPU(obj)->env) == MXL_RV32 ? + VM_1_10_SV32 : VM_1_10_SV57); +#endif + + env->priv_ver = PRIV_VERSION_LATEST; + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_zifencei = true; + cpu->cfg.ext_zicsr = true; + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; +} + +static void riscv_max_cpu_init(Object *obj) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; + + env->priv_ver = PRIV_VERSION_LATEST; +#ifndef CONFIG_USER_ONLY +#ifdef TARGET_RISCV32 + set_satp_mode_max_supported(cpu, VM_1_10_SV32); +#else + set_satp_mode_max_supported(cpu, VM_1_10_SV57); +#endif #endif - set_priv_version(env, PRIV_VERSION_1_12_0); } #if defined(TARGET_RISCV64) static void rv64_base_cpu_init(Object *obj) { - CPURISCVState *env = &RISCV_CPU(obj)->env; - /* We set this in the realise function */ - set_misa(env, MXL_RV64, 0); + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; + + /* Set latest version of privileged specification */ + env->priv_ver = PRIV_VERSION_LATEST; +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); +#endif } static void rv64_sifive_u_cpu_init(Object *obj) { - CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + riscv_cpu_set_misa_ext(env, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + env->priv_ver = PRIV_VERSION_1_10_0; +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV39); +#endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_zifencei = true; + cpu->cfg.ext_zicsr = true; + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; } static void rv64_sifive_e_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, MXL_RV64, RVI | RVM | RVA | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); - qdev_prop_set_bit(DEVICE(obj), "mmu", false); + RISCVCPU *cpu = RISCV_CPU(obj); + + riscv_cpu_set_misa_ext(env, RVI | RVM | RVA | RVC | RVU); + env->priv_ver = PRIV_VERSION_1_10_0; +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(cpu, VM_1_10_MBARE); +#endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_zifencei = true; + cpu->cfg.ext_zicsr = true; + cpu->cfg.pmp = true; +} + +static void rv64_thead_c906_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + RISCVCPU *cpu = RISCV_CPU(obj); + + riscv_cpu_set_misa_ext(env, RVG | RVC | RVS | RVU); + env->priv_ver = PRIV_VERSION_1_11_0; + + cpu->cfg.ext_zfa = true; + cpu->cfg.ext_zfh = true; + cpu->cfg.mmu = true; + cpu->cfg.ext_xtheadba = true; + cpu->cfg.ext_xtheadbb = true; + cpu->cfg.ext_xtheadbs = true; + cpu->cfg.ext_xtheadcmo = true; + cpu->cfg.ext_xtheadcondmov = true; + cpu->cfg.ext_xtheadfmemidx = true; + cpu->cfg.ext_xtheadmac = true; + cpu->cfg.ext_xtheadmemidx = true; + cpu->cfg.ext_xtheadmempair = true; + cpu->cfg.ext_xtheadsync = true; + + cpu->cfg.mvendorid = THEAD_VENDOR_ID; +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(cpu, VM_1_10_SV39); +#endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.pmp = true; +} + +static void rv64_veyron_v1_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + RISCVCPU *cpu = RISCV_CPU(obj); + + riscv_cpu_set_misa_ext(env, RVG | RVC | RVS | RVU | RVH); + env->priv_ver = PRIV_VERSION_1_12_0; + + /* Enable ISA extensions */ + cpu->cfg.mmu = true; + cpu->cfg.ext_zifencei = true; + cpu->cfg.ext_zicsr = true; + cpu->cfg.pmp = true; + cpu->cfg.ext_zicbom = true; + cpu->cfg.cbom_blocksize = 64; + cpu->cfg.cboz_blocksize = 64; + cpu->cfg.ext_zicboz = true; + cpu->cfg.ext_smaia = true; + cpu->cfg.ext_ssaia = true; + cpu->cfg.ext_sscofpmf = true; + cpu->cfg.ext_sstc = true; + cpu->cfg.ext_svinval = true; + cpu->cfg.ext_svnapot = true; + cpu->cfg.ext_svpbmt = true; + cpu->cfg.ext_smstateen = true; + cpu->cfg.ext_zba = true; + cpu->cfg.ext_zbb = true; + cpu->cfg.ext_zbc = true; + cpu->cfg.ext_zbs = true; + cpu->cfg.ext_XVentanaCondOps = true; + + cpu->cfg.mvendorid = VEYRON_V1_MVENDORID; + cpu->cfg.marchid = VEYRON_V1_MARCHID; + cpu->cfg.mimpid = VEYRON_V1_MIMPID; + +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(cpu, VM_1_10_SV48); +#endif } static void rv128_base_cpu_init(Object *obj) { + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + if (qemu_tcg_mttcg_enabled()) { /* Missing 128-bit aligned atomics */ error_report("128-bit RISC-V currently does not work with Multi " "Threaded TCG. Please use: -accel tcg,thread=single"); exit(EXIT_FAILURE); } + + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; + + /* Set latest version of privileged specification */ + env->priv_ver = PRIV_VERSION_LATEST; +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); +#endif +} + +static void rv64i_bare_cpu_init(Object *obj) +{ CPURISCVState *env = &RISCV_CPU(obj)->env; - /* We set this in the realise function */ - set_misa(env, MXL_RV128, 0); + riscv_cpu_set_misa_ext(env, RVI); +} + +static void rv64e_bare_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + riscv_cpu_set_misa_ext(env, RVE); } #else static void rv32_base_cpu_init(Object *obj) { - CPURISCVState *env = &RISCV_CPU(obj)->env; - /* We set this in the realise function */ - set_misa(env, MXL_RV32, 0); + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; + + /* Set latest version of privileged specification */ + env->priv_ver = PRIV_VERSION_LATEST; +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32); +#endif } static void rv32_sifive_u_cpu_init(Object *obj) { - CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + riscv_cpu_set_misa_ext(env, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + env->priv_ver = PRIV_VERSION_1_10_0; +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32); +#endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_zifencei = true; + cpu->cfg.ext_zicsr = true; + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; } static void rv32_sifive_e_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, MXL_RV32, RVI | RVM | RVA | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); - qdev_prop_set_bit(DEVICE(obj), "mmu", false); + RISCVCPU *cpu = RISCV_CPU(obj); + + riscv_cpu_set_misa_ext(env, RVI | RVM | RVA | RVC | RVU); + env->priv_ver = PRIV_VERSION_1_10_0; +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(cpu, VM_1_10_MBARE); +#endif + + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_zifencei = true; + cpu->cfg.ext_zicsr = true; + cpu->cfg.pmp = true; } static void rv32_ibex_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, MXL_RV32, RVI | RVM | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); - qdev_prop_set_bit(DEVICE(obj), "mmu", false); - qdev_prop_set_bit(DEVICE(obj), "x-epmp", true); + RISCVCPU *cpu = RISCV_CPU(obj); + + riscv_cpu_set_misa_ext(env, RVI | RVM | RVC | RVU); + env->priv_ver = PRIV_VERSION_1_12_0; +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(cpu, VM_1_10_MBARE); +#endif + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_zifencei = true; + cpu->cfg.ext_zicsr = true; + cpu->cfg.pmp = true; + cpu->cfg.ext_smepmp = true; } static void rv32_imafcu_nommu_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); - set_resetvec(env, DEFAULT_RSTVEC); - qdev_prop_set_bit(DEVICE(obj), "mmu", false); -} + RISCVCPU *cpu = RISCV_CPU(obj); + + riscv_cpu_set_misa_ext(env, RVI | RVM | RVA | RVF | RVC | RVU); + env->priv_ver = PRIV_VERSION_1_10_0; +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(cpu, VM_1_10_MBARE); #endif -#if defined(CONFIG_KVM) -static void riscv_host_cpu_init(Object *obj) + /* inherited from parent obj via riscv_cpu_init() */ + cpu->cfg.ext_zifencei = true; + cpu->cfg.ext_zicsr = true; + cpu->cfg.pmp = true; +} + +static void rv32i_bare_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; -#if defined(TARGET_RISCV32) - set_misa(env, MXL_RV32, 0); -#elif defined(TARGET_RISCV64) - set_misa(env, MXL_RV64, 0); -#endif + riscv_cpu_set_misa_ext(env, RVI); +} + +static void rv32e_bare_cpu_init(Object *obj) +{ + CPURISCVState *env = &RISCV_CPU(obj)->env; + riscv_cpu_set_misa_ext(env, RVE); } #endif @@ -263,22 +732,30 @@ static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model) oc = object_class_by_name(typename); g_strfreev(cpuname); g_free(typename); - if (!oc || !object_class_dynamic_cast(oc, TYPE_RISCV_CPU) || - object_class_is_abstract(oc)) { - return NULL; - } + return oc; } +char *riscv_cpu_get_name(RISCVCPU *cpu) +{ + RISCVCPUClass *rcc = RISCV_CPU_GET_CLASS(cpu); + const char *typename = object_class_get_name(OBJECT_CLASS(rcc)); + + g_assert(g_str_has_suffix(typename, RISCV_CPU_TYPE_SUFFIX)); + + return cpu_model_from_type(typename); +} + static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - int i; + int i, j; + uint8_t *p; #if !defined(CONFIG_USER_ONLY) if (riscv_has_ext(env, RVH)) { - qemu_fprintf(f, " %s %d\n", "V = ", riscv_cpu_virt_enabled(env)); + qemu_fprintf(f, " %s %d\n", "V = ", env->virt_enabled); } #endif qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc ", env->pc); @@ -288,6 +765,10 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) CSR_MHARTID, CSR_MSTATUS, CSR_MSTATUSH, + /* + * CSR_SSTATUS is intentionally omitted here as its value + * can be figured out by looking at CSR_MSTATUS + */ CSR_HSTATUS, CSR_VSSTATUS, CSR_MIP, @@ -321,7 +802,7 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) CSR_MPMMASK, }; - for (int i = 0; i < ARRAY_SIZE(dump_csrs); ++i) { + for (i = 0; i < ARRAY_SIZE(dump_csrs); ++i) { int csrno = dump_csrs[i]; target_ulong val = 0; RISCVException res = riscv_csrrw_debug(env, csrno, &val, 0, 0); @@ -354,6 +835,41 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } } + if (riscv_has_ext(env, RVV) && (flags & CPU_DUMP_VPU)) { + static const int dump_rvv_csrs[] = { + CSR_VSTART, + CSR_VXSAT, + CSR_VXRM, + CSR_VCSR, + CSR_VL, + CSR_VTYPE, + CSR_VLENB, + }; + for (i = 0; i < ARRAY_SIZE(dump_rvv_csrs); ++i) { + int csrno = dump_rvv_csrs[i]; + target_ulong val = 0; + RISCVException res = riscv_csrrw_debug(env, csrno, &val, 0, 0); + + /* + * Rely on the smode, hmode, etc, predicates within csr.c + * to do the filtering of the registers that are present. + */ + if (res == RISCV_EXCP_NONE) { + qemu_fprintf(f, " %-8s " TARGET_FMT_lx "\n", + csr_ops[csrno].name, val); + } + } + uint16_t vlenb = cpu->cfg.vlenb; + + for (i = 0; i < 32; i++) { + qemu_fprintf(f, " %-8s ", riscv_rvv_regnames[i]); + p = (uint8_t *)env->vreg; + for (j = vlenb - 1 ; j >= 0; j--) { + qemu_fprintf(f, "%02x", *(p + i * vlenb + BYTE(j))); + } + qemu_fprintf(f, "\n"); + } + } } static void riscv_cpu_set_pc(CPUState *cs, vaddr value) @@ -368,18 +884,16 @@ static void riscv_cpu_set_pc(CPUState *cs, vaddr value) } } -static void riscv_cpu_synchronize_from_tb(CPUState *cs, - const TranslationBlock *tb) +static vaddr riscv_cpu_get_pc(CPUState *cs) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL); - if (xl == MXL_RV32) { - env->pc = (int32_t)tb->pc; - } else { - env->pc = tb->pc; + /* Match cpu_get_tb_cpu_state. */ + if (env->xl == MXL_RV32) { + return env->pc & UINT32_MAX; } + return env->pc; } static bool riscv_cpu_has_work(CPUState *cs) @@ -391,38 +905,35 @@ static bool riscv_cpu_has_work(CPUState *cs) * Definition of the WFI instruction requires it to ignore the privilege * mode and delegation registers, but respect individual enables */ - return (env->mip & env->mie) != 0; + return riscv_cpu_all_pending(env) != 0 || + riscv_cpu_sirq_pending(env) != RISCV_EXCP_NONE || + riscv_cpu_vsirq_pending(env) != RISCV_EXCP_NONE; #else return true; #endif } -void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb, - target_ulong *data) +static int riscv_cpu_mmu_index(CPUState *cs, bool ifetch) { - RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL); - if (xl == MXL_RV32) { - env->pc = (int32_t)data[0]; - } else { - env->pc = data[0]; - } - env->bins = data[1]; + return riscv_env_mmu_index(cpu_env(cs), ifetch); } -static void riscv_cpu_reset(DeviceState *dev) +static void riscv_cpu_reset_hold(Object *obj) { #ifndef CONFIG_USER_ONLY uint8_t iprio; int i, irq, rdzero; #endif - CPUState *cs = CPU(dev); + CPUState *cs = CPU(obj); RISCVCPU *cpu = RISCV_CPU(cs); - RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(obj); CPURISCVState *env = &cpu->env; - mcc->parent_reset(dev); + if (mcc->parent_phases.hold) { + mcc->parent_phases.hold(obj); + } #ifndef CONFIG_USER_ONLY - env->misa_mxl = env->misa_mxl_max; + env->misa_mxl = mcc->misa_mxl_max; env->priv = PRV_M; env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV); if (env->misa_mxl > MXL_RV32) { @@ -449,6 +960,11 @@ static void riscv_cpu_reset(DeviceState *dev) env->bins = 0; env->two_stage_lookup = false; + env->menvcfg = (cpu->cfg.ext_svpbmt ? MENVCFG_PBMTE : 0) | + (!cpu->cfg.ext_svade && cpu->cfg.ext_svadu ? + MENVCFG_ADUE : 0); + env->henvcfg = 0; + /* Initialized default priorities of local interrupts. */ for (i = 0; i < ARRAY_SIZE(env->miprio); i++) { iprio = riscv_cpu_default_priority(i); @@ -464,7 +980,26 @@ static void riscv_cpu_reset(DeviceState *dev) i++; } /* mmte is supposed to have pm.current hardwired to 1 */ - env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT); + env->mmte |= (EXT_STATUS_INITIAL | MMTE_M_PM_CURRENT); + + /* + * Bits 10, 6, 2 and 12 of mideleg are read only 1 when the Hypervisor + * extension is enabled. + */ + if (riscv_has_ext(env, RVH)) { + env->mideleg |= HS_MODE_INTERRUPTS; + } + + /* + * Clear mseccfg and unlock all the PMP entries upon reset. + * This is allowed as per the priv and smepmp specifications + * and is needed to clear stale entries across reboots. + */ + if (riscv_cpu_cfg(env)->ext_smepmp) { + env->mseccfg = 0; + } + + pmp_unlock_entries(env); #endif env->xl = riscv_cpu_mxl(env); riscv_cpu_update_mask(env); @@ -473,8 +1008,8 @@ static void riscv_cpu_reset(DeviceState *dev) set_default_nan_mode(1, &env->fp_status); #ifndef CONFIG_USER_ONLY - if (riscv_feature(env, RISCV_FEATURE_DEBUG)) { - riscv_trigger_init(env); + if (cpu->cfg.debug) { + riscv_trigger_reset_hold(env); } if (kvm_enabled()) { @@ -486,8 +1021,10 @@ static void riscv_cpu_reset(DeviceState *dev) static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) { RISCVCPU *cpu = RISCV_CPU(s); + CPURISCVState *env = &cpu->env; + info->target_info = &cpu->cfg; - switch (riscv_cpu_mxl(&cpu->env)) { + switch (env->xl) { case MXL_RV32: info->print_insn = print_insn_riscv32; break; @@ -502,268 +1039,202 @@ static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) } } +#ifndef CONFIG_USER_ONLY +static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) +{ + bool rv32 = riscv_cpu_is_32bit(cpu); + uint8_t satp_mode_map_max, satp_mode_supported_max; + + /* The CPU wants the OS to decide which satp mode to use */ + if (cpu->cfg.satp_mode.supported == 0) { + return; + } + + satp_mode_supported_max = + satp_mode_max_from_map(cpu->cfg.satp_mode.supported); + + if (cpu->cfg.satp_mode.map == 0) { + if (cpu->cfg.satp_mode.init == 0) { + /* If unset by the user, we fallback to the default satp mode. */ + set_satp_mode_default_map(cpu); + } else { + /* + * Find the lowest level that was disabled and then enable the + * first valid level below which can be found in + * valid_vm_1_10_32/64. + */ + for (int i = 1; i < 16; ++i) { + if ((cpu->cfg.satp_mode.init & (1 << i)) && + (cpu->cfg.satp_mode.supported & (1 << i))) { + for (int j = i - 1; j >= 0; --j) { + if (cpu->cfg.satp_mode.supported & (1 << j)) { + cpu->cfg.satp_mode.map |= (1 << j); + break; + } + } + break; + } + } + } + } + + satp_mode_map_max = satp_mode_max_from_map(cpu->cfg.satp_mode.map); + + /* Make sure the user asked for a supported configuration (HW and qemu) */ + if (satp_mode_map_max > satp_mode_supported_max) { + error_setg(errp, "satp_mode %s is higher than hw max capability %s", + satp_mode_str(satp_mode_map_max, rv32), + satp_mode_str(satp_mode_supported_max, rv32)); + return; + } + + /* + * Make sure the user did not ask for an invalid configuration as per + * the specification. + */ + if (!rv32) { + for (int i = satp_mode_map_max - 1; i >= 0; --i) { + if (!(cpu->cfg.satp_mode.map & (1 << i)) && + (cpu->cfg.satp_mode.init & (1 << i)) && + (cpu->cfg.satp_mode.supported & (1 << i))) { + error_setg(errp, "cannot disable %s satp mode if %s " + "is enabled", satp_mode_str(i, false), + satp_mode_str(satp_mode_map_max, false)); + return; + } + } + } + + /* Finally expand the map so that all valid modes are set */ + for (int i = satp_mode_map_max - 1; i >= 0; --i) { + if (cpu->cfg.satp_mode.supported & (1 << i)) { + cpu->cfg.satp_mode.map |= (1 << i); + } + } +} +#endif + +void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp) +{ + Error *local_err = NULL; + +#ifndef CONFIG_USER_ONLY + riscv_cpu_satp_mode_finalize(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } +#endif + + if (tcg_enabled()) { + riscv_tcg_cpu_finalize_features(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + } else if (kvm_enabled()) { + riscv_kvm_cpu_finalize_features(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + } +} + static void riscv_cpu_realize(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); RISCVCPU *cpu = RISCV_CPU(dev); - CPURISCVState *env = &cpu->env; RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); - CPUClass *cc = CPU_CLASS(mcc); - int priv_version = 0; Error *local_err = NULL; + if (object_dynamic_cast(OBJECT(dev), TYPE_RISCV_CPU_ANY) != NULL) { + warn_report("The 'any' CPU is deprecated and will be " + "removed in the future."); + } + cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; } - if (cpu->cfg.priv_spec) { - if (!g_strcmp0(cpu->cfg.priv_spec, "v1.12.0")) { - priv_version = PRIV_VERSION_1_12_0; - } else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) { - priv_version = PRIV_VERSION_1_11_0; - } else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.10.0")) { - priv_version = PRIV_VERSION_1_10_0; - } else { - error_setg(errp, - "Unsupported privilege spec version '%s'", - cpu->cfg.priv_spec); - return; - } - } - - if (priv_version) { - set_priv_version(env, priv_version); - } else if (!env->priv_ver) { - set_priv_version(env, PRIV_VERSION_1_12_0); - } - - if (cpu->cfg.mmu) { - riscv_set_feature(env, RISCV_FEATURE_MMU); - } - - if (cpu->cfg.pmp) { - riscv_set_feature(env, RISCV_FEATURE_PMP); - - /* - * Enhanced PMP should only be available - * on harts with PMP support - */ - if (cpu->cfg.epmp) { - riscv_set_feature(env, RISCV_FEATURE_EPMP); - } - } - - if (cpu->cfg.aia) { - riscv_set_feature(env, RISCV_FEATURE_AIA); - } - - if (cpu->cfg.debug) { - riscv_set_feature(env, RISCV_FEATURE_DEBUG); - } - - set_resetvec(env, cpu->cfg.resetvec); - - /* Validate that MISA_MXL is set properly. */ - switch (env->misa_mxl_max) { -#ifdef TARGET_RISCV64 - case MXL_RV64: - case MXL_RV128: - cc->gdb_core_xml_file = "riscv-64bit-cpu.xml"; - break; -#endif - case MXL_RV32: - cc->gdb_core_xml_file = "riscv-32bit-cpu.xml"; - break; - default: - g_assert_not_reached(); - } - assert(env->misa_mxl_max == env->misa_mxl); - - /* If only MISA_EXT is unset for misa, then set it from properties */ - if (env->misa_ext == 0) { - uint32_t ext = 0; - - /* Do some ISA extension error checking */ - if (cpu->cfg.ext_g && !(cpu->cfg.ext_i && cpu->cfg.ext_m && - cpu->cfg.ext_a && cpu->cfg.ext_f && - cpu->cfg.ext_d && - cpu->cfg.ext_icsr && cpu->cfg.ext_ifencei)) { - warn_report("Setting G will also set IMAFD_Zicsr_Zifencei"); - cpu->cfg.ext_i = true; - cpu->cfg.ext_m = true; - cpu->cfg.ext_a = true; - cpu->cfg.ext_f = true; - cpu->cfg.ext_d = true; - cpu->cfg.ext_icsr = true; - cpu->cfg.ext_ifencei = true; - } - - if (cpu->cfg.ext_i && cpu->cfg.ext_e) { - error_setg(errp, - "I and E extensions are incompatible"); - return; - } - - if (!cpu->cfg.ext_i && !cpu->cfg.ext_e) { - error_setg(errp, - "Either I or E extension must be set"); - return; - } - - if (cpu->cfg.ext_f && !cpu->cfg.ext_icsr) { - error_setg(errp, "F extension requires Zicsr"); - return; - } - - if ((cpu->cfg.ext_zfh || cpu->cfg.ext_zfhmin) && !cpu->cfg.ext_f) { - error_setg(errp, "Zfh/Zfhmin extensions require F extension"); - return; - } - - if (cpu->cfg.ext_d && !cpu->cfg.ext_f) { - error_setg(errp, "D extension requires F extension"); - return; - } - - if (cpu->cfg.ext_v && !cpu->cfg.ext_d) { - error_setg(errp, "V extension requires D extension"); - return; - } - - if ((cpu->cfg.ext_zve32f || cpu->cfg.ext_zve64f) && !cpu->cfg.ext_f) { - error_setg(errp, "Zve32f/Zve64f extensions require F extension"); - return; - } - - /* Set the ISA extensions, checks should have happened above */ - if (cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinx || - cpu->cfg.ext_zhinxmin) { - cpu->cfg.ext_zfinx = true; - } - - if (cpu->cfg.ext_zfinx) { - if (!cpu->cfg.ext_icsr) { - error_setg(errp, "Zfinx extension requires Zicsr"); - return; - } - if (cpu->cfg.ext_f) { - error_setg(errp, - "Zfinx cannot be supported together with F extension"); - return; - } - } - - if (cpu->cfg.ext_zk) { - cpu->cfg.ext_zkn = true; - cpu->cfg.ext_zkr = true; - cpu->cfg.ext_zkt = true; - } - - if (cpu->cfg.ext_zkn) { - cpu->cfg.ext_zbkb = true; - cpu->cfg.ext_zbkc = true; - cpu->cfg.ext_zbkx = true; - cpu->cfg.ext_zkne = true; - cpu->cfg.ext_zknd = true; - cpu->cfg.ext_zknh = true; - } - - if (cpu->cfg.ext_zks) { - cpu->cfg.ext_zbkb = true; - cpu->cfg.ext_zbkc = true; - cpu->cfg.ext_zbkx = true; - cpu->cfg.ext_zksed = true; - cpu->cfg.ext_zksh = true; - } - - if (cpu->cfg.ext_i) { - ext |= RVI; - } - if (cpu->cfg.ext_e) { - ext |= RVE; - } - if (cpu->cfg.ext_m) { - ext |= RVM; - } - if (cpu->cfg.ext_a) { - ext |= RVA; - } - if (cpu->cfg.ext_f) { - ext |= RVF; - } - if (cpu->cfg.ext_d) { - ext |= RVD; - } - if (cpu->cfg.ext_c) { - ext |= RVC; - } - if (cpu->cfg.ext_s) { - ext |= RVS; - } - if (cpu->cfg.ext_u) { - ext |= RVU; - } - if (cpu->cfg.ext_h) { - ext |= RVH; - } - if (cpu->cfg.ext_v) { - int vext_version = VEXT_VERSION_1_00_0; - ext |= RVV; - if (!is_power_of_2(cpu->cfg.vlen)) { - error_setg(errp, - "Vector extension VLEN must be power of 2"); - return; - } - if (cpu->cfg.vlen > RV_VLEN_MAX || cpu->cfg.vlen < 128) { - error_setg(errp, - "Vector extension implementation only supports VLEN " - "in the range [128, %d]", RV_VLEN_MAX); - return; - } - if (!is_power_of_2(cpu->cfg.elen)) { - error_setg(errp, - "Vector extension ELEN must be power of 2"); - return; - } - if (cpu->cfg.elen > 64 || cpu->cfg.vlen < 8) { - error_setg(errp, - "Vector extension implementation only supports ELEN " - "in the range [8, 64]"); - return; - } - if (cpu->cfg.vext_spec) { - if (!g_strcmp0(cpu->cfg.vext_spec, "v1.0")) { - vext_version = VEXT_VERSION_1_00_0; - } else { - error_setg(errp, - "Unsupported vector spec version '%s'", - cpu->cfg.vext_spec); - return; - } - } else { - qemu_log("vector version is not specified, " - "use the default value v1.0\n"); - } - set_vext_version(env, vext_version); - } - if (cpu->cfg.ext_j) { - ext |= RVJ; - } - - set_misa(env, env->misa_mxl, ext); + riscv_cpu_finalize_features(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; } riscv_cpu_register_gdb_regs_for_features(cs); +#ifndef CONFIG_USER_ONLY + if (cpu->cfg.debug) { + riscv_trigger_realize(&cpu->env); + } +#endif + qemu_init_vcpu(cs); cpu_reset(cs); mcc->parent_realize(dev, errp); } +bool riscv_cpu_accelerator_compatible(RISCVCPU *cpu) +{ + if (tcg_enabled()) { + return riscv_cpu_tcg_compatible(cpu); + } + + return true; +} + #ifndef CONFIG_USER_ONLY +static void cpu_riscv_get_satp(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVSATPMap *satp_map = opaque; + uint8_t satp = satp_mode_from_str(name); + bool value; + + value = satp_map->map & (1 << satp); + + visit_type_bool(v, name, &value, errp); +} + +static void cpu_riscv_set_satp(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVSATPMap *satp_map = opaque; + uint8_t satp = satp_mode_from_str(name); + bool value; + + if (!visit_type_bool(v, name, &value, errp)) { + return; + } + + satp_map->map = deposit32(satp_map->map, satp, 1, value); + satp_map->init |= 1 << satp; +} + +void riscv_add_satp_mode_properties(Object *obj) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + + if (cpu->env.misa_mxl == MXL_RV32) { + object_property_add(obj, "sv32", "bool", cpu_riscv_get_satp, + cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode); + } else { + object_property_add(obj, "sv39", "bool", cpu_riscv_get_satp, + cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode); + object_property_add(obj, "sv48", "bool", cpu_riscv_get_satp, + cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode); + object_property_add(obj, "sv57", "bool", cpu_riscv_get_satp, + cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode); + object_property_add(obj, "sv64", "bool", cpu_riscv_get_satp, + cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode); + } +} + static void riscv_cpu_set_irq(void *opaque, int irq, int level) { RISCVCPU *cpu = RISCV_CPU(opaque); @@ -785,7 +1256,7 @@ static void riscv_cpu_set_irq(void *opaque, int irq, int level) if (kvm_enabled()) { kvm_riscv_set_irq(cpu, irq, level); } else { - riscv_cpu_update_mip(cpu, 1 << irq, BOOL_TO_MASK(level)); + riscv_cpu_update_mip(env, 1 << irq, BOOL_TO_MASK(level)); } break; case IRQ_S_EXT: @@ -793,7 +1264,7 @@ static void riscv_cpu_set_irq(void *opaque, int irq, int level) kvm_riscv_set_irq(cpu, irq, level); } else { env->external_seip = level; - riscv_cpu_update_mip(cpu, 1 << irq, + riscv_cpu_update_mip(env, 1 << irq, BOOL_TO_MASK(level | env->software_seip)); } break; @@ -819,7 +1290,7 @@ static void riscv_cpu_set_irq(void *opaque, int irq, int level) } /* Update mip.SGEIP bit */ - riscv_cpu_update_mip(cpu, MIP_SGEIP, + riscv_cpu_update_mip(env, MIP_SGEIP, BOOL_TO_MASK(!!(env->hgeie & env->hgeip))); } else { g_assert_not_reached(); @@ -827,124 +1298,1024 @@ static void riscv_cpu_set_irq(void *opaque, int irq, int level) } #endif /* CONFIG_USER_ONLY */ +static bool riscv_cpu_is_dynamic(Object *cpu_obj) +{ + return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL; +} + +static void riscv_cpu_post_init(Object *obj) +{ + accel_cpu_instance_init(CPU(obj)); +} + static void riscv_cpu_init(Object *obj) +{ + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(obj); + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + + env->misa_mxl = mcc->misa_mxl_max; + +#ifndef CONFIG_USER_ONLY + qdev_init_gpio_in(DEVICE(obj), riscv_cpu_set_irq, + IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX); +#endif /* CONFIG_USER_ONLY */ + + general_user_opts = g_hash_table_new(g_str_hash, g_str_equal); + + /* + * The timer and performance counters extensions were supported + * in QEMU before they were added as discrete extensions in the + * ISA. To keep compatibility we'll always default them to 'true' + * for all CPUs. Each accelerator will decide what to do when + * users disable them. + */ + RISCV_CPU(obj)->cfg.ext_zicntr = true; + RISCV_CPU(obj)->cfg.ext_zihpm = true; + + /* Default values for non-bool cpu properties */ + cpu->cfg.pmu_mask = MAKE_64BIT_MASK(3, 16); + cpu->cfg.vlenb = 128 >> 3; + cpu->cfg.elen = 64; + cpu->cfg.cbom_blocksize = 64; + cpu->cfg.cbop_blocksize = 64; + cpu->cfg.cboz_blocksize = 64; + cpu->env.vext_ver = VEXT_VERSION_1_00_0; +} + +static void riscv_bare_cpu_init(Object *obj) { RISCVCPU *cpu = RISCV_CPU(obj); - cpu_set_cpustate_pointers(cpu); + /* + * Bare CPUs do not inherit the timer and performance + * counters from the parent class (see riscv_cpu_init() + * for info on why the parent enables them). + * + * Users have to explicitly enable these counters for + * bare CPUs. + */ + cpu->cfg.ext_zicntr = false; + cpu->cfg.ext_zihpm = false; + /* Set to QEMU's first supported priv version */ + cpu->env.priv_ver = PRIV_VERSION_1_10_0; + + /* + * Support all available satp_mode settings. The default + * value will be set to MBARE if the user doesn't set + * satp_mode manually (see set_satp_mode_default()). + */ #ifndef CONFIG_USER_ONLY - qdev_init_gpio_in(DEVICE(cpu), riscv_cpu_set_irq, - IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX); -#endif /* CONFIG_USER_ONLY */ + set_satp_mode_max_supported(cpu, VM_1_10_SV64); +#endif } -static Property riscv_cpu_properties[] = { +typedef struct misa_ext_info { + const char *name; + const char *description; +} MISAExtInfo; + +#define MISA_INFO_IDX(_bit) \ + __builtin_ctz(_bit) + +#define MISA_EXT_INFO(_bit, _propname, _descr) \ + [MISA_INFO_IDX(_bit)] = {.name = _propname, .description = _descr} + +static const MISAExtInfo misa_ext_info_arr[] = { + MISA_EXT_INFO(RVA, "a", "Atomic instructions"), + MISA_EXT_INFO(RVC, "c", "Compressed instructions"), + MISA_EXT_INFO(RVD, "d", "Double-precision float point"), + MISA_EXT_INFO(RVF, "f", "Single-precision float point"), + MISA_EXT_INFO(RVI, "i", "Base integer instruction set"), + MISA_EXT_INFO(RVE, "e", "Base integer instruction set (embedded)"), + MISA_EXT_INFO(RVM, "m", "Integer multiplication and division"), + MISA_EXT_INFO(RVS, "s", "Supervisor-level instructions"), + MISA_EXT_INFO(RVU, "u", "User-level instructions"), + MISA_EXT_INFO(RVH, "h", "Hypervisor"), + MISA_EXT_INFO(RVJ, "x-j", "Dynamic translated languages"), + MISA_EXT_INFO(RVV, "v", "Vector operations"), + MISA_EXT_INFO(RVG, "g", "General purpose (IMAFD_Zicsr_Zifencei)"), + MISA_EXT_INFO(RVB, "x-b", "Bit manipulation (Zba_Zbb_Zbs)") +}; + +static void riscv_cpu_validate_misa_mxl(RISCVCPUClass *mcc) +{ + CPUClass *cc = CPU_CLASS(mcc); + + /* Validate that MISA_MXL is set properly. */ + switch (mcc->misa_mxl_max) { +#ifdef TARGET_RISCV64 + case MXL_RV64: + case MXL_RV128: + cc->gdb_core_xml_file = "riscv-64bit-cpu.xml"; + break; +#endif + case MXL_RV32: + cc->gdb_core_xml_file = "riscv-32bit-cpu.xml"; + break; + default: + g_assert_not_reached(); + } +} + +static int riscv_validate_misa_info_idx(uint32_t bit) +{ + int idx; + + /* + * Our lowest valid input (RVA) is 1 and + * __builtin_ctz() is UB with zero. + */ + g_assert(bit != 0); + idx = MISA_INFO_IDX(bit); + + g_assert(idx < ARRAY_SIZE(misa_ext_info_arr)); + return idx; +} + +const char *riscv_get_misa_ext_name(uint32_t bit) +{ + int idx = riscv_validate_misa_info_idx(bit); + const char *val = misa_ext_info_arr[idx].name; + + g_assert(val != NULL); + return val; +} + +const char *riscv_get_misa_ext_description(uint32_t bit) +{ + int idx = riscv_validate_misa_info_idx(bit); + const char *val = misa_ext_info_arr[idx].description; + + g_assert(val != NULL); + return val; +} + +#define MULTI_EXT_CFG_BOOL(_name, _prop, _defval) \ + {.name = _name, .offset = CPU_CFG_OFFSET(_prop), \ + .enabled = _defval} + +const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { /* Defaults for standard extensions */ - DEFINE_PROP_BOOL("i", RISCVCPU, cfg.ext_i, true), - DEFINE_PROP_BOOL("e", RISCVCPU, cfg.ext_e, false), - DEFINE_PROP_BOOL("g", RISCVCPU, cfg.ext_g, false), - DEFINE_PROP_BOOL("m", RISCVCPU, cfg.ext_m, true), - DEFINE_PROP_BOOL("a", RISCVCPU, cfg.ext_a, true), - DEFINE_PROP_BOOL("f", RISCVCPU, cfg.ext_f, true), - DEFINE_PROP_BOOL("d", RISCVCPU, cfg.ext_d, true), - 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), - DEFINE_PROP_BOOL("v", RISCVCPU, cfg.ext_v, false), - DEFINE_PROP_BOOL("h", RISCVCPU, cfg.ext_h, true), - 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), - DEFINE_PROP_BOOL("Zfh", RISCVCPU, cfg.ext_zfh, false), - DEFINE_PROP_BOOL("Zfhmin", RISCVCPU, cfg.ext_zfhmin, false), - DEFINE_PROP_BOOL("Zve32f", RISCVCPU, cfg.ext_zve32f, false), - DEFINE_PROP_BOOL("Zve64f", RISCVCPU, cfg.ext_zve64f, false), - DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true), - DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true), - DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true), + MULTI_EXT_CFG_BOOL("sscofpmf", ext_sscofpmf, false), + MULTI_EXT_CFG_BOOL("zifencei", ext_zifencei, true), + MULTI_EXT_CFG_BOOL("zicsr", ext_zicsr, true), + MULTI_EXT_CFG_BOOL("zihintntl", ext_zihintntl, true), + MULTI_EXT_CFG_BOOL("zihintpause", ext_zihintpause, true), + MULTI_EXT_CFG_BOOL("zacas", ext_zacas, false), + MULTI_EXT_CFG_BOOL("zaamo", ext_zaamo, false), + MULTI_EXT_CFG_BOOL("zalrsc", ext_zalrsc, false), + MULTI_EXT_CFG_BOOL("zawrs", ext_zawrs, true), + MULTI_EXT_CFG_BOOL("zfa", ext_zfa, true), + MULTI_EXT_CFG_BOOL("zfbfmin", ext_zfbfmin, false), + MULTI_EXT_CFG_BOOL("zfh", ext_zfh, false), + MULTI_EXT_CFG_BOOL("zfhmin", ext_zfhmin, false), + MULTI_EXT_CFG_BOOL("zve32f", ext_zve32f, false), + MULTI_EXT_CFG_BOOL("zve64f", ext_zve64f, false), + MULTI_EXT_CFG_BOOL("zve64d", ext_zve64d, false), + MULTI_EXT_CFG_BOOL("zvfbfmin", ext_zvfbfmin, false), + MULTI_EXT_CFG_BOOL("zvfbfwma", ext_zvfbfwma, false), + MULTI_EXT_CFG_BOOL("zvfh", ext_zvfh, false), + MULTI_EXT_CFG_BOOL("zvfhmin", ext_zvfhmin, false), + MULTI_EXT_CFG_BOOL("sstc", ext_sstc, true), - DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec), - DEFINE_PROP_STRING("vext_spec", RISCVCPU, cfg.vext_spec), - DEFINE_PROP_UINT16("vlen", RISCVCPU, cfg.vlen, 128), - DEFINE_PROP_UINT16("elen", RISCVCPU, cfg.elen, 64), + MULTI_EXT_CFG_BOOL("smaia", ext_smaia, false), + MULTI_EXT_CFG_BOOL("smepmp", ext_smepmp, false), + MULTI_EXT_CFG_BOOL("smstateen", ext_smstateen, false), + MULTI_EXT_CFG_BOOL("ssaia", ext_ssaia, false), + MULTI_EXT_CFG_BOOL("svade", ext_svade, false), + MULTI_EXT_CFG_BOOL("svadu", ext_svadu, true), + MULTI_EXT_CFG_BOOL("svinval", ext_svinval, false), + MULTI_EXT_CFG_BOOL("svnapot", ext_svnapot, false), + MULTI_EXT_CFG_BOOL("svpbmt", ext_svpbmt, false), - DEFINE_PROP_UINT32("mvendorid", RISCVCPU, cfg.mvendorid, 0), - DEFINE_PROP_UINT64("marchid", RISCVCPU, cfg.marchid, RISCV_CPU_MARCHID), - DEFINE_PROP_UINT64("mimpid", RISCVCPU, cfg.mimpid, RISCV_CPU_MIMPID), + MULTI_EXT_CFG_BOOL("zicntr", ext_zicntr, true), + MULTI_EXT_CFG_BOOL("zihpm", ext_zihpm, true), - DEFINE_PROP_BOOL("svinval", RISCVCPU, cfg.ext_svinval, false), - DEFINE_PROP_BOOL("svnapot", RISCVCPU, cfg.ext_svnapot, false), - DEFINE_PROP_BOOL("svpbmt", RISCVCPU, cfg.ext_svpbmt, false), + MULTI_EXT_CFG_BOOL("zba", ext_zba, true), + MULTI_EXT_CFG_BOOL("zbb", ext_zbb, true), + MULTI_EXT_CFG_BOOL("zbc", ext_zbc, true), + MULTI_EXT_CFG_BOOL("zbkb", ext_zbkb, false), + MULTI_EXT_CFG_BOOL("zbkc", ext_zbkc, false), + MULTI_EXT_CFG_BOOL("zbkx", ext_zbkx, false), + MULTI_EXT_CFG_BOOL("zbs", ext_zbs, true), + MULTI_EXT_CFG_BOOL("zk", ext_zk, false), + MULTI_EXT_CFG_BOOL("zkn", ext_zkn, false), + MULTI_EXT_CFG_BOOL("zknd", ext_zknd, false), + MULTI_EXT_CFG_BOOL("zkne", ext_zkne, false), + MULTI_EXT_CFG_BOOL("zknh", ext_zknh, false), + MULTI_EXT_CFG_BOOL("zkr", ext_zkr, false), + MULTI_EXT_CFG_BOOL("zks", ext_zks, false), + MULTI_EXT_CFG_BOOL("zksed", ext_zksed, false), + MULTI_EXT_CFG_BOOL("zksh", ext_zksh, false), + MULTI_EXT_CFG_BOOL("zkt", ext_zkt, false), + MULTI_EXT_CFG_BOOL("ztso", ext_ztso, false), - DEFINE_PROP_BOOL("zba", RISCVCPU, cfg.ext_zba, true), - DEFINE_PROP_BOOL("zbb", RISCVCPU, cfg.ext_zbb, true), - DEFINE_PROP_BOOL("zbc", RISCVCPU, cfg.ext_zbc, true), - DEFINE_PROP_BOOL("zbkb", RISCVCPU, cfg.ext_zbkb, false), - DEFINE_PROP_BOOL("zbkc", RISCVCPU, cfg.ext_zbkc, false), - DEFINE_PROP_BOOL("zbkx", RISCVCPU, cfg.ext_zbkx, false), - DEFINE_PROP_BOOL("zbs", RISCVCPU, cfg.ext_zbs, true), - DEFINE_PROP_BOOL("zk", RISCVCPU, cfg.ext_zk, false), - DEFINE_PROP_BOOL("zkn", RISCVCPU, cfg.ext_zkn, false), - DEFINE_PROP_BOOL("zknd", RISCVCPU, cfg.ext_zknd, false), - DEFINE_PROP_BOOL("zkne", RISCVCPU, cfg.ext_zkne, false), - DEFINE_PROP_BOOL("zknh", RISCVCPU, cfg.ext_zknh, false), - DEFINE_PROP_BOOL("zkr", RISCVCPU, cfg.ext_zkr, false), - DEFINE_PROP_BOOL("zks", RISCVCPU, cfg.ext_zks, false), - DEFINE_PROP_BOOL("zksed", RISCVCPU, cfg.ext_zksed, false), - DEFINE_PROP_BOOL("zksh", RISCVCPU, cfg.ext_zksh, false), - DEFINE_PROP_BOOL("zkt", RISCVCPU, cfg.ext_zkt, false), + MULTI_EXT_CFG_BOOL("zdinx", ext_zdinx, false), + MULTI_EXT_CFG_BOOL("zfinx", ext_zfinx, false), + MULTI_EXT_CFG_BOOL("zhinx", ext_zhinx, false), + MULTI_EXT_CFG_BOOL("zhinxmin", ext_zhinxmin, false), - DEFINE_PROP_BOOL("zdinx", RISCVCPU, cfg.ext_zdinx, false), - DEFINE_PROP_BOOL("zfinx", RISCVCPU, cfg.ext_zfinx, false), - DEFINE_PROP_BOOL("zhinx", RISCVCPU, cfg.ext_zhinx, false), - DEFINE_PROP_BOOL("zhinxmin", RISCVCPU, cfg.ext_zhinxmin, false), + MULTI_EXT_CFG_BOOL("zicbom", ext_zicbom, true), + MULTI_EXT_CFG_BOOL("zicbop", ext_zicbop, true), + MULTI_EXT_CFG_BOOL("zicboz", ext_zicboz, true), - /* Vendor-specific custom extensions */ - DEFINE_PROP_BOOL("xventanacondops", RISCVCPU, cfg.ext_XVentanaCondOps, false), + MULTI_EXT_CFG_BOOL("zmmul", ext_zmmul, false), - /* These are experimental so mark with 'x-' */ - DEFINE_PROP_BOOL("x-j", RISCVCPU, cfg.ext_j, false), - /* ePMP 0.9.3 */ - DEFINE_PROP_BOOL("x-epmp", RISCVCPU, cfg.epmp, false), - DEFINE_PROP_BOOL("x-aia", RISCVCPU, cfg.aia, false), + MULTI_EXT_CFG_BOOL("zca", ext_zca, false), + MULTI_EXT_CFG_BOOL("zcb", ext_zcb, false), + MULTI_EXT_CFG_BOOL("zcd", ext_zcd, false), + MULTI_EXT_CFG_BOOL("zce", ext_zce, false), + MULTI_EXT_CFG_BOOL("zcf", ext_zcf, false), + MULTI_EXT_CFG_BOOL("zcmp", ext_zcmp, false), + MULTI_EXT_CFG_BOOL("zcmt", ext_zcmt, false), + MULTI_EXT_CFG_BOOL("zicond", ext_zicond, false), - DEFINE_PROP_UINT64("resetvec", RISCVCPU, cfg.resetvec, DEFAULT_RSTVEC), + /* Vector cryptography extensions */ + MULTI_EXT_CFG_BOOL("zvbb", ext_zvbb, false), + MULTI_EXT_CFG_BOOL("zvbc", ext_zvbc, false), + MULTI_EXT_CFG_BOOL("zvkb", ext_zvkg, false), + MULTI_EXT_CFG_BOOL("zvkg", ext_zvkg, false), + MULTI_EXT_CFG_BOOL("zvkned", ext_zvkned, false), + MULTI_EXT_CFG_BOOL("zvknha", ext_zvknha, false), + MULTI_EXT_CFG_BOOL("zvknhb", ext_zvknhb, false), + MULTI_EXT_CFG_BOOL("zvksed", ext_zvksed, false), + MULTI_EXT_CFG_BOOL("zvksh", ext_zvksh, false), + MULTI_EXT_CFG_BOOL("zvkt", ext_zvkt, false), + MULTI_EXT_CFG_BOOL("zvkn", ext_zvkn, false), + MULTI_EXT_CFG_BOOL("zvknc", ext_zvknc, false), + MULTI_EXT_CFG_BOOL("zvkng", ext_zvkng, false), + MULTI_EXT_CFG_BOOL("zvks", ext_zvks, false), + MULTI_EXT_CFG_BOOL("zvksc", ext_zvksc, false), + MULTI_EXT_CFG_BOOL("zvksg", ext_zvksg, false), - DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false), DEFINE_PROP_END_OF_LIST(), }; -static gchar *riscv_gdb_arch_name(CPUState *cs) +const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[] = { + MULTI_EXT_CFG_BOOL("xtheadba", ext_xtheadba, false), + MULTI_EXT_CFG_BOOL("xtheadbb", ext_xtheadbb, false), + MULTI_EXT_CFG_BOOL("xtheadbs", ext_xtheadbs, false), + MULTI_EXT_CFG_BOOL("xtheadcmo", ext_xtheadcmo, false), + MULTI_EXT_CFG_BOOL("xtheadcondmov", ext_xtheadcondmov, false), + MULTI_EXT_CFG_BOOL("xtheadfmemidx", ext_xtheadfmemidx, false), + MULTI_EXT_CFG_BOOL("xtheadfmv", ext_xtheadfmv, false), + MULTI_EXT_CFG_BOOL("xtheadmac", ext_xtheadmac, false), + MULTI_EXT_CFG_BOOL("xtheadmemidx", ext_xtheadmemidx, false), + MULTI_EXT_CFG_BOOL("xtheadmempair", ext_xtheadmempair, false), + MULTI_EXT_CFG_BOOL("xtheadsync", ext_xtheadsync, false), + MULTI_EXT_CFG_BOOL("xventanacondops", ext_XVentanaCondOps, false), + + DEFINE_PROP_END_OF_LIST(), +}; + +/* These are experimental so mark with 'x-' */ +const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { + DEFINE_PROP_END_OF_LIST(), +}; + +/* + * 'Named features' is the name we give to extensions that we + * don't want to expose to users. They are either immutable + * (always enabled/disable) or they'll vary depending on + * the resulting CPU state. They have riscv,isa strings + * and priv_ver like regular extensions. + */ +const RISCVCPUMultiExtConfig riscv_cpu_named_features[] = { + MULTI_EXT_CFG_BOOL("zic64b", ext_zic64b, true), + + DEFINE_PROP_END_OF_LIST(), +}; + +/* Deprecated entries marked for future removal */ +const RISCVCPUMultiExtConfig riscv_cpu_deprecated_exts[] = { + MULTI_EXT_CFG_BOOL("Zifencei", ext_zifencei, true), + MULTI_EXT_CFG_BOOL("Zicsr", ext_zicsr, true), + MULTI_EXT_CFG_BOOL("Zihintntl", ext_zihintntl, true), + MULTI_EXT_CFG_BOOL("Zihintpause", ext_zihintpause, true), + MULTI_EXT_CFG_BOOL("Zawrs", ext_zawrs, true), + MULTI_EXT_CFG_BOOL("Zfa", ext_zfa, true), + MULTI_EXT_CFG_BOOL("Zfh", ext_zfh, false), + MULTI_EXT_CFG_BOOL("Zfhmin", ext_zfhmin, false), + MULTI_EXT_CFG_BOOL("Zve32f", ext_zve32f, false), + MULTI_EXT_CFG_BOOL("Zve64f", ext_zve64f, false), + MULTI_EXT_CFG_BOOL("Zve64d", ext_zve64d, false), + + DEFINE_PROP_END_OF_LIST(), +}; + +static void cpu_set_prop_err(RISCVCPU *cpu, const char *propname, + Error **errp) +{ + g_autofree char *cpuname = riscv_cpu_get_name(cpu); + error_setg(errp, "CPU '%s' does not allow changing the value of '%s'", + cpuname, propname); +} + +static void prop_pmu_num_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + uint8_t pmu_num, curr_pmu_num; + uint32_t pmu_mask; + + visit_type_uint8(v, name, &pmu_num, errp); + + curr_pmu_num = ctpop32(cpu->cfg.pmu_mask); + + if (pmu_num != curr_pmu_num && riscv_cpu_is_vendor(obj)) { + cpu_set_prop_err(cpu, name, errp); + error_append_hint(errp, "Current '%s' val: %u\n", + name, curr_pmu_num); + return; + } + + if (pmu_num > (RV_MAX_MHPMCOUNTERS - 3)) { + error_setg(errp, "Number of counters exceeds maximum available"); + return; + } + + if (pmu_num == 0) { + pmu_mask = 0; + } else { + pmu_mask = MAKE_64BIT_MASK(3, pmu_num); + } + + warn_report("\"pmu-num\" property is deprecated; use \"pmu-mask\""); + cpu->cfg.pmu_mask = pmu_mask; + cpu_option_add_user_setting("pmu-mask", pmu_mask); +} + +static void prop_pmu_num_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + uint8_t pmu_num = ctpop32(cpu->cfg.pmu_mask); + + visit_type_uint8(v, name, &pmu_num, errp); +} + +static const PropertyInfo prop_pmu_num = { + .name = "pmu-num", + .get = prop_pmu_num_get, + .set = prop_pmu_num_set, +}; + +static void prop_pmu_mask_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + uint32_t value; + uint8_t pmu_num; + + visit_type_uint32(v, name, &value, errp); + + if (value != cpu->cfg.pmu_mask && riscv_cpu_is_vendor(obj)) { + cpu_set_prop_err(cpu, name, errp); + error_append_hint(errp, "Current '%s' val: %x\n", + name, cpu->cfg.pmu_mask); + return; + } + + pmu_num = ctpop32(value); + + if (pmu_num > (RV_MAX_MHPMCOUNTERS - 3)) { + error_setg(errp, "Number of counters exceeds maximum available"); + return; + } + + cpu_option_add_user_setting(name, value); + cpu->cfg.pmu_mask = value; +} + +static void prop_pmu_mask_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint8_t pmu_mask = RISCV_CPU(obj)->cfg.pmu_mask; + + visit_type_uint8(v, name, &pmu_mask, errp); +} + +static const PropertyInfo prop_pmu_mask = { + .name = "pmu-mask", + .get = prop_pmu_mask_get, + .set = prop_pmu_mask_set, +}; + +static void prop_mmu_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + bool value; + + visit_type_bool(v, name, &value, errp); + + if (cpu->cfg.mmu != value && riscv_cpu_is_vendor(obj)) { + cpu_set_prop_err(cpu, "mmu", errp); + return; + } + + cpu_option_add_user_setting(name, value); + cpu->cfg.mmu = value; +} + +static void prop_mmu_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + bool value = RISCV_CPU(obj)->cfg.mmu; + + visit_type_bool(v, name, &value, errp); +} + +static const PropertyInfo prop_mmu = { + .name = "mmu", + .get = prop_mmu_get, + .set = prop_mmu_set, +}; + +static void prop_pmp_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + bool value; + + visit_type_bool(v, name, &value, errp); + + if (cpu->cfg.pmp != value && riscv_cpu_is_vendor(obj)) { + cpu_set_prop_err(cpu, name, errp); + return; + } + + cpu_option_add_user_setting(name, value); + cpu->cfg.pmp = value; +} + +static void prop_pmp_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + bool value = RISCV_CPU(obj)->cfg.pmp; + + visit_type_bool(v, name, &value, errp); +} + +static const PropertyInfo prop_pmp = { + .name = "pmp", + .get = prop_pmp_get, + .set = prop_pmp_set, +}; + +static int priv_spec_from_str(const char *priv_spec_str) +{ + int priv_version = -1; + + if (!g_strcmp0(priv_spec_str, PRIV_VER_1_12_0_STR)) { + priv_version = PRIV_VERSION_1_12_0; + } else if (!g_strcmp0(priv_spec_str, PRIV_VER_1_11_0_STR)) { + priv_version = PRIV_VERSION_1_11_0; + } else if (!g_strcmp0(priv_spec_str, PRIV_VER_1_10_0_STR)) { + priv_version = PRIV_VERSION_1_10_0; + } + + return priv_version; +} + +static const char *priv_spec_to_str(int priv_version) +{ + switch (priv_version) { + case PRIV_VERSION_1_10_0: + return PRIV_VER_1_10_0_STR; + case PRIV_VERSION_1_11_0: + return PRIV_VER_1_11_0_STR; + case PRIV_VERSION_1_12_0: + return PRIV_VER_1_12_0_STR; + default: + return NULL; + } +} + +static void prop_priv_spec_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + g_autofree char *value = NULL; + int priv_version = -1; + + visit_type_str(v, name, &value, errp); + + priv_version = priv_spec_from_str(value); + if (priv_version < 0) { + error_setg(errp, "Unsupported privilege spec version '%s'", value); + return; + } + + if (priv_version != cpu->env.priv_ver && riscv_cpu_is_vendor(obj)) { + cpu_set_prop_err(cpu, name, errp); + error_append_hint(errp, "Current '%s' val: %s\n", name, + object_property_get_str(obj, name, NULL)); + return; + } + + cpu_option_add_user_setting(name, priv_version); + cpu->env.priv_ver = priv_version; +} + +static void prop_priv_spec_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + const char *value = priv_spec_to_str(cpu->env.priv_ver); + + visit_type_str(v, name, (char **)&value, errp); +} + +static const PropertyInfo prop_priv_spec = { + .name = "priv_spec", + .get = prop_priv_spec_get, + .set = prop_priv_spec_set, +}; + +static void prop_vext_spec_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + g_autofree char *value = NULL; + + visit_type_str(v, name, &value, errp); + + if (g_strcmp0(value, VEXT_VER_1_00_0_STR) != 0) { + error_setg(errp, "Unsupported vector spec version '%s'", value); + return; + } + + cpu_option_add_user_setting(name, VEXT_VERSION_1_00_0); + cpu->env.vext_ver = VEXT_VERSION_1_00_0; +} + +static void prop_vext_spec_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + const char *value = VEXT_VER_1_00_0_STR; + + visit_type_str(v, name, (char **)&value, errp); +} + +static const PropertyInfo prop_vext_spec = { + .name = "vext_spec", + .get = prop_vext_spec_get, + .set = prop_vext_spec_set, +}; + +static void prop_vlen_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + uint16_t value; + + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } + + if (!is_power_of_2(value)) { + error_setg(errp, "Vector extension VLEN must be power of 2"); + return; + } + + if (value != cpu->cfg.vlenb && riscv_cpu_is_vendor(obj)) { + cpu_set_prop_err(cpu, name, errp); + error_append_hint(errp, "Current '%s' val: %u\n", + name, cpu->cfg.vlenb << 3); + return; + } + + cpu_option_add_user_setting(name, value); + cpu->cfg.vlenb = value >> 3; +} + +static void prop_vlen_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint16_t value = RISCV_CPU(obj)->cfg.vlenb << 3; + + visit_type_uint16(v, name, &value, errp); +} + +static const PropertyInfo prop_vlen = { + .name = "vlen", + .get = prop_vlen_get, + .set = prop_vlen_set, +}; + +static void prop_elen_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + uint16_t value; + + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } + + if (!is_power_of_2(value)) { + error_setg(errp, "Vector extension ELEN must be power of 2"); + return; + } + + if (value != cpu->cfg.elen && riscv_cpu_is_vendor(obj)) { + cpu_set_prop_err(cpu, name, errp); + error_append_hint(errp, "Current '%s' val: %u\n", + name, cpu->cfg.elen); + return; + } + + cpu_option_add_user_setting(name, value); + cpu->cfg.elen = value; +} + +static void prop_elen_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint16_t value = RISCV_CPU(obj)->cfg.elen; + + visit_type_uint16(v, name, &value, errp); +} + +static const PropertyInfo prop_elen = { + .name = "elen", + .get = prop_elen_get, + .set = prop_elen_set, +}; + +static void prop_cbom_blksize_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + uint16_t value; + + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } + + if (value != cpu->cfg.cbom_blocksize && riscv_cpu_is_vendor(obj)) { + cpu_set_prop_err(cpu, name, errp); + error_append_hint(errp, "Current '%s' val: %u\n", + name, cpu->cfg.cbom_blocksize); + return; + } + + cpu_option_add_user_setting(name, value); + cpu->cfg.cbom_blocksize = value; +} + +static void prop_cbom_blksize_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint16_t value = RISCV_CPU(obj)->cfg.cbom_blocksize; + + visit_type_uint16(v, name, &value, errp); +} + +static const PropertyInfo prop_cbom_blksize = { + .name = "cbom_blocksize", + .get = prop_cbom_blksize_get, + .set = prop_cbom_blksize_set, +}; + +static void prop_cbop_blksize_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + uint16_t value; + + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } + + if (value != cpu->cfg.cbop_blocksize && riscv_cpu_is_vendor(obj)) { + cpu_set_prop_err(cpu, name, errp); + error_append_hint(errp, "Current '%s' val: %u\n", + name, cpu->cfg.cbop_blocksize); + return; + } + + cpu_option_add_user_setting(name, value); + cpu->cfg.cbop_blocksize = value; +} + +static void prop_cbop_blksize_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint16_t value = RISCV_CPU(obj)->cfg.cbop_blocksize; + + visit_type_uint16(v, name, &value, errp); +} + +static const PropertyInfo prop_cbop_blksize = { + .name = "cbop_blocksize", + .get = prop_cbop_blksize_get, + .set = prop_cbop_blksize_set, +}; + +static void prop_cboz_blksize_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + uint16_t value; + + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } + + if (value != cpu->cfg.cboz_blocksize && riscv_cpu_is_vendor(obj)) { + cpu_set_prop_err(cpu, name, errp); + error_append_hint(errp, "Current '%s' val: %u\n", + name, cpu->cfg.cboz_blocksize); + return; + } + + cpu_option_add_user_setting(name, value); + cpu->cfg.cboz_blocksize = value; +} + +static void prop_cboz_blksize_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint16_t value = RISCV_CPU(obj)->cfg.cboz_blocksize; + + visit_type_uint16(v, name, &value, errp); +} + +static const PropertyInfo prop_cboz_blksize = { + .name = "cboz_blocksize", + .get = prop_cboz_blksize_get, + .set = prop_cboz_blksize_set, +}; + +static void prop_mvendorid_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + bool dynamic_cpu = riscv_cpu_is_dynamic(obj); + RISCVCPU *cpu = RISCV_CPU(obj); + uint32_t prev_val = cpu->cfg.mvendorid; + uint32_t value; + + if (!visit_type_uint32(v, name, &value, errp)) { + return; + } + + if (!dynamic_cpu && prev_val != value) { + error_setg(errp, "Unable to change %s mvendorid (0x%x)", + object_get_typename(obj), prev_val); + return; + } + + cpu->cfg.mvendorid = value; +} + +static void prop_mvendorid_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t value = RISCV_CPU(obj)->cfg.mvendorid; + + visit_type_uint32(v, name, &value, errp); +} + +static const PropertyInfo prop_mvendorid = { + .name = "mvendorid", + .get = prop_mvendorid_get, + .set = prop_mvendorid_set, +}; + +static void prop_mimpid_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + bool dynamic_cpu = riscv_cpu_is_dynamic(obj); + RISCVCPU *cpu = RISCV_CPU(obj); + uint64_t prev_val = cpu->cfg.mimpid; + uint64_t value; + + if (!visit_type_uint64(v, name, &value, errp)) { + return; + } + + if (!dynamic_cpu && prev_val != value) { + error_setg(errp, "Unable to change %s mimpid (0x%" PRIu64 ")", + object_get_typename(obj), prev_val); + return; + } + + cpu->cfg.mimpid = value; +} + +static void prop_mimpid_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint64_t value = RISCV_CPU(obj)->cfg.mimpid; + + visit_type_uint64(v, name, &value, errp); +} + +static const PropertyInfo prop_mimpid = { + .name = "mimpid", + .get = prop_mimpid_get, + .set = prop_mimpid_set, +}; + +static void prop_marchid_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + bool dynamic_cpu = riscv_cpu_is_dynamic(obj); + RISCVCPU *cpu = RISCV_CPU(obj); + uint64_t prev_val = cpu->cfg.marchid; + uint64_t value, invalid_val; + uint32_t mxlen = 0; + + if (!visit_type_uint64(v, name, &value, errp)) { + return; + } + + if (!dynamic_cpu && prev_val != value) { + error_setg(errp, "Unable to change %s marchid (0x%" PRIu64 ")", + object_get_typename(obj), prev_val); + return; + } + + switch (riscv_cpu_mxl(&cpu->env)) { + case MXL_RV32: + mxlen = 32; + break; + case MXL_RV64: + case MXL_RV128: + mxlen = 64; + break; + default: + g_assert_not_reached(); + } + + invalid_val = 1LL << (mxlen - 1); + + if (value == invalid_val) { + error_setg(errp, "Unable to set marchid with MSB (%u) bit set " + "and the remaining bits zero", mxlen); + return; + } + + cpu->cfg.marchid = value; +} + +static void prop_marchid_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint64_t value = RISCV_CPU(obj)->cfg.marchid; + + visit_type_uint64(v, name, &value, errp); +} + +static const PropertyInfo prop_marchid = { + .name = "marchid", + .get = prop_marchid_get, + .set = prop_marchid_set, +}; + +/* + * RVA22U64 defines some 'named features' that are cache + * related: Za64rs, Zic64b, Ziccif, Ziccrse, Ziccamoa + * and Zicclsm. They are always implemented in TCG and + * doesn't need to be manually enabled by the profile. + */ +static RISCVCPUProfile RVA22U64 = { + .parent = NULL, + .name = "rva22u64", + .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVU, + .priv_spec = RISCV_PROFILE_ATTR_UNUSED, + .satp_mode = RISCV_PROFILE_ATTR_UNUSED, + .ext_offsets = { + CPU_CFG_OFFSET(ext_zicsr), CPU_CFG_OFFSET(ext_zihintpause), + CPU_CFG_OFFSET(ext_zba), CPU_CFG_OFFSET(ext_zbb), + CPU_CFG_OFFSET(ext_zbs), CPU_CFG_OFFSET(ext_zfhmin), + CPU_CFG_OFFSET(ext_zkt), CPU_CFG_OFFSET(ext_zicntr), + CPU_CFG_OFFSET(ext_zihpm), CPU_CFG_OFFSET(ext_zicbom), + CPU_CFG_OFFSET(ext_zicbop), CPU_CFG_OFFSET(ext_zicboz), + + /* mandatory named features for this profile */ + CPU_CFG_OFFSET(ext_zic64b), + + RISCV_PROFILE_EXT_LIST_END + } +}; + +/* + * As with RVA22U64, RVA22S64 also defines 'named features'. + * + * Cache related features that we consider enabled since we don't + * implement cache: Ssccptr + * + * Other named features that we already implement: Sstvecd, Sstvala, + * Sscounterenw + * + * The remaining features/extensions comes from RVA22U64. + */ +static RISCVCPUProfile RVA22S64 = { + .parent = &RVA22U64, + .name = "rva22s64", + .misa_ext = RVS, + .priv_spec = PRIV_VERSION_1_12_0, + .satp_mode = VM_1_10_SV39, + .ext_offsets = { + /* rva22s64 exts */ + CPU_CFG_OFFSET(ext_zifencei), CPU_CFG_OFFSET(ext_svpbmt), + CPU_CFG_OFFSET(ext_svinval), CPU_CFG_OFFSET(ext_svade), + + RISCV_PROFILE_EXT_LIST_END + } +}; + +RISCVCPUProfile *riscv_profiles[] = { + &RVA22U64, + &RVA22S64, + NULL, +}; + +static Property riscv_cpu_properties[] = { + DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true), + + {.name = "pmu-mask", .info = &prop_pmu_mask}, + {.name = "pmu-num", .info = &prop_pmu_num}, /* Deprecated */ + + {.name = "mmu", .info = &prop_mmu}, + {.name = "pmp", .info = &prop_pmp}, + + {.name = "priv_spec", .info = &prop_priv_spec}, + {.name = "vext_spec", .info = &prop_vext_spec}, + + {.name = "vlen", .info = &prop_vlen}, + {.name = "elen", .info = &prop_elen}, + + {.name = "cbom_blocksize", .info = &prop_cbom_blksize}, + {.name = "cbop_blocksize", .info = &prop_cbop_blksize}, + {.name = "cboz_blocksize", .info = &prop_cboz_blksize}, + + {.name = "mvendorid", .info = &prop_mvendorid}, + {.name = "mimpid", .info = &prop_mimpid}, + {.name = "marchid", .info = &prop_marchid}, + +#ifndef CONFIG_USER_ONLY + DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC), +#endif + + DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false), + + DEFINE_PROP_BOOL("rvv_ta_all_1s", RISCVCPU, cfg.rvv_ta_all_1s, false), + DEFINE_PROP_BOOL("rvv_ma_all_1s", RISCVCPU, cfg.rvv_ma_all_1s, false), + + /* + * write_misa() is marked as experimental for now so mark + * it with -x and default to 'false'. + */ + DEFINE_PROP_BOOL("x-misa-w", RISCVCPU, cfg.misa_w, false), + DEFINE_PROP_END_OF_LIST(), +}; + +#if defined(TARGET_RISCV64) +static void rva22u64_profile_cpu_init(Object *obj) +{ + rv64i_bare_cpu_init(obj); + + RVA22U64.enabled = true; +} + +static void rva22s64_profile_cpu_init(Object *obj) +{ + rv64i_bare_cpu_init(obj); + + RVA22S64.enabled = true; +} +#endif + +static const gchar *riscv_gdb_arch_name(CPUState *cs) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; switch (riscv_cpu_mxl(env)) { case MXL_RV32: - return g_strdup("riscv:rv32"); + return "riscv:rv32"; case MXL_RV64: case MXL_RV128: - return g_strdup("riscv:rv64"); + return "riscv:rv64"; default: g_assert_not_reached(); } } -static const char *riscv_gdb_get_dynamic_xml(CPUState *cs, const char *xmlname) +#ifndef CONFIG_USER_ONLY +static int64_t riscv_get_arch_id(CPUState *cs) { RISCVCPU *cpu = RISCV_CPU(cs); - if (strcmp(xmlname, "riscv-csr.xml") == 0) { - return cpu->dyn_csr_xml; - } else if (strcmp(xmlname, "riscv-vector.xml") == 0) { - return cpu->dyn_vreg_xml; - } - - return NULL; + return cpu->env.mhartid; } -#ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps riscv_sysemu_ops = { @@ -955,115 +2326,56 @@ static const struct SysemuCPUOps riscv_sysemu_ops = { }; #endif -#include "hw/core/tcg-cpu-ops.h" - -static const struct TCGCPUOps riscv_tcg_ops = { - .initialize = riscv_translate_init, - .synchronize_from_tb = riscv_cpu_synchronize_from_tb, - -#ifndef CONFIG_USER_ONLY - .tlb_fill = riscv_cpu_tlb_fill, - .cpu_exec_interrupt = riscv_cpu_exec_interrupt, - .do_interrupt = riscv_cpu_do_interrupt, - .do_transaction_failed = riscv_cpu_do_transaction_failed, - .do_unaligned_access = riscv_cpu_do_unaligned_access, - .debug_excp_handler = riscv_cpu_debug_excp_handler, - .debug_check_breakpoint = riscv_cpu_debug_check_breakpoint, - .debug_check_watchpoint = riscv_cpu_debug_check_watchpoint, -#endif /* !CONFIG_USER_ONLY */ -}; - -static void riscv_cpu_class_init(ObjectClass *c, void *data) +static void riscv_cpu_common_class_init(ObjectClass *c, void *data) { RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); DeviceClass *dc = DEVICE_CLASS(c); + ResettableClass *rc = RESETTABLE_CLASS(c); device_class_set_parent_realize(dc, riscv_cpu_realize, &mcc->parent_realize); - device_class_set_parent_reset(dc, riscv_cpu_reset, &mcc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, riscv_cpu_reset_hold, NULL, + &mcc->parent_phases); cc->class_by_name = riscv_cpu_class_by_name; cc->has_work = riscv_cpu_has_work; + cc->mmu_index = riscv_cpu_mmu_index; cc->dump_state = riscv_cpu_dump_state; cc->set_pc = riscv_cpu_set_pc; + cc->get_pc = riscv_cpu_get_pc; cc->gdb_read_register = riscv_cpu_gdb_read_register; cc->gdb_write_register = riscv_cpu_gdb_write_register; - cc->gdb_num_core_regs = 33; cc->gdb_stop_before_watchpoint = true; cc->disas_set_info = riscv_cpu_disas_set_info; #ifndef CONFIG_USER_ONLY cc->sysemu_ops = &riscv_sysemu_ops; + cc->get_arch_id = riscv_get_arch_id; #endif cc->gdb_arch_name = riscv_gdb_arch_name; - cc->gdb_get_dynamic_xml = riscv_gdb_get_dynamic_xml; - cc->tcg_ops = &riscv_tcg_ops; device_class_set_props(dc, riscv_cpu_properties); } -#define ISA_EDATA_ENTRY(name, prop) {#name, cpu->cfg.prop} - -static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str, int max_str_len) +static void riscv_cpu_class_init(ObjectClass *c, void *data) { + RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); + + mcc->misa_mxl_max = (uint32_t)(uintptr_t)data; + riscv_cpu_validate_misa_mxl(mcc); +} + +static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str, + int max_str_len) +{ + const RISCVIsaExtData *edata; char *old = *isa_str; char *new = *isa_str; - int i; - /** - * Here are the ordering rules of extension naming defined by RISC-V - * specification : - * 1. All extensions should be separated from other multi-letter extensions - * by an underscore. - * 2. The first letter following the 'Z' conventionally indicates the most - * closely related alphabetical extension category, IMAFDQLCBKJTPVH. - * If multiple 'Z' extensions are named, they should be ordered first - * by category, then alphabetically within a category. - * 3. Standard supervisor-level extensions (starts with 'S') should be - * listed after standard unprivileged extensions. If multiple - * supervisor-level extensions are listed, they should be ordered - * alphabetically. - * 4. Non-standard extensions (starts with 'X') must be listed after all - * standard extensions. They must be separated from other multi-letter - * extensions by an underscore. - */ - struct isa_ext_data isa_edata_arr[] = { - ISA_EDATA_ENTRY(zicsr, ext_icsr), - ISA_EDATA_ENTRY(zifencei, ext_ifencei), - ISA_EDATA_ENTRY(zfh, ext_zfh), - ISA_EDATA_ENTRY(zfhmin, ext_zfhmin), - ISA_EDATA_ENTRY(zfinx, ext_zfinx), - ISA_EDATA_ENTRY(zdinx, ext_zdinx), - ISA_EDATA_ENTRY(zba, ext_zba), - ISA_EDATA_ENTRY(zbb, ext_zbb), - ISA_EDATA_ENTRY(zbc, ext_zbc), - ISA_EDATA_ENTRY(zbkb, ext_zbkb), - ISA_EDATA_ENTRY(zbkc, ext_zbkc), - ISA_EDATA_ENTRY(zbkx, ext_zbkx), - ISA_EDATA_ENTRY(zbs, ext_zbs), - ISA_EDATA_ENTRY(zk, ext_zk), - ISA_EDATA_ENTRY(zkn, ext_zkn), - ISA_EDATA_ENTRY(zknd, ext_zknd), - ISA_EDATA_ENTRY(zkne, ext_zkne), - ISA_EDATA_ENTRY(zknh, ext_zknh), - ISA_EDATA_ENTRY(zkr, ext_zkr), - ISA_EDATA_ENTRY(zks, ext_zks), - ISA_EDATA_ENTRY(zksed, ext_zksed), - ISA_EDATA_ENTRY(zksh, ext_zksh), - ISA_EDATA_ENTRY(zkt, ext_zkt), - ISA_EDATA_ENTRY(zve32f, ext_zve32f), - ISA_EDATA_ENTRY(zve64f, ext_zve64f), - ISA_EDATA_ENTRY(zhinx, ext_zhinx), - ISA_EDATA_ENTRY(zhinxmin, ext_zhinxmin), - ISA_EDATA_ENTRY(svinval, ext_svinval), - ISA_EDATA_ENTRY(svnapot, ext_svnapot), - ISA_EDATA_ENTRY(svpbmt, ext_svpbmt), - }; - - for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) { - if (isa_edata_arr[i].enabled) { - new = g_strconcat(old, "_", isa_edata_arr[i].name, NULL); + for (edata = isa_edata_arr; edata && edata->name; edata++) { + if (isa_ext_is_enabled(cpu, edata->ext_enable_offset)) { + new = g_strconcat(old, "_", edata->name, NULL); g_free(old); old = new; } @@ -1074,10 +2386,13 @@ static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str, int max_str_len) char *riscv_isa_string(RISCVCPU *cpu) { + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); int i; const size_t maxlen = sizeof("rv128") + sizeof(riscv_single_letter_exts); char *isa_str = g_new(char, maxlen); - char *p = isa_str + snprintf(isa_str, maxlen, "rv%d", TARGET_LONG_BITS); + int xlen = riscv_cpu_max_xlen(mcc); + char *p = isa_str + snprintf(isa_str, maxlen, "rv%d", xlen); + for (i = 0; i < sizeof(riscv_single_letter_exts) - 1; i++) { if (cpu->env.misa_ext & RV(riscv_single_letter_exts[i])) { *p++ = qemu_tolower(riscv_single_letter_exts[i]); @@ -1090,40 +2405,102 @@ char *riscv_isa_string(RISCVCPU *cpu) return isa_str; } -static gint riscv_cpu_list_compare(gconstpointer a, gconstpointer b) +#ifndef CONFIG_USER_ONLY +static char **riscv_isa_extensions_list(RISCVCPU *cpu, int *count) { - ObjectClass *class_a = (ObjectClass *)a; - ObjectClass *class_b = (ObjectClass *)b; - const char *name_a, *name_b; + int maxlen = ARRAY_SIZE(riscv_single_letter_exts) + ARRAY_SIZE(isa_edata_arr); + char **extensions = g_new(char *, maxlen); - name_a = object_class_get_name(class_a); - name_b = object_class_get_name(class_b); - return strcmp(name_a, name_b); + for (int i = 0; i < sizeof(riscv_single_letter_exts) - 1; i++) { + if (cpu->env.misa_ext & RV(riscv_single_letter_exts[i])) { + extensions[*count] = g_new(char, 2); + snprintf(extensions[*count], 2, "%c", + qemu_tolower(riscv_single_letter_exts[i])); + (*count)++; + } + } + + for (const RISCVIsaExtData *edata = isa_edata_arr; edata->name; edata++) { + if (isa_ext_is_enabled(cpu, edata->ext_enable_offset)) { + extensions[*count] = g_strdup(edata->name); + (*count)++; + } + } + + return extensions; } -static void riscv_cpu_list_entry(gpointer data, gpointer user_data) +void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) { - const char *typename = object_class_get_name(OBJECT_CLASS(data)); - int len = strlen(typename) - strlen(RISCV_CPU_TYPE_SUFFIX); + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); + const size_t maxlen = sizeof("rv128i"); + g_autofree char *isa_base = g_new(char, maxlen); + g_autofree char *riscv_isa; + char **isa_extensions; + int count = 0; + int xlen = riscv_cpu_max_xlen(mcc); - qemu_printf("%.*s\n", len, typename); + riscv_isa = riscv_isa_string(cpu); + qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", riscv_isa); + + snprintf(isa_base, maxlen, "rv%di", xlen); + qemu_fdt_setprop_string(fdt, nodename, "riscv,isa-base", isa_base); + + isa_extensions = riscv_isa_extensions_list(cpu, &count); + qemu_fdt_setprop_string_array(fdt, nodename, "riscv,isa-extensions", + isa_extensions, count); + + for (int i = 0; i < count; i++) { + g_free(isa_extensions[i]); + } + + g_free(isa_extensions); } +#endif -void riscv_cpu_list(void) -{ - GSList *list; +#define DEFINE_CPU(type_name, misa_mxl_max, initfn) \ + { \ + .name = (type_name), \ + .parent = TYPE_RISCV_CPU, \ + .instance_init = (initfn), \ + .class_init = riscv_cpu_class_init, \ + .class_data = (void *)(misa_mxl_max) \ + } - list = object_class_get_list(TYPE_RISCV_CPU, false); - list = g_slist_sort(list, riscv_cpu_list_compare); - g_slist_foreach(list, riscv_cpu_list_entry, NULL); - g_slist_free(list); -} +#define DEFINE_DYNAMIC_CPU(type_name, misa_mxl_max, initfn) \ + { \ + .name = (type_name), \ + .parent = TYPE_RISCV_DYNAMIC_CPU, \ + .instance_init = (initfn), \ + .class_init = riscv_cpu_class_init, \ + .class_data = (void *)(misa_mxl_max) \ + } -#define DEFINE_CPU(type_name, initfn) \ - { \ - .name = type_name, \ - .parent = TYPE_RISCV_CPU, \ - .instance_init = initfn \ +#define DEFINE_VENDOR_CPU(type_name, misa_mxl_max, initfn) \ + { \ + .name = (type_name), \ + .parent = TYPE_RISCV_VENDOR_CPU, \ + .instance_init = (initfn), \ + .class_init = riscv_cpu_class_init, \ + .class_data = (void *)(misa_mxl_max) \ + } + +#define DEFINE_BARE_CPU(type_name, misa_mxl_max, initfn) \ + { \ + .name = (type_name), \ + .parent = TYPE_RISCV_BARE_CPU, \ + .instance_init = (initfn), \ + .class_init = riscv_cpu_class_init, \ + .class_data = (void *)(misa_mxl_max) \ + } + +#define DEFINE_PROFILE_CPU(type_name, misa_mxl_max, initfn) \ + { \ + .name = (type_name), \ + .parent = TYPE_RISCV_BARE_CPU, \ + .instance_init = (initfn), \ + .class_init = riscv_cpu_class_init, \ + .class_data = (void *)(misa_mxl_max) \ } static const TypeInfo riscv_cpu_type_infos[] = { @@ -1131,28 +2508,53 @@ static const TypeInfo riscv_cpu_type_infos[] = { .name = TYPE_RISCV_CPU, .parent = TYPE_CPU, .instance_size = sizeof(RISCVCPU), - .instance_align = __alignof__(RISCVCPU), + .instance_align = __alignof(RISCVCPU), .instance_init = riscv_cpu_init, + .instance_post_init = riscv_cpu_post_init, .abstract = true, .class_size = sizeof(RISCVCPUClass), - .class_init = riscv_cpu_class_init, + .class_init = riscv_cpu_common_class_init, + }, + { + .name = TYPE_RISCV_DYNAMIC_CPU, + .parent = TYPE_RISCV_CPU, + .abstract = true, + }, + { + .name = TYPE_RISCV_VENDOR_CPU, + .parent = TYPE_RISCV_CPU, + .abstract = true, + }, + { + .name = TYPE_RISCV_BARE_CPU, + .parent = TYPE_RISCV_CPU, + .instance_init = riscv_bare_cpu_init, + .abstract = true, }, - DEFINE_CPU(TYPE_RISCV_CPU_ANY, riscv_any_cpu_init), -#if defined(CONFIG_KVM) - DEFINE_CPU(TYPE_RISCV_CPU_HOST, riscv_host_cpu_init), -#endif #if defined(TARGET_RISCV32) - DEFINE_CPU(TYPE_RISCV_CPU_BASE32, rv32_base_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_IBEX, rv32_ibex_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31, rv32_sifive_e_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E34, rv32_imafcu_nommu_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rv32_sifive_u_cpu_init), + DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_ANY, MXL_RV32, riscv_any_cpu_init), + DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_MAX, MXL_RV32, riscv_max_cpu_init), + DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE32, MXL_RV32, rv32_base_cpu_init), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_IBEX, MXL_RV32, rv32_ibex_cpu_init), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_E31, MXL_RV32, rv32_sifive_e_cpu_init), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_E34, MXL_RV32, rv32_imafcu_nommu_cpu_init), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_U34, MXL_RV32, rv32_sifive_u_cpu_init), + DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV32I, MXL_RV32, rv32i_bare_cpu_init), + DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV32E, MXL_RV32, rv32e_bare_cpu_init), #elif defined(TARGET_RISCV64) - DEFINE_CPU(TYPE_RISCV_CPU_BASE64, rv64_base_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51, rv64_sifive_e_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rv64_sifive_u_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_SHAKTI_C, rv64_sifive_u_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_BASE128, rv128_base_cpu_init), + DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_ANY, MXL_RV64, riscv_any_cpu_init), + DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_MAX, MXL_RV64, riscv_max_cpu_init), + DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE64, MXL_RV64, rv64_base_cpu_init), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_E51, MXL_RV64, rv64_sifive_e_cpu_init), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_U54, MXL_RV64, rv64_sifive_u_cpu_init), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SHAKTI_C, MXL_RV64, rv64_sifive_u_cpu_init), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_THEAD_C906, MXL_RV64, rv64_thead_c906_cpu_init), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_VEYRON_V1, MXL_RV64, rv64_veyron_v1_cpu_init), + DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE128, MXL_RV128, rv128_base_cpu_init), + DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV64I, MXL_RV64, rv64i_bare_cpu_init), + DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV64E, MXL_RV64, rv64e_bare_cpu_init), + DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22U64, MXL_RV64, rva22u64_profile_cpu_init), + DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22S64, MXL_RV64, rva22s64_profile_cpu_init), #endif }; diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index f08c3e8813..3b1a02b944 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -22,11 +22,26 @@ #include "hw/core/cpu.h" #include "hw/registerfields.h" +#include "hw/qdev-properties.h" #include "exec/cpu-defs.h" +#include "exec/gdbstub.h" #include "qemu/cpu-float.h" #include "qom/object.h" #include "qemu/int128.h" #include "cpu_bits.h" +#include "cpu_cfg.h" +#include "qapi/qapi-types-common.h" +#include "cpu-qom.h" + +typedef struct CPUArchState CPURISCVState; + +#define CPU_RESOLVING_TYPE TYPE_RISCV_CPU + +#if defined(TARGET_RISCV32) +# define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE32 +#elif defined(TARGET_RISCV64) +# define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE64 +#endif #define TCG_GUEST_DEFAULT_MO 0 @@ -36,33 +51,12 @@ */ #define TARGET_INSN_START_EXTRA_WORDS 1 -#define TYPE_RISCV_CPU "riscv-cpu" - -#define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU -#define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX) -#define CPU_RESOLVING_TYPE TYPE_RISCV_CPU - -#define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any") -#define TYPE_RISCV_CPU_BASE32 RISCV_CPU_TYPE_NAME("rv32") -#define TYPE_RISCV_CPU_BASE64 RISCV_CPU_TYPE_NAME("rv64") -#define TYPE_RISCV_CPU_BASE128 RISCV_CPU_TYPE_NAME("x-rv128") -#define TYPE_RISCV_CPU_IBEX RISCV_CPU_TYPE_NAME("lowrisc-ibex") -#define TYPE_RISCV_CPU_SHAKTI_C RISCV_CPU_TYPE_NAME("shakti-c") -#define TYPE_RISCV_CPU_SIFIVE_E31 RISCV_CPU_TYPE_NAME("sifive-e31") -#define TYPE_RISCV_CPU_SIFIVE_E34 RISCV_CPU_TYPE_NAME("sifive-e34") -#define TYPE_RISCV_CPU_SIFIVE_E51 RISCV_CPU_TYPE_NAME("sifive-e51") -#define TYPE_RISCV_CPU_SIFIVE_U34 RISCV_CPU_TYPE_NAME("sifive-u34") -#define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54") -#define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host") - -#if defined(TARGET_RISCV32) -# define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE32 -#elif defined(TARGET_RISCV64) -# define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE64 -#endif - #define RV(x) ((target_ulong)1 << (x - 'A')) +/* + * Update misa_bits[], misa_ext_info_arr[] and misa_ext_cfgs[] + * when adding new MISA bits here. + */ #define RVI RV('I') #define RVE RV('E') /* E and I are mutually exclusive */ #define RVM RV('M') @@ -75,28 +69,45 @@ #define RVU RV('U') #define RVH RV('H') #define RVJ RV('J') +#define RVG RV('G') +#define RVB RV('B') -/* 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 - is currently no bit in misa to indicate whether an MMU exists or not - so a cpu features bitfield is required, likewise for optional PMP support */ -enum { - RISCV_FEATURE_MMU, - RISCV_FEATURE_PMP, - RISCV_FEATURE_EPMP, - RISCV_FEATURE_MISA, - RISCV_FEATURE_AIA, - RISCV_FEATURE_DEBUG -}; +extern const uint32_t misa_bits[]; +const char *riscv_get_misa_ext_name(uint32_t bit); +const char *riscv_get_misa_ext_description(uint32_t bit); + +#define CPU_CFG_OFFSET(_prop) offsetof(struct RISCVCPUConfig, _prop) + +typedef struct riscv_cpu_profile { + struct riscv_cpu_profile *parent; + const char *name; + uint32_t misa_ext; + bool enabled; + bool user_set; + int priv_spec; + int satp_mode; + const int32_t ext_offsets[]; +} RISCVCPUProfile; + +#define RISCV_PROFILE_EXT_LIST_END -1 +#define RISCV_PROFILE_ATTR_UNUSED -1 + +extern RISCVCPUProfile *riscv_profiles[]; /* Privileged specification version */ +#define PRIV_VER_1_10_0_STR "v1.10.0" +#define PRIV_VER_1_11_0_STR "v1.11.0" +#define PRIV_VER_1_12_0_STR "v1.12.0" enum { PRIV_VERSION_1_10_0 = 0, PRIV_VERSION_1_11_0, PRIV_VERSION_1_12_0, + + PRIV_VERSION_LATEST = PRIV_VERSION_1_12_0, }; #define VEXT_VERSION_1_00_0 0x00010000 +#define VEXT_VER_1_00_0_STR "v1.0" enum { TRANSLATE_SUCCESS, @@ -105,18 +116,26 @@ enum { TRANSLATE_G_STAGE_FAIL }; +/* Extension context status */ +typedef enum { + EXT_STATUS_DISABLED = 0, + EXT_STATUS_INITIAL, + EXT_STATUS_CLEAN, + EXT_STATUS_DIRTY, +} RISCVExtStatus; + #define MMU_USER_IDX 3 #define MAX_RISCV_PMPS (16) -typedef struct CPUArchState CPURISCVState; - #if !defined(CONFIG_USER_ONLY) #include "pmp.h" #include "debug.h" #endif #define RV_VLEN_MAX 1024 +#define RV_MAX_MHPMEVENTS 32 +#define RV_MAX_MHPMCOUNTERS 32 FIELD(VTYPE, VLMUL, 0, 3) FIELD(VTYPE, VSEW, 3, 3) @@ -125,10 +144,23 @@ FIELD(VTYPE, VMA, 7, 1) FIELD(VTYPE, VEDIV, 8, 2) FIELD(VTYPE, RESERVED, 10, sizeof(target_ulong) * 8 - 11) +typedef struct PMUCTRState { + /* Current value of a counter */ + target_ulong mhpmcounter_val; + /* Current value of a counter in RV32 */ + target_ulong mhpmcounterh_val; + /* Snapshot values of counter */ + target_ulong mhpmcounter_prev; + /* Snapshort value of a counter in RV32 */ + target_ulong mhpmcounterh_prev; + bool started; + /* Value beyond UINT32_MAX/UINT64_MAX before overflow interrupt trigger */ + target_ulong irq_overflow_left; +} PMUCTRState; + struct CPUArchState { target_ulong gpr[32]; target_ulong gprh[32]; /* 64 top bits of the 128-bit registers */ - uint64_t fpr[32]; /* assume both F and D extensions */ /* vector coprocessor state. */ uint64_t vreg[32 * RV_VLEN_MAX / 64] QEMU_ALIGNED(16); @@ -143,7 +175,10 @@ struct CPUArchState { target_ulong load_res; target_ulong load_val; + /* Floating-Point state */ + uint64_t fpr[32]; /* assume both F and D extensions */ target_ulong frm; + float_status fp_status; target_ulong badaddr; target_ulong bins; @@ -151,12 +186,10 @@ struct CPUArchState { target_ulong guest_phys_fault_addr; target_ulong priv_ver; - target_ulong bext_ver; target_ulong vext_ver; /* RISCVMXL, but uint32_t for vmstate migration */ uint32_t misa_mxl; /* current mxl */ - uint32_t misa_mxl_max; /* max mxl for this cpu */ uint32_t misa_ext; /* current extensions */ uint32_t misa_ext_mask; /* max ext for this cpu */ uint32_t xl; /* current xlen */ @@ -164,7 +197,7 @@ struct CPUArchState { /* 128-bit helpers upper part return value */ target_ulong retxh; - uint32_t features; + target_ulong jvt; #ifdef CONFIG_USER_ONLY uint32_t elf_flags; @@ -173,9 +206,9 @@ struct CPUArchState { #ifndef CONFIG_USER_ONLY target_ulong priv; /* This contains QEMU specific information about the virt state. */ - target_ulong virt; + bool virt_enabled; target_ulong geilen; - target_ulong resetvec; + uint64_t resetvec; target_ulong mhartid; /* @@ -199,6 +232,18 @@ struct CPUArchState { uint64_t mie; uint64_t mideleg; + /* + * When mideleg[i]=0 and mvien[i]=1, sie[i] is no more + * alias of mie[i] and needs to be maintained separately. + */ + uint64_t sie; + + /* + * When hideleg[i]=0 and hvien[i]=1, vsie[i] is no more + * alias of sie[i] (mie[i]) and needs to be maintained separately. + */ + uint64_t vsie; + target_ulong satp; /* since: priv-1.10.0 */ target_ulong stval; target_ulong medeleg; @@ -219,18 +264,28 @@ struct CPUArchState { /* AIA CSRs */ target_ulong miselect; target_ulong siselect; + uint64_t mvien; + uint64_t mvip; /* Hypervisor CSRs */ target_ulong hstatus; target_ulong hedeleg; uint64_t hideleg; - target_ulong hcounteren; + uint32_t hcounteren; target_ulong htval; target_ulong htinst; target_ulong hgatp; target_ulong hgeie; target_ulong hgeip; uint64_t htimedelta; + uint64_t hvien; + + /* + * Bits VSSIP, VSTIP and VSEIP in hvip are maintained in mip. Other bits + * from 0:12 are reserved. Bits 13:63 are not aliased and must be separately + * maintain in hvip. + */ + uint64_t hvip; /* Hypervisor controlled virtual interrupt priorities */ target_ulong hvictl; @@ -268,20 +323,38 @@ struct CPUArchState { target_ulong satp_hs; uint64_t mstatus_hs; - /* Signals whether the current exception occurred with two-stage address - translation active. */ + /* + * Signals whether the current exception occurred with two-stage address + * translation active. + */ bool two_stage_lookup; + /* + * Signals whether the current exception occurred while doing two-stage + * address translation for the VS-stage page table walk. + */ + bool two_stage_indirect_lookup; - target_ulong scounteren; - target_ulong mcounteren; + uint32_t scounteren; + uint32_t mcounteren; + + uint32_t mcountinhibit; + + /* PMU counter state */ + PMUCTRState pmu_ctrs[RV_MAX_MHPMCOUNTERS]; + + /* PMU event selector configured values. First three are unused */ + target_ulong mhpmevent_val[RV_MAX_MHPMEVENTS]; + + /* PMU event selector configured values for RV32 */ + target_ulong mhpmeventh_val[RV_MAX_MHPMEVENTS]; target_ulong sscratch; target_ulong mscratch; - /* temporary htif regs */ - uint64_t mfromhost; - uint64_t mtohost; - uint64_t timecmp; + /* Sstc CSRs */ + uint64_t stimecmp; + + uint64_t vstimecmp; /* physical memory protection */ pmp_table_t pmp_state; @@ -289,7 +362,15 @@ struct CPUArchState { /* trigger module */ target_ulong trigger_cur; - type2_trigger_t type2_trig[TRIGGER_TYPE2_NUM]; + target_ulong tdata1[RV_MAX_TRIGGERS]; + target_ulong tdata2[RV_MAX_TRIGGERS]; + target_ulong tdata3[RV_MAX_TRIGGERS]; + target_ulong mcontext; + struct CPUBreakpoint *cpu_breakpoint[RV_MAX_TRIGGERS]; + struct CPUWatchpoint *cpu_watchpoint[RV_MAX_TRIGGERS]; + QEMUTimer *itrigger_timer[RV_MAX_TRIGGERS]; + int64_t last_icount; + bool itrigger_enabled; /* machine specific rdtime callback */ uint64_t (*rdtime_fn)(void *); @@ -325,136 +406,72 @@ struct CPUArchState { target_ulong upmmask; target_ulong upmbase; - /* CSRs for execution enviornment configuration */ + /* CSRs for execution environment configuration */ uint64_t menvcfg; + uint64_t mstateen[SMSTATEEN_MAX_COUNT]; + uint64_t hstateen[SMSTATEEN_MAX_COUNT]; + uint64_t sstateen[SMSTATEEN_MAX_COUNT]; target_ulong senvcfg; uint64_t henvcfg; #endif target_ulong cur_pmmask; target_ulong cur_pmbase; - float_status fp_status; - /* Fields from here on are preserved across CPU reset. */ - QEMUTimer *timer; /* Internal timer */ + QEMUTimer *stimer; /* Internal timer for S-mode interrupt */ + QEMUTimer *vstimer; /* Internal timer for VS-mode interrupt */ + bool vstime_irq; hwaddr kernel_addr; hwaddr fdt_addr; +#ifdef CONFIG_KVM /* kvm timer */ bool kvm_timer_dirty; uint64_t kvm_timer_time; uint64_t kvm_timer_compare; uint64_t kvm_timer_state; uint64_t kvm_timer_frequency; +#endif /* CONFIG_KVM */ }; -OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU) - -/** - * RISCVCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * - * A RISCV CPU model. - */ -struct RISCVCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - DeviceRealize parent_realize; - DeviceReset parent_reset; -}; - -struct RISCVCPUConfig { - bool ext_i; - bool ext_e; - bool ext_g; - bool ext_m; - bool ext_a; - bool ext_f; - bool ext_d; - bool ext_c; - bool ext_s; - bool ext_u; - bool ext_h; - bool ext_j; - bool ext_v; - bool ext_zba; - bool ext_zbb; - bool ext_zbc; - bool ext_zbkb; - bool ext_zbkc; - bool ext_zbkx; - bool ext_zbs; - bool ext_zk; - bool ext_zkn; - bool ext_zknd; - bool ext_zkne; - bool ext_zknh; - bool ext_zkr; - bool ext_zks; - bool ext_zksed; - bool ext_zksh; - bool ext_zkt; - bool ext_counters; - bool ext_ifencei; - bool ext_icsr; - bool ext_svinval; - bool ext_svnapot; - bool ext_svpbmt; - bool ext_zdinx; - bool ext_zfh; - bool ext_zfhmin; - bool ext_zfinx; - bool ext_zhinx; - bool ext_zhinxmin; - bool ext_zve32f; - bool ext_zve64f; - - uint32_t mvendorid; - uint64_t marchid; - uint64_t mimpid; - - /* Vendor-specific custom extensions */ - bool ext_XVentanaCondOps; - - char *priv_spec; - char *user_spec; - char *bext_spec; - char *vext_spec; - uint16_t vlen; - uint16_t elen; - bool mmu; - bool pmp; - bool epmp; - bool aia; - bool debug; - uint64_t resetvec; - - bool short_isa_string; -}; - -typedef struct RISCVCPUConfig RISCVCPUConfig; - -/** +/* * RISCVCPU: * @env: #CPURISCVState * * A RISCV CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; + CPURISCVState env; - char *dyn_csr_xml; - char *dyn_vreg_xml; + GDBFeature dyn_csr_feature; + GDBFeature dyn_vreg_feature; /* Configuration Settings */ RISCVCPUConfig cfg; + + QEMUTimer *pmu_timer; + /* A bitmask of Available programmable counters */ + uint32_t pmu_avail_ctrs; + /* Mapping of events to counters */ + GHashTable *pmu_event_ctr_map; +}; + +/** + * RISCVCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A RISCV CPU model. + */ +struct RISCVCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; + uint32_t misa_mxl_max; /* max mxl for this cpu */ }; static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) @@ -462,16 +479,6 @@ static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) return (env->misa_ext & ext) != 0; } -static inline bool riscv_feature(CPURISCVState *env, int feature) -{ - return env->features & (1ULL << feature); -} - -static inline void riscv_set_feature(CPURISCVState *env, int feature) -{ - env->features |= (1ULL << feature); -} - #include "cpu_user.h" extern const char * const riscv_int_regnames[]; @@ -481,13 +488,14 @@ extern const char * const riscv_fpr_regnames[]; const char *riscv_cpu_get_trap_name(target_ulong cause, bool async); void riscv_cpu_do_interrupt(CPUState *cpu); int riscv_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque); + int cpuid, DumpState *s); int riscv_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque); + int cpuid, DumpState *s); 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); int riscv_cpu_hviprio_index2irq(int index, int *out_irq, int *out_rdzero); uint8_t riscv_cpu_default_priority(int irq); +uint64_t riscv_cpu_all_pending(CPURISCVState *env); int riscv_cpu_mirq_pending(CPURISCVState *env); int riscv_cpu_sirq_pending(CPURISCVState *env); int riscv_cpu_vsirq_pending(CPURISCVState *env); @@ -495,33 +503,32 @@ bool riscv_cpu_fp_enabled(CPURISCVState *env); target_ulong riscv_cpu_get_geilen(CPURISCVState *env); void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen); bool riscv_cpu_vector_enabled(CPURISCVState *env); -bool riscv_cpu_virt_enabled(CPURISCVState *env); void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable); -bool riscv_cpu_two_stage_lookup(int mmu_idx); -int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch); -hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int riscv_env_mmu_index(CPURISCVState *env, bool ifetch); G_NORETURN void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr); + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr); bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +char *riscv_isa_string(RISCVCPU *cpu); +int riscv_cpu_max_xlen(RISCVCPUClass *mcc); +bool riscv_cpu_option_set(const char *optname); + +#ifndef CONFIG_USER_ONLY +void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename); void riscv_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, unsigned size, MMUAccessType access_type, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr); -char *riscv_isa_string(RISCVCPU *cpu); -void riscv_cpu_list(void); - -#define cpu_list riscv_cpu_list -#define cpu_mmu_index riscv_cpu_mmu_index - -#ifndef CONFIG_USER_ONLY +hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request); void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env); int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts); -uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value); +uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask, + uint64_t value); +void riscv_cpu_interrupt(CPURISCVState *env); #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 *), void *arg); @@ -532,6 +539,8 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, target_ulong new_val, target_ulong write_mask), void *rmw_fn_arg); + +RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit); #endif void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv); @@ -542,29 +551,30 @@ G_NORETURN void riscv_raise_exception(CPURISCVState *env, target_ulong riscv_cpu_get_fflags(CPURISCVState *env); void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong); -#define TB_FLAGS_PRIV_MMU_MASK 3 -#define TB_FLAGS_PRIV_HYP_ACCESS_MASK (1 << 2) -#define TB_FLAGS_MSTATUS_FS MSTATUS_FS -#define TB_FLAGS_MSTATUS_VS MSTATUS_VS - #include "exec/cpu-all.h" FIELD(TB_FLAGS, MEM_IDX, 0, 3) -FIELD(TB_FLAGS, LMUL, 3, 3) -FIELD(TB_FLAGS, SEW, 6, 3) -/* Skip MSTATUS_VS (0x600) bits */ -FIELD(TB_FLAGS, VL_EQ_VLMAX, 11, 1) -FIELD(TB_FLAGS, VILL, 12, 1) -/* Skip MSTATUS_FS (0x6000) bits */ -/* Is a Hypervisor instruction load/store allowed? */ -FIELD(TB_FLAGS, HLSX, 15, 1) -FIELD(TB_FLAGS, MSTATUS_HS_FS, 16, 2) -FIELD(TB_FLAGS, MSTATUS_HS_VS, 18, 2) +FIELD(TB_FLAGS, FS, 3, 2) +/* Vector flags */ +FIELD(TB_FLAGS, VS, 5, 2) +FIELD(TB_FLAGS, LMUL, 7, 3) +FIELD(TB_FLAGS, SEW, 10, 3) +FIELD(TB_FLAGS, VL_EQ_VLMAX, 13, 1) +FIELD(TB_FLAGS, VILL, 14, 1) +FIELD(TB_FLAGS, VSTART_EQ_ZERO, 15, 1) /* The combination of MXL/SXL/UXL that applies to the current cpu mode. */ -FIELD(TB_FLAGS, XL, 20, 2) +FIELD(TB_FLAGS, XL, 16, 2) /* If PointerMasking should be applied */ -FIELD(TB_FLAGS, PM_MASK_ENABLED, 22, 1) -FIELD(TB_FLAGS, PM_BASE_ENABLED, 23, 1) +FIELD(TB_FLAGS, PM_MASK_ENABLED, 18, 1) +FIELD(TB_FLAGS, PM_BASE_ENABLED, 19, 1) +FIELD(TB_FLAGS, VTA, 20, 1) +FIELD(TB_FLAGS, VMA, 21, 1) +/* Native debug itrigger */ +FIELD(TB_FLAGS, ITRIGGER, 22, 1) +/* Virtual mode enabled */ +FIELD(TB_FLAGS, VIRT_ENABLED, 23, 1) +FIELD(TB_FLAGS, PRIV, 24, 2) +FIELD(TB_FLAGS, AXL, 26, 2) #ifdef TARGET_RISCV32 #define riscv_cpu_mxl(env) ((void)(env), MXL_RV32) @@ -576,13 +586,25 @@ static inline RISCVMXL riscv_cpu_mxl(CPURISCVState *env) #endif #define riscv_cpu_mxl_bits(env) (1UL << (4 + riscv_cpu_mxl(env))) -#if defined(TARGET_RISCV32) -#define cpu_recompute_xl(env) ((void)(env), MXL_RV32) -#else -static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env) +static inline const RISCVCPUConfig *riscv_cpu_cfg(CPURISCVState *env) +{ + return &env_archcpu(env)->cfg; +} + +#if !defined(CONFIG_USER_ONLY) +static inline int cpu_address_mode(CPURISCVState *env) +{ + int mode = env->priv; + + if (mode == PRV_M && get_field(env->mstatus, MSTATUS_MPRV)) { + mode = get_field(env->mstatus, MSTATUS_MPP); + } + return mode; +} + +static inline RISCVMXL cpu_get_xl(CPURISCVState *env, target_ulong mode) { RISCVMXL xl = env->misa_mxl; -#if !defined(CONFIG_USER_ONLY) /* * When emulating a 32-bit-only cpu, use RV32. * When emulating a 64-bit cpu, and MXL has been reduced to RV32, @@ -590,22 +612,49 @@ static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env) * back to RV64 for lower privs. */ if (xl != MXL_RV32) { - switch (env->priv) { + switch (mode) { case PRV_M: break; case PRV_U: xl = get_field(env->mstatus, MSTATUS64_UXL); break; - default: /* PRV_S | PRV_H */ + default: /* PRV_S */ xl = get_field(env->mstatus, MSTATUS64_SXL); break; } } -#endif return xl; } #endif +#if defined(TARGET_RISCV32) +#define cpu_recompute_xl(env) ((void)(env), MXL_RV32) +#else +static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env) +{ +#if !defined(CONFIG_USER_ONLY) + return cpu_get_xl(env, env->priv); +#else + return env->misa_mxl; +#endif +} +#endif + +#if defined(TARGET_RISCV32) +#define cpu_address_xl(env) ((void)(env), MXL_RV32) +#else +static inline RISCVMXL cpu_address_xl(CPURISCVState *env) +{ +#ifdef CONFIG_USER_ONLY + return env->xl; +#else + int mode = cpu_address_mode(env); + + return cpu_get_xl(env, mode); +#endif +} +#endif + static inline int riscv_cpu_xlen(CPURISCVState *env) { return 16 << env->xl; @@ -642,17 +691,24 @@ static inline RISCVMXL riscv_cpu_sxl(CPURISCVState *env) * = 256 >> 7 * = 2 */ -static inline uint32_t vext_get_vlmax(RISCVCPU *cpu, target_ulong vtype) +static inline uint32_t vext_get_vlmax(uint32_t vlenb, uint32_t vsew, + int8_t lmul) { - uint8_t sew = FIELD_EX64(vtype, VTYPE, VSEW); - int8_t lmul = sextract32(FIELD_EX64(vtype, VTYPE, VLMUL), 0, 3); - return cpu->cfg.vlen >> (sew + 3 - lmul); + uint32_t vlen = vlenb << 3; + + /* + * We need to use 'vlen' instead of 'vlenb' to + * preserve the '+ 3' in the formula. Otherwise + * we risk a negative shift if vsew < lmul. + */ + return vlen >> (vsew + 3 - lmul); } -void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *pflags); +void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags); void riscv_cpu_update_mask(CPURISCVState *env); +bool riscv_cpu_is_32bit(RISCVCPU *cpu); RISCVException riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, @@ -712,12 +768,60 @@ enum { CSR_TABLE_SIZE = 0x1000 }; +/* + * The event id are encoded based on the encoding specified in the + * SBI specification v0.3 + */ + +enum riscv_pmu_event_idx { + RISCV_PMU_EVENT_HW_CPU_CYCLES = 0x01, + RISCV_PMU_EVENT_HW_INSTRUCTIONS = 0x02, + RISCV_PMU_EVENT_CACHE_DTLB_READ_MISS = 0x10019, + RISCV_PMU_EVENT_CACHE_DTLB_WRITE_MISS = 0x1001B, + RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS = 0x10021, +}; + +/* used by tcg/tcg-cpu.c*/ +void isa_ext_update_enabled(RISCVCPU *cpu, uint32_t ext_offset, bool en); +bool isa_ext_is_enabled(RISCVCPU *cpu, uint32_t ext_offset); +void riscv_cpu_set_misa_ext(CPURISCVState *env, uint32_t ext); +bool riscv_cpu_is_vendor(Object *cpu_obj); + +typedef struct RISCVCPUMultiExtConfig { + const char *name; + uint32_t offset; + bool enabled; +} RISCVCPUMultiExtConfig; + +extern const RISCVCPUMultiExtConfig riscv_cpu_extensions[]; +extern const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[]; +extern const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[]; +extern const RISCVCPUMultiExtConfig riscv_cpu_named_features[]; +extern const RISCVCPUMultiExtConfig riscv_cpu_deprecated_exts[]; + +typedef struct isa_ext_data { + const char *name; + int min_version; + int ext_enable_offset; +} RISCVIsaExtData; +extern const RISCVIsaExtData isa_edata_arr[]; +char *riscv_cpu_get_name(RISCVCPU *cpu); + +void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp); +void riscv_add_satp_mode_properties(Object *obj); +bool riscv_cpu_accelerator_compatible(RISCVCPU *cpu); + /* CSR function table */ extern riscv_csr_operations csr_ops[CSR_TABLE_SIZE]; +extern const bool valid_vm_1_10_32[], valid_vm_1_10_64[]; + void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops); void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops); void riscv_cpu_register_gdb_regs_for_features(CPUState *cs); +uint8_t satp_mode_max_from_map(uint32_t map); +const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit); + #endif /* RISCV_CPU_H */ diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 4d04b20d06..fc2068ee4d 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -9,6 +9,9 @@ (((uint64_t)(val) * ((mask) & ~((mask) << 1))) & \ (uint64_t)(mask))) +/* Extension context status mask */ +#define EXT_STATUS_MASK 0x3ULL + /* Floating point round mode */ #define FSR_RD_SHIFT 5 #define FSR_RD (0x7 << FSR_RD_SHIFT) @@ -29,14 +32,6 @@ #define FSR_NXA (FPEXC_NX << FSR_AEXC_SHIFT) #define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA) -/* Vector Fixed-Point round model */ -#define FSR_VXRM_SHIFT 9 -#define FSR_VXRM (0x3 << FSR_VXRM_SHIFT) - -/* Vector Fixed-Point saturation flag */ -#define FSR_VXSAT_SHIFT 8 -#define FSR_VXSAT (0x1 << FSR_VXSAT_SHIFT) - /* Control and Status Registers */ /* User Trap Setup */ @@ -174,14 +169,8 @@ #define CSR_MIREG 0x351 /* Machine-Level Interrupts (AIA) */ -#define CSR_MTOPI 0xfb0 - -/* Machine-Level IMSIC Interface (AIA) */ -#define CSR_MSETEIPNUM 0x358 -#define CSR_MCLREIPNUM 0x359 -#define CSR_MSETEIENUM 0x35a -#define CSR_MCLREIENUM 0x35b #define CSR_MTOPEI 0x35c +#define CSR_MTOPI 0xfb0 /* Virtual Interrupts for Supervisor Level (AIA) */ #define CSR_MVIEN 0x308 @@ -196,8 +185,6 @@ /* Supervisor Trap Setup */ #define CSR_SSTATUS 0x100 -#define CSR_SEDELEG 0x102 -#define CSR_SIDELEG 0x103 #define CSR_SIE 0x104 #define CSR_STVEC 0x105 #define CSR_SCOUNTEREN 0x106 @@ -205,6 +192,12 @@ /* Supervisor Configuration CSRs */ #define CSR_SENVCFG 0x10A +/* Supervisor state CSRs */ +#define CSR_SSTATEEN0 0x10C +#define CSR_SSTATEEN1 0x10D +#define CSR_SSTATEEN2 0x10E +#define CSR_SSTATEEN3 0x10F + /* Supervisor Trap Handling */ #define CSR_SSCRATCH 0x140 #define CSR_SEPC 0x141 @@ -212,6 +205,10 @@ #define CSR_STVAL 0x143 #define CSR_SIP 0x144 +/* Sstc supervisor CSRs */ +#define CSR_STIMECMP 0x14D +#define CSR_STIMECMPH 0x15D + /* Supervisor Protection and Translation */ #define CSR_SPTBR 0x180 #define CSR_SATP 0x180 @@ -221,14 +218,8 @@ #define CSR_SIREG 0x151 /* Supervisor-Level Interrupts (AIA) */ -#define CSR_STOPI 0xdb0 - -/* Supervisor-Level IMSIC Interface (AIA) */ -#define CSR_SSETEIPNUM 0x158 -#define CSR_SCLREIPNUM 0x159 -#define CSR_SSETEIENUM 0x15a -#define CSR_SCLREIENUM 0x15b #define CSR_STOPEI 0x15c +#define CSR_STOPI 0xdb0 /* Supervisor-Level High-Half CSRs (AIA) */ #define CSR_SIEH 0x114 @@ -254,6 +245,16 @@ #define CSR_HENVCFG 0x60A #define CSR_HENVCFGH 0x61A +/* Hypervisor state CSRs */ +#define CSR_HSTATEEN0 0x60C +#define CSR_HSTATEEN0H 0x61C +#define CSR_HSTATEEN1 0x60D +#define CSR_HSTATEEN1H 0x61D +#define CSR_HSTATEEN2 0x60E +#define CSR_HSTATEEN2H 0x61E +#define CSR_HSTATEEN3 0x60F +#define CSR_HSTATEEN3H 0x61F + /* Virtual CSRs */ #define CSR_VSSTATUS 0x200 #define CSR_VSIE 0x204 @@ -265,6 +266,10 @@ #define CSR_VSIP 0x244 #define CSR_VSATP 0x280 +/* Sstc virtual CSRs */ +#define CSR_VSTIMECMP 0x24D +#define CSR_VSTIMECMPH 0x25D + #define CSR_MTINST 0x34a #define CSR_MTVAL2 0x34b @@ -279,14 +284,8 @@ #define CSR_VSIREG 0x251 /* VS-Level Interrupts (H-extension with AIA) */ -#define CSR_VSTOPI 0xeb0 - -/* VS-Level IMSIC Interface (H-extension with AIA) */ -#define CSR_VSSETEIPNUM 0x258 -#define CSR_VSCLREIPNUM 0x259 -#define CSR_VSSETEIENUM 0x25a -#define CSR_VSCLREIENUM 0x25b #define CSR_VSTOPEI 0x25c +#define CSR_VSTOPI 0xeb0 /* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ #define CSR_HIDELEGH 0x613 @@ -301,6 +300,28 @@ #define CSR_MENVCFG 0x30A #define CSR_MENVCFGH 0x31A +/* Machine state CSRs */ +#define CSR_MSTATEEN0 0x30C +#define CSR_MSTATEEN0H 0x31C +#define CSR_MSTATEEN1 0x30D +#define CSR_MSTATEEN1H 0x31D +#define CSR_MSTATEEN2 0x30E +#define CSR_MSTATEEN2H 0x31E +#define CSR_MSTATEEN3 0x30F +#define CSR_MSTATEEN3H 0x31F + +/* Common defines for all smstateen */ +#define SMSTATEEN_MAX_COUNT 4 +#define SMSTATEEN0_CS (1ULL << 0) +#define SMSTATEEN0_FCSR (1ULL << 1) +#define SMSTATEEN0_JVT (1ULL << 2) +#define SMSTATEEN0_HSCONTXT (1ULL << 57) +#define SMSTATEEN0_IMSIC (1ULL << 58) +#define SMSTATEEN0_AIA (1ULL << 59) +#define SMSTATEEN0_SVSLCT (1ULL << 60) +#define SMSTATEEN0_HSENVCFG (1ULL << 62) +#define SMSTATEEN_STATEEN (1ULL << 63) + /* Enhanced Physical Memory Protection (ePMP) */ #define CSR_MSECCFG 0x747 #define CSR_MSECCFGH 0x757 @@ -331,6 +352,8 @@ #define CSR_TDATA1 0x7a1 #define CSR_TDATA2 0x7a2 #define CSR_TDATA3 0x7a3 +#define CSR_TINFO 0x7a4 +#define CSR_MCONTEXT 0x7a8 /* Debug Mode Registers */ #define CSR_DCSR 0x7b0 @@ -367,6 +390,10 @@ #define CSR_MHPMCOUNTER29 0xb1d #define CSR_MHPMCOUNTER30 0xb1e #define CSR_MHPMCOUNTER31 0xb1f + +/* Machine counter-inhibit register */ +#define CSR_MCOUNTINHIBIT 0x320 + #define CSR_MHPMEVENT3 0x323 #define CSR_MHPMEVENT4 0x324 #define CSR_MHPMEVENT5 0x325 @@ -396,6 +423,37 @@ #define CSR_MHPMEVENT29 0x33d #define CSR_MHPMEVENT30 0x33e #define CSR_MHPMEVENT31 0x33f + +#define CSR_MHPMEVENT3H 0x723 +#define CSR_MHPMEVENT4H 0x724 +#define CSR_MHPMEVENT5H 0x725 +#define CSR_MHPMEVENT6H 0x726 +#define CSR_MHPMEVENT7H 0x727 +#define CSR_MHPMEVENT8H 0x728 +#define CSR_MHPMEVENT9H 0x729 +#define CSR_MHPMEVENT10H 0x72a +#define CSR_MHPMEVENT11H 0x72b +#define CSR_MHPMEVENT12H 0x72c +#define CSR_MHPMEVENT13H 0x72d +#define CSR_MHPMEVENT14H 0x72e +#define CSR_MHPMEVENT15H 0x72f +#define CSR_MHPMEVENT16H 0x730 +#define CSR_MHPMEVENT17H 0x731 +#define CSR_MHPMEVENT18H 0x732 +#define CSR_MHPMEVENT19H 0x733 +#define CSR_MHPMEVENT20H 0x734 +#define CSR_MHPMEVENT21H 0x735 +#define CSR_MHPMEVENT22H 0x736 +#define CSR_MHPMEVENT23H 0x737 +#define CSR_MHPMEVENT24H 0x738 +#define CSR_MHPMEVENT25H 0x739 +#define CSR_MHPMEVENT26H 0x73a +#define CSR_MHPMEVENT27H 0x73b +#define CSR_MHPMEVENT28H 0x73c +#define CSR_MHPMEVENT29H 0x73d +#define CSR_MHPMEVENT30H 0x73e +#define CSR_MHPMEVENT31H 0x73f + #define CSR_MHPMCOUNTER3H 0xb83 #define CSR_MHPMCOUNTER4H 0xb84 #define CSR_MHPMCOUNTER5H 0xb85 @@ -457,10 +515,14 @@ #define CSR_VSMTE 0x2c0 #define CSR_VSPMMASK 0x2c1 #define CSR_VSPMBASE 0x2c2 +#define CSR_SCOUNTOVF 0xda0 /* Crypto Extension */ #define CSR_SEED 0x015 +/* Zcmt Extension */ +#define CSR_JVT 0x017 + /* mstatus CSR bits */ #define MSTATUS_UIE 0x00000001 #define MSTATUS_SIE 0x00000002 @@ -542,12 +604,9 @@ typedef enum { /* Privilege modes */ #define PRV_U 0 #define PRV_S 1 -#define PRV_H 2 /* Reserved */ +#define PRV_RESERVED 2 #define PRV_M 3 -/* Virtulisation Register Fields */ -#define VIRT_ONOFF 1 - /* RV32 satp CSR field masks */ #define SATP32_MODE 0x80000000 #define SATP32_ASID 0x7fc00000 @@ -578,6 +637,7 @@ typedef enum { #define PTE_SOFT 0x300 /* Reserved for Software */ #define PTE_PBMT 0x6000000000000000ULL /* Page-based memory types */ #define PTE_N 0x8000000000000000ULL /* NAPOT translation */ +#define PTE_RESERVED 0x1FC0000000000000ULL /* Reserved bits */ #define PTE_ATTR (PTE_N | PTE_PBMT) /* All attributes bits */ /* Page table PPN shift amount */ @@ -589,7 +649,7 @@ typedef enum { /* Leaf page shift amount */ #define PGSHIFT 12 -/* Default Reset Vector adress */ +/* Default Reset Vector address */ #define DEFAULT_RSTVEC 0x1000 /* Exception causes */ @@ -634,6 +694,7 @@ typedef enum RISCVException { #define IRQ_VS_EXT 10 #define IRQ_M_EXT 11 #define IRQ_S_GEXT 12 +#define IRQ_PMU_OVF 13 #define IRQ_LOCAL_MAX 16 #define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1) @@ -651,11 +712,13 @@ typedef enum RISCVException { #define MIP_VSEIP (1 << IRQ_VS_EXT) #define MIP_MEIP (1 << IRQ_M_EXT) #define MIP_SGEIP (1 << IRQ_S_GEXT) +#define MIP_LCOFIP (1 << IRQ_PMU_OVF) /* sip masks */ #define SIP_SSIP MIP_SSIP #define SIP_STIP MIP_STIP #define SIP_SEIP MIP_SEIP +#define SIP_LCOFIP MIP_LCOFIP /* MIE masks */ #define MIE_SEIE (1 << IRQ_S_EXT) @@ -665,27 +728,28 @@ typedef enum RISCVException { #define MIE_SSIE (1 << IRQ_S_SOFT) #define MIE_USIE (1 << IRQ_U_SOFT) -/* General PointerMasking CSR bits*/ +/* Machine constants */ +#define M_MODE_INTERRUPTS ((uint64_t)(MIP_MSIP | MIP_MTIP | MIP_MEIP)) +#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP)) +#define VS_MODE_INTERRUPTS ((uint64_t)(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)) +#define HS_MODE_INTERRUPTS ((uint64_t)(MIP_SGEIP | VS_MODE_INTERRUPTS)) + +/* General PointerMasking CSR bits */ #define PM_ENABLE 0x00000001ULL #define PM_CURRENT 0x00000002ULL #define PM_INSN 0x00000004ULL -#define PM_XS_MASK 0x00000003ULL -/* PointerMasking XS bits values */ -#define PM_EXT_DISABLE 0x00000000ULL -#define PM_EXT_INITIAL 0x00000001ULL -#define PM_EXT_CLEAN 0x00000002ULL -#define PM_EXT_DIRTY 0x00000003ULL - -/* Execution enviornment configuration bits */ +/* Execution environment configuration bits */ #define MENVCFG_FIOM BIT(0) #define MENVCFG_CBIE (3UL << 4) #define MENVCFG_CBCFE BIT(6) #define MENVCFG_CBZE BIT(7) +#define MENVCFG_ADUE (1ULL << 61) #define MENVCFG_PBMTE (1ULL << 62) #define MENVCFG_STCE (1ULL << 63) /* For RV32 */ +#define MENVCFGH_ADUE BIT(29) #define MENVCFGH_PBMTE BIT(30) #define MENVCFGH_STCE BIT(31) @@ -698,10 +762,12 @@ typedef enum RISCVException { #define HENVCFG_CBIE MENVCFG_CBIE #define HENVCFG_CBCFE MENVCFG_CBCFE #define HENVCFG_CBZE MENVCFG_CBZE +#define HENVCFG_ADUE MENVCFG_ADUE #define HENVCFG_PBMTE MENVCFG_PBMTE #define HENVCFG_STCE MENVCFG_STCE /* For RV32 */ +#define HENVCFGH_ADUE MENVCFGH_ADUE #define HENVCFGH_PBMTE MENVCFGH_PBMTE #define HENVCFGH_STCE MENVCFGH_STCE @@ -711,7 +777,7 @@ typedef enum RISCVException { #define S_OFFSET 5ULL #define M_OFFSET 8ULL -#define PM_XS_BITS (PM_XS_MASK << XS_OFFSET) +#define PM_XS_BITS (EXT_STATUS_MASK << XS_OFFSET) #define U_PM_ENABLE (PM_ENABLE << U_OFFSET) #define U_PM_CURRENT (PM_CURRENT << U_OFFSET) #define U_PM_INSN (PM_INSN << U_OFFSET) @@ -788,7 +854,7 @@ typedef enum RISCVException { #define IPRIO_IRQ_BITS 8 #define IPRIO_MMAXIPRIO 255 #define IPRIO_DEFAULT_UPPER 4 -#define IPRIO_DEFAULT_MIDDLE (IPRIO_DEFAULT_UPPER + 24) +#define IPRIO_DEFAULT_MIDDLE (IPRIO_DEFAULT_UPPER + 12) #define IPRIO_DEFAULT_M IPRIO_DEFAULT_MIDDLE #define IPRIO_DEFAULT_S (IPRIO_DEFAULT_M + 3) #define IPRIO_DEFAULT_SGEXT (IPRIO_DEFAULT_S + 3) @@ -809,4 +875,33 @@ typedef enum RISCVException { #define SEED_OPST_WAIT (0b01 << 30) #define SEED_OPST_ES16 (0b10 << 30) #define SEED_OPST_DEAD (0b11 << 30) +/* PMU related bits */ +#define MIE_LCOFIE (1 << IRQ_PMU_OVF) + +#define MHPMEVENT_BIT_OF BIT_ULL(63) +#define MHPMEVENTH_BIT_OF BIT(31) +#define MHPMEVENT_BIT_MINH BIT_ULL(62) +#define MHPMEVENTH_BIT_MINH BIT(30) +#define MHPMEVENT_BIT_SINH BIT_ULL(61) +#define MHPMEVENTH_BIT_SINH BIT(29) +#define MHPMEVENT_BIT_UINH BIT_ULL(60) +#define MHPMEVENTH_BIT_UINH BIT(28) +#define MHPMEVENT_BIT_VSINH BIT_ULL(59) +#define MHPMEVENTH_BIT_VSINH BIT(27) +#define MHPMEVENT_BIT_VUINH BIT_ULL(58) +#define MHPMEVENTH_BIT_VUINH BIT(26) + +#define MHPMEVENT_SSCOF_MASK _ULL(0xFFFF000000000000) +#define MHPMEVENT_IDX_MASK 0xFFFFF +#define MHPMEVENT_SSCOF_RESVD 16 + +/* JVT CSR bits */ +#define JVT_MODE 0x3F +#define JVT_BASE (~0x3F) + +/* Debug Sdtrig CSR masks */ +#define MCONTEXT32 0x0000003F +#define MCONTEXT64 0x0000000000001FFFULL +#define MCONTEXT32_HCONTEXT 0x0000007F +#define MCONTEXT64_HCONTEXT 0x0000000000003FFFULL #endif diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h new file mode 100644 index 0000000000..cb750154bd --- /dev/null +++ b/target/riscv/cpu_cfg.h @@ -0,0 +1,210 @@ +/* + * QEMU RISC-V CPU CFG + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, Inc. + * Copyright (c) 2021-2023 PLCT Lab + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef RISCV_CPU_CFG_H +#define RISCV_CPU_CFG_H + +/* + * map is a 16-bit bitmap: the most significant set bit in map is the maximum + * satp mode that is supported. It may be chosen by the user and must respect + * what qemu implements (valid_1_10_32/64) and what the hw is capable of + * (supported bitmap below). + * + * init is a 16-bit bitmap used to make sure the user selected a correct + * configuration as per the specification. + * + * supported is a 16-bit bitmap used to reflect the hw capabilities. + */ +typedef struct { + uint16_t map, init, supported; +} RISCVSATPMap; + +struct RISCVCPUConfig { + bool ext_zba; + bool ext_zbb; + bool ext_zbc; + bool ext_zbkb; + bool ext_zbkc; + bool ext_zbkx; + bool ext_zbs; + bool ext_zca; + bool ext_zcb; + bool ext_zcd; + bool ext_zce; + bool ext_zcf; + bool ext_zcmp; + bool ext_zcmt; + bool ext_zk; + bool ext_zkn; + bool ext_zknd; + bool ext_zkne; + bool ext_zknh; + bool ext_zkr; + bool ext_zks; + bool ext_zksed; + bool ext_zksh; + bool ext_zkt; + bool ext_zifencei; + bool ext_zicntr; + bool ext_zicsr; + bool ext_zicbom; + bool ext_zicbop; + bool ext_zicboz; + bool ext_zicond; + bool ext_zihintntl; + bool ext_zihintpause; + bool ext_zihpm; + bool ext_ztso; + bool ext_smstateen; + bool ext_sstc; + bool ext_svadu; + bool ext_svinval; + bool ext_svnapot; + bool ext_svpbmt; + bool ext_zdinx; + bool ext_zaamo; + bool ext_zacas; + bool ext_zalrsc; + bool ext_zawrs; + bool ext_zfa; + bool ext_zfbfmin; + bool ext_zfh; + bool ext_zfhmin; + bool ext_zfinx; + bool ext_zhinx; + bool ext_zhinxmin; + bool ext_zve32f; + bool ext_zve64f; + bool ext_zve64d; + bool ext_zvbb; + bool ext_zvbc; + bool ext_zvkb; + bool ext_zvkg; + bool ext_zvkned; + bool ext_zvknha; + bool ext_zvknhb; + bool ext_zvksed; + bool ext_zvksh; + bool ext_zvkt; + bool ext_zvkn; + bool ext_zvknc; + bool ext_zvkng; + bool ext_zvks; + bool ext_zvksc; + bool ext_zvksg; + bool ext_zmmul; + bool ext_zvfbfmin; + bool ext_zvfbfwma; + bool ext_zvfh; + bool ext_zvfhmin; + bool ext_smaia; + bool ext_ssaia; + bool ext_sscofpmf; + bool ext_smepmp; + bool rvv_ta_all_1s; + bool rvv_ma_all_1s; + + uint32_t mvendorid; + uint64_t marchid; + uint64_t mimpid; + + /* Named features */ + bool ext_svade; + bool ext_zic64b; + + /* + * Always 'true' booleans for named features + * TCG always implement/can't be user disabled, + * based on spec version. + */ + bool has_priv_1_12; + bool has_priv_1_11; + + /* Vendor-specific custom extensions */ + bool ext_xtheadba; + bool ext_xtheadbb; + bool ext_xtheadbs; + bool ext_xtheadcmo; + bool ext_xtheadcondmov; + bool ext_xtheadfmemidx; + bool ext_xtheadfmv; + bool ext_xtheadmac; + bool ext_xtheadmemidx; + bool ext_xtheadmempair; + bool ext_xtheadsync; + bool ext_XVentanaCondOps; + + uint32_t pmu_mask; + uint16_t vlenb; + uint16_t elen; + uint16_t cbom_blocksize; + uint16_t cbop_blocksize; + uint16_t cboz_blocksize; + bool mmu; + bool pmp; + bool debug; + bool misa_w; + + bool short_isa_string; + +#ifndef CONFIG_USER_ONLY + RISCVSATPMap satp_mode; +#endif +}; + +typedef struct RISCVCPUConfig RISCVCPUConfig; + +/* Helper functions to test for extensions. */ + +static inline bool always_true_p(const RISCVCPUConfig *cfg __attribute__((__unused__))) +{ + return true; +} + +static inline bool has_xthead_p(const RISCVCPUConfig *cfg) +{ + return cfg->ext_xtheadba || cfg->ext_xtheadbb || + cfg->ext_xtheadbs || cfg->ext_xtheadcmo || + cfg->ext_xtheadcondmov || + cfg->ext_xtheadfmemidx || cfg->ext_xtheadfmv || + cfg->ext_xtheadmac || cfg->ext_xtheadmemidx || + cfg->ext_xtheadmempair || cfg->ext_xtheadsync; +} + +#define MATERIALISE_EXT_PREDICATE(ext) \ + static inline bool has_ ## ext ## _p(const RISCVCPUConfig *cfg) \ + { \ + return cfg->ext_ ## ext ; \ + } + +MATERIALISE_EXT_PREDICATE(xtheadba) +MATERIALISE_EXT_PREDICATE(xtheadbb) +MATERIALISE_EXT_PREDICATE(xtheadbs) +MATERIALISE_EXT_PREDICATE(xtheadcmo) +MATERIALISE_EXT_PREDICATE(xtheadcondmov) +MATERIALISE_EXT_PREDICATE(xtheadfmemidx) +MATERIALISE_EXT_PREDICATE(xtheadfmv) +MATERIALISE_EXT_PREDICATE(xtheadmac) +MATERIALISE_EXT_PREDICATE(xtheadmemidx) +MATERIALISE_EXT_PREDICATE(xtheadmempair) +MATERIALISE_EXT_PREDICATE(xtheadsync) +MATERIALISE_EXT_PREDICATE(XVentanaCondOps) + +#endif diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index d99fac9d2d..fc090d729a 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -21,32 +21,58 @@ #include "qemu/log.h" #include "qemu/main-loop.h" #include "cpu.h" +#include "internals.h" +#include "pmu.h" #include "exec/exec-all.h" +#include "instmap.h" #include "tcg/tcg-op.h" #include "trace.h" #include "semihosting/common-semi.h" +#include "sysemu/cpu-timers.h" +#include "cpu_bits.h" +#include "debug.h" +#include "tcg/oversized-guest.h" -int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) +int riscv_env_mmu_index(CPURISCVState *env, bool ifetch) { #ifdef CONFIG_USER_ONLY return 0; #else - return env->priv; + bool virt = env->virt_enabled; + int mode = env->priv; + + /* All priv -> mmu_idx mapping are here */ + if (!ifetch) { + uint64_t status = env->mstatus; + + if (mode == PRV_M && get_field(status, MSTATUS_MPRV)) { + mode = get_field(env->mstatus, MSTATUS_MPP); + virt = get_field(env->mstatus, MSTATUS_MPV) && + (mode != PRV_M); + if (virt) { + status = env->vsstatus; + } + } + if (mode == PRV_S && get_field(status, MSTATUS_SUM)) { + mode = MMUIdx_S_SUM; + } + } + + return mode | (virt ? MMU_2STAGE_BIT : 0); #endif } -void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *pflags) +void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) { - CPUState *cs = env_cpu(env); - RISCVCPU *cpu = RISCV_CPU(cs); - + RISCVCPU *cpu = env_archcpu(env); + RISCVExtStatus fs, vs; uint32_t flags = 0; *pc = env->xl == MXL_RV32 ? env->pc & UINT32_MAX : env->pc; *cs_base = 0; - if (riscv_has_ext(env, RVV) || cpu->cfg.ext_zve32f || cpu->cfg.ext_zve64f) { + if (cpu->cfg.ext_zve32f) { /* * If env->vl equals to VLMAX, we can use generic vector operation * expanders (GVEC) to accerlate the vector operations. @@ -55,51 +81,64 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, * which is not supported by GVEC. So we set vl_eq_vlmax flag to true * only when maxsz >= 8 bytes. */ - uint32_t vlmax = vext_get_vlmax(env_archcpu(env), env->vtype); - uint32_t sew = FIELD_EX64(env->vtype, VTYPE, VSEW); - uint32_t maxsz = vlmax << sew; + + /* lmul encoded as in DisasContext::lmul */ + int8_t lmul = sextract32(FIELD_EX64(env->vtype, VTYPE, VLMUL), 0, 3); + uint32_t vsew = FIELD_EX64(env->vtype, VTYPE, VSEW); + uint32_t vlmax = vext_get_vlmax(cpu->cfg.vlenb, vsew, lmul); + uint32_t maxsz = vlmax << vsew; bool vl_eq_vlmax = (env->vstart == 0) && (vlmax == env->vl) && (maxsz >= 8); flags = FIELD_DP32(flags, TB_FLAGS, VILL, env->vill); - flags = FIELD_DP32(flags, TB_FLAGS, SEW, sew); + flags = FIELD_DP32(flags, TB_FLAGS, SEW, vsew); flags = FIELD_DP32(flags, TB_FLAGS, LMUL, - FIELD_EX64(env->vtype, VTYPE, VLMUL)); + FIELD_EX64(env->vtype, VTYPE, VLMUL)); flags = FIELD_DP32(flags, TB_FLAGS, VL_EQ_VLMAX, vl_eq_vlmax); + flags = FIELD_DP32(flags, TB_FLAGS, VTA, + FIELD_EX64(env->vtype, VTYPE, VTA)); + flags = FIELD_DP32(flags, TB_FLAGS, VMA, + FIELD_EX64(env->vtype, VTYPE, VMA)); + flags = FIELD_DP32(flags, TB_FLAGS, VSTART_EQ_ZERO, env->vstart == 0); } else { flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1); } #ifdef CONFIG_USER_ONLY - flags |= TB_FLAGS_MSTATUS_FS; - flags |= TB_FLAGS_MSTATUS_VS; + fs = EXT_STATUS_DIRTY; + vs = EXT_STATUS_DIRTY; #else - flags |= cpu_mmu_index(env, 0); - if (riscv_cpu_fp_enabled(env)) { - flags |= env->mstatus & MSTATUS_FS; + flags = FIELD_DP32(flags, TB_FLAGS, PRIV, env->priv); + + flags |= riscv_env_mmu_index(env, 0); + fs = get_field(env->mstatus, MSTATUS_FS); + vs = get_field(env->mstatus, MSTATUS_VS); + + if (env->virt_enabled) { + flags = FIELD_DP32(flags, TB_FLAGS, VIRT_ENABLED, 1); + /* + * Merge DISABLED and !DIRTY states using MIN. + * We will set both fields when dirtying. + */ + fs = MIN(fs, get_field(env->mstatus_hs, MSTATUS_FS)); + vs = MIN(vs, get_field(env->mstatus_hs, MSTATUS_VS)); } - if (riscv_cpu_vector_enabled(env)) { - flags |= env->mstatus & MSTATUS_VS; + /* With Zfinx, floating point is enabled/disabled by Smstateen. */ + if (!riscv_has_ext(env, RVF)) { + fs = (smstateen_acc_ok(env, 0, SMSTATEEN0_FCSR) == RISCV_EXCP_NONE) + ? EXT_STATUS_DIRTY : EXT_STATUS_DISABLED; } - if (riscv_has_ext(env, RVH)) { - if (env->priv == PRV_M || - (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) || - (env->priv == PRV_U && !riscv_cpu_virt_enabled(env) && - get_field(env->hstatus, HSTATUS_HU))) { - flags = FIELD_DP32(flags, TB_FLAGS, HLSX, 1); - } - - flags = FIELD_DP32(flags, TB_FLAGS, MSTATUS_HS_FS, - get_field(env->mstatus_hs, MSTATUS_FS)); - - flags = FIELD_DP32(flags, TB_FLAGS, MSTATUS_HS_VS, - get_field(env->mstatus_hs, MSTATUS_VS)); + if (cpu->cfg.debug && !icount_enabled()) { + flags = FIELD_DP32(flags, TB_FLAGS, ITRIGGER, env->itrigger_enabled); } #endif + flags = FIELD_DP32(flags, TB_FLAGS, FS, fs); + flags = FIELD_DP32(flags, TB_FLAGS, VS, vs); flags = FIELD_DP32(flags, TB_FLAGS, XL, env->xl); - if (env->cur_pmmask < (env->xl == MXL_RV32 ? UINT32_MAX : UINT64_MAX)) { + flags = FIELD_DP32(flags, TB_FLAGS, AXL, cpu_address_xl(env)); + if (env->cur_pmmask != 0) { flags = FIELD_DP32(flags, TB_FLAGS, PM_MASK_ENABLED, 1); } if (env->cur_pmbase != 0) { @@ -111,14 +150,17 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, void riscv_cpu_update_mask(CPURISCVState *env) { - target_ulong mask = -1, base = 0; + target_ulong mask = 0, base = 0; + RISCVMXL xl = env->xl; /* * TODO: Current RVJ spec does not specify * how the extension interacts with XLEN. */ #ifndef CONFIG_USER_ONLY + int mode = cpu_address_mode(env); + xl = cpu_get_xl(env, mode); if (riscv_has_ext(env, RVJ)) { - switch (env->priv) { + switch (mode) { case PRV_M: if (env->mmte & M_PM_ENABLE) { mask = env->mpmmask; @@ -142,7 +184,7 @@ void riscv_cpu_update_mask(CPURISCVState *env) } } #endif - if (env->xl == MXL_RV32) { + if (xl == MXL_RV32) { env->cur_pmmask = mask & UINT32_MAX; env->cur_pmbase = base & UINT32_MAX; } else { @@ -166,17 +208,17 @@ void riscv_cpu_update_mask(CPURISCVState *env) * 14 " * 15 " * 16 " - * 18 Debug/trace interrupt - * 20 (Reserved interrupt) + * 17 " + * 18 " + * 19 " + * 20 " + * 21 " * 22 " - * 24 " - * 26 " - * 28 " - * 30 (Reserved for standard reporting of bus or system errors) + * 23 " */ static const int hviprio_index2irq[] = { - 0, 1, 4, 5, 8, 13, 14, 15, 16, 18, 20, 22, 24, 26, 28, 30 }; + 0, 1, 4, 5, 8, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; static const int hviprio_index2rdzero[] = { 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -205,92 +247,88 @@ int riscv_cpu_hviprio_index2irq(int index, int *out_irq, int *out_rdzero) * Default | * Priority | Major Interrupt Numbers * ---------------------------------------------------------------- - * Highest | 63 (3f), 62 (3e), 31 (1f), 30 (1e), 61 (3d), 60 (3c), - * | 59 (3b), 58 (3a), 29 (1d), 28 (1c), 57 (39), 56 (38), - * | 55 (37), 54 (36), 27 (1b), 26 (1a), 53 (35), 52 (34), - * | 51 (33), 50 (32), 25 (19), 24 (18), 49 (31), 48 (30) + * Highest | 47, 23, 46, 45, 22, 44, + * | 43, 21, 42, 41, 20, 40 * | * | 11 (0b), 3 (03), 7 (07) * | 9 (09), 1 (01), 5 (05) * | 12 (0c) * | 10 (0a), 2 (02), 6 (06) * | - * | 47 (2f), 46 (2e), 23 (17), 22 (16), 45 (2d), 44 (2c), - * | 43 (2b), 42 (2a), 21 (15), 20 (14), 41 (29), 40 (28), - * | 39 (27), 38 (26), 19 (13), 18 (12), 37 (25), 36 (24), - * Lowest | 35 (23), 34 (22), 17 (11), 16 (10), 33 (21), 32 (20) + * | 39, 19, 38, 37, 18, 36, + * Lowest | 35, 17, 34, 33, 16, 32 * ---------------------------------------------------------------- */ static const uint8_t default_iprio[64] = { - [63] = IPRIO_DEFAULT_UPPER, - [62] = IPRIO_DEFAULT_UPPER + 1, - [31] = IPRIO_DEFAULT_UPPER + 2, - [30] = IPRIO_DEFAULT_UPPER + 3, - [61] = IPRIO_DEFAULT_UPPER + 4, - [60] = IPRIO_DEFAULT_UPPER + 5, + /* Custom interrupts 48 to 63 */ + [63] = IPRIO_MMAXIPRIO, + [62] = IPRIO_MMAXIPRIO, + [61] = IPRIO_MMAXIPRIO, + [60] = IPRIO_MMAXIPRIO, + [59] = IPRIO_MMAXIPRIO, + [58] = IPRIO_MMAXIPRIO, + [57] = IPRIO_MMAXIPRIO, + [56] = IPRIO_MMAXIPRIO, + [55] = IPRIO_MMAXIPRIO, + [54] = IPRIO_MMAXIPRIO, + [53] = IPRIO_MMAXIPRIO, + [52] = IPRIO_MMAXIPRIO, + [51] = IPRIO_MMAXIPRIO, + [50] = IPRIO_MMAXIPRIO, + [49] = IPRIO_MMAXIPRIO, + [48] = IPRIO_MMAXIPRIO, - [59] = IPRIO_DEFAULT_UPPER + 6, - [58] = IPRIO_DEFAULT_UPPER + 7, - [29] = IPRIO_DEFAULT_UPPER + 8, - [28] = IPRIO_DEFAULT_UPPER + 9, - [57] = IPRIO_DEFAULT_UPPER + 10, - [56] = IPRIO_DEFAULT_UPPER + 11, + /* Custom interrupts 24 to 31 */ + [31] = IPRIO_MMAXIPRIO, + [30] = IPRIO_MMAXIPRIO, + [29] = IPRIO_MMAXIPRIO, + [28] = IPRIO_MMAXIPRIO, + [27] = IPRIO_MMAXIPRIO, + [26] = IPRIO_MMAXIPRIO, + [25] = IPRIO_MMAXIPRIO, + [24] = IPRIO_MMAXIPRIO, - [55] = IPRIO_DEFAULT_UPPER + 12, - [54] = IPRIO_DEFAULT_UPPER + 13, - [27] = IPRIO_DEFAULT_UPPER + 14, - [26] = IPRIO_DEFAULT_UPPER + 15, - [53] = IPRIO_DEFAULT_UPPER + 16, - [52] = IPRIO_DEFAULT_UPPER + 17, + [47] = IPRIO_DEFAULT_UPPER, + [23] = IPRIO_DEFAULT_UPPER + 1, + [46] = IPRIO_DEFAULT_UPPER + 2, + [45] = IPRIO_DEFAULT_UPPER + 3, + [22] = IPRIO_DEFAULT_UPPER + 4, + [44] = IPRIO_DEFAULT_UPPER + 5, - [51] = IPRIO_DEFAULT_UPPER + 18, - [50] = IPRIO_DEFAULT_UPPER + 19, - [25] = IPRIO_DEFAULT_UPPER + 20, - [24] = IPRIO_DEFAULT_UPPER + 21, - [49] = IPRIO_DEFAULT_UPPER + 22, - [48] = IPRIO_DEFAULT_UPPER + 23, + [43] = IPRIO_DEFAULT_UPPER + 6, + [21] = IPRIO_DEFAULT_UPPER + 7, + [42] = IPRIO_DEFAULT_UPPER + 8, + [41] = IPRIO_DEFAULT_UPPER + 9, + [20] = IPRIO_DEFAULT_UPPER + 10, + [40] = IPRIO_DEFAULT_UPPER + 11, - [11] = IPRIO_DEFAULT_M, - [3] = IPRIO_DEFAULT_M + 1, - [7] = IPRIO_DEFAULT_M + 2, + [11] = IPRIO_DEFAULT_M, + [3] = IPRIO_DEFAULT_M + 1, + [7] = IPRIO_DEFAULT_M + 2, - [9] = IPRIO_DEFAULT_S, - [1] = IPRIO_DEFAULT_S + 1, - [5] = IPRIO_DEFAULT_S + 2, + [9] = IPRIO_DEFAULT_S, + [1] = IPRIO_DEFAULT_S + 1, + [5] = IPRIO_DEFAULT_S + 2, - [12] = IPRIO_DEFAULT_SGEXT, + [12] = IPRIO_DEFAULT_SGEXT, - [10] = IPRIO_DEFAULT_VS, - [2] = IPRIO_DEFAULT_VS + 1, - [6] = IPRIO_DEFAULT_VS + 2, + [10] = IPRIO_DEFAULT_VS, + [2] = IPRIO_DEFAULT_VS + 1, + [6] = IPRIO_DEFAULT_VS + 2, - [47] = IPRIO_DEFAULT_LOWER, - [46] = IPRIO_DEFAULT_LOWER + 1, - [23] = IPRIO_DEFAULT_LOWER + 2, - [22] = IPRIO_DEFAULT_LOWER + 3, - [45] = IPRIO_DEFAULT_LOWER + 4, - [44] = IPRIO_DEFAULT_LOWER + 5, + [39] = IPRIO_DEFAULT_LOWER, + [19] = IPRIO_DEFAULT_LOWER + 1, + [38] = IPRIO_DEFAULT_LOWER + 2, + [37] = IPRIO_DEFAULT_LOWER + 3, + [18] = IPRIO_DEFAULT_LOWER + 4, + [36] = IPRIO_DEFAULT_LOWER + 5, - [43] = IPRIO_DEFAULT_LOWER + 6, - [42] = IPRIO_DEFAULT_LOWER + 7, - [21] = IPRIO_DEFAULT_LOWER + 8, - [20] = IPRIO_DEFAULT_LOWER + 9, - [41] = IPRIO_DEFAULT_LOWER + 10, - [40] = IPRIO_DEFAULT_LOWER + 11, - - [39] = IPRIO_DEFAULT_LOWER + 12, - [38] = IPRIO_DEFAULT_LOWER + 13, - [19] = IPRIO_DEFAULT_LOWER + 14, - [18] = IPRIO_DEFAULT_LOWER + 15, - [37] = IPRIO_DEFAULT_LOWER + 16, - [36] = IPRIO_DEFAULT_LOWER + 17, - - [35] = IPRIO_DEFAULT_LOWER + 18, - [34] = IPRIO_DEFAULT_LOWER + 19, - [17] = IPRIO_DEFAULT_LOWER + 20, - [16] = IPRIO_DEFAULT_LOWER + 21, - [33] = IPRIO_DEFAULT_LOWER + 22, - [32] = IPRIO_DEFAULT_LOWER + 23, + [35] = IPRIO_DEFAULT_LOWER + 6, + [17] = IPRIO_DEFAULT_LOWER + 7, + [34] = IPRIO_DEFAULT_LOWER + 8, + [33] = IPRIO_DEFAULT_LOWER + 9, + [16] = IPRIO_DEFAULT_LOWER + 10, + [32] = IPRIO_DEFAULT_LOWER + 11, }; uint8_t riscv_cpu_default_priority(int irq) @@ -314,7 +352,8 @@ static int riscv_cpu_pending_to_irq(CPURISCVState *env, } irq = ctz64(pending); - if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + if (!((extirq == IRQ_M_EXT) ? riscv_cpu_cfg(env)->ext_smaia : + riscv_cpu_cfg(env)->ext_ssaia)) { return irq; } @@ -340,12 +379,18 @@ static int riscv_cpu_pending_to_irq(CPURISCVState *env, return best_irq; } -static uint64_t riscv_cpu_all_pending(CPURISCVState *env) +/* + * Doesn't report interrupts inserted using mvip from M-mode firmware or + * using hvip bits 13:63 from HS-mode. Those are returned in + * riscv_cpu_sirq_pending() and riscv_cpu_vsirq_pending(). + */ +uint64_t riscv_cpu_all_pending(CPURISCVState *env) { uint32_t gein = get_field(env->hstatus, HSTATUS_VGEIN); uint64_t vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0; + uint64_t vstip = (env->vstime_irq) ? MIP_VSTIP : 0; - return (env->mip | vsgein) & env->mie; + return (env->mip | vsgein | vstip) & env->mie; } int riscv_cpu_mirq_pending(CPURISCVState *env) @@ -361,27 +406,35 @@ int riscv_cpu_sirq_pending(CPURISCVState *env) { uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg & ~(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP); + uint64_t irqs_f = env->mvip & env->mvien & ~env->mideleg & env->sie; return riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, - irqs, env->siprio); + irqs | irqs_f, env->siprio); } int riscv_cpu_vsirq_pending(CPURISCVState *env) { - uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg & - (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP); + uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg & env->hideleg; + uint64_t irqs_f_vs = env->hvip & env->hvien & ~env->hideleg & env->vsie; + uint64_t vsbits; + + /* Bring VS-level bits to correct position */ + vsbits = irqs & VS_MODE_INTERRUPTS; + irqs &= ~VS_MODE_INTERRUPTS; + irqs |= vsbits >> 1; return riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, - irqs >> 1, env->hviprio); + (irqs | irqs_f_vs), env->hviprio); } static int riscv_cpu_local_irq_pending(CPURISCVState *env) { + uint64_t irqs, pending, mie, hsie, vsie, irqs_f, irqs_f_vs; + uint64_t vsbits, irq_delegated; int virq; - uint64_t irqs, pending, mie, hsie, vsie; /* Determine interrupt enable state of all privilege modes */ - if (riscv_cpu_virt_enabled(env)) { + if (env->virt_enabled) { mie = 1; hsie = 1; vsie = (env->priv < PRV_S) || @@ -404,19 +457,36 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env) irqs, env->miprio); } + /* Check for virtual S-mode interrupts. */ + irqs_f = env->mvip & (env->mvien & ~env->mideleg) & env->sie; + /* Check HS-mode interrupts */ - irqs = pending & env->mideleg & ~env->hideleg & -hsie; + irqs = ((pending & env->mideleg & ~env->hideleg) | irqs_f) & -hsie; if (irqs) { return riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, irqs, env->siprio); } + /* Check for virtual VS-mode interrupts. */ + irqs_f_vs = env->hvip & env->hvien & ~env->hideleg & env->vsie; + /* Check VS-mode interrupts */ - irqs = pending & env->mideleg & env->hideleg & -vsie; + irq_delegated = pending & env->mideleg & env->hideleg; + + /* Bring VS-level bits to correct position */ + vsbits = irq_delegated & VS_MODE_INTERRUPTS; + irq_delegated &= ~VS_MODE_INTERRUPTS; + irq_delegated |= vsbits >> 1; + + irqs = (irq_delegated | irqs_f_vs) & -vsie; if (irqs) { virq = riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, - irqs >> 1, env->hviprio); - return (virq <= 0) ? virq : virq + 1; + irqs, env->hviprio); + if (virq <= 0 || (virq > 12 && virq <= 63)) { + return virq; + } else { + return virq + 1; + } } /* Indicate no pending interrupt */ @@ -442,7 +512,7 @@ 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)) { + if (env->virt_enabled && !(env->mstatus_hs & MSTATUS_FS)) { return false; } return true; @@ -455,7 +525,7 @@ bool riscv_cpu_fp_enabled(CPURISCVState *env) bool riscv_cpu_vector_enabled(CPURISCVState *env) { if (env->mstatus & MSTATUS_VS) { - if (riscv_cpu_virt_enabled(env) && !(env->mstatus_hs & MSTATUS_VS)) { + if (env->virt_enabled && !(env->mstatus_hs & MSTATUS_VS)) { return false; } return true; @@ -473,7 +543,7 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env) if (riscv_has_ext(env, RVF)) { mstatus_mask |= MSTATUS_FS; } - bool current_virt = riscv_cpu_virt_enabled(env); + bool current_virt = env->virt_enabled; g_assert(riscv_has_ext(env, RVH)); @@ -548,27 +618,15 @@ void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen) env->geilen = geilen; } -bool riscv_cpu_virt_enabled(CPURISCVState *env) -{ - if (!riscv_has_ext(env, RVH)) { - return false; - } - - return get_field(env->virt, VIRT_ONOFF); -} - +/* This function can only be called to set virt when RVH is enabled */ 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) { + if (env->virt_enabled != enable) { tlb_flush(env_cpu(env)); } - env->virt = set_field(env->virt, VIRT_ONOFF, enable); + env->virt_enabled = enable; if (enable) { /* @@ -580,15 +638,10 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable) * * To solve this, we check and inject interrupt after setting V=1. */ - riscv_cpu_update_mip(env_archcpu(env), 0, 0); + riscv_cpu_update_mip(env, 0, 0); } } -bool riscv_cpu_two_stage_lookup(int mmu_idx) -{ - return mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK; -} - int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts) { CPURISCVState *env = &cpu->env; @@ -600,34 +653,42 @@ int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts) } } -uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value) +void riscv_cpu_interrupt(CPURISCVState *env) { - CPURISCVState *env = &cpu->env; - CPUState *cs = CPU(cpu); - uint64_t gein, vsgein = 0, old = env->mip; - bool locked = false; + uint64_t gein, vsgein = 0, vstip = 0, irqf = 0; + CPUState *cs = env_cpu(env); - if (riscv_cpu_virt_enabled(env)) { + BQL_LOCK_GUARD(); + + if (env->virt_enabled) { gein = get_field(env->hstatus, HSTATUS_VGEIN); vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0; + irqf = env->hvien & env->hvip & env->vsie; + } else { + irqf = env->mvien & env->mvip & env->sie; } - if (!qemu_mutex_iothread_locked()) { - locked = true; - qemu_mutex_lock_iothread(); - } + vstip = env->vstime_irq ? MIP_VSTIP : 0; - env->mip = (env->mip & ~mask) | (value & mask); - - if (env->mip | vsgein) { + if (env->mip | vsgein | vstip | irqf) { cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } +} - if (locked) { - qemu_mutex_unlock_iothread(); - } +uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask, uint64_t value) +{ + uint64_t old = env->mip; + + /* No need to update mip for VSTIP */ + mask = ((mask == MIP_VSTIP) && env->vstime_irq) ? 0 : mask; + + BQL_LOCK_GUARD(); + + env->mip = (env->mip & ~mask) | (value & mask); + + riscv_cpu_interrupt(env); return old; } @@ -655,11 +716,10 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) { - if (newpriv > PRV_M) { - g_assert_not_reached(); - } - if (newpriv == PRV_H) { - newpriv = PRV_U; + g_assert(newpriv <= PRV_M && newpriv != PRV_RESERVED); + + if (icount_enabled() && newpriv != env->priv) { + riscv_itrigger_update_priv(env); } /* tlb_flush is unnecessary as mode is contained in mmu_idx */ env->priv = newpriv; @@ -685,42 +745,36 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) * * @env: CPURISCVState * @prot: The returned protection attributes - * @tlb_size: TLB page size containing addr. It could be modified after PMP - * permission checking. NULL if not set TLB page for addr. * @addr: The physical address to be checked permission * @access_type: The type of MMU access * @mode: Indicates current privilege level. */ -static int get_physical_address_pmp(CPURISCVState *env, int *prot, - target_ulong *tlb_size, hwaddr addr, +static int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr, int size, MMUAccessType access_type, int mode) { pmp_priv_t pmp_priv; - target_ulong tlb_size_pmp = 0; + bool pmp_has_privs; - if (!riscv_feature(env, RISCV_FEATURE_PMP)) { + if (!riscv_cpu_cfg(env)->pmp) { *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return TRANSLATE_SUCCESS; } - if (!pmp_hart_has_privs(env, addr, size, 1 << access_type, &pmp_priv, - mode)) { + pmp_has_privs = pmp_hart_has_privs(env, addr, size, 1 << access_type, + &pmp_priv, mode); + if (!pmp_has_privs) { *prot = 0; return TRANSLATE_PMP_FAIL; } *prot = pmp_priv_to_page_prot(pmp_priv); - if (tlb_size != NULL) { - if (pmp_is_range_in_tlb(env, addr & ~(*tlb_size - 1), &tlb_size_pmp)) { - *tlb_size = tlb_size_pmp; - } - } return TRANSLATE_SUCCESS; } -/* get_physical_address - get the physical address for this virtual address +/* + * get_physical_address - get the physical address for this virtual address * * Do a page table walk to obtain the physical address corresponding to a * virtual address. Returns 0 if the translation was successful @@ -730,7 +784,7 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot, * @env: CPURISCVState * @physical: This will be set to the calculated physical address * @prot: The returned protection attributes - * @addr: The virtual address to be translated + * @addr: The virtual address or guest physical address to be translated * @fault_pte_addr: If not NULL, this will be set to fault pte address * when a error occurs on pte address translation. * This will already be shifted to match htval. @@ -742,21 +796,22 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot, * @is_debug: Is this access from a debugger or the monitor? */ static int get_physical_address(CPURISCVState *env, hwaddr *physical, - int *prot, target_ulong addr, + int *ret_prot, vaddr addr, target_ulong *fault_pte_addr, int access_type, int mmu_idx, bool first_stage, bool two_stage, bool is_debug) { - /* NOTE: the env->pc value visible here will not be + /* + * NOTE: the env->pc value visible here will not be * correct, but the value visible to the exception handler - * (riscv_cpu_do_interrupt) is correct */ + * (riscv_cpu_do_interrupt) is correct + */ MemTxResult res; MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; - int mode = mmu_idx & TB_FLAGS_PRIV_MMU_MASK; + int mode = mmuidx_priv(mmu_idx); bool use_background = false; hwaddr ppn; - RISCVCPU *cpu = env_archcpu(env); int napot_bits = 0; target_ulong napot_mask; @@ -767,42 +822,20 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, * 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 (!riscv_cpu_virt_enabled(env) && two_stage) { + if (!env->virt_enabled && two_stage) { use_background = true; } - /* MPRV does not affect the virtual-machine load/store - instructions, HLV, HLVX, and HSV. */ - if (riscv_cpu_two_stage_lookup(mmu_idx)) { - mode = get_field(env->hstatus, HSTATUS_SPVP); - } else if (mode == PRV_M && access_type != MMU_INST_FETCH) { - if (get_field(env->mstatus, MSTATUS_MPRV)) { - mode = get_field(env->mstatus, MSTATUS_MPP); - } - } - - 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)) { + if (mode == PRV_M || !riscv_cpu_cfg(env)->mmu) { *physical = addr; - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + *ret_prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return TRANSLATE_SUCCESS; } - *prot = 0; + *ret_prot = 0; hwaddr base; - 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); - } + int levels, ptidxbits, ptesize, vm, widened; if (first_stage == true) { if (use_background) { @@ -833,8 +866,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, } widened = 2; } - /* status.SUM will be ignored if execute on background */ - sum = get_field(env->mstatus, MSTATUS_SUM) || use_background || is_debug; + switch (vm) { case VM_1_10_SV32: levels = 2; ptidxbits = 10; ptesize = 4; break; @@ -846,7 +878,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, levels = 5; ptidxbits = 9; ptesize = 8; break; case VM_1_10_MBARE: *physical = addr; - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + *ret_prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return TRANSLATE_SUCCESS; default: g_assert_not_reached(); @@ -854,20 +886,39 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, CPUState *cs = env_cpu(env); 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; + if (first_stage == true) { + 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; + } } else { - mask = 0; + if (vm != VM_1_10_SV32 && addr >> va_bits != 0) { + return TRANSLATE_FAIL; + } } - masked_msbs = (addr >> (va_bits - 1)) & mask; - if (masked_msbs != 0 && masked_msbs != mask) { - return TRANSLATE_FAIL; + bool pbmte = env->menvcfg & MENVCFG_PBMTE; + bool svade = riscv_cpu_cfg(env)->ext_svade; + bool svadu = riscv_cpu_cfg(env)->ext_svadu; + bool adue = svadu ? env->menvcfg & MENVCFG_ADUE : !svade; + + if (first_stage && two_stage && env->virt_enabled) { + pbmte = pbmte && (env->henvcfg & HENVCFG_PBMTE); + adue = adue && (env->henvcfg & HENVCFG_ADUE); } int ptshift = (levels - 1) * ptidxbits; + target_ulong pte; + hwaddr pte_addr; int i; #if !TCG_OVERSIZED_GUEST @@ -884,7 +935,6 @@ restart: } /* check that physical address of PTE is legal */ - hwaddr pte_addr; if (two_stage && first_stage) { int vbase_prot; @@ -893,7 +943,7 @@ restart: /* Do the second stage translation on the base PTE address. */ int vbase_ret = get_physical_address(env, &vbase, &vbase_prot, base, NULL, MMU_DATA_LOAD, - mmu_idx, false, true, + MMUIdx_U, false, true, is_debug); if (vbase_ret != TRANSLATE_SUCCESS) { @@ -909,14 +959,13 @@ restart: } int pmp_prot; - int pmp_ret = get_physical_address_pmp(env, &pmp_prot, NULL, pte_addr, + int pmp_ret = get_physical_address_pmp(env, &pmp_prot, pte_addr, sizeof(target_ulong), MMU_DATA_LOAD, PRV_S); if (pmp_ret != TRANSLATE_SUCCESS) { return TRANSLATE_PMP_FAIL; } - target_ulong pte; if (riscv_cpu_mxl(env) == MXL_RV32) { pte = address_space_ldl(cs->as, pte_addr, attrs, &res); } else { @@ -929,175 +978,226 @@ restart: if (riscv_cpu_sxl(env) == MXL_RV32) { ppn = pte >> PTE_PPN_SHIFT; - } else if (cpu->cfg.ext_svpbmt || cpu->cfg.ext_svnapot) { - ppn = (pte & (target_ulong)PTE_PPN_MASK) >> PTE_PPN_SHIFT; } else { - ppn = pte >> PTE_PPN_SHIFT; - if ((pte & ~(target_ulong)PTE_PPN_MASK) >> PTE_PPN_SHIFT) { + if (pte & PTE_RESERVED) { return TRANSLATE_FAIL; } + + if (!pbmte && (pte & PTE_PBMT)) { + return TRANSLATE_FAIL; + } + + if (!riscv_cpu_cfg(env)->ext_svnapot && (pte & PTE_N)) { + return TRANSLATE_FAIL; + } + + ppn = (pte & (target_ulong)PTE_PPN_MASK) >> PTE_PPN_SHIFT; } if (!(pte & PTE_V)) { /* Invalid PTE */ return TRANSLATE_FAIL; - } else if (!cpu->cfg.ext_svpbmt && (pte & PTE_PBMT)) { + } + if (pte & (PTE_R | PTE_W | PTE_X)) { + goto leaf; + } + + /* Inner PTE, continue walking */ + if (pte & (PTE_D | PTE_A | PTE_U | PTE_ATTR)) { return TRANSLATE_FAIL; - } else if (!(pte & (PTE_R | PTE_W | PTE_X))) { - /* Inner PTE, continue walking */ - if (pte & (PTE_D | PTE_A | PTE_U | PTE_ATTR)) { + } + base = ppn << PGSHIFT; + } + + /* No leaf pte at any translation level. */ + return TRANSLATE_FAIL; + + leaf: + if (ppn & ((1ULL << ptshift) - 1)) { + /* Misaligned PPN */ + return TRANSLATE_FAIL; + } + if (!pbmte && (pte & PTE_PBMT)) { + /* Reserved without Svpbmt. */ + return TRANSLATE_FAIL; + } + + /* Check for reserved combinations of RWX flags. */ + switch (pte & (PTE_R | PTE_W | PTE_X)) { + case PTE_W: + case PTE_W | PTE_X: + return TRANSLATE_FAIL; + } + + int prot = 0; + if (pte & PTE_R) { + prot |= PAGE_READ; + } + if (pte & PTE_W) { + prot |= PAGE_WRITE; + } + if (pte & PTE_X) { + bool mxr = false; + + /* + * Use mstatus for first stage or for the second stage without + * virt_enabled (MPRV+MPV) + */ + if (first_stage || !env->virt_enabled) { + mxr = get_field(env->mstatus, MSTATUS_MXR); + } + + /* MPRV+MPV case, check VSSTATUS */ + if (first_stage && two_stage && !env->virt_enabled) { + mxr |= get_field(env->vsstatus, MSTATUS_MXR); + } + + /* + * Setting MXR at HS-level overrides both VS-stage and G-stage + * execute-only permissions + */ + if (env->virt_enabled) { + mxr |= get_field(env->mstatus_hs, MSTATUS_MXR); + } + + if (mxr) { + prot |= PAGE_READ; + } + prot |= PAGE_EXEC; + } + + if (pte & PTE_U) { + if (mode != PRV_U) { + if (!mmuidx_sum(mmu_idx)) { return TRANSLATE_FAIL; } - base = ppn << PGSHIFT; - } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) { - /* Reserved leaf PTE flags: PTE_W */ - return TRANSLATE_FAIL; - } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) { - /* Reserved leaf PTE flags: PTE_W + PTE_X */ - return TRANSLATE_FAIL; - } else if ((pte & PTE_U) && ((mode != PRV_U) && - (!sum || access_type == MMU_INST_FETCH))) { - /* User PTE flags when not U mode and mstatus.SUM is not set, - or the access type is an instruction fetch */ - return TRANSLATE_FAIL; - } else if (!(pte & PTE_U) && (mode != PRV_S)) { - /* Supervisor PTE flags when not S mode */ - return TRANSLATE_FAIL; - } else if (ppn & ((1ULL << ptshift) - 1)) { - /* Misaligned PPN */ - return TRANSLATE_FAIL; - } else if (access_type == MMU_DATA_LOAD && !((pte & PTE_R) || - ((pte & PTE_X) && mxr))) { - /* Read access check failed */ - return TRANSLATE_FAIL; - } else if (access_type == MMU_DATA_STORE && !(pte & PTE_W)) { - /* Write access check failed */ - return TRANSLATE_FAIL; - } else if (access_type == MMU_INST_FETCH && !(pte & PTE_X)) { - /* Fetch access check failed */ - return TRANSLATE_FAIL; - } else { - /* if necessary, set accessed and dirty bits. */ - target_ulong updated_pte = pte | PTE_A | - (access_type == MMU_DATA_STORE ? PTE_D : 0); + /* SUM allows only read+write, not execute. */ + prot &= PAGE_READ | PAGE_WRITE; + } + } else if (mode != PRV_S) { + /* Supervisor PTE flags when not S mode */ + return TRANSLATE_FAIL; + } - /* Page table updates need to be atomic with MTTCG enabled */ - if (updated_pte != pte) { - /* - * - if accessed or dirty bits need updating, and the PTE is - * in RAM, then we do so atomically with a compare and swap. - * - if the PTE is in IO space or ROM, then it can't be updated - * and we return TRANSLATE_FAIL. - * - if the PTE changed by the time we went to update it, then - * it is no longer valid and we must re-walk the page table. - */ - MemoryRegion *mr; - hwaddr l = sizeof(target_ulong), addr1; - mr = address_space_translate(cs->as, pte_addr, - &addr1, &l, false, MEMTXATTRS_UNSPECIFIED); - if (memory_region_is_ram(mr)) { - target_ulong *pte_pa = - qemu_map_ram_ptr(mr->ram_block, addr1); + if (!((prot >> access_type) & 1)) { + /* Access check failed */ + return TRANSLATE_FAIL; + } + + target_ulong updated_pte = pte; + + /* + * If ADUE is enabled, set accessed and dirty bits. + * Otherwise raise an exception if necessary. + */ + if (adue) { + updated_pte |= PTE_A | (access_type == MMU_DATA_STORE ? PTE_D : 0); + } else if (!(pte & PTE_A) || + (access_type == MMU_DATA_STORE && !(pte & PTE_D))) { + return TRANSLATE_FAIL; + } + + /* Page table updates need to be atomic with MTTCG enabled */ + if (updated_pte != pte && !is_debug) { + if (!adue) { + return TRANSLATE_FAIL; + } + + /* + * - if accessed or dirty bits need updating, and the PTE is + * in RAM, then we do so atomically with a compare and swap. + * - if the PTE is in IO space or ROM, then it can't be updated + * and we return TRANSLATE_FAIL. + * - if the PTE changed by the time we went to update it, then + * it is no longer valid and we must re-walk the page table. + */ + MemoryRegion *mr; + hwaddr l = sizeof(target_ulong), addr1; + mr = address_space_translate(cs->as, pte_addr, &addr1, &l, + false, MEMTXATTRS_UNSPECIFIED); + if (memory_region_is_ram(mr)) { + target_ulong *pte_pa = qemu_map_ram_ptr(mr->ram_block, addr1); #if TCG_OVERSIZED_GUEST - /* MTTCG is not enabled on oversized TCG guests so - * page table updates do not need to be atomic */ - *pte_pa = pte = updated_pte; + /* + * MTTCG is not enabled on oversized TCG guests so + * page table updates do not need to be atomic + */ + *pte_pa = pte = updated_pte; #else - target_ulong old_pte = - qatomic_cmpxchg(pte_pa, pte, updated_pte); - if (old_pte != pte) { - goto restart; - } else { - pte = updated_pte; - } + target_ulong old_pte = qatomic_cmpxchg(pte_pa, pte, updated_pte); + if (old_pte != pte) { + goto restart; + } + pte = updated_pte; #endif - } else { - /* misconfigured PTE in ROM (AD bits are not preset) or - * PTE is in IO space and can't be updated atomically */ - return TRANSLATE_FAIL; - } - } - - /* for superpage mappings, make a fake leaf PTE for the TLB's - benefit. */ - target_ulong vpn = addr >> PGSHIFT; - - if (cpu->cfg.ext_svnapot && (pte & PTE_N)) { - napot_bits = ctzl(ppn) + 1; - if ((i != (levels - 1)) || (napot_bits != 4)) { - return TRANSLATE_FAIL; - } - } - - napot_mask = (1 << napot_bits) - 1; - *physical = (((ppn & ~napot_mask) | (vpn & napot_mask) | - (vpn & (((target_ulong)1 << ptshift) - 1)) - ) << PGSHIFT) | (addr & ~TARGET_PAGE_MASK); - - /* set permissions on the TLB entry */ - if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) { - *prot |= PAGE_READ; - } - if ((pte & PTE_X)) { - *prot |= PAGE_EXEC; - } - /* add write permission on stores or if the page is already dirty, - so that we TLB miss on later writes to update the dirty bit */ - if ((pte & PTE_W) && - (access_type == MMU_DATA_STORE || (pte & PTE_D))) { - *prot |= PAGE_WRITE; - } - return TRANSLATE_SUCCESS; + } else { + /* + * Misconfigured PTE in ROM (AD bits are not preset) or + * PTE is in IO space and can't be updated atomically. + */ + return TRANSLATE_FAIL; } } - return TRANSLATE_FAIL; + + /* For superpage mappings, make a fake leaf PTE for the TLB's benefit. */ + target_ulong vpn = addr >> PGSHIFT; + + if (riscv_cpu_cfg(env)->ext_svnapot && (pte & PTE_N)) { + napot_bits = ctzl(ppn) + 1; + if ((i != (levels - 1)) || (napot_bits != 4)) { + return TRANSLATE_FAIL; + } + } + + napot_mask = (1 << napot_bits) - 1; + *physical = (((ppn & ~napot_mask) | (vpn & napot_mask) | + (vpn & (((target_ulong)1 << ptshift) - 1)) + ) << PGSHIFT) | (addr & ~TARGET_PAGE_MASK); + + /* + * Remove write permission unless this is a store, or the page is + * already dirty, so that we TLB miss on later writes to update + * the dirty bit. + */ + if (access_type != MMU_DATA_STORE && !(pte & PTE_D)) { + prot &= ~PAGE_WRITE; + } + *ret_prot = prot; + + return TRANSLATE_SUCCESS; } static void raise_mmu_exception(CPURISCVState *env, target_ulong address, MMUAccessType access_type, bool pmp_violation, - bool first_stage, bool two_stage) + bool first_stage, bool two_stage, + bool two_stage_indirect) { CPUState *cs = env_cpu(env); - int page_fault_exceptions, vm; - uint64_t stap_mode; - - if (riscv_cpu_mxl(env) == MXL_RV32) { - stap_mode = SATP32_MODE; - } else { - stap_mode = SATP64_MODE; - } - - if (first_stage) { - vm = get_field(env->satp, stap_mode); - } else { - vm = get_field(env->hgatp, stap_mode); - } - - page_fault_exceptions = vm != VM_1_10_MBARE && !pmp_violation; switch (access_type) { case MMU_INST_FETCH: - if (riscv_cpu_virt_enabled(env) && !first_stage) { + if (env->virt_enabled && !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; + cs->exception_index = pmp_violation ? + RISCV_EXCP_INST_ACCESS_FAULT : RISCV_EXCP_INST_PAGE_FAULT; } break; case MMU_DATA_LOAD: if (two_stage && !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; + cs->exception_index = pmp_violation ? + RISCV_EXCP_LOAD_ACCESS_FAULT : RISCV_EXCP_LOAD_PAGE_FAULT; } break; case MMU_DATA_STORE: if (two_stage && !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; + cs->exception_index = pmp_violation ? + RISCV_EXCP_STORE_AMO_ACCESS_FAULT : + RISCV_EXCP_STORE_PAGE_FAULT; } break; default: @@ -1105,6 +1205,7 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, } env->badaddr = address; env->two_stage_lookup = two_stage; + env->two_stage_indirect_lookup = two_stage_indirect; } hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) @@ -1113,16 +1214,16 @@ hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) CPURISCVState *env = &cpu->env; hwaddr phys_addr; int prot; - int mmu_idx = cpu_mmu_index(&cpu->env, false); + int mmu_idx = riscv_env_mmu_index(&cpu->env, false); if (get_physical_address(env, &phys_addr, &prot, addr, NULL, 0, mmu_idx, - true, riscv_cpu_virt_enabled(env), true)) { + true, env->virt_enabled, true)) { return -1; } - if (riscv_cpu_virt_enabled(env)) { + if (env->virt_enabled) { if (get_physical_address(env, &phys_addr, &prot, phys_addr, NULL, - 0, mmu_idx, false, true, true)) { + 0, MMUIdx_U, false, true, true)) { return -1; } } @@ -1148,8 +1249,8 @@ void riscv_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, } env->badaddr = addr; - env->two_stage_lookup = riscv_cpu_virt_enabled(env) || - riscv_cpu_two_stage_lookup(mmu_idx); + env->two_stage_lookup = mmuidx_2stage(mmu_idx); + env->two_stage_indirect_lookup = false; cpu_loop_exit_restore(cs, retaddr); } @@ -1173,11 +1274,33 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, g_assert_not_reached(); } env->badaddr = addr; - env->two_stage_lookup = riscv_cpu_virt_enabled(env) || - riscv_cpu_two_stage_lookup(mmu_idx); + env->two_stage_lookup = mmuidx_2stage(mmu_idx); + env->two_stage_indirect_lookup = false; cpu_loop_exit_restore(cs, retaddr); } + +static void pmu_tlb_fill_incr_ctr(RISCVCPU *cpu, MMUAccessType access_type) +{ + enum riscv_pmu_event_idx pmu_event_type; + + switch (access_type) { + case MMU_INST_FETCH: + pmu_event_type = RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS; + break; + case MMU_DATA_LOAD: + pmu_event_type = RISCV_PMU_EVENT_CACHE_DTLB_READ_MISS; + break; + case MMU_DATA_STORE: + pmu_event_type = RISCV_PMU_EVENT_CACHE_DTLB_WRITE_MISS; + break; + default: + return; + } + + riscv_pmu_incr_ctr(cpu, pmu_event_type); +} + bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) @@ -1189,9 +1312,10 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, int prot, prot2, prot_pmp; bool pmp_violation = false; bool first_stage_error = true; - bool two_stage_lookup = false; + bool two_stage_lookup = mmuidx_2stage(mmu_idx); + bool two_stage_indirect_error = false; int ret = TRANSLATE_FAIL; - int mode = mmu_idx; + int mode = mmuidx_priv(mmu_idx); /* default TLB page size */ target_ulong tlb_size = TARGET_PAGE_SIZE; @@ -1200,21 +1324,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, qemu_log_mask(CPU_LOG_MMU, "%s ad %" VADDR_PRIx " rw %d mmu_idx %d\n", __func__, address, access_type, mmu_idx); - /* MPRV does not affect the virtual-machine load/store - instructions, HLV, HLVX, and HSV. */ - if (riscv_cpu_two_stage_lookup(mmu_idx)) { - mode = get_field(env->hstatus, HSTATUS_SPVP); - } else if (mode == PRV_M && access_type != MMU_INST_FETCH && - get_field(env->mstatus, MSTATUS_MPRV)) { - mode = get_field(env->mstatus, MSTATUS_MPP); - if (riscv_has_ext(env, RVH) && get_field(env->mstatus, MSTATUS_MPV)) { - two_stage_lookup = true; - } - } - - if (riscv_cpu_virt_enabled(env) || - ((riscv_cpu_two_stage_lookup(mmu_idx) || two_stage_lookup) && - access_type != MMU_INST_FETCH)) { + pmu_tlb_fill_incr_ctr(cpu, access_type); + if (two_stage_lookup) { /* Two stage lookup */ ret = get_physical_address(env, &pa, &prot, address, &env->guest_phys_fault_addr, access_type, @@ -1227,12 +1338,12 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, */ if (ret == TRANSLATE_G_STAGE_FAIL) { first_stage_error = false; - access_type = MMU_DATA_LOAD; + two_stage_indirect_error = true; } qemu_log_mask(CPU_LOG_MMU, "%s 1st-stage address=%" VADDR_PRIx " ret %d physical " - TARGET_FMT_plx " prot %d\n", + HWADDR_FMT_plx " prot %d\n", __func__, address, ret, pa, prot); if (ret == TRANSLATE_SUCCESS) { @@ -1240,22 +1351,24 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, im_address = pa; ret = get_physical_address(env, &pa, &prot2, im_address, NULL, - access_type, mmu_idx, false, true, + access_type, MMUIdx_U, false, true, false); 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, prot2); + "%s 2nd-stage address=%" VADDR_PRIx + " ret %d physical " + HWADDR_FMT_plx " prot %d\n", + __func__, im_address, ret, pa, prot2); prot &= prot2; if (ret == TRANSLATE_SUCCESS) { - ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa, + ret = get_physical_address_pmp(env, &prot_pmp, pa, size, access_type, mode); + tlb_size = pmp_get_tlb_size(env, pa); qemu_log_mask(CPU_LOG_MMU, - "%s PMP address=" TARGET_FMT_plx " ret %d prot" + "%s PMP address=" HWADDR_FMT_plx " ret %d prot" " %d tlb_size " TARGET_FMT_lu "\n", __func__, pa, ret, prot_pmp, tlb_size); @@ -1280,15 +1393,16 @@ 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", + HWADDR_FMT_plx " prot %d\n", __func__, address, ret, pa, prot); if (ret == TRANSLATE_SUCCESS) { - ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa, + ret = get_physical_address_pmp(env, &prot_pmp, pa, size, access_type, mode); + tlb_size = pmp_get_tlb_size(env, pa); qemu_log_mask(CPU_LOG_MMU, - "%s PMP address=" TARGET_FMT_plx " ret %d prot" + "%s PMP address=" HWADDR_FMT_plx " ret %d prot" " %d tlb_size " TARGET_FMT_lu "\n", __func__, pa, ret, prot_pmp, tlb_size); @@ -1308,14 +1422,218 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, return false; } else { raise_mmu_exception(env, address, access_type, pmp_violation, - first_stage_error, - riscv_cpu_virt_enabled(env) || - riscv_cpu_two_stage_lookup(mmu_idx)); + first_stage_error, two_stage_lookup, + two_stage_indirect_error); cpu_loop_exit_restore(cs, retaddr); } return true; } + +static target_ulong riscv_transformed_insn(CPURISCVState *env, + target_ulong insn, + target_ulong taddr) +{ + target_ulong xinsn = 0; + target_ulong access_rs1 = 0, access_imm = 0, access_size = 0; + + /* + * Only Quadrant 0 and Quadrant 2 of RVC instruction space need to + * be uncompressed. The Quadrant 1 of RVC instruction space need + * not be transformed because these instructions won't generate + * any load/store trap. + */ + + if ((insn & 0x3) != 0x3) { + /* Transform 16bit instruction into 32bit instruction */ + switch (GET_C_OP(insn)) { + case OPC_RISC_C_OP_QUAD0: /* Quadrant 0 */ + switch (GET_C_FUNC(insn)) { + case OPC_RISC_C_FUNC_FLD_LQ: + if (riscv_cpu_xlen(env) != 128) { /* C.FLD (RV32/64) */ + xinsn = OPC_RISC_FLD; + xinsn = SET_RD(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_LD_IMM(insn); + access_size = 8; + } + break; + case OPC_RISC_C_FUNC_LW: /* C.LW */ + xinsn = OPC_RISC_LW; + xinsn = SET_RD(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_LW_IMM(insn); + access_size = 4; + break; + case OPC_RISC_C_FUNC_FLW_LD: + if (riscv_cpu_xlen(env) == 32) { /* C.FLW (RV32) */ + xinsn = OPC_RISC_FLW; + xinsn = SET_RD(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_LW_IMM(insn); + access_size = 4; + } else { /* C.LD (RV64/RV128) */ + xinsn = OPC_RISC_LD; + xinsn = SET_RD(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_LD_IMM(insn); + access_size = 8; + } + break; + case OPC_RISC_C_FUNC_FSD_SQ: + if (riscv_cpu_xlen(env) != 128) { /* C.FSD (RV32/64) */ + xinsn = OPC_RISC_FSD; + xinsn = SET_RS2(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_SD_IMM(insn); + access_size = 8; + } + break; + case OPC_RISC_C_FUNC_SW: /* C.SW */ + xinsn = OPC_RISC_SW; + xinsn = SET_RS2(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_SW_IMM(insn); + access_size = 4; + break; + case OPC_RISC_C_FUNC_FSW_SD: + if (riscv_cpu_xlen(env) == 32) { /* C.FSW (RV32) */ + xinsn = OPC_RISC_FSW; + xinsn = SET_RS2(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_SW_IMM(insn); + access_size = 4; + } else { /* C.SD (RV64/RV128) */ + xinsn = OPC_RISC_SD; + xinsn = SET_RS2(xinsn, GET_C_RS2S(insn)); + access_rs1 = GET_C_RS1S(insn); + access_imm = GET_C_SD_IMM(insn); + access_size = 8; + } + break; + default: + break; + } + break; + case OPC_RISC_C_OP_QUAD2: /* Quadrant 2 */ + switch (GET_C_FUNC(insn)) { + case OPC_RISC_C_FUNC_FLDSP_LQSP: + if (riscv_cpu_xlen(env) != 128) { /* C.FLDSP (RV32/64) */ + xinsn = OPC_RISC_FLD; + xinsn = SET_RD(xinsn, GET_C_RD(insn)); + access_rs1 = 2; + access_imm = GET_C_LDSP_IMM(insn); + access_size = 8; + } + break; + case OPC_RISC_C_FUNC_LWSP: /* C.LWSP */ + xinsn = OPC_RISC_LW; + xinsn = SET_RD(xinsn, GET_C_RD(insn)); + access_rs1 = 2; + access_imm = GET_C_LWSP_IMM(insn); + access_size = 4; + break; + case OPC_RISC_C_FUNC_FLWSP_LDSP: + if (riscv_cpu_xlen(env) == 32) { /* C.FLWSP (RV32) */ + xinsn = OPC_RISC_FLW; + xinsn = SET_RD(xinsn, GET_C_RD(insn)); + access_rs1 = 2; + access_imm = GET_C_LWSP_IMM(insn); + access_size = 4; + } else { /* C.LDSP (RV64/RV128) */ + xinsn = OPC_RISC_LD; + xinsn = SET_RD(xinsn, GET_C_RD(insn)); + access_rs1 = 2; + access_imm = GET_C_LDSP_IMM(insn); + access_size = 8; + } + break; + case OPC_RISC_C_FUNC_FSDSP_SQSP: + if (riscv_cpu_xlen(env) != 128) { /* C.FSDSP (RV32/64) */ + xinsn = OPC_RISC_FSD; + xinsn = SET_RS2(xinsn, GET_C_RS2(insn)); + access_rs1 = 2; + access_imm = GET_C_SDSP_IMM(insn); + access_size = 8; + } + break; + case OPC_RISC_C_FUNC_SWSP: /* C.SWSP */ + xinsn = OPC_RISC_SW; + xinsn = SET_RS2(xinsn, GET_C_RS2(insn)); + access_rs1 = 2; + access_imm = GET_C_SWSP_IMM(insn); + access_size = 4; + break; + case 7: + if (riscv_cpu_xlen(env) == 32) { /* C.FSWSP (RV32) */ + xinsn = OPC_RISC_FSW; + xinsn = SET_RS2(xinsn, GET_C_RS2(insn)); + access_rs1 = 2; + access_imm = GET_C_SWSP_IMM(insn); + access_size = 4; + } else { /* C.SDSP (RV64/RV128) */ + xinsn = OPC_RISC_SD; + xinsn = SET_RS2(xinsn, GET_C_RS2(insn)); + access_rs1 = 2; + access_imm = GET_C_SDSP_IMM(insn); + access_size = 8; + } + break; + default: + break; + } + break; + default: + break; + } + + /* + * Clear Bit1 of transformed instruction to indicate that + * original insruction was a 16bit instruction + */ + xinsn &= ~((target_ulong)0x2); + } else { + /* Transform 32bit (or wider) instructions */ + switch (MASK_OP_MAJOR(insn)) { + case OPC_RISC_ATOMIC: + xinsn = insn; + access_rs1 = GET_RS1(insn); + access_size = 1 << GET_FUNCT3(insn); + break; + case OPC_RISC_LOAD: + case OPC_RISC_FP_LOAD: + xinsn = SET_I_IMM(insn, 0); + access_rs1 = GET_RS1(insn); + access_imm = GET_IMM(insn); + access_size = 1 << GET_FUNCT3(insn); + break; + case OPC_RISC_STORE: + case OPC_RISC_FP_STORE: + xinsn = SET_S_IMM(insn, 0); + access_rs1 = GET_RS1(insn); + access_imm = GET_STORE_IMM(insn); + access_size = 1 << GET_FUNCT3(insn); + break; + case OPC_RISC_SYSTEM: + if (MASK_OP_SYSTEM(insn) == OPC_RISC_HLVHSV) { + xinsn = insn; + access_rs1 = GET_RS1(insn); + access_size = 1 << ((GET_FUNCT7(insn) >> 1) & 0x3); + access_size = 1 << access_size; + } + break; + default: + break; + } + } + + if (access_size) { + xinsn = SET_RS1(xinsn, (taddr - (env->gpr[access_rs1] + access_imm)) & + (access_size - 1)); + } + + return xinsn; +} #endif /* !CONFIG_USER_ONLY */ /* @@ -1333,47 +1651,77 @@ void riscv_cpu_do_interrupt(CPUState *cs) bool write_gva = false; uint64_t s; - /* cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide + /* + * 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. */ bool async = !!(cs->exception_index & RISCV_EXCP_INT_FLAG); target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK; uint64_t deleg = async ? env->mideleg : env->medeleg; + bool s_injected = env->mvip & (1 << cause) & env->mvien && + !(env->mip & (1 << cause)); + bool vs_injected = env->hvip & (1 << cause) & env->hvien && + !(env->mip & (1 << cause)); target_ulong tval = 0; + target_ulong tinst = 0; target_ulong htval = 0; target_ulong mtval2 = 0; - if (cause == RISCV_EXCP_SEMIHOST) { - if (env->priv >= PRV_S) { - env->gpr[xA0] = do_common_semihosting(cs); - env->pc += 4; - return; - } - cause = RISCV_EXCP_BREAKPOINT; - } - if (!async) { /* set tval to badaddr for traps with address information */ switch (cause) { - case RISCV_EXCP_INST_GUEST_PAGE_FAULT: + case RISCV_EXCP_SEMIHOST: + do_common_semihosting(cs); + env->pc += 4; + return; case RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT: case RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT: - case RISCV_EXCP_INST_ADDR_MIS: - case RISCV_EXCP_INST_ACCESS_FAULT: case RISCV_EXCP_LOAD_ADDR_MIS: case RISCV_EXCP_STORE_AMO_ADDR_MIS: case RISCV_EXCP_LOAD_ACCESS_FAULT: case RISCV_EXCP_STORE_AMO_ACCESS_FAULT: - case RISCV_EXCP_INST_PAGE_FAULT: case RISCV_EXCP_LOAD_PAGE_FAULT: case RISCV_EXCP_STORE_PAGE_FAULT: write_gva = env->two_stage_lookup; tval = env->badaddr; + if (env->two_stage_indirect_lookup) { + /* + * special pseudoinstruction for G-stage fault taken while + * doing VS-stage page table walk. + */ + tinst = (riscv_cpu_xlen(env) == 32) ? 0x00002000 : 0x00003000; + } else { + /* + * The "Addr. Offset" field in transformed instruction is + * non-zero only for misaligned access. + */ + tinst = riscv_transformed_insn(env, env->bins, tval); + } + break; + case RISCV_EXCP_INST_GUEST_PAGE_FAULT: + case RISCV_EXCP_INST_ADDR_MIS: + case RISCV_EXCP_INST_ACCESS_FAULT: + case RISCV_EXCP_INST_PAGE_FAULT: + write_gva = env->two_stage_lookup; + tval = env->badaddr; + if (env->two_stage_indirect_lookup) { + /* + * special pseudoinstruction for G-stage fault taken while + * doing VS-stage page table walk. + */ + tinst = (riscv_cpu_xlen(env) == 32) ? 0x00002000 : 0x00003000; + } break; case RISCV_EXCP_ILLEGAL_INST: case RISCV_EXCP_VIRT_INSTRUCTION_FAULT: tval = env->bins; break; + case RISCV_EXCP_BREAKPOINT: + if (cs->watchpoint_hit) { + tval = cs->watchpoint_hit->hitaddr; + cs->watchpoint_hit = NULL; + } + break; default: break; } @@ -1383,9 +1731,9 @@ void riscv_cpu_do_interrupt(CPUState *cs) if (env->priv == PRV_M) { cause = RISCV_EXCP_M_ECALL; - } else if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) { + } else if (env->priv == PRV_S && env->virt_enabled) { cause = RISCV_EXCP_VS_ECALL; - } else if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) { + } else if (env->priv == PRV_S && !env->virt_enabled) { cause = RISCV_EXCP_S_ECALL; } else if (env->priv == PRV_U) { cause = RISCV_EXCP_U_ECALL; @@ -1402,31 +1750,30 @@ void riscv_cpu_do_interrupt(CPUState *cs) __func__, env->mhartid, async, cause, env->pc, tval, riscv_cpu_get_trap_name(cause, async)); - if (env->priv <= PRV_S && - cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) { + if (env->priv <= PRV_S && cause < 64 && + (((deleg >> cause) & 1) || s_injected || vs_injected)) { /* handle the trap in S-mode */ if (riscv_has_ext(env, RVH)) { uint64_t hdeleg = async ? env->hideleg : env->hedeleg; - if (riscv_cpu_virt_enabled(env) && ((hdeleg >> cause) & 1)) { + if (env->virt_enabled && + (((hdeleg >> cause) & 1) || vs_injected)) { /* Trap to VS mode */ /* * 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) { + if (async && (cause == IRQ_VS_TIMER || cause == IRQ_VS_SOFT || + cause == IRQ_VS_EXT)) { cause = cause - 1; } write_gva = false; - } else if (riscv_cpu_virt_enabled(env)) { + } else if (env->virt_enabled) { /* Trap into HS mode, from virt */ riscv_cpu_swap_hypervisor_regs(env); env->hstatus = set_field(env->hstatus, HSTATUS_SPVP, env->priv); - env->hstatus = set_field(env->hstatus, HSTATUS_SPV, - riscv_cpu_virt_enabled(env)); - + env->hstatus = set_field(env->hstatus, HSTATUS_SPV, true); htval = env->guest_phys_fault_addr; @@ -1448,18 +1795,19 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->sepc = env->pc; env->stval = tval; env->htval = htval; + env->htinst = tinst; env->pc = (env->stvec >> 2 << 2) + - ((async && (env->stvec & 3) == 1) ? cause * 4 : 0); + ((async && (env->stvec & 3) == 1) ? cause * 4 : 0); riscv_cpu_set_mode(env, PRV_S); } else { /* handle the trap in M-mode */ if (riscv_has_ext(env, RVH)) { - if (riscv_cpu_virt_enabled(env)) { + if (env->virt_enabled) { riscv_cpu_swap_hypervisor_regs(env); } env->mstatus = set_field(env->mstatus, MSTATUS_MPV, - riscv_cpu_virt_enabled(env)); - if (riscv_cpu_virt_enabled(env) && tval) { + env->virt_enabled); + if (env->virt_enabled && tval) { env->mstatus = set_field(env->mstatus, MSTATUS_GVA, 1); } @@ -1478,18 +1826,21 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->mepc = env->pc; env->mtval = tval; env->mtval2 = mtval2; + env->mtinst = tinst; env->pc = (env->mtvec >> 2 << 2) + - ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0); + ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0); riscv_cpu_set_mode(env, PRV_M); } - /* NOTE: it is not necessary to yield load reservations here. It is only + /* + * NOTE: it is not necessary to yield load reservations here. It is only * necessary for an SC from "another hart" to cause a load reservation * to be yielded. Refer to the memory consistency model section of the * RISC-V ISA Specification. */ env->two_stage_lookup = false; + env->two_stage_indirect_lookup = false; #endif cs->exception_index = RISCV_EXCP_NONE; /* mark handled to qemu */ } diff --git a/target/riscv/cpu_vendorid.h b/target/riscv/cpu_vendorid.h new file mode 100644 index 0000000000..96b6b9c2cb --- /dev/null +++ b/target/riscv/cpu_vendorid.h @@ -0,0 +1,10 @@ +#ifndef TARGET_RISCV_CPU_VENDORID_H +#define TARGET_RISCV_CPU_VENDORID_H + +#define THEAD_VENDOR_ID 0x5b7 + +#define VEYRON_V1_MARCHID 0x8000000000010000 +#define VEYRON_V1_MIMPID 0x111 +#define VEYRON_V1_MVENDORID 0x61f + +#endif /* TARGET_RISCV_CPU_VENDORID_H */ diff --git a/target/riscv/crypto_helper.c b/target/riscv/crypto_helper.c index 2ef30281b1..bb084e00ef 100644 --- a/target/riscv/crypto_helper.c +++ b/target/riscv/crypto_helper.c @@ -22,31 +22,9 @@ #include "exec/exec-all.h" #include "exec/helper-proto.h" #include "crypto/aes.h" +#include "crypto/aes-round.h" #include "crypto/sm4.h" -#define AES_XTIME(a) \ - ((a << 1) ^ ((a & 0x80) ? 0x1b : 0)) - -#define AES_GFMUL(a, b) (( \ - (((b) & 0x1) ? (a) : 0) ^ \ - (((b) & 0x2) ? AES_XTIME(a) : 0) ^ \ - (((b) & 0x4) ? AES_XTIME(AES_XTIME(a)) : 0) ^ \ - (((b) & 0x8) ? AES_XTIME(AES_XTIME(AES_XTIME(a))) : 0)) & 0xFF) - -static inline uint32_t aes_mixcolumn_byte(uint8_t x, bool fwd) -{ - uint32_t u; - - if (fwd) { - u = (AES_GFMUL(x, 3) << 24) | (x << 16) | (x << 8) | - (AES_GFMUL(x, 2) << 0); - } else { - u = (AES_GFMUL(x, 0xb) << 24) | (AES_GFMUL(x, 0xd) << 16) | - (AES_GFMUL(x, 0x9) << 8) | (AES_GFMUL(x, 0xe) << 0); - } - return u; -} - #define sext32_xlen(x) (target_ulong)(int32_t)(x) static inline target_ulong aes32_operation(target_ulong shamt, @@ -54,23 +32,20 @@ static inline target_ulong aes32_operation(target_ulong shamt, bool enc, bool mix) { uint8_t si = rs2 >> shamt; - uint8_t so; uint32_t mixed; target_ulong res; if (enc) { - so = AES_sbox[si]; if (mix) { - mixed = aes_mixcolumn_byte(so, true); + mixed = be32_to_cpu(AES_Te0[si]); } else { - mixed = so; + mixed = AES_sbox[si]; } } else { - so = AES_isbox[si]; if (mix) { - mixed = aes_mixcolumn_byte(so, false); + mixed = be32_to_cpu(AES_Td0[si]); } else { - mixed = so; + mixed = AES_isbox[si]; } } mixed = rol32(mixed, shamt); @@ -103,114 +78,50 @@ target_ulong HELPER(aes32dsi)(target_ulong rs1, target_ulong rs2, return aes32_operation(shamt, rs1, rs2, false, false); } -#define BY(X, I) ((X >> (8 * I)) & 0xFF) - -#define AES_SHIFROWS_LO(RS1, RS2) ( \ - (((RS1 >> 24) & 0xFF) << 56) | (((RS2 >> 48) & 0xFF) << 48) | \ - (((RS2 >> 8) & 0xFF) << 40) | (((RS1 >> 32) & 0xFF) << 32) | \ - (((RS2 >> 56) & 0xFF) << 24) | (((RS2 >> 16) & 0xFF) << 16) | \ - (((RS1 >> 40) & 0xFF) << 8) | (((RS1 >> 0) & 0xFF) << 0)) - -#define AES_INVSHIFROWS_LO(RS1, RS2) ( \ - (((RS2 >> 24) & 0xFF) << 56) | (((RS2 >> 48) & 0xFF) << 48) | \ - (((RS1 >> 8) & 0xFF) << 40) | (((RS1 >> 32) & 0xFF) << 32) | \ - (((RS1 >> 56) & 0xFF) << 24) | (((RS2 >> 16) & 0xFF) << 16) | \ - (((RS2 >> 40) & 0xFF) << 8) | (((RS1 >> 0) & 0xFF) << 0)) - -#define AES_MIXBYTE(COL, B0, B1, B2, B3) ( \ - BY(COL, B3) ^ BY(COL, B2) ^ AES_GFMUL(BY(COL, B1), 3) ^ \ - AES_GFMUL(BY(COL, B0), 2)) - -#define AES_MIXCOLUMN(COL) ( \ - AES_MIXBYTE(COL, 3, 0, 1, 2) << 24 | \ - AES_MIXBYTE(COL, 2, 3, 0, 1) << 16 | \ - AES_MIXBYTE(COL, 1, 2, 3, 0) << 8 | AES_MIXBYTE(COL, 0, 1, 2, 3) << 0) - -#define AES_INVMIXBYTE(COL, B0, B1, B2, B3) ( \ - AES_GFMUL(BY(COL, B3), 0x9) ^ AES_GFMUL(BY(COL, B2), 0xd) ^ \ - AES_GFMUL(BY(COL, B1), 0xb) ^ AES_GFMUL(BY(COL, B0), 0xe)) - -#define AES_INVMIXCOLUMN(COL) ( \ - AES_INVMIXBYTE(COL, 3, 0, 1, 2) << 24 | \ - AES_INVMIXBYTE(COL, 2, 3, 0, 1) << 16 | \ - AES_INVMIXBYTE(COL, 1, 2, 3, 0) << 8 | \ - AES_INVMIXBYTE(COL, 0, 1, 2, 3) << 0) - -static inline target_ulong aes64_operation(target_ulong rs1, target_ulong rs2, - bool enc, bool mix) -{ - uint64_t RS1 = rs1; - uint64_t RS2 = rs2; - uint64_t result; - uint64_t temp; - uint32_t col_0; - uint32_t col_1; - - if (enc) { - temp = AES_SHIFROWS_LO(RS1, RS2); - temp = (((uint64_t)AES_sbox[(temp >> 0) & 0xFF] << 0) | - ((uint64_t)AES_sbox[(temp >> 8) & 0xFF] << 8) | - ((uint64_t)AES_sbox[(temp >> 16) & 0xFF] << 16) | - ((uint64_t)AES_sbox[(temp >> 24) & 0xFF] << 24) | - ((uint64_t)AES_sbox[(temp >> 32) & 0xFF] << 32) | - ((uint64_t)AES_sbox[(temp >> 40) & 0xFF] << 40) | - ((uint64_t)AES_sbox[(temp >> 48) & 0xFF] << 48) | - ((uint64_t)AES_sbox[(temp >> 56) & 0xFF] << 56)); - if (mix) { - col_0 = temp & 0xFFFFFFFF; - col_1 = temp >> 32; - - col_0 = AES_MIXCOLUMN(col_0); - col_1 = AES_MIXCOLUMN(col_1); - - result = ((uint64_t)col_1 << 32) | col_0; - } else { - result = temp; - } - } else { - temp = AES_INVSHIFROWS_LO(RS1, RS2); - temp = (((uint64_t)AES_isbox[(temp >> 0) & 0xFF] << 0) | - ((uint64_t)AES_isbox[(temp >> 8) & 0xFF] << 8) | - ((uint64_t)AES_isbox[(temp >> 16) & 0xFF] << 16) | - ((uint64_t)AES_isbox[(temp >> 24) & 0xFF] << 24) | - ((uint64_t)AES_isbox[(temp >> 32) & 0xFF] << 32) | - ((uint64_t)AES_isbox[(temp >> 40) & 0xFF] << 40) | - ((uint64_t)AES_isbox[(temp >> 48) & 0xFF] << 48) | - ((uint64_t)AES_isbox[(temp >> 56) & 0xFF] << 56)); - if (mix) { - col_0 = temp & 0xFFFFFFFF; - col_1 = temp >> 32; - - col_0 = AES_INVMIXCOLUMN(col_0); - col_1 = AES_INVMIXCOLUMN(col_1); - - result = ((uint64_t)col_1 << 32) | col_0; - } else { - result = temp; - } - } - - return result; -} +static const AESState aes_zero = { }; target_ulong HELPER(aes64esm)(target_ulong rs1, target_ulong rs2) { - return aes64_operation(rs1, rs2, true, true); + AESState t; + + t.d[HOST_BIG_ENDIAN] = rs1; + t.d[!HOST_BIG_ENDIAN] = rs2; + aesenc_SB_SR_MC_AK(&t, &t, &aes_zero, false); + return t.d[HOST_BIG_ENDIAN]; } target_ulong HELPER(aes64es)(target_ulong rs1, target_ulong rs2) { - return aes64_operation(rs1, rs2, true, false); + AESState t; + + t.d[HOST_BIG_ENDIAN] = rs1; + t.d[!HOST_BIG_ENDIAN] = rs2; + aesenc_SB_SR_AK(&t, &t, &aes_zero, false); + return t.d[HOST_BIG_ENDIAN]; } target_ulong HELPER(aes64ds)(target_ulong rs1, target_ulong rs2) { - return aes64_operation(rs1, rs2, false, false); + AESState t; + + t.d[HOST_BIG_ENDIAN] = rs1; + t.d[!HOST_BIG_ENDIAN] = rs2; + aesdec_ISB_ISR_AK(&t, &t, &aes_zero, false); + return t.d[HOST_BIG_ENDIAN]; } target_ulong HELPER(aes64dsm)(target_ulong rs1, target_ulong rs2) { - return aes64_operation(rs1, rs2, false, true); + AESState t, z = { }; + + /* + * This instruction does not include a round key, + * so supply a zero to our primitive. + */ + t.d[HOST_BIG_ENDIAN] = rs1; + t.d[!HOST_BIG_ENDIAN] = rs2; + aesdec_ISB_ISR_IMC_AK(&t, &t, &z, false); + return t.d[HOST_BIG_ENDIAN]; } target_ulong HELPER(aes64ks2)(target_ulong rs1, target_ulong rs2) @@ -237,39 +148,27 @@ target_ulong HELPER(aes64ks1i)(target_ulong rs1, target_ulong rnum) uint8_t enc_rnum = rnum; uint32_t temp = (RS1 >> 32) & 0xFFFFFFFF; - uint8_t rcon_ = 0; - target_ulong result; + AESState t, rc = {}; if (enc_rnum != 0xA) { temp = ror32(temp, 8); /* Rotate right by 8 */ - rcon_ = round_consts[enc_rnum]; + rc.w[0] = rc.w[1] = round_consts[enc_rnum]; } - temp = ((uint32_t)AES_sbox[(temp >> 24) & 0xFF] << 24) | - ((uint32_t)AES_sbox[(temp >> 16) & 0xFF] << 16) | - ((uint32_t)AES_sbox[(temp >> 8) & 0xFF] << 8) | - ((uint32_t)AES_sbox[(temp >> 0) & 0xFF] << 0); + t.w[0] = t.w[1] = t.w[2] = t.w[3] = temp; + aesenc_SB_SR_AK(&t, &t, &rc, false); - temp ^= rcon_; - - result = ((uint64_t)temp << 32) | temp; - - return result; + return t.d[0]; } target_ulong HELPER(aes64im)(target_ulong rs1) { - uint64_t RS1 = rs1; - uint32_t col_0 = RS1 & 0xFFFFFFFF; - uint32_t col_1 = RS1 >> 32; - target_ulong result; + AESState t; - col_0 = AES_INVMIXCOLUMN(col_0); - col_1 = AES_INVMIXCOLUMN(col_1); - - result = ((uint64_t)col_1 << 32) | col_0; - - return result; + t.d[HOST_BIG_ENDIAN] = rs1; + t.d[!HOST_BIG_ENDIAN] = 0; + aesdec_IMC(&t, &t, false); + return t.d[HOST_BIG_ENDIAN]; } target_ulong HELPER(sm4ed)(target_ulong rs1, target_ulong rs2, diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 6dbe9b541f..726096444f 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -21,12 +21,16 @@ #include "qemu/log.h" #include "qemu/timer.h" #include "cpu.h" -#include "qemu/main-loop.h" +#include "tcg/tcg-cpu.h" +#include "pmu.h" +#include "time_helper.h" #include "exec/exec-all.h" +#include "exec/tb-flush.h" #include "sysemu/cpu-timers.h" #include "qemu/guest-random.h" #include "qapi/error.h" + /* CSR function table public API */ void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) { @@ -39,24 +43,57 @@ void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops) } /* Predicates */ +#if !defined(CONFIG_USER_ONLY) +RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit) +{ + bool virt = env->virt_enabled; + + if (env->priv == PRV_M || !riscv_cpu_cfg(env)->ext_smstateen) { + return RISCV_EXCP_NONE; + } + + if (!(env->mstateen[index] & bit)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + if (virt) { + if (!(env->hstateen[index] & bit)) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } + + if (env->priv == PRV_U && !(env->sstateen[index] & bit)) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } + } + + if (env->priv == PRV_U && riscv_has_ext(env, RVS)) { + if (!(env->sstateen[index] & bit)) { + return RISCV_EXCP_ILLEGAL_INST; + } + } + + return RISCV_EXCP_NONE; +} +#endif + static RISCVException fs(CPURISCVState *env, int csrno) { #if !defined(CONFIG_USER_ONLY) if (!env->debugger && !riscv_cpu_fp_enabled(env) && - !RISCV_CPU(env_cpu(env))->cfg.ext_zfinx) { + !riscv_cpu_cfg(env)->ext_zfinx) { return RISCV_EXCP_ILLEGAL_INST; } + + if (!env->debugger && !riscv_cpu_fp_enabled(env)) { + return smstateen_acc_ok(env, 0, SMSTATEEN0_FCSR); + } #endif return RISCV_EXCP_NONE; } static RISCVException vs(CPURISCVState *env, int csrno) { - CPUState *cs = env_cpu(env); - RISCVCPU *cpu = RISCV_CPU(cs); - - if (env->misa_ext & RVV || - cpu->cfg.ext_zve32f || cpu->cfg.ext_zve64f) { + if (riscv_cpu_cfg(env)->ext_zve32f) { #if !defined(CONFIG_USER_ONLY) if (!env->debugger && !riscv_cpu_vector_enabled(env)) { return RISCV_EXCP_ILLEGAL_INST; @@ -70,70 +107,55 @@ static RISCVException vs(CPURISCVState *env, int csrno) static RISCVException ctr(CPURISCVState *env, int csrno) { #if !defined(CONFIG_USER_ONLY) - CPUState *cs = env_cpu(env); - RISCVCPU *cpu = RISCV_CPU(cs); + RISCVCPU *cpu = env_archcpu(env); + int ctr_index; + target_ulong ctr_mask; + int base_csrno = CSR_CYCLE; + bool rv32 = riscv_cpu_mxl(env) == MXL_RV32 ? true : false; - if (!cpu->cfg.ext_counters) { - /* The Counters extensions is not enabled */ + if (rv32 && csrno >= CSR_CYCLEH) { + /* Offset for RV32 hpmcounternh counters */ + base_csrno += 0x80; + } + ctr_index = csrno - base_csrno; + ctr_mask = BIT(ctr_index); + + if ((csrno >= CSR_CYCLE && csrno <= CSR_INSTRET) || + (csrno >= CSR_CYCLEH && csrno <= CSR_INSTRETH)) { + if (!riscv_cpu_cfg(env)->ext_zicntr) { + return RISCV_EXCP_ILLEGAL_INST; + } + + goto skip_ext_pmu_check; + } + + if (!(cpu->pmu_avail_ctrs & ctr_mask)) { + /* No counter is enabled in PMU or the counter is out of range */ return RISCV_EXCP_ILLEGAL_INST; } - if (riscv_cpu_virt_enabled(env)) { - switch (csrno) { - case CSR_CYCLE: - if (!get_field(env->hcounteren, COUNTEREN_CY) && - get_field(env->mcounteren, COUNTEREN_CY)) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - case CSR_TIME: - if (!get_field(env->hcounteren, COUNTEREN_TM) && - get_field(env->mcounteren, COUNTEREN_TM)) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - case CSR_INSTRET: - if (!get_field(env->hcounteren, COUNTEREN_IR) && - get_field(env->mcounteren, COUNTEREN_IR)) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - case CSR_HPMCOUNTER3...CSR_HPMCOUNTER31: - if (!get_field(env->hcounteren, 1 << (csrno - CSR_HPMCOUNTER3)) && - get_field(env->mcounteren, 1 << (csrno - CSR_HPMCOUNTER3))) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - } - if (riscv_cpu_mxl(env) == MXL_RV32) { - switch (csrno) { - case CSR_CYCLEH: - if (!get_field(env->hcounteren, COUNTEREN_CY) && - get_field(env->mcounteren, COUNTEREN_CY)) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - case CSR_TIMEH: - if (!get_field(env->hcounteren, COUNTEREN_TM) && - get_field(env->mcounteren, COUNTEREN_TM)) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - case CSR_INSTRETH: - if (!get_field(env->hcounteren, COUNTEREN_IR) && - get_field(env->mcounteren, COUNTEREN_IR)) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - case CSR_HPMCOUNTER3H...CSR_HPMCOUNTER31H: - if (!get_field(env->hcounteren, 1 << (csrno - CSR_HPMCOUNTER3H)) && - get_field(env->mcounteren, 1 << (csrno - CSR_HPMCOUNTER3H))) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - break; - } +skip_ext_pmu_check: + + if (env->debugger) { + return RISCV_EXCP_NONE; + } + + if (env->priv < PRV_M && !get_field(env->mcounteren, ctr_mask)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + if (env->virt_enabled) { + if (!get_field(env->hcounteren, ctr_mask) || + (env->priv == PRV_U && !get_field(env->scounteren, ctr_mask))) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } } + + if (riscv_has_ext(env, RVS) && env->priv == PRV_U && + !get_field(env->scounteren, ctr_mask)) { + return RISCV_EXCP_ILLEGAL_INST; + } + #endif return RISCV_EXCP_NONE; } @@ -147,7 +169,64 @@ static RISCVException ctr32(CPURISCVState *env, int csrno) return ctr(env, csrno); } +static RISCVException zcmt(CPURISCVState *env, int csrno) +{ + if (!riscv_cpu_cfg(env)->ext_zcmt) { + return RISCV_EXCP_ILLEGAL_INST; + } + #if !defined(CONFIG_USER_ONLY) + RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_JVT); + if (ret != RISCV_EXCP_NONE) { + return ret; + } +#endif + + return RISCV_EXCP_NONE; +} + +#if !defined(CONFIG_USER_ONLY) +static RISCVException mctr(CPURISCVState *env, int csrno) +{ + RISCVCPU *cpu = env_archcpu(env); + uint32_t pmu_avail_ctrs = cpu->pmu_avail_ctrs; + int ctr_index; + int base_csrno = CSR_MHPMCOUNTER3; + + if ((riscv_cpu_mxl(env) == MXL_RV32) && csrno >= CSR_MCYCLEH) { + /* Offset for RV32 mhpmcounternh counters */ + csrno -= 0x80; + } + + g_assert(csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31); + + ctr_index = csrno - base_csrno; + if ((BIT(ctr_index) & pmu_avail_ctrs >> 3) == 0) { + /* The PMU is not enabled or counter is out of range */ + return RISCV_EXCP_ILLEGAL_INST; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException mctr32(CPURISCVState *env, int csrno) +{ + if (riscv_cpu_mxl(env) != MXL_RV32) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return mctr(env, csrno); +} + +static RISCVException sscofpmf(CPURISCVState *env, int csrno) +{ + if (!riscv_cpu_cfg(env)->ext_sscofpmf) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return RISCV_EXCP_NONE; +} + static RISCVException any(CPURISCVState *env, int csrno) { return RISCV_EXCP_NONE; @@ -163,18 +242,18 @@ static RISCVException any32(CPURISCVState *env, int csrno) } -static int aia_any(CPURISCVState *env, int csrno) +static RISCVException aia_any(CPURISCVState *env, int csrno) { - if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + if (!riscv_cpu_cfg(env)->ext_smaia) { return RISCV_EXCP_ILLEGAL_INST; } return any(env, csrno); } -static int aia_any32(CPURISCVState *env, int csrno) +static RISCVException aia_any32(CPURISCVState *env, int csrno) { - if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + if (!riscv_cpu_cfg(env)->ext_smaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -190,7 +269,7 @@ static RISCVException smode(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } -static int smode32(CPURISCVState *env, int csrno) +static RISCVException smode32(CPURISCVState *env, int csrno) { if (riscv_cpu_mxl(env) != MXL_RV32) { return RISCV_EXCP_ILLEGAL_INST; @@ -199,18 +278,18 @@ static int smode32(CPURISCVState *env, int csrno) return smode(env, csrno); } -static int aia_smode(CPURISCVState *env, int csrno) +static RISCVException aia_smode(CPURISCVState *env, int csrno) { - if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } return smode(env, csrno); } -static int aia_smode32(CPURISCVState *env, int csrno) +static RISCVException aia_smode32(CPURISCVState *env, int csrno) { - if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -219,15 +298,8 @@ static int aia_smode32(CPURISCVState *env, int csrno) static RISCVException 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 RISCV_EXCP_NONE; - } else { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } + if (riscv_has_ext(env, RVH)) { + return RISCV_EXCP_NONE; } return RISCV_EXCP_ILLEGAL_INST; @@ -236,17 +308,184 @@ static RISCVException hmode(CPURISCVState *env, int csrno) static RISCVException hmode32(CPURISCVState *env, int csrno) { if (riscv_cpu_mxl(env) != MXL_RV32) { - if (!riscv_cpu_virt_enabled(env)) { - return RISCV_EXCP_ILLEGAL_INST; - } else { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } + return RISCV_EXCP_ILLEGAL_INST; } return hmode(env, csrno); } +static RISCVException umode(CPURISCVState *env, int csrno) +{ + if (riscv_has_ext(env, RVU)) { + return RISCV_EXCP_NONE; + } + + return RISCV_EXCP_ILLEGAL_INST; +} + +static RISCVException umode32(CPURISCVState *env, int csrno) +{ + if (riscv_cpu_mxl(env) != MXL_RV32) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return umode(env, csrno); +} + +static RISCVException mstateen(CPURISCVState *env, int csrno) +{ + if (!riscv_cpu_cfg(env)->ext_smstateen) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return any(env, csrno); +} + +static RISCVException hstateen_pred(CPURISCVState *env, int csrno, int base) +{ + if (!riscv_cpu_cfg(env)->ext_smstateen) { + return RISCV_EXCP_ILLEGAL_INST; + } + + RISCVException ret = hmode(env, csrno); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + if (env->debugger) { + return RISCV_EXCP_NONE; + } + + if (env->priv < PRV_M) { + if (!(env->mstateen[csrno - base] & SMSTATEEN_STATEEN)) { + return RISCV_EXCP_ILLEGAL_INST; + } + } + + return RISCV_EXCP_NONE; +} + +static RISCVException hstateen(CPURISCVState *env, int csrno) +{ + return hstateen_pred(env, csrno, CSR_HSTATEEN0); +} + +static RISCVException hstateenh(CPURISCVState *env, int csrno) +{ + return hstateen_pred(env, csrno, CSR_HSTATEEN0H); +} + +static RISCVException sstateen(CPURISCVState *env, int csrno) +{ + bool virt = env->virt_enabled; + int index = csrno - CSR_SSTATEEN0; + + if (!riscv_cpu_cfg(env)->ext_smstateen) { + return RISCV_EXCP_ILLEGAL_INST; + } + + RISCVException ret = smode(env, csrno); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + if (env->debugger) { + return RISCV_EXCP_NONE; + } + + if (env->priv < PRV_M) { + if (!(env->mstateen[index] & SMSTATEEN_STATEEN)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + if (virt) { + if (!(env->hstateen[index] & SMSTATEEN_STATEEN)) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } + } + } + + return RISCV_EXCP_NONE; +} + +static RISCVException sstc(CPURISCVState *env, int csrno) +{ + bool hmode_check = false; + + if (!riscv_cpu_cfg(env)->ext_sstc || !env->rdtime_fn) { + return RISCV_EXCP_ILLEGAL_INST; + } + + if ((csrno == CSR_VSTIMECMP) || (csrno == CSR_VSTIMECMPH)) { + hmode_check = true; + } + + RISCVException ret = hmode_check ? hmode(env, csrno) : smode(env, csrno); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + if (env->debugger) { + return RISCV_EXCP_NONE; + } + + if (env->priv == PRV_M) { + return RISCV_EXCP_NONE; + } + + /* + * No need of separate function for rv32 as menvcfg stores both menvcfg + * menvcfgh for RV32. + */ + if (!(get_field(env->mcounteren, COUNTEREN_TM) && + get_field(env->menvcfg, MENVCFG_STCE))) { + return RISCV_EXCP_ILLEGAL_INST; + } + + if (env->virt_enabled) { + if (!(get_field(env->hcounteren, COUNTEREN_TM) && + get_field(env->henvcfg, HENVCFG_STCE))) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } + } + + return RISCV_EXCP_NONE; +} + +static RISCVException sstc_32(CPURISCVState *env, int csrno) +{ + if (riscv_cpu_mxl(env) != MXL_RV32) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return sstc(env, csrno); +} + +static RISCVException satp(CPURISCVState *env, int csrno) +{ + if (env->priv == PRV_S && !env->virt_enabled && + get_field(env->mstatus, MSTATUS_TVM)) { + return RISCV_EXCP_ILLEGAL_INST; + } + if (env->priv == PRV_S && env->virt_enabled && + get_field(env->hstatus, HSTATUS_VTVM)) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } + + return smode(env, csrno); +} + +static RISCVException hgatp(CPURISCVState *env, int csrno) +{ + if (env->priv == PRV_S && !env->virt_enabled && + get_field(env->mstatus, MSTATUS_TVM)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return hmode(env, csrno); +} + /* Checks if PointerMasking registers could be accessed */ static RISCVException pointer_masking(CPURISCVState *env, int csrno) { @@ -257,18 +496,18 @@ static RISCVException pointer_masking(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } -static int aia_hmode(CPURISCVState *env, int csrno) +static RISCVException aia_hmode(CPURISCVState *env, int csrno) { - if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } return hmode(env, csrno); } -static int aia_hmode32(CPURISCVState *env, int csrno) +static RISCVException aia_hmode32(CPURISCVState *env, int csrno) { - if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -277,16 +516,28 @@ static int aia_hmode32(CPURISCVState *env, int csrno) static RISCVException pmp(CPURISCVState *env, int csrno) { - if (riscv_feature(env, RISCV_FEATURE_PMP)) { + if (riscv_cpu_cfg(env)->pmp) { + if (csrno <= CSR_PMPCFG3) { + uint32_t reg_index = csrno - CSR_PMPCFG0; + + /* TODO: RV128 restriction check */ + if ((reg_index & 1) && (riscv_cpu_mxl(env) == MXL_RV64)) { + return RISCV_EXCP_ILLEGAL_INST; + } + } + return RISCV_EXCP_NONE; } return RISCV_EXCP_ILLEGAL_INST; } -static RISCVException epmp(CPURISCVState *env, int csrno) +static RISCVException have_mseccfg(CPURISCVState *env, int csrno) { - if (env->priv == PRV_M && riscv_feature(env, RISCV_FEATURE_EPMP)) { + if (riscv_cpu_cfg(env)->ext_smepmp) { + return RISCV_EXCP_NONE; + } + if (riscv_cpu_cfg(env)->ext_zkr) { return RISCV_EXCP_NONE; } @@ -295,7 +546,7 @@ static RISCVException epmp(CPURISCVState *env, int csrno) static RISCVException debug(CPURISCVState *env, int csrno) { - if (riscv_feature(env, RISCV_FEATURE_DEBUG)) { + if (riscv_cpu_cfg(env)->debug) { return RISCV_EXCP_NONE; } @@ -305,13 +556,15 @@ static RISCVException debug(CPURISCVState *env, int csrno) static RISCVException seed(CPURISCVState *env, int csrno) { - RISCVCPU *cpu = env_archcpu(env); - - if (!cpu->cfg.ext_zkr) { + if (!riscv_cpu_cfg(env)->ext_zkr) { return RISCV_EXCP_ILLEGAL_INST; } #if !defined(CONFIG_USER_ONLY) + if (env->debugger) { + return RISCV_EXCP_NONE; + } + /* * With a CSR read-write instruction: * 1) The seed CSR is always available in machine mode as normal. @@ -323,7 +576,7 @@ static RISCVException seed(CPURISCVState *env, int csrno) */ if (env->priv == PRV_M) { return RISCV_EXCP_NONE; - } else if (riscv_cpu_virt_enabled(env)) { + } else if (env->virt_enabled) { if (env->mseccfg & MSECCFG_SSEED) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } else { @@ -428,9 +681,10 @@ static RISCVException read_vl(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static int read_vlenb(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_vlenb(CPURISCVState *env, int csrno, + target_ulong *val) { - *val = env_archcpu(env)->cfg.vlen >> 3; + *val = riscv_cpu_cfg(env)->vlenb; return RISCV_EXCP_NONE; } @@ -485,17 +739,19 @@ static RISCVException write_vstart(CPURISCVState *env, int csrno, * The vstart CSR is defined to have only enough writable bits * to hold the largest element index, i.e. lg2(VLEN) bits. */ - env->vstart = val & ~(~0ULL << ctzl(env_archcpu(env)->cfg.vlen)); + env->vstart = val & ~(~0ULL << ctzl(riscv_cpu_cfg(env)->vlenb << 3)); return RISCV_EXCP_NONE; } -static int read_vcsr(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_vcsr(CPURISCVState *env, int csrno, + target_ulong *val) { *val = (env->vxrm << VCSR_VXRM_SHIFT) | (env->vxsat << VCSR_VXSAT_SHIFT); return RISCV_EXCP_NONE; } -static int write_vcsr(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_vcsr(CPURISCVState *env, int csrno, + target_ulong val) { #if !defined(CONFIG_USER_ONLY) env->mstatus |= MSTATUS_VS; @@ -506,34 +762,28 @@ static int write_vcsr(CPURISCVState *env, int csrno, target_ulong val) } /* User Timers and Counters */ -static RISCVException read_instret(CPURISCVState *env, int csrno, - target_ulong *val) +static target_ulong get_ticks(bool shift) { -#if !defined(CONFIG_USER_ONLY) - if (icount_enabled()) { - *val = icount_get(); - } else { - *val = cpu_get_host_ticks(); - } -#else - *val = cpu_get_host_ticks(); -#endif - return RISCV_EXCP_NONE; -} + int64_t val; + target_ulong result; -static RISCVException read_instreth(CPURISCVState *env, int csrno, - target_ulong *val) -{ #if !defined(CONFIG_USER_ONLY) if (icount_enabled()) { - *val = icount_get() >> 32; + val = icount_get(); } else { - *val = cpu_get_host_ticks() >> 32; + val = cpu_get_host_ticks(); } #else - *val = cpu_get_host_ticks() >> 32; + val = cpu_get_host_ticks(); #endif - return RISCV_EXCP_NONE; + + if (shift) { + result = val >> 32; + } else { + result = val; + } + + return result; } #if defined(CONFIG_USER_ONLY) @@ -551,12 +801,224 @@ static RISCVException read_timeh(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException read_hpmcounter(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = get_ticks(false); + return RISCV_EXCP_NONE; +} + +static RISCVException read_hpmcounterh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = get_ticks(true); + return RISCV_EXCP_NONE; +} + #else /* CONFIG_USER_ONLY */ +static RISCVException read_mhpmevent(CPURISCVState *env, int csrno, + target_ulong *val) +{ + int evt_index = csrno - CSR_MCOUNTINHIBIT; + + *val = env->mhpmevent_val[evt_index]; + + return RISCV_EXCP_NONE; +} + +static RISCVException write_mhpmevent(CPURISCVState *env, int csrno, + target_ulong val) +{ + int evt_index = csrno - CSR_MCOUNTINHIBIT; + uint64_t mhpmevt_val = val; + + env->mhpmevent_val[evt_index] = val; + + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpmevt_val = mhpmevt_val | + ((uint64_t)env->mhpmeventh_val[evt_index] << 32); + } + riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); + + return RISCV_EXCP_NONE; +} + +static RISCVException read_mhpmeventh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + int evt_index = csrno - CSR_MHPMEVENT3H + 3; + + *val = env->mhpmeventh_val[evt_index]; + + return RISCV_EXCP_NONE; +} + +static RISCVException write_mhpmeventh(CPURISCVState *env, int csrno, + target_ulong val) +{ + int evt_index = csrno - CSR_MHPMEVENT3H + 3; + uint64_t mhpmevth_val = val; + uint64_t mhpmevt_val = env->mhpmevent_val[evt_index]; + + mhpmevt_val = mhpmevt_val | (mhpmevth_val << 32); + env->mhpmeventh_val[evt_index] = val; + + riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); + + return RISCV_EXCP_NONE; +} + +static RISCVException write_mhpmcounter(CPURISCVState *env, int csrno, + target_ulong val) +{ + int ctr_idx = csrno - CSR_MCYCLE; + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + uint64_t mhpmctr_val = val; + + counter->mhpmcounter_val = val; + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { + counter->mhpmcounter_prev = get_ticks(false); + if (ctr_idx > 2) { + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpmctr_val = mhpmctr_val | + ((uint64_t)counter->mhpmcounterh_val << 32); + } + riscv_pmu_setup_timer(env, mhpmctr_val, ctr_idx); + } + } else { + /* Other counters can keep incrementing from the given value */ + counter->mhpmcounter_prev = val; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException write_mhpmcounterh(CPURISCVState *env, int csrno, + target_ulong val) +{ + int ctr_idx = csrno - CSR_MCYCLEH; + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + uint64_t mhpmctr_val = counter->mhpmcounter_val; + uint64_t mhpmctrh_val = val; + + counter->mhpmcounterh_val = val; + mhpmctr_val = mhpmctr_val | (mhpmctrh_val << 32); + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { + counter->mhpmcounterh_prev = get_ticks(true); + if (ctr_idx > 2) { + riscv_pmu_setup_timer(env, mhpmctr_val, ctr_idx); + } + } else { + counter->mhpmcounterh_prev = val; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, + bool upper_half, uint32_t ctr_idx) +{ + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + target_ulong ctr_prev = upper_half ? counter->mhpmcounterh_prev : + counter->mhpmcounter_prev; + target_ulong ctr_val = upper_half ? counter->mhpmcounterh_val : + counter->mhpmcounter_val; + + if (get_field(env->mcountinhibit, BIT(ctr_idx))) { + /* + * Counter should not increment if inhibit bit is set. We can't really + * stop the icount counting. Just return the counter value written by + * the supervisor to indicate that counter was not incremented. + */ + if (!counter->started) { + *val = ctr_val; + return RISCV_EXCP_NONE; + } else { + /* Mark that the counter has been stopped */ + counter->started = false; + } + } + + /* + * The kernel computes the perf delta by subtracting the current value from + * the value it initialized previously (ctr_val). + */ + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { + *val = get_ticks(upper_half) - ctr_prev + ctr_val; + } else { + *val = ctr_val; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException read_hpmcounter(CPURISCVState *env, int csrno, + target_ulong *val) +{ + uint16_t ctr_index; + + if (csrno >= CSR_MCYCLE && csrno <= CSR_MHPMCOUNTER31) { + ctr_index = csrno - CSR_MCYCLE; + } else if (csrno >= CSR_CYCLE && csrno <= CSR_HPMCOUNTER31) { + ctr_index = csrno - CSR_CYCLE; + } else { + return RISCV_EXCP_ILLEGAL_INST; + } + + return riscv_pmu_read_ctr(env, val, false, ctr_index); +} + +static RISCVException read_hpmcounterh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + uint16_t ctr_index; + + if (csrno >= CSR_MCYCLEH && csrno <= CSR_MHPMCOUNTER31H) { + ctr_index = csrno - CSR_MCYCLEH; + } else if (csrno >= CSR_CYCLEH && csrno <= CSR_HPMCOUNTER31H) { + ctr_index = csrno - CSR_CYCLEH; + } else { + return RISCV_EXCP_ILLEGAL_INST; + } + + return riscv_pmu_read_ctr(env, val, true, ctr_index); +} + +static RISCVException read_scountovf(CPURISCVState *env, int csrno, + target_ulong *val) +{ + int mhpmevt_start = CSR_MHPMEVENT3 - CSR_MCOUNTINHIBIT; + int i; + *val = 0; + target_ulong *mhpm_evt_val; + uint64_t of_bit_mask; + + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpm_evt_val = env->mhpmeventh_val; + of_bit_mask = MHPMEVENTH_BIT_OF; + } else { + mhpm_evt_val = env->mhpmevent_val; + of_bit_mask = MHPMEVENT_BIT_OF; + } + + for (i = mhpmevt_start; i < RV_MAX_MHPMEVENTS; i++) { + if ((get_field(env->mcounteren, BIT(i))) && + (mhpm_evt_val[i] & of_bit_mask)) { + *val |= BIT(i); + } + } + + return RISCV_EXCP_NONE; +} + static RISCVException read_time(CPURISCVState *env, int csrno, target_ulong *val) { - uint64_t delta = riscv_cpu_virt_enabled(env) ? env->htimedelta : 0; + uint64_t delta = env->virt_enabled ? env->htimedelta : 0; if (!env->rdtime_fn) { return RISCV_EXCP_ILLEGAL_INST; @@ -569,7 +1031,7 @@ static RISCVException read_time(CPURISCVState *env, int csrno, static RISCVException read_timeh(CPURISCVState *env, int csrno, target_ulong *val) { - uint64_t delta = riscv_cpu_virt_enabled(env) ? env->htimedelta : 0; + uint64_t delta = env->virt_enabled ? env->htimedelta : 0; if (!env->rdtime_fn) { return RISCV_EXCP_ILLEGAL_INST; @@ -579,20 +1041,118 @@ static RISCVException read_timeh(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -/* Machine constants */ +static RISCVException read_vstimecmp(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->vstimecmp; -#define M_MODE_INTERRUPTS ((uint64_t)(MIP_MSIP | MIP_MTIP | MIP_MEIP)) -#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP)) -#define VS_MODE_INTERRUPTS ((uint64_t)(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)) -#define HS_MODE_INTERRUPTS ((uint64_t)(MIP_SGEIP | VS_MODE_INTERRUPTS)) + return RISCV_EXCP_NONE; +} + +static RISCVException read_vstimecmph(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->vstimecmp >> 32; + + return RISCV_EXCP_NONE; +} + +static RISCVException write_vstimecmp(CPURISCVState *env, int csrno, + target_ulong val) +{ + if (riscv_cpu_mxl(env) == MXL_RV32) { + env->vstimecmp = deposit64(env->vstimecmp, 0, 32, (uint64_t)val); + } else { + env->vstimecmp = val; + } + + riscv_timer_write_timecmp(env, env->vstimer, env->vstimecmp, + env->htimedelta, MIP_VSTIP); + + return RISCV_EXCP_NONE; +} + +static RISCVException write_vstimecmph(CPURISCVState *env, int csrno, + target_ulong val) +{ + env->vstimecmp = deposit64(env->vstimecmp, 32, 32, (uint64_t)val); + riscv_timer_write_timecmp(env, env->vstimer, env->vstimecmp, + env->htimedelta, MIP_VSTIP); + + return RISCV_EXCP_NONE; +} + +static RISCVException read_stimecmp(CPURISCVState *env, int csrno, + target_ulong *val) +{ + if (env->virt_enabled) { + *val = env->vstimecmp; + } else { + *val = env->stimecmp; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException read_stimecmph(CPURISCVState *env, int csrno, + target_ulong *val) +{ + if (env->virt_enabled) { + *val = env->vstimecmp >> 32; + } else { + *val = env->stimecmp >> 32; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException write_stimecmp(CPURISCVState *env, int csrno, + target_ulong val) +{ + if (env->virt_enabled) { + if (env->hvictl & HVICTL_VTI) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } + return write_vstimecmp(env, csrno, val); + } + + if (riscv_cpu_mxl(env) == MXL_RV32) { + env->stimecmp = deposit64(env->stimecmp, 0, 32, (uint64_t)val); + } else { + env->stimecmp = val; + } + + riscv_timer_write_timecmp(env, env->stimer, env->stimecmp, 0, MIP_STIP); + + return RISCV_EXCP_NONE; +} + +static RISCVException write_stimecmph(CPURISCVState *env, int csrno, + target_ulong val) +{ + if (env->virt_enabled) { + if (env->hvictl & HVICTL_VTI) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } + return write_vstimecmph(env, csrno, val); + } + + env->stimecmp = deposit64(env->stimecmp, 32, 32, (uint64_t)val); + riscv_timer_write_timecmp(env, env->stimer, env->stimecmp, 0, MIP_STIP); + + return RISCV_EXCP_NONE; +} #define VSTOPI_NUM_SRCS 5 -static const uint64_t delegable_ints = S_MODE_INTERRUPTS | - VS_MODE_INTERRUPTS; -static const uint64_t vs_delegable_ints = VS_MODE_INTERRUPTS; +#define LOCAL_INTERRUPTS (~0x1FFF) + +static const uint64_t delegable_ints = + S_MODE_INTERRUPTS | VS_MODE_INTERRUPTS | MIP_LCOFIP; +static const uint64_t vs_delegable_ints = + (VS_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & ~MIP_LCOFIP; static const uint64_t all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS | - HS_MODE_INTERRUPTS; + HS_MODE_INTERRUPTS | LOCAL_INTERRUPTS; #define DELEGABLE_EXCPS ((1ULL << (RISCV_EXCP_INST_ADDR_MIS)) | \ (1ULL << (RISCV_EXCP_INST_ACCESS_FAULT)) | \ (1ULL << (RISCV_EXCP_ILLEGAL_INST)) | \ @@ -623,21 +1183,43 @@ static const target_ulong vs_delegable_excps = DELEGABLE_EXCPS & 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_VS; -static const target_ulong sip_writable_mask = SIP_SSIP | MIP_USIP | MIP_UEIP; -static const target_ulong hip_writable_mask = MIP_VSSIP; -static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | MIP_VSEIP; -static const target_ulong vsip_writable_mask = MIP_VSSIP; -static const char valid_vm_1_10_32[16] = { - [VM_1_10_MBARE] = 1, - [VM_1_10_SV32] = 1 +/* + * Spec allows for bits 13:63 to be either read-only or writable. + * So far we have interrupt LCOFIP in that region which is writable. + * + * Also, spec allows to inject virtual interrupts in this region even + * without any hardware interrupts for that interrupt number. + * + * For now interrupt in 13:63 region are all kept writable. 13 being + * LCOFIP and 14:63 being virtual only. Change this in future if we + * introduce more interrupts that are not writable. + */ + +/* Bit STIP can be an alias of mip.STIP that's why it's writable in mvip. */ +static const target_ulong mvip_writable_mask = MIP_SSIP | MIP_STIP | MIP_SEIP | + LOCAL_INTERRUPTS; +static const target_ulong mvien_writable_mask = MIP_SSIP | MIP_SEIP | + LOCAL_INTERRUPTS; + +static const target_ulong sip_writable_mask = SIP_SSIP | LOCAL_INTERRUPTS; +static const target_ulong hip_writable_mask = MIP_VSSIP; +static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | + MIP_VSEIP | LOCAL_INTERRUPTS; +static const target_ulong hvien_writable_mask = LOCAL_INTERRUPTS; + +static const target_ulong vsip_writable_mask = MIP_VSSIP | LOCAL_INTERRUPTS; + +const bool valid_vm_1_10_32[16] = { + [VM_1_10_MBARE] = true, + [VM_1_10_SV32] = true }; -static const char valid_vm_1_10_64[16] = { - [VM_1_10_MBARE] = 1, - [VM_1_10_SV39] = 1, - [VM_1_10_SV48] = 1, - [VM_1_10_SV57] = 1 +const bool valid_vm_1_10_64[16] = { + [VM_1_10_MBARE] = true, + [VM_1_10_SV39] = true, + [VM_1_10_SV48] = true, + [VM_1_10_SV57] = true }; /* Machine Information Registers */ @@ -657,30 +1239,21 @@ static RISCVException write_ignore(CPURISCVState *env, int csrno, static RISCVException read_mvendorid(CPURISCVState *env, int csrno, target_ulong *val) { - CPUState *cs = env_cpu(env); - RISCVCPU *cpu = RISCV_CPU(cs); - - *val = cpu->cfg.mvendorid; + *val = riscv_cpu_cfg(env)->mvendorid; return RISCV_EXCP_NONE; } static RISCVException read_marchid(CPURISCVState *env, int csrno, target_ulong *val) { - CPUState *cs = env_cpu(env); - RISCVCPU *cpu = RISCV_CPU(cs); - - *val = cpu->cfg.marchid; + *val = riscv_cpu_cfg(env)->marchid; return RISCV_EXCP_NONE; } static RISCVException read_mimpid(CPURISCVState *env, int csrno, target_ulong *val) { - CPUState *cs = env_cpu(env); - RISCVCPU *cpu = RISCV_CPU(cs); - - *val = cpu->cfg.mimpid; + *val = riscv_cpu_cfg(env)->mimpid; return RISCV_EXCP_NONE; } @@ -720,13 +1293,62 @@ static RISCVException read_mstatus(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static int validate_vm(CPURISCVState *env, target_ulong vm) +static bool validate_vm(CPURISCVState *env, target_ulong vm) { + uint64_t mode_supported = riscv_cpu_cfg(env)->satp_mode.map; + return get_field(mode_supported, (1 << vm)); +} + +static target_ulong legalize_xatp(CPURISCVState *env, target_ulong old_xatp, + target_ulong val) +{ + target_ulong mask; + bool vm; if (riscv_cpu_mxl(env) == MXL_RV32) { - return valid_vm_1_10_32[vm & 0xf]; + vm = validate_vm(env, get_field(val, SATP32_MODE)); + mask = (val ^ old_xatp) & (SATP32_MODE | SATP32_ASID | SATP32_PPN); } else { - return valid_vm_1_10_64[vm & 0xf]; + vm = validate_vm(env, get_field(val, SATP64_MODE)); + mask = (val ^ old_xatp) & (SATP64_MODE | SATP64_ASID | SATP64_PPN); } + + if (vm && mask) { + /* + * The ISA defines SATP.MODE=Bare as "no translation", but we still + * pass these through QEMU's TLB emulation as it improves + * performance. Flushing the TLB on SATP writes with paging + * enabled avoids leaking those invalid cached mappings. + */ + tlb_flush(env_cpu(env)); + return val; + } + return old_xatp; +} + +static target_ulong legalize_mpp(CPURISCVState *env, target_ulong old_mpp, + target_ulong val) +{ + bool valid = false; + target_ulong new_mpp = get_field(val, MSTATUS_MPP); + + switch (new_mpp) { + case PRV_M: + valid = true; + break; + case PRV_S: + valid = riscv_has_ext(env, RVS); + break; + case PRV_U: + valid = riscv_has_ext(env, RVU); + break; + } + + /* Remain field unchanged if new_mpp value is invalid */ + if (!valid) { + val = set_field(val, MSTATUS_MPP, old_mpp); + } + + return val; } static RISCVException write_mstatus(CPURISCVState *env, int csrno, @@ -736,26 +1358,32 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, uint64_t mask = 0; RISCVMXL xl = riscv_cpu_mxl(env); + /* + * MPP field have been made WARL since priv version 1.11. However, + * legalization for it will not break any software running on 1.10. + */ + val = legalize_mpp(env, get_field(mstatus, MSTATUS_MPP), val); + /* flush tlb on mstatus fields that affect VM */ - if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | MSTATUS_MPV | - MSTATUS_MPRV | MSTATUS_SUM)) { + if ((val ^ mstatus) & MSTATUS_MXR) { tlb_flush(env_cpu(env)); } mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | MSTATUS_SPP | MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_MPP | MSTATUS_MXR | MSTATUS_TVM | MSTATUS_TSR | - MSTATUS_TW | MSTATUS_VS; + MSTATUS_TW; if (riscv_has_ext(env, RVF)) { mask |= MSTATUS_FS; } + if (riscv_has_ext(env, RVV)) { + mask |= MSTATUS_VS; + } if (xl != MXL_RV32 || env->debugger) { - /* - * RV32: MPV and GVA are not in mstatus. The current plan is to - * add them to mstatush. For now, we just don't support it. - */ - mask |= MSTATUS_MPV | MSTATUS_GVA; + if (riscv_has_ext(env, RVH)) { + mask |= MSTATUS_MPV | MSTATUS_GVA; + } if ((val & MSTATUS64_UXL) != 0) { mask |= MSTATUS64_UXL; } @@ -763,13 +1391,17 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, mstatus = (mstatus & ~mask) | (val & mask); - if (xl > MXL_RV32) { - /* SXL field is for now read only */ - mstatus = set_field(mstatus, MSTATUS64_SXL, xl); - } env->mstatus = mstatus; - env->xl = cpu_recompute_xl(env); + /* + * Except in debug mode, UXL/SXL can only be modified by higher + * privilege mode. So xl will not be changed in normal mode. + */ + if (env->debugger) { + env->xl = cpu_recompute_xl(env); + } + + riscv_cpu_update_mask(env); return RISCV_EXCP_NONE; } @@ -784,11 +1416,7 @@ static RISCVException write_mstatush(CPURISCVState *env, int csrno, target_ulong val) { uint64_t valh = (uint64_t)val << 32; - uint64_t mask = MSTATUS_MPV | MSTATUS_GVA; - - if ((valh ^ env->mstatus) & (MSTATUS_MPV)) { - tlb_flush(env_cpu(env)); - } + uint64_t mask = riscv_has_ext(env, RVH) ? MSTATUS_MPV | MSTATUS_GVA : 0; env->mstatus = (env->mstatus & ~mask) | (valh & mask); @@ -798,7 +1426,8 @@ static RISCVException write_mstatush(CPURISCVState *env, int csrno, static RISCVException read_mstatus_i128(CPURISCVState *env, int csrno, Int128 *val) { - *val = int128_make128(env->mstatus, add_status_sd(MXL_RV128, env->mstatus)); + *val = int128_make128(env->mstatus, add_status_sd(MXL_RV128, + env->mstatus)); return RISCV_EXCP_NONE; } @@ -834,60 +1463,56 @@ static RISCVException read_misa(CPURISCVState *env, int csrno, static RISCVException write_misa(CPURISCVState *env, int csrno, target_ulong val) { - if (!riscv_feature(env, RISCV_FEATURE_MISA)) { + RISCVCPU *cpu = env_archcpu(env); + uint32_t orig_misa_ext = env->misa_ext; + Error *local_err = NULL; + + if (!riscv_cpu_cfg(env)->misa_w) { /* drop write to misa */ return RISCV_EXCP_NONE; } - /* 'I' or 'E' must be present */ - if (!(val & (RVI | RVE))) { - /* It is not, drop write to misa */ - return RISCV_EXCP_NONE; - } - - /* 'E' excludes all other extensions */ - if (val & RVE) { - /* when we support 'E' we can do "val = RVE;" however - * for now we just drop writes if 'E' is present. - */ - return RISCV_EXCP_NONE; - } - - /* - * misa.MXL writes are not supported by QEMU. - * Drop writes to those bits. - */ - /* Mask extensions that are not supported by this hart */ val &= env->misa_ext_mask; - /* Mask extensions that are not supported by QEMU */ - val &= (RVI | RVE | RVM | RVA | RVF | RVD | RVC | RVS | RVU | RVV); - - /* 'D' depends on 'F', so clear 'D' if 'F' is not present */ - if ((val & RVD) && !(val & RVF)) { - val &= ~RVD; - } - - /* Suppress 'C' if next instruction is not aligned + /* + * Suppress 'C' if next instruction is not aligned * TODO: this should check next_pc */ if ((val & RVC) && (GETPC() & ~3) != 0) { val &= ~RVC; } + /* Disable RVG if any of its dependencies are disabled */ + if (!(val & RVI && val & RVM && val & RVA && + val & RVF && val & RVD)) { + val &= ~RVG; + } + /* If nothing changed, do nothing. */ if (val == env->misa_ext) { return RISCV_EXCP_NONE; } - if (!(val & RVF)) { + env->misa_ext = val; + riscv_cpu_validate_set_extensions(cpu, &local_err); + if (local_err != NULL) { + /* Rollback on validation error */ + qemu_log_mask(LOG_GUEST_ERROR, "Unable to write MISA ext value " + "0x%x, keeping existing MISA ext 0x%x\n", + env->misa_ext, orig_misa_ext); + + env->misa_ext = orig_misa_ext; + + return RISCV_EXCP_NONE; + } + + if (!(env->misa_ext & RVF)) { env->mstatus &= ~MSTATUS_FS; } /* flush translation cache */ tb_flush(env_cpu(env)); - env->misa_ext = val; env->xl = riscv_cpu_mxl(env); return RISCV_EXCP_NONE; } @@ -970,7 +1595,7 @@ static RISCVException rmw_mie64(CPURISCVState *env, int csrno, env->mie = (env->mie & ~mask) | (new_val & mask); if (!riscv_has_ext(env, RVH)) { - env->mie &= ~((uint64_t)MIP_SGEIP); + env->mie &= ~((uint64_t)HS_MODE_INTERRUPTS); } return RISCV_EXCP_NONE; @@ -1007,7 +1632,54 @@ static RISCVException rmw_mieh(CPURISCVState *env, int csrno, return ret; } -static int read_mtopi(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException rmw_mvien64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) +{ + uint64_t mask = wr_mask & mvien_writable_mask; + + if (ret_val) { + *ret_val = env->mvien; + } + + env->mvien = (env->mvien & ~mask) | (new_val & mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException rmw_mvien(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_mvien64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; + } + + return ret; +} + +static RISCVException rmw_mvienh(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_mvien64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + + return ret; +} + +static RISCVException read_mtopi(CPURISCVState *env, int csrno, + target_ulong *val) { int irq; uint8_t iprio; @@ -1031,7 +1703,7 @@ static int read_mtopi(CPURISCVState *env, int csrno, target_ulong *val) static int aia_xlate_vs_csrno(CPURISCVState *env, int csrno) { - if (!riscv_cpu_virt_enabled(env)) { + if (!env->virt_enabled) { return csrno; } @@ -1040,14 +1712,6 @@ static int aia_xlate_vs_csrno(CPURISCVState *env, int csrno) return CSR_VSISELECT; case CSR_SIREG: return CSR_VSIREG; - case CSR_SSETEIPNUM: - return CSR_VSSETEIPNUM; - case CSR_SCLREIPNUM: - return CSR_VSCLREIPNUM; - case CSR_SSETEIENUM: - return CSR_VSSETEIENUM; - case CSR_SCLREIENUM: - return CSR_VSCLREIENUM; case CSR_STOPEI: return CSR_VSTOPEI; default: @@ -1055,8 +1719,9 @@ static int aia_xlate_vs_csrno(CPURISCVState *env, int csrno) }; } -static int rmw_xiselect(CPURISCVState *env, int csrno, target_ulong *val, - target_ulong new_val, target_ulong wr_mask) +static RISCVException rmw_xiselect(CPURISCVState *env, int csrno, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) { target_ulong *iselect; @@ -1135,10 +1800,11 @@ static int rmw_iprio(target_ulong xlen, return 0; } -static int rmw_xireg(CPURISCVState *env, int csrno, target_ulong *val, - target_ulong new_val, target_ulong wr_mask) +static RISCVException rmw_xireg(CPURISCVState *env, int csrno, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) { - bool virt; + bool virt, isel_reserved; uint8_t *iprio; int ret = -EINVAL; target_ulong priv, isel, vgein; @@ -1148,6 +1814,7 @@ static int rmw_xireg(CPURISCVState *env, int csrno, target_ulong *val, /* Decode register details from CSR number */ virt = false; + isel_reserved = false; switch (csrno) { case CSR_MIREG: iprio = env->miprio; @@ -1155,6 +1822,11 @@ static int rmw_xireg(CPURISCVState *env, int csrno, target_ulong *val, priv = PRV_M; break; case CSR_SIREG: + if (env->priv == PRV_S && env->mvien & MIP_SEIP && + env->siselect >= ISELECT_IMSIC_EIDELIVERY && + env->siselect <= ISELECT_IMSIC_EIE63) { + goto done; + } iprio = env->siprio; isel = env->siselect; priv = PRV_S; @@ -1192,136 +1864,21 @@ static int rmw_xireg(CPURISCVState *env, int csrno, target_ulong *val, riscv_cpu_mxl_bits(env)), val, new_val, wr_mask); } - } - -done: - if (ret) { - return (riscv_cpu_virt_enabled(env) && virt) ? - RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; - } - return RISCV_EXCP_NONE; -} - -static int rmw_xsetclreinum(CPURISCVState *env, int csrno, target_ulong *val, - target_ulong new_val, target_ulong wr_mask) -{ - int ret = -EINVAL; - bool set, pend, virt; - target_ulong priv, isel, vgein, xlen, nval, wmask; - - /* Translate CSR number for VS-mode */ - csrno = aia_xlate_vs_csrno(env, csrno); - - /* Decode register details from CSR number */ - virt = set = pend = false; - switch (csrno) { - case CSR_MSETEIPNUM: - priv = PRV_M; - set = true; - pend = true; - break; - case CSR_MCLREIPNUM: - priv = PRV_M; - pend = true; - break; - case CSR_MSETEIENUM: - priv = PRV_M; - set = true; - break; - case CSR_MCLREIENUM: - priv = PRV_M; - break; - case CSR_SSETEIPNUM: - priv = PRV_S; - set = true; - pend = true; - break; - case CSR_SCLREIPNUM: - priv = PRV_S; - pend = true; - break; - case CSR_SSETEIENUM: - priv = PRV_S; - set = true; - break; - case CSR_SCLREIENUM: - priv = PRV_S; - break; - case CSR_VSSETEIPNUM: - priv = PRV_S; - virt = true; - set = true; - pend = true; - break; - case CSR_VSCLREIPNUM: - priv = PRV_S; - virt = true; - pend = true; - break; - case CSR_VSSETEIENUM: - priv = PRV_S; - virt = true; - set = true; - break; - case CSR_VSCLREIENUM: - priv = PRV_S; - virt = true; - break; - default: - goto done; - }; - - /* IMSIC CSRs only available when machine implements IMSIC. */ - if (!env->aia_ireg_rmw_fn[priv]) { - goto done; - } - - /* Find the selected guest interrupt file */ - vgein = (virt) ? get_field(env->hstatus, HSTATUS_VGEIN) : 0; - - /* Selected guest interrupt file should be valid */ - if (virt && (!vgein || env->geilen < vgein)) { - goto done; - } - - /* Set/Clear CSRs always read zero */ - if (val) { - *val = 0; - } - - if (wr_mask) { - /* Get interrupt number */ - new_val &= wr_mask; - - /* Find target interrupt pending/enable register */ - xlen = riscv_cpu_mxl_bits(env); - isel = (new_val / xlen); - isel *= (xlen / IMSIC_EIPx_BITS); - isel += (pend) ? ISELECT_IMSIC_EIP0 : ISELECT_IMSIC_EIE0; - - /* Find the interrupt bit to be set/clear */ - wmask = ((target_ulong)1) << (new_val % xlen); - nval = (set) ? wmask : 0; - - /* Call machine specific IMSIC register emulation */ - ret = env->aia_ireg_rmw_fn[priv](env->aia_ireg_rmw_fn_arg[priv], - AIA_MAKE_IREG(isel, priv, virt, - vgein, xlen), - NULL, nval, wmask); } else { - ret = 0; + isel_reserved = true; } done: if (ret) { - return (riscv_cpu_virt_enabled(env) && virt) ? + return (env->virt_enabled && virt && !isel_reserved) ? RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; } return RISCV_EXCP_NONE; } -static int rmw_xtopei(CPURISCVState *env, int csrno, target_ulong *val, - target_ulong new_val, target_ulong wr_mask) +static RISCVException rmw_xtopei(CPURISCVState *env, int csrno, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) { bool virt; int ret = -EINVAL; @@ -1337,6 +1894,9 @@ static int rmw_xtopei(CPURISCVState *env, int csrno, target_ulong *val, priv = PRV_M; break; case CSR_STOPEI: + if (env->mvien & MIP_SEIP && env->priv == PRV_S) { + goto done; + } priv = PRV_S; break; case CSR_VSTOPEI: @@ -1368,7 +1928,7 @@ static int rmw_xtopei(CPURISCVState *env, int csrno, target_ulong *val, done: if (ret) { - return (riscv_cpu_virt_enabled(env) && virt) ? + return (env->virt_enabled && virt) ? RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; } return RISCV_EXCP_NONE; @@ -1393,6 +1953,35 @@ static RISCVException write_mtvec(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException read_mcountinhibit(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mcountinhibit; + return RISCV_EXCP_NONE; +} + +static RISCVException write_mcountinhibit(CPURISCVState *env, int csrno, + target_ulong val) +{ + int cidx; + PMUCTRState *counter; + RISCVCPU *cpu = env_archcpu(env); + + /* WARL register - disable unavailable counters; TM bit is always 0 */ + env->mcountinhibit = + val & (cpu->pmu_avail_ctrs | COUNTEREN_CY | COUNTEREN_IR); + + /* Check if any other counter is also monitoring cycles/instructions */ + for (cidx = 0; cidx < RV_MAX_MHPMCOUNTERS; cidx++) { + if (!get_field(env->mcountinhibit, BIT(cidx))) { + counter = &env->pmu_ctrs[cidx]; + counter->started = true; + } + } + + return RISCV_EXCP_NONE; +} + static RISCVException read_mcounteren(CPURISCVState *env, int csrno, target_ulong *val) { @@ -1403,7 +1992,11 @@ static RISCVException read_mcounteren(CPURISCVState *env, int csrno, static RISCVException write_mcounteren(CPURISCVState *env, int csrno, target_ulong val) { - env->mcounteren = val; + RISCVCPU *cpu = env_archcpu(env); + + /* WARL register - disable unavailable counters */ + env->mcounteren = val & (cpu->pmu_avail_ctrs | COUNTEREN_CY | COUNTEREN_TM | + COUNTEREN_IR); return RISCV_EXCP_NONE; } @@ -1438,28 +2031,28 @@ static RISCVException write_mscratch(CPURISCVState *env, int csrno, } static RISCVException read_mepc(CPURISCVState *env, int csrno, - target_ulong *val) + target_ulong *val) { *val = env->mepc; return RISCV_EXCP_NONE; } static RISCVException write_mepc(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val) { env->mepc = val; return RISCV_EXCP_NONE; } static RISCVException read_mcause(CPURISCVState *env, int csrno, - target_ulong *val) + target_ulong *val) { *val = env->mcause; return RISCV_EXCP_NONE; } static RISCVException write_mcause(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val) { env->mcause = val; return RISCV_EXCP_NONE; @@ -1481,19 +2074,22 @@ static RISCVException write_mtval(CPURISCVState *env, int csrno, /* Execution environment configuration setup */ static RISCVException read_menvcfg(CPURISCVState *env, int csrno, - target_ulong *val) + target_ulong *val) { *val = env->menvcfg; return RISCV_EXCP_NONE; } static RISCVException write_menvcfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val) { + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | MENVCFG_CBZE; if (riscv_cpu_mxl(env) == MXL_RV64) { - mask |= MENVCFG_PBMTE | MENVCFG_STCE; + mask |= (cfg->ext_svpbmt ? MENVCFG_PBMTE : 0) | + (cfg->ext_sstc ? MENVCFG_STCE : 0) | + (cfg->ext_svadu ? MENVCFG_ADUE : 0); } env->menvcfg = (env->menvcfg & ~mask) | (val & mask); @@ -1501,16 +2097,19 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, } static RISCVException read_menvcfgh(CPURISCVState *env, int csrno, - target_ulong *val) + target_ulong *val) { *val = env->menvcfg >> 32; return RISCV_EXCP_NONE; } static RISCVException write_menvcfgh(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val) { - uint64_t mask = MENVCFG_PBMTE | MENVCFG_STCE; + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); + uint64_t mask = (cfg->ext_svpbmt ? MENVCFG_PBMTE : 0) | + (cfg->ext_sstc ? MENVCFG_STCE : 0) | + (cfg->ext_svadu ? MENVCFG_ADUE : 0); uint64_t valh = (uint64_t)val << 32; env->menvcfg = (env->menvcfg & ~mask) | (valh & mask); @@ -1519,36 +2118,67 @@ static RISCVException write_menvcfgh(CPURISCVState *env, int csrno, } static RISCVException read_senvcfg(CPURISCVState *env, int csrno, - target_ulong *val) + target_ulong *val) { + RISCVException ret; + + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + *val = env->senvcfg; return RISCV_EXCP_NONE; } static RISCVException write_senvcfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val) { uint64_t mask = SENVCFG_FIOM | SENVCFG_CBIE | SENVCFG_CBCFE | SENVCFG_CBZE; + RISCVException ret; + + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG); + if (ret != RISCV_EXCP_NONE) { + return ret; + } env->senvcfg = (env->senvcfg & ~mask) | (val & mask); - return RISCV_EXCP_NONE; } static RISCVException read_henvcfg(CPURISCVState *env, int csrno, - target_ulong *val) + target_ulong *val) { - *val = env->henvcfg; + RISCVException ret; + + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + /* + * henvcfg.pbmte is read_only 0 when menvcfg.pbmte = 0 + * henvcfg.stce is read_only 0 when menvcfg.stce = 0 + * henvcfg.adue is read_only 0 when menvcfg.adue = 0 + */ + *val = env->henvcfg & (~(HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE) | + env->menvcfg); return RISCV_EXCP_NONE; } static RISCVException write_henvcfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val) { uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE; + RISCVException ret; + + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG); + if (ret != RISCV_EXCP_NONE) { + return ret; + } if (riscv_cpu_mxl(env) == MXL_RV64) { - mask |= HENVCFG_PBMTE | HENVCFG_STCE; + mask |= env->menvcfg & (HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE); } env->henvcfg = (env->henvcfg & ~mask) | (val & mask); @@ -1557,28 +2187,243 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno, } static RISCVException read_henvcfgh(CPURISCVState *env, int csrno, - target_ulong *val) + target_ulong *val) { - *val = env->henvcfg >> 32; + RISCVException ret; + + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + *val = (env->henvcfg & (~(HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE) | + env->menvcfg)) >> 32; return RISCV_EXCP_NONE; } static RISCVException write_henvcfgh(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val) { - uint64_t mask = HENVCFG_PBMTE | HENVCFG_STCE; + uint64_t mask = env->menvcfg & (HENVCFG_PBMTE | HENVCFG_STCE | + HENVCFG_ADUE); uint64_t valh = (uint64_t)val << 32; + RISCVException ret; + + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG); + if (ret != RISCV_EXCP_NONE) { + return ret; + } env->henvcfg = (env->henvcfg & ~mask) | (valh & mask); + return RISCV_EXCP_NONE; +} + +static RISCVException read_mstateen(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mstateen[csrno - CSR_MSTATEEN0]; return RISCV_EXCP_NONE; } +static RISCVException write_mstateen(CPURISCVState *env, int csrno, + uint64_t wr_mask, target_ulong new_val) +{ + uint64_t *reg; + + reg = &env->mstateen[csrno - CSR_MSTATEEN0]; + *reg = (*reg & ~wr_mask) | (new_val & wr_mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException write_mstateen0(CPURISCVState *env, int csrno, + target_ulong new_val) +{ + uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + if (!riscv_has_ext(env, RVF)) { + wr_mask |= SMSTATEEN0_FCSR; + } + + return write_mstateen(env, csrno, wr_mask, new_val); +} + +static RISCVException write_mstateen_1_3(CPURISCVState *env, int csrno, + target_ulong new_val) +{ + return write_mstateen(env, csrno, SMSTATEEN_STATEEN, new_val); +} + +static RISCVException read_mstateenh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mstateen[csrno - CSR_MSTATEEN0H] >> 32; + + return RISCV_EXCP_NONE; +} + +static RISCVException write_mstateenh(CPURISCVState *env, int csrno, + uint64_t wr_mask, target_ulong new_val) +{ + uint64_t *reg, val; + + reg = &env->mstateen[csrno - CSR_MSTATEEN0H]; + val = (uint64_t)new_val << 32; + val |= *reg & 0xFFFFFFFF; + *reg = (*reg & ~wr_mask) | (val & wr_mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException write_mstateen0h(CPURISCVState *env, int csrno, + target_ulong new_val) +{ + uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + + return write_mstateenh(env, csrno, wr_mask, new_val); +} + +static RISCVException write_mstateenh_1_3(CPURISCVState *env, int csrno, + target_ulong new_val) +{ + return write_mstateenh(env, csrno, SMSTATEEN_STATEEN, new_val); +} + +static RISCVException read_hstateen(CPURISCVState *env, int csrno, + target_ulong *val) +{ + int index = csrno - CSR_HSTATEEN0; + + *val = env->hstateen[index] & env->mstateen[index]; + + return RISCV_EXCP_NONE; +} + +static RISCVException write_hstateen(CPURISCVState *env, int csrno, + uint64_t mask, target_ulong new_val) +{ + int index = csrno - CSR_HSTATEEN0; + uint64_t *reg, wr_mask; + + reg = &env->hstateen[index]; + wr_mask = env->mstateen[index] & mask; + *reg = (*reg & ~wr_mask) | (new_val & wr_mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException write_hstateen0(CPURISCVState *env, int csrno, + target_ulong new_val) +{ + uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + + if (!riscv_has_ext(env, RVF)) { + wr_mask |= SMSTATEEN0_FCSR; + } + + return write_hstateen(env, csrno, wr_mask, new_val); +} + +static RISCVException write_hstateen_1_3(CPURISCVState *env, int csrno, + target_ulong new_val) +{ + return write_hstateen(env, csrno, SMSTATEEN_STATEEN, new_val); +} + +static RISCVException read_hstateenh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + int index = csrno - CSR_HSTATEEN0H; + + *val = (env->hstateen[index] >> 32) & (env->mstateen[index] >> 32); + + return RISCV_EXCP_NONE; +} + +static RISCVException write_hstateenh(CPURISCVState *env, int csrno, + uint64_t mask, target_ulong new_val) +{ + int index = csrno - CSR_HSTATEEN0H; + uint64_t *reg, wr_mask, val; + + reg = &env->hstateen[index]; + val = (uint64_t)new_val << 32; + val |= *reg & 0xFFFFFFFF; + wr_mask = env->mstateen[index] & mask; + *reg = (*reg & ~wr_mask) | (val & wr_mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException write_hstateen0h(CPURISCVState *env, int csrno, + target_ulong new_val) +{ + uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + + return write_hstateenh(env, csrno, wr_mask, new_val); +} + +static RISCVException write_hstateenh_1_3(CPURISCVState *env, int csrno, + target_ulong new_val) +{ + return write_hstateenh(env, csrno, SMSTATEEN_STATEEN, new_val); +} + +static RISCVException read_sstateen(CPURISCVState *env, int csrno, + target_ulong *val) +{ + bool virt = env->virt_enabled; + int index = csrno - CSR_SSTATEEN0; + + *val = env->sstateen[index] & env->mstateen[index]; + if (virt) { + *val &= env->hstateen[index]; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException write_sstateen(CPURISCVState *env, int csrno, + uint64_t mask, target_ulong new_val) +{ + bool virt = env->virt_enabled; + int index = csrno - CSR_SSTATEEN0; + uint64_t wr_mask; + uint64_t *reg; + + wr_mask = env->mstateen[index] & mask; + if (virt) { + wr_mask &= env->hstateen[index]; + } + + reg = &env->sstateen[index]; + *reg = (*reg & ~wr_mask) | (new_val & wr_mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException write_sstateen0(CPURISCVState *env, int csrno, + target_ulong new_val) +{ + uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + + if (!riscv_has_ext(env, RVF)) { + wr_mask |= SMSTATEEN0_FCSR; + } + + return write_sstateen(env, csrno, wr_mask, new_val); +} + +static RISCVException write_sstateen_1_3(CPURISCVState *env, int csrno, + target_ulong new_val) +{ + return write_sstateen(env, csrno, SMSTATEEN_STATEEN, new_val); +} + static RISCVException rmw_mip64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) { - RISCVCPU *cpu = env_archcpu(env); uint64_t old_mip, mask = wr_mask & delegable_ints; uint32_t gin; @@ -1587,8 +2432,14 @@ static RISCVException rmw_mip64(CPURISCVState *env, int csrno, new_val |= env->external_seip * MIP_SEIP; } + if (riscv_cpu_cfg(env)->ext_sstc && (env->priv == PRV_M) && + get_field(env->menvcfg, MENVCFG_STCE)) { + /* sstc extension forbids STIP & VSTIP to be writeable in mip */ + mask = mask & ~(MIP_STIP | MIP_VSTIP); + } + if (mask) { - old_mip = riscv_cpu_update_mip(cpu, mask, (new_val & mask)); + old_mip = riscv_cpu_update_mip(env, mask, (new_val & mask)); } else { old_mip = env->mip; } @@ -1596,6 +2447,7 @@ static RISCVException rmw_mip64(CPURISCVState *env, int csrno, if (csrno != CSR_HVIP) { gin = get_field(env->hstatus, HSTATUS_VGEIN); old_mip |= (env->hgeip & ((target_ulong)1 << gin)) ? MIP_VSEIP : 0; + old_mip |= env->vstime_irq ? MIP_VSTIP : 0; } if (ret_val) { @@ -1636,6 +2488,143 @@ static RISCVException rmw_miph(CPURISCVState *env, int csrno, return ret; } +/* + * The function is written for two use-cases: + * 1- To access mvip csr as is for m-mode access. + * 2- To access sip as a combination of mip and mvip for s-mode. + * + * Both report bits 1, 5, 9 and 13:63 but with the exception of + * STIP being read-only zero in case of mvip when sstc extension + * is present. + * Also, sip needs to be read-only zero when both mideleg[i] and + * mvien[i] are zero but mvip needs to be an alias of mip. + */ +static RISCVException rmw_mvip64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) +{ + RISCVCPU *cpu = env_archcpu(env); + target_ulong ret_mip = 0; + RISCVException ret; + uint64_t old_mvip; + + /* + * mideleg[i] mvien[i] + * 0 0 No delegation. mvip[i] is alias of mip[i]. + * 0 1 mvip[i] becomes source of interrupt, mip bypassed. + * 1 X mip[i] is source of interrupt and mvip[i] aliases + * mip[i]. + * + * So alias condition would be for bits: + * ((S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & (mideleg | ~mvien)) | + * (!sstc & MIP_STIP) + * + * Non-alias condition will be for bits: + * (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & (~mideleg & mvien) + * + * alias_mask denotes the bits that come from mip nalias_mask denotes bits + * that come from hvip. + */ + uint64_t alias_mask = ((S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & + (env->mideleg | ~env->mvien)) | MIP_STIP; + uint64_t nalias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & + (~env->mideleg & env->mvien); + uint64_t wr_mask_mvip; + uint64_t wr_mask_mip; + + /* + * mideleg[i] mvien[i] + * 0 0 sip[i] read-only zero. + * 0 1 sip[i] alias of mvip[i]. + * 1 X sip[i] alias of mip[i]. + * + * Both alias and non-alias mask remain same for sip except for bits + * which are zero in both mideleg and mvien. + */ + if (csrno == CSR_SIP) { + /* Remove bits that are zero in both mideleg and mvien. */ + alias_mask &= (env->mideleg | env->mvien); + nalias_mask &= (env->mideleg | env->mvien); + } + + /* + * If sstc is present, mvip.STIP is not an alias of mip.STIP so clear + * that our in mip returned value. + */ + if (cpu->cfg.ext_sstc && (env->priv == PRV_M) && + get_field(env->menvcfg, MENVCFG_STCE)) { + alias_mask &= ~MIP_STIP; + } + + wr_mask_mip = wr_mask & alias_mask & mvip_writable_mask; + wr_mask_mvip = wr_mask & nalias_mask & mvip_writable_mask; + + /* + * For bits set in alias_mask, mvip needs to be alias of mip, so forward + * this to rmw_mip. + */ + ret = rmw_mip(env, CSR_MIP, &ret_mip, new_val, wr_mask_mip); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + old_mvip = env->mvip; + + /* + * Write to mvip. Update only non-alias bits. Alias bits were updated + * in mip in rmw_mip above. + */ + if (wr_mask_mvip) { + env->mvip = (env->mvip & ~wr_mask_mvip) | (new_val & wr_mask_mvip); + + /* + * Given mvip is separate source from mip, we need to trigger interrupt + * from here separately. Normally this happen from riscv_cpu_update_mip. + */ + riscv_cpu_interrupt(env); + } + + if (ret_val) { + ret_mip &= alias_mask; + old_mvip &= nalias_mask; + + *ret_val = old_mvip | ret_mip; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException rmw_mvip(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_mvip64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; + } + + return ret; +} + +static RISCVException rmw_mviph(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_mvip64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + + return ret; +} + /* Supervisor Trap Setup */ static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno, Int128 *val) @@ -1680,23 +2669,36 @@ static RISCVException rmw_vsie64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) { + uint64_t alias_mask = (LOCAL_INTERRUPTS | VS_MODE_INTERRUPTS) & + env->hideleg; + uint64_t nalias_mask = LOCAL_INTERRUPTS & (~env->hideleg & env->hvien); + uint64_t rval, rval_vs, vsbits; + uint64_t wr_mask_vsie; + uint64_t wr_mask_mie; RISCVException ret; - uint64_t rval, vsbits, mask = env->hideleg & VS_MODE_INTERRUPTS; /* Bring VS-level bits to correct position */ vsbits = new_val & (VS_MODE_INTERRUPTS >> 1); new_val &= ~(VS_MODE_INTERRUPTS >> 1); new_val |= vsbits << 1; + vsbits = wr_mask & (VS_MODE_INTERRUPTS >> 1); wr_mask &= ~(VS_MODE_INTERRUPTS >> 1); wr_mask |= vsbits << 1; - ret = rmw_mie64(env, csrno, &rval, new_val, wr_mask & mask); + wr_mask_mie = wr_mask & alias_mask; + wr_mask_vsie = wr_mask & nalias_mask; + + ret = rmw_mie64(env, csrno, &rval, new_val, wr_mask_mie); + + rval_vs = env->vsie & nalias_mask; + env->vsie = (env->vsie & ~wr_mask_vsie) | (new_val & wr_mask_vsie); + if (ret_val) { - rval &= mask; + rval &= alias_mask; vsbits = rval & VS_MODE_INTERRUPTS; rval &= ~VS_MODE_INTERRUPTS; - *ret_val = rval | (vsbits >> 1); + *ret_val = rval | (vsbits >> 1) | rval_vs; } return ret; @@ -1737,20 +2739,37 @@ static RISCVException rmw_sie64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) { + uint64_t nalias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & + (~env->mideleg & env->mvien); + uint64_t alias_mask = (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS) & env->mideleg; + uint64_t sie_mask = wr_mask & nalias_mask; RISCVException ret; - uint64_t mask = env->mideleg & S_MODE_INTERRUPTS; - if (riscv_cpu_virt_enabled(env)) { + /* + * mideleg[i] mvien[i] + * 0 0 sie[i] read-only zero. + * 0 1 sie[i] is a separate writable bit. + * 1 X sie[i] alias of mie[i]. + * + * Both alias and non-alias mask remain same for sip except for bits + * which are zero in both mideleg and mvien. + */ + if (env->virt_enabled) { if (env->hvictl & HVICTL_VTI) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } ret = rmw_vsie64(env, CSR_VSIE, ret_val, new_val, wr_mask); + if (ret_val) { + *ret_val &= alias_mask; + } } else { - ret = rmw_mie64(env, csrno, ret_val, new_val, wr_mask & mask); - } + ret = rmw_mie64(env, csrno, ret_val, new_val, wr_mask & alias_mask); + if (ret_val) { + *ret_val &= alias_mask; + *ret_val |= env->sie & nalias_mask; + } - if (ret_val) { - *ret_val &= mask; + env->sie = (env->sie & ~sie_mask) | (new_val & sie_mask); } return ret; @@ -1892,12 +2911,20 @@ static RISCVException write_stval(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException rmw_hvip64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask); + static RISCVException rmw_vsip64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) { RISCVException ret; - uint64_t rval, vsbits, mask = env->hideleg & vsip_writable_mask; + uint64_t rval, mask = env->hideleg & VS_MODE_INTERRUPTS; + uint64_t vsbits; + + /* Add virtualized bits into vsip mask. */ + mask |= env->hvien & ~env->hideleg; /* Bring VS-level bits to correct position */ vsbits = new_val & (VS_MODE_INTERRUPTS >> 1); @@ -1907,7 +2934,8 @@ static RISCVException rmw_vsip64(CPURISCVState *env, int csrno, wr_mask &= ~(VS_MODE_INTERRUPTS >> 1); wr_mask |= vsbits << 1; - ret = rmw_mip64(env, csrno, &rval, new_val, wr_mask & mask); + ret = rmw_hvip64(env, csrno, &rval, new_val, + wr_mask & mask & vsip_writable_mask); if (ret_val) { rval &= mask; vsbits = rval & VS_MODE_INTERRUPTS; @@ -1954,19 +2982,20 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno, uint64_t new_val, uint64_t wr_mask) { RISCVException ret; - uint64_t mask = env->mideleg & sip_writable_mask; + uint64_t mask = (env->mideleg | env->mvien) & sip_writable_mask; - if (riscv_cpu_virt_enabled(env)) { + if (env->virt_enabled) { if (env->hvictl & HVICTL_VTI) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } ret = rmw_vsip64(env, CSR_VSIP, ret_val, new_val, wr_mask); } else { - ret = rmw_mip64(env, csrno, ret_val, new_val, wr_mask & mask); + ret = rmw_mvip64(env, csrno, ret_val, new_val, wr_mask & mask); } if (ret_val) { - *ret_val &= env->mideleg & S_MODE_INTERRUPTS; + *ret_val &= (env->mideleg | env->mvien) & + (S_MODE_INTERRUPTS | LOCAL_INTERRUPTS); } return ret; @@ -2007,55 +3036,27 @@ static RISCVException rmw_siph(CPURISCVState *env, int csrno, static RISCVException read_satp(CPURISCVState *env, int csrno, target_ulong *val) { - if (!riscv_feature(env, RISCV_FEATURE_MMU)) { + if (!riscv_cpu_cfg(env)->mmu) { *val = 0; return RISCV_EXCP_NONE; } - - if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) { - return RISCV_EXCP_ILLEGAL_INST; - } else { - *val = env->satp; - } - + *val = env->satp; return RISCV_EXCP_NONE; } static RISCVException write_satp(CPURISCVState *env, int csrno, target_ulong val) { - target_ulong vm, mask; - - if (!riscv_feature(env, RISCV_FEATURE_MMU)) { + if (!riscv_cpu_cfg(env)->mmu) { return RISCV_EXCP_NONE; } - if (riscv_cpu_mxl(env) == MXL_RV32) { - vm = validate_vm(env, get_field(val, SATP32_MODE)); - mask = (val ^ env->satp) & (SATP32_MODE | SATP32_ASID | SATP32_PPN); - } else { - vm = validate_vm(env, get_field(val, SATP64_MODE)); - mask = (val ^ env->satp) & (SATP64_MODE | SATP64_ASID | SATP64_PPN); - } - - if (vm && mask) { - if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) { - return RISCV_EXCP_ILLEGAL_INST; - } else { - /* - * The ISA defines SATP.MODE=Bare as "no translation", but we still - * pass these through QEMU's TLB emulation as it improves - * performance. Flushing the TLB on SATP writes with paging - * enabled avoids leaking those invalid cached mappings. - */ - tlb_flush(env_cpu(env)); - env->satp = val; - } - } + env->satp = legalize_xatp(env, env->satp, val); return RISCV_EXCP_NONE; } -static int read_vstopi(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_vstopi(CPURISCVState *env, int csrno, + target_ulong *val) { int irq, ret; target_ulong topei; @@ -2140,15 +3141,17 @@ static int read_vstopi(CPURISCVState *env, int csrno, target_ulong *val) *val = (iid & TOPI_IID_MASK) << TOPI_IID_SHIFT; *val |= iprio; + return RISCV_EXCP_NONE; } -static int read_stopi(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_stopi(CPURISCVState *env, int csrno, + target_ulong *val) { int irq; uint8_t iprio; - if (riscv_cpu_virt_enabled(env)) { + if (env->virt_enabled) { return read_vstopi(env, CSR_VSTOPI, val); } @@ -2188,7 +3191,8 @@ static RISCVException write_hstatus(CPURISCVState *env, int csrno, { env->hstatus = val; if (riscv_cpu_mxl(env) != MXL_RV32 && get_field(val, HSTATUS_VSXL) != 2) { - qemu_log_mask(LOG_UNIMP, "QEMU does not support mixed HSXLEN options."); + qemu_log_mask(LOG_UNIMP, + "QEMU does not support mixed HSXLEN options."); } if (get_field(val, HSTATUS_VSBE) != 0) { qemu_log_mask(LOG_UNIMP, "QEMU does not support big endian guests."); @@ -2210,6 +3214,52 @@ static RISCVException write_hedeleg(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException rmw_hvien64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) +{ + uint64_t mask = wr_mask & hvien_writable_mask; + + if (ret_val) { + *ret_val = env->hvien; + } + + env->hvien = (env->hvien & ~mask) | (new_val & mask); + + return RISCV_EXCP_NONE; +} + +static RISCVException rmw_hvien(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_hvien64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; + } + + return ret; +} + +static RISCVException rmw_hvienh(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_hvien64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + + return ret; +} + static RISCVException rmw_hideleg64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) @@ -2255,16 +3305,94 @@ static RISCVException rmw_hidelegh(CPURISCVState *env, int csrno, return ret; } +/* + * The function is written for two use-cases: + * 1- To access hvip csr as is for HS-mode access. + * 2- To access vsip as a combination of hvip, and mip for vs-mode. + * + * Both report bits 2, 6, 10 and 13:63. + * vsip needs to be read-only zero when both hideleg[i] and + * hvien[i] are zero. + */ static RISCVException rmw_hvip64(CPURISCVState *env, int csrno, uint64_t *ret_val, uint64_t new_val, uint64_t wr_mask) { RISCVException ret; + uint64_t old_hvip; + uint64_t ret_mip; + + /* + * For bits 10, 6 and 2, vsip[i] is an alias of hip[i]. These bits are + * present in hip, hvip and mip. Where mip[i] is alias of hip[i] and hvip[i] + * is OR'ed in hip[i] to inject virtual interrupts from hypervisor. These + * bits are actually being maintained in mip so we read them from there. + * This way we have a single source of truth and allows for easier + * implementation. + * + * For bits 13:63 we have: + * + * hideleg[i] hvien[i] + * 0 0 No delegation. vsip[i] readonly zero. + * 0 1 vsip[i] is alias of hvip[i], sip bypassed. + * 1 X vsip[i] is alias of sip[i], hvip bypassed. + * + * alias_mask denotes the bits that come from sip (mip here given we + * maintain all bits there). nalias_mask denotes bits that come from + * hvip. + */ + uint64_t alias_mask = (env->hideleg | ~env->hvien) | VS_MODE_INTERRUPTS; + uint64_t nalias_mask = (~env->hideleg & env->hvien); + uint64_t wr_mask_hvip; + uint64_t wr_mask_mip; + + /* + * Both alias and non-alias mask remain same for vsip except: + * 1- For VS* bits if they are zero in hideleg. + * 2- For 13:63 bits if they are zero in both hideleg and hvien. + */ + if (csrno == CSR_VSIP) { + /* zero-out VS* bits that are not delegated to VS mode. */ + alias_mask &= (env->hideleg | ~VS_MODE_INTERRUPTS); + + /* + * zero-out 13:63 bits that are zero in both hideleg and hvien. + * nalias_mask mask can not contain any VS* bits so only second + * condition applies on it. + */ + nalias_mask &= (env->hideleg | env->hvien); + alias_mask &= (env->hideleg | env->hvien); + } + + wr_mask_hvip = wr_mask & nalias_mask & hvip_writable_mask; + wr_mask_mip = wr_mask & alias_mask & hvip_writable_mask; + + /* Aliased bits, bits 10, 6, 2 need to come from mip. */ + ret = rmw_mip64(env, csrno, &ret_mip, new_val, wr_mask_mip); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + old_hvip = env->hvip; + + if (wr_mask_hvip) { + env->hvip = (env->hvip & ~wr_mask_hvip) | (new_val & wr_mask_hvip); + + /* + * Given hvip is separate source from mip, we need to trigger interrupt + * from here separately. Normally this happen from riscv_cpu_update_mip. + */ + riscv_cpu_interrupt(env); + } - ret = rmw_mip64(env, csrno, ret_val, new_val, - wr_mask & hvip_writable_mask); if (ret_val) { - *ret_val &= VS_MODE_INTERRUPTS; + /* Only take VS* bits from mip. */ + ret_mip &= alias_mask; + + /* Take in non-delegated 13:63 bits from hvip. */ + old_hvip &= nalias_mask; + + *ret_val = ret_mip | old_hvip; } return ret; @@ -2359,7 +3487,7 @@ static RISCVException write_hgeie(CPURISCVState *env, int csrno, val &= ((((target_ulong)1) << env->geilen) - 1) << 1; env->hgeie = val; /* Update mip.SGEIP bit */ - riscv_cpu_update_mip(env_archcpu(env), MIP_SGEIP, + riscv_cpu_update_mip(env, MIP_SGEIP, BOOL_TO_MASK(!!(env->hgeie & env->hgeip))); return RISCV_EXCP_NONE; } @@ -2410,7 +3538,7 @@ static RISCVException read_hgatp(CPURISCVState *env, int csrno, static RISCVException write_hgatp(CPURISCVState *env, int csrno, target_ulong val) { - env->hgatp = val; + env->hgatp = legalize_xatp(env, env->hgatp, val); return RISCV_EXCP_NONE; } @@ -2437,6 +3565,12 @@ static RISCVException write_htimedelta(CPURISCVState *env, int csrno, } else { env->htimedelta = val; } + + if (riscv_cpu_cfg(env)->ext_sstc && env->rdtime_fn) { + riscv_timer_write_timecmp(env, env->vstimer, env->vstimecmp, + env->htimedelta, MIP_VSTIP); + } + return RISCV_EXCP_NONE; } @@ -2459,29 +3593,37 @@ static RISCVException write_htimedeltah(CPURISCVState *env, int csrno, } env->htimedelta = deposit64(env->htimedelta, 32, 32, (uint64_t)val); + + if (riscv_cpu_cfg(env)->ext_sstc && env->rdtime_fn) { + riscv_timer_write_timecmp(env, env->vstimer, env->vstimecmp, + env->htimedelta, MIP_VSTIP); + } + return RISCV_EXCP_NONE; } -static int read_hvictl(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_hvictl(CPURISCVState *env, int csrno, + target_ulong *val) { *val = env->hvictl; return RISCV_EXCP_NONE; } -static int write_hvictl(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_hvictl(CPURISCVState *env, int csrno, + target_ulong val) { env->hvictl = val & HVICTL_VALID_MASK; return RISCV_EXCP_NONE; } -static int read_hvipriox(CPURISCVState *env, int first_index, +static RISCVException read_hvipriox(CPURISCVState *env, int first_index, uint8_t *iprio, target_ulong *val) { int i, irq, rdzero, num_irqs = 4 * (riscv_cpu_mxl_bits(env) / 32); /* First index has to be a multiple of number of irqs per register */ if (first_index % num_irqs) { - return (riscv_cpu_virt_enabled(env)) ? + return (env->virt_enabled) ? RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; } @@ -2500,18 +3642,18 @@ static int read_hvipriox(CPURISCVState *env, int first_index, return RISCV_EXCP_NONE; } -static int write_hvipriox(CPURISCVState *env, int first_index, +static RISCVException write_hvipriox(CPURISCVState *env, int first_index, uint8_t *iprio, target_ulong val) { int i, irq, rdzero, num_irqs = 4 * (riscv_cpu_mxl_bits(env) / 32); /* First index has to be a multiple of number of irqs per register */ if (first_index % num_irqs) { - return (riscv_cpu_virt_enabled(env)) ? + return (env->virt_enabled) ? RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; } - /* Fill-up priority arrary */ + /* Fill-up priority array */ for (i = 0; i < num_irqs; i++) { if (riscv_cpu_hviprio_index2irq(first_index + i, &irq, &rdzero)) { continue; @@ -2526,42 +3668,50 @@ static int write_hvipriox(CPURISCVState *env, int first_index, return RISCV_EXCP_NONE; } -static int read_hviprio1(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_hviprio1(CPURISCVState *env, int csrno, + target_ulong *val) { return read_hvipriox(env, 0, env->hviprio, val); } -static int write_hviprio1(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_hviprio1(CPURISCVState *env, int csrno, + target_ulong val) { return write_hvipriox(env, 0, env->hviprio, val); } -static int read_hviprio1h(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_hviprio1h(CPURISCVState *env, int csrno, + target_ulong *val) { return read_hvipriox(env, 4, env->hviprio, val); } -static int write_hviprio1h(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_hviprio1h(CPURISCVState *env, int csrno, + target_ulong val) { return write_hvipriox(env, 4, env->hviprio, val); } -static int read_hviprio2(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_hviprio2(CPURISCVState *env, int csrno, + target_ulong *val) { return read_hvipriox(env, 8, env->hviprio, val); } -static int write_hviprio2(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_hviprio2(CPURISCVState *env, int csrno, + target_ulong val) { return write_hvipriox(env, 8, env->hviprio, val); } -static int read_hviprio2h(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_hviprio2h(CPURISCVState *env, int csrno, + target_ulong *val) { return read_hvipriox(env, 12, env->hviprio, val); } -static int write_hviprio2h(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_hviprio2h(CPURISCVState *env, int csrno, + target_ulong val) { return write_hvipriox(env, 12, env->hviprio, val); } @@ -2585,7 +3735,8 @@ static RISCVException write_vsstatus(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static int read_vstvec(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_vstvec(CPURISCVState *env, int csrno, + target_ulong *val) { *val = env->vstvec; return RISCV_EXCP_NONE; @@ -2664,7 +3815,7 @@ static RISCVException read_vsatp(CPURISCVState *env, int csrno, static RISCVException write_vsatp(CPURISCVState *env, int csrno, target_ulong val) { - env->vsatp = val; + env->vsatp = legalize_xatp(env, env->vsatp, val); return RISCV_EXCP_NONE; } @@ -2705,30 +3856,18 @@ static RISCVException read_mseccfg(CPURISCVState *env, int csrno, } static RISCVException write_mseccfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val) { mseccfg_csr_write(env, val); return RISCV_EXCP_NONE; } -static bool check_pmp_reg_index(CPURISCVState *env, uint32_t reg_index) -{ - /* TODO: RV128 restriction check */ - if ((reg_index & 1) && (riscv_cpu_mxl(env) == MXL_RV64)) { - return false; - } - return true; -} - static RISCVException read_pmpcfg(CPURISCVState *env, int csrno, target_ulong *val) { uint32_t reg_index = csrno - CSR_PMPCFG0; - if (!check_pmp_reg_index(env, reg_index)) { - return RISCV_EXCP_ILLEGAL_INST; - } - *val = pmpcfg_csr_read(env, csrno - CSR_PMPCFG0); + *val = pmpcfg_csr_read(env, reg_index); return RISCV_EXCP_NONE; } @@ -2737,10 +3876,7 @@ static RISCVException write_pmpcfg(CPURISCVState *env, int csrno, { uint32_t reg_index = csrno - CSR_PMPCFG0; - if (!check_pmp_reg_index(env, reg_index)) { - return RISCV_EXCP_ILLEGAL_INST; - } - pmpcfg_csr_write(env, csrno - CSR_PMPCFG0, val); + pmpcfg_csr_write(env, reg_index, val); return RISCV_EXCP_NONE; } @@ -2776,7 +3912,7 @@ static RISCVException read_tdata(CPURISCVState *env, int csrno, target_ulong *val) { /* return 0 in tdata1 to end the trigger enumeration */ - if (env->trigger_cur >= TRIGGER_NUM && csrno == CSR_TDATA1) { + if (env->trigger_cur >= RV_MAX_TRIGGERS && csrno == CSR_TDATA1) { *val = 0; return RISCV_EXCP_NONE; } @@ -2800,6 +3936,38 @@ static RISCVException write_tdata(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException read_tinfo(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = tinfo_csr_read(env); + return RISCV_EXCP_NONE; +} + +static RISCVException read_mcontext(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mcontext; + return RISCV_EXCP_NONE; +} + +static RISCVException write_mcontext(CPURISCVState *env, int csrno, + target_ulong val) +{ + bool rv32 = riscv_cpu_mxl(env) == MXL_RV32 ? true : false; + int32_t mask; + + if (riscv_has_ext(env, RVH)) { + /* Spec suggest 7-bit for RV32 and 14-bit for RV64 w/ H extension */ + mask = rv32 ? MCONTEXT32_HCONTEXT : MCONTEXT64_HCONTEXT; + } else { + /* Spec suggest 6-bit for RV32 and 13-bit for RV64 w/o H extension */ + mask = rv32 ? MCONTEXT32 : MCONTEXT64; + } + + env->mcontext = val & mask; + return RISCV_EXCP_NONE; +} + /* * Functions to access Pointer Masking feature registers * We have to check if current priv lvl could modify @@ -2851,16 +4019,16 @@ static RISCVException write_mmte(CPURISCVState *env, int csrno, target_ulong wpri_val = val & MMTE_MASK; if (val != wpri_val) { - qemu_log_mask(LOG_GUEST_ERROR, "%s" TARGET_FMT_lx " %s" TARGET_FMT_lx "\n", - "MMTE: WPRI violation written 0x", val, - "vs expected 0x", wpri_val); + qemu_log_mask(LOG_GUEST_ERROR, "%s" TARGET_FMT_lx " %s" + TARGET_FMT_lx "\n", "MMTE: WPRI violation written 0x", + val, "vs expected 0x", wpri_val); } /* for machine mode pm.current is hardwired to 1 */ wpri_val |= MMTE_M_PM_CURRENT; /* hardwiring pm.instruction bit to 0, since it's not supported yet */ wpri_val &= ~(MMTE_M_PM_INSN | MMTE_S_PM_INSN | MMTE_U_PM_INSN); - env->mmte = wpri_val | PM_EXT_DIRTY; + env->mmte = wpri_val | EXT_STATUS_DIRTY; riscv_cpu_update_mask(env); /* Set XS and SD bits, since PM CSRs are dirty */ @@ -2882,9 +4050,9 @@ static RISCVException write_smte(CPURISCVState *env, int csrno, target_ulong wpri_val = val & SMTE_MASK; if (val != wpri_val) { - qemu_log_mask(LOG_GUEST_ERROR, "%s" TARGET_FMT_lx " %s" TARGET_FMT_lx "\n", - "SMTE: WPRI violation written 0x", val, - "vs expected 0x", wpri_val); + qemu_log_mask(LOG_GUEST_ERROR, "%s" TARGET_FMT_lx " %s" + TARGET_FMT_lx "\n", "SMTE: WPRI violation written 0x", + val, "vs expected 0x", wpri_val); } /* if pm.current==0 we can't modify current PM CSRs */ @@ -2910,9 +4078,9 @@ static RISCVException write_umte(CPURISCVState *env, int csrno, target_ulong wpri_val = val & UMTE_MASK; if (val != wpri_val) { - qemu_log_mask(LOG_GUEST_ERROR, "%s" TARGET_FMT_lx " %s" TARGET_FMT_lx "\n", - "UMTE: WPRI violation written 0x", val, - "vs expected 0x", wpri_val); + qemu_log_mask(LOG_GUEST_ERROR, "%s" TARGET_FMT_lx " %s" + TARGET_FMT_lx "\n", "UMTE: WPRI violation written 0x", + val, "vs expected 0x", wpri_val); } if (check_pm_current_disabled(env, csrno)) { @@ -2937,10 +4105,10 @@ static RISCVException write_mpmmask(CPURISCVState *env, int csrno, uint64_t mstatus; env->mpmmask = val; - if ((env->priv == PRV_M) && (env->mmte & M_PM_ENABLE)) { + if ((cpu_address_mode(env) == PRV_M) && (env->mmte & M_PM_ENABLE)) { env->cur_pmmask = val; } - env->mmte |= PM_EXT_DIRTY; + env->mmte |= EXT_STATUS_DIRTY; /* Set XS and SD bits, since PM CSRs are dirty */ mstatus = env->mstatus | MSTATUS_XS; @@ -2965,10 +4133,13 @@ static RISCVException write_spmmask(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } env->spmmask = val; - if ((env->priv == PRV_S) && (env->mmte & S_PM_ENABLE)) { + if ((cpu_address_mode(env) == PRV_S) && (env->mmte & S_PM_ENABLE)) { env->cur_pmmask = val; + if (cpu_get_xl(env, PRV_S) == MXL_RV32) { + env->cur_pmmask &= UINT32_MAX; + } } - env->mmte |= PM_EXT_DIRTY; + env->mmte |= EXT_STATUS_DIRTY; /* Set XS and SD bits, since PM CSRs are dirty */ mstatus = env->mstatus | MSTATUS_XS; @@ -2993,10 +4164,13 @@ static RISCVException write_upmmask(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } env->upmmask = val; - if ((env->priv == PRV_U) && (env->mmte & U_PM_ENABLE)) { + if ((cpu_address_mode(env) == PRV_U) && (env->mmte & U_PM_ENABLE)) { env->cur_pmmask = val; + if (cpu_get_xl(env, PRV_U) == MXL_RV32) { + env->cur_pmmask &= UINT32_MAX; + } } - env->mmte |= PM_EXT_DIRTY; + env->mmte |= EXT_STATUS_DIRTY; /* Set XS and SD bits, since PM CSRs are dirty */ mstatus = env->mstatus | MSTATUS_XS; @@ -3017,10 +4191,10 @@ static RISCVException write_mpmbase(CPURISCVState *env, int csrno, uint64_t mstatus; env->mpmbase = val; - if ((env->priv == PRV_M) && (env->mmte & M_PM_ENABLE)) { + if ((cpu_address_mode(env) == PRV_M) && (env->mmte & M_PM_ENABLE)) { env->cur_pmbase = val; } - env->mmte |= PM_EXT_DIRTY; + env->mmte |= EXT_STATUS_DIRTY; /* Set XS and SD bits, since PM CSRs are dirty */ mstatus = env->mstatus | MSTATUS_XS; @@ -3045,10 +4219,13 @@ static RISCVException write_spmbase(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } env->spmbase = val; - if ((env->priv == PRV_S) && (env->mmte & S_PM_ENABLE)) { + if ((cpu_address_mode(env) == PRV_S) && (env->mmte & S_PM_ENABLE)) { env->cur_pmbase = val; + if (cpu_get_xl(env, PRV_S) == MXL_RV32) { + env->cur_pmbase &= UINT32_MAX; + } } - env->mmte |= PM_EXT_DIRTY; + env->mmte |= EXT_STATUS_DIRTY; /* Set XS and SD bits, since PM CSRs are dirty */ mstatus = env->mstatus | MSTATUS_XS; @@ -3073,10 +4250,13 @@ static RISCVException write_upmbase(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } env->upmbase = val; - if ((env->priv == PRV_U) && (env->mmte & U_PM_ENABLE)) { + if ((cpu_address_mode(env) == PRV_U) && (env->mmte & U_PM_ENABLE)) { env->cur_pmbase = val; + if (cpu_get_xl(env, PRV_U) == MXL_RV32) { + env->cur_pmbase &= UINT32_MAX; + } } - env->mmte |= PM_EXT_DIRTY; + env->mmte |= EXT_STATUS_DIRTY; /* Set XS and SD bits, since PM CSRs are dirty */ mstatus = env->mstatus | MSTATUS_XS; @@ -3132,53 +4312,65 @@ static RISCVException rmw_seed(CPURISCVState *env, int csrno, static inline RISCVException riscv_csrrw_check(CPURISCVState *env, int csrno, - bool write_mask, - RISCVCPU *cpu) + bool write_mask) { /* check privileges and return RISCV_EXCP_ILLEGAL_INST if check fails */ - int read_only = get_field(csrno, 0xC00) == 3; + bool read_only = get_field(csrno, 0xC00) == 3; int csr_min_priv = csr_ops[csrno].min_priv_ver; + + /* ensure the CSR extension is enabled */ + if (!riscv_cpu_cfg(env)->ext_zicsr) { + return RISCV_EXCP_ILLEGAL_INST; + } + + /* ensure CSR is implemented by checking predicate */ + if (!csr_ops[csrno].predicate) { + return RISCV_EXCP_ILLEGAL_INST; + } + + /* privileged spec version check */ + if (env->priv_ver < csr_min_priv) { + return RISCV_EXCP_ILLEGAL_INST; + } + + /* read / write check */ + if (write_mask && read_only) { + return RISCV_EXCP_ILLEGAL_INST; + } + + /* + * The predicate() not only does existence check but also does some + * access control check which triggers for example virtual instruction + * exception in some cases. When writing read-only CSRs in those cases + * illegal instruction exception should be triggered instead of virtual + * instruction exception. Hence this comes after the read / write check. + */ + RISCVException ret = csr_ops[csrno].predicate(env, csrno); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + #if !defined(CONFIG_USER_ONLY) int csr_priv, effective_priv = env->priv; - if (riscv_has_ext(env, RVH) && env->priv == PRV_S) { + if (riscv_has_ext(env, RVH) && env->priv == PRV_S && + !env->virt_enabled) { /* - * We are in either HS or VS mode. - * Add 1 to the effective privledge level to allow us to access the - * Hypervisor CSRs. The `hmode` predicate will determine if access - * should be allowed(HS) or if a virtual instruction exception should be - * raised(VS). + * We are in HS mode. Add 1 to the effective privilege level to + * allow us to access the Hypervisor CSRs. */ effective_priv++; } csr_priv = get_field(csrno, 0x300); if (!env->debugger && (effective_priv < csr_priv)) { - if (csr_priv == (PRV_S + 1) && riscv_cpu_virt_enabled(env)) { + if (csr_priv == (PRV_S + 1) && env->virt_enabled) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } return RISCV_EXCP_ILLEGAL_INST; } #endif - if (write_mask && read_only) { - return RISCV_EXCP_ILLEGAL_INST; - } - - /* ensure the CSR extension is enabled. */ - if (!cpu->cfg.ext_icsr) { - return RISCV_EXCP_ILLEGAL_INST; - } - - /* check predicate */ - if (!csr_ops[csrno].predicate) { - return RISCV_EXCP_ILLEGAL_INST; - } - - if (env->priv_ver < csr_min_priv) { - return RISCV_EXCP_ILLEGAL_INST; - } - - return csr_ops[csrno].predicate(env, csrno); + return RISCV_EXCP_NONE; } static RISCVException riscv_csrrw_do64(CPURISCVState *env, int csrno, @@ -3187,21 +4379,27 @@ static RISCVException riscv_csrrw_do64(CPURISCVState *env, int csrno, target_ulong write_mask) { RISCVException ret; - target_ulong old_value; + target_ulong old_value = 0; /* execute combined read/write operation if it exists */ if (csr_ops[csrno].op) { return csr_ops[csrno].op(env, csrno, ret_value, new_value, write_mask); } - /* if no accessor exists then return failure */ - if (!csr_ops[csrno].read) { - return RISCV_EXCP_ILLEGAL_INST; - } - /* read old value */ - ret = csr_ops[csrno].read(env, csrno, &old_value); - if (ret != RISCV_EXCP_NONE) { - return ret; + /* + * ret_value == NULL means that rd=x0 and we're coming from helper_csrw() + * and we can't throw side effects caused by CSR reads. + */ + if (ret_value) { + /* if no accessor exists then return failure */ + if (!csr_ops[csrno].read) { + return RISCV_EXCP_ILLEGAL_INST; + } + /* read old value */ + ret = csr_ops[csrno].read(env, csrno, &old_value); + if (ret != RISCV_EXCP_NONE) { + return ret; + } } /* write value if writable and write mask set, otherwise drop writes */ @@ -3227,9 +4425,7 @@ RISCVException riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, target_ulong write_mask) { - RISCVCPU *cpu = env_archcpu(env); - - RISCVException ret = riscv_csrrw_check(env, csrno, write_mask, cpu); + RISCVException ret = riscv_csrrw_check(env, csrno, write_mask); if (ret != RISCV_EXCP_NONE) { return ret; } @@ -3282,9 +4478,8 @@ RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, Int128 new_value, Int128 write_mask) { RISCVException ret; - RISCVCPU *cpu = env_archcpu(env); - ret = riscv_csrrw_check(env, csrno, int128_nz(write_mask), cpu); + ret = riscv_csrrw_check(env, csrno, int128_nz(write_mask)); if (ret != RISCV_EXCP_NONE) { return ret; } @@ -3297,7 +4492,8 @@ RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, * Fall back to 64-bit version for now, if the 128-bit alternative isn't * at all defined. * Note, some CSRs don't need to extend to MXLEN (64 upper bits non - * significant), for those, this fallback is correctly handling the accesses + * significant), for those, this fallback is correctly handling the + * accesses */ target_ulong old_value; ret = riscv_csrrw_do64(env, csrno, &old_value, @@ -3329,32 +4525,42 @@ RISCVException riscv_csrrw_debug(CPURISCVState *env, int csrno, return ret; } -/* Control and Status Register function table */ +static RISCVException read_jvt(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->jvt; + return RISCV_EXCP_NONE; +} + +static RISCVException write_jvt(CPURISCVState *env, int csrno, + target_ulong val) +{ + env->jvt = val; + return RISCV_EXCP_NONE; +} + +/* + * Control and Status Register function table + * riscv_csr_operations::predicate() must be provided for an implemented CSR + */ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* User Floating-Point CSRs */ [CSR_FFLAGS] = { "fflags", fs, read_fflags, write_fflags }, [CSR_FRM] = { "frm", fs, read_frm, write_frm }, [CSR_FCSR] = { "fcsr", fs, read_fcsr, write_fcsr }, /* Vector CSRs */ - [CSR_VSTART] = { "vstart", vs, read_vstart, write_vstart, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VXSAT] = { "vxsat", vs, read_vxsat, write_vxsat, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VXRM] = { "vxrm", vs, read_vxrm, write_vxrm, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VCSR] = { "vcsr", vs, read_vcsr, write_vcsr, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VL] = { "vl", vs, read_vl, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VTYPE] = { "vtype", vs, read_vtype, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VLENB] = { "vlenb", vs, read_vlenb, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSTART] = { "vstart", vs, read_vstart, write_vstart }, + [CSR_VXSAT] = { "vxsat", vs, read_vxsat, write_vxsat }, + [CSR_VXRM] = { "vxrm", vs, read_vxrm, write_vxrm }, + [CSR_VCSR] = { "vcsr", vs, read_vcsr, write_vcsr }, + [CSR_VL] = { "vl", vs, read_vl }, + [CSR_VTYPE] = { "vtype", vs, read_vtype }, + [CSR_VLENB] = { "vlenb", vs, read_vlenb }, /* User Timers and Counters */ - [CSR_CYCLE] = { "cycle", ctr, read_instret }, - [CSR_INSTRET] = { "instret", ctr, read_instret }, - [CSR_CYCLEH] = { "cycleh", ctr32, read_instreth }, - [CSR_INSTRETH] = { "instreth", ctr32, read_instreth }, + [CSR_CYCLE] = { "cycle", ctr, read_hpmcounter }, + [CSR_INSTRET] = { "instret", ctr, read_hpmcounter }, + [CSR_CYCLEH] = { "cycleh", ctr32, read_hpmcounterh }, + [CSR_INSTRETH] = { "instreth", ctr32, read_hpmcounterh }, /* * In privileged mode, the monitor will have to emulate TIME CSRs only if @@ -3366,12 +4572,19 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* Crypto Extension */ [CSR_SEED] = { "seed", seed, NULL, NULL, rmw_seed }, + /* Zcmt Extension */ + [CSR_JVT] = {"jvt", zcmt, read_jvt, write_jvt}, + #if !defined(CONFIG_USER_ONLY) /* Machine Timers and Counters */ - [CSR_MCYCLE] = { "mcycle", any, read_instret }, - [CSR_MINSTRET] = { "minstret", any, read_instret }, - [CSR_MCYCLEH] = { "mcycleh", any32, read_instreth }, - [CSR_MINSTRETH] = { "minstreth", any32, read_instreth }, + [CSR_MCYCLE] = { "mcycle", any, read_hpmcounter, + write_mhpmcounter }, + [CSR_MINSTRET] = { "minstret", any, read_hpmcounter, + write_mhpmcounter }, + [CSR_MCYCLEH] = { "mcycleh", any32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MINSTRETH] = { "minstreth", any32, read_hpmcounterh, + write_mhpmcounterh }, /* Machine Information Registers */ [CSR_MVENDORID] = { "mvendorid", any, read_mvendorid }, @@ -3380,23 +4593,25 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MHARTID] = { "mhartid", any, read_mhartid }, [CSR_MCONFIGPTR] = { "mconfigptr", any, read_zero, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Machine Trap Setup */ - [CSR_MSTATUS] = { "mstatus", any, read_mstatus, write_mstatus, NULL, - read_mstatus_i128 }, - [CSR_MISA] = { "misa", any, read_misa, write_misa, NULL, - read_misa_i128 }, - [CSR_MIDELEG] = { "mideleg", any, NULL, NULL, rmw_mideleg }, - [CSR_MEDELEG] = { "medeleg", any, read_medeleg, write_medeleg }, - [CSR_MIE] = { "mie", any, NULL, NULL, rmw_mie }, - [CSR_MTVEC] = { "mtvec", any, read_mtvec, write_mtvec }, - [CSR_MCOUNTEREN] = { "mcounteren", any, read_mcounteren, write_mcounteren }, + [CSR_MSTATUS] = { "mstatus", any, read_mstatus, write_mstatus, + NULL, read_mstatus_i128 }, + [CSR_MISA] = { "misa", any, read_misa, write_misa, + NULL, read_misa_i128 }, + [CSR_MIDELEG] = { "mideleg", any, NULL, NULL, rmw_mideleg }, + [CSR_MEDELEG] = { "medeleg", any, read_medeleg, write_medeleg }, + [CSR_MIE] = { "mie", any, NULL, NULL, rmw_mie }, + [CSR_MTVEC] = { "mtvec", any, read_mtvec, write_mtvec }, + [CSR_MCOUNTEREN] = { "mcounteren", umode, read_mcounteren, + write_mcounteren }, - [CSR_MSTATUSH] = { "mstatush", any32, read_mstatush, write_mstatush }, + [CSR_MSTATUSH] = { "mstatush", any32, read_mstatush, + write_mstatush }, /* Machine Trap Handling */ - [CSR_MSCRATCH] = { "mscratch", any, read_mscratch, write_mscratch, NULL, - read_mscratch_i128, write_mscratch_i128 }, + [CSR_MSCRATCH] = { "mscratch", any, read_mscratch, write_mscratch, + NULL, read_mscratch_i128, write_mscratch_i128 }, [CSR_MEPC] = { "mepc", any, read_mepc, write_mepc }, [CSR_MCAUSE] = { "mcause", any, read_mcause, write_mcause }, [CSR_MTVAL] = { "mtval", any, read_mtval, write_mtval }, @@ -3407,161 +4622,224 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MIREG] = { "mireg", aia_any, NULL, NULL, rmw_xireg }, /* Machine-Level Interrupts (AIA) */ - [CSR_MTOPI] = { "mtopi", aia_any, read_mtopi }, - - /* Machine-Level IMSIC Interface (AIA) */ - [CSR_MSETEIPNUM] = { "mseteipnum", aia_any, NULL, NULL, rmw_xsetclreinum }, - [CSR_MCLREIPNUM] = { "mclreipnum", aia_any, NULL, NULL, rmw_xsetclreinum }, - [CSR_MSETEIENUM] = { "mseteienum", aia_any, NULL, NULL, rmw_xsetclreinum }, - [CSR_MCLREIENUM] = { "mclreienum", aia_any, NULL, NULL, rmw_xsetclreinum }, - [CSR_MTOPEI] = { "mtopei", aia_any, NULL, NULL, rmw_xtopei }, + [CSR_MTOPEI] = { "mtopei", aia_any, NULL, NULL, rmw_xtopei }, + [CSR_MTOPI] = { "mtopi", aia_any, read_mtopi }, /* Virtual Interrupts for Supervisor Level (AIA) */ - [CSR_MVIEN] = { "mvien", aia_any, read_zero, write_ignore }, - [CSR_MVIP] = { "mvip", aia_any, read_zero, write_ignore }, + [CSR_MVIEN] = { "mvien", aia_any, NULL, NULL, rmw_mvien }, + [CSR_MVIP] = { "mvip", aia_any, NULL, NULL, rmw_mvip }, /* Machine-Level High-Half CSRs (AIA) */ [CSR_MIDELEGH] = { "midelegh", aia_any32, NULL, NULL, rmw_midelegh }, [CSR_MIEH] = { "mieh", aia_any32, NULL, NULL, rmw_mieh }, - [CSR_MVIENH] = { "mvienh", aia_any32, read_zero, write_ignore }, - [CSR_MVIPH] = { "mviph", aia_any32, read_zero, write_ignore }, + [CSR_MVIENH] = { "mvienh", aia_any32, NULL, NULL, rmw_mvienh }, + [CSR_MVIPH] = { "mviph", aia_any32, NULL, NULL, rmw_mviph }, [CSR_MIPH] = { "miph", aia_any32, NULL, NULL, rmw_miph }, /* Execution environment configuration */ - [CSR_MENVCFG] = { "menvcfg", any, read_menvcfg, write_menvcfg, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MENVCFGH] = { "menvcfgh", any32, read_menvcfgh, write_menvcfgh, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MENVCFG] = { "menvcfg", umode, read_menvcfg, write_menvcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MENVCFGH] = { "menvcfgh", umode32, read_menvcfgh, write_menvcfgh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_SENVCFG] = { "senvcfg", smode, read_senvcfg, write_senvcfg, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_HENVCFG] = { "henvcfg", hmode, read_henvcfg, write_henvcfg, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_HENVCFGH] = { "henvcfgh", hmode32, read_henvcfgh, write_henvcfgh, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + + /* Smstateen extension CSRs */ + [CSR_MSTATEEN0] = { "mstateen0", mstateen, read_mstateen, write_mstateen0, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MSTATEEN0H] = { "mstateen0h", mstateen, read_mstateenh, + write_mstateen0h, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MSTATEEN1] = { "mstateen1", mstateen, read_mstateen, + write_mstateen_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MSTATEEN1H] = { "mstateen1h", mstateen, read_mstateenh, + write_mstateenh_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MSTATEEN2] = { "mstateen2", mstateen, read_mstateen, + write_mstateen_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MSTATEEN2H] = { "mstateen2h", mstateen, read_mstateenh, + write_mstateenh_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MSTATEEN3] = { "mstateen3", mstateen, read_mstateen, + write_mstateen_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MSTATEEN3H] = { "mstateen3h", mstateen, read_mstateenh, + write_mstateenh_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HSTATEEN0] = { "hstateen0", hstateen, read_hstateen, write_hstateen0, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HSTATEEN0H] = { "hstateen0h", hstateenh, read_hstateenh, + write_hstateen0h, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HSTATEEN1] = { "hstateen1", hstateen, read_hstateen, + write_hstateen_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HSTATEEN1H] = { "hstateen1h", hstateenh, read_hstateenh, + write_hstateenh_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HSTATEEN2] = { "hstateen2", hstateen, read_hstateen, + write_hstateen_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HSTATEEN2H] = { "hstateen2h", hstateenh, read_hstateenh, + write_hstateenh_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HSTATEEN3] = { "hstateen3", hstateen, read_hstateen, + write_hstateen_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HSTATEEN3H] = { "hstateen3h", hstateenh, read_hstateenh, + write_hstateenh_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_SSTATEEN0] = { "sstateen0", sstateen, read_sstateen, write_sstateen0, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_SSTATEEN1] = { "sstateen1", sstateen, read_sstateen, + write_sstateen_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_SSTATEEN2] = { "sstateen2", sstateen, read_sstateen, + write_sstateen_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_SSTATEEN3] = { "sstateen3", sstateen, read_sstateen, + write_sstateen_1_3, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Supervisor Trap Setup */ - [CSR_SSTATUS] = { "sstatus", smode, read_sstatus, write_sstatus, NULL, - read_sstatus_i128 }, - [CSR_SIE] = { "sie", smode, NULL, NULL, rmw_sie }, - [CSR_STVEC] = { "stvec", smode, read_stvec, write_stvec }, - [CSR_SCOUNTEREN] = { "scounteren", smode, read_scounteren, write_scounteren }, + [CSR_SSTATUS] = { "sstatus", smode, read_sstatus, write_sstatus, + NULL, read_sstatus_i128 }, + [CSR_SIE] = { "sie", smode, NULL, NULL, rmw_sie }, + [CSR_STVEC] = { "stvec", smode, read_stvec, write_stvec }, + [CSR_SCOUNTEREN] = { "scounteren", smode, read_scounteren, + write_scounteren }, /* Supervisor Trap Handling */ - [CSR_SSCRATCH] = { "sscratch", smode, read_sscratch, write_sscratch, NULL, - read_sscratch_i128, write_sscratch_i128 }, + [CSR_SSCRATCH] = { "sscratch", smode, read_sscratch, write_sscratch, + NULL, read_sscratch_i128, write_sscratch_i128 }, [CSR_SEPC] = { "sepc", smode, read_sepc, write_sepc }, [CSR_SCAUSE] = { "scause", smode, read_scause, write_scause }, - [CSR_STVAL] = { "stval", smode, read_stval, write_stval }, + [CSR_STVAL] = { "stval", smode, read_stval, write_stval }, [CSR_SIP] = { "sip", smode, NULL, NULL, rmw_sip }, + [CSR_STIMECMP] = { "stimecmp", sstc, read_stimecmp, write_stimecmp, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_STIMECMPH] = { "stimecmph", sstc_32, read_stimecmph, write_stimecmph, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSTIMECMP] = { "vstimecmp", sstc, read_vstimecmp, + write_vstimecmp, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSTIMECMPH] = { "vstimecmph", sstc_32, read_vstimecmph, + write_vstimecmph, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Supervisor Protection and Translation */ - [CSR_SATP] = { "satp", smode, read_satp, write_satp }, + [CSR_SATP] = { "satp", satp, read_satp, write_satp }, /* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */ [CSR_SISELECT] = { "siselect", aia_smode, NULL, NULL, rmw_xiselect }, [CSR_SIREG] = { "sireg", aia_smode, NULL, NULL, rmw_xireg }, /* Supervisor-Level Interrupts (AIA) */ - [CSR_STOPI] = { "stopi", aia_smode, read_stopi }, - - /* Supervisor-Level IMSIC Interface (AIA) */ - [CSR_SSETEIPNUM] = { "sseteipnum", aia_smode, NULL, NULL, rmw_xsetclreinum }, - [CSR_SCLREIPNUM] = { "sclreipnum", aia_smode, NULL, NULL, rmw_xsetclreinum }, - [CSR_SSETEIENUM] = { "sseteienum", aia_smode, NULL, NULL, rmw_xsetclreinum }, - [CSR_SCLREIENUM] = { "sclreienum", aia_smode, NULL, NULL, rmw_xsetclreinum }, [CSR_STOPEI] = { "stopei", aia_smode, NULL, NULL, rmw_xtopei }, + [CSR_STOPI] = { "stopi", aia_smode, read_stopi }, /* Supervisor-Level High-Half CSRs (AIA) */ [CSR_SIEH] = { "sieh", aia_smode32, NULL, NULL, rmw_sieh }, [CSR_SIPH] = { "siph", aia_smode32, NULL, NULL, rmw_siph }, - [CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_HIDELEG] = { "hideleg", hmode, NULL, NULL, rmw_hideleg, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, write_hcounteren, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HTVAL] = { "htval", hmode, read_htval, write_htval, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, + write_hcounteren, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTVAL] = { "htval", hmode, read_htval, write_htval, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_HGEIP] = { "hgeip", hmode, read_hgeip, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HGATP] = { "hgatp", hmode, read_hgatp, write_hgatp, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, write_htimedelta, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, write_htimedeltah, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HGATP] = { "hgatp", hgatp, read_hgatp, write_hgatp, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, + write_htimedelta, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, + write_htimedeltah, + .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, write_vsstatus, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie , - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, write_vsscratch, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSCAUSE] = { "vscause", hmode, read_vscause, write_vscause, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSTVAL] = { "vstval", hmode, read_vstval, write_vstval, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, + write_vsstatus, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie , + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, + write_vsscratch, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSCAUSE] = { "vscause", hmode, read_vscause, write_vscause, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSTVAL] = { "vstval", hmode, read_vstval, write_vstval, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp, + .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2, - .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */ - [CSR_HVIEN] = { "hvien", aia_hmode, read_zero, write_ignore }, - [CSR_HVICTL] = { "hvictl", aia_hmode, read_hvictl, write_hvictl }, - [CSR_HVIPRIO1] = { "hviprio1", aia_hmode, read_hviprio1, write_hviprio1 }, - [CSR_HVIPRIO2] = { "hviprio2", aia_hmode, read_hviprio2, write_hviprio2 }, - + [CSR_HVIEN] = { "hvien", aia_hmode, NULL, NULL, rmw_hvien }, + [CSR_HVICTL] = { "hvictl", aia_hmode, read_hvictl, + write_hvictl }, + [CSR_HVIPRIO1] = { "hviprio1", aia_hmode, read_hviprio1, + write_hviprio1 }, + [CSR_HVIPRIO2] = { "hviprio2", aia_hmode, read_hviprio2, + write_hviprio2 }, /* * VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) */ - [CSR_VSISELECT] = { "vsiselect", aia_hmode, NULL, NULL, rmw_xiselect }, - [CSR_VSIREG] = { "vsireg", aia_hmode, NULL, NULL, rmw_xireg }, + [CSR_VSISELECT] = { "vsiselect", aia_hmode, NULL, NULL, + rmw_xiselect }, + [CSR_VSIREG] = { "vsireg", aia_hmode, NULL, NULL, rmw_xireg }, /* VS-Level Interrupts (H-extension with AIA) */ + [CSR_VSTOPEI] = { "vstopei", aia_hmode, NULL, NULL, rmw_xtopei }, [CSR_VSTOPI] = { "vstopi", aia_hmode, read_vstopi }, - /* VS-Level IMSIC Interface (H-extension with AIA) */ - [CSR_VSSETEIPNUM] = { "vsseteipnum", aia_hmode, NULL, NULL, rmw_xsetclreinum }, - [CSR_VSCLREIPNUM] = { "vsclreipnum", aia_hmode, NULL, NULL, rmw_xsetclreinum }, - [CSR_VSSETEIENUM] = { "vsseteienum", aia_hmode, NULL, NULL, rmw_xsetclreinum }, - [CSR_VSCLREIENUM] = { "vsclreienum", aia_hmode, NULL, NULL, rmw_xsetclreinum }, - [CSR_VSTOPEI] = { "vstopei", aia_hmode, NULL, NULL, rmw_xtopei }, - /* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ - [CSR_HIDELEGH] = { "hidelegh", aia_hmode32, NULL, NULL, rmw_hidelegh }, - [CSR_HVIENH] = { "hvienh", aia_hmode32, read_zero, write_ignore }, + [CSR_HIDELEGH] = { "hidelegh", aia_hmode32, NULL, NULL, + rmw_hidelegh }, + [CSR_HVIENH] = { "hvienh", aia_hmode32, NULL, NULL, rmw_hvienh }, [CSR_HVIPH] = { "hviph", aia_hmode32, NULL, NULL, rmw_hviph }, - [CSR_HVIPRIO1H] = { "hviprio1h", aia_hmode32, read_hviprio1h, write_hviprio1h }, - [CSR_HVIPRIO2H] = { "hviprio2h", aia_hmode32, read_hviprio2h, write_hviprio2h }, + [CSR_HVIPRIO1H] = { "hviprio1h", aia_hmode32, read_hviprio1h, + write_hviprio1h }, + [CSR_HVIPRIO2H] = { "hviprio2h", aia_hmode32, read_hviprio2h, + write_hviprio2h }, [CSR_VSIEH] = { "vsieh", aia_hmode32, NULL, NULL, rmw_vsieh }, [CSR_VSIPH] = { "vsiph", aia_hmode32, NULL, NULL, rmw_vsiph }, /* Physical Memory Protection */ - [CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg, - .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MSECCFG] = { "mseccfg", have_mseccfg, read_mseccfg, write_mseccfg, + .min_priv_ver = PRIV_VERSION_1_11_0 }, [CSR_PMPCFG0] = { "pmpcfg0", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG1] = { "pmpcfg1", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG2] = { "pmpcfg2", pmp, read_pmpcfg, write_pmpcfg }, @@ -3584,173 +4862,363 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_PMPADDR15] = { "pmpaddr15", pmp, read_pmpaddr, write_pmpaddr }, /* Debug CSRs */ - [CSR_TSELECT] = { "tselect", debug, read_tselect, write_tselect }, - [CSR_TDATA1] = { "tdata1", debug, read_tdata, write_tdata }, - [CSR_TDATA2] = { "tdata2", debug, read_tdata, write_tdata }, - [CSR_TDATA3] = { "tdata3", debug, read_tdata, write_tdata }, + [CSR_TSELECT] = { "tselect", debug, read_tselect, write_tselect }, + [CSR_TDATA1] = { "tdata1", debug, read_tdata, write_tdata }, + [CSR_TDATA2] = { "tdata2", debug, read_tdata, write_tdata }, + [CSR_TDATA3] = { "tdata3", debug, read_tdata, write_tdata }, + [CSR_TINFO] = { "tinfo", debug, read_tinfo, write_ignore }, + [CSR_MCONTEXT] = { "mcontext", debug, read_mcontext, write_mcontext }, /* User Pointer Masking */ - [CSR_UMTE] = { "umte", pointer_masking, read_umte, write_umte }, - [CSR_UPMMASK] = { "upmmask", pointer_masking, read_upmmask, write_upmmask }, - [CSR_UPMBASE] = { "upmbase", pointer_masking, read_upmbase, write_upmbase }, + [CSR_UMTE] = { "umte", pointer_masking, read_umte, write_umte }, + [CSR_UPMMASK] = { "upmmask", pointer_masking, read_upmmask, + write_upmmask }, + [CSR_UPMBASE] = { "upmbase", pointer_masking, read_upmbase, + write_upmbase }, /* Machine Pointer Masking */ - [CSR_MMTE] = { "mmte", pointer_masking, read_mmte, write_mmte }, - [CSR_MPMMASK] = { "mpmmask", pointer_masking, read_mpmmask, write_mpmmask }, - [CSR_MPMBASE] = { "mpmbase", pointer_masking, read_mpmbase, write_mpmbase }, + [CSR_MMTE] = { "mmte", pointer_masking, read_mmte, write_mmte }, + [CSR_MPMMASK] = { "mpmmask", pointer_masking, read_mpmmask, + write_mpmmask }, + [CSR_MPMBASE] = { "mpmbase", pointer_masking, read_mpmbase, + write_mpmbase }, /* Supervisor Pointer Masking */ - [CSR_SMTE] = { "smte", pointer_masking, read_smte, write_smte }, - [CSR_SPMMASK] = { "spmmask", pointer_masking, read_spmmask, write_spmmask }, - [CSR_SPMBASE] = { "spmbase", pointer_masking, read_spmbase, write_spmbase }, + [CSR_SMTE] = { "smte", pointer_masking, read_smte, write_smte }, + [CSR_SPMMASK] = { "spmmask", pointer_masking, read_spmmask, + write_spmmask }, + [CSR_SPMBASE] = { "spmbase", pointer_masking, read_spmbase, + write_spmbase }, /* Performance Counters */ - [CSR_HPMCOUNTER3] = { "hpmcounter3", ctr, read_zero }, - [CSR_HPMCOUNTER4] = { "hpmcounter4", ctr, read_zero }, - [CSR_HPMCOUNTER5] = { "hpmcounter5", ctr, read_zero }, - [CSR_HPMCOUNTER6] = { "hpmcounter6", ctr, read_zero }, - [CSR_HPMCOUNTER7] = { "hpmcounter7", ctr, read_zero }, - [CSR_HPMCOUNTER8] = { "hpmcounter8", ctr, read_zero }, - [CSR_HPMCOUNTER9] = { "hpmcounter9", ctr, read_zero }, - [CSR_HPMCOUNTER10] = { "hpmcounter10", ctr, read_zero }, - [CSR_HPMCOUNTER11] = { "hpmcounter11", ctr, read_zero }, - [CSR_HPMCOUNTER12] = { "hpmcounter12", ctr, read_zero }, - [CSR_HPMCOUNTER13] = { "hpmcounter13", ctr, read_zero }, - [CSR_HPMCOUNTER14] = { "hpmcounter14", ctr, read_zero }, - [CSR_HPMCOUNTER15] = { "hpmcounter15", ctr, read_zero }, - [CSR_HPMCOUNTER16] = { "hpmcounter16", ctr, read_zero }, - [CSR_HPMCOUNTER17] = { "hpmcounter17", ctr, read_zero }, - [CSR_HPMCOUNTER18] = { "hpmcounter18", ctr, read_zero }, - [CSR_HPMCOUNTER19] = { "hpmcounter19", ctr, read_zero }, - [CSR_HPMCOUNTER20] = { "hpmcounter20", ctr, read_zero }, - [CSR_HPMCOUNTER21] = { "hpmcounter21", ctr, read_zero }, - [CSR_HPMCOUNTER22] = { "hpmcounter22", ctr, read_zero }, - [CSR_HPMCOUNTER23] = { "hpmcounter23", ctr, read_zero }, - [CSR_HPMCOUNTER24] = { "hpmcounter24", ctr, read_zero }, - [CSR_HPMCOUNTER25] = { "hpmcounter25", ctr, read_zero }, - [CSR_HPMCOUNTER26] = { "hpmcounter26", ctr, read_zero }, - [CSR_HPMCOUNTER27] = { "hpmcounter27", ctr, read_zero }, - [CSR_HPMCOUNTER28] = { "hpmcounter28", ctr, read_zero }, - [CSR_HPMCOUNTER29] = { "hpmcounter29", ctr, read_zero }, - [CSR_HPMCOUNTER30] = { "hpmcounter30", ctr, read_zero }, - [CSR_HPMCOUNTER31] = { "hpmcounter31", ctr, read_zero }, + [CSR_HPMCOUNTER3] = { "hpmcounter3", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER4] = { "hpmcounter4", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER5] = { "hpmcounter5", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER6] = { "hpmcounter6", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER7] = { "hpmcounter7", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER8] = { "hpmcounter8", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER9] = { "hpmcounter9", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER10] = { "hpmcounter10", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER11] = { "hpmcounter11", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER12] = { "hpmcounter12", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER13] = { "hpmcounter13", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER14] = { "hpmcounter14", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER15] = { "hpmcounter15", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER16] = { "hpmcounter16", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER17] = { "hpmcounter17", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER18] = { "hpmcounter18", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER19] = { "hpmcounter19", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER20] = { "hpmcounter20", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER21] = { "hpmcounter21", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER22] = { "hpmcounter22", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER23] = { "hpmcounter23", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER24] = { "hpmcounter24", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER25] = { "hpmcounter25", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER26] = { "hpmcounter26", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER27] = { "hpmcounter27", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER28] = { "hpmcounter28", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER29] = { "hpmcounter29", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER30] = { "hpmcounter30", ctr, read_hpmcounter }, + [CSR_HPMCOUNTER31] = { "hpmcounter31", ctr, read_hpmcounter }, - [CSR_MHPMCOUNTER3] = { "mhpmcounter3", any, read_zero }, - [CSR_MHPMCOUNTER4] = { "mhpmcounter4", any, read_zero }, - [CSR_MHPMCOUNTER5] = { "mhpmcounter5", any, read_zero }, - [CSR_MHPMCOUNTER6] = { "mhpmcounter6", any, read_zero }, - [CSR_MHPMCOUNTER7] = { "mhpmcounter7", any, read_zero }, - [CSR_MHPMCOUNTER8] = { "mhpmcounter8", any, read_zero }, - [CSR_MHPMCOUNTER9] = { "mhpmcounter9", any, read_zero }, - [CSR_MHPMCOUNTER10] = { "mhpmcounter10", any, read_zero }, - [CSR_MHPMCOUNTER11] = { "mhpmcounter11", any, read_zero }, - [CSR_MHPMCOUNTER12] = { "mhpmcounter12", any, read_zero }, - [CSR_MHPMCOUNTER13] = { "mhpmcounter13", any, read_zero }, - [CSR_MHPMCOUNTER14] = { "mhpmcounter14", any, read_zero }, - [CSR_MHPMCOUNTER15] = { "mhpmcounter15", any, read_zero }, - [CSR_MHPMCOUNTER16] = { "mhpmcounter16", any, read_zero }, - [CSR_MHPMCOUNTER17] = { "mhpmcounter17", any, read_zero }, - [CSR_MHPMCOUNTER18] = { "mhpmcounter18", any, read_zero }, - [CSR_MHPMCOUNTER19] = { "mhpmcounter19", any, read_zero }, - [CSR_MHPMCOUNTER20] = { "mhpmcounter20", any, read_zero }, - [CSR_MHPMCOUNTER21] = { "mhpmcounter21", any, read_zero }, - [CSR_MHPMCOUNTER22] = { "mhpmcounter22", any, read_zero }, - [CSR_MHPMCOUNTER23] = { "mhpmcounter23", any, read_zero }, - [CSR_MHPMCOUNTER24] = { "mhpmcounter24", any, read_zero }, - [CSR_MHPMCOUNTER25] = { "mhpmcounter25", any, read_zero }, - [CSR_MHPMCOUNTER26] = { "mhpmcounter26", any, read_zero }, - [CSR_MHPMCOUNTER27] = { "mhpmcounter27", any, read_zero }, - [CSR_MHPMCOUNTER28] = { "mhpmcounter28", any, read_zero }, - [CSR_MHPMCOUNTER29] = { "mhpmcounter29", any, read_zero }, - [CSR_MHPMCOUNTER30] = { "mhpmcounter30", any, read_zero }, - [CSR_MHPMCOUNTER31] = { "mhpmcounter31", any, read_zero }, + [CSR_MHPMCOUNTER3] = { "mhpmcounter3", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER4] = { "mhpmcounter4", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER5] = { "mhpmcounter5", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER6] = { "mhpmcounter6", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER7] = { "mhpmcounter7", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER8] = { "mhpmcounter8", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER9] = { "mhpmcounter9", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER10] = { "mhpmcounter10", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER11] = { "mhpmcounter11", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER12] = { "mhpmcounter12", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER13] = { "mhpmcounter13", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER14] = { "mhpmcounter14", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER15] = { "mhpmcounter15", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER16] = { "mhpmcounter16", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER17] = { "mhpmcounter17", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER18] = { "mhpmcounter18", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER19] = { "mhpmcounter19", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER20] = { "mhpmcounter20", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER21] = { "mhpmcounter21", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER22] = { "mhpmcounter22", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER23] = { "mhpmcounter23", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER24] = { "mhpmcounter24", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER25] = { "mhpmcounter25", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER26] = { "mhpmcounter26", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER27] = { "mhpmcounter27", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER28] = { "mhpmcounter28", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER29] = { "mhpmcounter29", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER30] = { "mhpmcounter30", mctr, read_hpmcounter, + write_mhpmcounter }, + [CSR_MHPMCOUNTER31] = { "mhpmcounter31", mctr, read_hpmcounter, + write_mhpmcounter }, - [CSR_MHPMEVENT3] = { "mhpmevent3", any, read_zero }, - [CSR_MHPMEVENT4] = { "mhpmevent4", any, read_zero }, - [CSR_MHPMEVENT5] = { "mhpmevent5", any, read_zero }, - [CSR_MHPMEVENT6] = { "mhpmevent6", any, read_zero }, - [CSR_MHPMEVENT7] = { "mhpmevent7", any, read_zero }, - [CSR_MHPMEVENT8] = { "mhpmevent8", any, read_zero }, - [CSR_MHPMEVENT9] = { "mhpmevent9", any, read_zero }, - [CSR_MHPMEVENT10] = { "mhpmevent10", any, read_zero }, - [CSR_MHPMEVENT11] = { "mhpmevent11", any, read_zero }, - [CSR_MHPMEVENT12] = { "mhpmevent12", any, read_zero }, - [CSR_MHPMEVENT13] = { "mhpmevent13", any, read_zero }, - [CSR_MHPMEVENT14] = { "mhpmevent14", any, read_zero }, - [CSR_MHPMEVENT15] = { "mhpmevent15", any, read_zero }, - [CSR_MHPMEVENT16] = { "mhpmevent16", any, read_zero }, - [CSR_MHPMEVENT17] = { "mhpmevent17", any, read_zero }, - [CSR_MHPMEVENT18] = { "mhpmevent18", any, read_zero }, - [CSR_MHPMEVENT19] = { "mhpmevent19", any, read_zero }, - [CSR_MHPMEVENT20] = { "mhpmevent20", any, read_zero }, - [CSR_MHPMEVENT21] = { "mhpmevent21", any, read_zero }, - [CSR_MHPMEVENT22] = { "mhpmevent22", any, read_zero }, - [CSR_MHPMEVENT23] = { "mhpmevent23", any, read_zero }, - [CSR_MHPMEVENT24] = { "mhpmevent24", any, read_zero }, - [CSR_MHPMEVENT25] = { "mhpmevent25", any, read_zero }, - [CSR_MHPMEVENT26] = { "mhpmevent26", any, read_zero }, - [CSR_MHPMEVENT27] = { "mhpmevent27", any, read_zero }, - [CSR_MHPMEVENT28] = { "mhpmevent28", any, read_zero }, - [CSR_MHPMEVENT29] = { "mhpmevent29", any, read_zero }, - [CSR_MHPMEVENT30] = { "mhpmevent30", any, read_zero }, - [CSR_MHPMEVENT31] = { "mhpmevent31", any, read_zero }, + [CSR_MCOUNTINHIBIT] = { "mcountinhibit", any, read_mcountinhibit, + write_mcountinhibit, + .min_priv_ver = PRIV_VERSION_1_11_0 }, - [CSR_HPMCOUNTER3H] = { "hpmcounter3h", ctr32, read_zero }, - [CSR_HPMCOUNTER4H] = { "hpmcounter4h", ctr32, read_zero }, - [CSR_HPMCOUNTER5H] = { "hpmcounter5h", ctr32, read_zero }, - [CSR_HPMCOUNTER6H] = { "hpmcounter6h", ctr32, read_zero }, - [CSR_HPMCOUNTER7H] = { "hpmcounter7h", ctr32, read_zero }, - [CSR_HPMCOUNTER8H] = { "hpmcounter8h", ctr32, read_zero }, - [CSR_HPMCOUNTER9H] = { "hpmcounter9h", ctr32, read_zero }, - [CSR_HPMCOUNTER10H] = { "hpmcounter10h", ctr32, read_zero }, - [CSR_HPMCOUNTER11H] = { "hpmcounter11h", ctr32, read_zero }, - [CSR_HPMCOUNTER12H] = { "hpmcounter12h", ctr32, read_zero }, - [CSR_HPMCOUNTER13H] = { "hpmcounter13h", ctr32, read_zero }, - [CSR_HPMCOUNTER14H] = { "hpmcounter14h", ctr32, read_zero }, - [CSR_HPMCOUNTER15H] = { "hpmcounter15h", ctr32, read_zero }, - [CSR_HPMCOUNTER16H] = { "hpmcounter16h", ctr32, read_zero }, - [CSR_HPMCOUNTER17H] = { "hpmcounter17h", ctr32, read_zero }, - [CSR_HPMCOUNTER18H] = { "hpmcounter18h", ctr32, read_zero }, - [CSR_HPMCOUNTER19H] = { "hpmcounter19h", ctr32, read_zero }, - [CSR_HPMCOUNTER20H] = { "hpmcounter20h", ctr32, read_zero }, - [CSR_HPMCOUNTER21H] = { "hpmcounter21h", ctr32, read_zero }, - [CSR_HPMCOUNTER22H] = { "hpmcounter22h", ctr32, read_zero }, - [CSR_HPMCOUNTER23H] = { "hpmcounter23h", ctr32, read_zero }, - [CSR_HPMCOUNTER24H] = { "hpmcounter24h", ctr32, read_zero }, - [CSR_HPMCOUNTER25H] = { "hpmcounter25h", ctr32, read_zero }, - [CSR_HPMCOUNTER26H] = { "hpmcounter26h", ctr32, read_zero }, - [CSR_HPMCOUNTER27H] = { "hpmcounter27h", ctr32, read_zero }, - [CSR_HPMCOUNTER28H] = { "hpmcounter28h", ctr32, read_zero }, - [CSR_HPMCOUNTER29H] = { "hpmcounter29h", ctr32, read_zero }, - [CSR_HPMCOUNTER30H] = { "hpmcounter30h", ctr32, read_zero }, - [CSR_HPMCOUNTER31H] = { "hpmcounter31h", ctr32, read_zero }, + [CSR_MHPMEVENT3] = { "mhpmevent3", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT4] = { "mhpmevent4", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT5] = { "mhpmevent5", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT6] = { "mhpmevent6", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT7] = { "mhpmevent7", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT8] = { "mhpmevent8", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT9] = { "mhpmevent9", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT10] = { "mhpmevent10", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT11] = { "mhpmevent11", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT12] = { "mhpmevent12", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT13] = { "mhpmevent13", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT14] = { "mhpmevent14", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT15] = { "mhpmevent15", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT16] = { "mhpmevent16", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT17] = { "mhpmevent17", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT18] = { "mhpmevent18", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT19] = { "mhpmevent19", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT20] = { "mhpmevent20", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT21] = { "mhpmevent21", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT22] = { "mhpmevent22", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT23] = { "mhpmevent23", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT24] = { "mhpmevent24", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT25] = { "mhpmevent25", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT26] = { "mhpmevent26", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT27] = { "mhpmevent27", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT28] = { "mhpmevent28", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT29] = { "mhpmevent29", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT30] = { "mhpmevent30", any, read_mhpmevent, + write_mhpmevent }, + [CSR_MHPMEVENT31] = { "mhpmevent31", any, read_mhpmevent, + write_mhpmevent }, + + [CSR_MHPMEVENT3H] = { "mhpmevent3h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT4H] = { "mhpmevent4h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT5H] = { "mhpmevent5h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT6H] = { "mhpmevent6h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT7H] = { "mhpmevent7h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT8H] = { "mhpmevent8h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT9H] = { "mhpmevent9h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT10H] = { "mhpmevent10h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT11H] = { "mhpmevent11h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT12H] = { "mhpmevent12h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT13H] = { "mhpmevent13h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT14H] = { "mhpmevent14h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT15H] = { "mhpmevent15h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT16H] = { "mhpmevent16h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT17H] = { "mhpmevent17h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT18H] = { "mhpmevent18h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT19H] = { "mhpmevent19h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT20H] = { "mhpmevent20h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT21H] = { "mhpmevent21h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT22H] = { "mhpmevent22h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT23H] = { "mhpmevent23h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT24H] = { "mhpmevent24h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT25H] = { "mhpmevent25h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT26H] = { "mhpmevent26h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT27H] = { "mhpmevent27h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT28H] = { "mhpmevent28h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT29H] = { "mhpmevent29h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT30H] = { "mhpmevent30h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT31H] = { "mhpmevent31h", sscofpmf, read_mhpmeventh, + write_mhpmeventh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + + [CSR_HPMCOUNTER3H] = { "hpmcounter3h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER4H] = { "hpmcounter4h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER5H] = { "hpmcounter5h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER6H] = { "hpmcounter6h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER7H] = { "hpmcounter7h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER8H] = { "hpmcounter8h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER9H] = { "hpmcounter9h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER10H] = { "hpmcounter10h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER11H] = { "hpmcounter11h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER12H] = { "hpmcounter12h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER13H] = { "hpmcounter13h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER14H] = { "hpmcounter14h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER15H] = { "hpmcounter15h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER16H] = { "hpmcounter16h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER17H] = { "hpmcounter17h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER18H] = { "hpmcounter18h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER19H] = { "hpmcounter19h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER20H] = { "hpmcounter20h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER21H] = { "hpmcounter21h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER22H] = { "hpmcounter22h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER23H] = { "hpmcounter23h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER24H] = { "hpmcounter24h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER25H] = { "hpmcounter25h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER26H] = { "hpmcounter26h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER27H] = { "hpmcounter27h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER28H] = { "hpmcounter28h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER29H] = { "hpmcounter29h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER30H] = { "hpmcounter30h", ctr32, read_hpmcounterh }, + [CSR_HPMCOUNTER31H] = { "hpmcounter31h", ctr32, read_hpmcounterh }, + + [CSR_MHPMCOUNTER3H] = { "mhpmcounter3h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER4H] = { "mhpmcounter4h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER5H] = { "mhpmcounter5h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER6H] = { "mhpmcounter6h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER7H] = { "mhpmcounter7h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER8H] = { "mhpmcounter8h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER9H] = { "mhpmcounter9h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER10H] = { "mhpmcounter10h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER11H] = { "mhpmcounter11h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER12H] = { "mhpmcounter12h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER13H] = { "mhpmcounter13h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER14H] = { "mhpmcounter14h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER15H] = { "mhpmcounter15h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER16H] = { "mhpmcounter16h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER17H] = { "mhpmcounter17h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER18H] = { "mhpmcounter18h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER19H] = { "mhpmcounter19h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER20H] = { "mhpmcounter20h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER21H] = { "mhpmcounter21h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER22H] = { "mhpmcounter22h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER23H] = { "mhpmcounter23h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER24H] = { "mhpmcounter24h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER25H] = { "mhpmcounter25h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER26H] = { "mhpmcounter26h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER27H] = { "mhpmcounter27h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER28H] = { "mhpmcounter28h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER29H] = { "mhpmcounter29h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER30H] = { "mhpmcounter30h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", mctr32, read_hpmcounterh, + write_mhpmcounterh }, + [CSR_SCOUNTOVF] = { "scountovf", sscofpmf, read_scountovf, + .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMCOUNTER3H] = { "mhpmcounter3h", any32, read_zero }, - [CSR_MHPMCOUNTER4H] = { "mhpmcounter4h", any32, read_zero }, - [CSR_MHPMCOUNTER5H] = { "mhpmcounter5h", any32, read_zero }, - [CSR_MHPMCOUNTER6H] = { "mhpmcounter6h", any32, read_zero }, - [CSR_MHPMCOUNTER7H] = { "mhpmcounter7h", any32, read_zero }, - [CSR_MHPMCOUNTER8H] = { "mhpmcounter8h", any32, read_zero }, - [CSR_MHPMCOUNTER9H] = { "mhpmcounter9h", any32, read_zero }, - [CSR_MHPMCOUNTER10H] = { "mhpmcounter10h", any32, read_zero }, - [CSR_MHPMCOUNTER11H] = { "mhpmcounter11h", any32, read_zero }, - [CSR_MHPMCOUNTER12H] = { "mhpmcounter12h", any32, read_zero }, - [CSR_MHPMCOUNTER13H] = { "mhpmcounter13h", any32, read_zero }, - [CSR_MHPMCOUNTER14H] = { "mhpmcounter14h", any32, read_zero }, - [CSR_MHPMCOUNTER15H] = { "mhpmcounter15h", any32, read_zero }, - [CSR_MHPMCOUNTER16H] = { "mhpmcounter16h", any32, read_zero }, - [CSR_MHPMCOUNTER17H] = { "mhpmcounter17h", any32, read_zero }, - [CSR_MHPMCOUNTER18H] = { "mhpmcounter18h", any32, read_zero }, - [CSR_MHPMCOUNTER19H] = { "mhpmcounter19h", any32, read_zero }, - [CSR_MHPMCOUNTER20H] = { "mhpmcounter20h", any32, read_zero }, - [CSR_MHPMCOUNTER21H] = { "mhpmcounter21h", any32, read_zero }, - [CSR_MHPMCOUNTER22H] = { "mhpmcounter22h", any32, read_zero }, - [CSR_MHPMCOUNTER23H] = { "mhpmcounter23h", any32, read_zero }, - [CSR_MHPMCOUNTER24H] = { "mhpmcounter24h", any32, read_zero }, - [CSR_MHPMCOUNTER25H] = { "mhpmcounter25h", any32, read_zero }, - [CSR_MHPMCOUNTER26H] = { "mhpmcounter26h", any32, read_zero }, - [CSR_MHPMCOUNTER27H] = { "mhpmcounter27h", any32, read_zero }, - [CSR_MHPMCOUNTER28H] = { "mhpmcounter28h", any32, read_zero }, - [CSR_MHPMCOUNTER29H] = { "mhpmcounter29h", any32, read_zero }, - [CSR_MHPMCOUNTER30H] = { "mhpmcounter30h", any32, read_zero }, - [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", any32, read_zero }, #endif /* !CONFIG_USER_ONLY */ }; diff --git a/target/riscv/debug.c b/target/riscv/debug.c index 2f2a51c732..e30d99cc2f 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -29,6 +29,8 @@ #include "cpu.h" #include "trace.h" #include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "sysemu/cpu-timers.h" /* * The following M-mode trigger CSRs are implemented: @@ -37,11 +39,9 @@ * - tdata1 * - tdata2 * - tdata3 + * - tinfo * - * We don't support writable 'type' field in the tdata1 register, so there is - * no need to implement the "tinfo" CSR. - * - * The following triggers are implemented: + * The following triggers are initialized by default: * * Index | Type | tdata mapping | Description * ------+------+------------------------+------------ @@ -52,8 +52,15 @@ /* tdata availability of a trigger */ typedef bool tdata_avail[TDATA_NUM]; -static tdata_avail tdata_mapping[TRIGGER_NUM] = { - [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = { true, true, false }, +static tdata_avail tdata_mapping[TRIGGER_TYPE_NUM] = { + [TRIGGER_TYPE_NO_EXIST] = { false, false, false }, + [TRIGGER_TYPE_AD_MATCH] = { true, true, true }, + [TRIGGER_TYPE_INST_CNT] = { true, false, true }, + [TRIGGER_TYPE_INT] = { true, true, true }, + [TRIGGER_TYPE_EXCP] = { true, true, true }, + [TRIGGER_TYPE_AD_MATCH6] = { true, true, true }, + [TRIGGER_TYPE_EXT_SRC] = { true, false, false }, + [TRIGGER_TYPE_UNAVAIL] = { true, true, true } }; /* only breakpoint size 1/2/4/8 supported */ @@ -67,17 +74,76 @@ static int access_size[SIZE_NUM] = { [6 ... 15] = -1, }; -static inline target_ulong trigger_type(CPURISCVState *env, - trigger_type_t type) +static inline target_ulong extract_trigger_type(CPURISCVState *env, + target_ulong tdata1) +{ + switch (riscv_cpu_mxl(env)) { + case MXL_RV32: + return extract32(tdata1, 28, 4); + case MXL_RV64: + case MXL_RV128: + return extract64(tdata1, 60, 4); + default: + g_assert_not_reached(); + } +} + +static inline target_ulong get_trigger_type(CPURISCVState *env, + target_ulong trigger_index) +{ + return extract_trigger_type(env, env->tdata1[trigger_index]); +} + +static trigger_action_t get_trigger_action(CPURISCVState *env, + target_ulong trigger_index) +{ + target_ulong tdata1 = env->tdata1[trigger_index]; + int trigger_type = get_trigger_type(env, trigger_index); + trigger_action_t action = DBG_ACTION_NONE; + + switch (trigger_type) { + case TRIGGER_TYPE_AD_MATCH: + action = (tdata1 & TYPE2_ACTION) >> 12; + break; + case TRIGGER_TYPE_AD_MATCH6: + action = (tdata1 & TYPE6_ACTION) >> 12; + break; + case TRIGGER_TYPE_INST_CNT: + case TRIGGER_TYPE_INT: + case TRIGGER_TYPE_EXCP: + case TRIGGER_TYPE_EXT_SRC: + qemu_log_mask(LOG_UNIMP, "trigger type: %d is not supported\n", + trigger_type); + break; + case TRIGGER_TYPE_NO_EXIST: + case TRIGGER_TYPE_UNAVAIL: + qemu_log_mask(LOG_GUEST_ERROR, "trigger type: %d does not exit\n", + trigger_type); + break; + default: + g_assert_not_reached(); + } + + return action; +} + +static inline target_ulong build_tdata1(CPURISCVState *env, + trigger_type_t type, + bool dmode, target_ulong data) { target_ulong tdata1; switch (riscv_cpu_mxl(env)) { case MXL_RV32: - tdata1 = RV32_TYPE(type); + tdata1 = RV32_TYPE(type) | + (dmode ? RV32_DMODE : 0) | + (data & RV32_DATA_MASK); break; case MXL_RV64: - tdata1 = RV64_TYPE(type); + case MXL_RV128: + tdata1 = RV64_TYPE(type) | + (dmode ? RV64_DMODE : 0) | + (data & RV64_DATA_MASK); break; default: g_assert_not_reached(); @@ -88,15 +154,13 @@ static inline target_ulong trigger_type(CPURISCVState *env, bool tdata_available(CPURISCVState *env, int tdata_index) { + int trigger_type = get_trigger_type(env, env->trigger_cur); + if (unlikely(tdata_index >= TDATA_NUM)) { return false; } - if (unlikely(env->trigger_cur >= TRIGGER_NUM)) { - return false; - } - - return tdata_mapping[env->trigger_cur][tdata_index]; + return tdata_mapping[trigger_type][tdata_index]; } target_ulong tselect_csr_read(CPURISCVState *env) @@ -106,8 +170,9 @@ target_ulong tselect_csr_read(CPURISCVState *env) void tselect_csr_write(CPURISCVState *env, target_ulong val) { - /* all target_ulong bits of tselect are implemented */ - env->trigger_cur = val; + if (val < RV_MAX_TRIGGERS) { + env->trigger_cur = val; + } } static target_ulong tdata1_validate(CPURISCVState *env, target_ulong val, @@ -123,6 +188,7 @@ static target_ulong tdata1_validate(CPURISCVState *env, target_ulong val, tdata1 = RV32_TYPE(t); break; case MXL_RV64: + case MXL_RV128: type = extract64(val, 60, 4); dmode = extract64(val, 59, 1); tdata1 = RV64_TYPE(t); @@ -135,6 +201,7 @@ static target_ulong tdata1_validate(CPURISCVState *env, target_ulong val, qemu_log_mask(LOG_GUEST_ERROR, "ignoring type write to tdata1 register\n"); } + if (dmode != 0) { qemu_log_mask(LOG_UNIMP, "debug mode is not supported\n"); } @@ -150,17 +217,41 @@ static inline void warn_always_zero_bit(target_ulong val, target_ulong mask, } } +static void do_trigger_action(CPURISCVState *env, target_ulong trigger_index) +{ + trigger_action_t action = get_trigger_action(env, trigger_index); + + switch (action) { + case DBG_ACTION_NONE: + break; + case DBG_ACTION_BP: + riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0); + break; + case DBG_ACTION_DBG_MODE: + case DBG_ACTION_TRACE0: + case DBG_ACTION_TRACE1: + case DBG_ACTION_TRACE2: + case DBG_ACTION_TRACE3: + case DBG_ACTION_EXT_DBG0: + case DBG_ACTION_EXT_DBG1: + qemu_log_mask(LOG_UNIMP, "action: %d is not supported\n", action); + break; + default: + g_assert_not_reached(); + } +} + +/* type 2 trigger */ + static uint32_t type2_breakpoint_size(CPURISCVState *env, target_ulong ctrl) { - uint32_t size, sizelo, sizehi = 0; + uint32_t sizelo, sizehi = 0; if (riscv_cpu_mxl(env) == MXL_RV64) { sizehi = extract32(ctrl, 21, 2); } sizelo = extract32(ctrl, 16, 2); - size = (sizehi << 2) | sizelo; - - return size; + return (sizehi << 2) | sizelo; } static inline bool type2_breakpoint_enabled(target_ulong ctrl) @@ -191,8 +282,8 @@ static target_ulong type2_mcontrol_validate(CPURISCVState *env, /* validate size encoding */ size = type2_breakpoint_size(env, ctrl); if (access_size[size] == -1) { - qemu_log_mask(LOG_UNIMP, "access size %d is not supported, using SIZE_ANY\n", - size); + qemu_log_mask(LOG_UNIMP, "access size %d is not supported, using " + "SIZE_ANY\n", size); } else { val |= (ctrl & TYPE2_SIZELO); if (riscv_cpu_mxl(env) == MXL_RV64) { @@ -209,8 +300,8 @@ static target_ulong type2_mcontrol_validate(CPURISCVState *env, static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index) { - target_ulong ctrl = env->type2_trig[index].mcontrol; - target_ulong addr = env->type2_trig[index].maddress; + target_ulong ctrl = env->tdata1[index]; + target_ulong addr = env->tdata2[index]; bool enabled = type2_breakpoint_enabled(ctrl); CPUState *cs = env_cpu(env); int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; @@ -221,7 +312,7 @@ static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index) } if (ctrl & TYPE2_EXEC) { - cpu_breakpoint_insert(cs, addr, flags, &env->type2_trig[index].bp); + cpu_breakpoint_insert(cs, addr, flags, &env->cpu_breakpoint[index]); } if (ctrl & TYPE2_LOAD) { @@ -235,10 +326,10 @@ static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index) size = type2_breakpoint_size(env, ctrl); if (size != 0) { cpu_watchpoint_insert(cs, addr, size, flags, - &env->type2_trig[index].wp); + &env->cpu_watchpoint[index]); } else { cpu_watchpoint_insert(cs, addr, 8, flags, - &env->type2_trig[index].wp); + &env->cpu_watchpoint[index]); } } } @@ -247,59 +338,42 @@ static void type2_breakpoint_remove(CPURISCVState *env, target_ulong index) { CPUState *cs = env_cpu(env); - if (env->type2_trig[index].bp) { - cpu_breakpoint_remove_by_ref(cs, env->type2_trig[index].bp); - env->type2_trig[index].bp = NULL; + if (env->cpu_breakpoint[index]) { + cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[index]); + env->cpu_breakpoint[index] = NULL; } - if (env->type2_trig[index].wp) { - cpu_watchpoint_remove_by_ref(cs, env->type2_trig[index].wp); - env->type2_trig[index].wp = NULL; + if (env->cpu_watchpoint[index]) { + cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]); + env->cpu_watchpoint[index] = NULL; } } -static target_ulong type2_reg_read(CPURISCVState *env, - target_ulong trigger_index, int tdata_index) -{ - uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0; - target_ulong tdata; - - switch (tdata_index) { - case TDATA1: - tdata = env->type2_trig[index].mcontrol; - break; - case TDATA2: - tdata = env->type2_trig[index].maddress; - break; - default: - g_assert_not_reached(); - } - - return tdata; -} - -static void type2_reg_write(CPURISCVState *env, target_ulong trigger_index, +static void type2_reg_write(CPURISCVState *env, target_ulong index, int tdata_index, target_ulong val) { - uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0; target_ulong new_val; switch (tdata_index) { case TDATA1: new_val = type2_mcontrol_validate(env, val); - if (new_val != env->type2_trig[index].mcontrol) { - env->type2_trig[index].mcontrol = new_val; + if (new_val != env->tdata1[index]) { + env->tdata1[index] = new_val; type2_breakpoint_remove(env, index); type2_breakpoint_insert(env, index); } break; case TDATA2: - if (val != env->type2_trig[index].maddress) { - env->type2_trig[index].maddress = val; + if (val != env->tdata2[index]) { + env->tdata2[index] = val; type2_breakpoint_remove(env, index); type2_breakpoint_insert(env, index); } break; + case TDATA3: + qemu_log_mask(LOG_UNIMP, + "tdata3 is not supported for type 2 trigger\n"); + break; default: g_assert_not_reached(); } @@ -307,35 +381,378 @@ static void type2_reg_write(CPURISCVState *env, target_ulong trigger_index, return; } -typedef target_ulong (*tdata_read_func)(CPURISCVState *env, - target_ulong trigger_index, - int tdata_index); +/* type 6 trigger */ -static tdata_read_func trigger_read_funcs[TRIGGER_NUM] = { - [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_read, -}; +static inline bool type6_breakpoint_enabled(target_ulong ctrl) +{ + bool mode = !!(ctrl & (TYPE6_VU | TYPE6_VS | TYPE6_U | TYPE6_S | TYPE6_M)); + bool rwx = !!(ctrl & (TYPE6_LOAD | TYPE6_STORE | TYPE6_EXEC)); -typedef void (*tdata_write_func)(CPURISCVState *env, - target_ulong trigger_index, - int tdata_index, - target_ulong val); + return mode && rwx; +} -static tdata_write_func trigger_write_funcs[TRIGGER_NUM] = { - [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_write, -}; +static target_ulong type6_mcontrol6_validate(CPURISCVState *env, + target_ulong ctrl) +{ + target_ulong val; + uint32_t size; + + /* validate the generic part first */ + val = tdata1_validate(env, ctrl, TRIGGER_TYPE_AD_MATCH6); + + /* validate unimplemented (always zero) bits */ + warn_always_zero_bit(ctrl, TYPE6_MATCH, "match"); + warn_always_zero_bit(ctrl, TYPE6_CHAIN, "chain"); + warn_always_zero_bit(ctrl, TYPE6_ACTION, "action"); + warn_always_zero_bit(ctrl, TYPE6_TIMING, "timing"); + warn_always_zero_bit(ctrl, TYPE6_SELECT, "select"); + warn_always_zero_bit(ctrl, TYPE6_HIT, "hit"); + + /* validate size encoding */ + size = extract32(ctrl, 16, 4); + if (access_size[size] == -1) { + qemu_log_mask(LOG_UNIMP, "access size %d is not supported, using " + "SIZE_ANY\n", size); + } else { + val |= (ctrl & TYPE6_SIZE); + } + + /* keep the mode and attribute bits */ + val |= (ctrl & (TYPE6_VU | TYPE6_VS | TYPE6_U | TYPE6_S | TYPE6_M | + TYPE6_LOAD | TYPE6_STORE | TYPE6_EXEC)); + + return val; +} + +static void type6_breakpoint_insert(CPURISCVState *env, target_ulong index) +{ + target_ulong ctrl = env->tdata1[index]; + target_ulong addr = env->tdata2[index]; + bool enabled = type6_breakpoint_enabled(ctrl); + CPUState *cs = env_cpu(env); + int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; + uint32_t size; + + if (!enabled) { + return; + } + + if (ctrl & TYPE6_EXEC) { + cpu_breakpoint_insert(cs, addr, flags, &env->cpu_breakpoint[index]); + } + + if (ctrl & TYPE6_LOAD) { + flags |= BP_MEM_READ; + } + + if (ctrl & TYPE6_STORE) { + flags |= BP_MEM_WRITE; + } + + if (flags & BP_MEM_ACCESS) { + size = extract32(ctrl, 16, 4); + if (size != 0) { + cpu_watchpoint_insert(cs, addr, size, flags, + &env->cpu_watchpoint[index]); + } else { + cpu_watchpoint_insert(cs, addr, 8, flags, + &env->cpu_watchpoint[index]); + } + } +} + +static void type6_breakpoint_remove(CPURISCVState *env, target_ulong index) +{ + type2_breakpoint_remove(env, index); +} + +static void type6_reg_write(CPURISCVState *env, target_ulong index, + int tdata_index, target_ulong val) +{ + target_ulong new_val; + + switch (tdata_index) { + case TDATA1: + new_val = type6_mcontrol6_validate(env, val); + if (new_val != env->tdata1[index]) { + env->tdata1[index] = new_val; + type6_breakpoint_remove(env, index); + type6_breakpoint_insert(env, index); + } + break; + case TDATA2: + if (val != env->tdata2[index]) { + env->tdata2[index] = val; + type6_breakpoint_remove(env, index); + type6_breakpoint_insert(env, index); + } + break; + case TDATA3: + qemu_log_mask(LOG_UNIMP, + "tdata3 is not supported for type 6 trigger\n"); + break; + default: + g_assert_not_reached(); + } + + return; +} + +/* icount trigger type */ +static inline int +itrigger_get_count(CPURISCVState *env, int index) +{ + return get_field(env->tdata1[index], ITRIGGER_COUNT); +} + +static inline void +itrigger_set_count(CPURISCVState *env, int index, int value) +{ + env->tdata1[index] = set_field(env->tdata1[index], + ITRIGGER_COUNT, value); +} + +static bool check_itrigger_priv(CPURISCVState *env, int index) +{ + target_ulong tdata1 = env->tdata1[index]; + if (env->virt_enabled) { + /* check VU/VS bit against current privilege level */ + return (get_field(tdata1, ITRIGGER_VS) == env->priv) || + (get_field(tdata1, ITRIGGER_VU) == env->priv); + } else { + /* check U/S/M bit against current privilege level */ + return (get_field(tdata1, ITRIGGER_M) == env->priv) || + (get_field(tdata1, ITRIGGER_S) == env->priv) || + (get_field(tdata1, ITRIGGER_U) == env->priv); + } +} + +bool riscv_itrigger_enabled(CPURISCVState *env) +{ + int count; + for (int i = 0; i < RV_MAX_TRIGGERS; i++) { + if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) { + continue; + } + if (check_itrigger_priv(env, i)) { + continue; + } + count = itrigger_get_count(env, i); + if (!count) { + continue; + } + return true; + } + + return false; +} + +void helper_itrigger_match(CPURISCVState *env) +{ + int count; + for (int i = 0; i < RV_MAX_TRIGGERS; i++) { + if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) { + continue; + } + if (check_itrigger_priv(env, i)) { + continue; + } + count = itrigger_get_count(env, i); + if (!count) { + continue; + } + itrigger_set_count(env, i, count--); + if (!count) { + env->itrigger_enabled = riscv_itrigger_enabled(env); + do_trigger_action(env, i); + } + } +} + +static void riscv_itrigger_update_count(CPURISCVState *env) +{ + int count, executed; + /* + * Record last icount, so that we can evaluate the executed instructions + * since last privilege mode change or timer expire. + */ + int64_t last_icount = env->last_icount, current_icount; + current_icount = env->last_icount = icount_get_raw(); + + for (int i = 0; i < RV_MAX_TRIGGERS; i++) { + if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) { + continue; + } + count = itrigger_get_count(env, i); + if (!count) { + continue; + } + /* + * Only when privilege is changed or itrigger timer expires, + * the count field in itrigger tdata1 register is updated. + * And the count field in itrigger only contains remaining value. + */ + if (check_itrigger_priv(env, i)) { + /* + * If itrigger enabled in this privilege mode, the number of + * executed instructions since last privilege change + * should be reduced from current itrigger count. + */ + executed = current_icount - last_icount; + itrigger_set_count(env, i, count - executed); + if (count == executed) { + do_trigger_action(env, i); + } + } else { + /* + * If itrigger is not enabled in this privilege mode, + * the number of executed instructions will be discard and + * the count field in itrigger will not change. + */ + timer_mod(env->itrigger_timer[i], + current_icount + count); + } + } +} + +static void riscv_itrigger_timer_cb(void *opaque) +{ + riscv_itrigger_update_count((CPURISCVState *)opaque); +} + +void riscv_itrigger_update_priv(CPURISCVState *env) +{ + riscv_itrigger_update_count(env); +} + +static target_ulong itrigger_validate(CPURISCVState *env, + target_ulong ctrl) +{ + target_ulong val; + + /* validate the generic part first */ + val = tdata1_validate(env, ctrl, TRIGGER_TYPE_INST_CNT); + + /* validate unimplemented (always zero) bits */ + warn_always_zero_bit(ctrl, ITRIGGER_ACTION, "action"); + warn_always_zero_bit(ctrl, ITRIGGER_HIT, "hit"); + warn_always_zero_bit(ctrl, ITRIGGER_PENDING, "pending"); + + /* keep the mode and attribute bits */ + val |= ctrl & (ITRIGGER_VU | ITRIGGER_VS | ITRIGGER_U | ITRIGGER_S | + ITRIGGER_M | ITRIGGER_COUNT); + + return val; +} + +static void itrigger_reg_write(CPURISCVState *env, target_ulong index, + int tdata_index, target_ulong val) +{ + target_ulong new_val; + + switch (tdata_index) { + case TDATA1: + /* set timer for icount */ + new_val = itrigger_validate(env, val); + if (new_val != env->tdata1[index]) { + env->tdata1[index] = new_val; + if (icount_enabled()) { + env->last_icount = icount_get_raw(); + /* set the count to timer */ + timer_mod(env->itrigger_timer[index], + env->last_icount + itrigger_get_count(env, index)); + } else { + env->itrigger_enabled = riscv_itrigger_enabled(env); + } + } + break; + case TDATA2: + qemu_log_mask(LOG_UNIMP, + "tdata2 is not supported for icount trigger\n"); + break; + case TDATA3: + qemu_log_mask(LOG_UNIMP, + "tdata3 is not supported for icount trigger\n"); + break; + default: + g_assert_not_reached(); + } + + return; +} + +static int itrigger_get_adjust_count(CPURISCVState *env) +{ + int count = itrigger_get_count(env, env->trigger_cur), executed; + if ((count != 0) && check_itrigger_priv(env, env->trigger_cur)) { + executed = icount_get_raw() - env->last_icount; + count += executed; + } + return count; +} target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index) { - tdata_read_func read_func = trigger_read_funcs[env->trigger_cur]; - - return read_func(env, env->trigger_cur, tdata_index); + int trigger_type; + switch (tdata_index) { + case TDATA1: + trigger_type = extract_trigger_type(env, + env->tdata1[env->trigger_cur]); + if ((trigger_type == TRIGGER_TYPE_INST_CNT) && icount_enabled()) { + return deposit64(env->tdata1[env->trigger_cur], 10, 14, + itrigger_get_adjust_count(env)); + } + return env->tdata1[env->trigger_cur]; + case TDATA2: + return env->tdata2[env->trigger_cur]; + case TDATA3: + return env->tdata3[env->trigger_cur]; + default: + g_assert_not_reached(); + } } void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val) { - tdata_write_func write_func = trigger_write_funcs[env->trigger_cur]; + int trigger_type; - return write_func(env, env->trigger_cur, tdata_index, val); + if (tdata_index == TDATA1) { + trigger_type = extract_trigger_type(env, val); + } else { + trigger_type = get_trigger_type(env, env->trigger_cur); + } + + switch (trigger_type) { + case TRIGGER_TYPE_AD_MATCH: + type2_reg_write(env, env->trigger_cur, tdata_index, val); + break; + case TRIGGER_TYPE_AD_MATCH6: + type6_reg_write(env, env->trigger_cur, tdata_index, val); + break; + case TRIGGER_TYPE_INST_CNT: + itrigger_reg_write(env, env->trigger_cur, tdata_index, val); + break; + case TRIGGER_TYPE_INT: + case TRIGGER_TYPE_EXCP: + case TRIGGER_TYPE_EXT_SRC: + qemu_log_mask(LOG_UNIMP, "trigger type: %d is not supported\n", + trigger_type); + break; + case TRIGGER_TYPE_NO_EXIST: + case TRIGGER_TYPE_UNAVAIL: + qemu_log_mask(LOG_GUEST_ERROR, "trigger type: %d does not exit\n", + trigger_type); + break; + default: + g_assert_not_reached(); + } +} + +target_ulong tinfo_csr_read(CPURISCVState *env) +{ + /* assume all triggers support the same types of triggers */ + return BIT(TRIGGER_TYPE_AD_MATCH) | + BIT(TRIGGER_TYPE_AD_MATCH6); } void riscv_cpu_debug_excp_handler(CPUState *cs) @@ -345,12 +762,11 @@ void riscv_cpu_debug_excp_handler(CPUState *cs) if (cs->watchpoint_hit) { if (cs->watchpoint_hit->flags & BP_CPU) { - cs->watchpoint_hit = NULL; - riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0); + do_trigger_action(env, DBG_ACTION_BP); } } else { if (cpu_breakpoint_test(cs, env->pc, BP_CPU)) { - riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0); + do_trigger_action(env, DBG_ACTION_BP); } } } @@ -362,18 +778,51 @@ bool riscv_cpu_debug_check_breakpoint(CPUState *cs) CPUBreakpoint *bp; target_ulong ctrl; target_ulong pc; + int trigger_type; int i; QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { - ctrl = env->type2_trig[i].mcontrol; - pc = env->type2_trig[i].maddress; + for (i = 0; i < RV_MAX_TRIGGERS; i++) { + trigger_type = get_trigger_type(env, i); - if ((ctrl & TYPE2_EXEC) && (bp->pc == pc)) { - /* check U/S/M bit against current privilege level */ - if ((ctrl >> 3) & BIT(env->priv)) { - return true; + switch (trigger_type) { + case TRIGGER_TYPE_AD_MATCH: + /* type 2 trigger cannot be fired in VU/VS mode */ + if (env->virt_enabled) { + return false; } + + ctrl = env->tdata1[i]; + pc = env->tdata2[i]; + + if ((ctrl & TYPE2_EXEC) && (bp->pc == pc)) { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } + } + break; + case TRIGGER_TYPE_AD_MATCH6: + ctrl = env->tdata1[i]; + pc = env->tdata2[i]; + + if ((ctrl & TYPE6_EXEC) && (bp->pc == pc)) { + if (env->virt_enabled) { + /* check VU/VS bit against current privilege level */ + if ((ctrl >> 23) & BIT(env->priv)) { + return true; + } + } else { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } + } + } + break; + default: + /* other trigger types are not supported or irrelevant */ + break; } } } @@ -387,39 +836,90 @@ bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) CPURISCVState *env = &cpu->env; target_ulong ctrl; target_ulong addr; + int trigger_type; int flags; int i; - for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { - ctrl = env->type2_trig[i].mcontrol; - addr = env->type2_trig[i].maddress; - flags = 0; + for (i = 0; i < RV_MAX_TRIGGERS; i++) { + trigger_type = get_trigger_type(env, i); - if (ctrl & TYPE2_LOAD) { - flags |= BP_MEM_READ; - } - if (ctrl & TYPE2_STORE) { - flags |= BP_MEM_WRITE; - } - - if ((wp->flags & flags) && (wp->vaddr == addr)) { - /* check U/S/M bit against current privilege level */ - if ((ctrl >> 3) & BIT(env->priv)) { - return true; + switch (trigger_type) { + case TRIGGER_TYPE_AD_MATCH: + /* type 2 trigger cannot be fired in VU/VS mode */ + if (env->virt_enabled) { + return false; } + + ctrl = env->tdata1[i]; + addr = env->tdata2[i]; + flags = 0; + + if (ctrl & TYPE2_LOAD) { + flags |= BP_MEM_READ; + } + if (ctrl & TYPE2_STORE) { + flags |= BP_MEM_WRITE; + } + + if ((wp->flags & flags) && (wp->vaddr == addr)) { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } + } + break; + case TRIGGER_TYPE_AD_MATCH6: + ctrl = env->tdata1[i]; + addr = env->tdata2[i]; + flags = 0; + + if (ctrl & TYPE6_LOAD) { + flags |= BP_MEM_READ; + } + if (ctrl & TYPE6_STORE) { + flags |= BP_MEM_WRITE; + } + + if ((wp->flags & flags) && (wp->vaddr == addr)) { + if (env->virt_enabled) { + /* check VU/VS bit against current privilege level */ + if ((ctrl >> 23) & BIT(env->priv)) { + return true; + } + } else { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } + } + } + break; + default: + /* other trigger types are not supported */ + break; } } return false; } -void riscv_trigger_init(CPURISCVState *env) +void riscv_trigger_realize(CPURISCVState *env) { - target_ulong type2 = trigger_type(env, TRIGGER_TYPE_AD_MATCH); int i; - /* type 2 triggers */ - for (i = 0; i < TRIGGER_TYPE2_NUM; i++) { + for (i = 0; i < RV_MAX_TRIGGERS; i++) { + env->itrigger_timer[i] = timer_new_ns(QEMU_CLOCK_VIRTUAL, + riscv_itrigger_timer_cb, env); + } +} + +void riscv_trigger_reset_hold(CPURISCVState *env) +{ + target_ulong tdata1 = build_tdata1(env, TRIGGER_TYPE_AD_MATCH, 0, 0); + int i; + + /* init to type 2 triggers */ + for (i = 0; i < RV_MAX_TRIGGERS; i++) { /* * type = TRIGGER_TYPE_AD_MATCH * dmode = 0 (both debug and M-mode can write tdata) @@ -433,9 +933,13 @@ void riscv_trigger_init(CPURISCVState *env) * chain = 0 (unimplemented, always 0) * match = 0 (always 0, when any compare value equals tdata2) */ - env->type2_trig[i].mcontrol = type2; - env->type2_trig[i].maddress = 0; - env->type2_trig[i].bp = NULL; - env->type2_trig[i].wp = NULL; + env->tdata1[i] = tdata1; + env->tdata2[i] = 0; + env->tdata3[i] = 0; + env->cpu_breakpoint[i] = NULL; + env->cpu_watchpoint[i] = NULL; + timer_del(env->itrigger_timer[i]); } + + env->mcontext = 0; } diff --git a/target/riscv/debug.h b/target/riscv/debug.h index 27b9cac6b4..5794aa6ee5 100644 --- a/target/riscv/debug.h +++ b/target/riscv/debug.h @@ -22,13 +22,7 @@ #ifndef RISCV_DEBUG_H #define RISCV_DEBUG_H -/* trigger indexes implemented */ -enum { - TRIGGER_TYPE2_IDX_0 = 0, - TRIGGER_TYPE2_IDX_1, - TRIGGER_TYPE2_NUM, - TRIGGER_NUM = TRIGGER_TYPE2_NUM -}; +#define RV_MAX_TRIGGERS 2 /* register index of tdata CSRs */ enum { @@ -46,24 +40,33 @@ typedef enum { TRIGGER_TYPE_EXCP = 5, /* exception trigger */ TRIGGER_TYPE_AD_MATCH6 = 6, /* new address/data match trigger */ TRIGGER_TYPE_EXT_SRC = 7, /* external source trigger */ - TRIGGER_TYPE_UNAVAIL = 15 /* trigger exists, but unavailable */ + TRIGGER_TYPE_UNAVAIL = 15, /* trigger exists, but unavailable */ + TRIGGER_TYPE_NUM } trigger_type_t; -typedef struct { - target_ulong mcontrol; - target_ulong maddress; - struct CPUBreakpoint *bp; - struct CPUWatchpoint *wp; -} type2_trigger_t; +/* actions */ +typedef enum { + DBG_ACTION_NONE = -1, /* sentinel value */ + DBG_ACTION_BP = 0, + DBG_ACTION_DBG_MODE, + DBG_ACTION_TRACE0, + DBG_ACTION_TRACE1, + DBG_ACTION_TRACE2, + DBG_ACTION_TRACE3, + DBG_ACTION_EXT_DBG0 = 8, + DBG_ACTION_EXT_DBG1 +} trigger_action_t; -/* tdata field masks */ +/* tdata1 field masks */ #define RV32_TYPE(t) ((uint32_t)(t) << 28) #define RV32_TYPE_MASK (0xf << 28) #define RV32_DMODE BIT(27) +#define RV32_DATA_MASK 0x7ffffff #define RV64_TYPE(t) ((uint64_t)(t) << 60) #define RV64_TYPE_MASK (0xfULL << 60) #define RV64_DMODE BIT_ULL(59) +#define RV64_DATA_MASK 0x7ffffffffffffff /* mcontrol field masks */ @@ -82,6 +85,24 @@ typedef struct { #define TYPE2_HIT BIT(20) #define TYPE2_SIZEHI (0x3 << 21) /* RV64 only */ +/* mcontrol6 field masks */ + +#define TYPE6_LOAD BIT(0) +#define TYPE6_STORE BIT(1) +#define TYPE6_EXEC BIT(2) +#define TYPE6_U BIT(3) +#define TYPE6_S BIT(4) +#define TYPE6_M BIT(6) +#define TYPE6_MATCH (0xf << 7) +#define TYPE6_CHAIN BIT(11) +#define TYPE6_ACTION (0xf << 12) +#define TYPE6_SIZE (0xf << 16) +#define TYPE6_TIMING BIT(20) +#define TYPE6_SELECT BIT(21) +#define TYPE6_HIT BIT(22) +#define TYPE6_VU BIT(23) +#define TYPE6_VS BIT(24) + /* access size */ enum { SIZE_ANY = 0, @@ -97,6 +118,17 @@ enum { SIZE_NUM = 16 }; +/* itrigger filed masks */ +#define ITRIGGER_ACTION 0x3f +#define ITRIGGER_U BIT(6) +#define ITRIGGER_S BIT(7) +#define ITRIGGER_PENDING BIT(8) +#define ITRIGGER_M BIT(9) +#define ITRIGGER_COUNT (0x3fff << 10) +#define ITRIGGER_HIT BIT(24) +#define ITRIGGER_VU BIT(25) +#define ITRIGGER_VS BIT(26) + bool tdata_available(CPURISCVState *env, int tdata_index); target_ulong tselect_csr_read(CPURISCVState *env); @@ -105,10 +137,15 @@ void tselect_csr_write(CPURISCVState *env, target_ulong val); target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index); void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val); +target_ulong tinfo_csr_read(CPURISCVState *env); + void riscv_cpu_debug_excp_handler(CPUState *cs); bool riscv_cpu_debug_check_breakpoint(CPUState *cs); bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp); -void riscv_trigger_init(CPURISCVState *env); +void riscv_trigger_realize(CPURISCVState *env); +void riscv_trigger_reset_hold(CPURISCVState *env); +bool riscv_itrigger_enabled(CPURISCVState *env); +void riscv_itrigger_update_priv(CPURISCVState *env); #endif /* RISCV_DEBUG_H */ diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index 5699c9517f..871a70a316 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -81,9 +81,41 @@ void helper_set_rounding_mode(CPURISCVState *env, uint32_t rm) set_float_rounding_mode(softrm, &env->fp_status); } -void helper_set_rod_rounding_mode(CPURISCVState *env) +void helper_set_rounding_mode_chkfrm(CPURISCVState *env, uint32_t rm) { - set_float_rounding_mode(float_round_to_odd, &env->fp_status); + int softrm; + + /* Always validate frm, even if rm != DYN. */ + if (unlikely(env->frm >= 5)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } + if (rm == RISCV_FRM_DYN) { + rm = env->frm; + } + switch (rm) { + case RISCV_FRM_RNE: + softrm = float_round_nearest_even; + break; + case RISCV_FRM_RTZ: + softrm = float_round_to_zero; + break; + case RISCV_FRM_RDN: + softrm = float_round_down; + break; + case RISCV_FRM_RUP: + softrm = float_round_up; + break; + case RISCV_FRM_RMM: + softrm = float_round_ties_away; + break; + case RISCV_FRM_ROD: + softrm = float_round_to_odd; + break; + default: + g_assert_not_reached(); + } + + set_float_rounding_mode(softrm, &env->fp_status); } static uint64_t do_fmadd_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2, @@ -216,8 +248,16 @@ uint64_t helper_fmin_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) float32 frs1 = check_nanbox_s(env, rs1); float32 frs2 = check_nanbox_s(env, rs2); return nanbox_s(env, env->priv_ver < PRIV_VERSION_1_11_0 ? - float32_minnum(frs1, frs2, &env->fp_status) : - float32_minimum_number(frs1, frs2, &env->fp_status)); + float32_minnum(frs1, frs2, &env->fp_status) : + float32_minimum_number(frs1, frs2, &env->fp_status)); +} + +uint64_t helper_fminm_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + float32 ret = float32_min(frs1, frs2, &env->fp_status); + return nanbox_s(env, ret); } uint64_t helper_fmax_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) @@ -225,8 +265,16 @@ uint64_t helper_fmax_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) float32 frs1 = check_nanbox_s(env, rs1); float32 frs2 = check_nanbox_s(env, rs2); return nanbox_s(env, env->priv_ver < PRIV_VERSION_1_11_0 ? - float32_maxnum(frs1, frs2, &env->fp_status) : - float32_maximum_number(frs1, frs2, &env->fp_status)); + float32_maxnum(frs1, frs2, &env->fp_status) : + float32_maximum_number(frs1, frs2, &env->fp_status)); +} + +uint64_t helper_fmaxm_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + float32 ret = float32_max(frs1, frs2, &env->fp_status); + return nanbox_s(env, ret); } uint64_t helper_fsqrt_s(CPURISCVState *env, uint64_t rs1) @@ -242,6 +290,13 @@ target_ulong helper_fle_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) return float32_le(frs1, frs2, &env->fp_status); } +target_ulong helper_fleq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + return float32_le_quiet(frs1, frs2, &env->fp_status); +} + target_ulong helper_flt_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float32 frs1 = check_nanbox_s(env, rs1); @@ -249,6 +304,13 @@ target_ulong helper_flt_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) return float32_lt(frs1, frs2, &env->fp_status); } +target_ulong helper_fltq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float32 frs1 = check_nanbox_s(env, rs1); + float32 frs2 = check_nanbox_s(env, rs2); + return float32_lt_quiet(frs1, frs2, &env->fp_status); +} + target_ulong helper_feq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float32 frs1 = check_nanbox_s(env, rs1); @@ -306,6 +368,30 @@ target_ulong helper_fclass_s(CPURISCVState *env, uint64_t rs1) return fclass_s(frs1); } +uint64_t helper_fround_s(CPURISCVState *env, uint64_t rs1) +{ + float_status *fs = &env->fp_status; + uint16_t nx_old = get_float_exception_flags(fs) & float_flag_inexact; + float32 frs1 = check_nanbox_s(env, rs1); + + frs1 = float32_round_to_int(frs1, fs); + + /* Restore the original NX flag. */ + uint16_t flags = get_float_exception_flags(fs); + flags &= ~float_flag_inexact; + flags |= nx_old; + set_float_exception_flags(flags, fs); + + return nanbox_s(env, frs1); +} + +uint64_t helper_froundnx_s(CPURISCVState *env, uint64_t rs1) +{ + float32 frs1 = check_nanbox_s(env, rs1); + frs1 = float32_round_to_int(frs1, &env->fp_status); + return nanbox_s(env, frs1); +} + uint64_t helper_fadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { return float64_add(frs1, frs2, &env->fp_status); @@ -329,15 +415,25 @@ uint64_t helper_fdiv_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) uint64_t helper_fmin_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { return env->priv_ver < PRIV_VERSION_1_11_0 ? - float64_minnum(frs1, frs2, &env->fp_status) : - float64_minimum_number(frs1, frs2, &env->fp_status); + float64_minnum(frs1, frs2, &env->fp_status) : + float64_minimum_number(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fminm_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_min(frs1, frs2, &env->fp_status); } uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { return env->priv_ver < PRIV_VERSION_1_11_0 ? - float64_maxnum(frs1, frs2, &env->fp_status) : - float64_maximum_number(frs1, frs2, &env->fp_status); + float64_maxnum(frs1, frs2, &env->fp_status) : + float64_maximum_number(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fmaxm_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_max(frs1, frs2, &env->fp_status); } uint64_t helper_fcvt_s_d(CPURISCVState *env, uint64_t rs1) @@ -361,11 +457,21 @@ target_ulong helper_fle_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) return float64_le(frs1, frs2, &env->fp_status); } +target_ulong helper_fleq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_le_quiet(frs1, frs2, &env->fp_status); +} + target_ulong helper_flt_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { return float64_lt(frs1, frs2, &env->fp_status); } +target_ulong helper_fltq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + return float64_lt_quiet(frs1, frs2, &env->fp_status); +} + target_ulong helper_feq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) { return float64_eq_quiet(frs1, frs2, &env->fp_status); @@ -376,6 +482,11 @@ target_ulong helper_fcvt_w_d(CPURISCVState *env, uint64_t frs1) return float64_to_int32(frs1, &env->fp_status); } +uint64_t helper_fcvtmod_w_d(CPURISCVState *env, uint64_t value) +{ + return float64_to_int32_modulo(value, float_round_to_zero, &env->fp_status); +} + target_ulong helper_fcvt_wu_d(CPURISCVState *env, uint64_t frs1) { return (int32_t)float64_to_uint32(frs1, &env->fp_status); @@ -416,6 +527,27 @@ target_ulong helper_fclass_d(uint64_t frs1) return fclass_d(frs1); } +uint64_t helper_fround_d(CPURISCVState *env, uint64_t frs1) +{ + float_status *fs = &env->fp_status; + uint16_t nx_old = get_float_exception_flags(fs) & float_flag_inexact; + + frs1 = float64_round_to_int(frs1, fs); + + /* Restore the original NX flag. */ + uint16_t flags = get_float_exception_flags(fs); + flags &= ~float_flag_inexact; + flags |= nx_old; + set_float_exception_flags(flags, fs); + + return frs1; +} + +uint64_t helper_froundnx_d(CPURISCVState *env, uint64_t frs1) +{ + return float64_round_to_int(frs1, &env->fp_status); +} + uint64_t helper_fadd_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float16 frs1 = check_nanbox_h(env, rs1); @@ -449,8 +581,16 @@ uint64_t helper_fmin_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) float16 frs1 = check_nanbox_h(env, rs1); float16 frs2 = check_nanbox_h(env, rs2); return nanbox_h(env, env->priv_ver < PRIV_VERSION_1_11_0 ? - float16_minnum(frs1, frs2, &env->fp_status) : - float16_minimum_number(frs1, frs2, &env->fp_status)); + float16_minnum(frs1, frs2, &env->fp_status) : + float16_minimum_number(frs1, frs2, &env->fp_status)); +} + +uint64_t helper_fminm_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + float16 ret = float16_min(frs1, frs2, &env->fp_status); + return nanbox_h(env, ret); } uint64_t helper_fmax_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) @@ -458,8 +598,16 @@ uint64_t helper_fmax_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) float16 frs1 = check_nanbox_h(env, rs1); float16 frs2 = check_nanbox_h(env, rs2); return nanbox_h(env, env->priv_ver < PRIV_VERSION_1_11_0 ? - float16_maxnum(frs1, frs2, &env->fp_status) : - float16_maximum_number(frs1, frs2, &env->fp_status)); + float16_maxnum(frs1, frs2, &env->fp_status) : + float16_maximum_number(frs1, frs2, &env->fp_status)); +} + +uint64_t helper_fmaxm_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + float16 ret = float16_max(frs1, frs2, &env->fp_status); + return nanbox_h(env, ret); } uint64_t helper_fsqrt_h(CPURISCVState *env, uint64_t rs1) @@ -475,6 +623,13 @@ target_ulong helper_fle_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) return float16_le(frs1, frs2, &env->fp_status); } +target_ulong helper_fleq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + return float16_le_quiet(frs1, frs2, &env->fp_status); +} + target_ulong helper_flt_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float16 frs1 = check_nanbox_h(env, rs1); @@ -482,6 +637,13 @@ target_ulong helper_flt_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) return float16_lt(frs1, frs2, &env->fp_status); } +target_ulong helper_fltq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) +{ + float16 frs1 = check_nanbox_h(env, rs1); + float16 frs2 = check_nanbox_h(env, rs2); + return float16_lt_quiet(frs1, frs2, &env->fp_status); +} + target_ulong helper_feq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2) { float16 frs1 = check_nanbox_h(env, rs1); @@ -495,6 +657,30 @@ target_ulong helper_fclass_h(CPURISCVState *env, uint64_t rs1) return fclass_h(frs1); } +uint64_t helper_fround_h(CPURISCVState *env, uint64_t rs1) +{ + float_status *fs = &env->fp_status; + uint16_t nx_old = get_float_exception_flags(fs) & float_flag_inexact; + float16 frs1 = check_nanbox_h(env, rs1); + + frs1 = float16_round_to_int(frs1, fs); + + /* Restore the original NX flag. */ + uint16_t flags = get_float_exception_flags(fs); + flags &= ~float_flag_inexact; + flags |= nx_old; + set_float_exception_flags(flags, fs); + + return nanbox_h(env, frs1); +} + +uint64_t helper_froundnx_h(CPURISCVState *env, uint64_t rs1) +{ + float16 frs1 = check_nanbox_s(env, rs1); + frs1 = float16_round_to_int(frs1, &env->fp_status); + return nanbox_h(env, frs1); +} + target_ulong helper_fcvt_w_h(CPURISCVState *env, uint64_t rs1) { float16 frs1 = check_nanbox_h(env, rs1); @@ -561,3 +747,15 @@ uint64_t helper_fcvt_d_h(CPURISCVState *env, uint64_t rs1) float16 frs1 = check_nanbox_h(env, rs1); return float16_to_float64(frs1, true, &env->fp_status); } + +uint64_t helper_fcvt_bf16_s(CPURISCVState *env, uint64_t rs1) +{ + float32 frs1 = check_nanbox_s(env, rs1); + return nanbox_h(env, float32_to_bfloat16(frs1, &env->fp_status)); +} + +uint64_t helper_fcvt_s_bf16(CPURISCVState *env, uint64_t rs1) +{ + float16 frs1 = check_nanbox_h(env, rs1); + return nanbox_s(env, bfloat16_to_float32(frs1, &env->fp_status)); +} diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index 9ed049c29e..be7a02cd90 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -18,6 +18,7 @@ #include "qemu/osdep.h" #include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "cpu.h" struct TypeSize { @@ -48,6 +49,7 @@ static const struct TypeSize vec_lanes[] = { int riscv_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cs); RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; target_ulong tmp; @@ -60,7 +62,7 @@ int riscv_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) return 0; } - switch (env->misa_mxl_max) { + switch (mcc->misa_mxl_max) { case MXL_RV32: return gdb_get_reg32(mem_buf, tmp); case MXL_RV64: @@ -74,12 +76,13 @@ int riscv_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cs); RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; int length = 0; target_ulong tmp; - switch (env->misa_mxl_max) { + switch (mcc->misa_mxl_max) { case MXL_RV32: tmp = (int32_t)ldl_p(mem_buf); length = 4; @@ -105,8 +108,11 @@ int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return length; } -static int riscv_gdb_get_fpu(CPURISCVState *env, GByteArray *buf, int n) +static int riscv_gdb_get_fpu(CPUState *cs, GByteArray *buf, int n) { + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + if (n < 32) { if (env->misa_ext & RVD) { return gdb_get_reg64(buf, env->fpr[n]); @@ -114,84 +120,27 @@ static int riscv_gdb_get_fpu(CPURISCVState *env, GByteArray *buf, int n) if (env->misa_ext & 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; - int result; - /* - * CSR_FFLAGS is at index 1 in csr_register, and gdb says it is FP - * register 33, so we recalculate the map index. - * This also works for CSR_FRM and CSR_FCSR. - */ - result = riscv_csrrw_debug(env, n - 32, &val, - 0, 0); - if (result == RISCV_EXCP_NONE) { - return gdb_get_regl(buf, val); - } } return 0; } -static int riscv_gdb_set_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) +static int riscv_gdb_set_fpu(CPUState *cs, uint8_t *mem_buf, int n) { + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + if (n < 32) { env->fpr[n] = ldq_p(mem_buf); /* always 64-bit */ return sizeof(uint64_t); - /* there is hole between ft11 and fflags in fpu.xml */ - } else if (n < 36 && n > 32) { - target_ulong val = ldtul_p(mem_buf); - int result; - /* - * CSR_FFLAGS is at index 1 in csr_register, and gdb says it is FP - * register 33, so we recalculate the map index. - * This also works for CSR_FRM and CSR_FCSR. - */ - result = riscv_csrrw_debug(env, n - 32, NULL, - val, -1); - if (result == RISCV_EXCP_NONE) { - return sizeof(target_ulong); - } } return 0; } -/* - * Convert register index number passed by GDB to the correspond - * vector CSR number. Vector CSRs are defined after vector registers - * in dynamic generated riscv-vector.xml, thus the starting register index - * of vector CSRs is 32. - * Return 0 if register index number is out of range. - */ -static int riscv_gdb_vector_csrno(int num_regs) +static int riscv_gdb_get_vector(CPUState *cs, GByteArray *buf, int n) { - /* - * The order of vector CSRs in the switch case - * should match with the order defined in csr_ops[]. - */ - switch (num_regs) { - case 32: - return CSR_VSTART; - case 33: - return CSR_VXSAT; - case 34: - return CSR_VXRM; - case 35: - return CSR_VCSR; - case 36: - return CSR_VL; - case 37: - return CSR_VTYPE; - case 38: - return CSR_VLENB; - default: - /* Unknown register. */ - return 0; - } -} - -static int riscv_gdb_get_vector(CPURISCVState *env, GByteArray *buf, int n) -{ - uint16_t vlenb = env_archcpu(env)->cfg.vlen >> 3; + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + uint16_t vlenb = cpu->cfg.vlenb; if (n < 32) { int i; int cnt = 0; @@ -202,25 +151,14 @@ static int riscv_gdb_get_vector(CPURISCVState *env, GByteArray *buf, int n) return cnt; } - int csrno = riscv_gdb_vector_csrno(n); - - if (!csrno) { - return 0; - } - - target_ulong val = 0; - int result = riscv_csrrw_debug(env, csrno, &val, 0, 0); - - if (result == 0) { - return gdb_get_regl(buf, val); - } - return 0; } -static int riscv_gdb_set_vector(CPURISCVState *env, uint8_t *mem_buf, int n) +static int riscv_gdb_set_vector(CPUState *cs, uint8_t *mem_buf, int n) { - uint16_t vlenb = env_archcpu(env)->cfg.vlen >> 3; + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + uint16_t vlenb = cpu->cfg.vlenb; if (n < 32) { int i; for (i = 0; i < vlenb; i += 8) { @@ -229,24 +167,14 @@ static int riscv_gdb_set_vector(CPURISCVState *env, uint8_t *mem_buf, int n) return vlenb; } - int csrno = riscv_gdb_vector_csrno(n); - - if (!csrno) { - return 0; - } - - target_ulong val = ldtul_p(mem_buf); - int result = riscv_csrrw_debug(env, csrno, NULL, val, -1); - - if (result == 0) { - return sizeof(target_ulong); - } - return 0; } -static int riscv_gdb_get_csr(CPURISCVState *env, GByteArray *buf, int n) +static int riscv_gdb_get_csr(CPUState *cs, GByteArray *buf, int n) { + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + if (n < CSR_TABLE_SIZE) { target_ulong val = 0; int result; @@ -259,8 +187,11 @@ static int riscv_gdb_get_csr(CPURISCVState *env, GByteArray *buf, int n) return 0; } -static int riscv_gdb_set_csr(CPURISCVState *env, uint8_t *mem_buf, int n) +static int riscv_gdb_set_csr(CPUState *cs, uint8_t *mem_buf, int n) { + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + if (n < CSR_TABLE_SIZE) { target_ulong val = ldtul_p(mem_buf); int result; @@ -273,25 +204,31 @@ static int riscv_gdb_set_csr(CPURISCVState *env, uint8_t *mem_buf, int n) return 0; } -static int riscv_gdb_get_virtual(CPURISCVState *cs, GByteArray *buf, int n) +static int riscv_gdb_get_virtual(CPUState *cs, GByteArray *buf, int n) { if (n == 0) { #ifdef CONFIG_USER_ONLY return gdb_get_regl(buf, 0); #else - return gdb_get_regl(buf, cs->priv); + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + + return gdb_get_regl(buf, env->priv); #endif } return 0; } -static int riscv_gdb_set_virtual(CPURISCVState *cs, uint8_t *mem_buf, int n) +static int riscv_gdb_set_virtual(CPUState *cs, uint8_t *mem_buf, int n) { if (n == 0) { #ifndef CONFIG_USER_ONLY - cs->priv = ldtul_p(mem_buf) & 0x3; - if (cs->priv == PRV_H) { - cs->priv = PRV_S; + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + + env->priv = ldtul_p(mem_buf) & 0x3; + if (env->priv == PRV_RESERVED) { + env->priv = PRV_S; } #endif return sizeof(target_ulong); @@ -299,139 +236,135 @@ static int riscv_gdb_set_virtual(CPURISCVState *cs, uint8_t *mem_buf, int n) return 0; } -static int riscv_gen_dynamic_csr_xml(CPUState *cs, int base_reg) +static GDBFeature *riscv_gen_dynamic_csr_feature(CPUState *cs, int base_reg) { + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cs); RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - GString *s = g_string_new(NULL); + GDBFeatureBuilder builder; riscv_csr_predicate_fn predicate; - int bitsize = 16 << env->misa_mxl_max; + int bitsize = riscv_cpu_max_xlen(mcc); + const char *name; int i; +#if !defined(CONFIG_USER_ONLY) + env->debugger = true; +#endif + /* Until gdb knows about 128-bit registers */ if (bitsize > 64) { bitsize = 64; } - g_string_printf(s, ""); - g_string_append_printf(s, ""); - g_string_append_printf(s, ""); + gdb_feature_builder_init(&builder, &cpu->dyn_csr_feature, + "org.gnu.gdb.riscv.csr", "riscv-csr.xml", + base_reg); for (i = 0; i < CSR_TABLE_SIZE; i++) { + if (env->priv_ver < csr_ops[i].min_priv_ver) { + continue; + } predicate = csr_ops[i].predicate; if (predicate && (predicate(env, i) == RISCV_EXCP_NONE)) { - if (csr_ops[i].name) { - g_string_append_printf(s, "", base_reg + i); + + gdb_feature_builder_append_reg(&builder, name, bitsize, i, + "int", NULL); } } - g_string_append_printf(s, ""); + gdb_feature_builder_end(&builder); - cpu->dyn_csr_xml = g_string_free(s, false); - return CSR_TABLE_SIZE; +#if !defined(CONFIG_USER_ONLY) + env->debugger = false; +#endif + + return &cpu->dyn_csr_feature; } -static int ricsv_gen_dynamic_vector_xml(CPUState *cs, int base_reg) +static GDBFeature *ricsv_gen_dynamic_vector_feature(CPUState *cs, int base_reg) { RISCVCPU *cpu = RISCV_CPU(cs); - GString *s = g_string_new(NULL); - g_autoptr(GString) ts = g_string_new(""); - int reg_width = cpu->cfg.vlen; - int num_regs = 0; + int reg_width = cpu->cfg.vlenb; + GDBFeatureBuilder builder; int i; - g_string_printf(s, ""); - g_string_append_printf(s, ""); - g_string_append_printf(s, ""); + gdb_feature_builder_init(&builder, &cpu->dyn_vreg_feature, + "org.gnu.gdb.riscv.vector", "riscv-vector.xml", + base_reg); /* 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, "%s", vec_lanes[i].id); - g_string_append_printf(s, - "", - ts->str, vec_lanes[i].gdb_type, count); + gdb_feature_builder_append_tag( + &builder, "", + vec_lanes[i].id, vec_lanes[i].gdb_type, count); } /* Define unions */ - g_string_append_printf(s, ""); + gdb_feature_builder_append_tag(&builder, ""); for (i = 0; i < ARRAY_SIZE(vec_lanes); i++) { - g_string_append_printf(s, "", - vec_lanes[i].suffix, - vec_lanes[i].id); + gdb_feature_builder_append_tag(&builder, + "", + vec_lanes[i].suffix, vec_lanes[i].id); } - g_string_append(s, ""); + gdb_feature_builder_append_tag(&builder, ""); /* Define vector registers */ for (i = 0; i < 32; i++) { - g_string_append_printf(s, - "", - i, reg_width, base_reg++); - num_regs++; + gdb_feature_builder_append_reg(&builder, g_strdup_printf("v%d", i), + reg_width, i, "riscv_vector", "vector"); } - /* Define vector CSRs */ - const char *vector_csrs[7] = { - "vstart", "vxsat", "vxrm", "vcsr", - "vl", "vtype", "vlenb" - }; + gdb_feature_builder_end(&builder); - for (i = 0; i < 7; i++) { - g_string_append_printf(s, - "", - vector_csrs[i], TARGET_LONG_BITS, base_reg++); - num_regs++; - } - - g_string_append_printf(s, ""); - - cpu->dyn_vreg_xml = g_string_free(s, false); - return num_regs; + return &cpu->dyn_vreg_feature; } void riscv_cpu_register_gdb_regs_for_features(CPUState *cs) { + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cs); RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; if (env->misa_ext & RVD) { gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu, - 36, "riscv-64bit-fpu.xml", 0); + gdb_find_static_feature("riscv-64bit-fpu.xml"), + 0); } else if (env->misa_ext & RVF) { gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu, - 36, "riscv-32bit-fpu.xml", 0); + gdb_find_static_feature("riscv-32bit-fpu.xml"), + 0); } if (env->misa_ext & RVV) { - gdb_register_coprocessor(cs, riscv_gdb_get_vector, riscv_gdb_set_vector, - ricsv_gen_dynamic_vector_xml(cs, - cs->gdb_num_regs), - "riscv-vector.xml", 0); + gdb_register_coprocessor(cs, riscv_gdb_get_vector, + riscv_gdb_set_vector, + ricsv_gen_dynamic_vector_feature(cs, cs->gdb_num_regs), + 0); } - switch (env->misa_mxl_max) { + switch (mcc->misa_mxl_max) { case MXL_RV32: gdb_register_coprocessor(cs, riscv_gdb_get_virtual, riscv_gdb_set_virtual, - 1, "riscv-32bit-virtual.xml", 0); + gdb_find_static_feature("riscv-32bit-virtual.xml"), + 0); break; case MXL_RV64: case MXL_RV128: gdb_register_coprocessor(cs, riscv_gdb_get_virtual, riscv_gdb_set_virtual, - 1, "riscv-64bit-virtual.xml", 0); + gdb_find_static_feature("riscv-64bit-virtual.xml"), + 0); break; default: g_assert_not_reached(); } - gdb_register_coprocessor(cs, riscv_gdb_get_csr, riscv_gdb_set_csr, - riscv_gen_dynamic_csr_xml(cs, cs->gdb_num_regs), - "riscv-csr.xml", 0); + if (cpu->cfg.ext_zicsr) { + gdb_register_coprocessor(cs, riscv_gdb_get_csr, riscv_gdb_set_csr, + riscv_gen_dynamic_csr_feature(cs, cs->gdb_num_regs), + 0); + } } diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 4ef3b2251d..8a63523851 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -3,7 +3,7 @@ DEF_HELPER_2(raise_exception, noreturn, env, i32) /* Floating Point - rounding mode */ DEF_HELPER_FLAGS_2(set_rounding_mode, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_FLAGS_1(set_rod_rounding_mode, TCG_CALL_NO_WG, void, env) +DEF_HELPER_FLAGS_2(set_rounding_mode_chkfrm, TCG_CALL_NO_WG, void, env, i32) /* Floating Point - fused */ DEF_HELPER_FLAGS_4(fmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) @@ -25,10 +25,14 @@ DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmul_s, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmin_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fminm_s, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmax_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmaxm_s, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_3(fle_s, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(fleq_s, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_3(flt_s, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(fltq_s, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_3(feq_s, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_2(fcvt_w_s, TCG_CALL_NO_RWG, tl, env, i64) DEF_HELPER_FLAGS_2(fcvt_wu_s, TCG_CALL_NO_RWG, tl, env, i64) @@ -39,6 +43,8 @@ DEF_HELPER_FLAGS_2(fcvt_s_wu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_s_l, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_s_lu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fclass_s, TCG_CALL_NO_RWG_SE, tl, env, i64) +DEF_HELPER_FLAGS_2(fround_s, TCG_CALL_NO_RWG_SE, i64, env, i64) +DEF_HELPER_FLAGS_2(froundnx_s, TCG_CALL_NO_RWG_SE, i64, env, i64) /* Floating Point - Double Precision */ DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64) @@ -46,14 +52,19 @@ DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmul_d, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmin_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fminm_d, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmax_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmaxm_d, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_2(fcvt_s_d, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_2(fcvt_d_s, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_3(fle_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(fleq_d, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_3(flt_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(fltq_d, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_3(feq_d, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_2(fcvt_w_d, TCG_CALL_NO_RWG, tl, env, i64) +DEF_HELPER_FLAGS_2(fcvtmod_w_d, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_2(fcvt_wu_d, TCG_CALL_NO_RWG, tl, env, i64) DEF_HELPER_FLAGS_2(fcvt_l_d, TCG_CALL_NO_RWG, tl, env, i64) DEF_HELPER_FLAGS_2(fcvt_lu_d, TCG_CALL_NO_RWG, tl, env, i64) @@ -62,6 +73,8 @@ DEF_HELPER_FLAGS_2(fcvt_d_wu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_d_l, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_d_lu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_1(fclass_d, TCG_CALL_NO_RWG_SE, tl, i64) +DEF_HELPER_FLAGS_2(fround_d, TCG_CALL_NO_RWG_SE, i64, env, i64) +DEF_HELPER_FLAGS_2(froundnx_d, TCG_CALL_NO_RWG_SE, i64, env, i64) /* Bitmanip */ DEF_HELPER_FLAGS_2(clmul, TCG_CALL_NO_RWG_SE, tl, tl, tl) @@ -78,10 +91,14 @@ DEF_HELPER_FLAGS_3(fsub_h, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmul_h, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fdiv_h, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmin_h, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fminm_h, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fmax_h, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmaxm_h, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_2(fsqrt_h, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_3(fle_h, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(fleq_h, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_3(flt_h, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(fltq_h, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_3(feq_h, TCG_CALL_NO_RWG, tl, env, i64, i64) DEF_HELPER_FLAGS_2(fcvt_s_h, TCG_CALL_NO_RWG, i64, env, i64) DEF_HELPER_FLAGS_2(fcvt_h_s, TCG_CALL_NO_RWG, i64, env, i64) @@ -96,6 +113,13 @@ DEF_HELPER_FLAGS_2(fcvt_h_wu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_h_l, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fcvt_h_lu, TCG_CALL_NO_RWG, i64, env, tl) DEF_HELPER_FLAGS_2(fclass_h, TCG_CALL_NO_RWG_SE, tl, env, i64) +DEF_HELPER_FLAGS_2(fround_h, TCG_CALL_NO_RWG_SE, i64, env, i64) +DEF_HELPER_FLAGS_2(froundnx_h, TCG_CALL_NO_RWG_SE, i64, env, i64) + +/* Cache-block operations */ +DEF_HELPER_2(cbo_clean_flush, void, env, tl) +DEF_HELPER_2(cbo_inval, void, env, tl) +DEF_HELPER_2(cbo_zero, void, env, tl) /* Special functions */ DEF_HELPER_2(csrr, tl, env, int) @@ -109,14 +133,25 @@ DEF_HELPER_1(sret, tl, env) DEF_HELPER_1(mret, tl, env) DEF_HELPER_1(wfi, void, env) DEF_HELPER_1(tlb_flush, void, env) +DEF_HELPER_1(tlb_flush_all, void, env) +/* Native Debug */ +DEF_HELPER_1(itrigger_match, void, env) #endif /* Hypervisor functions */ #ifndef CONFIG_USER_ONLY DEF_HELPER_1(hyp_tlb_flush, void, env) DEF_HELPER_1(hyp_gvma_tlb_flush, void, env) -DEF_HELPER_2(hyp_hlvx_hu, tl, env, tl) -DEF_HELPER_2(hyp_hlvx_wu, tl, env, tl) +DEF_HELPER_FLAGS_2(hyp_hlv_bu, TCG_CALL_NO_WG, tl, env, tl) +DEF_HELPER_FLAGS_2(hyp_hlv_hu, TCG_CALL_NO_WG, tl, env, tl) +DEF_HELPER_FLAGS_2(hyp_hlv_wu, TCG_CALL_NO_WG, tl, env, tl) +DEF_HELPER_FLAGS_2(hyp_hlv_d, TCG_CALL_NO_WG, tl, env, tl) +DEF_HELPER_FLAGS_2(hyp_hlvx_hu, TCG_CALL_NO_WG, tl, env, tl) +DEF_HELPER_FLAGS_2(hyp_hlvx_wu, TCG_CALL_NO_WG, tl, env, tl) +DEF_HELPER_FLAGS_3(hyp_hsv_b, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(hyp_hsv_h, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(hyp_hsv_w, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(hyp_hsv_d, TCG_CALL_NO_WG, void, env, tl, tl) #endif /* Vector functions */ @@ -1009,9 +1044,12 @@ DEF_HELPER_6(vwredsum_vs_b, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_6(vwredsum_vs_h, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_6(vwredsum_vs_w, void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_6(vfredsum_vs_h, void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_6(vfredsum_vs_w, void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_6(vfredsum_vs_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfredusum_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfredusum_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfredusum_vs_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfredosum_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfredosum_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfredosum_vs_d, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_6(vfredmax_vs_h, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_6(vfredmax_vs_w, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_6(vfredmax_vs_d, void, ptr, ptr, ptr, ptr, env, i32) @@ -1019,8 +1057,10 @@ DEF_HELPER_6(vfredmin_vs_h, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_6(vfredmin_vs_w, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_6(vfredmin_vs_d, void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_6(vfwredsum_vs_h, void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_6(vfwredsum_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwredusum_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwredusum_vs_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwredosum_vs_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwredosum_vs_w, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_6(vmand_mm, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_6(vmnand_mm, void, ptr, ptr, ptr, ptr, env, i32) @@ -1129,3 +1169,114 @@ DEF_HELPER_FLAGS_1(aes64im, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_3(sm4ed, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) DEF_HELPER_FLAGS_3(sm4ks, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) + +/* Zce helper */ +DEF_HELPER_FLAGS_2(cm_jalt, TCG_CALL_NO_WG, tl, env, i32) + +/* BF16 functions */ +DEF_HELPER_FLAGS_2(fcvt_bf16_s, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_2(fcvt_s_bf16, TCG_CALL_NO_RWG, i64, env, i64) + +DEF_HELPER_5(vfncvtbf16_f_f_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vfwcvtbf16_f_f_v, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_6(vfwmaccbf16_vv, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vfwmaccbf16_vf, void, ptr, ptr, i64, ptr, env, i32) + +/* Vector crypto functions */ +DEF_HELPER_6(vclmul_vv, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vclmul_vx, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vclmulh_vv, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vclmulh_vx, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vror_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vror_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vror_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vror_vv_d, void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_6(vror_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vror_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vror_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vror_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vrol_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vrol_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vrol_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vrol_vv_d, void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_6(vrol_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vrol_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vrol_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vrol_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_5(vrev8_v_b, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vrev8_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vrev8_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vrev8_v_d, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vbrev8_v_b, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vbrev8_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vbrev8_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vbrev8_v_d, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vbrev_v_b, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vbrev_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vbrev_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vbrev_v_d, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_5(vclz_v_b, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vclz_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vclz_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vclz_v_d, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vctz_v_b, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vctz_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vctz_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vctz_v_d, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vcpop_v_b, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vcpop_v_h, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vcpop_v_w, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vcpop_v_d, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_6(vwsll_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsll_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsll_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwsll_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsll_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwsll_vx_w, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_6(vandn_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vandn_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vandn_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vandn_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vandn_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vandn_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vandn_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vandn_vx_d, void, ptr, ptr, tl, ptr, env, i32) + +DEF_HELPER_2(egs_check, void, i32, env) + +DEF_HELPER_4(vaesef_vv, void, ptr, ptr, env, i32) +DEF_HELPER_4(vaesef_vs, void, ptr, ptr, env, i32) +DEF_HELPER_4(vaesdf_vv, void, ptr, ptr, env, i32) +DEF_HELPER_4(vaesdf_vs, void, ptr, ptr, env, i32) +DEF_HELPER_4(vaesem_vv, void, ptr, ptr, env, i32) +DEF_HELPER_4(vaesem_vs, void, ptr, ptr, env, i32) +DEF_HELPER_4(vaesdm_vv, void, ptr, ptr, env, i32) +DEF_HELPER_4(vaesdm_vs, void, ptr, ptr, env, i32) +DEF_HELPER_4(vaesz_vs, void, ptr, ptr, env, i32) +DEF_HELPER_5(vaeskf1_vi, void, ptr, ptr, i32, env, i32) +DEF_HELPER_5(vaeskf2_vi, void, ptr, ptr, i32, env, i32) + +DEF_HELPER_5(vsha2ms_vv, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vsha2ch32_vv, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vsha2ch64_vv, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vsha2cl32_vv, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vsha2cl64_vv, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_5(vsm3me_vv, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_5(vsm3c_vi, void, ptr, ptr, i32, env, i32) + +DEF_HELPER_5(vghsh_vv, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_4(vgmul_vv, void, ptr, ptr, env, i32) + +DEF_HELPER_5(vsm4k_vi, void, ptr, ptr, i32, env, i32) +DEF_HELPER_4(vsm4r_vv, void, ptr, ptr, env, i32) +DEF_HELPER_4(vsm4r_vs, void, ptr, ptr, env, i32) diff --git a/target/riscv/insn16.decode b/target/riscv/insn16.decode index 02c8f61b48..b96c534e73 100644 --- a/target/riscv/insn16.decode +++ b/target/riscv/insn16.decode @@ -21,6 +21,8 @@ %rs1_3 7:3 !function=ex_rvc_register %rs2_3 2:3 !function=ex_rvc_register %rs2_5 2:5 +%r1s 7:3 !function=ex_sreg_register +%r2s 2:3 !function=ex_sreg_register # Immediates: %imm_ci 12:s1 2:5 @@ -31,7 +33,8 @@ %imm_cb 12:s1 5:2 2:1 10:2 3:2 !function=ex_shift_1 %imm_cj 12:s1 8:1 9:2 6:1 7:1 2:1 11:1 3:3 !function=ex_shift_1 -%shimm_6bit 12:1 2:5 !function=ex_rvc_shifti +%shlimm_6bit 12:1 2:5 !function=ex_rvc_shiftli +%shrimm_6bit 12:1 2:5 !function=ex_rvc_shiftri %uimm_6bit_lq 2:4 12:1 6:1 !function=ex_shift_4 %uimm_6bit_ld 2:3 12:1 5:2 !function=ex_shift_3 %uimm_6bit_lw 2:2 12:1 4:3 !function=ex_shift_2 @@ -42,6 +45,11 @@ %imm_addi16sp 12:s1 3:2 5:1 2:1 6:1 !function=ex_shift_4 %imm_lui 12:s1 2:5 !function=ex_shift_12 +%uimm_cl_b 5:1 6:1 +%uimm_cl_h 5:1 !function=ex_shift_1 +%spimm 2:2 !function=ex_shift_4 +%urlist 4:4 +%index 2:8 # Argument sets imported from insn32.decode: &empty !extern @@ -52,7 +60,11 @@ &b imm rs2 rs1 !extern &u imm rd !extern &shift shamt rs1 rd !extern +&r2 rd rs1 !extern +&r2_s rs1 rs2 !extern +&cmpp urlist spimm +&cmjt index # Formats 16: @cr .... ..... ..... .. &r rs2=%rs2_5 rs1=%rd %rd @@ -82,12 +94,21 @@ @c_addi16sp ... . ..... ..... .. &i imm=%imm_addi16sp rs1=2 rd=2 @c_shift ... . .. ... ..... .. \ - &shift rd=%rs1_3 rs1=%rs1_3 shamt=%shimm_6bit + &shift rd=%rs1_3 rs1=%rs1_3 shamt=%shrimm_6bit @c_shift2 ... . .. ... ..... .. \ - &shift rd=%rd rs1=%rd shamt=%shimm_6bit + &shift rd=%rd rs1=%rd shamt=%shlimm_6bit @c_andi ... . .. ... ..... .. &i imm=%imm_ci rs1=%rs1_3 rd=%rs1_3 +@cu ... ... ... .. ... .. &r2 rs1=%rs1_3 rd=%rs1_3 +@cl_b ... . .. ... .. ... .. &i imm=%uimm_cl_b rs1=%rs1_3 rd=%rs2_3 +@cl_h ... . .. ... .. ... .. &i imm=%uimm_cl_h rs1=%rs1_3 rd=%rs2_3 +@cs_b ... . .. ... .. ... .. &s imm=%uimm_cl_b rs1=%rs1_3 rs2=%rs2_3 +@cs_h ... . .. ... .. ... .. &s imm=%uimm_cl_h rs1=%rs1_3 rs2=%rs2_3 +@cm_pp ... ... ........ .. &cmpp %urlist %spimm +@cm_mv ... ... ... .. ... .. &r2_s rs2=%r2s rs1=%r1s +@cm_jt ... ... ........ .. &cmjt %index + # *** RV32/64C Standard Extension (Quadrant 0) *** { # Opcode of all zeros is illegal; rd != 0, nzuimm == 0 is reserved. @@ -96,23 +117,23 @@ } { lq 001 ... ... .. ... 00 @cl_q - fld 001 ... ... .. ... 00 @cl_d + c_fld 001 ... ... .. ... 00 @cl_d } lw 010 ... ... .. ... 00 @cl_w { sq 101 ... ... .. ... 00 @cs_q - fsd 101 ... ... .. ... 00 @cs_d + c_fsd 101 ... ... .. ... 00 @cs_d } sw 110 ... ... .. ... 00 @cs_w # *** RV32C and RV64C specific Standard Extension (Quadrant 0) *** { ld 011 ... ... .. ... 00 @cl_d - flw 011 ... ... .. ... 00 @cl_w + c_flw 011 ... ... .. ... 00 @cl_w } { sd 111 ... ... .. ... 00 @cs_d - fsw 111 ... ... .. ... 00 @cs_w + c_fsw 111 ... ... .. ... 00 @cs_w } # *** RV32/64C Standard Extension (Quadrant 1) *** @@ -147,7 +168,7 @@ addw 100 1 11 ... 01 ... 01 @cs_2 slli 000 . ..... ..... 10 @c_shift2 { lq 001 ... ... .. ... 10 @c_lqsp - fld 001 . ..... ..... 10 @c_ldsp + c_fld 001 . ..... ..... 10 @c_ldsp } { illegal 010 - 00000 ----- 10 # c.lwsp, RES rd=0 @@ -165,7 +186,19 @@ slli 000 . ..... ..... 10 @c_shift2 } { sq 101 ... ... .. ... 10 @c_sqsp - fsd 101 ...... ..... 10 @c_sdsp + c_fsd 101 ...... ..... 10 @c_sdsp + + # *** RV64 and RV32 Zcmp/Zcmt Extension *** + [ + cm_push 101 11000 .... .. 10 @cm_pp + cm_pop 101 11010 .... .. 10 @cm_pp + cm_popret 101 11110 .... .. 10 @cm_pp + cm_popretz 101 11100 .... .. 10 @cm_pp + cm_mva01s 101 011 ... 11 ... 10 @cm_mv + cm_mvsa01 101 011 ... 01 ... 10 @cm_mv + + cm_jalt 101 000 ........ 10 @cm_jt + ] } sw 110 . ..... ..... 10 @c_swsp @@ -173,9 +206,23 @@ sw 110 . ..... ..... 10 @c_swsp { c64_illegal 011 - 00000 ----- 10 # c.ldsp, RES rd=0 ld 011 . ..... ..... 10 @c_ldsp - flw 011 . ..... ..... 10 @c_lwsp + c_flw 011 . ..... ..... 10 @c_lwsp } { sd 111 . ..... ..... 10 @c_sdsp - fsw 111 . ..... ..... 10 @c_swsp + c_fsw 111 . ..... ..... 10 @c_swsp } + +# *** RV64 and RV32 Zcb Extension *** +c_zext_b 100 111 ... 11 000 01 @cu +c_sext_b 100 111 ... 11 001 01 @cu +c_zext_h 100 111 ... 11 010 01 @cu +c_sext_h 100 111 ... 11 011 01 @cu +c_zext_w 100 111 ... 11 100 01 @cu +c_not 100 111 ... 11 101 01 @cu +c_mul 100 111 ... 10 ... 01 @cs_2 +c_lbu 100 000 ... .. ... 00 @cl_b +c_lhu 100 001 ... 0. ... 00 @cl_h +c_lh 100 001 ... 1. ... 00 @cl_h +c_sb 100 010 ... .. ... 00 @cs_b +c_sh 100 011 ... 0. ... 00 @cs_h diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 4033565393..f22df04cfd 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -37,6 +37,7 @@ %imm_u 12:s20 !function=ex_shift_12 %imm_bs 30:2 !function=ex_shift_3 %imm_rnum 20:4 +%imm_z6 26:1 15:5 # Argument sets: &empty @@ -74,6 +75,7 @@ @r_rm ....... ..... ..... ... ..... ....... %rs2 %rs1 %rm %rd @r2_rm ....... ..... ..... ... ..... ....... %rs1 %rm %rd @r2 ....... ..... ..... ... ..... ....... &r2 %rs1 %rd +@r2_vm_1 ...... . ..... ..... ... ..... ....... &rmr vm=1 %rs2 %rd @r2_nfvm ... ... vm:1 ..... ..... ... ..... ....... &r2nfvm %nf %rs1 %rd @r2_vm ...... vm:1 ..... ..... ... ..... ....... &rmr %rs2 %rd @r1_vm ...... vm:1 ..... ..... ... ..... ....... %rd @@ -82,6 +84,7 @@ @r_vm ...... vm:1 ..... ..... ... ..... ....... &rmrr %rs2 %rs1 %rd @r_vm_1 ...... . ..... ..... ... ..... ....... &rmrr vm=1 %rs2 %rs1 %rd @r_vm_0 ...... . ..... ..... ... ..... ....... &rmrr vm=0 %rs2 %rs1 %rd +@r2_zimm6 ..... . vm:1 ..... ..... ... ..... ....... &rmrr %rs2 rs1=%imm_z6 %rd @r2_zimm11 . zimm:11 ..... ... ..... ....... %rs1 %rd @r2_zimm10 .. zimm:10 ..... ... ..... ....... %rs1 %rd @r2_s ....... ..... ..... ... ..... ....... %rs2 %rs1 @@ -134,6 +137,7 @@ addi ............ ..... 000 ..... 0010011 @i slti ............ ..... 010 ..... 0010011 @i sltiu ............ ..... 011 ..... 0010011 @i xori ............ ..... 100 ..... 0010011 @i +# cbo.prefetch_{i,r,m} instructions are ori with rd=x0 and not decoded. ori ............ ..... 110 ..... 0010011 @i andi ............ ..... 111 ..... 0010011 @i slli 00000. ...... ..... 001 ..... 0010011 @sh @@ -149,7 +153,12 @@ srl 0000000 ..... ..... 101 ..... 0110011 @r sra 0100000 ..... ..... 101 ..... 0110011 @r or 0000000 ..... ..... 110 ..... 0110011 @r and 0000000 ..... ..... 111 ..... 0110011 @r -fence ---- pred:4 succ:4 ----- 000 ----- 0001111 + +{ + pause 0000 0001 0000 00000 000 00000 0001111 + fence ---- pred:4 succ:4 ----- 000 ----- 0001111 +} + fence_i ---- ---- ---- ----- 001 ----- 0001111 csrrw ............ ..... 001 ..... 1110011 @csr csrrs ............ ..... 010 ..... 1110011 @csr @@ -174,7 +183,20 @@ sraw 0100000 ..... ..... 101 ..... 0111011 @r # *** RV128I Base Instruction Set (in addition to RV64I) *** ldu ............ ..... 111 ..... 0000011 @i -lq ............ ..... 010 ..... 0001111 @i +{ + [ + # *** RV32 Zicbom Standard Extension *** + cbo_clean 0000000 00001 ..... 010 00000 0001111 @sfence_vm + cbo_flush 0000000 00010 ..... 010 00000 0001111 @sfence_vm + cbo_inval 0000000 00000 ..... 010 00000 0001111 @sfence_vm + + # *** RV32 Zicboz Standard Extension *** + cbo_zero 0000000 00100 ..... 010 00000 0001111 @sfence_vm + ] + + # *** RVI128 lq *** + lq ............ ..... 010 ..... 0001111 @i +} sq ............ ..... 100 ..... 0100011 @s addid ............ ..... 000 ..... 1011011 @i sllid 000000 ...... ..... 001 ..... 1011011 @sh6 @@ -659,11 +681,13 @@ vredmax_vs 000111 . ..... ..... 010 ..... 1010111 @r_vm vwredsumu_vs 110000 . ..... ..... 000 ..... 1010111 @r_vm vwredsum_vs 110001 . ..... ..... 000 ..... 1010111 @r_vm # Vector ordered and unordered reduction sum -vfredsum_vs 0000-1 . ..... ..... 001 ..... 1010111 @r_vm +vfredusum_vs 000001 . ..... ..... 001 ..... 1010111 @r_vm +vfredosum_vs 000011 . ..... ..... 001 ..... 1010111 @r_vm vfredmin_vs 000101 . ..... ..... 001 ..... 1010111 @r_vm vfredmax_vs 000111 . ..... ..... 001 ..... 1010111 @r_vm # Vector widening ordered and unordered float reduction sum -vfwredsum_vs 1100-1 . ..... ..... 001 ..... 1010111 @r_vm +vfwredusum_vs 110001 . ..... ..... 001 ..... 1010111 @r_vm +vfwredosum_vs 110011 . ..... ..... 001 ..... 1010111 @r_vm vmand_mm 011001 - ..... ..... 010 ..... 1010111 @r vmnand_mm 011101 - ..... ..... 010 ..... 1010111 @r vmandn_mm 011000 - ..... ..... 010 ..... 1010111 @r @@ -711,6 +735,10 @@ vsetvli 0 ........... ..... 111 ..... 1010111 @r2_zimm11 vsetivli 11 .......... ..... 111 ..... 1010111 @r2_zimm10 vsetvl 1000000 ..... ..... 111 ..... 1010111 @r +# *** Zawrs Standard Extension *** +wrs_nto 000000001101 00000 000 00000 1110011 +wrs_sto 000000011101 00000 000 00000 1110011 + # *** RV32 Zba Standard Extension *** sh1add 0010000 .......... 010 ..... 0110011 @r sh2add 0010000 .......... 100 ..... 0110011 @r @@ -796,6 +824,32 @@ binvi 01101. ........... 001 ..... 0010011 @sh bset 0010100 .......... 001 ..... 0110011 @r bseti 00101. ........... 001 ..... 0010011 @sh +# *** Zfa Standard Extension *** +fli_s 1111000 00001 ..... 000 ..... 1010011 @r2 +fli_d 1111001 00001 ..... 000 ..... 1010011 @r2 +fli_h 1111010 00001 ..... 000 ..... 1010011 @r2 +fminm_s 0010100 ..... ..... 010 ..... 1010011 @r +fmaxm_s 0010100 ..... ..... 011 ..... 1010011 @r +fminm_d 0010101 ..... ..... 010 ..... 1010011 @r +fmaxm_d 0010101 ..... ..... 011 ..... 1010011 @r +fminm_h 0010110 ..... ..... 010 ..... 1010011 @r +fmaxm_h 0010110 ..... ..... 011 ..... 1010011 @r +fround_s 0100000 00100 ..... ... ..... 1010011 @r2_rm +froundnx_s 0100000 00101 ..... ... ..... 1010011 @r2_rm +fround_d 0100001 00100 ..... ... ..... 1010011 @r2_rm +froundnx_d 0100001 00101 ..... ... ..... 1010011 @r2_rm +fround_h 0100010 00100 ..... ... ..... 1010011 @r2_rm +froundnx_h 0100010 00101 ..... ... ..... 1010011 @r2_rm +fcvtmod_w_d 1100001 01000 ..... 001 ..... 1010011 @r2 +fmvh_x_d 1110001 00001 ..... 000 ..... 1010011 @r2 +fmvp_d_x 1011001 ..... ..... 000 ..... 1010011 @r +fleq_s 1010000 ..... ..... 100 ..... 1010011 @r +fltq_s 1010000 ..... ..... 101 ..... 1010011 @r +fleq_d 1010001 ..... ..... 100 ..... 1010011 @r +fltq_d 1010001 ..... ..... 101 ..... 1010011 @r +fleq_h 1010010 ..... ..... 100 ..... 1010011 @r +fltq_h 1010010 ..... ..... 101 ..... 1010011 @r + # *** RV32 Zfh Extension *** flh ............ ..... 001 ..... 0000111 @i fsh ....... ..... ..... 001 ..... 0100111 @s @@ -879,3 +933,80 @@ sm3p1 00 01000 01001 ..... 001 ..... 0010011 @r2 # *** RV32 Zksed Standard Extension *** sm4ed .. 11000 ..... ..... 000 ..... 0110011 @k_aes sm4ks .. 11010 ..... ..... 000 ..... 0110011 @k_aes + +# *** RV32 Zicond Standard Extension *** +czero_eqz 0000111 ..... ..... 101 ..... 0110011 @r +czero_nez 0000111 ..... ..... 111 ..... 0110011 @r + +# *** Zfbfmin Standard Extension *** +fcvt_bf16_s 0100010 01000 ..... ... ..... 1010011 @r2_rm +fcvt_s_bf16 0100000 00110 ..... ... ..... 1010011 @r2_rm + +# *** Zvfbfmin Standard Extension *** +vfncvtbf16_f_f_w 010010 . ..... 11101 001 ..... 1010111 @r2_vm +vfwcvtbf16_f_f_v 010010 . ..... 01101 001 ..... 1010111 @r2_vm + +# *** Zvfbfwma Standard Extension *** +vfwmaccbf16_vv 111011 . ..... ..... 001 ..... 1010111 @r_vm +vfwmaccbf16_vf 111011 . ..... ..... 101 ..... 1010111 @r_vm + +# *** Zvbc vector crypto extension *** +vclmul_vv 001100 . ..... ..... 010 ..... 1010111 @r_vm +vclmul_vx 001100 . ..... ..... 110 ..... 1010111 @r_vm +vclmulh_vv 001101 . ..... ..... 010 ..... 1010111 @r_vm +vclmulh_vx 001101 . ..... ..... 110 ..... 1010111 @r_vm + +# *** Zvbb vector crypto extension *** +vrol_vv 010101 . ..... ..... 000 ..... 1010111 @r_vm +vrol_vx 010101 . ..... ..... 100 ..... 1010111 @r_vm +vror_vv 010100 . ..... ..... 000 ..... 1010111 @r_vm +vror_vx 010100 . ..... ..... 100 ..... 1010111 @r_vm +vror_vi 01010. . ..... ..... 011 ..... 1010111 @r2_zimm6 +vbrev8_v 010010 . ..... 01000 010 ..... 1010111 @r2_vm +vrev8_v 010010 . ..... 01001 010 ..... 1010111 @r2_vm +vandn_vv 000001 . ..... ..... 000 ..... 1010111 @r_vm +vandn_vx 000001 . ..... ..... 100 ..... 1010111 @r_vm +vbrev_v 010010 . ..... 01010 010 ..... 1010111 @r2_vm +vclz_v 010010 . ..... 01100 010 ..... 1010111 @r2_vm +vctz_v 010010 . ..... 01101 010 ..... 1010111 @r2_vm +vcpop_v 010010 . ..... 01110 010 ..... 1010111 @r2_vm +vwsll_vv 110101 . ..... ..... 000 ..... 1010111 @r_vm +vwsll_vx 110101 . ..... ..... 100 ..... 1010111 @r_vm +vwsll_vi 110101 . ..... ..... 011 ..... 1010111 @r_vm + +# *** Zvkned vector crypto extension *** +vaesef_vv 101000 1 ..... 00011 010 ..... 1110111 @r2_vm_1 +vaesef_vs 101001 1 ..... 00011 010 ..... 1110111 @r2_vm_1 +vaesdf_vv 101000 1 ..... 00001 010 ..... 1110111 @r2_vm_1 +vaesdf_vs 101001 1 ..... 00001 010 ..... 1110111 @r2_vm_1 +vaesem_vv 101000 1 ..... 00010 010 ..... 1110111 @r2_vm_1 +vaesem_vs 101001 1 ..... 00010 010 ..... 1110111 @r2_vm_1 +vaesdm_vv 101000 1 ..... 00000 010 ..... 1110111 @r2_vm_1 +vaesdm_vs 101001 1 ..... 00000 010 ..... 1110111 @r2_vm_1 +vaesz_vs 101001 1 ..... 00111 010 ..... 1110111 @r2_vm_1 +vaeskf1_vi 100010 1 ..... ..... 010 ..... 1110111 @r_vm_1 +vaeskf2_vi 101010 1 ..... ..... 010 ..... 1110111 @r_vm_1 + +# *** Zvknh vector crypto extension *** +vsha2ms_vv 101101 1 ..... ..... 010 ..... 1110111 @r_vm_1 +vsha2ch_vv 101110 1 ..... ..... 010 ..... 1110111 @r_vm_1 +vsha2cl_vv 101111 1 ..... ..... 010 ..... 1110111 @r_vm_1 + +# *** Zvksh vector crypto extension *** +vsm3me_vv 100000 1 ..... ..... 010 ..... 1110111 @r_vm_1 +vsm3c_vi 101011 1 ..... ..... 010 ..... 1110111 @r_vm_1 + +# *** Zvkg vector crypto extension *** +vghsh_vv 101100 1 ..... ..... 010 ..... 1110111 @r_vm_1 +vgmul_vv 101000 1 ..... 10001 010 ..... 1110111 @r2_vm_1 + +# *** Zvksed vector crypto extension *** +vsm4k_vi 100001 1 ..... ..... 010 ..... 1110111 @r_vm_1 +vsm4r_vv 101000 1 ..... 10000 010 ..... 1110111 @r2_vm_1 +vsm4r_vs 101001 1 ..... 10000 010 ..... 1110111 @r2_vm_1 + +# *** RV32 Zacas Standard Extension *** +amocas_w 00101 . . ..... ..... 010 ..... 0101111 @atom_st +amocas_d 00101 . . ..... ..... 011 ..... 0101111 @atom_st +# *** RV64 Zacas Standard Extension *** +amocas_q 00101 . . ..... ..... 100 ..... 0101111 @atom_st diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index 53613682e8..620ab54eb0 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -52,7 +52,8 @@ static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a) * that no exception will be raised when fetching them. */ - if ((pre_addr & TARGET_PAGE_MASK) == (post_addr & TARGET_PAGE_MASK)) { + if (semihosting_enabled(ctx->priv == PRV_U) && + (pre_addr & TARGET_PAGE_MASK) == (post_addr & TARGET_PAGE_MASK)) { pre = opcode_at(&ctx->base, pre_addr); ebreak = opcode_at(&ctx->base, ebreak_addr); post = opcode_at(&ctx->base, post_addr); @@ -75,8 +76,10 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a) { #ifndef CONFIG_USER_ONLY if (has_ext(ctx, RVS)) { - gen_helper_sret(cpu_pc, cpu_env); - tcg_gen_exit_tb(NULL, 0); /* no chaining */ + decode_save_opc(ctx); + translator_io_start(&ctx->base); + gen_helper_sret(cpu_pc, tcg_env); + exit_tb(ctx); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; } else { return false; @@ -90,8 +93,10 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a) static bool trans_mret(DisasContext *ctx, arg_mret *a) { #ifndef CONFIG_USER_ONLY - gen_helper_mret(cpu_pc, cpu_env); - tcg_gen_exit_tb(NULL, 0); /* no chaining */ + decode_save_opc(ctx); + translator_io_start(&ctx->base); + gen_helper_mret(cpu_pc, tcg_env); + exit_tb(ctx); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; return true; #else @@ -102,8 +107,9 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a) static bool trans_wfi(DisasContext *ctx, arg_wfi *a) { #ifndef CONFIG_USER_ONLY - gen_set_pc_imm(ctx, ctx->pc_succ_insn); - gen_helper_wfi(cpu_env); + decode_save_opc(ctx); + gen_update_pc(ctx, ctx->cur_insn_len); + gen_helper_wfi(tcg_env); return true; #else return false; @@ -113,7 +119,8 @@ static bool trans_wfi(DisasContext *ctx, arg_wfi *a) static bool trans_sfence_vma(DisasContext *ctx, arg_sfence_vma *a) { #ifndef CONFIG_USER_ONLY - gen_helper_tlb_flush(cpu_env); + decode_save_opc(ctx); + gen_helper_tlb_flush(tcg_env); return true; #endif return false; diff --git a/target/riscv/insn_trans/trans_rva.c.inc b/target/riscv/insn_trans/trans_rva.c.inc index 45db82c9be..4a9e4591d1 100644 --- a/target/riscv/insn_trans/trans_rva.c.inc +++ b/target/riscv/insn_trans/trans_rva.c.inc @@ -18,15 +18,33 @@ * this program. If not, see . */ +#define REQUIRE_A_OR_ZAAMO(ctx) do { \ + if (!ctx->cfg_ptr->ext_zaamo && !has_ext(ctx, RVA)) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_A_OR_ZALRSC(ctx) do { \ + if (!ctx->cfg_ptr->ext_zalrsc && !has_ext(ctx, RVA)) { \ + return false; \ + } \ +} while (0) + static bool gen_lr(DisasContext *ctx, arg_atomic *a, MemOp mop) { - TCGv src1 = get_address(ctx, a->rs1, 0); + TCGv src1; + decode_save_opc(ctx); + src1 = get_address(ctx, a->rs1, 0); if (a->rl) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); } tcg_gen_qemu_ld_tl(load_val, src1, ctx->mem_idx, mop); - if (a->aq) { + /* + * TSO defines AMOs as acquire+release-RCsc, but does not define LR/SC as + * AMOs. Instead treat them like loads. + */ + if (a->aq || ctx->ztso) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); } @@ -43,6 +61,7 @@ static bool gen_sc(DisasContext *ctx, arg_atomic *a, MemOp mop) TCGLabel *l1 = gen_new_label(); TCGLabel *l2 = gen_new_label(); + decode_save_opc(ctx); src1 = get_address(ctx, a->rs1, 0); tcg_gen_brcond_tl(TCG_COND_NE, load_res, src1, l1); @@ -61,9 +80,10 @@ static bool gen_sc(DisasContext *ctx, arg_atomic *a, MemOp mop) gen_set_label(l1); /* * Address comparison failure. However, we still need to - * provide the memory barrier implied by AQ/RL. + * provide the memory barrier implied by AQ/RL/TSO. */ - tcg_gen_mb(TCG_MO_ALL + a->aq * TCG_BAR_LDAQ + a->rl * TCG_BAR_STRL); + TCGBar bar_strl = (ctx->ztso || a->rl) ? TCG_BAR_STRL : 0; + tcg_gen_mb(TCG_MO_ALL + a->aq * TCG_BAR_LDAQ + bar_strl); gen_set_gpr(ctx, a->rd, tcg_constant_tl(1)); gen_set_label(l2); @@ -81,9 +101,10 @@ static bool gen_amo(DisasContext *ctx, arg_atomic *a, MemOp mop) { TCGv dest = dest_gpr(ctx, a->rd); - TCGv src1 = get_address(ctx, a->rs1, 0); - TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); + TCGv src1, src2 = get_gpr(ctx, a->rs2, EXT_NONE); + decode_save_opc(ctx); + src1 = get_address(ctx, a->rs1, 0); func(dest, src1, src2, ctx->mem_idx, mop); gen_set_gpr(ctx, a->rd, dest); @@ -92,132 +113,143 @@ static bool gen_amo(DisasContext *ctx, arg_atomic *a, static bool trans_lr_w(DisasContext *ctx, arg_lr_w *a) { - REQUIRE_EXT(ctx, RVA); + REQUIRE_A_OR_ZALRSC(ctx); return gen_lr(ctx, a, (MO_ALIGN | MO_TESL)); } static bool trans_sc_w(DisasContext *ctx, arg_sc_w *a) { - REQUIRE_EXT(ctx, RVA); + REQUIRE_A_OR_ZALRSC(ctx); return gen_sc(ctx, a, (MO_ALIGN | MO_TESL)); } static bool trans_amoswap_w(DisasContext *ctx, arg_amoswap_w *a) { - REQUIRE_EXT(ctx, RVA); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, (MO_ALIGN | MO_TESL)); } static bool trans_amoadd_w(DisasContext *ctx, arg_amoadd_w *a) { - REQUIRE_EXT(ctx, RVA); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, (MO_ALIGN | MO_TESL)); } static bool trans_amoxor_w(DisasContext *ctx, arg_amoxor_w *a) { - REQUIRE_EXT(ctx, RVA); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, (MO_ALIGN | MO_TESL)); } static bool trans_amoand_w(DisasContext *ctx, arg_amoand_w *a) { - REQUIRE_EXT(ctx, RVA); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, (MO_ALIGN | MO_TESL)); } static bool trans_amoor_w(DisasContext *ctx, arg_amoor_w *a) { - REQUIRE_EXT(ctx, RVA); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, (MO_ALIGN | MO_TESL)); } static bool trans_amomin_w(DisasContext *ctx, arg_amomin_w *a) { - REQUIRE_EXT(ctx, RVA); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, (MO_ALIGN | MO_TESL)); } static bool trans_amomax_w(DisasContext *ctx, arg_amomax_w *a) { - REQUIRE_EXT(ctx, RVA); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, (MO_ALIGN | MO_TESL)); } static bool trans_amominu_w(DisasContext *ctx, arg_amominu_w *a) { - REQUIRE_EXT(ctx, RVA); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, (MO_ALIGN | MO_TESL)); } static bool trans_amomaxu_w(DisasContext *ctx, arg_amomaxu_w *a) { - REQUIRE_EXT(ctx, RVA); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, (MO_ALIGN | MO_TESL)); } static bool trans_lr_d(DisasContext *ctx, arg_lr_d *a) { REQUIRE_64BIT(ctx); + REQUIRE_A_OR_ZALRSC(ctx); return gen_lr(ctx, a, MO_ALIGN | MO_TEUQ); } static bool trans_sc_d(DisasContext *ctx, arg_sc_d *a) { REQUIRE_64BIT(ctx); + REQUIRE_A_OR_ZALRSC(ctx); return gen_sc(ctx, a, (MO_ALIGN | MO_TEUQ)); } static bool trans_amoswap_d(DisasContext *ctx, arg_amoswap_d *a) { REQUIRE_64BIT(ctx); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, (MO_ALIGN | MO_TEUQ)); } static bool trans_amoadd_d(DisasContext *ctx, arg_amoadd_d *a) { REQUIRE_64BIT(ctx); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, (MO_ALIGN | MO_TEUQ)); } static bool trans_amoxor_d(DisasContext *ctx, arg_amoxor_d *a) { REQUIRE_64BIT(ctx); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, (MO_ALIGN | MO_TEUQ)); } static bool trans_amoand_d(DisasContext *ctx, arg_amoand_d *a) { REQUIRE_64BIT(ctx); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, (MO_ALIGN | MO_TEUQ)); } static bool trans_amoor_d(DisasContext *ctx, arg_amoor_d *a) { REQUIRE_64BIT(ctx); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, (MO_ALIGN | MO_TEUQ)); } static bool trans_amomin_d(DisasContext *ctx, arg_amomin_d *a) { REQUIRE_64BIT(ctx); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, (MO_ALIGN | MO_TEUQ)); } static bool trans_amomax_d(DisasContext *ctx, arg_amomax_d *a) { REQUIRE_64BIT(ctx); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, (MO_ALIGN | MO_TEUQ)); } static bool trans_amominu_d(DisasContext *ctx, arg_amominu_d *a) { REQUIRE_64BIT(ctx); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, (MO_ALIGN | MO_TEUQ)); } static bool trans_amomaxu_d(DisasContext *ctx, arg_amomaxu_d *a) { REQUIRE_64BIT(ctx); + REQUIRE_A_OR_ZAAMO(ctx); return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, (MO_ALIGN | MO_TEUQ)); } diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index e2b8329f1e..e4dcc7c991 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -64,7 +64,6 @@ static void gen_clzw(TCGv ret, TCGv arg1) TCGv t = tcg_temp_new(); tcg_gen_shli_tl(t, arg1, 32); tcg_gen_clzi_tl(ret, t, 32); - tcg_temp_free(t); } static bool trans_clz(DisasContext *ctx, arg_clz *a) @@ -161,8 +160,6 @@ static void gen_bset(TCGv ret, TCGv arg1, TCGv shamt) gen_sbop_mask(t, shamt); tcg_gen_or_tl(ret, arg1, t); - - tcg_temp_free(t); } static bool trans_bset(DisasContext *ctx, arg_bset *a) @@ -183,8 +180,6 @@ static void gen_bclr(TCGv ret, TCGv arg1, TCGv shamt) gen_sbop_mask(t, shamt); tcg_gen_andc_tl(ret, arg1, t); - - tcg_temp_free(t); } static bool trans_bclr(DisasContext *ctx, arg_bclr *a) @@ -205,8 +200,6 @@ static void gen_binv(TCGv ret, TCGv arg1, TCGv shamt) gen_sbop_mask(t, shamt); tcg_gen_xor_tl(ret, arg1, t); - - tcg_temp_free(t); } static bool trans_binv(DisasContext *ctx, arg_binv *a) @@ -252,9 +245,6 @@ static void gen_rorw(TCGv ret, TCGv arg1, TCGv arg2) /* sign-extend 64-bits */ tcg_gen_ext_i32_tl(ret, t1); - - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); } static bool trans_ror(DisasContext *ctx, arg_ror *a) @@ -270,8 +260,6 @@ static void gen_roriw(TCGv ret, TCGv arg1, target_long shamt) tcg_gen_trunc_tl_i32(t1, arg1); tcg_gen_rotri_i32(t1, t1, shamt); tcg_gen_ext_i32_tl(ret, t1); - - tcg_temp_free_i32(t1); } static bool trans_rori(DisasContext *ctx, arg_rori *a) @@ -294,9 +282,6 @@ static void gen_rolw(TCGv ret, TCGv arg1, TCGv arg2) /* sign-extend 64-bits */ tcg_gen_ext_i32_tl(ret, t1); - - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); } static bool trans_rol(DisasContext *ctx, arg_rol *a) @@ -340,8 +325,6 @@ static void gen_orc_b(TCGv ret, TCGv source1) /* Replicate the lsb of each byte across the byte. */ tcg_gen_muli_tl(ret, tmp, 0xff); - - tcg_temp_free(tmp); } static bool trans_orc_b(DisasContext *ctx, arg_orc_b *a) @@ -357,8 +340,6 @@ static void gen_sh##SHAMT##add(TCGv ret, TCGv arg1, TCGv arg2) \ \ tcg_gen_shli_tl(t, arg1, SHAMT); \ tcg_gen_add_tl(ret, t, arg2); \ - \ - tcg_temp_free(t); \ } GEN_SHADD(1) @@ -401,6 +382,7 @@ static bool trans_ctzw(DisasContext *ctx, arg_ctzw *a) { REQUIRE_64BIT(ctx); REQUIRE_ZBB(ctx); + ctx->ol = MXL_RV32; return gen_unary(ctx, a, EXT_ZERO, gen_ctzw); } @@ -445,8 +427,6 @@ static void gen_sh##SHAMT##add_uw(TCGv ret, TCGv arg1, TCGv arg2) \ \ tcg_gen_shli_tl(t, t, SHAMT); \ tcg_gen_add_tl(ret, t, arg2); \ - \ - tcg_temp_free(t); \ } GEN_SHADD_UW(1) @@ -471,7 +451,6 @@ static void gen_add_uw(TCGv ret, TCGv arg1, TCGv arg2) TCGv t = tcg_temp_new(); tcg_gen_ext32u_tl(t, arg1); tcg_gen_add_tl(ret, t, arg2); - tcg_temp_free(t); } static bool trans_add_uw(DisasContext *ctx, arg_add_uw *a) @@ -530,7 +509,6 @@ static void gen_packh(TCGv ret, TCGv src1, TCGv src2) tcg_gen_ext8u_tl(t, src2); tcg_gen_deposit_tl(ret, src1, t, 8, TARGET_LONG_BITS - 8); - tcg_temp_free(t); } static void gen_packw(TCGv ret, TCGv src1, TCGv src2) @@ -539,7 +517,6 @@ static void gen_packw(TCGv ret, TCGv src1, TCGv src2) tcg_gen_ext16s_tl(t, src2); tcg_gen_deposit_tl(ret, src1, t, 16, TARGET_LONG_BITS - 16); - tcg_temp_free(t); } static bool trans_brev8(DisasContext *ctx, arg_brev8 *a) diff --git a/target/riscv/insn_trans/trans_rvbf16.c.inc b/target/riscv/insn_trans/trans_rvbf16.c.inc new file mode 100644 index 0000000000..0a9cd1ec31 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvbf16.c.inc @@ -0,0 +1,163 @@ +/* + * RISC-V translation routines for the BF16 Standard Extensions. + * + * Copyright (c) 2020-2023 PLCT Lab + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#define REQUIRE_ZFBFMIN(ctx) do { \ + if (!ctx->cfg_ptr->ext_zfbfmin) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_ZVFBFMIN(ctx) do { \ + if (!ctx->cfg_ptr->ext_zvfbfmin) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_ZVFBFWMA(ctx) do { \ + if (!ctx->cfg_ptr->ext_zvfbfwma) { \ + return false; \ + } \ +} while (0) + +static bool trans_fcvt_bf16_s(DisasContext *ctx, arg_fcvt_bf16_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFBFMIN(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_bf16_s(dest, tcg_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fcvt_s_bf16(DisasContext *ctx, arg_fcvt_s_bf16 *a) +{ + REQUIRE_FPU; + REQUIRE_ZFBFMIN(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_s_bf16(dest, tcg_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_vfncvtbf16_f_f_w(DisasContext *ctx, arg_vfncvtbf16_f_f_w *a) +{ + REQUIRE_FPU; + REQUIRE_ZVFBFMIN(ctx); + + if (opfv_narrow_check(ctx, a) && (ctx->sew == MO_16)) { + uint32_t data = 0; + + gen_set_rm_chkfrm(ctx, RISCV_FRM_DYN); + + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul); + data = FIELD_DP32(data, VDATA, VTA, ctx->vta); + data = FIELD_DP32(data, VDATA, VMA, ctx->vma); + tcg_gen_gvec_3_ptr(vreg_ofs(ctx, a->rd), vreg_ofs(ctx, 0), + vreg_ofs(ctx, a->rs2), tcg_env, + ctx->cfg_ptr->vlenb, + ctx->cfg_ptr->vlenb, data, + gen_helper_vfncvtbf16_f_f_w); + finalize_rvv_inst(ctx); + return true; + } + return false; +} + +static bool trans_vfwcvtbf16_f_f_v(DisasContext *ctx, arg_vfwcvtbf16_f_f_v *a) +{ + REQUIRE_FPU; + REQUIRE_ZVFBFMIN(ctx); + + if (opfv_widen_check(ctx, a) && (ctx->sew == MO_16)) { + uint32_t data = 0; + + gen_set_rm_chkfrm(ctx, RISCV_FRM_DYN); + + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul); + data = FIELD_DP32(data, VDATA, VTA, ctx->vta); + data = FIELD_DP32(data, VDATA, VMA, ctx->vma); + tcg_gen_gvec_3_ptr(vreg_ofs(ctx, a->rd), vreg_ofs(ctx, 0), + vreg_ofs(ctx, a->rs2), tcg_env, + ctx->cfg_ptr->vlenb, + ctx->cfg_ptr->vlenb, data, + gen_helper_vfwcvtbf16_f_f_v); + finalize_rvv_inst(ctx); + return true; + } + return false; +} + +static bool trans_vfwmaccbf16_vv(DisasContext *ctx, arg_vfwmaccbf16_vv *a) +{ + REQUIRE_FPU; + REQUIRE_ZVFBFWMA(ctx); + + if (require_rvv(ctx) && vext_check_isa_ill(ctx) && (ctx->sew == MO_16) && + vext_check_dss(ctx, a->rd, a->rs1, a->rs2, a->vm)) { + uint32_t data = 0; + + gen_set_rm_chkfrm(ctx, RISCV_FRM_DYN); + + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul); + data = FIELD_DP32(data, VDATA, VTA, ctx->vta); + data = FIELD_DP32(data, VDATA, VMA, ctx->vma); + tcg_gen_gvec_4_ptr(vreg_ofs(ctx, a->rd), vreg_ofs(ctx, 0), + vreg_ofs(ctx, a->rs1), + vreg_ofs(ctx, a->rs2), tcg_env, + ctx->cfg_ptr->vlenb, + ctx->cfg_ptr->vlenb, data, + gen_helper_vfwmaccbf16_vv); + finalize_rvv_inst(ctx); + return true; + } + return false; +} + +static bool trans_vfwmaccbf16_vf(DisasContext *ctx, arg_vfwmaccbf16_vf *a) +{ + REQUIRE_FPU; + REQUIRE_ZVFBFWMA(ctx); + + if (require_rvv(ctx) && (ctx->sew == MO_16) && vext_check_isa_ill(ctx) && + vext_check_ds(ctx, a->rd, a->rs2, a->vm)) { + uint32_t data = 0; + + gen_set_rm(ctx, RISCV_FRM_DYN); + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul); + data = FIELD_DP32(data, VDATA, VTA, ctx->vta); + data = FIELD_DP32(data, VDATA, VMA, ctx->vma); + return opfvf_trans(a->rd, a->rs1, a->rs2, data, + gen_helper_vfwmaccbf16_vf, ctx); + } + + return false; +} diff --git a/target/riscv/insn_trans/trans_rvd.c.inc b/target/riscv/insn_trans/trans_rvd.c.inc index 1397c1ce1c..d9ce9e407f 100644 --- a/target/riscv/insn_trans/trans_rvd.c.inc +++ b/target/riscv/insn_trans/trans_rvd.c.inc @@ -31,6 +31,14 @@ } \ } while (0) +#define REQUIRE_ZCD_OR_DC(ctx) do { \ + if (!ctx->cfg_ptr->ext_zcd) { \ + if (!has_ext(ctx, RVD) || !has_ext(ctx, RVC)) { \ + return false; \ + } \ + } \ +} while (0) + static bool trans_fld(DisasContext *ctx, arg_fld *a) { TCGv addr; @@ -38,6 +46,7 @@ static bool trans_fld(DisasContext *ctx, arg_fld *a) REQUIRE_FPU; REQUIRE_EXT(ctx, RVD); + decode_save_opc(ctx); addr = get_address(ctx, a->rs1, a->imm); tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], addr, ctx->mem_idx, MO_TEUQ); @@ -52,11 +61,24 @@ static bool trans_fsd(DisasContext *ctx, arg_fsd *a) REQUIRE_FPU; REQUIRE_EXT(ctx, RVD); + decode_save_opc(ctx); addr = get_address(ctx, a->rs1, a->imm); tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, MO_TEUQ); return true; } +static bool trans_c_fld(DisasContext *ctx, arg_fld *a) +{ + REQUIRE_ZCD_OR_DC(ctx); + return trans_fld(ctx, a); +} + +static bool trans_c_fsd(DisasContext *ctx, arg_fsd *a) +{ + REQUIRE_ZCD_OR_DC(ctx); + return trans_fsd(ctx, a); +} + static bool trans_fmadd_d(DisasContext *ctx, arg_fmadd_d *a) { REQUIRE_FPU; @@ -69,7 +91,7 @@ static bool trans_fmadd_d(DisasContext *ctx, arg_fmadd_d *a) TCGv_i64 src3 = get_fpr_d(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fmadd_d(dest, cpu_env, src1, src2, src3); + gen_helper_fmadd_d(dest, tcg_env, src1, src2, src3); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -87,7 +109,7 @@ static bool trans_fmsub_d(DisasContext *ctx, arg_fmsub_d *a) TCGv_i64 src3 = get_fpr_d(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fmsub_d(dest, cpu_env, src1, src2, src3); + gen_helper_fmsub_d(dest, tcg_env, src1, src2, src3); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -105,7 +127,7 @@ static bool trans_fnmsub_d(DisasContext *ctx, arg_fnmsub_d *a) TCGv_i64 src3 = get_fpr_d(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fnmsub_d(dest, cpu_env, src1, src2, src3); + gen_helper_fnmsub_d(dest, tcg_env, src1, src2, src3); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -123,7 +145,7 @@ static bool trans_fnmadd_d(DisasContext *ctx, arg_fnmadd_d *a) TCGv_i64 src3 = get_fpr_d(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fnmadd_d(dest, cpu_env, src1, src2, src3); + gen_helper_fnmadd_d(dest, tcg_env, src1, src2, src3); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -140,7 +162,7 @@ static bool trans_fadd_d(DisasContext *ctx, arg_fadd_d *a) TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fadd_d(dest, cpu_env, src1, src2); + gen_helper_fadd_d(dest, tcg_env, src1, src2); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -157,7 +179,7 @@ static bool trans_fsub_d(DisasContext *ctx, arg_fsub_d *a) TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fsub_d(dest, cpu_env, src1, src2); + gen_helper_fsub_d(dest, tcg_env, src1, src2); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -174,7 +196,7 @@ static bool trans_fmul_d(DisasContext *ctx, arg_fmul_d *a) TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fmul_d(dest, cpu_env, src1, src2); + gen_helper_fmul_d(dest, tcg_env, src1, src2); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -191,7 +213,7 @@ static bool trans_fdiv_d(DisasContext *ctx, arg_fdiv_d *a) TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fdiv_d(dest, cpu_env, src1, src2); + gen_helper_fdiv_d(dest, tcg_env, src1, src2); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -207,7 +229,7 @@ static bool trans_fsqrt_d(DisasContext *ctx, arg_fsqrt_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fsqrt_d(dest, cpu_env, src1); + gen_helper_fsqrt_d(dest, tcg_env, src1); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -248,7 +270,6 @@ static bool trans_fsgnjn_d(DisasContext *ctx, arg_fsgnjn_d *a) TCGv_i64 t0 = tcg_temp_new_i64(); tcg_gen_not_i64(t0, src2); tcg_gen_deposit_i64(dest, t0, src1, 0, 63); - tcg_temp_free_i64(t0); } gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -271,7 +292,6 @@ static bool trans_fsgnjx_d(DisasContext *ctx, arg_fsgnjx_d *a) TCGv_i64 t0 = tcg_temp_new_i64(); tcg_gen_andi_i64(t0, src2, INT64_MIN); tcg_gen_xor_i64(dest, src1, t0); - tcg_temp_free_i64(t0); } gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -288,7 +308,7 @@ static bool trans_fmin_d(DisasContext *ctx, arg_fmin_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); - gen_helper_fmin_d(dest, cpu_env, src1, src2); + gen_helper_fmin_d(dest, tcg_env, src1, src2); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -304,7 +324,7 @@ static bool trans_fmax_d(DisasContext *ctx, arg_fmax_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); - gen_helper_fmax_d(dest, cpu_env, src1, src2); + gen_helper_fmax_d(dest, tcg_env, src1, src2); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -320,7 +340,7 @@ static bool trans_fcvt_s_d(DisasContext *ctx, arg_fcvt_s_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_s_d(dest, cpu_env, src1); + gen_helper_fcvt_s_d(dest, tcg_env, src1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -336,7 +356,7 @@ static bool trans_fcvt_d_s(DisasContext *ctx, arg_fcvt_d_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_d_s(dest, cpu_env, src1); + gen_helper_fcvt_d_s(dest, tcg_env, src1); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -352,7 +372,7 @@ static bool trans_feq_d(DisasContext *ctx, arg_feq_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); - gen_helper_feq_d(dest, cpu_env, src1, src2); + gen_helper_feq_d(dest, tcg_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -367,7 +387,7 @@ static bool trans_flt_d(DisasContext *ctx, arg_flt_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); - gen_helper_flt_d(dest, cpu_env, src1, src2); + gen_helper_flt_d(dest, tcg_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -382,7 +402,7 @@ static bool trans_fle_d(DisasContext *ctx, arg_fle_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); - gen_helper_fle_d(dest, cpu_env, src1, src2); + gen_helper_fle_d(dest, tcg_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -411,7 +431,7 @@ static bool trans_fcvt_w_d(DisasContext *ctx, arg_fcvt_w_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_w_d(dest, cpu_env, src1); + gen_helper_fcvt_w_d(dest, tcg_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -426,7 +446,7 @@ static bool trans_fcvt_wu_d(DisasContext *ctx, arg_fcvt_wu_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_wu_d(dest, cpu_env, src1); + gen_helper_fcvt_wu_d(dest, tcg_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -441,7 +461,7 @@ static bool trans_fcvt_d_w(DisasContext *ctx, arg_fcvt_d_w *a) TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_d_w(dest, cpu_env, src); + gen_helper_fcvt_d_w(dest, tcg_env, src); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -458,7 +478,7 @@ static bool trans_fcvt_d_wu(DisasContext *ctx, arg_fcvt_d_wu *a) TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_d_wu(dest, cpu_env, src); + gen_helper_fcvt_d_wu(dest, tcg_env, src); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -476,7 +496,7 @@ static bool trans_fcvt_l_d(DisasContext *ctx, arg_fcvt_l_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_l_d(dest, cpu_env, src1); + gen_helper_fcvt_l_d(dest, tcg_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -492,7 +512,7 @@ static bool trans_fcvt_lu_d(DisasContext *ctx, arg_fcvt_lu_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_lu_d(dest, cpu_env, src1); + gen_helper_fcvt_lu_d(dest, tcg_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -522,7 +542,7 @@ static bool trans_fcvt_d_l(DisasContext *ctx, arg_fcvt_d_l *a) TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_d_l(dest, cpu_env, src); + gen_helper_fcvt_d_l(dest, tcg_env, src); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -540,7 +560,7 @@ static bool trans_fcvt_d_lu(DisasContext *ctx, arg_fcvt_d_lu *a) TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_d_lu(dest, cpu_env, src); + gen_helper_fcvt_d_lu(dest, tcg_env, src); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); diff --git a/target/riscv/insn_trans/trans_rvf.c.inc b/target/riscv/insn_trans/trans_rvf.c.inc index a1d3eb52ad..97a368970b 100644 --- a/target/riscv/insn_trans/trans_rvf.c.inc +++ b/target/riscv/insn_trans/trans_rvf.c.inc @@ -19,9 +19,10 @@ */ #define REQUIRE_FPU do {\ - if (ctx->mstatus_fs == 0) \ - if (!ctx->cfg_ptr->ext_zfinx) \ - return false; \ + if (ctx->mstatus_fs == EXT_STATUS_DISABLED) { \ + ctx->virt_inst_excp = ctx->virt_enabled && ctx->cfg_ptr->ext_zfinx; \ + return false; \ + } \ } while (0) #define REQUIRE_ZFINX_OR_F(ctx) do {\ @@ -30,6 +31,14 @@ } \ } while (0) +#define REQUIRE_ZCF_OR_FC(ctx) do { \ + if (!ctx->cfg_ptr->ext_zcf) { \ + if (!has_ext(ctx, RVF) || !has_ext(ctx, RVC)) { \ + return false; \ + } \ + } \ +} while (0) + static bool trans_flw(DisasContext *ctx, arg_flw *a) { TCGv_i64 dest; @@ -38,6 +47,7 @@ static bool trans_flw(DisasContext *ctx, arg_flw *a) REQUIRE_FPU; REQUIRE_EXT(ctx, RVF); + decode_save_opc(ctx); addr = get_address(ctx, a->rs1, a->imm); dest = cpu_fpr[a->rd]; tcg_gen_qemu_ld_i64(dest, addr, ctx->mem_idx, MO_TEUL); @@ -54,11 +64,24 @@ static bool trans_fsw(DisasContext *ctx, arg_fsw *a) REQUIRE_FPU; REQUIRE_EXT(ctx, RVF); + decode_save_opc(ctx); addr = get_address(ctx, a->rs1, a->imm); tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, MO_TEUL); return true; } +static bool trans_c_flw(DisasContext *ctx, arg_flw *a) +{ + REQUIRE_ZCF_OR_FC(ctx); + return trans_flw(ctx, a); +} + +static bool trans_c_fsw(DisasContext *ctx, arg_fsw *a) +{ + REQUIRE_ZCF_OR_FC(ctx); + return trans_fsw(ctx, a); +} + static bool trans_fmadd_s(DisasContext *ctx, arg_fmadd_s *a) { REQUIRE_FPU; @@ -70,7 +93,7 @@ static bool trans_fmadd_s(DisasContext *ctx, arg_fmadd_s *a) TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fmadd_s(dest, cpu_env, src1, src2, src3); + gen_helper_fmadd_s(dest, tcg_env, src1, src2, src3); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -87,7 +110,7 @@ static bool trans_fmsub_s(DisasContext *ctx, arg_fmsub_s *a) TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fmsub_s(dest, cpu_env, src1, src2, src3); + gen_helper_fmsub_s(dest, tcg_env, src1, src2, src3); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -104,7 +127,7 @@ static bool trans_fnmsub_s(DisasContext *ctx, arg_fnmsub_s *a) TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fnmsub_s(dest, cpu_env, src1, src2, src3); + gen_helper_fnmsub_s(dest, tcg_env, src1, src2, src3); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -121,7 +144,7 @@ static bool trans_fnmadd_s(DisasContext *ctx, arg_fnmadd_s *a) TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fnmadd_s(dest, cpu_env, src1, src2, src3); + gen_helper_fnmadd_s(dest, tcg_env, src1, src2, src3); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -137,7 +160,7 @@ static bool trans_fadd_s(DisasContext *ctx, arg_fadd_s *a) TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fadd_s(dest, cpu_env, src1, src2); + gen_helper_fadd_s(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -153,7 +176,7 @@ static bool trans_fsub_s(DisasContext *ctx, arg_fsub_s *a) TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fsub_s(dest, cpu_env, src1, src2); + gen_helper_fsub_s(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -169,7 +192,7 @@ static bool trans_fmul_s(DisasContext *ctx, arg_fmul_s *a) TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fmul_s(dest, cpu_env, src1, src2); + gen_helper_fmul_s(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -185,7 +208,7 @@ static bool trans_fdiv_s(DisasContext *ctx, arg_fdiv_s *a) TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fdiv_s(dest, cpu_env, src1, src2); + gen_helper_fdiv_s(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -200,7 +223,7 @@ static bool trans_fsqrt_s(DisasContext *ctx, arg_fsqrt_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fsqrt_s(dest, cpu_env, src1); + gen_helper_fsqrt_s(dest, tcg_env, src1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -231,9 +254,6 @@ static bool trans_fsgnj_s(DisasContext *ctx, arg_fsgnj_s *a) /* This formulation retains the nanboxing of rs2 in normal 'F'. */ tcg_gen_deposit_i64(dest, rs2, rs1, 0, 31); - - tcg_temp_free_i64(rs1); - tcg_temp_free_i64(rs2); } else { tcg_gen_deposit_i64(dest, src2, src1, 0, 31); tcg_gen_ext32s_i64(dest, dest); @@ -279,15 +299,12 @@ static bool trans_fsgnjn_s(DisasContext *ctx, arg_fsgnjn_s *a) tcg_gen_nor_i64(rs2, rs2, mask); tcg_gen_and_i64(dest, mask, rs1); tcg_gen_or_i64(dest, dest, rs2); - - tcg_temp_free_i64(rs2); } - /* signed-extended intead of nanboxing for result if enable zfinx */ + /* signed-extended instead of nanboxing for result if enable zfinx */ if (ctx->cfg_ptr->ext_zfinx) { tcg_gen_ext32s_i64(dest, dest); } gen_set_fpr_hs(ctx, a->rd, dest); - tcg_temp_free_i64(rs1); mark_fs_dirty(ctx); return true; } @@ -327,14 +344,11 @@ static bool trans_fsgnjx_s(DisasContext *ctx, arg_fsgnjx_s *a) */ tcg_gen_andi_i64(dest, rs2, MAKE_64BIT_MASK(31, 1)); tcg_gen_xor_i64(dest, rs1, dest); - - tcg_temp_free_i64(rs2); } - /* signed-extended intead of nanboxing for result if enable zfinx */ + /* signed-extended instead of nanboxing for result if enable zfinx */ if (ctx->cfg_ptr->ext_zfinx) { tcg_gen_ext32s_i64(dest, dest); } - tcg_temp_free_i64(rs1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -349,7 +363,7 @@ static bool trans_fmin_s(DisasContext *ctx, arg_fmin_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fmin_s(dest, cpu_env, src1, src2); + gen_helper_fmin_s(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -364,7 +378,7 @@ static bool trans_fmax_s(DisasContext *ctx, arg_fmax_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fmax_s(dest, cpu_env, src1, src2); + gen_helper_fmax_s(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -379,7 +393,7 @@ static bool trans_fcvt_w_s(DisasContext *ctx, arg_fcvt_w_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_w_s(dest, cpu_env, src1); + gen_helper_fcvt_w_s(dest, tcg_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -393,7 +407,7 @@ static bool trans_fcvt_wu_s(DisasContext *ctx, arg_fcvt_wu_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_wu_s(dest, cpu_env, src1); + gen_helper_fcvt_wu_s(dest, tcg_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -425,7 +439,7 @@ static bool trans_feq_s(DisasContext *ctx, arg_feq_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_feq_s(dest, cpu_env, src1, src2); + gen_helper_feq_s(dest, tcg_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -439,7 +453,7 @@ static bool trans_flt_s(DisasContext *ctx, arg_flt_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_flt_s(dest, cpu_env, src1, src2); + gen_helper_flt_s(dest, tcg_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -453,7 +467,7 @@ static bool trans_fle_s(DisasContext *ctx, arg_fle_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fle_s(dest, cpu_env, src1, src2); + gen_helper_fle_s(dest, tcg_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -466,7 +480,7 @@ static bool trans_fclass_s(DisasContext *ctx, arg_fclass_s *a) TCGv dest = dest_gpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); - gen_helper_fclass_s(dest, cpu_env, src1); + gen_helper_fclass_s(dest, tcg_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -480,7 +494,7 @@ static bool trans_fcvt_s_w(DisasContext *ctx, arg_fcvt_s_w *a) TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_s_w(dest, cpu_env, src); + gen_helper_fcvt_s_w(dest, tcg_env, src); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -495,7 +509,7 @@ static bool trans_fcvt_s_wu(DisasContext *ctx, arg_fcvt_s_wu *a) TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_s_wu(dest, cpu_env, src); + gen_helper_fcvt_s_wu(dest, tcg_env, src); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -527,7 +541,7 @@ static bool trans_fcvt_l_s(DisasContext *ctx, arg_fcvt_l_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_l_s(dest, cpu_env, src1); + gen_helper_fcvt_l_s(dest, tcg_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -542,7 +556,7 @@ static bool trans_fcvt_lu_s(DisasContext *ctx, arg_fcvt_lu_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_lu_s(dest, cpu_env, src1); + gen_helper_fcvt_lu_s(dest, tcg_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -557,7 +571,7 @@ static bool trans_fcvt_s_l(DisasContext *ctx, arg_fcvt_s_l *a) TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_s_l(dest, cpu_env, src); + gen_helper_fcvt_s_l(dest, tcg_env, src); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -573,7 +587,7 @@ static bool trans_fcvt_s_lu(DisasContext *ctx, arg_fcvt_s_lu *a) TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_s_lu(dest, cpu_env, src); + gen_helper_fcvt_s_lu(dest, tcg_env, src); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; diff --git a/target/riscv/insn_trans/trans_rvh.c.inc b/target/riscv/insn_trans/trans_rvh.c.inc index cebcb3f8f6..aa9d41c18c 100644 --- a/target/riscv/insn_trans/trans_rvh.c.inc +++ b/target/riscv/insn_trans/trans_rvh.c.inc @@ -16,160 +16,139 @@ * this program. If not, see . */ -#ifndef CONFIG_USER_ONLY -static bool check_access(DisasContext *ctx) -{ - if (!ctx->hlsx) { - if (ctx->virt_enabled) { - generate_exception(ctx, RISCV_EXCP_VIRT_INSTRUCTION_FAULT); - } else { - generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST); - } - return false; - } - return true; -} -#endif - -static bool do_hlv(DisasContext *ctx, arg_r2 *a, MemOp mop) -{ #ifdef CONFIG_USER_ONLY - return false; +#define do_hlv(ctx, a, func) false +#define do_hsv(ctx, a, func) false #else - if (check_access(ctx)) { - TCGv dest = dest_gpr(ctx, a->rd); - TCGv addr = get_gpr(ctx, a->rs1, EXT_NONE); - int mem_idx = ctx->mem_idx | TB_FLAGS_PRIV_HYP_ACCESS_MASK; - tcg_gen_qemu_ld_tl(dest, addr, mem_idx, mop); - gen_set_gpr(ctx, a->rd, dest); - } - return true; -#endif +static void gen_helper_hyp_hlv_b(TCGv r, TCGv_env e, TCGv a) +{ + gen_helper_hyp_hlv_bu(r, e, a); + tcg_gen_ext8s_tl(r, r); } +static void gen_helper_hyp_hlv_h(TCGv r, TCGv_env e, TCGv a) +{ + gen_helper_hyp_hlv_hu(r, e, a); + tcg_gen_ext16s_tl(r, r); +} + +static void gen_helper_hyp_hlv_w(TCGv r, TCGv_env e, TCGv a) +{ + gen_helper_hyp_hlv_wu(r, e, a); + tcg_gen_ext32s_tl(r, r); +} + +static bool do_hlv(DisasContext *ctx, arg_r2 *a, + void (*func)(TCGv, TCGv_env, TCGv)) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv addr = get_gpr(ctx, a->rs1, EXT_NONE); + + decode_save_opc(ctx); + func(dest, tcg_env, addr); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +static bool do_hsv(DisasContext *ctx, arg_r2_s *a, + void (*func)(TCGv_env, TCGv, TCGv)) +{ + TCGv addr = get_gpr(ctx, a->rs1, EXT_NONE); + TCGv data = get_gpr(ctx, a->rs2, EXT_NONE); + + decode_save_opc(ctx); + func(tcg_env, addr, data); + return true; +} +#endif /* CONFIG_USER_ONLY */ + static bool trans_hlv_b(DisasContext *ctx, arg_hlv_b *a) { REQUIRE_EXT(ctx, RVH); - return do_hlv(ctx, a, MO_SB); + return do_hlv(ctx, a, gen_helper_hyp_hlv_b); } static bool trans_hlv_h(DisasContext *ctx, arg_hlv_h *a) { REQUIRE_EXT(ctx, RVH); - return do_hlv(ctx, a, MO_TESW); + return do_hlv(ctx, a, gen_helper_hyp_hlv_h); } static bool trans_hlv_w(DisasContext *ctx, arg_hlv_w *a) { REQUIRE_EXT(ctx, RVH); - return do_hlv(ctx, a, MO_TESL); + return do_hlv(ctx, a, gen_helper_hyp_hlv_w); } static bool trans_hlv_bu(DisasContext *ctx, arg_hlv_bu *a) { REQUIRE_EXT(ctx, RVH); - return do_hlv(ctx, a, MO_UB); + return do_hlv(ctx, a, gen_helper_hyp_hlv_bu); } static bool trans_hlv_hu(DisasContext *ctx, arg_hlv_hu *a) { REQUIRE_EXT(ctx, RVH); - return do_hlv(ctx, a, MO_TEUW); -} - -static bool do_hsv(DisasContext *ctx, arg_r2_s *a, MemOp mop) -{ -#ifdef CONFIG_USER_ONLY - return false; -#else - if (check_access(ctx)) { - TCGv addr = get_gpr(ctx, a->rs1, EXT_NONE); - TCGv data = get_gpr(ctx, a->rs2, EXT_NONE); - int mem_idx = ctx->mem_idx | TB_FLAGS_PRIV_HYP_ACCESS_MASK; - tcg_gen_qemu_st_tl(data, addr, mem_idx, mop); - } - return true; -#endif + return do_hlv(ctx, a, gen_helper_hyp_hlv_hu); } static bool trans_hsv_b(DisasContext *ctx, arg_hsv_b *a) { REQUIRE_EXT(ctx, RVH); - return do_hsv(ctx, a, MO_SB); + return do_hsv(ctx, a, gen_helper_hyp_hsv_b); } static bool trans_hsv_h(DisasContext *ctx, arg_hsv_h *a) { REQUIRE_EXT(ctx, RVH); - return do_hsv(ctx, a, MO_TESW); + return do_hsv(ctx, a, gen_helper_hyp_hsv_h); } static bool trans_hsv_w(DisasContext *ctx, arg_hsv_w *a) { REQUIRE_EXT(ctx, RVH); - return do_hsv(ctx, a, MO_TESL); + return do_hsv(ctx, a, gen_helper_hyp_hsv_w); } static bool trans_hlv_wu(DisasContext *ctx, arg_hlv_wu *a) { REQUIRE_64BIT(ctx); REQUIRE_EXT(ctx, RVH); - return do_hlv(ctx, a, MO_TEUL); + return do_hlv(ctx, a, gen_helper_hyp_hlv_wu); } static bool trans_hlv_d(DisasContext *ctx, arg_hlv_d *a) { REQUIRE_64BIT(ctx); REQUIRE_EXT(ctx, RVH); - return do_hlv(ctx, a, MO_TEUQ); + return do_hlv(ctx, a, gen_helper_hyp_hlv_d); } static bool trans_hsv_d(DisasContext *ctx, arg_hsv_d *a) { REQUIRE_64BIT(ctx); REQUIRE_EXT(ctx, RVH); - return do_hsv(ctx, a, MO_TEUQ); + return do_hsv(ctx, a, gen_helper_hyp_hsv_d); } -#ifndef CONFIG_USER_ONLY -static bool do_hlvx(DisasContext *ctx, arg_r2 *a, - void (*func)(TCGv, TCGv_env, TCGv)) -{ - if (check_access(ctx)) { - TCGv dest = dest_gpr(ctx, a->rd); - TCGv addr = get_gpr(ctx, a->rs1, EXT_NONE); - func(dest, cpu_env, addr); - gen_set_gpr(ctx, a->rd, dest); - } - return true; -} -#endif - static bool trans_hlvx_hu(DisasContext *ctx, arg_hlvx_hu *a) { REQUIRE_EXT(ctx, RVH); -#ifndef CONFIG_USER_ONLY - return do_hlvx(ctx, a, gen_helper_hyp_hlvx_hu); -#else - return false; -#endif + return do_hlv(ctx, a, gen_helper_hyp_hlvx_hu); } static bool trans_hlvx_wu(DisasContext *ctx, arg_hlvx_wu *a) { REQUIRE_EXT(ctx, RVH); -#ifndef CONFIG_USER_ONLY - return do_hlvx(ctx, a, gen_helper_hyp_hlvx_wu); -#else - return false; -#endif + return do_hlv(ctx, a, gen_helper_hyp_hlvx_wu); } static bool trans_hfence_gvma(DisasContext *ctx, arg_sfence_vma *a) { REQUIRE_EXT(ctx, RVH); #ifndef CONFIG_USER_ONLY - gen_helper_hyp_gvma_tlb_flush(cpu_env); + decode_save_opc(ctx); + gen_helper_hyp_gvma_tlb_flush(tcg_env); return true; #endif return false; @@ -179,7 +158,8 @@ static bool trans_hfence_vvma(DisasContext *ctx, arg_sfence_vma *a) { REQUIRE_EXT(ctx, RVH); #ifndef CONFIG_USER_ONLY - gen_helper_hyp_tlb_flush(cpu_env); + decode_save_opc(ctx); + gen_helper_hyp_tlb_flush(tcg_env); return true; #endif return false; diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index f1342f30f8..ad40d3e87f 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -32,17 +32,15 @@ static bool trans_c64_illegal(DisasContext *ctx, arg_empty *a) static bool trans_lui(DisasContext *ctx, arg_lui *a) { - if (a->rd != 0) { - gen_set_gpri(ctx, a->rd, a->imm); - } + gen_set_gpri(ctx, a->rd, a->imm); return true; } static bool trans_auipc(DisasContext *ctx, arg_auipc *a) { - if (a->rd != 0) { - gen_set_gpri(ctx, a->rd, a->imm + ctx->base.pc_next); - } + TCGv target_pc = dest_gpr(ctx, a->rd); + gen_pc_plus_diff(target_pc, ctx, a->imm); + gen_set_gpr(ctx, a->rd, target_pc); return true; } @@ -55,26 +53,33 @@ static bool trans_jal(DisasContext *ctx, arg_jal *a) static bool trans_jalr(DisasContext *ctx, arg_jalr *a) { TCGLabel *misaligned = NULL; + TCGv target_pc = tcg_temp_new(); + TCGv succ_pc = dest_gpr(ctx, a->rd); - tcg_gen_addi_tl(cpu_pc, get_gpr(ctx, a->rs1, EXT_NONE), a->imm); - tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2); + tcg_gen_addi_tl(target_pc, get_gpr(ctx, a->rs1, EXT_NONE), a->imm); + tcg_gen_andi_tl(target_pc, target_pc, (target_ulong)-2); - gen_set_pc(ctx, cpu_pc); - if (!has_ext(ctx, RVC)) { + if (get_xl(ctx) == MXL_RV32) { + tcg_gen_ext32s_tl(target_pc, target_pc); + } + + if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { TCGv t0 = tcg_temp_new(); misaligned = gen_new_label(); - tcg_gen_andi_tl(t0, cpu_pc, 0x2); + tcg_gen_andi_tl(t0, target_pc, 0x2); tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, misaligned); - tcg_temp_free(t0); } - gen_set_gpri(ctx, a->rd, ctx->pc_succ_insn); - tcg_gen_lookup_and_goto_ptr(); + gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); + gen_set_gpr(ctx, a->rd, succ_pc); + + tcg_gen_mov_tl(cpu_pc, target_pc); + lookup_and_goto_ptr(ctx); if (misaligned) { gen_set_label(misaligned); - gen_exception_inst_addr_mis(ctx); + gen_exception_inst_addr_mis(ctx, target_pc); } ctx->base.is_jmp = DISAS_NORETURN; @@ -112,8 +117,6 @@ static TCGCond gen_compare_i128(bool bz, TCGv rl, tcg_gen_xor_tl(tmp, ah, bh); tcg_gen_and_tl(rl, rl, tmp); tcg_gen_xor_tl(rl, rh, rl); - - tcg_temp_free(tmp); } break; @@ -132,8 +135,6 @@ static TCGCond gen_compare_i128(bool bz, TCGv rl, /* seed third word with 1, which will be result */ tcg_gen_sub2_tl(tmp, rh, ah, one, tmp, zero); tcg_gen_sub2_tl(tmp, rl, tmp, rh, bh, zero); - - tcg_temp_free(tmp); } break; @@ -144,8 +145,6 @@ static TCGCond gen_compare_i128(bool bz, TCGv rl, if (invert) { cond = tcg_invert_cond(cond); } - - tcg_temp_free(rh); return cond; } @@ -164,6 +163,7 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) TCGLabel *l = gen_new_label(); TCGv src1 = get_gpr(ctx, a->rs1, EXT_SIGN); TCGv src2 = get_gpr(ctx, a->rs2, EXT_SIGN); + target_ulong orig_pc_save = ctx->pc_save; if (get_xl(ctx) == MXL_RV128) { TCGv src1h = get_gprh(ctx, a->rs1); @@ -173,21 +173,24 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) cond = gen_compare_i128(a->rs2 == 0, tmp, src1, src1h, src2, src2h, cond); tcg_gen_brcondi_tl(cond, tmp, 0, l); - - tcg_temp_free(tmp); } else { tcg_gen_brcond_tl(cond, src1, src2, l); } - gen_goto_tb(ctx, 1, ctx->pc_succ_insn); + gen_goto_tb(ctx, 1, ctx->cur_insn_len); + ctx->pc_save = orig_pc_save; gen_set_label(l); /* branch taken */ - if (!has_ext(ctx, RVC) && ((ctx->base.pc_next + a->imm) & 0x3)) { + if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca && + (a->imm & 0x3)) { /* misaligned */ - gen_exception_inst_addr_mis(ctx); + TCGv target_pc = tcg_temp_new(); + gen_pc_plus_diff(target_pc, ctx, a->imm); + gen_exception_inst_addr_mis(ctx, target_pc); } else { - gen_goto_tb(ctx, 0, ctx->base.pc_next + a->imm); + gen_goto_tb(ctx, 0, a->imm); } + ctx->pc_save = -1; ctx->base.is_jmp = DISAS_NORETURN; return true; @@ -258,18 +261,25 @@ static bool gen_load_i128(DisasContext *ctx, arg_lb *a, MemOp memop) } gen_set_gpr128(ctx, a->rd, destl, desth); - - tcg_temp_free(addrl); return true; } static bool gen_load(DisasContext *ctx, arg_lb *a, MemOp memop) { + bool out; + + decode_save_opc(ctx); if (get_xl(ctx) == MXL_RV128) { - return gen_load_i128(ctx, a, memop); + out = gen_load_i128(ctx, a, memop); } else { - return gen_load_tl(ctx, a, memop); + out = gen_load_tl(ctx, a, memop); } + + if (ctx->ztso) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } + + return out; } static bool trans_lb(DisasContext *ctx, arg_lb *a) @@ -326,6 +336,10 @@ static bool gen_store_tl(DisasContext *ctx, arg_sb *a, MemOp memop) TCGv addr = get_address(ctx, a->rs1, a->imm); TCGv data = get_gpr(ctx, a->rs2, EXT_NONE); + if (ctx->ztso) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, memop); return true; } @@ -347,13 +361,12 @@ static bool gen_store_i128(DisasContext *ctx, arg_sb *a, MemOp memop) tcg_gen_addi_tl(addrl, addrl, 8); tcg_gen_qemu_st_tl(src2h, addrl, ctx->mem_idx, MO_TEUQ); } - - tcg_temp_free(addrl); return true; } static bool gen_store(DisasContext *ctx, arg_sb *a, MemOp memop) { + decode_save_opc(ctx); if (get_xl(ctx) == MXL_RV128) { return gen_store_i128(ctx, a, memop); } else { @@ -570,14 +583,6 @@ static void gen_sll_i128(TCGv destl, TCGv desth, tcg_gen_movcond_tl(TCG_COND_NE, destl, hs, zero, zero, ll); tcg_gen_movcond_tl(TCG_COND_NE, desth, hs, zero, ll, h1); - - tcg_temp_free(ls); - tcg_temp_free(rs); - tcg_temp_free(hs); - tcg_temp_free(ll); - tcg_temp_free(lr); - tcg_temp_free(h0); - tcg_temp_free(h1); } static bool trans_sll(DisasContext *ctx, arg_sll *a) @@ -620,14 +625,6 @@ static void gen_srl_i128(TCGv destl, TCGv desth, tcg_gen_movcond_tl(TCG_COND_NE, destl, hs, zero, h1, h0); tcg_gen_movcond_tl(TCG_COND_NE, desth, hs, zero, zero, h1); - - tcg_temp_free(ls); - tcg_temp_free(rs); - tcg_temp_free(hs); - tcg_temp_free(ll); - tcg_temp_free(lr); - tcg_temp_free(h0); - tcg_temp_free(h1); } static bool trans_srl(DisasContext *ctx, arg_srl *a) @@ -661,14 +658,6 @@ static void gen_sra_i128(TCGv destl, TCGv desth, tcg_gen_movcond_tl(TCG_COND_NE, destl, hs, zero, h1, h0); tcg_gen_movcond_tl(TCG_COND_NE, desth, hs, zero, lr, h1); - - tcg_temp_free(ls); - tcg_temp_free(rs); - tcg_temp_free(hs); - tcg_temp_free(ll); - tcg_temp_free(lr); - tcg_temp_free(h0); - tcg_temp_free(h1); } static bool trans_sra(DisasContext *ctx, arg_sra *a) @@ -796,6 +785,22 @@ static bool trans_srad(DisasContext *ctx, arg_srad *a) return gen_shift(ctx, a, EXT_SIGN, tcg_gen_sar_tl, NULL); } +static bool trans_pause(DisasContext *ctx, arg_pause *a) +{ + if (!ctx->cfg_ptr->ext_zihintpause) { + return false; + } + + /* + * PAUSE is a no-op in QEMU, + * end the TB and return to main loop + */ + gen_update_pc(ctx, ctx->cur_insn_len); + exit_tb(ctx); + ctx->base.is_jmp = DISAS_NORETURN; + + return true; +} static bool trans_fence(DisasContext *ctx, arg_fence *a) { @@ -806,7 +811,7 @@ static bool trans_fence(DisasContext *ctx, arg_fence *a) static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a) { - if (!ctx->cfg_ptr->ext_ifencei) { + if (!ctx->cfg_ptr->ext_zifencei) { return false; } @@ -814,17 +819,19 @@ static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a) * FENCE_I is a no-op in QEMU, * however we need to end the translation block */ - gen_set_pc_imm(ctx, ctx->pc_succ_insn); - tcg_gen_exit_tb(NULL, 0); + gen_update_pc(ctx, ctx->cur_insn_len); + exit_tb(ctx); ctx->base.is_jmp = DISAS_NORETURN; return true; } static bool do_csr_post(DisasContext *ctx) { + /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ + decode_save_opc(ctx); /* We may have changed important cpu state -- exit to main loop. */ - gen_set_pc_imm(ctx, ctx->pc_succ_insn); - tcg_gen_exit_tb(NULL, 0); + gen_update_pc(ctx, ctx->cur_insn_len); + exit_tb(ctx); ctx->base.is_jmp = DISAS_NORETURN; return true; } @@ -834,10 +841,8 @@ static bool do_csrr(DisasContext *ctx, int rd, int rc) TCGv dest = dest_gpr(ctx, rd); TCGv_i32 csr = tcg_constant_i32(rc); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_csrr(dest, cpu_env, csr); + translator_io_start(&ctx->base); + gen_helper_csrr(dest, tcg_env, csr); gen_set_gpr(ctx, rd, dest); return do_csr_post(ctx); } @@ -846,10 +851,8 @@ static bool do_csrw(DisasContext *ctx, int rc, TCGv src) { TCGv_i32 csr = tcg_constant_i32(rc); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_csrw(cpu_env, csr, src); + translator_io_start(&ctx->base); + gen_helper_csrw(tcg_env, csr, src); return do_csr_post(ctx); } @@ -858,10 +861,8 @@ static bool do_csrrw(DisasContext *ctx, int rd, int rc, TCGv src, TCGv mask) TCGv dest = dest_gpr(ctx, rd); TCGv_i32 csr = tcg_constant_i32(rc); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_csrrw(dest, cpu_env, csr, src, mask); + translator_io_start(&ctx->base); + gen_helper_csrrw(dest, tcg_env, csr, src, mask); gen_set_gpr(ctx, rd, dest); return do_csr_post(ctx); } @@ -872,11 +873,9 @@ static bool do_csrr_i128(DisasContext *ctx, int rd, int rc) TCGv desth = dest_gprh(ctx, rd); TCGv_i32 csr = tcg_constant_i32(rc); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_csrr_i128(destl, cpu_env, csr); - tcg_gen_ld_tl(desth, cpu_env, offsetof(CPURISCVState, retxh)); + translator_io_start(&ctx->base); + gen_helper_csrr_i128(destl, tcg_env, csr); + tcg_gen_ld_tl(desth, tcg_env, offsetof(CPURISCVState, retxh)); gen_set_gpr128(ctx, rd, destl, desth); return do_csr_post(ctx); } @@ -885,10 +884,8 @@ static bool do_csrw_i128(DisasContext *ctx, int rc, TCGv srcl, TCGv srch) { TCGv_i32 csr = tcg_constant_i32(rc); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_csrw_i128(cpu_env, csr, srcl, srch); + translator_io_start(&ctx->base); + gen_helper_csrw_i128(tcg_env, csr, srcl, srch); return do_csr_post(ctx); } @@ -899,11 +896,9 @@ static bool do_csrrw_i128(DisasContext *ctx, int rd, int rc, TCGv desth = dest_gprh(ctx, rd); TCGv_i32 csr = tcg_constant_i32(rc); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_csrrw_i128(destl, cpu_env, csr, srcl, srch, maskl, maskh); - tcg_gen_ld_tl(desth, cpu_env, offsetof(CPURISCVState, retxh)); + translator_io_start(&ctx->base); + gen_helper_csrrw_i128(destl, tcg_env, csr, srcl, srch, maskl, maskh); + tcg_gen_ld_tl(desth, tcg_env, offsetof(CPURISCVState, retxh)); gen_set_gpr128(ctx, rd, destl, desth); return do_csr_post(ctx); } diff --git a/target/riscv/insn_trans/trans_rvk.c.inc b/target/riscv/insn_trans/trans_rvk.c.inc index 90f4eeff60..6600c710a7 100644 --- a/target/riscv/insn_trans/trans_rvk.c.inc +++ b/target/riscv/insn_trans/trans_rvk.c.inc @@ -161,9 +161,6 @@ static bool gen_sha256(DisasContext *ctx, arg_r2 *a, DisasExtend ext, tcg_gen_ext_i32_tl(dest, t1); gen_set_gpr(ctx, a->rd, dest); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); return true; } @@ -212,9 +209,6 @@ static bool gen_sha512_rv32(DisasContext *ctx, arg_r *a, DisasExtend ext, tcg_gen_trunc_i64_tl(dest, t1); gen_set_gpr(ctx, a->rd, dest); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); return true; } @@ -271,9 +265,6 @@ static bool gen_sha512h_rv32(DisasContext *ctx, arg_r *a, DisasExtend ext, tcg_gen_trunc_i64_tl(dest, t1); gen_set_gpr(ctx, a->rd, dest); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); return true; } @@ -310,9 +301,6 @@ static bool gen_sha512_rv64(DisasContext *ctx, arg_r2 *a, DisasExtend ext, tcg_gen_trunc_i64_tl(dest, t1); gen_set_gpr(ctx, a->rd, dest); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); return true; } @@ -359,9 +347,6 @@ static bool gen_sm3(DisasContext *ctx, arg_r2 *a, int32_t b, int32_t c) tcg_gen_xor_i32(t1, t1, t0); tcg_gen_ext_i32_tl(dest, t1); gen_set_gpr(ctx, a->rd, dest); - - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); return true; } diff --git a/target/riscv/insn_trans/trans_rvm.c.inc b/target/riscv/insn_trans/trans_rvm.c.inc index 16b029edf0..795f0ccf14 100644 --- a/target/riscv/insn_trans/trans_rvm.c.inc +++ b/target/riscv/insn_trans/trans_rvm.c.inc @@ -18,6 +18,12 @@ * this program. If not, see . */ +#define REQUIRE_M_OR_ZMMUL(ctx) do { \ + if (!ctx->cfg_ptr->ext_zmmul && !has_ext(ctx, RVM)) { \ + return false; \ + } \ +} while (0) + static void gen_mulhu_i128(TCGv r2, TCGv r3, TCGv al, TCGv ah, TCGv bl, TCGv bh) { TCGv tmpl = tcg_temp_new(); @@ -39,9 +45,6 @@ static void gen_mulhu_i128(TCGv r2, TCGv r3, TCGv al, TCGv ah, TCGv bl, TCGv bh) tcg_gen_mulu2_tl(tmpl, tmph, ah, bh); tcg_gen_add2_tl(r2, r3, r2, r3, tmpl, tmph); - - tcg_temp_free(tmpl); - tcg_temp_free(tmph); } static void gen_mul_i128(TCGv rl, TCGv rh, @@ -57,15 +60,11 @@ static void gen_mul_i128(TCGv rl, TCGv rh, tcg_gen_add2_tl(rh, tmpx, rh, zero, tmpl, tmph); tcg_gen_mulu2_tl(tmpl, tmph, rs1h, rs2l); tcg_gen_add2_tl(rh, tmph, rh, tmpx, tmpl, tmph); - - tcg_temp_free(tmpl); - tcg_temp_free(tmph); - tcg_temp_free(tmpx); } static bool trans_mul(DisasContext *ctx, arg_mul *a) { - REQUIRE_EXT(ctx, RVM); + REQUIRE_M_OR_ZMMUL(ctx); return gen_arith(ctx, a, EXT_NONE, tcg_gen_mul_tl, gen_mul_i128); } @@ -86,11 +85,6 @@ static void gen_mulh_i128(TCGv rl, TCGv rh, tcg_gen_and_tl(t1h, t1h, rs1h); tcg_gen_sub2_tl(t0l, t0h, rl, rh, t0l, t0h); tcg_gen_sub2_tl(rl, rh, t0l, t0h, t1l, t1h); - - tcg_temp_free(t0l); - tcg_temp_free(t0h); - tcg_temp_free(t1l); - tcg_temp_free(t1h); } static void gen_mulh(TCGv ret, TCGv s1, TCGv s2) @@ -98,7 +92,6 @@ static void gen_mulh(TCGv ret, TCGv s1, TCGv s2) TCGv discard = tcg_temp_new(); tcg_gen_muls2_tl(discard, ret, s1, s2); - tcg_temp_free(discard); } static void gen_mulh_w(TCGv ret, TCGv s1, TCGv s2) @@ -109,7 +102,7 @@ static void gen_mulh_w(TCGv ret, TCGv s1, TCGv s2) static bool trans_mulh(DisasContext *ctx, arg_mulh *a) { - REQUIRE_EXT(ctx, RVM); + REQUIRE_M_OR_ZMMUL(ctx); return gen_arith_per_ol(ctx, a, EXT_SIGN, gen_mulh, gen_mulh_w, gen_mulh_i128); } @@ -126,9 +119,6 @@ static void gen_mulhsu_i128(TCGv rl, TCGv rh, tcg_gen_and_tl(t0l, t0h, rs2l); tcg_gen_and_tl(t0h, t0h, rs2h); tcg_gen_sub2_tl(rl, rh, rl, rh, t0l, t0h); - - tcg_temp_free(t0l); - tcg_temp_free(t0h); } static void gen_mulhsu(TCGv ret, TCGv arg1, TCGv arg2) @@ -141,9 +131,6 @@ static void gen_mulhsu(TCGv ret, TCGv arg1, TCGv arg2) tcg_gen_sari_tl(rl, arg1, TARGET_LONG_BITS - 1); tcg_gen_and_tl(rl, rl, arg2); tcg_gen_sub_tl(ret, rh, rl); - - tcg_temp_free(rl); - tcg_temp_free(rh); } static void gen_mulhsu_w(TCGv ret, TCGv arg1, TCGv arg2) @@ -154,14 +141,12 @@ static void gen_mulhsu_w(TCGv ret, TCGv arg1, TCGv arg2) tcg_gen_ext32s_tl(t1, arg1); tcg_gen_ext32u_tl(t2, arg2); tcg_gen_mul_tl(ret, t1, t2); - tcg_temp_free(t1); - tcg_temp_free(t2); tcg_gen_sari_tl(ret, ret, 32); } static bool trans_mulhsu(DisasContext *ctx, arg_mulhsu *a) { - REQUIRE_EXT(ctx, RVM); + REQUIRE_M_OR_ZMMUL(ctx); return gen_arith_per_ol(ctx, a, EXT_NONE, gen_mulhsu, gen_mulhsu_w, gen_mulhsu_i128); } @@ -171,12 +156,11 @@ static void gen_mulhu(TCGv ret, TCGv s1, TCGv s2) TCGv discard = tcg_temp_new(); tcg_gen_mulu2_tl(discard, ret, s1, s2); - tcg_temp_free(discard); } static bool trans_mulhu(DisasContext *ctx, arg_mulhu *a) { - REQUIRE_EXT(ctx, RVM); + REQUIRE_M_OR_ZMMUL(ctx); /* gen_mulh_w works for either sign as input. */ return gen_arith_per_ol(ctx, a, EXT_ZERO, gen_mulhu, gen_mulh_w, gen_mulhu_i128); @@ -185,8 +169,8 @@ static bool trans_mulhu(DisasContext *ctx, arg_mulhu *a) static void gen_div_i128(TCGv rdl, TCGv rdh, TCGv rs1l, TCGv rs1h, TCGv rs2l, TCGv rs2h) { - gen_helper_divs_i128(rdl, cpu_env, rs1l, rs1h, rs2l, rs2h); - tcg_gen_ld_tl(rdh, cpu_env, offsetof(CPURISCVState, retxh)); + gen_helper_divs_i128(rdl, tcg_env, rs1l, rs1h, rs2l, rs2h); + tcg_gen_ld_tl(rdh, tcg_env, offsetof(CPURISCVState, retxh)); } static void gen_div(TCGv ret, TCGv source1, TCGv source2) @@ -217,9 +201,6 @@ static void gen_div(TCGv ret, TCGv source1, TCGv source2) tcg_gen_movcond_tl(TCG_COND_EQ, temp2, source2, zero, one, temp2); tcg_gen_div_tl(ret, temp1, temp2); - - tcg_temp_free(temp1); - tcg_temp_free(temp2); } static bool trans_div(DisasContext *ctx, arg_div *a) @@ -231,8 +212,8 @@ static bool trans_div(DisasContext *ctx, arg_div *a) static void gen_divu_i128(TCGv rdl, TCGv rdh, TCGv rs1l, TCGv rs1h, TCGv rs2l, TCGv rs2h) { - gen_helper_divu_i128(rdl, cpu_env, rs1l, rs1h, rs2l, rs2h); - tcg_gen_ld_tl(rdh, cpu_env, offsetof(CPURISCVState, retxh)); + gen_helper_divu_i128(rdl, tcg_env, rs1l, rs1h, rs2l, rs2h); + tcg_gen_ld_tl(rdh, tcg_env, offsetof(CPURISCVState, retxh)); } static void gen_divu(TCGv ret, TCGv source1, TCGv source2) @@ -252,9 +233,6 @@ static void gen_divu(TCGv ret, TCGv source1, TCGv source2) tcg_gen_movcond_tl(TCG_COND_EQ, temp1, source2, zero, max, source1); tcg_gen_movcond_tl(TCG_COND_EQ, temp2, source2, zero, one, source2); tcg_gen_divu_tl(ret, temp1, temp2); - - tcg_temp_free(temp1); - tcg_temp_free(temp2); } static bool trans_divu(DisasContext *ctx, arg_divu *a) @@ -266,8 +244,8 @@ static bool trans_divu(DisasContext *ctx, arg_divu *a) static void gen_rem_i128(TCGv rdl, TCGv rdh, TCGv rs1l, TCGv rs1h, TCGv rs2l, TCGv rs2h) { - gen_helper_rems_i128(rdl, cpu_env, rs1l, rs1h, rs2l, rs2h); - tcg_gen_ld_tl(rdh, cpu_env, offsetof(CPURISCVState, retxh)); + gen_helper_rems_i128(rdl, tcg_env, rs1l, rs1h, rs2l, rs2h); + tcg_gen_ld_tl(rdh, tcg_env, offsetof(CPURISCVState, retxh)); } static void gen_rem(TCGv ret, TCGv source1, TCGv source2) @@ -300,9 +278,6 @@ static void gen_rem(TCGv ret, TCGv source1, TCGv source2) /* If div by zero, the required result is the original dividend. */ tcg_gen_movcond_tl(TCG_COND_EQ, ret, source2, zero, source1, temp1); - - tcg_temp_free(temp1); - tcg_temp_free(temp2); } static bool trans_rem(DisasContext *ctx, arg_rem *a) @@ -314,8 +289,8 @@ static bool trans_rem(DisasContext *ctx, arg_rem *a) static void gen_remu_i128(TCGv rdl, TCGv rdh, TCGv rs1l, TCGv rs1h, TCGv rs2l, TCGv rs2h) { - gen_helper_remu_i128(rdl, cpu_env, rs1l, rs1h, rs2l, rs2h); - tcg_gen_ld_tl(rdh, cpu_env, offsetof(CPURISCVState, retxh)); + gen_helper_remu_i128(rdl, tcg_env, rs1l, rs1h, rs2l, rs2h); + tcg_gen_ld_tl(rdh, tcg_env, offsetof(CPURISCVState, retxh)); } static void gen_remu(TCGv ret, TCGv source1, TCGv source2) @@ -336,8 +311,6 @@ static void gen_remu(TCGv ret, TCGv source1, TCGv source2) /* If div by zero, the required result is the original dividend. */ tcg_gen_movcond_tl(TCG_COND_EQ, ret, source2, zero, source1, temp); - - tcg_temp_free(temp); } static bool trans_remu(DisasContext *ctx, arg_remu *a) @@ -349,7 +322,7 @@ static bool trans_remu(DisasContext *ctx, arg_remu *a) static bool trans_mulw(DisasContext *ctx, arg_mulw *a) { REQUIRE_64_OR_128BIT(ctx); - REQUIRE_EXT(ctx, RVM); + REQUIRE_M_OR_ZMMUL(ctx); ctx->ol = MXL_RV32; return gen_arith(ctx, a, EXT_NONE, tcg_gen_mul_tl, NULL); } @@ -389,7 +362,7 @@ static bool trans_remuw(DisasContext *ctx, arg_remuw *a) static bool trans_muld(DisasContext *ctx, arg_muld *a) { REQUIRE_128BIT(ctx); - REQUIRE_EXT(ctx, RVM); + REQUIRE_M_OR_ZMMUL(ctx); ctx->ol = MXL_RV64; return gen_arith(ctx, a, EXT_SIGN, tcg_gen_mul_tl, NULL); } diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 391c61fe93..7d84e7d812 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -29,21 +29,22 @@ static inline bool is_overlapped(const int8_t astart, int8_t asize, static bool require_rvv(DisasContext *s) { - return s->mstatus_vs != 0; + return s->mstatus_vs != EXT_STATUS_DISABLED; } static bool require_rvf(DisasContext *s) { - if (s->mstatus_fs == 0) { + if (s->mstatus_fs == EXT_STATUS_DISABLED) { return false; } switch (s->sew) { case MO_16: + return s->cfg_ptr->ext_zvfh; case MO_32: - return has_ext(s, RVF); + return s->cfg_ptr->ext_zve32f; case MO_64: - return has_ext(s, RVD); + return s->cfg_ptr->ext_zve64d; default: return false; } @@ -51,63 +52,38 @@ static bool require_rvf(DisasContext *s) static bool require_scale_rvf(DisasContext *s) { - if (s->mstatus_fs == 0) { + if (s->mstatus_fs == EXT_STATUS_DISABLED) { return false; } switch (s->sew) { case MO_8: + return s->cfg_ptr->ext_zvfh; case MO_16: - return has_ext(s, RVF); + return s->cfg_ptr->ext_zve32f; case MO_32: - return has_ext(s, RVD); + return s->cfg_ptr->ext_zve64d; default: return false; } } -static bool require_zve32f(DisasContext *s) +static bool require_scale_rvfmin(DisasContext *s) { - /* RVV + Zve32f = RVV. */ - if (has_ext(s, RVV)) { - return true; + if (s->mstatus_fs == EXT_STATUS_DISABLED) { + return false; } - /* Zve32f doesn't support FP64. (Section 18.2) */ - return s->cfg_ptr->ext_zve32f ? s->sew <= MO_32 : true; -} - -static bool require_scale_zve32f(DisasContext *s) -{ - /* RVV + Zve32f = RVV. */ - if (has_ext(s, RVV)) { - return true; + switch (s->sew) { + case MO_8: + return s->cfg_ptr->ext_zvfhmin; + case MO_16: + return s->cfg_ptr->ext_zve32f; + case MO_32: + return s->cfg_ptr->ext_zve64d; + default: + return false; } - - /* Zve32f doesn't support FP64. (Section 18.2) */ - return s->cfg_ptr->ext_zve64f ? s->sew <= MO_16 : true; -} - -static bool require_zve64f(DisasContext *s) -{ - /* RVV + Zve64f = RVV. */ - if (has_ext(s, RVV)) { - return true; - } - - /* Zve64f doesn't support FP64. (Section 18.2) */ - return s->cfg_ptr->ext_zve64f ? s->sew <= MO_32 : true; -} - -static bool require_scale_zve64f(DisasContext *s) -{ - /* RVV + Zve64f = RVV. */ - if (has_ext(s, RVV)) { - return true; - } - - /* Zve64f doesn't support FP64. (Section 18.2) */ - return s->cfg_ptr->ext_zve64f ? s->sew <= MO_16 : true; } /* Destination vector register group cannot overlap source mask register. */ @@ -173,9 +149,7 @@ static bool do_vsetvl(DisasContext *s, int rd, int rs1, TCGv s2) { TCGv s1, dst; - if (!require_rvv(s) || - !(has_ext(s, RVV) || s->cfg_ptr->ext_zve32f || - s->cfg_ptr->ext_zve64f)) { + if (!require_rvv(s) || !s->cfg_ptr->ext_zve32f) { return false; } @@ -191,18 +165,13 @@ static bool do_vsetvl(DisasContext *s, int rd, int rs1, TCGv s2) s1 = get_gpr(s, rs1, EXT_ZERO); } - gen_helper_vsetvl(dst, cpu_env, s1, s2); + gen_helper_vsetvl(dst, tcg_env, s1, s2); gen_set_gpr(s, rd, dst); - mark_vs_dirty(s); + finalize_rvv_inst(s); - gen_set_pc_imm(s, s->pc_succ_insn); - tcg_gen_lookup_and_goto_ptr(); + gen_update_pc(s, s->cur_insn_len); + lookup_and_goto_ptr(s); s->base.is_jmp = DISAS_NORETURN; - - if (rd == 0 && rs1 == 0) { - tcg_temp_free(s1); - } - return true; } @@ -210,19 +179,17 @@ static bool do_vsetivli(DisasContext *s, int rd, TCGv s1, TCGv s2) { TCGv dst; - if (!require_rvv(s) || - !(has_ext(s, RVV) || s->cfg_ptr->ext_zve32f || - s->cfg_ptr->ext_zve64f)) { + if (!require_rvv(s) || !s->cfg_ptr->ext_zve32f) { return false; } dst = dest_gpr(s, rd); - gen_helper_vsetvl(dst, cpu_env, s1, s2); + gen_helper_vsetvl(dst, tcg_env, s1, s2); gen_set_gpr(s, rd, dst); - mark_vs_dirty(s); - gen_set_pc_imm(s, s->pc_succ_insn); - tcg_gen_lookup_and_goto_ptr(); + finalize_rvv_inst(s); + gen_update_pc(s, s->cur_insn_len); + lookup_and_goto_ptr(s); s->base.is_jmp = DISAS_NORETURN; return true; @@ -242,15 +209,15 @@ static bool trans_vsetvli(DisasContext *s, arg_vsetvli *a) static bool trans_vsetivli(DisasContext *s, arg_vsetivli *a) { - TCGv s1 = tcg_const_tl(a->rs1); - TCGv s2 = tcg_const_tl(a->zimm); + TCGv s1 = tcg_constant_tl(a->rs1); + TCGv s2 = tcg_constant_tl(a->zimm); return do_vsetivli(s, a->rd, s1, s2); } /* vector register offset from env */ static uint32_t vreg_ofs(DisasContext *s, int reg) { - return offsetof(CPURISCVState, vreg) + reg * s->cfg_ptr->vlen / 8; + return offsetof(CPURISCVState, vreg) + reg * s->cfg_ptr->vlenb; } /* check functions */ @@ -271,8 +238,8 @@ static bool vext_check_store(DisasContext *s, int vd, int nf, uint8_t eew) { int8_t emul = eew - s->sew + s->lmul; return (emul >= -3 && emul <= 3) && - require_align(vd, emul) && - require_nf(vd, nf, emul); + require_align(vd, emul) && + require_nf(vd, nf, emul); } /* @@ -315,13 +282,12 @@ static bool vext_check_st_index(DisasContext *s, int vd, int vs2, int nf, require_nf(vd, nf, s->lmul); /* - * All Zve* extensions support all vector load and store instructions, - * except Zve64* extensions do not support EEW=64 for index values - * when XLEN=32. (Section 18.2) + * V extension supports all vector load and store instructions, + * except V extension does not support EEW=64 for index values + * when XLEN=32. (Section 18.3) */ if (get_xl(s) == MXL_RV32) { - ret &= (!has_ext(s, RVV) && - s->cfg_ptr->ext_zve64f ? eew != MO_64 : true); + ret &= (eew != MO_64); } return ret; @@ -349,7 +315,7 @@ static bool vext_check_ld_index(DisasContext *s, int vd, int vs2, int8_t seg_vd; int8_t emul = eew - s->sew + s->lmul; bool ret = vext_check_st_index(s, vd, vs2, nf, eew) && - require_vm(vm, vd); + require_vm(vm, vd); /* Each segment register group has to follow overlap rules. */ for (int i = 0; i < nf; ++i) { @@ -379,8 +345,8 @@ static bool vext_check_ld_index(DisasContext *s, int vd, int vs2, static bool vext_check_ss(DisasContext *s, int vd, int vs, int vm) { return require_vm(vm, vd) && - require_align(vd, s->lmul) && - require_align(vs, s->lmul); + require_align(vd, s->lmul) && + require_align(vs, s->lmul); } /* @@ -399,7 +365,7 @@ static bool vext_check_ss(DisasContext *s, int vd, int vs, int vm) static bool vext_check_sss(DisasContext *s, int vd, int vs1, int vs2, int vm) { return vext_check_ss(s, vd, vs2, vm) && - require_align(vs1, s->lmul); + require_align(vs1, s->lmul); } static bool vext_check_ms(DisasContext *s, int vd, int vs) @@ -430,7 +396,7 @@ static bool vext_check_ms(DisasContext *s, int vd, int vs) static bool vext_check_mss(DisasContext *s, int vd, int vs1, int vs2) { bool ret = vext_check_ms(s, vd, vs2) && - require_align(vs1, s->lmul); + require_align(vs1, s->lmul); if (vd != vs1) { ret &= require_noover(vd, 0, vs1, s->lmul); } @@ -494,14 +460,14 @@ static bool vext_narrow_check_common(DisasContext *s, int vd, int vs2, static bool vext_check_ds(DisasContext *s, int vd, int vs, int vm) { return vext_wide_check_common(s, vd, vm) && - require_align(vs, s->lmul) && - require_noover(vd, s->lmul + 1, vs, s->lmul); + require_align(vs, s->lmul) && + require_noover(vd, s->lmul + 1, vs, s->lmul); } static bool vext_check_dd(DisasContext *s, int vd, int vs, int vm) { return vext_wide_check_common(s, vd, vm) && - require_align(vs, s->lmul + 1); + require_align(vs, s->lmul + 1); } /* @@ -519,8 +485,8 @@ static bool vext_check_dd(DisasContext *s, int vd, int vs, int vm) static bool vext_check_dss(DisasContext *s, int vd, int vs1, int vs2, int vm) { return vext_check_ds(s, vd, vs2, vm) && - require_align(vs1, s->lmul) && - require_noover(vd, s->lmul + 1, vs1, s->lmul); + require_align(vs1, s->lmul) && + require_noover(vd, s->lmul + 1, vs1, s->lmul); } /* @@ -541,7 +507,7 @@ static bool vext_check_dss(DisasContext *s, int vd, int vs1, int vs2, int vm) static bool vext_check_dds(DisasContext *s, int vd, int vs1, int vs2, int vm) { return vext_check_ds(s, vd, vs1, vm) && - require_align(vs2, s->lmul + 1); + require_align(vs2, s->lmul + 1); } static bool vext_check_sd(DisasContext *s, int vd, int vs, int vm) @@ -569,7 +535,7 @@ static bool vext_check_sd(DisasContext *s, int vd, int vs, int vm) static bool vext_check_sds(DisasContext *s, int vd, int vs1, int vs2, int vm) { return vext_check_sd(s, vd, vs2, vm) && - require_align(vs1, s->lmul); + require_align(vs1, s->lmul); } /* @@ -581,7 +547,7 @@ static bool vext_check_sds(DisasContext *s, int vd, int vs1, int vs2, int vm) */ static bool vext_check_reduction(DisasContext *s, int vs2) { - return require_align(vs2, s->lmul) && (s->vstart == 0); + return require_align(vs2, s->lmul) && s->vstart_eq_zero; } /* @@ -650,9 +616,6 @@ static bool ldst_us_trans(uint32_t vd, uint32_t rs1, uint32_t data, TCGv base; TCGv_i32 desc; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); - dest = tcg_temp_new_ptr(); mask = tcg_temp_new_ptr(); base = get_gpr(s, rs1, EXT_NONE); @@ -661,25 +624,40 @@ static bool ldst_us_trans(uint32_t vd, uint32_t rs1, uint32_t data, * As simd_desc supports at most 2048 bytes, and in this implementation, * the max vector group length is 4096 bytes. So split it into two parts. * - * The first part is vlen in bytes, encoded in maxsz of simd_desc. + * The first part is vlen in bytes (vlenb), encoded in maxsz of simd_desc. * The second part is lmul, encoded in data of simd_desc. */ - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); - tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); - tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0)); - fn(dest, mask, base, cpu_env, desc); - - tcg_temp_free_ptr(dest); - tcg_temp_free_ptr(mask); - - if (!is_store) { - mark_vs_dirty(s); + /* + * According to the specification + * + * Additionally, if the Ztso extension is implemented, then vector memory + * instructions in the V extension and Zve family of extensions follow + * RVTSO at the instruction level. The Ztso extension does not + * strengthen the ordering of intra-instruction element accesses. + * + * as a result neither ordered nor unordered accesses from the V + * instructions need ordering within the loop but we do still need barriers + * around the loop. + */ + if (is_store && s->ztso) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); } - gen_set_label(over); + mark_vs_dirty(s); + + fn(dest, mask, base, tcg_env, desc); + + if (!is_store && s->ztso) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } + + finalize_rvv_inst(s); return true; } @@ -710,6 +688,8 @@ static bool ld_us_op(DisasContext *s, arg_r2nfvm *a, uint8_t eew) data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, emul); data = FIELD_DP32(data, VDATA, NF, a->nf); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); return ldst_us_trans(a->rd, a->rs1, data, fn, s, false); } @@ -773,6 +753,9 @@ static bool ld_us_mask_op(DisasContext *s, arg_vlm_v *a, uint8_t eew) /* EMUL = 1, NFIELDS = 1 */ data = FIELD_DP32(data, VDATA, LMUL, 0); data = FIELD_DP32(data, VDATA, NF, 1); + /* Mask destination register are always tail-agnostic */ + data = FIELD_DP32(data, VDATA, VTA, s->cfg_vta_all_1s); + data = FIELD_DP32(data, VDATA, VMA, s->vma); return ldst_us_trans(a->rd, a->rs1, data, fn, s, false); } @@ -810,35 +793,27 @@ typedef void gen_helper_ldst_stride(TCGv_ptr, TCGv_ptr, TCGv, static bool ldst_stride_trans(uint32_t vd, uint32_t rs1, uint32_t rs2, uint32_t data, gen_helper_ldst_stride *fn, - DisasContext *s, bool is_store) + DisasContext *s) { TCGv_ptr dest, mask; TCGv base, stride; TCGv_i32 desc; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); - dest = tcg_temp_new_ptr(); mask = tcg_temp_new_ptr(); base = get_gpr(s, rs1, EXT_NONE); stride = get_gpr(s, rs2, EXT_NONE); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); - tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); - tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0)); - fn(dest, mask, base, stride, cpu_env, desc); + mark_vs_dirty(s); - tcg_temp_free_ptr(dest); - tcg_temp_free_ptr(mask); + fn(dest, mask, base, stride, tcg_env, desc); - if (!is_store) { - mark_vs_dirty(s); - } - - gen_set_label(over); + finalize_rvv_inst(s); return true; } @@ -860,7 +835,9 @@ static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, emul); data = FIELD_DP32(data, VDATA, NF, a->nf); - return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s, false); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); + return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s); } static bool ld_stride_check(DisasContext *s, arg_rnfvm* a, uint8_t eew) @@ -894,7 +871,7 @@ static bool st_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) return false; } - return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s, true); + return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s); } static bool st_stride_check(DisasContext *s, arg_rnfvm* a, uint8_t eew) @@ -917,37 +894,28 @@ typedef void gen_helper_ldst_index(TCGv_ptr, TCGv_ptr, TCGv, static bool ldst_index_trans(uint32_t vd, uint32_t rs1, uint32_t vs2, uint32_t data, gen_helper_ldst_index *fn, - DisasContext *s, bool is_store) + DisasContext *s) { TCGv_ptr dest, mask, index; TCGv base; TCGv_i32 desc; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); - dest = tcg_temp_new_ptr(); mask = tcg_temp_new_ptr(); index = tcg_temp_new_ptr(); base = get_gpr(s, rs1, EXT_NONE); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); - tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); - tcg_gen_addi_ptr(index, cpu_env, vreg_ofs(s, vs2)); - tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(index, tcg_env, vreg_ofs(s, vs2)); + tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0)); - fn(dest, mask, base, index, cpu_env, desc); + mark_vs_dirty(s); - tcg_temp_free_ptr(dest); - tcg_temp_free_ptr(mask); - tcg_temp_free_ptr(index); + fn(dest, mask, base, index, tcg_env, desc); - if (!is_store) { - mark_vs_dirty(s); - } - - gen_set_label(over); + finalize_rvv_inst(s); return true; } @@ -988,7 +956,9 @@ static bool ld_index_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, emul); data = FIELD_DP32(data, VDATA, NF, a->nf); - return ldst_index_trans(a->rd, a->rs1, a->rs2, data, fn, s, false); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); + return ldst_index_trans(a->rd, a->rs1, a->rs2, data, fn, s); } static bool ld_index_check(DisasContext *s, arg_rnfvm* a, uint8_t eew) @@ -1040,7 +1010,7 @@ static bool st_index_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, emul); data = FIELD_DP32(data, VDATA, NF, a->nf); - return ldst_index_trans(a->rd, a->rs1, a->rs2, data, fn, s, true); + return ldst_index_trans(a->rd, a->rs1, a->rs2, data, fn, s); } static bool st_index_check(DisasContext *s, arg_rnfvm* a, uint8_t eew) @@ -1065,24 +1035,18 @@ static bool ldff_trans(uint32_t vd, uint32_t rs1, uint32_t data, TCGv base; TCGv_i32 desc; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); - dest = tcg_temp_new_ptr(); mask = tcg_temp_new_ptr(); base = get_gpr(s, rs1, EXT_NONE); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); - tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); - tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0)); - fn(dest, mask, base, cpu_env, desc); + fn(dest, mask, base, tcg_env, desc); - tcg_temp_free_ptr(dest); - tcg_temp_free_ptr(mask); - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } @@ -1104,6 +1068,8 @@ static bool ldff_op(DisasContext *s, arg_r2nfvm *a, uint8_t eew) data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, emul); data = FIELD_DP32(data, VDATA, NF, a->nf); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); return ldff_trans(a->rd, a->rs1, data, fn, s); } @@ -1118,34 +1084,26 @@ GEN_VEXT_TRANS(vle64ff_v, MO_64, r2nfvm, ldff_op, ld_us_check) typedef void gen_helper_ldst_whole(TCGv_ptr, TCGv, TCGv_env, TCGv_i32); static bool ldst_whole_trans(uint32_t vd, uint32_t rs1, uint32_t nf, - uint32_t width, gen_helper_ldst_whole *fn, - DisasContext *s, bool is_store) + gen_helper_ldst_whole *fn, + DisasContext *s) { - uint32_t evl = (s->cfg_ptr->vlen / 8) * nf / width; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_GEU, cpu_vstart, evl, over); - TCGv_ptr dest; TCGv base; TCGv_i32 desc; uint32_t data = FIELD_DP32(0, VDATA, NF, nf); dest = tcg_temp_new_ptr(); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); base = get_gpr(s, rs1, EXT_NONE); - tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd)); - fn(dest, base, cpu_env, desc); + mark_vs_dirty(s); - tcg_temp_free_ptr(dest); - - if (!is_store) { - mark_vs_dirty(s); - } - gen_set_label(over); + fn(dest, base, tcg_env, desc); + finalize_rvv_inst(s); return true; } @@ -1153,42 +1111,42 @@ static bool ldst_whole_trans(uint32_t vd, uint32_t rs1, uint32_t nf, * load and store whole register instructions ignore vtype and vl setting. * Thus, we don't need to check vill bit. (Section 7.9) */ -#define GEN_LDST_WHOLE_TRANS(NAME, ARG_NF, WIDTH, IS_STORE) \ +#define GEN_LDST_WHOLE_TRANS(NAME, ARG_NF) \ static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \ { \ if (require_rvv(s) && \ QEMU_IS_ALIGNED(a->rd, ARG_NF)) { \ - return ldst_whole_trans(a->rd, a->rs1, ARG_NF, WIDTH, \ - gen_helper_##NAME, s, IS_STORE); \ + return ldst_whole_trans(a->rd, a->rs1, ARG_NF, \ + gen_helper_##NAME, s); \ } \ return false; \ } -GEN_LDST_WHOLE_TRANS(vl1re8_v, 1, 1, false) -GEN_LDST_WHOLE_TRANS(vl1re16_v, 1, 2, false) -GEN_LDST_WHOLE_TRANS(vl1re32_v, 1, 4, false) -GEN_LDST_WHOLE_TRANS(vl1re64_v, 1, 8, false) -GEN_LDST_WHOLE_TRANS(vl2re8_v, 2, 1, false) -GEN_LDST_WHOLE_TRANS(vl2re16_v, 2, 2, false) -GEN_LDST_WHOLE_TRANS(vl2re32_v, 2, 4, false) -GEN_LDST_WHOLE_TRANS(vl2re64_v, 2, 8, false) -GEN_LDST_WHOLE_TRANS(vl4re8_v, 4, 1, false) -GEN_LDST_WHOLE_TRANS(vl4re16_v, 4, 2, false) -GEN_LDST_WHOLE_TRANS(vl4re32_v, 4, 4, false) -GEN_LDST_WHOLE_TRANS(vl4re64_v, 4, 8, false) -GEN_LDST_WHOLE_TRANS(vl8re8_v, 8, 1, false) -GEN_LDST_WHOLE_TRANS(vl8re16_v, 8, 2, false) -GEN_LDST_WHOLE_TRANS(vl8re32_v, 8, 4, false) -GEN_LDST_WHOLE_TRANS(vl8re64_v, 8, 8, false) +GEN_LDST_WHOLE_TRANS(vl1re8_v, 1) +GEN_LDST_WHOLE_TRANS(vl1re16_v, 1) +GEN_LDST_WHOLE_TRANS(vl1re32_v, 1) +GEN_LDST_WHOLE_TRANS(vl1re64_v, 1) +GEN_LDST_WHOLE_TRANS(vl2re8_v, 2) +GEN_LDST_WHOLE_TRANS(vl2re16_v, 2) +GEN_LDST_WHOLE_TRANS(vl2re32_v, 2) +GEN_LDST_WHOLE_TRANS(vl2re64_v, 2) +GEN_LDST_WHOLE_TRANS(vl4re8_v, 4) +GEN_LDST_WHOLE_TRANS(vl4re16_v, 4) +GEN_LDST_WHOLE_TRANS(vl4re32_v, 4) +GEN_LDST_WHOLE_TRANS(vl4re64_v, 4) +GEN_LDST_WHOLE_TRANS(vl8re8_v, 8) +GEN_LDST_WHOLE_TRANS(vl8re16_v, 8) +GEN_LDST_WHOLE_TRANS(vl8re32_v, 8) +GEN_LDST_WHOLE_TRANS(vl8re64_v, 8) /* * The vector whole register store instructions are encoded similar to * unmasked unit-stride store of elements with EEW=8. */ -GEN_LDST_WHOLE_TRANS(vs1r_v, 1, 1, true) -GEN_LDST_WHOLE_TRANS(vs2r_v, 2, 1, true) -GEN_LDST_WHOLE_TRANS(vs4r_v, 4, 1, true) -GEN_LDST_WHOLE_TRANS(vs8r_v, 8, 1, true) +GEN_LDST_WHOLE_TRANS(vs1r_v, 1) +GEN_LDST_WHOLE_TRANS(vs2r_v, 2) +GEN_LDST_WHOLE_TRANS(vs4r_v, 4) +GEN_LDST_WHOLE_TRANS(vs8r_v, 8) /* *** Vector Integer Arithmetic Instructions @@ -1197,12 +1155,12 @@ GEN_LDST_WHOLE_TRANS(vs8r_v, 8, 1, true) /* * MAXSZ returns the maximum vector size can be operated in bytes, * which is used in GVEC IR when vl_eq_vlmax flag is set to true - * to accerlate vector operation. + * to accelerate vector operation. */ static inline uint32_t MAXSZ(DisasContext *s) { - int scale = s->lmul - 3; - return s->cfg_ptr->vlen >> -scale; + int max_sz = s->cfg_ptr->vlenb * 8; + return max_sz >> (3 - s->lmul); } static bool opivv_check(DisasContext *s, arg_rmrr *a) @@ -1219,14 +1177,7 @@ static inline bool do_opivv_gvec(DisasContext *s, arg_rmrr *a, GVecGen3Fn *gvec_fn, gen_helper_gvec_4_ptr *fn) { - TCGLabel *over = gen_new_label(); - if (!opivv_check(s, a)) { - return false; - } - - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); - - if (a->vm && s->vl_eq_vlmax) { + if (a->vm && s->vl_eq_vlmax && !(s->vta && s->lmul < 0)) { gvec_fn(s->sew, vreg_ofs(s, a->rd), vreg_ofs(s, a->rs2), vreg_ofs(s, a->rs1), MAXSZ(s), MAXSZ(s)); @@ -1235,13 +1186,14 @@ do_opivv_gvec(DisasContext *s, arg_rmrr *a, GVecGen3Fn *gvec_fn, data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2), - cpu_env, s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data, fn); + tcg_env, s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data, fn); } - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } @@ -1253,6 +1205,9 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ gen_helper_##NAME##_b, gen_helper_##NAME##_h, \ gen_helper_##NAME##_w, gen_helper_##NAME##_d, \ }; \ + if (!opivv_check(s, a)) { \ + return false; \ + } \ return do_opivv_gvec(s, a, tcg_gen_gvec_##SUF, fns[s->sew]); \ } @@ -1270,9 +1225,6 @@ static bool opivx_trans(uint32_t vd, uint32_t rs1, uint32_t vs2, uint32_t vm, TCGv_i32 desc; uint32_t data = 0; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); - dest = tcg_temp_new_ptr(); mask = tcg_temp_new_ptr(); src2 = tcg_temp_new_ptr(); @@ -1280,20 +1232,19 @@ static bool opivx_trans(uint32_t vd, uint32_t rs1, uint32_t vs2, uint32_t vm, data = FIELD_DP32(data, VDATA, VM, vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data)); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s); + data = FIELD_DP32(data, VDATA, VMA, s->vma); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); - tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); - tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, vs2)); - tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(src2, tcg_env, vreg_ofs(s, vs2)); + tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0)); - fn(dest, mask, src1, src2, cpu_env, desc); + fn(dest, mask, src1, src2, tcg_env, desc); - tcg_temp_free_ptr(dest); - tcg_temp_free_ptr(mask); - tcg_temp_free_ptr(src2); - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } @@ -1311,19 +1262,14 @@ static inline bool do_opivx_gvec(DisasContext *s, arg_rmrr *a, GVecGen2sFn *gvec_fn, gen_helper_opivx *fn) { - if (!opivx_check(s, a)) { - return false; - } - - if (a->vm && s->vl_eq_vlmax) { + if (a->vm && s->vl_eq_vlmax && !(s->vta && s->lmul < 0)) { TCGv_i64 src1 = tcg_temp_new_i64(); tcg_gen_ext_tl_i64(src1, get_gpr(s, a->rs1, EXT_SIGN)); gvec_fn(s->sew, vreg_ofs(s, a->rd), vreg_ofs(s, a->rs2), src1, MAXSZ(s), MAXSZ(s)); - tcg_temp_free_i64(src1); - mark_vs_dirty(s); + finalize_rvv_inst(s); return true; } return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s); @@ -1337,6 +1283,9 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ gen_helper_##NAME##_b, gen_helper_##NAME##_h, \ gen_helper_##NAME##_w, gen_helper_##NAME##_d, \ }; \ + if (!opivx_check(s, a)) { \ + return false; \ + } \ return do_opivx_gvec(s, a, tcg_gen_gvec_##SUF, fns[s->sew]); \ } @@ -1434,9 +1383,6 @@ static bool opivi_trans(uint32_t vd, uint32_t imm, uint32_t vs2, uint32_t vm, TCGv_i32 desc; uint32_t data = 0; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); - dest = tcg_temp_new_ptr(); mask = tcg_temp_new_ptr(); src2 = tcg_temp_new_ptr(); @@ -1444,20 +1390,19 @@ static bool opivi_trans(uint32_t vd, uint32_t imm, uint32_t vs2, uint32_t vm, data = FIELD_DP32(data, VDATA, VM, vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data)); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s); + data = FIELD_DP32(data, VDATA, VMA, s->vma); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); - tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); - tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, vs2)); - tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(src2, tcg_env, vreg_ofs(s, vs2)); + tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0)); - fn(dest, mask, src1, src2, cpu_env, desc); + fn(dest, mask, src1, src2, tcg_env, desc); - tcg_temp_free_ptr(dest); - tcg_temp_free_ptr(mask); - tcg_temp_free_ptr(src2); - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } @@ -1468,14 +1413,10 @@ static inline bool do_opivi_gvec(DisasContext *s, arg_rmrr *a, GVecGen2iFn *gvec_fn, gen_helper_opivx *fn, imm_mode_t imm_mode) { - if (!opivx_check(s, a)) { - return false; - } - - if (a->vm && s->vl_eq_vlmax) { + if (a->vm && s->vl_eq_vlmax && !(s->vta && s->lmul < 0)) { gvec_fn(s->sew, vreg_ofs(s, a->rd), vreg_ofs(s, a->rs2), extract_imm(s, a->rs1, imm_mode), MAXSZ(s), MAXSZ(s)); - mark_vs_dirty(s); + finalize_rvv_inst(s); return true; } return opivi_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s, imm_mode); @@ -1489,6 +1430,9 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ gen_helper_##OPIVX##_b, gen_helper_##OPIVX##_h, \ gen_helper_##OPIVX##_w, gen_helper_##OPIVX##_d, \ }; \ + if (!opivx_check(s, a)) { \ + return false; \ + } \ return do_opivi_gvec(s, a, tcg_gen_gvec_##SUF, \ fns[s->sew], IMM_MODE); \ } @@ -1520,19 +1464,18 @@ static bool do_opivv_widen(DisasContext *s, arg_rmrr *a, { if (checkfn(s, a)) { uint32_t data = 0; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2), - cpu_env, s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, + tcg_env, s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data, fn); - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } return false; @@ -1562,30 +1505,24 @@ static bool opivx_widen_check(DisasContext *s, arg_rmrr *a) vext_check_ds(s, a->rd, a->rs2, a->vm); } -static bool do_opivx_widen(DisasContext *s, arg_rmrr *a, - gen_helper_opivx *fn) -{ - if (opivx_widen_check(s, a)) { - return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s); - } - return false; +#define GEN_OPIVX_WIDEN_TRANS(NAME, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (CHECK(s, a)) { \ + static gen_helper_opivx * const fns[3] = { \ + gen_helper_##NAME##_b, \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w \ + }; \ + return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fns[s->sew], s); \ + } \ + return false; \ } -#define GEN_OPIVX_WIDEN_TRANS(NAME) \ -static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ -{ \ - static gen_helper_opivx * const fns[3] = { \ - gen_helper_##NAME##_b, \ - gen_helper_##NAME##_h, \ - gen_helper_##NAME##_w \ - }; \ - return do_opivx_widen(s, a, fns[s->sew]); \ -} - -GEN_OPIVX_WIDEN_TRANS(vwaddu_vx) -GEN_OPIVX_WIDEN_TRANS(vwadd_vx) -GEN_OPIVX_WIDEN_TRANS(vwsubu_vx) -GEN_OPIVX_WIDEN_TRANS(vwsub_vx) +GEN_OPIVX_WIDEN_TRANS(vwaddu_vx, opivx_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwadd_vx, opivx_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwsubu_vx, opivx_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwsub_vx, opivx_widen_check) /* WIDEN OPIVV with WIDEN */ static bool opiwv_widen_check(DisasContext *s, arg_rmrr *a) @@ -1600,18 +1537,17 @@ static bool do_opiwv_widen(DisasContext *s, arg_rmrr *a, { if (opiwv_widen_check(s, a)) { uint32_t data = 0; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2), - cpu_env, s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data, fn); - mark_vs_dirty(s); - gen_set_label(over); + tcg_env, s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data, fn); + finalize_rvv_inst(s); return true; } return false; @@ -1666,33 +1602,36 @@ GEN_OPIWX_WIDEN_TRANS(vwadd_wx) GEN_OPIWX_WIDEN_TRANS(vwsubu_wx) GEN_OPIWX_WIDEN_TRANS(vwsub_wx) +static bool opivv_trans(uint32_t vd, uint32_t vs1, uint32_t vs2, uint32_t vm, + gen_helper_gvec_4_ptr *fn, DisasContext *s) +{ + uint32_t data = 0; + + data = FIELD_DP32(data, VDATA, VM, vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s); + data = FIELD_DP32(data, VDATA, VMA, s->vma); + tcg_gen_gvec_4_ptr(vreg_ofs(s, vd), vreg_ofs(s, 0), vreg_ofs(s, vs1), + vreg_ofs(s, vs2), tcg_env, s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data, fn); + finalize_rvv_inst(s); + return true; +} + /* Vector Integer Add-with-Carry / Subtract-with-Borrow Instructions */ /* OPIVV without GVEC IR */ -#define GEN_OPIVV_TRANS(NAME, CHECK) \ -static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ -{ \ - if (CHECK(s, a)) { \ - uint32_t data = 0; \ - static gen_helper_gvec_4_ptr * const fns[4] = { \ - gen_helper_##NAME##_b, gen_helper_##NAME##_h, \ - gen_helper_##NAME##_w, gen_helper_##NAME##_d, \ - }; \ - TCGLabel *over = gen_new_label(); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ - \ - data = FIELD_DP32(data, VDATA, VM, a->vm); \ - data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ - tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ - vreg_ofs(s, a->rs1), \ - vreg_ofs(s, a->rs2), cpu_env, \ - s->cfg_ptr->vlen / 8, \ - s->cfg_ptr->vlen / 8, data, \ - fns[s->sew]); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ - return true; \ - } \ - return false; \ +#define GEN_OPIVV_TRANS(NAME, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (CHECK(s, a)) { \ + static gen_helper_gvec_4_ptr * const fns[4] = { \ + gen_helper_##NAME##_b, gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, gen_helper_##NAME##_d, \ + }; \ + return opivv_trans(a->rd, a->rs1, a->rs2, a->vm, fns[s->sew], s);\ + } \ + return false; \ } /* @@ -1801,11 +1740,7 @@ static inline bool do_opivx_gvec_shift(DisasContext *s, arg_rmrr *a, GVecGen2sFn32 *gvec_fn, gen_helper_opivx *fn) { - if (!opivx_check(s, a)) { - return false; - } - - if (a->vm && s->vl_eq_vlmax) { + if (a->vm && s->vl_eq_vlmax && !(s->vta && s->lmul < 0)) { TCGv_i32 src1 = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(src1, get_gpr(s, a->rs1, EXT_NONE)); @@ -1813,8 +1748,7 @@ do_opivx_gvec_shift(DisasContext *s, arg_rmrr *a, GVecGen2sFn32 *gvec_fn, gvec_fn(s->sew, vreg_ofs(s, a->rd), vreg_ofs(s, a->rs2), src1, MAXSZ(s), MAXSZ(s)); - tcg_temp_free_i32(src1); - mark_vs_dirty(s); + finalize_rvv_inst(s); return true; } return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s); @@ -1827,7 +1761,9 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ gen_helper_##NAME##_b, gen_helper_##NAME##_h, \ gen_helper_##NAME##_w, gen_helper_##NAME##_d, \ }; \ - \ + if (!opivx_check(s, a)) { \ + return false; \ + } \ return do_opivx_gvec_shift(s, a, tcg_gen_gvec_##SUF, fns[s->sew]); \ } @@ -1858,19 +1794,18 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ gen_helper_##NAME##_h, \ gen_helper_##NAME##_w, \ }; \ - TCGLabel *over = gen_new_label(); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ - vreg_ofs(s, a->rs2), cpu_env, \ - s->cfg_ptr->vlen / 8, \ - s->cfg_ptr->vlen / 8, data, \ + vreg_ofs(s, a->rs2), tcg_env, \ + s->cfg_ptr->vlenb, \ + s->cfg_ptr->vlenb, data, \ fns[s->sew]); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + finalize_rvv_inst(s); \ return true; \ } \ return false; \ @@ -1987,8 +1922,7 @@ static bool vmulh_vv_check(DisasContext *s, arg_rmrr *a) * are not included for EEW=64 in Zve64*. (Section 18.2) */ return opivv_check(s, a) && - (!has_ext(s, RVV) && - s->cfg_ptr->ext_zve64f ? s->sew != MO_64 : true); + (!has_ext(s, RVV) ? s->sew != MO_64 : true); } static bool vmulh_vx_check(DisasContext *s, arg_rmrr *a) @@ -2001,8 +1935,7 @@ static bool vmulh_vx_check(DisasContext *s, arg_rmrr *a) * are not included for EEW=64 in Zve64*. (Section 18.2) */ return opivx_check(s, a) && - (!has_ext(s, RVV) && - s->cfg_ptr->ext_zve64f ? s->sew != MO_64 : true); + (!has_ext(s, RVV) ? s->sew != MO_64 : true); } GEN_OPIVV_GVEC_TRANS(vmul_vv, mul) @@ -2028,9 +1961,9 @@ GEN_OPIVX_TRANS(vrem_vx, opivx_check) GEN_OPIVV_WIDEN_TRANS(vwmul_vv, opivv_widen_check) GEN_OPIVV_WIDEN_TRANS(vwmulu_vv, opivv_widen_check) GEN_OPIVV_WIDEN_TRANS(vwmulsu_vv, opivv_widen_check) -GEN_OPIVX_WIDEN_TRANS(vwmul_vx) -GEN_OPIVX_WIDEN_TRANS(vwmulu_vx) -GEN_OPIVX_WIDEN_TRANS(vwmulsu_vx) +GEN_OPIVX_WIDEN_TRANS(vwmul_vx, opivx_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmulu_vx, opivx_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmulsu_vx, opivx_widen_check) /* Vector Single-Width Integer Multiply-Add Instructions */ GEN_OPIVV_TRANS(vmacc_vv, opivv_check) @@ -2046,10 +1979,10 @@ GEN_OPIVX_TRANS(vnmsub_vx, opivx_check) GEN_OPIVV_WIDEN_TRANS(vwmaccu_vv, opivv_widen_check) GEN_OPIVV_WIDEN_TRANS(vwmacc_vv, opivv_widen_check) GEN_OPIVV_WIDEN_TRANS(vwmaccsu_vv, opivv_widen_check) -GEN_OPIVX_WIDEN_TRANS(vwmaccu_vx) -GEN_OPIVX_WIDEN_TRANS(vwmacc_vx) -GEN_OPIVX_WIDEN_TRANS(vwmaccsu_vx) -GEN_OPIVX_WIDEN_TRANS(vwmaccus_vx) +GEN_OPIVX_WIDEN_TRANS(vwmaccu_vx, opivx_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmacc_vx, opivx_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmaccsu_vx, opivx_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmaccus_vx, opivx_widen_check) /* Vector Integer Merge and Move Instructions */ static bool trans_vmv_v_v(DisasContext *s, arg_vmv_v_v *a) @@ -2058,26 +1991,24 @@ static bool trans_vmv_v_v(DisasContext *s, arg_vmv_v_v *a) vext_check_isa_ill(s) && /* vmv.v.v has rs2 = 0 and vm = 1 */ vext_check_sss(s, a->rd, a->rs1, 0, 1)) { - if (s->vl_eq_vlmax) { + if (s->vl_eq_vlmax && !(s->vta && s->lmul < 0)) { tcg_gen_gvec_mov(s->sew, vreg_ofs(s, a->rd), vreg_ofs(s, a->rs1), MAXSZ(s), MAXSZ(s)); } else { uint32_t data = FIELD_DP32(0, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); static gen_helper_gvec_2_ptr * const fns[4] = { gen_helper_vmv_v_v_b, gen_helper_vmv_v_v_h, gen_helper_vmv_v_v_w, gen_helper_vmv_v_v_d, }; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); tcg_gen_gvec_2_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, a->rs1), - cpu_env, s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data, + tcg_env, s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data, fns[s->sew]); - gen_set_label(over); } - mark_vs_dirty(s); + finalize_rvv_inst(s); return true; } return false; @@ -2091,36 +2022,38 @@ static bool trans_vmv_v_x(DisasContext *s, arg_vmv_v_x *a) /* vmv.v.x has rs2 = 0 and vm = 1 */ vext_check_ss(s, a->rd, 0, 1)) { TCGv s1; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); s1 = get_gpr(s, a->rs1, EXT_SIGN); - if (s->vl_eq_vlmax) { - tcg_gen_gvec_dup_tl(s->sew, vreg_ofs(s, a->rd), - MAXSZ(s), MAXSZ(s), s1); + if (s->vl_eq_vlmax && !(s->vta && s->lmul < 0)) { + if (get_xl(s) == MXL_RV32 && s->sew == MO_64) { + TCGv_i64 s1_i64 = tcg_temp_new_i64(); + tcg_gen_ext_tl_i64(s1_i64, s1); + tcg_gen_gvec_dup_i64(s->sew, vreg_ofs(s, a->rd), + MAXSZ(s), MAXSZ(s), s1_i64); + } else { + tcg_gen_gvec_dup_tl(s->sew, vreg_ofs(s, a->rd), + MAXSZ(s), MAXSZ(s), s1); + } } else { TCGv_i32 desc; TCGv_i64 s1_i64 = tcg_temp_new_i64(); TCGv_ptr dest = tcg_temp_new_ptr(); uint32_t data = FIELD_DP32(0, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); static gen_helper_vmv_vx * const fns[4] = { gen_helper_vmv_v_x_b, gen_helper_vmv_v_x_h, gen_helper_vmv_v_x_w, gen_helper_vmv_v_x_d, }; tcg_gen_ext_tl_i64(s1_i64, s1); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data)); - tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, a->rd)); - fns[s->sew](dest, s1_i64, cpu_env, desc); - - tcg_temp_free_ptr(dest); - tcg_temp_free_i64(s1_i64); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, a->rd)); + fns[s->sew](dest, s1_i64, tcg_env, desc); } - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } return false; @@ -2133,33 +2066,28 @@ static bool trans_vmv_v_i(DisasContext *s, arg_vmv_v_i *a) /* vmv.v.i has rs2 = 0 and vm = 1 */ vext_check_ss(s, a->rd, 0, 1)) { int64_t simm = sextract64(a->rs1, 0, 5); - if (s->vl_eq_vlmax) { + if (s->vl_eq_vlmax && !(s->vta && s->lmul < 0)) { tcg_gen_gvec_dup_imm(s->sew, vreg_ofs(s, a->rd), MAXSZ(s), MAXSZ(s), simm); - mark_vs_dirty(s); } else { TCGv_i32 desc; TCGv_i64 s1; TCGv_ptr dest; uint32_t data = FIELD_DP32(0, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); static gen_helper_vmv_vx * const fns[4] = { gen_helper_vmv_v_x_b, gen_helper_vmv_v_x_h, gen_helper_vmv_v_x_w, gen_helper_vmv_v_x_d, }; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); s1 = tcg_constant_i64(simm); dest = tcg_temp_new_ptr(); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data)); - tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, a->rd)); - fns[s->sew](dest, s1, cpu_env, desc); - - tcg_temp_free_ptr(dest); - mark_vs_dirty(s); - gen_set_label(over); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, a->rd)); + fns[s->sew](dest, s1, tcg_env, desc); } + finalize_rvv_inst(s); return true; } return false; @@ -2205,8 +2133,7 @@ static bool vsmul_vv_check(DisasContext *s, arg_rmrr *a) * for EEW=64 in Zve64*. (Section 18.2) */ return opivv_check(s, a) && - (!has_ext(s, RVV) && - s->cfg_ptr->ext_zve64f ? s->sew != MO_64 : true); + (!has_ext(s, RVV) ? s->sew != MO_64 : true); } static bool vsmul_vx_check(DisasContext *s, arg_rmrr *a) @@ -2217,8 +2144,7 @@ static bool vsmul_vx_check(DisasContext *s, arg_rmrr *a) * for EEW=64 in Zve64*. (Section 18.2) */ return opivx_check(s, a) && - (!has_ext(s, RVV) && - s->cfg_ptr->ext_zve64f ? s->sew != MO_64 : true); + (!has_ext(s, RVV) ? s->sew != MO_64 : true); } GEN_OPIVV_TRANS(vsmul_vv, vsmul_vv_check) @@ -2251,7 +2177,7 @@ GEN_OPIWI_NARROW_TRANS(vnclip_wi, IMM_ZX, vnclip_wx) * * If SEW < FLEN, check whether input fp register is a valid * NaN-boxed value, in which case the least-significant SEW bits - * of the f regsiter are used, else the canonical NaN value is used. + * of the f register are used, else the canonical NaN value is used. */ static void do_nanbox(DisasContext *s, TCGv_i64 out, TCGv_i64 in) { @@ -2281,9 +2207,7 @@ static bool opfvv_check(DisasContext *s, arg_rmrr *a) return require_rvv(s) && require_rvf(s) && vext_check_isa_ill(s) && - vext_check_sss(s, a->rd, a->rs1, a->rs2, a->vm) && - require_zve32f(s) && - require_zve64f(s); + vext_check_sss(s, a->rd, a->rs1, a->rs2, a->vm); } /* OPFVV without GVEC IR */ @@ -2297,20 +2221,21 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ gen_helper_##NAME##_w, \ gen_helper_##NAME##_d, \ }; \ - TCGLabel *over = gen_new_label(); \ gen_set_rm(s, RISCV_FRM_DYN); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = \ + FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s);\ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ - vreg_ofs(s, a->rs2), cpu_env, \ - s->cfg_ptr->vlen / 8, \ - s->cfg_ptr->vlen / 8, data, \ + vreg_ofs(s, a->rs2), tcg_env, \ + s->cfg_ptr->vlenb, \ + s->cfg_ptr->vlenb, data, \ fns[s->sew - 1]); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + finalize_rvv_inst(s); \ return true; \ } \ return false; \ @@ -2328,31 +2253,23 @@ static bool opfvf_trans(uint32_t vd, uint32_t rs1, uint32_t vs2, TCGv_i32 desc; TCGv_i64 t1; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); - dest = tcg_temp_new_ptr(); mask = tcg_temp_new_ptr(); src2 = tcg_temp_new_ptr(); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); - tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); - tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, vs2)); - tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(src2, tcg_env, vreg_ofs(s, vs2)); + tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0)); /* NaN-box f[rs1] */ t1 = tcg_temp_new_i64(); do_nanbox(s, t1, cpu_fpr[rs1]); - fn(dest, mask, t1, src2, cpu_env, desc); + fn(dest, mask, t1, src2, tcg_env, desc); - tcg_temp_free_ptr(dest); - tcg_temp_free_ptr(mask); - tcg_temp_free_ptr(src2); - tcg_temp_free_i64(t1); - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } @@ -2365,9 +2282,7 @@ static bool opfvf_check(DisasContext *s, arg_rmrr *a) return require_rvv(s) && require_rvf(s) && vext_check_isa_ill(s) && - vext_check_ss(s, a->rd, a->rs2, a->vm) && - require_zve32f(s) && - require_zve64f(s); + vext_check_ss(s, a->rd, a->rs2, a->vm); } /* OPFVF without GVEC IR */ @@ -2384,6 +2299,10 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ gen_set_rm(s, RISCV_FRM_DYN); \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VTA_ALL_1S, \ + s->cfg_vta_all_1s); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ return opfvf_trans(a->rd, a->rs1, a->rs2, data, \ fns[s->sew - 1], s); \ } \ @@ -2401,9 +2320,7 @@ static bool opfvv_widen_check(DisasContext *s, arg_rmrr *a) require_scale_rvf(s) && (s->sew != MO_8) && vext_check_isa_ill(s) && - vext_check_dss(s, a->rd, a->rs1, a->rs2, a->vm) && - require_scale_zve32f(s) && - require_scale_zve64f(s); + vext_check_dss(s, a->rd, a->rs1, a->rs2, a->vm); } /* OPFVV with WIDEN */ @@ -2415,20 +2332,19 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ static gen_helper_gvec_4_ptr * const fns[2] = { \ gen_helper_##NAME##_h, gen_helper_##NAME##_w, \ }; \ - TCGLabel *over = gen_new_label(); \ gen_set_rm(s, RISCV_FRM_DYN); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ - vreg_ofs(s, a->rs2), cpu_env, \ - s->cfg_ptr->vlen / 8, \ - s->cfg_ptr->vlen / 8, data, \ + vreg_ofs(s, a->rs2), tcg_env, \ + s->cfg_ptr->vlenb, \ + s->cfg_ptr->vlenb, data, \ fns[s->sew - 1]); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + finalize_rvv_inst(s); \ return true; \ } \ return false; \ @@ -2443,9 +2359,7 @@ static bool opfvf_widen_check(DisasContext *s, arg_rmrr *a) require_scale_rvf(s) && (s->sew != MO_8) && vext_check_isa_ill(s) && - vext_check_ds(s, a->rd, a->rs2, a->vm) && - require_scale_zve32f(s) && - require_scale_zve64f(s); + vext_check_ds(s, a->rd, a->rs2, a->vm); } /* OPFVF with WIDEN */ @@ -2460,6 +2374,8 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ gen_set_rm(s, RISCV_FRM_DYN); \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ return opfvf_trans(a->rd, a->rs1, a->rs2, data, \ fns[s->sew - 1], s); \ } \ @@ -2475,9 +2391,7 @@ static bool opfwv_widen_check(DisasContext *s, arg_rmrr *a) require_scale_rvf(s) && (s->sew != MO_8) && vext_check_isa_ill(s) && - vext_check_dds(s, a->rd, a->rs1, a->rs2, a->vm) && - require_scale_zve32f(s) && - require_scale_zve64f(s); + vext_check_dds(s, a->rd, a->rs1, a->rs2, a->vm); } /* WIDEN OPFVV with WIDEN */ @@ -2489,20 +2403,19 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ static gen_helper_gvec_4_ptr * const fns[2] = { \ gen_helper_##NAME##_h, gen_helper_##NAME##_w, \ }; \ - TCGLabel *over = gen_new_label(); \ gen_set_rm(s, RISCV_FRM_DYN); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ - vreg_ofs(s, a->rs2), cpu_env, \ - s->cfg_ptr->vlen / 8, \ - s->cfg_ptr->vlen / 8, data, \ + vreg_ofs(s, a->rs2), tcg_env, \ + s->cfg_ptr->vlenb, \ + s->cfg_ptr->vlenb, data, \ fns[s->sew - 1]); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + finalize_rvv_inst(s); \ return true; \ } \ return false; \ @@ -2517,9 +2430,7 @@ static bool opfwf_widen_check(DisasContext *s, arg_rmrr *a) require_scale_rvf(s) && (s->sew != MO_8) && vext_check_isa_ill(s) && - vext_check_dd(s, a->rd, a->rs2, a->vm) && - require_scale_zve32f(s) && - require_scale_zve64f(s); + vext_check_dd(s, a->rd, a->rs2, a->vm); } /* WIDEN OPFVF with WIDEN */ @@ -2534,6 +2445,8 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ gen_set_rm(s, RISCV_FRM_DYN); \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ return opfvf_trans(a->rd, a->rs1, a->rs2, data, \ fns[s->sew - 1], s); \ } \ @@ -2594,9 +2507,7 @@ static bool opfv_check(DisasContext *s, arg_rmr *a) require_rvf(s) && vext_check_isa_ill(s) && /* OPFV instructions ignore vs1 check */ - vext_check_ss(s, a->rd, a->rs2, a->vm) && - require_zve32f(s) && - require_zve64f(s); + vext_check_ss(s, a->rd, a->rs2, a->vm); } static bool do_opfv(DisasContext *s, arg_rmr *a, @@ -2605,23 +2516,18 @@ static bool do_opfv(DisasContext *s, arg_rmr *a, int rm) { if (checkfn(s, a)) { - if (rm != RISCV_FRM_DYN) { - gen_set_rm(s, RISCV_FRM_DYN); - } - uint32_t data = 0; - TCGLabel *over = gen_new_label(); - gen_set_rm(s, rm); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + gen_set_rm_chkfrm(s, rm); data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), - vreg_ofs(s, a->rs2), cpu_env, - s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data, fn); - mark_vs_dirty(s); - gen_set_label(over); + vreg_ofs(s, a->rs2), tcg_env, + s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data, fn); + finalize_rvv_inst(s); return true; } return false; @@ -2662,9 +2568,7 @@ static bool opfvv_cmp_check(DisasContext *s, arg_rmrr *a) return require_rvv(s) && require_rvf(s) && vext_check_isa_ill(s) && - vext_check_mss(s, a->rd, a->rs1, a->rs2) && - require_zve32f(s) && - require_zve64f(s); + vext_check_mss(s, a->rd, a->rs1, a->rs2); } GEN_OPFVV_TRANS(vmfeq_vv, opfvv_cmp_check) @@ -2677,9 +2581,7 @@ static bool opfvf_cmp_check(DisasContext *s, arg_rmrr *a) return require_rvv(s) && require_rvf(s) && vext_check_isa_ill(s) && - vext_check_ms(s, a->rd, a->rs2) && - require_zve32f(s) && - require_zve64f(s); + vext_check_ms(s, a->rd, a->rs2); } GEN_OPFVF_TRANS(vmfeq_vf, opfvf_cmp_check) @@ -2700,49 +2602,42 @@ static bool trans_vfmv_v_f(DisasContext *s, arg_vfmv_v_f *a) if (require_rvv(s) && require_rvf(s) && vext_check_isa_ill(s) && - require_align(a->rd, s->lmul) && - require_zve32f(s) && - require_zve64f(s)) { + require_align(a->rd, s->lmul)) { gen_set_rm(s, RISCV_FRM_DYN); TCGv_i64 t1; - if (s->vl_eq_vlmax) { + if (s->vl_eq_vlmax && !(s->vta && s->lmul < 0)) { t1 = tcg_temp_new_i64(); /* NaN-box f[rs1] */ do_nanbox(s, t1, cpu_fpr[a->rs1]); tcg_gen_gvec_dup_i64(s->sew, vreg_ofs(s, a->rd), MAXSZ(s), MAXSZ(s), t1); - mark_vs_dirty(s); } else { TCGv_ptr dest; TCGv_i32 desc; uint32_t data = FIELD_DP32(0, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); static gen_helper_vmv_vx * const fns[3] = { gen_helper_vmv_v_x_h, gen_helper_vmv_v_x_w, gen_helper_vmv_v_x_d, }; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); t1 = tcg_temp_new_i64(); /* NaN-box f[rs1] */ do_nanbox(s, t1, cpu_fpr[a->rs1]); dest = tcg_temp_new_ptr(); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data)); - tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, a->rd)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, a->rd)); - fns[s->sew - 1](dest, t1, cpu_env, desc); - - tcg_temp_free_ptr(dest); - mark_vs_dirty(s); - gen_set_label(over); + fns[s->sew - 1](dest, t1, tcg_env, desc); } - tcg_temp_free_i64(t1); + finalize_rvv_inst(s); return true; } return false; @@ -2784,46 +2679,37 @@ static bool opfv_widen_check(DisasContext *s, arg_rmr *a) static bool opxfv_widen_check(DisasContext *s, arg_rmr *a) { return opfv_widen_check(s, a) && - require_rvf(s) && - require_zve32f(s) && - require_zve64f(s); + require_rvf(s); } static bool opffv_widen_check(DisasContext *s, arg_rmr *a) { return opfv_widen_check(s, a) && - require_scale_rvf(s) && - (s->sew != MO_8) && - require_scale_zve32f(s) && - require_scale_zve64f(s); + require_scale_rvfmin(s) && + (s->sew != MO_8); } #define GEN_OPFV_WIDEN_TRANS(NAME, CHECK, HELPER, FRM) \ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ { \ if (CHECK(s, a)) { \ - if (FRM != RISCV_FRM_DYN) { \ - gen_set_rm(s, RISCV_FRM_DYN); \ - } \ - \ uint32_t data = 0; \ static gen_helper_gvec_3_ptr * const fns[2] = { \ gen_helper_##HELPER##_h, \ gen_helper_##HELPER##_w, \ }; \ - TCGLabel *over = gen_new_label(); \ - gen_set_rm(s, FRM); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ + gen_set_rm_chkfrm(s, FRM); \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ - vreg_ofs(s, a->rs2), cpu_env, \ - s->cfg_ptr->vlen / 8, \ - s->cfg_ptr->vlen / 8, data, \ + vreg_ofs(s, a->rs2), tcg_env, \ + s->cfg_ptr->vlenb, \ + s->cfg_ptr->vlenb, data, \ fns[s->sew - 1]); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + finalize_rvv_inst(s); \ return true; \ } \ return false; \ @@ -2847,9 +2733,7 @@ static bool opfxv_widen_check(DisasContext *s, arg_rmr *a) require_scale_rvf(s) && vext_check_isa_ill(s) && /* OPFV widening instructions ignore vs1 check */ - vext_check_ds(s, a->rd, a->rs2, a->vm) && - require_scale_zve32f(s) && - require_scale_zve64f(s); + vext_check_ds(s, a->rd, a->rs2, a->vm); } #define GEN_OPFXV_WIDEN_TRANS(NAME) \ @@ -2862,18 +2746,18 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ gen_helper_##NAME##_h, \ gen_helper_##NAME##_w, \ }; \ - TCGLabel *over = gen_new_label(); \ gen_set_rm(s, RISCV_FRM_DYN); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ - vreg_ofs(s, a->rs2), cpu_env, \ - s->cfg_ptr->vlen / 8, \ - s->cfg_ptr->vlen / 8, data, \ + vreg_ofs(s, a->rs2), tcg_env, \ + s->cfg_ptr->vlenb, \ + s->cfg_ptr->vlenb, data, \ fns[s->sew]); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + finalize_rvv_inst(s); \ return true; \ } \ return false; \ @@ -2900,46 +2784,44 @@ static bool opfxv_narrow_check(DisasContext *s, arg_rmr *a) { return opfv_narrow_check(s, a) && require_rvf(s) && - (s->sew != MO_64) && - require_zve32f(s) && - require_zve64f(s); + (s->sew != MO_64); } static bool opffv_narrow_check(DisasContext *s, arg_rmr *a) +{ + return opfv_narrow_check(s, a) && + require_scale_rvfmin(s) && + (s->sew != MO_8); +} + +static bool opffv_rod_narrow_check(DisasContext *s, arg_rmr *a) { return opfv_narrow_check(s, a) && require_scale_rvf(s) && - (s->sew != MO_8) && - require_scale_zve32f(s) && - require_scale_zve64f(s); + (s->sew != MO_8); } #define GEN_OPFV_NARROW_TRANS(NAME, CHECK, HELPER, FRM) \ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ { \ if (CHECK(s, a)) { \ - if (FRM != RISCV_FRM_DYN) { \ - gen_set_rm(s, RISCV_FRM_DYN); \ - } \ - \ uint32_t data = 0; \ static gen_helper_gvec_3_ptr * const fns[2] = { \ gen_helper_##HELPER##_h, \ gen_helper_##HELPER##_w, \ }; \ - TCGLabel *over = gen_new_label(); \ - gen_set_rm(s, FRM); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ + gen_set_rm_chkfrm(s, FRM); \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ - vreg_ofs(s, a->rs2), cpu_env, \ - s->cfg_ptr->vlen / 8, \ - s->cfg_ptr->vlen / 8, data, \ + vreg_ofs(s, a->rs2), tcg_env, \ + s->cfg_ptr->vlenb, \ + s->cfg_ptr->vlenb, data, \ fns[s->sew - 1]); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + finalize_rvv_inst(s); \ return true; \ } \ return false; \ @@ -2952,7 +2834,7 @@ GEN_OPFV_NARROW_TRANS(vfncvt_f_x_w, opfxv_narrow_check, vfncvt_f_x_w, GEN_OPFV_NARROW_TRANS(vfncvt_f_f_w, opffv_narrow_check, vfncvt_f_f_w, RISCV_FRM_DYN) /* Reuse the helper function from vfncvt.f.f.w */ -GEN_OPFV_NARROW_TRANS(vfncvt_rod_f_f_w, opffv_narrow_check, vfncvt_f_f_w, +GEN_OPFV_NARROW_TRANS(vfncvt_rod_f_f_w, opffv_rod_narrow_check, vfncvt_f_f_w, RISCV_FRM_ROD) static bool opxfv_narrow_check(DisasContext *s, arg_rmr *a) @@ -2961,37 +2843,31 @@ static bool opxfv_narrow_check(DisasContext *s, arg_rmr *a) require_scale_rvf(s) && vext_check_isa_ill(s) && /* OPFV narrowing instructions ignore vs1 check */ - vext_check_sd(s, a->rd, a->rs2, a->vm) && - require_scale_zve32f(s) && - require_scale_zve64f(s); + vext_check_sd(s, a->rd, a->rs2, a->vm); } #define GEN_OPXFV_NARROW_TRANS(NAME, HELPER, FRM) \ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ { \ if (opxfv_narrow_check(s, a)) { \ - if (FRM != RISCV_FRM_DYN) { \ - gen_set_rm(s, RISCV_FRM_DYN); \ - } \ - \ uint32_t data = 0; \ static gen_helper_gvec_3_ptr * const fns[3] = { \ gen_helper_##HELPER##_b, \ gen_helper_##HELPER##_h, \ gen_helper_##HELPER##_w, \ }; \ - TCGLabel *over = gen_new_label(); \ - gen_set_rm(s, FRM); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ + gen_set_rm_chkfrm(s, FRM); \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ - vreg_ofs(s, a->rs2), cpu_env, \ - s->cfg_ptr->vlen / 8, \ - s->cfg_ptr->vlen / 8, data, \ + vreg_ofs(s, a->rs2), tcg_env, \ + s->cfg_ptr->vlenb, \ + s->cfg_ptr->vlenb, data, \ fns[s->sew]); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + finalize_rvv_inst(s); \ return true; \ } \ return false; \ @@ -3037,12 +2913,11 @@ GEN_OPIVV_WIDEN_TRANS(vwredsumu_vs, reduction_widen_check) static bool freduction_check(DisasContext *s, arg_rmrr *a) { return reduction_check(s, a) && - require_rvf(s) && - require_zve32f(s) && - require_zve64f(s); + require_rvf(s); } -GEN_OPFVV_TRANS(vfredsum_vs, freduction_check) +GEN_OPFVV_TRANS(vfredusum_vs, freduction_check) +GEN_OPFVV_TRANS(vfredosum_vs, freduction_check) GEN_OPFVV_TRANS(vfredmax_vs, freduction_check) GEN_OPFVV_TRANS(vfredmin_vs, freduction_check) @@ -3054,7 +2929,8 @@ static bool freduction_widen_check(DisasContext *s, arg_rmrr *a) (s->sew != MO_8); } -GEN_OPFVV_WIDEN_TRANS(vfwredsum_vs, freduction_widen_check) +GEN_OPFVV_WIDEN_TRANS(vfwredusum_vs, freduction_widen_check) +GEN_OPFVV_WIDEN_TRANS(vfwredosum_vs, freduction_widen_check) /* *** Vector Mask Operations @@ -3068,17 +2944,16 @@ static bool trans_##NAME(DisasContext *s, arg_r *a) \ vext_check_isa_ill(s)) { \ uint32_t data = 0; \ gen_helper_gvec_4_ptr *fn = gen_helper_##NAME; \ - TCGLabel *over = gen_new_label(); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = \ + FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s);\ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ - vreg_ofs(s, a->rs2), cpu_env, \ - s->cfg_ptr->vlen / 8, \ - s->cfg_ptr->vlen / 8, data, fn); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + vreg_ofs(s, a->rs2), tcg_env, \ + s->cfg_ptr->vlenb, \ + s->cfg_ptr->vlenb, data, fn); \ + finalize_rvv_inst(s); \ return true; \ } \ return false; \ @@ -3098,7 +2973,7 @@ static bool trans_vcpop_m(DisasContext *s, arg_rmr *a) { if (require_rvv(s) && vext_check_isa_ill(s) && - s->vstart == 0) { + s->vstart_eq_zero) { TCGv_ptr src2, mask; TCGv dst; TCGv_i32 desc; @@ -3109,18 +2984,14 @@ static bool trans_vcpop_m(DisasContext *s, arg_rmr *a) mask = tcg_temp_new_ptr(); src2 = tcg_temp_new_ptr(); dst = dest_gpr(s, a->rd); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); - tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, a->rs2)); - tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + tcg_gen_addi_ptr(src2, tcg_env, vreg_ofs(s, a->rs2)); + tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0)); - gen_helper_vcpop_m(dst, mask, src2, cpu_env, desc); + gen_helper_vcpop_m(dst, mask, src2, tcg_env, desc); gen_set_gpr(s, a->rd, dst); - - tcg_temp_free_ptr(mask); - tcg_temp_free_ptr(src2); - return true; } return false; @@ -3131,7 +3002,7 @@ static bool trans_vfirst_m(DisasContext *s, arg_rmr *a) { if (require_rvv(s) && vext_check_isa_ill(s) && - s->vstart == 0) { + s->vstart_eq_zero) { TCGv_ptr src2, mask; TCGv dst; TCGv_i32 desc; @@ -3142,25 +3013,24 @@ static bool trans_vfirst_m(DisasContext *s, arg_rmr *a) mask = tcg_temp_new_ptr(); src2 = tcg_temp_new_ptr(); dst = dest_gpr(s, a->rd); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); - tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, a->rs2)); - tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + tcg_gen_addi_ptr(src2, tcg_env, vreg_ofs(s, a->rs2)); + tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0)); - gen_helper_vfirst_m(dst, mask, src2, cpu_env, desc); + gen_helper_vfirst_m(dst, mask, src2, tcg_env, desc); gen_set_gpr(s, a->rd, dst); - - tcg_temp_free_ptr(mask); - tcg_temp_free_ptr(src2); return true; } return false; } -/* vmsbf.m set-before-first mask bit */ -/* vmsif.m set-includ-first mask bit */ -/* vmsof.m set-only-first mask bit */ +/* + * vmsbf.m set-before-first mask bit + * vmsif.m set-including-first mask bit + * vmsof.m set-only-first mask bit + */ #define GEN_M_TRANS(NAME) \ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ { \ @@ -3168,21 +3038,21 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ vext_check_isa_ill(s) && \ require_vm(a->vm, a->rd) && \ (a->rd != a->rs2) && \ - (s->vstart == 0)) { \ + s->vstart_eq_zero) { \ uint32_t data = 0; \ gen_helper_gvec_3_ptr *fn = gen_helper_##NAME; \ - TCGLabel *over = gen_new_label(); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = \ + FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s);\ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), \ vreg_ofs(s, 0), vreg_ofs(s, a->rs2), \ - cpu_env, s->cfg_ptr->vlen / 8, \ - s->cfg_ptr->vlen / 8, \ + tcg_env, s->cfg_ptr->vlenb, \ + s->cfg_ptr->vlenb, \ data, fn); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + finalize_rvv_inst(s); \ return true; \ } \ return false; \ @@ -3206,23 +3076,22 @@ static bool trans_viota_m(DisasContext *s, arg_viota_m *a) !is_overlapped(a->rd, 1 << MAX(s->lmul, 0), a->rs2, 1) && require_vm(a->vm, a->rd) && require_align(a->rd, s->lmul) && - (s->vstart == 0)) { + s->vstart_eq_zero) { uint32_t data = 0; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); static gen_helper_gvec_3_ptr * const fns[4] = { gen_helper_viota_m_b, gen_helper_viota_m_h, gen_helper_viota_m_w, gen_helper_viota_m_d, }; tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), - vreg_ofs(s, a->rs2), cpu_env, - s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data, fns[s->sew]); - mark_vs_dirty(s); - gen_set_label(over); + vreg_ofs(s, a->rs2), tcg_env, + s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data, fns[s->sew]); + finalize_rvv_inst(s); return true; } return false; @@ -3236,21 +3105,20 @@ static bool trans_vid_v(DisasContext *s, arg_vid_v *a) require_align(a->rd, s->lmul) && require_vm(a->vm, a->rd)) { uint32_t data = 0; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); static gen_helper_gvec_2_ptr * const fns[4] = { gen_helper_vid_v_b, gen_helper_vid_v_h, gen_helper_vid_v_w, gen_helper_vid_v_d, }; tcg_gen_gvec_2_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), - cpu_env, s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, + tcg_env, s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data, fns[s->sew]); - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } return false; @@ -3294,7 +3162,7 @@ static void load_element(TCGv_i64 dest, TCGv_ptr base, } } -/* offset of the idx element with base regsiter r */ +/* offset of the idx element with base register r */ static uint32_t endian_ofs(DisasContext *s, int r, int idx) { #if HOST_BIG_ENDIAN @@ -3334,13 +3202,11 @@ static void vec_element_loadx(DisasContext *s, TCGv_i64 dest, /* Convert the index to a pointer. */ tcg_gen_ext_i32_ptr(base, ofs); - tcg_gen_add_ptr(base, base, cpu_env); + tcg_gen_add_ptr(base, base, tcg_env); /* Perform the load. */ load_element(dest, base, vreg_ofs(s, vreg), s->sew, false); - tcg_temp_free_ptr(base); - tcg_temp_free_i32(ofs); /* Flush out-of-range indexing to zero. */ t_vlmax = tcg_constant_i64(vlmax); @@ -3349,14 +3215,12 @@ static void vec_element_loadx(DisasContext *s, TCGv_i64 dest, tcg_gen_movcond_i64(TCG_COND_LTU, dest, t_idx, t_vlmax, dest, t_zero); - - tcg_temp_free_i64(t_idx); } static void vec_element_loadi(DisasContext *s, TCGv_i64 dest, int vreg, int idx, bool sign) { - load_element(dest, cpu_env, endian_ofs(s, vreg, idx), s->sew, sign); + load_element(dest, tcg_env, endian_ofs(s, vreg, idx), s->sew, sign); } /* Integer Scalar Move Instruction */ @@ -3390,7 +3254,7 @@ static void store_element(TCGv_i64 val, TCGv_ptr base, static void vec_element_storei(DisasContext *s, int vreg, int idx, TCGv_i64 val) { - store_element(val, cpu_env, endian_ofs(s, vreg, idx), s->sew); + store_element(val, tcg_env, endian_ofs(s, vreg, idx), s->sew); } /* vmv.x.s rd, vs2 # x[rd] = vs2[0] */ @@ -3410,9 +3274,8 @@ static bool trans_vmv_x_s(DisasContext *s, arg_vmv_x_s *a) vec_element_loadi(s, t1, a->rs2, 0, true); tcg_gen_trunc_i64_tl(dest, t1); gen_set_gpr(s, a->rd, dest); - tcg_temp_free_i64(t1); - tcg_temp_free(dest); - + tcg_gen_movi_tl(cpu_vstart, 0); + finalize_rvv_inst(s); return true; } return false; @@ -3428,7 +3291,6 @@ static bool trans_vmv_s_x(DisasContext *s, arg_vmv_s_x *a) TCGv s1; TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); t1 = tcg_temp_new_i64(); @@ -3440,9 +3302,9 @@ static bool trans_vmv_s_x(DisasContext *s, arg_vmv_s_x *a) s1 = get_gpr(s, a->rs1, EXT_NONE); tcg_gen_ext_tl_i64(t1, s1); vec_element_storei(s, a->rd, 0, t1); - tcg_temp_free_i64(t1); - mark_vs_dirty(s); gen_set_label(over); + tcg_gen_movi_tl(cpu_vstart, 0); + finalize_rvv_inst(s); return true; } return false; @@ -3453,9 +3315,7 @@ static bool trans_vfmv_f_s(DisasContext *s, arg_vfmv_f_s *a) { if (require_rvv(s) && require_rvf(s) && - vext_check_isa_ill(s) && - require_zve32f(s) && - require_zve64f(s)) { + vext_check_isa_ill(s)) { gen_set_rm(s, RISCV_FRM_DYN); unsigned int ofs = (8 << s->sew); @@ -3471,6 +3331,8 @@ static bool trans_vfmv_f_s(DisasContext *s, arg_vfmv_f_s *a) } mark_fs_dirty(s); + tcg_gen_movi_tl(cpu_vstart, 0); + finalize_rvv_inst(s); return true; } return false; @@ -3481,17 +3343,14 @@ static bool trans_vfmv_s_f(DisasContext *s, arg_vfmv_s_f *a) { if (require_rvv(s) && require_rvf(s) && - vext_check_isa_ill(s) && - require_zve32f(s) && - require_zve64f(s)) { + vext_check_isa_ill(s)) { gen_set_rm(s, RISCV_FRM_DYN); /* The instructions ignore LMUL and vector register group. */ TCGv_i64 t1; TCGLabel *over = gen_new_label(); - /* if vl == 0 or vstart >= vl, skip vector register write back */ - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); + /* if vstart >= vl, skip vector register write back */ tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); /* NaN-box f[rs1] */ @@ -3499,9 +3358,10 @@ static bool trans_vfmv_s_f(DisasContext *s, arg_vfmv_s_f *a) do_nanbox(s, t1, cpu_fpr[a->rs1]); vec_element_storei(s, a->rd, 0, t1); - tcg_temp_free_i64(t1); - mark_vs_dirty(s); + gen_set_label(over); + tcg_gen_movi_tl(cpu_vstart, 0); + finalize_rvv_inst(s); return true; } return false; @@ -3534,17 +3394,13 @@ GEN_OPIVI_TRANS(vslidedown_vi, IMM_ZX, vslidedown_vx, slidedown_check) static bool fslideup_check(DisasContext *s, arg_rmrr *a) { return slideup_check(s, a) && - require_rvf(s) && - require_zve32f(s) && - require_zve64f(s); + require_rvf(s); } static bool fslidedown_check(DisasContext *s, arg_rmrr *a) { return slidedown_check(s, a) && - require_rvf(s) && - require_zve32f(s) && - require_zve64f(s); + require_rvf(s); } GEN_OPFVF_TRANS(vfslide1up_vf, fslideup_check) @@ -3599,9 +3455,8 @@ static bool trans_vrgather_vx(DisasContext *s, arg_rmrr *a) return false; } - if (a->vm && s->vl_eq_vlmax) { - int scale = s->lmul - (s->sew + 3); - int vlmax = s->cfg_ptr->vlen >> -scale; + if (a->vm && s->vl_eq_vlmax && !(s->vta && s->lmul < 0)) { + int vlmax = vext_get_vlmax(s->cfg_ptr->vlenb, s->sew, s->lmul); TCGv_i64 dest = tcg_temp_new_i64(); if (a->rs1 == 0) { @@ -3612,8 +3467,7 @@ static bool trans_vrgather_vx(DisasContext *s, arg_rmrr *a) tcg_gen_gvec_dup_i64(s->sew, vreg_ofs(s, a->rd), MAXSZ(s), MAXSZ(s), dest); - tcg_temp_free_i64(dest); - mark_vs_dirty(s); + finalize_rvv_inst(s); } else { static gen_helper_opivx * const fns[4] = { gen_helper_vrgather_vx_b, gen_helper_vrgather_vx_h, @@ -3631,9 +3485,8 @@ static bool trans_vrgather_vi(DisasContext *s, arg_rmrr *a) return false; } - if (a->vm && s->vl_eq_vlmax) { - int scale = s->lmul - (s->sew + 3); - int vlmax = s->cfg_ptr->vlen >> -scale; + if (a->vm && s->vl_eq_vlmax && !(s->vta && s->lmul < 0)) { + int vlmax = vext_get_vlmax(s->cfg_ptr->vlenb, s->sew, s->lmul); if (a->rs1 >= vlmax) { tcg_gen_gvec_dup_imm(MO_64, vreg_ofs(s, a->rd), MAXSZ(s), MAXSZ(s), 0); @@ -3642,7 +3495,7 @@ static bool trans_vrgather_vi(DisasContext *s, arg_rmrr *a) endian_ofs(s, a->rs2, a->rs1), MAXSZ(s), MAXSZ(s)); } - mark_vs_dirty(s); + finalize_rvv_inst(s); } else { static gen_helper_opivx * const fns[4] = { gen_helper_vrgather_vx_b, gen_helper_vrgather_vx_h, @@ -3668,7 +3521,7 @@ static bool vcompress_vm_check(DisasContext *s, arg_r *a) require_align(a->rs2, s->lmul) && (a->rd != a->rs2) && !is_overlapped(a->rd, 1 << MAX(s->lmul, 0), a->rs1, 1) && - (s->vstart == 0); + s->vstart_eq_zero; } static bool trans_vcompress_vm(DisasContext *s, arg_r *a) @@ -3679,46 +3532,40 @@ static bool trans_vcompress_vm(DisasContext *s, arg_r *a) gen_helper_vcompress_vm_b, gen_helper_vcompress_vm_h, gen_helper_vcompress_vm_w, gen_helper_vcompress_vm_d, }; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2), - cpu_env, s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data, + tcg_env, s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data, fns[s->sew]); - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } return false; } /* - * Whole Vector Register Move Instructions ignore vtype and vl setting. - * Thus, we don't need to check vill bit. (Section 16.6) + * Whole Vector Register Move Instructions depend on vtype register(vsew). + * Thus, we need to check vill bit. (Section 16.6) */ #define GEN_VMV_WHOLE_TRANS(NAME, LEN) \ static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \ { \ if (require_rvv(s) && \ + vext_check_isa_ill(s) && \ QEMU_IS_ALIGNED(a->rd, LEN) && \ QEMU_IS_ALIGNED(a->rs2, LEN)) { \ - uint32_t maxsz = (s->cfg_ptr->vlen >> 3) * LEN; \ - if (s->vstart == 0) { \ - /* EEW = 8 */ \ - tcg_gen_gvec_mov(MO_8, vreg_ofs(s, a->rd), \ + uint32_t maxsz = s->cfg_ptr->vlenb * LEN; \ + if (s->vstart_eq_zero) { \ + tcg_gen_gvec_mov(s->sew, vreg_ofs(s, a->rd), \ vreg_ofs(s, a->rs2), maxsz, maxsz); \ - mark_vs_dirty(s); \ } else { \ - TCGLabel *over = gen_new_label(); \ - tcg_gen_brcondi_tl(TCG_COND_GEU, cpu_vstart, maxsz, over); \ tcg_gen_gvec_2_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, a->rs2), \ - cpu_env, maxsz, maxsz, 0, gen_helper_vmvr_v); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + tcg_env, maxsz, maxsz, 0, gen_helper_vmvr_v); \ } \ + finalize_rvv_inst(s); \ return true; \ } \ return false; \ @@ -3746,8 +3593,6 @@ static bool int_ext_op(DisasContext *s, arg_rmr *a, uint8_t seq) { uint32_t data = 0; gen_helper_gvec_3_ptr *fn; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); static gen_helper_gvec_3_ptr * const fns[6][4] = { { @@ -3782,14 +3627,16 @@ static bool int_ext_op(DisasContext *s, arg_rmr *a, uint8_t seq) } data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VMA, s->vma); tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), - vreg_ofs(s, a->rs2), cpu_env, - s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data, fn); + vreg_ofs(s, a->rs2), tcg_env, + s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data, fn); - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } diff --git a/target/riscv/insn_trans/trans_rvvk.c.inc b/target/riscv/insn_trans/trans_rvvk.c.inc new file mode 100644 index 0000000000..ae1f40174a --- /dev/null +++ b/target/riscv/insn_trans/trans_rvvk.c.inc @@ -0,0 +1,599 @@ +/* + * RISC-V translation routines for the vector crypto extension. + * + * Copyright (C) 2023 SiFive, Inc. + * Written by Codethink Ltd and SiFive. + * + * 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 . + */ + +/* + * Zvbc + */ + +#define GEN_VV_MASKED_TRANS(NAME, CHECK) \ + static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ + { \ + if (CHECK(s, a)) { \ + return opivv_trans(a->rd, a->rs1, a->rs2, a->vm, \ + gen_helper_##NAME, s); \ + } \ + return false; \ + } + +static bool vclmul_vv_check(DisasContext *s, arg_rmrr *a) +{ + return opivv_check(s, a) && + s->cfg_ptr->ext_zvbc == true && + s->sew == MO_64; +} + +GEN_VV_MASKED_TRANS(vclmul_vv, vclmul_vv_check) +GEN_VV_MASKED_TRANS(vclmulh_vv, vclmul_vv_check) + +#define GEN_VX_MASKED_TRANS(NAME, CHECK) \ + static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ + { \ + if (CHECK(s, a)) { \ + return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, \ + gen_helper_##NAME, s); \ + } \ + return false; \ + } + +static bool vclmul_vx_check(DisasContext *s, arg_rmrr *a) +{ + return opivx_check(s, a) && + s->cfg_ptr->ext_zvbc == true && + s->sew == MO_64; +} + +GEN_VX_MASKED_TRANS(vclmul_vx, vclmul_vx_check) +GEN_VX_MASKED_TRANS(vclmulh_vx, vclmul_vx_check) + +/* + * Zvbb + */ + +#define GEN_OPIVI_GVEC_TRANS_CHECK(NAME, IMM_MODE, OPIVX, SUF, CHECK) \ + static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ + { \ + if (CHECK(s, a)) { \ + static gen_helper_opivx *const fns[4] = { \ + gen_helper_##OPIVX##_b, \ + gen_helper_##OPIVX##_h, \ + gen_helper_##OPIVX##_w, \ + gen_helper_##OPIVX##_d, \ + }; \ + return do_opivi_gvec(s, a, tcg_gen_gvec_##SUF, fns[s->sew], \ + IMM_MODE); \ + } \ + return false; \ + } + +#define GEN_OPIVV_GVEC_TRANS_CHECK(NAME, SUF, CHECK) \ + static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ + { \ + if (CHECK(s, a)) { \ + static gen_helper_gvec_4_ptr *const fns[4] = { \ + gen_helper_##NAME##_b, \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, \ + gen_helper_##NAME##_d, \ + }; \ + return do_opivv_gvec(s, a, tcg_gen_gvec_##SUF, fns[s->sew]); \ + } \ + return false; \ + } + +#define GEN_OPIVX_GVEC_SHIFT_TRANS_CHECK(NAME, SUF, CHECK) \ + static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ + { \ + if (CHECK(s, a)) { \ + static gen_helper_opivx *const fns[4] = { \ + gen_helper_##NAME##_b, \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, \ + gen_helper_##NAME##_d, \ + }; \ + return do_opivx_gvec_shift(s, a, tcg_gen_gvec_##SUF, \ + fns[s->sew]); \ + } \ + return false; \ + } + +static bool zvkb_vv_check(DisasContext *s, arg_rmrr *a) +{ + return opivv_check(s, a) && + (s->cfg_ptr->ext_zvbb == true || s->cfg_ptr->ext_zvkb == true); +} + +static bool zvkb_vx_check(DisasContext *s, arg_rmrr *a) +{ + return opivx_check(s, a) && + (s->cfg_ptr->ext_zvbb == true || s->cfg_ptr->ext_zvkb == true); +} + +/* vrol.v[vx] */ +GEN_OPIVV_GVEC_TRANS_CHECK(vrol_vv, rotlv, zvkb_vv_check) +GEN_OPIVX_GVEC_SHIFT_TRANS_CHECK(vrol_vx, rotls, zvkb_vx_check) + +/* vror.v[vxi] */ +GEN_OPIVV_GVEC_TRANS_CHECK(vror_vv, rotrv, zvkb_vv_check) +GEN_OPIVX_GVEC_SHIFT_TRANS_CHECK(vror_vx, rotrs, zvkb_vx_check) +GEN_OPIVI_GVEC_TRANS_CHECK(vror_vi, IMM_TRUNC_SEW, vror_vx, rotri, + zvkb_vx_check) + +#define GEN_OPIVX_GVEC_TRANS_CHECK(NAME, SUF, CHECK) \ + static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ + { \ + if (CHECK(s, a)) { \ + static gen_helper_opivx *const fns[4] = { \ + gen_helper_##NAME##_b, \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, \ + gen_helper_##NAME##_d, \ + }; \ + return do_opivx_gvec(s, a, tcg_gen_gvec_##SUF, fns[s->sew]); \ + } \ + return false; \ + } + +/* vandn.v[vx] */ +GEN_OPIVV_GVEC_TRANS_CHECK(vandn_vv, andc, zvkb_vv_check) +GEN_OPIVX_GVEC_TRANS_CHECK(vandn_vx, andcs, zvkb_vx_check) + +#define GEN_OPIV_TRANS(NAME, CHECK) \ + static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ + { \ + if (CHECK(s, a)) { \ + uint32_t data = 0; \ + static gen_helper_gvec_3_ptr *const fns[4] = { \ + gen_helper_##NAME##_b, \ + gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, \ + gen_helper_##NAME##_d, \ + }; \ + \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ + tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ + vreg_ofs(s, a->rs2), tcg_env, \ + s->cfg_ptr->vlenb, s->cfg_ptr->vlenb, \ + data, fns[s->sew]); \ + finalize_rvv_inst(s); \ + return true; \ + } \ + return false; \ + } + +static bool zvbb_opiv_check(DisasContext *s, arg_rmr *a) +{ + return s->cfg_ptr->ext_zvbb == true && + require_rvv(s) && + vext_check_isa_ill(s) && + vext_check_ss(s, a->rd, a->rs2, a->vm); +} + +static bool zvkb_opiv_check(DisasContext *s, arg_rmr *a) +{ + return (s->cfg_ptr->ext_zvbb == true || s->cfg_ptr->ext_zvkb == true) && + require_rvv(s) && + vext_check_isa_ill(s) && + vext_check_ss(s, a->rd, a->rs2, a->vm); +} + +GEN_OPIV_TRANS(vbrev8_v, zvkb_opiv_check) +GEN_OPIV_TRANS(vrev8_v, zvkb_opiv_check) +GEN_OPIV_TRANS(vbrev_v, zvbb_opiv_check) +GEN_OPIV_TRANS(vclz_v, zvbb_opiv_check) +GEN_OPIV_TRANS(vctz_v, zvbb_opiv_check) +GEN_OPIV_TRANS(vcpop_v, zvbb_opiv_check) + +static bool vwsll_vv_check(DisasContext *s, arg_rmrr *a) +{ + return s->cfg_ptr->ext_zvbb && opivv_widen_check(s, a); +} + +static bool vwsll_vx_check(DisasContext *s, arg_rmrr *a) +{ + return s->cfg_ptr->ext_zvbb && opivx_widen_check(s, a); +} + +/* OPIVI without GVEC IR */ +#define GEN_OPIVI_WIDEN_TRANS(NAME, IMM_MODE, OPIVX, CHECK) \ + static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ + { \ + if (CHECK(s, a)) { \ + static gen_helper_opivx *const fns[3] = { \ + gen_helper_##OPIVX##_b, \ + gen_helper_##OPIVX##_h, \ + gen_helper_##OPIVX##_w, \ + }; \ + return opivi_trans(a->rd, a->rs1, a->rs2, a->vm, fns[s->sew], s, \ + IMM_MODE); \ + } \ + return false; \ + } + +GEN_OPIVV_WIDEN_TRANS(vwsll_vv, vwsll_vv_check) +GEN_OPIVX_WIDEN_TRANS(vwsll_vx, vwsll_vx_check) +GEN_OPIVI_WIDEN_TRANS(vwsll_vi, IMM_ZX, vwsll_vx, vwsll_vx_check) + +/* + * Zvkned + */ + +#define ZVKNED_EGS 4 + +#define GEN_V_UNMASKED_TRANS(NAME, CHECK, EGS) \ + static bool trans_##NAME(DisasContext *s, arg_##NAME *a) \ + { \ + if (CHECK(s, a)) { \ + TCGv_ptr rd_v, rs2_v; \ + TCGv_i32 desc, egs; \ + uint32_t data = 0; \ + \ + if (!s->vstart_eq_zero || !s->vl_eq_vlmax) { \ + /* save opcode for unwinding in case we throw an exception */ \ + decode_save_opc(s); \ + egs = tcg_constant_i32(EGS); \ + gen_helper_egs_check(egs, tcg_env); \ + } \ + \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ + rd_v = tcg_temp_new_ptr(); \ + rs2_v = tcg_temp_new_ptr(); \ + desc = tcg_constant_i32( \ + simd_desc(s->cfg_ptr->vlenb, s->cfg_ptr->vlenb, data)); \ + tcg_gen_addi_ptr(rd_v, tcg_env, vreg_ofs(s, a->rd)); \ + tcg_gen_addi_ptr(rs2_v, tcg_env, vreg_ofs(s, a->rs2)); \ + gen_helper_##NAME(rd_v, rs2_v, tcg_env, desc); \ + finalize_rvv_inst(s); \ + return true; \ + } \ + return false; \ + } + +static bool vaes_check_vv(DisasContext *s, arg_rmr *a) +{ + int egw_bytes = ZVKNED_EGS << s->sew; + return s->cfg_ptr->ext_zvkned == true && + require_rvv(s) && + vext_check_isa_ill(s) && + MAXSZ(s) >= egw_bytes && + require_align(a->rd, s->lmul) && + require_align(a->rs2, s->lmul) && + s->sew == MO_32; +} + +static bool vaes_check_overlap(DisasContext *s, int vd, int vs2) +{ + int8_t op_size = s->lmul <= 0 ? 1 : 1 << s->lmul; + return !is_overlapped(vd, op_size, vs2, 1); +} + +static bool vaes_check_vs(DisasContext *s, arg_rmr *a) +{ + int egw_bytes = ZVKNED_EGS << s->sew; + return vaes_check_overlap(s, a->rd, a->rs2) && + MAXSZ(s) >= egw_bytes && + s->cfg_ptr->ext_zvkned == true && + require_rvv(s) && + vext_check_isa_ill(s) && + require_align(a->rd, s->lmul) && + s->sew == MO_32; +} + +GEN_V_UNMASKED_TRANS(vaesef_vv, vaes_check_vv, ZVKNED_EGS) +GEN_V_UNMASKED_TRANS(vaesef_vs, vaes_check_vs, ZVKNED_EGS) +GEN_V_UNMASKED_TRANS(vaesdf_vv, vaes_check_vv, ZVKNED_EGS) +GEN_V_UNMASKED_TRANS(vaesdf_vs, vaes_check_vs, ZVKNED_EGS) +GEN_V_UNMASKED_TRANS(vaesdm_vv, vaes_check_vv, ZVKNED_EGS) +GEN_V_UNMASKED_TRANS(vaesdm_vs, vaes_check_vs, ZVKNED_EGS) +GEN_V_UNMASKED_TRANS(vaesz_vs, vaes_check_vs, ZVKNED_EGS) +GEN_V_UNMASKED_TRANS(vaesem_vv, vaes_check_vv, ZVKNED_EGS) +GEN_V_UNMASKED_TRANS(vaesem_vs, vaes_check_vs, ZVKNED_EGS) + +#define GEN_VI_UNMASKED_TRANS(NAME, CHECK, EGS) \ + static bool trans_##NAME(DisasContext *s, arg_##NAME *a) \ + { \ + if (CHECK(s, a)) { \ + TCGv_ptr rd_v, rs2_v; \ + TCGv_i32 uimm_v, desc, egs; \ + uint32_t data = 0; \ + \ + if (!s->vstart_eq_zero || !s->vl_eq_vlmax) { \ + /* save opcode for unwinding in case we throw an exception */ \ + decode_save_opc(s); \ + egs = tcg_constant_i32(EGS); \ + gen_helper_egs_check(egs, tcg_env); \ + } \ + \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ + \ + rd_v = tcg_temp_new_ptr(); \ + rs2_v = tcg_temp_new_ptr(); \ + uimm_v = tcg_constant_i32(a->rs1); \ + desc = tcg_constant_i32( \ + simd_desc(s->cfg_ptr->vlenb, s->cfg_ptr->vlenb, data)); \ + tcg_gen_addi_ptr(rd_v, tcg_env, vreg_ofs(s, a->rd)); \ + tcg_gen_addi_ptr(rs2_v, tcg_env, vreg_ofs(s, a->rs2)); \ + gen_helper_##NAME(rd_v, rs2_v, uimm_v, tcg_env, desc); \ + finalize_rvv_inst(s); \ + return true; \ + } \ + return false; \ + } + +static bool vaeskf1_check(DisasContext *s, arg_vaeskf1_vi *a) +{ + int egw_bytes = ZVKNED_EGS << s->sew; + return s->cfg_ptr->ext_zvkned == true && + require_rvv(s) && + vext_check_isa_ill(s) && + MAXSZ(s) >= egw_bytes && + s->sew == MO_32 && + require_align(a->rd, s->lmul) && + require_align(a->rs2, s->lmul); +} + +static bool vaeskf2_check(DisasContext *s, arg_vaeskf2_vi *a) +{ + int egw_bytes = ZVKNED_EGS << s->sew; + return s->cfg_ptr->ext_zvkned == true && + require_rvv(s) && + vext_check_isa_ill(s) && + MAXSZ(s) >= egw_bytes && + s->sew == MO_32 && + require_align(a->rd, s->lmul) && + require_align(a->rs2, s->lmul); +} + +GEN_VI_UNMASKED_TRANS(vaeskf1_vi, vaeskf1_check, ZVKNED_EGS) +GEN_VI_UNMASKED_TRANS(vaeskf2_vi, vaeskf2_check, ZVKNED_EGS) + +/* + * Zvknh + */ + +#define ZVKNH_EGS 4 + +#define GEN_VV_UNMASKED_TRANS(NAME, CHECK, EGS) \ + static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ + { \ + if (CHECK(s, a)) { \ + uint32_t data = 0; \ + TCGv_i32 egs; \ + \ + if (!s->vstart_eq_zero || !s->vl_eq_vlmax) { \ + /* save opcode for unwinding in case we throw an exception */ \ + decode_save_opc(s); \ + egs = tcg_constant_i32(EGS); \ + gen_helper_egs_check(egs, tcg_env); \ + } \ + \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ + \ + tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, a->rs1), \ + vreg_ofs(s, a->rs2), tcg_env, \ + s->cfg_ptr->vlenb, s->cfg_ptr->vlenb, \ + data, gen_helper_##NAME); \ + \ + finalize_rvv_inst(s); \ + return true; \ + } \ + return false; \ + } + +static bool vsha_check_sew(DisasContext *s) +{ + return (s->cfg_ptr->ext_zvknha == true && s->sew == MO_32) || + (s->cfg_ptr->ext_zvknhb == true && + (s->sew == MO_32 || s->sew == MO_64)); +} + +static bool vsha_check(DisasContext *s, arg_rmrr *a) +{ + int egw_bytes = ZVKNH_EGS << s->sew; + int mult = 1 << MAX(s->lmul, 0); + return opivv_check(s, a) && + vsha_check_sew(s) && + MAXSZ(s) >= egw_bytes && + !is_overlapped(a->rd, mult, a->rs1, mult) && + !is_overlapped(a->rd, mult, a->rs2, mult) && + s->lmul >= 0; +} + +GEN_VV_UNMASKED_TRANS(vsha2ms_vv, vsha_check, ZVKNH_EGS) + +static bool trans_vsha2cl_vv(DisasContext *s, arg_rmrr *a) +{ + if (vsha_check(s, a)) { + uint32_t data = 0; + TCGv_i32 egs; + + if (!s->vstart_eq_zero || !s->vl_eq_vlmax) { + /* save opcode for unwinding in case we throw an exception */ + decode_save_opc(s); + egs = tcg_constant_i32(ZVKNH_EGS); + gen_helper_egs_check(egs, tcg_env); + } + + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s); + data = FIELD_DP32(data, VDATA, VMA, s->vma); + + tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, a->rs1), + vreg_ofs(s, a->rs2), tcg_env, s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data, + s->sew == MO_32 ? + gen_helper_vsha2cl32_vv : gen_helper_vsha2cl64_vv); + + finalize_rvv_inst(s); + return true; + } + return false; +} + +static bool trans_vsha2ch_vv(DisasContext *s, arg_rmrr *a) +{ + if (vsha_check(s, a)) { + uint32_t data = 0; + TCGv_i32 egs; + + if (!s->vstart_eq_zero || !s->vl_eq_vlmax) { + /* save opcode for unwinding in case we throw an exception */ + decode_save_opc(s); + egs = tcg_constant_i32(ZVKNH_EGS); + gen_helper_egs_check(egs, tcg_env); + } + + data = FIELD_DP32(data, VDATA, VM, a->vm); + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); + data = FIELD_DP32(data, VDATA, VTA, s->vta); + data = FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s); + data = FIELD_DP32(data, VDATA, VMA, s->vma); + + tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, a->rs1), + vreg_ofs(s, a->rs2), tcg_env, s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data, + s->sew == MO_32 ? + gen_helper_vsha2ch32_vv : gen_helper_vsha2ch64_vv); + + finalize_rvv_inst(s); + return true; + } + return false; +} + +/* + * Zvksh + */ + +#define ZVKSH_EGS 8 + +static inline bool vsm3_check(DisasContext *s, arg_rmrr *a) +{ + int egw_bytes = ZVKSH_EGS << s->sew; + int mult = 1 << MAX(s->lmul, 0); + return s->cfg_ptr->ext_zvksh == true && + require_rvv(s) && + vext_check_isa_ill(s) && + !is_overlapped(a->rd, mult, a->rs2, mult) && + MAXSZ(s) >= egw_bytes && + s->sew == MO_32; +} + +static inline bool vsm3me_check(DisasContext *s, arg_rmrr *a) +{ + return vsm3_check(s, a) && vext_check_sss(s, a->rd, a->rs1, a->rs2, a->vm); +} + +static inline bool vsm3c_check(DisasContext *s, arg_rmrr *a) +{ + return vsm3_check(s, a) && vext_check_ss(s, a->rd, a->rs2, a->vm); +} + +GEN_VV_UNMASKED_TRANS(vsm3me_vv, vsm3me_check, ZVKSH_EGS) +GEN_VI_UNMASKED_TRANS(vsm3c_vi, vsm3c_check, ZVKSH_EGS) + +/* + * Zvkg + */ + +#define ZVKG_EGS 4 + +static bool vgmul_check(DisasContext *s, arg_rmr *a) +{ + int egw_bytes = ZVKG_EGS << s->sew; + return s->cfg_ptr->ext_zvkg == true && + vext_check_isa_ill(s) && + require_rvv(s) && + MAXSZ(s) >= egw_bytes && + vext_check_ss(s, a->rd, a->rs2, a->vm) && + s->sew == MO_32; +} + +GEN_V_UNMASKED_TRANS(vgmul_vv, vgmul_check, ZVKG_EGS) + +static bool vghsh_check(DisasContext *s, arg_rmrr *a) +{ + int egw_bytes = ZVKG_EGS << s->sew; + return s->cfg_ptr->ext_zvkg == true && + opivv_check(s, a) && + MAXSZ(s) >= egw_bytes && + s->sew == MO_32; +} + +GEN_VV_UNMASKED_TRANS(vghsh_vv, vghsh_check, ZVKG_EGS) + +/* + * Zvksed + */ + +#define ZVKSED_EGS 4 + +static bool zvksed_check(DisasContext *s) +{ + int egw_bytes = ZVKSED_EGS << s->sew; + return s->cfg_ptr->ext_zvksed == true && + require_rvv(s) && + vext_check_isa_ill(s) && + MAXSZ(s) >= egw_bytes && + s->sew == MO_32; +} + +static bool vsm4k_vi_check(DisasContext *s, arg_rmrr *a) +{ + return zvksed_check(s) && + require_align(a->rd, s->lmul) && + require_align(a->rs2, s->lmul); +} + +GEN_VI_UNMASKED_TRANS(vsm4k_vi, vsm4k_vi_check, ZVKSED_EGS) + +static bool vsm4r_vv_check(DisasContext *s, arg_rmr *a) +{ + return zvksed_check(s) && + require_align(a->rd, s->lmul) && + require_align(a->rs2, s->lmul); +} + +GEN_V_UNMASKED_TRANS(vsm4r_vv, vsm4r_vv_check, ZVKSED_EGS) + +static bool vsm4r_vs_check(DisasContext *s, arg_rmr *a) +{ + return zvksed_check(s) && + !is_overlapped(a->rd, 1 << MAX(s->lmul, 0), a->rs2, 1) && + require_align(a->rd, s->lmul); +} + +GEN_V_UNMASKED_TRANS(vsm4r_vs, vsm4r_vs_check, ZVKSED_EGS) diff --git a/target/riscv/insn_trans/trans_rvzacas.c.inc b/target/riscv/insn_trans/trans_rvzacas.c.inc new file mode 100644 index 0000000000..5d274d4c08 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzacas.c.inc @@ -0,0 +1,150 @@ +/* + * RISC-V translation routines for the RV64 Zacas Standard Extension. + * + * Copyright (c) 2020-2023 PLCT Lab + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#define REQUIRE_ZACAS(ctx) do { \ + if (!ctx->cfg_ptr->ext_zacas) { \ + return false; \ + } \ +} while (0) + +static bool gen_cmpxchg(DisasContext *ctx, arg_atomic *a, MemOp mop) +{ + TCGv dest = get_gpr(ctx, a->rd, EXT_NONE); + TCGv src1 = get_address(ctx, a->rs1, 0); + TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); + + decode_save_opc(ctx); + tcg_gen_atomic_cmpxchg_tl(dest, src1, dest, src2, ctx->mem_idx, mop); + + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +static bool trans_amocas_w(DisasContext *ctx, arg_amocas_w *a) +{ + REQUIRE_ZACAS(ctx); + return gen_cmpxchg(ctx, a, MO_ALIGN | MO_TESL); +} + +static TCGv_i64 get_gpr_pair(DisasContext *ctx, int reg_num) +{ + TCGv_i64 t; + + assert(get_ol(ctx) == MXL_RV32); + + if (reg_num == 0) { + return tcg_constant_i64(0); + } + + t = tcg_temp_new_i64(); + tcg_gen_concat_tl_i64(t, cpu_gpr[reg_num], cpu_gpr[reg_num + 1]); + return t; +} + +static void gen_set_gpr_pair(DisasContext *ctx, int reg_num, TCGv_i64 t) +{ + assert(get_ol(ctx) == MXL_RV32); + + if (reg_num != 0) { +#ifdef TARGET_RISCV32 + tcg_gen_extr_i64_i32(cpu_gpr[reg_num], cpu_gpr[reg_num + 1], t); +#else + tcg_gen_ext32s_i64(cpu_gpr[reg_num], t); + tcg_gen_sari_i64(cpu_gpr[reg_num + 1], t, 32); +#endif + + if (get_xl_max(ctx) == MXL_RV128) { + tcg_gen_sari_tl(cpu_gprh[reg_num], cpu_gpr[reg_num], 63); + tcg_gen_sari_tl(cpu_gprh[reg_num + 1], cpu_gpr[reg_num + 1], 63); + } + } +} + +static bool gen_cmpxchg64(DisasContext *ctx, arg_atomic *a, MemOp mop) +{ + /* + * Encodings with odd numbered registers specified in rs2 and rd are + * reserved. + */ + if ((a->rs2 | a->rd) & 1) { + return false; + } + + TCGv_i64 dest = get_gpr_pair(ctx, a->rd); + TCGv src1 = get_address(ctx, a->rs1, 0); + TCGv_i64 src2 = get_gpr_pair(ctx, a->rs2); + + decode_save_opc(ctx); + tcg_gen_atomic_cmpxchg_i64(dest, src1, dest, src2, ctx->mem_idx, mop); + + gen_set_gpr_pair(ctx, a->rd, dest); + return true; +} + +static bool trans_amocas_d(DisasContext *ctx, arg_amocas_d *a) +{ + REQUIRE_ZACAS(ctx); + switch (get_ol(ctx)) { + case MXL_RV32: + return gen_cmpxchg64(ctx, a, MO_ALIGN | MO_TEUQ); + case MXL_RV64: + case MXL_RV128: + return gen_cmpxchg(ctx, a, MO_ALIGN | MO_TEUQ); + default: + g_assert_not_reached(); + } +} + +static bool trans_amocas_q(DisasContext *ctx, arg_amocas_q *a) +{ + REQUIRE_ZACAS(ctx); + REQUIRE_64BIT(ctx); + + /* + * Encodings with odd numbered registers specified in rs2 and rd are + * reserved. + */ + if ((a->rs2 | a->rd) & 1) { + return false; + } + +#ifdef TARGET_RISCV64 + TCGv_i128 dest = tcg_temp_new_i128(); + TCGv src1 = get_address(ctx, a->rs1, 0); + TCGv_i128 src2 = tcg_temp_new_i128(); + TCGv_i64 src2l = get_gpr(ctx, a->rs2, EXT_NONE); + TCGv_i64 src2h = get_gpr(ctx, a->rs2 == 0 ? 0 : a->rs2 + 1, EXT_NONE); + TCGv_i64 destl = get_gpr(ctx, a->rd, EXT_NONE); + TCGv_i64 desth = get_gpr(ctx, a->rd == 0 ? 0 : a->rd + 1, EXT_NONE); + + tcg_gen_concat_i64_i128(src2, src2l, src2h); + tcg_gen_concat_i64_i128(dest, destl, desth); + decode_save_opc(ctx); + tcg_gen_atomic_cmpxchg_i128(dest, src1, dest, src2, ctx->mem_idx, + (MO_ALIGN | MO_TEUO)); + + tcg_gen_extr_i128_i64(destl, desth, dest); + + if (a->rd != 0) { + gen_set_gpr(ctx, a->rd, destl); + gen_set_gpr(ctx, a->rd + 1, desth); + } +#endif + + return true; +} diff --git a/target/riscv/insn_trans/trans_rvzawrs.c.inc b/target/riscv/insn_trans/trans_rvzawrs.c.inc new file mode 100644 index 0000000000..32efbff4d5 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzawrs.c.inc @@ -0,0 +1,51 @@ +/* + * RISC-V translation routines for the RISC-V Zawrs Extension. + * + * Copyright (c) 2022 Christoph Muellner, christoph.muellner@vrull.io + * + * 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 . + */ + +static bool trans_wrs(DisasContext *ctx) +{ + if (!ctx->cfg_ptr->ext_zawrs) { + return false; + } + + /* + * The specification says: + * While stalled, an implementation is permitted to occasionally + * terminate the stall and complete execution for any reason. + * + * So let's just exit TB and return to the main loop. + */ + + /* Clear the load reservation (if any). */ + tcg_gen_movi_tl(load_res, -1); + + gen_update_pc(ctx, ctx->cur_insn_len); + tcg_gen_exit_tb(NULL, 0); + ctx->base.is_jmp = DISAS_NORETURN; + + return true; +} + +#define GEN_TRANS_WRS(insn) \ +static bool trans_ ## insn(DisasContext *ctx, arg_ ## insn *a) \ +{ \ + (void)a; \ + return trans_wrs(ctx); \ +} + +GEN_TRANS_WRS(wrs_nto) +GEN_TRANS_WRS(wrs_sto) diff --git a/target/riscv/insn_trans/trans_rvzce.c.inc b/target/riscv/insn_trans/trans_rvzce.c.inc new file mode 100644 index 0000000000..cd234ad960 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzce.c.inc @@ -0,0 +1,317 @@ +/* + * RISC-V translation routines for the Zc[b,mp,mt] Standard Extensions. + * + * Copyright (c) 2021-2022 PLCT Lab + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#define REQUIRE_ZCB(ctx) do { \ + if (!ctx->cfg_ptr->ext_zcb) \ + return false; \ +} while (0) + +#define REQUIRE_ZCMP(ctx) do { \ + if (!ctx->cfg_ptr->ext_zcmp) \ + return false; \ +} while (0) + +#define REQUIRE_ZCMT(ctx) do { \ + if (!ctx->cfg_ptr->ext_zcmt) \ + return false; \ +} while (0) + +static bool trans_c_zext_b(DisasContext *ctx, arg_c_zext_b *a) +{ + REQUIRE_ZCB(ctx); + return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext8u_tl); +} + +static bool trans_c_zext_h(DisasContext *ctx, arg_c_zext_h *a) +{ + REQUIRE_ZCB(ctx); + REQUIRE_ZBB(ctx); + return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext16u_tl); +} + +static bool trans_c_sext_b(DisasContext *ctx, arg_c_sext_b *a) +{ + REQUIRE_ZCB(ctx); + REQUIRE_ZBB(ctx); + return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext8s_tl); +} + +static bool trans_c_sext_h(DisasContext *ctx, arg_c_sext_h *a) +{ + REQUIRE_ZCB(ctx); + REQUIRE_ZBB(ctx); + return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext16s_tl); +} + +static bool trans_c_zext_w(DisasContext *ctx, arg_c_zext_w *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_ZCB(ctx); + REQUIRE_ZBA(ctx); + return gen_unary(ctx, a, EXT_NONE, tcg_gen_ext32u_tl); +} + +static bool trans_c_not(DisasContext *ctx, arg_c_not *a) +{ + REQUIRE_ZCB(ctx); + return gen_unary(ctx, a, EXT_NONE, tcg_gen_not_tl); +} + +static bool trans_c_mul(DisasContext *ctx, arg_c_mul *a) +{ + REQUIRE_ZCB(ctx); + REQUIRE_M_OR_ZMMUL(ctx); + return gen_arith(ctx, a, EXT_NONE, tcg_gen_mul_tl, NULL); +} + +static bool trans_c_lbu(DisasContext *ctx, arg_c_lbu *a) +{ + REQUIRE_ZCB(ctx); + return gen_load(ctx, a, MO_UB); +} + +static bool trans_c_lhu(DisasContext *ctx, arg_c_lhu *a) +{ + REQUIRE_ZCB(ctx); + return gen_load(ctx, a, MO_UW); +} + +static bool trans_c_lh(DisasContext *ctx, arg_c_lh *a) +{ + REQUIRE_ZCB(ctx); + return gen_load(ctx, a, MO_SW); +} + +static bool trans_c_sb(DisasContext *ctx, arg_c_sb *a) +{ + REQUIRE_ZCB(ctx); + return gen_store(ctx, a, MO_UB); +} + +static bool trans_c_sh(DisasContext *ctx, arg_c_sh *a) +{ + REQUIRE_ZCB(ctx); + return gen_store(ctx, a, MO_UW); +} + +#define X_S0 8 +#define X_S1 9 +#define X_Sn 16 + +static uint32_t decode_push_pop_list(DisasContext *ctx, target_ulong rlist) +{ + uint32_t reg_bitmap = 0; + + if (has_ext(ctx, RVE) && rlist > 6) { + return 0; + } + + switch (rlist) { + case 15: + reg_bitmap |= 1 << (X_Sn + 11) ; + reg_bitmap |= 1 << (X_Sn + 10) ; + /* FALL THROUGH */ + case 14: + reg_bitmap |= 1 << (X_Sn + 9) ; + /* FALL THROUGH */ + case 13: + reg_bitmap |= 1 << (X_Sn + 8) ; + /* FALL THROUGH */ + case 12: + reg_bitmap |= 1 << (X_Sn + 7) ; + /* FALL THROUGH */ + case 11: + reg_bitmap |= 1 << (X_Sn + 6) ; + /* FALL THROUGH */ + case 10: + reg_bitmap |= 1 << (X_Sn + 5) ; + /* FALL THROUGH */ + case 9: + reg_bitmap |= 1 << (X_Sn + 4) ; + /* FALL THROUGH */ + case 8: + reg_bitmap |= 1 << (X_Sn + 3) ; + /* FALL THROUGH */ + case 7: + reg_bitmap |= 1 << (X_Sn + 2) ; + /* FALL THROUGH */ + case 6: + reg_bitmap |= 1 << X_S1 ; + /* FALL THROUGH */ + case 5: + reg_bitmap |= 1 << X_S0; + /* FALL THROUGH */ + case 4: + reg_bitmap |= 1 << xRA; + break; + default: + break; + } + + return reg_bitmap; +} + +static bool gen_pop(DisasContext *ctx, arg_cmpp *a, bool ret, bool ret_val) +{ + REQUIRE_ZCMP(ctx); + + uint32_t reg_bitmap = decode_push_pop_list(ctx, a->urlist); + if (reg_bitmap == 0) { + return false; + } + + MemOp memop = get_ol(ctx) == MXL_RV32 ? MO_TEUL : MO_TEUQ; + int reg_size = memop_size(memop); + target_ulong stack_adj = ROUND_UP(ctpop32(reg_bitmap) * reg_size, 16) + + a->spimm; + TCGv sp = dest_gpr(ctx, xSP); + TCGv addr = tcg_temp_new(); + int i; + + tcg_gen_addi_tl(addr, sp, stack_adj - reg_size); + + for (i = X_Sn + 11; i >= 0; i--) { + if (reg_bitmap & (1 << i)) { + TCGv dest = dest_gpr(ctx, i); + tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, memop); + gen_set_gpr(ctx, i, dest); + tcg_gen_subi_tl(addr, addr, reg_size); + } + } + + tcg_gen_addi_tl(sp, sp, stack_adj); + gen_set_gpr(ctx, xSP, sp); + + if (ret_val) { + gen_set_gpr(ctx, xA0, ctx->zero); + } + + if (ret) { + TCGv ret_addr = get_gpr(ctx, xRA, EXT_SIGN); + tcg_gen_mov_tl(cpu_pc, ret_addr); + tcg_gen_lookup_and_goto_ptr(); + ctx->base.is_jmp = DISAS_NORETURN; + } + + return true; +} + +static bool trans_cm_push(DisasContext *ctx, arg_cm_push *a) +{ + REQUIRE_ZCMP(ctx); + + uint32_t reg_bitmap = decode_push_pop_list(ctx, a->urlist); + if (reg_bitmap == 0) { + return false; + } + + MemOp memop = get_ol(ctx) == MXL_RV32 ? MO_TEUL : MO_TEUQ; + int reg_size = memop_size(memop); + target_ulong stack_adj = ROUND_UP(ctpop32(reg_bitmap) * reg_size, 16) + + a->spimm; + TCGv sp = dest_gpr(ctx, xSP); + TCGv addr = tcg_temp_new(); + int i; + + tcg_gen_subi_tl(addr, sp, reg_size); + + for (i = X_Sn + 11; i >= 0; i--) { + if (reg_bitmap & (1 << i)) { + TCGv val = get_gpr(ctx, i, EXT_NONE); + tcg_gen_qemu_st_tl(val, addr, ctx->mem_idx, memop); + tcg_gen_subi_tl(addr, addr, reg_size); + } + } + + tcg_gen_subi_tl(sp, sp, stack_adj); + gen_set_gpr(ctx, xSP, sp); + + return true; +} + +static bool trans_cm_pop(DisasContext *ctx, arg_cm_pop *a) +{ + return gen_pop(ctx, a, false, false); +} + +static bool trans_cm_popret(DisasContext *ctx, arg_cm_popret *a) +{ + return gen_pop(ctx, a, true, false); +} + +static bool trans_cm_popretz(DisasContext *ctx, arg_cm_popret *a) +{ + return gen_pop(ctx, a, true, true); +} + +static bool trans_cm_mva01s(DisasContext *ctx, arg_cm_mva01s *a) +{ + REQUIRE_ZCMP(ctx); + + TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); + TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); + + gen_set_gpr(ctx, xA0, src1); + gen_set_gpr(ctx, xA1, src2); + + return true; +} + +static bool trans_cm_mvsa01(DisasContext *ctx, arg_cm_mvsa01 *a) +{ + REQUIRE_ZCMP(ctx); + + if (a->rs1 == a->rs2) { + return false; + } + + TCGv a0 = get_gpr(ctx, xA0, EXT_NONE); + TCGv a1 = get_gpr(ctx, xA1, EXT_NONE); + + gen_set_gpr(ctx, a->rs1, a0); + gen_set_gpr(ctx, a->rs2, a1); + + return true; +} + +static bool trans_cm_jalt(DisasContext *ctx, arg_cm_jalt *a) +{ + REQUIRE_ZCMT(ctx); + + TCGv addr = tcg_temp_new(); + + /* + * Update pc to current for the non-unwinding exception + * that might come from cpu_ld*_code() in the helper. + */ + gen_update_pc(ctx, 0); + gen_helper_cm_jalt(addr, tcg_env, tcg_constant_i32(a->index)); + + /* c.jt vs c.jalt depends on the index. */ + if (a->index >= 32) { + TCGv succ_pc = dest_gpr(ctx, xRA); + gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); + gen_set_gpr(ctx, xRA, succ_pc); + } + + tcg_gen_mov_tl(cpu_pc, addr); + + tcg_gen_lookup_and_goto_ptr(); + ctx->base.is_jmp = DISAS_NORETURN; + return true; +} diff --git a/target/riscv/insn_trans/trans_rvzfa.c.inc b/target/riscv/insn_trans/trans_rvzfa.c.inc new file mode 100644 index 0000000000..fd7e2daebf --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzfa.c.inc @@ -0,0 +1,521 @@ +/* + * RISC-V translation routines for the Zfa Standard Extension. + * + * Copyright (c) 2023 Christoph Müllner, christoph.muellner@vrull.eu + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#define REQUIRE_ZFA(ctx) do { \ + if (!ctx->cfg_ptr->ext_zfa) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_ZFH(ctx) do { \ + if (!ctx->cfg_ptr->ext_zfh) { \ + return false; \ + } \ +} while (0) + +static bool trans_fli_s(DisasContext *ctx, arg_fli_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVF); + + /* Values below are NaN-boxed to avoid a gen_nanbox_s(). */ + static const uint64_t fli_s_table[] = { + 0xffffffffbf800000, /* -1.0 */ + 0xffffffff00800000, /* minimum positive normal */ + 0xffffffff37800000, /* 1.0 * 2^-16 */ + 0xffffffff38000000, /* 1.0 * 2^-15 */ + 0xffffffff3b800000, /* 1.0 * 2^-8 */ + 0xffffffff3c000000, /* 1.0 * 2^-7 */ + 0xffffffff3d800000, /* 1.0 * 2^-4 */ + 0xffffffff3e000000, /* 1.0 * 2^-3 */ + 0xffffffff3e800000, /* 0.25 */ + 0xffffffff3ea00000, /* 0.3125 */ + 0xffffffff3ec00000, /* 0.375 */ + 0xffffffff3ee00000, /* 0.4375 */ + 0xffffffff3f000000, /* 0.5 */ + 0xffffffff3f200000, /* 0.625 */ + 0xffffffff3f400000, /* 0.75 */ + 0xffffffff3f600000, /* 0.875 */ + 0xffffffff3f800000, /* 1.0 */ + 0xffffffff3fa00000, /* 1.25 */ + 0xffffffff3fc00000, /* 1.5 */ + 0xffffffff3fe00000, /* 1.75 */ + 0xffffffff40000000, /* 2.0 */ + 0xffffffff40200000, /* 2.5 */ + 0xffffffff40400000, /* 3 */ + 0xffffffff40800000, /* 4 */ + 0xffffffff41000000, /* 8 */ + 0xffffffff41800000, /* 16 */ + 0xffffffff43000000, /* 2^7 */ + 0xffffffff43800000, /* 2^8 */ + 0xffffffff47000000, /* 2^15 */ + 0xffffffff47800000, /* 2^16 */ + 0xffffffff7f800000, /* +inf */ + 0xffffffff7fc00000, /* Canonical NaN */ + }; + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + tcg_gen_movi_i64(dest, fli_s_table[a->rs1]); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fli_d(DisasContext *ctx, arg_fli_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + static const uint64_t fli_d_table[] = { + 0xbff0000000000000, /* -1.0 */ + 0x0010000000000000, /* minimum positive normal */ + 0x3ef0000000000000, /* 1.0 * 2^-16 */ + 0x3f00000000000000, /* 1.0 * 2^-15 */ + 0x3f70000000000000, /* 1.0 * 2^-8 */ + 0x3f80000000000000, /* 1.0 * 2^-7 */ + 0x3fb0000000000000, /* 1.0 * 2^-4 */ + 0x3fc0000000000000, /* 1.0 * 2^-3 */ + 0x3fd0000000000000, /* 0.25 */ + 0x3fd4000000000000, /* 0.3125 */ + 0x3fd8000000000000, /* 0.375 */ + 0x3fdc000000000000, /* 0.4375 */ + 0x3fe0000000000000, /* 0.5 */ + 0x3fe4000000000000, /* 0.625 */ + 0x3fe8000000000000, /* 0.75 */ + 0x3fec000000000000, /* 0.875 */ + 0x3ff0000000000000, /* 1.0 */ + 0x3ff4000000000000, /* 1.25 */ + 0x3ff8000000000000, /* 1.5 */ + 0x3ffc000000000000, /* 1.75 */ + 0x4000000000000000, /* 2.0 */ + 0x4004000000000000, /* 2.5 */ + 0x4008000000000000, /* 3 */ + 0x4010000000000000, /* 4 */ + 0x4020000000000000, /* 8 */ + 0x4030000000000000, /* 16 */ + 0x4060000000000000, /* 2^7 */ + 0x4070000000000000, /* 2^8 */ + 0x40e0000000000000, /* 2^15 */ + 0x40f0000000000000, /* 2^16 */ + 0x7ff0000000000000, /* +inf */ + 0x7ff8000000000000, /* Canonical NaN */ + }; + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + tcg_gen_movi_i64(dest, fli_d_table[a->rs1]); + gen_set_fpr_d(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fli_h(DisasContext *ctx, arg_fli_h *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_ZFH(ctx); + + /* Values below are NaN-boxed to avoid a gen_nanbox_h(). */ + static const uint64_t fli_h_table[] = { + 0xffffffffffffbc00, /* -1.0 */ + 0xffffffffffff0400, /* minimum positive normal */ + 0xffffffffffff0100, /* 1.0 * 2^-16 */ + 0xffffffffffff0200, /* 1.0 * 2^-15 */ + 0xffffffffffff1c00, /* 1.0 * 2^-8 */ + 0xffffffffffff2000, /* 1.0 * 2^-7 */ + 0xffffffffffff2c00, /* 1.0 * 2^-4 */ + 0xffffffffffff3000, /* 1.0 * 2^-3 */ + 0xffffffffffff3400, /* 0.25 */ + 0xffffffffffff3500, /* 0.3125 */ + 0xffffffffffff3600, /* 0.375 */ + 0xffffffffffff3700, /* 0.4375 */ + 0xffffffffffff3800, /* 0.5 */ + 0xffffffffffff3900, /* 0.625 */ + 0xffffffffffff3a00, /* 0.75 */ + 0xffffffffffff3b00, /* 0.875 */ + 0xffffffffffff3c00, /* 1.0 */ + 0xffffffffffff3d00, /* 1.25 */ + 0xffffffffffff3e00, /* 1.5 */ + 0xffffffffffff3f00, /* 1.75 */ + 0xffffffffffff4000, /* 2.0 */ + 0xffffffffffff4100, /* 2.5 */ + 0xffffffffffff4200, /* 3 */ + 0xffffffffffff4400, /* 4 */ + 0xffffffffffff4800, /* 8 */ + 0xffffffffffff4c00, /* 16 */ + 0xffffffffffff5800, /* 2^7 */ + 0xffffffffffff5c00, /* 2^8 */ + 0xffffffffffff7800, /* 2^15 */ + 0xffffffffffff7c00, /* 2^16 */ + 0xffffffffffff7c00, /* +inf */ + 0xffffffffffff7e00, /* Canonical NaN */ + }; + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + tcg_gen_movi_i64(dest, fli_h_table[a->rs1]); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fminm_s(DisasContext *ctx, arg_fminm_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVF); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fminm_s(dest, tcg_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmaxm_s(DisasContext *ctx, arg_fmaxm_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVF); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fmaxm_s(dest, tcg_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fminm_d(DisasContext *ctx, arg_fminm_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); + + gen_helper_fminm_d(dest, tcg_env, src1, src2); + gen_set_fpr_d(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmaxm_d(DisasContext *ctx, arg_fmaxm_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); + + gen_helper_fmaxm_d(dest, tcg_env, src1, src2); + gen_set_fpr_d(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fminm_h(DisasContext *ctx, arg_fminm_h *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fminm_h(dest, tcg_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmaxm_h(DisasContext *ctx, arg_fmaxm_h *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fmaxm_h(dest, tcg_env, src1, src2); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fround_s(DisasContext *ctx, arg_fround_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVF); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fround_s(dest, tcg_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_froundnx_s(DisasContext *ctx, arg_froundnx_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVF); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_froundnx_s(dest, tcg_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fround_d(DisasContext *ctx, arg_fround_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fround_d(dest, tcg_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_froundnx_d(DisasContext *ctx, arg_froundnx_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_froundnx_d(dest, tcg_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fround_h(DisasContext *ctx, arg_fround_h *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fround_h(dest, tcg_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_froundnx_h(DisasContext *ctx, arg_froundnx_h *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_ZFH(ctx); + + TCGv_i64 dest = dest_fpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_froundnx_h(dest, tcg_env, src1); + gen_set_fpr_hs(ctx, a->rd, dest); + + mark_fs_dirty(ctx); + return true; +} + +bool trans_fcvtmod_w_d(DisasContext *ctx, arg_fcvtmod_w_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + TCGv dst = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); + TCGv_i64 t1 = tcg_temp_new_i64(); + + /* Rounding mode is RTZ. */ + gen_set_rm(ctx, RISCV_FRM_RTZ); + gen_helper_fcvtmod_w_d(t1, tcg_env, src1); + tcg_gen_trunc_i64_tl(dst, t1); + gen_set_gpr(ctx, a->rd, dst); + + return true; +} + +bool trans_fmvh_x_d(DisasContext *ctx, arg_fmvh_x_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + REQUIRE_32BIT(ctx); + + TCGv dst = dest_gpr(ctx, a->rd); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_sari_i64(t1, cpu_fpr[a->rs1], 32); + tcg_gen_trunc_i64_tl(dst, t1); + gen_set_gpr(ctx, a->rd, dst); + return true; +} + +bool trans_fmvp_d_x(DisasContext *ctx, arg_fmvp_d_x *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + REQUIRE_32BIT(ctx); + + TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); + TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); + tcg_gen_concat_tl_i64(cpu_fpr[a->rd], src1, src2); + + mark_fs_dirty(ctx); + return true; +} + +bool trans_fleq_s(DisasContext *ctx, arg_fleq_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVF); + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fleq_s(dest, tcg_env, src1, src2); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +bool trans_fltq_s(DisasContext *ctx, arg_fltq_s *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVF); + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fltq_s(dest, tcg_env, src1, src2); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +bool trans_fleq_d(DisasContext *ctx, arg_fleq_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fleq_d(dest, tcg_env, src1, src2); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +bool trans_fltq_d(DisasContext *ctx, arg_fltq_d *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_EXT(ctx, RVD); + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fltq_d(dest, tcg_env, src1, src2); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +bool trans_fleq_h(DisasContext *ctx, arg_fleq_h *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_ZFH(ctx); + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fleq_h(dest, tcg_env, src1, src2); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +bool trans_fltq_h(DisasContext *ctx, arg_fltq_h *a) +{ + REQUIRE_FPU; + REQUIRE_ZFA(ctx); + REQUIRE_ZFH(ctx); + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); + TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); + + gen_helper_fltq_h(dest, tcg_env, src1, src2); + gen_set_gpr(ctx, a->rd, dest); + return true; +} diff --git a/target/riscv/insn_trans/trans_rvzfh.c.inc b/target/riscv/insn_trans/trans_rvzfh.c.inc index 5d07150cd0..1eb458b491 100644 --- a/target/riscv/insn_trans/trans_rvzfh.c.inc +++ b/target/riscv/insn_trans/trans_rvzfh.c.inc @@ -28,15 +28,14 @@ } \ } while (0) -#define REQUIRE_ZFH_OR_ZFHMIN(ctx) do { \ - if (!(ctx->cfg_ptr->ext_zfh || ctx->cfg_ptr->ext_zfhmin)) { \ +#define REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx) do { \ + if (!ctx->cfg_ptr->ext_zfhmin && !ctx->cfg_ptr->ext_zfbfmin) { \ return false; \ } \ } while (0) -#define REQUIRE_ZFH_OR_ZFHMIN_OR_ZHINX_OR_ZHINXMIN(ctx) do { \ - if (!(ctx->cfg_ptr->ext_zfh || ctx->cfg_ptr->ext_zfhmin || \ - ctx->cfg_ptr->ext_zhinx || ctx->cfg_ptr->ext_zhinxmin)) { \ +#define REQUIRE_ZFHMIN_OR_ZHINXMIN(ctx) do { \ + if (!(ctx->cfg_ptr->ext_zfhmin || ctx->cfg_ptr->ext_zhinxmin)) { \ return false; \ } \ } while (0) @@ -47,11 +46,12 @@ static bool trans_flh(DisasContext *ctx, arg_flh *a) TCGv t0; REQUIRE_FPU; - REQUIRE_ZFH_OR_ZFHMIN(ctx); + REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); + decode_save_opc(ctx); t0 = get_gpr(ctx, a->rs1, EXT_NONE); if (a->imm) { - TCGv temp = temp_new(ctx); + TCGv temp = tcg_temp_new(); tcg_gen_addi_tl(temp, t0, a->imm); t0 = temp; } @@ -69,8 +69,9 @@ static bool trans_fsh(DisasContext *ctx, arg_fsh *a) TCGv t0; REQUIRE_FPU; - REQUIRE_ZFH_OR_ZFHMIN(ctx); + REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); + decode_save_opc(ctx); t0 = get_gpr(ctx, a->rs1, EXT_NONE); if (a->imm) { TCGv temp = tcg_temp_new(); @@ -94,7 +95,7 @@ static bool trans_fmadd_h(DisasContext *ctx, arg_fmadd_h *a) TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fmadd_h(dest, cpu_env, src1, src2, src3); + gen_helper_fmadd_h(dest, tcg_env, src1, src2, src3); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -111,7 +112,7 @@ static bool trans_fmsub_h(DisasContext *ctx, arg_fmsub_h *a) TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fmsub_h(dest, cpu_env, src1, src2, src3); + gen_helper_fmsub_h(dest, tcg_env, src1, src2, src3); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -128,7 +129,7 @@ static bool trans_fnmsub_h(DisasContext *ctx, arg_fnmsub_h *a) TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fnmsub_h(dest, cpu_env, src1, src2, src3); + gen_helper_fnmsub_h(dest, tcg_env, src1, src2, src3); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -145,7 +146,7 @@ static bool trans_fnmadd_h(DisasContext *ctx, arg_fnmadd_h *a) TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3); gen_set_rm(ctx, a->rm); - gen_helper_fnmadd_h(dest, cpu_env, src1, src2, src3); + gen_helper_fnmadd_h(dest, tcg_env, src1, src2, src3); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -161,7 +162,7 @@ static bool trans_fadd_h(DisasContext *ctx, arg_fadd_h *a) TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fadd_h(dest, cpu_env, src1, src2); + gen_helper_fadd_h(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -177,7 +178,7 @@ static bool trans_fsub_h(DisasContext *ctx, arg_fsub_h *a) TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fsub_h(dest, cpu_env, src1, src2); + gen_helper_fsub_h(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -193,7 +194,7 @@ static bool trans_fmul_h(DisasContext *ctx, arg_fmul_h *a) TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fmul_h(dest, cpu_env, src1, src2); + gen_helper_fmul_h(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -209,7 +210,7 @@ static bool trans_fdiv_h(DisasContext *ctx, arg_fdiv_h *a) TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); gen_set_rm(ctx, a->rm); - gen_helper_fdiv_h(dest, cpu_env, src1, src2); + gen_helper_fdiv_h(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -224,7 +225,7 @@ static bool trans_fsqrt_h(DisasContext *ctx, arg_fsqrt_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fsqrt_h(dest, cpu_env, src1); + gen_helper_fsqrt_h(dest, tcg_env, src1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -255,9 +256,6 @@ static bool trans_fsgnj_h(DisasContext *ctx, arg_fsgnj_h *a) /* This formulation retains the nanboxing of rs2 in normal 'Zfh'. */ tcg_gen_deposit_i64(dest, rs2, rs1, 0, 15); - - tcg_temp_free_i64(rs1); - tcg_temp_free_i64(rs2); } else { tcg_gen_deposit_i64(dest, src2, src1, 0, 15); tcg_gen_ext16s_i64(dest, dest); @@ -301,20 +299,16 @@ static bool trans_fsgnjn_h(DisasContext *ctx, arg_fsgnjn_h *a) * Replace bit 15 in rs1 with inverse in rs2. * This formulation retains the nanboxing of rs1. */ - mask = tcg_const_i64(~MAKE_64BIT_MASK(15, 1)); + mask = tcg_constant_i64(~MAKE_64BIT_MASK(15, 1)); tcg_gen_not_i64(rs2, rs2); tcg_gen_andc_i64(rs2, rs2, mask); tcg_gen_and_i64(dest, mask, rs1); tcg_gen_or_i64(dest, dest, rs2); - - tcg_temp_free_i64(mask); - tcg_temp_free_i64(rs2); } - /* signed-extended intead of nanboxing for result if enable zfinx */ + /* signed-extended instead of nanboxing for result if enable zfinx */ if (ctx->cfg_ptr->ext_zfinx) { tcg_gen_ext16s_i64(dest, dest); } - tcg_temp_free_i64(rs1); mark_fs_dirty(ctx); return true; } @@ -354,14 +348,11 @@ static bool trans_fsgnjx_h(DisasContext *ctx, arg_fsgnjx_h *a) */ tcg_gen_andi_i64(dest, rs2, MAKE_64BIT_MASK(15, 1)); tcg_gen_xor_i64(dest, rs1, dest); - - tcg_temp_free_i64(rs2); } - /* signed-extended intead of nanboxing for result if enable zfinx */ + /* signed-extended instead of nanboxing for result if enable zfinx */ if (ctx->cfg_ptr->ext_zfinx) { tcg_gen_ext16s_i64(dest, dest); } - tcg_temp_free_i64(rs1); mark_fs_dirty(ctx); return true; } @@ -375,7 +366,7 @@ static bool trans_fmin_h(DisasContext *ctx, arg_fmin_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fmin_h(dest, cpu_env, src1, src2); + gen_helper_fmin_h(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -390,7 +381,7 @@ static bool trans_fmax_h(DisasContext *ctx, arg_fmax_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fmax_h(dest, cpu_env, src1, src2); + gen_helper_fmax_h(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -399,13 +390,13 @@ static bool trans_fmax_h(DisasContext *ctx, arg_fmax_h *a) static bool trans_fcvt_s_h(DisasContext *ctx, arg_fcvt_s_h *a) { REQUIRE_FPU; - REQUIRE_ZFH_OR_ZFHMIN_OR_ZHINX_OR_ZHINXMIN(ctx); + REQUIRE_ZFHMIN_OR_ZHINXMIN(ctx); TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_s_h(dest, cpu_env, src1); + gen_helper_fcvt_s_h(dest, tcg_env, src1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -416,14 +407,14 @@ static bool trans_fcvt_s_h(DisasContext *ctx, arg_fcvt_s_h *a) static bool trans_fcvt_d_h(DisasContext *ctx, arg_fcvt_d_h *a) { REQUIRE_FPU; - REQUIRE_ZFH_OR_ZFHMIN_OR_ZHINX_OR_ZHINXMIN(ctx); + REQUIRE_ZFHMIN_OR_ZHINXMIN(ctx); REQUIRE_ZDINX_OR_D(ctx); TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_d_h(dest, cpu_env, src1); + gen_helper_fcvt_d_h(dest, tcg_env, src1); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -434,13 +425,13 @@ static bool trans_fcvt_d_h(DisasContext *ctx, arg_fcvt_d_h *a) static bool trans_fcvt_h_s(DisasContext *ctx, arg_fcvt_h_s *a) { REQUIRE_FPU; - REQUIRE_ZFH_OR_ZFHMIN_OR_ZHINX_OR_ZHINXMIN(ctx); + REQUIRE_ZFHMIN_OR_ZHINXMIN(ctx); TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_h_s(dest, cpu_env, src1); + gen_helper_fcvt_h_s(dest, tcg_env, src1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -450,14 +441,14 @@ static bool trans_fcvt_h_s(DisasContext *ctx, arg_fcvt_h_s *a) static bool trans_fcvt_h_d(DisasContext *ctx, arg_fcvt_h_d *a) { REQUIRE_FPU; - REQUIRE_ZFH_OR_ZFHMIN_OR_ZHINX_OR_ZHINXMIN(ctx); + REQUIRE_ZFHMIN_OR_ZHINXMIN(ctx); REQUIRE_ZDINX_OR_D(ctx); TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_h_d(dest, cpu_env, src1); + gen_helper_fcvt_h_d(dest, tcg_env, src1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -473,7 +464,7 @@ static bool trans_feq_h(DisasContext *ctx, arg_feq_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_feq_h(dest, cpu_env, src1, src2); + gen_helper_feq_h(dest, tcg_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -487,7 +478,7 @@ static bool trans_flt_h(DisasContext *ctx, arg_flt_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_flt_h(dest, cpu_env, src1, src2); + gen_helper_flt_h(dest, tcg_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; @@ -502,7 +493,7 @@ static bool trans_fle_h(DisasContext *ctx, arg_fle_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fle_h(dest, cpu_env, src1, src2); + gen_helper_fle_h(dest, tcg_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -515,7 +506,7 @@ static bool trans_fclass_h(DisasContext *ctx, arg_fclass_h *a) TCGv dest = dest_gpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); - gen_helper_fclass_h(dest, cpu_env, src1); + gen_helper_fclass_h(dest, tcg_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -529,7 +520,7 @@ static bool trans_fcvt_w_h(DisasContext *ctx, arg_fcvt_w_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_w_h(dest, cpu_env, src1); + gen_helper_fcvt_w_h(dest, tcg_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -543,7 +534,7 @@ static bool trans_fcvt_wu_h(DisasContext *ctx, arg_fcvt_wu_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_wu_h(dest, cpu_env, src1); + gen_helper_fcvt_wu_h(dest, tcg_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -557,7 +548,7 @@ static bool trans_fcvt_h_w(DisasContext *ctx, arg_fcvt_h_w *a) TCGv t0 = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_h_w(dest, cpu_env, t0); + gen_helper_fcvt_h_w(dest, tcg_env, t0); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -573,7 +564,7 @@ static bool trans_fcvt_h_wu(DisasContext *ctx, arg_fcvt_h_wu *a) TCGv t0 = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_h_wu(dest, cpu_env, t0); + gen_helper_fcvt_h_wu(dest, tcg_env, t0); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -583,7 +574,7 @@ static bool trans_fcvt_h_wu(DisasContext *ctx, arg_fcvt_h_wu *a) static bool trans_fmv_x_h(DisasContext *ctx, arg_fmv_x_h *a) { REQUIRE_FPU; - REQUIRE_ZFH_OR_ZFHMIN(ctx); + REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); TCGv dest = dest_gpr(ctx, a->rd); @@ -603,7 +594,7 @@ static bool trans_fmv_x_h(DisasContext *ctx, arg_fmv_x_h *a) static bool trans_fmv_h_x(DisasContext *ctx, arg_fmv_h_x *a) { REQUIRE_FPU; - REQUIRE_ZFH_OR_ZFHMIN(ctx); + REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); TCGv t0 = get_gpr(ctx, a->rs1, EXT_ZERO); @@ -624,7 +615,7 @@ static bool trans_fcvt_l_h(DisasContext *ctx, arg_fcvt_l_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_l_h(dest, cpu_env, src1); + gen_helper_fcvt_l_h(dest, tcg_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -639,7 +630,7 @@ static bool trans_fcvt_lu_h(DisasContext *ctx, arg_fcvt_lu_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_lu_h(dest, cpu_env, src1); + gen_helper_fcvt_lu_h(dest, tcg_env, src1); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -654,7 +645,7 @@ static bool trans_fcvt_h_l(DisasContext *ctx, arg_fcvt_h_l *a) TCGv t0 = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_h_l(dest, cpu_env, t0); + gen_helper_fcvt_h_l(dest, tcg_env, t0); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -671,7 +662,7 @@ static bool trans_fcvt_h_lu(DisasContext *ctx, arg_fcvt_h_lu *a) TCGv t0 = get_gpr(ctx, a->rs1, EXT_SIGN); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_h_lu(dest, cpu_env, t0); + gen_helper_fcvt_h_lu(dest, tcg_env, t0); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); diff --git a/target/riscv/insn_trans/trans_rvzicbo.c.inc b/target/riscv/insn_trans/trans_rvzicbo.c.inc new file mode 100644 index 0000000000..d5d7095903 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzicbo.c.inc @@ -0,0 +1,57 @@ +/* + * RISC-V translation routines for the RISC-V CBO Extension. + * + * Copyright (c) 2021 Philipp Tomsich, philipp.tomsich@vrull.eu + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#define REQUIRE_ZICBOM(ctx) do { \ + if (!ctx->cfg_ptr->ext_zicbom) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_ZICBOZ(ctx) do { \ + if (!ctx->cfg_ptr->ext_zicboz) { \ + return false; \ + } \ +} while (0) + +static bool trans_cbo_clean(DisasContext *ctx, arg_cbo_clean *a) +{ + REQUIRE_ZICBOM(ctx); + gen_helper_cbo_clean_flush(tcg_env, cpu_gpr[a->rs1]); + return true; +} + +static bool trans_cbo_flush(DisasContext *ctx, arg_cbo_flush *a) +{ + REQUIRE_ZICBOM(ctx); + gen_helper_cbo_clean_flush(tcg_env, cpu_gpr[a->rs1]); + return true; +} + +static bool trans_cbo_inval(DisasContext *ctx, arg_cbo_inval *a) +{ + REQUIRE_ZICBOM(ctx); + gen_helper_cbo_inval(tcg_env, cpu_gpr[a->rs1]); + return true; +} + +static bool trans_cbo_zero(DisasContext *ctx, arg_cbo_zero *a) +{ + REQUIRE_ZICBOZ(ctx); + gen_helper_cbo_zero(tcg_env, cpu_gpr[a->rs1]); + return true; +} diff --git a/target/riscv/insn_trans/trans_rvzicond.c.inc b/target/riscv/insn_trans/trans_rvzicond.c.inc new file mode 100644 index 0000000000..c8e43fa325 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzicond.c.inc @@ -0,0 +1,55 @@ +/* + * RISC-V translation routines for the Zicond Standard Extension. + * + * Copyright (c) 2020-2023 PLCT Lab + * Copyright (c) 2022 VRULL GmbH. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#define REQUIRE_ZICOND(ctx) do { \ + if (!ctx->cfg_ptr->ext_zicond) { \ + return false; \ + } \ +} while (0) + +/* Emits "$rd = ($rs2 $zero) ? $zero : $rs1" */ +static void gen_czero(TCGv dest, TCGv src1, TCGv src2, TCGCond cond) +{ + TCGv zero = tcg_constant_tl(0); + tcg_gen_movcond_tl(cond, dest, src2, zero, zero, src1); +} + +static void gen_czero_eqz(TCGv dest, TCGv src1, TCGv src2) +{ + gen_czero(dest, src1, src2, TCG_COND_EQ); +} + +static void gen_czero_nez(TCGv dest, TCGv src1, TCGv src2) +{ + gen_czero(dest, src1, src2, TCG_COND_NE); +} + +static bool trans_czero_eqz(DisasContext *ctx, arg_r *a) +{ + REQUIRE_ZICOND(ctx); + + return gen_logic(ctx, a, gen_czero_eqz); +} + +static bool trans_czero_nez(DisasContext *ctx, arg_r *a) +{ + REQUIRE_ZICOND(ctx); + + return gen_logic(ctx, a, gen_czero_nez); +} diff --git a/target/riscv/insn_trans/trans_svinval.c.inc b/target/riscv/insn_trans/trans_svinval.c.inc index 2682bd969f..0f692a1088 100644 --- a/target/riscv/insn_trans/trans_svinval.c.inc +++ b/target/riscv/insn_trans/trans_svinval.c.inc @@ -28,7 +28,8 @@ static bool trans_sinval_vma(DisasContext *ctx, arg_sinval_vma *a) /* Do the same as sfence.vma currently */ REQUIRE_EXT(ctx, RVS); #ifndef CONFIG_USER_ONLY - gen_helper_tlb_flush(cpu_env); + decode_save_opc(ctx); + gen_helper_tlb_flush(tcg_env); return true; #endif return false; @@ -56,7 +57,8 @@ static bool trans_hinval_vvma(DisasContext *ctx, arg_hinval_vvma *a) /* Do the same as hfence.vvma currently */ REQUIRE_EXT(ctx, RVH); #ifndef CONFIG_USER_ONLY - gen_helper_hyp_tlb_flush(cpu_env); + decode_save_opc(ctx); + gen_helper_hyp_tlb_flush(tcg_env); return true; #endif return false; @@ -68,7 +70,8 @@ static bool trans_hinval_gvma(DisasContext *ctx, arg_hinval_gvma *a) /* Do the same as hfence.gvma currently */ REQUIRE_EXT(ctx, RVH); #ifndef CONFIG_USER_ONLY - gen_helper_hyp_gvma_tlb_flush(cpu_env); + decode_save_opc(ctx); + gen_helper_hyp_gvma_tlb_flush(tcg_env); return true; #endif return false; diff --git a/target/riscv/insn_trans/trans_xthead.c.inc b/target/riscv/insn_trans/trans_xthead.c.inc new file mode 100644 index 0000000000..22488412d4 --- /dev/null +++ b/target/riscv/insn_trans/trans_xthead.c.inc @@ -0,0 +1,1046 @@ +/* + * RISC-V translation routines for the T-Head vendor extensions (xthead*). + * + * Copyright (c) 2022 VRULL GmbH. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#define REQUIRE_XTHEADBA(ctx) do { \ + if (!ctx->cfg_ptr->ext_xtheadba) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_XTHEADBB(ctx) do { \ + if (!ctx->cfg_ptr->ext_xtheadbb) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_XTHEADBS(ctx) do { \ + if (!ctx->cfg_ptr->ext_xtheadbs) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_XTHEADCMO(ctx) do { \ + if (!ctx->cfg_ptr->ext_xtheadcmo) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_XTHEADCONDMOV(ctx) do { \ + if (!ctx->cfg_ptr->ext_xtheadcondmov) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_XTHEADFMEMIDX(ctx) do { \ + if (!ctx->cfg_ptr->ext_xtheadfmemidx) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_XTHEADFMV(ctx) do { \ + if (!ctx->cfg_ptr->ext_xtheadfmv) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_XTHEADMAC(ctx) do { \ + if (!ctx->cfg_ptr->ext_xtheadmac) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_XTHEADMEMIDX(ctx) do { \ + if (!ctx->cfg_ptr->ext_xtheadmemidx) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_XTHEADMEMPAIR(ctx) do { \ + if (!ctx->cfg_ptr->ext_xtheadmempair) { \ + return false; \ + } \ +} while (0) + +#define REQUIRE_XTHEADSYNC(ctx) do { \ + if (!ctx->cfg_ptr->ext_xtheadsync) { \ + return false; \ + } \ +} while (0) + +/* + * Calculate and return the address for indexed mem operations: + * If !zext_offs, then the address is rs1 + (rs2 << imm2). + * If zext_offs, then the address is rs1 + (zext(rs2[31:0]) << imm2). + */ +static TCGv get_th_address_indexed(DisasContext *ctx, int rs1, int rs2, + int imm2, bool zext_offs) +{ + TCGv src2 = get_gpr(ctx, rs2, EXT_NONE); + TCGv offs = tcg_temp_new(); + + if (zext_offs) { + tcg_gen_extract_tl(offs, src2, 0, 32); + tcg_gen_shli_tl(offs, offs, imm2); + } else { + tcg_gen_shli_tl(offs, src2, imm2); + } + + return get_address_indexed(ctx, rs1, offs); +} + +/* XTheadBa */ + +/* + * th.addsl is similar to sh[123]add (from Zba), but not an + * alternative encoding: while sh[123] applies the shift to rs1, + * th.addsl shifts rs2. + */ + +#define GEN_TH_ADDSL(SHAMT) \ +static void gen_th_addsl##SHAMT(TCGv ret, TCGv arg1, TCGv arg2) \ +{ \ + TCGv t = tcg_temp_new(); \ + tcg_gen_shli_tl(t, arg2, SHAMT); \ + tcg_gen_add_tl(ret, t, arg1); \ +} + +GEN_TH_ADDSL(1) +GEN_TH_ADDSL(2) +GEN_TH_ADDSL(3) + +#define GEN_TRANS_TH_ADDSL(SHAMT) \ +static bool trans_th_addsl##SHAMT(DisasContext *ctx, \ + arg_th_addsl##SHAMT * a) \ +{ \ + REQUIRE_XTHEADBA(ctx); \ + return gen_arith(ctx, a, EXT_NONE, gen_th_addsl##SHAMT, NULL); \ +} + +GEN_TRANS_TH_ADDSL(1) +GEN_TRANS_TH_ADDSL(2) +GEN_TRANS_TH_ADDSL(3) + +/* XTheadBb */ + +/* th.srri is an alternate encoding for rori (from Zbb) */ +static bool trans_th_srri(DisasContext *ctx, arg_th_srri * a) +{ + REQUIRE_XTHEADBB(ctx); + return gen_shift_imm_fn_per_ol(ctx, a, EXT_NONE, + tcg_gen_rotri_tl, gen_roriw, NULL); +} + +/* th.srriw is an alternate encoding for roriw (from Zbb) */ +static bool trans_th_srriw(DisasContext *ctx, arg_th_srriw *a) +{ + REQUIRE_XTHEADBB(ctx); + REQUIRE_64BIT(ctx); + ctx->ol = MXL_RV32; + return gen_shift_imm_fn(ctx, a, EXT_NONE, gen_roriw, NULL); +} + +/* th.ext and th.extu perform signed/unsigned bitfield extraction */ +static bool gen_th_bfextract(DisasContext *ctx, arg_th_bfext *a, + void (*f)(TCGv, TCGv, unsigned int, unsigned int)) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv source = get_gpr(ctx, a->rs1, EXT_ZERO); + + if (a->lsb <= a->msb) { + f(dest, source, a->lsb, a->msb - a->lsb + 1); + gen_set_gpr(ctx, a->rd, dest); + } + return true; +} + +static bool trans_th_ext(DisasContext *ctx, arg_th_ext *a) +{ + REQUIRE_XTHEADBB(ctx); + return gen_th_bfextract(ctx, a, tcg_gen_sextract_tl); +} + +static bool trans_th_extu(DisasContext *ctx, arg_th_extu *a) +{ + REQUIRE_XTHEADBB(ctx); + return gen_th_bfextract(ctx, a, tcg_gen_extract_tl); +} + +/* th.ff0: find first zero (clz on an inverted input) */ +static bool gen_th_ff0(DisasContext *ctx, arg_th_ff0 *a, DisasExtend ext) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, ext); + + int olen = get_olen(ctx); + TCGv t = tcg_temp_new(); + + tcg_gen_not_tl(t, src1); + if (olen != TARGET_LONG_BITS) { + if (olen == 32) { + gen_clzw(dest, t); + } else { + g_assert_not_reached(); + } + } else { + gen_clz(dest, t); + } + + gen_set_gpr(ctx, a->rd, dest); + + return true; +} + +static bool trans_th_ff0(DisasContext *ctx, arg_th_ff0 *a) +{ + REQUIRE_XTHEADBB(ctx); + return gen_th_ff0(ctx, a, EXT_NONE); +} + +/* th.ff1 is an alternate encoding for clz (from Zbb) */ +static bool trans_th_ff1(DisasContext *ctx, arg_th_ff1 *a) +{ + REQUIRE_XTHEADBB(ctx); + return gen_unary_per_ol(ctx, a, EXT_NONE, gen_clz, gen_clzw); +} + +static void gen_th_revw(TCGv ret, TCGv arg1) +{ + tcg_gen_bswap32_tl(ret, arg1, TCG_BSWAP_OS); +} + +/* th.rev is an alternate encoding for the RV64 rev8 (from Zbb) */ +static bool trans_th_rev(DisasContext *ctx, arg_th_rev *a) +{ + REQUIRE_XTHEADBB(ctx); + + return gen_unary_per_ol(ctx, a, EXT_NONE, tcg_gen_bswap_tl, gen_th_revw); +} + +/* th.revw is a sign-extended byte-swap of the lower word */ +static bool trans_th_revw(DisasContext *ctx, arg_th_revw *a) +{ + REQUIRE_XTHEADBB(ctx); + REQUIRE_64BIT(ctx); + return gen_unary(ctx, a, EXT_NONE, gen_th_revw); +} + +/* th.tstnbz is equivalent to an orc.b (from Zbb) with inverted result */ +static void gen_th_tstnbz(TCGv ret, TCGv source1) +{ + gen_orc_b(ret, source1); + tcg_gen_not_tl(ret, ret); +} + +static bool trans_th_tstnbz(DisasContext *ctx, arg_th_tstnbz *a) +{ + REQUIRE_XTHEADBB(ctx); + return gen_unary(ctx, a, EXT_ZERO, gen_th_tstnbz); +} + +/* XTheadBs */ + +/* th.tst is an alternate encoding for bexti (from Zbs) */ +static bool trans_th_tst(DisasContext *ctx, arg_th_tst *a) +{ + REQUIRE_XTHEADBS(ctx); + return gen_shift_imm_tl(ctx, a, EXT_NONE, gen_bext); +} + +/* XTheadCmo */ + +/* Test if priv level is M, S, or U (cannot fail). */ +#define REQUIRE_PRIV_MSU(ctx) + +/* Test if priv level is M or S. */ +#define REQUIRE_PRIV_MS(ctx) \ +do { \ + if (ctx->priv == PRV_U) { \ + return false; \ + } \ +} while (0) + +#define NOP_PRIVCHECK(insn, extcheck, privcheck) \ +static bool trans_ ## insn(DisasContext *ctx, arg_ ## insn * a) \ +{ \ + (void) a; \ + extcheck(ctx); \ + privcheck(ctx); \ + return true; \ +} + +NOP_PRIVCHECK(th_dcache_call, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_dcache_ciall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_dcache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_dcache_cpa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_dcache_cipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_dcache_ipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_dcache_cva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) +NOP_PRIVCHECK(th_dcache_civa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) +NOP_PRIVCHECK(th_dcache_iva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) +NOP_PRIVCHECK(th_dcache_csw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_dcache_cisw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_dcache_isw, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_dcache_cpal1, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_dcache_cval1, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) + +NOP_PRIVCHECK(th_icache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_icache_ialls, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_icache_ipa, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_icache_iva, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MSU) + +NOP_PRIVCHECK(th_l2cache_call, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_l2cache_ciall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) +NOP_PRIVCHECK(th_l2cache_iall, REQUIRE_XTHEADCMO, REQUIRE_PRIV_MS) + +/* XTheadCondMov */ + +static bool gen_th_condmove(DisasContext *ctx, arg_r *a, TCGCond cond) +{ + TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); + TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); + TCGv old = get_gpr(ctx, a->rd, EXT_NONE); + TCGv dest = dest_gpr(ctx, a->rd); + + tcg_gen_movcond_tl(cond, dest, src2, ctx->zero, src1, old); + + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +/* th.mveqz: "if (rs2 == 0) rd = rs1;" */ +static bool trans_th_mveqz(DisasContext *ctx, arg_th_mveqz *a) +{ + REQUIRE_XTHEADCONDMOV(ctx); + return gen_th_condmove(ctx, a, TCG_COND_EQ); +} + +/* th.mvnez: "if (rs2 != 0) rd = rs1;" */ +static bool trans_th_mvnez(DisasContext *ctx, arg_th_mveqz *a) +{ + REQUIRE_XTHEADCONDMOV(ctx); + return gen_th_condmove(ctx, a, TCG_COND_NE); +} + +/* XTheadFMem */ + +/* + * Load 64-bit float from indexed address. + * If !zext_offs, then address is rs1 + (rs2 << imm2). + * If zext_offs, then address is rs1 + (zext(rs2[31:0]) << imm2). + */ +static bool gen_fload_idx(DisasContext *ctx, arg_th_memidx *a, MemOp memop, + bool zext_offs) +{ + TCGv_i64 rd = cpu_fpr[a->rd]; + TCGv addr = get_th_address_indexed(ctx, a->rs1, a->rs2, a->imm2, zext_offs); + + tcg_gen_qemu_ld_i64(rd, addr, ctx->mem_idx, memop); + if ((memop & MO_SIZE) == MO_32) { + gen_nanbox_s(rd, rd); + } + + mark_fs_dirty(ctx); + return true; +} + +/* + * Store 64-bit float to indexed address. + * If !zext_offs, then address is rs1 + (rs2 << imm2). + * If zext_offs, then address is rs1 + (zext(rs2[31:0]) << imm2). + */ +static bool gen_fstore_idx(DisasContext *ctx, arg_th_memidx *a, MemOp memop, + bool zext_offs) +{ + TCGv_i64 rd = cpu_fpr[a->rd]; + TCGv addr = get_th_address_indexed(ctx, a->rs1, a->rs2, a->imm2, zext_offs); + + tcg_gen_qemu_st_i64(rd, addr, ctx->mem_idx, memop); + + return true; +} + +static bool trans_th_flrd(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADFMEMIDX(ctx); + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + return gen_fload_idx(ctx, a, MO_TEUQ, false); +} + +static bool trans_th_flrw(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADFMEMIDX(ctx); + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + return gen_fload_idx(ctx, a, MO_TEUL, false); +} + +static bool trans_th_flurd(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADFMEMIDX(ctx); + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + return gen_fload_idx(ctx, a, MO_TEUQ, true); +} + +static bool trans_th_flurw(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADFMEMIDX(ctx); + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + return gen_fload_idx(ctx, a, MO_TEUL, true); +} + +static bool trans_th_fsrd(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADFMEMIDX(ctx); + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + return gen_fstore_idx(ctx, a, MO_TEUQ, false); +} + +static bool trans_th_fsrw(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADFMEMIDX(ctx); + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + return gen_fstore_idx(ctx, a, MO_TEUL, false); +} + +static bool trans_th_fsurd(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADFMEMIDX(ctx); + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + return gen_fstore_idx(ctx, a, MO_TEUQ, true); +} + +static bool trans_th_fsurw(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADFMEMIDX(ctx); + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + return gen_fstore_idx(ctx, a, MO_TEUL, true); +} + +/* XTheadFmv */ + +static bool trans_th_fmv_hw_x(DisasContext *ctx, arg_th_fmv_hw_x *a) +{ + REQUIRE_XTHEADFMV(ctx); + REQUIRE_32BIT(ctx); + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + TCGv src1 = get_gpr(ctx, a->rs1, EXT_ZERO); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_extu_tl_i64(t1, src1); + tcg_gen_deposit_i64(cpu_fpr[a->rd], cpu_fpr[a->rd], t1, 32, 32); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_th_fmv_x_hw(DisasContext *ctx, arg_th_fmv_x_hw *a) +{ + REQUIRE_XTHEADFMV(ctx); + REQUIRE_32BIT(ctx); + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + TCGv dst; + TCGv_i64 t1; + + dst = dest_gpr(ctx, a->rd); + t1 = tcg_temp_new_i64(); + + tcg_gen_extract_i64(t1, cpu_fpr[a->rs1], 32, 32); + tcg_gen_trunc_i64_tl(dst, t1); + gen_set_gpr(ctx, a->rd, dst); + mark_fs_dirty(ctx); + return true; +} + +/* XTheadMac */ + +static bool gen_th_mac(DisasContext *ctx, arg_r *a, + void (*accumulate_func)(TCGv, TCGv, TCGv), + void (*extend_operand_func)(TCGv, TCGv)) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src0 = get_gpr(ctx, a->rd, EXT_NONE); + TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); + TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); + TCGv tmp = tcg_temp_new(); + + if (extend_operand_func) { + TCGv tmp2 = tcg_temp_new(); + extend_operand_func(tmp, src1); + extend_operand_func(tmp2, src2); + tcg_gen_mul_tl(tmp, tmp, tmp2); + } else { + tcg_gen_mul_tl(tmp, src1, src2); + } + + accumulate_func(dest, src0, tmp); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +/* th.mula: "rd = rd + rs1 * rs2" */ +static bool trans_th_mula(DisasContext *ctx, arg_th_mula *a) +{ + REQUIRE_XTHEADMAC(ctx); + return gen_th_mac(ctx, a, tcg_gen_add_tl, NULL); +} + +/* th.mulah: "rd = sext.w(rd + sext.w(rs1[15:0]) * sext.w(rs2[15:0]))" */ +static bool trans_th_mulah(DisasContext *ctx, arg_th_mulah *a) +{ + REQUIRE_XTHEADMAC(ctx); + ctx->ol = MXL_RV32; + return gen_th_mac(ctx, a, tcg_gen_add_tl, tcg_gen_ext16s_tl); +} + +/* th.mulaw: "rd = sext.w(rd + rs1 * rs2)" */ +static bool trans_th_mulaw(DisasContext *ctx, arg_th_mulaw *a) +{ + REQUIRE_XTHEADMAC(ctx); + REQUIRE_64BIT(ctx); + ctx->ol = MXL_RV32; + return gen_th_mac(ctx, a, tcg_gen_add_tl, NULL); +} + +/* th.muls: "rd = rd - rs1 * rs2" */ +static bool trans_th_muls(DisasContext *ctx, arg_th_muls *a) +{ + REQUIRE_XTHEADMAC(ctx); + return gen_th_mac(ctx, a, tcg_gen_sub_tl, NULL); +} + +/* th.mulsh: "rd = sext.w(rd - sext.w(rs1[15:0]) * sext.w(rs2[15:0]))" */ +static bool trans_th_mulsh(DisasContext *ctx, arg_th_mulsh *a) +{ + REQUIRE_XTHEADMAC(ctx); + ctx->ol = MXL_RV32; + return gen_th_mac(ctx, a, tcg_gen_sub_tl, tcg_gen_ext16s_tl); +} + +/* th.mulsw: "rd = sext.w(rd - rs1 * rs2)" */ +static bool trans_th_mulsw(DisasContext *ctx, arg_th_mulsw *a) +{ + REQUIRE_XTHEADMAC(ctx); + REQUIRE_64BIT(ctx); + ctx->ol = MXL_RV32; + return gen_th_mac(ctx, a, tcg_gen_sub_tl, NULL); +} + +/* XTheadMemIdx */ + +/* + * Load with memop from indexed address and add (imm5 << imm2) to rs1. + * If !preinc, then the load address is rs1. + * If preinc, then the load address is rs1 + (imm5) << imm2). + */ +static bool gen_load_inc(DisasContext *ctx, arg_th_meminc *a, MemOp memop, + bool preinc) +{ + if (a->rs1 == a->rd) { + return false; + } + + int imm = a->imm5 << a->imm2; + TCGv addr = get_address(ctx, a->rs1, preinc ? imm : 0); + TCGv rd = dest_gpr(ctx, a->rd); + TCGv rs1 = get_gpr(ctx, a->rs1, EXT_NONE); + + tcg_gen_qemu_ld_tl(rd, addr, ctx->mem_idx, memop); + tcg_gen_addi_tl(rs1, rs1, imm); + gen_set_gpr(ctx, a->rd, rd); + gen_set_gpr(ctx, a->rs1, rs1); + return true; +} + +/* + * Store with memop to indexed address and add (imm5 << imm2) to rs1. + * If !preinc, then the store address is rs1. + * If preinc, then the store address is rs1 + (imm5) << imm2). + */ +static bool gen_store_inc(DisasContext *ctx, arg_th_meminc *a, MemOp memop, + bool preinc) +{ + int imm = a->imm5 << a->imm2; + TCGv addr = get_address(ctx, a->rs1, preinc ? imm : 0); + TCGv data = get_gpr(ctx, a->rd, EXT_NONE); + TCGv rs1 = get_gpr(ctx, a->rs1, EXT_NONE); + + tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, memop); + tcg_gen_addi_tl(rs1, rs1, imm); + gen_set_gpr(ctx, a->rs1, rs1); + return true; +} + +static bool trans_th_ldia(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + REQUIRE_64BIT(ctx); + return gen_load_inc(ctx, a, MO_TESQ, false); +} + +static bool trans_th_ldib(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + REQUIRE_64BIT(ctx); + return gen_load_inc(ctx, a, MO_TESQ, true); +} + +static bool trans_th_lwia(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_inc(ctx, a, MO_TESL, false); +} + +static bool trans_th_lwib(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_inc(ctx, a, MO_TESL, true); +} + +static bool trans_th_lwuia(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + REQUIRE_64BIT(ctx); + return gen_load_inc(ctx, a, MO_TEUL, false); +} + +static bool trans_th_lwuib(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + REQUIRE_64BIT(ctx); + return gen_load_inc(ctx, a, MO_TEUL, true); +} + +static bool trans_th_lhia(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_inc(ctx, a, MO_TESW, false); +} + +static bool trans_th_lhib(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_inc(ctx, a, MO_TESW, true); +} + +static bool trans_th_lhuia(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_inc(ctx, a, MO_TEUW, false); +} + +static bool trans_th_lhuib(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_inc(ctx, a, MO_TEUW, true); +} + +static bool trans_th_lbia(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_inc(ctx, a, MO_SB, false); +} + +static bool trans_th_lbib(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_inc(ctx, a, MO_SB, true); +} + +static bool trans_th_lbuia(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_inc(ctx, a, MO_UB, false); +} + +static bool trans_th_lbuib(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_inc(ctx, a, MO_UB, true); +} + +static bool trans_th_sdia(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + REQUIRE_64BIT(ctx); + return gen_store_inc(ctx, a, MO_TESQ, false); +} + +static bool trans_th_sdib(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + REQUIRE_64BIT(ctx); + return gen_store_inc(ctx, a, MO_TESQ, true); +} + +static bool trans_th_swia(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_store_inc(ctx, a, MO_TESL, false); +} + +static bool trans_th_swib(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_store_inc(ctx, a, MO_TESL, true); +} + +static bool trans_th_shia(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_store_inc(ctx, a, MO_TESW, false); +} + +static bool trans_th_shib(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_store_inc(ctx, a, MO_TESW, true); +} + +static bool trans_th_sbia(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_store_inc(ctx, a, MO_SB, false); +} + +static bool trans_th_sbib(DisasContext *ctx, arg_th_meminc *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_store_inc(ctx, a, MO_SB, true); +} + +/* + * Load with memop from indexed address. + * If !zext_offs, then address is rs1 + (rs2 << imm2). + * If zext_offs, then address is rs1 + (zext(rs2[31:0]) << imm2). + */ +static bool gen_load_idx(DisasContext *ctx, arg_th_memidx *a, MemOp memop, + bool zext_offs) +{ + TCGv rd = dest_gpr(ctx, a->rd); + TCGv addr = get_th_address_indexed(ctx, a->rs1, a->rs2, a->imm2, zext_offs); + + tcg_gen_qemu_ld_tl(rd, addr, ctx->mem_idx, memop); + gen_set_gpr(ctx, a->rd, rd); + + return true; +} + +/* + * Store with memop to indexed address. + * If !zext_offs, then address is rs1 + (rs2 << imm2). + * If zext_offs, then address is rs1 + (zext(rs2[31:0]) << imm2). + */ +static bool gen_store_idx(DisasContext *ctx, arg_th_memidx *a, MemOp memop, + bool zext_offs) +{ + TCGv data = get_gpr(ctx, a->rd, EXT_NONE); + TCGv addr = get_th_address_indexed(ctx, a->rs1, a->rs2, a->imm2, zext_offs); + + tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, memop); + + return true; +} + +static bool trans_th_lrd(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + REQUIRE_64BIT(ctx); + return gen_load_idx(ctx, a, MO_TESQ, false); +} + +static bool trans_th_lrw(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_idx(ctx, a, MO_TESL, false); +} + +static bool trans_th_lrwu(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + REQUIRE_64BIT(ctx); + return gen_load_idx(ctx, a, MO_TEUL, false); +} + +static bool trans_th_lrh(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_idx(ctx, a, MO_TESW, false); +} + +static bool trans_th_lrhu(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_idx(ctx, a, MO_TEUW, false); +} + +static bool trans_th_lrb(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_idx(ctx, a, MO_SB, false); +} + +static bool trans_th_lrbu(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_idx(ctx, a, MO_UB, false); +} + +static bool trans_th_srd(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + REQUIRE_64BIT(ctx); + return gen_store_idx(ctx, a, MO_TESQ, false); +} + +static bool trans_th_srw(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_store_idx(ctx, a, MO_TESL, false); +} + +static bool trans_th_srh(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_store_idx(ctx, a, MO_TESW, false); +} + +static bool trans_th_srb(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_store_idx(ctx, a, MO_SB, false); +} +static bool trans_th_lurd(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + REQUIRE_64BIT(ctx); + return gen_load_idx(ctx, a, MO_TESQ, true); +} + +static bool trans_th_lurw(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_idx(ctx, a, MO_TESL, true); +} + +static bool trans_th_lurwu(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + REQUIRE_64BIT(ctx); + return gen_load_idx(ctx, a, MO_TEUL, true); +} + +static bool trans_th_lurh(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_idx(ctx, a, MO_TESW, true); +} + +static bool trans_th_lurhu(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_idx(ctx, a, MO_TEUW, true); +} + +static bool trans_th_lurb(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_idx(ctx, a, MO_SB, true); +} + +static bool trans_th_lurbu(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_load_idx(ctx, a, MO_UB, true); +} + +static bool trans_th_surd(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + REQUIRE_64BIT(ctx); + return gen_store_idx(ctx, a, MO_TESQ, true); +} + +static bool trans_th_surw(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_store_idx(ctx, a, MO_TESL, true); +} + +static bool trans_th_surh(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_store_idx(ctx, a, MO_TESW, true); +} + +static bool trans_th_surb(DisasContext *ctx, arg_th_memidx *a) +{ + REQUIRE_XTHEADMEMIDX(ctx); + return gen_store_idx(ctx, a, MO_SB, true); +} + +/* XTheadMemPair */ + +static bool gen_loadpair_tl(DisasContext *ctx, arg_th_pair *a, MemOp memop, + int shamt) +{ + if (a->rs == a->rd1 || a->rs == a->rd2 || a->rd1 == a->rd2) { + return false; + } + + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv addr1 = tcg_temp_new(); + TCGv addr2 = tcg_temp_new(); + int imm = a->sh2 << shamt; + + addr1 = get_address(ctx, a->rs, imm); + addr2 = get_address(ctx, a->rs, memop_size(memop) + imm); + + tcg_gen_qemu_ld_tl(t1, addr1, ctx->mem_idx, memop); + tcg_gen_qemu_ld_tl(t2, addr2, ctx->mem_idx, memop); + gen_set_gpr(ctx, a->rd1, t1); + gen_set_gpr(ctx, a->rd2, t2); + return true; +} + +static bool trans_th_ldd(DisasContext *ctx, arg_th_pair *a) +{ + REQUIRE_XTHEADMEMPAIR(ctx); + REQUIRE_64BIT(ctx); + return gen_loadpair_tl(ctx, a, MO_TESQ, 4); +} + +static bool trans_th_lwd(DisasContext *ctx, arg_th_pair *a) +{ + REQUIRE_XTHEADMEMPAIR(ctx); + return gen_loadpair_tl(ctx, a, MO_TESL, 3); +} + +static bool trans_th_lwud(DisasContext *ctx, arg_th_pair *a) +{ + REQUIRE_XTHEADMEMPAIR(ctx); + return gen_loadpair_tl(ctx, a, MO_TEUL, 3); +} + +static bool gen_storepair_tl(DisasContext *ctx, arg_th_pair *a, MemOp memop, + int shamt) +{ + TCGv data1 = get_gpr(ctx, a->rd1, EXT_NONE); + TCGv data2 = get_gpr(ctx, a->rd2, EXT_NONE); + TCGv addr1 = tcg_temp_new(); + TCGv addr2 = tcg_temp_new(); + int imm = a->sh2 << shamt; + + addr1 = get_address(ctx, a->rs, imm); + addr2 = get_address(ctx, a->rs, memop_size(memop) + imm); + + tcg_gen_qemu_st_tl(data1, addr1, ctx->mem_idx, memop); + tcg_gen_qemu_st_tl(data2, addr2, ctx->mem_idx, memop); + return true; +} + +static bool trans_th_sdd(DisasContext *ctx, arg_th_pair *a) +{ + REQUIRE_XTHEADMEMPAIR(ctx); + REQUIRE_64BIT(ctx); + return gen_storepair_tl(ctx, a, MO_TESQ, 4); +} + +static bool trans_th_swd(DisasContext *ctx, arg_th_pair *a) +{ + REQUIRE_XTHEADMEMPAIR(ctx); + return gen_storepair_tl(ctx, a, MO_TESL, 3); +} + +/* XTheadSync */ + +static bool trans_th_sfence_vmas(DisasContext *ctx, arg_th_sfence_vmas *a) +{ + (void) a; + REQUIRE_XTHEADSYNC(ctx); + +#ifndef CONFIG_USER_ONLY + REQUIRE_PRIV_MS(ctx); + gen_helper_tlb_flush_all(tcg_env); + return true; +#else + return false; +#endif +} + +static void gen_th_sync_local(DisasContext *ctx) +{ + /* + * Emulate out-of-order barriers with pipeline flush + * by exiting the translation block. + */ + gen_update_pc(ctx, ctx->cur_insn_len); + tcg_gen_exit_tb(NULL, 0); + ctx->base.is_jmp = DISAS_NORETURN; +} + +static bool trans_th_sync(DisasContext *ctx, arg_th_sync *a) +{ + (void) a; + REQUIRE_XTHEADSYNC(ctx); + + REQUIRE_PRIV_MSU(ctx); + + /* + * th.sync is an out-of-order barrier. + */ + gen_th_sync_local(ctx); + + return true; +} + +static bool trans_th_sync_i(DisasContext *ctx, arg_th_sync_i *a) +{ + (void) a; + REQUIRE_XTHEADSYNC(ctx); + + REQUIRE_PRIV_MSU(ctx); + + /* + * th.sync.i is th.sync plus pipeline flush. + */ + gen_th_sync_local(ctx); + + return true; +} + +static bool trans_th_sync_is(DisasContext *ctx, arg_th_sync_is *a) +{ + /* This instruction has the same behaviour like th.sync.i. */ + return trans_th_sync_i(ctx, a); +} + +static bool trans_th_sync_s(DisasContext *ctx, arg_th_sync_s *a) +{ + /* This instruction has the same behaviour like th.sync. */ + return trans_th_sync(ctx, a); +} diff --git a/target/riscv/insn_trans/trans_xventanacondops.c.inc b/target/riscv/insn_trans/trans_xventanacondops.c.inc index 16849e6d4e..38c15f2825 100644 --- a/target/riscv/insn_trans/trans_xventanacondops.c.inc +++ b/target/riscv/insn_trans/trans_xventanacondops.c.inc @@ -1,7 +1,7 @@ /* * RISC-V translation routines for the XVentanaCondOps extension. * - * Copyright (c) 2021-2022 VRULL GmbH. + * Copyright (c) 2021-2023 VRULL GmbH. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -16,24 +16,12 @@ * this program. If not, see . */ -static bool gen_vt_condmask(DisasContext *ctx, arg_r *a, TCGCond cond) -{ - TCGv dest = dest_gpr(ctx, a->rd); - TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); - TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); - - tcg_gen_movcond_tl(cond, dest, src2, ctx->zero, src1, ctx->zero); - - gen_set_gpr(ctx, a->rd, dest); - return true; -} - static bool trans_vt_maskc(DisasContext *ctx, arg_r *a) { - return gen_vt_condmask(ctx, a, TCG_COND_NE); + return gen_logic(ctx, a, gen_czero_eqz); } static bool trans_vt_maskcn(DisasContext *ctx, arg_r *a) { - return gen_vt_condmask(ctx, a, TCG_COND_EQ); + return gen_logic(ctx, a, gen_czero_nez); } diff --git a/target/riscv/instmap.h b/target/riscv/instmap.h index 40b6d2b64d..f877530576 100644 --- a/target/riscv/instmap.h +++ b/target/riscv/instmap.h @@ -184,6 +184,8 @@ enum { OPC_RISC_CSRRWI = OPC_RISC_SYSTEM | (0x5 << 12), OPC_RISC_CSRRSI = OPC_RISC_SYSTEM | (0x6 << 12), OPC_RISC_CSRRCI = OPC_RISC_SYSTEM | (0x7 << 12), + + OPC_RISC_HLVHSV = OPC_RISC_SYSTEM | (0x4 << 12), }; #define MASK_OP_FP_LOAD(op) (MASK_OP_MAJOR(op) | (op & (0x7 << 12))) @@ -310,12 +312,20 @@ enum { | (extract32(inst, 12, 8) << 12) \ | (sextract64(inst, 31, 1) << 20)) +#define GET_FUNCT3(inst) extract32(inst, 12, 3) +#define GET_FUNCT7(inst) extract32(inst, 25, 7) #define GET_RM(inst) extract32(inst, 12, 3) #define GET_RS3(inst) extract32(inst, 27, 5) #define GET_RS1(inst) extract32(inst, 15, 5) #define GET_RS2(inst) extract32(inst, 20, 5) #define GET_RD(inst) extract32(inst, 7, 5) #define GET_IMM(inst) sextract64(inst, 20, 12) +#define SET_RS1(inst, val) deposit32(inst, 15, 5, val) +#define SET_RS2(inst, val) deposit32(inst, 20, 5, val) +#define SET_RD(inst, val) deposit32(inst, 7, 5, val) +#define SET_I_IMM(inst, val) deposit32(inst, 20, 12, val) +#define SET_S_IMM(inst, val) \ + deposit32(deposit32(inst, 7, 5, val), 25, 7, (val) >> 5) /* RVC decoding macros */ #define GET_C_IMM(inst) (extract32(inst, 2, 5) \ @@ -346,6 +356,8 @@ enum { | (extract32(inst, 5, 1) << 6)) #define GET_C_LD_IMM(inst) ((extract16(inst, 10, 3) << 3) \ | (extract16(inst, 5, 2) << 6)) +#define GET_C_SW_IMM(inst) GET_C_LW_IMM(inst) +#define GET_C_SD_IMM(inst) GET_C_LD_IMM(inst) #define GET_C_J_IMM(inst) ((extract32(inst, 3, 3) << 1) \ | (extract32(inst, 11, 1) << 4) \ | (extract32(inst, 2, 1) << 5) \ @@ -366,4 +378,37 @@ enum { #define GET_C_RS1S(inst) (8 + extract16(inst, 7, 3)) #define GET_C_RS2S(inst) (8 + extract16(inst, 2, 3)) +#define GET_C_FUNC(inst) extract32(inst, 13, 3) +#define GET_C_OP(inst) extract32(inst, 0, 2) + +enum { + /* RVC Quadrants */ + OPC_RISC_C_OP_QUAD0 = 0x0, + OPC_RISC_C_OP_QUAD1 = 0x1, + OPC_RISC_C_OP_QUAD2 = 0x2 +}; + +enum { + /* RVC Quadrant 0 */ + OPC_RISC_C_FUNC_ADDI4SPN = 0x0, + OPC_RISC_C_FUNC_FLD_LQ = 0x1, + OPC_RISC_C_FUNC_LW = 0x2, + OPC_RISC_C_FUNC_FLW_LD = 0x3, + OPC_RISC_C_FUNC_FSD_SQ = 0x5, + OPC_RISC_C_FUNC_SW = 0x6, + OPC_RISC_C_FUNC_FSW_SD = 0x7 +}; + +enum { + /* RVC Quadrant 2 */ + OPC_RISC_C_FUNC_SLLI_SLLI64 = 0x0, + OPC_RISC_C_FUNC_FLDSP_LQSP = 0x1, + OPC_RISC_C_FUNC_LWSP = 0x2, + OPC_RISC_C_FUNC_FLWSP_LDSP = 0x3, + OPC_RISC_C_FUNC_JR_MV_EBREAK_JALR_ADD = 0x4, + OPC_RISC_C_FUNC_FSDSP_SQSP = 0x5, + OPC_RISC_C_FUNC_SWSP = 0x6, + OPC_RISC_C_FUNC_FSWSP_SDSP = 0x7 +}; + #endif diff --git a/target/riscv/internals.h b/target/riscv/internals.h index dbb322bfa7..8239ae83cc 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -21,11 +21,49 @@ #include "hw/registerfields.h" +/* + * The current MMU Modes are: + * - U 0b000 + * - S 0b001 + * - S+SUM 0b010 + * - M 0b011 + * - U+2STAGE 0b100 + * - S+2STAGE 0b101 + * - S+SUM+2STAGE 0b110 + */ +#define MMUIdx_U 0 +#define MMUIdx_S 1 +#define MMUIdx_S_SUM 2 +#define MMUIdx_M 3 +#define MMU_2STAGE_BIT (1 << 2) + +static inline int mmuidx_priv(int mmu_idx) +{ + int ret = mmu_idx & 3; + if (ret == MMUIdx_S_SUM) { + ret = PRV_S; + } + return ret; +} + +static inline bool mmuidx_sum(int mmu_idx) +{ + return (mmu_idx & 3) == MMUIdx_S_SUM; +} + +static inline bool mmuidx_2stage(int mmu_idx) +{ + return mmu_idx & MMU_2STAGE_BIT; +} + /* share data between vector helpers and decode code */ FIELD(VDATA, VM, 0, 1) FIELD(VDATA, LMUL, 1, 3) -FIELD(VDATA, NF, 4, 4) -FIELD(VDATA, WD, 4, 1) +FIELD(VDATA, VTA, 4, 1) +FIELD(VDATA, VTA_ALL_1S, 5, 1) +FIELD(VDATA, VMA, 6, 1) +FIELD(VDATA, NF, 7, 4) +FIELD(VDATA, WD, 7, 1) /* float point classify helpers */ target_ulong fclass_h(uint64_t frs1); @@ -49,7 +87,7 @@ enum { static inline uint64_t nanbox_s(CPURISCVState *env, float32 f) { /* the value is sign-extended instead of NaN-boxing for zfinx */ - if (RISCV_CPU(env_cpu(env))->cfg.ext_zfinx) { + if (env_archcpu(env)->cfg.ext_zfinx) { return (int32_t)f; } else { return f | MAKE_64BIT_MASK(32, 32); @@ -59,7 +97,7 @@ static inline uint64_t nanbox_s(CPURISCVState *env, float32 f) static inline float32 check_nanbox_s(CPURISCVState *env, uint64_t f) { /* Disable NaN-boxing check when enable zfinx */ - if (RISCV_CPU(env_cpu(env))->cfg.ext_zfinx) { + if (env_archcpu(env)->cfg.ext_zfinx) { return (uint32_t)f; } @@ -75,7 +113,7 @@ static inline float32 check_nanbox_s(CPURISCVState *env, uint64_t f) static inline uint64_t nanbox_h(CPURISCVState *env, float16 f) { /* the value is sign-extended instead of NaN-boxing for zfinx */ - if (RISCV_CPU(env_cpu(env))->cfg.ext_zfinx) { + if (env_archcpu(env)->cfg.ext_zfinx) { return (int16_t)f; } else { return f | MAKE_64BIT_MASK(16, 48); @@ -85,7 +123,7 @@ static inline uint64_t nanbox_h(CPURISCVState *env, float16 f) static inline float16 check_nanbox_h(CPURISCVState *env, uint64_t f) { /* Disable nanbox check when enable zfinx */ - if (RISCV_CPU(env_cpu(env))->cfg.ext_zfinx) { + if (env_archcpu(env)->cfg.ext_zfinx) { return (uint16_t)f; } diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c deleted file mode 100644 index 70b4cff06f..0000000000 --- a/target/riscv/kvm.c +++ /dev/null @@ -1,534 +0,0 @@ -/* - * RISC-V implementation of KVM hooks - * - * Copyright (c) 2020 Huawei Technologies Co., Ltd - * - * 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 - -#include - -#include "qemu/timer.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_int.h" -#include "cpu.h" -#include "trace.h" -#include "hw/pci/pci.h" -#include "exec/memattrs.h" -#include "exec/address-spaces.h" -#include "hw/boards.h" -#include "hw/irq.h" -#include "qemu/log.h" -#include "hw/loader.h" -#include "kvm_riscv.h" -#include "sbi_ecall_interface.h" -#include "chardev/char-fe.h" -#include "migration/migration.h" -#include "sysemu/runstate.h" - -static uint64_t kvm_riscv_reg_id(CPURISCVState *env, uint64_t type, - uint64_t idx) -{ - uint64_t id = KVM_REG_RISCV | type | idx; - - switch (riscv_cpu_mxl(env)) { - case MXL_RV32: - id |= KVM_REG_SIZE_U32; - break; - case MXL_RV64: - id |= KVM_REG_SIZE_U64; - break; - default: - g_assert_not_reached(); - } - return id; -} - -#define RISCV_CORE_REG(env, name) kvm_riscv_reg_id(env, KVM_REG_RISCV_CORE, \ - KVM_REG_RISCV_CORE_REG(name)) - -#define RISCV_CSR_REG(env, name) kvm_riscv_reg_id(env, KVM_REG_RISCV_CSR, \ - KVM_REG_RISCV_CSR_REG(name)) - -#define RISCV_TIMER_REG(env, name) kvm_riscv_reg_id(env, KVM_REG_RISCV_TIMER, \ - KVM_REG_RISCV_TIMER_REG(name)) - -#define RISCV_FP_F_REG(env, idx) kvm_riscv_reg_id(env, KVM_REG_RISCV_FP_F, idx) - -#define RISCV_FP_D_REG(env, idx) kvm_riscv_reg_id(env, KVM_REG_RISCV_FP_D, idx) - -#define KVM_RISCV_GET_CSR(cs, env, csr, reg) \ - do { \ - int ret = kvm_get_one_reg(cs, RISCV_CSR_REG(env, csr), ®); \ - if (ret) { \ - return ret; \ - } \ - } while (0) - -#define KVM_RISCV_SET_CSR(cs, env, csr, reg) \ - do { \ - int ret = kvm_set_one_reg(cs, RISCV_CSR_REG(env, csr), ®); \ - if (ret) { \ - return ret; \ - } \ - } while (0) - -#define KVM_RISCV_GET_TIMER(cs, env, name, reg) \ - do { \ - int ret = kvm_get_one_reg(cs, RISCV_TIMER_REG(env, name), ®); \ - if (ret) { \ - abort(); \ - } \ - } while (0) - -#define KVM_RISCV_SET_TIMER(cs, env, name, reg) \ - do { \ - int ret = kvm_set_one_reg(cs, RISCV_TIMER_REG(env, time), ®); \ - if (ret) { \ - abort(); \ - } \ - } while (0) - -static int kvm_riscv_get_regs_core(CPUState *cs) -{ - int ret = 0; - int i; - target_ulong reg; - CPURISCVState *env = &RISCV_CPU(cs)->env; - - ret = kvm_get_one_reg(cs, RISCV_CORE_REG(env, regs.pc), ®); - if (ret) { - return ret; - } - env->pc = reg; - - for (i = 1; i < 32; i++) { - uint64_t id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CORE, i); - ret = kvm_get_one_reg(cs, id, ®); - if (ret) { - return ret; - } - env->gpr[i] = reg; - } - - return ret; -} - -static int kvm_riscv_put_regs_core(CPUState *cs) -{ - int ret = 0; - int i; - target_ulong reg; - CPURISCVState *env = &RISCV_CPU(cs)->env; - - reg = env->pc; - ret = kvm_set_one_reg(cs, RISCV_CORE_REG(env, regs.pc), ®); - if (ret) { - return ret; - } - - for (i = 1; i < 32; i++) { - uint64_t id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CORE, i); - reg = env->gpr[i]; - ret = kvm_set_one_reg(cs, id, ®); - if (ret) { - return ret; - } - } - - return ret; -} - -static int kvm_riscv_get_regs_csr(CPUState *cs) -{ - int ret = 0; - CPURISCVState *env = &RISCV_CPU(cs)->env; - - KVM_RISCV_GET_CSR(cs, env, sstatus, env->mstatus); - KVM_RISCV_GET_CSR(cs, env, sie, env->mie); - KVM_RISCV_GET_CSR(cs, env, stvec, env->stvec); - KVM_RISCV_GET_CSR(cs, env, sscratch, env->sscratch); - KVM_RISCV_GET_CSR(cs, env, sepc, env->sepc); - KVM_RISCV_GET_CSR(cs, env, scause, env->scause); - KVM_RISCV_GET_CSR(cs, env, stval, env->stval); - KVM_RISCV_GET_CSR(cs, env, sip, env->mip); - KVM_RISCV_GET_CSR(cs, env, satp, env->satp); - return ret; -} - -static int kvm_riscv_put_regs_csr(CPUState *cs) -{ - int ret = 0; - CPURISCVState *env = &RISCV_CPU(cs)->env; - - KVM_RISCV_SET_CSR(cs, env, sstatus, env->mstatus); - KVM_RISCV_SET_CSR(cs, env, sie, env->mie); - KVM_RISCV_SET_CSR(cs, env, stvec, env->stvec); - KVM_RISCV_SET_CSR(cs, env, sscratch, env->sscratch); - KVM_RISCV_SET_CSR(cs, env, sepc, env->sepc); - KVM_RISCV_SET_CSR(cs, env, scause, env->scause); - KVM_RISCV_SET_CSR(cs, env, stval, env->stval); - KVM_RISCV_SET_CSR(cs, env, sip, env->mip); - KVM_RISCV_SET_CSR(cs, env, satp, env->satp); - - return ret; -} - -static int kvm_riscv_get_regs_fp(CPUState *cs) -{ - int ret = 0; - int i; - CPURISCVState *env = &RISCV_CPU(cs)->env; - - if (riscv_has_ext(env, RVD)) { - uint64_t reg; - for (i = 0; i < 32; i++) { - ret = kvm_get_one_reg(cs, RISCV_FP_D_REG(env, i), ®); - if (ret) { - return ret; - } - env->fpr[i] = reg; - } - return ret; - } - - if (riscv_has_ext(env, RVF)) { - uint32_t reg; - for (i = 0; i < 32; i++) { - ret = kvm_get_one_reg(cs, RISCV_FP_F_REG(env, i), ®); - if (ret) { - return ret; - } - env->fpr[i] = reg; - } - return ret; - } - - return ret; -} - -static int kvm_riscv_put_regs_fp(CPUState *cs) -{ - int ret = 0; - int i; - CPURISCVState *env = &RISCV_CPU(cs)->env; - - if (riscv_has_ext(env, RVD)) { - uint64_t reg; - for (i = 0; i < 32; i++) { - reg = env->fpr[i]; - ret = kvm_set_one_reg(cs, RISCV_FP_D_REG(env, i), ®); - if (ret) { - return ret; - } - } - return ret; - } - - if (riscv_has_ext(env, RVF)) { - uint32_t reg; - for (i = 0; i < 32; i++) { - reg = env->fpr[i]; - ret = kvm_set_one_reg(cs, RISCV_FP_F_REG(env, i), ®); - if (ret) { - return ret; - } - } - return ret; - } - - return ret; -} - -static void kvm_riscv_get_regs_timer(CPUState *cs) -{ - CPURISCVState *env = &RISCV_CPU(cs)->env; - - if (env->kvm_timer_dirty) { - return; - } - - KVM_RISCV_GET_TIMER(cs, env, time, env->kvm_timer_time); - KVM_RISCV_GET_TIMER(cs, env, compare, env->kvm_timer_compare); - KVM_RISCV_GET_TIMER(cs, env, state, env->kvm_timer_state); - KVM_RISCV_GET_TIMER(cs, env, frequency, env->kvm_timer_frequency); - - env->kvm_timer_dirty = true; -} - -static void kvm_riscv_put_regs_timer(CPUState *cs) -{ - uint64_t reg; - CPURISCVState *env = &RISCV_CPU(cs)->env; - - if (!env->kvm_timer_dirty) { - return; - } - - KVM_RISCV_SET_TIMER(cs, env, time, env->kvm_timer_time); - KVM_RISCV_SET_TIMER(cs, env, compare, env->kvm_timer_compare); - - /* - * To set register of RISCV_TIMER_REG(state) will occur a error from KVM - * on env->kvm_timer_state == 0, It's better to adapt in KVM, but it - * doesn't matter that adaping in QEMU now. - * TODO If KVM changes, adapt here. - */ - if (env->kvm_timer_state) { - KVM_RISCV_SET_TIMER(cs, env, state, env->kvm_timer_state); - } - - /* - * For now, migration will not work between Hosts with different timer - * frequency. Therefore, we should check whether they are the same here - * during the migration. - */ - if (migration_is_running(migrate_get_current()->state)) { - KVM_RISCV_GET_TIMER(cs, env, frequency, reg); - if (reg != env->kvm_timer_frequency) { - error_report("Dst Hosts timer frequency != Src Hosts"); - } - } - - env->kvm_timer_dirty = false; -} - -const KVMCapabilityInfo kvm_arch_required_capabilities[] = { - KVM_CAP_LAST_INFO -}; - -int kvm_arch_get_registers(CPUState *cs) -{ - int ret = 0; - - ret = kvm_riscv_get_regs_core(cs); - if (ret) { - return ret; - } - - ret = kvm_riscv_get_regs_csr(cs); - if (ret) { - return ret; - } - - ret = kvm_riscv_get_regs_fp(cs); - if (ret) { - return ret; - } - - return ret; -} - -int kvm_arch_put_registers(CPUState *cs, int level) -{ - int ret = 0; - - ret = kvm_riscv_put_regs_core(cs); - if (ret) { - return ret; - } - - ret = kvm_riscv_put_regs_csr(cs); - if (ret) { - return ret; - } - - ret = kvm_riscv_put_regs_fp(cs); - if (ret) { - return ret; - } - - return ret; -} - -int kvm_arch_release_virq_post(int virq) -{ - return 0; -} - -int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, - uint64_t address, uint32_t data, PCIDevice *dev) -{ - return 0; -} - -int kvm_arch_destroy_vcpu(CPUState *cs) -{ - return 0; -} - -unsigned long kvm_arch_vcpu_id(CPUState *cpu) -{ - return cpu->cpu_index; -} - -static void kvm_riscv_vm_state_change(void *opaque, bool running, - RunState state) -{ - CPUState *cs = opaque; - - if (running) { - kvm_riscv_put_regs_timer(cs); - } else { - kvm_riscv_get_regs_timer(cs); - } -} - -void kvm_arch_init_irq_routing(KVMState *s) -{ -} - -int kvm_arch_init_vcpu(CPUState *cs) -{ - int ret = 0; - target_ulong isa; - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - uint64_t id; - - qemu_add_vm_change_state_handler(kvm_riscv_vm_state_change, cs); - - id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, - KVM_REG_RISCV_CONFIG_REG(isa)); - ret = kvm_get_one_reg(cs, id, &isa); - if (ret) { - return ret; - } - env->misa_ext = isa; - - return ret; -} - -int kvm_arch_msi_data_to_gsi(uint32_t data) -{ - abort(); -} - -int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route, - int vector, PCIDevice *dev) -{ - return 0; -} - -int kvm_arch_init(MachineState *ms, KVMState *s) -{ - return 0; -} - -int kvm_arch_irqchip_create(KVMState *s) -{ - return 0; -} - -int kvm_arch_process_async_events(CPUState *cs) -{ - return 0; -} - -void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) -{ -} - -MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) -{ - return MEMTXATTRS_UNSPECIFIED; -} - -bool kvm_arch_stop_on_emulation_error(CPUState *cs) -{ - return true; -} - -static int kvm_riscv_handle_sbi(CPUState *cs, struct kvm_run *run) -{ - int ret = 0; - unsigned char ch; - switch (run->riscv_sbi.extension_id) { - case SBI_EXT_0_1_CONSOLE_PUTCHAR: - ch = run->riscv_sbi.args[0]; - qemu_chr_fe_write(serial_hd(0)->be, &ch, sizeof(ch)); - break; - case SBI_EXT_0_1_CONSOLE_GETCHAR: - ret = qemu_chr_fe_read_all(serial_hd(0)->be, &ch, sizeof(ch)); - if (ret == sizeof(ch)) { - run->riscv_sbi.args[0] = ch; - } else { - run->riscv_sbi.args[0] = -1; - } - break; - default: - qemu_log_mask(LOG_UNIMP, - "%s: un-handled SBI EXIT, specific reasons is %lu\n", - __func__, run->riscv_sbi.extension_id); - ret = -1; - break; - } - return ret; -} - -int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) -{ - int ret = 0; - switch (run->exit_reason) { - case KVM_EXIT_RISCV_SBI: - ret = kvm_riscv_handle_sbi(cs, run); - break; - default: - qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n", - __func__, run->exit_reason); - ret = -1; - break; - } - return ret; -} - -void kvm_riscv_reset_vcpu(RISCVCPU *cpu) -{ - CPURISCVState *env = &cpu->env; - - if (!kvm_enabled()) { - return; - } - env->pc = cpu->env.kernel_addr; - env->gpr[10] = kvm_arch_vcpu_id(CPU(cpu)); /* a0 */ - env->gpr[11] = cpu->env.fdt_addr; /* a1 */ - env->satp = 0; -} - -void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level) -{ - int ret; - unsigned virq = level ? KVM_INTERRUPT_SET : KVM_INTERRUPT_UNSET; - - if (irq != IRQ_S_EXT) { - perror("kvm riscv set irq != IRQ_S_EXT\n"); - abort(); - } - - ret = kvm_vcpu_ioctl(CPU(cpu), KVM_INTERRUPT, &virq); - if (ret < 0) { - perror("Set irq failed"); - abort(); - } -} - -bool kvm_arch_cpu_check_are_resettable(void) -{ - return true; -} diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c new file mode 100644 index 0000000000..6a6c6cae80 --- /dev/null +++ b/target/riscv/kvm/kvm-cpu.c @@ -0,0 +1,1829 @@ +/* + * RISC-V implementation of KVM hooks + * + * Copyright (c) 2020 Huawei Technologies Co., Ltd + * + * 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 +#include + +#include + +#include "qemu/timer.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "qapi/visitor.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "sysemu/kvm_int.h" +#include "cpu.h" +#include "trace.h" +#include "hw/core/accel-cpu.h" +#include "hw/pci/pci.h" +#include "exec/memattrs.h" +#include "exec/address-spaces.h" +#include "hw/boards.h" +#include "hw/irq.h" +#include "hw/intc/riscv_imsic.h" +#include "qemu/log.h" +#include "hw/loader.h" +#include "kvm_riscv.h" +#include "sbi_ecall_interface.h" +#include "chardev/char-fe.h" +#include "migration/misc.h" +#include "sysemu/runstate.h" +#include "hw/riscv/numa.h" + +#define PR_RISCV_V_SET_CONTROL 69 +#define PR_RISCV_V_VSTATE_CTRL_ON 2 + +void riscv_kvm_aplic_request(void *opaque, int irq, int level) +{ + kvm_set_irq(kvm_state, irq, !!level); +} + +static bool cap_has_mp_state; + +static uint64_t kvm_riscv_reg_id_ulong(CPURISCVState *env, uint64_t type, + uint64_t idx) +{ + uint64_t id = KVM_REG_RISCV | type | idx; + + switch (riscv_cpu_mxl(env)) { + case MXL_RV32: + id |= KVM_REG_SIZE_U32; + break; + case MXL_RV64: + id |= KVM_REG_SIZE_U64; + break; + default: + g_assert_not_reached(); + } + return id; +} + +static uint64_t kvm_riscv_reg_id_u32(uint64_t type, uint64_t idx) +{ + return KVM_REG_RISCV | KVM_REG_SIZE_U32 | type | idx; +} + +static uint64_t kvm_riscv_reg_id_u64(uint64_t type, uint64_t idx) +{ + return KVM_REG_RISCV | KVM_REG_SIZE_U64 | type | idx; +} + +static uint64_t kvm_encode_reg_size_id(uint64_t id, size_t size_b) +{ + uint64_t size_ctz = __builtin_ctz(size_b); + + return id | (size_ctz << KVM_REG_SIZE_SHIFT); +} + +static uint64_t kvm_riscv_vector_reg_id(RISCVCPU *cpu, + uint64_t idx) +{ + uint64_t id; + size_t size_b; + + g_assert(idx < 32); + + id = KVM_REG_RISCV | KVM_REG_RISCV_VECTOR | KVM_REG_RISCV_VECTOR_REG(idx); + size_b = cpu->cfg.vlenb; + + return kvm_encode_reg_size_id(id, size_b); +} + +#define RISCV_CORE_REG(env, name) \ + kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CORE, \ + KVM_REG_RISCV_CORE_REG(name)) + +#define RISCV_CSR_REG(env, name) \ + kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CSR, \ + KVM_REG_RISCV_CSR_REG(name)) + +#define RISCV_CONFIG_REG(env, name) \ + kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CONFIG, \ + KVM_REG_RISCV_CONFIG_REG(name)) + +#define RISCV_TIMER_REG(name) kvm_riscv_reg_id_u64(KVM_REG_RISCV_TIMER, \ + KVM_REG_RISCV_TIMER_REG(name)) + +#define RISCV_FP_F_REG(idx) kvm_riscv_reg_id_u32(KVM_REG_RISCV_FP_F, idx) + +#define RISCV_FP_D_REG(idx) kvm_riscv_reg_id_u64(KVM_REG_RISCV_FP_D, idx) + +#define RISCV_VECTOR_CSR_REG(env, name) \ + kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_VECTOR, \ + KVM_REG_RISCV_VECTOR_CSR_REG(name)) + +#define KVM_RISCV_GET_CSR(cs, env, csr, reg) \ + do { \ + int _ret = kvm_get_one_reg(cs, RISCV_CSR_REG(env, csr), ®); \ + if (_ret) { \ + return _ret; \ + } \ + } while (0) + +#define KVM_RISCV_SET_CSR(cs, env, csr, reg) \ + do { \ + int _ret = kvm_set_one_reg(cs, RISCV_CSR_REG(env, csr), ®); \ + if (_ret) { \ + return _ret; \ + } \ + } while (0) + +#define KVM_RISCV_GET_TIMER(cs, name, reg) \ + do { \ + int ret = kvm_get_one_reg(cs, RISCV_TIMER_REG(name), ®); \ + if (ret) { \ + abort(); \ + } \ + } while (0) + +#define KVM_RISCV_SET_TIMER(cs, name, reg) \ + do { \ + int ret = kvm_set_one_reg(cs, RISCV_TIMER_REG(name), ®); \ + if (ret) { \ + abort(); \ + } \ + } while (0) + +typedef struct KVMCPUConfig { + const char *name; + const char *description; + target_ulong offset; + uint64_t kvm_reg_id; + bool user_set; + bool supported; +} KVMCPUConfig; + +#define KVM_MISA_CFG(_bit, _reg_id) \ + {.offset = _bit, .kvm_reg_id = _reg_id} + +/* KVM ISA extensions */ +static KVMCPUConfig kvm_misa_ext_cfgs[] = { + KVM_MISA_CFG(RVA, KVM_RISCV_ISA_EXT_A), + KVM_MISA_CFG(RVC, KVM_RISCV_ISA_EXT_C), + KVM_MISA_CFG(RVD, KVM_RISCV_ISA_EXT_D), + KVM_MISA_CFG(RVF, KVM_RISCV_ISA_EXT_F), + KVM_MISA_CFG(RVH, KVM_RISCV_ISA_EXT_H), + KVM_MISA_CFG(RVI, KVM_RISCV_ISA_EXT_I), + KVM_MISA_CFG(RVM, KVM_RISCV_ISA_EXT_M), + KVM_MISA_CFG(RVV, KVM_RISCV_ISA_EXT_V), +}; + +static void kvm_cpu_get_misa_ext_cfg(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + KVMCPUConfig *misa_ext_cfg = opaque; + target_ulong misa_bit = misa_ext_cfg->offset; + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + bool value = env->misa_ext_mask & misa_bit; + + visit_type_bool(v, name, &value, errp); +} + +static void kvm_cpu_set_misa_ext_cfg(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + KVMCPUConfig *misa_ext_cfg = opaque; + target_ulong misa_bit = misa_ext_cfg->offset; + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + bool value, host_bit; + + if (!visit_type_bool(v, name, &value, errp)) { + return; + } + + host_bit = env->misa_ext_mask & misa_bit; + + if (value == host_bit) { + return; + } + + if (!value) { + misa_ext_cfg->user_set = true; + return; + } + + /* + * Forbid users to enable extensions that aren't + * available in the hart. + */ + error_setg(errp, "Enabling MISA bit '%s' is not allowed: it's not " + "enabled in the host", misa_ext_cfg->name); +} + +static void kvm_riscv_update_cpu_misa_ext(RISCVCPU *cpu, CPUState *cs) +{ + CPURISCVState *env = &cpu->env; + uint64_t id, reg; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(kvm_misa_ext_cfgs); i++) { + KVMCPUConfig *misa_cfg = &kvm_misa_ext_cfgs[i]; + target_ulong misa_bit = misa_cfg->offset; + + if (!misa_cfg->user_set) { + continue; + } + + /* If we're here we're going to disable the MISA bit */ + reg = 0; + id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_ISA_EXT, + misa_cfg->kvm_reg_id); + ret = kvm_set_one_reg(cs, id, ®); + if (ret != 0) { + /* + * We're not checking for -EINVAL because if the bit is about + * to be disabled, it means that it was already enabled by + * KVM. We determined that by fetching the 'isa' register + * during init() time. Any error at this point is worth + * aborting. + */ + error_report("Unable to set KVM reg %s, error %d", + misa_cfg->name, ret); + exit(EXIT_FAILURE); + } + env->misa_ext &= ~misa_bit; + } +} + +#define KVM_EXT_CFG(_name, _prop, _reg_id) \ + {.name = _name, .offset = CPU_CFG_OFFSET(_prop), \ + .kvm_reg_id = _reg_id} + +static KVMCPUConfig kvm_multi_ext_cfgs[] = { + KVM_EXT_CFG("zicbom", ext_zicbom, KVM_RISCV_ISA_EXT_ZICBOM), + KVM_EXT_CFG("zicboz", ext_zicboz, KVM_RISCV_ISA_EXT_ZICBOZ), + KVM_EXT_CFG("zicntr", ext_zicntr, KVM_RISCV_ISA_EXT_ZICNTR), + KVM_EXT_CFG("zicond", ext_zicond, KVM_RISCV_ISA_EXT_ZICOND), + KVM_EXT_CFG("zicsr", ext_zicsr, KVM_RISCV_ISA_EXT_ZICSR), + KVM_EXT_CFG("zifencei", ext_zifencei, KVM_RISCV_ISA_EXT_ZIFENCEI), + KVM_EXT_CFG("zihintntl", ext_zihintntl, KVM_RISCV_ISA_EXT_ZIHINTNTL), + KVM_EXT_CFG("zihintpause", ext_zihintpause, KVM_RISCV_ISA_EXT_ZIHINTPAUSE), + KVM_EXT_CFG("zihpm", ext_zihpm, KVM_RISCV_ISA_EXT_ZIHPM), + KVM_EXT_CFG("zfa", ext_zfa, KVM_RISCV_ISA_EXT_ZFA), + KVM_EXT_CFG("zfh", ext_zfh, KVM_RISCV_ISA_EXT_ZFH), + KVM_EXT_CFG("zfhmin", ext_zfhmin, KVM_RISCV_ISA_EXT_ZFHMIN), + KVM_EXT_CFG("zba", ext_zba, KVM_RISCV_ISA_EXT_ZBA), + KVM_EXT_CFG("zbb", ext_zbb, KVM_RISCV_ISA_EXT_ZBB), + KVM_EXT_CFG("zbc", ext_zbc, KVM_RISCV_ISA_EXT_ZBC), + KVM_EXT_CFG("zbkb", ext_zbkb, KVM_RISCV_ISA_EXT_ZBKB), + KVM_EXT_CFG("zbkc", ext_zbkc, KVM_RISCV_ISA_EXT_ZBKC), + KVM_EXT_CFG("zbkx", ext_zbkx, KVM_RISCV_ISA_EXT_ZBKX), + KVM_EXT_CFG("zbs", ext_zbs, KVM_RISCV_ISA_EXT_ZBS), + KVM_EXT_CFG("zknd", ext_zknd, KVM_RISCV_ISA_EXT_ZKND), + KVM_EXT_CFG("zkne", ext_zkne, KVM_RISCV_ISA_EXT_ZKNE), + KVM_EXT_CFG("zknh", ext_zknh, KVM_RISCV_ISA_EXT_ZKNH), + KVM_EXT_CFG("zkr", ext_zkr, KVM_RISCV_ISA_EXT_ZKR), + KVM_EXT_CFG("zksed", ext_zksed, KVM_RISCV_ISA_EXT_ZKSED), + KVM_EXT_CFG("zksh", ext_zksh, KVM_RISCV_ISA_EXT_ZKSH), + KVM_EXT_CFG("zkt", ext_zkt, KVM_RISCV_ISA_EXT_ZKT), + KVM_EXT_CFG("zvbb", ext_zvbb, KVM_RISCV_ISA_EXT_ZVBB), + KVM_EXT_CFG("zvbc", ext_zvbc, KVM_RISCV_ISA_EXT_ZVBC), + KVM_EXT_CFG("zvfh", ext_zvfh, KVM_RISCV_ISA_EXT_ZVFH), + KVM_EXT_CFG("zvfhmin", ext_zvfhmin, KVM_RISCV_ISA_EXT_ZVFHMIN), + KVM_EXT_CFG("zvkb", ext_zvkb, KVM_RISCV_ISA_EXT_ZVKB), + KVM_EXT_CFG("zvkg", ext_zvkg, KVM_RISCV_ISA_EXT_ZVKG), + KVM_EXT_CFG("zvkned", ext_zvkned, KVM_RISCV_ISA_EXT_ZVKNED), + KVM_EXT_CFG("zvknha", ext_zvknha, KVM_RISCV_ISA_EXT_ZVKNHA), + KVM_EXT_CFG("zvknhb", ext_zvknhb, KVM_RISCV_ISA_EXT_ZVKNHB), + KVM_EXT_CFG("zvksed", ext_zvksed, KVM_RISCV_ISA_EXT_ZVKSED), + KVM_EXT_CFG("zvksh", ext_zvksh, KVM_RISCV_ISA_EXT_ZVKSH), + KVM_EXT_CFG("zvkt", ext_zvkt, KVM_RISCV_ISA_EXT_ZVKT), + KVM_EXT_CFG("smstateen", ext_smstateen, KVM_RISCV_ISA_EXT_SMSTATEEN), + KVM_EXT_CFG("ssaia", ext_ssaia, KVM_RISCV_ISA_EXT_SSAIA), + KVM_EXT_CFG("sstc", ext_sstc, KVM_RISCV_ISA_EXT_SSTC), + KVM_EXT_CFG("svinval", ext_svinval, KVM_RISCV_ISA_EXT_SVINVAL), + KVM_EXT_CFG("svnapot", ext_svnapot, KVM_RISCV_ISA_EXT_SVNAPOT), + KVM_EXT_CFG("svpbmt", ext_svpbmt, KVM_RISCV_ISA_EXT_SVPBMT), +}; + +static void *kvmconfig_get_cfg_addr(RISCVCPU *cpu, KVMCPUConfig *kvmcfg) +{ + return (void *)&cpu->cfg + kvmcfg->offset; +} + +static void kvm_cpu_cfg_set(RISCVCPU *cpu, KVMCPUConfig *multi_ext, + uint32_t val) +{ + bool *ext_enabled = kvmconfig_get_cfg_addr(cpu, multi_ext); + + *ext_enabled = val; +} + +static uint32_t kvm_cpu_cfg_get(RISCVCPU *cpu, + KVMCPUConfig *multi_ext) +{ + bool *ext_enabled = kvmconfig_get_cfg_addr(cpu, multi_ext); + + return *ext_enabled; +} + +static void kvm_cpu_get_multi_ext_cfg(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + KVMCPUConfig *multi_ext_cfg = opaque; + RISCVCPU *cpu = RISCV_CPU(obj); + bool value = kvm_cpu_cfg_get(cpu, multi_ext_cfg); + + visit_type_bool(v, name, &value, errp); +} + +static void kvm_cpu_set_multi_ext_cfg(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + KVMCPUConfig *multi_ext_cfg = opaque; + RISCVCPU *cpu = RISCV_CPU(obj); + bool value, host_val; + + if (!visit_type_bool(v, name, &value, errp)) { + return; + } + + host_val = kvm_cpu_cfg_get(cpu, multi_ext_cfg); + + /* + * Ignore if the user is setting the same value + * as the host. + */ + if (value == host_val) { + return; + } + + if (!multi_ext_cfg->supported) { + /* + * Error out if the user is trying to enable an + * extension that KVM doesn't support. Ignore + * option otherwise. + */ + if (value) { + error_setg(errp, "KVM does not support disabling extension %s", + multi_ext_cfg->name); + } + + return; + } + + multi_ext_cfg->user_set = true; + kvm_cpu_cfg_set(cpu, multi_ext_cfg, value); +} + +static KVMCPUConfig kvm_cbom_blocksize = { + .name = "cbom_blocksize", + .offset = CPU_CFG_OFFSET(cbom_blocksize), + .kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicbom_block_size) +}; + +static KVMCPUConfig kvm_cboz_blocksize = { + .name = "cboz_blocksize", + .offset = CPU_CFG_OFFSET(cboz_blocksize), + .kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicboz_block_size) +}; + +static KVMCPUConfig kvm_v_vlenb = { + .name = "vlenb", + .offset = CPU_CFG_OFFSET(vlenb), + .kvm_reg_id = KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_VECTOR | + KVM_REG_RISCV_VECTOR_CSR_REG(vlenb) +}; + +static void kvm_riscv_update_cpu_cfg_isa_ext(RISCVCPU *cpu, CPUState *cs) +{ + CPURISCVState *env = &cpu->env; + uint64_t id, reg; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) { + KVMCPUConfig *multi_ext_cfg = &kvm_multi_ext_cfgs[i]; + + if (!multi_ext_cfg->user_set) { + continue; + } + + id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_ISA_EXT, + multi_ext_cfg->kvm_reg_id); + reg = kvm_cpu_cfg_get(cpu, multi_ext_cfg); + ret = kvm_set_one_reg(cs, id, ®); + if (ret != 0) { + error_report("Unable to %s extension %s in KVM, error %d", + reg ? "enable" : "disable", + multi_ext_cfg->name, ret); + exit(EXIT_FAILURE); + } + } +} + +static void cpu_get_cfg_unavailable(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + bool value = false; + + visit_type_bool(v, name, &value, errp); +} + +static void cpu_set_cfg_unavailable(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + const char *propname = opaque; + bool value; + + if (!visit_type_bool(v, name, &value, errp)) { + return; + } + + if (value) { + error_setg(errp, "'%s' is not available with KVM", + propname); + } +} + +static void riscv_cpu_add_kvm_unavail_prop(Object *obj, const char *prop_name) +{ + /* Check if KVM created the property already */ + if (object_property_find(obj, prop_name)) { + return; + } + + /* + * Set the default to disabled for every extension + * unknown to KVM and error out if the user attempts + * to enable any of them. + */ + object_property_add(obj, prop_name, "bool", + cpu_get_cfg_unavailable, + cpu_set_cfg_unavailable, + NULL, (void *)prop_name); +} + +static void riscv_cpu_add_kvm_unavail_prop_array(Object *obj, + const RISCVCPUMultiExtConfig *array) +{ + const RISCVCPUMultiExtConfig *prop; + + g_assert(array); + + for (prop = array; prop && prop->name; prop++) { + riscv_cpu_add_kvm_unavail_prop(obj, prop->name); + } +} + +static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj) +{ + int i; + + riscv_add_satp_mode_properties(cpu_obj); + + for (i = 0; i < ARRAY_SIZE(kvm_misa_ext_cfgs); i++) { + KVMCPUConfig *misa_cfg = &kvm_misa_ext_cfgs[i]; + int bit = misa_cfg->offset; + + misa_cfg->name = riscv_get_misa_ext_name(bit); + misa_cfg->description = riscv_get_misa_ext_description(bit); + + object_property_add(cpu_obj, misa_cfg->name, "bool", + kvm_cpu_get_misa_ext_cfg, + kvm_cpu_set_misa_ext_cfg, + NULL, misa_cfg); + object_property_set_description(cpu_obj, misa_cfg->name, + misa_cfg->description); + } + + for (i = 0; misa_bits[i] != 0; i++) { + const char *ext_name = riscv_get_misa_ext_name(misa_bits[i]); + riscv_cpu_add_kvm_unavail_prop(cpu_obj, ext_name); + } + + for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) { + KVMCPUConfig *multi_cfg = &kvm_multi_ext_cfgs[i]; + + object_property_add(cpu_obj, multi_cfg->name, "bool", + kvm_cpu_get_multi_ext_cfg, + kvm_cpu_set_multi_ext_cfg, + NULL, multi_cfg); + } + + riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_extensions); + riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_vendor_exts); + riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_experimental_exts); + + /* We don't have the needed KVM support for profiles */ + for (i = 0; riscv_profiles[i] != NULL; i++) { + riscv_cpu_add_kvm_unavail_prop(cpu_obj, riscv_profiles[i]->name); + } +} + +static int kvm_riscv_get_regs_core(CPUState *cs) +{ + int ret = 0; + int i; + target_ulong reg; + CPURISCVState *env = &RISCV_CPU(cs)->env; + + ret = kvm_get_one_reg(cs, RISCV_CORE_REG(env, regs.pc), ®); + if (ret) { + return ret; + } + env->pc = reg; + + for (i = 1; i < 32; i++) { + uint64_t id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CORE, i); + ret = kvm_get_one_reg(cs, id, ®); + if (ret) { + return ret; + } + env->gpr[i] = reg; + } + + return ret; +} + +static int kvm_riscv_put_regs_core(CPUState *cs) +{ + int ret = 0; + int i; + target_ulong reg; + CPURISCVState *env = &RISCV_CPU(cs)->env; + + reg = env->pc; + ret = kvm_set_one_reg(cs, RISCV_CORE_REG(env, regs.pc), ®); + if (ret) { + return ret; + } + + for (i = 1; i < 32; i++) { + uint64_t id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CORE, i); + reg = env->gpr[i]; + ret = kvm_set_one_reg(cs, id, ®); + if (ret) { + return ret; + } + } + + return ret; +} + +static int kvm_riscv_get_regs_csr(CPUState *cs) +{ + CPURISCVState *env = &RISCV_CPU(cs)->env; + + KVM_RISCV_GET_CSR(cs, env, sstatus, env->mstatus); + KVM_RISCV_GET_CSR(cs, env, sie, env->mie); + KVM_RISCV_GET_CSR(cs, env, stvec, env->stvec); + KVM_RISCV_GET_CSR(cs, env, sscratch, env->sscratch); + KVM_RISCV_GET_CSR(cs, env, sepc, env->sepc); + KVM_RISCV_GET_CSR(cs, env, scause, env->scause); + KVM_RISCV_GET_CSR(cs, env, stval, env->stval); + KVM_RISCV_GET_CSR(cs, env, sip, env->mip); + KVM_RISCV_GET_CSR(cs, env, satp, env->satp); + + return 0; +} + +static int kvm_riscv_put_regs_csr(CPUState *cs) +{ + CPURISCVState *env = &RISCV_CPU(cs)->env; + + KVM_RISCV_SET_CSR(cs, env, sstatus, env->mstatus); + KVM_RISCV_SET_CSR(cs, env, sie, env->mie); + KVM_RISCV_SET_CSR(cs, env, stvec, env->stvec); + KVM_RISCV_SET_CSR(cs, env, sscratch, env->sscratch); + KVM_RISCV_SET_CSR(cs, env, sepc, env->sepc); + KVM_RISCV_SET_CSR(cs, env, scause, env->scause); + KVM_RISCV_SET_CSR(cs, env, stval, env->stval); + KVM_RISCV_SET_CSR(cs, env, sip, env->mip); + KVM_RISCV_SET_CSR(cs, env, satp, env->satp); + + return 0; +} + +static int kvm_riscv_get_regs_fp(CPUState *cs) +{ + int ret = 0; + int i; + CPURISCVState *env = &RISCV_CPU(cs)->env; + + if (riscv_has_ext(env, RVD)) { + uint64_t reg; + for (i = 0; i < 32; i++) { + ret = kvm_get_one_reg(cs, RISCV_FP_D_REG(i), ®); + if (ret) { + return ret; + } + env->fpr[i] = reg; + } + return ret; + } + + if (riscv_has_ext(env, RVF)) { + uint32_t reg; + for (i = 0; i < 32; i++) { + ret = kvm_get_one_reg(cs, RISCV_FP_F_REG(i), ®); + if (ret) { + return ret; + } + env->fpr[i] = reg; + } + return ret; + } + + return ret; +} + +static int kvm_riscv_put_regs_fp(CPUState *cs) +{ + int ret = 0; + int i; + CPURISCVState *env = &RISCV_CPU(cs)->env; + + if (riscv_has_ext(env, RVD)) { + uint64_t reg; + for (i = 0; i < 32; i++) { + reg = env->fpr[i]; + ret = kvm_set_one_reg(cs, RISCV_FP_D_REG(i), ®); + if (ret) { + return ret; + } + } + return ret; + } + + if (riscv_has_ext(env, RVF)) { + uint32_t reg; + for (i = 0; i < 32; i++) { + reg = env->fpr[i]; + ret = kvm_set_one_reg(cs, RISCV_FP_F_REG(i), ®); + if (ret) { + return ret; + } + } + return ret; + } + + return ret; +} + +static void kvm_riscv_get_regs_timer(CPUState *cs) +{ + CPURISCVState *env = &RISCV_CPU(cs)->env; + + if (env->kvm_timer_dirty) { + return; + } + + KVM_RISCV_GET_TIMER(cs, time, env->kvm_timer_time); + KVM_RISCV_GET_TIMER(cs, compare, env->kvm_timer_compare); + KVM_RISCV_GET_TIMER(cs, state, env->kvm_timer_state); + KVM_RISCV_GET_TIMER(cs, frequency, env->kvm_timer_frequency); + + env->kvm_timer_dirty = true; +} + +static void kvm_riscv_put_regs_timer(CPUState *cs) +{ + uint64_t reg; + CPURISCVState *env = &RISCV_CPU(cs)->env; + + if (!env->kvm_timer_dirty) { + return; + } + + KVM_RISCV_SET_TIMER(cs, time, env->kvm_timer_time); + KVM_RISCV_SET_TIMER(cs, compare, env->kvm_timer_compare); + + /* + * To set register of RISCV_TIMER_REG(state) will occur a error from KVM + * on env->kvm_timer_state == 0, It's better to adapt in KVM, but it + * doesn't matter that adaping in QEMU now. + * TODO If KVM changes, adapt here. + */ + if (env->kvm_timer_state) { + KVM_RISCV_SET_TIMER(cs, state, env->kvm_timer_state); + } + + /* + * For now, migration will not work between Hosts with different timer + * frequency. Therefore, we should check whether they are the same here + * during the migration. + */ + if (migration_is_running()) { + KVM_RISCV_GET_TIMER(cs, frequency, reg); + if (reg != env->kvm_timer_frequency) { + error_report("Dst Hosts timer frequency != Src Hosts"); + } + } + + env->kvm_timer_dirty = false; +} + +uint64_t kvm_riscv_get_timebase_frequency(CPUState *cs) +{ + uint64_t reg; + + KVM_RISCV_GET_TIMER(cs, frequency, reg); + + return reg; +} + +static int kvm_riscv_get_regs_vector(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + target_ulong reg; + uint64_t vreg_id; + int vreg_idx, ret = 0; + + if (!riscv_has_ext(env, RVV)) { + return 0; + } + + ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vstart), ®); + if (ret) { + return ret; + } + env->vstart = reg; + + ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vl), ®); + if (ret) { + return ret; + } + env->vl = reg; + + ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vtype), ®); + if (ret) { + return ret; + } + env->vtype = reg; + + if (kvm_v_vlenb.supported) { + ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vlenb), ®); + if (ret) { + return ret; + } + cpu->cfg.vlenb = reg; + + for (int i = 0; i < 32; i++) { + /* + * vreg[] is statically allocated using RV_VLEN_MAX. + * Use it instead of vlenb to calculate vreg_idx for + * simplicity. + */ + vreg_idx = i * RV_VLEN_MAX / 64; + vreg_id = kvm_riscv_vector_reg_id(cpu, i); + + ret = kvm_get_one_reg(cs, vreg_id, &env->vreg[vreg_idx]); + if (ret) { + return ret; + } + } + } + + return 0; +} + +static int kvm_riscv_put_regs_vector(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + target_ulong reg; + uint64_t vreg_id; + int vreg_idx, ret = 0; + + if (!riscv_has_ext(env, RVV)) { + return 0; + } + + reg = env->vstart; + ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vstart), ®); + if (ret) { + return ret; + } + + reg = env->vl; + ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vl), ®); + if (ret) { + return ret; + } + + reg = env->vtype; + ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vtype), ®); + if (ret) { + return ret; + } + + if (kvm_v_vlenb.supported) { + reg = cpu->cfg.vlenb; + ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vlenb), ®); + + for (int i = 0; i < 32; i++) { + /* + * vreg[] is statically allocated using RV_VLEN_MAX. + * Use it instead of vlenb to calculate vreg_idx for + * simplicity. + */ + vreg_idx = i * RV_VLEN_MAX / 64; + vreg_id = kvm_riscv_vector_reg_id(cpu, i); + + ret = kvm_set_one_reg(cs, vreg_id, &env->vreg[vreg_idx]); + if (ret) { + return ret; + } + } + } + + return ret; +} + +typedef struct KVMScratchCPU { + int kvmfd; + int vmfd; + int cpufd; +} KVMScratchCPU; + +/* + * Heavily inspired by kvm_arm_create_scratch_host_vcpu() + * from target/arm/kvm.c. + */ +static bool kvm_riscv_create_scratch_vcpu(KVMScratchCPU *scratch) +{ + int kvmfd = -1, vmfd = -1, cpufd = -1; + + kvmfd = qemu_open_old("/dev/kvm", O_RDWR); + if (kvmfd < 0) { + goto err; + } + do { + vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0); + } while (vmfd == -1 && errno == EINTR); + if (vmfd < 0) { + goto err; + } + cpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0); + if (cpufd < 0) { + goto err; + } + + scratch->kvmfd = kvmfd; + scratch->vmfd = vmfd; + scratch->cpufd = cpufd; + + return true; + + err: + if (cpufd >= 0) { + close(cpufd); + } + if (vmfd >= 0) { + close(vmfd); + } + if (kvmfd >= 0) { + close(kvmfd); + } + + return false; +} + +static void kvm_riscv_destroy_scratch_vcpu(KVMScratchCPU *scratch) +{ + close(scratch->cpufd); + close(scratch->vmfd); + close(scratch->kvmfd); +} + +static void kvm_riscv_init_machine_ids(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) +{ + CPURISCVState *env = &cpu->env; + struct kvm_one_reg reg; + int ret; + + reg.id = RISCV_CONFIG_REG(env, mvendorid); + reg.addr = (uint64_t)&cpu->cfg.mvendorid; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to retrieve mvendorid from host, error %d", ret); + } + + reg.id = RISCV_CONFIG_REG(env, marchid); + reg.addr = (uint64_t)&cpu->cfg.marchid; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to retrieve marchid from host, error %d", ret); + } + + reg.id = RISCV_CONFIG_REG(env, mimpid); + reg.addr = (uint64_t)&cpu->cfg.mimpid; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to retrieve mimpid from host, error %d", ret); + } +} + +static void kvm_riscv_init_misa_ext_mask(RISCVCPU *cpu, + KVMScratchCPU *kvmcpu) +{ + CPURISCVState *env = &cpu->env; + struct kvm_one_reg reg; + int ret; + + reg.id = RISCV_CONFIG_REG(env, isa); + reg.addr = (uint64_t)&env->misa_ext_mask; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + + if (ret) { + error_report("Unable to fetch ISA register from KVM, " + "error %d", ret); + kvm_riscv_destroy_scratch_vcpu(kvmcpu); + exit(EXIT_FAILURE); + } + + env->misa_ext = env->misa_ext_mask; +} + +static void kvm_riscv_read_cbomz_blksize(RISCVCPU *cpu, KVMScratchCPU *kvmcpu, + KVMCPUConfig *cbomz_cfg) +{ + CPURISCVState *env = &cpu->env; + struct kvm_one_reg reg; + int ret; + + reg.id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CONFIG, + cbomz_cfg->kvm_reg_id); + reg.addr = (uint64_t)kvmconfig_get_cfg_addr(cpu, cbomz_cfg); + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to read KVM reg %s, error %d", + cbomz_cfg->name, ret); + exit(EXIT_FAILURE); + } +} + +static void kvm_riscv_read_multiext_legacy(RISCVCPU *cpu, + KVMScratchCPU *kvmcpu) +{ + CPURISCVState *env = &cpu->env; + uint64_t val; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) { + KVMCPUConfig *multi_ext_cfg = &kvm_multi_ext_cfgs[i]; + struct kvm_one_reg reg; + + reg.id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_ISA_EXT, + multi_ext_cfg->kvm_reg_id); + reg.addr = (uint64_t)&val; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + if (errno == EINVAL) { + /* Silently default to 'false' if KVM does not support it. */ + multi_ext_cfg->supported = false; + val = false; + } else { + error_report("Unable to read ISA_EXT KVM register %s: %s", + multi_ext_cfg->name, strerror(errno)); + exit(EXIT_FAILURE); + } + } else { + multi_ext_cfg->supported = true; + } + + kvm_cpu_cfg_set(cpu, multi_ext_cfg, val); + } + + if (cpu->cfg.ext_zicbom) { + kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cbom_blocksize); + } + + if (cpu->cfg.ext_zicboz) { + kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cboz_blocksize); + } +} + +static int uint64_cmp(const void *a, const void *b) +{ + uint64_t val1 = *(const uint64_t *)a; + uint64_t val2 = *(const uint64_t *)b; + + if (val1 < val2) { + return -1; + } + + if (val1 > val2) { + return 1; + } + + return 0; +} + +static void kvm_riscv_read_vlenb(RISCVCPU *cpu, KVMScratchCPU *kvmcpu, + struct kvm_reg_list *reglist) +{ + struct kvm_one_reg reg; + struct kvm_reg_list *reg_search; + uint64_t val; + int ret; + + reg_search = bsearch(&kvm_v_vlenb.kvm_reg_id, reglist->reg, reglist->n, + sizeof(uint64_t), uint64_cmp); + + if (reg_search) { + reg.id = kvm_v_vlenb.kvm_reg_id; + reg.addr = (uint64_t)&val; + + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to read vlenb register, error code: %s", + strerrorname_np(errno)); + exit(EXIT_FAILURE); + } + + kvm_v_vlenb.supported = true; + cpu->cfg.vlenb = val; + } +} + +static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) +{ + KVMCPUConfig *multi_ext_cfg; + struct kvm_one_reg reg; + struct kvm_reg_list rl_struct; + struct kvm_reg_list *reglist; + uint64_t val, reg_id, *reg_search; + int i, ret; + + rl_struct.n = 0; + ret = ioctl(kvmcpu->cpufd, KVM_GET_REG_LIST, &rl_struct); + + /* + * If KVM_GET_REG_LIST isn't supported we'll get errno 22 + * (EINVAL). Use read_legacy() in this case. + */ + if (errno == EINVAL) { + return kvm_riscv_read_multiext_legacy(cpu, kvmcpu); + } else if (errno != E2BIG) { + /* + * E2BIG is an expected error message for the API since we + * don't know the number of registers. The right amount will + * be written in rl_struct.n. + * + * Error out if we get any other errno. + */ + error_report("Error when accessing get-reg-list: %s", + strerror(errno)); + exit(EXIT_FAILURE); + } + + reglist = g_malloc(sizeof(struct kvm_reg_list) + + rl_struct.n * sizeof(uint64_t)); + reglist->n = rl_struct.n; + ret = ioctl(kvmcpu->cpufd, KVM_GET_REG_LIST, reglist); + if (ret) { + error_report("Error when reading KVM_GET_REG_LIST: %s", + strerror(errno)); + exit(EXIT_FAILURE); + } + + /* sort reglist to use bsearch() */ + qsort(®list->reg, reglist->n, sizeof(uint64_t), uint64_cmp); + + for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) { + multi_ext_cfg = &kvm_multi_ext_cfgs[i]; + reg_id = kvm_riscv_reg_id_ulong(&cpu->env, KVM_REG_RISCV_ISA_EXT, + multi_ext_cfg->kvm_reg_id); + reg_search = bsearch(®_id, reglist->reg, reglist->n, + sizeof(uint64_t), uint64_cmp); + if (!reg_search) { + continue; + } + + reg.id = reg_id; + reg.addr = (uint64_t)&val; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to read ISA_EXT KVM register %s: %s", + multi_ext_cfg->name, strerror(errno)); + exit(EXIT_FAILURE); + } + + multi_ext_cfg->supported = true; + kvm_cpu_cfg_set(cpu, multi_ext_cfg, val); + } + + if (cpu->cfg.ext_zicbom) { + kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cbom_blocksize); + } + + if (cpu->cfg.ext_zicboz) { + kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cboz_blocksize); + } + + if (riscv_has_ext(&cpu->env, RVV)) { + kvm_riscv_read_vlenb(cpu, kvmcpu, reglist); + } +} + +static void riscv_init_kvm_registers(Object *cpu_obj) +{ + RISCVCPU *cpu = RISCV_CPU(cpu_obj); + KVMScratchCPU kvmcpu; + + if (!kvm_riscv_create_scratch_vcpu(&kvmcpu)) { + return; + } + + kvm_riscv_init_machine_ids(cpu, &kvmcpu); + kvm_riscv_init_misa_ext_mask(cpu, &kvmcpu); + kvm_riscv_init_multiext_cfg(cpu, &kvmcpu); + + kvm_riscv_destroy_scratch_vcpu(&kvmcpu); +} + +const KVMCapabilityInfo kvm_arch_required_capabilities[] = { + KVM_CAP_LAST_INFO +}; + +int kvm_arch_get_registers(CPUState *cs) +{ + int ret = 0; + + ret = kvm_riscv_get_regs_core(cs); + if (ret) { + return ret; + } + + ret = kvm_riscv_get_regs_csr(cs); + if (ret) { + return ret; + } + + ret = kvm_riscv_get_regs_fp(cs); + if (ret) { + return ret; + } + + ret = kvm_riscv_get_regs_vector(cs); + if (ret) { + return ret; + } + + return ret; +} + +int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state) +{ + if (cap_has_mp_state) { + struct kvm_mp_state mp_state = { + .mp_state = state + }; + + int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state); + if (ret) { + fprintf(stderr, "%s: failed to sync MP_STATE %d/%s\n", + __func__, ret, strerror(-ret)); + return -1; + } + } + + return 0; +} + +int kvm_arch_put_registers(CPUState *cs, int level) +{ + int ret = 0; + + ret = kvm_riscv_put_regs_core(cs); + if (ret) { + return ret; + } + + ret = kvm_riscv_put_regs_csr(cs); + if (ret) { + return ret; + } + + ret = kvm_riscv_put_regs_fp(cs); + if (ret) { + return ret; + } + + ret = kvm_riscv_put_regs_vector(cs); + if (ret) { + return ret; + } + + if (KVM_PUT_RESET_STATE == level) { + RISCVCPU *cpu = RISCV_CPU(cs); + if (cs->cpu_index == 0) { + ret = kvm_riscv_sync_mpstate_to_kvm(cpu, KVM_MP_STATE_RUNNABLE); + } else { + ret = kvm_riscv_sync_mpstate_to_kvm(cpu, KVM_MP_STATE_STOPPED); + } + if (ret) { + return ret; + } + } + + return ret; +} + +int kvm_arch_release_virq_post(int virq) +{ + return 0; +} + +int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, + uint64_t address, uint32_t data, PCIDevice *dev) +{ + return 0; +} + +int kvm_arch_destroy_vcpu(CPUState *cs) +{ + return 0; +} + +unsigned long kvm_arch_vcpu_id(CPUState *cpu) +{ + return cpu->cpu_index; +} + +static void kvm_riscv_vm_state_change(void *opaque, bool running, + RunState state) +{ + CPUState *cs = opaque; + + if (running) { + kvm_riscv_put_regs_timer(cs); + } else { + kvm_riscv_get_regs_timer(cs); + } +} + +void kvm_arch_init_irq_routing(KVMState *s) +{ +} + +static int kvm_vcpu_set_machine_ids(RISCVCPU *cpu, CPUState *cs) +{ + CPURISCVState *env = &cpu->env; + target_ulong reg; + uint64_t id; + int ret; + + id = RISCV_CONFIG_REG(env, mvendorid); + /* + * cfg.mvendorid is an uint32 but a target_ulong will + * be written. Assign it to a target_ulong var to avoid + * writing pieces of other cpu->cfg fields in the reg. + */ + reg = cpu->cfg.mvendorid; + ret = kvm_set_one_reg(cs, id, ®); + if (ret != 0) { + return ret; + } + + id = RISCV_CONFIG_REG(env, marchid); + ret = kvm_set_one_reg(cs, id, &cpu->cfg.marchid); + if (ret != 0) { + return ret; + } + + id = RISCV_CONFIG_REG(env, mimpid); + ret = kvm_set_one_reg(cs, id, &cpu->cfg.mimpid); + + return ret; +} + +int kvm_arch_init_vcpu(CPUState *cs) +{ + int ret = 0; + RISCVCPU *cpu = RISCV_CPU(cs); + + qemu_add_vm_change_state_handler(kvm_riscv_vm_state_change, cs); + + if (!object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST)) { + ret = kvm_vcpu_set_machine_ids(cpu, cs); + if (ret != 0) { + return ret; + } + } + + kvm_riscv_update_cpu_misa_ext(cpu, cs); + kvm_riscv_update_cpu_cfg_isa_ext(cpu, cs); + + return ret; +} + +int kvm_arch_msi_data_to_gsi(uint32_t data) +{ + abort(); +} + +int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route, + int vector, PCIDevice *dev) +{ + return 0; +} + +int kvm_arch_get_default_type(MachineState *ms) +{ + return 0; +} + +int kvm_arch_init(MachineState *ms, KVMState *s) +{ + cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE); + return 0; +} + +int kvm_arch_irqchip_create(KVMState *s) +{ + if (kvm_kernel_irqchip_split()) { + error_report("-machine kernel_irqchip=split is not supported on RISC-V."); + exit(1); + } + + /* + * We can create the VAIA using the newer device control API. + */ + return kvm_check_extension(s, KVM_CAP_DEVICE_CTRL); +} + +int kvm_arch_process_async_events(CPUState *cs) +{ + return 0; +} + +void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) +{ +} + +MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) +{ + return MEMTXATTRS_UNSPECIFIED; +} + +bool kvm_arch_stop_on_emulation_error(CPUState *cs) +{ + return true; +} + +static int kvm_riscv_handle_sbi(CPUState *cs, struct kvm_run *run) +{ + int ret = 0; + unsigned char ch; + switch (run->riscv_sbi.extension_id) { + case SBI_EXT_0_1_CONSOLE_PUTCHAR: + ch = run->riscv_sbi.args[0]; + qemu_chr_fe_write(serial_hd(0)->be, &ch, sizeof(ch)); + break; + case SBI_EXT_0_1_CONSOLE_GETCHAR: + ret = qemu_chr_fe_read_all(serial_hd(0)->be, &ch, sizeof(ch)); + if (ret == sizeof(ch)) { + run->riscv_sbi.ret[0] = ch; + } else { + run->riscv_sbi.ret[0] = -1; + } + ret = 0; + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s: un-handled SBI EXIT, specific reasons is %lu\n", + __func__, run->riscv_sbi.extension_id); + ret = -1; + break; + } + return ret; +} + +int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) +{ + int ret = 0; + switch (run->exit_reason) { + case KVM_EXIT_RISCV_SBI: + ret = kvm_riscv_handle_sbi(cs, run); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n", + __func__, run->exit_reason); + ret = -1; + break; + } + return ret; +} + +void kvm_riscv_reset_vcpu(RISCVCPU *cpu) +{ + CPURISCVState *env = &cpu->env; + int i; + + if (!kvm_enabled()) { + return; + } + for (i = 0; i < 32; i++) { + env->gpr[i] = 0; + } + env->pc = cpu->env.kernel_addr; + env->gpr[10] = kvm_arch_vcpu_id(CPU(cpu)); /* a0 */ + env->gpr[11] = cpu->env.fdt_addr; /* a1 */ + env->satp = 0; + env->mie = 0; + env->stvec = 0; + env->sscratch = 0; + env->sepc = 0; + env->scause = 0; + env->stval = 0; + env->mip = 0; +} + +void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level) +{ + int ret; + unsigned virq = level ? KVM_INTERRUPT_SET : KVM_INTERRUPT_UNSET; + + if (irq != IRQ_S_EXT) { + perror("kvm riscv set irq != IRQ_S_EXT\n"); + abort(); + } + + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_INTERRUPT, &virq); + if (ret < 0) { + perror("Set irq failed"); + abort(); + } +} + +bool kvm_arch_cpu_check_are_resettable(void) +{ + return true; +} + +static int aia_mode; + +static const char *kvm_aia_mode_str(uint64_t mode) +{ + switch (mode) { + case KVM_DEV_RISCV_AIA_MODE_EMUL: + return "emul"; + case KVM_DEV_RISCV_AIA_MODE_HWACCEL: + return "hwaccel"; + case KVM_DEV_RISCV_AIA_MODE_AUTO: + default: + return "auto"; + }; +} + +static char *riscv_get_kvm_aia(Object *obj, Error **errp) +{ + return g_strdup(kvm_aia_mode_str(aia_mode)); +} + +static void riscv_set_kvm_aia(Object *obj, const char *val, Error **errp) +{ + if (!strcmp(val, "emul")) { + aia_mode = KVM_DEV_RISCV_AIA_MODE_EMUL; + } else if (!strcmp(val, "hwaccel")) { + aia_mode = KVM_DEV_RISCV_AIA_MODE_HWACCEL; + } else if (!strcmp(val, "auto")) { + aia_mode = KVM_DEV_RISCV_AIA_MODE_AUTO; + } else { + error_setg(errp, "Invalid KVM AIA mode"); + error_append_hint(errp, "Valid values are emul, hwaccel, and auto.\n"); + } +} + +void kvm_arch_accel_class_init(ObjectClass *oc) +{ + object_class_property_add_str(oc, "riscv-aia", riscv_get_kvm_aia, + riscv_set_kvm_aia); + object_class_property_set_description(oc, "riscv-aia", + "Set KVM AIA mode. Valid values are " + "emul, hwaccel, and auto. Default " + "is auto."); + object_property_set_default_str(object_class_property_find(oc, "riscv-aia"), + "auto"); +} + +void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, + uint64_t aia_irq_num, uint64_t aia_msi_num, + uint64_t aplic_base, uint64_t imsic_base, + uint64_t guest_num) +{ + int ret, i; + int aia_fd = -1; + uint64_t default_aia_mode; + uint64_t socket_count = riscv_socket_count(machine); + uint64_t max_hart_per_socket = 0; + uint64_t socket, base_hart, hart_count, socket_imsic_base, imsic_addr; + uint64_t socket_bits, hart_bits, guest_bits; + + aia_fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_RISCV_AIA, false); + + if (aia_fd < 0) { + error_report("Unable to create in-kernel irqchip"); + exit(1); + } + + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, + KVM_DEV_RISCV_AIA_CONFIG_MODE, + &default_aia_mode, false, NULL); + if (ret < 0) { + error_report("KVM AIA: failed to get current KVM AIA mode"); + exit(1); + } + qemu_log("KVM AIA: default mode is %s\n", + kvm_aia_mode_str(default_aia_mode)); + + if (default_aia_mode != aia_mode) { + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, + KVM_DEV_RISCV_AIA_CONFIG_MODE, + &aia_mode, true, NULL); + if (ret < 0) + warn_report("KVM AIA: failed to set KVM AIA mode"); + else + qemu_log("KVM AIA: set current mode to %s\n", + kvm_aia_mode_str(aia_mode)); + } + + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, + KVM_DEV_RISCV_AIA_CONFIG_SRCS, + &aia_irq_num, true, NULL); + if (ret < 0) { + error_report("KVM AIA: failed to set number of input irq lines"); + exit(1); + } + + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, + KVM_DEV_RISCV_AIA_CONFIG_IDS, + &aia_msi_num, true, NULL); + if (ret < 0) { + error_report("KVM AIA: failed to set number of msi"); + exit(1); + } + + + if (socket_count > 1) { + socket_bits = find_last_bit(&socket_count, BITS_PER_LONG) + 1; + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, + KVM_DEV_RISCV_AIA_CONFIG_GROUP_BITS, + &socket_bits, true, NULL); + if (ret < 0) { + error_report("KVM AIA: failed to set group_bits"); + exit(1); + } + + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, + KVM_DEV_RISCV_AIA_CONFIG_GROUP_SHIFT, + &group_shift, true, NULL); + if (ret < 0) { + error_report("KVM AIA: failed to set group_shift"); + exit(1); + } + } + + guest_bits = guest_num == 0 ? 0 : + find_last_bit(&guest_num, BITS_PER_LONG) + 1; + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, + KVM_DEV_RISCV_AIA_CONFIG_GUEST_BITS, + &guest_bits, true, NULL); + if (ret < 0) { + error_report("KVM AIA: failed to set guest_bits"); + exit(1); + } + + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_ADDR, + KVM_DEV_RISCV_AIA_ADDR_APLIC, + &aplic_base, true, NULL); + if (ret < 0) { + error_report("KVM AIA: failed to set the base address of APLIC"); + exit(1); + } + + for (socket = 0; socket < socket_count; socket++) { + socket_imsic_base = imsic_base + socket * (1U << group_shift); + hart_count = riscv_socket_hart_count(machine, socket); + base_hart = riscv_socket_first_hartid(machine, socket); + + if (max_hart_per_socket < hart_count) { + max_hart_per_socket = hart_count; + } + + for (i = 0; i < hart_count; i++) { + imsic_addr = socket_imsic_base + i * IMSIC_HART_SIZE(guest_bits); + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_ADDR, + KVM_DEV_RISCV_AIA_ADDR_IMSIC(i + base_hart), + &imsic_addr, true, NULL); + if (ret < 0) { + error_report("KVM AIA: failed to set the IMSIC address for hart %d", i); + exit(1); + } + } + } + + hart_bits = find_last_bit(&max_hart_per_socket, BITS_PER_LONG) + 1; + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, + KVM_DEV_RISCV_AIA_CONFIG_HART_BITS, + &hart_bits, true, NULL); + if (ret < 0) { + error_report("KVM AIA: failed to set hart_bits"); + exit(1); + } + + if (kvm_has_gsi_routing()) { + for (uint64_t idx = 0; idx < aia_irq_num + 1; ++idx) { + /* KVM AIA only has one APLIC instance */ + kvm_irqchip_add_irq_route(kvm_state, idx, 0, idx); + } + kvm_gsi_routing_allowed = true; + kvm_irqchip_commit_routes(kvm_state); + } + + ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CTRL, + KVM_DEV_RISCV_AIA_CTRL_INIT, + NULL, true, NULL); + if (ret < 0) { + error_report("KVM AIA: initialized fail"); + exit(1); + } + + kvm_msi_via_irqfd_allowed = true; +} + +static void kvm_cpu_instance_init(CPUState *cs) +{ + Object *obj = OBJECT(RISCV_CPU(cs)); + + riscv_init_kvm_registers(obj); + + kvm_riscv_add_cpu_user_properties(obj); +} + +/* + * We'll get here via the following path: + * + * riscv_cpu_realize() + * -> cpu_exec_realizefn() + * -> kvm_cpu_realize() (via accel_cpu_common_realize()) + */ +static bool kvm_cpu_realize(CPUState *cs, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + int ret; + + if (riscv_has_ext(&cpu->env, RVV)) { + ret = prctl(PR_RISCV_V_SET_CONTROL, PR_RISCV_V_VSTATE_CTRL_ON); + if (ret) { + error_setg(errp, "Error in prctl PR_RISCV_V_SET_CONTROL, code: %s", + strerrorname_np(errno)); + return false; + } + } + + return true; +} + +void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp) +{ + CPURISCVState *env = &cpu->env; + KVMScratchCPU kvmcpu; + struct kvm_one_reg reg; + uint64_t val; + int ret; + + /* short-circuit without spinning the scratch CPU */ + if (!cpu->cfg.ext_zicbom && !cpu->cfg.ext_zicboz && + !riscv_has_ext(env, RVV)) { + return; + } + + if (!kvm_riscv_create_scratch_vcpu(&kvmcpu)) { + error_setg(errp, "Unable to create scratch KVM cpu"); + return; + } + + if (cpu->cfg.ext_zicbom && + riscv_cpu_option_set(kvm_cbom_blocksize.name)) { + + reg.id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CONFIG, + kvm_cbom_blocksize.kvm_reg_id); + reg.addr = (uint64_t)&val; + ret = ioctl(kvmcpu.cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_setg(errp, "Unable to read cbom_blocksize, error %d", errno); + return; + } + + if (cpu->cfg.cbom_blocksize != val) { + error_setg(errp, "Unable to set cbom_blocksize to a different " + "value than the host (%lu)", val); + return; + } + } + + if (cpu->cfg.ext_zicboz && + riscv_cpu_option_set(kvm_cboz_blocksize.name)) { + + reg.id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CONFIG, + kvm_cboz_blocksize.kvm_reg_id); + reg.addr = (uint64_t)&val; + ret = ioctl(kvmcpu.cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_setg(errp, "Unable to read cboz_blocksize, error %d", errno); + return; + } + + if (cpu->cfg.cboz_blocksize != val) { + error_setg(errp, "Unable to set cboz_blocksize to a different " + "value than the host (%lu)", val); + return; + } + } + + /* Users are setting vlen, not vlenb */ + if (riscv_has_ext(env, RVV) && riscv_cpu_option_set("vlen")) { + if (!kvm_v_vlenb.supported) { + error_setg(errp, "Unable to set 'vlenb': register not supported"); + return; + } + + reg.id = kvm_v_vlenb.kvm_reg_id; + reg.addr = (uint64_t)&val; + ret = ioctl(kvmcpu.cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_setg(errp, "Unable to read vlenb register, error %d", errno); + return; + } + + if (cpu->cfg.vlenb != val) { + error_setg(errp, "Unable to set 'vlen' to a different " + "value than the host (%lu)", val * 8); + return; + } + } + + kvm_riscv_destroy_scratch_vcpu(&kvmcpu); +} + +static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data) +{ + AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); + + acc->cpu_instance_init = kvm_cpu_instance_init; + acc->cpu_target_realize = kvm_cpu_realize; +} + +static const TypeInfo kvm_cpu_accel_type_info = { + .name = ACCEL_CPU_NAME("kvm"), + + .parent = TYPE_ACCEL_CPU, + .class_init = kvm_cpu_accel_class_init, + .abstract = true, +}; +static void kvm_cpu_accel_register_types(void) +{ + type_register_static(&kvm_cpu_accel_type_info); +} +type_init(kvm_cpu_accel_register_types); + +static void riscv_host_cpu_class_init(ObjectClass *c, void *data) +{ + RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); + +#if defined(TARGET_RISCV32) + mcc->misa_mxl_max = MXL_RV32; +#elif defined(TARGET_RISCV64) + mcc->misa_mxl_max = MXL_RV64; +#endif +} + +static const TypeInfo riscv_kvm_cpu_type_infos[] = { + { + .name = TYPE_RISCV_CPU_HOST, + .parent = TYPE_RISCV_CPU, + .class_init = riscv_host_cpu_class_init, + } +}; + +DEFINE_TYPES(riscv_kvm_cpu_type_infos) diff --git a/target/riscv/kvm_riscv.h b/target/riscv/kvm/kvm_riscv.h similarity index 63% rename from target/riscv/kvm_riscv.h rename to target/riscv/kvm/kvm_riscv.h index ed281bdce0..5851898868 100644 --- a/target/riscv/kvm_riscv.h +++ b/target/riscv/kvm/kvm_riscv.h @@ -21,5 +21,13 @@ void kvm_riscv_reset_vcpu(RISCVCPU *cpu); void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level); +void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, + uint64_t aia_irq_num, uint64_t aia_msi_num, + uint64_t aplic_base, uint64_t imsic_base, + uint64_t guest_num); +void riscv_kvm_aplic_request(void *opaque, int irq, int level); +int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state); +void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp); +uint64_t kvm_riscv_get_timebase_frequency(CPUState *cs); #endif diff --git a/target/riscv/kvm/meson.build b/target/riscv/kvm/meson.build new file mode 100644 index 0000000000..7e92415091 --- /dev/null +++ b/target/riscv/kvm/meson.build @@ -0,0 +1 @@ +riscv_ss.add(when: 'CONFIG_KVM', if_true: files('kvm-cpu.c')) diff --git a/target/riscv/m128_helper.c b/target/riscv/m128_helper.c index 7bf115b85e..ec14aaa901 100644 --- a/target/riscv/m128_helper.c +++ b/target/riscv/m128_helper.c @@ -19,13 +19,12 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "qemu/main-loop.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" target_ulong HELPER(divu_i128)(CPURISCVState *env, - target_ulong ul, target_ulong uh, - target_ulong vl, target_ulong vh) + target_ulong ul, target_ulong uh, + target_ulong vl, target_ulong vh) { target_ulong ql, qh; Int128 q; @@ -44,8 +43,8 @@ target_ulong HELPER(divu_i128)(CPURISCVState *env, } target_ulong HELPER(remu_i128)(CPURISCVState *env, - target_ulong ul, target_ulong uh, - target_ulong vl, target_ulong vh) + target_ulong ul, target_ulong uh, + target_ulong vl, target_ulong vh) { target_ulong rl, rh; Int128 r; @@ -64,8 +63,8 @@ target_ulong HELPER(remu_i128)(CPURISCVState *env, } target_ulong HELPER(divs_i128)(CPURISCVState *env, - target_ulong ul, target_ulong uh, - target_ulong vl, target_ulong vh) + target_ulong ul, target_ulong uh, + target_ulong vl, target_ulong vh) { target_ulong qh, ql; Int128 q; @@ -89,8 +88,8 @@ target_ulong HELPER(divs_i128)(CPURISCVState *env, } target_ulong HELPER(rems_i128)(CPURISCVState *env, - target_ulong ul, target_ulong uh, - target_ulong vl, target_ulong vh) + target_ulong ul, target_ulong uh, + target_ulong vl, target_ulong vh) { target_ulong rh, rl; Int128 r; diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 2a437b29a1..76f2150f78 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -21,13 +21,14 @@ #include "qemu/error-report.h" #include "sysemu/kvm.h" #include "migration/cpu.h" +#include "sysemu/cpu-timers.h" +#include "debug.h" static bool pmp_needed(void *opaque) { RISCVCPU *cpu = opaque; - CPURISCVState *env = &cpu->env; - return riscv_feature(env, RISCV_FEATURE_PMP); + return cpu->cfg.pmp; } static int pmp_post_load(void *opaque, int version_id) @@ -48,7 +49,7 @@ static const VMStateDescription vmstate_pmp_entry = { .name = "cpu/pmp/entry", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINTTL(addr_reg, pmp_entry_t), VMSTATE_UINT8(cfg_reg, pmp_entry_t), VMSTATE_END_OF_LIST() @@ -61,7 +62,7 @@ static const VMStateDescription vmstate_pmp = { .minimum_version_id = 1, .needed = pmp_needed, .post_load = pmp_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(env.pmp_state.pmp, RISCVCPU, MAX_RISCV_PMPS, 0, vmstate_pmp_entry, pmp_entry_t), VMSTATE_END_OF_LIST() @@ -78,20 +79,23 @@ static bool hyper_needed(void *opaque) static const VMStateDescription vmstate_hyper = { .name = "cpu/hyper", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 4, + .minimum_version_id = 4, .needed = hyper_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINTTL(env.hstatus, RISCVCPU), VMSTATE_UINTTL(env.hedeleg, RISCVCPU), VMSTATE_UINT64(env.hideleg, RISCVCPU), - VMSTATE_UINTTL(env.hcounteren, RISCVCPU), + VMSTATE_UINT32(env.hcounteren, RISCVCPU), VMSTATE_UINTTL(env.htval, RISCVCPU), VMSTATE_UINTTL(env.htinst, RISCVCPU), VMSTATE_UINTTL(env.hgatp, RISCVCPU), VMSTATE_UINTTL(env.hgeie, RISCVCPU), VMSTATE_UINTTL(env.hgeip, RISCVCPU), + VMSTATE_UINT64(env.hvien, RISCVCPU), + VMSTATE_UINT64(env.hvip, RISCVCPU), VMSTATE_UINT64(env.htimedelta, RISCVCPU), + VMSTATE_UINT64(env.vstimecmp, RISCVCPU), VMSTATE_UINTTL(env.hvictl, RISCVCPU), VMSTATE_UINT8_ARRAY(env.hviprio, RISCVCPU, 64), @@ -104,6 +108,7 @@ static const VMStateDescription vmstate_hyper = { VMSTATE_UINTTL(env.vstval, RISCVCPU), VMSTATE_UINTTL(env.vsatp, RISCVCPU), VMSTATE_UINTTL(env.vsiselect, RISCVCPU), + VMSTATE_UINT64(env.vsie, RISCVCPU), VMSTATE_UINTTL(env.mtval2, RISCVCPU), VMSTATE_UINTTL(env.mtinst, RISCVCPU), @@ -133,16 +138,16 @@ static const VMStateDescription vmstate_vector = { .version_id = 2, .minimum_version_id = 2, .needed = vector_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT64_ARRAY(env.vreg, RISCVCPU, 32 * RV_VLEN_MAX / 64), - VMSTATE_UINTTL(env.vxrm, RISCVCPU), - VMSTATE_UINTTL(env.vxsat, RISCVCPU), - VMSTATE_UINTTL(env.vl, RISCVCPU), - VMSTATE_UINTTL(env.vstart, RISCVCPU), - VMSTATE_UINTTL(env.vtype, RISCVCPU), - VMSTATE_BOOL(env.vill, RISCVCPU), - VMSTATE_END_OF_LIST() - } + .fields = (const VMStateField[]) { + VMSTATE_UINT64_ARRAY(env.vreg, RISCVCPU, 32 * RV_VLEN_MAX / 64), + VMSTATE_UINTTL(env.vxrm, RISCVCPU), + VMSTATE_UINTTL(env.vxsat, RISCVCPU), + VMSTATE_UINTTL(env.vl, RISCVCPU), + VMSTATE_UINTTL(env.vstart, RISCVCPU), + VMSTATE_UINTTL(env.vtype, RISCVCPU), + VMSTATE_BOOL(env.vill, RISCVCPU), + VMSTATE_END_OF_LIST() + } }; static bool pointermasking_needed(void *opaque) @@ -158,7 +163,7 @@ static const VMStateDescription vmstate_pointermasking = { .version_id = 1, .minimum_version_id = 1, .needed = pointermasking_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINTTL(env.mmte, RISCVCPU), VMSTATE_UINTTL(env.mpmmask, RISCVCPU), VMSTATE_UINTTL(env.mpmbase, RISCVCPU), @@ -173,10 +178,9 @@ static const VMStateDescription vmstate_pointermasking = { static bool rv128_needed(void *opaque) { - RISCVCPU *cpu = opaque; - CPURISCVState *env = &cpu->env; + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(opaque); - return env->misa_mxl_max == MXL_RV128; + return mcc->misa_mxl_max == MXL_RV128; } static const VMStateDescription vmstate_rv128 = { @@ -184,7 +188,7 @@ static const VMStateDescription vmstate_rv128 = { .version_id = 1, .minimum_version_id = 1, .needed = rv128_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.gprh, RISCVCPU, 32), VMSTATE_UINT64(env.mscratchh, RISCVCPU), VMSTATE_UINT64(env.sscratchh, RISCVCPU), @@ -192,12 +196,13 @@ static const VMStateDescription vmstate_rv128 = { } }; +#ifdef CONFIG_KVM static bool kvmtimer_needed(void *opaque) { return kvm_enabled(); } -static int cpu_post_load(void *opaque, int version_id) +static int cpu_kvmtimer_post_load(void *opaque, int version_id) { RISCVCPU *cpu = opaque; CPURISCVState *env = &cpu->env; @@ -211,43 +216,46 @@ static const VMStateDescription vmstate_kvmtimer = { .version_id = 1, .minimum_version_id = 1, .needed = kvmtimer_needed, - .post_load = cpu_post_load, - .fields = (VMStateField[]) { + .post_load = cpu_kvmtimer_post_load, + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.kvm_timer_time, RISCVCPU), VMSTATE_UINT64(env.kvm_timer_compare, RISCVCPU), VMSTATE_UINT64(env.kvm_timer_state, RISCVCPU), VMSTATE_END_OF_LIST() } }; +#endif static bool debug_needed(void *opaque) { RISCVCPU *cpu = opaque; - CPURISCVState *env = &cpu->env; - return riscv_feature(env, RISCV_FEATURE_DEBUG); + return cpu->cfg.debug; } -static const VMStateDescription vmstate_debug_type2 = { - .name = "cpu/debug/type2", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINTTL(mcontrol, type2_trigger_t), - VMSTATE_UINTTL(maddress, type2_trigger_t), - VMSTATE_END_OF_LIST() - } -}; +static int debug_post_load(void *opaque, int version_id) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + + if (icount_enabled()) { + env->itrigger_enabled = riscv_itrigger_enabled(env); + } + + return 0; +} static const VMStateDescription vmstate_debug = { .name = "cpu/debug", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .needed = debug_needed, - .fields = (VMStateField[]) { + .post_load = debug_post_load, + .fields = (const VMStateField[]) { VMSTATE_UINTTL(env.trigger_cur, RISCVCPU), - VMSTATE_STRUCT_ARRAY(env.type2_trig, RISCVCPU, TRIGGER_TYPE2_NUM, - 0, vmstate_debug_type2, type2_trigger_t), + VMSTATE_UINTTL_ARRAY(env.tdata1, RISCVCPU, RV_MAX_TRIGGERS), + VMSTATE_UINTTL_ARRAY(env.tdata2, RISCVCPU, RV_MAX_TRIGGERS), + VMSTATE_UINTTL_ARRAY(env.tdata3, RISCVCPU, RV_MAX_TRIGGERS), VMSTATE_END_OF_LIST() } }; @@ -262,6 +270,26 @@ static int riscv_cpu_post_load(void *opaque, int version_id) return 0; } +static bool smstateen_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + + return cpu->cfg.ext_smstateen; +} + +static const VMStateDescription vmstate_smstateen = { + .name = "cpu/smtateen", + .version_id = 1, + .minimum_version_id = 1, + .needed = smstateen_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT64_ARRAY(env.mstateen, RISCVCPU, 4), + VMSTATE_UINT64_ARRAY(env.hstateen, RISCVCPU, 4), + VMSTATE_UINT64_ARRAY(env.sstateen, RISCVCPU, 4), + VMSTATE_END_OF_LIST() + } +}; + static bool envcfg_needed(void *opaque) { RISCVCPU *cpu = opaque; @@ -275,21 +303,60 @@ static const VMStateDescription vmstate_envcfg = { .version_id = 1, .minimum_version_id = 1, .needed = envcfg_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.menvcfg, RISCVCPU), VMSTATE_UINTTL(env.senvcfg, RISCVCPU), VMSTATE_UINT64(env.henvcfg, RISCVCPU), + VMSTATE_END_OF_LIST() + } +}; +static bool pmu_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + + return (cpu->cfg.pmu_mask > 0); +} + +static const VMStateDescription vmstate_pmu_ctr_state = { + .name = "cpu/pmu", + .version_id = 1, + .minimum_version_id = 1, + .needed = pmu_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINTTL(mhpmcounter_val, PMUCTRState), + VMSTATE_UINTTL(mhpmcounterh_val, PMUCTRState), + VMSTATE_UINTTL(mhpmcounter_prev, PMUCTRState), + VMSTATE_UINTTL(mhpmcounterh_prev, PMUCTRState), + VMSTATE_BOOL(started, PMUCTRState), + VMSTATE_END_OF_LIST() + } +}; + +static bool jvt_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + + return cpu->cfg.ext_zcmt; +} + +static const VMStateDescription vmstate_jvt = { + .name = "cpu/jvt", + .version_id = 1, + .minimum_version_id = 1, + .needed = jvt_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINTTL(env.jvt, RISCVCPU), VMSTATE_END_OF_LIST() } }; const VMStateDescription vmstate_riscv_cpu = { .name = "cpu", - .version_id = 3, - .minimum_version_id = 3, + .version_id = 10, + .minimum_version_id = 10, .post_load = riscv_cpu_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.gpr, RISCVCPU, 32), VMSTATE_UINT64_ARRAY(env.fpr, RISCVCPU, 32), VMSTATE_UINT8_ARRAY(env.miprio, RISCVCPU, 64), @@ -304,17 +371,19 @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINTTL(env.vext_ver, RISCVCPU), VMSTATE_UINT32(env.misa_mxl, RISCVCPU), VMSTATE_UINT32(env.misa_ext, RISCVCPU), - VMSTATE_UINT32(env.misa_mxl_max, RISCVCPU), + VMSTATE_UNUSED(4), VMSTATE_UINT32(env.misa_ext_mask, RISCVCPU), - VMSTATE_UINT32(env.features, RISCVCPU), VMSTATE_UINTTL(env.priv, RISCVCPU), - VMSTATE_UINTTL(env.virt, RISCVCPU), - VMSTATE_UINTTL(env.resetvec, RISCVCPU), + VMSTATE_BOOL(env.virt_enabled, RISCVCPU), + VMSTATE_UINT64(env.resetvec, RISCVCPU), VMSTATE_UINTTL(env.mhartid, RISCVCPU), VMSTATE_UINT64(env.mstatus, RISCVCPU), VMSTATE_UINT64(env.mip, RISCVCPU), VMSTATE_UINT64(env.miclaim, RISCVCPU), VMSTATE_UINT64(env.mie, RISCVCPU), + VMSTATE_UINT64(env.mvien, RISCVCPU), + VMSTATE_UINT64(env.mvip, RISCVCPU), + VMSTATE_UINT64(env.sie, RISCVCPU), VMSTATE_UINT64(env.mideleg, RISCVCPU), VMSTATE_UINTTL(env.satp, RISCVCPU), VMSTATE_UINTTL(env.stval, RISCVCPU), @@ -328,25 +397,32 @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINTTL(env.mtval, RISCVCPU), VMSTATE_UINTTL(env.miselect, RISCVCPU), VMSTATE_UINTTL(env.siselect, RISCVCPU), - VMSTATE_UINTTL(env.scounteren, RISCVCPU), - VMSTATE_UINTTL(env.mcounteren, RISCVCPU), + VMSTATE_UINT32(env.scounteren, RISCVCPU), + VMSTATE_UINT32(env.mcounteren, RISCVCPU), + VMSTATE_UINT32(env.mcountinhibit, RISCVCPU), + VMSTATE_STRUCT_ARRAY(env.pmu_ctrs, RISCVCPU, RV_MAX_MHPMCOUNTERS, 0, + vmstate_pmu_ctr_state, PMUCTRState), + VMSTATE_UINTTL_ARRAY(env.mhpmevent_val, RISCVCPU, RV_MAX_MHPMEVENTS), + VMSTATE_UINTTL_ARRAY(env.mhpmeventh_val, RISCVCPU, RV_MAX_MHPMEVENTS), VMSTATE_UINTTL(env.sscratch, RISCVCPU), VMSTATE_UINTTL(env.mscratch, RISCVCPU), - VMSTATE_UINT64(env.mfromhost, RISCVCPU), - VMSTATE_UINT64(env.mtohost, RISCVCPU), - VMSTATE_UINT64(env.timecmp, RISCVCPU), + VMSTATE_UINT64(env.stimecmp, RISCVCPU), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_pmp, &vmstate_hyper, &vmstate_vector, &vmstate_pointermasking, &vmstate_rv128, +#ifdef CONFIG_KVM &vmstate_kvmtimer, +#endif &vmstate_envcfg, &vmstate_debug, + &vmstate_smstateen, + &vmstate_jvt, NULL } }; diff --git a/target/riscv/meson.build b/target/riscv/meson.build index 096249f3a3..a5e0734e7f 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -1,9 +1,8 @@ # FIXME extra_args should accept files() -dir = meson.current_source_dir() - gen = [ decodetree.process('insn16.decode', extra_args: ['--static-decode=decode_insn16', '--insnwidth=16']), decodetree.process('insn32.decode', extra_args: '--static-decode=decode_insn32'), + decodetree.process('xthead.decode', extra_args: '--static-decode=decode_xthead'), decodetree.process('XVentanaCondOps.decode', extra_args: '--static-decode=decode_XVentanaCodeOps'), ] @@ -17,21 +16,29 @@ riscv_ss.add(files( 'gdbstub.c', 'op_helper.c', 'vector_helper.c', + 'vector_internals.c', 'bitmanip_helper.c', 'translate.c', 'm128_helper.c', - 'crypto_helper.c' + 'crypto_helper.c', + 'zce_helper.c', + 'vcrypto_helper.c' )) -riscv_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c')) -riscv_softmmu_ss = ss.source_set() -riscv_softmmu_ss.add(files( +riscv_system_ss = ss.source_set() +riscv_system_ss.add(files( 'arch_dump.c', 'pmp.c', 'debug.c', 'monitor.c', - 'machine.c' + 'machine.c', + 'pmu.c', + 'time_helper.c', + 'riscv-qmp-cmds.c', )) +subdir('tcg') +subdir('kvm') + target_arch += {'riscv': riscv_ss} -target_softmmu_arch += {'riscv': riscv_softmmu_ss} +target_system_arch += {'riscv': riscv_system_ss} diff --git a/target/riscv/monitor.c b/target/riscv/monitor.c index 17e63fab00..f5b1ffe6c3 100644 --- a/target/riscv/monitor.c +++ b/target/riscv/monitor.c @@ -55,7 +55,7 @@ static void print_pte_header(Monitor *mon) static void print_pte(Monitor *mon, int va_bits, target_ulong vaddr, hwaddr paddr, target_ulong size, int attr) { - /* santity check on vaddr */ + /* sanity check on vaddr */ if (vaddr >= (1UL << va_bits)) { return; } @@ -64,7 +64,7 @@ static void print_pte(Monitor *mon, int va_bits, target_ulong vaddr, return; } - monitor_printf(mon, TARGET_FMT_lx " " TARGET_FMT_plx " " TARGET_FMT_lx + monitor_printf(mon, TARGET_FMT_lx " " HWADDR_FMT_plx " " TARGET_FMT_lx " %c%c%c%c%c%c%c\n", addr_canonical(va_bits, vaddr), paddr, size, @@ -218,7 +218,7 @@ void hmp_info_mem(Monitor *mon, const QDict *qdict) return; } - if (!riscv_feature(env, RISCV_FEATURE_MMU)) { + if (!riscv_cpu_cfg(env)->mmu) { monitor_printf(mon, "S-mode MMU unavailable\n"); return; } diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 09f1f5185d..f414aaebdb 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -3,6 +3,7 @@ * * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu * Copyright (c) 2017-2018 SiFive, Inc. + * Copyright (c) 2022 VRULL GmbH * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -19,8 +20,9 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "qemu/main-loop.h" +#include "internals.h" #include "exec/exec-all.h" +#include "exec/cpu_ldst.h" #include "exec/helper-proto.h" /* Exceptions processing helpers */ @@ -123,6 +125,140 @@ target_ulong helper_csrrw_i128(CPURISCVState *env, int csr, return int128_getlo(rv); } + +/* + * check_zicbo_envcfg + * + * Raise virtual exceptions and illegal instruction exceptions for + * Zicbo[mz] instructions based on the settings of [mhs]envcfg as + * specified in section 2.5.1 of the CMO specification. + */ +static void check_zicbo_envcfg(CPURISCVState *env, target_ulong envbits, + uintptr_t ra) +{ +#ifndef CONFIG_USER_ONLY + if ((env->priv < PRV_M) && !get_field(env->menvcfg, envbits)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra); + } + + if (env->virt_enabled && + (((env->priv <= PRV_S) && !get_field(env->henvcfg, envbits)) || + ((env->priv < PRV_S) && !get_field(env->senvcfg, envbits)))) { + riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, ra); + } + + if ((env->priv < PRV_S) && !get_field(env->senvcfg, envbits)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra); + } +#endif +} + +void helper_cbo_zero(CPURISCVState *env, target_ulong address) +{ + RISCVCPU *cpu = env_archcpu(env); + uint16_t cbozlen = cpu->cfg.cboz_blocksize; + int mmu_idx = riscv_env_mmu_index(env, false); + uintptr_t ra = GETPC(); + void *mem; + + check_zicbo_envcfg(env, MENVCFG_CBZE, ra); + + /* Mask off low-bits to align-down to the cache-block. */ + address &= ~(cbozlen - 1); + + /* + * cbo.zero requires MMU_DATA_STORE access. Do a probe_write() + * to raise any exceptions, including PMP. + */ + mem = probe_write(env, address, cbozlen, mmu_idx, ra); + + if (likely(mem)) { + memset(mem, 0, cbozlen); + } else { + /* + * This means that we're dealing with an I/O page. Section 4.2 + * of cmobase v1.0.1 says: + * + * "Cache-block zero instructions store zeros independently + * of whether data from the underlying memory locations are + * cacheable." + * + * Write zeros in address + cbozlen regardless of not being + * a RAM page. + */ + for (int i = 0; i < cbozlen; i++) { + cpu_stb_mmuidx_ra(env, address + i, 0, mmu_idx, ra); + } + } +} + +/* + * check_zicbom_access + * + * Check access permissions (LOAD, STORE or FETCH as specified in + * section 2.5.2 of the CMO specification) for Zicbom, raising + * either store page-fault (non-virtualized) or store guest-page + * fault (virtualized). + */ +static void check_zicbom_access(CPURISCVState *env, + target_ulong address, + uintptr_t ra) +{ + RISCVCPU *cpu = env_archcpu(env); + int mmu_idx = riscv_env_mmu_index(env, false); + uint16_t cbomlen = cpu->cfg.cbom_blocksize; + void *phost; + int ret; + + /* Mask off low-bits to align-down to the cache-block. */ + address &= ~(cbomlen - 1); + + /* + * Section 2.5.2 of cmobase v1.0.1: + * + * "A cache-block management instruction is permitted to + * access the specified cache block whenever a load instruction + * or store instruction is permitted to access the corresponding + * physical addresses. If neither a load instruction nor store + * instruction is permitted to access the physical addresses, + * but an instruction fetch is permitted to access the physical + * addresses, whether a cache-block management instruction is + * permitted to access the cache block is UNSPECIFIED." + */ + ret = probe_access_flags(env, address, cbomlen, MMU_DATA_LOAD, + mmu_idx, true, &phost, ra); + if (ret != TLB_INVALID_MASK) { + /* Success: readable */ + return; + } + + /* + * Since not readable, must be writable. On failure, store + * fault/store guest amo fault will be raised by + * riscv_cpu_tlb_fill(). PMP exceptions will be caught + * there as well. + */ + probe_write(env, address, cbomlen, mmu_idx, ra); +} + +void helper_cbo_clean_flush(CPURISCVState *env, target_ulong address) +{ + uintptr_t ra = GETPC(); + check_zicbo_envcfg(env, MENVCFG_CBCFE, ra); + check_zicbom_access(env, address, ra); + + /* We don't emulate the cache-hierarchy, so we're done. */ +} + +void helper_cbo_inval(CPURISCVState *env, target_ulong address) +{ + uintptr_t ra = GETPC(); + check_zicbo_envcfg(env, MENVCFG_CBIE, ra); + check_zicbom_access(env, address, ra); + + /* We don't emulate the cache-hierarchy, so we're done. */ +} + #ifndef CONFIG_USER_ONLY target_ulong helper_sret(CPURISCVState *env) @@ -143,27 +279,29 @@ target_ulong helper_sret(CPURISCVState *env) riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } - if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) && - get_field(env->hstatus, HSTATUS_VTSR)) { + if (env->virt_enabled && get_field(env->hstatus, HSTATUS_VTSR)) { riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); } mstatus = env->mstatus; + prev_priv = get_field(mstatus, MSTATUS_SPP); + mstatus = set_field(mstatus, MSTATUS_SIE, + get_field(mstatus, MSTATUS_SPIE)); + mstatus = set_field(mstatus, MSTATUS_SPIE, 1); + mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); + if (env->priv_ver >= PRIV_VERSION_1_12_0) { + mstatus = set_field(mstatus, MSTATUS_MPRV, 0); + } + env->mstatus = mstatus; - if (riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) { + if (riscv_has_ext(env, RVH) && !env->virt_enabled) { /* 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, 0); - mstatus = set_field(mstatus, MSTATUS_SPP, 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) { @@ -171,14 +309,6 @@ target_ulong helper_sret(CPURISCVState *env) } riscv_cpu_set_virt_enabled(env, prev_virt); - } else { - prev_priv = get_field(mstatus, MSTATUS_SPP); - - mstatus = set_field(mstatus, MSTATUS_SIE, - 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); @@ -200,17 +330,22 @@ target_ulong helper_mret(CPURISCVState *env) uint64_t mstatus = env->mstatus; target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); - if (riscv_feature(env, RISCV_FEATURE_PMP) && + if (riscv_cpu_cfg(env)->pmp && !pmp_get_num_rules(env) && (prev_priv != PRV_M)) { - riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, GETPC()); } - target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV); + target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV) && + (prev_priv != PRV_M); mstatus = set_field(mstatus, MSTATUS_MIE, get_field(mstatus, MSTATUS_MPIE)); mstatus = set_field(mstatus, MSTATUS_MPIE, 1); - mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); + mstatus = set_field(mstatus, MSTATUS_MPP, + riscv_has_ext(env, RVU) ? PRV_U : PRV_M); mstatus = set_field(mstatus, MSTATUS_MPV, 0); + if ((env->priv_ver >= PRIV_VERSION_1_12_0) && (prev_priv != PRV_M)) { + mstatus = set_field(mstatus, MSTATUS_MPRV, 0); + } env->mstatus = mstatus; riscv_cpu_set_mode(env, prev_priv); @@ -233,10 +368,10 @@ void helper_wfi(CPURISCVState *env) bool prv_s = env->priv == PRV_S; if (((prv_s || (!rvs && prv_u)) && get_field(env->mstatus, MSTATUS_TW)) || - (rvs && prv_u && !riscv_cpu_virt_enabled(env))) { + (rvs && prv_u && !env->virt_enabled)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); - } else if (riscv_cpu_virt_enabled(env) && (prv_u || - (prv_s && get_field(env->hstatus, HSTATUS_VTW)))) { + } else if (env->virt_enabled && + (prv_u || (prv_s && get_field(env->hstatus, HSTATUS_VTW)))) { riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); } else { cs->halted = 1; @@ -248,28 +383,34 @@ void helper_wfi(CPURISCVState *env) void helper_tlb_flush(CPURISCVState *env) { CPUState *cs = env_cpu(env); - if (!(env->priv >= PRV_S) || - (env->priv == PRV_S && - get_field(env->mstatus, MSTATUS_TVM))) { + if (!env->virt_enabled && + (env->priv == PRV_U || + (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)))) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); - } else if (riscv_has_ext(env, RVH) && riscv_cpu_virt_enabled(env) && - get_field(env->hstatus, HSTATUS_VTVM)) { + } else if (env->virt_enabled && + (env->priv == PRV_U || get_field(env->hstatus, HSTATUS_VTVM))) { riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); } else { tlb_flush(cs); } } +void helper_tlb_flush_all(CPURISCVState *env) +{ + CPUState *cs = env_cpu(env); + tlb_flush_all_cpus_synced(cs); +} + void helper_hyp_tlb_flush(CPURISCVState *env) { CPUState *cs = env_cpu(env); - if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) { + if (env->virt_enabled) { riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); } if (env->priv == PRV_M || - (env->priv == PRV_S && !riscv_cpu_virt_enabled(env))) { + (env->priv == PRV_S && !env->virt_enabled)) { tlb_flush(cs); return; } @@ -279,7 +420,7 @@ void helper_hyp_tlb_flush(CPURISCVState *env) void helper_hyp_gvma_tlb_flush(CPURISCVState *env) { - if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env) && + if (env->priv == PRV_S && !env->virt_enabled && get_field(env->mstatus, MSTATUS_TVM)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } @@ -287,18 +428,118 @@ void helper_hyp_gvma_tlb_flush(CPURISCVState *env) helper_hyp_tlb_flush(env); } -target_ulong helper_hyp_hlvx_hu(CPURISCVState *env, target_ulong address) +static int check_access_hlsv(CPURISCVState *env, bool x, uintptr_t ra) { - int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK; + if (env->priv == PRV_M) { + /* always allowed */ + } else if (env->virt_enabled) { + riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, ra); + } else if (env->priv == PRV_U && !get_field(env->hstatus, HSTATUS_HU)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra); + } - return cpu_lduw_mmuidx_ra(env, address, mmu_idx, GETPC()); + int mode = get_field(env->hstatus, HSTATUS_SPVP); + if (!x && mode == PRV_S && get_field(env->vsstatus, MSTATUS_SUM)) { + mode = MMUIdx_S_SUM; + } + return mode | MMU_2STAGE_BIT; } -target_ulong helper_hyp_hlvx_wu(CPURISCVState *env, target_ulong address) +target_ulong helper_hyp_hlv_bu(CPURISCVState *env, target_ulong addr) { - int mmu_idx = cpu_mmu_index(env, true) | TB_FLAGS_PRIV_HYP_ACCESS_MASK; + uintptr_t ra = GETPC(); + int mmu_idx = check_access_hlsv(env, false, ra); + MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - return cpu_ldl_mmuidx_ra(env, address, mmu_idx, GETPC()); + return cpu_ldb_mmu(env, addr, oi, ra); +} + +target_ulong helper_hyp_hlv_hu(CPURISCVState *env, target_ulong addr) +{ + uintptr_t ra = GETPC(); + int mmu_idx = check_access_hlsv(env, false, ra); + MemOpIdx oi = make_memop_idx(MO_TEUW, mmu_idx); + + return cpu_ldw_mmu(env, addr, oi, ra); +} + +target_ulong helper_hyp_hlv_wu(CPURISCVState *env, target_ulong addr) +{ + uintptr_t ra = GETPC(); + int mmu_idx = check_access_hlsv(env, false, ra); + MemOpIdx oi = make_memop_idx(MO_TEUL, mmu_idx); + + return cpu_ldl_mmu(env, addr, oi, ra); +} + +target_ulong helper_hyp_hlv_d(CPURISCVState *env, target_ulong addr) +{ + uintptr_t ra = GETPC(); + int mmu_idx = check_access_hlsv(env, false, ra); + MemOpIdx oi = make_memop_idx(MO_TEUQ, mmu_idx); + + return cpu_ldq_mmu(env, addr, oi, ra); +} + +void helper_hyp_hsv_b(CPURISCVState *env, target_ulong addr, target_ulong val) +{ + uintptr_t ra = GETPC(); + int mmu_idx = check_access_hlsv(env, false, ra); + MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); + + cpu_stb_mmu(env, addr, val, oi, ra); +} + +void helper_hyp_hsv_h(CPURISCVState *env, target_ulong addr, target_ulong val) +{ + uintptr_t ra = GETPC(); + int mmu_idx = check_access_hlsv(env, false, ra); + MemOpIdx oi = make_memop_idx(MO_TEUW, mmu_idx); + + cpu_stw_mmu(env, addr, val, oi, ra); +} + +void helper_hyp_hsv_w(CPURISCVState *env, target_ulong addr, target_ulong val) +{ + uintptr_t ra = GETPC(); + int mmu_idx = check_access_hlsv(env, false, ra); + MemOpIdx oi = make_memop_idx(MO_TEUL, mmu_idx); + + cpu_stl_mmu(env, addr, val, oi, ra); +} + +void helper_hyp_hsv_d(CPURISCVState *env, target_ulong addr, target_ulong val) +{ + uintptr_t ra = GETPC(); + int mmu_idx = check_access_hlsv(env, false, ra); + MemOpIdx oi = make_memop_idx(MO_TEUQ, mmu_idx); + + cpu_stq_mmu(env, addr, val, oi, ra); +} + +/* + * TODO: These implementations are not quite correct. They perform the + * access using execute permission just fine, but the final PMP check + * is supposed to have read permission as well. Without replicating + * a fair fraction of cputlb.c, fixing this requires adding new mmu_idx + * which would imply that exact check in tlb_fill. + */ +target_ulong helper_hyp_hlvx_hu(CPURISCVState *env, target_ulong addr) +{ + uintptr_t ra = GETPC(); + int mmu_idx = check_access_hlsv(env, true, ra); + MemOpIdx oi = make_memop_idx(MO_TEUW, mmu_idx); + + return cpu_ldw_code_mmu(env, addr, oi, GETPC()); +} + +target_ulong helper_hyp_hlvx_wu(CPURISCVState *env, target_ulong addr) +{ + uintptr_t ra = GETPC(); + int mmu_idx = check_access_hlsv(env, true, ra); + MemOpIdx oi = make_memop_idx(MO_TEUL, mmu_idx); + + return cpu_ldl_code_mmu(env, addr, oi, ra); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 151da3fa08..2a76b611a0 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -26,10 +26,9 @@ #include "trace.h" #include "exec/exec-all.h" -static void pmp_write_cfg(CPURISCVState *env, uint32_t addr_index, - uint8_t val); +static bool pmp_write_cfg(CPURISCVState *env, uint32_t addr_index, + uint8_t val); static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index); -static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index); /* * Accessor method to extract address matching type 'a field' from cfg reg @@ -45,6 +44,10 @@ static inline uint8_t pmp_get_a_field(uint8_t cfg) */ static inline int pmp_is_locked(CPURISCVState *env, uint32_t pmp_index) { + /* mseccfg.RLB is set */ + if (MSECCFG_RLB_ISSET(env)) { + return 0; + } if (env->pmp_state.pmp[pmp_index].cfg_reg & PMP_LOCK) { return 1; @@ -83,12 +86,12 @@ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index) * Accessor to set the cfg reg for a specific PMP/HART * Bounds checks and relevant lock bit. */ -static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) +static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) { if (pmp_index < MAX_RISCV_PMPS) { bool locked = true; - if (riscv_feature(env, RISCV_FEATURE_EPMP)) { + if (riscv_cpu_cfg(env)->ext_smepmp) { /* mseccfg.RLB is set */ if (MSECCFG_RLB_ISSET(env)) { locked = false; @@ -119,28 +122,46 @@ static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) if (locked) { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - locked\n"); - } else { + } else if (env->pmp_state.pmp[pmp_index].cfg_reg != val) { + /* If !mseccfg.MML then ignore writes with encoding RW=01 */ + if ((val & PMP_WRITE) && !(val & PMP_READ) && + !MSECCFG_MML_ISSET(env)) { + return false; + } env->pmp_state.pmp[pmp_index].cfg_reg = val; - pmp_update_rule(env, pmp_index); + pmp_update_rule_addr(env, pmp_index); + return true; } } else { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - out of bounds\n"); } + + return false; } -static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea) +void pmp_unlock_entries(CPURISCVState *env) +{ + uint32_t pmp_num = pmp_get_num_rules(env); + int i; + + for (i = 0; i < pmp_num; i++) { + env->pmp_state.pmp[i].cfg_reg &= ~(PMP_LOCK | PMP_AMATCH); + } +} + +static void pmp_decode_napot(hwaddr a, hwaddr *sa, hwaddr *ea) { /* - aaaa...aaa0 8-byte NAPOT range - aaaa...aa01 16-byte NAPOT range - aaaa...a011 32-byte NAPOT range - ... - aa01...1111 2^XLEN-byte NAPOT range - a011...1111 2^(XLEN+1)-byte NAPOT range - 0111...1111 2^(XLEN+2)-byte NAPOT range - 1111...1111 Reserved - */ + * aaaa...aaa0 8-byte NAPOT range + * aaaa...aa01 16-byte NAPOT range + * aaaa...a011 32-byte NAPOT range + * ... + * aa01...1111 2^XLEN-byte NAPOT range + * a011...1111 2^(XLEN+1)-byte NAPOT range + * 0111...1111 2^(XLEN+2)-byte NAPOT range + * 1111...1111 Reserved + */ a = (a << 2) | 0x3; *sa = a & (a + 1); *ea = a | (a + 1); @@ -151,8 +172,8 @@ void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index) uint8_t this_cfg = env->pmp_state.pmp[pmp_index].cfg_reg; target_ulong this_addr = env->pmp_state.pmp[pmp_index].addr_reg; target_ulong prev_addr = 0u; - target_ulong sa = 0u; - target_ulong ea = 0u; + hwaddr sa = 0u; + hwaddr ea = 0u; if (pmp_index >= 1u) { prev_addr = env->pmp_state.pmp[pmp_index - 1].addr_reg; @@ -167,6 +188,9 @@ void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index) case PMP_AMATCH_TOR: sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ ea = (this_addr << 2) - 1u; + if (sa > ea) { + sa = ea = 0u; + } break; case PMP_AMATCH_NA4: @@ -202,23 +226,12 @@ void pmp_update_rule_nums(CPURISCVState *env) } } -/* Convert cfg/addr reg values here into simple 'sa' --> start address and 'ea' - * end address values. - * This function is called relatively infrequently whereas the check that - * an address is within a pmp rule is called often, so optimise that one - */ -static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index) -{ - pmp_update_rule_addr(env, pmp_index); - pmp_update_rule_nums(env); -} - -static int pmp_is_in_range(CPURISCVState *env, int pmp_index, target_ulong addr) +static int pmp_is_in_range(CPURISCVState *env, int pmp_index, hwaddr addr) { int result = 0; - if ((addr >= env->pmp_state.addr[pmp_index].sa) - && (addr <= env->pmp_state.addr[pmp_index].ea)) { + if ((addr >= env->pmp_state.addr[pmp_index].sa) && + (addr <= env->pmp_state.addr[pmp_index].ea)) { result = 1; } else { result = 0; @@ -230,39 +243,37 @@ static int pmp_is_in_range(CPURISCVState *env, int pmp_index, target_ulong addr) /* * Check if the address has required RWX privs when no PMP entry is matched. */ -static bool pmp_hart_has_privs_default(CPURISCVState *env, target_ulong addr, - target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs, - target_ulong mode) +static bool pmp_hart_has_privs_default(CPURISCVState *env, pmp_priv_t privs, + pmp_priv_t *allowed_privs, + target_ulong mode) { bool ret; - if (riscv_feature(env, RISCV_FEATURE_EPMP)) { - if (MSECCFG_MMWP_ISSET(env)) { - /* - * The Machine Mode Whitelist Policy (mseccfg.MMWP) is set - * so we default to deny all, even for M-mode. - */ + if (MSECCFG_MMWP_ISSET(env)) { + /* + * The Machine Mode Whitelist Policy (mseccfg.MMWP) is set + * so we default to deny all, even for M-mode. + */ + *allowed_privs = 0; + return false; + } else if (MSECCFG_MML_ISSET(env)) { + /* + * The Machine Mode Lockdown (mseccfg.MML) bit is set + * so we can only execute code in M-mode with an applicable + * rule. Other modes are disabled. + */ + if (mode == PRV_M && !(privs & PMP_EXEC)) { + ret = true; + *allowed_privs = PMP_READ | PMP_WRITE; + } else { + ret = false; *allowed_privs = 0; - return false; - } else if (MSECCFG_MML_ISSET(env)) { - /* - * The Machine Mode Lockdown (mseccfg.MML) bit is set - * so we can only execute code in M-mode with an applicable - * rule. Other modes are disabled. - */ - if (mode == PRV_M && !(privs & PMP_EXEC)) { - ret = true; - *allowed_privs = PMP_READ | PMP_WRITE; - } else { - ret = false; - *allowed_privs = 0; - } - - return ret; } + + return ret; } - if ((!riscv_feature(env, RISCV_FEATURE_PMP)) || (mode == PRV_M)) { + if (!riscv_cpu_cfg(env)->pmp || (mode == PRV_M)) { /* * Privileged spec v1.10 states if HW doesn't implement any PMP entry * or no PMP entry matches an M-Mode access, the access succeeds. @@ -289,25 +300,25 @@ static bool pmp_hart_has_privs_default(CPURISCVState *env, target_ulong addr, /* * Check if the address has required RWX privs to complete desired operation + * Return true if a pmp rule match or default match + * Return false if no match */ -bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, - target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs, - target_ulong mode) +bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr, + target_ulong size, pmp_priv_t privs, + pmp_priv_t *allowed_privs, target_ulong mode) { int i = 0; - int ret = -1; int pmp_size = 0; - target_ulong s = 0; - target_ulong e = 0; + hwaddr s = 0; + hwaddr e = 0; /* Short cut if no rules */ if (0 == pmp_get_num_rules(env)) { - return pmp_hart_has_privs_default(env, addr, size, privs, - allowed_privs, mode); + return pmp_hart_has_privs_default(env, privs, allowed_privs, mode); } if (size == 0) { - if (riscv_feature(env, RISCV_FEATURE_MMU)) { + if (riscv_cpu_cfg(env)->mmu) { /* * If size is unknown (0), assume that all bytes * from addr to the end of the page will be accessed. @@ -320,8 +331,10 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, pmp_size = size; } - /* 1.10 draft priv spec states there is an implicit order - from low to high */ + /* + * 1.10 draft priv spec states there is an implicit order + * from low to high + */ for (i = 0; i < MAX_RISCV_PMPS; i++) { s = pmp_is_in_range(env, i, addr); e = pmp_is_in_range(env, i, addr + pmp_size - 1); @@ -330,8 +343,8 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, if ((s + e) == 1) { qemu_log_mask(LOG_GUEST_ERROR, "pmp violation - access is partially inside\n"); - ret = 0; - break; + *allowed_privs = 0; + return false; } /* fully inside */ @@ -340,9 +353,9 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, /* * Convert the PMP permissions to match the truth table in the - * ePMP spec. + * Smepmp spec. */ - const uint8_t epmp_operation = + const uint8_t smepmp_operation = ((env->pmp_state.pmp[i].cfg_reg & PMP_LOCK) >> 4) | ((env->pmp_state.pmp[i].cfg_reg & PMP_READ) << 2) | (env->pmp_state.pmp[i].cfg_reg & PMP_WRITE) | @@ -367,7 +380,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, * If mseccfg.MML Bit set, do the enhanced pmp priv check */ if (mode == PRV_M) { - switch (epmp_operation) { + switch (smepmp_operation) { case 0: case 1: case 4: @@ -398,7 +411,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, g_assert_not_reached(); } } else { - switch (epmp_operation) { + switch (smepmp_operation) { case 0: case 8: case 9: @@ -433,39 +446,42 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, } } - ret = ((privs & *allowed_privs) == privs); - break; + /* + * If matching address range was found, the protection bits + * defined with PMP must be used. We shouldn't fallback on + * finding default privileges. + */ + return (privs & *allowed_privs) == privs; } } /* No rule matched */ - if (ret == -1) { - return pmp_hart_has_privs_default(env, addr, size, privs, - allowed_privs, mode); - } - - return ret == 1 ? true : false; + return pmp_hart_has_privs_default(env, privs, allowed_privs, mode); } /* * Handle a write to a pmpcfg CSR */ void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index, - target_ulong val) + target_ulong val) { int i; uint8_t cfg_val; int pmpcfg_nums = 2 << riscv_cpu_mxl(env); + bool modified = false; trace_pmpcfg_csr_write(env->mhartid, reg_index, val); for (i = 0; i < pmpcfg_nums; i++) { cfg_val = (val >> 8 * i) & 0xff; - pmp_write_cfg(env, (reg_index * 4) + i, cfg_val); + modified |= pmp_write_cfg(env, (reg_index * 4) + i, cfg_val); } /* If PMP permission of any addr has been changed, flush TLB pages. */ - tlb_flush(env_cpu(env)); + if (modified) { + pmp_update_rule_nums(env); + tlb_flush(env_cpu(env)); + } } @@ -493,9 +509,10 @@ target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index) * Handle a write to a pmpaddr CSR */ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, - target_ulong val) + target_ulong val) { trace_pmpaddr_csr_write(env->mhartid, addr_index, val); + bool is_next_cfg_tor = false; if (addr_index < MAX_RISCV_PMPS) { /* @@ -504,9 +521,9 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, */ if (addr_index + 1 < MAX_RISCV_PMPS) { uint8_t pmp_cfg = env->pmp_state.pmp[addr_index + 1].cfg_reg; + is_next_cfg_tor = PMP_AMATCH_TOR == pmp_get_a_field(pmp_cfg); - if (pmp_cfg & PMP_LOCK && - PMP_AMATCH_TOR == pmp_get_a_field(pmp_cfg)) { + if (pmp_cfg & PMP_LOCK && is_next_cfg_tor) { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpaddr write - pmpcfg + 1 locked\n"); return; @@ -514,8 +531,14 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, } if (!pmp_is_locked(env, addr_index)) { - env->pmp_state.pmp[addr_index].addr_reg = val; - pmp_update_rule(env, addr_index); + if (env->pmp_state.pmp[addr_index].addr_reg != val) { + env->pmp_state.pmp[addr_index].addr_reg = val; + pmp_update_rule_addr(env, addr_index); + if (is_next_cfg_tor) { + pmp_update_rule_addr(env, addr_index + 1); + } + tlb_flush(env_cpu(env)); + } } else { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpaddr write - locked\n"); @@ -564,8 +587,15 @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val) } } - /* Sticky bits */ - val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML)); + if (riscv_cpu_cfg(env)->ext_smepmp) { + /* Sticky bits */ + val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML)); + if ((val ^ env->mseccfg) & (MSECCFG_MMWP | MSECCFG_MML)) { + tlb_flush(env_cpu(env)); + } + } else { + val &= ~(MSECCFG_MMWP | MSECCFG_MML | MSECCFG_RLB); + } env->mseccfg = val; } @@ -580,55 +610,67 @@ target_ulong mseccfg_csr_read(CPURISCVState *env) } /* - * Calculate the TLB size if the start address or the end address of - * PMP entry is presented in the TLB page. + * Calculate the TLB size. + * It's possible that PMP regions only cover partial of the TLB page, and + * this may split the page into regions with different permissions. + * For example if PMP0 is (0x80000008~0x8000000F, R) and PMP1 is (0x80000000 + * ~0x80000FFF, RWX), then region 0x80000008~0x8000000F has R permission, and + * the other regions in this page have RWX permissions. + * A write access to 0x80000000 will match PMP1. However we cannot cache the + * translation result in the TLB since this will make the write access to + * 0x80000008 bypass the check of PMP0. + * To avoid this we return a size of 1 (which means no caching) if the PMP + * region only covers partial of the TLB page. */ -static target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index, - target_ulong tlb_sa, target_ulong tlb_ea) -{ - target_ulong pmp_sa = env->pmp_state.addr[pmp_index].sa; - target_ulong pmp_ea = env->pmp_state.addr[pmp_index].ea; - - if (pmp_sa >= tlb_sa && pmp_ea <= tlb_ea) { - return pmp_ea - pmp_sa + 1; - } - - if (pmp_sa >= tlb_sa && pmp_sa <= tlb_ea && pmp_ea >= tlb_ea) { - return tlb_ea - pmp_sa + 1; - } - - if (pmp_ea <= tlb_ea && pmp_ea >= tlb_sa && pmp_sa <= tlb_sa) { - return pmp_ea - tlb_sa + 1; - } - - return 0; -} - -/* - * Check is there a PMP entry which range covers this page. If so, - * try to find the minimum granularity for the TLB size. - */ -bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa, - target_ulong *tlb_size) +target_ulong pmp_get_tlb_size(CPURISCVState *env, hwaddr addr) { + hwaddr pmp_sa; + hwaddr pmp_ea; + hwaddr tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1); + hwaddr tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1; int i; - target_ulong val; - target_ulong tlb_ea = (tlb_sa + TARGET_PAGE_SIZE - 1); + + /* + * If PMP is not supported or there are no PMP rules, the TLB page will not + * be split into regions with different permissions by PMP so we set the + * size to TARGET_PAGE_SIZE. + */ + if (!riscv_cpu_cfg(env)->pmp || !pmp_get_num_rules(env)) { + return TARGET_PAGE_SIZE; + } for (i = 0; i < MAX_RISCV_PMPS; i++) { - val = pmp_get_tlb_size(env, i, tlb_sa, tlb_ea); - if (val) { - if (*tlb_size == 0 || *tlb_size > val) { - *tlb_size = val; - } + if (pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg) == PMP_AMATCH_OFF) { + continue; + } + + pmp_sa = env->pmp_state.addr[i].sa; + pmp_ea = env->pmp_state.addr[i].ea; + + /* + * Only the first PMP entry that covers (whole or partial of) the TLB + * page really matters: + * If it covers the whole TLB page, set the size to TARGET_PAGE_SIZE, + * since the following PMP entries have lower priority and will not + * affect the permissions of the page. + * If it only covers partial of the TLB page, set the size to 1 since + * the allowed permissions of the region may be different from other + * region of the page. + */ + if (pmp_sa <= tlb_sa && pmp_ea >= tlb_ea) { + return TARGET_PAGE_SIZE; + } else if ((pmp_sa >= tlb_sa && pmp_sa <= tlb_ea) || + (pmp_ea >= tlb_sa && pmp_ea <= tlb_ea)) { + return 1; } } - if (*tlb_size != 0) { - return true; - } - - return false; + /* + * If no PMP entry matches the TLB page, the TLB page will also not be + * split into regions with different permissions by PMP so we set the size + * to TARGET_PAGE_SIZE. + */ + return TARGET_PAGE_SIZE; } /* diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index a8dd797476..f5c10ce85c 100644 --- a/target/riscv/pmp.h +++ b/target/riscv/pmp.h @@ -28,6 +28,7 @@ typedef enum { PMP_READ = 1 << 0, PMP_WRITE = 1 << 1, PMP_EXEC = 1 << 2, + PMP_AMATCH = (3 << 3), PMP_LOCK = 1 << 7 } pmp_priv_t; @@ -52,8 +53,8 @@ typedef struct { } pmp_entry_t; typedef struct { - target_ulong sa; - target_ulong ea; + hwaddr sa; + hwaddr ea; } pmp_addr_t; typedef struct { @@ -63,24 +64,25 @@ typedef struct { } pmp_table_t; void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index, - target_ulong val); + target_ulong val); target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index); void mseccfg_csr_write(CPURISCVState *env, target_ulong val); target_ulong mseccfg_csr_read(CPURISCVState *env); void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, - target_ulong val); + target_ulong val); target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index); -bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, - target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs, - target_ulong mode); -bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa, - target_ulong *tlb_size); +bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr, + target_ulong size, pmp_priv_t privs, + pmp_priv_t *allowed_privs, + target_ulong mode); +target_ulong pmp_get_tlb_size(CPURISCVState *env, hwaddr addr); void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index); void pmp_update_rule_nums(CPURISCVState *env); uint32_t pmp_get_num_rules(CPURISCVState *env); int pmp_priv_to_page_prot(pmp_priv_t pmp_priv); +void pmp_unlock_entries(CPURISCVState *env); #define MSECCFG_MML_ISSET(env) get_field(env->mseccfg, MSECCFG_MML) #define MSECCFG_MMWP_ISSET(env) get_field(env->mseccfg, MSECCFG_MMWP) diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c new file mode 100644 index 0000000000..0e7d58b8a5 --- /dev/null +++ b/target/riscv/pmu.c @@ -0,0 +1,451 @@ +/* + * RISC-V PMU file. + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "cpu.h" +#include "pmu.h" +#include "sysemu/cpu-timers.h" +#include "sysemu/device_tree.h" + +#define RISCV_TIMEBASE_FREQ 1000000000 /* 1Ghz */ + +/* + * To keep it simple, any event can be mapped to any programmable counters in + * QEMU. The generic cycle & instruction count events can also be monitored + * using programmable counters. In that case, mcycle & minstret must continue + * to provide the correct value as well. Heterogeneous PMU per hart is not + * supported yet. Thus, number of counters are same across all harts. + */ +void riscv_pmu_generate_fdt_node(void *fdt, uint32_t cmask, char *pmu_name) +{ + uint32_t fdt_event_ctr_map[15] = {}; + + /* + * The event encoding is specified in the SBI specification + * Event idx is a 20bits wide number encoded as follows: + * event_idx[19:16] = type + * event_idx[15:0] = code + * The code field in cache events are encoded as follows: + * event_idx.code[15:3] = cache_id + * event_idx.code[2:1] = op_id + * event_idx.code[0:0] = result_id + */ + + /* SBI_PMU_HW_CPU_CYCLES: 0x01 : type(0x00) */ + fdt_event_ctr_map[0] = cpu_to_be32(0x00000001); + fdt_event_ctr_map[1] = cpu_to_be32(0x00000001); + fdt_event_ctr_map[2] = cpu_to_be32(cmask | 1 << 0); + + /* SBI_PMU_HW_INSTRUCTIONS: 0x02 : type(0x00) */ + fdt_event_ctr_map[3] = cpu_to_be32(0x00000002); + fdt_event_ctr_map[4] = cpu_to_be32(0x00000002); + fdt_event_ctr_map[5] = cpu_to_be32(cmask | 1 << 2); + + /* SBI_PMU_HW_CACHE_DTLB : 0x03 READ : 0x00 MISS : 0x00 type(0x01) */ + fdt_event_ctr_map[6] = cpu_to_be32(0x00010019); + fdt_event_ctr_map[7] = cpu_to_be32(0x00010019); + fdt_event_ctr_map[8] = cpu_to_be32(cmask); + + /* SBI_PMU_HW_CACHE_DTLB : 0x03 WRITE : 0x01 MISS : 0x00 type(0x01) */ + fdt_event_ctr_map[9] = cpu_to_be32(0x0001001B); + fdt_event_ctr_map[10] = cpu_to_be32(0x0001001B); + fdt_event_ctr_map[11] = cpu_to_be32(cmask); + + /* SBI_PMU_HW_CACHE_ITLB : 0x04 READ : 0x00 MISS : 0x00 type(0x01) */ + fdt_event_ctr_map[12] = cpu_to_be32(0x00010021); + fdt_event_ctr_map[13] = cpu_to_be32(0x00010021); + fdt_event_ctr_map[14] = cpu_to_be32(cmask); + + /* This a OpenSBI specific DT property documented in OpenSBI docs */ + qemu_fdt_setprop(fdt, pmu_name, "riscv,event-to-mhpmcounters", + fdt_event_ctr_map, sizeof(fdt_event_ctr_map)); +} + +static bool riscv_pmu_counter_valid(RISCVCPU *cpu, uint32_t ctr_idx) +{ + if (ctr_idx < 3 || ctr_idx >= RV_MAX_MHPMCOUNTERS || + !(cpu->pmu_avail_ctrs & BIT(ctr_idx))) { + return false; + } else { + return true; + } +} + +static bool riscv_pmu_counter_enabled(RISCVCPU *cpu, uint32_t ctr_idx) +{ + CPURISCVState *env = &cpu->env; + + if (riscv_pmu_counter_valid(cpu, ctr_idx) && + !get_field(env->mcountinhibit, BIT(ctr_idx))) { + return true; + } else { + return false; + } +} + +static int riscv_pmu_incr_ctr_rv32(RISCVCPU *cpu, uint32_t ctr_idx) +{ + CPURISCVState *env = &cpu->env; + target_ulong max_val = UINT32_MAX; + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + bool virt_on = env->virt_enabled; + + /* Privilege mode filtering */ + if ((env->priv == PRV_M && + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_MINH)) || + (env->priv == PRV_S && virt_on && + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_VSINH)) || + (env->priv == PRV_U && virt_on && + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_VUINH)) || + (env->priv == PRV_S && !virt_on && + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_SINH)) || + (env->priv == PRV_U && !virt_on && + (env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_UINH))) { + return 0; + } + + /* Handle the overflow scenario */ + if (counter->mhpmcounter_val == max_val) { + if (counter->mhpmcounterh_val == max_val) { + counter->mhpmcounter_val = 0; + counter->mhpmcounterh_val = 0; + /* Generate interrupt only if OF bit is clear */ + if (!(env->mhpmeventh_val[ctr_idx] & MHPMEVENTH_BIT_OF)) { + env->mhpmeventh_val[ctr_idx] |= MHPMEVENTH_BIT_OF; + riscv_cpu_update_mip(env, MIP_LCOFIP, BOOL_TO_MASK(1)); + } + } else { + counter->mhpmcounterh_val++; + } + } else { + counter->mhpmcounter_val++; + } + + return 0; +} + +static int riscv_pmu_incr_ctr_rv64(RISCVCPU *cpu, uint32_t ctr_idx) +{ + CPURISCVState *env = &cpu->env; + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + uint64_t max_val = UINT64_MAX; + bool virt_on = env->virt_enabled; + + /* Privilege mode filtering */ + if ((env->priv == PRV_M && + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_MINH)) || + (env->priv == PRV_S && virt_on && + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_VSINH)) || + (env->priv == PRV_U && virt_on && + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_VUINH)) || + (env->priv == PRV_S && !virt_on && + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_SINH)) || + (env->priv == PRV_U && !virt_on && + (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_UINH))) { + return 0; + } + + /* Handle the overflow scenario */ + if (counter->mhpmcounter_val == max_val) { + counter->mhpmcounter_val = 0; + /* Generate interrupt only if OF bit is clear */ + if (!(env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_OF)) { + env->mhpmevent_val[ctr_idx] |= MHPMEVENT_BIT_OF; + riscv_cpu_update_mip(env, MIP_LCOFIP, BOOL_TO_MASK(1)); + } + } else { + counter->mhpmcounter_val++; + } + return 0; +} + +int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) +{ + uint32_t ctr_idx; + int ret; + CPURISCVState *env = &cpu->env; + gpointer value; + + if (!cpu->cfg.pmu_mask) { + return 0; + } + value = g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(event_idx)); + if (!value) { + return -1; + } + + ctr_idx = GPOINTER_TO_UINT(value); + if (!riscv_pmu_counter_enabled(cpu, ctr_idx) || + get_field(env->mcountinhibit, BIT(ctr_idx))) { + return -1; + } + + if (riscv_cpu_mxl(env) == MXL_RV32) { + ret = riscv_pmu_incr_ctr_rv32(cpu, ctr_idx); + } else { + ret = riscv_pmu_incr_ctr_rv64(cpu, ctr_idx); + } + + return ret; +} + +bool riscv_pmu_ctr_monitor_instructions(CPURISCVState *env, + uint32_t target_ctr) +{ + RISCVCPU *cpu; + uint32_t event_idx; + uint32_t ctr_idx; + + /* Fixed instret counter */ + if (target_ctr == 2) { + return true; + } + + cpu = env_archcpu(env); + if (!cpu->pmu_event_ctr_map) { + return false; + } + + event_idx = RISCV_PMU_EVENT_HW_INSTRUCTIONS; + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(event_idx))); + if (!ctr_idx) { + return false; + } + + return target_ctr == ctr_idx ? true : false; +} + +bool riscv_pmu_ctr_monitor_cycles(CPURISCVState *env, uint32_t target_ctr) +{ + RISCVCPU *cpu; + uint32_t event_idx; + uint32_t ctr_idx; + + /* Fixed mcycle counter */ + if (target_ctr == 0) { + return true; + } + + cpu = env_archcpu(env); + if (!cpu->pmu_event_ctr_map) { + return false; + } + + event_idx = RISCV_PMU_EVENT_HW_CPU_CYCLES; + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(event_idx))); + + /* Counter zero is not used for event_ctr_map */ + if (!ctr_idx) { + return false; + } + + return (target_ctr == ctr_idx) ? true : false; +} + +static gboolean pmu_remove_event_map(gpointer key, gpointer value, + gpointer udata) +{ + return (GPOINTER_TO_UINT(value) == GPOINTER_TO_UINT(udata)) ? true : false; +} + +static int64_t pmu_icount_ticks_to_ns(int64_t value) +{ + int64_t ret = 0; + + if (icount_enabled()) { + ret = icount_to_ns(value); + } else { + ret = (NANOSECONDS_PER_SECOND / RISCV_TIMEBASE_FREQ) * value; + } + + return ret; +} + +int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, + uint32_t ctr_idx) +{ + uint32_t event_idx; + RISCVCPU *cpu = env_archcpu(env); + + if (!riscv_pmu_counter_valid(cpu, ctr_idx) || !cpu->pmu_event_ctr_map) { + return -1; + } + + /* + * Expected mhpmevent value is zero for reset case. Remove the current + * mapping. + */ + if (!value) { + g_hash_table_foreach_remove(cpu->pmu_event_ctr_map, + pmu_remove_event_map, + GUINT_TO_POINTER(ctr_idx)); + return 0; + } + + event_idx = value & MHPMEVENT_IDX_MASK; + if (g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(event_idx))) { + return 0; + } + + switch (event_idx) { + case RISCV_PMU_EVENT_HW_CPU_CYCLES: + case RISCV_PMU_EVENT_HW_INSTRUCTIONS: + case RISCV_PMU_EVENT_CACHE_DTLB_READ_MISS: + case RISCV_PMU_EVENT_CACHE_DTLB_WRITE_MISS: + case RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS: + break; + default: + /* We don't support any raw events right now */ + return -1; + } + g_hash_table_insert(cpu->pmu_event_ctr_map, GUINT_TO_POINTER(event_idx), + GUINT_TO_POINTER(ctr_idx)); + + return 0; +} + +static void pmu_timer_trigger_irq(RISCVCPU *cpu, + enum riscv_pmu_event_idx evt_idx) +{ + uint32_t ctr_idx; + CPURISCVState *env = &cpu->env; + PMUCTRState *counter; + target_ulong *mhpmevent_val; + uint64_t of_bit_mask; + int64_t irq_trigger_at; + + if (evt_idx != RISCV_PMU_EVENT_HW_CPU_CYCLES && + evt_idx != RISCV_PMU_EVENT_HW_INSTRUCTIONS) { + return; + } + + ctr_idx = GPOINTER_TO_UINT(g_hash_table_lookup(cpu->pmu_event_ctr_map, + GUINT_TO_POINTER(evt_idx))); + if (!riscv_pmu_counter_enabled(cpu, ctr_idx)) { + return; + } + + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpmevent_val = &env->mhpmeventh_val[ctr_idx]; + of_bit_mask = MHPMEVENTH_BIT_OF; + } else { + mhpmevent_val = &env->mhpmevent_val[ctr_idx]; + of_bit_mask = MHPMEVENT_BIT_OF; + } + + counter = &env->pmu_ctrs[ctr_idx]; + if (counter->irq_overflow_left > 0) { + irq_trigger_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + counter->irq_overflow_left; + timer_mod_anticipate_ns(cpu->pmu_timer, irq_trigger_at); + counter->irq_overflow_left = 0; + return; + } + + if (cpu->pmu_avail_ctrs & BIT(ctr_idx)) { + /* Generate interrupt only if OF bit is clear */ + if (!(*mhpmevent_val & of_bit_mask)) { + *mhpmevent_val |= of_bit_mask; + riscv_cpu_update_mip(env, MIP_LCOFIP, BOOL_TO_MASK(1)); + } + } +} + +/* Timer callback for instret and cycle counter overflow */ +void riscv_pmu_timer_cb(void *priv) +{ + RISCVCPU *cpu = priv; + + /* Timer event was triggered only for these events */ + pmu_timer_trigger_irq(cpu, RISCV_PMU_EVENT_HW_CPU_CYCLES); + pmu_timer_trigger_irq(cpu, RISCV_PMU_EVENT_HW_INSTRUCTIONS); +} + +int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx) +{ + uint64_t overflow_delta, overflow_at; + int64_t overflow_ns, overflow_left = 0; + RISCVCPU *cpu = env_archcpu(env); + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + + if (!riscv_pmu_counter_valid(cpu, ctr_idx) || !cpu->cfg.ext_sscofpmf) { + return -1; + } + + if (value) { + overflow_delta = UINT64_MAX - value + 1; + } else { + overflow_delta = UINT64_MAX; + } + + /* + * QEMU supports only int64_t timers while RISC-V counters are uint64_t. + * Compute the leftover and save it so that it can be reprogrammed again + * when timer expires. + */ + if (overflow_delta > INT64_MAX) { + overflow_left = overflow_delta - INT64_MAX; + } + + if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { + overflow_ns = pmu_icount_ticks_to_ns((int64_t)overflow_delta); + overflow_left = pmu_icount_ticks_to_ns(overflow_left) ; + } else { + return -1; + } + overflow_at = (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + overflow_ns; + + if (overflow_at > INT64_MAX) { + overflow_left += overflow_at - INT64_MAX; + counter->irq_overflow_left = overflow_left; + overflow_at = INT64_MAX; + } + timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); + + return 0; +} + + +void riscv_pmu_init(RISCVCPU *cpu, Error **errp) +{ + if (cpu->cfg.pmu_mask & (COUNTEREN_CY | COUNTEREN_TM | COUNTEREN_IR)) { + error_setg(errp, "\"pmu-mask\" contains invalid bits (0-2) set"); + return; + } + + if (ctpop32(cpu->cfg.pmu_mask) > (RV_MAX_MHPMCOUNTERS - 3)) { + error_setg(errp, "Number of counters exceeds maximum available"); + return; + } + + cpu->pmu_event_ctr_map = g_hash_table_new(g_direct_hash, g_direct_equal); + if (!cpu->pmu_event_ctr_map) { + error_setg(errp, "Unable to allocate PMU event hash table"); + return; + } + + cpu->pmu_avail_ctrs = cpu->cfg.pmu_mask; +} diff --git a/target/riscv/pmu.h b/target/riscv/pmu.h new file mode 100644 index 0000000000..7c0ad661e0 --- /dev/null +++ b/target/riscv/pmu.h @@ -0,0 +1,38 @@ +/* + * RISC-V PMU header file. + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef RISCV_PMU_H +#define RISCV_PMU_H + +#include "cpu.h" +#include "qapi/error.h" + +bool riscv_pmu_ctr_monitor_instructions(CPURISCVState *env, + uint32_t target_ctr); +bool riscv_pmu_ctr_monitor_cycles(CPURISCVState *env, + uint32_t target_ctr); +void riscv_pmu_timer_cb(void *priv); +void riscv_pmu_init(RISCVCPU *cpu, Error **errp); +int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, + uint32_t ctr_idx); +int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx); +void riscv_pmu_generate_fdt_node(void *fdt, uint32_t cmask, char *pmu_name); +int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, + uint32_t ctr_idx); + +#endif /* RISCV_PMU_H */ diff --git a/target/riscv/riscv-qmp-cmds.c b/target/riscv/riscv-qmp-cmds.c new file mode 100644 index 0000000000..d363dc318d --- /dev/null +++ b/target/riscv/riscv-qmp-cmds.c @@ -0,0 +1,242 @@ +/* + * QEMU CPU QMP commands for RISC-V + * + * Copyright (c) 2023 Ventana Micro Systems Inc. + * + * 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-machine-target.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/visitor.h" +#include "qom/qom-qobject.h" +#include "sysemu/kvm.h" +#include "sysemu/tcg.h" +#include "cpu-qom.h" +#include "cpu.h" + +static void riscv_cpu_add_definition(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CpuDefinitionInfoList **cpu_list = user_data; + CpuDefinitionInfo *info = g_malloc0(sizeof(*info)); + const char *typename = object_class_get_name(oc); + ObjectClass *dyn_class; + + info->name = cpu_model_from_type(typename); + info->q_typename = g_strdup(typename); + + dyn_class = object_class_dynamic_cast(oc, TYPE_RISCV_DYNAMIC_CPU); + info->q_static = dyn_class == NULL; + + QAPI_LIST_PREPEND(*cpu_list, info); +} + +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) +{ + CpuDefinitionInfoList *cpu_list = NULL; + GSList *list = object_class_get_list(TYPE_RISCV_CPU, false); + + g_slist_foreach(list, riscv_cpu_add_definition, &cpu_list); + g_slist_free(list); + + return cpu_list; +} + +static void riscv_check_if_cpu_available(RISCVCPU *cpu, Error **errp) +{ + if (!riscv_cpu_accelerator_compatible(cpu)) { + g_autofree char *name = riscv_cpu_get_name(cpu); + const char *accel = kvm_enabled() ? "kvm" : "tcg"; + + error_setg(errp, "'%s' CPU not available with %s", name, accel); + return; + } +} + +static void riscv_obj_add_qdict_prop(Object *obj, QDict *qdict_out, + const char *name) +{ + ObjectProperty *prop = object_property_find(obj, name); + + if (prop) { + QObject *value; + + assert(prop->get); + value = object_property_get_qobject(obj, name, &error_abort); + + qdict_put_obj(qdict_out, name, value); + } +} + +static void riscv_obj_add_multiext_props(Object *obj, QDict *qdict_out, + const RISCVCPUMultiExtConfig *arr) +{ + for (int i = 0; arr[i].name != NULL; i++) { + riscv_obj_add_qdict_prop(obj, qdict_out, arr[i].name); + } +} + +static void riscv_obj_add_named_feats_qdict(Object *obj, QDict *qdict_out) +{ + const RISCVCPUMultiExtConfig *named_cfg; + RISCVCPU *cpu = RISCV_CPU(obj); + QObject *value; + bool flag_val; + + for (int i = 0; riscv_cpu_named_features[i].name != NULL; i++) { + named_cfg = &riscv_cpu_named_features[i]; + flag_val = isa_ext_is_enabled(cpu, named_cfg->offset); + value = QOBJECT(qbool_from_bool(flag_val)); + + qdict_put_obj(qdict_out, named_cfg->name, value); + } +} + +static void riscv_obj_add_profiles_qdict(Object *obj, QDict *qdict_out) +{ + RISCVCPUProfile *profile; + QObject *value; + + for (int i = 0; riscv_profiles[i] != NULL; i++) { + profile = riscv_profiles[i]; + value = QOBJECT(qbool_from_bool(profile->enabled)); + + qdict_put_obj(qdict_out, profile->name, value); + } +} + +static void riscv_cpuobj_validate_qdict_in(Object *obj, QObject *props, + const char *props_arg_name, + Error **errp) +{ + const QDict *qdict_in; + const QDictEntry *qe; + Visitor *visitor; + Error *local_err = NULL; + + visitor = qobject_input_visitor_new(props); + if (!visit_start_struct(visitor, props_arg_name, NULL, 0, &local_err)) { + goto err; + } + + qdict_in = qobject_to(QDict, props); + for (qe = qdict_first(qdict_in); qe; qe = qdict_next(qdict_in, qe)) { + object_property_find_err(obj, qe->key, &local_err); + if (local_err) { + goto err; + } + + object_property_set(obj, qe->key, visitor, &local_err); + if (local_err) { + goto err; + } + } + + visit_check_struct(visitor, &local_err); + if (local_err) { + goto err; + } + + visit_end_struct(visitor, NULL); + +err: + error_propagate(errp, local_err); + visit_free(visitor); +} + +CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + CpuModelExpansionInfo *expansion_info; + QDict *qdict_out; + ObjectClass *oc; + Object *obj; + Error *local_err = NULL; + + if (type != CPU_MODEL_EXPANSION_TYPE_FULL) { + error_setg(errp, "The requested expansion type is not supported"); + return NULL; + } + + oc = cpu_class_by_name(TYPE_RISCV_CPU, model->name); + if (!oc) { + error_setg(errp, "The CPU type '%s' is not a known RISC-V CPU type", + model->name); + return NULL; + } + + obj = object_new(object_class_get_name(oc)); + + riscv_check_if_cpu_available(RISCV_CPU(obj), &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + object_unref(obj); + return NULL; + } + + if (model->props) { + riscv_cpuobj_validate_qdict_in(obj, model->props, "model.props", + &local_err); + if (local_err) { + error_propagate(errp, local_err); + object_unref(obj); + return NULL; + } + } + + riscv_cpu_finalize_features(RISCV_CPU(obj), &local_err); + if (local_err) { + error_propagate(errp, local_err); + object_unref(obj); + return NULL; + } + + expansion_info = g_new0(CpuModelExpansionInfo, 1); + expansion_info->model = g_malloc0(sizeof(*expansion_info->model)); + expansion_info->model->name = g_strdup(model->name); + + qdict_out = qdict_new(); + + riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_extensions); + riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_experimental_exts); + riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_vendor_exts); + riscv_obj_add_named_feats_qdict(obj, qdict_out); + riscv_obj_add_profiles_qdict(obj, qdict_out); + + /* Add our CPU boolean options too */ + riscv_obj_add_qdict_prop(obj, qdict_out, "mmu"); + riscv_obj_add_qdict_prop(obj, qdict_out, "pmp"); + + if (!qdict_size(qdict_out)) { + qobject_unref(qdict_out); + } else { + expansion_info->model->props = QOBJECT(qdict_out); + } + + object_unref(obj); + + return expansion_info; +} diff --git a/target/riscv/sbi_ecall_interface.h b/target/riscv/sbi_ecall_interface.h index 77574ed4cb..43899d08f6 100644 --- a/target/riscv/sbi_ecall_interface.h +++ b/target/riscv/sbi_ecall_interface.h @@ -28,7 +28,7 @@ #define SBI_EXT_RFENCE 0x52464E43 #define SBI_EXT_HSM 0x48534D -/* SBI function IDs for BASE extension*/ +/* SBI function IDs for BASE extension */ #define SBI_EXT_BASE_GET_SPEC_VERSION 0x0 #define SBI_EXT_BASE_GET_IMP_ID 0x1 #define SBI_EXT_BASE_GET_IMP_VERSION 0x2 @@ -37,13 +37,13 @@ #define SBI_EXT_BASE_GET_MARCHID 0x5 #define SBI_EXT_BASE_GET_MIMPID 0x6 -/* SBI function IDs for TIME extension*/ +/* SBI function IDs for TIME extension */ #define SBI_EXT_TIME_SET_TIMER 0x0 -/* SBI function IDs for IPI extension*/ +/* SBI function IDs for IPI extension */ #define SBI_EXT_IPI_SEND_IPI 0x0 -/* SBI function IDs for RFENCE extension*/ +/* SBI function IDs for RFENCE extension */ #define SBI_EXT_RFENCE_REMOTE_FENCE_I 0x0 #define SBI_EXT_RFENCE_REMOTE_SFENCE_VMA 0x1 #define SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID 0x2 diff --git a/target/riscv/tcg/meson.build b/target/riscv/tcg/meson.build new file mode 100644 index 0000000000..061df3d74a --- /dev/null +++ b/target/riscv/tcg/meson.build @@ -0,0 +1,2 @@ +riscv_ss.add(when: 'CONFIG_TCG', if_true: files( + 'tcg-cpu.c')) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c new file mode 100644 index 0000000000..b5b95e052d --- /dev/null +++ b/target/riscv/tcg/tcg-cpu.c @@ -0,0 +1,1367 @@ +/* + * riscv TCG cpu class initialization + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, 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 "exec/exec-all.h" +#include "tcg-cpu.h" +#include "cpu.h" +#include "pmu.h" +#include "time_helper.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qemu/accel.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "hw/core/accel-cpu.h" +#include "hw/core/tcg-cpu-ops.h" +#include "tcg/tcg.h" + +/* Hash that stores user set extensions */ +static GHashTable *multi_ext_user_opts; +static GHashTable *misa_ext_user_opts; + +static bool cpu_cfg_ext_is_user_set(uint32_t ext_offset) +{ + return g_hash_table_contains(multi_ext_user_opts, + GUINT_TO_POINTER(ext_offset)); +} + +static bool cpu_misa_ext_is_user_set(uint32_t misa_bit) +{ + return g_hash_table_contains(misa_ext_user_opts, + GUINT_TO_POINTER(misa_bit)); +} + +static void cpu_cfg_ext_add_user_opt(uint32_t ext_offset, bool value) +{ + g_hash_table_insert(multi_ext_user_opts, GUINT_TO_POINTER(ext_offset), + (gpointer)value); +} + +static void cpu_misa_ext_add_user_opt(uint32_t bit, bool value) +{ + g_hash_table_insert(misa_ext_user_opts, GUINT_TO_POINTER(bit), + (gpointer)value); +} + +static void riscv_cpu_write_misa_bit(RISCVCPU *cpu, uint32_t bit, + bool enabled) +{ + CPURISCVState *env = &cpu->env; + + if (enabled) { + env->misa_ext |= bit; + env->misa_ext_mask |= bit; + } else { + env->misa_ext &= ~bit; + env->misa_ext_mask &= ~bit; + } +} + +static const char *cpu_priv_ver_to_str(int priv_ver) +{ + switch (priv_ver) { + case PRIV_VERSION_1_10_0: + return "v1.10.0"; + case PRIV_VERSION_1_11_0: + return "v1.11.0"; + case PRIV_VERSION_1_12_0: + return "v1.12.0"; + } + + g_assert_not_reached(); +} + +static void riscv_cpu_synchronize_from_tb(CPUState *cs, + const TranslationBlock *tb) +{ + if (!(tb_cflags(tb) & CF_PCREL)) { + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL); + + tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + + if (xl == MXL_RV32) { + env->pc = (int32_t) tb->pc; + } else { + env->pc = tb->pc; + } + } +} + +static void riscv_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL); + target_ulong pc; + + if (tb_cflags(tb) & CF_PCREL) { + pc = (env->pc & TARGET_PAGE_MASK) | data[0]; + } else { + pc = data[0]; + } + + if (xl == MXL_RV32) { + env->pc = (int32_t)pc; + } else { + env->pc = pc; + } + env->bins = data[1]; +} + +static const TCGCPUOps riscv_tcg_ops = { + .initialize = riscv_translate_init, + .synchronize_from_tb = riscv_cpu_synchronize_from_tb, + .restore_state_to_opc = riscv_restore_state_to_opc, + +#ifndef CONFIG_USER_ONLY + .tlb_fill = riscv_cpu_tlb_fill, + .cpu_exec_interrupt = riscv_cpu_exec_interrupt, + .do_interrupt = riscv_cpu_do_interrupt, + .do_transaction_failed = riscv_cpu_do_transaction_failed, + .do_unaligned_access = riscv_cpu_do_unaligned_access, + .debug_excp_handler = riscv_cpu_debug_excp_handler, + .debug_check_breakpoint = riscv_cpu_debug_check_breakpoint, + .debug_check_watchpoint = riscv_cpu_debug_check_watchpoint, +#endif /* !CONFIG_USER_ONLY */ +}; + +static int cpu_cfg_ext_get_min_version(uint32_t ext_offset) +{ + const RISCVIsaExtData *edata; + + for (edata = isa_edata_arr; edata && edata->name; edata++) { + if (edata->ext_enable_offset != ext_offset) { + continue; + } + + return edata->min_version; + } + + g_assert_not_reached(); +} + +static const char *cpu_cfg_ext_get_name(uint32_t ext_offset) +{ + const RISCVCPUMultiExtConfig *feat; + const RISCVIsaExtData *edata; + + for (edata = isa_edata_arr; edata->name != NULL; edata++) { + if (edata->ext_enable_offset == ext_offset) { + return edata->name; + } + } + + for (feat = riscv_cpu_named_features; feat->name != NULL; feat++) { + if (feat->offset == ext_offset) { + return feat->name; + } + } + + g_assert_not_reached(); +} + +static bool cpu_cfg_offset_is_named_feat(uint32_t ext_offset) +{ + const RISCVCPUMultiExtConfig *feat; + + for (feat = riscv_cpu_named_features; feat->name != NULL; feat++) { + if (feat->offset == ext_offset) { + return true; + } + } + + return false; +} + +static void riscv_cpu_enable_named_feat(RISCVCPU *cpu, uint32_t feat_offset) +{ + /* + * All other named features are already enabled + * in riscv_tcg_cpu_instance_init(). + */ + if (feat_offset == CPU_CFG_OFFSET(ext_zic64b)) { + cpu->cfg.cbom_blocksize = 64; + cpu->cfg.cbop_blocksize = 64; + cpu->cfg.cboz_blocksize = 64; + } +} + +static void cpu_bump_multi_ext_priv_ver(CPURISCVState *env, + uint32_t ext_offset) +{ + int ext_priv_ver; + + if (env->priv_ver == PRIV_VERSION_LATEST) { + return; + } + + ext_priv_ver = cpu_cfg_ext_get_min_version(ext_offset); + + if (env->priv_ver < ext_priv_ver) { + /* + * Note: the 'priv_spec' command line option, if present, + * will take precedence over this priv_ver bump. + */ + env->priv_ver = ext_priv_ver; + } +} + +static void cpu_cfg_ext_auto_update(RISCVCPU *cpu, uint32_t ext_offset, + bool value) +{ + CPURISCVState *env = &cpu->env; + bool prev_val = isa_ext_is_enabled(cpu, ext_offset); + int min_version; + + if (prev_val == value) { + return; + } + + if (cpu_cfg_ext_is_user_set(ext_offset)) { + return; + } + + if (value && env->priv_ver != PRIV_VERSION_LATEST) { + /* Do not enable it if priv_ver is older than min_version */ + min_version = cpu_cfg_ext_get_min_version(ext_offset); + if (env->priv_ver < min_version) { + return; + } + } + + isa_ext_update_enabled(cpu, ext_offset, value); +} + +static void riscv_cpu_validate_misa_priv(CPURISCVState *env, Error **errp) +{ + if (riscv_has_ext(env, RVH) && env->priv_ver < PRIV_VERSION_1_12_0) { + error_setg(errp, "H extension requires priv spec 1.12.0"); + return; + } +} + +static void riscv_cpu_validate_v(CPURISCVState *env, RISCVCPUConfig *cfg, + Error **errp) +{ + uint32_t vlen = cfg->vlenb << 3; + + if (vlen > RV_VLEN_MAX || vlen < 128) { + error_setg(errp, + "Vector extension implementation only supports VLEN " + "in the range [128, %d]", RV_VLEN_MAX); + return; + } + + if (cfg->elen > 64 || cfg->elen < 8) { + error_setg(errp, + "Vector extension implementation only supports ELEN " + "in the range [8, 64]"); + return; + } +} + +static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu) +{ + CPURISCVState *env = &cpu->env; + const RISCVIsaExtData *edata; + + /* Force disable extensions if priv spec version does not match */ + for (edata = isa_edata_arr; edata && edata->name; edata++) { + if (isa_ext_is_enabled(cpu, edata->ext_enable_offset) && + (env->priv_ver < edata->min_version)) { + /* + * These two extensions are always enabled as they were supported + * by QEMU before they were added as extensions in the ISA. + */ + if (!strcmp(edata->name, "zicntr") || + !strcmp(edata->name, "zihpm")) { + continue; + } + + isa_ext_update_enabled(cpu, edata->ext_enable_offset, false); +#ifndef CONFIG_USER_ONLY + warn_report("disabling %s extension for hart 0x" TARGET_FMT_lx + " because privilege spec version does not match", + edata->name, env->mhartid); +#else + warn_report("disabling %s extension because " + "privilege spec version does not match", + edata->name); +#endif + } + } +} + +static void riscv_cpu_update_named_features(RISCVCPU *cpu) +{ + if (cpu->env.priv_ver >= PRIV_VERSION_1_11_0) { + cpu->cfg.has_priv_1_11 = true; + } + + if (cpu->env.priv_ver >= PRIV_VERSION_1_12_0) { + cpu->cfg.has_priv_1_12 = true; + } + + /* zic64b is 1.12 or later */ + cpu->cfg.ext_zic64b = cpu->cfg.cbom_blocksize == 64 && + cpu->cfg.cbop_blocksize == 64 && + cpu->cfg.cboz_blocksize == 64 && + cpu->cfg.has_priv_1_12; +} + +static void riscv_cpu_validate_g(RISCVCPU *cpu) +{ + const char *warn_msg = "RVG mandates disabled extension %s"; + uint32_t g_misa_bits[] = {RVI, RVM, RVA, RVF, RVD}; + bool send_warn = cpu_misa_ext_is_user_set(RVG); + + for (int i = 0; i < ARRAY_SIZE(g_misa_bits); i++) { + uint32_t bit = g_misa_bits[i]; + + if (riscv_has_ext(&cpu->env, bit)) { + continue; + } + + if (!cpu_misa_ext_is_user_set(bit)) { + riscv_cpu_write_misa_bit(cpu, bit, true); + continue; + } + + if (send_warn) { + warn_report(warn_msg, riscv_get_misa_ext_name(bit)); + } + } + + if (!cpu->cfg.ext_zicsr) { + if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zicsr))) { + cpu->cfg.ext_zicsr = true; + } else if (send_warn) { + warn_report(warn_msg, "zicsr"); + } + } + + if (!cpu->cfg.ext_zifencei) { + if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zifencei))) { + cpu->cfg.ext_zifencei = true; + } else if (send_warn) { + warn_report(warn_msg, "zifencei"); + } + } +} + +static void riscv_cpu_validate_b(RISCVCPU *cpu) +{ + const char *warn_msg = "RVB mandates disabled extension %s"; + + if (!cpu->cfg.ext_zba) { + if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zba))) { + cpu->cfg.ext_zba = true; + } else { + warn_report(warn_msg, "zba"); + } + } + + if (!cpu->cfg.ext_zbb) { + if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zbb))) { + cpu->cfg.ext_zbb = true; + } else { + warn_report(warn_msg, "zbb"); + } + } + + if (!cpu->cfg.ext_zbs) { + if (!cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zbs))) { + cpu->cfg.ext_zbs = true; + } else { + warn_report(warn_msg, "zbs"); + } + } +} + +/* + * Check consistency between chosen extensions while setting + * cpu->cfg accordingly. + */ +void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) +{ + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); + CPURISCVState *env = &cpu->env; + Error *local_err = NULL; + + if (riscv_has_ext(env, RVG)) { + riscv_cpu_validate_g(cpu); + } + + if (riscv_has_ext(env, RVB)) { + riscv_cpu_validate_b(cpu); + } + + if (riscv_has_ext(env, RVI) && riscv_has_ext(env, RVE)) { + error_setg(errp, + "I and E extensions are incompatible"); + return; + } + + if (!riscv_has_ext(env, RVI) && !riscv_has_ext(env, RVE)) { + error_setg(errp, + "Either I or E extension must be set"); + return; + } + + if (riscv_has_ext(env, RVS) && !riscv_has_ext(env, RVU)) { + error_setg(errp, + "Setting S extension without U extension is illegal"); + return; + } + + if (riscv_has_ext(env, RVH) && !riscv_has_ext(env, RVI)) { + error_setg(errp, + "H depends on an I base integer ISA with 32 x registers"); + return; + } + + if (riscv_has_ext(env, RVH) && !riscv_has_ext(env, RVS)) { + error_setg(errp, "H extension implicitly requires S-mode"); + return; + } + + if (riscv_has_ext(env, RVF) && !cpu->cfg.ext_zicsr) { + error_setg(errp, "F extension requires Zicsr"); + return; + } + + if ((cpu->cfg.ext_zacas) && !riscv_has_ext(env, RVA)) { + error_setg(errp, "Zacas extension requires A extension"); + return; + } + + if ((cpu->cfg.ext_zawrs) && !riscv_has_ext(env, RVA)) { + error_setg(errp, "Zawrs extension requires A extension"); + return; + } + + if (cpu->cfg.ext_zfa && !riscv_has_ext(env, RVF)) { + error_setg(errp, "Zfa extension requires F extension"); + return; + } + + if (cpu->cfg.ext_zfh) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zfhmin), true); + } + + if (cpu->cfg.ext_zfhmin && !riscv_has_ext(env, RVF)) { + error_setg(errp, "Zfh/Zfhmin extensions require F extension"); + return; + } + + if (cpu->cfg.ext_zfbfmin && !riscv_has_ext(env, RVF)) { + error_setg(errp, "Zfbfmin extension depends on F extension"); + return; + } + + if (riscv_has_ext(env, RVD) && !riscv_has_ext(env, RVF)) { + error_setg(errp, "D extension requires F extension"); + return; + } + + if (riscv_has_ext(env, RVV)) { + riscv_cpu_validate_v(env, &cpu->cfg, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + /* The V vector extension depends on the Zve64d extension */ + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve64d), true); + } + + /* The Zve64d extension depends on the Zve64f extension */ + if (cpu->cfg.ext_zve64d) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve64f), true); + } + + /* The Zve64f extension depends on the Zve32f extension */ + if (cpu->cfg.ext_zve64f) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve32f), true); + } + + if (cpu->cfg.ext_zve64d && !riscv_has_ext(env, RVD)) { + error_setg(errp, "Zve64d/V extensions require D extension"); + return; + } + + if (cpu->cfg.ext_zve32f && !riscv_has_ext(env, RVF)) { + error_setg(errp, "Zve32f/Zve64f extensions require F extension"); + return; + } + + if (cpu->cfg.ext_zvfh) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvfhmin), true); + } + + if (cpu->cfg.ext_zvfhmin && !cpu->cfg.ext_zve32f) { + error_setg(errp, "Zvfh/Zvfhmin extensions require Zve32f extension"); + return; + } + + if (cpu->cfg.ext_zvfh && !cpu->cfg.ext_zfhmin) { + error_setg(errp, "Zvfh extensions requires Zfhmin extension"); + return; + } + + if (cpu->cfg.ext_zvfbfmin && !cpu->cfg.ext_zve32f) { + error_setg(errp, "Zvfbfmin extension depends on Zve32f extension"); + return; + } + + if (cpu->cfg.ext_zvfbfwma && !cpu->cfg.ext_zvfbfmin) { + error_setg(errp, "Zvfbfwma extension depends on Zvfbfmin extension"); + return; + } + + /* Set the ISA extensions, checks should have happened above */ + if (cpu->cfg.ext_zhinx) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true); + } + + if ((cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinxmin) && !cpu->cfg.ext_zfinx) { + error_setg(errp, "Zdinx/Zhinx/Zhinxmin extensions require Zfinx"); + return; + } + + if (cpu->cfg.ext_zfinx) { + if (!cpu->cfg.ext_zicsr) { + error_setg(errp, "Zfinx extension requires Zicsr"); + return; + } + if (riscv_has_ext(env, RVF)) { + error_setg(errp, + "Zfinx cannot be supported together with F extension"); + return; + } + } + + if (cpu->cfg.ext_zce) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcb), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcmp), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcmt), true); + if (riscv_has_ext(env, RVF) && mcc->misa_mxl_max == MXL_RV32) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcf), true); + } + } + + /* zca, zcd and zcf has a PRIV 1.12.0 restriction */ + if (riscv_has_ext(env, RVC) && env->priv_ver >= PRIV_VERSION_1_12_0) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true); + if (riscv_has_ext(env, RVF) && mcc->misa_mxl_max == MXL_RV32) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcf), true); + } + if (riscv_has_ext(env, RVD)) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcd), true); + } + } + + if (mcc->misa_mxl_max != MXL_RV32 && cpu->cfg.ext_zcf) { + error_setg(errp, "Zcf extension is only relevant to RV32"); + return; + } + + if (!riscv_has_ext(env, RVF) && cpu->cfg.ext_zcf) { + error_setg(errp, "Zcf extension requires F extension"); + return; + } + + if (!riscv_has_ext(env, RVD) && cpu->cfg.ext_zcd) { + error_setg(errp, "Zcd extension requires D extension"); + return; + } + + if ((cpu->cfg.ext_zcf || cpu->cfg.ext_zcd || cpu->cfg.ext_zcb || + cpu->cfg.ext_zcmp || cpu->cfg.ext_zcmt) && !cpu->cfg.ext_zca) { + error_setg(errp, "Zcf/Zcd/Zcb/Zcmp/Zcmt extensions require Zca " + "extension"); + return; + } + + if (cpu->cfg.ext_zcd && (cpu->cfg.ext_zcmp || cpu->cfg.ext_zcmt)) { + error_setg(errp, "Zcmp/Zcmt extensions are incompatible with " + "Zcd extension"); + return; + } + + if (cpu->cfg.ext_zcmt && !cpu->cfg.ext_zicsr) { + error_setg(errp, "Zcmt extension requires Zicsr extension"); + return; + } + + /* + * Shorthand vector crypto extensions + */ + if (cpu->cfg.ext_zvknc) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkn), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbc), true); + } + + if (cpu->cfg.ext_zvkng) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkn), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkg), true); + } + + if (cpu->cfg.ext_zvkn) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkned), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvknhb), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkb), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkt), true); + } + + if (cpu->cfg.ext_zvksc) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvks), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbc), true); + } + + if (cpu->cfg.ext_zvksg) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvks), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkg), true); + } + + if (cpu->cfg.ext_zvks) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvksed), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvksh), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkb), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvkt), true); + } + + if (cpu->cfg.ext_zvkt) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbb), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvbc), true); + } + + /* + * In principle Zve*x would also suffice here, were they supported + * in qemu + */ + if ((cpu->cfg.ext_zvbb || cpu->cfg.ext_zvkb || cpu->cfg.ext_zvkg || + cpu->cfg.ext_zvkned || cpu->cfg.ext_zvknha || cpu->cfg.ext_zvksed || + cpu->cfg.ext_zvksh) && !cpu->cfg.ext_zve32f) { + error_setg(errp, + "Vector crypto extensions require V or Zve* extensions"); + return; + } + + if ((cpu->cfg.ext_zvbc || cpu->cfg.ext_zvknhb) && !cpu->cfg.ext_zve64f) { + error_setg( + errp, + "Zvbc and Zvknhb extensions require V or Zve64{f,d} extensions"); + return; + } + + if (cpu->cfg.ext_zk) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkn), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkr), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkt), true); + } + + if (cpu->cfg.ext_zkn) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkb), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkc), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkx), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkne), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zknd), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zknh), true); + } + + if (cpu->cfg.ext_zks) { + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkb), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkc), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkx), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zksed), true); + cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zksh), true); + } + + if (cpu->cfg.ext_zicntr && !cpu->cfg.ext_zicsr) { + if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zicntr))) { + error_setg(errp, "zicntr requires zicsr"); + return; + } + cpu->cfg.ext_zicntr = false; + } + + if (cpu->cfg.ext_zihpm && !cpu->cfg.ext_zicsr) { + if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zihpm))) { + error_setg(errp, "zihpm requires zicsr"); + return; + } + cpu->cfg.ext_zihpm = false; + } + + if (!cpu->cfg.ext_zihpm) { + cpu->cfg.pmu_mask = 0; + cpu->pmu_avail_ctrs = 0; + } + + /* + * Disable isa extensions based on priv spec after we + * validated and set everything we need. + */ + riscv_cpu_disable_priv_spec_isa_exts(cpu); +} + +#ifndef CONFIG_USER_ONLY +static bool riscv_cpu_validate_profile_satp(RISCVCPU *cpu, + RISCVCPUProfile *profile, + bool send_warn) +{ + int satp_max = satp_mode_max_from_map(cpu->cfg.satp_mode.supported); + + if (profile->satp_mode > satp_max) { + if (send_warn) { + bool is_32bit = riscv_cpu_is_32bit(cpu); + const char *req_satp = satp_mode_str(profile->satp_mode, is_32bit); + const char *cur_satp = satp_mode_str(satp_max, is_32bit); + + warn_report("Profile %s requires satp mode %s, " + "but satp mode %s was set", profile->name, + req_satp, cur_satp); + } + + return false; + } + + return true; +} +#endif + +static void riscv_cpu_validate_profile(RISCVCPU *cpu, + RISCVCPUProfile *profile) +{ + CPURISCVState *env = &cpu->env; + const char *warn_msg = "Profile %s mandates disabled extension %s"; + bool send_warn = profile->user_set && profile->enabled; + bool parent_enabled, profile_impl = true; + int i; + +#ifndef CONFIG_USER_ONLY + if (profile->satp_mode != RISCV_PROFILE_ATTR_UNUSED) { + profile_impl = riscv_cpu_validate_profile_satp(cpu, profile, + send_warn); + } +#endif + + if (profile->priv_spec != RISCV_PROFILE_ATTR_UNUSED && + profile->priv_spec != env->priv_ver) { + profile_impl = false; + + if (send_warn) { + warn_report("Profile %s requires priv spec %s, " + "but priv ver %s was set", profile->name, + cpu_priv_ver_to_str(profile->priv_spec), + cpu_priv_ver_to_str(env->priv_ver)); + } + } + + for (i = 0; misa_bits[i] != 0; i++) { + uint32_t bit = misa_bits[i]; + + if (!(profile->misa_ext & bit)) { + continue; + } + + if (!riscv_has_ext(&cpu->env, bit)) { + profile_impl = false; + + if (send_warn) { + warn_report(warn_msg, profile->name, + riscv_get_misa_ext_name(bit)); + } + } + } + + for (i = 0; profile->ext_offsets[i] != RISCV_PROFILE_EXT_LIST_END; i++) { + int ext_offset = profile->ext_offsets[i]; + + if (!isa_ext_is_enabled(cpu, ext_offset)) { + profile_impl = false; + + if (send_warn) { + warn_report(warn_msg, profile->name, + cpu_cfg_ext_get_name(ext_offset)); + } + } + } + + profile->enabled = profile_impl; + + if (profile->parent != NULL) { + parent_enabled = object_property_get_bool(OBJECT(cpu), + profile->parent->name, + NULL); + profile->enabled = profile->enabled && parent_enabled; + } +} + +static void riscv_cpu_validate_profiles(RISCVCPU *cpu) +{ + for (int i = 0; riscv_profiles[i] != NULL; i++) { + riscv_cpu_validate_profile(cpu, riscv_profiles[i]); + } +} + +void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp) +{ + CPURISCVState *env = &cpu->env; + Error *local_err = NULL; + + riscv_cpu_validate_misa_priv(env, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + riscv_cpu_update_named_features(cpu); + riscv_cpu_validate_profiles(cpu); + + if (cpu->cfg.ext_smepmp && !cpu->cfg.pmp) { + /* + * Enhanced PMP should only be available + * on harts with PMP support + */ + error_setg(errp, "Invalid configuration: Smepmp requires PMP support"); + return; + } + + riscv_cpu_validate_set_extensions(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } +} + +bool riscv_cpu_tcg_compatible(RISCVCPU *cpu) +{ + return object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST) == NULL; +} + +static bool riscv_cpu_is_generic(Object *cpu_obj) +{ + return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL; +} + +/* + * We'll get here via the following path: + * + * riscv_cpu_realize() + * -> cpu_exec_realizefn() + * -> tcg_cpu_realize() (via accel_cpu_common_realize()) + */ +static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + + if (!riscv_cpu_tcg_compatible(cpu)) { + g_autofree char *name = riscv_cpu_get_name(cpu); + error_setg(errp, "'%s' CPU is not compatible with TCG acceleration", + name); + return false; + } + +#ifndef CONFIG_USER_ONLY + CPURISCVState *env = &cpu->env; + Error *local_err = NULL; + + CPU(cs)->tcg_cflags |= CF_PCREL; + + if (cpu->cfg.ext_sstc) { + riscv_timer_init(cpu); + } + + if (cpu->cfg.pmu_mask) { + riscv_pmu_init(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return false; + } + + if (cpu->cfg.ext_sscofpmf) { + cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + riscv_pmu_timer_cb, cpu); + } + } + + /* With H-Ext, VSSIP, VSTIP, VSEIP and SGEIP are hardwired to one. */ + if (riscv_has_ext(env, RVH)) { + env->mideleg = MIP_VSSIP | MIP_VSTIP | MIP_VSEIP | MIP_SGEIP; + } +#endif + + return true; +} + +typedef struct RISCVCPUMisaExtConfig { + target_ulong misa_bit; + bool enabled; +} RISCVCPUMisaExtConfig; + +static void cpu_set_misa_ext_cfg(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + const RISCVCPUMisaExtConfig *misa_ext_cfg = opaque; + target_ulong misa_bit = misa_ext_cfg->misa_bit; + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + bool vendor_cpu = riscv_cpu_is_vendor(obj); + bool prev_val, value; + + if (!visit_type_bool(v, name, &value, errp)) { + return; + } + + cpu_misa_ext_add_user_opt(misa_bit, value); + + prev_val = env->misa_ext & misa_bit; + + if (value == prev_val) { + return; + } + + if (value) { + if (vendor_cpu) { + g_autofree char *cpuname = riscv_cpu_get_name(cpu); + error_setg(errp, "'%s' CPU does not allow enabling extensions", + cpuname); + return; + } + + if (misa_bit == RVH && env->priv_ver < PRIV_VERSION_1_12_0) { + /* + * Note: the 'priv_spec' command line option, if present, + * will take precedence over this priv_ver bump. + */ + env->priv_ver = PRIV_VERSION_1_12_0; + } + } + + riscv_cpu_write_misa_bit(cpu, misa_bit, value); +} + +static void cpu_get_misa_ext_cfg(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + const RISCVCPUMisaExtConfig *misa_ext_cfg = opaque; + target_ulong misa_bit = misa_ext_cfg->misa_bit; + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + bool value; + + value = env->misa_ext & misa_bit; + + visit_type_bool(v, name, &value, errp); +} + +#define MISA_CFG(_bit, _enabled) \ + {.misa_bit = _bit, .enabled = _enabled} + +static const RISCVCPUMisaExtConfig misa_ext_cfgs[] = { + MISA_CFG(RVA, true), + MISA_CFG(RVC, true), + MISA_CFG(RVD, true), + MISA_CFG(RVF, true), + MISA_CFG(RVI, true), + MISA_CFG(RVE, false), + MISA_CFG(RVM, true), + MISA_CFG(RVS, true), + MISA_CFG(RVU, true), + MISA_CFG(RVH, true), + MISA_CFG(RVJ, false), + MISA_CFG(RVV, false), + MISA_CFG(RVG, false), + MISA_CFG(RVB, false), +}; + +/* + * We do not support user choice tracking for MISA + * extensions yet because, so far, we do not silently + * change MISA bits during realize() (RVG enables MISA + * bits but the user is warned about it). + */ +static void riscv_cpu_add_misa_properties(Object *cpu_obj) +{ + bool use_def_vals = riscv_cpu_is_generic(cpu_obj); + int i; + + for (i = 0; i < ARRAY_SIZE(misa_ext_cfgs); i++) { + const RISCVCPUMisaExtConfig *misa_cfg = &misa_ext_cfgs[i]; + int bit = misa_cfg->misa_bit; + const char *name = riscv_get_misa_ext_name(bit); + const char *desc = riscv_get_misa_ext_description(bit); + + /* Check if KVM already created the property */ + if (object_property_find(cpu_obj, name)) { + continue; + } + + object_property_add(cpu_obj, name, "bool", + cpu_get_misa_ext_cfg, + cpu_set_misa_ext_cfg, + NULL, (void *)misa_cfg); + object_property_set_description(cpu_obj, name, desc); + if (use_def_vals) { + riscv_cpu_write_misa_bit(RISCV_CPU(cpu_obj), bit, + misa_cfg->enabled); + } + } +} + +static void cpu_set_profile(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPUProfile *profile = opaque; + RISCVCPU *cpu = RISCV_CPU(obj); + bool value; + int i, ext_offset; + + if (riscv_cpu_is_vendor(obj)) { + error_setg(errp, "Profile %s is not available for vendor CPUs", + profile->name); + return; + } + + if (cpu->env.misa_mxl != MXL_RV64) { + error_setg(errp, "Profile %s only available for 64 bit CPUs", + profile->name); + return; + } + + if (!visit_type_bool(v, name, &value, errp)) { + return; + } + + profile->user_set = true; + profile->enabled = value; + + if (profile->parent != NULL) { + object_property_set_bool(obj, profile->parent->name, + profile->enabled, NULL); + } + + if (profile->enabled) { + cpu->env.priv_ver = profile->priv_spec; + } + +#ifndef CONFIG_USER_ONLY + if (profile->satp_mode != RISCV_PROFILE_ATTR_UNUSED) { + object_property_set_bool(obj, "mmu", true, NULL); + const char *satp_prop = satp_mode_str(profile->satp_mode, + riscv_cpu_is_32bit(cpu)); + object_property_set_bool(obj, satp_prop, profile->enabled, NULL); + } +#endif + + for (i = 0; misa_bits[i] != 0; i++) { + uint32_t bit = misa_bits[i]; + + if (!(profile->misa_ext & bit)) { + continue; + } + + if (bit == RVI && !profile->enabled) { + /* + * Disabling profiles will not disable the base + * ISA RV64I. + */ + continue; + } + + cpu_misa_ext_add_user_opt(bit, profile->enabled); + riscv_cpu_write_misa_bit(cpu, bit, profile->enabled); + } + + for (i = 0; profile->ext_offsets[i] != RISCV_PROFILE_EXT_LIST_END; i++) { + ext_offset = profile->ext_offsets[i]; + + if (profile->enabled) { + if (cpu_cfg_offset_is_named_feat(ext_offset)) { + riscv_cpu_enable_named_feat(cpu, ext_offset); + } + + cpu_bump_multi_ext_priv_ver(&cpu->env, ext_offset); + } + + cpu_cfg_ext_add_user_opt(ext_offset, profile->enabled); + isa_ext_update_enabled(cpu, ext_offset, profile->enabled); + } +} + +static void cpu_get_profile(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPUProfile *profile = opaque; + bool value = profile->enabled; + + visit_type_bool(v, name, &value, errp); +} + +static void riscv_cpu_add_profiles(Object *cpu_obj) +{ + for (int i = 0; riscv_profiles[i] != NULL; i++) { + const RISCVCPUProfile *profile = riscv_profiles[i]; + + object_property_add(cpu_obj, profile->name, "bool", + cpu_get_profile, cpu_set_profile, + NULL, (void *)profile); + + /* + * CPUs might enable a profile right from the start. + * Enable its mandatory extensions right away in this + * case. + */ + if (profile->enabled) { + object_property_set_bool(cpu_obj, profile->name, true, NULL); + } + } +} + +static bool cpu_ext_is_deprecated(const char *ext_name) +{ + return isupper(ext_name[0]); +} + +/* + * String will be allocated in the heap. Caller is responsible + * for freeing it. + */ +static char *cpu_ext_to_lower(const char *ext_name) +{ + char *ret = g_malloc0(strlen(ext_name) + 1); + + strcpy(ret, ext_name); + ret[0] = tolower(ret[0]); + + return ret; +} + +static void cpu_set_multi_ext_cfg(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + const RISCVCPUMultiExtConfig *multi_ext_cfg = opaque; + RISCVCPU *cpu = RISCV_CPU(obj); + bool vendor_cpu = riscv_cpu_is_vendor(obj); + bool prev_val, value; + + if (!visit_type_bool(v, name, &value, errp)) { + return; + } + + if (cpu_ext_is_deprecated(multi_ext_cfg->name)) { + g_autofree char *lower = cpu_ext_to_lower(multi_ext_cfg->name); + + warn_report("CPU property '%s' is deprecated. Please use '%s' instead", + multi_ext_cfg->name, lower); + } + + cpu_cfg_ext_add_user_opt(multi_ext_cfg->offset, value); + + prev_val = isa_ext_is_enabled(cpu, multi_ext_cfg->offset); + + if (value == prev_val) { + return; + } + + if (value && vendor_cpu) { + g_autofree char *cpuname = riscv_cpu_get_name(cpu); + error_setg(errp, "'%s' CPU does not allow enabling extensions", + cpuname); + return; + } + + if (value) { + cpu_bump_multi_ext_priv_ver(&cpu->env, multi_ext_cfg->offset); + } + + isa_ext_update_enabled(cpu, multi_ext_cfg->offset, value); +} + +static void cpu_get_multi_ext_cfg(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + const RISCVCPUMultiExtConfig *multi_ext_cfg = opaque; + bool value = isa_ext_is_enabled(RISCV_CPU(obj), multi_ext_cfg->offset); + + visit_type_bool(v, name, &value, errp); +} + +static void cpu_add_multi_ext_prop(Object *cpu_obj, + const RISCVCPUMultiExtConfig *multi_cfg) +{ + bool generic_cpu = riscv_cpu_is_generic(cpu_obj); + bool deprecated_ext = cpu_ext_is_deprecated(multi_cfg->name); + + object_property_add(cpu_obj, multi_cfg->name, "bool", + cpu_get_multi_ext_cfg, + cpu_set_multi_ext_cfg, + NULL, (void *)multi_cfg); + + if (!generic_cpu || deprecated_ext) { + return; + } + + /* + * Set def val directly instead of using + * object_property_set_bool() to save the set() + * callback hash for user inputs. + */ + isa_ext_update_enabled(RISCV_CPU(cpu_obj), multi_cfg->offset, + multi_cfg->enabled); +} + +static void riscv_cpu_add_multiext_prop_array(Object *obj, + const RISCVCPUMultiExtConfig *array) +{ + const RISCVCPUMultiExtConfig *prop; + + g_assert(array); + + for (prop = array; prop && prop->name; prop++) { + cpu_add_multi_ext_prop(obj, prop); + } +} + +/* + * Add CPU properties with user-facing flags. + * + * This will overwrite existing env->misa_ext values with the + * defaults set via riscv_cpu_add_misa_properties(). + */ +static void riscv_cpu_add_user_properties(Object *obj) +{ +#ifndef CONFIG_USER_ONLY + riscv_add_satp_mode_properties(obj); +#endif + + riscv_cpu_add_misa_properties(obj); + + riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_extensions); + riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_vendor_exts); + riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_experimental_exts); + + riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_deprecated_exts); + + riscv_cpu_add_profiles(obj); +} + +/* + * The 'max' type CPU will have all possible ratified + * non-vendor extensions enabled. + */ +static void riscv_init_max_cpu_extensions(Object *obj) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + const RISCVCPUMultiExtConfig *prop; + + /* Enable RVG, RVJ and RVV that are disabled by default */ + riscv_cpu_set_misa_ext(env, env->misa_ext | RVG | RVJ | RVV); + + for (prop = riscv_cpu_extensions; prop && prop->name; prop++) { + isa_ext_update_enabled(cpu, prop->offset, true); + } + + /* + * Some extensions can't be added without backward compatibilty concerns. + * Disable those, the user can still opt in to them on the command line. + */ + cpu->cfg.ext_svade = false; + + /* set vector version */ + env->vext_ver = VEXT_VERSION_1_00_0; + + /* Zfinx is not compatible with F. Disable it */ + isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zfinx), false); + isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zdinx), false); + isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zhinx), false); + isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zhinxmin), false); + + isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zce), false); + isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcmp), false); + isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcmt), false); + + if (env->misa_mxl != MXL_RV32) { + isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcf), false); + } +} + +static bool riscv_cpu_has_max_extensions(Object *cpu_obj) +{ + return object_dynamic_cast(cpu_obj, TYPE_RISCV_CPU_MAX) != NULL; +} + +static void riscv_tcg_cpu_instance_init(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + Object *obj = OBJECT(cpu); + + misa_ext_user_opts = g_hash_table_new(NULL, g_direct_equal); + multi_ext_user_opts = g_hash_table_new(NULL, g_direct_equal); + riscv_cpu_add_user_properties(obj); + + if (riscv_cpu_has_max_extensions(obj)) { + riscv_init_max_cpu_extensions(obj); + } +} + +static void riscv_tcg_cpu_init_ops(AccelCPUClass *accel_cpu, CPUClass *cc) +{ + /* + * All cpus use the same set of operations. + */ + cc->tcg_ops = &riscv_tcg_ops; +} + +static void riscv_tcg_cpu_class_init(CPUClass *cc) +{ + cc->init_accel_cpu = riscv_tcg_cpu_init_ops; +} + +static void riscv_tcg_cpu_accel_class_init(ObjectClass *oc, void *data) +{ + AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); + + acc->cpu_class_init = riscv_tcg_cpu_class_init; + acc->cpu_instance_init = riscv_tcg_cpu_instance_init; + acc->cpu_target_realize = riscv_tcg_cpu_realize; +} + +static const TypeInfo riscv_tcg_cpu_accel_type_info = { + .name = ACCEL_CPU_NAME("tcg"), + + .parent = TYPE_ACCEL_CPU, + .class_init = riscv_tcg_cpu_accel_class_init, + .abstract = true, +}; + +static void riscv_tcg_cpu_accel_register_types(void) +{ + type_register_static(&riscv_tcg_cpu_accel_type_info); +} +type_init(riscv_tcg_cpu_accel_register_types); diff --git a/target/riscv/tcg/tcg-cpu.h b/target/riscv/tcg/tcg-cpu.h new file mode 100644 index 0000000000..f7b32417f8 --- /dev/null +++ b/target/riscv/tcg/tcg-cpu.h @@ -0,0 +1,29 @@ +/* + * riscv TCG cpu class initialization + * + * Copyright (c) 2023 Ventana Micro Systems Inc. + * + * 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 RISCV_TCG_CPU_H +#define RISCV_TCG_CPU_H + +#include "cpu.h" + +void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp); +void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp); +bool riscv_cpu_tcg_compatible(RISCVCPU *cpu); + +#endif diff --git a/target/riscv/time_helper.c b/target/riscv/time_helper.c new file mode 100644 index 0000000000..8d245bed3a --- /dev/null +++ b/target/riscv/time_helper.c @@ -0,0 +1,141 @@ +/* + * RISC-V timer helper implementation. + * + * Copyright (c) 2022 Rivos 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 "cpu_bits.h" +#include "time_helper.h" +#include "hw/intc/riscv_aclint.h" + +static void riscv_vstimer_cb(void *opaque) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + env->vstime_irq = 1; + riscv_cpu_update_mip(env, 0, BOOL_TO_MASK(1)); +} + +static void riscv_stimer_cb(void *opaque) +{ + RISCVCPU *cpu = opaque; + riscv_cpu_update_mip(&cpu->env, MIP_STIP, BOOL_TO_MASK(1)); +} + +/* + * Called when timecmp is written to update the QEMU timer or immediately + * trigger timer interrupt if mtimecmp <= current timer value. + */ +void riscv_timer_write_timecmp(CPURISCVState *env, QEMUTimer *timer, + uint64_t timecmp, uint64_t delta, + uint32_t timer_irq) +{ + uint64_t diff, ns_diff, next; + RISCVAclintMTimerState *mtimer = env->rdtime_fn_arg; + uint32_t timebase_freq = mtimer->timebase_freq; + uint64_t rtc_r = env->rdtime_fn(env->rdtime_fn_arg) + delta; + + if (timecmp <= rtc_r) { + /* + * If we're setting an stimecmp value in the "past", + * immediately raise the timer interrupt + */ + if (timer_irq == MIP_VSTIP) { + env->vstime_irq = 1; + riscv_cpu_update_mip(env, 0, BOOL_TO_MASK(1)); + } else { + riscv_cpu_update_mip(env, MIP_STIP, BOOL_TO_MASK(1)); + } + return; + } + + /* Clear the [VS|S]TIP bit in mip */ + if (timer_irq == MIP_VSTIP) { + env->vstime_irq = 0; + riscv_cpu_update_mip(env, 0, BOOL_TO_MASK(0)); + } else { + riscv_cpu_update_mip(env, timer_irq, BOOL_TO_MASK(0)); + } + + /* + * Sstc specification says the following about timer interrupt: + * "A supervisor timer interrupt becomes pending - as reflected in + * the STIP bit in the mip and sip registers - whenever time contains + * a value greater than or equal to stimecmp, treating the values + * as unsigned integers. Writes to stimecmp are guaranteed to be + * reflected in STIP eventually, but not necessarily immediately. + * The interrupt remains posted until stimecmp becomes greater + * than time - typically as a result of writing stimecmp." + * + * When timecmp = UINT64_MAX, the time CSR will eventually reach + * timecmp value but on next timer tick the time CSR will wrap-around + * and become zero which is less than UINT64_MAX. Now, the timer + * interrupt behaves like a level triggered interrupt so it will + * become 1 when time = timecmp = UINT64_MAX and next timer tick + * it will become 0 again because time = 0 < timecmp = UINT64_MAX. + * + * Based on above, we don't re-start the QEMU timer when timecmp + * equals UINT64_MAX. + */ + if (timecmp == UINT64_MAX) { + return; + } + + /* otherwise, set up the future timer interrupt */ + diff = timecmp - rtc_r; + /* back to ns (note args switched in muldiv64) */ + ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq); + + /* + * check if ns_diff overflowed and check if the addition would potentially + * overflow + */ + if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) || + ns_diff > INT64_MAX) { + next = INT64_MAX; + } else { + /* + * as it is very unlikely qemu_clock_get_ns will return a value + * greater than INT64_MAX, no additional check is needed for an + * unsigned integer overflow. + */ + next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff; + /* + * if ns_diff is INT64_MAX next may still be outside the range + * of a signed integer. + */ + next = MIN(next, INT64_MAX); + } + + timer_mod(timer, next); +} + +void riscv_timer_init(RISCVCPU *cpu) +{ + CPURISCVState *env; + + if (!cpu) { + return; + } + + env = &cpu->env; + env->stimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_stimer_cb, cpu); + env->stimecmp = 0; + + env->vstimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &riscv_vstimer_cb, cpu); + env->vstimecmp = 0; +} diff --git a/target/riscv/kvm-stub.c b/target/riscv/time_helper.h similarity index 62% rename from target/riscv/kvm-stub.c rename to target/riscv/time_helper.h index 4e8fc31a21..cacd79b80c 100644 --- a/target/riscv/kvm-stub.c +++ b/target/riscv/time_helper.h @@ -1,7 +1,7 @@ /* - * QEMU KVM RISC-V specific function stubs + * RISC-V timer header file. * - * Copyright (c) 2020 Huawei Technologies Co., Ltd + * Copyright (c) 2022 Rivos 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, @@ -15,16 +15,16 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -#include "qemu/osdep.h" + +#ifndef RISCV_TIME_HELPER_H +#define RISCV_TIME_HELPER_H + #include "cpu.h" -#include "kvm_riscv.h" +#include "qemu/timer.h" -void kvm_riscv_reset_vcpu(RISCVCPU *cpu) -{ - abort(); -} +void riscv_timer_write_timecmp(CPURISCVState *env, QEMUTimer *timer, + uint64_t timecmp, uint64_t delta, + uint32_t timer_irq); +void riscv_timer_init(RISCVCPU *cpu); -void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level) -{ - abort(); -} +#endif diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 55a4713af2..9ff09ebdb6 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -28,10 +28,15 @@ #include "exec/translator.h" #include "exec/log.h" +#include "semihosting/semihost.h" #include "instmap.h" #include "internals.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + /* global register indices */ static TCGv cpu_gpr[32], cpu_gprh[32], cpu_pc, cpu_vl, cpu_vstart; static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */ @@ -41,8 +46,6 @@ static TCGv load_val; static TCGv pm_mask; static TCGv pm_base; -#include "exec/gen-icount.h" - /* * If an operation is being performed on less than TARGET_LONG_BITS, * it may require the inputs to be sign- or zero-extended; which will @@ -56,28 +59,30 @@ typedef enum { typedef struct DisasContext { DisasContextBase base; - /* pc_succ_insn points to the instruction following base.pc_next */ - target_ulong pc_succ_insn; + target_ulong cur_insn_len; + target_ulong pc_save; target_ulong priv_ver; RISCVMXL misa_mxl_max; RISCVMXL xl; + RISCVMXL address_xl; uint32_t misa_ext; uint32_t opcode; - uint32_t mstatus_fs; - uint32_t mstatus_vs; - uint32_t mstatus_hs_fs; - uint32_t mstatus_hs_vs; + RISCVExtStatus mstatus_fs; + RISCVExtStatus mstatus_vs; uint32_t mem_idx; - /* Remember the rounding mode encoded in the previous fp instruction, - which we have already installed into env->fp_status. Or -1 for - no previous fp instruction. Note that we exit the TB when writing - to any system register, which includes CSR_FRM, so we do not have - to reset this known value. */ + uint32_t priv; + /* + * Remember the rounding mode encoded in the previous fp instruction, + * which we have already installed into env->fp_status. Or -1 for + * no previous fp instruction. Note that we exit the TB when writing + * to any system register, which includes CSR_FRM, so we do not have + * to reset this known value. + */ int frm; RISCVMXL ol; + bool virt_inst_excp; bool virt_enabled; const RISCVCPUConfig *cfg_ptr; - bool hlsx; /* vector extension */ bool vill; /* @@ -94,21 +99,23 @@ typedef struct DisasContext { */ int8_t lmul; uint8_t sew; - target_ulong vstart; + uint8_t vta; + uint8_t vma; + bool cfg_vta_all_1s; + bool vstart_eq_zero; bool vl_eq_vlmax; - uint8_t ntemp; CPUState *cs; TCGv zero; - /* Space for 3 operands plus 1 extra for address computation. */ - TCGv temp[4]; - /* Space for 4 operands(1 dest and <=3 src) for float point computation */ - TCGv_i64 ftemp[4]; - uint8_t nftemp; /* PointerMasking extension */ bool pm_mask_enabled; bool pm_base_enabled; - /* TCG of the current insn_start */ - TCGOp *insn_start; + /* Ztso */ + bool ztso; + /* Use icount trigger for native debug */ + bool itrigger; + /* FRM is known to contain a valid value. */ + bool frm_valid; + bool insn_start_updated; } DisasContext; static inline bool has_ext(DisasContext *ctx, uint32_t ext) @@ -116,19 +123,6 @@ static inline bool has_ext(DisasContext *ctx, uint32_t ext) return ctx->misa_ext & ext; } -static bool always_true_p(DisasContext *ctx __attribute__((__unused__))) -{ - return true; -} - -#define MATERIALISE_EXT_PREDICATE(ext) \ - static bool has_ ## ext ## _p(DisasContext *ctx) \ - { \ - return ctx->cfg_ptr->ext_ ## ext ; \ - } - -MATERIALISE_EXT_PREDICATE(XVentanaCondOps); - #ifdef TARGET_RISCV32 #define get_xl(ctx) MXL_RV32 #elif defined(CONFIG_USER_ONLY) @@ -137,6 +131,14 @@ MATERIALISE_EXT_PREDICATE(XVentanaCondOps); #define get_xl(ctx) ((ctx)->xl) #endif +#ifdef TARGET_RISCV32 +#define get_address_xl(ctx) MXL_RV32 +#elif defined(CONFIG_USER_ONLY) +#define get_address_xl(ctx) MXL_RV64 +#else +#define get_address_xl(ctx) ((ctx)->address_xl) +#endif + /* The word size for this machine mode. */ static inline int __attribute__((unused)) get_xlen(DisasContext *ctx) { @@ -188,12 +190,10 @@ static void gen_nanbox_h(TCGv_i64 out, TCGv_i64 in) */ static void gen_check_nanbox_h(TCGv_i64 out, TCGv_i64 in) { - TCGv_i64 t_max = tcg_const_i64(0xffffffffffff0000ull); - TCGv_i64 t_nan = tcg_const_i64(0xffffffffffff7e00ull); + TCGv_i64 t_max = tcg_constant_i64(0xffffffffffff0000ull); + TCGv_i64 t_nan = tcg_constant_i64(0xffffffffffff7e00ull); tcg_gen_movcond_i64(TCG_COND_GEU, out, in, t_max, in, t_nan); - tcg_temp_free_i64(t_max); - tcg_temp_free_i64(t_nan); } static void gen_check_nanbox_s(TCGv_i64 out, TCGv_i64 in) @@ -204,57 +204,110 @@ static void gen_check_nanbox_s(TCGv_i64 out, TCGv_i64 in) tcg_gen_movcond_i64(TCG_COND_GEU, out, in, t_max, in, t_nan); } -static void gen_set_pc_imm(DisasContext *ctx, target_ulong dest) +static void decode_save_opc(DisasContext *ctx) { - if (get_xl(ctx) == MXL_RV32) { - dest = (int32_t)dest; - } - tcg_gen_movi_tl(cpu_pc, dest); + assert(!ctx->insn_start_updated); + ctx->insn_start_updated = true; + tcg_set_insn_start_param(ctx->base.insn_start, 1, ctx->opcode); } -static void gen_set_pc(DisasContext *ctx, TCGv dest) +static void gen_pc_plus_diff(TCGv target, DisasContext *ctx, + target_long diff) { - if (get_xl(ctx) == MXL_RV32) { - tcg_gen_ext32s_tl(cpu_pc, dest); + target_ulong dest = ctx->base.pc_next + diff; + + assert(ctx->pc_save != -1); + if (tb_cflags(ctx->base.tb) & CF_PCREL) { + tcg_gen_addi_tl(target, cpu_pc, dest - ctx->pc_save); + if (get_xl(ctx) == MXL_RV32) { + tcg_gen_ext32s_tl(target, target); + } } else { - tcg_gen_mov_tl(cpu_pc, dest); + if (get_xl(ctx) == MXL_RV32) { + dest = (int32_t)dest; + } + tcg_gen_movi_tl(target, dest); } } +static void gen_update_pc(DisasContext *ctx, target_long diff) +{ + gen_pc_plus_diff(cpu_pc, ctx, diff); + ctx->pc_save = ctx->base.pc_next + diff; +} + static void generate_exception(DisasContext *ctx, int excp) { - gen_set_pc_imm(ctx, ctx->base.pc_next); - gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); - ctx->base.is_jmp = DISAS_NORETURN; -} - -static void generate_exception_mtval(DisasContext *ctx, int excp) -{ - gen_set_pc_imm(ctx, ctx->base.pc_next); - tcg_gen_st_tl(cpu_pc, cpu_env, offsetof(CPURISCVState, badaddr)); - gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); + gen_update_pc(ctx, 0); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp)); ctx->base.is_jmp = DISAS_NORETURN; } static void gen_exception_illegal(DisasContext *ctx) { - generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST); + tcg_gen_st_i32(tcg_constant_i32(ctx->opcode), tcg_env, + offsetof(CPURISCVState, bins)); + if (ctx->virt_inst_excp) { + generate_exception(ctx, RISCV_EXCP_VIRT_INSTRUCTION_FAULT); + } else { + generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST); + } } -static void gen_exception_inst_addr_mis(DisasContext *ctx) +static void gen_exception_inst_addr_mis(DisasContext *ctx, TCGv target) { - generate_exception_mtval(ctx, RISCV_EXCP_INST_ADDR_MIS); + tcg_gen_st_tl(target, tcg_env, offsetof(CPURISCVState, badaddr)); + generate_exception(ctx, RISCV_EXCP_INST_ADDR_MIS); } -static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +static void lookup_and_goto_ptr(DisasContext *ctx) { - if (translator_use_goto_tb(&ctx->base, dest)) { - tcg_gen_goto_tb(n); - gen_set_pc_imm(ctx, dest); +#ifndef CONFIG_USER_ONLY + if (ctx->itrigger) { + gen_helper_itrigger_match(tcg_env); + } +#endif + tcg_gen_lookup_and_goto_ptr(); +} + +static void exit_tb(DisasContext *ctx) +{ +#ifndef CONFIG_USER_ONLY + if (ctx->itrigger) { + gen_helper_itrigger_match(tcg_env); + } +#endif + tcg_gen_exit_tb(NULL, 0); +} + +static void gen_goto_tb(DisasContext *ctx, int n, target_long diff) +{ + target_ulong dest = ctx->base.pc_next + diff; + + /* + * Under itrigger, instruction executes one by one like singlestep, + * direct block chain benefits will be small. + */ + if (translator_use_goto_tb(&ctx->base, dest) && !ctx->itrigger) { + /* + * For pcrel, the pc must always be up-to-date on entry to + * the linked TB, so that it can use simple additions for all + * further adjustments. For !pcrel, the linked TB is compiled + * to know its full virtual address, so we can delay the + * update to pc to the unlinked path. A long chain of links + * can thus avoid many updates to the PC. + */ + if (tb_cflags(ctx->base.tb) & CF_PCREL) { + gen_update_pc(ctx, diff); + tcg_gen_goto_tb(n); + } else { + tcg_gen_goto_tb(n); + gen_update_pc(ctx, diff); + } tcg_gen_exit_tb(ctx->base.tb, n); } else { - gen_set_pc_imm(ctx, dest); - tcg_gen_lookup_and_goto_ptr(); + gen_update_pc(ctx, diff); + lookup_and_goto_ptr(ctx); } } @@ -266,12 +319,6 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) * * Further, we may provide an extension for word operations. */ -static TCGv temp_new(DisasContext *ctx) -{ - assert(ctx->ntemp < ARRAY_SIZE(ctx->temp)); - return ctx->temp[ctx->ntemp++] = tcg_temp_new(); -} - static TCGv get_gpr(DisasContext *ctx, int reg_num, DisasExtend ext) { TCGv t; @@ -286,11 +333,11 @@ static TCGv get_gpr(DisasContext *ctx, int reg_num, DisasExtend ext) case EXT_NONE: break; case EXT_SIGN: - t = temp_new(ctx); + t = tcg_temp_new(); tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); return t; case EXT_ZERO: - t = temp_new(ctx); + t = tcg_temp_new(); tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); return t; default: @@ -318,7 +365,7 @@ static TCGv get_gprh(DisasContext *ctx, int reg_num) static TCGv dest_gpr(DisasContext *ctx, int reg_num) { if (reg_num == 0 || get_olen(ctx) < TARGET_LONG_BITS) { - return temp_new(ctx); + return tcg_temp_new(); } return cpu_gpr[reg_num]; } @@ -326,7 +373,7 @@ static TCGv dest_gpr(DisasContext *ctx, int reg_num) static TCGv dest_gprh(DisasContext *ctx, int reg_num) { if (reg_num == 0) { - return temp_new(ctx); + return tcg_temp_new(); } return cpu_gprh[reg_num]; } @@ -382,12 +429,6 @@ static void gen_set_gpr128(DisasContext *ctx, int reg_num, TCGv rl, TCGv rh) } } -static TCGv_i64 ftemp_new(DisasContext *ctx) -{ - assert(ctx->nftemp < ARRAY_SIZE(ctx->ftemp)); - return ctx->ftemp[ctx->nftemp++] = tcg_temp_new_i64(); -} - static TCGv_i64 get_fpr_hs(DisasContext *ctx, int reg_num) { if (!ctx->cfg_ptr->ext_zfinx) { @@ -401,7 +442,7 @@ static TCGv_i64 get_fpr_hs(DisasContext *ctx, int reg_num) case MXL_RV32: #ifdef TARGET_RISCV32 { - TCGv_i64 t = ftemp_new(ctx); + TCGv_i64 t = tcg_temp_new_i64(); tcg_gen_ext_i32_i64(t, cpu_gpr[reg_num]); return t; } @@ -427,7 +468,7 @@ static TCGv_i64 get_fpr_d(DisasContext *ctx, int reg_num) switch (get_xl(ctx)) { case MXL_RV32: { - TCGv_i64 t = ftemp_new(ctx); + TCGv_i64 t = tcg_temp_new_i64(); tcg_gen_concat_tl_i64(t, cpu_gpr[reg_num], cpu_gpr[reg_num + 1]); return t; } @@ -447,12 +488,12 @@ static TCGv_i64 dest_fpr(DisasContext *ctx, int reg_num) } if (reg_num == 0) { - return ftemp_new(ctx); + return tcg_temp_new_i64(); } switch (get_xl(ctx)) { case MXL_RV32: - return ftemp_new(ctx); + return tcg_temp_new_i64(); #ifdef TARGET_RISCV64 case MXL_RV64: return cpu_gpr[reg_num]; @@ -462,7 +503,7 @@ static TCGv_i64 dest_fpr(DisasContext *ctx, int reg_num) } } -/* assume t is nanboxing (for normal) or sign-extended (for zfinx) */ +/* assume it is nanboxing (for normal) or sign-extended (for zfinx) */ static void gen_set_fpr_hs(DisasContext *ctx, int reg_num, TCGv_i64 t) { if (!ctx->cfg_ptr->ext_zfinx) { @@ -516,31 +557,53 @@ static void gen_set_fpr_d(DisasContext *ctx, int reg_num, TCGv_i64 t) static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) { - target_ulong next_pc; + TCGv succ_pc = dest_gpr(ctx, rd); /* check misaligned: */ - next_pc = ctx->base.pc_next + imm; - if (!has_ext(ctx, RVC)) { - if ((next_pc & 0x3) != 0) { - gen_exception_inst_addr_mis(ctx); + if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { + if ((imm & 0x3) != 0) { + TCGv target_pc = tcg_temp_new(); + gen_pc_plus_diff(target_pc, ctx, imm); + gen_exception_inst_addr_mis(ctx, target_pc); return; } } - gen_set_gpri(ctx, rd, ctx->pc_succ_insn); - gen_goto_tb(ctx, 0, ctx->base.pc_next + imm); /* must use this for safety */ + gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); + gen_set_gpr(ctx, rd, succ_pc); + + gen_goto_tb(ctx, 0, imm); /* must use this for safety */ ctx->base.is_jmp = DISAS_NORETURN; } /* Compute a canonical address from a register plus offset. */ static TCGv get_address(DisasContext *ctx, int rs1, int imm) { - TCGv addr = temp_new(ctx); + TCGv addr = tcg_temp_new(); TCGv src1 = get_gpr(ctx, rs1, EXT_NONE); tcg_gen_addi_tl(addr, src1, imm); if (ctx->pm_mask_enabled) { - tcg_gen_and_tl(addr, addr, pm_mask); + tcg_gen_andc_tl(addr, addr, pm_mask); + } else if (get_address_xl(ctx) == MXL_RV32) { + tcg_gen_ext32u_tl(addr, addr); + } + if (ctx->pm_base_enabled) { + tcg_gen_or_tl(addr, addr, pm_base); + } + + return addr; +} + +/* Compute a canonical address from a register plus reg offset. */ +static TCGv get_address_indexed(DisasContext *ctx, int rs1, TCGv offs) +{ + TCGv addr = tcg_temp_new(); + TCGv src1 = get_gpr(ctx, rs1, EXT_NONE); + + tcg_gen_add_tl(addr, src1, offs); + if (ctx->pm_mask_enabled) { + tcg_gen_andc_tl(addr, addr, pm_mask); } else if (get_xl(ctx) == MXL_RV32) { tcg_gen_ext32u_tl(addr, addr); } @@ -551,8 +614,7 @@ static TCGv get_address(DisasContext *ctx, int rs1, int imm) } #ifndef CONFIG_USER_ONLY -/* The states of mstatus_fs are: - * 0 = disabled, 1 = initial, 2 = clean, 3 = dirty +/* * We will have already diagnosed disabled state, * and need to turn initial/clean into dirty. */ @@ -564,26 +626,20 @@ static void mark_fs_dirty(DisasContext *ctx) return; } - if (ctx->mstatus_fs != MSTATUS_FS) { + if (ctx->mstatus_fs != EXT_STATUS_DIRTY) { /* Remember the state change for the rest of the TB. */ - ctx->mstatus_fs = MSTATUS_FS; + ctx->mstatus_fs = EXT_STATUS_DIRTY; tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); + tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); - tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); - tcg_temp_free(tmp); - } + tcg_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); - if (ctx->virt_enabled && ctx->mstatus_hs_fs != MSTATUS_FS) { - /* Remember the stage change for the rest of the TB. */ - ctx->mstatus_hs_fs = MSTATUS_FS; - - tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); - tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); - tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); - tcg_temp_free(tmp); + if (ctx->virt_enabled) { + tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); + tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); + tcg_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); + } } } #else @@ -591,8 +647,7 @@ static inline void mark_fs_dirty(DisasContext *ctx) { } #endif #ifndef CONFIG_USER_ONLY -/* The states of mstatus_vs are: - * 0 = disabled, 1 = initial, 2 = clean, 3 = dirty +/* * We will have already diagnosed disabled state, * and need to turn initial/clean into dirty. */ @@ -600,32 +655,32 @@ static void mark_vs_dirty(DisasContext *ctx) { TCGv tmp; - if (ctx->mstatus_vs != MSTATUS_VS) { + if (ctx->mstatus_vs != EXT_STATUS_DIRTY) { /* Remember the state change for the rest of the TB. */ - ctx->mstatus_vs = MSTATUS_VS; + ctx->mstatus_vs = EXT_STATUS_DIRTY; tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); + tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); tcg_gen_ori_tl(tmp, tmp, MSTATUS_VS); - tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); - tcg_temp_free(tmp); - } + tcg_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); - if (ctx->virt_enabled && ctx->mstatus_hs_vs != MSTATUS_VS) { - /* Remember the stage change for the rest of the TB. */ - ctx->mstatus_hs_vs = MSTATUS_VS; - - tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); - tcg_gen_ori_tl(tmp, tmp, MSTATUS_VS); - tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); - tcg_temp_free(tmp); + if (ctx->virt_enabled) { + tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); + tcg_gen_ori_tl(tmp, tmp, MSTATUS_VS); + tcg_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); + } } } #else static inline void mark_vs_dirty(DisasContext *ctx) { } #endif +static void finalize_rvv_inst(DisasContext *ctx) +{ + mark_vs_dirty(ctx); + ctx->vstart_eq_zero = true; +} + static void gen_set_rm(DisasContext *ctx, int rm) { if (ctx->frm == rm) { @@ -633,12 +688,27 @@ static void gen_set_rm(DisasContext *ctx, int rm) } ctx->frm = rm; - if (rm == RISCV_FRM_ROD) { - gen_helper_set_rod_rounding_mode(cpu_env); - return; + if (rm == RISCV_FRM_DYN) { + /* The helper will return only if frm valid. */ + ctx->frm_valid = true; } - gen_helper_set_rounding_mode(cpu_env, tcg_constant_i32(rm)); + /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ + decode_save_opc(ctx); + gen_helper_set_rounding_mode(tcg_env, tcg_constant_i32(rm)); +} + +static void gen_set_rm_chkfrm(DisasContext *ctx, int rm) +{ + if (ctx->frm == rm && ctx->frm_valid) { + return; + } + ctx->frm = rm; + ctx->frm_valid = true; + + /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ + decode_save_opc(ctx); + gen_helper_set_rounding_mode_chkfrm(tcg_env, tcg_constant_i32(rm)); } static int ex_plus_1(DisasContext *ctx, int nf) @@ -688,8 +758,8 @@ EX_SH(12) } while (0) #define REQUIRE_EITHER_EXT(ctx, A, B) do { \ - if (!ctx->cfg_ptr->ext_##A && \ - !ctx->cfg_ptr->ext_##B) { \ + if (!ctx->cfg_ptr->ext_##A && \ + !ctx->cfg_ptr->ext_##B) { \ return false; \ } \ } while (0) @@ -699,10 +769,31 @@ static int ex_rvc_register(DisasContext *ctx, int reg) return 8 + reg; } -static int ex_rvc_shifti(DisasContext *ctx, int imm) +static int ex_sreg_register(DisasContext *ctx, int reg) +{ + return reg < 2 ? reg + 8 : reg + 16; +} + +static int ex_rvc_shiftli(DisasContext *ctx, int imm) { /* For RV128 a shamt of 0 means a shift by 64. */ - return imm ? imm : 64; + if (get_ol(ctx) == MXL_RV128) { + imm = imm ? imm : 64; + } + return imm; +} + +static int ex_rvc_shiftri(DisasContext *ctx, int imm) +{ + /* + * For RV128 a shamt of 0 means a shift by 64, furthermore, for right + * shifts, the shamt is sign-extended. + */ + if (get_ol(ctx) == MXL_RV128) { + imm = imm | (imm & 32) << 1; + imm = imm ? imm : 64; + } + return imm; } /* Include the auto-generated decoder for 32 bit insn */ @@ -939,7 +1030,6 @@ static bool gen_shift(DisasContext *ctx, arg_r *a, DisasExtend ext, f128(dest, desth, src1, src1h, ext2); gen_set_gpr128(ctx, a->rd, dest, desth); } - tcg_temp_free(ext2); return true; } @@ -991,7 +1081,7 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPUState *cpu = ctx->cs; - CPURISCVState *env = cpu->env_ptr; + CPURISCVState *env = cpu_env(cpu); return cpu_ldl_code(env, pc); } @@ -1005,22 +1095,34 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) #include "insn_trans/trans_rvh.c.inc" #include "insn_trans/trans_rvv.c.inc" #include "insn_trans/trans_rvb.c.inc" +#include "insn_trans/trans_rvzicond.c.inc" +#include "insn_trans/trans_rvzacas.c.inc" +#include "insn_trans/trans_rvzawrs.c.inc" +#include "insn_trans/trans_rvzicbo.c.inc" +#include "insn_trans/trans_rvzfa.c.inc" #include "insn_trans/trans_rvzfh.c.inc" #include "insn_trans/trans_rvk.c.inc" +#include "insn_trans/trans_rvvk.c.inc" #include "insn_trans/trans_privileged.c.inc" #include "insn_trans/trans_svinval.c.inc" +#include "insn_trans/trans_rvbf16.c.inc" +#include "decode-xthead.c.inc" +#include "insn_trans/trans_xthead.c.inc" #include "insn_trans/trans_xventanacondops.c.inc" /* Include the auto-generated decoder for 16 bit insn */ #include "decode-insn16.c.inc" +#include "insn_trans/trans_rvzce.c.inc" + /* Include decoders for factored-out extensions */ #include "decode-XVentanaCondOps.c.inc" -static inline void decode_save_opc(DisasContext *ctx, target_ulong opc) +/* The specification allows for longer insns, but not supported by qemu. */ +#define MAX_INSN_LEN 4 + +static inline int insn_len(uint16_t first_word) { - assert(ctx->insn_start != NULL); - tcg_set_insn_start_param(ctx->insn_start, 1, opc); - ctx->insn_start = NULL; + return (first_word & 3) == 3 ? 4 : 2; } static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) @@ -1030,36 +1132,36 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) * that are tested in-order until a decoder matches onto the opcode. */ static const struct { - bool (*guard_func)(DisasContext *); + bool (*guard_func)(const RISCVCPUConfig *); bool (*decode_func)(DisasContext *, uint32_t); } decoders[] = { { always_true_p, decode_insn32 }, + { has_xthead_p, decode_xthead }, { has_XVentanaCondOps_p, decode_XVentanaCodeOps }, }; + ctx->virt_inst_excp = false; + ctx->cur_insn_len = insn_len(opcode); /* Check for compressed insn */ - if (extract16(opcode, 0, 2) != 3) { - decode_save_opc(ctx, opcode); - if (!has_ext(ctx, RVC)) { - gen_exception_illegal(ctx); - } else { - ctx->opcode = opcode; - ctx->pc_succ_insn = ctx->base.pc_next + 2; - if (decode_insn16(ctx, opcode)) { - return; - } + if (ctx->cur_insn_len == 2) { + ctx->opcode = opcode; + /* + * The Zca extension is added as way to refer to instructions in the C + * extension that do not include the floating-point loads and stores + */ + if ((has_ext(ctx, RVC) || ctx->cfg_ptr->ext_zca) && + decode_insn16(ctx, opcode)) { + return; } } else { uint32_t opcode32 = opcode; opcode32 = deposit32(opcode32, 16, 16, translator_lduw(env, &ctx->base, ctx->base.pc_next + 2)); - decode_save_opc(ctx, opcode32); ctx->opcode = opcode32; - ctx->pc_succ_insn = ctx->base.pc_next + 4; for (size_t i = 0; i < ARRAY_SIZE(decoders); ++i) { - if (decoders[i].guard_func(ctx) && + if (decoders[i].guard_func(ctx->cfg_ptr) && decoders[i].decode_func(ctx, opcode32)) { return; } @@ -1072,45 +1174,39 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - CPURISCVState *env = cs->env_ptr; + CPURISCVState *env = cpu_env(cs); + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cs); RISCVCPU *cpu = RISCV_CPU(cs); uint32_t tb_flags = ctx->base.tb->flags; - ctx->pc_succ_insn = ctx->base.pc_first; + ctx->pc_save = ctx->base.pc_first; + ctx->priv = FIELD_EX32(tb_flags, TB_FLAGS, PRIV); ctx->mem_idx = FIELD_EX32(tb_flags, TB_FLAGS, MEM_IDX); - ctx->mstatus_fs = tb_flags & TB_FLAGS_MSTATUS_FS; - ctx->mstatus_vs = tb_flags & TB_FLAGS_MSTATUS_VS; + ctx->mstatus_fs = FIELD_EX32(tb_flags, TB_FLAGS, FS); + ctx->mstatus_vs = FIELD_EX32(tb_flags, TB_FLAGS, VS); 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); - } else { - ctx->virt_enabled = false; - } -#else - ctx->virt_enabled = false; -#endif + ctx->virt_enabled = FIELD_EX32(tb_flags, TB_FLAGS, VIRT_ENABLED); ctx->misa_ext = env->misa_ext; ctx->frm = -1; /* unknown rounding mode */ ctx->cfg_ptr = &(cpu->cfg); - ctx->mstatus_hs_fs = FIELD_EX32(tb_flags, TB_FLAGS, MSTATUS_HS_FS); - ctx->mstatus_hs_vs = FIELD_EX32(tb_flags, TB_FLAGS, MSTATUS_HS_VS); - ctx->hlsx = FIELD_EX32(tb_flags, TB_FLAGS, HLSX); ctx->vill = FIELD_EX32(tb_flags, TB_FLAGS, VILL); ctx->sew = FIELD_EX32(tb_flags, TB_FLAGS, SEW); ctx->lmul = sextract32(FIELD_EX32(tb_flags, TB_FLAGS, LMUL), 0, 3); - ctx->vstart = env->vstart; + ctx->vta = FIELD_EX32(tb_flags, TB_FLAGS, VTA) && cpu->cfg.rvv_ta_all_1s; + ctx->vma = FIELD_EX32(tb_flags, TB_FLAGS, VMA) && cpu->cfg.rvv_ma_all_1s; + ctx->cfg_vta_all_1s = cpu->cfg.rvv_ta_all_1s; + ctx->vstart_eq_zero = FIELD_EX32(tb_flags, TB_FLAGS, VSTART_EQ_ZERO); ctx->vl_eq_vlmax = FIELD_EX32(tb_flags, TB_FLAGS, VL_EQ_VLMAX); - ctx->misa_mxl_max = env->misa_mxl_max; + ctx->misa_mxl_max = mcc->misa_mxl_max; ctx->xl = FIELD_EX32(tb_flags, TB_FLAGS, XL); + ctx->address_xl = FIELD_EX32(tb_flags, TB_FLAGS, AXL); ctx->cs = cs; - ctx->ntemp = 0; - memset(ctx->temp, 0, sizeof(ctx->temp)); - ctx->nftemp = 0; - memset(ctx->ftemp, 0, sizeof(ctx->ftemp)); ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED); ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED); + ctx->ztso = cpu->cfg.ext_ztso; + ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER); ctx->zero = tcg_constant_tl(0); + ctx->virt_inst_excp = false; } static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -1120,39 +1216,41 @@ static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + target_ulong pc_next = ctx->base.pc_next; - tcg_gen_insn_start(ctx->base.pc_next, 0); - ctx->insn_start = tcg_last_op(); + if (tb_cflags(dcbase->tb) & CF_PCREL) { + pc_next &= ~TARGET_PAGE_MASK; + } + + tcg_gen_insn_start(pc_next, 0); + ctx->insn_start_updated = false; } static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - CPURISCVState *env = cpu->env_ptr; + CPURISCVState *env = cpu_env(cpu); uint16_t opcode16 = translator_lduw(env, &ctx->base, ctx->base.pc_next); - int i; ctx->ol = ctx->xl; decode_opc(env, ctx, opcode16); - ctx->base.pc_next = ctx->pc_succ_insn; - - for (i = ctx->ntemp - 1; i >= 0; --i) { - tcg_temp_free(ctx->temp[i]); - ctx->temp[i] = NULL; - } - ctx->ntemp = 0; - for (i = ctx->nftemp - 1; i >= 0; --i) { - tcg_temp_free_i64(ctx->ftemp[i]); - ctx->ftemp[i] = NULL; - } - ctx->nftemp = 0; + ctx->base.pc_next += ctx->cur_insn_len; + /* Only the first insn within a TB is allowed to cross a page boundary. */ if (ctx->base.is_jmp == DISAS_NEXT) { - target_ulong page_start; - - page_start = ctx->base.pc_first & TARGET_PAGE_MASK; - if (ctx->base.pc_next - page_start >= TARGET_PAGE_SIZE) { + if (ctx->itrigger || !is_same_page(&ctx->base, ctx->base.pc_next)) { ctx->base.is_jmp = DISAS_TOO_MANY; + } else { + unsigned page_ofs = ctx->base.pc_next & ~TARGET_PAGE_MASK; + + if (page_ofs > TARGET_PAGE_SIZE - MAX_INSN_LEN) { + uint16_t next_insn = cpu_lduw_code(env, ctx->base.pc_next); + int len = insn_len(next_insn); + + if (!is_same_page(&ctx->base, ctx->base.pc_next + len - 1)) { + ctx->base.is_jmp = DISAS_TOO_MANY; + } + } } } } @@ -1163,7 +1261,7 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) switch (ctx->base.is_jmp) { case DISAS_TOO_MANY: - gen_goto_tb(ctx, 0, ctx->base.pc_next); + gen_goto_tb(ctx, 0, 0); break; case DISAS_NORETURN: break; @@ -1182,8 +1280,8 @@ static void riscv_tr_disas_log(const DisasContextBase *dcbase, fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); #ifndef CONFIG_USER_ONLY - fprintf(logfile, "Priv: "TARGET_FMT_ld"; Virt: "TARGET_FMT_ld"\n", - env->priv, env->virt); + fprintf(logfile, "Priv: "TARGET_FMT_ld"; Virt: %d\n", + env->priv, env->virt_enabled); #endif target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); } @@ -1197,11 +1295,12 @@ static const TranslatorOps riscv_tr_ops = { .disas_log = riscv_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; - translator_loop(&riscv_tr_ops, &ctx.base, cs, tb, max_insns); + translator_loop(cs, tb, max_insns, pc, host_pc, &riscv_tr_ops, &ctx.base); } void riscv_translate_init(void) @@ -1217,28 +1316,28 @@ void riscv_translate_init(void) cpu_gprh[0] = NULL; for (i = 1; i < 32; i++) { - cpu_gpr[i] = tcg_global_mem_new(cpu_env, + cpu_gpr[i] = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, gpr[i]), riscv_int_regnames[i]); - cpu_gprh[i] = tcg_global_mem_new(cpu_env, + cpu_gprh[i] = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, gprh[i]), riscv_int_regnamesh[i]); } for (i = 0; i < 32; i++) { - cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, + cpu_fpr[i] = tcg_global_mem_new_i64(tcg_env, offsetof(CPURISCVState, fpr[i]), riscv_fpr_regnames[i]); } - cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, pc), "pc"); - cpu_vl = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, vl), "vl"); - cpu_vstart = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, vstart), + cpu_pc = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, pc), "pc"); + cpu_vl = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, vl), "vl"); + cpu_vstart = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, vstart), "vstart"); - load_res = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_res), + load_res = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, load_res), "load_res"); - load_val = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_val), + load_val = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, load_val), "load_val"); /* Assign PM CSRs to tcg globals */ - pm_mask = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, cur_pmmask), + pm_mask = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, cur_pmmask), "pmmask"); - pm_base = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, cur_pmbase), + pm_base = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, cur_pmbase), "pmbase"); } diff --git a/target/riscv/vcrypto_helper.c b/target/riscv/vcrypto_helper.c new file mode 100644 index 0000000000..f7423df226 --- /dev/null +++ b/target/riscv/vcrypto_helper.c @@ -0,0 +1,1002 @@ +/* + * RISC-V Vector Crypto Extension Helpers for QEMU. + * + * Copyright (C) 2023 SiFive, Inc. + * Written by Codethink Ltd and SiFive. + * + * 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/host-utils.h" +#include "qemu/bitops.h" +#include "qemu/bswap.h" +#include "cpu.h" +#include "crypto/aes.h" +#include "crypto/aes-round.h" +#include "crypto/sm4.h" +#include "exec/memop.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "internals.h" +#include "vector_internals.h" + +static uint64_t clmul64(uint64_t y, uint64_t x) +{ + uint64_t result = 0; + for (int j = 63; j >= 0; j--) { + if ((y >> j) & 1) { + result ^= (x << j); + } + } + return result; +} + +static uint64_t clmulh64(uint64_t y, uint64_t x) +{ + uint64_t result = 0; + for (int j = 63; j >= 1; j--) { + if ((y >> j) & 1) { + result ^= (x >> (64 - j)); + } + } + return result; +} + +RVVCALL(OPIVV2, vclmul_vv, OP_UUU_D, H8, H8, H8, clmul64) +GEN_VEXT_VV(vclmul_vv, 8) +RVVCALL(OPIVX2, vclmul_vx, OP_UUU_D, H8, H8, clmul64) +GEN_VEXT_VX(vclmul_vx, 8) +RVVCALL(OPIVV2, vclmulh_vv, OP_UUU_D, H8, H8, H8, clmulh64) +GEN_VEXT_VV(vclmulh_vv, 8) +RVVCALL(OPIVX2, vclmulh_vx, OP_UUU_D, H8, H8, clmulh64) +GEN_VEXT_VX(vclmulh_vx, 8) + +RVVCALL(OPIVV2, vror_vv_b, OP_UUU_B, H1, H1, H1, ror8) +RVVCALL(OPIVV2, vror_vv_h, OP_UUU_H, H2, H2, H2, ror16) +RVVCALL(OPIVV2, vror_vv_w, OP_UUU_W, H4, H4, H4, ror32) +RVVCALL(OPIVV2, vror_vv_d, OP_UUU_D, H8, H8, H8, ror64) +GEN_VEXT_VV(vror_vv_b, 1) +GEN_VEXT_VV(vror_vv_h, 2) +GEN_VEXT_VV(vror_vv_w, 4) +GEN_VEXT_VV(vror_vv_d, 8) + +RVVCALL(OPIVX2, vror_vx_b, OP_UUU_B, H1, H1, ror8) +RVVCALL(OPIVX2, vror_vx_h, OP_UUU_H, H2, H2, ror16) +RVVCALL(OPIVX2, vror_vx_w, OP_UUU_W, H4, H4, ror32) +RVVCALL(OPIVX2, vror_vx_d, OP_UUU_D, H8, H8, ror64) +GEN_VEXT_VX(vror_vx_b, 1) +GEN_VEXT_VX(vror_vx_h, 2) +GEN_VEXT_VX(vror_vx_w, 4) +GEN_VEXT_VX(vror_vx_d, 8) + +RVVCALL(OPIVV2, vrol_vv_b, OP_UUU_B, H1, H1, H1, rol8) +RVVCALL(OPIVV2, vrol_vv_h, OP_UUU_H, H2, H2, H2, rol16) +RVVCALL(OPIVV2, vrol_vv_w, OP_UUU_W, H4, H4, H4, rol32) +RVVCALL(OPIVV2, vrol_vv_d, OP_UUU_D, H8, H8, H8, rol64) +GEN_VEXT_VV(vrol_vv_b, 1) +GEN_VEXT_VV(vrol_vv_h, 2) +GEN_VEXT_VV(vrol_vv_w, 4) +GEN_VEXT_VV(vrol_vv_d, 8) + +RVVCALL(OPIVX2, vrol_vx_b, OP_UUU_B, H1, H1, rol8) +RVVCALL(OPIVX2, vrol_vx_h, OP_UUU_H, H2, H2, rol16) +RVVCALL(OPIVX2, vrol_vx_w, OP_UUU_W, H4, H4, rol32) +RVVCALL(OPIVX2, vrol_vx_d, OP_UUU_D, H8, H8, rol64) +GEN_VEXT_VX(vrol_vx_b, 1) +GEN_VEXT_VX(vrol_vx_h, 2) +GEN_VEXT_VX(vrol_vx_w, 4) +GEN_VEXT_VX(vrol_vx_d, 8) + +static uint64_t brev8(uint64_t val) +{ + val = ((val & 0x5555555555555555ull) << 1) | + ((val & 0xAAAAAAAAAAAAAAAAull) >> 1); + val = ((val & 0x3333333333333333ull) << 2) | + ((val & 0xCCCCCCCCCCCCCCCCull) >> 2); + val = ((val & 0x0F0F0F0F0F0F0F0Full) << 4) | + ((val & 0xF0F0F0F0F0F0F0F0ull) >> 4); + + return val; +} + +RVVCALL(OPIVV1, vbrev8_v_b, OP_UU_B, H1, H1, brev8) +RVVCALL(OPIVV1, vbrev8_v_h, OP_UU_H, H2, H2, brev8) +RVVCALL(OPIVV1, vbrev8_v_w, OP_UU_W, H4, H4, brev8) +RVVCALL(OPIVV1, vbrev8_v_d, OP_UU_D, H8, H8, brev8) +GEN_VEXT_V(vbrev8_v_b, 1) +GEN_VEXT_V(vbrev8_v_h, 2) +GEN_VEXT_V(vbrev8_v_w, 4) +GEN_VEXT_V(vbrev8_v_d, 8) + +#define DO_IDENTITY(a) (a) +RVVCALL(OPIVV1, vrev8_v_b, OP_UU_B, H1, H1, DO_IDENTITY) +RVVCALL(OPIVV1, vrev8_v_h, OP_UU_H, H2, H2, bswap16) +RVVCALL(OPIVV1, vrev8_v_w, OP_UU_W, H4, H4, bswap32) +RVVCALL(OPIVV1, vrev8_v_d, OP_UU_D, H8, H8, bswap64) +GEN_VEXT_V(vrev8_v_b, 1) +GEN_VEXT_V(vrev8_v_h, 2) +GEN_VEXT_V(vrev8_v_w, 4) +GEN_VEXT_V(vrev8_v_d, 8) + +#define DO_ANDN(a, b) ((a) & ~(b)) +RVVCALL(OPIVV2, vandn_vv_b, OP_UUU_B, H1, H1, H1, DO_ANDN) +RVVCALL(OPIVV2, vandn_vv_h, OP_UUU_H, H2, H2, H2, DO_ANDN) +RVVCALL(OPIVV2, vandn_vv_w, OP_UUU_W, H4, H4, H4, DO_ANDN) +RVVCALL(OPIVV2, vandn_vv_d, OP_UUU_D, H8, H8, H8, DO_ANDN) +GEN_VEXT_VV(vandn_vv_b, 1) +GEN_VEXT_VV(vandn_vv_h, 2) +GEN_VEXT_VV(vandn_vv_w, 4) +GEN_VEXT_VV(vandn_vv_d, 8) + +RVVCALL(OPIVX2, vandn_vx_b, OP_UUU_B, H1, H1, DO_ANDN) +RVVCALL(OPIVX2, vandn_vx_h, OP_UUU_H, H2, H2, DO_ANDN) +RVVCALL(OPIVX2, vandn_vx_w, OP_UUU_W, H4, H4, DO_ANDN) +RVVCALL(OPIVX2, vandn_vx_d, OP_UUU_D, H8, H8, DO_ANDN) +GEN_VEXT_VX(vandn_vx_b, 1) +GEN_VEXT_VX(vandn_vx_h, 2) +GEN_VEXT_VX(vandn_vx_w, 4) +GEN_VEXT_VX(vandn_vx_d, 8) + +RVVCALL(OPIVV1, vbrev_v_b, OP_UU_B, H1, H1, revbit8) +RVVCALL(OPIVV1, vbrev_v_h, OP_UU_H, H2, H2, revbit16) +RVVCALL(OPIVV1, vbrev_v_w, OP_UU_W, H4, H4, revbit32) +RVVCALL(OPIVV1, vbrev_v_d, OP_UU_D, H8, H8, revbit64) +GEN_VEXT_V(vbrev_v_b, 1) +GEN_VEXT_V(vbrev_v_h, 2) +GEN_VEXT_V(vbrev_v_w, 4) +GEN_VEXT_V(vbrev_v_d, 8) + +RVVCALL(OPIVV1, vclz_v_b, OP_UU_B, H1, H1, clz8) +RVVCALL(OPIVV1, vclz_v_h, OP_UU_H, H2, H2, clz16) +RVVCALL(OPIVV1, vclz_v_w, OP_UU_W, H4, H4, clz32) +RVVCALL(OPIVV1, vclz_v_d, OP_UU_D, H8, H8, clz64) +GEN_VEXT_V(vclz_v_b, 1) +GEN_VEXT_V(vclz_v_h, 2) +GEN_VEXT_V(vclz_v_w, 4) +GEN_VEXT_V(vclz_v_d, 8) + +RVVCALL(OPIVV1, vctz_v_b, OP_UU_B, H1, H1, ctz8) +RVVCALL(OPIVV1, vctz_v_h, OP_UU_H, H2, H2, ctz16) +RVVCALL(OPIVV1, vctz_v_w, OP_UU_W, H4, H4, ctz32) +RVVCALL(OPIVV1, vctz_v_d, OP_UU_D, H8, H8, ctz64) +GEN_VEXT_V(vctz_v_b, 1) +GEN_VEXT_V(vctz_v_h, 2) +GEN_VEXT_V(vctz_v_w, 4) +GEN_VEXT_V(vctz_v_d, 8) + +RVVCALL(OPIVV1, vcpop_v_b, OP_UU_B, H1, H1, ctpop8) +RVVCALL(OPIVV1, vcpop_v_h, OP_UU_H, H2, H2, ctpop16) +RVVCALL(OPIVV1, vcpop_v_w, OP_UU_W, H4, H4, ctpop32) +RVVCALL(OPIVV1, vcpop_v_d, OP_UU_D, H8, H8, ctpop64) +GEN_VEXT_V(vcpop_v_b, 1) +GEN_VEXT_V(vcpop_v_h, 2) +GEN_VEXT_V(vcpop_v_w, 4) +GEN_VEXT_V(vcpop_v_d, 8) + +#define DO_SLL(N, M) (N << (M & (sizeof(N) * 8 - 1))) +RVVCALL(OPIVV2, vwsll_vv_b, WOP_UUU_B, H2, H1, H1, DO_SLL) +RVVCALL(OPIVV2, vwsll_vv_h, WOP_UUU_H, H4, H2, H2, DO_SLL) +RVVCALL(OPIVV2, vwsll_vv_w, WOP_UUU_W, H8, H4, H4, DO_SLL) +GEN_VEXT_VV(vwsll_vv_b, 2) +GEN_VEXT_VV(vwsll_vv_h, 4) +GEN_VEXT_VV(vwsll_vv_w, 8) + +RVVCALL(OPIVX2, vwsll_vx_b, WOP_UUU_B, H2, H1, DO_SLL) +RVVCALL(OPIVX2, vwsll_vx_h, WOP_UUU_H, H4, H2, DO_SLL) +RVVCALL(OPIVX2, vwsll_vx_w, WOP_UUU_W, H8, H4, DO_SLL) +GEN_VEXT_VX(vwsll_vx_b, 2) +GEN_VEXT_VX(vwsll_vx_h, 4) +GEN_VEXT_VX(vwsll_vx_w, 8) + +void HELPER(egs_check)(uint32_t egs, CPURISCVState *env) +{ + uint32_t vl = env->vl; + uint32_t vstart = env->vstart; + + if (vl % egs != 0 || vstart % egs != 0) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } +} + +static inline void xor_round_key(AESState *round_state, AESState *round_key) +{ + round_state->v = round_state->v ^ round_key->v; +} + +#define GEN_ZVKNED_HELPER_VV(NAME, ...) \ + void HELPER(NAME)(void *vd, void *vs2, CPURISCVState *env, \ + uint32_t desc) \ + { \ + uint32_t vl = env->vl; \ + uint32_t total_elems = vext_get_total_elems(env, desc, 4); \ + uint32_t vta = vext_vta(desc); \ + \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ + for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { \ + AESState round_key; \ + round_key.d[0] = *((uint64_t *)vs2 + H8(i * 2 + 0)); \ + round_key.d[1] = *((uint64_t *)vs2 + H8(i * 2 + 1)); \ + AESState round_state; \ + round_state.d[0] = *((uint64_t *)vd + H8(i * 2 + 0)); \ + round_state.d[1] = *((uint64_t *)vd + H8(i * 2 + 1)); \ + __VA_ARGS__; \ + *((uint64_t *)vd + H8(i * 2 + 0)) = round_state.d[0]; \ + *((uint64_t *)vd + H8(i * 2 + 1)) = round_state.d[1]; \ + } \ + env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * 4, total_elems * 4); \ + } + +#define GEN_ZVKNED_HELPER_VS(NAME, ...) \ + void HELPER(NAME)(void *vd, void *vs2, CPURISCVState *env, \ + uint32_t desc) \ + { \ + uint32_t vl = env->vl; \ + uint32_t total_elems = vext_get_total_elems(env, desc, 4); \ + uint32_t vta = vext_vta(desc); \ + \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ + for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { \ + AESState round_key; \ + round_key.d[0] = *((uint64_t *)vs2 + H8(0)); \ + round_key.d[1] = *((uint64_t *)vs2 + H8(1)); \ + AESState round_state; \ + round_state.d[0] = *((uint64_t *)vd + H8(i * 2 + 0)); \ + round_state.d[1] = *((uint64_t *)vd + H8(i * 2 + 1)); \ + __VA_ARGS__; \ + *((uint64_t *)vd + H8(i * 2 + 0)) = round_state.d[0]; \ + *((uint64_t *)vd + H8(i * 2 + 1)) = round_state.d[1]; \ + } \ + env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * 4, total_elems * 4); \ + } + +GEN_ZVKNED_HELPER_VV(vaesef_vv, aesenc_SB_SR_AK(&round_state, + &round_state, + &round_key, + false);) +GEN_ZVKNED_HELPER_VS(vaesef_vs, aesenc_SB_SR_AK(&round_state, + &round_state, + &round_key, + false);) +GEN_ZVKNED_HELPER_VV(vaesdf_vv, aesdec_ISB_ISR_AK(&round_state, + &round_state, + &round_key, + false);) +GEN_ZVKNED_HELPER_VS(vaesdf_vs, aesdec_ISB_ISR_AK(&round_state, + &round_state, + &round_key, + false);) +GEN_ZVKNED_HELPER_VV(vaesem_vv, aesenc_SB_SR_MC_AK(&round_state, + &round_state, + &round_key, + false);) +GEN_ZVKNED_HELPER_VS(vaesem_vs, aesenc_SB_SR_MC_AK(&round_state, + &round_state, + &round_key, + false);) +GEN_ZVKNED_HELPER_VV(vaesdm_vv, aesdec_ISB_ISR_AK_IMC(&round_state, + &round_state, + &round_key, + false);) +GEN_ZVKNED_HELPER_VS(vaesdm_vs, aesdec_ISB_ISR_AK_IMC(&round_state, + &round_state, + &round_key, + false);) +GEN_ZVKNED_HELPER_VS(vaesz_vs, xor_round_key(&round_state, &round_key);) + +void HELPER(vaeskf1_vi)(void *vd_vptr, void *vs2_vptr, uint32_t uimm, + CPURISCVState *env, uint32_t desc) +{ + uint32_t *vd = vd_vptr; + uint32_t *vs2 = vs2_vptr; + uint32_t vl = env->vl; + uint32_t total_elems = vext_get_total_elems(env, desc, 4); + uint32_t vta = vext_vta(desc); + + VSTART_CHECK_EARLY_EXIT(env); + + uimm &= 0b1111; + if (uimm > 10 || uimm == 0) { + uimm ^= 0b1000; + } + + for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { + uint32_t rk[8], tmp; + static const uint32_t rcon[] = { + 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, + 0x00000020, 0x00000040, 0x00000080, 0x0000001B, 0x00000036, + }; + + rk[0] = vs2[i * 4 + H4(0)]; + rk[1] = vs2[i * 4 + H4(1)]; + rk[2] = vs2[i * 4 + H4(2)]; + rk[3] = vs2[i * 4 + H4(3)]; + tmp = ror32(rk[3], 8); + + rk[4] = rk[0] ^ (((uint32_t)AES_sbox[(tmp >> 24) & 0xff] << 24) | + ((uint32_t)AES_sbox[(tmp >> 16) & 0xff] << 16) | + ((uint32_t)AES_sbox[(tmp >> 8) & 0xff] << 8) | + ((uint32_t)AES_sbox[(tmp >> 0) & 0xff] << 0)) + ^ rcon[uimm - 1]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + + vd[i * 4 + H4(0)] = rk[4]; + vd[i * 4 + H4(1)] = rk[5]; + vd[i * 4 + H4(2)] = rk[6]; + vd[i * 4 + H4(3)] = rk[7]; + } + env->vstart = 0; + /* set tail elements to 1s */ + vext_set_elems_1s(vd, vta, vl * 4, total_elems * 4); +} + +void HELPER(vaeskf2_vi)(void *vd_vptr, void *vs2_vptr, uint32_t uimm, + CPURISCVState *env, uint32_t desc) +{ + uint32_t *vd = vd_vptr; + uint32_t *vs2 = vs2_vptr; + uint32_t vl = env->vl; + uint32_t total_elems = vext_get_total_elems(env, desc, 4); + uint32_t vta = vext_vta(desc); + + VSTART_CHECK_EARLY_EXIT(env); + + uimm &= 0b1111; + if (uimm > 14 || uimm < 2) { + uimm ^= 0b1000; + } + + for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { + uint32_t rk[12], tmp; + static const uint32_t rcon[] = { + 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, + 0x00000020, 0x00000040, 0x00000080, 0x0000001B, 0x00000036, + }; + + rk[0] = vd[i * 4 + H4(0)]; + rk[1] = vd[i * 4 + H4(1)]; + rk[2] = vd[i * 4 + H4(2)]; + rk[3] = vd[i * 4 + H4(3)]; + rk[4] = vs2[i * 4 + H4(0)]; + rk[5] = vs2[i * 4 + H4(1)]; + rk[6] = vs2[i * 4 + H4(2)]; + rk[7] = vs2[i * 4 + H4(3)]; + + if (uimm % 2 == 0) { + tmp = ror32(rk[7], 8); + rk[8] = rk[0] ^ (((uint32_t)AES_sbox[(tmp >> 24) & 0xff] << 24) | + ((uint32_t)AES_sbox[(tmp >> 16) & 0xff] << 16) | + ((uint32_t)AES_sbox[(tmp >> 8) & 0xff] << 8) | + ((uint32_t)AES_sbox[(tmp >> 0) & 0xff] << 0)) + ^ rcon[(uimm - 1) / 2]; + } else { + rk[8] = rk[0] ^ (((uint32_t)AES_sbox[(rk[7] >> 24) & 0xff] << 24) | + ((uint32_t)AES_sbox[(rk[7] >> 16) & 0xff] << 16) | + ((uint32_t)AES_sbox[(rk[7] >> 8) & 0xff] << 8) | + ((uint32_t)AES_sbox[(rk[7] >> 0) & 0xff] << 0)); + } + rk[9] = rk[1] ^ rk[8]; + rk[10] = rk[2] ^ rk[9]; + rk[11] = rk[3] ^ rk[10]; + + vd[i * 4 + H4(0)] = rk[8]; + vd[i * 4 + H4(1)] = rk[9]; + vd[i * 4 + H4(2)] = rk[10]; + vd[i * 4 + H4(3)] = rk[11]; + } + env->vstart = 0; + /* set tail elements to 1s */ + vext_set_elems_1s(vd, vta, vl * 4, total_elems * 4); +} + +static inline uint32_t sig0_sha256(uint32_t x) +{ + return ror32(x, 7) ^ ror32(x, 18) ^ (x >> 3); +} + +static inline uint32_t sig1_sha256(uint32_t x) +{ + return ror32(x, 17) ^ ror32(x, 19) ^ (x >> 10); +} + +static inline uint64_t sig0_sha512(uint64_t x) +{ + return ror64(x, 1) ^ ror64(x, 8) ^ (x >> 7); +} + +static inline uint64_t sig1_sha512(uint64_t x) +{ + return ror64(x, 19) ^ ror64(x, 61) ^ (x >> 6); +} + +static inline void vsha2ms_e32(uint32_t *vd, uint32_t *vs1, uint32_t *vs2) +{ + uint32_t res[4]; + res[0] = sig1_sha256(vs1[H4(2)]) + vs2[H4(1)] + sig0_sha256(vd[H4(1)]) + + vd[H4(0)]; + res[1] = sig1_sha256(vs1[H4(3)]) + vs2[H4(2)] + sig0_sha256(vd[H4(2)]) + + vd[H4(1)]; + res[2] = + sig1_sha256(res[0]) + vs2[H4(3)] + sig0_sha256(vd[H4(3)]) + vd[H4(2)]; + res[3] = + sig1_sha256(res[1]) + vs1[H4(0)] + sig0_sha256(vs2[H4(0)]) + vd[H4(3)]; + vd[H4(3)] = res[3]; + vd[H4(2)] = res[2]; + vd[H4(1)] = res[1]; + vd[H4(0)] = res[0]; +} + +static inline void vsha2ms_e64(uint64_t *vd, uint64_t *vs1, uint64_t *vs2) +{ + uint64_t res[4]; + res[0] = sig1_sha512(vs1[2]) + vs2[1] + sig0_sha512(vd[1]) + vd[0]; + res[1] = sig1_sha512(vs1[3]) + vs2[2] + sig0_sha512(vd[2]) + vd[1]; + res[2] = sig1_sha512(res[0]) + vs2[3] + sig0_sha512(vd[3]) + vd[2]; + res[3] = sig1_sha512(res[1]) + vs1[0] + sig0_sha512(vs2[0]) + vd[3]; + vd[3] = res[3]; + vd[2] = res[2]; + vd[1] = res[1]; + vd[0] = res[0]; +} + +void HELPER(vsha2ms_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, + uint32_t desc) +{ + uint32_t sew = FIELD_EX64(env->vtype, VTYPE, VSEW); + uint32_t esz = sew == MO_32 ? 4 : 8; + uint32_t total_elems; + uint32_t vta = vext_vta(desc); + + VSTART_CHECK_EARLY_EXIT(env); + + for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { + if (sew == MO_32) { + vsha2ms_e32(((uint32_t *)vd) + i * 4, ((uint32_t *)vs1) + i * 4, + ((uint32_t *)vs2) + i * 4); + } else { + /* If not 32 then SEW should be 64 */ + vsha2ms_e64(((uint64_t *)vd) + i * 4, ((uint64_t *)vs1) + i * 4, + ((uint64_t *)vs2) + i * 4); + } + } + /* set tail elements to 1s */ + total_elems = vext_get_total_elems(env, desc, esz); + vext_set_elems_1s(vd, vta, env->vl * esz, total_elems * esz); + env->vstart = 0; +} + +static inline uint64_t sum0_64(uint64_t x) +{ + return ror64(x, 28) ^ ror64(x, 34) ^ ror64(x, 39); +} + +static inline uint32_t sum0_32(uint32_t x) +{ + return ror32(x, 2) ^ ror32(x, 13) ^ ror32(x, 22); +} + +static inline uint64_t sum1_64(uint64_t x) +{ + return ror64(x, 14) ^ ror64(x, 18) ^ ror64(x, 41); +} + +static inline uint32_t sum1_32(uint32_t x) +{ + return ror32(x, 6) ^ ror32(x, 11) ^ ror32(x, 25); +} + +#define ch(x, y, z) ((x & y) ^ ((~x) & z)) + +#define maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) + +static void vsha2c_64(uint64_t *vs2, uint64_t *vd, uint64_t *vs1) +{ + uint64_t a = vs2[3], b = vs2[2], e = vs2[1], f = vs2[0]; + uint64_t c = vd[3], d = vd[2], g = vd[1], h = vd[0]; + uint64_t W0 = vs1[0], W1 = vs1[1]; + uint64_t T1 = h + sum1_64(e) + ch(e, f, g) + W0; + uint64_t T2 = sum0_64(a) + maj(a, b, c); + + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + T1 = h + sum1_64(e) + ch(e, f, g) + W1; + T2 = sum0_64(a) + maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + vd[0] = f; + vd[1] = e; + vd[2] = b; + vd[3] = a; +} + +static void vsha2c_32(uint32_t *vs2, uint32_t *vd, uint32_t *vs1) +{ + uint32_t a = vs2[H4(3)], b = vs2[H4(2)], e = vs2[H4(1)], f = vs2[H4(0)]; + uint32_t c = vd[H4(3)], d = vd[H4(2)], g = vd[H4(1)], h = vd[H4(0)]; + uint32_t W0 = vs1[H4(0)], W1 = vs1[H4(1)]; + uint32_t T1 = h + sum1_32(e) + ch(e, f, g) + W0; + uint32_t T2 = sum0_32(a) + maj(a, b, c); + + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + T1 = h + sum1_32(e) + ch(e, f, g) + W1; + T2 = sum0_32(a) + maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + vd[H4(0)] = f; + vd[H4(1)] = e; + vd[H4(2)] = b; + vd[H4(3)] = a; +} + +void HELPER(vsha2ch32_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, + uint32_t desc) +{ + const uint32_t esz = 4; + uint32_t total_elems; + uint32_t vta = vext_vta(desc); + + VSTART_CHECK_EARLY_EXIT(env); + + for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { + vsha2c_32(((uint32_t *)vs2) + 4 * i, ((uint32_t *)vd) + 4 * i, + ((uint32_t *)vs1) + 4 * i + 2); + } + + /* set tail elements to 1s */ + total_elems = vext_get_total_elems(env, desc, esz); + vext_set_elems_1s(vd, vta, env->vl * esz, total_elems * esz); + env->vstart = 0; +} + +void HELPER(vsha2ch64_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, + uint32_t desc) +{ + const uint32_t esz = 8; + uint32_t total_elems; + uint32_t vta = vext_vta(desc); + + VSTART_CHECK_EARLY_EXIT(env); + + for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { + vsha2c_64(((uint64_t *)vs2) + 4 * i, ((uint64_t *)vd) + 4 * i, + ((uint64_t *)vs1) + 4 * i + 2); + } + + /* set tail elements to 1s */ + total_elems = vext_get_total_elems(env, desc, esz); + vext_set_elems_1s(vd, vta, env->vl * esz, total_elems * esz); + env->vstart = 0; +} + +void HELPER(vsha2cl32_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, + uint32_t desc) +{ + const uint32_t esz = 4; + uint32_t total_elems; + uint32_t vta = vext_vta(desc); + + VSTART_CHECK_EARLY_EXIT(env); + + for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { + vsha2c_32(((uint32_t *)vs2) + 4 * i, ((uint32_t *)vd) + 4 * i, + (((uint32_t *)vs1) + 4 * i)); + } + + /* set tail elements to 1s */ + total_elems = vext_get_total_elems(env, desc, esz); + vext_set_elems_1s(vd, vta, env->vl * esz, total_elems * esz); + env->vstart = 0; +} + +void HELPER(vsha2cl64_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, + uint32_t desc) +{ + uint32_t esz = 8; + uint32_t total_elems; + uint32_t vta = vext_vta(desc); + + VSTART_CHECK_EARLY_EXIT(env); + + for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { + vsha2c_64(((uint64_t *)vs2) + 4 * i, ((uint64_t *)vd) + 4 * i, + (((uint64_t *)vs1) + 4 * i)); + } + + /* set tail elements to 1s */ + total_elems = vext_get_total_elems(env, desc, esz); + vext_set_elems_1s(vd, vta, env->vl * esz, total_elems * esz); + env->vstart = 0; +} + +static inline uint32_t p1(uint32_t x) +{ + return x ^ rol32(x, 15) ^ rol32(x, 23); +} + +static inline uint32_t zvksh_w(uint32_t m16, uint32_t m9, uint32_t m3, + uint32_t m13, uint32_t m6) +{ + return p1(m16 ^ m9 ^ rol32(m3, 15)) ^ rol32(m13, 7) ^ m6; +} + +void HELPER(vsm3me_vv)(void *vd_vptr, void *vs1_vptr, void *vs2_vptr, + CPURISCVState *env, uint32_t desc) +{ + uint32_t esz = memop_size(FIELD_EX64(env->vtype, VTYPE, VSEW)); + uint32_t total_elems = vext_get_total_elems(env, desc, esz); + uint32_t vta = vext_vta(desc); + uint32_t *vd = vd_vptr; + uint32_t *vs1 = vs1_vptr; + uint32_t *vs2 = vs2_vptr; + + VSTART_CHECK_EARLY_EXIT(env); + + for (int i = env->vstart / 8; i < env->vl / 8; i++) { + uint32_t w[24]; + for (int j = 0; j < 8; j++) { + w[j] = bswap32(vs1[H4((i * 8) + j)]); + w[j + 8] = bswap32(vs2[H4((i * 8) + j)]); + } + for (int j = 0; j < 8; j++) { + w[j + 16] = + zvksh_w(w[j], w[j + 7], w[j + 13], w[j + 3], w[j + 10]); + } + for (int j = 0; j < 8; j++) { + vd[(i * 8) + j] = bswap32(w[H4(j + 16)]); + } + } + vext_set_elems_1s(vd_vptr, vta, env->vl * esz, total_elems * esz); + env->vstart = 0; +} + +static inline uint32_t ff1(uint32_t x, uint32_t y, uint32_t z) +{ + return x ^ y ^ z; +} + +static inline uint32_t ff2(uint32_t x, uint32_t y, uint32_t z) +{ + return (x & y) | (x & z) | (y & z); +} + +static inline uint32_t ff_j(uint32_t x, uint32_t y, uint32_t z, uint32_t j) +{ + return (j <= 15) ? ff1(x, y, z) : ff2(x, y, z); +} + +static inline uint32_t gg1(uint32_t x, uint32_t y, uint32_t z) +{ + return x ^ y ^ z; +} + +static inline uint32_t gg2(uint32_t x, uint32_t y, uint32_t z) +{ + return (x & y) | (~x & z); +} + +static inline uint32_t gg_j(uint32_t x, uint32_t y, uint32_t z, uint32_t j) +{ + return (j <= 15) ? gg1(x, y, z) : gg2(x, y, z); +} + +static inline uint32_t t_j(uint32_t j) +{ + return (j <= 15) ? 0x79cc4519 : 0x7a879d8a; +} + +static inline uint32_t p_0(uint32_t x) +{ + return x ^ rol32(x, 9) ^ rol32(x, 17); +} + +static void sm3c(uint32_t *vd, uint32_t *vs1, uint32_t *vs2, uint32_t uimm) +{ + uint32_t x0, x1; + uint32_t j; + uint32_t ss1, ss2, tt1, tt2; + x0 = vs2[0] ^ vs2[4]; + x1 = vs2[1] ^ vs2[5]; + j = 2 * uimm; + ss1 = rol32(rol32(vs1[0], 12) + vs1[4] + rol32(t_j(j), j % 32), 7); + ss2 = ss1 ^ rol32(vs1[0], 12); + tt1 = ff_j(vs1[0], vs1[1], vs1[2], j) + vs1[3] + ss2 + x0; + tt2 = gg_j(vs1[4], vs1[5], vs1[6], j) + vs1[7] + ss1 + vs2[0]; + vs1[3] = vs1[2]; + vd[3] = rol32(vs1[1], 9); + vs1[1] = vs1[0]; + vd[1] = tt1; + vs1[7] = vs1[6]; + vd[7] = rol32(vs1[5], 19); + vs1[5] = vs1[4]; + vd[5] = p_0(tt2); + j = 2 * uimm + 1; + ss1 = rol32(rol32(vd[1], 12) + vd[5] + rol32(t_j(j), j % 32), 7); + ss2 = ss1 ^ rol32(vd[1], 12); + tt1 = ff_j(vd[1], vs1[1], vd[3], j) + vs1[3] + ss2 + x1; + tt2 = gg_j(vd[5], vs1[5], vd[7], j) + vs1[7] + ss1 + vs2[1]; + vd[2] = rol32(vs1[1], 9); + vd[0] = tt1; + vd[6] = rol32(vs1[5], 19); + vd[4] = p_0(tt2); +} + +void HELPER(vsm3c_vi)(void *vd_vptr, void *vs2_vptr, uint32_t uimm, + CPURISCVState *env, uint32_t desc) +{ + uint32_t esz = memop_size(FIELD_EX64(env->vtype, VTYPE, VSEW)); + uint32_t total_elems = vext_get_total_elems(env, desc, esz); + uint32_t vta = vext_vta(desc); + uint32_t *vd = vd_vptr; + uint32_t *vs2 = vs2_vptr; + uint32_t v1[8], v2[8], v3[8]; + + VSTART_CHECK_EARLY_EXIT(env); + + for (int i = env->vstart / 8; i < env->vl / 8; i++) { + for (int k = 0; k < 8; k++) { + v2[k] = bswap32(vd[H4(i * 8 + k)]); + v3[k] = bswap32(vs2[H4(i * 8 + k)]); + } + sm3c(v1, v2, v3, uimm); + for (int k = 0; k < 8; k++) { + vd[i * 8 + k] = bswap32(v1[H4(k)]); + } + } + vext_set_elems_1s(vd_vptr, vta, env->vl * esz, total_elems * esz); + env->vstart = 0; +} + +void HELPER(vghsh_vv)(void *vd_vptr, void *vs1_vptr, void *vs2_vptr, + CPURISCVState *env, uint32_t desc) +{ + uint64_t *vd = vd_vptr; + uint64_t *vs1 = vs1_vptr; + uint64_t *vs2 = vs2_vptr; + uint32_t vta = vext_vta(desc); + uint32_t total_elems = vext_get_total_elems(env, desc, 4); + + VSTART_CHECK_EARLY_EXIT(env); + + for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { + uint64_t Y[2] = {vd[i * 2 + 0], vd[i * 2 + 1]}; + uint64_t H[2] = {brev8(vs2[i * 2 + 0]), brev8(vs2[i * 2 + 1])}; + uint64_t X[2] = {vs1[i * 2 + 0], vs1[i * 2 + 1]}; + uint64_t Z[2] = {0, 0}; + + uint64_t S[2] = {brev8(Y[0] ^ X[0]), brev8(Y[1] ^ X[1])}; + + for (int j = 0; j < 128; j++) { + if ((S[j / 64] >> (j % 64)) & 1) { + Z[0] ^= H[0]; + Z[1] ^= H[1]; + } + bool reduce = ((H[1] >> 63) & 1); + H[1] = H[1] << 1 | H[0] >> 63; + H[0] = H[0] << 1; + if (reduce) { + H[0] ^= 0x87; + } + } + + vd[i * 2 + 0] = brev8(Z[0]); + vd[i * 2 + 1] = brev8(Z[1]); + } + /* set tail elements to 1s */ + vext_set_elems_1s(vd, vta, env->vl * 4, total_elems * 4); + env->vstart = 0; +} + +void HELPER(vgmul_vv)(void *vd_vptr, void *vs2_vptr, CPURISCVState *env, + uint32_t desc) +{ + uint64_t *vd = vd_vptr; + uint64_t *vs2 = vs2_vptr; + uint32_t vta = vext_vta(desc); + uint32_t total_elems = vext_get_total_elems(env, desc, 4); + + VSTART_CHECK_EARLY_EXIT(env); + + for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { + uint64_t Y[2] = {brev8(vd[i * 2 + 0]), brev8(vd[i * 2 + 1])}; + uint64_t H[2] = {brev8(vs2[i * 2 + 0]), brev8(vs2[i * 2 + 1])}; + uint64_t Z[2] = {0, 0}; + + for (int j = 0; j < 128; j++) { + if ((Y[j / 64] >> (j % 64)) & 1) { + Z[0] ^= H[0]; + Z[1] ^= H[1]; + } + bool reduce = ((H[1] >> 63) & 1); + H[1] = H[1] << 1 | H[0] >> 63; + H[0] = H[0] << 1; + if (reduce) { + H[0] ^= 0x87; + } + } + + vd[i * 2 + 0] = brev8(Z[0]); + vd[i * 2 + 1] = brev8(Z[1]); + } + /* set tail elements to 1s */ + vext_set_elems_1s(vd, vta, env->vl * 4, total_elems * 4); + env->vstart = 0; +} + +void HELPER(vsm4k_vi)(void *vd, void *vs2, uint32_t uimm5, CPURISCVState *env, + uint32_t desc) +{ + const uint32_t egs = 4; + uint32_t rnd = uimm5 & 0x7; + uint32_t group_start = env->vstart / egs; + uint32_t group_end = env->vl / egs; + uint32_t esz = sizeof(uint32_t); + uint32_t total_elems = vext_get_total_elems(env, desc, esz); + + VSTART_CHECK_EARLY_EXIT(env); + + for (uint32_t i = group_start; i < group_end; ++i) { + uint32_t vstart = i * egs; + uint32_t vend = (i + 1) * egs; + uint32_t rk[4] = {0}; + uint32_t tmp[8] = {0}; + + for (uint32_t j = vstart; j < vend; ++j) { + rk[j - vstart] = *((uint32_t *)vs2 + H4(j)); + } + + for (uint32_t j = 0; j < egs; ++j) { + tmp[j] = rk[j]; + } + + for (uint32_t j = 0; j < egs; ++j) { + uint32_t b, s; + b = tmp[j + 1] ^ tmp[j + 2] ^ tmp[j + 3] ^ sm4_ck[rnd * 4 + j]; + + s = sm4_subword(b); + + tmp[j + 4] = tmp[j] ^ (s ^ rol32(s, 13) ^ rol32(s, 23)); + } + + for (uint32_t j = vstart; j < vend; ++j) { + *((uint32_t *)vd + H4(j)) = tmp[egs + (j - vstart)]; + } + } + + env->vstart = 0; + /* set tail elements to 1s */ + vext_set_elems_1s(vd, vext_vta(desc), env->vl * esz, total_elems * esz); +} + +static void do_sm4_round(uint32_t *rk, uint32_t *buf) +{ + const uint32_t egs = 4; + uint32_t s, b; + + for (uint32_t j = egs; j < egs * 2; ++j) { + b = buf[j - 3] ^ buf[j - 2] ^ buf[j - 1] ^ rk[j - 4]; + + s = sm4_subword(b); + + buf[j] = buf[j - 4] ^ (s ^ rol32(s, 2) ^ rol32(s, 10) ^ rol32(s, 18) ^ + rol32(s, 24)); + } +} + +void HELPER(vsm4r_vv)(void *vd, void *vs2, CPURISCVState *env, uint32_t desc) +{ + const uint32_t egs = 4; + uint32_t group_start = env->vstart / egs; + uint32_t group_end = env->vl / egs; + uint32_t esz = sizeof(uint32_t); + uint32_t total_elems = vext_get_total_elems(env, desc, esz); + + VSTART_CHECK_EARLY_EXIT(env); + + for (uint32_t i = group_start; i < group_end; ++i) { + uint32_t vstart = i * egs; + uint32_t vend = (i + 1) * egs; + uint32_t rk[4] = {0}; + uint32_t tmp[8] = {0}; + + for (uint32_t j = vstart; j < vend; ++j) { + rk[j - vstart] = *((uint32_t *)vs2 + H4(j)); + } + + for (uint32_t j = vstart; j < vend; ++j) { + tmp[j - vstart] = *((uint32_t *)vd + H4(j)); + } + + do_sm4_round(rk, tmp); + + for (uint32_t j = vstart; j < vend; ++j) { + *((uint32_t *)vd + H4(j)) = tmp[egs + (j - vstart)]; + } + } + + env->vstart = 0; + /* set tail elements to 1s */ + vext_set_elems_1s(vd, vext_vta(desc), env->vl * esz, total_elems * esz); +} + +void HELPER(vsm4r_vs)(void *vd, void *vs2, CPURISCVState *env, uint32_t desc) +{ + const uint32_t egs = 4; + uint32_t group_start = env->vstart / egs; + uint32_t group_end = env->vl / egs; + uint32_t esz = sizeof(uint32_t); + uint32_t total_elems = vext_get_total_elems(env, desc, esz); + + VSTART_CHECK_EARLY_EXIT(env); + + for (uint32_t i = group_start; i < group_end; ++i) { + uint32_t vstart = i * egs; + uint32_t vend = (i + 1) * egs; + uint32_t rk[4] = {0}; + uint32_t tmp[8] = {0}; + + for (uint32_t j = 0; j < egs; ++j) { + rk[j] = *((uint32_t *)vs2 + H4(j)); + } + + for (uint32_t j = vstart; j < vend; ++j) { + tmp[j - vstart] = *((uint32_t *)vd + H4(j)); + } + + do_sm4_round(rk, tmp); + + for (uint32_t j = vstart; j < vend; ++j) { + *((uint32_t *)vd + H4(j)) = tmp[egs + (j - vstart)]; + } + } + + env->vstart = 0; + /* set tail elements to 1s */ + vext_set_elems_1s(vd, vext_vta(desc), env->vl * esz, total_elems * esz); +} diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 576b14e5a3..fa139040f8 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -22,10 +22,12 @@ #include "cpu.h" #include "exec/memop.h" #include "exec/exec-all.h" +#include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" #include "tcg/tcg-gvec-desc.h" #include "internals.h" +#include "vector_internals.h" #include target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1, @@ -33,27 +35,32 @@ target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1, { int vlmax, vl; RISCVCPU *cpu = env_archcpu(env); - uint64_t lmul = FIELD_EX64(s2, VTYPE, VLMUL); - uint16_t sew = 8 << FIELD_EX64(s2, VTYPE, VSEW); + uint64_t vlmul = FIELD_EX64(s2, VTYPE, VLMUL); + uint8_t vsew = FIELD_EX64(s2, VTYPE, VSEW); + uint16_t sew = 8 << vsew; uint8_t ediv = FIELD_EX64(s2, VTYPE, VEDIV); int xlen = riscv_cpu_xlen(env); bool vill = (s2 >> (xlen - 1)) & 0x1; target_ulong reserved = s2 & MAKE_64BIT_MASK(R_VTYPE_RESERVED_SHIFT, xlen - 1 - R_VTYPE_RESERVED_SHIFT); + uint16_t vlen = cpu->cfg.vlenb << 3; + int8_t lmul; - if (lmul & 4) { - /* Fractional LMUL. */ - if (lmul == 4 || - cpu->cfg.elen >> (8 - lmul) < sew) { + if (vlmul & 4) { + /* + * Fractional LMUL, check: + * + * VLEN * LMUL >= SEW + * VLEN >> (8 - lmul) >= sew + * (vlenb << 3) >> (8 - lmul) >= sew + */ + if (vlmul == 4 || (vlen >> (8 - vlmul)) < sew) { vill = true; } } - if ((sew > cpu->cfg.elen) - || vill - || (ediv != 0) - || (reserved != 0)) { + if ((sew > cpu->cfg.elen) || vill || (ediv != 0) || (reserved != 0)) { /* only set vill bit. */ env->vill = 1; env->vtype = 0; @@ -62,7 +69,9 @@ target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1, return 0; } - vlmax = vext_get_vlmax(cpu, s2); + /* lmul encoded as in DisasContext::lmul */ + lmul = sextract32(FIELD_EX64(s2, VTYPE, VLMUL), 0, 3); + vlmax = vext_get_vlmax(cpu->cfg.vlenb, vsew, lmul); if (s1 <= vlmax) { vl = s1; } else { @@ -75,59 +84,12 @@ target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1, return vl; } -/* - * Note that vector data is stored in host-endian 64-bit chunks, - * so addressing units smaller than that needs a host-endian fixup. - */ -#if HOST_BIG_ENDIAN -#define H1(x) ((x) ^ 7) -#define H1_2(x) ((x) ^ 6) -#define H1_4(x) ((x) ^ 4) -#define H2(x) ((x) ^ 3) -#define H4(x) ((x) ^ 1) -#define H8(x) ((x)) -#else -#define H1(x) (x) -#define H1_2(x) (x) -#define H1_4(x) (x) -#define H2(x) (x) -#define H4(x) (x) -#define H8(x) (x) -#endif - -static inline uint32_t vext_nf(uint32_t desc) -{ - return FIELD_EX32(simd_data(desc), VDATA, NF); -} - -static inline uint32_t vext_vm(uint32_t desc) -{ - return FIELD_EX32(simd_data(desc), VDATA, VM); -} - -/* - * Encode LMUL to lmul as following: - * LMUL vlmul lmul - * 1 000 0 - * 2 001 1 - * 4 010 2 - * 8 011 3 - * - 100 - - * 1/8 101 -3 - * 1/4 110 -2 - * 1/2 111 -1 - */ -static inline int32_t vext_lmul(uint32_t desc) -{ - return sextract32(FIELD_EX32(simd_data(desc), VDATA, LMUL), 0, 3); -} - /* * Get the maximum number of elements can be operated. * - * esz: log2 of element size in bytes. + * log2_esz: log2 of element size in bytes. */ -static inline uint32_t vext_max_elems(uint32_t desc, uint32_t esz) +static inline uint32_t vext_max_elems(uint32_t desc, uint32_t log2_esz) { /* * As simd_desc support at most 2048 bytes, the max vlen is 1024 bits. @@ -136,19 +98,19 @@ static inline uint32_t vext_max_elems(uint32_t desc, uint32_t esz) uint32_t vlenb = simd_maxsz(desc); /* Return VLMAX */ - int scale = vext_lmul(desc) - esz; + int scale = vext_lmul(desc) - log2_esz; return scale < 0 ? vlenb >> -scale : vlenb << scale; } static inline target_ulong adjust_addr(CPURISCVState *env, target_ulong addr) { - return (addr & env->cur_pmmask) | env->cur_pmbase; + return (addr & ~env->cur_pmmask) | env->cur_pmbase; } /* * This function checks watchpoint before real load operation. * - * In softmmu mode, the TLB API probe_access is enough for watchpoint check. + * In system mode, the TLB API probe_access is enough for watchpoint check. * In user mode, there is no watchpoint support now. * * It will trigger an exception if there is no mapping in TLB @@ -161,14 +123,15 @@ static void probe_pages(CPURISCVState *env, target_ulong addr, { target_ulong pagelen = -(addr | TARGET_PAGE_MASK); target_ulong curlen = MIN(pagelen, len); + int mmu_index = riscv_env_mmu_index(env, false); probe_access(env, adjust_addr(env, addr), curlen, access_type, - cpu_mmu_index(env, false), ra); + mmu_index, ra); if (len > curlen) { addr += curlen; curlen = len - curlen; probe_access(env, adjust_addr(env, addr), curlen, access_type, - cpu_mmu_index(env, false), ra); + mmu_index, ra); } } @@ -181,20 +144,8 @@ static inline void vext_set_elem_mask(void *v0, int index, ((uint64_t *)v0)[idx] = deposit64(old, pos, 1, value); } -/* - * Earlier designs (pre-0.9) had a varying number of bits - * per mask value (MLEN). In the 0.9 design, MLEN=1. - * (Section 4.5) - */ -static inline int vext_elem_mask(void *v0, int index) -{ - int idx = index / 64; - int pos = index % 64; - return (((uint64_t *)v0)[idx] >> pos) & 1; -} - /* elements operations for load and store */ -typedef void vext_ldst_elem_fn(CPURISCVState *env, target_ulong addr, +typedef void vext_ldst_elem_fn(CPURISCVState *env, abi_ptr addr, uint32_t idx, void *vd, uintptr_t retaddr); #define GEN_VEXT_LD_ELEM(NAME, ETYPE, H, LDSUF) \ @@ -223,33 +174,59 @@ GEN_VEXT_ST_ELEM(ste_h, int16_t, H2, stw) GEN_VEXT_ST_ELEM(ste_w, int32_t, H4, stl) GEN_VEXT_ST_ELEM(ste_d, int64_t, H8, stq) +static void vext_set_tail_elems_1s(target_ulong vl, void *vd, + uint32_t desc, uint32_t nf, + uint32_t esz, uint32_t max_elems) +{ + uint32_t vta = vext_vta(desc); + int k; + + if (vta == 0) { + return; + } + + for (k = 0; k < nf; ++k) { + vext_set_elems_1s(vd, vta, (k * max_elems + vl) * esz, + (k * max_elems + max_elems) * esz); + } +} + /* - *** stride: access vector element from strided memory + * stride: access vector element from strided memory */ static void vext_ldst_stride(void *vd, void *v0, target_ulong base, target_ulong stride, CPURISCVState *env, uint32_t desc, uint32_t vm, vext_ldst_elem_fn *ldst_elem, - uint32_t esz, uintptr_t ra, MMUAccessType access_type) + uint32_t log2_esz, uintptr_t ra) { uint32_t i, k; uint32_t nf = vext_nf(desc); - uint32_t max_elems = vext_max_elems(desc, esz); + uint32_t max_elems = vext_max_elems(desc, log2_esz); + uint32_t esz = 1 << log2_esz; + uint32_t vma = vext_vma(desc); - for (i = env->vstart; i < env->vl; i++, env->vstart++) { - if (!vm && !vext_elem_mask(v0, i)) { - continue; - } + VSTART_CHECK_EARLY_EXIT(env); + for (i = env->vstart; i < env->vl; env->vstart = ++i) { k = 0; while (k < nf) { - target_ulong addr = base + stride * i + (k << esz); + if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + vext_set_elems_1s(vd, vma, (i + k * max_elems) * esz, + (i + k * max_elems + 1) * esz); + k++; + continue; + } + target_ulong addr = base + stride * i + (k << log2_esz); ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra); k++; } } env->vstart = 0; + + vext_set_tail_elems_1s(env->vl, vd, desc, nf, esz, max_elems); } #define GEN_VEXT_LD_STRIDE(NAME, ETYPE, LOAD_FN) \ @@ -259,7 +236,7 @@ void HELPER(NAME)(void *vd, void * v0, target_ulong base, \ { \ uint32_t vm = vext_vm(desc); \ vext_ldst_stride(vd, v0, base, stride, env, desc, vm, LOAD_FN, \ - ctzl(sizeof(ETYPE)), GETPC(), MMU_DATA_LOAD); \ + ctzl(sizeof(ETYPE)), GETPC()); \ } GEN_VEXT_LD_STRIDE(vlse8_v, int8_t, lde_b) @@ -274,7 +251,7 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ { \ uint32_t vm = vext_vm(desc); \ vext_ldst_stride(vd, v0, base, stride, env, desc, vm, STORE_FN, \ - ctzl(sizeof(ETYPE)), GETPC(), MMU_DATA_STORE); \ + ctzl(sizeof(ETYPE)), GETPC()); \ } GEN_VEXT_ST_STRIDE(vsse8_v, int8_t, ste_b) @@ -283,34 +260,39 @@ GEN_VEXT_ST_STRIDE(vsse32_v, int32_t, ste_w) GEN_VEXT_ST_STRIDE(vsse64_v, int64_t, ste_d) /* - *** unit-stride: access elements stored contiguously in memory + * unit-stride: access elements stored contiguously in memory */ -/* unmasked unit-stride load and store operation*/ +/* unmasked unit-stride load and store operation */ static void vext_ldst_us(void *vd, target_ulong base, CPURISCVState *env, uint32_t desc, - vext_ldst_elem_fn *ldst_elem, uint32_t esz, uint32_t evl, - uintptr_t ra, MMUAccessType access_type) + vext_ldst_elem_fn *ldst_elem, uint32_t log2_esz, uint32_t evl, + uintptr_t ra) { uint32_t i, k; uint32_t nf = vext_nf(desc); - uint32_t max_elems = vext_max_elems(desc, esz); + uint32_t max_elems = vext_max_elems(desc, log2_esz); + uint32_t esz = 1 << log2_esz; + + VSTART_CHECK_EARLY_EXIT(env); /* load bytes from guest memory */ - for (i = env->vstart; i < evl; i++, env->vstart++) { + for (i = env->vstart; i < evl; env->vstart = ++i) { k = 0; while (k < nf) { - target_ulong addr = base + ((i * nf + k) << esz); + target_ulong addr = base + ((i * nf + k) << log2_esz); ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra); k++; } } env->vstart = 0; + + vext_set_tail_elems_1s(evl, vd, desc, nf, esz, max_elems); } /* - * masked unit-stride load and store operation will be a special case of stride, - * stride = NF * sizeof (MTYPE) + * masked unit-stride load and store operation will be a special case of + * stride, stride = NF * sizeof (ETYPE) */ #define GEN_VEXT_LD_US(NAME, ETYPE, LOAD_FN) \ @@ -319,14 +301,14 @@ void HELPER(NAME##_mask)(void *vd, void *v0, target_ulong base, \ { \ uint32_t stride = vext_nf(desc) << ctzl(sizeof(ETYPE)); \ vext_ldst_stride(vd, v0, base, stride, env, desc, false, LOAD_FN, \ - ctzl(sizeof(ETYPE)), GETPC(), MMU_DATA_LOAD); \ + ctzl(sizeof(ETYPE)), GETPC()); \ } \ \ void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ CPURISCVState *env, uint32_t desc) \ { \ vext_ldst_us(vd, base, env, desc, LOAD_FN, \ - ctzl(sizeof(ETYPE)), env->vl, GETPC(), MMU_DATA_LOAD); \ + ctzl(sizeof(ETYPE)), env->vl, GETPC()); \ } GEN_VEXT_LD_US(vle8_v, int8_t, lde_b) @@ -340,14 +322,14 @@ void HELPER(NAME##_mask)(void *vd, void *v0, target_ulong base, \ { \ uint32_t stride = vext_nf(desc) << ctzl(sizeof(ETYPE)); \ vext_ldst_stride(vd, v0, base, stride, env, desc, false, STORE_FN, \ - ctzl(sizeof(ETYPE)), GETPC(), MMU_DATA_STORE); \ + ctzl(sizeof(ETYPE)), GETPC()); \ } \ \ void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ CPURISCVState *env, uint32_t desc) \ { \ vext_ldst_us(vd, base, env, desc, STORE_FN, \ - ctzl(sizeof(ETYPE)), env->vl, GETPC(), MMU_DATA_STORE); \ + ctzl(sizeof(ETYPE)), env->vl, GETPC()); \ } GEN_VEXT_ST_US(vse8_v, int8_t, ste_b) @@ -356,7 +338,7 @@ GEN_VEXT_ST_US(vse32_v, int32_t, ste_w) GEN_VEXT_ST_US(vse64_v, int64_t, ste_d) /* - *** unit stride mask load and store, EEW = 1 + * unit stride mask load and store, EEW = 1 */ void HELPER(vlm_v)(void *vd, void *v0, target_ulong base, CPURISCVState *env, uint32_t desc) @@ -364,7 +346,7 @@ void HELPER(vlm_v)(void *vd, void *v0, target_ulong base, /* evl = ceil(vl/8) */ uint8_t evl = (env->vl + 7) >> 3; vext_ldst_us(vd, base, env, desc, lde_b, - 0, evl, GETPC(), MMU_DATA_LOAD); + 0, evl, GETPC()); } void HELPER(vsm_v)(void *vd, void *v0, target_ulong base, @@ -373,11 +355,11 @@ void HELPER(vsm_v)(void *vd, void *v0, target_ulong base, /* evl = ceil(vl/8) */ uint8_t evl = (env->vl + 7) >> 3; vext_ldst_us(vd, base, env, desc, ste_b, - 0, evl, GETPC(), MMU_DATA_STORE); + 0, evl, GETPC()); } /* - *** index: access vector element from indexed memory + * index: access vector element from indexed memory */ typedef target_ulong vext_get_index_addr(target_ulong base, uint32_t idx, void *vs2); @@ -399,27 +381,36 @@ vext_ldst_index(void *vd, void *v0, target_ulong base, void *vs2, CPURISCVState *env, uint32_t desc, vext_get_index_addr get_index_addr, vext_ldst_elem_fn *ldst_elem, - uint32_t esz, uintptr_t ra, MMUAccessType access_type) + uint32_t log2_esz, uintptr_t ra) { uint32_t i, k; uint32_t nf = vext_nf(desc); uint32_t vm = vext_vm(desc); - uint32_t max_elems = vext_max_elems(desc, esz); + uint32_t max_elems = vext_max_elems(desc, log2_esz); + uint32_t esz = 1 << log2_esz; + uint32_t vma = vext_vma(desc); + + VSTART_CHECK_EARLY_EXIT(env); /* load bytes from guest memory */ - for (i = env->vstart; i < env->vl; i++, env->vstart++) { - if (!vm && !vext_elem_mask(v0, i)) { - continue; - } - + for (i = env->vstart; i < env->vl; env->vstart = ++i) { k = 0; while (k < nf) { - abi_ptr addr = get_index_addr(base, i, vs2) + (k << esz); + if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + vext_set_elems_1s(vd, vma, (i + k * max_elems) * esz, + (i + k * max_elems + 1) * esz); + k++; + continue; + } + abi_ptr addr = get_index_addr(base, i, vs2) + (k << log2_esz); ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra); k++; } } env->vstart = 0; + + vext_set_tail_elems_1s(env->vl, vd, desc, nf, esz, max_elems); } #define GEN_VEXT_LD_INDEX(NAME, ETYPE, INDEX_FN, LOAD_FN) \ @@ -427,7 +418,7 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ void *vs2, CPURISCVState *env, uint32_t desc) \ { \ vext_ldst_index(vd, v0, base, vs2, env, desc, INDEX_FN, \ - LOAD_FN, ctzl(sizeof(ETYPE)), GETPC(), MMU_DATA_LOAD); \ + LOAD_FN, ctzl(sizeof(ETYPE)), GETPC()); \ } GEN_VEXT_LD_INDEX(vlxei8_8_v, int8_t, idx_b, lde_b) @@ -453,7 +444,7 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ { \ vext_ldst_index(vd, v0, base, vs2, env, desc, INDEX_FN, \ STORE_FN, ctzl(sizeof(ETYPE)), \ - GETPC(), MMU_DATA_STORE); \ + GETPC()); \ } GEN_VEXT_ST_INDEX(vsxei8_8_v, int8_t, idx_b, ste_b) @@ -474,39 +465,43 @@ GEN_VEXT_ST_INDEX(vsxei64_32_v, int32_t, idx_d, ste_w) GEN_VEXT_ST_INDEX(vsxei64_64_v, int64_t, idx_d, ste_d) /* - *** unit-stride fault-only-fisrt load instructions + * unit-stride fault-only-fisrt load instructions */ static inline void vext_ldff(void *vd, void *v0, target_ulong base, CPURISCVState *env, uint32_t desc, vext_ldst_elem_fn *ldst_elem, - uint32_t esz, uintptr_t ra) + uint32_t log2_esz, uintptr_t ra) { void *host; uint32_t i, k, vl = 0; uint32_t nf = vext_nf(desc); uint32_t vm = vext_vm(desc); - uint32_t max_elems = vext_max_elems(desc, esz); + uint32_t max_elems = vext_max_elems(desc, log2_esz); + uint32_t esz = 1 << log2_esz; + uint32_t vma = vext_vma(desc); target_ulong addr, offset, remain; + int mmu_index = riscv_env_mmu_index(env, false); - /* probe every access*/ + VSTART_CHECK_EARLY_EXIT(env); + + /* probe every access */ for (i = env->vstart; i < env->vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { continue; } - addr = adjust_addr(env, base + i * (nf << esz)); + addr = adjust_addr(env, base + i * (nf << log2_esz)); if (i == 0) { - probe_pages(env, addr, nf << esz, ra, MMU_DATA_LOAD); + probe_pages(env, addr, nf << log2_esz, ra, MMU_DATA_LOAD); } else { /* if it triggers an exception, no need to check watchpoint */ - remain = nf << esz; + remain = nf << log2_esz; while (remain > 0) { offset = -(addr | TARGET_PAGE_MASK); - host = tlb_vaddr_to_host(env, addr, MMU_DATA_LOAD, - cpu_mmu_index(env, false)); + host = tlb_vaddr_to_host(env, addr, MMU_DATA_LOAD, mmu_index); if (host) { #ifdef CONFIG_USER_ONLY - if (page_check_range(addr, offset, PAGE_READ) < 0) { + if (!page_check_range(addr, offset, PAGE_READ)) { vl = i; goto ProbeSuccess; } @@ -532,16 +527,22 @@ ProbeSuccess: } for (i = env->vstart; i < env->vl; i++) { k = 0; - if (!vm && !vext_elem_mask(v0, i)) { - continue; - } while (k < nf) { - target_ulong addr = base + ((i * nf + k) << esz); + if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + vext_set_elems_1s(vd, vma, (i + k * max_elems) * esz, + (i + k * max_elems + 1) * esz); + k++; + continue; + } + addr = base + ((i * nf + k) << log2_esz); ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra); k++; } } env->vstart = 0; + + vext_set_tail_elems_1s(env->vl, vd, desc, nf, esz, max_elems); } #define GEN_VEXT_LDFF(NAME, ETYPE, LOAD_FN) \ @@ -567,22 +568,22 @@ GEN_VEXT_LDFF(vle64ff_v, int64_t, lde_d) #define DO_MAX(N, M) ((N) >= (M) ? (N) : (M)) #define DO_MIN(N, M) ((N) >= (M) ? (M) : (N)) -/* Unsigned min/max */ -#define DO_MAXU(N, M) DO_MAX((UMTYPE)N, (UMTYPE)M) -#define DO_MINU(N, M) DO_MIN((UMTYPE)N, (UMTYPE)M) - /* - *** load and store whole register instructions + * load and store whole register instructions */ static void vext_ldst_whole(void *vd, target_ulong base, CPURISCVState *env, uint32_t desc, - vext_ldst_elem_fn *ldst_elem, uint32_t esz, uintptr_t ra, - MMUAccessType access_type) + vext_ldst_elem_fn *ldst_elem, uint32_t log2_esz, uintptr_t ra) { uint32_t i, k, off, pos; uint32_t nf = vext_nf(desc); - uint32_t vlenb = env_archcpu(env)->cfg.vlen >> 3; - uint32_t max_elems = vlenb >> esz; + uint32_t vlenb = riscv_cpu_cfg(env)->vlenb; + uint32_t max_elems = vlenb >> log2_esz; + + if (env->vstart >= ((vlenb * nf) >> log2_esz)) { + env->vstart = 0; + return; + } k = env->vstart / max_elems; off = env->vstart % max_elems; @@ -590,8 +591,9 @@ vext_ldst_whole(void *vd, target_ulong base, CPURISCVState *env, uint32_t desc, if (off) { /* load/store rest of elements of current segment pointed by vstart */ for (pos = off; pos < max_elems; pos++, env->vstart++) { - target_ulong addr = base + ((pos + k * max_elems) << esz); - ldst_elem(env, adjust_addr(env, addr), pos + k * max_elems, vd, ra); + target_ulong addr = base + ((pos + k * max_elems) << log2_esz); + ldst_elem(env, adjust_addr(env, addr), pos + k * max_elems, vd, + ra); } k++; } @@ -599,7 +601,7 @@ vext_ldst_whole(void *vd, target_ulong base, CPURISCVState *env, uint32_t desc, /* load/store elements for rest of segments */ for (; k < nf; k++) { for (i = 0; i < max_elems; i++, env->vstart++) { - target_ulong addr = base + ((i + k * max_elems) << esz); + target_ulong addr = base + ((i + k * max_elems) << log2_esz); ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra); } } @@ -612,8 +614,7 @@ void HELPER(NAME)(void *vd, target_ulong base, \ CPURISCVState *env, uint32_t desc) \ { \ vext_ldst_whole(vd, base, env, desc, LOAD_FN, \ - ctzl(sizeof(ETYPE)), GETPC(), \ - MMU_DATA_LOAD); \ + ctzl(sizeof(ETYPE)), GETPC()); \ } GEN_VEXT_LD_WHOLE(vl1re8_v, int8_t, lde_b) @@ -638,8 +639,7 @@ void HELPER(NAME)(void *vd, target_ulong base, \ CPURISCVState *env, uint32_t desc) \ { \ vext_ldst_whole(vd, base, env, desc, STORE_FN, \ - ctzl(sizeof(ETYPE)), GETPC(), \ - MMU_DATA_STORE); \ + ctzl(sizeof(ETYPE)), GETPC()); \ } GEN_VEXT_ST_WHOLE(vs1r_v, int8_t, ste_b) @@ -648,28 +648,18 @@ GEN_VEXT_ST_WHOLE(vs4r_v, int8_t, ste_b) GEN_VEXT_ST_WHOLE(vs8r_v, int8_t, ste_b) /* - *** Vector Integer Arithmetic Instructions + * Vector Integer Arithmetic Instructions */ -/* expand macro args before macro */ -#define RVVCALL(macro, ...) macro(__VA_ARGS__) - /* (TD, T1, T2, TX1, TX2) */ #define OP_SSS_B int8_t, int8_t, int8_t, int8_t, int8_t #define OP_SSS_H int16_t, int16_t, int16_t, int16_t, int16_t #define OP_SSS_W int32_t, int32_t, int32_t, int32_t, int32_t #define OP_SSS_D int64_t, int64_t, int64_t, int64_t, int64_t -#define OP_UUU_B uint8_t, uint8_t, uint8_t, uint8_t, uint8_t -#define OP_UUU_H uint16_t, uint16_t, uint16_t, uint16_t, uint16_t -#define OP_UUU_W uint32_t, uint32_t, uint32_t, uint32_t, uint32_t -#define OP_UUU_D uint64_t, uint64_t, uint64_t, uint64_t, uint64_t #define OP_SUS_B int8_t, uint8_t, int8_t, uint8_t, int8_t #define OP_SUS_H int16_t, uint16_t, int16_t, uint16_t, int16_t #define OP_SUS_W int32_t, uint32_t, int32_t, uint32_t, int32_t #define OP_SUS_D int64_t, uint64_t, int64_t, uint64_t, int64_t -#define WOP_UUU_B uint16_t, uint8_t, uint8_t, uint16_t, uint16_t -#define WOP_UUU_H uint32_t, uint16_t, uint16_t, uint32_t, uint32_t -#define WOP_UUU_W uint64_t, uint32_t, uint32_t, uint64_t, uint64_t #define WOP_SSS_B int16_t, int8_t, int8_t, int16_t, int16_t #define WOP_SSS_H int32_t, int16_t, int16_t, int32_t, int32_t #define WOP_SSS_W int64_t, int32_t, int32_t, int64_t, int64_t @@ -686,16 +676,6 @@ GEN_VEXT_ST_WHOLE(vs8r_v, int8_t, ste_b) #define NOP_UUU_H uint16_t, uint16_t, uint32_t, uint16_t, uint32_t #define NOP_UUU_W uint32_t, uint32_t, uint64_t, uint32_t, uint64_t -/* operation of two vector elements */ -typedef void opivv2_fn(void *vd, void *vs1, void *vs2, int i); - -#define OPIVV2(NAME, TD, T1, T2, TX1, TX2, HD, HS1, HS2, OP) \ -static void do_##NAME(void *vd, void *vs1, void *vs2, int i) \ -{ \ - TX1 s1 = *((T1 *)vs1 + HS1(i)); \ - TX2 s2 = *((T2 *)vs2 + HS2(i)); \ - *((TD *)vd + HD(i)) = OP(s2, s1); \ -} #define DO_SUB(N, M) (N - M) #define DO_RSUB(N, M) (M - N) @@ -708,55 +688,15 @@ RVVCALL(OPIVV2, vsub_vv_h, OP_SSS_H, H2, H2, H2, DO_SUB) RVVCALL(OPIVV2, vsub_vv_w, OP_SSS_W, H4, H4, H4, DO_SUB) RVVCALL(OPIVV2, vsub_vv_d, OP_SSS_D, H8, H8, H8, DO_SUB) -static void do_vext_vv(void *vd, void *v0, void *vs1, void *vs2, - CPURISCVState *env, uint32_t desc, - uint32_t esz, uint32_t dsz, - opivv2_fn *fn) -{ - uint32_t vm = vext_vm(desc); - uint32_t vl = env->vl; - uint32_t i; +GEN_VEXT_VV(vadd_vv_b, 1) +GEN_VEXT_VV(vadd_vv_h, 2) +GEN_VEXT_VV(vadd_vv_w, 4) +GEN_VEXT_VV(vadd_vv_d, 8) +GEN_VEXT_VV(vsub_vv_b, 1) +GEN_VEXT_VV(vsub_vv_h, 2) +GEN_VEXT_VV(vsub_vv_w, 4) +GEN_VEXT_VV(vsub_vv_d, 8) - for (i = env->vstart; i < vl; i++) { - if (!vm && !vext_elem_mask(v0, i)) { - continue; - } - fn(vd, vs1, vs2, i); - } - env->vstart = 0; -} - -/* generate the helpers for OPIVV */ -#define GEN_VEXT_VV(NAME, ESZ, DSZ) \ -void HELPER(NAME)(void *vd, void *v0, void *vs1, \ - void *vs2, CPURISCVState *env, \ - uint32_t desc) \ -{ \ - do_vext_vv(vd, v0, vs1, vs2, env, desc, ESZ, DSZ, \ - do_##NAME); \ -} - -GEN_VEXT_VV(vadd_vv_b, 1, 1) -GEN_VEXT_VV(vadd_vv_h, 2, 2) -GEN_VEXT_VV(vadd_vv_w, 4, 4) -GEN_VEXT_VV(vadd_vv_d, 8, 8) -GEN_VEXT_VV(vsub_vv_b, 1, 1) -GEN_VEXT_VV(vsub_vv_h, 2, 2) -GEN_VEXT_VV(vsub_vv_w, 4, 4) -GEN_VEXT_VV(vsub_vv_d, 8, 8) - -typedef void opivx2_fn(void *vd, target_long s1, void *vs2, int i); - -/* - * (T1)s1 gives the real operator type. - * (TX1)(T1)s1 expands the operator type of widen or narrow operations. - */ -#define OPIVX2(NAME, TD, T1, T2, TX1, TX2, HD, HS2, OP) \ -static void do_##NAME(void *vd, target_long s1, void *vs2, int i) \ -{ \ - TX2 s2 = *((T2 *)vs2 + HS2(i)); \ - *((TD *)vd + HD(i)) = OP(s2, (TX1)(T1)s1); \ -} RVVCALL(OPIVX2, vadd_vx_b, OP_SSS_B, H1, H1, DO_ADD) RVVCALL(OPIVX2, vadd_vx_h, OP_SSS_H, H2, H2, DO_ADD) @@ -771,46 +711,18 @@ RVVCALL(OPIVX2, vrsub_vx_h, OP_SSS_H, H2, H2, DO_RSUB) RVVCALL(OPIVX2, vrsub_vx_w, OP_SSS_W, H4, H4, DO_RSUB) RVVCALL(OPIVX2, vrsub_vx_d, OP_SSS_D, H8, H8, DO_RSUB) -static void do_vext_vx(void *vd, void *v0, target_long s1, void *vs2, - CPURISCVState *env, uint32_t desc, - uint32_t esz, uint32_t dsz, - opivx2_fn fn) -{ - uint32_t vm = vext_vm(desc); - uint32_t vl = env->vl; - uint32_t i; - - for (i = env->vstart; i < vl; i++) { - if (!vm && !vext_elem_mask(v0, i)) { - continue; - } - fn(vd, s1, vs2, i); - } - env->vstart = 0; -} - -/* generate the helpers for OPIVX */ -#define GEN_VEXT_VX(NAME, ESZ, DSZ) \ -void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ - void *vs2, CPURISCVState *env, \ - uint32_t desc) \ -{ \ - do_vext_vx(vd, v0, s1, vs2, env, desc, ESZ, DSZ, \ - do_##NAME); \ -} - -GEN_VEXT_VX(vadd_vx_b, 1, 1) -GEN_VEXT_VX(vadd_vx_h, 2, 2) -GEN_VEXT_VX(vadd_vx_w, 4, 4) -GEN_VEXT_VX(vadd_vx_d, 8, 8) -GEN_VEXT_VX(vsub_vx_b, 1, 1) -GEN_VEXT_VX(vsub_vx_h, 2, 2) -GEN_VEXT_VX(vsub_vx_w, 4, 4) -GEN_VEXT_VX(vsub_vx_d, 8, 8) -GEN_VEXT_VX(vrsub_vx_b, 1, 1) -GEN_VEXT_VX(vrsub_vx_h, 2, 2) -GEN_VEXT_VX(vrsub_vx_w, 4, 4) -GEN_VEXT_VX(vrsub_vx_d, 8, 8) +GEN_VEXT_VX(vadd_vx_b, 1) +GEN_VEXT_VX(vadd_vx_h, 2) +GEN_VEXT_VX(vadd_vx_w, 4) +GEN_VEXT_VX(vadd_vx_d, 8) +GEN_VEXT_VX(vsub_vx_b, 1) +GEN_VEXT_VX(vsub_vx_h, 2) +GEN_VEXT_VX(vsub_vx_w, 4) +GEN_VEXT_VX(vsub_vx_d, 8) +GEN_VEXT_VX(vrsub_vx_b, 1) +GEN_VEXT_VX(vrsub_vx_h, 2) +GEN_VEXT_VX(vrsub_vx_w, 4) +GEN_VEXT_VX(vrsub_vx_d, 8) void HELPER(vec_rsubs8)(void *d, void *a, uint64_t b, uint32_t desc) { @@ -889,30 +801,30 @@ RVVCALL(OPIVV2, vwadd_wv_w, WOP_WSSS_W, H8, H4, H4, DO_ADD) RVVCALL(OPIVV2, vwsub_wv_b, WOP_WSSS_B, H2, H1, H1, DO_SUB) RVVCALL(OPIVV2, vwsub_wv_h, WOP_WSSS_H, H4, H2, H2, DO_SUB) RVVCALL(OPIVV2, vwsub_wv_w, WOP_WSSS_W, H8, H4, H4, DO_SUB) -GEN_VEXT_VV(vwaddu_vv_b, 1, 2) -GEN_VEXT_VV(vwaddu_vv_h, 2, 4) -GEN_VEXT_VV(vwaddu_vv_w, 4, 8) -GEN_VEXT_VV(vwsubu_vv_b, 1, 2) -GEN_VEXT_VV(vwsubu_vv_h, 2, 4) -GEN_VEXT_VV(vwsubu_vv_w, 4, 8) -GEN_VEXT_VV(vwadd_vv_b, 1, 2) -GEN_VEXT_VV(vwadd_vv_h, 2, 4) -GEN_VEXT_VV(vwadd_vv_w, 4, 8) -GEN_VEXT_VV(vwsub_vv_b, 1, 2) -GEN_VEXT_VV(vwsub_vv_h, 2, 4) -GEN_VEXT_VV(vwsub_vv_w, 4, 8) -GEN_VEXT_VV(vwaddu_wv_b, 1, 2) -GEN_VEXT_VV(vwaddu_wv_h, 2, 4) -GEN_VEXT_VV(vwaddu_wv_w, 4, 8) -GEN_VEXT_VV(vwsubu_wv_b, 1, 2) -GEN_VEXT_VV(vwsubu_wv_h, 2, 4) -GEN_VEXT_VV(vwsubu_wv_w, 4, 8) -GEN_VEXT_VV(vwadd_wv_b, 1, 2) -GEN_VEXT_VV(vwadd_wv_h, 2, 4) -GEN_VEXT_VV(vwadd_wv_w, 4, 8) -GEN_VEXT_VV(vwsub_wv_b, 1, 2) -GEN_VEXT_VV(vwsub_wv_h, 2, 4) -GEN_VEXT_VV(vwsub_wv_w, 4, 8) +GEN_VEXT_VV(vwaddu_vv_b, 2) +GEN_VEXT_VV(vwaddu_vv_h, 4) +GEN_VEXT_VV(vwaddu_vv_w, 8) +GEN_VEXT_VV(vwsubu_vv_b, 2) +GEN_VEXT_VV(vwsubu_vv_h, 4) +GEN_VEXT_VV(vwsubu_vv_w, 8) +GEN_VEXT_VV(vwadd_vv_b, 2) +GEN_VEXT_VV(vwadd_vv_h, 4) +GEN_VEXT_VV(vwadd_vv_w, 8) +GEN_VEXT_VV(vwsub_vv_b, 2) +GEN_VEXT_VV(vwsub_vv_h, 4) +GEN_VEXT_VV(vwsub_vv_w, 8) +GEN_VEXT_VV(vwaddu_wv_b, 2) +GEN_VEXT_VV(vwaddu_wv_h, 4) +GEN_VEXT_VV(vwaddu_wv_w, 8) +GEN_VEXT_VV(vwsubu_wv_b, 2) +GEN_VEXT_VV(vwsubu_wv_h, 4) +GEN_VEXT_VV(vwsubu_wv_w, 8) +GEN_VEXT_VV(vwadd_wv_b, 2) +GEN_VEXT_VV(vwadd_wv_h, 4) +GEN_VEXT_VV(vwadd_wv_w, 8) +GEN_VEXT_VV(vwsub_wv_b, 2) +GEN_VEXT_VV(vwsub_wv_h, 4) +GEN_VEXT_VV(vwsub_wv_w, 8) RVVCALL(OPIVX2, vwaddu_vx_b, WOP_UUU_B, H2, H1, DO_ADD) RVVCALL(OPIVX2, vwaddu_vx_h, WOP_UUU_H, H4, H2, DO_ADD) @@ -938,30 +850,30 @@ RVVCALL(OPIVX2, vwadd_wx_w, WOP_WSSS_W, H8, H4, DO_ADD) RVVCALL(OPIVX2, vwsub_wx_b, WOP_WSSS_B, H2, H1, DO_SUB) RVVCALL(OPIVX2, vwsub_wx_h, WOP_WSSS_H, H4, H2, DO_SUB) RVVCALL(OPIVX2, vwsub_wx_w, WOP_WSSS_W, H8, H4, DO_SUB) -GEN_VEXT_VX(vwaddu_vx_b, 1, 2) -GEN_VEXT_VX(vwaddu_vx_h, 2, 4) -GEN_VEXT_VX(vwaddu_vx_w, 4, 8) -GEN_VEXT_VX(vwsubu_vx_b, 1, 2) -GEN_VEXT_VX(vwsubu_vx_h, 2, 4) -GEN_VEXT_VX(vwsubu_vx_w, 4, 8) -GEN_VEXT_VX(vwadd_vx_b, 1, 2) -GEN_VEXT_VX(vwadd_vx_h, 2, 4) -GEN_VEXT_VX(vwadd_vx_w, 4, 8) -GEN_VEXT_VX(vwsub_vx_b, 1, 2) -GEN_VEXT_VX(vwsub_vx_h, 2, 4) -GEN_VEXT_VX(vwsub_vx_w, 4, 8) -GEN_VEXT_VX(vwaddu_wx_b, 1, 2) -GEN_VEXT_VX(vwaddu_wx_h, 2, 4) -GEN_VEXT_VX(vwaddu_wx_w, 4, 8) -GEN_VEXT_VX(vwsubu_wx_b, 1, 2) -GEN_VEXT_VX(vwsubu_wx_h, 2, 4) -GEN_VEXT_VX(vwsubu_wx_w, 4, 8) -GEN_VEXT_VX(vwadd_wx_b, 1, 2) -GEN_VEXT_VX(vwadd_wx_h, 2, 4) -GEN_VEXT_VX(vwadd_wx_w, 4, 8) -GEN_VEXT_VX(vwsub_wx_b, 1, 2) -GEN_VEXT_VX(vwsub_wx_h, 2, 4) -GEN_VEXT_VX(vwsub_wx_w, 4, 8) +GEN_VEXT_VX(vwaddu_vx_b, 2) +GEN_VEXT_VX(vwaddu_vx_h, 4) +GEN_VEXT_VX(vwaddu_vx_w, 8) +GEN_VEXT_VX(vwsubu_vx_b, 2) +GEN_VEXT_VX(vwsubu_vx_h, 4) +GEN_VEXT_VX(vwsubu_vx_w, 8) +GEN_VEXT_VX(vwadd_vx_b, 2) +GEN_VEXT_VX(vwadd_vx_h, 4) +GEN_VEXT_VX(vwadd_vx_w, 8) +GEN_VEXT_VX(vwsub_vx_b, 2) +GEN_VEXT_VX(vwsub_vx_h, 4) +GEN_VEXT_VX(vwsub_vx_w, 8) +GEN_VEXT_VX(vwaddu_wx_b, 2) +GEN_VEXT_VX(vwaddu_wx_h, 4) +GEN_VEXT_VX(vwaddu_wx_w, 8) +GEN_VEXT_VX(vwsubu_wx_b, 2) +GEN_VEXT_VX(vwsubu_wx_h, 4) +GEN_VEXT_VX(vwsubu_wx_w, 8) +GEN_VEXT_VX(vwadd_wx_b, 2) +GEN_VEXT_VX(vwadd_wx_h, 4) +GEN_VEXT_VX(vwadd_wx_w, 8) +GEN_VEXT_VX(vwsub_wx_b, 2) +GEN_VEXT_VX(vwsub_wx_h, 4) +GEN_VEXT_VX(vwsub_wx_w, 8) /* Vector Integer Add-with-Carry / Subtract-with-Borrow Instructions */ #define DO_VADC(N, M, C) (N + M + C) @@ -972,8 +884,14 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ CPURISCVState *env, uint32_t desc) \ { \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = \ + vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ @@ -982,6 +900,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ *((ETYPE *)vd + H(i)) = DO_OP(s2, s1, carry); \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } GEN_VEXT_VADC_VVM(vadc_vvm_b, uint8_t, H1, DO_VADC) @@ -999,15 +919,22 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ CPURISCVState *env, uint32_t desc) \ { \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ ETYPE carry = vext_elem_mask(v0, i); \ \ *((ETYPE *)vd + H(i)) = DO_OP(s2, (ETYPE)(target_long)s1, carry);\ } \ - env->vstart = 0; \ + env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } GEN_VEXT_VADC_VXM(vadc_vxm_b, uint8_t, H1, DO_VADC) @@ -1030,8 +957,12 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ { \ uint32_t vl = env->vl; \ uint32_t vm = vext_vm(desc); \ + uint32_t total_elems = riscv_cpu_cfg(env)->vlenb << 3; \ + uint32_t vta_all_1s = vext_vta_all_1s(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ @@ -1039,6 +970,15 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ vext_set_elem_mask(vd, i, DO_OP(s2, s1, carry)); \ } \ env->vstart = 0; \ + /* + * mask destination register are always tail-agnostic + * set tail elements to 1s + */ \ + if (vta_all_1s) { \ + for (; i < total_elems; i++) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ + } \ } GEN_VEXT_VMADC_VVM(vmadc_vvm_b, uint8_t, H1, DO_MADC) @@ -1057,8 +997,12 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ { \ uint32_t vl = env->vl; \ uint32_t vm = vext_vm(desc); \ + uint32_t total_elems = riscv_cpu_cfg(env)->vlenb << 3; \ + uint32_t vta_all_1s = vext_vta_all_1s(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ ETYPE carry = !vm && vext_elem_mask(v0, i); \ @@ -1066,6 +1010,15 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ DO_OP(s2, (ETYPE)(target_long)s1, carry)); \ } \ env->vstart = 0; \ + /* + * mask destination register are always tail-agnostic + * set tail elements to 1s + */ \ + if (vta_all_1s) { \ + for (; i < total_elems; i++) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ + } \ } GEN_VEXT_VMADC_VXM(vmadc_vxm_b, uint8_t, H1, DO_MADC) @@ -1091,18 +1044,18 @@ RVVCALL(OPIVV2, vxor_vv_b, OP_SSS_B, H1, H1, H1, DO_XOR) RVVCALL(OPIVV2, vxor_vv_h, OP_SSS_H, H2, H2, H2, DO_XOR) RVVCALL(OPIVV2, vxor_vv_w, OP_SSS_W, H4, H4, H4, DO_XOR) RVVCALL(OPIVV2, vxor_vv_d, OP_SSS_D, H8, H8, H8, DO_XOR) -GEN_VEXT_VV(vand_vv_b, 1, 1) -GEN_VEXT_VV(vand_vv_h, 2, 2) -GEN_VEXT_VV(vand_vv_w, 4, 4) -GEN_VEXT_VV(vand_vv_d, 8, 8) -GEN_VEXT_VV(vor_vv_b, 1, 1) -GEN_VEXT_VV(vor_vv_h, 2, 2) -GEN_VEXT_VV(vor_vv_w, 4, 4) -GEN_VEXT_VV(vor_vv_d, 8, 8) -GEN_VEXT_VV(vxor_vv_b, 1, 1) -GEN_VEXT_VV(vxor_vv_h, 2, 2) -GEN_VEXT_VV(vxor_vv_w, 4, 4) -GEN_VEXT_VV(vxor_vv_d, 8, 8) +GEN_VEXT_VV(vand_vv_b, 1) +GEN_VEXT_VV(vand_vv_h, 2) +GEN_VEXT_VV(vand_vv_w, 4) +GEN_VEXT_VV(vand_vv_d, 8) +GEN_VEXT_VV(vor_vv_b, 1) +GEN_VEXT_VV(vor_vv_h, 2) +GEN_VEXT_VV(vor_vv_w, 4) +GEN_VEXT_VV(vor_vv_d, 8) +GEN_VEXT_VV(vxor_vv_b, 1) +GEN_VEXT_VV(vxor_vv_h, 2) +GEN_VEXT_VV(vxor_vv_w, 4) +GEN_VEXT_VV(vxor_vv_d, 8) RVVCALL(OPIVX2, vand_vx_b, OP_SSS_B, H1, H1, DO_AND) RVVCALL(OPIVX2, vand_vx_h, OP_SSS_H, H2, H2, DO_AND) @@ -1116,18 +1069,18 @@ RVVCALL(OPIVX2, vxor_vx_b, OP_SSS_B, H1, H1, DO_XOR) RVVCALL(OPIVX2, vxor_vx_h, OP_SSS_H, H2, H2, DO_XOR) RVVCALL(OPIVX2, vxor_vx_w, OP_SSS_W, H4, H4, DO_XOR) RVVCALL(OPIVX2, vxor_vx_d, OP_SSS_D, H8, H8, DO_XOR) -GEN_VEXT_VX(vand_vx_b, 1, 1) -GEN_VEXT_VX(vand_vx_h, 2, 2) -GEN_VEXT_VX(vand_vx_w, 4, 4) -GEN_VEXT_VX(vand_vx_d, 8, 8) -GEN_VEXT_VX(vor_vx_b, 1, 1) -GEN_VEXT_VX(vor_vx_h, 2, 2) -GEN_VEXT_VX(vor_vx_w, 4, 4) -GEN_VEXT_VX(vor_vx_d, 8, 8) -GEN_VEXT_VX(vxor_vx_b, 1, 1) -GEN_VEXT_VX(vxor_vx_h, 2, 2) -GEN_VEXT_VX(vxor_vx_w, 4, 4) -GEN_VEXT_VX(vxor_vx_d, 8, 8) +GEN_VEXT_VX(vand_vx_b, 1) +GEN_VEXT_VX(vand_vx_h, 2) +GEN_VEXT_VX(vand_vx_w, 4) +GEN_VEXT_VX(vand_vx_d, 8) +GEN_VEXT_VX(vor_vx_b, 1) +GEN_VEXT_VX(vor_vx_h, 2) +GEN_VEXT_VX(vor_vx_w, 4) +GEN_VEXT_VX(vor_vx_d, 8) +GEN_VEXT_VX(vxor_vx_b, 1) +GEN_VEXT_VX(vxor_vx_h, 2) +GEN_VEXT_VX(vxor_vx_w, 4) +GEN_VEXT_VX(vxor_vx_d, 8) /* Vector Single-Width Bit Shift Instructions */ #define DO_SLL(N, M) (N << (M)) @@ -1140,10 +1093,18 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(TS1); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ TS1 s1 = *((TS1 *)vs1 + HS1(i)); \ @@ -1151,6 +1112,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ *((TS1 *)vd + HS1(i)) = OP(s2, s1 & MASK); \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } GEN_VEXT_SHIFT_VV(vsll_vv_b, uint8_t, uint8_t, H1, H1, DO_SLL, 0x7) @@ -1168,23 +1131,38 @@ GEN_VEXT_SHIFT_VV(vsra_vv_h, uint16_t, int16_t, H2, H2, DO_SRL, 0xf) GEN_VEXT_SHIFT_VV(vsra_vv_w, uint32_t, int32_t, H4, H4, DO_SRL, 0x1f) GEN_VEXT_SHIFT_VV(vsra_vv_d, uint64_t, int64_t, H8, H8, DO_SRL, 0x3f) -/* generate the helpers for shift instructions with one vector and one scalar */ +/* + * generate the helpers for shift instructions with one vector and one scalar + */ #define GEN_VEXT_SHIFT_VX(NAME, TD, TS2, HD, HS2, OP, MASK) \ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ - void *vs2, CPURISCVState *env, uint32_t desc) \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(TD); \ + uint32_t total_elems = \ + vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, \ + (i + 1) * esz); \ continue; \ } \ TS2 s2 = *((TS2 *)vs2 + HS2(i)); \ *((TD *)vd + HD(i)) = OP(s2, s1 & MASK); \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz);\ } GEN_VEXT_SHIFT_VX(vsll_vx_b, uint8_t, int8_t, H1, H1, DO_SLL, 0x7) @@ -1229,17 +1207,35 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t total_elems = riscv_cpu_cfg(env)->vlenb << 3; \ + uint32_t vta_all_1s = vext_vta_all_1s(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + if (vma) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ continue; \ } \ vext_set_elem_mask(vd, i, DO_OP(s2, s1)); \ } \ env->vstart = 0; \ + /* + * mask destination register are always tail-agnostic + * set tail elements to 1s + */ \ + if (vta_all_1s) { \ + for (; i < total_elems; i++) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ + } \ } GEN_VEXT_CMP_VV(vmseq_vv_b, uint8_t, H1, DO_MSEQ) @@ -1278,17 +1274,35 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t total_elems = riscv_cpu_cfg(env)->vlenb << 3; \ + uint32_t vta_all_1s = vext_vta_all_1s(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + if (vma) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ continue; \ } \ vext_set_elem_mask(vd, i, \ DO_OP(s2, (ETYPE)(target_long)s1)); \ } \ env->vstart = 0; \ + /* + * mask destination register are always tail-agnostic + * set tail elements to 1s + */ \ + if (vta_all_1s) { \ + for (; i < total_elems; i++) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ + } \ } GEN_VEXT_CMP_VX(vmseq_vx_b, uint8_t, H1, DO_MSEQ) @@ -1348,22 +1362,22 @@ RVVCALL(OPIVV2, vmax_vv_b, OP_SSS_B, H1, H1, H1, DO_MAX) RVVCALL(OPIVV2, vmax_vv_h, OP_SSS_H, H2, H2, H2, DO_MAX) RVVCALL(OPIVV2, vmax_vv_w, OP_SSS_W, H4, H4, H4, DO_MAX) RVVCALL(OPIVV2, vmax_vv_d, OP_SSS_D, H8, H8, H8, DO_MAX) -GEN_VEXT_VV(vminu_vv_b, 1, 1) -GEN_VEXT_VV(vminu_vv_h, 2, 2) -GEN_VEXT_VV(vminu_vv_w, 4, 4) -GEN_VEXT_VV(vminu_vv_d, 8, 8) -GEN_VEXT_VV(vmin_vv_b, 1, 1) -GEN_VEXT_VV(vmin_vv_h, 2, 2) -GEN_VEXT_VV(vmin_vv_w, 4, 4) -GEN_VEXT_VV(vmin_vv_d, 8, 8) -GEN_VEXT_VV(vmaxu_vv_b, 1, 1) -GEN_VEXT_VV(vmaxu_vv_h, 2, 2) -GEN_VEXT_VV(vmaxu_vv_w, 4, 4) -GEN_VEXT_VV(vmaxu_vv_d, 8, 8) -GEN_VEXT_VV(vmax_vv_b, 1, 1) -GEN_VEXT_VV(vmax_vv_h, 2, 2) -GEN_VEXT_VV(vmax_vv_w, 4, 4) -GEN_VEXT_VV(vmax_vv_d, 8, 8) +GEN_VEXT_VV(vminu_vv_b, 1) +GEN_VEXT_VV(vminu_vv_h, 2) +GEN_VEXT_VV(vminu_vv_w, 4) +GEN_VEXT_VV(vminu_vv_d, 8) +GEN_VEXT_VV(vmin_vv_b, 1) +GEN_VEXT_VV(vmin_vv_h, 2) +GEN_VEXT_VV(vmin_vv_w, 4) +GEN_VEXT_VV(vmin_vv_d, 8) +GEN_VEXT_VV(vmaxu_vv_b, 1) +GEN_VEXT_VV(vmaxu_vv_h, 2) +GEN_VEXT_VV(vmaxu_vv_w, 4) +GEN_VEXT_VV(vmaxu_vv_d, 8) +GEN_VEXT_VV(vmax_vv_b, 1) +GEN_VEXT_VV(vmax_vv_h, 2) +GEN_VEXT_VV(vmax_vv_w, 4) +GEN_VEXT_VV(vmax_vv_d, 8) RVVCALL(OPIVX2, vminu_vx_b, OP_UUU_B, H1, H1, DO_MIN) RVVCALL(OPIVX2, vminu_vx_h, OP_UUU_H, H2, H2, DO_MIN) @@ -1381,22 +1395,22 @@ RVVCALL(OPIVX2, vmax_vx_b, OP_SSS_B, H1, H1, DO_MAX) RVVCALL(OPIVX2, vmax_vx_h, OP_SSS_H, H2, H2, DO_MAX) RVVCALL(OPIVX2, vmax_vx_w, OP_SSS_W, H4, H4, DO_MAX) RVVCALL(OPIVX2, vmax_vx_d, OP_SSS_D, H8, H8, DO_MAX) -GEN_VEXT_VX(vminu_vx_b, 1, 1) -GEN_VEXT_VX(vminu_vx_h, 2, 2) -GEN_VEXT_VX(vminu_vx_w, 4, 4) -GEN_VEXT_VX(vminu_vx_d, 8, 8) -GEN_VEXT_VX(vmin_vx_b, 1, 1) -GEN_VEXT_VX(vmin_vx_h, 2, 2) -GEN_VEXT_VX(vmin_vx_w, 4, 4) -GEN_VEXT_VX(vmin_vx_d, 8, 8) -GEN_VEXT_VX(vmaxu_vx_b, 1, 1) -GEN_VEXT_VX(vmaxu_vx_h, 2, 2) -GEN_VEXT_VX(vmaxu_vx_w, 4, 4) -GEN_VEXT_VX(vmaxu_vx_d, 8, 8) -GEN_VEXT_VX(vmax_vx_b, 1, 1) -GEN_VEXT_VX(vmax_vx_h, 2, 2) -GEN_VEXT_VX(vmax_vx_w, 4, 4) -GEN_VEXT_VX(vmax_vx_d, 8, 8) +GEN_VEXT_VX(vminu_vx_b, 1) +GEN_VEXT_VX(vminu_vx_h, 2) +GEN_VEXT_VX(vminu_vx_w, 4) +GEN_VEXT_VX(vminu_vx_d, 8) +GEN_VEXT_VX(vmin_vx_b, 1) +GEN_VEXT_VX(vmin_vx_h, 2) +GEN_VEXT_VX(vmin_vx_w, 4) +GEN_VEXT_VX(vmin_vx_d, 8) +GEN_VEXT_VX(vmaxu_vx_b, 1) +GEN_VEXT_VX(vmaxu_vx_h, 2) +GEN_VEXT_VX(vmaxu_vx_w, 4) +GEN_VEXT_VX(vmaxu_vx_d, 8) +GEN_VEXT_VX(vmax_vx_b, 1) +GEN_VEXT_VX(vmax_vx_h, 2) +GEN_VEXT_VX(vmax_vx_w, 4) +GEN_VEXT_VX(vmax_vx_d, 8) /* Vector Single-Width Integer Multiply Instructions */ #define DO_MUL(N, M) (N * M) @@ -1404,10 +1418,10 @@ RVVCALL(OPIVV2, vmul_vv_b, OP_SSS_B, H1, H1, H1, DO_MUL) RVVCALL(OPIVV2, vmul_vv_h, OP_SSS_H, H2, H2, H2, DO_MUL) RVVCALL(OPIVV2, vmul_vv_w, OP_SSS_W, H4, H4, H4, DO_MUL) RVVCALL(OPIVV2, vmul_vv_d, OP_SSS_D, H8, H8, H8, DO_MUL) -GEN_VEXT_VV(vmul_vv_b, 1, 1) -GEN_VEXT_VV(vmul_vv_h, 2, 2) -GEN_VEXT_VV(vmul_vv_w, 4, 4) -GEN_VEXT_VV(vmul_vv_d, 8, 8) +GEN_VEXT_VV(vmul_vv_b, 1) +GEN_VEXT_VV(vmul_vv_h, 2) +GEN_VEXT_VV(vmul_vv_w, 4) +GEN_VEXT_VV(vmul_vv_d, 8) static int8_t do_mulh_b(int8_t s2, int8_t s1) { @@ -1511,18 +1525,18 @@ RVVCALL(OPIVV2, vmulhsu_vv_b, OP_SUS_B, H1, H1, H1, do_mulhsu_b) RVVCALL(OPIVV2, vmulhsu_vv_h, OP_SUS_H, H2, H2, H2, do_mulhsu_h) RVVCALL(OPIVV2, vmulhsu_vv_w, OP_SUS_W, H4, H4, H4, do_mulhsu_w) RVVCALL(OPIVV2, vmulhsu_vv_d, OP_SUS_D, H8, H8, H8, do_mulhsu_d) -GEN_VEXT_VV(vmulh_vv_b, 1, 1) -GEN_VEXT_VV(vmulh_vv_h, 2, 2) -GEN_VEXT_VV(vmulh_vv_w, 4, 4) -GEN_VEXT_VV(vmulh_vv_d, 8, 8) -GEN_VEXT_VV(vmulhu_vv_b, 1, 1) -GEN_VEXT_VV(vmulhu_vv_h, 2, 2) -GEN_VEXT_VV(vmulhu_vv_w, 4, 4) -GEN_VEXT_VV(vmulhu_vv_d, 8, 8) -GEN_VEXT_VV(vmulhsu_vv_b, 1, 1) -GEN_VEXT_VV(vmulhsu_vv_h, 2, 2) -GEN_VEXT_VV(vmulhsu_vv_w, 4, 4) -GEN_VEXT_VV(vmulhsu_vv_d, 8, 8) +GEN_VEXT_VV(vmulh_vv_b, 1) +GEN_VEXT_VV(vmulh_vv_h, 2) +GEN_VEXT_VV(vmulh_vv_w, 4) +GEN_VEXT_VV(vmulh_vv_d, 8) +GEN_VEXT_VV(vmulhu_vv_b, 1) +GEN_VEXT_VV(vmulhu_vv_h, 2) +GEN_VEXT_VV(vmulhu_vv_w, 4) +GEN_VEXT_VV(vmulhu_vv_d, 8) +GEN_VEXT_VV(vmulhsu_vv_b, 1) +GEN_VEXT_VV(vmulhsu_vv_h, 2) +GEN_VEXT_VV(vmulhsu_vv_w, 4) +GEN_VEXT_VV(vmulhsu_vv_d, 8) RVVCALL(OPIVX2, vmul_vx_b, OP_SSS_B, H1, H1, DO_MUL) RVVCALL(OPIVX2, vmul_vx_h, OP_SSS_H, H2, H2, DO_MUL) @@ -1540,29 +1554,29 @@ RVVCALL(OPIVX2, vmulhsu_vx_b, OP_SUS_B, H1, H1, do_mulhsu_b) RVVCALL(OPIVX2, vmulhsu_vx_h, OP_SUS_H, H2, H2, do_mulhsu_h) RVVCALL(OPIVX2, vmulhsu_vx_w, OP_SUS_W, H4, H4, do_mulhsu_w) RVVCALL(OPIVX2, vmulhsu_vx_d, OP_SUS_D, H8, H8, do_mulhsu_d) -GEN_VEXT_VX(vmul_vx_b, 1, 1) -GEN_VEXT_VX(vmul_vx_h, 2, 2) -GEN_VEXT_VX(vmul_vx_w, 4, 4) -GEN_VEXT_VX(vmul_vx_d, 8, 8) -GEN_VEXT_VX(vmulh_vx_b, 1, 1) -GEN_VEXT_VX(vmulh_vx_h, 2, 2) -GEN_VEXT_VX(vmulh_vx_w, 4, 4) -GEN_VEXT_VX(vmulh_vx_d, 8, 8) -GEN_VEXT_VX(vmulhu_vx_b, 1, 1) -GEN_VEXT_VX(vmulhu_vx_h, 2, 2) -GEN_VEXT_VX(vmulhu_vx_w, 4, 4) -GEN_VEXT_VX(vmulhu_vx_d, 8, 8) -GEN_VEXT_VX(vmulhsu_vx_b, 1, 1) -GEN_VEXT_VX(vmulhsu_vx_h, 2, 2) -GEN_VEXT_VX(vmulhsu_vx_w, 4, 4) -GEN_VEXT_VX(vmulhsu_vx_d, 8, 8) +GEN_VEXT_VX(vmul_vx_b, 1) +GEN_VEXT_VX(vmul_vx_h, 2) +GEN_VEXT_VX(vmul_vx_w, 4) +GEN_VEXT_VX(vmul_vx_d, 8) +GEN_VEXT_VX(vmulh_vx_b, 1) +GEN_VEXT_VX(vmulh_vx_h, 2) +GEN_VEXT_VX(vmulh_vx_w, 4) +GEN_VEXT_VX(vmulh_vx_d, 8) +GEN_VEXT_VX(vmulhu_vx_b, 1) +GEN_VEXT_VX(vmulhu_vx_h, 2) +GEN_VEXT_VX(vmulhu_vx_w, 4) +GEN_VEXT_VX(vmulhu_vx_d, 8) +GEN_VEXT_VX(vmulhsu_vx_b, 1) +GEN_VEXT_VX(vmulhsu_vx_h, 2) +GEN_VEXT_VX(vmulhsu_vx_w, 4) +GEN_VEXT_VX(vmulhsu_vx_d, 8) /* Vector Integer Divide Instructions */ #define DO_DIVU(N, M) (unlikely(M == 0) ? (__typeof(N))(-1) : N / M) #define DO_REMU(N, M) (unlikely(M == 0) ? N : N % M) -#define DO_DIV(N, M) (unlikely(M == 0) ? (__typeof(N))(-1) :\ +#define DO_DIV(N, M) (unlikely(M == 0) ? (__typeof(N))(-1) : \ unlikely((N == -N) && (M == (__typeof(N))(-1))) ? N : N / M) -#define DO_REM(N, M) (unlikely(M == 0) ? N :\ +#define DO_REM(N, M) (unlikely(M == 0) ? N : \ unlikely((N == -N) && (M == (__typeof(N))(-1))) ? 0 : N % M) RVVCALL(OPIVV2, vdivu_vv_b, OP_UUU_B, H1, H1, H1, DO_DIVU) @@ -1581,22 +1595,22 @@ RVVCALL(OPIVV2, vrem_vv_b, OP_SSS_B, H1, H1, H1, DO_REM) RVVCALL(OPIVV2, vrem_vv_h, OP_SSS_H, H2, H2, H2, DO_REM) RVVCALL(OPIVV2, vrem_vv_w, OP_SSS_W, H4, H4, H4, DO_REM) RVVCALL(OPIVV2, vrem_vv_d, OP_SSS_D, H8, H8, H8, DO_REM) -GEN_VEXT_VV(vdivu_vv_b, 1, 1) -GEN_VEXT_VV(vdivu_vv_h, 2, 2) -GEN_VEXT_VV(vdivu_vv_w, 4, 4) -GEN_VEXT_VV(vdivu_vv_d, 8, 8) -GEN_VEXT_VV(vdiv_vv_b, 1, 1) -GEN_VEXT_VV(vdiv_vv_h, 2, 2) -GEN_VEXT_VV(vdiv_vv_w, 4, 4) -GEN_VEXT_VV(vdiv_vv_d, 8, 8) -GEN_VEXT_VV(vremu_vv_b, 1, 1) -GEN_VEXT_VV(vremu_vv_h, 2, 2) -GEN_VEXT_VV(vremu_vv_w, 4, 4) -GEN_VEXT_VV(vremu_vv_d, 8, 8) -GEN_VEXT_VV(vrem_vv_b, 1, 1) -GEN_VEXT_VV(vrem_vv_h, 2, 2) -GEN_VEXT_VV(vrem_vv_w, 4, 4) -GEN_VEXT_VV(vrem_vv_d, 8, 8) +GEN_VEXT_VV(vdivu_vv_b, 1) +GEN_VEXT_VV(vdivu_vv_h, 2) +GEN_VEXT_VV(vdivu_vv_w, 4) +GEN_VEXT_VV(vdivu_vv_d, 8) +GEN_VEXT_VV(vdiv_vv_b, 1) +GEN_VEXT_VV(vdiv_vv_h, 2) +GEN_VEXT_VV(vdiv_vv_w, 4) +GEN_VEXT_VV(vdiv_vv_d, 8) +GEN_VEXT_VV(vremu_vv_b, 1) +GEN_VEXT_VV(vremu_vv_h, 2) +GEN_VEXT_VV(vremu_vv_w, 4) +GEN_VEXT_VV(vremu_vv_d, 8) +GEN_VEXT_VV(vrem_vv_b, 1) +GEN_VEXT_VV(vrem_vv_h, 2) +GEN_VEXT_VV(vrem_vv_w, 4) +GEN_VEXT_VV(vrem_vv_d, 8) RVVCALL(OPIVX2, vdivu_vx_b, OP_UUU_B, H1, H1, DO_DIVU) RVVCALL(OPIVX2, vdivu_vx_h, OP_UUU_H, H2, H2, DO_DIVU) @@ -1614,22 +1628,22 @@ RVVCALL(OPIVX2, vrem_vx_b, OP_SSS_B, H1, H1, DO_REM) RVVCALL(OPIVX2, vrem_vx_h, OP_SSS_H, H2, H2, DO_REM) RVVCALL(OPIVX2, vrem_vx_w, OP_SSS_W, H4, H4, DO_REM) RVVCALL(OPIVX2, vrem_vx_d, OP_SSS_D, H8, H8, DO_REM) -GEN_VEXT_VX(vdivu_vx_b, 1, 1) -GEN_VEXT_VX(vdivu_vx_h, 2, 2) -GEN_VEXT_VX(vdivu_vx_w, 4, 4) -GEN_VEXT_VX(vdivu_vx_d, 8, 8) -GEN_VEXT_VX(vdiv_vx_b, 1, 1) -GEN_VEXT_VX(vdiv_vx_h, 2, 2) -GEN_VEXT_VX(vdiv_vx_w, 4, 4) -GEN_VEXT_VX(vdiv_vx_d, 8, 8) -GEN_VEXT_VX(vremu_vx_b, 1, 1) -GEN_VEXT_VX(vremu_vx_h, 2, 2) -GEN_VEXT_VX(vremu_vx_w, 4, 4) -GEN_VEXT_VX(vremu_vx_d, 8, 8) -GEN_VEXT_VX(vrem_vx_b, 1, 1) -GEN_VEXT_VX(vrem_vx_h, 2, 2) -GEN_VEXT_VX(vrem_vx_w, 4, 4) -GEN_VEXT_VX(vrem_vx_d, 8, 8) +GEN_VEXT_VX(vdivu_vx_b, 1) +GEN_VEXT_VX(vdivu_vx_h, 2) +GEN_VEXT_VX(vdivu_vx_w, 4) +GEN_VEXT_VX(vdivu_vx_d, 8) +GEN_VEXT_VX(vdiv_vx_b, 1) +GEN_VEXT_VX(vdiv_vx_h, 2) +GEN_VEXT_VX(vdiv_vx_w, 4) +GEN_VEXT_VX(vdiv_vx_d, 8) +GEN_VEXT_VX(vremu_vx_b, 1) +GEN_VEXT_VX(vremu_vx_h, 2) +GEN_VEXT_VX(vremu_vx_w, 4) +GEN_VEXT_VX(vremu_vx_d, 8) +GEN_VEXT_VX(vrem_vx_b, 1) +GEN_VEXT_VX(vrem_vx_h, 2) +GEN_VEXT_VX(vrem_vx_w, 4) +GEN_VEXT_VX(vrem_vx_d, 8) /* Vector Widening Integer Multiply Instructions */ RVVCALL(OPIVV2, vwmul_vv_b, WOP_SSS_B, H2, H1, H1, DO_MUL) @@ -1641,15 +1655,15 @@ RVVCALL(OPIVV2, vwmulu_vv_w, WOP_UUU_W, H8, H4, H4, DO_MUL) RVVCALL(OPIVV2, vwmulsu_vv_b, WOP_SUS_B, H2, H1, H1, DO_MUL) RVVCALL(OPIVV2, vwmulsu_vv_h, WOP_SUS_H, H4, H2, H2, DO_MUL) RVVCALL(OPIVV2, vwmulsu_vv_w, WOP_SUS_W, H8, H4, H4, DO_MUL) -GEN_VEXT_VV(vwmul_vv_b, 1, 2) -GEN_VEXT_VV(vwmul_vv_h, 2, 4) -GEN_VEXT_VV(vwmul_vv_w, 4, 8) -GEN_VEXT_VV(vwmulu_vv_b, 1, 2) -GEN_VEXT_VV(vwmulu_vv_h, 2, 4) -GEN_VEXT_VV(vwmulu_vv_w, 4, 8) -GEN_VEXT_VV(vwmulsu_vv_b, 1, 2) -GEN_VEXT_VV(vwmulsu_vv_h, 2, 4) -GEN_VEXT_VV(vwmulsu_vv_w, 4, 8) +GEN_VEXT_VV(vwmul_vv_b, 2) +GEN_VEXT_VV(vwmul_vv_h, 4) +GEN_VEXT_VV(vwmul_vv_w, 8) +GEN_VEXT_VV(vwmulu_vv_b, 2) +GEN_VEXT_VV(vwmulu_vv_h, 4) +GEN_VEXT_VV(vwmulu_vv_w, 8) +GEN_VEXT_VV(vwmulsu_vv_b, 2) +GEN_VEXT_VV(vwmulsu_vv_h, 4) +GEN_VEXT_VV(vwmulsu_vv_w, 8) RVVCALL(OPIVX2, vwmul_vx_b, WOP_SSS_B, H2, H1, DO_MUL) RVVCALL(OPIVX2, vwmul_vx_h, WOP_SSS_H, H4, H2, DO_MUL) @@ -1660,18 +1674,18 @@ RVVCALL(OPIVX2, vwmulu_vx_w, WOP_UUU_W, H8, H4, DO_MUL) RVVCALL(OPIVX2, vwmulsu_vx_b, WOP_SUS_B, H2, H1, DO_MUL) RVVCALL(OPIVX2, vwmulsu_vx_h, WOP_SUS_H, H4, H2, DO_MUL) RVVCALL(OPIVX2, vwmulsu_vx_w, WOP_SUS_W, H8, H4, DO_MUL) -GEN_VEXT_VX(vwmul_vx_b, 1, 2) -GEN_VEXT_VX(vwmul_vx_h, 2, 4) -GEN_VEXT_VX(vwmul_vx_w, 4, 8) -GEN_VEXT_VX(vwmulu_vx_b, 1, 2) -GEN_VEXT_VX(vwmulu_vx_h, 2, 4) -GEN_VEXT_VX(vwmulu_vx_w, 4, 8) -GEN_VEXT_VX(vwmulsu_vx_b, 1, 2) -GEN_VEXT_VX(vwmulsu_vx_h, 2, 4) -GEN_VEXT_VX(vwmulsu_vx_w, 4, 8) +GEN_VEXT_VX(vwmul_vx_b, 2) +GEN_VEXT_VX(vwmul_vx_h, 4) +GEN_VEXT_VX(vwmul_vx_w, 8) +GEN_VEXT_VX(vwmulu_vx_b, 2) +GEN_VEXT_VX(vwmulu_vx_h, 4) +GEN_VEXT_VX(vwmulu_vx_w, 8) +GEN_VEXT_VX(vwmulsu_vx_b, 2) +GEN_VEXT_VX(vwmulsu_vx_h, 4) +GEN_VEXT_VX(vwmulsu_vx_w, 8) /* Vector Single-Width Integer Multiply-Add Instructions */ -#define OPIVV3(NAME, TD, T1, T2, TX1, TX2, HD, HS1, HS2, OP) \ +#define OPIVV3(NAME, TD, T1, T2, TX1, TX2, HD, HS1, HS2, OP) \ static void do_##NAME(void *vd, void *vs1, void *vs2, int i) \ { \ TX1 s1 = *((T1 *)vs1 + HS1(i)); \ @@ -1700,22 +1714,22 @@ RVVCALL(OPIVV3, vnmsub_vv_b, OP_SSS_B, H1, H1, H1, DO_NMSUB) RVVCALL(OPIVV3, vnmsub_vv_h, OP_SSS_H, H2, H2, H2, DO_NMSUB) RVVCALL(OPIVV3, vnmsub_vv_w, OP_SSS_W, H4, H4, H4, DO_NMSUB) RVVCALL(OPIVV3, vnmsub_vv_d, OP_SSS_D, H8, H8, H8, DO_NMSUB) -GEN_VEXT_VV(vmacc_vv_b, 1, 1) -GEN_VEXT_VV(vmacc_vv_h, 2, 2) -GEN_VEXT_VV(vmacc_vv_w, 4, 4) -GEN_VEXT_VV(vmacc_vv_d, 8, 8) -GEN_VEXT_VV(vnmsac_vv_b, 1, 1) -GEN_VEXT_VV(vnmsac_vv_h, 2, 2) -GEN_VEXT_VV(vnmsac_vv_w, 4, 4) -GEN_VEXT_VV(vnmsac_vv_d, 8, 8) -GEN_VEXT_VV(vmadd_vv_b, 1, 1) -GEN_VEXT_VV(vmadd_vv_h, 2, 2) -GEN_VEXT_VV(vmadd_vv_w, 4, 4) -GEN_VEXT_VV(vmadd_vv_d, 8, 8) -GEN_VEXT_VV(vnmsub_vv_b, 1, 1) -GEN_VEXT_VV(vnmsub_vv_h, 2, 2) -GEN_VEXT_VV(vnmsub_vv_w, 4, 4) -GEN_VEXT_VV(vnmsub_vv_d, 8, 8) +GEN_VEXT_VV(vmacc_vv_b, 1) +GEN_VEXT_VV(vmacc_vv_h, 2) +GEN_VEXT_VV(vmacc_vv_w, 4) +GEN_VEXT_VV(vmacc_vv_d, 8) +GEN_VEXT_VV(vnmsac_vv_b, 1) +GEN_VEXT_VV(vnmsac_vv_h, 2) +GEN_VEXT_VV(vnmsac_vv_w, 4) +GEN_VEXT_VV(vnmsac_vv_d, 8) +GEN_VEXT_VV(vmadd_vv_b, 1) +GEN_VEXT_VV(vmadd_vv_h, 2) +GEN_VEXT_VV(vmadd_vv_w, 4) +GEN_VEXT_VV(vmadd_vv_d, 8) +GEN_VEXT_VV(vnmsub_vv_b, 1) +GEN_VEXT_VV(vnmsub_vv_h, 2) +GEN_VEXT_VV(vnmsub_vv_w, 4) +GEN_VEXT_VV(vnmsub_vv_d, 8) #define OPIVX3(NAME, TD, T1, T2, TX1, TX2, HD, HS2, OP) \ static void do_##NAME(void *vd, target_long s1, void *vs2, int i) \ @@ -1741,22 +1755,22 @@ RVVCALL(OPIVX3, vnmsub_vx_b, OP_SSS_B, H1, H1, DO_NMSUB) RVVCALL(OPIVX3, vnmsub_vx_h, OP_SSS_H, H2, H2, DO_NMSUB) RVVCALL(OPIVX3, vnmsub_vx_w, OP_SSS_W, H4, H4, DO_NMSUB) RVVCALL(OPIVX3, vnmsub_vx_d, OP_SSS_D, H8, H8, DO_NMSUB) -GEN_VEXT_VX(vmacc_vx_b, 1, 1) -GEN_VEXT_VX(vmacc_vx_h, 2, 2) -GEN_VEXT_VX(vmacc_vx_w, 4, 4) -GEN_VEXT_VX(vmacc_vx_d, 8, 8) -GEN_VEXT_VX(vnmsac_vx_b, 1, 1) -GEN_VEXT_VX(vnmsac_vx_h, 2, 2) -GEN_VEXT_VX(vnmsac_vx_w, 4, 4) -GEN_VEXT_VX(vnmsac_vx_d, 8, 8) -GEN_VEXT_VX(vmadd_vx_b, 1, 1) -GEN_VEXT_VX(vmadd_vx_h, 2, 2) -GEN_VEXT_VX(vmadd_vx_w, 4, 4) -GEN_VEXT_VX(vmadd_vx_d, 8, 8) -GEN_VEXT_VX(vnmsub_vx_b, 1, 1) -GEN_VEXT_VX(vnmsub_vx_h, 2, 2) -GEN_VEXT_VX(vnmsub_vx_w, 4, 4) -GEN_VEXT_VX(vnmsub_vx_d, 8, 8) +GEN_VEXT_VX(vmacc_vx_b, 1) +GEN_VEXT_VX(vmacc_vx_h, 2) +GEN_VEXT_VX(vmacc_vx_w, 4) +GEN_VEXT_VX(vmacc_vx_d, 8) +GEN_VEXT_VX(vnmsac_vx_b, 1) +GEN_VEXT_VX(vnmsac_vx_h, 2) +GEN_VEXT_VX(vnmsac_vx_w, 4) +GEN_VEXT_VX(vnmsac_vx_d, 8) +GEN_VEXT_VX(vmadd_vx_b, 1) +GEN_VEXT_VX(vmadd_vx_h, 2) +GEN_VEXT_VX(vmadd_vx_w, 4) +GEN_VEXT_VX(vmadd_vx_d, 8) +GEN_VEXT_VX(vnmsub_vx_b, 1) +GEN_VEXT_VX(vnmsub_vx_h, 2) +GEN_VEXT_VX(vnmsub_vx_w, 4) +GEN_VEXT_VX(vnmsub_vx_d, 8) /* Vector Widening Integer Multiply-Add Instructions */ RVVCALL(OPIVV3, vwmaccu_vv_b, WOP_UUU_B, H2, H1, H1, DO_MACC) @@ -1768,15 +1782,15 @@ RVVCALL(OPIVV3, vwmacc_vv_w, WOP_SSS_W, H8, H4, H4, DO_MACC) RVVCALL(OPIVV3, vwmaccsu_vv_b, WOP_SSU_B, H2, H1, H1, DO_MACC) RVVCALL(OPIVV3, vwmaccsu_vv_h, WOP_SSU_H, H4, H2, H2, DO_MACC) RVVCALL(OPIVV3, vwmaccsu_vv_w, WOP_SSU_W, H8, H4, H4, DO_MACC) -GEN_VEXT_VV(vwmaccu_vv_b, 1, 2) -GEN_VEXT_VV(vwmaccu_vv_h, 2, 4) -GEN_VEXT_VV(vwmaccu_vv_w, 4, 8) -GEN_VEXT_VV(vwmacc_vv_b, 1, 2) -GEN_VEXT_VV(vwmacc_vv_h, 2, 4) -GEN_VEXT_VV(vwmacc_vv_w, 4, 8) -GEN_VEXT_VV(vwmaccsu_vv_b, 1, 2) -GEN_VEXT_VV(vwmaccsu_vv_h, 2, 4) -GEN_VEXT_VV(vwmaccsu_vv_w, 4, 8) +GEN_VEXT_VV(vwmaccu_vv_b, 2) +GEN_VEXT_VV(vwmaccu_vv_h, 4) +GEN_VEXT_VV(vwmaccu_vv_w, 8) +GEN_VEXT_VV(vwmacc_vv_b, 2) +GEN_VEXT_VV(vwmacc_vv_h, 4) +GEN_VEXT_VV(vwmacc_vv_w, 8) +GEN_VEXT_VV(vwmaccsu_vv_b, 2) +GEN_VEXT_VV(vwmaccsu_vv_h, 4) +GEN_VEXT_VV(vwmaccsu_vv_w, 8) RVVCALL(OPIVX3, vwmaccu_vx_b, WOP_UUU_B, H2, H1, DO_MACC) RVVCALL(OPIVX3, vwmaccu_vx_h, WOP_UUU_H, H4, H2, DO_MACC) @@ -1790,18 +1804,18 @@ RVVCALL(OPIVX3, vwmaccsu_vx_w, WOP_SSU_W, H8, H4, DO_MACC) RVVCALL(OPIVX3, vwmaccus_vx_b, WOP_SUS_B, H2, H1, DO_MACC) RVVCALL(OPIVX3, vwmaccus_vx_h, WOP_SUS_H, H4, H2, DO_MACC) RVVCALL(OPIVX3, vwmaccus_vx_w, WOP_SUS_W, H8, H4, DO_MACC) -GEN_VEXT_VX(vwmaccu_vx_b, 1, 2) -GEN_VEXT_VX(vwmaccu_vx_h, 2, 4) -GEN_VEXT_VX(vwmaccu_vx_w, 4, 8) -GEN_VEXT_VX(vwmacc_vx_b, 1, 2) -GEN_VEXT_VX(vwmacc_vx_h, 2, 4) -GEN_VEXT_VX(vwmacc_vx_w, 4, 8) -GEN_VEXT_VX(vwmaccsu_vx_b, 1, 2) -GEN_VEXT_VX(vwmaccsu_vx_h, 2, 4) -GEN_VEXT_VX(vwmaccsu_vx_w, 4, 8) -GEN_VEXT_VX(vwmaccus_vx_b, 1, 2) -GEN_VEXT_VX(vwmaccus_vx_h, 2, 4) -GEN_VEXT_VX(vwmaccus_vx_w, 4, 8) +GEN_VEXT_VX(vwmaccu_vx_b, 2) +GEN_VEXT_VX(vwmaccu_vx_h, 4) +GEN_VEXT_VX(vwmaccu_vx_w, 8) +GEN_VEXT_VX(vwmacc_vx_b, 2) +GEN_VEXT_VX(vwmacc_vx_h, 4) +GEN_VEXT_VX(vwmacc_vx_w, 8) +GEN_VEXT_VX(vwmaccsu_vx_b, 2) +GEN_VEXT_VX(vwmaccsu_vx_h, 4) +GEN_VEXT_VX(vwmaccsu_vx_w, 8) +GEN_VEXT_VX(vwmaccus_vx_b, 2) +GEN_VEXT_VX(vwmaccus_vx_h, 4) +GEN_VEXT_VX(vwmaccus_vx_w, 8) /* Vector Integer Merge and Move Instructions */ #define GEN_VEXT_VMV_VV(NAME, ETYPE, H) \ @@ -1809,13 +1823,20 @@ void HELPER(NAME)(void *vd, void *vs1, CPURISCVState *env, \ uint32_t desc) \ { \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ *((ETYPE *)vd + H(i)) = s1; \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } GEN_VEXT_VMV_VV(vmv_v_v_b, int8_t, H1) @@ -1828,12 +1849,19 @@ void HELPER(NAME)(void *vd, uint64_t s1, CPURISCVState *env, \ uint32_t desc) \ { \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ *((ETYPE *)vd + H(i)) = (ETYPE)s1; \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } GEN_VEXT_VMV_VX(vmv_v_x_b, int8_t, H1) @@ -1846,13 +1874,20 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ CPURISCVState *env, uint32_t desc) \ { \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE *vt = (!vext_elem_mask(v0, i) ? vs2 : vs1); \ *((ETYPE *)vd + H(i)) = *(vt + H(i)); \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } GEN_VEXT_VMERGE_VV(vmerge_vvm_b, int8_t, H1) @@ -1865,8 +1900,13 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ void *vs2, CPURISCVState *env, uint32_t desc) \ { \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ ETYPE d = (!vext_elem_mask(v0, i) ? s2 : \ @@ -1874,6 +1914,8 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ *((ETYPE *)vd + H(i)) = d; \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } GEN_VEXT_VMERGE_VX(vmerge_vxm_b, int8_t, H1) @@ -1882,7 +1924,7 @@ GEN_VEXT_VMERGE_VX(vmerge_vxm_w, int32_t, H4) GEN_VEXT_VMERGE_VX(vmerge_vxm_d, int64_t, H8) /* - *** Vector Fixed-Point Arithmetic Instructions + * Vector Fixed-Point Arithmetic Instructions */ /* Vector Single-Width Saturating Add and Subtract */ @@ -1908,10 +1950,14 @@ static inline void vext_vv_rm_1(void *vd, void *v0, void *vs1, void *vs2, CPURISCVState *env, uint32_t vl, uint32_t vm, int vxrm, - opivv2_rm_fn *fn) + opivv2_rm_fn *fn, uint32_t vma, uint32_t esz) { + VSTART_CHECK_EARLY_EXIT(env); + for (uint32_t i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); continue; } fn(vd, vs1, vs2, i, env, vxrm); @@ -1922,42 +1968,48 @@ vext_vv_rm_1(void *vd, void *v0, void *vs1, void *vs2, static inline void vext_vv_rm_2(void *vd, void *v0, void *vs1, void *vs2, CPURISCVState *env, - uint32_t desc, uint32_t esz, uint32_t dsz, - opivv2_rm_fn *fn) + uint32_t desc, + opivv2_rm_fn *fn, uint32_t esz) { uint32_t vm = vext_vm(desc); uint32_t vl = env->vl; + uint32_t total_elems = vext_get_total_elems(env, desc, esz); + uint32_t vta = vext_vta(desc); + uint32_t vma = vext_vma(desc); switch (env->vxrm) { case 0: /* rnu */ vext_vv_rm_1(vd, v0, vs1, vs2, - env, vl, vm, 0, fn); + env, vl, vm, 0, fn, vma, esz); break; case 1: /* rne */ vext_vv_rm_1(vd, v0, vs1, vs2, - env, vl, vm, 1, fn); + env, vl, vm, 1, fn, vma, esz); break; case 2: /* rdn */ vext_vv_rm_1(vd, v0, vs1, vs2, - env, vl, vm, 2, fn); + env, vl, vm, 2, fn, vma, esz); break; default: /* rod */ vext_vv_rm_1(vd, v0, vs1, vs2, - env, vl, vm, 3, fn); + env, vl, vm, 3, fn, vma, esz); break; } + /* set tail elements to 1s */ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); } /* generate helpers for fixed point instructions with OPIVV format */ -#define GEN_VEXT_VV_RM(NAME, ESZ, DSZ) \ +#define GEN_VEXT_VV_RM(NAME, ESZ) \ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ CPURISCVState *env, uint32_t desc) \ { \ - vext_vv_rm_2(vd, v0, vs1, vs2, env, desc, ESZ, DSZ, \ - do_##NAME); \ + vext_vv_rm_2(vd, v0, vs1, vs2, env, desc, \ + do_##NAME, ESZ); \ } -static inline uint8_t saddu8(CPURISCVState *env, int vxrm, uint8_t a, uint8_t b) +static inline uint8_t saddu8(CPURISCVState *env, int vxrm, uint8_t a, + uint8_t b) { uint8_t res = a + b; if (res < a) { @@ -2004,10 +2056,10 @@ RVVCALL(OPIVV2_RM, vsaddu_vv_b, OP_UUU_B, H1, H1, H1, saddu8) RVVCALL(OPIVV2_RM, vsaddu_vv_h, OP_UUU_H, H2, H2, H2, saddu16) RVVCALL(OPIVV2_RM, vsaddu_vv_w, OP_UUU_W, H4, H4, H4, saddu32) RVVCALL(OPIVV2_RM, vsaddu_vv_d, OP_UUU_D, H8, H8, H8, saddu64) -GEN_VEXT_VV_RM(vsaddu_vv_b, 1, 1) -GEN_VEXT_VV_RM(vsaddu_vv_h, 2, 2) -GEN_VEXT_VV_RM(vsaddu_vv_w, 4, 4) -GEN_VEXT_VV_RM(vsaddu_vv_d, 8, 8) +GEN_VEXT_VV_RM(vsaddu_vv_b, 1) +GEN_VEXT_VV_RM(vsaddu_vv_h, 2) +GEN_VEXT_VV_RM(vsaddu_vv_w, 4) +GEN_VEXT_VV_RM(vsaddu_vv_d, 8) typedef void opivx2_rm_fn(void *vd, target_long s1, void *vs2, int i, CPURISCVState *env, int vxrm); @@ -2025,10 +2077,14 @@ static inline void vext_vx_rm_1(void *vd, void *v0, target_long s1, void *vs2, CPURISCVState *env, uint32_t vl, uint32_t vm, int vxrm, - opivx2_rm_fn *fn) + opivx2_rm_fn *fn, uint32_t vma, uint32_t esz) { + VSTART_CHECK_EARLY_EXIT(env); + for (uint32_t i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); continue; } fn(vd, s1, vs2, i, env, vxrm); @@ -2039,49 +2095,55 @@ vext_vx_rm_1(void *vd, void *v0, target_long s1, void *vs2, static inline void vext_vx_rm_2(void *vd, void *v0, target_long s1, void *vs2, CPURISCVState *env, - uint32_t desc, uint32_t esz, uint32_t dsz, - opivx2_rm_fn *fn) + uint32_t desc, + opivx2_rm_fn *fn, uint32_t esz) { uint32_t vm = vext_vm(desc); uint32_t vl = env->vl; + uint32_t total_elems = vext_get_total_elems(env, desc, esz); + uint32_t vta = vext_vta(desc); + uint32_t vma = vext_vma(desc); switch (env->vxrm) { case 0: /* rnu */ vext_vx_rm_1(vd, v0, s1, vs2, - env, vl, vm, 0, fn); + env, vl, vm, 0, fn, vma, esz); break; case 1: /* rne */ vext_vx_rm_1(vd, v0, s1, vs2, - env, vl, vm, 1, fn); + env, vl, vm, 1, fn, vma, esz); break; case 2: /* rdn */ vext_vx_rm_1(vd, v0, s1, vs2, - env, vl, vm, 2, fn); + env, vl, vm, 2, fn, vma, esz); break; default: /* rod */ vext_vx_rm_1(vd, v0, s1, vs2, - env, vl, vm, 3, fn); + env, vl, vm, 3, fn, vma, esz); break; } + /* set tail elements to 1s */ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); } /* generate helpers for fixed point instructions with OPIVX format */ -#define GEN_VEXT_VX_RM(NAME, ESZ, DSZ) \ +#define GEN_VEXT_VX_RM(NAME, ESZ) \ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ - void *vs2, CPURISCVState *env, uint32_t desc) \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ { \ - vext_vx_rm_2(vd, v0, s1, vs2, env, desc, ESZ, DSZ, \ - do_##NAME); \ + vext_vx_rm_2(vd, v0, s1, vs2, env, desc, \ + do_##NAME, ESZ); \ } RVVCALL(OPIVX2_RM, vsaddu_vx_b, OP_UUU_B, H1, H1, saddu8) RVVCALL(OPIVX2_RM, vsaddu_vx_h, OP_UUU_H, H2, H2, saddu16) RVVCALL(OPIVX2_RM, vsaddu_vx_w, OP_UUU_W, H4, H4, saddu32) RVVCALL(OPIVX2_RM, vsaddu_vx_d, OP_UUU_D, H8, H8, saddu64) -GEN_VEXT_VX_RM(vsaddu_vx_b, 1, 1) -GEN_VEXT_VX_RM(vsaddu_vx_h, 2, 2) -GEN_VEXT_VX_RM(vsaddu_vx_w, 4, 4) -GEN_VEXT_VX_RM(vsaddu_vx_d, 8, 8) +GEN_VEXT_VX_RM(vsaddu_vx_b, 1) +GEN_VEXT_VX_RM(vsaddu_vx_h, 2) +GEN_VEXT_VX_RM(vsaddu_vx_w, 4) +GEN_VEXT_VX_RM(vsaddu_vx_d, 8) static inline int8_t sadd8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) { @@ -2093,7 +2155,8 @@ static inline int8_t sadd8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) return res; } -static inline int16_t sadd16(CPURISCVState *env, int vxrm, int16_t a, int16_t b) +static inline int16_t sadd16(CPURISCVState *env, int vxrm, int16_t a, + int16_t b) { int16_t res = a + b; if ((res ^ a) & (res ^ b) & INT16_MIN) { @@ -2103,7 +2166,8 @@ static inline int16_t sadd16(CPURISCVState *env, int vxrm, int16_t a, int16_t b) return res; } -static inline int32_t sadd32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) +static inline int32_t sadd32(CPURISCVState *env, int vxrm, int32_t a, + int32_t b) { int32_t res = a + b; if ((res ^ a) & (res ^ b) & INT32_MIN) { @@ -2113,7 +2177,8 @@ static inline int32_t sadd32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) return res; } -static inline int64_t sadd64(CPURISCVState *env, int vxrm, int64_t a, int64_t b) +static inline int64_t sadd64(CPURISCVState *env, int vxrm, int64_t a, + int64_t b) { int64_t res = a + b; if ((res ^ a) & (res ^ b) & INT64_MIN) { @@ -2127,21 +2192,22 @@ RVVCALL(OPIVV2_RM, vsadd_vv_b, OP_SSS_B, H1, H1, H1, sadd8) RVVCALL(OPIVV2_RM, vsadd_vv_h, OP_SSS_H, H2, H2, H2, sadd16) RVVCALL(OPIVV2_RM, vsadd_vv_w, OP_SSS_W, H4, H4, H4, sadd32) RVVCALL(OPIVV2_RM, vsadd_vv_d, OP_SSS_D, H8, H8, H8, sadd64) -GEN_VEXT_VV_RM(vsadd_vv_b, 1, 1) -GEN_VEXT_VV_RM(vsadd_vv_h, 2, 2) -GEN_VEXT_VV_RM(vsadd_vv_w, 4, 4) -GEN_VEXT_VV_RM(vsadd_vv_d, 8, 8) +GEN_VEXT_VV_RM(vsadd_vv_b, 1) +GEN_VEXT_VV_RM(vsadd_vv_h, 2) +GEN_VEXT_VV_RM(vsadd_vv_w, 4) +GEN_VEXT_VV_RM(vsadd_vv_d, 8) RVVCALL(OPIVX2_RM, vsadd_vx_b, OP_SSS_B, H1, H1, sadd8) RVVCALL(OPIVX2_RM, vsadd_vx_h, OP_SSS_H, H2, H2, sadd16) RVVCALL(OPIVX2_RM, vsadd_vx_w, OP_SSS_W, H4, H4, sadd32) RVVCALL(OPIVX2_RM, vsadd_vx_d, OP_SSS_D, H8, H8, sadd64) -GEN_VEXT_VX_RM(vsadd_vx_b, 1, 1) -GEN_VEXT_VX_RM(vsadd_vx_h, 2, 2) -GEN_VEXT_VX_RM(vsadd_vx_w, 4, 4) -GEN_VEXT_VX_RM(vsadd_vx_d, 8, 8) +GEN_VEXT_VX_RM(vsadd_vx_b, 1) +GEN_VEXT_VX_RM(vsadd_vx_h, 2) +GEN_VEXT_VX_RM(vsadd_vx_w, 4) +GEN_VEXT_VX_RM(vsadd_vx_d, 8) -static inline uint8_t ssubu8(CPURISCVState *env, int vxrm, uint8_t a, uint8_t b) +static inline uint8_t ssubu8(CPURISCVState *env, int vxrm, uint8_t a, + uint8_t b) { uint8_t res = a - b; if (res > a) { @@ -2188,19 +2254,19 @@ RVVCALL(OPIVV2_RM, vssubu_vv_b, OP_UUU_B, H1, H1, H1, ssubu8) RVVCALL(OPIVV2_RM, vssubu_vv_h, OP_UUU_H, H2, H2, H2, ssubu16) RVVCALL(OPIVV2_RM, vssubu_vv_w, OP_UUU_W, H4, H4, H4, ssubu32) RVVCALL(OPIVV2_RM, vssubu_vv_d, OP_UUU_D, H8, H8, H8, ssubu64) -GEN_VEXT_VV_RM(vssubu_vv_b, 1, 1) -GEN_VEXT_VV_RM(vssubu_vv_h, 2, 2) -GEN_VEXT_VV_RM(vssubu_vv_w, 4, 4) -GEN_VEXT_VV_RM(vssubu_vv_d, 8, 8) +GEN_VEXT_VV_RM(vssubu_vv_b, 1) +GEN_VEXT_VV_RM(vssubu_vv_h, 2) +GEN_VEXT_VV_RM(vssubu_vv_w, 4) +GEN_VEXT_VV_RM(vssubu_vv_d, 8) RVVCALL(OPIVX2_RM, vssubu_vx_b, OP_UUU_B, H1, H1, ssubu8) RVVCALL(OPIVX2_RM, vssubu_vx_h, OP_UUU_H, H2, H2, ssubu16) RVVCALL(OPIVX2_RM, vssubu_vx_w, OP_UUU_W, H4, H4, ssubu32) RVVCALL(OPIVX2_RM, vssubu_vx_d, OP_UUU_D, H8, H8, ssubu64) -GEN_VEXT_VX_RM(vssubu_vx_b, 1, 1) -GEN_VEXT_VX_RM(vssubu_vx_h, 2, 2) -GEN_VEXT_VX_RM(vssubu_vx_w, 4, 4) -GEN_VEXT_VX_RM(vssubu_vx_d, 8, 8) +GEN_VEXT_VX_RM(vssubu_vx_b, 1) +GEN_VEXT_VX_RM(vssubu_vx_h, 2) +GEN_VEXT_VX_RM(vssubu_vx_w, 4) +GEN_VEXT_VX_RM(vssubu_vx_d, 8) static inline int8_t ssub8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) { @@ -2212,7 +2278,8 @@ static inline int8_t ssub8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) return res; } -static inline int16_t ssub16(CPURISCVState *env, int vxrm, int16_t a, int16_t b) +static inline int16_t ssub16(CPURISCVState *env, int vxrm, int16_t a, + int16_t b) { int16_t res = a - b; if ((res ^ a) & (a ^ b) & INT16_MIN) { @@ -2222,7 +2289,8 @@ static inline int16_t ssub16(CPURISCVState *env, int vxrm, int16_t a, int16_t b) return res; } -static inline int32_t ssub32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) +static inline int32_t ssub32(CPURISCVState *env, int vxrm, int32_t a, + int32_t b) { int32_t res = a - b; if ((res ^ a) & (a ^ b) & INT32_MIN) { @@ -2232,7 +2300,8 @@ static inline int32_t ssub32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) return res; } -static inline int64_t ssub64(CPURISCVState *env, int vxrm, int64_t a, int64_t b) +static inline int64_t ssub64(CPURISCVState *env, int vxrm, int64_t a, + int64_t b) { int64_t res = a - b; if ((res ^ a) & (a ^ b) & INT64_MIN) { @@ -2246,19 +2315,19 @@ RVVCALL(OPIVV2_RM, vssub_vv_b, OP_SSS_B, H1, H1, H1, ssub8) RVVCALL(OPIVV2_RM, vssub_vv_h, OP_SSS_H, H2, H2, H2, ssub16) RVVCALL(OPIVV2_RM, vssub_vv_w, OP_SSS_W, H4, H4, H4, ssub32) RVVCALL(OPIVV2_RM, vssub_vv_d, OP_SSS_D, H8, H8, H8, ssub64) -GEN_VEXT_VV_RM(vssub_vv_b, 1, 1) -GEN_VEXT_VV_RM(vssub_vv_h, 2, 2) -GEN_VEXT_VV_RM(vssub_vv_w, 4, 4) -GEN_VEXT_VV_RM(vssub_vv_d, 8, 8) +GEN_VEXT_VV_RM(vssub_vv_b, 1) +GEN_VEXT_VV_RM(vssub_vv_h, 2) +GEN_VEXT_VV_RM(vssub_vv_w, 4) +GEN_VEXT_VV_RM(vssub_vv_d, 8) RVVCALL(OPIVX2_RM, vssub_vx_b, OP_SSS_B, H1, H1, ssub8) RVVCALL(OPIVX2_RM, vssub_vx_h, OP_SSS_H, H2, H2, ssub16) RVVCALL(OPIVX2_RM, vssub_vx_w, OP_SSS_W, H4, H4, ssub32) RVVCALL(OPIVX2_RM, vssub_vx_d, OP_SSS_D, H8, H8, ssub64) -GEN_VEXT_VX_RM(vssub_vx_b, 1, 1) -GEN_VEXT_VX_RM(vssub_vx_h, 2, 2) -GEN_VEXT_VX_RM(vssub_vx_w, 4, 4) -GEN_VEXT_VX_RM(vssub_vx_d, 8, 8) +GEN_VEXT_VX_RM(vssub_vx_b, 1) +GEN_VEXT_VX_RM(vssub_vx_h, 2) +GEN_VEXT_VX_RM(vssub_vx_w, 4) +GEN_VEXT_VX_RM(vssub_vx_d, 8) /* Vector Single-Width Averaging Add and Subtract */ static inline uint8_t get_round(int vxrm, uint64_t v, uint8_t shift) @@ -2288,7 +2357,8 @@ static inline uint8_t get_round(int vxrm, uint64_t v, uint8_t shift) return 0; /* round-down (truncate) */ } -static inline int32_t aadd32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) +static inline int32_t aadd32(CPURISCVState *env, int vxrm, int32_t a, + int32_t b) { int64_t res = (int64_t)a + b; uint8_t round = get_round(vxrm, res, 1); @@ -2296,7 +2366,8 @@ static inline int32_t aadd32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) return (res >> 1) + round; } -static inline int64_t aadd64(CPURISCVState *env, int vxrm, int64_t a, int64_t b) +static inline int64_t aadd64(CPURISCVState *env, int vxrm, int64_t a, + int64_t b) { int64_t res = a + b; uint8_t round = get_round(vxrm, res, 1); @@ -2310,19 +2381,19 @@ RVVCALL(OPIVV2_RM, vaadd_vv_b, OP_SSS_B, H1, H1, H1, aadd32) RVVCALL(OPIVV2_RM, vaadd_vv_h, OP_SSS_H, H2, H2, H2, aadd32) RVVCALL(OPIVV2_RM, vaadd_vv_w, OP_SSS_W, H4, H4, H4, aadd32) RVVCALL(OPIVV2_RM, vaadd_vv_d, OP_SSS_D, H8, H8, H8, aadd64) -GEN_VEXT_VV_RM(vaadd_vv_b, 1, 1) -GEN_VEXT_VV_RM(vaadd_vv_h, 2, 2) -GEN_VEXT_VV_RM(vaadd_vv_w, 4, 4) -GEN_VEXT_VV_RM(vaadd_vv_d, 8, 8) +GEN_VEXT_VV_RM(vaadd_vv_b, 1) +GEN_VEXT_VV_RM(vaadd_vv_h, 2) +GEN_VEXT_VV_RM(vaadd_vv_w, 4) +GEN_VEXT_VV_RM(vaadd_vv_d, 8) RVVCALL(OPIVX2_RM, vaadd_vx_b, OP_SSS_B, H1, H1, aadd32) RVVCALL(OPIVX2_RM, vaadd_vx_h, OP_SSS_H, H2, H2, aadd32) RVVCALL(OPIVX2_RM, vaadd_vx_w, OP_SSS_W, H4, H4, aadd32) RVVCALL(OPIVX2_RM, vaadd_vx_d, OP_SSS_D, H8, H8, aadd64) -GEN_VEXT_VX_RM(vaadd_vx_b, 1, 1) -GEN_VEXT_VX_RM(vaadd_vx_h, 2, 2) -GEN_VEXT_VX_RM(vaadd_vx_w, 4, 4) -GEN_VEXT_VX_RM(vaadd_vx_d, 8, 8) +GEN_VEXT_VX_RM(vaadd_vx_b, 1) +GEN_VEXT_VX_RM(vaadd_vx_h, 2) +GEN_VEXT_VX_RM(vaadd_vx_w, 4) +GEN_VEXT_VX_RM(vaadd_vx_d, 8) static inline uint32_t aaddu32(CPURISCVState *env, int vxrm, uint32_t a, uint32_t b) @@ -2347,21 +2418,22 @@ RVVCALL(OPIVV2_RM, vaaddu_vv_b, OP_UUU_B, H1, H1, H1, aaddu32) RVVCALL(OPIVV2_RM, vaaddu_vv_h, OP_UUU_H, H2, H2, H2, aaddu32) RVVCALL(OPIVV2_RM, vaaddu_vv_w, OP_UUU_W, H4, H4, H4, aaddu32) RVVCALL(OPIVV2_RM, vaaddu_vv_d, OP_UUU_D, H8, H8, H8, aaddu64) -GEN_VEXT_VV_RM(vaaddu_vv_b, 1, 1) -GEN_VEXT_VV_RM(vaaddu_vv_h, 2, 2) -GEN_VEXT_VV_RM(vaaddu_vv_w, 4, 4) -GEN_VEXT_VV_RM(vaaddu_vv_d, 8, 8) +GEN_VEXT_VV_RM(vaaddu_vv_b, 1) +GEN_VEXT_VV_RM(vaaddu_vv_h, 2) +GEN_VEXT_VV_RM(vaaddu_vv_w, 4) +GEN_VEXT_VV_RM(vaaddu_vv_d, 8) RVVCALL(OPIVX2_RM, vaaddu_vx_b, OP_UUU_B, H1, H1, aaddu32) RVVCALL(OPIVX2_RM, vaaddu_vx_h, OP_UUU_H, H2, H2, aaddu32) RVVCALL(OPIVX2_RM, vaaddu_vx_w, OP_UUU_W, H4, H4, aaddu32) RVVCALL(OPIVX2_RM, vaaddu_vx_d, OP_UUU_D, H8, H8, aaddu64) -GEN_VEXT_VX_RM(vaaddu_vx_b, 1, 1) -GEN_VEXT_VX_RM(vaaddu_vx_h, 2, 2) -GEN_VEXT_VX_RM(vaaddu_vx_w, 4, 4) -GEN_VEXT_VX_RM(vaaddu_vx_d, 8, 8) +GEN_VEXT_VX_RM(vaaddu_vx_b, 1) +GEN_VEXT_VX_RM(vaaddu_vx_h, 2) +GEN_VEXT_VX_RM(vaaddu_vx_w, 4) +GEN_VEXT_VX_RM(vaaddu_vx_d, 8) -static inline int32_t asub32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) +static inline int32_t asub32(CPURISCVState *env, int vxrm, int32_t a, + int32_t b) { int64_t res = (int64_t)a - b; uint8_t round = get_round(vxrm, res, 1); @@ -2369,7 +2441,8 @@ static inline int32_t asub32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) return (res >> 1) + round; } -static inline int64_t asub64(CPURISCVState *env, int vxrm, int64_t a, int64_t b) +static inline int64_t asub64(CPURISCVState *env, int vxrm, int64_t a, + int64_t b) { int64_t res = (int64_t)a - b; uint8_t round = get_round(vxrm, res, 1); @@ -2383,19 +2456,19 @@ RVVCALL(OPIVV2_RM, vasub_vv_b, OP_SSS_B, H1, H1, H1, asub32) RVVCALL(OPIVV2_RM, vasub_vv_h, OP_SSS_H, H2, H2, H2, asub32) RVVCALL(OPIVV2_RM, vasub_vv_w, OP_SSS_W, H4, H4, H4, asub32) RVVCALL(OPIVV2_RM, vasub_vv_d, OP_SSS_D, H8, H8, H8, asub64) -GEN_VEXT_VV_RM(vasub_vv_b, 1, 1) -GEN_VEXT_VV_RM(vasub_vv_h, 2, 2) -GEN_VEXT_VV_RM(vasub_vv_w, 4, 4) -GEN_VEXT_VV_RM(vasub_vv_d, 8, 8) +GEN_VEXT_VV_RM(vasub_vv_b, 1) +GEN_VEXT_VV_RM(vasub_vv_h, 2) +GEN_VEXT_VV_RM(vasub_vv_w, 4) +GEN_VEXT_VV_RM(vasub_vv_d, 8) RVVCALL(OPIVX2_RM, vasub_vx_b, OP_SSS_B, H1, H1, asub32) RVVCALL(OPIVX2_RM, vasub_vx_h, OP_SSS_H, H2, H2, asub32) RVVCALL(OPIVX2_RM, vasub_vx_w, OP_SSS_W, H4, H4, asub32) RVVCALL(OPIVX2_RM, vasub_vx_d, OP_SSS_D, H8, H8, asub64) -GEN_VEXT_VX_RM(vasub_vx_b, 1, 1) -GEN_VEXT_VX_RM(vasub_vx_h, 2, 2) -GEN_VEXT_VX_RM(vasub_vx_w, 4, 4) -GEN_VEXT_VX_RM(vasub_vx_d, 8, 8) +GEN_VEXT_VX_RM(vasub_vx_b, 1) +GEN_VEXT_VX_RM(vasub_vx_h, 2) +GEN_VEXT_VX_RM(vasub_vx_w, 4) +GEN_VEXT_VX_RM(vasub_vx_d, 8) static inline uint32_t asubu32(CPURISCVState *env, int vxrm, uint32_t a, uint32_t b) @@ -2420,19 +2493,19 @@ RVVCALL(OPIVV2_RM, vasubu_vv_b, OP_UUU_B, H1, H1, H1, asubu32) RVVCALL(OPIVV2_RM, vasubu_vv_h, OP_UUU_H, H2, H2, H2, asubu32) RVVCALL(OPIVV2_RM, vasubu_vv_w, OP_UUU_W, H4, H4, H4, asubu32) RVVCALL(OPIVV2_RM, vasubu_vv_d, OP_UUU_D, H8, H8, H8, asubu64) -GEN_VEXT_VV_RM(vasubu_vv_b, 1, 1) -GEN_VEXT_VV_RM(vasubu_vv_h, 2, 2) -GEN_VEXT_VV_RM(vasubu_vv_w, 4, 4) -GEN_VEXT_VV_RM(vasubu_vv_d, 8, 8) +GEN_VEXT_VV_RM(vasubu_vv_b, 1) +GEN_VEXT_VV_RM(vasubu_vv_h, 2) +GEN_VEXT_VV_RM(vasubu_vv_w, 4) +GEN_VEXT_VV_RM(vasubu_vv_d, 8) RVVCALL(OPIVX2_RM, vasubu_vx_b, OP_UUU_B, H1, H1, asubu32) RVVCALL(OPIVX2_RM, vasubu_vx_h, OP_UUU_H, H2, H2, asubu32) RVVCALL(OPIVX2_RM, vasubu_vx_w, OP_UUU_W, H4, H4, asubu32) RVVCALL(OPIVX2_RM, vasubu_vx_d, OP_UUU_D, H8, H8, asubu64) -GEN_VEXT_VX_RM(vasubu_vx_b, 1, 1) -GEN_VEXT_VX_RM(vasubu_vx_h, 2, 2) -GEN_VEXT_VX_RM(vasubu_vx_w, 4, 4) -GEN_VEXT_VX_RM(vasubu_vx_d, 8, 8) +GEN_VEXT_VX_RM(vasubu_vx_b, 1) +GEN_VEXT_VX_RM(vasubu_vx_h, 2) +GEN_VEXT_VX_RM(vasubu_vx_w, 4) +GEN_VEXT_VX_RM(vasubu_vx_d, 8) /* Vector Single-Width Fractional Multiply with Rounding and Saturation */ static inline int8_t vsmul8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) @@ -2442,7 +2515,7 @@ static inline int8_t vsmul8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) res = (int16_t)a * (int16_t)b; round = get_round(vxrm, res, 7); - res = (res >> 7) + round; + res = (res >> 7) + round; if (res > INT8_MAX) { env->vxsat = 0x1; @@ -2462,7 +2535,7 @@ static int16_t vsmul16(CPURISCVState *env, int vxrm, int16_t a, int16_t b) res = (int32_t)a * (int32_t)b; round = get_round(vxrm, res, 15); - res = (res >> 15) + round; + res = (res >> 15) + round; if (res > INT16_MAX) { env->vxsat = 0x1; @@ -2482,7 +2555,7 @@ static int32_t vsmul32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) res = (int64_t)a * (int64_t)b; round = get_round(vxrm, res, 31); - res = (res >> 31) + round; + res = (res >> 31) + round; if (res > INT32_MAX) { env->vxsat = 0x1; @@ -2527,19 +2600,19 @@ RVVCALL(OPIVV2_RM, vsmul_vv_b, OP_SSS_B, H1, H1, H1, vsmul8) RVVCALL(OPIVV2_RM, vsmul_vv_h, OP_SSS_H, H2, H2, H2, vsmul16) RVVCALL(OPIVV2_RM, vsmul_vv_w, OP_SSS_W, H4, H4, H4, vsmul32) RVVCALL(OPIVV2_RM, vsmul_vv_d, OP_SSS_D, H8, H8, H8, vsmul64) -GEN_VEXT_VV_RM(vsmul_vv_b, 1, 1) -GEN_VEXT_VV_RM(vsmul_vv_h, 2, 2) -GEN_VEXT_VV_RM(vsmul_vv_w, 4, 4) -GEN_VEXT_VV_RM(vsmul_vv_d, 8, 8) +GEN_VEXT_VV_RM(vsmul_vv_b, 1) +GEN_VEXT_VV_RM(vsmul_vv_h, 2) +GEN_VEXT_VV_RM(vsmul_vv_w, 4) +GEN_VEXT_VV_RM(vsmul_vv_d, 8) RVVCALL(OPIVX2_RM, vsmul_vx_b, OP_SSS_B, H1, H1, vsmul8) RVVCALL(OPIVX2_RM, vsmul_vx_h, OP_SSS_H, H2, H2, vsmul16) RVVCALL(OPIVX2_RM, vsmul_vx_w, OP_SSS_W, H4, H4, vsmul32) RVVCALL(OPIVX2_RM, vsmul_vx_d, OP_SSS_D, H8, H8, vsmul64) -GEN_VEXT_VX_RM(vsmul_vx_b, 1, 1) -GEN_VEXT_VX_RM(vsmul_vx_h, 2, 2) -GEN_VEXT_VX_RM(vsmul_vx_w, 4, 4) -GEN_VEXT_VX_RM(vsmul_vx_d, 8, 8) +GEN_VEXT_VX_RM(vsmul_vx_b, 1) +GEN_VEXT_VX_RM(vsmul_vx_h, 2) +GEN_VEXT_VX_RM(vsmul_vx_w, 4) +GEN_VEXT_VX_RM(vsmul_vx_d, 8) /* Vector Single-Width Scaling Shift Instructions */ static inline uint8_t @@ -2549,115 +2622,101 @@ vssrl8(CPURISCVState *env, int vxrm, uint8_t a, uint8_t b) uint8_t res; round = get_round(vxrm, a, shift); - res = (a >> shift) + round; + res = (a >> shift) + round; return res; } static inline uint16_t vssrl16(CPURISCVState *env, int vxrm, uint16_t a, uint16_t b) { uint8_t round, shift = b & 0xf; - uint16_t res; round = get_round(vxrm, a, shift); - res = (a >> shift) + round; - return res; + return (a >> shift) + round; } static inline uint32_t vssrl32(CPURISCVState *env, int vxrm, uint32_t a, uint32_t b) { uint8_t round, shift = b & 0x1f; - uint32_t res; round = get_round(vxrm, a, shift); - res = (a >> shift) + round; - return res; + return (a >> shift) + round; } static inline uint64_t vssrl64(CPURISCVState *env, int vxrm, uint64_t a, uint64_t b) { uint8_t round, shift = b & 0x3f; - uint64_t res; round = get_round(vxrm, a, shift); - res = (a >> shift) + round; - return res; + return (a >> shift) + round; } RVVCALL(OPIVV2_RM, vssrl_vv_b, OP_UUU_B, H1, H1, H1, vssrl8) RVVCALL(OPIVV2_RM, vssrl_vv_h, OP_UUU_H, H2, H2, H2, vssrl16) RVVCALL(OPIVV2_RM, vssrl_vv_w, OP_UUU_W, H4, H4, H4, vssrl32) RVVCALL(OPIVV2_RM, vssrl_vv_d, OP_UUU_D, H8, H8, H8, vssrl64) -GEN_VEXT_VV_RM(vssrl_vv_b, 1, 1) -GEN_VEXT_VV_RM(vssrl_vv_h, 2, 2) -GEN_VEXT_VV_RM(vssrl_vv_w, 4, 4) -GEN_VEXT_VV_RM(vssrl_vv_d, 8, 8) +GEN_VEXT_VV_RM(vssrl_vv_b, 1) +GEN_VEXT_VV_RM(vssrl_vv_h, 2) +GEN_VEXT_VV_RM(vssrl_vv_w, 4) +GEN_VEXT_VV_RM(vssrl_vv_d, 8) RVVCALL(OPIVX2_RM, vssrl_vx_b, OP_UUU_B, H1, H1, vssrl8) RVVCALL(OPIVX2_RM, vssrl_vx_h, OP_UUU_H, H2, H2, vssrl16) RVVCALL(OPIVX2_RM, vssrl_vx_w, OP_UUU_W, H4, H4, vssrl32) RVVCALL(OPIVX2_RM, vssrl_vx_d, OP_UUU_D, H8, H8, vssrl64) -GEN_VEXT_VX_RM(vssrl_vx_b, 1, 1) -GEN_VEXT_VX_RM(vssrl_vx_h, 2, 2) -GEN_VEXT_VX_RM(vssrl_vx_w, 4, 4) -GEN_VEXT_VX_RM(vssrl_vx_d, 8, 8) +GEN_VEXT_VX_RM(vssrl_vx_b, 1) +GEN_VEXT_VX_RM(vssrl_vx_h, 2) +GEN_VEXT_VX_RM(vssrl_vx_w, 4) +GEN_VEXT_VX_RM(vssrl_vx_d, 8) static inline int8_t vssra8(CPURISCVState *env, int vxrm, int8_t a, int8_t b) { uint8_t round, shift = b & 0x7; - int8_t res; round = get_round(vxrm, a, shift); - res = (a >> shift) + round; - return res; + return (a >> shift) + round; } static inline int16_t vssra16(CPURISCVState *env, int vxrm, int16_t a, int16_t b) { uint8_t round, shift = b & 0xf; - int16_t res; round = get_round(vxrm, a, shift); - res = (a >> shift) + round; - return res; + return (a >> shift) + round; } static inline int32_t vssra32(CPURISCVState *env, int vxrm, int32_t a, int32_t b) { uint8_t round, shift = b & 0x1f; - int32_t res; round = get_round(vxrm, a, shift); - res = (a >> shift) + round; - return res; + return (a >> shift) + round; } static inline int64_t vssra64(CPURISCVState *env, int vxrm, int64_t a, int64_t b) { uint8_t round, shift = b & 0x3f; - int64_t res; round = get_round(vxrm, a, shift); - res = (a >> shift) + round; - return res; + return (a >> shift) + round; } RVVCALL(OPIVV2_RM, vssra_vv_b, OP_SSS_B, H1, H1, H1, vssra8) RVVCALL(OPIVV2_RM, vssra_vv_h, OP_SSS_H, H2, H2, H2, vssra16) RVVCALL(OPIVV2_RM, vssra_vv_w, OP_SSS_W, H4, H4, H4, vssra32) RVVCALL(OPIVV2_RM, vssra_vv_d, OP_SSS_D, H8, H8, H8, vssra64) -GEN_VEXT_VV_RM(vssra_vv_b, 1, 1) -GEN_VEXT_VV_RM(vssra_vv_h, 2, 2) -GEN_VEXT_VV_RM(vssra_vv_w, 4, 4) -GEN_VEXT_VV_RM(vssra_vv_d, 8, 8) +GEN_VEXT_VV_RM(vssra_vv_b, 1) +GEN_VEXT_VV_RM(vssra_vv_h, 2) +GEN_VEXT_VV_RM(vssra_vv_w, 4) +GEN_VEXT_VV_RM(vssra_vv_d, 8) RVVCALL(OPIVX2_RM, vssra_vx_b, OP_SSS_B, H1, H1, vssra8) RVVCALL(OPIVX2_RM, vssra_vx_h, OP_SSS_H, H2, H2, vssra16) RVVCALL(OPIVX2_RM, vssra_vx_w, OP_SSS_W, H4, H4, vssra32) RVVCALL(OPIVX2_RM, vssra_vx_d, OP_SSS_D, H8, H8, vssra64) -GEN_VEXT_VX_RM(vssra_vx_b, 1, 1) -GEN_VEXT_VX_RM(vssra_vx_h, 2, 2) -GEN_VEXT_VX_RM(vssra_vx_w, 4, 4) -GEN_VEXT_VX_RM(vssra_vx_d, 8, 8) +GEN_VEXT_VX_RM(vssra_vx_b, 1) +GEN_VEXT_VX_RM(vssra_vx_h, 2) +GEN_VEXT_VX_RM(vssra_vx_w, 4) +GEN_VEXT_VX_RM(vssra_vx_d, 8) /* Vector Narrowing Fixed-Point Clip Instructions */ static inline int8_t @@ -2667,7 +2726,7 @@ vnclip8(CPURISCVState *env, int vxrm, int16_t a, int8_t b) int16_t res; round = get_round(vxrm, a, shift); - res = (a >> shift) + round; + res = (a >> shift) + round; if (res > INT8_MAX) { env->vxsat = 0x1; return INT8_MAX; @@ -2686,7 +2745,7 @@ vnclip16(CPURISCVState *env, int vxrm, int32_t a, int16_t b) int32_t res; round = get_round(vxrm, a, shift); - res = (a >> shift) + round; + res = (a >> shift) + round; if (res > INT16_MAX) { env->vxsat = 0x1; return INT16_MAX; @@ -2705,7 +2764,7 @@ vnclip32(CPURISCVState *env, int vxrm, int64_t a, int32_t b) int64_t res; round = get_round(vxrm, a, shift); - res = (a >> shift) + round; + res = (a >> shift) + round; if (res > INT32_MAX) { env->vxsat = 0x1; return INT32_MAX; @@ -2720,16 +2779,16 @@ vnclip32(CPURISCVState *env, int vxrm, int64_t a, int32_t b) RVVCALL(OPIVV2_RM, vnclip_wv_b, NOP_SSS_B, H1, H2, H1, vnclip8) RVVCALL(OPIVV2_RM, vnclip_wv_h, NOP_SSS_H, H2, H4, H2, vnclip16) RVVCALL(OPIVV2_RM, vnclip_wv_w, NOP_SSS_W, H4, H8, H4, vnclip32) -GEN_VEXT_VV_RM(vnclip_wv_b, 1, 1) -GEN_VEXT_VV_RM(vnclip_wv_h, 2, 2) -GEN_VEXT_VV_RM(vnclip_wv_w, 4, 4) +GEN_VEXT_VV_RM(vnclip_wv_b, 1) +GEN_VEXT_VV_RM(vnclip_wv_h, 2) +GEN_VEXT_VV_RM(vnclip_wv_w, 4) RVVCALL(OPIVX2_RM, vnclip_wx_b, NOP_SSS_B, H1, H2, vnclip8) RVVCALL(OPIVX2_RM, vnclip_wx_h, NOP_SSS_H, H2, H4, vnclip16) RVVCALL(OPIVX2_RM, vnclip_wx_w, NOP_SSS_W, H4, H8, vnclip32) -GEN_VEXT_VX_RM(vnclip_wx_b, 1, 1) -GEN_VEXT_VX_RM(vnclip_wx_h, 2, 2) -GEN_VEXT_VX_RM(vnclip_wx_w, 4, 4) +GEN_VEXT_VX_RM(vnclip_wx_b, 1) +GEN_VEXT_VX_RM(vnclip_wx_h, 2) +GEN_VEXT_VX_RM(vnclip_wx_w, 4) static inline uint8_t vnclipu8(CPURISCVState *env, int vxrm, uint16_t a, uint8_t b) @@ -2738,7 +2797,7 @@ vnclipu8(CPURISCVState *env, int vxrm, uint16_t a, uint8_t b) uint16_t res; round = get_round(vxrm, a, shift); - res = (a >> shift) + round; + res = (a >> shift) + round; if (res > UINT8_MAX) { env->vxsat = 0x1; return UINT8_MAX; @@ -2754,7 +2813,7 @@ vnclipu16(CPURISCVState *env, int vxrm, uint32_t a, uint16_t b) uint32_t res; round = get_round(vxrm, a, shift); - res = (a >> shift) + round; + res = (a >> shift) + round; if (res > UINT16_MAX) { env->vxsat = 0x1; return UINT16_MAX; @@ -2770,7 +2829,7 @@ vnclipu32(CPURISCVState *env, int vxrm, uint64_t a, uint32_t b) uint64_t res; round = get_round(vxrm, a, shift); - res = (a >> shift) + round; + res = (a >> shift) + round; if (res > UINT32_MAX) { env->vxsat = 0x1; return UINT32_MAX; @@ -2782,19 +2841,19 @@ vnclipu32(CPURISCVState *env, int vxrm, uint64_t a, uint32_t b) RVVCALL(OPIVV2_RM, vnclipu_wv_b, NOP_UUU_B, H1, H2, H1, vnclipu8) RVVCALL(OPIVV2_RM, vnclipu_wv_h, NOP_UUU_H, H2, H4, H2, vnclipu16) RVVCALL(OPIVV2_RM, vnclipu_wv_w, NOP_UUU_W, H4, H8, H4, vnclipu32) -GEN_VEXT_VV_RM(vnclipu_wv_b, 1, 1) -GEN_VEXT_VV_RM(vnclipu_wv_h, 2, 2) -GEN_VEXT_VV_RM(vnclipu_wv_w, 4, 4) +GEN_VEXT_VV_RM(vnclipu_wv_b, 1) +GEN_VEXT_VV_RM(vnclipu_wv_h, 2) +GEN_VEXT_VV_RM(vnclipu_wv_w, 4) RVVCALL(OPIVX2_RM, vnclipu_wx_b, NOP_UUU_B, H1, H2, vnclipu8) RVVCALL(OPIVX2_RM, vnclipu_wx_h, NOP_UUU_H, H2, H4, vnclipu16) RVVCALL(OPIVX2_RM, vnclipu_wx_w, NOP_UUU_W, H4, H8, vnclipu32) -GEN_VEXT_VX_RM(vnclipu_wx_b, 1, 1) -GEN_VEXT_VX_RM(vnclipu_wx_h, 2, 2) -GEN_VEXT_VX_RM(vnclipu_wx_w, 4, 4) +GEN_VEXT_VX_RM(vnclipu_wx_b, 1) +GEN_VEXT_VX_RM(vnclipu_wx_h, 2) +GEN_VEXT_VX_RM(vnclipu_wx_w, 4) /* - *** Vector Float Point Arithmetic Instructions + * Vector Float Point Arithmetic Instructions */ /* Vector Single-Width Floating-Point Add/Subtract Instructions */ #define OPFVV2(NAME, TD, T1, T2, TX1, TX2, HD, HS1, HS2, OP) \ @@ -2806,30 +2865,42 @@ static void do_##NAME(void *vd, void *vs1, void *vs2, int i, \ *((TD *)vd + HD(i)) = OP(s2, s1, &env->fp_status); \ } -#define GEN_VEXT_VV_ENV(NAME, ESZ, DSZ) \ +#define GEN_VEXT_VV_ENV(NAME, ESZ) \ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ void *vs2, CPURISCVState *env, \ uint32_t desc) \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t total_elems = \ + vext_get_total_elems(env, desc, ESZ); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * ESZ, \ + (i + 1) * ESZ); \ continue; \ } \ do_##NAME(vd, vs1, vs2, i, env); \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * ESZ, \ + total_elems * ESZ); \ } RVVCALL(OPFVV2, vfadd_vv_h, OP_UUU_H, H2, H2, H2, float16_add) RVVCALL(OPFVV2, vfadd_vv_w, OP_UUU_W, H4, H4, H4, float32_add) RVVCALL(OPFVV2, vfadd_vv_d, OP_UUU_D, H8, H8, H8, float64_add) -GEN_VEXT_VV_ENV(vfadd_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfadd_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfadd_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfadd_vv_h, 2) +GEN_VEXT_VV_ENV(vfadd_vv_w, 4) +GEN_VEXT_VV_ENV(vfadd_vv_d, 8) #define OPFVF2(NAME, TD, T1, T2, TX1, TX2, HD, HS2, OP) \ static void do_##NAME(void *vd, uint64_t s1, void *vs2, int i, \ @@ -2839,43 +2910,55 @@ static void do_##NAME(void *vd, uint64_t s1, void *vs2, int i, \ *((TD *)vd + HD(i)) = OP(s2, (TX1)(T1)s1, &env->fp_status);\ } -#define GEN_VEXT_VF(NAME, ESZ, DSZ) \ +#define GEN_VEXT_VF(NAME, ESZ) \ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, \ void *vs2, CPURISCVState *env, \ uint32_t desc) \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t total_elems = \ + vext_get_total_elems(env, desc, ESZ); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * ESZ, \ + (i + 1) * ESZ); \ continue; \ } \ do_##NAME(vd, s1, vs2, i, env); \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * ESZ, \ + total_elems * ESZ); \ } RVVCALL(OPFVF2, vfadd_vf_h, OP_UUU_H, H2, H2, float16_add) RVVCALL(OPFVF2, vfadd_vf_w, OP_UUU_W, H4, H4, float32_add) RVVCALL(OPFVF2, vfadd_vf_d, OP_UUU_D, H8, H8, float64_add) -GEN_VEXT_VF(vfadd_vf_h, 2, 2) -GEN_VEXT_VF(vfadd_vf_w, 4, 4) -GEN_VEXT_VF(vfadd_vf_d, 8, 8) +GEN_VEXT_VF(vfadd_vf_h, 2) +GEN_VEXT_VF(vfadd_vf_w, 4) +GEN_VEXT_VF(vfadd_vf_d, 8) RVVCALL(OPFVV2, vfsub_vv_h, OP_UUU_H, H2, H2, H2, float16_sub) RVVCALL(OPFVV2, vfsub_vv_w, OP_UUU_W, H4, H4, H4, float32_sub) RVVCALL(OPFVV2, vfsub_vv_d, OP_UUU_D, H8, H8, H8, float64_sub) -GEN_VEXT_VV_ENV(vfsub_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfsub_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfsub_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfsub_vv_h, 2) +GEN_VEXT_VV_ENV(vfsub_vv_w, 4) +GEN_VEXT_VV_ENV(vfsub_vv_d, 8) RVVCALL(OPFVF2, vfsub_vf_h, OP_UUU_H, H2, H2, float16_sub) RVVCALL(OPFVF2, vfsub_vf_w, OP_UUU_W, H4, H4, float32_sub) RVVCALL(OPFVF2, vfsub_vf_d, OP_UUU_D, H8, H8, float64_sub) -GEN_VEXT_VF(vfsub_vf_h, 2, 2) -GEN_VEXT_VF(vfsub_vf_w, 4, 4) -GEN_VEXT_VF(vfsub_vf_d, 8, 8) +GEN_VEXT_VF(vfsub_vf_h, 2) +GEN_VEXT_VF(vfsub_vf_w, 4) +GEN_VEXT_VF(vfsub_vf_d, 8) static uint16_t float16_rsub(uint16_t a, uint16_t b, float_status *s) { @@ -2895,54 +2978,54 @@ static uint64_t float64_rsub(uint64_t a, uint64_t b, float_status *s) RVVCALL(OPFVF2, vfrsub_vf_h, OP_UUU_H, H2, H2, float16_rsub) RVVCALL(OPFVF2, vfrsub_vf_w, OP_UUU_W, H4, H4, float32_rsub) RVVCALL(OPFVF2, vfrsub_vf_d, OP_UUU_D, H8, H8, float64_rsub) -GEN_VEXT_VF(vfrsub_vf_h, 2, 2) -GEN_VEXT_VF(vfrsub_vf_w, 4, 4) -GEN_VEXT_VF(vfrsub_vf_d, 8, 8) +GEN_VEXT_VF(vfrsub_vf_h, 2) +GEN_VEXT_VF(vfrsub_vf_w, 4) +GEN_VEXT_VF(vfrsub_vf_d, 8) /* Vector Widening Floating-Point Add/Subtract Instructions */ static uint32_t vfwadd16(uint16_t a, uint16_t b, float_status *s) { return float32_add(float16_to_float32(a, true, s), - float16_to_float32(b, true, s), s); + float16_to_float32(b, true, s), s); } static uint64_t vfwadd32(uint32_t a, uint32_t b, float_status *s) { return float64_add(float32_to_float64(a, s), - float32_to_float64(b, s), s); + float32_to_float64(b, s), s); } RVVCALL(OPFVV2, vfwadd_vv_h, WOP_UUU_H, H4, H2, H2, vfwadd16) RVVCALL(OPFVV2, vfwadd_vv_w, WOP_UUU_W, H8, H4, H4, vfwadd32) -GEN_VEXT_VV_ENV(vfwadd_vv_h, 2, 4) -GEN_VEXT_VV_ENV(vfwadd_vv_w, 4, 8) +GEN_VEXT_VV_ENV(vfwadd_vv_h, 4) +GEN_VEXT_VV_ENV(vfwadd_vv_w, 8) RVVCALL(OPFVF2, vfwadd_vf_h, WOP_UUU_H, H4, H2, vfwadd16) RVVCALL(OPFVF2, vfwadd_vf_w, WOP_UUU_W, H8, H4, vfwadd32) -GEN_VEXT_VF(vfwadd_vf_h, 2, 4) -GEN_VEXT_VF(vfwadd_vf_w, 4, 8) +GEN_VEXT_VF(vfwadd_vf_h, 4) +GEN_VEXT_VF(vfwadd_vf_w, 8) static uint32_t vfwsub16(uint16_t a, uint16_t b, float_status *s) { return float32_sub(float16_to_float32(a, true, s), - float16_to_float32(b, true, s), s); + float16_to_float32(b, true, s), s); } static uint64_t vfwsub32(uint32_t a, uint32_t b, float_status *s) { return float64_sub(float32_to_float64(a, s), - float32_to_float64(b, s), s); + float32_to_float64(b, s), s); } RVVCALL(OPFVV2, vfwsub_vv_h, WOP_UUU_H, H4, H2, H2, vfwsub16) RVVCALL(OPFVV2, vfwsub_vv_w, WOP_UUU_W, H8, H4, H4, vfwsub32) -GEN_VEXT_VV_ENV(vfwsub_vv_h, 2, 4) -GEN_VEXT_VV_ENV(vfwsub_vv_w, 4, 8) +GEN_VEXT_VV_ENV(vfwsub_vv_h, 4) +GEN_VEXT_VV_ENV(vfwsub_vv_w, 8) RVVCALL(OPFVF2, vfwsub_vf_h, WOP_UUU_H, H4, H2, vfwsub16) RVVCALL(OPFVF2, vfwsub_vf_w, WOP_UUU_W, H8, H4, vfwsub32) -GEN_VEXT_VF(vfwsub_vf_h, 2, 4) -GEN_VEXT_VF(vfwsub_vf_w, 4, 8) +GEN_VEXT_VF(vfwsub_vf_h, 4) +GEN_VEXT_VF(vfwsub_vf_w, 8) static uint32_t vfwaddw16(uint32_t a, uint16_t b, float_status *s) { @@ -2956,12 +3039,12 @@ static uint64_t vfwaddw32(uint64_t a, uint32_t b, float_status *s) RVVCALL(OPFVV2, vfwadd_wv_h, WOP_WUUU_H, H4, H2, H2, vfwaddw16) RVVCALL(OPFVV2, vfwadd_wv_w, WOP_WUUU_W, H8, H4, H4, vfwaddw32) -GEN_VEXT_VV_ENV(vfwadd_wv_h, 2, 4) -GEN_VEXT_VV_ENV(vfwadd_wv_w, 4, 8) +GEN_VEXT_VV_ENV(vfwadd_wv_h, 4) +GEN_VEXT_VV_ENV(vfwadd_wv_w, 8) RVVCALL(OPFVF2, vfwadd_wf_h, WOP_WUUU_H, H4, H2, vfwaddw16) RVVCALL(OPFVF2, vfwadd_wf_w, WOP_WUUU_W, H8, H4, vfwaddw32) -GEN_VEXT_VF(vfwadd_wf_h, 2, 4) -GEN_VEXT_VF(vfwadd_wf_w, 4, 8) +GEN_VEXT_VF(vfwadd_wf_h, 4) +GEN_VEXT_VF(vfwadd_wf_w, 8) static uint32_t vfwsubw16(uint32_t a, uint16_t b, float_status *s) { @@ -2975,39 +3058,39 @@ static uint64_t vfwsubw32(uint64_t a, uint32_t b, float_status *s) RVVCALL(OPFVV2, vfwsub_wv_h, WOP_WUUU_H, H4, H2, H2, vfwsubw16) RVVCALL(OPFVV2, vfwsub_wv_w, WOP_WUUU_W, H8, H4, H4, vfwsubw32) -GEN_VEXT_VV_ENV(vfwsub_wv_h, 2, 4) -GEN_VEXT_VV_ENV(vfwsub_wv_w, 4, 8) +GEN_VEXT_VV_ENV(vfwsub_wv_h, 4) +GEN_VEXT_VV_ENV(vfwsub_wv_w, 8) RVVCALL(OPFVF2, vfwsub_wf_h, WOP_WUUU_H, H4, H2, vfwsubw16) RVVCALL(OPFVF2, vfwsub_wf_w, WOP_WUUU_W, H8, H4, vfwsubw32) -GEN_VEXT_VF(vfwsub_wf_h, 2, 4) -GEN_VEXT_VF(vfwsub_wf_w, 4, 8) +GEN_VEXT_VF(vfwsub_wf_h, 4) +GEN_VEXT_VF(vfwsub_wf_w, 8) /* Vector Single-Width Floating-Point Multiply/Divide Instructions */ RVVCALL(OPFVV2, vfmul_vv_h, OP_UUU_H, H2, H2, H2, float16_mul) RVVCALL(OPFVV2, vfmul_vv_w, OP_UUU_W, H4, H4, H4, float32_mul) RVVCALL(OPFVV2, vfmul_vv_d, OP_UUU_D, H8, H8, H8, float64_mul) -GEN_VEXT_VV_ENV(vfmul_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfmul_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfmul_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfmul_vv_h, 2) +GEN_VEXT_VV_ENV(vfmul_vv_w, 4) +GEN_VEXT_VV_ENV(vfmul_vv_d, 8) RVVCALL(OPFVF2, vfmul_vf_h, OP_UUU_H, H2, H2, float16_mul) RVVCALL(OPFVF2, vfmul_vf_w, OP_UUU_W, H4, H4, float32_mul) RVVCALL(OPFVF2, vfmul_vf_d, OP_UUU_D, H8, H8, float64_mul) -GEN_VEXT_VF(vfmul_vf_h, 2, 2) -GEN_VEXT_VF(vfmul_vf_w, 4, 4) -GEN_VEXT_VF(vfmul_vf_d, 8, 8) +GEN_VEXT_VF(vfmul_vf_h, 2) +GEN_VEXT_VF(vfmul_vf_w, 4) +GEN_VEXT_VF(vfmul_vf_d, 8) RVVCALL(OPFVV2, vfdiv_vv_h, OP_UUU_H, H2, H2, H2, float16_div) RVVCALL(OPFVV2, vfdiv_vv_w, OP_UUU_W, H4, H4, H4, float32_div) RVVCALL(OPFVV2, vfdiv_vv_d, OP_UUU_D, H8, H8, H8, float64_div) -GEN_VEXT_VV_ENV(vfdiv_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfdiv_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfdiv_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfdiv_vv_h, 2) +GEN_VEXT_VV_ENV(vfdiv_vv_w, 4) +GEN_VEXT_VV_ENV(vfdiv_vv_d, 8) RVVCALL(OPFVF2, vfdiv_vf_h, OP_UUU_H, H2, H2, float16_div) RVVCALL(OPFVF2, vfdiv_vf_w, OP_UUU_W, H4, H4, float32_div) RVVCALL(OPFVF2, vfdiv_vf_d, OP_UUU_D, H8, H8, float64_div) -GEN_VEXT_VF(vfdiv_vf_h, 2, 2) -GEN_VEXT_VF(vfdiv_vf_w, 4, 4) -GEN_VEXT_VF(vfdiv_vf_d, 8, 8) +GEN_VEXT_VF(vfdiv_vf_h, 2) +GEN_VEXT_VF(vfdiv_vf_w, 4) +GEN_VEXT_VF(vfdiv_vf_d, 8) static uint16_t float16_rdiv(uint16_t a, uint16_t b, float_status *s) { @@ -3027,36 +3110,36 @@ static uint64_t float64_rdiv(uint64_t a, uint64_t b, float_status *s) RVVCALL(OPFVF2, vfrdiv_vf_h, OP_UUU_H, H2, H2, float16_rdiv) RVVCALL(OPFVF2, vfrdiv_vf_w, OP_UUU_W, H4, H4, float32_rdiv) RVVCALL(OPFVF2, vfrdiv_vf_d, OP_UUU_D, H8, H8, float64_rdiv) -GEN_VEXT_VF(vfrdiv_vf_h, 2, 2) -GEN_VEXT_VF(vfrdiv_vf_w, 4, 4) -GEN_VEXT_VF(vfrdiv_vf_d, 8, 8) +GEN_VEXT_VF(vfrdiv_vf_h, 2) +GEN_VEXT_VF(vfrdiv_vf_w, 4) +GEN_VEXT_VF(vfrdiv_vf_d, 8) /* Vector Widening Floating-Point Multiply */ static uint32_t vfwmul16(uint16_t a, uint16_t b, float_status *s) { return float32_mul(float16_to_float32(a, true, s), - float16_to_float32(b, true, s), s); + float16_to_float32(b, true, s), s); } static uint64_t vfwmul32(uint32_t a, uint32_t b, float_status *s) { return float64_mul(float32_to_float64(a, s), - float32_to_float64(b, s), s); + float32_to_float64(b, s), s); } RVVCALL(OPFVV2, vfwmul_vv_h, WOP_UUU_H, H4, H2, H2, vfwmul16) RVVCALL(OPFVV2, vfwmul_vv_w, WOP_UUU_W, H8, H4, H4, vfwmul32) -GEN_VEXT_VV_ENV(vfwmul_vv_h, 2, 4) -GEN_VEXT_VV_ENV(vfwmul_vv_w, 4, 8) +GEN_VEXT_VV_ENV(vfwmul_vv_h, 4) +GEN_VEXT_VV_ENV(vfwmul_vv_w, 8) RVVCALL(OPFVF2, vfwmul_vf_h, WOP_UUU_H, H4, H2, vfwmul16) RVVCALL(OPFVF2, vfwmul_vf_w, WOP_UUU_W, H8, H4, vfwmul32) -GEN_VEXT_VF(vfwmul_vf_h, 2, 4) -GEN_VEXT_VF(vfwmul_vf_w, 4, 8) +GEN_VEXT_VF(vfwmul_vf_h, 4) +GEN_VEXT_VF(vfwmul_vf_w, 8) /* Vector Single-Width Floating-Point Fused Multiply-Add Instructions */ #define OPFVV3(NAME, TD, T1, T2, TX1, TX2, HD, HS1, HS2, OP) \ static void do_##NAME(void *vd, void *vs1, void *vs2, int i, \ - CPURISCVState *env) \ + CPURISCVState *env) \ { \ TX1 s1 = *((T1 *)vs1 + HS1(i)); \ TX2 s2 = *((T2 *)vs2 + HS2(i)); \ @@ -3082,13 +3165,13 @@ static uint64_t fmacc64(uint64_t a, uint64_t b, uint64_t d, float_status *s) RVVCALL(OPFVV3, vfmacc_vv_h, OP_UUU_H, H2, H2, H2, fmacc16) RVVCALL(OPFVV3, vfmacc_vv_w, OP_UUU_W, H4, H4, H4, fmacc32) RVVCALL(OPFVV3, vfmacc_vv_d, OP_UUU_D, H8, H8, H8, fmacc64) -GEN_VEXT_VV_ENV(vfmacc_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfmacc_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfmacc_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfmacc_vv_h, 2) +GEN_VEXT_VV_ENV(vfmacc_vv_w, 4) +GEN_VEXT_VV_ENV(vfmacc_vv_d, 8) #define OPFVF3(NAME, TD, T1, T2, TX1, TX2, HD, HS2, OP) \ static void do_##NAME(void *vd, uint64_t s1, void *vs2, int i, \ - CPURISCVState *env) \ + CPURISCVState *env) \ { \ TX2 s2 = *((T2 *)vs2 + HS2(i)); \ TD d = *((TD *)vd + HD(i)); \ @@ -3098,40 +3181,40 @@ static void do_##NAME(void *vd, uint64_t s1, void *vs2, int i, \ RVVCALL(OPFVF3, vfmacc_vf_h, OP_UUU_H, H2, H2, fmacc16) RVVCALL(OPFVF3, vfmacc_vf_w, OP_UUU_W, H4, H4, fmacc32) RVVCALL(OPFVF3, vfmacc_vf_d, OP_UUU_D, H8, H8, fmacc64) -GEN_VEXT_VF(vfmacc_vf_h, 2, 2) -GEN_VEXT_VF(vfmacc_vf_w, 4, 4) -GEN_VEXT_VF(vfmacc_vf_d, 8, 8) +GEN_VEXT_VF(vfmacc_vf_h, 2) +GEN_VEXT_VF(vfmacc_vf_w, 4) +GEN_VEXT_VF(vfmacc_vf_d, 8) static uint16_t fnmacc16(uint16_t a, uint16_t b, uint16_t d, float_status *s) { - return float16_muladd(a, b, d, - float_muladd_negate_c | float_muladd_negate_product, s); + return float16_muladd(a, b, d, float_muladd_negate_c | + float_muladd_negate_product, s); } static uint32_t fnmacc32(uint32_t a, uint32_t b, uint32_t d, float_status *s) { - return float32_muladd(a, b, d, - float_muladd_negate_c | float_muladd_negate_product, s); + return float32_muladd(a, b, d, float_muladd_negate_c | + float_muladd_negate_product, s); } static uint64_t fnmacc64(uint64_t a, uint64_t b, uint64_t d, float_status *s) { - return float64_muladd(a, b, d, - float_muladd_negate_c | float_muladd_negate_product, s); + return float64_muladd(a, b, d, float_muladd_negate_c | + float_muladd_negate_product, s); } RVVCALL(OPFVV3, vfnmacc_vv_h, OP_UUU_H, H2, H2, H2, fnmacc16) RVVCALL(OPFVV3, vfnmacc_vv_w, OP_UUU_W, H4, H4, H4, fnmacc32) RVVCALL(OPFVV3, vfnmacc_vv_d, OP_UUU_D, H8, H8, H8, fnmacc64) -GEN_VEXT_VV_ENV(vfnmacc_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfnmacc_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfnmacc_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfnmacc_vv_h, 2) +GEN_VEXT_VV_ENV(vfnmacc_vv_w, 4) +GEN_VEXT_VV_ENV(vfnmacc_vv_d, 8) RVVCALL(OPFVF3, vfnmacc_vf_h, OP_UUU_H, H2, H2, fnmacc16) RVVCALL(OPFVF3, vfnmacc_vf_w, OP_UUU_W, H4, H4, fnmacc32) RVVCALL(OPFVF3, vfnmacc_vf_d, OP_UUU_D, H8, H8, fnmacc64) -GEN_VEXT_VF(vfnmacc_vf_h, 2, 2) -GEN_VEXT_VF(vfnmacc_vf_w, 4, 4) -GEN_VEXT_VF(vfnmacc_vf_d, 8, 8) +GEN_VEXT_VF(vfnmacc_vf_h, 2) +GEN_VEXT_VF(vfnmacc_vf_w, 4) +GEN_VEXT_VF(vfnmacc_vf_d, 8) static uint16_t fmsac16(uint16_t a, uint16_t b, uint16_t d, float_status *s) { @@ -3151,15 +3234,15 @@ static uint64_t fmsac64(uint64_t a, uint64_t b, uint64_t d, float_status *s) RVVCALL(OPFVV3, vfmsac_vv_h, OP_UUU_H, H2, H2, H2, fmsac16) RVVCALL(OPFVV3, vfmsac_vv_w, OP_UUU_W, H4, H4, H4, fmsac32) RVVCALL(OPFVV3, vfmsac_vv_d, OP_UUU_D, H8, H8, H8, fmsac64) -GEN_VEXT_VV_ENV(vfmsac_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfmsac_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfmsac_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfmsac_vv_h, 2) +GEN_VEXT_VV_ENV(vfmsac_vv_w, 4) +GEN_VEXT_VV_ENV(vfmsac_vv_d, 8) RVVCALL(OPFVF3, vfmsac_vf_h, OP_UUU_H, H2, H2, fmsac16) RVVCALL(OPFVF3, vfmsac_vf_w, OP_UUU_W, H4, H4, fmsac32) RVVCALL(OPFVF3, vfmsac_vf_d, OP_UUU_D, H8, H8, fmsac64) -GEN_VEXT_VF(vfmsac_vf_h, 2, 2) -GEN_VEXT_VF(vfmsac_vf_w, 4, 4) -GEN_VEXT_VF(vfmsac_vf_d, 8, 8) +GEN_VEXT_VF(vfmsac_vf_h, 2) +GEN_VEXT_VF(vfmsac_vf_w, 4) +GEN_VEXT_VF(vfmsac_vf_d, 8) static uint16_t fnmsac16(uint16_t a, uint16_t b, uint16_t d, float_status *s) { @@ -3179,15 +3262,15 @@ static uint64_t fnmsac64(uint64_t a, uint64_t b, uint64_t d, float_status *s) RVVCALL(OPFVV3, vfnmsac_vv_h, OP_UUU_H, H2, H2, H2, fnmsac16) RVVCALL(OPFVV3, vfnmsac_vv_w, OP_UUU_W, H4, H4, H4, fnmsac32) RVVCALL(OPFVV3, vfnmsac_vv_d, OP_UUU_D, H8, H8, H8, fnmsac64) -GEN_VEXT_VV_ENV(vfnmsac_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfnmsac_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfnmsac_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfnmsac_vv_h, 2) +GEN_VEXT_VV_ENV(vfnmsac_vv_w, 4) +GEN_VEXT_VV_ENV(vfnmsac_vv_d, 8) RVVCALL(OPFVF3, vfnmsac_vf_h, OP_UUU_H, H2, H2, fnmsac16) RVVCALL(OPFVF3, vfnmsac_vf_w, OP_UUU_W, H4, H4, fnmsac32) RVVCALL(OPFVF3, vfnmsac_vf_d, OP_UUU_D, H8, H8, fnmsac64) -GEN_VEXT_VF(vfnmsac_vf_h, 2, 2) -GEN_VEXT_VF(vfnmsac_vf_w, 4, 4) -GEN_VEXT_VF(vfnmsac_vf_d, 8, 8) +GEN_VEXT_VF(vfnmsac_vf_h, 2) +GEN_VEXT_VF(vfnmsac_vf_w, 4) +GEN_VEXT_VF(vfnmsac_vf_d, 8) static uint16_t fmadd16(uint16_t a, uint16_t b, uint16_t d, float_status *s) { @@ -3207,46 +3290,46 @@ static uint64_t fmadd64(uint64_t a, uint64_t b, uint64_t d, float_status *s) RVVCALL(OPFVV3, vfmadd_vv_h, OP_UUU_H, H2, H2, H2, fmadd16) RVVCALL(OPFVV3, vfmadd_vv_w, OP_UUU_W, H4, H4, H4, fmadd32) RVVCALL(OPFVV3, vfmadd_vv_d, OP_UUU_D, H8, H8, H8, fmadd64) -GEN_VEXT_VV_ENV(vfmadd_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfmadd_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfmadd_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfmadd_vv_h, 2) +GEN_VEXT_VV_ENV(vfmadd_vv_w, 4) +GEN_VEXT_VV_ENV(vfmadd_vv_d, 8) RVVCALL(OPFVF3, vfmadd_vf_h, OP_UUU_H, H2, H2, fmadd16) RVVCALL(OPFVF3, vfmadd_vf_w, OP_UUU_W, H4, H4, fmadd32) RVVCALL(OPFVF3, vfmadd_vf_d, OP_UUU_D, H8, H8, fmadd64) -GEN_VEXT_VF(vfmadd_vf_h, 2, 2) -GEN_VEXT_VF(vfmadd_vf_w, 4, 4) -GEN_VEXT_VF(vfmadd_vf_d, 8, 8) +GEN_VEXT_VF(vfmadd_vf_h, 2) +GEN_VEXT_VF(vfmadd_vf_w, 4) +GEN_VEXT_VF(vfmadd_vf_d, 8) static uint16_t fnmadd16(uint16_t a, uint16_t b, uint16_t d, float_status *s) { - return float16_muladd(d, b, a, - float_muladd_negate_c | float_muladd_negate_product, s); + return float16_muladd(d, b, a, float_muladd_negate_c | + float_muladd_negate_product, s); } static uint32_t fnmadd32(uint32_t a, uint32_t b, uint32_t d, float_status *s) { - return float32_muladd(d, b, a, - float_muladd_negate_c | float_muladd_negate_product, s); + return float32_muladd(d, b, a, float_muladd_negate_c | + float_muladd_negate_product, s); } static uint64_t fnmadd64(uint64_t a, uint64_t b, uint64_t d, float_status *s) { - return float64_muladd(d, b, a, - float_muladd_negate_c | float_muladd_negate_product, s); + return float64_muladd(d, b, a, float_muladd_negate_c | + float_muladd_negate_product, s); } RVVCALL(OPFVV3, vfnmadd_vv_h, OP_UUU_H, H2, H2, H2, fnmadd16) RVVCALL(OPFVV3, vfnmadd_vv_w, OP_UUU_W, H4, H4, H4, fnmadd32) RVVCALL(OPFVV3, vfnmadd_vv_d, OP_UUU_D, H8, H8, H8, fnmadd64) -GEN_VEXT_VV_ENV(vfnmadd_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfnmadd_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfnmadd_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfnmadd_vv_h, 2) +GEN_VEXT_VV_ENV(vfnmadd_vv_w, 4) +GEN_VEXT_VV_ENV(vfnmadd_vv_d, 8) RVVCALL(OPFVF3, vfnmadd_vf_h, OP_UUU_H, H2, H2, fnmadd16) RVVCALL(OPFVF3, vfnmadd_vf_w, OP_UUU_W, H4, H4, fnmadd32) RVVCALL(OPFVF3, vfnmadd_vf_d, OP_UUU_D, H8, H8, fnmadd64) -GEN_VEXT_VF(vfnmadd_vf_h, 2, 2) -GEN_VEXT_VF(vfnmadd_vf_w, 4, 4) -GEN_VEXT_VF(vfnmadd_vf_d, 8, 8) +GEN_VEXT_VF(vfnmadd_vf_h, 2) +GEN_VEXT_VF(vfnmadd_vf_w, 4) +GEN_VEXT_VF(vfnmadd_vf_d, 8) static uint16_t fmsub16(uint16_t a, uint16_t b, uint16_t d, float_status *s) { @@ -3266,15 +3349,15 @@ static uint64_t fmsub64(uint64_t a, uint64_t b, uint64_t d, float_status *s) RVVCALL(OPFVV3, vfmsub_vv_h, OP_UUU_H, H2, H2, H2, fmsub16) RVVCALL(OPFVV3, vfmsub_vv_w, OP_UUU_W, H4, H4, H4, fmsub32) RVVCALL(OPFVV3, vfmsub_vv_d, OP_UUU_D, H8, H8, H8, fmsub64) -GEN_VEXT_VV_ENV(vfmsub_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfmsub_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfmsub_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfmsub_vv_h, 2) +GEN_VEXT_VV_ENV(vfmsub_vv_w, 4) +GEN_VEXT_VV_ENV(vfmsub_vv_d, 8) RVVCALL(OPFVF3, vfmsub_vf_h, OP_UUU_H, H2, H2, fmsub16) RVVCALL(OPFVF3, vfmsub_vf_w, OP_UUU_W, H4, H4, fmsub32) RVVCALL(OPFVF3, vfmsub_vf_d, OP_UUU_D, H8, H8, fmsub64) -GEN_VEXT_VF(vfmsub_vf_h, 2, 2) -GEN_VEXT_VF(vfmsub_vf_w, 4, 4) -GEN_VEXT_VF(vfmsub_vf_d, 8, 8) +GEN_VEXT_VF(vfmsub_vf_h, 2) +GEN_VEXT_VF(vfmsub_vf_w, 4) +GEN_VEXT_VF(vfmsub_vf_d, 8) static uint16_t fnmsub16(uint16_t a, uint16_t b, uint16_t d, float_status *s) { @@ -3294,147 +3377,165 @@ static uint64_t fnmsub64(uint64_t a, uint64_t b, uint64_t d, float_status *s) RVVCALL(OPFVV3, vfnmsub_vv_h, OP_UUU_H, H2, H2, H2, fnmsub16) RVVCALL(OPFVV3, vfnmsub_vv_w, OP_UUU_W, H4, H4, H4, fnmsub32) RVVCALL(OPFVV3, vfnmsub_vv_d, OP_UUU_D, H8, H8, H8, fnmsub64) -GEN_VEXT_VV_ENV(vfnmsub_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfnmsub_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfnmsub_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfnmsub_vv_h, 2) +GEN_VEXT_VV_ENV(vfnmsub_vv_w, 4) +GEN_VEXT_VV_ENV(vfnmsub_vv_d, 8) RVVCALL(OPFVF3, vfnmsub_vf_h, OP_UUU_H, H2, H2, fnmsub16) RVVCALL(OPFVF3, vfnmsub_vf_w, OP_UUU_W, H4, H4, fnmsub32) RVVCALL(OPFVF3, vfnmsub_vf_d, OP_UUU_D, H8, H8, fnmsub64) -GEN_VEXT_VF(vfnmsub_vf_h, 2, 2) -GEN_VEXT_VF(vfnmsub_vf_w, 4, 4) -GEN_VEXT_VF(vfnmsub_vf_d, 8, 8) +GEN_VEXT_VF(vfnmsub_vf_h, 2) +GEN_VEXT_VF(vfnmsub_vf_w, 4) +GEN_VEXT_VF(vfnmsub_vf_d, 8) /* Vector Widening Floating-Point Fused Multiply-Add Instructions */ static uint32_t fwmacc16(uint16_t a, uint16_t b, uint32_t d, float_status *s) { return float32_muladd(float16_to_float32(a, true, s), - float16_to_float32(b, true, s), d, 0, s); + float16_to_float32(b, true, s), d, 0, s); } static uint64_t fwmacc32(uint32_t a, uint32_t b, uint64_t d, float_status *s) { return float64_muladd(float32_to_float64(a, s), - float32_to_float64(b, s), d, 0, s); + float32_to_float64(b, s), d, 0, s); } RVVCALL(OPFVV3, vfwmacc_vv_h, WOP_UUU_H, H4, H2, H2, fwmacc16) RVVCALL(OPFVV3, vfwmacc_vv_w, WOP_UUU_W, H8, H4, H4, fwmacc32) -GEN_VEXT_VV_ENV(vfwmacc_vv_h, 2, 4) -GEN_VEXT_VV_ENV(vfwmacc_vv_w, 4, 8) +GEN_VEXT_VV_ENV(vfwmacc_vv_h, 4) +GEN_VEXT_VV_ENV(vfwmacc_vv_w, 8) RVVCALL(OPFVF3, vfwmacc_vf_h, WOP_UUU_H, H4, H2, fwmacc16) RVVCALL(OPFVF3, vfwmacc_vf_w, WOP_UUU_W, H8, H4, fwmacc32) -GEN_VEXT_VF(vfwmacc_vf_h, 2, 4) -GEN_VEXT_VF(vfwmacc_vf_w, 4, 8) +GEN_VEXT_VF(vfwmacc_vf_h, 4) +GEN_VEXT_VF(vfwmacc_vf_w, 8) + +static uint32_t fwmaccbf16(uint16_t a, uint16_t b, uint32_t d, float_status *s) +{ + return float32_muladd(bfloat16_to_float32(a, s), + bfloat16_to_float32(b, s), d, 0, s); +} + +RVVCALL(OPFVV3, vfwmaccbf16_vv, WOP_UUU_H, H4, H2, H2, fwmaccbf16) +GEN_VEXT_VV_ENV(vfwmaccbf16_vv, 4) +RVVCALL(OPFVF3, vfwmaccbf16_vf, WOP_UUU_H, H4, H2, fwmaccbf16) +GEN_VEXT_VF(vfwmaccbf16_vf, 4) static uint32_t fwnmacc16(uint16_t a, uint16_t b, uint32_t d, float_status *s) { return float32_muladd(float16_to_float32(a, true, s), - float16_to_float32(b, true, s), d, - float_muladd_negate_c | float_muladd_negate_product, s); + float16_to_float32(b, true, s), d, + float_muladd_negate_c | float_muladd_negate_product, + s); } static uint64_t fwnmacc32(uint32_t a, uint32_t b, uint64_t d, float_status *s) { - return float64_muladd(float32_to_float64(a, s), - float32_to_float64(b, s), d, - float_muladd_negate_c | float_muladd_negate_product, s); + return float64_muladd(float32_to_float64(a, s), float32_to_float64(b, s), + d, float_muladd_negate_c | + float_muladd_negate_product, s); } RVVCALL(OPFVV3, vfwnmacc_vv_h, WOP_UUU_H, H4, H2, H2, fwnmacc16) RVVCALL(OPFVV3, vfwnmacc_vv_w, WOP_UUU_W, H8, H4, H4, fwnmacc32) -GEN_VEXT_VV_ENV(vfwnmacc_vv_h, 2, 4) -GEN_VEXT_VV_ENV(vfwnmacc_vv_w, 4, 8) +GEN_VEXT_VV_ENV(vfwnmacc_vv_h, 4) +GEN_VEXT_VV_ENV(vfwnmacc_vv_w, 8) RVVCALL(OPFVF3, vfwnmacc_vf_h, WOP_UUU_H, H4, H2, fwnmacc16) RVVCALL(OPFVF3, vfwnmacc_vf_w, WOP_UUU_W, H8, H4, fwnmacc32) -GEN_VEXT_VF(vfwnmacc_vf_h, 2, 4) -GEN_VEXT_VF(vfwnmacc_vf_w, 4, 8) +GEN_VEXT_VF(vfwnmacc_vf_h, 4) +GEN_VEXT_VF(vfwnmacc_vf_w, 8) static uint32_t fwmsac16(uint16_t a, uint16_t b, uint32_t d, float_status *s) { return float32_muladd(float16_to_float32(a, true, s), - float16_to_float32(b, true, s), d, - float_muladd_negate_c, s); + float16_to_float32(b, true, s), d, + float_muladd_negate_c, s); } static uint64_t fwmsac32(uint32_t a, uint32_t b, uint64_t d, float_status *s) { return float64_muladd(float32_to_float64(a, s), - float32_to_float64(b, s), d, - float_muladd_negate_c, s); + float32_to_float64(b, s), d, + float_muladd_negate_c, s); } RVVCALL(OPFVV3, vfwmsac_vv_h, WOP_UUU_H, H4, H2, H2, fwmsac16) RVVCALL(OPFVV3, vfwmsac_vv_w, WOP_UUU_W, H8, H4, H4, fwmsac32) -GEN_VEXT_VV_ENV(vfwmsac_vv_h, 2, 4) -GEN_VEXT_VV_ENV(vfwmsac_vv_w, 4, 8) +GEN_VEXT_VV_ENV(vfwmsac_vv_h, 4) +GEN_VEXT_VV_ENV(vfwmsac_vv_w, 8) RVVCALL(OPFVF3, vfwmsac_vf_h, WOP_UUU_H, H4, H2, fwmsac16) RVVCALL(OPFVF3, vfwmsac_vf_w, WOP_UUU_W, H8, H4, fwmsac32) -GEN_VEXT_VF(vfwmsac_vf_h, 2, 4) -GEN_VEXT_VF(vfwmsac_vf_w, 4, 8) +GEN_VEXT_VF(vfwmsac_vf_h, 4) +GEN_VEXT_VF(vfwmsac_vf_w, 8) static uint32_t fwnmsac16(uint16_t a, uint16_t b, uint32_t d, float_status *s) { return float32_muladd(float16_to_float32(a, true, s), - float16_to_float32(b, true, s), d, - float_muladd_negate_product, s); + float16_to_float32(b, true, s), d, + float_muladd_negate_product, s); } static uint64_t fwnmsac32(uint32_t a, uint32_t b, uint64_t d, float_status *s) { return float64_muladd(float32_to_float64(a, s), - float32_to_float64(b, s), d, - float_muladd_negate_product, s); + float32_to_float64(b, s), d, + float_muladd_negate_product, s); } RVVCALL(OPFVV3, vfwnmsac_vv_h, WOP_UUU_H, H4, H2, H2, fwnmsac16) RVVCALL(OPFVV3, vfwnmsac_vv_w, WOP_UUU_W, H8, H4, H4, fwnmsac32) -GEN_VEXT_VV_ENV(vfwnmsac_vv_h, 2, 4) -GEN_VEXT_VV_ENV(vfwnmsac_vv_w, 4, 8) +GEN_VEXT_VV_ENV(vfwnmsac_vv_h, 4) +GEN_VEXT_VV_ENV(vfwnmsac_vv_w, 8) RVVCALL(OPFVF3, vfwnmsac_vf_h, WOP_UUU_H, H4, H2, fwnmsac16) RVVCALL(OPFVF3, vfwnmsac_vf_w, WOP_UUU_W, H8, H4, fwnmsac32) -GEN_VEXT_VF(vfwnmsac_vf_h, 2, 4) -GEN_VEXT_VF(vfwnmsac_vf_w, 4, 8) +GEN_VEXT_VF(vfwnmsac_vf_h, 4) +GEN_VEXT_VF(vfwnmsac_vf_w, 8) /* Vector Floating-Point Square-Root Instruction */ -/* (TD, T2, TX2) */ -#define OP_UU_H uint16_t, uint16_t, uint16_t -#define OP_UU_W uint32_t, uint32_t, uint32_t -#define OP_UU_D uint64_t, uint64_t, uint64_t - -#define OPFVV1(NAME, TD, T2, TX2, HD, HS2, OP) \ +#define OPFVV1(NAME, TD, T2, TX2, HD, HS2, OP) \ static void do_##NAME(void *vd, void *vs2, int i, \ - CPURISCVState *env) \ + CPURISCVState *env) \ { \ TX2 s2 = *((T2 *)vs2 + HS2(i)); \ *((TD *)vd + HD(i)) = OP(s2, &env->fp_status); \ } -#define GEN_VEXT_V_ENV(NAME, ESZ, DSZ) \ +#define GEN_VEXT_V_ENV(NAME, ESZ) \ void HELPER(NAME)(void *vd, void *v0, void *vs2, \ - CPURISCVState *env, uint32_t desc) \ + CPURISCVState *env, uint32_t desc) \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t total_elems = \ + vext_get_total_elems(env, desc, ESZ); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ if (vl == 0) { \ return; \ } \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * ESZ, \ + (i + 1) * ESZ); \ continue; \ } \ do_##NAME(vd, vs2, i, env); \ } \ env->vstart = 0; \ + vext_set_elems_1s(vd, vta, vl * ESZ, \ + total_elems * ESZ); \ } RVVCALL(OPFVV1, vfsqrt_v_h, OP_UU_H, H2, H2, float16_sqrt) RVVCALL(OPFVV1, vfsqrt_v_w, OP_UU_W, H4, H4, float32_sqrt) RVVCALL(OPFVV1, vfsqrt_v_d, OP_UU_D, H8, H8, float64_sqrt) -GEN_VEXT_V_ENV(vfsqrt_v_h, 2, 2) -GEN_VEXT_V_ENV(vfsqrt_v_w, 4, 4) -GEN_VEXT_V_ENV(vfsqrt_v_d, 8, 8) +GEN_VEXT_V_ENV(vfsqrt_v_h, 2) +GEN_VEXT_V_ENV(vfsqrt_v_w, 4) +GEN_VEXT_V_ENV(vfsqrt_v_d, 8) /* * Vector Floating-Point Reciprocal Square-Root Estimate Instruction @@ -3479,9 +3580,9 @@ static uint64_t frsqrt7(uint64_t f, int exp_size, int frac_size) } int idx = ((exp & 1) << (precision - 1)) | - (frac >> (frac_size - precision + 1)); + (frac >> (frac_size - precision + 1)); uint64_t out_frac = (uint64_t)(lookup_table[idx]) << - (frac_size - precision); + (frac_size - precision); uint64_t out_exp = (3 * MAKE_64BIT_MASK(0, exp_size - 1) + ~exp) / 2; uint64_t val = 0; @@ -3503,9 +3604,9 @@ static float16 frsqrt7_h(float16 f, float_status *s) * frsqrt7(-subnormal) = canonical NaN */ if (float16_is_signaling_nan(f, s) || - (float16_is_infinity(f) && sign) || - (float16_is_normal(f) && sign) || - (float16_is_zero_or_denormal(f) && !float16_is_zero(f) && sign)) { + (float16_is_infinity(f) && sign) || + (float16_is_normal(f) && sign) || + (float16_is_zero_or_denormal(f) && !float16_is_zero(f) && sign)) { s->float_exception_flags |= float_flag_invalid; return float16_default_nan(s); } @@ -3543,9 +3644,9 @@ static float32 frsqrt7_s(float32 f, float_status *s) * frsqrt7(-subnormal) = canonical NaN */ if (float32_is_signaling_nan(f, s) || - (float32_is_infinity(f) && sign) || - (float32_is_normal(f) && sign) || - (float32_is_zero_or_denormal(f) && !float32_is_zero(f) && sign)) { + (float32_is_infinity(f) && sign) || + (float32_is_normal(f) && sign) || + (float32_is_zero_or_denormal(f) && !float32_is_zero(f) && sign)) { s->float_exception_flags |= float_flag_invalid; return float32_default_nan(s); } @@ -3583,9 +3684,9 @@ static float64 frsqrt7_d(float64 f, float_status *s) * frsqrt7(-subnormal) = canonical NaN */ if (float64_is_signaling_nan(f, s) || - (float64_is_infinity(f) && sign) || - (float64_is_normal(f) && sign) || - (float64_is_zero_or_denormal(f) && !float64_is_zero(f) && sign)) { + (float64_is_infinity(f) && sign) || + (float64_is_normal(f) && sign) || + (float64_is_zero_or_denormal(f) && !float64_is_zero(f) && sign)) { s->float_exception_flags |= float_flag_invalid; return float64_default_nan(s); } @@ -3614,9 +3715,9 @@ static float64 frsqrt7_d(float64 f, float_status *s) RVVCALL(OPFVV1, vfrsqrt7_v_h, OP_UU_H, H2, H2, frsqrt7_h) RVVCALL(OPFVV1, vfrsqrt7_v_w, OP_UU_W, H4, H4, frsqrt7_s) RVVCALL(OPFVV1, vfrsqrt7_v_d, OP_UU_D, H8, H8, frsqrt7_d) -GEN_VEXT_V_ENV(vfrsqrt7_v_h, 2, 2) -GEN_VEXT_V_ENV(vfrsqrt7_v_w, 4, 4) -GEN_VEXT_V_ENV(vfrsqrt7_v_d, 8, 8) +GEN_VEXT_V_ENV(vfrsqrt7_v_h, 2) +GEN_VEXT_V_ENV(vfrsqrt7_v_w, 4) +GEN_VEXT_V_ENV(vfrsqrt7_v_d, 8) /* * Vector Floating-Point Reciprocal Estimate Instruction @@ -3673,18 +3774,18 @@ static uint64_t frec7(uint64_t f, int exp_size, int frac_size, ((s->float_rounding_mode == float_round_up) && sign)) { /* Return greatest/negative finite value. */ return (sign << (exp_size + frac_size)) | - (MAKE_64BIT_MASK(frac_size, exp_size) - 1); + (MAKE_64BIT_MASK(frac_size, exp_size) - 1); } else { /* Return +-inf. */ return (sign << (exp_size + frac_size)) | - MAKE_64BIT_MASK(frac_size, exp_size); + MAKE_64BIT_MASK(frac_size, exp_size); } } } int idx = frac >> (frac_size - precision); uint64_t out_frac = (uint64_t)(lookup_table[idx]) << - (frac_size - precision); + (frac_size - precision); uint64_t out_exp = 2 * MAKE_64BIT_MASK(0, exp_size - 1) + ~exp; if (out_exp == 0 || out_exp == UINT64_MAX) { @@ -3805,36 +3906,36 @@ static float64 frec7_d(float64 f, float_status *s) RVVCALL(OPFVV1, vfrec7_v_h, OP_UU_H, H2, H2, frec7_h) RVVCALL(OPFVV1, vfrec7_v_w, OP_UU_W, H4, H4, frec7_s) RVVCALL(OPFVV1, vfrec7_v_d, OP_UU_D, H8, H8, frec7_d) -GEN_VEXT_V_ENV(vfrec7_v_h, 2, 2) -GEN_VEXT_V_ENV(vfrec7_v_w, 4, 4) -GEN_VEXT_V_ENV(vfrec7_v_d, 8, 8) +GEN_VEXT_V_ENV(vfrec7_v_h, 2) +GEN_VEXT_V_ENV(vfrec7_v_w, 4) +GEN_VEXT_V_ENV(vfrec7_v_d, 8) /* Vector Floating-Point MIN/MAX Instructions */ RVVCALL(OPFVV2, vfmin_vv_h, OP_UUU_H, H2, H2, H2, float16_minimum_number) RVVCALL(OPFVV2, vfmin_vv_w, OP_UUU_W, H4, H4, H4, float32_minimum_number) RVVCALL(OPFVV2, vfmin_vv_d, OP_UUU_D, H8, H8, H8, float64_minimum_number) -GEN_VEXT_VV_ENV(vfmin_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfmin_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfmin_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfmin_vv_h, 2) +GEN_VEXT_VV_ENV(vfmin_vv_w, 4) +GEN_VEXT_VV_ENV(vfmin_vv_d, 8) RVVCALL(OPFVF2, vfmin_vf_h, OP_UUU_H, H2, H2, float16_minimum_number) RVVCALL(OPFVF2, vfmin_vf_w, OP_UUU_W, H4, H4, float32_minimum_number) RVVCALL(OPFVF2, vfmin_vf_d, OP_UUU_D, H8, H8, float64_minimum_number) -GEN_VEXT_VF(vfmin_vf_h, 2, 2) -GEN_VEXT_VF(vfmin_vf_w, 4, 4) -GEN_VEXT_VF(vfmin_vf_d, 8, 8) +GEN_VEXT_VF(vfmin_vf_h, 2) +GEN_VEXT_VF(vfmin_vf_w, 4) +GEN_VEXT_VF(vfmin_vf_d, 8) RVVCALL(OPFVV2, vfmax_vv_h, OP_UUU_H, H2, H2, H2, float16_maximum_number) RVVCALL(OPFVV2, vfmax_vv_w, OP_UUU_W, H4, H4, H4, float32_maximum_number) RVVCALL(OPFVV2, vfmax_vv_d, OP_UUU_D, H8, H8, H8, float64_maximum_number) -GEN_VEXT_VV_ENV(vfmax_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfmax_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfmax_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfmax_vv_h, 2) +GEN_VEXT_VV_ENV(vfmax_vv_w, 4) +GEN_VEXT_VV_ENV(vfmax_vv_d, 8) RVVCALL(OPFVF2, vfmax_vf_h, OP_UUU_H, H2, H2, float16_maximum_number) RVVCALL(OPFVF2, vfmax_vf_w, OP_UUU_W, H4, H4, float32_maximum_number) RVVCALL(OPFVF2, vfmax_vf_d, OP_UUU_D, H8, H8, float64_maximum_number) -GEN_VEXT_VF(vfmax_vf_h, 2, 2) -GEN_VEXT_VF(vfmax_vf_w, 4, 4) -GEN_VEXT_VF(vfmax_vf_d, 8, 8) +GEN_VEXT_VF(vfmax_vf_h, 2) +GEN_VEXT_VF(vfmax_vf_w, 4) +GEN_VEXT_VF(vfmax_vf_d, 8) /* Vector Floating-Point Sign-Injection Instructions */ static uint16_t fsgnj16(uint16_t a, uint16_t b, float_status *s) @@ -3855,15 +3956,15 @@ static uint64_t fsgnj64(uint64_t a, uint64_t b, float_status *s) RVVCALL(OPFVV2, vfsgnj_vv_h, OP_UUU_H, H2, H2, H2, fsgnj16) RVVCALL(OPFVV2, vfsgnj_vv_w, OP_UUU_W, H4, H4, H4, fsgnj32) RVVCALL(OPFVV2, vfsgnj_vv_d, OP_UUU_D, H8, H8, H8, fsgnj64) -GEN_VEXT_VV_ENV(vfsgnj_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfsgnj_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfsgnj_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfsgnj_vv_h, 2) +GEN_VEXT_VV_ENV(vfsgnj_vv_w, 4) +GEN_VEXT_VV_ENV(vfsgnj_vv_d, 8) RVVCALL(OPFVF2, vfsgnj_vf_h, OP_UUU_H, H2, H2, fsgnj16) RVVCALL(OPFVF2, vfsgnj_vf_w, OP_UUU_W, H4, H4, fsgnj32) RVVCALL(OPFVF2, vfsgnj_vf_d, OP_UUU_D, H8, H8, fsgnj64) -GEN_VEXT_VF(vfsgnj_vf_h, 2, 2) -GEN_VEXT_VF(vfsgnj_vf_w, 4, 4) -GEN_VEXT_VF(vfsgnj_vf_d, 8, 8) +GEN_VEXT_VF(vfsgnj_vf_h, 2) +GEN_VEXT_VF(vfsgnj_vf_w, 4) +GEN_VEXT_VF(vfsgnj_vf_d, 8) static uint16_t fsgnjn16(uint16_t a, uint16_t b, float_status *s) { @@ -3883,15 +3984,15 @@ static uint64_t fsgnjn64(uint64_t a, uint64_t b, float_status *s) RVVCALL(OPFVV2, vfsgnjn_vv_h, OP_UUU_H, H2, H2, H2, fsgnjn16) RVVCALL(OPFVV2, vfsgnjn_vv_w, OP_UUU_W, H4, H4, H4, fsgnjn32) RVVCALL(OPFVV2, vfsgnjn_vv_d, OP_UUU_D, H8, H8, H8, fsgnjn64) -GEN_VEXT_VV_ENV(vfsgnjn_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfsgnjn_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfsgnjn_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfsgnjn_vv_h, 2) +GEN_VEXT_VV_ENV(vfsgnjn_vv_w, 4) +GEN_VEXT_VV_ENV(vfsgnjn_vv_d, 8) RVVCALL(OPFVF2, vfsgnjn_vf_h, OP_UUU_H, H2, H2, fsgnjn16) RVVCALL(OPFVF2, vfsgnjn_vf_w, OP_UUU_W, H4, H4, fsgnjn32) RVVCALL(OPFVF2, vfsgnjn_vf_d, OP_UUU_D, H8, H8, fsgnjn64) -GEN_VEXT_VF(vfsgnjn_vf_h, 2, 2) -GEN_VEXT_VF(vfsgnjn_vf_w, 4, 4) -GEN_VEXT_VF(vfsgnjn_vf_d, 8, 8) +GEN_VEXT_VF(vfsgnjn_vf_h, 2) +GEN_VEXT_VF(vfsgnjn_vf_w, 4) +GEN_VEXT_VF(vfsgnjn_vf_d, 8) static uint16_t fsgnjx16(uint16_t a, uint16_t b, float_status *s) { @@ -3911,15 +4012,15 @@ static uint64_t fsgnjx64(uint64_t a, uint64_t b, float_status *s) RVVCALL(OPFVV2, vfsgnjx_vv_h, OP_UUU_H, H2, H2, H2, fsgnjx16) RVVCALL(OPFVV2, vfsgnjx_vv_w, OP_UUU_W, H4, H4, H4, fsgnjx32) RVVCALL(OPFVV2, vfsgnjx_vv_d, OP_UUU_D, H8, H8, H8, fsgnjx64) -GEN_VEXT_VV_ENV(vfsgnjx_vv_h, 2, 2) -GEN_VEXT_VV_ENV(vfsgnjx_vv_w, 4, 4) -GEN_VEXT_VV_ENV(vfsgnjx_vv_d, 8, 8) +GEN_VEXT_VV_ENV(vfsgnjx_vv_h, 2) +GEN_VEXT_VV_ENV(vfsgnjx_vv_w, 4) +GEN_VEXT_VV_ENV(vfsgnjx_vv_d, 8) RVVCALL(OPFVF2, vfsgnjx_vf_h, OP_UUU_H, H2, H2, fsgnjx16) RVVCALL(OPFVF2, vfsgnjx_vf_w, OP_UUU_W, H4, H4, fsgnjx32) RVVCALL(OPFVF2, vfsgnjx_vf_d, OP_UUU_D, H8, H8, fsgnjx64) -GEN_VEXT_VF(vfsgnjx_vf_h, 2, 2) -GEN_VEXT_VF(vfsgnjx_vf_w, 4, 4) -GEN_VEXT_VF(vfsgnjx_vf_d, 8, 8) +GEN_VEXT_VF(vfsgnjx_vf_h, 2) +GEN_VEXT_VF(vfsgnjx_vf_w, 4) +GEN_VEXT_VF(vfsgnjx_vf_d, 8) /* Vector Floating-Point Compare Instructions */ #define GEN_VEXT_CMP_VV_ENV(NAME, ETYPE, H, DO_OP) \ @@ -3928,18 +4029,36 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t total_elems = riscv_cpu_cfg(env)->vlenb << 3; \ + uint32_t vta_all_1s = vext_vta_all_1s(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + if (vma) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ continue; \ } \ vext_set_elem_mask(vd, i, \ DO_OP(s2, s1, &env->fp_status)); \ } \ env->vstart = 0; \ + /* + * mask destination register are always tail-agnostic + * set tail elements to 1s + */ \ + if (vta_all_1s) { \ + for (; i < total_elems; i++) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ + } \ } GEN_VEXT_CMP_VV_ENV(vmfeq_vv_h, uint16_t, H2, float16_eq_quiet) @@ -3952,17 +4071,35 @@ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t total_elems = riscv_cpu_cfg(env)->vlenb << 3; \ + uint32_t vta_all_1s = vext_vta_all_1s(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + if (vma) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ continue; \ } \ vext_set_elem_mask(vd, i, \ DO_OP(s2, (ETYPE)s1, &env->fp_status)); \ } \ env->vstart = 0; \ + /* + * mask destination register are always tail-agnostic + * set tail elements to 1s + */ \ + if (vta_all_1s) { \ + for (; i < total_elems; i++) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ + } \ } GEN_VEXT_CMP_VF(vmfeq_vf_h, uint16_t, H2, float16_eq_quiet) @@ -4056,30 +4193,6 @@ GEN_VEXT_CMP_VF(vmfge_vf_w, uint32_t, H4, vmfge32) GEN_VEXT_CMP_VF(vmfge_vf_d, uint64_t, H8, vmfge64) /* Vector Floating-Point Classify Instruction */ -#define OPIVV1(NAME, TD, T2, TX2, HD, HS2, OP) \ -static void do_##NAME(void *vd, void *vs2, int i) \ -{ \ - TX2 s2 = *((T2 *)vs2 + HS2(i)); \ - *((TD *)vd + HD(i)) = OP(s2); \ -} - -#define GEN_VEXT_V(NAME, ESZ, DSZ) \ -void HELPER(NAME)(void *vd, void *v0, void *vs2, \ - CPURISCVState *env, uint32_t desc) \ -{ \ - uint32_t vm = vext_vm(desc); \ - uint32_t vl = env->vl; \ - uint32_t i; \ - \ - for (i = env->vstart; i < vl; i++) { \ - if (!vm && !vext_elem_mask(v0, i)) { \ - continue; \ - } \ - do_##NAME(vd, vs2, i); \ - } \ - env->vstart = 0; \ -} - target_ulong fclass_h(uint64_t frs1) { float16 f = frs1; @@ -4140,25 +4253,34 @@ target_ulong fclass_d(uint64_t frs1) RVVCALL(OPIVV1, vfclass_v_h, OP_UU_H, H2, H2, fclass_h) RVVCALL(OPIVV1, vfclass_v_w, OP_UU_W, H4, H4, fclass_s) RVVCALL(OPIVV1, vfclass_v_d, OP_UU_D, H8, H8, fclass_d) -GEN_VEXT_V(vfclass_v_h, 2, 2) -GEN_VEXT_V(vfclass_v_w, 4, 4) -GEN_VEXT_V(vfclass_v_d, 8, 8) +GEN_VEXT_V(vfclass_v_h, 2) +GEN_VEXT_V(vfclass_v_w, 4) +GEN_VEXT_V(vfclass_v_d, 8) /* Vector Floating-Point Merge Instruction */ + #define GEN_VFMERGE_VF(NAME, ETYPE, H) \ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ CPURISCVState *env, uint32_t desc) \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = \ + vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ - *((ETYPE *)vd + H(i)) \ - = (!vm && !vext_elem_mask(v0, i) ? s2 : s1); \ + *((ETYPE *)vd + H(i)) = \ + (!vm && !vext_elem_mask(v0, i) ? s2 : s1); \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } GEN_VFMERGE_VF(vfmerge_vfm_h, int16_t, H2) @@ -4170,70 +4292,73 @@ GEN_VFMERGE_VF(vfmerge_vfm_d, int64_t, H8) RVVCALL(OPFVV1, vfcvt_xu_f_v_h, OP_UU_H, H2, H2, float16_to_uint16) RVVCALL(OPFVV1, vfcvt_xu_f_v_w, OP_UU_W, H4, H4, float32_to_uint32) RVVCALL(OPFVV1, vfcvt_xu_f_v_d, OP_UU_D, H8, H8, float64_to_uint64) -GEN_VEXT_V_ENV(vfcvt_xu_f_v_h, 2, 2) -GEN_VEXT_V_ENV(vfcvt_xu_f_v_w, 4, 4) -GEN_VEXT_V_ENV(vfcvt_xu_f_v_d, 8, 8) +GEN_VEXT_V_ENV(vfcvt_xu_f_v_h, 2) +GEN_VEXT_V_ENV(vfcvt_xu_f_v_w, 4) +GEN_VEXT_V_ENV(vfcvt_xu_f_v_d, 8) /* vfcvt.x.f.v vd, vs2, vm # Convert float to signed integer. */ RVVCALL(OPFVV1, vfcvt_x_f_v_h, OP_UU_H, H2, H2, float16_to_int16) RVVCALL(OPFVV1, vfcvt_x_f_v_w, OP_UU_W, H4, H4, float32_to_int32) RVVCALL(OPFVV1, vfcvt_x_f_v_d, OP_UU_D, H8, H8, float64_to_int64) -GEN_VEXT_V_ENV(vfcvt_x_f_v_h, 2, 2) -GEN_VEXT_V_ENV(vfcvt_x_f_v_w, 4, 4) -GEN_VEXT_V_ENV(vfcvt_x_f_v_d, 8, 8) +GEN_VEXT_V_ENV(vfcvt_x_f_v_h, 2) +GEN_VEXT_V_ENV(vfcvt_x_f_v_w, 4) +GEN_VEXT_V_ENV(vfcvt_x_f_v_d, 8) /* vfcvt.f.xu.v vd, vs2, vm # Convert unsigned integer to float. */ RVVCALL(OPFVV1, vfcvt_f_xu_v_h, OP_UU_H, H2, H2, uint16_to_float16) RVVCALL(OPFVV1, vfcvt_f_xu_v_w, OP_UU_W, H4, H4, uint32_to_float32) RVVCALL(OPFVV1, vfcvt_f_xu_v_d, OP_UU_D, H8, H8, uint64_to_float64) -GEN_VEXT_V_ENV(vfcvt_f_xu_v_h, 2, 2) -GEN_VEXT_V_ENV(vfcvt_f_xu_v_w, 4, 4) -GEN_VEXT_V_ENV(vfcvt_f_xu_v_d, 8, 8) +GEN_VEXT_V_ENV(vfcvt_f_xu_v_h, 2) +GEN_VEXT_V_ENV(vfcvt_f_xu_v_w, 4) +GEN_VEXT_V_ENV(vfcvt_f_xu_v_d, 8) /* vfcvt.f.x.v vd, vs2, vm # Convert integer to float. */ RVVCALL(OPFVV1, vfcvt_f_x_v_h, OP_UU_H, H2, H2, int16_to_float16) RVVCALL(OPFVV1, vfcvt_f_x_v_w, OP_UU_W, H4, H4, int32_to_float32) RVVCALL(OPFVV1, vfcvt_f_x_v_d, OP_UU_D, H8, H8, int64_to_float64) -GEN_VEXT_V_ENV(vfcvt_f_x_v_h, 2, 2) -GEN_VEXT_V_ENV(vfcvt_f_x_v_w, 4, 4) -GEN_VEXT_V_ENV(vfcvt_f_x_v_d, 8, 8) +GEN_VEXT_V_ENV(vfcvt_f_x_v_h, 2) +GEN_VEXT_V_ENV(vfcvt_f_x_v_w, 4) +GEN_VEXT_V_ENV(vfcvt_f_x_v_d, 8) /* Widening Floating-Point/Integer Type-Convert Instructions */ /* (TD, T2, TX2) */ #define WOP_UU_B uint16_t, uint8_t, uint8_t #define WOP_UU_H uint32_t, uint16_t, uint16_t #define WOP_UU_W uint64_t, uint32_t, uint32_t -/* vfwcvt.xu.f.v vd, vs2, vm # Convert float to double-width unsigned integer.*/ +/* + * vfwcvt.xu.f.v vd, vs2, vm # Convert float to double-width unsigned integer. + */ RVVCALL(OPFVV1, vfwcvt_xu_f_v_h, WOP_UU_H, H4, H2, float16_to_uint32) RVVCALL(OPFVV1, vfwcvt_xu_f_v_w, WOP_UU_W, H8, H4, float32_to_uint64) -GEN_VEXT_V_ENV(vfwcvt_xu_f_v_h, 2, 4) -GEN_VEXT_V_ENV(vfwcvt_xu_f_v_w, 4, 8) +GEN_VEXT_V_ENV(vfwcvt_xu_f_v_h, 4) +GEN_VEXT_V_ENV(vfwcvt_xu_f_v_w, 8) /* vfwcvt.x.f.v vd, vs2, vm # Convert float to double-width signed integer. */ RVVCALL(OPFVV1, vfwcvt_x_f_v_h, WOP_UU_H, H4, H2, float16_to_int32) RVVCALL(OPFVV1, vfwcvt_x_f_v_w, WOP_UU_W, H8, H4, float32_to_int64) -GEN_VEXT_V_ENV(vfwcvt_x_f_v_h, 2, 4) -GEN_VEXT_V_ENV(vfwcvt_x_f_v_w, 4, 8) +GEN_VEXT_V_ENV(vfwcvt_x_f_v_h, 4) +GEN_VEXT_V_ENV(vfwcvt_x_f_v_w, 8) -/* vfwcvt.f.xu.v vd, vs2, vm # Convert unsigned integer to double-width float */ +/* + * vfwcvt.f.xu.v vd, vs2, vm # Convert unsigned integer to double-width float. + */ RVVCALL(OPFVV1, vfwcvt_f_xu_v_b, WOP_UU_B, H2, H1, uint8_to_float16) RVVCALL(OPFVV1, vfwcvt_f_xu_v_h, WOP_UU_H, H4, H2, uint16_to_float32) RVVCALL(OPFVV1, vfwcvt_f_xu_v_w, WOP_UU_W, H8, H4, uint32_to_float64) -GEN_VEXT_V_ENV(vfwcvt_f_xu_v_b, 1, 2) -GEN_VEXT_V_ENV(vfwcvt_f_xu_v_h, 2, 4) -GEN_VEXT_V_ENV(vfwcvt_f_xu_v_w, 4, 8) +GEN_VEXT_V_ENV(vfwcvt_f_xu_v_b, 2) +GEN_VEXT_V_ENV(vfwcvt_f_xu_v_h, 4) +GEN_VEXT_V_ENV(vfwcvt_f_xu_v_w, 8) /* vfwcvt.f.x.v vd, vs2, vm # Convert integer to double-width float. */ RVVCALL(OPFVV1, vfwcvt_f_x_v_b, WOP_UU_B, H2, H1, int8_to_float16) RVVCALL(OPFVV1, vfwcvt_f_x_v_h, WOP_UU_H, H4, H2, int16_to_float32) RVVCALL(OPFVV1, vfwcvt_f_x_v_w, WOP_UU_W, H8, H4, int32_to_float64) -GEN_VEXT_V_ENV(vfwcvt_f_x_v_b, 1, 2) -GEN_VEXT_V_ENV(vfwcvt_f_x_v_h, 2, 4) -GEN_VEXT_V_ENV(vfwcvt_f_x_v_w, 4, 8) +GEN_VEXT_V_ENV(vfwcvt_f_x_v_b, 2) +GEN_VEXT_V_ENV(vfwcvt_f_x_v_h, 4) +GEN_VEXT_V_ENV(vfwcvt_f_x_v_w, 8) /* - * vfwcvt.f.f.v vd, vs2, vm - * Convert single-width float to double-width float. + * vfwcvt.f.f.v vd, vs2, vm # Convert single-width float to double-width float. */ static uint32_t vfwcvtffv16(uint16_t a, float_status *s) { @@ -4242,8 +4367,11 @@ static uint32_t vfwcvtffv16(uint16_t a, float_status *s) RVVCALL(OPFVV1, vfwcvt_f_f_v_h, WOP_UU_H, H4, H2, vfwcvtffv16) RVVCALL(OPFVV1, vfwcvt_f_f_v_w, WOP_UU_W, H8, H4, float32_to_float64) -GEN_VEXT_V_ENV(vfwcvt_f_f_v_h, 2, 4) -GEN_VEXT_V_ENV(vfwcvt_f_f_v_w, 4, 8) +GEN_VEXT_V_ENV(vfwcvt_f_f_v_h, 4) +GEN_VEXT_V_ENV(vfwcvt_f_f_v_w, 8) + +RVVCALL(OPFVV1, vfwcvtbf16_f_f_v, WOP_UU_H, H4, H2, bfloat16_to_float32) +GEN_VEXT_V_ENV(vfwcvtbf16_f_f_v, 4) /* Narrowing Floating-Point/Integer Type-Convert Instructions */ /* (TD, T2, TX2) */ @@ -4254,29 +4382,31 @@ GEN_VEXT_V_ENV(vfwcvt_f_f_v_w, 4, 8) RVVCALL(OPFVV1, vfncvt_xu_f_w_b, NOP_UU_B, H1, H2, float16_to_uint8) RVVCALL(OPFVV1, vfncvt_xu_f_w_h, NOP_UU_H, H2, H4, float32_to_uint16) RVVCALL(OPFVV1, vfncvt_xu_f_w_w, NOP_UU_W, H4, H8, float64_to_uint32) -GEN_VEXT_V_ENV(vfncvt_xu_f_w_b, 1, 1) -GEN_VEXT_V_ENV(vfncvt_xu_f_w_h, 2, 2) -GEN_VEXT_V_ENV(vfncvt_xu_f_w_w, 4, 4) +GEN_VEXT_V_ENV(vfncvt_xu_f_w_b, 1) +GEN_VEXT_V_ENV(vfncvt_xu_f_w_h, 2) +GEN_VEXT_V_ENV(vfncvt_xu_f_w_w, 4) /* vfncvt.x.f.v vd, vs2, vm # Convert double-width float to signed integer. */ RVVCALL(OPFVV1, vfncvt_x_f_w_b, NOP_UU_B, H1, H2, float16_to_int8) RVVCALL(OPFVV1, vfncvt_x_f_w_h, NOP_UU_H, H2, H4, float32_to_int16) RVVCALL(OPFVV1, vfncvt_x_f_w_w, NOP_UU_W, H4, H8, float64_to_int32) -GEN_VEXT_V_ENV(vfncvt_x_f_w_b, 1, 1) -GEN_VEXT_V_ENV(vfncvt_x_f_w_h, 2, 2) -GEN_VEXT_V_ENV(vfncvt_x_f_w_w, 4, 4) +GEN_VEXT_V_ENV(vfncvt_x_f_w_b, 1) +GEN_VEXT_V_ENV(vfncvt_x_f_w_h, 2) +GEN_VEXT_V_ENV(vfncvt_x_f_w_w, 4) -/* vfncvt.f.xu.v vd, vs2, vm # Convert double-width unsigned integer to float */ +/* + * vfncvt.f.xu.v vd, vs2, vm # Convert double-width unsigned integer to float. + */ RVVCALL(OPFVV1, vfncvt_f_xu_w_h, NOP_UU_H, H2, H4, uint32_to_float16) RVVCALL(OPFVV1, vfncvt_f_xu_w_w, NOP_UU_W, H4, H8, uint64_to_float32) -GEN_VEXT_V_ENV(vfncvt_f_xu_w_h, 2, 2) -GEN_VEXT_V_ENV(vfncvt_f_xu_w_w, 4, 4) +GEN_VEXT_V_ENV(vfncvt_f_xu_w_h, 2) +GEN_VEXT_V_ENV(vfncvt_f_xu_w_w, 4) /* vfncvt.f.x.v vd, vs2, vm # Convert double-width integer to float. */ RVVCALL(OPFVV1, vfncvt_f_x_w_h, NOP_UU_H, H2, H4, int32_to_float16) RVVCALL(OPFVV1, vfncvt_f_x_w_w, NOP_UU_W, H4, H8, int64_to_float32) -GEN_VEXT_V_ENV(vfncvt_f_x_w_h, 2, 2) -GEN_VEXT_V_ENV(vfncvt_f_x_w_w, 4, 4) +GEN_VEXT_V_ENV(vfncvt_f_x_w_h, 2) +GEN_VEXT_V_ENV(vfncvt_f_x_w_w, 4) /* vfncvt.f.f.v vd, vs2, vm # Convert double float to single-width float. */ static uint16_t vfncvtffv16(uint32_t a, float_status *s) @@ -4286,19 +4416,26 @@ static uint16_t vfncvtffv16(uint32_t a, float_status *s) RVVCALL(OPFVV1, vfncvt_f_f_w_h, NOP_UU_H, H2, H4, vfncvtffv16) RVVCALL(OPFVV1, vfncvt_f_f_w_w, NOP_UU_W, H4, H8, float64_to_float32) -GEN_VEXT_V_ENV(vfncvt_f_f_w_h, 2, 2) -GEN_VEXT_V_ENV(vfncvt_f_f_w_w, 4, 4) +GEN_VEXT_V_ENV(vfncvt_f_f_w_h, 2) +GEN_VEXT_V_ENV(vfncvt_f_f_w_w, 4) + +RVVCALL(OPFVV1, vfncvtbf16_f_f_w, NOP_UU_H, H2, H4, float32_to_bfloat16) +GEN_VEXT_V_ENV(vfncvtbf16_f_f_w, 2) /* - *** Vector Reduction Operations + * Vector Reduction Operations */ /* Vector Single-Width Integer Reduction Instructions */ #define GEN_VEXT_RED(NAME, TD, TS2, HD, HS2, OP) \ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ - void *vs2, CPURISCVState *env, uint32_t desc) \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(TD); \ + uint32_t vlenb = simd_maxsz(desc); \ + uint32_t vta = vext_vta(desc); \ uint32_t i; \ TD s1 = *((TD *)vs1 + HD(0)); \ \ @@ -4311,6 +4448,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ } \ *((TD *)vd + HD(0)) = s1; \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, esz, vlenb); \ } /* vd[0] = sum(vs1[0], vs2[*]) */ @@ -4380,6 +4519,9 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(TD); \ + uint32_t vlenb = simd_maxsz(desc); \ + uint32_t vta = vext_vta(desc); \ uint32_t i; \ TD s1 = *((TD *)vs1 + HD(0)); \ \ @@ -4392,67 +4534,56 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ } \ *((TD *)vd + HD(0)) = s1; \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, esz, vlenb); \ } /* Unordered sum */ -GEN_VEXT_FRED(vfredsum_vs_h, uint16_t, uint16_t, H2, H2, float16_add) -GEN_VEXT_FRED(vfredsum_vs_w, uint32_t, uint32_t, H4, H4, float32_add) -GEN_VEXT_FRED(vfredsum_vs_d, uint64_t, uint64_t, H8, H8, float64_add) +GEN_VEXT_FRED(vfredusum_vs_h, uint16_t, uint16_t, H2, H2, float16_add) +GEN_VEXT_FRED(vfredusum_vs_w, uint32_t, uint32_t, H4, H4, float32_add) +GEN_VEXT_FRED(vfredusum_vs_d, uint64_t, uint64_t, H8, H8, float64_add) + +/* Ordered sum */ +GEN_VEXT_FRED(vfredosum_vs_h, uint16_t, uint16_t, H2, H2, float16_add) +GEN_VEXT_FRED(vfredosum_vs_w, uint32_t, uint32_t, H4, H4, float32_add) +GEN_VEXT_FRED(vfredosum_vs_d, uint64_t, uint64_t, H8, H8, float64_add) /* Maximum value */ -GEN_VEXT_FRED(vfredmax_vs_h, uint16_t, uint16_t, H2, H2, float16_maximum_number) -GEN_VEXT_FRED(vfredmax_vs_w, uint32_t, uint32_t, H4, H4, float32_maximum_number) -GEN_VEXT_FRED(vfredmax_vs_d, uint64_t, uint64_t, H8, H8, float64_maximum_number) +GEN_VEXT_FRED(vfredmax_vs_h, uint16_t, uint16_t, H2, H2, + float16_maximum_number) +GEN_VEXT_FRED(vfredmax_vs_w, uint32_t, uint32_t, H4, H4, + float32_maximum_number) +GEN_VEXT_FRED(vfredmax_vs_d, uint64_t, uint64_t, H8, H8, + float64_maximum_number) /* Minimum value */ -GEN_VEXT_FRED(vfredmin_vs_h, uint16_t, uint16_t, H2, H2, float16_minimum_number) -GEN_VEXT_FRED(vfredmin_vs_w, uint32_t, uint32_t, H4, H4, float32_minimum_number) -GEN_VEXT_FRED(vfredmin_vs_d, uint64_t, uint64_t, H8, H8, float64_minimum_number) +GEN_VEXT_FRED(vfredmin_vs_h, uint16_t, uint16_t, H2, H2, + float16_minimum_number) +GEN_VEXT_FRED(vfredmin_vs_w, uint32_t, uint32_t, H4, H4, + float32_minimum_number) +GEN_VEXT_FRED(vfredmin_vs_d, uint64_t, uint64_t, H8, H8, + float64_minimum_number) + +/* Vector Widening Floating-Point Add Instructions */ +static uint32_t fwadd16(uint32_t a, uint16_t b, float_status *s) +{ + return float32_add(a, float16_to_float32(b, true, s), s); +} + +static uint64_t fwadd32(uint64_t a, uint32_t b, float_status *s) +{ + return float64_add(a, float32_to_float64(b, s), s); +} /* Vector Widening Floating-Point Reduction Instructions */ -/* Unordered reduce 2*SEW = 2*SEW + sum(promote(SEW)) */ -void HELPER(vfwredsum_vs_h)(void *vd, void *v0, void *vs1, - void *vs2, CPURISCVState *env, uint32_t desc) -{ - uint32_t vm = vext_vm(desc); - uint32_t vl = env->vl; - uint32_t i; - uint32_t s1 = *((uint32_t *)vs1 + H4(0)); - - for (i = env->vstart; i < vl; i++) { - uint16_t s2 = *((uint16_t *)vs2 + H2(i)); - if (!vm && !vext_elem_mask(v0, i)) { - continue; - } - s1 = float32_add(s1, float16_to_float32(s2, true, &env->fp_status), - &env->fp_status); - } - *((uint32_t *)vd + H4(0)) = s1; - env->vstart = 0; -} - -void HELPER(vfwredsum_vs_w)(void *vd, void *v0, void *vs1, - void *vs2, CPURISCVState *env, uint32_t desc) -{ - uint32_t vm = vext_vm(desc); - uint32_t vl = env->vl; - uint32_t i; - uint64_t s1 = *((uint64_t *)vs1); - - for (i = env->vstart; i < vl; i++) { - uint32_t s2 = *((uint32_t *)vs2 + H4(i)); - if (!vm && !vext_elem_mask(v0, i)) { - continue; - } - s1 = float64_add(s1, float32_to_float64(s2, &env->fp_status), - &env->fp_status); - } - *((uint64_t *)vd) = s1; - env->vstart = 0; -} +/* Ordered/unordered reduce 2*SEW = 2*SEW + sum(promote(SEW)) */ +GEN_VEXT_FRED(vfwredusum_vs_h, uint32_t, uint16_t, H4, H2, fwadd16) +GEN_VEXT_FRED(vfwredusum_vs_w, uint64_t, uint32_t, H8, H4, fwadd32) +GEN_VEXT_FRED(vfwredosum_vs_h, uint32_t, uint16_t, H4, H2, fwadd16) +GEN_VEXT_FRED(vfwredosum_vs_w, uint64_t, uint32_t, H8, H4, fwadd32) /* - *** Vector Mask Operations + * Vector Mask Operations */ /* Vector Mask-Register Logical Instructions */ #define GEN_VEXT_MASK_VV(NAME, OP) \ @@ -4461,15 +4592,28 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ uint32_t desc) \ { \ uint32_t vl = env->vl; \ + uint32_t total_elems = riscv_cpu_cfg(env)->vlenb << 3;\ + uint32_t vta_all_1s = vext_vta_all_1s(desc); \ uint32_t i; \ int a, b; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ a = vext_elem_mask(vs1, i); \ b = vext_elem_mask(vs2, i); \ vext_set_elem_mask(vd, i, OP(b, a)); \ } \ env->vstart = 0; \ + /* + * mask destination register are always tail-agnostic + * set tail elements to 1s + */ \ + if (vta_all_1s) { \ + for (; i < total_elems; i++) { \ + vext_set_elem_mask(vd, i, 1); \ + } \ + } \ } #define DO_NAND(N, M) (!(N & M)) @@ -4507,7 +4651,7 @@ target_ulong HELPER(vcpop_m)(void *v0, void *vs2, CPURISCVState *env, return cnt; } -/* vfirst find-first-set mask bit*/ +/* vfirst find-first-set mask bit */ target_ulong HELPER(vfirst_m)(void *v0, void *vs2, CPURISCVState *env, uint32_t desc) { @@ -4537,11 +4681,18 @@ static void vmsetm(void *vd, void *v0, void *vs2, CPURISCVState *env, { uint32_t vm = vext_vm(desc); uint32_t vl = env->vl; + uint32_t total_elems = riscv_cpu_cfg(env)->vlenb << 3; + uint32_t vta_all_1s = vext_vta_all_1s(desc); + uint32_t vma = vext_vma(desc); int i; bool first_mask_bit = false; for (i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + if (vma) { + vext_set_elem_mask(vd, i, 1); + } continue; } /* write a zero to all following active elements */ @@ -4565,6 +4716,15 @@ static void vmsetm(void *vd, void *v0, void *vs2, CPURISCVState *env, } } env->vstart = 0; + /* + * mask destination register are always tail-agnostic + * set tail elements to 1s + */ + if (vta_all_1s) { + for (; i < total_elems; i++) { + vext_set_elem_mask(vd, i, 1); + } + } } void HELPER(vmsbf_m)(void *vd, void *v0, void *vs2, CPURISCVState *env, @@ -4592,11 +4752,17 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, CPURISCVState *env, \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t sum = 0; \ int i; \ \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ *((ETYPE *)vd + H(i)) = sum; \ @@ -4605,6 +4771,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, CPURISCVState *env, \ } \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } GEN_VEXT_VIOTA_M(viota_m_b, uint8_t, H1) @@ -4618,15 +4786,25 @@ void HELPER(NAME)(void *vd, void *v0, CPURISCVState *env, uint32_t desc) \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ int i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ *((ETYPE *)vd + H(i)) = i; \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } GEN_VEXT_VID_V(vid_v_b, uint8_t, H1) @@ -4635,7 +4813,7 @@ GEN_VEXT_VID_V(vid_v_w, uint32_t, H4) GEN_VEXT_VID_V(vid_v_d, uint64_t, H8) /* - *** Vector Permutation Instructions + * Vector Permutation Instructions */ /* Vector Slide Instructions */ @@ -4645,15 +4823,26 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ { \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ target_ulong offset = s1, i_min, i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ i_min = MAX(env->vstart, offset); \ for (i = i_min; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ *((ETYPE *)vd + H(i)) = *((ETYPE *)vs2 + H(i - offset)); \ } \ + env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } /* vslideup.vx vd, vs2, rs1, vm # vd[i+rs1] = vs2[i] */ @@ -4669,13 +4858,23 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ uint32_t vlmax = vext_max_elems(desc, ctzl(sizeof(ETYPE))); \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ - target_ulong i_max, i; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ + target_ulong i_max, i_min, i; \ \ - i_max = MAX(MIN(s1 < vlmax ? vlmax - s1 : 0, vl), env->vstart); \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ + i_min = MIN(s1 < vlmax ? vlmax - s1 : 0, vl); \ + i_max = MAX(i_min, env->vstart); \ for (i = env->vstart; i < i_max; ++i) { \ - if (vm || vext_elem_mask(v0, i)) { \ - *((ETYPE *)vd + H(i)) = *((ETYPE *)vs2 + H(i + s1)); \ + if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ + continue; \ } \ + *((ETYPE *)vd + H(i)) = *((ETYPE *)vs2 + H(i + s1)); \ } \ \ for (i = i_max; i < vl; ++i) { \ @@ -4685,6 +4884,8 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ } \ \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } /* vslidedown.vx vd, vs2, rs1, vm # vd[i] = vs2[i+rs1] */ @@ -4693,17 +4894,26 @@ GEN_VEXT_VSLIDEDOWN_VX(vslidedown_vx_h, uint16_t, H2) GEN_VEXT_VSLIDEDOWN_VX(vslidedown_vx_w, uint32_t, H4) GEN_VEXT_VSLIDEDOWN_VX(vslidedown_vx_d, uint64_t, H8) -#define GEN_VEXT_VSLIE1UP(ESZ, H) \ -static void vslide1up_##ESZ(void *vd, void *v0, target_ulong s1, void *vs2, \ - CPURISCVState *env, uint32_t desc) \ +#define GEN_VEXT_VSLIE1UP(BITWIDTH, H) \ +static void vslide1up_##BITWIDTH(void *vd, void *v0, uint64_t s1, \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ { \ - typedef uint##ESZ##_t ETYPE; \ + typedef uint##BITWIDTH##_t ETYPE; \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ if (i == 0) { \ @@ -4713,6 +4923,8 @@ static void vslide1up_##ESZ(void *vd, void *v0, target_ulong s1, void *vs2, \ } \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } GEN_VEXT_VSLIE1UP(8, H1) @@ -4720,11 +4932,11 @@ GEN_VEXT_VSLIE1UP(16, H2) GEN_VEXT_VSLIE1UP(32, H4) GEN_VEXT_VSLIE1UP(64, H8) -#define GEN_VEXT_VSLIDE1UP_VX(NAME, ESZ) \ +#define GEN_VEXT_VSLIDE1UP_VX(NAME, BITWIDTH) \ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ CPURISCVState *env, uint32_t desc) \ { \ - vslide1up_##ESZ(vd, v0, s1, vs2, env, desc); \ + vslide1up_##BITWIDTH(vd, v0, s1, vs2, env, desc); \ } /* vslide1up.vx vd, vs2, rs1, vm # vd[0]=x[rs1], vd[i+1] = vs2[i] */ @@ -4733,17 +4945,26 @@ GEN_VEXT_VSLIDE1UP_VX(vslide1up_vx_h, 16) GEN_VEXT_VSLIDE1UP_VX(vslide1up_vx_w, 32) GEN_VEXT_VSLIDE1UP_VX(vslide1up_vx_d, 64) -#define GEN_VEXT_VSLIDE1DOWN(ESZ, H) \ -static void vslide1down_##ESZ(void *vd, void *v0, target_ulong s1, void *vs2, \ - CPURISCVState *env, uint32_t desc) \ +#define GEN_VEXT_VSLIDE1DOWN(BITWIDTH, H) \ +static void vslide1down_##BITWIDTH(void *vd, void *v0, uint64_t s1, \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ { \ - typedef uint##ESZ##_t ETYPE; \ + typedef uint##BITWIDTH##_t ETYPE; \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ if (i == vl - 1) { \ @@ -4753,6 +4974,8 @@ static void vslide1down_##ESZ(void *vd, void *v0, target_ulong s1, void *vs2, \ } \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } GEN_VEXT_VSLIDE1DOWN(8, H1) @@ -4760,11 +4983,11 @@ GEN_VEXT_VSLIDE1DOWN(16, H2) GEN_VEXT_VSLIDE1DOWN(32, H4) GEN_VEXT_VSLIDE1DOWN(64, H8) -#define GEN_VEXT_VSLIDE1DOWN_VX(NAME, ESZ) \ +#define GEN_VEXT_VSLIDE1DOWN_VX(NAME, BITWIDTH) \ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ CPURISCVState *env, uint32_t desc) \ { \ - vslide1down_##ESZ(vd, v0, s1, vs2, env, desc); \ + vslide1down_##BITWIDTH(vd, v0, s1, vs2, env, desc); \ } /* vslide1down.vx vd, vs2, rs1, vm # vd[i] = vs2[i+1], vd[vl-1]=x[rs1] */ @@ -4774,11 +4997,11 @@ GEN_VEXT_VSLIDE1DOWN_VX(vslide1down_vx_w, 32) GEN_VEXT_VSLIDE1DOWN_VX(vslide1down_vx_d, 64) /* Vector Floating-Point Slide Instructions */ -#define GEN_VEXT_VFSLIDE1UP_VF(NAME, ESZ) \ +#define GEN_VEXT_VFSLIDE1UP_VF(NAME, BITWIDTH) \ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ CPURISCVState *env, uint32_t desc) \ { \ - vslide1up_##ESZ(vd, v0, s1, vs2, env, desc); \ + vslide1up_##BITWIDTH(vd, v0, s1, vs2, env, desc); \ } /* vfslide1up.vf vd, vs2, rs1, vm # vd[0]=f[rs1], vd[i+1] = vs2[i] */ @@ -4786,11 +5009,11 @@ GEN_VEXT_VFSLIDE1UP_VF(vfslide1up_vf_h, 16) GEN_VEXT_VFSLIDE1UP_VF(vfslide1up_vf_w, 32) GEN_VEXT_VFSLIDE1UP_VF(vfslide1up_vf_d, 64) -#define GEN_VEXT_VFSLIDE1DOWN_VF(NAME, ESZ) \ +#define GEN_VEXT_VFSLIDE1DOWN_VF(NAME, BITWIDTH) \ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ CPURISCVState *env, uint32_t desc) \ { \ - vslide1down_##ESZ(vd, v0, s1, vs2, env, desc); \ + vslide1down_##BITWIDTH(vd, v0, s1, vs2, env, desc); \ } /* vfslide1down.vf vd, vs2, rs1, vm # vd[i] = vs2[i+1], vd[vl-1]=f[rs1] */ @@ -4806,11 +5029,19 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ uint32_t vlmax = vext_max_elems(desc, ctzl(sizeof(TS2))); \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(TS2); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint64_t index; \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ index = *((TS1 *)vs1 + HS1(i)); \ @@ -4821,6 +5052,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ } \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } /* vd[i] = (vs1[i] >= VLMAX) ? 0 : vs2[vs1[i]]; */ @@ -4841,11 +5074,19 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ uint32_t vlmax = vext_max_elems(desc, ctzl(sizeof(ETYPE))); \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint64_t index = s1; \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ if (index >= vlmax) { \ @@ -4855,6 +5096,8 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ } \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } /* vd[i] = (x[rs1] >= VLMAX) ? 0 : vs2[rs1] */ @@ -4869,6 +5112,9 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ CPURISCVState *env, uint32_t desc) \ { \ uint32_t vl = env->vl; \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ uint32_t num = 0, i; \ \ for (i = env->vstart; i < vl; i++) { \ @@ -4879,6 +5125,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ num++; \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } /* Compress into vd elements of vs2 where vs1 is enabled */ @@ -4896,9 +5144,22 @@ void HELPER(vmvr_v)(void *vd, void *vs2, CPURISCVState *env, uint32_t desc) uint32_t startb = env->vstart * sewb; uint32_t i = startb; + if (startb >= maxsz) { + env->vstart = 0; + return; + } + + if (HOST_BIG_ENDIAN && i % 8 != 0) { + uint32_t j = ROUND_UP(i, 8); + memcpy((uint8_t *)vd + H1(j - 1), + (uint8_t *)vs2 + H1(j - 1), + j - i); + i = j; + } + memcpy((uint8_t *)vd + H1(i), (uint8_t *)vs2 + H1(i), - maxsz - startb); + maxsz - i); env->vstart = 0; } @@ -4910,15 +5171,25 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, \ { \ uint32_t vl = env->vl; \ uint32_t vm = vext_vm(desc); \ + uint32_t esz = sizeof(ETYPE); \ + uint32_t total_elems = vext_get_total_elems(env, desc, esz); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ continue; \ } \ *((ETYPE *)vd + HD(i)) = *((DTYPE *)vs2 + HS1(i)); \ } \ env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); \ } GEN_VEXT_INT_EXT(vzext_vf2_h, uint16_t, uint8_t, H2, H1) diff --git a/target/riscv/vector_internals.c b/target/riscv/vector_internals.c new file mode 100644 index 0000000000..996c21eb31 --- /dev/null +++ b/target/riscv/vector_internals.c @@ -0,0 +1,86 @@ +/* + * RISC-V Vector Extension Internals + * + * Copyright (c) 2020 T-Head Semiconductor Co., Ltd. All rights reserved. + * + * 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 "vector_internals.h" + +/* set agnostic elements to 1s */ +void vext_set_elems_1s(void *base, uint32_t is_agnostic, uint32_t cnt, + uint32_t tot) +{ + if (is_agnostic == 0) { + /* policy undisturbed */ + return; + } + if (tot - cnt == 0) { + return ; + } + memset(base + cnt, -1, tot - cnt); +} + +void do_vext_vv(void *vd, void *v0, void *vs1, void *vs2, + CPURISCVState *env, uint32_t desc, + opivv2_fn *fn, uint32_t esz) +{ + uint32_t vm = vext_vm(desc); + uint32_t vl = env->vl; + uint32_t total_elems = vext_get_total_elems(env, desc, esz); + uint32_t vta = vext_vta(desc); + uint32_t vma = vext_vma(desc); + uint32_t i; + + VSTART_CHECK_EARLY_EXIT(env); + + for (i = env->vstart; i < vl; i++) { + if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); + continue; + } + fn(vd, vs1, vs2, i); + } + env->vstart = 0; + /* set tail elements to 1s */ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); +} + +void do_vext_vx(void *vd, void *v0, target_long s1, void *vs2, + CPURISCVState *env, uint32_t desc, + opivx2_fn fn, uint32_t esz) +{ + uint32_t vm = vext_vm(desc); + uint32_t vl = env->vl; + uint32_t total_elems = vext_get_total_elems(env, desc, esz); + uint32_t vta = vext_vta(desc); + uint32_t vma = vext_vma(desc); + uint32_t i; + + VSTART_CHECK_EARLY_EXIT(env); + + for (i = env->vstart; i < vl; i++) { + if (!vm && !vext_elem_mask(v0, i)) { + /* set masked-off elements to 1s */ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); + continue; + } + fn(vd, s1, vs2, i); + } + env->vstart = 0; + /* set tail elements to 1s */ + vext_set_elems_1s(vd, vta, vl * esz, total_elems * esz); +} diff --git a/target/riscv/vector_internals.h b/target/riscv/vector_internals.h new file mode 100644 index 0000000000..9e1e15b575 --- /dev/null +++ b/target/riscv/vector_internals.h @@ -0,0 +1,236 @@ +/* + * RISC-V Vector Extension Internals + * + * Copyright (c) 2020 T-Head Semiconductor Co., Ltd. All rights reserved. + * + * 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 TARGET_RISCV_VECTOR_INTERNALS_H +#define TARGET_RISCV_VECTOR_INTERNALS_H + +#include "qemu/bitops.h" +#include "cpu.h" +#include "tcg/tcg-gvec-desc.h" +#include "internals.h" + +#define VSTART_CHECK_EARLY_EXIT(env) do { \ + if (env->vstart >= env->vl) { \ + env->vstart = 0; \ + return; \ + } \ +} while (0) + +static inline uint32_t vext_nf(uint32_t desc) +{ + return FIELD_EX32(simd_data(desc), VDATA, NF); +} + +/* + * Note that vector data is stored in host-endian 64-bit chunks, + * so addressing units smaller than that needs a host-endian fixup. + */ +#if HOST_BIG_ENDIAN +#define H1(x) ((x) ^ 7) +#define H1_2(x) ((x) ^ 6) +#define H1_4(x) ((x) ^ 4) +#define H2(x) ((x) ^ 3) +#define H4(x) ((x) ^ 1) +#define H8(x) ((x)) +#else +#define H1(x) (x) +#define H1_2(x) (x) +#define H1_4(x) (x) +#define H2(x) (x) +#define H4(x) (x) +#define H8(x) (x) +#endif + +/* + * Encode LMUL to lmul as following: + * LMUL vlmul lmul + * 1 000 0 + * 2 001 1 + * 4 010 2 + * 8 011 3 + * - 100 - + * 1/8 101 -3 + * 1/4 110 -2 + * 1/2 111 -1 + */ +static inline int32_t vext_lmul(uint32_t desc) +{ + return sextract32(FIELD_EX32(simd_data(desc), VDATA, LMUL), 0, 3); +} + +static inline uint32_t vext_vm(uint32_t desc) +{ + return FIELD_EX32(simd_data(desc), VDATA, VM); +} + +static inline uint32_t vext_vma(uint32_t desc) +{ + return FIELD_EX32(simd_data(desc), VDATA, VMA); +} + +static inline uint32_t vext_vta(uint32_t desc) +{ + return FIELD_EX32(simd_data(desc), VDATA, VTA); +} + +static inline uint32_t vext_vta_all_1s(uint32_t desc) +{ + return FIELD_EX32(simd_data(desc), VDATA, VTA_ALL_1S); +} + +/* + * Earlier designs (pre-0.9) had a varying number of bits + * per mask value (MLEN). In the 0.9 design, MLEN=1. + * (Section 4.5) + */ +static inline int vext_elem_mask(void *v0, int index) +{ + int idx = index / 64; + int pos = index % 64; + return (((uint64_t *)v0)[idx] >> pos) & 1; +} + +/* + * Get number of total elements, including prestart, body and tail elements. + * Note that when LMUL < 1, the tail includes the elements past VLMAX that + * are held in the same vector register. + */ +static inline uint32_t vext_get_total_elems(CPURISCVState *env, uint32_t desc, + uint32_t esz) +{ + uint32_t vlenb = simd_maxsz(desc); + uint32_t sew = 1 << FIELD_EX64(env->vtype, VTYPE, VSEW); + int8_t emul = ctzl(esz) - ctzl(sew) + vext_lmul(desc) < 0 ? 0 : + ctzl(esz) - ctzl(sew) + vext_lmul(desc); + return (vlenb << emul) / esz; +} + +/* set agnostic elements to 1s */ +void vext_set_elems_1s(void *base, uint32_t is_agnostic, uint32_t cnt, + uint32_t tot); + +/* expand macro args before macro */ +#define RVVCALL(macro, ...) macro(__VA_ARGS__) + +/* (TD, T2, TX2) */ +#define OP_UU_B uint8_t, uint8_t, uint8_t +#define OP_UU_H uint16_t, uint16_t, uint16_t +#define OP_UU_W uint32_t, uint32_t, uint32_t +#define OP_UU_D uint64_t, uint64_t, uint64_t + +/* (TD, T1, T2, TX1, TX2) */ +#define OP_UUU_B uint8_t, uint8_t, uint8_t, uint8_t, uint8_t +#define OP_UUU_H uint16_t, uint16_t, uint16_t, uint16_t, uint16_t +#define OP_UUU_W uint32_t, uint32_t, uint32_t, uint32_t, uint32_t +#define OP_UUU_D uint64_t, uint64_t, uint64_t, uint64_t, uint64_t + +#define OPIVV1(NAME, TD, T2, TX2, HD, HS2, OP) \ +static void do_##NAME(void *vd, void *vs2, int i) \ +{ \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + *((TD *)vd + HD(i)) = OP(s2); \ +} + +#define GEN_VEXT_V(NAME, ESZ) \ +void HELPER(NAME)(void *vd, void *v0, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t vm = vext_vm(desc); \ + uint32_t vl = env->vl; \ + uint32_t total_elems = \ + vext_get_total_elems(env, desc, ESZ); \ + uint32_t vta = vext_vta(desc); \ + uint32_t vma = vext_vma(desc); \ + uint32_t i; \ + \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ + for (i = env->vstart; i < vl; i++) { \ + if (!vm && !vext_elem_mask(v0, i)) { \ + /* set masked-off elements to 1s */ \ + vext_set_elems_1s(vd, vma, i * ESZ, \ + (i + 1) * ESZ); \ + continue; \ + } \ + do_##NAME(vd, vs2, i); \ + } \ + env->vstart = 0; \ + /* set tail elements to 1s */ \ + vext_set_elems_1s(vd, vta, vl * ESZ, \ + total_elems * ESZ); \ +} + +/* operation of two vector elements */ +typedef void opivv2_fn(void *vd, void *vs1, void *vs2, int i); + +#define OPIVV2(NAME, TD, T1, T2, TX1, TX2, HD, HS1, HS2, OP) \ +static void do_##NAME(void *vd, void *vs1, void *vs2, int i) \ +{ \ + TX1 s1 = *((T1 *)vs1 + HS1(i)); \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + *((TD *)vd + HD(i)) = OP(s2, s1); \ +} + +void do_vext_vv(void *vd, void *v0, void *vs1, void *vs2, + CPURISCVState *env, uint32_t desc, + opivv2_fn *fn, uint32_t esz); + +/* generate the helpers for OPIVV */ +#define GEN_VEXT_VV(NAME, ESZ) \ +void HELPER(NAME)(void *vd, void *v0, void *vs1, \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + do_vext_vv(vd, v0, vs1, vs2, env, desc, \ + do_##NAME, ESZ); \ +} + +typedef void opivx2_fn(void *vd, target_long s1, void *vs2, int i); + +/* + * (T1)s1 gives the real operator type. + * (TX1)(T1)s1 expands the operator type of widen or narrow operations. + */ +#define OPIVX2(NAME, TD, T1, T2, TX1, TX2, HD, HS2, OP) \ +static void do_##NAME(void *vd, target_long s1, void *vs2, int i) \ +{ \ + TX2 s2 = *((T2 *)vs2 + HS2(i)); \ + *((TD *)vd + HD(i)) = OP(s2, (TX1)(T1)s1); \ +} + +void do_vext_vx(void *vd, void *v0, target_long s1, void *vs2, + CPURISCVState *env, uint32_t desc, + opivx2_fn fn, uint32_t esz); + +/* generate the helpers for OPIVX */ +#define GEN_VEXT_VX(NAME, ESZ) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + do_vext_vx(vd, v0, s1, vs2, env, desc, \ + do_##NAME, ESZ); \ +} + +/* Three of the widening shortening macros: */ +/* (TD, T1, T2, TX1, TX2) */ +#define WOP_UUU_B uint16_t, uint8_t, uint8_t, uint16_t, uint16_t +#define WOP_UUU_H uint32_t, uint16_t, uint16_t, uint32_t, uint32_t +#define WOP_UUU_W uint64_t, uint32_t, uint32_t, uint64_t, uint64_t + +#endif /* TARGET_RISCV_VECTOR_INTERNALS_H */ diff --git a/target/riscv/xthead.decode b/target/riscv/xthead.decode new file mode 100644 index 0000000000..d1d104bcf2 --- /dev/null +++ b/target/riscv/xthead.decode @@ -0,0 +1,185 @@ +# +# Translation routines for the instructions of the XThead* ISA extensions +# +# Copyright (c) 2022 Christoph Muellner, christoph.muellner@vrull.eu +# Dr. Philipp Tomsich, philipp.tomsich@vrull.eu +# +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# The documentation of the ISA extensions can be found here: +# https://github.com/T-head-Semi/thead-extension-spec/releases/latest + +# Fields: +%rd 7:5 +%rd1 7:5 +%rs 15:5 +%rs1 15:5 +%rd2 20:5 +%rs2 20:5 +%sh5 20:5 +%imm5 20:s5 +%sh6 20:6 +%sh2 25:2 +%imm2 25:2 + +# Argument sets +&r rd rs1 rs2 !extern +&r2 rd rs1 !extern +&shift shamt rs1 rd !extern +&th_bfext msb lsb rs1 rd +&th_pair rd1 rs rd2 sh2 +&th_memidx rd rs1 rs2 imm2 +&th_meminc rd rs1 imm5 imm2 + +# Formats +@sfence_vm ....... ..... ..... ... ..... ....... %rs1 +@rs2_s ....... ..... ..... ... ..... ....... %rs2 %rs1 +@r ....... ..... ..... ... ..... ....... &r %rs2 %rs1 %rd +@r2 ....... ..... ..... ... ..... ....... &r2 %rs1 %rd +@th_bfext msb:6 lsb:6 ..... ... ..... ....... &th_bfext %rs1 %rd +@sh5 ....... ..... ..... ... ..... ....... &shift shamt=%sh5 %rs1 %rd +@sh6 ...... ...... ..... ... ..... ....... &shift shamt=%sh6 %rs1 %rd +@th_pair ..... .. ..... ..... ... ..... ....... &th_pair %rd1 %rs %rd2 %sh2 +@th_memidx ..... .. ..... ..... ... ..... ....... &th_memidx %rd %rs1 %rs2 %imm2 +@th_meminc ..... .. ..... ..... ... ..... ....... &th_meminc %rd %rs1 %imm5 %imm2 + +# XTheadBa +# Instead of defining a new encoding, we simply use the decoder to +# extract the imm[0:1] field and dispatch to separate translation +# functions (mirroring the `sh[123]add` instructions from Zba and +# the regular RVI `add` instruction. +# +# The only difference between sh[123]add and addsl is that the shift +# is applied to rs1 (for addsl) instead of rs2 (for sh[123]add). +# +# Note that shift-by-0 is a valid operation according to the manual. +# This will be equivalent to a regular add. +add 0000000 ..... ..... 001 ..... 0001011 @r +th_addsl1 0000001 ..... ..... 001 ..... 0001011 @r +th_addsl2 0000010 ..... ..... 001 ..... 0001011 @r +th_addsl3 0000011 ..... ..... 001 ..... 0001011 @r + +# XTheadBb +th_ext ...... ...... ..... 010 ..... 0001011 @th_bfext +th_extu ...... ...... ..... 011 ..... 0001011 @th_bfext +th_ff0 1000010 00000 ..... 001 ..... 0001011 @r2 +th_ff1 1000011 00000 ..... 001 ..... 0001011 @r2 +th_srri 000100 ...... ..... 001 ..... 0001011 @sh6 +th_srriw 0001010 ..... ..... 001 ..... 0001011 @sh5 +th_rev 1000001 00000 ..... 001 ..... 0001011 @r2 +th_revw 1001000 00000 ..... 001 ..... 0001011 @r2 +th_tstnbz 1000000 00000 ..... 001 ..... 0001011 @r2 + +# XTheadBs +th_tst 100010 ...... ..... 001 ..... 0001011 @sh6 + +# XTheadCmo +th_dcache_call 0000000 00001 00000 000 00000 0001011 +th_dcache_ciall 0000000 00011 00000 000 00000 0001011 +th_dcache_iall 0000000 00010 00000 000 00000 0001011 +th_dcache_cpa 0000001 01001 ..... 000 00000 0001011 @sfence_vm +th_dcache_cipa 0000001 01011 ..... 000 00000 0001011 @sfence_vm +th_dcache_ipa 0000001 01010 ..... 000 00000 0001011 @sfence_vm +th_dcache_cva 0000001 00101 ..... 000 00000 0001011 @sfence_vm +th_dcache_civa 0000001 00111 ..... 000 00000 0001011 @sfence_vm +th_dcache_iva 0000001 00110 ..... 000 00000 0001011 @sfence_vm +th_dcache_csw 0000001 00001 ..... 000 00000 0001011 @sfence_vm +th_dcache_cisw 0000001 00011 ..... 000 00000 0001011 @sfence_vm +th_dcache_isw 0000001 00010 ..... 000 00000 0001011 @sfence_vm +th_dcache_cpal1 0000001 01000 ..... 000 00000 0001011 @sfence_vm +th_dcache_cval1 0000001 00100 ..... 000 00000 0001011 @sfence_vm +th_icache_iall 0000000 10000 00000 000 00000 0001011 +th_icache_ialls 0000000 10001 00000 000 00000 0001011 +th_icache_ipa 0000001 11000 ..... 000 00000 0001011 @sfence_vm +th_icache_iva 0000001 10000 ..... 000 00000 0001011 @sfence_vm +th_l2cache_call 0000000 10101 00000 000 00000 0001011 +th_l2cache_ciall 0000000 10111 00000 000 00000 0001011 +th_l2cache_iall 0000000 10110 00000 000 00000 0001011 + +# XTheadCondMov +th_mveqz 0100000 ..... ..... 001 ..... 0001011 @r +th_mvnez 0100001 ..... ..... 001 ..... 0001011 @r + +# XTheadFMemIdx +th_flrd 01100 .. ..... ..... 110 ..... 0001011 @th_memidx +th_flrw 01000 .. ..... ..... 110 ..... 0001011 @th_memidx +th_flurd 01110 .. ..... ..... 110 ..... 0001011 @th_memidx +th_flurw 01010 .. ..... ..... 110 ..... 0001011 @th_memidx +th_fsrd 01100 .. ..... ..... 111 ..... 0001011 @th_memidx +th_fsrw 01000 .. ..... ..... 111 ..... 0001011 @th_memidx +th_fsurd 01110 .. ..... ..... 111 ..... 0001011 @th_memidx +th_fsurw 01010 .. ..... ..... 111 ..... 0001011 @th_memidx + +# XTheadFmv +th_fmv_hw_x 1010000 00000 ..... 001 ..... 0001011 @r2 +th_fmv_x_hw 1100000 00000 ..... 001 ..... 0001011 @r2 + +# XTheadMac +th_mula 00100 00 ..... ..... 001 ..... 0001011 @r +th_mulah 00101 00 ..... ..... 001 ..... 0001011 @r +th_mulaw 00100 10 ..... ..... 001 ..... 0001011 @r +th_muls 00100 01 ..... ..... 001 ..... 0001011 @r +th_mulsh 00101 01 ..... ..... 001 ..... 0001011 @r +th_mulsw 00100 11 ..... ..... 001 ..... 0001011 @r + +# XTheadMemIdx +th_ldia 01111 .. ..... ..... 100 ..... 0001011 @th_meminc +th_ldib 01101 .. ..... ..... 100 ..... 0001011 @th_meminc +th_lwia 01011 .. ..... ..... 100 ..... 0001011 @th_meminc +th_lwib 01001 .. ..... ..... 100 ..... 0001011 @th_meminc +th_lwuia 11011 .. ..... ..... 100 ..... 0001011 @th_meminc +th_lwuib 11001 .. ..... ..... 100 ..... 0001011 @th_meminc +th_lhia 00111 .. ..... ..... 100 ..... 0001011 @th_meminc +th_lhib 00101 .. ..... ..... 100 ..... 0001011 @th_meminc +th_lhuia 10111 .. ..... ..... 100 ..... 0001011 @th_meminc +th_lhuib 10101 .. ..... ..... 100 ..... 0001011 @th_meminc +th_lbia 00011 .. ..... ..... 100 ..... 0001011 @th_meminc +th_lbib 00001 .. ..... ..... 100 ..... 0001011 @th_meminc +th_lbuia 10011 .. ..... ..... 100 ..... 0001011 @th_meminc +th_lbuib 10001 .. ..... ..... 100 ..... 0001011 @th_meminc +th_sdia 01111 .. ..... ..... 101 ..... 0001011 @th_meminc +th_sdib 01101 .. ..... ..... 101 ..... 0001011 @th_meminc +th_swia 01011 .. ..... ..... 101 ..... 0001011 @th_meminc +th_swib 01001 .. ..... ..... 101 ..... 0001011 @th_meminc +th_shia 00111 .. ..... ..... 101 ..... 0001011 @th_meminc +th_shib 00101 .. ..... ..... 101 ..... 0001011 @th_meminc +th_sbia 00011 .. ..... ..... 101 ..... 0001011 @th_meminc +th_sbib 00001 .. ..... ..... 101 ..... 0001011 @th_meminc + +th_lrd 01100 .. ..... ..... 100 ..... 0001011 @th_memidx +th_lrw 01000 .. ..... ..... 100 ..... 0001011 @th_memidx +th_lrwu 11000 .. ..... ..... 100 ..... 0001011 @th_memidx +th_lrh 00100 .. ..... ..... 100 ..... 0001011 @th_memidx +th_lrhu 10100 .. ..... ..... 100 ..... 0001011 @th_memidx +th_lrb 00000 .. ..... ..... 100 ..... 0001011 @th_memidx +th_lrbu 10000 .. ..... ..... 100 ..... 0001011 @th_memidx +th_srd 01100 .. ..... ..... 101 ..... 0001011 @th_memidx +th_srw 01000 .. ..... ..... 101 ..... 0001011 @th_memidx +th_srh 00100 .. ..... ..... 101 ..... 0001011 @th_memidx +th_srb 00000 .. ..... ..... 101 ..... 0001011 @th_memidx + +th_lurd 01110 .. ..... ..... 100 ..... 0001011 @th_memidx +th_lurw 01010 .. ..... ..... 100 ..... 0001011 @th_memidx +th_lurwu 11010 .. ..... ..... 100 ..... 0001011 @th_memidx +th_lurh 00110 .. ..... ..... 100 ..... 0001011 @th_memidx +th_lurhu 10110 .. ..... ..... 100 ..... 0001011 @th_memidx +th_lurb 00010 .. ..... ..... 100 ..... 0001011 @th_memidx +th_lurbu 10010 .. ..... ..... 100 ..... 0001011 @th_memidx +th_surd 01110 .. ..... ..... 101 ..... 0001011 @th_memidx +th_surw 01010 .. ..... ..... 101 ..... 0001011 @th_memidx +th_surh 00110 .. ..... ..... 101 ..... 0001011 @th_memidx +th_surb 00010 .. ..... ..... 101 ..... 0001011 @th_memidx + +# XTheadMemPair +th_ldd 11111 .. ..... ..... 100 ..... 0001011 @th_pair +th_lwd 11100 .. ..... ..... 100 ..... 0001011 @th_pair +th_lwud 11110 .. ..... ..... 100 ..... 0001011 @th_pair +th_sdd 11111 .. ..... ..... 101 ..... 0001011 @th_pair +th_swd 11100 .. ..... ..... 101 ..... 0001011 @th_pair + +# XTheadSync +th_sfence_vmas 0000010 ..... ..... 000 00000 0001011 @rs2_s +th_sync 0000000 11000 00000 000 00000 0001011 +th_sync_i 0000000 11010 00000 000 00000 0001011 +th_sync_is 0000000 11011 00000 000 00000 0001011 +th_sync_s 0000000 11001 00000 000 00000 0001011 diff --git a/target/riscv/zce_helper.c b/target/riscv/zce_helper.c new file mode 100644 index 0000000000..b433bda16d --- /dev/null +++ b/target/riscv/zce_helper.c @@ -0,0 +1,55 @@ +/* + * RISC-V Zcmt Extension Helper for QEMU. + * + * Copyright (c) 2021-2022 PLCT Lab + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" + +target_ulong HELPER(cm_jalt)(CPURISCVState *env, uint32_t index) +{ + +#if !defined(CONFIG_USER_ONLY) + RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_JVT); + if (ret != RISCV_EXCP_NONE) { + riscv_raise_exception(env, ret, 0); + } +#endif + + target_ulong target; + target_ulong val = env->jvt; + int xlen = riscv_cpu_xlen(env); + uint8_t mode = get_field(val, JVT_MODE); + target_ulong base = val & JVT_BASE; + target_ulong t0; + + if (mode != 0) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, 0); + } + + if (xlen == 32) { + t0 = base + (index << 2); + target = cpu_ldl_code(env, t0); + } else { + t0 = base + (index << 3); + target = cpu_ldq_code(env, t0); + } + + return target & ~0x1; +} diff --git a/target/rx/cpu-param.h b/target/rx/cpu-param.h index b156ad1ca0..521d669bdf 100644 --- a/target/rx/cpu-param.h +++ b/target/rx/cpu-param.h @@ -25,6 +25,4 @@ #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 index 4533759d96..ac2e5785ef 100644 --- a/target/rx/cpu-qom.h +++ b/target/rx/cpu-qom.h @@ -1,5 +1,5 @@ /* - * RX CPU + * QEMU RX CPU QOM header (target agnostic) * * Copyright (c) 2019 Yoshinori Sato * @@ -20,7 +20,6 @@ #define RX_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_RX_CPU "rx-cpu" @@ -28,20 +27,7 @@ OBJECT_DECLARE_CPU_TYPE(RXCPU, RXCPUClass, RX_CPU) -/* - * RXCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * - * A RX CPU model. - */ -struct RXCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceReset parent_reset; -}; +#define RX_CPU_TYPE_SUFFIX "-" TYPE_RX_CPU +#define RX_CPU_TYPE_NAME(model) model RX_CPU_TYPE_SUFFIX #endif diff --git a/target/rx/cpu.c b/target/rx/cpu.c index fb30080ac4..da673a595d 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -24,6 +24,7 @@ #include "exec/exec-all.h" #include "hw/loader.h" #include "fpu/softfloat.h" +#include "tcg/debug-assert.h" static void rx_cpu_set_pc(CPUState *cs, vaddr value) { @@ -32,28 +33,52 @@ static void rx_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.pc = value; } +static vaddr rx_cpu_get_pc(CPUState *cs) +{ + RXCPU *cpu = RX_CPU(cs); + + return cpu->env.pc; +} + static void rx_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { RXCPU *cpu = RX_CPU(cs); + tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); cpu->env.pc = tb->pc; } +static void rx_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + RXCPU *cpu = RX_CPU(cs); + + cpu->env.pc = data[0]; +} + 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) +static int riscv_cpu_mmu_index(CPUState *cs, bool ifunc) { - RXCPU *cpu = RX_CPU(dev); - RXCPUClass *rcc = RX_CPU_GET_CLASS(cpu); - CPURXState *env = &cpu->env; + return 0; +} + +static void rx_cpu_reset_hold(Object *obj) +{ + CPUState *cs = CPU(obj); + RXCPUClass *rcc = RX_CPU_GET_CLASS(obj); + CPURXState *env = cpu_env(cs); uint32_t *resetvec; - rcc->parent_reset(dev); + if (rcc->parent_phases.hold) { + rcc->parent_phases.hold(obj); + } memset(env, 0, offsetof(CPURXState, end_reset_fields)); @@ -69,38 +94,18 @@ static void rx_cpu_reset(DeviceState *dev) 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)) { + if (oc != NULL && object_class_dynamic_cast(oc, TYPE_RX_CPU) != NULL) { 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; } @@ -163,12 +168,8 @@ static bool rx_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, static void rx_cpu_init(Object *obj) { - CPUState *cs = CPU(obj); RXCPU *cpu = RX_CPU(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); } @@ -182,9 +183,10 @@ static const struct SysemuCPUOps rx_sysemu_ops = { #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps rx_tcg_ops = { +static const TCGCPUOps rx_tcg_ops = { .initialize = rx_translate_init, .synchronize_from_tb = rx_cpu_synchronize_from_tb, + .restore_state_to_opc = rx_restore_state_to_opc, .tlb_fill = rx_cpu_tlb_fill, #ifndef CONFIG_USER_ONLY @@ -198,16 +200,19 @@ static void rx_cpu_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); CPUClass *cc = CPU_CLASS(klass); RXCPUClass *rcc = RX_CPU_CLASS(klass); + ResettableClass *rc = RESETTABLE_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); + resettable_class_set_parent_phases(rc, NULL, rx_cpu_reset_hold, NULL, + &rcc->parent_phases); cc->class_by_name = rx_cpu_class_by_name; cc->has_work = rx_cpu_has_work; + cc->mmu_index = riscv_cpu_mmu_index; cc->dump_state = rx_cpu_dump_state; cc->set_pc = rx_cpu_set_pc; + cc->get_pc = rx_cpu_get_pc; #ifndef CONFIG_USER_ONLY cc->sysemu_ops = &rx_sysemu_ops; @@ -216,7 +221,6 @@ static void rx_cpu_class_init(ObjectClass *klass, void *data) cc->gdb_write_register = rx_cpu_gdb_write_register; cc->disas_set_info = rx_cpu_disas_set_info; - cc->gdb_num_core_regs = 26; cc->gdb_core_xml_file = "rx-core.xml"; cc->tcg_ops = &rx_tcg_ops; } @@ -225,6 +229,7 @@ static const TypeInfo rx_cpu_info = { .name = TYPE_RX_CPU, .parent = TYPE_CPU, .instance_size = sizeof(RXCPU), + .instance_align = __alignof(RXCPU), .instance_init = rx_cpu_init, .abstract = true, .class_size = sizeof(RXCPUClass), diff --git a/target/rx/cpu.h b/target/rx/cpu.h index 5655dffeff..c53593d7aa 100644 --- a/target/rx/cpu.h +++ b/target/rx/cpu.h @@ -107,34 +107,40 @@ typedef struct CPUArchState { * A RX CPU */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPURXState env; }; -#define RX_CPU_TYPE_SUFFIX "-" TYPE_RX_CPU -#define RX_CPU_TYPE_NAME(model) model RX_CPU_TYPE_SUFFIX +/* + * RXCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A RX CPU model. + */ +struct RXCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; + #define CPU_RESOLVING_TYPE TYPE_RX_CPU const char *rx_crname(uint8_t cr); #ifndef CONFIG_USER_ONLY void rx_cpu_do_interrupt(CPUState *cpu); bool rx_cpu_exec_interrupt(CPUState *cpu, int int_req); +hwaddr rx_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #endif /* !CONFIG_USER_ONLY */ 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); -void rx_cpu_list(void); void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte); -#define cpu_list rx_cpu_list - #include "exec/cpu-all.h" #define CPU_INTERRUPT_SOFT CPU_INTERRUPT_TGT_INT_0 @@ -143,8 +149,8 @@ void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte); #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) +static inline void cpu_get_tb_cpu_state(CPURXState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { *pc = env->pc; *cs_base = 0; @@ -152,11 +158,6 @@ static inline void cpu_get_tb_cpu_state(CPURXState *env, target_ulong *pc, *flags = FIELD_DP32(*flags, PSW, U, env->psw_u); } -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; diff --git a/target/rx/gdbstub.c b/target/rx/gdbstub.c index 7eb2059a84..f222bf003b 100644 --- a/target/rx/gdbstub.c +++ b/target/rx/gdbstub.c @@ -17,12 +17,11 @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" int rx_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - RXCPU *cpu = RX_CPU(cs); - CPURXState *env = &cpu->env; + CPURXState *env = cpu_env(cs); switch (n) { case 0 ... 15: @@ -53,8 +52,7 @@ int rx_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int rx_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - RXCPU *cpu = RX_CPU(cs); - CPURXState *env = &cpu->env; + CPURXState *env = cpu_env(cs); uint32_t psw; switch (n) { case 0 ... 15: diff --git a/target/rx/helper.c b/target/rx/helper.c index f34945e7e2..80912e8dcb 100644 --- a/target/rx/helper.c +++ b/target/rx/helper.c @@ -45,8 +45,7 @@ void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte) #define INT_FLAGS (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR) void rx_cpu_do_interrupt(CPUState *cs) { - RXCPU *cpu = RX_CPU(cs); - CPURXState *env = &cpu->env; + CPURXState *env = cpu_env(cs); int do_irq = cs->interrupt_request & INT_FLAGS; uint32_t save_psw; @@ -122,8 +121,7 @@ void rx_cpu_do_interrupt(CPUState *cs) bool rx_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { - RXCPU *cpu = RX_CPU(cs); - CPURXState *env = &cpu->env; + CPURXState *env = cpu_env(cs); int accept = 0; /* hardware interrupt (Normal) */ if ((interrupt_request & CPU_INTERRUPT_HARD) && @@ -144,9 +142,9 @@ bool rx_cpu_exec_interrupt(CPUState *cs, int interrupt_request) return false; } -#endif /* !CONFIG_USER_ONLY */ - hwaddr rx_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { return addr; } + +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/rx/meson.build b/target/rx/meson.build index 8de0ad49b9..d196737ce3 100644 --- a/target/rx/meson.build +++ b/target/rx/meson.build @@ -13,4 +13,4 @@ rx_ss.add(files( 'disas.c')) target_arch += {'rx': rx_ss} -target_softmmu_arch += {'rx': ss.source_set()} +target_system_arch += {'rx': ss.source_set()} diff --git a/target/rx/op_helper.c b/target/rx/op_helper.c index 9ca32dcc82..691a12b2be 100644 --- a/target/rx/op_helper.c +++ b/target/rx/op_helper.c @@ -23,6 +23,7 @@ #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #include "fpu/softfloat.h" +#include "tcg/debug-assert.h" static inline G_NORETURN void raise_exception(CPURXState *env, int index, @@ -215,19 +216,19 @@ void helper_scmpu(CPURXState *env) } static uint32_t (* const cpu_ldufn[])(CPUArchState *env, - target_ulong ptr, + abi_ptr 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, + abi_ptr 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, + abi_ptr ptr, uint32_t val, uintptr_t retaddr) = { cpu_stb_data_ra, cpu_stw_data_ra, cpu_stl_data_ra, @@ -286,7 +287,7 @@ void helper_suntil(CPURXState *env, uint32_t sz) uint32_t tmp; tcg_debug_assert(sz < 3); if (env->regs[3] == 0) { - return ; + return; } do { tmp = cpu_ldufn[sz](env, env->regs[1], GETPC()); @@ -305,7 +306,7 @@ void helper_swhile(CPURXState *env, uint32_t sz) uint32_t tmp; tcg_debug_assert(sz < 3); if (env->regs[3] == 0) { - return ; + return; } do { tmp = cpu_ldufn[sz](env, env->regs[1], GETPC()); diff --git a/target/rx/translate.c b/target/rx/translate.c index 62aee66937..f6e9e0ec90 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -28,6 +28,11 @@ #include "exec/translator.h" #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + + typedef struct DisasContext { DisasContextBase base; CPURXState *env; @@ -68,8 +73,6 @@ 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) @@ -128,8 +131,7 @@ static int bdsp_s(DisasContext *ctx, int d) void rx_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - RXCPU *cpu = RX_CPU(cs); - CPURXState *env = &cpu->env; + CPURXState *env = cpu_env(cs); int i; uint32_t psw; @@ -234,7 +236,7 @@ static int is_privileged(DisasContext *ctx, int is_exception) { if (FIELD_EX32(ctx->tb_flags, PSW, PM)) { if (is_exception) { - gen_helper_raise_privilege_violation(cpu_env); + gen_helper_raise_privilege_violation(tcg_env); } return 0; } else { @@ -315,7 +317,7 @@ static void move_from_cr(DisasContext *ctx, TCGv ret, int cr, uint32_t pc) { switch (cr) { case 0: /* PSW */ - gen_helper_pack_psw(ret, cpu_env); + gen_helper_pack_psw(ret, tcg_env); break; case 1: /* PC */ tcg_gen_movi_i32(ret, pc); @@ -367,7 +369,7 @@ static void move_to_cr(DisasContext *ctx, TCGv val, int cr) } switch (cr) { case 0: /* PSW */ - gen_helper_set_psw(cpu_env, val); + gen_helper_set_psw(tcg_env, val); if (is_privileged(ctx, 0)) { /* PSW.{I,U} may be updated here. exit TB. */ ctx->base.is_jmp = DISAS_UPDATE; @@ -382,7 +384,7 @@ static void move_to_cr(DisasContext *ctx, TCGv val, int cr) } break; case 3: /* FPSW */ - gen_helper_set_fpsw(cpu_env, val); + gen_helper_set_fpsw(tcg_env, val); break; case 8: /* BPSW */ tcg_gen_mov_i32(cpu_bpsw, val); @@ -429,7 +431,6 @@ static bool trans_MOV_rm(DisasContext *ctx, arg_MOV_rm *a) 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; } @@ -440,7 +441,6 @@ static bool trans_MOV_mr(DisasContext *ctx, arg_MOV_mr *a) 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; } @@ -458,12 +458,10 @@ static bool trans_MOV_ir(DisasContext *ctx, arg_MOV_ir *a) static bool trans_MOV_im(DisasContext *ctx, arg_MOV_im *a) { TCGv imm, mem; - imm = tcg_const_i32(a->imm); + imm = tcg_constant_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; } @@ -474,7 +472,6 @@ static bool trans_MOV_ar(DisasContext *ctx, arg_MOV_ar *a) 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; } @@ -485,7 +482,6 @@ static bool trans_MOV_ra(DisasContext *ctx, arg_MOV_ra *a) 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; } @@ -495,13 +491,11 @@ static bool trans_MOV_ra(DisasContext *ctx, arg_MOV_ra *a) /* 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]); + tcg_gen_ext_i32(cpu_regs[a->rd], cpu_regs[a->rs], a->sz | MO_SIGN); return true; } @@ -521,9 +515,7 @@ static bool trans_MOV_mm(DisasContext *ctx, arg_MOV_mm *a) 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; } @@ -541,7 +533,6 @@ static bool trans_MOV_rp(DisasContext *ctx, arg_MOV_rp *a) 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; } @@ -559,7 +550,6 @@ static bool trans_MOV_pr(DisasContext *ctx, arg_MOV_pr *a) 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; } @@ -571,17 +561,13 @@ static bool trans_MOVU_mr(DisasContext *ctx, arg_MOVU_mr *a) 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]); + tcg_gen_ext_i32(cpu_regs[a->rd], cpu_regs[a->rs], a->sz); return true; } @@ -592,7 +578,6 @@ static bool trans_MOVU_ar(DisasContext *ctx, arg_MOVU_ar *a) 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; } @@ -610,7 +595,6 @@ static bool trans_MOVU_pr(DisasContext *ctx, arg_MOVU_pr *a) 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; } @@ -635,7 +619,6 @@ static bool trans_POPC(DisasContext *ctx, arg_POPC *a) val = tcg_temp_new(); pop(val); move_to_cr(ctx, val, a->cr); - tcg_temp_free(val); return true; } @@ -663,7 +646,6 @@ static bool trans_PUSH_r(DisasContext *ctx, arg_PUSH_r *a) 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; } @@ -677,8 +659,6 @@ static bool trans_PUSH_m(DisasContext *ctx, arg_PUSH_m *a) 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; } @@ -689,7 +669,6 @@ static bool trans_PUSHC(DisasContext *ctx, arg_PUSHC *a) val = tcg_temp_new(); move_from_cr(ctx, val, a->cr, ctx->pc); push(val); - tcg_temp_free(val); return true; } @@ -717,7 +696,6 @@ static bool trans_XCHG_rr(DisasContext *ctx, arg_XCHG_rr *a) 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; } @@ -741,7 +719,6 @@ static bool trans_XCHG_mr(DisasContext *ctx, arg_XCHG_mr *a) } 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; } @@ -749,12 +726,10 @@ static inline void stcond(TCGCond cond, int rd, int imm) { TCGv z; TCGv _imm; - z = tcg_const_i32(0); - _imm = tcg_const_i32(imm); + z = tcg_constant_i32(0); + _imm = tcg_constant_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 */ @@ -785,12 +760,9 @@ static bool trans_SCCnd(DisasContext *ctx, arg_SCCnd *a) 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; } @@ -840,9 +812,8 @@ static inline void rx_gen_op_rrr(op3fn opr, int dst, int src, int src2) static inline void rx_gen_op_irr(op3fn opr, int dst, int src, uint32_t src2) { - TCGv imm = tcg_const_i32(src2); + TCGv imm = tcg_constant_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, @@ -852,7 +823,6 @@ static inline void rx_gen_op_mr(op3fn opr, DisasContext *ctx, 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) @@ -994,16 +964,14 @@ static bool trans_NEG_rr(DisasContext *ctx, arg_NEG_rr *a) /* ret = arg1 + arg2 + psw_c */ static void rx_adc(TCGv ret, TCGv arg1, TCGv arg2) { - TCGv z; - z = tcg_const_i32(0); + TCGv z = tcg_constant_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_xor_i32(cpu_psw_z, arg1, arg2); + tcg_gen_andc_i32(cpu_psw_o, cpu_psw_o, cpu_psw_z); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); tcg_gen_mov_i32(ret, cpu_psw_s); - tcg_temp_free(z); } /* adc #imm, rd */ @@ -1034,15 +1002,13 @@ static bool trans_ADC_mr(DisasContext *ctx, arg_ADC_mr *a) /* ret = arg1 + arg2 */ static void rx_add(TCGv ret, TCGv arg1, TCGv arg2) { - TCGv z; - z = tcg_const_i32(0); + TCGv z = tcg_constant_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_xor_i32(cpu_psw_z, arg1, arg2); + tcg_gen_andc_i32(cpu_psw_o, cpu_psw_o, cpu_psw_z); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); tcg_gen_mov_i32(ret, cpu_psw_s); - tcg_temp_free(z); } /* add #uimm4, rd */ @@ -1071,24 +1037,23 @@ static bool trans_ADD_rrr(DisasContext *ctx, arg_ADD_rrr *a) /* 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); + tcg_gen_xor_i32(cpu_psw_z, arg1, arg2); + tcg_gen_and_i32(cpu_psw_o, cpu_psw_o, cpu_psw_z); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); /* CMP not required 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) @@ -1097,7 +1062,6 @@ static void rx_sbb(TCGv ret, TCGv arg1, TCGv arg2) temp = tcg_temp_new(); tcg_gen_not_i32(temp, arg2); rx_adc(ret, arg1, temp); - tcg_temp_free(temp); } /* cmp #imm4, rs2 */ @@ -1157,23 +1121,11 @@ static bool trans_SBB_mr(DisasContext *ctx, arg_SBB_mr *a) 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); + rx_gen_op_rr(tcg_gen_abs_i32, a->rd, a->rs); return true; } @@ -1233,13 +1185,12 @@ static bool trans_MUL_rrr(DisasContext *ctx, arg_MUL_rrr *a) /* emul #imm, rd */ static bool trans_EMUL_ir(DisasContext *ctx, arg_EMUL_ir *a) { - TCGv imm = tcg_const_i32(a->imm); + TCGv imm = tcg_constant_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; } @@ -1255,20 +1206,18 @@ static bool trans_EMUL_mr(DisasContext *ctx, arg_EMUL_mr *a) 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); + TCGv imm = tcg_constant_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; } @@ -1284,18 +1233,17 @@ static bool trans_EMULU_mr(DisasContext *ctx, arg_EMULU_mr *a) 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); + gen_helper_div(ret, tcg_env, arg1, arg2); } static void rx_divu(TCGv ret, TCGv arg1, TCGv arg2) { - gen_helper_divu(ret, cpu_env, arg1, arg2); + gen_helper_divu(ret, tcg_env, arg1, arg2); } /* div #imm, rd */ @@ -1362,10 +1310,10 @@ static bool trans_SHLL_rr(DisasContext *ctx, arg_SHLL_rr *a) 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); + count = tcg_temp_new(); tmp = tcg_temp_new(); tcg_gen_andi_i32(tmp, cpu_regs[a->rs], 31); - tcg_gen_sub_i32(count, count, tmp); + tcg_gen_sub_i32(count, tcg_constant_i32(32), 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); @@ -1381,8 +1329,6 @@ static bool trans_SHLL_rr(DisasContext *ctx, arg_SHLL_rr *a) 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; } @@ -1436,7 +1382,6 @@ static inline void shiftr_reg(uint32_t rd, uint32_t rs, unsigned int alith) 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 */ @@ -1480,7 +1425,6 @@ static bool trans_ROLC(DisasContext *ctx, arg_ROLC *a) 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; } @@ -1570,7 +1514,6 @@ static bool trans_REVW(DisasContext *ctx, arg_REVW *a) 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; } @@ -1592,7 +1535,6 @@ static void rx_bcnd_main(DisasContext *ctx, int cd, int dst) 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 */ @@ -1640,9 +1582,8 @@ static bool trans_BRA_l(DisasContext *ctx, arg_BRA_l *a) static inline void rx_save_pc(DisasContext *ctx) { - TCGv pc = tcg_const_i32(ctx->base.pc_next); + TCGv pc = tcg_constant_i32(ctx->base.pc_next); push(pc); - tcg_temp_free(pc); } /* jmp rs */ @@ -1697,36 +1638,35 @@ static bool trans_NOP(DisasContext *ctx, arg_NOP *a) /* scmpu */ static bool trans_SCMPU(DisasContext *ctx, arg_SCMPU *a) { - gen_helper_scmpu(cpu_env); + gen_helper_scmpu(tcg_env); return true; } /* smovu */ static bool trans_SMOVU(DisasContext *ctx, arg_SMOVU *a) { - gen_helper_smovu(cpu_env); + gen_helper_smovu(tcg_env); return true; } /* smovf */ static bool trans_SMOVF(DisasContext *ctx, arg_SMOVF *a) { - gen_helper_smovf(cpu_env); + gen_helper_smovf(tcg_env); return true; } /* smovb */ static bool trans_SMOVB(DisasContext *ctx, arg_SMOVB *a) { - gen_helper_smovb(cpu_env); + gen_helper_smovb(tcg_env); return true; } #define STRING(op) \ do { \ - TCGv size = tcg_const_i32(a->sz); \ - gen_helper_##op(cpu_env, size); \ - tcg_temp_free(size); \ + TCGv size = tcg_constant_i32(a->sz); \ + gen_helper_##op(tcg_env, size); \ } while (0) /* suntile. */ @@ -1767,8 +1707,6 @@ static void rx_mul64hi(TCGv_i64 ret, int rs, int 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) @@ -1782,8 +1720,6 @@ static void rx_mul64lo(TCGv_i64 ret, int rs, int 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 */ @@ -1807,7 +1743,6 @@ static bool trans_MACHI(DisasContext *ctx, arg_MACHI *a) 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; } @@ -1818,7 +1753,6 @@ static bool trans_MACLO(DisasContext *ctx, arg_MACLO *a) 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; } @@ -1836,7 +1770,6 @@ static bool trans_MVFACMI(DisasContext *ctx, arg_MVFACMI *a) 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; } @@ -1847,7 +1780,6 @@ static bool trans_MVTACHI(DisasContext *ctx, arg_MVTACHI *a) 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; } @@ -1858,16 +1790,14 @@ static bool trans_MVTACLO(DisasContext *ctx, arg_MVTACLO *a) 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); + TCGv imm = tcg_constant_i32(a->imm + 1); + gen_helper_racw(tcg_env, imm); return true; } @@ -1876,22 +1806,20 @@ static bool trans_SAT(DisasContext *ctx, arg_SAT *a) { TCGv tmp, z; tmp = tcg_temp_new(); - z = tcg_const_i32(0); + z = tcg_constant_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); + gen_helper_satr(tcg_env); return true; } @@ -1900,10 +1828,9 @@ static bool trans_SATR(DisasContext *ctx, arg_SATR *a) 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, \ + TCGv imm = tcg_constant_i32(li(ctx, 0)); \ + gen_helper_##op(cpu_regs[a->rd], tcg_env, \ cpu_regs[a->rd], imm); \ - tcg_temp_free(imm); \ return true; \ } \ static bool cat3(trans_, name, _mr)(DisasContext *ctx, \ @@ -1912,9 +1839,8 @@ static bool trans_SATR(DisasContext *ctx, arg_SATR *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, \ + gen_helper_##op(cpu_regs[a->rd], tcg_env, \ cpu_regs[a->rd], val); \ - tcg_temp_free(mem); \ return true; \ } @@ -1924,8 +1850,7 @@ static bool trans_SATR(DisasContext *ctx, arg_SATR *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); \ + gen_helper_##op(cpu_regs[a->rd], tcg_env, val); \ return true; \ } @@ -1937,9 +1862,8 @@ 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); + TCGv imm = tcg_constant_i32(li(ctx, 0)); + gen_helper_fcmp(tcg_env, cpu_regs[a->rd], imm); return true; } @@ -1950,8 +1874,7 @@ 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); + gen_helper_fcmp(tcg_env, cpu_regs[a->rd], val); return true; } @@ -1965,8 +1888,7 @@ 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); + gen_helper_itof(cpu_regs[a->rd], tcg_env, val); return true; } @@ -1977,7 +1899,6 @@ static void rx_bsetm(TCGv mem, TCGv mask) 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) @@ -1987,7 +1908,6 @@ static void rx_bclrm(TCGv mem, TCGv mask) 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) @@ -1998,7 +1918,6 @@ static void rx_btstm(TCGv mem, TCGv mask) 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) @@ -2008,7 +1927,6 @@ static void rx_bnotm(TCGv mem, TCGv mask) 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) @@ -2028,7 +1946,6 @@ static inline void rx_btstr(TCGv reg, TCGv mask) 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) @@ -2042,49 +1959,41 @@ static inline void rx_bnotr(TCGv reg, TCGv mask) { \ TCGv mask, mem, addr; \ mem = tcg_temp_new(); \ - mask = tcg_const_i32(1 << a->imm); \ + mask = tcg_constant_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); \ + mask = tcg_constant_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); \ + mask = tcg_temp_new(); \ b = tcg_temp_new(); \ tcg_gen_andi_i32(b, cpu_regs[a->rs], 31); \ - tcg_gen_shl_i32(mask, mask, b); \ + tcg_gen_shl_i32(mask, tcg_constant_i32(1), 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); \ + mask = tcg_temp_new(); \ b = tcg_temp_new(); \ tcg_gen_andi_i32(b, cpu_regs[a->rd], 7); \ - tcg_gen_shl_i32(mask, mask, b); \ + tcg_gen_shl_i32(mask, tcg_constant_i32(1), 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; \ } @@ -2103,8 +2012,6 @@ static inline void bmcnd_op(TCGv val, TCGCond cond, int pos) 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] */ @@ -2117,8 +2024,6 @@ static bool trans_BMCnd_im(DisasContext *ctx, arg_BMCnd_im *a) 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; } @@ -2155,7 +2060,7 @@ static inline void clrsetpsw(DisasContext *ctx, int cb, int val) tcg_gen_movi_i32(cpu_psw_o, val << 31); break; default: - qemu_log_mask(LOG_GUEST_ERROR, "Invalid distination %d", cb); + qemu_log_mask(LOG_GUEST_ERROR, "Invalid destination %d", cb); break; } } else if (is_privileged(ctx, 0)) { @@ -2173,7 +2078,7 @@ static inline void clrsetpsw(DisasContext *ctx, int cb, int val) } break; default: - qemu_log_mask(LOG_GUEST_ERROR, "Invalid distination %d", cb); + qemu_log_mask(LOG_GUEST_ERROR, "Invalid destination %d", cb); break; } } @@ -2208,9 +2113,8 @@ static bool trans_MVTC_i(DisasContext *ctx, arg_MVTC_i *a) { TCGv imm; - imm = tcg_const_i32(a->imm); + imm = tcg_constant_i32(a->imm); move_to_cr(ctx, imm, a->cr); - tcg_temp_free(imm); return true; } @@ -2236,9 +2140,8 @@ static bool trans_RTFI(DisasContext *ctx, arg_RTFI *a) 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); + gen_helper_set_psw_rte(tcg_env, psw); ctx->base.is_jmp = DISAS_EXIT; - tcg_temp_free(psw); } return true; } @@ -2251,9 +2154,8 @@ static bool trans_RTE(DisasContext *ctx, arg_RTE *a) psw = tcg_temp_new(); pop(cpu_pc); pop(psw); - gen_helper_set_psw_rte(cpu_env, psw); + gen_helper_set_psw_rte(tcg_env, psw); ctx->base.is_jmp = DISAS_EXIT; - tcg_temp_free(psw); } return true; } @@ -2262,7 +2164,7 @@ static bool trans_RTE(DisasContext *ctx, arg_RTE *a) static bool trans_BRK(DisasContext *ctx, arg_BRK *a) { tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next); - gen_helper_rxbrk(cpu_env); + gen_helper_rxbrk(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; return true; } @@ -2273,10 +2175,9 @@ static bool trans_INT(DisasContext *ctx, arg_INT *a) TCGv vec; tcg_debug_assert(a->imm < 0x100); - vec = tcg_const_i32(a->imm); + vec = tcg_constant_i32(a->imm); tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next); - gen_helper_rxint(cpu_env, vec); - tcg_temp_free(vec); + gen_helper_rxint(tcg_env, vec); ctx->base.is_jmp = DISAS_NORETURN; return true; } @@ -2286,16 +2187,15 @@ static bool trans_WAIT(DisasContext *ctx, arg_WAIT *a) { if (is_privileged(ctx, 1)) { tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next); - gen_helper_wait(cpu_env); + gen_helper_wait(tcg_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; + ctx->env = cpu_env(cs); ctx->tb_flags = ctx->base.tb->flags; } @@ -2318,7 +2218,7 @@ static void rx_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ctx->pc = ctx->base.pc_next; insn = decode_load(ctx); if (!decode(ctx, insn)) { - gen_helper_raise_illegal_instruction(cpu_env); + gen_helper_raise_illegal_instruction(tcg_env); } } @@ -2363,21 +2263,16 @@ static const TranslatorOps rx_tr_ops = { .disas_log = rx_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { 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]; + translator_loop(cs, tb, max_insns, pc, host_pc, &rx_tr_ops, &dc.base); } #define ALLOC_REGISTER(sym, name) \ - cpu_##sym = tcg_global_mem_new_i32(cpu_env, \ + cpu_##sym = tcg_global_mem_new_i32(tcg_env, \ offsetof(CPURXState, sym), name) void rx_translate_init(void) @@ -2389,7 +2284,7 @@ void rx_translate_init(void) int i; for (i = 0; i < NUM_REGS; i++) { - cpu_regs[i] = tcg_global_mem_new_i32(cpu_env, + cpu_regs[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPURXState, regs[i]), regnames[i]); } @@ -2409,6 +2304,6 @@ void rx_translate_init(void) ALLOC_REGISTER(isp, "ISP"); ALLOC_REGISTER(fintv, "FINTV"); ALLOC_REGISTER(intb, "INTB"); - cpu_acc = tcg_global_mem_new_i64(cpu_env, + cpu_acc = tcg_global_mem_new_i64(tcg_env, offsetof(CPURXState, acc), "ACC"); } diff --git a/target/s390x/arch_dump.c b/target/s390x/arch_dump.c index 08daf93ae1..7e8a1b4fc0 100644 --- a/target/s390x/arch_dump.c +++ b/target/s390x/arch_dump.c @@ -12,11 +12,13 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "cpu.h" #include "s390x-internal.h" #include "elf.h" #include "sysemu/dump.h" - +#include "kvm/kvm_s390x.h" +#include "target/s390x/kvm/pv.h" struct S390xUserRegsStruct { uint64_t psw[2]; @@ -76,9 +78,16 @@ typedef struct noteStruct { uint64_t todcmp; uint32_t todpreg; uint64_t ctrs[16]; + uint8_t dynamic[1]; /* + * Would be a flexible array member, if + * that was legal inside a union. Real + * size comes from PV info interface. + */ } contents; } QEMU_PACKED Note; +static bool pv_dump_initialized; + static void s390x_write_elf64_prstatus(Note *note, S390CPU *cpu, int id) { int i; @@ -177,57 +186,81 @@ static void s390x_write_elf64_prefix(Note *note, S390CPU *cpu, int id) note->contents.prefix = cpu_to_be32((uint32_t)(cpu->env.psa)); } +static void s390x_write_elf64_pv(Note *note, S390CPU *cpu, int id) +{ + note->hdr.n_type = cpu_to_be32(NT_S390_PV_CPU_DATA); + if (!pv_dump_initialized) { + return; + } + kvm_s390_dump_cpu(cpu, ¬e->contents.dynamic); +} typedef struct NoteFuncDescStruct { int contents_size; + uint64_t (*note_size_func)(void); /* NULL for non-dynamic sized contents */ void (*note_contents_func)(Note *note, S390CPU *cpu, int id); + bool pvonly; } NoteFuncDesc; static const NoteFuncDesc note_core[] = { - {sizeof_field(Note, contents.prstatus), s390x_write_elf64_prstatus}, - {sizeof_field(Note, contents.fpregset), s390x_write_elf64_fpregset}, - { 0, NULL} + {sizeof_field(Note, contents.prstatus), NULL, s390x_write_elf64_prstatus, false}, + {sizeof_field(Note, contents.fpregset), NULL, s390x_write_elf64_fpregset, false}, + { 0, NULL, NULL, false} }; static const NoteFuncDesc note_linux[] = { - {sizeof_field(Note, contents.prefix), s390x_write_elf64_prefix}, - {sizeof_field(Note, contents.ctrs), s390x_write_elf64_ctrs}, - {sizeof_field(Note, contents.timer), s390x_write_elf64_timer}, - {sizeof_field(Note, contents.todcmp), s390x_write_elf64_todcmp}, - {sizeof_field(Note, contents.todpreg), s390x_write_elf64_todpreg}, - {sizeof_field(Note, contents.vregslo), s390x_write_elf64_vregslo}, - {sizeof_field(Note, contents.vregshi), s390x_write_elf64_vregshi}, - {sizeof_field(Note, contents.gscb), s390x_write_elf64_gscb}, - { 0, NULL} + {sizeof_field(Note, contents.prefix), NULL, s390x_write_elf64_prefix, false}, + {sizeof_field(Note, contents.ctrs), NULL, s390x_write_elf64_ctrs, false}, + {sizeof_field(Note, contents.timer), NULL, s390x_write_elf64_timer, false}, + {sizeof_field(Note, contents.todcmp), NULL, s390x_write_elf64_todcmp, false}, + {sizeof_field(Note, contents.todpreg), NULL, s390x_write_elf64_todpreg, false}, + {sizeof_field(Note, contents.vregslo), NULL, s390x_write_elf64_vregslo, false}, + {sizeof_field(Note, contents.vregshi), NULL, s390x_write_elf64_vregshi, false}, + {sizeof_field(Note, contents.gscb), NULL, s390x_write_elf64_gscb, false}, + {0, kvm_s390_pv_dmp_get_size_cpu, s390x_write_elf64_pv, true}, + { 0, NULL, NULL, false} }; static int s390x_write_elf64_notes(const char *note_name, WriteCoreDumpFunction f, S390CPU *cpu, int id, - void *opaque, + DumpState *s, const NoteFuncDesc *funcs) { - Note note; + g_autofree Note *notep = NULL; const NoteFuncDesc *nf; - int note_size; + int note_size, prev_size = 0, content_size; int ret = -1; - assert(strlen(note_name) < sizeof(note.name)); + assert(strlen(note_name) < sizeof(notep->name)); for (nf = funcs; nf->note_contents_func; nf++) { - memset(¬e, 0, sizeof(note)); - note.hdr.n_namesz = cpu_to_be32(strlen(note_name) + 1); - note.hdr.n_descsz = cpu_to_be32(nf->contents_size); - g_strlcpy(note.name, note_name, sizeof(note.name)); - (*nf->note_contents_func)(¬e, cpu, id); + if (nf->pvonly && !s390_is_pv()) { + continue; + } - note_size = sizeof(note) - sizeof(note.contents) + nf->contents_size; - ret = f(¬e, note_size, opaque); + content_size = nf->note_size_func ? nf->note_size_func() : nf->contents_size; + note_size = sizeof(Note) - sizeof(notep->contents) + content_size; + if (prev_size < note_size) { + g_free(notep); + notep = g_malloc(note_size); + prev_size = note_size; + } + + memset(notep, 0, note_size); + + /* Setup note header data */ + notep->hdr.n_descsz = cpu_to_be32(content_size); + notep->hdr.n_namesz = cpu_to_be32(strlen(note_name) + 1); + g_strlcpy(notep->name, note_name, sizeof(notep->name)); + + /* Get contents and write them out */ + (*nf->note_contents_func)(notep, cpu, id); + ret = f(notep, note_size, s); if (ret < 0) { return -1; } - } return 0; @@ -235,16 +268,185 @@ static int s390x_write_elf64_notes(const char *note_name, int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque) + int cpuid, DumpState *s) { S390CPU *cpu = S390_CPU(cs); int r; - r = s390x_write_elf64_notes("CORE", f, cpu, cpuid, opaque, note_core); + r = s390x_write_elf64_notes("CORE", f, cpu, cpuid, s, note_core); if (r) { return r; } - return s390x_write_elf64_notes("LINUX", f, cpu, cpuid, opaque, note_linux); + return s390x_write_elf64_notes("LINUX", f, cpu, cpuid, s, note_linux); +} + +/* PV dump section size functions */ +static uint64_t get_mem_state_size_from_len(uint64_t len) +{ + return (len / (MiB)) * kvm_s390_pv_dmp_get_size_mem_state(); +} + +static uint64_t get_size_mem_state(DumpState *s) +{ + return get_mem_state_size_from_len(s->total_size); +} + +static uint64_t get_size_completion_data(DumpState *s) +{ + return kvm_s390_pv_dmp_get_size_completion_data(); +} + +/* PV dump section data functions*/ +static int get_data_completion(DumpState *s, uint8_t *buff) +{ + int rc; + + if (!pv_dump_initialized) { + return 0; + } + rc = kvm_s390_dump_completion_data(buff); + if (!rc) { + pv_dump_initialized = false; + } + return rc; +} + +static int get_mem_state(DumpState *s, uint8_t *buff) +{ + int64_t memblock_size, memblock_start; + GuestPhysBlock *block; + uint64_t off; + int rc; + + QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) { + memblock_start = dump_filtered_memblock_start(block, s->filter_area_begin, + s->filter_area_length); + if (memblock_start == -1) { + continue; + } + + memblock_size = dump_filtered_memblock_size(block, s->filter_area_begin, + s->filter_area_length); + + off = get_mem_state_size_from_len(block->target_start); + + rc = kvm_s390_dump_mem_state(block->target_start, + get_mem_state_size_from_len(memblock_size), + buff + off); + if (rc) { + return rc; + } + } + + return 0; +} + +static struct sections { + uint64_t (*sections_size_func)(DumpState *s); + int (*sections_contents_func)(DumpState *s, uint8_t *buff); + char sctn_str[12]; +} sections[] = { + { get_size_mem_state, get_mem_state, "pv_mem_meta"}, + { get_size_completion_data, get_data_completion, "pv_compl"}, + {NULL , NULL, ""} +}; + +static uint64_t arch_sections_write_hdr(DumpState *s, uint8_t *buff) +{ + Elf64_Shdr *shdr = (void *)buff; + struct sections *sctn = sections; + uint64_t off = s->section_offset; + + if (!pv_dump_initialized) { + return 0; + } + + for (; sctn->sections_size_func; off += shdr->sh_size, sctn++, shdr++) { + memset(shdr, 0, sizeof(*shdr)); + shdr->sh_type = SHT_PROGBITS; + shdr->sh_offset = off; + shdr->sh_size = sctn->sections_size_func(s); + shdr->sh_name = s->string_table_buf->len; + g_array_append_vals(s->string_table_buf, sctn->sctn_str, sizeof(sctn->sctn_str)); + } + + return (uintptr_t)shdr - (uintptr_t)buff; +} + + +/* Add arch specific number of sections and their respective sizes */ +static void arch_sections_add(DumpState *s) +{ + struct sections *sctn = sections; + + /* + * We only do a PV dump if we are running a PV guest, KVM supports + * the dump API and we got valid dump length information. + */ + if (!s390_is_pv() || !kvm_s390_get_protected_dump() || + !kvm_s390_pv_info_basic_valid()) { + return; + } + + /* + * Start the UV dump process by doing the initialize dump call via + * KVM as the proxy. + */ + if (!kvm_s390_dump_init()) { + pv_dump_initialized = true; + } else { + /* + * Dump init failed, maybe the guest owner disabled dumping. + * We'll continue the non-PV dump process since this is no + * reason to crash qemu. + */ + return; + } + + for (; sctn->sections_size_func; sctn++) { + s->shdr_num += 1; + s->elf_section_data_size += sctn->sections_size_func(s); + } +} + +/* + * After the PV dump has been initialized, the CPU data has been + * fetched and memory has been dumped, we need to grab the tweak data + * and the completion data. + */ +static int arch_sections_write(DumpState *s, uint8_t *buff) +{ + struct sections *sctn = sections; + int rc; + + if (!pv_dump_initialized) { + return -EINVAL; + } + + for (; sctn->sections_size_func; sctn++) { + rc = sctn->sections_contents_func(s, buff); + buff += sctn->sections_size_func(s); + if (rc) { + return rc; + } + } + return 0; +} + +static void arch_cleanup(DumpState *s) +{ + g_autofree uint8_t *buff = NULL; + int rc; + + if (!pv_dump_initialized) { + return; + } + + buff = g_malloc(kvm_s390_pv_dmp_get_size_completion_data()); + rc = kvm_s390_dump_completion_data(buff); + if (!rc) { + pv_dump_initialized = false; + } } int cpu_get_dump_info(ArchDumpInfo *info, @@ -253,7 +455,17 @@ int cpu_get_dump_info(ArchDumpInfo *info, info->d_machine = EM_S390; info->d_endian = ELFDATA2MSB; info->d_class = ELFCLASS64; - + /* + * This is evaluated for each dump so we can freely switch + * between PV and non-PV. + */ + if (s390_is_pv() && kvm_s390_get_protected_dump() && + kvm_s390_pv_info_basic_valid()) { + info->arch_sections_add_fn = *arch_sections_add; + info->arch_sections_write_hdr_fn = *arch_sections_write_hdr; + info->arch_sections_write_fn = *arch_sections_write; + info->arch_cleanup_fn = *arch_cleanup; + } return 0; } @@ -261,7 +473,7 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) { int name_size = 8; /* "LINUX" or "CORE" + pad */ size_t elf_note_size = 0; - int note_head_size; + int note_head_size, content_size; const NoteFuncDesc *nf; assert(class == ELFCLASS64); @@ -270,12 +482,15 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) note_head_size = sizeof(Elf64_Nhdr); for (nf = note_core; nf->note_contents_func; nf++) { - elf_note_size = elf_note_size + note_head_size + name_size + - nf->contents_size; + elf_note_size = elf_note_size + note_head_size + name_size + nf->contents_size; } for (nf = note_linux; nf->note_contents_func; nf++) { + if (nf->pvonly && !s390_is_pv()) { + continue; + } + content_size = nf->contents_size ? nf->contents_size : nf->note_size_func(); elf_note_size = elf_note_size + note_head_size + name_size + - nf->contents_size; + content_size; } return (elf_note_size) * nr_cpus; diff --git a/target/s390x/cpu-dump.c b/target/s390x/cpu-dump.c index ffa9e94d84..69cc9f7746 100644 --- a/target/s390x/cpu-dump.c +++ b/target/s390x/cpu-dump.c @@ -27,8 +27,7 @@ void s390_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; + CPUS390XState *env = cpu_env(cs); int i; qemu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64, diff --git a/target/s390x/cpu-param.h b/target/s390x/cpu-param.h index bf951a002e..84ca08626b 100644 --- a/target/s390x/cpu-param.h +++ b/target/s390x/cpu-param.h @@ -12,6 +12,5 @@ #define TARGET_PAGE_BITS 12 #define TARGET_PHYS_ADDR_SPACE_BITS 64 #define TARGET_VIRT_ADDR_SPACE_BITS 64 -#define NB_MMU_MODES 4 #endif diff --git a/target/s390x/cpu-qom.h b/target/s390x/cpu-qom.h index 00cae2b131..c59bb1eab1 100644 --- a/target/s390x/cpu-qom.h +++ b/target/s390x/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU S/390 CPU + * QEMU S/390 CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -21,47 +21,12 @@ #define QEMU_S390_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_S390_CPU "s390x-cpu" OBJECT_DECLARE_CPU_TYPE(S390CPU, S390CPUClass, S390_CPU) -typedef struct S390CPUModel S390CPUModel; -typedef struct S390CPUDef S390CPUDef; - -typedef struct CPUArchState CPUS390XState; - -typedef enum cpu_reset_type { - S390_CPU_RESET_NORMAL, - S390_CPU_RESET_INITIAL, - S390_CPU_RESET_CLEAR, -} cpu_reset_type; - -/** - * S390CPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * @load_normal: Performs a load normal. - * @cpu_reset: Performs a CPU reset. - * @initial_cpu_reset: Performs an initial CPU reset. - * - * An S/390 CPU model. - */ -struct S390CPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - const S390CPUDef *cpu_def; - bool kvm_required; - bool is_static; - bool is_migration_safe; - const char *desc; - - DeviceRealize parent_realize; - DeviceReset parent_reset; - void (*load_normal)(CPUState *cpu); - void (*reset)(CPUState *cpu, cpu_reset_type type); -}; +#define S390_CPU_TYPE_SUFFIX "-" TYPE_S390_CPU +#define S390_CPU_TYPE_NAME(name) (name S390_CPU_TYPE_SUFFIX) #endif diff --git a/target/s390x/cpu-sysemu.c b/target/s390x/cpu-sysemu.c index 948e4bd3e0..1cd30c1d84 100644 --- a/target/s390x/cpu-sysemu.c +++ b/target/s390x/cpu-sysemu.c @@ -21,6 +21,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "cpu.h" #include "s390x-internal.h" @@ -32,7 +33,7 @@ #include "qapi/qapi-visit-run-state.h" #include "sysemu/hw_accel.h" -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" #include "hw/boards.h" #include "sysemu/sysemu.h" #include "sysemu/tcg.h" @@ -306,3 +307,16 @@ void s390_do_cpu_set_diag318(CPUState *cs, run_on_cpu_data arg) kvm_s390_set_diag318(cs, arg.host_ulong); } } + +void s390_cpu_topology_set_changed(bool changed) +{ + int ret; + + if (kvm_enabled()) { + ret = kvm_s390_topology_set_mtcr(changed); + if (ret) { + error_report("Failed to set Modified Topology Change Report: %s", + strerror(-ret)); + } + } +} diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index c31bb2351f..f7194534ae 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -26,19 +26,43 @@ #include "s390x-internal.h" #include "kvm/kvm_s390x.h" #include "sysemu/kvm.h" -#include "sysemu/reset.h" #include "qemu/module.h" #include "trace.h" #include "qapi/qapi-types-machine.h" #include "sysemu/hw_accel.h" #include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "fpu/softfloat-helpers.h" #include "disas/capstone.h" #include "sysemu/tcg.h" +#ifndef CONFIG_USER_ONLY +#include "sysemu/reset.h" +#endif +#include "hw/s390x/cpu-topology.h" #define CR0_RESET 0xE0UL #define CR14_RESET 0xC2000000UL; +#ifndef CONFIG_USER_ONLY +static bool is_early_exception_psw(uint64_t mask, uint64_t addr) +{ + if (mask & PSW_MASK_RESERVED) { + return true; + } + + switch (mask & (PSW_MASK_32 | PSW_MASK_64)) { + case 0: + return addr & ~0xffffffULL; + case PSW_MASK_32: + return addr & ~0x7fffffffULL; + case PSW_MASK_32 | PSW_MASK_64: + return false; + default: /* PSW_MASK_64 */ + return true; + } +} +#endif + void s390_cpu_set_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) { #ifndef CONFIG_USER_ONLY @@ -55,6 +79,12 @@ void s390_cpu_set_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) env->cc_op = (mask >> 44) & 3; #ifndef CONFIG_USER_ONLY + if (is_early_exception_psw(mask, addr)) { + env->int_pgm_ilen = 0; + trigger_pgm_exception(env, PGM_SPECIFICATION); + return; + } + if ((old_mask ^ mask) & PSW_MASK_PER) { s390_cpu_recompute_watchpoints(env_cpu(env)); } @@ -88,6 +118,13 @@ static void s390_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.psw.addr = value; } +static vaddr s390_cpu_get_pc(CPUState *cs) +{ + S390CPU *cpu = S390_CPU(cs); + + return cpu->env.psw.addr; +} + static bool s390_cpu_has_work(CPUState *cs) { S390CPU *cpu = S390_CPU(cs); @@ -105,6 +142,26 @@ static bool s390_cpu_has_work(CPUState *cs) return s390_cpu_has_int(cpu); } +static int s390x_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return s390x_env_mmu_index(cpu_env(cs), ifetch); +} + +static void s390_query_cpu_fast(CPUState *cpu, CpuInfoFast *value) +{ + S390CPU *s390_cpu = S390_CPU(cpu); + + value->u.s390x.cpu_state = s390_cpu->env.cpu_state; +#if !defined(CONFIG_USER_ONLY) + if (s390_has_topology()) { + value->u.s390x.has_dedicated = true; + value->u.s390x.dedicated = s390_cpu->env.dedicated; + value->u.s390x.has_entitlement = true; + value->u.s390x.entitlement = s390_cpu->env.entitlement; + } +#endif +} + /* S390CPUClass::reset() */ static void s390_cpu_reset(CPUState *s, cpu_reset_type type) { @@ -232,9 +289,7 @@ out: static void s390_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); - S390CPU *cpu = S390_CPU(obj); - cpu_set_cpustate_pointers(cpu); cs->exception_index = EXCP_HLT; #if !defined(CONFIG_USER_ONLY) @@ -242,14 +297,20 @@ static void s390_cpu_initfn(Object *obj) #endif } -static gchar *s390_gdb_arch_name(CPUState *cs) +static const gchar *s390_gdb_arch_name(CPUState *cs) { - return g_strdup("s390:64-bit"); + return "s390:64-bit"; } static Property s390x_cpu_properties[] = { #if !defined(CONFIG_USER_ONLY) DEFINE_PROP_UINT32("core-id", S390CPU, env.core_id, 0), + DEFINE_PROP_INT32("socket-id", S390CPU, env.socket_id, -1), + DEFINE_PROP_INT32("book-id", S390CPU, env.book_id, -1), + DEFINE_PROP_INT32("drawer-id", S390CPU, env.drawer_id, -1), + DEFINE_PROP_BOOL("dedicated", S390CPU, env.dedicated, false), + DEFINE_PROP_CPUS390ENTITLEMENT("entitlement", S390CPU, env.entitlement, + S390_CPU_ENTITLEMENT_AUTO), #endif DEFINE_PROP_END_OF_LIST() }; @@ -263,8 +324,9 @@ static void s390_cpu_reset_full(DeviceState *dev) #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps s390_tcg_ops = { +static const TCGCPUOps s390_tcg_ops = { .initialize = s390x_translate_init, + .restore_state_to_opc = s390x_restore_state_to_opc, #ifdef CONFIG_USER_ONLY .record_sigsegv = s390_cpu_record_sigsegv, @@ -295,15 +357,17 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) scc->reset = s390_cpu_reset; cc->class_by_name = s390_cpu_class_by_name, cc->has_work = s390_cpu_has_work; + cc->mmu_index = s390x_cpu_mmu_index; cc->dump_state = s390_cpu_dump_state; + cc->query_cpu_fast = s390_query_cpu_fast; cc->set_pc = s390_cpu_set_pc; + cc->get_pc = s390_cpu_get_pc; cc->gdb_read_register = s390_cpu_gdb_read_register; cc->gdb_write_register = s390_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY s390_cpu_class_init_sysemu(cc); #endif cc->disas_set_info = s390_cpu_disas_set_info; - cc->gdb_num_core_regs = S390_NUM_CORE_REGS; cc->gdb_core_xml_file = "s390x-core64.xml"; cc->gdb_arch_name = s390_gdb_arch_name; diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 7d6d01325b..43a46a5a06 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -29,12 +29,15 @@ #include "cpu_models.h" #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" +#include "qapi/qapi-types-machine-common.h" #define ELF_MACHINE_UNAME "S390X" /* The z/Architecture has a strong memory model with some store-after-load re-ordering */ #define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) +#define TARGET_HAS_PRECISE_SMC + #define TARGET_INSN_START_EXTRA_WORDS 2 #define MMU_USER_IDX 0 @@ -52,7 +55,7 @@ typedef struct PSW { uint64_t addr; } PSW; -struct CPUArchState { +typedef struct CPUArchState { uint64_t regs[16]; /* GP registers */ /* * The floating point registers are part of the vector registers. @@ -75,9 +78,6 @@ struct CPUArchState { float_status fpu_status; /* passed to softfloat lib */ - /* The low part of a 128-bit return, or remainder of a divide. */ - uint64_t retxl; - PSW psw; S390CrashReason crash_reason; @@ -87,6 +87,7 @@ struct CPUArchState { uint64_t cc_vr; uint64_t ex_value; + uint64_t ex_target; uint64_t __excp_addr; uint64_t psa; @@ -131,6 +132,11 @@ struct CPUArchState { #if !defined(CONFIG_USER_ONLY) uint32_t core_id; /* PoP "CPU address", same as cpu_index */ + int32_t socket_id; + int32_t book_id; + int32_t drawer_id; + bool dedicated; + CpuS390Entitlement entitlement; /* Used only for vertical polarization */ uint64_t cpuid; #endif @@ -151,7 +157,7 @@ struct CPUArchState { /* currently processed sigp order */ uint8_t sigp_order; -}; +} CPUS390XState; static inline uint64_t *get_freg(CPUS390XState *cs, int nr) { @@ -165,11 +171,8 @@ static inline uint64_t *get_freg(CPUS390XState *cs, int nr) * An S/390 CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUS390XState env; S390CPUModel *model; /* needed for live migration */ @@ -177,6 +180,36 @@ struct ArchCPU { uint32_t irqstate_saved_size; }; +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. + * @parent_reset: The parent class' reset handler. + * @load_normal: Performs a load normal. + * @cpu_reset: Performs a CPU reset. + * @initial_cpu_reset: Performs an initial CPU reset. + * + * An S/390 CPU model. + */ +struct S390CPUClass { + CPUClass parent_class; + + const S390CPUDef *cpu_def; + bool kvm_required; + bool is_static; + bool is_migration_safe; + const char *desc; + + DeviceRealize parent_realize; + DeviceReset parent_reset; + void (*load_normal)(CPUState *cpu); + void (*reset)(CPUState *cpu, cpu_reset_type type); +}; #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_s390_cpu; @@ -292,6 +325,7 @@ extern const VMStateDescription vmstate_s390_cpu; #define PSW_MASK_32 0x0000000080000000ULL #define PSW_MASK_SHORT_ADDR 0x000000007fffffffULL #define PSW_MASK_SHORT_CTRL 0xffffffff80000000ULL +#define PSW_MASK_RESERVED 0xb80800fe7fffffffULL #undef PSW_ASC_PRIMARY #undef PSW_ASC_ACCREG @@ -347,7 +381,7 @@ extern const VMStateDescription vmstate_s390_cpu; #define MMU_HOME_IDX 2 #define MMU_REAL_IDX 3 -static inline int cpu_mmu_index(CPUS390XState *env, bool ifetch) +static inline int s390x_env_mmu_index(CPUS390XState *env, bool ifetch) { #ifdef CONFIG_USER_ONLY return MMU_USER_IDX; @@ -378,9 +412,21 @@ static inline int cpu_mmu_index(CPUS390XState *env, bool ifetch) #endif } -static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) +#ifdef CONFIG_TCG + +#include "tcg/tcg_s390x.h" + +static inline void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { + if (env->psw.addr & 1) { + /* + * Instructions must be at even addresses. + * This needs to be checked before address translation. + */ + env->int_pgm_ilen = 2; /* see s390_cpu_tlb_fill() */ + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, 0); + } *pc = env->psw.addr; *cs_base = env->ex_value; *flags = (env->psw.mask >> FLAG_MASK_PSW_SHIFT) & FLAG_MASK_PSW; @@ -392,6 +438,8 @@ static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc, } } +#endif /* CONFIG_TCG */ + /* PER bits from control register 9 */ #define PER_CR9_EVENT_BRANCH 0x80000000 #define PER_CR9_EVENT_IFETCH 0x40000000 @@ -443,8 +491,6 @@ static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc, #define S390_R13_REGNUM 15 #define S390_R14_REGNUM 16 #define S390_R15_REGNUM 17 -/* Total Core Registers. */ -#define S390_NUM_CORE_REGS 18 static inline void setcc(S390CPU *cpu, uint64_t cc) { @@ -555,6 +601,29 @@ typedef struct SysIB_322 { } SysIB_322; QEMU_BUILD_BUG_ON(sizeof(SysIB_322) != 4096); +/* + * Topology Magnitude fields (MAG) indicates the maximum number of + * topology list entries (TLE) at the corresponding nesting level. + */ +#define S390_TOPOLOGY_MAG 6 +#define S390_TOPOLOGY_MAG6 0 +#define S390_TOPOLOGY_MAG5 1 +#define S390_TOPOLOGY_MAG4 2 +#define S390_TOPOLOGY_MAG3 3 +#define S390_TOPOLOGY_MAG2 4 +#define S390_TOPOLOGY_MAG1 5 +/* Configuration topology */ +typedef struct SysIB_151x { + uint8_t reserved0[2]; + uint16_t length; + uint8_t mag[S390_TOPOLOGY_MAG]; + uint8_t reserved1; + uint8_t mnest; + uint32_t reserved2; + char tle[]; +} SysIB_151x; +QEMU_BUILD_BUG_ON(sizeof(SysIB_151x) != 16); + typedef union SysIB { SysIB_111 sysib_111; SysIB_121 sysib_121; @@ -562,9 +631,62 @@ typedef union SysIB { SysIB_221 sysib_221; SysIB_222 sysib_222; SysIB_322 sysib_322; + SysIB_151x sysib_151x; } SysIB; QEMU_BUILD_BUG_ON(sizeof(SysIB) != 4096); +/* + * CPU Topology List provided by STSI with fc=15 provides a list + * of two different Topology List Entries (TLE) types to specify + * the topology hierarchy. + * + * - Container Topology List Entry + * Defines a container to contain other Topology List Entries + * of any type, nested containers or CPU. + * - CPU Topology List Entry + * Specifies the CPUs position, type, entitlement and polarization + * of the CPUs contained in the last container TLE. + * + * There can be theoretically up to five levels of containers, QEMU + * uses only three levels, the drawer's, book's and socket's level. + * + * A container with a nesting level (NL) greater than 1 can only + * contain another container of nesting level NL-1. + * + * A container of nesting level 1 (socket), contains as many CPU TLE + * as needed to describe the position and qualities of all CPUs inside + * the container. + * The qualities of a CPU are polarization, entitlement and type. + * + * The CPU TLE defines the position of the CPUs of identical qualities + * using a 64bits mask which first bit has its offset defined by + * the CPU address origin field of the CPU TLE like in: + * CPU address = origin * 64 + bit position within the mask + */ +/* Container type Topology List Entry */ +typedef struct SYSIBContainerListEntry { + uint8_t nl; + uint8_t reserved[6]; + uint8_t id; +} SYSIBContainerListEntry; +QEMU_BUILD_BUG_ON(sizeof(SYSIBContainerListEntry) != 8); + +/* CPU type Topology List Entry */ +typedef struct SysIBCPUListEntry { + uint8_t nl; + uint8_t reserved0[3]; +#define SYSIB_TLE_POLARITY_MASK 0x03 +#define SYSIB_TLE_DEDICATED 0x04 + uint8_t flags; + uint8_t type; + uint16_t origin; + uint64_t mask; +} SysIBCPUListEntry; +QEMU_BUILD_BUG_ON(sizeof(SysIBCPUListEntry) != 16); + +void insert_stsi_15_1_x(S390CPU *cpu, int sel2, uint64_t addr, uint8_t ar, uintptr_t ra); +void s390_cpu_topology_set_changed(bool changed); + /* MMU defines */ #define ASCE_ORIGIN (~0xfffULL) /* segment table origin */ #define ASCE_SUBSPACE 0x200 /* subspace group control */ @@ -801,8 +923,6 @@ void s390_set_qemu_cpu_model(uint16_t type, uint8_t gen, uint8_t ec_ga, /* helper.c */ -#define S390_CPU_TYPE_SUFFIX "-" TYPE_S390_CPU -#define S390_CPU_TYPE_NAME(name) (name S390_CPU_TYPE_SUFFIX) #define CPU_RESOLVING_TYPE TYPE_S390_CPU /* interrupt.c */ diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index 5528acd082..d28eb65845 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -14,7 +14,9 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "cpu_features.h" -#include "hw/s390x/pv.h" +#ifndef CONFIG_USER_ONLY +#include "target/s390x/kvm/pv.h" +#endif #define DEF_FEAT(_FEAT, _NAME, _TYPE, _BIT, _DESC) \ [S390_FEAT_##_FEAT] = { \ @@ -107,6 +109,7 @@ void s390_fill_feat_block(const S390FeatBitmap features, S390FeatType type, feat = find_next_bit(features, S390_FEAT_MAX, feat + 1); } +#ifndef CONFIG_USER_ONLY if (!s390_is_pv()) { return; } @@ -147,6 +150,7 @@ void s390_fill_feat_block(const S390FeatBitmap features, S390FeatType type, default: return; } +#endif } void s390_add_from_feat_block(S390FeatBitmap features, S390FeatType type, @@ -245,7 +249,7 @@ static void init_groups(void) { int i; - /* init all bitmaps from gnerated data initially */ + /* init all bitmaps from generated data initially */ for (i = 0; i < ARRAY_SIZE(s390_feature_groups); i++) { s390_init_feat_bitmap(s390_feature_groups[i].init, s390_feature_groups[i].feat); diff --git a/target/s390x/cpu_features.h b/target/s390x/cpu_features.h index 87463f064d..a9bd68a2e1 100644 --- a/target/s390x/cpu_features.h +++ b/target/s390x/cpu_features.h @@ -43,6 +43,7 @@ typedef enum { S390_FEAT_TYPE_KDSA, S390_FEAT_TYPE_SORTL, S390_FEAT_TYPE_DFLTCC, + S390_FEAT_TYPE_UV_FEAT_GUEST, } S390FeatType; /* Definition of a CPU feature */ diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index 3603e5fb12..c53ac13352 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -114,6 +114,7 @@ DEF_FEAT(VECTOR_PACKED_DECIMAL_ENH2, "vxpdeh2", STFL, 192, "Vector-Packed-Decima DEF_FEAT(BEAR_ENH, "beareh", STFL, 193, "BEAR-enhancement facility") DEF_FEAT(RDP, "rdp", STFL, 194, "Reset-DAT-protection facility") DEF_FEAT(PAI, "pai", STFL, 196, "Processor-Activity-Instrumentation facility") +DEF_FEAT(PAIE, "paie", STFL, 197, "Processor-Activity-Instrumentation extension-1") /* Features exposed via SCLP SCCB Byte 80 - 98 (bit numbers relative to byte-80) */ DEF_FEAT(SIE_GSLS, "gsls", SCLP_CONF_CHAR, 40, "SIE: Guest-storage-limit-suppression facility") @@ -141,7 +142,7 @@ DEF_FEAT(SIE_CEI, "cei", SCLP_CPU, 43, "SIE: Conditional-external-interception f /* * Features exposed via no feature bit (but e.g., instruction sensing) - * -> the feature bit number is irrelavant + * -> the feature bit number is irrelevant */ DEF_FEAT(DAT_ENH_2, "dateh2", MISC, 0, "DAT-enhancement facility 2") DEF_FEAT(CMM, "cmm", MISC, 0, "Collaborative-memory-management facility") @@ -378,3 +379,7 @@ DEF_FEAT(DEFLATE_GHDT, "dfltcc-gdht", DFLTCC, 1, "DFLTCC GDHT") DEF_FEAT(DEFLATE_CMPR, "dfltcc-cmpr", DFLTCC, 2, "DFLTCC CMPR") DEF_FEAT(DEFLATE_XPND, "dfltcc-xpnd", DFLTCC, 4, "DFLTCC XPND") DEF_FEAT(DEFLATE_F0, "dfltcc-f0", DFLTCC, 192, "DFLTCC format 0 parameter-block") + +/* Features exposed via the UV-CALL instruction */ +DEF_FEAT(UV_FEAT_AP, "appv", UV_FEAT_GUEST, 4, "AP instructions installed for secure guests") +DEF_FEAT(UV_FEAT_AP_INTR, "appvi", UV_FEAT_GUEST, 5, "AP instructions interruption support for secure guests") diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 1a562d2801..8ed3bb6a27 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -17,14 +17,15 @@ #include "sysemu/kvm.h" #include "sysemu/tcg.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "qapi/visitor.h" #include "qemu/module.h" #include "qemu/hw-version.h" #include "qemu/qemu-print.h" #ifndef CONFIG_USER_ONLY #include "sysemu/sysemu.h" +#include "target/s390x/kvm/pv.h" #endif -#include "hw/s390x/pv.h" #define CPUDEF_INIT(_type, _gen, _ec_ga, _mha_pow, _hmfai, _name, _desc) \ { \ @@ -195,11 +196,7 @@ uint32_t s390_get_ibc_val(void) void s390_get_feat_block(S390FeatType type, uint8_t *data) { - static S390CPU *cpu; - - if (!cpu) { - cpu = S390_CPU(qemu_get_cpu(0)); - } + S390CPU *cpu = S390_CPU(first_cpu); if (!cpu || !cpu->model) { return; @@ -236,6 +233,7 @@ bool s390_has_feat(S390Feat feat) return 0; } +#ifndef CONFIG_USER_ONLY if (s390_is_pv()) { switch (feat) { case S390_FEAT_DIAG_318: @@ -253,12 +251,14 @@ bool s390_has_feat(S390Feat feat) case S390_FEAT_SIE_CMMA: case S390_FEAT_SIE_PFMFI: case S390_FEAT_SIE_IBS: + case S390_FEAT_CONFIGURATION_TOPOLOGY: return false; break; default: break; } } +#endif return test_bit(feat, cpu->model->features); } @@ -334,18 +334,31 @@ const S390CPUDef *s390_find_cpu_def(uint16_t type, uint8_t gen, uint8_t ec_ga, static void s390_print_cpu_model_list_entry(gpointer data, gpointer user_data) { const S390CPUClass *scc = S390_CPU_CLASS((ObjectClass *)data); + CPUClass *cc = CPU_CLASS(scc); char *name = g_strdup(object_class_get_name((ObjectClass *)data)); - const char *details = ""; + g_autoptr(GString) details = g_string_new(""); if (scc->is_static) { - details = "(static, migration-safe)"; - } else if (scc->is_migration_safe) { - details = "(migration-safe)"; + g_string_append(details, "static, "); + } + if (scc->is_migration_safe) { + g_string_append(details, "migration-safe, "); + } + if (cc->deprecation_note) { + g_string_append(details, "deprecated, "); + } + if (details->len) { + /* cull trailing ', ' */ + g_string_truncate(details, details->len - 2); } /* strip off the -s390x-cpu */ g_strrstr(name, "-" TYPE_S390_CPU)[0] = 0; - qemu_printf("s390 %-15s %-35s %s\n", name, scc->desc, details); + if (details->len) { + qemu_printf("s390 %-15s %-35s (%s)\n", name, scc->desc, details->str); + } else { + qemu_printf("s390 %-15s %-35s\n", name, scc->desc); + } g_free(name); } @@ -467,6 +480,8 @@ static void check_consistency(const S390CPUModel *model) { S390_FEAT_DIAG_318, S390_FEAT_EXTENDED_LENGTH_SCCB }, { S390_FEAT_NNPA, S390_FEAT_VECTOR }, { S390_FEAT_RDP, S390_FEAT_LOCAL_TLB_CLEARING }, + { S390_FEAT_UV_FEAT_AP, S390_FEAT_AP }, + { S390_FEAT_UV_FEAT_AP_INTR, S390_FEAT_UV_FEAT_AP }, }; int i; @@ -485,21 +500,28 @@ static void error_prepend_missing_feat(const char *name, void *opaque) error_prepend((Error **) opaque, "%s ", name); } +static void check_compat_model_failed(Error **errp, + const S390CPUModel *max_model, + const char *msg) +{ + error_setg(errp, "%s. Maximum supported model in the current configuration: \'%s\'", + msg, max_model->def->name); + error_append_hint(errp, "Consider a different accelerator, try \"-accel help\"\n"); + return; +} + static void check_compatibility(const S390CPUModel *max_model, const S390CPUModel *model, Error **errp) { + ERRP_GUARD(); S390FeatBitmap missing; if (model->def->gen > max_model->def->gen) { - error_setg(errp, "Selected CPU generation is too new. Maximum " - "supported model in the configuration: \'%s\'", - max_model->def->name); + check_compat_model_failed(errp, max_model, "Selected CPU generation is too new"); return; } else if (model->def->gen == max_model->def->gen && model->def->ec_ga > max_model->def->ec_ga) { - error_setg(errp, "Selected CPU GA level is too new. Maximum " - "supported model in the configuration: \'%s\'", - max_model->def->name); + check_compat_model_failed(errp, max_model, "Selected CPU GA level is too new"); return; } @@ -521,7 +543,9 @@ static void check_compatibility(const S390CPUModel *max_model, error_setg(errp, " "); s390_feat_bitmap_to_ascii(missing, errp, error_prepend_missing_feat); error_prepend(errp, "Some features requested in the CPU model are not " - "available in the configuration: "); + "available in the current configuration: "); + error_append_hint(errp, + "Consider a different accelerator, QEMU, or kernel version\n"); } S390CPUModel *get_max_cpu_model(Error **errp) @@ -551,6 +575,7 @@ S390CPUModel *get_max_cpu_model(Error **errp) void s390_realize_cpu_model(CPUState *cs, Error **errp) { + ERRP_GUARD(); Error *err = NULL; S390CPUClass *xcc = S390_CPU_GET_CLASS(cs); S390CPU *cpu = S390_CPU(cs); @@ -591,8 +616,8 @@ void s390_realize_cpu_model(CPUState *cs, Error **errp) #if !defined(CONFIG_USER_ONLY) cpu->env.cpuid = s390_cpuid_from_cpu_model(cpu->model); if (tcg_enabled()) { - /* basic mode, write the cpu address into the first 4 bit of the ID */ - cpu->env.cpuid = deposit64(cpu->env.cpuid, 54, 4, cpu->env.core_id); + cpu->env.cpuid = deposit64(cpu->env.cpuid, CPU_PHYS_ADDR_SHIFT, + CPU_PHYS_ADDR_BITS, cpu->env.core_id); } #endif } @@ -738,7 +763,7 @@ void s390_set_qemu_cpu_model(uint16_t type, uint8_t gen, uint8_t ec_ga, const S390CPUDef *def = s390_find_cpu_def(type, gen, ec_ga, NULL); g_assert(def); - g_assert(QTAILQ_EMPTY_RCU(&cpus)); + g_assert(QTAILQ_EMPTY_RCU(&cpus_queue)); /* build the CPU model */ s390_qemu_cpu_model.def = def; @@ -959,7 +984,7 @@ static void register_types(void) init_ignored_base_feat(); - /* init all bitmaps from gnerated data initially */ + /* init all bitmaps from generated data initially */ s390_init_feat_bitmap(qemu_max_init, qemu_max_cpu_feat); for (i = 0; i < ARRAY_SIZE(s390_cpu_defs); i++) { s390_init_feat_bitmap(s390_cpu_defs[i].base_init, diff --git a/target/s390x/cpu_models.h b/target/s390x/cpu_models.h index 74d1f87e4f..d7b8912989 100644 --- a/target/s390x/cpu_models.h +++ b/target/s390x/cpu_models.h @@ -18,19 +18,19 @@ #include "hw/core/cpu.h" /* static CPU definition */ -struct S390CPUDef { +typedef struct S390CPUDef { const char *name; /* name exposed to the user */ const char *desc; /* description exposed to the user */ uint8_t gen; /* hw generation identification */ uint16_t type; /* cpu type identification */ uint8_t ec_ga; /* EC GA version (on which also the BC is based) */ - uint8_t mha_pow; /* Maximum Host Adress Power, mha = 2^pow-1 */ + uint8_t mha_pow; /* maximum host address power, mha = 2^pow-1 */ uint32_t hmfai; /* hypervisor-managed facilities */ /* base/min features, must never be changed between QEMU versions */ S390FeatBitmap base_feat; /* used to init base_feat from generated data */ S390FeatInit base_init; - /* deafault features, QEMU version specific */ + /* default features, QEMU version specific */ S390FeatBitmap default_feat; /* used to init default_feat from generated data */ S390FeatInit default_init; @@ -38,10 +38,10 @@ struct S390CPUDef { S390FeatBitmap full_feat; /* used to init full_feat from generated data */ S390FeatInit full_init; -}; +} S390CPUDef; /* CPU model based on a CPU definition */ -struct S390CPUModel { +typedef struct S390CPUModel { const S390CPUDef *def; S390FeatBitmap features; /* values copied from the "host" model, can change during migration */ @@ -49,7 +49,7 @@ struct S390CPUModel { uint32_t cpu_id; /* CPU id */ uint8_t cpu_id_format; /* CPU id format bit */ uint8_t cpu_ver; /* CPU version, usually "ff" for kvm */ -}; +} S390CPUModel; /* * CPU ID @@ -96,10 +96,18 @@ static inline bool s390_known_cpu_type(uint16_t type) { return s390_get_gen_for_cpu_type(type) != 0; } +#define CPU_ID_SHIFT 32 +#define CPU_ID_BITS 24 +/* + * When cpu_id_format is 0 (basic mode), the leftmost 4 bits of cpu_id contain + * the rightmost 4 bits of the physical CPU address. + */ +#define CPU_PHYS_ADDR_BITS 4 +#define CPU_PHYS_ADDR_SHIFT (CPU_ID_SHIFT + CPU_ID_BITS - CPU_PHYS_ADDR_BITS) static inline uint64_t s390_cpuid_from_cpu_model(const S390CPUModel *model) { return ((uint64_t)model->cpu_ver << 56) | - ((uint64_t)model->cpu_id << 32) | + ((uint64_t)model->cpu_id << CPU_ID_SHIFT) | ((uint64_t)model->def->type << 16) | (model->def->gen == 7 ? 0 : (uint64_t)model->cpu_id_format << 15); } diff --git a/target/s390x/cpu_models_sysemu.c b/target/s390x/cpu_models_sysemu.c index d8a141a023..2d99218069 100644 --- a/target/s390x/cpu_models_sysemu.c +++ b/target/s390x/cpu_models_sysemu.c @@ -17,7 +17,6 @@ #include "sysemu/kvm.h" #include "qapi/error.h" #include "qapi/visitor.h" -#include "qapi/qmp/qerror.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qmp/qdict.h" #include "qapi/qapi-commands-machine-target.h" @@ -98,24 +97,16 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) } static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info, - Error **errp) + const char *info_arg_name, Error **errp) { Error *err = NULL; - const QDict *qdict = NULL; + const QDict *qdict; const QDictEntry *e; Visitor *visitor; ObjectClass *oc; S390CPU *cpu; Object *obj; - if (info->props) { - qdict = qobject_to(QDict, info->props); - if (!qdict) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict"); - return; - } - } - oc = cpu_class_by_name(TYPE_S390_CPU, info->name); if (!oc) { error_setg(errp, "The CPU definition \'%s\' is unknown.", info->name); @@ -135,13 +126,17 @@ static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info, return; } - if (qdict) { + if (info->props) { + g_autofree const char *props_name = g_strdup_printf("%s.props", + info_arg_name); + visitor = qobject_input_visitor_new(info->props); - if (!visit_start_struct(visitor, NULL, NULL, 0, errp)) { + if (!visit_start_struct(visitor, props_name, NULL, 0, errp)) { visit_free(visitor); object_unref(obj); return; } + qdict = qobject_to(QDict, info->props); for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { if (!object_property_set(obj, e->key, visitor, &err)) { break; @@ -210,7 +205,6 @@ static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, qobject_unref(qdict); } else { info->props = QOBJECT(qdict); - info->has_props = true; } } @@ -224,7 +218,7 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, bool delta_changes = false; /* convert it to our internal representation */ - cpu_model_from_info(&s390_model, model, &err); + cpu_model_from_info(&s390_model, model, "model", &err); if (err) { error_propagate(errp, err); return NULL; @@ -262,12 +256,12 @@ CpuModelCompareInfo *qmp_query_cpu_model_comparison(CpuModelInfo *infoa, S390CPUModel modela, modelb; /* convert both models to our internal representation */ - cpu_model_from_info(&modela, infoa, &err); + cpu_model_from_info(&modela, infoa, "modela", &err); if (err) { error_propagate(errp, err); return NULL; } - cpu_model_from_info(&modelb, infob, &err); + cpu_model_from_info(&modelb, infob, "modelb", &err); if (err) { error_propagate(errp, err); return NULL; @@ -339,13 +333,13 @@ 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, &err); + cpu_model_from_info(&modela, infoa, "modela", &err); if (err) { error_propagate(errp, err); return NULL; } - cpu_model_from_info(&modelb, infob, &err); + cpu_model_from_info(&modelb, infob, "modelb", &err); if (err) { error_propagate(errp, err); return NULL; diff --git a/target/s390x/diag.c b/target/s390x/diag.c index 76b01dcd68..27ffd48576 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -19,9 +19,11 @@ #include "sysemu/cpus.h" #include "hw/s390x/ipl.h" #include "hw/s390x/s390-virtio-ccw.h" -#include "hw/s390x/pv.h" #include "sysemu/kvm.h" #include "kvm/kvm_s390x.h" +#include "target/s390x/kvm/pv.h" +#include "qemu/error-report.h" + int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3) { @@ -75,7 +77,7 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) { bool valid; CPUState *cs = env_cpu(env); - S390CPU *cpu = S390_CPU(cs); + S390CPU *cpu = env_archcpu(env); uint64_t addr = env->regs[r1]; uint64_t subcode = env->regs[r3]; IplParameterBlock *iplb; diff --git a/target/s390x/gdbstub.c b/target/s390x/gdbstub.c index a5d69d0e0b..a9f4eb92ad 100644 --- a/target/s390x/gdbstub.c +++ b/target/s390x/gdbstub.c @@ -23,14 +23,14 @@ #include "s390x-internal.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/bitops.h" #include "sysemu/hw_accel.h" #include "sysemu/tcg.h" int s390_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; + CPUS390XState *env = cpu_env(cs); switch (n) { case S390_PSWM_REGNUM: @@ -45,8 +45,7 @@ int s390_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int s390_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; + CPUS390XState *env = cpu_env(cs); target_ulong tmpl = ldtul_p(mem_buf); switch (n) { @@ -68,11 +67,12 @@ int s390_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) /* the values represent the positions in s390-acr.xml */ #define S390_A0_REGNUM 0 #define S390_A15_REGNUM 15 -/* total number of registers in s390-acr.xml */ -#define S390_NUM_AC_REGS 16 -static int cpu_read_ac_reg(CPUS390XState *env, GByteArray *buf, int n) +static int cpu_read_ac_reg(CPUState *cs, GByteArray *buf, int n) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + switch (n) { case S390_A0_REGNUM ... S390_A15_REGNUM: return gdb_get_reg32(buf, env->aregs[n]); @@ -81,8 +81,11 @@ static int cpu_read_ac_reg(CPUS390XState *env, GByteArray *buf, int n) } } -static int cpu_write_ac_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_write_ac_reg(CPUState *cs, uint8_t *mem_buf, int n) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + switch (n) { case S390_A0_REGNUM ... S390_A15_REGNUM: env->aregs[n] = ldl_p(mem_buf); @@ -97,11 +100,12 @@ static int cpu_write_ac_reg(CPUS390XState *env, uint8_t *mem_buf, int n) #define S390_FPC_REGNUM 0 #define S390_F0_REGNUM 1 #define S390_F15_REGNUM 16 -/* total number of registers in s390-fpr.xml */ -#define S390_NUM_FP_REGS 17 -static int cpu_read_fp_reg(CPUS390XState *env, GByteArray *buf, int n) +static int cpu_read_fp_reg(CPUState *cs, GByteArray *buf, int n) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + switch (n) { case S390_FPC_REGNUM: return gdb_get_reg32(buf, env->fpc); @@ -112,8 +116,11 @@ static int cpu_read_fp_reg(CPUS390XState *env, GByteArray *buf, int n) } } -static int cpu_write_fp_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_write_fp_reg(CPUState *cs, uint8_t *mem_buf, int n) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + switch (n) { case S390_FPC_REGNUM: env->fpc = ldl_p(mem_buf); @@ -131,11 +138,11 @@ static int cpu_write_fp_reg(CPUS390XState *env, uint8_t *mem_buf, int n) #define S390_V15L_REGNUM 15 #define S390_V16_REGNUM 16 #define S390_V31_REGNUM 31 -/* total number of registers in s390-vx.xml */ -#define S390_NUM_VREGS 32 -static int cpu_read_vreg(CPUS390XState *env, GByteArray *buf, int n) +static int cpu_read_vreg(CPUState *cs, GByteArray *buf, int n) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; int ret; switch (n) { @@ -153,8 +160,11 @@ static int cpu_read_vreg(CPUS390XState *env, GByteArray *buf, int n) return ret; } -static int cpu_write_vreg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_write_vreg(CPUState *cs, uint8_t *mem_buf, int n) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + switch (n) { case S390_V0L_REGNUM ... S390_V15L_REGNUM: env->vregs[n][1] = ldtul_p(mem_buf + 8); @@ -171,12 +181,13 @@ static int cpu_write_vreg(CPUS390XState *env, uint8_t *mem_buf, int n) /* the values represent the positions in s390-cr.xml */ #define S390_C0_REGNUM 0 #define S390_C15_REGNUM 15 -/* total number of registers in s390-cr.xml */ -#define S390_NUM_C_REGS 16 #ifndef CONFIG_USER_ONLY -static int cpu_read_c_reg(CPUS390XState *env, GByteArray *buf, int n) +static int cpu_read_c_reg(CPUState *cs, GByteArray *buf, int n) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + switch (n) { case S390_C0_REGNUM ... S390_C15_REGNUM: return gdb_get_regl(buf, env->cregs[n]); @@ -185,8 +196,11 @@ static int cpu_read_c_reg(CPUS390XState *env, GByteArray *buf, int n) } } -static int cpu_write_c_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_write_c_reg(CPUState *cs, uint8_t *mem_buf, int n) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + switch (n) { case S390_C0_REGNUM ... S390_C15_REGNUM: env->cregs[n] = ldtul_p(mem_buf); @@ -205,15 +219,12 @@ static int cpu_write_c_reg(CPUS390XState *env, uint8_t *mem_buf, int n) #define S390_VIRT_CPUTM_REGNUM 1 #define S390_VIRT_BEA_REGNUM 2 #define S390_VIRT_PREFIX_REGNUM 3 -#define S390_VIRT_PP_REGNUM 4 -#define S390_VIRT_PFT_REGNUM 5 -#define S390_VIRT_PFS_REGNUM 6 -#define S390_VIRT_PFC_REGNUM 7 -/* total number of registers in s390-virt.xml */ -#define S390_NUM_VIRT_REGS 8 -static int cpu_read_virt_reg(CPUS390XState *env, GByteArray *mem_buf, int n) +static int cpu_read_virt_reg(CPUState *cs, GByteArray *mem_buf, int n) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + switch (n) { case S390_VIRT_CKC_REGNUM: return gdb_get_regl(mem_buf, env->ckc); @@ -223,51 +234,82 @@ static int cpu_read_virt_reg(CPUS390XState *env, GByteArray *mem_buf, int n) return gdb_get_regl(mem_buf, env->gbea); case S390_VIRT_PREFIX_REGNUM: return gdb_get_regl(mem_buf, env->psa); - case S390_VIRT_PP_REGNUM: + default: + return 0; + } +} + +static int cpu_write_virt_reg(CPUState *cs, uint8_t *mem_buf, int n) +{ + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + + switch (n) { + case S390_VIRT_CKC_REGNUM: + env->ckc = ldtul_p(mem_buf); + cpu_synchronize_post_init(cs); + return 8; + case S390_VIRT_CPUTM_REGNUM: + env->cputm = ldtul_p(mem_buf); + cpu_synchronize_post_init(cs); + return 8; + case S390_VIRT_BEA_REGNUM: + env->gbea = ldtul_p(mem_buf); + cpu_synchronize_post_init(cs); + return 8; + case S390_VIRT_PREFIX_REGNUM: + env->psa = ldtul_p(mem_buf); + cpu_synchronize_post_init(cs); + return 8; + default: + return 0; + } +} + +/* the values represent the positions in s390-virt-kvm.xml */ +#define S390_VIRT_KVM_PP_REGNUM 0 +#define S390_VIRT_KVM_PFT_REGNUM 1 +#define S390_VIRT_KVM_PFS_REGNUM 2 +#define S390_VIRT_KVM_PFC_REGNUM 3 + +static int cpu_read_virt_kvm_reg(CPUState *cs, GByteArray *mem_buf, int n) +{ + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + + switch (n) { + case S390_VIRT_KVM_PP_REGNUM: return gdb_get_regl(mem_buf, env->pp); - case S390_VIRT_PFT_REGNUM: + case S390_VIRT_KVM_PFT_REGNUM: return gdb_get_regl(mem_buf, env->pfault_token); - case S390_VIRT_PFS_REGNUM: + case S390_VIRT_KVM_PFS_REGNUM: return gdb_get_regl(mem_buf, env->pfault_select); - case S390_VIRT_PFC_REGNUM: + case S390_VIRT_KVM_PFC_REGNUM: return gdb_get_regl(mem_buf, env->pfault_compare); default: return 0; } } -static int cpu_write_virt_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_write_virt_kvm_reg(CPUState *cs, uint8_t *mem_buf, int n) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + switch (n) { - case S390_VIRT_CKC_REGNUM: - env->ckc = ldtul_p(mem_buf); - cpu_synchronize_post_init(env_cpu(env)); - return 8; - case S390_VIRT_CPUTM_REGNUM: - env->cputm = ldtul_p(mem_buf); - cpu_synchronize_post_init(env_cpu(env)); - return 8; - case S390_VIRT_BEA_REGNUM: - env->gbea = ldtul_p(mem_buf); - cpu_synchronize_post_init(env_cpu(env)); - return 8; - case S390_VIRT_PREFIX_REGNUM: - env->psa = ldtul_p(mem_buf); - cpu_synchronize_post_init(env_cpu(env)); - return 8; - case S390_VIRT_PP_REGNUM: + case S390_VIRT_KVM_PP_REGNUM: env->pp = ldtul_p(mem_buf); cpu_synchronize_post_init(env_cpu(env)); return 8; - case S390_VIRT_PFT_REGNUM: + case S390_VIRT_KVM_PFT_REGNUM: env->pfault_token = ldtul_p(mem_buf); cpu_synchronize_post_init(env_cpu(env)); return 8; - case S390_VIRT_PFS_REGNUM: + case S390_VIRT_KVM_PFS_REGNUM: env->pfault_select = ldtul_p(mem_buf); cpu_synchronize_post_init(env_cpu(env)); return 8; - case S390_VIRT_PFC_REGNUM: + case S390_VIRT_KVM_PFC_REGNUM: env->pfault_compare = ldtul_p(mem_buf); cpu_synchronize_post_init(env_cpu(env)); return 8; @@ -282,16 +324,20 @@ static int cpu_write_virt_reg(CPUS390XState *env, uint8_t *mem_buf, int n) #define S390_GS_GSD_REGNUM 1 #define S390_GS_GSSM_REGNUM 2 #define S390_GS_GSEPLA_REGNUM 3 -/* total number of registers in s390-gs.xml */ -#define S390_NUM_GS_REGS 4 -static int cpu_read_gs_reg(CPUS390XState *env, GByteArray *buf, int n) +static int cpu_read_gs_reg(CPUState *cs, GByteArray *buf, int n) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + return gdb_get_regl(buf, env->gscb[n]); } -static int cpu_write_gs_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_write_gs_reg(CPUState *cs, uint8_t *mem_buf, int n) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + env->gscb[n] = ldtul_p(mem_buf); cpu_synchronize_post_init(env_cpu(env)); return 8; @@ -301,29 +347,34 @@ void s390_cpu_gdb_init(CPUState *cs) { gdb_register_coprocessor(cs, cpu_read_ac_reg, cpu_write_ac_reg, - S390_NUM_AC_REGS, "s390-acr.xml", 0); + gdb_find_static_feature("s390-acr.xml"), 0); gdb_register_coprocessor(cs, cpu_read_fp_reg, cpu_write_fp_reg, - S390_NUM_FP_REGS, "s390-fpr.xml", 0); + gdb_find_static_feature("s390-fpr.xml"), 0); gdb_register_coprocessor(cs, cpu_read_vreg, cpu_write_vreg, - S390_NUM_VREGS, "s390-vx.xml", 0); + gdb_find_static_feature("s390-vx.xml"), 0); gdb_register_coprocessor(cs, cpu_read_gs_reg, cpu_write_gs_reg, - S390_NUM_GS_REGS, "s390-gs.xml", 0); + gdb_find_static_feature("s390-gs.xml"), 0); #ifndef CONFIG_USER_ONLY gdb_register_coprocessor(cs, cpu_read_c_reg, cpu_write_c_reg, - S390_NUM_C_REGS, "s390-cr.xml", 0); + gdb_find_static_feature("s390-cr.xml"), 0); + + gdb_register_coprocessor(cs, cpu_read_virt_reg, + cpu_write_virt_reg, + gdb_find_static_feature("s390-virt.xml"), 0); if (kvm_enabled()) { - gdb_register_coprocessor(cs, cpu_read_virt_reg, - cpu_write_virt_reg, - S390_NUM_VIRT_REGS, "s390-virt.xml", 0); + gdb_register_coprocessor(cs, cpu_read_virt_kvm_reg, + cpu_write_virt_kvm_reg, + gdb_find_static_feature("s390-virt-kvm.xml"), + 0); } #endif } diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index ad140184b9..2b2bfc3736 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -575,6 +575,9 @@ static uint16_t full_GEN16_GA1[] = { S390_FEAT_BEAR_ENH, S390_FEAT_RDP, S390_FEAT_PAI, + S390_FEAT_PAIE, + S390_FEAT_UV_FEAT_AP, + S390_FEAT_UV_FEAT_AP_INTR, }; @@ -669,6 +672,7 @@ static uint16_t default_GEN16_GA1[] = { S390_FEAT_BEAR_ENH, S390_FEAT_RDP, S390_FEAT_PAI, + S390_FEAT_PAIE, }; /* QEMU (CPU model) features */ @@ -742,13 +746,20 @@ static uint16_t qemu_V7_0[] = { S390_FEAT_MISC_INSTRUCTION_EXT3, }; +static uint16_t qemu_V7_1[] = { + S390_FEAT_VECTOR_ENH2, +}; + /* * Features for the "qemu" CPU model of the latest QEMU machine and the "max" * CPU model under TCG. Don't include features that are not part of the full * feature set of the current "max" CPU model generation. */ static uint16_t qemu_MAX[] = { - S390_FEAT_VECTOR_ENH2, + S390_FEAT_MSA_EXT_5, + S390_FEAT_KIMD_SHA_512, + S390_FEAT_KLMD_SHA_512, + S390_FEAT_PRNO_TRNG, }; /****** END FEATURE DEFS ******/ @@ -871,6 +882,7 @@ static FeatGroupDefSpec QemuFeatDef[] = { QEMU_FEAT_INITIALIZER(V6_0), QEMU_FEAT_INITIALIZER(V6_2), QEMU_FEAT_INITIALIZER(V7_0), + QEMU_FEAT_INITIALIZER(V7_1), QEMU_FEAT_INITIALIZER(MAX), }; diff --git a/target/s390x/helper.c b/target/s390x/helper.c index 473c8e51b0..00d5d403f3 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -21,10 +21,10 @@ #include "qemu/osdep.h" #include "cpu.h" #include "s390x-internal.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/timer.h" #include "hw/s390x/ioinst.h" -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" @@ -139,8 +139,7 @@ void do_restart_interrupt(CPUS390XState *env) void s390_cpu_recompute_watchpoints(CPUState *cs) { const int wp_flags = BP_CPU | BP_MEM_WRITE | BP_STOP_BEFORE_ACCESS; - S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; + CPUS390XState *env = cpu_env(cs); /* We are called when the watchpoints have changed. First remove them all. */ diff --git a/target/s390x/helper.h b/target/s390x/helper.h index bf33d86f74..cc1c20e9e3 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -10,13 +10,13 @@ DEF_HELPER_FLAGS_4(clc, TCG_CALL_NO_WG, i32, env, i32, i64, i64) DEF_HELPER_3(mvcl, i32, env, i32, i32) DEF_HELPER_3(clcl, i32, env, i32, i32) DEF_HELPER_FLAGS_4(clm, TCG_CALL_NO_WG, i32, env, i32, i32, i64) -DEF_HELPER_FLAGS_3(divs32, TCG_CALL_NO_WG, s64, env, s64, s64) +DEF_HELPER_FLAGS_3(divs32, TCG_CALL_NO_WG, i64, env, s64, s64) DEF_HELPER_FLAGS_3(divu32, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(divs64, TCG_CALL_NO_WG, s64, env, s64, s64) -DEF_HELPER_FLAGS_4(divu64, TCG_CALL_NO_WG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_3(divs64, TCG_CALL_NO_WG, i128, env, s64, s64) +DEF_HELPER_FLAGS_4(divu64, TCG_CALL_NO_WG, i128, env, i64, i64, i64) DEF_HELPER_3(srst, void, env, i32, i32) DEF_HELPER_3(srstu, void, env, i32, i32) -DEF_HELPER_4(clst, i64, env, i64, i64, i64) +DEF_HELPER_4(clst, i128, env, i64, i64, i64) DEF_HELPER_FLAGS_4(mvn, TCG_CALL_NO_WG, void, env, i32, i64, i64) DEF_HELPER_FLAGS_4(mvo, TCG_CALL_NO_WG, void, env, i32, i64, i64) DEF_HELPER_FLAGS_4(mvpg, TCG_CALL_NO_WG, i32, env, i64, i32, i32) @@ -31,66 +31,67 @@ DEF_HELPER_4(clcle, i32, env, i32, i64, i32) DEF_HELPER_4(clclu, i32, env, i32, i64, i32) DEF_HELPER_3(cegb, i64, env, s64, i32) DEF_HELPER_3(cdgb, i64, env, s64, i32) -DEF_HELPER_3(cxgb, i64, env, s64, i32) +DEF_HELPER_3(cxgb, i128, env, s64, i32) DEF_HELPER_3(celgb, i64, env, i64, i32) DEF_HELPER_3(cdlgb, i64, env, i64, i32) -DEF_HELPER_3(cxlgb, i64, env, i64, i32) -DEF_HELPER_4(cdsg, void, env, i64, i32, i32) -DEF_HELPER_4(cdsg_parallel, void, env, i64, i32, i32) +DEF_HELPER_3(cxlgb, i128, env, i64, i32) DEF_HELPER_4(csst, i32, env, i32, i64, i64) DEF_HELPER_4(csst_parallel, i32, env, i32, i64, i64) DEF_HELPER_FLAGS_3(aeb, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(adb, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_5(axb, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_3(axb, TCG_CALL_NO_WG, i128, env, i128, i128) DEF_HELPER_FLAGS_3(seb, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(sdb, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_5(sxb, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_3(sxb, TCG_CALL_NO_WG, i128, env, i128, i128) DEF_HELPER_FLAGS_3(deb, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(ddb, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_5(dxb, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_3(dxb, TCG_CALL_NO_WG, i128, env, i128, i128) DEF_HELPER_FLAGS_3(meeb, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(mdeb, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(mdb, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_5(mxb, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i64) -DEF_HELPER_FLAGS_4(mxdb, TCG_CALL_NO_WG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_3(mxb, TCG_CALL_NO_WG, i128, env, i128, i128) +DEF_HELPER_FLAGS_3(mxdb, TCG_CALL_NO_WG, i128, env, i64, i64) DEF_HELPER_FLAGS_2(ldeb, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_4(ldxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) -DEF_HELPER_FLAGS_2(lxdb, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(lxeb, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_3(ldxb, TCG_CALL_NO_WG, i64, env, i128, i32) +DEF_HELPER_FLAGS_2(lxdb, TCG_CALL_NO_WG, i128, env, i64) +DEF_HELPER_FLAGS_2(lxeb, TCG_CALL_NO_WG, i128, env, i64) DEF_HELPER_FLAGS_3(ledb, TCG_CALL_NO_WG, i64, env, i64, i32) -DEF_HELPER_FLAGS_4(lexb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) +DEF_HELPER_FLAGS_3(lexb, TCG_CALL_NO_WG, i64, env, i128, i32) DEF_HELPER_FLAGS_3(ceb, TCG_CALL_NO_WG_SE, i32, env, i64, i64) DEF_HELPER_FLAGS_3(cdb, TCG_CALL_NO_WG_SE, i32, env, i64, i64) -DEF_HELPER_FLAGS_5(cxb, TCG_CALL_NO_WG_SE, i32, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_3(cxb, TCG_CALL_NO_WG_SE, i32, env, i128, i128) DEF_HELPER_FLAGS_3(keb, TCG_CALL_NO_WG, i32, env, i64, i64) DEF_HELPER_FLAGS_3(kdb, TCG_CALL_NO_WG, i32, env, i64, i64) -DEF_HELPER_FLAGS_5(kxb, TCG_CALL_NO_WG, i32, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_3(kxb, TCG_CALL_NO_WG, i32, env, i128, i128) DEF_HELPER_3(cgeb, i64, env, i64, i32) DEF_HELPER_3(cgdb, i64, env, i64, i32) -DEF_HELPER_4(cgxb, i64, env, i64, i64, i32) +DEF_HELPER_3(cgxb, i64, env, i128, i32) DEF_HELPER_3(cfeb, i64, env, i64, i32) DEF_HELPER_3(cfdb, i64, env, i64, i32) -DEF_HELPER_4(cfxb, i64, env, i64, i64, i32) +DEF_HELPER_3(cfxb, i64, env, i128, i32) DEF_HELPER_3(clgeb, i64, env, i64, i32) DEF_HELPER_3(clgdb, i64, env, i64, i32) -DEF_HELPER_4(clgxb, i64, env, i64, i64, i32) +DEF_HELPER_3(clgxb, i64, env, i128, i32) DEF_HELPER_3(clfeb, i64, env, i64, i32) DEF_HELPER_3(clfdb, i64, env, i64, i32) -DEF_HELPER_4(clfxb, i64, env, i64, i64, i32) +DEF_HELPER_3(clfxb, i64, env, i128, i32) DEF_HELPER_FLAGS_3(fieb, TCG_CALL_NO_WG, i64, env, i64, i32) DEF_HELPER_FLAGS_3(fidb, TCG_CALL_NO_WG, i64, env, i64, i32) -DEF_HELPER_FLAGS_4(fixb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) +DEF_HELPER_FLAGS_3(fixb, TCG_CALL_NO_WG, i128, env, i128, i32) DEF_HELPER_FLAGS_4(maeb, TCG_CALL_NO_WG, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_4(madb, TCG_CALL_NO_WG, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_4(mseb, TCG_CALL_NO_WG, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_4(msdb, TCG_CALL_NO_WG, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_3(tceb, TCG_CALL_NO_RWG_SE, i32, env, i64, i64) DEF_HELPER_FLAGS_3(tcdb, TCG_CALL_NO_RWG_SE, i32, env, i64, i64) -DEF_HELPER_FLAGS_4(tcxb, TCG_CALL_NO_RWG_SE, i32, env, i64, i64, i64) +DEF_HELPER_FLAGS_3(tcxb, TCG_CALL_NO_RWG_SE, i32, env, i128, i64) DEF_HELPER_FLAGS_2(sqeb, TCG_CALL_NO_WG, i64, env, i64) DEF_HELPER_FLAGS_2(sqdb, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_3(sqxb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(sqxb, TCG_CALL_NO_WG, i128, env, i128) +DEF_HELPER_3(cvb, void, env, i32, i64) +DEF_HELPER_FLAGS_2(cvbg, TCG_CALL_NO_WG, i64, env, i128) DEF_HELPER_FLAGS_1(cvd, TCG_CALL_NO_RWG_SE, i64, s32) +DEF_HELPER_FLAGS_1(cvdg, TCG_CALL_NO_RWG_SE, i128, s64) DEF_HELPER_FLAGS_4(pack, TCG_CALL_NO_WG, void, env, i32, i64, i64) DEF_HELPER_FLAGS_4(pka, TCG_CALL_NO_WG, void, env, i64, i64, i32) DEF_HELPER_FLAGS_4(pku, TCG_CALL_NO_WG, void, env, i64, i64, i32) @@ -99,21 +100,17 @@ DEF_HELPER_FLAGS_4(unpka, TCG_CALL_NO_WG, i32, env, i64, i32, i64) DEF_HELPER_FLAGS_4(unpku, TCG_CALL_NO_WG, i32, env, i64, i32, i64) DEF_HELPER_FLAGS_3(tp, TCG_CALL_NO_WG, i32, env, i64, i32) DEF_HELPER_FLAGS_4(tr, TCG_CALL_NO_WG, void, env, i32, i64, i64) -DEF_HELPER_4(tre, i64, env, i64, i64, i64) +DEF_HELPER_4(tre, i128, env, i64, i64, i64) DEF_HELPER_4(trt, i32, env, i32, i64, i64) DEF_HELPER_4(trtr, i32, env, i32, i64, i64) DEF_HELPER_5(trXX, i32, env, i32, i32, i32, i32) -DEF_HELPER_4(cksm, i64, env, i64, i64, i64) +DEF_HELPER_4(cksm, i128, env, i64, i64, i64) DEF_HELPER_FLAGS_5(calc_cc, TCG_CALL_NO_RWG_SE, i32, env, i32, i64, i64, i64) DEF_HELPER_FLAGS_2(sfpc, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_2(sfas, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_2(srnm, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_1(popcnt, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_2(stfle, i32, env, i64) -DEF_HELPER_FLAGS_2(lpq, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(lpq_parallel, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_4(stpq, TCG_CALL_NO_WG, void, env, i64, i64, i64) -DEF_HELPER_FLAGS_4(stpq_parallel, TCG_CALL_NO_WG, void, env, i64, i64, i64) DEF_HELPER_4(mvcos, i32, env, i64, i64, i64) DEF_HELPER_4(cu12, i32, env, i32, i32, i32) DEF_HELPER_4(cu14, i32, env, i32, i32, i32) @@ -353,15 +350,15 @@ DEF_HELPER_FLAGS_3(tprot, TCG_CALL_NO_WG, i32, env, i64, i64) DEF_HELPER_2(iske, i64, env, i64) DEF_HELPER_3(sske, void, env, i64, i64) DEF_HELPER_2(rrbe, i32, env, i64) -DEF_HELPER_4(mvcs, i32, env, i64, i64, i64) -DEF_HELPER_4(mvcp, i32, env, i64, i64, i64) +DEF_HELPER_5(mvcs, i32, env, i64, i64, i64, i64) +DEF_HELPER_5(mvcp, i32, env, i64, i64, i64, i64) DEF_HELPER_4(sigp, i32, env, i64, i32, i32) DEF_HELPER_FLAGS_2(sacf, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_4(idte, TCG_CALL_NO_RWG, void, env, i64, i64, i32) 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_3(lra, i64, 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) diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c index b12f18d346..bbe45a497a 100644 --- a/target/s390x/ioinst.c +++ b/target/s390x/ioinst.c @@ -16,7 +16,7 @@ #include "hw/s390x/ioinst.h" #include "trace.h" #include "hw/s390x/s390-pci-bus.h" -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" /* All I/O instructions but chsc use the s format */ static uint64_t get_address_from_regs(CPUS390XState *env, uint32_t ipb, @@ -285,7 +285,7 @@ void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, /* * As operand exceptions have a lower priority than access exceptions, * we check whether the memory area is writable (injecting the - * access execption if it is not) first. + * access exception if it is not) first. */ if (!s390_cpu_virt_mem_check_write(cpu, addr, ar, sizeof(schib))) { s390_program_interrupt(env, PGM_OPERAND, ra); diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 7bd8db0e7b..4ce809c5d4 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -50,17 +50,7 @@ #include "exec/memattrs.h" #include "hw/s390x/s390-virtio-ccw.h" #include "hw/s390x/s390-virtio-hcall.h" -#include "hw/s390x/pv.h" - -#ifndef DEBUG_KVM -#define DEBUG_KVM 0 -#endif - -#define DPRINTF(fmt, ...) do { \ - if (DEBUG_KVM) { \ - fprintf(stderr, fmt, ## __VA_ARGS__); \ - } \ -} while (0) +#include "target/s390x/kvm/pv.h" #define kvm_vm_check_mem_attr(s, attr) \ kvm_vm_check_attr(s, KVM_S390_VM_MEM_CTRL, attr) @@ -96,6 +86,7 @@ #define PRIV_B9_EQBS 0x9c #define PRIV_B9_CLP 0xa0 +#define PRIV_B9_PTF 0xa2 #define PRIV_B9_PCISTG 0xd0 #define PRIV_B9_PCILG 0xd2 #define PRIV_B9_RPCIT 0xd3 @@ -148,7 +139,6 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; -static int cap_sync_regs; static int cap_async_pf; static int cap_mem_op; static int cap_mem_op_extension; @@ -157,6 +147,8 @@ static int cap_ri; static int cap_hpage_1m; static int cap_vcpu_resets; static int cap_protected; +static int cap_zpci_op; +static int cap_protected_dump; static bool mem_op_storage_key_support; @@ -248,7 +240,7 @@ static void kvm_s390_enable_cmma(void) trace_kvm_enable_cmma(rc); } -static void kvm_s390_set_attr(uint64_t attr) +static void kvm_s390_set_crypto_attr(uint64_t attr) { struct kvm_device_attr attribute = { .group = KVM_S390_VM_CRYPTO, @@ -273,7 +265,7 @@ static void kvm_s390_init_aes_kw(void) } if (kvm_vm_check_attr(kvm_state, KVM_S390_VM_CRYPTO, attr)) { - kvm_s390_set_attr(attr); + kvm_s390_set_crypto_attr(attr); } } @@ -287,7 +279,7 @@ static void kvm_s390_init_dea_kw(void) } if (kvm_vm_check_attr(kvm_state, KVM_S390_VM_CRYPTO, attr)) { - kvm_s390_set_attr(attr); + kvm_s390_set_crypto_attr(attr); } } @@ -338,23 +330,35 @@ static void ccw_machine_class_foreach(ObjectClass *oc, void *opaque) mc->default_cpu_type = S390_CPU_TYPE_NAME("host"); } +int kvm_arch_get_default_type(MachineState *ms) +{ + return 0; +} + int kvm_arch_init(MachineState *ms, KVMState *s) { + int required_caps[] = { + KVM_CAP_DEVICE_CTRL, + KVM_CAP_SYNC_REGS, + }; + + for (int i = 0; i < ARRAY_SIZE(required_caps); i++) { + if (!kvm_check_extension(s, required_caps[i])) { + error_report("KVM is missing capability #%d - " + "please use kernel 3.15 or newer", required_caps[i]); + return -1; + } + } + object_class_foreach(ccw_machine_class_foreach, TYPE_S390_CCW_MACHINE, false, NULL); - if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) { - error_report("KVM is missing capability KVM_CAP_DEVICE_CTRL - " - "please use kernel 3.15 or newer"); - return -1; - } if (!kvm_check_extension(s, KVM_CAP_S390_COW)) { error_report("KVM is missing capability KVM_CAP_S390_COW - " "unsupported environment"); return -1; } - cap_sync_regs = kvm_check_extension(s, KVM_CAP_SYNC_REGS); cap_async_pf = kvm_check_extension(s, KVM_CAP_ASYNC_PF); cap_mem_op = kvm_check_extension(s, KVM_CAP_S390_MEM_OP); cap_mem_op_extension = kvm_check_extension(s, KVM_CAP_S390_MEM_OP_EXTENSION); @@ -362,10 +366,13 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_s390_irq = kvm_check_extension(s, KVM_CAP_S390_INJECT_IRQ); cap_vcpu_resets = kvm_check_extension(s, KVM_CAP_S390_VCPU_RESETS); cap_protected = kvm_check_extension(s, KVM_CAP_S390_PROTECTED); + cap_zpci_op = kvm_check_extension(s, KVM_CAP_S390_ZPCI_OP); + cap_protected_dump = kvm_check_extension(s, KVM_CAP_S390_PROTECTED_DUMP); kvm_vm_enable_cap(s, KVM_CAP_S390_USER_SIGP, 0); kvm_vm_enable_cap(s, KVM_CAP_S390_VECTOR_REGISTERS, 0); kvm_vm_enable_cap(s, KVM_CAP_S390_USER_STSI, 0); + kvm_vm_enable_cap(s, KVM_CAP_S390_CPU_TOPOLOGY, 0); if (ri_allowed()) { if (kvm_vm_enable_cap(s, KVM_CAP_S390_RI, 0) == 0) { cap_ri = 1; @@ -459,37 +466,27 @@ void kvm_s390_reset_vcpu_normal(S390CPU *cpu) static int can_sync_regs(CPUState *cs, int regs) { - return cap_sync_regs && (cs->kvm_run->kvm_valid_regs & regs) == regs; + return (cs->kvm_run->kvm_valid_regs & regs) == regs; } +#define KVM_SYNC_REQUIRED_REGS (KVM_SYNC_GPRS | KVM_SYNC_ACRS | \ + KVM_SYNC_CRS | KVM_SYNC_PREFIX) + int kvm_arch_put_registers(CPUState *cs, int level) { - S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; - struct kvm_sregs sregs; - struct kvm_regs regs; + CPUS390XState *env = cpu_env(cs); struct kvm_fpu fpu = {}; int r; int i; + g_assert(can_sync_regs(cs, KVM_SYNC_REQUIRED_REGS)); + /* always save the PSW and the GPRS*/ cs->kvm_run->psw_addr = env->psw.addr; cs->kvm_run->psw_mask = env->psw.mask; - if (can_sync_regs(cs, KVM_SYNC_GPRS)) { - for (i = 0; i < 16; i++) { - cs->kvm_run->s.regs.gprs[i] = env->regs[i]; - cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_GPRS; - } - } else { - for (i = 0; i < 16; i++) { - regs.gprs[i] = env->regs[i]; - } - r = kvm_vcpu_ioctl(cs, KVM_SET_REGS, ®s); - if (r < 0) { - return r; - } - } + memcpy(cs->kvm_run->s.regs.gprs, env->regs, sizeof(cs->kvm_run->s.regs.gprs)); + cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_GPRS; if (can_sync_regs(cs, KVM_SYNC_VRS)) { for (i = 0; i < 32; i++) { @@ -522,6 +519,15 @@ int kvm_arch_put_registers(CPUState *cs, int level) return 0; } + /* + * Access registers, control registers and the prefix - these are + * always available via kvm_sync_regs in the kernels that we support + */ + memcpy(cs->kvm_run->s.regs.acrs, env->aregs, sizeof(cs->kvm_run->s.regs.acrs)); + memcpy(cs->kvm_run->s.regs.crs, env->cregs, sizeof(cs->kvm_run->s.regs.crs)); + cs->kvm_run->s.regs.prefix = env->psa; + cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_ACRS | KVM_SYNC_CRS | KVM_SYNC_PREFIX; + if (can_sync_regs(cs, KVM_SYNC_ARCH0)) { cs->kvm_run->s.regs.cputm = env->cputm; cs->kvm_run->s.regs.ckc = env->ckc; @@ -568,25 +574,6 @@ int kvm_arch_put_registers(CPUState *cs, int level) } } - /* access registers and control registers*/ - if (can_sync_regs(cs, KVM_SYNC_ACRS | KVM_SYNC_CRS)) { - for (i = 0; i < 16; i++) { - cs->kvm_run->s.regs.acrs[i] = env->aregs[i]; - cs->kvm_run->s.regs.crs[i] = env->cregs[i]; - } - cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_ACRS; - cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_CRS; - } else { - for (i = 0; i < 16; i++) { - sregs.acrs[i] = env->aregs[i]; - sregs.crs[i] = env->cregs[i]; - } - r = kvm_vcpu_ioctl(cs, KVM_SET_SREGS, &sregs); - if (r < 0) { - return r; - } - } - if (can_sync_regs(cs, KVM_SYNC_GSCB)) { memcpy(cs->kvm_run->s.regs.gscb, env->gscb, 32); cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_GSCB; @@ -608,22 +595,12 @@ int kvm_arch_put_registers(CPUState *cs, int level) cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_DIAG318; } - /* Finally the prefix */ - if (can_sync_regs(cs, KVM_SYNC_PREFIX)) { - cs->kvm_run->s.regs.prefix = env->psa; - cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_PREFIX; - } else { - /* prefix is only supported via sync regs */ - } return 0; } int kvm_arch_get_registers(CPUState *cs) { - S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; - struct kvm_sregs sregs; - struct kvm_regs regs; + CPUS390XState *env = cpu_env(cs); struct kvm_fpu fpu; int i, r; @@ -631,37 +608,14 @@ int kvm_arch_get_registers(CPUState *cs) env->psw.addr = cs->kvm_run->psw_addr; env->psw.mask = cs->kvm_run->psw_mask; - /* the GPRS */ - if (can_sync_regs(cs, KVM_SYNC_GPRS)) { - for (i = 0; i < 16; i++) { - env->regs[i] = cs->kvm_run->s.regs.gprs[i]; - } - } else { - r = kvm_vcpu_ioctl(cs, KVM_GET_REGS, ®s); - if (r < 0) { - return r; - } - for (i = 0; i < 16; i++) { - env->regs[i] = regs.gprs[i]; - } - } + /* the GPRS, ACRS and CRS */ + g_assert(can_sync_regs(cs, KVM_SYNC_REQUIRED_REGS)); + memcpy(env->regs, cs->kvm_run->s.regs.gprs, sizeof(env->regs)); + memcpy(env->aregs, cs->kvm_run->s.regs.acrs, sizeof(env->aregs)); + memcpy(env->cregs, cs->kvm_run->s.regs.crs, sizeof(env->cregs)); - /* The ACRS and CRS */ - if (can_sync_regs(cs, KVM_SYNC_ACRS | KVM_SYNC_CRS)) { - for (i = 0; i < 16; i++) { - env->aregs[i] = cs->kvm_run->s.regs.acrs[i]; - env->cregs[i] = cs->kvm_run->s.regs.crs[i]; - } - } else { - r = kvm_vcpu_ioctl(cs, KVM_GET_SREGS, &sregs); - if (r < 0) { - return r; - } - for (i = 0; i < 16; i++) { - env->aregs[i] = sregs.acrs[i]; - env->cregs[i] = sregs.crs[i]; - } - } + /* The prefix */ + env->psa = cs->kvm_run->s.regs.prefix; /* Floating point and vector registers */ if (can_sync_regs(cs, KVM_SYNC_VRS)) { @@ -686,11 +640,6 @@ int kvm_arch_get_registers(CPUState *cs) env->fpc = fpu.fpc; } - /* The prefix */ - if (can_sync_regs(cs, KVM_SYNC_PREFIX)) { - env->psa = cs->kvm_run->s.regs.prefix; - } - if (can_sync_regs(cs, KVM_SYNC_ARCH0)) { env->cputm = cs->kvm_run->s.regs.cputm; env->ckc = cs->kvm_run->s.regs.ckc; @@ -908,11 +857,11 @@ static void determine_sw_breakpoint_instr(void) if (kvm_vm_enable_cap(kvm_state, KVM_CAP_S390_USER_INSTR0, 0)) { sw_bp_inst = diag_501; sw_bp_ilen = sizeof(diag_501); - DPRINTF("KVM: will use 4-byte sw breakpoints.\n"); + trace_kvm_sw_breakpoint(4); } else { sw_bp_inst = instr_0x0000; sw_bp_ilen = sizeof(instr_0x0000); - DPRINTF("KVM: will use 2-byte sw breakpoints.\n"); + trace_kvm_sw_breakpoint(2); } } @@ -991,8 +940,7 @@ static int insert_hw_breakpoint(target_ulong addr, int len, int type) return 0; } -int kvm_arch_insert_hw_breakpoint(target_ulong addr, - target_ulong len, int type) +int kvm_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type) { switch (type) { case GDB_BREAKPOINT_HW: @@ -1010,8 +958,7 @@ int kvm_arch_insert_hw_breakpoint(target_ulong addr, return insert_hw_breakpoint(addr, len, type); } -int kvm_arch_remove_hw_breakpoint(target_ulong addr, - target_ulong len, int type) +int kvm_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type) { int size; struct kvm_hw_breakpoint *bp = find_hw_breakpoint(addr, len, type); @@ -1031,7 +978,7 @@ int kvm_arch_remove_hw_breakpoint(target_ulong addr, } size = nb_hw_breakpoints * sizeof(struct kvm_hw_breakpoint); hw_breakpoints = - (struct kvm_hw_breakpoint *)g_realloc(hw_breakpoints, size); + g_realloc(hw_breakpoints, size); } else { g_free(hw_breakpoints); hw_breakpoints = NULL; @@ -1225,12 +1172,12 @@ static void kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, break; case ICPT_PV_INSTR: g_assert(s390_is_pv()); - sclp_service_call_protected(env, sccb, code); + sclp_service_call_protected(cpu, sccb, code); /* Setting the CC is done by the Ultravisor. */ break; case ICPT_INSTRUCTION: g_assert(!s390_is_pv()); - r = sclp_service_call(env, sccb, code); + r = sclp_service_call(cpu, sccb, code); if (r < 0) { kvm_s390_program_interrupt(cpu, -r); return; @@ -1303,7 +1250,7 @@ static int handle_b2(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) break; default: rc = -1; - DPRINTF("KVM: unhandled PRIV: 0xb2%x\n", ipa1); + trace_kvm_insn_unhandled_priv(ipa1); break; } @@ -1409,7 +1356,7 @@ static int kvm_sic_service_call(S390CPU *cpu, struct kvm_run *run) mode = env->regs[r1] & 0xffff; isc = (env->regs[r3] >> 27) & 0x7; - r = css_do_sic(env, isc, mode); + r = css_do_sic(cpu, isc, mode); if (r) { kvm_s390_program_interrupt(cpu, -r); } @@ -1460,6 +1407,13 @@ static int kvm_mpcifc_service_call(S390CPU *cpu, struct kvm_run *run) } } +static void kvm_handle_ptf(S390CPU *cpu, struct kvm_run *run) +{ + uint8_t r1 = (run->s390_sieic.ipb >> 20) & 0x0f; + + s390_handle_ptf(cpu, r1, RA_IGNORED); +} + static int handle_b9(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) { int r = 0; @@ -1477,13 +1431,16 @@ static int handle_b9(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) case PRIV_B9_RPCIT: r = kvm_rpcit_service_call(cpu, run); break; + case PRIV_B9_PTF: + kvm_handle_ptf(cpu, run); + break; case PRIV_B9_EQBS: /* just inject exception */ r = -1; break; default: r = -1; - DPRINTF("KVM: unhandled PRIV: 0xb9%x\n", ipa1); + trace_kvm_insn_unhandled_priv(ipa1); break; } @@ -1507,7 +1464,7 @@ static int handle_eb(S390CPU *cpu, struct kvm_run *run, uint8_t ipbl) break; default: r = -1; - DPRINTF("KVM: unhandled PRIV: 0xeb%x\n", ipbl); + trace_kvm_insn_unhandled_priv(ipbl); break; } @@ -1527,7 +1484,7 @@ static int handle_e3(S390CPU *cpu, struct kvm_run *run, uint8_t ipbl) break; default: r = -1; - DPRINTF("KVM: unhandled PRIV: 0xe3%x\n", ipbl); + trace_kvm_insn_unhandled_priv(ipbl); break; } @@ -1650,7 +1607,7 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb) r = handle_sw_breakpoint(cpu, run); break; default: - DPRINTF("KVM: unknown DIAG: 0x%x\n", func_code); + trace_kvm_insn_diag(func_code); kvm_s390_program_interrupt(cpu, PGM_SPECIFICATION); break; } @@ -1680,8 +1637,7 @@ static int handle_instruction(S390CPU *cpu, struct kvm_run *run) uint8_t ipa1 = run->s390_sieic.ipa & 0x00ff; int r = -1; - DPRINTF("handle_instruction 0x%x 0x%x\n", - run->s390_sieic.ipa, run->s390_sieic.ipb); + trace_kvm_insn(run->s390_sieic.ipa, run->s390_sieic.ipb); switch (ipa0) { case IPA0_B2: r = handle_b2(cpu, run, ipa1); @@ -1761,7 +1717,7 @@ static int handle_intercept(S390CPU *cpu) int icpt_code = run->s390_sieic.icptcode; int r = 0; - DPRINTF("intercept: 0x%x (at 0x%lx)\n", icpt_code, (long)run->psw_addr); + trace_kvm_intercept(icpt_code, (long)run->psw_addr); switch (icpt_code) { case ICPT_INSTRUCTION: case ICPT_PV_INSTR: @@ -1915,9 +1871,12 @@ static int handle_stsi(S390CPU *cpu) if (run->s390_stsi.sel1 != 2 || run->s390_stsi.sel2 != 2) { return 0; } - /* Only sysib 3.2.2 needs post-handling for now. */ insert_stsi_3_2_2(cpu, run->s390_stsi.addr, run->s390_stsi.ar); return 0; + case 15: + insert_stsi_15_1_x(cpu, run->s390_stsi.sel2, run->s390_stsi.addr, + run->s390_stsi.ar, RA_IGNORED); + return 0; default: return 0; } @@ -1962,7 +1921,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) S390CPU *cpu = S390_CPU(cs); int ret = 0; - qemu_mutex_lock_iothread(); + bql_lock(); kvm_cpu_synchronize_state(cs); @@ -1986,7 +1945,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) fprintf(stderr, "Unknown KVM exit: %d\n", run->exit_reason); break; } - qemu_mutex_unlock_iothread(); + bql_unlock(); if (ret == 0) { ret = EXCP_INTERRUPT; @@ -2043,6 +2002,11 @@ int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch, return kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick); } +int kvm_s390_get_protected_dump(void) +{ + return cap_protected_dump; +} + int kvm_s390_get_ri(void) { return cap_ri; @@ -2141,13 +2105,13 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, uint32_t vec = data & ZPCI_MSI_VEC_MASK; if (!dev) { - DPRINTF("add_msi_route no pci device\n"); + trace_kvm_msi_route_fixup("no pci device"); return -ENODEV; } pbdev = s390_pci_find_dev_by_target(s390_get_phb(), DEVICE(dev)->id); if (!pbdev) { - DPRINTF("add_msi_route no zpci device\n"); + trace_kvm_msi_route_fixup("no zpci device"); return -ENODEV; } @@ -2287,6 +2251,53 @@ static int configure_cpu_subfunc(const S390FeatBitmap features) return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); } +static bool ap_available(void) +{ + return kvm_vm_check_attr(kvm_state, KVM_S390_VM_CRYPTO, + KVM_S390_VM_CRYPTO_ENABLE_APIE); +} + +static bool ap_enabled(const S390FeatBitmap features) +{ + return test_bit(S390_FEAT_AP, features); +} + +static bool uv_feat_supported(void) +{ + return kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL, + KVM_S390_VM_CPU_PROCESSOR_UV_FEAT_GUEST); +} + +static int query_uv_feat_guest(S390FeatBitmap features) +{ + struct kvm_s390_vm_cpu_uv_feat prop = {}; + struct kvm_device_attr attr = { + .group = KVM_S390_VM_CPU_MODEL, + .attr = KVM_S390_VM_CPU_MACHINE_UV_FEAT_GUEST, + .addr = (uint64_t) &prop, + }; + int rc; + + /* AP support check is currently the only user of the UV feature test */ + if (!(uv_feat_supported() && ap_available())) { + return 0; + } + + rc = kvm_vm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr); + if (rc) { + return rc; + } + + if (prop.ap) { + set_bit(S390_FEAT_UV_FEAT_AP, features); + } + if (prop.ap_intr) { + set_bit(S390_FEAT_UV_FEAT_AP_INTR, features); + } + + return 0; +} + static int kvm_to_feat[][2] = { { KVM_S390_VM_CPU_FEAT_ESOP, S390_FEAT_ESOP }, { KVM_S390_VM_CPU_FEAT_SIEF2, S390_FEAT_SIE_F2 }, @@ -2447,6 +2458,14 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) set_bit(S390_FEAT_UNPACK, model->features); } + /* + * If we have kernel support for CPU Topology indicate the + * configuration-topology facility. + */ + if (kvm_check_extension(kvm_state, KVM_CAP_S390_CPU_TOPOLOGY)) { + set_bit(S390_FEAT_CONFIGURATION_TOPOLOGY, model->features); + } + /* We emulate a zPCI bus and AEN, therefore we don't need HW support */ set_bit(S390_FEAT_ZPCI, model->features); set_bit(S390_FEAT_ADAPTER_EVENT_NOTIFICATION, model->features); @@ -2466,8 +2485,7 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) return; } /* for now, we can only provide the AP feature with HW support */ - if (kvm_vm_check_attr(kvm_state, KVM_S390_VM_CRYPTO, - KVM_S390_VM_CRYPTO_ENABLE_APIE)) { + if (ap_available()) { set_bit(S390_FEAT_AP, model->features); } @@ -2482,18 +2500,45 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) set_bit(S390_FEAT_DIAG_318, model->features); } + /* Test for Ultravisor features that influence secure guest behavior */ + query_uv_feat_guest(model->features); + /* strip of features that are not part of the maximum model */ bitmap_and(model->features, model->features, model->def->full_feat, S390_FEAT_MAX); } +static int configure_uv_feat_guest(const S390FeatBitmap features) +{ + struct kvm_s390_vm_cpu_uv_feat uv_feat = {}; + struct kvm_device_attr attribute = { + .group = KVM_S390_VM_CPU_MODEL, + .attr = KVM_S390_VM_CPU_PROCESSOR_UV_FEAT_GUEST, + .addr = (__u64) &uv_feat, + }; + + /* AP support check is currently the only user of the UV feature test */ + if (!(uv_feat_supported() && ap_enabled(features))) { + return 0; + } + + if (test_bit(S390_FEAT_UV_FEAT_AP, features)) { + uv_feat.ap = 1; + } + if (test_bit(S390_FEAT_UV_FEAT_AP_INTR, features)) { + uv_feat.ap_intr = 1; + } + + return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attribute); +} + static void kvm_s390_configure_apie(bool interpret) { uint64_t attr = interpret ? KVM_S390_VM_CRYPTO_ENABLE_APIE : KVM_S390_VM_CRYPTO_DISABLE_APIE; if (kvm_vm_check_attr(kvm_state, KVM_S390_VM_CRYPTO, attr)) { - kvm_s390_set_attr(attr); + kvm_s390_set_crypto_attr(attr); } } @@ -2547,9 +2592,16 @@ void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) kvm_s390_enable_cmma(); } - if (test_bit(S390_FEAT_AP, model->features)) { + if (ap_enabled(model->features)) { kvm_s390_configure_apie(true); } + + /* configure UV-features for the guest indicated via query / test_bit */ + rc = configure_uv_feat_guest(model->features); + if (rc) { + error_setg(errp, "KVM: Error configuring CPU UV features %d", rc); + return; + } } void kvm_s390_restart_interrupt(S390CPU *cpu) @@ -2574,3 +2626,29 @@ bool kvm_arch_cpu_check_are_resettable(void) { return true; } + +int kvm_s390_get_zpci_op(void) +{ + return cap_zpci_op; +} + +int kvm_s390_topology_set_mtcr(uint64_t attr) +{ + struct kvm_device_attr attribute = { + .group = KVM_S390_VM_CPU_TOPOLOGY, + .attr = attr, + }; + + if (!s390_has_feat(S390_FEAT_CONFIGURATION_TOPOLOGY)) { + return 0; + } + if (!kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_TOPOLOGY, attr)) { + return -ENOTSUP; + } + + return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attribute); +} + +void kvm_arch_accel_class_init(ObjectClass *oc) +{ +} diff --git a/target/s390x/kvm/kvm_s390x.h b/target/s390x/kvm/kvm_s390x.h index 05a5e1e6f4..649dae5948 100644 --- a/target/s390x/kvm/kvm_s390x.h +++ b/target/s390x/kvm/kvm_s390x.h @@ -26,7 +26,9 @@ int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state); void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu); int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu); int kvm_s390_get_hpage_1m(void); +int kvm_s390_get_protected_dump(void); int kvm_s390_get_ri(void); +int kvm_s390_get_zpci_op(void); int kvm_s390_get_clock(uint8_t *tod_high, uint64_t *tod_clock); int kvm_s390_get_clock_ext(uint8_t *tod_high, uint64_t *tod_clock); int kvm_s390_set_clock(uint8_t tod_high, uint64_t tod_clock); @@ -45,5 +47,6 @@ void kvm_s390_crypto_reset(void); void kvm_s390_restart_interrupt(S390CPU *cpu); void kvm_s390_stop_interrupt(S390CPU *cpu); void kvm_s390_set_diag318(CPUState *cs, uint64_t diag318_info); +int kvm_s390_topology_set_mtcr(uint64_t attr); #endif /* KVM_S390X_H */ diff --git a/target/s390x/kvm/meson.build b/target/s390x/kvm/meson.build index d1356356b1..588a9aa737 100644 --- a/target/s390x/kvm/meson.build +++ b/target/s390x/kvm/meson.build @@ -1,6 +1,10 @@ s390x_ss.add(when: 'CONFIG_KVM', if_true: files( - 'kvm.c' + 'pv.c', + 'kvm.c', + 'stsi-topology.c' +), if_false: files( + 'stubs.c' )) # Newer kernels on s390 check for an S390_PGSTE program header and @@ -12,6 +16,6 @@ s390x_ss.add(when: 'CONFIG_KVM', if_true: files( # - KVM is enabled # - the linker supports --s390-pgste if host_machine.cpu_family() == 's390x' and cc.has_link_argument('-Wl,--s390-pgste') - s390x_softmmu_ss.add(when: 'CONFIG_KVM', + s390x_system_ss.add(when: 'CONFIG_KVM', if_true: declare_dependency(link_args: ['-Wl,--s390-pgste'])) endif diff --git a/target/s390x/kvm/pv.c b/target/s390x/kvm/pv.c new file mode 100644 index 0000000000..7ca7faec73 --- /dev/null +++ b/target/s390x/kvm/pv.c @@ -0,0 +1,375 @@ +/* + * Protected Virtualization functions + * + * Copyright IBM Corp. 2020 + * Author(s): + * Janosch Frank + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ +#include "qemu/osdep.h" + +#include + +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "sysemu/kvm.h" +#include "sysemu/cpus.h" +#include "qom/object_interfaces.h" +#include "exec/confidential-guest-support.h" +#include "hw/s390x/ipl.h" +#include "hw/s390x/sclp.h" +#include "target/s390x/kvm/kvm_s390x.h" +#include "target/s390x/kvm/pv.h" + +static bool info_valid; +static struct kvm_s390_pv_info_vm info_vm; +static struct kvm_s390_pv_info_dump info_dump; + +static int __s390_pv_cmd(uint32_t cmd, const char *cmdname, void *data, + int *pvrc) +{ + struct kvm_pv_cmd pv_cmd = { + .cmd = cmd, + .data = (uint64_t)data, + }; + int rc; + + do { + rc = kvm_vm_ioctl(kvm_state, KVM_S390_PV_COMMAND, &pv_cmd); + } while (rc == -EINTR); + + if (rc) { + error_report("KVM PV command %d (%s) failed: header rc %x rrc %x " + "IOCTL rc: %d", cmd, cmdname, pv_cmd.rc, pv_cmd.rrc, + rc); + } + if (pvrc) { + *pvrc = pv_cmd.rc; + } + return rc; +} + +/* + * This macro lets us pass the command as a string to the function so + * we can print it on an error. + */ +#define s390_pv_cmd(cmd, data) __s390_pv_cmd(cmd, #cmd, data, NULL) +#define s390_pv_cmd_pvrc(cmd, data, pvrc) __s390_pv_cmd(cmd, #cmd, data, pvrc) +#define s390_pv_cmd_exit(cmd, data) \ +{ \ + int rc; \ + \ + rc = __s390_pv_cmd(cmd, #cmd, data, NULL); \ + if (rc) { \ + exit(1); \ + } \ +} + +int s390_pv_query_info(void) +{ + struct kvm_s390_pv_info info = { + .header.id = KVM_PV_INFO_VM, + .header.len_max = sizeof(info.header) + sizeof(info.vm), + }; + int rc; + + /* Info API's first user is dump so they are bundled */ + if (!kvm_s390_get_protected_dump()) { + return 0; + } + + rc = s390_pv_cmd(KVM_PV_INFO, &info); + if (rc) { + error_report("KVM PV INFO cmd %x failed: %s", + info.header.id, strerror(-rc)); + return rc; + } + memcpy(&info_vm, &info.vm, sizeof(info.vm)); + + info.header.id = KVM_PV_INFO_DUMP; + info.header.len_max = sizeof(info.header) + sizeof(info.dump); + rc = s390_pv_cmd(KVM_PV_INFO, &info); + if (rc) { + error_report("KVM PV INFO cmd %x failed: %s", + info.header.id, strerror(-rc)); + return rc; + } + + memcpy(&info_dump, &info.dump, sizeof(info.dump)); + info_valid = true; + + return rc; +} + +int s390_pv_vm_enable(void) +{ + return s390_pv_cmd(KVM_PV_ENABLE, NULL); +} + +void s390_pv_vm_disable(void) +{ + s390_pv_cmd_exit(KVM_PV_DISABLE, NULL); +} + +static void *s390_pv_do_unprot_async_fn(void *p) +{ + s390_pv_cmd_exit(KVM_PV_ASYNC_CLEANUP_PERFORM, NULL); + return NULL; +} + +bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms) +{ + /* + * t is only needed to create the thread; once qemu_thread_create + * returns, it can safely be discarded. + */ + QemuThread t; + + /* + * If the feature is not present or if the VM is not larger than 2 GiB, + * KVM_PV_ASYNC_CLEANUP_PREPARE fill fail; no point in attempting it. + */ + if ((MACHINE(ms)->maxram_size <= 2 * GiB) || + !kvm_check_extension(kvm_state, KVM_CAP_S390_PROTECTED_ASYNC_DISABLE)) { + return false; + } + if (s390_pv_cmd(KVM_PV_ASYNC_CLEANUP_PREPARE, NULL) != 0) { + return false; + } + + qemu_thread_create(&t, "async_cleanup", s390_pv_do_unprot_async_fn, NULL, + QEMU_THREAD_DETACHED); + + return true; +} + +int s390_pv_set_sec_parms(uint64_t origin, uint64_t length, Error **errp) +{ + int ret, pvrc; + struct kvm_s390_pv_sec_parm args = { + .origin = origin, + .length = length, + }; + + ret = s390_pv_cmd_pvrc(KVM_PV_SET_SEC_PARMS, &args, &pvrc); + if (ret) { + error_setg(errp, "Failed to set secure execution parameters"); + if (pvrc == 0x108) { + error_append_hint(errp, "Please check whether the image is " + "correctly encrypted for this host\n"); + } + } + + return ret; +} + +/* + * Called for each component in the SE type IPL parameter block 0. + */ +int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) +{ + struct kvm_s390_pv_unp args = { + .addr = addr, + .size = size, + .tweak = tweak, + }; + + return s390_pv_cmd(KVM_PV_UNPACK, &args); +} + +void s390_pv_prep_reset(void) +{ + s390_pv_cmd_exit(KVM_PV_PREP_RESET, NULL); +} + +int s390_pv_verify(void) +{ + return s390_pv_cmd(KVM_PV_VERIFY, NULL); +} + +void s390_pv_unshare(void) +{ + s390_pv_cmd_exit(KVM_PV_UNSHARE_ALL, NULL); +} + +void s390_pv_inject_reset_error(CPUState *cs) +{ + int r1 = (cs->kvm_run->s390_sieic.ipa & 0x00f0) >> 4; + CPUS390XState *env = &S390_CPU(cs)->env; + + /* Report that we are unable to enter protected mode */ + env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV; +} + +uint64_t kvm_s390_pv_dmp_get_size_cpu(void) +{ + return info_dump.dump_cpu_buffer_len; +} + +uint64_t kvm_s390_pv_dmp_get_size_completion_data(void) +{ + return info_dump.dump_config_finalize_len; +} + +uint64_t kvm_s390_pv_dmp_get_size_mem_state(void) +{ + return info_dump.dump_config_mem_buffer_per_1m; +} + +bool kvm_s390_pv_info_basic_valid(void) +{ + return info_valid; +} + +static int s390_pv_dump_cmd(uint64_t subcmd, uint64_t uaddr, uint64_t gaddr, + uint64_t len) +{ + struct kvm_s390_pv_dmp dmp = { + .subcmd = subcmd, + .buff_addr = uaddr, + .buff_len = len, + .gaddr = gaddr, + }; + int ret; + + ret = s390_pv_cmd(KVM_PV_DUMP, (void *)&dmp); + if (ret) { + error_report("KVM DUMP command %ld failed", subcmd); + } + return ret; +} + +int kvm_s390_dump_cpu(S390CPU *cpu, void *buff) +{ + struct kvm_s390_pv_dmp dmp = { + .subcmd = KVM_PV_DUMP_CPU, + .buff_addr = (uint64_t)buff, + .gaddr = 0, + .buff_len = info_dump.dump_cpu_buffer_len, + }; + struct kvm_pv_cmd pv = { + .cmd = KVM_PV_DUMP, + .data = (uint64_t)&dmp, + }; + + return kvm_vcpu_ioctl(CPU(cpu), KVM_S390_PV_CPU_COMMAND, &pv); +} + +int kvm_s390_dump_init(void) +{ + return s390_pv_dump_cmd(KVM_PV_DUMP_INIT, 0, 0, 0); +} + +int kvm_s390_dump_mem_state(uint64_t gaddr, size_t len, void *dest) +{ + return s390_pv_dump_cmd(KVM_PV_DUMP_CONFIG_STOR_STATE, (uint64_t)dest, + gaddr, len); +} + +int kvm_s390_dump_completion_data(void *buff) +{ + return s390_pv_dump_cmd(KVM_PV_DUMP_COMPLETE, (uint64_t)buff, 0, + info_dump.dump_config_finalize_len); +} + +#define TYPE_S390_PV_GUEST "s390-pv-guest" +OBJECT_DECLARE_SIMPLE_TYPE(S390PVGuest, S390_PV_GUEST) + +/** + * S390PVGuest: + * + * The S390PVGuest object is basically a dummy used to tell the + * confidential guest support system to use s390's PV mechanism. + * + * # $QEMU \ + * -object s390-pv-guest,id=pv0 \ + * -machine ...,confidential-guest-support=pv0 + */ +struct S390PVGuest { + ConfidentialGuestSupport parent_obj; +}; + +typedef struct S390PVGuestClass S390PVGuestClass; + +struct S390PVGuestClass { + ConfidentialGuestSupportClass parent_class; +}; + +/* + * If protected virtualization is enabled, the amount of data that the + * Read SCP Info Service Call can use is limited to one page. The + * available space also depends on the Extended-Length SCCB (ELS) + * feature which can take more buffer space to store feature + * information. This impacts the maximum number of CPUs supported in + * the machine. + */ +static uint32_t s390_pv_get_max_cpus(void) +{ + int offset_cpu = s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB) ? + offsetof(ReadInfo, entries) : SCLP_READ_SCP_INFO_FIXED_CPU_OFFSET; + + return (TARGET_PAGE_SIZE - offset_cpu) / sizeof(CPUEntry); +} + +static bool s390_pv_check_cpus(Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + uint32_t pv_max_cpus = s390_pv_get_max_cpus(); + + if (ms->smp.max_cpus > pv_max_cpus) { + error_setg(errp, "Protected VMs support a maximum of %d CPUs", + pv_max_cpus); + return false; + } + + return true; +} + +static bool s390_pv_guest_check(ConfidentialGuestSupport *cgs, Error **errp) +{ + return s390_pv_check_cpus(errp); +} + +int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +{ + if (!object_dynamic_cast(OBJECT(cgs), TYPE_S390_PV_GUEST)) { + return 0; + } + + if (!s390_has_feat(S390_FEAT_UNPACK)) { + error_setg(errp, + "CPU model does not support Protected Virtualization"); + return -1; + } + + if (!s390_pv_guest_check(cgs, errp)) { + return -1; + } + + cgs->ready = true; + + return 0; +} + +OBJECT_DEFINE_TYPE_WITH_INTERFACES(S390PVGuest, + s390_pv_guest, + S390_PV_GUEST, + CONFIDENTIAL_GUEST_SUPPORT, + { TYPE_USER_CREATABLE }, + { NULL }) + +static void s390_pv_guest_class_init(ObjectClass *oc, void *data) +{ +} + +static void s390_pv_guest_init(Object *obj) +{ +} + +static void s390_pv_guest_finalize(Object *obj) +{ +} diff --git a/include/hw/s390x/pv.h b/target/s390x/kvm/pv.h similarity index 58% rename from include/hw/s390x/pv.h rename to target/s390x/kvm/pv.h index 1f1f545bfc..5877d28ff1 100644 --- a/include/hw/s390x/pv.h +++ b/target/s390x/kvm/pv.h @@ -14,10 +14,10 @@ #include "qapi/error.h" #include "sysemu/kvm.h" +#include "hw/s390x/s390-virtio-ccw.h" #ifdef CONFIG_KVM #include "cpu.h" -#include "hw/s390x/s390-virtio-ccw.h" static inline bool s390_is_pv(void) { @@ -38,24 +38,46 @@ static inline bool s390_is_pv(void) return ccw->pv; } +int s390_pv_query_info(void); int s390_pv_vm_enable(void); void s390_pv_vm_disable(void); -int s390_pv_set_sec_parms(uint64_t origin, uint64_t length); +bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms); +int s390_pv_set_sec_parms(uint64_t origin, uint64_t length, Error **errp); int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak); void s390_pv_prep_reset(void); int s390_pv_verify(void); void s390_pv_unshare(void); void s390_pv_inject_reset_error(CPUState *cs); +uint64_t kvm_s390_pv_dmp_get_size_cpu(void); +uint64_t kvm_s390_pv_dmp_get_size_mem_state(void); +uint64_t kvm_s390_pv_dmp_get_size_completion_data(void); +bool kvm_s390_pv_info_basic_valid(void); +int kvm_s390_dump_init(void); +int kvm_s390_dump_cpu(S390CPU *cpu, void *buff); +int kvm_s390_dump_mem_state(uint64_t addr, size_t len, void *dest); +int kvm_s390_dump_completion_data(void *buff); #else /* CONFIG_KVM */ static inline bool s390_is_pv(void) { return false; } +static inline int s390_pv_query_info(void) { return 0; } static inline int s390_pv_vm_enable(void) { return 0; } static inline void s390_pv_vm_disable(void) {} -static inline int s390_pv_set_sec_parms(uint64_t origin, uint64_t length) { return 0; } +static inline bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms) { return false; } +static inline int s390_pv_set_sec_parms(uint64_t origin, uint64_t length, + Error **errp) { return 0; } static inline int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) { return 0; } static inline void s390_pv_prep_reset(void) {} static inline int s390_pv_verify(void) { return 0; } static inline void s390_pv_unshare(void) {} static inline void s390_pv_inject_reset_error(CPUState *cs) {}; +static inline uint64_t kvm_s390_pv_dmp_get_size_cpu(void) { return 0; } +static inline uint64_t kvm_s390_pv_dmp_get_size_mem_state(void) { return 0; } +static inline uint64_t kvm_s390_pv_dmp_get_size_completion_data(void) { return 0; } +static inline bool kvm_s390_pv_info_basic_valid(void) { return false; } +static inline int kvm_s390_dump_init(void) { return 0; } +static inline int kvm_s390_dump_cpu(S390CPU *cpu, void *buff) { return 0; } +static inline int kvm_s390_dump_mem_state(uint64_t addr, size_t len, + void *dest) { return 0; } +static inline int kvm_s390_dump_completion_data(void *buff) { return 0; } #endif /* CONFIG_KVM */ int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); diff --git a/target/s390x/kvm/stsi-topology.c b/target/s390x/kvm/stsi-topology.c new file mode 100644 index 0000000000..c8d6389cd8 --- /dev/null +++ b/target/s390x/kvm/stsi-topology.c @@ -0,0 +1,336 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU S390x CPU Topology + * + * Copyright IBM Corp. 2022, 2023 + * Author(s): Pierre Morel + * + */ +#include "qemu/osdep.h" +#include "cpu.h" +#include "hw/s390x/sclp.h" +#include "hw/s390x/cpu-topology.h" + +QEMU_BUILD_BUG_ON(S390_CPU_ENTITLEMENT_LOW != 1); +QEMU_BUILD_BUG_ON(S390_CPU_ENTITLEMENT_MEDIUM != 2); +QEMU_BUILD_BUG_ON(S390_CPU_ENTITLEMENT_HIGH != 3); + +/** + * fill_container: + * @p: The address of the container TLE to fill + * @level: The level of nesting for this container + * @id: The container receives a unique ID inside its own container + * + * Returns the next free TLE entry. + */ +static char *fill_container(char *p, int level, int id) +{ + SYSIBContainerListEntry *tle = (SYSIBContainerListEntry *)p; + + tle->nl = level; + tle->id = id; + return p + sizeof(*tle); +} + +/** + * fill_tle_cpu: + * @p: The address of the CPU TLE to fill + * @entry: a pointer to the S390TopologyEntry defining this + * CPU container. + * + * Returns the next free TLE entry. + */ +static char *fill_tle_cpu(char *p, S390TopologyEntry *entry) +{ + SysIBCPUListEntry *tle = (SysIBCPUListEntry *)p; + S390TopologyId topology_id = entry->id; + + tle->nl = 0; + tle->flags = 0; + if (topology_id.vertical) { + tle->flags |= topology_id.entitlement; + } + if (topology_id.dedicated) { + tle->flags |= SYSIB_TLE_DEDICATED; + } + tle->type = topology_id.type; + tle->origin = cpu_to_be16(topology_id.origin * 64); + tle->mask = cpu_to_be64(entry->mask); + return p + sizeof(*tle); +} + +/* + * Macro to check that the size of data after increment + * will not get bigger than the size of the SysIB. + */ +#define SYSIB_GUARD(data, x) do { \ + data += x; \ + if (data > sizeof(SysIB)) { \ + return 0; \ + } \ + } while (0) + +/** + * stsi_topology_fill_sysib: + * @p: A pointer to the position of the first TLE + * @level: The nested level wanted by the guest + * + * Fill the SYSIB with the topology information as described in + * the PoP, nesting containers as appropriate, with the maximum + * nesting limited by @level. + * + * Return value: + * On success: the size of the SysIB_15x after being filled with TLE. + * On error: 0 in the case we would overrun the end of the SysIB. + */ +static int stsi_topology_fill_sysib(S390TopologyList *topology_list, + char *p, int level) +{ + S390TopologyEntry *entry; + int last_drawer = -1; + int last_book = -1; + int last_socket = -1; + int drawer_id = 0; + int book_id = 0; + int socket_id = 0; + int n = sizeof(SysIB_151x); + + QTAILQ_FOREACH(entry, topology_list, next) { + bool drawer_change = last_drawer != entry->id.drawer; + bool book_change = drawer_change || last_book != entry->id.book; + bool socket_change = book_change || last_socket != entry->id.socket; + + if (level > 3 && drawer_change) { + SYSIB_GUARD(n, sizeof(SYSIBContainerListEntry)); + p = fill_container(p, 3, drawer_id++); + book_id = 0; + } + if (level > 2 && book_change) { + SYSIB_GUARD(n, sizeof(SYSIBContainerListEntry)); + p = fill_container(p, 2, book_id++); + socket_id = 0; + } + if (socket_change) { + SYSIB_GUARD(n, sizeof(SYSIBContainerListEntry)); + p = fill_container(p, 1, socket_id++); + } + + SYSIB_GUARD(n, sizeof(SysIBCPUListEntry)); + p = fill_tle_cpu(p, entry); + last_drawer = entry->id.drawer; + last_book = entry->id.book; + last_socket = entry->id.socket; + } + + return n; +} + +/** + * setup_stsi: + * @topology_list: ordered list of groups of CPUs with same properties + * @sysib: pointer to a SysIB to be filled with SysIB_151x data + * @level: Nested level specified by the guest + * + * Setup the SYSIB for STSI 15.1, the header as well as the description + * of the topology. + */ +static int setup_stsi(S390TopologyList *topology_list, SysIB_151x *sysib, + int level) +{ + sysib->mnest = level; + switch (level) { + case 4: + sysib->mag[S390_TOPOLOGY_MAG4] = current_machine->smp.drawers; + sysib->mag[S390_TOPOLOGY_MAG3] = current_machine->smp.books; + sysib->mag[S390_TOPOLOGY_MAG2] = current_machine->smp.sockets; + sysib->mag[S390_TOPOLOGY_MAG1] = current_machine->smp.cores; + break; + case 3: + sysib->mag[S390_TOPOLOGY_MAG3] = current_machine->smp.drawers * + current_machine->smp.books; + sysib->mag[S390_TOPOLOGY_MAG2] = current_machine->smp.sockets; + sysib->mag[S390_TOPOLOGY_MAG1] = current_machine->smp.cores; + break; + case 2: + sysib->mag[S390_TOPOLOGY_MAG2] = current_machine->smp.drawers * + current_machine->smp.books * + current_machine->smp.sockets; + sysib->mag[S390_TOPOLOGY_MAG1] = current_machine->smp.cores; + break; + } + + return stsi_topology_fill_sysib(topology_list, sysib->tle, level); +} + +/** + * s390_topology_add_cpu_to_entry: + * @entry: Topology entry to setup + * @cpu: the S390CPU to add + * + * Set the core bit inside the topology mask. + */ +static void s390_topology_add_cpu_to_entry(S390TopologyEntry *entry, + S390CPU *cpu) +{ + set_bit(63 - (cpu->env.core_id % 64), &entry->mask); +} + +/** + * s390_topology_from_cpu: + * @cpu: S390CPU to calculate the topology id + * + * Initialize the topology id from the CPU environment. + */ +static S390TopologyId s390_topology_from_cpu(S390CPU *cpu) +{ + S390TopologyId topology_id = { + .drawer = cpu->env.drawer_id, + .book = cpu->env.book_id, + .socket = cpu->env.socket_id, + .type = S390_TOPOLOGY_CPU_IFL, + .vertical = s390_topology.polarization == S390_CPU_POLARIZATION_VERTICAL, + .entitlement = cpu->env.entitlement, + .dedicated = cpu->env.dedicated, + .origin = cpu->env.core_id / 64, + }; + + return topology_id; +} + +/** + * s390_topology_id_cmp: + * @l: first S390TopologyId + * @r: second S390TopologyId + * + * Compare two topology ids according to the sorting order specified by the PoP. + * + * Returns a negative number if the first id is less than, 0 if it is equal to + * and positive if it is larger than the second id. + */ +static int s390_topology_id_cmp(const S390TopologyId *l, + const S390TopologyId *r) +{ + int l_polarization = l->vertical ? l->entitlement : 0; + int r_polarization = r->vertical ? r->entitlement : 0; + + /* + * lexical order, compare less significant values only if more significant + * ones are equal + */ + return l->sentinel - r->sentinel ?: + l->drawer - r->drawer ?: + l->book - r->book ?: + l->socket - r->socket ?: + l->type - r->type ?: + /* logic is inverted for the next two */ + r_polarization - l_polarization ?: + r->dedicated - l->dedicated ?: + l->origin - r->origin; +} + +static bool s390_topology_id_eq(const S390TopologyId *l, + const S390TopologyId *r) +{ + return !s390_topology_id_cmp(l, r); +} + +static bool s390_topology_id_lt(const S390TopologyId *l, + const S390TopologyId *r) +{ + return s390_topology_id_cmp(l, r) < 0; +} + +/** + * s390_topology_fill_list_sorted: + * @topology_list: list to fill + * + * Create S390TopologyEntrys as appropriate from all CPUs and fill the + * topology_list with the entries according to the order specified by the PoP. + */ +static void s390_topology_fill_list_sorted(S390TopologyList *topology_list) +{ + CPUState *cs; + S390TopologyEntry sentinel = { .id.sentinel = 1 }; + + QTAILQ_INIT(topology_list); + + QTAILQ_INSERT_HEAD(topology_list, &sentinel, next); + + CPU_FOREACH(cs) { + S390TopologyId id = s390_topology_from_cpu(S390_CPU(cs)); + S390TopologyEntry *entry = NULL, *tmp; + + QTAILQ_FOREACH(tmp, topology_list, next) { + if (s390_topology_id_eq(&id, &tmp->id)) { + entry = tmp; + break; + } else if (s390_topology_id_lt(&id, &tmp->id)) { + entry = g_malloc0(sizeof(*entry)); + entry->id = id; + QTAILQ_INSERT_BEFORE(tmp, entry, next); + break; + } + } + assert(entry); + s390_topology_add_cpu_to_entry(entry, S390_CPU(cs)); + } + + QTAILQ_REMOVE(topology_list, &sentinel, next); +} + +/** + * s390_topology_empty_list: + * + * Clear all entries in the S390Topology list. + */ +static void s390_topology_empty_list(S390TopologyList *topology_list) +{ + S390TopologyEntry *entry = NULL; + S390TopologyEntry *tmp = NULL; + + QTAILQ_FOREACH_SAFE(entry, topology_list, next, tmp) { + QTAILQ_REMOVE(topology_list, entry, next); + g_free(entry); + } +} + +/** + * insert_stsi_15_1_x: + * @cpu: the CPU doing the call for which we set CC + * @sel2: the selector 2, containing the nested level + * @addr: Guest logical address of the guest SysIB + * @ar: the access register number + * @ra: the return address + * + * Emulate STSI 15.1.x, that is, perform all necessary checks and + * fill the SYSIB. + * In case the topology description is too long to fit into the SYSIB, + * set CC=3 and abort without writing the SYSIB. + */ +void insert_stsi_15_1_x(S390CPU *cpu, int sel2, uint64_t addr, uint8_t ar, uintptr_t ra) +{ + S390TopologyList topology_list; + SysIB sysib = {0}; + int length; + + if (!s390_has_topology() || sel2 < 2 || sel2 > SCLP_READ_SCP_INFO_MNEST) { + setcc(cpu, 3); + return; + } + + s390_topology_fill_list_sorted(&topology_list); + length = setup_stsi(&topology_list, &sysib.sysib_151x, sel2); + s390_topology_empty_list(&topology_list); + + if (!length) { + setcc(cpu, 3); + return; + } + + sysib.sysib_151x.length = cpu_to_be16(length); + if (!s390_cpu_virt_mem_write(cpu, addr, ar, &sysib, length)) { + setcc(cpu, 0); + } else { + s390_cpu_virt_mem_handle_exc(cpu, ra); + } +} diff --git a/target/s390x/kvm/stubs.c b/target/s390x/kvm/stubs.c new file mode 100644 index 0000000000..5fd63b9a7e --- /dev/null +++ b/target/s390x/kvm/stubs.c @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "kvm_s390x.h" + +int kvm_s390_get_protected_dump(void) +{ + return false; +} diff --git a/target/s390x/kvm/trace-events b/target/s390x/kvm/trace-events index 5289f5f675..cdf2c4f8f2 100644 --- a/target/s390x/kvm/trace-events +++ b/target/s390x/kvm/trace-events @@ -1,7 +1,14 @@ -# See docs/devel/tracing.txt for syntax documentation. +# See docs/devel/tracing.rst for syntax documentation. # kvm.c kvm_enable_cmma(int rc) "CMMA: enabling with result code %d" kvm_clear_cmma(int rc) "CMMA: clearing with result code %d" kvm_failed_cpu_state_set(int cpu_index, uint8_t state, const char *msg) "Warning: Unable to set cpu %d state %" PRIu8 " to KVM: %s" kvm_assign_subch_ioeventfd(int fd, uint32_t addr, bool assign, int datamatch) "fd: %d sch: @0x%x assign: %d vq: %d" + +kvm_sw_breakpoint(uint32_t n) "KVM: will use %d-byte sw breakpoints" +kvm_insn_unhandled_priv(uint32_t x) "KVM: unhandled PRIV: 0x%x" +kvm_insn_diag(uint32_t x) "KVM: unknown DIAG: 0x%x" +kvm_insn(uint32_t ipa, uint32_t ipb) "handle_instruction 0x%x 0x%x" +kvm_intercept(uint32_t icpt_code, uint64_t psw_addr) "intercept: 0x%x (at 0x%"PRIx64"lx)" +kvm_msi_route_fixup(const char* msg) "%s" diff --git a/target/s390x/machine.c b/target/s390x/machine.c index 37a076858c..a125ebcc2f 100644 --- a/target/s390x/machine.c +++ b/target/s390x/machine.c @@ -66,7 +66,7 @@ static const VMStateDescription vmstate_fpu = { .version_id = 1, .minimum_version_id = 1, .needed = fpu_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.vregs[0][0], S390CPU), VMSTATE_UINT64(env.vregs[1][0], S390CPU), VMSTATE_UINT64(env.vregs[2][0], S390CPU), @@ -98,7 +98,7 @@ static const VMStateDescription vmstate_vregs = { .version_id = 1, .minimum_version_id = 1, .needed = vregs_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { /* vregs[0][0] -> vregs[15][0] and fregs are overlays */ VMSTATE_UINT64(env.vregs[16][0], S390CPU), VMSTATE_UINT64(env.vregs[17][0], S390CPU), @@ -157,12 +157,12 @@ static bool riccb_needed(void *opaque) return s390_has_feat(S390_FEAT_RUNTIME_INSTRUMENTATION); } -const VMStateDescription vmstate_riccb = { +static const VMStateDescription vmstate_riccb = { .name = "cpu/riccb", .version_id = 1, .minimum_version_id = 1, .needed = riccb_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(env.riccb, S390CPU, 64), VMSTATE_END_OF_LIST() } @@ -174,12 +174,12 @@ static bool exval_needed(void *opaque) return cpu->env.ex_value != 0; } -const VMStateDescription vmstate_exval = { +static const VMStateDescription vmstate_exval = { .name = "cpu/exval", .version_id = 1, .minimum_version_id = 1, .needed = exval_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.ex_value, S390CPU), VMSTATE_END_OF_LIST() } @@ -190,12 +190,12 @@ static bool gscb_needed(void *opaque) return s390_has_feat(S390_FEAT_GUARDED_STORAGE); } -const VMStateDescription vmstate_gscb = { +static const VMStateDescription vmstate_gscb = { .name = "cpu/gscb", .version_id = 1, .minimum_version_id = 1, .needed = gscb_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.gscb, S390CPU, 4), VMSTATE_END_OF_LIST() } @@ -206,12 +206,12 @@ static bool bpbc_needed(void *opaque) return s390_has_feat(S390_FEAT_BPB); } -const VMStateDescription vmstate_bpbc = { +static const VMStateDescription vmstate_bpbc = { .name = "cpu/bpbc", .version_id = 1, .minimum_version_id = 1, .needed = bpbc_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(env.bpbc, S390CPU), VMSTATE_END_OF_LIST() } @@ -222,12 +222,12 @@ static bool etoken_needed(void *opaque) return s390_has_feat(S390_FEAT_ETOKEN); } -const VMStateDescription vmstate_etoken = { +static const VMStateDescription vmstate_etoken = { .name = "cpu/etoken", .version_id = 1, .minimum_version_id = 1, .needed = etoken_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.etoken, S390CPU), VMSTATE_UINT64(env.etoken_extension, S390CPU), VMSTATE_END_OF_LIST() @@ -239,12 +239,12 @@ static bool diag318_needed(void *opaque) return s390_has_feat(S390_FEAT_DIAG_318); } -const VMStateDescription vmstate_diag318 = { +static const VMStateDescription vmstate_diag318 = { .name = "cpu/diag318", .version_id = 1, .minimum_version_id = 1, .needed = diag318_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.diag318_info, S390CPU), VMSTATE_END_OF_LIST() } @@ -256,7 +256,7 @@ const VMStateDescription vmstate_s390_cpu = { .pre_save = cpu_pre_save, .version_id = 4, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.regs, S390CPU, 16), VMSTATE_UINT64(env.psw.mask, S390CPU), VMSTATE_UINT64(env.psw.addr, S390CPU), @@ -278,7 +278,7 @@ const VMStateDescription vmstate_s390_cpu = { irqstate_saved_size), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_fpu, &vmstate_vregs, &vmstate_riccb, diff --git a/target/s390x/meson.build b/target/s390x/meson.build index 84c1402a6a..02ca43d9f0 100644 --- a/target/s390x/meson.build +++ b/target/s390x/meson.build @@ -18,8 +18,8 @@ gen_features_h = custom_target('gen-features.h', s390x_ss.add(gen_features_h) -s390x_softmmu_ss = ss.source_set() -s390x_softmmu_ss.add(files( +s390x_system_ss = ss.source_set() +s390x_system_ss.add(files( 'helper.c', 'arch_dump.c', 'diag.c', @@ -40,5 +40,5 @@ subdir('tcg') subdir('kvm') target_arch += {'s390x': s390x_ss} -target_softmmu_arch += {'s390x': s390x_softmmu_ss} +target_system_arch += {'s390x': s390x_system_ss} target_user_arch += {'s390x': s390x_user_ss} diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index b04b57c235..fbb2f1b4d4 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -417,7 +417,7 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, vaddr &= TARGET_PAGE_MASK; - if (!(env->psw.mask & PSW_MASK_DAT)) { + if (rw != MMU_S390_LRA && !(env->psw.mask & PSW_MASK_DAT)) { *raddr = vaddr; goto nodat; } diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 6aba7fd0ca..825252d728 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -11,6 +11,7 @@ #define S390X_INTERNAL_H #include "cpu.h" +#include "fpu/softfloat.h" #ifndef CONFIG_USER_ONLY typedef struct LowCore { @@ -227,7 +228,7 @@ static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb, /* arch_dump.c */ int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, - int cpuid, void *opaque); + int cpuid, DumpState *s); /* cc_helper.c */ @@ -299,7 +300,7 @@ uint32_t set_cc_nz_f128(float128 v); uint8_t s390_softfloat_exc_to_ieee(unsigned int exc); int s390_swap_bfp_rounding_mode(CPUS390XState *env, int m3); void s390_restore_bfp_rounding_mode(CPUS390XState *env, int old_mode); -int float_comp_to_cc(CPUS390XState *env, int float_compare); +int float_comp_to_cc(CPUS390XState *env, FloatRelation float_compare); #define DCMASK_ZERO 0x0c00 #define DCMASK_NORMAL 0x0300 @@ -398,7 +399,9 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, /* translate.c */ void s390x_translate_init(void); - +void s390x_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data); /* sigp.c */ int handle_sigp(CPUS390XState *env, uint8_t order, uint64_t r1, uint64_t r3); diff --git a/target/s390x/tcg/cc_helper.c b/target/s390x/tcg/cc_helper.c index b2e8d3d9f5..b36f8cdc8b 100644 --- a/target/s390x/tcg/cc_helper.c +++ b/target/s390x/tcg/cc_helper.c @@ -487,6 +487,10 @@ void HELPER(sacf)(CPUS390XState *env, uint64_t a1) { HELPER_LOG("%s: %16" PRIx64 "\n", __func__, a1); + if (!(env->psw.mask & PSW_MASK_DAT)) { + tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, GETPC()); + } + switch (a1 & 0xf00) { case 0x000: env->psw.mask &= ~PSW_MASK_ASC; @@ -497,6 +501,9 @@ void HELPER(sacf)(CPUS390XState *env, uint64_t a1) env->psw.mask |= PSW_ASC_SECONDARY; break; case 0x300: + if ((env->psw.mask & PSW_MASK_PSTATE) != 0) { + tcg_s390_program_interrupt(env, PGM_PRIVILEGED, GETPC()); + } env->psw.mask &= ~PSW_MASK_ASC; env->psw.mask |= PSW_ASC_HOME; break; diff --git a/target/s390x/tcg/crypto_helper.c b/target/s390x/tcg/crypto_helper.c index 138d9e7ad9..93aabd236f 100644 --- a/target/s390x/tcg/crypto_helper.c +++ b/target/s390x/tcg/crypto_helper.c @@ -1,23 +1,274 @@ /* * s390x crypto helpers * + * Copyright (C) 2022 Jason A. Donenfeld . All Rights Reserved. * Copyright (c) 2017 Red Hat Inc * * Authors: * David Hildenbrand + * Jason A. Donenfeld * * 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 "qemu/guest-random.h" #include "s390x-internal.h" #include "tcg_s390x.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" +static uint64_t R(uint64_t x, int c) +{ + return (x >> c) | (x << (64 - c)); +} +static uint64_t Ch(uint64_t x, uint64_t y, uint64_t z) +{ + return (x & y) ^ (~x & z); +} +static uint64_t Maj(uint64_t x, uint64_t y, uint64_t z) +{ + return (x & y) ^ (x & z) ^ (y & z); +} +static uint64_t Sigma0(uint64_t x) +{ + return R(x, 28) ^ R(x, 34) ^ R(x, 39); +} +static uint64_t Sigma1(uint64_t x) +{ + return R(x, 14) ^ R(x, 18) ^ R(x, 41); +} +static uint64_t sigma0(uint64_t x) +{ + return R(x, 1) ^ R(x, 8) ^ (x >> 7); +} +static uint64_t sigma1(uint64_t x) +{ + return R(x, 19) ^ R(x, 61) ^ (x >> 6); +} + +static const uint64_t K[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, + 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, + 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, + 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, + 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, + 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, + 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, + 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, + 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, + 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, + 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, + 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, + 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, + 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +/* a is icv/ocv, w is a single message block. w will get reused internally. */ +static void sha512_bda(uint64_t a[8], uint64_t w[16]) +{ + uint64_t t, z[8], b[8]; + int i, j; + + memcpy(z, a, sizeof(z)); + for (i = 0; i < 80; i++) { + memcpy(b, a, sizeof(b)); + + t = a[7] + Sigma1(a[4]) + Ch(a[4], a[5], a[6]) + K[i] + w[i % 16]; + b[7] = t + Sigma0(a[0]) + Maj(a[0], a[1], a[2]); + b[3] += t; + for (j = 0; j < 8; ++j) { + a[(j + 1) % 8] = b[j]; + } + if (i % 16 == 15) { + for (j = 0; j < 16; ++j) { + w[j] += w[(j + 9) % 16] + sigma0(w[(j + 1) % 16]) + + sigma1(w[(j + 14) % 16]); + } + } + } + + for (i = 0; i < 8; i++) { + a[i] += z[i]; + } +} + +/* a is icv/ocv, w is a single message block that needs be64 conversion. */ +static void sha512_bda_be64(uint64_t a[8], uint64_t w[16]) +{ + uint64_t t[16]; + int i; + + for (i = 0; i < 16; i++) { + t[i] = be64_to_cpu(w[i]); + } + sha512_bda(a, t); +} + +static void sha512_read_icv(CPUS390XState *env, uint64_t addr, + uint64_t a[8], uintptr_t ra) +{ + int i; + + for (i = 0; i < 8; i++, addr += 8) { + addr = wrap_address(env, addr); + a[i] = cpu_ldq_be_data_ra(env, addr, ra); + } +} + +static void sha512_write_ocv(CPUS390XState *env, uint64_t addr, + uint64_t a[8], uintptr_t ra) +{ + int i; + + for (i = 0; i < 8; i++, addr += 8) { + addr = wrap_address(env, addr); + cpu_stq_be_data_ra(env, addr, a[i], ra); + } +} + +static void sha512_read_block(CPUS390XState *env, uint64_t addr, + uint64_t a[16], uintptr_t ra) +{ + int i; + + for (i = 0; i < 16; i++, addr += 8) { + addr = wrap_address(env, addr); + a[i] = cpu_ldq_be_data_ra(env, addr, ra); + } +} + +static void sha512_read_mbl_be64(CPUS390XState *env, uint64_t addr, + uint8_t a[16], uintptr_t ra) +{ + int i; + + for (i = 0; i < 16; i++, addr += 1) { + addr = wrap_address(env, addr); + a[i] = cpu_ldub_data_ra(env, addr, ra); + } +} + +static int cpacf_sha512(CPUS390XState *env, uintptr_t ra, uint64_t param_addr, + uint64_t *message_reg, uint64_t *len_reg, uint32_t type) +{ + enum { MAX_BLOCKS_PER_RUN = 64 }; /* Arbitrary: keep interactivity. */ + uint64_t len = *len_reg, a[8], processed = 0; + int i, message_reg_len = 64; + + g_assert(type == S390_FEAT_TYPE_KIMD || type == S390_FEAT_TYPE_KLMD); + + if (!(env->psw.mask & PSW_MASK_64)) { + len = (uint32_t)len; + message_reg_len = (env->psw.mask & PSW_MASK_32) ? 32 : 24; + } + + /* KIMD: length has to be properly aligned. */ + if (type == S390_FEAT_TYPE_KIMD && !QEMU_IS_ALIGNED(len, 128)) { + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); + } + + sha512_read_icv(env, param_addr, a, ra); + + /* Process full blocks first. */ + for (; len >= 128; len -= 128, processed += 128) { + uint64_t w[16]; + + if (processed >= MAX_BLOCKS_PER_RUN * 128) { + break; + } + + sha512_read_block(env, *message_reg + processed, w, ra); + sha512_bda(a, w); + } + + /* KLMD: Process partial/empty block last. */ + if (type == S390_FEAT_TYPE_KLMD && len < 128) { + uint8_t x[128]; + + /* Read the remainder of the message byte-per-byte. */ + for (i = 0; i < len; i++) { + uint64_t addr = wrap_address(env, *message_reg + processed + i); + + x[i] = cpu_ldub_data_ra(env, addr, ra); + } + /* Pad the remainder with zero and set the top bit. */ + memset(x + len, 0, 128 - len); + x[len] = 128; + + /* + * Place the MBL either into this block (if there is space left), + * or use an additional one. + */ + if (len < 112) { + sha512_read_mbl_be64(env, param_addr + 64, x + 112, ra); + } + sha512_bda_be64(a, (uint64_t *)x); + + if (len >= 112) { + memset(x, 0, 112); + sha512_read_mbl_be64(env, param_addr + 64, x + 112, ra); + sha512_bda_be64(a, (uint64_t *)x); + } + + processed += len; + len = 0; + } + + /* + * Modify memory after we read all inputs and modify registers only after + * writing memory succeeded. + * + * TODO: if writing fails halfway through (e.g., when crossing page + * boundaries), we're in trouble. We'd need something like access_prepare(). + */ + sha512_write_ocv(env, param_addr, a, ra); + *message_reg = deposit64(*message_reg, 0, message_reg_len, + *message_reg + processed); + *len_reg -= processed; + return !len ? 0 : 3; +} + +static void fill_buf_random(CPUS390XState *env, uintptr_t ra, + uint64_t *buf_reg, uint64_t *len_reg) +{ + uint8_t tmp[256]; + uint64_t len = *len_reg; + int buf_reg_len = 64; + + if (!(env->psw.mask & PSW_MASK_64)) { + len = (uint32_t)len; + buf_reg_len = (env->psw.mask & PSW_MASK_32) ? 32 : 24; + } + + while (len) { + size_t block = MIN(len, sizeof(tmp)); + + qemu_guest_getrandom_nofail(tmp, block); + for (size_t i = 0; i < block; ++i) { + cpu_stb_data_ra(env, wrap_address(env, *buf_reg), tmp[i], ra); + *buf_reg = deposit64(*buf_reg, 0, buf_reg_len, *buf_reg + 1); + --*len_reg; + } + len -= block; + } +} + uint32_t HELPER(msa)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t r3, uint32_t type) { @@ -52,6 +303,13 @@ uint32_t HELPER(msa)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t r3, cpu_stb_data_ra(env, param_addr, subfunc[i], ra); } break; + case 3: /* CPACF_*_SHA_512 */ + return cpacf_sha512(env, ra, env->regs[1], &env->regs[r2], + &env->regs[r2 + 1], type); + case 114: /* CPACF_PRNO_TRNG */ + fill_buf_random(env, ra, &env->regs[r1], &env->regs[r1 + 1]); + fill_buf_random(env, ra, &env->regs[r2], &env->regs[r2 + 1]); + break; default: /* we don't implement any other subfunction yet */ g_assert_not_reached(); diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index 29ccf70df1..f1c33f7967 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -21,15 +21,14 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "s390x-internal.h" #include "exec/helper-proto.h" -#include "qemu/timer.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "hw/s390x/ioinst.h" -#include "exec/address-spaces.h" +#include "s390x-internal.h" #include "tcg_s390x.h" #ifndef CONFIG_USER_ONLY +#include "qemu/timer.h" +#include "exec/address-spaces.h" +#include "hw/s390x/ioinst.h" #include "hw/s390x/s390_flic.h" #include "hw/boards.h" #endif @@ -39,7 +38,7 @@ G_NORETURN void tcg_s390_program_interrupt(CPUS390XState *env, { CPUState *cs = env_cpu(env); - cpu_restore_state(cs, ra, true); + cpu_restore_state(cs, ra); qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n", env->psw.addr); trigger_pgm_exception(env, code); @@ -85,16 +84,13 @@ void HELPER(data_exception)(CPUS390XState *env, uint32_t dxc) /* * Unaligned accesses are only diagnosed with MO_ALIGN. At the moment, - * this is only for the atomic operations, for which we want to raise a - * specification exception. + * this is only for the atomic and relative long operations, for which we want + * to raise a specification exception. */ static G_NORETURN void do_unaligned_access(CPUState *cs, uintptr_t retaddr) { - S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; - - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, retaddr); + tcg_s390_program_interrupt(cpu_env(cs), PGM_SPECIFICATION, retaddr); } #if defined(CONFIG_USER_ONLY) @@ -147,8 +143,7 @@ bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; + CPUS390XState *env = cpu_env(cs); target_ulong vaddr, raddr; uint64_t asc, tec; int prot, excp; @@ -190,11 +185,6 @@ bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size, return false; } - if (excp != PGM_ADDRESSING) { - stq_phys(env_cpu(env)->as, - env->psa + offsetof(LowCore, trans_exc_code), tec); - } - /* * For data accesses, ILEN will be filled in from the unwind info, * within cpu_loop_exit_restore. For code accesses, retaddr == 0, @@ -211,19 +201,33 @@ static void do_program_interrupt(CPUS390XState *env) uint64_t mask, addr; LowCore *lowcore; int ilen = env->int_pgm_ilen; + bool set_trans_exc_code = false; + bool advance = false; - assert(ilen == 2 || ilen == 4 || ilen == 6); + assert((env->int_pgm_code == PGM_SPECIFICATION && ilen == 0) || + ilen == 2 || ilen == 4 || ilen == 6); switch (env->int_pgm_code) { case PGM_PER: - if (env->per_perc_atmid & PER_CODE_EVENT_NULLIFICATION) { - break; - } - /* FALL THROUGH */ + advance = !(env->per_perc_atmid & PER_CODE_EVENT_NULLIFICATION); + break; + case PGM_ASCE_TYPE: + case PGM_REG_FIRST_TRANS: + case PGM_REG_SEC_TRANS: + case PGM_REG_THIRD_TRANS: + case PGM_SEGMENT_TRANS: + case PGM_PAGE_TRANS: + assert(env->int_pgm_code == env->tlb_fill_exc); + set_trans_exc_code = true; + break; + case PGM_PROTECTION: + assert(env->int_pgm_code == env->tlb_fill_exc); + set_trans_exc_code = true; + advance = true; + break; case PGM_OPERATION: case PGM_PRIVILEGED: case PGM_EXECUTE: - case PGM_PROTECTION: case PGM_ADDRESSING: case PGM_SPECIFICATION: case PGM_DATA: @@ -242,11 +246,15 @@ static void do_program_interrupt(CPUS390XState *env) case PGM_PC_TRANS_SPEC: case PGM_ALET_SPEC: case PGM_MONITOR: - /* advance the PSW if our exception is not nullifying */ - env->psw.addr += ilen; + advance = true; break; } + /* advance the PSW if our exception is not nullifying */ + if (advance) { + env->psw.addr += ilen; + } + qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d psw: %" PRIx64 " %" PRIx64 "\n", __func__, env->int_pgm_code, ilen, env->psw.mask, @@ -262,6 +270,10 @@ static void do_program_interrupt(CPUS390XState *env) env->per_perc_atmid = 0; } + if (set_trans_exc_code) { + lowcore->trans_exc_code = cpu_to_be64(env->tlb_fill_tec); + } + lowcore->pgm_ilen = cpu_to_be16(ilen); lowcore->pgm_code = cpu_to_be16(env->int_pgm_code); lowcore->program_old_psw.mask = cpu_to_be64(s390_cpu_get_psw_mask(env)); @@ -553,7 +565,7 @@ try_deliver: /* don't trigger a cpu_loop_exit(), use an interrupt instead */ cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HALT); } else if (cs->halted) { - /* unhalt if we had a WAIT PSW somehwere in our injection chain */ + /* unhalt if we had a WAIT PSW somewhere in our injection chain */ s390_cpu_unhalt(cpu); } } @@ -584,8 +596,7 @@ bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request) void s390x_cpu_debug_excp_handler(CPUState *cs) { - S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; + CPUS390XState *env = cpu_env(cs); CPUWatchpoint *wp_hit = cs->watchpoint_hit; if (wp_hit && wp_hit->flags & BP_CPU) { @@ -638,7 +649,7 @@ void monitor_event(CPUS390XState *env, void HELPER(monitor_call)(CPUS390XState *env, uint64_t monitor_code, uint32_t monitor_class) { - g_assert(monitor_class <= 0xff); + g_assert(monitor_class <= 0xf); if (env->cregs[8] & (0x8000 >> monitor_class)) { monitor_event(env, monitor_code, monitor_class, GETPC()); diff --git a/target/s390x/tcg/fpu_helper.c b/target/s390x/tcg/fpu_helper.c index 4067205405..d8bd5748fa 100644 --- a/target/s390x/tcg/fpu_helper.c +++ b/target/s390x/tcg/fpu_helper.c @@ -23,7 +23,6 @@ #include "s390x-internal.h" #include "tcg_s390x.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" @@ -34,7 +33,15 @@ #define HELPER_LOG(x...) #endif -#define RET128(F) (env->retxl = F.low, F.high) +static inline Int128 RET128(float128 f) +{ + return int128_make128(f.low, f.high); +} + +static inline float128 ARG128(Int128 i) +{ + return make_float128(int128_gethi(i), int128_getlo(i)); +} uint8_t s390_softfloat_exc_to_ieee(unsigned int exc) { @@ -44,7 +51,8 @@ uint8_t s390_softfloat_exc_to_ieee(unsigned int exc) s390_exc |= (exc & float_flag_divbyzero) ? S390_IEEE_MASK_DIVBYZERO : 0; s390_exc |= (exc & float_flag_overflow) ? S390_IEEE_MASK_OVERFLOW : 0; s390_exc |= (exc & float_flag_underflow) ? S390_IEEE_MASK_UNDERFLOW : 0; - s390_exc |= (exc & float_flag_inexact) ? S390_IEEE_MASK_INEXACT : 0; + s390_exc |= (exc & (float_flag_inexact | float_flag_invalid_cvti)) ? + S390_IEEE_MASK_INEXACT : 0; return s390_exc; } @@ -78,7 +86,7 @@ static void handle_exceptions(CPUS390XState *env, bool XxC, uintptr_t retaddr) /* * FIXME: - * 1. Right now, all inexact conditions are inidicated as + * 1. Right now, all inexact conditions are indicated as * "truncated" (0) and never as "incremented" (1) in the DXC. * 2. Only traps due to invalid/divbyzero are suppressing. Other traps * are completing, meaning the target register has to be written! @@ -89,7 +97,7 @@ static void handle_exceptions(CPUS390XState *env, bool XxC, uintptr_t retaddr) /* * invalid/divbyzero cannot coexist with other conditions. * overflow/underflow however can coexist with inexact, we have to - * handle it separatly. + * handle it separately. */ if (s390_exc & ~S390_IEEE_MASK_INEXACT) { if (s390_exc & ~S390_IEEE_MASK_INEXACT & env->fpc >> 24) { @@ -224,12 +232,9 @@ uint64_t HELPER(adb)(CPUS390XState *env, uint64_t f1, uint64_t f2) } /* 128-bit FP addition */ -uint64_t HELPER(axb)(CPUS390XState *env, uint64_t ah, uint64_t al, - uint64_t bh, uint64_t bl) +Int128 HELPER(axb)(CPUS390XState *env, Int128 a, Int128 b) { - float128 ret = float128_add(make_float128(ah, al), - make_float128(bh, bl), - &env->fpu_status); + float128 ret = float128_add(ARG128(a), ARG128(b), &env->fpu_status); handle_exceptions(env, false, GETPC()); return RET128(ret); } @@ -251,12 +256,9 @@ uint64_t HELPER(sdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) } /* 128-bit FP subtraction */ -uint64_t HELPER(sxb)(CPUS390XState *env, uint64_t ah, uint64_t al, - uint64_t bh, uint64_t bl) +Int128 HELPER(sxb)(CPUS390XState *env, Int128 a, Int128 b) { - float128 ret = float128_sub(make_float128(ah, al), - make_float128(bh, bl), - &env->fpu_status); + float128 ret = float128_sub(ARG128(a), ARG128(b), &env->fpu_status); handle_exceptions(env, false, GETPC()); return RET128(ret); } @@ -278,12 +280,9 @@ uint64_t HELPER(ddb)(CPUS390XState *env, uint64_t f1, uint64_t f2) } /* 128-bit FP division */ -uint64_t HELPER(dxb)(CPUS390XState *env, uint64_t ah, uint64_t al, - uint64_t bh, uint64_t bl) +Int128 HELPER(dxb)(CPUS390XState *env, Int128 a, Int128 b) { - float128 ret = float128_div(make_float128(ah, al), - make_float128(bh, bl), - &env->fpu_status); + float128 ret = float128_div(ARG128(a), ARG128(b), &env->fpu_status); handle_exceptions(env, false, GETPC()); return RET128(ret); } @@ -307,29 +306,27 @@ uint64_t HELPER(mdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) /* 64/32-bit FP multiplication */ uint64_t HELPER(mdeb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { + float64 f1_64 = float32_to_float64(f1, &env->fpu_status); float64 ret = float32_to_float64(f2, &env->fpu_status); - ret = float64_mul(f1, ret, &env->fpu_status); + ret = float64_mul(f1_64, ret, &env->fpu_status); handle_exceptions(env, false, GETPC()); return ret; } /* 128-bit FP multiplication */ -uint64_t HELPER(mxb)(CPUS390XState *env, uint64_t ah, uint64_t al, - uint64_t bh, uint64_t bl) +Int128 HELPER(mxb)(CPUS390XState *env, Int128 a, Int128 b) { - float128 ret = float128_mul(make_float128(ah, al), - make_float128(bh, bl), - &env->fpu_status); + float128 ret = float128_mul(ARG128(a), ARG128(b), &env->fpu_status); handle_exceptions(env, false, GETPC()); return RET128(ret); } /* 128/64-bit FP multiplication */ -uint64_t HELPER(mxdb)(CPUS390XState *env, uint64_t ah, uint64_t al, - uint64_t f2) +Int128 HELPER(mxdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { + float128 f1_128 = float64_to_float128(f1, &env->fpu_status); float128 ret = float64_to_float128(f2, &env->fpu_status); - ret = float128_mul(make_float128(ah, al), ret, &env->fpu_status); + ret = float128_mul(f1_128, ret, &env->fpu_status); handle_exceptions(env, false, GETPC()); return RET128(ret); } @@ -343,11 +340,10 @@ uint64_t HELPER(ldeb)(CPUS390XState *env, uint64_t f2) } /* convert 128-bit float to 64-bit float */ -uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al, - uint32_t m34) +uint64_t HELPER(ldxb)(CPUS390XState *env, Int128 a, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); - float64 ret = float128_to_float64(make_float128(ah, al), &env->fpu_status); + float64 ret = float128_to_float64(ARG128(a), &env->fpu_status); s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); @@ -355,7 +351,7 @@ uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al, } /* convert 64-bit float to 128-bit float */ -uint64_t HELPER(lxdb)(CPUS390XState *env, uint64_t f2) +Int128 HELPER(lxdb)(CPUS390XState *env, uint64_t f2) { float128 ret = float64_to_float128(f2, &env->fpu_status); handle_exceptions(env, false, GETPC()); @@ -363,7 +359,7 @@ uint64_t HELPER(lxdb)(CPUS390XState *env, uint64_t f2) } /* convert 32-bit float to 128-bit float */ -uint64_t HELPER(lxeb)(CPUS390XState *env, uint64_t f2) +Int128 HELPER(lxeb)(CPUS390XState *env, uint64_t f2) { float128 ret = float32_to_float128(f2, &env->fpu_status); handle_exceptions(env, false, GETPC()); @@ -382,11 +378,10 @@ uint64_t HELPER(ledb)(CPUS390XState *env, uint64_t f2, uint32_t m34) } /* convert 128-bit float to 32-bit float */ -uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al, - uint32_t m34) +uint64_t HELPER(lexb)(CPUS390XState *env, Int128 a, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); - float32 ret = float128_to_float32(make_float128(ah, al), &env->fpu_status); + float32 ret = float128_to_float32(ARG128(a), &env->fpu_status); s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); @@ -410,11 +405,9 @@ uint32_t HELPER(cdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) } /* 128-bit FP compare */ -uint32_t HELPER(cxb)(CPUS390XState *env, uint64_t ah, uint64_t al, - uint64_t bh, uint64_t bl) +uint32_t HELPER(cxb)(CPUS390XState *env, Int128 a, Int128 b) { - FloatRelation cmp = float128_compare_quiet(make_float128(ah, al), - make_float128(bh, bl), + FloatRelation cmp = float128_compare_quiet(ARG128(a), ARG128(b), &env->fpu_status); handle_exceptions(env, false, GETPC()); return float_comp_to_cc(env, cmp); @@ -486,7 +479,7 @@ uint64_t HELPER(cdgb)(CPUS390XState *env, int64_t v2, uint32_t m34) } /* convert 64-bit int to 128-bit float */ -uint64_t HELPER(cxgb)(CPUS390XState *env, int64_t v2, uint32_t m34) +Int128 HELPER(cxgb)(CPUS390XState *env, int64_t v2, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float128 ret = int64_to_float128(v2, &env->fpu_status); @@ -519,7 +512,7 @@ uint64_t HELPER(cdlgb)(CPUS390XState *env, uint64_t v2, uint32_t m34) } /* convert 64-bit uint to 128-bit float */ -uint64_t HELPER(cxlgb)(CPUS390XState *env, uint64_t v2, uint32_t m34) +Int128 HELPER(cxlgb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float128 ret = uint64_to_float128(v2, &env->fpu_status); @@ -562,10 +555,10 @@ uint64_t HELPER(cgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) } /* convert 128-bit float to 64-bit int */ -uint64_t HELPER(cgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) +uint64_t HELPER(cgxb)(CPUS390XState *env, Int128 i2, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); - float128 v2 = make_float128(h, l); + float128 v2 = ARG128(i2); int64_t ret = float128_to_int64(v2, &env->fpu_status); uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status); @@ -611,10 +604,10 @@ uint64_t HELPER(cfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) } /* convert 128-bit float to 32-bit int */ -uint64_t HELPER(cfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) +uint64_t HELPER(cfxb)(CPUS390XState *env, Int128 i2, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); - float128 v2 = make_float128(h, l); + float128 v2 = ARG128(i2); int32_t ret = float128_to_int32(v2, &env->fpu_status); uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status); @@ -660,10 +653,10 @@ uint64_t HELPER(clgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) } /* convert 128-bit float to 64-bit uint */ -uint64_t HELPER(clgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) +uint64_t HELPER(clgxb)(CPUS390XState *env, Int128 i2, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); - float128 v2 = make_float128(h, l); + float128 v2 = ARG128(i2); uint64_t ret = float128_to_uint64(v2, &env->fpu_status); uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status); @@ -709,10 +702,10 @@ uint64_t HELPER(clfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) } /* convert 128-bit float to 32-bit uint */ -uint64_t HELPER(clfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) +uint64_t HELPER(clfxb)(CPUS390XState *env, Int128 i2, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); - float128 v2 = make_float128(h, l); + float128 v2 = ARG128(i2); uint32_t ret = float128_to_uint32(v2, &env->fpu_status); uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status); @@ -748,12 +741,10 @@ uint64_t HELPER(fidb)(CPUS390XState *env, uint64_t f2, uint32_t m34) } /* round to integer 128-bit */ -uint64_t HELPER(fixb)(CPUS390XState *env, uint64_t ah, uint64_t al, - uint32_t m34) +Int128 HELPER(fixb)(CPUS390XState *env, Int128 a, uint32_t m34) { int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); - float128 ret = float128_round_to_int(make_float128(ah, al), - &env->fpu_status); + float128 ret = float128_round_to_int(ARG128(a), &env->fpu_status); s390_restore_bfp_rounding_mode(env, old_mode); handle_exceptions(env, xxc_from_m34(m34), GETPC()); @@ -777,11 +768,9 @@ uint32_t HELPER(kdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) } /* 128-bit FP compare and signal */ -uint32_t HELPER(kxb)(CPUS390XState *env, uint64_t ah, uint64_t al, - uint64_t bh, uint64_t bl) +uint32_t HELPER(kxb)(CPUS390XState *env, Int128 a, Int128 b) { - FloatRelation cmp = float128_compare(make_float128(ah, al), - make_float128(bh, bl), + FloatRelation cmp = float128_compare(ARG128(a), ARG128(b), &env->fpu_status); handle_exceptions(env, false, GETPC()); return float_comp_to_cc(env, cmp); @@ -868,9 +857,9 @@ uint32_t HELPER(tcdb)(CPUS390XState *env, uint64_t v1, uint64_t m2) } /* test data class 128-bit */ -uint32_t HELPER(tcxb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint64_t m2) +uint32_t HELPER(tcxb)(CPUS390XState *env, Int128 a, uint64_t m2) { - return (m2 & float128_dcmask(env, make_float128(ah, al))) != 0; + return (m2 & float128_dcmask(env, ARG128(a))) != 0; } /* square root 32-bit */ @@ -890,9 +879,9 @@ uint64_t HELPER(sqdb)(CPUS390XState *env, uint64_t f2) } /* square root 128-bit */ -uint64_t HELPER(sqxb)(CPUS390XState *env, uint64_t ah, uint64_t al) +Int128 HELPER(sqxb)(CPUS390XState *env, Int128 a) { - float128 ret = float128_sqrt(make_float128(ah, al), &env->fpu_status); + float128 ret = float128_sqrt(ARG128(a), &env->fpu_status); handle_exceptions(env, false, GETPC()); return RET128(ret); } diff --git a/target/s390x/tcg/insn-data.def b/target/s390x/tcg/insn-data.h.inc similarity index 93% rename from target/s390x/tcg/insn-data.def rename to target/s390x/tcg/insn-data.h.inc index 5e448bb2c4..e7d61cdec2 100644 --- a/target/s390x/tcg/insn-data.def +++ b/target/s390x/tcg/insn-data.h.inc @@ -8,7 +8,7 @@ * * OPC = (op << 8) | op2 where op is the major, op2 the minor opcode * NAME = name of the opcode, used internally - * FMT = format of the opcode (defined in insn-format.def) + * FMT = format of the opcode (defined in insn-format.h.inc) * FAC = facility the opcode is available in (defined in DisasFacility) * I1 = func in1_xx fills o->in1 * I2 = func in2_xx fills o->in2 @@ -34,7 +34,7 @@ C(0xe318, AGF, RXY_a, Z, r1, m2_32s, r1, 0, add, adds64) F(0xb30a, AEBR, RRE, Z, e1, e2, new, e1, aeb, f32, IF_BFP) F(0xb31a, ADBR, RRE, Z, f1, f2, new, f1, adb, f64, IF_BFP) - F(0xb34a, AXBR, RRE, Z, x2h, x2l, x1, x1, axb, f128, IF_BFP) + F(0xb34a, AXBR, RRE, Z, x1, x2, new_x, x1, axb, f128, IF_BFP) F(0xed0a, AEB, RXE, Z, e1, m2_32u, new, e1, aeb, f32, IF_BFP) F(0xed1a, ADB, RXE, Z, f1, m2_64, new, f1, adb, f64, IF_BFP) /* ADD HIGH */ @@ -157,7 +157,7 @@ C(0xb2fa, NIAI, E, EH, 0, 0, 0, 0, 0, 0) /* CHECKSUM */ - C(0xb241, CKSM, RRE, Z, r1_o, ra2, new, r1_32, cksm, 0) + C(0xb241, CKSM, RRE, Z, r1_o, ra2_E, new, r1_32, cksm, 0) /* COPY SIGN */ F(0xb372, CPSDR, RRF_b, FPSSH, f3, f2, new, f1, cps, 0, IF_AFP1 | IF_AFP2 | IF_AFP3) @@ -172,13 +172,13 @@ C(0xe330, CGF, RXY_a, Z, r1_o, m2_32s, 0, 0, 0, cmps64) F(0xb309, CEBR, RRE, Z, e1, e2, 0, 0, ceb, 0, IF_BFP) F(0xb319, CDBR, RRE, Z, f1, f2, 0, 0, cdb, 0, IF_BFP) - F(0xb349, CXBR, RRE, Z, x2h, x2l, x1, 0, cxb, 0, IF_BFP) + F(0xb349, CXBR, RRE, Z, x1, x2, 0, 0, cxb, 0, IF_BFP) F(0xed09, CEB, RXE, Z, e1, m2_32u, 0, 0, ceb, 0, IF_BFP) F(0xed19, CDB, RXE, Z, f1, m2_64, 0, 0, cdb, 0, IF_BFP) /* COMPARE AND SIGNAL */ F(0xb308, KEBR, RRE, Z, e1, e2, 0, 0, keb, 0, IF_BFP) F(0xb318, KDBR, RRE, Z, f1, f2, 0, 0, kdb, 0, IF_BFP) - F(0xb348, KXBR, RRE, Z, x2h, x2l, x1, 0, kxb, 0, IF_BFP) + F(0xb348, KXBR, RRE, Z, x1, x2, 0, 0, kxb, 0, IF_BFP) F(0xed08, KEB, RXE, Z, e1, m2_32u, 0, 0, keb, 0, IF_BFP) F(0xed18, KDB, RXE, Z, f1, m2_64, 0, 0, kdb, 0, IF_BFP) /* COMPARE IMMEDIATE */ @@ -199,8 +199,8 @@ C(0xe55c, CHSI, SIL, GIE, m1_32s, i2, 0, 0, 0, cmps64) C(0xe558, CGHSI, SIL, GIE, m1_64, i2, 0, 0, 0, cmps64) /* COMPARE HALFWORD RELATIVE LONG */ - C(0xc605, CHRL, RIL_b, GIE, r1_o, mri2_32s, 0, 0, 0, cmps32) - C(0xc604, CGHRL, RIL_b, GIE, r1_o, mri2_64, 0, 0, 0, cmps64) + C(0xc605, CHRL, RIL_b, GIE, r1_o, mri2_16s, 0, 0, 0, cmps32) + C(0xc604, CGHRL, RIL_b, GIE, r1_o, mri2_16s, 0, 0, 0, cmps64) /* COMPARE HIGH */ C(0xb9cd, CHHR, RRE, HW, r1_sr32, r2_sr32, 0, 0, 0, cmps32) C(0xb9dd, CHLR, RRE, HW, r1_sr32, r2_o, 0, 0, 0, cmps32) @@ -276,7 +276,7 @@ /* COMPARE DOUBLE AND SWAP */ D(0xbb00, CDS, RS_a, Z, r3_D32, r1_D32, new, r1_D32, cs, 0, MO_TEUQ) D(0xeb31, CDSY, RSY_a, LD, r3_D32, r1_D32, new, r1_D32, cs, 0, MO_TEUQ) - C(0xeb3e, CDSG, RSY_a, Z, 0, 0, 0, 0, cdsg, 0) + C(0xeb3e, CDSG, RSY_a, Z, la2, r3_D64, 0, r1_D64, cdsg, 0) /* COMPARE AND SWAP AND STORE */ C(0xc802, CSST, SSF, CASS, la1, a2, 0, 0, csst, 0) @@ -290,40 +290,45 @@ D(0xb961, CLGRT, RRF_c, GIE, r1_o, r2_o, 0, 0, ct, 0, 1) D(0xeb23, CLT, RSY_b, MIE, r1_32u, m2_32u, 0, 0, ct, 0, 1) D(0xeb2b, CLGT, RSY_b, MIE, r1_o, m2_64, 0, 0, ct, 0, 1) - D(0xec73, CLFIT, RIE_a, GIE, r1_32u, i2_32u, 0, 0, ct, 0, 1) - D(0xec71, CLGIT, RIE_a, GIE, r1_o, i2_32u, 0, 0, ct, 0, 1) + D(0xec73, CLFIT, RIE_a, GIE, r1_32u, i2_16u, 0, 0, ct, 0, 1) + D(0xec71, CLGIT, RIE_a, GIE, r1_o, i2_16u, 0, 0, ct, 0, 1) +/* CONVERT TO BINARY */ + C(0x4f00, CVB, RX_a, Z, la2, 0, 0, 0, cvb, 0) + C(0xe306, CVBY, RXY_a, LD, la2, 0, 0, 0, cvb, 0) + C(0xe30e, CVBG, RXY_a, Z, la2, 0, r1, 0, cvbg, 0) /* CONVERT TO DECIMAL */ C(0x4e00, CVD, RX_a, Z, r1_o, a2, 0, 0, cvd, 0) C(0xe326, CVDY, RXY_a, LD, r1_o, a2, 0, 0, cvd, 0) + C(0xe32e, CVDG, RXY_a, Z, r1_o, a2, 0, 0, cvdg, 0) /* CONVERT TO FIXED */ F(0xb398, CFEBR, RRF_e, Z, 0, e2, new, r1_32, cfeb, 0, IF_BFP) F(0xb399, CFDBR, RRF_e, Z, 0, f2, new, r1_32, cfdb, 0, IF_BFP) - F(0xb39a, CFXBR, RRF_e, Z, x2h, x2l, new, r1_32, cfxb, 0, IF_BFP) + F(0xb39a, CFXBR, RRF_e, Z, 0, x2, new, r1_32, cfxb, 0, IF_BFP) F(0xb3a8, CGEBR, RRF_e, Z, 0, e2, r1, 0, cgeb, 0, IF_BFP) F(0xb3a9, CGDBR, RRF_e, Z, 0, f2, r1, 0, cgdb, 0, IF_BFP) - F(0xb3aa, CGXBR, RRF_e, Z, x2h, x2l, r1, 0, cgxb, 0, IF_BFP) + F(0xb3aa, CGXBR, RRF_e, Z, 0, x2, r1, 0, cgxb, 0, IF_BFP) /* CONVERT FROM FIXED */ F(0xb394, CEFBR, RRF_e, Z, 0, r2_32s, new, e1, cegb, 0, IF_BFP) F(0xb395, CDFBR, RRF_e, Z, 0, r2_32s, new, f1, cdgb, 0, IF_BFP) - F(0xb396, CXFBR, RRF_e, Z, 0, r2_32s, new_P, x1, cxgb, 0, IF_BFP) + F(0xb396, CXFBR, RRF_e, Z, 0, r2_32s, new_x, x1, cxgb, 0, IF_BFP) F(0xb3a4, CEGBR, RRF_e, Z, 0, r2_o, new, e1, cegb, 0, IF_BFP) F(0xb3a5, CDGBR, RRF_e, Z, 0, r2_o, new, f1, cdgb, 0, IF_BFP) - F(0xb3a6, CXGBR, RRF_e, Z, 0, r2_o, new_P, x1, cxgb, 0, IF_BFP) + F(0xb3a6, CXGBR, RRF_e, Z, 0, r2_o, new_x, x1, cxgb, 0, IF_BFP) /* CONVERT TO LOGICAL */ F(0xb39c, CLFEBR, RRF_e, FPE, 0, e2, new, r1_32, clfeb, 0, IF_BFP) F(0xb39d, CLFDBR, RRF_e, FPE, 0, f2, new, r1_32, clfdb, 0, IF_BFP) - F(0xb39e, CLFXBR, RRF_e, FPE, x2h, x2l, new, r1_32, clfxb, 0, IF_BFP) + F(0xb39e, CLFXBR, RRF_e, FPE, 0, x2, new, r1_32, clfxb, 0, IF_BFP) F(0xb3ac, CLGEBR, RRF_e, FPE, 0, e2, r1, 0, clgeb, 0, IF_BFP) F(0xb3ad, CLGDBR, RRF_e, FPE, 0, f2, r1, 0, clgdb, 0, IF_BFP) - F(0xb3ae, CLGXBR, RRF_e, FPE, x2h, x2l, r1, 0, clgxb, 0, IF_BFP) + F(0xb3ae, CLGXBR, RRF_e, FPE, 0, x2, r1, 0, clgxb, 0, IF_BFP) /* CONVERT FROM LOGICAL */ F(0xb390, CELFBR, RRF_e, FPE, 0, r2_32u, new, e1, celgb, 0, IF_BFP) F(0xb391, CDLFBR, RRF_e, FPE, 0, r2_32u, new, f1, cdlgb, 0, IF_BFP) - F(0xb392, CXLFBR, RRF_e, FPE, 0, r2_32u, new_P, x1, cxlgb, 0, IF_BFP) + F(0xb392, CXLFBR, RRF_e, FPE, 0, r2_32u, new_x, x1, cxlgb, 0, IF_BFP) F(0xb3a0, CELGBR, RRF_e, FPE, 0, r2_o, new, e1, celgb, 0, IF_BFP) F(0xb3a1, CDLGBR, RRF_e, FPE, 0, r2_o, new, f1, cdlgb, 0, IF_BFP) - F(0xb3a2, CXLGBR, RRF_e, FPE, 0, r2_o, new_P, x1, cxlgb, 0, IF_BFP) + F(0xb3a2, CXLGBR, RRF_e, FPE, 0, r2_o, new_x, x1, cxlgb, 0, IF_BFP) /* CONVERT UTF-8 TO UTF-16 */ D(0xb2a7, CU12, RRF_c, Z, 0, 0, 0, 0, cuXX, 0, 12) @@ -343,7 +348,7 @@ C(0x5d00, D, RX_a, Z, r1_D32, m2_32s, new_P, r1_P32, divs32, 0) F(0xb30d, DEBR, RRE, Z, e1, e2, new, e1, deb, 0, IF_BFP) F(0xb31d, DDBR, RRE, Z, f1, f2, new, f1, ddb, 0, IF_BFP) - F(0xb34d, DXBR, RRE, Z, x2h, x2l, x1, x1, dxb, 0, IF_BFP) + F(0xb34d, DXBR, RRE, Z, x1, x2, new_x, x1, dxb, 0, IF_BFP) F(0xed0d, DEB, RXE, Z, e1, m2_32u, new, e1, deb, 0, IF_BFP) F(0xed1d, DDB, RXE, Z, f1, m2_64, new, f1, ddb, 0, IF_BFP) /* DIVIDE LOGICAL */ @@ -410,25 +415,25 @@ /* LOAD */ C(0x1800, LR, RR_a, Z, 0, r2_o, 0, cond_r1r2_32, mov2, 0) - C(0x5800, L, RX_a, Z, 0, a2, new, r1_32, ld32s, 0) - C(0xe358, LY, RXY_a, LD, 0, a2, new, r1_32, ld32s, 0) + D(0x5800, L, RX_a, Z, 0, a2, new, r1_32, ld32s, 0, 0) + D(0xe358, LY, RXY_a, LD, 0, a2, new, r1_32, ld32s, 0, 0) C(0xb904, LGR, RRE, Z, 0, r2_o, 0, r1, mov2, 0) C(0xb914, LGFR, RRE, Z, 0, r2_32s, 0, r1, mov2, 0) - C(0xe304, LG, RXY_a, Z, 0, a2, r1, 0, ld64, 0) - C(0xe314, LGF, RXY_a, Z, 0, a2, r1, 0, ld32s, 0) + D(0xe304, LG, RXY_a, Z, 0, a2, r1, 0, ld64, 0, 0) + D(0xe314, LGF, RXY_a, Z, 0, a2, r1, 0, ld32s, 0, 0) F(0x2800, LDR, RR_a, Z, 0, f2, 0, f1, mov2, 0, IF_AFP1 | IF_AFP2) F(0x6800, LD, RX_a, Z, 0, m2_64, 0, f1, mov2, 0, IF_AFP1) F(0xed65, LDY, RXY_a, LD, 0, m2_64, 0, f1, mov2, 0, IF_AFP1) F(0x3800, LER, RR_a, Z, 0, e2, 0, cond_e1e2, mov2, 0, IF_AFP1 | IF_AFP2) F(0x7800, LE, RX_a, Z, 0, m2_32u, 0, e1, mov2, 0, IF_AFP1) F(0xed64, LEY, RXY_a, LD, 0, m2_32u, 0, e1, mov2, 0, IF_AFP1) - F(0xb365, LXR, RRE, Z, x2h, x2l, 0, x1, movx, 0, IF_AFP1) + F(0xb365, LXR, RRE, Z, x2h, x2l, 0, x1_P, movx, 0, IF_AFP1) /* LOAD IMMEDIATE */ C(0xc001, LGFI, RIL_a, EI, 0, i2, 0, r1, mov2, 0) /* LOAD RELATIVE LONG */ - C(0xc40d, LRL, RIL_b, GIE, 0, ri2, new, r1_32, ld32s, 0) - C(0xc408, LGRL, RIL_b, GIE, 0, ri2, r1, 0, ld64, 0) - C(0xc40c, LGFRL, RIL_b, GIE, 0, ri2, r1, 0, ld32s, 0) + D(0xc40d, LRL, RIL_b, GIE, 0, ri2, new, r1_32, ld32s, 0, MO_ALIGN) + D(0xc408, LGRL, RIL_b, GIE, 0, ri2, r1, 0, ld64, 0, MO_ALIGN) + D(0xc40c, LGFRL, RIL_b, GIE, 0, ri2, r1, 0, ld32s, 0, MO_ALIGN) /* LOAD ADDRESS */ C(0x4100, LA, RX_a, Z, 0, a2, 0, r1, mov2, 0) C(0xe371, LAY, RXY_a, LD, 0, a2, 0, r1, mov2, 0) @@ -442,7 +447,7 @@ D(0xebe8, LAAG, RSY_a, ILA, r3, a2, new, in2_r1, laa, adds64, MO_TEUQ) /* LOAD AND ADD LOGICAL */ D(0xebfa, LAAL, RSY_a, ILA, r3_32u, a2, new, in2_r1_32, laa, addu32, MO_TEUL) - D(0xebea, LAALG, RSY_a, ILA, r3, a2, new, in2_r1, laa, addu64, MO_TEUQ) + D(0xebea, LAALG, RSY_a, ILA, r3, a2, new, in2_r1, laa_addu64, addu64, MO_TEUQ) /* LOAD AND AND */ D(0xebf4, LAN, RSY_a, ILA, r3_32s, a2, new, in2_r1_32, lan, nz32, MO_TESL) D(0xebe4, LANG, RSY_a, ILA, r3, a2, new, in2_r1, lan, nz64, MO_TEUQ) @@ -456,17 +461,17 @@ C(0x1200, LTR, RR_a, Z, 0, r2_o, 0, cond_r1r2_32, mov2, s32) C(0xb902, LTGR, RRE, Z, 0, r2_o, 0, r1, mov2, s64) C(0xb912, LTGFR, RRE, Z, 0, r2_32s, 0, r1, mov2, s64) - C(0xe312, LT, RXY_a, EI, 0, a2, new, r1_32, ld32s, s64) - C(0xe302, LTG, RXY_a, EI, 0, a2, r1, 0, ld64, s64) - C(0xe332, LTGF, RXY_a, GIE, 0, a2, r1, 0, ld32s, s64) + D(0xe312, LT, RXY_a, EI, 0, a2, new, r1_32, ld32s, s64, 0) + D(0xe302, LTG, RXY_a, EI, 0, a2, r1, 0, ld64, s64, 0) + D(0xe332, LTGF, RXY_a, GIE, 0, a2, r1, 0, ld32s, s64, 0) F(0xb302, LTEBR, RRE, Z, 0, e2, 0, cond_e1e2, mov2, f32, IF_BFP) F(0xb312, LTDBR, RRE, Z, 0, f2, 0, f1, mov2, f64, IF_BFP) - F(0xb342, LTXBR, RRE, Z, x2h, x2l, 0, x1, movx, f128, IF_BFP) + F(0xb342, LTXBR, RRE, Z, x2h, x2l, 0, x1_P, movx, f128, IF_BFP) /* LOAD AND TRAP */ C(0xe39f, LAT, RXY_a, LAT, 0, m2_32u, r1, 0, lat, 0) C(0xe385, LGAT, RXY_a, LAT, 0, a2, r1, 0, lgat, 0) /* LOAD AND ZERO RIGHTMOST BYTE */ - C(0xe3eb, LZRF, RXY_a, LZRB, 0, m2_32u, new, r1_32, lzrb, 0) + C(0xe33b, LZRF, RXY_a, LZRB, 0, m2_32u, new, r1_32, lzrb, 0) C(0xe32a, LZRG, RXY_a, LZRB, 0, m2_64, r1, 0, lzrb, 0) /* LOAD LOGICAL AND ZERO RIGHTMOST BYTE */ C(0xe33a, LLZRGF, RXY_a, LZRB, 0, m2_32u, r1, 0, lzrb, 0) @@ -483,10 +488,10 @@ C(0xb913, LCGFR, RRE, Z, 0, r2_32s, r1, 0, neg, neg64) F(0xb303, LCEBR, RRE, Z, 0, e2, new, e1, negf32, f32, IF_BFP) F(0xb313, LCDBR, RRE, Z, 0, f2, new, f1, negf64, f64, IF_BFP) - F(0xb343, LCXBR, RRE, Z, x2h, x2l, new_P, x1, negf128, f128, IF_BFP) + F(0xb343, LCXBR, RRE, Z, x2h, x2l, new_P, x1_P, negf128, f128, IF_BFP) F(0xb373, LCDFR, RRE, FPSSH, 0, f2, new, f1, negf64, 0, IF_AFP1 | IF_AFP2) /* LOAD COUNT TO BLOCK BOUNDARY */ - C(0xe727, LCBB, RXE, V, la2, 0, r1, 0, lcbb, 0) + C(0xe727, LCBB, RXE, V, la2, 0, new, r1_32, lcbb, 0) /* LOAD HALFWORD */ C(0xb927, LHR, RRE, EI, 0, r2_16s, 0, r1_32, mov2, 0) C(0xb907, LGHR, RRE, EI, 0, r2_16s, 0, r1, mov2, 0) @@ -502,16 +507,16 @@ C(0xc405, LHRL, RIL_b, GIE, 0, ri2, new, r1_32, ld16s, 0) C(0xc404, LGHRL, RIL_b, GIE, 0, ri2, r1, 0, ld16s, 0) /* LOAD HIGH */ - C(0xe3ca, LFH, RXY_a, HW, 0, a2, new, r1_32h, ld32u, 0) + D(0xe3ca, LFH, RXY_a, HW, 0, a2, new, r1_32h, ld32u, 0, 0) /* LOAG HIGH AND TRAP */ C(0xe3c8, LFHAT, RXY_a, LAT, 0, m2_32u, r1, 0, lfhat, 0) /* LOAD LOGICAL */ C(0xb916, LLGFR, RRE, Z, 0, r2_32u, 0, r1, mov2, 0) - C(0xe316, LLGF, RXY_a, Z, 0, a2, r1, 0, ld32u, 0) + D(0xe316, LLGF, RXY_a, Z, 0, a2, r1, 0, ld32u, 0, 0) /* LOAD LOGICAL AND TRAP */ C(0xe39d, LLGFAT, RXY_a, LAT, 0, a2, r1, 0, llgfat, 0) /* LOAD LOGICAL RELATIVE LONG */ - C(0xc40e, LLGFRL, RIL_b, GIE, 0, ri2, r1, 0, ld32u, 0) + D(0xc40e, LLGFRL, RIL_b, GIE, 0, ri2, r1, 0, ld32u, 0, MO_ALIGN) /* LOAD LOGICAL CHARACTER */ C(0xb994, LLCR, RRE, EI, 0, r2_8u, 0, r1_32, mov2, 0) C(0xb984, LLGCR, RRE, EI, 0, r2_8u, 0, r1, mov2, 0) @@ -529,7 +534,7 @@ /* LOAD LOGICAL HALFWORD RELATIVE LONG */ C(0xc402, LLHRL, RIL_b, GIE, 0, ri2, new, r1_32, ld16u, 0) C(0xc406, LLGHRL, RIL_b, GIE, 0, ri2, r1, 0, ld16u, 0) -/* LOAD LOGICAL IMMEDATE */ +/* LOAD LOGICAL IMMEDIATE */ D(0xc00e, LLIHF, RIL_a, EI, 0, i2_32u_shl, 0, r1, mov2, 0, 32) D(0xc00f, LLILF, RIL_a, EI, 0, i2_32u_shl, 0, r1, mov2, 0, 0) D(0xa50c, LLIHH, RI_a, Z, 0, i2_16u_shl, 0, r1, mov2, 0, 48) @@ -552,7 +557,7 @@ C(0xb911, LNGFR, RRE, Z, 0, r2_32s, r1, 0, nabs, nabs64) F(0xb301, LNEBR, RRE, Z, 0, e2, new, e1, nabsf32, f32, IF_BFP) F(0xb311, LNDBR, RRE, Z, 0, f2, new, f1, nabsf64, f64, IF_BFP) - F(0xb341, LNXBR, RRE, Z, x2h, x2l, new_P, x1, nabsf128, f128, IF_BFP) + F(0xb341, LNXBR, RRE, Z, x2h, x2l, new_P, x1_P, nabsf128, f128, IF_BFP) F(0xb371, LNDFR, RRE, FPSSH, 0, f2, new, f1, nabsf64, 0, IF_AFP1 | IF_AFP2) /* LOAD ON CONDITION */ C(0xb9f2, LOCR, RRF_c, LOC, r1, r2, new, r1_32, loc, 0) @@ -564,20 +569,20 @@ C(0xec46, LOCGHI, RIE_g, LOC2, r1, i2, r1, 0, loc, 0) C(0xec4e, LOCHHI, RIE_g, LOC2, r1_sr32, i2, new, r1_32h, loc, 0) /* LOAD HIGH ON CONDITION */ - C(0xb9e0, LOCFHR, RRF_c, LOC2, r1_sr32, r2, new, r1_32h, loc, 0) + C(0xb9e0, LOCFHR, RRF_c, LOC2, r1_sr32, r2_sr32, new, r1_32h, loc, 0) C(0xebe0, LOCFH, RSY_b, LOC2, r1_sr32, m2_32u, new, r1_32h, loc, 0) /* LOAD PAIR DISJOINT */ D(0xc804, LPD, SSF, ILA, 0, 0, new_P, r3_P32, lpd, 0, MO_TEUL) D(0xc805, LPDG, SSF, ILA, 0, 0, new_P, r3_P64, lpd, 0, MO_TEUQ) /* LOAD PAIR FROM QUADWORD */ - C(0xe38f, LPQ, RXY_a, Z, 0, a2, r1_P, 0, lpq, 0) + C(0xe38f, LPQ, RXY_a, Z, 0, a2, 0, r1_D64, lpq, 0) /* LOAD POSITIVE */ C(0x1000, LPR, RR_a, Z, 0, r2_32s, new, r1_32, abs, abs32) C(0xb900, LPGR, RRE, Z, 0, r2, r1, 0, abs, abs64) C(0xb910, LPGFR, RRE, Z, 0, r2_32s, r1, 0, abs, abs64) F(0xb300, LPEBR, RRE, Z, 0, e2, new, e1, absf32, f32, IF_BFP) F(0xb310, LPDBR, RRE, Z, 0, f2, new, f1, absf64, f64, IF_BFP) - F(0xb340, LPXBR, RRE, Z, x2h, x2l, new_P, x1, absf128, f128, IF_BFP) + F(0xb340, LPXBR, RRE, Z, x2h, x2l, new_P, x1_P, absf128, f128, IF_BFP) F(0xb370, LPDFR, RRE, FPSSH, 0, f2, new, f1, absf64, 0, IF_AFP1 | IF_AFP2) /* LOAD REVERSED */ C(0xb91f, LRVR, RRE, Z, 0, r2_32u, new, r1_32, rev32, 0) @@ -588,7 +593,7 @@ /* LOAD ZERO */ F(0xb374, LZER, RRE, Z, 0, 0, 0, e1, zero, 0, IF_AFP1) F(0xb375, LZDR, RRE, Z, 0, 0, 0, f1, zero, 0, IF_AFP1) - F(0xb376, LZXR, RRE, Z, 0, 0, 0, x1, zero2, 0, IF_AFP1) + F(0xb376, LZXR, RRE, Z, 0, 0, 0, x1_P, zero2, 0, IF_AFP1) /* LOAD FPC */ F(0xb29d, LFPC, S, Z, 0, m2_32u, 0, 0, sfpc, 0, IF_BFP) @@ -597,21 +602,21 @@ /* LOAD FP INTEGER */ F(0xb357, FIEBR, RRF_e, Z, 0, e2, new, e1, fieb, 0, IF_BFP) F(0xb35f, FIDBR, RRF_e, Z, 0, f2, new, f1, fidb, 0, IF_BFP) - F(0xb347, FIXBR, RRF_e, Z, x2h, x2l, new_P, x1, fixb, 0, IF_BFP) + F(0xb347, FIXBR, RRF_e, Z, 0, x2, new_x, x1, fixb, 0, IF_BFP) /* LOAD LENGTHENED */ F(0xb304, LDEBR, RRE, Z, 0, e2, new, f1, ldeb, 0, IF_BFP) - F(0xb305, LXDBR, RRE, Z, 0, f2, new_P, x1, lxdb, 0, IF_BFP) - F(0xb306, LXEBR, RRE, Z, 0, e2, new_P, x1, lxeb, 0, IF_BFP) + F(0xb305, LXDBR, RRE, Z, 0, f2, new_x, x1, lxdb, 0, IF_BFP) + F(0xb306, LXEBR, RRE, Z, 0, e2, new_x, x1, lxeb, 0, IF_BFP) F(0xed04, LDEB, RXE, Z, 0, m2_32u, new, f1, ldeb, 0, IF_BFP) - F(0xed05, LXDB, RXE, Z, 0, m2_64, new_P, x1, lxdb, 0, IF_BFP) - F(0xed06, LXEB, RXE, Z, 0, m2_32u, new_P, x1, lxeb, 0, IF_BFP) - F(0xb324, LDER, RXE, Z, 0, e2, new, f1, lde, 0, IF_AFP1) + F(0xed05, LXDB, RXE, Z, 0, m2_64, new_x, x1, lxdb, 0, IF_BFP) + F(0xed06, LXEB, RXE, Z, 0, m2_32u, new_x, x1, lxeb, 0, IF_BFP) + F(0xb324, LDER, RRE, Z, 0, e2, new, f1, lde, 0, IF_AFP1) F(0xed24, LDE, RXE, Z, 0, m2_32u, new, f1, lde, 0, IF_AFP1) /* LOAD ROUNDED */ F(0xb344, LEDBR, RRF_e, Z, 0, f2, new, e1, ledb, 0, IF_BFP) - F(0xb345, LDXBR, RRF_e, Z, x2h, x2l, new, f1, ldxb, 0, IF_BFP) - F(0xb346, LEXBR, RRF_e, Z, x2h, x2l, new, e1, lexb, 0, IF_BFP) + F(0xb345, LDXBR, RRF_e, Z, 0, x2, new, f1, ldxb, 0, IF_BFP) + F(0xb346, LEXBR, RRF_e, Z, 0, x2, new, e1, lexb, 0, IF_BFP) /* LOAD MULTIPLE */ C(0x9800, LM, RS_a, Z, 0, a2, 0, 0, lm32, 0) @@ -666,13 +671,13 @@ C(0xe384, MG, RXY_a, MIE2,r1p1_o, m2_64, r1_P, 0, muls128, 0) F(0xb317, MEEBR, RRE, Z, e1, e2, new, e1, meeb, 0, IF_BFP) F(0xb31c, MDBR, RRE, Z, f1, f2, new, f1, mdb, 0, IF_BFP) - F(0xb34c, MXBR, RRE, Z, x2h, x2l, x1, x1, mxb, 0, IF_BFP) - F(0xb30c, MDEBR, RRE, Z, f1, e2, new, f1, mdeb, 0, IF_BFP) - F(0xb307, MXDBR, RRE, Z, 0, f2, x1, x1, mxdb, 0, IF_BFP) + F(0xb34c, MXBR, RRE, Z, x1, x2, new_x, x1, mxb, 0, IF_BFP) + F(0xb30c, MDEBR, RRE, Z, e1, e2, new, f1, mdeb, 0, IF_BFP) + F(0xb307, MXDBR, RRE, Z, f1, f2, new_x, x1, mxdb, 0, IF_BFP) F(0xed17, MEEB, RXE, Z, e1, m2_32u, new, e1, meeb, 0, IF_BFP) F(0xed1c, MDB, RXE, Z, f1, m2_64, new, f1, mdb, 0, IF_BFP) - F(0xed0c, MDEB, RXE, Z, f1, m2_32u, new, f1, mdeb, 0, IF_BFP) - F(0xed07, MXDB, RXE, Z, 0, m2_64, x1, x1, mxdb, 0, IF_BFP) + F(0xed0c, MDEB, RXE, Z, e1, m2_32u, new, f1, mdeb, 0, IF_BFP) + F(0xed07, MXDB, RXE, Z, f1, m2_64, new_x, x1, mxdb, 0, IF_BFP) /* MULTIPLY HALFWORD */ C(0x4c00, MH, RX_a, Z, r1_o, m2_16s, new, r1_32, mul, 0) C(0xe37c, MHY, RXY_a, GIE, r1_o, m2_16s, new, r1_32, mul, 0) @@ -835,21 +840,21 @@ /* SQUARE ROOT */ F(0xb314, SQEBR, RRE, Z, 0, e2, new, e1, sqeb, 0, IF_BFP) F(0xb315, SQDBR, RRE, Z, 0, f2, new, f1, sqdb, 0, IF_BFP) - F(0xb316, SQXBR, RRE, Z, x2h, x2l, new_P, x1, sqxb, 0, IF_BFP) + F(0xb316, SQXBR, RRE, Z, 0, x2, new_x, x1, sqxb, 0, IF_BFP) F(0xed14, SQEB, RXE, Z, 0, m2_32u, new, e1, sqeb, 0, IF_BFP) F(0xed15, SQDB, RXE, Z, 0, m2_64, new, f1, sqdb, 0, IF_BFP) /* STORE */ - C(0x5000, ST, RX_a, Z, r1_o, a2, 0, 0, st32, 0) - C(0xe350, STY, RXY_a, LD, r1_o, a2, 0, 0, st32, 0) - C(0xe324, STG, RXY_a, Z, r1_o, a2, 0, 0, st64, 0) - F(0x6000, STD, RX_a, Z, f1, a2, 0, 0, st64, 0, IF_AFP1) - F(0xed67, STDY, RXY_a, LD, f1, a2, 0, 0, st64, 0, IF_AFP1) - F(0x7000, STE, RX_a, Z, e1, a2, 0, 0, st32, 0, IF_AFP1) - F(0xed66, STEY, RXY_a, LD, e1, a2, 0, 0, st32, 0, IF_AFP1) + D(0x5000, ST, RX_a, Z, r1_o, a2, 0, 0, st32, 0, 0) + D(0xe350, STY, RXY_a, LD, r1_o, a2, 0, 0, st32, 0, 0) + D(0xe324, STG, RXY_a, Z, r1_o, a2, 0, 0, st64, 0, 0) + E(0x6000, STD, RX_a, Z, f1, a2, 0, 0, st64, 0, 0, IF_AFP1) + E(0xed67, STDY, RXY_a, LD, f1, a2, 0, 0, st64, 0, 0, IF_AFP1) + E(0x7000, STE, RX_a, Z, e1, a2, 0, 0, st32, 0, 0, IF_AFP1) + E(0xed66, STEY, RXY_a, LD, e1, a2, 0, 0, st32, 0, 0, IF_AFP1) /* STORE RELATIVE LONG */ - C(0xc40f, STRL, RIL_b, GIE, r1_o, ri2, 0, 0, st32, 0) - C(0xc40b, STGRL, RIL_b, GIE, r1_o, ri2, 0, 0, st64, 0) + D(0xc40f, STRL, RIL_b, GIE, r1_o, ri2, 0, 0, st32, 0, MO_ALIGN) + D(0xc40b, STGRL, RIL_b, GIE, r1_o, ri2, 0, 0, st64, 0, MO_ALIGN) /* STORE CHARACTER */ C(0x4200, STC, RX_a, Z, r1_o, a2, 0, 0, st8, 0) C(0xe372, STCY, RXY_a, LD, r1_o, a2, 0, 0, st8, 0) @@ -867,7 +872,7 @@ /* STORE HALFWORD RELATIVE LONG */ C(0xc407, STHRL, RIL_b, GIE, r1_o, ri2, 0, 0, st16, 0) /* STORE HIGH */ - C(0xe3cb, STFH, RXY_a, HW, r1_sr32, a2, 0, 0, st32, 0) + D(0xe3cb, STFH, RXY_a, HW, r1_sr32, a2, 0, 0, st32, 0, 0) /* STORE ON CONDITION */ D(0xebf3, STOC, RSY_b, LOC, 0, 0, 0, 0, soc, 0, 0) D(0xebe3, STOCG, RSY_b, LOC, 0, 0, 0, 0, soc, 0, 1) @@ -913,7 +918,7 @@ C(0xe319, SGF, RXY_a, Z, r1, m2_32s, r1, 0, sub, subs64) F(0xb30b, SEBR, RRE, Z, e1, e2, new, e1, seb, f32, IF_BFP) F(0xb31b, SDBR, RRE, Z, f1, f2, new, f1, sdb, f64, IF_BFP) - F(0xb34b, SXBR, RRE, Z, x2h, x2l, x1, x1, sxb, f128, IF_BFP) + F(0xb34b, SXBR, RRE, Z, x1, x2, new_x, x1, sxb, f128, IF_BFP) F(0xed0b, SEB, RXE, Z, e1, m2_32u, new, e1, seb, f32, IF_BFP) F(0xed1b, SDB, RXE, Z, f1, m2_64, new, f1, sdb, f64, IF_BFP) /* SUBTRACT HALFWORD */ @@ -957,7 +962,7 @@ /* TEST DATA CLASS */ F(0xed10, TCEB, RXE, Z, e1, a2, 0, 0, tceb, 0, IF_BFP) F(0xed11, TCDB, RXE, Z, f1, a2, 0, 0, tcdb, 0, IF_BFP) - F(0xed12, TCXB, RXE, Z, 0, a2, x1, 0, tcxb, 0, IF_BFP) + F(0xed12, TCXB, RXE, Z, x1, a2, 0, 0, tcxb, 0, IF_BFP) /* TEST DECIMAL */ C(0xebc0, TP, RSL, E2, la1, 0, 0, 0, tp, 0) @@ -1355,9 +1360,9 @@ E(0xb24b, LURA, RRE, Z, 0, ra2, new, r1_32, lura, 0, MO_TEUL, IF_PRIV) E(0xb905, LURAG, RRE, Z, 0, ra2, r1, 0, lura, 0, MO_TEUQ, IF_PRIV) /* MOVE TO PRIMARY */ - F(0xda00, MVCP, SS_d, Z, la1, a2, 0, 0, mvcp, 0, IF_PRIV) + C(0xda00, MVCP, SS_d, Z, la1, a2, 0, 0, mvcp, 0) /* MOVE TO SECONDARY */ - F(0xdb00, MVCS, SS_d, Z, la1, a2, 0, 0, mvcs, 0, IF_PRIV) + C(0xdb00, MVCS, SS_d, Z, la1, a2, 0, 0, mvcs, 0) /* PURGE TLB */ F(0xb20d, PTLB, S, Z, 0, 0, 0, 0, ptlb, 0, IF_PRIV) /* RESET REFERENCE BIT EXTENDED */ @@ -1365,7 +1370,7 @@ /* SERVICE CALL LOGICAL PROCESSOR (PV hypercall) */ F(0xb220, SERVC, RRE, Z, r1_o, r2_o, 0, 0, servc, 0, IF_PRIV | IF_IO) /* SET ADDRESS SPACE CONTROL FAST */ - F(0xb279, SACF, S, Z, 0, a2, 0, 0, sacf, 0, IF_PRIV) + C(0xb279, SACF, S, Z, 0, a2, 0, 0, sacf, 0) /* SET CLOCK */ F(0xb204, SCK, S, Z, 0, m2_64a, 0, 0, sck, 0, IF_PRIV | IF_IO) /* SET CLOCK COMPARATOR */ diff --git a/target/s390x/tcg/insn-format.def b/target/s390x/tcg/insn-format.h.inc similarity index 100% rename from target/s390x/tcg/insn-format.def rename to target/s390x/tcg/insn-format.h.inc diff --git a/target/s390x/tcg/int_helper.c b/target/s390x/tcg/int_helper.c index 954542388a..2af970f2c8 100644 --- a/target/s390x/tcg/int_helper.c +++ b/target/s390x/tcg/int_helper.c @@ -25,6 +25,7 @@ #include "exec/exec-all.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" /* #define DEBUG_HELPER */ #ifdef DEBUG_HELPER @@ -34,88 +35,143 @@ #endif /* 64/32 -> 32 signed division */ -int64_t HELPER(divs32)(CPUS390XState *env, int64_t a, int64_t b64) +uint64_t HELPER(divs32)(CPUS390XState *env, int64_t a, int64_t b64) { - int32_t ret, b = b64; - int64_t q; + int32_t b = b64; + int64_t q, r; if (b == 0) { tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } - ret = q = a / b; - env->retxl = a % b; + q = a / b; + r = a % b; /* Catch non-representable quotient. */ - if (ret != q) { + if (q != (int32_t)q) { tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } - return ret; + return deposit64(q, 32, 32, r); } /* 64/32 -> 32 unsigned division */ uint64_t HELPER(divu32)(CPUS390XState *env, uint64_t a, uint64_t b64) { - uint32_t ret, b = b64; - uint64_t q; + uint32_t b = b64; + uint64_t q, r; if (b == 0) { tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } - ret = q = a / b; - env->retxl = a % b; + q = a / b; + r = a % b; /* Catch non-representable quotient. */ - if (ret != q) { + if (q != (uint32_t)q) { tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } - return ret; + return deposit64(q, 32, 32, r); } /* 64/64 -> 64 signed division */ -int64_t HELPER(divs64)(CPUS390XState *env, int64_t a, int64_t b) +Int128 HELPER(divs64)(CPUS390XState *env, int64_t a, int64_t b) { /* Catch divide by zero, and non-representable quotient (MIN / -1). */ if (b == 0 || (b == -1 && a == (1ll << 63))) { tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } - env->retxl = a % b; - return a / b; + return int128_make128(a / b, a % b); } /* 128 -> 64/64 unsigned division */ -uint64_t HELPER(divu64)(CPUS390XState *env, uint64_t ah, uint64_t al, - uint64_t b) +Int128 HELPER(divu64)(CPUS390XState *env, uint64_t ah, uint64_t al, uint64_t b) { - uint64_t ret; - /* Signal divide by zero. */ - if (b == 0) { + if (b != 0) { + uint64_t r = divu128(&al, &ah, b); + if (ah == 0) { + return int128_make128(al, r); + } + } + /* divide by zero or overflow */ + tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); +} + +void HELPER(cvb)(CPUS390XState *env, uint32_t r1, uint64_t dec) +{ + int64_t pow10 = 1, bin = 0; + int digit, sign; + + sign = dec & 0xf; + if (sign < 0xa) { + tcg_s390_data_exception(env, 0, GETPC()); + } + dec >>= 4; + + while (dec) { + digit = dec & 0xf; + if (digit > 0x9) { + tcg_s390_data_exception(env, 0, GETPC()); + } + dec >>= 4; + bin += digit * pow10; + pow10 *= 10; + } + + if (sign == 0xb || sign == 0xd) { + bin = -bin; + } + + /* R1 is updated even on fixed-point-divide exception. */ + env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | (uint32_t)bin; + if (bin != (int32_t)bin) { tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } - if (ah == 0) { - /* 64 -> 64/64 case */ - env->retxl = al % b; - ret = al / b; - } else { - /* ??? Move i386 idivq helper to host-utils. */ -#ifdef CONFIG_INT128 - __uint128_t a = ((__uint128_t)ah << 64) | al; - __uint128_t q = a / b; - env->retxl = a % b; - ret = q; - if (ret != q) { +} + +uint64_t HELPER(cvbg)(CPUS390XState *env, Int128 dec) +{ + uint64_t dec64[] = {int128_getlo(dec), int128_gethi(dec)}; + int64_t bin = 0, pow10, tmp; + int digit, i, sign; + + sign = dec64[0] & 0xf; + if (sign < 0xa) { + tcg_s390_data_exception(env, 0, GETPC()); + } + dec64[0] >>= 4; + pow10 = (sign == 0xb || sign == 0xd) ? -1 : 1; + + for (i = 1; i < 20; i++) { + digit = dec64[i >> 4] & 0xf; + if (digit > 0x9) { + tcg_s390_data_exception(env, 0, GETPC()); + } + dec64[i >> 4] >>= 4; + /* + * Prepend the next digit and check for overflow. The multiplication + * cannot overflow, since, conveniently, the int64_t limits are + * approximately +-9.2E+18. If bin is zero, the addition cannot + * overflow. Otherwise bin is known to have the same sign as the rhs + * addend, in which case overflow happens if and only if the result + * has a different sign. + */ + tmp = bin + pow10 * digit; + if (bin && ((tmp ^ bin) < 0)) { tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } -#else - /* 32-bit hosts would need special wrapper functionality - just abort if - we encounter such a case; it's very unlikely anyways. */ - cpu_abort(env_cpu(env), "128 -> 64/64 division not implemented\n"); -#endif + bin = tmp; + pow10 *= 10; } - return ret; + + g_assert(!dec64[0]); + if (dec64[1]) { + tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); + } + + return bin; } uint64_t HELPER(cvd)(int32_t reg) @@ -138,6 +194,27 @@ uint64_t HELPER(cvd)(int32_t reg) return dec; } +Int128 HELPER(cvdg)(int64_t reg) +{ + /* positive 0 */ + Int128 dec = int128_make64(0x0c); + Int128 bin = int128_makes64(reg); + Int128 base = int128_make64(10); + int shift; + + if (!int128_nonneg(bin)) { + bin = int128_neg(bin); + dec = int128_make64(0x0d); + } + + for (shift = 4; (shift < 128) && int128_nz(bin); shift += 4) { + dec = int128_or(dec, int128_lshift(int128_remu(bin, base), shift)); + bin = int128_divu(bin, base); + } + + return dec; +} + uint64_t HELPER(popcnt)(uint64_t val) { /* Note that we don't fold past bytes. */ diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index fc52aa128b..557831def4 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -26,6 +26,7 @@ #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" +#include "hw/core/tcg-cpu-ops.h" #include "qemu/int128.h" #include "qemu/atomic128.h" #include "trace.h" @@ -35,6 +36,12 @@ #include "hw/boards.h" #endif +#ifdef CONFIG_USER_ONLY +# define user_or_likely(X) true +#else +# define user_or_likely(X) likely(X) +#endif + /*****************************************************************************/ /* Softmmu support */ @@ -51,7 +58,7 @@ static inline bool psw_key_valid(CPUS390XState *env, uint8_t psw_key) if (env->psw.mask & PSW_MASK_PSTATE) { /* PSW key has range 0..15, it is valid if the bit is 1 in the PKM */ - return pkm & (0x80 >> psw_key); + return pkm & (0x8000 >> psw_key); } return true; } @@ -114,19 +121,15 @@ static inline void cpu_stsize_data_ra(CPUS390XState *env, uint64_t addr, typedef struct S390Access { target_ulong vaddr1; target_ulong vaddr2; - char *haddr1; - char *haddr2; + void *haddr1; + void *haddr2; uint16_t size1; uint16_t size2; /* * If we can't access the host page directly, we'll have to do I/O access * via ld/st helpers. These are internal details, so we store the * mmu idx to do the access here instead of passing it around in the - * helpers. Maybe, one day we can get rid of ld/st access - once we can - * handle TLB_NOTDIRTY differently. We don't expect these special accesses - * to trigger exceptions - only if we would have TLB_NOTDIRTY on LAP - * pages, we might trigger a new MMU translation - very unlikely that - * the mapping changes in between and we would trigger a fault. + * helpers. */ int mmu_idx; } S390Access; @@ -138,27 +141,26 @@ typedef struct S390Access { * For !CONFIG_USER_ONLY, the TEC is stored stored to env->tlb_fill_tec. * For CONFIG_USER_ONLY, the faulting address is stored to env->__excp_addr. */ -static int s390_probe_access(CPUArchState *env, target_ulong addr, int size, - MMUAccessType access_type, int mmu_idx, - bool nonfault, void **phost, uintptr_t ra) +static inline int s390_probe_access(CPUArchState *env, target_ulong addr, + int size, MMUAccessType access_type, + int mmu_idx, bool nonfault, + void **phost, uintptr_t ra) { -#if defined(CONFIG_USER_ONLY) - return probe_access_flags(env, addr, access_type, mmu_idx, - nonfault, phost, ra); -#else - int flags; + int flags = probe_access_flags(env, addr, 0, access_type, mmu_idx, + nonfault, phost, ra); - /* - * For !CONFIG_USER_ONLY, we cannot rely on TLB_INVALID_MASK or haddr==NULL - * to detect if there was an exception during tlb_fill(). - */ - env->tlb_fill_exc = 0; - flags = probe_access_flags(env, addr, access_type, mmu_idx, nonfault, phost, - ra); - if (env->tlb_fill_exc) { + if (unlikely(flags & TLB_INVALID_MASK)) { +#ifdef CONFIG_USER_ONLY + /* Address is in TEC in system mode; see s390_cpu_record_sigsegv. */ + env->__excp_addr = addr & TARGET_PAGE_MASK; + return (page_get_flags(addr) & PAGE_VALID + ? PGM_PROTECTION : PGM_ADDRESSING); +#else return env->tlb_fill_exc; +#endif } +#ifndef CONFIG_USER_ONLY if (unlikely(flags & TLB_WATCHPOINT)) { /* S390 does not presently use transaction attributes. */ cpu_check_watchpoint(env_cpu(env), addr, size, @@ -166,8 +168,9 @@ static int s390_probe_access(CPUArchState *env, target_ulong addr, int size, (access_type == MMU_DATA_STORE ? BP_MEM_WRITE : BP_MEM_READ), ra); } - return 0; #endif + + return 0; } static int access_prepare_nf(S390Access *access, CPUS390XState *env, @@ -175,51 +178,46 @@ static int access_prepare_nf(S390Access *access, CPUS390XState *env, MMUAccessType access_type, int mmu_idx, uintptr_t ra) { - void *haddr1, *haddr2 = NULL; int size1, size2, exc; - vaddr vaddr2 = 0; assert(size > 0 && size <= 4096); size1 = MIN(size, -(vaddr1 | TARGET_PAGE_MASK)), size2 = size - size1; + memset(access, 0, sizeof(*access)); + access->vaddr1 = vaddr1; + access->size1 = size1; + access->size2 = size2; + access->mmu_idx = mmu_idx; + exc = s390_probe_access(env, vaddr1, size1, access_type, mmu_idx, nonfault, - &haddr1, ra); - if (exc) { + &access->haddr1, ra); + if (unlikely(exc)) { return exc; } if (unlikely(size2)) { /* The access crosses page boundaries. */ - vaddr2 = wrap_address(env, vaddr1 + size1); + vaddr vaddr2 = wrap_address(env, vaddr1 + size1); + + access->vaddr2 = vaddr2; exc = s390_probe_access(env, vaddr2, size2, access_type, mmu_idx, - nonfault, &haddr2, ra); - if (exc) { + nonfault, &access->haddr2, ra); + if (unlikely(exc)) { return exc; } } - - *access = (S390Access) { - .vaddr1 = vaddr1, - .vaddr2 = vaddr2, - .haddr1 = haddr1, - .haddr2 = haddr2, - .size1 = size1, - .size2 = size2, - .mmu_idx = mmu_idx - }; return 0; } -static S390Access access_prepare(CPUS390XState *env, vaddr vaddr, int size, - MMUAccessType access_type, int mmu_idx, - uintptr_t ra) +static inline void access_prepare(S390Access *ret, CPUS390XState *env, + vaddr vaddr, int size, + MMUAccessType access_type, int mmu_idx, + uintptr_t ra) { - S390Access ret; - int exc = access_prepare_nf(&ret, env, false, vaddr, size, + int exc = access_prepare_nf(ret, env, false, vaddr, size, access_type, mmu_idx, ra); assert(!exc); - return ret; } /* Helper to handle memset on a single page. */ @@ -228,28 +226,14 @@ static void do_access_memset(CPUS390XState *env, vaddr vaddr, char *haddr, uintptr_t ra) { #ifdef CONFIG_USER_ONLY - g_assert(haddr); memset(haddr, byte, size); #else - MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - int i; - if (likely(haddr)) { memset(haddr, byte, size); } else { - /* - * Do a single access and test if we can then get access to the - * page. This is especially relevant to speed up TLB_NOTDIRTY. - */ - g_assert(size > 0); - cpu_stb_mmu(env, vaddr, byte, oi, ra); - haddr = tlb_vaddr_to_host(env, vaddr, MMU_DATA_STORE, mmu_idx); - if (likely(haddr)) { - memset(haddr + 1, byte, size - 1); - } else { - for (i = 1; i < size; i++) { - cpu_stb_mmu(env, vaddr + i, byte, oi, ra); - } + MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); + for (int i = 0; i < size; i++) { + cpu_stb_mmu(env, vaddr + i, byte, oi, ra); } } #endif @@ -268,70 +252,43 @@ static void access_memset(CPUS390XState *env, S390Access *desta, desta->mmu_idx, ra); } -static uint8_t do_access_get_byte(CPUS390XState *env, vaddr vaddr, char **haddr, - int offset, int mmu_idx, uintptr_t ra) -{ -#ifdef CONFIG_USER_ONLY - return ldub_p(*haddr + offset); -#else - MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - uint8_t byte; - - if (likely(*haddr)) { - return ldub_p(*haddr + offset); - } - /* - * Do a single access and test if we can then get access to the - * page. This is especially relevant to speed up TLB_NOTDIRTY. - */ - byte = cpu_ldb_mmu(env, vaddr + offset, oi, ra); - *haddr = tlb_vaddr_to_host(env, vaddr, MMU_DATA_LOAD, mmu_idx); - return byte; -#endif -} - static uint8_t access_get_byte(CPUS390XState *env, S390Access *access, int offset, uintptr_t ra) { - if (offset < access->size1) { - return do_access_get_byte(env, access->vaddr1, &access->haddr1, - offset, access->mmu_idx, ra); - } - return do_access_get_byte(env, access->vaddr2, &access->haddr2, - offset - access->size1, access->mmu_idx, ra); -} + target_ulong vaddr = access->vaddr1; + void *haddr = access->haddr1; -static void do_access_set_byte(CPUS390XState *env, vaddr vaddr, char **haddr, - int offset, uint8_t byte, int mmu_idx, - uintptr_t ra) -{ -#ifdef CONFIG_USER_ONLY - stb_p(*haddr + offset, byte); -#else - MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - - if (likely(*haddr)) { - stb_p(*haddr + offset, byte); - return; + if (unlikely(offset >= access->size1)) { + offset -= access->size1; + vaddr = access->vaddr2; + haddr = access->haddr2; + } + + if (user_or_likely(haddr)) { + return ldub_p(haddr + offset); + } else { + MemOpIdx oi = make_memop_idx(MO_UB, access->mmu_idx); + return cpu_ldb_mmu(env, vaddr + offset, oi, ra); } - /* - * Do a single access and test if we can then get access to the - * page. This is especially relevant to speed up TLB_NOTDIRTY. - */ - cpu_stb_mmu(env, vaddr + offset, byte, oi, ra); - *haddr = tlb_vaddr_to_host(env, vaddr, MMU_DATA_STORE, mmu_idx); -#endif } static void access_set_byte(CPUS390XState *env, S390Access *access, int offset, uint8_t byte, uintptr_t ra) { - if (offset < access->size1) { - do_access_set_byte(env, access->vaddr1, &access->haddr1, offset, byte, - access->mmu_idx, ra); + target_ulong vaddr = access->vaddr1; + void *haddr = access->haddr1; + + if (unlikely(offset >= access->size1)) { + offset -= access->size1; + vaddr = access->vaddr2; + haddr = access->haddr2; + } + + if (user_or_likely(haddr)) { + stb_p(haddr + offset, byte); } else { - do_access_set_byte(env, access->vaddr2, &access->haddr2, - offset - access->size1, byte, access->mmu_idx, ra); + MemOpIdx oi = make_memop_idx(MO_UB, access->mmu_idx); + cpu_stb_mmu(env, vaddr + offset, byte, oi, ra); } } @@ -342,16 +299,17 @@ static void access_set_byte(CPUS390XState *env, S390Access *access, static void access_memmove(CPUS390XState *env, S390Access *desta, S390Access *srca, uintptr_t ra) { + int len = desta->size1 + desta->size2; int diff; - g_assert(desta->size1 + desta->size2 == srca->size1 + srca->size2); + assert(len == srca->size1 + srca->size2); /* Fallback to slow access in case we don't have access to all host pages */ if (unlikely(!desta->haddr1 || (desta->size2 && !desta->haddr2) || !srca->haddr1 || (srca->size2 && !srca->haddr2))) { int i; - for (i = 0; i < desta->size1 + desta->size2; i++) { + for (i = 0; i < len; i++) { uint8_t byte = access_get_byte(env, srca, i, ra); access_set_byte(env, desta, i, byte, ra); @@ -359,20 +317,20 @@ static void access_memmove(CPUS390XState *env, S390Access *desta, return; } - if (srca->size1 == desta->size1) { + diff = desta->size1 - srca->size1; + if (likely(diff == 0)) { memmove(desta->haddr1, srca->haddr1, srca->size1); if (unlikely(srca->size2)) { memmove(desta->haddr2, srca->haddr2, srca->size2); } - } else if (srca->size1 < desta->size1) { - diff = desta->size1 - srca->size1; + } else if (diff > 0) { memmove(desta->haddr1, srca->haddr1, srca->size1); memmove(desta->haddr1 + srca->size1, srca->haddr2, diff); if (likely(desta->size2)) { memmove(desta->haddr2, srca->haddr2 + diff, desta->size2); } } else { - diff = srca->size1 - desta->size1; + diff = -diff; memmove(desta->haddr1, srca->haddr1, desta->size1); memmove(desta->haddr2, srca->haddr1 + desta->size1, diff); if (likely(srca->size2)) { @@ -400,7 +358,7 @@ static int mmu_idx_from_as(uint8_t as) static uint32_t do_helper_nc(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src, uintptr_t ra) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = s390x_env_mmu_index(env, false); S390Access srca1, srca2, desta; uint32_t i; uint8_t c = 0; @@ -411,9 +369,9 @@ static uint32_t do_helper_nc(CPUS390XState *env, uint32_t l, uint64_t dest, /* NC always processes one more byte than specified - maximum is 256 */ l++; - srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + access_prepare(&srca1, env, src, l, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&srca2, env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra); for (i = 0; i < l; i++) { const uint8_t x = access_get_byte(env, &srca1, i, ra) & access_get_byte(env, &srca2, i, ra); @@ -434,7 +392,7 @@ uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest, static uint32_t do_helper_xc(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src, uintptr_t ra) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = s390x_env_mmu_index(env, false); S390Access srca1, srca2, desta; uint32_t i; uint8_t c = 0; @@ -445,9 +403,9 @@ static uint32_t do_helper_xc(CPUS390XState *env, uint32_t l, uint64_t dest, /* XC always processes one more byte than specified - maximum is 256 */ l++; - srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + access_prepare(&srca1, env, src, l, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&srca2, env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra); /* xor with itself is the same as memset(0) */ if (src == dest) { @@ -475,7 +433,7 @@ uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest, static uint32_t do_helper_oc(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src, uintptr_t ra) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = s390x_env_mmu_index(env, false); S390Access srca1, srca2, desta; uint32_t i; uint8_t c = 0; @@ -486,9 +444,9 @@ static uint32_t do_helper_oc(CPUS390XState *env, uint32_t l, uint64_t dest, /* OC always processes one more byte than specified - maximum is 256 */ l++; - srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + access_prepare(&srca1, env, src, l, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&srca2, env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra); for (i = 0; i < l; i++) { const uint8_t x = access_get_byte(env, &srca1, i, ra) | access_get_byte(env, &srca2, i, ra); @@ -509,7 +467,7 @@ uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest, static uint32_t do_helper_mvc(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src, uintptr_t ra) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = s390x_env_mmu_index(env, false); S390Access srca, desta; uint32_t i; @@ -519,8 +477,8 @@ static uint32_t do_helper_mvc(CPUS390XState *env, uint32_t l, uint64_t dest, /* MVC always copies one more byte than specified - maximum is 256 */ l++; - srca = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + access_prepare(&srca, env, src, l, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra); /* * "When the operands overlap, the result is obtained as if the operands @@ -550,16 +508,17 @@ void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) /* move right to left */ void HELPER(mvcrl)(CPUS390XState *env, uint64_t l, uint64_t dest, uint64_t src) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = s390x_env_mmu_index(env, false); const uint64_t ra = GETPC(); S390Access srca, desta; int32_t i; /* MVCRL always copies one more byte than specified - maximum is 256 */ + l &= 0xff; l++; - srca = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + access_prepare(&srca, env, src, l, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra); for (i = l - 1; i >= 0; i--) { uint8_t byte = access_get_byte(env, &srca, i, ra); @@ -570,7 +529,7 @@ void HELPER(mvcrl)(CPUS390XState *env, uint64_t l, uint64_t dest, uint64_t src) /* move inverse */ void HELPER(mvcin)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = s390x_env_mmu_index(env, false); S390Access srca, desta; uintptr_t ra = GETPC(); int i; @@ -579,8 +538,8 @@ void HELPER(mvcin)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) l++; src = wrap_address(env, src - l + 1); - srca = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + access_prepare(&srca, env, src, l, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra); for (i = 0; i < l; i++) { const uint8_t x = access_get_byte(env, &srca, l - i - 1, ra); @@ -591,7 +550,7 @@ void HELPER(mvcin)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) /* move numerics */ void HELPER(mvn)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = s390x_env_mmu_index(env, false); S390Access srca1, srca2, desta; uintptr_t ra = GETPC(); int i; @@ -599,9 +558,9 @@ void HELPER(mvn)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) /* MVN always copies one more byte than specified - maximum is 256 */ l++; - srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + access_prepare(&srca1, env, src, l, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&srca2, env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra); for (i = 0; i < l; i++) { const uint8_t x = (access_get_byte(env, &srca1, i, ra) & 0x0f) | (access_get_byte(env, &srca2, i, ra) & 0xf0); @@ -613,7 +572,7 @@ void HELPER(mvn)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) /* move with offset */ void HELPER(mvo)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = s390x_env_mmu_index(env, false); /* MVO always processes one more byte than specified - maximum is 16 */ const int len_dest = (l >> 4) + 1; const int len_src = (l & 0xf) + 1; @@ -622,8 +581,8 @@ void HELPER(mvo)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) S390Access srca, desta; int i, j; - srca = access_prepare(env, src, len_src, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, len_dest, MMU_DATA_STORE, mmu_idx, ra); + access_prepare(&srca, env, src, len_src, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&desta, env, dest, len_dest, MMU_DATA_STORE, mmu_idx, ra); /* Handle rightmost byte */ byte_dest = cpu_ldub_data_ra(env, dest + len_dest - 1, ra); @@ -647,7 +606,7 @@ void HELPER(mvo)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) /* move zones */ void HELPER(mvz)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = s390x_env_mmu_index(env, false); S390Access srca1, srca2, desta; uintptr_t ra = GETPC(); int i; @@ -655,9 +614,9 @@ void HELPER(mvz)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) /* MVZ always copies one more byte than specified - maximum is 256 */ l++; - srca1 = access_prepare(env, src, l, MMU_DATA_LOAD, mmu_idx, ra); - srca2 = access_prepare(env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, dest, l, MMU_DATA_STORE, mmu_idx, ra); + access_prepare(&srca1, env, src, l, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&srca2, env, dest, l, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra); for (i = 0; i < l; i++) { const uint8_t x = (access_get_byte(env, &srca1, i, ra) & 0xf0) | (access_get_byte(env, &srca2, i, ra) & 0x0f); @@ -708,6 +667,11 @@ uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask, HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1, mask, addr); + if (!mask) { + /* Recognize access exceptions for the first byte */ + probe_read(env, addr, 1, s390x_env_mmu_index(env, false), ra); + } + while (mask) { if (mask & 8) { uint8_t d = cpu_ldub_data_ra(env, addr, ra); @@ -890,7 +854,7 @@ void HELPER(srstu)(CPUS390XState *env, uint32_t r1, uint32_t r2) } /* unsigned string compare (c is string terminator) */ -uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2) +Int128 HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2) { uintptr_t ra = GETPC(); uint32_t len; @@ -908,23 +872,20 @@ uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2) if (v1 == c) { /* Equal. CC=0, and don't advance the registers. */ env->cc_op = 0; - env->retxl = s2; - return s1; + return int128_make128(s2, s1); } } else { /* Unequal. CC={1,2}, and advance the registers. Note that the terminator need not be zero, but the string that contains the terminator is by definition "low". */ env->cc_op = (v1 == c ? 1 : v2 == c ? 2 : v1 < v2 ? 1 : 2); - env->retxl = s2 + len; - return s1 + len; + return int128_make128(s2 + len, s1 + len); } } /* CPU-determined bytes equal; advance the registers. */ env->cc_op = 3; - env->retxl = s2 + len; - return s1 + len; + return int128_make128(s2 + len, s1 + len); } /* move page */ @@ -932,7 +893,7 @@ uint32_t HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint32_t r1, uint32_t r2) { const uint64_t src = get_address(env, r2) & TARGET_PAGE_MASK; const uint64_t dst = get_address(env, r1) & TARGET_PAGE_MASK; - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = s390x_env_mmu_index(env, false); const bool f = extract64(r0, 11, 1); const bool s = extract64(r0, 10, 1); const bool cco = extract64(r0, 8, 1); @@ -985,7 +946,7 @@ inject_exc: /* string copy */ uint32_t HELPER(mvst)(CPUS390XState *env, uint32_t r1, uint32_t r2) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = s390x_env_mmu_index(env, false); const uint64_t d = get_address(env, r1); const uint64_t s = get_address(env, r2); const uint8_t c = env->regs[0]; @@ -1004,8 +965,8 @@ uint32_t HELPER(mvst)(CPUS390XState *env, uint32_t r1, uint32_t r2) * this point). We might over-indicate watchpoints within the pages * (if we ever care, we have to limit processing to a single byte). */ - srca = access_prepare(env, s, len, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, d, len, MMU_DATA_STORE, mmu_idx, ra); + access_prepare(&srca, env, s, len, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&desta, env, d, len, MMU_DATA_STORE, mmu_idx, ra); for (i = 0; i < len; i++) { const uint8_t v = access_get_byte(env, &srca, i, ra); @@ -1066,7 +1027,7 @@ static inline uint32_t do_mvcl(CPUS390XState *env, uint64_t *src, uint64_t *srclen, uint16_t pad, int wordsize, uintptr_t ra) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = s390x_env_mmu_index(env, false); int len = MIN(*destlen, -(*dest | TARGET_PAGE_MASK)); S390Access srca, desta; int i, cc; @@ -1092,19 +1053,19 @@ static inline uint32_t do_mvcl(CPUS390XState *env, len = MIN(MIN(*srclen, -(*src | TARGET_PAGE_MASK)), len); *destlen -= len; *srclen -= len; - srca = access_prepare(env, *src, len, MMU_DATA_LOAD, mmu_idx, ra); - desta = access_prepare(env, *dest, len, MMU_DATA_STORE, mmu_idx, ra); + access_prepare(&srca, env, *src, len, MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&desta, env, *dest, len, MMU_DATA_STORE, mmu_idx, ra); access_memmove(env, &desta, &srca, ra); *src = wrap_address(env, *src + len); *dest = wrap_address(env, *dest + len); } else if (wordsize == 1) { /* Pad the remaining area */ *destlen -= len; - desta = access_prepare(env, *dest, len, MMU_DATA_STORE, mmu_idx, ra); + access_prepare(&desta, env, *dest, len, MMU_DATA_STORE, mmu_idx, ra); access_memset(env, &desta, pad, ra); *dest = wrap_address(env, *dest + len); } else { - desta = access_prepare(env, *dest, len, MMU_DATA_STORE, mmu_idx, ra); + access_prepare(&desta, env, *dest, len, MMU_DATA_STORE, mmu_idx, ra); /* The remaining length selects the padding byte. */ for (i = 0; i < len; (*destlen)--, i++) { @@ -1123,7 +1084,7 @@ static inline uint32_t do_mvcl(CPUS390XState *env, /* move long */ uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2) { - const int mmu_idx = cpu_mmu_index(env, false); + const int mmu_idx = s390x_env_mmu_index(env, false); uintptr_t ra = GETPC(); uint64_t destlen = env->regs[r1 + 1] & 0xffffff; uint64_t dest = get_address(env, r1); @@ -1160,16 +1121,16 @@ uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2) while (destlen) { cur_len = MIN(destlen, -(dest | TARGET_PAGE_MASK)); if (!srclen) { - desta = access_prepare(env, dest, cur_len, MMU_DATA_STORE, mmu_idx, - ra); + access_prepare(&desta, env, dest, cur_len, + MMU_DATA_STORE, mmu_idx, ra); access_memset(env, &desta, pad, ra); } else { cur_len = MIN(MIN(srclen, -(src | TARGET_PAGE_MASK)), cur_len); - srca = access_prepare(env, src, cur_len, MMU_DATA_LOAD, mmu_idx, - ra); - desta = access_prepare(env, dest, cur_len, MMU_DATA_STORE, mmu_idx, - ra); + access_prepare(&srca, env, src, cur_len, + MMU_DATA_LOAD, mmu_idx, ra); + access_prepare(&desta, env, dest, cur_len, + MMU_DATA_STORE, mmu_idx, ra); access_memmove(env, &desta, &srca, ra); src = wrap_address(env, src + cur_len); srclen -= cur_len; @@ -1357,8 +1318,8 @@ uint32_t HELPER(clclu)(CPUS390XState *env, uint32_t r1, uint64_t a2, } /* checksum */ -uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1, - uint64_t src, uint64_t src_len) +Int128 HELPER(cksm)(CPUS390XState *env, uint64_t r1, + uint64_t src, uint64_t src_len) { uintptr_t ra = GETPC(); uint64_t max_len, len; @@ -1399,8 +1360,7 @@ uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1, env->cc_op = (len == src_len ? 0 : 3); /* Return both cksm and processed length. */ - env->retxl = cksm; - return len; + return int128_make128(cksm, len); } void HELPER(pack)(CPUS390XState *env, uint32_t len, uint64_t dest, uint64_t src) @@ -1640,8 +1600,8 @@ void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array, do_helper_tr(env, len, array, trans, GETPC()); } -uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array, - uint64_t len, uint64_t trans) +Int128 HELPER(tre)(CPUS390XState *env, uint64_t array, + uint64_t len, uint64_t trans) { uintptr_t ra = GETPC(); uint8_t end = env->regs[0] & 0xff; @@ -1676,8 +1636,7 @@ uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array, } env->cc_op = cc; - env->retxl = len - i; - return array + i; + return int128_make128(len - i, array + i); } static inline uint32_t do_helper_trt(CPUS390XState *env, int len, @@ -1780,62 +1739,15 @@ uint32_t HELPER(trXX)(CPUS390XState *env, uint32_t r1, uint32_t r2, return cc; } -void HELPER(cdsg)(CPUS390XState *env, uint64_t addr, - uint32_t r1, uint32_t r3) -{ - uintptr_t ra = GETPC(); - Int128 cmpv = int128_make128(env->regs[r1 + 1], env->regs[r1]); - Int128 newv = int128_make128(env->regs[r3 + 1], env->regs[r3]); - Int128 oldv; - uint64_t oldh, oldl; - bool fail; - - check_alignment(env, addr, 16, ra); - - oldh = cpu_ldq_data_ra(env, addr + 0, ra); - oldl = cpu_ldq_data_ra(env, addr + 8, ra); - - oldv = int128_make128(oldl, oldh); - fail = !int128_eq(oldv, cmpv); - if (fail) { - newv = oldv; - } - - cpu_stq_data_ra(env, addr + 0, int128_gethi(newv), ra); - cpu_stq_data_ra(env, addr + 8, int128_getlo(newv), ra); - - env->cc_op = fail; - env->regs[r1] = int128_gethi(oldv); - env->regs[r1 + 1] = int128_getlo(oldv); -} - -void HELPER(cdsg_parallel)(CPUS390XState *env, uint64_t addr, - uint32_t r1, uint32_t r3) -{ - uintptr_t ra = GETPC(); - Int128 cmpv = int128_make128(env->regs[r1 + 1], env->regs[r1]); - Int128 newv = int128_make128(env->regs[r3 + 1], env->regs[r3]); - int mem_idx; - MemOpIdx oi; - Int128 oldv; - bool fail; - - assert(HAVE_CMPXCHG128); - - mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_TE | MO_128 | MO_ALIGN, mem_idx); - oldv = cpu_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra); - fail = !int128_eq(oldv, cmpv); - - env->cc_op = fail; - env->regs[r1] = int128_gethi(oldv); - env->regs[r1 + 1] = int128_getlo(oldv); -} - static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, uint64_t a2, bool parallel) { - uint32_t mem_idx = cpu_mmu_index(env, false); + uint32_t mem_idx = s390x_env_mmu_index(env, false); + MemOpIdx oi16 = make_memop_idx(MO_TE | MO_128, mem_idx); + MemOpIdx oi8 = make_memop_idx(MO_TE | MO_64, mem_idx); + MemOpIdx oi4 = make_memop_idx(MO_TE | MO_32, mem_idx); + MemOpIdx oi2 = make_memop_idx(MO_TE | MO_16, mem_idx); + MemOpIdx oi1 = make_memop_idx(MO_8, mem_idx); uintptr_t ra = GETPC(); uint32_t fc = extract32(env->regs[0], 0, 8); uint32_t sc = extract32(env->regs[0], 8, 8); @@ -1874,34 +1786,30 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, max = 3; #endif if ((HAVE_CMPXCHG128 ? 0 : fc + 2 > max) || - (HAVE_ATOMIC128 ? 0 : sc > max)) { + (HAVE_ATOMIC128_RW ? 0 : sc > max)) { cpu_loop_exit_atomic(env_cpu(env), ra); } } - /* All loads happen before all stores. For simplicity, load the entire - store value area from the parameter list. */ - svh = cpu_ldq_data_ra(env, pl + 16, ra); - svl = cpu_ldq_data_ra(env, pl + 24, ra); + /* + * All loads happen before all stores. For simplicity, load the entire + * store value area from the parameter list. + */ + svh = cpu_ldq_mmu(env, pl + 16, oi8, ra); + svl = cpu_ldq_mmu(env, pl + 24, oi8, ra); switch (fc) { case 0: { - uint32_t nv = cpu_ldl_data_ra(env, pl, ra); + uint32_t nv = cpu_ldl_mmu(env, pl, oi4, ra); uint32_t cv = env->regs[r3]; uint32_t ov; if (parallel) { -#ifdef CONFIG_USER_ONLY - uint32_t *haddr = g2h(env_cpu(env), a1); - ov = qatomic_cmpxchg__nocheck(haddr, cv, nv); -#else - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mem_idx); - ov = cpu_atomic_cmpxchgl_be_mmu(env, a1, cv, nv, oi, ra); -#endif + ov = cpu_atomic_cmpxchgl_be_mmu(env, a1, cv, nv, oi4, ra); } else { - ov = cpu_ldl_data_ra(env, a1, ra); - cpu_stl_data_ra(env, a1, (ov == cv ? nv : ov), ra); + ov = cpu_ldl_mmu(env, a1, oi4, ra); + cpu_stl_mmu(env, a1, (ov == cv ? nv : ov), oi4, ra); } cc = (ov != cv); env->regs[r3] = deposit64(env->regs[r3], 32, 32, ov); @@ -1910,21 +1818,20 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, case 1: { - uint64_t nv = cpu_ldq_data_ra(env, pl, ra); + uint64_t nv = cpu_ldq_mmu(env, pl, oi8, ra); uint64_t cv = env->regs[r3]; uint64_t ov; if (parallel) { #ifdef CONFIG_ATOMIC64 - MemOpIdx oi = make_memop_idx(MO_TEUQ | MO_ALIGN, mem_idx); - ov = cpu_atomic_cmpxchgq_be_mmu(env, a1, cv, nv, oi, ra); + ov = cpu_atomic_cmpxchgq_be_mmu(env, a1, cv, nv, oi8, ra); #else /* Note that we asserted !parallel above. */ g_assert_not_reached(); #endif } else { - ov = cpu_ldq_data_ra(env, a1, ra); - cpu_stq_data_ra(env, a1, (ov == cv ? nv : ov), ra); + ov = cpu_ldq_mmu(env, a1, oi8, ra); + cpu_stq_mmu(env, a1, (ov == cv ? nv : ov), oi8, ra); } cc = (ov != cv); env->regs[r3] = ov; @@ -1933,27 +1840,19 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, case 2: { - uint64_t nvh = cpu_ldq_data_ra(env, pl, ra); - uint64_t nvl = cpu_ldq_data_ra(env, pl + 8, ra); - Int128 nv = int128_make128(nvl, nvh); + Int128 nv = cpu_ld16_mmu(env, pl, oi16, ra); Int128 cv = int128_make128(env->regs[r3 + 1], env->regs[r3]); Int128 ov; if (!parallel) { - uint64_t oh = cpu_ldq_data_ra(env, a1 + 0, ra); - uint64_t ol = cpu_ldq_data_ra(env, a1 + 8, ra); - - ov = int128_make128(ol, oh); + ov = cpu_ld16_mmu(env, a1, oi16, ra); cc = !int128_eq(ov, cv); if (cc) { nv = ov; } - - cpu_stq_data_ra(env, a1 + 0, int128_gethi(nv), ra); - cpu_stq_data_ra(env, a1 + 8, int128_getlo(nv), ra); + cpu_st16_mmu(env, a1, nv, oi16, ra); } else if (HAVE_CMPXCHG128) { - MemOpIdx oi = make_memop_idx(MO_TE | MO_128 | MO_ALIGN, mem_idx); - ov = cpu_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi, ra); + ov = cpu_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi16, ra); cc = !int128_eq(ov, cv); } else { /* Note that we asserted !parallel above. */ @@ -1975,29 +1874,19 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, if (cc == 0) { switch (sc) { case 0: - cpu_stb_data_ra(env, a2, svh >> 56, ra); + cpu_stb_mmu(env, a2, svh >> 56, oi1, ra); break; case 1: - cpu_stw_data_ra(env, a2, svh >> 48, ra); + cpu_stw_mmu(env, a2, svh >> 48, oi2, ra); break; case 2: - cpu_stl_data_ra(env, a2, svh >> 32, ra); + cpu_stl_mmu(env, a2, svh >> 32, oi4, ra); break; case 3: - cpu_stq_data_ra(env, a2, svh, ra); + cpu_stq_mmu(env, a2, svh, oi8, ra); break; case 4: - if (!parallel) { - cpu_stq_data_ra(env, a2 + 0, svh, ra); - cpu_stq_data_ra(env, a2 + 8, svl, ra); - } else if (HAVE_ATOMIC128) { - MemOpIdx oi = make_memop_idx(MO_TEUQ | MO_ALIGN_16, mem_idx); - Int128 sv = int128_make128(svl, svh); - cpu_atomic_sto_be_mmu(env, a2, sv, oi, ra); - } else { - /* Note that we asserted !parallel above. */ - g_assert_not_reached(); - } + cpu_st16_mmu(env, a2, int128_make128(svl, svh), oi16, ra); break; default: g_assert_not_reached(); @@ -2299,7 +2188,8 @@ uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2) return re >> 1; } -uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) +uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2, + uint64_t key) { const uint8_t psw_as = (env->psw.mask & PSW_MASK_ASC) >> PSW_SHIFT_ASC; S390Access srca, desta; @@ -2314,6 +2204,10 @@ uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) s390_program_interrupt(env, PGM_SPECIAL_OP, ra); } + if (!psw_key_valid(env, (key >> 4) & 0xf)) { + s390_program_interrupt(env, PGM_PRIVILEGED, ra); + } + l = wrap_length32(env, l); if (l > 256) { /* max 256 */ @@ -2323,14 +2217,14 @@ uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) return cc; } - /* TODO: Access key handling */ - srca = access_prepare(env, a2, l, MMU_DATA_LOAD, MMU_PRIMARY_IDX, ra); - desta = access_prepare(env, a1, l, MMU_DATA_STORE, MMU_SECONDARY_IDX, ra); + access_prepare(&srca, env, a2, l, MMU_DATA_LOAD, MMU_PRIMARY_IDX, ra); + access_prepare(&desta, env, a1, l, MMU_DATA_STORE, MMU_SECONDARY_IDX, ra); access_memmove(env, &desta, &srca, ra); return cc; } -uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) +uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2, + uint64_t key) { const uint8_t psw_as = (env->psw.mask & PSW_MASK_ASC) >> PSW_SHIFT_ASC; S390Access srca, desta; @@ -2345,6 +2239,10 @@ uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) s390_program_interrupt(env, PGM_SPECIAL_OP, ra); } + if (!psw_key_valid(env, (key >> 4) & 0xf)) { + s390_program_interrupt(env, PGM_PRIVILEGED, ra); + } + l = wrap_length32(env, l); if (l > 256) { /* max 256 */ @@ -2353,10 +2251,8 @@ uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) } else if (!l) { return cc; } - - /* TODO: Access key handling */ - srca = access_prepare(env, a2, l, MMU_DATA_LOAD, MMU_SECONDARY_IDX, ra); - desta = access_prepare(env, a1, l, MMU_DATA_STORE, MMU_PRIMARY_IDX, ra); + access_prepare(&srca, env, a2, l, MMU_DATA_LOAD, MMU_SECONDARY_IDX, ra); + access_prepare(&desta, env, a1, l, MMU_DATA_STORE, MMU_PRIMARY_IDX, ra); access_memmove(env, &desta, &srca, ra); return cc; } @@ -2465,7 +2361,7 @@ void HELPER(purge)(CPUS390XState *env) } /* load real address */ -uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr) +uint64_t HELPER(lra)(CPUS390XState *env, uint64_t r1, uint64_t addr) { uint64_t asc = env->psw.mask & PSW_MASK_ASC; uint64_t ret, tec; @@ -2479,7 +2375,7 @@ uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr) exc = mmu_translate(env, addr, MMU_S390_LRA, asc, &ret, &flags, &tec); if (exc) { cc = 3; - ret = exc | 0x80000000; + ret = (r1 & 0xFFFFFFFF00000000ULL) | exc | 0x80000000; } else { cc = 0; ret |= addr & ~TARGET_PAGE_MASK; @@ -2490,67 +2386,6 @@ uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr) } #endif -/* load pair from quadword */ -uint64_t HELPER(lpq)(CPUS390XState *env, uint64_t addr) -{ - uintptr_t ra = GETPC(); - uint64_t hi, lo; - - check_alignment(env, addr, 16, ra); - hi = cpu_ldq_data_ra(env, addr + 0, ra); - lo = cpu_ldq_data_ra(env, addr + 8, ra); - - env->retxl = lo; - return hi; -} - -uint64_t HELPER(lpq_parallel)(CPUS390XState *env, uint64_t addr) -{ - uintptr_t ra = GETPC(); - uint64_t hi, lo; - int mem_idx; - MemOpIdx oi; - Int128 v; - - assert(HAVE_ATOMIC128); - - mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_TEUQ | MO_ALIGN_16, mem_idx); - v = cpu_atomic_ldo_be_mmu(env, addr, oi, ra); - hi = int128_gethi(v); - lo = int128_getlo(v); - - env->retxl = lo; - return hi; -} - -/* store pair to quadword */ -void HELPER(stpq)(CPUS390XState *env, uint64_t addr, - uint64_t low, uint64_t high) -{ - uintptr_t ra = GETPC(); - - check_alignment(env, addr, 16, ra); - cpu_stq_data_ra(env, addr + 0, high, ra); - cpu_stq_data_ra(env, addr + 8, low, ra); -} - -void HELPER(stpq_parallel)(CPUS390XState *env, uint64_t addr, - uint64_t low, uint64_t high) -{ - uintptr_t ra = GETPC(); - int mem_idx; - MemOpIdx oi; - Int128 v; - - assert(HAVE_ATOMIC128); - - mem_idx = cpu_mmu_index(env, false); - oi = make_memop_idx(MO_TEUQ | MO_ALIGN_16, mem_idx); - v = int128_make128(low, high); - cpu_atomic_sto_be_mmu(env, addr, v, oi, ra); -} - /* Execute instruction. This instruction executes an insn modified with the contents of r1. It does not change the executed instruction in memory; it does not change the program counter. @@ -2560,8 +2395,16 @@ void HELPER(stpq_parallel)(CPUS390XState *env, uint64_t addr, */ void HELPER(ex)(CPUS390XState *env, uint32_t ilen, uint64_t r1, uint64_t addr) { - uint64_t insn = cpu_lduw_code(env, addr); - uint8_t opc = insn >> 8; + uint64_t insn; + uint8_t opc; + + /* EXECUTE targets must be at even addresses. */ + if (addr & 1) { + tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC()); + } + + insn = cpu_lduw_code(env, addr); + opc = insn >> 8; /* Or in the contents of R1[56:63]. */ insn |= r1 & 0xff; @@ -2622,6 +2465,7 @@ void HELPER(ex)(CPUS390XState *env, uint32_t ilen, uint64_t r1, uint64_t addr) that ex_value is non-zero, which flags that we are in a state that requires such execution. */ env->ex_value = insn | ilen; + env->ex_target = addr; } uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src, @@ -2697,10 +2541,12 @@ uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src, /* FIXME: Access using correct keys and AR-mode */ if (len) { - S390Access srca = access_prepare(env, src, len, MMU_DATA_LOAD, - mmu_idx_from_as(src_as), ra); - S390Access desta = access_prepare(env, dest, len, MMU_DATA_STORE, - mmu_idx_from_as(dest_as), ra); + S390Access srca, desta; + + access_prepare(&srca, env, src, len, MMU_DATA_LOAD, + mmu_idx_from_as(src_as), ra); + access_prepare(&desta, env, dest, len, MMU_DATA_STORE, + mmu_idx_from_as(dest_as), ra); access_memmove(env, &desta, &srca, ra); } @@ -3021,12 +2867,14 @@ uint32_t HELPER(cu42)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3) void probe_write_access(CPUS390XState *env, uint64_t addr, uint64_t len, uintptr_t ra) { + const int mmu_idx = s390x_env_mmu_index(env, false); + /* test the actual access, not just any access to the page due to LAP */ while (len) { const uint64_t pagelen = -(addr | TARGET_PAGE_MASK); const uint64_t curlen = MIN(pagelen, len); - probe_write(env, addr, curlen, cpu_mmu_index(env, false), ra); + probe_write(env, addr, curlen, mmu_idx, ra); addr = wrap_address(env, addr + curlen); len -= curlen; } diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index aab9c47747..8764846ce8 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -20,10 +20,8 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -#include "qemu/main-loop.h" #include "cpu.h" #include "s390x-internal.h" -#include "exec/memory.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "qemu/timer.h" @@ -103,9 +101,9 @@ uint64_t HELPER(stck)(CPUS390XState *env) /* SCLP service call */ uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2) { - qemu_mutex_lock_iothread(); - int r = sclp_service_call(env, r1, r2); - qemu_mutex_unlock_iothread(); + bql_lock(); + int r = sclp_service_call(env_archcpu(env), r1, r2); + bql_unlock(); if (r < 0) { tcg_s390_program_interrupt(env, -r, GETPC()); } @@ -119,9 +117,9 @@ void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num) switch (num) { case 0x500: /* KVM hypercall */ - qemu_mutex_lock_iothread(); + bql_lock(); r = s390_virtio_hypercall(env); - qemu_mutex_unlock_iothread(); + bql_unlock(); break; case 0x44: /* yield */ @@ -129,9 +127,9 @@ void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num) break; case 0x308: /* ipl */ - qemu_mutex_lock_iothread(); + bql_lock(); handle_diag_308(env, r1, r3, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); r = 0; break; case 0x288: @@ -158,6 +156,13 @@ void HELPER(spx)(CPUS390XState *env, uint64_t a1) if (prefix == old_prefix) { return; } + /* + * Since prefix got aligned to 8k and memory increments are a multiple of + * 8k checking the first page is sufficient + */ + if (!mmu_absolute_addr_valid(prefix, true)) { + tcg_s390_program_interrupt(env, PGM_ADDRESSING, GETPC()); + } env->psa = prefix; HELPER_LOG("prefix: %#x\n", prefix); @@ -180,7 +185,7 @@ static void update_ckc_timer(CPUS390XState *env) /* stop the timer and remove pending CKC IRQs */ timer_del(env->tod_timer); - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); env->pending_int &= ~INTERRUPT_EXT_CLOCK_COMPARATOR; /* the tod has to exceed the ckc, this can never happen if ckc is all 1's */ @@ -202,16 +207,14 @@ void HELPER(sckc)(CPUS390XState *env, uint64_t ckc) { env->ckc = ckc; - qemu_mutex_lock_iothread(); + bql_lock(); update_ckc_timer(env); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque) { - S390CPU *cpu = S390_CPU(cs); - - update_ckc_timer(&cpu->env); + update_ckc_timer(cpu_env(cs)); } /* Set Clock */ @@ -224,9 +227,9 @@ uint32_t HELPER(sck)(CPUS390XState *env, uint64_t tod_low) .low = tod_low, }; - qemu_mutex_lock_iothread(); + bql_lock(); tdc->set(td, &tod, &error_abort); - qemu_mutex_unlock_iothread(); + bql_unlock(); return 0; } @@ -326,7 +329,7 @@ uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint64_t r0, uint64_t r1) /* same as machine type number in STORE CPU ID, but in EBCDIC */ snprintf(type, ARRAY_SIZE(type), "%X", cpu->model->def->type); ebcdic_put(sysib.sysib_111.type, type, 4); - /* model number (not stored in STORE CPU ID for z/Architecure) */ + /* model number (not stored in STORE CPU ID for z/Architecture) */ ebcdic_put(sysib.sysib_111.model, "QEMU ", 16); ebcdic_put(sysib.sysib_111.sequence, "QEMU ", 16); ebcdic_put(sysib.sysib_111.plant, "QEMU", 4); @@ -416,9 +419,9 @@ uint32_t HELPER(sigp)(CPUS390XState *env, uint64_t order_code, uint32_t r1, int cc; /* TODO: needed to inject interrupts - push further down */ - qemu_mutex_lock_iothread(); + bql_lock(); cc = handle_sigp(env, order_code & SIGP_ORDER_MASK, r1, r3); - qemu_mutex_unlock_iothread(); + bql_unlock(); return cc; } @@ -428,92 +431,92 @@ uint32_t HELPER(sigp)(CPUS390XState *env, uint64_t order_code, uint32_t r1, void HELPER(xsch)(CPUS390XState *env, uint64_t r1) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); ioinst_handle_xsch(cpu, r1, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(csch)(CPUS390XState *env, uint64_t r1) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); ioinst_handle_csch(cpu, r1, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(hsch)(CPUS390XState *env, uint64_t r1) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); ioinst_handle_hsch(cpu, r1, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(msch)(CPUS390XState *env, uint64_t r1, uint64_t inst) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); ioinst_handle_msch(cpu, r1, inst >> 16, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(rchp)(CPUS390XState *env, uint64_t r1) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); ioinst_handle_rchp(cpu, r1, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(rsch)(CPUS390XState *env, uint64_t r1) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); ioinst_handle_rsch(cpu, r1, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(sal)(CPUS390XState *env, uint64_t r1) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); ioinst_handle_sal(cpu, r1, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(schm)(CPUS390XState *env, uint64_t r1, uint64_t r2, uint64_t inst) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); ioinst_handle_schm(cpu, r1, r2, inst >> 16, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(ssch)(CPUS390XState *env, uint64_t r1, uint64_t inst) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); ioinst_handle_ssch(cpu, r1, inst >> 16, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(stcrw)(CPUS390XState *env, uint64_t inst) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); ioinst_handle_stcrw(cpu, inst >> 16, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(stsch)(CPUS390XState *env, uint64_t r1, uint64_t inst) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); ioinst_handle_stsch(cpu, r1, inst >> 16, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } uint32_t HELPER(tpi)(CPUS390XState *env, uint64_t addr) @@ -528,10 +531,10 @@ uint32_t HELPER(tpi)(CPUS390XState *env, uint64_t addr) tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra); } - qemu_mutex_lock_iothread(); + bql_lock(); io = qemu_s390_flic_dequeue_io(flic, env->cregs[6]); if (!io) { - qemu_mutex_unlock_iothread(); + bql_unlock(); return 0; } @@ -549,7 +552,7 @@ uint32_t HELPER(tpi)(CPUS390XState *env, uint64_t addr) if (s390_cpu_virt_mem_write(cpu, addr, 0, &intc, sizeof(intc))) { /* writing failed, reinject and properly clean up */ s390_io_interrupt(io->id, io->nr, io->parm, io->word); - qemu_mutex_unlock_iothread(); + bql_unlock(); g_free(io); s390_cpu_virt_mem_handle_exc(cpu, ra); return 0; @@ -565,24 +568,24 @@ uint32_t HELPER(tpi)(CPUS390XState *env, uint64_t addr) } g_free(io); - qemu_mutex_unlock_iothread(); + bql_unlock(); return 1; } void HELPER(tsch)(CPUS390XState *env, uint64_t r1, uint64_t inst) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); ioinst_handle_tsch(cpu, r1, inst >> 16, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(chsc)(CPUS390XState *env, uint64_t inst) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); ioinst_handle_chsc(cpu, inst >> 16, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } #endif @@ -721,27 +724,27 @@ void HELPER(clp)(CPUS390XState *env, uint32_t r2) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); clp_service_call(cpu, r2, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(pcilg)(CPUS390XState *env, uint32_t r1, uint32_t r2) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); pcilg_service_call(cpu, r1, r2, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(pcistg)(CPUS390XState *env, uint32_t r1, uint32_t r2) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); pcistg_service_call(cpu, r1, r2, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(stpcifc)(CPUS390XState *env, uint32_t r1, uint64_t fiba, @@ -749,18 +752,19 @@ void HELPER(stpcifc)(CPUS390XState *env, uint32_t r1, uint64_t fiba, { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); stpcifc_service_call(cpu, r1, fiba, ar, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(sic)(CPUS390XState *env, uint64_t r1, uint64_t r3) { + S390CPU *cpu = env_archcpu(env); int r; - qemu_mutex_lock_iothread(); - r = css_do_sic(env, (r3 >> 27) & 0x7, r1 & 0xffff); - qemu_mutex_unlock_iothread(); + bql_lock(); + r = css_do_sic(cpu, (r3 >> 27) & 0x7, r1 & 0xffff); + bql_unlock(); /* css_do_sic() may actually return a PGM_xxx value to inject */ if (r) { tcg_s390_program_interrupt(env, -r, GETPC()); @@ -771,9 +775,9 @@ void HELPER(rpcit)(CPUS390XState *env, uint32_t r1, uint32_t r2) { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); rpcit_service_call(cpu, r1, r2, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(pcistb)(CPUS390XState *env, uint32_t r1, uint32_t r3, @@ -781,9 +785,9 @@ void HELPER(pcistb)(CPUS390XState *env, uint32_t r1, uint32_t r3, { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); pcistb_service_call(cpu, r1, r3, gaddr, ar, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(mpcifc)(CPUS390XState *env, uint32_t r1, uint64_t fiba, @@ -791,8 +795,8 @@ void HELPER(mpcifc)(CPUS390XState *env, uint32_t r1, uint64_t fiba, { S390CPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); mpcifc_service_call(cpu, r1, fiba, ar, GETPC()); - qemu_mutex_unlock_iothread(); + bql_unlock(); } #endif diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index fd2433d625..90a74ee795 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -38,7 +38,6 @@ #include "qemu/log.h" #include "qemu/host-utils.h" #include "exec/cpu_ldst.h" -#include "exec/gen-icount.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -46,6 +45,10 @@ #include "exec/log.h" #include "qemu/atomic128.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + /* Information that (most) every instruction needs to manipulate. */ typedef struct DisasContext DisasContext; @@ -138,7 +141,6 @@ struct DisasFields { struct DisasContext { DisasContextBase base; const DisasInsn *insn; - TCGOp *insn_start; DisasFields fields; uint64_t ex_value; /* @@ -149,14 +151,13 @@ struct DisasContext { uint64_t pc_tmp; uint32_t ilen; enum cc_op cc_op; + bool exit_to_mainloop; }; /* Information carried about a condition to be evaluated. */ typedef struct { TCGCond cond:8; bool is_64; - bool g1; - bool g2; union { struct { TCGv_i64 a, b; } s64; struct { TCGv_i32 a, b; } s32; @@ -170,8 +171,6 @@ static uint64_t inline_branch_miss[CC_OP_MAX]; static void pc_to_link_info(TCGv_i64 out, DisasContext *s, uint64_t pc) { - TCGv_i64 tmp; - if (s->base.tb->flags & FLAG_MASK_32) { if (s->base.tb->flags & FLAG_MASK_64) { tcg_gen_movi_i64(out, pc); @@ -180,9 +179,7 @@ static void pc_to_link_info(TCGv_i64 out, DisasContext *s, uint64_t pc) pc |= 0x80000000; } assert(!(s->base.tb->flags & FLAG_MASK_64)); - tmp = tcg_const_i64(pc); - tcg_gen_deposit_i64(out, out, tmp, 0, 32); - tcg_temp_free_i64(tmp); + tcg_gen_deposit_i64(out, out, tcg_constant_i64(pc), 0, 32); } static TCGv_i64 psw_addr; @@ -201,28 +198,28 @@ void s390x_translate_init(void) { int i; - psw_addr = tcg_global_mem_new_i64(cpu_env, + psw_addr = tcg_global_mem_new_i64(tcg_env, offsetof(CPUS390XState, psw.addr), "psw_addr"); - psw_mask = tcg_global_mem_new_i64(cpu_env, + psw_mask = tcg_global_mem_new_i64(tcg_env, offsetof(CPUS390XState, psw.mask), "psw_mask"); - gbea = tcg_global_mem_new_i64(cpu_env, + gbea = tcg_global_mem_new_i64(tcg_env, offsetof(CPUS390XState, gbea), "gbea"); - cc_op = tcg_global_mem_new_i32(cpu_env, offsetof(CPUS390XState, cc_op), + cc_op = tcg_global_mem_new_i32(tcg_env, offsetof(CPUS390XState, cc_op), "cc_op"); - cc_src = tcg_global_mem_new_i64(cpu_env, offsetof(CPUS390XState, cc_src), + cc_src = tcg_global_mem_new_i64(tcg_env, offsetof(CPUS390XState, cc_src), "cc_src"); - cc_dst = tcg_global_mem_new_i64(cpu_env, offsetof(CPUS390XState, cc_dst), + cc_dst = tcg_global_mem_new_i64(tcg_env, offsetof(CPUS390XState, cc_dst), "cc_dst"); - cc_vr = tcg_global_mem_new_i64(cpu_env, offsetof(CPUS390XState, cc_vr), + cc_vr = tcg_global_mem_new_i64(tcg_env, offsetof(CPUS390XState, cc_vr), "cc_vr"); for (i = 0; i < 16; i++) { snprintf(cpu_reg_names[i], sizeof(cpu_reg_names[0]), "r%d", i); - regs[i] = tcg_global_mem_new(cpu_env, + regs[i] = tcg_global_mem_new(tcg_env, offsetof(CPUS390XState, regs[i]), cpu_reg_names[i]); } @@ -292,7 +289,7 @@ static TCGv_i64 load_freg(int reg) { TCGv_i64 r = tcg_temp_new_i64(); - tcg_gen_ld_i64(r, cpu_env, freg64_offset(reg)); + tcg_gen_ld_i64(r, tcg_env, freg64_offset(reg)); return r; } @@ -300,7 +297,17 @@ static TCGv_i64 load_freg32_i64(int reg) { TCGv_i64 r = tcg_temp_new_i64(); - tcg_gen_ld32u_i64(r, cpu_env, freg32_offset(reg)); + tcg_gen_ld32u_i64(r, tcg_env, freg32_offset(reg)); + return r; +} + +static TCGv_i128 load_freg_128(int reg) +{ + TCGv_i64 h = load_freg(reg); + TCGv_i64 l = load_freg(reg + 2); + TCGv_i128 r = tcg_temp_new_i128(); + + tcg_gen_concat_i64_i128(r, l, h); return r; } @@ -311,7 +318,7 @@ static void store_reg(int reg, TCGv_i64 v) static void store_freg(int reg, TCGv_i64 v) { - tcg_gen_st_i64(v, cpu_env, freg64_offset(reg)); + tcg_gen_st_i64(v, tcg_env, freg64_offset(reg)); } static void store_reg32_i64(int reg, TCGv_i64 v) @@ -327,12 +334,7 @@ static void store_reg32h_i64(int reg, TCGv_i64 v) static void store_freg32_i64(int reg, TCGv_i64 v) { - tcg_gen_st32_i64(v, cpu_env, freg32_offset(reg)); -} - -static void return_low128(TCGv_i64 dest) -{ - tcg_gen_ld_i64(dest, cpu_env, offsetof(CPUS390XState, retxl)); + tcg_gen_st32_i64(v, tcg_env, freg32_offset(reg)); } static void update_psw_addr(DisasContext *s) @@ -347,11 +349,8 @@ static void per_branch(DisasContext *s, bool to_next) tcg_gen_movi_i64(gbea, s->base.pc_next); if (s->base.tb->flags & FLAG_MASK_PER) { - TCGv_i64 next_pc = to_next ? tcg_const_i64(s->pc_tmp) : psw_addr; - gen_helper_per_branch(cpu_env, gbea, next_pc); - if (to_next) { - tcg_temp_free_i64(next_pc); - } + TCGv_i64 next_pc = to_next ? tcg_constant_i64(s->pc_tmp) : psw_addr; + gen_helper_per_branch(tcg_env, gbea, next_pc); } #endif } @@ -365,13 +364,12 @@ static void per_branch_cond(DisasContext *s, TCGCond cond, tcg_gen_brcond_i64(tcg_invert_cond(cond), arg1, arg2, lab); tcg_gen_movi_i64(gbea, s->base.pc_next); - gen_helper_per_branch(cpu_env, gbea, psw_addr); + gen_helper_per_branch(tcg_env, gbea, psw_addr); gen_set_label(lab); } else { - TCGv_i64 pc = tcg_const_i64(s->base.pc_next); + TCGv_i64 pc = tcg_constant_i64(s->base.pc_next); tcg_gen_movcond_i64(cond, gbea, arg1, arg2, gbea, pc); - tcg_temp_free_i64(pc); } #endif } @@ -417,7 +415,7 @@ static int get_mem_index(DisasContext *s) case PSW_ASC_HOME >> FLAG_MASK_PSW_SHIFT: return MMU_HOME_IDX; default: - tcg_abort(); + g_assert_not_reached(); break; } #endif @@ -425,23 +423,17 @@ static int get_mem_index(DisasContext *s) static void gen_exception(int excp) { - TCGv_i32 tmp = tcg_const_i32(excp); - gen_helper_exception(cpu_env, tmp); - tcg_temp_free_i32(tmp); + gen_helper_exception(tcg_env, tcg_constant_i32(excp)); } static void gen_program_exception(DisasContext *s, int code) { - TCGv_i32 tmp; + /* Remember what pgm exception this was. */ + tcg_gen_st_i32(tcg_constant_i32(code), tcg_env, + offsetof(CPUS390XState, int_pgm_code)); - /* Remember what pgm exeption this was. */ - tmp = tcg_const_i32(code); - tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUS390XState, int_pgm_code)); - tcg_temp_free_i32(tmp); - - tmp = tcg_const_i32(s->ilen); - tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUS390XState, int_pgm_ilen)); - tcg_temp_free_i32(tmp); + tcg_gen_st_i32(tcg_constant_i32(s->ilen), tcg_env, + offsetof(CPUS390XState, int_pgm_ilen)); /* update the psw */ update_psw_addr(s); @@ -460,9 +452,7 @@ static inline void gen_illegal_opcode(DisasContext *s) static inline void gen_data_exception(uint8_t dxc) { - TCGv_i32 tmp = tcg_const_i32(dxc); - gen_helper_data_exception(cpu_env, tmp); - tcg_temp_free_i32(tmp); + gen_helper_data_exception(tcg_env, tcg_constant_i32(dxc)); } static inline void gen_trap(DisasContext *s) @@ -490,7 +480,7 @@ static TCGv_i64 get_address(DisasContext *s, int x2, int b2, int d2) /* * Note that d2 is limited to 20 bits, signed. If we crop negative - * displacements early we create larger immedate addends. + * displacements early we create larger immediate addends. */ if (b2 && x2) { tcg_gen_add_i64(tmp, regs[b2], regs[x2]); @@ -583,13 +573,13 @@ static void gen_op_calc_cc(DisasContext *s) switch (s->cc_op) { default: - dummy = tcg_const_i64(0); + dummy = tcg_constant_i64(0); /* FALLTHRU */ case CC_OP_ADD_64: case CC_OP_SUB_64: case CC_OP_ADD_32: case CC_OP_SUB_32: - local_cc_op = tcg_const_i32(s->cc_op); + local_cc_op = tcg_constant_i32(s->cc_op); break; case CC_OP_CONST0: case CC_OP_CONST1: @@ -612,6 +602,9 @@ static void gen_op_calc_cc(DisasContext *s) /* env->cc_op already is the cc value */ break; case CC_OP_NZ: + tcg_gen_setcondi_i64(TCG_COND_NE, cc_dst, cc_dst, 0); + tcg_gen_extrl_i64_i32(cc_op, cc_dst); + break; case CC_OP_ABS_64: case CC_OP_NABS_64: case CC_OP_ABS_32: @@ -626,7 +619,7 @@ static void gen_op_calc_cc(DisasContext *s) case CC_OP_LCBB: case CC_OP_MULS_32: /* 1 argument */ - gen_helper_calc_cc(cc_op, cpu_env, local_cc_op, dummy, cc_dst, dummy); + gen_helper_calc_cc(cc_op, tcg_env, local_cc_op, dummy, cc_dst, dummy); break; case CC_OP_ADDU: case CC_OP_ICM: @@ -642,28 +635,21 @@ static void gen_op_calc_cc(DisasContext *s) case CC_OP_VC: case CC_OP_MULS_64: /* 2 arguments */ - gen_helper_calc_cc(cc_op, cpu_env, local_cc_op, cc_src, cc_dst, dummy); + gen_helper_calc_cc(cc_op, tcg_env, local_cc_op, cc_src, cc_dst, dummy); break; case CC_OP_ADD_64: case CC_OP_SUB_64: case CC_OP_ADD_32: case CC_OP_SUB_32: /* 3 arguments */ - gen_helper_calc_cc(cc_op, cpu_env, local_cc_op, cc_src, cc_dst, cc_vr); + gen_helper_calc_cc(cc_op, tcg_env, local_cc_op, cc_src, cc_dst, cc_vr); break; case CC_OP_DYNAMIC: /* unknown operation - assume 3 arguments and cc_op in env */ - gen_helper_calc_cc(cc_op, cpu_env, cc_op, cc_src, cc_dst, cc_vr); + gen_helper_calc_cc(cc_op, tcg_env, cc_op, cc_src, cc_dst, cc_vr); break; default: - tcg_abort(); - } - - if (local_cc_op) { - tcg_temp_free_i32(local_cc_op); - } - if (dummy) { - tcg_temp_free_i64(dummy); + g_assert_not_reached(); } /* We now have cc in cc_op as constant */ @@ -729,7 +715,6 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) c->cond = (mask ? TCG_COND_ALWAYS : TCG_COND_NEVER); c->u.s32.a = cc_op; c->u.s32.b = cc_op; - c->g1 = c->g2 = true; c->is_64 = false; return; } @@ -768,10 +753,10 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) case CC_OP_TM_64: switch (mask) { case 8: - cond = TCG_COND_EQ; + cond = TCG_COND_TSTEQ; break; case 4 | 2 | 1: - cond = TCG_COND_NE; + cond = TCG_COND_TSTNE; break; default: goto do_dynamic; @@ -782,11 +767,11 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) case CC_OP_ICM: switch (mask) { case 8: - cond = TCG_COND_EQ; + cond = TCG_COND_TSTEQ; break; case 4 | 2 | 1: case 4 | 2: - cond = TCG_COND_NE; + cond = TCG_COND_TSTNE; break; default: goto do_dynamic; @@ -846,13 +831,12 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) /* Load up the arguments of the comparison. */ c->is_64 = true; - c->g1 = c->g2 = false; switch (old_cc_op) { case CC_OP_LTGT0_32: c->is_64 = false; c->u.s32.a = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(c->u.s32.a, cc_dst); - c->u.s32.b = tcg_const_i32(0); + c->u.s32.b = tcg_constant_i32(0); break; case CC_OP_LTGT_32: case CC_OP_LTUGTU_32: @@ -867,29 +851,22 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) case CC_OP_NZ: case CC_OP_FLOGR: c->u.s64.a = cc_dst; - c->u.s64.b = tcg_const_i64(0); - c->g1 = true; - break; - case CC_OP_LTGT_64: - case CC_OP_LTUGTU_64: - c->u.s64.a = cc_src; - c->u.s64.b = cc_dst; - c->g1 = c->g2 = true; + c->u.s64.b = tcg_constant_i64(0); break; + case CC_OP_LTGT_64: + case CC_OP_LTUGTU_64: case CC_OP_TM_32: case CC_OP_TM_64: case CC_OP_ICM: - c->u.s64.a = tcg_temp_new_i64(); - c->u.s64.b = tcg_const_i64(0); - tcg_gen_and_i64(c->u.s64.a, cc_src, cc_dst); + c->u.s64.a = cc_src; + c->u.s64.b = cc_dst; break; case CC_OP_ADDU: case CC_OP_SUBU: c->is_64 = true; - c->u.s64.b = tcg_const_i64(0); - c->g1 = true; + c->u.s64.b = tcg_constant_i64(0); switch (mask) { case 8 | 2: case 4 | 1: /* result */ @@ -907,71 +884,45 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) case CC_OP_STATIC: c->is_64 = false; c->u.s32.a = cc_op; - c->g1 = true; - switch (mask) { - case 0x8 | 0x4 | 0x2: /* cc != 3 */ - cond = TCG_COND_NE; - c->u.s32.b = tcg_const_i32(3); - break; - case 0x8 | 0x4 | 0x1: /* cc != 2 */ - cond = TCG_COND_NE; - c->u.s32.b = tcg_const_i32(2); - break; - case 0x8 | 0x2 | 0x1: /* cc != 1 */ - cond = TCG_COND_NE; - c->u.s32.b = tcg_const_i32(1); - break; - case 0x8 | 0x2: /* cc == 0 || cc == 2 => (cc & 1) == 0 */ + + /* Fold half of the cases using bit 3 to invert. */ + switch (mask & 8 ? mask ^ 0xf : mask) { + case 0x1: /* cc == 3 */ cond = TCG_COND_EQ; - c->g1 = false; - c->u.s32.a = tcg_temp_new_i32(); - c->u.s32.b = tcg_const_i32(0); - tcg_gen_andi_i32(c->u.s32.a, cc_op, 1); - break; - case 0x8 | 0x4: /* cc < 2 */ - cond = TCG_COND_LTU; - c->u.s32.b = tcg_const_i32(2); - break; - case 0x8: /* cc == 0 */ - cond = TCG_COND_EQ; - c->u.s32.b = tcg_const_i32(0); - break; - case 0x4 | 0x2 | 0x1: /* cc != 0 */ - cond = TCG_COND_NE; - c->u.s32.b = tcg_const_i32(0); - break; - case 0x4 | 0x1: /* cc == 1 || cc == 3 => (cc & 1) != 0 */ - cond = TCG_COND_NE; - c->g1 = false; - c->u.s32.a = tcg_temp_new_i32(); - c->u.s32.b = tcg_const_i32(0); - tcg_gen_andi_i32(c->u.s32.a, cc_op, 1); - break; - case 0x4: /* cc == 1 */ - cond = TCG_COND_EQ; - c->u.s32.b = tcg_const_i32(1); - break; - case 0x2 | 0x1: /* cc > 1 */ - cond = TCG_COND_GTU; - c->u.s32.b = tcg_const_i32(1); + c->u.s32.b = tcg_constant_i32(3); break; case 0x2: /* cc == 2 */ cond = TCG_COND_EQ; - c->u.s32.b = tcg_const_i32(2); + c->u.s32.b = tcg_constant_i32(2); break; - case 0x1: /* cc == 3 */ + case 0x4: /* cc == 1 */ cond = TCG_COND_EQ; - c->u.s32.b = tcg_const_i32(3); + c->u.s32.b = tcg_constant_i32(1); + break; + case 0x2 | 0x1: /* cc == 2 || cc == 3 => cc > 1 */ + cond = TCG_COND_GTU; + c->u.s32.b = tcg_constant_i32(1); + break; + case 0x4 | 0x1: /* cc == 1 || cc == 3 => (cc & 1) != 0 */ + cond = TCG_COND_TSTNE; + c->u.s32.b = tcg_constant_i32(1); + break; + case 0x4 | 0x2: /* cc == 1 || cc == 2 => (cc - 1) <= 1 */ + cond = TCG_COND_LEU; + c->u.s32.a = tcg_temp_new_i32(); + c->u.s32.b = tcg_constant_i32(1); + tcg_gen_addi_i32(c->u.s32.a, cc_op, -1); + break; + case 0x4 | 0x2 | 0x1: /* cc != 0 */ + cond = TCG_COND_NE; + c->u.s32.b = tcg_constant_i32(0); break; default: - /* CC is masked by something else: (8 >> cc) & mask. */ - cond = TCG_COND_NE; - c->g1 = false; - c->u.s32.a = tcg_const_i32(8); - c->u.s32.b = tcg_const_i32(0); - tcg_gen_shr_i32(c->u.s32.a, c->u.s32.a, cc_op); - tcg_gen_andi_i32(c->u.s32.a, c->u.s32.a, mask); - break; + /* case 0: never, handled above. */ + g_assert_not_reached(); + } + if (mask & 8) { + cond = tcg_invert_cond(cond); } break; @@ -981,24 +932,6 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) c->cond = cond; } -static void free_compare(DisasCompare *c) -{ - if (!c->g1) { - if (c->is_64) { - tcg_temp_free_i64(c->u.s64.a); - } else { - tcg_temp_free_i32(c->u.s32.a); - } - } - if (!c->g2) { - if (c->is_64) { - tcg_temp_free_i64(c->u.s64.b); - } else { - tcg_temp_free_i32(c->u.s32.b); - } - } -} - /* ====================================================================== */ /* Define the insn format enumeration. */ #define F0(N) FMT_##N, @@ -1010,7 +943,7 @@ static void free_compare(DisasCompare *c) #define F6(N, X1, X2, X3, X4, X5, X6) F0(N) typedef enum { -#include "insn-format.def" +#include "insn-format.h.inc" } DisasFormat; #undef F0 @@ -1075,7 +1008,7 @@ typedef struct DisasFormatInfo { #define F6(N, X1, X2, X3, X4, X5, X6) { { X1, X2, X3, X4, X5, X6 } }, static const DisasFormatInfo format_info[] = { -#include "insn-format.def" +#include "insn-format.h.inc" }; #undef F0 @@ -1099,9 +1032,9 @@ static const DisasFormatInfo format_info[] = { them, and store them back. See the "in1", "in2", "prep", "wout" sets of routines below for more details. */ typedef struct { - bool g_out, g_out2, g_in1, g_in2; TCGv_i64 out, out2, in1, in2; TCGv_i64 addr1; + TCGv_i128 out_128, in1_128, in2_128; } DisasOps; /* Instructions can place constraints on their operands, raising specification @@ -1123,19 +1056,9 @@ typedef struct { exiting the TB. */ #define DISAS_PC_UPDATED DISAS_TARGET_0 -/* We have emitted one or more goto_tb. No fixup required. */ -#define DISAS_GOTO_TB DISAS_TARGET_1 - /* We have updated the PC and CC values. */ #define DISAS_PC_CC_UPDATED DISAS_TARGET_2 -/* We are exiting the TB, but have neither emitted a goto_tb, nor - updated the PC for the next instruction to be executed. */ -#define DISAS_PC_STALE DISAS_TARGET_3 - -/* We are exiting the TB to the main loop. */ -#define DISAS_PC_STALE_NOCHAIN DISAS_TARGET_4 - /* Instruction flags */ #define IF_AFP1 0x0001 /* r1 is a fp reg for HFP/FPS instructions */ @@ -1189,7 +1112,7 @@ static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest) tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, dest); tcg_gen_exit_tb(s->base.tb, 0); - return DISAS_GOTO_TB; + return DISAS_NORETURN; } else { tcg_gen_movi_i64(psw_addr, dest); per_branch(s, false); @@ -1258,7 +1181,7 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, tcg_gen_movi_i64(psw_addr, dest); tcg_gen_exit_tb(s->base.tb, 1); - ret = DISAS_GOTO_TB; + ret = DISAS_NORETURN; } else { /* Fallthru can use goto_tb, but taken branch cannot. */ /* Store taken branch destination before the brcond. This @@ -1293,9 +1216,9 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, Most commonly we're single-stepping or some other condition that disables all use of goto_tb. Just update the PC and exit. */ - TCGv_i64 next = tcg_const_i64(s->pc_tmp); + TCGv_i64 next = tcg_constant_i64(s->pc_tmp); if (is_imm) { - cdest = tcg_const_i64(dest); + cdest = tcg_constant_i64(dest); } if (c->is_64) { @@ -1305,26 +1228,17 @@ static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, } else { TCGv_i32 t0 = tcg_temp_new_i32(); TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 z = tcg_const_i64(0); + TCGv_i64 z = tcg_constant_i64(0); tcg_gen_setcond_i32(c->cond, t0, c->u.s32.a, c->u.s32.b); tcg_gen_extu_i32_i64(t1, t0); - tcg_temp_free_i32(t0); tcg_gen_movcond_i64(TCG_COND_NE, psw_addr, t1, z, cdest, next); per_branch_cond(s, TCG_COND_NE, t1, z); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(z); } - if (is_imm) { - tcg_temp_free_i64(cdest); - } - tcg_temp_free_i64(next); - ret = DISAS_PC_UPDATED; } egress: - free_compare(c); return ret; } @@ -1403,10 +1317,9 @@ static DisasJumpType op_addc64(DisasContext *s, DisasOps *o) { compute_carry(s); - TCGv_i64 zero = tcg_const_i64(0); + TCGv_i64 zero = tcg_constant_i64(0); tcg_gen_add2_i64(o->out, cc_src, o->in1, zero, cc_src, zero); tcg_gen_add2_i64(o->out, cc_src, o->out, cc_src, o->in2, zero); - tcg_temp_free_i64(zero); return DISAS_NEXT; } @@ -1458,20 +1371,19 @@ static DisasJumpType op_asiu64(DisasContext *s, DisasOps *o) static DisasJumpType op_aeb(DisasContext *s, DisasOps *o) { - gen_helper_aeb(o->out, cpu_env, o->in1, o->in2); + gen_helper_aeb(o->out, tcg_env, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_adb(DisasContext *s, DisasOps *o) { - gen_helper_adb(o->out, cpu_env, o->in1, o->in2); + gen_helper_adb(o->out, tcg_env, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_axb(DisasContext *s, DisasOps *o) { - gen_helper_axb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); - return_low128(o->out2); + gen_helper_axb(o->out_128, tcg_env, o->in1_128, o->in2_128); return DISAS_NEXT; } @@ -1486,11 +1398,11 @@ static DisasJumpType op_andi(DisasContext *s, DisasOps *o) int shift = s->insn->data & 0xff; int size = s->insn->data >> 8; uint64_t mask = ((1ull << size) - 1) << shift; + TCGv_i64 t = tcg_temp_new_i64(); - assert(!o->g_in2); - tcg_gen_shli_i64(o->in2, o->in2, shift); - tcg_gen_ori_i64(o->in2, o->in2, ~mask); - tcg_gen_and_i64(o->out, o->in1, o->in2); + tcg_gen_shli_i64(t, o->in2, shift); + tcg_gen_ori_i64(t, t, ~mask); + tcg_gen_and_i64(o->out, o->in1, t); /* Produce the CC from only the bits manipulated. */ tcg_gen_andi_i64(cc_dst, o->out, mask); @@ -1579,7 +1491,6 @@ static void save_link_info(DisasContext *s, DisasOps *o) tcg_gen_extu_i32_i64(t, cc_op); tcg_gen_shli_i64(t, t, 28); tcg_gen_or_i64(o->out, o->out, t); - tcg_temp_free_i64(t); } static DisasJumpType op_bal(DisasContext *s, DisasOps *o) @@ -1594,18 +1505,51 @@ static DisasJumpType op_bal(DisasContext *s, DisasOps *o) } } +/* + * Disassemble the target of a branch. The results are returned in a form + * suitable for passing into help_branch(): + * + * - bool IS_IMM reflects whether the target is fixed or computed. Non-EXECUTEd + * branches, whose DisasContext *S contains the relative immediate field RI, + * are considered fixed. All the other branches are considered computed. + * - int IMM is the value of RI. + * - TCGv_i64 CDEST is the address of the computed target. + */ +#define disas_jdest(s, ri, is_imm, imm, cdest) do { \ + if (have_field(s, ri)) { \ + if (unlikely(s->ex_value)) { \ + cdest = tcg_temp_new_i64(); \ + tcg_gen_ld_i64(cdest, tcg_env, offsetof(CPUS390XState, ex_target));\ + tcg_gen_addi_i64(cdest, cdest, (int64_t)get_field(s, ri) * 2); \ + is_imm = false; \ + } else { \ + is_imm = true; \ + } \ + } else { \ + is_imm = false; \ + } \ + imm = is_imm ? get_field(s, ri) : 0; \ +} while (false) + static DisasJumpType op_basi(DisasContext *s, DisasOps *o) { + DisasCompare c; + bool is_imm; + int imm; + pc_to_link_info(o->out, s, s->pc_tmp); - return help_goto_direct(s, s->base.pc_next + (int64_t)get_field(s, i2) * 2); + + disas_jdest(s, i2, is_imm, imm, o->in2); + disas_jcc(s, &c, 0xf); + return help_branch(s, &c, is_imm, imm, o->in2); } static DisasJumpType op_bc(DisasContext *s, DisasOps *o) { int m1 = get_field(s, m1); - bool is_imm = have_field(s, i2); - int imm = is_imm ? get_field(s, i2) : 0; DisasCompare c; + bool is_imm; + int imm; /* BCR with R2 = 0 causes no branching */ if (have_field(s, r2) && get_field(s, r2) == 0) { @@ -1622,6 +1566,7 @@ static DisasJumpType op_bc(DisasContext *s, DisasOps *o) return DISAS_NEXT; } + disas_jdest(s, i2, is_imm, imm, o->in2); disas_jcc(s, &c, m1); return help_branch(s, &c, is_imm, imm, o->in2); } @@ -1629,24 +1574,22 @@ static DisasJumpType op_bc(DisasContext *s, DisasOps *o) static DisasJumpType op_bct32(DisasContext *s, DisasOps *o) { int r1 = get_field(s, r1); - bool is_imm = have_field(s, i2); - int imm = is_imm ? get_field(s, i2) : 0; DisasCompare c; + bool is_imm; TCGv_i64 t; + int imm; c.cond = TCG_COND_NE; c.is_64 = false; - c.g1 = false; - c.g2 = false; t = tcg_temp_new_i64(); tcg_gen_subi_i64(t, regs[r1], 1); store_reg32_i64(r1, t); c.u.s32.a = tcg_temp_new_i32(); - c.u.s32.b = tcg_const_i32(0); + c.u.s32.b = tcg_constant_i32(0); tcg_gen_extrl_i64_i32(c.u.s32.a, t); - tcg_temp_free_i64(t); + disas_jdest(s, i2, is_imm, imm, o->in2); return help_branch(s, &c, is_imm, imm, o->in2); } @@ -1659,17 +1602,14 @@ static DisasJumpType op_bcth(DisasContext *s, DisasOps *o) c.cond = TCG_COND_NE; c.is_64 = false; - c.g1 = false; - c.g2 = false; t = tcg_temp_new_i64(); tcg_gen_shri_i64(t, regs[r1], 32); tcg_gen_subi_i64(t, t, 1); store_reg32h_i64(r1, t); c.u.s32.a = tcg_temp_new_i32(); - c.u.s32.b = tcg_const_i32(0); + c.u.s32.b = tcg_constant_i32(0); tcg_gen_extrl_i64_i32(c.u.s32.a, t); - tcg_temp_free_i64(t); return help_branch(s, &c, 1, imm, o->in2); } @@ -1677,19 +1617,18 @@ static DisasJumpType op_bcth(DisasContext *s, DisasOps *o) static DisasJumpType op_bct64(DisasContext *s, DisasOps *o) { int r1 = get_field(s, r1); - bool is_imm = have_field(s, i2); - int imm = is_imm ? get_field(s, i2) : 0; DisasCompare c; + bool is_imm; + int imm; c.cond = TCG_COND_NE; c.is_64 = true; - c.g1 = true; - c.g2 = false; tcg_gen_subi_i64(regs[r1], regs[r1], 1); c.u.s64.a = regs[r1]; - c.u.s64.b = tcg_const_i64(0); + c.u.s64.b = tcg_constant_i64(0); + disas_jdest(s, i2, is_imm, imm, o->in2); return help_branch(s, &c, is_imm, imm, o->in2); } @@ -1697,15 +1636,13 @@ static DisasJumpType op_bx32(DisasContext *s, DisasOps *o) { 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; + bool is_imm; TCGv_i64 t; + int imm; c.cond = (s->insn->data ? TCG_COND_LE : TCG_COND_GT); c.is_64 = false; - c.g1 = false; - c.g2 = false; t = tcg_temp_new_i64(); tcg_gen_add_i64(t, regs[r1], regs[r3]); @@ -1714,8 +1651,8 @@ static DisasJumpType op_bx32(DisasContext *s, DisasOps *o) tcg_gen_extrl_i64_i32(c.u.s32.a, t); tcg_gen_extrl_i64_i32(c.u.s32.b, regs[r3 | 1]); store_reg32_i64(r1, t); - tcg_temp_free_i64(t); + disas_jdest(s, i2, is_imm, imm, o->in2); return help_branch(s, &c, is_imm, imm, o->in2); } @@ -1723,25 +1660,23 @@ static DisasJumpType op_bx64(DisasContext *s, DisasOps *o) { 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; + bool is_imm; + int imm; c.cond = (s->insn->data ? TCG_COND_LE : TCG_COND_GT); c.is_64 = true; if (r1 == (r3 | 1)) { c.u.s64.b = load_reg(r3 | 1); - c.g2 = false; } else { c.u.s64.b = regs[r3 | 1]; - c.g2 = true; } tcg_gen_add_i64(regs[r1], regs[r1], regs[r3]); c.u.s64.a = regs[r1]; - c.g1 = true; + disas_jdest(s, i2, is_imm, imm, o->in2); return help_branch(s, &c, is_imm, imm, o->in2); } @@ -1755,14 +1690,13 @@ static DisasJumpType op_cj(DisasContext *s, DisasOps *o) if (s->insn->data) { c.cond = tcg_unsigned_cond(c.cond); } - c.is_64 = c.g1 = c.g2 = true; + c.is_64 = true; c.u.s64.a = o->in1; c.u.s64.b = o->in2; - is_imm = have_field(s, i4); - if (is_imm) { - imm = get_field(s, i4); - } else { + o->out = NULL; + disas_jdest(s, i4, is_imm, imm, o->out); + if (!is_imm && !o->out) { imm = 0; o->out = get_address(s, 0, get_field(s, b4), get_field(s, d4)); @@ -1773,21 +1707,21 @@ static DisasJumpType op_cj(DisasContext *s, DisasOps *o) static DisasJumpType op_ceb(DisasContext *s, DisasOps *o) { - gen_helper_ceb(cc_op, cpu_env, o->in1, o->in2); + gen_helper_ceb(cc_op, tcg_env, o->in1, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_cdb(DisasContext *s, DisasOps *o) { - gen_helper_cdb(cc_op, cpu_env, o->in1, o->in2); + gen_helper_cdb(cc_op, tcg_env, o->in1, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_cxb(DisasContext *s, DisasOps *o) { - gen_helper_cxb(cc_op, cpu_env, o->out, o->out2, o->in1, o->in2); + gen_helper_cxb(cc_op, tcg_env, o->in1_128, o->in2_128); set_cc_static(s); return DISAS_NEXT; } @@ -1814,7 +1748,7 @@ static TCGv_i32 fpinst_extract_m34(DisasContext *s, bool m3_with_fpe, return NULL; } - return tcg_const_i32(deposit32(m3, 4, 4, m4)); + return tcg_constant_i32(deposit32(m3, 4, 4, m4)); } static DisasJumpType op_cfeb(DisasContext *s, DisasOps *o) @@ -1824,8 +1758,7 @@ static DisasJumpType op_cfeb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cfeb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_cfeb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1837,8 +1770,7 @@ static DisasJumpType op_cfdb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cfdb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_cfdb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1850,8 +1782,7 @@ static DisasJumpType op_cfxb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cfxb(o->out, cpu_env, o->in1, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_cfxb(o->out, tcg_env, o->in2_128, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1863,8 +1794,7 @@ static DisasJumpType op_cgeb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cgeb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_cgeb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1876,8 +1806,7 @@ static DisasJumpType op_cgdb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cgdb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_cgdb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1889,8 +1818,7 @@ static DisasJumpType op_cgxb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cgxb(o->out, cpu_env, o->in1, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_cgxb(o->out, tcg_env, o->in2_128, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1902,8 +1830,7 @@ static DisasJumpType op_clfeb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_clfeb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_clfeb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1915,8 +1842,7 @@ static DisasJumpType op_clfdb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_clfdb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_clfdb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1928,8 +1854,7 @@ static DisasJumpType op_clfxb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_clfxb(o->out, cpu_env, o->in1, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_clfxb(o->out, tcg_env, o->in2_128, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1941,8 +1866,7 @@ static DisasJumpType op_clgeb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_clgeb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_clgeb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1954,8 +1878,7 @@ static DisasJumpType op_clgdb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_clgdb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_clgdb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1967,8 +1890,7 @@ static DisasJumpType op_clgxb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_clgxb(o->out, cpu_env, o->in1, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_clgxb(o->out, tcg_env, o->in2_128, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1980,8 +1902,7 @@ static DisasJumpType op_cegb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cegb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_cegb(o->out, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -1992,8 +1913,7 @@ static DisasJumpType op_cdgb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cdgb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_cdgb(o->out, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -2004,9 +1924,7 @@ static DisasJumpType op_cxgb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cxgb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); - return_low128(o->out2); + gen_helper_cxgb(o->out_128, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -2017,8 +1935,7 @@ static DisasJumpType op_celgb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_celgb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_celgb(o->out, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -2029,8 +1946,7 @@ static DisasJumpType op_cdlgb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cdlgb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_cdlgb(o->out, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -2041,24 +1957,22 @@ static DisasJumpType op_cxlgb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cxlgb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); - return_low128(o->out2); + gen_helper_cxlgb(o->out_128, tcg_env, o->in2, m34); return DISAS_NEXT; } static DisasJumpType op_cksm(DisasContext *s, DisasOps *o) { int r2 = get_field(s, r2); + TCGv_i128 pair = tcg_temp_new_i128(); TCGv_i64 len = tcg_temp_new_i64(); - gen_helper_cksm(len, cpu_env, o->in1, o->in2, regs[r2 + 1]); + gen_helper_cksm(pair, tcg_env, o->in1, o->in2, regs[r2 + 1]); set_cc_static(s); - return_low128(o->out); + tcg_gen_extr_i128_i64(o->out, len, pair); tcg_gen_add_i64(regs[r2], regs[r2], len); tcg_gen_sub_i64(regs[r2 + 1], regs[r2 + 1], len); - tcg_temp_free_i64(len); return DISAS_NEXT; } @@ -2066,34 +1980,28 @@ static DisasJumpType op_cksm(DisasContext *s, DisasOps *o) static DisasJumpType op_clc(DisasContext *s, DisasOps *o) { int l = get_field(s, l1); + TCGv_i64 src; TCGv_i32 vl; + MemOp mop; switch (l + 1) { case 1: - tcg_gen_qemu_ld8u(cc_src, o->addr1, get_mem_index(s)); - tcg_gen_qemu_ld8u(cc_dst, o->in2, get_mem_index(s)); - break; case 2: - tcg_gen_qemu_ld16u(cc_src, o->addr1, get_mem_index(s)); - tcg_gen_qemu_ld16u(cc_dst, o->in2, get_mem_index(s)); - break; case 4: - tcg_gen_qemu_ld32u(cc_src, o->addr1, get_mem_index(s)); - tcg_gen_qemu_ld32u(cc_dst, o->in2, get_mem_index(s)); - break; case 8: - tcg_gen_qemu_ld64(cc_src, o->addr1, get_mem_index(s)); - tcg_gen_qemu_ld64(cc_dst, o->in2, get_mem_index(s)); - break; + mop = ctz32(l + 1) | MO_TE; + /* Do not update cc_src yet: loading cc_dst may cause an exception. */ + src = tcg_temp_new_i64(); + tcg_gen_qemu_ld_tl(src, o->addr1, get_mem_index(s), mop); + tcg_gen_qemu_ld_tl(cc_dst, o->in2, get_mem_index(s), mop); + gen_op_update2_cc_i64(s, CC_OP_LTUGTU_64, src, cc_dst); + return DISAS_NEXT; default: - vl = tcg_const_i32(l); - gen_helper_clc(cc_op, cpu_env, vl, o->addr1, o->in2); - tcg_temp_free_i32(vl); + vl = tcg_constant_i32(l); + gen_helper_clc(cc_op, tcg_env, vl, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } - gen_op_update2_cc_i64(s, CC_OP_LTUGTU_64, cc_src, cc_dst); - return DISAS_NEXT; } static DisasJumpType op_clcl(DisasContext *s, DisasOps *o) @@ -2108,11 +2016,9 @@ static DisasJumpType op_clcl(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - t1 = tcg_const_i32(r1); - t2 = tcg_const_i32(r2); - gen_helper_clcl(cc_op, cpu_env, t1, t2); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); + t1 = tcg_constant_i32(r1); + t2 = tcg_constant_i32(r2); + gen_helper_clcl(cc_op, tcg_env, t1, t2); set_cc_static(s); return DISAS_NEXT; } @@ -2129,11 +2035,9 @@ static DisasJumpType op_clcle(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - t1 = tcg_const_i32(r1); - t3 = tcg_const_i32(r3); - gen_helper_clcle(cc_op, cpu_env, t1, o->in2, t3); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t3); + t1 = tcg_constant_i32(r1); + t3 = tcg_constant_i32(r3); + gen_helper_clcle(cc_op, tcg_env, t1, o->in2, t3); set_cc_static(s); return DISAS_NEXT; } @@ -2150,32 +2054,32 @@ static DisasJumpType op_clclu(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - t1 = tcg_const_i32(r1); - t3 = tcg_const_i32(r3); - gen_helper_clclu(cc_op, cpu_env, t1, o->in2, t3); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t3); + t1 = tcg_constant_i32(r1); + t3 = tcg_constant_i32(r3); + gen_helper_clclu(cc_op, tcg_env, t1, o->in2, t3); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_clm(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s, m3)); + TCGv_i32 m3 = tcg_constant_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); + gen_helper_clm(cc_op, tcg_env, t1, m3, o->in2); set_cc_static(s); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(m3); return DISAS_NEXT; } static DisasJumpType op_clst(DisasContext *s, DisasOps *o) { - gen_helper_clst(o->in1, cpu_env, regs[0], o->in1, o->in2); + TCGv_i128 pair = tcg_temp_new_i128(); + + gen_helper_clst(pair, tcg_env, regs[0], o->in1, o->in2); + tcg_gen_extr_i128_i64(o->in2, o->in1, pair); + set_cc_static(s); - return_low128(o->in2); return DISAS_NEXT; } @@ -2185,7 +2089,6 @@ static DisasJumpType op_cps(DisasContext *s, DisasOps *o) tcg_gen_andi_i64(t, o->in1, 0x8000000000000000ull); tcg_gen_andi_i64(o->out, o->in2, 0x7fffffffffffffffull); tcg_gen_or_i64(o->out, o->out, t); - tcg_temp_free_i64(t); return DISAS_NEXT; } @@ -2201,14 +2104,12 @@ static DisasJumpType op_cs(DisasContext *s, DisasOps *o) addr = get_address(s, 0, b2, d2); tcg_gen_atomic_cmpxchg_i64(o->out, addr, o->in2, o->in1, get_mem_index(s), s->insn->data | MO_ALIGN); - tcg_temp_free_i64(addr); /* Are the memory and expected values (un)equal? Note that this setcond produces the output CC value, thus the NE sense of the test. */ cc = tcg_temp_new_i64(); tcg_gen_setcond_i64(TCG_COND_NE, cc, o->in2, o->out); tcg_gen_extrl_i64_i32(cc_op, cc); - tcg_temp_free_i64(cc); set_cc_static(s); return DISAS_NEXT; @@ -2217,44 +2118,37 @@ static DisasJumpType op_cs(DisasContext *s, DisasOps *o) static DisasJumpType op_cdsg(DisasContext *s, DisasOps *o) { 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; - /* Note that R1:R1+1 = expected value and R3:R3+1 = new value. */ - addr = get_address(s, 0, b2, d2); - t_r1 = tcg_const_i32(r1); - t_r3 = tcg_const_i32(r3); - if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_helper_cdsg(cpu_env, addr, t_r1, t_r3); - } else if (HAVE_CMPXCHG128) { - gen_helper_cdsg_parallel(cpu_env, addr, t_r1, t_r3); - } else { - gen_helper_exit_atomic(cpu_env); - ret = DISAS_NORETURN; - } - tcg_temp_free_i64(addr); - tcg_temp_free_i32(t_r1); - tcg_temp_free_i32(t_r3); + o->out_128 = tcg_temp_new_i128(); + tcg_gen_concat_i64_i128(o->out_128, regs[r1 + 1], regs[r1]); - set_cc_static(s); - return ret; + /* Note out (R1:R1+1) = expected value and in2 (R3:R3+1) = new value. */ + tcg_gen_atomic_cmpxchg_i128(o->out_128, o->addr1, o->out_128, o->in2_128, + get_mem_index(s), MO_BE | MO_128 | MO_ALIGN); + + /* + * Extract result into cc_dst:cc_src, compare vs the expected value + * in the as yet unmodified input registers, then update CC_OP. + */ + tcg_gen_extr_i128_i64(cc_src, cc_dst, o->out_128); + tcg_gen_xor_i64(cc_dst, cc_dst, regs[r1]); + tcg_gen_xor_i64(cc_src, cc_src, regs[r1 + 1]); + tcg_gen_or_i64(cc_dst, cc_dst, cc_src); + set_cc_nz_u64(s, cc_dst); + + return DISAS_NEXT; } static DisasJumpType op_csst(DisasContext *s, DisasOps *o) { int r3 = get_field(s, r3); - TCGv_i32 t_r3 = tcg_const_i32(r3); + TCGv_i32 t_r3 = tcg_constant_i32(r3); if (tb_cflags(s->base.tb) & CF_PARALLEL) { - gen_helper_csst_parallel(cc_op, cpu_env, t_r3, o->addr1, o->in2); + gen_helper_csst_parallel(cc_op, tcg_env, t_r3, o->addr1, o->in2); } else { - gen_helper_csst(cc_op, cpu_env, t_r3, o->addr1, o->in2); + gen_helper_csst(cc_op, tcg_env, t_r3, o->addr1, o->in2); } - tcg_temp_free_i32(t_r3); set_cc_static(s); return DISAS_NEXT; @@ -2275,7 +2169,6 @@ static DisasJumpType op_csp(DisasContext *s, DisasOps *o) tcg_gen_andi_i64(addr, o->in2, -1ULL << (mop & MO_SIZE)); tcg_gen_atomic_cmpxchg_i64(old, addr, o->in1, o->out2, get_mem_index(s), mop | MO_ALIGN); - tcg_temp_free_i64(addr); /* Are the memory and expected values (un)equal? */ cc = tcg_temp_new_i64(); @@ -2289,31 +2182,51 @@ static DisasJumpType op_csp(DisasContext *s, DisasOps *o) } else { tcg_gen_mov_i64(o->out, old); } - tcg_temp_free_i64(old); /* If the comparison was equal, and the LSB of R2 was set, then we need to flush the TLB (for all cpus). */ tcg_gen_xori_i64(cc, cc, 1); tcg_gen_and_i64(cc, cc, o->in2); tcg_gen_brcondi_i64(TCG_COND_EQ, cc, 0, lab); - tcg_temp_free_i64(cc); - gen_helper_purge(cpu_env); + gen_helper_purge(tcg_env); gen_set_label(lab); return DISAS_NEXT; } #endif +static DisasJumpType op_cvb(DisasContext *s, DisasOps *o) +{ + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(t, o->addr1, get_mem_index(s), MO_TEUQ); + gen_helper_cvb(tcg_env, tcg_constant_i32(get_field(s, r1)), t); + return DISAS_NEXT; +} + +static DisasJumpType op_cvbg(DisasContext *s, DisasOps *o) +{ + TCGv_i128 t = tcg_temp_new_i128(); + tcg_gen_qemu_ld_i128(t, o->addr1, get_mem_index(s), MO_TE | MO_128); + gen_helper_cvbg(o->out, tcg_env, t); + return DISAS_NEXT; +} + static DisasJumpType op_cvd(DisasContext *s, DisasOps *o) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i32 t2 = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(t2, o->in1); gen_helper_cvd(t1, t2); - tcg_temp_free_i32(t2); - tcg_gen_qemu_st64(t1, o->in2, get_mem_index(s)); - tcg_temp_free_i64(t1); + tcg_gen_qemu_st_i64(t1, o->in2, get_mem_index(s), MO_TEUQ); + return DISAS_NEXT; +} + +static DisasJumpType op_cvdg(DisasContext *s, DisasOps *o) +{ + TCGv_i128 t = tcg_temp_new_i128(); + gen_helper_cvdg(t, o->in1); + tcg_gen_qemu_st_i128(t, o->in2, get_mem_index(s), MO_TE | MO_128); return DISAS_NEXT; } @@ -2352,36 +2265,33 @@ static DisasJumpType op_cuXX(DisasContext *s, DisasOps *o) m3 = 0; } - tr1 = tcg_const_i32(r1); - tr2 = tcg_const_i32(r2); - chk = tcg_const_i32(m3); + tr1 = tcg_constant_i32(r1); + tr2 = tcg_constant_i32(r2); + chk = tcg_constant_i32(m3); switch (s->insn->data) { case 12: - gen_helper_cu12(cc_op, cpu_env, tr1, tr2, chk); + gen_helper_cu12(cc_op, tcg_env, tr1, tr2, chk); break; case 14: - gen_helper_cu14(cc_op, cpu_env, tr1, tr2, chk); + gen_helper_cu14(cc_op, tcg_env, tr1, tr2, chk); break; case 21: - gen_helper_cu21(cc_op, cpu_env, tr1, tr2, chk); + gen_helper_cu21(cc_op, tcg_env, tr1, tr2, chk); break; case 24: - gen_helper_cu24(cc_op, cpu_env, tr1, tr2, chk); + gen_helper_cu24(cc_op, tcg_env, tr1, tr2, chk); break; case 41: - gen_helper_cu41(cc_op, cpu_env, tr1, tr2, chk); + gen_helper_cu41(cc_op, tcg_env, tr1, tr2, chk); break; case 42: - gen_helper_cu42(cc_op, cpu_env, tr1, tr2, chk); + gen_helper_cu42(cc_op, tcg_env, tr1, tr2, chk); break; default: g_assert_not_reached(); } - tcg_temp_free_i32(tr1); - tcg_temp_free_i32(tr2); - tcg_temp_free_i32(chk); set_cc_static(s); return DISAS_NEXT; } @@ -2389,70 +2299,69 @@ 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, r1)); - TCGv_i32 r3 = tcg_const_i32(get_field(s, r3)); - TCGv_i32 func_code = tcg_const_i32(get_field(s, i2)); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); + TCGv_i32 func_code = tcg_constant_i32(get_field(s, i2)); - gen_helper_diag(cpu_env, r1, r3, func_code); - - tcg_temp_free_i32(func_code); - tcg_temp_free_i32(r3); - tcg_temp_free_i32(r1); + gen_helper_diag(tcg_env, r1, r3, func_code); return DISAS_NEXT; } #endif static DisasJumpType op_divs32(DisasContext *s, DisasOps *o) { - gen_helper_divs32(o->out2, cpu_env, o->in1, o->in2); - return_low128(o->out); + gen_helper_divs32(o->out, tcg_env, o->in1, o->in2); + tcg_gen_extr32_i64(o->out2, o->out, o->out); return DISAS_NEXT; } static DisasJumpType op_divu32(DisasContext *s, DisasOps *o) { - gen_helper_divu32(o->out2, cpu_env, o->in1, o->in2); - return_low128(o->out); + gen_helper_divu32(o->out, tcg_env, o->in1, o->in2); + tcg_gen_extr32_i64(o->out2, o->out, o->out); return DISAS_NEXT; } static DisasJumpType op_divs64(DisasContext *s, DisasOps *o) { - gen_helper_divs64(o->out2, cpu_env, o->in1, o->in2); - return_low128(o->out); + TCGv_i128 t = tcg_temp_new_i128(); + + gen_helper_divs64(t, tcg_env, o->in1, o->in2); + tcg_gen_extr_i128_i64(o->out2, o->out, t); return DISAS_NEXT; } static DisasJumpType op_divu64(DisasContext *s, DisasOps *o) { - gen_helper_divu64(o->out2, cpu_env, o->out, o->out2, o->in2); - return_low128(o->out); + TCGv_i128 t = tcg_temp_new_i128(); + + gen_helper_divu64(t, tcg_env, o->out, o->out2, o->in2); + tcg_gen_extr_i128_i64(o->out2, o->out, t); return DISAS_NEXT; } static DisasJumpType op_deb(DisasContext *s, DisasOps *o) { - gen_helper_deb(o->out, cpu_env, o->in1, o->in2); + gen_helper_deb(o->out, tcg_env, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_ddb(DisasContext *s, DisasOps *o) { - gen_helper_ddb(o->out, cpu_env, o->in1, o->in2); + gen_helper_ddb(o->out, tcg_env, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_dxb(DisasContext *s, DisasOps *o) { - gen_helper_dxb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); - return_low128(o->out2); + gen_helper_dxb(o->out_128, tcg_env, o->in1_128, o->in2_128); return DISAS_NEXT; } static DisasJumpType op_ear(DisasContext *s, DisasOps *o) { int r2 = get_field(s, r2); - tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, aregs[r2])); + tcg_gen_ld32u_i64(o->out, tcg_env, offsetof(CPUS390XState, aregs[r2])); return DISAS_NEXT; } @@ -2465,7 +2374,7 @@ static DisasJumpType op_ecag(DisasContext *s, DisasOps *o) static DisasJumpType op_efpc(DisasContext *s, DisasOps *o) { - tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, fpc)); + tcg_gen_ld32u_i64(o->out, tcg_env, offsetof(CPUS390XState, fpc)); return DISAS_NEXT; } @@ -2474,16 +2383,18 @@ static DisasJumpType op_epsw(DisasContext *s, DisasOps *o) int r1 = get_field(s, r1); int r2 = get_field(s, r2); TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t_cc = tcg_temp_new_i64(); /* Note the "subsequently" in the PoO, which implies a defined result if r1 == r2. Thus we cannot defer these writes to an output hook. */ + gen_op_calc_cc(s); + tcg_gen_extu_i32_i64(t_cc, cc_op); tcg_gen_shri_i64(t, psw_mask, 32); + tcg_gen_deposit_i64(t, t, t_cc, 12, 2); store_reg32_i64(r1, t); if (r2 != 0) { store_reg32_i64(r2, psw_mask); } - - tcg_temp_free_i64(t); return DISAS_NEXT; } @@ -2503,18 +2414,13 @@ static DisasJumpType op_ex(DisasContext *s, DisasOps *o) update_cc_op(s); if (r1 == 0) { - v1 = tcg_const_i64(0); + v1 = tcg_constant_i64(0); } else { v1 = regs[r1]; } - ilen = tcg_const_i32(s->ilen); - gen_helper_ex(cpu_env, ilen, v1, o->in2); - tcg_temp_free_i32(ilen); - - if (r1 == 0) { - tcg_temp_free_i64(v1); - } + ilen = tcg_constant_i32(s->ilen); + gen_helper_ex(tcg_env, ilen, v1, o->in2); return DISAS_PC_CC_UPDATED; } @@ -2526,8 +2432,7 @@ static DisasJumpType op_fieb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_fieb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_fieb(o->out, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -2538,8 +2443,7 @@ static DisasJumpType op_fidb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_fidb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_fidb(o->out, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -2550,9 +2454,7 @@ static DisasJumpType op_fixb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_fixb(o->out, cpu_env, o->in1, o->in2, m34); - return_low128(o->out2); - tcg_temp_free_i32(m34); + gen_helper_fixb(o->out_128, tcg_env, o->in2_128, m34); return DISAS_NEXT; } @@ -2586,7 +2488,7 @@ static DisasJumpType op_icm(DisasContext *s, DisasOps *o) switch (m3) { case 0xf: /* Effectively a 32-bit load. */ - tcg_gen_qemu_ld32u(tmp, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(tmp, o->in2, get_mem_index(s), MO_TEUL); len = 32; goto one_insert; @@ -2594,7 +2496,7 @@ static DisasJumpType op_icm(DisasContext *s, DisasOps *o) case 0x6: case 0x3: /* Effectively a 16-bit load. */ - tcg_gen_qemu_ld16u(tmp, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(tmp, o->in2, get_mem_index(s), MO_TEUW); len = 16; goto one_insert; @@ -2603,7 +2505,7 @@ static DisasJumpType op_icm(DisasContext *s, DisasOps *o) case 0x2: case 0x1: /* Effectively an 8-bit load. */ - tcg_gen_qemu_ld8u(tmp, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(tmp, o->in2, get_mem_index(s), MO_UB); len = 8; goto one_insert; @@ -2613,13 +2515,19 @@ static DisasJumpType op_icm(DisasContext *s, DisasOps *o) ccm = ((1ull << len) - 1) << pos; break; + case 0: + /* Recognize access exceptions for the first byte. */ + tcg_gen_qemu_ld_i64(tmp, o->in2, get_mem_index(s), MO_UB); + gen_op_movi_cc(s, 0); + return DISAS_NEXT; + default: /* This is going to be a sequence of loads and inserts. */ pos = base + 32 - 8; ccm = 0; while (m3) { if (m3 & 0x8) { - tcg_gen_qemu_ld8u(tmp, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(tmp, o->in2, get_mem_index(s), MO_UB); tcg_gen_addi_i64(o->in2, o->in2, 1); tcg_gen_deposit_i64(o->out, o->out, tmp, pos, 8); ccm |= 0xffull << pos; @@ -2632,7 +2540,6 @@ static DisasJumpType op_icm(DisasContext *s, DisasOps *o) tcg_gen_movi_i64(tmp, ccm); gen_op_update2_cc_i64(s, CC_OP_ICM, tmp, o->out); - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -2655,8 +2562,6 @@ static DisasJumpType op_ipm(DisasContext *s, DisasOps *o) tcg_gen_extu_i32_i64(t2, cc_op); tcg_gen_deposit_i64(t1, t1, t2, 4, 60); tcg_gen_deposit_i64(o->out, o->out, t1, 24, 8); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); return DISAS_NEXT; } @@ -2666,12 +2571,11 @@ 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, m4)); + m4 = tcg_constant_i32(get_field(s, m4)); } else { - m4 = tcg_const_i32(0); + m4 = tcg_constant_i32(0); } - gen_helper_idte(cpu_env, o->in1, o->in2, m4); - tcg_temp_free_i32(m4); + gen_helper_idte(tcg_env, o->in1, o->in2, m4); return DISAS_NEXT; } @@ -2680,18 +2584,17 @@ 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, m4)); + m4 = tcg_constant_i32(get_field(s, m4)); } else { - m4 = tcg_const_i32(0); + m4 = tcg_constant_i32(0); } - gen_helper_ipte(cpu_env, o->in1, o->in2, m4); - tcg_temp_free_i32(m4); + gen_helper_ipte(tcg_env, o->in1, o->in2, m4); return DISAS_NEXT; } static DisasJumpType op_iske(DisasContext *s, DisasOps *o) { - gen_helper_iske(o->out, cpu_env, o->in2); + gen_helper_iske(o->out, tcg_env, o->in2); return DISAS_NEXT; } #endif @@ -2741,51 +2644,62 @@ static DisasJumpType op_msa(DisasContext *s, DisasOps *o) g_assert_not_reached(); }; - t_r1 = tcg_const_i32(r1); - t_r2 = tcg_const_i32(r2); - t_r3 = tcg_const_i32(r3); - type = tcg_const_i32(s->insn->data); - gen_helper_msa(cc_op, cpu_env, t_r1, t_r2, t_r3, type); + t_r1 = tcg_constant_i32(r1); + t_r2 = tcg_constant_i32(r2); + t_r3 = tcg_constant_i32(r3); + type = tcg_constant_i32(s->insn->data); + gen_helper_msa(cc_op, tcg_env, t_r1, t_r2, t_r3, type); set_cc_static(s); - tcg_temp_free_i32(t_r1); - tcg_temp_free_i32(t_r2); - tcg_temp_free_i32(t_r3); - tcg_temp_free_i32(type); return DISAS_NEXT; } static DisasJumpType op_keb(DisasContext *s, DisasOps *o) { - gen_helper_keb(cc_op, cpu_env, o->in1, o->in2); + gen_helper_keb(cc_op, tcg_env, o->in1, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_kdb(DisasContext *s, DisasOps *o) { - gen_helper_kdb(cc_op, cpu_env, o->in1, o->in2); + gen_helper_kdb(cc_op, tcg_env, o->in1, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_kxb(DisasContext *s, DisasOps *o) { - gen_helper_kxb(cc_op, cpu_env, o->out, o->out2, o->in1, o->in2); + gen_helper_kxb(cc_op, tcg_env, o->in1_128, o->in2_128); set_cc_static(s); return DISAS_NEXT; } -static DisasJumpType op_laa(DisasContext *s, DisasOps *o) +static DisasJumpType help_laa(DisasContext *s, DisasOps *o, bool addu64) { /* The real output is indeed the original value in memory; recompute the addition for the computation of CC. */ tcg_gen_atomic_fetch_add_i64(o->in2, o->in2, o->in1, get_mem_index(s), s->insn->data | MO_ALIGN); /* However, we need to recompute the addition for setting CC. */ - tcg_gen_add_i64(o->out, o->in1, o->in2); + if (addu64) { + tcg_gen_movi_i64(cc_src, 0); + tcg_gen_add2_i64(o->out, cc_src, o->in1, cc_src, o->in2, cc_src); + } else { + tcg_gen_add_i64(o->out, o->in1, o->in2); + } return DISAS_NEXT; } +static DisasJumpType op_laa(DisasContext *s, DisasOps *o) +{ + return help_laa(s, o, false); +} + +static DisasJumpType op_laa_addu64(DisasContext *s, DisasOps *o) +{ + return help_laa(s, o, true); +} + static DisasJumpType op_lan(DisasContext *s, DisasOps *o) { /* The real output is indeed the original value in memory; @@ -2821,7 +2735,7 @@ static DisasJumpType op_lax(DisasContext *s, DisasOps *o) static DisasJumpType op_ldeb(DisasContext *s, DisasOps *o) { - gen_helper_ldeb(o->out, cpu_env, o->in2); + gen_helper_ldeb(o->out, tcg_env, o->in2); return DISAS_NEXT; } @@ -2832,8 +2746,7 @@ static DisasJumpType op_ledb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_ledb(o->out, cpu_env, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_ledb(o->out, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -2844,8 +2757,7 @@ static DisasJumpType op_ldxb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_ldxb(o->out, cpu_env, o->in1, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_ldxb(o->out, tcg_env, o->in2_128, m34); return DISAS_NEXT; } @@ -2856,22 +2768,19 @@ static DisasJumpType op_lexb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_lexb(o->out, cpu_env, o->in1, o->in2, m34); - tcg_temp_free_i32(m34); + gen_helper_lexb(o->out, tcg_env, o->in2_128, m34); return DISAS_NEXT; } static DisasJumpType op_lxdb(DisasContext *s, DisasOps *o) { - gen_helper_lxdb(o->out, cpu_env, o->in2); - return_low128(o->out2); + gen_helper_lxdb(o->out_128, tcg_env, o->in2); return DISAS_NEXT; } static DisasJumpType op_lxeb(DisasContext *s, DisasOps *o) { - gen_helper_lxeb(o->out, cpu_env, o->in2); - return_low128(o->out2); + gen_helper_lxeb(o->out_128, tcg_env, o->in2); return DISAS_NEXT; } @@ -2889,43 +2798,46 @@ static DisasJumpType op_llgt(DisasContext *s, DisasOps *o) static DisasJumpType op_ld8s(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_ld8s(o->out, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->out, o->in2, get_mem_index(s), MO_SB); return DISAS_NEXT; } static DisasJumpType op_ld8u(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_ld8u(o->out, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->out, o->in2, get_mem_index(s), MO_UB); return DISAS_NEXT; } static DisasJumpType op_ld16s(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_ld16s(o->out, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->out, o->in2, get_mem_index(s), MO_TESW); return DISAS_NEXT; } static DisasJumpType op_ld16u(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_ld16u(o->out, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->out, o->in2, get_mem_index(s), MO_TEUW); return DISAS_NEXT; } static DisasJumpType op_ld32s(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_ld32s(o->out, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_tl(o->out, o->in2, get_mem_index(s), + MO_TESL | s->insn->data); return DISAS_NEXT; } static DisasJumpType op_ld32u(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_ld32u(o->out, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_tl(o->out, o->in2, get_mem_index(s), + MO_TEUL | s->insn->data); return DISAS_NEXT; } static DisasJumpType op_ld64(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_ld64(o->out, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->out, o->in2, get_mem_index(s), + MO_TEUQ | s->insn->data); return DISAS_NEXT; } @@ -2943,7 +2855,7 @@ static DisasJumpType op_lat(DisasContext *s, DisasOps *o) static DisasJumpType op_lgat(DisasContext *s, DisasOps *o) { TCGLabel *lab = gen_new_label(); - tcg_gen_qemu_ld64(o->out, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->out, o->in2, get_mem_index(s), MO_TEUQ); /* The value is stored even in case of trap. */ tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab); gen_trap(s); @@ -2965,7 +2877,8 @@ static DisasJumpType op_lfhat(DisasContext *s, DisasOps *o) static DisasJumpType op_llgfat(DisasContext *s, DisasOps *o) { TCGLabel *lab = gen_new_label(); - tcg_gen_qemu_ld32u(o->out, o->in2, get_mem_index(s)); + + tcg_gen_qemu_ld_i64(o->out, o->in2, get_mem_index(s), MO_TEUL); /* The value is stored even in case of trap. */ tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab); gen_trap(s); @@ -2999,22 +2912,17 @@ static DisasJumpType op_loc(DisasContext *s, DisasOps *o) if (c.is_64) { tcg_gen_movcond_i64(c.cond, o->out, c.u.s64.a, c.u.s64.b, o->in2, o->in1); - free_compare(&c); } else { TCGv_i32 t32 = tcg_temp_new_i32(); TCGv_i64 t, z; tcg_gen_setcond_i32(c.cond, t32, c.u.s32.a, c.u.s32.b); - free_compare(&c); t = tcg_temp_new_i64(); tcg_gen_extu_i32_i64(t, t32); - tcg_temp_free_i32(t32); - z = tcg_const_i64(0); + z = tcg_constant_i64(0); tcg_gen_movcond_i64(TCG_COND_NE, o->out, t, z, o->in2, o->in1); - tcg_temp_free_i64(t); - tcg_temp_free_i64(z); } return DISAS_NEXT; @@ -3023,56 +2931,56 @@ 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, 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); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); + + gen_helper_lctl(tcg_env, r1, o->in2, r3); /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return DISAS_PC_STALE_NOCHAIN; + s->exit_to_mainloop = true; + return DISAS_TOO_MANY; } static DisasJumpType op_lctlg(DisasContext *s, DisasOps *o) { - 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); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); + + gen_helper_lctlg(tcg_env, r1, o->in2, r3); /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return DISAS_PC_STALE_NOCHAIN; + s->exit_to_mainloop = true; + return DISAS_TOO_MANY; } static DisasJumpType op_lra(DisasContext *s, DisasOps *o) { - gen_helper_lra(o->out, cpu_env, o->in2); + gen_helper_lra(o->out, tcg_env, o->out, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_lpp(DisasContext *s, DisasOps *o) { - tcg_gen_st_i64(o->in2, cpu_env, offsetof(CPUS390XState, pp)); + tcg_gen_st_i64(o->in2, tcg_env, offsetof(CPUS390XState, pp)); return DISAS_NEXT; } static DisasJumpType op_lpsw(DisasContext *s, DisasOps *o) { - TCGv_i64 t1, t2; + TCGv_i64 mask, addr; per_breaking_event(s); - t1 = tcg_temp_new_i64(); - t2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(t1, o->in2, get_mem_index(s), - MO_TEUL | MO_ALIGN_8); - tcg_gen_addi_i64(o->in2, o->in2, 4); - tcg_gen_qemu_ld32u(t2, o->in2, get_mem_index(s)); - /* Convert the 32-bit PSW_MASK into the 64-bit PSW_MASK. */ - tcg_gen_shli_i64(t1, t1, 32); - gen_helper_load_psw(cpu_env, t1, t2); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); + /* + * Convert the short PSW into the normal PSW, similar to what + * s390_cpu_load_normal() does. + */ + mask = tcg_temp_new_i64(); + addr = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(mask, o->in2, get_mem_index(s), MO_TEUQ | MO_ALIGN_8); + tcg_gen_andi_i64(addr, mask, PSW_MASK_SHORT_ADDR); + tcg_gen_andi_i64(mask, mask, PSW_MASK_SHORT_CTRL); + tcg_gen_xori_i64(mask, mask, PSW_MASK_SHORTPSW); + gen_helper_load_psw(tcg_env, mask, addr); return DISAS_NORETURN; } @@ -3087,21 +2995,18 @@ static DisasJumpType op_lpswe(DisasContext *s, DisasOps *o) tcg_gen_qemu_ld_i64(t1, o->in2, get_mem_index(s), MO_TEUQ | MO_ALIGN_8); tcg_gen_addi_i64(o->in2, o->in2, 8); - tcg_gen_qemu_ld64(t2, o->in2, get_mem_index(s)); - gen_helper_load_psw(cpu_env, t1, t2); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); + tcg_gen_qemu_ld_i64(t2, o->in2, get_mem_index(s), MO_TEUQ); + gen_helper_load_psw(tcg_env, t1, t2); return DISAS_NORETURN; } #endif static DisasJumpType op_lam(DisasContext *s, DisasOps *o) { - 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); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); + + gen_helper_lam(tcg_env, r1, o->in2, r3); return DISAS_NEXT; } @@ -3114,25 +3019,22 @@ static DisasJumpType op_lm32(DisasContext *s, DisasOps *o) /* Only one register to read. */ t1 = tcg_temp_new_i64(); if (unlikely(r1 == r3)) { - tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(t1, o->in2, get_mem_index(s), MO_TEUL); store_reg32_i64(r1, t1); - tcg_temp_free(t1); return DISAS_NEXT; } /* First load the values of the first and last registers to trigger possible page faults. */ t2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(t1, o->in2, get_mem_index(s), MO_TEUL); tcg_gen_addi_i64(t2, o->in2, 4 * ((r3 - r1) & 15)); - tcg_gen_qemu_ld32u(t2, t2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(t2, t2, get_mem_index(s), MO_TEUL); store_reg32_i64(r1, t1); store_reg32_i64(r3, t2); /* Only two registers to read. */ if (((r1 + 1) & 15) == r3) { - tcg_temp_free(t2); - tcg_temp_free(t1); return DISAS_NEXT; } @@ -3142,12 +3044,9 @@ static DisasJumpType op_lm32(DisasContext *s, DisasOps *o) while (r1 != r3) { r1 = (r1 + 1) & 15; tcg_gen_add_i64(o->in2, o->in2, t2); - tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(t1, o->in2, get_mem_index(s), MO_TEUL); store_reg32_i64(r1, t1); } - tcg_temp_free(t2); - tcg_temp_free(t1); - return DISAS_NEXT; } @@ -3160,25 +3059,22 @@ static DisasJumpType op_lmh(DisasContext *s, DisasOps *o) /* Only one register to read. */ t1 = tcg_temp_new_i64(); if (unlikely(r1 == r3)) { - tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(t1, o->in2, get_mem_index(s), MO_TEUL); store_reg32h_i64(r1, t1); - tcg_temp_free(t1); return DISAS_NEXT; } /* First load the values of the first and last registers to trigger possible page faults. */ t2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(t1, o->in2, get_mem_index(s), MO_TEUL); tcg_gen_addi_i64(t2, o->in2, 4 * ((r3 - r1) & 15)); - tcg_gen_qemu_ld32u(t2, t2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(t2, t2, get_mem_index(s), MO_TEUL); store_reg32h_i64(r1, t1); store_reg32h_i64(r3, t2); /* Only two registers to read. */ if (((r1 + 1) & 15) == r3) { - tcg_temp_free(t2); - tcg_temp_free(t1); return DISAS_NEXT; } @@ -3188,12 +3084,9 @@ static DisasJumpType op_lmh(DisasContext *s, DisasOps *o) while (r1 != r3) { r1 = (r1 + 1) & 15; tcg_gen_add_i64(o->in2, o->in2, t2); - tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(t1, o->in2, get_mem_index(s), MO_TEUL); store_reg32h_i64(r1, t1); } - tcg_temp_free(t2); - tcg_temp_free(t1); - return DISAS_NEXT; } @@ -3205,7 +3098,7 @@ static DisasJumpType op_lm64(DisasContext *s, DisasOps *o) /* Only one register to read. */ if (unlikely(r1 == r3)) { - tcg_gen_qemu_ld64(regs[r1], o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(regs[r1], o->in2, get_mem_index(s), MO_TEUQ); return DISAS_NEXT; } @@ -3213,15 +3106,13 @@ static DisasJumpType op_lm64(DisasContext *s, DisasOps *o) possible page faults. */ t1 = tcg_temp_new_i64(); t2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld64(t1, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(t1, o->in2, get_mem_index(s), MO_TEUQ); tcg_gen_addi_i64(t2, o->in2, 8 * ((r3 - r1) & 15)); - tcg_gen_qemu_ld64(regs[r3], t2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(regs[r3], t2, get_mem_index(s), MO_TEUQ); tcg_gen_mov_i64(regs[r1], t1); - tcg_temp_free(t2); /* Only two registers to read. */ if (((r1 + 1) & 15) == r3) { - tcg_temp_free(t1); return DISAS_NEXT; } @@ -3231,10 +3122,8 @@ static DisasJumpType op_lm64(DisasContext *s, DisasOps *o) while (r1 != r3) { r1 = (r1 + 1) & 15; tcg_gen_add_i64(o->in2, o->in2, t1); - tcg_gen_qemu_ld64(regs[r1], o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(regs[r1], o->in2, get_mem_index(s), MO_TEUQ); } - tcg_temp_free(t1); - return DISAS_NEXT; } @@ -3256,8 +3145,6 @@ static DisasJumpType op_lpd(DisasContext *s, DisasOps *o) 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); - tcg_temp_free_i64(a2); /* ... and indicate that we performed them while interlocked. */ gen_op_movi_cc(s, 0); @@ -3266,15 +3153,9 @@ static DisasJumpType op_lpd(DisasContext *s, DisasOps *o) static DisasJumpType op_lpq(DisasContext *s, DisasOps *o) { - if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_helper_lpq(o->out, cpu_env, o->in2); - } else if (HAVE_ATOMIC128) { - gen_helper_lpq_parallel(o->out, cpu_env, o->in2); - } else { - gen_helper_exit_atomic(cpu_env); - return DISAS_NORETURN; - } - return_low128(o->out2); + o->out_128 = tcg_temp_new_i128(); + tcg_gen_qemu_ld_i128(o->out_128, o->in2, get_mem_index(s), + MO_TE | MO_128 | MO_ALIGN); return DISAS_NEXT; } @@ -3311,20 +3192,16 @@ static DisasJumpType op_lcbb(DisasContext *s, DisasOps *o) static DisasJumpType op_mc(DisasContext *s, DisasOps *o) { -#if !defined(CONFIG_USER_ONLY) - TCGv_i32 i2; -#endif - const uint16_t monitor_class = get_field(s, i2); + const uint8_t monitor_class = get_field(s, i2); - if (monitor_class & 0xff00) { + if (monitor_class & 0xf0) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } #if !defined(CONFIG_USER_ONLY) - i2 = tcg_const_i32(monitor_class); - gen_helper_monitor_call(cpu_env, o->addr1, i2); - tcg_temp_free_i32(i2); + gen_helper_monitor_call(tcg_env, o->addr1, + tcg_constant_i32(monitor_class)); #endif /* Defaults to a NOP. */ return DISAS_NEXT; @@ -3333,9 +3210,7 @@ static DisasJumpType op_mc(DisasContext *s, DisasOps *o) static DisasJumpType op_mov2(DisasContext *s, DisasOps *o) { o->out = o->in2; - o->g_out = o->g_in2; o->in2 = NULL; - o->g_in2 = false; return DISAS_NEXT; } @@ -3343,11 +3218,10 @@ static DisasJumpType op_mov2e(DisasContext *s, DisasOps *o) { int b2 = get_field(s, b2); TCGv ar1 = tcg_temp_new_i64(); + int r1 = get_field(s, r1); o->out = o->in2; - o->g_out = o->g_in2; o->in2 = NULL; - o->g_in2 = false; switch (s->base.tb->flags & FLAG_MASK_ASC) { case PSW_ASC_PRIMARY >> FLAG_MASK_PSW_SHIFT: @@ -3358,7 +3232,7 @@ static DisasJumpType op_mov2e(DisasContext *s, DisasOps *o) break; case PSW_ASC_SECONDARY >> FLAG_MASK_PSW_SHIFT: if (b2) { - tcg_gen_ld32u_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[b2])); + tcg_gen_ld32u_i64(ar1, tcg_env, offsetof(CPUS390XState, aregs[b2])); } else { tcg_gen_movi_i64(ar1, 0); } @@ -3368,9 +3242,7 @@ static DisasJumpType op_mov2e(DisasContext *s, DisasOps *o) break; } - tcg_gen_st32_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[1])); - tcg_temp_free_i64(ar1); - + tcg_gen_st32_i64(ar1, tcg_env, offsetof(CPUS390XState, aregs[r1])); return DISAS_NEXT; } @@ -3378,33 +3250,30 @@ static DisasJumpType op_movx(DisasContext *s, DisasOps *o) { o->out = o->in1; o->out2 = o->in2; - o->g_out = o->g_in1; - o->g_out2 = o->g_in2; o->in1 = NULL; o->in2 = NULL; - o->g_in1 = o->g_in2 = false; return DISAS_NEXT; } static DisasJumpType op_mvc(DisasContext *s, DisasOps *o) { - 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); + TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); + + gen_helper_mvc(tcg_env, l, o->addr1, o->in2); return DISAS_NEXT; } static DisasJumpType op_mvcrl(DisasContext *s, DisasOps *o) { - gen_helper_mvcrl(cpu_env, regs[0], o->addr1, o->in2); + gen_helper_mvcrl(tcg_env, regs[0], o->addr1, o->in2); return DISAS_NEXT; } static DisasJumpType op_mvcin(DisasContext *s, DisasOps *o) { - TCGv_i32 l = tcg_const_i32(get_field(s, l1)); - gen_helper_mvcin(cpu_env, l, o->addr1, o->in2); - tcg_temp_free_i32(l); + TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); + + gen_helper_mvcin(tcg_env, l, o->addr1, o->in2); return DISAS_NEXT; } @@ -3420,11 +3289,9 @@ static DisasJumpType op_mvcl(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - t1 = tcg_const_i32(r1); - t2 = tcg_const_i32(r2); - gen_helper_mvcl(cc_op, cpu_env, t1, t2); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); + t1 = tcg_constant_i32(r1); + t2 = tcg_constant_i32(r2); + gen_helper_mvcl(cc_op, tcg_env, t1, t2); set_cc_static(s); return DISAS_NEXT; } @@ -3441,11 +3308,9 @@ static DisasJumpType op_mvcle(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - t1 = tcg_const_i32(r1); - t3 = tcg_const_i32(r3); - gen_helper_mvcle(cc_op, cpu_env, t1, o->in2, t3); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t3); + t1 = tcg_constant_i32(r1); + t3 = tcg_constant_i32(r3); + gen_helper_mvcle(cc_op, tcg_env, t1, o->in2, t3); set_cc_static(s); return DISAS_NEXT; } @@ -3462,11 +3327,9 @@ static DisasJumpType op_mvclu(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - t1 = tcg_const_i32(r1); - t3 = tcg_const_i32(r3); - gen_helper_mvclu(cc_op, cpu_env, t1, o->in2, t3); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t3); + t1 = tcg_constant_i32(r1); + t3 = tcg_constant_i32(r3); + gen_helper_mvclu(cc_op, tcg_env, t1, o->in2, t3); set_cc_static(s); return DISAS_NEXT; } @@ -3474,7 +3337,7 @@ static DisasJumpType op_mvclu(DisasContext *s, DisasOps *o) static DisasJumpType op_mvcos(DisasContext *s, DisasOps *o) { int r3 = get_field(s, r3); - gen_helper_mvcos(cc_op, cpu_env, o->addr1, o->in2, regs[r3]); + gen_helper_mvcos(cc_op, tcg_env, o->addr1, o->in2, regs[r3]); set_cc_static(s); return DISAS_NEXT; } @@ -3483,7 +3346,8 @@ static DisasJumpType op_mvcos(DisasContext *s, DisasOps *o) static DisasJumpType op_mvcp(DisasContext *s, DisasOps *o) { int r1 = get_field(s, l1); - gen_helper_mvcp(cc_op, cpu_env, regs[r1], o->addr1, o->in2); + int r3 = get_field(s, r3); + gen_helper_mvcp(cc_op, tcg_env, regs[r1], o->addr1, o->in2, regs[r3]); set_cc_static(s); return DISAS_NEXT; } @@ -3491,7 +3355,8 @@ static DisasJumpType op_mvcp(DisasContext *s, DisasOps *o) static DisasJumpType op_mvcs(DisasContext *s, DisasOps *o) { int r1 = get_field(s, l1); - gen_helper_mvcs(cc_op, cpu_env, regs[r1], o->addr1, o->in2); + int r3 = get_field(s, r3); + gen_helper_mvcs(cc_op, tcg_env, regs[r1], o->addr1, o->in2, regs[r3]); set_cc_static(s); return DISAS_NEXT; } @@ -3499,49 +3364,45 @@ 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, l1)); - gen_helper_mvn(cpu_env, l, o->addr1, o->in2); - tcg_temp_free_i32(l); + TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); + + gen_helper_mvn(tcg_env, l, o->addr1, o->in2); return DISAS_NEXT; } static DisasJumpType op_mvo(DisasContext *s, DisasOps *o) { - 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); + TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); + + gen_helper_mvo(tcg_env, l, o->addr1, o->in2); return DISAS_NEXT; } static DisasJumpType op_mvpg(DisasContext *s, DisasOps *o) { - TCGv_i32 t1 = tcg_const_i32(get_field(s, r1)); - TCGv_i32 t2 = tcg_const_i32(get_field(s, r2)); + TCGv_i32 t1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 t2 = tcg_constant_i32(get_field(s, r2)); - gen_helper_mvpg(cc_op, cpu_env, regs[0], t1, t2); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); + gen_helper_mvpg(cc_op, tcg_env, regs[0], t1, t2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_mvst(DisasContext *s, DisasOps *o) { - TCGv_i32 t1 = tcg_const_i32(get_field(s, r1)); - TCGv_i32 t2 = tcg_const_i32(get_field(s, r2)); + TCGv_i32 t1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 t2 = tcg_constant_i32(get_field(s, r2)); - gen_helper_mvst(cc_op, cpu_env, t1, t2); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); + gen_helper_mvst(cc_op, tcg_env, t1, t2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_mvz(DisasContext *s, DisasOps *o) { - 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); + TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); + + gen_helper_mvz(tcg_env, l, o->addr1, o->in2); return DISAS_NEXT; } @@ -3565,77 +3426,69 @@ static DisasJumpType op_muls128(DisasContext *s, DisasOps *o) static DisasJumpType op_meeb(DisasContext *s, DisasOps *o) { - gen_helper_meeb(o->out, cpu_env, o->in1, o->in2); + gen_helper_meeb(o->out, tcg_env, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_mdeb(DisasContext *s, DisasOps *o) { - gen_helper_mdeb(o->out, cpu_env, o->in1, o->in2); + gen_helper_mdeb(o->out, tcg_env, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_mdb(DisasContext *s, DisasOps *o) { - gen_helper_mdb(o->out, cpu_env, o->in1, o->in2); + gen_helper_mdb(o->out, tcg_env, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_mxb(DisasContext *s, DisasOps *o) { - gen_helper_mxb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); - return_low128(o->out2); + gen_helper_mxb(o->out_128, tcg_env, o->in1_128, o->in2_128); return DISAS_NEXT; } static DisasJumpType op_mxdb(DisasContext *s, DisasOps *o) { - gen_helper_mxdb(o->out, cpu_env, o->out, o->out2, o->in2); - return_low128(o->out2); + gen_helper_mxdb(o->out_128, tcg_env, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_maeb(DisasContext *s, DisasOps *o) { 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); + gen_helper_maeb(o->out, tcg_env, o->in1, o->in2, r3); return DISAS_NEXT; } static DisasJumpType op_madb(DisasContext *s, DisasOps *o) { 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); + gen_helper_madb(o->out, tcg_env, o->in1, o->in2, r3); return DISAS_NEXT; } static DisasJumpType op_mseb(DisasContext *s, DisasOps *o) { 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); + gen_helper_mseb(o->out, tcg_env, o->in1, o->in2, r3); return DISAS_NEXT; } static DisasJumpType op_msdb(DisasContext *s, DisasOps *o) { 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); + gen_helper_msdb(o->out, tcg_env, o->in1, o->in2, r3); return DISAS_NEXT; } static DisasJumpType op_nabs(DisasContext *s, DisasOps *o) { - TCGv_i64 z, n; - z = tcg_const_i64(0); - n = tcg_temp_new_i64(); + TCGv_i64 z = tcg_constant_i64(0); + TCGv_i64 n = tcg_temp_new_i64(); + tcg_gen_neg_i64(n, o->in2); tcg_gen_movcond_i64(TCG_COND_GE, o->out, o->in2, z, n, o->in2); - tcg_temp_free_i64(n); - tcg_temp_free_i64(z); return DISAS_NEXT; } @@ -3660,9 +3513,9 @@ 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, l1)); - gen_helper_nc(cc_op, cpu_env, l, o->addr1, o->in2); - tcg_temp_free_i32(l); + TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); + + gen_helper_nc(cc_op, tcg_env, l, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -3694,9 +3547,9 @@ 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, l1)); - gen_helper_oc(cc_op, cpu_env, l, o->addr1, o->in2); - tcg_temp_free_i32(l); + TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); + + gen_helper_oc(cc_op, tcg_env, l, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -3712,10 +3565,10 @@ static DisasJumpType op_ori(DisasContext *s, DisasOps *o) int shift = s->insn->data & 0xff; int size = s->insn->data >> 8; uint64_t mask = ((1ull << size) - 1) << shift; + TCGv_i64 t = tcg_temp_new_i64(); - assert(!o->g_in2); - tcg_gen_shli_i64(o->in2, o->in2, shift); - tcg_gen_or_i64(o->out, o->in1, o->in2); + tcg_gen_shli_i64(t, o->in2, shift); + tcg_gen_or_i64(o->out, o->in1, t); /* Produce the CC from only the bits manipulated. */ tcg_gen_andi_i64(cc_dst, o->out, mask); @@ -3746,9 +3599,9 @@ 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, l1)); - gen_helper_pack(cpu_env, l, o->addr1, o->in2); - tcg_temp_free_i32(l); + TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); + + gen_helper_pack(tcg_env, l, o->addr1, o->in2); return DISAS_NEXT; } @@ -3762,9 +3615,8 @@ static DisasJumpType op_pka(DisasContext *s, DisasOps *o) gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - l = tcg_const_i32(l2); - gen_helper_pka(cpu_env, o->addr1, o->in2, l); - tcg_temp_free_i32(l); + l = tcg_constant_i32(l2); + gen_helper_pka(tcg_env, o->addr1, o->in2, l); return DISAS_NEXT; } @@ -3778,9 +3630,8 @@ static DisasJumpType op_pku(DisasContext *s, DisasOps *o) gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - l = tcg_const_i32(l2); - gen_helper_pku(cpu_env, o->addr1, o->in2, l); - tcg_temp_free_i32(l); + l = tcg_constant_i32(l2); + gen_helper_pku(tcg_env, o->addr1, o->in2, l); return DISAS_NEXT; } @@ -3799,7 +3650,7 @@ static DisasJumpType op_popcnt(DisasContext *s, DisasOps *o) #ifndef CONFIG_USER_ONLY static DisasJumpType op_ptlb(DisasContext *s, DisasOps *o) { - gen_helper_ptlb(cpu_env); + gen_helper_ptlb(tcg_env); return DISAS_NEXT; } #endif @@ -3897,12 +3748,15 @@ static DisasJumpType op_rosbg(DisasContext *s, DisasOps *o) int i3 = get_field(s, i3); int i4 = get_field(s, i4); int i5 = get_field(s, i5); + TCGv_i64 orig_out; uint64_t mask; /* If this is a test-only form, arrange to discard the result. */ if (i3 & 0x80) { + tcg_debug_assert(o->out != NULL); + orig_out = o->out; o->out = tcg_temp_new_i64(); - o->g_out = false; + tcg_gen_mov_i64(o->out, orig_out); } i3 &= 63; @@ -3972,9 +3826,6 @@ static DisasJumpType op_rll32(DisasContext *s, DisasOps *o) tcg_gen_extrl_i64_i32(t2, o->in2); tcg_gen_rotl_i32(to, t1, t2); tcg_gen_extu_i32_i64(o->out, to); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(to); return DISAS_NEXT; } @@ -3987,16 +3838,16 @@ static DisasJumpType op_rll64(DisasContext *s, DisasOps *o) #ifndef CONFIG_USER_ONLY static DisasJumpType op_rrbe(DisasContext *s, DisasOps *o) { - gen_helper_rrbe(cc_op, cpu_env, o->in2); + gen_helper_rrbe(cc_op, tcg_env, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_sacf(DisasContext *s, DisasOps *o) { - gen_helper_sacf(cpu_env, o->in2); + gen_helper_sacf(tcg_env, o->in2); /* Addressing mode has changed, so end the block. */ - return DISAS_PC_STALE; + return DISAS_TOO_MANY; } #endif @@ -4027,75 +3878,71 @@ static DisasJumpType op_sam(DisasContext *s, DisasOps *o) } s->pc_tmp &= mask; - tsam = tcg_const_i64(sam); + tsam = tcg_constant_i64(sam); tcg_gen_deposit_i64(psw_mask, psw_mask, tsam, 31, 2); - tcg_temp_free_i64(tsam); /* Always exit the TB, since we (may have) changed execution mode. */ - return DISAS_PC_STALE; + return DISAS_TOO_MANY; } static DisasJumpType op_sar(DisasContext *s, DisasOps *o) { int r1 = get_field(s, r1); - tcg_gen_st32_i64(o->in2, cpu_env, offsetof(CPUS390XState, aregs[r1])); + tcg_gen_st32_i64(o->in2, tcg_env, offsetof(CPUS390XState, aregs[r1])); return DISAS_NEXT; } static DisasJumpType op_seb(DisasContext *s, DisasOps *o) { - gen_helper_seb(o->out, cpu_env, o->in1, o->in2); + gen_helper_seb(o->out, tcg_env, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_sdb(DisasContext *s, DisasOps *o) { - gen_helper_sdb(o->out, cpu_env, o->in1, o->in2); + gen_helper_sdb(o->out, tcg_env, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_sxb(DisasContext *s, DisasOps *o) { - gen_helper_sxb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); - return_low128(o->out2); + gen_helper_sxb(o->out_128, tcg_env, o->in1_128, o->in2_128); return DISAS_NEXT; } static DisasJumpType op_sqeb(DisasContext *s, DisasOps *o) { - gen_helper_sqeb(o->out, cpu_env, o->in2); + gen_helper_sqeb(o->out, tcg_env, o->in2); return DISAS_NEXT; } static DisasJumpType op_sqdb(DisasContext *s, DisasOps *o) { - gen_helper_sqdb(o->out, cpu_env, o->in2); + gen_helper_sqdb(o->out, tcg_env, o->in2); return DISAS_NEXT; } static DisasJumpType op_sqxb(DisasContext *s, DisasOps *o) { - gen_helper_sqxb(o->out, cpu_env, o->in1, o->in2); - return_low128(o->out2); + gen_helper_sqxb(o->out_128, tcg_env, o->in2_128); return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY static DisasJumpType op_servc(DisasContext *s, DisasOps *o) { - gen_helper_servc(cc_op, cpu_env, o->in2, o->in1); + gen_helper_servc(cc_op, tcg_env, o->in2, o->in1); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_sigp(DisasContext *s, DisasOps *o) { - 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); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); + + gen_helper_sigp(cc_op, tcg_env, o->in2, r1, r3); set_cc_static(s); - tcg_temp_free_i32(r1); - tcg_temp_free_i32(r3); return DISAS_NEXT; } #endif @@ -4119,27 +3966,24 @@ static DisasJumpType op_soc(DisasContext *s, DisasOps *o) } else { tcg_gen_brcond_i32(c.cond, c.u.s32.a, c.u.s32.b, lab); } - free_compare(&c); 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)); + tcg_gen_qemu_st_i64(regs[r1], a, get_mem_index(s), MO_TEUQ); break; case 0: /* STOC */ - tcg_gen_qemu_st32(regs[r1], a, get_mem_index(s)); + tcg_gen_qemu_st_i64(regs[r1], a, get_mem_index(s), MO_TEUL); break; case 2: /* STOCFH */ h = tcg_temp_new_i64(); tcg_gen_shri_i64(h, regs[r1], 32); - tcg_gen_qemu_st32(h, a, get_mem_index(s)); - tcg_temp_free_i64(h); + tcg_gen_qemu_st_i64(h, a, get_mem_index(s), MO_TEUL); break; default: g_assert_not_reached(); } - tcg_temp_free_i64(a); gen_set_label(lab); return DISAS_NEXT; @@ -4156,9 +4000,6 @@ static DisasJumpType op_sla(DisasContext *s, DisasOps *o) t = o->in1; } gen_op_update2_cc_i64(s, CC_OP_SLA, t, o->in2); - if (s->insn->data == 31) { - tcg_temp_free_i64(t); - } tcg_gen_shl_i64(o->out, o->in1, o->in2); /* The arithmetic left shift is curious in that it does not affect the sign bit. Copy that over from the source unchanged. */ @@ -4188,13 +4029,13 @@ static DisasJumpType op_srl(DisasContext *s, DisasOps *o) static DisasJumpType op_sfpc(DisasContext *s, DisasOps *o) { - gen_helper_sfpc(cpu_env, o->in2); + gen_helper_sfpc(tcg_env, o->in2); return DISAS_NEXT; } static DisasJumpType op_sfas(DisasContext *s, DisasOps *o) { - gen_helper_sfas(cpu_env, o->in2); + gen_helper_sfas(tcg_env, o->in2); return DISAS_NEXT; } @@ -4202,7 +4043,7 @@ static DisasJumpType op_srnm(DisasContext *s, DisasOps *o) { /* Bits other than 62 and 63 are ignored. Bit 29 is set to zero. */ tcg_gen_andi_i64(o->addr1, o->addr1, 0x3ull); - gen_helper_srnm(cpu_env, o->addr1); + gen_helper_srnm(tcg_env, o->addr1); return DISAS_NEXT; } @@ -4210,7 +4051,7 @@ static DisasJumpType op_srnmb(DisasContext *s, DisasOps *o) { /* Bits 0-55 are are ignored. */ tcg_gen_andi_i64(o->addr1, o->addr1, 0xffull); - gen_helper_srnm(cpu_env, o->addr1); + gen_helper_srnm(tcg_env, o->addr1); return DISAS_NEXT; } @@ -4222,11 +4063,9 @@ static DisasJumpType op_srnmt(DisasContext *s, DisasOps *o) tcg_gen_andi_i64(o->addr1, o->addr1, 0x7ull); /* No need to call a helper, we don't implement dfp */ - tcg_gen_ld32u_i64(tmp, cpu_env, offsetof(CPUS390XState, fpc)); + tcg_gen_ld32u_i64(tmp, tcg_env, offsetof(CPUS390XState, fpc)); tcg_gen_deposit_i64(tmp, tmp, o->addr1, 4, 3); - tcg_gen_st32_i64(tmp, cpu_env, offsetof(CPUS390XState, fpc)); - - tcg_temp_free_i64(tmp); + tcg_gen_st32_i64(tmp, tcg_env, offsetof(CPUS390XState, fpc)); return DISAS_NEXT; } @@ -4259,16 +4098,14 @@ static DisasJumpType op_ectg(DisasContext *s, DisasOps *o) gen_addi_and_wrap_i64(s, o->addr1, regs[r3], 0); /* load the third operand into r3 before modifying anything */ - tcg_gen_qemu_ld64(regs[r3], o->addr1, get_mem_index(s)); + tcg_gen_qemu_ld_i64(regs[r3], o->addr1, get_mem_index(s), MO_TEUQ); /* subtract CPU timer from first operand and store in GR0 */ - gen_helper_stpt(tmp, cpu_env); + gen_helper_stpt(tmp, tcg_env); tcg_gen_sub_i64(regs[0], o->in1, tmp); /* store second operand in GR1 */ tcg_gen_mov_i64(regs[1], o->in2); - - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -4282,27 +4119,42 @@ static DisasJumpType op_spka(DisasContext *s, DisasOps *o) static DisasJumpType op_sske(DisasContext *s, DisasOps *o) { - gen_helper_sske(cpu_env, o->in1, o->in2); + gen_helper_sske(tcg_env, o->in1, o->in2); return DISAS_NEXT; } +static void gen_check_psw_mask(DisasContext *s) +{ + TCGv_i64 reserved = tcg_temp_new_i64(); + TCGLabel *ok = gen_new_label(); + + tcg_gen_andi_i64(reserved, psw_mask, PSW_MASK_RESERVED); + tcg_gen_brcondi_i64(TCG_COND_EQ, reserved, 0, ok); + gen_program_exception(s, PGM_SPECIFICATION); + gen_set_label(ok); +} + static DisasJumpType op_ssm(DisasContext *s, DisasOps *o) { tcg_gen_deposit_i64(psw_mask, psw_mask, o->in2, 56, 8); + + gen_check_psw_mask(s); + /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return DISAS_PC_STALE_NOCHAIN; + s->exit_to_mainloop = true; + return DISAS_TOO_MANY; } static DisasJumpType op_stap(DisasContext *s, DisasOps *o) { - tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, core_id)); + tcg_gen_ld32u_i64(o->out, tcg_env, offsetof(CPUS390XState, core_id)); return DISAS_NEXT; } #endif static DisasJumpType op_stck(DisasContext *s, DisasOps *o) { - gen_helper_stck(o->out, cpu_env); + gen_helper_stck(o->out, tcg_env); /* ??? We don't implement clock states. */ gen_op_movi_cc(s, 0); return DISAS_NEXT; @@ -4313,9 +4165,9 @@ static DisasJumpType op_stcke(DisasContext *s, DisasOps *o) TCGv_i64 c1 = tcg_temp_new_i64(); TCGv_i64 c2 = tcg_temp_new_i64(); TCGv_i64 todpr = tcg_temp_new_i64(); - gen_helper_stck(c1, cpu_env); + gen_helper_stck(c1, tcg_env); /* 16 bit value store in an uint32_t (only valid bits set) */ - tcg_gen_ld32u_i64(todpr, cpu_env, offsetof(CPUS390XState, todpr)); + tcg_gen_ld32u_i64(todpr, tcg_env, offsetof(CPUS390XState, todpr)); /* Shift the 64-bit value into its place as a zero-extended 104-bit value. Note that "bit positions 64-103 are always non-zero so that they compare differently to STCK"; we set @@ -4324,12 +4176,9 @@ static DisasJumpType op_stcke(DisasContext *s, DisasOps *o) tcg_gen_shri_i64(c1, c1, 8); tcg_gen_ori_i64(c2, c2, 0x10000); tcg_gen_or_i64(c2, c2, todpr); - tcg_gen_qemu_st64(c1, o->in2, get_mem_index(s)); + tcg_gen_qemu_st_i64(c1, o->in2, get_mem_index(s), MO_TEUQ); tcg_gen_addi_i64(o->in2, o->in2, 8); - tcg_gen_qemu_st64(c2, o->in2, get_mem_index(s)); - tcg_temp_free_i64(c1); - tcg_temp_free_i64(c2); - tcg_temp_free_i64(todpr); + tcg_gen_qemu_st_i64(c2, o->in2, get_mem_index(s), MO_TEUQ); /* ??? We don't implement clock states. */ gen_op_movi_cc(s, 0); return DISAS_NEXT; @@ -4338,137 +4187,135 @@ static DisasJumpType op_stcke(DisasContext *s, DisasOps *o) #ifndef CONFIG_USER_ONLY static DisasJumpType op_sck(DisasContext *s, DisasOps *o) { - gen_helper_sck(cc_op, cpu_env, o->in2); + gen_helper_sck(cc_op, tcg_env, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_sckc(DisasContext *s, DisasOps *o) { - gen_helper_sckc(cpu_env, o->in2); + gen_helper_sckc(tcg_env, o->in2); return DISAS_NEXT; } static DisasJumpType op_sckpf(DisasContext *s, DisasOps *o) { - gen_helper_sckpf(cpu_env, regs[0]); + gen_helper_sckpf(tcg_env, regs[0]); return DISAS_NEXT; } static DisasJumpType op_stckc(DisasContext *s, DisasOps *o) { - gen_helper_stckc(o->out, cpu_env); + gen_helper_stckc(o->out, tcg_env); return DISAS_NEXT; } static DisasJumpType op_stctg(DisasContext *s, DisasOps *o) { - 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); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); + + gen_helper_stctg(tcg_env, r1, o->in2, r3); return DISAS_NEXT; } static DisasJumpType op_stctl(DisasContext *s, DisasOps *o) { - 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); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); + + gen_helper_stctl(tcg_env, r1, o->in2, r3); return DISAS_NEXT; } static DisasJumpType op_stidp(DisasContext *s, DisasOps *o) { - tcg_gen_ld_i64(o->out, cpu_env, offsetof(CPUS390XState, cpuid)); + tcg_gen_ld_i64(o->out, tcg_env, offsetof(CPUS390XState, cpuid)); return DISAS_NEXT; } static DisasJumpType op_spt(DisasContext *s, DisasOps *o) { - gen_helper_spt(cpu_env, o->in2); + gen_helper_spt(tcg_env, o->in2); return DISAS_NEXT; } static DisasJumpType op_stfl(DisasContext *s, DisasOps *o) { - gen_helper_stfl(cpu_env); + gen_helper_stfl(tcg_env); return DISAS_NEXT; } static DisasJumpType op_stpt(DisasContext *s, DisasOps *o) { - gen_helper_stpt(o->out, cpu_env); + gen_helper_stpt(o->out, tcg_env); return DISAS_NEXT; } static DisasJumpType op_stsi(DisasContext *s, DisasOps *o) { - gen_helper_stsi(cc_op, cpu_env, o->in2, regs[0], regs[1]); + gen_helper_stsi(cc_op, tcg_env, o->in2, regs[0], regs[1]); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_spx(DisasContext *s, DisasOps *o) { - gen_helper_spx(cpu_env, o->in2); + gen_helper_spx(tcg_env, o->in2); return DISAS_NEXT; } static DisasJumpType op_xsch(DisasContext *s, DisasOps *o) { - gen_helper_xsch(cpu_env, regs[1]); + gen_helper_xsch(tcg_env, regs[1]); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_csch(DisasContext *s, DisasOps *o) { - gen_helper_csch(cpu_env, regs[1]); + gen_helper_csch(tcg_env, regs[1]); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_hsch(DisasContext *s, DisasOps *o) { - gen_helper_hsch(cpu_env, regs[1]); + gen_helper_hsch(tcg_env, regs[1]); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_msch(DisasContext *s, DisasOps *o) { - gen_helper_msch(cpu_env, regs[1], o->in2); + gen_helper_msch(tcg_env, regs[1], o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_rchp(DisasContext *s, DisasOps *o) { - gen_helper_rchp(cpu_env, regs[1]); + gen_helper_rchp(tcg_env, regs[1]); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_rsch(DisasContext *s, DisasOps *o) { - gen_helper_rsch(cpu_env, regs[1]); + gen_helper_rsch(tcg_env, regs[1]); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_sal(DisasContext *s, DisasOps *o) { - gen_helper_sal(cpu_env, regs[1]); + gen_helper_sal(tcg_env, regs[1]); return DISAS_NEXT; } static DisasJumpType op_schm(DisasContext *s, DisasOps *o) { - gen_helper_schm(cpu_env, regs[1], regs[2], o->in2); + gen_helper_schm(tcg_env, regs[1], regs[2], o->in2); return DISAS_NEXT; } @@ -4487,49 +4334,49 @@ static DisasJumpType op_stcps(DisasContext *s, DisasOps *o) static DisasJumpType op_ssch(DisasContext *s, DisasOps *o) { - gen_helper_ssch(cpu_env, regs[1], o->in2); + gen_helper_ssch(tcg_env, regs[1], o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_stsch(DisasContext *s, DisasOps *o) { - gen_helper_stsch(cpu_env, regs[1], o->in2); + gen_helper_stsch(tcg_env, regs[1], o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_stcrw(DisasContext *s, DisasOps *o) { - gen_helper_stcrw(cpu_env, o->in2); + gen_helper_stcrw(tcg_env, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_tpi(DisasContext *s, DisasOps *o) { - gen_helper_tpi(cc_op, cpu_env, o->addr1); + gen_helper_tpi(cc_op, tcg_env, o->addr1); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_tsch(DisasContext *s, DisasOps *o) { - gen_helper_tsch(cpu_env, regs[1], o->in2); + gen_helper_tsch(tcg_env, regs[1], o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_chsc(DisasContext *s, DisasOps *o) { - gen_helper_chsc(cpu_env, o->in2); + gen_helper_chsc(tcg_env, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_stpx(DisasContext *s, DisasOps *o) { - tcg_gen_ld_i64(o->out, cpu_env, offsetof(CPUS390XState, psa)); + tcg_gen_ld_i64(o->out, tcg_env, offsetof(CPUS390XState, psa)); tcg_gen_andi_i64(o->out, o->out, 0x7fffe000); return DISAS_NEXT; } @@ -4544,8 +4391,7 @@ static DisasJumpType op_stnosm(DisasContext *s, DisasOps *o) restart, we'll have the wrong SYSTEM MASK in place. */ t = tcg_temp_new_i64(); tcg_gen_shri_i64(t, psw_mask, 56); - tcg_gen_qemu_st8(t, o->addr1, get_mem_index(s)); - tcg_temp_free_i64(t); + tcg_gen_qemu_st_i64(t, o->addr1, get_mem_index(s), MO_UB); if (s->fields.op == 0xac) { tcg_gen_andi_i64(psw_mask, psw_mask, @@ -4554,8 +4400,11 @@ static DisasJumpType op_stnosm(DisasContext *s, DisasOps *o) tcg_gen_ori_i64(psw_mask, psw_mask, i2 << 56); } + gen_check_psw_mask(s); + /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return DISAS_PC_STALE_NOCHAIN; + s->exit_to_mainloop = true; + return DISAS_TOO_MANY; } static DisasJumpType op_stura(DisasContext *s, DisasOps *o) @@ -4564,7 +4413,7 @@ static DisasJumpType op_stura(DisasContext *s, DisasOps *o) if (s->base.tb->flags & FLAG_MASK_PER) { update_psw_addr(s); - gen_helper_per_store_real(cpu_env); + gen_helper_per_store_real(tcg_env); } return DISAS_NEXT; } @@ -4572,42 +4421,43 @@ static DisasJumpType op_stura(DisasContext *s, DisasOps *o) static DisasJumpType op_stfle(DisasContext *s, DisasOps *o) { - gen_helper_stfle(cc_op, cpu_env, o->in2); + gen_helper_stfle(cc_op, tcg_env, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_st8(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_st8(o->in1, o->in2, get_mem_index(s)); + tcg_gen_qemu_st_i64(o->in1, o->in2, get_mem_index(s), MO_UB); return DISAS_NEXT; } static DisasJumpType op_st16(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_st16(o->in1, o->in2, get_mem_index(s)); + tcg_gen_qemu_st_i64(o->in1, o->in2, get_mem_index(s), MO_TEUW); return DISAS_NEXT; } static DisasJumpType op_st32(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_st32(o->in1, o->in2, get_mem_index(s)); + tcg_gen_qemu_st_tl(o->in1, o->in2, get_mem_index(s), + MO_TEUL | s->insn->data); return DISAS_NEXT; } static DisasJumpType op_st64(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_st64(o->in1, o->in2, get_mem_index(s)); + tcg_gen_qemu_st_i64(o->in1, o->in2, get_mem_index(s), + MO_TEUQ | s->insn->data); return DISAS_NEXT; } static DisasJumpType op_stam(DisasContext *s, DisasOps *o) { - 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); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); + + gen_helper_stam(tcg_env, r1, o->in2, r3); return DISAS_NEXT; } @@ -4622,7 +4472,7 @@ static DisasJumpType op_stcm(DisasContext *s, DisasOps *o) case 0xf: /* Effectively a 32-bit store. */ tcg_gen_shri_i64(tmp, o->in1, pos); - tcg_gen_qemu_st32(tmp, o->in2, get_mem_index(s)); + tcg_gen_qemu_st_i64(tmp, o->in2, get_mem_index(s), MO_TEUL); break; case 0xc: @@ -4630,7 +4480,7 @@ static DisasJumpType op_stcm(DisasContext *s, DisasOps *o) case 0x3: /* Effectively a 16-bit store. */ tcg_gen_shri_i64(tmp, o->in1, pos); - tcg_gen_qemu_st16(tmp, o->in2, get_mem_index(s)); + tcg_gen_qemu_st_i64(tmp, o->in2, get_mem_index(s), MO_TEUW); break; case 0x8: @@ -4639,7 +4489,7 @@ static DisasJumpType op_stcm(DisasContext *s, DisasOps *o) case 0x1: /* Effectively an 8-bit store. */ tcg_gen_shri_i64(tmp, o->in1, pos); - tcg_gen_qemu_st8(tmp, o->in2, get_mem_index(s)); + tcg_gen_qemu_st_i64(tmp, o->in2, get_mem_index(s), MO_UB); break; default: @@ -4648,7 +4498,7 @@ static DisasJumpType op_stcm(DisasContext *s, DisasOps *o) while (m3) { if (m3 & 0x8) { tcg_gen_shri_i64(tmp, o->in1, pos); - tcg_gen_qemu_st8(tmp, o->in2, get_mem_index(s)); + tcg_gen_qemu_st_i64(tmp, o->in2, get_mem_index(s), MO_UB); tcg_gen_addi_i64(o->in2, o->in2, 1); } m3 = (m3 << 1) & 0xf; @@ -4656,7 +4506,6 @@ static DisasJumpType op_stcm(DisasContext *s, DisasOps *o) } break; } - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -4665,14 +4514,11 @@ static DisasJumpType op_stm(DisasContext *s, DisasOps *o) int r1 = get_field(s, r1); int r3 = get_field(s, r3); int size = s->insn->data; - TCGv_i64 tsize = tcg_const_i64(size); + TCGv_i64 tsize = tcg_constant_i64(size); while (1) { - if (size == 8) { - tcg_gen_qemu_st64(regs[r1], o->in2, get_mem_index(s)); - } else { - tcg_gen_qemu_st32(regs[r1], o->in2, get_mem_index(s)); - } + tcg_gen_qemu_st_i64(regs[r1], o->in2, get_mem_index(s), + size == 8 ? MO_TEUQ : MO_TEUL); if (r1 == r3) { break; } @@ -4680,7 +4526,6 @@ static DisasJumpType op_stm(DisasContext *s, DisasOps *o) r1 = (r1 + 1) & 15; } - tcg_temp_free_i64(tsize); return DISAS_NEXT; } @@ -4689,60 +4534,47 @@ static DisasJumpType op_stmh(DisasContext *s, DisasOps *o) 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); + TCGv_i64 t4 = tcg_constant_i64(4); + TCGv_i64 t32 = tcg_constant_i64(32); while (1) { tcg_gen_shl_i64(t, regs[r1], t32); - tcg_gen_qemu_st32(t, o->in2, get_mem_index(s)); + tcg_gen_qemu_st_i64(t, o->in2, get_mem_index(s), MO_TEUL); if (r1 == r3) { break; } tcg_gen_add_i64(o->in2, o->in2, t4); r1 = (r1 + 1) & 15; } - - tcg_temp_free_i64(t); - tcg_temp_free_i64(t4); - tcg_temp_free_i64(t32); return DISAS_NEXT; } static DisasJumpType op_stpq(DisasContext *s, DisasOps *o) { - if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_helper_stpq(cpu_env, o->in2, o->out2, o->out); - } else if (HAVE_ATOMIC128) { - gen_helper_stpq_parallel(cpu_env, o->in2, o->out2, o->out); - } else { - gen_helper_exit_atomic(cpu_env); - return DISAS_NORETURN; - } + TCGv_i128 t16 = tcg_temp_new_i128(); + + tcg_gen_concat_i64_i128(t16, o->out2, o->out); + tcg_gen_qemu_st_i128(t16, o->in2, get_mem_index(s), + MO_TE | MO_128 | MO_ALIGN); return DISAS_NEXT; } static DisasJumpType op_srst(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); - TCGv_i32 r2 = tcg_const_i32(get_field(s, r2)); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r2 = tcg_constant_i32(get_field(s, r2)); - gen_helper_srst(cpu_env, r1, r2); - - tcg_temp_free_i32(r1); - tcg_temp_free_i32(r2); + gen_helper_srst(tcg_env, r1, r2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_srstu(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); - TCGv_i32 r2 = tcg_const_i32(get_field(s, r2)); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r2 = tcg_constant_i32(get_field(s, r2)); - gen_helper_srstu(cpu_env, r1, r2); - - tcg_temp_free_i32(r1); - tcg_temp_free_i32(r2); + gen_helper_srstu(tcg_env, r1, r2); set_cc_static(s); return DISAS_NEXT; } @@ -4800,10 +4632,9 @@ static DisasJumpType op_subb64(DisasContext *s, DisasOps *o) * Borrow is {0, -1}, so add to subtract; replicate the * borrow input to produce 128-bit -1 for the addition. */ - TCGv_i64 zero = tcg_const_i64(0); + TCGv_i64 zero = tcg_constant_i64(0); tcg_gen_add2_i64(o->out, cc_src, o->in1, zero, cc_src, cc_src); tcg_gen_sub2_i64(o->out, cc_src, o->out, cc_src, o->in2, zero); - tcg_temp_free_i64(zero); return DISAS_NEXT; } @@ -4815,13 +4646,11 @@ static DisasJumpType op_svc(DisasContext *s, DisasOps *o) update_psw_addr(s); update_cc_op(s); - 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); + t = tcg_constant_i32(get_field(s, i1) & 0xff); + tcg_gen_st_i32(t, tcg_env, offsetof(CPUS390XState, int_svc_code)); - t = tcg_const_i32(s->ilen); - tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, int_svc_ilen)); - tcg_temp_free_i32(t); + t = tcg_constant_i32(s->ilen); + tcg_gen_st_i32(t, tcg_env, offsetof(CPUS390XState, int_svc_ilen)); gen_exception(EXCP_SVC); return DISAS_NORETURN; @@ -4839,21 +4668,21 @@ static DisasJumpType op_tam(DisasContext *s, DisasOps *o) static DisasJumpType op_tceb(DisasContext *s, DisasOps *o) { - gen_helper_tceb(cc_op, cpu_env, o->in1, o->in2); + gen_helper_tceb(cc_op, tcg_env, o->in1, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_tcdb(DisasContext *s, DisasOps *o) { - gen_helper_tcdb(cc_op, cpu_env, o->in1, o->in2); + gen_helper_tcdb(cc_op, tcg_env, o->in1, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_tcxb(DisasContext *s, DisasOps *o) { - gen_helper_tcxb(cc_op, cpu_env, o->out, o->out2, o->in2); + gen_helper_tcxb(cc_op, tcg_env, o->in1_128, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -4862,14 +4691,14 @@ static DisasJumpType op_tcxb(DisasContext *s, DisasOps *o) static DisasJumpType op_testblock(DisasContext *s, DisasOps *o) { - gen_helper_testblock(cc_op, cpu_env, o->in2); + gen_helper_testblock(cc_op, tcg_env, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_tprot(DisasContext *s, DisasOps *o) { - gen_helper_tprot(cc_op, cpu_env, o->addr1, o->in2); + gen_helper_tprot(cc_op, tcg_env, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -4878,53 +4707,55 @@ 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, l1) + 1); - gen_helper_tp(cc_op, cpu_env, o->addr1, l1); - tcg_temp_free_i32(l1); + TCGv_i32 l1 = tcg_constant_i32(get_field(s, l1) + 1); + + gen_helper_tp(cc_op, tcg_env, o->addr1, l1); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_tr(DisasContext *s, DisasOps *o) { - 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); + TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); + + gen_helper_tr(tcg_env, l, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_tre(DisasContext *s, DisasOps *o) { - gen_helper_tre(o->out, cpu_env, o->out, o->out2, o->in2); - return_low128(o->out2); + TCGv_i128 pair = tcg_temp_new_i128(); + + gen_helper_tre(pair, tcg_env, o->out, o->out2, o->in2); + tcg_gen_extr_i128_i64(o->out2, o->out, pair); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_trt(DisasContext *s, DisasOps *o) { - 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); + TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); + + gen_helper_trt(cc_op, tcg_env, l, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_trtr(DisasContext *s, DisasOps *o) { - 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); + TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); + + gen_helper_trtr(cc_op, tcg_env, l, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_trXX(DisasContext *s, DisasOps *o) { - 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 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r2 = tcg_constant_i32(get_field(s, r2)); + TCGv_i32 sizes = tcg_constant_i32(s->insn->opc & 3); TCGv_i32 tst = tcg_temp_new_i32(); int m3 = get_field(s, m3); @@ -4941,31 +4772,28 @@ static DisasJumpType op_trXX(DisasContext *s, DisasOps *o) tcg_gen_ext16u_i32(tst, tst); } } - gen_helper_trXX(cc_op, cpu_env, r1, r2, tst, sizes); + gen_helper_trXX(cc_op, tcg_env, r1, r2, tst, sizes); - tcg_temp_free_i32(r1); - tcg_temp_free_i32(r2); - tcg_temp_free_i32(sizes); - tcg_temp_free_i32(tst); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_ts(DisasContext *s, DisasOps *o) { - TCGv_i32 t1 = tcg_const_i32(0xff); - tcg_gen_atomic_xchg_i32(t1, o->in2, t1, get_mem_index(s), MO_UB); + TCGv_i32 ff = tcg_constant_i32(0xff); + TCGv_i32 t1 = tcg_temp_new_i32(); + + tcg_gen_atomic_xchg_i32(t1, o->in2, ff, get_mem_index(s), MO_UB); tcg_gen_extract_i32(cc_op, t1, 7, 1); - tcg_temp_free_i32(t1); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_unpk(DisasContext *s, DisasOps *o) { - 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); + TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); + + gen_helper_unpk(tcg_env, l, o->addr1, o->in2); return DISAS_NEXT; } @@ -4979,9 +4807,8 @@ static DisasJumpType op_unpka(DisasContext *s, DisasOps *o) gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - l = tcg_const_i32(l1); - gen_helper_unpka(cc_op, cpu_env, o->addr1, l, o->in2); - tcg_temp_free_i32(l); + l = tcg_constant_i32(l1); + gen_helper_unpka(cc_op, tcg_env, o->addr1, l, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -4996,9 +4823,8 @@ static DisasJumpType op_unpku(DisasContext *s, DisasOps *o) gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - l = tcg_const_i32(l1); - gen_helper_unpku(cc_op, cpu_env, o->addr1, l, o->in2); - tcg_temp_free_i32(l); + l = tcg_constant_i32(l1); + gen_helper_unpku(cc_op, tcg_env, o->addr1, l, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -5017,32 +4843,32 @@ static DisasJumpType op_xc(DisasContext *s, DisasOps *o) /* If the addresses are identical, this is a store/memset of zero. */ if (b1 == b2 && d1 == d2 && (l + 1) <= 32) { - o->in2 = tcg_const_i64(0); + o->in2 = tcg_constant_i64(0); l++; while (l >= 8) { - tcg_gen_qemu_st64(o->in2, o->addr1, get_mem_index(s)); + tcg_gen_qemu_st_i64(o->in2, o->addr1, get_mem_index(s), MO_UQ); l -= 8; if (l > 0) { tcg_gen_addi_i64(o->addr1, o->addr1, 8); } } if (l >= 4) { - tcg_gen_qemu_st32(o->in2, o->addr1, get_mem_index(s)); + tcg_gen_qemu_st_i64(o->in2, o->addr1, get_mem_index(s), MO_UL); l -= 4; if (l > 0) { tcg_gen_addi_i64(o->addr1, o->addr1, 4); } } if (l >= 2) { - tcg_gen_qemu_st16(o->in2, o->addr1, get_mem_index(s)); + tcg_gen_qemu_st_i64(o->in2, o->addr1, get_mem_index(s), MO_UW); l -= 2; if (l > 0) { tcg_gen_addi_i64(o->addr1, o->addr1, 2); } } if (l) { - tcg_gen_qemu_st8(o->in2, o->addr1, get_mem_index(s)); + tcg_gen_qemu_st_i64(o->in2, o->addr1, get_mem_index(s), MO_UB); } gen_op_movi_cc(s, 0); return DISAS_NEXT; @@ -5050,9 +4876,8 @@ static DisasJumpType op_xc(DisasContext *s, DisasOps *o) /* But in general we'll defer to a helper. */ o->in2 = get_address(s, 0, b2, d2); - t32 = tcg_const_i32(l); - gen_helper_xc(cc_op, cpu_env, t32, o->addr1, o->in2); - tcg_temp_free_i32(t32); + t32 = tcg_constant_i32(l); + gen_helper_xc(cc_op, tcg_env, t32, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -5068,10 +4893,10 @@ static DisasJumpType op_xori(DisasContext *s, DisasOps *o) int shift = s->insn->data & 0xff; int size = s->insn->data >> 8; uint64_t mask = ((1ull << size) - 1) << shift; + TCGv_i64 t = tcg_temp_new_i64(); - assert(!o->g_in2); - tcg_gen_shli_i64(o->in2, o->in2, shift); - tcg_gen_xor_i64(o->out, o->in1, o->in2); + tcg_gen_shli_i64(t, o->in2, shift); + tcg_gen_xor_i64(o->out, o->in1, t); /* Produce the CC from only the bits manipulated. */ tcg_gen_andi_i64(cc_dst, o->out, mask); @@ -5102,105 +4927,90 @@ static DisasJumpType op_xi(DisasContext *s, DisasOps *o) static DisasJumpType op_zero(DisasContext *s, DisasOps *o) { - o->out = tcg_const_i64(0); + o->out = tcg_constant_i64(0); return DISAS_NEXT; } static DisasJumpType op_zero2(DisasContext *s, DisasOps *o) { - o->out = tcg_const_i64(0); + o->out = tcg_constant_i64(0); o->out2 = o->out; - o->g_out2 = true; return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY static DisasJumpType op_clp(DisasContext *s, DisasOps *o) { - TCGv_i32 r2 = tcg_const_i32(get_field(s, r2)); + TCGv_i32 r2 = tcg_constant_i32(get_field(s, r2)); - gen_helper_clp(cpu_env, r2); - tcg_temp_free_i32(r2); + gen_helper_clp(tcg_env, r2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_pcilg(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); - TCGv_i32 r2 = tcg_const_i32(get_field(s, r2)); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r2 = tcg_constant_i32(get_field(s, r2)); - gen_helper_pcilg(cpu_env, r1, r2); - tcg_temp_free_i32(r1); - tcg_temp_free_i32(r2); + gen_helper_pcilg(tcg_env, r1, r2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_pcistg(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); - TCGv_i32 r2 = tcg_const_i32(get_field(s, r2)); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r2 = tcg_constant_i32(get_field(s, r2)); - gen_helper_pcistg(cpu_env, r1, r2); - tcg_temp_free_i32(r1); - tcg_temp_free_i32(r2); + gen_helper_pcistg(tcg_env, r1, r2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_stpcifc(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); - TCGv_i32 ar = tcg_const_i32(get_field(s, b2)); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 ar = tcg_constant_i32(get_field(s, b2)); - gen_helper_stpcifc(cpu_env, r1, o->addr1, ar); - tcg_temp_free_i32(ar); - tcg_temp_free_i32(r1); + gen_helper_stpcifc(tcg_env, r1, o->addr1, ar); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_sic(DisasContext *s, DisasOps *o) { - gen_helper_sic(cpu_env, o->in1, o->in2); + gen_helper_sic(tcg_env, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_rpcit(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); - TCGv_i32 r2 = tcg_const_i32(get_field(s, r2)); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r2 = tcg_constant_i32(get_field(s, r2)); - gen_helper_rpcit(cpu_env, r1, r2); - tcg_temp_free_i32(r1); - tcg_temp_free_i32(r2); + gen_helper_rpcit(tcg_env, r1, r2); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_pcistb(DisasContext *s, DisasOps *o) { - 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)); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); + TCGv_i32 ar = tcg_constant_i32(get_field(s, b2)); - gen_helper_pcistb(cpu_env, r1, r3, o->addr1, ar); - tcg_temp_free_i32(ar); - tcg_temp_free_i32(r1); - tcg_temp_free_i32(r3); + gen_helper_pcistb(tcg_env, r1, r3, o->addr1, ar); set_cc_static(s); return DISAS_NEXT; } static DisasJumpType op_mpcifc(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); - TCGv_i32 ar = tcg_const_i32(get_field(s, b2)); + TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); + TCGv_i32 ar = tcg_constant_i32(get_field(s, b2)); - gen_helper_mpcifc(cpu_env, r1, o->addr1, ar); - tcg_temp_free_i32(ar); - tcg_temp_free_i32(r1); + gen_helper_mpcifc(tcg_env, r1, o->addr1, ar); set_cc_static(s); return DISAS_NEXT; } @@ -5383,10 +5193,15 @@ static void prep_new_P(DisasContext *s, DisasOps *o) } #define SPEC_prep_new_P 0 +static void prep_new_x(DisasContext *s, DisasOps *o) +{ + o->out_128 = tcg_temp_new_i128(); +} +#define SPEC_prep_new_x 0 + static void prep_r1(DisasContext *s, DisasOps *o) { o->out = regs[get_field(s, r1)]; - o->g_out = true; } #define SPEC_prep_r1 0 @@ -5395,18 +5210,9 @@ static void prep_r1_P(DisasContext *s, DisasOps *o) int r1 = get_field(s, r1); o->out = regs[r1]; o->out2 = regs[r1 + 1]; - o->g_out = o->g_out2 = true; } #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, DisasOps *o) -{ - o->out = load_freg(get_field(s, r1)); - o->out2 = load_freg(get_field(s, r1) + 2); -} -#define SPEC_prep_x1 SPEC_r1_f128 - /* ====================================================================== */ /* The "Write OUTput" generators. These generally perform some non-trivial copy of data to TCG globals, or to main memory. The trivial cases are @@ -5466,10 +5272,16 @@ static void wout_r1_D32(DisasContext *s, DisasOps *o) store_reg32_i64(r1 + 1, o->out); tcg_gen_shri_i64(t, o->out, 32); store_reg32_i64(r1, t); - tcg_temp_free_i64(t); } #define SPEC_wout_r1_D32 SPEC_r1_even +static void wout_r1_D64(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s, r1); + tcg_gen_extr_i128_i64(regs[r1 + 1], regs[r1], o->out_128); +} +#define SPEC_wout_r1_D64 SPEC_r1_even + static void wout_r3_P32(DisasContext *s, DisasOps *o) { int r3 = get_field(s, r3); @@ -5501,11 +5313,26 @@ static void wout_f1(DisasContext *s, DisasOps *o) static void wout_x1(DisasContext *s, DisasOps *o) { int f1 = get_field(s, r1); + + /* Split out_128 into out+out2 for cout_f128. */ + tcg_debug_assert(o->out == NULL); + o->out = tcg_temp_new_i64(); + o->out2 = tcg_temp_new_i64(); + + tcg_gen_extr_i128_i64(o->out2, o->out, o->out_128); store_freg(f1, o->out); store_freg(f1 + 2, o->out2); } #define SPEC_wout_x1 SPEC_r1_f128 +static void wout_x1_P(DisasContext *s, DisasOps *o) +{ + int f1 = get_field(s, r1); + store_freg(f1, o->out); + store_freg(f1 + 2, o->out2); +} +#define SPEC_wout_x1_P SPEC_r1_f128 + static void wout_cond_r1r2_32(DisasContext *s, DisasOps *o) { if (get_field(s, r1) != get_field(s, r2)) { @@ -5524,13 +5351,13 @@ static void wout_cond_e1e2(DisasContext *s, DisasOps *o) static void wout_m1_8(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_st8(o->out, o->addr1, get_mem_index(s)); + tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_UB); } #define SPEC_wout_m1_8 0 static void wout_m1_16(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_st16(o->out, o->addr1, get_mem_index(s)); + tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_TEUW); } #define SPEC_wout_m1_16 0 @@ -5544,7 +5371,7 @@ static void wout_m1_16a(DisasContext *s, DisasOps *o) static void wout_m1_32(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_st32(o->out, o->addr1, get_mem_index(s)); + tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_TEUL); } #define SPEC_wout_m1_32 0 @@ -5558,7 +5385,7 @@ static void wout_m1_32a(DisasContext *s, DisasOps *o) static void wout_m1_64(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_st64(o->out, o->addr1, get_mem_index(s)); + tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_TEUQ); } #define SPEC_wout_m1_64 0 @@ -5572,7 +5399,7 @@ static void wout_m1_64a(DisasContext *s, DisasOps *o) static void wout_m2_32(DisasContext *s, DisasOps *o) { - tcg_gen_qemu_st32(o->out, o->in2, get_mem_index(s)); + tcg_gen_qemu_st_i64(o->out, o->in2, get_mem_index(s), MO_TEUL); } #define SPEC_wout_m2_32 0 @@ -5600,7 +5427,6 @@ static void in1_r1(DisasContext *s, DisasOps *o) static void in1_r1_o(DisasContext *s, DisasOps *o) { o->in1 = regs[get_field(s, r1)]; - o->g_in1 = true; } #define SPEC_in1_r1_o 0 @@ -5634,7 +5460,6 @@ static void in1_r1p1(DisasContext *s, DisasOps *o) static void in1_r1p1_o(DisasContext *s, DisasOps *o) { o->in1 = regs[get_field(s, r1) + 1]; - o->g_in1 = true; } #define SPEC_in1_r1p1_o SPEC_r1_even @@ -5689,7 +5514,6 @@ static void in1_r3(DisasContext *s, DisasOps *o) static void in1_r3_o(DisasContext *s, DisasOps *o) { o->in1 = regs[get_field(s, r3)]; - o->g_in1 = true; } #define SPEC_in1_r3_o 0 @@ -5734,6 +5558,12 @@ static void in1_f1(DisasContext *s, DisasOps *o) } #define SPEC_in1_f1 0 +static void in1_x1(DisasContext *s, DisasOps *o) +{ + o->in1_128 = load_freg_128(get_field(s, r1)); +} +#define SPEC_in1_x1 SPEC_r1_f128 + /* Load the high double word of an extended (128-bit) format FP number */ static void in1_x2h(DisasContext *s, DisasOps *o) { @@ -5764,7 +5594,7 @@ static void in1_m1_8u(DisasContext *s, DisasOps *o) { in1_la1(s, o); o->in1 = tcg_temp_new_i64(); - tcg_gen_qemu_ld8u(o->in1, o->addr1, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->in1, o->addr1, get_mem_index(s), MO_UB); } #define SPEC_in1_m1_8u 0 @@ -5772,7 +5602,7 @@ static void in1_m1_16s(DisasContext *s, DisasOps *o) { in1_la1(s, o); o->in1 = tcg_temp_new_i64(); - tcg_gen_qemu_ld16s(o->in1, o->addr1, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->in1, o->addr1, get_mem_index(s), MO_TESW); } #define SPEC_in1_m1_16s 0 @@ -5780,7 +5610,7 @@ static void in1_m1_16u(DisasContext *s, DisasOps *o) { in1_la1(s, o); o->in1 = tcg_temp_new_i64(); - tcg_gen_qemu_ld16u(o->in1, o->addr1, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->in1, o->addr1, get_mem_index(s), MO_TEUW); } #define SPEC_in1_m1_16u 0 @@ -5788,7 +5618,7 @@ static void in1_m1_32s(DisasContext *s, DisasOps *o) { in1_la1(s, o); o->in1 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32s(o->in1, o->addr1, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->in1, o->addr1, get_mem_index(s), MO_TESL); } #define SPEC_in1_m1_32s 0 @@ -5796,7 +5626,7 @@ static void in1_m1_32u(DisasContext *s, DisasOps *o) { in1_la1(s, o); o->in1 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32u(o->in1, o->addr1, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->in1, o->addr1, get_mem_index(s), MO_TEUL); } #define SPEC_in1_m1_32u 0 @@ -5804,7 +5634,7 @@ static void in1_m1_64(DisasContext *s, DisasOps *o) { in1_la1(s, o); o->in1 = tcg_temp_new_i64(); - tcg_gen_qemu_ld64(o->in1, o->addr1, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->in1, o->addr1, get_mem_index(s), MO_TEUQ); } #define SPEC_in1_m1_64 0 @@ -5814,7 +5644,6 @@ static void in1_m1_64(DisasContext *s, DisasOps *o) static void in2_r1_o(DisasContext *s, DisasOps *o) { o->in2 = regs[get_field(s, r1)]; - o->g_in2 = true; } #define SPEC_in2_r1_o 0 @@ -5849,7 +5678,6 @@ static void in2_r2(DisasContext *s, DisasOps *o) static void in2_r2_o(DisasContext *s, DisasOps *o) { o->in2 = regs[get_field(s, r2)]; - o->g_in2 = true; } #define SPEC_in2_r2_o 0 @@ -5896,6 +5724,14 @@ static void in2_r3(DisasContext *s, DisasOps *o) } #define SPEC_in2_r3 0 +static void in2_r3_D64(DisasContext *s, DisasOps *o) +{ + int r3 = get_field(s, r3); + o->in2_128 = tcg_temp_new_i128(); + tcg_gen_concat_i64_i128(o->in2_128, regs[r3 + 1], regs[r3]); +} +#define SPEC_in2_r3_D64 SPEC_r3_even + static void in2_r3_sr32(DisasContext *s, DisasOps *o) { o->in2 = tcg_temp_new_i64(); @@ -5943,6 +5779,12 @@ static void in2_f2(DisasContext *s, DisasOps *o) } #define SPEC_in2_f2 0 +static void in2_x2(DisasContext *s, DisasOps *o) +{ + o->in2_128 = load_freg_128(get_field(s, r2)); +} +#define SPEC_in2_x2 SPEC_r2_f128 + /* Load the low double word of an extended (128-bit) format FP number */ static void in2_x2l(DisasContext *s, DisasOps *o) { @@ -5960,6 +5802,12 @@ static void in2_ra2(DisasContext *s, DisasOps *o) } #define SPEC_in2_ra2 0 +static void in2_ra2_E(DisasContext *s, DisasOps *o) +{ + return in2_ra2(s, o); +} +#define SPEC_in2_ra2_E SPEC_r2_even + static void in2_a2(DisasContext *s, DisasOps *o) { int x2 = have_field(s, x2) ? get_field(s, x2) : 0; @@ -5967,9 +5815,23 @@ static void in2_a2(DisasContext *s, DisasOps *o) } #define SPEC_in2_a2 0 +static TCGv gen_ri2(DisasContext *s) +{ + TCGv ri2 = NULL; + bool is_imm; + int imm; + + disas_jdest(s, i2, is_imm, imm, ri2); + if (is_imm) { + ri2 = tcg_constant_i64(s->base.pc_next + (int64_t)imm * 2); + } + + return ri2; +} + static void in2_ri2(DisasContext *s, DisasOps *o) { - o->in2 = tcg_const_i64(s->base.pc_next + (int64_t)get_field(s, i2) * 2); + o->in2 = gen_ri2(s); } #define SPEC_in2_ri2 0 @@ -5979,7 +5841,7 @@ static void in2_sh(DisasContext *s, DisasOps *o) int d2 = get_field(s, d2); if (b2 == 0) { - o->in2 = tcg_const_i64(d2 & 0x3f); + o->in2 = tcg_constant_i64(d2 & 0x3f); } else { o->in2 = get_address(s, 0, b2, d2); tcg_gen_andi_i64(o->in2, o->in2, 0x3f); @@ -5990,35 +5852,35 @@ static void in2_sh(DisasContext *s, DisasOps *o) static void in2_m2_8u(DisasContext *s, DisasOps *o) { in2_a2(s, o); - tcg_gen_qemu_ld8u(o->in2, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->in2, o->in2, get_mem_index(s), MO_UB); } #define SPEC_in2_m2_8u 0 static void in2_m2_16s(DisasContext *s, DisasOps *o) { in2_a2(s, o); - tcg_gen_qemu_ld16s(o->in2, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->in2, o->in2, get_mem_index(s), MO_TESW); } #define SPEC_in2_m2_16s 0 static void in2_m2_16u(DisasContext *s, DisasOps *o) { in2_a2(s, o); - tcg_gen_qemu_ld16u(o->in2, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->in2, o->in2, get_mem_index(s), MO_TEUW); } #define SPEC_in2_m2_16u 0 static void in2_m2_32s(DisasContext *s, DisasOps *o) { in2_a2(s, o); - tcg_gen_qemu_ld32s(o->in2, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->in2, o->in2, get_mem_index(s), MO_TESL); } #define SPEC_in2_m2_32s 0 static void in2_m2_32u(DisasContext *s, DisasOps *o) { in2_a2(s, o); - tcg_gen_qemu_ld32u(o->in2, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->in2, o->in2, get_mem_index(s), MO_TEUL); } #define SPEC_in2_m2_32u 0 @@ -6034,14 +5896,14 @@ static void in2_m2_32ua(DisasContext *s, DisasOps *o) static void in2_m2_64(DisasContext *s, DisasOps *o) { in2_a2(s, o); - tcg_gen_qemu_ld64(o->in2, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->in2, o->in2, get_mem_index(s), MO_TEUQ); } #define SPEC_in2_m2_64 0 static void in2_m2_64w(DisasContext *s, DisasOps *o) { in2_a2(s, o); - tcg_gen_qemu_ld64(o->in2, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(o->in2, o->in2, get_mem_index(s), MO_TEUQ); gen_addi_and_wrap_i64(s, o->in2, o->in2, 0); } #define SPEC_in2_m2_64w 0 @@ -6055,76 +5917,86 @@ static void in2_m2_64a(DisasContext *s, DisasOps *o) #define SPEC_in2_m2_64a 0 #endif +static void in2_mri2_16s(DisasContext *s, DisasOps *o) +{ + o->in2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(o->in2, gen_ri2(s), get_mem_index(s), MO_TESW); +} +#define SPEC_in2_mri2_16s 0 + static void in2_mri2_16u(DisasContext *s, DisasOps *o) { - in2_ri2(s, o); - tcg_gen_qemu_ld16u(o->in2, o->in2, get_mem_index(s)); + o->in2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(o->in2, gen_ri2(s), get_mem_index(s), MO_TEUW); } #define SPEC_in2_mri2_16u 0 static void in2_mri2_32s(DisasContext *s, DisasOps *o) { - in2_ri2(s, o); - tcg_gen_qemu_ld32s(o->in2, o->in2, get_mem_index(s)); + o->in2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_tl(o->in2, gen_ri2(s), get_mem_index(s), + MO_TESL | MO_ALIGN); } #define SPEC_in2_mri2_32s 0 static void in2_mri2_32u(DisasContext *s, DisasOps *o) { - in2_ri2(s, o); - tcg_gen_qemu_ld32u(o->in2, o->in2, get_mem_index(s)); + o->in2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_tl(o->in2, gen_ri2(s), get_mem_index(s), + MO_TEUL | MO_ALIGN); } #define SPEC_in2_mri2_32u 0 static void in2_mri2_64(DisasContext *s, DisasOps *o) { - in2_ri2(s, o); - tcg_gen_qemu_ld64(o->in2, o->in2, get_mem_index(s)); + o->in2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(o->in2, gen_ri2(s), get_mem_index(s), + MO_TEUQ | MO_ALIGN); } #define SPEC_in2_mri2_64 0 static void in2_i2(DisasContext *s, DisasOps *o) { - o->in2 = tcg_const_i64(get_field(s, i2)); + o->in2 = tcg_constant_i64(get_field(s, i2)); } #define SPEC_in2_i2 0 static void in2_i2_8u(DisasContext *s, DisasOps *o) { - o->in2 = tcg_const_i64((uint8_t)get_field(s, i2)); + o->in2 = tcg_constant_i64((uint8_t)get_field(s, i2)); } #define SPEC_in2_i2_8u 0 static void in2_i2_16u(DisasContext *s, DisasOps *o) { - o->in2 = tcg_const_i64((uint16_t)get_field(s, i2)); + o->in2 = tcg_constant_i64((uint16_t)get_field(s, i2)); } #define SPEC_in2_i2_16u 0 static void in2_i2_32u(DisasContext *s, DisasOps *o) { - o->in2 = tcg_const_i64((uint32_t)get_field(s, i2)); + o->in2 = tcg_constant_i64((uint32_t)get_field(s, i2)); } #define SPEC_in2_i2_32u 0 static void in2_i2_16u_shl(DisasContext *s, DisasOps *o) { uint64_t i2 = (uint16_t)get_field(s, i2); - o->in2 = tcg_const_i64(i2 << s->insn->data); + o->in2 = tcg_constant_i64(i2 << s->insn->data); } #define SPEC_in2_i2_16u_shl 0 static void in2_i2_32u_shl(DisasContext *s, DisasOps *o) { uint64_t i2 = (uint32_t)get_field(s, i2); - o->in2 = tcg_const_i64(i2 << s->insn->data); + o->in2 = tcg_constant_i64(i2 << s->insn->data); } #define SPEC_in2_i2_32u_shl 0 #ifndef CONFIG_USER_ONLY static void in2_insn(DisasContext *s, DisasOps *o) { - o->in2 = tcg_const_i64(s->fields.raw_insn); + o->in2 = tcg_constant_i64(s->fields.raw_insn); } #define SPEC_in2_insn 0 #endif @@ -6148,7 +6020,7 @@ static void in2_insn(DisasContext *s, DisasOps *o) #define E(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D, FL) insn_ ## NM, enum DisasInsnEnum { -#include "insn-data.def" +#include "insn-data.h.inc" }; #undef E @@ -6228,7 +6100,7 @@ enum DisasInsnEnum { #define FAC_MIE3 S390_FEAT_MISC_INSTRUCTION_EXT3 /* miscellaneous-instruction-extensions facility 3 */ static const DisasInsn insn_info[] = { -#include "insn-data.def" +#include "insn-data.h.inc" }; #undef E @@ -6238,7 +6110,7 @@ static const DisasInsn insn_info[] = { static const DisasInsn *lookup_opc(uint16_t opc) { switch (opc) { -#include "insn-data.def" +#include "insn-data.h.inc" default: return NULL; } @@ -6321,13 +6193,18 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s) if (unlikely(s->ex_value)) { /* Drop the EX data now, so that it's clear on exception paths. */ - TCGv_i64 zero = tcg_const_i64(0); - tcg_gen_st_i64(zero, cpu_env, offsetof(CPUS390XState, ex_value)); - tcg_temp_free_i64(zero); + tcg_gen_st_i64(tcg_constant_i64(0), tcg_env, + offsetof(CPUS390XState, ex_value)); /* Extract the values saved by EXECUTE. */ insn = s->ex_value & 0xffffffffffff0000ull; ilen = s->ex_value & 0xf; + + /* Register insn bytes with translator so plugins work. */ + for (int i = 0; i < ilen; i++) { + uint8_t byte = extract64(insn, 56 - (i * 8), 8); + translator_fake_ldb(byte, pc + i); + } op = insn >> 56; } else { insn = ld_code2(env, s, pc); @@ -6436,7 +6313,7 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) insn = extract_insn(env, s); /* Update insn_start now that we know the ILEN. */ - tcg_set_insn_start_param(s->insn_start, 2, s->ilen); + tcg_set_insn_start_param(s->base.insn_start, 2, s->ilen); /* Not found means unimplemented/illegal opcode. */ if (insn == NULL) { @@ -6449,9 +6326,8 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) #ifndef CONFIG_USER_ONLY if (s->base.tb->flags & FLAG_MASK_PER) { - TCGv_i64 addr = tcg_const_i64(s->base.pc_next); - gen_helper_per_ifetch(cpu_env, addr); - tcg_temp_free_i64(addr); + TCGv_i64 addr = tcg_constant_i64(s->base.pc_next); + gen_helper_per_ifetch(tcg_env, addr); } #endif @@ -6504,10 +6380,7 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) /* input/output is the special case for icount mode */ if (unlikely(insn->flags & IF_IO)) { - icount = tb_cflags(s->base.tb) & CF_USE_ICOUNT; - if (icount) { - gen_io_start(); - } + icount = translator_io_start(&s->base); } } @@ -6546,37 +6419,20 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } } - /* Free any temporaries created by the helpers. */ - if (o.out && !o.g_out) { - tcg_temp_free_i64(o.out); - } - if (o.out2 && !o.g_out2) { - tcg_temp_free_i64(o.out2); - } - if (o.in1 && !o.g_in1) { - tcg_temp_free_i64(o.in1); - } - if (o.in2 && !o.g_in2) { - tcg_temp_free_i64(o.in2); - } - if (o.addr1) { - tcg_temp_free_i64(o.addr1); - } - /* io should be the last instruction in tb when icount is enabled */ if (unlikely(icount && ret == DISAS_NEXT)) { - ret = DISAS_PC_STALE; + ret = DISAS_TOO_MANY; } #ifndef CONFIG_USER_ONLY if (s->base.tb->flags & FLAG_MASK_PER) { /* An exception might be triggered, save PSW if not already done. */ - if (ret == DISAS_NEXT || ret == DISAS_PC_STALE) { + if (ret == DISAS_NEXT || ret == DISAS_TOO_MANY) { tcg_gen_movi_i64(psw_addr, s->pc_tmp); } /* Call the helper to check for a possible PER exception. */ - gen_helper_per_check_exception(cpu_env); + gen_helper_per_check_exception(tcg_env); } #endif @@ -6598,6 +6454,7 @@ static void s390x_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->cc_op = CC_OP_DYNAMIC; dc->ex_value = dc->base.tb->cs_base; + dc->exit_to_mainloop = (dc->base.tb->flags & FLAG_MASK_PER) || dc->ex_value; } static void s390x_tr_tb_start(DisasContextBase *db, CPUState *cs) @@ -6610,20 +6467,26 @@ static void s390x_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) /* Delay the set of ilen until we've read the insn. */ tcg_gen_insn_start(dc->base.pc_next, dc->cc_op, 0); - dc->insn_start = tcg_last_op(); +} + +static target_ulong get_next_pc(CPUS390XState *env, DisasContext *s, + uint64_t pc) +{ + uint64_t insn = cpu_lduw_code(env, pc); + + return pc + get_ilen((insn >> 8) & 0xff); } static void s390x_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) { - CPUS390XState *env = cs->env_ptr; + CPUS390XState *env = cpu_env(cs); DisasContext *dc = container_of(dcbase, DisasContext, base); dc->base.is_jmp = translate_one(env, dc); if (dc->base.is_jmp == DISAS_NEXT) { - uint64_t page_start; - - page_start = dc->base.pc_first & TARGET_PAGE_MASK; - if (dc->base.pc_next - page_start >= TARGET_PAGE_SIZE || dc->ex_value) { + if (dc->ex_value || + !is_same_page(dcbase, dc->base.pc_next) || + !is_same_page(dcbase, get_next_pc(env, dc, dc->base.pc_next))) { dc->base.is_jmp = DISAS_TOO_MANY; } } @@ -6634,12 +6497,9 @@ static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) DisasContext *dc = container_of(dcbase, DisasContext, base); switch (dc->base.is_jmp) { - case DISAS_GOTO_TB: case DISAS_NORETURN: break; case DISAS_TOO_MANY: - case DISAS_PC_STALE: - case DISAS_PC_STALE_NOCHAIN: update_psw_addr(dc); /* FALLTHRU */ case DISAS_PC_UPDATED: @@ -6649,8 +6509,7 @@ static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) /* FALLTHRU */ case DISAS_PC_CC_UPDATED: /* Exit the TB, either by raising a debug exception or by return. */ - if ((dc->base.tb->flags & FLAG_MASK_PER) || - dc->base.is_jmp == DISAS_PC_STALE_NOCHAIN) { + if (dc->exit_to_mainloop) { tcg_gen_exit_tb(NULL, 0); } else { tcg_gen_lookup_and_goto_ptr(); @@ -6684,16 +6543,19 @@ static const TranslatorOps s390x_tr_ops = { .disas_log = s390x_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc; - translator_loop(&s390x_tr_ops, &dc.base, cs, tb, max_insns); + translator_loop(cs, tb, max_insns, pc, host_pc, &s390x_tr_ops, &dc.base); } -void restore_state_to_opc(CPUS390XState *env, TranslationBlock *tb, - target_ulong *data) +void s390x_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) { + CPUS390XState *env = cpu_env(cs); int cc_op = data[1]; env->psw.addr = data[0]; diff --git a/target/s390x/tcg/translate_vx.c.inc b/target/s390x/tcg/translate_vx.c.inc index 3526ba3e3b..e073e5ad3a 100644 --- a/target/s390x/tcg/translate_vx.c.inc +++ b/target/s390x/tcg/translate_vx.c.inc @@ -36,7 +36,7 @@ * * CC handling: * As gvec ool-helpers can currently not return values (besides via - * pointers like vectors or cpu_env), whenever we have to set the CC and + * pointers like vectors or tcg_env), whenever we have to set the CC and * can't conclude the value from the result vector, we will directly * set it in "env->cc_op" and mark it as static via set_cc_static()". * Whenever this is done, the helper writes globals (cc_op). @@ -57,7 +57,7 @@ #define FPF_LONG 3 #define FPF_EXT 4 -static inline bool valid_vec_element(uint8_t enr, MemOp es) +static inline bool valid_vec_element(uint16_t enr, MemOp es) { return !(enr & ~(NUM_VEC_ELEMENTS(es) - 1)); } @@ -69,26 +69,26 @@ static void read_vec_element_i64(TCGv_i64 dst, uint8_t reg, uint8_t enr, switch ((unsigned)memop) { case ES_8: - tcg_gen_ld8u_i64(dst, cpu_env, offs); + tcg_gen_ld8u_i64(dst, tcg_env, offs); break; case ES_16: - tcg_gen_ld16u_i64(dst, cpu_env, offs); + tcg_gen_ld16u_i64(dst, tcg_env, offs); break; case ES_32: - tcg_gen_ld32u_i64(dst, cpu_env, offs); + tcg_gen_ld32u_i64(dst, tcg_env, offs); break; case ES_8 | MO_SIGN: - tcg_gen_ld8s_i64(dst, cpu_env, offs); + tcg_gen_ld8s_i64(dst, tcg_env, offs); break; case ES_16 | MO_SIGN: - tcg_gen_ld16s_i64(dst, cpu_env, offs); + tcg_gen_ld16s_i64(dst, tcg_env, offs); break; case ES_32 | MO_SIGN: - tcg_gen_ld32s_i64(dst, cpu_env, offs); + tcg_gen_ld32s_i64(dst, tcg_env, offs); break; case ES_64: case ES_64 | MO_SIGN: - tcg_gen_ld_i64(dst, cpu_env, offs); + tcg_gen_ld_i64(dst, tcg_env, offs); break; default: g_assert_not_reached(); @@ -102,20 +102,20 @@ static void read_vec_element_i32(TCGv_i32 dst, uint8_t reg, uint8_t enr, switch (memop) { case ES_8: - tcg_gen_ld8u_i32(dst, cpu_env, offs); + tcg_gen_ld8u_i32(dst, tcg_env, offs); break; case ES_16: - tcg_gen_ld16u_i32(dst, cpu_env, offs); + tcg_gen_ld16u_i32(dst, tcg_env, offs); break; case ES_8 | MO_SIGN: - tcg_gen_ld8s_i32(dst, cpu_env, offs); + tcg_gen_ld8s_i32(dst, tcg_env, offs); break; case ES_16 | MO_SIGN: - tcg_gen_ld16s_i32(dst, cpu_env, offs); + tcg_gen_ld16s_i32(dst, tcg_env, offs); break; case ES_32: case ES_32 | MO_SIGN: - tcg_gen_ld_i32(dst, cpu_env, offs); + tcg_gen_ld_i32(dst, tcg_env, offs); break; default: g_assert_not_reached(); @@ -129,16 +129,16 @@ static void write_vec_element_i64(TCGv_i64 src, int reg, uint8_t enr, switch (memop) { case ES_8: - tcg_gen_st8_i64(src, cpu_env, offs); + tcg_gen_st8_i64(src, tcg_env, offs); break; case ES_16: - tcg_gen_st16_i64(src, cpu_env, offs); + tcg_gen_st16_i64(src, tcg_env, offs); break; case ES_32: - tcg_gen_st32_i64(src, cpu_env, offs); + tcg_gen_st32_i64(src, tcg_env, offs); break; case ES_64: - tcg_gen_st_i64(src, cpu_env, offs); + tcg_gen_st_i64(src, tcg_env, offs); break; default: g_assert_not_reached(); @@ -152,13 +152,13 @@ static void write_vec_element_i32(TCGv_i32 src, int reg, uint8_t enr, switch (memop) { case ES_8: - tcg_gen_st8_i32(src, cpu_env, offs); + tcg_gen_st8_i32(src, tcg_env, offs); break; case ES_16: - tcg_gen_st16_i32(src, cpu_env, offs); + tcg_gen_st16_i32(src, tcg_env, offs); break; case ES_32: - tcg_gen_st_i32(src, cpu_env, offs); + tcg_gen_st_i32(src, tcg_env, offs); break; default: g_assert_not_reached(); @@ -173,18 +173,16 @@ static void get_vec_element_ptr_i64(TCGv_ptr ptr, uint8_t reg, TCGv_i64 enr, /* mask off invalid parts from the element nr */ tcg_gen_andi_i64(tmp, enr, NUM_VEC_ELEMENTS(es) - 1); - /* convert it to an element offset relative to cpu_env (vec_reg_offset() */ + /* convert it to an element offset relative to tcg_env (vec_reg_offset() */ tcg_gen_shli_i64(tmp, tmp, es); #if !HOST_BIG_ENDIAN tcg_gen_xori_i64(tmp, tmp, 8 - NUM_VEC_ELEMENT_BYTES(es)); #endif tcg_gen_addi_i64(tmp, tmp, vec_full_reg_offset(reg)); - /* generate the final ptr by adding cpu_env */ + /* generate the final ptr by adding tcg_env */ tcg_gen_trunc_i64_ptr(ptr, tmp); - tcg_gen_add_ptr(ptr, ptr, cpu_env); - - tcg_temp_free_i64(tmp); + tcg_gen_add_ptr(ptr, ptr, tcg_env); } #define gen_gvec_2(v1, v2, gen) \ @@ -272,13 +270,6 @@ static void gen_gvec128_3_i64(gen_gvec128_3_i64_fn fn, uint8_t d, uint8_t a, fn(dl, dh, al, ah, bl, bh); write_vec_element_i64(dh, d, 0, ES_64); write_vec_element_i64(dl, d, 1, ES_64); - - tcg_temp_free_i64(dh); - tcg_temp_free_i64(dl); - tcg_temp_free_i64(ah); - tcg_temp_free_i64(al); - tcg_temp_free_i64(bh); - tcg_temp_free_i64(bl); } typedef void (*gen_gvec128_4_i64_fn)(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, @@ -305,26 +296,15 @@ static void gen_gvec128_4_i64(gen_gvec128_4_i64_fn fn, uint8_t d, uint8_t a, fn(dl, dh, al, ah, bl, bh, cl, ch); write_vec_element_i64(dh, d, 0, ES_64); write_vec_element_i64(dl, d, 1, ES_64); - - tcg_temp_free_i64(dh); - tcg_temp_free_i64(dl); - tcg_temp_free_i64(ah); - tcg_temp_free_i64(al); - tcg_temp_free_i64(bh); - tcg_temp_free_i64(bl); - tcg_temp_free_i64(ch); - tcg_temp_free_i64(cl); } static void gen_addi2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, TCGv_i64 ah, uint64_t b) { - TCGv_i64 bl = tcg_const_i64(b); - TCGv_i64 bh = tcg_const_i64(0); + TCGv_i64 bl = tcg_constant_i64(b); + TCGv_i64 bh = tcg_constant_i64(0); tcg_gen_add2_i64(dl, dh, al, ah, bl, bh); - tcg_temp_free_i64(bl); - tcg_temp_free_i64(bh); } static DisasJumpType op_vbperm(DisasContext *s, DisasOps *o) @@ -353,7 +333,6 @@ static DisasJumpType op_vge(DisasContext *s, DisasOps *o) tcg_gen_qemu_ld_i64(tmp, o->addr1, get_mem_index(s), MO_TE | es); write_vec_element_i64(tmp, get_field(s, v1), enr, es); - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -388,7 +367,6 @@ static DisasJumpType op_vgbm(DisasContext *s, DisasOps *o) 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, v1), 1, ES_64); - tcg_temp_free_i64(t); } return DISAS_NEXT; } @@ -429,8 +407,6 @@ static DisasJumpType op_vl(DisasContext *s, DisasOps *o) tcg_gen_qemu_ld_i64(t1, o->addr1, get_mem_index(s), MO_TEUQ); 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,7 +429,6 @@ 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, v1), tmp); - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -471,7 +446,6 @@ static DisasJumpType op_vlebr(DisasContext *s, DisasOps *o) tmp = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(tmp, o->addr1, get_mem_index(s), MO_LE | es); write_vec_element_i64(tmp, get_field(s, v1), enr, es); - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -488,7 +462,6 @@ static DisasJumpType op_vlbrrep(DisasContext *s, DisasOps *o) tmp = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(tmp, o->addr1, get_mem_index(s), MO_LE | es); gen_gvec_dup_i64(es, get_field(s, v1), tmp); - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -520,7 +493,6 @@ static DisasJumpType op_vllebrz(DisasContext *s, DisasOps *o) write_vec_element_i64(tmp, get_field(s, v1), 0, ES_64); write_vec_element_i64(tcg_constant_i64(0), get_field(s, v1), 1, ES_64); - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -574,9 +546,6 @@ static DisasJumpType op_vlbr(DisasContext *s, DisasOps *o) write: write_vec_element_i64(t0, get_field(s, v1), 0, ES_64); write_vec_element_i64(t1, get_field(s, v1), 1, ES_64); - - tcg_temp_free(t0); - tcg_temp_free(t1); return DISAS_NEXT; } @@ -594,7 +563,6 @@ 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, v1), enr, es); - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -609,9 +577,8 @@ static DisasJumpType op_vlei(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - tmp = tcg_const_i64((int16_t)get_field(s, i2)); + tmp = tcg_constant_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; } @@ -650,8 +617,6 @@ static DisasJumpType op_vler(DisasContext *s, DisasOps *o) 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; } @@ -691,8 +656,6 @@ static DisasJumpType op_vlgv(DisasContext *s, DisasOps *o) default: g_assert_not_reached(); } - tcg_temp_free_ptr(ptr); - return DISAS_NEXT; } @@ -733,7 +696,6 @@ static DisasJumpType op_vllez(DisasContext *s, DisasOps *o) tcg_gen_qemu_ld_i64(t, o->addr1, get_mem_index(s), MO_TE | es); gen_gvec_dup_imm(es, get_field(s, v1), 0); write_vec_element_i64(t, get_field(s, v1), enr, es); - tcg_temp_free_i64(t); return DISAS_NEXT; } @@ -771,9 +733,6 @@ static DisasJumpType op_vlm(DisasContext *s, DisasOps *o) /* Store the last element, loaded first */ write_vec_element_i64(t0, v1, 1, ES_64); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); return DISAS_NEXT; } @@ -795,10 +754,8 @@ static DisasJumpType op_vlbb(DisasContext *s, DisasOps *o) tcg_gen_ori_i64(bytes, o->addr1, -block_size); tcg_gen_neg_i64(bytes, bytes); - tcg_gen_addi_ptr(a0, cpu_env, v1_offs); - gen_helper_vll(cpu_env, a0, o->addr1, bytes); - tcg_temp_free_i64(bytes); - tcg_temp_free_ptr(a0); + tcg_gen_addi_ptr(a0, tcg_env, v1_offs); + gen_helper_vll(tcg_env, a0, o->addr1, bytes); return DISAS_NEXT; } @@ -838,8 +795,6 @@ static DisasJumpType op_vlvg(DisasContext *s, DisasOps *o) default: g_assert_not_reached(); } - tcg_temp_free_ptr(ptr); - return DISAS_NEXT; } @@ -857,9 +812,8 @@ static DisasJumpType op_vll(DisasContext *s, DisasOps *o) /* convert highest index into an actual length */ tcg_gen_addi_i64(o->in2, o->in2, 1); - tcg_gen_addi_ptr(a0, cpu_env, v1_offs); - gen_helper_vll(cpu_env, a0, o->addr1, o->in2); - tcg_temp_free_ptr(a0); + tcg_gen_addi_ptr(a0, tcg_env, v1_offs); + gen_helper_vll(tcg_env, a0, o->addr1, o->in2); return DISAS_NEXT; } @@ -901,7 +855,6 @@ static DisasJumpType op_vmr(DisasContext *s, DisasOps *o) write_vec_element_i64(tmp, v1, dst_idx, es); } } - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -945,7 +898,7 @@ static DisasJumpType op_vpk(DisasContext *s, DisasOps *o) switch (s->fields.op2) { case 0x97: if (get_field(s, m5) & 0x1) { - gen_gvec_3_ptr(v1, v2, v3, cpu_env, 0, vpks_cc[es - 1]); + gen_gvec_3_ptr(v1, v2, v3, tcg_env, 0, vpks_cc[es - 1]); set_cc_static(s); } else { gen_gvec_3_ool(v1, v2, v3, 0, vpks[es - 1]); @@ -953,14 +906,14 @@ static DisasJumpType op_vpk(DisasContext *s, DisasOps *o) break; case 0x95: if (get_field(s, m5) & 0x1) { - gen_gvec_3_ptr(v1, v2, v3, cpu_env, 0, vpkls_cc[es - 1]); + gen_gvec_3_ptr(v1, v2, v3, tcg_env, 0, vpkls_cc[es - 1]); set_cc_static(s); } else { gen_gvec_3_ool(v1, v2, v3, 0, vpkls[es - 1]); } break; case 0x94: - /* If sources and destination dont't overlap -> fast path */ + /* If sources and destination don't overlap -> fast path */ if (v1 != v2 && v1 != v3) { const uint8_t src_es = get_field(s, m4); const uint8_t dst_es = src_es - 1; @@ -977,7 +930,6 @@ static DisasJumpType op_vpk(DisasContext *s, DisasOps *o) } write_vec_element_i64(tmp, v1, dst_idx, dst_es); } - tcg_temp_free_i64(tmp); } else { gen_gvec_3_ool(v1, v2, v3, 0, vpk[es - 1]); } @@ -1007,14 +959,12 @@ static DisasJumpType op_vpdi(DisasContext *s, DisasOps *o) 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; } static DisasJumpType op_vrep(DisasContext *s, DisasOps *o) { - const uint8_t enr = get_field(s, i2); + const uint16_t enr = get_field(s, i2); const uint8_t es = get_field(s, m4); if (es > ES_64 || !valid_vec_element(enr, es)) { @@ -1060,7 +1010,6 @@ static DisasJumpType op_vsce(DisasContext *s, DisasOps *o) 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; } @@ -1101,23 +1050,23 @@ static DisasJumpType op_vseg(DisasContext *s, DisasOps *o) 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; } static DisasJumpType op_vst(DisasContext *s, DisasOps *o) { - TCGv_i64 tmp = tcg_const_i64(16); + TCGv_i64 tmp; /* Probe write access before actually modifying memory */ - gen_helper_probe_write_access(cpu_env, o->addr1, tmp); + gen_helper_probe_write_access(tcg_env, o->addr1, + tcg_constant_i64(16)); + tmp = tcg_temp_new_i64(); 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_TEUQ); gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); 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_TEUQ); - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -1135,7 +1084,6 @@ static DisasJumpType op_vstebr(DisasContext *s, DisasOps *o) tmp = tcg_temp_new_i64(); read_vec_element_i64(tmp, get_field(s, v1), enr, es); tcg_gen_qemu_st_i64(tmp, o->addr1, get_mem_index(s), MO_LE | es); - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -1150,7 +1098,7 @@ static DisasJumpType op_vstbr(DisasContext *s, DisasOps *o) } /* Probe write access before actually modifying memory */ - gen_helper_probe_write_access(cpu_env, o->addr1, tcg_constant_i64(16)); + gen_helper_probe_write_access(tcg_env, o->addr1, tcg_constant_i64(16)); t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); @@ -1190,9 +1138,6 @@ write: tcg_gen_qemu_st_i64(t0, o->addr1, get_mem_index(s), MO_LEUQ); gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); tcg_gen_qemu_st_i64(t1, o->addr1, get_mem_index(s), MO_LEUQ); - - tcg_temp_free(t0); - tcg_temp_free(t1); return DISAS_NEXT; } @@ -1210,7 +1155,6 @@ static DisasJumpType op_vste(DisasContext *s, DisasOps *o) tmp = tcg_temp_new_i64(); read_vec_element_i64(tmp, get_field(s, v1), enr, es); tcg_gen_qemu_st_i64(tmp, o->addr1, get_mem_index(s), MO_TE | es); - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -1225,7 +1169,7 @@ static DisasJumpType op_vster(DisasContext *s, DisasOps *o) } /* Probe write access before actually modifying memory */ - gen_helper_probe_write_access(cpu_env, o->addr1, tcg_constant_i64(16)); + gen_helper_probe_write_access(tcg_env, o->addr1, tcg_constant_i64(16)); /* Begin with the two doublewords swapped... */ t0 = tcg_temp_new_i64(); @@ -1252,9 +1196,6 @@ static DisasJumpType op_vster(DisasContext *s, DisasOps *o) tcg_gen_qemu_st_i64(t0, o->addr1, get_mem_index(s), MO_TEUQ); gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); tcg_gen_qemu_st_i64(t1, o->addr1, get_mem_index(s), MO_TEUQ); - - tcg_temp_free(t0); - tcg_temp_free(t1); return DISAS_NEXT; } @@ -1270,9 +1211,10 @@ static DisasJumpType op_vstm(DisasContext *s, DisasOps *o) } /* Probe write access before actually modifying memory */ - tmp = tcg_const_i64((v3 - v1 + 1) * 16); - gen_helper_probe_write_access(cpu_env, o->addr1, tmp); + gen_helper_probe_write_access(tcg_env, o->addr1, + tcg_constant_i64((v3 - v1 + 1) * 16)); + tmp = tcg_temp_new_i64(); for (;; v1++) { read_vec_element_i64(tmp, v1, 0, ES_64); tcg_gen_qemu_st_i64(tmp, o->addr1, get_mem_index(s), MO_TEUQ); @@ -1284,7 +1226,6 @@ static DisasJumpType op_vstm(DisasContext *s, DisasOps *o) } gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); } - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -1295,9 +1236,8 @@ static DisasJumpType op_vstl(DisasContext *s, DisasOps *o) /* convert highest index into an actual length */ tcg_gen_addi_i64(o->in2, o->in2, 1); - tcg_gen_addi_ptr(a0, cpu_env, v1_offs); - gen_helper_vstl(cpu_env, a0, o->addr1, o->in2); - tcg_temp_free_ptr(a0); + tcg_gen_addi_ptr(a0, tcg_env, v1_offs); + gen_helper_vstl(tcg_env, a0, o->addr1, o->in2); return DISAS_NEXT; } @@ -1335,7 +1275,6 @@ static DisasJumpType op_vup(DisasContext *s, DisasOps *o) write_vec_element_i64(tmp, v1, dst_idx, dst_es); } } - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -1359,7 +1298,7 @@ static DisasJumpType op_va(DisasContext *s, DisasOps *o) static void gen_acc(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b, uint8_t es) { const uint8_t msb_bit_nr = NUM_VEC_ELEMENT_BITS(es) - 1; - TCGv_i64 msb_mask = tcg_const_i64(dup_const(es, 1ull << msb_bit_nr)); + TCGv_i64 msb_mask = tcg_constant_i64(dup_const(es, 1ull << msb_bit_nr)); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); TCGv_i64 t3 = tcg_temp_new_i64(); @@ -1377,10 +1316,6 @@ static void gen_acc(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b, uint8_t es) /* Isolate and shift the carry into position */ tcg_gen_and_i64(d, d, msb_mask); tcg_gen_shri_i64(d, d, msb_bit_nr); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); } static void gen_acc8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) @@ -1399,7 +1334,6 @@ static void gen_acc_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) tcg_gen_add_i32(t, a, b); tcg_gen_setcond_i32(TCG_COND_LTU, d, t, b); - tcg_temp_free_i32(t); } static void gen_acc_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) @@ -1408,7 +1342,6 @@ static void gen_acc_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) tcg_gen_add_i64(t, a, b); tcg_gen_setcond_i64(TCG_COND_LTU, d, t, b); - tcg_temp_free_i64(t); } static void gen_acc2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, @@ -1416,16 +1349,12 @@ static void gen_acc2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, { TCGv_i64 th = tcg_temp_new_i64(); TCGv_i64 tl = tcg_temp_new_i64(); - TCGv_i64 zero = tcg_const_i64(0); + TCGv_i64 zero = tcg_constant_i64(0); tcg_gen_add2_i64(tl, th, al, zero, bl, zero); tcg_gen_add2_i64(tl, th, th, zero, ah, zero); tcg_gen_add2_i64(tl, dl, tl, th, bh, zero); tcg_gen_mov_i64(dh, zero); - - tcg_temp_free_i64(th); - tcg_temp_free_i64(tl); - tcg_temp_free_i64(zero); } static DisasJumpType op_vacc(DisasContext *s, DisasOps *o) @@ -1455,15 +1384,12 @@ static void gen_ac2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh, TCGv_i64 cl, TCGv_i64 ch) { TCGv_i64 tl = tcg_temp_new_i64(); - TCGv_i64 th = tcg_const_i64(0); + TCGv_i64 zero = tcg_constant_i64(0); /* extract the carry only */ tcg_gen_extract_i64(tl, cl, 0, 1); tcg_gen_add2_i64(dl, dh, al, ah, bl, bh); - tcg_gen_add2_i64(dl, dh, dl, dh, tl, th); - - tcg_temp_free_i64(tl); - tcg_temp_free_i64(th); + tcg_gen_add2_i64(dl, dh, dl, dh, tl, zero); } static DisasJumpType op_vac(DisasContext *s, DisasOps *o) @@ -1484,7 +1410,7 @@ static void gen_accc2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, TCGv_i64 ah, { TCGv_i64 tl = tcg_temp_new_i64(); TCGv_i64 th = tcg_temp_new_i64(); - TCGv_i64 zero = tcg_const_i64(0); + TCGv_i64 zero = tcg_constant_i64(0); tcg_gen_andi_i64(tl, cl, 1); tcg_gen_add2_i64(tl, th, tl, zero, al, zero); @@ -1492,10 +1418,6 @@ static void gen_accc2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, TCGv_i64 ah, tcg_gen_add2_i64(tl, th, th, zero, ah, zero); tcg_gen_add2_i64(tl, dl, tl, th, bh, zero); tcg_gen_mov_i64(dh, zero); - - tcg_temp_free_i64(tl); - tcg_temp_free_i64(th); - tcg_temp_free_i64(zero); } static DisasJumpType op_vaccc(DisasContext *s, DisasOps *o) @@ -1536,9 +1458,6 @@ static void gen_avg_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) tcg_gen_addi_i64(t0, t0, 1); tcg_gen_shri_i64(t0, t0, 1); tcg_gen_extrl_i64_i32(d, t0); - - tcg_temp_free(t0); - tcg_temp_free(t1); } static void gen_avg_i64(TCGv_i64 dl, TCGv_i64 al, TCGv_i64 bl) @@ -1553,10 +1472,6 @@ static void gen_avg_i64(TCGv_i64 dl, TCGv_i64 al, TCGv_i64 bl) tcg_gen_add2_i64(dl, dh, al, ah, bl, bh); gen_addi2_i64(dl, dh, dl, dh, 1); tcg_gen_extract2_i64(dl, dl, dh, 1); - - tcg_temp_free_i64(dh); - tcg_temp_free_i64(ah); - tcg_temp_free_i64(bh); } static DisasJumpType op_vavg(DisasContext *s, DisasOps *o) @@ -1589,22 +1504,16 @@ static void gen_avgl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) tcg_gen_addi_i64(t0, t0, 1); tcg_gen_shri_i64(t0, t0, 1); tcg_gen_extrl_i64_i32(d, t0); - - tcg_temp_free(t0); - tcg_temp_free(t1); } static void gen_avgl_i64(TCGv_i64 dl, TCGv_i64 al, TCGv_i64 bl) { TCGv_i64 dh = tcg_temp_new_i64(); - TCGv_i64 zero = tcg_const_i64(0); + TCGv_i64 zero = tcg_constant_i64(0); tcg_gen_add2_i64(dl, dh, al, zero, bl, zero); gen_addi2_i64(dl, dh, dl, dh, 1); tcg_gen_extract2_i64(dl, dl, dh, 1); - - tcg_temp_free_i64(dh); - tcg_temp_free_i64(zero); } static DisasJumpType op_vavgl(DisasContext *s, DisasOps *o) @@ -1639,9 +1548,6 @@ static DisasJumpType op_vcksm(DisasContext *s, DisasOps *o) } gen_gvec_dup_imm(ES_32, get_field(s, v1), 0); write_vec_element_i32(sum, get_field(s, v1), 1, ES_32); - - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(sum); return DISAS_NEXT; } @@ -1686,9 +1592,6 @@ static DisasJumpType op_vc(DisasContext *s, DisasOps *o) 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); - tcg_temp_free_i64(high); } return DISAS_NEXT; } @@ -1857,8 +1760,6 @@ static void gen_mal_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b, TCGv_i32 c) tcg_gen_mul_i32(t0, a, b); tcg_gen_add_i32(d, t0, c); - - tcg_temp_free_i32(t0); } static void gen_mah_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b, TCGv_i32 c) @@ -1873,10 +1774,6 @@ static void gen_mah_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b, TCGv_i32 c) tcg_gen_mul_i64(t0, t0, t1); tcg_gen_add_i64(t0, t0, t2); tcg_gen_extrh_i64_i32(d, t0); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); } static void gen_malh_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b, TCGv_i32 c) @@ -1891,10 +1788,6 @@ static void gen_malh_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b, TCGv_i32 c) tcg_gen_mul_i64(t0, t0, t1); tcg_gen_add_i64(t0, t0, t2); tcg_gen_extrh_i64_i32(d, t0); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); } static DisasJumpType op_vma(DisasContext *s, DisasOps *o) @@ -1978,7 +1871,6 @@ static void gen_mh_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) TCGv_i32 t = tcg_temp_new_i32(); tcg_gen_muls2_i32(t, d, a, b); - tcg_temp_free_i32(t); } static void gen_mlh_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) @@ -1986,7 +1878,6 @@ static void gen_mlh_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) TCGv_i32 t = tcg_temp_new_i32(); tcg_gen_mulu2_i32(t, d, a, b); - tcg_temp_free_i32(t); } static DisasJumpType op_vm(DisasContext *s, DisasOps *o) @@ -2075,7 +1966,7 @@ static DisasJumpType op_vmsl(DisasContext *s, DisasOps *o) l2 = tcg_temp_new_i64(); h2 = tcg_temp_new_i64(); - /* Multipy both even elements from v2 and v3 */ + /* Multiply both even elements from v2 and v3 */ read_vec_element_i64(l1, get_field(s, v2), 0, ES_64); read_vec_element_i64(h1, get_field(s, v3), 0, ES_64); tcg_gen_mulu2_i64(l1, h1, l1, h1); @@ -2084,7 +1975,7 @@ static DisasJumpType op_vmsl(DisasContext *s, DisasOps *o) tcg_gen_add2_i64(l1, h1, l1, h1, l1, h1); } - /* Multipy both odd elements from v2 and v3 */ + /* Multiply both odd elements from v2 and v3 */ read_vec_element_i64(l2, get_field(s, v2), 1, ES_64); read_vec_element_i64(h2, get_field(s, v3), 1, ES_64); tcg_gen_mulu2_i64(l2, h2, l2, h2); @@ -2103,11 +1994,6 @@ static DisasJumpType op_vmsl(DisasContext *s, DisasOps *o) /* Store final result into v1. */ write_vec_element_i64(h1, get_field(s, v1), 0, ES_64); write_vec_element_i64(l1, get_field(s, v1), 1, ES_64); - - tcg_temp_free_i64(l1); - tcg_temp_free_i64(h1); - tcg_temp_free_i64(l2); - tcg_temp_free_i64(h2); return DISAS_NEXT; } @@ -2173,8 +2059,6 @@ static void gen_rim_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b, int32_t c) tcg_gen_and_i32(t, t, b); tcg_gen_andc_i32(d, d, b); tcg_gen_or_i32(d, d, t); - - tcg_temp_free_i32(t); } static void gen_rim_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b, int64_t c) @@ -2185,8 +2069,6 @@ static void gen_rim_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b, int64_t c) tcg_gen_and_i64(t, t, b); tcg_gen_andc_i64(d, d, b); tcg_gen_or_i64(d, d, t); - - tcg_temp_free_i64(t); } static DisasJumpType op_verim(DisasContext *s, DisasOps *o) @@ -2295,7 +2177,6 @@ static DisasJumpType op_ves(DisasContext *s, DisasOps *o) default: g_assert_not_reached(); } - tcg_temp_free_i32(shift); } return DISAS_NEXT; } @@ -2315,7 +2196,6 @@ static DisasJumpType gen_vsh_by_byte(DisasContext *s, DisasOps *o, read_vec_element_i64(shift, get_field(s, v3), 7, ES_8); tcg_gen_andi_i64(shift, shift, byte ? 0x78 : 7); gen_gvec_2i_ool(get_field(s, v1), get_field(s, v2), shift, 0, gen); - tcg_temp_free_i64(shift); } return DISAS_NEXT; } @@ -2371,10 +2251,6 @@ static DisasJumpType op_vsld(DisasContext *s, DisasOps *o) write_vec_element_i64(t0, get_field(s, v1), 0, ES_64); write_vec_element_i64(t1, get_field(s, v1), 1, ES_64); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); return DISAS_NEXT; } @@ -2401,10 +2277,6 @@ static DisasJumpType op_vsrd(DisasContext *s, DisasOps *o) write_vec_element_i64(t0, get_field(s, v1), 0, ES_64); write_vec_element_i64(t1, get_field(s, v1), 1, ES_64); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); return DISAS_NEXT; } @@ -2440,7 +2312,7 @@ static void gen_scbi2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, { TCGv_i64 th = tcg_temp_new_i64(); TCGv_i64 tl = tcg_temp_new_i64(); - TCGv_i64 zero = tcg_const_i64(0); + TCGv_i64 zero = tcg_constant_i64(0); tcg_gen_sub2_i64(tl, th, al, zero, bl, zero); tcg_gen_andi_i64(th, th, 1); @@ -2449,10 +2321,6 @@ static void gen_scbi2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, /* "invert" the result: -1 -> 0; 0 -> 1 */ tcg_gen_addi_i64(dl, th, 1); tcg_gen_mov_i64(dh, zero); - - tcg_temp_free_i64(th); - tcg_temp_free_i64(tl); - tcg_temp_free_i64(zero); } static DisasJumpType op_vscbi(DisasContext *s, DisasOps *o) @@ -2487,8 +2355,6 @@ static void gen_sbi2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, TCGv_i64 ah, tcg_gen_not_i64(tl, bl); tcg_gen_not_i64(th, bh); gen_ac2_i64(dl, dh, al, ah, tl, th, cl, ch); - tcg_temp_free_i64(tl); - tcg_temp_free_i64(th); } static DisasJumpType op_vsbi(DisasContext *s, DisasOps *o) @@ -2513,9 +2379,6 @@ static void gen_sbcbi2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, TCGv_i64 ah, tcg_gen_not_i64(tl, bl); tcg_gen_not_i64(th, bh); gen_accc2_i64(dl, dh, al, ah, tl, th, cl, ch); - - tcg_temp_free_i64(tl); - tcg_temp_free_i64(th); } static DisasJumpType op_vsbcbi(DisasContext *s, DisasOps *o) @@ -2555,8 +2418,6 @@ static DisasJumpType op_vsumg(DisasContext *s, DisasOps *o) } write_vec_element_i64(sum, get_field(s, v1), dst_idx, ES_64); } - tcg_temp_free_i64(sum); - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -2572,11 +2433,12 @@ static DisasJumpType op_vsumq(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - sumh = tcg_const_i64(0); + sumh = tcg_temp_new_i64(); suml = tcg_temp_new_i64(); - zero = tcg_const_i64(0); + zero = tcg_constant_i64(0); tmpl = tcg_temp_new_i64(); + tcg_gen_mov_i64(sumh, zero); 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, v2), idx, es); @@ -2584,11 +2446,6 @@ static DisasJumpType op_vsumq(DisasContext *s, DisasOps *o) } 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); - tcg_temp_free_i64(zero); - tcg_temp_free_i64(tmpl); return DISAS_NEXT; } @@ -2616,15 +2473,13 @@ static DisasJumpType op_vsum(DisasContext *s, DisasOps *o) } write_vec_element_i32(sum, get_field(s, v1), dst_idx, ES_32); } - tcg_temp_free_i32(sum); - tcg_temp_free_i32(tmp); return DISAS_NEXT; } static DisasJumpType op_vtm(DisasContext *s, DisasOps *o) { gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), - cpu_env, 0, gen_helper_gvec_vtm); + tcg_env, 0, gen_helper_gvec_vtm); set_cc_static(s); return DISAS_NEXT; } @@ -2650,7 +2505,7 @@ static DisasJumpType op_vfae(DisasContext *s, DisasOps *o) if (extract32(m5, 0, 1)) { gen_gvec_3_ptr(get_field(s, v1), get_field(s, v2), - get_field(s, v3), cpu_env, m5, g_cc[es]); + get_field(s, v3), tcg_env, m5, g_cc[es]); set_cc_static(s); } else { gen_gvec_3_ool(get_field(s, v1), get_field(s, v2), @@ -2681,7 +2536,7 @@ static DisasJumpType op_vfee(DisasContext *s, DisasOps *o) if (extract32(m5, 0, 1)) { gen_gvec_3_ptr(get_field(s, v1), get_field(s, v2), - get_field(s, v3), cpu_env, m5, g_cc[es]); + get_field(s, v3), tcg_env, m5, g_cc[es]); set_cc_static(s); } else { gen_gvec_3_ool(get_field(s, v1), get_field(s, v2), @@ -2712,7 +2567,7 @@ static DisasJumpType op_vfene(DisasContext *s, DisasOps *o) if (extract32(m5, 0, 1)) { gen_gvec_3_ptr(get_field(s, v1), get_field(s, v2), - get_field(s, v3), cpu_env, m5, g_cc[es]); + get_field(s, v3), tcg_env, m5, g_cc[es]); set_cc_static(s); } else { gen_gvec_3_ool(get_field(s, v1), get_field(s, v2), @@ -2723,7 +2578,7 @@ static DisasJumpType op_vfene(DisasContext *s, DisasOps *o) static DisasJumpType op_vistr(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s, m4); + const uint8_t es = get_field(s, m3); const uint8_t m5 = get_field(s, m5); static gen_helper_gvec_2 * const g[3] = { gen_helper_gvec_vistr8, @@ -2743,7 +2598,7 @@ static DisasJumpType op_vistr(DisasContext *s, DisasOps *o) if (extract32(m5, 0, 1)) { gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), - cpu_env, 0, g_cc[es]); + tcg_env, 0, g_cc[es]); set_cc_static(s); } else { gen_gvec_2_ool(get_field(s, v1), get_field(s, v2), 0, @@ -2786,11 +2641,11 @@ static DisasJumpType op_vstrc(DisasContext *s, DisasOps *o) if (extract32(m6, 2, 1)) { 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]); + tcg_env, m6, g_cc_rt[es]); } else { 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]); + tcg_env, m6, g_cc[es]); } set_cc_static(s); } else { @@ -2827,7 +2682,7 @@ static DisasJumpType op_vstrs(DisasContext *s, DisasOps *o) gen_gvec_4_ptr(get_field(s, v1), get_field(s, v2), get_field(s, v3), get_field(s, v4), - cpu_env, 0, fns[es][zs]); + tcg_env, 0, fns[es][zs]); set_cc_static(s); return DISAS_NEXT; } @@ -2925,7 +2780,7 @@ static DisasJumpType op_vfa(DisasContext *s, DisasOps *o) } gen_gvec_3_ptr(get_field(s, v1), get_field(s, v2), - get_field(s, v3), cpu_env, m5, fn); + get_field(s, v3), tcg_env, m5, fn); return DISAS_NEXT; } @@ -2967,7 +2822,7 @@ static DisasJumpType op_wfc(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), cpu_env, 0, fn); + gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), tcg_env, 0, fn); set_cc_static(s); return DISAS_NEXT; } @@ -3038,7 +2893,7 @@ static DisasJumpType op_vfc(DisasContext *s, DisasOps *o) } gen_gvec_3_ptr(get_field(s, v1), get_field(s, v2), get_field(s, v3), - cpu_env, m5, fn); + tcg_env, m5, fn); if (cs) { set_cc_static(s); } @@ -3152,7 +3007,7 @@ static DisasJumpType op_vcdg(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), cpu_env, + gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), tcg_env, deposit32(m4, 4, 4, erm), fn); return DISAS_NEXT; } @@ -3181,7 +3036,7 @@ static DisasJumpType op_vfll(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), cpu_env, m4, fn); + gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), tcg_env, m4, fn); return DISAS_NEXT; } @@ -3192,7 +3047,7 @@ static DisasJumpType op_vfmax(DisasContext *s, DisasOps *o) const uint8_t m5 = get_field(s, m5); gen_helper_gvec_3_ptr *fn; - if (m6 == 5 || m6 == 6 || m6 == 7 || m6 > 13) { + if (m6 == 5 || m6 == 6 || m6 == 7 || m6 >= 13 || (m5 & 7)) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } @@ -3225,7 +3080,7 @@ static DisasJumpType op_vfmax(DisasContext *s, DisasOps *o) } gen_gvec_3_ptr(get_field(s, v1), get_field(s, v2), get_field(s, v3), - cpu_env, deposit32(m5, 4, 4, m6), fn); + tcg_env, deposit32(m5, 4, 4, m6), fn); return DISAS_NEXT; } @@ -3314,7 +3169,7 @@ static DisasJumpType op_vfma(DisasContext *s, DisasOps *o) } gen_gvec_4_ptr(get_field(s, v1), get_field(s, v2), - get_field(s, v3), get_field(s, v4), cpu_env, m5, fn); + get_field(s, v3), get_field(s, v4), tcg_env, m5, fn); return DISAS_NEXT; } @@ -3404,9 +3259,6 @@ static DisasJumpType op_vfpso(DisasContext *s, DisasOps *o) read_vec_element_i64(tmp, v2, 1, ES_64); write_vec_element_i64(tmp, v1, 1, ES_64); } - - tcg_temp_free_i64(tmp); - return DISAS_NEXT; } @@ -3439,7 +3291,7 @@ static DisasJumpType op_vfsq(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), cpu_env, m4, fn); + gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), tcg_env, m4, fn); return DISAS_NEXT; } @@ -3473,7 +3325,7 @@ static DisasJumpType op_vftci(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), cpu_env, + gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), tcg_env, deposit32(m5, 4, 12, i3), fn); set_cc_static(s); return DISAS_NEXT; diff --git a/target/s390x/tcg/vec_fpu_helper.c b/target/s390x/tcg/vec_fpu_helper.c index 2a618a1093..75cf605b9f 100644 --- a/target/s390x/tcg/vec_fpu_helper.c +++ b/target/s390x/tcg/vec_fpu_helper.c @@ -824,7 +824,7 @@ static S390MinMaxRes vfmin_res(uint16_t dcmask_a, uint16_t dcmask_b, default: g_assert_not_reached(); } - } else if (unlikely(dcmask_a & dcmask_b & DCMASK_ZERO)) { + } else if (unlikely((dcmask_a & DCMASK_ZERO) && (dcmask_b & DCMASK_ZERO))) { switch (type) { case S390_MINMAX_TYPE_JAVA: return neg_a ? S390_MINMAX_RES_A : S390_MINMAX_RES_B; @@ -874,7 +874,7 @@ static S390MinMaxRes vfmax_res(uint16_t dcmask_a, uint16_t dcmask_b, default: g_assert_not_reached(); } - } else if (unlikely(dcmask_a & dcmask_b & DCMASK_ZERO)) { + } else if (unlikely((dcmask_a & DCMASK_ZERO) && (dcmask_b & DCMASK_ZERO))) { const bool neg_a = dcmask_a & DCMASK_NEGATIVE; switch (type) { diff --git a/target/s390x/tcg/vec_helper.c b/target/s390x/tcg/vec_helper.c index 48d86722b2..dafc4c3582 100644 --- a/target/s390x/tcg/vec_helper.c +++ b/target/s390x/tcg/vec_helper.c @@ -193,7 +193,7 @@ void HELPER(vstl)(CPUS390XState *env, const void *v1, uint64_t addr, uint64_t bytes) { /* Probe write access before actually modifying memory */ - probe_write_access(env, addr, bytes, GETPC()); + probe_write_access(env, addr, MIN(bytes, 16), GETPC()); if (likely(bytes >= 16)) { cpu_stq_data_ra(env, addr, s390_vec_read_element64(v1, 0), GETPC()); diff --git a/target/s390x/tcg/vec_int_helper.c b/target/s390x/tcg/vec_int_helper.c index 53ab5c5eb3..b18d8a6d16 100644 --- a/target/s390x/tcg/vec_int_helper.c +++ b/target/s390x/tcg/vec_int_helper.c @@ -14,19 +14,13 @@ #include "vec.h" #include "exec/helper-proto.h" #include "tcg/tcg-gvec-desc.h" +#include "crypto/clmul.h" static bool s390_vec_is_zero(const S390Vector *v) { return !v->doubleword[0] && !v->doubleword[1]; } -static void s390_vec_xor(S390Vector *res, const S390Vector *a, - const S390Vector *b) -{ - res->doubleword[0] = a->doubleword[0] ^ b->doubleword[0]; - res->doubleword[1] = a->doubleword[1] ^ b->doubleword[1]; -} - static void s390_vec_and(S390Vector *res, const S390Vector *a, const S390Vector *b) { @@ -164,117 +158,105 @@ DEF_VCTZ(8) DEF_VCTZ(16) /* like binary multiplication, but XOR instead of addition */ -#define DEF_GALOIS_MULTIPLY(BITS, TBITS) \ -static uint##TBITS##_t galois_multiply##BITS(uint##TBITS##_t a, \ - uint##TBITS##_t b) \ -{ \ - uint##TBITS##_t res = 0; \ - \ - while (b) { \ - if (b & 0x1) { \ - res = res ^ a; \ - } \ - a = a << 1; \ - b = b >> 1; \ - } \ - return res; \ -} -DEF_GALOIS_MULTIPLY(8, 16) -DEF_GALOIS_MULTIPLY(16, 32) -DEF_GALOIS_MULTIPLY(32, 64) -static S390Vector galois_multiply64(uint64_t a, uint64_t b) +/* + * There is no carry across the two doublewords, so their order does + * not matter. Nor is there partial overlap between registers. + */ +static inline uint64_t do_gfma8(uint64_t n, uint64_t m, uint64_t a) { - S390Vector res = {}; - S390Vector va = { - .doubleword[1] = a, - }; - S390Vector vb = { - .doubleword[1] = b, - }; - - while (!s390_vec_is_zero(&vb)) { - if (vb.doubleword[1] & 0x1) { - s390_vec_xor(&res, &res, &va); - } - s390_vec_shl(&va, &va, 1); - s390_vec_shr(&vb, &vb, 1); - } - return res; + return clmul_8x4_even(n, m) ^ clmul_8x4_odd(n, m) ^ a; } -#define DEF_VGFM(BITS, TBITS) \ -void HELPER(gvec_vgfm##BITS)(void *v1, const void *v2, const void *v3, \ - uint32_t desc) \ -{ \ - int i; \ - \ - for (i = 0; i < (128 / TBITS); i++) { \ - uint##BITS##_t a = s390_vec_read_element##BITS(v2, i * 2); \ - uint##BITS##_t b = s390_vec_read_element##BITS(v3, i * 2); \ - uint##TBITS##_t d = galois_multiply##BITS(a, b); \ - \ - a = s390_vec_read_element##BITS(v2, i * 2 + 1); \ - b = s390_vec_read_element##BITS(v3, i * 2 + 1); \ - d = d ^ galois_multiply32(a, b); \ - s390_vec_write_element##TBITS(v1, i, d); \ - } \ +void HELPER(gvec_vgfm8)(void *v1, const void *v2, const void *v3, uint32_t d) +{ + uint64_t *q1 = v1; + const uint64_t *q2 = v2, *q3 = v3; + + q1[0] = do_gfma8(q2[0], q3[0], 0); + q1[1] = do_gfma8(q2[1], q3[1], 0); +} + +void HELPER(gvec_vgfma8)(void *v1, const void *v2, const void *v3, + const void *v4, uint32_t desc) +{ + uint64_t *q1 = v1; + const uint64_t *q2 = v2, *q3 = v3, *q4 = v4; + + q1[0] = do_gfma8(q2[0], q3[0], q4[0]); + q1[1] = do_gfma8(q2[1], q3[1], q4[1]); +} + +static inline uint64_t do_gfma16(uint64_t n, uint64_t m, uint64_t a) +{ + return clmul_16x2_even(n, m) ^ clmul_16x2_odd(n, m) ^ a; +} + +void HELPER(gvec_vgfm16)(void *v1, const void *v2, const void *v3, uint32_t d) +{ + uint64_t *q1 = v1; + const uint64_t *q2 = v2, *q3 = v3; + + q1[0] = do_gfma16(q2[0], q3[0], 0); + q1[1] = do_gfma16(q2[1], q3[1], 0); +} + +void HELPER(gvec_vgfma16)(void *v1, const void *v2, const void *v3, + const void *v4, uint32_t d) +{ + uint64_t *q1 = v1; + const uint64_t *q2 = v2, *q3 = v3, *q4 = v4; + + q1[0] = do_gfma16(q2[0], q3[0], q4[0]); + q1[1] = do_gfma16(q2[1], q3[1], q4[1]); +} + +static inline uint64_t do_gfma32(uint64_t n, uint64_t m, uint64_t a) +{ + return clmul_32(n, m) ^ clmul_32(n >> 32, m >> 32) ^ a; +} + +void HELPER(gvec_vgfm32)(void *v1, const void *v2, const void *v3, uint32_t d) +{ + uint64_t *q1 = v1; + const uint64_t *q2 = v2, *q3 = v3; + + q1[0] = do_gfma32(q2[0], q3[0], 0); + q1[1] = do_gfma32(q2[1], q3[1], 0); +} + +void HELPER(gvec_vgfma32)(void *v1, const void *v2, const void *v3, + const void *v4, uint32_t d) +{ + uint64_t *q1 = v1; + const uint64_t *q2 = v2, *q3 = v3, *q4 = v4; + + q1[0] = do_gfma32(q2[0], q3[0], q4[0]); + q1[1] = do_gfma32(q2[1], q3[1], q4[1]); } -DEF_VGFM(8, 16) -DEF_VGFM(16, 32) -DEF_VGFM(32, 64) void HELPER(gvec_vgfm64)(void *v1, const void *v2, const void *v3, uint32_t desc) { - S390Vector tmp1, tmp2; - uint64_t a, b; + uint64_t *q1 = v1; + const uint64_t *q2 = v2, *q3 = v3; + Int128 r; - a = s390_vec_read_element64(v2, 0); - b = s390_vec_read_element64(v3, 0); - tmp1 = galois_multiply64(a, b); - a = s390_vec_read_element64(v2, 1); - b = s390_vec_read_element64(v3, 1); - tmp2 = galois_multiply64(a, b); - s390_vec_xor(v1, &tmp1, &tmp2); + r = int128_xor(clmul_64(q2[0], q3[0]), clmul_64(q2[1], q3[1])); + q1[0] = int128_gethi(r); + q1[1] = int128_getlo(r); } -#define DEF_VGFMA(BITS, TBITS) \ -void HELPER(gvec_vgfma##BITS)(void *v1, const void *v2, const void *v3, \ - const void *v4, uint32_t desc) \ -{ \ - int i; \ - \ - for (i = 0; i < (128 / TBITS); i++) { \ - uint##BITS##_t a = s390_vec_read_element##BITS(v2, i * 2); \ - uint##BITS##_t b = s390_vec_read_element##BITS(v3, i * 2); \ - uint##TBITS##_t d = galois_multiply##BITS(a, b); \ - \ - a = s390_vec_read_element##BITS(v2, i * 2 + 1); \ - b = s390_vec_read_element##BITS(v3, i * 2 + 1); \ - d = d ^ galois_multiply32(a, b); \ - d = d ^ s390_vec_read_element##TBITS(v4, i); \ - s390_vec_write_element##TBITS(v1, i, d); \ - } \ -} -DEF_VGFMA(8, 16) -DEF_VGFMA(16, 32) -DEF_VGFMA(32, 64) - void HELPER(gvec_vgfma64)(void *v1, const void *v2, const void *v3, const void *v4, uint32_t desc) { - S390Vector tmp1, tmp2; - uint64_t a, b; + uint64_t *q1 = v1; + const uint64_t *q2 = v2, *q3 = v3, *q4 = v4; + Int128 r; - a = s390_vec_read_element64(v2, 0); - b = s390_vec_read_element64(v3, 0); - tmp1 = galois_multiply64(a, b); - a = s390_vec_read_element64(v2, 1); - b = s390_vec_read_element64(v3, 1); - tmp2 = galois_multiply64(a, b); - s390_vec_xor(&tmp1, &tmp1, &tmp2); - s390_vec_xor(v1, &tmp1, v4); + r = int128_xor(clmul_64(q2[0], q3[0]), clmul_64(q2[1], q3[1])); + q1[0] = q4[0] ^ int128_gethi(r); + q1[1] = q4[1] ^ int128_getlo(r); } #define DEF_VMAL(BITS) \ diff --git a/target/s390x/tcg/vec_string_helper.c b/target/s390x/tcg/vec_string_helper.c index 9b85becdfb..a19f429768 100644 --- a/target/s390x/tcg/vec_string_helper.c +++ b/target/s390x/tcg/vec_string_helper.c @@ -474,9 +474,9 @@ DEF_VSTRC_CC_RT_HELPER(32) static int vstrs(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, const S390Vector *v4, uint8_t es, bool zs) { - int substr_elen, substr_0, str_elen, i, j, k, cc; + int substr_elen, i, j, k, cc; int nelem = 16 >> es; - bool eos = false; + int str_leftmost_0; substr_elen = s390_vec_read_element8(v4, 7) >> es; @@ -498,47 +498,20 @@ static int vstrs(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, } /* If ZS, look for eos in the searched string. */ + str_leftmost_0 = nelem; if (zs) { for (k = 0; k < nelem; k++) { if (s390_vec_read_element(v2, k, es) == 0) { - eos = true; + str_leftmost_0 = k; break; } } - str_elen = k; - } else { - str_elen = nelem; } - substr_0 = s390_vec_read_element(v3, 0, es); - - for (k = 0; ; k++) { - for (; k < str_elen; k++) { - if (s390_vec_read_element(v2, k, es) == substr_0) { - break; - } - } - - /* If we reached the end of the string, no match. */ - if (k == str_elen) { - cc = eos; /* no match (with or without zero char) */ - goto done; - } - - /* If the substring is only one char, match. */ - if (substr_elen == 1) { - cc = 2; /* full match */ - goto done; - } - - /* If the match begins at the last char, we have a partial match. */ - if (k == str_elen - 1) { - cc = 3; /* partial match */ - goto done; - } - + cc = str_leftmost_0 == nelem ? 0 : 1; /* No match. */ + for (k = 0; k < nelem; k++) { i = MIN(nelem, k + substr_elen); - for (j = k + 1; j < i; j++) { + for (j = k; j < i; j++) { uint32_t e2 = s390_vec_read_element(v2, j, es); uint32_t e3 = s390_vec_read_element(v3, j - k, es); if (e2 != e3) { @@ -546,9 +519,16 @@ static int vstrs(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, } } if (j == i) { - /* Matched up until "end". */ - cc = i - k == substr_elen ? 2 : 3; /* full or partial match */ - goto done; + /* All elements matched. */ + if (k > str_leftmost_0) { + cc = 1; /* Ignored match. */ + k = nelem; + } else if (i - k == substr_elen) { + cc = 2; /* Full match. */ + } else { + cc = 3; /* Partial match. */ + } + break; } } diff --git a/target/sh4/cpu-param.h b/target/sh4/cpu-param.h index 98a02509bb..a7cdb7edb6 100644 --- a/target/sh4/cpu-param.h +++ b/target/sh4/cpu-param.h @@ -16,6 +16,5 @@ #else # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif -#define NB_MMU_MODES 2 #endif diff --git a/target/sh4/cpu-qom.h b/target/sh4/cpu-qom.h index d4192d1090..6cf5fbb074 100644 --- a/target/sh4/cpu-qom.h +++ b/target/sh4/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU SuperH CPU + * QEMU SuperH CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -21,7 +21,6 @@ #define QEMU_SUPERH_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_SUPERH_CPU "superh-cpu" @@ -31,28 +30,7 @@ OBJECT_DECLARE_CPU_TYPE(SuperHCPU, SuperHCPUClass, SUPERH_CPU) -/** - * SuperHCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * @pvr: Processor Version Register - * @prr: Processor Revision Register - * @cvr: Cache Version Register - * - * A SuperH CPU model. - */ -struct SuperHCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceReset parent_reset; - - uint32_t pvr; - uint32_t prr; - uint32_t cvr; -}; - +#define SUPERH_CPU_TYPE_SUFFIX "-" TYPE_SUPERH_CPU +#define SUPERH_CPU_TYPE_NAME(model) model SUPERH_CPU_TYPE_SUFFIX #endif diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 06b2691dc4..4f5a4a3d98 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -26,6 +26,7 @@ #include "migration/vmstate.h" #include "exec/exec-all.h" #include "fpu/softfloat-helpers.h" +#include "tcg/tcg.h" static void superh_cpu_set_pc(CPUState *cs, vaddr value) { @@ -34,26 +35,48 @@ static void superh_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.pc = value; } +static vaddr superh_cpu_get_pc(CPUState *cs) +{ + SuperHCPU *cpu = SUPERH_CPU(cs); + + return cpu->env.pc; +} + static void superh_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { SuperHCPU *cpu = SUPERH_CPU(cs); + tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); cpu->env.pc = tb->pc; cpu->env.flags = tb->flags & TB_FLAG_ENVFLAGS_MASK; } +static void superh_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + SuperHCPU *cpu = SUPERH_CPU(cs); + + cpu->env.pc = data[0]; + cpu->env.flags = data[1]; + /* + * Theoretically delayed_pc should also be restored. In practice the + * branch instruction is re-executed after exception, so the delayed + * branch target will be recomputed. + */ +} + #ifndef CONFIG_USER_ONLY static bool superh_io_recompile_replay_branch(CPUState *cs, const TranslationBlock *tb) { - SuperHCPU *cpu = SUPERH_CPU(cs); - CPUSH4State *env = &cpu->env; + CPUSH4State *env = cpu_env(cs); - if ((env->flags & ((DELAY_SLOT | DELAY_SLOT_CONDITIONAL))) != 0 - && env->pc != tb->pc) { + if ((env->flags & (TB_FLAG_DELAY_SLOT | TB_FLAG_DELAY_SLOT_COND)) + && !(cs->tcg_cflags & CF_PCREL) && env->pc != tb->pc) { env->pc -= 2; - env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL); + env->flags &= ~(TB_FLAG_DELAY_SLOT | TB_FLAG_DELAY_SLOT_COND); return true; } return false; @@ -65,14 +88,30 @@ static bool superh_cpu_has_work(CPUState *cs) return cs->interrupt_request & CPU_INTERRUPT_HARD; } -static void superh_cpu_reset(DeviceState *dev) +static int sh4_cpu_mmu_index(CPUState *cs, bool ifetch) { - CPUState *s = CPU(dev); - SuperHCPU *cpu = SUPERH_CPU(s); - SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(cpu); - CPUSH4State *env = &cpu->env; + CPUSH4State *env = cpu_env(cs); - scc->parent_reset(dev); + /* + * The instruction in a RTE delay slot is fetched in privileged mode, + * but executed in user mode. + */ + if (ifetch && (env->flags & TB_FLAG_DELAY_SLOT_RTE)) { + return 0; + } else { + return (env->sr & (1u << SR_MD)) == 0 ? 1 : 0; + } +} + +static void superh_cpu_reset_hold(Object *obj) +{ + CPUState *cs = CPU(obj); + SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(obj); + CPUSH4State *env = cpu_env(cs); + + if (scc->parent_phases.hold) { + scc->parent_phases.hold(obj); + } memset(env, 0, offsetof(CPUSH4State, end_reset_fields)); @@ -96,23 +135,6 @@ static void superh_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) info->print_insn = print_insn_sh; } -static void superh_cpu_list_entry(gpointer data, gpointer user_data) -{ - const char *typename = object_class_get_name(OBJECT_CLASS(data)); - int len = strlen(typename) - strlen(SUPERH_CPU_TYPE_SUFFIX); - - qemu_printf("%.*s\n", len, typename); -} - -void sh4_cpu_list(void) -{ - GSList *list; - - list = object_class_get_list_sorted(TYPE_SUPERH_CPU, false); - g_slist_foreach(list, superh_cpu_list_entry, NULL); - g_slist_free(list); -} - static ObjectClass *superh_cpu_class_by_name(const char *cpu_model) { ObjectClass *oc; @@ -126,9 +148,6 @@ static ObjectClass *superh_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf(SUPERH_CPU_TYPE_NAME("%s"), s); oc = object_class_by_name(typename); - if (oc != NULL && object_class_is_abstract(oc)) { - oc = NULL; - } out: g_free(s); @@ -138,8 +157,7 @@ out: static void sh7750r_cpu_initfn(Object *obj) { - SuperHCPU *cpu = SUPERH_CPU(obj); - CPUSH4State *env = &cpu->env; + CPUSH4State *env = cpu_env(CPU(obj)); env->id = SH_CPU_SH7750R; env->features = SH_FEATURE_BCR3_AND_BCR4; @@ -156,8 +174,7 @@ static void sh7750r_class_init(ObjectClass *oc, void *data) static void sh7751r_cpu_initfn(Object *obj) { - SuperHCPU *cpu = SUPERH_CPU(obj); - CPUSH4State *env = &cpu->env; + CPUSH4State *env = cpu_env(CPU(obj)); env->id = SH_CPU_SH7751R; env->features = SH_FEATURE_BCR3_AND_BCR4; @@ -174,8 +191,7 @@ static void sh7751r_class_init(ObjectClass *oc, void *data) static void sh7785_cpu_initfn(Object *obj) { - SuperHCPU *cpu = SUPERH_CPU(obj); - CPUSH4State *env = &cpu->env; + CPUSH4State *env = cpu_env(CPU(obj)); env->id = SH_CPU_SH7785; env->features = SH_FEATURE_SH4A; @@ -210,10 +226,7 @@ static void superh_cpu_realizefn(DeviceState *dev, Error **errp) static void superh_cpu_initfn(Object *obj) { - SuperHCPU *cpu = SUPERH_CPU(obj); - CPUSH4State *env = &cpu->env; - - cpu_set_cpustate_pointers(cpu); + CPUSH4State *env = cpu_env(CPU(obj)); env->movcal_backup_tail = &(env->movcal_backup); } @@ -233,9 +246,10 @@ static const struct SysemuCPUOps sh4_sysemu_ops = { #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps superh_tcg_ops = { +static const TCGCPUOps superh_tcg_ops = { .initialize = sh4_translate_init, .synchronize_from_tb = superh_cpu_synchronize_from_tb, + .restore_state_to_opc = superh_restore_state_to_opc, #ifndef CONFIG_USER_ONLY .tlb_fill = superh_cpu_tlb_fill, @@ -251,16 +265,20 @@ static void superh_cpu_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); device_class_set_parent_realize(dc, superh_cpu_realizefn, &scc->parent_realize); - device_class_set_parent_reset(dc, superh_cpu_reset, &scc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, superh_cpu_reset_hold, NULL, + &scc->parent_phases); cc->class_by_name = superh_cpu_class_by_name; cc->has_work = superh_cpu_has_work; + cc->mmu_index = sh4_cpu_mmu_index; cc->dump_state = superh_cpu_dump_state; cc->set_pc = superh_cpu_set_pc; + cc->get_pc = superh_cpu_get_pc; cc->gdb_read_register = superh_cpu_gdb_read_register; cc->gdb_write_register = superh_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY @@ -285,6 +303,7 @@ static const TypeInfo superh_cpu_type_infos[] = { .name = TYPE_SUPERH_CPU, .parent = TYPE_CPU, .instance_size = sizeof(SuperHCPU), + .instance_align = __alignof(SuperHCPU), .instance_init = superh_cpu_initfn, .abstract = true, .class_size = sizeof(SuperHCPUClass), diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index 9f15ef913c..d928bcf006 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -78,41 +78,48 @@ #define FPSCR_RM_NEAREST (0 << 0) #define FPSCR_RM_ZERO (1 << 0) -#define DELAY_SLOT_MASK 0x7 -#define DELAY_SLOT (1 << 0) -#define DELAY_SLOT_CONDITIONAL (1 << 1) -#define DELAY_SLOT_RTE (1 << 2) +#define TB_FLAG_DELAY_SLOT (1 << 0) +#define TB_FLAG_DELAY_SLOT_COND (1 << 1) +#define TB_FLAG_DELAY_SLOT_RTE (1 << 2) +#define TB_FLAG_PENDING_MOVCA (1 << 3) +#define TB_FLAG_GUSA_SHIFT 4 /* [11:4] */ +#define TB_FLAG_GUSA_EXCLUSIVE (1 << 12) +#define TB_FLAG_UNALIGN (1 << 13) +#define TB_FLAG_SR_FD (1 << SR_FD) /* 15 */ +#define TB_FLAG_FPSCR_PR FPSCR_PR /* 19 */ +#define TB_FLAG_FPSCR_SZ FPSCR_SZ /* 20 */ +#define TB_FLAG_FPSCR_FR FPSCR_FR /* 21 */ +#define TB_FLAG_SR_RB (1 << SR_RB) /* 29 */ +#define TB_FLAG_SR_MD (1 << SR_MD) /* 30 */ -#define TB_FLAG_PENDING_MOVCA (1 << 3) -#define TB_FLAG_UNALIGN (1 << 4) - -#define GUSA_SHIFT 4 -#ifdef CONFIG_USER_ONLY -#define GUSA_EXCLUSIVE (1 << 12) -#define GUSA_MASK ((0xff << GUSA_SHIFT) | GUSA_EXCLUSIVE) -#else -/* Provide dummy versions of the above to allow tests against tbflags - to be elided while avoiding ifdefs. */ -#define GUSA_EXCLUSIVE 0 -#define GUSA_MASK 0 -#endif - -#define TB_FLAG_ENVFLAGS_MASK (DELAY_SLOT_MASK | GUSA_MASK) +#define TB_FLAG_DELAY_SLOT_MASK (TB_FLAG_DELAY_SLOT | \ + TB_FLAG_DELAY_SLOT_COND | \ + TB_FLAG_DELAY_SLOT_RTE) +#define TB_FLAG_GUSA_MASK ((0xff << TB_FLAG_GUSA_SHIFT) | \ + TB_FLAG_GUSA_EXCLUSIVE) +#define TB_FLAG_FPSCR_MASK (TB_FLAG_FPSCR_PR | \ + TB_FLAG_FPSCR_SZ | \ + TB_FLAG_FPSCR_FR) +#define TB_FLAG_SR_MASK (TB_FLAG_SR_FD | \ + TB_FLAG_SR_RB | \ + TB_FLAG_SR_MD) +#define TB_FLAG_ENVFLAGS_MASK (TB_FLAG_DELAY_SLOT_MASK | \ + TB_FLAG_GUSA_MASK) typedef struct tlb_t { - uint32_t vpn; /* virtual page number */ - uint32_t ppn; /* physical page number */ - uint32_t size; /* mapped page size in bytes */ - uint8_t asid; /* address space identifier */ - uint8_t v:1; /* validity */ - uint8_t sz:2; /* page size */ - uint8_t sh:1; /* share status */ - uint8_t c:1; /* cacheability */ - uint8_t pr:2; /* protection key */ - uint8_t d:1; /* dirty */ - uint8_t wt:1; /* write through */ - uint8_t sa:3; /* space attribute (PCMCIA) */ - uint8_t tc:1; /* timing control */ + uint32_t vpn; /* virtual page number */ + uint32_t ppn; /* physical page number */ + uint32_t size; /* mapped page size in bytes */ + uint8_t asid; /* address space identifier */ + uint8_t v:1; /* validity */ + uint8_t sz:2; /* page size */ + uint8_t sh:1; /* share status */ + uint8_t c:1; /* cacheability */ + uint8_t pr:2; /* protection key */ + uint8_t d:1; /* dirty */ + uint8_t wt:1; /* write through */ + uint8_t sa:3; /* space attribute (PCMCIA) */ + uint8_t tc:1; /* timing control */ } tlb_t; #define UTLB_SIZE 64 @@ -132,44 +139,54 @@ typedef struct memory_content { } memory_content; typedef struct CPUArchState { - uint32_t flags; /* general execution flags */ - uint32_t gregs[24]; /* general registers */ - float32 fregs[32]; /* floating point registers */ + uint32_t flags; /* general execution flags */ + uint32_t gregs[24]; /* general registers */ + float32 fregs[32]; /* floating point registers */ uint32_t sr; /* status register (with T split out) */ uint32_t sr_m; /* M bit of status register */ uint32_t sr_q; /* Q bit of status register */ uint32_t sr_t; /* T bit of status register */ - uint32_t ssr; /* saved status register */ - uint32_t spc; /* saved program counter */ - uint32_t gbr; /* global base register */ - uint32_t vbr; /* vector base register */ - uint32_t sgr; /* saved global register 15 */ - uint32_t dbr; /* debug base register */ - uint32_t pc; /* program counter */ + uint32_t ssr; /* saved status register */ + uint32_t spc; /* saved program counter */ + uint32_t gbr; /* global base register */ + uint32_t vbr; /* vector base register */ + uint32_t sgr; /* saved global register 15 */ + uint32_t dbr; /* debug base register */ + uint32_t pc; /* program counter */ uint32_t delayed_pc; /* target of delayed branch */ uint32_t delayed_cond; /* condition of delayed branch */ - uint32_t mach; /* multiply and accumulate high */ - uint32_t macl; /* multiply and accumulate low */ - uint32_t pr; /* procedure register */ - uint32_t fpscr; /* floating point status/control register */ - uint32_t fpul; /* floating point communication register */ + uint32_t pr; /* procedure register */ + uint32_t fpscr; /* floating point status/control register */ + uint32_t fpul; /* floating point communication register */ + + /* multiply and accumulate: high, low and combined. */ + union { + uint64_t mac; + struct { +#if HOST_BIG_ENDIAN + uint32_t mach, macl; +#else + uint32_t macl, mach; +#endif + }; + }; /* float point status register */ float_status fp_status; /* Those belong to the specific unit (SH7750) but are handled here */ - uint32_t mmucr; /* MMU control register */ - uint32_t pteh; /* page table entry high register */ - uint32_t ptel; /* page table entry low register */ - uint32_t ptea; /* page table entry assistance register */ + uint32_t mmucr; /* MMU control register */ + uint32_t pteh; /* page table entry high register */ + uint32_t ptel; /* page table entry low register */ + uint32_t ptea; /* page table entry assistance register */ uint32_t ttb; /* translation table base register */ - uint32_t tea; /* TLB exception address register */ - uint32_t tra; /* TRAPA exception register */ - uint32_t expevt; /* exception event register */ - uint32_t intevt; /* interrupt event register */ + uint32_t tea; /* TLB exception address register */ + uint32_t tra; /* TRAPA exception register */ + uint32_t expevt; /* exception event register */ + uint32_t intevt; /* interrupt event register */ - tlb_t itlb[ITLB_SIZE]; /* instruction translation table */ - tlb_t utlb[UTLB_SIZE]; /* unified translation table */ + tlb_t itlb[ITLB_SIZE]; /* instruction translation table */ + tlb_t utlb[UTLB_SIZE]; /* unified translation table */ /* LDST = LOCK_ADDR != -1. */ uint32_t lock_addr; @@ -179,13 +196,13 @@ typedef struct CPUArchState { struct {} end_reset_fields; /* Fields from here on are preserved over CPU reset. */ - int id; /* CPU model */ + int id; /* CPU model */ /* The features that we should emulate. See sh_features above. */ uint32_t features; void *intc_handle; - int in_sleep; /* SR_BL ignored during sleep */ + int in_sleep; /* SR_BL ignored during sleep */ memory_content *movcal_backup; memory_content **movcal_backup_tail; } CPUSH4State; @@ -197,17 +214,33 @@ typedef struct CPUArchState { * A SuperH CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUSH4State env; }; +/** + * SuperHCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * @pvr: Processor Version Register + * @prr: Processor Revision Register + * @cvr: Cache Version Register + * + * A SuperH CPU model. + */ +struct SuperHCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; + + uint32_t pvr; + uint32_t prr; + uint32_t cvr; +}; void superh_cpu_dump_state(CPUState *cpu, FILE *f, int flags); -hwaddr superh_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int superh_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int superh_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); G_NORETURN void superh_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, @@ -215,9 +248,9 @@ G_NORETURN void superh_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, uintptr_t retaddr); void sh4_translate_init(void); -void sh4_cpu_list(void); #if !defined(CONFIG_USER_ONLY) +hwaddr superh_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); bool superh_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); @@ -246,24 +279,10 @@ int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr); void cpu_load_tlb(CPUSH4State * env); -#define SUPERH_CPU_TYPE_SUFFIX "-" TYPE_SUPERH_CPU -#define SUPERH_CPU_TYPE_NAME(model) model SUPERH_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_SUPERH_CPU -#define cpu_list sh4_cpu_list - /* MMU modes definitions */ #define MMU_USER_IDX 1 -static inline int cpu_mmu_index (CPUSH4State *env, bool ifetch) -{ - /* The instruction in a RTE delay slot is fetched in privileged - mode, but executed in user mode. */ - if (ifetch && (env->flags & DELAY_SLOT_RTE)) { - return 0; - } else { - return (env->sr & (1u << SR_MD)) == 0 ? 1 : 0; - } -} #include "exec/cpu-all.h" @@ -361,16 +380,15 @@ static inline void cpu_write_sr(CPUSH4State *env, target_ulong sr) env->sr = sr & ~((1u << SR_M) | (1u << SR_Q) | (1u << SR_T)); } -static inline void cpu_get_tb_cpu_state(CPUSH4State *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) +static inline void cpu_get_tb_cpu_state(CPUSH4State *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { *pc = env->pc; /* For a gUSA region, notice the end of the region. */ - *cs_base = env->flags & GUSA_MASK ? env->gregs[0] : 0; - *flags = env->flags /* TB_FLAG_ENVFLAGS_MASK: bits 0-2, 4-12 */ - | (env->fpscr & (FPSCR_FR | FPSCR_SZ | FPSCR_PR)) /* Bits 19-21 */ - | (env->sr & ((1u << SR_MD) | (1u << SR_RB))) /* Bits 29-30 */ - | (env->sr & (1u << SR_FD)) /* Bit 15 */ + *cs_base = env->flags & TB_FLAG_GUSA_MASK ? env->gregs[0] : 0; + *flags = env->flags + | (env->fpscr & TB_FLAG_FPSCR_MASK) + | (env->sr & TB_FLAG_SR_MASK) | (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 3 */ #ifdef CONFIG_USER_ONLY *flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; diff --git a/target/sh4/gdbstub.c b/target/sh4/gdbstub.c index 3488f68e32..75926d4e04 100644 --- a/target/sh4/gdbstub.c +++ b/target/sh4/gdbstub.c @@ -19,15 +19,14 @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" /* 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, GByteArray *mem_buf, int n) { - SuperHCPU *cpu = SUPERH_CPU(cs); - CPUSH4State *env = &cpu->env; + CPUSH4State *env = cpu_env(cs); switch (n) { case 0 ... 7: @@ -76,8 +75,7 @@ int superh_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int superh_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - SuperHCPU *cpu = SUPERH_CPU(cs); - CPUSH4State *env = &cpu->env; + CPUSH4State *env = cpu_env(cs); switch (n) { case 0 ... 7: diff --git a/target/sh4/helper.c b/target/sh4/helper.c index 6a620e36fc..7c6f9d374a 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -55,8 +55,7 @@ int cpu_sh4_is_cached(CPUSH4State *env, target_ulong addr) void superh_cpu_do_interrupt(CPUState *cs) { - SuperHCPU *cpu = SUPERH_CPU(cs); - CPUSH4State *env = &cpu->env; + CPUSH4State *env = cpu_env(cs); int do_irq = cs->interrupt_request & CPU_INTERRUPT_HARD; int do_exp, irq_vector = cs->exception_index; @@ -84,60 +83,60 @@ void superh_cpu_do_interrupt(CPUState *cs) if (do_irq) { irq_vector = sh_intc_get_pending_vector(env->intc_handle, - (env->sr >> 4) & 0xf); + (env->sr >> 4) & 0xf); if (irq_vector == -1) { return; /* masked */ - } + } } if (qemu_loglevel_mask(CPU_LOG_INT)) { - const char *expname; + const char *expname; switch (cs->exception_index) { - case 0x0e0: - expname = "addr_error"; - break; - case 0x040: - expname = "tlb_miss"; - break; - case 0x0a0: - expname = "tlb_violation"; - break; - case 0x180: - expname = "illegal_instruction"; - break; - case 0x1a0: - expname = "slot_illegal_instruction"; - break; - case 0x800: - expname = "fpu_disable"; - break; - case 0x820: - expname = "slot_fpu"; - break; - case 0x100: - expname = "data_write"; - break; - case 0x060: - expname = "dtlb_miss_write"; - break; - case 0x0c0: - expname = "dtlb_violation_write"; - break; - case 0x120: - expname = "fpu_exception"; - break; - case 0x080: - expname = "initial_page_write"; - break; - case 0x160: - expname = "trapa"; - break; - default: + case 0x0e0: + expname = "addr_error"; + break; + case 0x040: + expname = "tlb_miss"; + break; + case 0x0a0: + expname = "tlb_violation"; + break; + case 0x180: + expname = "illegal_instruction"; + break; + case 0x1a0: + expname = "slot_illegal_instruction"; + break; + case 0x800: + expname = "fpu_disable"; + break; + case 0x820: + expname = "slot_fpu"; + break; + case 0x100: + expname = "data_write"; + break; + case 0x060: + expname = "dtlb_miss_write"; + break; + case 0x0c0: + expname = "dtlb_violation_write"; + break; + case 0x120: + expname = "fpu_exception"; + break; + case 0x080: + expname = "initial_page_write"; + break; + case 0x160: + expname = "trapa"; + break; + default: expname = do_irq ? "interrupt" : "???"; break; - } - qemu_log("exception 0x%03x [%s] raised\n", - irq_vector, expname); + } + qemu_log("exception 0x%03x [%s] raised\n", + irq_vector, expname); log_cpu_state(cs, 0); } @@ -147,11 +146,11 @@ void superh_cpu_do_interrupt(CPUState *cs) env->sr |= (1u << SR_BL) | (1u << SR_MD) | (1u << SR_RB); env->lock_addr = -1; - if (env->flags & DELAY_SLOT_MASK) { + if (env->flags & TB_FLAG_DELAY_SLOT_MASK) { /* Branch instruction should be executed again before delay slot. */ - env->spc -= 2; - /* Clear flags for exception/interrupt routine. */ - env->flags &= ~DELAY_SLOT_MASK; + env->spc -= 2; + /* Clear flags for exception/interrupt routine. */ + env->flags &= ~TB_FLAG_DELAY_SLOT_MASK; } if (do_exp) { @@ -191,19 +190,19 @@ static void update_itlb_use(CPUSH4State * env, int itlbnb) switch (itlbnb) { case 0: - and_mask = 0x1f; - break; + and_mask = 0x1f; + break; case 1: - and_mask = 0xe7; - or_mask = 0x80; - break; + and_mask = 0xe7; + or_mask = 0x80; + break; case 2: - and_mask = 0xfb; - or_mask = 0x50; - break; + and_mask = 0xfb; + or_mask = 0x50; + break; case 3: - or_mask = 0x2c; - break; + or_mask = 0x2c; + break; } env->mmucr &= (and_mask << 24) | 0x00ffffff; @@ -213,16 +212,16 @@ static void update_itlb_use(CPUSH4State * env, int itlbnb) static int itlb_replacement(CPUSH4State * env) { if ((env->mmucr & 0xe0000000) == 0xe0000000) { - return 0; + return 0; } if ((env->mmucr & 0x98000000) == 0x18000000) { - return 1; + return 1; } if ((env->mmucr & 0x54000000) == 0x04000000) { - return 2; + return 2; } if ((env->mmucr & 0x2c000000) == 0x00000000) { - return 3; + return 3; } cpu_abort(env_cpu(env), "Unhandled itlb_replacement"); } @@ -231,7 +230,7 @@ static int itlb_replacement(CPUSH4State * env) Return entry, MMU_DTLB_MISS or MMU_DTLB_MULTIPLE */ static int find_tlb_entry(CPUSH4State * env, target_ulong address, - tlb_t * entries, uint8_t nbtlb, int use_asid) + tlb_t * entries, uint8_t nbtlb, int use_asid) { int match = MMU_DTLB_MISS; uint32_t start, end; @@ -241,17 +240,17 @@ static int find_tlb_entry(CPUSH4State * env, target_ulong address, asid = env->pteh & 0xff; for (i = 0; i < nbtlb; i++) { - if (!entries[i].v) - continue; /* Invalid entry */ - if (!entries[i].sh && use_asid && entries[i].asid != asid) - continue; /* Bad ASID */ - start = (entries[i].vpn << 10) & ~(entries[i].size - 1); - end = start + entries[i].size - 1; - if (address >= start && address <= end) { /* Match */ - if (match != MMU_DTLB_MISS) - return MMU_DTLB_MULTIPLE; /* Multiple match */ - match = i; - } + if (!entries[i].v) + continue; /* Invalid entry */ + if (!entries[i].sh && use_asid && entries[i].asid != asid) + continue; /* Bad ASID */ + start = (entries[i].vpn << 10) & ~(entries[i].size - 1); + end = start + entries[i].size - 1; + if (address >= start && address <= end) { /* Match */ + if (match != MMU_DTLB_MISS) + return MMU_DTLB_MULTIPLE; /* Multiple match */ + match = i; + } } return match; } @@ -265,7 +264,7 @@ static void increment_urc(CPUSH4State * env) urc = ((env->mmucr) >> 10) & 0x3f; urc++; if ((urb > 0 && urc > urb) || urc > (UTLB_SIZE - 1)) - urc = 0; + urc = 0; env->mmucr = (env->mmucr & 0xffff03ff) | (urc << 10); } @@ -297,11 +296,11 @@ static int find_itlb_entry(CPUSH4State * env, target_ulong address, e = find_tlb_entry(env, address, env->itlb, ITLB_SIZE, use_asid); if (e == MMU_DTLB_MULTIPLE) { - e = MMU_ITLB_MULTIPLE; + e = MMU_ITLB_MULTIPLE; } else if (e == MMU_DTLB_MISS) { - e = MMU_ITLB_MISS; + e = MMU_ITLB_MISS; } else if (e >= 0) { - update_itlb_use(env, e); + update_itlb_use(env, e); } return e; } @@ -432,11 +431,10 @@ static int get_physical_address(CPUSH4State * env, target_ulong * physical, hwaddr superh_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { - SuperHCPU *cpu = SUPERH_CPU(cs); target_ulong physical; int prot; - if (get_physical_address(&cpu->env, &physical, &prot, addr, MMU_DATA_LOAD) + if (get_physical_address(cpu_env(cs), &physical, &prot, addr, MMU_DATA_LOAD) == MMU_OK) { return physical; } @@ -518,7 +516,7 @@ uint32_t cpu_sh4_read_mmaped_itlb_addr(CPUSH4State *s, } void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, hwaddr addr, - uint32_t mem_value) + uint32_t mem_value) { uint32_t vpn = (mem_value & 0xfffffc00) >> 10; uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8); @@ -601,7 +599,7 @@ uint32_t cpu_sh4_read_mmaped_utlb_addr(CPUSH4State *s, } void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, hwaddr addr, - uint32_t mem_value) + uint32_t mem_value) { int associate = addr & 0x0000080; uint32_t vpn = (mem_value & 0xfffffc00) >> 10; @@ -612,48 +610,48 @@ void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, hwaddr addr, if (associate) { int i; - tlb_t * utlb_match_entry = NULL; - int needs_tlb_flush = 0; + tlb_t * utlb_match_entry = NULL; + int needs_tlb_flush = 0; - /* search UTLB */ - for (i = 0; i < UTLB_SIZE; i++) { + /* search UTLB */ + for (i = 0; i < UTLB_SIZE; i++) { tlb_t * entry = &s->utlb[i]; if (!entry->v) - continue; + continue; if (entry->vpn == vpn && (!use_asid || entry->asid == asid || entry->sh)) { - if (utlb_match_entry) { + if (utlb_match_entry) { CPUState *cs = env_cpu(s); - /* Multiple TLB Exception */ + /* Multiple TLB Exception */ cs->exception_index = 0x140; - s->tea = addr; - break; - } - if (entry->v && !v) - needs_tlb_flush = 1; - entry->v = v; - entry->d = d; - utlb_match_entry = entry; - } - increment_urc(s); /* per utlb access */ - } + s->tea = addr; + break; + } + if (entry->v && !v) + needs_tlb_flush = 1; + entry->v = v; + entry->d = d; + utlb_match_entry = entry; + } + increment_urc(s); /* per utlb access */ + } - /* search ITLB */ - for (i = 0; i < ITLB_SIZE; i++) { + /* search ITLB */ + for (i = 0; i < ITLB_SIZE; i++) { tlb_t * entry = &s->itlb[i]; if (entry->vpn == vpn && (!use_asid || entry->asid == asid || entry->sh)) { - if (entry->v && !v) - needs_tlb_flush = 1; - if (utlb_match_entry) - *entry = *utlb_match_entry; - else - entry->v = v; - break; - } - } + if (entry->v && !v) + needs_tlb_flush = 1; + if (utlb_match_entry) + *entry = *utlb_match_entry; + else + entry->v = v; + break; + } + } if (needs_tlb_flush) { tlb_flush_page(env_cpu(s), vpn << 10); @@ -661,18 +659,18 @@ void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, hwaddr addr, } else { int index = (addr & 0x00003f00) >> 8; tlb_t * entry = &s->utlb[index]; - if (entry->v) { + if (entry->v) { CPUState *cs = env_cpu(s); - /* Overwriting valid entry in utlb. */ + /* Overwriting valid entry in utlb. */ target_ulong address = entry->vpn << 10; tlb_flush_page(cs, address); - } - entry->asid = asid; - entry->vpn = vpn; - entry->d = d; - entry->v = v; - increment_urc(s); + } + entry->asid = asid; + entry->vpn = vpn; + entry->d = d; + entry->v = v; + increment_urc(s); } } @@ -782,11 +780,8 @@ int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr) bool superh_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { if (interrupt_request & CPU_INTERRUPT_HARD) { - SuperHCPU *cpu = SUPERH_CPU(cs); - CPUSH4State *env = &cpu->env; - /* Delay slots are indivisible, ignore interrupts */ - if (env->flags & DELAY_SLOT_MASK) { + if (cpu_env(cs)->flags & TB_FLAG_DELAY_SLOT_MASK) { return false; } else { superh_cpu_do_interrupt(cs); @@ -800,8 +795,7 @@ bool superh_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - SuperHCPU *cpu = SUPERH_CPU(cs); - CPUSH4State *env = &cpu->env; + CPUSH4State *env = cpu_env(cs); int ret; target_ulong physical; diff --git a/target/sh4/helper.h b/target/sh4/helper.h index 8d792f6b55..29011d3dbb 100644 --- a/target/sh4/helper.h +++ b/target/sh4/helper.h @@ -11,8 +11,8 @@ DEF_HELPER_3(movcal, void, env, i32, i32) DEF_HELPER_1(discard_movcal_backup, void, env) DEF_HELPER_2(ocbi, void, env, i32) -DEF_HELPER_3(macl, void, env, i32, i32) -DEF_HELPER_3(macw, void, env, i32, i32) +DEF_HELPER_3(macl, void, env, s32, s32) +DEF_HELPER_3(macw, void, env, s32, s32) DEF_HELPER_2(ld_fpscr, void, env, i32) diff --git a/target/sh4/meson.build b/target/sh4/meson.build index 56a57576da..fe09f96684 100644 --- a/target/sh4/meson.build +++ b/target/sh4/meson.build @@ -7,8 +7,8 @@ sh4_ss.add(files( 'translate.c', )) -sh4_softmmu_ss = ss.source_set() -sh4_softmmu_ss.add(files('monitor.c')) +sh4_system_ss = ss.source_set() +sh4_system_ss.add(files('monitor.c')) target_arch += {'sh4': sh4_ss} -target_softmmu_arch += {'sh4': sh4_softmmu_ss} +target_system_arch += {'sh4': sh4_system_ss} diff --git a/target/sh4/op_helper.c b/target/sh4/op_helper.c index a663335c39..99394b714c 100644 --- a/target/sh4/op_helper.c +++ b/target/sh4/op_helper.c @@ -29,9 +29,7 @@ void superh_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { - CPUSH4State *env = cs->env_ptr; - - env->tea = addr; + cpu_env(cs)->tea = addr; switch (access_type) { case MMU_INST_FETCH: case MMU_DATA_LOAD: @@ -114,12 +112,12 @@ void helper_movcal(CPUSH4State *env, uint32_t address, uint32_t value) { memory_content *r = g_new(memory_content, 1); - r->address = address; - r->value = value; - r->next = NULL; + r->address = address; + r->value = value; + r->next = NULL; - *(env->movcal_backup_tail) = r; - env->movcal_backup_tail = &(r->next); + *(env->movcal_backup_tail) = r; + env->movcal_backup_tail = &(r->next); } } @@ -129,12 +127,12 @@ void helper_discard_movcal_backup(CPUSH4State *env) while(current) { - memory_content *next = current->next; + memory_content *next = current->next; g_free(current); - env->movcal_backup = current = next; - if (current == NULL) - env->movcal_backup_tail = &(env->movcal_backup); - } + env->movcal_backup = current = next; + if (current == NULL) + env->movcal_backup_tail = &(env->movcal_backup); + } } void helper_ocbi(CPUSH4State *env, uint32_t address) @@ -142,56 +140,65 @@ void helper_ocbi(CPUSH4State *env, uint32_t address) memory_content **current = &(env->movcal_backup); while (*current) { - uint32_t a = (*current)->address; - if ((a & ~0x1F) == (address & ~0x1F)) - { - memory_content *next = (*current)->next; + uint32_t a = (*current)->address; + if ((a & ~0x1F) == (address & ~0x1F)) + { + memory_content *next = (*current)->next; cpu_stl_data(env, a, (*current)->value); - - if (next == NULL) - { - env->movcal_backup_tail = current; - } + + if (next == NULL) + { + env->movcal_backup_tail = current; + } g_free(*current); - *current = next; - break; - } + *current = next; + break; + } } } -void helper_macl(CPUSH4State *env, uint32_t arg0, uint32_t arg1) +void helper_macl(CPUSH4State *env, int32_t arg0, int32_t arg1) { + const int64_t min = -(1ll << 47); + const int64_t max = (1ll << 47) - 1; + int64_t mul = (int64_t)arg0 * arg1; + int64_t mac = env->mac; int64_t res; - res = ((uint64_t) env->mach << 32) | env->macl; - res += (int64_t) (int32_t) arg0 *(int64_t) (int32_t) arg1; - env->mach = (res >> 32) & 0xffffffff; - env->macl = res & 0xffffffff; - if (env->sr & (1u << SR_S)) { - if (res < 0) - env->mach |= 0xffff0000; - else - env->mach &= 0x00007fff; + if (!(env->sr & (1u << SR_S))) { + res = mac + mul; + } else if (sadd64_overflow(mac, mul, &res)) { + res = mac < 0 ? min : max; + } else { + res = MIN(MAX(res, min), max); } + + env->mac = res; } -void helper_macw(CPUSH4State *env, uint32_t arg0, uint32_t arg1) +void helper_macw(CPUSH4State *env, int32_t arg0, int32_t arg1) { - int64_t res; + /* Inputs are already sign-extended from 16 bits. */ + int32_t mul = arg0 * arg1; - res = ((uint64_t) env->mach << 32) | env->macl; - res += (int64_t) (int16_t) arg0 *(int64_t) (int16_t) arg1; - env->mach = (res >> 32) & 0xffffffff; - env->macl = res & 0xffffffff; if (env->sr & (1u << SR_S)) { - if (res < -0x80000000) { - env->mach = 1; - env->macl = 0x80000000; - } else if (res > 0x000000007fffffff) { - env->mach = 1; - env->macl = 0x7fffffff; - } + /* + * In saturation arithmetic mode, the accumulator is 32-bit + * with carry. MACH is not considered during the addition + * operation nor the 32-bit saturation logic. + */ + int32_t res, macl = env->macl; + + if (sadd32_overflow(macl, mul, &res)) { + res = macl < 0 ? INT32_MIN : INT32_MAX; + /* If overflow occurs, the MACH register is set to 1. */ + env->mach = 1; + } + env->macl = res; + } else { + /* In non-saturation arithmetic mode, the accumulator is 64-bit */ + env->mac += mul; } } @@ -199,9 +206,9 @@ void helper_ld_fpscr(CPUSH4State *env, uint32_t val) { env->fpscr = val & FPSCR_MASK; if ((val & FPSCR_RM_MASK) == FPSCR_RM_ZERO) { - set_float_rounding_mode(float_round_to_zero, &env->fp_status); + set_float_rounding_mode(float_round_to_zero, &env->fp_status); } else { - set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + set_float_rounding_mode(float_round_nearest_even, &env->fp_status); } set_flush_to_zero((val & FPSCR_DN) != 0, &env->fp_status); } diff --git a/target/sh4/translate.c b/target/sh4/translate.c index f1b190e7cf..ebb6c901bf 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -17,20 +17,21 @@ * License along with this library; if not, see . */ -#define DEBUG_DISAS - #include "qemu/osdep.h" #include "cpu.h" #include "disas/disas.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 "exec/log.h" #include "qemu/qemu-print.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + typedef struct DisasContext { DisasContextBase base; @@ -73,8 +74,6 @@ static TCGv cpu_fregs[32]; /* internal register indexes */ static TCGv cpu_flags, cpu_delayed_pc, cpu_delayed_cond; -#include "exec/gen-icount.h" - void sh4_translate_init(void) { int i; @@ -97,71 +96,70 @@ void sh4_translate_init(void) }; for (i = 0; i < 24; i++) { - cpu_gregs[i] = tcg_global_mem_new_i32(cpu_env, + cpu_gregs[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, gregs[i]), gregnames[i]); } memcpy(cpu_gregs + 24, cpu_gregs + 8, 8 * sizeof(TCGv)); - cpu_pc = tcg_global_mem_new_i32(cpu_env, + cpu_pc = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, pc), "PC"); - cpu_sr = tcg_global_mem_new_i32(cpu_env, + cpu_sr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, sr), "SR"); - cpu_sr_m = tcg_global_mem_new_i32(cpu_env, + cpu_sr_m = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, sr_m), "SR_M"); - cpu_sr_q = tcg_global_mem_new_i32(cpu_env, + cpu_sr_q = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, sr_q), "SR_Q"); - cpu_sr_t = tcg_global_mem_new_i32(cpu_env, + cpu_sr_t = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, sr_t), "SR_T"); - cpu_ssr = tcg_global_mem_new_i32(cpu_env, + cpu_ssr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, ssr), "SSR"); - cpu_spc = tcg_global_mem_new_i32(cpu_env, + cpu_spc = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, spc), "SPC"); - cpu_gbr = tcg_global_mem_new_i32(cpu_env, + cpu_gbr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, gbr), "GBR"); - cpu_vbr = tcg_global_mem_new_i32(cpu_env, + cpu_vbr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, vbr), "VBR"); - cpu_sgr = tcg_global_mem_new_i32(cpu_env, + cpu_sgr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, sgr), "SGR"); - cpu_dbr = tcg_global_mem_new_i32(cpu_env, + cpu_dbr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, dbr), "DBR"); - cpu_mach = tcg_global_mem_new_i32(cpu_env, + cpu_mach = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, mach), "MACH"); - cpu_macl = tcg_global_mem_new_i32(cpu_env, + cpu_macl = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, macl), "MACL"); - cpu_pr = tcg_global_mem_new_i32(cpu_env, + cpu_pr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, pr), "PR"); - cpu_fpscr = tcg_global_mem_new_i32(cpu_env, + cpu_fpscr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, fpscr), "FPSCR"); - cpu_fpul = tcg_global_mem_new_i32(cpu_env, + cpu_fpul = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, fpul), "FPUL"); - cpu_flags = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUSH4State, flags), "_flags_"); - cpu_delayed_pc = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUSH4State, delayed_pc), - "_delayed_pc_"); - cpu_delayed_cond = tcg_global_mem_new_i32(cpu_env, + cpu_flags = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUSH4State, flags), "_flags_"); + cpu_delayed_pc = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUSH4State, delayed_pc), + "_delayed_pc_"); + cpu_delayed_cond = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, delayed_cond), "_delayed_cond_"); - cpu_lock_addr = tcg_global_mem_new_i32(cpu_env, + cpu_lock_addr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, lock_addr), "_lock_addr_"); - cpu_lock_value = tcg_global_mem_new_i32(cpu_env, + cpu_lock_value = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, lock_value), "_lock_value_"); for (i = 0; i < 32; i++) - cpu_fregs[i] = tcg_global_mem_new_i32(cpu_env, + cpu_fregs[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, fregs[i]), fregnames[i]); } void superh_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - SuperHCPU *cpu = SUPERH_CPU(cs); - CPUSH4State *env = &cpu->env; + CPUSH4State *env = cpu_env(cs); int i; qemu_fprintf(f, "pc=0x%08x sr=0x%08x pr=0x%08x fpscr=0x%08x\n", @@ -171,17 +169,17 @@ void superh_cpu_dump_state(CPUState *cs, FILE *f, int flags) qemu_fprintf(f, "sgr=0x%08x dbr=0x%08x delayed_pc=0x%08x fpul=0x%08x\n", env->sgr, env->dbr, env->delayed_pc, env->fpul); for (i = 0; i < 24; i += 4) { - qemu_printf("r%d=0x%08x r%d=0x%08x r%d=0x%08x r%d=0x%08x\n", - i, env->gregs[i], i + 1, env->gregs[i + 1], - i + 2, env->gregs[i + 2], i + 3, env->gregs[i + 3]); + qemu_fprintf(f, "r%d=0x%08x r%d=0x%08x r%d=0x%08x r%d=0x%08x\n", + i, env->gregs[i], i + 1, env->gregs[i + 1], + i + 2, env->gregs[i + 2], i + 3, env->gregs[i + 3]); } - if (env->flags & DELAY_SLOT) { - qemu_printf("in delay slot (delayed_pc=0x%08x)\n", - env->delayed_pc); - } else if (env->flags & DELAY_SLOT_CONDITIONAL) { - qemu_printf("in conditional delay slot (delayed_pc=0x%08x)\n", - env->delayed_pc); - } else if (env->flags & DELAY_SLOT_RTE) { + if (env->flags & TB_FLAG_DELAY_SLOT) { + qemu_fprintf(f, "in delay slot (delayed_pc=0x%08x)\n", + env->delayed_pc); + } else if (env->flags & TB_FLAG_DELAY_SLOT_COND) { + qemu_fprintf(f, "in conditional delay slot (delayed_pc=0x%08x)\n", + env->delayed_pc); + } else if (env->flags & TB_FLAG_DELAY_SLOT_RTE) { qemu_fprintf(f, "in rte delay slot (delayed_pc=0x%08x)\n", env->delayed_pc); } @@ -196,7 +194,6 @@ static void gen_read_sr(TCGv dst) tcg_gen_or_i32(dst, dst, t0); tcg_gen_shli_i32(t0, cpu_sr_t, SR_T); tcg_gen_or_i32(dst, cpu_sr, t0); - tcg_temp_free_i32(t0); } static void gen_write_sr(TCGv src) @@ -223,7 +220,7 @@ static inline void gen_save_cpu_state(DisasContext *ctx, bool save_pc) static inline bool use_exit_tb(DisasContext *ctx) { - return (ctx->tbflags & GUSA_EXCLUSIVE) != 0; + return (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE) != 0; } static bool use_goto_tb(DisasContext *ctx, target_ulong dest) @@ -254,9 +251,9 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) static void gen_jump(DisasContext * ctx) { if (ctx->delayed_pc == -1) { - /* Target is not statically known, it comes necessarily from a - delayed jump as immediate jump are conditinal jumps */ - tcg_gen_mov_i32(cpu_pc, cpu_delayed_pc); + /* Target is not statically known, it comes necessarily from a + delayed jump as immediate jump are conditinal jumps */ + tcg_gen_mov_i32(cpu_pc, cpu_delayed_pc); tcg_gen_discard_i32(cpu_delayed_pc); if (use_exit_tb(ctx)) { tcg_gen_exit_tb(NULL, 0); @@ -265,7 +262,7 @@ static void gen_jump(DisasContext * ctx) } ctx->base.is_jmp = DISAS_NORETURN; } else { - gen_goto_tb(ctx, 0, ctx->delayed_pc); + gen_goto_tb(ctx, 0, ctx->delayed_pc); } } @@ -276,12 +273,12 @@ static void gen_conditional_jump(DisasContext *ctx, target_ulong dest, TCGLabel *l1 = gen_new_label(); TCGCond cond_not_taken = jump_if_true ? TCG_COND_EQ : TCG_COND_NE; - if (ctx->tbflags & GUSA_EXCLUSIVE) { + if (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE) { /* When in an exclusive region, we must continue to the end. Therefore, exit the region on a taken branch, but otherwise fall through to the next instruction. */ tcg_gen_brcondi_i32(cond_not_taken, cpu_sr_t, 0, l1); - tcg_gen_movi_i32(cpu_flags, ctx->envflags & ~GUSA_MASK); + tcg_gen_movi_i32(cpu_flags, ctx->envflags & ~TB_FLAG_GUSA_MASK); /* Note that this won't actually use a goto_tb opcode because we disallow it in use_goto_tb, but it handles exit + singlestep. */ gen_goto_tb(ctx, 0, dest); @@ -307,14 +304,14 @@ static void gen_delayed_conditional_jump(DisasContext * ctx) tcg_gen_mov_i32(ds, cpu_delayed_cond); tcg_gen_discard_i32(cpu_delayed_cond); - if (ctx->tbflags & GUSA_EXCLUSIVE) { + if (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE) { /* When in an exclusive region, we must continue to the end. Therefore, exit the region on a taken branch, but otherwise fall through to the next instruction. */ tcg_gen_brcondi_i32(TCG_COND_EQ, ds, 0, l1); /* Leave the gUSA region. */ - tcg_gen_movi_i32(cpu_flags, ctx->envflags & ~GUSA_MASK); + tcg_gen_movi_i32(cpu_flags, ctx->envflags & ~TB_FLAG_GUSA_MASK); gen_jump(ctx); gen_set_label(l1); @@ -361,8 +358,8 @@ static inline void gen_store_fpr64(DisasContext *ctx, TCGv_i64 t, int reg) #define XHACK(x) ((((x) & 1 ) << 4) | ((x) & 0xe)) #define CHECK_NOT_DELAY_SLOT \ - if (ctx->envflags & DELAY_SLOT_MASK) { \ - goto do_illegal_slot; \ + if (ctx->envflags & TB_FLAG_DELAY_SLOT_MASK) { \ + goto do_illegal_slot; \ } #define CHECK_PRIVILEGED \ @@ -413,311 +410,300 @@ static void _decode_opc(DisasContext * ctx) TB, or if we already saw movca.l in this TB and did not flush stores yet. */ if (ctx->has_movcal) - { - int opcode = ctx->opcode & 0xf0ff; - if (opcode != 0x0093 /* ocbi */ - && opcode != 0x00c3 /* movca.l */) - { - gen_helper_discard_movcal_backup(cpu_env); - ctx->has_movcal = 0; - } - } + { + int opcode = ctx->opcode & 0xf0ff; + if (opcode != 0x0093 /* ocbi */ + && opcode != 0x00c3 /* movca.l */) + { + gen_helper_discard_movcal_backup(tcg_env); + ctx->has_movcal = 0; + } + } #if 0 fprintf(stderr, "Translating opcode 0x%04x\n", ctx->opcode); #endif switch (ctx->opcode) { - case 0x0019: /* div0u */ + case 0x0019: /* div0u */ tcg_gen_movi_i32(cpu_sr_m, 0); tcg_gen_movi_i32(cpu_sr_q, 0); tcg_gen_movi_i32(cpu_sr_t, 0); - return; - case 0x000b: /* rts */ - CHECK_NOT_DELAY_SLOT - tcg_gen_mov_i32(cpu_delayed_pc, cpu_pr); - ctx->envflags |= DELAY_SLOT; - ctx->delayed_pc = (uint32_t) - 1; - return; - case 0x0028: /* clrmac */ - tcg_gen_movi_i32(cpu_mach, 0); - tcg_gen_movi_i32(cpu_macl, 0); - return; - case 0x0048: /* clrs */ + return; + case 0x000b: /* rts */ + CHECK_NOT_DELAY_SLOT + tcg_gen_mov_i32(cpu_delayed_pc, cpu_pr); + ctx->envflags |= TB_FLAG_DELAY_SLOT; + ctx->delayed_pc = (uint32_t) - 1; + return; + case 0x0028: /* clrmac */ + tcg_gen_movi_i32(cpu_mach, 0); + tcg_gen_movi_i32(cpu_macl, 0); + return; + case 0x0048: /* clrs */ tcg_gen_andi_i32(cpu_sr, cpu_sr, ~(1u << SR_S)); - return; - case 0x0008: /* clrt */ + return; + case 0x0008: /* clrt */ tcg_gen_movi_i32(cpu_sr_t, 0); - return; - case 0x0038: /* ldtlb */ - CHECK_PRIVILEGED - gen_helper_ldtlb(cpu_env); - return; - case 0x002b: /* rte */ - CHECK_PRIVILEGED - CHECK_NOT_DELAY_SLOT + return; + case 0x0038: /* ldtlb */ + CHECK_PRIVILEGED + gen_helper_ldtlb(tcg_env); + return; + case 0x002b: /* rte */ + CHECK_PRIVILEGED + CHECK_NOT_DELAY_SLOT gen_write_sr(cpu_ssr); - tcg_gen_mov_i32(cpu_delayed_pc, cpu_spc); - ctx->envflags |= DELAY_SLOT_RTE; - ctx->delayed_pc = (uint32_t) - 1; + tcg_gen_mov_i32(cpu_delayed_pc, cpu_spc); + ctx->envflags |= TB_FLAG_DELAY_SLOT_RTE; + ctx->delayed_pc = (uint32_t) - 1; ctx->base.is_jmp = DISAS_STOP; - return; - case 0x0058: /* sets */ + return; + case 0x0058: /* sets */ tcg_gen_ori_i32(cpu_sr, cpu_sr, (1u << SR_S)); - return; - case 0x0018: /* sett */ + return; + case 0x0018: /* sett */ tcg_gen_movi_i32(cpu_sr_t, 1); - return; - case 0xfbfd: /* frchg */ + return; + case 0xfbfd: /* frchg */ CHECK_FPSCR_PR_0 - tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_FR); + tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_FR); ctx->base.is_jmp = DISAS_STOP; - return; - case 0xf3fd: /* fschg */ + return; + case 0xf3fd: /* fschg */ CHECK_FPSCR_PR_0 tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_SZ); ctx->base.is_jmp = DISAS_STOP; - return; - case 0xf7fd: /* fpchg */ + return; + case 0xf7fd: /* fpchg */ CHECK_SH4A tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_PR); ctx->base.is_jmp = DISAS_STOP; return; - case 0x0009: /* nop */ - return; - case 0x001b: /* sleep */ - CHECK_PRIVILEGED + case 0x0009: /* nop */ + return; + case 0x001b: /* sleep */ + CHECK_PRIVILEGED tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next + 2); - gen_helper_sleep(cpu_env); - return; + gen_helper_sleep(tcg_env); + return; } switch (ctx->opcode & 0xf000) { - case 0x1000: /* mov.l Rm,@(disp,Rn) */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_addi_i32(addr, REG(B11_8), B3_0 * 4); + case 0x1000: /* mov.l Rm,@(disp,Rn) */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_addi_i32(addr, REG(B11_8), B3_0 * 4); tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUL | UNALIGN(ctx)); - tcg_temp_free(addr); - } - return; - case 0x5000: /* mov.l @(disp,Rm),Rn */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 4); + } + return; + case 0x5000: /* mov.l @(disp,Rm),Rn */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 4); tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL | UNALIGN(ctx)); - tcg_temp_free(addr); - } - return; - case 0xe000: /* mov #imm,Rn */ + } + return; + case 0xe000: /* mov #imm,Rn */ #ifdef CONFIG_USER_ONLY - /* Detect the start of a gUSA region. If so, update envflags - and end the TB. This will allow us to see the end of the - region (stored in R0) in the next TB. */ + /* + * Detect the start of a gUSA region (mov #-n, r15). + * If so, update envflags and end the TB. This will allow us + * to see the end of the region (stored in R0) in the next TB. + */ if (B11_8 == 15 && B7_0s < 0 && (tb_cflags(ctx->base.tb) & CF_PARALLEL)) { - ctx->envflags = deposit32(ctx->envflags, GUSA_SHIFT, 8, B7_0s); + ctx->envflags = + deposit32(ctx->envflags, TB_FLAG_GUSA_SHIFT, 8, B7_0s); ctx->base.is_jmp = DISAS_STOP; } #endif - tcg_gen_movi_i32(REG(B11_8), B7_0s); - return; - case 0x9000: /* mov.w @(disp,PC),Rn */ - { - TCGv addr = tcg_const_i32(ctx->base.pc_next + 4 + B7_0 * 2); - tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESW); - tcg_temp_free(addr); - } - return; - case 0xd000: /* mov.l @(disp,PC),Rn */ - { - TCGv addr = tcg_const_i32((ctx->base.pc_next + 4 + B7_0 * 4) & ~3); - tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL); - tcg_temp_free(addr); - } - return; - case 0x7000: /* add #imm,Rn */ - tcg_gen_addi_i32(REG(B11_8), REG(B11_8), B7_0s); - return; - case 0xa000: /* bra disp */ - CHECK_NOT_DELAY_SLOT + tcg_gen_movi_i32(REG(B11_8), B7_0s); + return; + case 0x9000: /* mov.w @(disp,PC),Rn */ + CHECK_NOT_DELAY_SLOT + { + TCGv addr = tcg_constant_i32(ctx->base.pc_next + 4 + B7_0 * 2); + tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, + MO_TESW | MO_ALIGN); + } + return; + case 0xd000: /* mov.l @(disp,PC),Rn */ + CHECK_NOT_DELAY_SLOT + { + TCGv addr = tcg_constant_i32((ctx->base.pc_next + 4 + B7_0 * 4) & ~3); + tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, + MO_TESL | MO_ALIGN); + } + return; + case 0x7000: /* add #imm,Rn */ + tcg_gen_addi_i32(REG(B11_8), REG(B11_8), B7_0s); + return; + case 0xa000: /* bra disp */ + CHECK_NOT_DELAY_SLOT ctx->delayed_pc = ctx->base.pc_next + 4 + B11_0s * 2; - ctx->envflags |= DELAY_SLOT; - return; - case 0xb000: /* bsr disp */ - CHECK_NOT_DELAY_SLOT + ctx->envflags |= TB_FLAG_DELAY_SLOT; + return; + case 0xb000: /* bsr disp */ + CHECK_NOT_DELAY_SLOT tcg_gen_movi_i32(cpu_pr, ctx->base.pc_next + 4); ctx->delayed_pc = ctx->base.pc_next + 4 + B11_0s * 2; - ctx->envflags |= DELAY_SLOT; - return; + ctx->envflags |= TB_FLAG_DELAY_SLOT; + return; } switch (ctx->opcode & 0xf00f) { - case 0x6003: /* mov Rm,Rn */ - tcg_gen_mov_i32(REG(B11_8), REG(B7_4)); - return; - case 0x2000: /* mov.b Rm,@Rn */ + case 0x6003: /* mov Rm,Rn */ + tcg_gen_mov_i32(REG(B11_8), REG(B7_4)); + return; + case 0x2000: /* mov.b Rm,@Rn */ tcg_gen_qemu_st_i32(REG(B7_4), REG(B11_8), ctx->memidx, MO_UB); - return; - case 0x2001: /* mov.w Rm,@Rn */ + return; + case 0x2001: /* mov.w Rm,@Rn */ tcg_gen_qemu_st_i32(REG(B7_4), REG(B11_8), ctx->memidx, MO_TEUW | UNALIGN(ctx)); - return; - case 0x2002: /* mov.l Rm,@Rn */ + return; + case 0x2002: /* mov.l Rm,@Rn */ tcg_gen_qemu_st_i32(REG(B7_4), REG(B11_8), ctx->memidx, MO_TEUL | UNALIGN(ctx)); - return; - case 0x6000: /* mov.b @Rm,Rn */ + return; + case 0x6000: /* mov.b @Rm,Rn */ tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_SB); - return; - case 0x6001: /* mov.w @Rm,Rn */ + return; + case 0x6001: /* mov.w @Rm,Rn */ tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESW | UNALIGN(ctx)); - return; - case 0x6002: /* mov.l @Rm,Rn */ + return; + case 0x6002: /* mov.l @Rm,Rn */ tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESL | UNALIGN(ctx)); - return; - case 0x2004: /* mov.b Rm,@-Rn */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_subi_i32(addr, REG(B11_8), 1); + return; + case 0x2004: /* mov.b Rm,@-Rn */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_subi_i32(addr, REG(B11_8), 1); /* might cause re-execution */ tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_UB); - tcg_gen_mov_i32(REG(B11_8), addr); /* modify register status */ - tcg_temp_free(addr); - } - return; - case 0x2005: /* mov.w Rm,@-Rn */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_subi_i32(addr, REG(B11_8), 2); + tcg_gen_mov_i32(REG(B11_8), addr); /* modify register status */ + } + return; + case 0x2005: /* mov.w Rm,@-Rn */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_subi_i32(addr, REG(B11_8), 2); tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUW | UNALIGN(ctx)); - tcg_gen_mov_i32(REG(B11_8), addr); - tcg_temp_free(addr); - } - return; - case 0x2006: /* mov.l Rm,@-Rn */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_subi_i32(addr, REG(B11_8), 4); + tcg_gen_mov_i32(REG(B11_8), addr); + } + return; + case 0x2006: /* mov.l Rm,@-Rn */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_subi_i32(addr, REG(B11_8), 4); tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUL | UNALIGN(ctx)); - tcg_gen_mov_i32(REG(B11_8), addr); - tcg_temp_free(addr); - } - return; - case 0x6004: /* mov.b @Rm+,Rn */ + tcg_gen_mov_i32(REG(B11_8), addr); + } + return; + case 0x6004: /* mov.b @Rm+,Rn */ tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_SB); - if ( B11_8 != B7_4 ) - tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 1); - return; - case 0x6005: /* mov.w @Rm+,Rn */ + if ( B11_8 != B7_4 ) + tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 1); + return; + case 0x6005: /* mov.w @Rm+,Rn */ tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESW | UNALIGN(ctx)); - if ( B11_8 != B7_4 ) - tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 2); - return; - case 0x6006: /* mov.l @Rm+,Rn */ + if ( B11_8 != B7_4 ) + tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 2); + return; + case 0x6006: /* mov.l @Rm+,Rn */ tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESL | UNALIGN(ctx)); - if ( B11_8 != B7_4 ) - tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4); - return; - case 0x0004: /* mov.b Rm,@(R0,Rn) */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_add_i32(addr, REG(B11_8), REG(0)); + if ( B11_8 != B7_4 ) + tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4); + return; + case 0x0004: /* mov.b Rm,@(R0,Rn) */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_add_i32(addr, REG(B11_8), REG(0)); tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_UB); - tcg_temp_free(addr); - } - return; - case 0x0005: /* mov.w Rm,@(R0,Rn) */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_add_i32(addr, REG(B11_8), REG(0)); + } + return; + case 0x0005: /* mov.w Rm,@(R0,Rn) */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_add_i32(addr, REG(B11_8), REG(0)); tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUW | UNALIGN(ctx)); - tcg_temp_free(addr); - } - return; - case 0x0006: /* mov.l Rm,@(R0,Rn) */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_add_i32(addr, REG(B11_8), REG(0)); + } + return; + case 0x0006: /* mov.l Rm,@(R0,Rn) */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_add_i32(addr, REG(B11_8), REG(0)); tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUL | UNALIGN(ctx)); - tcg_temp_free(addr); - } - return; - case 0x000c: /* mov.b @(R0,Rm),Rn */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_add_i32(addr, REG(B7_4), REG(0)); + } + return; + case 0x000c: /* mov.b @(R0,Rm),Rn */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_add_i32(addr, REG(B7_4), REG(0)); tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_SB); - tcg_temp_free(addr); - } - return; - case 0x000d: /* mov.w @(R0,Rm),Rn */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_add_i32(addr, REG(B7_4), REG(0)); + } + return; + case 0x000d: /* mov.w @(R0,Rm),Rn */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_add_i32(addr, REG(B7_4), REG(0)); tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESW | UNALIGN(ctx)); - tcg_temp_free(addr); - } - return; - case 0x000e: /* mov.l @(R0,Rm),Rn */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_add_i32(addr, REG(B7_4), REG(0)); + } + return; + case 0x000e: /* mov.l @(R0,Rm),Rn */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_add_i32(addr, REG(B7_4), REG(0)); tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL | UNALIGN(ctx)); - tcg_temp_free(addr); - } - return; - case 0x6008: /* swap.b Rm,Rn */ - { + } + return; + case 0x6008: /* swap.b Rm,Rn */ + { TCGv low = tcg_temp_new(); tcg_gen_bswap16_i32(low, REG(B7_4), 0); tcg_gen_deposit_i32(REG(B11_8), REG(B7_4), low, 0, 16); - tcg_temp_free(low); - } - return; - case 0x6009: /* swap.w Rm,Rn */ + } + return; + case 0x6009: /* swap.w Rm,Rn */ tcg_gen_rotli_i32(REG(B11_8), REG(B7_4), 16); - return; - case 0x200d: /* xtrct Rm,Rn */ - { - TCGv high, low; - high = tcg_temp_new(); - tcg_gen_shli_i32(high, REG(B7_4), 16); - low = tcg_temp_new(); - tcg_gen_shri_i32(low, REG(B11_8), 16); - tcg_gen_or_i32(REG(B11_8), high, low); - tcg_temp_free(low); - tcg_temp_free(high); - } - return; - case 0x300c: /* add Rm,Rn */ - tcg_gen_add_i32(REG(B11_8), REG(B11_8), REG(B7_4)); - return; - case 0x300e: /* addc Rm,Rn */ + return; + case 0x200d: /* xtrct Rm,Rn */ + { + TCGv high, low; + high = tcg_temp_new(); + tcg_gen_shli_i32(high, REG(B7_4), 16); + low = tcg_temp_new(); + tcg_gen_shri_i32(low, REG(B11_8), 16); + tcg_gen_or_i32(REG(B11_8), high, low); + } + return; + case 0x300c: /* add Rm,Rn */ + tcg_gen_add_i32(REG(B11_8), REG(B11_8), REG(B7_4)); + return; + case 0x300e: /* addc Rm,Rn */ { TCGv t0, t1; - t0 = tcg_const_tl(0); + t0 = tcg_constant_tl(0); t1 = tcg_temp_new(); tcg_gen_add2_i32(t1, cpu_sr_t, cpu_sr_t, t0, REG(B7_4), t0); tcg_gen_add2_i32(REG(B11_8), cpu_sr_t, REG(B11_8), t0, t1, cpu_sr_t); - tcg_temp_free(t0); - tcg_temp_free(t1); } - return; - case 0x300f: /* addv Rm,Rn */ + return; + case 0x300f: /* addv Rm,Rn */ { TCGv t0, t1, t2; t0 = tcg_temp_new(); @@ -727,55 +713,50 @@ static void _decode_opc(DisasContext * ctx) t2 = tcg_temp_new(); tcg_gen_xor_i32(t2, REG(B7_4), REG(B11_8)); tcg_gen_andc_i32(cpu_sr_t, t1, t2); - tcg_temp_free(t2); tcg_gen_shri_i32(cpu_sr_t, cpu_sr_t, 31); - tcg_temp_free(t1); tcg_gen_mov_i32(REG(B7_4), t0); - tcg_temp_free(t0); } - return; - case 0x2009: /* and Rm,Rn */ - tcg_gen_and_i32(REG(B11_8), REG(B11_8), REG(B7_4)); - return; - case 0x3000: /* cmp/eq Rm,Rn */ + return; + case 0x2009: /* and Rm,Rn */ + tcg_gen_and_i32(REG(B11_8), REG(B11_8), REG(B7_4)); + return; + case 0x3000: /* cmp/eq Rm,Rn */ tcg_gen_setcond_i32(TCG_COND_EQ, cpu_sr_t, REG(B11_8), REG(B7_4)); - return; - case 0x3003: /* cmp/ge Rm,Rn */ + return; + case 0x3003: /* cmp/ge Rm,Rn */ tcg_gen_setcond_i32(TCG_COND_GE, cpu_sr_t, REG(B11_8), REG(B7_4)); - return; - case 0x3007: /* cmp/gt Rm,Rn */ + return; + case 0x3007: /* cmp/gt Rm,Rn */ tcg_gen_setcond_i32(TCG_COND_GT, cpu_sr_t, REG(B11_8), REG(B7_4)); - return; - case 0x3006: /* cmp/hi Rm,Rn */ + return; + case 0x3006: /* cmp/hi Rm,Rn */ tcg_gen_setcond_i32(TCG_COND_GTU, cpu_sr_t, REG(B11_8), REG(B7_4)); - return; - case 0x3002: /* cmp/hs Rm,Rn */ + return; + case 0x3002: /* cmp/hs Rm,Rn */ tcg_gen_setcond_i32(TCG_COND_GEU, cpu_sr_t, REG(B11_8), REG(B7_4)); - return; - case 0x200c: /* cmp/str Rm,Rn */ - { - TCGv cmp1 = tcg_temp_new(); - TCGv cmp2 = tcg_temp_new(); + return; + case 0x200c: /* cmp/str Rm,Rn */ + { + TCGv cmp1 = tcg_temp_new(); + TCGv cmp2 = tcg_temp_new(); tcg_gen_xor_i32(cmp2, REG(B7_4), REG(B11_8)); tcg_gen_subi_i32(cmp1, cmp2, 0x01010101); tcg_gen_andc_i32(cmp1, cmp1, cmp2); tcg_gen_andi_i32(cmp1, cmp1, 0x80808080); tcg_gen_setcondi_i32(TCG_COND_NE, cpu_sr_t, cmp1, 0); - tcg_temp_free(cmp2); - tcg_temp_free(cmp1); - } - return; - case 0x2007: /* div0s Rm,Rn */ + } + return; + case 0x2007: /* div0s Rm,Rn */ tcg_gen_shri_i32(cpu_sr_q, REG(B11_8), 31); /* SR_Q */ tcg_gen_shri_i32(cpu_sr_m, REG(B7_4), 31); /* SR_M */ tcg_gen_xor_i32(cpu_sr_t, cpu_sr_q, cpu_sr_m); /* SR_T */ - return; - case 0x3004: /* div1 Rm,Rn */ + return; + case 0x3004: /* div1 Rm,Rn */ { TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); - TCGv zero = tcg_const_i32(0); + TCGv zero = tcg_constant_i32(0); /* shift left arg1, saving the bit being pushed out and inserting T on the right */ @@ -798,108 +779,98 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_xor_i32(t1, t1, t0); tcg_gen_xori_i32(cpu_sr_t, t1, 1); tcg_gen_xor_i32(cpu_sr_q, cpu_sr_m, t1); - - tcg_temp_free(zero); - tcg_temp_free(t2); - tcg_temp_free(t1); - tcg_temp_free(t0); } - return; - case 0x300d: /* dmuls.l Rm,Rn */ + return; + case 0x300d: /* dmuls.l Rm,Rn */ tcg_gen_muls2_i32(cpu_macl, cpu_mach, REG(B7_4), REG(B11_8)); - return; - case 0x3005: /* dmulu.l Rm,Rn */ + return; + case 0x3005: /* dmulu.l Rm,Rn */ tcg_gen_mulu2_i32(cpu_macl, cpu_mach, REG(B7_4), REG(B11_8)); - return; - case 0x600e: /* exts.b Rm,Rn */ - tcg_gen_ext8s_i32(REG(B11_8), REG(B7_4)); - return; - case 0x600f: /* exts.w Rm,Rn */ - tcg_gen_ext16s_i32(REG(B11_8), REG(B7_4)); - return; - case 0x600c: /* extu.b Rm,Rn */ - tcg_gen_ext8u_i32(REG(B11_8), REG(B7_4)); - return; - case 0x600d: /* extu.w Rm,Rn */ - tcg_gen_ext16u_i32(REG(B11_8), REG(B7_4)); - return; - case 0x000f: /* mac.l @Rm+,@Rn+ */ - { - TCGv arg0, arg1; - arg0 = tcg_temp_new(); - tcg_gen_qemu_ld_i32(arg0, REG(B7_4), ctx->memidx, MO_TESL); - arg1 = tcg_temp_new(); - tcg_gen_qemu_ld_i32(arg1, REG(B11_8), ctx->memidx, MO_TESL); - gen_helper_macl(cpu_env, arg0, arg1); - tcg_temp_free(arg1); - tcg_temp_free(arg0); - tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4); - tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); - } - return; - case 0x400f: /* mac.w @Rm+,@Rn+ */ - { - TCGv arg0, arg1; - arg0 = tcg_temp_new(); - tcg_gen_qemu_ld_i32(arg0, REG(B7_4), ctx->memidx, MO_TESL); - arg1 = tcg_temp_new(); - tcg_gen_qemu_ld_i32(arg1, REG(B11_8), ctx->memidx, MO_TESL); - gen_helper_macw(cpu_env, arg0, arg1); - tcg_temp_free(arg1); - tcg_temp_free(arg0); - tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 2); - tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 2); - } - return; - case 0x0007: /* mul.l Rm,Rn */ - tcg_gen_mul_i32(cpu_macl, REG(B7_4), REG(B11_8)); - return; - case 0x200f: /* muls.w Rm,Rn */ - { - TCGv arg0, arg1; - arg0 = tcg_temp_new(); - tcg_gen_ext16s_i32(arg0, REG(B7_4)); - arg1 = tcg_temp_new(); - tcg_gen_ext16s_i32(arg1, REG(B11_8)); - tcg_gen_mul_i32(cpu_macl, arg0, arg1); - tcg_temp_free(arg1); - tcg_temp_free(arg0); - } - return; - case 0x200e: /* mulu.w Rm,Rn */ - { - TCGv arg0, arg1; - arg0 = tcg_temp_new(); - tcg_gen_ext16u_i32(arg0, REG(B7_4)); - arg1 = tcg_temp_new(); - tcg_gen_ext16u_i32(arg1, REG(B11_8)); - tcg_gen_mul_i32(cpu_macl, arg0, arg1); - tcg_temp_free(arg1); - tcg_temp_free(arg0); - } - return; - case 0x600b: /* neg Rm,Rn */ - tcg_gen_neg_i32(REG(B11_8), REG(B7_4)); - return; - case 0x600a: /* negc Rm,Rn */ + return; + case 0x600e: /* exts.b Rm,Rn */ + tcg_gen_ext8s_i32(REG(B11_8), REG(B7_4)); + return; + case 0x600f: /* exts.w Rm,Rn */ + tcg_gen_ext16s_i32(REG(B11_8), REG(B7_4)); + return; + case 0x600c: /* extu.b Rm,Rn */ + tcg_gen_ext8u_i32(REG(B11_8), REG(B7_4)); + return; + case 0x600d: /* extu.w Rm,Rn */ + tcg_gen_ext16u_i32(REG(B11_8), REG(B7_4)); + return; + case 0x000f: /* mac.l @Rm+,@Rn+ */ { - TCGv t0 = tcg_const_i32(0); + TCGv arg0, arg1; + arg0 = tcg_temp_new(); + tcg_gen_qemu_ld_i32(arg0, REG(B7_4), ctx->memidx, + MO_TESL | MO_ALIGN); + arg1 = tcg_temp_new(); + tcg_gen_qemu_ld_i32(arg1, REG(B11_8), ctx->memidx, + MO_TESL | MO_ALIGN); + gen_helper_macl(tcg_env, arg0, arg1); + tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4); + tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); + } + return; + case 0x400f: /* mac.w @Rm+,@Rn+ */ + { + TCGv arg0, arg1; + arg0 = tcg_temp_new(); + tcg_gen_qemu_ld_i32(arg0, REG(B7_4), ctx->memidx, + MO_TESW | MO_ALIGN); + arg1 = tcg_temp_new(); + tcg_gen_qemu_ld_i32(arg1, REG(B11_8), ctx->memidx, + MO_TESW | MO_ALIGN); + gen_helper_macw(tcg_env, arg0, arg1); + tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 2); + tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 2); + } + return; + case 0x0007: /* mul.l Rm,Rn */ + tcg_gen_mul_i32(cpu_macl, REG(B7_4), REG(B11_8)); + return; + case 0x200f: /* muls.w Rm,Rn */ + { + TCGv arg0, arg1; + arg0 = tcg_temp_new(); + tcg_gen_ext16s_i32(arg0, REG(B7_4)); + arg1 = tcg_temp_new(); + tcg_gen_ext16s_i32(arg1, REG(B11_8)); + tcg_gen_mul_i32(cpu_macl, arg0, arg1); + } + return; + case 0x200e: /* mulu.w Rm,Rn */ + { + TCGv arg0, arg1; + arg0 = tcg_temp_new(); + tcg_gen_ext16u_i32(arg0, REG(B7_4)); + arg1 = tcg_temp_new(); + tcg_gen_ext16u_i32(arg1, REG(B11_8)); + tcg_gen_mul_i32(cpu_macl, arg0, arg1); + } + return; + case 0x600b: /* neg Rm,Rn */ + tcg_gen_neg_i32(REG(B11_8), REG(B7_4)); + return; + case 0x600a: /* negc Rm,Rn */ + { + TCGv t0 = tcg_constant_i32(0); tcg_gen_add2_i32(REG(B11_8), cpu_sr_t, REG(B7_4), t0, cpu_sr_t, t0); tcg_gen_sub2_i32(REG(B11_8), cpu_sr_t, t0, t0, REG(B11_8), cpu_sr_t); tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1); - tcg_temp_free(t0); } - return; - case 0x6007: /* not Rm,Rn */ - tcg_gen_not_i32(REG(B11_8), REG(B7_4)); - return; - case 0x200b: /* or Rm,Rn */ - tcg_gen_or_i32(REG(B11_8), REG(B11_8), REG(B7_4)); - return; - case 0x400c: /* shad Rm,Rn */ - { + return; + case 0x6007: /* not Rm,Rn */ + tcg_gen_not_i32(REG(B11_8), REG(B7_4)); + return; + case 0x200b: /* or Rm,Rn */ + tcg_gen_or_i32(REG(B11_8), REG(B11_8), REG(B7_4)); + return; + case 0x400c: /* shad Rm,Rn */ + { TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); @@ -918,14 +889,10 @@ static void _decode_opc(DisasContext * ctx) /* select between the two cases */ tcg_gen_movi_i32(t0, 0); tcg_gen_movcond_i32(TCG_COND_GE, REG(B11_8), REG(B7_4), t0, t1, t2); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - } - return; - case 0x400d: /* shld Rm,Rn */ - { + } + return; + case 0x400d: /* shld Rm,Rn */ + { TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); @@ -944,29 +911,23 @@ static void _decode_opc(DisasContext * ctx) /* select between the two cases */ tcg_gen_movi_i32(t0, 0); tcg_gen_movcond_i32(TCG_COND_GE, REG(B11_8), REG(B7_4), t0, t1, t2); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - } - return; - case 0x3008: /* sub Rm,Rn */ - tcg_gen_sub_i32(REG(B11_8), REG(B11_8), REG(B7_4)); - return; - case 0x300a: /* subc Rm,Rn */ + } + return; + case 0x3008: /* sub Rm,Rn */ + tcg_gen_sub_i32(REG(B11_8), REG(B11_8), REG(B7_4)); + return; + case 0x300a: /* subc Rm,Rn */ { TCGv t0, t1; - t0 = tcg_const_tl(0); + t0 = tcg_constant_tl(0); t1 = tcg_temp_new(); tcg_gen_add2_i32(t1, cpu_sr_t, cpu_sr_t, t0, REG(B7_4), t0); tcg_gen_sub2_i32(REG(B11_8), cpu_sr_t, REG(B11_8), t0, t1, cpu_sr_t); tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1); - tcg_temp_free(t0); - tcg_temp_free(t1); } - return; - case 0x300b: /* subv Rm,Rn */ + return; + case 0x300b: /* subv Rm,Rn */ { TCGv t0, t1, t2; t0 = tcg_temp_new(); @@ -976,589 +937,566 @@ static void _decode_opc(DisasContext * ctx) t2 = tcg_temp_new(); tcg_gen_xor_i32(t2, REG(B11_8), REG(B7_4)); tcg_gen_and_i32(t1, t1, t2); - tcg_temp_free(t2); tcg_gen_shri_i32(cpu_sr_t, t1, 31); - tcg_temp_free(t1); tcg_gen_mov_i32(REG(B11_8), t0); - tcg_temp_free(t0); } - return; - case 0x2008: /* tst Rm,Rn */ - { - TCGv val = tcg_temp_new(); - tcg_gen_and_i32(val, REG(B7_4), REG(B11_8)); + return; + case 0x2008: /* tst Rm,Rn */ + { + TCGv val = tcg_temp_new(); + tcg_gen_and_i32(val, REG(B7_4), REG(B11_8)); tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); - tcg_temp_free(val); - } - return; - case 0x200a: /* xor Rm,Rn */ - tcg_gen_xor_i32(REG(B11_8), REG(B11_8), REG(B7_4)); - return; + } + return; + case 0x200a: /* xor Rm,Rn */ + tcg_gen_xor_i32(REG(B11_8), REG(B11_8), REG(B7_4)); + return; case 0xf00c: /* fmov {F,D,X}Rm,{F,D,X}Rn - FPSCR: Nothing */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED if (ctx->tbflags & FPSCR_SZ) { int xsrc = XHACK(B7_4); int xdst = XHACK(B11_8); tcg_gen_mov_i32(FREG(xdst), FREG(xsrc)); tcg_gen_mov_i32(FREG(xdst + 1), FREG(xsrc + 1)); - } else { + } else { tcg_gen_mov_i32(FREG(B11_8), FREG(B7_4)); - } - return; + } + return; case 0xf00a: /* fmov {F,D,X}Rm,@Rn - FPSCR: Nothing */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED if (ctx->tbflags & FPSCR_SZ) { TCGv_i64 fp = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp, XHACK(B7_4)); - tcg_gen_qemu_st_i64(fp, REG(B11_8), ctx->memidx, MO_TEUQ); - tcg_temp_free_i64(fp); - } else { - tcg_gen_qemu_st_i32(FREG(B7_4), REG(B11_8), ctx->memidx, MO_TEUL); - } - return; + tcg_gen_qemu_st_i64(fp, REG(B11_8), ctx->memidx, + MO_TEUQ | MO_ALIGN); + } else { + tcg_gen_qemu_st_i32(FREG(B7_4), REG(B11_8), ctx->memidx, + MO_TEUL | MO_ALIGN); + } + return; case 0xf008: /* fmov @Rm,{F,D,X}Rn - FPSCR: Nothing */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED if (ctx->tbflags & FPSCR_SZ) { TCGv_i64 fp = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(fp, REG(B7_4), ctx->memidx, MO_TEUQ); + tcg_gen_qemu_ld_i64(fp, REG(B7_4), ctx->memidx, + MO_TEUQ | MO_ALIGN); gen_store_fpr64(ctx, fp, XHACK(B11_8)); - tcg_temp_free_i64(fp); - } else { - tcg_gen_qemu_ld_i32(FREG(B11_8), REG(B7_4), ctx->memidx, MO_TEUL); - } - return; + } else { + tcg_gen_qemu_ld_i32(FREG(B11_8), REG(B7_4), ctx->memidx, + MO_TEUL | MO_ALIGN); + } + return; case 0xf009: /* fmov @Rm+,{F,D,X}Rn - FPSCR: Nothing */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED if (ctx->tbflags & FPSCR_SZ) { TCGv_i64 fp = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(fp, REG(B7_4), ctx->memidx, MO_TEUQ); + tcg_gen_qemu_ld_i64(fp, REG(B7_4), ctx->memidx, + MO_TEUQ | MO_ALIGN); gen_store_fpr64(ctx, fp, XHACK(B11_8)); - tcg_temp_free_i64(fp); tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 8); - } else { - tcg_gen_qemu_ld_i32(FREG(B11_8), REG(B7_4), ctx->memidx, MO_TEUL); - tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4); - } - return; + } else { + tcg_gen_qemu_ld_i32(FREG(B11_8), REG(B7_4), ctx->memidx, + MO_TEUL | MO_ALIGN); + tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4); + } + return; case 0xf00b: /* fmov {F,D,X}Rm,@-Rn - FPSCR: Nothing */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED { TCGv addr = tcg_temp_new_i32(); if (ctx->tbflags & FPSCR_SZ) { TCGv_i64 fp = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp, XHACK(B7_4)); tcg_gen_subi_i32(addr, REG(B11_8), 8); - tcg_gen_qemu_st_i64(fp, addr, ctx->memidx, MO_TEUQ); - tcg_temp_free_i64(fp); + tcg_gen_qemu_st_i64(fp, addr, ctx->memidx, + MO_TEUQ | MO_ALIGN); } else { tcg_gen_subi_i32(addr, REG(B11_8), 4); - tcg_gen_qemu_st_i32(FREG(B7_4), addr, ctx->memidx, MO_TEUL); + tcg_gen_qemu_st_i32(FREG(B7_4), addr, ctx->memidx, + MO_TEUL | MO_ALIGN); } tcg_gen_mov_i32(REG(B11_8), addr); - tcg_temp_free(addr); } - return; + return; case 0xf006: /* fmov @(R0,Rm),{F,D,X}Rm - FPSCR: Nothing */ - CHECK_FPU_ENABLED - { - TCGv addr = tcg_temp_new_i32(); - tcg_gen_add_i32(addr, REG(B7_4), REG(0)); + CHECK_FPU_ENABLED + { + TCGv addr = tcg_temp_new_i32(); + tcg_gen_add_i32(addr, REG(B7_4), REG(0)); if (ctx->tbflags & FPSCR_SZ) { TCGv_i64 fp = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(fp, addr, ctx->memidx, MO_TEUQ); + tcg_gen_qemu_ld_i64(fp, addr, ctx->memidx, + MO_TEUQ | MO_ALIGN); gen_store_fpr64(ctx, fp, XHACK(B11_8)); - tcg_temp_free_i64(fp); - } else { - tcg_gen_qemu_ld_i32(FREG(B11_8), addr, ctx->memidx, MO_TEUL); - } - tcg_temp_free(addr); - } - return; + } else { + tcg_gen_qemu_ld_i32(FREG(B11_8), addr, ctx->memidx, + MO_TEUL | MO_ALIGN); + } + } + return; case 0xf007: /* fmov {F,D,X}Rn,@(R0,Rn) - FPSCR: Nothing */ - CHECK_FPU_ENABLED - { - TCGv addr = tcg_temp_new(); - tcg_gen_add_i32(addr, REG(B11_8), REG(0)); + CHECK_FPU_ENABLED + { + TCGv addr = tcg_temp_new(); + tcg_gen_add_i32(addr, REG(B11_8), REG(0)); if (ctx->tbflags & FPSCR_SZ) { TCGv_i64 fp = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp, XHACK(B7_4)); - tcg_gen_qemu_st_i64(fp, addr, ctx->memidx, MO_TEUQ); - tcg_temp_free_i64(fp); - } else { - tcg_gen_qemu_st_i32(FREG(B7_4), addr, ctx->memidx, MO_TEUL); - } - tcg_temp_free(addr); - } - return; + tcg_gen_qemu_st_i64(fp, addr, ctx->memidx, + MO_TEUQ | MO_ALIGN); + } else { + tcg_gen_qemu_st_i32(FREG(B7_4), addr, ctx->memidx, + MO_TEUL | MO_ALIGN); + } + } + return; case 0xf000: /* fadd Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */ case 0xf001: /* fsub Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */ case 0xf002: /* fmul Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */ case 0xf003: /* fdiv Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */ case 0xf004: /* fcmp/eq Rm,Rn - FPSCR: R[PR,Enable.V]/W[Cause,Flag] */ case 0xf005: /* fcmp/gt Rm,Rn - FPSCR: R[PR,Enable.V]/W[Cause,Flag] */ - { - CHECK_FPU_ENABLED + { + CHECK_FPU_ENABLED if (ctx->tbflags & FPSCR_PR) { TCGv_i64 fp0, fp1; if (ctx->opcode & 0x0110) { goto do_illegal; } - fp0 = tcg_temp_new_i64(); - fp1 = tcg_temp_new_i64(); + fp0 = tcg_temp_new_i64(); + fp1 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, B11_8); gen_load_fpr64(ctx, fp1, B7_4); switch (ctx->opcode & 0xf00f) { - case 0xf000: /* fadd Rm,Rn */ - gen_helper_fadd_DT(fp0, cpu_env, fp0, fp1); + case 0xf000: /* fadd Rm,Rn */ + gen_helper_fadd_DT(fp0, tcg_env, fp0, fp1); break; - case 0xf001: /* fsub Rm,Rn */ - gen_helper_fsub_DT(fp0, cpu_env, fp0, fp1); + case 0xf001: /* fsub Rm,Rn */ + gen_helper_fsub_DT(fp0, tcg_env, fp0, fp1); break; - case 0xf002: /* fmul Rm,Rn */ - gen_helper_fmul_DT(fp0, cpu_env, fp0, fp1); + case 0xf002: /* fmul Rm,Rn */ + gen_helper_fmul_DT(fp0, tcg_env, fp0, fp1); break; - case 0xf003: /* fdiv Rm,Rn */ - gen_helper_fdiv_DT(fp0, cpu_env, fp0, fp1); + case 0xf003: /* fdiv Rm,Rn */ + gen_helper_fdiv_DT(fp0, tcg_env, fp0, fp1); break; - case 0xf004: /* fcmp/eq Rm,Rn */ - gen_helper_fcmp_eq_DT(cpu_sr_t, cpu_env, fp0, fp1); + case 0xf004: /* fcmp/eq Rm,Rn */ + gen_helper_fcmp_eq_DT(cpu_sr_t, tcg_env, fp0, fp1); return; - case 0xf005: /* fcmp/gt Rm,Rn */ - gen_helper_fcmp_gt_DT(cpu_sr_t, cpu_env, fp0, fp1); + case 0xf005: /* fcmp/gt Rm,Rn */ + gen_helper_fcmp_gt_DT(cpu_sr_t, tcg_env, fp0, fp1); return; } gen_store_fpr64(ctx, fp0, B11_8); - tcg_temp_free_i64(fp0); - tcg_temp_free_i64(fp1); - } else { + } else { switch (ctx->opcode & 0xf00f) { - case 0xf000: /* fadd Rm,Rn */ - gen_helper_fadd_FT(FREG(B11_8), cpu_env, + case 0xf000: /* fadd Rm,Rn */ + gen_helper_fadd_FT(FREG(B11_8), tcg_env, FREG(B11_8), FREG(B7_4)); break; - case 0xf001: /* fsub Rm,Rn */ - gen_helper_fsub_FT(FREG(B11_8), cpu_env, + case 0xf001: /* fsub Rm,Rn */ + gen_helper_fsub_FT(FREG(B11_8), tcg_env, FREG(B11_8), FREG(B7_4)); break; - case 0xf002: /* fmul Rm,Rn */ - gen_helper_fmul_FT(FREG(B11_8), cpu_env, + case 0xf002: /* fmul Rm,Rn */ + gen_helper_fmul_FT(FREG(B11_8), tcg_env, FREG(B11_8), FREG(B7_4)); break; - case 0xf003: /* fdiv Rm,Rn */ - gen_helper_fdiv_FT(FREG(B11_8), cpu_env, + case 0xf003: /* fdiv Rm,Rn */ + gen_helper_fdiv_FT(FREG(B11_8), tcg_env, FREG(B11_8), FREG(B7_4)); break; - case 0xf004: /* fcmp/eq Rm,Rn */ - gen_helper_fcmp_eq_FT(cpu_sr_t, cpu_env, + case 0xf004: /* fcmp/eq Rm,Rn */ + gen_helper_fcmp_eq_FT(cpu_sr_t, tcg_env, FREG(B11_8), FREG(B7_4)); return; - case 0xf005: /* fcmp/gt Rm,Rn */ - gen_helper_fcmp_gt_FT(cpu_sr_t, cpu_env, + case 0xf005: /* fcmp/gt Rm,Rn */ + gen_helper_fcmp_gt_FT(cpu_sr_t, tcg_env, FREG(B11_8), FREG(B7_4)); return; } - } - } - return; + } + } + return; case 0xf00e: /* fmac FR0,RM,Rn */ CHECK_FPU_ENABLED CHECK_FPSCR_PR_0 - gen_helper_fmac_FT(FREG(B11_8), cpu_env, + gen_helper_fmac_FT(FREG(B11_8), tcg_env, FREG(0), FREG(B7_4), FREG(B11_8)); return; } switch (ctx->opcode & 0xff00) { - case 0xc900: /* and #imm,R0 */ - tcg_gen_andi_i32(REG(0), REG(0), B7_0); - return; - case 0xcd00: /* and.b #imm,@(R0,GBR) */ - { - TCGv addr, val; - addr = tcg_temp_new(); - tcg_gen_add_i32(addr, REG(0), cpu_gbr); - val = tcg_temp_new(); + case 0xc900: /* and #imm,R0 */ + tcg_gen_andi_i32(REG(0), REG(0), B7_0); + return; + case 0xcd00: /* and.b #imm,@(R0,GBR) */ + { + TCGv addr, val; + addr = tcg_temp_new(); + tcg_gen_add_i32(addr, REG(0), cpu_gbr); + val = tcg_temp_new(); tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB); - tcg_gen_andi_i32(val, val, B7_0); + tcg_gen_andi_i32(val, val, B7_0); tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB); - tcg_temp_free(val); - tcg_temp_free(addr); - } - return; - case 0x8b00: /* bf label */ - CHECK_NOT_DELAY_SLOT + } + return; + case 0x8b00: /* bf label */ + CHECK_NOT_DELAY_SLOT gen_conditional_jump(ctx, ctx->base.pc_next + 4 + B7_0s * 2, false); - return; - case 0x8f00: /* bf/s label */ - CHECK_NOT_DELAY_SLOT + return; + case 0x8f00: /* bf/s label */ + CHECK_NOT_DELAY_SLOT tcg_gen_xori_i32(cpu_delayed_cond, cpu_sr_t, 1); ctx->delayed_pc = ctx->base.pc_next + 4 + B7_0s * 2; - ctx->envflags |= DELAY_SLOT_CONDITIONAL; - return; - case 0x8900: /* bt label */ - CHECK_NOT_DELAY_SLOT + ctx->envflags |= TB_FLAG_DELAY_SLOT_COND; + return; + case 0x8900: /* bt label */ + CHECK_NOT_DELAY_SLOT gen_conditional_jump(ctx, ctx->base.pc_next + 4 + B7_0s * 2, true); - return; - case 0x8d00: /* bt/s label */ - CHECK_NOT_DELAY_SLOT + return; + case 0x8d00: /* bt/s label */ + CHECK_NOT_DELAY_SLOT tcg_gen_mov_i32(cpu_delayed_cond, cpu_sr_t); ctx->delayed_pc = ctx->base.pc_next + 4 + B7_0s * 2; - ctx->envflags |= DELAY_SLOT_CONDITIONAL; - return; - case 0x8800: /* cmp/eq #imm,R0 */ + ctx->envflags |= TB_FLAG_DELAY_SLOT_COND; + return; + case 0x8800: /* cmp/eq #imm,R0 */ tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, REG(0), B7_0s); - return; - case 0xc400: /* mov.b @(disp,GBR),R0 */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_addi_i32(addr, cpu_gbr, B7_0); + return; + case 0xc400: /* mov.b @(disp,GBR),R0 */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_addi_i32(addr, cpu_gbr, B7_0); tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_SB); - tcg_temp_free(addr); - } - return; - case 0xc500: /* mov.w @(disp,GBR),R0 */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 2); - tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_TESW); - tcg_temp_free(addr); - } - return; - case 0xc600: /* mov.l @(disp,GBR),R0 */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 4); - tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_TESL); - tcg_temp_free(addr); - } - return; - case 0xc000: /* mov.b R0,@(disp,GBR) */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_addi_i32(addr, cpu_gbr, B7_0); + } + return; + case 0xc500: /* mov.w @(disp,GBR),R0 */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 2); + tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_TESW | MO_ALIGN); + } + return; + case 0xc600: /* mov.l @(disp,GBR),R0 */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 4); + tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_TESL | MO_ALIGN); + } + return; + case 0xc000: /* mov.b R0,@(disp,GBR) */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_addi_i32(addr, cpu_gbr, B7_0); tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_UB); - tcg_temp_free(addr); - } - return; - case 0xc100: /* mov.w R0,@(disp,GBR) */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 2); - tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_TEUW); - tcg_temp_free(addr); - } - return; - case 0xc200: /* mov.l R0,@(disp,GBR) */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 4); - tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_TEUL); - tcg_temp_free(addr); - } - return; - case 0x8000: /* mov.b R0,@(disp,Rn) */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_addi_i32(addr, REG(B7_4), B3_0); + } + return; + case 0xc100: /* mov.w R0,@(disp,GBR) */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 2); + tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_TEUW | MO_ALIGN); + } + return; + case 0xc200: /* mov.l R0,@(disp,GBR) */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 4); + tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_TEUL | MO_ALIGN); + } + return; + case 0x8000: /* mov.b R0,@(disp,Rn) */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_addi_i32(addr, REG(B7_4), B3_0); tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_UB); - tcg_temp_free(addr); - } - return; - case 0x8100: /* mov.w R0,@(disp,Rn) */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 2); + } + return; + case 0x8100: /* mov.w R0,@(disp,Rn) */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 2); tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_TEUW | UNALIGN(ctx)); - tcg_temp_free(addr); - } - return; - case 0x8400: /* mov.b @(disp,Rn),R0 */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_addi_i32(addr, REG(B7_4), B3_0); + } + return; + case 0x8400: /* mov.b @(disp,Rn),R0 */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_addi_i32(addr, REG(B7_4), B3_0); tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_SB); - tcg_temp_free(addr); - } - return; - case 0x8500: /* mov.w @(disp,Rn),R0 */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 2); + } + return; + case 0x8500: /* mov.w @(disp,Rn),R0 */ + { + TCGv addr = tcg_temp_new(); + tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 2); tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_TESW | UNALIGN(ctx)); - tcg_temp_free(addr); - } - return; - case 0xc700: /* mova @(disp,PC),R0 */ + } + return; + case 0xc700: /* mova @(disp,PC),R0 */ + CHECK_NOT_DELAY_SLOT tcg_gen_movi_i32(REG(0), ((ctx->base.pc_next & 0xfffffffc) + 4 + B7_0 * 4) & ~3); - return; - case 0xcb00: /* or #imm,R0 */ - tcg_gen_ori_i32(REG(0), REG(0), B7_0); - return; - case 0xcf00: /* or.b #imm,@(R0,GBR) */ - { - TCGv addr, val; - addr = tcg_temp_new(); - tcg_gen_add_i32(addr, REG(0), cpu_gbr); - val = tcg_temp_new(); + return; + case 0xcb00: /* or #imm,R0 */ + tcg_gen_ori_i32(REG(0), REG(0), B7_0); + return; + case 0xcf00: /* or.b #imm,@(R0,GBR) */ + { + TCGv addr, val; + addr = tcg_temp_new(); + tcg_gen_add_i32(addr, REG(0), cpu_gbr); + val = tcg_temp_new(); tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB); - tcg_gen_ori_i32(val, val, B7_0); + tcg_gen_ori_i32(val, val, B7_0); tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB); - tcg_temp_free(val); - tcg_temp_free(addr); - } - return; - case 0xc300: /* trapa #imm */ - { - TCGv imm; - CHECK_NOT_DELAY_SLOT + } + return; + case 0xc300: /* trapa #imm */ + { + TCGv imm; + CHECK_NOT_DELAY_SLOT gen_save_cpu_state(ctx, true); - imm = tcg_const_i32(B7_0); - gen_helper_trapa(cpu_env, imm); - tcg_temp_free(imm); + imm = tcg_constant_i32(B7_0); + gen_helper_trapa(tcg_env, imm); ctx->base.is_jmp = DISAS_NORETURN; - } - return; - case 0xc800: /* tst #imm,R0 */ - { - TCGv val = tcg_temp_new(); - tcg_gen_andi_i32(val, REG(0), B7_0); + } + return; + case 0xc800: /* tst #imm,R0 */ + { + TCGv val = tcg_temp_new(); + tcg_gen_andi_i32(val, REG(0), B7_0); tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); - tcg_temp_free(val); - } - return; - case 0xcc00: /* tst.b #imm,@(R0,GBR) */ - { - TCGv val = tcg_temp_new(); - tcg_gen_add_i32(val, REG(0), cpu_gbr); + } + return; + case 0xcc00: /* tst.b #imm,@(R0,GBR) */ + { + TCGv val = tcg_temp_new(); + tcg_gen_add_i32(val, REG(0), cpu_gbr); tcg_gen_qemu_ld_i32(val, val, ctx->memidx, MO_UB); - tcg_gen_andi_i32(val, val, B7_0); + tcg_gen_andi_i32(val, val, B7_0); tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); - tcg_temp_free(val); - } - return; - case 0xca00: /* xor #imm,R0 */ - tcg_gen_xori_i32(REG(0), REG(0), B7_0); - return; - case 0xce00: /* xor.b #imm,@(R0,GBR) */ - { - TCGv addr, val; - addr = tcg_temp_new(); - tcg_gen_add_i32(addr, REG(0), cpu_gbr); - val = tcg_temp_new(); + } + return; + case 0xca00: /* xor #imm,R0 */ + tcg_gen_xori_i32(REG(0), REG(0), B7_0); + return; + case 0xce00: /* xor.b #imm,@(R0,GBR) */ + { + TCGv addr, val; + addr = tcg_temp_new(); + tcg_gen_add_i32(addr, REG(0), cpu_gbr); + val = tcg_temp_new(); tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB); - tcg_gen_xori_i32(val, val, B7_0); + tcg_gen_xori_i32(val, val, B7_0); tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB); - tcg_temp_free(val); - tcg_temp_free(addr); - } - return; + } + return; } switch (ctx->opcode & 0xf08f) { - case 0x408e: /* ldc Rm,Rn_BANK */ - CHECK_PRIVILEGED - tcg_gen_mov_i32(ALTREG(B6_4), REG(B11_8)); - return; - case 0x4087: /* ldc.l @Rm+,Rn_BANK */ - CHECK_PRIVILEGED - tcg_gen_qemu_ld_i32(ALTREG(B6_4), REG(B11_8), ctx->memidx, MO_TESL); - tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); - return; - case 0x0082: /* stc Rm_BANK,Rn */ - CHECK_PRIVILEGED - tcg_gen_mov_i32(REG(B11_8), ALTREG(B6_4)); - return; - case 0x4083: /* stc.l Rm_BANK,@-Rn */ - CHECK_PRIVILEGED - { - TCGv addr = tcg_temp_new(); - tcg_gen_subi_i32(addr, REG(B11_8), 4); - tcg_gen_qemu_st_i32(ALTREG(B6_4), addr, ctx->memidx, MO_TEUL); - tcg_gen_mov_i32(REG(B11_8), addr); - tcg_temp_free(addr); - } - return; + case 0x408e: /* ldc Rm,Rn_BANK */ + CHECK_PRIVILEGED + tcg_gen_mov_i32(ALTREG(B6_4), REG(B11_8)); + return; + case 0x4087: /* ldc.l @Rm+,Rn_BANK */ + CHECK_PRIVILEGED + tcg_gen_qemu_ld_i32(ALTREG(B6_4), REG(B11_8), ctx->memidx, + MO_TESL | MO_ALIGN); + tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); + return; + case 0x0082: /* stc Rm_BANK,Rn */ + CHECK_PRIVILEGED + tcg_gen_mov_i32(REG(B11_8), ALTREG(B6_4)); + return; + case 0x4083: /* stc.l Rm_BANK,@-Rn */ + CHECK_PRIVILEGED + { + TCGv addr = tcg_temp_new(); + tcg_gen_subi_i32(addr, REG(B11_8), 4); + tcg_gen_qemu_st_i32(ALTREG(B6_4), addr, ctx->memidx, + MO_TEUL | MO_ALIGN); + tcg_gen_mov_i32(REG(B11_8), addr); + } + return; } switch (ctx->opcode & 0xf0ff) { - case 0x0023: /* braf Rn */ - CHECK_NOT_DELAY_SLOT + case 0x0023: /* braf Rn */ + CHECK_NOT_DELAY_SLOT tcg_gen_addi_i32(cpu_delayed_pc, REG(B11_8), ctx->base.pc_next + 4); - ctx->envflags |= DELAY_SLOT; - ctx->delayed_pc = (uint32_t) - 1; - return; - case 0x0003: /* bsrf Rn */ - CHECK_NOT_DELAY_SLOT + ctx->envflags |= TB_FLAG_DELAY_SLOT; + ctx->delayed_pc = (uint32_t) - 1; + return; + case 0x0003: /* bsrf Rn */ + CHECK_NOT_DELAY_SLOT tcg_gen_movi_i32(cpu_pr, ctx->base.pc_next + 4); - tcg_gen_add_i32(cpu_delayed_pc, REG(B11_8), cpu_pr); - ctx->envflags |= DELAY_SLOT; - ctx->delayed_pc = (uint32_t) - 1; - return; - case 0x4015: /* cmp/pl Rn */ + tcg_gen_add_i32(cpu_delayed_pc, REG(B11_8), cpu_pr); + ctx->envflags |= TB_FLAG_DELAY_SLOT; + ctx->delayed_pc = (uint32_t) - 1; + return; + case 0x4015: /* cmp/pl Rn */ tcg_gen_setcondi_i32(TCG_COND_GT, cpu_sr_t, REG(B11_8), 0); - return; - case 0x4011: /* cmp/pz Rn */ + return; + case 0x4011: /* cmp/pz Rn */ tcg_gen_setcondi_i32(TCG_COND_GE, cpu_sr_t, REG(B11_8), 0); - return; - case 0x4010: /* dt Rn */ - tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 1); + return; + case 0x4010: /* dt Rn */ + tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 1); tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, REG(B11_8), 0); - return; - case 0x402b: /* jmp @Rn */ - CHECK_NOT_DELAY_SLOT - tcg_gen_mov_i32(cpu_delayed_pc, REG(B11_8)); - ctx->envflags |= DELAY_SLOT; - ctx->delayed_pc = (uint32_t) - 1; - return; - case 0x400b: /* jsr @Rn */ - CHECK_NOT_DELAY_SLOT + return; + case 0x402b: /* jmp @Rn */ + CHECK_NOT_DELAY_SLOT + tcg_gen_mov_i32(cpu_delayed_pc, REG(B11_8)); + ctx->envflags |= TB_FLAG_DELAY_SLOT; + ctx->delayed_pc = (uint32_t) - 1; + return; + case 0x400b: /* jsr @Rn */ + CHECK_NOT_DELAY_SLOT tcg_gen_movi_i32(cpu_pr, ctx->base.pc_next + 4); - tcg_gen_mov_i32(cpu_delayed_pc, REG(B11_8)); - ctx->envflags |= DELAY_SLOT; - ctx->delayed_pc = (uint32_t) - 1; - return; - case 0x400e: /* ldc Rm,SR */ - CHECK_PRIVILEGED + tcg_gen_mov_i32(cpu_delayed_pc, REG(B11_8)); + ctx->envflags |= TB_FLAG_DELAY_SLOT; + ctx->delayed_pc = (uint32_t) - 1; + return; + case 0x400e: /* ldc Rm,SR */ + CHECK_PRIVILEGED { TCGv val = tcg_temp_new(); tcg_gen_andi_i32(val, REG(B11_8), 0x700083f3); gen_write_sr(val); - tcg_temp_free(val); ctx->base.is_jmp = DISAS_STOP; } - return; - case 0x4007: /* ldc.l @Rm+,SR */ - CHECK_PRIVILEGED - { - TCGv val = tcg_temp_new(); - tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, MO_TESL); - tcg_gen_andi_i32(val, val, 0x700083f3); - gen_write_sr(val); - tcg_temp_free(val); - tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); - ctx->base.is_jmp = DISAS_STOP; - } - return; - case 0x0002: /* stc SR,Rn */ - CHECK_PRIVILEGED - gen_read_sr(REG(B11_8)); - return; - case 0x4003: /* stc SR,@-Rn */ - CHECK_PRIVILEGED - { - TCGv addr = tcg_temp_new(); - TCGv val = tcg_temp_new(); - tcg_gen_subi_i32(addr, REG(B11_8), 4); - gen_read_sr(val); - tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_TEUL); - tcg_gen_mov_i32(REG(B11_8), addr); - tcg_temp_free(val); - tcg_temp_free(addr); - } - return; -#define LD(reg,ldnum,ldpnum,prechk) \ - case ldnum: \ - prechk \ - tcg_gen_mov_i32 (cpu_##reg, REG(B11_8)); \ - return; \ - case ldpnum: \ - prechk \ - tcg_gen_qemu_ld_i32(cpu_##reg, REG(B11_8), ctx->memidx, MO_TESL); \ - tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); \ - return; -#define ST(reg,stnum,stpnum,prechk) \ - case stnum: \ - prechk \ - tcg_gen_mov_i32 (REG(B11_8), cpu_##reg); \ - return; \ - case stpnum: \ - prechk \ - { \ - TCGv addr = tcg_temp_new(); \ - tcg_gen_subi_i32(addr, REG(B11_8), 4); \ - tcg_gen_qemu_st_i32(cpu_##reg, addr, ctx->memidx, MO_TEUL); \ - tcg_gen_mov_i32(REG(B11_8), addr); \ - tcg_temp_free(addr); \ - } \ - return; -#define LDST(reg,ldnum,ldpnum,stnum,stpnum,prechk) \ - LD(reg,ldnum,ldpnum,prechk) \ - ST(reg,stnum,stpnum,prechk) - LDST(gbr, 0x401e, 0x4017, 0x0012, 0x4013, {}) - LDST(vbr, 0x402e, 0x4027, 0x0022, 0x4023, CHECK_PRIVILEGED) - LDST(ssr, 0x403e, 0x4037, 0x0032, 0x4033, CHECK_PRIVILEGED) - LDST(spc, 0x404e, 0x4047, 0x0042, 0x4043, CHECK_PRIVILEGED) - ST(sgr, 0x003a, 0x4032, CHECK_PRIVILEGED) - LD(sgr, 0x403a, 0x4036, CHECK_PRIVILEGED CHECK_SH4A) - LDST(dbr, 0x40fa, 0x40f6, 0x00fa, 0x40f2, CHECK_PRIVILEGED) - LDST(mach, 0x400a, 0x4006, 0x000a, 0x4002, {}) - LDST(macl, 0x401a, 0x4016, 0x001a, 0x4012, {}) - LDST(pr, 0x402a, 0x4026, 0x002a, 0x4022, {}) - LDST(fpul, 0x405a, 0x4056, 0x005a, 0x4052, {CHECK_FPU_ENABLED}) - case 0x406a: /* lds Rm,FPSCR */ - CHECK_FPU_ENABLED - gen_helper_ld_fpscr(cpu_env, REG(B11_8)); - ctx->base.is_jmp = DISAS_STOP; - return; - case 0x4066: /* lds.l @Rm+,FPSCR */ - CHECK_FPU_ENABLED - { - TCGv addr = tcg_temp_new(); - tcg_gen_qemu_ld_i32(addr, REG(B11_8), ctx->memidx, MO_TESL); - tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); - gen_helper_ld_fpscr(cpu_env, addr); - tcg_temp_free(addr); - ctx->base.is_jmp = DISAS_STOP; - } - return; - case 0x006a: /* sts FPSCR,Rn */ - CHECK_FPU_ENABLED - tcg_gen_andi_i32(REG(B11_8), cpu_fpscr, 0x003fffff); - return; - case 0x4062: /* sts FPSCR,@-Rn */ - CHECK_FPU_ENABLED - { - TCGv addr, val; - val = tcg_temp_new(); - tcg_gen_andi_i32(val, cpu_fpscr, 0x003fffff); - addr = tcg_temp_new(); - tcg_gen_subi_i32(addr, REG(B11_8), 4); - tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_TEUL); - tcg_gen_mov_i32(REG(B11_8), addr); - tcg_temp_free(addr); - tcg_temp_free(val); - } - return; - case 0x00c3: /* movca.l R0,@Rm */ + return; + case 0x4007: /* ldc.l @Rm+,SR */ + CHECK_PRIVILEGED { TCGv val = tcg_temp_new(); - tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, MO_TEUL); - gen_helper_movcal(cpu_env, REG(B11_8), val); - tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL); - tcg_temp_free(val); + tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, + MO_TESL | MO_ALIGN); + tcg_gen_andi_i32(val, val, 0x700083f3); + gen_write_sr(val); + tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); + ctx->base.is_jmp = DISAS_STOP; + } + return; + case 0x0002: /* stc SR,Rn */ + CHECK_PRIVILEGED + gen_read_sr(REG(B11_8)); + return; + case 0x4003: /* stc SR,@-Rn */ + CHECK_PRIVILEGED + { + TCGv addr = tcg_temp_new(); + TCGv val = tcg_temp_new(); + tcg_gen_subi_i32(addr, REG(B11_8), 4); + gen_read_sr(val); + tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_TEUL | MO_ALIGN); + tcg_gen_mov_i32(REG(B11_8), addr); + } + return; +#define LD(reg,ldnum,ldpnum,prechk) \ + case ldnum: \ + prechk \ + tcg_gen_mov_i32 (cpu_##reg, REG(B11_8)); \ + return; \ + case ldpnum: \ + prechk \ + tcg_gen_qemu_ld_i32(cpu_##reg, REG(B11_8), ctx->memidx, \ + MO_TESL | MO_ALIGN); \ + tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); \ + return; +#define ST(reg,stnum,stpnum,prechk) \ + case stnum: \ + prechk \ + tcg_gen_mov_i32 (REG(B11_8), cpu_##reg); \ + return; \ + case stpnum: \ + prechk \ + { \ + TCGv addr = tcg_temp_new(); \ + tcg_gen_subi_i32(addr, REG(B11_8), 4); \ + tcg_gen_qemu_st_i32(cpu_##reg, addr, ctx->memidx, \ + MO_TEUL | MO_ALIGN); \ + tcg_gen_mov_i32(REG(B11_8), addr); \ + } \ + return; +#define LDST(reg,ldnum,ldpnum,stnum,stpnum,prechk) \ + LD(reg,ldnum,ldpnum,prechk) \ + ST(reg,stnum,stpnum,prechk) + LDST(gbr, 0x401e, 0x4017, 0x0012, 0x4013, {}) + LDST(vbr, 0x402e, 0x4027, 0x0022, 0x4023, CHECK_PRIVILEGED) + LDST(ssr, 0x403e, 0x4037, 0x0032, 0x4033, CHECK_PRIVILEGED) + LDST(spc, 0x404e, 0x4047, 0x0042, 0x4043, CHECK_PRIVILEGED) + ST(sgr, 0x003a, 0x4032, CHECK_PRIVILEGED) + LD(sgr, 0x403a, 0x4036, CHECK_PRIVILEGED CHECK_SH4A) + LDST(dbr, 0x40fa, 0x40f6, 0x00fa, 0x40f2, CHECK_PRIVILEGED) + LDST(mach, 0x400a, 0x4006, 0x000a, 0x4002, {}) + LDST(macl, 0x401a, 0x4016, 0x001a, 0x4012, {}) + LDST(pr, 0x402a, 0x4026, 0x002a, 0x4022, {}) + LDST(fpul, 0x405a, 0x4056, 0x005a, 0x4052, {CHECK_FPU_ENABLED}) + case 0x406a: /* lds Rm,FPSCR */ + CHECK_FPU_ENABLED + gen_helper_ld_fpscr(tcg_env, REG(B11_8)); + ctx->base.is_jmp = DISAS_STOP; + return; + case 0x4066: /* lds.l @Rm+,FPSCR */ + CHECK_FPU_ENABLED + { + TCGv addr = tcg_temp_new(); + tcg_gen_qemu_ld_i32(addr, REG(B11_8), ctx->memidx, + MO_TESL | MO_ALIGN); + tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); + gen_helper_ld_fpscr(tcg_env, addr); + ctx->base.is_jmp = DISAS_STOP; + } + return; + case 0x006a: /* sts FPSCR,Rn */ + CHECK_FPU_ENABLED + tcg_gen_andi_i32(REG(B11_8), cpu_fpscr, 0x003fffff); + return; + case 0x4062: /* sts FPSCR,@-Rn */ + CHECK_FPU_ENABLED + { + TCGv addr, val; + val = tcg_temp_new(); + tcg_gen_andi_i32(val, cpu_fpscr, 0x003fffff); + addr = tcg_temp_new(); + tcg_gen_subi_i32(addr, REG(B11_8), 4); + tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_TEUL | MO_ALIGN); + tcg_gen_mov_i32(REG(B11_8), addr); + } + return; + case 0x00c3: /* movca.l R0,@Rm */ + { + TCGv val = tcg_temp_new(); + tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, + MO_TEUL | MO_ALIGN); + gen_helper_movcal(tcg_env, REG(B11_8), val); + tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, + MO_TEUL | MO_ALIGN); } ctx->has_movcal = 1; - return; - case 0x40a9: /* movua.l @Rm,R0 */ + return; + case 0x40a9: /* movua.l @Rm,R0 */ CHECK_SH4A /* Load non-boundary-aligned data */ tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL | MO_UNALN); return; - case 0x40e9: /* movua.l @Rm+,R0 */ + case 0x40e9: /* movua.l @Rm+,R0 */ CHECK_SH4A /* Load non-boundary-aligned data */ tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL | MO_UNALN); tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); return; - case 0x0029: /* movt Rn */ + case 0x0029: /* movt Rn */ tcg_gen_mov_i32(REG(B11_8), cpu_sr_t); - return; + return; case 0x0073: /* MOVCO.L * LDST -> T @@ -1581,12 +1519,13 @@ static void _decode_opc(DisasContext * ctx) cpu_lock_addr, fail); tmp = tcg_temp_new(); tcg_gen_atomic_cmpxchg_i32(tmp, REG(B11_8), cpu_lock_value, - REG(0), ctx->memidx, MO_TEUL); + REG(0), ctx->memidx, + MO_TEUL | MO_ALIGN); tcg_gen_setcond_i32(TCG_COND_EQ, cpu_sr_t, tmp, cpu_lock_value); - tcg_temp_free(tmp); } else { tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_lock_addr, -1, fail); - tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL); + tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, + MO_TEUL | MO_ALIGN); tcg_gen_movi_i32(cpu_sr_t, 1); } tcg_gen_br(done); @@ -1611,211 +1550,199 @@ static void _decode_opc(DisasContext * ctx) if ((tb_cflags(ctx->base.tb) & CF_PARALLEL)) { TCGv tmp = tcg_temp_new(); tcg_gen_mov_i32(tmp, REG(B11_8)); - tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TESL); + tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, + MO_TESL | MO_ALIGN); tcg_gen_mov_i32(cpu_lock_value, REG(0)); tcg_gen_mov_i32(cpu_lock_addr, tmp); - tcg_temp_free(tmp); } else { - tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TESL); + tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, + MO_TESL | MO_ALIGN); tcg_gen_movi_i32(cpu_lock_addr, 0); } return; - case 0x0093: /* ocbi @Rn */ - { - gen_helper_ocbi(cpu_env, REG(B11_8)); - } - return; - case 0x00a3: /* ocbp @Rn */ - case 0x00b3: /* ocbwb @Rn */ + case 0x0093: /* ocbi @Rn */ + { + gen_helper_ocbi(tcg_env, REG(B11_8)); + } + return; + case 0x00a3: /* ocbp @Rn */ + case 0x00b3: /* ocbwb @Rn */ /* These instructions are supposed to do nothing in case of a cache miss. Given that we only partially emulate caches it is safe to simply ignore them. */ - return; - case 0x0083: /* pref @Rn */ - return; - case 0x00d3: /* prefi @Rn */ + return; + case 0x0083: /* pref @Rn */ + return; + case 0x00d3: /* prefi @Rn */ CHECK_SH4A return; - case 0x00e3: /* icbi @Rn */ + case 0x00e3: /* icbi @Rn */ CHECK_SH4A return; - case 0x00ab: /* synco */ + case 0x00ab: /* synco */ CHECK_SH4A tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); return; - case 0x4024: /* rotcl Rn */ - { - TCGv tmp = tcg_temp_new(); + case 0x4024: /* rotcl Rn */ + { + TCGv tmp = tcg_temp_new(); tcg_gen_mov_i32(tmp, cpu_sr_t); tcg_gen_shri_i32(cpu_sr_t, REG(B11_8), 31); - tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); + tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); tcg_gen_or_i32(REG(B11_8), REG(B11_8), tmp); - tcg_temp_free(tmp); - } - return; - case 0x4025: /* rotcr Rn */ - { - TCGv tmp = tcg_temp_new(); - tcg_gen_shli_i32(tmp, cpu_sr_t, 31); - tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); - tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1); - tcg_gen_or_i32(REG(B11_8), REG(B11_8), tmp); - tcg_temp_free(tmp); - } - return; - case 0x4004: /* rotl Rn */ - tcg_gen_rotli_i32(REG(B11_8), REG(B11_8), 1); - tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 0); - return; - case 0x4005: /* rotr Rn */ - tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 0); - tcg_gen_rotri_i32(REG(B11_8), REG(B11_8), 1); - return; - case 0x4000: /* shll Rn */ - case 0x4020: /* shal Rn */ - tcg_gen_shri_i32(cpu_sr_t, REG(B11_8), 31); - tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); - return; - case 0x4021: /* shar Rn */ - tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); - tcg_gen_sari_i32(REG(B11_8), REG(B11_8), 1); - return; - case 0x4001: /* shlr Rn */ - tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); - tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1); - return; - case 0x4008: /* shll2 Rn */ - tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 2); - return; - case 0x4018: /* shll8 Rn */ - tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 8); - return; - case 0x4028: /* shll16 Rn */ - tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 16); - return; - case 0x4009: /* shlr2 Rn */ - tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 2); - return; - case 0x4019: /* shlr8 Rn */ - tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 8); - return; - case 0x4029: /* shlr16 Rn */ - tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 16); - return; - case 0x401b: /* tas.b @Rn */ - { - TCGv val = tcg_const_i32(0x80); - tcg_gen_atomic_fetch_or_i32(val, REG(B11_8), val, - ctx->memidx, MO_UB); - tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); - tcg_temp_free(val); } return; + case 0x4025: /* rotcr Rn */ + { + TCGv tmp = tcg_temp_new(); + tcg_gen_shli_i32(tmp, cpu_sr_t, 31); + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); + tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1); + tcg_gen_or_i32(REG(B11_8), REG(B11_8), tmp); + } + return; + case 0x4004: /* rotl Rn */ + tcg_gen_rotli_i32(REG(B11_8), REG(B11_8), 1); + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 0); + return; + case 0x4005: /* rotr Rn */ + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 0); + tcg_gen_rotri_i32(REG(B11_8), REG(B11_8), 1); + return; + case 0x4000: /* shll Rn */ + case 0x4020: /* shal Rn */ + tcg_gen_shri_i32(cpu_sr_t, REG(B11_8), 31); + tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); + return; + case 0x4021: /* shar Rn */ + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); + tcg_gen_sari_i32(REG(B11_8), REG(B11_8), 1); + return; + case 0x4001: /* shlr Rn */ + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); + tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1); + return; + case 0x4008: /* shll2 Rn */ + tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 2); + return; + case 0x4018: /* shll8 Rn */ + tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 8); + return; + case 0x4028: /* shll16 Rn */ + tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 16); + return; + case 0x4009: /* shlr2 Rn */ + tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 2); + return; + case 0x4019: /* shlr8 Rn */ + tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 8); + return; + case 0x4029: /* shlr16 Rn */ + tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 16); + return; + case 0x401b: /* tas.b @Rn */ + tcg_gen_atomic_fetch_or_i32(cpu_sr_t, REG(B11_8), + tcg_constant_i32(0x80), ctx->memidx, MO_UB); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, cpu_sr_t, 0); + return; case 0xf00d: /* fsts FPUL,FRn - FPSCR: Nothing */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED tcg_gen_mov_i32(FREG(B11_8), cpu_fpul); - return; + return; case 0xf01d: /* flds FRm,FPUL - FPSCR: Nothing */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED tcg_gen_mov_i32(cpu_fpul, FREG(B11_8)); - return; + return; case 0xf02d: /* float FPUL,FRn/DRn - FPSCR: R[PR,Enable.I]/W[Cause,Flag] */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED if (ctx->tbflags & FPSCR_PR) { - TCGv_i64 fp; + TCGv_i64 fp; if (ctx->opcode & 0x0100) { goto do_illegal; } - fp = tcg_temp_new_i64(); - gen_helper_float_DT(fp, cpu_env, cpu_fpul); + fp = tcg_temp_new_i64(); + gen_helper_float_DT(fp, tcg_env, cpu_fpul); gen_store_fpr64(ctx, fp, B11_8); - tcg_temp_free_i64(fp); - } - else { - gen_helper_float_FT(FREG(B11_8), cpu_env, cpu_fpul); - } - return; + } + else { + gen_helper_float_FT(FREG(B11_8), tcg_env, cpu_fpul); + } + return; case 0xf03d: /* ftrc FRm/DRm,FPUL - FPSCR: R[PR,Enable.V]/W[Cause,Flag] */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED if (ctx->tbflags & FPSCR_PR) { - TCGv_i64 fp; + TCGv_i64 fp; if (ctx->opcode & 0x0100) { goto do_illegal; } - fp = tcg_temp_new_i64(); + fp = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp, B11_8); - gen_helper_ftrc_DT(cpu_fpul, cpu_env, fp); - tcg_temp_free_i64(fp); - } - else { - gen_helper_ftrc_FT(cpu_fpul, cpu_env, FREG(B11_8)); - } - return; + gen_helper_ftrc_DT(cpu_fpul, tcg_env, fp); + } + else { + gen_helper_ftrc_FT(cpu_fpul, tcg_env, FREG(B11_8)); + } + return; case 0xf04d: /* fneg FRn/DRn - FPSCR: Nothing */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED tcg_gen_xori_i32(FREG(B11_8), FREG(B11_8), 0x80000000); - return; + return; case 0xf05d: /* fabs FRn/DRn - FPCSR: Nothing */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED tcg_gen_andi_i32(FREG(B11_8), FREG(B11_8), 0x7fffffff); - return; + return; case 0xf06d: /* fsqrt FRn */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED if (ctx->tbflags & FPSCR_PR) { if (ctx->opcode & 0x0100) { goto do_illegal; } - TCGv_i64 fp = tcg_temp_new_i64(); + TCGv_i64 fp = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp, B11_8); - gen_helper_fsqrt_DT(fp, cpu_env, fp); + gen_helper_fsqrt_DT(fp, tcg_env, fp); gen_store_fpr64(ctx, fp, B11_8); - tcg_temp_free_i64(fp); - } else { - gen_helper_fsqrt_FT(FREG(B11_8), cpu_env, FREG(B11_8)); - } - return; + } else { + gen_helper_fsqrt_FT(FREG(B11_8), tcg_env, FREG(B11_8)); + } + return; case 0xf07d: /* fsrra FRn */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED CHECK_FPSCR_PR_0 - gen_helper_fsrra_FT(FREG(B11_8), cpu_env, FREG(B11_8)); - break; + gen_helper_fsrra_FT(FREG(B11_8), tcg_env, FREG(B11_8)); + break; case 0xf08d: /* fldi0 FRn - FPSCR: R[PR] */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED CHECK_FPSCR_PR_0 tcg_gen_movi_i32(FREG(B11_8), 0); return; case 0xf09d: /* fldi1 FRn - FPSCR: R[PR] */ - CHECK_FPU_ENABLED + CHECK_FPU_ENABLED CHECK_FPSCR_PR_0 tcg_gen_movi_i32(FREG(B11_8), 0x3f800000); return; case 0xf0ad: /* fcnvsd FPUL,DRn */ - CHECK_FPU_ENABLED - { - TCGv_i64 fp = tcg_temp_new_i64(); - gen_helper_fcnvsd_FT_DT(fp, cpu_env, cpu_fpul); + CHECK_FPU_ENABLED + { + TCGv_i64 fp = tcg_temp_new_i64(); + gen_helper_fcnvsd_FT_DT(fp, tcg_env, cpu_fpul); gen_store_fpr64(ctx, fp, B11_8); - tcg_temp_free_i64(fp); - } - return; + } + return; case 0xf0bd: /* fcnvds DRn,FPUL */ - CHECK_FPU_ENABLED - { - TCGv_i64 fp = tcg_temp_new_i64(); + CHECK_FPU_ENABLED + { + TCGv_i64 fp = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp, B11_8); - gen_helper_fcnvds_DT_FT(cpu_fpul, cpu_env, fp); - tcg_temp_free_i64(fp); - } - return; + gen_helper_fcnvds_DT_FT(cpu_fpul, tcg_env, fp); + } + return; case 0xf0ed: /* fipr FVm,FVn */ CHECK_FPU_ENABLED CHECK_FPSCR_PR_1 { - TCGv m = tcg_const_i32((ctx->opcode >> 8) & 3); - TCGv n = tcg_const_i32((ctx->opcode >> 10) & 3); - gen_helper_fipr(cpu_env, m, n); - tcg_temp_free(m); - tcg_temp_free(n); + TCGv m = tcg_constant_i32((ctx->opcode >> 8) & 3); + TCGv n = tcg_constant_i32((ctx->opcode >> 10) & 3); + gen_helper_fipr(tcg_env, m, n); return; } break; @@ -1826,9 +1753,8 @@ static void _decode_opc(DisasContext * ctx) if ((ctx->opcode & 0x0300) != 0x0100) { goto do_illegal; } - TCGv n = tcg_const_i32((ctx->opcode >> 10) & 3); - gen_helper_ftrv(cpu_env, n); - tcg_temp_free(n); + TCGv n = tcg_constant_i32((ctx->opcode >> 10) & 3); + gen_helper_ftrv(tcg_env, n); return; } break; @@ -1839,23 +1765,23 @@ static void _decode_opc(DisasContext * ctx) fflush(stderr); #endif do_illegal: - if (ctx->envflags & DELAY_SLOT_MASK) { + if (ctx->envflags & TB_FLAG_DELAY_SLOT_MASK) { do_illegal_slot: gen_save_cpu_state(ctx, true); - gen_helper_raise_slot_illegal_instruction(cpu_env); + gen_helper_raise_slot_illegal_instruction(tcg_env); } else { gen_save_cpu_state(ctx, true); - gen_helper_raise_illegal_instruction(cpu_env); + gen_helper_raise_illegal_instruction(tcg_env); } ctx->base.is_jmp = DISAS_NORETURN; return; do_fpu_disabled: gen_save_cpu_state(ctx, true); - if (ctx->envflags & DELAY_SLOT_MASK) { - gen_helper_raise_slot_fpu_disable(cpu_env); + if (ctx->envflags & TB_FLAG_DELAY_SLOT_MASK) { + gen_helper_raise_slot_fpu_disable(tcg_env); } else { - gen_helper_raise_fpu_disable(cpu_env); + gen_helper_raise_fpu_disable(tcg_env); } ctx->base.is_jmp = DISAS_NORETURN; return; @@ -1867,31 +1793,43 @@ static void decode_opc(DisasContext * ctx) _decode_opc(ctx); - if (old_flags & DELAY_SLOT_MASK) { + if (old_flags & TB_FLAG_DELAY_SLOT_MASK) { /* go out of the delay slot */ - ctx->envflags &= ~DELAY_SLOT_MASK; + ctx->envflags &= ~TB_FLAG_DELAY_SLOT_MASK; /* When in an exclusive region, we must continue to the end for conditional branches. */ - if (ctx->tbflags & GUSA_EXCLUSIVE - && old_flags & DELAY_SLOT_CONDITIONAL) { + if (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE + && old_flags & TB_FLAG_DELAY_SLOT_COND) { gen_delayed_conditional_jump(ctx); return; } /* Otherwise this is probably an invalid gUSA region. Drop the GUSA bits so the next TB doesn't see them. */ - ctx->envflags &= ~GUSA_MASK; + ctx->envflags &= ~TB_FLAG_GUSA_MASK; tcg_gen_movi_i32(cpu_flags, ctx->envflags); - if (old_flags & DELAY_SLOT_CONDITIONAL) { - gen_delayed_conditional_jump(ctx); + if (old_flags & TB_FLAG_DELAY_SLOT_COND) { + gen_delayed_conditional_jump(ctx); } else { gen_jump(ctx); - } + } } } #ifdef CONFIG_USER_ONLY +/* + * Restart with the EXCLUSIVE bit set, within a TB run via + * cpu_exec_step_atomic holding the exclusive lock. + */ +static void gen_restart_exclusive(DisasContext *ctx) +{ + ctx->envflags |= TB_FLAG_GUSA_EXCLUSIVE; + gen_save_cpu_state(ctx, false); + gen_helper_exclusive(tcg_env); + ctx->base.is_jmp = DISAS_NORETURN; +} + /* For uniprocessors, SH4 uses optimistic restartable atomic sequences. Upon an interrupt, a real kernel would simply notice magic values in the registers and reset the PC to the start of the sequence. @@ -2031,7 +1969,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) } op_dst = B11_8; op_opc = INDEX_op_xor_i32; - op_arg = tcg_const_i32(-1); + op_arg = tcg_constant_i32(-1); break; case 0x7000 ... 0x700f: /* add #imm,Rn */ @@ -2039,7 +1977,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) goto fail; } op_opc = INDEX_op_add_i32; - op_arg = tcg_const_i32(B7_0s); + op_arg = tcg_constant_i32(B7_0s); break; case 0x3000: /* cmp/eq Rm,Rn */ @@ -2085,7 +2023,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) goto fail; } op_opc = INDEX_op_setcond_i32; - op_arg = tcg_const_i32(0); + op_arg = tcg_constant_i32(0); NEXT_INSN; if ((ctx->opcode & 0xff00) != 0x8900 /* bt label */ @@ -2217,41 +2155,39 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) g_assert_not_reached(); } - /* If op_src is not a valid register, then op_arg was a constant. */ - if (op_src < 0 && op_arg) { - tcg_temp_free_i32(op_arg); - } - /* The entire region has been translated. */ - ctx->envflags &= ~GUSA_MASK; - ctx->base.pc_next = pc_end; - ctx->base.num_insns += max_insns - 1; - return; + ctx->envflags &= ~TB_FLAG_GUSA_MASK; + goto done; fail: qemu_log_mask(LOG_UNIMP, "Unrecognized gUSA sequence %08x-%08x\n", pc, pc_end); - /* Restart with the EXCLUSIVE bit set, within a TB run via - cpu_exec_step_atomic holding the exclusive lock. */ - ctx->envflags |= GUSA_EXCLUSIVE; - gen_save_cpu_state(ctx, false); - gen_helper_exclusive(cpu_env); - ctx->base.is_jmp = DISAS_NORETURN; + gen_restart_exclusive(ctx); /* We're not executing an instruction, but we must report one for the purposes of accounting within the TB. We might as well report the entire region consumed via ctx->base.pc_next so that it's immediately available in the disassembly dump. */ + + done: ctx->base.pc_next = pc_end; ctx->base.num_insns += max_insns - 1; + + /* + * Emit insn_start to cover each of the insns in the region. + * This matches an assert in tcg.c making sure that we have + * tb->icount * insn_start. + */ + for (i = 1; i < max_insns; ++i) { + tcg_gen_insn_start(pc + i * 2, ctx->envflags); + } } #endif static void sh4_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - CPUSH4State *env = cs->env_ptr; uint32_t tbflags; int bound; @@ -2261,23 +2197,25 @@ static void sh4_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) /* We don't know if the delayed pc came from a dynamic or static branch, so assume it is a dynamic branch. */ ctx->delayed_pc = -1; /* use delayed pc from env pointer */ - ctx->features = env->features; + ctx->features = cpu_env(cs)->features; ctx->has_movcal = (tbflags & TB_FLAG_PENDING_MOVCA); ctx->gbank = ((tbflags & (1 << SR_MD)) && (tbflags & (1 << SR_RB))) * 0x10; ctx->fbank = tbflags & FPSCR_FR ? 0x10 : 0; - if (tbflags & GUSA_MASK) { +#ifdef CONFIG_USER_ONLY + if (tbflags & TB_FLAG_GUSA_MASK) { + /* In gUSA exclusive region. */ uint32_t pc = ctx->base.pc_next; uint32_t pc_end = ctx->base.tb->cs_base; - int backup = sextract32(ctx->tbflags, GUSA_SHIFT, 8); + int backup = sextract32(ctx->tbflags, TB_FLAG_GUSA_SHIFT, 8); int max_insns = (pc_end - pc) / 2; if (pc != pc_end + backup || max_insns < 2) { /* This is a malformed gUSA region. Don't do anything special, since the interpreter is likely to get confused. */ - ctx->envflags &= ~GUSA_MASK; - } else if (tbflags & GUSA_EXCLUSIVE) { + ctx->envflags &= ~TB_FLAG_GUSA_MASK; + } else if (tbflags & TB_FLAG_GUSA_EXCLUSIVE) { /* Regardless of single-stepping or the end of the page, we must complete execution of the gUSA region while holding the exclusive lock. */ @@ -2285,6 +2223,7 @@ static void sh4_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) return; } } +#endif /* Since the ISA is fixed-width, we can bound by the number of instructions remaining on the page. */ @@ -2305,18 +2244,28 @@ static void sh4_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) static void sh4_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) { - CPUSH4State *env = cs->env_ptr; + CPUSH4State *env = cpu_env(cs); DisasContext *ctx = container_of(dcbase, DisasContext, base); #ifdef CONFIG_USER_ONLY - if (unlikely(ctx->envflags & GUSA_MASK) - && !(ctx->envflags & GUSA_EXCLUSIVE)) { - /* We're in an gUSA region, and we have not already fallen - back on using an exclusive region. Attempt to parse the - region into a single supported atomic operation. Failure - is handled within the parser by raising an exception to - retry using an exclusive region. */ - decode_gusa(ctx, env); + if (unlikely(ctx->envflags & TB_FLAG_GUSA_MASK) + && !(ctx->envflags & TB_FLAG_GUSA_EXCLUSIVE)) { + /* + * We're in an gUSA region, and we have not already fallen + * back on using an exclusive region. Attempt to parse the + * region into a single supported atomic operation. Failure + * is handled within the parser by raising an exception to + * retry using an exclusive region. + * + * Parsing the region in one block conflicts with plugins, + * so always use exclusive mode if plugins enabled. + */ + if (ctx->base.plugin_enabled) { + gen_restart_exclusive(ctx); + ctx->base.pc_next += 2; + } else { + decode_gusa(ctx, env); + } return; } #endif @@ -2330,9 +2279,9 @@ static void sh4_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - if (ctx->tbflags & GUSA_EXCLUSIVE) { + if (ctx->tbflags & TB_FLAG_GUSA_EXCLUSIVE) { /* Ending the region of exclusivity. Clear the bits. */ - ctx->envflags &= ~GUSA_MASK; + ctx->envflags &= ~TB_FLAG_GUSA_MASK; } switch (ctx->base.is_jmp) { @@ -2368,19 +2317,10 @@ static const TranslatorOps sh4_tr_ops = { .disas_log = sh4_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; - translator_loop(&sh4_tr_ops, &ctx.base, cs, tb, max_insns); -} - -void restore_state_to_opc(CPUSH4State *env, TranslationBlock *tb, - target_ulong *data) -{ - env->pc = data[0]; - env->flags = data[1]; - /* Theoretically delayed_pc should also be restored. In practice the - branch instruction is re-executed after exception, so the delayed - branch target will be recomputed. */ + translator_loop(cs, tb, max_insns, pc, host_pc, &sh4_tr_ops, &ctx.base); } diff --git a/target/sparc/asi.h b/target/sparc/asi.h index bb58735ddb..a66829674b 100644 --- a/target/sparc/asi.h +++ b/target/sparc/asi.h @@ -144,15 +144,15 @@ * ASIs, "(4V)" designates SUN4V specific ASIs. "(NG4)" designates SPARC-T4 * and later ASIs. */ -#define ASI_REAL 0x14 /* Real address, cachable */ -#define ASI_PHYS_USE_EC 0x14 /* PADDR, E-cachable */ -#define ASI_REAL_IO 0x15 /* Real address, non-cachable */ +#define ASI_REAL 0x14 /* Real address, cacheable */ +#define ASI_PHYS_USE_EC 0x14 /* PADDR, E-cacheable */ +#define ASI_REAL_IO 0x15 /* Real address, non-cacheable */ #define ASI_PHYS_BYPASS_EC_E 0x15 /* PADDR, E-bit */ #define ASI_BLK_AIUP_4V 0x16 /* (4V) Prim, user, block ld/st */ #define ASI_BLK_AIUS_4V 0x17 /* (4V) Sec, user, block ld/st */ -#define ASI_REAL_L 0x1c /* Real address, cachable, LE */ -#define ASI_PHYS_USE_EC_L 0x1c /* PADDR, E-cachable, little endian*/ -#define ASI_REAL_IO_L 0x1d /* Real address, non-cachable, LE */ +#define ASI_REAL_L 0x1c /* Real address, cacheable, LE */ +#define ASI_PHYS_USE_EC_L 0x1c /* PADDR, E-cacheable, little endian*/ +#define ASI_REAL_IO_L 0x1d /* Real address, non-cacheable, LE */ #define ASI_PHYS_BYPASS_EC_E_L 0x1d /* PADDR, E-bit, little endian */ #define ASI_BLK_AIUP_L_4V 0x1e /* (4V) Prim, user, block, l-endian*/ #define ASI_BLK_AIUS_L_4V 0x1f /* (4V) Sec, user, block, l-endian */ @@ -163,15 +163,15 @@ #define ASI_BLK_INIT_QUAD_LDD_AIUS 0x23 /* (NG) init-store, twin load, * secondary, user */ -#define ASI_NUCLEUS_QUAD_LDD 0x24 /* Cachable, qword load */ +#define ASI_NUCLEUS_QUAD_LDD 0x24 /* Cacheable, qword load */ #define ASI_QUEUE 0x25 /* (4V) Interrupt Queue Registers */ -#define ASI_TWINX_REAL 0x26 /* twin load, real, cachable */ +#define ASI_TWINX_REAL 0x26 /* twin load, real, cacheable */ #define ASI_QUAD_LDD_PHYS_4V 0x26 /* (4V) Physical, qword load */ #define ASI_TWINX_N 0x27 /* twin load, nucleus */ #define ASI_TWINX_AIUP_L 0x2a /* twin load, primary user, LE */ #define ASI_TWINX_AIUS_L 0x2b /* twin load, secondary user, LE */ -#define ASI_NUCLEUS_QUAD_LDD_L 0x2c /* Cachable, qword load, l-endian */ -#define ASI_TWINX_REAL_L 0x2e /* twin load, real, cachable, LE */ +#define ASI_NUCLEUS_QUAD_LDD_L 0x2c /* Cacheable, qword load, l-endian */ +#define ASI_TWINX_REAL_L 0x2e /* twin load, real, cacheable, LE */ #define ASI_QUAD_LDD_PHYS_L_4V 0x2e /* (4V) Phys, qword load, l-endian */ #define ASI_TWINX_NL 0x2f /* twin load, nucleus, LE */ #define ASI_PCACHE_DATA_STATUS 0x30 /* (III) PCache data stat RAM diag */ @@ -231,7 +231,7 @@ #define ASI_INTR_ID 0x63 /* (CMT) Interrupt ID register */ #define ASI_CORE_ID 0x63 /* (CMT) LP ID register */ #define ASI_CESR_ID 0x63 /* (CMT) CESR ID register */ -#define ASI_IC_INSTR 0x66 /* Insn cache instrucion ram diag */ +#define ASI_IC_INSTR 0x66 /* Insn cache instruction ram diag */ #define ASI_IC_TAG 0x67 /* Insn cache tag/valid ram diag */ #define ASI_IC_STAG 0x68 /* (III) Insn cache snoop tag ram */ #define ASI_IC_PRE_DECODE 0x6e /* Insn cache pre-decode ram diag */ diff --git a/target/sparc/cc_helper.c b/target/sparc/cc_helper.c deleted file mode 100644 index 7ad5b9b29e..0000000000 --- a/target/sparc/cc_helper.c +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Helpers for lazy condition code handling - * - * Copyright (c) 2003-2005 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.1 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 "cpu.h" -#include "exec/helper-proto.h" - -static uint32_t compute_all_flags(CPUSPARCState *env) -{ - return env->psr & PSR_ICC; -} - -static uint32_t compute_C_flags(CPUSPARCState *env) -{ - return env->psr & PSR_CARRY; -} - -static inline uint32_t get_NZ_icc(int32_t dst) -{ - uint32_t ret = 0; - - if (dst == 0) { - ret = PSR_ZERO; - } else if (dst < 0) { - ret = PSR_NEG; - } - return ret; -} - -#ifdef TARGET_SPARC64 -static uint32_t compute_all_flags_xcc(CPUSPARCState *env) -{ - return env->xcc & PSR_ICC; -} - -static uint32_t compute_C_flags_xcc(CPUSPARCState *env) -{ - return env->xcc & PSR_CARRY; -} - -static inline uint32_t get_NZ_xcc(target_long dst) -{ - uint32_t ret = 0; - - if (!dst) { - ret = PSR_ZERO; - } else if (dst < 0) { - ret = PSR_NEG; - } - return ret; -} -#endif - -static inline uint32_t get_V_div_icc(target_ulong src2) -{ - uint32_t ret = 0; - - if (src2 != 0) { - ret = PSR_OVF; - } - return ret; -} - -static uint32_t compute_all_div(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_V_div_icc(CC_SRC2); - return ret; -} - -static uint32_t compute_C_div(CPUSPARCState *env) -{ - return 0; -} - -static inline uint32_t get_C_add_icc(uint32_t dst, uint32_t src1) -{ - uint32_t ret = 0; - - if (dst < src1) { - ret = PSR_CARRY; - } - return ret; -} - -static inline uint32_t get_C_addx_icc(uint32_t dst, uint32_t src1, - uint32_t src2) -{ - uint32_t ret = 0; - - if (((src1 & src2) | (~dst & (src1 | src2))) & (1U << 31)) { - ret = PSR_CARRY; - } - return ret; -} - -static inline uint32_t get_V_add_icc(uint32_t dst, uint32_t src1, - uint32_t src2) -{ - uint32_t ret = 0; - - if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1U << 31)) { - ret = PSR_OVF; - } - return ret; -} - -#ifdef TARGET_SPARC64 -static inline uint32_t get_C_add_xcc(target_ulong dst, target_ulong src1) -{ - uint32_t ret = 0; - - if (dst < src1) { - ret = PSR_CARRY; - } - return ret; -} - -static inline uint32_t get_C_addx_xcc(target_ulong dst, target_ulong src1, - target_ulong src2) -{ - uint32_t ret = 0; - - if (((src1 & src2) | (~dst & (src1 | src2))) & (1ULL << 63)) { - ret = PSR_CARRY; - } - return ret; -} - -static inline uint32_t get_V_add_xcc(target_ulong dst, target_ulong src1, - target_ulong src2) -{ - uint32_t ret = 0; - - if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1ULL << 63)) { - ret = PSR_OVF; - } - return ret; -} - -static uint32_t compute_all_add_xcc(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_xcc(CC_DST); - ret |= get_C_add_xcc(CC_DST, CC_SRC); - ret |= get_V_add_xcc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_C_add_xcc(CPUSPARCState *env) -{ - return get_C_add_xcc(CC_DST, CC_SRC); -} -#endif - -static uint32_t compute_all_add(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_add_icc(CC_DST, CC_SRC); - ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_C_add(CPUSPARCState *env) -{ - return get_C_add_icc(CC_DST, CC_SRC); -} - -#ifdef TARGET_SPARC64 -static uint32_t compute_all_addx_xcc(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_xcc(CC_DST); - ret |= get_C_addx_xcc(CC_DST, CC_SRC, CC_SRC2); - ret |= get_V_add_xcc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_C_addx_xcc(CPUSPARCState *env) -{ - return get_C_addx_xcc(CC_DST, CC_SRC, CC_SRC2); -} -#endif - -static uint32_t compute_all_addx(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_addx_icc(CC_DST, CC_SRC, CC_SRC2); - ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_C_addx(CPUSPARCState *env) -{ - return get_C_addx_icc(CC_DST, CC_SRC, CC_SRC2); -} - -static inline uint32_t get_V_tag_icc(target_ulong src1, target_ulong src2) -{ - uint32_t ret = 0; - - if ((src1 | src2) & 0x3) { - ret = PSR_OVF; - } - return ret; -} - -static uint32_t compute_all_tadd(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_add_icc(CC_DST, CC_SRC); - ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2); - ret |= get_V_tag_icc(CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_all_taddtv(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_add_icc(CC_DST, CC_SRC); - return ret; -} - -static inline uint32_t get_C_sub_icc(uint32_t src1, uint32_t src2) -{ - uint32_t ret = 0; - - if (src1 < src2) { - ret = PSR_CARRY; - } - return ret; -} - -static inline uint32_t get_C_subx_icc(uint32_t dst, uint32_t src1, - uint32_t src2) -{ - uint32_t ret = 0; - - if (((~src1 & src2) | (dst & (~src1 | src2))) & (1U << 31)) { - ret = PSR_CARRY; - } - return ret; -} - -static inline uint32_t get_V_sub_icc(uint32_t dst, uint32_t src1, - uint32_t src2) -{ - uint32_t ret = 0; - - if (((src1 ^ src2) & (src1 ^ dst)) & (1U << 31)) { - ret = PSR_OVF; - } - return ret; -} - - -#ifdef TARGET_SPARC64 -static inline uint32_t get_C_sub_xcc(target_ulong src1, target_ulong src2) -{ - uint32_t ret = 0; - - if (src1 < src2) { - ret = PSR_CARRY; - } - return ret; -} - -static inline uint32_t get_C_subx_xcc(target_ulong dst, target_ulong src1, - target_ulong src2) -{ - uint32_t ret = 0; - - if (((~src1 & src2) | (dst & (~src1 | src2))) & (1ULL << 63)) { - ret = PSR_CARRY; - } - return ret; -} - -static inline uint32_t get_V_sub_xcc(target_ulong dst, target_ulong src1, - target_ulong src2) -{ - uint32_t ret = 0; - - if (((src1 ^ src2) & (src1 ^ dst)) & (1ULL << 63)) { - ret = PSR_OVF; - } - return ret; -} - -static uint32_t compute_all_sub_xcc(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_xcc(CC_DST); - ret |= get_C_sub_xcc(CC_SRC, CC_SRC2); - ret |= get_V_sub_xcc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_C_sub_xcc(CPUSPARCState *env) -{ - return get_C_sub_xcc(CC_SRC, CC_SRC2); -} -#endif - -static uint32_t compute_all_sub(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_sub_icc(CC_SRC, CC_SRC2); - ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_C_sub(CPUSPARCState *env) -{ - return get_C_sub_icc(CC_SRC, CC_SRC2); -} - -#ifdef TARGET_SPARC64 -static uint32_t compute_all_subx_xcc(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_xcc(CC_DST); - ret |= get_C_subx_xcc(CC_DST, CC_SRC, CC_SRC2); - ret |= get_V_sub_xcc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_C_subx_xcc(CPUSPARCState *env) -{ - return get_C_subx_xcc(CC_DST, CC_SRC, CC_SRC2); -} -#endif - -static uint32_t compute_all_subx(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_subx_icc(CC_DST, CC_SRC, CC_SRC2); - ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_C_subx(CPUSPARCState *env) -{ - return get_C_subx_icc(CC_DST, CC_SRC, CC_SRC2); -} - -static uint32_t compute_all_tsub(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_sub_icc(CC_SRC, CC_SRC2); - ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2); - ret |= get_V_tag_icc(CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_all_tsubtv(CPUSPARCState *env) -{ - uint32_t ret; - - ret = get_NZ_icc(CC_DST); - ret |= get_C_sub_icc(CC_SRC, CC_SRC2); - return ret; -} - -static uint32_t compute_all_logic(CPUSPARCState *env) -{ - return get_NZ_icc(CC_DST); -} - -static uint32_t compute_C_logic(CPUSPARCState *env) -{ - return 0; -} - -#ifdef TARGET_SPARC64 -static uint32_t compute_all_logic_xcc(CPUSPARCState *env) -{ - return get_NZ_xcc(CC_DST); -} -#endif - -typedef struct CCTable { - uint32_t (*compute_all)(CPUSPARCState *env); /* return all the flags */ - uint32_t (*compute_c)(CPUSPARCState *env); /* return the C flag */ -} CCTable; - -static const CCTable icc_table[CC_OP_NB] = { - /* CC_OP_DYNAMIC should never happen */ - [CC_OP_FLAGS] = { compute_all_flags, compute_C_flags }, - [CC_OP_DIV] = { compute_all_div, compute_C_div }, - [CC_OP_ADD] = { compute_all_add, compute_C_add }, - [CC_OP_ADDX] = { compute_all_addx, compute_C_addx }, - [CC_OP_TADD] = { compute_all_tadd, compute_C_add }, - [CC_OP_TADDTV] = { compute_all_taddtv, compute_C_add }, - [CC_OP_SUB] = { compute_all_sub, compute_C_sub }, - [CC_OP_SUBX] = { compute_all_subx, compute_C_subx }, - [CC_OP_TSUB] = { compute_all_tsub, compute_C_sub }, - [CC_OP_TSUBTV] = { compute_all_tsubtv, compute_C_sub }, - [CC_OP_LOGIC] = { compute_all_logic, compute_C_logic }, -}; - -#ifdef TARGET_SPARC64 -static const CCTable xcc_table[CC_OP_NB] = { - /* CC_OP_DYNAMIC should never happen */ - [CC_OP_FLAGS] = { compute_all_flags_xcc, compute_C_flags_xcc }, - [CC_OP_DIV] = { compute_all_logic_xcc, compute_C_logic }, - [CC_OP_ADD] = { compute_all_add_xcc, compute_C_add_xcc }, - [CC_OP_ADDX] = { compute_all_addx_xcc, compute_C_addx_xcc }, - [CC_OP_TADD] = { compute_all_add_xcc, compute_C_add_xcc }, - [CC_OP_TADDTV] = { compute_all_add_xcc, compute_C_add_xcc }, - [CC_OP_SUB] = { compute_all_sub_xcc, compute_C_sub_xcc }, - [CC_OP_SUBX] = { compute_all_subx_xcc, compute_C_subx_xcc }, - [CC_OP_TSUB] = { compute_all_sub_xcc, compute_C_sub_xcc }, - [CC_OP_TSUBTV] = { compute_all_sub_xcc, compute_C_sub_xcc }, - [CC_OP_LOGIC] = { compute_all_logic_xcc, compute_C_logic }, -}; -#endif - -void helper_compute_psr(CPUSPARCState *env) -{ - uint32_t new_psr; - - new_psr = icc_table[CC_OP].compute_all(env); - env->psr = new_psr; -#ifdef TARGET_SPARC64 - new_psr = xcc_table[CC_OP].compute_all(env); - env->xcc = new_psr; -#endif - CC_OP = CC_OP_FLAGS; -} - -uint32_t helper_compute_C_icc(CPUSPARCState *env) -{ - return icc_table[CC_OP].compute_c(env) >> PSR_CARRY_SHIFT; -} diff --git a/target/sparc/cpu-feature.h.inc b/target/sparc/cpu-feature.h.inc new file mode 100644 index 0000000000..d800f18c4e --- /dev/null +++ b/target/sparc/cpu-feature.h.inc @@ -0,0 +1,14 @@ +FEATURE(FLOAT128) +FEATURE(MUL) +FEATURE(DIV) +FEATURE(VIS1) +FEATURE(VIS2) +FEATURE(FSMULD) +FEATURE(HYPV) +FEATURE(CMT) +FEATURE(GL) +FEATURE(TA0_SHUTDOWN) /* Shutdown on "ta 0x0" */ +FEATURE(ASR17) +FEATURE(CACHE_CTRL) +FEATURE(POWERDOWN) +FEATURE(CASA) diff --git a/target/sparc/cpu-param.h b/target/sparc/cpu-param.h index 72ddc4a34f..cb11980404 100644 --- a/target/sparc/cpu-param.h +++ b/target/sparc/cpu-param.h @@ -16,13 +16,11 @@ # else # define TARGET_VIRT_ADDR_SPACE_BITS 44 # endif -# define NB_MMU_MODES 6 #else # define TARGET_LONG_BITS 32 # define TARGET_PAGE_BITS 12 /* 4k */ # define TARGET_PHYS_ADDR_SPACE_BITS 36 # define TARGET_VIRT_ADDR_SPACE_BITS 32 -# define NB_MMU_MODES 3 #endif #endif diff --git a/target/sparc/cpu-qom.h b/target/sparc/cpu-qom.h index 86ed37d933..a86331bd58 100644 --- a/target/sparc/cpu-qom.h +++ b/target/sparc/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU SPARC CPU + * QEMU SPARC CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -21,7 +21,6 @@ #define QEMU_SPARC_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #ifdef TARGET_SPARC64 #define TYPE_SPARC_CPU "sparc64-cpu" @@ -31,23 +30,7 @@ OBJECT_DECLARE_CPU_TYPE(SPARCCPU, SPARCCPUClass, SPARC_CPU) -typedef struct sparc_def_t sparc_def_t; -/** - * SPARCCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * - * A SPARC CPU model. - */ -struct SPARCCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceReset parent_reset; - sparc_def_t *cpu_def; -}; - +#define SPARC_CPU_TYPE_SUFFIX "-" TYPE_SPARC_CPU +#define SPARC_CPU_TYPE_NAME(model) model SPARC_CPU_TYPE_SUFFIX #endif diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 55268ed2a1..e820f50acf 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -25,17 +25,19 @@ #include "exec/exec-all.h" #include "hw/qdev-properties.h" #include "qapi/visitor.h" +#include "tcg/tcg.h" //#define DEBUG_FEATURES -static void sparc_cpu_reset(DeviceState *dev) +static void sparc_cpu_reset_hold(Object *obj) { - CPUState *s = CPU(dev); - SPARCCPU *cpu = SPARC_CPU(s); - SPARCCPUClass *scc = SPARC_CPU_GET_CLASS(cpu); - CPUSPARCState *env = &cpu->env; + CPUState *cs = CPU(obj); + SPARCCPUClass *scc = SPARC_CPU_GET_CLASS(obj); + CPUSPARCState *env = cpu_env(cs); - scc->parent_reset(dev); + if (scc->parent_phases.hold) { + scc->parent_phases.hold(obj); + } memset(env, 0, offsetof(CPUSPARCState, end_reset_fields)); env->cwp = 0; @@ -43,7 +45,6 @@ static void sparc_cpu_reset(DeviceState *dev) env->wim = 1; #endif env->regwptr = env->regbase + (env->cwp * 16); - CC_OP = CC_OP_FLAGS; #if defined(CONFIG_USER_ONLY) #ifdef TARGET_SPARC64 env->cleanwin = env->nwindows - 2; @@ -81,8 +82,7 @@ static void sparc_cpu_reset(DeviceState *dev) static bool sparc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { if (interrupt_request & CPU_INTERRUPT_HARD) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; + CPUSPARCState *env = cpu_env(cs); if (cpu_interrupts_enabled(env) && env->interrupt_index > 0) { int pil = env->interrupt_index & 0xf; @@ -366,7 +366,7 @@ static const sparc_def_t sparc_defs[] = { { .name = "Fujitsu MB86904", .iu_version = 0x04 << 24, /* Impl 0, ver 4 */ - .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ + .fpu_version = 4 << FSR_VER_SHIFT, /* FPU version 4 (Meiko) */ .mmu_version = 0x04 << 24, /* Impl 0, ver 4 */ .mmu_bm = 0x00004000, .mmu_ctpr_mask = 0x00ffffc0, @@ -379,7 +379,7 @@ static const sparc_def_t sparc_defs[] = { { .name = "Fujitsu MB86907", .iu_version = 0x05 << 24, /* Impl 0, ver 5 */ - .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ + .fpu_version = 4 << FSR_VER_SHIFT, /* FPU version 4 (Meiko) */ .mmu_version = 0x05 << 24, /* Impl 0, ver 5 */ .mmu_bm = 0x00004000, .mmu_ctpr_mask = 0xffffffc0, @@ -392,7 +392,7 @@ static const sparc_def_t sparc_defs[] = { { .name = "TI MicroSparc I", .iu_version = 0x41000000, - .fpu_version = 4 << 17, + .fpu_version = 4 << FSR_VER_SHIFT, .mmu_version = 0x41000000, .mmu_bm = 0x00004000, .mmu_ctpr_mask = 0x007ffff0, @@ -400,14 +400,12 @@ static const sparc_def_t sparc_defs[] = { .mmu_sfsr_mask = 0x00016fff, .mmu_trcr_mask = 0x0000003f, .nwindows = 7, - .features = CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | CPU_FEATURE_MUL | - CPU_FEATURE_DIV | CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT | - CPU_FEATURE_FMUL, + .features = CPU_FEATURE_MUL | CPU_FEATURE_DIV, }, { .name = "TI MicroSparc II", .iu_version = 0x42000000, - .fpu_version = 4 << 17, + .fpu_version = 4 << FSR_VER_SHIFT, .mmu_version = 0x02000000, .mmu_bm = 0x00004000, .mmu_ctpr_mask = 0x00ffffc0, @@ -420,7 +418,7 @@ static const sparc_def_t sparc_defs[] = { { .name = "TI MicroSparc IIep", .iu_version = 0x42000000, - .fpu_version = 4 << 17, + .fpu_version = 4 << FSR_VER_SHIFT, .mmu_version = 0x04000000, .mmu_bm = 0x00004000, .mmu_ctpr_mask = 0x00ffffc0, @@ -433,7 +431,7 @@ static const sparc_def_t sparc_defs[] = { { .name = "TI SuperSparc 40", /* STP1020NPGA */ .iu_version = 0x41000000, /* SuperSPARC 2.x */ - .fpu_version = 0 << 17, + .fpu_version = 0 << FSR_VER_SHIFT, .mmu_version = 0x00000800, /* SuperSPARC 2.x, no MXCC */ .mmu_bm = 0x00002000, .mmu_ctpr_mask = 0xffffffc0, @@ -446,7 +444,7 @@ static const sparc_def_t sparc_defs[] = { { .name = "TI SuperSparc 50", /* STP1020PGA */ .iu_version = 0x40000000, /* SuperSPARC 3.x */ - .fpu_version = 0 << 17, + .fpu_version = 0 << FSR_VER_SHIFT, .mmu_version = 0x01000800, /* SuperSPARC 3.x, no MXCC */ .mmu_bm = 0x00002000, .mmu_ctpr_mask = 0xffffffc0, @@ -459,7 +457,7 @@ static const sparc_def_t sparc_defs[] = { { .name = "TI SuperSparc 51", .iu_version = 0x40000000, /* SuperSPARC 3.x */ - .fpu_version = 0 << 17, + .fpu_version = 0 << FSR_VER_SHIFT, .mmu_version = 0x01000000, /* SuperSPARC 3.x, MXCC */ .mmu_bm = 0x00002000, .mmu_ctpr_mask = 0xffffffc0, @@ -473,7 +471,7 @@ static const sparc_def_t sparc_defs[] = { { .name = "TI SuperSparc 60", /* STP1020APGA */ .iu_version = 0x40000000, /* SuperSPARC 3.x */ - .fpu_version = 0 << 17, + .fpu_version = 0 << FSR_VER_SHIFT, .mmu_version = 0x01000800, /* SuperSPARC 3.x, no MXCC */ .mmu_bm = 0x00002000, .mmu_ctpr_mask = 0xffffffc0, @@ -486,7 +484,7 @@ static const sparc_def_t sparc_defs[] = { { .name = "TI SuperSparc 61", .iu_version = 0x44000000, /* SuperSPARC 3.x */ - .fpu_version = 0 << 17, + .fpu_version = 0 << FSR_VER_SHIFT, .mmu_version = 0x01000000, /* SuperSPARC 3.x, MXCC */ .mmu_bm = 0x00002000, .mmu_ctpr_mask = 0xffffffc0, @@ -500,7 +498,7 @@ static const sparc_def_t sparc_defs[] = { { .name = "TI SuperSparc II", .iu_version = 0x40000000, /* SuperSPARC II 1.x */ - .fpu_version = 0 << 17, + .fpu_version = 0 << FSR_VER_SHIFT, .mmu_version = 0x08000000, /* SuperSPARC II 1.x, MXCC */ .mmu_bm = 0x00002000, .mmu_ctpr_mask = 0xffffffc0, @@ -514,7 +512,7 @@ static const sparc_def_t sparc_defs[] = { { .name = "LEON2", .iu_version = 0xf2000000, - .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ + .fpu_version = 4 << FSR_VER_SHIFT, /* FPU version 4 (Meiko) */ .mmu_version = 0xf2000000, .mmu_bm = 0x00004000, .mmu_ctpr_mask = 0x007ffff0, @@ -527,7 +525,7 @@ static const sparc_def_t sparc_defs[] = { { .name = "LEON3", .iu_version = 0xf3000000, - .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ + .fpu_version = 4 << FSR_VER_SHIFT, /* FPU version 4 (Meiko) */ .mmu_version = 0xf3000000, .mmu_bm = 0x00000000, .mmu_ctpr_mask = 0xfffffffc, @@ -542,21 +540,20 @@ static const sparc_def_t sparc_defs[] = { #endif }; +/* This must match sparc_cpu_properties[]. */ static const char * const feature_name[] = { - "float", - "float128", - "swap", - "mul", - "div", - "flush", - "fsqrt", - "fmul", - "vis1", - "vis2", - "fsmuld", - "hypv", - "cmt", - "gl", + [CPU_FEATURE_BIT_FLOAT128] = "float128", +#ifdef TARGET_SPARC64 + [CPU_FEATURE_BIT_CMT] = "cmt", + [CPU_FEATURE_BIT_GL] = "gl", + [CPU_FEATURE_BIT_HYPV] = "hypv", + [CPU_FEATURE_BIT_VIS1] = "vis1", + [CPU_FEATURE_BIT_VIS2] = "vis2", +#else + [CPU_FEATURE_BIT_MUL] = "mul", + [CPU_FEATURE_BIT_DIV] = "div", + [CPU_FEATURE_BIT_FSMULD] = "fsmuld", +#endif }; static void print_features(uint32_t features, const char *prefix) @@ -577,9 +574,10 @@ void sparc_cpu_list(void) { unsigned int i; + qemu_printf("Available CPU types:\n"); for (i = 0; i < ARRAY_SIZE(sparc_defs); i++) { - qemu_printf("Sparc %16s IU " TARGET_FMT_lx - " FPU %08x MMU %08x NWINS %d ", + qemu_printf(" %-20s (IU " TARGET_FMT_lx + " FPU %08x MMU %08x NWINS %d) ", sparc_defs[i].name, sparc_defs[i].iu_version, sparc_defs[i].fpu_version, @@ -614,8 +612,7 @@ static void cpu_print_cc(FILE *f, uint32_t cc) static void sparc_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; + CPUSPARCState *env = cpu_env(cs); int i, x; qemu_fprintf(f, "pc: " TARGET_FMT_lx " npc: " TARGET_FMT_lx "\n", env->pc, @@ -670,8 +667,8 @@ static void sparc_cpu_dump_state(CPUState *cs, FILE *f, int flags) "cleanwin: %d cwp: %d\n", env->cansave, env->canrestore, env->otherwin, env->wstate, env->cleanwin, env->nwindows - 1 - env->cwp); - qemu_fprintf(f, "fsr: " TARGET_FMT_lx " y: " TARGET_FMT_lx " fprs: " - TARGET_FMT_lx "\n", env->fsr, env->y, env->fprs); + qemu_fprintf(f, "fsr: " TARGET_FMT_lx " y: " TARGET_FMT_lx " fprs: %016x\n", + cpu_get_fsr(env), env->y, env->fprs); #else qemu_fprintf(f, "psr: %08x (icc: ", cpu_get_psr(env)); @@ -680,7 +677,7 @@ static void sparc_cpu_dump_state(CPUState *cs, FILE *f, int flags) env->psrps ? 'P' : '-', env->psret ? 'E' : '-', env->wim); qemu_fprintf(f, "fsr: " TARGET_FMT_lx " y: " TARGET_FMT_lx "\n", - env->fsr, env->y); + cpu_get_fsr(env), env->y); #endif qemu_fprintf(f, "\n"); } @@ -693,22 +690,55 @@ static void sparc_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.npc = value + 4; } +static vaddr sparc_cpu_get_pc(CPUState *cs) +{ + SPARCCPU *cpu = SPARC_CPU(cs); + + return cpu->env.pc; +} + static void sparc_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { SPARCCPU *cpu = SPARC_CPU(cs); + tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); cpu->env.pc = tb->pc; cpu->env.npc = tb->cs_base; } static bool sparc_cpu_has_work(CPUState *cs) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; - return (cs->interrupt_request & CPU_INTERRUPT_HARD) && - cpu_interrupts_enabled(env); + cpu_interrupts_enabled(cpu_env(cs)); +} + +static int sparc_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + CPUSPARCState *env = cpu_env(cs); + +#ifndef TARGET_SPARC64 + if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */ + return MMU_PHYS_IDX; + } else { + return env->psrs; + } +#else + /* IMMU or DMMU disabled. */ + if (ifetch + ? (env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0 + : (env->lsu & DMMU_E) == 0) { + return MMU_PHYS_IDX; + } else if (cpu_hypervisor_mode(env)) { + return MMU_PHYS_IDX; + } else if (env->tl > 0) { + return MMU_NUCLEUS_IDX; + } else if (cpu_supervisor_mode(env)) { + return MMU_KERNEL_IDX; + } else { + return MMU_USER_IDX; + } +#endif } static char *sparc_cpu_type_name(const char *cpu_model) @@ -742,17 +772,14 @@ static void sparc_cpu_realizefn(DeviceState *dev, Error **errp) CPUState *cs = CPU(dev); SPARCCPUClass *scc = SPARC_CPU_GET_CLASS(dev); Error *local_err = NULL; - SPARCCPU *cpu = SPARC_CPU(dev); - CPUSPARCState *env = &cpu->env; + CPUSPARCState *env = cpu_env(cs); #if defined(CONFIG_USER_ONLY) - if ((env->def.features & CPU_FEATURE_FLOAT)) { - env->def.features |= CPU_FEATURE_FLOAT128; - } + /* We are emulating the kernel, which will trap and emulate float128. */ + env->def.features |= CPU_FEATURE_FLOAT128; #endif env->version = env->def.iu_version; - env->fsr = env->def.fpu_version; env->nwindows = env->def.nwindows; #if !defined(TARGET_SPARC64) env->mmuregs[0] |= env->def.mmu_version; @@ -764,6 +791,7 @@ static void sparc_cpu_realizefn(DeviceState *dev, Error **errp) env->version |= env->def.maxtl << 8; env->version |= env->def.nwindows - 1; #endif + cpu_put_fsr(env, 0); cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { @@ -782,8 +810,6 @@ static void sparc_cpu_initfn(Object *obj) SPARCCPUClass *scc = SPARC_CPU_GET_CLASS(obj); CPUSPARCState *env = &cpu->env; - cpu_set_cpustate_pointers(cpu); - if (scc->cpu_def) { env->def = *scc->cpu_def; } @@ -826,21 +852,29 @@ static PropertyInfo qdev_prop_nwindows = { .set = sparc_set_nwindows, }; +/* This must match feature_name[]. */ static Property sparc_cpu_properties[] = { - DEFINE_PROP_BIT("float", SPARCCPU, env.def.features, 0, false), - DEFINE_PROP_BIT("float128", SPARCCPU, env.def.features, 1, false), - DEFINE_PROP_BIT("swap", SPARCCPU, env.def.features, 2, false), - DEFINE_PROP_BIT("mul", SPARCCPU, env.def.features, 3, false), - DEFINE_PROP_BIT("div", SPARCCPU, env.def.features, 4, false), - DEFINE_PROP_BIT("flush", SPARCCPU, env.def.features, 5, false), - DEFINE_PROP_BIT("fsqrt", SPARCCPU, env.def.features, 6, false), - DEFINE_PROP_BIT("fmul", SPARCCPU, env.def.features, 7, false), - DEFINE_PROP_BIT("vis1", SPARCCPU, env.def.features, 8, false), - DEFINE_PROP_BIT("vis2", SPARCCPU, env.def.features, 9, false), - DEFINE_PROP_BIT("fsmuld", SPARCCPU, env.def.features, 10, false), - DEFINE_PROP_BIT("hypv", SPARCCPU, env.def.features, 11, false), - DEFINE_PROP_BIT("cmt", SPARCCPU, env.def.features, 12, false), - DEFINE_PROP_BIT("gl", SPARCCPU, env.def.features, 13, false), + DEFINE_PROP_BIT("float128", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_FLOAT128, false), +#ifdef TARGET_SPARC64 + DEFINE_PROP_BIT("cmt", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_CMT, false), + DEFINE_PROP_BIT("gl", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_GL, false), + DEFINE_PROP_BIT("hypv", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_HYPV, false), + DEFINE_PROP_BIT("vis1", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_VIS1, false), + DEFINE_PROP_BIT("vis2", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_VIS2, false), +#else + DEFINE_PROP_BIT("mul", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_MUL, false), + DEFINE_PROP_BIT("div", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_DIV, false), + DEFINE_PROP_BIT("fsmuld", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_FSMULD, false), +#endif DEFINE_PROP_UNSIGNED("iu-version", SPARCCPU, env.def.iu_version, 0, qdev_prop_uint64, target_ulong), DEFINE_PROP_UINT32("fpu-version", SPARCCPU, env.def.fpu_version, 0), @@ -862,9 +896,10 @@ static const struct SysemuCPUOps sparc_sysemu_ops = { #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps sparc_tcg_ops = { +static const TCGCPUOps sparc_tcg_ops = { .initialize = sparc_tcg_init, .synchronize_from_tb = sparc_cpu_synchronize_from_tb, + .restore_state_to_opc = sparc_restore_state_to_opc, #ifndef CONFIG_USER_ONLY .tlb_fill = sparc_cpu_tlb_fill, @@ -881,21 +916,25 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) SPARCCPUClass *scc = SPARC_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); device_class_set_parent_realize(dc, sparc_cpu_realizefn, &scc->parent_realize); device_class_set_props(dc, sparc_cpu_properties); - device_class_set_parent_reset(dc, sparc_cpu_reset, &scc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, sparc_cpu_reset_hold, NULL, + &scc->parent_phases); cc->class_by_name = sparc_cpu_class_by_name; cc->parse_features = sparc_cpu_parse_features; cc->has_work = sparc_cpu_has_work; + cc->mmu_index = sparc_cpu_mmu_index; cc->dump_state = sparc_cpu_dump_state; #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) cc->memory_rw_debug = sparc_cpu_memory_rw_debug; #endif cc->set_pc = sparc_cpu_set_pc; + cc->get_pc = sparc_cpu_get_pc; cc->gdb_read_register = sparc_cpu_gdb_read_register; cc->gdb_write_register = sparc_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY @@ -915,6 +954,7 @@ static const TypeInfo sparc_cpu_type_info = { .name = TYPE_SPARC_CPU, .parent = TYPE_CPU, .instance_size = sizeof(SPARCCPU), + .instance_align = __alignof(SPARCCPU), .instance_init = sparc_cpu_initfn, .abstract = true, .class_size = sizeof(SPARCCPUClass), diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index f80ea2e8cf..f3cdd17c62 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -6,10 +6,35 @@ #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" +/* + * From Oracle SPARC Architecture 2015: + * + * Compatibility notes: The PSO memory model described in SPARC V8 and + * SPARC V9 compatibility architecture specifications was never implemented + * in a SPARC V9 implementation and is not included in the Oracle SPARC + * Architecture specification. + * + * The RMO memory model described in the SPARC V9 specification was + * implemented in some non-Sun SPARC V9 implementations, but is not + * directly supported in Oracle SPARC Architecture 2015 implementations. + * + * Therefore always use TSO in QEMU. + * + * D.5 Specification of Partial Store Order (PSO) + * ... [loads] are followed by an implied MEMBAR #LoadLoad | #LoadStore. + * + * D.6 Specification of Total Store Order (TSO) + * ... PSO with the additional requirement that all [stores] are followed + * by an implied MEMBAR #StoreStore. + */ +#define TCG_GUEST_DEFAULT_MO (TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST) + #if !defined(TARGET_SPARC64) #define TARGET_DPREGS 16 +#define TARGET_FCCREGS 1 #else #define TARGET_DPREGS 32 +#define TARGET_FCCREGS 4 #endif /*#define EXCP_INTERRUPT 0x100*/ @@ -114,32 +139,6 @@ enum { #define PSR_CWP 0x1f #endif -#define CC_SRC (env->cc_src) -#define CC_SRC2 (env->cc_src2) -#define CC_DST (env->cc_dst) -#define CC_OP (env->cc_op) - -/* Even though lazy evaluation of CPU condition codes tends to be less - * important on RISC systems where condition codes are only updated - * when explicitly requested, SPARC uses it to update 32-bit and 64-bit - * condition codes. - */ -enum { - CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ - CC_OP_FLAGS, /* all cc are back in status register */ - CC_OP_DIV, /* modify N, Z and V, C = 0*/ - CC_OP_ADD, /* modify all flags, CC_DST = res, CC_SRC = src1 */ - CC_OP_ADDX, /* modify all flags, CC_DST = res, CC_SRC = src1 */ - CC_OP_TADD, /* modify all flags, CC_DST = res, CC_SRC = src1 */ - CC_OP_TADDTV, /* modify all flags except V, CC_DST = res, CC_SRC = src1 */ - CC_OP_SUB, /* modify all flags, CC_DST = res, CC_SRC = src1 */ - CC_OP_SUBX, /* modify all flags, CC_DST = res, CC_SRC = src1 */ - CC_OP_TSUB, /* modify all flags, CC_DST = res, CC_SRC = src1 */ - CC_OP_TSUBTV, /* modify all flags except V, CC_DST = res, CC_SRC = src1 */ - CC_OP_LOGIC, /* modify N and Z, C = V = 0, CC_DST = res */ - CC_OP_NB, -}; - /* Trap base register */ #define TBR_BASE_MASK 0xfffff000 @@ -179,6 +178,7 @@ enum { #define FSR_DZM (1ULL << 24) #define FSR_NXM (1ULL << 23) #define FSR_TEM_MASK (FSR_NVM | FSR_OFM | FSR_UFM | FSR_DZM | FSR_NXM) +#define FSR_TEM_SHIFT 23 #define FSR_NVA (1ULL << 9) #define FSR_OFA (1ULL << 8) @@ -186,6 +186,7 @@ enum { #define FSR_DZA (1ULL << 6) #define FSR_NXA (1ULL << 5) #define FSR_AEXC_MASK (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA) +#define FSR_AEXC_SHIFT 5 #define FSR_NVC (1ULL << 4) #define FSR_OFC (1ULL << 3) @@ -194,32 +195,22 @@ enum { #define FSR_NXC (1ULL << 0) #define FSR_CEXC_MASK (FSR_NVC | FSR_OFC | FSR_UFC | FSR_DZC | FSR_NXC) +#define FSR_VER_SHIFT 17 +#define FSR_VER_MASK (7 << FSR_VER_SHIFT) + #define FSR_FTT2 (1ULL << 16) #define FSR_FTT1 (1ULL << 15) #define FSR_FTT0 (1ULL << 14) -//gcc warns about constant overflow for ~FSR_FTT_MASK -//#define FSR_FTT_MASK (FSR_FTT2 | FSR_FTT1 | FSR_FTT0) -#ifdef TARGET_SPARC64 -#define FSR_FTT_NMASK 0xfffffffffffe3fffULL -#define FSR_FTT_CEXC_NMASK 0xfffffffffffe3fe0ULL -#define FSR_LDFSR_OLDMASK 0x0000003f000fc000ULL -#define FSR_LDXFSR_MASK 0x0000003fcfc00fffULL -#define FSR_LDXFSR_OLDMASK 0x00000000000fc000ULL -#else -#define FSR_FTT_NMASK 0xfffe3fffULL -#define FSR_FTT_CEXC_NMASK 0xfffe3fe0ULL -#define FSR_LDFSR_OLDMASK 0x000fc000ULL -#endif -#define FSR_LDFSR_MASK 0xcfc00fffULL +#define FSR_FTT_MASK (FSR_FTT2 | FSR_FTT1 | FSR_FTT0) #define FSR_FTT_IEEE_EXCP (1ULL << 14) #define FSR_FTT_UNIMPFPOP (3ULL << 14) #define FSR_FTT_SEQ_ERROR (4ULL << 14) #define FSR_FTT_INVAL_FPR (6ULL << 14) -#define FSR_FCC1_SHIFT 11 -#define FSR_FCC1 (1ULL << FSR_FCC1_SHIFT) -#define FSR_FCC0_SHIFT 10 -#define FSR_FCC0 (1ULL << FSR_FCC0_SHIFT) +#define FSR_FCC0_SHIFT 10 +#define FSR_FCC1_SHIFT 32 +#define FSR_FCC2_SHIFT 34 +#define FSR_FCC3_SHIFT 36 /* MMU */ #define MMU_E (1<<0) @@ -253,7 +244,7 @@ typedef struct trap_state { #endif #define TARGET_INSN_START_EXTRA_WORDS 1 -struct sparc_def_t { +typedef struct sparc_def_t { const char *name; target_ulong iu_version; uint32_t fpu_version; @@ -267,40 +258,29 @@ struct sparc_def_t { uint32_t features; uint32_t nwindows; uint32_t maxtl; +} sparc_def_t; + +#define FEATURE(X) CPU_FEATURE_BIT_##X, +enum { +#include "cpu-feature.h.inc" }; -#define CPU_FEATURE_FLOAT (1 << 0) -#define CPU_FEATURE_FLOAT128 (1 << 1) -#define CPU_FEATURE_SWAP (1 << 2) -#define CPU_FEATURE_MUL (1 << 3) -#define CPU_FEATURE_DIV (1 << 4) -#define CPU_FEATURE_FLUSH (1 << 5) -#define CPU_FEATURE_FSQRT (1 << 6) -#define CPU_FEATURE_FMUL (1 << 7) -#define CPU_FEATURE_VIS1 (1 << 8) -#define CPU_FEATURE_VIS2 (1 << 9) -#define CPU_FEATURE_FSMULD (1 << 10) -#define CPU_FEATURE_HYPV (1 << 11) -#define CPU_FEATURE_CMT (1 << 12) -#define CPU_FEATURE_GL (1 << 13) -#define CPU_FEATURE_TA0_SHUTDOWN (1 << 14) /* Shutdown on "ta 0x0" */ -#define CPU_FEATURE_ASR17 (1 << 15) -#define CPU_FEATURE_CACHE_CTRL (1 << 16) -#define CPU_FEATURE_POWERDOWN (1 << 17) -#define CPU_FEATURE_CASA (1 << 18) +#undef FEATURE +#define FEATURE(X) CPU_FEATURE_##X = 1u << CPU_FEATURE_BIT_##X, + +enum { +#include "cpu-feature.h.inc" +}; + +#undef FEATURE #ifndef TARGET_SPARC64 -#define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \ - CPU_FEATURE_MUL | CPU_FEATURE_DIV | \ - CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT | \ - CPU_FEATURE_FMUL | CPU_FEATURE_FSMULD) +#define CPU_DEFAULT_FEATURES (CPU_FEATURE_MUL | CPU_FEATURE_DIV | \ + CPU_FEATURE_FSMULD) #else -#define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \ - CPU_FEATURE_MUL | CPU_FEATURE_DIV | \ - CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT | \ - CPU_FEATURE_FMUL | CPU_FEATURE_VIS1 | \ - CPU_FEATURE_VIS2 | CPU_FEATURE_FSMULD | \ - CPU_FEATURE_CASA) +#define CPU_DEFAULT_FEATURES (CPU_FEATURE_MUL | CPU_FEATURE_DIV | \ + CPU_FEATURE_FSMULD | CPU_FEATURE_CASA | \ + CPU_FEATURE_VIS1 | CPU_FEATURE_VIS2) enum { mmu_us_12, // Ultrasparc < III (64 entry TLB) mmu_us_3, // Ultrasparc III (512 entry TLB) @@ -447,16 +427,40 @@ struct CPUArchState { target_ulong npc; /* next program counter */ target_ulong y; /* multiply/divide register */ - /* emulator internal flags handling */ - target_ulong cc_src, cc_src2; - target_ulong cc_dst; - uint32_t cc_op; + /* + * Bit 31 is for icc, bit 63 for xcc. + * Other bits are garbage. + */ + target_long cc_N; + target_long cc_V; + + /* + * Z is represented as == 0; any non-zero value is !Z. + * For sparc64, the high 32-bits of icc.Z are garbage. + */ + target_ulong icc_Z; +#ifdef TARGET_SPARC64 + target_ulong xcc_Z; +#endif + + /* + * For sparc32, icc.C is boolean. + * For sparc64, xcc.C is boolean; + * icc.C is bit 32 with other bits garbage. + */ + target_ulong icc_C; +#ifdef TARGET_SPARC64 + target_ulong xcc_C; +#endif target_ulong cond; /* conditional branch result (XXX: save it in a temporary register when possible) */ - uint32_t psr; /* processor state register */ - target_ulong fsr; /* FPU state register */ + /* FPU State Register, in parts */ + uint32_t fsr; /* rm, tem, aexc */ + uint32_t fsr_cexc_ftt; /* cexc, ftt */ + uint32_t fcc[TARGET_FCCREGS]; /* fcc* */ + CPU_DoubleU fpr[TARGET_DPREGS]; /* floating point registers */ uint32_t cwp; /* index of current register window (extracted from PSR) */ @@ -504,14 +508,11 @@ struct CPUArchState { uint64_t mmubpregs[4]; uint64_t prom_addr; #endif - /* temporary float registers */ - float128 qt0, qt1; float_status fp_status; #if defined(TARGET_SPARC64) #define MAXTL_MAX 8 #define MAXTL_MASK (MAXTL_MAX - 1) trap_state ts[MAXTL_MAX]; - uint32_t xcc; /* Extended integer condition codes */ uint32_t asi; uint32_t pstate; uint32_t tl; @@ -522,7 +523,7 @@ struct CPUArchState { uint64_t igregs[8]; /* interrupt general registers */ uint64_t mgregs[8]; /* mmu general registers */ uint64_t glregs[8 * MAXTL_MAX]; - uint64_t fprs; + uint32_t fprs; uint64_t tick_cmpr, stick_cmpr; CPUTimer *tick, *stick; #define TICK_NPT_MASK 0x8000000000000000ULL @@ -544,10 +545,9 @@ struct CPUArchState { #endif sparc_def_t def; - void *irq_manager; - void (*qemu_irq_ack)(CPUSPARCState *env, void *irq_manager, int intno); - - /* Leon3 cache control */ + /* Leon3 */ + DeviceState *irq_manager; + void (*qemu_irq_ack)(CPUSPARCState *env, int intno); uint32_t cache_control; }; @@ -558,21 +558,33 @@ struct CPUArchState { * A SPARC CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUSPARCState env; }; +/** + * SPARCCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * A SPARC CPU model. + */ +struct SPARCCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; + sparc_def_t *cpu_def; +}; #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_sparc_cpu; + +hwaddr sparc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); #endif void sparc_cpu_do_interrupt(CPUState *cpu); -hwaddr sparc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int sparc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int sparc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); G_NORETURN void sparc_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, @@ -581,7 +593,6 @@ G_NORETURN void sparc_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, uintptr_t retaddr); G_NORETURN void cpu_raise_exception_ra(CPUSPARCState *, int, uintptr_t); -#ifndef NO_CPU_IO_DEFS /* cpu_init.c */ void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu); void sparc_cpu_list(void); @@ -600,12 +611,18 @@ int sparc_cpu_memory_rw_debug(CPUState *cpu, vaddr addr, /* translate.c */ void sparc_tcg_init(void); +void sparc_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data); -/* cpu-exec.c */ +/* fop_helper.c */ +target_ulong cpu_get_fsr(CPUSPARCState *); +void cpu_put_fsr(CPUSPARCState *, target_ulong); /* win_helper.c */ target_ulong cpu_get_psr(CPUSPARCState *env1); void cpu_put_psr(CPUSPARCState *env1, target_ulong val); +void cpu_put_psr_icc(CPUSPARCState *env1, target_ulong val); void cpu_put_psr_raw(CPUSPARCState *env1, target_ulong val); #ifdef TARGET_SPARC64 void cpu_change_pstate(CPUSPARCState *env1, uint32_t new_pstate); @@ -634,7 +651,6 @@ static inline int tlb_compare_context(const SparcTLBEntry *tlb, return compare_masked(context, tlb->tag, MMU_CONTEXT_MASK); } -#endif #endif /* cpu-exec.c */ @@ -650,8 +666,6 @@ hwaddr cpu_get_phys_page_nofault(CPUSPARCState *env, target_ulong addr, #endif #endif -#define SPARC_CPU_TYPE_SUFFIX "-" TYPE_SPARC_CPU -#define SPARC_CPU_TYPE_NAME(model) model SPARC_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_SPARC_CPU #define cpu_list sparc_cpu_list @@ -692,34 +706,6 @@ static inline int cpu_supervisor_mode(CPUSPARCState *env1) } #endif -static inline int cpu_mmu_index(CPUSPARCState *env, bool ifetch) -{ -#if defined(CONFIG_USER_ONLY) - return MMU_USER_IDX; -#elif !defined(TARGET_SPARC64) - if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */ - return MMU_PHYS_IDX; - } else { - return env->psrs; - } -#else - /* IMMU or DMMU disabled. */ - if (ifetch - ? (env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0 - : (env->lsu & DMMU_E) == 0) { - return MMU_PHYS_IDX; - } else if (cpu_hypervisor_mode(env)) { - return MMU_PHYS_IDX; - } else if (env->tl > 0) { - return MMU_NUCLEUS_IDX; - } else if (cpu_supervisor_mode(env)) { - return MMU_KERNEL_IDX; - } else { - return MMU_USER_IDX; - } -#endif -} - static inline int cpu_interrupts_enabled(CPUSPARCState *env1) { #if !defined (TARGET_SPARC64) @@ -761,13 +747,13 @@ trap_state* cpu_tsptr(CPUSPARCState* env); #define TB_FLAG_HYPER (1 << 7) #define TB_FLAG_ASI_SHIFT 24 -static inline void cpu_get_tb_cpu_state(CPUSPARCState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *pflags) +static inline void cpu_get_tb_cpu_state(CPUSPARCState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) { uint32_t flags; *pc = env->pc; *cs_base = env->npc; - flags = cpu_mmu_index(env, false); + flags = cpu_mmu_index(env_cpu(env), false); #ifndef CONFIG_USER_ONLY if (cpu_supervisor_mode(env)) { flags |= TB_FLAG_SUPER; @@ -782,14 +768,12 @@ static inline void cpu_get_tb_cpu_state(CPUSPARCState *env, target_ulong *pc, if (env->pstate & PS_AM) { flags |= TB_FLAG_AM_ENABLED; } - if ((env->def.features & CPU_FEATURE_FLOAT) - && (env->pstate & PS_PEF) - && (env->fprs & FPRS_FEF)) { + if ((env->pstate & PS_PEF) && (env->fprs & FPRS_FEF)) { flags |= TB_FLAG_FPU_ENABLED; } flags |= env->asi << TB_FLAG_ASI_SHIFT; #else - if ((env->def.features & CPU_FEATURE_FLOAT) && env->psref) { + if (env->psref) { flags |= TB_FLAG_FPU_ENABLED; } #endif diff --git a/target/sparc/fop_helper.c b/target/sparc/fop_helper.c index f54fa9b959..1205a599ef 100644 --- a/target/sparc/fop_helper.c +++ b/target/sparc/fop_helper.c @@ -23,13 +23,32 @@ #include "exec/helper-proto.h" #include "fpu/softfloat.h" -#define QT0 (env->qt0) -#define QT1 (env->qt1) +static inline float128 f128_in(Int128 i) +{ + union { + Int128 i; + float128 f; + } u; -static target_ulong do_check_ieee_exceptions(CPUSPARCState *env, uintptr_t ra) + u.i = i; + return u.f; +} + +static inline Int128 f128_ret(float128 f) +{ + union { + Int128 i; + float128 f; + } u; + + u.f = f; + return u.i; +} + +static void check_ieee_exceptions(CPUSPARCState *env, uintptr_t ra) { target_ulong status = get_float_exception_flags(&env->fp_status); - target_ulong fsr = env->fsr; + uint32_t cexc = 0; if (unlikely(status)) { /* Keep exception flags clear for next time. */ @@ -37,333 +56,384 @@ static target_ulong do_check_ieee_exceptions(CPUSPARCState *env, uintptr_t ra) /* Copy IEEE 754 flags into FSR */ if (status & float_flag_invalid) { - fsr |= FSR_NVC; + cexc |= FSR_NVC; } if (status & float_flag_overflow) { - fsr |= FSR_OFC; + cexc |= FSR_OFC; } if (status & float_flag_underflow) { - fsr |= FSR_UFC; + cexc |= FSR_UFC; } if (status & float_flag_divbyzero) { - fsr |= FSR_DZC; + cexc |= FSR_DZC; } if (status & float_flag_inexact) { - fsr |= FSR_NXC; + cexc |= FSR_NXC; } - if ((fsr & FSR_CEXC_MASK) & ((fsr & FSR_TEM_MASK) >> 23)) { - CPUState *cs = env_cpu(env); - - /* Unmasked exception, generate a trap. Note that while - the helper is marked as NO_WG, we can get away with - writing to cpu state along the exception path, since - TCG generated code will never see the write. */ - env->fsr = fsr | FSR_FTT_IEEE_EXCP; - cs->exception_index = TT_FP_EXCP; - cpu_loop_exit_restore(cs, ra); - } else { - /* Accumulate exceptions */ - fsr |= (fsr & FSR_CEXC_MASK) << 5; + if (cexc & (env->fsr >> FSR_TEM_SHIFT)) { + /* Unmasked exception, generate an IEEE trap. */ + env->fsr_cexc_ftt = cexc | FSR_FTT_IEEE_EXCP; + cpu_raise_exception_ra(env, TT_FP_EXCP, ra); } + + /* Accumulate exceptions */ + env->fsr |= cexc << FSR_AEXC_SHIFT; } - return fsr; + /* No trap, so FTT is cleared. */ + env->fsr_cexc_ftt = cexc; } -target_ulong helper_check_ieee_exceptions(CPUSPARCState *env) +float32 helper_fadds(CPUSPARCState *env, float32 src1, float32 src2) { - return do_check_ieee_exceptions(env, GETPC()); + float32 ret = float32_add(src1, src2, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -#define F_HELPER(name, p) void helper_f##name##p(CPUSPARCState *env) +float32 helper_fsubs(CPUSPARCState *env, float32 src1, float32 src2) +{ + float32 ret = float32_sub(src1, src2, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; +} -#define F_BINOP(name) \ - float32 helper_f ## name ## s (CPUSPARCState *env, float32 src1, \ - float32 src2) \ - { \ - return float32_ ## name (src1, src2, &env->fp_status); \ - } \ - float64 helper_f ## name ## d (CPUSPARCState * env, float64 src1,\ - float64 src2) \ - { \ - return float64_ ## name (src1, src2, &env->fp_status); \ - } \ - F_HELPER(name, q) \ - { \ - QT0 = float128_ ## name (QT0, QT1, &env->fp_status); \ - } +float32 helper_fmuls(CPUSPARCState *env, float32 src1, float32 src2) +{ + float32 ret = float32_mul(src1, src2, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; +} -F_BINOP(add); -F_BINOP(sub); -F_BINOP(mul); -F_BINOP(div); -#undef F_BINOP +float32 helper_fdivs(CPUSPARCState *env, float32 src1, float32 src2) +{ + float32 ret = float32_div(src1, src2, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; +} + +float64 helper_faddd(CPUSPARCState *env, float64 src1, float64 src2) +{ + float64 ret = float64_add(src1, src2, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; +} + +float64 helper_fsubd(CPUSPARCState *env, float64 src1, float64 src2) +{ + float64 ret = float64_sub(src1, src2, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; +} + +float64 helper_fmuld(CPUSPARCState *env, float64 src1, float64 src2) +{ + float64 ret = float64_mul(src1, src2, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; +} + +float64 helper_fdivd(CPUSPARCState *env, float64 src1, float64 src2) +{ + float64 ret = float64_div(src1, src2, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; +} + +Int128 helper_faddq(CPUSPARCState *env, Int128 src1, Int128 src2) +{ + float128 ret = float128_add(f128_in(src1), f128_in(src2), &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return f128_ret(ret); +} + +Int128 helper_fsubq(CPUSPARCState *env, Int128 src1, Int128 src2) +{ + float128 ret = float128_sub(f128_in(src1), f128_in(src2), &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return f128_ret(ret); +} + +Int128 helper_fmulq(CPUSPARCState *env, Int128 src1, Int128 src2) +{ + float128 ret = float128_mul(f128_in(src1), f128_in(src2), &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return f128_ret(ret); +} + +Int128 helper_fdivq(CPUSPARCState *env, Int128 src1, Int128 src2) +{ + float128 ret = float128_div(f128_in(src1), f128_in(src2), &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return f128_ret(ret); +} float64 helper_fsmuld(CPUSPARCState *env, float32 src1, float32 src2) { - return float64_mul(float32_to_float64(src1, &env->fp_status), - float32_to_float64(src2, &env->fp_status), - &env->fp_status); + float64 ret = float64_mul(float32_to_float64(src1, &env->fp_status), + float32_to_float64(src2, &env->fp_status), + &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -void helper_fdmulq(CPUSPARCState *env, float64 src1, float64 src2) +Int128 helper_fdmulq(CPUSPARCState *env, float64 src1, float64 src2) { - QT0 = float128_mul(float64_to_float128(src1, &env->fp_status), - float64_to_float128(src2, &env->fp_status), - &env->fp_status); + float128 ret = float128_mul(float64_to_float128(src1, &env->fp_status), + float64_to_float128(src2, &env->fp_status), + &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return f128_ret(ret); } -float32 helper_fnegs(float32 src) -{ - return float32_chs(src); -} - -#ifdef TARGET_SPARC64 -float64 helper_fnegd(float64 src) -{ - return float64_chs(src); -} - -F_HELPER(neg, q) -{ - QT0 = float128_chs(QT1); -} -#endif - /* Integer to float conversion. */ float32 helper_fitos(CPUSPARCState *env, int32_t src) { - return int32_to_float32(src, &env->fp_status); + float32 ret = int32_to_float32(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } float64 helper_fitod(CPUSPARCState *env, int32_t src) { - return int32_to_float64(src, &env->fp_status); + float64 ret = int32_to_float64(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -void helper_fitoq(CPUSPARCState *env, int32_t src) +Int128 helper_fitoq(CPUSPARCState *env, int32_t src) { - QT0 = int32_to_float128(src, &env->fp_status); + float128 ret = int32_to_float128(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return f128_ret(ret); } #ifdef TARGET_SPARC64 float32 helper_fxtos(CPUSPARCState *env, int64_t src) { - return int64_to_float32(src, &env->fp_status); + float32 ret = int64_to_float32(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } float64 helper_fxtod(CPUSPARCState *env, int64_t src) { - return int64_to_float64(src, &env->fp_status); + float64 ret = int64_to_float64(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -void helper_fxtoq(CPUSPARCState *env, int64_t src) +Int128 helper_fxtoq(CPUSPARCState *env, int64_t src) { - QT0 = int64_to_float128(src, &env->fp_status); + float128 ret = int64_to_float128(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return f128_ret(ret); } #endif -#undef F_HELPER /* floating point conversion */ float32 helper_fdtos(CPUSPARCState *env, float64 src) { - return float64_to_float32(src, &env->fp_status); + float32 ret = float64_to_float32(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } float64 helper_fstod(CPUSPARCState *env, float32 src) { - return float32_to_float64(src, &env->fp_status); + float64 ret = float32_to_float64(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -float32 helper_fqtos(CPUSPARCState *env) +float32 helper_fqtos(CPUSPARCState *env, Int128 src) { - return float128_to_float32(QT1, &env->fp_status); + float32 ret = float128_to_float32(f128_in(src), &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -void helper_fstoq(CPUSPARCState *env, float32 src) +Int128 helper_fstoq(CPUSPARCState *env, float32 src) { - QT0 = float32_to_float128(src, &env->fp_status); + float128 ret = float32_to_float128(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return f128_ret(ret); } -float64 helper_fqtod(CPUSPARCState *env) +float64 helper_fqtod(CPUSPARCState *env, Int128 src) { - return float128_to_float64(QT1, &env->fp_status); + float64 ret = float128_to_float64(f128_in(src), &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -void helper_fdtoq(CPUSPARCState *env, float64 src) +Int128 helper_fdtoq(CPUSPARCState *env, float64 src) { - QT0 = float64_to_float128(src, &env->fp_status); + float128 ret = float64_to_float128(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return f128_ret(ret); } /* Float to integer conversion. */ int32_t helper_fstoi(CPUSPARCState *env, float32 src) { - return float32_to_int32_round_to_zero(src, &env->fp_status); + int32_t ret = float32_to_int32_round_to_zero(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } int32_t helper_fdtoi(CPUSPARCState *env, float64 src) { - return float64_to_int32_round_to_zero(src, &env->fp_status); + int32_t ret = float64_to_int32_round_to_zero(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -int32_t helper_fqtoi(CPUSPARCState *env) +int32_t helper_fqtoi(CPUSPARCState *env, Int128 src) { - return float128_to_int32_round_to_zero(QT1, &env->fp_status); + int32_t ret = float128_to_int32_round_to_zero(f128_in(src), + &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } #ifdef TARGET_SPARC64 int64_t helper_fstox(CPUSPARCState *env, float32 src) { - return float32_to_int64_round_to_zero(src, &env->fp_status); + int64_t ret = float32_to_int64_round_to_zero(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } int64_t helper_fdtox(CPUSPARCState *env, float64 src) { - return float64_to_int64_round_to_zero(src, &env->fp_status); + int64_t ret = float64_to_int64_round_to_zero(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -int64_t helper_fqtox(CPUSPARCState *env) +int64_t helper_fqtox(CPUSPARCState *env, Int128 src) { - return float128_to_int64_round_to_zero(QT1, &env->fp_status); -} -#endif - -float32 helper_fabss(float32 src) -{ - return float32_abs(src); -} - -#ifdef TARGET_SPARC64 -float64 helper_fabsd(float64 src) -{ - return float64_abs(src); -} - -void helper_fabsq(CPUSPARCState *env) -{ - QT0 = float128_abs(QT1); + int64_t ret = float128_to_int64_round_to_zero(f128_in(src), + &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } #endif float32 helper_fsqrts(CPUSPARCState *env, float32 src) { - return float32_sqrt(src, &env->fp_status); + float32 ret = float32_sqrt(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } float64 helper_fsqrtd(CPUSPARCState *env, float64 src) { - return float64_sqrt(src, &env->fp_status); + float64 ret = float64_sqrt(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -void helper_fsqrtq(CPUSPARCState *env) +Int128 helper_fsqrtq(CPUSPARCState *env, Int128 src) { - QT0 = float128_sqrt(QT1, &env->fp_status); + float128 ret = float128_sqrt(f128_in(src), &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return f128_ret(ret); } -#define GEN_FCMP(name, size, reg1, reg2, FS, E) \ - target_ulong glue(helper_, name) (CPUSPARCState *env) \ - { \ - FloatRelation ret; \ - target_ulong fsr; \ - if (E) { \ - ret = glue(size, _compare)(reg1, reg2, &env->fp_status); \ - } else { \ - ret = glue(size, _compare_quiet)(reg1, reg2, \ - &env->fp_status); \ - } \ - fsr = do_check_ieee_exceptions(env, GETPC()); \ - switch (ret) { \ - case float_relation_unordered: \ - fsr |= (FSR_FCC1 | FSR_FCC0) << FS; \ - fsr |= FSR_NVA; \ - break; \ - case float_relation_less: \ - fsr &= ~(FSR_FCC1) << FS; \ - fsr |= FSR_FCC0 << FS; \ - break; \ - case float_relation_greater: \ - fsr &= ~(FSR_FCC0) << FS; \ - fsr |= FSR_FCC1 << FS; \ - break; \ - default: \ - fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ - break; \ - } \ - return fsr; \ - } -#define GEN_FCMP_T(name, size, FS, E) \ - target_ulong glue(helper_, name)(CPUSPARCState *env, size src1, size src2)\ - { \ - FloatRelation ret; \ - target_ulong fsr; \ - if (E) { \ - ret = glue(size, _compare)(src1, src2, &env->fp_status); \ - } else { \ - ret = glue(size, _compare_quiet)(src1, src2, \ - &env->fp_status); \ - } \ - fsr = do_check_ieee_exceptions(env, GETPC()); \ - switch (ret) { \ - case float_relation_unordered: \ - fsr |= (FSR_FCC1 | FSR_FCC0) << FS; \ - break; \ - case float_relation_less: \ - fsr &= ~(FSR_FCC1 << FS); \ - fsr |= FSR_FCC0 << FS; \ - break; \ - case float_relation_greater: \ - fsr &= ~(FSR_FCC0 << FS); \ - fsr |= FSR_FCC1 << FS; \ - break; \ - default: \ - fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ - break; \ - } \ - return fsr; \ +static uint32_t finish_fcmp(CPUSPARCState *env, FloatRelation r, uintptr_t ra) +{ + check_ieee_exceptions(env, ra); + + /* + * FCC values: + * 0 = + * 1 < + * 2 > + * 3 unordered + */ + switch (r) { + case float_relation_equal: + return 0; + case float_relation_less: + return 1; + case float_relation_greater: + return 2; + case float_relation_unordered: + env->fsr |= FSR_NVA; + return 3; } + g_assert_not_reached(); +} -GEN_FCMP_T(fcmps, float32, 0, 0); -GEN_FCMP_T(fcmpd, float64, 0, 0); +uint32_t helper_fcmps(CPUSPARCState *env, float32 src1, float32 src2) +{ + FloatRelation r = float32_compare_quiet(src1, src2, &env->fp_status); + return finish_fcmp(env, r, GETPC()); +} -GEN_FCMP_T(fcmpes, float32, 0, 1); -GEN_FCMP_T(fcmped, float64, 0, 1); +uint32_t helper_fcmpes(CPUSPARCState *env, float32 src1, float32 src2) +{ + FloatRelation r = float32_compare(src1, src2, &env->fp_status); + return finish_fcmp(env, r, GETPC()); +} -GEN_FCMP(fcmpq, float128, QT0, QT1, 0, 0); -GEN_FCMP(fcmpeq, float128, QT0, QT1, 0, 1); +uint32_t helper_fcmpd(CPUSPARCState *env, float64 src1, float64 src2) +{ + FloatRelation r = float64_compare_quiet(src1, src2, &env->fp_status); + return finish_fcmp(env, r, GETPC()); +} +uint32_t helper_fcmped(CPUSPARCState *env, float64 src1, float64 src2) +{ + FloatRelation r = float64_compare(src1, src2, &env->fp_status); + return finish_fcmp(env, r, GETPC()); +} + +uint32_t helper_fcmpq(CPUSPARCState *env, Int128 src1, Int128 src2) +{ + FloatRelation r = float128_compare_quiet(f128_in(src1), f128_in(src2), + &env->fp_status); + return finish_fcmp(env, r, GETPC()); +} + +uint32_t helper_fcmpeq(CPUSPARCState *env, Int128 src1, Int128 src2) +{ + FloatRelation r = float128_compare(f128_in(src1), f128_in(src2), + &env->fp_status); + return finish_fcmp(env, r, GETPC()); +} + +target_ulong cpu_get_fsr(CPUSPARCState *env) +{ + target_ulong fsr = env->fsr | env->fsr_cexc_ftt; + + fsr |= env->fcc[0] << FSR_FCC0_SHIFT; #ifdef TARGET_SPARC64 -GEN_FCMP_T(fcmps_fcc1, float32, 22, 0); -GEN_FCMP_T(fcmpd_fcc1, float64, 22, 0); -GEN_FCMP(fcmpq_fcc1, float128, QT0, QT1, 22, 0); - -GEN_FCMP_T(fcmps_fcc2, float32, 24, 0); -GEN_FCMP_T(fcmpd_fcc2, float64, 24, 0); -GEN_FCMP(fcmpq_fcc2, float128, QT0, QT1, 24, 0); - -GEN_FCMP_T(fcmps_fcc3, float32, 26, 0); -GEN_FCMP_T(fcmpd_fcc3, float64, 26, 0); -GEN_FCMP(fcmpq_fcc3, float128, QT0, QT1, 26, 0); - -GEN_FCMP_T(fcmpes_fcc1, float32, 22, 1); -GEN_FCMP_T(fcmped_fcc1, float64, 22, 1); -GEN_FCMP(fcmpeq_fcc1, float128, QT0, QT1, 22, 1); - -GEN_FCMP_T(fcmpes_fcc2, float32, 24, 1); -GEN_FCMP_T(fcmped_fcc2, float64, 24, 1); -GEN_FCMP(fcmpeq_fcc2, float128, QT0, QT1, 24, 1); - -GEN_FCMP_T(fcmpes_fcc3, float32, 26, 1); -GEN_FCMP_T(fcmped_fcc3, float64, 26, 1); -GEN_FCMP(fcmpeq_fcc3, float128, QT0, QT1, 26, 1); + fsr |= (uint64_t)env->fcc[1] << FSR_FCC1_SHIFT; + fsr |= (uint64_t)env->fcc[2] << FSR_FCC2_SHIFT; + fsr |= (uint64_t)env->fcc[3] << FSR_FCC3_SHIFT; #endif -#undef GEN_FCMP_T -#undef GEN_FCMP -static void set_fsr(CPUSPARCState *env, target_ulong fsr) + /* VER is kept completely separate until re-assembly. */ + fsr |= env->def.fpu_version; + + return fsr; +} + +target_ulong helper_get_fsr(CPUSPARCState *env) +{ + return cpu_get_fsr(env); +} + +static void set_fsr_nonsplit(CPUSPARCState *env, target_ulong fsr) { int rnd_mode; + env->fsr = fsr & (FSR_RD_MASK | FSR_TEM_MASK | FSR_AEXC_MASK); + switch (fsr & FSR_RD_MASK) { case FSR_RD_NEAREST: rnd_mode = float_round_nearest_even; @@ -382,20 +452,23 @@ static void set_fsr(CPUSPARCState *env, target_ulong fsr) set_float_rounding_mode(rnd_mode, &env->fp_status); } -target_ulong helper_ldfsr(CPUSPARCState *env, target_ulong old_fsr, - uint32_t new_fsr) +void cpu_put_fsr(CPUSPARCState *env, target_ulong fsr) { - old_fsr = (new_fsr & FSR_LDFSR_MASK) | (old_fsr & FSR_LDFSR_OLDMASK); - set_fsr(env, old_fsr); - return old_fsr; + env->fsr_cexc_ftt = fsr & (FSR_CEXC_MASK | FSR_FTT_MASK); + + env->fcc[0] = extract32(fsr, FSR_FCC0_SHIFT, 2); +#ifdef TARGET_SPARC64 + env->fcc[1] = extract64(fsr, FSR_FCC1_SHIFT, 2); + env->fcc[2] = extract64(fsr, FSR_FCC2_SHIFT, 2); + env->fcc[3] = extract64(fsr, FSR_FCC3_SHIFT, 2); +#endif + + set_fsr_nonsplit(env, fsr); } -#ifdef TARGET_SPARC64 -target_ulong helper_ldxfsr(CPUSPARCState *env, target_ulong old_fsr, - uint64_t new_fsr) +void helper_set_fsr_nofcc_noftt(CPUSPARCState *env, uint32_t fsr) { - old_fsr = (new_fsr & FSR_LDXFSR_MASK) | (old_fsr & FSR_LDXFSR_OLDMASK); - set_fsr(env, old_fsr); - return old_fsr; + env->fsr_cexc_ftt &= FSR_FTT_MASK; + env->fsr_cexc_ftt |= fsr & FSR_CEXC_MASK; + set_fsr_nonsplit(env, fsr); } -#endif diff --git a/target/sparc/gdbstub.c b/target/sparc/gdbstub.c index 5d1e808e8c..07ea81ab5f 100644 --- a/target/sparc/gdbstub.c +++ b/target/sparc/gdbstub.c @@ -19,7 +19,7 @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #ifdef TARGET_ABI32 #define gdb_get_rega(buf, val) gdb_get_reg32(buf, val) @@ -29,8 +29,7 @@ int sparc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; + CPUSPARCState *env = cpu_env(cs); if (n < 8) { /* g0..g7 */ @@ -64,7 +63,7 @@ int sparc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) case 69: return gdb_get_rega(mem_buf, env->npc); case 70: - return gdb_get_rega(mem_buf, env->fsr); + return gdb_get_rega(mem_buf, cpu_get_fsr(env)); case 71: return gdb_get_rega(mem_buf, 0); /* csr */ default: @@ -94,7 +93,7 @@ int sparc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) ((env->pstate & 0xfff) << 8) | cpu_get_cwp64(env)); case 83: - return gdb_get_regl(mem_buf, env->fsr); + return gdb_get_regl(mem_buf, cpu_get_fsr(env)); case 84: return gdb_get_regl(mem_buf, env->fprs); case 85: @@ -156,7 +155,7 @@ int sparc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->npc = tmp; break; case 70: - env->fsr = tmp; + cpu_put_fsr(env, tmp); break; default: return 0; @@ -191,7 +190,7 @@ int sparc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) cpu_put_cwp64(env, tmp & 0xff); break; case 83: - env->fsr = tmp; + cpu_put_fsr(env, tmp); break; case 84: env->fprs = tmp; diff --git a/target/sparc/helper.c b/target/sparc/helper.c index c4358bba84..2247e243b5 100644 --- a/target/sparc/helper.c +++ b/target/sparc/helper.c @@ -81,113 +81,58 @@ void helper_tick_set_limit(void *opaque, uint64_t limit) } #endif -static target_ulong do_udiv(CPUSPARCState *env, target_ulong a, - target_ulong b, int cc, uintptr_t ra) +uint64_t helper_udiv(CPUSPARCState *env, target_ulong a, target_ulong b) { - int overflow = 0; - uint64_t x0; - uint32_t x1; + uint64_t a64 = (uint32_t)a | ((uint64_t)env->y << 32); + uint32_t b32 = b; + uint32_t r; - x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32); - x1 = (b & 0xffffffff); - - if (x1 == 0) { - cpu_raise_exception_ra(env, TT_DIV_ZERO, ra); - } - - x0 = x0 / x1; - if (x0 > UINT32_MAX) { - x0 = UINT32_MAX; - overflow = 1; - } - - if (cc) { - env->cc_dst = x0; - env->cc_src2 = overflow; - env->cc_op = CC_OP_DIV; - } - return x0; -} - -target_ulong helper_udiv(CPUSPARCState *env, target_ulong a, target_ulong b) -{ - return do_udiv(env, a, b, 0, GETPC()); -} - -target_ulong helper_udiv_cc(CPUSPARCState *env, target_ulong a, target_ulong b) -{ - return do_udiv(env, a, b, 1, GETPC()); -} - -static target_ulong do_sdiv(CPUSPARCState *env, target_ulong a, - target_ulong b, int cc, uintptr_t ra) -{ - int overflow = 0; - int64_t x0; - int32_t x1; - - x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32); - x1 = (b & 0xffffffff); - - if (x1 == 0) { - cpu_raise_exception_ra(env, TT_DIV_ZERO, ra); - } else if (x1 == -1 && x0 == INT64_MIN) { - x0 = INT32_MAX; - overflow = 1; - } else { - x0 = x0 / x1; - if ((int32_t) x0 != x0) { - x0 = x0 < 0 ? INT32_MIN : INT32_MAX; - overflow = 1; - } - } - - if (cc) { - env->cc_dst = x0; - env->cc_src2 = overflow; - env->cc_op = CC_OP_DIV; - } - return x0; -} - -target_ulong helper_sdiv(CPUSPARCState *env, target_ulong a, target_ulong b) -{ - return do_sdiv(env, a, b, 0, GETPC()); -} - -target_ulong helper_sdiv_cc(CPUSPARCState *env, target_ulong a, target_ulong b) -{ - return do_sdiv(env, a, b, 1, GETPC()); -} - -#ifdef TARGET_SPARC64 -int64_t helper_sdivx(CPUSPARCState *env, int64_t a, int64_t b) -{ - if (b == 0) { - /* Raise divide by zero trap. */ - cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC()); - } else if (b == -1) { - /* Avoid overflow trap with i386 divide insn. */ - return -a; - } else { - return a / b; - } -} - -uint64_t helper_udivx(CPUSPARCState *env, uint64_t a, uint64_t b) -{ - if (b == 0) { - /* Raise divide by zero trap. */ + if (b32 == 0) { cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC()); } - return a / b; + + a64 /= b32; + r = a64; + if (unlikely(a64 > UINT32_MAX)) { + return -1; /* r = UINT32_MAX, v = 1 */ + } + return r; +} + +uint64_t helper_sdiv(CPUSPARCState *env, target_ulong a, target_ulong b) +{ + int64_t a64 = (uint32_t)a | ((uint64_t)env->y << 32); + int32_t b32 = b; + int32_t r; + + if (b32 == 0) { + cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC()); + } + + if (unlikely(a64 == INT64_MIN)) { + /* + * Special case INT64_MIN / -1 is required to avoid trap on x86 host. + * However, with a dividend of INT64_MIN, there is no 32-bit divisor + * which can yield a 32-bit result: + * INT64_MIN / INT32_MIN = 0x1_0000_0000 + * INT64_MIN / INT32_MAX = -0x1_0000_0002 + * Therefore we know we must overflow and saturate. + */ + return (uint32_t)(b32 < 0 ? INT32_MAX : INT32_MIN) | (-1ull << 32); + } + + a64 /= b; + r = a64; + if (unlikely(r != a64)) { + return (uint32_t)(a64 < 0 ? INT32_MIN : INT32_MAX) | (-1ull << 32); + } + return (uint32_t)r; } -#endif target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1, target_ulong src2) { - target_ulong dst; + target_ulong dst, v; /* Tag overflow occurs if either input has bits 0 or 1 set. */ if ((src1 | src2) & 3) { @@ -197,15 +142,23 @@ target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1, dst = src1 + src2; /* Tag overflow occurs if the addition overflows. */ - if (~(src1 ^ src2) & (src1 ^ dst) & (1u << 31)) { + v = ~(src1 ^ src2) & (src1 ^ dst); + if (v & (1u << 31)) { goto tag_overflow; } /* Only modify the CC after any exceptions have been generated. */ - env->cc_op = CC_OP_TADDTV; - env->cc_src = src1; - env->cc_src2 = src2; - env->cc_dst = dst; + env->cc_V = v; + env->cc_N = dst; + env->icc_Z = dst; +#ifdef TARGET_SPARC64 + env->xcc_Z = dst; + env->icc_C = dst ^ src1 ^ src2; + env->xcc_C = dst < src1; +#else + env->icc_C = dst < src1; +#endif + return dst; tag_overflow: @@ -215,7 +168,7 @@ target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1, target_ulong helper_tsubcctv(CPUSPARCState *env, target_ulong src1, target_ulong src2) { - target_ulong dst; + target_ulong dst, v; /* Tag overflow occurs if either input has bits 0 or 1 set. */ if ((src1 | src2) & 3) { @@ -225,15 +178,23 @@ target_ulong helper_tsubcctv(CPUSPARCState *env, target_ulong src1, dst = src1 - src2; /* Tag overflow occurs if the subtraction overflows. */ - if ((src1 ^ src2) & (src1 ^ dst) & (1u << 31)) { + v = (src1 ^ src2) & (src1 ^ dst); + if (v & (1u << 31)) { goto tag_overflow; } /* Only modify the CC after any exceptions have been generated. */ - env->cc_op = CC_OP_TSUBTV; - env->cc_src = src1; - env->cc_src2 = src2; - env->cc_dst = dst; + env->cc_V = v; + env->cc_N = dst; + env->icc_Z = dst; +#ifdef TARGET_SPARC64 + env->xcc_Z = dst; + env->icc_C = dst ^ src1 ^ src2; + env->xcc_C = src1 < src2; +#else + env->icc_C = src1 < src2; +#endif + return dst; tag_overflow: @@ -251,4 +212,20 @@ void helper_power_down(CPUSPARCState *env) env->npc = env->pc + 4; cpu_loop_exit(cs); } + +target_ulong helper_rdasr17(CPUSPARCState *env) +{ + CPUState *cs = env_cpu(env); + target_ulong val; + + /* + * TODO: There are many more fields to be filled, + * some of which are writable. + */ + val = env->def.nwindows - 1; /* [4:0] NWIN */ + val |= 1 << 8; /* [8] V8 */ + val |= (cs->cpu_index) << 28; /* [31:28] INDEX */ + + return val; +} #endif diff --git a/target/sparc/helper.h b/target/sparc/helper.h index b8f1e78c75..b8087d0d2b 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -2,6 +2,7 @@ DEF_HELPER_1(rett, void, env) DEF_HELPER_2(wrpsr, void, env, tl) DEF_HELPER_1(rdpsr, tl, env) +DEF_HELPER_1(rdasr17, tl, env) DEF_HELPER_1(power_down, void, env) #else DEF_HELPER_FLAGS_2(wrpil, TCG_CALL_NO_RWG, void, env, tl) @@ -24,106 +25,74 @@ DEF_HELPER_FLAGS_2(tick_set_count, TCG_CALL_NO_RWG, void, ptr, i64) DEF_HELPER_FLAGS_3(tick_get_count, TCG_CALL_NO_WG, i64, env, ptr, int) DEF_HELPER_FLAGS_2(tick_set_limit, TCG_CALL_NO_RWG, void, ptr, i64) #endif -DEF_HELPER_FLAGS_3(check_align, TCG_CALL_NO_WG, void, env, tl, i32) DEF_HELPER_1(debug, void, env) DEF_HELPER_1(save, void, env) DEF_HELPER_1(restore, void, env) -DEF_HELPER_3(udiv, tl, env, tl, tl) -DEF_HELPER_3(udiv_cc, tl, env, tl, tl) -DEF_HELPER_3(sdiv, tl, env, tl, tl) -DEF_HELPER_3(sdiv_cc, tl, env, tl, tl) +DEF_HELPER_FLAGS_3(udiv, TCG_CALL_NO_WG, i64, env, tl, tl) +DEF_HELPER_FLAGS_3(sdiv, TCG_CALL_NO_WG, i64, env, tl, tl) DEF_HELPER_3(taddcctv, tl, env, tl, tl) DEF_HELPER_3(tsubcctv, tl, env, tl, tl) -#ifdef TARGET_SPARC64 -DEF_HELPER_FLAGS_3(sdivx, TCG_CALL_NO_WG, s64, env, s64, s64) -DEF_HELPER_FLAGS_3(udivx, TCG_CALL_NO_WG, i64, env, i64, i64) +#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) +DEF_HELPER_FLAGS_3(ld_code, TCG_CALL_NO_WG, i64, env, tl, i32) #endif #if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) DEF_HELPER_FLAGS_4(ld_asi, TCG_CALL_NO_WG, i64, env, tl, int, i32) DEF_HELPER_FLAGS_5(st_asi, TCG_CALL_NO_WG, void, env, tl, i64, int, i32) #endif -DEF_HELPER_FLAGS_1(check_ieee_exceptions, TCG_CALL_NO_WG, tl, env) -DEF_HELPER_FLAGS_3(ldfsr, TCG_CALL_NO_RWG, tl, env, tl, i32) -DEF_HELPER_FLAGS_1(fabss, TCG_CALL_NO_RWG_SE, f32, f32) -DEF_HELPER_FLAGS_2(fsqrts, TCG_CALL_NO_RWG, f32, env, f32) -DEF_HELPER_FLAGS_2(fsqrtd, TCG_CALL_NO_RWG, f64, env, f64) -DEF_HELPER_FLAGS_3(fcmps, TCG_CALL_NO_WG, tl, env, f32, f32) -DEF_HELPER_FLAGS_3(fcmpd, TCG_CALL_NO_WG, tl, env, f64, f64) -DEF_HELPER_FLAGS_3(fcmpes, TCG_CALL_NO_WG, tl, env, f32, f32) -DEF_HELPER_FLAGS_3(fcmped, TCG_CALL_NO_WG, tl, env, f64, f64) -DEF_HELPER_FLAGS_1(fsqrtq, TCG_CALL_NO_RWG, void, env) -DEF_HELPER_FLAGS_1(fcmpq, TCG_CALL_NO_WG, tl, env) -DEF_HELPER_FLAGS_1(fcmpeq, TCG_CALL_NO_WG, tl, env) -#ifdef TARGET_SPARC64 -DEF_HELPER_FLAGS_3(ldxfsr, TCG_CALL_NO_RWG, tl, env, tl, i64) -DEF_HELPER_FLAGS_1(fabsd, TCG_CALL_NO_RWG_SE, f64, f64) -DEF_HELPER_FLAGS_3(fcmps_fcc1, TCG_CALL_NO_WG, tl, env, f32, f32) -DEF_HELPER_FLAGS_3(fcmps_fcc2, TCG_CALL_NO_WG, tl, env, f32, f32) -DEF_HELPER_FLAGS_3(fcmps_fcc3, TCG_CALL_NO_WG, tl, env, f32, f32) -DEF_HELPER_FLAGS_3(fcmpd_fcc1, TCG_CALL_NO_WG, tl, env, f64, f64) -DEF_HELPER_FLAGS_3(fcmpd_fcc2, TCG_CALL_NO_WG, tl, env, f64, f64) -DEF_HELPER_FLAGS_3(fcmpd_fcc3, TCG_CALL_NO_WG, tl, env, f64, f64) -DEF_HELPER_FLAGS_3(fcmpes_fcc1, TCG_CALL_NO_WG, tl, env, f32, f32) -DEF_HELPER_FLAGS_3(fcmpes_fcc2, TCG_CALL_NO_WG, tl, env, f32, f32) -DEF_HELPER_FLAGS_3(fcmpes_fcc3, TCG_CALL_NO_WG, tl, env, f32, f32) -DEF_HELPER_FLAGS_3(fcmped_fcc1, TCG_CALL_NO_WG, tl, env, f64, f64) -DEF_HELPER_FLAGS_3(fcmped_fcc2, TCG_CALL_NO_WG, tl, env, f64, f64) -DEF_HELPER_FLAGS_3(fcmped_fcc3, TCG_CALL_NO_WG, tl, env, f64, f64) -DEF_HELPER_FLAGS_1(fabsq, TCG_CALL_NO_RWG, void, env) -DEF_HELPER_FLAGS_1(fcmpq_fcc1, TCG_CALL_NO_WG, tl, env) -DEF_HELPER_FLAGS_1(fcmpq_fcc2, TCG_CALL_NO_WG, tl, env) -DEF_HELPER_FLAGS_1(fcmpq_fcc3, TCG_CALL_NO_WG, tl, env) -DEF_HELPER_FLAGS_1(fcmpeq_fcc1, TCG_CALL_NO_WG, tl, env) -DEF_HELPER_FLAGS_1(fcmpeq_fcc2, TCG_CALL_NO_WG, tl, env) -DEF_HELPER_FLAGS_1(fcmpeq_fcc3, TCG_CALL_NO_WG, tl, env) -#endif +DEF_HELPER_FLAGS_1(get_fsr, TCG_CALL_NO_WG_SE, tl, env) +DEF_HELPER_FLAGS_2(set_fsr_nofcc_noftt, TCG_CALL_NO_RWG, void, env, i32) +DEF_HELPER_FLAGS_2(fsqrts, TCG_CALL_NO_WG, f32, env, f32) +DEF_HELPER_FLAGS_2(fsqrtd, TCG_CALL_NO_WG, f64, env, f64) +DEF_HELPER_FLAGS_2(fsqrtq, TCG_CALL_NO_WG, i128, env, i128) +DEF_HELPER_FLAGS_3(fcmps, TCG_CALL_NO_WG, i32, env, f32, f32) +DEF_HELPER_FLAGS_3(fcmpes, TCG_CALL_NO_WG, i32, env, f32, f32) +DEF_HELPER_FLAGS_3(fcmpd, TCG_CALL_NO_WG, i32, env, f64, f64) +DEF_HELPER_FLAGS_3(fcmped, TCG_CALL_NO_WG, i32, env, f64, f64) +DEF_HELPER_FLAGS_3(fcmpq, TCG_CALL_NO_WG, i32, env, i128, i128) +DEF_HELPER_FLAGS_3(fcmpeq, TCG_CALL_NO_WG, i32, env, i128, i128) DEF_HELPER_2(raise_exception, noreturn, env, int) -#define F_HELPER_0_1(name) \ - DEF_HELPER_FLAGS_1(f ## name, TCG_CALL_NO_RWG, void, env) -DEF_HELPER_FLAGS_3(faddd, TCG_CALL_NO_RWG, f64, env, f64, f64) -DEF_HELPER_FLAGS_3(fsubd, TCG_CALL_NO_RWG, f64, env, f64, f64) -DEF_HELPER_FLAGS_3(fmuld, TCG_CALL_NO_RWG, f64, env, f64, f64) -DEF_HELPER_FLAGS_3(fdivd, TCG_CALL_NO_RWG, f64, env, f64, f64) -F_HELPER_0_1(addq) -F_HELPER_0_1(subq) -F_HELPER_0_1(mulq) -F_HELPER_0_1(divq) +DEF_HELPER_FLAGS_3(faddd, TCG_CALL_NO_WG, f64, env, f64, f64) +DEF_HELPER_FLAGS_3(fsubd, TCG_CALL_NO_WG, f64, env, f64, f64) +DEF_HELPER_FLAGS_3(fmuld, TCG_CALL_NO_WG, f64, env, f64, f64) +DEF_HELPER_FLAGS_3(fdivd, TCG_CALL_NO_WG, f64, env, f64, f64) -DEF_HELPER_FLAGS_3(fadds, TCG_CALL_NO_RWG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fsubs, TCG_CALL_NO_RWG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fmuls, TCG_CALL_NO_RWG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fdivs, TCG_CALL_NO_RWG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(faddq, TCG_CALL_NO_WG, i128, env, i128, i128) +DEF_HELPER_FLAGS_3(fsubq, TCG_CALL_NO_WG, i128, env, i128, i128) +DEF_HELPER_FLAGS_3(fmulq, TCG_CALL_NO_WG, i128, env, i128, i128) +DEF_HELPER_FLAGS_3(fdivq, TCG_CALL_NO_WG, i128, env, i128, i128) -DEF_HELPER_FLAGS_3(fsmuld, TCG_CALL_NO_RWG, f64, env, f32, f32) -DEF_HELPER_FLAGS_3(fdmulq, TCG_CALL_NO_RWG, void, env, f64, f64) +DEF_HELPER_FLAGS_3(fadds, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fsubs, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fmuls, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fdivs, TCG_CALL_NO_WG, f32, env, f32, f32) -DEF_HELPER_FLAGS_1(fnegs, TCG_CALL_NO_RWG_SE, f32, f32) -DEF_HELPER_FLAGS_2(fitod, TCG_CALL_NO_RWG_SE, f64, env, s32) -DEF_HELPER_FLAGS_2(fitoq, TCG_CALL_NO_RWG, void, env, s32) +DEF_HELPER_FLAGS_3(fsmuld, TCG_CALL_NO_WG, f64, env, f32, f32) +DEF_HELPER_FLAGS_3(fdmulq, TCG_CALL_NO_WG, i128, env, f64, f64) -DEF_HELPER_FLAGS_2(fitos, TCG_CALL_NO_RWG, f32, env, s32) +DEF_HELPER_FLAGS_2(fitod, TCG_CALL_NO_WG, f64, env, s32) +DEF_HELPER_FLAGS_2(fitoq, TCG_CALL_NO_WG, i128, env, s32) + +DEF_HELPER_FLAGS_2(fitos, TCG_CALL_NO_WG, f32, env, s32) #ifdef TARGET_SPARC64 -DEF_HELPER_FLAGS_1(fnegd, TCG_CALL_NO_RWG_SE, f64, f64) -DEF_HELPER_FLAGS_1(fnegq, TCG_CALL_NO_RWG, void, env) -DEF_HELPER_FLAGS_2(fxtos, TCG_CALL_NO_RWG, f32, env, s64) -DEF_HELPER_FLAGS_2(fxtod, TCG_CALL_NO_RWG, f64, env, s64) -DEF_HELPER_FLAGS_2(fxtoq, TCG_CALL_NO_RWG, void, env, s64) +DEF_HELPER_FLAGS_2(fxtos, TCG_CALL_NO_WG, f32, env, s64) +DEF_HELPER_FLAGS_2(fxtod, TCG_CALL_NO_WG, f64, env, s64) +DEF_HELPER_FLAGS_2(fxtoq, TCG_CALL_NO_WG, i128, env, s64) #endif -DEF_HELPER_FLAGS_2(fdtos, TCG_CALL_NO_RWG, f32, env, f64) -DEF_HELPER_FLAGS_2(fstod, TCG_CALL_NO_RWG, f64, env, f32) -DEF_HELPER_FLAGS_1(fqtos, TCG_CALL_NO_RWG, f32, env) -DEF_HELPER_FLAGS_2(fstoq, TCG_CALL_NO_RWG, void, env, f32) -DEF_HELPER_FLAGS_1(fqtod, TCG_CALL_NO_RWG, f64, env) -DEF_HELPER_FLAGS_2(fdtoq, TCG_CALL_NO_RWG, void, env, f64) -DEF_HELPER_FLAGS_2(fstoi, TCG_CALL_NO_RWG, s32, env, f32) -DEF_HELPER_FLAGS_2(fdtoi, TCG_CALL_NO_RWG, s32, env, f64) -DEF_HELPER_FLAGS_1(fqtoi, TCG_CALL_NO_RWG, s32, env) +DEF_HELPER_FLAGS_2(fdtos, TCG_CALL_NO_WG, f32, env, f64) +DEF_HELPER_FLAGS_2(fstod, TCG_CALL_NO_WG, f64, env, f32) +DEF_HELPER_FLAGS_2(fqtos, TCG_CALL_NO_WG, f32, env, i128) +DEF_HELPER_FLAGS_2(fstoq, TCG_CALL_NO_WG, i128, env, f32) +DEF_HELPER_FLAGS_2(fqtod, TCG_CALL_NO_WG, f64, env, i128) +DEF_HELPER_FLAGS_2(fdtoq, TCG_CALL_NO_WG, i128, env, f64) +DEF_HELPER_FLAGS_2(fstoi, TCG_CALL_NO_WG, s32, env, f32) +DEF_HELPER_FLAGS_2(fdtoi, TCG_CALL_NO_WG, s32, env, f64) +DEF_HELPER_FLAGS_2(fqtoi, TCG_CALL_NO_WG, s32, env, i128) #ifdef TARGET_SPARC64 -DEF_HELPER_FLAGS_2(fstox, TCG_CALL_NO_RWG, s64, env, f32) -DEF_HELPER_FLAGS_2(fdtox, TCG_CALL_NO_RWG, s64, env, f64) -DEF_HELPER_FLAGS_1(fqtox, TCG_CALL_NO_RWG, s64, env) +DEF_HELPER_FLAGS_2(fstox, TCG_CALL_NO_WG, s64, env, f32) +DEF_HELPER_FLAGS_2(fdtox, TCG_CALL_NO_WG, s64, env, f64) +DEF_HELPER_FLAGS_2(fqtox, TCG_CALL_NO_WG, s64, env, i128) DEF_HELPER_FLAGS_2(fpmerge, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fmul8x16, TCG_CALL_NO_RWG_SE, i64, i64, i64) @@ -139,18 +108,6 @@ DEF_HELPER_FLAGS_2(fpack16, TCG_CALL_NO_RWG_SE, i32, i64, i64) DEF_HELPER_FLAGS_3(fpack32, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) DEF_HELPER_FLAGS_2(fpackfix, TCG_CALL_NO_RWG_SE, i32, i64, i64) DEF_HELPER_FLAGS_3(bshuffle, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) -#define VIS_HELPER(name) \ - DEF_HELPER_FLAGS_2(f ## name ## 16, TCG_CALL_NO_RWG_SE, \ - i64, i64, i64) \ - DEF_HELPER_FLAGS_2(f ## name ## 16s, TCG_CALL_NO_RWG_SE, \ - i32, i32, i32) \ - DEF_HELPER_FLAGS_2(f ## name ## 32, TCG_CALL_NO_RWG_SE, \ - i64, i64, i64) \ - DEF_HELPER_FLAGS_2(f ## name ## 32s, TCG_CALL_NO_RWG_SE, \ - i32, i32, i32) - -VIS_HELPER(padd) -VIS_HELPER(psub) #define VIS_CMPHELPER(name) \ DEF_HELPER_FLAGS_2(f##name##16, TCG_CALL_NO_RWG_SE, \ i64, i64, i64) \ @@ -161,8 +118,5 @@ VIS_CMPHELPER(cmpeq) VIS_CMPHELPER(cmple) VIS_CMPHELPER(cmpne) #endif -#undef F_HELPER_0_1 #undef VIS_HELPER #undef VIS_CMPHELPER -DEF_HELPER_1(compute_psr, void, env) -DEF_HELPER_FLAGS_1(compute_C_icc, TCG_CALL_NO_WG_SE, i32, env) diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode new file mode 100644 index 0000000000..2d26404cb2 --- /dev/null +++ b/target/sparc/insns.decode @@ -0,0 +1,548 @@ +# SPDX-License-Identifier: LGPL-2.0+ +# +# Sparc instruction decode definitions. +# Copyright (c) 2023 Richard Henderson + +## +## Major Opcodes 00 and 01 -- branches, call, and sethi. +## + +&bcc i a cond cc +BPcc 00 a:1 cond:4 001 cc:1 0 - i:s19 &bcc +Bicc 00 a:1 cond:4 010 i:s22 &bcc cc=0 +FBPfcc 00 a:1 cond:4 101 cc:2 - i:s19 &bcc +FBfcc 00 a:1 cond:4 110 i:s22 &bcc cc=0 + +%d16 20:s2 0:14 +BPr 00 a:1 0 cond:3 011 .. - rs1:5 .............. i=%d16 + +NCP 00 - ---- 111 ---------------------- # CBcc + +SETHI 00 rd:5 100 i:22 + +CALL 01 i:s30 + +## +## Major Opcode 10 -- integer, floating-point, vis, and system insns. +## + +&r_r_ri rd rs1 rs2_or_imm imm:bool +@n_r_ri .. ..... ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri rd=0 +@r_r_ri .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri + +&r_r_ri_cc rd rs1 rs2_or_imm imm:bool cc:bool +@r_r_ri_cc .. rd:5 . cc:1 .... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_cc +@r_r_ri_cc0 .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_cc cc=0 +@r_r_ri_cc1 .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_cc cc=1 + +&r_r_r rd rs1 rs2 +@r_r_r .. rd:5 ...... rs1:5 . ........ rs2:5 &r_r_r +@r_r_r_swap .. rd:5 ...... rs2:5 . ........ rs1:5 &r_r_r + +&r_r rd rs +@r_r1 .. rd:5 ...... rs:5 . ........ ..... &r_r +@r_r2 .. rd:5 ...... ..... . ........ rs:5 &r_r + +{ + [ + STBAR 10 00000 101000 01111 0 0000000000000 + MEMBAR 10 00000 101000 01111 1 000000 cmask:3 mmask:4 + + RDCCR 10 rd:5 101000 00010 0 0000000000000 + RDASI 10 rd:5 101000 00011 0 0000000000000 + RDTICK 10 rd:5 101000 00100 0 0000000000000 + RDPC 10 rd:5 101000 00101 0 0000000000000 + RDFPRS 10 rd:5 101000 00110 0 0000000000000 + RDASR17 10 rd:5 101000 10001 0 0000000000000 + RDGSR 10 rd:5 101000 10011 0 0000000000000 + RDSOFTINT 10 rd:5 101000 10110 0 0000000000000 + RDTICK_CMPR 10 rd:5 101000 10111 0 0000000000000 + RDSTICK 10 rd:5 101000 11000 0 0000000000000 + RDSTICK_CMPR 10 rd:5 101000 11001 0 0000000000000 + RDSTRAND_STATUS 10 rd:5 101000 11010 0 0000000000000 + ] + # Before v8, all rs1 accepted; otherwise rs1==0. + RDY 10 rd:5 101000 rs1:5 0 0000000000000 +} + +{ + [ + WRY 10 00000 110000 ..... . ............. @n_r_ri + WRCCR 10 00010 110000 ..... . ............. @n_r_ri + WRASI 10 00011 110000 ..... . ............. @n_r_ri + WRFPRS 10 00110 110000 ..... . ............. @n_r_ri + { + WRGSR 10 10011 110000 ..... . ............. @n_r_ri + WRPOWERDOWN 10 10011 110000 ..... . ............. @n_r_ri + } + WRSOFTINT_SET 10 10100 110000 ..... . ............. @n_r_ri + WRSOFTINT_CLR 10 10101 110000 ..... . ............. @n_r_ri + WRSOFTINT 10 10110 110000 ..... . ............. @n_r_ri + WRTICK_CMPR 10 10111 110000 ..... . ............. @n_r_ri + WRSTICK 10 11000 110000 ..... . ............. @n_r_ri + WRSTICK_CMPR 10 11001 110000 ..... . ............. @n_r_ri + ] + # Before v8, rs1==0 was WRY, and the rest executed as nop. + [ + NOP_v7 10 ----- 110000 ----- 0 00000000 ----- + NOP_v7 10 ----- 110000 ----- 1 -------- ----- + ] +} + +{ + RDPSR 10 rd:5 101001 00000 0 0000000000000 + RDHPR_hpstate 10 rd:5 101001 00000 0 0000000000000 +} +RDHPR_htstate 10 rd:5 101001 00001 0 0000000000000 +RDHPR_hintp 10 rd:5 101001 00011 0 0000000000000 +RDHPR_htba 10 rd:5 101001 00101 0 0000000000000 +RDHPR_hver 10 rd:5 101001 00110 0 0000000000000 +RDHPR_hstick_cmpr 10 rd:5 101001 11111 0 0000000000000 + +{ + WRPSR 10 00000 110001 ..... . ............. @n_r_ri + SAVED 10 00000 110001 00000 0 0000000000000 +} +RESTORED 10 00001 110001 00000 0 0000000000000 +# UA2005 ALLCLEAN +# UA2005 OTHERW +# UA2005 NORMALW +# UA2005 INVALW + +{ + RDWIM 10 rd:5 101010 00000 0 0000000000000 + RDPR_tpc 10 rd:5 101010 00000 0 0000000000000 +} +RDPR_tnpc 10 rd:5 101010 00001 0 0000000000000 +RDPR_tstate 10 rd:5 101010 00010 0 0000000000000 +RDPR_tt 10 rd:5 101010 00011 0 0000000000000 +RDPR_tick 10 rd:5 101010 00100 0 0000000000000 +RDPR_tba 10 rd:5 101010 00101 0 0000000000000 +RDPR_pstate 10 rd:5 101010 00110 0 0000000000000 +RDPR_tl 10 rd:5 101010 00111 0 0000000000000 +RDPR_pil 10 rd:5 101010 01000 0 0000000000000 +RDPR_cwp 10 rd:5 101010 01001 0 0000000000000 +RDPR_cansave 10 rd:5 101010 01010 0 0000000000000 +RDPR_canrestore 10 rd:5 101010 01011 0 0000000000000 +RDPR_cleanwin 10 rd:5 101010 01100 0 0000000000000 +RDPR_otherwin 10 rd:5 101010 01101 0 0000000000000 +RDPR_wstate 10 rd:5 101010 01110 0 0000000000000 +RDPR_gl 10 rd:5 101010 10000 0 0000000000000 +RDPR_strand_status 10 rd:5 101010 11010 0 0000000000000 +RDPR_ver 10 rd:5 101010 11111 0 0000000000000 + +{ + WRWIM 10 00000 110010 ..... . ............. @n_r_ri + WRPR_tpc 10 00000 110010 ..... . ............. @n_r_ri +} +WRPR_tnpc 10 00001 110010 ..... . ............. @n_r_ri +WRPR_tstate 10 00010 110010 ..... . ............. @n_r_ri +WRPR_tt 10 00011 110010 ..... . ............. @n_r_ri +WRPR_tick 10 00100 110010 ..... . ............. @n_r_ri +WRPR_tba 10 00101 110010 ..... . ............. @n_r_ri +WRPR_pstate 10 00110 110010 ..... . ............. @n_r_ri +WRPR_tl 10 00111 110010 ..... . ............. @n_r_ri +WRPR_pil 10 01000 110010 ..... . ............. @n_r_ri +WRPR_cwp 10 01001 110010 ..... . ............. @n_r_ri +WRPR_cansave 10 01010 110010 ..... . ............. @n_r_ri +WRPR_canrestore 10 01011 110010 ..... . ............. @n_r_ri +WRPR_cleanwin 10 01100 110010 ..... . ............. @n_r_ri +WRPR_otherwin 10 01101 110010 ..... . ............. @n_r_ri +WRPR_wstate 10 01110 110010 ..... . ............. @n_r_ri +WRPR_gl 10 10000 110010 ..... . ............. @n_r_ri +WRPR_strand_status 10 11010 110010 ..... . ............. @n_r_ri + +{ + FLUSHW 10 00000 101011 00000 0 0000000000000 + RDTBR 10 rd:5 101011 00000 0 0000000000000 +} + +{ + WRTBR 10 00000 110011 ..... . ............. @n_r_ri + WRHPR_hpstate 10 00000 110011 ..... . ............. @n_r_ri +} +WRHPR_htstate 10 00001 110011 ..... . ............. @n_r_ri +WRHPR_hintp 10 00011 110011 ..... . ............. @n_r_ri +WRHPR_htba 10 00101 110011 ..... . ............. @n_r_ri +WRHPR_hstick_cmpr 10 11111 110011 ..... . ............. @n_r_ri + +ADD 10 ..... 0.0000 ..... . ............. @r_r_ri_cc +AND 10 ..... 0.0001 ..... . ............. @r_r_ri_cc +OR 10 ..... 0.0010 ..... . ............. @r_r_ri_cc +XOR 10 ..... 0.0011 ..... . ............. @r_r_ri_cc +SUB 10 ..... 0.0100 ..... . ............. @r_r_ri_cc +ANDN 10 ..... 0.0101 ..... . ............. @r_r_ri_cc +ORN 10 ..... 0.0110 ..... . ............. @r_r_ri_cc +XORN 10 ..... 0.0111 ..... . ............. @r_r_ri_cc +ADDC 10 ..... 0.1000 ..... . ............. @r_r_ri_cc +SUBC 10 ..... 0.1100 ..... . ............. @r_r_ri_cc + +MULX 10 ..... 001001 ..... . ............. @r_r_ri_cc0 +UMUL 10 ..... 0.1010 ..... . ............. @r_r_ri_cc +SMUL 10 ..... 0.1011 ..... . ............. @r_r_ri_cc +MULScc 10 ..... 100100 ..... . ............. @r_r_ri_cc1 + +UDIVX 10 ..... 001101 ..... . ............. @r_r_ri +SDIVX 10 ..... 101101 ..... . ............. @r_r_ri +UDIV 10 ..... 001110 ..... . ............. @r_r_ri +UDIVcc 10 ..... 011110 ..... . ............. @r_r_ri_cc1 +SDIV 10 ..... 0.1111 ..... . ............. @r_r_ri_cc + +TADDcc 10 ..... 100000 ..... . ............. @r_r_ri_cc1 +TSUBcc 10 ..... 100001 ..... . ............. @r_r_ri_cc1 +TADDccTV 10 ..... 100010 ..... . ............. @r_r_ri_cc1 +TSUBccTV 10 ..... 100011 ..... . ............. @r_r_ri_cc1 + +POPC 10 rd:5 101110 00000 imm:1 rs2_or_imm:s13 \ + &r_r_ri_cc rs1=0 cc=0 + +&shiftr rd rs1 rs2 x:bool +@shiftr .. rd:5 ...... rs1:5 . x:1 ....... rs2:5 &shiftr + +SLL_r 10 ..... 100101 ..... 0 . 0000000 ..... @shiftr +SRL_r 10 ..... 100110 ..... 0 . 0000000 ..... @shiftr +SRA_r 10 ..... 100111 ..... 0 . 0000000 ..... @shiftr + +&shifti rd rs1 i x:bool +@shifti .. rd:5 ...... rs1:5 . x:1 ...... i:6 &shifti + +SLL_i 10 ..... 100101 ..... 1 . 000000 ...... @shifti +SRL_i 10 ..... 100110 ..... 1 . 000000 ...... @shifti +SRA_i 10 ..... 100111 ..... 1 . 000000 ...... @shifti + +Tcc_r 10 0 cond:4 111010 rs1:5 0 cc:1 0000000 rs2:5 +{ + # For v7, the entire simm13 field is present, but masked to 7 bits. + # For v8, [12:7] are reserved. However, a compatibility note for + # the Tcc insn in the v9 manual suggests that the v8 reserved field + # was ignored and did not produce traps. + Tcc_i_v7 10 0 cond:4 111010 rs1:5 1 ------ i:7 + + # For v9, bits [12:11] are cc1 and cc0 (and cc0 must be 0). + # Bits [10:8] are reserved and the OSA2011 manual says they must be 0. + Tcc_i_v9 10 0 cond:4 111010 rs1:5 1 cc:1 0 000 i:8 +} + +MOVcc 10 rd:5 101100 1 cond:4 imm:1 cc:1 0 rs2_or_imm:s11 +MOVfcc 10 rd:5 101100 0 cond:4 imm:1 cc:2 rs2_or_imm:s11 +MOVR 10 rd:5 101111 rs1:5 imm:1 cond:3 rs2_or_imm:s10 + +JMPL 10 ..... 111000 ..... . ............. @r_r_ri +{ + RETT 10 00000 111001 ..... . ............. @n_r_ri + RETURN 10 00000 111001 ..... . ............. @n_r_ri +} +NOP 10 00000 111011 ----- 0 00000000----- # FLUSH reg+reg +NOP 10 00000 111011 ----- 1 ------------- # FLUSH reg+imm +SAVE 10 ..... 111100 ..... . ............. @r_r_ri +RESTORE 10 ..... 111101 ..... . ............. @r_r_ri + +DONE 10 00000 111110 00000 0 0000000000000 +RETRY 10 00001 111110 00000 0 0000000000000 + +FMOVs 10 ..... 110100 00000 0 0000 0001 ..... @r_r2 +FMOVd 10 ..... 110100 00000 0 0000 0010 ..... @r_r2 +FMOVq 10 ..... 110100 00000 0 0000 0011 ..... @r_r2 +FNEGs 10 ..... 110100 00000 0 0000 0101 ..... @r_r2 +FNEGd 10 ..... 110100 00000 0 0000 0110 ..... @r_r2 +FNEGq 10 ..... 110100 00000 0 0000 0111 ..... @r_r2 +FABSs 10 ..... 110100 00000 0 0000 1001 ..... @r_r2 +FABSd 10 ..... 110100 00000 0 0000 1010 ..... @r_r2 +FABSq 10 ..... 110100 00000 0 0000 1011 ..... @r_r2 +FSQRTs 10 ..... 110100 00000 0 0010 1001 ..... @r_r2 +FSQRTd 10 ..... 110100 00000 0 0010 1010 ..... @r_r2 +FSQRTq 10 ..... 110100 00000 0 0010 1011 ..... @r_r2 +FADDs 10 ..... 110100 ..... 0 0100 0001 ..... @r_r_r +FADDd 10 ..... 110100 ..... 0 0100 0010 ..... @r_r_r +FADDq 10 ..... 110100 ..... 0 0100 0011 ..... @r_r_r +FSUBs 10 ..... 110100 ..... 0 0100 0101 ..... @r_r_r +FSUBd 10 ..... 110100 ..... 0 0100 0110 ..... @r_r_r +FSUBq 10 ..... 110100 ..... 0 0100 0111 ..... @r_r_r +FMULs 10 ..... 110100 ..... 0 0100 1001 ..... @r_r_r +FMULd 10 ..... 110100 ..... 0 0100 1010 ..... @r_r_r +FMULq 10 ..... 110100 ..... 0 0100 1011 ..... @r_r_r +FDIVs 10 ..... 110100 ..... 0 0100 1101 ..... @r_r_r +FDIVd 10 ..... 110100 ..... 0 0100 1110 ..... @r_r_r +FDIVq 10 ..... 110100 ..... 0 0100 1111 ..... @r_r_r +FsMULd 10 ..... 110100 ..... 0 0110 1001 ..... @r_r_r +FdMULq 10 ..... 110100 ..... 0 0110 1110 ..... @r_r_r +FsTOx 10 ..... 110100 00000 0 1000 0001 ..... @r_r2 +FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_r2 +FqTOx 10 ..... 110100 00000 0 1000 0011 ..... @r_r2 +FxTOs 10 ..... 110100 00000 0 1000 0100 ..... @r_r2 +FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @r_r2 +FxTOq 10 ..... 110100 00000 0 1000 1100 ..... @r_r2 +FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 +FdTOs 10 ..... 110100 00000 0 1100 0110 ..... @r_r2 +FqTOs 10 ..... 110100 00000 0 1100 0111 ..... @r_r2 +FiTOd 10 ..... 110100 00000 0 1100 1000 ..... @r_r2 +FsTOd 10 ..... 110100 00000 0 1100 1001 ..... @r_r2 +FqTOd 10 ..... 110100 00000 0 1100 1011 ..... @r_r2 +FiTOq 10 ..... 110100 00000 0 1100 1100 ..... @r_r2 +FsTOq 10 ..... 110100 00000 0 1100 1101 ..... @r_r2 +FdTOq 10 ..... 110100 00000 0 1100 1110 ..... @r_r2 +FsTOi 10 ..... 110100 00000 0 1101 0001 ..... @r_r2 +FdTOi 10 ..... 110100 00000 0 1101 0010 ..... @r_r2 +FqTOi 10 ..... 110100 00000 0 1101 0011 ..... @r_r2 + +FMOVscc 10 rd:5 110101 0 cond:4 1 cc:1 0 000001 rs2:5 +FMOVdcc 10 rd:5 110101 0 cond:4 1 cc:1 0 000010 rs2:5 +FMOVqcc 10 rd:5 110101 0 cond:4 1 cc:1 0 000011 rs2:5 + +FMOVsfcc 10 rd:5 110101 0 cond:4 0 cc:2 000001 rs2:5 +FMOVdfcc 10 rd:5 110101 0 cond:4 0 cc:2 000010 rs2:5 +FMOVqfcc 10 rd:5 110101 0 cond:4 0 cc:2 000011 rs2:5 + +FMOVRs 10 rd:5 110101 rs1:5 0 cond:3 00101 rs2:5 +FMOVRd 10 rd:5 110101 rs1:5 0 cond:3 00110 rs2:5 +FMOVRq 10 rd:5 110101 rs1:5 0 cond:3 00111 rs2:5 + +FCMPs 10 000 cc:2 110101 rs1:5 0 0101 0001 rs2:5 +FCMPd 10 000 cc:2 110101 rs1:5 0 0101 0010 rs2:5 +FCMPq 10 000 cc:2 110101 rs1:5 0 0101 0011 rs2:5 +FCMPEs 10 000 cc:2 110101 rs1:5 0 0101 0101 rs2:5 +FCMPEd 10 000 cc:2 110101 rs1:5 0 0101 0110 rs2:5 +FCMPEq 10 000 cc:2 110101 rs1:5 0 0101 0111 rs2:5 + +{ + [ + EDGE8cc 10 ..... 110110 ..... 0 0000 0000 ..... @r_r_r + EDGE8N 10 ..... 110110 ..... 0 0000 0001 ..... @r_r_r + EDGE8Lcc 10 ..... 110110 ..... 0 0000 0010 ..... @r_r_r + EDGE8LN 10 ..... 110110 ..... 0 0000 0011 ..... @r_r_r + EDGE16cc 10 ..... 110110 ..... 0 0000 0100 ..... @r_r_r + EDGE16N 10 ..... 110110 ..... 0 0000 0101 ..... @r_r_r + EDGE16Lcc 10 ..... 110110 ..... 0 0000 0110 ..... @r_r_r + EDGE16LN 10 ..... 110110 ..... 0 0000 0111 ..... @r_r_r + EDGE32cc 10 ..... 110110 ..... 0 0000 1000 ..... @r_r_r + EDGE32N 10 ..... 110110 ..... 0 0000 1001 ..... @r_r_r + EDGE32Lcc 10 ..... 110110 ..... 0 0000 1010 ..... @r_r_r + EDGE32LN 10 ..... 110110 ..... 0 0000 1011 ..... @r_r_r + + ARRAY8 10 ..... 110110 ..... 0 0001 0000 ..... @r_r_r + ARRAY16 10 ..... 110110 ..... 0 0001 0010 ..... @r_r_r + ARRAY32 10 ..... 110110 ..... 0 0001 0100 ..... @r_r_r + + ALIGNADDR 10 ..... 110110 ..... 0 0001 1000 ..... @r_r_r + ALIGNADDRL 10 ..... 110110 ..... 0 0001 1010 ..... @r_r_r + + BMASK 10 ..... 110110 ..... 0 0001 1001 ..... @r_r_r + + FPCMPLE16 10 ..... 110110 ..... 0 0010 0000 ..... @r_r_r + FPCMPNE16 10 ..... 110110 ..... 0 0010 0010 ..... @r_r_r + FPCMPGT16 10 ..... 110110 ..... 0 0010 1000 ..... @r_r_r + FPCMPEQ16 10 ..... 110110 ..... 0 0010 1010 ..... @r_r_r + FPCMPLE32 10 ..... 110110 ..... 0 0010 0100 ..... @r_r_r + FPCMPNE32 10 ..... 110110 ..... 0 0010 0110 ..... @r_r_r + FPCMPGT32 10 ..... 110110 ..... 0 0010 1100 ..... @r_r_r + FPCMPEQ32 10 ..... 110110 ..... 0 0010 1110 ..... @r_r_r + + FMUL8x16 10 ..... 110110 ..... 0 0011 0001 ..... @r_r_r + FMUL8x16AU 10 ..... 110110 ..... 0 0011 0011 ..... @r_r_r + FMUL8x16AL 10 ..... 110110 ..... 0 0011 0101 ..... @r_r_r + FMUL8SUx16 10 ..... 110110 ..... 0 0011 0110 ..... @r_r_r + FMUL8ULx16 10 ..... 110110 ..... 0 0011 0111 ..... @r_r_r + FMULD8SUx16 10 ..... 110110 ..... 0 0011 1000 ..... @r_r_r + FMULD8ULx16 10 ..... 110110 ..... 0 0011 1001 ..... @r_r_r + FPACK32 10 ..... 110110 ..... 0 0011 1010 ..... @r_r_r + FPACK16 10 ..... 110110 00000 0 0011 1011 ..... @r_r2 + FPACKFIX 10 ..... 110110 00000 0 0011 1101 ..... @r_r2 + PDIST 10 ..... 110110 ..... 0 0011 1110 ..... @r_r_r + + FALIGNDATAg 10 ..... 110110 ..... 0 0100 1000 ..... @r_r_r + FPMERGE 10 ..... 110110 ..... 0 0100 1011 ..... @r_r_r + BSHUFFLE 10 ..... 110110 ..... 0 0100 1100 ..... @r_r_r + FEXPAND 10 ..... 110110 ..... 0 0100 1101 ..... @r_r_r + + FSRCd 10 ..... 110110 ..... 0 0111 0100 00000 @r_r1 # FSRC1d + FSRCs 10 ..... 110110 ..... 0 0111 0101 00000 @r_r1 # FSRC1s + FSRCd 10 ..... 110110 00000 0 0111 1000 ..... @r_r2 # FSRC2d + FSRCs 10 ..... 110110 00000 0 0111 1001 ..... @r_r2 # FSRC2s + FNOTd 10 ..... 110110 ..... 0 0110 1010 00000 @r_r1 # FNOT1d + FNOTs 10 ..... 110110 ..... 0 0110 1011 00000 @r_r1 # FNOT1s + FNOTd 10 ..... 110110 00000 0 0110 0110 ..... @r_r2 # FNOT2d + FNOTs 10 ..... 110110 00000 0 0110 0111 ..... @r_r2 # FNOT2s + + FPADD16 10 ..... 110110 ..... 0 0101 0000 ..... @r_r_r + FPADD16s 10 ..... 110110 ..... 0 0101 0001 ..... @r_r_r + FPADD32 10 ..... 110110 ..... 0 0101 0010 ..... @r_r_r + FPADD32s 10 ..... 110110 ..... 0 0101 0011 ..... @r_r_r + FPSUB16 10 ..... 110110 ..... 0 0101 0100 ..... @r_r_r + FPSUB16s 10 ..... 110110 ..... 0 0101 0101 ..... @r_r_r + FPSUB32 10 ..... 110110 ..... 0 0101 0110 ..... @r_r_r + FPSUB32s 10 ..... 110110 ..... 0 0101 0111 ..... @r_r_r + + FNORd 10 ..... 110110 ..... 0 0110 0010 ..... @r_r_r + FNORs 10 ..... 110110 ..... 0 0110 0011 ..... @r_r_r + FANDNOTd 10 ..... 110110 ..... 0 0110 0100 ..... @r_r_r # FANDNOT2d + FANDNOTs 10 ..... 110110 ..... 0 0110 0101 ..... @r_r_r # FANDNOT2s + FANDNOTd 10 ..... 110110 ..... 0 0110 1000 ..... @r_r_r_swap # ... 1d + FANDNOTs 10 ..... 110110 ..... 0 0110 1001 ..... @r_r_r_swap # ... 1s + FXORd 10 ..... 110110 ..... 0 0110 1100 ..... @r_r_r + FXORs 10 ..... 110110 ..... 0 0110 1101 ..... @r_r_r + FNANDd 10 ..... 110110 ..... 0 0110 1110 ..... @r_r_r + FNANDs 10 ..... 110110 ..... 0 0110 1111 ..... @r_r_r + FANDd 10 ..... 110110 ..... 0 0111 0000 ..... @r_r_r + FANDs 10 ..... 110110 ..... 0 0111 0001 ..... @r_r_r + FXNORd 10 ..... 110110 ..... 0 0111 0010 ..... @r_r_r + FXNORs 10 ..... 110110 ..... 0 0111 0011 ..... @r_r_r + FORNOTd 10 ..... 110110 ..... 0 0111 0110 ..... @r_r_r # FORNOT2d + FORNOTs 10 ..... 110110 ..... 0 0111 0111 ..... @r_r_r # FORNOT2s + FORNOTd 10 ..... 110110 ..... 0 0111 1010 ..... @r_r_r_swap # ... 1d + FORNOTs 10 ..... 110110 ..... 0 0111 1011 ..... @r_r_r_swap # ... 1s + FORd 10 ..... 110110 ..... 0 0111 1100 ..... @r_r_r + FORs 10 ..... 110110 ..... 0 0111 1101 ..... @r_r_r + + FZEROd 10 rd:5 110110 00000 0 0110 0000 00000 + FZEROs 10 rd:5 110110 00000 0 0110 0001 00000 + FONEd 10 rd:5 110110 00000 0 0111 1110 00000 + FONEs 10 rd:5 110110 00000 0 0111 1111 00000 + ] + NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 +} + +NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 + +## +## Major Opcode 11 -- load and store instructions +## + +%dfp_rd 25:5 !function=extract_dfpreg +%qfp_rd 25:5 !function=extract_qfpreg + +&r_r_ri_asi rd rs1 rs2_or_imm asi imm:bool +@r_r_ri_na .. rd:5 ...... rs1:5 imm:1 rs2_or_imm:s13 &r_r_ri_asi asi=-1 +@d_r_ri_na .. ..... ...... rs1:5 imm:1 rs2_or_imm:s13 \ + &r_r_ri_asi rd=%dfp_rd asi=-1 +@q_r_ri_na .. ..... ...... rs1:5 imm:1 rs2_or_imm:s13 \ + &r_r_ri_asi rd=%qfp_rd asi=-1 + +@r_r_r_asi .. rd:5 ...... rs1:5 0 asi:8 rs2_or_imm:5 &r_r_ri_asi imm=0 +@r_r_i_asi .. rd:5 ...... rs1:5 1 rs2_or_imm:s13 \ + &r_r_ri_asi imm=1 asi=-2 +@d_r_r_asi .. ..... ...... rs1:5 0 asi:8 rs2_or_imm:5 \ + &r_r_ri_asi rd=%dfp_rd imm=0 +@d_r_i_asi .. ..... ...... rs1:5 1 rs2_or_imm:s13 \ + &r_r_ri_asi rd=%dfp_rd imm=1 asi=-2 +@q_r_r_asi .. ..... ...... rs1:5 0 asi:8 rs2_or_imm:5 \ + &r_r_ri_asi rd=%qfp_rd imm=0 +@q_r_i_asi .. ..... ...... rs1:5 1 rs2_or_imm:s13 \ + &r_r_ri_asi rd=%qfp_rd imm=1 asi=-2 +@casa_imm .. rd:5 ...... rs1:5 1 00000000 rs2_or_imm:5 \ + &r_r_ri_asi imm=1 asi=-2 + +LDUW 11 ..... 000000 ..... . ............. @r_r_ri_na +LDUB 11 ..... 000001 ..... . ............. @r_r_ri_na +LDUH 11 ..... 000010 ..... . ............. @r_r_ri_na +LDD 11 ..... 000011 ..... . ............. @r_r_ri_na +LDSW 11 ..... 001000 ..... . ............. @r_r_ri_na +LDSB 11 ..... 001001 ..... . ............. @r_r_ri_na +LDSH 11 ..... 001010 ..... . ............. @r_r_ri_na +LDX 11 ..... 001011 ..... . ............. @r_r_ri_na + +STW 11 ..... 000100 ..... . ............. @r_r_ri_na +STB 11 ..... 000101 ..... . ............. @r_r_ri_na +STH 11 ..... 000110 ..... . ............. @r_r_ri_na +STD 11 ..... 000111 ..... . ............. @r_r_ri_na +STX 11 ..... 001110 ..... . ............. @r_r_ri_na + +LDUW 11 ..... 010000 ..... . ............. @r_r_r_asi # LDUWA +LDUW 11 ..... 010000 ..... . ............. @r_r_i_asi # LDUWA +LDUB 11 ..... 010001 ..... . ............. @r_r_r_asi # LDUBA +LDUB 11 ..... 010001 ..... . ............. @r_r_i_asi # LDUBA +LDUH 11 ..... 010010 ..... . ............. @r_r_r_asi # LDUHA +LDUH 11 ..... 010010 ..... . ............. @r_r_i_asi # LDUHA +LDD 11 ..... 010011 ..... . ............. @r_r_r_asi # LDDA +LDD 11 ..... 010011 ..... . ............. @r_r_i_asi # LDDA +LDX 11 ..... 011011 ..... . ............. @r_r_r_asi # LDXA +LDX 11 ..... 011011 ..... . ............. @r_r_i_asi # LDXA +LDSB 11 ..... 011001 ..... . ............. @r_r_r_asi # LDSBA +LDSB 11 ..... 011001 ..... . ............. @r_r_i_asi # LDSBA +LDSH 11 ..... 011010 ..... . ............. @r_r_r_asi # LDSHA +LDSH 11 ..... 011010 ..... . ............. @r_r_i_asi # LDSHA +LDSW 11 ..... 011000 ..... . ............. @r_r_r_asi # LDSWA +LDSW 11 ..... 011000 ..... . ............. @r_r_i_asi # LDSWA + +STW 11 ..... 010100 ..... . ............. @r_r_r_asi # STWA +STW 11 ..... 010100 ..... . ............. @r_r_i_asi # STWA +STB 11 ..... 010101 ..... . ............. @r_r_r_asi # STBA +STB 11 ..... 010101 ..... . ............. @r_r_i_asi # STBA +STH 11 ..... 010110 ..... . ............. @r_r_r_asi # STHA +STH 11 ..... 010110 ..... . ............. @r_r_i_asi # STHA +STD 11 ..... 010111 ..... . ............. @r_r_r_asi # STDA +STD 11 ..... 010111 ..... . ............. @r_r_i_asi # STDA +STX 11 ..... 011110 ..... . ............. @r_r_r_asi # STXA +STX 11 ..... 011110 ..... . ............. @r_r_i_asi # STXA + +LDF 11 ..... 100000 ..... . ............. @r_r_ri_na +LDFSR 11 00000 100001 ..... . ............. @n_r_ri +LDXFSR 11 00001 100001 ..... . ............. @n_r_ri +LDQF 11 ..... 100010 ..... . ............. @q_r_ri_na +LDDF 11 ..... 100011 ..... . ............. @d_r_ri_na + +STF 11 ..... 100100 ..... . ............. @r_r_ri_na +STFSR 11 00000 100101 ..... . ............. @n_r_ri +STXFSR 11 00001 100101 ..... . ............. @n_r_ri +{ + STQF 11 ..... 100110 ..... . ............. @q_r_ri_na + STDFQ 11 ----- 100110 ----- - ------------- +} +STDF 11 ..... 100111 ..... . ............. @d_r_ri_na + +LDSTUB 11 ..... 001101 ..... . ............. @r_r_ri_na +LDSTUB 11 ..... 011101 ..... . ............. @r_r_r_asi # LDSTUBA +LDSTUB 11 ..... 011101 ..... . ............. @r_r_i_asi # LDSTUBA + +SWAP 11 ..... 001111 ..... . ............. @r_r_ri_na +SWAP 11 ..... 011111 ..... . ............. @r_r_r_asi # SWAPA +SWAP 11 ..... 011111 ..... . ............. @r_r_i_asi # SWAPA + +CASA 11 ..... 111100 ..... . ............. @r_r_r_asi +CASA 11 ..... 111100 ..... . ............. @casa_imm +CASXA 11 ..... 111110 ..... . ............. @r_r_r_asi +CASXA 11 ..... 111110 ..... . ............. @casa_imm + +NOP_v9 11 ----- 101101 ----- 0 00000000 ----- # PREFETCH +NOP_v9 11 ----- 101101 ----- 1 ------------- # PREFETCH +NOP_v9 11 ----- 111101 ----- - ------------- # PREFETCHA + +{ + [ + LDFA 11 ..... 110000 ..... . ............. @r_r_r_asi + LDFA 11 ..... 110000 ..... . ............. @r_r_i_asi + ] + NCP 11 ----- 110000 ----- --------- ----- # v8 LDC +} +NCP 11 ----- 110001 ----- --------- ----- # v8 LDCSR +LDQFA 11 ..... 110010 ..... . ............. @q_r_r_asi +LDQFA 11 ..... 110010 ..... . ............. @q_r_i_asi +{ + [ + LDDFA 11 ..... 110011 ..... . ............. @d_r_r_asi + LDDFA 11 ..... 110011 ..... . ............. @d_r_i_asi + ] + NCP 11 ----- 110011 ----- --------- ----- # v8 LDDC +} + +{ + [ + STFA 11 ..... 110100 ..... . ............. @r_r_r_asi + STFA 11 ..... 110100 ..... . ............. @r_r_i_asi + ] + NCP 11 ----- 110100 ----- --------- ----- # v8 STC +} +NCP 11 ----- 110101 ----- --------- ----- # v8 STCSR +{ + [ + STQFA 11 ..... 110110 ..... . ............. @q_r_r_asi + STQFA 11 ..... 110110 ..... . ............. @q_r_i_asi + ] + NCP 11 ----- 110110 ----- --------- ----- # v8 STDCQ +} +{ + [ + STDFA 11 ..... 110111 ..... . ............. @d_r_r_asi + STDFA 11 ..... 110111 ..... . ............. @d_r_i_asi + ] + NCP 11 ----- 110111 ----- --------- ----- # v8 STDC +} diff --git a/target/sparc/int32_helper.c b/target/sparc/int32_helper.c index 82e8418e46..6b7d65b031 100644 --- a/target/sparc/int32_helper.c +++ b/target/sparc/int32_helper.c @@ -70,7 +70,7 @@ void cpu_check_irqs(CPUSPARCState *env) CPUState *cs; /* We should be holding the BQL before we mess with IRQs */ - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); if (env->pil_in && (env->interrupt_index == 0 || (env->interrupt_index & ~15) == TT_EXTINT)) { @@ -99,15 +99,9 @@ void cpu_check_irqs(CPUSPARCState *env) void sparc_cpu_do_interrupt(CPUState *cs) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; + CPUSPARCState *env = cpu_env(cs); int cwp, intno = cs->exception_index; - /* Compute PSR before exposing state. */ - if (env->cc_op != CC_OP_FLAGS) { - cpu_get_psr(env); - } - if (qemu_loglevel_mask(CPU_LOG_INT)) { static int count; const char *name; @@ -165,7 +159,7 @@ void sparc_cpu_do_interrupt(CPUState *cs) #if !defined(CONFIG_USER_ONLY) /* IRQ acknowledgment */ if ((intno & ~15) == TT_EXTINT && env->qemu_irq_ack != NULL) { - env->qemu_irq_ack(env, env->irq_manager, intno); + env->qemu_irq_ack(env, intno); } #endif } diff --git a/target/sparc/int64_helper.c b/target/sparc/int64_helper.c index 793e57c536..bd14c7a0db 100644 --- a/target/sparc/int64_helper.c +++ b/target/sparc/int64_helper.c @@ -69,7 +69,7 @@ void cpu_check_irqs(CPUSPARCState *env) (env->softint & ~(SOFTINT_TIMER | SOFTINT_STIMER)); /* We should be holding the BQL before we mess with IRQs */ - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); /* TT_IVEC has a higher priority (16) than TT_EXTINT (31..17) */ if (env->ivec_status & 0x20) { @@ -130,16 +130,10 @@ void cpu_check_irqs(CPUSPARCState *env) void sparc_cpu_do_interrupt(CPUState *cs) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; + CPUSPARCState *env = cpu_env(cs); int intno = cs->exception_index; trap_state *tsptr; - /* Compute PSR before exposing state. */ - if (env->cc_op != CC_OP_FLAGS) { - cpu_get_psr(env); - } - #ifdef DEBUG_PCALL if (qemu_loglevel_mask(CPU_LOG_INT)) { static int count; @@ -272,9 +266,9 @@ static bool do_modify_softint(CPUSPARCState *env, uint32_t value) env->softint = value; #if !defined(CONFIG_USER_ONLY) if (cpu_interrupts_enabled(env)) { - qemu_mutex_lock_iothread(); + bql_lock(); cpu_check_irqs(env); - qemu_mutex_unlock_iothread(); + bql_unlock(); } #endif return true; diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index ec4fae78c3..2846a86cc4 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -66,9 +66,6 @@ #endif #endif -#define QT0 (env->qt0) -#define QT1 (env->qt1) - #if defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) /* Calculates TSB pointer value for fault page size * UltraSPARC IIi has fixed sizes (8k or 64k) for the page pointers @@ -360,6 +357,7 @@ static inline void do_check_asi(CPUSPARCState *env, int asi, uintptr_t ra) #endif /* !CONFIG_USER_ONLY */ #endif +#if defined(TARGET_SPARC64) || !defined(CONFIG_USER_ONLY) static void do_check_align(CPUSPARCState *env, target_ulong addr, uint32_t align, uintptr_t ra) { @@ -367,11 +365,7 @@ static void do_check_align(CPUSPARCState *env, target_ulong addr, cpu_raise_exception_ra(env, TT_UNALIGNED, ra); } } - -void helper_check_align(CPUSPARCState *env, target_ulong addr, uint32_t align) -{ - do_check_align(env, addr, align, GETPC()); -} +#endif #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) && \ defined(DEBUG_MXCC) @@ -424,18 +418,17 @@ static void sparc_raise_mmu_fault(CPUState *cs, hwaddr addr, bool is_write, bool is_exec, int is_asi, unsigned size, uintptr_t retaddr) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; + CPUSPARCState *env = cpu_env(cs); int fault_type; #ifdef DEBUG_UNASSIGNED if (is_asi) { - printf("Unassigned mem %s access of %d byte%s to " TARGET_FMT_plx + printf("Unassigned mem %s access of %d byte%s to " HWADDR_FMT_plx " asi 0x%02x from " TARGET_FMT_lx "\n", is_exec ? "exec" : is_write ? "write" : "read", size, size == 1 ? "" : "s", addr, is_asi, env->pc); } else { - printf("Unassigned mem %s access of %d byte%s to " TARGET_FMT_plx + printf("Unassigned mem %s access of %d byte%s to " HWADDR_FMT_plx " from " TARGET_FMT_lx "\n", is_exec ? "exec" : is_write ? "write" : "read", size, size == 1 ? "" : "s", addr, env->pc); @@ -486,11 +479,10 @@ static void sparc_raise_mmu_fault(CPUState *cs, hwaddr addr, bool is_write, bool is_exec, int is_asi, unsigned size, uintptr_t retaddr) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; + CPUSPARCState *env = cpu_env(cs); #ifdef DEBUG_UNASSIGNED - printf("Unassigned mem access to " TARGET_FMT_plx " from " TARGET_FMT_lx + printf("Unassigned mem access to " HWADDR_FMT_plx " from " TARGET_FMT_lx "\n", addr, env->pc); #endif @@ -691,23 +683,6 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case ASI_M_DIAGS: /* Turbosparc DTLB Diagnostic */ case ASI_M_IODIAG: /* Turbosparc IOTLB Diagnostic */ break; - case ASI_KERNELTXT: /* Supervisor code access */ - switch (size) { - case 1: - ret = cpu_ldub_code(env, addr); - break; - case 2: - ret = cpu_lduw_code(env, addr); - break; - default: - case 4: - ret = cpu_ldl_code(env, addr); - break; - case 8: - ret = cpu_ldq_code(env, addr); - break; - } - break; case ASI_M_TXTC_TAG: /* SparcStation 5 I-cache tag */ case ASI_M_TXTC_DATA: /* SparcStation 5 I-cache data */ case ASI_M_DATAC_TAG: /* SparcStation 5 D-cache tag */ @@ -785,7 +760,6 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case 0x4c: /* SuperSPARC MMU Breakpoint Action */ ret = env->mmubpaction; break; - case ASI_USERTXT: /* User code access, XXX */ default: sparc_raise_mmu_fault(cs, addr, false, false, asi, size, GETPC()); ret = 0; @@ -793,6 +767,8 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case ASI_USERDATA: /* User data access */ case ASI_KERNELDATA: /* Supervisor data access */ + case ASI_USERTXT: /* User code access */ + case ASI_KERNELTXT: /* Supervisor code access */ case ASI_P: /* Implicit primary context data access (v9 only?) */ case ASI_M_BYPASS: /* MMU passthrough */ case ASI_LEON_BYPASS: /* LEON MMU passthrough */ @@ -1167,6 +1143,49 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, #endif } +uint64_t helper_ld_code(CPUSPARCState *env, target_ulong addr, uint32_t oi) +{ + MemOp mop = get_memop(oi); + uintptr_t ra = GETPC(); + uint64_t ret; + + switch (mop & MO_SIZE) { + case MO_8: + ret = cpu_ldb_code_mmu(env, addr, oi, ra); + if (mop & MO_SIGN) { + ret = (int8_t)ret; + } + break; + case MO_16: + ret = cpu_ldw_code_mmu(env, addr, oi, ra); + if ((mop & MO_BSWAP) != MO_TE) { + ret = bswap16(ret); + } + if (mop & MO_SIGN) { + ret = (int16_t)ret; + } + break; + case MO_32: + ret = cpu_ldl_code_mmu(env, addr, oi, ra); + if ((mop & MO_BSWAP) != MO_TE) { + ret = bswap32(ret); + } + if (mop & MO_SIGN) { + ret = (int32_t)ret; + } + break; + case MO_64: + ret = cpu_ldq_code_mmu(env, addr, oi, ra); + if ((mop & MO_BSWAP) != MO_TE) { + ret = bswap64(ret); + } + break; + default: + g_assert_not_reached(); + } + return ret; +} + #endif /* CONFIG_USER_ONLY */ #else /* TARGET_SPARC64 */ @@ -1189,7 +1208,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case ASI_PNFL: /* Primary no-fault LE */ case ASI_SNF: /* Secondary no-fault */ case ASI_SNFL: /* Secondary no-fault LE */ - if (page_check_range(addr, size, PAGE_READ) == -1) { + if (!page_check_range(addr, size, PAGE_READ)) { ret = 0; break; } @@ -1332,25 +1351,13 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, ret = cpu_ldb_mmu(env, addr, oi, GETPC()); break; case 2: - if (asi & 8) { - ret = cpu_ldw_le_mmu(env, addr, oi, GETPC()); - } else { - ret = cpu_ldw_be_mmu(env, addr, oi, GETPC()); - } + ret = cpu_ldw_mmu(env, addr, oi, GETPC()); break; case 4: - if (asi & 8) { - ret = cpu_ldl_le_mmu(env, addr, oi, GETPC()); - } else { - ret = cpu_ldl_be_mmu(env, addr, oi, GETPC()); - } + ret = cpu_ldl_mmu(env, addr, oi, GETPC()); break; case 8: - if (asi & 8) { - ret = cpu_ldq_le_mmu(env, addr, oi, GETPC()); - } else { - ret = cpu_ldq_be_mmu(env, addr, oi, GETPC()); - } + ret = cpu_ldq_mmu(env, addr, oi, GETPC()); break; default: g_assert_not_reached(); @@ -1663,7 +1670,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, int idx = ((asi & 2) >> 1) | ((asi & 8) >> 2); env->dmmu.sun4v_tsb_pointers[idx] = val; } else { - helper_raise_exception(env, TT_ILL_INSN); + goto illegal_insn; } break; case 0x33: @@ -1675,7 +1682,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, */ env->dmmu.sun4v_ctx_config[(asi & 8) >> 3] = val; } else { - helper_raise_exception(env, TT_ILL_INSN); + goto illegal_insn; } break; case 0x35: @@ -1692,7 +1699,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, int idx = ((asi & 2) >> 1) | ((asi & 8) >> 2); env->immu.sun4v_tsb_pointers[idx] = val; } else { - helper_raise_exception(env, TT_ILL_INSN); + goto illegal_insn; } break; case 0x37: @@ -1704,7 +1711,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, */ env->immu.sun4v_ctx_config[(asi & 8) >> 3] = val; } else { - helper_raise_exception(env, TT_ILL_INSN); + goto illegal_insn; } break; case ASI_UPA_CONFIG: /* UPA config */ @@ -1933,6 +1940,8 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, default: sparc_raise_mmu_fault(cs, addr, true, false, 1, size, GETPC()); return; + illegal_insn: + cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC()); } } #endif /* CONFIG_USER_ONLY */ diff --git a/target/sparc/machine.c b/target/sparc/machine.c index 44b9e7d75d..48e0cf22f3 100644 --- a/target/sparc/machine.c +++ b/target/sparc/machine.c @@ -10,7 +10,7 @@ static const VMStateDescription vmstate_cpu_timer = { .name = "cpu_timer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(frequency, CPUTimer), VMSTATE_UINT32(disabled, CPUTimer), VMSTATE_UINT64(disabled_mask, CPUTimer), @@ -29,7 +29,7 @@ static const VMStateDescription vmstate_trap_state = { .name = "trap_state", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(tpc, trap_state), VMSTATE_UINT64(tnpc, trap_state), VMSTATE_UINT64(tstate, trap_state), @@ -42,7 +42,7 @@ static const VMStateDescription vmstate_tlb_entry = { .name = "tlb_entry", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(tag, SparcTLBEntry), VMSTATE_UINT64(tte, SparcTLBEntry), VMSTATE_END_OF_LIST() @@ -83,6 +83,68 @@ static const VMStateInfo vmstate_psr = { .put = put_psr, }; +static int get_fsr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + SPARCCPU *cpu = opaque; + target_ulong val = qemu_get_betl(f); + + cpu_put_fsr(&cpu->env, val); + return 0; +} + +static int put_fsr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + SPARCCPU *cpu = opaque; + target_ulong val = cpu_get_fsr(&cpu->env); + + qemu_put_betl(f, val); + return 0; +} + +static const VMStateInfo vmstate_fsr = { + .name = "fsr", + .get = get_fsr, + .put = put_fsr, +}; + +#ifdef TARGET_SPARC64 +static int get_xcc(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + uint32_t val = qemu_get_be32(f); + + /* Do not clobber icc.[NV] */ + env->cc_N = deposit64(env->cc_N, 32, 32, -(val & PSR_NEG)); + env->cc_V = deposit64(env->cc_V, 32, 32, -(val & PSR_OVF)); + env->xcc_Z = ~val & PSR_ZERO; + env->xcc_C = (val >> PSR_CARRY_SHIFT) & 1; + + return 0; +} + +static int put_xcc(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + uint32_t val = cpu_get_ccr(env); + + /* Extract just xcc out of ccr and shift into legacy position. */ + qemu_put_be32(f, (val & 0xf0) << (20 - 4)); + return 0; +} + +static const VMStateInfo vmstate_xcc = { + .name = "xcc", + .get = get_xcc, + .put = put_xcc, +}; +#endif + static int cpu_pre_save(void *opaque) { SPARCCPU *cpu = opaque; @@ -111,7 +173,7 @@ const VMStateDescription vmstate_sparc_cpu = { .version_id = SPARC_VMSTATE_VER, .minimum_version_id = SPARC_VMSTATE_VER, .pre_save = cpu_pre_save, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.gregs, SPARCCPU, 8), VMSTATE_UINT32(env.nwindows, SPARCCPU), VMSTATE_VARRAY_MULTIPLY(env.regbase, SPARCCPU, env.nwindows, 16, @@ -121,7 +183,6 @@ const VMStateDescription vmstate_sparc_cpu = { VMSTATE_UINTTL(env.npc, SPARCCPU), VMSTATE_UINTTL(env.y, SPARCCPU), { - .name = "psr", .version_id = 0, .size = sizeof(uint32_t), @@ -129,7 +190,14 @@ const VMStateDescription vmstate_sparc_cpu = { .flags = VMS_SINGLE, .offset = 0, }, - VMSTATE_UINTTL(env.fsr, SPARCCPU), + { + .name = "fsr", + .version_id = 0, + .size = sizeof(target_ulong), + .info = &vmstate_fsr, + .flags = VMS_SINGLE, + .offset = 0, + }, VMSTATE_UINTTL(env.tbr, SPARCCPU), VMSTATE_INT32(env.interrupt_index, SPARCCPU), VMSTATE_UINT32(env.pil_in, SPARCCPU), @@ -155,7 +223,14 @@ const VMStateDescription vmstate_sparc_cpu = { VMSTATE_UINT32(env.mmu_version, SPARCCPU), VMSTATE_STRUCT_ARRAY(env.ts, SPARCCPU, MAXTL_MAX, 0, vmstate_trap_state, trap_state), - VMSTATE_UINT32(env.xcc, SPARCCPU), + { + .name = "xcc", + .version_id = 0, + .size = sizeof(uint32_t), + .info = &vmstate_xcc, + .flags = VMS_SINGLE, + .offset = 0, + }, VMSTATE_UINT32(env.asi, SPARCCPU), VMSTATE_UINT32(env.pstate, SPARCCPU), VMSTATE_UINT32(env.tl, SPARCCPU), @@ -168,7 +243,8 @@ const VMStateDescription vmstate_sparc_cpu = { VMSTATE_UINT64_ARRAY(env.bgregs, SPARCCPU, 8), VMSTATE_UINT64_ARRAY(env.igregs, SPARCCPU, 8), VMSTATE_UINT64_ARRAY(env.mgregs, SPARCCPU, 8), - VMSTATE_UINT64(env.fprs, SPARCCPU), + VMSTATE_UNUSED(4), /* was unused high half of uint64_t fprs */ + VMSTATE_UINT32(env.fprs, SPARCCPU), VMSTATE_UINT64(env.tick_cmpr, SPARCCPU), VMSTATE_UINT64(env.stick_cmpr, SPARCCPU), VMSTATE_CPU_TIMER(env.tick, SPARCCPU), diff --git a/target/sparc/meson.build b/target/sparc/meson.build index a801802ee2..46289c8669 100644 --- a/target/sparc/meson.build +++ b/target/sparc/meson.build @@ -1,6 +1,8 @@ +gen = decodetree.process('insns.decode') + sparc_ss = ss.source_set() +sparc_ss.add(gen) sparc_ss.add(files( - 'cc_helper.c', 'cpu.c', 'fop_helper.c', 'gdbstub.c', @@ -12,12 +14,12 @@ sparc_ss.add(files( sparc_ss.add(when: 'TARGET_SPARC', if_true: files('int32_helper.c')) sparc_ss.add(when: 'TARGET_SPARC64', if_true: files('int64_helper.c', 'vis_helper.c')) -sparc_softmmu_ss = ss.source_set() -sparc_softmmu_ss.add(files( +sparc_system_ss = ss.source_set() +sparc_system_ss.add(files( 'machine.c', 'mmu_helper.c', 'monitor.c', )) target_arch += {'sparc': sparc_ss} -target_softmmu_arch += {'sparc': sparc_softmmu_ss} +target_system_arch += {'sparc': sparc_system_ss} diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index 919448a494..ad1591d9fd 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -64,10 +64,9 @@ static const int perm_table[2][8] = { } }; -static int get_physical_address(CPUSPARCState *env, hwaddr *physical, - int *prot, int *access_index, MemTxAttrs *attrs, - target_ulong address, int rw, int mmu_idx, - target_ulong *page_size) +static int get_physical_address(CPUSPARCState *env, CPUTLBEntryFull *full, + int *access_index, target_ulong address, + int rw, int mmu_idx) { int access_perms = 0; hwaddr pde_ptr; @@ -80,20 +79,20 @@ static int get_physical_address(CPUSPARCState *env, hwaddr *physical, is_user = mmu_idx == MMU_USER_IDX; if (mmu_idx == MMU_PHYS_IDX) { - *page_size = TARGET_PAGE_SIZE; + full->lg_page_size = TARGET_PAGE_BITS; /* Boot mode: instruction fetches are taken from PROM */ if (rw == 2 && (env->mmuregs[0] & env->def.mmu_bm)) { - *physical = env->prom_addr | (address & 0x7ffffULL); - *prot = PAGE_READ | PAGE_EXEC; + full->phys_addr = env->prom_addr | (address & 0x7ffffULL); + full->prot = PAGE_READ | PAGE_EXEC; return 0; } - *physical = address; - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + full->phys_addr = address; + full->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return 0; } *access_index = ((rw & 1) << 2) | (rw & 2) | (is_user ? 0 : 1); - *physical = 0xffffffffffff0000ULL; + full->phys_addr = 0xffffffffffff0000ULL; /* SPARC reference MMU table walk: Context table->L1->L2->PTE */ /* Context base + context number */ @@ -157,16 +156,17 @@ static int get_physical_address(CPUSPARCState *env, hwaddr *physical, case 2: /* L3 PTE */ page_offset = 0; } - *page_size = TARGET_PAGE_SIZE; + full->lg_page_size = TARGET_PAGE_BITS; break; case 2: /* L2 PTE */ page_offset = address & 0x3f000; - *page_size = 0x40000; + full->lg_page_size = 18; } break; case 2: /* L1 PTE */ page_offset = address & 0xfff000; - *page_size = 0x1000000; + full->lg_page_size = 24; + break; } } @@ -188,16 +188,16 @@ static int get_physical_address(CPUSPARCState *env, hwaddr *physical, } /* the page can be put in the TLB */ - *prot = perm_table[is_user][access_perms]; + full->prot = perm_table[is_user][access_perms]; if (!(pde & PG_MODIFIED_MASK)) { /* only set write access if already dirty... otherwise wait for dirty access */ - *prot &= ~PAGE_WRITE; + full->prot &= ~PAGE_WRITE; } /* Even if large ptes, we map only one 4KB page in the cache to avoid filling it too fast */ - *physical = ((hwaddr)(pde & PTE_ADDR_MASK) << 4) + page_offset; + full->phys_addr = ((hwaddr)(pde & PTE_ADDR_MASK) << 4) + page_offset; return error_code; } @@ -206,13 +206,10 @@ bool sparc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; - hwaddr paddr; + CPUSPARCState *env = cpu_env(cs); + CPUTLBEntryFull full = {}; target_ulong vaddr; - target_ulong page_size; - int error_code = 0, prot, access_index; - MemTxAttrs attrs = {}; + int error_code = 0, access_index; /* * TODO: If we ever need tlb_vaddr_to_host for this target, @@ -223,16 +220,15 @@ bool sparc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, assert(!probe); address &= TARGET_PAGE_MASK; - error_code = get_physical_address(env, &paddr, &prot, &access_index, &attrs, - address, access_type, - mmu_idx, &page_size); + error_code = get_physical_address(env, &full, &access_index, + address, access_type, mmu_idx); vaddr = address; if (likely(error_code == 0)) { qemu_log_mask(CPU_LOG_MMU, "Translate at %" VADDR_PRIx " -> " - TARGET_FMT_plx ", vaddr " TARGET_FMT_lx "\n", - address, paddr, vaddr); - tlb_set_page(cs, vaddr, paddr, prot, mmu_idx, page_size); + HWADDR_FMT_plx ", vaddr " TARGET_FMT_lx "\n", + address, full.phys_addr, vaddr); + tlb_set_page_full(cs, mmu_idx, vaddr, &full); return true; } @@ -247,8 +243,8 @@ bool sparc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, permissions. If no mapping is available, redirect accesses to neverland. Fake/overridden mappings will be flushed when switching to normal mode. */ - prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - tlb_set_page(cs, vaddr, paddr, prot, mmu_idx, TARGET_PAGE_SIZE); + full.prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + tlb_set_page_full(cs, mmu_idx, vaddr, &full); return true; } else { if (access_type == MMU_INST_FETCH) { @@ -356,27 +352,27 @@ void dump_mmu(CPUSPARCState *env) hwaddr pa; uint32_t pde; - qemu_printf("Root ptr: " TARGET_FMT_plx ", ctx: %d\n", + qemu_printf("Root ptr: " HWADDR_FMT_plx ", ctx: %d\n", (hwaddr)env->mmuregs[1] << 4, env->mmuregs[2]); for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) { pde = mmu_probe(env, va, 2); if (pde) { pa = cpu_get_phys_page_debug(cs, va); - qemu_printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_plx + qemu_printf("VA: " TARGET_FMT_lx ", PA: " HWADDR_FMT_plx " PDE: " TARGET_FMT_lx "\n", va, pa, pde); for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) { pde = mmu_probe(env, va1, 1); if (pde) { pa = cpu_get_phys_page_debug(cs, va1); qemu_printf(" VA: " TARGET_FMT_lx ", PA: " - TARGET_FMT_plx " PDE: " TARGET_FMT_lx "\n", + HWADDR_FMT_plx " PDE: " TARGET_FMT_lx "\n", va1, pa, pde); for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) { pde = mmu_probe(env, va2, 0); if (pde) { pa = cpu_get_phys_page_debug(cs, va2); qemu_printf(" VA: " TARGET_FMT_lx ", PA: " - TARGET_FMT_plx " PTE: " + HWADDR_FMT_plx " PTE: " TARGET_FMT_lx "\n", va2, pa, pde); } @@ -394,8 +390,7 @@ void dump_mmu(CPUSPARCState *env) int sparc_cpu_memory_rw_debug(CPUState *cs, vaddr address, uint8_t *buf, int len, bool is_write) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; + CPUSPARCState *env = cpu_env(cs); target_ulong addr = address; int i; int len1; @@ -545,8 +540,7 @@ static uint64_t build_sfsr(CPUSPARCState *env, int mmu_idx, int rw) return sfsr; } -static int get_physical_address_data(CPUSPARCState *env, hwaddr *physical, - int *prot, MemTxAttrs *attrs, +static int get_physical_address_data(CPUSPARCState *env, CPUTLBEntryFull *full, target_ulong address, int rw, int mmu_idx) { CPUState *cs = env_cpu(env); @@ -579,11 +573,12 @@ static int get_physical_address_data(CPUSPARCState *env, hwaddr *physical, for (i = 0; i < 64; i++) { /* ctx match, vaddr match, valid? */ - if (ultrasparc_tag_match(&env->dtlb[i], address, context, physical)) { + if (ultrasparc_tag_match(&env->dtlb[i], address, context, + &full->phys_addr)) { int do_fault = 0; if (TTE_IS_IE(env->dtlb[i].tte)) { - attrs->byte_swap = true; + full->tlb_fill_flags |= TLB_BSWAP; } /* access ok? */ @@ -616,9 +611,9 @@ static int get_physical_address_data(CPUSPARCState *env, hwaddr *physical, } if (!do_fault) { - *prot = PAGE_READ; + full->prot = PAGE_READ; if (TTE_IS_W_OK(env->dtlb[i].tte)) { - *prot |= PAGE_WRITE; + full->prot |= PAGE_WRITE; } TTE_SET_USED(env->dtlb[i].tte); @@ -645,8 +640,7 @@ static int get_physical_address_data(CPUSPARCState *env, hwaddr *physical, return 1; } -static int get_physical_address_code(CPUSPARCState *env, hwaddr *physical, - int *prot, MemTxAttrs *attrs, +static int get_physical_address_code(CPUSPARCState *env, CPUTLBEntryFull *full, target_ulong address, int mmu_idx) { CPUState *cs = env_cpu(env); @@ -681,7 +675,7 @@ static int get_physical_address_code(CPUSPARCState *env, hwaddr *physical, for (i = 0; i < 64; i++) { /* ctx match, vaddr match, valid? */ if (ultrasparc_tag_match(&env->itlb[i], - address, context, physical)) { + address, context, &full->phys_addr)) { /* access ok? */ if (TTE_IS_PRIV(env->itlb[i].tte) && is_user) { /* Fault status register */ @@ -708,7 +702,7 @@ static int get_physical_address_code(CPUSPARCState *env, hwaddr *physical, return 1; } - *prot = PAGE_EXEC; + full->prot = PAGE_EXEC; TTE_SET_USED(env->itlb[i].tte); return 0; } @@ -722,14 +716,13 @@ static int get_physical_address_code(CPUSPARCState *env, hwaddr *physical, return 1; } -static int get_physical_address(CPUSPARCState *env, hwaddr *physical, - int *prot, int *access_index, MemTxAttrs *attrs, - target_ulong address, int rw, int mmu_idx, - target_ulong *page_size) +static int get_physical_address(CPUSPARCState *env, CPUTLBEntryFull *full, + int *access_index, target_ulong address, + int rw, int mmu_idx) { /* ??? We treat everything as a small page, then explicitly flush everything when an entry is evicted. */ - *page_size = TARGET_PAGE_SIZE; + full->lg_page_size = TARGET_PAGE_BITS; /* safety net to catch wrong softmmu index use from dynamic code */ if (env->tl > 0 && mmu_idx != MMU_NUCLEUS_IDX) { @@ -747,17 +740,15 @@ static int get_physical_address(CPUSPARCState *env, hwaddr *physical, } if (mmu_idx == MMU_PHYS_IDX) { - *physical = ultrasparc_truncate_physical(address); - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + full->phys_addr = ultrasparc_truncate_physical(address); + full->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return 0; } if (rw == 2) { - return get_physical_address_code(env, physical, prot, attrs, address, - mmu_idx); + return get_physical_address_code(env, full, address, mmu_idx); } else { - return get_physical_address_data(env, physical, prot, attrs, address, - rw, mmu_idx); + return get_physical_address_data(env, full, address, rw, mmu_idx); } } @@ -766,27 +757,18 @@ bool sparc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; - target_ulong vaddr; - hwaddr paddr; - target_ulong page_size; - MemTxAttrs attrs = {}; - int error_code = 0, prot, access_index; + CPUSPARCState *env = cpu_env(cs); + CPUTLBEntryFull full = {}; + int error_code = 0, access_index; address &= TARGET_PAGE_MASK; - error_code = get_physical_address(env, &paddr, &prot, &access_index, &attrs, - address, access_type, - mmu_idx, &page_size); + error_code = get_physical_address(env, &full, &access_index, + address, access_type, mmu_idx); if (likely(error_code == 0)) { - vaddr = address; - - trace_mmu_helper_mmu_fault(address, paddr, mmu_idx, env->tl, + trace_mmu_helper_mmu_fault(address, full.phys_addr, mmu_idx, env->tl, env->dmmu.mmu_primary_context, env->dmmu.mmu_secondary_context); - - tlb_set_page_with_attrs(cs, vaddr, paddr, attrs, prot, mmu_idx, - page_size); + tlb_set_page_full(cs, mmu_idx, address, &full); return true; } if (probe) { @@ -888,12 +870,14 @@ void dump_mmu(CPUSPARCState *env) static int cpu_sparc_get_phys_page(CPUSPARCState *env, hwaddr *phys, target_ulong addr, int rw, int mmu_idx) { - target_ulong page_size; - int prot, access_index; - MemTxAttrs attrs = {}; + CPUTLBEntryFull full = {}; + int access_index, ret; - return get_physical_address(env, phys, &prot, &access_index, &attrs, addr, - rw, mmu_idx, &page_size); + ret = get_physical_address(env, &full, &access_index, addr, rw, mmu_idx); + if (ret == 0) { + *phys = full.phys_addr; + } + return ret; } #if defined(TARGET_SPARC64) @@ -911,10 +895,9 @@ hwaddr cpu_get_phys_page_nofault(CPUSPARCState *env, target_ulong addr, hwaddr sparc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; + CPUSPARCState *env = cpu_env(cs); hwaddr phys_addr; - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = cpu_mmu_index(cs, false); if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 2, mmu_idx) != 0) { if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 0, mmu_idx) != 0) { @@ -924,14 +907,12 @@ hwaddr sparc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) return phys_addr; } -#ifndef CONFIG_USER_ONLY G_NORETURN void sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; + CPUSPARCState *env = cpu_env(cs); #ifdef TARGET_SPARC64 env->dmmu.sfsr = build_sfsr(env, mmu_idx, access_type); @@ -942,4 +923,3 @@ G_NORETURN void sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, cpu_raise_exception_ra(env, TT_UNALIGNED, retaddr); } -#endif /* !CONFIG_USER_ONLY */ diff --git a/target/sparc/monitor.c b/target/sparc/monitor.c index 318413686a..73f15aa272 100644 --- a/target/sparc/monitor.c +++ b/target/sparc/monitor.c @@ -154,7 +154,7 @@ const MonitorDef monitor_defs[] = { { "otherwin", offsetof(CPUSPARCState, otherwin) }, { "wstate", offsetof(CPUSPARCState, wstate) }, { "cleanwin", offsetof(CPUSPARCState, cleanwin) }, - { "fprs", offsetof(CPUSPARCState, fprs) }, + { "fprs", offsetof(CPUSPARCState, fprs), NULL, MD_I32 }, #endif { NULL }, }; diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 2e28222d31..571b3e3f03 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -25,54 +25,147 @@ #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" -#include "exec/cpu_ldst.h" - +#include "tcg/tcg-op-gvec.h" #include "exec/helper-gen.h" - #include "exec/translator.h" #include "exec/log.h" #include "asi.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H -#define DEBUG_DISAS +#ifdef TARGET_SPARC64 +# define gen_helper_rdpsr(D, E) qemu_build_not_reached() +# define gen_helper_rdasr17(D, E) qemu_build_not_reached() +# define gen_helper_rett(E) qemu_build_not_reached() +# define gen_helper_power_down(E) qemu_build_not_reached() +# define gen_helper_wrpsr(E, S) qemu_build_not_reached() +#else +# define gen_helper_clear_softint(E, S) qemu_build_not_reached() +# define gen_helper_done(E) qemu_build_not_reached() +# define gen_helper_flushw(E) qemu_build_not_reached() +# define gen_helper_rdccr(D, E) qemu_build_not_reached() +# define gen_helper_rdcwp(D, E) qemu_build_not_reached() +# define gen_helper_restored(E) qemu_build_not_reached() +# define gen_helper_retry(E) qemu_build_not_reached() +# define gen_helper_saved(E) qemu_build_not_reached() +# define gen_helper_set_softint(E, S) qemu_build_not_reached() +# define gen_helper_tick_get_count(D, E, T, C) qemu_build_not_reached() +# define gen_helper_tick_set_count(P, S) qemu_build_not_reached() +# define gen_helper_tick_set_limit(P, S) qemu_build_not_reached() +# define gen_helper_wrccr(E, S) qemu_build_not_reached() +# define gen_helper_wrcwp(E, S) qemu_build_not_reached() +# define gen_helper_wrgl(E, S) qemu_build_not_reached() +# define gen_helper_write_softint(E, S) qemu_build_not_reached() +# define gen_helper_wrpil(E, S) qemu_build_not_reached() +# define gen_helper_wrpstate(E, S) qemu_build_not_reached() +# define gen_helper_fcmpeq16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpeq32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpgt16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpgt32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmple16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmple32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpne16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpne32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fdtox ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fexpand ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fmul8sux16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fmul8ulx16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fmul8x16al ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fmul8x16au ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fmul8x16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fmuld8sux16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fmuld8ulx16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fpmerge ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fqtox ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fstox ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fxtod ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fxtoq ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fxtos ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_pdist ({ qemu_build_not_reached(); NULL; }) +# define MAXTL_MASK 0 +#endif -#define DYNAMIC_PC 1 /* dynamic pc value */ -#define JUMP_PC 2 /* dynamic pc value which takes only two values - according to jump_pc[T2] */ +/* Dynamic PC, must exit to main loop. */ +#define DYNAMIC_PC 1 +/* Dynamic PC, one of two values according to jump_pc[T2]. */ +#define JUMP_PC 2 +/* Dynamic PC, may lookup next TB. */ +#define DYNAMIC_PC_LOOKUP 3 #define DISAS_EXIT DISAS_TARGET_0 /* global register indexes */ static TCGv_ptr cpu_regwptr; -static TCGv cpu_cc_src, cpu_cc_src2, cpu_cc_dst; -static TCGv_i32 cpu_cc_op; -static TCGv_i32 cpu_psr; -static TCGv cpu_fsr, cpu_pc, cpu_npc; +static TCGv cpu_pc, cpu_npc; static TCGv cpu_regs[32]; static TCGv cpu_y; -#ifndef CONFIG_USER_ONLY static TCGv cpu_tbr; -#endif static TCGv cpu_cond; +static TCGv cpu_cc_N; +static TCGv cpu_cc_V; +static TCGv cpu_icc_Z; +static TCGv cpu_icc_C; #ifdef TARGET_SPARC64 -static TCGv_i32 cpu_xcc, cpu_fprs; +static TCGv cpu_xcc_Z; +static TCGv cpu_xcc_C; +static TCGv_i32 cpu_fprs; static TCGv cpu_gsr; -static TCGv cpu_tick_cmpr, cpu_stick_cmpr, cpu_hstick_cmpr; -static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr, cpu_ver; #else -static TCGv cpu_wim; +# define cpu_fprs ({ qemu_build_not_reached(); (TCGv)NULL; }) +# define cpu_gsr ({ qemu_build_not_reached(); (TCGv)NULL; }) #endif + +#ifdef TARGET_SPARC64 +#define cpu_cc_Z cpu_xcc_Z +#define cpu_cc_C cpu_xcc_C +#else +#define cpu_cc_Z cpu_icc_Z +#define cpu_cc_C cpu_icc_C +#define cpu_xcc_Z ({ qemu_build_not_reached(); NULL; }) +#define cpu_xcc_C ({ qemu_build_not_reached(); NULL; }) +#endif + /* Floating point registers */ static TCGv_i64 cpu_fpr[TARGET_DPREGS]; +static TCGv_i32 cpu_fcc[TARGET_FCCREGS]; -#include "exec/gen-icount.h" +#define env_field_offsetof(X) offsetof(CPUSPARCState, X) +#ifdef TARGET_SPARC64 +# define env32_field_offsetof(X) ({ qemu_build_not_reached(); 0; }) +# define env64_field_offsetof(X) env_field_offsetof(X) +#else +# define env32_field_offsetof(X) env_field_offsetof(X) +# define env64_field_offsetof(X) ({ qemu_build_not_reached(); 0; }) +#endif + +typedef struct DisasCompare { + TCGCond cond; + TCGv c1; + int c2; +} DisasCompare; + +typedef struct DisasDelayException { + struct DisasDelayException *next; + TCGLabel *lab; + TCGv_i32 excp; + /* Saved state at parent insn. */ + target_ulong pc; + target_ulong npc; +} DisasDelayException; typedef struct DisasContext { DisasContextBase base; target_ulong pc; /* current Program Counter: integer or DYNAMIC_PC */ target_ulong npc; /* next PC: integer or DYNAMIC_PC or JUMP_PC */ - target_ulong jump_pc[2]; /* used when JUMP_PC pc value is used */ + + /* Used when JUMP_PC value is used. */ + DisasCompare jump; + target_ulong jump_pc[2]; + int mem_idx; + bool cpu_cond_live; bool fpu_enabled; bool address_mask_32bit; #ifndef CONFIG_USER_ONLY @@ -82,25 +175,14 @@ typedef struct DisasContext { #endif #endif - uint32_t cc_op; /* current CC operation */ sparc_def_t *def; - TCGv_i32 t32[3]; - TCGv ttl[5]; - int n_t32; - int n_ttl; #ifdef TARGET_SPARC64 int fprs_dirty; int asi; #endif + DisasDelayException *delay_excp_list; } DisasContext; -typedef struct { - TCGCond cond; - bool is_bool; - bool g1, g2; - TCGv c1, c2; -} DisasCompare; - // This function uses non-native bit order #define GET_FIELD(X, FROM, TO) \ ((X) >> (31 - (TO)) & ((1 << ((TO) - (FROM) + 1)) - 1)) @@ -123,31 +205,9 @@ typedef struct { #define UA2005_HTRAP_MASK 0xff #define V8_TRAP_MASK 0x7f -static int sign_extend(int x, int len) -{ - len = 32 - len; - return (x << len) >> len; -} - #define IS_IMM (insn & (1<<13)) -static inline TCGv_i32 get_temp_i32(DisasContext *dc) -{ - TCGv_i32 t; - assert(dc->n_t32 < ARRAY_SIZE(dc->t32)); - dc->t32[dc->n_t32++] = t = tcg_temp_new_i32(); - return t; -} - -static inline TCGv get_temp_tl(DisasContext *dc) -{ - TCGv t; - assert(dc->n_ttl < ARRAY_SIZE(dc->ttl)); - dc->ttl[dc->n_ttl++] = t = tcg_temp_new(); - return t; -} - -static inline void gen_update_fprs_dirty(DisasContext *dc, int rd) +static void gen_update_fprs_dirty(DisasContext *dc, int rd) { #if defined(TARGET_SPARC64) int bit = (rd < 32) ? 1 : 2; @@ -163,44 +223,25 @@ static inline void gen_update_fprs_dirty(DisasContext *dc, int rd) /* floating point registers moves */ static TCGv_i32 gen_load_fpr_F(DisasContext *dc, unsigned int src) { -#if TCG_TARGET_REG_BITS == 32 - if (src & 1) { - return TCGV_LOW(cpu_fpr[src / 2]); - } else { - return TCGV_HIGH(cpu_fpr[src / 2]); - } -#else - TCGv_i32 ret = get_temp_i32(dc); + TCGv_i32 ret = tcg_temp_new_i32(); if (src & 1) { tcg_gen_extrl_i64_i32(ret, cpu_fpr[src / 2]); } else { tcg_gen_extrh_i64_i32(ret, cpu_fpr[src / 2]); } return ret; -#endif } static void gen_store_fpr_F(DisasContext *dc, unsigned int dst, TCGv_i32 v) { -#if TCG_TARGET_REG_BITS == 32 - if (dst & 1) { - tcg_gen_mov_i32(TCGV_LOW(cpu_fpr[dst / 2]), v); - } else { - tcg_gen_mov_i32(TCGV_HIGH(cpu_fpr[dst / 2]), v); - } -#else - TCGv_i64 t = (TCGv_i64)v; + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_extu_i32_i64(t, v); tcg_gen_deposit_i64(cpu_fpr[dst / 2], cpu_fpr[dst / 2], t, (dst & 1 ? 0 : 32), 32); -#endif gen_update_fprs_dirty(dc, dst); } -static TCGv_i32 gen_dest_fpr_F(DisasContext *dc) -{ - return get_temp_i32(dc); -} - static TCGv_i64 gen_load_fpr_D(DisasContext *dc, unsigned int src) { src = DFPREG(src); @@ -219,108 +260,71 @@ static TCGv_i64 gen_dest_fpr_D(DisasContext *dc, unsigned int dst) return cpu_fpr[DFPREG(dst) / 2]; } -static void gen_op_load_fpr_QT0(unsigned int src) +static TCGv_i128 gen_load_fpr_Q(DisasContext *dc, unsigned int src) { - tcg_gen_st_i64(cpu_fpr[src / 2], cpu_env, offsetof(CPUSPARCState, qt0) + - offsetof(CPU_QuadU, ll.upper)); - tcg_gen_st_i64(cpu_fpr[src/2 + 1], cpu_env, offsetof(CPUSPARCState, qt0) + - offsetof(CPU_QuadU, ll.lower)); + TCGv_i128 ret = tcg_temp_new_i128(); + + src = QFPREG(src); + tcg_gen_concat_i64_i128(ret, cpu_fpr[src / 2 + 1], cpu_fpr[src / 2]); + return ret; } -static void gen_op_load_fpr_QT1(unsigned int src) +static void gen_store_fpr_Q(DisasContext *dc, unsigned int dst, TCGv_i128 v) { - tcg_gen_st_i64(cpu_fpr[src / 2], cpu_env, offsetof(CPUSPARCState, qt1) + - offsetof(CPU_QuadU, ll.upper)); - tcg_gen_st_i64(cpu_fpr[src/2 + 1], cpu_env, offsetof(CPUSPARCState, qt1) + - offsetof(CPU_QuadU, ll.lower)); -} - -static void gen_op_store_QT0_fpr(unsigned int dst) -{ - tcg_gen_ld_i64(cpu_fpr[dst / 2], cpu_env, offsetof(CPUSPARCState, qt0) + - offsetof(CPU_QuadU, ll.upper)); - tcg_gen_ld_i64(cpu_fpr[dst/2 + 1], cpu_env, offsetof(CPUSPARCState, qt0) + - offsetof(CPU_QuadU, ll.lower)); -} - -static void gen_store_fpr_Q(DisasContext *dc, unsigned int dst, - TCGv_i64 v1, TCGv_i64 v2) -{ - dst = QFPREG(dst); - - tcg_gen_mov_i64(cpu_fpr[dst / 2], v1); - tcg_gen_mov_i64(cpu_fpr[dst / 2 + 1], v2); + dst = DFPREG(dst); + tcg_gen_extr_i128_i64(cpu_fpr[dst / 2 + 1], cpu_fpr[dst / 2], v); gen_update_fprs_dirty(dc, dst); } -#ifdef TARGET_SPARC64 -static TCGv_i64 gen_load_fpr_Q0(DisasContext *dc, unsigned int src) -{ - src = QFPREG(src); - return cpu_fpr[src / 2]; -} - -static TCGv_i64 gen_load_fpr_Q1(DisasContext *dc, unsigned int src) -{ - src = QFPREG(src); - return cpu_fpr[src / 2 + 1]; -} - -static void gen_move_Q(DisasContext *dc, unsigned int rd, unsigned int rs) -{ - rd = QFPREG(rd); - rs = QFPREG(rs); - - tcg_gen_mov_i64(cpu_fpr[rd / 2], cpu_fpr[rs / 2]); - tcg_gen_mov_i64(cpu_fpr[rd / 2 + 1], cpu_fpr[rs / 2 + 1]); - gen_update_fprs_dirty(dc, rd); -} -#endif - /* moves */ #ifdef CONFIG_USER_ONLY #define supervisor(dc) 0 -#ifdef TARGET_SPARC64 #define hypervisor(dc) 0 -#endif #else #ifdef TARGET_SPARC64 #define hypervisor(dc) (dc->hypervisor) #define supervisor(dc) (dc->supervisor | dc->hypervisor) #else #define supervisor(dc) (dc->supervisor) +#define hypervisor(dc) 0 #endif #endif -#ifdef TARGET_SPARC64 -#ifndef TARGET_ABI32 -#define AM_CHECK(dc) ((dc)->address_mask_32bit) +#if !defined(TARGET_SPARC64) +# define AM_CHECK(dc) false +#elif defined(TARGET_ABI32) +# define AM_CHECK(dc) true +#elif defined(CONFIG_USER_ONLY) +# define AM_CHECK(dc) false #else -#define AM_CHECK(dc) (1) -#endif +# define AM_CHECK(dc) ((dc)->address_mask_32bit) #endif -static inline void gen_address_mask(DisasContext *dc, TCGv addr) +static void gen_address_mask(DisasContext *dc, TCGv addr) { -#ifdef TARGET_SPARC64 - if (AM_CHECK(dc)) + if (AM_CHECK(dc)) { tcg_gen_andi_tl(addr, addr, 0xffffffffULL); -#endif + } } -static inline TCGv gen_load_gpr(DisasContext *dc, int reg) +static target_ulong address_mask_i(DisasContext *dc, target_ulong addr) +{ + return AM_CHECK(dc) ? (uint32_t)addr : addr; +} + +static TCGv gen_load_gpr(DisasContext *dc, int reg) { if (reg > 0) { assert(reg < 32); return cpu_regs[reg]; } else { - TCGv t = get_temp_tl(dc); + TCGv t = tcg_temp_new(); tcg_gen_movi_tl(t, 0); return t; } } -static inline void gen_store_gpr(DisasContext *dc, int reg, TCGv v) +static void gen_store_gpr(DisasContext *dc, int reg, TCGv v) { if (reg > 0) { assert(reg < 32); @@ -328,13 +332,13 @@ static inline void gen_store_gpr(DisasContext *dc, int reg, TCGv v) } } -static inline TCGv gen_dest_gpr(DisasContext *dc, int reg) +static TCGv gen_dest_gpr(DisasContext *dc, int reg) { if (reg > 0) { assert(reg < 32); return cpu_regs[reg]; } else { - return get_temp_tl(dc); + return tcg_temp_new(); } } @@ -354,291 +358,172 @@ static void gen_goto_tb(DisasContext *s, int tb_num, tcg_gen_movi_tl(cpu_npc, npc); tcg_gen_exit_tb(s->base.tb, tb_num); } else { - /* jump to another page: currently not optimized */ + /* jump to another page: we can use an indirect jump */ tcg_gen_movi_tl(cpu_pc, pc); tcg_gen_movi_tl(cpu_npc, npc); - tcg_gen_exit_tb(NULL, 0); + tcg_gen_lookup_and_goto_ptr(); } } -// XXX suboptimal -static inline void gen_mov_reg_N(TCGv reg, TCGv_i32 src) +static TCGv gen_carry32(void) { - tcg_gen_extu_i32_tl(reg, src); - tcg_gen_extract_tl(reg, reg, PSR_NEG_SHIFT, 1); -} - -static inline void gen_mov_reg_Z(TCGv reg, TCGv_i32 src) -{ - tcg_gen_extu_i32_tl(reg, src); - tcg_gen_extract_tl(reg, reg, PSR_ZERO_SHIFT, 1); -} - -static inline void gen_mov_reg_V(TCGv reg, TCGv_i32 src) -{ - tcg_gen_extu_i32_tl(reg, src); - tcg_gen_extract_tl(reg, reg, PSR_OVF_SHIFT, 1); -} - -static inline void gen_mov_reg_C(TCGv reg, TCGv_i32 src) -{ - tcg_gen_extu_i32_tl(reg, src); - tcg_gen_extract_tl(reg, reg, PSR_CARRY_SHIFT, 1); -} - -static inline void gen_op_add_cc(TCGv dst, TCGv src1, TCGv src2) -{ - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_mov_tl(cpu_cc_src2, src2); - tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2); - tcg_gen_mov_tl(dst, cpu_cc_dst); -} - -static TCGv_i32 gen_add32_carry32(void) -{ - TCGv_i32 carry_32, cc_src1_32, cc_src2_32; - - /* Carry is computed from a previous add: (dst < src) */ -#if TARGET_LONG_BITS == 64 - cc_src1_32 = tcg_temp_new_i32(); - cc_src2_32 = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(cc_src1_32, cpu_cc_dst); - tcg_gen_extrl_i64_i32(cc_src2_32, cpu_cc_src); -#else - cc_src1_32 = cpu_cc_dst; - cc_src2_32 = cpu_cc_src; -#endif - - carry_32 = tcg_temp_new_i32(); - tcg_gen_setcond_i32(TCG_COND_LTU, carry_32, cc_src1_32, cc_src2_32); - -#if TARGET_LONG_BITS == 64 - tcg_temp_free_i32(cc_src1_32); - tcg_temp_free_i32(cc_src2_32); -#endif - - return carry_32; -} - -static TCGv_i32 gen_sub32_carry32(void) -{ - TCGv_i32 carry_32, cc_src1_32, cc_src2_32; - - /* Carry is computed from a previous borrow: (src1 < src2) */ -#if TARGET_LONG_BITS == 64 - cc_src1_32 = tcg_temp_new_i32(); - cc_src2_32 = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(cc_src1_32, cpu_cc_src); - tcg_gen_extrl_i64_i32(cc_src2_32, cpu_cc_src2); -#else - cc_src1_32 = cpu_cc_src; - cc_src2_32 = cpu_cc_src2; -#endif - - carry_32 = tcg_temp_new_i32(); - tcg_gen_setcond_i32(TCG_COND_LTU, carry_32, cc_src1_32, cc_src2_32); - -#if TARGET_LONG_BITS == 64 - tcg_temp_free_i32(cc_src1_32); - tcg_temp_free_i32(cc_src2_32); -#endif - - return carry_32; -} - -static void gen_op_addx_int(DisasContext *dc, TCGv dst, TCGv src1, - TCGv src2, int update_cc) -{ - TCGv_i32 carry_32; - TCGv carry; - - switch (dc->cc_op) { - case CC_OP_DIV: - case CC_OP_LOGIC: - /* Carry is known to be zero. Fall back to plain ADD. */ - if (update_cc) { - gen_op_add_cc(dst, src1, src2); - } else { - tcg_gen_add_tl(dst, src1, src2); - } - return; - - case CC_OP_ADD: - case CC_OP_TADD: - case CC_OP_TADDTV: - if (TARGET_LONG_BITS == 32) { - /* We can re-use the host's hardware carry generation by using - an ADD2 opcode. We discard the low part of the output. - Ideally we'd combine this operation with the add that - generated the carry in the first place. */ - carry = tcg_temp_new(); - tcg_gen_add2_tl(carry, dst, cpu_cc_src, src1, cpu_cc_src2, src2); - tcg_temp_free(carry); - goto add_done; - } - carry_32 = gen_add32_carry32(); - break; - - case CC_OP_SUB: - case CC_OP_TSUB: - case CC_OP_TSUBTV: - carry_32 = gen_sub32_carry32(); - break; - - default: - /* We need external help to produce the carry. */ - carry_32 = tcg_temp_new_i32(); - gen_helper_compute_C_icc(carry_32, cpu_env); - break; + if (TARGET_LONG_BITS == 64) { + TCGv t = tcg_temp_new(); + tcg_gen_extract_tl(t, cpu_icc_C, 32, 1); + return t; } + return cpu_icc_C; +} -#if TARGET_LONG_BITS == 64 - carry = tcg_temp_new(); - tcg_gen_extu_i32_i64(carry, carry_32); -#else - carry = carry_32; -#endif +static void gen_op_addcc_int(TCGv dst, TCGv src1, TCGv src2, TCGv cin) +{ + TCGv z = tcg_constant_tl(0); + if (cin) { + tcg_gen_add2_tl(cpu_cc_N, cpu_cc_C, src1, z, cin, z); + tcg_gen_add2_tl(cpu_cc_N, cpu_cc_C, cpu_cc_N, cpu_cc_C, src2, z); + } else { + tcg_gen_add2_tl(cpu_cc_N, cpu_cc_C, src1, z, src2, z); + } + tcg_gen_xor_tl(cpu_cc_Z, src1, src2); + tcg_gen_xor_tl(cpu_cc_V, cpu_cc_N, src2); + tcg_gen_andc_tl(cpu_cc_V, cpu_cc_V, cpu_cc_Z); + if (TARGET_LONG_BITS == 64) { + /* + * Carry-in to bit 32 is result ^ src1 ^ src2. + * We already have the src xor term in Z, from computation of V. + */ + tcg_gen_xor_tl(cpu_icc_C, cpu_cc_Z, cpu_cc_N); + tcg_gen_mov_tl(cpu_icc_Z, cpu_cc_N); + } + tcg_gen_mov_tl(cpu_cc_Z, cpu_cc_N); + tcg_gen_mov_tl(dst, cpu_cc_N); +} + +static void gen_op_addcc(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_addcc_int(dst, src1, src2, NULL); +} + +static void gen_op_taddcc(TCGv dst, TCGv src1, TCGv src2) +{ + TCGv t = tcg_temp_new(); + + /* Save the tag bits around modification of dst. */ + tcg_gen_or_tl(t, src1, src2); + + gen_op_addcc(dst, src1, src2); + + /* Incorprate tag bits into icc.V */ + tcg_gen_andi_tl(t, t, 3); + tcg_gen_neg_tl(t, t); + tcg_gen_ext32u_tl(t, t); + tcg_gen_or_tl(cpu_cc_V, cpu_cc_V, t); +} + +static void gen_op_addc(TCGv dst, TCGv src1, TCGv src2) +{ tcg_gen_add_tl(dst, src1, src2); - tcg_gen_add_tl(dst, dst, carry); - - tcg_temp_free_i32(carry_32); -#if TARGET_LONG_BITS == 64 - tcg_temp_free(carry); -#endif - - add_done: - if (update_cc) { - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_mov_tl(cpu_cc_src2, src2); - tcg_gen_mov_tl(cpu_cc_dst, dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADDX); - dc->cc_op = CC_OP_ADDX; - } + tcg_gen_add_tl(dst, dst, gen_carry32()); } -static inline void gen_op_sub_cc(TCGv dst, TCGv src1, TCGv src2) +static void gen_op_addccc(TCGv dst, TCGv src1, TCGv src2) { - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_mov_tl(cpu_cc_src2, src2); - tcg_gen_sub_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2); - tcg_gen_mov_tl(dst, cpu_cc_dst); + gen_op_addcc_int(dst, src1, src2, gen_carry32()); } -static void gen_op_subx_int(DisasContext *dc, TCGv dst, TCGv src1, - TCGv src2, int update_cc) +static void gen_op_subcc_int(TCGv dst, TCGv src1, TCGv src2, TCGv cin) { - TCGv_i32 carry_32; - TCGv carry; + TCGv z = tcg_constant_tl(0); - switch (dc->cc_op) { - case CC_OP_DIV: - case CC_OP_LOGIC: - /* Carry is known to be zero. Fall back to plain SUB. */ - if (update_cc) { - gen_op_sub_cc(dst, src1, src2); - } else { - tcg_gen_sub_tl(dst, src1, src2); - } - return; - - case CC_OP_ADD: - case CC_OP_TADD: - case CC_OP_TADDTV: - carry_32 = gen_add32_carry32(); - break; - - case CC_OP_SUB: - case CC_OP_TSUB: - case CC_OP_TSUBTV: - if (TARGET_LONG_BITS == 32) { - /* We can re-use the host's hardware carry generation by using - a SUB2 opcode. We discard the low part of the output. - Ideally we'd combine this operation with the add that - generated the carry in the first place. */ - carry = tcg_temp_new(); - tcg_gen_sub2_tl(carry, dst, cpu_cc_src, src1, cpu_cc_src2, src2); - tcg_temp_free(carry); - goto sub_done; - } - carry_32 = gen_sub32_carry32(); - break; - - default: - /* We need external help to produce the carry. */ - carry_32 = tcg_temp_new_i32(); - gen_helper_compute_C_icc(carry_32, cpu_env); - break; + if (cin) { + tcg_gen_sub2_tl(cpu_cc_N, cpu_cc_C, src1, z, cin, z); + tcg_gen_sub2_tl(cpu_cc_N, cpu_cc_C, cpu_cc_N, cpu_cc_C, src2, z); + } else { + tcg_gen_sub2_tl(cpu_cc_N, cpu_cc_C, src1, z, src2, z); } - -#if TARGET_LONG_BITS == 64 - carry = tcg_temp_new(); - tcg_gen_extu_i32_i64(carry, carry_32); -#else - carry = carry_32; + tcg_gen_neg_tl(cpu_cc_C, cpu_cc_C); + tcg_gen_xor_tl(cpu_cc_Z, src1, src2); + tcg_gen_xor_tl(cpu_cc_V, cpu_cc_N, src1); + tcg_gen_and_tl(cpu_cc_V, cpu_cc_V, cpu_cc_Z); +#ifdef TARGET_SPARC64 + tcg_gen_xor_tl(cpu_icc_C, cpu_cc_Z, cpu_cc_N); + tcg_gen_mov_tl(cpu_icc_Z, cpu_cc_N); #endif + tcg_gen_mov_tl(cpu_cc_Z, cpu_cc_N); + tcg_gen_mov_tl(dst, cpu_cc_N); +} +static void gen_op_subcc(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_subcc_int(dst, src1, src2, NULL); +} + +static void gen_op_tsubcc(TCGv dst, TCGv src1, TCGv src2) +{ + TCGv t = tcg_temp_new(); + + /* Save the tag bits around modification of dst. */ + tcg_gen_or_tl(t, src1, src2); + + gen_op_subcc(dst, src1, src2); + + /* Incorprate tag bits into icc.V */ + tcg_gen_andi_tl(t, t, 3); + tcg_gen_neg_tl(t, t); + tcg_gen_ext32u_tl(t, t); + tcg_gen_or_tl(cpu_cc_V, cpu_cc_V, t); +} + +static void gen_op_subc(TCGv dst, TCGv src1, TCGv src2) +{ tcg_gen_sub_tl(dst, src1, src2); - tcg_gen_sub_tl(dst, dst, carry); - - tcg_temp_free_i32(carry_32); -#if TARGET_LONG_BITS == 64 - tcg_temp_free(carry); -#endif - - sub_done: - if (update_cc) { - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_mov_tl(cpu_cc_src2, src2); - tcg_gen_mov_tl(cpu_cc_dst, dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUBX); - dc->cc_op = CC_OP_SUBX; - } + tcg_gen_sub_tl(dst, dst, gen_carry32()); } -static inline void gen_op_mulscc(TCGv dst, TCGv src1, TCGv src2) +static void gen_op_subccc(TCGv dst, TCGv src1, TCGv src2) { - TCGv r_temp, zero, t0; + gen_op_subcc_int(dst, src1, src2, gen_carry32()); +} - r_temp = tcg_temp_new(); - t0 = tcg_temp_new(); +static void gen_op_mulscc(TCGv dst, TCGv src1, TCGv src2) +{ + TCGv zero = tcg_constant_tl(0); + TCGv one = tcg_constant_tl(1); + TCGv t_src1 = tcg_temp_new(); + TCGv t_src2 = tcg_temp_new(); + TCGv t0 = tcg_temp_new(); - /* old op: - if (!(env->y & 1)) - T1 = 0; - */ - zero = tcg_const_tl(0); - tcg_gen_andi_tl(cpu_cc_src, src1, 0xffffffff); - tcg_gen_andi_tl(r_temp, cpu_y, 0x1); - tcg_gen_andi_tl(cpu_cc_src2, src2, 0xffffffff); - tcg_gen_movcond_tl(TCG_COND_EQ, cpu_cc_src2, r_temp, zero, - zero, cpu_cc_src2); - tcg_temp_free(zero); + tcg_gen_ext32u_tl(t_src1, src1); + tcg_gen_ext32u_tl(t_src2, src2); - // b2 = T0 & 1; - // env->y = (b2 << 31) | (env->y >> 1); + /* + * if (!(env->y & 1)) + * src2 = 0; + */ + tcg_gen_movcond_tl(TCG_COND_TSTEQ, t_src2, cpu_y, one, zero, t_src2); + + /* + * b2 = src1 & 1; + * y = (b2 << 31) | (y >> 1); + */ tcg_gen_extract_tl(t0, cpu_y, 1, 31); - tcg_gen_deposit_tl(cpu_y, t0, cpu_cc_src, 31, 1); + tcg_gen_deposit_tl(cpu_y, t0, src1, 31, 1); // b1 = N ^ V; - gen_mov_reg_N(t0, cpu_psr); - gen_mov_reg_V(r_temp, cpu_psr); - tcg_gen_xor_tl(t0, t0, r_temp); - tcg_temp_free(r_temp); + tcg_gen_xor_tl(t0, cpu_cc_N, cpu_cc_V); - // T0 = (b1 << 31) | (T0 >> 1); - // src1 = T0; - tcg_gen_shli_tl(t0, t0, 31); - tcg_gen_shri_tl(cpu_cc_src, cpu_cc_src, 1); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t0); - tcg_temp_free(t0); + /* + * src1 = (b1 << 31) | (src1 >> 1) + */ + tcg_gen_andi_tl(t0, t0, 1u << 31); + tcg_gen_shri_tl(t_src1, t_src1, 1); + tcg_gen_or_tl(t_src1, t_src1, t0); - tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2); - - tcg_gen_mov_tl(dst, cpu_cc_dst); + gen_op_addcc(dst, t_src1, t_src2); } -static inline void gen_op_multiply(TCGv dst, TCGv src1, TCGv src2, int sign_ext) +static void gen_op_multiply(TCGv dst, TCGv src1, TCGv src2, int sign_ext) { #if TARGET_LONG_BITS == 32 if (sign_ext) { @@ -659,403 +544,233 @@ static inline void gen_op_multiply(TCGv dst, TCGv src1, TCGv src2, int sign_ext) } tcg_gen_mul_i64(dst, t0, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_gen_shri_i64(cpu_y, dst, 32); #endif } -static inline void gen_op_umul(TCGv dst, TCGv src1, TCGv src2) +static void gen_op_umul(TCGv dst, TCGv src1, TCGv src2) { /* zero-extend truncated operands before multiplication */ gen_op_multiply(dst, src1, src2, 0); } -static inline void gen_op_smul(TCGv dst, TCGv src1, TCGv src2) +static void gen_op_smul(TCGv dst, TCGv src1, TCGv src2) { /* sign-extend truncated operands before multiplication */ gen_op_multiply(dst, src1, src2, 1); } -// 1 -static inline void gen_op_eval_ba(TCGv dst) +static void gen_op_sdiv(TCGv dst, TCGv src1, TCGv src2) { - tcg_gen_movi_tl(dst, 1); +#ifdef TARGET_SPARC64 + gen_helper_sdiv(dst, tcg_env, src1, src2); + tcg_gen_ext32s_tl(dst, dst); +#else + TCGv_i64 t64 = tcg_temp_new_i64(); + gen_helper_sdiv(t64, tcg_env, src1, src2); + tcg_gen_trunc_i64_tl(dst, t64); +#endif } -// Z -static inline void gen_op_eval_be(TCGv dst, TCGv_i32 src) +static void gen_op_udivcc(TCGv dst, TCGv src1, TCGv src2) { - gen_mov_reg_Z(dst, src); + TCGv_i64 t64; + +#ifdef TARGET_SPARC64 + t64 = cpu_cc_V; +#else + t64 = tcg_temp_new_i64(); +#endif + + gen_helper_udiv(t64, tcg_env, src1, src2); + +#ifdef TARGET_SPARC64 + tcg_gen_ext32u_tl(cpu_cc_N, t64); + tcg_gen_shri_tl(cpu_cc_V, t64, 32); + tcg_gen_mov_tl(cpu_icc_Z, cpu_cc_N); + tcg_gen_movi_tl(cpu_icc_C, 0); +#else + tcg_gen_extr_i64_tl(cpu_cc_N, cpu_cc_V, t64); +#endif + tcg_gen_mov_tl(cpu_cc_Z, cpu_cc_N); + tcg_gen_movi_tl(cpu_cc_C, 0); + tcg_gen_mov_tl(dst, cpu_cc_N); } -// Z | (N ^ V) -static inline void gen_op_eval_ble(TCGv dst, TCGv_i32 src) +static void gen_op_sdivcc(TCGv dst, TCGv src1, TCGv src2) { - TCGv t0 = tcg_temp_new(); - gen_mov_reg_N(t0, src); - gen_mov_reg_V(dst, src); - tcg_gen_xor_tl(dst, dst, t0); - gen_mov_reg_Z(t0, src); - tcg_gen_or_tl(dst, dst, t0); - tcg_temp_free(t0); + TCGv_i64 t64; + +#ifdef TARGET_SPARC64 + t64 = cpu_cc_V; +#else + t64 = tcg_temp_new_i64(); +#endif + + gen_helper_sdiv(t64, tcg_env, src1, src2); + +#ifdef TARGET_SPARC64 + tcg_gen_ext32s_tl(cpu_cc_N, t64); + tcg_gen_shri_tl(cpu_cc_V, t64, 32); + tcg_gen_mov_tl(cpu_icc_Z, cpu_cc_N); + tcg_gen_movi_tl(cpu_icc_C, 0); +#else + tcg_gen_extr_i64_tl(cpu_cc_N, cpu_cc_V, t64); +#endif + tcg_gen_mov_tl(cpu_cc_Z, cpu_cc_N); + tcg_gen_movi_tl(cpu_cc_C, 0); + tcg_gen_mov_tl(dst, cpu_cc_N); } -// N ^ V -static inline void gen_op_eval_bl(TCGv dst, TCGv_i32 src) +static void gen_op_taddcctv(TCGv dst, TCGv src1, TCGv src2) { - TCGv t0 = tcg_temp_new(); - gen_mov_reg_V(t0, src); - gen_mov_reg_N(dst, src); - tcg_gen_xor_tl(dst, dst, t0); - tcg_temp_free(t0); + gen_helper_taddcctv(dst, tcg_env, src1, src2); } -// C | Z -static inline void gen_op_eval_bleu(TCGv dst, TCGv_i32 src) +static void gen_op_tsubcctv(TCGv dst, TCGv src1, TCGv src2) { - TCGv t0 = tcg_temp_new(); - gen_mov_reg_Z(t0, src); - gen_mov_reg_C(dst, src); - tcg_gen_or_tl(dst, dst, t0); - tcg_temp_free(t0); + gen_helper_tsubcctv(dst, tcg_env, src1, src2); } -// C -static inline void gen_op_eval_bcs(TCGv dst, TCGv_i32 src) +static void gen_op_popc(TCGv dst, TCGv src1, TCGv src2) { - gen_mov_reg_C(dst, src); + tcg_gen_ctpop_tl(dst, src2); } -// V -static inline void gen_op_eval_bvs(TCGv dst, TCGv_i32 src) +#ifndef TARGET_SPARC64 +static void gen_helper_array8(TCGv dst, TCGv src1, TCGv src2) { - gen_mov_reg_V(dst, src); + g_assert_not_reached(); +} +#endif + +static void gen_op_array16(TCGv dst, TCGv src1, TCGv src2) +{ + gen_helper_array8(dst, src1, src2); + tcg_gen_shli_tl(dst, dst, 1); } -// 0 -static inline void gen_op_eval_bn(TCGv dst) +static void gen_op_array32(TCGv dst, TCGv src1, TCGv src2) { - tcg_gen_movi_tl(dst, 0); + gen_helper_array8(dst, src1, src2); + tcg_gen_shli_tl(dst, dst, 2); } -// N -static inline void gen_op_eval_bneg(TCGv dst, TCGv_i32 src) +static void gen_op_fpack16(TCGv_i32 dst, TCGv_i64 src) { - gen_mov_reg_N(dst, src); +#ifdef TARGET_SPARC64 + gen_helper_fpack16(dst, cpu_gsr, src); +#else + g_assert_not_reached(); +#endif } -// !Z -static inline void gen_op_eval_bne(TCGv dst, TCGv_i32 src) +static void gen_op_fpackfix(TCGv_i32 dst, TCGv_i64 src) { - gen_mov_reg_Z(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); +#ifdef TARGET_SPARC64 + gen_helper_fpackfix(dst, cpu_gsr, src); +#else + g_assert_not_reached(); +#endif } -// !(Z | (N ^ V)) -static inline void gen_op_eval_bg(TCGv dst, TCGv_i32 src) +static void gen_op_fpack32(TCGv_i64 dst, TCGv_i64 src1, TCGv_i64 src2) { - gen_op_eval_ble(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); +#ifdef TARGET_SPARC64 + gen_helper_fpack32(dst, cpu_gsr, src1, src2); +#else + g_assert_not_reached(); +#endif } -// !(N ^ V) -static inline void gen_op_eval_bge(TCGv dst, TCGv_i32 src) +static void gen_op_faligndata(TCGv_i64 dst, TCGv_i64 s1, TCGv_i64 s2) { - gen_op_eval_bl(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); +#ifdef TARGET_SPARC64 + TCGv t1, t2, shift; + + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + shift = tcg_temp_new(); + + tcg_gen_andi_tl(shift, cpu_gsr, 7); + tcg_gen_shli_tl(shift, shift, 3); + tcg_gen_shl_tl(t1, s1, shift); + + /* + * A shift of 64 does not produce 0 in TCG. Divide this into a + * shift of (up to 63) followed by a constant shift of 1. + */ + tcg_gen_xori_tl(shift, shift, 63); + tcg_gen_shr_tl(t2, s2, shift); + tcg_gen_shri_tl(t2, t2, 1); + + tcg_gen_or_tl(dst, t1, t2); +#else + g_assert_not_reached(); +#endif } -// !(C | Z) -static inline void gen_op_eval_bgu(TCGv dst, TCGv_i32 src) +static void gen_op_bshuffle(TCGv_i64 dst, TCGv_i64 src1, TCGv_i64 src2) { - gen_op_eval_bleu(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); +#ifdef TARGET_SPARC64 + gen_helper_bshuffle(dst, cpu_gsr, src1, src2); +#else + g_assert_not_reached(); +#endif } -// !C -static inline void gen_op_eval_bcc(TCGv dst, TCGv_i32 src) +static void finishing_insn(DisasContext *dc) { - gen_mov_reg_C(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); -} - -// !N -static inline void gen_op_eval_bpos(TCGv dst, TCGv_i32 src) -{ - gen_mov_reg_N(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); -} - -// !V -static inline void gen_op_eval_bvc(TCGv dst, TCGv_i32 src) -{ - gen_mov_reg_V(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); -} - -/* - FPSR bit field FCC1 | FCC0: - 0 = - 1 < - 2 > - 3 unordered -*/ -static inline void gen_mov_reg_FCC0(TCGv reg, TCGv src, - unsigned int fcc_offset) -{ - tcg_gen_shri_tl(reg, src, FSR_FCC0_SHIFT + fcc_offset); - tcg_gen_andi_tl(reg, reg, 0x1); -} - -static inline void gen_mov_reg_FCC1(TCGv reg, TCGv src, - unsigned int fcc_offset) -{ - tcg_gen_shri_tl(reg, src, FSR_FCC1_SHIFT + fcc_offset); - tcg_gen_andi_tl(reg, reg, 0x1); -} - -// !0: FCC0 | FCC1 -static inline void gen_op_eval_fbne(TCGv dst, TCGv src, - unsigned int fcc_offset) -{ - TCGv t0 = tcg_temp_new(); - gen_mov_reg_FCC0(dst, src, fcc_offset); - gen_mov_reg_FCC1(t0, src, fcc_offset); - tcg_gen_or_tl(dst, dst, t0); - tcg_temp_free(t0); -} - -// 1 or 2: FCC0 ^ FCC1 -static inline void gen_op_eval_fblg(TCGv dst, TCGv src, - unsigned int fcc_offset) -{ - TCGv t0 = tcg_temp_new(); - gen_mov_reg_FCC0(dst, src, fcc_offset); - gen_mov_reg_FCC1(t0, src, fcc_offset); - tcg_gen_xor_tl(dst, dst, t0); - tcg_temp_free(t0); -} - -// 1 or 3: FCC0 -static inline void gen_op_eval_fbul(TCGv dst, TCGv src, - unsigned int fcc_offset) -{ - gen_mov_reg_FCC0(dst, src, fcc_offset); -} - -// 1: FCC0 & !FCC1 -static inline void gen_op_eval_fbl(TCGv dst, TCGv src, - unsigned int fcc_offset) -{ - TCGv t0 = tcg_temp_new(); - gen_mov_reg_FCC0(dst, src, fcc_offset); - gen_mov_reg_FCC1(t0, src, fcc_offset); - tcg_gen_andc_tl(dst, dst, t0); - tcg_temp_free(t0); -} - -// 2 or 3: FCC1 -static inline void gen_op_eval_fbug(TCGv dst, TCGv src, - unsigned int fcc_offset) -{ - gen_mov_reg_FCC1(dst, src, fcc_offset); -} - -// 2: !FCC0 & FCC1 -static inline void gen_op_eval_fbg(TCGv dst, TCGv src, - unsigned int fcc_offset) -{ - TCGv t0 = tcg_temp_new(); - gen_mov_reg_FCC0(dst, src, fcc_offset); - gen_mov_reg_FCC1(t0, src, fcc_offset); - tcg_gen_andc_tl(dst, t0, dst); - tcg_temp_free(t0); -} - -// 3: FCC0 & FCC1 -static inline void gen_op_eval_fbu(TCGv dst, TCGv src, - unsigned int fcc_offset) -{ - TCGv t0 = tcg_temp_new(); - gen_mov_reg_FCC0(dst, src, fcc_offset); - gen_mov_reg_FCC1(t0, src, fcc_offset); - tcg_gen_and_tl(dst, dst, t0); - tcg_temp_free(t0); -} - -// 0: !(FCC0 | FCC1) -static inline void gen_op_eval_fbe(TCGv dst, TCGv src, - unsigned int fcc_offset) -{ - TCGv t0 = tcg_temp_new(); - gen_mov_reg_FCC0(dst, src, fcc_offset); - gen_mov_reg_FCC1(t0, src, fcc_offset); - tcg_gen_or_tl(dst, dst, t0); - tcg_gen_xori_tl(dst, dst, 0x1); - tcg_temp_free(t0); -} - -// 0 or 3: !(FCC0 ^ FCC1) -static inline void gen_op_eval_fbue(TCGv dst, TCGv src, - unsigned int fcc_offset) -{ - TCGv t0 = tcg_temp_new(); - gen_mov_reg_FCC0(dst, src, fcc_offset); - gen_mov_reg_FCC1(t0, src, fcc_offset); - tcg_gen_xor_tl(dst, dst, t0); - tcg_gen_xori_tl(dst, dst, 0x1); - tcg_temp_free(t0); -} - -// 0 or 2: !FCC0 -static inline void gen_op_eval_fbge(TCGv dst, TCGv src, - unsigned int fcc_offset) -{ - gen_mov_reg_FCC0(dst, src, fcc_offset); - tcg_gen_xori_tl(dst, dst, 0x1); -} - -// !1: !(FCC0 & !FCC1) -static inline void gen_op_eval_fbuge(TCGv dst, TCGv src, - unsigned int fcc_offset) -{ - TCGv t0 = tcg_temp_new(); - gen_mov_reg_FCC0(dst, src, fcc_offset); - gen_mov_reg_FCC1(t0, src, fcc_offset); - tcg_gen_andc_tl(dst, dst, t0); - tcg_gen_xori_tl(dst, dst, 0x1); - tcg_temp_free(t0); -} - -// 0 or 1: !FCC1 -static inline void gen_op_eval_fble(TCGv dst, TCGv src, - unsigned int fcc_offset) -{ - gen_mov_reg_FCC1(dst, src, fcc_offset); - tcg_gen_xori_tl(dst, dst, 0x1); -} - -// !2: !(!FCC0 & FCC1) -static inline void gen_op_eval_fbule(TCGv dst, TCGv src, - unsigned int fcc_offset) -{ - TCGv t0 = tcg_temp_new(); - gen_mov_reg_FCC0(dst, src, fcc_offset); - gen_mov_reg_FCC1(t0, src, fcc_offset); - tcg_gen_andc_tl(dst, t0, dst); - tcg_gen_xori_tl(dst, dst, 0x1); - tcg_temp_free(t0); -} - -// !3: !(FCC0 & FCC1) -static inline void gen_op_eval_fbo(TCGv dst, TCGv src, - unsigned int fcc_offset) -{ - TCGv t0 = tcg_temp_new(); - gen_mov_reg_FCC0(dst, src, fcc_offset); - gen_mov_reg_FCC1(t0, src, fcc_offset); - tcg_gen_and_tl(dst, dst, t0); - tcg_gen_xori_tl(dst, dst, 0x1); - tcg_temp_free(t0); -} - -static inline void gen_branch2(DisasContext *dc, target_ulong pc1, - target_ulong pc2, TCGv r_cond) -{ - TCGLabel *l1 = gen_new_label(); - - tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, 0, l1); - - gen_goto_tb(dc, 0, pc1, pc1 + 4); - - gen_set_label(l1); - gen_goto_tb(dc, 1, pc2, pc2 + 4); -} - -static void gen_branch_a(DisasContext *dc, target_ulong pc1) -{ - TCGLabel *l1 = gen_new_label(); - target_ulong npc = dc->npc; - - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_cond, 0, l1); - - gen_goto_tb(dc, 0, npc, pc1); - - gen_set_label(l1); - gen_goto_tb(dc, 1, npc + 4, npc + 8); - - dc->base.is_jmp = DISAS_NORETURN; -} - -static void gen_branch_n(DisasContext *dc, target_ulong pc1) -{ - target_ulong npc = dc->npc; - - if (likely(npc != DYNAMIC_PC)) { - dc->pc = npc; - dc->jump_pc[0] = pc1; - dc->jump_pc[1] = npc + 4; - dc->npc = JUMP_PC; - } else { - TCGv t, z; - - tcg_gen_mov_tl(cpu_pc, cpu_npc); - - tcg_gen_addi_tl(cpu_npc, cpu_npc, 4); - t = tcg_const_tl(pc1); - z = tcg_const_tl(0); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_npc, cpu_cond, z, t, cpu_npc); - tcg_temp_free(t); - tcg_temp_free(z); - - dc->pc = DYNAMIC_PC; + /* + * From here, there is no future path through an unwinding exception. + * If the current insn cannot raise an exception, the computation of + * cpu_cond may be able to be elided. + */ + if (dc->cpu_cond_live) { + tcg_gen_discard_tl(cpu_cond); + dc->cpu_cond_live = false; } } -static inline void gen_generic_branch(DisasContext *dc) +static void gen_generic_branch(DisasContext *dc) { - TCGv npc0 = tcg_const_tl(dc->jump_pc[0]); - TCGv npc1 = tcg_const_tl(dc->jump_pc[1]); - TCGv zero = tcg_const_tl(0); + TCGv npc0 = tcg_constant_tl(dc->jump_pc[0]); + TCGv npc1 = tcg_constant_tl(dc->jump_pc[1]); + TCGv c2 = tcg_constant_tl(dc->jump.c2); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_npc, cpu_cond, zero, npc0, npc1); - - tcg_temp_free(npc0); - tcg_temp_free(npc1); - tcg_temp_free(zero); + tcg_gen_movcond_tl(dc->jump.cond, cpu_npc, dc->jump.c1, c2, npc0, npc1); } /* call this function before using the condition register as it may have been set for a jump */ -static inline void flush_cond(DisasContext *dc) +static void flush_cond(DisasContext *dc) { if (dc->npc == JUMP_PC) { gen_generic_branch(dc); - dc->npc = DYNAMIC_PC; + dc->npc = DYNAMIC_PC_LOOKUP; } } -static inline void save_npc(DisasContext *dc) +static void save_npc(DisasContext *dc) { - if (dc->npc == JUMP_PC) { - gen_generic_branch(dc); - dc->npc = DYNAMIC_PC; - } else if (dc->npc != DYNAMIC_PC) { + if (dc->npc & 3) { + switch (dc->npc) { + case JUMP_PC: + gen_generic_branch(dc); + dc->npc = DYNAMIC_PC_LOOKUP; + break; + case DYNAMIC_PC: + case DYNAMIC_PC_LOOKUP: + break; + default: + g_assert_not_reached(); + } + } else { tcg_gen_movi_tl(cpu_npc, dc->npc); } } -static inline void update_psr(DisasContext *dc) -{ - if (dc->cc_op != CC_OP_FLAGS) { - dc->cc_op = CC_OP_FLAGS; - gen_helper_compute_psr(cpu_env); - } -} - -static inline void save_state(DisasContext *dc) +static void save_state(DisasContext *dc) { tcg_gen_movi_tl(cpu_pc, dc->pc); save_npc(dc); @@ -1063,616 +778,325 @@ static inline void save_state(DisasContext *dc) static void gen_exception(DisasContext *dc, int which) { - TCGv_i32 t; - + finishing_insn(dc); save_state(dc); - t = tcg_const_i32(which); - gen_helper_raise_exception(cpu_env, t); - tcg_temp_free_i32(t); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(which)); dc->base.is_jmp = DISAS_NORETURN; } -static void gen_check_align(TCGv addr, int mask) +static TCGLabel *delay_exceptionv(DisasContext *dc, TCGv_i32 excp) { - TCGv_i32 r_mask = tcg_const_i32(mask); - gen_helper_check_align(cpu_env, addr, r_mask); - tcg_temp_free_i32(r_mask); + DisasDelayException *e = g_new0(DisasDelayException, 1); + + e->next = dc->delay_excp_list; + dc->delay_excp_list = e; + + e->lab = gen_new_label(); + e->excp = excp; + e->pc = dc->pc; + /* Caller must have used flush_cond before branch. */ + assert(e->npc != JUMP_PC); + e->npc = dc->npc; + + return e->lab; } -static inline void gen_mov_pc_npc(DisasContext *dc) +static TCGLabel *delay_exception(DisasContext *dc, int excp) { - if (dc->npc == JUMP_PC) { - gen_generic_branch(dc); - tcg_gen_mov_tl(cpu_pc, cpu_npc); - dc->pc = DYNAMIC_PC; - } else if (dc->npc == DYNAMIC_PC) { - tcg_gen_mov_tl(cpu_pc, cpu_npc); - dc->pc = DYNAMIC_PC; + return delay_exceptionv(dc, tcg_constant_i32(excp)); +} + +static void gen_check_align(DisasContext *dc, TCGv addr, int mask) +{ + TCGv t = tcg_temp_new(); + TCGLabel *lab; + + tcg_gen_andi_tl(t, addr, mask); + + flush_cond(dc); + lab = delay_exception(dc, TT_UNALIGNED); + tcg_gen_brcondi_tl(TCG_COND_NE, t, 0, lab); +} + +static void gen_mov_pc_npc(DisasContext *dc) +{ + finishing_insn(dc); + + if (dc->npc & 3) { + switch (dc->npc) { + case JUMP_PC: + gen_generic_branch(dc); + tcg_gen_mov_tl(cpu_pc, cpu_npc); + dc->pc = DYNAMIC_PC_LOOKUP; + break; + case DYNAMIC_PC: + case DYNAMIC_PC_LOOKUP: + tcg_gen_mov_tl(cpu_pc, cpu_npc); + dc->pc = dc->npc; + break; + default: + g_assert_not_reached(); + } } else { dc->pc = dc->npc; } } -static inline void gen_op_next_insn(void) -{ - tcg_gen_mov_tl(cpu_pc, cpu_npc); - tcg_gen_addi_tl(cpu_npc, cpu_npc, 4); -} - -static void free_compare(DisasCompare *cmp) -{ - if (!cmp->g1) { - tcg_temp_free(cmp->c1); - } - if (!cmp->g2) { - tcg_temp_free(cmp->c2); - } -} - static void gen_compare(DisasCompare *cmp, bool xcc, unsigned int cond, DisasContext *dc) { - static int subcc_cond[16] = { - TCG_COND_NEVER, - TCG_COND_EQ, - TCG_COND_LE, - TCG_COND_LT, - TCG_COND_LEU, - TCG_COND_LTU, - -1, /* neg */ - -1, /* overflow */ - TCG_COND_ALWAYS, - TCG_COND_NE, - TCG_COND_GT, - TCG_COND_GE, - TCG_COND_GTU, - TCG_COND_GEU, - -1, /* pos */ - -1, /* no overflow */ - }; + TCGv t1; - static int logic_cond[16] = { - TCG_COND_NEVER, - TCG_COND_EQ, /* eq: Z */ - TCG_COND_LE, /* le: Z | (N ^ V) -> Z | N */ - TCG_COND_LT, /* lt: N ^ V -> N */ - TCG_COND_EQ, /* leu: C | Z -> Z */ - TCG_COND_NEVER, /* ltu: C -> 0 */ - TCG_COND_LT, /* neg: N */ - TCG_COND_NEVER, /* vs: V -> 0 */ - TCG_COND_ALWAYS, - TCG_COND_NE, /* ne: !Z */ - TCG_COND_GT, /* gt: !(Z | (N ^ V)) -> !(Z | N) */ - TCG_COND_GE, /* ge: !(N ^ V) -> !N */ - TCG_COND_NE, /* gtu: !(C | Z) -> !Z */ - TCG_COND_ALWAYS, /* geu: !C -> 1 */ - TCG_COND_GE, /* pos: !N */ - TCG_COND_ALWAYS, /* vc: !V -> 1 */ - }; + cmp->c1 = t1 = tcg_temp_new(); + cmp->c2 = 0; - TCGv_i32 r_src; - TCGv r_dst; - -#ifdef TARGET_SPARC64 - if (xcc) { - r_src = cpu_xcc; - } else { - r_src = cpu_psr; - } -#else - r_src = cpu_psr; -#endif - - switch (dc->cc_op) { - case CC_OP_LOGIC: - cmp->cond = logic_cond[cond]; - do_compare_dst_0: - cmp->is_bool = false; - cmp->g2 = false; - cmp->c2 = tcg_const_tl(0); -#ifdef TARGET_SPARC64 - if (!xcc) { - cmp->g1 = false; - cmp->c1 = tcg_temp_new(); - tcg_gen_ext32s_tl(cmp->c1, cpu_cc_dst); - break; - } -#endif - cmp->g1 = true; - cmp->c1 = cpu_cc_dst; + switch (cond & 7) { + case 0x0: /* never */ + cmp->cond = TCG_COND_NEVER; + cmp->c1 = tcg_constant_tl(0); break; - case CC_OP_SUB: - switch (cond) { - case 6: /* neg */ - case 14: /* pos */ - cmp->cond = (cond == 6 ? TCG_COND_LT : TCG_COND_GE); - goto do_compare_dst_0; - - case 7: /* overflow */ - case 15: /* !overflow */ - goto do_dynamic; - - default: - cmp->cond = subcc_cond[cond]; - cmp->is_bool = false; -#ifdef TARGET_SPARC64 - if (!xcc) { - /* Note that sign-extension works for unsigned compares as - long as both operands are sign-extended. */ - cmp->g1 = cmp->g2 = false; - cmp->c1 = tcg_temp_new(); - cmp->c2 = tcg_temp_new(); - tcg_gen_ext32s_tl(cmp->c1, cpu_cc_src); - tcg_gen_ext32s_tl(cmp->c2, cpu_cc_src2); - break; - } -#endif - cmp->g1 = cmp->g2 = true; - cmp->c1 = cpu_cc_src; - cmp->c2 = cpu_cc_src2; - break; + case 0x1: /* eq: Z */ + cmp->cond = TCG_COND_EQ; + if (TARGET_LONG_BITS == 32 || xcc) { + tcg_gen_mov_tl(t1, cpu_cc_Z); + } else { + tcg_gen_ext32u_tl(t1, cpu_icc_Z); } break; - default: - do_dynamic: - gen_helper_compute_psr(cpu_env); - dc->cc_op = CC_OP_FLAGS; - /* FALLTHRU */ + case 0x2: /* le: Z | (N ^ V) */ + /* + * Simplify: + * cc_Z || (N ^ V) < 0 NE + * cc_Z && !((N ^ V) < 0) EQ + * cc_Z & ~((N ^ V) >> TLB) EQ + */ + cmp->cond = TCG_COND_EQ; + tcg_gen_xor_tl(t1, cpu_cc_N, cpu_cc_V); + tcg_gen_sextract_tl(t1, t1, xcc ? 63 : 31, 1); + tcg_gen_andc_tl(t1, xcc ? cpu_cc_Z : cpu_icc_Z, t1); + if (TARGET_LONG_BITS == 64 && !xcc) { + tcg_gen_ext32u_tl(t1, t1); + } + break; - case CC_OP_FLAGS: - /* We're going to generate a boolean result. */ + case 0x3: /* lt: N ^ V */ + cmp->cond = TCG_COND_LT; + tcg_gen_xor_tl(t1, cpu_cc_N, cpu_cc_V); + if (TARGET_LONG_BITS == 64 && !xcc) { + tcg_gen_ext32s_tl(t1, t1); + } + break; + + case 0x4: /* leu: Z | C */ + /* + * Simplify: + * cc_Z == 0 || cc_C != 0 NE + * cc_Z != 0 && cc_C == 0 EQ + * cc_Z & (cc_C ? 0 : -1) EQ + * cc_Z & (cc_C - 1) EQ + */ + cmp->cond = TCG_COND_EQ; + if (TARGET_LONG_BITS == 32 || xcc) { + tcg_gen_subi_tl(t1, cpu_cc_C, 1); + tcg_gen_and_tl(t1, t1, cpu_cc_Z); + } else { + tcg_gen_extract_tl(t1, cpu_icc_C, 32, 1); + tcg_gen_subi_tl(t1, t1, 1); + tcg_gen_and_tl(t1, t1, cpu_icc_Z); + tcg_gen_ext32u_tl(t1, t1); + } + break; + + case 0x5: /* ltu: C */ cmp->cond = TCG_COND_NE; - cmp->is_bool = true; - cmp->g1 = cmp->g2 = false; - cmp->c1 = r_dst = tcg_temp_new(); - cmp->c2 = tcg_const_tl(0); - - switch (cond) { - case 0x0: - gen_op_eval_bn(r_dst); - break; - case 0x1: - gen_op_eval_be(r_dst, r_src); - break; - case 0x2: - gen_op_eval_ble(r_dst, r_src); - break; - case 0x3: - gen_op_eval_bl(r_dst, r_src); - break; - case 0x4: - gen_op_eval_bleu(r_dst, r_src); - break; - case 0x5: - gen_op_eval_bcs(r_dst, r_src); - break; - case 0x6: - gen_op_eval_bneg(r_dst, r_src); - break; - case 0x7: - gen_op_eval_bvs(r_dst, r_src); - break; - case 0x8: - gen_op_eval_ba(r_dst); - break; - case 0x9: - gen_op_eval_bne(r_dst, r_src); - break; - case 0xa: - gen_op_eval_bg(r_dst, r_src); - break; - case 0xb: - gen_op_eval_bge(r_dst, r_src); - break; - case 0xc: - gen_op_eval_bgu(r_dst, r_src); - break; - case 0xd: - gen_op_eval_bcc(r_dst, r_src); - break; - case 0xe: - gen_op_eval_bpos(r_dst, r_src); - break; - case 0xf: - gen_op_eval_bvc(r_dst, r_src); - break; + if (TARGET_LONG_BITS == 32 || xcc) { + tcg_gen_mov_tl(t1, cpu_cc_C); + } else { + tcg_gen_extract_tl(t1, cpu_icc_C, 32, 1); } break; + + case 0x6: /* neg: N */ + cmp->cond = TCG_COND_LT; + if (TARGET_LONG_BITS == 32 || xcc) { + tcg_gen_mov_tl(t1, cpu_cc_N); + } else { + tcg_gen_ext32s_tl(t1, cpu_cc_N); + } + break; + + case 0x7: /* vs: V */ + cmp->cond = TCG_COND_LT; + if (TARGET_LONG_BITS == 32 || xcc) { + tcg_gen_mov_tl(t1, cpu_cc_V); + } else { + tcg_gen_ext32s_tl(t1, cpu_cc_V); + } + break; + } + if (cond & 8) { + cmp->cond = tcg_invert_cond(cmp->cond); } } static void gen_fcompare(DisasCompare *cmp, unsigned int cc, unsigned int cond) { - unsigned int offset; - TCGv r_dst; + TCGv_i32 fcc = cpu_fcc[cc]; + TCGv_i32 c1 = fcc; + int c2 = 0; + TCGCond tcond; - /* For now we still generate a straight boolean result. */ - cmp->cond = TCG_COND_NE; - cmp->is_bool = true; - cmp->g1 = cmp->g2 = false; - cmp->c1 = r_dst = tcg_temp_new(); - cmp->c2 = tcg_const_tl(0); - - switch (cc) { - default: - case 0x0: - offset = 0; + /* + * FCC values: + * 0 = + * 1 < + * 2 > + * 3 unordered + */ + switch (cond & 7) { + case 0x0: /* fbn */ + tcond = TCG_COND_NEVER; break; - case 0x1: - offset = 32 - 10; + case 0x1: /* fbne : !0 */ + tcond = TCG_COND_NE; break; - case 0x2: - offset = 34 - 10; + case 0x2: /* fblg : 1 or 2 */ + /* fcc in {1,2} - 1 -> fcc in {0,1} */ + c1 = tcg_temp_new_i32(); + tcg_gen_addi_i32(c1, fcc, -1); + c2 = 1; + tcond = TCG_COND_LEU; break; - case 0x3: - offset = 36 - 10; + case 0x3: /* fbul : 1 or 3 */ + c1 = tcg_temp_new_i32(); + tcg_gen_andi_i32(c1, fcc, 1); + tcond = TCG_COND_NE; + break; + case 0x4: /* fbl : 1 */ + c2 = 1; + tcond = TCG_COND_EQ; + break; + case 0x5: /* fbug : 2 or 3 */ + c2 = 2; + tcond = TCG_COND_GEU; + break; + case 0x6: /* fbg : 2 */ + c2 = 2; + tcond = TCG_COND_EQ; + break; + case 0x7: /* fbu : 3 */ + c2 = 3; + tcond = TCG_COND_EQ; break; } - - switch (cond) { - case 0x0: - gen_op_eval_bn(r_dst); - break; - case 0x1: - gen_op_eval_fbne(r_dst, cpu_fsr, offset); - break; - case 0x2: - gen_op_eval_fblg(r_dst, cpu_fsr, offset); - break; - case 0x3: - gen_op_eval_fbul(r_dst, cpu_fsr, offset); - break; - case 0x4: - gen_op_eval_fbl(r_dst, cpu_fsr, offset); - break; - case 0x5: - gen_op_eval_fbug(r_dst, cpu_fsr, offset); - break; - case 0x6: - gen_op_eval_fbg(r_dst, cpu_fsr, offset); - break; - case 0x7: - gen_op_eval_fbu(r_dst, cpu_fsr, offset); - break; - case 0x8: - gen_op_eval_ba(r_dst); - break; - case 0x9: - gen_op_eval_fbe(r_dst, cpu_fsr, offset); - break; - case 0xa: - gen_op_eval_fbue(r_dst, cpu_fsr, offset); - break; - case 0xb: - gen_op_eval_fbge(r_dst, cpu_fsr, offset); - break; - case 0xc: - gen_op_eval_fbuge(r_dst, cpu_fsr, offset); - break; - case 0xd: - gen_op_eval_fble(r_dst, cpu_fsr, offset); - break; - case 0xe: - gen_op_eval_fbule(r_dst, cpu_fsr, offset); - break; - case 0xf: - gen_op_eval_fbo(r_dst, cpu_fsr, offset); - break; - } -} - -static void gen_cond(TCGv r_dst, unsigned int cc, unsigned int cond, - DisasContext *dc) -{ - DisasCompare cmp; - gen_compare(&cmp, cc, cond, dc); - - /* The interface is to return a boolean in r_dst. */ - if (cmp.is_bool) { - tcg_gen_mov_tl(r_dst, cmp.c1); - } else { - tcg_gen_setcond_tl(cmp.cond, r_dst, cmp.c1, cmp.c2); + if (cond & 8) { + tcond = tcg_invert_cond(tcond); } - free_compare(&cmp); + cmp->cond = tcond; + cmp->c2 = c2; + cmp->c1 = tcg_temp_new(); + tcg_gen_extu_i32_tl(cmp->c1, c1); } -static void gen_fcond(TCGv r_dst, unsigned int cc, unsigned int cond) +static bool gen_compare_reg(DisasCompare *cmp, int cond, TCGv r_src) { - DisasCompare cmp; - gen_fcompare(&cmp, cc, cond); + static const TCGCond cond_reg[4] = { + TCG_COND_NEVER, /* reserved */ + TCG_COND_EQ, + TCG_COND_LE, + TCG_COND_LT, + }; + TCGCond tcond; - /* The interface is to return a boolean in r_dst. */ - if (cmp.is_bool) { - tcg_gen_mov_tl(r_dst, cmp.c1); - } else { - tcg_gen_setcond_tl(cmp.cond, r_dst, cmp.c1, cmp.c2); + if ((cond & 3) == 0) { + return false; + } + tcond = cond_reg[cond & 3]; + if (cond & 4) { + tcond = tcg_invert_cond(tcond); } - free_compare(&cmp); + cmp->cond = tcond; + cmp->c1 = tcg_temp_new(); + cmp->c2 = 0; + tcg_gen_mov_tl(cmp->c1, r_src); + return true; } -#ifdef TARGET_SPARC64 -// Inverted logic -static const int gen_tcg_cond_reg[8] = { - -1, - TCG_COND_NE, - TCG_COND_GT, - TCG_COND_GE, - -1, - TCG_COND_EQ, - TCG_COND_LE, - TCG_COND_LT, -}; - -static void gen_compare_reg(DisasCompare *cmp, int cond, TCGv r_src) +static void gen_op_clear_ieee_excp_and_FTT(void) { - cmp->cond = tcg_invert_cond(gen_tcg_cond_reg[cond]); - cmp->is_bool = false; - cmp->g1 = true; - cmp->g2 = false; - cmp->c1 = r_src; - cmp->c2 = tcg_const_tl(0); + tcg_gen_st_i32(tcg_constant_i32(0), tcg_env, + offsetof(CPUSPARCState, fsr_cexc_ftt)); } -static inline void gen_cond_reg(TCGv r_dst, int cond, TCGv r_src) +static void gen_op_fmovs(TCGv_i32 dst, TCGv_i32 src) { - DisasCompare cmp; - gen_compare_reg(&cmp, cond, r_src); - - /* The interface is to return a boolean in r_dst. */ - tcg_gen_setcond_tl(cmp.cond, r_dst, cmp.c1, cmp.c2); - - free_compare(&cmp); -} -#endif - -static void do_branch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) -{ - unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29)); - target_ulong target = dc->pc + offset; - -#ifdef TARGET_SPARC64 - if (unlikely(AM_CHECK(dc))) { - target &= 0xffffffffULL; - } -#endif - if (cond == 0x0) { - /* unconditional not taken */ - if (a) { - dc->pc = dc->npc + 4; - dc->npc = dc->pc + 4; - } else { - dc->pc = dc->npc; - dc->npc = dc->pc + 4; - } - } else if (cond == 0x8) { - /* unconditional taken */ - if (a) { - dc->pc = target; - dc->npc = dc->pc + 4; - } else { - dc->pc = dc->npc; - dc->npc = target; - tcg_gen_mov_tl(cpu_pc, cpu_npc); - } - } else { - flush_cond(dc); - gen_cond(cpu_cond, cc, cond, dc); - if (a) { - gen_branch_a(dc, target); - } else { - gen_branch_n(dc, target); - } - } + gen_op_clear_ieee_excp_and_FTT(); + tcg_gen_mov_i32(dst, src); } -static void do_fbranch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) +static void gen_op_fnegs(TCGv_i32 dst, TCGv_i32 src) { - unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29)); - target_ulong target = dc->pc + offset; - -#ifdef TARGET_SPARC64 - if (unlikely(AM_CHECK(dc))) { - target &= 0xffffffffULL; - } -#endif - if (cond == 0x0) { - /* unconditional not taken */ - if (a) { - dc->pc = dc->npc + 4; - dc->npc = dc->pc + 4; - } else { - dc->pc = dc->npc; - dc->npc = dc->pc + 4; - } - } else if (cond == 0x8) { - /* unconditional taken */ - if (a) { - dc->pc = target; - dc->npc = dc->pc + 4; - } else { - dc->pc = dc->npc; - dc->npc = target; - tcg_gen_mov_tl(cpu_pc, cpu_npc); - } - } else { - flush_cond(dc); - gen_fcond(cpu_cond, cc, cond); - if (a) { - gen_branch_a(dc, target); - } else { - gen_branch_n(dc, target); - } - } + gen_op_clear_ieee_excp_and_FTT(); + tcg_gen_xori_i32(dst, src, 1u << 31); } -#ifdef TARGET_SPARC64 -static void do_branch_reg(DisasContext *dc, int32_t offset, uint32_t insn, - TCGv r_reg) +static void gen_op_fabss(TCGv_i32 dst, TCGv_i32 src) { - unsigned int cond = GET_FIELD_SP(insn, 25, 27), a = (insn & (1 << 29)); - target_ulong target = dc->pc + offset; - - if (unlikely(AM_CHECK(dc))) { - target &= 0xffffffffULL; - } - flush_cond(dc); - gen_cond_reg(cpu_cond, cond, r_reg); - if (a) { - gen_branch_a(dc, target); - } else { - gen_branch_n(dc, target); - } + gen_op_clear_ieee_excp_and_FTT(); + tcg_gen_andi_i32(dst, src, ~(1u << 31)); } -static inline void gen_op_fcmps(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2) +static void gen_op_fmovd(TCGv_i64 dst, TCGv_i64 src) { - switch (fccno) { - case 0: - gen_helper_fcmps(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - case 1: - gen_helper_fcmps_fcc1(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - case 2: - gen_helper_fcmps_fcc2(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - case 3: - gen_helper_fcmps_fcc3(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - } + gen_op_clear_ieee_excp_and_FTT(); + tcg_gen_mov_i64(dst, src); } -static inline void gen_op_fcmpd(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2) +static void gen_op_fnegd(TCGv_i64 dst, TCGv_i64 src) { - switch (fccno) { - case 0: - gen_helper_fcmpd(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - case 1: - gen_helper_fcmpd_fcc1(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - case 2: - gen_helper_fcmpd_fcc2(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - case 3: - gen_helper_fcmpd_fcc3(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - } + gen_op_clear_ieee_excp_and_FTT(); + tcg_gen_xori_i64(dst, src, 1ull << 63); } -static inline void gen_op_fcmpq(int fccno) +static void gen_op_fabsd(TCGv_i64 dst, TCGv_i64 src) { - switch (fccno) { - case 0: - gen_helper_fcmpq(cpu_fsr, cpu_env); - break; - case 1: - gen_helper_fcmpq_fcc1(cpu_fsr, cpu_env); - break; - case 2: - gen_helper_fcmpq_fcc2(cpu_fsr, cpu_env); - break; - case 3: - gen_helper_fcmpq_fcc3(cpu_fsr, cpu_env); - break; - } + gen_op_clear_ieee_excp_and_FTT(); + tcg_gen_andi_i64(dst, src, ~(1ull << 63)); } -static inline void gen_op_fcmpes(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2) +static void gen_op_fnegq(TCGv_i128 dst, TCGv_i128 src) { - switch (fccno) { - case 0: - gen_helper_fcmpes(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - case 1: - gen_helper_fcmpes_fcc1(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - case 2: - gen_helper_fcmpes_fcc2(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - case 3: - gen_helper_fcmpes_fcc3(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - } + TCGv_i64 l = tcg_temp_new_i64(); + TCGv_i64 h = tcg_temp_new_i64(); + + tcg_gen_extr_i128_i64(l, h, src); + tcg_gen_xori_i64(h, h, 1ull << 63); + tcg_gen_concat_i64_i128(dst, l, h); } -static inline void gen_op_fcmped(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2) +static void gen_op_fabsq(TCGv_i128 dst, TCGv_i128 src) { - switch (fccno) { - case 0: - gen_helper_fcmped(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - case 1: - gen_helper_fcmped_fcc1(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - case 2: - gen_helper_fcmped_fcc2(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - case 3: - gen_helper_fcmped_fcc3(cpu_fsr, cpu_env, r_rs1, r_rs2); - break; - } + TCGv_i64 l = tcg_temp_new_i64(); + TCGv_i64 h = tcg_temp_new_i64(); + + tcg_gen_extr_i128_i64(l, h, src); + tcg_gen_andi_i64(h, h, ~(1ull << 63)); + tcg_gen_concat_i64_i128(dst, l, h); } -static inline void gen_op_fcmpeq(int fccno) +static void gen_op_fpexception_im(DisasContext *dc, int ftt) { - switch (fccno) { - case 0: - gen_helper_fcmpeq(cpu_fsr, cpu_env); - break; - case 1: - gen_helper_fcmpeq_fcc1(cpu_fsr, cpu_env); - break; - case 2: - gen_helper_fcmpeq_fcc2(cpu_fsr, cpu_env); - break; - case 3: - gen_helper_fcmpeq_fcc3(cpu_fsr, cpu_env); - break; - } -} - -#else - -static inline void gen_op_fcmps(int fccno, TCGv r_rs1, TCGv r_rs2) -{ - gen_helper_fcmps(cpu_fsr, cpu_env, r_rs1, r_rs2); -} - -static inline void gen_op_fcmpd(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2) -{ - gen_helper_fcmpd(cpu_fsr, cpu_env, r_rs1, r_rs2); -} - -static inline void gen_op_fcmpq(int fccno) -{ - gen_helper_fcmpq(cpu_fsr, cpu_env); -} - -static inline void gen_op_fcmpes(int fccno, TCGv r_rs1, TCGv r_rs2) -{ - gen_helper_fcmpes(cpu_fsr, cpu_env, r_rs1, r_rs2); -} - -static inline void gen_op_fcmped(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2) -{ - gen_helper_fcmped(cpu_fsr, cpu_env, r_rs1, r_rs2); -} - -static inline void gen_op_fcmpeq(int fccno) -{ - gen_helper_fcmpeq(cpu_fsr, cpu_env); -} -#endif - -static void gen_op_fpexception_im(DisasContext *dc, int fsr_flags) -{ - tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_NMASK); - tcg_gen_ori_tl(cpu_fsr, cpu_fsr, fsr_flags); + /* + * CEXC is only set when succesfully completing an FPop, + * or when raising FSR_FTT_IEEE_EXCP, i.e. check_ieee_exception. + * Thus we can simply store FTT into this field. + */ + tcg_gen_st_i32(tcg_constant_i32(ftt), tcg_env, + offsetof(CPUSPARCState, fsr_cexc_ftt)); gen_exception(dc, TT_FP_EXCP); } @@ -1687,349 +1111,13 @@ static int gen_trap_ifnofpu(DisasContext *dc) return 0; } -static inline void gen_op_clear_ieee_excp_and_FTT(void) -{ - tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_CEXC_NMASK); -} - -static inline void gen_fop_FF(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i32)) -{ - TCGv_i32 dst, src; - - src = gen_load_fpr_F(dc, rs); - dst = gen_dest_fpr_F(dc); - - gen(dst, cpu_env, src); - gen_helper_check_ieee_exceptions(cpu_fsr, cpu_env); - - gen_store_fpr_F(dc, rd, dst); -} - -static inline void gen_ne_fop_FF(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i32, TCGv_i32)) -{ - TCGv_i32 dst, src; - - src = gen_load_fpr_F(dc, rs); - dst = gen_dest_fpr_F(dc); - - gen(dst, src); - - gen_store_fpr_F(dc, rd, dst); -} - -static inline void gen_fop_FFF(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32)) -{ - TCGv_i32 dst, src1, src2; - - src1 = gen_load_fpr_F(dc, rs1); - src2 = gen_load_fpr_F(dc, rs2); - dst = gen_dest_fpr_F(dc); - - gen(dst, cpu_env, src1, src2); - gen_helper_check_ieee_exceptions(cpu_fsr, cpu_env); - - gen_store_fpr_F(dc, rd, dst); -} - -#ifdef TARGET_SPARC64 -static inline void gen_ne_fop_FFF(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32)) -{ - TCGv_i32 dst, src1, src2; - - src1 = gen_load_fpr_F(dc, rs1); - src2 = gen_load_fpr_F(dc, rs2); - dst = gen_dest_fpr_F(dc); - - gen(dst, src1, src2); - - gen_store_fpr_F(dc, rd, dst); -} -#endif - -static inline void gen_fop_DD(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i64)) -{ - TCGv_i64 dst, src; - - src = gen_load_fpr_D(dc, rs); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, cpu_env, src); - gen_helper_check_ieee_exceptions(cpu_fsr, cpu_env); - - gen_store_fpr_D(dc, rd, dst); -} - -#ifdef TARGET_SPARC64 -static inline void gen_ne_fop_DD(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i64, TCGv_i64)) -{ - TCGv_i64 dst, src; - - src = gen_load_fpr_D(dc, rs); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, src); - - gen_store_fpr_D(dc, rd, dst); -} -#endif - -static inline void gen_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i64, TCGv_i64)) -{ - TCGv_i64 dst, src1, src2; - - src1 = gen_load_fpr_D(dc, rs1); - src2 = gen_load_fpr_D(dc, rs2); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, cpu_env, src1, src2); - gen_helper_check_ieee_exceptions(cpu_fsr, cpu_env); - - gen_store_fpr_D(dc, rd, dst); -} - -#ifdef TARGET_SPARC64 -static inline void gen_ne_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64)) -{ - TCGv_i64 dst, src1, src2; - - src1 = gen_load_fpr_D(dc, rs1); - src2 = gen_load_fpr_D(dc, rs2); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, src1, src2); - - gen_store_fpr_D(dc, rd, dst); -} - -static inline void gen_gsr_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) -{ - TCGv_i64 dst, src1, src2; - - src1 = gen_load_fpr_D(dc, rs1); - src2 = gen_load_fpr_D(dc, rs2); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, cpu_gsr, src1, src2); - - gen_store_fpr_D(dc, rd, dst); -} - -static inline void gen_ne_fop_DDDD(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) -{ - TCGv_i64 dst, src0, src1, src2; - - src1 = gen_load_fpr_D(dc, rs1); - src2 = gen_load_fpr_D(dc, rs2); - src0 = gen_load_fpr_D(dc, rd); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, src0, src1, src2); - - gen_store_fpr_D(dc, rd, dst); -} -#endif - -static inline void gen_fop_QQ(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_ptr)) -{ - gen_op_load_fpr_QT1(QFPREG(rs)); - - gen(cpu_env); - gen_helper_check_ieee_exceptions(cpu_fsr, cpu_env); - - gen_op_store_QT0_fpr(QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); -} - -#ifdef TARGET_SPARC64 -static inline void gen_ne_fop_QQ(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_ptr)) -{ - gen_op_load_fpr_QT1(QFPREG(rs)); - - gen(cpu_env); - - gen_op_store_QT0_fpr(QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); -} -#endif - -static inline void gen_fop_QQQ(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_ptr)) -{ - gen_op_load_fpr_QT0(QFPREG(rs1)); - gen_op_load_fpr_QT1(QFPREG(rs2)); - - gen(cpu_env); - gen_helper_check_ieee_exceptions(cpu_fsr, cpu_env); - - gen_op_store_QT0_fpr(QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); -} - -static inline void gen_fop_DFF(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i32, TCGv_i32)) -{ - TCGv_i64 dst; - TCGv_i32 src1, src2; - - src1 = gen_load_fpr_F(dc, rs1); - src2 = gen_load_fpr_F(dc, rs2); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, cpu_env, src1, src2); - gen_helper_check_ieee_exceptions(cpu_fsr, cpu_env); - - gen_store_fpr_D(dc, rd, dst); -} - -static inline void gen_fop_QDD(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_ptr, TCGv_i64, TCGv_i64)) -{ - TCGv_i64 src1, src2; - - src1 = gen_load_fpr_D(dc, rs1); - src2 = gen_load_fpr_D(dc, rs2); - - gen(cpu_env, src1, src2); - gen_helper_check_ieee_exceptions(cpu_fsr, cpu_env); - - gen_op_store_QT0_fpr(QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); -} - -#ifdef TARGET_SPARC64 -static inline void gen_fop_DF(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i32)) -{ - TCGv_i64 dst; - TCGv_i32 src; - - src = gen_load_fpr_F(dc, rs); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, cpu_env, src); - gen_helper_check_ieee_exceptions(cpu_fsr, cpu_env); - - gen_store_fpr_D(dc, rd, dst); -} -#endif - -static inline void gen_ne_fop_DF(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i32)) -{ - TCGv_i64 dst; - TCGv_i32 src; - - src = gen_load_fpr_F(dc, rs); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, cpu_env, src); - - gen_store_fpr_D(dc, rd, dst); -} - -static inline void gen_fop_FD(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i64)) -{ - TCGv_i32 dst; - TCGv_i64 src; - - src = gen_load_fpr_D(dc, rs); - dst = gen_dest_fpr_F(dc); - - gen(dst, cpu_env, src); - gen_helper_check_ieee_exceptions(cpu_fsr, cpu_env); - - gen_store_fpr_F(dc, rd, dst); -} - -static inline void gen_fop_FQ(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i32, TCGv_ptr)) -{ - TCGv_i32 dst; - - gen_op_load_fpr_QT1(QFPREG(rs)); - dst = gen_dest_fpr_F(dc); - - gen(dst, cpu_env); - gen_helper_check_ieee_exceptions(cpu_fsr, cpu_env); - - gen_store_fpr_F(dc, rd, dst); -} - -static inline void gen_fop_DQ(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i64, TCGv_ptr)) -{ - TCGv_i64 dst; - - gen_op_load_fpr_QT1(QFPREG(rs)); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, cpu_env); - gen_helper_check_ieee_exceptions(cpu_fsr, cpu_env); - - gen_store_fpr_D(dc, rd, dst); -} - -static inline void gen_ne_fop_QF(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_ptr, TCGv_i32)) -{ - TCGv_i32 src; - - src = gen_load_fpr_F(dc, rs); - - gen(cpu_env, src); - - gen_op_store_QT0_fpr(QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); -} - -static inline void gen_ne_fop_QD(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_ptr, TCGv_i64)) -{ - TCGv_i64 src; - - src = gen_load_fpr_D(dc, rs); - - gen(cpu_env, src); - - gen_op_store_QT0_fpr(QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); -} - -static void gen_swap(DisasContext *dc, TCGv dst, TCGv src, - TCGv addr, int mmu_idx, MemOp memop) -{ - gen_address_mask(dc, addr); - tcg_gen_atomic_xchg_tl(dst, addr, src, mmu_idx, memop); -} - -static void gen_ldstub(DisasContext *dc, TCGv dst, TCGv addr, int mmu_idx) -{ - TCGv m1 = tcg_const_tl(0xff); - gen_address_mask(dc, addr); - tcg_gen_atomic_xchg_tl(dst, addr, m1, mmu_idx, MO_UB); - tcg_temp_free(m1); -} - /* asi moves */ -#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) typedef enum { GET_ASI_HELPER, GET_ASI_EXCP, GET_ASI_DIRECT, GET_ASI_DTWINX, + GET_ASI_CODE, GET_ASI_BLOCK, GET_ASI_SHORT, GET_ASI_BCOPY, @@ -2043,15 +1131,25 @@ typedef struct { MemOp memop; } DisasASI; -static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) +/* + * Build DisasASI. + * For asi == -1, treat as non-asi. + * For ask == -2, treat as immediate offset (v8 error, v9 %asi). + */ +static DisasASI resolve_asi(DisasContext *dc, int asi, MemOp memop) { - int asi = GET_FIELD(insn, 19, 26); ASIType type = GET_ASI_HELPER; int mem_idx = dc->mem_idx; + if (asi == -1) { + /* Artificial "non-asi" case. */ + type = GET_ASI_DIRECT; + goto done; + } + #ifndef TARGET_SPARC64 /* Before v9, all asis are immediate and privileged. */ - if (IS_IMM) { + if (asi < 0) { gen_exception(dc, TT_ILL_INSN); type = GET_ASI_EXCP; } else if (supervisor(dc) @@ -2062,14 +1160,22 @@ static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) || (asi == ASI_USERDATA && (dc->def->features & CPU_FEATURE_CASA))) { switch (asi) { - case ASI_USERDATA: /* User data access */ + case ASI_USERDATA: /* User data access */ mem_idx = MMU_USER_IDX; type = GET_ASI_DIRECT; break; - case ASI_KERNELDATA: /* Supervisor data access */ + case ASI_KERNELDATA: /* Supervisor data access */ mem_idx = MMU_KERNEL_IDX; type = GET_ASI_DIRECT; break; + case ASI_USERTXT: /* User text access */ + mem_idx = MMU_USER_IDX; + type = GET_ASI_CODE; + break; + case ASI_KERNELTXT: /* Supervisor text access */ + mem_idx = MMU_KERNEL_IDX; + type = GET_ASI_CODE; + break; case ASI_M_BYPASS: /* MMU passthrough */ case ASI_LEON_BYPASS: /* LEON MMU passthrough */ mem_idx = MMU_PHYS_IDX; @@ -2094,7 +1200,7 @@ static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) type = GET_ASI_EXCP; } #else - if (IS_IMM) { + if (asi < 0) { asi = dc->asi; } /* With v9, all asis below 0x80 are privileged. */ @@ -2253,123 +1359,141 @@ static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) } #endif + done: return (DisasASI){ type, asi, mem_idx, memop }; } -static void gen_ld_asi(DisasContext *dc, TCGv dst, TCGv addr, - int insn, MemOp memop) +#if defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) +static void gen_helper_ld_asi(TCGv_i64 r, TCGv_env e, TCGv a, + TCGv_i32 asi, TCGv_i32 mop) { - DisasASI da = get_asi(dc, insn, memop); + g_assert_not_reached(); +} - switch (da.type) { +static void gen_helper_st_asi(TCGv_env e, TCGv a, TCGv_i64 r, + TCGv_i32 asi, TCGv_i32 mop) +{ + g_assert_not_reached(); +} +#endif + +static void gen_ld_asi(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr) +{ + switch (da->type) { case GET_ASI_EXCP: break; case GET_ASI_DTWINX: /* Reserved for ldda. */ gen_exception(dc, TT_ILL_INSN); break; case GET_ASI_DIRECT: - gen_address_mask(dc, addr); - tcg_gen_qemu_ld_tl(dst, addr, da.mem_idx, da.memop); + tcg_gen_qemu_ld_tl(dst, addr, da->mem_idx, da->memop | MO_ALIGN); break; + + case GET_ASI_CODE: +#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) + { + MemOpIdx oi = make_memop_idx(da->memop, da->mem_idx); + TCGv_i64 t64 = tcg_temp_new_i64(); + + gen_helper_ld_code(t64, tcg_env, addr, tcg_constant_i32(oi)); + tcg_gen_trunc_i64_tl(dst, t64); + } + break; +#else + g_assert_not_reached(); +#endif + default: { - TCGv_i32 r_asi = tcg_const_i32(da.asi); - TCGv_i32 r_mop = tcg_const_i32(memop); + TCGv_i32 r_asi = tcg_constant_i32(da->asi); + TCGv_i32 r_mop = tcg_constant_i32(da->memop | MO_ALIGN); save_state(dc); #ifdef TARGET_SPARC64 - gen_helper_ld_asi(dst, cpu_env, addr, r_asi, r_mop); + gen_helper_ld_asi(dst, tcg_env, addr, r_asi, r_mop); #else { TCGv_i64 t64 = tcg_temp_new_i64(); - gen_helper_ld_asi(t64, cpu_env, addr, r_asi, r_mop); + gen_helper_ld_asi(t64, tcg_env, addr, r_asi, r_mop); tcg_gen_trunc_i64_tl(dst, t64); - tcg_temp_free_i64(t64); } #endif - tcg_temp_free_i32(r_mop); - tcg_temp_free_i32(r_asi); } break; } } -static void gen_st_asi(DisasContext *dc, TCGv src, TCGv addr, - int insn, MemOp memop) +static void gen_st_asi(DisasContext *dc, DisasASI *da, TCGv src, TCGv addr) { - DisasASI da = get_asi(dc, insn, memop); - - switch (da.type) { + switch (da->type) { case GET_ASI_EXCP: break; + case GET_ASI_DTWINX: /* Reserved for stda. */ -#ifndef TARGET_SPARC64 - gen_exception(dc, TT_ILL_INSN); - break; -#else - if (!(dc->def->features & CPU_FEATURE_HYPV)) { + if (TARGET_LONG_BITS == 32) { + gen_exception(dc, TT_ILL_INSN); + break; + } else if (!(dc->def->features & CPU_FEATURE_HYPV)) { /* Pre OpenSPARC CPUs don't have these */ gen_exception(dc, TT_ILL_INSN); - return; + break; } - /* in OpenSPARC T1+ CPUs TWINX ASIs in store instructions - * are ST_BLKINIT_ ASIs */ -#endif + /* In OpenSPARC T1+ CPUs TWINX ASIs in store are ST_BLKINIT_ ASIs */ /* fall through */ + case GET_ASI_DIRECT: - gen_address_mask(dc, addr); - tcg_gen_qemu_st_tl(src, addr, da.mem_idx, da.memop); + tcg_gen_qemu_st_tl(src, addr, da->mem_idx, da->memop | MO_ALIGN); break; -#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) + case GET_ASI_BCOPY: - /* Copy 32 bytes from the address in SRC to ADDR. */ - /* ??? The original qemu code suggests 4-byte alignment, dropping - the low bits, but the only place I can see this used is in the - Linux kernel with 32 byte alignment, which would make more sense - as a cacheline-style operation. */ + assert(TARGET_LONG_BITS == 32); + /* + * Copy 32 bytes from the address in SRC to ADDR. + * + * From Ross RT625 hyperSPARC manual, section 4.6: + * "Block Copy and Block Fill will work only on cache line boundaries." + * + * It does not specify if an unaliged address is truncated or trapped. + * Previous qemu behaviour was to truncate to 4 byte alignment, which + * is obviously wrong. The only place I can see this used is in the + * Linux kernel which begins with page alignment, advancing by 32, + * so is always aligned. Assume truncation as the simpler option. + * + * Since the loads and stores are paired, allow the copy to happen + * in the host endianness. The copy need not be atomic. + */ { + MemOp mop = MO_128 | MO_ATOM_IFALIGN_PAIR; TCGv saddr = tcg_temp_new(); TCGv daddr = tcg_temp_new(); - TCGv four = tcg_const_tl(4); - TCGv_i32 tmp = tcg_temp_new_i32(); - int i; + TCGv_i128 tmp = tcg_temp_new_i128(); - tcg_gen_andi_tl(saddr, src, -4); - tcg_gen_andi_tl(daddr, addr, -4); - for (i = 0; i < 32; i += 4) { - /* Since the loads and stores are paired, allow the - copy to happen in the host endianness. */ - tcg_gen_qemu_ld_i32(tmp, saddr, da.mem_idx, MO_UL); - tcg_gen_qemu_st_i32(tmp, daddr, da.mem_idx, MO_UL); - tcg_gen_add_tl(saddr, saddr, four); - tcg_gen_add_tl(daddr, daddr, four); - } - - tcg_temp_free(saddr); - tcg_temp_free(daddr); - tcg_temp_free(four); - tcg_temp_free_i32(tmp); + tcg_gen_andi_tl(saddr, src, -32); + tcg_gen_andi_tl(daddr, addr, -32); + tcg_gen_qemu_ld_i128(tmp, saddr, da->mem_idx, mop); + tcg_gen_qemu_st_i128(tmp, daddr, da->mem_idx, mop); + tcg_gen_addi_tl(saddr, saddr, 16); + tcg_gen_addi_tl(daddr, daddr, 16); + tcg_gen_qemu_ld_i128(tmp, saddr, da->mem_idx, mop); + tcg_gen_qemu_st_i128(tmp, daddr, da->mem_idx, mop); } break; -#endif + default: { - TCGv_i32 r_asi = tcg_const_i32(da.asi); - TCGv_i32 r_mop = tcg_const_i32(memop & MO_SIZE); + TCGv_i32 r_asi = tcg_constant_i32(da->asi); + TCGv_i32 r_mop = tcg_constant_i32(da->memop | MO_ALIGN); save_state(dc); #ifdef TARGET_SPARC64 - gen_helper_st_asi(cpu_env, addr, src, r_asi, r_mop); + gen_helper_st_asi(tcg_env, addr, src, r_asi, r_mop); #else { TCGv_i64 t64 = tcg_temp_new_i64(); tcg_gen_extu_tl_i64(t64, src); - gen_helper_st_asi(cpu_env, addr, t64, r_asi, r_mop); - tcg_temp_free_i64(t64); + gen_helper_st_asi(tcg_env, addr, t64, r_asi, r_mop); } #endif - tcg_temp_free_i32(r_mop); - tcg_temp_free_i32(r_asi); /* A write to a TLB register may alter page maps. End the TB. */ dc->npc = DYNAMIC_PC; @@ -2378,16 +1502,15 @@ static void gen_st_asi(DisasContext *dc, TCGv src, TCGv addr, } } -static void gen_swap_asi(DisasContext *dc, TCGv dst, TCGv src, - TCGv addr, int insn) +static void gen_swap_asi(DisasContext *dc, DisasASI *da, + TCGv dst, TCGv src, TCGv addr) { - DisasASI da = get_asi(dc, insn, MO_TEUL); - - switch (da.type) { + switch (da->type) { case GET_ASI_EXCP: break; case GET_ASI_DIRECT: - gen_swap(dc, dst, src, addr, da.mem_idx, da.memop); + tcg_gen_atomic_xchg_tl(dst, addr, src, + da->mem_idx, da->memop | MO_ALIGN); break; default: /* ??? Should be DAE_invalid_asi. */ @@ -2396,21 +1519,15 @@ static void gen_swap_asi(DisasContext *dc, TCGv dst, TCGv src, } } -static void gen_cas_asi(DisasContext *dc, TCGv addr, TCGv cmpv, - int insn, int rd) +static void gen_cas_asi(DisasContext *dc, DisasASI *da, + TCGv oldv, TCGv newv, TCGv cmpv, TCGv addr) { - DisasASI da = get_asi(dc, insn, MO_TEUL); - TCGv oldv; - - switch (da.type) { + switch (da->type) { case GET_ASI_EXCP: return; case GET_ASI_DIRECT: - oldv = tcg_temp_new(); - tcg_gen_atomic_cmpxchg_tl(oldv, addr, cmpv, gen_load_gpr(dc, rd), - da.mem_idx, da.memop); - gen_store_gpr(dc, rd, oldv); - tcg_temp_free(oldv); + tcg_gen_atomic_cmpxchg_tl(oldv, addr, cmpv, newv, + da->mem_idx, da->memop | MO_ALIGN); break; default: /* ??? Should be DAE_invalid_asi. */ @@ -2419,38 +1536,33 @@ static void gen_cas_asi(DisasContext *dc, TCGv addr, TCGv cmpv, } } -static void gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn) +static void gen_ldstub_asi(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr) { - DisasASI da = get_asi(dc, insn, MO_UB); - - switch (da.type) { + switch (da->type) { case GET_ASI_EXCP: break; case GET_ASI_DIRECT: - gen_ldstub(dc, dst, addr, da.mem_idx); + tcg_gen_atomic_xchg_tl(dst, addr, tcg_constant_tl(0xff), + da->mem_idx, MO_UB); break; default: /* ??? In theory, this should be raise DAE_invalid_asi. But the SS-20 roms do ldstuba [%l0] #ASI_M_CTL, %o1. */ if (tb_cflags(dc->base.tb) & CF_PARALLEL) { - gen_helper_exit_atomic(cpu_env); + gen_helper_exit_atomic(tcg_env); } else { - TCGv_i32 r_asi = tcg_const_i32(da.asi); - TCGv_i32 r_mop = tcg_const_i32(MO_UB); + TCGv_i32 r_asi = tcg_constant_i32(da->asi); + TCGv_i32 r_mop = tcg_constant_i32(MO_UB); TCGv_i64 s64, t64; save_state(dc); t64 = tcg_temp_new_i64(); - gen_helper_ld_asi(t64, cpu_env, addr, r_asi, r_mop); + gen_helper_ld_asi(t64, tcg_env, addr, r_asi, r_mop); - s64 = tcg_const_i64(0xff); - gen_helper_st_asi(cpu_env, addr, s64, r_asi, r_mop); - tcg_temp_free_i64(s64); - tcg_temp_free_i32(r_mop); - tcg_temp_free_i32(r_asi); + s64 = tcg_constant_i64(0xff); + gen_helper_st_asi(tcg_env, addr, s64, r_asi, r_mop); tcg_gen_trunc_i64_tl(dst, t64); - tcg_temp_free_i64(t64); /* End the TB. */ dc->npc = DYNAMIC_PC; @@ -2458,40 +1570,45 @@ static void gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn) break; } } -#endif -#ifdef TARGET_SPARC64 -static void gen_ldf_asi(DisasContext *dc, TCGv addr, - int insn, int size, int rd) +static void gen_ldf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, + TCGv addr, int rd) { - DisasASI da = get_asi(dc, insn, (size == 4 ? MO_TEUL : MO_TEUQ)); + MemOp memop = da->memop; + MemOp size = memop & MO_SIZE; TCGv_i32 d32; TCGv_i64 d64; + TCGv addr_tmp; - switch (da.type) { + /* TODO: Use 128-bit load/store below. */ + if (size == MO_128) { + memop = (memop & ~MO_SIZE) | MO_64; + } + + switch (da->type) { case GET_ASI_EXCP: break; case GET_ASI_DIRECT: - gen_address_mask(dc, addr); + memop |= MO_ALIGN_4; switch (size) { - case 4: - d32 = gen_dest_fpr_F(dc); - tcg_gen_qemu_ld_i32(d32, addr, da.mem_idx, da.memop); + case MO_32: + d32 = tcg_temp_new_i32(); + tcg_gen_qemu_ld_i32(d32, addr, da->mem_idx, memop); gen_store_fpr_F(dc, rd, d32); break; - case 8: - tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da.mem_idx, - da.memop | MO_ALIGN_4); + + case MO_64: + tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da->mem_idx, memop); break; - case 16: + + case MO_128: d64 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(d64, addr, da.mem_idx, da.memop | MO_ALIGN_4); - tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_ld_i64(cpu_fpr[rd/2+1], addr, da.mem_idx, - da.memop | MO_ALIGN_4); + tcg_gen_qemu_ld_i64(d64, addr, da->mem_idx, memop); + addr_tmp = tcg_temp_new(); + tcg_gen_addi_tl(addr_tmp, addr, 8); + tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + 1], addr_tmp, da->mem_idx, memop); tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); - tcg_temp_free_i64(d64); break; default: g_assert_not_reached(); @@ -2500,26 +1617,18 @@ static void gen_ldf_asi(DisasContext *dc, TCGv addr, case GET_ASI_BLOCK: /* Valid for lddfa on aligned registers only. */ - if (size == 8 && (rd & 7) == 0) { - MemOp memop; - TCGv eight; - int i; - - gen_address_mask(dc, addr); - + if (orig_size == MO_64 && (rd & 7) == 0) { /* The first operation checks required alignment. */ - memop = da.memop | MO_ALIGN_64; - eight = tcg_const_tl(8); - for (i = 0; ; ++i) { - tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + i], addr, - da.mem_idx, memop); + addr_tmp = tcg_temp_new(); + for (int i = 0; ; ++i) { + tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + i], addr, da->mem_idx, + memop | (i == 0 ? MO_ALIGN_64 : 0)); if (i == 7) { break; } - tcg_gen_add_tl(addr, addr, eight); - memop = da.memop; + tcg_gen_addi_tl(addr_tmp, addr, 8); + addr = addr_tmp; } - tcg_temp_free(eight); } else { gen_exception(dc, TT_ILL_INSN); } @@ -2527,9 +1636,9 @@ static void gen_ldf_asi(DisasContext *dc, TCGv addr, case GET_ASI_SHORT: /* Valid for lddfa only. */ - if (size == 8) { - gen_address_mask(dc, addr); - tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop); + if (orig_size == MO_64) { + tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da->mem_idx, + memop | MO_ALIGN); } else { gen_exception(dc, TT_ILL_INSN); } @@ -2537,8 +1646,8 @@ static void gen_ldf_asi(DisasContext *dc, TCGv addr, default: { - TCGv_i32 r_asi = tcg_const_i32(da.asi); - TCGv_i32 r_mop = tcg_const_i32(da.memop); + TCGv_i32 r_asi = tcg_constant_i32(da->asi); + TCGv_i32 r_mop = tcg_constant_i32(memop | MO_ALIGN); save_state(dc); /* According to the table in the UA2011 manual, the only @@ -2546,66 +1655,73 @@ static void gen_ldf_asi(DisasContext *dc, TCGv addr, the NO_FAULT asis. We still need a helper for these, but we can just use the integer asi helper for them. */ switch (size) { - case 4: + case MO_32: d64 = tcg_temp_new_i64(); - gen_helper_ld_asi(d64, cpu_env, addr, r_asi, r_mop); - d32 = gen_dest_fpr_F(dc); + gen_helper_ld_asi(d64, tcg_env, addr, r_asi, r_mop); + d32 = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(d32, d64); - tcg_temp_free_i64(d64); gen_store_fpr_F(dc, rd, d32); break; - case 8: - gen_helper_ld_asi(cpu_fpr[rd / 2], cpu_env, addr, r_asi, r_mop); + case MO_64: + gen_helper_ld_asi(cpu_fpr[rd / 2], tcg_env, addr, + r_asi, r_mop); break; - case 16: + case MO_128: d64 = tcg_temp_new_i64(); - gen_helper_ld_asi(d64, cpu_env, addr, r_asi, r_mop); - tcg_gen_addi_tl(addr, addr, 8); - gen_helper_ld_asi(cpu_fpr[rd/2+1], cpu_env, addr, r_asi, r_mop); + gen_helper_ld_asi(d64, tcg_env, addr, r_asi, r_mop); + addr_tmp = tcg_temp_new(); + tcg_gen_addi_tl(addr_tmp, addr, 8); + gen_helper_ld_asi(cpu_fpr[rd / 2 + 1], tcg_env, addr_tmp, + r_asi, r_mop); tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); - tcg_temp_free_i64(d64); break; default: g_assert_not_reached(); } - tcg_temp_free_i32(r_mop); - tcg_temp_free_i32(r_asi); } break; } } -static void gen_stf_asi(DisasContext *dc, TCGv addr, - int insn, int size, int rd) +static void gen_stf_asi(DisasContext *dc, DisasASI *da, MemOp orig_size, + TCGv addr, int rd) { - DisasASI da = get_asi(dc, insn, (size == 4 ? MO_TEUL : MO_TEUQ)); + MemOp memop = da->memop; + MemOp size = memop & MO_SIZE; TCGv_i32 d32; + TCGv addr_tmp; - switch (da.type) { + /* TODO: Use 128-bit load/store below. */ + if (size == MO_128) { + memop = (memop & ~MO_SIZE) | MO_64; + } + + switch (da->type) { case GET_ASI_EXCP: break; case GET_ASI_DIRECT: - gen_address_mask(dc, addr); + memop |= MO_ALIGN_4; switch (size) { - case 4: + case MO_32: d32 = gen_load_fpr_F(dc, rd); - tcg_gen_qemu_st_i32(d32, addr, da.mem_idx, da.memop); + tcg_gen_qemu_st_i32(d32, addr, da->mem_idx, memop | MO_ALIGN); break; - case 8: - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, - da.memop | MO_ALIGN_4); + case MO_64: + tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da->mem_idx, + memop | MO_ALIGN_4); break; - case 16: + case MO_128: /* Only 4-byte alignment required. However, it is legal for the cpu to signal the alignment fault, and the OS trap handler is required to fix it up. Requiring 16-byte alignment here avoids having to probe the second page before performing the first write. */ - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, - da.memop | MO_ALIGN_16); - tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_st_i64(cpu_fpr[rd/2+1], addr, da.mem_idx, da.memop); + tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da->mem_idx, + memop | MO_ALIGN_16); + addr_tmp = tcg_temp_new(); + tcg_gen_addi_tl(addr_tmp, addr, 8); + tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + 1], addr_tmp, da->mem_idx, memop); break; default: g_assert_not_reached(); @@ -2614,26 +1730,18 @@ static void gen_stf_asi(DisasContext *dc, TCGv addr, case GET_ASI_BLOCK: /* Valid for stdfa on aligned registers only. */ - if (size == 8 && (rd & 7) == 0) { - MemOp memop; - TCGv eight; - int i; - - gen_address_mask(dc, addr); - + if (orig_size == MO_64 && (rd & 7) == 0) { /* The first operation checks required alignment. */ - memop = da.memop | MO_ALIGN_64; - eight = tcg_const_tl(8); - for (i = 0; ; ++i) { - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + i], addr, - da.mem_idx, memop); + addr_tmp = tcg_temp_new(); + for (int i = 0; ; ++i) { + tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + i], addr, da->mem_idx, + memop | (i == 0 ? MO_ALIGN_64 : 0)); if (i == 7) { break; } - tcg_gen_add_tl(addr, addr, eight); - memop = da.memop; + tcg_gen_addi_tl(addr_tmp, addr, 8); + addr = addr_tmp; } - tcg_temp_free(eight); } else { gen_exception(dc, TT_ILL_INSN); } @@ -2641,9 +1749,9 @@ static void gen_stf_asi(DisasContext *dc, TCGv addr, case GET_ASI_SHORT: /* Valid for stdfa only. */ - if (size == 8) { - gen_address_mask(dc, addr); - tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop); + if (orig_size == MO_64) { + tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da->mem_idx, + memop | MO_ALIGN); } else { gen_exception(dc, TT_ILL_INSN); } @@ -2658,64 +1766,94 @@ static void gen_stf_asi(DisasContext *dc, TCGv addr, } } -static void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) +static void gen_ldda_asi(DisasContext *dc, DisasASI *da, TCGv addr, int rd) { - DisasASI da = get_asi(dc, insn, MO_TEUQ); - TCGv_i64 hi = gen_dest_gpr(dc, rd); - TCGv_i64 lo = gen_dest_gpr(dc, rd + 1); + TCGv hi = gen_dest_gpr(dc, rd); + TCGv lo = gen_dest_gpr(dc, rd + 1); - switch (da.type) { + switch (da->type) { case GET_ASI_EXCP: return; case GET_ASI_DTWINX: - gen_address_mask(dc, addr); - tcg_gen_qemu_ld_i64(hi, addr, da.mem_idx, da.memop | MO_ALIGN_16); - tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_ld_i64(lo, addr, da.mem_idx, da.memop); +#ifdef TARGET_SPARC64 + { + MemOp mop = (da->memop & MO_BSWAP) | MO_128 | MO_ALIGN_16; + TCGv_i128 t = tcg_temp_new_i128(); + + tcg_gen_qemu_ld_i128(t, addr, da->mem_idx, mop); + /* + * Note that LE twinx acts as if each 64-bit register result is + * byte swapped. We perform one 128-bit LE load, so must swap + * the order of the writebacks. + */ + if ((mop & MO_BSWAP) == MO_TE) { + tcg_gen_extr_i128_i64(lo, hi, t); + } else { + tcg_gen_extr_i128_i64(hi, lo, t); + } + } break; +#else + g_assert_not_reached(); +#endif case GET_ASI_DIRECT: { TCGv_i64 tmp = tcg_temp_new_i64(); - gen_address_mask(dc, addr); - tcg_gen_qemu_ld_i64(tmp, addr, da.mem_idx, da.memop); + tcg_gen_qemu_ld_i64(tmp, addr, da->mem_idx, da->memop | MO_ALIGN); /* Note that LE ldda acts as if each 32-bit register result is byte swapped. Having just performed one 64-bit bswap, we need now to swap the writebacks. */ - if ((da.memop & MO_BSWAP) == MO_TE) { - tcg_gen_extr32_i64(lo, hi, tmp); + if ((da->memop & MO_BSWAP) == MO_TE) { + tcg_gen_extr_i64_tl(lo, hi, tmp); } else { - tcg_gen_extr32_i64(hi, lo, tmp); + tcg_gen_extr_i64_tl(hi, lo, tmp); } - tcg_temp_free_i64(tmp); } break; + case GET_ASI_CODE: +#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) + { + MemOpIdx oi = make_memop_idx(da->memop, da->mem_idx); + TCGv_i64 tmp = tcg_temp_new_i64(); + + gen_helper_ld_code(tmp, tcg_env, addr, tcg_constant_i32(oi)); + + /* See above. */ + if ((da->memop & MO_BSWAP) == MO_TE) { + tcg_gen_extr_i64_tl(lo, hi, tmp); + } else { + tcg_gen_extr_i64_tl(hi, lo, tmp); + } + } + break; +#else + g_assert_not_reached(); +#endif + default: /* ??? In theory we've handled all of the ASIs that are valid for ldda, and this should raise DAE_invalid_asi. However, real hardware allows others. This can be seen with e.g. FreeBSD 10.3 wrt ASI_IC_TAG. */ { - TCGv_i32 r_asi = tcg_const_i32(da.asi); - TCGv_i32 r_mop = tcg_const_i32(da.memop); + TCGv_i32 r_asi = tcg_constant_i32(da->asi); + TCGv_i32 r_mop = tcg_constant_i32(da->memop); TCGv_i64 tmp = tcg_temp_new_i64(); save_state(dc); - gen_helper_ld_asi(tmp, cpu_env, addr, r_asi, r_mop); - tcg_temp_free_i32(r_asi); - tcg_temp_free_i32(r_mop); + gen_helper_ld_asi(tmp, tcg_env, addr, r_asi, r_mop); /* See above. */ - if ((da.memop & MO_BSWAP) == MO_TE) { - tcg_gen_extr32_i64(lo, hi, tmp); + if ((da->memop & MO_BSWAP) == MO_TE) { + tcg_gen_extr_i64_tl(lo, hi, tmp); } else { - tcg_gen_extr32_i64(hi, lo, tmp); + tcg_gen_extr_i64_tl(hi, lo, tmp); } - tcg_temp_free_i64(tmp); } break; } @@ -2724,22 +1862,37 @@ static void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) gen_store_gpr(dc, rd + 1, lo); } -static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, - int insn, int rd) +static void gen_stda_asi(DisasContext *dc, DisasASI *da, TCGv addr, int rd) { - DisasASI da = get_asi(dc, insn, MO_TEUQ); + TCGv hi = gen_load_gpr(dc, rd); TCGv lo = gen_load_gpr(dc, rd + 1); - switch (da.type) { + switch (da->type) { case GET_ASI_EXCP: break; case GET_ASI_DTWINX: - gen_address_mask(dc, addr); - tcg_gen_qemu_st_i64(hi, addr, da.mem_idx, da.memop | MO_ALIGN_16); - tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_st_i64(lo, addr, da.mem_idx, da.memop); +#ifdef TARGET_SPARC64 + { + MemOp mop = (da->memop & MO_BSWAP) | MO_128 | MO_ALIGN_16; + TCGv_i128 t = tcg_temp_new_i128(); + + /* + * Note that LE twinx acts as if each 64-bit register result is + * byte swapped. We perform one 128-bit LE store, so must swap + * the order of the construction. + */ + if ((mop & MO_BSWAP) == MO_TE) { + tcg_gen_concat_i64_i128(t, lo, hi); + } else { + tcg_gen_concat_i64_i128(t, hi, lo); + } + tcg_gen_qemu_st_i128(t, addr, da->mem_idx, mop); + } break; +#else + g_assert_not_reached(); +#endif case GET_ASI_DIRECT: { @@ -2748,14 +1901,33 @@ static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, /* Note that LE stda acts as if each 32-bit register result is byte swapped. We will perform one 64-bit LE store, so now we must swap the order of the construction. */ - if ((da.memop & MO_BSWAP) == MO_TE) { - tcg_gen_concat32_i64(t64, lo, hi); + if ((da->memop & MO_BSWAP) == MO_TE) { + tcg_gen_concat_tl_i64(t64, lo, hi); } else { - tcg_gen_concat32_i64(t64, hi, lo); + tcg_gen_concat_tl_i64(t64, hi, lo); } - gen_address_mask(dc, addr); - tcg_gen_qemu_st_i64(t64, addr, da.mem_idx, da.memop); - tcg_temp_free_i64(t64); + tcg_gen_qemu_st_i64(t64, addr, da->mem_idx, da->memop | MO_ALIGN); + } + break; + + case GET_ASI_BFILL: + assert(TARGET_LONG_BITS == 32); + /* + * Store 32 bytes of [rd:rd+1] to ADDR. + * See comments for GET_ASI_COPY above. + */ + { + MemOp mop = MO_TE | MO_128 | MO_ATOM_IFALIGN_PAIR; + TCGv_i64 t8 = tcg_temp_new_i64(); + TCGv_i128 t16 = tcg_temp_new_i128(); + TCGv daddr = tcg_temp_new(); + + tcg_gen_concat_tl_i64(t8, lo, hi); + tcg_gen_concat_i64_i128(t16, t8, t8); + tcg_gen_andi_tl(daddr, addr, -32); + tcg_gen_qemu_st_i128(t16, daddr, da->mem_idx, mop); + tcg_gen_addi_tl(daddr, daddr, 16); + tcg_gen_qemu_st_i128(t16, daddr, da->mem_idx, mop); } break; @@ -2763,263 +1935,1563 @@ static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, /* ??? In theory we've handled all of the ASIs that are valid for stda, and this should raise DAE_invalid_asi. */ { - TCGv_i32 r_asi = tcg_const_i32(da.asi); - TCGv_i32 r_mop = tcg_const_i32(da.memop); + TCGv_i32 r_asi = tcg_constant_i32(da->asi); + TCGv_i32 r_mop = tcg_constant_i32(da->memop); TCGv_i64 t64 = tcg_temp_new_i64(); /* See above. */ - if ((da.memop & MO_BSWAP) == MO_TE) { - tcg_gen_concat32_i64(t64, lo, hi); + if ((da->memop & MO_BSWAP) == MO_TE) { + tcg_gen_concat_tl_i64(t64, lo, hi); } else { - tcg_gen_concat32_i64(t64, hi, lo); + tcg_gen_concat_tl_i64(t64, hi, lo); } save_state(dc); - gen_helper_st_asi(cpu_env, addr, t64, r_asi, r_mop); - tcg_temp_free_i32(r_mop); - tcg_temp_free_i32(r_asi); - tcg_temp_free_i64(t64); + gen_helper_st_asi(tcg_env, addr, t64, r_asi, r_mop); } break; } } -static void gen_casx_asi(DisasContext *dc, TCGv addr, TCGv cmpv, - int insn, int rd) -{ - DisasASI da = get_asi(dc, insn, MO_TEUQ); - TCGv oldv; - - switch (da.type) { - case GET_ASI_EXCP: - return; - case GET_ASI_DIRECT: - oldv = tcg_temp_new(); - tcg_gen_atomic_cmpxchg_tl(oldv, addr, cmpv, gen_load_gpr(dc, rd), - da.mem_idx, da.memop); - gen_store_gpr(dc, rd, oldv); - tcg_temp_free(oldv); - break; - default: - /* ??? Should be DAE_invalid_asi. */ - gen_exception(dc, TT_DATA_ACCESS); - break; - } -} - -#elif !defined(CONFIG_USER_ONLY) -static void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) -{ - /* ??? Work around an apparent bug in Ubuntu gcc 4.8.2-10ubuntu2+12, - whereby "rd + 1" elicits "error: array subscript is above array". - Since we have already asserted that rd is even, the semantics - are unchanged. */ - TCGv lo = gen_dest_gpr(dc, rd | 1); - TCGv hi = gen_dest_gpr(dc, rd); - TCGv_i64 t64 = tcg_temp_new_i64(); - DisasASI da = get_asi(dc, insn, MO_TEUQ); - - switch (da.type) { - case GET_ASI_EXCP: - tcg_temp_free_i64(t64); - return; - case GET_ASI_DIRECT: - gen_address_mask(dc, addr); - tcg_gen_qemu_ld_i64(t64, addr, da.mem_idx, da.memop); - break; - default: - { - TCGv_i32 r_asi = tcg_const_i32(da.asi); - TCGv_i32 r_mop = tcg_const_i32(MO_UQ); - - save_state(dc); - gen_helper_ld_asi(t64, cpu_env, addr, r_asi, r_mop); - tcg_temp_free_i32(r_mop); - tcg_temp_free_i32(r_asi); - } - break; - } - - tcg_gen_extr_i64_i32(lo, hi, t64); - tcg_temp_free_i64(t64); - gen_store_gpr(dc, rd | 1, lo); - gen_store_gpr(dc, rd, hi); -} - -static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, - int insn, int rd) -{ - DisasASI da = get_asi(dc, insn, MO_TEUQ); - TCGv lo = gen_load_gpr(dc, rd + 1); - TCGv_i64 t64 = tcg_temp_new_i64(); - - tcg_gen_concat_tl_i64(t64, lo, hi); - - switch (da.type) { - case GET_ASI_EXCP: - break; - case GET_ASI_DIRECT: - gen_address_mask(dc, addr); - tcg_gen_qemu_st_i64(t64, addr, da.mem_idx, da.memop); - break; - case GET_ASI_BFILL: - /* Store 32 bytes of T64 to ADDR. */ - /* ??? The original qemu code suggests 8-byte alignment, dropping - the low bits, but the only place I can see this used is in the - Linux kernel with 32 byte alignment, which would make more sense - as a cacheline-style operation. */ - { - TCGv d_addr = tcg_temp_new(); - TCGv eight = tcg_const_tl(8); - int i; - - tcg_gen_andi_tl(d_addr, addr, -8); - for (i = 0; i < 32; i += 8) { - tcg_gen_qemu_st_i64(t64, d_addr, da.mem_idx, da.memop); - tcg_gen_add_tl(d_addr, d_addr, eight); - } - - tcg_temp_free(d_addr); - tcg_temp_free(eight); - } - break; - default: - { - TCGv_i32 r_asi = tcg_const_i32(da.asi); - TCGv_i32 r_mop = tcg_const_i32(MO_UQ); - - save_state(dc); - gen_helper_st_asi(cpu_env, addr, t64, r_asi, r_mop); - tcg_temp_free_i32(r_mop); - tcg_temp_free_i32(r_asi); - } - break; - } - - tcg_temp_free_i64(t64); -} -#endif - -static TCGv get_src1(DisasContext *dc, unsigned int insn) -{ - unsigned int rs1 = GET_FIELD(insn, 13, 17); - return gen_load_gpr(dc, rs1); -} - -static TCGv get_src2(DisasContext *dc, unsigned int insn) -{ - if (IS_IMM) { /* immediate */ - target_long simm = GET_FIELDs(insn, 19, 31); - TCGv t = get_temp_tl(dc); - tcg_gen_movi_tl(t, simm); - return t; - } else { /* register */ - unsigned int rs2 = GET_FIELD(insn, 27, 31); - return gen_load_gpr(dc, rs2); - } -} - -#ifdef TARGET_SPARC64 static void gen_fmovs(DisasContext *dc, DisasCompare *cmp, int rd, int rs) { +#ifdef TARGET_SPARC64 TCGv_i32 c32, zero, dst, s1, s2; + TCGv_i64 c64 = tcg_temp_new_i64(); /* We have two choices here: extend the 32 bit data and use movcond_i64, or fold the comparison down to 32 bits and use movcond_i32. Choose the later. */ c32 = tcg_temp_new_i32(); - if (cmp->is_bool) { - tcg_gen_extrl_i64_i32(c32, cmp->c1); - } else { - TCGv_i64 c64 = tcg_temp_new_i64(); - tcg_gen_setcond_i64(cmp->cond, c64, cmp->c1, cmp->c2); - tcg_gen_extrl_i64_i32(c32, c64); - tcg_temp_free_i64(c64); - } + tcg_gen_setcondi_i64(cmp->cond, c64, cmp->c1, cmp->c2); + tcg_gen_extrl_i64_i32(c32, c64); s1 = gen_load_fpr_F(dc, rs); s2 = gen_load_fpr_F(dc, rd); - dst = gen_dest_fpr_F(dc); - zero = tcg_const_i32(0); + dst = tcg_temp_new_i32(); + zero = tcg_constant_i32(0); tcg_gen_movcond_i32(TCG_COND_NE, dst, c32, zero, s1, s2); - tcg_temp_free_i32(c32); - tcg_temp_free_i32(zero); gen_store_fpr_F(dc, rd, dst); +#else + qemu_build_not_reached(); +#endif } static void gen_fmovd(DisasContext *dc, DisasCompare *cmp, int rd, int rs) { +#ifdef TARGET_SPARC64 TCGv_i64 dst = gen_dest_fpr_D(dc, rd); - tcg_gen_movcond_i64(cmp->cond, dst, cmp->c1, cmp->c2, + tcg_gen_movcond_i64(cmp->cond, dst, cmp->c1, tcg_constant_tl(cmp->c2), gen_load_fpr_D(dc, rs), gen_load_fpr_D(dc, rd)); gen_store_fpr_D(dc, rd, dst); +#else + qemu_build_not_reached(); +#endif } static void gen_fmovq(DisasContext *dc, DisasCompare *cmp, int rd, int rs) { +#ifdef TARGET_SPARC64 int qd = QFPREG(rd); int qs = QFPREG(rs); + TCGv c2 = tcg_constant_tl(cmp->c2); - tcg_gen_movcond_i64(cmp->cond, cpu_fpr[qd / 2], cmp->c1, cmp->c2, + tcg_gen_movcond_i64(cmp->cond, cpu_fpr[qd / 2], cmp->c1, c2, cpu_fpr[qs / 2], cpu_fpr[qd / 2]); - tcg_gen_movcond_i64(cmp->cond, cpu_fpr[qd / 2 + 1], cmp->c1, cmp->c2, + tcg_gen_movcond_i64(cmp->cond, cpu_fpr[qd / 2 + 1], cmp->c1, c2, cpu_fpr[qs / 2 + 1], cpu_fpr[qd / 2 + 1]); gen_update_fprs_dirty(dc, qd); +#else + qemu_build_not_reached(); +#endif } -#ifndef CONFIG_USER_ONLY -static inline void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr, TCGv_env cpu_env) +#ifdef TARGET_SPARC64 +static void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr) { TCGv_i32 r_tl = tcg_temp_new_i32(); /* load env->tl into r_tl */ - tcg_gen_ld_i32(r_tl, cpu_env, offsetof(CPUSPARCState, tl)); + tcg_gen_ld_i32(r_tl, tcg_env, offsetof(CPUSPARCState, tl)); /* tl = [0 ... MAXTL_MASK] where MAXTL_MASK must be power of 2 */ tcg_gen_andi_i32(r_tl, r_tl, MAXTL_MASK); /* calculate offset to current trap state from env->ts, reuse r_tl */ tcg_gen_muli_i32(r_tl, r_tl, sizeof (trap_state)); - tcg_gen_addi_ptr(r_tsptr, cpu_env, offsetof(CPUSPARCState, ts)); + tcg_gen_addi_ptr(r_tsptr, tcg_env, offsetof(CPUSPARCState, ts)); /* tsptr = env->ts[env->tl & MAXTL_MASK] */ { TCGv_ptr r_tl_tmp = tcg_temp_new_ptr(); tcg_gen_ext_i32_ptr(r_tl_tmp, r_tl); tcg_gen_add_ptr(r_tsptr, r_tsptr, r_tl_tmp); - tcg_temp_free_ptr(r_tl_tmp); } - - tcg_temp_free_i32(r_tl); } #endif -static void gen_edge(DisasContext *dc, TCGv dst, TCGv s1, TCGv s2, +static int extract_dfpreg(DisasContext *dc, int x) +{ + return DFPREG(x); +} + +static int extract_qfpreg(DisasContext *dc, int x) +{ + return QFPREG(x); +} + +/* Include the auto-generated decoder. */ +#include "decode-insns.c.inc" + +#define TRANS(NAME, AVAIL, FUNC, ...) \ + static bool trans_##NAME(DisasContext *dc, arg_##NAME *a) \ + { return avail_##AVAIL(dc) && FUNC(dc, __VA_ARGS__); } + +#define avail_ALL(C) true +#ifdef TARGET_SPARC64 +# define avail_32(C) false +# define avail_ASR17(C) false +# define avail_CASA(C) true +# define avail_DIV(C) true +# define avail_MUL(C) true +# define avail_POWERDOWN(C) false +# define avail_64(C) true +# define avail_GL(C) ((C)->def->features & CPU_FEATURE_GL) +# define avail_HYPV(C) ((C)->def->features & CPU_FEATURE_HYPV) +# define avail_VIS1(C) ((C)->def->features & CPU_FEATURE_VIS1) +# define avail_VIS2(C) ((C)->def->features & CPU_FEATURE_VIS2) +#else +# define avail_32(C) true +# define avail_ASR17(C) ((C)->def->features & CPU_FEATURE_ASR17) +# define avail_CASA(C) ((C)->def->features & CPU_FEATURE_CASA) +# define avail_DIV(C) ((C)->def->features & CPU_FEATURE_DIV) +# define avail_MUL(C) ((C)->def->features & CPU_FEATURE_MUL) +# define avail_POWERDOWN(C) ((C)->def->features & CPU_FEATURE_POWERDOWN) +# define avail_64(C) false +# define avail_GL(C) false +# define avail_HYPV(C) false +# define avail_VIS1(C) false +# define avail_VIS2(C) false +#endif + +/* Default case for non jump instructions. */ +static bool advance_pc(DisasContext *dc) +{ + TCGLabel *l1; + + finishing_insn(dc); + + if (dc->npc & 3) { + switch (dc->npc) { + case DYNAMIC_PC: + case DYNAMIC_PC_LOOKUP: + dc->pc = dc->npc; + tcg_gen_mov_tl(cpu_pc, cpu_npc); + tcg_gen_addi_tl(cpu_npc, cpu_npc, 4); + break; + + case JUMP_PC: + /* we can do a static jump */ + l1 = gen_new_label(); + tcg_gen_brcondi_tl(dc->jump.cond, dc->jump.c1, dc->jump.c2, l1); + + /* jump not taken */ + gen_goto_tb(dc, 1, dc->jump_pc[1], dc->jump_pc[1] + 4); + + /* jump taken */ + gen_set_label(l1); + gen_goto_tb(dc, 0, dc->jump_pc[0], dc->jump_pc[0] + 4); + + dc->base.is_jmp = DISAS_NORETURN; + break; + + default: + g_assert_not_reached(); + } + } else { + dc->pc = dc->npc; + dc->npc = dc->npc + 4; + } + return true; +} + +/* + * Major opcodes 00 and 01 -- branches, call, and sethi + */ + +static bool advance_jump_cond(DisasContext *dc, DisasCompare *cmp, + bool annul, int disp) +{ + target_ulong dest = address_mask_i(dc, dc->pc + disp * 4); + target_ulong npc; + + finishing_insn(dc); + + if (cmp->cond == TCG_COND_ALWAYS) { + if (annul) { + dc->pc = dest; + dc->npc = dest + 4; + } else { + gen_mov_pc_npc(dc); + dc->npc = dest; + } + return true; + } + + if (cmp->cond == TCG_COND_NEVER) { + npc = dc->npc; + if (npc & 3) { + gen_mov_pc_npc(dc); + if (annul) { + tcg_gen_addi_tl(cpu_pc, cpu_pc, 4); + } + tcg_gen_addi_tl(cpu_npc, cpu_pc, 4); + } else { + dc->pc = npc + (annul ? 4 : 0); + dc->npc = dc->pc + 4; + } + return true; + } + + flush_cond(dc); + npc = dc->npc; + + if (annul) { + TCGLabel *l1 = gen_new_label(); + + tcg_gen_brcondi_tl(tcg_invert_cond(cmp->cond), cmp->c1, cmp->c2, l1); + gen_goto_tb(dc, 0, npc, dest); + gen_set_label(l1); + gen_goto_tb(dc, 1, npc + 4, npc + 8); + + dc->base.is_jmp = DISAS_NORETURN; + } else { + if (npc & 3) { + switch (npc) { + case DYNAMIC_PC: + case DYNAMIC_PC_LOOKUP: + tcg_gen_mov_tl(cpu_pc, cpu_npc); + tcg_gen_addi_tl(cpu_npc, cpu_npc, 4); + tcg_gen_movcond_tl(cmp->cond, cpu_npc, + cmp->c1, tcg_constant_tl(cmp->c2), + tcg_constant_tl(dest), cpu_npc); + dc->pc = npc; + break; + default: + g_assert_not_reached(); + } + } else { + dc->pc = npc; + dc->npc = JUMP_PC; + dc->jump = *cmp; + dc->jump_pc[0] = dest; + dc->jump_pc[1] = npc + 4; + + /* The condition for cpu_cond is always NE -- normalize. */ + if (cmp->cond == TCG_COND_NE) { + tcg_gen_xori_tl(cpu_cond, cmp->c1, cmp->c2); + } else { + tcg_gen_setcondi_tl(cmp->cond, cpu_cond, cmp->c1, cmp->c2); + } + dc->cpu_cond_live = true; + } + } + return true; +} + +static bool raise_priv(DisasContext *dc) +{ + gen_exception(dc, TT_PRIV_INSN); + return true; +} + +static bool raise_unimpfpop(DisasContext *dc) +{ + gen_op_fpexception_im(dc, FSR_FTT_UNIMPFPOP); + return true; +} + +static bool gen_trap_float128(DisasContext *dc) +{ + if (dc->def->features & CPU_FEATURE_FLOAT128) { + return false; + } + return raise_unimpfpop(dc); +} + +static bool do_bpcc(DisasContext *dc, arg_bcc *a) +{ + DisasCompare cmp; + + gen_compare(&cmp, a->cc, a->cond, dc); + return advance_jump_cond(dc, &cmp, a->a, a->i); +} + +TRANS(Bicc, ALL, do_bpcc, a) +TRANS(BPcc, 64, do_bpcc, a) + +static bool do_fbpfcc(DisasContext *dc, arg_bcc *a) +{ + DisasCompare cmp; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + gen_fcompare(&cmp, a->cc, a->cond); + return advance_jump_cond(dc, &cmp, a->a, a->i); +} + +TRANS(FBPfcc, 64, do_fbpfcc, a) +TRANS(FBfcc, ALL, do_fbpfcc, a) + +static bool trans_BPr(DisasContext *dc, arg_BPr *a) +{ + DisasCompare cmp; + + if (!avail_64(dc)) { + return false; + } + if (!gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1))) { + return false; + } + return advance_jump_cond(dc, &cmp, a->a, a->i); +} + +static bool trans_CALL(DisasContext *dc, arg_CALL *a) +{ + target_long target = address_mask_i(dc, dc->pc + a->i * 4); + + gen_store_gpr(dc, 15, tcg_constant_tl(dc->pc)); + gen_mov_pc_npc(dc); + dc->npc = target; + return true; +} + +static bool trans_NCP(DisasContext *dc, arg_NCP *a) +{ + /* + * For sparc32, always generate the no-coprocessor exception. + * For sparc64, always generate illegal instruction. + */ +#ifdef TARGET_SPARC64 + return false; +#else + gen_exception(dc, TT_NCP_INSN); + return true; +#endif +} + +static bool trans_SETHI(DisasContext *dc, arg_SETHI *a) +{ + /* Special-case %g0 because that's the canonical nop. */ + if (a->rd) { + gen_store_gpr(dc, a->rd, tcg_constant_tl((uint32_t)a->i << 10)); + } + return advance_pc(dc); +} + +/* + * Major Opcode 10 -- integer, floating-point, vis, and system insns. + */ + +static bool do_tcc(DisasContext *dc, int cond, int cc, + int rs1, bool imm, int rs2_or_imm) +{ + int mask = ((dc->def->features & CPU_FEATURE_HYPV) && supervisor(dc) + ? UA2005_HTRAP_MASK : V8_TRAP_MASK); + DisasCompare cmp; + TCGLabel *lab; + TCGv_i32 trap; + + /* Trap never. */ + if (cond == 0) { + return advance_pc(dc); + } + + /* + * Immediate traps are the most common case. Since this value is + * live across the branch, it really pays to evaluate the constant. + */ + if (rs1 == 0 && (imm || rs2_or_imm == 0)) { + trap = tcg_constant_i32((rs2_or_imm & mask) + TT_TRAP); + } else { + trap = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(trap, gen_load_gpr(dc, rs1)); + if (imm) { + tcg_gen_addi_i32(trap, trap, rs2_or_imm); + } else { + TCGv_i32 t2 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t2, gen_load_gpr(dc, rs2_or_imm)); + tcg_gen_add_i32(trap, trap, t2); + } + tcg_gen_andi_i32(trap, trap, mask); + tcg_gen_addi_i32(trap, trap, TT_TRAP); + } + + finishing_insn(dc); + + /* Trap always. */ + if (cond == 8) { + save_state(dc); + gen_helper_raise_exception(tcg_env, trap); + dc->base.is_jmp = DISAS_NORETURN; + return true; + } + + /* Conditional trap. */ + flush_cond(dc); + lab = delay_exceptionv(dc, trap); + gen_compare(&cmp, cc, cond, dc); + tcg_gen_brcondi_tl(cmp.cond, cmp.c1, cmp.c2, lab); + + return advance_pc(dc); +} + +static bool trans_Tcc_r(DisasContext *dc, arg_Tcc_r *a) +{ + if (avail_32(dc) && a->cc) { + return false; + } + return do_tcc(dc, a->cond, a->cc, a->rs1, false, a->rs2); +} + +static bool trans_Tcc_i_v7(DisasContext *dc, arg_Tcc_i_v7 *a) +{ + if (avail_64(dc)) { + return false; + } + return do_tcc(dc, a->cond, 0, a->rs1, true, a->i); +} + +static bool trans_Tcc_i_v9(DisasContext *dc, arg_Tcc_i_v9 *a) +{ + if (avail_32(dc)) { + return false; + } + return do_tcc(dc, a->cond, a->cc, a->rs1, true, a->i); +} + +static bool trans_STBAR(DisasContext *dc, arg_STBAR *a) +{ + tcg_gen_mb(TCG_MO_ST_ST | TCG_BAR_SC); + return advance_pc(dc); +} + +static bool trans_MEMBAR(DisasContext *dc, arg_MEMBAR *a) +{ + if (avail_32(dc)) { + return false; + } + if (a->mmask) { + /* Note TCG_MO_* was modeled on sparc64, so mmask matches. */ + tcg_gen_mb(a->mmask | TCG_BAR_SC); + } + if (a->cmask) { + /* For #Sync, etc, end the TB to recognize interrupts. */ + dc->base.is_jmp = DISAS_EXIT; + } + return advance_pc(dc); +} + +static bool do_rd_special(DisasContext *dc, bool priv, int rd, + TCGv (*func)(DisasContext *, TCGv)) +{ + if (!priv) { + return raise_priv(dc); + } + gen_store_gpr(dc, rd, func(dc, gen_dest_gpr(dc, rd))); + return advance_pc(dc); +} + +static TCGv do_rdy(DisasContext *dc, TCGv dst) +{ + return cpu_y; +} + +static bool trans_RDY(DisasContext *dc, arg_RDY *a) +{ + /* + * TODO: Need a feature bit for sparcv8. In the meantime, treat all + * 32-bit cpus like sparcv7, which ignores the rs1 field. + * This matches after all other ASR, so Leon3 Asr17 is handled first. + */ + if (avail_64(dc) && a->rs1 != 0) { + return false; + } + return do_rd_special(dc, true, a->rd, do_rdy); +} + +static TCGv do_rd_leon3_config(DisasContext *dc, TCGv dst) +{ + gen_helper_rdasr17(dst, tcg_env); + return dst; +} + +TRANS(RDASR17, ASR17, do_rd_special, true, a->rd, do_rd_leon3_config) + +static TCGv do_rdccr(DisasContext *dc, TCGv dst) +{ + gen_helper_rdccr(dst, tcg_env); + return dst; +} + +TRANS(RDCCR, 64, do_rd_special, true, a->rd, do_rdccr) + +static TCGv do_rdasi(DisasContext *dc, TCGv dst) +{ +#ifdef TARGET_SPARC64 + return tcg_constant_tl(dc->asi); +#else + qemu_build_not_reached(); +#endif +} + +TRANS(RDASI, 64, do_rd_special, true, a->rd, do_rdasi) + +static TCGv do_rdtick(DisasContext *dc, TCGv dst) +{ + TCGv_ptr r_tickptr = tcg_temp_new_ptr(); + + tcg_gen_ld_ptr(r_tickptr, tcg_env, env64_field_offsetof(tick)); + if (translator_io_start(&dc->base)) { + dc->base.is_jmp = DISAS_EXIT; + } + gen_helper_tick_get_count(dst, tcg_env, r_tickptr, + tcg_constant_i32(dc->mem_idx)); + return dst; +} + +/* TODO: non-priv access only allowed when enabled. */ +TRANS(RDTICK, 64, do_rd_special, true, a->rd, do_rdtick) + +static TCGv do_rdpc(DisasContext *dc, TCGv dst) +{ + return tcg_constant_tl(address_mask_i(dc, dc->pc)); +} + +TRANS(RDPC, 64, do_rd_special, true, a->rd, do_rdpc) + +static TCGv do_rdfprs(DisasContext *dc, TCGv dst) +{ + tcg_gen_ext_i32_tl(dst, cpu_fprs); + return dst; +} + +TRANS(RDFPRS, 64, do_rd_special, true, a->rd, do_rdfprs) + +static TCGv do_rdgsr(DisasContext *dc, TCGv dst) +{ + gen_trap_ifnofpu(dc); + return cpu_gsr; +} + +TRANS(RDGSR, 64, do_rd_special, true, a->rd, do_rdgsr) + +static TCGv do_rdsoftint(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(softint)); + return dst; +} + +TRANS(RDSOFTINT, 64, do_rd_special, supervisor(dc), a->rd, do_rdsoftint) + +static TCGv do_rdtick_cmpr(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(tick_cmpr)); + return dst; +} + +/* TODO: non-priv access only allowed when enabled. */ +TRANS(RDTICK_CMPR, 64, do_rd_special, true, a->rd, do_rdtick_cmpr) + +static TCGv do_rdstick(DisasContext *dc, TCGv dst) +{ + TCGv_ptr r_tickptr = tcg_temp_new_ptr(); + + tcg_gen_ld_ptr(r_tickptr, tcg_env, env64_field_offsetof(stick)); + if (translator_io_start(&dc->base)) { + dc->base.is_jmp = DISAS_EXIT; + } + gen_helper_tick_get_count(dst, tcg_env, r_tickptr, + tcg_constant_i32(dc->mem_idx)); + return dst; +} + +/* TODO: non-priv access only allowed when enabled. */ +TRANS(RDSTICK, 64, do_rd_special, true, a->rd, do_rdstick) + +static TCGv do_rdstick_cmpr(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(stick_cmpr)); + return dst; +} + +/* TODO: supervisor access only allowed when enabled by hypervisor. */ +TRANS(RDSTICK_CMPR, 64, do_rd_special, supervisor(dc), a->rd, do_rdstick_cmpr) + +/* + * UltraSPARC-T1 Strand status. + * HYPV check maybe not enough, UA2005 & UA2007 describe + * this ASR as impl. dep + */ +static TCGv do_rdstrand_status(DisasContext *dc, TCGv dst) +{ + return tcg_constant_tl(1); +} + +TRANS(RDSTRAND_STATUS, HYPV, do_rd_special, true, a->rd, do_rdstrand_status) + +static TCGv do_rdpsr(DisasContext *dc, TCGv dst) +{ + gen_helper_rdpsr(dst, tcg_env); + return dst; +} + +TRANS(RDPSR, 32, do_rd_special, supervisor(dc), a->rd, do_rdpsr) + +static TCGv do_rdhpstate(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(hpstate)); + return dst; +} + +TRANS(RDHPR_hpstate, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhpstate) + +static TCGv do_rdhtstate(DisasContext *dc, TCGv dst) +{ + TCGv_i32 tl = tcg_temp_new_i32(); + TCGv_ptr tp = tcg_temp_new_ptr(); + + tcg_gen_ld_i32(tl, tcg_env, env64_field_offsetof(tl)); + tcg_gen_andi_i32(tl, tl, MAXTL_MASK); + tcg_gen_shli_i32(tl, tl, 3); + tcg_gen_ext_i32_ptr(tp, tl); + tcg_gen_add_ptr(tp, tp, tcg_env); + + tcg_gen_ld_tl(dst, tp, env64_field_offsetof(htstate)); + return dst; +} + +TRANS(RDHPR_htstate, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhtstate) + +static TCGv do_rdhintp(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(hintp)); + return dst; +} + +TRANS(RDHPR_hintp, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhintp) + +static TCGv do_rdhtba(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(htba)); + return dst; +} + +TRANS(RDHPR_htba, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhtba) + +static TCGv do_rdhver(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(hver)); + return dst; +} + +TRANS(RDHPR_hver, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdhver) + +static TCGv do_rdhstick_cmpr(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(hstick_cmpr)); + return dst; +} + +TRANS(RDHPR_hstick_cmpr, HYPV, do_rd_special, hypervisor(dc), a->rd, + do_rdhstick_cmpr) + +static TCGv do_rdwim(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld_tl(dst, tcg_env, env32_field_offsetof(wim)); + return dst; +} + +TRANS(RDWIM, 32, do_rd_special, supervisor(dc), a->rd, do_rdwim) + +static TCGv do_rdtpc(DisasContext *dc, TCGv dst) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_ld_tl(dst, r_tsptr, offsetof(trap_state, tpc)); + return dst; +#else + qemu_build_not_reached(); +#endif +} + +TRANS(RDPR_tpc, 64, do_rd_special, supervisor(dc), a->rd, do_rdtpc) + +static TCGv do_rdtnpc(DisasContext *dc, TCGv dst) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_ld_tl(dst, r_tsptr, offsetof(trap_state, tnpc)); + return dst; +#else + qemu_build_not_reached(); +#endif +} + +TRANS(RDPR_tnpc, 64, do_rd_special, supervisor(dc), a->rd, do_rdtnpc) + +static TCGv do_rdtstate(DisasContext *dc, TCGv dst) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_ld_tl(dst, r_tsptr, offsetof(trap_state, tstate)); + return dst; +#else + qemu_build_not_reached(); +#endif +} + +TRANS(RDPR_tstate, 64, do_rd_special, supervisor(dc), a->rd, do_rdtstate) + +static TCGv do_rdtt(DisasContext *dc, TCGv dst) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_ld32s_tl(dst, r_tsptr, offsetof(trap_state, tt)); + return dst; +#else + qemu_build_not_reached(); +#endif +} + +TRANS(RDPR_tt, 64, do_rd_special, supervisor(dc), a->rd, do_rdtt) +TRANS(RDPR_tick, 64, do_rd_special, supervisor(dc), a->rd, do_rdtick) + +static TCGv do_rdtba(DisasContext *dc, TCGv dst) +{ + return cpu_tbr; +} + +TRANS(RDTBR, 32, do_rd_special, supervisor(dc), a->rd, do_rdtba) +TRANS(RDPR_tba, 64, do_rd_special, supervisor(dc), a->rd, do_rdtba) + +static TCGv do_rdpstate(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(pstate)); + return dst; +} + +TRANS(RDPR_pstate, 64, do_rd_special, supervisor(dc), a->rd, do_rdpstate) + +static TCGv do_rdtl(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(tl)); + return dst; +} + +TRANS(RDPR_tl, 64, do_rd_special, supervisor(dc), a->rd, do_rdtl) + +static TCGv do_rdpil(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env_field_offsetof(psrpil)); + return dst; +} + +TRANS(RDPR_pil, 64, do_rd_special, supervisor(dc), a->rd, do_rdpil) + +static TCGv do_rdcwp(DisasContext *dc, TCGv dst) +{ + gen_helper_rdcwp(dst, tcg_env); + return dst; +} + +TRANS(RDPR_cwp, 64, do_rd_special, supervisor(dc), a->rd, do_rdcwp) + +static TCGv do_rdcansave(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(cansave)); + return dst; +} + +TRANS(RDPR_cansave, 64, do_rd_special, supervisor(dc), a->rd, do_rdcansave) + +static TCGv do_rdcanrestore(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(canrestore)); + return dst; +} + +TRANS(RDPR_canrestore, 64, do_rd_special, supervisor(dc), a->rd, + do_rdcanrestore) + +static TCGv do_rdcleanwin(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(cleanwin)); + return dst; +} + +TRANS(RDPR_cleanwin, 64, do_rd_special, supervisor(dc), a->rd, do_rdcleanwin) + +static TCGv do_rdotherwin(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(otherwin)); + return dst; +} + +TRANS(RDPR_otherwin, 64, do_rd_special, supervisor(dc), a->rd, do_rdotherwin) + +static TCGv do_rdwstate(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(wstate)); + return dst; +} + +TRANS(RDPR_wstate, 64, do_rd_special, supervisor(dc), a->rd, do_rdwstate) + +static TCGv do_rdgl(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld32s_tl(dst, tcg_env, env64_field_offsetof(gl)); + return dst; +} + +TRANS(RDPR_gl, GL, do_rd_special, supervisor(dc), a->rd, do_rdgl) + +/* UA2005 strand status */ +static TCGv do_rdssr(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(ssr)); + return dst; +} + +TRANS(RDPR_strand_status, HYPV, do_rd_special, hypervisor(dc), a->rd, do_rdssr) + +static TCGv do_rdver(DisasContext *dc, TCGv dst) +{ + tcg_gen_ld_tl(dst, tcg_env, env64_field_offsetof(version)); + return dst; +} + +TRANS(RDPR_ver, 64, do_rd_special, supervisor(dc), a->rd, do_rdver) + +static bool trans_FLUSHW(DisasContext *dc, arg_FLUSHW *a) +{ + if (avail_64(dc)) { + gen_helper_flushw(tcg_env); + return advance_pc(dc); + } + return false; +} + +static bool do_wr_special(DisasContext *dc, arg_r_r_ri *a, bool priv, + void (*func)(DisasContext *, TCGv)) +{ + TCGv src; + + /* For simplicity, we under-decoded the rs2 form. */ + if (!a->imm && (a->rs2_or_imm & ~0x1f)) { + return false; + } + if (!priv) { + return raise_priv(dc); + } + + if (a->rs1 == 0 && (a->imm || a->rs2_or_imm == 0)) { + src = tcg_constant_tl(a->rs2_or_imm); + } else { + TCGv src1 = gen_load_gpr(dc, a->rs1); + if (a->rs2_or_imm == 0) { + src = src1; + } else { + src = tcg_temp_new(); + if (a->imm) { + tcg_gen_xori_tl(src, src1, a->rs2_or_imm); + } else { + tcg_gen_xor_tl(src, src1, gen_load_gpr(dc, a->rs2_or_imm)); + } + } + } + func(dc, src); + return advance_pc(dc); +} + +static void do_wry(DisasContext *dc, TCGv src) +{ + tcg_gen_ext32u_tl(cpu_y, src); +} + +TRANS(WRY, ALL, do_wr_special, a, true, do_wry) + +static void do_wrccr(DisasContext *dc, TCGv src) +{ + gen_helper_wrccr(tcg_env, src); +} + +TRANS(WRCCR, 64, do_wr_special, a, true, do_wrccr) + +static void do_wrasi(DisasContext *dc, TCGv src) +{ + TCGv tmp = tcg_temp_new(); + + tcg_gen_ext8u_tl(tmp, src); + tcg_gen_st32_tl(tmp, tcg_env, env64_field_offsetof(asi)); + /* End TB to notice changed ASI. */ + dc->base.is_jmp = DISAS_EXIT; +} + +TRANS(WRASI, 64, do_wr_special, a, true, do_wrasi) + +static void do_wrfprs(DisasContext *dc, TCGv src) +{ +#ifdef TARGET_SPARC64 + tcg_gen_trunc_tl_i32(cpu_fprs, src); + dc->fprs_dirty = 0; + dc->base.is_jmp = DISAS_EXIT; +#else + qemu_build_not_reached(); +#endif +} + +TRANS(WRFPRS, 64, do_wr_special, a, true, do_wrfprs) + +static void do_wrgsr(DisasContext *dc, TCGv src) +{ + gen_trap_ifnofpu(dc); + tcg_gen_mov_tl(cpu_gsr, src); +} + +TRANS(WRGSR, 64, do_wr_special, a, true, do_wrgsr) + +static void do_wrsoftint_set(DisasContext *dc, TCGv src) +{ + gen_helper_set_softint(tcg_env, src); +} + +TRANS(WRSOFTINT_SET, 64, do_wr_special, a, supervisor(dc), do_wrsoftint_set) + +static void do_wrsoftint_clr(DisasContext *dc, TCGv src) +{ + gen_helper_clear_softint(tcg_env, src); +} + +TRANS(WRSOFTINT_CLR, 64, do_wr_special, a, supervisor(dc), do_wrsoftint_clr) + +static void do_wrsoftint(DisasContext *dc, TCGv src) +{ + gen_helper_write_softint(tcg_env, src); +} + +TRANS(WRSOFTINT, 64, do_wr_special, a, supervisor(dc), do_wrsoftint) + +static void do_wrtick_cmpr(DisasContext *dc, TCGv src) +{ + TCGv_ptr r_tickptr = tcg_temp_new_ptr(); + + tcg_gen_st_tl(src, tcg_env, env64_field_offsetof(tick_cmpr)); + tcg_gen_ld_ptr(r_tickptr, tcg_env, env64_field_offsetof(tick)); + translator_io_start(&dc->base); + gen_helper_tick_set_limit(r_tickptr, src); + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; +} + +TRANS(WRTICK_CMPR, 64, do_wr_special, a, supervisor(dc), do_wrtick_cmpr) + +static void do_wrstick(DisasContext *dc, TCGv src) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tickptr = tcg_temp_new_ptr(); + + tcg_gen_ld_ptr(r_tickptr, tcg_env, offsetof(CPUSPARCState, stick)); + translator_io_start(&dc->base); + gen_helper_tick_set_count(r_tickptr, src); + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; +#else + qemu_build_not_reached(); +#endif +} + +TRANS(WRSTICK, 64, do_wr_special, a, supervisor(dc), do_wrstick) + +static void do_wrstick_cmpr(DisasContext *dc, TCGv src) +{ + TCGv_ptr r_tickptr = tcg_temp_new_ptr(); + + tcg_gen_st_tl(src, tcg_env, env64_field_offsetof(stick_cmpr)); + tcg_gen_ld_ptr(r_tickptr, tcg_env, env64_field_offsetof(stick)); + translator_io_start(&dc->base); + gen_helper_tick_set_limit(r_tickptr, src); + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; +} + +TRANS(WRSTICK_CMPR, 64, do_wr_special, a, supervisor(dc), do_wrstick_cmpr) + +static void do_wrpowerdown(DisasContext *dc, TCGv src) +{ + finishing_insn(dc); + save_state(dc); + gen_helper_power_down(tcg_env); +} + +TRANS(WRPOWERDOWN, POWERDOWN, do_wr_special, a, supervisor(dc), do_wrpowerdown) + +static void do_wrpsr(DisasContext *dc, TCGv src) +{ + gen_helper_wrpsr(tcg_env, src); + dc->base.is_jmp = DISAS_EXIT; +} + +TRANS(WRPSR, 32, do_wr_special, a, supervisor(dc), do_wrpsr) + +static void do_wrwim(DisasContext *dc, TCGv src) +{ + target_ulong mask = MAKE_64BIT_MASK(0, dc->def->nwindows); + TCGv tmp = tcg_temp_new(); + + tcg_gen_andi_tl(tmp, src, mask); + tcg_gen_st_tl(tmp, tcg_env, env32_field_offsetof(wim)); +} + +TRANS(WRWIM, 32, do_wr_special, a, supervisor(dc), do_wrwim) + +static void do_wrtpc(DisasContext *dc, TCGv src) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_st_tl(src, r_tsptr, offsetof(trap_state, tpc)); +#else + qemu_build_not_reached(); +#endif +} + +TRANS(WRPR_tpc, 64, do_wr_special, a, supervisor(dc), do_wrtpc) + +static void do_wrtnpc(DisasContext *dc, TCGv src) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_st_tl(src, r_tsptr, offsetof(trap_state, tnpc)); +#else + qemu_build_not_reached(); +#endif +} + +TRANS(WRPR_tnpc, 64, do_wr_special, a, supervisor(dc), do_wrtnpc) + +static void do_wrtstate(DisasContext *dc, TCGv src) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_st_tl(src, r_tsptr, offsetof(trap_state, tstate)); +#else + qemu_build_not_reached(); +#endif +} + +TRANS(WRPR_tstate, 64, do_wr_special, a, supervisor(dc), do_wrtstate) + +static void do_wrtt(DisasContext *dc, TCGv src) +{ +#ifdef TARGET_SPARC64 + TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + + gen_load_trap_state_at_tl(r_tsptr); + tcg_gen_st32_tl(src, r_tsptr, offsetof(trap_state, tt)); +#else + qemu_build_not_reached(); +#endif +} + +TRANS(WRPR_tt, 64, do_wr_special, a, supervisor(dc), do_wrtt) + +static void do_wrtick(DisasContext *dc, TCGv src) +{ + TCGv_ptr r_tickptr = tcg_temp_new_ptr(); + + tcg_gen_ld_ptr(r_tickptr, tcg_env, env64_field_offsetof(tick)); + translator_io_start(&dc->base); + gen_helper_tick_set_count(r_tickptr, src); + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; +} + +TRANS(WRPR_tick, 64, do_wr_special, a, supervisor(dc), do_wrtick) + +static void do_wrtba(DisasContext *dc, TCGv src) +{ + tcg_gen_mov_tl(cpu_tbr, src); +} + +TRANS(WRPR_tba, 64, do_wr_special, a, supervisor(dc), do_wrtba) + +static void do_wrpstate(DisasContext *dc, TCGv src) +{ + save_state(dc); + if (translator_io_start(&dc->base)) { + dc->base.is_jmp = DISAS_EXIT; + } + gen_helper_wrpstate(tcg_env, src); + dc->npc = DYNAMIC_PC; +} + +TRANS(WRPR_pstate, 64, do_wr_special, a, supervisor(dc), do_wrpstate) + +static void do_wrtl(DisasContext *dc, TCGv src) +{ + save_state(dc); + tcg_gen_st32_tl(src, tcg_env, env64_field_offsetof(tl)); + dc->npc = DYNAMIC_PC; +} + +TRANS(WRPR_tl, 64, do_wr_special, a, supervisor(dc), do_wrtl) + +static void do_wrpil(DisasContext *dc, TCGv src) +{ + if (translator_io_start(&dc->base)) { + dc->base.is_jmp = DISAS_EXIT; + } + gen_helper_wrpil(tcg_env, src); +} + +TRANS(WRPR_pil, 64, do_wr_special, a, supervisor(dc), do_wrpil) + +static void do_wrcwp(DisasContext *dc, TCGv src) +{ + gen_helper_wrcwp(tcg_env, src); +} + +TRANS(WRPR_cwp, 64, do_wr_special, a, supervisor(dc), do_wrcwp) + +static void do_wrcansave(DisasContext *dc, TCGv src) +{ + tcg_gen_st32_tl(src, tcg_env, env64_field_offsetof(cansave)); +} + +TRANS(WRPR_cansave, 64, do_wr_special, a, supervisor(dc), do_wrcansave) + +static void do_wrcanrestore(DisasContext *dc, TCGv src) +{ + tcg_gen_st32_tl(src, tcg_env, env64_field_offsetof(canrestore)); +} + +TRANS(WRPR_canrestore, 64, do_wr_special, a, supervisor(dc), do_wrcanrestore) + +static void do_wrcleanwin(DisasContext *dc, TCGv src) +{ + tcg_gen_st32_tl(src, tcg_env, env64_field_offsetof(cleanwin)); +} + +TRANS(WRPR_cleanwin, 64, do_wr_special, a, supervisor(dc), do_wrcleanwin) + +static void do_wrotherwin(DisasContext *dc, TCGv src) +{ + tcg_gen_st32_tl(src, tcg_env, env64_field_offsetof(otherwin)); +} + +TRANS(WRPR_otherwin, 64, do_wr_special, a, supervisor(dc), do_wrotherwin) + +static void do_wrwstate(DisasContext *dc, TCGv src) +{ + tcg_gen_st32_tl(src, tcg_env, env64_field_offsetof(wstate)); +} + +TRANS(WRPR_wstate, 64, do_wr_special, a, supervisor(dc), do_wrwstate) + +static void do_wrgl(DisasContext *dc, TCGv src) +{ + gen_helper_wrgl(tcg_env, src); +} + +TRANS(WRPR_gl, GL, do_wr_special, a, supervisor(dc), do_wrgl) + +/* UA2005 strand status */ +static void do_wrssr(DisasContext *dc, TCGv src) +{ + tcg_gen_st_tl(src, tcg_env, env64_field_offsetof(ssr)); +} + +TRANS(WRPR_strand_status, HYPV, do_wr_special, a, hypervisor(dc), do_wrssr) + +TRANS(WRTBR, 32, do_wr_special, a, supervisor(dc), do_wrtba) + +static void do_wrhpstate(DisasContext *dc, TCGv src) +{ + tcg_gen_st_tl(src, tcg_env, env64_field_offsetof(hpstate)); + dc->base.is_jmp = DISAS_EXIT; +} + +TRANS(WRHPR_hpstate, HYPV, do_wr_special, a, hypervisor(dc), do_wrhpstate) + +static void do_wrhtstate(DisasContext *dc, TCGv src) +{ + TCGv_i32 tl = tcg_temp_new_i32(); + TCGv_ptr tp = tcg_temp_new_ptr(); + + tcg_gen_ld_i32(tl, tcg_env, env64_field_offsetof(tl)); + tcg_gen_andi_i32(tl, tl, MAXTL_MASK); + tcg_gen_shli_i32(tl, tl, 3); + tcg_gen_ext_i32_ptr(tp, tl); + tcg_gen_add_ptr(tp, tp, tcg_env); + + tcg_gen_st_tl(src, tp, env64_field_offsetof(htstate)); +} + +TRANS(WRHPR_htstate, HYPV, do_wr_special, a, hypervisor(dc), do_wrhtstate) + +static void do_wrhintp(DisasContext *dc, TCGv src) +{ + tcg_gen_st_tl(src, tcg_env, env64_field_offsetof(hintp)); +} + +TRANS(WRHPR_hintp, HYPV, do_wr_special, a, hypervisor(dc), do_wrhintp) + +static void do_wrhtba(DisasContext *dc, TCGv src) +{ + tcg_gen_st_tl(src, tcg_env, env64_field_offsetof(htba)); +} + +TRANS(WRHPR_htba, HYPV, do_wr_special, a, hypervisor(dc), do_wrhtba) + +static void do_wrhstick_cmpr(DisasContext *dc, TCGv src) +{ + TCGv_ptr r_tickptr = tcg_temp_new_ptr(); + + tcg_gen_st_tl(src, tcg_env, env64_field_offsetof(hstick_cmpr)); + tcg_gen_ld_ptr(r_tickptr, tcg_env, env64_field_offsetof(hstick)); + translator_io_start(&dc->base); + gen_helper_tick_set_limit(r_tickptr, src); + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; +} + +TRANS(WRHPR_hstick_cmpr, HYPV, do_wr_special, a, hypervisor(dc), + do_wrhstick_cmpr) + +static bool do_saved_restored(DisasContext *dc, bool saved) +{ + if (!supervisor(dc)) { + return raise_priv(dc); + } + if (saved) { + gen_helper_saved(tcg_env); + } else { + gen_helper_restored(tcg_env); + } + return advance_pc(dc); +} + +TRANS(SAVED, 64, do_saved_restored, true) +TRANS(RESTORED, 64, do_saved_restored, false) + +static bool trans_NOP(DisasContext *dc, arg_NOP *a) +{ + return advance_pc(dc); +} + +/* + * TODO: Need a feature bit for sparcv8. + * In the meantime, treat all 32-bit cpus like sparcv7. + */ +TRANS(NOP_v7, 32, trans_NOP, a) +TRANS(NOP_v9, 64, trans_NOP, a) + +static bool do_arith_int(DisasContext *dc, arg_r_r_ri_cc *a, + void (*func)(TCGv, TCGv, TCGv), + void (*funci)(TCGv, TCGv, target_long), + bool logic_cc) +{ + TCGv dst, src1; + + /* For simplicity, we under-decoded the rs2 form. */ + if (!a->imm && a->rs2_or_imm & ~0x1f) { + return false; + } + + if (logic_cc) { + dst = cpu_cc_N; + } else { + dst = gen_dest_gpr(dc, a->rd); + } + src1 = gen_load_gpr(dc, a->rs1); + + if (a->imm || a->rs2_or_imm == 0) { + if (funci) { + funci(dst, src1, a->rs2_or_imm); + } else { + func(dst, src1, tcg_constant_tl(a->rs2_or_imm)); + } + } else { + func(dst, src1, cpu_regs[a->rs2_or_imm]); + } + + if (logic_cc) { + if (TARGET_LONG_BITS == 64) { + tcg_gen_mov_tl(cpu_icc_Z, cpu_cc_N); + tcg_gen_movi_tl(cpu_icc_C, 0); + } + tcg_gen_mov_tl(cpu_cc_Z, cpu_cc_N); + tcg_gen_movi_tl(cpu_cc_C, 0); + tcg_gen_movi_tl(cpu_cc_V, 0); + } + + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +static bool do_arith(DisasContext *dc, arg_r_r_ri_cc *a, + void (*func)(TCGv, TCGv, TCGv), + void (*funci)(TCGv, TCGv, target_long), + void (*func_cc)(TCGv, TCGv, TCGv)) +{ + if (a->cc) { + return do_arith_int(dc, a, func_cc, NULL, false); + } + return do_arith_int(dc, a, func, funci, false); +} + +static bool do_logic(DisasContext *dc, arg_r_r_ri_cc *a, + void (*func)(TCGv, TCGv, TCGv), + void (*funci)(TCGv, TCGv, target_long)) +{ + return do_arith_int(dc, a, func, funci, a->cc); +} + +TRANS(ADD, ALL, do_arith, a, tcg_gen_add_tl, tcg_gen_addi_tl, gen_op_addcc) +TRANS(SUB, ALL, do_arith, a, tcg_gen_sub_tl, tcg_gen_subi_tl, gen_op_subcc) +TRANS(ADDC, ALL, do_arith, a, gen_op_addc, NULL, gen_op_addccc) +TRANS(SUBC, ALL, do_arith, a, gen_op_subc, NULL, gen_op_subccc) + +TRANS(TADDcc, ALL, do_arith, a, NULL, NULL, gen_op_taddcc) +TRANS(TSUBcc, ALL, do_arith, a, NULL, NULL, gen_op_tsubcc) +TRANS(TADDccTV, ALL, do_arith, a, NULL, NULL, gen_op_taddcctv) +TRANS(TSUBccTV, ALL, do_arith, a, NULL, NULL, gen_op_tsubcctv) + +TRANS(AND, ALL, do_logic, a, tcg_gen_and_tl, tcg_gen_andi_tl) +TRANS(XOR, ALL, do_logic, a, tcg_gen_xor_tl, tcg_gen_xori_tl) +TRANS(ANDN, ALL, do_logic, a, tcg_gen_andc_tl, NULL) +TRANS(ORN, ALL, do_logic, a, tcg_gen_orc_tl, NULL) +TRANS(XORN, ALL, do_logic, a, tcg_gen_eqv_tl, NULL) + +TRANS(MULX, 64, do_arith, a, tcg_gen_mul_tl, tcg_gen_muli_tl, NULL) +TRANS(UMUL, MUL, do_logic, a, gen_op_umul, NULL) +TRANS(SMUL, MUL, do_logic, a, gen_op_smul, NULL) +TRANS(MULScc, ALL, do_arith, a, NULL, NULL, gen_op_mulscc) + +TRANS(UDIVcc, DIV, do_arith, a, NULL, NULL, gen_op_udivcc) +TRANS(SDIV, DIV, do_arith, a, gen_op_sdiv, NULL, gen_op_sdivcc) + +/* TODO: Should have feature bit -- comes in with UltraSparc T2. */ +TRANS(POPC, 64, do_arith, a, gen_op_popc, NULL, NULL) + +static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a) +{ + /* OR with %g0 is the canonical alias for MOV. */ + if (!a->cc && a->rs1 == 0) { + if (a->imm || a->rs2_or_imm == 0) { + gen_store_gpr(dc, a->rd, tcg_constant_tl(a->rs2_or_imm)); + } else if (a->rs2_or_imm & ~0x1f) { + /* For simplicity, we under-decoded the rs2 form. */ + return false; + } else { + gen_store_gpr(dc, a->rd, cpu_regs[a->rs2_or_imm]); + } + return advance_pc(dc); + } + return do_logic(dc, a, tcg_gen_or_tl, tcg_gen_ori_tl); +} + +static bool trans_UDIV(DisasContext *dc, arg_r_r_ri *a) +{ + TCGv_i64 t1, t2; + TCGv dst; + + if (!avail_DIV(dc)) { + return false; + } + /* For simplicity, we under-decoded the rs2 form. */ + if (!a->imm && a->rs2_or_imm & ~0x1f) { + return false; + } + + if (unlikely(a->rs2_or_imm == 0)) { + gen_exception(dc, TT_DIV_ZERO); + return true; + } + + if (a->imm) { + t2 = tcg_constant_i64((uint32_t)a->rs2_or_imm); + } else { + TCGLabel *lab; + TCGv_i32 n2; + + finishing_insn(dc); + flush_cond(dc); + + n2 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(n2, cpu_regs[a->rs2_or_imm]); + + lab = delay_exception(dc, TT_DIV_ZERO); + tcg_gen_brcondi_i32(TCG_COND_EQ, n2, 0, lab); + + t2 = tcg_temp_new_i64(); +#ifdef TARGET_SPARC64 + tcg_gen_ext32u_i64(t2, cpu_regs[a->rs2_or_imm]); +#else + tcg_gen_extu_i32_i64(t2, cpu_regs[a->rs2_or_imm]); +#endif + } + + t1 = tcg_temp_new_i64(); + tcg_gen_concat_tl_i64(t1, gen_load_gpr(dc, a->rs1), cpu_y); + + tcg_gen_divu_i64(t1, t1, t2); + tcg_gen_umin_i64(t1, t1, tcg_constant_i64(UINT32_MAX)); + + dst = gen_dest_gpr(dc, a->rd); + tcg_gen_trunc_i64_tl(dst, t1); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +static bool trans_UDIVX(DisasContext *dc, arg_r_r_ri *a) +{ + TCGv dst, src1, src2; + + if (!avail_64(dc)) { + return false; + } + /* For simplicity, we under-decoded the rs2 form. */ + if (!a->imm && a->rs2_or_imm & ~0x1f) { + return false; + } + + if (unlikely(a->rs2_or_imm == 0)) { + gen_exception(dc, TT_DIV_ZERO); + return true; + } + + if (a->imm) { + src2 = tcg_constant_tl(a->rs2_or_imm); + } else { + TCGLabel *lab; + + finishing_insn(dc); + flush_cond(dc); + + lab = delay_exception(dc, TT_DIV_ZERO); + src2 = cpu_regs[a->rs2_or_imm]; + tcg_gen_brcondi_tl(TCG_COND_EQ, src2, 0, lab); + } + + dst = gen_dest_gpr(dc, a->rd); + src1 = gen_load_gpr(dc, a->rs1); + + tcg_gen_divu_tl(dst, src1, src2); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +static bool trans_SDIVX(DisasContext *dc, arg_r_r_ri *a) +{ + TCGv dst, src1, src2; + + if (!avail_64(dc)) { + return false; + } + /* For simplicity, we under-decoded the rs2 form. */ + if (!a->imm && a->rs2_or_imm & ~0x1f) { + return false; + } + + if (unlikely(a->rs2_or_imm == 0)) { + gen_exception(dc, TT_DIV_ZERO); + return true; + } + + dst = gen_dest_gpr(dc, a->rd); + src1 = gen_load_gpr(dc, a->rs1); + + if (a->imm) { + if (unlikely(a->rs2_or_imm == -1)) { + tcg_gen_neg_tl(dst, src1); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); + } + src2 = tcg_constant_tl(a->rs2_or_imm); + } else { + TCGLabel *lab; + TCGv t1, t2; + + finishing_insn(dc); + flush_cond(dc); + + lab = delay_exception(dc, TT_DIV_ZERO); + src2 = cpu_regs[a->rs2_or_imm]; + tcg_gen_brcondi_tl(TCG_COND_EQ, src2, 0, lab); + + /* + * Need to avoid INT64_MIN / -1, which will trap on x86 host. + * Set SRC2 to 1 as a new divisor, to produce the correct result. + */ + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + tcg_gen_setcondi_tl(TCG_COND_EQ, t1, src1, (target_long)INT64_MIN); + tcg_gen_setcondi_tl(TCG_COND_EQ, t2, src2, -1); + tcg_gen_and_tl(t1, t1, t2); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t1, tcg_constant_tl(0), + tcg_constant_tl(1), src2); + src2 = t1; + } + + tcg_gen_div_tl(dst, src1, src2); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +static bool gen_edge(DisasContext *dc, arg_r_r_r *a, int width, bool cc, bool left) { - TCGv lo1, lo2, t1, t2; + TCGv dst, s1, s2, lo1, lo2; uint64_t amask, tabl, tabr; int shift, imask, omask; + dst = gen_dest_gpr(dc, a->rd); + s1 = gen_load_gpr(dc, a->rs1); + s2 = gen_load_gpr(dc, a->rs2); + if (cc) { - tcg_gen_mov_tl(cpu_cc_src, s1); - tcg_gen_mov_tl(cpu_cc_src2, s2); - tcg_gen_sub_tl(cpu_cc_dst, s1, s2); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUB); - dc->cc_op = CC_OP_SUB; + gen_op_subcc(cpu_cc_N, s1, s2); } - /* Theory of operation: there are two tables, left and right (not to - be confused with the left and right versions of the opcode). These - are indexed by the low 3 bits of the inputs. To make things "easy", - these tables are loaded into two constants, TABL and TABR below. - The operation index = (input & imask) << shift calculates the index - into the constant, while val = (table >> index) & omask calculates - the value we're looking for. */ + /* + * Theory of operation: there are two tables, left and right (not to + * be confused with the left and right versions of the opcode). These + * are indexed by the low 3 bits of the inputs. To make things "easy", + * these tables are loaded into two constants, TABL and TABR below. + * The operation index = (input & imask) << shift calculates the index + * into the constant, while val = (table >> index) & omask calculates + * the value we're looking for. + */ switch (width) { case 8: imask = 0x7; @@ -3068,2751 +3540,1360 @@ static void gen_edge(DisasContext *dc, TCGv dst, TCGv s1, TCGv s2, tcg_gen_shli_tl(lo1, lo1, shift); tcg_gen_shli_tl(lo2, lo2, shift); - t1 = tcg_const_tl(tabl); - t2 = tcg_const_tl(tabr); - tcg_gen_shr_tl(lo1, t1, lo1); - tcg_gen_shr_tl(lo2, t2, lo2); - tcg_gen_andi_tl(dst, lo1, omask); + tcg_gen_shr_tl(lo1, tcg_constant_tl(tabl), lo1); + tcg_gen_shr_tl(lo2, tcg_constant_tl(tabr), lo2); + tcg_gen_andi_tl(lo1, lo1, omask); tcg_gen_andi_tl(lo2, lo2, omask); - amask = -8; - if (AM_CHECK(dc)) { - amask &= 0xffffffffULL; - } + amask = address_mask_i(dc, -8); tcg_gen_andi_tl(s1, s1, amask); tcg_gen_andi_tl(s2, s2, amask); - /* We want to compute - dst = (s1 == s2 ? lo1 : lo1 & lo2). - We've already done dst = lo1, so this reduces to - dst &= (s1 == s2 ? -1 : lo2) - Which we perform by - lo2 |= -(s1 == s2) - dst &= lo2 - */ - tcg_gen_setcond_tl(TCG_COND_EQ, t1, s1, s2); - tcg_gen_neg_tl(t1, t1); - tcg_gen_or_tl(lo2, lo2, t1); - tcg_gen_and_tl(dst, dst, lo2); + /* Compute dst = (s1 == s2 ? lo1 : lo1 & lo2). */ + tcg_gen_and_tl(lo2, lo2, lo1); + tcg_gen_movcond_tl(TCG_COND_EQ, dst, s1, s2, lo1, lo2); - tcg_temp_free(lo1); - tcg_temp_free(lo2); - tcg_temp_free(t1); - tcg_temp_free(t2); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); } -static void gen_alignaddr(TCGv dst, TCGv s1, TCGv s2, bool left) +TRANS(EDGE8cc, VIS1, gen_edge, a, 8, 1, 0) +TRANS(EDGE8Lcc, VIS1, gen_edge, a, 8, 1, 1) +TRANS(EDGE16cc, VIS1, gen_edge, a, 16, 1, 0) +TRANS(EDGE16Lcc, VIS1, gen_edge, a, 16, 1, 1) +TRANS(EDGE32cc, VIS1, gen_edge, a, 32, 1, 0) +TRANS(EDGE32Lcc, VIS1, gen_edge, a, 32, 1, 1) + +TRANS(EDGE8N, VIS2, gen_edge, a, 8, 0, 0) +TRANS(EDGE8LN, VIS2, gen_edge, a, 8, 0, 1) +TRANS(EDGE16N, VIS2, gen_edge, a, 16, 0, 0) +TRANS(EDGE16LN, VIS2, gen_edge, a, 16, 0, 1) +TRANS(EDGE32N, VIS2, gen_edge, a, 32, 0, 0) +TRANS(EDGE32LN, VIS2, gen_edge, a, 32, 0, 1) + +static bool do_rrr(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv, TCGv, TCGv)) { + TCGv dst = gen_dest_gpr(dc, a->rd); + TCGv src1 = gen_load_gpr(dc, a->rs1); + TCGv src2 = gen_load_gpr(dc, a->rs2); + + func(dst, src1, src2); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(ARRAY8, VIS1, do_rrr, a, gen_helper_array8) +TRANS(ARRAY16, VIS1, do_rrr, a, gen_op_array16) +TRANS(ARRAY32, VIS1, do_rrr, a, gen_op_array32) + +static void gen_op_alignaddr(TCGv dst, TCGv s1, TCGv s2) +{ +#ifdef TARGET_SPARC64 TCGv tmp = tcg_temp_new(); tcg_gen_add_tl(tmp, s1, s2); tcg_gen_andi_tl(dst, tmp, -8); - if (left) { - tcg_gen_neg_tl(tmp, tmp); - } tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, tmp, 0, 3); - - tcg_temp_free(tmp); +#else + g_assert_not_reached(); +#endif } -static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) +static void gen_op_alignaddrl(TCGv dst, TCGv s1, TCGv s2) { - TCGv t1, t2, shift; +#ifdef TARGET_SPARC64 + TCGv tmp = tcg_temp_new(); - t1 = tcg_temp_new(); - t2 = tcg_temp_new(); - shift = tcg_temp_new(); - - tcg_gen_andi_tl(shift, gsr, 7); - tcg_gen_shli_tl(shift, shift, 3); - tcg_gen_shl_tl(t1, s1, shift); - - /* A shift of 64 does not produce 0 in TCG. Divide this into a - shift of (up to 63) followed by a constant shift of 1. */ - tcg_gen_xori_tl(shift, shift, 63); - tcg_gen_shr_tl(t2, s2, shift); - tcg_gen_shri_tl(t2, t2, 1); - - tcg_gen_or_tl(dst, t1, t2); - - tcg_temp_free(t1); - tcg_temp_free(t2); - tcg_temp_free(shift); + tcg_gen_add_tl(tmp, s1, s2); + tcg_gen_andi_tl(dst, tmp, -8); + tcg_gen_neg_tl(tmp, tmp); + tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, tmp, 0, 3); +#else + g_assert_not_reached(); +#endif } -#endif -#define CHECK_IU_FEATURE(dc, FEATURE) \ - if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ - goto illegal_insn; -#define CHECK_FPU_FEATURE(dc, FEATURE) \ - if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \ - goto nfpu_insn; +TRANS(ALIGNADDR, VIS1, do_rrr, a, gen_op_alignaddr) +TRANS(ALIGNADDRL, VIS1, do_rrr, a, gen_op_alignaddrl) -/* before an instruction, dc->pc must be static */ -static void disas_sparc_insn(DisasContext * dc, unsigned int insn) +static void gen_op_bmask(TCGv dst, TCGv s1, TCGv s2) { - unsigned int opc, rs1, rs2, rd; - TCGv cpu_src1, cpu_src2; - TCGv_i32 cpu_src1_32, cpu_src2_32, cpu_dst_32; - TCGv_i64 cpu_src1_64, cpu_src2_64, cpu_dst_64; - target_long simm; - - opc = GET_FIELD(insn, 0, 1); - rd = GET_FIELD(insn, 2, 6); - - switch (opc) { - case 0: /* branches/sethi */ - { - unsigned int xop = GET_FIELD(insn, 7, 9); - int32_t target; - switch (xop) { #ifdef TARGET_SPARC64 - case 0x1: /* V9 BPcc */ - { - int cc; - - target = GET_FIELD_SP(insn, 0, 18); - target = sign_extend(target, 19); - target <<= 2; - cc = GET_FIELD_SP(insn, 20, 21); - if (cc == 0) - do_branch(dc, target, insn, 0); - else if (cc == 2) - do_branch(dc, target, insn, 1); - else - goto illegal_insn; - goto jmp_insn; - } - case 0x3: /* V9 BPr */ - { - target = GET_FIELD_SP(insn, 0, 13) | - (GET_FIELD_SP(insn, 20, 21) << 14); - target = sign_extend(target, 16); - target <<= 2; - cpu_src1 = get_src1(dc, insn); - do_branch_reg(dc, target, insn, cpu_src1); - goto jmp_insn; - } - case 0x5: /* V9 FBPcc */ - { - int cc = GET_FIELD_SP(insn, 20, 21); - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - target = GET_FIELD_SP(insn, 0, 18); - target = sign_extend(target, 19); - target <<= 2; - do_fbranch(dc, target, insn, cc); - goto jmp_insn; - } + tcg_gen_add_tl(dst, s1, s2); + tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, dst, 32, 32); #else - case 0x7: /* CBN+x */ - { - goto ncp_insn; - } + g_assert_not_reached(); #endif - case 0x2: /* BN+x */ - { - target = GET_FIELD(insn, 10, 31); - target = sign_extend(target, 22); - target <<= 2; - do_branch(dc, target, insn, 0); - goto jmp_insn; - } - case 0x6: /* FBN+x */ - { - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - target = GET_FIELD(insn, 10, 31); - target = sign_extend(target, 22); - target <<= 2; - do_fbranch(dc, target, insn, 0); - goto jmp_insn; - } - case 0x4: /* SETHI */ - /* Special-case %g0 because that's the canonical nop. */ - if (rd) { - uint32_t value = GET_FIELD(insn, 10, 31); - TCGv t = gen_dest_gpr(dc, rd); - tcg_gen_movi_tl(t, value << 10); - gen_store_gpr(dc, rd, t); - } - break; - case 0x0: /* UNIMPL */ - default: - goto illegal_insn; - } - break; - } - break; - case 1: /*CALL*/ - { - target_long target = GET_FIELDs(insn, 2, 31) << 2; - TCGv o7 = gen_dest_gpr(dc, 15); +} - tcg_gen_movi_tl(o7, dc->pc); - gen_store_gpr(dc, 15, o7); - target += dc->pc; - gen_mov_pc_npc(dc); -#ifdef TARGET_SPARC64 - if (unlikely(AM_CHECK(dc))) { - target &= 0xffffffffULL; - } -#endif - dc->npc = target; - } - goto jmp_insn; - case 2: /* FPU & Logical Operations */ - { - unsigned int xop = GET_FIELD(insn, 7, 12); - TCGv cpu_dst = get_temp_tl(dc); - TCGv cpu_tmp0; +TRANS(BMASK, VIS2, do_rrr, a, gen_op_bmask) - if (xop == 0x3a) { /* generate trap */ - int cond = GET_FIELD(insn, 3, 6); - TCGv_i32 trap; - TCGLabel *l1 = NULL; - int mask; +static bool do_shift_r(DisasContext *dc, arg_shiftr *a, bool l, bool u) +{ + TCGv dst, src1, src2; - if (cond == 0) { - /* Trap never. */ - break; - } - - save_state(dc); - - if (cond != 8) { - /* Conditional trap. */ - DisasCompare cmp; -#ifdef TARGET_SPARC64 - /* V9 icc/xcc */ - int cc = GET_FIELD_SP(insn, 11, 12); - if (cc == 0) { - gen_compare(&cmp, 0, cond, dc); - } else if (cc == 2) { - gen_compare(&cmp, 1, cond, dc); - } else { - goto illegal_insn; - } -#else - gen_compare(&cmp, 0, cond, dc); -#endif - l1 = gen_new_label(); - tcg_gen_brcond_tl(tcg_invert_cond(cmp.cond), - cmp.c1, cmp.c2, l1); - free_compare(&cmp); - } - - mask = ((dc->def->features & CPU_FEATURE_HYPV) && supervisor(dc) - ? UA2005_HTRAP_MASK : V8_TRAP_MASK); - - /* Don't use the normal temporaries, as they may well have - gone out of scope with the branch above. While we're - doing that we might as well pre-truncate to 32-bit. */ - trap = tcg_temp_new_i32(); - - rs1 = GET_FIELD_SP(insn, 14, 18); - if (IS_IMM) { - rs2 = GET_FIELD_SP(insn, 0, 7); - if (rs1 == 0) { - tcg_gen_movi_i32(trap, (rs2 & mask) + TT_TRAP); - /* Signal that the trap value is fully constant. */ - mask = 0; - } else { - TCGv t1 = gen_load_gpr(dc, rs1); - tcg_gen_trunc_tl_i32(trap, t1); - tcg_gen_addi_i32(trap, trap, rs2); - } - } else { - TCGv t1, t2; - rs2 = GET_FIELD_SP(insn, 0, 4); - t1 = gen_load_gpr(dc, rs1); - t2 = gen_load_gpr(dc, rs2); - tcg_gen_add_tl(t1, t1, t2); - tcg_gen_trunc_tl_i32(trap, t1); - } - if (mask != 0) { - tcg_gen_andi_i32(trap, trap, mask); - tcg_gen_addi_i32(trap, trap, TT_TRAP); - } - - gen_helper_raise_exception(cpu_env, trap); - tcg_temp_free_i32(trap); - - if (cond == 8) { - /* An unconditional trap ends the TB. */ - dc->base.is_jmp = DISAS_NORETURN; - goto jmp_insn; - } else { - /* A conditional trap falls through to the next insn. */ - gen_set_label(l1); - break; - } - } else if (xop == 0x28) { - rs1 = GET_FIELD(insn, 13, 17); - switch(rs1) { - case 0: /* rdy */ -#ifndef TARGET_SPARC64 - case 0x01 ... 0x0e: /* undefined in the SPARCv8 - manual, rdy on the microSPARC - II */ - case 0x0f: /* stbar in the SPARCv8 manual, - rdy on the microSPARC II */ - case 0x10 ... 0x1f: /* implementation-dependent in the - SPARCv8 manual, rdy on the - microSPARC II */ - /* Read Asr17 */ - if (rs1 == 0x11 && dc->def->features & CPU_FEATURE_ASR17) { - TCGv t = gen_dest_gpr(dc, rd); - /* Read Asr17 for a Leon3 monoprocessor */ - tcg_gen_movi_tl(t, (1 << 8) | (dc->def->nwindows - 1)); - gen_store_gpr(dc, rd, t); - break; - } -#endif - gen_store_gpr(dc, rd, cpu_y); - break; -#ifdef TARGET_SPARC64 - case 0x2: /* V9 rdccr */ - update_psr(dc); - gen_helper_rdccr(cpu_dst, cpu_env); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x3: /* V9 rdasi */ - tcg_gen_movi_tl(cpu_dst, dc->asi); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x4: /* V9 rdtick */ - { - TCGv_ptr r_tickptr; - TCGv_i32 r_const; - - r_tickptr = tcg_temp_new_ptr(); - r_const = tcg_const_i32(dc->mem_idx); - tcg_gen_ld_ptr(r_tickptr, cpu_env, - offsetof(CPUSPARCState, tick)); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_tick_get_count(cpu_dst, cpu_env, r_tickptr, - r_const); - tcg_temp_free_ptr(r_tickptr); - tcg_temp_free_i32(r_const); - gen_store_gpr(dc, rd, cpu_dst); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - /* I/O operations in icount mode must end the TB */ - dc->base.is_jmp = DISAS_EXIT; - } - } - break; - case 0x5: /* V9 rdpc */ - { - TCGv t = gen_dest_gpr(dc, rd); - if (unlikely(AM_CHECK(dc))) { - tcg_gen_movi_tl(t, dc->pc & 0xffffffffULL); - } else { - tcg_gen_movi_tl(t, dc->pc); - } - gen_store_gpr(dc, rd, t); - } - break; - case 0x6: /* V9 rdfprs */ - tcg_gen_ext_i32_tl(cpu_dst, cpu_fprs); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0xf: /* V9 membar */ - break; /* no effect */ - case 0x13: /* Graphics Status */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_store_gpr(dc, rd, cpu_gsr); - break; - case 0x16: /* Softint */ - tcg_gen_ld32s_tl(cpu_dst, cpu_env, - offsetof(CPUSPARCState, softint)); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x17: /* Tick compare */ - gen_store_gpr(dc, rd, cpu_tick_cmpr); - break; - case 0x18: /* System tick */ - { - TCGv_ptr r_tickptr; - TCGv_i32 r_const; - - r_tickptr = tcg_temp_new_ptr(); - r_const = tcg_const_i32(dc->mem_idx); - tcg_gen_ld_ptr(r_tickptr, cpu_env, - offsetof(CPUSPARCState, stick)); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_tick_get_count(cpu_dst, cpu_env, r_tickptr, - r_const); - tcg_temp_free_ptr(r_tickptr); - tcg_temp_free_i32(r_const); - gen_store_gpr(dc, rd, cpu_dst); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - /* I/O operations in icount mode must end the TB */ - dc->base.is_jmp = DISAS_EXIT; - } - } - break; - case 0x19: /* System tick compare */ - gen_store_gpr(dc, rd, cpu_stick_cmpr); - break; - case 0x1a: /* UltraSPARC-T1 Strand status */ - /* XXX HYPV check maybe not enough, UA2005 & UA2007 describe - * this ASR as impl. dep - */ - CHECK_IU_FEATURE(dc, HYPV); - { - TCGv t = gen_dest_gpr(dc, rd); - tcg_gen_movi_tl(t, 1UL); - gen_store_gpr(dc, rd, t); - } - break; - case 0x10: /* Performance Control */ - case 0x11: /* Performance Instrumentation Counter */ - case 0x12: /* Dispatch Control */ - case 0x14: /* Softint set, WO */ - case 0x15: /* Softint clear, WO */ -#endif - default: - goto illegal_insn; - } -#if !defined(CONFIG_USER_ONLY) - } else if (xop == 0x29) { /* rdpsr / UA2005 rdhpr */ -#ifndef TARGET_SPARC64 - if (!supervisor(dc)) { - goto priv_insn; - } - update_psr(dc); - gen_helper_rdpsr(cpu_dst, cpu_env); -#else - CHECK_IU_FEATURE(dc, HYPV); - if (!hypervisor(dc)) - goto priv_insn; - rs1 = GET_FIELD(insn, 13, 17); - switch (rs1) { - case 0: // hpstate - tcg_gen_ld_i64(cpu_dst, cpu_env, - offsetof(CPUSPARCState, hpstate)); - break; - case 1: // htstate - // gen_op_rdhtstate(); - break; - case 3: // hintp - tcg_gen_mov_tl(cpu_dst, cpu_hintp); - break; - case 5: // htba - tcg_gen_mov_tl(cpu_dst, cpu_htba); - break; - case 6: // hver - tcg_gen_mov_tl(cpu_dst, cpu_hver); - break; - case 31: // hstick_cmpr - tcg_gen_mov_tl(cpu_dst, cpu_hstick_cmpr); - break; - default: - goto illegal_insn; - } -#endif - gen_store_gpr(dc, rd, cpu_dst); - break; - } else if (xop == 0x2a) { /* rdwim / V9 rdpr */ - if (!supervisor(dc)) { - goto priv_insn; - } - cpu_tmp0 = get_temp_tl(dc); -#ifdef TARGET_SPARC64 - rs1 = GET_FIELD(insn, 13, 17); - switch (rs1) { - case 0: // tpc - { - TCGv_ptr r_tsptr; - - r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr, cpu_env); - tcg_gen_ld_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, tpc)); - tcg_temp_free_ptr(r_tsptr); - } - break; - case 1: // tnpc - { - TCGv_ptr r_tsptr; - - r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr, cpu_env); - tcg_gen_ld_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, tnpc)); - tcg_temp_free_ptr(r_tsptr); - } - break; - case 2: // tstate - { - TCGv_ptr r_tsptr; - - r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr, cpu_env); - tcg_gen_ld_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, tstate)); - tcg_temp_free_ptr(r_tsptr); - } - break; - case 3: // tt - { - TCGv_ptr r_tsptr = tcg_temp_new_ptr(); - - gen_load_trap_state_at_tl(r_tsptr, cpu_env); - tcg_gen_ld32s_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, tt)); - tcg_temp_free_ptr(r_tsptr); - } - break; - case 4: // tick - { - TCGv_ptr r_tickptr; - TCGv_i32 r_const; - - r_tickptr = tcg_temp_new_ptr(); - r_const = tcg_const_i32(dc->mem_idx); - tcg_gen_ld_ptr(r_tickptr, cpu_env, - offsetof(CPUSPARCState, tick)); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_tick_get_count(cpu_tmp0, cpu_env, - r_tickptr, r_const); - tcg_temp_free_ptr(r_tickptr); - tcg_temp_free_i32(r_const); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - /* I/O operations in icount mode must end the TB */ - dc->base.is_jmp = DISAS_EXIT; - } - } - break; - case 5: // tba - tcg_gen_mov_tl(cpu_tmp0, cpu_tbr); - break; - case 6: // pstate - tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, pstate)); - break; - case 7: // tl - tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, tl)); - break; - case 8: // pil - tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, psrpil)); - break; - case 9: // cwp - gen_helper_rdcwp(cpu_tmp0, cpu_env); - break; - case 10: // cansave - tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, cansave)); - break; - case 11: // canrestore - tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, canrestore)); - break; - case 12: // cleanwin - tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, cleanwin)); - break; - case 13: // otherwin - tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, otherwin)); - break; - case 14: // wstate - tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, wstate)); - break; - case 16: // UA2005 gl - CHECK_IU_FEATURE(dc, GL); - tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, gl)); - break; - case 26: // UA2005 strand status - CHECK_IU_FEATURE(dc, HYPV); - if (!hypervisor(dc)) - goto priv_insn; - tcg_gen_mov_tl(cpu_tmp0, cpu_ssr); - break; - case 31: // ver - tcg_gen_mov_tl(cpu_tmp0, cpu_ver); - break; - case 15: // fq - default: - goto illegal_insn; - } -#else - tcg_gen_ext_i32_tl(cpu_tmp0, cpu_wim); -#endif - gen_store_gpr(dc, rd, cpu_tmp0); - break; -#endif -#if defined(TARGET_SPARC64) || !defined(CONFIG_USER_ONLY) - } else if (xop == 0x2b) { /* rdtbr / V9 flushw */ -#ifdef TARGET_SPARC64 - gen_helper_flushw(cpu_env); -#else - if (!supervisor(dc)) - goto priv_insn; - gen_store_gpr(dc, rd, cpu_tbr); -#endif - break; -#endif - } else if (xop == 0x34) { /* FPU Operations */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_op_clear_ieee_excp_and_FTT(); - rs1 = GET_FIELD(insn, 13, 17); - rs2 = GET_FIELD(insn, 27, 31); - xop = GET_FIELD(insn, 18, 26); - - switch (xop) { - case 0x1: /* fmovs */ - cpu_src1_32 = gen_load_fpr_F(dc, rs2); - gen_store_fpr_F(dc, rd, cpu_src1_32); - break; - case 0x5: /* fnegs */ - gen_ne_fop_FF(dc, rd, rs2, gen_helper_fnegs); - break; - case 0x9: /* fabss */ - gen_ne_fop_FF(dc, rd, rs2, gen_helper_fabss); - break; - case 0x29: /* fsqrts */ - CHECK_FPU_FEATURE(dc, FSQRT); - gen_fop_FF(dc, rd, rs2, gen_helper_fsqrts); - break; - case 0x2a: /* fsqrtd */ - CHECK_FPU_FEATURE(dc, FSQRT); - gen_fop_DD(dc, rd, rs2, gen_helper_fsqrtd); - break; - case 0x2b: /* fsqrtq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_QQ(dc, rd, rs2, gen_helper_fsqrtq); - break; - case 0x41: /* fadds */ - gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fadds); - break; - case 0x42: /* faddd */ - gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_faddd); - break; - case 0x43: /* faddq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_faddq); - break; - case 0x45: /* fsubs */ - gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fsubs); - break; - case 0x46: /* fsubd */ - gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fsubd); - break; - case 0x47: /* fsubq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fsubq); - break; - case 0x49: /* fmuls */ - CHECK_FPU_FEATURE(dc, FMUL); - gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fmuls); - break; - case 0x4a: /* fmuld */ - CHECK_FPU_FEATURE(dc, FMUL); - gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmuld); - break; - case 0x4b: /* fmulq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - CHECK_FPU_FEATURE(dc, FMUL); - gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fmulq); - break; - case 0x4d: /* fdivs */ - gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fdivs); - break; - case 0x4e: /* fdivd */ - gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fdivd); - break; - case 0x4f: /* fdivq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fdivq); - break; - case 0x69: /* fsmuld */ - CHECK_FPU_FEATURE(dc, FSMULD); - gen_fop_DFF(dc, rd, rs1, rs2, gen_helper_fsmuld); - break; - case 0x6e: /* fdmulq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_QDD(dc, rd, rs1, rs2, gen_helper_fdmulq); - break; - case 0xc4: /* fitos */ - gen_fop_FF(dc, rd, rs2, gen_helper_fitos); - break; - case 0xc6: /* fdtos */ - gen_fop_FD(dc, rd, rs2, gen_helper_fdtos); - break; - case 0xc7: /* fqtos */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_FQ(dc, rd, rs2, gen_helper_fqtos); - break; - case 0xc8: /* fitod */ - gen_ne_fop_DF(dc, rd, rs2, gen_helper_fitod); - break; - case 0xc9: /* fstod */ - gen_ne_fop_DF(dc, rd, rs2, gen_helper_fstod); - break; - case 0xcb: /* fqtod */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_DQ(dc, rd, rs2, gen_helper_fqtod); - break; - case 0xcc: /* fitoq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_ne_fop_QF(dc, rd, rs2, gen_helper_fitoq); - break; - case 0xcd: /* fstoq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_ne_fop_QF(dc, rd, rs2, gen_helper_fstoq); - break; - case 0xce: /* fdtoq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_ne_fop_QD(dc, rd, rs2, gen_helper_fdtoq); - break; - case 0xd1: /* fstoi */ - gen_fop_FF(dc, rd, rs2, gen_helper_fstoi); - break; - case 0xd2: /* fdtoi */ - gen_fop_FD(dc, rd, rs2, gen_helper_fdtoi); - break; - case 0xd3: /* fqtoi */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_FQ(dc, rd, rs2, gen_helper_fqtoi); - break; -#ifdef TARGET_SPARC64 - case 0x2: /* V9 fmovd */ - cpu_src1_64 = gen_load_fpr_D(dc, rs2); - gen_store_fpr_D(dc, rd, cpu_src1_64); - break; - case 0x3: /* V9 fmovq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_move_Q(dc, rd, rs2); - break; - case 0x6: /* V9 fnegd */ - gen_ne_fop_DD(dc, rd, rs2, gen_helper_fnegd); - break; - case 0x7: /* V9 fnegq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_ne_fop_QQ(dc, rd, rs2, gen_helper_fnegq); - break; - case 0xa: /* V9 fabsd */ - gen_ne_fop_DD(dc, rd, rs2, gen_helper_fabsd); - break; - case 0xb: /* V9 fabsq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_ne_fop_QQ(dc, rd, rs2, gen_helper_fabsq); - break; - case 0x81: /* V9 fstox */ - gen_fop_DF(dc, rd, rs2, gen_helper_fstox); - break; - case 0x82: /* V9 fdtox */ - gen_fop_DD(dc, rd, rs2, gen_helper_fdtox); - break; - case 0x83: /* V9 fqtox */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_fop_DQ(dc, rd, rs2, gen_helper_fqtox); - break; - case 0x84: /* V9 fxtos */ - gen_fop_FD(dc, rd, rs2, gen_helper_fxtos); - break; - case 0x88: /* V9 fxtod */ - gen_fop_DD(dc, rd, rs2, gen_helper_fxtod); - break; - case 0x8c: /* V9 fxtoq */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_ne_fop_QD(dc, rd, rs2, gen_helper_fxtoq); - break; -#endif - default: - goto illegal_insn; - } - } else if (xop == 0x35) { /* FPU Operations */ -#ifdef TARGET_SPARC64 - int cond; -#endif - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_op_clear_ieee_excp_and_FTT(); - rs1 = GET_FIELD(insn, 13, 17); - rs2 = GET_FIELD(insn, 27, 31); - xop = GET_FIELD(insn, 18, 26); - -#ifdef TARGET_SPARC64 -#define FMOVR(sz) \ - do { \ - DisasCompare cmp; \ - cond = GET_FIELD_SP(insn, 10, 12); \ - cpu_src1 = get_src1(dc, insn); \ - gen_compare_reg(&cmp, cond, cpu_src1); \ - gen_fmov##sz(dc, &cmp, rd, rs2); \ - free_compare(&cmp); \ - } while (0) - - if ((xop & 0x11f) == 0x005) { /* V9 fmovsr */ - FMOVR(s); - break; - } else if ((xop & 0x11f) == 0x006) { // V9 fmovdr - FMOVR(d); - break; - } else if ((xop & 0x11f) == 0x007) { // V9 fmovqr - CHECK_FPU_FEATURE(dc, FLOAT128); - FMOVR(q); - break; - } -#undef FMOVR -#endif - switch (xop) { -#ifdef TARGET_SPARC64 -#define FMOVCC(fcc, sz) \ - do { \ - DisasCompare cmp; \ - cond = GET_FIELD_SP(insn, 14, 17); \ - gen_fcompare(&cmp, fcc, cond); \ - gen_fmov##sz(dc, &cmp, rd, rs2); \ - free_compare(&cmp); \ - } while (0) - - case 0x001: /* V9 fmovscc %fcc0 */ - FMOVCC(0, s); - break; - case 0x002: /* V9 fmovdcc %fcc0 */ - FMOVCC(0, d); - break; - case 0x003: /* V9 fmovqcc %fcc0 */ - CHECK_FPU_FEATURE(dc, FLOAT128); - FMOVCC(0, q); - break; - case 0x041: /* V9 fmovscc %fcc1 */ - FMOVCC(1, s); - break; - case 0x042: /* V9 fmovdcc %fcc1 */ - FMOVCC(1, d); - break; - case 0x043: /* V9 fmovqcc %fcc1 */ - CHECK_FPU_FEATURE(dc, FLOAT128); - FMOVCC(1, q); - break; - case 0x081: /* V9 fmovscc %fcc2 */ - FMOVCC(2, s); - break; - case 0x082: /* V9 fmovdcc %fcc2 */ - FMOVCC(2, d); - break; - case 0x083: /* V9 fmovqcc %fcc2 */ - CHECK_FPU_FEATURE(dc, FLOAT128); - FMOVCC(2, q); - break; - case 0x0c1: /* V9 fmovscc %fcc3 */ - FMOVCC(3, s); - break; - case 0x0c2: /* V9 fmovdcc %fcc3 */ - FMOVCC(3, d); - break; - case 0x0c3: /* V9 fmovqcc %fcc3 */ - CHECK_FPU_FEATURE(dc, FLOAT128); - FMOVCC(3, q); - break; -#undef FMOVCC -#define FMOVCC(xcc, sz) \ - do { \ - DisasCompare cmp; \ - cond = GET_FIELD_SP(insn, 14, 17); \ - gen_compare(&cmp, xcc, cond, dc); \ - gen_fmov##sz(dc, &cmp, rd, rs2); \ - free_compare(&cmp); \ - } while (0) - - case 0x101: /* V9 fmovscc %icc */ - FMOVCC(0, s); - break; - case 0x102: /* V9 fmovdcc %icc */ - FMOVCC(0, d); - break; - case 0x103: /* V9 fmovqcc %icc */ - CHECK_FPU_FEATURE(dc, FLOAT128); - FMOVCC(0, q); - break; - case 0x181: /* V9 fmovscc %xcc */ - FMOVCC(1, s); - break; - case 0x182: /* V9 fmovdcc %xcc */ - FMOVCC(1, d); - break; - case 0x183: /* V9 fmovqcc %xcc */ - CHECK_FPU_FEATURE(dc, FLOAT128); - FMOVCC(1, q); - break; -#undef FMOVCC -#endif - case 0x51: /* fcmps, V9 %fcc */ - cpu_src1_32 = gen_load_fpr_F(dc, rs1); - cpu_src2_32 = gen_load_fpr_F(dc, rs2); - gen_op_fcmps(rd & 3, cpu_src1_32, cpu_src2_32); - break; - case 0x52: /* fcmpd, V9 %fcc */ - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_op_fcmpd(rd & 3, cpu_src1_64, cpu_src2_64); - break; - case 0x53: /* fcmpq, V9 %fcc */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_op_load_fpr_QT0(QFPREG(rs1)); - gen_op_load_fpr_QT1(QFPREG(rs2)); - gen_op_fcmpq(rd & 3); - break; - case 0x55: /* fcmpes, V9 %fcc */ - cpu_src1_32 = gen_load_fpr_F(dc, rs1); - cpu_src2_32 = gen_load_fpr_F(dc, rs2); - gen_op_fcmpes(rd & 3, cpu_src1_32, cpu_src2_32); - break; - case 0x56: /* fcmped, V9 %fcc */ - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_op_fcmped(rd & 3, cpu_src1_64, cpu_src2_64); - break; - case 0x57: /* fcmpeq, V9 %fcc */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_op_load_fpr_QT0(QFPREG(rs1)); - gen_op_load_fpr_QT1(QFPREG(rs2)); - gen_op_fcmpeq(rd & 3); - break; - default: - goto illegal_insn; - } - } else if (xop == 0x2) { - TCGv dst = gen_dest_gpr(dc, rd); - rs1 = GET_FIELD(insn, 13, 17); - if (rs1 == 0) { - /* clr/mov shortcut : or %g0, x, y -> mov x, y */ - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 19, 31); - tcg_gen_movi_tl(dst, simm); - gen_store_gpr(dc, rd, dst); - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - if (rs2 == 0) { - tcg_gen_movi_tl(dst, 0); - gen_store_gpr(dc, rd, dst); - } else { - cpu_src2 = gen_load_gpr(dc, rs2); - gen_store_gpr(dc, rd, cpu_src2); - } - } - } else { - cpu_src1 = get_src1(dc, insn); - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 19, 31); - tcg_gen_ori_tl(dst, cpu_src1, simm); - gen_store_gpr(dc, rd, dst); - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - if (rs2 == 0) { - /* mov shortcut: or x, %g0, y -> mov x, y */ - gen_store_gpr(dc, rd, cpu_src1); - } else { - cpu_src2 = gen_load_gpr(dc, rs2); - tcg_gen_or_tl(dst, cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, dst); - } - } - } -#ifdef TARGET_SPARC64 - } else if (xop == 0x25) { /* sll, V9 sllx */ - cpu_src1 = get_src1(dc, insn); - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 20, 31); - if (insn & (1 << 12)) { - tcg_gen_shli_i64(cpu_dst, cpu_src1, simm & 0x3f); - } else { - tcg_gen_shli_i64(cpu_dst, cpu_src1, simm & 0x1f); - } - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - cpu_src2 = gen_load_gpr(dc, rs2); - cpu_tmp0 = get_temp_tl(dc); - if (insn & (1 << 12)) { - tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x3f); - } else { - tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x1f); - } - tcg_gen_shl_i64(cpu_dst, cpu_src1, cpu_tmp0); - } - gen_store_gpr(dc, rd, cpu_dst); - } else if (xop == 0x26) { /* srl, V9 srlx */ - cpu_src1 = get_src1(dc, insn); - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 20, 31); - if (insn & (1 << 12)) { - tcg_gen_shri_i64(cpu_dst, cpu_src1, simm & 0x3f); - } else { - tcg_gen_andi_i64(cpu_dst, cpu_src1, 0xffffffffULL); - tcg_gen_shri_i64(cpu_dst, cpu_dst, simm & 0x1f); - } - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - cpu_src2 = gen_load_gpr(dc, rs2); - cpu_tmp0 = get_temp_tl(dc); - if (insn & (1 << 12)) { - tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x3f); - tcg_gen_shr_i64(cpu_dst, cpu_src1, cpu_tmp0); - } else { - tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x1f); - tcg_gen_andi_i64(cpu_dst, cpu_src1, 0xffffffffULL); - tcg_gen_shr_i64(cpu_dst, cpu_dst, cpu_tmp0); - } - } - gen_store_gpr(dc, rd, cpu_dst); - } else if (xop == 0x27) { /* sra, V9 srax */ - cpu_src1 = get_src1(dc, insn); - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 20, 31); - if (insn & (1 << 12)) { - tcg_gen_sari_i64(cpu_dst, cpu_src1, simm & 0x3f); - } else { - tcg_gen_ext32s_i64(cpu_dst, cpu_src1); - tcg_gen_sari_i64(cpu_dst, cpu_dst, simm & 0x1f); - } - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - cpu_src2 = gen_load_gpr(dc, rs2); - cpu_tmp0 = get_temp_tl(dc); - if (insn & (1 << 12)) { - tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x3f); - tcg_gen_sar_i64(cpu_dst, cpu_src1, cpu_tmp0); - } else { - tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x1f); - tcg_gen_ext32s_i64(cpu_dst, cpu_src1); - tcg_gen_sar_i64(cpu_dst, cpu_dst, cpu_tmp0); - } - } - gen_store_gpr(dc, rd, cpu_dst); -#endif - } else if (xop < 0x36) { - if (xop < 0x20) { - cpu_src1 = get_src1(dc, insn); - cpu_src2 = get_src2(dc, insn); - switch (xop & ~0x10) { - case 0x0: /* add */ - if (xop & 0x10) { - gen_op_add_cc(cpu_dst, cpu_src1, cpu_src2); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADD); - dc->cc_op = CC_OP_ADD; - } else { - tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_src2); - } - break; - case 0x1: /* and */ - tcg_gen_and_tl(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; - case 0x2: /* or */ - tcg_gen_or_tl(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; - case 0x3: /* xor */ - tcg_gen_xor_tl(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; - case 0x4: /* sub */ - if (xop & 0x10) { - gen_op_sub_cc(cpu_dst, cpu_src1, cpu_src2); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUB); - dc->cc_op = CC_OP_SUB; - } else { - tcg_gen_sub_tl(cpu_dst, cpu_src1, cpu_src2); - } - break; - case 0x5: /* andn */ - tcg_gen_andc_tl(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; - case 0x6: /* orn */ - tcg_gen_orc_tl(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; - case 0x7: /* xorn */ - tcg_gen_eqv_tl(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; - case 0x8: /* addx, V9 addc */ - gen_op_addx_int(dc, cpu_dst, cpu_src1, cpu_src2, - (xop & 0x10)); - break; -#ifdef TARGET_SPARC64 - case 0x9: /* V9 mulx */ - tcg_gen_mul_i64(cpu_dst, cpu_src1, cpu_src2); - break; -#endif - case 0xa: /* umul */ - CHECK_IU_FEATURE(dc, MUL); - gen_op_umul(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; - case 0xb: /* smul */ - CHECK_IU_FEATURE(dc, MUL); - gen_op_smul(cpu_dst, cpu_src1, cpu_src2); - if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); - dc->cc_op = CC_OP_LOGIC; - } - break; - case 0xc: /* subx, V9 subc */ - gen_op_subx_int(dc, cpu_dst, cpu_src1, cpu_src2, - (xop & 0x10)); - break; -#ifdef TARGET_SPARC64 - case 0xd: /* V9 udivx */ - gen_helper_udivx(cpu_dst, cpu_env, cpu_src1, cpu_src2); - break; -#endif - case 0xe: /* udiv */ - CHECK_IU_FEATURE(dc, DIV); - if (xop & 0x10) { - gen_helper_udiv_cc(cpu_dst, cpu_env, cpu_src1, - cpu_src2); - dc->cc_op = CC_OP_DIV; - } else { - gen_helper_udiv(cpu_dst, cpu_env, cpu_src1, - cpu_src2); - } - break; - case 0xf: /* sdiv */ - CHECK_IU_FEATURE(dc, DIV); - if (xop & 0x10) { - gen_helper_sdiv_cc(cpu_dst, cpu_env, cpu_src1, - cpu_src2); - dc->cc_op = CC_OP_DIV; - } else { - gen_helper_sdiv(cpu_dst, cpu_env, cpu_src1, - cpu_src2); - } - break; - default: - goto illegal_insn; - } - gen_store_gpr(dc, rd, cpu_dst); - } else { - cpu_src1 = get_src1(dc, insn); - cpu_src2 = get_src2(dc, insn); - switch (xop) { - case 0x20: /* taddcc */ - gen_op_add_cc(cpu_dst, cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_TADD); - dc->cc_op = CC_OP_TADD; - break; - case 0x21: /* tsubcc */ - gen_op_sub_cc(cpu_dst, cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_TSUB); - dc->cc_op = CC_OP_TSUB; - break; - case 0x22: /* taddcctv */ - gen_helper_taddcctv(cpu_dst, cpu_env, - cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - dc->cc_op = CC_OP_TADDTV; - break; - case 0x23: /* tsubcctv */ - gen_helper_tsubcctv(cpu_dst, cpu_env, - cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - dc->cc_op = CC_OP_TSUBTV; - break; - case 0x24: /* mulscc */ - update_psr(dc); - gen_op_mulscc(cpu_dst, cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADD); - dc->cc_op = CC_OP_ADD; - break; -#ifndef TARGET_SPARC64 - case 0x25: /* sll */ - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 20, 31); - tcg_gen_shli_tl(cpu_dst, cpu_src1, simm & 0x1f); - } else { /* register */ - cpu_tmp0 = get_temp_tl(dc); - tcg_gen_andi_tl(cpu_tmp0, cpu_src2, 0x1f); - tcg_gen_shl_tl(cpu_dst, cpu_src1, cpu_tmp0); - } - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x26: /* srl */ - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 20, 31); - tcg_gen_shri_tl(cpu_dst, cpu_src1, simm & 0x1f); - } else { /* register */ - cpu_tmp0 = get_temp_tl(dc); - tcg_gen_andi_tl(cpu_tmp0, cpu_src2, 0x1f); - tcg_gen_shr_tl(cpu_dst, cpu_src1, cpu_tmp0); - } - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x27: /* sra */ - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 20, 31); - tcg_gen_sari_tl(cpu_dst, cpu_src1, simm & 0x1f); - } else { /* register */ - cpu_tmp0 = get_temp_tl(dc); - tcg_gen_andi_tl(cpu_tmp0, cpu_src2, 0x1f); - tcg_gen_sar_tl(cpu_dst, cpu_src1, cpu_tmp0); - } - gen_store_gpr(dc, rd, cpu_dst); - break; -#endif - case 0x30: - { - cpu_tmp0 = get_temp_tl(dc); - switch(rd) { - case 0: /* wry */ - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - tcg_gen_andi_tl(cpu_y, cpu_tmp0, 0xffffffff); - break; -#ifndef TARGET_SPARC64 - case 0x01 ... 0x0f: /* undefined in the - SPARCv8 manual, nop - on the microSPARC - II */ - case 0x10 ... 0x1f: /* implementation-dependent - in the SPARCv8 - manual, nop on the - microSPARC II */ - if ((rd == 0x13) && (dc->def->features & - CPU_FEATURE_POWERDOWN)) { - /* LEON3 power-down */ - save_state(dc); - gen_helper_power_down(cpu_env); - } - break; -#else - case 0x2: /* V9 wrccr */ - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - gen_helper_wrccr(cpu_env, cpu_tmp0); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_FLAGS); - dc->cc_op = CC_OP_FLAGS; - break; - case 0x3: /* V9 wrasi */ - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - tcg_gen_andi_tl(cpu_tmp0, cpu_tmp0, 0xff); - tcg_gen_st32_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, asi)); - /* End TB to notice changed ASI. */ - save_state(dc); - gen_op_next_insn(); - tcg_gen_exit_tb(NULL, 0); - dc->base.is_jmp = DISAS_NORETURN; - break; - case 0x6: /* V9 wrfprs */ - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - tcg_gen_trunc_tl_i32(cpu_fprs, cpu_tmp0); - dc->fprs_dirty = 0; - save_state(dc); - gen_op_next_insn(); - tcg_gen_exit_tb(NULL, 0); - dc->base.is_jmp = DISAS_NORETURN; - break; - case 0xf: /* V9 sir, nop if user */ -#if !defined(CONFIG_USER_ONLY) - if (supervisor(dc)) { - ; // XXX - } -#endif - break; - case 0x13: /* Graphics Status */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - tcg_gen_xor_tl(cpu_gsr, cpu_src1, cpu_src2); - break; - case 0x14: /* Softint set */ - if (!supervisor(dc)) - goto illegal_insn; - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - gen_helper_set_softint(cpu_env, cpu_tmp0); - break; - case 0x15: /* Softint clear */ - if (!supervisor(dc)) - goto illegal_insn; - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - gen_helper_clear_softint(cpu_env, cpu_tmp0); - break; - case 0x16: /* Softint write */ - if (!supervisor(dc)) - goto illegal_insn; - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - gen_helper_write_softint(cpu_env, cpu_tmp0); - break; - case 0x17: /* Tick compare */ -#if !defined(CONFIG_USER_ONLY) - if (!supervisor(dc)) - goto illegal_insn; -#endif - { - TCGv_ptr r_tickptr; - - tcg_gen_xor_tl(cpu_tick_cmpr, cpu_src1, - cpu_src2); - r_tickptr = tcg_temp_new_ptr(); - tcg_gen_ld_ptr(r_tickptr, cpu_env, - offsetof(CPUSPARCState, tick)); - if (tb_cflags(dc->base.tb) & - CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_tick_set_limit(r_tickptr, - cpu_tick_cmpr); - tcg_temp_free_ptr(r_tickptr); - /* End TB to handle timer interrupt */ - dc->base.is_jmp = DISAS_EXIT; - } - break; - case 0x18: /* System tick */ -#if !defined(CONFIG_USER_ONLY) - if (!supervisor(dc)) - goto illegal_insn; -#endif - { - TCGv_ptr r_tickptr; - - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, - cpu_src2); - r_tickptr = tcg_temp_new_ptr(); - tcg_gen_ld_ptr(r_tickptr, cpu_env, - offsetof(CPUSPARCState, stick)); - if (tb_cflags(dc->base.tb) & - CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_tick_set_count(r_tickptr, - cpu_tmp0); - tcg_temp_free_ptr(r_tickptr); - /* End TB to handle timer interrupt */ - dc->base.is_jmp = DISAS_EXIT; - } - break; - case 0x19: /* System tick compare */ -#if !defined(CONFIG_USER_ONLY) - if (!supervisor(dc)) - goto illegal_insn; -#endif - { - TCGv_ptr r_tickptr; - - tcg_gen_xor_tl(cpu_stick_cmpr, cpu_src1, - cpu_src2); - r_tickptr = tcg_temp_new_ptr(); - tcg_gen_ld_ptr(r_tickptr, cpu_env, - offsetof(CPUSPARCState, stick)); - if (tb_cflags(dc->base.tb) & - CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_tick_set_limit(r_tickptr, - cpu_stick_cmpr); - tcg_temp_free_ptr(r_tickptr); - /* End TB to handle timer interrupt */ - dc->base.is_jmp = DISAS_EXIT; - } - break; - - case 0x10: /* Performance Control */ - case 0x11: /* Performance Instrumentation - Counter */ - case 0x12: /* Dispatch Control */ -#endif - default: - goto illegal_insn; - } - } - break; -#if !defined(CONFIG_USER_ONLY) - case 0x31: /* wrpsr, V9 saved, restored */ - { - if (!supervisor(dc)) - goto priv_insn; -#ifdef TARGET_SPARC64 - switch (rd) { - case 0: - gen_helper_saved(cpu_env); - break; - case 1: - gen_helper_restored(cpu_env); - break; - case 2: /* UA2005 allclean */ - case 3: /* UA2005 otherw */ - case 4: /* UA2005 normalw */ - case 5: /* UA2005 invalw */ - // XXX - default: - goto illegal_insn; - } -#else - cpu_tmp0 = get_temp_tl(dc); - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - gen_helper_wrpsr(cpu_env, cpu_tmp0); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_FLAGS); - dc->cc_op = CC_OP_FLAGS; - save_state(dc); - gen_op_next_insn(); - tcg_gen_exit_tb(NULL, 0); - dc->base.is_jmp = DISAS_NORETURN; -#endif - } - break; - case 0x32: /* wrwim, V9 wrpr */ - { - if (!supervisor(dc)) - goto priv_insn; - cpu_tmp0 = get_temp_tl(dc); - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); -#ifdef TARGET_SPARC64 - switch (rd) { - case 0: // tpc - { - TCGv_ptr r_tsptr; - - r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr, cpu_env); - tcg_gen_st_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, tpc)); - tcg_temp_free_ptr(r_tsptr); - } - break; - case 1: // tnpc - { - TCGv_ptr r_tsptr; - - r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr, cpu_env); - tcg_gen_st_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, tnpc)); - tcg_temp_free_ptr(r_tsptr); - } - break; - case 2: // tstate - { - TCGv_ptr r_tsptr; - - r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr, cpu_env); - tcg_gen_st_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, - tstate)); - tcg_temp_free_ptr(r_tsptr); - } - break; - case 3: // tt - { - TCGv_ptr r_tsptr; - - r_tsptr = tcg_temp_new_ptr(); - gen_load_trap_state_at_tl(r_tsptr, cpu_env); - tcg_gen_st32_tl(cpu_tmp0, r_tsptr, - offsetof(trap_state, tt)); - tcg_temp_free_ptr(r_tsptr); - } - break; - case 4: // tick - { - TCGv_ptr r_tickptr; - - r_tickptr = tcg_temp_new_ptr(); - tcg_gen_ld_ptr(r_tickptr, cpu_env, - offsetof(CPUSPARCState, tick)); - if (tb_cflags(dc->base.tb) & - CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_tick_set_count(r_tickptr, - cpu_tmp0); - tcg_temp_free_ptr(r_tickptr); - /* End TB to handle timer interrupt */ - dc->base.is_jmp = DISAS_EXIT; - } - break; - case 5: // tba - tcg_gen_mov_tl(cpu_tbr, cpu_tmp0); - break; - case 6: // pstate - save_state(dc); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_wrpstate(cpu_env, cpu_tmp0); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - /* I/O ops in icount mode must end the TB */ - dc->base.is_jmp = DISAS_EXIT; - } - dc->npc = DYNAMIC_PC; - break; - case 7: // tl - save_state(dc); - tcg_gen_st32_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, tl)); - dc->npc = DYNAMIC_PC; - break; - case 8: // pil - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_wrpil(cpu_env, cpu_tmp0); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - /* I/O ops in icount mode must end the TB */ - dc->base.is_jmp = DISAS_EXIT; - } - break; - case 9: // cwp - gen_helper_wrcwp(cpu_env, cpu_tmp0); - break; - case 10: // cansave - tcg_gen_st32_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, - cansave)); - break; - case 11: // canrestore - tcg_gen_st32_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, - canrestore)); - break; - case 12: // cleanwin - tcg_gen_st32_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, - cleanwin)); - break; - case 13: // otherwin - tcg_gen_st32_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, - otherwin)); - break; - case 14: // wstate - tcg_gen_st32_tl(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, - wstate)); - break; - case 16: // UA2005 gl - CHECK_IU_FEATURE(dc, GL); - gen_helper_wrgl(cpu_env, cpu_tmp0); - break; - case 26: // UA2005 strand status - CHECK_IU_FEATURE(dc, HYPV); - if (!hypervisor(dc)) - goto priv_insn; - tcg_gen_mov_tl(cpu_ssr, cpu_tmp0); - break; - default: - goto illegal_insn; - } -#else - tcg_gen_trunc_tl_i32(cpu_wim, cpu_tmp0); - if (dc->def->nwindows != 32) { - tcg_gen_andi_tl(cpu_wim, cpu_wim, - (1 << dc->def->nwindows) - 1); - } -#endif - } - break; - case 0x33: /* wrtbr, UA2005 wrhpr */ - { -#ifndef TARGET_SPARC64 - if (!supervisor(dc)) - goto priv_insn; - tcg_gen_xor_tl(cpu_tbr, cpu_src1, cpu_src2); -#else - CHECK_IU_FEATURE(dc, HYPV); - if (!hypervisor(dc)) - goto priv_insn; - cpu_tmp0 = get_temp_tl(dc); - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); - switch (rd) { - case 0: // hpstate - tcg_gen_st_i64(cpu_tmp0, cpu_env, - offsetof(CPUSPARCState, - hpstate)); - save_state(dc); - gen_op_next_insn(); - tcg_gen_exit_tb(NULL, 0); - dc->base.is_jmp = DISAS_NORETURN; - break; - case 1: // htstate - // XXX gen_op_wrhtstate(); - break; - case 3: // hintp - tcg_gen_mov_tl(cpu_hintp, cpu_tmp0); - break; - case 5: // htba - tcg_gen_mov_tl(cpu_htba, cpu_tmp0); - break; - case 31: // hstick_cmpr - { - TCGv_ptr r_tickptr; - - tcg_gen_mov_tl(cpu_hstick_cmpr, cpu_tmp0); - r_tickptr = tcg_temp_new_ptr(); - tcg_gen_ld_ptr(r_tickptr, cpu_env, - offsetof(CPUSPARCState, hstick)); - if (tb_cflags(dc->base.tb) & - CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_tick_set_limit(r_tickptr, - cpu_hstick_cmpr); - tcg_temp_free_ptr(r_tickptr); - /* End TB to handle timer interrupt */ - dc->base.is_jmp = DISAS_EXIT; - } - break; - case 6: // hver readonly - default: - goto illegal_insn; - } -#endif - } - break; -#endif -#ifdef TARGET_SPARC64 - case 0x2c: /* V9 movcc */ - { - int cc = GET_FIELD_SP(insn, 11, 12); - int cond = GET_FIELD_SP(insn, 14, 17); - DisasCompare cmp; - TCGv dst; - - if (insn & (1 << 18)) { - if (cc == 0) { - gen_compare(&cmp, 0, cond, dc); - } else if (cc == 2) { - gen_compare(&cmp, 1, cond, dc); - } else { - goto illegal_insn; - } - } else { - gen_fcompare(&cmp, cc, cond); - } - - /* The get_src2 above loaded the normal 13-bit - immediate field, not the 11-bit field we have - in movcc. But it did handle the reg case. */ - if (IS_IMM) { - simm = GET_FIELD_SPs(insn, 0, 10); - tcg_gen_movi_tl(cpu_src2, simm); - } - - dst = gen_load_gpr(dc, rd); - tcg_gen_movcond_tl(cmp.cond, dst, - cmp.c1, cmp.c2, - cpu_src2, dst); - free_compare(&cmp); - gen_store_gpr(dc, rd, dst); - break; - } - case 0x2d: /* V9 sdivx */ - gen_helper_sdivx(cpu_dst, cpu_env, cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x2e: /* V9 popc */ - tcg_gen_ctpop_tl(cpu_dst, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x2f: /* V9 movr */ - { - int cond = GET_FIELD_SP(insn, 10, 12); - DisasCompare cmp; - TCGv dst; - - gen_compare_reg(&cmp, cond, cpu_src1); - - /* The get_src2 above loaded the normal 13-bit - immediate field, not the 10-bit field we have - in movr. But it did handle the reg case. */ - if (IS_IMM) { - simm = GET_FIELD_SPs(insn, 0, 9); - tcg_gen_movi_tl(cpu_src2, simm); - } - - dst = gen_load_gpr(dc, rd); - tcg_gen_movcond_tl(cmp.cond, dst, - cmp.c1, cmp.c2, - cpu_src2, dst); - free_compare(&cmp); - gen_store_gpr(dc, rd, dst); - break; - } -#endif - default: - goto illegal_insn; - } - } - } else if (xop == 0x36) { /* UltraSparc shutdown, VIS, V8 CPop1 */ -#ifdef TARGET_SPARC64 - int opf = GET_FIELD_SP(insn, 5, 13); - rs1 = GET_FIELD(insn, 13, 17); - rs2 = GET_FIELD(insn, 27, 31); - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - - switch (opf) { - case 0x000: /* VIS I edge8cc */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 1, 0); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x001: /* VIS II edge8n */ - CHECK_FPU_FEATURE(dc, VIS2); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 0, 0); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x002: /* VIS I edge8lcc */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 1, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x003: /* VIS II edge8ln */ - CHECK_FPU_FEATURE(dc, VIS2); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 0, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x004: /* VIS I edge16cc */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 1, 0); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x005: /* VIS II edge16n */ - CHECK_FPU_FEATURE(dc, VIS2); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 0, 0); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x006: /* VIS I edge16lcc */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 1, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x007: /* VIS II edge16ln */ - CHECK_FPU_FEATURE(dc, VIS2); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 0, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x008: /* VIS I edge32cc */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 1, 0); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x009: /* VIS II edge32n */ - CHECK_FPU_FEATURE(dc, VIS2); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 0, 0); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x00a: /* VIS I edge32lcc */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 1, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x00b: /* VIS II edge32ln */ - CHECK_FPU_FEATURE(dc, VIS2); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 0, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x010: /* VIS I array8 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_helper_array8(cpu_dst, cpu_src1, cpu_src2); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x012: /* VIS I array16 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_helper_array8(cpu_dst, cpu_src1, cpu_src2); - tcg_gen_shli_i64(cpu_dst, cpu_dst, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x014: /* VIS I array32 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_helper_array8(cpu_dst, cpu_src1, cpu_src2); - tcg_gen_shli_i64(cpu_dst, cpu_dst, 2); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x018: /* VIS I alignaddr */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_alignaddr(cpu_dst, cpu_src1, cpu_src2, 0); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x01a: /* VIS I alignaddrl */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_alignaddr(cpu_dst, cpu_src1, cpu_src2, 1); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x019: /* VIS II bmask */ - CHECK_FPU_FEATURE(dc, VIS2); - cpu_src1 = gen_load_gpr(dc, rs1); - cpu_src2 = gen_load_gpr(dc, rs2); - tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_src2); - tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, cpu_dst, 32, 32); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x020: /* VIS I fcmple16 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmple16(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x022: /* VIS I fcmpne16 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmpne16(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x024: /* VIS I fcmple32 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmple32(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x026: /* VIS I fcmpne32 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmpne32(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x028: /* VIS I fcmpgt16 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmpgt16(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x02a: /* VIS I fcmpeq16 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmpeq16(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x02c: /* VIS I fcmpgt32 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmpgt32(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x02e: /* VIS I fcmpeq32 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - cpu_src2_64 = gen_load_fpr_D(dc, rs2); - gen_helper_fcmpeq32(cpu_dst, cpu_src1_64, cpu_src2_64); - gen_store_gpr(dc, rd, cpu_dst); - break; - case 0x031: /* VIS I fmul8x16 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8x16); - break; - case 0x033: /* VIS I fmul8x16au */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8x16au); - break; - case 0x035: /* VIS I fmul8x16al */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8x16al); - break; - case 0x036: /* VIS I fmul8sux16 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8sux16); - break; - case 0x037: /* VIS I fmul8ulx16 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8ulx16); - break; - case 0x038: /* VIS I fmuld8sux16 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmuld8sux16); - break; - case 0x039: /* VIS I fmuld8ulx16 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmuld8ulx16); - break; - case 0x03a: /* VIS I fpack32 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpack32); - break; - case 0x03b: /* VIS I fpack16 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs2); - cpu_dst_32 = gen_dest_fpr_F(dc); - gen_helper_fpack16(cpu_dst_32, cpu_gsr, cpu_src1_64); - gen_store_fpr_F(dc, rd, cpu_dst_32); - break; - case 0x03d: /* VIS I fpackfix */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs2); - cpu_dst_32 = gen_dest_fpr_F(dc); - gen_helper_fpackfix(cpu_dst_32, cpu_gsr, cpu_src1_64); - gen_store_fpr_F(dc, rd, cpu_dst_32); - break; - case 0x03e: /* VIS I pdist */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDDD(dc, rd, rs1, rs2, gen_helper_pdist); - break; - case 0x048: /* VIS I faligndata */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_faligndata); - break; - case 0x04b: /* VIS I fpmerge */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpmerge); - break; - case 0x04c: /* VIS II bshuffle */ - CHECK_FPU_FEATURE(dc, VIS2); - gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_helper_bshuffle); - break; - case 0x04d: /* VIS I fexpand */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fexpand); - break; - case 0x050: /* VIS I fpadd16 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpadd16); - break; - case 0x051: /* VIS I fpadd16s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, gen_helper_fpadd16s); - break; - case 0x052: /* VIS I fpadd32 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpadd32); - break; - case 0x053: /* VIS I fpadd32s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_add_i32); - break; - case 0x054: /* VIS I fpsub16 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpsub16); - break; - case 0x055: /* VIS I fpsub16s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, gen_helper_fpsub16s); - break; - case 0x056: /* VIS I fpsub32 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpsub32); - break; - case 0x057: /* VIS I fpsub32s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_sub_i32); - break; - case 0x060: /* VIS I fzero */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_dst_64 = gen_dest_fpr_D(dc, rd); - tcg_gen_movi_i64(cpu_dst_64, 0); - gen_store_fpr_D(dc, rd, cpu_dst_64); - break; - case 0x061: /* VIS I fzeros */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_dst_32 = gen_dest_fpr_F(dc); - tcg_gen_movi_i32(cpu_dst_32, 0); - gen_store_fpr_F(dc, rd, cpu_dst_32); - break; - case 0x062: /* VIS I fnor */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_nor_i64); - break; - case 0x063: /* VIS I fnors */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_nor_i32); - break; - case 0x064: /* VIS I fandnot2 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_andc_i64); - break; - case 0x065: /* VIS I fandnot2s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_andc_i32); - break; - case 0x066: /* VIS I fnot2 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DD(dc, rd, rs2, tcg_gen_not_i64); - break; - case 0x067: /* VIS I fnot2s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FF(dc, rd, rs2, tcg_gen_not_i32); - break; - case 0x068: /* VIS I fandnot1 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs2, rs1, tcg_gen_andc_i64); - break; - case 0x069: /* VIS I fandnot1s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs2, rs1, tcg_gen_andc_i32); - break; - case 0x06a: /* VIS I fnot1 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DD(dc, rd, rs1, tcg_gen_not_i64); - break; - case 0x06b: /* VIS I fnot1s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FF(dc, rd, rs1, tcg_gen_not_i32); - break; - case 0x06c: /* VIS I fxor */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_xor_i64); - break; - case 0x06d: /* VIS I fxors */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_xor_i32); - break; - case 0x06e: /* VIS I fnand */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_nand_i64); - break; - case 0x06f: /* VIS I fnands */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_nand_i32); - break; - case 0x070: /* VIS I fand */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_and_i64); - break; - case 0x071: /* VIS I fands */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_and_i32); - break; - case 0x072: /* VIS I fxnor */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_eqv_i64); - break; - case 0x073: /* VIS I fxnors */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_eqv_i32); - break; - case 0x074: /* VIS I fsrc1 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs1); - gen_store_fpr_D(dc, rd, cpu_src1_64); - break; - case 0x075: /* VIS I fsrc1s */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_32 = gen_load_fpr_F(dc, rs1); - gen_store_fpr_F(dc, rd, cpu_src1_32); - break; - case 0x076: /* VIS I fornot2 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_orc_i64); - break; - case 0x077: /* VIS I fornot2s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_orc_i32); - break; - case 0x078: /* VIS I fsrc2 */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_64 = gen_load_fpr_D(dc, rs2); - gen_store_fpr_D(dc, rd, cpu_src1_64); - break; - case 0x079: /* VIS I fsrc2s */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_src1_32 = gen_load_fpr_F(dc, rs2); - gen_store_fpr_F(dc, rd, cpu_src1_32); - break; - case 0x07a: /* VIS I fornot1 */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs2, rs1, tcg_gen_orc_i64); - break; - case 0x07b: /* VIS I fornot1s */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs2, rs1, tcg_gen_orc_i32); - break; - case 0x07c: /* VIS I for */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_or_i64); - break; - case 0x07d: /* VIS I fors */ - CHECK_FPU_FEATURE(dc, VIS1); - gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_or_i32); - break; - case 0x07e: /* VIS I fone */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_dst_64 = gen_dest_fpr_D(dc, rd); - tcg_gen_movi_i64(cpu_dst_64, -1); - gen_store_fpr_D(dc, rd, cpu_dst_64); - break; - case 0x07f: /* VIS I fones */ - CHECK_FPU_FEATURE(dc, VIS1); - cpu_dst_32 = gen_dest_fpr_F(dc); - tcg_gen_movi_i32(cpu_dst_32, -1); - gen_store_fpr_F(dc, rd, cpu_dst_32); - break; - case 0x080: /* VIS I shutdown */ - case 0x081: /* VIS II siam */ - // XXX - goto illegal_insn; - default: - goto illegal_insn; - } -#else - goto ncp_insn; -#endif - } else if (xop == 0x37) { /* V8 CPop2, V9 impdep2 */ -#ifdef TARGET_SPARC64 - goto illegal_insn; -#else - goto ncp_insn; -#endif -#ifdef TARGET_SPARC64 - } else if (xop == 0x39) { /* V9 return */ - save_state(dc); - cpu_src1 = get_src1(dc, insn); - cpu_tmp0 = get_temp_tl(dc); - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 19, 31); - tcg_gen_addi_tl(cpu_tmp0, cpu_src1, simm); - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - if (rs2) { - cpu_src2 = gen_load_gpr(dc, rs2); - tcg_gen_add_tl(cpu_tmp0, cpu_src1, cpu_src2); - } else { - tcg_gen_mov_tl(cpu_tmp0, cpu_src1); - } - } - gen_helper_restore(cpu_env); - gen_mov_pc_npc(dc); - gen_check_align(cpu_tmp0, 3); - tcg_gen_mov_tl(cpu_npc, cpu_tmp0); - dc->npc = DYNAMIC_PC; - goto jmp_insn; -#endif - } else { - cpu_src1 = get_src1(dc, insn); - cpu_tmp0 = get_temp_tl(dc); - if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 19, 31); - tcg_gen_addi_tl(cpu_tmp0, cpu_src1, simm); - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - if (rs2) { - cpu_src2 = gen_load_gpr(dc, rs2); - tcg_gen_add_tl(cpu_tmp0, cpu_src1, cpu_src2); - } else { - tcg_gen_mov_tl(cpu_tmp0, cpu_src1); - } - } - switch (xop) { - case 0x38: /* jmpl */ - { - TCGv t = gen_dest_gpr(dc, rd); - tcg_gen_movi_tl(t, dc->pc); - gen_store_gpr(dc, rd, t); - - gen_mov_pc_npc(dc); - gen_check_align(cpu_tmp0, 3); - gen_address_mask(dc, cpu_tmp0); - tcg_gen_mov_tl(cpu_npc, cpu_tmp0); - dc->npc = DYNAMIC_PC; - } - goto jmp_insn; -#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) - case 0x39: /* rett, V9 return */ - { - if (!supervisor(dc)) - goto priv_insn; - gen_mov_pc_npc(dc); - gen_check_align(cpu_tmp0, 3); - tcg_gen_mov_tl(cpu_npc, cpu_tmp0); - dc->npc = DYNAMIC_PC; - gen_helper_rett(cpu_env); - } - goto jmp_insn; -#endif - case 0x3b: /* flush */ - if (!((dc)->def->features & CPU_FEATURE_FLUSH)) - goto unimp_flush; - /* nop */ - break; - case 0x3c: /* save */ - gen_helper_save(cpu_env); - gen_store_gpr(dc, rd, cpu_tmp0); - break; - case 0x3d: /* restore */ - gen_helper_restore(cpu_env); - gen_store_gpr(dc, rd, cpu_tmp0); - break; -#if !defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64) - case 0x3e: /* V9 done/retry */ - { - switch (rd) { - case 0: - if (!supervisor(dc)) - goto priv_insn; - dc->npc = DYNAMIC_PC; - dc->pc = DYNAMIC_PC; - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_done(cpu_env); - goto jmp_insn; - case 1: - if (!supervisor(dc)) - goto priv_insn; - dc->npc = DYNAMIC_PC; - dc->pc = DYNAMIC_PC; - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_retry(cpu_env); - goto jmp_insn; - default: - goto illegal_insn; - } - } - break; -#endif - default: - goto illegal_insn; - } - } - break; - } - break; - case 3: /* load/store instructions */ - { - unsigned int xop = GET_FIELD(insn, 7, 12); - /* ??? gen_address_mask prevents us from using a source - register directly. Always generate a temporary. */ - TCGv cpu_addr = get_temp_tl(dc); - - tcg_gen_mov_tl(cpu_addr, get_src1(dc, insn)); - if (xop == 0x3c || xop == 0x3e) { - /* V9 casa/casxa : no offset */ - } else if (IS_IMM) { /* immediate */ - simm = GET_FIELDs(insn, 19, 31); - if (simm != 0) { - tcg_gen_addi_tl(cpu_addr, cpu_addr, simm); - } - } else { /* register */ - rs2 = GET_FIELD(insn, 27, 31); - if (rs2 != 0) { - tcg_gen_add_tl(cpu_addr, cpu_addr, gen_load_gpr(dc, rs2)); - } - } - if (xop < 4 || (xop > 7 && xop < 0x14 && xop != 0x0e) || - (xop > 0x17 && xop <= 0x1d ) || - (xop > 0x2c && xop <= 0x33) || xop == 0x1f || xop == 0x3d) { - TCGv cpu_val = gen_dest_gpr(dc, rd); - - switch (xop) { - case 0x0: /* ld, V9 lduw, load unsigned word */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld32u(cpu_val, cpu_addr, dc->mem_idx); - break; - case 0x1: /* ldub, load unsigned byte */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld8u(cpu_val, cpu_addr, dc->mem_idx); - break; - case 0x2: /* lduh, load unsigned halfword */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld16u(cpu_val, cpu_addr, dc->mem_idx); - break; - case 0x3: /* ldd, load double word */ - if (rd & 1) - goto illegal_insn; - else { - TCGv_i64 t64; - - gen_address_mask(dc, cpu_addr); - t64 = tcg_temp_new_i64(); - tcg_gen_qemu_ld64(t64, cpu_addr, dc->mem_idx); - tcg_gen_trunc_i64_tl(cpu_val, t64); - tcg_gen_ext32u_tl(cpu_val, cpu_val); - gen_store_gpr(dc, rd + 1, cpu_val); - tcg_gen_shri_i64(t64, t64, 32); - tcg_gen_trunc_i64_tl(cpu_val, t64); - tcg_temp_free_i64(t64); - tcg_gen_ext32u_tl(cpu_val, cpu_val); - } - break; - case 0x9: /* ldsb, load signed byte */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld8s(cpu_val, cpu_addr, dc->mem_idx); - break; - case 0xa: /* ldsh, load signed halfword */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld16s(cpu_val, cpu_addr, dc->mem_idx); - break; - case 0xd: /* ldstub */ - gen_ldstub(dc, cpu_val, cpu_addr, dc->mem_idx); - break; - case 0x0f: - /* swap, swap register with memory. Also atomically */ - CHECK_IU_FEATURE(dc, SWAP); - cpu_src1 = gen_load_gpr(dc, rd); - gen_swap(dc, cpu_val, cpu_src1, cpu_addr, - dc->mem_idx, MO_TEUL); - break; -#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) - case 0x10: /* lda, V9 lduwa, load word alternate */ - gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_TEUL); - break; - case 0x11: /* lduba, load unsigned byte alternate */ - gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_UB); - break; - case 0x12: /* lduha, load unsigned halfword alternate */ - gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_TEUW); - break; - case 0x13: /* ldda, load double word alternate */ - if (rd & 1) { - goto illegal_insn; - } - gen_ldda_asi(dc, cpu_addr, insn, rd); - goto skip_move; - case 0x19: /* ldsba, load signed byte alternate */ - gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_SB); - break; - case 0x1a: /* ldsha, load signed halfword alternate */ - gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_TESW); - break; - case 0x1d: /* ldstuba -- XXX: should be atomically */ - gen_ldstub_asi(dc, cpu_val, cpu_addr, insn); - break; - case 0x1f: /* swapa, swap reg with alt. memory. Also - atomically */ - CHECK_IU_FEATURE(dc, SWAP); - cpu_src1 = gen_load_gpr(dc, rd); - gen_swap_asi(dc, cpu_val, cpu_src1, cpu_addr, insn); - break; - -#ifndef TARGET_SPARC64 - case 0x30: /* ldc */ - case 0x31: /* ldcsr */ - case 0x33: /* lddc */ - goto ncp_insn; -#endif -#endif -#ifdef TARGET_SPARC64 - case 0x08: /* V9 ldsw */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld32s(cpu_val, cpu_addr, dc->mem_idx); - break; - case 0x0b: /* V9 ldx */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld64(cpu_val, cpu_addr, dc->mem_idx); - break; - case 0x18: /* V9 ldswa */ - gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_TESL); - break; - case 0x1b: /* V9 ldxa */ - gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_TEUQ); - break; - case 0x2d: /* V9 prefetch, no effect */ - goto skip_move; - case 0x30: /* V9 ldfa */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_ldf_asi(dc, cpu_addr, insn, 4, rd); - gen_update_fprs_dirty(dc, rd); - goto skip_move; - case 0x33: /* V9 lddfa */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_ldf_asi(dc, cpu_addr, insn, 8, DFPREG(rd)); - gen_update_fprs_dirty(dc, DFPREG(rd)); - goto skip_move; - case 0x3d: /* V9 prefetcha, no effect */ - goto skip_move; - case 0x32: /* V9 ldqfa */ - CHECK_FPU_FEATURE(dc, FLOAT128); - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_ldf_asi(dc, cpu_addr, insn, 16, QFPREG(rd)); - gen_update_fprs_dirty(dc, QFPREG(rd)); - goto skip_move; -#endif - default: - goto illegal_insn; - } - gen_store_gpr(dc, rd, cpu_val); -#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) - skip_move: ; -#endif - } else if (xop >= 0x20 && xop < 0x24) { - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - switch (xop) { - case 0x20: /* ldf, load fpreg */ - gen_address_mask(dc, cpu_addr); - cpu_dst_32 = gen_dest_fpr_F(dc); - tcg_gen_qemu_ld_i32(cpu_dst_32, cpu_addr, - dc->mem_idx, MO_TEUL); - gen_store_fpr_F(dc, rd, cpu_dst_32); - break; - case 0x21: /* ldfsr, V9 ldxfsr */ -#ifdef TARGET_SPARC64 - gen_address_mask(dc, cpu_addr); - if (rd == 1) { - TCGv_i64 t64 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(t64, cpu_addr, - dc->mem_idx, MO_TEUQ); - gen_helper_ldxfsr(cpu_fsr, cpu_env, cpu_fsr, t64); - tcg_temp_free_i64(t64); - break; - } -#endif - cpu_dst_32 = get_temp_i32(dc); - tcg_gen_qemu_ld_i32(cpu_dst_32, cpu_addr, - dc->mem_idx, MO_TEUL); - gen_helper_ldfsr(cpu_fsr, cpu_env, cpu_fsr, cpu_dst_32); - break; - case 0x22: /* ldqf, load quad fpreg */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_address_mask(dc, cpu_addr); - cpu_src1_64 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(cpu_src1_64, cpu_addr, dc->mem_idx, - MO_TEUQ | MO_ALIGN_4); - tcg_gen_addi_tl(cpu_addr, cpu_addr, 8); - cpu_src2_64 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(cpu_src2_64, cpu_addr, dc->mem_idx, - MO_TEUQ | MO_ALIGN_4); - gen_store_fpr_Q(dc, rd, cpu_src1_64, cpu_src2_64); - tcg_temp_free_i64(cpu_src1_64); - tcg_temp_free_i64(cpu_src2_64); - break; - case 0x23: /* lddf, load double fpreg */ - gen_address_mask(dc, cpu_addr); - cpu_dst_64 = gen_dest_fpr_D(dc, rd); - tcg_gen_qemu_ld_i64(cpu_dst_64, cpu_addr, dc->mem_idx, - MO_TEUQ | MO_ALIGN_4); - gen_store_fpr_D(dc, rd, cpu_dst_64); - break; - default: - goto illegal_insn; - } - } else if (xop < 8 || (xop >= 0x14 && xop < 0x18) || - xop == 0xe || xop == 0x1e) { - TCGv cpu_val = gen_load_gpr(dc, rd); - - switch (xop) { - case 0x4: /* st, store word */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_st32(cpu_val, cpu_addr, dc->mem_idx); - break; - case 0x5: /* stb, store byte */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_st8(cpu_val, cpu_addr, dc->mem_idx); - break; - case 0x6: /* sth, store halfword */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_st16(cpu_val, cpu_addr, dc->mem_idx); - break; - case 0x7: /* std, store double word */ - if (rd & 1) - goto illegal_insn; - else { - TCGv_i64 t64; - TCGv lo; - - gen_address_mask(dc, cpu_addr); - lo = gen_load_gpr(dc, rd + 1); - t64 = tcg_temp_new_i64(); - tcg_gen_concat_tl_i64(t64, lo, cpu_val); - tcg_gen_qemu_st64(t64, cpu_addr, dc->mem_idx); - tcg_temp_free_i64(t64); - } - break; -#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) - case 0x14: /* sta, V9 stwa, store word alternate */ - gen_st_asi(dc, cpu_val, cpu_addr, insn, MO_TEUL); - break; - case 0x15: /* stba, store byte alternate */ - gen_st_asi(dc, cpu_val, cpu_addr, insn, MO_UB); - break; - case 0x16: /* stha, store halfword alternate */ - gen_st_asi(dc, cpu_val, cpu_addr, insn, MO_TEUW); - break; - case 0x17: /* stda, store double word alternate */ - if (rd & 1) { - goto illegal_insn; - } - gen_stda_asi(dc, cpu_val, cpu_addr, insn, rd); - break; -#endif -#ifdef TARGET_SPARC64 - case 0x0e: /* V9 stx */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_st64(cpu_val, cpu_addr, dc->mem_idx); - break; - case 0x1e: /* V9 stxa */ - gen_st_asi(dc, cpu_val, cpu_addr, insn, MO_TEUQ); - break; -#endif - default: - goto illegal_insn; - } - } else if (xop > 0x23 && xop < 0x28) { - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - switch (xop) { - case 0x24: /* stf, store fpreg */ - gen_address_mask(dc, cpu_addr); - cpu_src1_32 = gen_load_fpr_F(dc, rd); - tcg_gen_qemu_st_i32(cpu_src1_32, cpu_addr, - dc->mem_idx, MO_TEUL); - break; - case 0x25: /* stfsr, V9 stxfsr */ - { -#ifdef TARGET_SPARC64 - gen_address_mask(dc, cpu_addr); - if (rd == 1) { - tcg_gen_qemu_st64(cpu_fsr, cpu_addr, dc->mem_idx); - break; - } -#endif - tcg_gen_qemu_st32(cpu_fsr, cpu_addr, dc->mem_idx); - } - break; - case 0x26: -#ifdef TARGET_SPARC64 - /* V9 stqf, store quad fpreg */ - CHECK_FPU_FEATURE(dc, FLOAT128); - gen_address_mask(dc, cpu_addr); - /* ??? While stqf only requires 4-byte alignment, it is - legal for the cpu to signal the unaligned exception. - The OS trap handler is then required to fix it up. - For qemu, this avoids having to probe the second page - before performing the first write. */ - cpu_src1_64 = gen_load_fpr_Q0(dc, rd); - tcg_gen_qemu_st_i64(cpu_src1_64, cpu_addr, - dc->mem_idx, MO_TEUQ | MO_ALIGN_16); - tcg_gen_addi_tl(cpu_addr, cpu_addr, 8); - cpu_src2_64 = gen_load_fpr_Q1(dc, rd); - tcg_gen_qemu_st_i64(cpu_src1_64, cpu_addr, - dc->mem_idx, MO_TEUQ); - break; -#else /* !TARGET_SPARC64 */ - /* stdfq, store floating point queue */ -#if defined(CONFIG_USER_ONLY) - goto illegal_insn; -#else - if (!supervisor(dc)) - goto priv_insn; - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - goto nfq_insn; -#endif -#endif - case 0x27: /* stdf, store double fpreg */ - gen_address_mask(dc, cpu_addr); - cpu_src1_64 = gen_load_fpr_D(dc, rd); - tcg_gen_qemu_st_i64(cpu_src1_64, cpu_addr, dc->mem_idx, - MO_TEUQ | MO_ALIGN_4); - break; - default: - goto illegal_insn; - } - } else if (xop > 0x33 && xop < 0x3f) { - switch (xop) { -#ifdef TARGET_SPARC64 - case 0x34: /* V9 stfa */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_stf_asi(dc, cpu_addr, insn, 4, rd); - break; - case 0x36: /* V9 stqfa */ - { - CHECK_FPU_FEATURE(dc, FLOAT128); - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_stf_asi(dc, cpu_addr, insn, 16, QFPREG(rd)); - } - break; - case 0x37: /* V9 stdfa */ - if (gen_trap_ifnofpu(dc)) { - goto jmp_insn; - } - gen_stf_asi(dc, cpu_addr, insn, 8, DFPREG(rd)); - break; - case 0x3e: /* V9 casxa */ - rs2 = GET_FIELD(insn, 27, 31); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_casx_asi(dc, cpu_addr, cpu_src2, insn, rd); - break; -#else - case 0x34: /* stc */ - case 0x35: /* stcsr */ - case 0x36: /* stdcq */ - case 0x37: /* stdc */ - goto ncp_insn; -#endif -#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) - case 0x3c: /* V9 or LEON3 casa */ -#ifndef TARGET_SPARC64 - CHECK_IU_FEATURE(dc, CASA); -#endif - rs2 = GET_FIELD(insn, 27, 31); - cpu_src2 = gen_load_gpr(dc, rs2); - gen_cas_asi(dc, cpu_addr, cpu_src2, insn, rd); - break; -#endif - default: - goto illegal_insn; - } - } else { - goto illegal_insn; - } - } - break; + /* Reject 64-bit shifts for sparc32. */ + if (avail_32(dc) && a->x) { + return false; } - /* default case for non jump instructions */ - if (dc->npc == DYNAMIC_PC) { - dc->pc = DYNAMIC_PC; - gen_op_next_insn(); - } else if (dc->npc == JUMP_PC) { - /* we can do a static jump */ - gen_branch2(dc, dc->jump_pc[0], dc->jump_pc[1], cpu_cond); - dc->base.is_jmp = DISAS_NORETURN; + + src2 = tcg_temp_new(); + tcg_gen_andi_tl(src2, gen_load_gpr(dc, a->rs2), a->x ? 63 : 31); + src1 = gen_load_gpr(dc, a->rs1); + dst = gen_dest_gpr(dc, a->rd); + + if (l) { + tcg_gen_shl_tl(dst, src1, src2); + if (!a->x) { + tcg_gen_ext32u_tl(dst, dst); + } + } else if (u) { + if (!a->x) { + tcg_gen_ext32u_tl(dst, src1); + src1 = dst; + } + tcg_gen_shr_tl(dst, src1, src2); } else { - dc->pc = dc->npc; - dc->npc = dc->npc + 4; - } - jmp_insn: - goto egress; - illegal_insn: - gen_exception(dc, TT_ILL_INSN); - goto egress; - unimp_flush: - gen_exception(dc, TT_UNIMP_FLUSH); - goto egress; -#if !defined(CONFIG_USER_ONLY) - priv_insn: - gen_exception(dc, TT_PRIV_INSN); - goto egress; -#endif - nfpu_insn: - gen_op_fpexception_im(dc, FSR_FTT_UNIMPFPOP); - goto egress; -#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) - nfq_insn: - gen_op_fpexception_im(dc, FSR_FTT_SEQ_ERROR); - goto egress; -#endif -#ifndef TARGET_SPARC64 - ncp_insn: - gen_exception(dc, TT_NCP_INSN); - goto egress; -#endif - egress: - if (dc->n_t32 != 0) { - int i; - for (i = dc->n_t32 - 1; i >= 0; --i) { - tcg_temp_free_i32(dc->t32[i]); + if (!a->x) { + tcg_gen_ext32s_tl(dst, src1); + src1 = dst; } - dc->n_t32 = 0; + tcg_gen_sar_tl(dst, src1, src2); } - if (dc->n_ttl != 0) { - int i; - for (i = dc->n_ttl - 1; i >= 0; --i) { - tcg_temp_free(dc->ttl[i]); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(SLL_r, ALL, do_shift_r, a, true, true) +TRANS(SRL_r, ALL, do_shift_r, a, false, true) +TRANS(SRA_r, ALL, do_shift_r, a, false, false) + +static bool do_shift_i(DisasContext *dc, arg_shifti *a, bool l, bool u) +{ + TCGv dst, src1; + + /* Reject 64-bit shifts for sparc32. */ + if (avail_32(dc) && (a->x || a->i >= 32)) { + return false; + } + + src1 = gen_load_gpr(dc, a->rs1); + dst = gen_dest_gpr(dc, a->rd); + + if (avail_32(dc) || a->x) { + if (l) { + tcg_gen_shli_tl(dst, src1, a->i); + } else if (u) { + tcg_gen_shri_tl(dst, src1, a->i); + } else { + tcg_gen_sari_tl(dst, src1, a->i); } - dc->n_ttl = 0; + } else { + if (l) { + tcg_gen_deposit_z_tl(dst, src1, a->i, 32 - a->i); + } else if (u) { + tcg_gen_extract_tl(dst, src1, a->i, 32 - a->i); + } else { + tcg_gen_sextract_tl(dst, src1, a->i, 32 - a->i); + } + } + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(SLL_i, ALL, do_shift_i, a, true, true) +TRANS(SRL_i, ALL, do_shift_i, a, false, true) +TRANS(SRA_i, ALL, do_shift_i, a, false, false) + +static TCGv gen_rs2_or_imm(DisasContext *dc, bool imm, int rs2_or_imm) +{ + /* For simplicity, we under-decoded the rs2 form. */ + if (!imm && rs2_or_imm & ~0x1f) { + return NULL; + } + if (imm || rs2_or_imm == 0) { + return tcg_constant_tl(rs2_or_imm); + } else { + return cpu_regs[rs2_or_imm]; } } +static bool do_mov_cond(DisasContext *dc, DisasCompare *cmp, int rd, TCGv src2) +{ + TCGv dst = gen_load_gpr(dc, rd); + TCGv c2 = tcg_constant_tl(cmp->c2); + + tcg_gen_movcond_tl(cmp->cond, dst, cmp->c1, c2, src2, dst); + gen_store_gpr(dc, rd, dst); + return advance_pc(dc); +} + +static bool trans_MOVcc(DisasContext *dc, arg_MOVcc *a) +{ + TCGv src2 = gen_rs2_or_imm(dc, a->imm, a->rs2_or_imm); + DisasCompare cmp; + + if (src2 == NULL) { + return false; + } + gen_compare(&cmp, a->cc, a->cond, dc); + return do_mov_cond(dc, &cmp, a->rd, src2); +} + +static bool trans_MOVfcc(DisasContext *dc, arg_MOVfcc *a) +{ + TCGv src2 = gen_rs2_or_imm(dc, a->imm, a->rs2_or_imm); + DisasCompare cmp; + + if (src2 == NULL) { + return false; + } + gen_fcompare(&cmp, a->cc, a->cond); + return do_mov_cond(dc, &cmp, a->rd, src2); +} + +static bool trans_MOVR(DisasContext *dc, arg_MOVR *a) +{ + TCGv src2 = gen_rs2_or_imm(dc, a->imm, a->rs2_or_imm); + DisasCompare cmp; + + if (src2 == NULL) { + return false; + } + if (!gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1))) { + return false; + } + return do_mov_cond(dc, &cmp, a->rd, src2); +} + +static bool do_add_special(DisasContext *dc, arg_r_r_ri *a, + bool (*func)(DisasContext *dc, int rd, TCGv src)) +{ + TCGv src1, sum; + + /* For simplicity, we under-decoded the rs2 form. */ + if (!a->imm && a->rs2_or_imm & ~0x1f) { + return false; + } + + /* + * Always load the sum into a new temporary. + * This is required to capture the value across a window change, + * e.g. SAVE and RESTORE, and may be optimized away otherwise. + */ + sum = tcg_temp_new(); + src1 = gen_load_gpr(dc, a->rs1); + if (a->imm || a->rs2_or_imm == 0) { + tcg_gen_addi_tl(sum, src1, a->rs2_or_imm); + } else { + tcg_gen_add_tl(sum, src1, cpu_regs[a->rs2_or_imm]); + } + return func(dc, a->rd, sum); +} + +static bool do_jmpl(DisasContext *dc, int rd, TCGv src) +{ + /* + * Preserve pc across advance, so that we can delay + * the writeback to rd until after src is consumed. + */ + target_ulong cur_pc = dc->pc; + + gen_check_align(dc, src, 3); + + gen_mov_pc_npc(dc); + tcg_gen_mov_tl(cpu_npc, src); + gen_address_mask(dc, cpu_npc); + gen_store_gpr(dc, rd, tcg_constant_tl(cur_pc)); + + dc->npc = DYNAMIC_PC_LOOKUP; + return true; +} + +TRANS(JMPL, ALL, do_add_special, a, do_jmpl) + +static bool do_rett(DisasContext *dc, int rd, TCGv src) +{ + if (!supervisor(dc)) { + return raise_priv(dc); + } + + gen_check_align(dc, src, 3); + + gen_mov_pc_npc(dc); + tcg_gen_mov_tl(cpu_npc, src); + gen_helper_rett(tcg_env); + + dc->npc = DYNAMIC_PC; + return true; +} + +TRANS(RETT, 32, do_add_special, a, do_rett) + +static bool do_return(DisasContext *dc, int rd, TCGv src) +{ + gen_check_align(dc, src, 3); + gen_helper_restore(tcg_env); + + gen_mov_pc_npc(dc); + tcg_gen_mov_tl(cpu_npc, src); + gen_address_mask(dc, cpu_npc); + + dc->npc = DYNAMIC_PC_LOOKUP; + return true; +} + +TRANS(RETURN, 64, do_add_special, a, do_return) + +static bool do_save(DisasContext *dc, int rd, TCGv src) +{ + gen_helper_save(tcg_env); + gen_store_gpr(dc, rd, src); + return advance_pc(dc); +} + +TRANS(SAVE, ALL, do_add_special, a, do_save) + +static bool do_restore(DisasContext *dc, int rd, TCGv src) +{ + gen_helper_restore(tcg_env); + gen_store_gpr(dc, rd, src); + return advance_pc(dc); +} + +TRANS(RESTORE, ALL, do_add_special, a, do_restore) + +static bool do_done_retry(DisasContext *dc, bool done) +{ + if (!supervisor(dc)) { + return raise_priv(dc); + } + dc->npc = DYNAMIC_PC; + dc->pc = DYNAMIC_PC; + translator_io_start(&dc->base); + if (done) { + gen_helper_done(tcg_env); + } else { + gen_helper_retry(tcg_env); + } + return true; +} + +TRANS(DONE, 64, do_done_retry, true) +TRANS(RETRY, 64, do_done_retry, false) + +/* + * Major opcode 11 -- load and store instructions + */ + +static TCGv gen_ldst_addr(DisasContext *dc, int rs1, bool imm, int rs2_or_imm) +{ + TCGv addr, tmp = NULL; + + /* For simplicity, we under-decoded the rs2 form. */ + if (!imm && rs2_or_imm & ~0x1f) { + return NULL; + } + + addr = gen_load_gpr(dc, rs1); + if (rs2_or_imm) { + tmp = tcg_temp_new(); + if (imm) { + tcg_gen_addi_tl(tmp, addr, rs2_or_imm); + } else { + tcg_gen_add_tl(tmp, addr, cpu_regs[rs2_or_imm]); + } + addr = tmp; + } + if (AM_CHECK(dc)) { + if (!tmp) { + tmp = tcg_temp_new(); + } + tcg_gen_ext32u_tl(tmp, addr); + addr = tmp; + } + return addr; +} + +static bool do_ld_gpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp mop) +{ + TCGv reg, addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + DisasASI da; + + if (addr == NULL) { + return false; + } + da = resolve_asi(dc, a->asi, mop); + + reg = gen_dest_gpr(dc, a->rd); + gen_ld_asi(dc, &da, reg, addr); + gen_store_gpr(dc, a->rd, reg); + return advance_pc(dc); +} + +TRANS(LDUW, ALL, do_ld_gpr, a, MO_TEUL) +TRANS(LDUB, ALL, do_ld_gpr, a, MO_UB) +TRANS(LDUH, ALL, do_ld_gpr, a, MO_TEUW) +TRANS(LDSB, ALL, do_ld_gpr, a, MO_SB) +TRANS(LDSH, ALL, do_ld_gpr, a, MO_TESW) +TRANS(LDSW, 64, do_ld_gpr, a, MO_TESL) +TRANS(LDX, 64, do_ld_gpr, a, MO_TEUQ) + +static bool do_st_gpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp mop) +{ + TCGv reg, addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + DisasASI da; + + if (addr == NULL) { + return false; + } + da = resolve_asi(dc, a->asi, mop); + + reg = gen_load_gpr(dc, a->rd); + gen_st_asi(dc, &da, reg, addr); + return advance_pc(dc); +} + +TRANS(STW, ALL, do_st_gpr, a, MO_TEUL) +TRANS(STB, ALL, do_st_gpr, a, MO_UB) +TRANS(STH, ALL, do_st_gpr, a, MO_TEUW) +TRANS(STX, 64, do_st_gpr, a, MO_TEUQ) + +static bool trans_LDD(DisasContext *dc, arg_r_r_ri_asi *a) +{ + TCGv addr; + DisasASI da; + + if (a->rd & 1) { + return false; + } + addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + if (addr == NULL) { + return false; + } + da = resolve_asi(dc, a->asi, MO_TEUQ); + gen_ldda_asi(dc, &da, addr, a->rd); + return advance_pc(dc); +} + +static bool trans_STD(DisasContext *dc, arg_r_r_ri_asi *a) +{ + TCGv addr; + DisasASI da; + + if (a->rd & 1) { + return false; + } + addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + if (addr == NULL) { + return false; + } + da = resolve_asi(dc, a->asi, MO_TEUQ); + gen_stda_asi(dc, &da, addr, a->rd); + return advance_pc(dc); +} + +static bool trans_LDSTUB(DisasContext *dc, arg_r_r_ri_asi *a) +{ + TCGv addr, reg; + DisasASI da; + + addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + if (addr == NULL) { + return false; + } + da = resolve_asi(dc, a->asi, MO_UB); + + reg = gen_dest_gpr(dc, a->rd); + gen_ldstub_asi(dc, &da, reg, addr); + gen_store_gpr(dc, a->rd, reg); + return advance_pc(dc); +} + +static bool trans_SWAP(DisasContext *dc, arg_r_r_ri_asi *a) +{ + TCGv addr, dst, src; + DisasASI da; + + addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + if (addr == NULL) { + return false; + } + da = resolve_asi(dc, a->asi, MO_TEUL); + + dst = gen_dest_gpr(dc, a->rd); + src = gen_load_gpr(dc, a->rd); + gen_swap_asi(dc, &da, dst, src, addr); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +static bool do_casa(DisasContext *dc, arg_r_r_ri_asi *a, MemOp mop) +{ + TCGv addr, o, n, c; + DisasASI da; + + addr = gen_ldst_addr(dc, a->rs1, true, 0); + if (addr == NULL) { + return false; + } + da = resolve_asi(dc, a->asi, mop); + + o = gen_dest_gpr(dc, a->rd); + n = gen_load_gpr(dc, a->rd); + c = gen_load_gpr(dc, a->rs2_or_imm); + gen_cas_asi(dc, &da, o, n, c, addr); + gen_store_gpr(dc, a->rd, o); + return advance_pc(dc); +} + +TRANS(CASA, CASA, do_casa, a, MO_TEUL) +TRANS(CASXA, 64, do_casa, a, MO_TEUQ) + +static bool do_ld_fpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp sz) +{ + TCGv addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + DisasASI da; + + if (addr == NULL) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (sz == MO_128 && gen_trap_float128(dc)) { + return true; + } + da = resolve_asi(dc, a->asi, MO_TE | sz); + gen_ldf_asi(dc, &da, sz, addr, a->rd); + gen_update_fprs_dirty(dc, a->rd); + return advance_pc(dc); +} + +TRANS(LDF, ALL, do_ld_fpr, a, MO_32) +TRANS(LDDF, ALL, do_ld_fpr, a, MO_64) +TRANS(LDQF, ALL, do_ld_fpr, a, MO_128) + +TRANS(LDFA, 64, do_ld_fpr, a, MO_32) +TRANS(LDDFA, 64, do_ld_fpr, a, MO_64) +TRANS(LDQFA, 64, do_ld_fpr, a, MO_128) + +static bool do_st_fpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp sz) +{ + TCGv addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + DisasASI da; + + if (addr == NULL) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (sz == MO_128 && gen_trap_float128(dc)) { + return true; + } + da = resolve_asi(dc, a->asi, MO_TE | sz); + gen_stf_asi(dc, &da, sz, addr, a->rd); + return advance_pc(dc); +} + +TRANS(STF, ALL, do_st_fpr, a, MO_32) +TRANS(STDF, ALL, do_st_fpr, a, MO_64) +TRANS(STQF, ALL, do_st_fpr, a, MO_128) + +TRANS(STFA, 64, do_st_fpr, a, MO_32) +TRANS(STDFA, 64, do_st_fpr, a, MO_64) +TRANS(STQFA, 64, do_st_fpr, a, MO_128) + +static bool trans_STDFQ(DisasContext *dc, arg_STDFQ *a) +{ + if (!avail_32(dc)) { + return false; + } + if (!supervisor(dc)) { + return raise_priv(dc); + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + gen_op_fpexception_im(dc, FSR_FTT_SEQ_ERROR); + return true; +} + +static bool trans_LDFSR(DisasContext *dc, arg_r_r_ri *a) +{ + TCGv addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + TCGv_i32 tmp; + + if (addr == NULL) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + + tmp = tcg_temp_new_i32(); + tcg_gen_qemu_ld_i32(tmp, addr, dc->mem_idx, MO_TEUL | MO_ALIGN); + + tcg_gen_extract_i32(cpu_fcc[0], tmp, FSR_FCC0_SHIFT, 2); + /* LDFSR does not change FCC[1-3]. */ + + gen_helper_set_fsr_nofcc_noftt(tcg_env, tmp); + return advance_pc(dc); +} + +static bool trans_LDXFSR(DisasContext *dc, arg_r_r_ri *a) +{ +#ifdef TARGET_SPARC64 + TCGv addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + TCGv_i64 t64; + TCGv_i32 lo, hi; + + if (addr == NULL) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + + t64 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(t64, addr, dc->mem_idx, MO_TEUQ | MO_ALIGN); + + lo = tcg_temp_new_i32(); + hi = cpu_fcc[3]; + tcg_gen_extr_i64_i32(lo, hi, t64); + tcg_gen_extract_i32(cpu_fcc[0], lo, FSR_FCC0_SHIFT, 2); + tcg_gen_extract_i32(cpu_fcc[1], hi, FSR_FCC1_SHIFT - 32, 2); + tcg_gen_extract_i32(cpu_fcc[2], hi, FSR_FCC2_SHIFT - 32, 2); + tcg_gen_extract_i32(cpu_fcc[3], hi, FSR_FCC3_SHIFT - 32, 2); + + gen_helper_set_fsr_nofcc_noftt(tcg_env, lo); + return advance_pc(dc); +#else + return false; +#endif +} + +static bool do_stfsr(DisasContext *dc, arg_r_r_ri *a, MemOp mop) +{ + TCGv addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + TCGv fsr; + + if (addr == NULL) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + + fsr = tcg_temp_new(); + gen_helper_get_fsr(fsr, tcg_env); + tcg_gen_qemu_st_tl(fsr, addr, dc->mem_idx, mop | MO_ALIGN); + return advance_pc(dc); +} + +TRANS(STFSR, ALL, do_stfsr, a, MO_TEUL) +TRANS(STXFSR, 64, do_stfsr, a, MO_TEUQ) + +static bool do_fc(DisasContext *dc, int rd, bool c) +{ + uint64_t mask; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + if (rd & 1) { + mask = MAKE_64BIT_MASK(0, 32); + } else { + mask = MAKE_64BIT_MASK(32, 32); + } + if (c) { + tcg_gen_ori_i64(cpu_fpr[rd / 2], cpu_fpr[rd / 2], mask); + } else { + tcg_gen_andi_i64(cpu_fpr[rd / 2], cpu_fpr[rd / 2], ~mask); + } + gen_update_fprs_dirty(dc, rd); + return advance_pc(dc); +} + +TRANS(FZEROs, VIS1, do_fc, a->rd, 0) +TRANS(FONEs, VIS1, do_fc, a->rd, 1) + +static bool do_dc(DisasContext *dc, int rd, int64_t c) +{ + if (gen_trap_ifnofpu(dc)) { + return true; + } + + tcg_gen_movi_i64(cpu_fpr[rd / 2], c); + gen_update_fprs_dirty(dc, rd); + return advance_pc(dc); +} + +TRANS(FZEROd, VIS1, do_dc, a->rd, 0) +TRANS(FONEd, VIS1, do_dc, a->rd, -1) + +static bool do_ff(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i32, TCGv_i32)) +{ + TCGv_i32 tmp; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + tmp = gen_load_fpr_F(dc, a->rs); + func(tmp, tmp); + gen_store_fpr_F(dc, a->rd, tmp); + return advance_pc(dc); +} + +TRANS(FMOVs, ALL, do_ff, a, gen_op_fmovs) +TRANS(FNEGs, ALL, do_ff, a, gen_op_fnegs) +TRANS(FABSs, ALL, do_ff, a, gen_op_fabss) +TRANS(FSRCs, VIS1, do_ff, a, tcg_gen_mov_i32) +TRANS(FNOTs, VIS1, do_ff, a, tcg_gen_not_i32) + +static bool do_fd(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i32, TCGv_i64)) +{ + TCGv_i32 dst; + TCGv_i64 src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = tcg_temp_new_i32(); + src = gen_load_fpr_D(dc, a->rs); + func(dst, src); + gen_store_fpr_F(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FPACK16, VIS1, do_fd, a, gen_op_fpack16) +TRANS(FPACKFIX, VIS1, do_fd, a, gen_op_fpackfix) + +static bool do_env_ff(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i32, TCGv_env, TCGv_i32)) +{ + TCGv_i32 tmp; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + tmp = gen_load_fpr_F(dc, a->rs); + func(tmp, tcg_env, tmp); + gen_store_fpr_F(dc, a->rd, tmp); + return advance_pc(dc); +} + +TRANS(FSQRTs, ALL, do_env_ff, a, gen_helper_fsqrts) +TRANS(FiTOs, ALL, do_env_ff, a, gen_helper_fitos) +TRANS(FsTOi, ALL, do_env_ff, a, gen_helper_fstoi) + +static bool do_env_fd(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i32, TCGv_env, TCGv_i64)) +{ + TCGv_i32 dst; + TCGv_i64 src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = tcg_temp_new_i32(); + src = gen_load_fpr_D(dc, a->rs); + func(dst, tcg_env, src); + gen_store_fpr_F(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FdTOs, ALL, do_env_fd, a, gen_helper_fdtos) +TRANS(FdTOi, ALL, do_env_fd, a, gen_helper_fdtoi) +TRANS(FxTOs, 64, do_env_fd, a, gen_helper_fxtos) + +static bool do_dd(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i64, TCGv_i64)) +{ + TCGv_i64 dst, src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = gen_dest_fpr_D(dc, a->rd); + src = gen_load_fpr_D(dc, a->rs); + func(dst, src); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FMOVd, 64, do_dd, a, gen_op_fmovd) +TRANS(FNEGd, 64, do_dd, a, gen_op_fnegd) +TRANS(FABSd, 64, do_dd, a, gen_op_fabsd) +TRANS(FSRCd, VIS1, do_dd, a, tcg_gen_mov_i64) +TRANS(FNOTd, VIS1, do_dd, a, tcg_gen_not_i64) + +static bool do_env_dd(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i64, TCGv_env, TCGv_i64)) +{ + TCGv_i64 dst, src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = gen_dest_fpr_D(dc, a->rd); + src = gen_load_fpr_D(dc, a->rs); + func(dst, tcg_env, src); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FSQRTd, ALL, do_env_dd, a, gen_helper_fsqrtd) +TRANS(FxTOd, 64, do_env_dd, a, gen_helper_fxtod) +TRANS(FdTOx, 64, do_env_dd, a, gen_helper_fdtox) + +static bool do_env_df(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i64, TCGv_env, TCGv_i32)) +{ + TCGv_i64 dst; + TCGv_i32 src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = gen_dest_fpr_D(dc, a->rd); + src = gen_load_fpr_F(dc, a->rs); + func(dst, tcg_env, src); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FiTOd, ALL, do_env_df, a, gen_helper_fitod) +TRANS(FsTOd, ALL, do_env_df, a, gen_helper_fstod) +TRANS(FsTOx, 64, do_env_df, a, gen_helper_fstox) + +static bool do_qq(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i128, TCGv_i128)) +{ + TCGv_i128 t; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + t = gen_load_fpr_Q(dc, a->rs); + func(t, t); + gen_store_fpr_Q(dc, a->rd, t); + return advance_pc(dc); +} + +TRANS(FMOVq, 64, do_qq, a, tcg_gen_mov_i128) +TRANS(FNEGq, 64, do_qq, a, gen_op_fnegq) +TRANS(FABSq, 64, do_qq, a, gen_op_fabsq) + +static bool do_env_qq(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i128, TCGv_env, TCGv_i128)) +{ + TCGv_i128 t; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + t = gen_load_fpr_Q(dc, a->rs); + func(t, tcg_env, t); + gen_store_fpr_Q(dc, a->rd, t); + return advance_pc(dc); +} + +TRANS(FSQRTq, ALL, do_env_qq, a, gen_helper_fsqrtq) + +static bool do_env_fq(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i32, TCGv_env, TCGv_i128)) +{ + TCGv_i128 src; + TCGv_i32 dst; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + src = gen_load_fpr_Q(dc, a->rs); + dst = tcg_temp_new_i32(); + func(dst, tcg_env, src); + gen_store_fpr_F(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FqTOs, ALL, do_env_fq, a, gen_helper_fqtos) +TRANS(FqTOi, ALL, do_env_fq, a, gen_helper_fqtoi) + +static bool do_env_dq(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i64, TCGv_env, TCGv_i128)) +{ + TCGv_i128 src; + TCGv_i64 dst; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + src = gen_load_fpr_Q(dc, a->rs); + dst = gen_dest_fpr_D(dc, a->rd); + func(dst, tcg_env, src); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FqTOd, ALL, do_env_dq, a, gen_helper_fqtod) +TRANS(FqTOx, 64, do_env_dq, a, gen_helper_fqtox) + +static bool do_env_qf(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i128, TCGv_env, TCGv_i32)) +{ + TCGv_i32 src; + TCGv_i128 dst; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + src = gen_load_fpr_F(dc, a->rs); + dst = tcg_temp_new_i128(); + func(dst, tcg_env, src); + gen_store_fpr_Q(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FiTOq, ALL, do_env_qf, a, gen_helper_fitoq) +TRANS(FsTOq, ALL, do_env_qf, a, gen_helper_fstoq) + +static bool do_env_qd(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i128, TCGv_env, TCGv_i64)) +{ + TCGv_i64 src; + TCGv_i128 dst; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + src = gen_load_fpr_D(dc, a->rs); + dst = tcg_temp_new_i128(); + func(dst, tcg_env, src); + gen_store_fpr_Q(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FdTOq, ALL, do_env_qd, a, gen_helper_fdtoq) +TRANS(FxTOq, 64, do_env_qd, a, gen_helper_fxtoq) + +static bool do_fff(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i32, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + func(src1, src1, src2); + gen_store_fpr_F(dc, a->rd, src1); + return advance_pc(dc); +} + +TRANS(FPADD16s, VIS1, do_fff, a, tcg_gen_vec_add16_i32) +TRANS(FPADD32s, VIS1, do_fff, a, tcg_gen_add_i32) +TRANS(FPSUB16s, VIS1, do_fff, a, tcg_gen_vec_sub16_i32) +TRANS(FPSUB32s, VIS1, do_fff, a, tcg_gen_sub_i32) +TRANS(FNORs, VIS1, do_fff, a, tcg_gen_nor_i32) +TRANS(FANDNOTs, VIS1, do_fff, a, tcg_gen_andc_i32) +TRANS(FXORs, VIS1, do_fff, a, tcg_gen_xor_i32) +TRANS(FNANDs, VIS1, do_fff, a, tcg_gen_nand_i32) +TRANS(FANDs, VIS1, do_fff, a, tcg_gen_and_i32) +TRANS(FXNORs, VIS1, do_fff, a, tcg_gen_eqv_i32) +TRANS(FORNOTs, VIS1, do_fff, a, tcg_gen_orc_i32) +TRANS(FORs, VIS1, do_fff, a, tcg_gen_or_i32) + +static bool do_env_fff(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + func(src1, tcg_env, src1, src2); + gen_store_fpr_F(dc, a->rd, src1); + return advance_pc(dc); +} + +TRANS(FADDs, ALL, do_env_fff, a, gen_helper_fadds) +TRANS(FSUBs, ALL, do_env_fff, a, gen_helper_fsubs) +TRANS(FMULs, ALL, do_env_fff, a, gen_helper_fmuls) +TRANS(FDIVs, ALL, do_env_fff, a, gen_helper_fdivs) + +static bool do_ddd(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 dst, src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = gen_dest_fpr_D(dc, a->rd); + src1 = gen_load_fpr_D(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + func(dst, src1, src2); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FMUL8x16, VIS1, do_ddd, a, gen_helper_fmul8x16) +TRANS(FMUL8x16AU, VIS1, do_ddd, a, gen_helper_fmul8x16au) +TRANS(FMUL8x16AL, VIS1, do_ddd, a, gen_helper_fmul8x16al) +TRANS(FMUL8SUx16, VIS1, do_ddd, a, gen_helper_fmul8sux16) +TRANS(FMUL8ULx16, VIS1, do_ddd, a, gen_helper_fmul8ulx16) +TRANS(FMULD8SUx16, VIS1, do_ddd, a, gen_helper_fmuld8sux16) +TRANS(FMULD8ULx16, VIS1, do_ddd, a, gen_helper_fmuld8ulx16) +TRANS(FPMERGE, VIS1, do_ddd, a, gen_helper_fpmerge) +TRANS(FEXPAND, VIS1, do_ddd, a, gen_helper_fexpand) + +TRANS(FPADD16, VIS1, do_ddd, a, tcg_gen_vec_add16_i64) +TRANS(FPADD32, VIS1, do_ddd, a, tcg_gen_vec_add32_i64) +TRANS(FPSUB16, VIS1, do_ddd, a, tcg_gen_vec_sub16_i64) +TRANS(FPSUB32, VIS1, do_ddd, a, tcg_gen_vec_sub32_i64) +TRANS(FNORd, VIS1, do_ddd, a, tcg_gen_nor_i64) +TRANS(FANDNOTd, VIS1, do_ddd, a, tcg_gen_andc_i64) +TRANS(FXORd, VIS1, do_ddd, a, tcg_gen_xor_i64) +TRANS(FNANDd, VIS1, do_ddd, a, tcg_gen_nand_i64) +TRANS(FANDd, VIS1, do_ddd, a, tcg_gen_and_i64) +TRANS(FXNORd, VIS1, do_ddd, a, tcg_gen_eqv_i64) +TRANS(FORNOTd, VIS1, do_ddd, a, tcg_gen_orc_i64) +TRANS(FORd, VIS1, do_ddd, a, tcg_gen_or_i64) + +TRANS(FPACK32, VIS1, do_ddd, a, gen_op_fpack32) +TRANS(FALIGNDATAg, VIS1, do_ddd, a, gen_op_faligndata) +TRANS(BSHUFFLE, VIS2, do_ddd, a, gen_op_bshuffle) + +static bool do_rdd(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 src1, src2; + TCGv dst; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = gen_dest_gpr(dc, a->rd); + src1 = gen_load_fpr_D(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + func(dst, src1, src2); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FPCMPLE16, VIS1, do_rdd, a, gen_helper_fcmple16) +TRANS(FPCMPNE16, VIS1, do_rdd, a, gen_helper_fcmpne16) +TRANS(FPCMPGT16, VIS1, do_rdd, a, gen_helper_fcmpgt16) +TRANS(FPCMPEQ16, VIS1, do_rdd, a, gen_helper_fcmpeq16) + +TRANS(FPCMPLE32, VIS1, do_rdd, a, gen_helper_fcmple32) +TRANS(FPCMPNE32, VIS1, do_rdd, a, gen_helper_fcmpne32) +TRANS(FPCMPGT32, VIS1, do_rdd, a, gen_helper_fcmpgt32) +TRANS(FPCMPEQ32, VIS1, do_rdd, a, gen_helper_fcmpeq32) + +static bool do_env_ddd(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 dst, src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = gen_dest_fpr_D(dc, a->rd); + src1 = gen_load_fpr_D(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + func(dst, tcg_env, src1, src2); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FADDd, ALL, do_env_ddd, a, gen_helper_faddd) +TRANS(FSUBd, ALL, do_env_ddd, a, gen_helper_fsubd) +TRANS(FMULd, ALL, do_env_ddd, a, gen_helper_fmuld) +TRANS(FDIVd, ALL, do_env_ddd, a, gen_helper_fdivd) + +static bool trans_FsMULd(DisasContext *dc, arg_r_r_r *a) +{ + TCGv_i64 dst; + TCGv_i32 src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (!(dc->def->features & CPU_FEATURE_FSMULD)) { + return raise_unimpfpop(dc); + } + + dst = gen_dest_fpr_D(dc, a->rd); + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + gen_helper_fsmuld(dst, tcg_env, src1, src2); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +static bool do_dddd(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 dst, src0, src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = gen_dest_fpr_D(dc, a->rd); + src0 = gen_load_fpr_D(dc, a->rd); + src1 = gen_load_fpr_D(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + func(dst, src0, src1, src2); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(PDIST, VIS1, do_dddd, a, gen_helper_pdist) + +static bool do_env_qqq(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i128, TCGv_env, TCGv_i128, TCGv_i128)) +{ + TCGv_i128 src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + src1 = gen_load_fpr_Q(dc, a->rs1); + src2 = gen_load_fpr_Q(dc, a->rs2); + func(src1, tcg_env, src1, src2); + gen_store_fpr_Q(dc, a->rd, src1); + return advance_pc(dc); +} + +TRANS(FADDq, ALL, do_env_qqq, a, gen_helper_faddq) +TRANS(FSUBq, ALL, do_env_qqq, a, gen_helper_fsubq) +TRANS(FMULq, ALL, do_env_qqq, a, gen_helper_fmulq) +TRANS(FDIVq, ALL, do_env_qqq, a, gen_helper_fdivq) + +static bool trans_FdMULq(DisasContext *dc, arg_r_r_r *a) +{ + TCGv_i64 src1, src2; + TCGv_i128 dst; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + src1 = gen_load_fpr_D(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + dst = tcg_temp_new_i128(); + gen_helper_fdmulq(dst, tcg_env, src1, src2); + gen_store_fpr_Q(dc, a->rd, dst); + return advance_pc(dc); +} + +static bool do_fmovr(DisasContext *dc, arg_FMOVRs *a, bool is_128, + void (*func)(DisasContext *, DisasCompare *, int, int)) +{ + DisasCompare cmp; + + if (!gen_compare_reg(&cmp, a->cond, gen_load_gpr(dc, a->rs1))) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (is_128 && gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + func(dc, &cmp, a->rd, a->rs2); + return advance_pc(dc); +} + +TRANS(FMOVRs, 64, do_fmovr, a, false, gen_fmovs) +TRANS(FMOVRd, 64, do_fmovr, a, false, gen_fmovd) +TRANS(FMOVRq, 64, do_fmovr, a, true, gen_fmovq) + +static bool do_fmovcc(DisasContext *dc, arg_FMOVscc *a, bool is_128, + void (*func)(DisasContext *, DisasCompare *, int, int)) +{ + DisasCompare cmp; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (is_128 && gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + gen_compare(&cmp, a->cc, a->cond, dc); + func(dc, &cmp, a->rd, a->rs2); + return advance_pc(dc); +} + +TRANS(FMOVscc, 64, do_fmovcc, a, false, gen_fmovs) +TRANS(FMOVdcc, 64, do_fmovcc, a, false, gen_fmovd) +TRANS(FMOVqcc, 64, do_fmovcc, a, true, gen_fmovq) + +static bool do_fmovfcc(DisasContext *dc, arg_FMOVsfcc *a, bool is_128, + void (*func)(DisasContext *, DisasCompare *, int, int)) +{ + DisasCompare cmp; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (is_128 && gen_trap_float128(dc)) { + return true; + } + + gen_op_clear_ieee_excp_and_FTT(); + gen_fcompare(&cmp, a->cc, a->cond); + func(dc, &cmp, a->rd, a->rs2); + return advance_pc(dc); +} + +TRANS(FMOVsfcc, 64, do_fmovfcc, a, false, gen_fmovs) +TRANS(FMOVdfcc, 64, do_fmovfcc, a, false, gen_fmovd) +TRANS(FMOVqfcc, 64, do_fmovfcc, a, true, gen_fmovq) + +static bool do_fcmps(DisasContext *dc, arg_FCMPs *a, bool e) +{ + TCGv_i32 src1, src2; + + if (avail_32(dc) && a->cc != 0) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + if (e) { + gen_helper_fcmpes(cpu_fcc[a->cc], tcg_env, src1, src2); + } else { + gen_helper_fcmps(cpu_fcc[a->cc], tcg_env, src1, src2); + } + return advance_pc(dc); +} + +TRANS(FCMPs, ALL, do_fcmps, a, false) +TRANS(FCMPEs, ALL, do_fcmps, a, true) + +static bool do_fcmpd(DisasContext *dc, arg_FCMPd *a, bool e) +{ + TCGv_i64 src1, src2; + + if (avail_32(dc) && a->cc != 0) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + + src1 = gen_load_fpr_D(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + if (e) { + gen_helper_fcmped(cpu_fcc[a->cc], tcg_env, src1, src2); + } else { + gen_helper_fcmpd(cpu_fcc[a->cc], tcg_env, src1, src2); + } + return advance_pc(dc); +} + +TRANS(FCMPd, ALL, do_fcmpd, a, false) +TRANS(FCMPEd, ALL, do_fcmpd, a, true) + +static bool do_fcmpq(DisasContext *dc, arg_FCMPq *a, bool e) +{ + TCGv_i128 src1, src2; + + if (avail_32(dc) && a->cc != 0) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + src1 = gen_load_fpr_Q(dc, a->rs1); + src2 = gen_load_fpr_Q(dc, a->rs2); + if (e) { + gen_helper_fcmpeq(cpu_fcc[a->cc], tcg_env, src1, src2); + } else { + gen_helper_fcmpq(cpu_fcc[a->cc], tcg_env, src1, src2); + } + return advance_pc(dc); +} + +TRANS(FCMPq, ALL, do_fcmpq, a, false) +TRANS(FCMPEq, ALL, do_fcmpq, a, true) + static void sparc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUSPARCState *env = cs->env_ptr; int bound; dc->pc = dc->base.pc_first; dc->npc = (target_ulong)dc->base.tb->cs_base; - dc->cc_op = CC_OP_DYNAMIC; dc->mem_idx = dc->base.tb->flags & TB_FLAG_MMU_MASK; - dc->def = &env->def; + dc->def = &cpu_env(cs)->def; dc->fpu_enabled = tb_fpu_enabled(dc->base.tb->flags); dc->address_mask_32bit = tb_am_enabled(dc->base.tb->flags); #ifndef CONFIG_USER_ONLY @@ -5840,24 +4921,36 @@ static void sparc_tr_tb_start(DisasContextBase *db, CPUState *cs) static void sparc_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); + target_ulong npc = dc->npc; - if (dc->npc & JUMP_PC) { - assert(dc->jump_pc[1] == dc->pc + 4); - tcg_gen_insn_start(dc->pc, dc->jump_pc[0] | JUMP_PC); - } else { - tcg_gen_insn_start(dc->pc, dc->npc); + if (npc & 3) { + switch (npc) { + case JUMP_PC: + assert(dc->jump_pc[1] == dc->pc + 4); + npc = dc->jump_pc[0] | JUMP_PC; + break; + case DYNAMIC_PC: + case DYNAMIC_PC_LOOKUP: + npc = DYNAMIC_PC; + break; + default: + g_assert_not_reached(); + } } + tcg_gen_insn_start(dc->pc, npc); } static void sparc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUSPARCState *env = cs->env_ptr; unsigned int insn; - insn = translator_ldl(env, &dc->base, dc->pc); + insn = translator_ldl(cpu_env(cs), &dc->base, dc->pc); dc->base.pc_next += 4; - disas_sparc_insn(dc, insn); + + if (!decode(dc, insn)) { + gen_exception(dc, TT_ILL_INSN); + } if (dc->base.is_jmp == DISAS_NORETURN) { return; @@ -5870,19 +4963,54 @@ static void sparc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); + DisasDelayException *e, *e_next; + bool may_lookup; + + finishing_insn(dc); switch (dc->base.is_jmp) { case DISAS_NEXT: case DISAS_TOO_MANY: - if (dc->pc != DYNAMIC_PC && - (dc->npc != DYNAMIC_PC && dc->npc != JUMP_PC)) { + if (((dc->pc | dc->npc) & 3) == 0) { /* static PC and NPC: we can use direct chaining */ gen_goto_tb(dc, 0, dc->pc, dc->npc); - } else { - if (dc->pc != DYNAMIC_PC) { - tcg_gen_movi_tl(cpu_pc, dc->pc); + break; + } + + may_lookup = true; + if (dc->pc & 3) { + switch (dc->pc) { + case DYNAMIC_PC_LOOKUP: + break; + case DYNAMIC_PC: + may_lookup = false; + break; + default: + g_assert_not_reached(); } - save_npc(dc); + } else { + tcg_gen_movi_tl(cpu_pc, dc->pc); + } + + if (dc->npc & 3) { + switch (dc->npc) { + case JUMP_PC: + gen_generic_branch(dc); + break; + case DYNAMIC_PC: + may_lookup = false; + break; + case DYNAMIC_PC_LOOKUP: + break; + default: + g_assert_not_reached(); + } + } else { + tcg_gen_movi_tl(cpu_npc, dc->npc); + } + if (may_lookup) { + tcg_gen_lookup_and_goto_ptr(); + } else { tcg_gen_exit_tb(NULL, 0); } break; @@ -5899,6 +5027,19 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) default: g_assert_not_reached(); } + + for (e = dc->delay_excp_list; e ; e = e_next) { + gen_set_label(e->lab); + + tcg_gen_movi_tl(cpu_pc, e->pc); + if (e->npc % 4 == 0) { + tcg_gen_movi_tl(cpu_npc, e->npc); + } + gen_helper_raise_exception(tcg_env, e->excp); + + e_next = e->next; + g_free(e); + } } static void sparc_tr_disas_log(const DisasContextBase *dcbase, @@ -5917,11 +5058,12 @@ static const TranslatorOps sparc_tr_ops = { .disas_log = sparc_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc = {}; - translator_loop(&sparc_tr_ops, &dc.base, cs, tb, max_insns); + translator_loop(cs, tb, max_insns, pc, host_pc, &sparc_tr_ops, &dc.base); } void sparc_tcg_init(void) @@ -5941,58 +5083,50 @@ void sparc_tcg_init(void) static const struct { TCGv_i32 *ptr; int off; const char *name; } r32[] = { #ifdef TARGET_SPARC64 - { &cpu_xcc, offsetof(CPUSPARCState, xcc), "xcc" }, { &cpu_fprs, offsetof(CPUSPARCState, fprs), "fprs" }, + { &cpu_fcc[0], offsetof(CPUSPARCState, fcc[0]), "fcc0" }, + { &cpu_fcc[1], offsetof(CPUSPARCState, fcc[1]), "fcc1" }, + { &cpu_fcc[2], offsetof(CPUSPARCState, fcc[2]), "fcc2" }, + { &cpu_fcc[3], offsetof(CPUSPARCState, fcc[3]), "fcc3" }, #else - { &cpu_wim, offsetof(CPUSPARCState, wim), "wim" }, + { &cpu_fcc[0], offsetof(CPUSPARCState, fcc[0]), "fcc" }, #endif - { &cpu_cc_op, offsetof(CPUSPARCState, cc_op), "cc_op" }, - { &cpu_psr, offsetof(CPUSPARCState, psr), "psr" }, }; static const struct { TCGv *ptr; int off; const char *name; } rtl[] = { #ifdef TARGET_SPARC64 { &cpu_gsr, offsetof(CPUSPARCState, gsr), "gsr" }, - { &cpu_tick_cmpr, offsetof(CPUSPARCState, tick_cmpr), "tick_cmpr" }, - { &cpu_stick_cmpr, offsetof(CPUSPARCState, stick_cmpr), "stick_cmpr" }, - { &cpu_hstick_cmpr, offsetof(CPUSPARCState, hstick_cmpr), - "hstick_cmpr" }, - { &cpu_hintp, offsetof(CPUSPARCState, hintp), "hintp" }, - { &cpu_htba, offsetof(CPUSPARCState, htba), "htba" }, - { &cpu_hver, offsetof(CPUSPARCState, hver), "hver" }, - { &cpu_ssr, offsetof(CPUSPARCState, ssr), "ssr" }, - { &cpu_ver, offsetof(CPUSPARCState, version), "ver" }, + { &cpu_xcc_Z, offsetof(CPUSPARCState, xcc_Z), "xcc_Z" }, + { &cpu_xcc_C, offsetof(CPUSPARCState, xcc_C), "xcc_C" }, #endif + { &cpu_cc_N, offsetof(CPUSPARCState, cc_N), "cc_N" }, + { &cpu_cc_V, offsetof(CPUSPARCState, cc_V), "cc_V" }, + { &cpu_icc_Z, offsetof(CPUSPARCState, icc_Z), "icc_Z" }, + { &cpu_icc_C, offsetof(CPUSPARCState, icc_C), "icc_C" }, { &cpu_cond, offsetof(CPUSPARCState, cond), "cond" }, - { &cpu_cc_src, offsetof(CPUSPARCState, cc_src), "cc_src" }, - { &cpu_cc_src2, offsetof(CPUSPARCState, cc_src2), "cc_src2" }, - { &cpu_cc_dst, offsetof(CPUSPARCState, cc_dst), "cc_dst" }, - { &cpu_fsr, offsetof(CPUSPARCState, fsr), "fsr" }, { &cpu_pc, offsetof(CPUSPARCState, pc), "pc" }, { &cpu_npc, offsetof(CPUSPARCState, npc), "npc" }, { &cpu_y, offsetof(CPUSPARCState, y), "y" }, -#ifndef CONFIG_USER_ONLY { &cpu_tbr, offsetof(CPUSPARCState, tbr), "tbr" }, -#endif }; unsigned int i; - cpu_regwptr = tcg_global_mem_new_ptr(cpu_env, + cpu_regwptr = tcg_global_mem_new_ptr(tcg_env, offsetof(CPUSPARCState, regwptr), "regwptr"); for (i = 0; i < ARRAY_SIZE(r32); ++i) { - *r32[i].ptr = tcg_global_mem_new_i32(cpu_env, r32[i].off, r32[i].name); + *r32[i].ptr = tcg_global_mem_new_i32(tcg_env, r32[i].off, r32[i].name); } for (i = 0; i < ARRAY_SIZE(rtl); ++i) { - *rtl[i].ptr = tcg_global_mem_new(cpu_env, rtl[i].off, rtl[i].name); + *rtl[i].ptr = tcg_global_mem_new(tcg_env, rtl[i].off, rtl[i].name); } cpu_regs[0] = NULL; for (i = 1; i < 8; ++i) { - cpu_regs[i] = tcg_global_mem_new(cpu_env, + cpu_regs[i] = tcg_global_mem_new(tcg_env, offsetof(CPUSPARCState, gregs[i]), gregnames[i]); } @@ -6004,15 +5138,17 @@ void sparc_tcg_init(void) } for (i = 0; i < TARGET_DPREGS; i++) { - cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, + cpu_fpr[i] = tcg_global_mem_new_i64(tcg_env, offsetof(CPUSPARCState, fpr[i]), fregnames[i]); } } -void restore_state_to_opc(CPUSPARCState *env, TranslationBlock *tb, - target_ulong *data) +void sparc_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) { + CPUSPARCState *env = cpu_env(cs); target_ulong pc = data[0]; target_ulong npc = data[1]; diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index 3afdc6975c..7763b16c24 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -275,65 +275,6 @@ uint64_t helper_fexpand(uint64_t src1, uint64_t src2) return d.ll; } -#define VIS_HELPER(name, F) \ - uint64_t name##16(uint64_t src1, uint64_t src2) \ - { \ - VIS64 s, d; \ - \ - s.ll = src1; \ - d.ll = src2; \ - \ - d.VIS_W64(0) = F(d.VIS_W64(0), s.VIS_W64(0)); \ - d.VIS_W64(1) = F(d.VIS_W64(1), s.VIS_W64(1)); \ - d.VIS_W64(2) = F(d.VIS_W64(2), s.VIS_W64(2)); \ - d.VIS_W64(3) = F(d.VIS_W64(3), s.VIS_W64(3)); \ - \ - return d.ll; \ - } \ - \ - uint32_t name##16s(uint32_t src1, uint32_t src2) \ - { \ - VIS32 s, d; \ - \ - s.l = src1; \ - d.l = src2; \ - \ - d.VIS_W32(0) = F(d.VIS_W32(0), s.VIS_W32(0)); \ - d.VIS_W32(1) = F(d.VIS_W32(1), s.VIS_W32(1)); \ - \ - return d.l; \ - } \ - \ - uint64_t name##32(uint64_t src1, uint64_t src2) \ - { \ - VIS64 s, d; \ - \ - s.ll = src1; \ - d.ll = src2; \ - \ - d.VIS_L64(0) = F(d.VIS_L64(0), s.VIS_L64(0)); \ - d.VIS_L64(1) = F(d.VIS_L64(1), s.VIS_L64(1)); \ - \ - return d.ll; \ - } \ - \ - uint32_t name##32s(uint32_t src1, uint32_t src2) \ - { \ - VIS32 s, d; \ - \ - s.l = src1; \ - d.l = src2; \ - \ - d.l = F(d.l, s.l); \ - \ - return d.l; \ - } - -#define FADD(a, b) ((a) + (b)) -#define FSUB(a, b) ((a) - (b)) -VIS_HELPER(helper_fpadd, FADD) -VIS_HELPER(helper_fpsub, FSUB) - #define VIS_CMPHELPER(name, F) \ uint64_t name##16(uint64_t src1, uint64_t src2) \ { \ diff --git a/target/sparc/win_helper.c b/target/sparc/win_helper.c index 3a7c0ff943..b53fc9ce94 100644 --- a/target/sparc/win_helper.c +++ b/target/sparc/win_helper.c @@ -53,23 +53,47 @@ void cpu_set_cwp(CPUSPARCState *env, int new_cwp) target_ulong cpu_get_psr(CPUSPARCState *env) { - helper_compute_psr(env); + target_ulong icc = 0; + + icc |= ((int32_t)env->cc_N < 0) << PSR_NEG_SHIFT; + icc |= ((int32_t)env->cc_V < 0) << PSR_OVF_SHIFT; + icc |= ((int32_t)env->icc_Z == 0) << PSR_ZERO_SHIFT; + if (TARGET_LONG_BITS == 64) { + icc |= extract64(env->icc_C, 32, 1) << PSR_CARRY_SHIFT; + } else { + icc |= env->icc_C << PSR_CARRY_SHIFT; + } #if !defined(TARGET_SPARC64) - return env->version | (env->psr & PSR_ICC) | + return env->version | icc | (env->psref ? PSR_EF : 0) | (env->psrpil << 8) | (env->psrs ? PSR_S : 0) | (env->psrps ? PSR_PS : 0) | (env->psret ? PSR_ET : 0) | env->cwp; #else - return env->psr & PSR_ICC; + return icc; #endif } +void cpu_put_psr_icc(CPUSPARCState *env, target_ulong val) +{ + if (TARGET_LONG_BITS == 64) { + /* Do not clobber xcc.[NV] */ + env->cc_N = deposit64(env->cc_N, 0, 32, -(val & PSR_NEG)); + env->cc_V = deposit64(env->cc_V, 0, 32, -(val & PSR_OVF)); + env->icc_C = -(val & PSR_CARRY); + } else { + env->cc_N = -(val & PSR_NEG); + env->cc_V = -(val & PSR_OVF); + env->icc_C = (val >> PSR_CARRY_SHIFT) & 1; + } + env->icc_Z = ~val & PSR_ZERO; +} + void cpu_put_psr_raw(CPUSPARCState *env, target_ulong val) { - env->psr = val & PSR_ICC; + cpu_put_psr_icc(env, val); #if !defined(TARGET_SPARC64) env->psref = (val & PSR_EF) ? 1 : 0; env->psrpil = (val & PSR_PIL) >> 8; @@ -77,7 +101,6 @@ void cpu_put_psr_raw(CPUSPARCState *env, target_ulong val) env->psrps = (val & PSR_PS) ? 1 : 0; env->psret = (val & PSR_ET) ? 1 : 0; #endif - env->cc_op = CC_OP_FLAGS; #if !defined(TARGET_SPARC64) cpu_set_cwp(env, val & PSR_CWP); #endif @@ -156,9 +179,9 @@ void helper_wrpsr(CPUSPARCState *env, target_ulong new_psr) cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC()); } else { /* cpu_put_psr may trigger interrupts, hence BQL */ - qemu_mutex_lock_iothread(); + bql_lock(); cpu_put_psr(env, new_psr); - qemu_mutex_unlock_iothread(); + bql_unlock(); } } @@ -244,18 +267,29 @@ void helper_restored(CPUSPARCState *env) target_ulong cpu_get_ccr(CPUSPARCState *env) { - target_ulong psr; + target_ulong ccr = 0; - psr = cpu_get_psr(env); + ccr |= (env->icc_C >> 32) & 1; + ccr |= ((int32_t)env->cc_V < 0) << 1; + ccr |= ((int32_t)env->icc_Z == 0) << 2; + ccr |= ((int32_t)env->cc_N < 0) << 3; - return ((env->xcc >> 20) << 4) | ((psr & PSR_ICC) >> 20); + ccr |= env->xcc_C << 4; + ccr |= (env->cc_V < 0) << 5; + ccr |= (env->xcc_Z == 0) << 6; + ccr |= (env->cc_N < 0) << 7; + + return ccr; } void cpu_put_ccr(CPUSPARCState *env, target_ulong val) { - env->xcc = (val >> 4) << 20; - env->psr = (val & 0xf) << 20; - CC_OP = CC_OP_FLAGS; + env->cc_N = deposit64(-(val & 0x08), 32, 32, -(val & 0x80)); + env->cc_V = deposit64(-(val & 0x02), 32, 32, -(val & 0x20)); + env->icc_C = (uint64_t)val << 32; + env->xcc_C = (val >> 4) & 1; + env->icc_Z = ~val & 0x04; + env->xcc_Z = ~val & 0x40; } target_ulong cpu_get_cwp64(CPUSPARCState *env) @@ -373,9 +407,9 @@ void helper_wrpstate(CPUSPARCState *env, target_ulong new_state) #if !defined(CONFIG_USER_ONLY) if (cpu_interrupts_enabled(env)) { - qemu_mutex_lock_iothread(); + bql_lock(); cpu_check_irqs(env); - qemu_mutex_unlock_iothread(); + bql_unlock(); } #endif } @@ -388,9 +422,9 @@ void helper_wrpil(CPUSPARCState *env, target_ulong new_pil) env->psrpil = new_pil; if (cpu_interrupts_enabled(env)) { - qemu_mutex_lock_iothread(); + bql_lock(); cpu_check_irqs(env); - qemu_mutex_unlock_iothread(); + bql_unlock(); } #endif } @@ -417,9 +451,9 @@ void helper_done(CPUSPARCState *env) #if !defined(CONFIG_USER_ONLY) if (cpu_interrupts_enabled(env)) { - qemu_mutex_lock_iothread(); + bql_lock(); cpu_check_irqs(env); - qemu_mutex_unlock_iothread(); + bql_unlock(); } #endif } @@ -446,9 +480,9 @@ void helper_retry(CPUSPARCState *env) #if !defined(CONFIG_USER_ONLY) if (cpu_interrupts_enabled(env)) { - qemu_mutex_lock_iothread(); + bql_lock(); cpu_check_irqs(env); - qemu_mutex_unlock_iothread(); + bql_unlock(); } #endif } diff --git a/target/target-common.c b/target/target-common.c new file mode 100644 index 0000000000..903b10cfe4 --- /dev/null +++ b/target/target-common.c @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "qemu/osdep.h" + +#include "cpu.h" +#include "exec/target_page.h" + +int qemu_target_page_mask(void) +{ + return TARGET_PAGE_MASK; +} diff --git a/target/tricore/cpu-param.h b/target/tricore/cpu-param.h index 2727913047..e29d551dd6 100644 --- a/target/tricore/cpu-param.h +++ b/target/tricore/cpu-param.h @@ -12,6 +12,5 @@ #define TARGET_PAGE_BITS 14 #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -#define NB_MMU_MODES 3 #endif diff --git a/target/tricore/cpu-qom.h b/target/tricore/cpu-qom.h index ee24e9fa76..e35dc1ad2d 100644 --- a/target/tricore/cpu-qom.h +++ b/target/tricore/cpu-qom.h @@ -1,4 +1,6 @@ /* + * QEMU TriCore CPU QOM header (target agnostic) + * * Copyright (c) 2012-2014 Bastian Koppelmann C-Lab/University Paderborn * * This library is free software; you can redistribute it and/or @@ -19,21 +21,12 @@ #define QEMU_TRICORE_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" - #define TYPE_TRICORE_CPU "tricore-cpu" OBJECT_DECLARE_CPU_TYPE(TriCoreCPU, TriCoreCPUClass, TRICORE_CPU) -struct TriCoreCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceReset parent_reset; -}; - +#define TRICORE_CPU_TYPE_SUFFIX "-" TYPE_TRICORE_CPU +#define TRICORE_CPU_TYPE_NAME(model) model TRICORE_CPU_TYPE_SUFFIX #endif /* QEMU_TRICORE_CPU_QOM_H */ diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index b95682b7f0..a9af73aeb5 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -22,44 +22,52 @@ #include "cpu.h" #include "exec/exec-all.h" #include "qemu/error-report.h" +#include "tcg/debug-assert.h" static inline void set_feature(CPUTriCoreState *env, int feature) { env->features |= 1ULL << feature; } -static gchar *tricore_gdb_arch_name(CPUState *cs) +static const gchar *tricore_gdb_arch_name(CPUState *cs) { - return g_strdup("tricore"); + return "tricore"; } static void tricore_cpu_set_pc(CPUState *cs, vaddr value) { - TriCoreCPU *cpu = TRICORE_CPU(cs); - CPUTriCoreState *env = &cpu->env; + cpu_env(cs)->PC = value & ~(target_ulong)1; +} - env->PC = value & ~(target_ulong)1; +static vaddr tricore_cpu_get_pc(CPUState *cs) +{ + return cpu_env(cs)->PC; } static void tricore_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - TriCoreCPU *cpu = TRICORE_CPU(cs); - CPUTriCoreState *env = &cpu->env; - - env->PC = tb->pc; + tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + cpu_env(cs)->PC = tb->pc; } -static void tricore_cpu_reset(DeviceState *dev) +static void tricore_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) { - CPUState *s = CPU(dev); - TriCoreCPU *cpu = TRICORE_CPU(s); - TriCoreCPUClass *tcc = TRICORE_CPU_GET_CLASS(cpu); - CPUTriCoreState *env = &cpu->env; + cpu_env(cs)->PC = data[0]; +} - tcc->parent_reset(dev); +static void tricore_cpu_reset_hold(Object *obj) +{ + CPUState *cs = CPU(obj); + TriCoreCPUClass *tcc = TRICORE_CPU_GET_CLASS(obj); - cpu_state_reset(env); + if (tcc->parent_phases.hold) { + tcc->parent_phases.hold(obj); + } + + cpu_state_reset(cpu_env(cs)); } static bool tricore_cpu_has_work(CPUState *cs) @@ -67,6 +75,11 @@ static bool tricore_cpu_has_work(CPUState *cs) return true; } +static int tricore_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return 0; +} + static void tricore_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -82,14 +95,18 @@ static void tricore_cpu_realizefn(DeviceState *dev, Error **errp) } /* Some features automatically imply others */ - if (tricore_feature(env, TRICORE_FEATURE_161)) { + if (tricore_has_feature(env, TRICORE_FEATURE_162)) { + set_feature(env, TRICORE_FEATURE_161); + } + + if (tricore_has_feature(env, TRICORE_FEATURE_161)) { set_feature(env, TRICORE_FEATURE_16); } - if (tricore_feature(env, TRICORE_FEATURE_16)) { + if (tricore_has_feature(env, TRICORE_FEATURE_16)) { set_feature(env, TRICORE_FEATURE_131); } - if (tricore_feature(env, TRICORE_FEATURE_131)) { + if (tricore_has_feature(env, TRICORE_FEATURE_131)) { set_feature(env, TRICORE_FEATURE_13); } cpu_reset(cs); @@ -98,14 +115,6 @@ static void tricore_cpu_realizefn(DeviceState *dev, Error **errp) tcc->parent_realize(dev, errp); } - -static void tricore_cpu_initfn(Object *obj) -{ - TriCoreCPU *cpu = TRICORE_CPU(obj); - - cpu_set_cpustate_pointers(cpu); -} - static ObjectClass *tricore_cpu_class_by_name(const char *cpu_model) { ObjectClass *oc; @@ -114,10 +123,7 @@ static ObjectClass *tricore_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf(TRICORE_CPU_TYPE_NAME("%s"), cpu_model); oc = object_class_by_name(typename); g_free(typename); - if (!oc || !object_class_dynamic_cast(oc, TYPE_TRICORE_CPU) || - object_class_is_abstract(oc)) { - return NULL; - } + return oc; } @@ -142,6 +148,14 @@ static void tc27x_initfn(Object *obj) set_feature(&cpu->env, TRICORE_FEATURE_161); } +static void tc37x_initfn(Object *obj) +{ + TriCoreCPU *cpu = TRICORE_CPU(obj); + + set_feature(&cpu->env, TRICORE_FEATURE_162); +} + + #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps tricore_sysemu_ops = { @@ -150,9 +164,10 @@ static const struct SysemuCPUOps tricore_sysemu_ops = { #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps tricore_tcg_ops = { +static const TCGCPUOps tricore_tcg_ops = { .initialize = tricore_tcg_init, .synchronize_from_tb = tricore_cpu_synchronize_from_tb, + .restore_state_to_opc = tricore_restore_state_to_opc, .tlb_fill = tricore_cpu_tlb_fill, }; @@ -161,13 +176,16 @@ static void tricore_cpu_class_init(ObjectClass *c, void *data) TriCoreCPUClass *mcc = TRICORE_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); DeviceClass *dc = DEVICE_CLASS(c); + ResettableClass *rc = RESETTABLE_CLASS(c); device_class_set_parent_realize(dc, tricore_cpu_realizefn, &mcc->parent_realize); - device_class_set_parent_reset(dc, tricore_cpu_reset, &mcc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, tricore_cpu_reset_hold, NULL, + &mcc->parent_phases); cc->class_by_name = tricore_cpu_class_by_name; cc->has_work = tricore_cpu_has_work; + cc->mmu_index = tricore_cpu_mmu_index; cc->gdb_read_register = tricore_cpu_gdb_read_register; cc->gdb_write_register = tricore_cpu_gdb_write_register; @@ -176,6 +194,7 @@ static void tricore_cpu_class_init(ObjectClass *c, void *data) cc->dump_state = tricore_cpu_dump_state; cc->set_pc = tricore_cpu_set_pc; + cc->get_pc = tricore_cpu_get_pc; cc->sysemu_ops = &tricore_sysemu_ops; cc->tcg_ops = &tricore_tcg_ops; } @@ -192,7 +211,7 @@ static const TypeInfo tricore_cpu_type_infos[] = { .name = TYPE_TRICORE_CPU, .parent = TYPE_CPU, .instance_size = sizeof(TriCoreCPU), - .instance_init = tricore_cpu_initfn, + .instance_align = __alignof(TriCoreCPU), .abstract = true, .class_size = sizeof(TriCoreCPUClass), .class_init = tricore_cpu_class_init, @@ -200,6 +219,7 @@ static const TypeInfo tricore_cpu_type_infos[] = { DEFINE_TRICORE_CPU_TYPE("tc1796", tc1796_initfn), DEFINE_TRICORE_CPU_TYPE("tc1797", tc1797_initfn), DEFINE_TRICORE_CPU_TYPE("tc27x", tc27x_initfn), + DEFINE_TRICORE_CPU_TYPE("tc37x", tc37x_initfn), }; DEFINE_TYPES(tricore_cpu_type_infos) diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h index 3b9c533a7c..220af69fc2 100644 --- a/target/tricore/cpu.h +++ b/target/tricore/cpu.h @@ -21,174 +21,39 @@ #define TRICORE_CPU_H #include "cpu-qom.h" +#include "hw/registerfields.h" #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" #include "tricore-defs.h" -struct tricore_boot_info; - -typedef struct tricore_def_t tricore_def_t; - typedef struct CPUArchState { /* GPR Register */ uint32_t gpr_a[16]; uint32_t gpr_d[16]; - /* CSFR Register */ - uint32_t PCXI; /* Frequently accessed PSW_USB bits are stored separately for efficiency. This contains all the other bits. Use psw_{read,write} to access the whole PSW. */ uint32_t PSW; - - /* PSW flag cache for faster execution - */ + /* PSW flag cache for faster execution */ uint32_t PSW_USB_C; uint32_t PSW_USB_V; /* Only if bit 31 set, then flag is set */ uint32_t PSW_USB_SV; /* Only if bit 31 set, then flag is set */ uint32_t PSW_USB_AV; /* Only if bit 31 set, then flag is set. */ uint32_t PSW_USB_SAV; /* Only if bit 31 set, then flag is set. */ - uint32_t PC; - uint32_t SYSCON; - uint32_t CPU_ID; - uint32_t CORE_ID; - uint32_t BIV; - uint32_t BTV; - uint32_t ISP; - uint32_t ICR; - uint32_t FCX; - uint32_t LCX; - uint32_t COMPAT; +#define R(ADDR, NAME, FEATURE) uint32_t NAME; +#define A(ADDR, NAME, FEATURE) uint32_t NAME; +#define E(ADDR, NAME, FEATURE) uint32_t NAME; +#include "csfr.h.inc" +#undef R +#undef A +#undef E - /* Mem Protection Register */ - uint32_t DPR0_0L; - uint32_t DPR0_0U; - uint32_t DPR0_1L; - uint32_t DPR0_1U; - uint32_t DPR0_2L; - uint32_t DPR0_2U; - uint32_t DPR0_3L; - uint32_t DPR0_3U; - - uint32_t DPR1_0L; - uint32_t DPR1_0U; - uint32_t DPR1_1L; - uint32_t DPR1_1U; - uint32_t DPR1_2L; - uint32_t DPR1_2U; - uint32_t DPR1_3L; - uint32_t DPR1_3U; - - uint32_t DPR2_0L; - uint32_t DPR2_0U; - uint32_t DPR2_1L; - uint32_t DPR2_1U; - uint32_t DPR2_2L; - uint32_t DPR2_2U; - uint32_t DPR2_3L; - uint32_t DPR2_3U; - - uint32_t DPR3_0L; - uint32_t DPR3_0U; - uint32_t DPR3_1L; - uint32_t DPR3_1U; - uint32_t DPR3_2L; - uint32_t DPR3_2U; - uint32_t DPR3_3L; - uint32_t DPR3_3U; - - uint32_t CPR0_0L; - uint32_t CPR0_0U; - uint32_t CPR0_1L; - uint32_t CPR0_1U; - uint32_t CPR0_2L; - uint32_t CPR0_2U; - uint32_t CPR0_3L; - uint32_t CPR0_3U; - - uint32_t CPR1_0L; - uint32_t CPR1_0U; - uint32_t CPR1_1L; - uint32_t CPR1_1U; - uint32_t CPR1_2L; - uint32_t CPR1_2U; - uint32_t CPR1_3L; - uint32_t CPR1_3U; - - uint32_t CPR2_0L; - uint32_t CPR2_0U; - uint32_t CPR2_1L; - uint32_t CPR2_1U; - uint32_t CPR2_2L; - uint32_t CPR2_2U; - uint32_t CPR2_3L; - uint32_t CPR2_3U; - - uint32_t CPR3_0L; - uint32_t CPR3_0U; - uint32_t CPR3_1L; - uint32_t CPR3_1U; - uint32_t CPR3_2L; - uint32_t CPR3_2U; - uint32_t CPR3_3L; - uint32_t CPR3_3U; - - uint32_t DPM0; - uint32_t DPM1; - uint32_t DPM2; - uint32_t DPM3; - - uint32_t CPM0; - uint32_t CPM1; - uint32_t CPM2; - uint32_t CPM3; - - /* Memory Management Registers */ - uint32_t MMU_CON; - uint32_t MMU_ASI; - uint32_t MMU_TVA; - uint32_t MMU_TPA; - uint32_t MMU_TPX; - uint32_t MMU_TFA; - /* {1.3.1 only */ - uint32_t BMACON; - uint32_t SMACON; - uint32_t DIEAR; - uint32_t DIETR; - uint32_t CCDIER; - uint32_t MIECON; - uint32_t PIEAR; - uint32_t PIETR; - uint32_t CCPIER; - /*} */ - /* Debug Registers */ - uint32_t DBGSR; - uint32_t EXEVT; - uint32_t CREVT; - uint32_t SWEVT; - uint32_t TR0EVT; - uint32_t TR1EVT; - uint32_t DMS; - uint32_t DCX; - uint32_t DBGTCR; - uint32_t CCTRL; - uint32_t CCNT; - uint32_t ICNT; - uint32_t M1CNT; - uint32_t M2CNT; - uint32_t M3CNT; /* Floating Point Registers */ float_status fp_status; - /* QEMU */ - int error_code; - uint32_t hflags; /* CPU State */ /* Internal CPU feature flags. */ uint64_t features; - - const tricore_def_t *cpu_model; - void *irq[8]; - struct QEMUTimer *timer; /* Internal timer */ } CPUTriCoreState; /** @@ -198,25 +63,48 @@ typedef struct CPUArchState { * A TriCore CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUTriCoreState env; }; +struct TriCoreCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; hwaddr tricore_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); void tricore_cpu_dump_state(CPUState *cpu, FILE *f, int flags); +FIELD(PCXI, PCPN_13, 24, 8) +FIELD(PCXI, PCPN_161, 22, 8) +FIELD(PCXI, PIE_13, 23, 1) +FIELD(PCXI, PIE_161, 21, 1) +FIELD(PCXI, UL_13, 22, 1) +FIELD(PCXI, UL_161, 20, 1) +FIELD(PCXI, PCXS, 16, 4) +FIELD(PCXI, PCXO, 0, 16) +uint32_t pcxi_get_ul(CPUTriCoreState *env); +uint32_t pcxi_get_pie(CPUTriCoreState *env); +uint32_t pcxi_get_pcpn(CPUTriCoreState *env); +uint32_t pcxi_get_pcxs(CPUTriCoreState *env); +uint32_t pcxi_get_pcxo(CPUTriCoreState *env); +void pcxi_set_ul(CPUTriCoreState *env, uint32_t val); +void pcxi_set_pie(CPUTriCoreState *env, uint32_t val); +void pcxi_set_pcpn(CPUTriCoreState *env, uint32_t val); -#define MASK_PCXI_PCPN 0xff000000 -#define MASK_PCXI_PIE_1_3 0x00800000 -#define MASK_PCXI_PIE_1_6 0x00200000 -#define MASK_PCXI_UL 0x00400000 -#define MASK_PCXI_PCXS 0x000f0000 -#define MASK_PCXI_PCXO 0x0000ffff +FIELD(ICR, IE_161, 15, 1) +FIELD(ICR, IE_13, 8, 1) +FIELD(ICR, PIPN, 16, 8) +FIELD(ICR, CCPN, 0, 8) + +uint32_t icr_get_ie(CPUTriCoreState *env); +uint32_t icr_get_ccpn(CPUTriCoreState *env); + +void icr_set_ccpn(CPUTriCoreState *env, uint32_t val); +void icr_set_ie(CPUTriCoreState *env, uint32_t val); #define MASK_PSW_USB 0xff000000 #define MASK_USB_C 0x80000000 @@ -239,10 +127,6 @@ void tricore_cpu_dump_state(CPUState *cpu, FILE *f, int flags); #define MASK_CPUID_MOD_32B 0x0000ff00 #define MASK_CPUID_REV 0x000000ff -#define MASK_ICR_PIPN 0x00ff0000 -#define MASK_ICR_IE_1_3 0x00000100 -#define MASK_ICR_IE_1_6 0x00008000 -#define MASK_ICR_CCPN 0x000000ff #define MASK_FCX_FCXS 0x000f0000 #define MASK_FCX_FCXO 0x0000ffff @@ -257,19 +141,21 @@ void tricore_cpu_dump_state(CPUState *cpu, FILE *f, int flags); #define MASK_DBGSR_PEVT 0x40 #define MASK_DBGSR_EVTSRC 0x1f00 -#define TRICORE_HFLAG_KUU 0x3 -#define TRICORE_HFLAG_UM0 0x00002 /* user mode-0 flag */ -#define TRICORE_HFLAG_UM1 0x00001 /* user mode-1 flag */ -#define TRICORE_HFLAG_SM 0x00000 /* kernel mode flag */ +enum tricore_priv_levels { + TRICORE_PRIV_UM0 = 0x0, /* user mode-0 flag */ + TRICORE_PRIV_UM1 = 0x1, /* user mode-1 flag */ + TRICORE_PRIV_SM = 0x2, /* kernel mode flag */ +}; enum tricore_features { TRICORE_FEATURE_13, TRICORE_FEATURE_131, TRICORE_FEATURE_16, TRICORE_FEATURE_161, + TRICORE_FEATURE_162, }; -static inline int tricore_feature(CPUTriCoreState *env, int feature) +static inline int tricore_has_feature(CPUTriCoreState *env, int feature) { return (env->features & (1ULL << feature)) != 0; } @@ -360,30 +246,25 @@ void fpu_set_state(CPUTriCoreState *env); #define MMU_USER_IDX 2 -void tricore_cpu_list(void); - -#define cpu_list tricore_cpu_list - -static inline int cpu_mmu_index(CPUTriCoreState *env, bool ifetch) -{ - return 0; -} - #include "exec/cpu-all.h" +FIELD(TB_FLAGS, PRIV, 0, 2) + void cpu_state_reset(CPUTriCoreState *s); void tricore_tcg_init(void); -static inline void cpu_get_tb_cpu_state(CPUTriCoreState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) +static inline void cpu_get_tb_cpu_state(CPUTriCoreState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { + uint32_t new_flags = 0; *pc = env->PC; *cs_base = 0; - *flags = 0; + + new_flags |= FIELD_DP32(new_flags, TB_FLAGS, PRIV, + extract32(env->PSW, 10, 2)); + *flags = new_flags; } -#define TRICORE_CPU_TYPE_SUFFIX "-" TYPE_TRICORE_CPU -#define TRICORE_CPU_TYPE_NAME(model) model TRICORE_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_TRICORE_CPU /* helpers.c */ diff --git a/target/tricore/csfr.def b/target/tricore/csfr.h.inc similarity index 99% rename from target/tricore/csfr.def rename to target/tricore/csfr.h.inc index ff004cbddc..cdfaf1d662 100644 --- a/target/tricore/csfr.def +++ b/target/tricore/csfr.h.inc @@ -1,4 +1,4 @@ -/* A(ll) access permited +/* A(ll) access permitted R(ead only) access E(nd init protected) access diff --git a/target/tricore/fpu_helper.c b/target/tricore/fpu_helper.c index cb7ee7dd35..5d38aea143 100644 --- a/target/tricore/fpu_helper.c +++ b/target/tricore/fpu_helper.c @@ -373,6 +373,80 @@ uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg) return (uint32_t)result; } +uint32_t helper_hptof(CPUTriCoreState *env, uint32_t arg) +{ + float16 f_arg = make_float16(arg); + uint32_t result = 0; + int32_t flags = 0; + + /* + * if we have any NAN we need to move the top 2 and lower 8 input mantissa + * bits to the top 2 and lower 8 output mantissa bits respectively. + * Softfloat on the other hand uses the top 10 mantissa bits. + */ + if (float16_is_any_nan(f_arg)) { + if (float16_is_signaling_nan(f_arg, &env->fp_status)) { + flags |= float_flag_invalid; + } + result = 0; + result = float32_set_sign(result, f_arg >> 15); + result = deposit32(result, 23, 8, 0xff); + result = deposit32(result, 21, 2, extract32(f_arg, 8, 2)); + result = deposit32(result, 0, 8, extract32(f_arg, 0, 8)); + } else { + set_flush_inputs_to_zero(0, &env->fp_status); + result = float16_to_float32(f_arg, true, &env->fp_status); + set_flush_inputs_to_zero(1, &env->fp_status); + flags = f_get_excp_flags(env); + } + + if (flags) { + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + + return result; +} + +uint32_t helper_ftohp(CPUTriCoreState *env, uint32_t arg) +{ + float32 f_arg = make_float32(arg); + uint32_t result = 0; + int32_t flags = 0; + + /* + * if we have any NAN we need to move the top 2 and lower 8 input mantissa + * bits to the top 2 and lower 8 output mantissa bits respectively. + * Softfloat on the other hand uses the top 10 mantissa bits. + */ + if (float32_is_any_nan(f_arg)) { + if (float32_is_signaling_nan(f_arg, &env->fp_status)) { + flags |= float_flag_invalid; + } + result = float16_set_sign(result, arg >> 31); + result = deposit32(result, 10, 5, 0x1f); + result = deposit32(result, 8, 2, extract32(arg, 21, 2)); + result = deposit32(result, 0, 8, extract32(arg, 0, 8)); + if (extract32(result, 0, 10) == 0) { + result |= (1 << 8); + } + } else { + set_flush_to_zero(0, &env->fp_status); + result = float32_to_float16(f_arg, true, &env->fp_status); + set_flush_to_zero(1, &env->fp_status); + flags = f_get_excp_flags(env); + } + + if (flags) { + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + + return result; +} + uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg) { float32 f_result; @@ -429,6 +503,38 @@ uint32_t helper_ftoiz(CPUTriCoreState *env, uint32_t arg) return result; } +uint32_t helper_ftou(CPUTriCoreState *env, uint32_t arg) +{ + float32 f_arg = make_float32(arg); + uint32_t result; + int32_t flags = 0; + + result = float32_to_uint32(f_arg, &env->fp_status); + + flags = f_get_excp_flags(env); + if (flags & float_flag_invalid) { + flags &= ~float_flag_inexact; + if (float32_is_any_nan(f_arg)) { + result = 0; + } + /* + * we need to check arg < 0.0 before rounding as TriCore needs to raise + * float_flag_invalid as well. For instance, when we have a negative + * exponent and sign, softfloat would only raise float_flat_inexact. + */ + } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) { + flags = float_flag_invalid; + result = 0; + } + + if (flags) { + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + return result; +} + uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg) { float32 f_arg = make_float32(arg); @@ -443,6 +549,11 @@ uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg) if (float32_is_any_nan(f_arg)) { result = 0; } + /* + * we need to check arg < 0.0 before rounding as TriCore needs to raise + * float_flag_invalid as well. For instance, when we have a negative + * exponent and sign, softfloat would only raise float_flat_inexact. + */ } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) { flags = float_flag_invalid; result = 0; diff --git a/target/tricore/gdbstub.c b/target/tricore/gdbstub.c index ebf32defde..f9309c5e27 100644 --- a/target/tricore/gdbstub.c +++ b/target/tricore/gdbstub.c @@ -18,7 +18,7 @@ */ #include "qemu/osdep.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #define LCX_REGNUM 32 @@ -106,8 +106,7 @@ static void tricore_cpu_gdb_write_csfr(CPUTriCoreState *env, int n, int tricore_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - TriCoreCPU *cpu = TRICORE_CPU(cs); - CPUTriCoreState *env = &cpu->env; + CPUTriCoreState *env = cpu_env(cs); if (n < 16) { /* data registers */ return gdb_get_reg32(mem_buf, env->gpr_d[n]); @@ -121,8 +120,7 @@ int tricore_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int tricore_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - TriCoreCPU *cpu = TRICORE_CPU(cs); - CPUTriCoreState *env = &cpu->env; + CPUTriCoreState *env = cpu_env(cs); uint32_t tmp; tmp = ldl_p(mem_buf); @@ -130,7 +128,7 @@ int tricore_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) if (n < 16) { /* data registers */ env->gpr_d[n] = tmp; } else if (n < 32) { /* address registers */ - env->gpr_d[n - 16] = tmp; + env->gpr_a[n - 16] = tmp; } else { tricore_cpu_gdb_write_csfr(env, n, tmp); } diff --git a/target/tricore/helper.c b/target/tricore/helper.c index 1db32808e8..76bd226370 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "hw/registerfields.h" #include "cpu.h" #include "exec/exec-all.h" #include "fpu/softfloat-helpers.h" @@ -30,7 +31,6 @@ enum { TLBRET_MATCH = 0 }; -#if defined(CONFIG_SOFTMMU) static int get_physical_address(CPUTriCoreState *env, hwaddr *physical, int *prot, target_ulong address, MMUAccessType access_type, int mmu_idx) @@ -48,7 +48,7 @@ hwaddr tricore_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) TriCoreCPU *cpu = TRICORE_CPU(cs); hwaddr phys_addr; int prot; - int mmu_idx = cpu_mmu_index(&cpu->env, false); + int mmu_idx = cpu_mmu_index(cs, false); if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, MMU_DATA_LOAD, mmu_idx)) { @@ -56,9 +56,8 @@ hwaddr tricore_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) } return phys_addr; } -#endif -/* TODO: Add exeption support*/ +/* TODO: Add exception support */ static void raise_mmu_exception(CPUTriCoreState *env, target_ulong address, int rw, int tlb_error) { @@ -68,8 +67,7 @@ bool tricore_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType rw, int mmu_idx, bool probe, uintptr_t retaddr) { - TriCoreCPU *cpu = TRICORE_CPU(cs); - CPUTriCoreState *env = &cpu->env; + CPUTriCoreState *env = cpu_env(cs); hwaddr physical; int prot; int ret = 0; @@ -78,9 +76,9 @@ bool tricore_cpu_tlb_fill(CPUState *cs, vaddr address, int size, ret = get_physical_address(env, &physical, &prot, address, rw, mmu_idx); - qemu_log_mask(CPU_LOG_MMU, "%s address=" TARGET_FMT_lx " ret %d physical " - TARGET_FMT_plx " prot %d\n", - __func__, (target_ulong)address, ret, physical, prot); + qemu_log_mask(CPU_LOG_MMU, "%s address=0x%" VADDR_PRIx " ret %d physical " + HWADDR_FMT_plx " prot %d\n", + __func__, address, ret, physical, prot); if (ret == TLBRET_MATCH) { tlb_set_page(cs, address & TARGET_PAGE_MASK, @@ -97,40 +95,33 @@ bool tricore_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } } -static void tricore_cpu_list_entry(gpointer data, gpointer user_data) -{ - ObjectClass *oc = data; - const char *typename; - char *name; - - typename = object_class_get_name(oc); - name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_TRICORE_CPU)); - qemu_printf(" %s\n", name); - g_free(name); -} - -void tricore_cpu_list(void) -{ - GSList *list; - - list = object_class_get_list_sorted(TYPE_TRICORE_CPU, false); - qemu_printf("Available CPUs:\n"); - g_slist_foreach(list, tricore_cpu_list_entry, NULL); - g_slist_free(list); -} - void fpu_set_state(CPUTriCoreState *env) { - set_float_rounding_mode(env->PSW & MASK_PSW_FPU_RM, &env->fp_status); + switch (extract32(env->PSW, 24, 2)) { + case 0: + set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + break; + case 1: + set_float_rounding_mode(float_round_up, &env->fp_status); + break; + case 2: + set_float_rounding_mode(float_round_down, &env->fp_status); + break; + case 3: + set_float_rounding_mode(float_round_to_zero, &env->fp_status); + break; + } + set_flush_inputs_to_zero(1, &env->fp_status); set_flush_to_zero(1, &env->fp_status); + set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status); set_default_nan_mode(1, &env->fp_status); } uint32_t psw_read(CPUTriCoreState *env) { /* clear all USB bits */ - env->PSW &= 0x6ffffff; + env->PSW &= 0x7ffffff; /* now set them from the cache */ env->PSW |= ((env->PSW_USB_C != 0) << 31); env->PSW |= ((env->PSW_USB_V & (1 << 31)) >> 1); @@ -152,3 +143,47 @@ void psw_write(CPUTriCoreState *env, uint32_t val) fpu_set_state(env); } + +#define FIELD_GETTER_WITH_FEATURE(NAME, REG, FIELD, FEATURE) \ +uint32_t NAME(CPUTriCoreState *env) \ +{ \ + if (tricore_has_feature(env, TRICORE_FEATURE_##FEATURE)) { \ + return FIELD_EX32(env->REG, REG, FIELD ## _ ## FEATURE); \ + } \ + return FIELD_EX32(env->REG, REG, FIELD ## _13); \ +} + +#define FIELD_GETTER(NAME, REG, FIELD) \ +uint32_t NAME(CPUTriCoreState *env) \ +{ \ + return FIELD_EX32(env->REG, REG, FIELD); \ +} + +#define FIELD_SETTER_WITH_FEATURE(NAME, REG, FIELD, FEATURE) \ +void NAME(CPUTriCoreState *env, uint32_t val) \ +{ \ + if (tricore_has_feature(env, TRICORE_FEATURE_##FEATURE)) { \ + env->REG = FIELD_DP32(env->REG, REG, FIELD ## _ ## FEATURE, val); \ + } \ + env->REG = FIELD_DP32(env->REG, REG, FIELD ## _13, val); \ +} + +#define FIELD_SETTER(NAME, REG, FIELD) \ +void NAME(CPUTriCoreState *env, uint32_t val) \ +{ \ + env->REG = FIELD_DP32(env->REG, REG, FIELD, val); \ +} + +FIELD_GETTER_WITH_FEATURE(pcxi_get_pcpn, PCXI, PCPN, 161) +FIELD_SETTER_WITH_FEATURE(pcxi_set_pcpn, PCXI, PCPN, 161) +FIELD_GETTER_WITH_FEATURE(pcxi_get_pie, PCXI, PIE, 161) +FIELD_SETTER_WITH_FEATURE(pcxi_set_pie, PCXI, PIE, 161) +FIELD_GETTER_WITH_FEATURE(pcxi_get_ul, PCXI, UL, 161) +FIELD_SETTER_WITH_FEATURE(pcxi_set_ul, PCXI, UL, 161) +FIELD_GETTER(pcxi_get_pcxs, PCXI, PCXS) +FIELD_GETTER(pcxi_get_pcxo, PCXI, PCXO) + +FIELD_GETTER_WITH_FEATURE(icr_get_ie, ICR, IE, 161) +FIELD_SETTER_WITH_FEATURE(icr_set_ie, ICR, IE, 161) +FIELD_GETTER(icr_get_ccpn, ICR, CCPN) +FIELD_SETTER(icr_set_ccpn, ICR, CCPN) diff --git a/target/tricore/helper.h b/target/tricore/helper.h index b64780c37d..1d97d078b0 100644 --- a/target/tricore/helper.h +++ b/target/tricore/helper.h @@ -111,9 +111,12 @@ DEF_HELPER_4(fmsub, i32, env, i32, i32, i32) DEF_HELPER_3(fcmp, i32, env, i32, i32) DEF_HELPER_2(qseed, i32, env, i32) DEF_HELPER_2(ftoi, i32, env, i32) +DEF_HELPER_2(ftohp, i32, env, i32) +DEF_HELPER_2(hptof, i32, env, i32) DEF_HELPER_2(itof, i32, env, i32) DEF_HELPER_2(utof, i32, env, i32) DEF_HELPER_2(ftoiz, i32, env, i32) +DEF_HELPER_2(ftou, i32, env, i32) DEF_HELPER_2(ftouz, i32, env, i32) DEF_HELPER_2(updfl, void, env, i32) /* dvinit */ @@ -131,7 +134,11 @@ DEF_HELPER_FLAGS_5(mul_h, TCG_CALL_NO_RWG_SE, i64, i32, i32, i32, i32, i32) DEF_HELPER_FLAGS_5(mulm_h, TCG_CALL_NO_RWG_SE, i64, i32, i32, i32, i32, i32) DEF_HELPER_FLAGS_5(mulr_h, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32, i32, i32) /* crc32 */ -DEF_HELPER_FLAGS_2(crc32, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(crc32b, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(crc32_be, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_2(crc32_le, TCG_CALL_NO_RWG_SE, i32, i32, i32) +DEF_HELPER_FLAGS_3(crcn, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) +DEF_HELPER_FLAGS_2(shuffle, TCG_CALL_NO_RWG_SE, i32, i32, i32) /* CSA */ DEF_HELPER_2(call, void, env, i32) DEF_HELPER_1(ret, void, env) diff --git a/target/tricore/meson.build b/target/tricore/meson.build index 0ccc829517..45f49f0128 100644 --- a/target/tricore/meson.build +++ b/target/tricore/meson.build @@ -9,7 +9,7 @@ tricore_ss.add(files( )) tricore_ss.add(zlib) -tricore_softmmu_ss = ss.source_set() +tricore_system_ss = ss.source_set() target_arch += {'tricore': tricore_ss} -target_softmmu_arch += {'tricore': tricore_softmmu_ss} +target_system_arch += {'tricore': tricore_system_ss} diff --git a/target/tricore/op_helper.c b/target/tricore/op_helper.c index a79c838a92..ba9c4444b3 100644 --- a/target/tricore/op_helper.c +++ b/target/tricore/op_helper.c @@ -31,7 +31,7 @@ void raise_exception_sync_internal(CPUTriCoreState *env, uint32_t class, int tin { CPUState *cs = env_cpu(env); /* in case we come from a helper-call we need to restore the PC */ - cpu_restore_state(cs, pc, true); + cpu_restore_state(cs, pc); /* Tin is loaded into d[15] */ env->gpr_d[15] = tin; @@ -84,11 +84,10 @@ void raise_exception_sync_internal(CPUTriCoreState *env, uint32_t class, int tin ICR.IE and ICR.CCPN are saved */ /* PCXI.PIE = ICR.IE */ - env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE_1_3) + - ((env->ICR & MASK_ICR_IE_1_3) << 15)); + pcxi_set_pie(env, icr_get_ie(env)); + /* PCXI.PCPN = ICR.CCPN */ - env->PCXI = (env->PCXI & 0xffffff) + - ((env->ICR & MASK_ICR_CCPN) << 24); + pcxi_set_pcpn(env, icr_get_ccpn(env)); /* Update PC using the trap vector table */ env->PC = env->BTV | (class << 5); @@ -2285,7 +2284,15 @@ uint32_t helper_mulr_h(uint32_t arg00, uint32_t arg01, return (result1 & 0xffff0000) | (result0 >> 16); } -uint32_t helper_crc32(uint32_t arg0, uint32_t arg1) +uint32_t helper_crc32b(uint32_t arg0, uint32_t arg1) +{ + uint8_t buf[1] = { arg0 & 0xff }; + + return crc32(arg1, buf, 1); +} + + +uint32_t helper_crc32_be(uint32_t arg0, uint32_t arg1) { uint8_t buf[4]; stl_be_p(buf, arg0); @@ -2293,6 +2300,113 @@ uint32_t helper_crc32(uint32_t arg0, uint32_t arg1) return crc32(arg1, buf, 4); } +uint32_t helper_crc32_le(uint32_t arg0, uint32_t arg1) +{ + uint8_t buf[4]; + stl_le_p(buf, arg0); + + return crc32(arg1, buf, 4); +} + +static uint32_t crc_div(uint32_t crc_in, uint32_t data, uint32_t gen, + uint32_t n, uint32_t m) +{ + uint32_t i; + + data = data << n; + for (i = 0; i < m; i++) { + if (crc_in & (1u << (n - 1))) { + crc_in <<= 1; + if (data & (1u << (m - 1))) { + crc_in++; + } + crc_in ^= gen; + } else { + crc_in <<= 1; + if (data & (1u << (m - 1))) { + crc_in++; + } + } + data <<= 1; + } + + return crc_in; +} + +uint32_t helper_crcn(uint32_t arg0, uint32_t arg1, uint32_t arg2) +{ + uint32_t crc_out, crc_in; + uint32_t n = extract32(arg0, 12, 4) + 1; + uint32_t gen = extract32(arg0, 16, n); + uint32_t inv = extract32(arg0, 9, 1); + uint32_t le = extract32(arg0, 8, 1); + uint32_t m = extract32(arg0, 0, 3) + 1; + uint32_t data = extract32(arg1, 0, m); + uint32_t seed = extract32(arg2, 0, n); + + if (le == 1) { + if (m == 0) { + data = 0; + } else { + data = revbit32(data) >> (32 - m); + } + } + + if (inv == 1) { + seed = ~seed; + } + + if (m > n) { + crc_in = (data >> (m - n)) ^ seed; + } else { + crc_in = (data << (n - m)) ^ seed; + } + + crc_out = crc_div(crc_in, data, gen, n, m); + + if (inv) { + crc_out = ~crc_out; + } + + return extract32(crc_out, 0, n); +} + +uint32_t helper_shuffle(uint32_t arg0, uint32_t arg1) +{ + uint32_t resb; + uint32_t byte_select; + uint32_t res = 0; + + byte_select = arg1 & 0x3; + resb = extract32(arg0, byte_select * 8, 8); + res |= resb << 0; + + byte_select = (arg1 >> 2) & 0x3; + resb = extract32(arg0, byte_select * 8, 8); + res |= resb << 8; + + byte_select = (arg1 >> 4) & 0x3; + resb = extract32(arg0, byte_select * 8, 8); + res |= resb << 16; + + byte_select = (arg1 >> 6) & 0x3; + resb = extract32(arg0, byte_select * 8, 8); + res |= resb << 24; + + if (arg1 & 0x100) { + /* Assign the correct nibble position. */ + res = ((res & 0xf0f0f0f0) >> 4) + | ((res & 0x0f0f0f0f) << 4); + /* Assign the correct bit position. */ + res = ((res & 0x88888888) >> 3) + | ((res & 0x44444444) >> 1) + | ((res & 0x22222222) << 1) + | ((res & 0x11111111) << 3); + } + + return res; +} + /* context save area (CSA) related helpers */ static int cdc_increment(target_ulong *psw) @@ -2344,7 +2458,7 @@ static bool cdc_zero(target_ulong *psw) return count == 0; } -static void save_context_upper(CPUTriCoreState *env, int ea) +static void save_context_upper(CPUTriCoreState *env, target_ulong ea) { cpu_stl_data(env, ea, env->PCXI); cpu_stl_data(env, ea+4, psw_read(env)); @@ -2364,7 +2478,7 @@ static void save_context_upper(CPUTriCoreState *env, int ea) cpu_stl_data(env, ea+60, env->gpr_d[15]); } -static void save_context_lower(CPUTriCoreState *env, int ea) +static void save_context_lower(CPUTriCoreState *env, target_ulong ea) { cpu_stl_data(env, ea, env->PCXI); cpu_stl_data(env, ea+4, env->gpr_a[11]); @@ -2384,7 +2498,7 @@ static void save_context_lower(CPUTriCoreState *env, int ea) cpu_stl_data(env, ea+60, env->gpr_d[7]); } -static void restore_context_upper(CPUTriCoreState *env, int ea, +static void restore_context_upper(CPUTriCoreState *env, target_ulong ea, target_ulong *new_PCXI, target_ulong *new_PSW) { *new_PCXI = cpu_ldl_data(env, ea); @@ -2405,7 +2519,7 @@ static void restore_context_upper(CPUTriCoreState *env, int ea, env->gpr_d[15] = cpu_ldl_data(env, ea+60); } -static void restore_context_lower(CPUTriCoreState *env, int ea, +static void restore_context_lower(CPUTriCoreState *env, target_ulong ea, target_ulong *ra, target_ulong *pcxi) { *pcxi = cpu_ldl_data(env, ea); @@ -2448,6 +2562,13 @@ void helper_call(CPUTriCoreState *env, uint32_t next_pc) } /* PSW.CDE = 1;*/ psw |= MASK_PSW_CDE; + /* + * we need to save PSW.CDE and not PSW.CDC into the CSAs. psw already + * contains the CDC from cdc_increment(), so we cannot call psw_write() + * here. + */ + env->PSW |= MASK_PSW_CDE; + /* tmp_FCX = FCX; */ tmp_FCX = env->FCX; /* EA = {FCX.FCXS, 6'b0, FCX.FCXO, 6'b0}; */ @@ -2461,13 +2582,11 @@ void helper_call(CPUTriCoreState *env, uint32_t next_pc) save_context_upper(env, ea); /* PCXI.PCPN = ICR.CCPN; */ - env->PCXI = (env->PCXI & 0xffffff) + - ((env->ICR & MASK_ICR_CCPN) << 24); + pcxi_set_pcpn(env, icr_get_ccpn(env)); /* PCXI.PIE = ICR.IE; */ - env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE_1_3) + - ((env->ICR & MASK_ICR_IE_1_3) << 15)); + pcxi_set_pie(env, icr_get_ie(env)); /* PCXI.UL = 1; */ - env->PCXI |= MASK_PCXI_UL; + pcxi_set_ul(env, 1); /* PCXI[19: 0] = FCX[19: 0]; */ env->PCXI = (env->PCXI & 0xfff00000) + (env->FCX & 0xfffff); @@ -2506,7 +2625,7 @@ void helper_ret(CPUTriCoreState *env) raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CSU, GETPC()); } /* if (PCXI.UL == 0) then trap(CTYP); */ - if ((env->PCXI & MASK_PCXI_UL) == 0) { + if (pcxi_get_ul(env) == 0) { /* CTYP trap */ cdc_increment(&psw); /* restore to the start of helper */ psw_write(env, psw); @@ -2516,8 +2635,8 @@ void helper_ret(CPUTriCoreState *env) env->PC = env->gpr_a[11] & 0xfffffffe; /* EA = {PCXI.PCXS, 6'b0, PCXI.PCXO, 6'b0}; */ - ea = ((env->PCXI & MASK_PCXI_PCXS) << 12) + - ((env->PCXI & MASK_PCXI_PCXO) << 6); + ea = (pcxi_get_pcxs(env) << 28) | + (pcxi_get_pcxo(env) << 6); /* {new_PCXI, new_PSW, A[10], A[11], D[8], D[9], D[10], D[11], A[12], A[13], A[14], A[15], D[12], D[13], D[14], D[15]} = M(EA, 16 * word); */ restore_context_upper(env, ea, &new_PCXI, &new_PSW); @@ -2528,12 +2647,12 @@ void helper_ret(CPUTriCoreState *env) /* PCXI = new_PCXI; */ env->PCXI = new_PCXI; - if (tricore_feature(env, TRICORE_FEATURE_13)) { - /* PSW = new_PSW */ - psw_write(env, new_PSW); - } else { + if (tricore_has_feature(env, TRICORE_FEATURE_131)) { /* PSW = {new_PSW[31:26], PSW[25:24], new_PSW[23:0]}; */ psw_write(env, (new_PSW & ~(0x3000000)) + (psw & (0x3000000))); + } else { /* TRICORE_FEATURE_13 only */ + /* PSW = new_PSW */ + psw_write(env, new_PSW); } } @@ -2559,21 +2678,21 @@ void helper_bisr(CPUTriCoreState *env, uint32_t const9) /* PCXI.PCPN = ICR.CCPN */ - env->PCXI = (env->PCXI & 0xffffff) + - ((env->ICR & MASK_ICR_CCPN) << 24); + pcxi_set_pcpn(env, icr_get_ccpn(env)); /* PCXI.PIE = ICR.IE */ - env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE_1_3) + - ((env->ICR & MASK_ICR_IE_1_3) << 15)); + pcxi_set_pie(env, icr_get_ie(env)); /* PCXI.UL = 0 */ - env->PCXI &= ~(MASK_PCXI_UL); + pcxi_set_ul(env, 0); + /* PCXI[19: 0] = FCX[19: 0] */ env->PCXI = (env->PCXI & 0xfff00000) + (env->FCX & 0xfffff); /* FXC[19: 0] = new_FCX[19: 0] */ env->FCX = (env->FCX & 0xfff00000) + (new_FCX & 0xfffff); - /* ICR.IE = 1 */ - env->ICR |= MASK_ICR_IE_1_3; - env->ICR |= const9; /* ICR.CCPN = const9[7: 0];*/ + /* ICR.IE = 1 */ + icr_set_ie(env, 1); + + icr_set_ccpn(env, const9); if (tmp_FCX == env->LCX) { /* FCD trap */ @@ -2592,7 +2711,7 @@ void helper_rfe(CPUTriCoreState *env) raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CSU, GETPC()); } /* if (PCXI.UL == 0) then trap(CTYP); */ - if ((env->PCXI & MASK_PCXI_UL) == 0) { + if (pcxi_get_ul(env) == 0) { /* raise CTYP trap */ raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CTYP, GETPC()); } @@ -2603,14 +2722,15 @@ void helper_rfe(CPUTriCoreState *env) } env->PC = env->gpr_a[11] & ~0x1; /* ICR.IE = PCXI.PIE; */ - env->ICR = (env->ICR & ~MASK_ICR_IE_1_3) - + ((env->PCXI & MASK_PCXI_PIE_1_3) >> 15); + icr_set_ie(env, pcxi_get_pie(env)); + /* ICR.CCPN = PCXI.PCPN; */ - env->ICR = (env->ICR & ~MASK_ICR_CCPN) + - ((env->PCXI & MASK_PCXI_PCPN) >> 24); + icr_set_ccpn(env, pcxi_get_pcpn(env)); + /*EA = {PCXI.PCXS, 6'b0, PCXI.PCXO, 6'b0};*/ - ea = ((env->PCXI & MASK_PCXI_PCXS) << 12) + - ((env->PCXI & MASK_PCXI_PCXO) << 6); + ea = (pcxi_get_pcxs(env) << 28) | + (pcxi_get_pcxo(env) << 6); + /*{new_PCXI, PSW, A[10], A[11], D[8], D[9], D[10], D[11], A[12], A[13], A[14], A[15], D[12], D[13], D[14], D[15]} = M(EA, 16 * word); */ restore_context_upper(env, ea, &new_PCXI, &new_PSW); @@ -2628,42 +2748,41 @@ void helper_rfm(CPUTriCoreState *env) { env->PC = (env->gpr_a[11] & ~0x1); /* ICR.IE = PCXI.PIE; */ - env->ICR = (env->ICR & ~MASK_ICR_IE_1_3) - | ((env->PCXI & MASK_PCXI_PIE_1_3) >> 15); + icr_set_ie(env, pcxi_get_pie(env)); /* ICR.CCPN = PCXI.PCPN; */ - env->ICR = (env->ICR & ~MASK_ICR_CCPN) | - ((env->PCXI & MASK_PCXI_PCPN) >> 24); + icr_set_ccpn(env, pcxi_get_pcpn(env)); + /* {PCXI, PSW, A[10], A[11]} = M(DCX, 4 * word); */ env->PCXI = cpu_ldl_data(env, env->DCX); psw_write(env, cpu_ldl_data(env, env->DCX+4)); env->gpr_a[10] = cpu_ldl_data(env, env->DCX+8); env->gpr_a[11] = cpu_ldl_data(env, env->DCX+12); - if (tricore_feature(env, TRICORE_FEATURE_131)) { + if (tricore_has_feature(env, TRICORE_FEATURE_131)) { env->DBGTCR = 0; } } -void helper_ldlcx(CPUTriCoreState *env, uint32_t ea) +void helper_ldlcx(CPUTriCoreState *env, target_ulong ea) { uint32_t dummy; /* insn doesn't load PCXI and RA */ restore_context_lower(env, ea, &dummy, &dummy); } -void helper_lducx(CPUTriCoreState *env, uint32_t ea) +void helper_lducx(CPUTriCoreState *env, target_ulong ea) { uint32_t dummy; /* insn doesn't load PCXI and PSW */ restore_context_upper(env, ea, &dummy, &dummy); } -void helper_stlcx(CPUTriCoreState *env, uint32_t ea) +void helper_stlcx(CPUTriCoreState *env, target_ulong ea) { save_context_lower(env, ea); } -void helper_stucx(CPUTriCoreState *env, uint32_t ea) +void helper_stucx(CPUTriCoreState *env, target_ulong ea) { save_context_upper(env, ea); } @@ -2691,13 +2810,13 @@ void helper_svlcx(CPUTriCoreState *env) save_context_lower(env, ea); /* PCXI.PCPN = ICR.CCPN; */ - env->PCXI = (env->PCXI & 0xffffff) + - ((env->ICR & MASK_ICR_CCPN) << 24); + pcxi_set_pcpn(env, icr_get_ccpn(env)); + /* PCXI.PIE = ICR.IE; */ - env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE_1_3) + - ((env->ICR & MASK_ICR_IE_1_3) << 15)); + pcxi_set_pie(env, icr_get_ie(env)); + /* PCXI.UL = 0; */ - env->PCXI &= ~MASK_PCXI_UL; + pcxi_set_ul(env, 0); /* PCXI[19: 0] = FCX[19: 0]; */ env->PCXI = (env->PCXI & 0xfff00000) + (env->FCX & 0xfffff); @@ -2734,13 +2853,13 @@ void helper_svucx(CPUTriCoreState *env) save_context_upper(env, ea); /* PCXI.PCPN = ICR.CCPN; */ - env->PCXI = (env->PCXI & 0xffffff) + - ((env->ICR & MASK_ICR_CCPN) << 24); + pcxi_set_pcpn(env, icr_get_ccpn(env)); + /* PCXI.PIE = ICR.IE; */ - env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE_1_3) + - ((env->ICR & MASK_ICR_IE_1_3) << 15)); + pcxi_set_pie(env, icr_get_ie(env)); + /* PCXI.UL = 1; */ - env->PCXI |= MASK_PCXI_UL; + pcxi_set_ul(env, 1); /* PCXI[19: 0] = FCX[19: 0]; */ env->PCXI = (env->PCXI & 0xfff00000) + (env->FCX & 0xfffff); @@ -2764,13 +2883,15 @@ void helper_rslcx(CPUTriCoreState *env) raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CSU, GETPC()); } /* if (PCXI.UL == 1) then trap(CTYP); */ - if ((env->PCXI & MASK_PCXI_UL) != 0) { + if (pcxi_get_ul(env) == 1) { /* CTYP trap */ raise_exception_sync_helper(env, TRAPC_CTX_MNG, TIN3_CTYP, GETPC()); } /* EA = {PCXI.PCXS, 6'b0, PCXI.PCXO, 6'b0}; */ - ea = ((env->PCXI & MASK_PCXI_PCXS) << 12) + - ((env->PCXI & MASK_PCXI_PCXO) << 6); + /* EA = {PCXI.PCXS, 6'b0, PCXI.PCXO, 6'b0}; */ + ea = (pcxi_get_pcxs(env) << 28) | + (pcxi_get_pcxo(env) << 6); + /* {new_PCXI, A[11], A[10], A[11], D[8], D[9], D[10], D[11], A[12], A[13], A[14], A[15], D[12], D[13], D[14], D[15]} = M(EA, 16 * word); */ restore_context_lower(env, ea, &env->gpr_a[11], &new_PCXI); diff --git a/target/tricore/translate.c b/target/tricore/translate.c index d170500fa5..c45e1d992e 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -33,6 +33,14 @@ #include "exec/translator.h" #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + +#define DISAS_EXIT DISAS_TARGET_0 +#define DISAS_EXIT_UPDATE DISAS_TARGET_1 +#define DISAS_JUMP DISAS_TARGET_2 + /* * TCG registers */ @@ -50,8 +58,6 @@ static TCGv cpu_PSW_SV; static TCGv cpu_PSW_AV; static TCGv cpu_PSW_SAV; -#include "exec/gen-icount.h" - static const char *regnames_a[] = { "a0" , "a1" , "a2" , "a3" , "a4" , "a5" , "a6" , "a7" , "a8" , "a9" , "sp" , "a11" , @@ -70,8 +76,9 @@ typedef struct DisasContext { uint32_t opcode; /* Routine used to access memory */ int mem_idx; - uint32_t hflags, saved_hflags; + int priv; uint64_t features; + uint32_t icr_ie_mask, icr_ie_offset; } DisasContext; static int has_feature(DisasContext *ctx, int feature) @@ -88,8 +95,7 @@ enum { void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - TriCoreCPU *cpu = TRICORE_CPU(cs); - CPUTriCoreState *env = &cpu->env; + CPUTriCoreState *env = cpu_env(cs); uint32_t psw; int i; @@ -121,12 +127,11 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) * Functions to generate micro-ops */ -/* Makros for generating helpers */ +/* Macros for generating helpers */ #define gen_helper_1arg(name, arg) do { \ - TCGv_i32 helper_tmp = tcg_const_i32(arg); \ - gen_helper_##name(cpu_env, helper_tmp); \ - tcg_temp_free_i32(helper_tmp); \ + TCGv_i32 helper_tmp = tcg_constant_i32(arg); \ + gen_helper_##name(tcg_env, helper_tmp); \ } while (0) #define GEN_HELPER_LL(name, ret, arg0, arg1, n) do { \ @@ -137,9 +142,6 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) tcg_gen_ext16s_tl(arg01, arg0); \ tcg_gen_ext16s_tl(arg11, arg1); \ gen_helper_##name(ret, arg00, arg01, arg11, arg11, n); \ - tcg_temp_free(arg00); \ - tcg_temp_free(arg01); \ - tcg_temp_free(arg11); \ } while (0) #define GEN_HELPER_LU(name, ret, arg0, arg1, n) do { \ @@ -152,10 +154,6 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) tcg_gen_sari_tl(arg11, arg1, 16); \ tcg_gen_ext16s_tl(arg10, arg1); \ gen_helper_##name(ret, arg00, arg01, arg10, arg11, n); \ - tcg_temp_free(arg00); \ - tcg_temp_free(arg01); \ - tcg_temp_free(arg10); \ - tcg_temp_free(arg11); \ } while (0) #define GEN_HELPER_UL(name, ret, arg0, arg1, n) do { \ @@ -168,10 +166,6 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) tcg_gen_sari_tl(arg10, arg1, 16); \ tcg_gen_ext16s_tl(arg11, arg1); \ gen_helper_##name(ret, arg00, arg01, arg10, arg11, n); \ - tcg_temp_free(arg00); \ - tcg_temp_free(arg01); \ - tcg_temp_free(arg10); \ - tcg_temp_free(arg11); \ } while (0) #define GEN_HELPER_UU(name, ret, arg0, arg1, n) do { \ @@ -182,9 +176,6 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) tcg_gen_ext16s_tl(arg00, arg0); \ tcg_gen_sari_tl(arg11, arg1, 16); \ gen_helper_##name(ret, arg00, arg01, arg11, arg11, n); \ - tcg_temp_free(arg00); \ - tcg_temp_free(arg01); \ - tcg_temp_free(arg11); \ } while (0) #define GEN_HELPER_RRR(name, rl, rh, al1, ah1, arg2) do { \ @@ -194,18 +185,13 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) tcg_gen_concat_i32_i64(arg1, al1, ah1); \ gen_helper_##name(ret, arg1, arg2); \ tcg_gen_extr_i64_i32(rl, rh, ret); \ - \ - tcg_temp_free_i64(ret); \ - tcg_temp_free_i64(arg1); \ } while (0) #define GEN_HELPER_RR(name, rl, rh, arg1, arg2) do { \ TCGv_i64 ret = tcg_temp_new_i64(); \ \ - gen_helper_##name(ret, cpu_env, arg1, arg2); \ + gen_helper_##name(ret, tcg_env, arg1, arg2); \ tcg_gen_extr_i64_i32(rl, rh, ret); \ - \ - tcg_temp_free_i64(ret); \ } while (0) #define EA_ABS_FORMAT(con) (((con & 0x3C000) << 14) + (con & 0x3FFF)) @@ -229,7 +215,6 @@ static inline void gen_offset_ld(DisasContext *ctx, TCGv r1, TCGv r2, TCGv temp = tcg_temp_new(); tcg_gen_addi_tl(temp, r2, con); tcg_gen_qemu_ld_tl(r1, temp, ctx->mem_idx, mop); - tcg_temp_free(temp); } static inline void gen_offset_st(DisasContext *ctx, TCGv r1, TCGv r2, @@ -238,7 +223,6 @@ static inline void gen_offset_st(DisasContext *ctx, TCGv r1, TCGv r2, TCGv temp = tcg_temp_new(); tcg_gen_addi_tl(temp, r2, con); tcg_gen_qemu_st_tl(r1, temp, ctx->mem_idx, mop); - tcg_temp_free(temp); } static void gen_st_2regs_64(TCGv rh, TCGv rl, TCGv address, DisasContext *ctx) @@ -247,8 +231,6 @@ static void gen_st_2regs_64(TCGv rh, TCGv rl, TCGv address, DisasContext *ctx) tcg_gen_concat_i32_i64(temp, rl, rh); tcg_gen_qemu_st_i64(temp, address, ctx->mem_idx, MO_LEUQ); - - tcg_temp_free_i64(temp); } static void gen_offset_st_2regs(TCGv rh, TCGv rl, TCGv base, int16_t con, @@ -257,7 +239,6 @@ static void gen_offset_st_2regs(TCGv rh, TCGv rl, TCGv base, int16_t con, TCGv temp = tcg_temp_new(); tcg_gen_addi_tl(temp, base, con); gen_st_2regs_64(rh, rl, temp, ctx); - tcg_temp_free(temp); } static void gen_ld_2regs_64(TCGv rh, TCGv rl, TCGv address, DisasContext *ctx) @@ -267,8 +248,6 @@ static void gen_ld_2regs_64(TCGv rh, TCGv rl, TCGv address, DisasContext *ctx) tcg_gen_qemu_ld_i64(temp, address, ctx->mem_idx, MO_LEUQ); /* write back to two 32 bit regs */ tcg_gen_extr_i64_i32(rl, rh, temp); - - tcg_temp_free_i64(temp); } static void gen_offset_ld_2regs(TCGv rh, TCGv rl, TCGv base, int16_t con, @@ -277,7 +256,6 @@ static void gen_offset_ld_2regs(TCGv rh, TCGv rl, TCGv base, int16_t con, TCGv temp = tcg_temp_new(); tcg_gen_addi_tl(temp, base, con); gen_ld_2regs_64(rh, rl, temp, ctx); - tcg_temp_free(temp); } static void gen_st_preincr(DisasContext *ctx, TCGv r1, TCGv r2, int16_t off, @@ -287,7 +265,6 @@ static void gen_st_preincr(DisasContext *ctx, TCGv r1, TCGv r2, int16_t off, tcg_gen_addi_tl(temp, r2, off); tcg_gen_qemu_st_tl(r1, temp, ctx->mem_idx, mop); tcg_gen_mov_tl(r2, temp); - tcg_temp_free(temp); } static void gen_ld_preincr(DisasContext *ctx, TCGv r1, TCGv r2, int16_t off, @@ -297,7 +274,6 @@ static void gen_ld_preincr(DisasContext *ctx, TCGv r1, TCGv r2, int16_t off, tcg_gen_addi_tl(temp, r2, off); tcg_gen_qemu_ld_tl(r1, temp, ctx->mem_idx, mop); tcg_gen_mov_tl(r2, temp); - tcg_temp_free(temp); } /* M(EA, word) = (M(EA, word) & ~E[a][63:32]) | (E[a][31:0] & E[a][63:32]); */ @@ -317,9 +293,6 @@ static void gen_ldmst(DisasContext *ctx, int ereg, TCGv ea) tcg_gen_or_tl(temp, temp, temp2); /* M(EA, word) = temp; */ tcg_gen_qemu_st_tl(temp, ea, ctx->mem_idx, MO_LEUL); - - tcg_temp_free(temp); - tcg_temp_free(temp2); } /* tmp = M(EA, word); @@ -332,22 +305,18 @@ static void gen_swap(DisasContext *ctx, int reg, TCGv ea) tcg_gen_qemu_ld_tl(temp, ea, ctx->mem_idx, MO_LEUL); tcg_gen_qemu_st_tl(cpu_gpr_d[reg], ea, ctx->mem_idx, MO_LEUL); tcg_gen_mov_tl(cpu_gpr_d[reg], temp); - - tcg_temp_free(temp); } static void gen_cmpswap(DisasContext *ctx, int reg, TCGv ea) { TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); + CHECK_REG_PAIR(reg); tcg_gen_qemu_ld_tl(temp, ea, ctx->mem_idx, MO_LEUL); tcg_gen_movcond_tl(TCG_COND_EQ, temp2, cpu_gpr_d[reg+1], temp, cpu_gpr_d[reg], temp); tcg_gen_qemu_st_tl(temp2, ea, ctx->mem_idx, MO_LEUL); tcg_gen_mov_tl(cpu_gpr_d[reg], temp); - - tcg_temp_free(temp); - tcg_temp_free(temp2); } static void gen_swapmsk(DisasContext *ctx, int reg, TCGv ea) @@ -355,28 +324,23 @@ static void gen_swapmsk(DisasContext *ctx, int reg, TCGv ea) TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); TCGv temp3 = tcg_temp_new(); - + CHECK_REG_PAIR(reg); tcg_gen_qemu_ld_tl(temp, ea, ctx->mem_idx, MO_LEUL); tcg_gen_and_tl(temp2, cpu_gpr_d[reg], cpu_gpr_d[reg+1]); tcg_gen_andc_tl(temp3, temp, cpu_gpr_d[reg+1]); tcg_gen_or_tl(temp2, temp2, temp3); tcg_gen_qemu_st_tl(temp2, ea, ctx->mem_idx, MO_LEUL); tcg_gen_mov_tl(cpu_gpr_d[reg], temp); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); } - /* We generate loads and store to core special function register (csfr) through the function gen_mfcr and gen_mtcr. To handle access permissions, we use 3 - makros R, A and E, which allow read-only, all and endinit protected access. - These makros also specify in which ISA version the csfr was introduced. */ + macros R, A and E, which allow read-only, all and endinit protected access. + These macros also specify in which ISA version the csfr was introduced. */ #define R(ADDRESS, REG, FEATURE) \ case ADDRESS: \ if (has_feature(ctx, FEATURE)) { \ - tcg_gen_ld_tl(ret, cpu_env, offsetof(CPUTriCoreState, REG)); \ + tcg_gen_ld_tl(ret, tcg_env, offsetof(CPUTriCoreState, REG)); \ } \ break; #define A(ADDRESS, REG, FEATURE) R(ADDRESS, REG, FEATURE) @@ -385,10 +349,10 @@ static inline void gen_mfcr(DisasContext *ctx, TCGv ret, int32_t offset) { /* since we're caching PSW make this a special case */ if (offset == 0xfe04) { - gen_helper_psw_read(ret, cpu_env); + gen_helper_psw_read(ret, tcg_env); } else { switch (offset) { -#include "csfr.def" +#include "csfr.h.inc" } } } @@ -397,11 +361,11 @@ static inline void gen_mfcr(DisasContext *ctx, TCGv ret, int32_t offset) #undef E #define R(ADDRESS, REG, FEATURE) /* don't gen writes to read-only reg, - since no execption occurs */ + since no exception occurs */ #define A(ADDRESS, REG, FEATURE) R(ADDRESS, REG, FEATURE) \ case ADDRESS: \ if (has_feature(ctx, FEATURE)) { \ - tcg_gen_st_tl(r1, cpu_env, offsetof(CPUTriCoreState, REG)); \ + tcg_gen_st_tl(r1, tcg_env, offsetof(CPUTriCoreState, REG)); \ } \ break; /* Endinit protected registers @@ -412,17 +376,18 @@ static inline void gen_mfcr(DisasContext *ctx, TCGv ret, int32_t offset) static inline void gen_mtcr(DisasContext *ctx, TCGv r1, int32_t offset) { - if ((ctx->hflags & TRICORE_HFLAG_KUU) == TRICORE_HFLAG_SM) { + if (ctx->priv == TRICORE_PRIV_SM) { /* since we're caching PSW make this a special case */ if (offset == 0xfe04) { - gen_helper_psw_write(cpu_env, r1); + gen_helper_psw_write(tcg_env, r1); + ctx->base.is_jmp = DISAS_EXIT_UPDATE; } else { switch (offset) { -#include "csfr.def" +#include "csfr.h.inc" } } } else { - /* generate privilege trap */ + generate_trap(ctx, TRAPC_PROT, TIN1_PRIV); } } @@ -447,9 +412,6 @@ static inline void gen_add_d(TCGv ret, TCGv r1, TCGv r2) tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ tcg_gen_mov_tl(ret, result); - - tcg_temp_free(result); - tcg_temp_free(t0); } static inline void @@ -476,11 +438,6 @@ gen_add64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ tcg_gen_mov_i64(ret, result); - - tcg_temp_free(temp); - tcg_temp_free_i64(result); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } static inline void @@ -527,11 +484,6 @@ gen_addsub64_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp); /* calc SAV bit */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); - tcg_temp_free(temp4); } /* ret = r2 + (r1 * r3); */ @@ -564,17 +516,12 @@ static inline void gen_madd32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); /* calc SAV */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); } static inline void gen_maddi32_d(TCGv ret, TCGv r1, TCGv r2, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_madd32_d(ret, r1, r2, temp); - tcg_temp_free(temp); } static inline void @@ -603,11 +550,6 @@ gen_madd64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, /* write back the result */ tcg_gen_mov_tl(ret_low, t3); tcg_gen_mov_tl(ret_high, t4); - - tcg_temp_free(t1); - tcg_temp_free(t2); - tcg_temp_free(t3); - tcg_temp_free(t4); } static inline void @@ -638,108 +580,98 @@ gen_maddu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_xor_tl(cpu_PSW_AV, ret_high, cpu_PSW_AV); /* calc SAV */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); } static inline void gen_maddi64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_madd64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); - tcg_temp_free(temp); } static inline void gen_maddui64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_maddu64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); - tcg_temp_free(temp); } static inline void gen_madd_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); + TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_extr_i64_i32(temp, temp2, temp64); gen_addsub64_h(ret_low, ret_high, r1_low, r1_high, temp, temp2, tcg_gen_add_tl, tcg_gen_add_tl); - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free_i64(temp64); } static inline void gen_maddsu_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); + TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_extr_i64_i32(temp, temp2, temp64); gen_addsub64_h(ret_low, ret_high, r1_low, r1_high, temp, temp2, tcg_gen_sub_tl, tcg_gen_add_tl); - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free_i64(temp64); } static inline void gen_maddsum_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); TCGv_i64 temp64_3 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_concat_i32_i64(temp64_3, r1_low, r1_high); @@ -751,11 +683,6 @@ gen_maddsum_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, gen_add64_d(temp64_2, temp64_3, temp64); /* write back result */ tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_2); - - tcg_temp_free(temp); - tcg_temp_free_i64(temp64); - tcg_temp_free_i64(temp64_2); - tcg_temp_free_i64(temp64_3); } static inline void gen_adds(TCGv ret, TCGv r1, TCGv r2); @@ -764,23 +691,24 @@ static inline void gen_madds_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); + TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); TCGv temp3 = tcg_temp_new(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_extr_i64_i32(temp, temp2, temp64); @@ -792,12 +720,6 @@ gen_madds_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_or_tl(cpu_PSW_V, cpu_PSW_V, temp); /* combine av bits */ tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp3); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); - tcg_temp_free_i64(temp64); - } static inline void gen_subs(TCGv ret, TCGv r1, TCGv r2); @@ -806,23 +728,24 @@ static inline void gen_maddsus_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); + TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); TCGv temp3 = tcg_temp_new(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_extr_i64_i32(temp, temp2, temp64); @@ -834,34 +757,28 @@ gen_maddsus_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_or_tl(cpu_PSW_V, cpu_PSW_V, temp); /* combine av bits */ tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp3); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); - tcg_temp_free_i64(temp64); - } static inline void gen_maddsums_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_sari_i64(temp64_2, temp64, 32); /* high */ @@ -870,12 +787,8 @@ gen_maddsums_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_shli_i64(temp64, temp64, 16); tcg_gen_concat_i32_i64(temp64_2, r1_low, r1_high); - gen_helper_add64_ssov(temp64, cpu_env, temp64_2, temp64); + gen_helper_add64_ssov(temp64, tcg_env, temp64_2, temp64); tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); - - tcg_temp_free(temp); - tcg_temp_free_i64(temp64); - tcg_temp_free_i64(temp64_2); } @@ -883,89 +796,77 @@ static inline void gen_maddm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); TCGv_i64 temp64_3 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mulm_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mulm_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mulm_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mulm_h, temp64, r2, r3, t_n); break; } tcg_gen_concat_i32_i64(temp64_2, r1_low, r1_high); gen_add64_d(temp64_3, temp64_2, temp64); /* write back result */ tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_3); - - tcg_temp_free(temp); - tcg_temp_free_i64(temp64); - tcg_temp_free_i64(temp64_2); - tcg_temp_free_i64(temp64_3); } static inline void gen_maddms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mulm_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mulm_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mulm_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mulm_h, temp64, r2, r3, t_n); break; } tcg_gen_concat_i32_i64(temp64_2, r1_low, r1_high); - gen_helper_add64_ssov(temp64, cpu_env, temp64_2, temp64); + gen_helper_add64_ssov(temp64, tcg_env, temp64_2, temp64); tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); - - tcg_temp_free(temp); - tcg_temp_free_i64(temp64); - tcg_temp_free_i64(temp64_2); } static inline void gen_maddr64_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } - gen_helper_addr_h(ret, cpu_env, temp64, r1_low, r1_high); - - tcg_temp_free(temp); - tcg_temp_free_i64(temp64); + gen_helper_addr_h(ret, tcg_env, temp64, r1_low, r1_high); } static inline void @@ -977,38 +878,32 @@ gen_maddr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) tcg_gen_andi_tl(temp2, r1, 0xffff0000); tcg_gen_shli_tl(temp, r1, 16); gen_maddr64_h(ret, temp, temp2, r2, r3, n, mode); - - tcg_temp_free(temp); - tcg_temp_free(temp2); } static inline void gen_maddsur32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); + TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_andi_tl(temp2, r1, 0xffff0000); tcg_gen_shli_tl(temp, r1, 16); - gen_helper_addsur_h(ret, cpu_env, temp64, temp, temp2); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free_i64(temp64); + gen_helper_addsur_h(ret, tcg_env, temp64, temp, temp2); } @@ -1016,26 +911,23 @@ static inline void gen_maddr64s_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } - gen_helper_addr_h_ssov(ret, cpu_env, temp64, r1_low, r1_high); - - tcg_temp_free(temp); - tcg_temp_free_i64(temp64); + gen_helper_addr_h_ssov(ret, tcg_env, temp64, r1_low, r1_high); } static inline void @@ -1047,54 +939,46 @@ gen_maddr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) tcg_gen_andi_tl(temp2, r1, 0xffff0000); tcg_gen_shli_tl(temp, r1, 16); gen_maddr64s_h(ret, temp, temp2, r2, r3, n, mode); - - tcg_temp_free(temp); - tcg_temp_free(temp2); } static inline void gen_maddsur32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); + TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_andi_tl(temp2, r1, 0xffff0000); tcg_gen_shli_tl(temp, r1, 16); - gen_helper_addsur_h_ssov(ret, cpu_env, temp64, temp, temp2); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free_i64(temp64); + gen_helper_addsur_h_ssov(ret, tcg_env, temp64, temp, temp2); } static inline void gen_maddr_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) { - TCGv temp = tcg_const_i32(n); - gen_helper_maddr_q(ret, cpu_env, r1, r2, r3, temp); - tcg_temp_free(temp); + TCGv t_n = tcg_constant_i32(n); + gen_helper_maddr_q(ret, tcg_env, r1, r2, r3, t_n); } static inline void gen_maddrs_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) { - TCGv temp = tcg_const_i32(n); - gen_helper_maddr_q_ssov(ret, cpu_env, r1, r2, r3, temp); - tcg_temp_free(temp); + TCGv t_n = tcg_constant_i32(n); + gen_helper_maddr_q_ssov(ret, tcg_env, r1, r2, r3, t_n); } static inline void @@ -1145,13 +1029,6 @@ gen_madd32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ tcg_gen_mov_tl(ret, temp3); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); } static inline void @@ -1169,9 +1046,6 @@ gen_m16add32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) tcg_gen_sub_tl(temp, temp, temp2); } gen_add_d(ret, arg1, temp); - - tcg_temp_free(temp); - tcg_temp_free(temp2); } static inline void @@ -1189,9 +1063,6 @@ gen_m16adds32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) tcg_gen_sub_tl(temp, temp, temp2); } gen_adds(ret, arg1, temp); - - tcg_temp_free(temp); - tcg_temp_free(temp2); } static inline void @@ -1219,12 +1090,6 @@ gen_m16add64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, gen_add64_d(t3, t1, t2); /* write back result */ tcg_gen_extr_i64_i32(rl, rh, t3); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); - tcg_temp_free(temp); - tcg_temp_free(temp2); } static inline void @@ -1249,13 +1114,8 @@ gen_m16adds64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_shli_i64(t2, t2, 16); tcg_gen_concat_i32_i64(t1, arg1_low, arg1_high); - gen_helper_add64_ssov(t1, cpu_env, t1, t2); + gen_helper_add64_ssov(t1, tcg_env, t1, t2); tcg_gen_extr_i64_i32(rl, rh, t1); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); } static inline void @@ -1294,9 +1154,6 @@ gen_madd64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_shli_tl(temp, temp, 31); /* negate v bit, if special condition */ tcg_gen_xor_tl(cpu_PSW_V, cpu_PSW_V, temp); - - tcg_temp_free(temp); - tcg_temp_free(temp2); } /* write back result */ tcg_gen_extr_i64_i32(rl, rh, t4); @@ -1307,11 +1164,6 @@ gen_madd64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_xor_tl(cpu_PSW_AV, rh, cpu_PSW_AV); /* calc SAV */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); - tcg_temp_free_i64(t4); } static inline void @@ -1329,11 +1181,7 @@ gen_madds32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, tcg_gen_mul_i64(t2, t2, t3); tcg_gen_sari_i64(t2, t2, up_shift - n); - gen_helper_madd32_q_add_ssov(ret, cpu_env, t1, t2); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); + gen_helper_madd32_q_add_ssov(ret, tcg_env, t1, t2); } static inline void @@ -1341,15 +1189,13 @@ gen_madds64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, TCGv arg3, uint32_t n) { TCGv_i64 r1 = tcg_temp_new_i64(); - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); tcg_gen_concat_i32_i64(r1, arg1_low, arg1_high); - gen_helper_madd64_q_ssov(r1, cpu_env, r1, arg2, arg3, temp); + gen_helper_madd64_q_ssov(r1, tcg_env, r1, arg2, arg3, t_n); tcg_gen_extr_i64_i32(rl, rh, r1); - - tcg_temp_free_i64(r1); - tcg_temp_free(temp); } + /* ret = r2 - (r1 * r3); */ static inline void gen_msub32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) { @@ -1381,17 +1227,12 @@ static inline void gen_msub32_d(TCGv ret, TCGv r1, TCGv r2, TCGv r3) tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); /* calc SAV */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); } static inline void gen_msubi32_d(TCGv ret, TCGv r1, TCGv r2, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_msub32_d(ret, r1, r2, temp); - tcg_temp_free(temp); } static inline void @@ -1420,20 +1261,14 @@ gen_msub64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, /* write back the result */ tcg_gen_mov_tl(ret_low, t3); tcg_gen_mov_tl(ret_high, t4); - - tcg_temp_free(t1); - tcg_temp_free(t2); - tcg_temp_free(t3); - tcg_temp_free(t4); } static inline void gen_msubi64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_msub64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); - tcg_temp_free(temp); } static inline void @@ -1462,27 +1297,22 @@ gen_msubu64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, tcg_gen_xor_tl(cpu_PSW_AV, ret_high, cpu_PSW_AV); /* calc SAV */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); } static inline void gen_msubui64_d(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_msubu64_d(ret_low, ret_high, r1, r2_low, r2_high, temp); - tcg_temp_free(temp); } static inline void gen_addi_d(TCGv ret, TCGv r1, target_ulong r2) { - TCGv temp = tcg_const_i32(r2); + TCGv temp = tcg_constant_i32(r2); gen_add_d(ret, r1, temp); - tcg_temp_free(temp); } + /* calculate the carry bit too */ static inline void gen_add_CC(TCGv ret, TCGv r1, TCGv r2) { @@ -1505,16 +1335,12 @@ static inline void gen_add_CC(TCGv ret, TCGv r1, TCGv r2) tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ tcg_gen_mov_tl(ret, result); - - tcg_temp_free(result); - tcg_temp_free(t0); } static inline void gen_addi_CC(TCGv ret, TCGv r1, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_add_CC(ret, r1, temp); - tcg_temp_free(temp); } static inline void gen_addc_CC(TCGv ret, TCGv r1, TCGv r2) @@ -1541,17 +1367,12 @@ static inline void gen_addc_CC(TCGv ret, TCGv r1, TCGv r2) tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ tcg_gen_mov_tl(ret, result); - - tcg_temp_free(result); - tcg_temp_free(t0); - tcg_temp_free(carry); } static inline void gen_addci_CC(TCGv ret, TCGv r1, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_addc_CC(ret, r1, temp); - tcg_temp_free(temp); } static inline void gen_cond_add(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, @@ -1561,7 +1382,7 @@ static inline void gen_cond_add(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, TCGv temp2 = tcg_temp_new(); TCGv result = tcg_temp_new(); TCGv mask = tcg_temp_new(); - TCGv t0 = tcg_const_i32(0); + TCGv t0 = tcg_constant_i32(0); /* create mask for sticky bits */ tcg_gen_setcond_tl(cond, mask, r4, t0); @@ -1585,20 +1406,13 @@ static inline void gen_cond_add(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, tcg_gen_or_tl(cpu_PSW_SAV, temp, cpu_PSW_SAV); /* write back result */ tcg_gen_movcond_tl(cond, r3, r4, t0, result, r1); - - tcg_temp_free(t0); - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(result); - tcg_temp_free(mask); } static inline void gen_condi_add(TCGCond cond, TCGv r1, int32_t r2, TCGv r3, TCGv r4) { - TCGv temp = tcg_const_i32(r2); + TCGv temp = tcg_constant_i32(r2); gen_cond_add(cond, r1, temp, r3, r4); - tcg_temp_free(temp); } static inline void gen_sub_d(TCGv ret, TCGv r1, TCGv r2) @@ -1620,9 +1434,6 @@ static inline void gen_sub_d(TCGv ret, TCGv r1, TCGv r2) tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ tcg_gen_mov_tl(ret, result); - - tcg_temp_free(temp); - tcg_temp_free(result); } static inline void @@ -1649,11 +1460,6 @@ gen_sub64_d(TCGv_i64 ret, TCGv_i64 r1, TCGv_i64 r2) tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ tcg_gen_mov_i64(ret, result); - - tcg_temp_free(temp); - tcg_temp_free_i64(result); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } static inline void gen_sub_CC(TCGv ret, TCGv r1, TCGv r2) @@ -1677,9 +1483,6 @@ static inline void gen_sub_CC(TCGv ret, TCGv r1, TCGv r2) tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ tcg_gen_mov_tl(ret, result); - - tcg_temp_free(result); - tcg_temp_free(temp); } static inline void gen_subc_CC(TCGv ret, TCGv r1, TCGv r2) @@ -1687,7 +1490,6 @@ static inline void gen_subc_CC(TCGv ret, TCGv r1, TCGv r2) TCGv temp = tcg_temp_new(); tcg_gen_not_tl(temp, r2); gen_addc_CC(ret, r1, temp); - tcg_temp_free(temp); } static inline void gen_cond_sub(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, @@ -1697,7 +1499,7 @@ static inline void gen_cond_sub(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, TCGv temp2 = tcg_temp_new(); TCGv result = tcg_temp_new(); TCGv mask = tcg_temp_new(); - TCGv t0 = tcg_const_i32(0); + TCGv t0 = tcg_constant_i32(0); /* create mask for sticky bits */ tcg_gen_setcond_tl(cond, mask, r4, t0); @@ -1721,64 +1523,57 @@ static inline void gen_cond_sub(TCGCond cond, TCGv r1, TCGv r2, TCGv r3, tcg_gen_or_tl(cpu_PSW_SAV, temp, cpu_PSW_SAV); /* write back result */ tcg_gen_movcond_tl(cond, r3, r4, t0, result, r1); - - tcg_temp_free(t0); - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(result); - tcg_temp_free(mask); } static inline void gen_msub_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); + TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_extr_i64_i32(temp, temp2, temp64); gen_addsub64_h(ret_low, ret_high, r1_low, r1_high, temp, temp2, tcg_gen_sub_tl, tcg_gen_sub_tl); - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free_i64(temp64); } static inline void gen_msubs_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); + TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); TCGv temp3 = tcg_temp_new(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_extr_i64_i32(temp, temp2, temp64); @@ -1790,100 +1585,83 @@ gen_msubs_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_or_tl(cpu_PSW_V, cpu_PSW_V, temp); /* combine av bits */ tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp3); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); - tcg_temp_free_i64(temp64); } static inline void gen_msubm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); TCGv_i64 temp64_3 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mulm_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mulm_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mulm_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mulm_h, temp64, r2, r3, t_n); break; } tcg_gen_concat_i32_i64(temp64_2, r1_low, r1_high); gen_sub64_d(temp64_3, temp64_2, temp64); /* write back result */ tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_3); - - tcg_temp_free(temp); - tcg_temp_free_i64(temp64); - tcg_temp_free_i64(temp64_2); - tcg_temp_free_i64(temp64_3); } static inline void gen_msubms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mulm_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mulm_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mulm_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mulm_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mulm_h, temp64, r2, r3, t_n); break; } tcg_gen_concat_i32_i64(temp64_2, r1_low, r1_high); - gen_helper_sub64_ssov(temp64, cpu_env, temp64_2, temp64); + gen_helper_sub64_ssov(temp64, tcg_env, temp64_2, temp64); tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); - - tcg_temp_free(temp); - tcg_temp_free_i64(temp64); - tcg_temp_free_i64(temp64_2); } static inline void gen_msubr64_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } - gen_helper_subr_h(ret, cpu_env, temp64, r1_low, r1_high); - - tcg_temp_free(temp); - tcg_temp_free_i64(temp64); + gen_helper_subr_h(ret, tcg_env, temp64, r1_low, r1_high); } static inline void @@ -1895,35 +1673,29 @@ gen_msubr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) tcg_gen_andi_tl(temp2, r1, 0xffff0000); tcg_gen_shli_tl(temp, r1, 16); gen_msubr64_h(ret, temp, temp2, r2, r3, n, mode); - - tcg_temp_free(temp); - tcg_temp_free(temp2); } static inline void gen_msubr64s_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } - gen_helper_subr_h_ssov(ret, cpu_env, temp64, r1_low, r1_high); - - tcg_temp_free(temp); - tcg_temp_free_i64(temp64); + gen_helper_subr_h_ssov(ret, tcg_env, temp64, r1_low, r1_high); } static inline void @@ -1935,33 +1707,26 @@ gen_msubr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) tcg_gen_andi_tl(temp2, r1, 0xffff0000); tcg_gen_shli_tl(temp, r1, 16); gen_msubr64s_h(ret, temp, temp2, r2, r3, n, mode); - - tcg_temp_free(temp); - tcg_temp_free(temp2); } static inline void gen_msubr_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) { - TCGv temp = tcg_const_i32(n); - gen_helper_msubr_q(ret, cpu_env, r1, r2, r3, temp); - tcg_temp_free(temp); + TCGv temp = tcg_constant_i32(n); + gen_helper_msubr_q(ret, tcg_env, r1, r2, r3, temp); } static inline void gen_msubrs_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) { - TCGv temp = tcg_const_i32(n); - gen_helper_msubr_q_ssov(ret, cpu_env, r1, r2, r3, temp); - tcg_temp_free(temp); + TCGv temp = tcg_constant_i32(n); + gen_helper_msubr_q_ssov(ret, tcg_env, r1, r2, r3, temp); } static inline void gen_msub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, uint32_t up_shift) { - TCGv temp = tcg_temp_new(); - TCGv temp2 = tcg_temp_new(); TCGv temp3 = tcg_temp_new(); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); @@ -1997,14 +1762,6 @@ gen_msub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ tcg_gen_mov_tl(ret, temp3); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); - tcg_temp_free_i64(t4); } static inline void @@ -2022,9 +1779,6 @@ gen_m16sub32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) tcg_gen_sub_tl(temp, temp, temp2); } gen_sub_d(ret, arg1, temp); - - tcg_temp_free(temp); - tcg_temp_free(temp2); } static inline void @@ -2042,9 +1796,6 @@ gen_m16subs32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n) tcg_gen_sub_tl(temp, temp, temp2); } gen_subs(ret, arg1, temp); - - tcg_temp_free(temp); - tcg_temp_free(temp2); } static inline void @@ -2072,12 +1823,6 @@ gen_m16sub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, gen_sub64_d(t3, t1, t2); /* write back result */ tcg_gen_extr_i64_i32(rl, rh, t3); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); - tcg_temp_free(temp); - tcg_temp_free(temp2); } static inline void @@ -2102,13 +1847,8 @@ gen_m16subs64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_shli_i64(t2, t2, 16); tcg_gen_concat_i32_i64(t1, arg1_low, arg1_high); - gen_helper_sub64_ssov(t1, cpu_env, t1, t2); + gen_helper_sub64_ssov(t1, tcg_env, t1, t2); tcg_gen_extr_i64_i32(rl, rh, t1); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); } static inline void @@ -2147,9 +1887,6 @@ gen_msub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_shli_tl(temp, temp, 31); /* negate v bit, if special condition */ tcg_gen_xor_tl(cpu_PSW_V, cpu_PSW_V, temp); - - tcg_temp_free(temp); - tcg_temp_free(temp2); } /* write back result */ tcg_gen_extr_i64_i32(rl, rh, t4); @@ -2160,11 +1897,6 @@ gen_msub64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, tcg_gen_xor_tl(cpu_PSW_AV, rh, cpu_PSW_AV); /* calc SAV */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); - tcg_temp_free_i64(t4); } static inline void @@ -2187,12 +1919,7 @@ gen_msubs32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, tcg_gen_sari_i64(t3, t2, up_shift - n); tcg_gen_add_i64(t3, t3, t4); - gen_helper_msub32_q_sub_ssov(ret, cpu_env, t1, t3); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); - tcg_temp_free_i64(t3); - tcg_temp_free_i64(t4); + gen_helper_msub32_q_sub_ssov(ret, tcg_env, t1, t3); } static inline void @@ -2200,65 +1927,60 @@ gen_msubs64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, TCGv arg3, uint32_t n) { TCGv_i64 r1 = tcg_temp_new_i64(); - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); tcg_gen_concat_i32_i64(r1, arg1_low, arg1_high); - gen_helper_msub64_q_ssov(r1, cpu_env, r1, arg2, arg3, temp); + gen_helper_msub64_q_ssov(r1, tcg_env, r1, arg2, arg3, t_n); tcg_gen_extr_i64_i32(rl, rh, r1); - - tcg_temp_free_i64(r1); - tcg_temp_free(temp); } static inline void gen_msubad_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); + TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_extr_i64_i32(temp, temp2, temp64); gen_addsub64_h(ret_low, ret_high, r1_low, r1_high, temp, temp2, tcg_gen_add_tl, tcg_gen_sub_tl); - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free_i64(temp64); } static inline void gen_msubadm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); TCGv_i64 temp64_3 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_concat_i32_i64(temp64_3, r1_low, r1_high); @@ -2270,63 +1992,56 @@ gen_msubadm_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, gen_sub64_d(temp64_2, temp64_3, temp64); /* write back result */ tcg_gen_extr_i64_i32(ret_low, ret_high, temp64_2); - - tcg_temp_free(temp); - tcg_temp_free_i64(temp64); - tcg_temp_free_i64(temp64_2); - tcg_temp_free_i64(temp64_3); } static inline void gen_msubadr32_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); + TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_andi_tl(temp2, r1, 0xffff0000); tcg_gen_shli_tl(temp, r1, 16); - gen_helper_subadr_h(ret, cpu_env, temp64, temp, temp2); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free_i64(temp64); + gen_helper_subadr_h(ret, tcg_env, temp64, temp, temp2); } static inline void gen_msubads_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); + TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); TCGv temp3 = tcg_temp_new(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_extr_i64_i32(temp, temp2, temp64); @@ -2338,33 +2053,28 @@ gen_msubads_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_or_tl(cpu_PSW_V, cpu_PSW_V, temp); /* combine av bits */ tcg_gen_or_tl(cpu_PSW_AV, cpu_PSW_AV, temp3); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); - tcg_temp_free_i64(temp64); } static inline void gen_msubadms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); TCGv_i64 temp64 = tcg_temp_new_i64(); TCGv_i64 temp64_2 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_sari_i64(temp64_2, temp64, 32); /* high */ @@ -2373,41 +2083,34 @@ gen_msubadms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, tcg_gen_shli_i64(temp64, temp64, 16); tcg_gen_concat_i32_i64(temp64_2, r1_low, r1_high); - gen_helper_sub64_ssov(temp64, cpu_env, temp64_2, temp64); + gen_helper_sub64_ssov(temp64, tcg_env, temp64_2, temp64); tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); - - tcg_temp_free(temp); - tcg_temp_free_i64(temp64); - tcg_temp_free_i64(temp64_2); } static inline void gen_msubadr32s_h(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n, uint32_t mode) { - TCGv temp = tcg_const_i32(n); + TCGv t_n = tcg_constant_i32(n); + TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); TCGv_i64 temp64 = tcg_temp_new_i64(); switch (mode) { case MODE_LL: - GEN_HELPER_LL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LL(mul_h, temp64, r2, r3, t_n); break; case MODE_LU: - GEN_HELPER_LU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_LU(mul_h, temp64, r2, r3, t_n); break; case MODE_UL: - GEN_HELPER_UL(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UL(mul_h, temp64, r2, r3, t_n); break; case MODE_UU: - GEN_HELPER_UU(mul_h, temp64, r2, r3, temp); + GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } tcg_gen_andi_tl(temp2, r1, 0xffff0000); tcg_gen_shli_tl(temp, r1, 16); - gen_helper_subadr_h_ssov(ret, cpu_env, temp64, temp, temp2); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free_i64(temp64); + gen_helper_subadr_h_ssov(ret, tcg_env, temp64, temp, temp2); } static inline void gen_abs(TCGv ret, TCGv r1) @@ -2449,23 +2152,18 @@ static inline void gen_absdif(TCGv ret, TCGv r1, TCGv r2) tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* write back result */ tcg_gen_mov_tl(ret, result); - - tcg_temp_free(temp); - tcg_temp_free(result); } static inline void gen_absdifi(TCGv ret, TCGv r1, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_absdif(ret, r1, temp); - tcg_temp_free(temp); } static inline void gen_absdifsi(TCGv ret, TCGv r1, int32_t con) { - TCGv temp = tcg_const_i32(con); - gen_helper_absdif_ssov(ret, cpu_env, r1, temp); - tcg_temp_free(temp); + TCGv temp = tcg_constant_i32(con); + gen_helper_absdif_ssov(ret, tcg_env, r1, temp); } static inline void gen_mul_i32s(TCGv ret, TCGv r1, TCGv r2) @@ -2486,16 +2184,12 @@ static inline void gen_mul_i32s(TCGv ret, TCGv r1, TCGv r2) tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); /* calc SAV bit */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - - tcg_temp_free(high); - tcg_temp_free(low); } static inline void gen_muli_i32s(TCGv ret, TCGv r1, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_mul_i32s(ret, r1, temp); - tcg_temp_free(temp); } static inline void gen_mul_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) @@ -2515,9 +2209,8 @@ static inline void gen_mul_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) static inline void gen_muli_i64s(TCGv ret_low, TCGv ret_high, TCGv r1, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_mul_i64s(ret_low, ret_high, r1, temp); - tcg_temp_free(temp); } static inline void gen_mul_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) @@ -2537,43 +2230,38 @@ static inline void gen_mul_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2) static inline void gen_muli_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_mul_i64u(ret_low, ret_high, r1, temp); - tcg_temp_free(temp); } static inline void gen_mulsi_i32(TCGv ret, TCGv r1, int32_t con) { - TCGv temp = tcg_const_i32(con); - gen_helper_mul_ssov(ret, cpu_env, r1, temp); - tcg_temp_free(temp); + TCGv temp = tcg_constant_i32(con); + gen_helper_mul_ssov(ret, tcg_env, r1, temp); } static inline void gen_mulsui_i32(TCGv ret, TCGv r1, int32_t con) { - TCGv temp = tcg_const_i32(con); - gen_helper_mul_suov(ret, cpu_env, r1, temp); - tcg_temp_free(temp); + TCGv temp = tcg_constant_i32(con); + gen_helper_mul_suov(ret, tcg_env, r1, temp); } + /* gen_maddsi_32(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r3], const9); */ static inline void gen_maddsi_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) { - TCGv temp = tcg_const_i32(con); - gen_helper_madd32_ssov(ret, cpu_env, r1, r2, temp); - tcg_temp_free(temp); + TCGv temp = tcg_constant_i32(con); + gen_helper_madd32_ssov(ret, tcg_env, r1, r2, temp); } static inline void gen_maddsui_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) { - TCGv temp = tcg_const_i32(con); - gen_helper_madd32_suov(ret, cpu_env, r1, r2, temp); - tcg_temp_free(temp); + TCGv temp = tcg_constant_i32(con); + gen_helper_madd32_suov(ret, tcg_env, r1, r2, temp); } static void gen_mul_q(TCGv rl, TCGv rh, TCGv arg1, TCGv arg2, uint32_t n, uint32_t up_shift) { - TCGv temp = tcg_temp_new(); TCGv_i64 temp_64 = tcg_temp_new_i64(); TCGv_i64 temp2_64 = tcg_temp_new_i64(); @@ -2626,9 +2314,6 @@ gen_mul_q(TCGv rl, TCGv rh, TCGv arg1, TCGv arg2, uint32_t n, uint32_t up_shift) } /* calc sav overflow bit */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - tcg_temp_free(temp); - tcg_temp_free_i64(temp_64); - tcg_temp_free_i64(temp2_64); } static void @@ -2651,8 +2336,6 @@ gen_mul_q_16(TCGv ret, TCGv arg1, TCGv arg2, uint32_t n) tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); /* calc sav overflow bit */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - - tcg_temp_free(temp); } static void gen_mulr_q(TCGv ret, TCGv arg1, TCGv arg2, uint32_t n) @@ -2679,8 +2362,6 @@ static void gen_mulr_q(TCGv ret, TCGv arg1, TCGv arg2, uint32_t n) tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* cut halfword off */ tcg_gen_andi_tl(ret, ret, 0xffff0000); - - tcg_temp_free(temp); } static inline void @@ -2689,18 +2370,16 @@ gen_madds_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, { TCGv_i64 temp64 = tcg_temp_new_i64(); tcg_gen_concat_i32_i64(temp64, r2_low, r2_high); - gen_helper_madd64_ssov(temp64, cpu_env, r1, temp64, r3); + gen_helper_madd64_ssov(temp64, tcg_env, r1, temp64, r3); tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); - tcg_temp_free_i64(temp64); } static inline void gen_maddsi_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_madds_64(ret_low, ret_high, r1, r2_low, r2_high, temp); - tcg_temp_free(temp); } static inline void @@ -2709,32 +2388,28 @@ gen_maddsu_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, { TCGv_i64 temp64 = tcg_temp_new_i64(); tcg_gen_concat_i32_i64(temp64, r2_low, r2_high); - gen_helper_madd64_suov(temp64, cpu_env, r1, temp64, r3); + gen_helper_madd64_suov(temp64, tcg_env, r1, temp64, r3); tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); - tcg_temp_free_i64(temp64); } static inline void gen_maddsui_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_maddsu_64(ret_low, ret_high, r1, r2_low, r2_high, temp); - tcg_temp_free(temp); } static inline void gen_msubsi_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) { - TCGv temp = tcg_const_i32(con); - gen_helper_msub32_ssov(ret, cpu_env, r1, r2, temp); - tcg_temp_free(temp); + TCGv temp = tcg_constant_i32(con); + gen_helper_msub32_ssov(ret, tcg_env, r1, r2, temp); } static inline void gen_msubsui_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) { - TCGv temp = tcg_const_i32(con); - gen_helper_msub32_suov(ret, cpu_env, r1, r2, temp); - tcg_temp_free(temp); + TCGv temp = tcg_constant_i32(con); + gen_helper_msub32_suov(ret, tcg_env, r1, r2, temp); } static inline void @@ -2743,18 +2418,16 @@ gen_msubs_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, { TCGv_i64 temp64 = tcg_temp_new_i64(); tcg_gen_concat_i32_i64(temp64, r2_low, r2_high); - gen_helper_msub64_ssov(temp64, cpu_env, r1, temp64, r3); + gen_helper_msub64_ssov(temp64, tcg_env, r1, temp64, r3); tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); - tcg_temp_free_i64(temp64); } static inline void gen_msubsi_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_msubs_64(ret_low, ret_high, r1, r2_low, r2_high, temp); - tcg_temp_free(temp); } static inline void @@ -2763,41 +2436,27 @@ gen_msubsu_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, { TCGv_i64 temp64 = tcg_temp_new_i64(); tcg_gen_concat_i32_i64(temp64, r2_low, r2_high); - gen_helper_msub64_suov(temp64, cpu_env, r1, temp64, r3); + gen_helper_msub64_suov(temp64, tcg_env, r1, temp64, r3); tcg_gen_extr_i64_i32(ret_low, ret_high, temp64); - tcg_temp_free_i64(temp64); } static inline void gen_msubsui_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_msubsu_64(ret_low, ret_high, r1, r2_low, r2_high, temp); - tcg_temp_free(temp); } static void gen_saturate(TCGv ret, TCGv arg, int32_t up, int32_t low) { - TCGv sat_neg = tcg_const_i32(low); - TCGv temp = tcg_const_i32(up); - - /* sat_neg = (arg < low ) ? low : arg; */ - tcg_gen_movcond_tl(TCG_COND_LT, sat_neg, arg, sat_neg, sat_neg, arg); - - /* ret = (sat_neg > up ) ? up : sat_neg; */ - tcg_gen_movcond_tl(TCG_COND_GT, ret, sat_neg, temp, temp, sat_neg); - - tcg_temp_free(sat_neg); - tcg_temp_free(temp); + tcg_gen_smax_tl(ret, arg, tcg_constant_i32(low)); + tcg_gen_smin_tl(ret, ret, tcg_constant_i32(up)); } static void gen_saturate_u(TCGv ret, TCGv arg, int32_t up) { - TCGv temp = tcg_const_i32(up); - /* sat_neg = (arg > up ) ? up : arg; */ - tcg_gen_movcond_tl(TCG_COND_GTU, ret, arg, temp, temp, arg); - tcg_temp_free(temp); + tcg_gen_umin_tl(ret, arg, tcg_constant_i32(up)); } static void gen_shi(TCGv ret, TCGv r1, int32_t shift_count) @@ -2826,9 +2485,6 @@ static void gen_sh_hi(TCGv ret, TCGv r1, int32_t shiftcount) gen_shi(temp_low, temp_low, shiftcount); gen_shi(ret, temp_high, shiftcount); tcg_gen_deposit_tl(ret, ret, temp_low, 0, 16); - - tcg_temp_free(temp_low); - tcg_temp_free(temp_high); } } @@ -2837,7 +2493,6 @@ static void gen_shaci(TCGv ret, TCGv r1, int32_t shift_count) uint32_t msk, msk_start; TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); - TCGv t_0 = tcg_const_i32(0); if (shift_count == 0) { /* Clear PSW.C and PSW.V */ @@ -2852,8 +2507,8 @@ static void gen_shaci(TCGv ret, TCGv r1, int32_t shift_count) /* clear PSW.V */ tcg_gen_movi_tl(cpu_PSW_V, 0); } else if (shift_count > 0) { - TCGv t_max = tcg_const_i32(0x7FFFFFFF >> shift_count); - TCGv t_min = tcg_const_i32(((int32_t) -0x80000000) >> shift_count); + TCGv t_max = tcg_constant_i32(0x7FFFFFFF >> shift_count); + TCGv t_min = tcg_constant_i32(((int32_t) -0x80000000) >> shift_count); /* calc carry */ msk_start = 32 - shift_count; @@ -2868,9 +2523,6 @@ static void gen_shaci(TCGv ret, TCGv r1, int32_t shift_count) tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_V, cpu_PSW_SV); /* do shift */ tcg_gen_shli_tl(ret, r1, shift_count); - - tcg_temp_free(t_max); - tcg_temp_free(t_min); } else { /* clear PSW.V */ tcg_gen_movi_tl(cpu_PSW_V, 0); @@ -2885,22 +2537,17 @@ static void gen_shaci(TCGv ret, TCGv r1, int32_t shift_count) tcg_gen_xor_tl(cpu_PSW_AV, ret, cpu_PSW_AV); /* calc sav overflow bit */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(t_0); } static void gen_shas(TCGv ret, TCGv r1, TCGv r2) { - gen_helper_sha_ssov(ret, cpu_env, r1, r2); + gen_helper_sha_ssov(ret, tcg_env, r1, r2); } static void gen_shasi(TCGv ret, TCGv r1, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_shas(ret, r1, temp); - tcg_temp_free(temp); } static void gen_sha_hi(TCGv ret, TCGv r1, int32_t shift_count) @@ -2917,9 +2564,6 @@ static void gen_sha_hi(TCGv ret, TCGv r1, int32_t shift_count) tcg_gen_shli_tl(low, r1, shift_count); tcg_gen_shli_tl(ret, high, shift_count); tcg_gen_deposit_tl(ret, ret, low, 0, 16); - - tcg_temp_free(low); - tcg_temp_free(high); } else { low = tcg_temp_new(); high = tcg_temp_new(); @@ -2928,11 +2572,7 @@ static void gen_sha_hi(TCGv ret, TCGv r1, int32_t shift_count) tcg_gen_sari_tl(low, low, -shift_count); tcg_gen_sari_tl(ret, r1, -shift_count); tcg_gen_deposit_tl(ret, ret, low, 0, 16); - - tcg_temp_free(low); - tcg_temp_free(high); } - } /* ret = {ret[30:0], (r1 cond r2)}; */ @@ -2944,45 +2584,39 @@ static void gen_sh_cond(int cond, TCGv ret, TCGv r1, TCGv r2) tcg_gen_shli_tl(temp, ret, 1); tcg_gen_setcond_tl(cond, temp2, r1, r2); tcg_gen_or_tl(ret, temp, temp2); - - tcg_temp_free(temp); - tcg_temp_free(temp2); } static void gen_sh_condi(int cond, TCGv ret, TCGv r1, int32_t con) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_sh_cond(cond, ret, r1, temp); - tcg_temp_free(temp); } static inline void gen_adds(TCGv ret, TCGv r1, TCGv r2) { - gen_helper_add_ssov(ret, cpu_env, r1, r2); + gen_helper_add_ssov(ret, tcg_env, r1, r2); } static inline void gen_addsi(TCGv ret, TCGv r1, int32_t con) { - TCGv temp = tcg_const_i32(con); - gen_helper_add_ssov(ret, cpu_env, r1, temp); - tcg_temp_free(temp); + TCGv temp = tcg_constant_i32(con); + gen_helper_add_ssov(ret, tcg_env, r1, temp); } static inline void gen_addsui(TCGv ret, TCGv r1, int32_t con) { - TCGv temp = tcg_const_i32(con); - gen_helper_add_suov(ret, cpu_env, r1, temp); - tcg_temp_free(temp); + TCGv temp = tcg_constant_i32(con); + gen_helper_add_suov(ret, tcg_env, r1, temp); } static inline void gen_subs(TCGv ret, TCGv r1, TCGv r2) { - gen_helper_sub_ssov(ret, cpu_env, r1, r2); + gen_helper_sub_ssov(ret, tcg_env, r1, r2); } static inline void gen_subsu(TCGv ret, TCGv r1, TCGv r2) { - gen_helper_sub_suov(ret, cpu_env, r1, r2); + gen_helper_sub_suov(ret, tcg_env, r1, r2); } static inline void gen_bit_2op(TCGv ret, TCGv r1, TCGv r2, @@ -3002,9 +2636,6 @@ static inline void gen_bit_2op(TCGv ret, TCGv r1, TCGv r2, (*op2)(temp1 , ret, temp1); tcg_gen_deposit_tl(ret, ret, temp1, 0, 1); - - tcg_temp_free(temp1); - tcg_temp_free(temp2); } /* ret = r1[pos1] op1 r2[pos2]; */ @@ -3023,9 +2654,6 @@ static inline void gen_bit_1op(TCGv ret, TCGv r1, TCGv r2, (*op1)(ret, temp1, temp2); tcg_gen_andi_tl(ret, ret, 0x1); - - tcg_temp_free(temp1); - tcg_temp_free(temp2); } static inline void gen_accumulating_cond(int cond, TCGv ret, TCGv r1, TCGv r2, @@ -3041,25 +2669,14 @@ static inline void gen_accumulating_cond(int cond, TCGv ret, TCGv r1, TCGv r2, (*op)(temp, temp, temp2); /* ret = {ret[31:1], temp} */ tcg_gen_deposit_tl(ret, ret, temp, 0, 1); - - tcg_temp_free(temp); - tcg_temp_free(temp2); } static inline void gen_accumulating_condi(int cond, TCGv ret, TCGv r1, int32_t con, void(*op)(TCGv, TCGv, TCGv)) { - TCGv temp = tcg_const_i32(con); + TCGv temp = tcg_constant_i32(con); gen_accumulating_cond(cond, ret, r1, temp, op); - tcg_temp_free(temp); -} - -/* ret = (r1 cond r2) ? 0xFFFFFFFF ? 0x00000000;*/ -static inline void gen_cond_w(TCGCond cond, TCGv ret, TCGv r1, TCGv r2) -{ - tcg_gen_setcond_tl(cond, ret, r1, r2); - tcg_gen_neg_tl(ret, ret); } static inline void gen_eqany_bi(TCGv ret, TCGv r1, int32_t con) @@ -3089,11 +2706,6 @@ static inline void gen_eqany_bi(TCGv ret, TCGv r1, int32_t con) tcg_gen_or_tl(ret, b0, b1); tcg_gen_or_tl(ret, ret, b2); tcg_gen_or_tl(ret, ret, b3); - - tcg_temp_free(b0); - tcg_temp_free(b1); - tcg_temp_free(b2); - tcg_temp_free(b3); } static inline void gen_eqany_hi(TCGv ret, TCGv r1, int32_t con) @@ -3111,10 +2723,8 @@ static inline void gen_eqany_hi(TCGv ret, TCGv r1, int32_t con) /* combine them */ tcg_gen_or_tl(ret, h0, h1); - - tcg_temp_free(h0); - tcg_temp_free(h1); } + /* mask = ((1 << width) -1) << pos; ret = (r1 & ~mask) | (r2 << pos) & mask); */ static inline void gen_insert(TCGv ret, TCGv r1, TCGv r2, TCGv width, TCGv pos) @@ -3132,10 +2742,6 @@ static inline void gen_insert(TCGv ret, TCGv r1, TCGv r2, TCGv width, TCGv pos) tcg_gen_and_tl(temp, temp, mask); tcg_gen_andc_tl(temp2, r1, mask); tcg_gen_or_tl(ret, temp, temp2); - - tcg_temp_free(mask); - tcg_temp_free(temp); - tcg_temp_free(temp2); } static inline void gen_bsplit(TCGv rl, TCGv rh, TCGv r1) @@ -3144,8 +2750,6 @@ static inline void gen_bsplit(TCGv rl, TCGv rh, TCGv r1) gen_helper_bsplit(temp, r1); tcg_gen_extr_i64_i32(rl, rh, temp); - - tcg_temp_free_i64(temp); } static inline void gen_unpack(TCGv rl, TCGv rh, TCGv r1) @@ -3154,8 +2758,6 @@ static inline void gen_unpack(TCGv rl, TCGv rh, TCGv r1) gen_helper_unpack(temp, r1); tcg_gen_extr_i64_i32(rl, rh, temp); - - tcg_temp_free_i64(temp); } static inline void @@ -3164,13 +2766,11 @@ gen_dvinit_b(DisasContext *ctx, TCGv rl, TCGv rh, TCGv r1, TCGv r2) TCGv_i64 ret = tcg_temp_new_i64(); if (!has_feature(ctx, TRICORE_FEATURE_131)) { - gen_helper_dvinit_b_13(ret, cpu_env, r1, r2); + gen_helper_dvinit_b_13(ret, tcg_env, r1, r2); } else { - gen_helper_dvinit_b_131(ret, cpu_env, r1, r2); + gen_helper_dvinit_b_131(ret, tcg_env, r1, r2); } tcg_gen_extr_i64_i32(rl, rh, ret); - - tcg_temp_free_i64(ret); } static inline void @@ -3179,13 +2779,11 @@ gen_dvinit_h(DisasContext *ctx, TCGv rl, TCGv rh, TCGv r1, TCGv r2) TCGv_i64 ret = tcg_temp_new_i64(); if (!has_feature(ctx, TRICORE_FEATURE_131)) { - gen_helper_dvinit_h_13(ret, cpu_env, r1, r2); + gen_helper_dvinit_h_13(ret, tcg_env, r1, r2); } else { - gen_helper_dvinit_h_131(ret, cpu_env, r1, r2); + gen_helper_dvinit_h_131(ret, tcg_env, r1, r2); } tcg_gen_extr_i64_i32(rl, rh, ret); - - tcg_temp_free_i64(ret); } static void gen_calc_usb_mul_h(TCGv arg_low, TCGv arg_high) @@ -3200,7 +2798,6 @@ static void gen_calc_usb_mul_h(TCGv arg_low, TCGv arg_high) /* calc SAV bit */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); tcg_gen_movi_tl(cpu_PSW_V, 0); - tcg_temp_free(temp); } static void gen_calc_usb_mulr_h(TCGv arg) @@ -3215,7 +2812,6 @@ static void gen_calc_usb_mulr_h(TCGv arg) tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); /* clear V bit */ tcg_gen_movi_tl(cpu_PSW_V, 0); - tcg_temp_free(temp); } /* helpers for generating program flow micro-ops */ @@ -3235,19 +2831,17 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) gen_save_pc(dest); tcg_gen_lookup_and_goto_ptr(); } + ctx->base.is_jmp = DISAS_NORETURN; } static void generate_trap(DisasContext *ctx, int class, int tin) { - TCGv_i32 classtemp = tcg_const_i32(class); - TCGv_i32 tintemp = tcg_const_i32(tin); + TCGv_i32 classtemp = tcg_constant_i32(class); + TCGv_i32 tintemp = tcg_constant_i32(tin); gen_save_pc(ctx->base.pc_next); - gen_helper_raise_exception_sync(cpu_env, classtemp, tintemp); + gen_helper_raise_exception_sync(tcg_env, classtemp, tintemp); ctx->base.is_jmp = DISAS_NORETURN; - - tcg_temp_free(classtemp); - tcg_temp_free(tintemp); } static inline void gen_branch_cond(DisasContext *ctx, TCGCond cond, TCGv r1, @@ -3265,9 +2859,8 @@ static inline void gen_branch_cond(DisasContext *ctx, TCGCond cond, TCGv r1, static inline void gen_branch_condi(DisasContext *ctx, TCGCond cond, TCGv r1, int r2, int16_t address) { - TCGv temp = tcg_const_i32(r2); + TCGv temp = tcg_constant_i32(r2); gen_branch_cond(ctx, cond, r1, temp, address); - tcg_temp_free(temp); } static void gen_loop(DisasContext *ctx, int r1, int32_t offset) @@ -3289,8 +2882,6 @@ static void gen_fcall_save_ctx(DisasContext *ctx) tcg_gen_qemu_st_tl(cpu_gpr_a[11], temp, ctx->mem_idx, MO_LESL); tcg_gen_movi_tl(cpu_gpr_a[11], ctx->pc_succ_insn); tcg_gen_mov_tl(cpu_gpr_a[10], temp); - - tcg_temp_free(temp); } static void gen_fret(DisasContext *ctx) @@ -3301,10 +2892,7 @@ static void gen_fret(DisasContext *ctx) tcg_gen_qemu_ld_tl(cpu_gpr_a[11], cpu_gpr_a[10], ctx->mem_idx, MO_LESL); tcg_gen_addi_tl(cpu_gpr_a[10], cpu_gpr_a[10], 4); tcg_gen_mov_tl(cpu_PC, temp); - tcg_gen_exit_tb(NULL, 0); - ctx->base.is_jmp = DISAS_NORETURN; - - tcg_temp_free(temp); + ctx->base.is_jmp = DISAS_EXIT; } static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, @@ -3350,13 +2938,11 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, temp = tcg_temp_new(); tcg_gen_andi_tl(temp, cpu_gpr_d[15], 0x1u << constant); gen_branch_condi(ctx, TCG_COND_EQ, temp, 0, offset); - tcg_temp_free(temp); break; case OPC1_16_SBRN_JNZ_T: temp = tcg_temp_new(); tcg_gen_andi_tl(temp, cpu_gpr_d[15], 0x1u << constant); gen_branch_condi(ctx, TCG_COND_NE, temp, 0, offset); - tcg_temp_free(temp); break; /* SBR-format jumps */ case OPC1_16_SBR_JEQ: @@ -3405,12 +2991,12 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, /* SR-format jumps */ case OPC1_16_SR_JI: tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], 0xfffffffe); - tcg_gen_exit_tb(NULL, 0); + ctx->base.is_jmp = DISAS_EXIT; break; case OPC2_32_SYS_RET: case OPC2_16_SR_RET: - gen_helper_ret(cpu_env); - tcg_gen_exit_tb(NULL, 0); + gen_helper_ret(tcg_env); + ctx->base.is_jmp = DISAS_EXIT; break; /* B-format */ case OPC1_32_B_CALLA: @@ -3474,7 +3060,6 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, tcg_gen_addi_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 1); gen_branch_condi(ctx, TCG_COND_NE, temp, constant, offset); } - tcg_temp_free(temp); break; /* BRN format */ case OPCM_32_BRN_JTT: @@ -3488,7 +3073,6 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, } else { gen_branch_condi(ctx, TCG_COND_EQ, temp, 0, offset); } - tcg_temp_free(temp); break; /* BRR Format */ case OPCM_32_BRR_EQ_NEQ: @@ -3553,8 +3137,6 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, tcg_gen_addi_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 1); gen_branch_cond(ctx, TCG_COND_NE, temp, temp2, offset); } - tcg_temp_free(temp); - tcg_temp_free(temp2); break; case OPCM_32_BRR_JNZ: if (MASK_OP_BRR_OP2(ctx->opcode) == OPC2_32_BRR_JNZ_A) { @@ -3566,7 +3148,6 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - ctx->base.is_jmp = DISAS_NORETURN; } @@ -3605,20 +3186,16 @@ static void decode_src_opc(DisasContext *ctx, int op1) cpu_gpr_d[15]); break; case OPC1_16_SRC_CMOV: - temp = tcg_const_tl(0); - temp2 = tcg_const_tl(const4); + temp = tcg_constant_tl(0); + temp2 = tcg_constant_tl(const4); tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[15], temp, temp2, cpu_gpr_d[r1]); - tcg_temp_free(temp); - tcg_temp_free(temp2); break; case OPC1_16_SRC_CMOVN: - temp = tcg_const_tl(0); - temp2 = tcg_const_tl(const4); + temp = tcg_constant_tl(0); + temp2 = tcg_constant_tl(const4); tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[15], temp, temp2, cpu_gpr_d[r1]); - tcg_temp_free(temp); - tcg_temp_free(temp2); break; case OPC1_16_SRC_EQ: tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_gpr_d[15], cpu_gpr_d[r1], @@ -3637,6 +3214,7 @@ static void decode_src_opc(DisasContext *ctx, int op1) break; case OPC1_16_SRC_MOV_E: if (has_feature(ctx, TRICORE_FEATURE_16)) { + CHECK_REG_PAIR(r1); tcg_gen_movi_tl(cpu_gpr_d[r1], const4); tcg_gen_sari_tl(cpu_gpr_d[r1+1], cpu_gpr_d[r1], 31); } else { @@ -3682,16 +3260,14 @@ static void decode_srr_opc(DisasContext *ctx, int op1) tcg_gen_and_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC1_16_SRR_CMOV: - temp = tcg_const_tl(0); + temp = tcg_constant_tl(0); tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr_d[r1], cpu_gpr_d[15], temp, cpu_gpr_d[r2], cpu_gpr_d[r1]); - tcg_temp_free(temp); break; case OPC1_16_SRR_CMOVN: - temp = tcg_const_tl(0); + temp = tcg_constant_tl(0); tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr_d[r1], cpu_gpr_d[15], temp, cpu_gpr_d[r2], cpu_gpr_d[r1]); - tcg_temp_free(temp); break; case OPC1_16_SRR_EQ: tcg_gen_setcond_tl(TCG_COND_EQ, cpu_gpr_d[15], cpu_gpr_d[r1], @@ -3791,7 +3367,11 @@ static void decode_sc_opc(DisasContext *ctx, int op1) tcg_gen_andi_tl(cpu_gpr_d[15], cpu_gpr_d[15], const16); break; case OPC1_16_SC_BISR: - gen_helper_1arg(bisr, const16 & 0xff); + if (ctx->priv == TRICORE_PRIV_SM) { + gen_helper_1arg(bisr, const16 & 0xff); + } else { + generate_trap(ctx, TRAPC_PROT, TIN1_PRIV); + } break; case OPC1_16_SC_LD_A: gen_offset_ld(ctx, cpu_gpr_a[15], cpu_gpr_a[10], const16 * 4, MO_LESL); @@ -3878,7 +3458,7 @@ static void decode_sro_opc(DisasContext *ctx, int op1) gen_offset_ld(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address, MO_UB); break; case OPC1_16_SRO_LD_H: - gen_offset_ld(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address, MO_LESW); + gen_offset_ld(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address * 2, MO_LESW); break; case OPC1_16_SRO_LD_W: gen_offset_ld(ctx, cpu_gpr_d[15], cpu_gpr_a[r2], address * 4, MO_LESL); @@ -3912,9 +3492,8 @@ static void decode_sr_system(DisasContext *ctx) gen_compute_branch(ctx, op2, 0, 0, 0, 0); break; case OPC2_16_SR_RFE: - gen_helper_rfe(cpu_env); - tcg_gen_exit_tb(NULL, 0); - ctx->base.is_jmp = DISAS_NORETURN; + gen_helper_rfe(tcg_env); + ctx->base.is_jmp = DISAS_EXIT; break; case OPC2_16_SR_DEBUG: /* raise EXCP_DEBUG */ @@ -3931,17 +3510,14 @@ static void decode_sr_accu(DisasContext *ctx) { uint32_t op2; uint32_t r1; - TCGv temp; r1 = MASK_OP_SR_S1D(ctx->opcode); op2 = MASK_OP_SR_OP2(ctx->opcode); switch (op2) { case OPC2_16_SR_RSUB: - /* overflow only if r1 = -0x80000000 */ - temp = tcg_const_i32(-0x80000000); - /* calc V bit */ - tcg_gen_setcond_tl(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r1], temp); + /* calc V bit -- overflow only if r1 = -0x80000000 */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r1], -0x80000000); tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); /* calc SV bit */ tcg_gen_or_tl(cpu_PSW_SV, cpu_PSW_SV, cpu_PSW_V); @@ -3952,7 +3528,6 @@ static void decode_sr_accu(DisasContext *ctx) tcg_gen_xor_tl(cpu_PSW_AV, cpu_gpr_d[r1], cpu_PSW_AV); /* calc sav */ tcg_gen_or_tl(cpu_PSW_SAV, cpu_PSW_SAV, cpu_PSW_AV); - tcg_temp_free(temp); break; case OPC2_16_SR_SAT_B: gen_saturate(cpu_gpr_d[r1], cpu_gpr_d[r1], 0x7f, -0x80); @@ -4047,7 +3622,6 @@ static void decode_16Bit_opc(DisasContext *ctx) temp = tcg_temp_new(); tcg_gen_shli_tl(temp, cpu_gpr_d[15], const16); tcg_gen_add_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], temp); - tcg_temp_free(temp); break; /* SLRO-format */ case OPC1_16_SLRO_LD_A: @@ -4219,7 +3793,7 @@ static void decode_abs_ldw(DisasContext *ctx) address = MASK_OP_ABS_OFF18(ctx->opcode); op2 = MASK_OP_ABS_OP2(ctx->opcode); - temp = tcg_const_i32(EA_ABS_FORMAT(address)); + temp = tcg_constant_i32(EA_ABS_FORMAT(address)); switch (op2) { case OPC2_32_ABS_LD_A: @@ -4239,8 +3813,6 @@ static void decode_abs_ldw(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - - tcg_temp_free(temp); } static void decode_abs_ldb(DisasContext *ctx) @@ -4254,7 +3826,7 @@ static void decode_abs_ldb(DisasContext *ctx) address = MASK_OP_ABS_OFF18(ctx->opcode); op2 = MASK_OP_ABS_OP2(ctx->opcode); - temp = tcg_const_i32(EA_ABS_FORMAT(address)); + temp = tcg_constant_i32(EA_ABS_FORMAT(address)); switch (op2) { case OPC2_32_ABS_LD_B: @@ -4272,8 +3844,6 @@ static void decode_abs_ldb(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - - tcg_temp_free(temp); } static void decode_abs_ldst_swap(DisasContext *ctx) @@ -4287,7 +3857,7 @@ static void decode_abs_ldst_swap(DisasContext *ctx) address = MASK_OP_ABS_OFF18(ctx->opcode); op2 = MASK_OP_ABS_OP2(ctx->opcode); - temp = tcg_const_i32(EA_ABS_FORMAT(address)); + temp = tcg_constant_i32(EA_ABS_FORMAT(address)); switch (op2) { case OPC2_32_ABS_LDMST: @@ -4299,8 +3869,6 @@ static void decode_abs_ldst_swap(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - - tcg_temp_free(temp); } static void decode_abs_ldst_context(DisasContext *ctx) @@ -4340,7 +3908,7 @@ static void decode_abs_store(DisasContext *ctx) address = MASK_OP_ABS_OFF18(ctx->opcode); op2 = MASK_OP_ABS_OP2(ctx->opcode); - temp = tcg_const_i32(EA_ABS_FORMAT(address)); + temp = tcg_constant_i32(EA_ABS_FORMAT(address)); switch (op2) { case OPC2_32_ABS_ST_A: @@ -4360,7 +3928,6 @@ static void decode_abs_store(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(temp); } static void decode_abs_storeb_h(DisasContext *ctx) @@ -4374,7 +3941,7 @@ static void decode_abs_storeb_h(DisasContext *ctx) address = MASK_OP_ABS_OFF18(ctx->opcode); op2 = MASK_OP_ABS_OP2(ctx->opcode); - temp = tcg_const_i32(EA_ABS_FORMAT(address)); + temp = tcg_constant_i32(EA_ABS_FORMAT(address)); switch (op2) { case OPC2_32_ABS_ST_B: @@ -4386,7 +3953,6 @@ static void decode_abs_storeb_h(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(temp); } /* Bit-format */ @@ -4486,7 +4052,6 @@ static void decode_bit_insert(DisasContext *ctx) tcg_gen_not_tl(temp, temp); } tcg_gen_deposit_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], temp, pos1, 1); - tcg_temp_free(temp); } static void decode_bit_logical_t2(DisasContext *ctx) @@ -4604,7 +4169,6 @@ static void decode_bit_sh_logic1(DisasContext *ctx) } tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], 1); tcg_gen_add_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], temp); - tcg_temp_free(temp); } static void decode_bit_sh_logic2(DisasContext *ctx) @@ -4645,7 +4209,6 @@ static void decode_bit_sh_logic2(DisasContext *ctx) } tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], 1); tcg_gen_add_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], temp); - tcg_temp_free(temp); } /* BO-format */ @@ -4743,7 +4306,6 @@ static void decode_bo_addrmode_post_pre_base(DisasContext *ctx) tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); gen_st_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); tcg_gen_mov_tl(cpu_gpr_a[r2], temp); - tcg_temp_free(temp); break; case OPC2_32_BO_ST_DA_SHORTOFF: CHECK_REG_PAIR(r1); @@ -4761,7 +4323,6 @@ static void decode_bo_addrmode_post_pre_base(DisasContext *ctx) tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); gen_st_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); tcg_gen_mov_tl(cpu_gpr_a[r2], temp); - tcg_temp_free(temp); break; case OPC2_32_BO_ST_H_SHORTOFF: gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUW); @@ -4778,7 +4339,6 @@ static void decode_bo_addrmode_post_pre_base(DisasContext *ctx) temp = tcg_temp_new(); tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); gen_offset_st(ctx, temp, cpu_gpr_a[r2], off10, MO_LEUW); - tcg_temp_free(temp); break; case OPC2_32_BO_ST_Q_POSTINC: temp = tcg_temp_new(); @@ -4786,13 +4346,11 @@ static void decode_bo_addrmode_post_pre_base(DisasContext *ctx) tcg_gen_qemu_st_tl(temp, cpu_gpr_a[r2], ctx->mem_idx, MO_LEUW); tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); - tcg_temp_free(temp); break; case OPC2_32_BO_ST_Q_PREINC: temp = tcg_temp_new(); tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); gen_st_preincr(ctx, temp, cpu_gpr_a[r2], off10, MO_LEUW); - tcg_temp_free(temp); break; case OPC2_32_BO_ST_W_SHORTOFF: gen_offset_st(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LEUL); @@ -4815,7 +4373,7 @@ static void decode_bo_addrmode_bitreverse_circular(DisasContext *ctx) uint32_t op2; uint32_t off10; int32_t r1, r2; - TCGv temp, temp2, temp3; + TCGv temp, temp2, t_off10; r1 = MASK_OP_BO_S1D(ctx->opcode); r2 = MASK_OP_BO_S2(ctx->opcode); @@ -4824,7 +4382,7 @@ static void decode_bo_addrmode_bitreverse_circular(DisasContext *ctx) temp = tcg_temp_new(); temp2 = tcg_temp_new(); - temp3 = tcg_const_i32(off10); + t_off10 = tcg_constant_i32(off10); CHECK_REG_PAIR(r2); tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); @@ -4838,7 +4396,7 @@ static void decode_bo_addrmode_bitreverse_circular(DisasContext *ctx) case OPC2_32_BO_CACHEA_WI_CIRC: case OPC2_32_BO_CACHEA_W_CIRC: case OPC2_32_BO_CACHEA_I_CIRC: - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_ST_A_BR: tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); @@ -4846,7 +4404,7 @@ static void decode_bo_addrmode_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_ST_A_CIRC: tcg_gen_qemu_st_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_ST_B_BR: tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); @@ -4854,7 +4412,7 @@ static void decode_bo_addrmode_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_ST_B_CIRC: tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_ST_D_BR: CHECK_REG_PAIR(r1); @@ -4869,7 +4427,7 @@ static void decode_bo_addrmode_bitreverse_circular(DisasContext *ctx) tcg_gen_rem_tl(temp, temp, temp2); tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); tcg_gen_qemu_st_tl(cpu_gpr_d[r1+1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_ST_DA_BR: CHECK_REG_PAIR(r1); @@ -4884,7 +4442,7 @@ static void decode_bo_addrmode_bitreverse_circular(DisasContext *ctx) tcg_gen_rem_tl(temp, temp, temp2); tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); tcg_gen_qemu_st_tl(cpu_gpr_a[r1+1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_ST_H_BR: tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); @@ -4892,7 +4450,7 @@ static void decode_bo_addrmode_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_ST_H_CIRC: tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_ST_Q_BR: tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); @@ -4902,7 +4460,7 @@ static void decode_bo_addrmode_bitreverse_circular(DisasContext *ctx) case OPC2_32_BO_ST_Q_CIRC: tcg_gen_shri_tl(temp, cpu_gpr_d[r1], 16); tcg_gen_qemu_st_tl(temp, temp2, ctx->mem_idx, MO_LEUW); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_ST_W_BR: tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); @@ -4910,14 +4468,11 @@ static void decode_bo_addrmode_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_ST_W_CIRC: tcg_gen_qemu_st_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); } static void decode_bo_addrmode_ld_post_pre_base(DisasContext *ctx) @@ -4964,7 +4519,7 @@ static void decode_bo_addrmode_ld_post_pre_base(DisasContext *ctx) tcg_gen_addi_tl(cpu_gpr_a[r2], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_LD_BU_PREINC: - gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_SB); + gen_ld_preincr(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_UB); break; case OPC2_32_BO_LD_D_SHORTOFF: CHECK_REG_PAIR(r1); @@ -4982,7 +4537,6 @@ static void decode_bo_addrmode_ld_post_pre_base(DisasContext *ctx) tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); gen_ld_2regs_64(cpu_gpr_d[r1+1], cpu_gpr_d[r1], temp, ctx); tcg_gen_mov_tl(cpu_gpr_a[r2], temp); - tcg_temp_free(temp); break; case OPC2_32_BO_LD_DA_SHORTOFF: CHECK_REG_PAIR(r1); @@ -5000,7 +4554,6 @@ static void decode_bo_addrmode_ld_post_pre_base(DisasContext *ctx) tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); gen_ld_2regs_64(cpu_gpr_a[r1+1], cpu_gpr_a[r1], temp, ctx); tcg_gen_mov_tl(cpu_gpr_a[r2], temp); - tcg_temp_free(temp); break; case OPC2_32_BO_LD_H_SHORTOFF: gen_offset_ld(ctx, cpu_gpr_d[r1], cpu_gpr_a[r2], off10, MO_LESW); @@ -5059,8 +4612,7 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) uint32_t op2; uint32_t off10; int r1, r2; - - TCGv temp, temp2, temp3; + TCGv temp, temp2, t_off10; r1 = MASK_OP_BO_S1D(ctx->opcode); r2 = MASK_OP_BO_S2(ctx->opcode); @@ -5069,7 +4621,7 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) temp = tcg_temp_new(); temp2 = tcg_temp_new(); - temp3 = tcg_const_i32(off10); + t_off10 = tcg_constant_i32(off10); CHECK_REG_PAIR(r2); tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); @@ -5082,7 +4634,7 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_LD_A_CIRC: tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_LD_B_BR: tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_SB); @@ -5090,7 +4642,7 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_LD_B_CIRC: tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_SB); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_LD_BU_BR: tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); @@ -5098,7 +4650,7 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_LD_BU_CIRC: tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_UB); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_LD_D_BR: CHECK_REG_PAIR(r1); @@ -5113,7 +4665,7 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) tcg_gen_rem_tl(temp, temp, temp2); tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); tcg_gen_qemu_ld_tl(cpu_gpr_d[r1+1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_LD_DA_BR: CHECK_REG_PAIR(r1); @@ -5128,7 +4680,7 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) tcg_gen_rem_tl(temp, temp, temp2); tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); tcg_gen_qemu_ld_tl(cpu_gpr_a[r1+1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_LD_H_BR: tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LESW); @@ -5136,7 +4688,7 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_LD_H_CIRC: tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LESW); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_LD_HU_BR: tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); @@ -5144,7 +4696,7 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_LD_HU_CIRC: tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_LD_Q_BR: tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); @@ -5154,7 +4706,7 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) case OPC2_32_BO_LD_Q_CIRC: tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUW); tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_LD_W_BR: tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); @@ -5162,14 +4714,11 @@ static void decode_bo_addrmode_ld_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_LD_W_CIRC: tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp2, ctx->mem_idx, MO_LEUL); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); } static void decode_bo_addrmode_stctx_post_pre_base(DisasContext *ctx) @@ -5178,7 +4727,7 @@ static void decode_bo_addrmode_stctx_post_pre_base(DisasContext *ctx) uint32_t off10; int r1, r2; - TCGv temp, temp2; + TCGv temp; r1 = MASK_OP_BO_S1D(ctx->opcode); r2 = MASK_OP_BO_S2(ctx->opcode); @@ -5187,12 +4736,11 @@ static void decode_bo_addrmode_stctx_post_pre_base(DisasContext *ctx) temp = tcg_temp_new(); - temp2 = tcg_temp_new(); switch (op2) { case OPC2_32_BO_LDLCX_SHORTOFF: tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_helper_ldlcx(cpu_env, temp); + gen_helper_ldlcx(tcg_env, temp); break; case OPC2_32_BO_LDMST_SHORTOFF: tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); @@ -5208,18 +4756,18 @@ static void decode_bo_addrmode_stctx_post_pre_base(DisasContext *ctx) break; case OPC2_32_BO_LDUCX_SHORTOFF: tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_helper_lducx(cpu_env, temp); + gen_helper_lducx(tcg_env, temp); break; case OPC2_32_BO_LEA_SHORTOFF: tcg_gen_addi_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], off10); break; case OPC2_32_BO_STLCX_SHORTOFF: tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_helper_stlcx(cpu_env, temp); + gen_helper_stlcx(tcg_env, temp); break; case OPC2_32_BO_STUCX_SHORTOFF: tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); - gen_helper_stucx(cpu_env, temp); + gen_helper_stucx(tcg_env, temp); break; case OPC2_32_BO_SWAP_W_SHORTOFF: tcg_gen_addi_tl(temp, cpu_gpr_a[r2], off10); @@ -5260,8 +4808,6 @@ static void decode_bo_addrmode_stctx_post_pre_base(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(temp); - tcg_temp_free(temp2); } static void decode_bo_addrmode_ldmst_bitreverse_circular(DisasContext *ctx) @@ -5269,8 +4815,7 @@ static void decode_bo_addrmode_ldmst_bitreverse_circular(DisasContext *ctx) uint32_t op2; uint32_t off10; int r1, r2; - - TCGv temp, temp2, temp3; + TCGv temp, temp2, t_off10; r1 = MASK_OP_BO_S1D(ctx->opcode); r2 = MASK_OP_BO_S2(ctx->opcode); @@ -5279,7 +4824,7 @@ static void decode_bo_addrmode_ldmst_bitreverse_circular(DisasContext *ctx) temp = tcg_temp_new(); temp2 = tcg_temp_new(); - temp3 = tcg_const_i32(off10); + t_off10 = tcg_constant_i32(off10); CHECK_REG_PAIR(r2); tcg_gen_ext16u_tl(temp, cpu_gpr_a[r2+1]); tcg_gen_add_tl(temp2, cpu_gpr_a[r2], temp); @@ -5291,7 +4836,7 @@ static void decode_bo_addrmode_ldmst_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_LDMST_CIRC: gen_ldmst(ctx, r1, temp2); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_SWAP_W_BR: gen_swap(ctx, r1, temp2); @@ -5299,7 +4844,7 @@ static void decode_bo_addrmode_ldmst_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_SWAP_W_CIRC: gen_swap(ctx, r1, temp2); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_CMPSWAP_W_BR: gen_cmpswap(ctx, r1, temp2); @@ -5307,7 +4852,7 @@ static void decode_bo_addrmode_ldmst_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_CMPSWAP_W_CIRC: gen_cmpswap(ctx, r1, temp2); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; case OPC2_32_BO_SWAPMSK_W_BR: gen_swapmsk(ctx, r1, temp2); @@ -5315,15 +4860,11 @@ static void decode_bo_addrmode_ldmst_bitreverse_circular(DisasContext *ctx) break; case OPC2_32_BO_SWAPMSK_W_CIRC: gen_swapmsk(ctx, r1, temp2); - gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], temp3); + gen_helper_circ_update(cpu_gpr_a[r2+1], cpu_gpr_a[r2+1], t_off10); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); } static void decode_bol_opc(DisasContext *ctx, int32_t op1) @@ -5341,13 +4882,11 @@ static void decode_bol_opc(DisasContext *ctx, int32_t op1) temp = tcg_temp_new(); tcg_gen_addi_tl(temp, cpu_gpr_a[r2], address); tcg_gen_qemu_ld_tl(cpu_gpr_a[r1], temp, ctx->mem_idx, MO_LEUL); - tcg_temp_free(temp); break; case OPC1_32_BOL_LD_W_LONGOFF: temp = tcg_temp_new(); tcg_gen_addi_tl(temp, cpu_gpr_a[r2], address); tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUL); - tcg_temp_free(temp); break; case OPC1_32_BOL_LEA_LONGOFF: tcg_gen_addi_tl(cpu_gpr_a[r1], cpu_gpr_a[r2], address); @@ -5422,8 +4961,6 @@ static void decode_rc_logical_shift(DisasContext *ctx) const9 = MASK_OP_RC_CONST9(ctx->opcode); op2 = MASK_OP_RC_OP2(ctx->opcode); - temp = tcg_temp_new(); - switch (op2) { case OPC2_32_RC_AND: tcg_gen_andi_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); @@ -5432,10 +4969,12 @@ static void decode_rc_logical_shift(DisasContext *ctx) tcg_gen_andi_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], ~const9); break; case OPC2_32_RC_NAND: + temp = tcg_temp_new(); tcg_gen_movi_tl(temp, const9); tcg_gen_nand_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], temp); break; case OPC2_32_RC_NOR: + temp = tcg_temp_new(); tcg_gen_movi_tl(temp, const9); tcg_gen_nor_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], temp); break; @@ -5471,10 +5010,17 @@ static void decode_rc_logical_shift(DisasContext *ctx) case OPC2_32_RC_XOR: tcg_gen_xori_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], const9); break; + case OPC2_32_RC_SHUFFLE: + if (has_feature(ctx, TRICORE_FEATURE_162)) { + temp = tcg_constant_i32(const9); + gen_helper_shuffle(cpu_gpr_d[r2], cpu_gpr_d[r1], temp); + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } + break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(temp); } static void decode_rc_accumulator(DisasContext *ctx) @@ -5674,7 +5220,6 @@ static void decode_rc_accumulator(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(temp); } static void decode_rc_serviceroutine(DisasContext *ctx) @@ -5687,10 +5232,14 @@ static void decode_rc_serviceroutine(DisasContext *ctx) switch (op2) { case OPC2_32_RC_BISR: - gen_helper_1arg(bisr, const9); + if (ctx->priv == TRICORE_PRIV_SM) { + gen_helper_1arg(bisr, const9); + } else { + generate_trap(ctx, TRAPC_PROT, TIN1_PRIV); + } break; case OPC2_32_RC_SYSCALL: - /* TODO: Add exception generation */ + generate_trap(ctx, TRAPC_SYSCALL, const9 & 0xff); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -5760,11 +5309,13 @@ static void decode_rcpw_insert(DisasContext *ctx) } break; case OPC2_32_RCPW_INSERT: + /* tcg_gen_deposit_tl() does not handle the case of width = 0 */ + if (width == 0) { + tcg_gen_mov_tl(cpu_gpr_d[r2], cpu_gpr_d[r1]); /* if pos + width > 32 undefined result */ - if (pos + width <= 32) { - temp = tcg_const_i32(const4); + } else if (pos + width <= 32) { + temp = tcg_constant_i32(const4); tcg_gen_deposit_tl(cpu_gpr_d[r2], cpu_gpr_d[r1], temp, pos, width); - tcg_temp_free(temp); } break; default: @@ -5794,27 +5345,24 @@ static void decode_rcrw_insert(DisasContext *ctx) switch (op2) { case OPC2_32_RCRW_IMASK: - tcg_gen_andi_tl(temp, cpu_gpr_d[r4], 0x1f); + CHECK_REG_PAIR(r4); + tcg_gen_andi_tl(temp, cpu_gpr_d[r3], 0x1f); tcg_gen_movi_tl(temp2, (1 << width) - 1); - tcg_gen_shl_tl(cpu_gpr_d[r3 + 1], temp2, temp); + tcg_gen_shl_tl(cpu_gpr_d[r4 + 1], temp2, temp); tcg_gen_movi_tl(temp2, const4); - tcg_gen_shl_tl(cpu_gpr_d[r3], temp2, temp); + tcg_gen_shl_tl(cpu_gpr_d[r4], temp2, temp); break; case OPC2_32_RCRW_INSERT: temp3 = tcg_temp_new(); tcg_gen_movi_tl(temp, width); tcg_gen_movi_tl(temp2, const4); - tcg_gen_andi_tl(temp3, cpu_gpr_d[r4], 0x1f); - gen_insert(cpu_gpr_d[r3], cpu_gpr_d[r1], temp2, temp, temp3); - - tcg_temp_free(temp3); + tcg_gen_andi_tl(temp3, cpu_gpr_d[r3], 0x1f); + gen_insert(cpu_gpr_d[r4], cpu_gpr_d[r1], temp2, temp, temp3); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(temp); - tcg_temp_free(temp2); } /* RCR format */ @@ -5843,20 +5391,16 @@ static void decode_rcr_cond_select(DisasContext *ctx) cpu_gpr_d[r3]); break; case OPC2_32_RCR_SEL: - temp = tcg_const_i32(0); - temp2 = tcg_const_i32(const9); + temp = tcg_constant_i32(0); + temp2 = tcg_constant_i32(const9); tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, cpu_gpr_d[r1], temp2); - tcg_temp_free(temp); - tcg_temp_free(temp2); break; case OPC2_32_RCR_SELN: - temp = tcg_const_i32(0); - temp2 = tcg_const_i32(const9); + temp = tcg_constant_i32(0); + temp2 = tcg_constant_i32(const9); tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, cpu_gpr_d[r1], temp2); - tcg_temp_free(temp); - tcg_temp_free(temp2); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -6048,44 +5592,44 @@ static void decode_rr_accumulator(DisasContext *ctx) gen_abs(cpu_gpr_d[r3], cpu_gpr_d[r2]); break; case OPC2_32_RR_ABS_B: - gen_helper_abs_b(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r2]); + gen_helper_abs_b(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r2]); break; case OPC2_32_RR_ABS_H: - gen_helper_abs_h(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r2]); + gen_helper_abs_h(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r2]); break; case OPC2_32_RR_ABSDIF: gen_absdif(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_ABSDIF_B: - gen_helper_absdif_b(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + gen_helper_absdif_b(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_ABSDIF_H: - gen_helper_absdif_h(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + gen_helper_absdif_h(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_ABSDIFS: - gen_helper_absdif_ssov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + gen_helper_absdif_ssov(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_ABSDIFS_H: - gen_helper_absdif_h_ssov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + gen_helper_absdif_h_ssov(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_ABSS: - gen_helper_abs_ssov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r2]); + gen_helper_abs_ssov(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r2]); break; case OPC2_32_RR_ABSS_H: - gen_helper_abs_h_ssov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r2]); + gen_helper_abs_h_ssov(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r2]); break; case OPC2_32_RR_ADD: gen_add_d(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_ADD_B: - gen_helper_add_b(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + gen_helper_add_b(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_ADD_H: - gen_helper_add_h(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + gen_helper_add_h(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_ADDC: gen_addc_CC(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); @@ -6094,15 +5638,15 @@ static void decode_rr_accumulator(DisasContext *ctx) gen_adds(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_ADDS_H: - gen_helper_add_h_ssov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + gen_helper_add_h_ssov(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_ADDS_HU: - gen_helper_add_h_suov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + gen_helper_add_h_suov(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_ADDS_U: - gen_helper_add_suov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + gen_helper_add_suov(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_ADDX: @@ -6143,7 +5687,8 @@ static void decode_rr_accumulator(DisasContext *ctx) gen_helper_eq_h(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_EQ_W: - gen_cond_w(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_negsetcond_tl(TCG_COND_EQ, cpu_gpr_d[r3], + cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_EQANY_B: gen_helper_eqany_b(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); @@ -6180,10 +5725,12 @@ static void decode_rr_accumulator(DisasContext *ctx) gen_helper_lt_hu(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_LT_W: - gen_cond_w(TCG_COND_LT, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_negsetcond_tl(TCG_COND_LT, cpu_gpr_d[r3], + cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_LT_WU: - gen_cond_w(TCG_COND_LTU, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + tcg_gen_negsetcond_tl(TCG_COND_LTU, cpu_gpr_d[r3], + cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_MAX: tcg_gen_movcond_tl(TCG_COND_GT, cpu_gpr_d[r3], cpu_gpr_d[r1], @@ -6236,8 +5783,6 @@ static void decode_rr_accumulator(DisasContext *ctx) tcg_gen_mov_tl(temp, cpu_gpr_d[r1]); tcg_gen_mov_tl(cpu_gpr_d[r3], cpu_gpr_d[r2]); tcg_gen_mov_tl(cpu_gpr_d[r3 + 1], temp); - - tcg_temp_free(temp); } else { generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } @@ -6319,10 +5864,10 @@ static void decode_rr_accumulator(DisasContext *ctx) gen_sub_d(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_SUB_B: - gen_helper_sub_b(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + gen_helper_sub_b(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_SUB_H: - gen_helper_sub_h(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + gen_helper_sub_h(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_SUBC: gen_subc_CC(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); @@ -6334,11 +5879,11 @@ static void decode_rr_accumulator(DisasContext *ctx) gen_subsu(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_SUBS_H: - gen_helper_sub_h_ssov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + gen_helper_sub_h_ssov(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_SUBS_HU: - gen_helper_sub_h_suov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + gen_helper_sub_h_suov(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_SUBX: @@ -6377,13 +5922,10 @@ static void decode_rr_logical_shift(DisasContext *ctx) { uint32_t op2; int r3, r2, r1; - TCGv temp; r3 = MASK_OP_RR_D(ctx->opcode); r2 = MASK_OP_RR_S2(ctx->opcode); r1 = MASK_OP_RR_S1(ctx->opcode); - - temp = tcg_temp_new(); op2 = MASK_OP_RR_OP2(ctx->opcode); switch (op2) { @@ -6431,7 +5973,7 @@ static void decode_rr_logical_shift(DisasContext *ctx) gen_helper_sh_h(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_SHA: - gen_helper_sha(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + gen_helper_sha(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_SHA_H: gen_helper_sha_h(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); @@ -6448,7 +5990,6 @@ static void decode_rr_logical_shift(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(temp); } static void decode_rr_address(DisasContext *ctx) @@ -6471,14 +6012,12 @@ static void decode_rr_address(DisasContext *ctx) temp = tcg_temp_new(); tcg_gen_shli_tl(temp, cpu_gpr_d[r1], n); tcg_gen_add_tl(cpu_gpr_a[r3], cpu_gpr_a[r2], temp); - tcg_temp_free(temp); break; case OPC2_32_RR_ADDSC_AT: temp = tcg_temp_new(); tcg_gen_sari_tl(temp, cpu_gpr_d[r1], 3); tcg_gen_add_tl(temp, cpu_gpr_a[r2], temp); tcg_gen_andi_tl(cpu_gpr_a[r3], temp, 0xFFFFFFFC); - tcg_temp_free(temp); break; case OPC2_32_RR_EQ_A: tcg_gen_setcond_tl(TCG_COND_EQ, cpu_gpr_d[r3], cpu_gpr_a[r1], @@ -6532,8 +6071,8 @@ static void decode_rr_idirect(DisasContext *ctx) tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], ~0x1); break; case OPC2_32_RR_JLI: - tcg_gen_movi_tl(cpu_gpr_a[11], ctx->pc_succ_insn); tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], ~0x1); + tcg_gen_movi_tl(cpu_gpr_a[11], ctx->pc_succ_insn); break; case OPC2_32_RR_CALLI: gen_helper_1arg(call, ctx->pc_succ_insn); @@ -6545,9 +6084,9 @@ static void decode_rr_idirect(DisasContext *ctx) break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + return; } - tcg_gen_exit_tb(NULL, 0); - ctx->base.is_jmp = DISAS_NORETURN; + ctx->base.is_jmp = DISAS_JUMP; } static void decode_rr_divide(DisasContext *ctx) @@ -6598,10 +6137,6 @@ static void decode_rr_divide(DisasContext *ctx) /* write result */ tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], 24); tcg_gen_mov_tl(cpu_gpr_d[r3+1], temp3); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); break; case OPC2_32_RR_DVINIT_H: CHECK_REG_PAIR(r3); @@ -6631,9 +6166,6 @@ static void decode_rr_divide(DisasContext *ctx) /* write result */ tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], 16); tcg_gen_mov_tl(cpu_gpr_d[r3+1], temp3); - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); break; case OPC2_32_RR_DVINIT: temp = tcg_temp_new(); @@ -6655,10 +6187,9 @@ static void decode_rr_divide(DisasContext *ctx) tcg_gen_mov_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); /* sign extend to high reg */ tcg_gen_sari_tl(cpu_gpr_d[r3+1], cpu_gpr_d[r1], 31); - tcg_temp_free(temp); - tcg_temp_free(temp2); break; case OPC2_32_RR_DVINIT_U: + CHECK_REG_PAIR(r3); /* overflow = (D[b] == 0) */ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_PSW_V, cpu_gpr_d[r2], 0); tcg_gen_shli_tl(cpu_PSW_V, cpu_PSW_V, 31); @@ -6678,15 +6209,38 @@ static void decode_rr_divide(DisasContext *ctx) CHECK_REG_PAIR(r3); gen_unpack(cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1]); break; - case OPC2_32_RR_CRC32: + case OPC2_32_RR_CRC32_B: + if (has_feature(ctx, TRICORE_FEATURE_162)) { + gen_helper_crc32b(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } + break; + case OPC2_32_RR_CRC32: /* CRC32B.W in 1.6.2 */ if (has_feature(ctx, TRICORE_FEATURE_161)) { - gen_helper_crc32(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + gen_helper_crc32_be(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } + break; + case OPC2_32_RR_CRC32L_W: + if (has_feature(ctx, TRICORE_FEATURE_162)) { + gen_helper_crc32_le(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2]); + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } + break; + + case OPC2_32_RR_POPCNT_W: + if (has_feature(ctx, TRICORE_FEATURE_162)) { + tcg_gen_ctpop_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); } else { generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } break; case OPC2_32_RR_DIV: if (has_feature(ctx, TRICORE_FEATURE_16)) { + CHECK_REG_PAIR(r3); GEN_HELPER_RR(divide, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2]); } else { @@ -6695,6 +6249,7 @@ static void decode_rr_divide(DisasContext *ctx) break; case OPC2_32_RR_DIV_U: if (has_feature(ctx, TRICORE_FEATURE_16)) { + CHECK_REG_PAIR(r3); GEN_HELPER_RR(divide_u, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1], cpu_gpr_d[r2]); } else { @@ -6702,34 +6257,55 @@ static void decode_rr_divide(DisasContext *ctx) } break; case OPC2_32_RR_MUL_F: - gen_helper_fmul(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + gen_helper_fmul(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_DIV_F: - gen_helper_fdiv(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + gen_helper_fdiv(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_FTOHP: + if (has_feature(ctx, TRICORE_FEATURE_162)) { + gen_helper_ftohp(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1]); + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } + break; + case OPC2_32_RR_HPTOF: + if (has_feature(ctx, TRICORE_FEATURE_162)) { + gen_helper_hptof(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1]); + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } break; case OPC2_32_RR_CMP_F: - gen_helper_fcmp(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + gen_helper_fcmp(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR_FTOI: - gen_helper_ftoi(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1]); + gen_helper_ftoi(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1]); break; case OPC2_32_RR_ITOF: - gen_helper_itof(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1]); + gen_helper_itof(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1]); + break; + case OPC2_32_RR_FTOU: + gen_helper_ftou(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1]); break; case OPC2_32_RR_FTOUZ: - gen_helper_ftouz(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1]); + if (has_feature(ctx, TRICORE_FEATURE_131)) { + gen_helper_ftouz(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1]); + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } break; case OPC2_32_RR_UPDFL: - gen_helper_updfl(cpu_env, cpu_gpr_d[r1]); + gen_helper_updfl(tcg_env, cpu_gpr_d[r1]); break; case OPC2_32_RR_UTOF: - gen_helper_utof(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1]); + gen_helper_utof(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1]); break; case OPC2_32_RR_FTOIZ: - gen_helper_ftoiz(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1]); + gen_helper_ftoiz(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1]); break; case OPC2_32_RR_QSEED_F: - gen_helper_qseed(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1]); + gen_helper_qseed(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1]); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -6748,7 +6324,7 @@ static void decode_rr1_mul(DisasContext *ctx) r1 = MASK_OP_RR1_S1(ctx->opcode); r2 = MASK_OP_RR1_S2(ctx->opcode); r3 = MASK_OP_RR1_D(ctx->opcode); - n = tcg_const_i32(MASK_OP_RR1_N(ctx->opcode)); + n = tcg_constant_i32(MASK_OP_RR1_N(ctx->opcode)); op2 = MASK_OP_RR1_OP2(ctx->opcode); switch (op2) { @@ -6758,7 +6334,6 @@ static void decode_rr1_mul(DisasContext *ctx) GEN_HELPER_LL(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); - tcg_temp_free_i64(temp64); break; case OPC2_32_RR1_MUL_H_32_LU: temp64 = tcg_temp_new_i64(); @@ -6766,7 +6341,6 @@ static void decode_rr1_mul(DisasContext *ctx) GEN_HELPER_LU(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); - tcg_temp_free_i64(temp64); break; case OPC2_32_RR1_MUL_H_32_UL: temp64 = tcg_temp_new_i64(); @@ -6774,7 +6348,6 @@ static void decode_rr1_mul(DisasContext *ctx) GEN_HELPER_UL(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); - tcg_temp_free_i64(temp64); break; case OPC2_32_RR1_MUL_H_32_UU: temp64 = tcg_temp_new_i64(); @@ -6782,7 +6355,6 @@ static void decode_rr1_mul(DisasContext *ctx) GEN_HELPER_UU(mul_h, temp64, cpu_gpr_d[r1], cpu_gpr_d[r2], n); tcg_gen_extr_i64_i32(cpu_gpr_d[r3], cpu_gpr_d[r3+1], temp64); gen_calc_usb_mul_h(cpu_gpr_d[r3], cpu_gpr_d[r3+1]); - tcg_temp_free_i64(temp64); break; case OPC2_32_RR1_MULM_H_64_LL: temp64 = tcg_temp_new_i64(); @@ -6793,7 +6365,6 @@ static void decode_rr1_mul(DisasContext *ctx) tcg_gen_movi_tl(cpu_PSW_V, 0); /* reset AV bit */ tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); - tcg_temp_free_i64(temp64); break; case OPC2_32_RR1_MULM_H_64_LU: temp64 = tcg_temp_new_i64(); @@ -6804,7 +6375,6 @@ static void decode_rr1_mul(DisasContext *ctx) tcg_gen_movi_tl(cpu_PSW_V, 0); /* reset AV bit */ tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); - tcg_temp_free_i64(temp64); break; case OPC2_32_RR1_MULM_H_64_UL: temp64 = tcg_temp_new_i64(); @@ -6815,7 +6385,6 @@ static void decode_rr1_mul(DisasContext *ctx) tcg_gen_movi_tl(cpu_PSW_V, 0); /* reset AV bit */ tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); - tcg_temp_free_i64(temp64); break; case OPC2_32_RR1_MULM_H_64_UU: temp64 = tcg_temp_new_i64(); @@ -6826,8 +6395,6 @@ static void decode_rr1_mul(DisasContext *ctx) tcg_gen_movi_tl(cpu_PSW_V, 0); /* reset AV bit */ tcg_gen_mov_tl(cpu_PSW_AV, cpu_PSW_V); - tcg_temp_free_i64(temp64); - break; case OPC2_32_RR1_MULR_H_16_LL: GEN_HELPER_LL(mulr_h, cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], n); @@ -6848,7 +6415,6 @@ static void decode_rr1_mul(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(n); } static void decode_rr1_mulq(DisasContext *ctx) @@ -6918,8 +6484,6 @@ static void decode_rr1_mulq(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(temp); - tcg_temp_free(temp2); } /* RR2 format */ @@ -6942,7 +6506,7 @@ static void decode_rr2_mul(DisasContext *ctx) cpu_gpr_d[r2]); break; case OPC2_32_RR2_MULS_32: - gen_helper_mul_ssov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + gen_helper_mul_ssov(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; case OPC2_32_RR2_MUL_U_64: @@ -6951,7 +6515,7 @@ static void decode_rr2_mul(DisasContext *ctx) cpu_gpr_d[r2]); break; case OPC2_32_RR2_MULS_U_32: - gen_helper_mul_suov(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], + gen_helper_mul_suov(cpu_gpr_d[r3], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); break; default: @@ -6977,28 +6541,16 @@ static void decode_rrpw_extract_insert(DisasContext *ctx) switch (op2) { case OPC2_32_RRPW_EXTR: if (width == 0) { - tcg_gen_movi_tl(cpu_gpr_d[r3], 0); - break; - } - - if (pos + width <= 32) { - /* optimize special cases */ - if ((pos == 0) && (width == 8)) { - tcg_gen_ext8s_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); - } else if ((pos == 0) && (width == 16)) { - tcg_gen_ext16s_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); - } else { - tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], 32 - pos - width); - tcg_gen_sari_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], 32 - width); - } + tcg_gen_movi_tl(cpu_gpr_d[r3], 0); + } else if (pos + width <= 32) { + tcg_gen_sextract_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], pos, width); } break; case OPC2_32_RRPW_EXTR_U: if (width == 0) { tcg_gen_movi_tl(cpu_gpr_d[r3], 0); } else { - tcg_gen_shri_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], pos); - tcg_gen_andi_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], ~0u >> (32-width)); + tcg_gen_extract_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], pos, width); } break; case OPC2_32_RRPW_IMASK: @@ -7009,12 +6561,14 @@ static void decode_rrpw_extract_insert(DisasContext *ctx) tcg_gen_movi_tl(temp, ((1u << width) - 1) << pos); tcg_gen_shli_tl(cpu_gpr_d[r3], cpu_gpr_d[r2], pos); tcg_gen_mov_tl(cpu_gpr_d[r3 + 1], temp); - tcg_temp_free(temp); } break; case OPC2_32_RRPW_INSERT: - if (pos + width <= 32) { + /* tcg_gen_deposit_tl() does not handle the case of width = 0 */ + if (width == 0) { + tcg_gen_mov_tl(cpu_gpr_d[r3], cpu_gpr_d[r1]); + } else if (pos + width <= 32) { tcg_gen_deposit_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], pos, width); } @@ -7055,16 +6609,14 @@ static void decode_rrr_cond_select(DisasContext *ctx) cpu_gpr_d[r3]); break; case OPC2_32_RRR_SEL: - temp = tcg_const_i32(0); + temp = tcg_constant_i32(0); tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, cpu_gpr_d[r1], cpu_gpr_d[r2]); - tcg_temp_free(temp); break; case OPC2_32_RRR_SELN: - temp = tcg_const_i32(0); + temp = tcg_constant_i32(0); tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr_d[r4], cpu_gpr_d[r3], temp, cpu_gpr_d[r1], cpu_gpr_d[r2]); - tcg_temp_free(temp); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); @@ -7131,18 +6683,26 @@ static void decode_rrr_divide(DisasContext *ctx) gen_helper_pack(cpu_gpr_d[r4], cpu_PSW_C, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1]); break; + case OPC2_32_RRR_CRCN: + if (has_feature(ctx, TRICORE_FEATURE_162)) { + gen_helper_crcn(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r2], + cpu_gpr_d[r3]); + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } + break; case OPC2_32_RRR_ADD_F: - gen_helper_fadd(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r3]); + gen_helper_fadd(cpu_gpr_d[r4], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r3]); break; case OPC2_32_RRR_SUB_F: - gen_helper_fsub(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r3]); + gen_helper_fsub(cpu_gpr_d[r4], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r3]); break; case OPC2_32_RRR_MADD_F: - gen_helper_fmadd(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], + gen_helper_fmadd(cpu_gpr_d[r4], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2], cpu_gpr_d[r3]); break; case OPC2_32_RRR_MSUB_F: - gen_helper_fmsub(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], + gen_helper_fmsub(cpu_gpr_d[r4], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r2], cpu_gpr_d[r3]); break; default: @@ -7173,7 +6733,7 @@ static void decode_rrr2_madd(DisasContext *ctx) cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MADDS_32: - gen_helper_madd32_ssov(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], + gen_helper_madd32_ssov(cpu_gpr_d[r4], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MADDS_64: @@ -7189,7 +6749,7 @@ static void decode_rrr2_madd(DisasContext *ctx) cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MADDS_U_32: - gen_helper_madd32_suov(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], + gen_helper_madd32_suov(cpu_gpr_d[r4], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MADDS_U_64: @@ -7226,7 +6786,7 @@ static void decode_rrr2_msub(DisasContext *ctx) cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MSUBS_32: - gen_helper_msub32_ssov(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], + gen_helper_msub32_ssov(cpu_gpr_d[r4], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MSUBS_64: @@ -7236,11 +6796,13 @@ static void decode_rrr2_msub(DisasContext *ctx) cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MSUB_U_64: + CHECK_REG_PAIR(r4); + CHECK_REG_PAIR(r3); gen_msubu64_d(cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MSUBS_U_32: - gen_helper_msub32_suov(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], + gen_helper_msub32_suov(cpu_gpr_d[r4], tcg_env, cpu_gpr_d[r1], cpu_gpr_d[r3], cpu_gpr_d[r2]); break; case OPC2_32_RRR2_MSUBS_U_64: @@ -7414,7 +6976,7 @@ static void decode_rrr1_maddq_h(DisasContext *ctx) r4 = MASK_OP_RRR1_D(ctx->opcode); n = MASK_OP_RRR1_N(ctx->opcode); - temp = tcg_const_i32(n); + temp = tcg_temp_new(); temp2 = tcg_temp_new(); switch (op2) { @@ -7577,8 +7139,6 @@ static void decode_rrr1_maddq_h(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(temp); - tcg_temp_free(temp2); } static void decode_rrr1_maddsu_h(DisasContext *ctx) @@ -7898,7 +7458,7 @@ static void decode_rrr1_msubq_h(DisasContext *ctx) r4 = MASK_OP_RRR1_D(ctx->opcode); n = MASK_OP_RRR1_N(ctx->opcode); - temp = tcg_const_i32(n); + temp = tcg_temp_new(); temp2 = tcg_temp_new(); switch (op2) { @@ -8061,8 +7621,6 @@ static void decode_rrr1_msubq_h(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(temp); - tcg_temp_free(temp2); } static void decode_rrr1_msubad_h(DisasContext *ctx) @@ -8245,10 +7803,18 @@ static void decode_rrrr_extract_insert(DisasContext *ctx) if (r1 == r2) { tcg_gen_rotl_tl(cpu_gpr_d[r4], cpu_gpr_d[r1], tmp_pos); } else { + TCGv msw = tcg_temp_new(); + TCGv zero = tcg_constant_tl(0); tcg_gen_shl_tl(tmp_width, cpu_gpr_d[r1], tmp_pos); - tcg_gen_subfi_tl(tmp_pos, 32, tmp_pos); - tcg_gen_shr_tl(tmp_pos, cpu_gpr_d[r2], tmp_pos); - tcg_gen_or_tl(cpu_gpr_d[r4], tmp_width, tmp_pos); + tcg_gen_subfi_tl(msw, 32, tmp_pos); + tcg_gen_shr_tl(msw, cpu_gpr_d[r2], msw); + /* + * if pos == 0, then we do cpu_gpr_d[r2] << 32, which is undefined + * behaviour. So check that case here and set the low bits to zero + * which effectivly returns cpu_gpr_d[r1] + */ + tcg_gen_movcond_tl(TCG_COND_EQ, msw, tmp_pos, zero, zero, msw); + tcg_gen_or_tl(cpu_gpr_d[r4], tmp_width, msw); } break; case OPC2_32_RRRR_EXTR: @@ -8276,8 +7842,6 @@ static void decode_rrrr_extract_insert(DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(tmp_pos); - tcg_temp_free(tmp_width); } /* RRRW format */ @@ -8317,14 +7881,12 @@ static void decode_rrrw_extract_insert(DisasContext *ctx) break; case OPC2_32_RRRW_IMASK: temp2 = tcg_temp_new(); - + CHECK_REG_PAIR(r4); tcg_gen_andi_tl(temp, cpu_gpr_d[r3], 0x1f); tcg_gen_movi_tl(temp2, (1 << width) - 1); tcg_gen_shl_tl(temp2, temp2, temp); tcg_gen_shl_tl(cpu_gpr_d[r4], cpu_gpr_d[r2], temp); tcg_gen_mov_tl(cpu_gpr_d[r4+1], temp2); - - tcg_temp_free(temp2); break; case OPC2_32_RRRW_INSERT: temp2 = tcg_temp_new(); @@ -8332,13 +7894,10 @@ static void decode_rrrw_extract_insert(DisasContext *ctx) tcg_gen_movi_tl(temp, width); tcg_gen_andi_tl(temp2, cpu_gpr_d[r3], 0x1f); gen_insert(cpu_gpr_d[r4], cpu_gpr_d[r1], cpu_gpr_d[r2], temp, temp2); - - tcg_temp_free(temp2); break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_temp_free(temp); } /* SYS Format*/ @@ -8357,12 +7916,33 @@ static void decode_sys_interrupts(DisasContext *ctx) /* raise EXCP_DEBUG */ break; case OPC2_32_SYS_DISABLE: - tcg_gen_andi_tl(cpu_ICR, cpu_ICR, ~MASK_ICR_IE_1_3); + if (ctx->priv == TRICORE_PRIV_SM || ctx->priv == TRICORE_PRIV_UM1) { + tcg_gen_andi_tl(cpu_ICR, cpu_ICR, ~ctx->icr_ie_mask); + } else { + generate_trap(ctx, TRAPC_PROT, TIN1_PRIV); + } break; + case OPC2_32_SYS_DISABLE_D: + if (has_feature(ctx, TRICORE_FEATURE_16)) { + if (ctx->priv == TRICORE_PRIV_SM || ctx->priv == TRICORE_PRIV_UM1) { + tcg_gen_extract_tl(cpu_gpr_d[r1], cpu_ICR, + ctx->icr_ie_offset, 1); + tcg_gen_andi_tl(cpu_ICR, cpu_ICR, ~ctx->icr_ie_mask); + } else { + generate_trap(ctx, TRAPC_PROT, TIN1_PRIV); + } + } else { + generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); + } case OPC2_32_SYS_DSYNC: break; case OPC2_32_SYS_ENABLE: - tcg_gen_ori_tl(cpu_ICR, cpu_ICR, MASK_ICR_IE_1_3); + if (ctx->priv == TRICORE_PRIV_SM || ctx->priv == TRICORE_PRIV_UM1) { + tcg_gen_ori_tl(cpu_ICR, cpu_ICR, ctx->icr_ie_mask); + ctx->base.is_jmp = DISAS_EXIT_UPDATE; + } else { + generate_trap(ctx, TRAPC_PROT, TIN1_PRIV); + } break; case OPC2_32_SYS_ISYNC: break; @@ -8375,39 +7955,39 @@ static void decode_sys_interrupts(DisasContext *ctx) gen_fret(ctx); break; case OPC2_32_SYS_RFE: - gen_helper_rfe(cpu_env); - tcg_gen_exit_tb(NULL, 0); - ctx->base.is_jmp = DISAS_NORETURN; + gen_helper_rfe(tcg_env); + ctx->base.is_jmp = DISAS_EXIT; break; case OPC2_32_SYS_RFM: - if ((ctx->hflags & TRICORE_HFLAG_KUU) == TRICORE_HFLAG_SM) { + if (ctx->priv == TRICORE_PRIV_SM) { tmp = tcg_temp_new(); l1 = gen_new_label(); - tcg_gen_ld32u_tl(tmp, cpu_env, offsetof(CPUTriCoreState, DBGSR)); + tcg_gen_ld32u_tl(tmp, tcg_env, offsetof(CPUTriCoreState, DBGSR)); tcg_gen_andi_tl(tmp, tmp, MASK_DBGSR_DE); tcg_gen_brcondi_tl(TCG_COND_NE, tmp, 1, l1); - gen_helper_rfm(cpu_env); + gen_helper_rfm(tcg_env); gen_set_label(l1); - tcg_gen_exit_tb(NULL, 0); - ctx->base.is_jmp = DISAS_NORETURN; - tcg_temp_free(tmp); + ctx->base.is_jmp = DISAS_EXIT; } else { - /* generate privilege trap */ + generate_trap(ctx, TRAPC_PROT, TIN1_PRIV); } break; case OPC2_32_SYS_RSLCX: - gen_helper_rslcx(cpu_env); + gen_helper_rslcx(tcg_env); break; case OPC2_32_SYS_SVLCX: - gen_helper_svlcx(cpu_env); + gen_helper_svlcx(tcg_env); break; case OPC2_32_SYS_RESTORE: if (has_feature(ctx, TRICORE_FEATURE_16)) { - if ((ctx->hflags & TRICORE_HFLAG_KUU) == TRICORE_HFLAG_SM || - (ctx->hflags & TRICORE_HFLAG_KUU) == TRICORE_HFLAG_UM1) { - tcg_gen_deposit_tl(cpu_ICR, cpu_ICR, cpu_gpr_d[r1], 8, 1); - } /* else raise privilege trap */ + if (ctx->priv == TRICORE_PRIV_SM || ctx->priv == TRICORE_PRIV_UM1) { + tcg_gen_deposit_tl(cpu_ICR, cpu_ICR, cpu_gpr_d[r1], + ctx->icr_ie_offset, 1); + ctx->base.is_jmp = DISAS_EXIT_UPDATE; + } else { + generate_trap(ctx, TRAPC_PROT, TIN1_PRIV); + } } else { generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } @@ -8431,7 +8011,7 @@ static void decode_sys_interrupts(DisasContext *ctx) static void decode_32Bit_opc(DisasContext *ctx) { - int op1; + int op1, op2; int32_t r1, r2, r3; int32_t address, const16; int8_t b, const4; @@ -8468,28 +8048,33 @@ static void decode_32Bit_opc(DisasContext *ctx) case OPC1_32_ABS_STOREQ: address = MASK_OP_ABS_OFF18(ctx->opcode); r1 = MASK_OP_ABS_S1D(ctx->opcode); - temp = tcg_const_i32(EA_ABS_FORMAT(address)); + temp = tcg_constant_i32(EA_ABS_FORMAT(address)); temp2 = tcg_temp_new(); tcg_gen_shri_tl(temp2, cpu_gpr_d[r1], 16); tcg_gen_qemu_st_tl(temp2, temp, ctx->mem_idx, MO_LEUW); - - tcg_temp_free(temp2); - tcg_temp_free(temp); break; case OPC1_32_ABS_LD_Q: address = MASK_OP_ABS_OFF18(ctx->opcode); r1 = MASK_OP_ABS_S1D(ctx->opcode); - temp = tcg_const_i32(EA_ABS_FORMAT(address)); + temp = tcg_constant_i32(EA_ABS_FORMAT(address)); tcg_gen_qemu_ld_tl(cpu_gpr_d[r1], temp, ctx->mem_idx, MO_LEUW); tcg_gen_shli_tl(cpu_gpr_d[r1], cpu_gpr_d[r1], 16); - - tcg_temp_free(temp); break; - case OPC1_32_ABS_LEA: + case OPCM_32_ABS_LEA_LHA: address = MASK_OP_ABS_OFF18(ctx->opcode); r1 = MASK_OP_ABS_S1D(ctx->opcode); + + if (has_feature(ctx, TRICORE_FEATURE_162)) { + op2 = MASK_OP_ABS_OP2(ctx->opcode); + if (op2 == OPC2_32_ABS_LHA) { + tcg_gen_movi_tl(cpu_gpr_a[r1], address << 14); + break; + } + /* otherwise translate regular LEA */ + } + tcg_gen_movi_tl(cpu_gpr_a[r1], EA_ABS_FORMAT(address)); break; /* ABSB-format */ @@ -8498,16 +8083,13 @@ static void decode_32Bit_opc(DisasContext *ctx) b = MASK_OP_ABSB_B(ctx->opcode); bpos = MASK_OP_ABSB_BPOS(ctx->opcode); - temp = tcg_const_i32(EA_ABS_FORMAT(address)); + temp = tcg_constant_i32(EA_ABS_FORMAT(address)); temp2 = tcg_temp_new(); tcg_gen_qemu_ld_tl(temp2, temp, ctx->mem_idx, MO_UB); tcg_gen_andi_tl(temp2, temp2, ~(0x1u << bpos)); tcg_gen_ori_tl(temp2, temp2, (b << bpos)); tcg_gen_qemu_st_tl(temp2, temp, ctx->mem_idx, MO_UB); - - tcg_temp_free(temp); - tcg_temp_free(temp2); break; /* B-format */ case OPC1_32_B_CALL: @@ -8628,20 +8210,16 @@ static void decode_32Bit_opc(DisasContext *ctx) r2 = MASK_OP_RCRR_S3(ctx->opcode); r3 = MASK_OP_RCRR_D(ctx->opcode); const16 = MASK_OP_RCRR_CONST4(ctx->opcode); - temp = tcg_const_i32(const16); + temp = tcg_constant_i32(const16); temp2 = tcg_temp_new(); /* width*/ temp3 = tcg_temp_new(); /* pos */ - CHECK_REG_PAIR(r3); + CHECK_REG_PAIR(r2); - tcg_gen_andi_tl(temp2, cpu_gpr_d[r3+1], 0x1f); - tcg_gen_andi_tl(temp3, cpu_gpr_d[r3], 0x1f); + tcg_gen_andi_tl(temp2, cpu_gpr_d[r2 + 1], 0x1f); + tcg_gen_andi_tl(temp3, cpu_gpr_d[r2], 0x1f); - gen_insert(cpu_gpr_d[r2], cpu_gpr_d[r1], temp, temp2, temp3); - - tcg_temp_free(temp); - tcg_temp_free(temp2); - tcg_temp_free(temp3); + gen_insert(cpu_gpr_d[r3], cpu_gpr_d[r1], temp, temp2, temp3); break; /* RCRW Format */ case OPCM_32_RCRW_MASK_INSERT: @@ -8706,15 +8284,9 @@ static void decode_32Bit_opc(DisasContext *ctx) r2 = MASK_OP_RRPW_S2(ctx->opcode); r3 = MASK_OP_RRPW_D(ctx->opcode); const16 = MASK_OP_RRPW_POS(ctx->opcode); - if (r1 == r2) { - tcg_gen_rotli_tl(cpu_gpr_d[r3], cpu_gpr_d[r1], const16); - } else { - temp = tcg_temp_new(); - tcg_gen_shli_tl(temp, cpu_gpr_d[r1], const16); - tcg_gen_shri_tl(cpu_gpr_d[r3], cpu_gpr_d[r2], 32 - const16); - tcg_gen_or_tl(cpu_gpr_d[r3], cpu_gpr_d[r3], temp); - tcg_temp_free(temp); - } + + tcg_gen_extract2_tl(cpu_gpr_d[r3], cpu_gpr_d[r2], cpu_gpr_d[r1], + 32 - const16); break; /* RRR Format */ case OPCM_32_RRR_COND_SELECT: @@ -8781,10 +8353,20 @@ static void tricore_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - CPUTriCoreState *env = cs->env_ptr; - ctx->mem_idx = cpu_mmu_index(env, false); - ctx->hflags = (uint32_t)ctx->base.tb->flags; + CPUTriCoreState *env = cpu_env(cs); + ctx->mem_idx = cpu_mmu_index(cs, false); + + uint32_t tb_flags = (uint32_t)ctx->base.tb->flags; + ctx->priv = FIELD_EX32(tb_flags, TB_FLAGS, PRIV); + ctx->features = env->features; + if (has_feature(ctx, TRICORE_FEATURE_161)) { + ctx->icr_ie_mask = R_ICR_IE_161_MASK; + ctx->icr_ie_offset = R_ICR_IE_161_SHIFT; + } else { + ctx->icr_ie_mask = R_ICR_IE_13_MASK; + ctx->icr_ie_offset = R_ICR_IE_13_SHIFT; + } } static void tricore_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -8807,7 +8389,7 @@ static bool insn_crosses_page(CPUTriCoreState *env, DisasContext *ctx) * 4 bytes from the page boundary, so we cross the page if the first * 16 bits indicate that this is a 32 bit insn. */ - uint16_t insn = cpu_lduw_code(env, ctx->base.pc_next); + uint16_t insn = translator_lduw(env, &ctx->base, ctx->base.pc_next); return !tricore_insn_is_16bit(insn); } @@ -8816,18 +8398,19 @@ static bool insn_crosses_page(CPUTriCoreState *env, DisasContext *ctx) static void tricore_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - CPUTriCoreState *env = cpu->env_ptr; + CPUTriCoreState *env = cpu_env(cpu); uint16_t insn_lo; bool is_16bit; - insn_lo = cpu_lduw_code(env, ctx->base.pc_next); + insn_lo = translator_lduw(env, &ctx->base, ctx->base.pc_next); is_16bit = tricore_insn_is_16bit(insn_lo); if (is_16bit) { ctx->opcode = insn_lo; ctx->pc_succ_insn = ctx->base.pc_next + 2; decode_16Bit_opc(ctx); } else { - uint32_t insn_hi = cpu_lduw_code(env, ctx->base.pc_next + 2); + uint32_t insn_hi = translator_lduw(env, &ctx->base, + ctx->base.pc_next + 2); ctx->opcode = insn_hi << 16 | insn_lo; ctx->pc_succ_insn = ctx->base.pc_next + 4; decode_32Bit_opc(ctx); @@ -8854,6 +8437,15 @@ static void tricore_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) case DISAS_TOO_MANY: gen_goto_tb(ctx, 0, ctx->base.pc_next); break; + case DISAS_EXIT_UPDATE: + gen_save_pc(ctx->base.pc_next); + /* fall through */ + case DISAS_EXIT: + tcg_gen_exit_tb(NULL, 0); + break; + case DISAS_JUMP: + tcg_gen_lookup_and_goto_ptr(); + break; case DISAS_NORETURN: break; default: @@ -8878,18 +8470,14 @@ static const TranslatorOps tricore_tr_ops = { }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; - translator_loop(&tricore_tr_ops, &ctx.base, cs, tb, max_insns); + translator_loop(cs, tb, max_insns, pc, host_pc, + &tricore_tr_ops, &ctx.base); } -void -restore_state_to_opc(CPUTriCoreState *env, TranslationBlock *tb, - target_ulong *data) -{ - env->PC = data[0]; -} /* * * Initialization @@ -8905,13 +8493,13 @@ void cpu_state_reset(CPUTriCoreState *env) static void tricore_tcg_init_csfr(void) { - cpu_PCXI = tcg_global_mem_new(cpu_env, + cpu_PCXI = tcg_global_mem_new(tcg_env, offsetof(CPUTriCoreState, PCXI), "PCXI"); - cpu_PSW = tcg_global_mem_new(cpu_env, + cpu_PSW = tcg_global_mem_new(tcg_env, offsetof(CPUTriCoreState, PSW), "PSW"); - cpu_PC = tcg_global_mem_new(cpu_env, + cpu_PC = tcg_global_mem_new(tcg_env, offsetof(CPUTriCoreState, PC), "PC"); - cpu_ICR = tcg_global_mem_new(cpu_env, + cpu_ICR = tcg_global_mem_new(tcg_env, offsetof(CPUTriCoreState, ICR), "ICR"); } @@ -8921,30 +8509,30 @@ void tricore_tcg_init(void) /* reg init */ for (i = 0 ; i < 16 ; i++) { - cpu_gpr_a[i] = tcg_global_mem_new(cpu_env, + cpu_gpr_a[i] = tcg_global_mem_new(tcg_env, offsetof(CPUTriCoreState, gpr_a[i]), regnames_a[i]); } for (i = 0 ; i < 16 ; i++) { - cpu_gpr_d[i] = tcg_global_mem_new(cpu_env, + cpu_gpr_d[i] = tcg_global_mem_new(tcg_env, offsetof(CPUTriCoreState, gpr_d[i]), regnames_d[i]); } tricore_tcg_init_csfr(); /* init PSW flag cache */ - cpu_PSW_C = tcg_global_mem_new(cpu_env, + cpu_PSW_C = tcg_global_mem_new(tcg_env, offsetof(CPUTriCoreState, PSW_USB_C), "PSW_C"); - cpu_PSW_V = tcg_global_mem_new(cpu_env, + cpu_PSW_V = tcg_global_mem_new(tcg_env, offsetof(CPUTriCoreState, PSW_USB_V), "PSW_V"); - cpu_PSW_SV = tcg_global_mem_new(cpu_env, + cpu_PSW_SV = tcg_global_mem_new(tcg_env, offsetof(CPUTriCoreState, PSW_USB_SV), "PSW_SV"); - cpu_PSW_AV = tcg_global_mem_new(cpu_env, + cpu_PSW_AV = tcg_global_mem_new(tcg_env, offsetof(CPUTriCoreState, PSW_USB_AV), "PSW_AV"); - cpu_PSW_SAV = tcg_global_mem_new(cpu_env, + cpu_PSW_SAV = tcg_global_mem_new(tcg_env, offsetof(CPUTriCoreState, PSW_USB_SAV), "PSW_SAV"); } diff --git a/target/tricore/tricore-opcodes.h b/target/tricore/tricore-opcodes.h index f7135f183d..60d2402b6e 100644 --- a/target/tricore/tricore-opcodes.h +++ b/target/tricore/tricore-opcodes.h @@ -430,7 +430,7 @@ enum { OPCM_32_ABS_STOREB_H = 0x25, OPC1_32_ABS_STOREQ = 0x65, OPC1_32_ABS_LD_Q = 0x45, - OPC1_32_ABS_LEA = 0xc5, + OPCM_32_ABS_LEA_LHA = 0xc5, /* ABSB Format */ OPC1_32_ABSB_ST_T = 0xd5, /* B Format */ @@ -592,6 +592,13 @@ enum { OPC2_32_ABS_ST_B = 0x00, OPC2_32_ABS_ST_H = 0x02, }; + +/* OPCM_32_ABS_LEA_LHA */ +enum { + OPC2_32_ABS_LEA = 0x00, + OPC2_32_ABS_LHA = 0x01, +}; + /* * Bit Format */ @@ -878,6 +885,7 @@ enum { OPC2_32_RC_SHAS = 0x02, OPC2_32_RC_XNOR = 0x0d, OPC2_32_RC_XOR = 0x0c, + OPC2_32_RC_SHUFFLE = 0x07, /* v1.6.2 only */ }; /* OPCM_32_RC_ACCUMULATOR */ enum { @@ -1132,7 +1140,10 @@ enum { OPC2_32_RR_DVINIT_U = 0x0a, OPC2_32_RR_PARITY = 0x02, OPC2_32_RR_UNPACK = 0x08, - OPC2_32_RR_CRC32 = 0x03, + OPC2_32_RR_CRC32 = 0x03, /* CRC32B.W in 1.6.2 */ + OPC2_32_RR_CRC32_B = 0x06, /* 1.6.2 only */ + OPC2_32_RR_CRC32L_W = 0x07, /* 1.6.2 only */ + OPC2_32_RR_POPCNT_W = 0x22, /* 1.6.2 only */ OPC2_32_RR_DIV = 0x20, OPC2_32_RR_DIV_U = 0x21, OPC2_32_RR_MUL_F = 0x04, @@ -1141,6 +1152,8 @@ enum { OPC2_32_RR_ITOF = 0x14, OPC2_32_RR_CMP_F = 0x00, OPC2_32_RR_FTOIZ = 0x13, + OPC2_32_RR_FTOHP = 0x25, /* 1.6.2 only */ + OPC2_32_RR_HPTOF = 0x24, /* 1.6.2 only */ OPC2_32_RR_FTOQ31 = 0x11, OPC2_32_RR_FTOQ31Z = 0x18, OPC2_32_RR_FTOU = 0x12, @@ -1236,6 +1249,7 @@ enum { OPC2_32_RRR_SUB_F = 0x03, OPC2_32_RRR_MADD_F = 0x06, OPC2_32_RRR_MSUB_F = 0x07, + OPC2_32_RRR_CRCN = 0x01, /* 1.6.2 up */ }; /* * RRR1 Format @@ -1456,6 +1470,7 @@ enum { enum { OPC2_32_SYS_DEBUG = 0x04, OPC2_32_SYS_DISABLE = 0x0d, + OPC2_32_SYS_DISABLE_D = 0x0f, /* 1.6 up */ OPC2_32_SYS_DSYNC = 0x12, OPC2_32_SYS_ENABLE = 0x0c, OPC2_32_SYS_ISYNC = 0x13, diff --git a/target/xtensa/Kconfig b/target/xtensa/Kconfig index a3c8dc7f6d..5e46049262 100644 --- a/target/xtensa/Kconfig +++ b/target/xtensa/Kconfig @@ -1,2 +1,3 @@ config XTENSA bool + select SEMIHOSTING diff --git a/target/xtensa/core-dc232b.c b/target/xtensa/core-dc232b.c index c982d09c24..9aba2667e3 100644 --- a/target/xtensa/core-dc232b.c +++ b/target/xtensa/core-dc232b.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "qemu/timer.h" diff --git a/target/xtensa/core-dc233c.c b/target/xtensa/core-dc233c.c index 595ab9a90f..9b0a625063 100644 --- a/target/xtensa/core-dc233c.c +++ b/target/xtensa/core-dc233c.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "core-dc233c/core-isa.h" diff --git a/target/xtensa/core-de212.c b/target/xtensa/core-de212.c index 50c995ba79..b08fe22e65 100644 --- a/target/xtensa/core-de212.c +++ b/target/xtensa/core-de212.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "core-de212/core-isa.h" diff --git a/target/xtensa/core-de233_fpu.c b/target/xtensa/core-de233_fpu.c index 41af8057fb..8845cdb592 100644 --- a/target/xtensa/core-de233_fpu.c +++ b/target/xtensa/core-de233_fpu.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "core-de233_fpu/core-isa.h" diff --git a/target/xtensa/core-dsp3400.c b/target/xtensa/core-dsp3400.c index 81e425c568..c0f94b9e27 100644 --- a/target/xtensa/core-dsp3400.c +++ b/target/xtensa/core-dsp3400.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "core-dsp3400/core-isa.h" diff --git a/target/xtensa/core-fsf.c b/target/xtensa/core-fsf.c index 3327c50b4f..310be8d61f 100644 --- a/target/xtensa/core-fsf.c +++ b/target/xtensa/core-fsf.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "core-fsf/core-isa.h" diff --git a/target/xtensa/core-lx106.c b/target/xtensa/core-lx106.c index 7a771d09a6..7f71d088f3 100644 --- a/target/xtensa/core-lx106.c +++ b/target/xtensa/core-lx106.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "core-lx106/core-isa.h" diff --git a/target/xtensa/core-sample_controller.c b/target/xtensa/core-sample_controller.c index fd5de5576b..8867001aac 100644 --- a/target/xtensa/core-sample_controller.c +++ b/target/xtensa/core-sample_controller.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "core-sample_controller/core-isa.h" diff --git a/target/xtensa/core-test_kc705_be.c b/target/xtensa/core-test_kc705_be.c index 294c16f2f4..bd082f49aa 100644 --- a/target/xtensa/core-test_kc705_be.c +++ b/target/xtensa/core-test_kc705_be.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "core-test_kc705_be/core-isa.h" diff --git a/target/xtensa/core-test_mmuhifi_c3.c b/target/xtensa/core-test_mmuhifi_c3.c index c0e5d32d1e..3090dd01ed 100644 --- a/target/xtensa/core-test_mmuhifi_c3.c +++ b/target/xtensa/core-test_mmuhifi_c3.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "core-test_mmuhifi_c3/core-isa.h" diff --git a/target/xtensa/cpu-param.h b/target/xtensa/cpu-param.h index b53e9a3e08..b1da0555de 100644 --- a/target/xtensa/cpu-param.h +++ b/target/xtensa/cpu-param.h @@ -16,6 +16,5 @@ #else #define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif -#define NB_MMU_MODES 4 #endif diff --git a/target/xtensa/cpu-qom.h b/target/xtensa/cpu-qom.h index 4fc35ee49b..d932346b5f 100644 --- a/target/xtensa/cpu-qom.h +++ b/target/xtensa/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU Xtensa CPU + * QEMU Xtensa CPU QOM header (target agnostic) * * Copyright (c) 2012 SUSE LINUX Products GmbH * All rights reserved. @@ -30,32 +30,12 @@ #define QEMU_XTENSA_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.h" #define TYPE_XTENSA_CPU "xtensa-cpu" OBJECT_DECLARE_CPU_TYPE(XtensaCPU, XtensaCPUClass, XTENSA_CPU) -typedef struct XtensaConfig XtensaConfig; - -/** - * XtensaCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_reset: The parent class' reset handler. - * @config: The CPU core configuration. - * - * An Xtensa CPU model. - */ -struct XtensaCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceReset parent_reset; - - const XtensaConfig *config; -}; - +#define XTENSA_CPU_TYPE_SUFFIX "-" TYPE_XTENSA_CPU +#define XTENSA_CPU_TYPE_NAME(model) model XTENSA_CPU_TYPE_SUFFIX #endif diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index fd553fdfb5..875cf843c9 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -35,6 +35,9 @@ #include "qemu/module.h" #include "migration/vmstate.h" #include "hw/qdev-clock.h" +#ifndef CONFIG_USER_ONLY +#include "exec/memory.h" +#endif static void xtensa_cpu_set_pc(CPUState *cs, vaddr value) @@ -44,6 +47,22 @@ static void xtensa_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.pc = value; } +static vaddr xtensa_cpu_get_pc(CPUState *cs) +{ + XtensaCPU *cpu = XTENSA_CPU(cs); + + return cpu->env.pc; +} + +static void xtensa_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + XtensaCPU *cpu = XTENSA_CPU(cs); + + cpu->env.pc = data[0]; +} + static bool xtensa_cpu_has_work(CPUState *cs) { #ifndef CONFIG_USER_ONLY @@ -55,6 +74,11 @@ static bool xtensa_cpu_has_work(CPUState *cs) #endif } +static int xtensa_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return xtensa_get_cring(cpu_env(cs)); +} + #ifdef CONFIG_USER_ONLY static bool abi_call0; @@ -69,16 +93,17 @@ bool xtensa_abi_call0(void) } #endif -static void xtensa_cpu_reset(DeviceState *dev) +static void xtensa_cpu_reset_hold(Object *obj) { - CPUState *s = CPU(dev); - XtensaCPU *cpu = XTENSA_CPU(s); - XtensaCPUClass *xcc = XTENSA_CPU_GET_CLASS(cpu); - CPUXtensaState *env = &cpu->env; + CPUState *cs = CPU(obj); + XtensaCPUClass *xcc = XTENSA_CPU_GET_CLASS(obj); + CPUXtensaState *env = cpu_env(cs); bool dfpu = xtensa_option_enabled(env->config, XTENSA_OPTION_DFP_COPROCESSOR); - xcc->parent_reset(dev); + if (xcc->parent_phases.hold) { + xcc->parent_phases.hold(obj); + } env->pc = env->config->exception_vector[EXC_RESET0 + env->static_vectors]; env->sregs[LITBASE] &= ~1; @@ -106,7 +131,7 @@ static void xtensa_cpu_reset(DeviceState *dev) #ifndef CONFIG_USER_ONLY reset_mmu(env); - s->halted = env->runstall; + cs->halted = env->runstall; #endif set_no_signaling_nans(!dfpu, &env->fp_status); set_use_first_nan(!dfpu, &env->fp_status); @@ -120,10 +145,7 @@ static ObjectClass *xtensa_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf(XTENSA_CPU_TYPE_NAME("%s"), cpu_model); oc = object_class_by_name(typename); g_free(typename); - if (oc == NULL || !object_class_dynamic_cast(oc, TYPE_XTENSA_CPU) || - object_class_is_abstract(oc)) { - return NULL; - } + return oc; } @@ -164,7 +186,6 @@ static void xtensa_cpu_initfn(Object *obj) XtensaCPUClass *xcc = XTENSA_CPU_GET_CLASS(obj); CPUXtensaState *env = &cpu->env; - cpu_set_cpustate_pointers(cpu); env->config = xcc->config; #ifndef CONFIG_USER_ONLY @@ -205,9 +226,10 @@ static const struct SysemuCPUOps xtensa_sysemu_ops = { #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps xtensa_tcg_ops = { +static const TCGCPUOps xtensa_tcg_ops = { .initialize = xtensa_translate_init, .debug_excp_handler = xtensa_breakpoint_handler, + .restore_state_to_opc = xtensa_restore_state_to_opc, #ifndef CONFIG_USER_ONLY .tlb_fill = xtensa_cpu_tlb_fill, @@ -215,6 +237,7 @@ static const struct TCGCPUOps xtensa_tcg_ops = { .do_interrupt = xtensa_cpu_do_interrupt, .do_transaction_failed = xtensa_cpu_do_transaction_failed, .do_unaligned_access = xtensa_cpu_do_unaligned_access, + .debug_check_breakpoint = xtensa_debug_check_breakpoint, #endif /* !CONFIG_USER_ONLY */ }; @@ -223,16 +246,20 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); XtensaCPUClass *xcc = XTENSA_CPU_CLASS(cc); + ResettableClass *rc = RESETTABLE_CLASS(oc); device_class_set_parent_realize(dc, xtensa_cpu_realizefn, &xcc->parent_realize); - device_class_set_parent_reset(dc, xtensa_cpu_reset, &xcc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, xtensa_cpu_reset_hold, NULL, + &xcc->parent_phases); cc->class_by_name = xtensa_cpu_class_by_name; cc->has_work = xtensa_cpu_has_work; + cc->mmu_index = xtensa_cpu_mmu_index; cc->dump_state = xtensa_cpu_dump_state; cc->set_pc = xtensa_cpu_set_pc; + cc->get_pc = xtensa_cpu_get_pc; cc->gdb_read_register = xtensa_cpu_gdb_read_register; cc->gdb_write_register = xtensa_cpu_gdb_write_register; cc->gdb_stop_before_watchpoint = true; @@ -248,6 +275,7 @@ static const TypeInfo xtensa_cpu_type_info = { .name = TYPE_XTENSA_CPU, .parent = TYPE_CPU, .instance_size = sizeof(XtensaCPU), + .instance_align = __alignof(XtensaCPU), .instance_init = xtensa_cpu_initfn, .abstract = true, .class_size = sizeof(XtensaCPUClass), diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index 579adcb769..6b8d0636d2 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -229,6 +229,7 @@ enum { #define MAX_NCCOMPARE 3 #define MAX_TLB_WAY_SIZE 8 #define MAX_NDBREAK 2 +#define MAX_NIBREAK 2 #define MAX_NMEMORY 4 #define MAX_MPU_FOREGROUND_SEGMENTS 32 @@ -426,7 +427,7 @@ extern const XtensaOpcodeTranslators xtensa_core_opcodes; extern const XtensaOpcodeTranslators xtensa_fpu2000_opcodes; extern const XtensaOpcodeTranslators xtensa_fpu_opcodes; -struct XtensaConfig { +typedef struct XtensaConfig { const char *name; uint64_t options; XtensaGdbRegmap gdb_regmap; @@ -489,7 +490,7 @@ struct XtensaConfig { const xtensa_mpu_entry *mpu_bg; bool use_first_nan; -}; +} XtensaConfig; typedef struct XtensaConfigList { const XtensaConfig *config; @@ -547,6 +548,8 @@ struct CPUArchState { /* Watchpoints for DBREAK registers */ struct CPUWatchpoint *cpu_watchpoint[MAX_NDBREAK]; + /* Breakpoints for IBREAK registers */ + struct CPUBreakpoint *cpu_breakpoint[MAX_NIBREAK]; }; /** @@ -556,15 +559,28 @@ struct CPUArchState { * An Xtensa CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - Clock *clock; - CPUNegativeOffsetState neg; CPUXtensaState env; + Clock *clock; }; +/** + * XtensaCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * @config: The CPU core configuration. + * + * An Xtensa CPU model. + */ +struct XtensaCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; + + const XtensaConfig *config; +}; #ifndef CONFIG_USER_ONLY bool xtensa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, @@ -576,9 +592,10 @@ void xtensa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, unsigned size, MMUAccessType access_type, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr); +hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +bool xtensa_debug_check_breakpoint(CPUState *cs); #endif 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, GByteArray *buf, int reg); @@ -587,10 +604,6 @@ G_NORETURN void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); -#define cpu_list xtensa_cpu_list - -#define XTENSA_CPU_TYPE_SUFFIX "-" TYPE_XTENSA_CPU -#define XTENSA_CPU_TYPE_NAME(model) model XTENSA_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_XTENSA_CPU #if TARGET_BIG_ENDIAN @@ -615,7 +628,6 @@ void check_interrupts(CPUXtensaState *s); void xtensa_irq_init(CPUXtensaState *env); qemu_irq *xtensa_get_extints(CPUXtensaState *env); qemu_irq xtensa_get_runstall(CPUXtensaState *env); -void xtensa_cpu_list(void); void xtensa_sync_window_from_phys(CPUXtensaState *env); void xtensa_sync_phys_from_window(CPUXtensaState *env); void xtensa_rotate_window(CPUXtensaState *env, uint32_t delta); @@ -701,11 +713,6 @@ static inline uint32_t xtensa_replicate_windowstart(CPUXtensaState *env) /* MMU modes definitions */ #define MMU_USER_IDX 3 -static inline int cpu_mmu_index(CPUXtensaState *env, bool ifetch) -{ - return xtensa_get_cring(env); -} - #define XTENSA_TBFLAG_RING_MASK 0x3 #define XTENSA_TBFLAG_EXCM 0x4 #define XTENSA_TBFLAG_LITBASE 0x8 @@ -727,8 +734,8 @@ static inline int cpu_mmu_index(CPUXtensaState *env, bool ifetch) #include "exec/cpu-all.h" -static inline void cpu_get_tb_cpu_state(CPUXtensaState *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) +static inline void cpu_get_tb_cpu_state(CPUXtensaState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) { *pc = env->pc; *cs_base = 0; diff --git a/target/xtensa/dbg_helper.c b/target/xtensa/dbg_helper.c index ce2a820c60..5546c82ecd 100644 --- a/target/xtensa/dbg_helper.c +++ b/target/xtensa/dbg_helper.c @@ -27,34 +27,27 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "qemu/main-loop.h" #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" #include "exec/address-spaces.h" -static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr) -{ - uint32_t paddr; - uint32_t page_size; - unsigned access; - int ret = xtensa_get_physical_addr(env, false, vaddr, 2, 0, - &paddr, &page_size, &access); - if (ret == 0) { - tb_invalidate_phys_addr(&address_space_memory, paddr, - MEMTXATTRS_UNSPECIFIED); - } -} - void HELPER(wsr_ibreakenable)(CPUXtensaState *env, uint32_t v) { + CPUState *cs = env_cpu(env); uint32_t change = v ^ env->sregs[IBREAKENABLE]; unsigned i; for (i = 0; i < env->config->nibreak; ++i) { if (change & (1 << i)) { - tb_invalidate_virtual_addr(env, env->sregs[IBREAKA + i]); + if (v & (1 << i)) { + cpu_breakpoint_insert(cs, env->sregs[IBREAKA + i], + BP_CPU, &env->cpu_breakpoint[i]); + } else { + cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[i]); + env->cpu_breakpoint[i] = NULL; + } } } env->sregs[IBREAKENABLE] = v & ((1 << env->config->nibreak) - 1); @@ -63,12 +56,31 @@ void HELPER(wsr_ibreakenable)(CPUXtensaState *env, uint32_t v) void HELPER(wsr_ibreaka)(CPUXtensaState *env, uint32_t i, uint32_t v) { if (env->sregs[IBREAKENABLE] & (1 << i) && env->sregs[IBREAKA + i] != v) { - tb_invalidate_virtual_addr(env, env->sregs[IBREAKA + i]); - tb_invalidate_virtual_addr(env, v); + CPUState *cs = env_cpu(env); + + cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[i]); + cpu_breakpoint_insert(cs, v, BP_CPU, &env->cpu_breakpoint[i]); } env->sregs[IBREAKA + i] = v; } +bool xtensa_debug_check_breakpoint(CPUState *cs) +{ + CPUXtensaState *env = cpu_env(cs); + unsigned int i; + + if (xtensa_get_cintlevel(env) >= env->config->debug_level) { + return false; + } + for (i = 0; i < env->config->nibreak; ++i) { + if (env->sregs[IBREAKENABLE] & (1 << i) && + env->sregs[IBREAKA + i] == env->pc) { + return true; + } + } + return false; +} + static void set_dbreak(CPUXtensaState *env, unsigned i, uint32_t dbreaka, uint32_t dbreakc) { diff --git a/target/xtensa/exc_helper.c b/target/xtensa/exc_helper.c index d4823a65cd..0514c2c1f3 100644 --- a/target/xtensa/exc_helper.c +++ b/target/xtensa/exc_helper.c @@ -31,6 +31,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" +#include "qemu/atomic.h" #include "exec/exec-all.h" void HELPER(exception)(CPUXtensaState *env, uint32_t excp) @@ -104,9 +105,9 @@ void HELPER(waiti)(CPUXtensaState *env, uint32_t pc, uint32_t intlevel) env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | (intlevel << PS_INTLEVEL_SHIFT); - qemu_mutex_lock_iothread(); + bql_lock(); check_interrupts(env); - qemu_mutex_unlock_iothread(); + bql_unlock(); if (env->pending_irq_level) { cpu_loop_exit(cpu); @@ -119,9 +120,9 @@ void HELPER(waiti)(CPUXtensaState *env, uint32_t pc, uint32_t intlevel) void HELPER(check_interrupts)(CPUXtensaState *env) { - qemu_mutex_lock_iothread(); + bql_lock(); check_interrupts(env); - qemu_mutex_unlock_iothread(); + bql_unlock(); } void HELPER(intset)(CPUXtensaState *env, uint32_t v) @@ -169,6 +170,9 @@ static void handle_interrupt(CPUXtensaState *env) CPUState *cs = env_cpu(env); if (level > 1) { + /* env->config->nlevel check should have ensured this */ + assert(level < sizeof(env->config->interrupt_vector)); + env->sregs[EPC1 + level - 1] = env->pc; env->sregs[EPS2 + level - 2] = env->sregs[PS]; env->sregs[PS] = @@ -201,8 +205,7 @@ static void handle_interrupt(CPUXtensaState *env) /* Called from cpu_handle_interrupt with BQL held */ void xtensa_cpu_do_interrupt(CPUState *cs) { - XtensaCPU *cpu = XTENSA_CPU(cs); - CPUXtensaState *env = &cpu->env; + CPUXtensaState *env = cpu_env(cs); if (cs->exception_index == EXC_IRQ) { qemu_log_mask(CPU_LOG_INT, diff --git a/target/xtensa/fpu_helper.c b/target/xtensa/fpu_helper.c index d2a10cc797..381e83ded8 100644 --- a/target/xtensa/fpu_helper.c +++ b/target/xtensa/fpu_helper.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "qemu/main-loop.h" #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" diff --git a/target/xtensa/gdbstub.c b/target/xtensa/gdbstub.c index b6696063e5..4748fb6532 100644 --- a/target/xtensa/gdbstub.c +++ b/target/xtensa/gdbstub.c @@ -19,7 +19,7 @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/log.h" enum { @@ -65,8 +65,7 @@ void xtensa_count_regs(const XtensaConfig *config, int xtensa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { - XtensaCPU *cpu = XTENSA_CPU(cs); - CPUXtensaState *env = &cpu->env; + CPUXtensaState *env = cpu_env(cs); const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n; #ifdef CONFIG_USER_ONLY int num_regs = env->config->gdb_regmap.num_core_regs; @@ -120,8 +119,7 @@ int xtensa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) int xtensa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - XtensaCPU *cpu = XTENSA_CPU(cs); - CPUXtensaState *env = &cpu->env; + CPUXtensaState *env = cpu_env(cs); uint32_t tmp; const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n; #ifdef CONFIG_USER_ONLY diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c index e0a9caab4b..ca214b948a 100644 --- a/target/xtensa/helper.c +++ b/target/xtensa/helper.c @@ -29,7 +29,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "exec/helper-proto.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" @@ -217,8 +217,7 @@ static uint32_t check_hw_breakpoints(CPUXtensaState *env) void xtensa_breakpoint_handler(CPUState *cs) { - XtensaCPU *cpu = XTENSA_CPU(cs); - CPUXtensaState *env = &cpu->env; + CPUXtensaState *env = cpu_env(cs); if (cs->watchpoint_hit) { if (cs->watchpoint_hit->flags & BP_CPU) { @@ -231,15 +230,18 @@ void xtensa_breakpoint_handler(CPUState *cs) } cpu_loop_exit_noexc(cs); } - } -} - -void xtensa_cpu_list(void) -{ - XtensaConfigList *core = xtensa_cores; - qemu_printf("Available CPUs:\n"); - for (; core; core = core->next) { - qemu_printf(" %s\n", core->config->name); + } else { + if (cpu_breakpoint_test(cs, env->pc, BP_GDB) + || !cpu_breakpoint_test(cs, env->pc, BP_CPU)) { + return; + } + if (env->sregs[ICOUNT] == 0xffffffff && + xtensa_get_cintlevel(env) < env->sregs[ICOUNTLEVEL]) { + debug_exception_env(env, DEBUGCAUSE_IC); + } else { + debug_exception_env(env, DEBUGCAUSE_IB); + } + cpu_loop_exit_noexc(cs); } } @@ -253,7 +255,7 @@ void xtensa_cpu_do_unaligned_access(CPUState *cs, assert(xtensa_option_enabled(env->config, XTENSA_OPTION_UNALIGNED_EXCEPTION)); - cpu_restore_state(CPU(cpu), retaddr, true); + cpu_restore_state(CPU(cpu), retaddr); HELPER(exception_cause_vaddr)(env, env->pc, LOAD_STORE_ALIGNMENT_CAUSE, addr); @@ -263,8 +265,7 @@ bool xtensa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - XtensaCPU *cpu = XTENSA_CPU(cs); - CPUXtensaState *env = &cpu->env; + CPUXtensaState *env = cpu_env(cs); uint32_t paddr; uint32_t page_size; unsigned access; @@ -284,7 +285,7 @@ bool xtensa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } else if (probe) { return false; } else { - cpu_restore_state(cs, retaddr, true); + cpu_restore_state(cs, retaddr); HELPER(exception_cause_vaddr)(env, env->pc, ret, address); } } @@ -294,10 +295,9 @@ void xtensa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr) { - XtensaCPU *cpu = XTENSA_CPU(cs); - CPUXtensaState *env = &cpu->env; + CPUXtensaState *env = cpu_env(cs); - cpu_restore_state(cs, retaddr, true); + cpu_restore_state(cs, retaddr); HELPER(exception_cause_vaddr)(env, env->pc, access_type == MMU_INST_FETCH ? INSTR_PIF_ADDR_ERROR_CAUSE : diff --git a/target/xtensa/import_core.sh b/target/xtensa/import_core.sh index b4c15556c2..17dfec8957 100755 --- a/target/xtensa/import_core.sh +++ b/target/xtensa/import_core.sh @@ -41,7 +41,7 @@ tar -xf "$OVERLAY" -O binutils/xtensa-modules.c | \ cat < "${TARGET}.c" #include "qemu/osdep.h" #include "cpu.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "core-$NAME/core-isa.h" diff --git a/target/xtensa/meson.build b/target/xtensa/meson.build index 20bbf9b335..f8d60101e3 100644 --- a/target/xtensa/meson.build +++ b/target/xtensa/meson.build @@ -15,8 +15,8 @@ xtensa_ss.add(files( 'xtensa-isa.c', )) -xtensa_softmmu_ss = ss.source_set() -xtensa_softmmu_ss.add(files( +xtensa_system_ss = ss.source_set() +xtensa_system_ss.add(files( 'dbg_helper.c', 'mmu_helper.c', 'monitor.c', @@ -24,4 +24,4 @@ xtensa_softmmu_ss.add(files( )) target_arch += {'xtensa': xtensa_ss} -target_softmmu_arch += {'xtensa': xtensa_softmmu_ss} +target_system_arch += {'xtensa': xtensa_system_ss} diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c index fa66e8e867..47063b0a57 100644 --- a/target/xtensa/mmu_helper.c +++ b/target/xtensa/mmu_helper.c @@ -27,14 +27,12 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "qemu/main-loop.h" #include "qemu/qemu-print.h" #include "qemu/units.h" #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" #define XTENSA_MPU_SEGMENT_MASK 0x0000001f #define XTENSA_MPU_ACC_RIGHTS_MASK 0x00000f00 @@ -68,7 +66,7 @@ void HELPER(itlb_hit_test)(CPUXtensaState *env, uint32_t vaddr) * only the side-effects (ie any MMU or other exception) */ probe_access(env, vaddr, 1, MMU_INST_FETCH, - cpu_mmu_index(env, true), GETPC()); + cpu_mmu_index(env_cpu(env), true), GETPC()); } void HELPER(wsr_rasid)(CPUXtensaState *env, uint32_t v) @@ -226,22 +224,31 @@ static void split_tlb_entry_spec_way(const CPUXtensaState *env, uint32_t v, * Split TLB address into TLB way, entry index and VPN (with index). * See ISA, 4.6.5.5 - 4.6.5.8 for the TLB addressing format */ -static void split_tlb_entry_spec(CPUXtensaState *env, uint32_t v, bool dtlb, - uint32_t *vpn, uint32_t *wi, uint32_t *ei) +static bool split_tlb_entry_spec(CPUXtensaState *env, uint32_t v, bool dtlb, + uint32_t *vpn, uint32_t *wi, uint32_t *ei) { if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { *wi = v & (dtlb ? 0xf : 0x7); - split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei); + if (*wi < (dtlb ? env->config->dtlb.nways : env->config->itlb.nways)) { + split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei); + return true; + } else { + return false; + } } else { *vpn = v & REGION_PAGE_MASK; *wi = 0; *ei = (v >> 29) & 0x7; + return true; } } static xtensa_tlb_entry *xtensa_tlb_get_entry(CPUXtensaState *env, bool dtlb, unsigned wi, unsigned ei) { + const xtensa_tlb *tlb = dtlb ? &env->config->dtlb : &env->config->itlb; + + assert(wi < tlb->nways && ei < tlb->way_size[wi]); return dtlb ? env->dtlb[wi] + ei : env->itlb[wi] + ei; @@ -254,11 +261,14 @@ static xtensa_tlb_entry *get_tlb_entry(CPUXtensaState *env, uint32_t wi; uint32_t ei; - split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei); - if (pwi) { - *pwi = wi; + if (split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei)) { + if (pwi) { + *pwi = wi; + } + return xtensa_tlb_get_entry(env, dtlb, wi, ei); + } else { + return NULL; } - return xtensa_tlb_get_entry(env, dtlb, wi, ei); } static void xtensa_tlb_set_entry_mmu(const CPUXtensaState *env, @@ -484,7 +494,12 @@ uint32_t HELPER(rtlb0)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { uint32_t wi; const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi); - return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid; + + if (entry) { + return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid; + } else { + return 0; + } } else { return v & REGION_PAGE_MASK; } @@ -493,7 +508,12 @@ uint32_t HELPER(rtlb0)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) uint32_t HELPER(rtlb1)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) { const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, NULL); - return entry->paddr | entry->attr; + + if (entry) { + return entry->paddr | entry->attr; + } else { + return 0; + } } void HELPER(itlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) @@ -501,7 +521,7 @@ void HELPER(itlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { uint32_t wi; xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi); - if (entry->variable && entry->asid) { + if (entry && entry->variable && entry->asid) { tlb_flush_page(env_cpu(env), entry->vaddr); entry->asid = 0; } @@ -539,8 +559,9 @@ void HELPER(wtlb)(CPUXtensaState *env, uint32_t p, uint32_t v, uint32_t dtlb) uint32_t vpn; uint32_t wi; uint32_t ei; - split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei); - xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p); + if (split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei)) { + xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p); + } } /*! diff --git a/target/xtensa/op_helper.c b/target/xtensa/op_helper.c index 1af7becc54..496754ba57 100644 --- a/target/xtensa/op_helper.c +++ b/target/xtensa/op_helper.c @@ -26,19 +26,18 @@ */ #include "qemu/osdep.h" -#include "qemu/main-loop.h" #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "qemu/atomic.h" #include "qemu/timer.h" #ifndef CONFIG_USER_ONLY void HELPER(update_ccount)(CPUXtensaState *env) { - XtensaCPU *cpu = XTENSA_CPU(env_cpu(env)); + XtensaCPU *cpu = env_archcpu(env); uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); env->ccount_time = now; @@ -59,7 +58,7 @@ void HELPER(wsr_ccount)(CPUXtensaState *env, uint32_t v) void HELPER(update_ccompare)(CPUXtensaState *env, uint32_t i) { - XtensaCPU *cpu = XTENSA_CPU(env_cpu(env)); + XtensaCPU *cpu = env_archcpu(env); uint64_t dcc; qatomic_and(&env->sregs[INTSET], diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 70e11eeb45..b206d57fc4 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -45,6 +45,10 @@ #include "exec/log.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + struct DisasContext { DisasContextBase base; @@ -57,7 +61,6 @@ struct DisasContext { bool sar_5bit; bool sar_m32_5bit; - bool sar_m32_allocated; TCGv_i32 sar_m32; unsigned window; @@ -91,8 +94,6 @@ static TCGv_i32 cpu_exclusive_val; static GHashTable *xtensa_regfile_table; -#include "exec/gen-icount.h" - static char *sr_name[256]; static char *ur_name[256]; @@ -153,49 +154,49 @@ void xtensa_translate_init(void) }; int i; - cpu_pc = tcg_global_mem_new_i32(cpu_env, + cpu_pc = tcg_global_mem_new_i32(tcg_env, offsetof(CPUXtensaState, pc), "pc"); for (i = 0; i < 16; i++) { - cpu_R[i] = tcg_global_mem_new_i32(cpu_env, + cpu_R[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUXtensaState, regs[i]), regnames[i]); } for (i = 0; i < 16; i++) { - cpu_FR[i] = tcg_global_mem_new_i32(cpu_env, + cpu_FR[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUXtensaState, fregs[i].f32[FP_F32_LOW]), fregnames[i]); } for (i = 0; i < 16; i++) { - cpu_FRD[i] = tcg_global_mem_new_i64(cpu_env, + cpu_FRD[i] = tcg_global_mem_new_i64(tcg_env, offsetof(CPUXtensaState, fregs[i].f64), fregnames[i]); } for (i = 0; i < 4; i++) { - cpu_MR[i] = tcg_global_mem_new_i32(cpu_env, + cpu_MR[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUXtensaState, sregs[MR + i]), mregnames[i]); } for (i = 0; i < 16; i++) { - cpu_BR[i] = tcg_global_mem_new_i32(cpu_env, + cpu_BR[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUXtensaState, sregs[BR]), bregnames[i]); if (i % 4 == 0) { - cpu_BR4[i / 4] = tcg_global_mem_new_i32(cpu_env, + cpu_BR4[i / 4] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUXtensaState, sregs[BR]), bregnames[i]); } if (i % 8 == 0) { - cpu_BR8[i / 8] = tcg_global_mem_new_i32(cpu_env, + cpu_BR8[i / 8] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUXtensaState, sregs[BR]), bregnames[i]); @@ -204,7 +205,7 @@ void xtensa_translate_init(void) for (i = 0; i < 256; ++i) { if (sr_name[i]) { - cpu_SR[i] = tcg_global_mem_new_i32(cpu_env, + cpu_SR[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUXtensaState, sregs[i]), sr_name[i]); @@ -213,7 +214,7 @@ void xtensa_translate_init(void) for (i = 0; i < 256; ++i) { if (ur_name[i]) { - cpu_UR[i] = tcg_global_mem_new_i32(cpu_env, + cpu_UR[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUXtensaState, uregs[i]), ur_name[i]); @@ -221,15 +222,15 @@ void xtensa_translate_init(void) } cpu_windowbase_next = - tcg_global_mem_new_i32(cpu_env, + tcg_global_mem_new_i32(tcg_env, offsetof(CPUXtensaState, windowbase_next), "windowbase_next"); cpu_exclusive_addr = - tcg_global_mem_new_i32(cpu_env, + tcg_global_mem_new_i32(tcg_env, offsetof(CPUXtensaState, exclusive_addr), "exclusive_addr"); cpu_exclusive_val = - tcg_global_mem_new_i32(cpu_env, + tcg_global_mem_new_i32(tcg_env, offsetof(CPUXtensaState, exclusive_val), "exclusive_val"); } @@ -284,14 +285,7 @@ static void init_sar_tracker(DisasContext *dc) { dc->sar_5bit = false; dc->sar_m32_5bit = false; - dc->sar_m32_allocated = false; -} - -static void reset_sar_tracker(DisasContext *dc) -{ - if (dc->sar_m32_allocated) { - tcg_temp_free(dc->sar_m32); - } + dc->sar_m32 = NULL; } static void gen_right_shift_sar(DisasContext *dc, TCGv_i32 sa) @@ -306,9 +300,8 @@ static void gen_right_shift_sar(DisasContext *dc, TCGv_i32 sa) static void gen_left_shift_sar(DisasContext *dc, TCGv_i32 sa) { - if (!dc->sar_m32_allocated) { - dc->sar_m32 = tcg_temp_local_new_i32(); - dc->sar_m32_allocated = true; + if (!dc->sar_m32) { + dc->sar_m32 = tcg_temp_new_i32(); } tcg_gen_andi_i32(dc->sar_m32, sa, 0x1f); tcg_gen_sub_i32(cpu_SR[SAR], tcg_constant_i32(32), dc->sar_m32); @@ -318,13 +311,13 @@ static void gen_left_shift_sar(DisasContext *dc, TCGv_i32 sa) static void gen_exception(DisasContext *dc, int excp) { - gen_helper_exception(cpu_env, tcg_constant_i32(excp)); + gen_helper_exception(tcg_env, tcg_constant_i32(excp)); } static void gen_exception_cause(DisasContext *dc, uint32_t cause) { TCGv_i32 pc = tcg_constant_i32(dc->pc); - gen_helper_exception_cause(cpu_env, pc, tcg_constant_i32(cause)); + gen_helper_exception_cause(tcg_env, pc, tcg_constant_i32(cause)); if (cause == ILLEGAL_INSTRUCTION_CAUSE || cause == SYSCALL_CAUSE) { dc->base.is_jmp = DISAS_NORETURN; @@ -334,7 +327,7 @@ static void gen_exception_cause(DisasContext *dc, uint32_t cause) static void gen_debug_exception(DisasContext *dc, uint32_t cause) { TCGv_i32 pc = tcg_constant_i32(dc->pc); - gen_helper_debug_exception(cpu_env, pc, tcg_constant_i32(cause)); + gen_helper_debug_exception(tcg_env, pc, tcg_constant_i32(cause)); if (cause & (DEBUGCAUSE_IB | DEBUGCAUSE_BI | DEBUGCAUSE_BN)) { dc->base.is_jmp = DISAS_NORETURN; } @@ -543,7 +536,7 @@ static bool gen_window_check(DisasContext *dc, uint32_t mask) TCGv_i32 pc = tcg_constant_i32(dc->pc); TCGv_i32 w = tcg_constant_i32(r / 4); - gen_helper_window_check(cpu_env, pc, w); + gen_helper_window_check(tcg_env, pc, w); dc->base.is_jmp = DISAS_NORETURN; return false; } @@ -582,14 +575,12 @@ static int gen_postprocess(DisasContext *dc, int slot) #ifndef CONFIG_USER_ONLY if (op_flags & XTENSA_OP_CHECK_INTERRUPTS) { - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_check_interrupts(cpu_env); + translator_io_start(&dc->base); + gen_helper_check_interrupts(tcg_env); } #endif if (op_flags & XTENSA_OP_SYNC_REGISTER_WINDOW) { - gen_helper_sync_windowbase(cpu_env); + gen_helper_sync_windowbase(tcg_env); } if (op_flags & XTENSA_OP_EXIT_TB_M1) { slot = -1; @@ -1051,13 +1042,13 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) if (op_flags & XTENSA_OP_UNDERFLOW) { TCGv_i32 pc = tcg_constant_i32(dc->pc); - gen_helper_test_underflow_retw(cpu_env, pc); + gen_helper_test_underflow_retw(tcg_env, pc); } if (op_flags & XTENSA_OP_ALLOCA) { TCGv_i32 pc = tcg_constant_i32(dc->pc); - gen_helper_movsp(cpu_env, pc); + gen_helper_movsp(tcg_env, pc); } if (coprocessor && !gen_check_cpenable(dc, coprocessor)) { @@ -1074,10 +1065,10 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) if (i == 0 || arg_copy[i].resource != resource) { resource = arg_copy[i].resource; if (arg_copy[i].arg->num_bits <= 32) { - temp = tcg_temp_local_new_i32(); + temp = tcg_temp_new_i32(); tcg_gen_mov_i32(temp, arg_copy[i].arg->in); } else if (arg_copy[i].arg->num_bits <= 64) { - temp = tcg_temp_local_new_i64(); + temp = tcg_temp_new_i64(); tcg_gen_mov_i64(temp, arg_copy[i].arg->in); } else { g_assert_not_reached(); @@ -1111,16 +1102,6 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) ops->translate(dc, pslot->arg, ops->par); } - for (i = 0; i < n_arg_copy; ++i) { - if (arg_copy[i].arg->num_bits <= 32) { - tcg_temp_free_i32(arg_copy[i].temp); - } else if (arg_copy[i].arg->num_bits <= 64) { - tcg_temp_free_i64(arg_copy[i].temp); - } else { - g_assert_not_reached(); - } - } - if (dc->base.is_jmp == DISAS_NEXT) { gen_postprocess(dc, 0); dc->op_flags = 0; @@ -1142,27 +1123,13 @@ static inline unsigned xtensa_insn_len(CPUXtensaState *env, DisasContext *dc) return xtensa_op0_insn_len(dc, b0); } -static void gen_ibreak_check(CPUXtensaState *env, DisasContext *dc) -{ - unsigned i; - - for (i = 0; i < dc->config->nibreak; ++i) { - if ((env->sregs[IBREAKENABLE] & (1 << i)) && - env->sregs[IBREAKA + i] == dc->pc) { - gen_debug_exception(dc, DEBUGCAUSE_IB); - break; - } - } -} - static void xtensa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUXtensaState *env = cpu->env_ptr; uint32_t tb_flags = dc->base.tb->flags; - dc->config = env->config; + dc->config = cpu_env(cpu)->config; dc->pc = dc->base.pc_first; dc->ring = tb_flags & XTENSA_TBFLAG_RING_MASK; dc->cring = (tb_flags & XTENSA_TBFLAG_EXCM) ? 0 : dc->ring; @@ -1187,7 +1154,7 @@ static void xtensa_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu) DisasContext *dc = container_of(dcbase, DisasContext, base); if (dc->icount) { - dc->next_icount = tcg_temp_local_new_i32(); + dc->next_icount = tcg_temp_new_i32(); } } @@ -1199,7 +1166,7 @@ static void xtensa_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) static void xtensa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUXtensaState *env = cpu->env_ptr; + CPUXtensaState *env = cpu_env(cpu); target_ulong page_start; /* These two conditions only apply to the first insn in the TB, @@ -1224,10 +1191,6 @@ static void xtensa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) gen_set_label(label); } - if (dc->debug) { - gen_ibreak_check(env, dc); - } - disas_xtensa_insn(env, dc); if (dc->icount) { @@ -1247,11 +1210,6 @@ static void xtensa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - reset_sar_tracker(dc); - if (dc->icount) { - tcg_temp_free(dc->next_icount); - } - switch (dc->base.is_jmp) { case DISAS_NORETURN: break; @@ -1279,16 +1237,17 @@ static const TranslatorOps xtensa_translator_ops = { .disas_log = xtensa_tr_disas_log, }; -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns) +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc = {}; - translator_loop(&xtensa_translator_ops, &dc.base, cpu, tb, max_insns); + translator_loop(cpu, tb, max_insns, pc, host_pc, + &xtensa_translator_ops, &dc.base); } void xtensa_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - XtensaCPU *cpu = XTENSA_CPU(cs); - CPUXtensaState *env = &cpu->env; + CPUXtensaState *env = cpu_env(cs); xtensa_isa isa = env->config->isa; int i, j; @@ -1353,12 +1312,6 @@ void xtensa_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } -void restore_state_to_opc(CPUXtensaState *env, TranslationBlock *tb, - target_ulong *data) -{ - env->pc = data[0]; -} - static void translate_abs(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { @@ -1383,14 +1336,13 @@ static void translate_addx(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_shli_i32(tmp, arg[1].in, par[0]); tcg_gen_add_i32(arg[0].out, tmp, arg[2].in); - tcg_temp_free(tmp); } static void translate_all(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { uint32_t shift = par[1]; - TCGv_i32 mask = tcg_const_i32(((1 << shift) - 1) << arg[1].imm); + TCGv_i32 mask = tcg_constant_i32(((1 << shift) - 1) << arg[1].imm); TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_and_i32(tmp, arg[1].in, mask); @@ -1402,8 +1354,6 @@ static void translate_all(DisasContext *dc, const OpcodeArg arg[], tcg_gen_shri_i32(tmp, tmp, arg[1].imm + shift); tcg_gen_deposit_i32(arg[0].out, arg[0].out, tmp, arg[0].imm, 1); - tcg_temp_free(mask); - tcg_temp_free(tmp); } static void translate_and(DisasContext *dc, const OpcodeArg arg[], @@ -1418,7 +1368,6 @@ static void translate_ball(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_and_i32(tmp, arg[0].in, arg[1].in); gen_brcond(dc, par[0], tmp, arg[1].in, arg[2].imm); - tcg_temp_free(tmp); } static void translate_bany(DisasContext *dc, const OpcodeArg arg[], @@ -1427,7 +1376,6 @@ static void translate_bany(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_and_i32(tmp, arg[0].in, arg[1].in); gen_brcondi(dc, par[0], tmp, 0, arg[2].imm); - tcg_temp_free(tmp); } static void translate_b(DisasContext *dc, const OpcodeArg arg[], @@ -1439,22 +1387,16 @@ static void translate_b(DisasContext *dc, const OpcodeArg arg[], static void translate_bb(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { -#if TARGET_BIG_ENDIAN - TCGv_i32 bit = tcg_const_i32(0x80000000u); -#else - TCGv_i32 bit = tcg_const_i32(0x00000001u); -#endif TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_andi_i32(tmp, arg[1].in, 0x1f); -#if TARGET_BIG_ENDIAN - tcg_gen_shr_i32(bit, bit, tmp); -#else - tcg_gen_shl_i32(bit, bit, tmp); -#endif - tcg_gen_and_i32(tmp, arg[0].in, bit); + if (TARGET_BIG_ENDIAN) { + tcg_gen_shr_i32(tmp, tcg_constant_i32(0x80000000u), tmp); + } else { + tcg_gen_shl_i32(tmp, tcg_constant_i32(0x00000001u), tmp); + } + tcg_gen_and_i32(tmp, arg[0].in, tmp); gen_brcondi(dc, par[0], tmp, 0, arg[2].imm); - tcg_temp_free(tmp); - tcg_temp_free(bit); } static void translate_bbi(DisasContext *dc, const OpcodeArg arg[], @@ -1467,7 +1409,6 @@ static void translate_bbi(DisasContext *dc, const OpcodeArg arg[], tcg_gen_andi_i32(tmp, arg[0].in, 0x00000001u << arg[1].imm); #endif gen_brcondi(dc, par[0], tmp, 0, arg[2].imm); - tcg_temp_free(tmp); } static void translate_bi(DisasContext *dc, const OpcodeArg arg[], @@ -1508,8 +1449,6 @@ static void translate_boolean(DisasContext *dc, const OpcodeArg arg[], tcg_gen_shri_i32(tmp2, arg[2].in, arg[2].imm); op[par[0]](tmp1, tmp1, tmp2); tcg_gen_deposit_i32(arg[0].out, arg[0].out, tmp1, arg[0].imm, 1); - tcg_temp_free(tmp1); - tcg_temp_free(tmp2); } static void translate_bp(DisasContext *dc, const OpcodeArg arg[], @@ -1519,7 +1458,6 @@ static void translate_bp(DisasContext *dc, const OpcodeArg arg[], tcg_gen_andi_i32(tmp, arg[0].in, 1 << arg[0].imm); gen_brcondi(dc, par[0], tmp, 0, arg[1].imm); - tcg_temp_free(tmp); } static void translate_call0(DisasContext *dc, const OpcodeArg arg[], @@ -1532,9 +1470,8 @@ static void translate_call0(DisasContext *dc, const OpcodeArg arg[], static void translate_callw(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - TCGv_i32 tmp = tcg_const_i32(arg[0].imm); + TCGv_i32 tmp = tcg_constant_i32(arg[0].imm); gen_callw_slot(dc, par[0], tmp, adjust_jump_slot(dc, arg[0].imm, 0)); - tcg_temp_free(tmp); } static void translate_callx0(DisasContext *dc, const OpcodeArg arg[], @@ -1544,7 +1481,6 @@ static void translate_callx0(DisasContext *dc, const OpcodeArg arg[], tcg_gen_mov_i32(tmp, arg[0].in); tcg_gen_movi_i32(cpu_R[0], dc->base.pc_next); gen_jump(dc, tmp); - tcg_temp_free(tmp); } static void translate_callxw(DisasContext *dc, const OpcodeArg arg[], @@ -1554,19 +1490,16 @@ static void translate_callxw(DisasContext *dc, const OpcodeArg arg[], tcg_gen_mov_i32(tmp, arg[0].in); gen_callw_slot(dc, par[0], tmp, -1); - tcg_temp_free(tmp); } static void translate_clamps(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - TCGv_i32 tmp1 = tcg_const_i32(-1u << arg[2].imm); - TCGv_i32 tmp2 = tcg_const_i32((1 << arg[2].imm) - 1); + TCGv_i32 tmp1 = tcg_constant_i32(-1u << arg[2].imm); + TCGv_i32 tmp2 = tcg_constant_i32((1 << arg[2].imm) - 1); - tcg_gen_smax_i32(tmp1, tmp1, arg[1].in); - tcg_gen_smin_i32(arg[0].out, tmp1, tmp2); - tcg_temp_free(tmp1); - tcg_temp_free(tmp2); + tcg_gen_smax_i32(arg[0].out, tmp1, arg[1].in); + tcg_gen_smin_i32(arg[0].out, arg[0].out, tmp2); } static void translate_clrb_expstate(DisasContext *dc, const OpcodeArg arg[], @@ -1585,10 +1518,9 @@ static void translate_clrex(DisasContext *dc, const OpcodeArg arg[], static void translate_const16(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - TCGv_i32 c = tcg_const_i32(arg[1].imm); + TCGv_i32 c = tcg_constant_i32(arg[1].imm); tcg_gen_deposit_i32(arg[0].out, c, arg[0].in, 16, 16); - tcg_temp_free(c); } static void translate_dcache(DisasContext *dc, const OpcodeArg arg[], @@ -1598,9 +1530,7 @@ static void translate_dcache(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 res = tcg_temp_new_i32(); tcg_gen_addi_i32(addr, arg[0].in, arg[1].imm); - tcg_gen_qemu_ld8u(res, addr, dc->cring); - tcg_temp_free(addr); - tcg_temp_free(res); + tcg_gen_qemu_ld_i32(res, addr, dc->cring, MO_UB); } static void translate_depbits(DisasContext *dc, const OpcodeArg arg[], @@ -1640,7 +1570,7 @@ static void translate_entry(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 pc = tcg_constant_i32(dc->pc); TCGv_i32 s = tcg_constant_i32(arg[0].imm); TCGv_i32 imm = tcg_constant_i32(arg[1].imm); - gen_helper_entry(cpu_env, pc, s, imm); + gen_helper_entry(tcg_env, pc, s, imm); } static void translate_extui(DisasContext *dc, const OpcodeArg arg[], @@ -1651,7 +1581,6 @@ static void translate_extui(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_shri_i32(tmp, arg[1].in, arg[2].imm); tcg_gen_andi_i32(arg[0].out, tmp, maskimm); - tcg_temp_free(tmp); } static void translate_getex(DisasContext *dc, const OpcodeArg arg[], @@ -1662,7 +1591,6 @@ static void translate_getex(DisasContext *dc, const OpcodeArg arg[], tcg_gen_extract_i32(tmp, cpu_SR[ATOMCTL], 8, 1); tcg_gen_deposit_i32(cpu_SR[ATOMCTL], cpu_SR[ATOMCTL], arg[0].in, 8, 1); tcg_gen_mov_i32(arg[0].out, tmp); - tcg_temp_free(tmp); } static void translate_icache(DisasContext *dc, const OpcodeArg arg[], @@ -1673,8 +1601,7 @@ static void translate_icache(DisasContext *dc, const OpcodeArg arg[], tcg_gen_movi_i32(cpu_pc, dc->pc); tcg_gen_addi_i32(addr, arg[0].in, arg[1].imm); - gen_helper_itlb_hit_test(cpu_env, addr); - tcg_temp_free(addr); + gen_helper_itlb_hit_test(tcg_env, addr); #endif } @@ -1684,7 +1611,7 @@ static void translate_itlb(DisasContext *dc, const OpcodeArg arg[], #ifndef CONFIG_USER_ONLY TCGv_i32 dtlb = tcg_constant_i32(par[0]); - gen_helper_itlb(cpu_env, arg[0].in, dtlb); + gen_helper_itlb(tcg_env, arg[0].in, dtlb); #endif } @@ -1709,7 +1636,6 @@ static void translate_l32e(DisasContext *dc, const OpcodeArg arg[], tcg_gen_addi_i32(addr, arg[1].in, arg[2].imm); mop = gen_load_store_alignment(dc, MO_TEUL, addr); tcg_gen_qemu_ld_tl(arg[0].out, addr, dc->ring, mop); - tcg_temp_free(addr); } #ifdef CONFIG_USER_ONLY @@ -1722,7 +1648,7 @@ static void gen_check_exclusive(DisasContext *dc, TCGv_i32 addr, bool is_write) if (!option_enabled(dc, XTENSA_OPTION_MPU)) { TCGv_i32 pc = tcg_constant_i32(dc->pc); - gen_helper_check_exclusive(cpu_env, pc, addr, + gen_helper_check_exclusive(tcg_env, pc, addr, tcg_constant_i32(is_write)); } } @@ -1740,7 +1666,6 @@ static void translate_l32ex(DisasContext *dc, const OpcodeArg arg[], tcg_gen_qemu_ld_i32(arg[0].out, addr, dc->cring, mop); tcg_gen_mov_i32(cpu_exclusive_addr, addr); tcg_gen_mov_i32(cpu_exclusive_val, arg[0].out); - tcg_temp_free(addr); } static void translate_ldst(DisasContext *dc, const OpcodeArg arg[], @@ -1763,7 +1688,6 @@ static void translate_ldst(DisasContext *dc, const OpcodeArg arg[], tcg_gen_mb(TCG_BAR_LDAQ | TCG_MO_ALL); } } - tcg_temp_free(addr); } static void translate_lct(DisasContext *dc, const OpcodeArg arg[], @@ -1778,13 +1702,12 @@ static void translate_l32r(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 tmp; if (dc->base.tb->flags & XTENSA_TBFLAG_LITBASE) { - tmp = tcg_const_i32(arg[1].raw_imm - 1); - tcg_gen_add_i32(tmp, cpu_SR[LITBASE], tmp); + tmp = tcg_temp_new(); + tcg_gen_addi_i32(tmp, cpu_SR[LITBASE], arg[1].raw_imm - 1); } else { - tmp = tcg_const_i32(arg[1].imm); + tmp = tcg_constant_i32(arg[1].imm); } - tcg_gen_qemu_ld32u(arg[0].out, tmp, dc->cring); - tcg_temp_free(tmp); + tcg_gen_qemu_ld_i32(arg[0].out, tmp, dc->cring, MO_TEUL); } static void translate_loop(DisasContext *dc, const OpcodeArg arg[], @@ -1870,19 +1793,12 @@ static void translate_mac16(DisasContext *dc, const OpcodeArg arg[], lo, hi); } tcg_gen_ext8s_i32(cpu_SR[ACCHI], cpu_SR[ACCHI]); - - tcg_temp_free_i32(lo); - tcg_temp_free_i32(hi); } - tcg_temp_free(m1); - tcg_temp_free(m2); } if (ld_offset) { tcg_gen_mov_i32(arg[1].out, vaddr); tcg_gen_mov_i32(cpu_SR[MR + arg[0].imm], mem32); } - tcg_temp_free(vaddr); - tcg_temp_free(mem32); } static void translate_memw(DisasContext *dc, const OpcodeArg arg[], @@ -1946,7 +1862,6 @@ static void translate_movp(DisasContext *dc, const OpcodeArg arg[], tcg_gen_movcond_i32(par[0], arg[0].out, tmp, zero, arg[1].in, arg[0].in); - tcg_temp_free(tmp); } static void translate_movsp(DisasContext *dc, const OpcodeArg arg[], @@ -1969,8 +1884,6 @@ static void translate_mul16(DisasContext *dc, const OpcodeArg arg[], tcg_gen_ext16u_i32(v2, arg[2].in); } tcg_gen_mul_i32(arg[0].out, v1, v2); - tcg_temp_free(v2); - tcg_temp_free(v1); } static void translate_mull(DisasContext *dc, const OpcodeArg arg[], @@ -1989,7 +1902,6 @@ static void translate_mulh(DisasContext *dc, const OpcodeArg arg[], } else { tcg_gen_mulu2_i32(lo, arg[0].out, arg[1].in, arg[2].in); } - tcg_temp_free(lo); } static void translate_neg(DisasContext *dc, const OpcodeArg arg[], @@ -2028,7 +1940,7 @@ static void translate_ptlb(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 dtlb = tcg_constant_i32(par[0]); tcg_gen_movi_i32(cpu_pc, dc->pc); - gen_helper_ptlb(arg[0].out, cpu_env, arg[1].in, dtlb); + gen_helper_ptlb(arg[0].out, tcg_env, arg[1].in, dtlb); #endif } @@ -2037,7 +1949,7 @@ static void translate_pptlb(DisasContext *dc, const OpcodeArg arg[], { #ifndef CONFIG_USER_ONLY tcg_gen_movi_i32(cpu_pc, dc->pc); - gen_helper_pptlb(arg[0].out, cpu_env, arg[1].in); + gen_helper_pptlb(arg[0].out, tcg_env, arg[1].in); #endif } @@ -2089,7 +2001,7 @@ static void translate_remu(DisasContext *dc, const OpcodeArg arg[], static void translate_rer(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_rer(arg[0].out, cpu_env, arg[1].in); + gen_helper_rer(arg[0].out, tcg_env, arg[1].in); } static void translate_ret(DisasContext *dc, const OpcodeArg arg[], @@ -2108,7 +2020,7 @@ static uint32_t test_exceptions_retw(DisasContext *dc, const OpcodeArg arg[], } else { TCGv_i32 pc = tcg_constant_i32(dc->pc); - gen_helper_test_ill_retw(cpu_env, pc); + gen_helper_test_ill_retw(tcg_env, pc); return 0; } } @@ -2116,15 +2028,14 @@ static uint32_t test_exceptions_retw(DisasContext *dc, const OpcodeArg arg[], static void translate_retw(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - TCGv_i32 tmp = tcg_const_i32(1); - tcg_gen_shl_i32(tmp, tmp, cpu_SR[WINDOW_BASE]); + TCGv_i32 tmp = tcg_temp_new(); + tcg_gen_shl_i32(tmp, tcg_constant_i32(1), cpu_SR[WINDOW_BASE]); tcg_gen_andc_i32(cpu_SR[WINDOW_START], cpu_SR[WINDOW_START], tmp); tcg_gen_movi_i32(tmp, dc->pc); tcg_gen_deposit_i32(tmp, tmp, cpu_R[0], 0, 30); - gen_helper_retw(cpu_env, cpu_R[0]); + gen_helper_retw(tcg_env, cpu_R[0]); gen_jump(dc, tmp); - tcg_temp_free(tmp); } static void translate_rfde(DisasContext *dc, const OpcodeArg arg[], @@ -2150,10 +2061,10 @@ static void translate_rfi(DisasContext *dc, const OpcodeArg arg[], static void translate_rfw(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - TCGv_i32 tmp = tcg_const_i32(1); + TCGv_i32 tmp = tcg_temp_new(); tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_EXCM); - tcg_gen_shl_i32(tmp, tmp, cpu_SR[WINDOW_BASE]); + tcg_gen_shl_i32(tmp, tcg_constant_i32(1), cpu_SR[WINDOW_BASE]); if (par[0]) { tcg_gen_andc_i32(cpu_SR[WINDOW_START], @@ -2163,8 +2074,7 @@ static void translate_rfw(DisasContext *dc, const OpcodeArg arg[], cpu_SR[WINDOW_START], tmp); } - tcg_temp_free(tmp); - gen_helper_restore_owb(cpu_env); + gen_helper_restore_owb(tcg_env); gen_jump(dc, cpu_SR[EPC1]); } @@ -2196,10 +2106,8 @@ static void translate_rsr_ccount(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_update_ccount(cpu_env); + translator_io_start(&dc->base); + gen_helper_update_ccount(tcg_env); tcg_gen_mov_i32(arg[0].out, cpu_SR[par[0]]); #endif } @@ -2213,7 +2121,6 @@ static void translate_rsr_ptevaddr(DisasContext *dc, const OpcodeArg arg[], tcg_gen_shri_i32(tmp, cpu_SR[EXCVADDR], 10); tcg_gen_or_i32(tmp, tmp, cpu_SR[PTEVADDR]); tcg_gen_andi_i32(arg[0].out, tmp, 0xfffffffc); - tcg_temp_free(tmp); #endif } @@ -2228,7 +2135,7 @@ static void translate_rtlb(DisasContext *dc, const OpcodeArg arg[], }; TCGv_i32 dtlb = tcg_constant_i32(par[0]); - helper[par[1]](arg[0].out, cpu_env, arg[1].in, dtlb); + helper[par[1]](arg[0].out, tcg_env, arg[1].in, dtlb); #endif } @@ -2236,7 +2143,7 @@ static void translate_rptlb0(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - gen_helper_rptlb0(arg[0].out, cpu_env, arg[1].in); + gen_helper_rptlb0(arg[0].out, tcg_env, arg[1].in); #endif } @@ -2244,7 +2151,7 @@ static void translate_rptlb1(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - gen_helper_rptlb1(arg[0].out, cpu_env, arg[1].in); + gen_helper_rptlb1(arg[0].out, tcg_env, arg[1].in); #endif } @@ -2270,15 +2177,15 @@ static void gen_check_atomctl(DisasContext *dc, TCGv_i32 addr) { TCGv_i32 pc = tcg_constant_i32(dc->pc); - gen_helper_check_atomctl(cpu_env, pc, addr); + gen_helper_check_atomctl(tcg_env, pc, addr); } #endif static void translate_s32c1i(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - TCGv_i32 tmp = tcg_temp_local_new_i32(); - TCGv_i32 addr = tcg_temp_local_new_i32(); + TCGv_i32 tmp = tcg_temp_new_i32(); + TCGv_i32 addr = tcg_temp_new_i32(); MemOp mop; tcg_gen_mov_i32(tmp, arg[0].in); @@ -2287,8 +2194,6 @@ static void translate_s32c1i(DisasContext *dc, const OpcodeArg arg[], gen_check_atomctl(dc, addr); tcg_gen_atomic_cmpxchg_i32(arg[0].out, addr, cpu_SR[SCOMPARE1], tmp, dc->cring, mop); - tcg_temp_free(addr); - tcg_temp_free(tmp); } static void translate_s32e(DisasContext *dc, const OpcodeArg arg[], @@ -2300,15 +2205,14 @@ static void translate_s32e(DisasContext *dc, const OpcodeArg arg[], tcg_gen_addi_i32(addr, arg[1].in, arg[2].imm); mop = gen_load_store_alignment(dc, MO_TEUL, addr); tcg_gen_qemu_st_tl(arg[0].in, addr, dc->ring, mop); - tcg_temp_free(addr); } static void translate_s32ex(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { TCGv_i32 prev = tcg_temp_new_i32(); - TCGv_i32 addr = tcg_temp_local_new_i32(); - TCGv_i32 res = tcg_temp_local_new_i32(); + TCGv_i32 addr = tcg_temp_new_i32(); + TCGv_i32 res = tcg_temp_new_i32(); TCGLabel *label = gen_new_label(); MemOp mop; @@ -2326,9 +2230,6 @@ static void translate_s32ex(DisasContext *dc, const OpcodeArg arg[], gen_set_label(label); tcg_gen_extract_i32(arg[0].out, cpu_SR[ATOMCTL], 8, 1); tcg_gen_deposit_i32(cpu_SR[ATOMCTL], cpu_SR[ATOMCTL], res, 8, 1); - tcg_temp_free(prev); - tcg_temp_free(addr); - tcg_temp_free(res); } static void translate_salt(DisasContext *dc, const OpcodeArg arg[], @@ -2342,31 +2243,21 @@ static void translate_salt(DisasContext *dc, const OpcodeArg arg[], static void translate_sext(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - int shift = 31 - arg[2].imm; - - if (shift == 24) { - tcg_gen_ext8s_i32(arg[0].out, arg[1].in); - } else if (shift == 16) { - tcg_gen_ext16s_i32(arg[0].out, arg[1].in); - } else { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_shli_i32(tmp, arg[1].in, shift); - tcg_gen_sari_i32(arg[0].out, tmp, shift); - tcg_temp_free(tmp); - } + tcg_gen_sextract_i32(arg[0].out, arg[1].in, 0, arg[2].imm + 1); } static uint32_t test_exceptions_simcall(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { + bool is_semi = semihosting_enabled(dc->cring != 0); #ifdef CONFIG_USER_ONLY bool ill = true; #else /* Between RE.2 and RE.3 simcall opcode's become nop for the hardware. */ - bool ill = dc->config->hw_version <= 250002 && !semihosting_enabled(); + bool ill = dc->config->hw_version <= 250002 && !is_semi; #endif - if (ill || !semihosting_enabled()) { + if (ill || !is_semi) { qemu_log_mask(LOG_GUEST_ERROR, "SIMCALL but semihosting is disabled\n"); } return ill ? XTENSA_OP_ILL : 0; @@ -2376,8 +2267,8 @@ static void translate_simcall(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - if (semihosting_enabled()) { - gen_helper_simcall(cpu_env); + if (semihosting_enabled(dc->cring != 0)) { + gen_helper_simcall(tcg_env); } #endif } @@ -2391,8 +2282,6 @@ static void translate_simcall(DisasContext *dc, const OpcodeArg arg[], tcg_gen_extu_i32_i64(tmp, reg); \ tcg_gen_##cmd##_i64(v, v, tmp); \ tcg_gen_extrl_i64_i32(arg[0].out, v); \ - tcg_temp_free_i64(v); \ - tcg_temp_free_i64(tmp); \ } while (0) #define gen_shift(cmd) gen_shift_reg(cmd, cpu_SR[SAR]) @@ -2404,12 +2293,11 @@ static void translate_sll(DisasContext *dc, const OpcodeArg arg[], tcg_gen_shl_i32(arg[0].out, arg[1].in, dc->sar_m32); } else { TCGv_i64 v = tcg_temp_new_i64(); - TCGv_i32 s = tcg_const_i32(32); - tcg_gen_sub_i32(s, s, cpu_SR[SAR]); + TCGv_i32 s = tcg_temp_new(); + tcg_gen_subfi_i32(s, 32, cpu_SR[SAR]); tcg_gen_andi_i32(s, s, 0x3f); tcg_gen_extu_i32_i64(v, arg[1].in); gen_shift_reg(shl, s); - tcg_temp_free(s); } } @@ -2476,7 +2364,6 @@ static void translate_ssa8b(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_shli_i32(tmp, arg[0].in, 3); gen_left_shift_sar(dc, tmp); - tcg_temp_free(tmp); } static void translate_ssa8l(DisasContext *dc, const OpcodeArg arg[], @@ -2485,7 +2372,6 @@ static void translate_ssa8l(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_shli_i32(tmp, arg[0].in, 3); gen_right_shift_sar(dc, tmp); - tcg_temp_free(tmp); } static void translate_ssai(DisasContext *dc, const OpcodeArg arg[], @@ -2518,7 +2404,6 @@ static void translate_subx(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_shli_i32(tmp, arg[1].in, par[0]); tcg_gen_sub_i32(arg[0].out, tmp, arg[2].in); - tcg_temp_free(tmp); } static void translate_waiti(DisasContext *dc, const OpcodeArg arg[], @@ -2527,10 +2412,8 @@ static void translate_waiti(DisasContext *dc, const OpcodeArg arg[], #ifndef CONFIG_USER_ONLY TCGv_i32 pc = tcg_constant_i32(dc->base.pc_next); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_waiti(cpu_env, pc, tcg_constant_i32(arg[0].imm)); + translator_io_start(&dc->base); + gen_helper_waiti(tcg_env, pc, tcg_constant_i32(arg[0].imm)); #endif } @@ -2540,7 +2423,7 @@ static void translate_wtlb(DisasContext *dc, const OpcodeArg arg[], #ifndef CONFIG_USER_ONLY TCGv_i32 dtlb = tcg_constant_i32(par[0]); - gen_helper_wtlb(cpu_env, arg[0].in, arg[1].in, dtlb); + gen_helper_wtlb(tcg_env, arg[0].in, arg[1].in, dtlb); #endif } @@ -2548,14 +2431,14 @@ static void translate_wptlb(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - gen_helper_wptlb(cpu_env, arg[0].in, arg[1].in); + gen_helper_wptlb(tcg_env, arg[0].in, arg[1].in); #endif } static void translate_wer(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_wer(cpu_env, arg[0].in, arg[1].in); + gen_helper_wer(tcg_env, arg[0].in, arg[1].in); } static void translate_wrmsk_expstate(DisasContext *dc, const OpcodeArg arg[], @@ -2594,11 +2477,9 @@ static void translate_wsr_ccompare(DisasContext *dc, const OpcodeArg arg[], uint32_t id = par[0] - CCOMPARE; assert(id < dc->config->nccompare); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + translator_io_start(&dc->base); tcg_gen_mov_i32(cpu_SR[par[0]], arg[0].in); - gen_helper_update_ccompare(cpu_env, tcg_constant_i32(id)); + gen_helper_update_ccompare(tcg_env, tcg_constant_i32(id)); #endif } @@ -2606,10 +2487,8 @@ static void translate_wsr_ccount(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_wsr_ccount(cpu_env, arg[0].in); + translator_io_start(&dc->base); + gen_helper_wsr_ccount(tcg_env, arg[0].in); #endif } @@ -2620,7 +2499,7 @@ static void translate_wsr_dbreaka(DisasContext *dc, const OpcodeArg arg[], unsigned id = par[0] - DBREAKA; assert(id < dc->config->ndbreak); - gen_helper_wsr_dbreaka(cpu_env, tcg_constant_i32(id), arg[0].in); + gen_helper_wsr_dbreaka(tcg_env, tcg_constant_i32(id), arg[0].in); #endif } @@ -2631,7 +2510,7 @@ static void translate_wsr_dbreakc(DisasContext *dc, const OpcodeArg arg[], unsigned id = par[0] - DBREAKC; assert(id < dc->config->ndbreak); - gen_helper_wsr_dbreakc(cpu_env, tcg_constant_i32(id), arg[0].in); + gen_helper_wsr_dbreakc(tcg_env, tcg_constant_i32(id), arg[0].in); #endif } @@ -2642,7 +2521,7 @@ static void translate_wsr_ibreaka(DisasContext *dc, const OpcodeArg arg[], unsigned id = par[0] - IBREAKA; assert(id < dc->config->nibreak); - gen_helper_wsr_ibreaka(cpu_env, tcg_constant_i32(id), arg[0].in); + gen_helper_wsr_ibreaka(tcg_env, tcg_constant_i32(id), arg[0].in); #endif } @@ -2650,7 +2529,7 @@ static void translate_wsr_ibreakenable(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - gen_helper_wsr_ibreakenable(cpu_env, arg[0].in); + gen_helper_wsr_ibreakenable(tcg_env, arg[0].in); #endif } @@ -2670,7 +2549,7 @@ static void translate_wsr_intclear(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - gen_helper_intclear(cpu_env, arg[0].in); + gen_helper_intclear(tcg_env, arg[0].in); #endif } @@ -2678,7 +2557,7 @@ static void translate_wsr_intset(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - gen_helper_intset(cpu_env, arg[0].in); + gen_helper_intset(tcg_env, arg[0].in); #endif } @@ -2686,7 +2565,7 @@ static void translate_wsr_memctl(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - gen_helper_wsr_memctl(cpu_env, arg[0].in); + gen_helper_wsr_memctl(tcg_env, arg[0].in); #endif } @@ -2694,7 +2573,7 @@ static void translate_wsr_mpuenb(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - gen_helper_wsr_mpuenb(cpu_env, arg[0].in); + gen_helper_wsr_mpuenb(tcg_env, arg[0].in); #endif } @@ -2717,7 +2596,7 @@ static void translate_wsr_rasid(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - gen_helper_wsr_rasid(cpu_env, arg[0].in); + gen_helper_wsr_rasid(tcg_env, arg[0].in); #endif } @@ -2770,7 +2649,6 @@ static void translate_xsr(DisasContext *dc, const OpcodeArg arg[], tcg_gen_mov_i32(tmp, arg[0].in); tcg_gen_mov_i32(arg[0].out, cpu_SR[par[0]]); tcg_gen_mov_i32(cpu_SR[par[0]], tmp); - tcg_temp_free(tmp); } else { tcg_gen_movi_i32(arg[0].out, 0); } @@ -2785,7 +2663,6 @@ static void translate_xsr_mask(DisasContext *dc, const OpcodeArg arg[], tcg_gen_mov_i32(tmp, arg[0].in); tcg_gen_mov_i32(arg[0].out, cpu_SR[par[0]]); tcg_gen_andi_i32(cpu_SR[par[0]], tmp, par[2]); - tcg_temp_free(tmp); } else { tcg_gen_movi_i32(arg[0].out, 0); } @@ -2797,15 +2674,11 @@ static void translate_xsr_ccount(DisasContext *dc, const OpcodeArg arg[], #ifndef CONFIG_USER_ONLY TCGv_i32 tmp = tcg_temp_new_i32(); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - - gen_helper_update_ccount(cpu_env); + translator_io_start(&dc->base); + gen_helper_update_ccount(tcg_env); tcg_gen_mov_i32(tmp, cpu_SR[par[0]]); - gen_helper_wsr_ccount(cpu_env, arg[0].in); + gen_helper_wsr_ccount(tcg_env, arg[0].in); tcg_gen_mov_i32(arg[0].out, tmp); - tcg_temp_free(tmp); #endif } @@ -2823,7 +2696,6 @@ static void translate_xsr_ccount(DisasContext *dc, const OpcodeArg arg[], } \ translate_wsr_##name(dc, arg, par); \ tcg_gen_mov_i32(arg[0].out, tmp); \ - tcg_temp_free(tmp); \ } gen_translate_xsr(acchi) @@ -6310,16 +6182,6 @@ static inline void put_f32_o1_i3(const OpcodeArg *arg, const OpcodeArg *arg32, (o0 >= 0 && arg[o0].num_bits == 64)) { if (o0 >= 0) { tcg_gen_extu_i32_i64(arg[o0].out, arg32[o0].out); - tcg_temp_free_i32(arg32[o0].out); - } - if (i0 >= 0) { - tcg_temp_free_i32(arg32[i0].in); - } - if (i1 >= 0) { - tcg_temp_free_i32(arg32[i1].in); - } - if (i2 >= 0) { - tcg_temp_free_i32(arg32[i2].in); } } } @@ -6404,7 +6266,7 @@ static void translate_abs_s(DisasContext *dc, const OpcodeArg arg[], static void translate_fpu2k_add_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_fpu2k_add_s(arg[0].out, cpu_env, + gen_helper_fpu2k_add_s(arg[0].out, tcg_env, arg[1].in, arg[2].in); } @@ -6439,13 +6301,10 @@ static void translate_compare_d(DisasContext *dc, const OpcodeArg arg[], tcg_gen_ori_i32(set_br, arg[0].in, 1 << arg[0].imm); tcg_gen_andi_i32(clr_br, arg[0].in, ~(1 << arg[0].imm)); - helper[par[0]](res, cpu_env, arg[1].in, arg[2].in); + helper[par[0]](res, tcg_env, arg[1].in, arg[2].in); tcg_gen_movcond_i32(TCG_COND_NE, arg[0].out, res, zero, set_br, clr_br); - tcg_temp_free(res); - tcg_temp_free(set_br); - tcg_temp_free(clr_br); } static void translate_compare_s(DisasContext *dc, const OpcodeArg arg[], @@ -6471,14 +6330,11 @@ static void translate_compare_s(DisasContext *dc, const OpcodeArg arg[], tcg_gen_andi_i32(clr_br, arg[0].in, ~(1 << arg[0].imm)); get_f32_i2(arg, arg32, 1, 2); - helper[par[0]](res, cpu_env, arg32[1].in, arg32[2].in); + helper[par[0]](res, tcg_env, arg32[1].in, arg32[2].in); tcg_gen_movcond_i32(TCG_COND_NE, arg[0].out, res, zero, set_br, clr_br); put_f32_i2(arg, arg32, 1, 2); - tcg_temp_free(res); - tcg_temp_free(set_br); - tcg_temp_free(clr_br); } static void translate_const_d(DisasContext *dc, const OpcodeArg arg[], @@ -6527,9 +6383,9 @@ static void translate_float_d(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 scale = tcg_constant_i32(-arg[2].imm); if (par[0]) { - gen_helper_uitof_d(arg[0].out, cpu_env, arg[1].in, scale); + gen_helper_uitof_d(arg[0].out, tcg_env, arg[1].in, scale); } else { - gen_helper_itof_d(arg[0].out, cpu_env, arg[1].in, scale); + gen_helper_itof_d(arg[0].out, tcg_env, arg[1].in, scale); } } @@ -6541,9 +6397,9 @@ static void translate_float_s(DisasContext *dc, const OpcodeArg arg[], get_f32_o1(arg, arg32, 0); if (par[0]) { - gen_helper_uitof_s(arg32[0].out, cpu_env, arg[1].in, scale); + gen_helper_uitof_s(arg32[0].out, tcg_env, arg[1].in, scale); } else { - gen_helper_itof_s(arg32[0].out, cpu_env, arg[1].in, scale); + gen_helper_itof_s(arg32[0].out, tcg_env, arg[1].in, scale); } put_f32_o1(arg, arg32, 0); } @@ -6555,10 +6411,10 @@ static void translate_ftoi_d(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 scale = tcg_constant_i32(arg[2].imm); if (par[1]) { - gen_helper_ftoui_d(arg[0].out, cpu_env, arg[1].in, + gen_helper_ftoui_d(arg[0].out, tcg_env, arg[1].in, rounding_mode, scale); } else { - gen_helper_ftoi_d(arg[0].out, cpu_env, arg[1].in, + gen_helper_ftoi_d(arg[0].out, tcg_env, arg[1].in, rounding_mode, scale); } } @@ -6572,10 +6428,10 @@ static void translate_ftoi_s(DisasContext *dc, const OpcodeArg arg[], get_f32_i1(arg, arg32, 1); if (par[1]) { - gen_helper_ftoui_s(arg[0].out, cpu_env, arg32[1].in, + gen_helper_ftoui_s(arg[0].out, tcg_env, arg32[1].in, rounding_mode, scale); } else { - gen_helper_ftoi_s(arg[0].out, cpu_env, arg32[1].in, + gen_helper_ftoi_s(arg[0].out, tcg_env, arg32[1].in, rounding_mode, scale); } put_f32_i1(arg, arg32, 1); @@ -6597,7 +6453,6 @@ static void translate_ldsti(DisasContext *dc, const OpcodeArg arg[], if (par[1]) { tcg_gen_mov_i32(arg[1].out, addr); } - tcg_temp_free(addr); } static void translate_ldstx(DisasContext *dc, const OpcodeArg arg[], @@ -6616,13 +6471,12 @@ static void translate_ldstx(DisasContext *dc, const OpcodeArg arg[], if (par[1]) { tcg_gen_mov_i32(arg[1].out, addr); } - tcg_temp_free(addr); } static void translate_fpu2k_madd_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_fpu2k_madd_s(arg[0].out, cpu_env, + gen_helper_fpu2k_madd_s(arg[0].out, tcg_env, arg[0].in, arg[1].in, arg[2].in); } @@ -6652,7 +6506,6 @@ static void translate_movcond_d(DisasContext *dc, const OpcodeArg arg[], tcg_gen_movcond_i64(par[0], arg[0].out, arg2, zero, arg[1].in, arg[0].in); - tcg_temp_free_i64(arg2); } static void translate_movcond_s(DisasContext *dc, const OpcodeArg arg[], @@ -6681,8 +6534,6 @@ static void translate_movp_d(DisasContext *dc, const OpcodeArg arg[], tcg_gen_movcond_i64(par[0], arg[0].out, tmp2, zero, arg[1].in, arg[0].in); - tcg_temp_free_i32(tmp1); - tcg_temp_free_i64(tmp2); } static void translate_movp_s(DisasContext *dc, const OpcodeArg arg[], @@ -6696,7 +6547,6 @@ static void translate_movp_s(DisasContext *dc, const OpcodeArg arg[], tcg_gen_movcond_i32(par[0], arg[0].out, tmp, zero, arg[1].in, arg[0].in); - tcg_temp_free(tmp); } else { translate_movp_d(dc, arg, par); } @@ -6705,14 +6555,14 @@ static void translate_movp_s(DisasContext *dc, const OpcodeArg arg[], static void translate_fpu2k_mul_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_fpu2k_mul_s(arg[0].out, cpu_env, + gen_helper_fpu2k_mul_s(arg[0].out, tcg_env, arg[1].in, arg[2].in); } static void translate_fpu2k_msub_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_fpu2k_msub_s(arg[0].out, cpu_env, + gen_helper_fpu2k_msub_s(arg[0].out, tcg_env, arg[0].in, arg[1].in, arg[2].in); } @@ -6751,7 +6601,7 @@ static void translate_rfr_s(DisasContext *dc, const OpcodeArg arg[], static void translate_fpu2k_sub_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_fpu2k_sub_s(arg[0].out, cpu_env, + gen_helper_fpu2k_sub_s(arg[0].out, tcg_env, arg[1].in, arg[2].in); } @@ -6774,7 +6624,7 @@ static void translate_wfr_s(DisasContext *dc, const OpcodeArg arg[], static void translate_wur_fpu2k_fcr(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_wur_fpu2k_fcr(cpu_env, arg[0].in); + gen_helper_wur_fpu2k_fcr(tcg_env, arg[0].in); } static void translate_wur_fpu2k_fsr(DisasContext *dc, const OpcodeArg arg[], @@ -7003,20 +6853,20 @@ const XtensaOpcodeTranslators xtensa_fpu2000_opcodes = { static void translate_add_d(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_add_d(arg[0].out, cpu_env, arg[1].in, arg[2].in); + gen_helper_add_d(arg[0].out, tcg_env, arg[1].in, arg[2].in); } static void translate_add_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { if (option_enabled(dc, XTENSA_OPTION_DFPU_SINGLE_ONLY)) { - gen_helper_fpu2k_add_s(arg[0].out, cpu_env, + gen_helper_fpu2k_add_s(arg[0].out, tcg_env, arg[1].in, arg[2].in); } else { OpcodeArg arg32[3]; get_f32_o1_i2(arg, arg32, 0, 1, 2); - gen_helper_add_s(arg32[0].out, cpu_env, arg32[1].in, arg32[2].in); + gen_helper_add_s(arg32[0].out, tcg_env, arg32[1].in, arg32[2].in); put_f32_o1_i2(arg, arg32, 0, 1, 2); } } @@ -7027,8 +6877,7 @@ static void translate_cvtd_s(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 v = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(v, arg[1].in); - gen_helper_cvtd_s(arg[0].out, cpu_env, v); - tcg_temp_free_i32(v); + gen_helper_cvtd_s(arg[0].out, tcg_env, v); } static void translate_cvts_d(DisasContext *dc, const OpcodeArg arg[], @@ -7036,9 +6885,8 @@ static void translate_cvts_d(DisasContext *dc, const OpcodeArg arg[], { TCGv_i32 v = tcg_temp_new_i32(); - gen_helper_cvts_d(v, cpu_env, arg[1].in); + gen_helper_cvts_d(v, tcg_env, arg[1].in); tcg_gen_extu_i32_i64(arg[0].out, v); - tcg_temp_free_i32(v); } static void translate_ldsti_d(DisasContext *dc, const OpcodeArg arg[], @@ -7066,9 +6914,6 @@ static void translate_ldsti_d(DisasContext *dc, const OpcodeArg arg[], tcg_gen_addi_i32(arg[1].out, arg[1].in, arg[2].imm); } } - if (par[1]) { - tcg_temp_free(addr); - } } static void translate_ldsti_s(DisasContext *dc, const OpcodeArg arg[], @@ -7101,9 +6946,6 @@ static void translate_ldsti_s(DisasContext *dc, const OpcodeArg arg[], tcg_gen_addi_i32(arg[1].out, arg[1].in, arg[2].imm); } } - if (par[1]) { - tcg_temp_free(addr); - } } static void translate_ldstx_d(DisasContext *dc, const OpcodeArg arg[], @@ -7131,9 +6973,6 @@ static void translate_ldstx_d(DisasContext *dc, const OpcodeArg arg[], tcg_gen_add_i32(arg[1].out, arg[1].in, arg[2].in); } } - if (par[1]) { - tcg_temp_free(addr); - } } static void translate_ldstx_s(DisasContext *dc, const OpcodeArg arg[], @@ -7166,15 +7005,12 @@ static void translate_ldstx_s(DisasContext *dc, const OpcodeArg arg[], tcg_gen_add_i32(arg[1].out, arg[1].in, arg[2].in); } } - if (par[1]) { - tcg_temp_free(addr); - } } static void translate_madd_d(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_madd_d(arg[0].out, cpu_env, + gen_helper_madd_d(arg[0].out, tcg_env, arg[0].in, arg[1].in, arg[2].in); } @@ -7182,13 +7018,13 @@ static void translate_madd_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { if (option_enabled(dc, XTENSA_OPTION_DFPU_SINGLE_ONLY)) { - gen_helper_fpu2k_madd_s(arg[0].out, cpu_env, + gen_helper_fpu2k_madd_s(arg[0].out, tcg_env, arg[0].in, arg[1].in, arg[2].in); } else { OpcodeArg arg32[3]; get_f32_o1_i3(arg, arg32, 0, 0, 1, 2); - gen_helper_madd_s(arg32[0].out, cpu_env, + gen_helper_madd_s(arg32[0].out, tcg_env, arg32[0].in, arg32[1].in, arg32[2].in); put_f32_o1_i3(arg, arg32, 0, 0, 1, 2); } @@ -7197,20 +7033,20 @@ static void translate_madd_s(DisasContext *dc, const OpcodeArg arg[], static void translate_mul_d(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_mul_d(arg[0].out, cpu_env, arg[1].in, arg[2].in); + gen_helper_mul_d(arg[0].out, tcg_env, arg[1].in, arg[2].in); } static void translate_mul_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { if (option_enabled(dc, XTENSA_OPTION_DFPU_SINGLE_ONLY)) { - gen_helper_fpu2k_mul_s(arg[0].out, cpu_env, + gen_helper_fpu2k_mul_s(arg[0].out, tcg_env, arg[1].in, arg[2].in); } else { OpcodeArg arg32[3]; get_f32_o1_i2(arg, arg32, 0, 1, 2); - gen_helper_mul_s(arg32[0].out, cpu_env, arg32[1].in, arg32[2].in); + gen_helper_mul_s(arg32[0].out, tcg_env, arg32[1].in, arg32[2].in); put_f32_o1_i2(arg, arg32, 0, 1, 2); } } @@ -7218,7 +7054,7 @@ static void translate_mul_s(DisasContext *dc, const OpcodeArg arg[], static void translate_msub_d(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_msub_d(arg[0].out, cpu_env, + gen_helper_msub_d(arg[0].out, tcg_env, arg[0].in, arg[1].in, arg[2].in); } @@ -7226,13 +7062,13 @@ static void translate_msub_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { if (option_enabled(dc, XTENSA_OPTION_DFPU_SINGLE_ONLY)) { - gen_helper_fpu2k_msub_s(arg[0].out, cpu_env, + gen_helper_fpu2k_msub_s(arg[0].out, tcg_env, arg[0].in, arg[1].in, arg[2].in); } else { OpcodeArg arg32[3]; get_f32_o1_i3(arg, arg32, 0, 0, 1, 2); - gen_helper_msub_s(arg32[0].out, cpu_env, + gen_helper_msub_s(arg32[0].out, tcg_env, arg32[0].in, arg32[1].in, arg32[2].in); put_f32_o1_i3(arg, arg32, 0, 0, 1, 2); } @@ -7241,20 +7077,20 @@ static void translate_msub_s(DisasContext *dc, const OpcodeArg arg[], static void translate_sub_d(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_sub_d(arg[0].out, cpu_env, arg[1].in, arg[2].in); + gen_helper_sub_d(arg[0].out, tcg_env, arg[1].in, arg[2].in); } static void translate_sub_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { if (option_enabled(dc, XTENSA_OPTION_DFPU_SINGLE_ONLY)) { - gen_helper_fpu2k_sub_s(arg[0].out, cpu_env, + gen_helper_fpu2k_sub_s(arg[0].out, tcg_env, arg[1].in, arg[2].in); } else { OpcodeArg arg32[3]; get_f32_o1_i2(arg, arg32, 0, 1, 2); - gen_helper_sub_s(arg32[0].out, cpu_env, arg32[1].in, arg32[2].in); + gen_helper_sub_s(arg32[0].out, tcg_env, arg32[1].in, arg32[2].in); put_f32_o1_i2(arg, arg32, 0, 1, 2); } } @@ -7262,7 +7098,7 @@ static void translate_sub_s(DisasContext *dc, const OpcodeArg arg[], static void translate_mkdadj_d(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_mkdadj_d(arg[0].out, cpu_env, arg[0].in, arg[1].in); + gen_helper_mkdadj_d(arg[0].out, tcg_env, arg[0].in, arg[1].in); } static void translate_mkdadj_s(DisasContext *dc, const OpcodeArg arg[], @@ -7271,14 +7107,14 @@ static void translate_mkdadj_s(DisasContext *dc, const OpcodeArg arg[], OpcodeArg arg32[2]; get_f32_o1_i2(arg, arg32, 0, 0, 1); - gen_helper_mkdadj_s(arg32[0].out, cpu_env, arg32[0].in, arg32[1].in); + gen_helper_mkdadj_s(arg32[0].out, tcg_env, arg32[0].in, arg32[1].in); put_f32_o1_i2(arg, arg32, 0, 0, 1); } static void translate_mksadj_d(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_mksadj_d(arg[0].out, cpu_env, arg[1].in); + gen_helper_mksadj_d(arg[0].out, tcg_env, arg[1].in); } static void translate_mksadj_s(DisasContext *dc, const OpcodeArg arg[], @@ -7287,26 +7123,26 @@ static void translate_mksadj_s(DisasContext *dc, const OpcodeArg arg[], OpcodeArg arg32[2]; get_f32_o1_i1(arg, arg32, 0, 1); - gen_helper_mksadj_s(arg32[0].out, cpu_env, arg32[1].in); + gen_helper_mksadj_s(arg32[0].out, tcg_env, arg32[1].in); put_f32_o1_i1(arg, arg32, 0, 1); } static void translate_wur_fpu_fcr(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_wur_fpu_fcr(cpu_env, arg[0].in); + gen_helper_wur_fpu_fcr(tcg_env, arg[0].in); } static void translate_rur_fpu_fsr(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_rur_fpu_fsr(arg[0].out, cpu_env); + gen_helper_rur_fpu_fsr(arg[0].out, tcg_env); } static void translate_wur_fpu_fsr(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_helper_wur_fpu_fsr(cpu_env, arg[0].in); + gen_helper_wur_fpu_fsr(tcg_env, arg[0].in); } static const XtensaOpcodeOps fpu_ops[] = { diff --git a/target/xtensa/win_helper.c b/target/xtensa/win_helper.c index 5a1555360a..ec9ff44db0 100644 --- a/target/xtensa/win_helper.c +++ b/target/xtensa/win_helper.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "qemu/main-loop.h" #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" diff --git a/tcg/README b/tcg/README deleted file mode 100644 index bc15cc3b32..0000000000 --- a/tcg/README +++ /dev/null @@ -1,784 +0,0 @@ -Tiny Code Generator - Fabrice Bellard. - -1) Introduction - -TCG (Tiny Code Generator) began as a generic backend for a C -compiler. It was simplified to be used in QEMU. It also has its roots -in the QOP code generator written by Paul Brook. - -2) Definitions - -TCG receives RISC-like "TCG ops" and performs some optimizations on them, -including liveness analysis and trivial constant expression -evaluation. TCG ops are then implemented in the host CPU back end, -also known as the TCG "target". - -The TCG "target" is the architecture for which we generate the -code. It is of course not the same as the "target" of QEMU which is -the emulated architecture. As TCG started as a generic C backend used -for cross compiling, it is assumed that the TCG target is different -from the host, although it is never the case for QEMU. - -In this document, we use "guest" to specify what architecture we are -emulating; "target" always means the TCG target, the machine on which -we are running QEMU. - -A TCG "function" corresponds to a QEMU Translated Block (TB). - -A TCG "temporary" is a variable only live in a basic -block. Temporaries are allocated explicitly in each function. - -A TCG "local temporary" is a variable only live in a function. Local -temporaries are allocated explicitly in each function. - -A TCG "global" is a variable which is live in all the functions -(equivalent of a C global variable). They are defined before the -functions defined. A TCG global can be a memory location (e.g. a QEMU -CPU register), a fixed host register (e.g. the QEMU CPU state pointer) -or a memory location which is stored in a register outside QEMU TBs -(not implemented yet). - -A TCG "basic block" corresponds to a list of instructions terminated -by a branch instruction. - -An operation with "undefined behavior" may result in a crash. - -An operation with "unspecified behavior" shall not crash. However, -the result may be one of several possibilities so may be considered -an "undefined result". - -3) Intermediate representation - -3.1) Introduction - -TCG instructions operate on variables which are temporaries, local -temporaries or globals. TCG instructions and variables are strongly -typed. Two types are supported: 32 bit integers and 64 bit -integers. Pointers are defined as an alias to 32 bit or 64 bit -integers depending on the TCG target word size. - -Each instruction has a fixed number of output variable operands, input -variable operands and always constant operands. - -The notable exception is the call instruction which has a variable -number of outputs and inputs. - -In the textual form, output operands usually come first, followed by -input operands, followed by constant operands. The output type is -included in the instruction name. Constants are prefixed with a '$'. - -add_i32 t0, t1, t2 (t0 <- t1 + t2) - -3.2) Assumptions - -* Basic blocks - -- Basic blocks end after branches (e.g. brcond_i32 instruction), - goto_tb and exit_tb instructions. -- Basic blocks start after the end of a previous basic block, or at a - set_label instruction. - -After the end of a basic block, the content of temporaries is -destroyed, but local temporaries and globals are preserved. - -* Floating point types are not supported yet - -* Pointers: depending on the TCG target, pointer size is 32 bit or 64 - bit. The type TCG_TYPE_PTR is an alias to TCG_TYPE_I32 or - TCG_TYPE_I64. - -* Helpers: - -Using the tcg_gen_helper_x_y it is possible to call any function -taking i32, i64 or pointer types. By default, before calling a helper, -all globals are stored at their canonical location and it is assumed -that the function can modify them. By default, the helper is allowed to -modify the CPU state or raise an exception. - -This can be overridden using the following function modifiers: -- TCG_CALL_NO_READ_GLOBALS means that the helper does not read globals, - either directly or via an exception. They will not be saved to their - canonical locations before calling the helper. -- TCG_CALL_NO_WRITE_GLOBALS means that the helper does not modify any globals. - They will only be saved to their canonical location before calling helpers, - but they won't be reloaded afterwards. -- TCG_CALL_NO_SIDE_EFFECTS means that the call to the function is removed if - the return value is not used. - -Note that TCG_CALL_NO_READ_GLOBALS implies TCG_CALL_NO_WRITE_GLOBALS. - -On some TCG targets (e.g. x86), several calling conventions are -supported. - -* Branches: - -Use the instruction 'br' to jump to a label. - -3.3) Code Optimizations - -When generating instructions, you can count on at least the following -optimizations: - -- Single instructions are simplified, e.g. - - and_i32 t0, t0, $0xffffffff - - is suppressed. - -- A liveness analysis is done at the basic block level. The - information is used to suppress moves from a dead variable to - another one. It is also used to remove instructions which compute - dead results. The later is especially useful for condition code - optimization in QEMU. - - In the following example: - - add_i32 t0, t1, t2 - add_i32 t0, t0, $1 - mov_i32 t0, $1 - - only the last instruction is kept. - -3.4) Instruction Reference - -********* Function call - -* call ptr - -call function 'ptr' (pointer type) - - optional 32 bit or 64 bit return value - optional 32 bit or 64 bit parameters - -********* Jumps/Labels - -* set_label $label - -Define label 'label' at the current program point. - -* br $label - -Jump to label. - -* brcond_i32/i64 t0, t1, cond, label - -Conditional jump if t0 cond t1 is true. cond can be: - TCG_COND_EQ - TCG_COND_NE - TCG_COND_LT /* signed */ - TCG_COND_GE /* signed */ - TCG_COND_LE /* signed */ - TCG_COND_GT /* signed */ - TCG_COND_LTU /* unsigned */ - TCG_COND_GEU /* unsigned */ - TCG_COND_LEU /* unsigned */ - TCG_COND_GTU /* unsigned */ - -********* Arithmetic - -* add_i32/i64 t0, t1, t2 - -t0=t1+t2 - -* sub_i32/i64 t0, t1, t2 - -t0=t1-t2 - -* neg_i32/i64 t0, t1 - -t0=-t1 (two's complement) - -* mul_i32/i64 t0, t1, t2 - -t0=t1*t2 - -* div_i32/i64 t0, t1, t2 - -t0=t1/t2 (signed). Undefined behavior if division by zero or overflow. - -* divu_i32/i64 t0, t1, t2 - -t0=t1/t2 (unsigned). Undefined behavior if division by zero. - -* rem_i32/i64 t0, t1, t2 - -t0=t1%t2 (signed). Undefined behavior if division by zero or overflow. - -* remu_i32/i64 t0, t1, t2 - -t0=t1%t2 (unsigned). Undefined behavior if division by zero. - -********* Logical - -* and_i32/i64 t0, t1, t2 - -t0=t1&t2 - -* or_i32/i64 t0, t1, t2 - -t0=t1|t2 - -* xor_i32/i64 t0, t1, t2 - -t0=t1^t2 - -* not_i32/i64 t0, t1 - -t0=~t1 - -* andc_i32/i64 t0, t1, t2 - -t0=t1&~t2 - -* eqv_i32/i64 t0, t1, t2 - -t0=~(t1^t2), or equivalently, t0=t1^~t2 - -* nand_i32/i64 t0, t1, t2 - -t0=~(t1&t2) - -* nor_i32/i64 t0, t1, t2 - -t0=~(t1|t2) - -* orc_i32/i64 t0, t1, t2 - -t0=t1|~t2 - -* clz_i32/i64 t0, t1, t2 - -t0 = t1 ? clz(t1) : t2 - -* ctz_i32/i64 t0, t1, t2 - -t0 = t1 ? ctz(t1) : t2 - -* ctpop_i32/i64 t0, t1 - -t0 = number of bits set in t1 -With "ctpop" short for "count population", matching -the function name used in include/qemu/host-utils.h. - -********* Shifts/Rotates - -* shl_i32/i64 t0, t1, t2 - -t0=t1 << t2. Unspecified behavior if t2 < 0 or t2 >= 32 (resp 64) - -* shr_i32/i64 t0, t1, t2 - -t0=t1 >> t2 (unsigned). Unspecified behavior if t2 < 0 or t2 >= 32 (resp 64) - -* sar_i32/i64 t0, t1, t2 - -t0=t1 >> t2 (signed). Unspecified behavior if t2 < 0 or t2 >= 32 (resp 64) - -* rotl_i32/i64 t0, t1, t2 - -Rotation of t2 bits to the left. -Unspecified behavior if t2 < 0 or t2 >= 32 (resp 64) - -* rotr_i32/i64 t0, t1, t2 - -Rotation of t2 bits to the right. -Unspecified behavior if t2 < 0 or t2 >= 32 (resp 64) - -********* Misc - -* mov_i32/i64 t0, t1 - -t0 = t1 - -Move t1 to t0 (both operands must have the same type). - -* ext8s_i32/i64 t0, t1 -ext8u_i32/i64 t0, t1 -ext16s_i32/i64 t0, t1 -ext16u_i32/i64 t0, t1 -ext32s_i64 t0, t1 -ext32u_i64 t0, t1 - -8, 16 or 32 bit sign/zero extension (both operands must have the same type) - -* bswap16_i32/i64 t0, t1, flags - -16 bit byte swap on the low bits of a 32/64 bit input. -If flags & TCG_BSWAP_IZ, then t1 is known to be zero-extended from bit 15. -If flags & TCG_BSWAP_OZ, then t0 will be zero-extended from bit 15. -If flags & TCG_BSWAP_OS, then t0 will be sign-extended from bit 15. -If neither TCG_BSWAP_OZ nor TCG_BSWAP_OS are set, then the bits of -t0 above bit 15 may contain any value. - -* bswap32_i64 t0, t1, flags - -32 bit byte swap on a 64-bit value. The flags are the same as for bswap16, -except they apply from bit 31 instead of bit 15. - -* bswap32_i32 t0, t1, flags -* bswap64_i64 t0, t1, flags - -32/64 bit byte swap. The flags are ignored, but still present -for consistency with the other bswap opcodes. - -* discard_i32/i64 t0 - -Indicate that the value of t0 won't be used later. It is useful to -force dead code elimination. - -* deposit_i32/i64 dest, t1, t2, pos, len - -Deposit T2 as a bitfield into T1, placing the result in DEST. -The bitfield is described by POS/LEN, which are immediate values: - - LEN - the length of the bitfield - POS - the position of the first bit, counting from the LSB - -For example, "deposit_i32 dest, t1, t2, 8, 4" indicates a 4-bit field -at bit 8. This operation would be equivalent to - - dest = (t1 & ~0x0f00) | ((t2 << 8) & 0x0f00) - -* extract_i32/i64 dest, t1, pos, len -* sextract_i32/i64 dest, t1, pos, len - -Extract a bitfield from T1, placing the result in DEST. -The bitfield is described by POS/LEN, which are immediate values, -as above for deposit. For extract_*, the result will be extended -to the left with zeros; for sextract_*, the result will be extended -to the left with copies of the bitfield sign bit at pos + len - 1. - -For example, "sextract_i32 dest, t1, 8, 4" indicates a 4-bit field -at bit 8. This operation would be equivalent to - - dest = (t1 << 20) >> 28 - -(using an arithmetic right shift). - -* extract2_i32/i64 dest, t1, t2, pos - -For N = {32,64}, extract an N-bit quantity from the concatenation -of t2:t1, beginning at pos. The tcg_gen_extract2_{i32,i64} expander -accepts 0 <= pos <= N as inputs. The backend code generator will -not see either 0 or N as inputs for these opcodes. - -* extrl_i64_i32 t0, t1 - -For 64-bit hosts only, extract the low 32-bits of input T1 and place it -into 32-bit output T0. Depending on the host, this may be a simple move, -or may require additional canonicalization. - -* extrh_i64_i32 t0, t1 - -For 64-bit hosts only, extract the high 32-bits of input T1 and place it -into 32-bit output T0. Depending on the host, this may be a simple shift, -or may require additional canonicalization. - -********* Conditional moves - -* setcond_i32/i64 dest, t1, t2, cond - -dest = (t1 cond t2) - -Set DEST to 1 if (T1 cond T2) is true, otherwise set to 0. - -* movcond_i32/i64 dest, c1, c2, v1, v2, cond - -dest = (c1 cond c2 ? v1 : v2) - -Set DEST to V1 if (C1 cond C2) is true, otherwise set to V2. - -********* Type conversions - -* ext_i32_i64 t0, t1 -Convert t1 (32 bit) to t0 (64 bit) and does sign extension - -* extu_i32_i64 t0, t1 -Convert t1 (32 bit) to t0 (64 bit) and does zero extension - -* trunc_i64_i32 t0, t1 -Truncate t1 (64 bit) to t0 (32 bit) - -* concat_i32_i64 t0, t1, t2 -Construct t0 (64-bit) taking the low half from t1 (32 bit) and the high half -from t2 (32 bit). - -* concat32_i64 t0, t1, t2 -Construct t0 (64-bit) taking the low half from t1 (64 bit) and the high half -from t2 (64 bit). - -********* Load/Store - -* ld_i32/i64 t0, t1, offset -ld8s_i32/i64 t0, t1, offset -ld8u_i32/i64 t0, t1, offset -ld16s_i32/i64 t0, t1, offset -ld16u_i32/i64 t0, t1, offset -ld32s_i64 t0, t1, offset -ld32u_i64 t0, t1, offset - -t0 = read(t1 + offset) -Load 8, 16, 32 or 64 bits with or without sign extension from host memory. -offset must be a constant. - -* st_i32/i64 t0, t1, offset -st8_i32/i64 t0, t1, offset -st16_i32/i64 t0, t1, offset -st32_i64 t0, t1, offset - -write(t0, t1 + offset) -Write 8, 16, 32 or 64 bits to host memory. - -All this opcodes assume that the pointed host memory doesn't correspond -to a global. In the latter case the behaviour is unpredictable. - -********* Multiword arithmetic support - -* add2_i32/i64 t0_low, t0_high, t1_low, t1_high, t2_low, t2_high -* sub2_i32/i64 t0_low, t0_high, t1_low, t1_high, t2_low, t2_high - -Similar to add/sub, except that the double-word inputs T1 and T2 are -formed from two single-word arguments, and the double-word output T0 -is returned in two single-word outputs. - -* mulu2_i32/i64 t0_low, t0_high, t1, t2 - -Similar to mul, except two unsigned inputs T1 and T2 yielding the full -double-word product T0. The later is returned in two single-word outputs. - -* muls2_i32/i64 t0_low, t0_high, t1, t2 - -Similar to mulu2, except the two inputs T1 and T2 are signed. - -* mulsh_i32/i64 t0, t1, t2 -* muluh_i32/i64 t0, t1, t2 - -Provide the high part of a signed or unsigned multiply, respectively. -If mulu2/muls2 are not provided by the backend, the tcg-op generator -can obtain the same results can be obtained by emitting a pair of -opcodes, mul+muluh/mulsh. - -********* Memory Barrier support - -* mb <$arg> - -Generate a target memory barrier instruction to ensure memory ordering as being -enforced by a corresponding guest memory barrier instruction. The ordering -enforced by the backend may be stricter than the ordering required by the guest. -It cannot be weaker. This opcode takes a constant argument which is required to -generate the appropriate barrier instruction. The backend should take care to -emit the target barrier instruction only when necessary i.e., for SMP guests and -when MTTCG is enabled. - -The guest translators should generate this opcode for all guest instructions -which have ordering side effects. - -Please see docs/devel/atomics.rst for more information on memory barriers. - -********* 64-bit guest on 32-bit host support - -The following opcodes are internal to TCG. Thus they are to be implemented by -32-bit host code generators, but are not to be emitted by guest translators. -They are emitted as needed by inline functions within "tcg-op.h". - -* brcond2_i32 t0_low, t0_high, t1_low, t1_high, cond, label - -Similar to brcond, except that the 64-bit values T0 and T1 -are formed from two 32-bit arguments. - -* setcond2_i32 dest, t1_low, t1_high, t2_low, t2_high, cond - -Similar to setcond, except that the 64-bit values T1 and T2 are -formed from two 32-bit arguments. The result is a 32-bit value. - -********* QEMU specific operations - -* exit_tb t0 - -Exit the current TB and return the value t0 (word type). - -* goto_tb index - -Exit the current TB and jump to the TB index 'index' (constant) if the -current TB was linked to this TB. Otherwise execute the next -instructions. Only indices 0 and 1 are valid and tcg_gen_goto_tb may be issued -at most once with each slot index per TB. - -* lookup_and_goto_ptr tb_addr - -Look up a TB address ('tb_addr') and jump to it if valid. If not valid, -jump to the TCG epilogue to go back to the exec loop. - -This operation is optional. If the TCG backend does not implement the -goto_ptr opcode, emitting this op is equivalent to emitting exit_tb(0). - -* qemu_ld_i32/i64 t0, t1, flags, memidx -* qemu_st_i32/i64 t0, t1, flags, memidx -* qemu_st8_i32 t0, t1, flags, memidx - -Load data at the guest address t1 into t0, or store data in t0 at guest -address t1. The _i32/_i64 size applies to the size of the input/output -register t0 only. The address t1 is always sized according to the guest, -and the width of the memory operation is controlled by flags. - -Both t0 and t1 may be split into little-endian ordered pairs of registers -if dealing with 64-bit quantities on a 32-bit host. - -The memidx selects the qemu tlb index to use (e.g. user or kernel access). -The flags are the MemOp bits, selecting the sign, width, and endianness -of the memory access. - -For a 32-bit host, qemu_ld/st_i64 is guaranteed to only be used with a -64-bit memory access specified in flags. - -For i386, qemu_st8_i32 is exactly like qemu_st_i32, except the size of -the memory operation is known to be 8-bit. This allows the backend to -provide a different set of register constraints. - -********* Host vector operations - -All of the vector ops have two parameters, TCGOP_VECL & TCGOP_VECE. -The former specifies the length of the vector in log2 64-bit units; the -later specifies the length of the element (if applicable) in log2 8-bit units. -E.g. VECL=1 -> 64 << 1 -> v128, and VECE=2 -> 1 << 2 -> i32. - -* mov_vec v0, v1 -* ld_vec v0, t1 -* st_vec v0, t1 - - Move, load and store. - -* dup_vec v0, r1 - - Duplicate the low N bits of R1 into VECL/VECE copies across V0. - -* dupi_vec v0, c - - Similarly, for a constant. - Smaller values will be replicated to host register size by the expanders. - -* dup2_vec v0, r1, r2 - - Duplicate r2:r1 into VECL/64 copies across V0. This opcode is - only present for 32-bit hosts. - -* add_vec v0, v1, v2 - - v0 = v1 + v2, in elements across the vector. - -* sub_vec v0, v1, v2 - - Similarly, v0 = v1 - v2. - -* mul_vec v0, v1, v2 - - Similarly, v0 = v1 * v2. - -* neg_vec v0, v1 - - Similarly, v0 = -v1. - -* abs_vec v0, v1 - - Similarly, v0 = v1 < 0 ? -v1 : v1, in elements across the vector. - -* smin_vec: -* umin_vec: - - Similarly, v0 = MIN(v1, v2), for signed and unsigned element types. - -* smax_vec: -* umax_vec: - - Similarly, v0 = MAX(v1, v2), for signed and unsigned element types. - -* ssadd_vec: -* sssub_vec: -* usadd_vec: -* ussub_vec: - - Signed and unsigned saturating addition and subtraction. If the true - result is not representable within the element type, the element is - set to the minimum or maximum value for the type. - -* and_vec v0, v1, v2 -* or_vec v0, v1, v2 -* xor_vec v0, v1, v2 -* andc_vec v0, v1, v2 -* orc_vec v0, v1, v2 -* not_vec v0, v1 - - Similarly, logical operations with and without complement. - Note that VECE is unused. - -* shli_vec v0, v1, i2 -* shls_vec v0, v1, s2 - - Shift all elements from v1 by a scalar i2/s2. I.e. - - for (i = 0; i < VECL/VECE; ++i) { - v0[i] = v1[i] << s2; - } - -* shri_vec v0, v1, i2 -* sari_vec v0, v1, i2 -* rotli_vec v0, v1, i2 -* shrs_vec v0, v1, s2 -* sars_vec v0, v1, s2 - - Similarly for logical and arithmetic right shift, and left rotate. - -* shlv_vec v0, v1, v2 - - Shift elements from v1 by elements from v2. I.e. - - for (i = 0; i < VECL/VECE; ++i) { - v0[i] = v1[i] << v2[i]; - } - -* shrv_vec v0, v1, v2 -* sarv_vec v0, v1, v2 -* rotlv_vec v0, v1, v2 -* rotrv_vec v0, v1, v2 - - Similarly for logical and arithmetic right shift, and rotates. - -* cmp_vec v0, v1, v2, cond - - Compare vectors by element, storing -1 for true and 0 for false. - -* bitsel_vec v0, v1, v2, v3 - - Bitwise select, v0 = (v2 & v1) | (v3 & ~v1), across the entire vector. - -* cmpsel_vec v0, c1, c2, v3, v4, cond - - Select elements based on comparison results: - for (i = 0; i < n; ++i) { - v0[i] = (c1[i] cond c2[i]) ? v3[i] : v4[i]. - } - -********* - -Note 1: Some shortcuts are defined when the last operand is known to be -a constant (e.g. addi for add, movi for mov). - -Note 2: When using TCG, the opcodes must never be generated directly -as some of them may not be available as "real" opcodes. Always use the -function tcg_gen_xxx(args). - -4) Backend - -tcg-target.h contains the target specific definitions. tcg-target.c.inc -contains the target specific code; it is #included by tcg/tcg.c, rather -than being a standalone C file. - -4.1) Assumptions - -The target word size (TCG_TARGET_REG_BITS) is expected to be 32 bit or -64 bit. It is expected that the pointer has the same size as the word. - -On a 32 bit target, all 64 bit operations are converted to 32 bits. A -few specific operations must be implemented to allow it (see add2_i32, -sub2_i32, brcond2_i32). - -On a 64 bit target, the values are transferred between 32 and 64-bit -registers using the following ops: -- trunc_shr_i64_i32 -- ext_i32_i64 -- extu_i32_i64 - -They ensure that the values are correctly truncated or extended when -moved from a 32-bit to a 64-bit register or vice-versa. Note that the -trunc_shr_i64_i32 is an optional op. It is not necessary to implement -it if all the following conditions are met: -- 64-bit registers can hold 32-bit values -- 32-bit values in a 64-bit register do not need to stay zero or - sign extended -- all 32-bit TCG ops ignore the high part of 64-bit registers - -Floating point operations are not supported in this version. A -previous incarnation of the code generator had full support of them, -but it is better to concentrate on integer operations first. - -4.2) Constraints - -GCC like constraints are used to define the constraints of every -instruction. Memory constraints are not supported in this -version. Aliases are specified in the input operands as for GCC. - -The same register may be used for both an input and an output, even when -they are not explicitly aliased. If an op expands to multiple target -instructions then care must be taken to avoid clobbering input values. -GCC style "early clobber" outputs are supported, with '&'. - -A target can define specific register or constant constraints. If an -operation uses a constant input constraint which does not allow all -constants, it must also accept registers in order to have a fallback. -The constraint 'i' is defined generically to accept any constant. -The constraint 'r' is not defined generically, but is consistently -used by each backend to indicate all registers. - -The movi_i32 and movi_i64 operations must accept any constants. - -The mov_i32 and mov_i64 operations must accept any registers of the -same type. - -The ld/st/sti instructions must accept signed 32 bit constant offsets. -This can be implemented by reserving a specific register in which to -compute the address if the offset is too big. - -The ld/st instructions must accept any destination (ld) or source (st) -register. - -The sti instruction may fail if it cannot store the given constant. - -4.3) Function call assumptions - -- The only supported types for parameters and return value are: 32 and - 64 bit integers and pointer. -- The stack grows downwards. -- The first N parameters are passed in registers. -- The next parameters are passed on the stack by storing them as words. -- Some registers are clobbered during the call. -- The function can return 0 or 1 value in registers. On a 32 bit - target, functions must be able to return 2 values in registers for - 64 bit return type. - -5) Recommended coding rules for best performance - -- Use globals to represent the parts of the QEMU CPU state which are - often modified, e.g. the integer registers and the condition - codes. TCG will be able to use host registers to store them. - -- Avoid globals stored in fixed registers. They must be used only to - store the pointer to the CPU state and possibly to store a pointer - to a register window. - -- Use temporaries. Use local temporaries only when really needed, - e.g. when you need to use a value after a jump. Local temporaries - introduce a performance hit in the current TCG implementation: their - content is saved to memory at end of each basic block. - -- Free temporaries and local temporaries when they are no longer used - (tcg_temp_free). Since tcg_const_x() also creates a temporary, you - should free it after it is used. Freeing temporaries does not yield - a better generated code, but it reduces the memory usage of TCG and - the speed of the translation. - -- Don't hesitate to use helpers for complicated or seldom used guest - instructions. There is little performance advantage in using TCG to - implement guest instructions taking more than about twenty TCG - instructions. Note that this rule of thumb is more applicable to - helpers doing complex logic or arithmetic, where the C compiler has - scope to do a good job of optimisation; it is less relevant where - the instruction is mostly doing loads and stores, and in those cases - inline TCG may still be faster for longer sequences. - -- The hard limit on the number of TCG instructions you can generate - per guest instruction is set by MAX_OP_PER_INSTR in exec-all.h -- - you cannot exceed this without risking a buffer overrun. - -- Use the 'discard' instruction if you know that TCG won't be able to - prove that a given global is "dead" at a given program point. The - x86 guest uses it to improve the condition codes optimisation. diff --git a/tcg/aarch64/tcg-target-con-set.h b/tcg/aarch64/tcg-target-con-set.h index d6c6866878..44fcc1206e 100644 --- a/tcg/aarch64/tcg-target-con-set.h +++ b/tcg/aarch64/tcg-target-con-set.h @@ -10,11 +10,10 @@ * tcg-target-con-str.h; the constraint combination is inclusive or. */ C_O0_I1(r) -C_O0_I2(lZ, l) -C_O0_I2(r, rA) +C_O0_I2(r, rC) C_O0_I2(rZ, r) C_O0_I2(w, r) -C_O1_I1(r, l) +C_O0_I3(rZ, rZ, r) C_O1_I1(r, r) C_O1_I1(w, r) C_O1_I1(w, w) @@ -23,6 +22,7 @@ C_O1_I2(r, 0, rZ) C_O1_I2(r, r, r) C_O1_I2(r, r, rA) C_O1_I2(r, r, rAL) +C_O1_I2(r, r, rC) C_O1_I2(r, r, ri) C_O1_I2(r, r, rL) C_O1_I2(r, rZ, rZ) @@ -32,5 +32,6 @@ C_O1_I2(w, w, wN) C_O1_I2(w, w, wO) C_O1_I2(w, w, wZ) C_O1_I3(w, w, w, w) -C_O1_I4(r, r, rA, rZ, rZ) +C_O1_I4(r, r, rC, rZ, rZ) +C_O2_I1(r, r, r) C_O2_I4(r, r, rZ, rZ, rA, rMZ) diff --git a/tcg/aarch64/tcg-target-con-str.h b/tcg/aarch64/tcg-target-con-str.h index 00adb64594..48e1722c68 100644 --- a/tcg/aarch64/tcg-target-con-str.h +++ b/tcg/aarch64/tcg-target-con-str.h @@ -9,7 +9,6 @@ * REGS(letter, register_mask) */ REGS('r', ALL_GENERAL_REGS) -REGS('l', ALL_QLDST_REGS) REGS('w', ALL_VECTOR_REGS) /* @@ -17,6 +16,7 @@ REGS('w', ALL_VECTOR_REGS) * CONST(letter, TCG_CT_CONST_* bit set) */ CONST('A', TCG_CT_CONST_AIMM) +CONST('C', TCG_CT_CONST_CMP) CONST('L', TCG_CT_CONST_LIMM) CONST('M', TCG_CT_CONST_MONE) CONST('O', TCG_CT_CONST_ORRI) diff --git a/tcg/aarch64/tcg-target-reg-bits.h b/tcg/aarch64/tcg-target-reg-bits.h new file mode 100644 index 0000000000..3b57a1aafb --- /dev/null +++ b/tcg/aarch64/tcg-target-reg-bits.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Define target-specific register size + * Copyright (c) 2023 Linaro + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +#define TCG_TARGET_REG_BITS 64 + +#endif diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index d997f7922a..56fc9cb9e0 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -40,11 +40,12 @@ static const int tcg_target_reg_alloc_order[] = { TCG_REG_X8, TCG_REG_X9, TCG_REG_X10, TCG_REG_X11, TCG_REG_X12, TCG_REG_X13, TCG_REG_X14, TCG_REG_X15, - TCG_REG_X16, TCG_REG_X17, TCG_REG_X0, TCG_REG_X1, TCG_REG_X2, TCG_REG_X3, TCG_REG_X4, TCG_REG_X5, TCG_REG_X6, TCG_REG_X7, + /* X16 reserved as temporary */ + /* X17 reserved as temporary */ /* X18 reserved by system */ /* X19 reserved for AREG0 */ /* X29 reserved as fp */ @@ -63,21 +64,20 @@ static const int tcg_target_call_iarg_regs[8] = { TCG_REG_X0, TCG_REG_X1, TCG_REG_X2, TCG_REG_X3, TCG_REG_X4, TCG_REG_X5, TCG_REG_X6, TCG_REG_X7 }; -static const int tcg_target_call_oarg_regs[1] = { - TCG_REG_X0 -}; -#define TCG_REG_TMP TCG_REG_X30 -#define TCG_VEC_TMP TCG_REG_V31 +static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) +{ + tcg_debug_assert(kind == TCG_CALL_RET_NORMAL); + tcg_debug_assert(slot >= 0 && slot <= 1); + return TCG_REG_X0 + slot; +} + +#define TCG_REG_TMP0 TCG_REG_X16 +#define TCG_REG_TMP1 TCG_REG_X17 +#define TCG_REG_TMP2 TCG_REG_X30 +#define TCG_VEC_TMP0 TCG_REG_V31 -#ifndef CONFIG_SOFTMMU -/* Note that XZR cannot be encoded in the address base register slot, - as that actaully encodes SP. So if we need to zero-extend the guest - address, via the address index register slot, we need to load even - a zero guest base into a register. */ -#define USE_GUEST_BASE (guest_base != 0 || TARGET_LONG_BITS == 32) #define TCG_REG_GUEST_BASE TCG_REG_X28 -#endif static bool reloc_pc26(tcg_insn_unit *src_rw, const tcg_insn_unit *target) { @@ -105,6 +105,18 @@ static bool reloc_pc19(tcg_insn_unit *src_rw, const tcg_insn_unit *target) return false; } +static bool reloc_pc14(tcg_insn_unit *src_rw, const tcg_insn_unit *target) +{ + const tcg_insn_unit *src_rx = tcg_splitwx_to_rx(src_rw); + ptrdiff_t offset = target - src_rx; + + if (offset == sextract64(offset, 0, 14)) { + *src_rw = deposit32(*src_rw, 5, 14, offset); + return true; + } + return false; +} + static bool patch_reloc(tcg_insn_unit *code_ptr, int type, intptr_t value, intptr_t addend) { @@ -115,6 +127,8 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, return reloc_pc26(code_ptr, (const tcg_insn_unit *)value); case R_AARCH64_CONDBR19: return reloc_pc19(code_ptr, (const tcg_insn_unit *)value); + case R_AARCH64_TSTBR14: + return reloc_pc14(code_ptr, (const tcg_insn_unit *)value); default: g_assert_not_reached(); } @@ -126,18 +140,11 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, #define TCG_CT_CONST_MONE 0x800 #define TCG_CT_CONST_ORRI 0x1000 #define TCG_CT_CONST_ANDI 0x2000 +#define TCG_CT_CONST_CMP 0x4000 #define ALL_GENERAL_REGS 0xffffffffu #define ALL_VECTOR_REGS 0xffffffff00000000ull -#ifdef CONFIG_SOFTMMU -#define ALL_QLDST_REGS \ - (ALL_GENERAL_REGS & ~((1 << TCG_REG_X0) | (1 << TCG_REG_X1) | \ - (1 << TCG_REG_X2) | (1 << TCG_REG_X3))) -#else -#define ALL_QLDST_REGS ALL_GENERAL_REGS -#endif - /* Match a constant valid for addition (12-bit, optionally shifted). */ static inline bool is_aimm(uint64_t val) { @@ -278,7 +285,8 @@ static bool is_shimm1632(uint32_t v32, int *cmode, int *imm8) } } -static bool tcg_target_const_match(int64_t val, TCGType type, int ct) +static bool tcg_target_const_match(int64_t val, int ct, + TCGType type, TCGCond cond, int vece) { if (ct & TCG_CT_CONST) { return 1; @@ -286,6 +294,15 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) if (type == TCG_TYPE_I32) { val = (int32_t)val; } + + if (ct & TCG_CT_CONST_CMP) { + if (is_tst_cond(cond)) { + ct |= TCG_CT_CONST_LIMM; + } else { + ct |= TCG_CT_CONST_AIMM; + } + } + if ((ct & TCG_CT_CONST_AIMM) && (is_aimm(val) || is_aimm(-val))) { return 1; } @@ -352,6 +369,9 @@ static const enum aarch64_cond_code tcg_cond_to_aarch64[] = { [TCG_COND_GTU] = COND_HI, [TCG_COND_GEU] = COND_HS, [TCG_COND_LEU] = COND_LS, + /* bit test */ + [TCG_COND_TSTEQ] = COND_EQ, + [TCG_COND_TSTNE] = COND_NE, }; typedef enum { @@ -374,6 +394,10 @@ typedef enum { /* Conditional branch (immediate). */ I3202_B_C = 0x54000000, + /* Test and branch (immediate). */ + I3205_TBZ = 0x36000000, + I3205_TBNZ = 0x37000000, + /* Unconditional branch (immediate). */ I3206_B = 0x14000000, I3206_BL = 0x94000000, @@ -391,6 +415,10 @@ typedef enum { I3305_LDR_v64 = 0x5c000000, I3305_LDR_v128 = 0x9c000000, + /* Load/store exclusive. */ + I3306_LDXP = 0xc8600000, + I3306_STXP = 0xc8200000, + /* Load/store register. Described here as 3.3.12, but the helper that emits them can transform to 3.3.10 or 3.3.13. */ I3312_STRB = 0x38000000 | LDST_ST << 22 | MO_8 << 30, @@ -455,6 +483,9 @@ typedef enum { I3406_ADR = 0x10000000, I3406_ADRP = 0x90000000, + /* Add/subtract extended register instructions. */ + I3501_ADD = 0x0b200000, + /* Add/subtract shifted register instructions (without a shift). */ I3502_ADD = 0x0b000000, I3502_ADDS = 0x2b000000, @@ -601,6 +632,10 @@ typedef enum { DMB_ISH = 0xd50338bf, DMB_LD = 0x00000100, DMB_ST = 0x00000200, + + BTI_C = 0xd503245f, + BTI_J = 0xd503249f, + BTI_JC = 0xd50324df, } AArch64Insn; static inline uint32_t tcg_in32(TCGContext *s) @@ -625,6 +660,12 @@ static void tcg_out_insn_3305(TCGContext *s, AArch64Insn insn, tcg_out32(s, insn | (imm19 & 0x7ffff) << 5 | rt); } +static void tcg_out_insn_3306(TCGContext *s, AArch64Insn insn, TCGReg rs, + TCGReg rt, TCGReg rt2, TCGReg rn) +{ + tcg_out32(s, insn | rs << 16 | rt2 << 10 | rn << 5 | rt); +} + static void tcg_out_insn_3201(TCGContext *s, AArch64Insn insn, TCGType ext, TCGReg rt, int imm19) { @@ -637,6 +678,14 @@ static void tcg_out_insn_3202(TCGContext *s, AArch64Insn insn, tcg_out32(s, insn | tcg_cond_to_aarch64[c] | (imm19 & 0x7ffff) << 5); } +static void tcg_out_insn_3205(TCGContext *s, AArch64Insn insn, + TCGReg rt, int imm6, int imm14) +{ + insn |= (imm6 & 0x20) << (31 - 5); + insn |= (imm6 & 0x1f) << 19; + tcg_out32(s, insn | (imm14 & 0x3fff) << 5 | rt); +} + static void tcg_out_insn_3206(TCGContext *s, AArch64Insn insn, int imm26) { tcg_out32(s, insn | (imm26 & 0x03ffffff)); @@ -707,6 +756,14 @@ static void tcg_out_insn_3406(TCGContext *s, AArch64Insn insn, tcg_out32(s, insn | (disp & 3) << 29 | (disp & 0x1ffffc) << (5 - 2) | rd); } +static inline void tcg_out_insn_3501(TCGContext *s, AArch64Insn insn, + TCGType sf, TCGReg rd, TCGReg rn, + TCGReg rm, int opt, int imm3) +{ + tcg_out32(s, insn | sf << 31 | rm << 16 | opt << 13 | + imm3 << 10 | rn << 5 | rd); +} + /* This function is for both 3.5.2 (Add/Subtract shifted register), for the rare occasion when we actually want to supply a shift amount. */ static inline void tcg_out_insn_3502S(TCGContext *s, AArch64Insn insn, @@ -828,6 +885,17 @@ static void tcg_out_insn_3313(TCGContext *s, AArch64Insn insn, | rn << 5 | (rd & 0x1f)); } +static void tcg_out_bti(TCGContext *s, AArch64Insn insn) +{ + /* + * While BTI insns are nops on hosts without FEAT_BTI, + * there is no point in emitting them in that case either. + */ + if (cpuinfo & CPUINFO_BTI) { + tcg_out32(s, insn); + } +} + /* Register to register move using ORR (shifted register with no shift). */ static void tcg_out_movr(TCGContext *s, TCGType ext, TCGReg rd, TCGReg rm) { @@ -985,7 +1053,7 @@ static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, static bool tcg_out_dupm_vec(TCGContext *s, TCGType type, unsigned vece, TCGReg r, TCGReg base, intptr_t offset) { - TCGReg temp = TCG_REG_TMP; + TCGReg temp = TCG_REG_TMP0; if (offset < -0xffffff || offset > 0xffffff) { tcg_out_movi(s, TCG_TYPE_PTR, temp, offset); @@ -1102,6 +1170,18 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, tcg_out_insn(s, 3305, LDR, 0, rd); } +static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2) +{ + return false; +} + +static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs, + tcg_target_long imm) +{ + /* This function is only used for passing structs by reference. */ + g_assert_not_reached(); +} + /* Define something more legible for general use. */ #define tcg_out_ldst_r tcg_out_insn_3310 @@ -1125,8 +1205,8 @@ static void tcg_out_ldst(TCGContext *s, AArch64Insn insn, TCGReg rd, } /* Worst-case scenario, move offset to temp register, use reg offset. */ - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP, offset); - tcg_out_ldst_r(s, insn, rd, rn, TCG_TYPE_I64, TCG_REG_TMP); + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP0, offset); + tcg_out_ldst_r(s, insn, rd, rn, TCG_TYPE_I64, TCG_REG_TMP0); } static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) @@ -1301,19 +1381,25 @@ static inline void tcg_out_dep(TCGContext *s, TCGType ext, TCGReg rd, tcg_out_bfm(s, ext, rd, rn, a, b); } -static void tcg_out_cmp(TCGContext *s, TCGType ext, TCGReg a, +static void tcg_out_cmp(TCGContext *s, TCGType ext, TCGCond cond, TCGReg a, tcg_target_long b, bool const_b) { - if (const_b) { - /* Using CMP or CMN aliases. */ - if (b >= 0) { - tcg_out_insn(s, 3401, SUBSI, ext, TCG_REG_XZR, a, b); + if (is_tst_cond(cond)) { + if (!const_b) { + tcg_out_insn(s, 3510, ANDS, ext, TCG_REG_XZR, a, b); } else { - tcg_out_insn(s, 3401, ADDSI, ext, TCG_REG_XZR, a, -b); + tcg_out_logicali(s, I3404_ANDSI, ext, TCG_REG_XZR, a, b); } } else { - /* Using CMP alias SUBS wzr, Wn, Wm */ - tcg_out_insn(s, 3502, SUBS, ext, TCG_REG_XZR, a, b); + if (!const_b) { + tcg_out_insn(s, 3502, SUBS, ext, TCG_REG_XZR, a, b); + } else if (b >= 0) { + tcg_debug_assert(is_aimm(b)); + tcg_out_insn(s, 3401, SUBSI, ext, TCG_REG_XZR, a, b); + } else { + tcg_debug_assert(is_aimm(-b)); + tcg_out_insn(s, 3401, ADDSI, ext, TCG_REG_XZR, a, -b); + } } } @@ -1324,58 +1410,21 @@ static void tcg_out_goto(TCGContext *s, const tcg_insn_unit *target) tcg_out_insn(s, 3206, B, offset); } -static void tcg_out_goto_long(TCGContext *s, const tcg_insn_unit *target) -{ - ptrdiff_t offset = tcg_pcrel_diff(s, target) >> 2; - if (offset == sextract64(offset, 0, 26)) { - tcg_out_insn(s, 3206, B, offset); - } else { - /* Choose X9 as a call-clobbered non-LR temporary. */ - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_X9, (intptr_t)target); - tcg_out_insn(s, 3207, BR, TCG_REG_X9); - } -} - -static inline void tcg_out_callr(TCGContext *s, TCGReg reg) -{ - tcg_out_insn(s, 3207, BLR, reg); -} - -static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target) +static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *target) { ptrdiff_t offset = tcg_pcrel_diff(s, target) >> 2; if (offset == sextract64(offset, 0, 26)) { tcg_out_insn(s, 3206, BL, offset); } else { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP, (intptr_t)target); - tcg_out_callr(s, TCG_REG_TMP); + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP0, (intptr_t)target); + tcg_out_insn(s, 3207, BLR, TCG_REG_TMP0); } } -void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx, - uintptr_t jmp_rw, uintptr_t addr) +static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target, + const TCGHelperInfo *info) { - tcg_insn_unit i1, i2; - TCGType rt = TCG_TYPE_I64; - TCGReg rd = TCG_REG_TMP; - uint64_t pair; - - ptrdiff_t offset = addr - jmp_rx; - - if (offset == sextract64(offset, 0, 26)) { - i1 = I3206_B | ((offset >> 2) & 0x3ffffff); - i2 = NOP; - } else { - offset = (addr >> 12) - (jmp_rx >> 12); - - /* patch ADRP */ - i1 = I3406_ADRP | (offset & 3) << 29 | (offset & 0x1ffffc) << (5 - 2) | rd; - /* patch ADDI */ - i2 = I3401_ADDI | rt << 31 | (addr & 0xfff) << 10 | rd << 5 | rd; - } - pair = (uint64_t)i2 << 32 | i1; - qatomic_set((uint64_t *)jmp_rw, pair); - flush_idcache_range(jmp_rx, jmp_rw, 8); + tcg_out_call_int(s, target); } static inline void tcg_out_goto_label(TCGContext *s, TCGLabel *l) @@ -1391,30 +1440,76 @@ static inline void tcg_out_goto_label(TCGContext *s, TCGLabel *l) static void tcg_out_brcond(TCGContext *s, TCGType ext, TCGCond c, TCGArg a, TCGArg b, bool b_const, TCGLabel *l) { - intptr_t offset; - bool need_cmp; + int tbit = -1; + bool need_cmp = true; - if (b_const && b == 0 && (c == TCG_COND_EQ || c == TCG_COND_NE)) { - need_cmp = false; - } else { - need_cmp = true; - tcg_out_cmp(s, ext, a, b, b_const); - } - - if (!l->has_value) { - tcg_out_reloc(s, s->code_ptr, R_AARCH64_CONDBR19, l, 0); - offset = tcg_in32(s) >> 5; - } else { - offset = tcg_pcrel_diff(s, l->u.value_ptr) >> 2; - tcg_debug_assert(offset == sextract64(offset, 0, 19)); + switch (c) { + case TCG_COND_EQ: + case TCG_COND_NE: + /* cmp xN,0; b.ne L -> cbnz xN,L */ + if (b_const && b == 0) { + need_cmp = false; + } + break; + case TCG_COND_LT: + case TCG_COND_GE: + /* cmp xN,0; b.mi L -> tbnz xN,63,L */ + if (b_const && b == 0) { + c = (c == TCG_COND_LT ? TCG_COND_TSTNE : TCG_COND_TSTEQ); + tbit = ext ? 63 : 31; + need_cmp = false; + } + break; + case TCG_COND_TSTEQ: + case TCG_COND_TSTNE: + /* tst xN,0xffffffff; b.ne L -> cbnz wN,L */ + if (b_const && b == UINT32_MAX) { + c = tcg_tst_eqne_cond(c); + ext = TCG_TYPE_I32; + need_cmp = false; + break; + } + /* tst xN,1< tbnz xN,B,L */ + if (b_const && is_power_of_2(b)) { + tbit = ctz64(b); + need_cmp = false; + } + break; + default: + break; } if (need_cmp) { - tcg_out_insn(s, 3202, B_C, c, offset); - } else if (c == TCG_COND_EQ) { - tcg_out_insn(s, 3201, CBZ, ext, a, offset); + tcg_out_cmp(s, ext, c, a, b, b_const); + tcg_out_reloc(s, s->code_ptr, R_AARCH64_CONDBR19, l, 0); + tcg_out_insn(s, 3202, B_C, c, 0); + return; + } + + if (tbit >= 0) { + tcg_out_reloc(s, s->code_ptr, R_AARCH64_TSTBR14, l, 0); + switch (c) { + case TCG_COND_TSTEQ: + tcg_out_insn(s, 3205, TBZ, a, tbit, 0); + break; + case TCG_COND_TSTNE: + tcg_out_insn(s, 3205, TBNZ, a, tbit, 0); + break; + default: + g_assert_not_reached(); + } } else { - tcg_out_insn(s, 3201, CBNZ, ext, a, offset); + tcg_out_reloc(s, s->code_ptr, R_AARCH64_CONDBR19, l, 0); + switch (c) { + case TCG_COND_EQ: + tcg_out_insn(s, 3201, CBZ, ext, a, 0); + break; + case TCG_COND_NE: + tcg_out_insn(s, 3201, CBNZ, ext, a, 0); + break; + default: + g_assert_not_reached(); + } } } @@ -1433,6 +1528,26 @@ static inline void tcg_out_sxt(TCGContext *s, TCGType ext, MemOp s_bits, tcg_out_sbfm(s, ext, rd, rn, 0, bits); } +static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rn) +{ + tcg_out_sxt(s, type, MO_8, rd, rn); +} + +static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rn) +{ + tcg_out_sxt(s, type, MO_16, rd, rn); +} + +static void tcg_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rn) +{ + tcg_out_sxt(s, TCG_TYPE_I64, MO_32, rd, rn); +} + +static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rn) +{ + tcg_out_ext32s(s, rd, rn); +} + static inline void tcg_out_uxt(TCGContext *s, MemOp s_bits, TCGReg rd, TCGReg rn) { @@ -1441,6 +1556,31 @@ static inline void tcg_out_uxt(TCGContext *s, MemOp s_bits, tcg_out_ubfm(s, 0, rd, rn, 0, bits); } +static void tcg_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rn) +{ + tcg_out_uxt(s, MO_8, rd, rn); +} + +static void tcg_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rn) +{ + tcg_out_uxt(s, MO_16, rd, rn); +} + +static void tcg_out_ext32u(TCGContext *s, TCGReg rd, TCGReg rn) +{ + tcg_out_movr(s, TCG_TYPE_I32, rd, rn); +} + +static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg rd, TCGReg rn) +{ + tcg_out_ext32u(s, rd, rn); +} + +static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rn) +{ + tcg_out_mov(s, TCG_TYPE_I32, rd, rn); +} + static void tcg_out_addsubi(TCGContext *s, int ext, TCGReg rd, TCGReg rn, int64_t aimm) { @@ -1460,7 +1600,7 @@ static void tcg_out_addsub2(TCGContext *s, TCGType ext, TCGReg rl, AArch64Insn insn; if (rl == ah || (!const_bh && rl == bh)) { - rl = TCG_REG_TMP; + rl = TCG_REG_TMP0; } if (const_bl) { @@ -1477,7 +1617,7 @@ static void tcg_out_addsub2(TCGContext *s, TCGType ext, TCGReg rl, possibility of adding 0+const in the low part, and the immediate add instructions encode XSP not XZR. Don't try anything more elaborate here than loading another zero. */ - al = TCG_REG_TMP; + al = TCG_REG_TMP0; tcg_out_movi(s, ext, al, 0); } tcg_out_insn_3401(s, insn, ext, rl, al, bl); @@ -1518,7 +1658,7 @@ static void tcg_out_cltz(TCGContext *s, TCGType ext, TCGReg d, { TCGReg a1 = a0; if (is_ctz) { - a1 = TCG_REG_TMP; + a1 = TCG_REG_TMP0; tcg_out_insn(s, 3507, RBIT, ext, a1, a0); } if (const_b && b == (ext ? 64 : 32)) { @@ -1526,8 +1666,8 @@ static void tcg_out_cltz(TCGContext *s, TCGType ext, TCGReg d, } else { AArch64Insn sel = I3506_CSEL; - tcg_out_cmp(s, ext, a0, 0, 1); - tcg_out_insn(s, 3507, CLZ, ext, TCG_REG_TMP, a1); + tcg_out_cmp(s, ext, TCG_COND_NE, a0, 0, 1); + tcg_out_insn(s, 3507, CLZ, ext, TCG_REG_TMP0, a1); if (const_b) { if (b == -1) { @@ -1540,352 +1680,430 @@ static void tcg_out_cltz(TCGContext *s, TCGType ext, TCGReg d, b = d; } } - tcg_out_insn_3506(s, sel, ext, d, TCG_REG_TMP, b, TCG_COND_NE); + tcg_out_insn_3506(s, sel, ext, d, TCG_REG_TMP0, b, TCG_COND_NE); } } -static void tcg_out_adr(TCGContext *s, TCGReg rd, const void *target) +typedef struct { + TCGReg base; + TCGReg index; + TCGType index_ext; + TCGAtomAlign aa; +} HostAddress; + +bool tcg_target_has_memory_bswap(MemOp memop) { - ptrdiff_t offset = tcg_pcrel_diff(s, target); - tcg_debug_assert(offset == sextract64(offset, 0, 21)); - tcg_out_insn(s, 3406, ADR, rd, offset); + return false; } -#ifdef CONFIG_SOFTMMU -/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, - * MemOpIdx oi, uintptr_t ra) - */ -static void * const qemu_ld_helpers[MO_SIZE + 1] = { - [MO_8] = helper_ret_ldub_mmu, -#if HOST_BIG_ENDIAN - [MO_16] = helper_be_lduw_mmu, - [MO_32] = helper_be_ldul_mmu, - [MO_64] = helper_be_ldq_mmu, -#else - [MO_16] = helper_le_lduw_mmu, - [MO_32] = helper_le_ldul_mmu, - [MO_64] = helper_le_ldq_mmu, -#endif -}; - -/* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, - * uintxx_t val, MemOpIdx oi, - * uintptr_t ra) - */ -static void * const qemu_st_helpers[MO_SIZE + 1] = { - [MO_8] = helper_ret_stb_mmu, -#if HOST_BIG_ENDIAN - [MO_16] = helper_be_stw_mmu, - [MO_32] = helper_be_stl_mmu, - [MO_64] = helper_be_stq_mmu, -#else - [MO_16] = helper_le_stw_mmu, - [MO_32] = helper_le_stl_mmu, - [MO_64] = helper_le_stq_mmu, -#endif +static const TCGLdstHelperParam ldst_helper_param = { + .ntmp = 1, .tmp = { TCG_REG_TMP0 } }; static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { - MemOpIdx oi = lb->oi; - MemOp opc = get_memop(oi); - MemOp size = opc & MO_SIZE; + MemOp opc = get_memop(lb->oi); if (!reloc_pc19(lb->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { return false; } - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_X0, TCG_AREG0); - tcg_out_mov(s, TARGET_LONG_BITS == 64, TCG_REG_X1, lb->addrlo_reg); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_X2, oi); - tcg_out_adr(s, TCG_REG_X3, lb->raddr); - tcg_out_call(s, qemu_ld_helpers[opc & MO_SIZE]); - if (opc & MO_SIGN) { - tcg_out_sxt(s, lb->type, size, lb->datalo_reg, TCG_REG_X0); - } else { - tcg_out_mov(s, size == MO_64, lb->datalo_reg, TCG_REG_X0); - } - + tcg_out_ld_helper_args(s, lb, &ldst_helper_param); + tcg_out_call_int(s, qemu_ld_helpers[opc & MO_SIZE]); + tcg_out_ld_helper_ret(s, lb, false, &ldst_helper_param); tcg_out_goto(s, lb->raddr); return true; } static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { - MemOpIdx oi = lb->oi; - MemOp opc = get_memop(oi); - MemOp size = opc & MO_SIZE; + MemOp opc = get_memop(lb->oi); if (!reloc_pc19(lb->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { return false; } - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_X0, TCG_AREG0); - tcg_out_mov(s, TARGET_LONG_BITS == 64, TCG_REG_X1, lb->addrlo_reg); - tcg_out_mov(s, size == MO_64, TCG_REG_X2, lb->datalo_reg); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_X3, oi); - tcg_out_adr(s, TCG_REG_X4, lb->raddr); - tcg_out_call(s, qemu_st_helpers[opc & MO_SIZE]); + tcg_out_st_helper_args(s, lb, &ldst_helper_param); + tcg_out_call_int(s, qemu_st_helpers[opc & MO_SIZE]); tcg_out_goto(s, lb->raddr); return true; } -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, MemOpIdx oi, - TCGType ext, TCGReg data_reg, TCGReg addr_reg, - tcg_insn_unit *raddr, tcg_insn_unit *label_ptr) -{ - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->oi = oi; - label->type = ext; - label->datalo_reg = data_reg; - label->addrlo_reg = addr_reg; - label->raddr = tcg_splitwx_to_rx(raddr); - label->label_ptr[0] = label_ptr; -} - /* We expect to use a 7-bit scaled negative offset from ENV. */ -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -512); +#define MIN_TLB_MASK_TABLE_OFS -512 -/* These offsets are built into the LDP below. */ -QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, mask) != 0); -QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, table) != 8); - -/* Load and compare a TLB entry, emitting the conditional jump to the - slow path for the failure case, which will be patched later when finalizing - the slow path. Generated code returns the host addend in X1, - clobbers X0,X2,X3,TMP. */ -static void tcg_out_tlb_read(TCGContext *s, TCGReg addr_reg, MemOp opc, - tcg_insn_unit **label_ptr, int mem_index, - bool is_read) +/* + * For system-mode, perform the TLB load and compare. + * For user-mode, perform any required alignment tests. + * In both cases, return a TCGLabelQemuLdst structure if the slow path + * is required and fill in @h with the host address for the fast path. + */ +static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, + TCGReg addr_reg, MemOpIdx oi, + bool is_ld) { - unsigned a_bits = get_alignment_bits(opc); - unsigned s_bits = opc & MO_SIZE; - unsigned a_mask = (1u << a_bits) - 1; - unsigned s_mask = (1u << s_bits) - 1; - TCGReg x3; - TCGType mask_type; - uint64_t compare_mask; + TCGType addr_type = s->addr_type; + TCGLabelQemuLdst *ldst = NULL; + MemOp opc = get_memop(oi); + MemOp s_bits = opc & MO_SIZE; + unsigned a_mask; - mask_type = (TARGET_PAGE_BITS + CPU_TLB_DYN_MAX_BITS > 32 - ? TCG_TYPE_I64 : TCG_TYPE_I32); + h->aa = atom_and_align_for_opc(s, opc, + have_lse2 ? MO_ATOM_WITHIN16 + : MO_ATOM_IFALIGN, + s_bits == MO_128); + a_mask = (1 << h->aa.align) - 1; - /* Load env_tlb(env)->f[mmu_idx].{mask,table} into {x0,x1}. */ - tcg_out_insn(s, 3314, LDP, TCG_REG_X0, TCG_REG_X1, TCG_AREG0, - TLB_MASK_TABLE_OFS(mem_index), 1, 0); + if (tcg_use_softmmu) { + unsigned s_mask = (1u << s_bits) - 1; + unsigned mem_index = get_mmuidx(oi); + TCGReg addr_adj; + TCGType mask_type; + uint64_t compare_mask; - /* Extract the TLB index from the address into X0. */ - tcg_out_insn(s, 3502S, AND_LSR, mask_type == TCG_TYPE_I64, - TCG_REG_X0, TCG_REG_X0, addr_reg, - TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addr_reg; - /* Add the tlb_table pointer, creating the CPUTLBEntry address into X1. */ - tcg_out_insn(s, 3502, ADD, 1, TCG_REG_X1, TCG_REG_X1, TCG_REG_X0); + mask_type = (s->page_bits + s->tlb_dyn_max_bits > 32 + ? TCG_TYPE_I64 : TCG_TYPE_I32); - /* Load the tlb comparator into X0, and the fast path addend into X1. */ - tcg_out_ld(s, TCG_TYPE_TL, TCG_REG_X0, TCG_REG_X1, is_read - ? offsetof(CPUTLBEntry, addr_read) - : offsetof(CPUTLBEntry, addr_write)); - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_X1, TCG_REG_X1, - offsetof(CPUTLBEntry, addend)); + /* Load cpu->neg.tlb.f[mmu_idx].{mask,table} into {tmp0,tmp1}. */ + QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, mask) != 0); + QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, table) != 8); + tcg_out_insn(s, 3314, LDP, TCG_REG_TMP0, TCG_REG_TMP1, TCG_AREG0, + tlb_mask_table_ofs(s, mem_index), 1, 0); - /* For aligned accesses, we check the first byte and include the alignment - bits within the address. For unaligned access, we check that we don't - cross pages using the address of the last byte of the access. */ - if (a_bits >= s_bits) { - x3 = addr_reg; + /* Extract the TLB index from the address into X0. */ + tcg_out_insn(s, 3502S, AND_LSR, mask_type == TCG_TYPE_I64, + TCG_REG_TMP0, TCG_REG_TMP0, addr_reg, + s->page_bits - CPU_TLB_ENTRY_BITS); + + /* Add the tlb_table pointer, forming the CPUTLBEntry address. */ + tcg_out_insn(s, 3502, ADD, 1, TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_TMP0); + + /* Load the tlb comparator into TMP0, and the fast path addend. */ + QEMU_BUILD_BUG_ON(HOST_BIG_ENDIAN); + tcg_out_ld(s, addr_type, TCG_REG_TMP0, TCG_REG_TMP1, + is_ld ? offsetof(CPUTLBEntry, addr_read) + : offsetof(CPUTLBEntry, addr_write)); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_REG_TMP1, + offsetof(CPUTLBEntry, addend)); + + /* + * For aligned accesses, we check the first byte and include + * the alignment bits within the address. For unaligned access, + * we check that we don't cross pages using the address of the + * last byte of the access. + */ + if (a_mask >= s_mask) { + addr_adj = addr_reg; + } else { + addr_adj = TCG_REG_TMP2; + tcg_out_insn(s, 3401, ADDI, addr_type, + addr_adj, addr_reg, s_mask - a_mask); + } + compare_mask = (uint64_t)s->page_mask | a_mask; + + /* Store the page mask part of the address into TMP2. */ + tcg_out_logicali(s, I3404_ANDI, addr_type, TCG_REG_TMP2, + addr_adj, compare_mask); + + /* Perform the address comparison. */ + tcg_out_cmp(s, addr_type, TCG_COND_NE, TCG_REG_TMP0, TCG_REG_TMP2, 0); + + /* If not equal, we jump to the slow path. */ + ldst->label_ptr[0] = s->code_ptr; + tcg_out_insn(s, 3202, B_C, TCG_COND_NE, 0); + + h->base = TCG_REG_TMP1; + h->index = addr_reg; + h->index_ext = addr_type; } else { - tcg_out_insn(s, 3401, ADDI, TARGET_LONG_BITS == 64, - TCG_REG_X3, addr_reg, s_mask - a_mask); - x3 = TCG_REG_X3; - } - compare_mask = (uint64_t)TARGET_PAGE_MASK | a_mask; + if (a_mask) { + ldst = new_ldst_label(s); - /* Store the page mask part of the address into X3. */ - tcg_out_logicali(s, I3404_ANDI, TARGET_LONG_BITS == 64, - TCG_REG_X3, x3, compare_mask); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addr_reg; - /* Perform the address comparison. */ - tcg_out_cmp(s, TARGET_LONG_BITS == 64, TCG_REG_X0, TCG_REG_X3, 0); + /* tst addr, #mask */ + tcg_out_logicali(s, I3404_ANDSI, 0, TCG_REG_XZR, addr_reg, a_mask); - /* If not equal, we jump to the slow path. */ - *label_ptr = s->code_ptr; - tcg_out_insn(s, 3202, B_C, TCG_COND_NE, 0); -} + /* b.ne slow_path */ + ldst->label_ptr[0] = s->code_ptr; + tcg_out_insn(s, 3202, B_C, TCG_COND_NE, 0); + } -#else -static void tcg_out_test_alignment(TCGContext *s, bool is_ld, TCGReg addr_reg, - unsigned a_bits) -{ - unsigned a_mask = (1 << a_bits) - 1; - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->addrlo_reg = addr_reg; - - /* tst addr, #mask */ - tcg_out_logicali(s, I3404_ANDSI, 0, TCG_REG_XZR, addr_reg, a_mask); - - label->label_ptr[0] = s->code_ptr; - - /* b.ne slow_path */ - tcg_out_insn(s, 3202, B_C, TCG_COND_NE, 0); - - label->raddr = tcg_splitwx_to_rx(s->code_ptr); -} - -static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) -{ - if (!reloc_pc19(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { - return false; + if (guest_base || addr_type == TCG_TYPE_I32) { + h->base = TCG_REG_GUEST_BASE; + h->index = addr_reg; + h->index_ext = addr_type; + } else { + h->base = addr_reg; + h->index = TCG_REG_XZR; + h->index_ext = TCG_TYPE_I64; + } } - tcg_out_mov(s, TCG_TYPE_TL, TCG_REG_X1, l->addrlo_reg); - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_X0, TCG_AREG0); - - /* "Tail call" to the helper, with the return address back inline. */ - tcg_out_adr(s, TCG_REG_LR, l->raddr); - tcg_out_goto_long(s, (const void *)(l->is_ld ? helper_unaligned_ld - : helper_unaligned_st)); - return true; + return ldst; } -static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - return tcg_out_fail_alignment(s, l); -} - -static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - return tcg_out_fail_alignment(s, l); -} -#endif /* CONFIG_SOFTMMU */ - static void tcg_out_qemu_ld_direct(TCGContext *s, MemOp memop, TCGType ext, - TCGReg data_r, TCGReg addr_r, - TCGType otype, TCGReg off_r) + TCGReg data_r, HostAddress h) { switch (memop & MO_SSIZE) { case MO_UB: - tcg_out_ldst_r(s, I3312_LDRB, data_r, addr_r, otype, off_r); + tcg_out_ldst_r(s, I3312_LDRB, data_r, h.base, h.index_ext, h.index); break; case MO_SB: tcg_out_ldst_r(s, ext ? I3312_LDRSBX : I3312_LDRSBW, - data_r, addr_r, otype, off_r); + data_r, h.base, h.index_ext, h.index); break; case MO_UW: - tcg_out_ldst_r(s, I3312_LDRH, data_r, addr_r, otype, off_r); + tcg_out_ldst_r(s, I3312_LDRH, data_r, h.base, h.index_ext, h.index); break; case MO_SW: tcg_out_ldst_r(s, (ext ? I3312_LDRSHX : I3312_LDRSHW), - data_r, addr_r, otype, off_r); + data_r, h.base, h.index_ext, h.index); break; case MO_UL: - tcg_out_ldst_r(s, I3312_LDRW, data_r, addr_r, otype, off_r); + tcg_out_ldst_r(s, I3312_LDRW, data_r, h.base, h.index_ext, h.index); break; case MO_SL: - tcg_out_ldst_r(s, I3312_LDRSWX, data_r, addr_r, otype, off_r); + tcg_out_ldst_r(s, I3312_LDRSWX, data_r, h.base, h.index_ext, h.index); break; case MO_UQ: - tcg_out_ldst_r(s, I3312_LDRX, data_r, addr_r, otype, off_r); + tcg_out_ldst_r(s, I3312_LDRX, data_r, h.base, h.index_ext, h.index); break; default: - tcg_abort(); + g_assert_not_reached(); } } static void tcg_out_qemu_st_direct(TCGContext *s, MemOp memop, - TCGReg data_r, TCGReg addr_r, - TCGType otype, TCGReg off_r) + TCGReg data_r, HostAddress h) { switch (memop & MO_SIZE) { case MO_8: - tcg_out_ldst_r(s, I3312_STRB, data_r, addr_r, otype, off_r); + tcg_out_ldst_r(s, I3312_STRB, data_r, h.base, h.index_ext, h.index); break; case MO_16: - tcg_out_ldst_r(s, I3312_STRH, data_r, addr_r, otype, off_r); + tcg_out_ldst_r(s, I3312_STRH, data_r, h.base, h.index_ext, h.index); break; case MO_32: - tcg_out_ldst_r(s, I3312_STRW, data_r, addr_r, otype, off_r); + tcg_out_ldst_r(s, I3312_STRW, data_r, h.base, h.index_ext, h.index); break; case MO_64: - tcg_out_ldst_r(s, I3312_STRX, data_r, addr_r, otype, off_r); + tcg_out_ldst_r(s, I3312_STRX, data_r, h.base, h.index_ext, h.index); break; default: - tcg_abort(); + g_assert_not_reached(); } } static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType ext) + MemOpIdx oi, TCGType data_type) { - MemOp memop = get_memop(oi); - const TCGType otype = TARGET_LONG_BITS == 64 ? TCG_TYPE_I64 : TCG_TYPE_I32; + TCGLabelQemuLdst *ldst; + HostAddress h; - /* Byte swapping is left to middle-end expansion. */ - tcg_debug_assert((memop & MO_BSWAP) == 0); + ldst = prepare_host_addr(s, &h, addr_reg, oi, true); + tcg_out_qemu_ld_direct(s, get_memop(oi), data_type, data_reg, h); -#ifdef CONFIG_SOFTMMU - unsigned mem_index = get_mmuidx(oi); - tcg_insn_unit *label_ptr; - - tcg_out_tlb_read(s, addr_reg, memop, &label_ptr, mem_index, 1); - tcg_out_qemu_ld_direct(s, memop, ext, data_reg, - TCG_REG_X1, otype, addr_reg); - add_qemu_ldst_label(s, true, oi, ext, data_reg, addr_reg, - s->code_ptr, label_ptr); -#else /* !CONFIG_SOFTMMU */ - unsigned a_bits = get_alignment_bits(memop); - if (a_bits) { - tcg_out_test_alignment(s, true, addr_reg, a_bits); + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = data_reg; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } - if (USE_GUEST_BASE) { - tcg_out_qemu_ld_direct(s, memop, ext, data_reg, - TCG_REG_GUEST_BASE, otype, addr_reg); - } else { - tcg_out_qemu_ld_direct(s, memop, ext, data_reg, - addr_reg, TCG_TYPE_I64, TCG_REG_XZR); - } -#endif /* CONFIG_SOFTMMU */ } static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi) + MemOpIdx oi, TCGType data_type) { - MemOp memop = get_memop(oi); - const TCGType otype = TARGET_LONG_BITS == 64 ? TCG_TYPE_I64 : TCG_TYPE_I32; + TCGLabelQemuLdst *ldst; + HostAddress h; - /* Byte swapping is left to middle-end expansion. */ - tcg_debug_assert((memop & MO_BSWAP) == 0); + ldst = prepare_host_addr(s, &h, addr_reg, oi, false); + tcg_out_qemu_st_direct(s, get_memop(oi), data_reg, h); -#ifdef CONFIG_SOFTMMU - unsigned mem_index = get_mmuidx(oi); - tcg_insn_unit *label_ptr; - - tcg_out_tlb_read(s, addr_reg, memop, &label_ptr, mem_index, 0); - tcg_out_qemu_st_direct(s, memop, data_reg, - TCG_REG_X1, otype, addr_reg); - add_qemu_ldst_label(s, false, oi, (memop & MO_SIZE)== MO_64, - data_reg, addr_reg, s->code_ptr, label_ptr); -#else /* !CONFIG_SOFTMMU */ - unsigned a_bits = get_alignment_bits(memop); - if (a_bits) { - tcg_out_test_alignment(s, false, addr_reg, a_bits); + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = data_reg; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } - if (USE_GUEST_BASE) { - tcg_out_qemu_st_direct(s, memop, data_reg, - TCG_REG_GUEST_BASE, otype, addr_reg); +} + +static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg addr_reg, MemOpIdx oi, bool is_ld) +{ + TCGLabelQemuLdst *ldst; + HostAddress h; + TCGReg base; + bool use_pair; + + ldst = prepare_host_addr(s, &h, addr_reg, oi, is_ld); + + /* Compose the final address, as LDP/STP have no indexing. */ + if (h.index == TCG_REG_XZR) { + base = h.base; } else { - tcg_out_qemu_st_direct(s, memop, data_reg, - addr_reg, TCG_TYPE_I64, TCG_REG_XZR); + base = TCG_REG_TMP2; + if (h.index_ext == TCG_TYPE_I32) { + /* add base, base, index, uxtw */ + tcg_out_insn(s, 3501, ADD, TCG_TYPE_I64, base, + h.base, h.index, MO_32, 0); + } else { + /* add base, base, index */ + tcg_out_insn(s, 3502, ADD, 1, base, h.base, h.index); + } + } + + use_pair = h.aa.atom < MO_128 || have_lse2; + + if (!use_pair) { + tcg_insn_unit *branch = NULL; + TCGReg ll, lh, sl, sh; + + /* + * If we have already checked for 16-byte alignment, that's all + * we need. Otherwise we have determined that misaligned atomicity + * may be handled with two 8-byte loads. + */ + if (h.aa.align < MO_128) { + /* + * TODO: align should be MO_64, so we only need test bit 3, + * which means we could use TBNZ instead of ANDS+B_C. + */ + tcg_out_logicali(s, I3404_ANDSI, 0, TCG_REG_XZR, addr_reg, 15); + branch = s->code_ptr; + tcg_out_insn(s, 3202, B_C, TCG_COND_NE, 0); + use_pair = true; + } + + if (is_ld) { + /* + * 16-byte atomicity without LSE2 requires LDXP+STXP loop: + * ldxp lo, hi, [base] + * stxp t0, lo, hi, [base] + * cbnz t0, .-8 + * Require no overlap between data{lo,hi} and base. + */ + if (datalo == base || datahi == base) { + tcg_out_mov(s, TCG_TYPE_REG, TCG_REG_TMP2, base); + base = TCG_REG_TMP2; + } + ll = sl = datalo; + lh = sh = datahi; + } else { + /* + * 16-byte atomicity without LSE2 requires LDXP+STXP loop: + * 1: ldxp t0, t1, [base] + * stxp t0, lo, hi, [base] + * cbnz t0, 1b + */ + tcg_debug_assert(base != TCG_REG_TMP0 && base != TCG_REG_TMP1); + ll = TCG_REG_TMP0; + lh = TCG_REG_TMP1; + sl = datalo; + sh = datahi; + } + + tcg_out_insn(s, 3306, LDXP, TCG_REG_XZR, ll, lh, base); + tcg_out_insn(s, 3306, STXP, TCG_REG_TMP0, sl, sh, base); + tcg_out_insn(s, 3201, CBNZ, 0, TCG_REG_TMP0, -2); + + if (use_pair) { + /* "b .+8", branching across the one insn of use_pair. */ + tcg_out_insn(s, 3206, B, 2); + reloc_pc19(branch, tcg_splitwx_to_rx(s->code_ptr)); + } + } + + if (use_pair) { + if (is_ld) { + tcg_out_insn(s, 3314, LDP, datalo, datahi, base, 0, 1, 0); + } else { + tcg_out_insn(s, 3314, STP, datalo, datahi, base, 0, 1, 0); + } + } + + if (ldst) { + ldst->type = TCG_TYPE_I128; + ldst->datalo_reg = datalo; + ldst->datahi_reg = datahi; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } -#endif /* CONFIG_SOFTMMU */ } static const tcg_insn_unit *tb_ret_addr; +static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) +{ + const tcg_insn_unit *target; + ptrdiff_t offset; + + /* Reuse the zeroing that exists for goto_ptr. */ + if (a0 == 0) { + target = tcg_code_gen_epilogue; + } else { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_X0, a0); + target = tb_ret_addr; + } + + offset = tcg_pcrel_diff(s, target) >> 2; + if (offset == sextract64(offset, 0, 26)) { + tcg_out_insn(s, 3206, B, offset); + } else { + /* + * Only x16/x17 generate BTI type Jump (2), + * other registers generate BTI type Jump|Call (3). + */ + QEMU_BUILD_BUG_ON(TCG_REG_TMP0 != TCG_REG_X16); + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP0, (intptr_t)target); + tcg_out_insn(s, 3207, BR, TCG_REG_TMP0); + } +} + +static void tcg_out_goto_tb(TCGContext *s, int which) +{ + /* + * Direct branch, or indirect address load, will be patched + * by tb_target_set_jmp_target. Assert indirect load offset + * in range early, regardless of direct branch distance. + */ + intptr_t i_off = tcg_pcrel_diff(s, (void *)get_jmp_target_addr(s, which)); + tcg_debug_assert(i_off == sextract64(i_off, 0, 21)); + + set_jmp_insn_offset(s, which); + tcg_out32(s, I3206_B); + tcg_out_insn(s, 3207, BR, TCG_REG_TMP0); + set_jmp_reset_offset(s, which); + tcg_out_bti(s, BTI_J); +} + +void tb_target_set_jmp_target(const TranslationBlock *tb, int n, + uintptr_t jmp_rx, uintptr_t jmp_rw) +{ + uintptr_t d_addr = tb->jmp_target_addr[n]; + ptrdiff_t d_offset = d_addr - jmp_rx; + tcg_insn_unit insn; + + /* Either directly branch, or indirect branch load. */ + if (d_offset == sextract64(d_offset, 0, 28)) { + insn = deposit32(I3206_B, 0, 26, d_offset >> 2); + } else { + uintptr_t i_addr = (uintptr_t)&tb->jmp_target_addr[n]; + ptrdiff_t i_offset = i_addr - jmp_rx; + + /* Note that we asserted this in range in tcg_out_goto_tb. */ + insn = deposit32(I3305_LDR | TCG_REG_TMP0, 5, 19, i_offset >> 2); + } + qatomic_set((uint32_t *)jmp_rw, insn); + flush_idcache_range(jmp_rx, jmp_rw, 4); +} + static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1905,39 +2123,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, #define REG0(I) (const_args[I] ? TCG_REG_XZR : (TCGReg)args[I]) switch (opc) { - case INDEX_op_exit_tb: - /* Reuse the zeroing that exists for goto_ptr. */ - if (a0 == 0) { - tcg_out_goto_long(s, tcg_code_gen_epilogue); - } else { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_X0, a0); - tcg_out_goto_long(s, tb_ret_addr); - } - break; - - case INDEX_op_goto_tb: - if (s->tb_jmp_insn_offset != NULL) { - /* TCG_TARGET_HAS_direct_jump */ - /* Ensure that ADRP+ADD are 8-byte aligned so that an atomic - write can be used to patch the target address. */ - if ((uintptr_t)s->code_ptr & 7) { - tcg_out32(s, NOP); - } - s->tb_jmp_insn_offset[a0] = tcg_current_code_size(s); - /* actual branch destination will be patched by - tb_target_set_jmp_target later. */ - tcg_out_insn(s, 3406, ADRP, TCG_REG_TMP, 0); - tcg_out_insn(s, 3401, ADDI, TCG_TYPE_I64, TCG_REG_TMP, TCG_REG_TMP, 0); - } else { - /* !TCG_TARGET_HAS_direct_jump */ - tcg_debug_assert(s->tb_jmp_target_addr != NULL); - intptr_t offset = tcg_pcrel_diff(s, (s->tb_jmp_target_addr + a0)) >> 2; - tcg_out_insn(s, 3305, LDR, offset, TCG_REG_TMP); - } - tcg_out_insn(s, 3207, BR, TCG_REG_TMP); - set_jmp_reset_offset(s, a0); - break; - case INDEX_op_goto_ptr: tcg_out_insn(s, 3207, BR, a0); break; @@ -2107,13 +2292,13 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_rem_i64: case INDEX_op_rem_i32: - tcg_out_insn(s, 3508, SDIV, ext, TCG_REG_TMP, a1, a2); - tcg_out_insn(s, 3509, MSUB, ext, a0, TCG_REG_TMP, a2, a1); + tcg_out_insn(s, 3508, SDIV, ext, TCG_REG_TMP0, a1, a2); + tcg_out_insn(s, 3509, MSUB, ext, a0, TCG_REG_TMP0, a2, a1); break; case INDEX_op_remu_i64: case INDEX_op_remu_i32: - tcg_out_insn(s, 3508, UDIV, ext, TCG_REG_TMP, a1, a2); - tcg_out_insn(s, 3509, MSUB, ext, a0, TCG_REG_TMP, a2, a1); + tcg_out_insn(s, 3508, UDIV, ext, TCG_REG_TMP0, a1, a2); + tcg_out_insn(s, 3509, MSUB, ext, a0, TCG_REG_TMP0, a2, a1); break; case INDEX_op_shl_i64: @@ -2157,8 +2342,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, if (c2) { tcg_out_rotl(s, ext, a0, a1, a2); } else { - tcg_out_insn(s, 3502, SUB, 0, TCG_REG_TMP, TCG_REG_XZR, a2); - tcg_out_insn(s, 3508, RORV, ext, a0, a1, TCG_REG_TMP); + tcg_out_insn(s, 3502, SUB, 0, TCG_REG_TMP0, TCG_REG_XZR, a2); + tcg_out_insn(s, 3508, RORV, ext, a0, a1, TCG_REG_TMP0); } break; @@ -2182,27 +2367,49 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, a2 = (int32_t)a2; /* FALLTHRU */ case INDEX_op_setcond_i64: - tcg_out_cmp(s, ext, a1, a2, c2); + tcg_out_cmp(s, ext, args[3], a1, a2, c2); /* Use CSET alias of CSINC Wd, WZR, WZR, invert(cond). */ tcg_out_insn(s, 3506, CSINC, TCG_TYPE_I32, a0, TCG_REG_XZR, TCG_REG_XZR, tcg_invert_cond(args[3])); break; + case INDEX_op_negsetcond_i32: + a2 = (int32_t)a2; + /* FALLTHRU */ + case INDEX_op_negsetcond_i64: + tcg_out_cmp(s, ext, args[3], a1, a2, c2); + /* Use CSETM alias of CSINV Wd, WZR, WZR, invert(cond). */ + tcg_out_insn(s, 3506, CSINV, ext, a0, TCG_REG_XZR, + TCG_REG_XZR, tcg_invert_cond(args[3])); + break; + case INDEX_op_movcond_i32: a2 = (int32_t)a2; /* FALLTHRU */ case INDEX_op_movcond_i64: - tcg_out_cmp(s, ext, a1, a2, c2); + tcg_out_cmp(s, ext, args[5], a1, a2, c2); tcg_out_insn(s, 3506, CSEL, ext, a0, REG0(3), REG0(4), args[5]); break; - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: tcg_out_qemu_ld(s, a0, a1, a2, ext); break; - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, REG0(0), a1, a2); + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: + tcg_out_qemu_st(s, REG0(0), a1, a2, ext); + break; + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: + tcg_out_qemu_ldst_i128(s, a0, a1, a2, args[3], true); + break; + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: + tcg_out_qemu_ldst_i128(s, REG0(0), REG0(1), a2, args[3], false); break; case INDEX_op_bswap64_i64: @@ -2211,7 +2418,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_bswap32_i64: tcg_out_rev(s, TCG_TYPE_I32, MO_32, a0, a1); if (a2 & TCG_BSWAP_OS) { - tcg_out_sxt(s, TCG_TYPE_I64, MO_32, a0, a0); + tcg_out_ext32s(s, a0, a0); } break; case INDEX_op_bswap32_i32: @@ -2222,38 +2429,13 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_rev(s, TCG_TYPE_I32, MO_16, a0, a1); if (a2 & TCG_BSWAP_OS) { /* Output must be sign-extended. */ - tcg_out_sxt(s, ext, MO_16, a0, a0); + tcg_out_ext16s(s, ext, a0, a0); } else if ((a2 & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { /* Output must be zero-extended, but input isn't. */ - tcg_out_uxt(s, MO_16, a0, a0); + tcg_out_ext16u(s, a0, a0); } break; - case INDEX_op_ext8s_i64: - case INDEX_op_ext8s_i32: - tcg_out_sxt(s, ext, MO_8, a0, a1); - break; - case INDEX_op_ext16s_i64: - case INDEX_op_ext16s_i32: - tcg_out_sxt(s, ext, MO_16, a0, a1); - break; - case INDEX_op_ext_i32_i64: - case INDEX_op_ext32s_i64: - tcg_out_sxt(s, TCG_TYPE_I64, MO_32, a0, a1); - break; - case INDEX_op_ext8u_i64: - case INDEX_op_ext8u_i32: - tcg_out_uxt(s, MO_8, a0, a1); - break; - case INDEX_op_ext16u_i64: - case INDEX_op_ext16u_i32: - tcg_out_uxt(s, MO_16, a0, a1); - break; - case INDEX_op_extu_i32_i64: - case INDEX_op_ext32u_i64: - tcg_out_movr(s, TCG_TYPE_I32, a0, a1); - break; - case INDEX_op_deposit_i64: case INDEX_op_deposit_i32: tcg_out_dep(s, ext, a0, REG0(2), args[3], args[4]); @@ -2307,6 +2489,21 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ + case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ + case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ + case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ + case INDEX_op_ext8s_i64: + case INDEX_op_ext8u_i32: + case INDEX_op_ext8u_i64: + case INDEX_op_ext16s_i64: + case INDEX_op_ext16s_i32: + case INDEX_op_ext16u_i64: + case INDEX_op_ext16u_i32: + case INDEX_op_ext32s_i64: + case INDEX_op_ext32u_i64: + case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); } @@ -2570,8 +2767,8 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, break; } } - tcg_out_dupi_vec(s, type, MO_8, TCG_VEC_TMP, 0); - a2 = TCG_VEC_TMP; + tcg_out_dupi_vec(s, type, MO_8, TCG_VEC_TMP0, 0); + a2 = TCG_VEC_TMP0; } if (is_scalar) { insn = cmp_scalar_insn[cond]; @@ -2790,9 +2987,13 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_add_i64: case INDEX_op_sub_i32: case INDEX_op_sub_i64: + return C_O1_I2(r, r, rA); + case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: - return C_O1_I2(r, r, rA); + case INDEX_op_negsetcond_i32: + case INDEX_op_negsetcond_i64: + return C_O1_I2(r, r, rC); case INDEX_op_mul_i32: case INDEX_op_mul_i64: @@ -2842,18 +3043,28 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: - return C_O0_I2(r, rA); + return C_O0_I2(r, rC); case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: - return C_O1_I4(r, r, rA, rZ, rZ); + return C_O1_I4(r, r, rC, rZ, rZ); - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: - return C_O1_I1(r, l); - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: - return C_O0_I2(lZ, l); + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: + return C_O1_I1(r, r); + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: + return C_O2_I1(r, r, r); + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: + return C_O0_I2(rZ, r); + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: + return C_O0_I3(rZ, rZ, r); case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: @@ -2949,9 +3160,11 @@ static void tcg_target_init(TCGContext *s) s->reserved_regs = 0; tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); tcg_regset_set_reg(s->reserved_regs, TCG_REG_FP); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP); tcg_regset_set_reg(s->reserved_regs, TCG_REG_X18); /* platform register */ - tcg_regset_set_reg(s->reserved_regs, TCG_VEC_TMP); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP0); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP1); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP2); + tcg_regset_set_reg(s->reserved_regs, TCG_VEC_TMP0); } /* Saving pairs: (X19, X20) .. (X27, X28), (X29(fp), X30(lr)). */ @@ -2974,6 +3187,8 @@ static void tcg_target_qemu_prologue(TCGContext *s) { TCGReg r; + tcg_out_bti(s, BTI_C); + /* Push (FP, LR) and allocate space for all saved registers. */ tcg_out_insn(s, 3314, STP, TCG_REG_FP, TCG_REG_LR, TCG_REG_SP, -PUSH_SIZE, 1, 1); @@ -2995,12 +3210,16 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_set_frame(s, TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE, CPU_TEMP_BUF_NLONGS * sizeof(long)); -#if !defined(CONFIG_SOFTMMU) - if (USE_GUEST_BASE) { + if (!tcg_use_softmmu) { + /* + * Note that XZR cannot be encoded in the address base register slot, + * as that actually encodes SP. Depending on the guest, we may need + * to zero-extend the guest address via the address index register slot, + * therefore we need to load even a zero guest base into a register. + */ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_GUEST_BASE, guest_base); tcg_regset_set_reg(s->reserved_regs, TCG_REG_GUEST_BASE); } -#endif tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); tcg_out_insn(s, 3207, BR, tcg_target_call_iarg_regs[1]); @@ -3010,10 +3229,12 @@ static void tcg_target_qemu_prologue(TCGContext *s) * and fall through to the rest of the epilogue. */ tcg_code_gen_epilogue = tcg_splitwx_to_rx(s->code_ptr); + tcg_out_bti(s, BTI_J); tcg_out_movi(s, TCG_TYPE_REG, TCG_REG_X0, 0); /* TB epilogue */ tb_ret_addr = tcg_splitwx_to_rx(s->code_ptr); + tcg_out_bti(s, BTI_J); /* Remove TCG locals stack space. */ tcg_out_insn(s, 3401, ADDI, TCG_TYPE_I64, TCG_REG_SP, TCG_REG_SP, @@ -3031,6 +3252,11 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_insn(s, 3207, RET, TCG_REG_LR); } +static void tcg_out_tb_start(TCGContext *s) +{ + tcg_out_bti(s, BTI_J); +} + static void tcg_out_nop_fill(tcg_insn_unit *p, int count) { int i; diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 485f685bd2..85d5746e47 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -13,10 +13,10 @@ #ifndef AARCH64_TCG_TARGET_H #define AARCH64_TCG_TARGET_H +#include "host/cpuinfo.h" + #define TCG_TARGET_INSN_UNIT_SIZE 4 -#define TCG_TARGET_TLB_DISPLACEMENT_BITS 24 -#define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB) -#undef TCG_TARGET_STACK_GROWSUP +#define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) typedef enum { TCG_REG_X0, TCG_REG_X1, TCG_REG_X2, TCG_REG_X3, @@ -52,8 +52,18 @@ typedef enum { /* used for function call generation */ #define TCG_REG_CALL_STACK TCG_REG_SP #define TCG_TARGET_STACK_ALIGN 16 -#define TCG_TARGET_CALL_ALIGN_ARGS 1 #define TCG_TARGET_CALL_STACK_OFFSET 0 +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +#ifdef CONFIG_DARWIN +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL +#else +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN +#endif +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL + +#define have_lse (cpuinfo & CPUINFO_LSE) +#define have_lse2 (cpuinfo & CPUINFO_LSE2) /* optional instructions */ #define TCG_TARGET_HAS_div_i32 1 @@ -65,7 +75,6 @@ typedef enum { #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 1 @@ -79,15 +88,14 @@ typedef enum { #define TCG_TARGET_HAS_extract_i32 1 #define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 -#define TCG_TARGET_HAS_movcond_i32 1 +#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_extrl_i64_i32 0 -#define TCG_TARGET_HAS_extrh_i64_i32 0 +#define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_div_i64 1 @@ -102,7 +110,6 @@ typedef enum { #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_orc_i64 1 @@ -116,14 +123,26 @@ typedef enum { #define TCG_TARGET_HAS_extract_i64 1 #define TCG_TARGET_HAS_sextract_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 -#define TCG_TARGET_HAS_movcond_i64 1 +#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_muls2_i64 0 #define TCG_TARGET_HAS_muluh_i64 1 #define TCG_TARGET_HAS_mulsh_i64 1 -#define TCG_TARGET_HAS_direct_jump 1 + +/* + * Without FEAT_LSE2, we must use LDXP+STXP to implement atomic 128-bit load, + * which requires writable pages. We must defer to the helper for user-only, + * but in system mode all ram is writable for the host. + */ +#ifdef CONFIG_USER_ONLY +#define TCG_TARGET_HAS_qemu_ldst_i128 have_lse2 +#else +#define TCG_TARGET_HAS_qemu_ldst_i128 1 +#endif + +#define TCG_TARGET_HAS_tst 1 #define TCG_TARGET_HAS_v64 1 #define TCG_TARGET_HAS_v128 1 @@ -150,10 +169,6 @@ typedef enum { #define TCG_TARGET_HAS_cmpsel_vec 0 #define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_HAS_MEMORY_BSWAP 0 - -void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t, uintptr_t); - #define TCG_TARGET_NEED_LDST_LABELS #define TCG_TARGET_NEED_POOL_LABELS diff --git a/tcg/arm/tcg-target-con-set.h b/tcg/arm/tcg-target-con-set.h index 3685e1786a..229ae258ac 100644 --- a/tcg/arm/tcg-target-con-set.h +++ b/tcg/arm/tcg-target-con-set.h @@ -12,18 +12,19 @@ C_O0_I1(r) C_O0_I2(r, r) C_O0_I2(r, rIN) -C_O0_I2(s, s) +C_O0_I2(q, q) C_O0_I2(w, r) -C_O0_I3(s, s, s) +C_O0_I3(q, q, q) +C_O0_I3(Q, p, q) C_O0_I4(r, r, rI, rI) -C_O0_I4(s, s, s, s) -C_O1_I1(r, l) +C_O0_I4(Q, p, q, q) +C_O1_I1(r, q) C_O1_I1(r, r) C_O1_I1(w, r) C_O1_I1(w, w) C_O1_I1(w, wr) C_O1_I2(r, 0, rZ) -C_O1_I2(r, l, l) +C_O1_I2(r, q, q) C_O1_I2(r, r, r) C_O1_I2(r, r, rI) C_O1_I2(r, r, rIK) @@ -38,8 +39,8 @@ C_O1_I2(w, w, wZ) C_O1_I3(w, w, w, w) C_O1_I4(r, r, r, rI, rI) C_O1_I4(r, r, rIN, rIK, 0) -C_O2_I1(r, r, l) -C_O2_I2(r, r, l, l) +C_O2_I1(e, p, q) +C_O2_I2(e, p, q, q) C_O2_I2(r, r, r, r) C_O2_I4(r, r, r, r, rIN, rIK) C_O2_I4(r, r, rI, rI, rIN, rIK) diff --git a/tcg/arm/tcg-target-con-str.h b/tcg/arm/tcg-target-con-str.h index 8f501149e1..f83f1d3919 100644 --- a/tcg/arm/tcg-target-con-str.h +++ b/tcg/arm/tcg-target-con-str.h @@ -8,9 +8,10 @@ * Define constraint letters for register sets: * REGS(letter, register_mask) */ +REGS('e', ALL_GENERAL_REGS & 0x5555) /* even regs */ REGS('r', ALL_GENERAL_REGS) -REGS('l', ALL_QLOAD_REGS) -REGS('s', ALL_QSTORE_REGS) +REGS('q', ALL_QLDST_REGS) +REGS('Q', ALL_QLDST_REGS & 0x5555) /* even qldst */ REGS('w', ALL_VECTOR_REGS) /* diff --git a/tcg/arm/tcg-target-reg-bits.h b/tcg/arm/tcg-target-reg-bits.h new file mode 100644 index 0000000000..23b7730a8d --- /dev/null +++ b/tcg/arm/tcg-target-reg-bits.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2023 Linaro + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +#define TCG_TARGET_REG_BITS 32 + +#endif diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 2c6c353eea..6a04c73c76 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -79,15 +79,17 @@ static const int tcg_target_reg_alloc_order[] = { static const int tcg_target_call_iarg_regs[4] = { TCG_REG_R0, TCG_REG_R1, TCG_REG_R2, TCG_REG_R3 }; -static const int tcg_target_call_oarg_regs[2] = { - TCG_REG_R0, TCG_REG_R1 -}; + +static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) +{ + tcg_debug_assert(kind == TCG_CALL_RET_NORMAL); + tcg_debug_assert(slot >= 0 && slot <= 3); + return TCG_REG_R0 + slot; +} #define TCG_REG_TMP TCG_REG_R12 #define TCG_VEC_TMP TCG_REG_Q15 -#ifndef CONFIG_SOFTMMU #define TCG_REG_GUEST_BASE TCG_REG_R11 -#endif typedef enum { COND_EQ = 0x0, @@ -135,6 +137,8 @@ typedef enum { ARITH_BIC = 0xe << 21, ARITH_MVN = 0xf << 21, + INSN_B = 0x0a000000, + INSN_CLZ = 0x016f0f10, INSN_RBIT = 0x06ff0f30, @@ -347,24 +351,11 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, #define ALL_VECTOR_REGS 0xffff0000u /* - * r0-r2 will be overwritten when reading the tlb entry (softmmu only) - * and r0-r1 doing the byte swapping, so don't use these. - * r3 is removed for softmmu to avoid clashes with helper arguments. + * r0-r3 will be overwritten when reading the tlb entry (system-mode only); + * r14 will be overwritten by the BLNE branching to the slow path. */ -#ifdef CONFIG_SOFTMMU -#define ALL_QLOAD_REGS \ - (ALL_GENERAL_REGS & ~((1 << TCG_REG_R0) | (1 << TCG_REG_R1) | \ - (1 << TCG_REG_R2) | (1 << TCG_REG_R3) | \ - (1 << TCG_REG_R14))) -#define ALL_QSTORE_REGS \ - (ALL_GENERAL_REGS & ~((1 << TCG_REG_R0) | (1 << TCG_REG_R1) | \ - (1 << TCG_REG_R2) | (1 << TCG_REG_R14) | \ - ((TARGET_LONG_BITS == 64) << TCG_REG_R3))) -#else -#define ALL_QLOAD_REGS ALL_GENERAL_REGS -#define ALL_QSTORE_REGS \ - (ALL_GENERAL_REGS & ~((1 << TCG_REG_R0) | (1 << TCG_REG_R1))) -#endif +#define ALL_QLDST_REGS \ + (ALL_GENERAL_REGS & ~((tcg_use_softmmu ? 0xf : 0) | (1 << TCG_REG_R14))) /* * ARM immediates for ALU instructions are made of an unsigned 8-bit @@ -510,7 +501,8 @@ static bool is_shimm1632(uint32_t v32, int *cmode, int *imm8) * mov operand2: values represented with x << (2 * y), x < 0x100 * add, sub, eor...: ditto */ -static bool tcg_target_const_match(int64_t val, TCGType type, int ct) +static bool tcg_target_const_match(int64_t val, int ct, + TCGType type, TCGCond cond, int vece) { if (ct & TCG_CT_CONST) { return 1; @@ -546,7 +538,7 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) static void tcg_out_b_imm(TCGContext *s, ARMCond cond, int32_t offset) { - tcg_out32(s, (cond << 28) | 0x0a000000 | + tcg_out32(s, (cond << 28) | INSN_B | (((offset - 8) >> 2) & 0x00ffffff)); } @@ -684,8 +676,8 @@ tcg_out_ldrd_rwb(TCGContext *s, ARMCond cond, TCGReg rt, TCGReg rn, TCGReg rm) tcg_out_memop_r(s, cond, INSN_LDRD_REG, rt, rn, rm, 1, 1, 1); } -static void tcg_out_strd_8(TCGContext *s, ARMCond cond, TCGReg rt, - TCGReg rn, int imm8) +static void __attribute__((unused)) +tcg_out_strd_8(TCGContext *s, ARMCond cond, TCGReg rt, TCGReg rn, int imm8) { tcg_out_memop_8(s, cond, INSN_STRD_IMM, rt, rn, imm8, 1, 0); } @@ -952,28 +944,52 @@ static void tcg_out_udiv(TCGContext *s, ARMCond cond, tcg_out32(s, 0x0730f010 | (cond << 28) | (rd << 16) | rn | (rm << 8)); } -static void tcg_out_ext8s(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn) +static void tcg_out_ext8s(TCGContext *s, TCGType t, TCGReg rd, TCGReg rn) { /* sxtb */ - tcg_out32(s, 0x06af0070 | (cond << 28) | (rd << 12) | rn); + tcg_out32(s, 0x06af0070 | (COND_AL << 28) | (rd << 12) | rn); } -static void __attribute__((unused)) -tcg_out_ext8u(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn) +static void tcg_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rn) { - tcg_out_dat_imm(s, cond, ARITH_AND, rd, rn, 0xff); + tcg_out_dat_imm(s, COND_AL, ARITH_AND, rd, rn, 0xff); } -static void tcg_out_ext16s(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn) +static void tcg_out_ext16s(TCGContext *s, TCGType t, TCGReg rd, TCGReg rn) { /* sxth */ - tcg_out32(s, 0x06bf0070 | (cond << 28) | (rd << 12) | rn); + tcg_out32(s, 0x06bf0070 | (COND_AL << 28) | (rd << 12) | rn); } -static void tcg_out_ext16u(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn) +static void tcg_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rn) { /* uxth */ - tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | rn); + tcg_out32(s, 0x06ff0070 | (COND_AL << 28) | (rd << 12) | rn); +} + +static void tcg_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rn) +{ + g_assert_not_reached(); +} + +static void tcg_out_ext32u(TCGContext *s, TCGReg rd, TCGReg rn) +{ + g_assert_not_reached(); +} + +static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rn) +{ + g_assert_not_reached(); +} + +static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg rd, TCGReg rn) +{ + g_assert_not_reached(); +} + +static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rn) +{ + g_assert_not_reached(); } static void tcg_out_bswap16(TCGContext *s, ARMCond cond, @@ -1131,7 +1147,7 @@ static void tcg_out_goto(TCGContext *s, ARMCond cond, const tcg_insn_unit *addr) * The call case is mostly used for helpers - so it's not unreasonable * for them to be beyond branch range. */ -static void tcg_out_call(TCGContext *s, const tcg_insn_unit *addr) +static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *addr) { intptr_t addri = (intptr_t)addr; ptrdiff_t disp = tcg_pcrel_diff(s, addr); @@ -1150,6 +1166,12 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *addr) tcg_out_blx_reg(s, COND_AL, TCG_REG_TMP); } +static void tcg_out_call(TCGContext *s, const tcg_insn_unit *addr, + const TCGHelperInfo *info) +{ + tcg_out_call_int(s, addr); +} + static void tcg_out_goto_label(TCGContext *s, ARMCond cond, TCGLabel *l) { if (l->has_value) { @@ -1169,6 +1191,33 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) } } +static TCGCond tcg_out_cmp(TCGContext *s, TCGCond cond, TCGReg a, + TCGArg b, int b_const) +{ + if (!is_tst_cond(cond)) { + tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, a, b, b_const); + return cond; + } + + cond = tcg_tst_eqne_cond(cond); + if (b_const) { + int imm12 = encode_imm(b); + + /* + * The compare constraints allow rIN, but TST does not support N. + * Be prepared to load the constant into a scratch register. + */ + if (imm12 >= 0) { + tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, a, imm12); + return cond; + } + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, b); + b = TCG_REG_TMP; + } + tcg_out_dat_reg(s, COND_AL, ARITH_TST, 0, a, b, SHIFT_IMM_LSL(0)); + return cond; +} + static TCGCond tcg_out_cmp2(TCGContext *s, const TCGArg *args, const int *const_args) { @@ -1187,13 +1236,22 @@ static TCGCond tcg_out_cmp2(TCGContext *s, const TCGArg *args, case TCG_COND_LEU: case TCG_COND_GTU: case TCG_COND_GEU: - /* We perform a conditional comparision. If the high half is - equal, then overwrite the flags with the comparison of the - low half. The resulting flags cover the whole. */ + /* + * We perform a conditional comparison. If the high half is + * equal, then overwrite the flags with the comparison of the + * low half. The resulting flags cover the whole. + */ tcg_out_dat_rI(s, COND_AL, ARITH_CMP, 0, ah, bh, const_bh); tcg_out_dat_rI(s, COND_EQ, ARITH_CMP, 0, al, bl, const_bl); return cond; + case TCG_COND_TSTEQ: + case TCG_COND_TSTNE: + /* Similar, but with TST instead of CMP. */ + tcg_out_dat_rI(s, COND_AL, ARITH_TST, 0, ah, bh, const_bh); + tcg_out_dat_rI(s, COND_EQ, ARITH_TST, 0, al, bl, const_bl); + return tcg_tst_eqne_cond(cond); + case TCG_COND_LT: case TCG_COND_GE: /* We perform a double-word subtraction and examine the result. @@ -1221,7 +1279,7 @@ static TCGCond tcg_out_cmp2(TCGContext *s, const TCGArg *args, /* * Note that TCGReg references Q-registers. - * Q-regno = 2 * D-regno, so shift left by 1 whlie inserting. + * Q-regno = 2 * D-regno, so shift left by 1 while inserting. */ static uint32_t encode_vd(TCGReg rd) { @@ -1289,260 +1347,42 @@ static void tcg_out_vldst(TCGContext *s, ARMInsn insn, tcg_out32(s, insn | (rn << 16) | encode_vd(rd) | 0xf); } -#ifdef CONFIG_SOFTMMU -/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, - * int mmu_idx, uintptr_t ra) - */ -static void * const qemu_ld_helpers[MO_SSIZE + 1] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_SB] = helper_ret_ldsb_mmu, -#if HOST_BIG_ENDIAN - [MO_UW] = helper_be_lduw_mmu, - [MO_UL] = helper_be_ldul_mmu, - [MO_UQ] = helper_be_ldq_mmu, - [MO_SW] = helper_be_ldsw_mmu, - [MO_SL] = helper_be_ldul_mmu, -#else - [MO_UW] = helper_le_lduw_mmu, - [MO_UL] = helper_le_ldul_mmu, - [MO_UQ] = helper_le_ldq_mmu, - [MO_SW] = helper_le_ldsw_mmu, - [MO_SL] = helper_le_ldul_mmu, -#endif +typedef struct { + ARMCond cond; + TCGReg base; + int index; + bool index_scratch; + TCGAtomAlign aa; +} HostAddress; + +bool tcg_target_has_memory_bswap(MemOp memop) +{ + return false; +} + +static TCGReg ldst_ra_gen(TCGContext *s, const TCGLabelQemuLdst *l, int arg) +{ + /* We arrive at the slow path via "BLNE", so R14 contains l->raddr. */ + return TCG_REG_R14; +} + +static const TCGLdstHelperParam ldst_helper_param = { + .ra_gen = ldst_ra_gen, + .ntmp = 1, + .tmp = { TCG_REG_TMP }, }; -/* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, - * uintxx_t val, int mmu_idx, uintptr_t ra) - */ -static void * const qemu_st_helpers[MO_SIZE + 1] = { - [MO_8] = helper_ret_stb_mmu, -#if HOST_BIG_ENDIAN - [MO_16] = helper_be_stw_mmu, - [MO_32] = helper_be_stl_mmu, - [MO_64] = helper_be_stq_mmu, -#else - [MO_16] = helper_le_stw_mmu, - [MO_32] = helper_le_stl_mmu, - [MO_64] = helper_le_stq_mmu, -#endif -}; - -/* Helper routines for marshalling helper function arguments into - * the correct registers and stack. - * argreg is where we want to put this argument, arg is the argument itself. - * Return value is the updated argreg ready for the next call. - * Note that argreg 0..3 is real registers, 4+ on stack. - * - * We provide routines for arguments which are: immediate, 32 bit - * value in register, 16 and 8 bit values in register (which must be zero - * extended before use) and 64 bit value in a lo:hi register pair. - */ -#define DEFINE_TCG_OUT_ARG(NAME, ARGTYPE, MOV_ARG, EXT_ARG) \ -static TCGReg NAME(TCGContext *s, TCGReg argreg, ARGTYPE arg) \ -{ \ - if (argreg < 4) { \ - MOV_ARG(s, COND_AL, argreg, arg); \ - } else { \ - int ofs = (argreg - 4) * 4; \ - EXT_ARG; \ - tcg_debug_assert(ofs + 4 <= TCG_STATIC_CALL_ARGS_SIZE); \ - tcg_out_st32_12(s, COND_AL, arg, TCG_REG_CALL_STACK, ofs); \ - } \ - return argreg + 1; \ -} - -DEFINE_TCG_OUT_ARG(tcg_out_arg_imm32, uint32_t, tcg_out_movi32, - (tcg_out_movi32(s, COND_AL, TCG_REG_TMP, arg), arg = TCG_REG_TMP)) -DEFINE_TCG_OUT_ARG(tcg_out_arg_reg8, TCGReg, tcg_out_ext8u, - (tcg_out_ext8u(s, COND_AL, TCG_REG_TMP, arg), arg = TCG_REG_TMP)) -DEFINE_TCG_OUT_ARG(tcg_out_arg_reg16, TCGReg, tcg_out_ext16u, - (tcg_out_ext16u(s, COND_AL, TCG_REG_TMP, arg), arg = TCG_REG_TMP)) -DEFINE_TCG_OUT_ARG(tcg_out_arg_reg32, TCGReg, tcg_out_mov_reg, ) - -static TCGReg tcg_out_arg_reg64(TCGContext *s, TCGReg argreg, - TCGReg arglo, TCGReg arghi) -{ - /* 64 bit arguments must go in even/odd register pairs - * and in 8-aligned stack slots. - */ - if (argreg & 1) { - argreg++; - } - if (argreg >= 4 && (arglo & 1) == 0 && arghi == arglo + 1) { - tcg_out_strd_8(s, COND_AL, arglo, - TCG_REG_CALL_STACK, (argreg - 4) * 4); - return argreg + 2; - } else { - argreg = tcg_out_arg_reg32(s, argreg, arglo); - argreg = tcg_out_arg_reg32(s, argreg, arghi); - return argreg; - } -} - -#define TLB_SHIFT (CPU_TLB_ENTRY_BITS + CPU_TLB_BITS) - -/* We expect to use an 9-bit sign-magnitude negative offset from ENV. */ -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -256); - -/* These offsets are built into the LDRD below. */ -QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, mask) != 0); -QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, table) != 4); - -/* Load and compare a TLB entry, leaving the flags set. Returns the register - containing the addend of the tlb entry. Clobbers R0, R1, R2, TMP. */ - -static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, - MemOp opc, int mem_index, bool is_load) -{ - int cmp_off = (is_load ? offsetof(CPUTLBEntry, addr_read) - : offsetof(CPUTLBEntry, addr_write)); - int fast_off = TLB_MASK_TABLE_OFS(mem_index); - unsigned s_mask = (1 << (opc & MO_SIZE)) - 1; - unsigned a_mask = (1 << get_alignment_bits(opc)) - 1; - TCGReg t_addr; - - /* Load env_tlb(env)->f[mmu_idx].{mask,table} into {r0,r1}. */ - tcg_out_ldrd_8(s, COND_AL, TCG_REG_R0, TCG_AREG0, fast_off); - - /* Extract the tlb index from the address into R0. */ - tcg_out_dat_reg(s, COND_AL, ARITH_AND, TCG_REG_R0, TCG_REG_R0, addrlo, - SHIFT_IMM_LSR(TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS)); - - /* - * Add the tlb_table pointer, creating the CPUTLBEntry address in R1. - * Load the tlb comparator into R2/R3 and the fast path addend into R1. - */ - if (cmp_off == 0) { - if (TARGET_LONG_BITS == 64) { - tcg_out_ldrd_rwb(s, COND_AL, TCG_REG_R2, TCG_REG_R1, TCG_REG_R0); - } else { - tcg_out_ld32_rwb(s, COND_AL, TCG_REG_R2, TCG_REG_R1, TCG_REG_R0); - } - } else { - tcg_out_dat_reg(s, COND_AL, ARITH_ADD, - TCG_REG_R1, TCG_REG_R1, TCG_REG_R0, 0); - if (TARGET_LONG_BITS == 64) { - tcg_out_ldrd_8(s, COND_AL, TCG_REG_R2, TCG_REG_R1, cmp_off); - } else { - tcg_out_ld32_12(s, COND_AL, TCG_REG_R2, TCG_REG_R1, cmp_off); - } - } - - /* Load the tlb addend. */ - tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R1, - offsetof(CPUTLBEntry, addend)); - - /* - * Check alignment, check comparators. - * Do this in 2-4 insns. Use MOVW for v7, if possible, - * to reduce the number of sequential conditional instructions. - * Almost all guests have at least 4k pages, which means that we need - * to clear at least 9 bits even for an 8-byte memory, which means it - * isn't worth checking for an immediate operand for BIC. - * - * For unaligned accesses, test the page of the last unit of alignment. - * This leaves the least significant alignment bits unchanged, and of - * course must be zero. - */ - t_addr = addrlo; - if (a_mask < s_mask) { - t_addr = TCG_REG_R0; - tcg_out_dat_imm(s, COND_AL, ARITH_ADD, t_addr, - addrlo, s_mask - a_mask); - } - if (use_armv7_instructions && TARGET_PAGE_BITS <= 16) { - tcg_out_movi32(s, COND_AL, TCG_REG_TMP, ~(TARGET_PAGE_MASK | a_mask)); - tcg_out_dat_reg(s, COND_AL, ARITH_BIC, TCG_REG_TMP, - t_addr, TCG_REG_TMP, 0); - tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, TCG_REG_R2, TCG_REG_TMP, 0); - } else { - if (a_mask) { - tcg_debug_assert(a_mask <= 0xff); - tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, addrlo, a_mask); - } - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_TMP, 0, t_addr, - SHIFT_IMM_LSR(TARGET_PAGE_BITS)); - tcg_out_dat_reg(s, (a_mask ? COND_EQ : COND_AL), ARITH_CMP, - 0, TCG_REG_R2, TCG_REG_TMP, - SHIFT_IMM_LSL(TARGET_PAGE_BITS)); - } - - if (TARGET_LONG_BITS == 64) { - tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, TCG_REG_R3, addrhi, 0); - } - - return TCG_REG_R1; -} - -/* Record the context of a call to the out of line helper code for the slow - path for a load or store, so that we can later generate the correct - helper code. */ -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, MemOpIdx oi, - TCGReg datalo, TCGReg datahi, TCGReg addrlo, - TCGReg addrhi, tcg_insn_unit *raddr, - tcg_insn_unit *label_ptr) -{ - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->oi = oi; - label->datalo_reg = datalo; - label->datahi_reg = datahi; - label->addrlo_reg = addrlo; - label->addrhi_reg = addrhi; - label->raddr = tcg_splitwx_to_rx(raddr); - label->label_ptr[0] = label_ptr; -} - static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { - TCGReg argreg, datalo, datahi; - MemOpIdx oi = lb->oi; - MemOp opc = get_memop(oi); + MemOp opc = get_memop(lb->oi); if (!reloc_pc24(lb->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { return false; } - argreg = tcg_out_arg_reg32(s, TCG_REG_R0, TCG_AREG0); - if (TARGET_LONG_BITS == 64) { - argreg = tcg_out_arg_reg64(s, argreg, lb->addrlo_reg, lb->addrhi_reg); - } else { - argreg = tcg_out_arg_reg32(s, argreg, lb->addrlo_reg); - } - argreg = tcg_out_arg_imm32(s, argreg, oi); - argreg = tcg_out_arg_reg32(s, argreg, TCG_REG_R14); - - /* Use the canonical unsigned helpers and minimize icache usage. */ - tcg_out_call(s, qemu_ld_helpers[opc & MO_SIZE]); - - datalo = lb->datalo_reg; - datahi = lb->datahi_reg; - switch (opc & MO_SSIZE) { - case MO_SB: - tcg_out_ext8s(s, COND_AL, datalo, TCG_REG_R0); - break; - case MO_SW: - tcg_out_ext16s(s, COND_AL, datalo, TCG_REG_R0); - break; - default: - tcg_out_mov_reg(s, COND_AL, datalo, TCG_REG_R0); - break; - case MO_UQ: - if (datalo != TCG_REG_R1) { - tcg_out_mov_reg(s, COND_AL, datalo, TCG_REG_R0); - tcg_out_mov_reg(s, COND_AL, datahi, TCG_REG_R1); - } else if (datahi != TCG_REG_R0) { - tcg_out_mov_reg(s, COND_AL, datahi, TCG_REG_R1); - tcg_out_mov_reg(s, COND_AL, datalo, TCG_REG_R0); - } else { - tcg_out_mov_reg(s, COND_AL, TCG_REG_TMP, TCG_REG_R0); - tcg_out_mov_reg(s, COND_AL, datahi, TCG_REG_R1); - tcg_out_mov_reg(s, COND_AL, datalo, TCG_REG_TMP); - } - break; - } + tcg_out_ld_helper_args(s, lb, &ldst_helper_param); + tcg_out_call_int(s, qemu_ld_helpers[opc & MO_SIZE]); + tcg_out_ld_helper_ret(s, lb, false, &ldst_helper_param); tcg_out_goto(s, COND_AL, lb->raddr); return true; @@ -1550,383 +1390,415 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { - TCGReg argreg, datalo, datahi; - MemOpIdx oi = lb->oi; - MemOp opc = get_memop(oi); + MemOp opc = get_memop(lb->oi); if (!reloc_pc24(lb->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { return false; } - argreg = TCG_REG_R0; - argreg = tcg_out_arg_reg32(s, argreg, TCG_AREG0); - if (TARGET_LONG_BITS == 64) { - argreg = tcg_out_arg_reg64(s, argreg, lb->addrlo_reg, lb->addrhi_reg); - } else { - argreg = tcg_out_arg_reg32(s, argreg, lb->addrlo_reg); - } - - datalo = lb->datalo_reg; - datahi = lb->datahi_reg; - switch (opc & MO_SIZE) { - case MO_8: - argreg = tcg_out_arg_reg8(s, argreg, datalo); - break; - case MO_16: - argreg = tcg_out_arg_reg16(s, argreg, datalo); - break; - case MO_32: - default: - argreg = tcg_out_arg_reg32(s, argreg, datalo); - break; - case MO_64: - argreg = tcg_out_arg_reg64(s, argreg, datalo, datahi); - break; - } - - argreg = tcg_out_arg_imm32(s, argreg, oi); - argreg = tcg_out_arg_reg32(s, argreg, TCG_REG_R14); + tcg_out_st_helper_args(s, lb, &ldst_helper_param); /* Tail-call to the helper, which will return to the fast path. */ tcg_out_goto(s, COND_AL, qemu_st_helpers[opc & MO_SIZE]); return true; } -#else -static void tcg_out_test_alignment(TCGContext *s, bool is_ld, TCGReg addrlo, - TCGReg addrhi, unsigned a_bits) +/* We expect to use an 9-bit sign-magnitude negative offset from ENV. */ +#define MIN_TLB_MASK_TABLE_OFS -256 + +static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, + TCGReg addrlo, TCGReg addrhi, + MemOpIdx oi, bool is_ld) { - unsigned a_mask = (1 << a_bits) - 1; - TCGLabelQemuLdst *label = new_ldst_label(s); + TCGLabelQemuLdst *ldst = NULL; + MemOp opc = get_memop(oi); + unsigned a_mask; - label->is_ld = is_ld; - label->addrlo_reg = addrlo; - label->addrhi_reg = addrhi; - - /* We are expecting a_bits to max out at 7, and can easily support 8. */ - tcg_debug_assert(a_mask <= 0xff); - /* tst addr, #mask */ - tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, addrlo, a_mask); - - /* blne slow_path */ - label->label_ptr[0] = s->code_ptr; - tcg_out_bl_imm(s, COND_NE, 0); - - label->raddr = tcg_splitwx_to_rx(s->code_ptr); -} - -static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) -{ - if (!reloc_pc24(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { - return false; - } - - if (TARGET_LONG_BITS == 64) { - /* 64-bit target address is aligned into R2:R3. */ - if (l->addrhi_reg != TCG_REG_R2) { - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R2, l->addrlo_reg); - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R3, l->addrhi_reg); - } else if (l->addrlo_reg != TCG_REG_R3) { - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R3, l->addrhi_reg); - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R2, l->addrlo_reg); - } else { - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R1, TCG_REG_R2); - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R2, TCG_REG_R3); - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R3, TCG_REG_R1); - } + if (tcg_use_softmmu) { + *h = (HostAddress){ + .cond = COND_AL, + .base = addrlo, + .index = TCG_REG_R1, + .index_scratch = true, + }; } else { - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R1, l->addrlo_reg); + *h = (HostAddress){ + .cond = COND_AL, + .base = addrlo, + .index = guest_base ? TCG_REG_GUEST_BASE : -1, + .index_scratch = false, + }; } - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R0, TCG_AREG0); - /* - * Tail call to the helper, with the return address back inline, - * just for the clarity of the debugging traceback -- the helper - * cannot return. We have used BLNE to arrive here, so LR is - * already set. - */ - tcg_out_goto(s, COND_AL, (const void *) - (l->is_ld ? helper_unaligned_ld : helper_unaligned_st)); - return true; + h->aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, false); + a_mask = (1 << h->aa.align) - 1; + + if (tcg_use_softmmu) { + int mem_index = get_mmuidx(oi); + int cmp_off = is_ld ? offsetof(CPUTLBEntry, addr_read) + : offsetof(CPUTLBEntry, addr_write); + int fast_off = tlb_mask_table_ofs(s, mem_index); + unsigned s_mask = (1 << (opc & MO_SIZE)) - 1; + TCGReg t_addr; + + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addrlo; + ldst->addrhi_reg = addrhi; + + /* Load cpu->neg.tlb.f[mmu_idx].{mask,table} into {r0,r1}. */ + QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, mask) != 0); + QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, table) != 4); + tcg_out_ldrd_8(s, COND_AL, TCG_REG_R0, TCG_AREG0, fast_off); + + /* Extract the tlb index from the address into R0. */ + tcg_out_dat_reg(s, COND_AL, ARITH_AND, TCG_REG_R0, TCG_REG_R0, addrlo, + SHIFT_IMM_LSR(s->page_bits - CPU_TLB_ENTRY_BITS)); + + /* + * Add the tlb_table pointer, creating the CPUTLBEntry address in R1. + * Load the tlb comparator into R2/R3 and the fast path addend into R1. + */ + QEMU_BUILD_BUG_ON(HOST_BIG_ENDIAN); + if (cmp_off == 0) { + if (s->addr_type == TCG_TYPE_I32) { + tcg_out_ld32_rwb(s, COND_AL, TCG_REG_R2, + TCG_REG_R1, TCG_REG_R0); + } else { + tcg_out_ldrd_rwb(s, COND_AL, TCG_REG_R2, + TCG_REG_R1, TCG_REG_R0); + } + } else { + tcg_out_dat_reg(s, COND_AL, ARITH_ADD, + TCG_REG_R1, TCG_REG_R1, TCG_REG_R0, 0); + if (s->addr_type == TCG_TYPE_I32) { + tcg_out_ld32_12(s, COND_AL, TCG_REG_R2, TCG_REG_R1, cmp_off); + } else { + tcg_out_ldrd_8(s, COND_AL, TCG_REG_R2, TCG_REG_R1, cmp_off); + } + } + + /* Load the tlb addend. */ + tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R1, + offsetof(CPUTLBEntry, addend)); + + /* + * Check alignment, check comparators. + * Do this in 2-4 insns. Use MOVW for v7, if possible, + * to reduce the number of sequential conditional instructions. + * Almost all guests have at least 4k pages, which means that we need + * to clear at least 9 bits even for an 8-byte memory, which means it + * isn't worth checking for an immediate operand for BIC. + * + * For unaligned accesses, test the page of the last unit of alignment. + * This leaves the least significant alignment bits unchanged, and of + * course must be zero. + */ + t_addr = addrlo; + if (a_mask < s_mask) { + t_addr = TCG_REG_R0; + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, t_addr, + addrlo, s_mask - a_mask); + } + if (use_armv7_instructions && s->page_bits <= 16) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, ~(s->page_mask | a_mask)); + tcg_out_dat_reg(s, COND_AL, ARITH_BIC, TCG_REG_TMP, + t_addr, TCG_REG_TMP, 0); + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, + TCG_REG_R2, TCG_REG_TMP, 0); + } else { + if (a_mask) { + tcg_debug_assert(a_mask <= 0xff); + tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, addrlo, a_mask); + } + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_TMP, 0, t_addr, + SHIFT_IMM_LSR(s->page_bits)); + tcg_out_dat_reg(s, (a_mask ? COND_EQ : COND_AL), ARITH_CMP, + 0, TCG_REG_R2, TCG_REG_TMP, + SHIFT_IMM_LSL(s->page_bits)); + } + + if (s->addr_type != TCG_TYPE_I32) { + tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, TCG_REG_R3, addrhi, 0); + } + } else if (a_mask) { + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addrlo; + ldst->addrhi_reg = addrhi; + + /* We are expecting alignment to max out at 7 */ + tcg_debug_assert(a_mask <= 0xff); + /* tst addr, #mask */ + tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, addrlo, a_mask); + } + + return ldst; } -static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +static void tcg_out_qemu_ld_direct(TCGContext *s, MemOp opc, TCGReg datalo, + TCGReg datahi, HostAddress h) { - return tcg_out_fail_alignment(s, l); -} + TCGReg base; -static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - return tcg_out_fail_alignment(s, l); -} -#endif /* SOFTMMU */ - -static void tcg_out_qemu_ld_index(TCGContext *s, MemOp opc, - TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addend, - bool scratch_addend) -{ /* Byte swapping is left to middle-end expansion. */ tcg_debug_assert((opc & MO_BSWAP) == 0); switch (opc & MO_SSIZE) { case MO_UB: - tcg_out_ld8_r(s, COND_AL, datalo, addrlo, addend); + if (h.index < 0) { + tcg_out_ld8_12(s, h.cond, datalo, h.base, 0); + } else { + tcg_out_ld8_r(s, h.cond, datalo, h.base, h.index); + } break; case MO_SB: - tcg_out_ld8s_r(s, COND_AL, datalo, addrlo, addend); + if (h.index < 0) { + tcg_out_ld8s_8(s, h.cond, datalo, h.base, 0); + } else { + tcg_out_ld8s_r(s, h.cond, datalo, h.base, h.index); + } break; case MO_UW: - tcg_out_ld16u_r(s, COND_AL, datalo, addrlo, addend); + if (h.index < 0) { + tcg_out_ld16u_8(s, h.cond, datalo, h.base, 0); + } else { + tcg_out_ld16u_r(s, h.cond, datalo, h.base, h.index); + } break; case MO_SW: - tcg_out_ld16s_r(s, COND_AL, datalo, addrlo, addend); + if (h.index < 0) { + tcg_out_ld16s_8(s, h.cond, datalo, h.base, 0); + } else { + tcg_out_ld16s_r(s, h.cond, datalo, h.base, h.index); + } break; case MO_UL: - tcg_out_ld32_r(s, COND_AL, datalo, addrlo, addend); + if (h.index < 0) { + tcg_out_ld32_12(s, h.cond, datalo, h.base, 0); + } else { + tcg_out_ld32_r(s, h.cond, datalo, h.base, h.index); + } break; case MO_UQ: + /* We used pair allocation for datalo, so already should be aligned. */ + tcg_debug_assert((datalo & 1) == 0); + tcg_debug_assert(datahi == datalo + 1); /* LDRD requires alignment; double-check that. */ - if (get_alignment_bits(opc) >= MO_64 - && (datalo & 1) == 0 && datahi == datalo + 1) { + if (get_alignment_bits(opc) >= MO_64) { + if (h.index < 0) { + tcg_out_ldrd_8(s, h.cond, datalo, h.base, 0); + break; + } /* * Rm (the second address op) must not overlap Rt or Rt + 1. * Since datalo is aligned, we can simplify the test via alignment. * Flip the two address arguments if that works. */ - if ((addend & ~1) != datalo) { - tcg_out_ldrd_r(s, COND_AL, datalo, addrlo, addend); + if ((h.index & ~1) != datalo) { + tcg_out_ldrd_r(s, h.cond, datalo, h.base, h.index); break; } - if ((addrlo & ~1) != datalo) { - tcg_out_ldrd_r(s, COND_AL, datalo, addend, addrlo); + if ((h.base & ~1) != datalo) { + tcg_out_ldrd_r(s, h.cond, datalo, h.index, h.base); break; } } - if (scratch_addend) { - tcg_out_ld32_rwb(s, COND_AL, datalo, addend, addrlo); - tcg_out_ld32_12(s, COND_AL, datahi, addend, 4); + if (h.index < 0) { + base = h.base; + if (datalo == h.base) { + tcg_out_mov_reg(s, h.cond, TCG_REG_TMP, base); + base = TCG_REG_TMP; + } + } else if (h.index_scratch) { + tcg_out_ld32_rwb(s, h.cond, datalo, h.index, h.base); + tcg_out_ld32_12(s, h.cond, datahi, h.index, 4); + break; } else { - tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_TMP, - addend, addrlo, SHIFT_IMM_LSL(0)); - tcg_out_ld32_12(s, COND_AL, datalo, TCG_REG_TMP, 0); - tcg_out_ld32_12(s, COND_AL, datahi, TCG_REG_TMP, 4); + tcg_out_dat_reg(s, h.cond, ARITH_ADD, TCG_REG_TMP, + h.base, h.index, SHIFT_IMM_LSL(0)); + base = TCG_REG_TMP; } + tcg_out_ld32_12(s, h.cond, datalo, base, 0); + tcg_out_ld32_12(s, h.cond, datahi, base, 4); break; default: g_assert_not_reached(); } } -#ifndef CONFIG_SOFTMMU -static void tcg_out_qemu_ld_direct(TCGContext *s, MemOp opc, TCGReg datalo, - TCGReg datahi, TCGReg addrlo) +static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg addrlo, TCGReg addrhi, + MemOpIdx oi, TCGType data_type) { - /* Byte swapping is left to middle-end expansion. */ - tcg_debug_assert((opc & MO_BSWAP) == 0); + MemOp opc = get_memop(oi); + TCGLabelQemuLdst *ldst; + HostAddress h; - switch (opc & MO_SSIZE) { - case MO_UB: - tcg_out_ld8_12(s, COND_AL, datalo, addrlo, 0); - break; - case MO_SB: - tcg_out_ld8s_8(s, COND_AL, datalo, addrlo, 0); - break; - case MO_UW: - tcg_out_ld16u_8(s, COND_AL, datalo, addrlo, 0); - break; - case MO_SW: - tcg_out_ld16s_8(s, COND_AL, datalo, addrlo, 0); - break; - case MO_UL: - tcg_out_ld32_12(s, COND_AL, datalo, addrlo, 0); - break; - case MO_UQ: - /* LDRD requires alignment; double-check that. */ - if (get_alignment_bits(opc) >= MO_64 - && (datalo & 1) == 0 && datahi == datalo + 1) { - tcg_out_ldrd_8(s, COND_AL, datalo, addrlo, 0); - } else if (datalo == addrlo) { - tcg_out_ld32_12(s, COND_AL, datahi, addrlo, 4); - tcg_out_ld32_12(s, COND_AL, datalo, addrlo, 0); - } else { - tcg_out_ld32_12(s, COND_AL, datalo, addrlo, 0); - tcg_out_ld32_12(s, COND_AL, datahi, addrlo, 4); - } - break; - default: - g_assert_not_reached(); - } -} -#endif + ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, true); + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = datalo; + ldst->datahi_reg = datahi; -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) -{ - TCGReg addrlo, datalo, datahi, addrhi __attribute__((unused)); - MemOpIdx oi; - MemOp opc; -#ifdef CONFIG_SOFTMMU - int mem_index; - TCGReg addend; - tcg_insn_unit *label_ptr; -#else - unsigned a_bits; -#endif + /* + * This a conditional BL only to load a pointer within this + * opcode into LR for the slow path. We will not be using + * the value for a tail call. + */ + ldst->label_ptr[0] = s->code_ptr; + tcg_out_bl_imm(s, COND_NE, 0); - datalo = *args++; - datahi = (is64 ? *args++ : 0); - addrlo = *args++; - addrhi = (TARGET_LONG_BITS == 64 ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); - -#ifdef CONFIG_SOFTMMU - mem_index = get_mmuidx(oi); - addend = tcg_out_tlb_read(s, addrlo, addrhi, opc, mem_index, 1); - - /* This a conditional BL only to load a pointer within this opcode into LR - for the slow path. We will not be using the value for a tail call. */ - label_ptr = s->code_ptr; - tcg_out_bl_imm(s, COND_NE, 0); - - tcg_out_qemu_ld_index(s, opc, datalo, datahi, addrlo, addend, true); - - add_qemu_ldst_label(s, true, oi, datalo, datahi, addrlo, addrhi, - s->code_ptr, label_ptr); -#else /* !CONFIG_SOFTMMU */ - a_bits = get_alignment_bits(opc); - if (a_bits) { - tcg_out_test_alignment(s, true, addrlo, addrhi, a_bits); - } - if (guest_base) { - tcg_out_qemu_ld_index(s, opc, datalo, datahi, - addrlo, TCG_REG_GUEST_BASE, false); + tcg_out_qemu_ld_direct(s, opc, datalo, datahi, h); + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } else { - tcg_out_qemu_ld_direct(s, opc, datalo, datahi, addrlo); - } -#endif -} - -static void tcg_out_qemu_st_index(TCGContext *s, ARMCond cond, MemOp opc, - TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addend, - bool scratch_addend) -{ - /* Byte swapping is left to middle-end expansion. */ - tcg_debug_assert((opc & MO_BSWAP) == 0); - - switch (opc & MO_SIZE) { - case MO_8: - tcg_out_st8_r(s, cond, datalo, addrlo, addend); - break; - case MO_16: - tcg_out_st16_r(s, cond, datalo, addrlo, addend); - break; - case MO_32: - tcg_out_st32_r(s, cond, datalo, addrlo, addend); - break; - case MO_64: - /* STRD requires alignment; double-check that. */ - if (get_alignment_bits(opc) >= MO_64 - && (datalo & 1) == 0 && datahi == datalo + 1) { - tcg_out_strd_r(s, cond, datalo, addrlo, addend); - } else if (scratch_addend) { - tcg_out_st32_rwb(s, cond, datalo, addend, addrlo); - tcg_out_st32_12(s, cond, datahi, addend, 4); - } else { - tcg_out_dat_reg(s, cond, ARITH_ADD, TCG_REG_TMP, - addend, addrlo, SHIFT_IMM_LSL(0)); - tcg_out_st32_12(s, cond, datalo, TCG_REG_TMP, 0); - tcg_out_st32_12(s, cond, datahi, TCG_REG_TMP, 4); - } - break; - default: - g_assert_not_reached(); + tcg_out_qemu_ld_direct(s, opc, datalo, datahi, h); } } -#ifndef CONFIG_SOFTMMU static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg datalo, - TCGReg datahi, TCGReg addrlo) + TCGReg datahi, HostAddress h) { /* Byte swapping is left to middle-end expansion. */ tcg_debug_assert((opc & MO_BSWAP) == 0); switch (opc & MO_SIZE) { case MO_8: - tcg_out_st8_12(s, COND_AL, datalo, addrlo, 0); + if (h.index < 0) { + tcg_out_st8_12(s, h.cond, datalo, h.base, 0); + } else { + tcg_out_st8_r(s, h.cond, datalo, h.base, h.index); + } break; case MO_16: - tcg_out_st16_8(s, COND_AL, datalo, addrlo, 0); + if (h.index < 0) { + tcg_out_st16_8(s, h.cond, datalo, h.base, 0); + } else { + tcg_out_st16_r(s, h.cond, datalo, h.base, h.index); + } break; case MO_32: - tcg_out_st32_12(s, COND_AL, datalo, addrlo, 0); + if (h.index < 0) { + tcg_out_st32_12(s, h.cond, datalo, h.base, 0); + } else { + tcg_out_st32_r(s, h.cond, datalo, h.base, h.index); + } break; case MO_64: + /* We used pair allocation for datalo, so already should be aligned. */ + tcg_debug_assert((datalo & 1) == 0); + tcg_debug_assert(datahi == datalo + 1); /* STRD requires alignment; double-check that. */ - if (get_alignment_bits(opc) >= MO_64 - && (datalo & 1) == 0 && datahi == datalo + 1) { - tcg_out_strd_8(s, COND_AL, datalo, addrlo, 0); + if (get_alignment_bits(opc) >= MO_64) { + if (h.index < 0) { + tcg_out_strd_8(s, h.cond, datalo, h.base, 0); + } else { + tcg_out_strd_r(s, h.cond, datalo, h.base, h.index); + } + } else if (h.index < 0) { + tcg_out_st32_12(s, h.cond, datalo, h.base, 0); + tcg_out_st32_12(s, h.cond, datahi, h.base, 4); + } else if (h.index_scratch) { + tcg_out_st32_rwb(s, h.cond, datalo, h.index, h.base); + tcg_out_st32_12(s, h.cond, datahi, h.index, 4); } else { - tcg_out_st32_12(s, COND_AL, datalo, addrlo, 0); - tcg_out_st32_12(s, COND_AL, datahi, addrlo, 4); + tcg_out_dat_reg(s, h.cond, ARITH_ADD, TCG_REG_TMP, + h.base, h.index, SHIFT_IMM_LSL(0)); + tcg_out_st32_12(s, h.cond, datalo, TCG_REG_TMP, 0); + tcg_out_st32_12(s, h.cond, datahi, TCG_REG_TMP, 4); } break; default: g_assert_not_reached(); } } -#endif -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) +static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg addrlo, TCGReg addrhi, + MemOpIdx oi, TCGType data_type) { - TCGReg addrlo, datalo, datahi, addrhi __attribute__((unused)); - MemOpIdx oi; - MemOp opc; -#ifdef CONFIG_SOFTMMU - int mem_index; - TCGReg addend; - tcg_insn_unit *label_ptr; -#else - unsigned a_bits; -#endif + MemOp opc = get_memop(oi); + TCGLabelQemuLdst *ldst; + HostAddress h; - datalo = *args++; - datahi = (is64 ? *args++ : 0); - addrlo = *args++; - addrhi = (TARGET_LONG_BITS == 64 ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); + ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, false); + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = datalo; + ldst->datahi_reg = datahi; -#ifdef CONFIG_SOFTMMU - mem_index = get_mmuidx(oi); - addend = tcg_out_tlb_read(s, addrlo, addrhi, opc, mem_index, 0); + h.cond = COND_EQ; + tcg_out_qemu_st_direct(s, opc, datalo, datahi, h); - tcg_out_qemu_st_index(s, COND_EQ, opc, datalo, datahi, - addrlo, addend, true); - - /* The conditional call must come last, as we're going to return here. */ - label_ptr = s->code_ptr; - tcg_out_bl_imm(s, COND_NE, 0); - - add_qemu_ldst_label(s, false, oi, datalo, datahi, addrlo, addrhi, - s->code_ptr, label_ptr); -#else /* !CONFIG_SOFTMMU */ - a_bits = get_alignment_bits(opc); - if (a_bits) { - tcg_out_test_alignment(s, false, addrlo, addrhi, a_bits); - } - if (guest_base) { - tcg_out_qemu_st_index(s, COND_AL, opc, datalo, datahi, - addrlo, TCG_REG_GUEST_BASE, false); + /* The conditional call is last, as we're going to return here. */ + ldst->label_ptr[0] = s->code_ptr; + tcg_out_bl_imm(s, COND_NE, 0); + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } else { - tcg_out_qemu_st_direct(s, opc, datalo, datahi, addrlo); + tcg_out_qemu_st_direct(s, opc, datalo, datahi, h); } -#endif } static void tcg_out_epilogue(TCGContext *s); +static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg) +{ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R0, arg); + tcg_out_epilogue(s); +} + +static void tcg_out_goto_tb(TCGContext *s, int which) +{ + uintptr_t i_addr; + intptr_t i_disp; + + /* Direct branch will be patched by tb_target_set_jmp_target. */ + set_jmp_insn_offset(s, which); + tcg_out32(s, INSN_NOP); + + /* When branch is out of range, fall through to indirect. */ + i_addr = get_jmp_target_addr(s, which); + i_disp = tcg_pcrel_diff(s, (void *)i_addr) - 8; + tcg_debug_assert(i_disp < 0); + if (i_disp >= -0xfff) { + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, i_disp); + } else { + /* + * The TB is close, but outside the 12 bits addressable by + * the load. We can extend this to 20 bits with a sub of a + * shifted immediate from pc. + */ + int h = -i_disp; + int l = -(h & 0xfff); + + h = encode_imm_nofail(h + l); + tcg_out_dat_imm(s, COND_AL, ARITH_SUB, TCG_REG_R0, TCG_REG_PC, h); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_R0, l); + } + set_jmp_reset_offset(s, which); +} + +void tb_target_set_jmp_target(const TranslationBlock *tb, int n, + uintptr_t jmp_rx, uintptr_t jmp_rw) +{ + uintptr_t addr = tb->jmp_target_addr[n]; + ptrdiff_t offset = addr - (jmp_rx + 8); + tcg_insn_unit insn; + + /* Either directly branch, or fall through to indirect branch. */ + if (offset == sextract64(offset, 0, 26)) { + /* B */ + insn = deposit32((COND_AL << 28) | INSN_B, 0, 24, offset >> 2); + } else { + insn = INSN_NOP; + } + + qatomic_set((uint32_t *)jmp_rw, insn); + flush_idcache_range(jmp_rx, jmp_rw, 4); +} + static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1935,33 +1807,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, int c; switch (opc) { - case INDEX_op_exit_tb: - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R0, args[0]); - tcg_out_epilogue(s); - break; - case INDEX_op_goto_tb: - { - /* Indirect jump method */ - intptr_t ptr, dif, dil; - TCGReg base = TCG_REG_PC; - - tcg_debug_assert(s->tb_jmp_insn_offset == 0); - ptr = (intptr_t)tcg_splitwx_to_rx(s->tb_jmp_target_addr + args[0]); - dif = tcg_pcrel_diff(s, (void *)ptr) - 8; - dil = sextract32(dif, 0, 12); - if (dif != dil) { - /* The TB is close, but outside the 12 bits addressable by - the load. We can extend this to 20 bits with a sub of a - shifted immediate from pc. In the vastly unlikely event - the code requires more than 1MB, we'll use 2 insns and - be no worse off. */ - base = TCG_REG_R0; - tcg_out_movi32(s, COND_AL, base, ptr - dil); - } - tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, base, dil); - set_jmp_reset_offset(s, args[0]); - } - break; case INDEX_op_goto_ptr: tcg_out_b_reg(s, COND_AL, args[0]); break; @@ -1998,9 +1843,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, /* Constraints mean that v2 is always in the same register as dest, * so we only need to do "if condition passed, move v1 to dest". */ - tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, - args[1], args[2], const_args[2]); - tcg_out_dat_rIK(s, tcg_cond_to_arm_cond[args[5]], ARITH_MOV, + c = tcg_out_cmp(s, args[5], args[1], args[2], const_args[2]); + tcg_out_dat_rIK(s, tcg_cond_to_arm_cond[c], ARITH_MOV, ARITH_MVN, args[0], 0, args[3], const_args[3]); break; case INDEX_op_add_i32: @@ -2150,17 +1994,21 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_brcond_i32: - tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, - args[0], args[1], const_args[1]); - tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[2]], - arg_label(args[3])); + c = tcg_out_cmp(s, args[2], args[0], args[1], const_args[1]); + tcg_out_goto_label(s, tcg_cond_to_arm_cond[c], arg_label(args[3])); break; case INDEX_op_setcond_i32: - tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, - args[1], args[2], const_args[2]); - tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[3]], + c = tcg_out_cmp(s, args[3], args[1], args[2], const_args[2]); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[c], ARITH_MOV, args[0], 0, 1); - tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[3])], + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(c)], + ARITH_MOV, args[0], 0, 0); + break; + case INDEX_op_negsetcond_i32: + c = tcg_out_cmp(s, args[3], args[1], args[2], const_args[2]); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[c], + ARITH_MVN, args[0], 0, 0); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(c)], ARITH_MOV, args[0], 0, 0); break; @@ -2175,17 +2023,36 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, ARITH_MOV, args[0], 0, 0); break; - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args, 0); + case INDEX_op_qemu_ld_a32_i32: + tcg_out_qemu_ld(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I32); break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args, 1); + case INDEX_op_qemu_ld_a64_i32: + tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], + args[3], TCG_TYPE_I32); break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args, 0); + case INDEX_op_qemu_ld_a32_i64: + tcg_out_qemu_ld(s, args[0], args[1], args[2], -1, + args[3], TCG_TYPE_I64); break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args, 1); + case INDEX_op_qemu_ld_a64_i64: + tcg_out_qemu_ld(s, args[0], args[1], args[2], args[3], + args[4], TCG_TYPE_I64); + break; + + case INDEX_op_qemu_st_a32_i32: + tcg_out_qemu_st(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I32); + break; + case INDEX_op_qemu_st_a64_i32: + tcg_out_qemu_st(s, args[0], -1, args[1], args[2], + args[3], TCG_TYPE_I32); + break; + case INDEX_op_qemu_st_a32_i64: + tcg_out_qemu_st(s, args[0], args[1], args[2], -1, + args[3], TCG_TYPE_I64); + break; + case INDEX_op_qemu_st_a64_i64: + tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], + args[4], TCG_TYPE_I64); break; case INDEX_op_bswap16_i32: @@ -2195,16 +2062,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_bswap32(s, COND_AL, args[0], args[1]); break; - case INDEX_op_ext8s_i32: - tcg_out_ext8s(s, COND_AL, args[0], args[1]); - break; - case INDEX_op_ext16s_i32: - tcg_out_ext16s(s, COND_AL, args[0], args[1]); - break; - case INDEX_op_ext16u_i32: - tcg_out_ext16u(s, COND_AL, args[0], args[1]); - break; - case INDEX_op_deposit_i32: tcg_out_deposit(s, COND_AL, args[0], args[2], args[3], args[4], const_args[2]); @@ -2250,8 +2107,14 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ case INDEX_op_call: /* Always emitted via tcg_out_call. */ + case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ + case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ + case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ + case INDEX_op_ext8u_i32: + case INDEX_op_ext16s_i32: + case INDEX_op_ext16u_i32: default: - tcg_abort(); + g_assert_not_reached(); } } @@ -2285,6 +2148,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_add_i32: case INDEX_op_sub_i32: case INDEX_op_setcond_i32: + case INDEX_op_negsetcond_i32: return C_O1_I2(r, r, rIN); case INDEX_op_and_i32: @@ -2330,14 +2194,22 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_setcond2_i32: return C_O1_I4(r, r, r, rI, rI); - case INDEX_op_qemu_ld_i32: - return TARGET_LONG_BITS == 32 ? C_O1_I1(r, l) : C_O1_I2(r, l, l); - case INDEX_op_qemu_ld_i64: - return TARGET_LONG_BITS == 32 ? C_O2_I1(r, r, l) : C_O2_I2(r, r, l, l); - case INDEX_op_qemu_st_i32: - return TARGET_LONG_BITS == 32 ? C_O0_I2(s, s) : C_O0_I3(s, s, s); - case INDEX_op_qemu_st_i64: - return TARGET_LONG_BITS == 32 ? C_O0_I3(s, s, s) : C_O0_I4(s, s, s, s); + case INDEX_op_qemu_ld_a32_i32: + return C_O1_I1(r, q); + case INDEX_op_qemu_ld_a64_i32: + return C_O1_I2(r, q, q); + case INDEX_op_qemu_ld_a32_i64: + return C_O2_I1(e, p, q); + case INDEX_op_qemu_ld_a64_i64: + return C_O2_I2(e, p, q, q); + case INDEX_op_qemu_st_a32_i32: + return C_O0_I2(q, q); + case INDEX_op_qemu_st_a64_i32: + return C_O0_I3(q, q, q); + case INDEX_op_qemu_st_a32_i64: + return C_O0_I3(Q, p, q); + case INDEX_op_qemu_st_a64_i64: + return C_O0_I4(Q, p, q, q); case INDEX_op_st_vec: return C_O0_I2(w, r); @@ -2534,6 +2406,31 @@ static void tcg_out_movi(TCGContext *s, TCGType type, tcg_out_movi32(s, COND_AL, ret, arg); } +static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2) +{ + return false; +} + +static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs, + tcg_target_long imm) +{ + int enc, opc = ARITH_ADD; + + /* All of the easiest immediates to encode are positive. */ + if (imm < 0) { + imm = -imm; + opc = ARITH_SUB; + } + enc = encode_imm(imm); + if (enc >= 0) { + tcg_out_dat_imm(s, COND_AL, opc, rd, rs, enc); + } else { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, imm); + tcg_out_dat_reg(s, COND_AL, opc, rd, rs, + TCG_REG_TMP, SHIFT_IMM_LSL(0)); + } +} + /* Type is always V128, with I64 elements. */ static void tcg_out_dup2_vec(TCGContext *s, TCGReg rd, TCGReg rl, TCGReg rh) { @@ -3060,12 +2957,10 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); -#ifndef CONFIG_SOFTMMU - if (guest_base) { + if (!tcg_use_softmmu && guest_base) { tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_GUEST_BASE, guest_base); tcg_regset_set_reg(s->reserved_regs, TCG_REG_GUEST_BASE); } -#endif tcg_out_b_reg(s, COND_AL, tcg_target_call_iarg_regs[1]); @@ -3091,6 +2986,11 @@ static void tcg_out_epilogue(TCGContext *s) (1 << TCG_REG_R10) | (1 << TCG_REG_R11) | (1 << TCG_REG_PC)); } +static void tcg_out_tb_start(TCGContext *s) +{ + /* nothing to do */ +} + typedef struct { DebugFrameHeader h; uint8_t fde_def_cfa[4]; diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 7e96495392..a43875cb09 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -30,9 +30,7 @@ extern int arm_arch; #define use_armv7_instructions (__ARM_ARCH >= 7 || arm_arch >= 7) -#undef TCG_TARGET_STACK_GROWSUP #define TCG_TARGET_INSN_UNIT_SIZE 4 -#define TCG_TARGET_TLB_DISPLACEMENT_BITS 16 #define MAX_CODE_GEN_BUFFER_SIZE UINT32_MAX typedef enum { @@ -89,8 +87,11 @@ extern bool use_neon_instructions; /* used for function call generation */ #define TCG_TARGET_STACK_ALIGN 8 -#define TCG_TARGET_CALL_ALIGN_ARGS 1 #define TCG_TARGET_CALL_STACK_OFFSET 0 +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_EVEN +#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF /* optional instructions */ #define TCG_TARGET_HAS_ext8s_i32 1 @@ -100,7 +101,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 0 @@ -114,16 +114,19 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_extract_i32 use_armv7_instructions #define TCG_TARGET_HAS_sextract_i32 use_armv7_instructions #define TCG_TARGET_HAS_extract2_i32 1 -#define TCG_TARGET_HAS_movcond_i32 1 +#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_muls2_i32 1 #define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_div_i32 use_idiv_instructions #define TCG_TARGET_HAS_rem_i32 0 -#define TCG_TARGET_HAS_direct_jump 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 +#define TCG_TARGET_HAS_qemu_ldst_i128 0 + +#define TCG_TARGET_HAS_tst 1 + #define TCG_TARGET_HAS_v64 use_neon_instructions #define TCG_TARGET_HAS_v128 use_neon_instructions #define TCG_TARGET_HAS_v256 0 @@ -149,11 +152,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_cmpsel_vec 0 #define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_HAS_MEMORY_BSWAP 0 - -/* not defined -- call should be eliminated at compile time */ -void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t, uintptr_t); - #define TCG_TARGET_NEED_LDST_LABELS #define TCG_TARGET_NEED_POOL_LABELS diff --git a/tcg/debuginfo.c b/tcg/debuginfo.c new file mode 100644 index 0000000000..3753f7ef67 --- /dev/null +++ b/tcg/debuginfo.c @@ -0,0 +1,95 @@ +/* + * Debug information support. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/lockable.h" +#include "tcg/debuginfo.h" + +#include + +static QemuMutex lock; +static Dwfl *dwfl; +static const Dwfl_Callbacks dwfl_callbacks = { + .find_elf = NULL, + .find_debuginfo = dwfl_standard_find_debuginfo, + .section_address = NULL, + .debuginfo_path = NULL, +}; + +__attribute__((constructor)) +static void debuginfo_init(void) +{ + qemu_mutex_init(&lock); +} + +void debuginfo_report_elf(const char *name, int fd, uint64_t bias) +{ + QEMU_LOCK_GUARD(&lock); + + if (dwfl) { + dwfl_report_begin_add(dwfl); + } else { + dwfl = dwfl_begin(&dwfl_callbacks); + } + + if (dwfl) { + dwfl_report_elf(dwfl, name, name, fd, bias, true); + dwfl_report_end(dwfl, NULL, NULL); + } +} + +void debuginfo_lock(void) +{ + qemu_mutex_lock(&lock); +} + +void debuginfo_query(struct debuginfo_query *q, size_t n) +{ + const char *symbol, *file; + Dwfl_Module *dwfl_module; + Dwfl_Line *dwfl_line; + GElf_Off dwfl_offset; + GElf_Sym dwfl_sym; + size_t i; + int line; + + if (!dwfl) { + return; + } + + for (i = 0; i < n; i++) { + dwfl_module = dwfl_addrmodule(dwfl, q[i].address); + if (!dwfl_module) { + continue; + } + + if (q[i].flags & DEBUGINFO_SYMBOL) { + symbol = dwfl_module_addrinfo(dwfl_module, q[i].address, + &dwfl_offset, &dwfl_sym, + NULL, NULL, NULL); + if (symbol) { + q[i].symbol = symbol; + q[i].offset = dwfl_offset; + } + } + + if (q[i].flags & DEBUGINFO_LINE) { + dwfl_line = dwfl_module_getsrc(dwfl_module, q[i].address); + if (dwfl_line) { + file = dwfl_lineinfo(dwfl_line, NULL, &line, 0, NULL, NULL); + if (file) { + q[i].file = file; + q[i].line = line; + } + } + } + } +} + +void debuginfo_unlock(void) +{ + qemu_mutex_unlock(&lock); +} diff --git a/tcg/i386/tcg-target-con-set.h b/tcg/i386/tcg-target-con-set.h index 91ceb0e1da..e24241cfa2 100644 --- a/tcg/i386/tcg-target-con-set.h +++ b/tcg/i386/tcg-target-con-set.h @@ -11,13 +11,16 @@ * * C_N1_Im(...) defines a constraint set with 1 output and inputs, * except that the output must use a new register. + * + * C_Nn_Om_Ik(...) defines a constraint set with outputs and + * inputs, except that the first outputs must use new registers. */ C_O0_I1(r) C_O0_I2(L, L) C_O0_I2(qi, r) C_O0_I2(re, r) C_O0_I2(ri, r) -C_O0_I2(r, re) +C_O0_I2(r, reT) C_O0_I2(s, L) C_O0_I2(x, r) C_O0_I3(L, L, L) @@ -30,8 +33,8 @@ C_O1_I1(r, q) C_O1_I1(r, r) C_O1_I1(x, r) C_O1_I1(x, x) -C_O1_I2(Q, 0, Q) -C_O1_I2(q, r, re) +C_O1_I2(q, 0, qi) +C_O1_I2(q, r, reT) C_O1_I2(r, 0, ci) C_O1_I2(r, 0, r) C_O1_I2(r, 0, re) @@ -47,10 +50,10 @@ C_N1_I2(r, r, r) C_N1_I2(r, r, rW) C_O1_I3(x, 0, x, x) C_O1_I3(x, x, x, x) -C_O1_I4(r, r, re, r, 0) +C_O1_I4(r, r, reT, r, 0) C_O1_I4(r, r, r, ri, ri) C_O2_I1(r, r, L) C_O2_I2(a, d, a, r) C_O2_I2(r, r, L, L) C_O2_I3(a, d, 0, 1, r) -C_O2_I4(r, r, 0, 1, re, re) +C_N1_O1_I4(r, r, 0, 1, re, re) diff --git a/tcg/i386/tcg-target-con-str.h b/tcg/i386/tcg-target-con-str.h index 24e6bcb80d..cc22db227b 100644 --- a/tcg/i386/tcg-target-con-str.h +++ b/tcg/i386/tcg-target-con-str.h @@ -19,7 +19,6 @@ REGS('D', 1u << TCG_REG_EDI) REGS('r', ALL_GENERAL_REGS) REGS('x', ALL_VECTOR_REGS) REGS('q', ALL_BYTEL_REGS) /* regs that can be used as a byte operand */ -REGS('Q', ALL_BYTEH_REGS) /* regs with a second byte (e.g. %ah) */ REGS('L', ALL_GENERAL_REGS & ~SOFTMMU_RESERVE_REGS) /* qemu_ld/st */ REGS('s', ALL_BYTEL_REGS & ~SOFTMMU_RESERVE_REGS) /* qemu_st8_i32 data */ @@ -29,5 +28,6 @@ REGS('s', ALL_BYTEL_REGS & ~SOFTMMU_RESERVE_REGS) /* qemu_st8_i32 data */ */ CONST('e', TCG_CT_CONST_S32) CONST('I', TCG_CT_CONST_I32) +CONST('T', TCG_CT_CONST_TST) CONST('W', TCG_CT_CONST_WSZ) CONST('Z', TCG_CT_CONST_U32) diff --git a/tcg/i386/tcg-target-reg-bits.h b/tcg/i386/tcg-target-reg-bits.h new file mode 100644 index 0000000000..aa386050eb --- /dev/null +++ b/tcg/i386/tcg-target-reg-bits.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +#ifdef __x86_64__ +# define TCG_TARGET_REG_BITS 64 +#else +# define TCG_TARGET_REG_BITS 32 +#endif + +#endif diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index d52206ba4d..c6ba498623 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -91,6 +91,8 @@ static const int tcg_target_reg_alloc_order[] = { #endif }; +#define TCG_TMP_VEC TCG_REG_XMM5 + static const int tcg_target_call_iarg_regs[] = { #if TCG_TARGET_REG_BITS == 64 #if defined(_WIN64) @@ -109,18 +111,28 @@ static const int tcg_target_call_iarg_regs[] = { #endif }; -static const int tcg_target_call_oarg_regs[] = { - TCG_REG_EAX, -#if TCG_TARGET_REG_BITS == 32 - TCG_REG_EDX +static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) +{ + switch (kind) { + case TCG_CALL_RET_NORMAL: + tcg_debug_assert(slot >= 0 && slot <= 1); + return slot ? TCG_REG_EDX : TCG_REG_EAX; +#ifdef _WIN64 + case TCG_CALL_RET_BY_VEC: + tcg_debug_assert(slot == 0); + return TCG_REG_XMM0; #endif -}; + default: + g_assert_not_reached(); + } +} /* Constants we accept. */ #define TCG_CT_CONST_S32 0x100 #define TCG_CT_CONST_U32 0x200 #define TCG_CT_CONST_I32 0x400 #define TCG_CT_CONST_WSZ 0x800 +#define TCG_CT_CONST_TST 0x1000 /* Registers used with L constraint, which are the first argument registers on x86_64, and two random call clobbered registers on @@ -133,7 +145,6 @@ static const int tcg_target_call_oarg_regs[] = { # define TCG_REG_L1 TCG_REG_EDX #endif -#define ALL_BYTEH_REGS 0x0000000fu #if TCG_TARGET_REG_BITS == 64 # define ALL_GENERAL_REGS 0x0000ffffu # define ALL_VECTOR_REGS 0xffff0000u @@ -141,49 +152,19 @@ static const int tcg_target_call_oarg_regs[] = { #else # define ALL_GENERAL_REGS 0x000000ffu # define ALL_VECTOR_REGS 0x00ff0000u -# define ALL_BYTEL_REGS ALL_BYTEH_REGS -#endif -#ifdef CONFIG_SOFTMMU -# define SOFTMMU_RESERVE_REGS ((1 << TCG_REG_L0) | (1 << TCG_REG_L1)) -#else -# define SOFTMMU_RESERVE_REGS 0 -#endif - -/* The host compiler should supply to enable runtime features - detection, as we're not going to go so far as our own inline assembly. - If not available, default values will be assumed. */ -#if defined(CONFIG_CPUID_H) -#include "qemu/cpuid.h" +# define ALL_BYTEL_REGS 0x0000000fu #endif +#define SOFTMMU_RESERVE_REGS \ + (tcg_use_softmmu ? (1 << TCG_REG_L0) | (1 << TCG_REG_L1) : 0) /* For 64-bit, we always know that CMOV is available. */ #if TCG_TARGET_REG_BITS == 64 -# define have_cmov 1 -#elif defined(CONFIG_CPUID_H) -static bool have_cmov; +# define have_cmov true #else -# define have_cmov 0 -#endif - -/* We need these symbols in tcg-target.h, and we can't properly conditionalize - it there. Therefore we always define the variable. */ -bool have_bmi1; -bool have_popcnt; -bool have_avx1; -bool have_avx2; -bool have_avx512bw; -bool have_avx512dq; -bool have_avx512vbmi2; -bool have_avx512vl; -bool have_movbe; - -#ifdef CONFIG_CPUID_H -static bool have_bmi2; -static bool have_lzcnt; -#else -# define have_bmi2 0 -# define have_lzcnt 0 +# define have_cmov (cpuinfo & CPUINFO_CMOV) #endif +#define have_bmi2 (cpuinfo & CPUINFO_BMI2) +#define have_lzcnt (cpuinfo & CPUINFO_LZCNT) static const tcg_insn_unit *tb_ret_addr; @@ -209,19 +190,21 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, tcg_patch8(code_ptr, value); break; default: - tcg_abort(); + g_assert_not_reached(); } return true; } /* test if a constant matches the constraint */ -static bool tcg_target_const_match(int64_t val, TCGType type, int ct) +static bool tcg_target_const_match(int64_t val, int ct, + TCGType type, TCGCond cond, int vece) { if (ct & TCG_CT_CONST) { return 1; } if (type == TCG_TYPE_I32) { - if (ct & (TCG_CT_CONST_S32 | TCG_CT_CONST_U32 | TCG_CT_CONST_I32)) { + if (ct & (TCG_CT_CONST_S32 | TCG_CT_CONST_U32 | + TCG_CT_CONST_I32 | TCG_CT_CONST_TST)) { return 1; } } else { @@ -234,6 +217,17 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) if ((ct & TCG_CT_CONST_I32) && ~val == (int32_t)~val) { return 1; } + /* + * This will be used in combination with TCG_CT_CONST_S32, + * so "normal" TESTQ is already matched. Also accept: + * TESTQ -> TESTL (uint32_t) + * TESTQ -> BT (is_power_of_2) + */ + if ((ct & TCG_CT_CONST_TST) + && is_tst_cond(cond) + && (val == (uint32_t)val || is_power_of_2(val))) { + return 1; + } } if ((ct & TCG_CT_CONST_WSZ) && val == (type == TCG_TYPE_I32 ? 32 : 64)) { return 1; @@ -264,6 +258,7 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define P_VEXL 0x80000 /* Set VEX.L = 1 */ #define P_EVEX 0x100000 /* Requires EVEX encoding */ +#define OPC_ARITH_EbIb (0x80) #define OPC_ARITH_EvIz (0x81) #define OPC_ARITH_EvIb (0x83) #define OPC_ARITH_GvEv (0x03) /* ... plus (ARITH_FOO << 3) */ @@ -293,6 +288,7 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define OPC_MOVL_GvEv (0x8b) /* loads, more or less */ #define OPC_MOVB_EvIz (0xc6) #define OPC_MOVL_EvIz (0xc7) +#define OPC_MOVB_Ib (0xb0) #define OPC_MOVL_Iv (0xb8) #define OPC_MOVBE_GyMy (0xf0 | P_EXT38) #define OPC_MOVBE_MyGy (0xf1 | P_EXT38) @@ -337,6 +333,8 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define OPC_PCMPGTW (0x65 | P_EXT | P_DATA16) #define OPC_PCMPGTD (0x66 | P_EXT | P_DATA16) #define OPC_PCMPGTQ (0x37 | P_EXT38 | P_DATA16) +#define OPC_PEXTRD (0x16 | P_EXT3A | P_DATA16) +#define OPC_PINSRD (0x22 | P_EXT3A | P_DATA16) #define OPC_PMAXSB (0x3c | P_EXT38 | P_DATA16) #define OPC_PMAXSW (0xee | P_EXT | P_DATA16) #define OPC_PMAXSD (0x3d | P_EXT38 | P_DATA16) @@ -411,6 +409,7 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define OPC_SHLX (0xf7 | P_EXT38 | P_DATA16) #define OPC_SHRX (0xf7 | P_EXT38 | P_SIMDF2) #define OPC_SHRD_Ib (0xac | P_EXT) +#define OPC_TESTB (0x84) #define OPC_TESTL (0x85) #define OPC_TZCNT (0xbc | P_EXT | P_SIMDF3) #define OPC_UD2 (0x0b | P_EXT) @@ -451,11 +450,18 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define OPC_VPTERNLOGQ (0x25 | P_EXT3A | P_DATA16 | P_VEXW | P_EVEX) #define OPC_VZEROUPPER (0x77 | P_EXT) #define OPC_XCHG_ax_r32 (0x90) +#define OPC_XCHG_EvGv (0x87) #define OPC_GRP3_Eb (0xf6) #define OPC_GRP3_Ev (0xf7) #define OPC_GRP5 (0xff) #define OPC_GRP14 (0x73 | P_EXT | P_DATA16) +#define OPC_GRPBT (0xba | P_EXT) + +#define OPC_GRPBT_BT 4 +#define OPC_GRPBT_BTS 5 +#define OPC_GRPBT_BTR 6 +#define OPC_GRPBT_BTC 7 /* Group 1 opcode extensions for 0x80-0x83. These are also used as modifiers for OPC_ARITH. */ @@ -520,6 +526,8 @@ static const uint8_t tcg_cond_to_jcc[] = { [TCG_COND_GEU] = JCC_JAE, [TCG_COND_LEU] = JCC_JBE, [TCG_COND_GTU] = JCC_JA, + [TCG_COND_TSTEQ] = JCC_JE, + [TCG_COND_TSTNE] = JCC_JNE, }; #if TCG_TARGET_REG_BITS == 64 @@ -608,6 +616,9 @@ static void tcg_out_vex_opc(TCGContext *s, int opc, int r, int v, { int tmp; + if (opc & P_GS) { + tcg_out8(s, 0x65); + } /* Use the two byte form if possible, which cannot encode VEX.W, VEX.B, VEX.X, or an m-mmmm field other than P_EXT. */ if ((opc & (P_EXT | P_EXT38 | P_EXT3A | P_VEXW)) == P_EXT @@ -1069,6 +1080,21 @@ static void tcg_out_movi(TCGContext *s, TCGType type, } } +static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_XCHG_EvGv + rexw, r1, r2); + return true; +} + +static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs, + tcg_target_long imm) +{ + /* This function is only used for passing structs by reference. */ + tcg_debug_assert(imm == (int32_t)imm); + tcg_out_modrm_offset(s, OPC_LEA | P_REXW, rd, rs, imm); +} + static inline void tcg_out_pushi(TCGContext *s, tcg_target_long val) { if (val == (int8_t)val) { @@ -1078,7 +1104,7 @@ static inline void tcg_out_pushi(TCGContext *s, tcg_target_long val) tcg_out_opc(s, OPC_PUSH_Iv, 0, 0, 0); tcg_out32(s, val); } else { - tcg_abort(); + g_assert_not_reached(); } } @@ -1176,9 +1202,16 @@ static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, * The gvec infrastructure is asserts that v128 vector loads * and stores use a 16-byte aligned offset. Validate that the * final pointer is aligned by using an insn that will SIGSEGV. + * + * This specific instance is also used by TCG_CALL_RET_BY_VEC, + * for _WIN64, which must have SSE2 but may not have AVX. */ tcg_debug_assert(arg >= 16); - tcg_out_vex_modrm_offset(s, OPC_MOVDQA_WxVx, arg, 0, arg1, arg2); + if (have_avx1) { + tcg_out_vex_modrm_offset(s, OPC_MOVDQA_WxVx, arg, 0, arg1, arg2); + } else { + tcg_out_modrm_offset(s, OPC_MOVDQA_WxVx, arg, arg1, arg2); + } break; case TCG_TYPE_V256: /* @@ -1235,43 +1268,63 @@ static inline void tcg_out_rolw_8(TCGContext *s, int reg) tcg_out_shifti(s, SHIFT_ROL + P_DATA16, reg, 8); } -static inline void tcg_out_ext8u(TCGContext *s, int dest, int src) +static void tcg_out_ext8u(TCGContext *s, TCGReg dest, TCGReg src) { /* movzbl */ tcg_debug_assert(src < 4 || TCG_TARGET_REG_BITS == 64); tcg_out_modrm(s, OPC_MOVZBL + P_REXB_RM, dest, src); } -static void tcg_out_ext8s(TCGContext *s, int dest, int src, int rexw) +static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) { + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; /* movsbl */ tcg_debug_assert(src < 4 || TCG_TARGET_REG_BITS == 64); tcg_out_modrm(s, OPC_MOVSBL + P_REXB_RM + rexw, dest, src); } -static inline void tcg_out_ext16u(TCGContext *s, int dest, int src) +static void tcg_out_ext16u(TCGContext *s, TCGReg dest, TCGReg src) { /* movzwl */ tcg_out_modrm(s, OPC_MOVZWL, dest, src); } -static inline void tcg_out_ext16s(TCGContext *s, int dest, int src, int rexw) +static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) { + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; /* movsw[lq] */ tcg_out_modrm(s, OPC_MOVSWL + rexw, dest, src); } -static inline void tcg_out_ext32u(TCGContext *s, int dest, int src) +static void tcg_out_ext32u(TCGContext *s, TCGReg dest, TCGReg src) { /* 32-bit mov zero extends. */ tcg_out_modrm(s, OPC_MOVL_GvEv, dest, src); } -static inline void tcg_out_ext32s(TCGContext *s, int dest, int src) +static void tcg_out_ext32s(TCGContext *s, TCGReg dest, TCGReg src) { + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out_modrm(s, OPC_MOVSLQ, dest, src); } +static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg dest, TCGReg src) +{ + tcg_out_ext32s(s, dest, src); +} + +static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg dest, TCGReg src) +{ + if (dest != src) { + tcg_out_ext32u(s, dest, src); + } +} + +static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg dest, TCGReg src) +{ + tcg_out_ext32u(s, dest, src); +} + static inline void tcg_out_bswap64(TCGContext *s, int reg) { tcg_out_opc(s, OPC_BSWAP + P_REXW + LOWREGMASK(reg), 0, reg, 0); @@ -1287,23 +1340,41 @@ static void tgen_arithi(TCGContext *s, int c, int r0, c &= 7; } - /* ??? While INC is 2 bytes shorter than ADDL $1, they also induce - partial flags update stalls on Pentium4 and are not recommended - by current Intel optimization manuals. */ - if (!cf && (c == ARITH_ADD || c == ARITH_SUB) && (val == 1 || val == -1)) { - int is_inc = (c == ARITH_ADD) ^ (val < 0); - if (TCG_TARGET_REG_BITS == 64) { - /* The single-byte increment encodings are re-tasked as the - REX prefixes. Use the MODRM encoding. */ - tcg_out_modrm(s, OPC_GRP5 + rexw, - (is_inc ? EXT5_INC_Ev : EXT5_DEC_Ev), r0); - } else { - tcg_out8(s, (is_inc ? OPC_INC_r32 : OPC_DEC_r32) + r0); + switch (c) { + case ARITH_ADD: + case ARITH_SUB: + if (!cf) { + /* + * ??? While INC is 2 bytes shorter than ADDL $1, they also induce + * partial flags update stalls on Pentium4 and are not recommended + * by current Intel optimization manuals. + */ + if (val == 1 || val == -1) { + int is_inc = (c == ARITH_ADD) ^ (val < 0); + if (TCG_TARGET_REG_BITS == 64) { + /* + * The single-byte increment encodings are re-tasked + * as the REX prefixes. Use the MODRM encoding. + */ + tcg_out_modrm(s, OPC_GRP5 + rexw, + (is_inc ? EXT5_INC_Ev : EXT5_DEC_Ev), r0); + } else { + tcg_out8(s, (is_inc ? OPC_INC_r32 : OPC_DEC_r32) + r0); + } + return; + } + if (val == 128) { + /* + * Facilitate using an 8-bit immediate. Carry is inverted + * by this transformation, so do it only if cf == 0. + */ + c ^= ARITH_ADD ^ ARITH_SUB; + val = -128; + } } - return; - } + break; - if (c == ARITH_AND) { + case ARITH_AND: if (TCG_TARGET_REG_BITS == 64) { if (val == 0xffffffffu) { tcg_out_ext32u(s, r0, r0); @@ -1322,6 +1393,17 @@ static void tgen_arithi(TCGContext *s, int c, int r0, tcg_out_ext16u(s, r0, r0); return; } + break; + + case ARITH_OR: + case ARITH_XOR: + if (val >= 0x80 && val <= 0xff + && (r0 < 4 || TCG_TARGET_REG_BITS == 64)) { + tcg_out_modrm(s, OPC_ARITH_EbIb + P_REXB_RM, c, r0); + tcg_out8(s, val); + return; + } + break; } if (val == (int8_t)val) { @@ -1335,7 +1417,7 @@ static void tgen_arithi(TCGContext *s, int c, int r0, return; } - tcg_abort(); + g_assert_not_reached(); } static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) @@ -1345,8 +1427,8 @@ static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) } } -/* Use SMALL != 0 to force a short forward branch. */ -static void tcg_out_jxx(TCGContext *s, int opc, TCGLabel *l, int small) +/* Set SMALL to force a short forward branch. */ +static void tcg_out_jxx(TCGContext *s, int opc, TCGLabel *l, bool small) { int32_t val, val1; @@ -1361,9 +1443,7 @@ static void tcg_out_jxx(TCGContext *s, int opc, TCGLabel *l, int small) } tcg_out8(s, val1); } else { - if (small) { - tcg_abort(); - } + tcg_debug_assert(!small); if (opc == -1) { tcg_out8(s, OPC_JMP_long); tcg_out32(s, val - 5); @@ -1391,139 +1471,291 @@ static void tcg_out_jxx(TCGContext *s, int opc, TCGLabel *l, int small) } } -static void tcg_out_cmp(TCGContext *s, TCGArg arg1, TCGArg arg2, - int const_arg2, int rexw) +static int tcg_out_cmp(TCGContext *s, TCGCond cond, TCGArg arg1, + TCGArg arg2, int const_arg2, int rexw) { - if (const_arg2) { - if (arg2 == 0) { - /* test r, r */ + int jz, js; + + if (!is_tst_cond(cond)) { + if (!const_arg2) { + tgen_arithr(s, ARITH_CMP + rexw, arg1, arg2); + } else if (arg2 == 0) { tcg_out_modrm(s, OPC_TESTL + rexw, arg1, arg1); } else { + tcg_debug_assert(!rexw || arg2 == (int32_t)arg2); tgen_arithi(s, ARITH_CMP + rexw, arg1, arg2, 0); } - } else { - tgen_arithr(s, ARITH_CMP + rexw, arg1, arg2); + return tcg_cond_to_jcc[cond]; } + + jz = tcg_cond_to_jcc[cond]; + js = (cond == TCG_COND_TSTNE ? JCC_JS : JCC_JNS); + + if (!const_arg2) { + tcg_out_modrm(s, OPC_TESTL + rexw, arg1, arg2); + return jz; + } + + if (arg2 <= 0xff && (TCG_TARGET_REG_BITS == 64 || arg1 < 4)) { + if (arg2 == 0x80) { + tcg_out_modrm(s, OPC_TESTB | P_REXB_R, arg1, arg1); + return js; + } + if (arg2 == 0xff) { + tcg_out_modrm(s, OPC_TESTB | P_REXB_R, arg1, arg1); + return jz; + } + tcg_out_modrm(s, OPC_GRP3_Eb | P_REXB_RM, EXT3_TESTi, arg1); + tcg_out8(s, arg2); + return jz; + } + + if ((arg2 & ~0xff00) == 0 && arg1 < 4) { + if (arg2 == 0x8000) { + tcg_out_modrm(s, OPC_TESTB, arg1 + 4, arg1 + 4); + return js; + } + if (arg2 == 0xff00) { + tcg_out_modrm(s, OPC_TESTB, arg1 + 4, arg1 + 4); + return jz; + } + tcg_out_modrm(s, OPC_GRP3_Eb, EXT3_TESTi, arg1 + 4); + tcg_out8(s, arg2 >> 8); + return jz; + } + + if (arg2 == 0xffff) { + tcg_out_modrm(s, OPC_TESTL | P_DATA16, arg1, arg1); + return jz; + } + if (arg2 == 0xffffffffu) { + tcg_out_modrm(s, OPC_TESTL, arg1, arg1); + return jz; + } + + if (is_power_of_2(rexw ? arg2 : (uint32_t)arg2)) { + int jc = (cond == TCG_COND_TSTNE ? JCC_JB : JCC_JAE); + int sh = ctz64(arg2); + + rexw = (sh & 32 ? P_REXW : 0); + if ((sh & 31) == 31) { + tcg_out_modrm(s, OPC_TESTL | rexw, arg1, arg1); + return js; + } else { + tcg_out_modrm(s, OPC_GRPBT | rexw, OPC_GRPBT_BT, arg1); + tcg_out8(s, sh); + return jc; + } + } + + if (rexw) { + if (arg2 == (uint32_t)arg2) { + rexw = 0; + } else { + tcg_debug_assert(arg2 == (int32_t)arg2); + } + } + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_TESTi, arg1); + tcg_out32(s, arg2); + return jz; } -static void tcg_out_brcond32(TCGContext *s, TCGCond cond, - TCGArg arg1, TCGArg arg2, int const_arg2, - TCGLabel *label, int small) +static void tcg_out_brcond(TCGContext *s, int rexw, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + TCGLabel *label, bool small) { - tcg_out_cmp(s, arg1, arg2, const_arg2, 0); - tcg_out_jxx(s, tcg_cond_to_jcc[cond], label, small); + int jcc = tcg_out_cmp(s, cond, arg1, arg2, const_arg2, rexw); + tcg_out_jxx(s, jcc, label, small); } -#if TCG_TARGET_REG_BITS == 64 -static void tcg_out_brcond64(TCGContext *s, TCGCond cond, - TCGArg arg1, TCGArg arg2, int const_arg2, - TCGLabel *label, int small) -{ - tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW); - tcg_out_jxx(s, tcg_cond_to_jcc[cond], label, small); -} -#else -/* XXX: we implement it at the target level to avoid having to - handle cross basic blocks temporaries */ +#if TCG_TARGET_REG_BITS == 32 static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, - const int *const_args, int small) + const int *const_args, bool small) { TCGLabel *label_next = gen_new_label(); TCGLabel *label_this = arg_label(args[5]); + TCGCond cond = args[4]; - switch(args[4]) { + switch (cond) { case TCG_COND_EQ: - tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2], - label_next, 1); - tcg_out_brcond32(s, TCG_COND_EQ, args[1], args[3], const_args[3], - label_this, small); + case TCG_COND_TSTEQ: + tcg_out_brcond(s, 0, tcg_invert_cond(cond), + args[0], args[2], const_args[2], label_next, 1); + tcg_out_brcond(s, 0, cond, args[1], args[3], const_args[3], + label_this, small); break; case TCG_COND_NE: - tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2], - label_this, small); - tcg_out_brcond32(s, TCG_COND_NE, args[1], args[3], const_args[3], - label_this, small); + case TCG_COND_TSTNE: + tcg_out_brcond(s, 0, cond, args[0], args[2], const_args[2], + label_this, small); + tcg_out_brcond(s, 0, cond, args[1], args[3], const_args[3], + label_this, small); break; case TCG_COND_LT: - tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_LT, args[1], args[3], const_args[3], + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_LTU, args[0], args[2], const_args[2], + label_this, small); break; case TCG_COND_LE: - tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_LT, args[1], args[3], const_args[3], + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_LEU, args[0], args[2], const_args[2], + label_this, small); break; case TCG_COND_GT: - tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_GT, args[1], args[3], const_args[3], + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_GTU, args[0], args[2], const_args[2], + label_this, small); break; case TCG_COND_GE: - tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_GT, args[1], args[3], const_args[3], + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_GEU, args[0], args[2], const_args[2], + label_this, small); break; case TCG_COND_LTU: - tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_LTU, args[1], args[3], const_args[3], + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_LTU, args[0], args[2], const_args[2], + label_this, small); break; case TCG_COND_LEU: - tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_LTU, args[1], args[3], const_args[3], + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_LEU, args[0], args[2], const_args[2], + label_this, small); break; case TCG_COND_GTU: - tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_GTU, args[1], args[3], const_args[3], + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_GTU, args[0], args[2], const_args[2], + label_this, small); break; case TCG_COND_GEU: - tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_GTU, args[1], args[3], const_args[3], + label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2], - label_this, small); + tcg_out_brcond(s, 0, TCG_COND_GEU, args[0], args[2], const_args[2], + label_this, small); break; default: - tcg_abort(); + g_assert_not_reached(); } tcg_out_label(s, label_next); } #endif -static void tcg_out_setcond32(TCGContext *s, TCGCond cond, TCGArg dest, - TCGArg arg1, TCGArg arg2, int const_arg2) +static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond, + TCGArg dest, TCGArg arg1, TCGArg arg2, + int const_arg2, bool neg) { - tcg_out_cmp(s, arg1, arg2, const_arg2, 0); - tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest); - tcg_out_ext8u(s, dest, dest); + bool inv = false; + bool cleared; + int jcc; + + switch (cond) { + case TCG_COND_NE: + inv = true; + /* fall through */ + case TCG_COND_EQ: + /* If arg2 is 0, convert to LTU/GEU vs 1. */ + if (const_arg2 && arg2 == 0) { + arg2 = 1; + goto do_ltu; + } + break; + + case TCG_COND_LEU: + inv = true; + /* fall through */ + case TCG_COND_GTU: + /* If arg2 is a register, swap for LTU/GEU. */ + if (!const_arg2) { + TCGReg t = arg1; + arg1 = arg2; + arg2 = t; + goto do_ltu; + } + break; + + case TCG_COND_GEU: + inv = true; + /* fall through */ + case TCG_COND_LTU: + do_ltu: + /* + * Relying on the carry bit, use SBB to produce -1 if LTU, 0 if GEU. + * We can then use NEG or INC to produce the desired result. + * This is always smaller than the SETCC expansion. + */ + tcg_out_cmp(s, TCG_COND_LTU, arg1, arg2, const_arg2, rexw); + + /* X - X - C = -C = (C ? -1 : 0) */ + tgen_arithr(s, ARITH_SBB + (neg ? rexw : 0), dest, dest); + if (inv && neg) { + /* ~(C ? -1 : 0) = (C ? 0 : -1) */ + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, dest); + } else if (inv) { + /* (C ? -1 : 0) + 1 = (C ? 0 : 1) */ + tgen_arithi(s, ARITH_ADD, dest, 1, 0); + } else if (!neg) { + /* -(C ? -1 : 0) = (C ? 1 : 0) */ + tcg_out_modrm(s, OPC_GRP3_Ev, EXT3_NEG, dest); + } + return; + + case TCG_COND_GE: + inv = true; + /* fall through */ + case TCG_COND_LT: + /* If arg2 is 0, extract the sign bit. */ + if (const_arg2 && arg2 == 0) { + tcg_out_mov(s, rexw ? TCG_TYPE_I64 : TCG_TYPE_I32, dest, arg1); + if (inv) { + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, dest); + } + tcg_out_shifti(s, (neg ? SHIFT_SAR : SHIFT_SHR) + rexw, + dest, rexw ? 63 : 31); + return; + } + break; + + default: + break; + } + + /* + * If dest does not overlap the inputs, clearing it first is preferred. + * The XOR breaks any false dependency for the low-byte write to dest, + * and is also one byte smaller than MOVZBL. + */ + cleared = false; + if (dest != arg1 && (const_arg2 || dest != arg2)) { + tgen_arithr(s, ARITH_XOR, dest, dest); + cleared = true; + } + + jcc = tcg_out_cmp(s, cond, arg1, arg2, const_arg2, rexw); + tcg_out_modrm(s, OPC_SETCC | jcc, 0, dest); + + if (!cleared) { + tcg_out_ext8u(s, dest, dest); + } + if (neg) { + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NEG, dest); + } } -#if TCG_TARGET_REG_BITS == 64 -static void tcg_out_setcond64(TCGContext *s, TCGCond cond, TCGArg dest, - TCGArg arg1, TCGArg arg2, int const_arg2) -{ - tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW); - tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest); - tcg_out_ext8u(s, dest, dest); -} -#else +#if TCG_TARGET_REG_BITS == 32 static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, const int *const_args) { @@ -1567,37 +1799,27 @@ static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, } #endif -static void tcg_out_cmov(TCGContext *s, TCGCond cond, int rexw, +static void tcg_out_cmov(TCGContext *s, int jcc, int rexw, TCGReg dest, TCGReg v1) { if (have_cmov) { - tcg_out_modrm(s, OPC_CMOVCC | tcg_cond_to_jcc[cond] | rexw, dest, v1); + tcg_out_modrm(s, OPC_CMOVCC | jcc | rexw, dest, v1); } else { TCGLabel *over = gen_new_label(); - tcg_out_jxx(s, tcg_cond_to_jcc[tcg_invert_cond(cond)], over, 1); + tcg_out_jxx(s, jcc ^ 1, over, 1); tcg_out_mov(s, TCG_TYPE_I32, dest, v1); tcg_out_label(s, over); } } -static void tcg_out_movcond32(TCGContext *s, TCGCond cond, TCGReg dest, - TCGReg c1, TCGArg c2, int const_c2, - TCGReg v1) +static void tcg_out_movcond(TCGContext *s, int rexw, TCGCond cond, + TCGReg dest, TCGReg c1, TCGArg c2, int const_c2, + TCGReg v1) { - tcg_out_cmp(s, c1, c2, const_c2, 0); - tcg_out_cmov(s, cond, 0, dest, v1); + int jcc = tcg_out_cmp(s, cond, c1, c2, const_c2, rexw); + tcg_out_cmov(s, jcc, rexw, dest, v1); } -#if TCG_TARGET_REG_BITS == 64 -static void tcg_out_movcond64(TCGContext *s, TCGCond cond, TCGReg dest, - TCGReg c1, TCGArg c2, int const_c2, - TCGReg v1) -{ - tcg_out_cmp(s, c1, c2, const_c2, P_REXW); - tcg_out_cmov(s, cond, P_REXW, dest, v1); -} -#endif - static void tcg_out_ctz(TCGContext *s, int rexw, TCGReg dest, TCGReg arg1, TCGArg arg2, bool const_a2) { @@ -1607,12 +1829,12 @@ static void tcg_out_ctz(TCGContext *s, int rexw, TCGReg dest, TCGReg arg1, tcg_debug_assert(arg2 == (rexw ? 64 : 32)); } else { tcg_debug_assert(dest != arg2); - tcg_out_cmov(s, TCG_COND_LTU, rexw, dest, arg2); + tcg_out_cmov(s, JCC_JB, rexw, dest, arg2); } } else { tcg_debug_assert(dest != arg2); tcg_out_modrm(s, OPC_BSF + rexw, dest, arg1); - tcg_out_cmov(s, TCG_COND_EQ, rexw, dest, arg2); + tcg_out_cmov(s, JCC_JE, rexw, dest, arg2); } } @@ -1625,7 +1847,7 @@ static void tcg_out_clz(TCGContext *s, int rexw, TCGReg dest, TCGReg arg1, tcg_debug_assert(arg2 == (rexw ? 64 : 32)); } else { tcg_debug_assert(dest != arg2); - tcg_out_cmov(s, TCG_COND_LTU, rexw, dest, arg2); + tcg_out_cmov(s, JCC_JB, rexw, dest, arg2); } } else { tcg_debug_assert(!const_a2); @@ -1637,8 +1859,8 @@ static void tcg_out_clz(TCGContext *s, int rexw, TCGReg dest, TCGReg arg1, tgen_arithi(s, ARITH_XOR + rexw, dest, rexw ? 63 : 31, 0); /* Since we have destroyed the flags from BSR, we have to re-test. */ - tcg_out_cmp(s, arg1, 0, 1, rexw); - tcg_out_cmov(s, TCG_COND_EQ, rexw, dest, arg2); + int jcc = tcg_out_cmp(s, TCG_COND_EQ, arg1, 0, 1, rexw); + tcg_out_cmov(s, jcc, rexw, dest, arg2); } } @@ -1652,7 +1874,7 @@ static void tcg_out_branch(TCGContext *s, int call, const tcg_insn_unit *dest) } else { /* rip-relative addressing into the constant pool. This is 6 + 8 = 14 bytes, as compared to using an - an immediate load 10 + 6 = 16 bytes, plus we may + immediate load 10 + 6 = 16 bytes, plus we may be able to re-use the pool constant for more calls. */ tcg_out_opc(s, OPC_GRP5, 0, 0, 0); tcg_out8(s, (call ? EXT5_CALLN_Ev : EXT5_JMPN_Ev) << 3 | 5); @@ -1661,9 +1883,26 @@ static void tcg_out_branch(TCGContext *s, int call, const tcg_insn_unit *dest) } } -static inline void tcg_out_call(TCGContext *s, const tcg_insn_unit *dest) +static void tcg_out_call(TCGContext *s, const tcg_insn_unit *dest, + const TCGHelperInfo *info) { tcg_out_branch(s, 1, dest); + +#ifndef _WIN32 + if (TCG_TARGET_REG_BITS == 32 && info->out_kind == TCG_CALL_RET_BY_REF) { + /* + * The sysv i386 abi for struct return places a reference as the + * first argument of the stack, and pops that argument with the + * return statement. Since we want to retain the aligned stack + * pointer for the callee, we do not want to actually push that + * argument before the call but rely on the normal store to the + * stack slot. But we do need to compensate for the pop in order + * to reset our correct stack pointer value. + * Pushing a garbage value back onto the stack is quickest. + */ + tcg_out_push(s, TCG_REG_EAX); + } +#endif } static void tcg_out_jmp(TCGContext *s, const tcg_insn_unit *dest) @@ -1686,160 +1925,80 @@ static void tcg_out_nopn(TCGContext *s, int n) tcg_out8(s, 0x90); } -#if defined(CONFIG_SOFTMMU) -/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, - * int mmu_idx, uintptr_t ra) - */ -static void * const qemu_ld_helpers[(MO_SIZE | MO_BSWAP) + 1] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_LEUW] = helper_le_lduw_mmu, - [MO_LEUL] = helper_le_ldul_mmu, - [MO_LEUQ] = helper_le_ldq_mmu, - [MO_BEUW] = helper_be_lduw_mmu, - [MO_BEUL] = helper_be_ldul_mmu, - [MO_BEUQ] = helper_be_ldq_mmu, -}; +typedef struct { + TCGReg base; + int index; + int ofs; + int seg; + TCGAtomAlign aa; +} HostAddress; -/* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, - * uintxx_t val, int mmu_idx, uintptr_t ra) - */ -static void * const qemu_st_helpers[(MO_SIZE | MO_BSWAP) + 1] = { - [MO_UB] = helper_ret_stb_mmu, - [MO_LEUW] = helper_le_stw_mmu, - [MO_LEUL] = helper_le_stl_mmu, - [MO_LEUQ] = helper_le_stq_mmu, - [MO_BEUW] = helper_be_stw_mmu, - [MO_BEUL] = helper_be_stl_mmu, - [MO_BEUQ] = helper_be_stq_mmu, -}; - -/* Perform the TLB load and compare. - - Inputs: - ADDRLO and ADDRHI contain the low and high part of the address. - - MEM_INDEX and S_BITS are the memory context and log2 size of the load. - - WHICH is the offset into the CPUTLBEntry structure of the slot to read. - This should be offsetof addr_read or addr_write. - - Outputs: - LABEL_PTRS is filled with 1 (32-bit addresses) or 2 (64-bit addresses) - positions of the displacements of forward jumps to the TLB miss case. - - Second argument register is loaded with the low part of the address. - In the TLB hit case, it has been adjusted as indicated by the TLB - and so is a host address. In the TLB miss case, it continues to - hold a guest address. - - First argument register is clobbered. */ - -static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi, - int mem_index, MemOp opc, - tcg_insn_unit **label_ptr, int which) +bool tcg_target_has_memory_bswap(MemOp memop) { - const TCGReg r0 = TCG_REG_L0; - const TCGReg r1 = TCG_REG_L1; - TCGType ttype = TCG_TYPE_I32; - TCGType tlbtype = TCG_TYPE_I32; - int trexw = 0, hrexw = 0, tlbrexw = 0; - unsigned a_bits = get_alignment_bits(opc); - unsigned s_bits = opc & MO_SIZE; - unsigned a_mask = (1 << a_bits) - 1; - unsigned s_mask = (1 << s_bits) - 1; - target_ulong tlb_mask; + TCGAtomAlign aa; - if (TCG_TARGET_REG_BITS == 64) { - if (TARGET_LONG_BITS == 64) { - ttype = TCG_TYPE_I64; - trexw = P_REXW; - } - if (TCG_TYPE_PTR == TCG_TYPE_I64) { - hrexw = P_REXW; - if (TARGET_PAGE_BITS + CPU_TLB_DYN_MAX_BITS > 32) { - tlbtype = TCG_TYPE_I64; - tlbrexw = P_REXW; - } - } + if (!have_movbe) { + return false; + } + if ((memop & MO_SIZE) < MO_128) { + return true; } - tcg_out_mov(s, tlbtype, r0, addrlo); - tcg_out_shifti(s, SHIFT_SHR + tlbrexw, r0, - TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - - tcg_out_modrm_offset(s, OPC_AND_GvEv + trexw, r0, TCG_AREG0, - TLB_MASK_TABLE_OFS(mem_index) + - offsetof(CPUTLBDescFast, mask)); - - tcg_out_modrm_offset(s, OPC_ADD_GvEv + hrexw, r0, TCG_AREG0, - TLB_MASK_TABLE_OFS(mem_index) + - offsetof(CPUTLBDescFast, table)); - - /* If the required alignment is at least as large as the access, simply - copy the address and mask. For lesser alignments, check that we don't - cross pages for the complete access. */ - if (a_bits >= s_bits) { - tcg_out_mov(s, ttype, r1, addrlo); - } else { - tcg_out_modrm_offset(s, OPC_LEA + trexw, r1, addrlo, s_mask - a_mask); - } - tlb_mask = (target_ulong)TARGET_PAGE_MASK | a_mask; - tgen_arithi(s, ARITH_AND + trexw, r1, tlb_mask, 0); - - /* cmp 0(r0), r1 */ - tcg_out_modrm_offset(s, OPC_CMP_GvEv + trexw, r1, r0, which); - - /* Prepare for both the fast path add of the tlb addend, and the slow - path function argument setup. */ - tcg_out_mov(s, ttype, r1, addrlo); - - /* jne slow_path */ - tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); - label_ptr[0] = s->code_ptr; - s->code_ptr += 4; - - if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { - /* cmp 4(r0), addrhi */ - tcg_out_modrm_offset(s, OPC_CMP_GvEv, addrhi, r0, which + 4); - - /* jne slow_path */ - tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); - label_ptr[1] = s->code_ptr; - s->code_ptr += 4; - } - - /* TLB Hit. */ - - /* add addend(r0), r1 */ - tcg_out_modrm_offset(s, OPC_ADD_GvEv + hrexw, r1, r0, - offsetof(CPUTLBEntry, addend)); + /* + * Reject 16-byte memop with 16-byte atomicity, i.e. VMOVDQA, + * but do allow a pair of 64-bit operations, i.e. MOVBEQ. + */ + aa = atom_and_align_for_opc(tcg_ctx, memop, MO_ATOM_IFALIGN, true); + return aa.atom < MO_128; } /* - * Record the context of a call to the out of line helper code for the slow path - * for a load or store, so that we can later generate the correct helper code + * Because i686 has no register parameters and because x86_64 has xchg + * to handle addr/data register overlap, we have placed all input arguments + * before we need might need a scratch reg. + * + * Even then, a scratch is only needed for l->raddr. Rather than expose + * a general-purpose scratch when we don't actually know it's available, + * use the ra_gen hook to load into RAX if needed. */ -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, bool is_64, - MemOpIdx oi, - TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - tcg_insn_unit *raddr, - tcg_insn_unit **label_ptr) +#if TCG_TARGET_REG_BITS == 64 +static TCGReg ldst_ra_gen(TCGContext *s, const TCGLabelQemuLdst *l, int arg) { - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->oi = oi; - label->type = is_64 ? TCG_TYPE_I64 : TCG_TYPE_I32; - label->datalo_reg = datalo; - label->datahi_reg = datahi; - label->addrlo_reg = addrlo; - label->addrhi_reg = addrhi; - label->raddr = tcg_splitwx_to_rx(raddr); - label->label_ptr[0] = label_ptr[0]; - if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { - label->label_ptr[1] = label_ptr[1]; + if (arg < 0) { + arg = TCG_REG_RAX; } + tcg_out_movi(s, TCG_TYPE_PTR, arg, (uintptr_t)l->raddr); + return arg; +} +static const TCGLdstHelperParam ldst_helper_param = { + .ra_gen = ldst_ra_gen +}; +#else +static const TCGLdstHelperParam ldst_helper_param = { }; +#endif + +static void tcg_out_vec_to_pair(TCGContext *s, TCGType type, + TCGReg l, TCGReg h, TCGReg v) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + /* vpmov{d,q} %v, %l */ + tcg_out_vex_modrm(s, OPC_MOVD_EyVy + rexw, v, 0, l); + /* vpextr{d,q} $1, %v, %h */ + tcg_out_vex_modrm(s, OPC_PEXTRD + rexw, v, 0, h); + tcg_out8(s, 1); +} + +static void tcg_out_pair_to_vec(TCGContext *s, TCGType type, + TCGReg v, TCGReg l, TCGReg h) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + /* vmov{d,q} %l, %v */ + tcg_out_vex_modrm(s, OPC_MOVD_VyEy + rexw, v, 0, l); + /* vpinsr{d,q} $1, %h, %v, %v */ + tcg_out_vex_modrm(s, OPC_PINSRD + rexw, v, v, h); + tcg_out8(s, 1); } /* @@ -1847,82 +2006,19 @@ static void add_qemu_ldst_label(TCGContext *s, bool is_ld, bool is_64, */ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { - MemOpIdx oi = l->oi; - MemOp opc = get_memop(oi); - TCGReg data_reg; + MemOp opc = get_memop(l->oi); tcg_insn_unit **label_ptr = &l->label_ptr[0]; - int rexw = (l->type == TCG_TYPE_I64 ? P_REXW : 0); /* resolve label address */ tcg_patch32(label_ptr[0], s->code_ptr - label_ptr[0] - 4); - if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + if (label_ptr[1]) { tcg_patch32(label_ptr[1], s->code_ptr - label_ptr[1] - 4); } - if (TCG_TARGET_REG_BITS == 32) { - int ofs = 0; + tcg_out_ld_helper_args(s, l, &ldst_helper_param); + tcg_out_branch(s, 1, qemu_ld_helpers[opc & MO_SIZE]); + tcg_out_ld_helper_ret(s, l, false, &ldst_helper_param); - tcg_out_st(s, TCG_TYPE_PTR, TCG_AREG0, TCG_REG_ESP, ofs); - ofs += 4; - - tcg_out_st(s, TCG_TYPE_I32, l->addrlo_reg, TCG_REG_ESP, ofs); - ofs += 4; - - if (TARGET_LONG_BITS == 64) { - tcg_out_st(s, TCG_TYPE_I32, l->addrhi_reg, TCG_REG_ESP, ofs); - ofs += 4; - } - - tcg_out_sti(s, TCG_TYPE_I32, oi, TCG_REG_ESP, ofs); - ofs += 4; - - tcg_out_sti(s, TCG_TYPE_PTR, (uintptr_t)l->raddr, TCG_REG_ESP, ofs); - } else { - tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); - /* The second argument is already loaded with addrlo. */ - tcg_out_movi(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[2], oi); - tcg_out_movi(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[3], - (uintptr_t)l->raddr); - } - - tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SIZE)]); - - data_reg = l->datalo_reg; - switch (opc & MO_SSIZE) { - case MO_SB: - tcg_out_ext8s(s, data_reg, TCG_REG_EAX, rexw); - break; - case MO_SW: - tcg_out_ext16s(s, data_reg, TCG_REG_EAX, rexw); - break; -#if TCG_TARGET_REG_BITS == 64 - case MO_SL: - tcg_out_ext32s(s, data_reg, TCG_REG_EAX); - break; -#endif - case MO_UB: - case MO_UW: - /* Note that the helpers have zero-extended to tcg_target_long. */ - case MO_UL: - tcg_out_mov(s, TCG_TYPE_I32, data_reg, TCG_REG_EAX); - break; - case MO_UQ: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_mov(s, TCG_TYPE_I64, data_reg, TCG_REG_RAX); - } else if (data_reg == TCG_REG_EDX) { - /* xchg %edx, %eax */ - tcg_out_opc(s, OPC_XCHG_ax_r32 + TCG_REG_EDX, 0, 0, 0); - tcg_out_mov(s, TCG_TYPE_I32, l->datahi_reg, TCG_REG_EAX); - } else { - tcg_out_mov(s, TCG_TYPE_I32, data_reg, TCG_REG_EAX); - tcg_out_mov(s, TCG_TYPE_I32, l->datahi_reg, TCG_REG_EDX); - } - break; - default: - tcg_abort(); - } - - /* Jump to the code corresponding to next IR of qemu_st */ tcg_out_jmp(s, l->raddr); return true; } @@ -1932,157 +2028,30 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) */ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { - MemOpIdx oi = l->oi; - MemOp opc = get_memop(oi); - MemOp s_bits = opc & MO_SIZE; + MemOp opc = get_memop(l->oi); tcg_insn_unit **label_ptr = &l->label_ptr[0]; - TCGReg retaddr; /* resolve label address */ tcg_patch32(label_ptr[0], s->code_ptr - label_ptr[0] - 4); - if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + if (label_ptr[1]) { tcg_patch32(label_ptr[1], s->code_ptr - label_ptr[1] - 4); } - if (TCG_TARGET_REG_BITS == 32) { - int ofs = 0; + tcg_out_st_helper_args(s, l, &ldst_helper_param); + tcg_out_branch(s, 1, qemu_st_helpers[opc & MO_SIZE]); - tcg_out_st(s, TCG_TYPE_PTR, TCG_AREG0, TCG_REG_ESP, ofs); - ofs += 4; - - tcg_out_st(s, TCG_TYPE_I32, l->addrlo_reg, TCG_REG_ESP, ofs); - ofs += 4; - - if (TARGET_LONG_BITS == 64) { - tcg_out_st(s, TCG_TYPE_I32, l->addrhi_reg, TCG_REG_ESP, ofs); - ofs += 4; - } - - tcg_out_st(s, TCG_TYPE_I32, l->datalo_reg, TCG_REG_ESP, ofs); - ofs += 4; - - if (s_bits == MO_64) { - tcg_out_st(s, TCG_TYPE_I32, l->datahi_reg, TCG_REG_ESP, ofs); - ofs += 4; - } - - tcg_out_sti(s, TCG_TYPE_I32, oi, TCG_REG_ESP, ofs); - ofs += 4; - - retaddr = TCG_REG_EAX; - tcg_out_movi(s, TCG_TYPE_PTR, retaddr, (uintptr_t)l->raddr); - tcg_out_st(s, TCG_TYPE_PTR, retaddr, TCG_REG_ESP, ofs); - } else { - tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); - /* The second argument is already loaded with addrlo. */ - tcg_out_mov(s, (s_bits == MO_64 ? TCG_TYPE_I64 : TCG_TYPE_I32), - tcg_target_call_iarg_regs[2], l->datalo_reg); - tcg_out_movi(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[3], oi); - - if (ARRAY_SIZE(tcg_target_call_iarg_regs) > 4) { - retaddr = tcg_target_call_iarg_regs[4]; - tcg_out_movi(s, TCG_TYPE_PTR, retaddr, (uintptr_t)l->raddr); - } else { - retaddr = TCG_REG_RAX; - tcg_out_movi(s, TCG_TYPE_PTR, retaddr, (uintptr_t)l->raddr); - tcg_out_st(s, TCG_TYPE_PTR, retaddr, TCG_REG_ESP, - TCG_TARGET_CALL_STACK_OFFSET); - } - } - - /* "Tail call" to the helper, with the return address back inline. */ - tcg_out_push(s, retaddr); - tcg_out_jmp(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]); - return true; -} -#else - -static void tcg_out_test_alignment(TCGContext *s, bool is_ld, TCGReg addrlo, - TCGReg addrhi, unsigned a_bits) -{ - unsigned a_mask = (1 << a_bits) - 1; - TCGLabelQemuLdst *label; - - /* - * We are expecting a_bits to max out at 7, so we can usually use testb. - * For i686, we have to use testl for %esi/%edi. - */ - if (a_mask <= 0xff && (TCG_TARGET_REG_BITS == 64 || addrlo < 4)) { - tcg_out_modrm(s, OPC_GRP3_Eb | P_REXB_RM, EXT3_TESTi, addrlo); - tcg_out8(s, a_mask); - } else { - tcg_out_modrm(s, OPC_GRP3_Ev, EXT3_TESTi, addrlo); - tcg_out32(s, a_mask); - } - - /* jne slow_path */ - tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); - - label = new_ldst_label(s); - label->is_ld = is_ld; - label->addrlo_reg = addrlo; - label->addrhi_reg = addrhi; - label->raddr = tcg_splitwx_to_rx(s->code_ptr + 4); - label->label_ptr[0] = s->code_ptr; - - s->code_ptr += 4; -} - -static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) -{ - /* resolve label address */ - tcg_patch32(l->label_ptr[0], s->code_ptr - l->label_ptr[0] - 4); - - if (TCG_TARGET_REG_BITS == 32) { - int ofs = 0; - - tcg_out_st(s, TCG_TYPE_PTR, TCG_AREG0, TCG_REG_ESP, ofs); - ofs += 4; - - tcg_out_st(s, TCG_TYPE_I32, l->addrlo_reg, TCG_REG_ESP, ofs); - ofs += 4; - if (TARGET_LONG_BITS == 64) { - tcg_out_st(s, TCG_TYPE_I32, l->addrhi_reg, TCG_REG_ESP, ofs); - ofs += 4; - } - - tcg_out_pushi(s, (uintptr_t)l->raddr); - } else { - tcg_out_mov(s, TCG_TYPE_TL, tcg_target_call_iarg_regs[1], - l->addrlo_reg); - tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); - - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RAX, (uintptr_t)l->raddr); - tcg_out_push(s, TCG_REG_RAX); - } - - /* "Tail call" to the helper, with the return address back inline. */ - tcg_out_jmp(s, (const void *)(l->is_ld ? helper_unaligned_ld - : helper_unaligned_st)); + tcg_out_jmp(s, l->raddr); return true; } -static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - return tcg_out_fail_alignment(s, l); -} +#ifdef CONFIG_USER_ONLY +static HostAddress x86_guest_base = { + .index = -1 +}; -static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - return tcg_out_fail_alignment(s, l); -} - -#if TCG_TARGET_REG_BITS == 32 -# define x86_guest_base_seg 0 -# define x86_guest_base_index -1 -# define x86_guest_base_offset guest_base -#else -static int x86_guest_base_seg; -static int x86_guest_base_index = -1; -static int32_t x86_guest_base_offset; -# if defined(__x86_64__) && defined(__linux__) -# include -# include +#if defined(__x86_64__) && defined(__linux__) +# include +# include int arch_prctl(int code, unsigned long addr); static inline int setup_guest_base_seg(void) { @@ -2091,8 +2060,10 @@ static inline int setup_guest_base_seg(void) } return 0; } -# elif defined (__FreeBSD__) || defined (__FreeBSD_kernel__) -# include +#define setup_guest_base_seg setup_guest_base_seg +#elif defined(__x86_64__) && \ + (defined (__FreeBSD__) || defined (__FreeBSD_kernel__)) +# include static inline int setup_guest_base_seg(void) { if (sysarch(AMD64_SET_GSBASE, &guest_base) == 0) { @@ -2100,21 +2071,143 @@ static inline int setup_guest_base_seg(void) } return 0; } -# else -static inline int setup_guest_base_seg(void) -{ - return 0; -} -# endif +#define setup_guest_base_seg setup_guest_base_seg #endif -#endif /* SOFTMMU */ +#else +# define x86_guest_base (*(HostAddress *)({ qemu_build_not_reached(); NULL; })) +#endif /* CONFIG_USER_ONLY */ +#ifndef setup_guest_base_seg +# define setup_guest_base_seg() 0 +#endif + +#define MIN_TLB_MASK_TABLE_OFS INT_MIN + +/* + * For softmmu, perform the TLB load and compare. + * For useronly, perform any required alignment tests. + * In both cases, return a TCGLabelQemuLdst structure if the slow path + * is required and fill in @h with the host address for the fast path. + */ +static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, + TCGReg addrlo, TCGReg addrhi, + MemOpIdx oi, bool is_ld) +{ + TCGLabelQemuLdst *ldst = NULL; + MemOp opc = get_memop(oi); + MemOp s_bits = opc & MO_SIZE; + unsigned a_mask; + + if (tcg_use_softmmu) { + h->index = TCG_REG_L0; + h->ofs = 0; + h->seg = 0; + } else { + *h = x86_guest_base; + } + h->base = addrlo; + h->aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, s_bits == MO_128); + a_mask = (1 << h->aa.align) - 1; + + if (tcg_use_softmmu) { + int cmp_ofs = is_ld ? offsetof(CPUTLBEntry, addr_read) + : offsetof(CPUTLBEntry, addr_write); + TCGType ttype = TCG_TYPE_I32; + TCGType tlbtype = TCG_TYPE_I32; + int trexw = 0, hrexw = 0, tlbrexw = 0; + unsigned mem_index = get_mmuidx(oi); + unsigned s_mask = (1 << s_bits) - 1; + int fast_ofs = tlb_mask_table_ofs(s, mem_index); + int tlb_mask; + + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addrlo; + ldst->addrhi_reg = addrhi; + + if (TCG_TARGET_REG_BITS == 64) { + ttype = s->addr_type; + trexw = (ttype == TCG_TYPE_I32 ? 0 : P_REXW); + if (TCG_TYPE_PTR == TCG_TYPE_I64) { + hrexw = P_REXW; + if (s->page_bits + s->tlb_dyn_max_bits > 32) { + tlbtype = TCG_TYPE_I64; + tlbrexw = P_REXW; + } + } + } + + tcg_out_mov(s, tlbtype, TCG_REG_L0, addrlo); + tcg_out_shifti(s, SHIFT_SHR + tlbrexw, TCG_REG_L0, + s->page_bits - CPU_TLB_ENTRY_BITS); + + tcg_out_modrm_offset(s, OPC_AND_GvEv + trexw, TCG_REG_L0, TCG_AREG0, + fast_ofs + offsetof(CPUTLBDescFast, mask)); + + tcg_out_modrm_offset(s, OPC_ADD_GvEv + hrexw, TCG_REG_L0, TCG_AREG0, + fast_ofs + offsetof(CPUTLBDescFast, table)); + + /* + * If the required alignment is at least as large as the access, + * simply copy the address and mask. For lesser alignments, + * check that we don't cross pages for the complete access. + */ + if (a_mask >= s_mask) { + tcg_out_mov(s, ttype, TCG_REG_L1, addrlo); + } else { + tcg_out_modrm_offset(s, OPC_LEA + trexw, TCG_REG_L1, + addrlo, s_mask - a_mask); + } + tlb_mask = s->page_mask | a_mask; + tgen_arithi(s, ARITH_AND + trexw, TCG_REG_L1, tlb_mask, 0); + + /* cmp 0(TCG_REG_L0), TCG_REG_L1 */ + tcg_out_modrm_offset(s, OPC_CMP_GvEv + trexw, + TCG_REG_L1, TCG_REG_L0, cmp_ofs); + + /* jne slow_path */ + tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); + ldst->label_ptr[0] = s->code_ptr; + s->code_ptr += 4; + + if (TCG_TARGET_REG_BITS == 32 && s->addr_type == TCG_TYPE_I64) { + /* cmp 4(TCG_REG_L0), addrhi */ + tcg_out_modrm_offset(s, OPC_CMP_GvEv, addrhi, + TCG_REG_L0, cmp_ofs + 4); + + /* jne slow_path */ + tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); + ldst->label_ptr[1] = s->code_ptr; + s->code_ptr += 4; + } + + /* TLB Hit. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_L0, TCG_REG_L0, + offsetof(CPUTLBEntry, addend)); + } else if (a_mask) { + int jcc; + + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addrlo; + ldst->addrhi_reg = addrhi; + + /* jne slow_path */ + jcc = tcg_out_cmp(s, TCG_COND_TSTNE, addrlo, a_mask, true, false); + tcg_out_opc(s, OPC_JCC_long + jcc, 0, 0, 0); + ldst->label_ptr[0] = s->code_ptr; + s->code_ptr += 4; + } + + return ldst; +} static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg base, int index, intptr_t ofs, - int seg, bool is64, MemOp memop) + HostAddress h, TCGType type, MemOp memop) { bool use_movbe = false; - int rexw = is64 * P_REXW; + int rexw = (type == TCG_TYPE_I32 ? 0 : P_REXW); int movop = OPC_MOVL_GvEv; /* Do big-endian loads with movbe. */ @@ -2126,140 +2219,178 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, switch (memop & MO_SSIZE) { case MO_UB: - tcg_out_modrm_sib_offset(s, OPC_MOVZBL + seg, datalo, - base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, OPC_MOVZBL + h.seg, datalo, + h.base, h.index, 0, h.ofs); break; case MO_SB: - tcg_out_modrm_sib_offset(s, OPC_MOVSBL + rexw + seg, datalo, - base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, OPC_MOVSBL + rexw + h.seg, datalo, + h.base, h.index, 0, h.ofs); break; case MO_UW: if (use_movbe) { /* There is no extending movbe; only low 16-bits are modified. */ - if (datalo != base && datalo != index) { + if (datalo != h.base && datalo != h.index) { /* XOR breaks dependency chains. */ tgen_arithr(s, ARITH_XOR, datalo, datalo); - tcg_out_modrm_sib_offset(s, OPC_MOVBE_GyMy + P_DATA16 + seg, - datalo, base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, OPC_MOVBE_GyMy + P_DATA16 + h.seg, + datalo, h.base, h.index, 0, h.ofs); } else { - tcg_out_modrm_sib_offset(s, OPC_MOVBE_GyMy + P_DATA16 + seg, - datalo, base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, OPC_MOVBE_GyMy + P_DATA16 + h.seg, + datalo, h.base, h.index, 0, h.ofs); tcg_out_ext16u(s, datalo, datalo); } } else { - tcg_out_modrm_sib_offset(s, OPC_MOVZWL + seg, datalo, - base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, OPC_MOVZWL + h.seg, datalo, + h.base, h.index, 0, h.ofs); } break; case MO_SW: if (use_movbe) { - tcg_out_modrm_sib_offset(s, OPC_MOVBE_GyMy + P_DATA16 + seg, - datalo, base, index, 0, ofs); - tcg_out_ext16s(s, datalo, datalo, rexw); + tcg_out_modrm_sib_offset(s, OPC_MOVBE_GyMy + P_DATA16 + h.seg, + datalo, h.base, h.index, 0, h.ofs); + tcg_out_ext16s(s, type, datalo, datalo); } else { - tcg_out_modrm_sib_offset(s, OPC_MOVSWL + rexw + seg, - datalo, base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, OPC_MOVSWL + rexw + h.seg, + datalo, h.base, h.index, 0, h.ofs); } break; case MO_UL: - tcg_out_modrm_sib_offset(s, movop + seg, datalo, base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, movop + h.seg, datalo, + h.base, h.index, 0, h.ofs); break; #if TCG_TARGET_REG_BITS == 64 case MO_SL: if (use_movbe) { - tcg_out_modrm_sib_offset(s, OPC_MOVBE_GyMy + seg, datalo, - base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, OPC_MOVBE_GyMy + h.seg, datalo, + h.base, h.index, 0, h.ofs); tcg_out_ext32s(s, datalo, datalo); } else { - tcg_out_modrm_sib_offset(s, OPC_MOVSLQ + seg, datalo, - base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, OPC_MOVSLQ + h.seg, datalo, + h.base, h.index, 0, h.ofs); } break; #endif case MO_UQ: if (TCG_TARGET_REG_BITS == 64) { - tcg_out_modrm_sib_offset(s, movop + P_REXW + seg, datalo, - base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, movop + P_REXW + h.seg, datalo, + h.base, h.index, 0, h.ofs); + break; + } + if (use_movbe) { + TCGReg t = datalo; + datalo = datahi; + datahi = t; + } + if (h.base == datalo || h.index == datalo) { + tcg_out_modrm_sib_offset(s, OPC_LEA, datahi, + h.base, h.index, 0, h.ofs); + tcg_out_modrm_offset(s, movop + h.seg, datalo, datahi, 0); + tcg_out_modrm_offset(s, movop + h.seg, datahi, datahi, 4); } else { + tcg_out_modrm_sib_offset(s, movop + h.seg, datalo, + h.base, h.index, 0, h.ofs); + tcg_out_modrm_sib_offset(s, movop + h.seg, datahi, + h.base, h.index, 0, h.ofs + 4); + } + break; + + case MO_128: + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + + /* + * Without 16-byte atomicity, use integer regs. + * That is where we want the data, and it allows bswaps. + */ + if (h.aa.atom < MO_128) { if (use_movbe) { TCGReg t = datalo; datalo = datahi; datahi = t; } - if (base != datalo) { - tcg_out_modrm_sib_offset(s, movop + seg, datalo, - base, index, 0, ofs); - tcg_out_modrm_sib_offset(s, movop + seg, datahi, - base, index, 0, ofs + 4); + if (h.base == datalo || h.index == datalo) { + tcg_out_modrm_sib_offset(s, OPC_LEA + P_REXW, datahi, + h.base, h.index, 0, h.ofs); + tcg_out_modrm_offset(s, movop + P_REXW + h.seg, + datalo, datahi, 0); + tcg_out_modrm_offset(s, movop + P_REXW + h.seg, + datahi, datahi, 8); } else { - tcg_out_modrm_sib_offset(s, movop + seg, datahi, - base, index, 0, ofs + 4); - tcg_out_modrm_sib_offset(s, movop + seg, datalo, - base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, movop + P_REXW + h.seg, datalo, + h.base, h.index, 0, h.ofs); + tcg_out_modrm_sib_offset(s, movop + P_REXW + h.seg, datahi, + h.base, h.index, 0, h.ofs + 8); } + break; } + + /* + * With 16-byte atomicity, a vector load is required. + * If we already have 16-byte alignment, then VMOVDQA always works. + * Else if VMOVDQU has atomicity with dynamic alignment, use that. + * Else use we require a runtime test for alignment for VMOVDQA; + * use VMOVDQU on the unaligned nonatomic path for simplicity. + */ + if (h.aa.align >= MO_128) { + tcg_out_vex_modrm_sib_offset(s, OPC_MOVDQA_VxWx + h.seg, + TCG_TMP_VEC, 0, + h.base, h.index, 0, h.ofs); + } else if (cpuinfo & CPUINFO_ATOMIC_VMOVDQU) { + tcg_out_vex_modrm_sib_offset(s, OPC_MOVDQU_VxWx + h.seg, + TCG_TMP_VEC, 0, + h.base, h.index, 0, h.ofs); + } else { + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + int jcc; + + jcc = tcg_out_cmp(s, TCG_COND_TSTNE, h.base, 15, true, false); + tcg_out_jxx(s, jcc, l1, true); + + tcg_out_vex_modrm_sib_offset(s, OPC_MOVDQA_VxWx + h.seg, + TCG_TMP_VEC, 0, + h.base, h.index, 0, h.ofs); + tcg_out_jxx(s, JCC_JMP, l2, true); + + tcg_out_label(s, l1); + tcg_out_vex_modrm_sib_offset(s, OPC_MOVDQU_VxWx + h.seg, + TCG_TMP_VEC, 0, + h.base, h.index, 0, h.ofs); + tcg_out_label(s, l2); + } + tcg_out_vec_to_pair(s, TCG_TYPE_I64, datalo, datahi, TCG_TMP_VEC); break; + default: g_assert_not_reached(); } } -/* XXX: qemu_ld and qemu_st could be modified to clobber only EDX and - EAX. It will be useful once fixed registers globals are less - common. */ -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) +static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg addrlo, TCGReg addrhi, + MemOpIdx oi, TCGType data_type) { - TCGReg datalo, datahi, addrlo; - TCGReg addrhi __attribute__((unused)); - MemOpIdx oi; - MemOp opc; -#if defined(CONFIG_SOFTMMU) - int mem_index; - tcg_insn_unit *label_ptr[2]; -#else - unsigned a_bits; -#endif + TCGLabelQemuLdst *ldst; + HostAddress h; - datalo = *args++; - datahi = (TCG_TARGET_REG_BITS == 32 && is64 ? *args++ : 0); - addrlo = *args++; - addrhi = (TARGET_LONG_BITS > TCG_TARGET_REG_BITS ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); + ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, true); + tcg_out_qemu_ld_direct(s, datalo, datahi, h, data_type, get_memop(oi)); -#if defined(CONFIG_SOFTMMU) - mem_index = get_mmuidx(oi); - - tcg_out_tlb_load(s, addrlo, addrhi, mem_index, opc, - label_ptr, offsetof(CPUTLBEntry, addr_read)); - - /* TLB Hit. */ - tcg_out_qemu_ld_direct(s, datalo, datahi, TCG_REG_L1, -1, 0, 0, is64, opc); - - /* Record the current context of a load into ldst label */ - add_qemu_ldst_label(s, true, is64, oi, datalo, datahi, addrlo, addrhi, - s->code_ptr, label_ptr); -#else - a_bits = get_alignment_bits(opc); - if (a_bits) { - tcg_out_test_alignment(s, true, addrlo, addrhi, a_bits); + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = datalo; + ldst->datahi_reg = datahi; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } - - tcg_out_qemu_ld_direct(s, datalo, datahi, addrlo, x86_guest_base_index, - x86_guest_base_offset, x86_guest_base_seg, - is64, opc); -#endif } static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg base, int index, intptr_t ofs, - int seg, MemOp memop) + HostAddress h, MemOp memop) { bool use_movbe = false; int movop = OPC_MOVL_EvGv; /* - * Do big-endian stores with movbe or softmmu. + * Do big-endian stores with movbe or system-mode. * User-only without movbe will have its swapping done generically. */ if (memop & MO_BSWAP) { @@ -2272,78 +2403,148 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, case MO_8: /* This is handled with constraints on INDEX_op_qemu_st8_i32. */ tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || datalo < 4); - tcg_out_modrm_sib_offset(s, OPC_MOVB_EvGv + P_REXB_R + seg, - datalo, base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, OPC_MOVB_EvGv + P_REXB_R + h.seg, + datalo, h.base, h.index, 0, h.ofs); break; case MO_16: - tcg_out_modrm_sib_offset(s, movop + P_DATA16 + seg, datalo, - base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, movop + P_DATA16 + h.seg, datalo, + h.base, h.index, 0, h.ofs); break; case MO_32: - tcg_out_modrm_sib_offset(s, movop + seg, datalo, base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, movop + h.seg, datalo, + h.base, h.index, 0, h.ofs); break; case MO_64: if (TCG_TARGET_REG_BITS == 64) { - tcg_out_modrm_sib_offset(s, movop + P_REXW + seg, datalo, - base, index, 0, ofs); + tcg_out_modrm_sib_offset(s, movop + P_REXW + h.seg, datalo, + h.base, h.index, 0, h.ofs); } else { if (use_movbe) { TCGReg t = datalo; datalo = datahi; datahi = t; } - tcg_out_modrm_sib_offset(s, movop + seg, datalo, - base, index, 0, ofs); - tcg_out_modrm_sib_offset(s, movop + seg, datahi, - base, index, 0, ofs + 4); + tcg_out_modrm_sib_offset(s, movop + h.seg, datalo, + h.base, h.index, 0, h.ofs); + tcg_out_modrm_sib_offset(s, movop + h.seg, datahi, + h.base, h.index, 0, h.ofs + 4); } break; + + case MO_128: + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + + /* + * Without 16-byte atomicity, use integer regs. + * That is where we have the data, and it allows bswaps. + */ + if (h.aa.atom < MO_128) { + if (use_movbe) { + TCGReg t = datalo; + datalo = datahi; + datahi = t; + } + tcg_out_modrm_sib_offset(s, movop + P_REXW + h.seg, datalo, + h.base, h.index, 0, h.ofs); + tcg_out_modrm_sib_offset(s, movop + P_REXW + h.seg, datahi, + h.base, h.index, 0, h.ofs + 8); + break; + } + + /* + * With 16-byte atomicity, a vector store is required. + * If we already have 16-byte alignment, then VMOVDQA always works. + * Else if VMOVDQU has atomicity with dynamic alignment, use that. + * Else use we require a runtime test for alignment for VMOVDQA; + * use VMOVDQU on the unaligned nonatomic path for simplicity. + */ + tcg_out_pair_to_vec(s, TCG_TYPE_I64, TCG_TMP_VEC, datalo, datahi); + if (h.aa.align >= MO_128) { + tcg_out_vex_modrm_sib_offset(s, OPC_MOVDQA_WxVx + h.seg, + TCG_TMP_VEC, 0, + h.base, h.index, 0, h.ofs); + } else if (cpuinfo & CPUINFO_ATOMIC_VMOVDQU) { + tcg_out_vex_modrm_sib_offset(s, OPC_MOVDQU_WxVx + h.seg, + TCG_TMP_VEC, 0, + h.base, h.index, 0, h.ofs); + } else { + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + int jcc; + + jcc = tcg_out_cmp(s, TCG_COND_TSTNE, h.base, 15, true, false); + tcg_out_jxx(s, jcc, l1, true); + + tcg_out_vex_modrm_sib_offset(s, OPC_MOVDQA_WxVx + h.seg, + TCG_TMP_VEC, 0, + h.base, h.index, 0, h.ofs); + tcg_out_jxx(s, JCC_JMP, l2, true); + + tcg_out_label(s, l1); + tcg_out_vex_modrm_sib_offset(s, OPC_MOVDQU_WxVx + h.seg, + TCG_TMP_VEC, 0, + h.base, h.index, 0, h.ofs); + tcg_out_label(s, l2); + } + break; + default: g_assert_not_reached(); } } -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) +static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg addrlo, TCGReg addrhi, + MemOpIdx oi, TCGType data_type) { - TCGReg datalo, datahi, addrlo; - TCGReg addrhi __attribute__((unused)); - MemOpIdx oi; - MemOp opc; -#if defined(CONFIG_SOFTMMU) - int mem_index; - tcg_insn_unit *label_ptr[2]; -#else - unsigned a_bits; -#endif + TCGLabelQemuLdst *ldst; + HostAddress h; - datalo = *args++; - datahi = (TCG_TARGET_REG_BITS == 32 && is64 ? *args++ : 0); - addrlo = *args++; - addrhi = (TARGET_LONG_BITS > TCG_TARGET_REG_BITS ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); + ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, false); + tcg_out_qemu_st_direct(s, datalo, datahi, h, get_memop(oi)); -#if defined(CONFIG_SOFTMMU) - mem_index = get_mmuidx(oi); - - tcg_out_tlb_load(s, addrlo, addrhi, mem_index, opc, - label_ptr, offsetof(CPUTLBEntry, addr_write)); - - /* TLB Hit. */ - tcg_out_qemu_st_direct(s, datalo, datahi, TCG_REG_L1, -1, 0, 0, opc); - - /* Record the current context of a store into ldst label */ - add_qemu_ldst_label(s, false, is64, oi, datalo, datahi, addrlo, addrhi, - s->code_ptr, label_ptr); -#else - a_bits = get_alignment_bits(opc); - if (a_bits) { - tcg_out_test_alignment(s, false, addrlo, addrhi, a_bits); + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = datalo; + ldst->datahi_reg = datahi; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } +} - tcg_out_qemu_st_direct(s, datalo, datahi, addrlo, x86_guest_base_index, - x86_guest_base_offset, x86_guest_base_seg, opc); -#endif +static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) +{ + /* Reuse the zeroing that exists for goto_ptr. */ + if (a0 == 0) { + tcg_out_jmp(s, tcg_code_gen_epilogue); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_EAX, a0); + tcg_out_jmp(s, tb_ret_addr); + } +} + +static void tcg_out_goto_tb(TCGContext *s, int which) +{ + /* + * Jump displacement must be aligned for atomic patching; + * see if we need to add extra nops before jump + */ + int gap = QEMU_ALIGN_PTR_UP(s->code_ptr + 1, 4) - s->code_ptr; + if (gap != 1) { + tcg_out_nopn(s, gap - 1); + } + tcg_out8(s, OPC_JMP_long); /* jmp im */ + set_jmp_insn_offset(s, which); + tcg_out32(s, 0); + set_jmp_reset_offset(s, which); +} + +void tb_target_set_jmp_target(const TranslationBlock *tb, int n, + uintptr_t jmp_rx, uintptr_t jmp_rw) +{ + /* patch the branch destination */ + uintptr_t addr = tb->jmp_target_addr[n]; + qatomic_set((int32_t *)jmp_rw, addr - (jmp_rx + 4)); + /* no need to flush icache explicitly */ } static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, @@ -2370,36 +2571,6 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const_a2 = const_args[2]; switch (opc) { - case INDEX_op_exit_tb: - /* Reuse the zeroing that exists for goto_ptr. */ - if (a0 == 0) { - tcg_out_jmp(s, tcg_code_gen_epilogue); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_EAX, a0); - tcg_out_jmp(s, tb_ret_addr); - } - break; - case INDEX_op_goto_tb: - if (s->tb_jmp_insn_offset) { - /* direct jump method */ - int gap; - /* jump displacement must be aligned for atomic patching; - * see if we need to add extra nops before jump - */ - gap = QEMU_ALIGN_PTR_UP(s->code_ptr + 1, 4) - s->code_ptr; - if (gap != 1) { - tcg_out_nopn(s, gap - 1); - } - tcg_out8(s, OPC_JMP_long); /* jmp im */ - s->tb_jmp_insn_offset[a0] = tcg_current_code_size(s); - tcg_out32(s, 0); - } else { - /* indirect jump method */ - tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev, -1, - (intptr_t)(s->tb_jmp_target_addr + a0)); - } - set_jmp_reset_offset(s, a0); - break; case INDEX_op_goto_ptr: /* jmp to the given host address (could be epilogue) */ tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, a0); @@ -2582,14 +2753,18 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_modrm(s, OPC_POPCNT + rexw, a0, a1); break; - case INDEX_op_brcond_i32: - tcg_out_brcond32(s, a2, a0, a1, const_args[1], arg_label(args[3]), 0); + OP_32_64(brcond): + tcg_out_brcond(s, rexw, a2, a0, a1, const_args[1], + arg_label(args[3]), 0); break; - case INDEX_op_setcond_i32: - tcg_out_setcond32(s, args[3], a0, a1, a2, const_a2); + OP_32_64(setcond): + tcg_out_setcond(s, rexw, args[3], a0, a1, a2, const_a2, false); break; - case INDEX_op_movcond_i32: - tcg_out_movcond32(s, args[5], a0, a1, a2, const_a2, args[3]); + OP_32_64(negsetcond): + tcg_out_setcond(s, rexw, args[3], a0, a1, a2, const_a2, true); + break; + OP_32_64(movcond): + tcg_out_movcond(s, rexw, args[5], a0, a1, a2, const_a2, args[3]); break; OP_32_64(bswap16): @@ -2624,31 +2799,64 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, a0); break; - OP_32_64(ext8s): - tcg_out_ext8s(s, a0, a1, rexw); + case INDEX_op_qemu_ld_a64_i32: + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_qemu_ld(s, a0, -1, a1, a2, args[3], TCG_TYPE_I32); + break; + } + /* fall through */ + case INDEX_op_qemu_ld_a32_i32: + tcg_out_qemu_ld(s, a0, -1, a1, -1, a2, TCG_TYPE_I32); break; - OP_32_64(ext16s): - tcg_out_ext16s(s, a0, a1, rexw); + case INDEX_op_qemu_ld_a32_i64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_qemu_ld(s, a0, -1, a1, -1, a2, TCG_TYPE_I64); + } else { + tcg_out_qemu_ld(s, a0, a1, a2, -1, args[3], TCG_TYPE_I64); + } break; - OP_32_64(ext8u): - tcg_out_ext8u(s, a0, a1); + case INDEX_op_qemu_ld_a64_i64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_qemu_ld(s, a0, -1, a1, -1, a2, TCG_TYPE_I64); + } else { + tcg_out_qemu_ld(s, a0, a1, a2, args[3], args[4], TCG_TYPE_I64); + } break; - OP_32_64(ext16u): - tcg_out_ext16u(s, a0, a1); + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + tcg_out_qemu_ld(s, a0, a1, a2, -1, args[3], TCG_TYPE_I128); break; - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args, 0); + case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st8_a64_i32: + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_qemu_st(s, a0, -1, a1, a2, args[3], TCG_TYPE_I32); + break; + } + /* fall through */ + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st8_a32_i32: + tcg_out_qemu_st(s, a0, -1, a1, -1, a2, TCG_TYPE_I32); break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args, 1); + case INDEX_op_qemu_st_a32_i64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_qemu_st(s, a0, -1, a1, -1, a2, TCG_TYPE_I64); + } else { + tcg_out_qemu_st(s, a0, a1, a2, -1, args[3], TCG_TYPE_I64); + } break; - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st8_i32: - tcg_out_qemu_st(s, args, 0); + case INDEX_op_qemu_st_a64_i64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_qemu_st(s, a0, -1, a1, -1, a2, TCG_TYPE_I64); + } else { + tcg_out_qemu_st(s, a0, a1, a2, args[3], args[4], TCG_TYPE_I64); + } break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args, 1); + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + tcg_out_qemu_st(s, a0, a1, a2, -1, args[3], TCG_TYPE_I128); break; OP_32_64(mulu2): @@ -2705,28 +2913,9 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, } break; - case INDEX_op_brcond_i64: - tcg_out_brcond64(s, a2, a0, a1, const_args[1], arg_label(args[3]), 0); - break; - case INDEX_op_setcond_i64: - tcg_out_setcond64(s, args[3], a0, a1, a2, const_a2); - break; - case INDEX_op_movcond_i64: - tcg_out_movcond64(s, args[5], a0, a1, a2, const_a2, args[3]); - break; - case INDEX_op_bswap64_i64: tcg_out_bswap64(s, a0); break; - case INDEX_op_extu_i32_i64: - case INDEX_op_ext32u_i64: - case INDEX_op_extrl_i64_i32: - tcg_out_ext32u(s, a0, a1); - break; - case INDEX_op_ext_i32_i64: - case INDEX_op_ext32s_i64: - tcg_out_ext32s(s, a0, a1); - break; case INDEX_op_extrh_i64_i32: tcg_out_shifti(s, SHIFT_SHR + P_REXW, a0, 32); break; @@ -2735,15 +2924,32 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, OP_32_64(deposit): if (args[3] == 0 && args[4] == 8) { /* load bits 0..7 */ - tcg_out_modrm(s, OPC_MOVB_EvGv | P_REXB_R | P_REXB_RM, a2, a0); - } else if (args[3] == 8 && args[4] == 8) { + if (const_a2) { + tcg_out_opc(s, OPC_MOVB_Ib | P_REXB_RM | LOWREGMASK(a0), + 0, a0, 0); + tcg_out8(s, a2); + } else { + tcg_out_modrm(s, OPC_MOVB_EvGv | P_REXB_R | P_REXB_RM, a2, a0); + } + } else if (TCG_TARGET_REG_BITS == 32 && args[3] == 8 && args[4] == 8) { /* load bits 8..15 */ - tcg_out_modrm(s, OPC_MOVB_EvGv, a2, a0 + 4); + if (const_a2) { + tcg_out8(s, OPC_MOVB_Ib + a0 + 4); + tcg_out8(s, a2); + } else { + tcg_out_modrm(s, OPC_MOVB_EvGv, a2, a0 + 4); + } } else if (args[3] == 0 && args[4] == 16) { /* load bits 0..15 */ - tcg_out_modrm(s, OPC_MOVL_EvGv | P_DATA16, a2, a0); + if (const_a2) { + tcg_out_opc(s, OPC_MOVL_Iv | P_DATA16 | LOWREGMASK(a0), + 0, a0, 0); + tcg_out16(s, a2); + } else { + tcg_out_modrm(s, OPC_MOVL_EvGv | P_DATA16, a2, a0); + } } else { - tcg_abort(); + g_assert_not_reached(); } break; @@ -2776,7 +2982,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, if (a1 < 4 && a0 < 8) { tcg_out_modrm(s, OPC_MOVSBL, a0, a1 + 4); } else { - tcg_out_ext16s(s, a0, a1, 0); + tcg_out_ext16s(s, TCG_TYPE_I32, a0, a1); tcg_out_shifti(s, SHIFT_SAR, a0, 8); } break; @@ -2793,8 +2999,23 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ + case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ + case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ + case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ + case INDEX_op_ext8s_i64: + case INDEX_op_ext8u_i32: + case INDEX_op_ext8u_i64: + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + case INDEX_op_ext16u_i32: + case INDEX_op_ext16u_i64: + case INDEX_op_ext32s_i64: + case INDEX_op_ext32u_i64: + case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: default: - tcg_abort(); + g_assert_not_reached(); } #undef OP_32_64 @@ -3239,7 +3460,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: - return C_O0_I2(r, re); + return C_O0_I2(r, reT); case INDEX_op_bswap16_i32: case INDEX_op_bswap16_i64: @@ -3281,15 +3502,17 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: - return C_O1_I2(Q, 0, Q); + return C_O1_I2(q, 0, qi); case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: - return C_O1_I2(q, r, re); + case INDEX_op_negsetcond_i32: + case INDEX_op_negsetcond_i64: + return C_O1_I2(q, r, reT); case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: - return C_O1_I4(r, r, re, r, 0); + return C_O1_I4(r, r, reT, r, 0); case INDEX_op_div2_i32: case INDEX_op_div2_i64: @@ -3307,7 +3530,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_add2_i64: case INDEX_op_sub2_i32: case INDEX_op_sub2_i64: - return C_O2_I4(r, r, 0, 1, re, re); + return C_N1_O1_I4(r, r, 0, 1, re, re); case INDEX_op_ctz_i32: case INDEX_op_ctz_i64: @@ -3317,26 +3540,38 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_clz_i64: return have_lzcnt ? C_N1_I2(r, r, rW) : C_N1_I2(r, r, r); - case INDEX_op_qemu_ld_i32: - return (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS - ? C_O1_I1(r, L) : C_O1_I2(r, L, L)); + case INDEX_op_qemu_ld_a32_i32: + return C_O1_I1(r, L); + case INDEX_op_qemu_ld_a64_i32: + return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, L) : C_O1_I2(r, L, L); - case INDEX_op_qemu_st_i32: - return (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS - ? C_O0_I2(L, L) : C_O0_I3(L, L, L)); - case INDEX_op_qemu_st8_i32: - return (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS - ? C_O0_I2(s, L) : C_O0_I3(s, L, L)); + case INDEX_op_qemu_st_a32_i32: + return C_O0_I2(L, L); + case INDEX_op_qemu_st_a64_i32: + return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(L, L) : C_O0_I3(L, L, L); + case INDEX_op_qemu_st8_a32_i32: + return C_O0_I2(s, L); + case INDEX_op_qemu_st8_a64_i32: + return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(s, L) : C_O0_I3(s, L, L); - case INDEX_op_qemu_ld_i64: - return (TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, L) - : TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? C_O2_I1(r, r, L) - : C_O2_I2(r, r, L, L)); + case INDEX_op_qemu_ld_a32_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, L) : C_O2_I1(r, r, L); + case INDEX_op_qemu_ld_a64_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, L) : C_O2_I2(r, r, L, L); - case INDEX_op_qemu_st_i64: - return (TCG_TARGET_REG_BITS == 64 ? C_O0_I2(L, L) - : TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? C_O0_I3(L, L, L) - : C_O0_I4(L, L, L, L)); + case INDEX_op_qemu_st_a32_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(L, L) : C_O0_I3(L, L, L); + case INDEX_op_qemu_st_a64_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(L, L) : C_O0_I4(L, L, L, L); + + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + return C_O2_I1(r, r, L); + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + return C_O0_I3(L, L, L); case INDEX_op_brcond2_i32: return C_O0_I4(r, r, ri, ri); @@ -3602,6 +3837,7 @@ static void expand_vec_sari(TCGType type, unsigned vece, break; case MO_64: + t1 = tcg_temp_new_vec(type); if (imm <= 32) { /* * We can emulate a small sign extend by performing an arithmetic @@ -3610,24 +3846,22 @@ static void expand_vec_sari(TCGType type, unsigned vece, * 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, 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), tcgv_vec_arg(t1), 0xaa); - tcg_temp_free_vec(t1); } else { /* Otherwise we will need to use a compare vs 0 to produce * the sign-extend, shift and merge. */ - t1 = tcg_const_zeros_vec(type); - tcg_gen_cmp_vec(TCG_COND_GT, MO_64, t1, t1, v1); + tcg_gen_cmp_vec(TCG_COND_GT, MO_64, t1, + tcg_constant_vec(type, MO_64, 0), v1); tcg_gen_shri_vec(MO_64, v0, v1, imm); tcg_gen_shli_vec(MO_64, t1, t1, 64 - imm); tcg_gen_or_vec(MO_64, v0, v0, t1); - tcg_temp_free_vec(t1); } + tcg_temp_free_vec(t1); break; default: @@ -4021,35 +4255,35 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_push(s, tcg_target_callee_save_regs[i]); } -#if TCG_TARGET_REG_BITS == 32 - tcg_out_ld(s, TCG_TYPE_PTR, TCG_AREG0, TCG_REG_ESP, - (ARRAY_SIZE(tcg_target_callee_save_regs) + 1) * 4); - tcg_out_addi(s, TCG_REG_ESP, -stack_addend); - /* jmp *tb. */ - tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev, TCG_REG_ESP, - (ARRAY_SIZE(tcg_target_callee_save_regs) + 2) * 4 - + stack_addend); -#else -# if !defined(CONFIG_SOFTMMU) && TCG_TARGET_REG_BITS == 64 - if (guest_base) { + if (!tcg_use_softmmu && guest_base) { int seg = setup_guest_base_seg(); if (seg != 0) { - x86_guest_base_seg = seg; + x86_guest_base.seg = seg; } else if (guest_base == (int32_t)guest_base) { - x86_guest_base_offset = guest_base; + x86_guest_base.ofs = guest_base; } else { + assert(TCG_TARGET_REG_BITS == 64); /* Choose R12 because, as a base, it requires a SIB byte. */ - x86_guest_base_index = TCG_REG_R12; - tcg_out_movi(s, TCG_TYPE_PTR, x86_guest_base_index, guest_base); - tcg_regset_set_reg(s->reserved_regs, x86_guest_base_index); + x86_guest_base.index = TCG_REG_R12; + tcg_out_movi(s, TCG_TYPE_PTR, x86_guest_base.index, guest_base); + tcg_regset_set_reg(s->reserved_regs, x86_guest_base.index); } } -# endif - tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); - tcg_out_addi(s, TCG_REG_ESP, -stack_addend); - /* jmp *tb. */ - tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, tcg_target_call_iarg_regs[1]); -#endif + + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_ld(s, TCG_TYPE_PTR, TCG_AREG0, TCG_REG_ESP, + (ARRAY_SIZE(tcg_target_callee_save_regs) + 1) * 4); + tcg_out_addi(s, TCG_REG_ESP, -stack_addend); + /* jmp *tb. */ + tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev, TCG_REG_ESP, + (ARRAY_SIZE(tcg_target_callee_save_regs) + 2) * 4 + + stack_addend); + } else { + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + tcg_out_addi(s, TCG_REG_ESP, -stack_addend); + /* jmp *tb. */ + tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, tcg_target_call_iarg_regs[1]); + } /* * Return path for goto_ptr. Set return value to 0, a-la exit_tb, @@ -4072,6 +4306,11 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_opc(s, OPC_RET, 0, 0, 0); } +static void tcg_out_tb_start(TCGContext *s) +{ + /* nothing to do */ +} + static void tcg_out_nop_fill(tcg_insn_unit *p, int count) { memset(p, 0x90, count); @@ -4079,70 +4318,6 @@ static void tcg_out_nop_fill(tcg_insn_unit *p, int count) static void tcg_target_init(TCGContext *s) { -#ifdef CONFIG_CPUID_H - unsigned a, b, c, d, b7 = 0, c7 = 0; - unsigned max = __get_cpuid_max(0, 0); - - if (max >= 7) { - /* BMI1 is available on AMD Piledriver and Intel Haswell CPUs. */ - __cpuid_count(7, 0, a, b7, c7, d); - have_bmi1 = (b7 & bit_BMI) != 0; - have_bmi2 = (b7 & bit_BMI2) != 0; - } - - if (max >= 1) { - __cpuid(1, a, b, c, d); -#ifndef have_cmov - /* For 32-bit, 99% certainty that we're running on hardware that - supports cmov, but we still need to check. In case cmov is not - available, we'll use a small forward branch. */ - have_cmov = (d & bit_CMOV) != 0; -#endif - - /* MOVBE is only available on Intel Atom and Haswell CPUs, so we - need to probe for it. */ - have_movbe = (c & bit_MOVBE) != 0; - have_popcnt = (c & bit_POPCNT) != 0; - - /* There are a number of things we must check before we can be - sure of not hitting invalid opcode. */ - if (c & bit_OSXSAVE) { - unsigned xcrl, xcrh; - /* The xgetbv instruction is not available to older versions of - * the assembler, so we encode the instruction manually. - */ - asm(".byte 0x0f, 0x01, 0xd0" : "=a" (xcrl), "=d" (xcrh) : "c" (0)); - if ((xcrl & 6) == 6) { - have_avx1 = (c & bit_AVX) != 0; - have_avx2 = (b7 & bit_AVX2) != 0; - - /* - * There are interesting instructions in AVX512, so long - * as we have AVX512VL, which indicates support for EVEX - * on sizes smaller than 512 bits. We are required to - * check that OPMASK and all extended ZMM state are enabled - * even if we're not using them -- the insns will fault. - */ - if ((xcrl & 0xe0) == 0xe0 - && (b7 & bit_AVX512F) - && (b7 & bit_AVX512VL)) { - have_avx512vl = true; - have_avx512bw = (b7 & bit_AVX512BW) != 0; - have_avx512dq = (b7 & bit_AVX512DQ) != 0; - have_avx512vbmi2 = (c7 & bit_AVX512VBMI2) != 0; - } - } - } - } - - max = __get_cpuid_max(0x8000000, 0); - if (max >= 1) { - __cpuid(0x80000001, a, b, c, d); - /* LZCNT was introduced with AMD Barcelona and Intel Haswell CPUs. */ - have_lzcnt = (c & bit_LZCNT) != 0; - } -#endif /* CONFIG_CPUID_H */ - tcg_target_available_regs[TCG_TYPE_I32] = ALL_GENERAL_REGS; if (TCG_TARGET_REG_BITS == 64) { tcg_target_available_regs[TCG_TYPE_I64] = ALL_GENERAL_REGS; @@ -4172,6 +4347,20 @@ static void tcg_target_init(TCGContext *s) s->reserved_regs = 0; tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); + tcg_regset_set_reg(s->reserved_regs, TCG_TMP_VEC); +#ifdef _WIN64 + /* These are call saved, and we don't save them, so don't use them. */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_XMM6); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_XMM7); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_XMM8); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_XMM9); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_XMM10); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_XMM11); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_XMM12); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_XMM13); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_XMM14); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_XMM15); +#endif } typedef struct { diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index 00fcbe297d..a10d4e1fce 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -25,15 +25,14 @@ #ifndef I386_TCG_TARGET_H #define I386_TCG_TARGET_H +#include "host/cpuinfo.h" + #define TCG_TARGET_INSN_UNIT_SIZE 1 -#define TCG_TARGET_TLB_DISPLACEMENT_BITS 31 #ifdef __x86_64__ -# define TCG_TARGET_REG_BITS 64 # define TCG_TARGET_NB_REGS 32 # define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB) #else -# define TCG_TARGET_REG_BITS 32 # define TCG_TARGET_NB_REGS 24 # define MAX_CODE_GEN_BUFFER_SIZE UINT32_MAX #endif @@ -98,16 +97,34 @@ typedef enum { #else #define TCG_TARGET_CALL_STACK_OFFSET 0 #endif +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +#if defined(_WIN64) +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_BY_REF +# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_VEC +#elif TCG_TARGET_REG_BITS == 64 +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL +# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL +#else +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL +# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF +#endif -extern bool have_bmi1; -extern bool have_popcnt; -extern bool have_avx1; -extern bool have_avx2; -extern bool have_avx512bw; -extern bool have_avx512dq; -extern bool have_avx512vbmi2; -extern bool have_avx512vl; -extern bool have_movbe; +#define have_bmi1 (cpuinfo & CPUINFO_BMI1) +#define have_popcnt (cpuinfo & CPUINFO_POPCNT) +#define have_avx1 (cpuinfo & CPUINFO_AVX1) +#define have_avx2 (cpuinfo & CPUINFO_AVX2) +#define have_movbe (cpuinfo & CPUINFO_MOVBE) + +/* + * There are interesting instructions in AVX512, so long as we have AVX512VL, + * which indicates support for EVEX on sizes smaller than 512 bits. + */ +#define have_avx512vl ((cpuinfo & CPUINFO_AVX512VL) && \ + (cpuinfo & CPUINFO_AVX512F)) +#define have_avx512bw ((cpuinfo & CPUINFO_AVX512BW) && have_avx512vl) +#define have_avx512dq ((cpuinfo & CPUINFO_AVX512DQ) && have_avx512vl) +#define have_avx512vbmi2 ((cpuinfo & CPUINFO_AVX512VBMI2) && have_avx512vl) /* optional instructions */ #define TCG_TARGET_HAS_div2_i32 1 @@ -118,7 +135,6 @@ extern bool have_movbe; #define TCG_TARGET_HAS_ext16u_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_andc_i32 have_bmi1 #define TCG_TARGET_HAS_orc_i32 0 @@ -132,19 +148,17 @@ extern bool have_movbe; #define TCG_TARGET_HAS_extract_i32 1 #define TCG_TARGET_HAS_sextract_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 -#define TCG_TARGET_HAS_movcond_i32 1 +#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_muls2_i32 1 #define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_direct_jump 1 #if TCG_TARGET_REG_BITS == 64 -/* Keep target addresses zero-extended in a register. */ -#define TCG_TARGET_HAS_extrl_i64_i32 (TARGET_LONG_BITS == 32) -#define TCG_TARGET_HAS_extrh_i64_i32 (TARGET_LONG_BITS == 32) +/* Keep 32-bit values zero-extended in a register. */ +#define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_div2_i64 1 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_ext8s_i64 1 @@ -156,7 +170,6 @@ extern bool have_movbe; #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_andc_i64 have_bmi1 #define TCG_TARGET_HAS_orc_i64 0 @@ -170,7 +183,7 @@ extern bool have_movbe; #define TCG_TARGET_HAS_extract_i64 1 #define TCG_TARGET_HAS_sextract_i64 0 #define TCG_TARGET_HAS_extract2_i64 1 -#define TCG_TARGET_HAS_movcond_i64 1 +#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 1 @@ -182,6 +195,11 @@ extern bool have_movbe; #define TCG_TARGET_HAS_qemu_st8_i32 1 #endif +#define TCG_TARGET_HAS_qemu_ldst_i128 \ + (TCG_TARGET_REG_BITS == 64 && (cpuinfo & CPUINFO_ATOMIC_VMOVDQA)) + +#define TCG_TARGET_HAS_tst 1 + /* We do not support older SSE systems, only beginning with AVX1. */ #define TCG_TARGET_HAS_v64 have_avx1 #define TCG_TARGET_HAS_v128 have_avx1 @@ -208,8 +226,8 @@ extern bool have_movbe; #define TCG_TARGET_HAS_cmpsel_vec -1 #define TCG_TARGET_deposit_i32_valid(ofs, len) \ - (((ofs) == 0 && (len) == 8) || ((ofs) == 8 && (len) == 8) || \ - ((ofs) == 0 && (len) == 16)) + (((ofs) == 0 && ((len) == 8 || (len) == 16)) || \ + (TCG_TARGET_REG_BITS == 32 && (ofs) == 8 && (len) == 8)) #define TCG_TARGET_deposit_i64_valid TCG_TARGET_deposit_i32_valid /* Check for the possibility of high-byte extraction and, for 64-bit, @@ -218,14 +236,6 @@ extern bool have_movbe; #define TCG_TARGET_extract_i64_valid(ofs, len) \ (((ofs) == 8 && (len) == 8) || ((ofs) + (len)) == 32) -static inline void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx, - uintptr_t jmp_rw, uintptr_t addr) -{ - /* patch the branch destination */ - qatomic_set((int32_t *)jmp_rw, addr - (jmp_rx + 4)); - /* no need to flush icache explicitly */ -} - /* This defines the natural memory order supported by this * architecture before guarantees made by various barrier * instructions. @@ -236,9 +246,6 @@ static inline void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx, #include "tcg/tcg-mo.h" #define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) - -#define TCG_TARGET_HAS_MEMORY_BSWAP have_movbe - #define TCG_TARGET_NEED_LDST_LABELS #define TCG_TARGET_NEED_POOL_LABELS diff --git a/tcg/loongarch64/tcg-insn-defs.c.inc b/tcg/loongarch64/tcg-insn-defs.c.inc index d162571856..ee3b483b02 100644 --- a/tcg/loongarch64/tcg-insn-defs.c.inc +++ b/tcg/loongarch64/tcg-insn-defs.c.inc @@ -4,7 +4,7 @@ * * This file is auto-generated by genqemutcgdefs from * https://github.com/loongson-community/loongarch-opcodes, - * from commit 961f0c60f5b63e574d785995600c71ad5413fdc4. + * from commit 8027da9a8157a8b47fc48ff1def292e09c5668bd. * DO NOT EDIT. */ @@ -74,6 +74,61 @@ typedef enum { OPC_ANDI = 0x03400000, OPC_ORI = 0x03800000, OPC_XORI = 0x03c00000, + OPC_VFMADD_S = 0x09100000, + OPC_VFMADD_D = 0x09200000, + OPC_VFMSUB_S = 0x09500000, + OPC_VFMSUB_D = 0x09600000, + OPC_VFNMADD_S = 0x09900000, + OPC_VFNMADD_D = 0x09a00000, + OPC_VFNMSUB_S = 0x09d00000, + OPC_VFNMSUB_D = 0x09e00000, + OPC_VFCMP_CAF_S = 0x0c500000, + OPC_VFCMP_SAF_S = 0x0c508000, + OPC_VFCMP_CLT_S = 0x0c510000, + OPC_VFCMP_SLT_S = 0x0c518000, + OPC_VFCMP_CEQ_S = 0x0c520000, + OPC_VFCMP_SEQ_S = 0x0c528000, + OPC_VFCMP_CLE_S = 0x0c530000, + OPC_VFCMP_SLE_S = 0x0c538000, + OPC_VFCMP_CUN_S = 0x0c540000, + OPC_VFCMP_SUN_S = 0x0c548000, + OPC_VFCMP_CULT_S = 0x0c550000, + OPC_VFCMP_SULT_S = 0x0c558000, + OPC_VFCMP_CUEQ_S = 0x0c560000, + OPC_VFCMP_SUEQ_S = 0x0c568000, + OPC_VFCMP_CULE_S = 0x0c570000, + OPC_VFCMP_SULE_S = 0x0c578000, + OPC_VFCMP_CNE_S = 0x0c580000, + OPC_VFCMP_SNE_S = 0x0c588000, + OPC_VFCMP_COR_S = 0x0c5a0000, + OPC_VFCMP_SOR_S = 0x0c5a8000, + OPC_VFCMP_CUNE_S = 0x0c5c0000, + OPC_VFCMP_SUNE_S = 0x0c5c8000, + OPC_VFCMP_CAF_D = 0x0c600000, + OPC_VFCMP_SAF_D = 0x0c608000, + OPC_VFCMP_CLT_D = 0x0c610000, + OPC_VFCMP_SLT_D = 0x0c618000, + OPC_VFCMP_CEQ_D = 0x0c620000, + OPC_VFCMP_SEQ_D = 0x0c628000, + OPC_VFCMP_CLE_D = 0x0c630000, + OPC_VFCMP_SLE_D = 0x0c638000, + OPC_VFCMP_CUN_D = 0x0c640000, + OPC_VFCMP_SUN_D = 0x0c648000, + OPC_VFCMP_CULT_D = 0x0c650000, + OPC_VFCMP_SULT_D = 0x0c658000, + OPC_VFCMP_CUEQ_D = 0x0c660000, + OPC_VFCMP_SUEQ_D = 0x0c668000, + OPC_VFCMP_CULE_D = 0x0c670000, + OPC_VFCMP_SULE_D = 0x0c678000, + OPC_VFCMP_CNE_D = 0x0c680000, + OPC_VFCMP_SNE_D = 0x0c688000, + OPC_VFCMP_COR_D = 0x0c6a0000, + OPC_VFCMP_SOR_D = 0x0c6a8000, + OPC_VFCMP_CUNE_D = 0x0c6c0000, + OPC_VFCMP_SUNE_D = 0x0c6c8000, + OPC_VBITSEL_V = 0x0d100000, + OPC_VSHUF_B = 0x0d500000, + OPC_ADDU16I_D = 0x10000000, OPC_LU12I_W = 0x14000000, OPC_CU32I_D = 0x16000000, OPC_PCADDU2I = 0x18000000, @@ -91,6 +146,16 @@ typedef enum { OPC_LD_BU = 0x2a000000, OPC_LD_HU = 0x2a400000, OPC_LD_WU = 0x2a800000, + OPC_VLD = 0x2c000000, + OPC_VST = 0x2c400000, + OPC_VLDREPL_D = 0x30100000, + OPC_VLDREPL_W = 0x30200000, + OPC_VLDREPL_H = 0x30400000, + OPC_VLDREPL_B = 0x30800000, + OPC_VSTELM_D = 0x31100000, + OPC_VSTELM_W = 0x31200000, + OPC_VSTELM_H = 0x31400000, + OPC_VSTELM_B = 0x31800000, OPC_LDX_B = 0x38000000, OPC_LDX_H = 0x38040000, OPC_LDX_W = 0x38080000, @@ -102,6 +167,8 @@ typedef enum { OPC_LDX_BU = 0x38200000, OPC_LDX_HU = 0x38240000, OPC_LDX_WU = 0x38280000, + OPC_VLDX = 0x38400000, + OPC_VSTX = 0x38440000, OPC_DBAR = 0x38720000, OPC_JIRL = 0x4c000000, OPC_B = 0x50000000, @@ -112,6 +179,652 @@ typedef enum { OPC_BLE = 0x64000000, OPC_BGTU = 0x68000000, OPC_BLEU = 0x6c000000, + OPC_VSEQ_B = 0x70000000, + OPC_VSEQ_H = 0x70008000, + OPC_VSEQ_W = 0x70010000, + OPC_VSEQ_D = 0x70018000, + OPC_VSLE_B = 0x70020000, + OPC_VSLE_H = 0x70028000, + OPC_VSLE_W = 0x70030000, + OPC_VSLE_D = 0x70038000, + OPC_VSLE_BU = 0x70040000, + OPC_VSLE_HU = 0x70048000, + OPC_VSLE_WU = 0x70050000, + OPC_VSLE_DU = 0x70058000, + OPC_VSLT_B = 0x70060000, + OPC_VSLT_H = 0x70068000, + OPC_VSLT_W = 0x70070000, + OPC_VSLT_D = 0x70078000, + OPC_VSLT_BU = 0x70080000, + OPC_VSLT_HU = 0x70088000, + OPC_VSLT_WU = 0x70090000, + OPC_VSLT_DU = 0x70098000, + OPC_VADD_B = 0x700a0000, + OPC_VADD_H = 0x700a8000, + OPC_VADD_W = 0x700b0000, + OPC_VADD_D = 0x700b8000, + OPC_VSUB_B = 0x700c0000, + OPC_VSUB_H = 0x700c8000, + OPC_VSUB_W = 0x700d0000, + OPC_VSUB_D = 0x700d8000, + OPC_VADDWEV_H_B = 0x701e0000, + OPC_VADDWEV_W_H = 0x701e8000, + OPC_VADDWEV_D_W = 0x701f0000, + OPC_VADDWEV_Q_D = 0x701f8000, + OPC_VSUBWEV_H_B = 0x70200000, + OPC_VSUBWEV_W_H = 0x70208000, + OPC_VSUBWEV_D_W = 0x70210000, + OPC_VSUBWEV_Q_D = 0x70218000, + OPC_VADDWOD_H_B = 0x70220000, + OPC_VADDWOD_W_H = 0x70228000, + OPC_VADDWOD_D_W = 0x70230000, + OPC_VADDWOD_Q_D = 0x70238000, + OPC_VSUBWOD_H_B = 0x70240000, + OPC_VSUBWOD_W_H = 0x70248000, + OPC_VSUBWOD_D_W = 0x70250000, + OPC_VSUBWOD_Q_D = 0x70258000, + OPC_VADDWEV_H_BU = 0x702e0000, + OPC_VADDWEV_W_HU = 0x702e8000, + OPC_VADDWEV_D_WU = 0x702f0000, + OPC_VADDWEV_Q_DU = 0x702f8000, + OPC_VSUBWEV_H_BU = 0x70300000, + OPC_VSUBWEV_W_HU = 0x70308000, + OPC_VSUBWEV_D_WU = 0x70310000, + OPC_VSUBWEV_Q_DU = 0x70318000, + OPC_VADDWOD_H_BU = 0x70320000, + OPC_VADDWOD_W_HU = 0x70328000, + OPC_VADDWOD_D_WU = 0x70330000, + OPC_VADDWOD_Q_DU = 0x70338000, + OPC_VSUBWOD_H_BU = 0x70340000, + OPC_VSUBWOD_W_HU = 0x70348000, + OPC_VSUBWOD_D_WU = 0x70350000, + OPC_VSUBWOD_Q_DU = 0x70358000, + OPC_VADDWEV_H_BU_B = 0x703e0000, + OPC_VADDWEV_W_HU_H = 0x703e8000, + OPC_VADDWEV_D_WU_W = 0x703f0000, + OPC_VADDWEV_Q_DU_D = 0x703f8000, + OPC_VADDWOD_H_BU_B = 0x70400000, + OPC_VADDWOD_W_HU_H = 0x70408000, + OPC_VADDWOD_D_WU_W = 0x70410000, + OPC_VADDWOD_Q_DU_D = 0x70418000, + OPC_VSADD_B = 0x70460000, + OPC_VSADD_H = 0x70468000, + OPC_VSADD_W = 0x70470000, + OPC_VSADD_D = 0x70478000, + OPC_VSSUB_B = 0x70480000, + OPC_VSSUB_H = 0x70488000, + OPC_VSSUB_W = 0x70490000, + OPC_VSSUB_D = 0x70498000, + OPC_VSADD_BU = 0x704a0000, + OPC_VSADD_HU = 0x704a8000, + OPC_VSADD_WU = 0x704b0000, + OPC_VSADD_DU = 0x704b8000, + OPC_VSSUB_BU = 0x704c0000, + OPC_VSSUB_HU = 0x704c8000, + OPC_VSSUB_WU = 0x704d0000, + OPC_VSSUB_DU = 0x704d8000, + OPC_VHADDW_H_B = 0x70540000, + OPC_VHADDW_W_H = 0x70548000, + OPC_VHADDW_D_W = 0x70550000, + OPC_VHADDW_Q_D = 0x70558000, + OPC_VHSUBW_H_B = 0x70560000, + OPC_VHSUBW_W_H = 0x70568000, + OPC_VHSUBW_D_W = 0x70570000, + OPC_VHSUBW_Q_D = 0x70578000, + OPC_VHADDW_HU_BU = 0x70580000, + OPC_VHADDW_WU_HU = 0x70588000, + OPC_VHADDW_DU_WU = 0x70590000, + OPC_VHADDW_QU_DU = 0x70598000, + OPC_VHSUBW_HU_BU = 0x705a0000, + OPC_VHSUBW_WU_HU = 0x705a8000, + OPC_VHSUBW_DU_WU = 0x705b0000, + OPC_VHSUBW_QU_DU = 0x705b8000, + OPC_VADDA_B = 0x705c0000, + OPC_VADDA_H = 0x705c8000, + OPC_VADDA_W = 0x705d0000, + OPC_VADDA_D = 0x705d8000, + OPC_VABSD_B = 0x70600000, + OPC_VABSD_H = 0x70608000, + OPC_VABSD_W = 0x70610000, + OPC_VABSD_D = 0x70618000, + OPC_VABSD_BU = 0x70620000, + OPC_VABSD_HU = 0x70628000, + OPC_VABSD_WU = 0x70630000, + OPC_VABSD_DU = 0x70638000, + OPC_VAVG_B = 0x70640000, + OPC_VAVG_H = 0x70648000, + OPC_VAVG_W = 0x70650000, + OPC_VAVG_D = 0x70658000, + OPC_VAVG_BU = 0x70660000, + OPC_VAVG_HU = 0x70668000, + OPC_VAVG_WU = 0x70670000, + OPC_VAVG_DU = 0x70678000, + OPC_VAVGR_B = 0x70680000, + OPC_VAVGR_H = 0x70688000, + OPC_VAVGR_W = 0x70690000, + OPC_VAVGR_D = 0x70698000, + OPC_VAVGR_BU = 0x706a0000, + OPC_VAVGR_HU = 0x706a8000, + OPC_VAVGR_WU = 0x706b0000, + OPC_VAVGR_DU = 0x706b8000, + OPC_VMAX_B = 0x70700000, + OPC_VMAX_H = 0x70708000, + OPC_VMAX_W = 0x70710000, + OPC_VMAX_D = 0x70718000, + OPC_VMIN_B = 0x70720000, + OPC_VMIN_H = 0x70728000, + OPC_VMIN_W = 0x70730000, + OPC_VMIN_D = 0x70738000, + OPC_VMAX_BU = 0x70740000, + OPC_VMAX_HU = 0x70748000, + OPC_VMAX_WU = 0x70750000, + OPC_VMAX_DU = 0x70758000, + OPC_VMIN_BU = 0x70760000, + OPC_VMIN_HU = 0x70768000, + OPC_VMIN_WU = 0x70770000, + OPC_VMIN_DU = 0x70778000, + OPC_VMUL_B = 0x70840000, + OPC_VMUL_H = 0x70848000, + OPC_VMUL_W = 0x70850000, + OPC_VMUL_D = 0x70858000, + OPC_VMUH_B = 0x70860000, + OPC_VMUH_H = 0x70868000, + OPC_VMUH_W = 0x70870000, + OPC_VMUH_D = 0x70878000, + OPC_VMUH_BU = 0x70880000, + OPC_VMUH_HU = 0x70888000, + OPC_VMUH_WU = 0x70890000, + OPC_VMUH_DU = 0x70898000, + OPC_VMULWEV_H_B = 0x70900000, + OPC_VMULWEV_W_H = 0x70908000, + OPC_VMULWEV_D_W = 0x70910000, + OPC_VMULWEV_Q_D = 0x70918000, + OPC_VMULWOD_H_B = 0x70920000, + OPC_VMULWOD_W_H = 0x70928000, + OPC_VMULWOD_D_W = 0x70930000, + OPC_VMULWOD_Q_D = 0x70938000, + OPC_VMULWEV_H_BU = 0x70980000, + OPC_VMULWEV_W_HU = 0x70988000, + OPC_VMULWEV_D_WU = 0x70990000, + OPC_VMULWEV_Q_DU = 0x70998000, + OPC_VMULWOD_H_BU = 0x709a0000, + OPC_VMULWOD_W_HU = 0x709a8000, + OPC_VMULWOD_D_WU = 0x709b0000, + OPC_VMULWOD_Q_DU = 0x709b8000, + OPC_VMULWEV_H_BU_B = 0x70a00000, + OPC_VMULWEV_W_HU_H = 0x70a08000, + OPC_VMULWEV_D_WU_W = 0x70a10000, + OPC_VMULWEV_Q_DU_D = 0x70a18000, + OPC_VMULWOD_H_BU_B = 0x70a20000, + OPC_VMULWOD_W_HU_H = 0x70a28000, + OPC_VMULWOD_D_WU_W = 0x70a30000, + OPC_VMULWOD_Q_DU_D = 0x70a38000, + OPC_VMADD_B = 0x70a80000, + OPC_VMADD_H = 0x70a88000, + OPC_VMADD_W = 0x70a90000, + OPC_VMADD_D = 0x70a98000, + OPC_VMSUB_B = 0x70aa0000, + OPC_VMSUB_H = 0x70aa8000, + OPC_VMSUB_W = 0x70ab0000, + OPC_VMSUB_D = 0x70ab8000, + OPC_VMADDWEV_H_B = 0x70ac0000, + OPC_VMADDWEV_W_H = 0x70ac8000, + OPC_VMADDWEV_D_W = 0x70ad0000, + OPC_VMADDWEV_Q_D = 0x70ad8000, + OPC_VMADDWOD_H_B = 0x70ae0000, + OPC_VMADDWOD_W_H = 0x70ae8000, + OPC_VMADDWOD_D_W = 0x70af0000, + OPC_VMADDWOD_Q_D = 0x70af8000, + OPC_VMADDWEV_H_BU = 0x70b40000, + OPC_VMADDWEV_W_HU = 0x70b48000, + OPC_VMADDWEV_D_WU = 0x70b50000, + OPC_VMADDWEV_Q_DU = 0x70b58000, + OPC_VMADDWOD_H_BU = 0x70b60000, + OPC_VMADDWOD_W_HU = 0x70b68000, + OPC_VMADDWOD_D_WU = 0x70b70000, + OPC_VMADDWOD_Q_DU = 0x70b78000, + OPC_VMADDWEV_H_BU_B = 0x70bc0000, + OPC_VMADDWEV_W_HU_H = 0x70bc8000, + OPC_VMADDWEV_D_WU_W = 0x70bd0000, + OPC_VMADDWEV_Q_DU_D = 0x70bd8000, + OPC_VMADDWOD_H_BU_B = 0x70be0000, + OPC_VMADDWOD_W_HU_H = 0x70be8000, + OPC_VMADDWOD_D_WU_W = 0x70bf0000, + OPC_VMADDWOD_Q_DU_D = 0x70bf8000, + OPC_VDIV_B = 0x70e00000, + OPC_VDIV_H = 0x70e08000, + OPC_VDIV_W = 0x70e10000, + OPC_VDIV_D = 0x70e18000, + OPC_VMOD_B = 0x70e20000, + OPC_VMOD_H = 0x70e28000, + OPC_VMOD_W = 0x70e30000, + OPC_VMOD_D = 0x70e38000, + OPC_VDIV_BU = 0x70e40000, + OPC_VDIV_HU = 0x70e48000, + OPC_VDIV_WU = 0x70e50000, + OPC_VDIV_DU = 0x70e58000, + OPC_VMOD_BU = 0x70e60000, + OPC_VMOD_HU = 0x70e68000, + OPC_VMOD_WU = 0x70e70000, + OPC_VMOD_DU = 0x70e78000, + OPC_VSLL_B = 0x70e80000, + OPC_VSLL_H = 0x70e88000, + OPC_VSLL_W = 0x70e90000, + OPC_VSLL_D = 0x70e98000, + OPC_VSRL_B = 0x70ea0000, + OPC_VSRL_H = 0x70ea8000, + OPC_VSRL_W = 0x70eb0000, + OPC_VSRL_D = 0x70eb8000, + OPC_VSRA_B = 0x70ec0000, + OPC_VSRA_H = 0x70ec8000, + OPC_VSRA_W = 0x70ed0000, + OPC_VSRA_D = 0x70ed8000, + OPC_VROTR_B = 0x70ee0000, + OPC_VROTR_H = 0x70ee8000, + OPC_VROTR_W = 0x70ef0000, + OPC_VROTR_D = 0x70ef8000, + OPC_VSRLR_B = 0x70f00000, + OPC_VSRLR_H = 0x70f08000, + OPC_VSRLR_W = 0x70f10000, + OPC_VSRLR_D = 0x70f18000, + OPC_VSRAR_B = 0x70f20000, + OPC_VSRAR_H = 0x70f28000, + OPC_VSRAR_W = 0x70f30000, + OPC_VSRAR_D = 0x70f38000, + OPC_VSRLN_B_H = 0x70f48000, + OPC_VSRLN_H_W = 0x70f50000, + OPC_VSRLN_W_D = 0x70f58000, + OPC_VSRAN_B_H = 0x70f68000, + OPC_VSRAN_H_W = 0x70f70000, + OPC_VSRAN_W_D = 0x70f78000, + OPC_VSRLRN_B_H = 0x70f88000, + OPC_VSRLRN_H_W = 0x70f90000, + OPC_VSRLRN_W_D = 0x70f98000, + OPC_VSRARN_B_H = 0x70fa8000, + OPC_VSRARN_H_W = 0x70fb0000, + OPC_VSRARN_W_D = 0x70fb8000, + OPC_VSSRLN_B_H = 0x70fc8000, + OPC_VSSRLN_H_W = 0x70fd0000, + OPC_VSSRLN_W_D = 0x70fd8000, + OPC_VSSRAN_B_H = 0x70fe8000, + OPC_VSSRAN_H_W = 0x70ff0000, + OPC_VSSRAN_W_D = 0x70ff8000, + OPC_VSSRLRN_B_H = 0x71008000, + OPC_VSSRLRN_H_W = 0x71010000, + OPC_VSSRLRN_W_D = 0x71018000, + OPC_VSSRARN_B_H = 0x71028000, + OPC_VSSRARN_H_W = 0x71030000, + OPC_VSSRARN_W_D = 0x71038000, + OPC_VSSRLN_BU_H = 0x71048000, + OPC_VSSRLN_HU_W = 0x71050000, + OPC_VSSRLN_WU_D = 0x71058000, + OPC_VSSRAN_BU_H = 0x71068000, + OPC_VSSRAN_HU_W = 0x71070000, + OPC_VSSRAN_WU_D = 0x71078000, + OPC_VSSRLRN_BU_H = 0x71088000, + OPC_VSSRLRN_HU_W = 0x71090000, + OPC_VSSRLRN_WU_D = 0x71098000, + OPC_VSSRARN_BU_H = 0x710a8000, + OPC_VSSRARN_HU_W = 0x710b0000, + OPC_VSSRARN_WU_D = 0x710b8000, + OPC_VBITCLR_B = 0x710c0000, + OPC_VBITCLR_H = 0x710c8000, + OPC_VBITCLR_W = 0x710d0000, + OPC_VBITCLR_D = 0x710d8000, + OPC_VBITSET_B = 0x710e0000, + OPC_VBITSET_H = 0x710e8000, + OPC_VBITSET_W = 0x710f0000, + OPC_VBITSET_D = 0x710f8000, + OPC_VBITREV_B = 0x71100000, + OPC_VBITREV_H = 0x71108000, + OPC_VBITREV_W = 0x71110000, + OPC_VBITREV_D = 0x71118000, + OPC_VPACKEV_B = 0x71160000, + OPC_VPACKEV_H = 0x71168000, + OPC_VPACKEV_W = 0x71170000, + OPC_VPACKEV_D = 0x71178000, + OPC_VPACKOD_B = 0x71180000, + OPC_VPACKOD_H = 0x71188000, + OPC_VPACKOD_W = 0x71190000, + OPC_VPACKOD_D = 0x71198000, + OPC_VILVL_B = 0x711a0000, + OPC_VILVL_H = 0x711a8000, + OPC_VILVL_W = 0x711b0000, + OPC_VILVL_D = 0x711b8000, + OPC_VILVH_B = 0x711c0000, + OPC_VILVH_H = 0x711c8000, + OPC_VILVH_W = 0x711d0000, + OPC_VILVH_D = 0x711d8000, + OPC_VPICKEV_B = 0x711e0000, + OPC_VPICKEV_H = 0x711e8000, + OPC_VPICKEV_W = 0x711f0000, + OPC_VPICKEV_D = 0x711f8000, + OPC_VPICKOD_B = 0x71200000, + OPC_VPICKOD_H = 0x71208000, + OPC_VPICKOD_W = 0x71210000, + OPC_VPICKOD_D = 0x71218000, + OPC_VREPLVE_B = 0x71220000, + OPC_VREPLVE_H = 0x71228000, + OPC_VREPLVE_W = 0x71230000, + OPC_VREPLVE_D = 0x71238000, + OPC_VAND_V = 0x71260000, + OPC_VOR_V = 0x71268000, + OPC_VXOR_V = 0x71270000, + OPC_VNOR_V = 0x71278000, + OPC_VANDN_V = 0x71280000, + OPC_VORN_V = 0x71288000, + OPC_VFRSTP_B = 0x712b0000, + OPC_VFRSTP_H = 0x712b8000, + OPC_VADD_Q = 0x712d0000, + OPC_VSUB_Q = 0x712d8000, + OPC_VSIGNCOV_B = 0x712e0000, + OPC_VSIGNCOV_H = 0x712e8000, + OPC_VSIGNCOV_W = 0x712f0000, + OPC_VSIGNCOV_D = 0x712f8000, + OPC_VFADD_S = 0x71308000, + OPC_VFADD_D = 0x71310000, + OPC_VFSUB_S = 0x71328000, + OPC_VFSUB_D = 0x71330000, + OPC_VFMUL_S = 0x71388000, + OPC_VFMUL_D = 0x71390000, + OPC_VFDIV_S = 0x713a8000, + OPC_VFDIV_D = 0x713b0000, + OPC_VFMAX_S = 0x713c8000, + OPC_VFMAX_D = 0x713d0000, + OPC_VFMIN_S = 0x713e8000, + OPC_VFMIN_D = 0x713f0000, + OPC_VFMAXA_S = 0x71408000, + OPC_VFMAXA_D = 0x71410000, + OPC_VFMINA_S = 0x71428000, + OPC_VFMINA_D = 0x71430000, + OPC_VFCVT_H_S = 0x71460000, + OPC_VFCVT_S_D = 0x71468000, + OPC_VFFINT_S_L = 0x71480000, + OPC_VFTINT_W_D = 0x71498000, + OPC_VFTINTRM_W_D = 0x714a0000, + OPC_VFTINTRP_W_D = 0x714a8000, + OPC_VFTINTRZ_W_D = 0x714b0000, + OPC_VFTINTRNE_W_D = 0x714b8000, + OPC_VSHUF_H = 0x717a8000, + OPC_VSHUF_W = 0x717b0000, + OPC_VSHUF_D = 0x717b8000, + OPC_VSEQI_B = 0x72800000, + OPC_VSEQI_H = 0x72808000, + OPC_VSEQI_W = 0x72810000, + OPC_VSEQI_D = 0x72818000, + OPC_VSLEI_B = 0x72820000, + OPC_VSLEI_H = 0x72828000, + OPC_VSLEI_W = 0x72830000, + OPC_VSLEI_D = 0x72838000, + OPC_VSLEI_BU = 0x72840000, + OPC_VSLEI_HU = 0x72848000, + OPC_VSLEI_WU = 0x72850000, + OPC_VSLEI_DU = 0x72858000, + OPC_VSLTI_B = 0x72860000, + OPC_VSLTI_H = 0x72868000, + OPC_VSLTI_W = 0x72870000, + OPC_VSLTI_D = 0x72878000, + OPC_VSLTI_BU = 0x72880000, + OPC_VSLTI_HU = 0x72888000, + OPC_VSLTI_WU = 0x72890000, + OPC_VSLTI_DU = 0x72898000, + OPC_VADDI_BU = 0x728a0000, + OPC_VADDI_HU = 0x728a8000, + OPC_VADDI_WU = 0x728b0000, + OPC_VADDI_DU = 0x728b8000, + OPC_VSUBI_BU = 0x728c0000, + OPC_VSUBI_HU = 0x728c8000, + OPC_VSUBI_WU = 0x728d0000, + OPC_VSUBI_DU = 0x728d8000, + OPC_VBSLL_V = 0x728e0000, + OPC_VBSRL_V = 0x728e8000, + OPC_VMAXI_B = 0x72900000, + OPC_VMAXI_H = 0x72908000, + OPC_VMAXI_W = 0x72910000, + OPC_VMAXI_D = 0x72918000, + OPC_VMINI_B = 0x72920000, + OPC_VMINI_H = 0x72928000, + OPC_VMINI_W = 0x72930000, + OPC_VMINI_D = 0x72938000, + OPC_VMAXI_BU = 0x72940000, + OPC_VMAXI_HU = 0x72948000, + OPC_VMAXI_WU = 0x72950000, + OPC_VMAXI_DU = 0x72958000, + OPC_VMINI_BU = 0x72960000, + OPC_VMINI_HU = 0x72968000, + OPC_VMINI_WU = 0x72970000, + OPC_VMINI_DU = 0x72978000, + OPC_VFRSTPI_B = 0x729a0000, + OPC_VFRSTPI_H = 0x729a8000, + OPC_VCLO_B = 0x729c0000, + OPC_VCLO_H = 0x729c0400, + OPC_VCLO_W = 0x729c0800, + OPC_VCLO_D = 0x729c0c00, + OPC_VCLZ_B = 0x729c1000, + OPC_VCLZ_H = 0x729c1400, + OPC_VCLZ_W = 0x729c1800, + OPC_VCLZ_D = 0x729c1c00, + OPC_VPCNT_B = 0x729c2000, + OPC_VPCNT_H = 0x729c2400, + OPC_VPCNT_W = 0x729c2800, + OPC_VPCNT_D = 0x729c2c00, + OPC_VNEG_B = 0x729c3000, + OPC_VNEG_H = 0x729c3400, + OPC_VNEG_W = 0x729c3800, + OPC_VNEG_D = 0x729c3c00, + OPC_VMSKLTZ_B = 0x729c4000, + OPC_VMSKLTZ_H = 0x729c4400, + OPC_VMSKLTZ_W = 0x729c4800, + OPC_VMSKLTZ_D = 0x729c4c00, + OPC_VMSKGEZ_B = 0x729c5000, + OPC_VMSKNZ_B = 0x729c6000, + OPC_VSETEQZ_V = 0x729c9800, + OPC_VSETNEZ_V = 0x729c9c00, + OPC_VSETANYEQZ_B = 0x729ca000, + OPC_VSETANYEQZ_H = 0x729ca400, + OPC_VSETANYEQZ_W = 0x729ca800, + OPC_VSETANYEQZ_D = 0x729cac00, + OPC_VSETALLNEZ_B = 0x729cb000, + OPC_VSETALLNEZ_H = 0x729cb400, + OPC_VSETALLNEZ_W = 0x729cb800, + OPC_VSETALLNEZ_D = 0x729cbc00, + OPC_VFLOGB_S = 0x729cc400, + OPC_VFLOGB_D = 0x729cc800, + OPC_VFCLASS_S = 0x729cd400, + OPC_VFCLASS_D = 0x729cd800, + OPC_VFSQRT_S = 0x729ce400, + OPC_VFSQRT_D = 0x729ce800, + OPC_VFRECIP_S = 0x729cf400, + OPC_VFRECIP_D = 0x729cf800, + OPC_VFRSQRT_S = 0x729d0400, + OPC_VFRSQRT_D = 0x729d0800, + OPC_VFRINT_S = 0x729d3400, + OPC_VFRINT_D = 0x729d3800, + OPC_VFRINTRM_S = 0x729d4400, + OPC_VFRINTRM_D = 0x729d4800, + OPC_VFRINTRP_S = 0x729d5400, + OPC_VFRINTRP_D = 0x729d5800, + OPC_VFRINTRZ_S = 0x729d6400, + OPC_VFRINTRZ_D = 0x729d6800, + OPC_VFRINTRNE_S = 0x729d7400, + OPC_VFRINTRNE_D = 0x729d7800, + OPC_VFCVTL_S_H = 0x729de800, + OPC_VFCVTH_S_H = 0x729dec00, + OPC_VFCVTL_D_S = 0x729df000, + OPC_VFCVTH_D_S = 0x729df400, + OPC_VFFINT_S_W = 0x729e0000, + OPC_VFFINT_S_WU = 0x729e0400, + OPC_VFFINT_D_L = 0x729e0800, + OPC_VFFINT_D_LU = 0x729e0c00, + OPC_VFFINTL_D_W = 0x729e1000, + OPC_VFFINTH_D_W = 0x729e1400, + OPC_VFTINT_W_S = 0x729e3000, + OPC_VFTINT_L_D = 0x729e3400, + OPC_VFTINTRM_W_S = 0x729e3800, + OPC_VFTINTRM_L_D = 0x729e3c00, + OPC_VFTINTRP_W_S = 0x729e4000, + OPC_VFTINTRP_L_D = 0x729e4400, + OPC_VFTINTRZ_W_S = 0x729e4800, + OPC_VFTINTRZ_L_D = 0x729e4c00, + OPC_VFTINTRNE_W_S = 0x729e5000, + OPC_VFTINTRNE_L_D = 0x729e5400, + OPC_VFTINT_WU_S = 0x729e5800, + OPC_VFTINT_LU_D = 0x729e5c00, + OPC_VFTINTRZ_WU_S = 0x729e7000, + OPC_VFTINTRZ_LU_D = 0x729e7400, + OPC_VFTINTL_L_S = 0x729e8000, + OPC_VFTINTH_L_S = 0x729e8400, + OPC_VFTINTRML_L_S = 0x729e8800, + OPC_VFTINTRMH_L_S = 0x729e8c00, + OPC_VFTINTRPL_L_S = 0x729e9000, + OPC_VFTINTRPH_L_S = 0x729e9400, + OPC_VFTINTRZL_L_S = 0x729e9800, + OPC_VFTINTRZH_L_S = 0x729e9c00, + OPC_VFTINTRNEL_L_S = 0x729ea000, + OPC_VFTINTRNEH_L_S = 0x729ea400, + OPC_VEXTH_H_B = 0x729ee000, + OPC_VEXTH_W_H = 0x729ee400, + OPC_VEXTH_D_W = 0x729ee800, + OPC_VEXTH_Q_D = 0x729eec00, + OPC_VEXTH_HU_BU = 0x729ef000, + OPC_VEXTH_WU_HU = 0x729ef400, + OPC_VEXTH_DU_WU = 0x729ef800, + OPC_VEXTH_QU_DU = 0x729efc00, + OPC_VREPLGR2VR_B = 0x729f0000, + OPC_VREPLGR2VR_H = 0x729f0400, + OPC_VREPLGR2VR_W = 0x729f0800, + OPC_VREPLGR2VR_D = 0x729f0c00, + OPC_VROTRI_B = 0x72a02000, + OPC_VROTRI_H = 0x72a04000, + OPC_VROTRI_W = 0x72a08000, + OPC_VROTRI_D = 0x72a10000, + OPC_VSRLRI_B = 0x72a42000, + OPC_VSRLRI_H = 0x72a44000, + OPC_VSRLRI_W = 0x72a48000, + OPC_VSRLRI_D = 0x72a50000, + OPC_VSRARI_B = 0x72a82000, + OPC_VSRARI_H = 0x72a84000, + OPC_VSRARI_W = 0x72a88000, + OPC_VSRARI_D = 0x72a90000, + OPC_VINSGR2VR_B = 0x72eb8000, + OPC_VINSGR2VR_H = 0x72ebc000, + OPC_VINSGR2VR_W = 0x72ebe000, + OPC_VINSGR2VR_D = 0x72ebf000, + OPC_VPICKVE2GR_B = 0x72ef8000, + OPC_VPICKVE2GR_H = 0x72efc000, + OPC_VPICKVE2GR_W = 0x72efe000, + OPC_VPICKVE2GR_D = 0x72eff000, + OPC_VPICKVE2GR_BU = 0x72f38000, + OPC_VPICKVE2GR_HU = 0x72f3c000, + OPC_VPICKVE2GR_WU = 0x72f3e000, + OPC_VPICKVE2GR_DU = 0x72f3f000, + OPC_VREPLVEI_B = 0x72f78000, + OPC_VREPLVEI_H = 0x72f7c000, + OPC_VREPLVEI_W = 0x72f7e000, + OPC_VREPLVEI_D = 0x72f7f000, + OPC_VSLLWIL_H_B = 0x73082000, + OPC_VSLLWIL_W_H = 0x73084000, + OPC_VSLLWIL_D_W = 0x73088000, + OPC_VEXTL_Q_D = 0x73090000, + OPC_VSLLWIL_HU_BU = 0x730c2000, + OPC_VSLLWIL_WU_HU = 0x730c4000, + OPC_VSLLWIL_DU_WU = 0x730c8000, + OPC_VEXTL_QU_DU = 0x730d0000, + OPC_VBITCLRI_B = 0x73102000, + OPC_VBITCLRI_H = 0x73104000, + OPC_VBITCLRI_W = 0x73108000, + OPC_VBITCLRI_D = 0x73110000, + OPC_VBITSETI_B = 0x73142000, + OPC_VBITSETI_H = 0x73144000, + OPC_VBITSETI_W = 0x73148000, + OPC_VBITSETI_D = 0x73150000, + OPC_VBITREVI_B = 0x73182000, + OPC_VBITREVI_H = 0x73184000, + OPC_VBITREVI_W = 0x73188000, + OPC_VBITREVI_D = 0x73190000, + OPC_VSAT_B = 0x73242000, + OPC_VSAT_H = 0x73244000, + OPC_VSAT_W = 0x73248000, + OPC_VSAT_D = 0x73250000, + OPC_VSAT_BU = 0x73282000, + OPC_VSAT_HU = 0x73284000, + OPC_VSAT_WU = 0x73288000, + OPC_VSAT_DU = 0x73290000, + OPC_VSLLI_B = 0x732c2000, + OPC_VSLLI_H = 0x732c4000, + OPC_VSLLI_W = 0x732c8000, + OPC_VSLLI_D = 0x732d0000, + OPC_VSRLI_B = 0x73302000, + OPC_VSRLI_H = 0x73304000, + OPC_VSRLI_W = 0x73308000, + OPC_VSRLI_D = 0x73310000, + OPC_VSRAI_B = 0x73342000, + OPC_VSRAI_H = 0x73344000, + OPC_VSRAI_W = 0x73348000, + OPC_VSRAI_D = 0x73350000, + OPC_VSRLNI_B_H = 0x73404000, + OPC_VSRLNI_H_W = 0x73408000, + OPC_VSRLNI_W_D = 0x73410000, + OPC_VSRLNI_D_Q = 0x73420000, + OPC_VSRLRNI_B_H = 0x73444000, + OPC_VSRLRNI_H_W = 0x73448000, + OPC_VSRLRNI_W_D = 0x73450000, + OPC_VSRLRNI_D_Q = 0x73460000, + OPC_VSSRLNI_B_H = 0x73484000, + OPC_VSSRLNI_H_W = 0x73488000, + OPC_VSSRLNI_W_D = 0x73490000, + OPC_VSSRLNI_D_Q = 0x734a0000, + OPC_VSSRLNI_BU_H = 0x734c4000, + OPC_VSSRLNI_HU_W = 0x734c8000, + OPC_VSSRLNI_WU_D = 0x734d0000, + OPC_VSSRLNI_DU_Q = 0x734e0000, + OPC_VSSRLRNI_B_H = 0x73504000, + OPC_VSSRLRNI_H_W = 0x73508000, + OPC_VSSRLRNI_W_D = 0x73510000, + OPC_VSSRLRNI_D_Q = 0x73520000, + OPC_VSSRLRNI_BU_H = 0x73544000, + OPC_VSSRLRNI_HU_W = 0x73548000, + OPC_VSSRLRNI_WU_D = 0x73550000, + OPC_VSSRLRNI_DU_Q = 0x73560000, + OPC_VSRANI_B_H = 0x73584000, + OPC_VSRANI_H_W = 0x73588000, + OPC_VSRANI_W_D = 0x73590000, + OPC_VSRANI_D_Q = 0x735a0000, + OPC_VSRARNI_B_H = 0x735c4000, + OPC_VSRARNI_H_W = 0x735c8000, + OPC_VSRARNI_W_D = 0x735d0000, + OPC_VSRARNI_D_Q = 0x735e0000, + OPC_VSSRANI_B_H = 0x73604000, + OPC_VSSRANI_H_W = 0x73608000, + OPC_VSSRANI_W_D = 0x73610000, + OPC_VSSRANI_D_Q = 0x73620000, + OPC_VSSRANI_BU_H = 0x73644000, + OPC_VSSRANI_HU_W = 0x73648000, + OPC_VSSRANI_WU_D = 0x73650000, + OPC_VSSRANI_DU_Q = 0x73660000, + OPC_VSSRARNI_B_H = 0x73684000, + OPC_VSSRARNI_H_W = 0x73688000, + OPC_VSSRARNI_W_D = 0x73690000, + OPC_VSSRARNI_D_Q = 0x736a0000, + OPC_VSSRARNI_BU_H = 0x736c4000, + OPC_VSSRARNI_HU_W = 0x736c8000, + OPC_VSSRARNI_WU_D = 0x736d0000, + OPC_VSSRARNI_DU_Q = 0x736e0000, + OPC_VEXTRINS_D = 0x73800000, + OPC_VEXTRINS_W = 0x73840000, + OPC_VEXTRINS_H = 0x73880000, + OPC_VEXTRINS_B = 0x738c0000, + OPC_VSHUF4I_B = 0x73900000, + OPC_VSHUF4I_H = 0x73940000, + OPC_VSHUF4I_W = 0x73980000, + OPC_VSHUF4I_D = 0x739c0000, + OPC_VBITSELI_B = 0x73c40000, + OPC_VANDI_B = 0x73d00000, + OPC_VORI_B = 0x73d40000, + OPC_VXORI_B = 0x73d80000, + OPC_VNORI_B = 0x73dc0000, + OPC_VLDI = 0x73e00000, + OPC_VPERMI_W = 0x73e40000, } LoongArchInsn; static int32_t __attribute__((unused)) @@ -132,6 +845,13 @@ encode_djk_slots(LoongArchInsn opc, uint32_t d, uint32_t j, uint32_t k) return opc | d | j << 5 | k << 10; } +static int32_t __attribute__((unused)) +encode_djka_slots(LoongArchInsn opc, uint32_t d, uint32_t j, uint32_t k, + uint32_t a) +{ + return opc | d | j << 5 | k << 10 | a << 15; +} + static int32_t __attribute__((unused)) encode_djkm_slots(LoongArchInsn opc, uint32_t d, uint32_t j, uint32_t k, uint32_t m) @@ -139,12 +859,27 @@ encode_djkm_slots(LoongArchInsn opc, uint32_t d, uint32_t j, uint32_t k, return opc | d | j << 5 | k << 10 | m << 16; } +static int32_t __attribute__((unused)) +encode_djkn_slots(LoongArchInsn opc, uint32_t d, uint32_t j, uint32_t k, + uint32_t n) +{ + return opc | d | j << 5 | k << 10 | n << 18; +} + static int32_t __attribute__((unused)) encode_dk_slots(LoongArchInsn opc, uint32_t d, uint32_t k) { return opc | d | k << 10; } +static int32_t __attribute__((unused)) +encode_cdvj_insn(LoongArchInsn opc, TCGReg cd, TCGReg vj) +{ + tcg_debug_assert(cd >= 0 && cd <= 0x7); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + return encode_dj_slots(opc, cd, vj & 0x1f); +} + static int32_t __attribute__((unused)) encode_dj_insn(LoongArchInsn opc, TCGReg d, TCGReg j) { @@ -237,6 +972,42 @@ encode_dsj20_insn(LoongArchInsn opc, TCGReg d, int32_t sj20) return encode_dj_slots(opc, d, sj20 & 0xfffff); } +static int32_t __attribute__((unused)) +encode_dvjuk1_insn(LoongArchInsn opc, TCGReg d, TCGReg vj, uint32_t uk1) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(uk1 <= 0x1); + return encode_djk_slots(opc, d, vj & 0x1f, uk1); +} + +static int32_t __attribute__((unused)) +encode_dvjuk2_insn(LoongArchInsn opc, TCGReg d, TCGReg vj, uint32_t uk2) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(uk2 <= 0x3); + return encode_djk_slots(opc, d, vj & 0x1f, uk2); +} + +static int32_t __attribute__((unused)) +encode_dvjuk3_insn(LoongArchInsn opc, TCGReg d, TCGReg vj, uint32_t uk3) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(uk3 <= 0x7); + return encode_djk_slots(opc, d, vj & 0x1f, uk3); +} + +static int32_t __attribute__((unused)) +encode_dvjuk4_insn(LoongArchInsn opc, TCGReg d, TCGReg vj, uint32_t uk4) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(uk4 <= 0xf); + return encode_djk_slots(opc, d, vj & 0x1f, uk4); +} + static int32_t __attribute__((unused)) encode_sd10k16_insn(LoongArchInsn opc, int32_t sd10k16) { @@ -251,6 +1022,265 @@ encode_ud15_insn(LoongArchInsn opc, uint32_t ud15) return encode_d_slot(opc, ud15); } +static int32_t __attribute__((unused)) +encode_vdj_insn(LoongArchInsn opc, TCGReg vd, TCGReg j) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + return encode_dj_slots(opc, vd & 0x1f, j); +} + +static int32_t __attribute__((unused)) +encode_vdjk_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, TCGReg k) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(k >= 0 && k <= 0x1f); + return encode_djk_slots(opc, vd & 0x1f, j, k); +} + +static int32_t __attribute__((unused)) +encode_vdjsk10_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk10) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk10 >= -0x200 && sk10 <= 0x1ff); + return encode_djk_slots(opc, vd & 0x1f, j, sk10 & 0x3ff); +} + +static int32_t __attribute__((unused)) +encode_vdjsk11_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk11) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk11 >= -0x400 && sk11 <= 0x3ff); + return encode_djk_slots(opc, vd & 0x1f, j, sk11 & 0x7ff); +} + +static int32_t __attribute__((unused)) +encode_vdjsk12_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk12) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk12 >= -0x800 && sk12 <= 0x7ff); + return encode_djk_slots(opc, vd & 0x1f, j, sk12 & 0xfff); +} + +static int32_t __attribute__((unused)) +encode_vdjsk8un1_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk8, + uint32_t un1) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk8 >= -0x80 && sk8 <= 0x7f); + tcg_debug_assert(un1 <= 0x1); + return encode_djkn_slots(opc, vd & 0x1f, j, sk8 & 0xff, un1); +} + +static int32_t __attribute__((unused)) +encode_vdjsk8un2_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk8, + uint32_t un2) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk8 >= -0x80 && sk8 <= 0x7f); + tcg_debug_assert(un2 <= 0x3); + return encode_djkn_slots(opc, vd & 0x1f, j, sk8 & 0xff, un2); +} + +static int32_t __attribute__((unused)) +encode_vdjsk8un3_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk8, + uint32_t un3) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk8 >= -0x80 && sk8 <= 0x7f); + tcg_debug_assert(un3 <= 0x7); + return encode_djkn_slots(opc, vd & 0x1f, j, sk8 & 0xff, un3); +} + +static int32_t __attribute__((unused)) +encode_vdjsk8un4_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk8, + uint32_t un4) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk8 >= -0x80 && sk8 <= 0x7f); + tcg_debug_assert(un4 <= 0xf); + return encode_djkn_slots(opc, vd & 0x1f, j, sk8 & 0xff, un4); +} + +static int32_t __attribute__((unused)) +encode_vdjsk9_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk9) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk9 >= -0x100 && sk9 <= 0xff); + return encode_djk_slots(opc, vd & 0x1f, j, sk9 & 0x1ff); +} + +static int32_t __attribute__((unused)) +encode_vdjuk1_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, uint32_t uk1) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(uk1 <= 0x1); + return encode_djk_slots(opc, vd & 0x1f, j, uk1); +} + +static int32_t __attribute__((unused)) +encode_vdjuk2_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, uint32_t uk2) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(uk2 <= 0x3); + return encode_djk_slots(opc, vd & 0x1f, j, uk2); +} + +static int32_t __attribute__((unused)) +encode_vdjuk3_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, uint32_t uk3) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(uk3 <= 0x7); + return encode_djk_slots(opc, vd & 0x1f, j, uk3); +} + +static int32_t __attribute__((unused)) +encode_vdjuk4_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, uint32_t uk4) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(uk4 <= 0xf); + return encode_djk_slots(opc, vd & 0x1f, j, uk4); +} + +static int32_t __attribute__((unused)) +encode_vdsj13_insn(LoongArchInsn opc, TCGReg vd, int32_t sj13) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(sj13 >= -0x1000 && sj13 <= 0xfff); + return encode_dj_slots(opc, vd & 0x1f, sj13 & 0x1fff); +} + +static int32_t __attribute__((unused)) +encode_vdvj_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + return encode_dj_slots(opc, vd & 0x1f, vj & 0x1f); +} + +static int32_t __attribute__((unused)) +encode_vdvjk_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, TCGReg k) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(k >= 0 && k <= 0x1f); + return encode_djk_slots(opc, vd & 0x1f, vj & 0x1f, k); +} + +static int32_t __attribute__((unused)) +encode_vdvjsk5_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(sk5 >= -0x10 && sk5 <= 0xf); + return encode_djk_slots(opc, vd & 0x1f, vj & 0x1f, sk5 & 0x1f); +} + +static int32_t __attribute__((unused)) +encode_vdvjuk1_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, uint32_t uk1) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(uk1 <= 0x1); + return encode_djk_slots(opc, vd & 0x1f, vj & 0x1f, uk1); +} + +static int32_t __attribute__((unused)) +encode_vdvjuk2_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, uint32_t uk2) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(uk2 <= 0x3); + return encode_djk_slots(opc, vd & 0x1f, vj & 0x1f, uk2); +} + +static int32_t __attribute__((unused)) +encode_vdvjuk3_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(uk3 <= 0x7); + return encode_djk_slots(opc, vd & 0x1f, vj & 0x1f, uk3); +} + +static int32_t __attribute__((unused)) +encode_vdvjuk4_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(uk4 <= 0xf); + return encode_djk_slots(opc, vd & 0x1f, vj & 0x1f, uk4); +} + +static int32_t __attribute__((unused)) +encode_vdvjuk5_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(uk5 <= 0x1f); + return encode_djk_slots(opc, vd & 0x1f, vj & 0x1f, uk5); +} + +static int32_t __attribute__((unused)) +encode_vdvjuk6_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(uk6 <= 0x3f); + return encode_djk_slots(opc, vd & 0x1f, vj & 0x1f, uk6); +} + +static int32_t __attribute__((unused)) +encode_vdvjuk7_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, uint32_t uk7) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(uk7 <= 0x7f); + return encode_djk_slots(opc, vd & 0x1f, vj & 0x1f, uk7); +} + +static int32_t __attribute__((unused)) +encode_vdvjuk8_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(uk8 <= 0xff); + return encode_djk_slots(opc, vd & 0x1f, vj & 0x1f, uk8); +} + +static int32_t __attribute__((unused)) +encode_vdvjvk_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(vk >= 0x20 && vk <= 0x3f); + return encode_djk_slots(opc, vd & 0x1f, vj & 0x1f, vk & 0x1f); +} + +static int32_t __attribute__((unused)) +encode_vdvjvkva_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, TCGReg vk, + TCGReg va) +{ + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + tcg_debug_assert(vk >= 0x20 && vk <= 0x3f); + tcg_debug_assert(va >= 0x20 && va <= 0x3f); + return encode_djka_slots(opc, vd & 0x1f, vj & 0x1f, vk & 0x1f, va & 0x1f); +} + /* Emits the `clz.w d, j` instruction. */ static void __attribute__((unused)) tcg_out_opc_clz_w(TCGContext *s, TCGReg d, TCGReg j) @@ -710,6 +1740,391 @@ tcg_out_opc_xori(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk12) tcg_out32(s, encode_djuk12_insn(OPC_XORI, d, j, uk12)); } +/* Emits the `vfmadd.s vd, vj, vk, va` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfmadd_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) +{ + tcg_out32(s, encode_vdvjvkva_insn(OPC_VFMADD_S, vd, vj, vk, va)); +} + +/* Emits the `vfmadd.d vd, vj, vk, va` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfmadd_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) +{ + tcg_out32(s, encode_vdvjvkva_insn(OPC_VFMADD_D, vd, vj, vk, va)); +} + +/* Emits the `vfmsub.s vd, vj, vk, va` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfmsub_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) +{ + tcg_out32(s, encode_vdvjvkva_insn(OPC_VFMSUB_S, vd, vj, vk, va)); +} + +/* Emits the `vfmsub.d vd, vj, vk, va` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfmsub_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) +{ + tcg_out32(s, encode_vdvjvkva_insn(OPC_VFMSUB_D, vd, vj, vk, va)); +} + +/* Emits the `vfnmadd.s vd, vj, vk, va` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfnmadd_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) +{ + tcg_out32(s, encode_vdvjvkva_insn(OPC_VFNMADD_S, vd, vj, vk, va)); +} + +/* Emits the `vfnmadd.d vd, vj, vk, va` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfnmadd_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) +{ + tcg_out32(s, encode_vdvjvkva_insn(OPC_VFNMADD_D, vd, vj, vk, va)); +} + +/* Emits the `vfnmsub.s vd, vj, vk, va` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfnmsub_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) +{ + tcg_out32(s, encode_vdvjvkva_insn(OPC_VFNMSUB_S, vd, vj, vk, va)); +} + +/* Emits the `vfnmsub.d vd, vj, vk, va` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfnmsub_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) +{ + tcg_out32(s, encode_vdvjvkva_insn(OPC_VFNMSUB_D, vd, vj, vk, va)); +} + +/* Emits the `vfcmp.caf.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_caf_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CAF_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.saf.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_saf_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SAF_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.clt.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_clt_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CLT_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.slt.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_slt_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SLT_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.ceq.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_ceq_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CEQ_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.seq.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_seq_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SEQ_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.cle.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cle_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CLE_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.sle.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sle_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SLE_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.cun.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cun_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CUN_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.sun.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sun_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SUN_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.cult.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cult_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CULT_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.sult.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sult_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SULT_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.cueq.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cueq_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CUEQ_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.sueq.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sueq_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SUEQ_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.cule.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cule_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CULE_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.sule.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sule_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SULE_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.cne.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cne_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CNE_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.sne.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sne_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SNE_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.cor.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cor_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_COR_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.sor.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sor_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SOR_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.cune.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cune_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CUNE_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.sune.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sune_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SUNE_S, vd, vj, vk)); +} + +/* Emits the `vfcmp.caf.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_caf_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CAF_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.saf.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_saf_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SAF_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.clt.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_clt_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CLT_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.slt.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_slt_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SLT_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.ceq.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_ceq_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CEQ_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.seq.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_seq_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SEQ_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.cle.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cle_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CLE_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.sle.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sle_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SLE_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.cun.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cun_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CUN_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.sun.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sun_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SUN_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.cult.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cult_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CULT_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.sult.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sult_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SULT_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.cueq.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cueq_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CUEQ_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.sueq.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sueq_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SUEQ_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.cule.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cule_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CULE_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.sule.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sule_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SULE_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.cne.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cne_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CNE_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.sne.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sne_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SNE_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.cor.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cor_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_COR_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.sor.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sor_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SOR_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.cune.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_cune_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_CUNE_D, vd, vj, vk)); +} + +/* Emits the `vfcmp.sune.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcmp_sune_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCMP_SUNE_D, vd, vj, vk)); +} + +/* Emits the `vbitsel.v vd, vj, vk, va` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitsel_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) +{ + tcg_out32(s, encode_vdvjvkva_insn(OPC_VBITSEL_V, vd, vj, vk, va)); +} + +/* Emits the `vshuf.b vd, vj, vk, va` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vshuf_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) +{ + tcg_out32(s, encode_vdvjvkva_insn(OPC_VSHUF_B, vd, vj, vk, va)); +} + +/* Emits the `addu16i.d d, j, sk16` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_addu16i_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +{ + tcg_out32(s, encode_djsk16_insn(OPC_ADDU16I_D, d, j, sk16)); +} + /* Emits the `lu12i.w d, sj20` instruction. */ static void __attribute__((unused)) tcg_out_opc_lu12i_w(TCGContext *s, TCGReg d, int32_t sj20) @@ -829,6 +2244,80 @@ tcg_out_opc_ld_wu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) tcg_out32(s, encode_djsk12_insn(OPC_LD_WU, d, j, sk12)); } +/* Emits the `vld vd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vld(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_vdjsk12_insn(OPC_VLD, vd, j, sk12)); +} + +/* Emits the `vst vd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vst(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_vdjsk12_insn(OPC_VST, vd, j, sk12)); +} + +/* Emits the `vldrepl.d vd, j, sk9` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vldrepl_d(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk9) +{ + tcg_out32(s, encode_vdjsk9_insn(OPC_VLDREPL_D, vd, j, sk9)); +} + +/* Emits the `vldrepl.w vd, j, sk10` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vldrepl_w(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk10) +{ + tcg_out32(s, encode_vdjsk10_insn(OPC_VLDREPL_W, vd, j, sk10)); +} + +/* Emits the `vldrepl.h vd, j, sk11` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vldrepl_h(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk11) +{ + tcg_out32(s, encode_vdjsk11_insn(OPC_VLDREPL_H, vd, j, sk11)); +} + +/* Emits the `vldrepl.b vd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vldrepl_b(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_vdjsk12_insn(OPC_VLDREPL_B, vd, j, sk12)); +} + +/* Emits the `vstelm.d vd, j, sk8, un1` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vstelm_d(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk8, + uint32_t un1) +{ + tcg_out32(s, encode_vdjsk8un1_insn(OPC_VSTELM_D, vd, j, sk8, un1)); +} + +/* Emits the `vstelm.w vd, j, sk8, un2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vstelm_w(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk8, + uint32_t un2) +{ + tcg_out32(s, encode_vdjsk8un2_insn(OPC_VSTELM_W, vd, j, sk8, un2)); +} + +/* Emits the `vstelm.h vd, j, sk8, un3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vstelm_h(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk8, + uint32_t un3) +{ + tcg_out32(s, encode_vdjsk8un3_insn(OPC_VSTELM_H, vd, j, sk8, un3)); +} + +/* Emits the `vstelm.b vd, j, sk8, un4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vstelm_b(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk8, + uint32_t un4) +{ + tcg_out32(s, encode_vdjsk8un4_insn(OPC_VSTELM_B, vd, j, sk8, un4)); +} + /* Emits the `ldx.b d, j, k` instruction. */ static void __attribute__((unused)) tcg_out_opc_ldx_b(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) @@ -906,6 +2395,20 @@ tcg_out_opc_ldx_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) tcg_out32(s, encode_djk_insn(OPC_LDX_WU, d, j, k)); } +/* Emits the `vldx vd, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vldx(TCGContext *s, TCGReg vd, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_vdjk_insn(OPC_VLDX, vd, j, k)); +} + +/* Emits the `vstx vd, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vstx(TCGContext *s, TCGReg vd, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_vdjk_insn(OPC_VSTX, vd, j, k)); +} + /* Emits the `dbar ud15` instruction. */ static void __attribute__((unused)) tcg_out_opc_dbar(TCGContext *s, uint32_t ud15) @@ -976,4 +2479,4526 @@ tcg_out_opc_bleu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) tcg_out32(s, encode_djsk16_insn(OPC_BLEU, d, j, sk16)); } +/* Emits the `vseq.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vseq_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSEQ_B, vd, vj, vk)); +} + +/* Emits the `vseq.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vseq_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSEQ_H, vd, vj, vk)); +} + +/* Emits the `vseq.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vseq_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSEQ_W, vd, vj, vk)); +} + +/* Emits the `vseq.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vseq_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSEQ_D, vd, vj, vk)); +} + +/* Emits the `vsle.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsle_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_B, vd, vj, vk)); +} + +/* Emits the `vsle.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsle_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_H, vd, vj, vk)); +} + +/* Emits the `vsle.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsle_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_W, vd, vj, vk)); +} + +/* Emits the `vsle.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsle_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_D, vd, vj, vk)); +} + +/* Emits the `vsle.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsle_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_BU, vd, vj, vk)); +} + +/* Emits the `vsle.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsle_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_HU, vd, vj, vk)); +} + +/* Emits the `vsle.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsle_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_WU, vd, vj, vk)); +} + +/* Emits the `vsle.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsle_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_DU, vd, vj, vk)); +} + +/* Emits the `vslt.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslt_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_B, vd, vj, vk)); +} + +/* Emits the `vslt.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslt_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_H, vd, vj, vk)); +} + +/* Emits the `vslt.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslt_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_W, vd, vj, vk)); +} + +/* Emits the `vslt.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslt_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_D, vd, vj, vk)); +} + +/* Emits the `vslt.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslt_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_BU, vd, vj, vk)); +} + +/* Emits the `vslt.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslt_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_HU, vd, vj, vk)); +} + +/* Emits the `vslt.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslt_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_WU, vd, vj, vk)); +} + +/* Emits the `vslt.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslt_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_DU, vd, vj, vk)); +} + +/* Emits the `vadd.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vadd_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADD_B, vd, vj, vk)); +} + +/* Emits the `vadd.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vadd_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADD_H, vd, vj, vk)); +} + +/* Emits the `vadd.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vadd_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADD_W, vd, vj, vk)); +} + +/* Emits the `vadd.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vadd_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADD_D, vd, vj, vk)); +} + +/* Emits the `vsub.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsub_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_B, vd, vj, vk)); +} + +/* Emits the `vsub.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsub_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_H, vd, vj, vk)); +} + +/* Emits the `vsub.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsub_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_W, vd, vj, vk)); +} + +/* Emits the `vsub.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsub_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_D, vd, vj, vk)); +} + +/* Emits the `vaddwev.h.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwev_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_H_B, vd, vj, vk)); +} + +/* Emits the `vaddwev.w.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwev_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_W_H, vd, vj, vk)); +} + +/* Emits the `vaddwev.d.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwev_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_D_W, vd, vj, vk)); +} + +/* Emits the `vaddwev.q.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwev_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_Q_D, vd, vj, vk)); +} + +/* Emits the `vsubwev.h.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwev_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_H_B, vd, vj, vk)); +} + +/* Emits the `vsubwev.w.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwev_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_W_H, vd, vj, vk)); +} + +/* Emits the `vsubwev.d.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwev_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_D_W, vd, vj, vk)); +} + +/* Emits the `vsubwev.q.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwev_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_Q_D, vd, vj, vk)); +} + +/* Emits the `vaddwod.h.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwod_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_H_B, vd, vj, vk)); +} + +/* Emits the `vaddwod.w.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwod_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_W_H, vd, vj, vk)); +} + +/* Emits the `vaddwod.d.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwod_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_D_W, vd, vj, vk)); +} + +/* Emits the `vaddwod.q.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwod_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_Q_D, vd, vj, vk)); +} + +/* Emits the `vsubwod.h.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwod_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_H_B, vd, vj, vk)); +} + +/* Emits the `vsubwod.w.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwod_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_W_H, vd, vj, vk)); +} + +/* Emits the `vsubwod.d.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwod_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_D_W, vd, vj, vk)); +} + +/* Emits the `vsubwod.q.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwod_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_Q_D, vd, vj, vk)); +} + +/* Emits the `vaddwev.h.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwev_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_H_BU, vd, vj, vk)); +} + +/* Emits the `vaddwev.w.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwev_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_W_HU, vd, vj, vk)); +} + +/* Emits the `vaddwev.d.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwev_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_D_WU, vd, vj, vk)); +} + +/* Emits the `vaddwev.q.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwev_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_Q_DU, vd, vj, vk)); +} + +/* Emits the `vsubwev.h.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwev_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_H_BU, vd, vj, vk)); +} + +/* Emits the `vsubwev.w.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwev_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_W_HU, vd, vj, vk)); +} + +/* Emits the `vsubwev.d.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwev_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_D_WU, vd, vj, vk)); +} + +/* Emits the `vsubwev.q.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwev_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWEV_Q_DU, vd, vj, vk)); +} + +/* Emits the `vaddwod.h.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwod_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_H_BU, vd, vj, vk)); +} + +/* Emits the `vaddwod.w.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwod_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_W_HU, vd, vj, vk)); +} + +/* Emits the `vaddwod.d.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwod_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_D_WU, vd, vj, vk)); +} + +/* Emits the `vaddwod.q.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwod_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_Q_DU, vd, vj, vk)); +} + +/* Emits the `vsubwod.h.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwod_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_H_BU, vd, vj, vk)); +} + +/* Emits the `vsubwod.w.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwod_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_W_HU, vd, vj, vk)); +} + +/* Emits the `vsubwod.d.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwod_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_D_WU, vd, vj, vk)); +} + +/* Emits the `vsubwod.q.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubwod_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUBWOD_Q_DU, vd, vj, vk)); +} + +/* Emits the `vaddwev.h.bu.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwev_h_bu_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_H_BU_B, vd, vj, vk)); +} + +/* Emits the `vaddwev.w.hu.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwev_w_hu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_W_HU_H, vd, vj, vk)); +} + +/* Emits the `vaddwev.d.wu.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwev_d_wu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_D_WU_W, vd, vj, vk)); +} + +/* Emits the `vaddwev.q.du.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwev_q_du_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWEV_Q_DU_D, vd, vj, vk)); +} + +/* Emits the `vaddwod.h.bu.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwod_h_bu_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_H_BU_B, vd, vj, vk)); +} + +/* Emits the `vaddwod.w.hu.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwod_w_hu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_W_HU_H, vd, vj, vk)); +} + +/* Emits the `vaddwod.d.wu.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwod_d_wu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_D_WU_W, vd, vj, vk)); +} + +/* Emits the `vaddwod.q.du.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddwod_q_du_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDWOD_Q_DU_D, vd, vj, vk)); +} + +/* Emits the `vsadd.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsadd_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSADD_B, vd, vj, vk)); +} + +/* Emits the `vsadd.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsadd_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSADD_H, vd, vj, vk)); +} + +/* Emits the `vsadd.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsadd_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSADD_W, vd, vj, vk)); +} + +/* Emits the `vsadd.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsadd_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSADD_D, vd, vj, vk)); +} + +/* Emits the `vssub.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssub_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSUB_B, vd, vj, vk)); +} + +/* Emits the `vssub.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssub_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSUB_H, vd, vj, vk)); +} + +/* Emits the `vssub.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssub_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSUB_W, vd, vj, vk)); +} + +/* Emits the `vssub.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssub_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSUB_D, vd, vj, vk)); +} + +/* Emits the `vsadd.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsadd_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSADD_BU, vd, vj, vk)); +} + +/* Emits the `vsadd.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsadd_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSADD_HU, vd, vj, vk)); +} + +/* Emits the `vsadd.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsadd_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSADD_WU, vd, vj, vk)); +} + +/* Emits the `vsadd.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsadd_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSADD_DU, vd, vj, vk)); +} + +/* Emits the `vssub.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssub_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSUB_BU, vd, vj, vk)); +} + +/* Emits the `vssub.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssub_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSUB_HU, vd, vj, vk)); +} + +/* Emits the `vssub.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssub_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSUB_WU, vd, vj, vk)); +} + +/* Emits the `vssub.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssub_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSUB_DU, vd, vj, vk)); +} + +/* Emits the `vhaddw.h.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhaddw_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_H_B, vd, vj, vk)); +} + +/* Emits the `vhaddw.w.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhaddw_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_W_H, vd, vj, vk)); +} + +/* Emits the `vhaddw.d.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhaddw_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_D_W, vd, vj, vk)); +} + +/* Emits the `vhaddw.q.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhaddw_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_Q_D, vd, vj, vk)); +} + +/* Emits the `vhsubw.h.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhsubw_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_H_B, vd, vj, vk)); +} + +/* Emits the `vhsubw.w.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhsubw_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_W_H, vd, vj, vk)); +} + +/* Emits the `vhsubw.d.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhsubw_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_D_W, vd, vj, vk)); +} + +/* Emits the `vhsubw.q.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhsubw_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_Q_D, vd, vj, vk)); +} + +/* Emits the `vhaddw.hu.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhaddw_hu_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_HU_BU, vd, vj, vk)); +} + +/* Emits the `vhaddw.wu.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhaddw_wu_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_WU_HU, vd, vj, vk)); +} + +/* Emits the `vhaddw.du.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhaddw_du_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_DU_WU, vd, vj, vk)); +} + +/* Emits the `vhaddw.qu.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhaddw_qu_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHADDW_QU_DU, vd, vj, vk)); +} + +/* Emits the `vhsubw.hu.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhsubw_hu_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_HU_BU, vd, vj, vk)); +} + +/* Emits the `vhsubw.wu.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhsubw_wu_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_WU_HU, vd, vj, vk)); +} + +/* Emits the `vhsubw.du.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhsubw_du_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_DU_WU, vd, vj, vk)); +} + +/* Emits the `vhsubw.qu.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vhsubw_qu_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VHSUBW_QU_DU, vd, vj, vk)); +} + +/* Emits the `vadda.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vadda_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDA_B, vd, vj, vk)); +} + +/* Emits the `vadda.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vadda_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDA_H, vd, vj, vk)); +} + +/* Emits the `vadda.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vadda_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDA_W, vd, vj, vk)); +} + +/* Emits the `vadda.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vadda_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADDA_D, vd, vj, vk)); +} + +/* Emits the `vabsd.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vabsd_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_B, vd, vj, vk)); +} + +/* Emits the `vabsd.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vabsd_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_H, vd, vj, vk)); +} + +/* Emits the `vabsd.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vabsd_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_W, vd, vj, vk)); +} + +/* Emits the `vabsd.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vabsd_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_D, vd, vj, vk)); +} + +/* Emits the `vabsd.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vabsd_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_BU, vd, vj, vk)); +} + +/* Emits the `vabsd.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vabsd_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_HU, vd, vj, vk)); +} + +/* Emits the `vabsd.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vabsd_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_WU, vd, vj, vk)); +} + +/* Emits the `vabsd.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vabsd_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VABSD_DU, vd, vj, vk)); +} + +/* Emits the `vavg.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavg_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_B, vd, vj, vk)); +} + +/* Emits the `vavg.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavg_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_H, vd, vj, vk)); +} + +/* Emits the `vavg.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavg_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_W, vd, vj, vk)); +} + +/* Emits the `vavg.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavg_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_D, vd, vj, vk)); +} + +/* Emits the `vavg.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavg_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_BU, vd, vj, vk)); +} + +/* Emits the `vavg.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavg_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_HU, vd, vj, vk)); +} + +/* Emits the `vavg.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavg_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_WU, vd, vj, vk)); +} + +/* Emits the `vavg.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavg_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVG_DU, vd, vj, vk)); +} + +/* Emits the `vavgr.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavgr_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_B, vd, vj, vk)); +} + +/* Emits the `vavgr.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavgr_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_H, vd, vj, vk)); +} + +/* Emits the `vavgr.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavgr_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_W, vd, vj, vk)); +} + +/* Emits the `vavgr.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavgr_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_D, vd, vj, vk)); +} + +/* Emits the `vavgr.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavgr_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_BU, vd, vj, vk)); +} + +/* Emits the `vavgr.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavgr_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_HU, vd, vj, vk)); +} + +/* Emits the `vavgr.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavgr_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_WU, vd, vj, vk)); +} + +/* Emits the `vavgr.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vavgr_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAVGR_DU, vd, vj, vk)); +} + +/* Emits the `vmax.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmax_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_B, vd, vj, vk)); +} + +/* Emits the `vmax.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmax_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_H, vd, vj, vk)); +} + +/* Emits the `vmax.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmax_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_W, vd, vj, vk)); +} + +/* Emits the `vmax.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmax_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_D, vd, vj, vk)); +} + +/* Emits the `vmin.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmin_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_B, vd, vj, vk)); +} + +/* Emits the `vmin.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmin_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_H, vd, vj, vk)); +} + +/* Emits the `vmin.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmin_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_W, vd, vj, vk)); +} + +/* Emits the `vmin.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmin_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_D, vd, vj, vk)); +} + +/* Emits the `vmax.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmax_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_BU, vd, vj, vk)); +} + +/* Emits the `vmax.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmax_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_HU, vd, vj, vk)); +} + +/* Emits the `vmax.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmax_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_WU, vd, vj, vk)); +} + +/* Emits the `vmax.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmax_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_DU, vd, vj, vk)); +} + +/* Emits the `vmin.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmin_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_BU, vd, vj, vk)); +} + +/* Emits the `vmin.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmin_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_HU, vd, vj, vk)); +} + +/* Emits the `vmin.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmin_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_WU, vd, vj, vk)); +} + +/* Emits the `vmin.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmin_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_DU, vd, vj, vk)); +} + +/* Emits the `vmul.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmul_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMUL_B, vd, vj, vk)); +} + +/* Emits the `vmul.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmul_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMUL_H, vd, vj, vk)); +} + +/* Emits the `vmul.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmul_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMUL_W, vd, vj, vk)); +} + +/* Emits the `vmul.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmul_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMUL_D, vd, vj, vk)); +} + +/* Emits the `vmuh.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmuh_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_B, vd, vj, vk)); +} + +/* Emits the `vmuh.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmuh_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_H, vd, vj, vk)); +} + +/* Emits the `vmuh.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmuh_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_W, vd, vj, vk)); +} + +/* Emits the `vmuh.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmuh_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_D, vd, vj, vk)); +} + +/* Emits the `vmuh.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmuh_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_BU, vd, vj, vk)); +} + +/* Emits the `vmuh.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmuh_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_HU, vd, vj, vk)); +} + +/* Emits the `vmuh.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmuh_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_WU, vd, vj, vk)); +} + +/* Emits the `vmuh.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmuh_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMUH_DU, vd, vj, vk)); +} + +/* Emits the `vmulwev.h.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwev_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_H_B, vd, vj, vk)); +} + +/* Emits the `vmulwev.w.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwev_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_W_H, vd, vj, vk)); +} + +/* Emits the `vmulwev.d.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwev_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_D_W, vd, vj, vk)); +} + +/* Emits the `vmulwev.q.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwev_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_Q_D, vd, vj, vk)); +} + +/* Emits the `vmulwod.h.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwod_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_H_B, vd, vj, vk)); +} + +/* Emits the `vmulwod.w.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwod_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_W_H, vd, vj, vk)); +} + +/* Emits the `vmulwod.d.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwod_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_D_W, vd, vj, vk)); +} + +/* Emits the `vmulwod.q.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwod_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_Q_D, vd, vj, vk)); +} + +/* Emits the `vmulwev.h.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwev_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_H_BU, vd, vj, vk)); +} + +/* Emits the `vmulwev.w.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwev_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_W_HU, vd, vj, vk)); +} + +/* Emits the `vmulwev.d.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwev_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_D_WU, vd, vj, vk)); +} + +/* Emits the `vmulwev.q.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwev_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_Q_DU, vd, vj, vk)); +} + +/* Emits the `vmulwod.h.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwod_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_H_BU, vd, vj, vk)); +} + +/* Emits the `vmulwod.w.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwod_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_W_HU, vd, vj, vk)); +} + +/* Emits the `vmulwod.d.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwod_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_D_WU, vd, vj, vk)); +} + +/* Emits the `vmulwod.q.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwod_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_Q_DU, vd, vj, vk)); +} + +/* Emits the `vmulwev.h.bu.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwev_h_bu_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_H_BU_B, vd, vj, vk)); +} + +/* Emits the `vmulwev.w.hu.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwev_w_hu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_W_HU_H, vd, vj, vk)); +} + +/* Emits the `vmulwev.d.wu.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwev_d_wu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_D_WU_W, vd, vj, vk)); +} + +/* Emits the `vmulwev.q.du.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwev_q_du_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWEV_Q_DU_D, vd, vj, vk)); +} + +/* Emits the `vmulwod.h.bu.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwod_h_bu_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_H_BU_B, vd, vj, vk)); +} + +/* Emits the `vmulwod.w.hu.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwod_w_hu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_W_HU_H, vd, vj, vk)); +} + +/* Emits the `vmulwod.d.wu.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwod_d_wu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_D_WU_W, vd, vj, vk)); +} + +/* Emits the `vmulwod.q.du.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmulwod_q_du_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMULWOD_Q_DU_D, vd, vj, vk)); +} + +/* Emits the `vmadd.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmadd_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADD_B, vd, vj, vk)); +} + +/* Emits the `vmadd.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmadd_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADD_H, vd, vj, vk)); +} + +/* Emits the `vmadd.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmadd_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADD_W, vd, vj, vk)); +} + +/* Emits the `vmadd.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmadd_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADD_D, vd, vj, vk)); +} + +/* Emits the `vmsub.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmsub_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMSUB_B, vd, vj, vk)); +} + +/* Emits the `vmsub.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmsub_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMSUB_H, vd, vj, vk)); +} + +/* Emits the `vmsub.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmsub_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMSUB_W, vd, vj, vk)); +} + +/* Emits the `vmsub.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmsub_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMSUB_D, vd, vj, vk)); +} + +/* Emits the `vmaddwev.h.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwev_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_H_B, vd, vj, vk)); +} + +/* Emits the `vmaddwev.w.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwev_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_W_H, vd, vj, vk)); +} + +/* Emits the `vmaddwev.d.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwev_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_D_W, vd, vj, vk)); +} + +/* Emits the `vmaddwev.q.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwev_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_Q_D, vd, vj, vk)); +} + +/* Emits the `vmaddwod.h.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwod_h_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_H_B, vd, vj, vk)); +} + +/* Emits the `vmaddwod.w.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwod_w_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_W_H, vd, vj, vk)); +} + +/* Emits the `vmaddwod.d.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwod_d_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_D_W, vd, vj, vk)); +} + +/* Emits the `vmaddwod.q.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwod_q_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_Q_D, vd, vj, vk)); +} + +/* Emits the `vmaddwev.h.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwev_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_H_BU, vd, vj, vk)); +} + +/* Emits the `vmaddwev.w.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwev_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_W_HU, vd, vj, vk)); +} + +/* Emits the `vmaddwev.d.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwev_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_D_WU, vd, vj, vk)); +} + +/* Emits the `vmaddwev.q.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwev_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_Q_DU, vd, vj, vk)); +} + +/* Emits the `vmaddwod.h.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwod_h_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_H_BU, vd, vj, vk)); +} + +/* Emits the `vmaddwod.w.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwod_w_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_W_HU, vd, vj, vk)); +} + +/* Emits the `vmaddwod.d.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwod_d_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_D_WU, vd, vj, vk)); +} + +/* Emits the `vmaddwod.q.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwod_q_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_Q_DU, vd, vj, vk)); +} + +/* Emits the `vmaddwev.h.bu.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwev_h_bu_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_H_BU_B, vd, vj, vk)); +} + +/* Emits the `vmaddwev.w.hu.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwev_w_hu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_W_HU_H, vd, vj, vk)); +} + +/* Emits the `vmaddwev.d.wu.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwev_d_wu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_D_WU_W, vd, vj, vk)); +} + +/* Emits the `vmaddwev.q.du.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwev_q_du_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWEV_Q_DU_D, vd, vj, vk)); +} + +/* Emits the `vmaddwod.h.bu.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwod_h_bu_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_H_BU_B, vd, vj, vk)); +} + +/* Emits the `vmaddwod.w.hu.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwod_w_hu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_W_HU_H, vd, vj, vk)); +} + +/* Emits the `vmaddwod.d.wu.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwod_d_wu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_D_WU_W, vd, vj, vk)); +} + +/* Emits the `vmaddwod.q.du.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaddwod_q_du_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMADDWOD_Q_DU_D, vd, vj, vk)); +} + +/* Emits the `vdiv.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vdiv_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_B, vd, vj, vk)); +} + +/* Emits the `vdiv.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vdiv_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_H, vd, vj, vk)); +} + +/* Emits the `vdiv.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vdiv_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_W, vd, vj, vk)); +} + +/* Emits the `vdiv.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vdiv_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_D, vd, vj, vk)); +} + +/* Emits the `vmod.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmod_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_B, vd, vj, vk)); +} + +/* Emits the `vmod.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmod_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_H, vd, vj, vk)); +} + +/* Emits the `vmod.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmod_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_W, vd, vj, vk)); +} + +/* Emits the `vmod.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmod_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_D, vd, vj, vk)); +} + +/* Emits the `vdiv.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vdiv_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_BU, vd, vj, vk)); +} + +/* Emits the `vdiv.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vdiv_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_HU, vd, vj, vk)); +} + +/* Emits the `vdiv.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vdiv_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_WU, vd, vj, vk)); +} + +/* Emits the `vdiv.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vdiv_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VDIV_DU, vd, vj, vk)); +} + +/* Emits the `vmod.bu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmod_bu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_BU, vd, vj, vk)); +} + +/* Emits the `vmod.hu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmod_hu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_HU, vd, vj, vk)); +} + +/* Emits the `vmod.wu vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmod_wu(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_WU, vd, vj, vk)); +} + +/* Emits the `vmod.du vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmod_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VMOD_DU, vd, vj, vk)); +} + +/* Emits the `vsll.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsll_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLL_B, vd, vj, vk)); +} + +/* Emits the `vsll.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsll_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLL_H, vd, vj, vk)); +} + +/* Emits the `vsll.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsll_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLL_W, vd, vj, vk)); +} + +/* Emits the `vsll.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsll_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSLL_D, vd, vj, vk)); +} + +/* Emits the `vsrl.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrl_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRL_B, vd, vj, vk)); +} + +/* Emits the `vsrl.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrl_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRL_H, vd, vj, vk)); +} + +/* Emits the `vsrl.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrl_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRL_W, vd, vj, vk)); +} + +/* Emits the `vsrl.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrl_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRL_D, vd, vj, vk)); +} + +/* Emits the `vsra.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsra_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRA_B, vd, vj, vk)); +} + +/* Emits the `vsra.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsra_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRA_H, vd, vj, vk)); +} + +/* Emits the `vsra.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsra_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRA_W, vd, vj, vk)); +} + +/* Emits the `vsra.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsra_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRA_D, vd, vj, vk)); +} + +/* Emits the `vrotr.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vrotr_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VROTR_B, vd, vj, vk)); +} + +/* Emits the `vrotr.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vrotr_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VROTR_H, vd, vj, vk)); +} + +/* Emits the `vrotr.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vrotr_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VROTR_W, vd, vj, vk)); +} + +/* Emits the `vrotr.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vrotr_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VROTR_D, vd, vj, vk)); +} + +/* Emits the `vsrlr.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlr_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLR_B, vd, vj, vk)); +} + +/* Emits the `vsrlr.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlr_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLR_H, vd, vj, vk)); +} + +/* Emits the `vsrlr.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlr_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLR_W, vd, vj, vk)); +} + +/* Emits the `vsrlr.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlr_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLR_D, vd, vj, vk)); +} + +/* Emits the `vsrar.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrar_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRAR_B, vd, vj, vk)); +} + +/* Emits the `vsrar.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrar_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRAR_H, vd, vj, vk)); +} + +/* Emits the `vsrar.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrar_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRAR_W, vd, vj, vk)); +} + +/* Emits the `vsrar.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrar_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRAR_D, vd, vj, vk)); +} + +/* Emits the `vsrln.b.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrln_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLN_B_H, vd, vj, vk)); +} + +/* Emits the `vsrln.h.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrln_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLN_H_W, vd, vj, vk)); +} + +/* Emits the `vsrln.w.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrln_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLN_W_D, vd, vj, vk)); +} + +/* Emits the `vsran.b.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsran_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRAN_B_H, vd, vj, vk)); +} + +/* Emits the `vsran.h.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsran_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRAN_H_W, vd, vj, vk)); +} + +/* Emits the `vsran.w.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsran_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRAN_W_D, vd, vj, vk)); +} + +/* Emits the `vsrlrn.b.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlrn_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLRN_B_H, vd, vj, vk)); +} + +/* Emits the `vsrlrn.h.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlrn_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLRN_H_W, vd, vj, vk)); +} + +/* Emits the `vsrlrn.w.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlrn_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRLRN_W_D, vd, vj, vk)); +} + +/* Emits the `vsrarn.b.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrarn_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRARN_B_H, vd, vj, vk)); +} + +/* Emits the `vsrarn.h.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrarn_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRARN_H_W, vd, vj, vk)); +} + +/* Emits the `vsrarn.w.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrarn_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSRARN_W_D, vd, vj, vk)); +} + +/* Emits the `vssrln.b.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrln_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLN_B_H, vd, vj, vk)); +} + +/* Emits the `vssrln.h.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrln_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLN_H_W, vd, vj, vk)); +} + +/* Emits the `vssrln.w.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrln_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLN_W_D, vd, vj, vk)); +} + +/* Emits the `vssran.b.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssran_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRAN_B_H, vd, vj, vk)); +} + +/* Emits the `vssran.h.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssran_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRAN_H_W, vd, vj, vk)); +} + +/* Emits the `vssran.w.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssran_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRAN_W_D, vd, vj, vk)); +} + +/* Emits the `vssrlrn.b.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlrn_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLRN_B_H, vd, vj, vk)); +} + +/* Emits the `vssrlrn.h.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlrn_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLRN_H_W, vd, vj, vk)); +} + +/* Emits the `vssrlrn.w.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlrn_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLRN_W_D, vd, vj, vk)); +} + +/* Emits the `vssrarn.b.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrarn_b_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRARN_B_H, vd, vj, vk)); +} + +/* Emits the `vssrarn.h.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrarn_h_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRARN_H_W, vd, vj, vk)); +} + +/* Emits the `vssrarn.w.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrarn_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRARN_W_D, vd, vj, vk)); +} + +/* Emits the `vssrln.bu.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrln_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLN_BU_H, vd, vj, vk)); +} + +/* Emits the `vssrln.hu.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrln_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLN_HU_W, vd, vj, vk)); +} + +/* Emits the `vssrln.wu.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrln_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLN_WU_D, vd, vj, vk)); +} + +/* Emits the `vssran.bu.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssran_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRAN_BU_H, vd, vj, vk)); +} + +/* Emits the `vssran.hu.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssran_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRAN_HU_W, vd, vj, vk)); +} + +/* Emits the `vssran.wu.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssran_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRAN_WU_D, vd, vj, vk)); +} + +/* Emits the `vssrlrn.bu.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlrn_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLRN_BU_H, vd, vj, vk)); +} + +/* Emits the `vssrlrn.hu.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlrn_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLRN_HU_W, vd, vj, vk)); +} + +/* Emits the `vssrlrn.wu.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlrn_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRLRN_WU_D, vd, vj, vk)); +} + +/* Emits the `vssrarn.bu.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrarn_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRARN_BU_H, vd, vj, vk)); +} + +/* Emits the `vssrarn.hu.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrarn_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRARN_HU_W, vd, vj, vk)); +} + +/* Emits the `vssrarn.wu.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrarn_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSSRARN_WU_D, vd, vj, vk)); +} + +/* Emits the `vbitclr.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitclr_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VBITCLR_B, vd, vj, vk)); +} + +/* Emits the `vbitclr.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitclr_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VBITCLR_H, vd, vj, vk)); +} + +/* Emits the `vbitclr.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitclr_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VBITCLR_W, vd, vj, vk)); +} + +/* Emits the `vbitclr.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitclr_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VBITCLR_D, vd, vj, vk)); +} + +/* Emits the `vbitset.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitset_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VBITSET_B, vd, vj, vk)); +} + +/* Emits the `vbitset.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitset_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VBITSET_H, vd, vj, vk)); +} + +/* Emits the `vbitset.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitset_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VBITSET_W, vd, vj, vk)); +} + +/* Emits the `vbitset.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitset_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VBITSET_D, vd, vj, vk)); +} + +/* Emits the `vbitrev.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitrev_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VBITREV_B, vd, vj, vk)); +} + +/* Emits the `vbitrev.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitrev_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VBITREV_H, vd, vj, vk)); +} + +/* Emits the `vbitrev.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitrev_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VBITREV_W, vd, vj, vk)); +} + +/* Emits the `vbitrev.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitrev_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VBITREV_D, vd, vj, vk)); +} + +/* Emits the `vpackev.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpackev_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKEV_B, vd, vj, vk)); +} + +/* Emits the `vpackev.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpackev_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKEV_H, vd, vj, vk)); +} + +/* Emits the `vpackev.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpackev_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKEV_W, vd, vj, vk)); +} + +/* Emits the `vpackev.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpackev_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKEV_D, vd, vj, vk)); +} + +/* Emits the `vpackod.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpackod_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKOD_B, vd, vj, vk)); +} + +/* Emits the `vpackod.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpackod_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKOD_H, vd, vj, vk)); +} + +/* Emits the `vpackod.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpackod_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKOD_W, vd, vj, vk)); +} + +/* Emits the `vpackod.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpackod_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPACKOD_D, vd, vj, vk)); +} + +/* Emits the `vilvl.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vilvl_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VILVL_B, vd, vj, vk)); +} + +/* Emits the `vilvl.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vilvl_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VILVL_H, vd, vj, vk)); +} + +/* Emits the `vilvl.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vilvl_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VILVL_W, vd, vj, vk)); +} + +/* Emits the `vilvl.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vilvl_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VILVL_D, vd, vj, vk)); +} + +/* Emits the `vilvh.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vilvh_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VILVH_B, vd, vj, vk)); +} + +/* Emits the `vilvh.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vilvh_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VILVH_H, vd, vj, vk)); +} + +/* Emits the `vilvh.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vilvh_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VILVH_W, vd, vj, vk)); +} + +/* Emits the `vilvh.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vilvh_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VILVH_D, vd, vj, vk)); +} + +/* Emits the `vpickev.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickev_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKEV_B, vd, vj, vk)); +} + +/* Emits the `vpickev.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickev_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKEV_H, vd, vj, vk)); +} + +/* Emits the `vpickev.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickev_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKEV_W, vd, vj, vk)); +} + +/* Emits the `vpickev.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickev_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKEV_D, vd, vj, vk)); +} + +/* Emits the `vpickod.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickod_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKOD_B, vd, vj, vk)); +} + +/* Emits the `vpickod.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickod_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKOD_H, vd, vj, vk)); +} + +/* Emits the `vpickod.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickod_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKOD_W, vd, vj, vk)); +} + +/* Emits the `vpickod.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickod_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VPICKOD_D, vd, vj, vk)); +} + +/* Emits the `vreplve.b vd, vj, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vreplve_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg k) +{ + tcg_out32(s, encode_vdvjk_insn(OPC_VREPLVE_B, vd, vj, k)); +} + +/* Emits the `vreplve.h vd, vj, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vreplve_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg k) +{ + tcg_out32(s, encode_vdvjk_insn(OPC_VREPLVE_H, vd, vj, k)); +} + +/* Emits the `vreplve.w vd, vj, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vreplve_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg k) +{ + tcg_out32(s, encode_vdvjk_insn(OPC_VREPLVE_W, vd, vj, k)); +} + +/* Emits the `vreplve.d vd, vj, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vreplve_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg k) +{ + tcg_out32(s, encode_vdvjk_insn(OPC_VREPLVE_D, vd, vj, k)); +} + +/* Emits the `vand.v vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vand_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VAND_V, vd, vj, vk)); +} + +/* Emits the `vor.v vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vor_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VOR_V, vd, vj, vk)); +} + +/* Emits the `vxor.v vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vxor_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VXOR_V, vd, vj, vk)); +} + +/* Emits the `vnor.v vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vnor_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VNOR_V, vd, vj, vk)); +} + +/* Emits the `vandn.v vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vandn_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VANDN_V, vd, vj, vk)); +} + +/* Emits the `vorn.v vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vorn_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VORN_V, vd, vj, vk)); +} + +/* Emits the `vfrstp.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrstp_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFRSTP_B, vd, vj, vk)); +} + +/* Emits the `vfrstp.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrstp_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFRSTP_H, vd, vj, vk)); +} + +/* Emits the `vadd.q vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vadd_q(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VADD_Q, vd, vj, vk)); +} + +/* Emits the `vsub.q vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsub_q(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_Q, vd, vj, vk)); +} + +/* Emits the `vsigncov.b vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsigncov_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSIGNCOV_B, vd, vj, vk)); +} + +/* Emits the `vsigncov.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsigncov_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSIGNCOV_H, vd, vj, vk)); +} + +/* Emits the `vsigncov.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsigncov_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSIGNCOV_W, vd, vj, vk)); +} + +/* Emits the `vsigncov.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsigncov_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSIGNCOV_D, vd, vj, vk)); +} + +/* Emits the `vfadd.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfadd_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFADD_S, vd, vj, vk)); +} + +/* Emits the `vfadd.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfadd_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFADD_D, vd, vj, vk)); +} + +/* Emits the `vfsub.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfsub_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFSUB_S, vd, vj, vk)); +} + +/* Emits the `vfsub.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfsub_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFSUB_D, vd, vj, vk)); +} + +/* Emits the `vfmul.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfmul_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFMUL_S, vd, vj, vk)); +} + +/* Emits the `vfmul.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfmul_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFMUL_D, vd, vj, vk)); +} + +/* Emits the `vfdiv.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfdiv_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFDIV_S, vd, vj, vk)); +} + +/* Emits the `vfdiv.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfdiv_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFDIV_D, vd, vj, vk)); +} + +/* Emits the `vfmax.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfmax_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFMAX_S, vd, vj, vk)); +} + +/* Emits the `vfmax.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfmax_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFMAX_D, vd, vj, vk)); +} + +/* Emits the `vfmin.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfmin_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFMIN_S, vd, vj, vk)); +} + +/* Emits the `vfmin.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfmin_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFMIN_D, vd, vj, vk)); +} + +/* Emits the `vfmaxa.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfmaxa_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFMAXA_S, vd, vj, vk)); +} + +/* Emits the `vfmaxa.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfmaxa_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFMAXA_D, vd, vj, vk)); +} + +/* Emits the `vfmina.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfmina_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFMINA_S, vd, vj, vk)); +} + +/* Emits the `vfmina.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfmina_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFMINA_D, vd, vj, vk)); +} + +/* Emits the `vfcvt.h.s vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcvt_h_s(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCVT_H_S, vd, vj, vk)); +} + +/* Emits the `vfcvt.s.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcvt_s_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFCVT_S_D, vd, vj, vk)); +} + +/* Emits the `vffint.s.l vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vffint_s_l(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFFINT_S_L, vd, vj, vk)); +} + +/* Emits the `vftint.w.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftint_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFTINT_W_D, vd, vj, vk)); +} + +/* Emits the `vftintrm.w.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrm_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFTINTRM_W_D, vd, vj, vk)); +} + +/* Emits the `vftintrp.w.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrp_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFTINTRP_W_D, vd, vj, vk)); +} + +/* Emits the `vftintrz.w.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrz_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFTINTRZ_W_D, vd, vj, vk)); +} + +/* Emits the `vftintrne.w.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrne_w_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VFTINTRNE_W_D, vd, vj, vk)); +} + +/* Emits the `vshuf.h vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vshuf_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSHUF_H, vd, vj, vk)); +} + +/* Emits the `vshuf.w vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vshuf_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSHUF_W, vd, vj, vk)); +} + +/* Emits the `vshuf.d vd, vj, vk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vshuf_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +{ + tcg_out32(s, encode_vdvjvk_insn(OPC_VSHUF_D, vd, vj, vk)); +} + +/* Emits the `vseqi.b vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vseqi_b(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VSEQI_B, vd, vj, sk5)); +} + +/* Emits the `vseqi.h vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vseqi_h(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VSEQI_H, vd, vj, sk5)); +} + +/* Emits the `vseqi.w vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vseqi_w(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VSEQI_W, vd, vj, sk5)); +} + +/* Emits the `vseqi.d vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vseqi_d(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VSEQI_D, vd, vj, sk5)); +} + +/* Emits the `vslei.b vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslei_b(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VSLEI_B, vd, vj, sk5)); +} + +/* Emits the `vslei.h vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslei_h(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VSLEI_H, vd, vj, sk5)); +} + +/* Emits the `vslei.w vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslei_w(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VSLEI_W, vd, vj, sk5)); +} + +/* Emits the `vslei.d vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslei_d(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VSLEI_D, vd, vj, sk5)); +} + +/* Emits the `vslei.bu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslei_bu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSLEI_BU, vd, vj, uk5)); +} + +/* Emits the `vslei.hu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslei_hu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSLEI_HU, vd, vj, uk5)); +} + +/* Emits the `vslei.wu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslei_wu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSLEI_WU, vd, vj, uk5)); +} + +/* Emits the `vslei.du vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslei_du(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSLEI_DU, vd, vj, uk5)); +} + +/* Emits the `vslti.b vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslti_b(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VSLTI_B, vd, vj, sk5)); +} + +/* Emits the `vslti.h vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslti_h(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VSLTI_H, vd, vj, sk5)); +} + +/* Emits the `vslti.w vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslti_w(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VSLTI_W, vd, vj, sk5)); +} + +/* Emits the `vslti.d vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslti_d(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VSLTI_D, vd, vj, sk5)); +} + +/* Emits the `vslti.bu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslti_bu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSLTI_BU, vd, vj, uk5)); +} + +/* Emits the `vslti.hu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslti_hu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSLTI_HU, vd, vj, uk5)); +} + +/* Emits the `vslti.wu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslti_wu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSLTI_WU, vd, vj, uk5)); +} + +/* Emits the `vslti.du vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslti_du(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSLTI_DU, vd, vj, uk5)); +} + +/* Emits the `vaddi.bu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddi_bu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VADDI_BU, vd, vj, uk5)); +} + +/* Emits the `vaddi.hu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddi_hu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VADDI_HU, vd, vj, uk5)); +} + +/* Emits the `vaddi.wu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddi_wu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VADDI_WU, vd, vj, uk5)); +} + +/* Emits the `vaddi.du vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vaddi_du(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VADDI_DU, vd, vj, uk5)); +} + +/* Emits the `vsubi.bu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubi_bu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSUBI_BU, vd, vj, uk5)); +} + +/* Emits the `vsubi.hu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubi_hu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSUBI_HU, vd, vj, uk5)); +} + +/* Emits the `vsubi.wu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubi_wu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSUBI_WU, vd, vj, uk5)); +} + +/* Emits the `vsubi.du vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsubi_du(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSUBI_DU, vd, vj, uk5)); +} + +/* Emits the `vbsll.v vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbsll_v(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VBSLL_V, vd, vj, uk5)); +} + +/* Emits the `vbsrl.v vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbsrl_v(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VBSRL_V, vd, vj, uk5)); +} + +/* Emits the `vmaxi.b vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaxi_b(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VMAXI_B, vd, vj, sk5)); +} + +/* Emits the `vmaxi.h vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaxi_h(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VMAXI_H, vd, vj, sk5)); +} + +/* Emits the `vmaxi.w vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaxi_w(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VMAXI_W, vd, vj, sk5)); +} + +/* Emits the `vmaxi.d vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaxi_d(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VMAXI_D, vd, vj, sk5)); +} + +/* Emits the `vmini.b vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmini_b(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VMINI_B, vd, vj, sk5)); +} + +/* Emits the `vmini.h vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmini_h(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VMINI_H, vd, vj, sk5)); +} + +/* Emits the `vmini.w vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmini_w(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VMINI_W, vd, vj, sk5)); +} + +/* Emits the `vmini.d vd, vj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmini_d(TCGContext *s, TCGReg vd, TCGReg vj, int32_t sk5) +{ + tcg_out32(s, encode_vdvjsk5_insn(OPC_VMINI_D, vd, vj, sk5)); +} + +/* Emits the `vmaxi.bu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaxi_bu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VMAXI_BU, vd, vj, uk5)); +} + +/* Emits the `vmaxi.hu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaxi_hu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VMAXI_HU, vd, vj, uk5)); +} + +/* Emits the `vmaxi.wu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaxi_wu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VMAXI_WU, vd, vj, uk5)); +} + +/* Emits the `vmaxi.du vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmaxi_du(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VMAXI_DU, vd, vj, uk5)); +} + +/* Emits the `vmini.bu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmini_bu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VMINI_BU, vd, vj, uk5)); +} + +/* Emits the `vmini.hu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmini_hu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VMINI_HU, vd, vj, uk5)); +} + +/* Emits the `vmini.wu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmini_wu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VMINI_WU, vd, vj, uk5)); +} + +/* Emits the `vmini.du vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmini_du(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VMINI_DU, vd, vj, uk5)); +} + +/* Emits the `vfrstpi.b vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrstpi_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VFRSTPI_B, vd, vj, uk5)); +} + +/* Emits the `vfrstpi.h vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrstpi_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VFRSTPI_H, vd, vj, uk5)); +} + +/* Emits the `vclo.b vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vclo_b(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VCLO_B, vd, vj)); +} + +/* Emits the `vclo.h vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vclo_h(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VCLO_H, vd, vj)); +} + +/* Emits the `vclo.w vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vclo_w(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VCLO_W, vd, vj)); +} + +/* Emits the `vclo.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vclo_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VCLO_D, vd, vj)); +} + +/* Emits the `vclz.b vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vclz_b(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VCLZ_B, vd, vj)); +} + +/* Emits the `vclz.h vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vclz_h(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VCLZ_H, vd, vj)); +} + +/* Emits the `vclz.w vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vclz_w(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VCLZ_W, vd, vj)); +} + +/* Emits the `vclz.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vclz_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VCLZ_D, vd, vj)); +} + +/* Emits the `vpcnt.b vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpcnt_b(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VPCNT_B, vd, vj)); +} + +/* Emits the `vpcnt.h vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpcnt_h(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VPCNT_H, vd, vj)); +} + +/* Emits the `vpcnt.w vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpcnt_w(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VPCNT_W, vd, vj)); +} + +/* Emits the `vpcnt.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpcnt_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VPCNT_D, vd, vj)); +} + +/* Emits the `vneg.b vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vneg_b(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VNEG_B, vd, vj)); +} + +/* Emits the `vneg.h vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vneg_h(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VNEG_H, vd, vj)); +} + +/* Emits the `vneg.w vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vneg_w(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VNEG_W, vd, vj)); +} + +/* Emits the `vneg.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vneg_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VNEG_D, vd, vj)); +} + +/* Emits the `vmskltz.b vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmskltz_b(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VMSKLTZ_B, vd, vj)); +} + +/* Emits the `vmskltz.h vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmskltz_h(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VMSKLTZ_H, vd, vj)); +} + +/* Emits the `vmskltz.w vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmskltz_w(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VMSKLTZ_W, vd, vj)); +} + +/* Emits the `vmskltz.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmskltz_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VMSKLTZ_D, vd, vj)); +} + +/* Emits the `vmskgez.b vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmskgez_b(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VMSKGEZ_B, vd, vj)); +} + +/* Emits the `vmsknz.b vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vmsknz_b(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VMSKNZ_B, vd, vj)); +} + +/* Emits the `vseteqz.v cd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vseteqz_v(TCGContext *s, TCGReg cd, TCGReg vj) +{ + tcg_out32(s, encode_cdvj_insn(OPC_VSETEQZ_V, cd, vj)); +} + +/* Emits the `vsetnez.v cd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsetnez_v(TCGContext *s, TCGReg cd, TCGReg vj) +{ + tcg_out32(s, encode_cdvj_insn(OPC_VSETNEZ_V, cd, vj)); +} + +/* Emits the `vsetanyeqz.b cd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsetanyeqz_b(TCGContext *s, TCGReg cd, TCGReg vj) +{ + tcg_out32(s, encode_cdvj_insn(OPC_VSETANYEQZ_B, cd, vj)); +} + +/* Emits the `vsetanyeqz.h cd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsetanyeqz_h(TCGContext *s, TCGReg cd, TCGReg vj) +{ + tcg_out32(s, encode_cdvj_insn(OPC_VSETANYEQZ_H, cd, vj)); +} + +/* Emits the `vsetanyeqz.w cd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsetanyeqz_w(TCGContext *s, TCGReg cd, TCGReg vj) +{ + tcg_out32(s, encode_cdvj_insn(OPC_VSETANYEQZ_W, cd, vj)); +} + +/* Emits the `vsetanyeqz.d cd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsetanyeqz_d(TCGContext *s, TCGReg cd, TCGReg vj) +{ + tcg_out32(s, encode_cdvj_insn(OPC_VSETANYEQZ_D, cd, vj)); +} + +/* Emits the `vsetallnez.b cd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsetallnez_b(TCGContext *s, TCGReg cd, TCGReg vj) +{ + tcg_out32(s, encode_cdvj_insn(OPC_VSETALLNEZ_B, cd, vj)); +} + +/* Emits the `vsetallnez.h cd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsetallnez_h(TCGContext *s, TCGReg cd, TCGReg vj) +{ + tcg_out32(s, encode_cdvj_insn(OPC_VSETALLNEZ_H, cd, vj)); +} + +/* Emits the `vsetallnez.w cd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsetallnez_w(TCGContext *s, TCGReg cd, TCGReg vj) +{ + tcg_out32(s, encode_cdvj_insn(OPC_VSETALLNEZ_W, cd, vj)); +} + +/* Emits the `vsetallnez.d cd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsetallnez_d(TCGContext *s, TCGReg cd, TCGReg vj) +{ + tcg_out32(s, encode_cdvj_insn(OPC_VSETALLNEZ_D, cd, vj)); +} + +/* Emits the `vflogb.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vflogb_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFLOGB_S, vd, vj)); +} + +/* Emits the `vflogb.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vflogb_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFLOGB_D, vd, vj)); +} + +/* Emits the `vfclass.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfclass_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFCLASS_S, vd, vj)); +} + +/* Emits the `vfclass.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfclass_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFCLASS_D, vd, vj)); +} + +/* Emits the `vfsqrt.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfsqrt_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFSQRT_S, vd, vj)); +} + +/* Emits the `vfsqrt.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfsqrt_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFSQRT_D, vd, vj)); +} + +/* Emits the `vfrecip.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrecip_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFRECIP_S, vd, vj)); +} + +/* Emits the `vfrecip.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrecip_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFRECIP_D, vd, vj)); +} + +/* Emits the `vfrsqrt.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrsqrt_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFRSQRT_S, vd, vj)); +} + +/* Emits the `vfrsqrt.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrsqrt_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFRSQRT_D, vd, vj)); +} + +/* Emits the `vfrint.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrint_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFRINT_S, vd, vj)); +} + +/* Emits the `vfrint.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrint_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFRINT_D, vd, vj)); +} + +/* Emits the `vfrintrm.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrintrm_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRM_S, vd, vj)); +} + +/* Emits the `vfrintrm.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrintrm_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRM_D, vd, vj)); +} + +/* Emits the `vfrintrp.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrintrp_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRP_S, vd, vj)); +} + +/* Emits the `vfrintrp.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrintrp_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRP_D, vd, vj)); +} + +/* Emits the `vfrintrz.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrintrz_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRZ_S, vd, vj)); +} + +/* Emits the `vfrintrz.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrintrz_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRZ_D, vd, vj)); +} + +/* Emits the `vfrintrne.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrintrne_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRNE_S, vd, vj)); +} + +/* Emits the `vfrintrne.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfrintrne_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFRINTRNE_D, vd, vj)); +} + +/* Emits the `vfcvtl.s.h vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcvtl_s_h(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFCVTL_S_H, vd, vj)); +} + +/* Emits the `vfcvth.s.h vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcvth_s_h(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFCVTH_S_H, vd, vj)); +} + +/* Emits the `vfcvtl.d.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcvtl_d_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFCVTL_D_S, vd, vj)); +} + +/* Emits the `vfcvth.d.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vfcvth_d_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFCVTH_D_S, vd, vj)); +} + +/* Emits the `vffint.s.w vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vffint_s_w(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFFINT_S_W, vd, vj)); +} + +/* Emits the `vffint.s.wu vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vffint_s_wu(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFFINT_S_WU, vd, vj)); +} + +/* Emits the `vffint.d.l vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vffint_d_l(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFFINT_D_L, vd, vj)); +} + +/* Emits the `vffint.d.lu vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vffint_d_lu(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFFINT_D_LU, vd, vj)); +} + +/* Emits the `vffintl.d.w vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vffintl_d_w(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFFINTL_D_W, vd, vj)); +} + +/* Emits the `vffinth.d.w vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vffinth_d_w(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFFINTH_D_W, vd, vj)); +} + +/* Emits the `vftint.w.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftint_w_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINT_W_S, vd, vj)); +} + +/* Emits the `vftint.l.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftint_l_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINT_L_D, vd, vj)); +} + +/* Emits the `vftintrm.w.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrm_w_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRM_W_S, vd, vj)); +} + +/* Emits the `vftintrm.l.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrm_l_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRM_L_D, vd, vj)); +} + +/* Emits the `vftintrp.w.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrp_w_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRP_W_S, vd, vj)); +} + +/* Emits the `vftintrp.l.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrp_l_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRP_L_D, vd, vj)); +} + +/* Emits the `vftintrz.w.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrz_w_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRZ_W_S, vd, vj)); +} + +/* Emits the `vftintrz.l.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrz_l_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRZ_L_D, vd, vj)); +} + +/* Emits the `vftintrne.w.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrne_w_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRNE_W_S, vd, vj)); +} + +/* Emits the `vftintrne.l.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrne_l_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRNE_L_D, vd, vj)); +} + +/* Emits the `vftint.wu.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftint_wu_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINT_WU_S, vd, vj)); +} + +/* Emits the `vftint.lu.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftint_lu_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINT_LU_D, vd, vj)); +} + +/* Emits the `vftintrz.wu.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrz_wu_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRZ_WU_S, vd, vj)); +} + +/* Emits the `vftintrz.lu.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrz_lu_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRZ_LU_D, vd, vj)); +} + +/* Emits the `vftintl.l.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintl_l_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTL_L_S, vd, vj)); +} + +/* Emits the `vftinth.l.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftinth_l_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTH_L_S, vd, vj)); +} + +/* Emits the `vftintrml.l.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrml_l_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRML_L_S, vd, vj)); +} + +/* Emits the `vftintrmh.l.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrmh_l_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRMH_L_S, vd, vj)); +} + +/* Emits the `vftintrpl.l.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrpl_l_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRPL_L_S, vd, vj)); +} + +/* Emits the `vftintrph.l.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrph_l_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRPH_L_S, vd, vj)); +} + +/* Emits the `vftintrzl.l.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrzl_l_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRZL_L_S, vd, vj)); +} + +/* Emits the `vftintrzh.l.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrzh_l_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRZH_L_S, vd, vj)); +} + +/* Emits the `vftintrnel.l.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrnel_l_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRNEL_L_S, vd, vj)); +} + +/* Emits the `vftintrneh.l.s vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vftintrneh_l_s(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VFTINTRNEH_L_S, vd, vj)); +} + +/* Emits the `vexth.h.b vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vexth_h_b(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_H_B, vd, vj)); +} + +/* Emits the `vexth.w.h vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vexth_w_h(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_W_H, vd, vj)); +} + +/* Emits the `vexth.d.w vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vexth_d_w(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_D_W, vd, vj)); +} + +/* Emits the `vexth.q.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vexth_q_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_Q_D, vd, vj)); +} + +/* Emits the `vexth.hu.bu vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vexth_hu_bu(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_HU_BU, vd, vj)); +} + +/* Emits the `vexth.wu.hu vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vexth_wu_hu(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_WU_HU, vd, vj)); +} + +/* Emits the `vexth.du.wu vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vexth_du_wu(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_DU_WU, vd, vj)); +} + +/* Emits the `vexth.qu.du vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vexth_qu_du(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VEXTH_QU_DU, vd, vj)); +} + +/* Emits the `vreplgr2vr.b vd, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vreplgr2vr_b(TCGContext *s, TCGReg vd, TCGReg j) +{ + tcg_out32(s, encode_vdj_insn(OPC_VREPLGR2VR_B, vd, j)); +} + +/* Emits the `vreplgr2vr.h vd, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vreplgr2vr_h(TCGContext *s, TCGReg vd, TCGReg j) +{ + tcg_out32(s, encode_vdj_insn(OPC_VREPLGR2VR_H, vd, j)); +} + +/* Emits the `vreplgr2vr.w vd, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vreplgr2vr_w(TCGContext *s, TCGReg vd, TCGReg j) +{ + tcg_out32(s, encode_vdj_insn(OPC_VREPLGR2VR_W, vd, j)); +} + +/* Emits the `vreplgr2vr.d vd, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vreplgr2vr_d(TCGContext *s, TCGReg vd, TCGReg j) +{ + tcg_out32(s, encode_vdj_insn(OPC_VREPLGR2VR_D, vd, j)); +} + +/* Emits the `vrotri.b vd, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vrotri_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_vdvjuk3_insn(OPC_VROTRI_B, vd, vj, uk3)); +} + +/* Emits the `vrotri.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vrotri_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VROTRI_H, vd, vj, uk4)); +} + +/* Emits the `vrotri.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vrotri_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VROTRI_W, vd, vj, uk5)); +} + +/* Emits the `vrotri.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vrotri_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VROTRI_D, vd, vj, uk6)); +} + +/* Emits the `vsrlri.b vd, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlri_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_vdvjuk3_insn(OPC_VSRLRI_B, vd, vj, uk3)); +} + +/* Emits the `vsrlri.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlri_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSRLRI_H, vd, vj, uk4)); +} + +/* Emits the `vsrlri.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlri_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSRLRI_W, vd, vj, uk5)); +} + +/* Emits the `vsrlri.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlri_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRLRI_D, vd, vj, uk6)); +} + +/* Emits the `vsrari.b vd, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrari_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_vdvjuk3_insn(OPC_VSRARI_B, vd, vj, uk3)); +} + +/* Emits the `vsrari.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrari_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSRARI_H, vd, vj, uk4)); +} + +/* Emits the `vsrari.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrari_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSRARI_W, vd, vj, uk5)); +} + +/* Emits the `vsrari.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrari_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRARI_D, vd, vj, uk6)); +} + +/* Emits the `vinsgr2vr.b vd, j, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vinsgr2vr_b(TCGContext *s, TCGReg vd, TCGReg j, uint32_t uk4) +{ + tcg_out32(s, encode_vdjuk4_insn(OPC_VINSGR2VR_B, vd, j, uk4)); +} + +/* Emits the `vinsgr2vr.h vd, j, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vinsgr2vr_h(TCGContext *s, TCGReg vd, TCGReg j, uint32_t uk3) +{ + tcg_out32(s, encode_vdjuk3_insn(OPC_VINSGR2VR_H, vd, j, uk3)); +} + +/* Emits the `vinsgr2vr.w vd, j, uk2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vinsgr2vr_w(TCGContext *s, TCGReg vd, TCGReg j, uint32_t uk2) +{ + tcg_out32(s, encode_vdjuk2_insn(OPC_VINSGR2VR_W, vd, j, uk2)); +} + +/* Emits the `vinsgr2vr.d vd, j, uk1` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vinsgr2vr_d(TCGContext *s, TCGReg vd, TCGReg j, uint32_t uk1) +{ + tcg_out32(s, encode_vdjuk1_insn(OPC_VINSGR2VR_D, vd, j, uk1)); +} + +/* Emits the `vpickve2gr.b d, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickve2gr_b(TCGContext *s, TCGReg d, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_dvjuk4_insn(OPC_VPICKVE2GR_B, d, vj, uk4)); +} + +/* Emits the `vpickve2gr.h d, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickve2gr_h(TCGContext *s, TCGReg d, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_dvjuk3_insn(OPC_VPICKVE2GR_H, d, vj, uk3)); +} + +/* Emits the `vpickve2gr.w d, vj, uk2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickve2gr_w(TCGContext *s, TCGReg d, TCGReg vj, uint32_t uk2) +{ + tcg_out32(s, encode_dvjuk2_insn(OPC_VPICKVE2GR_W, d, vj, uk2)); +} + +/* Emits the `vpickve2gr.d d, vj, uk1` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickve2gr_d(TCGContext *s, TCGReg d, TCGReg vj, uint32_t uk1) +{ + tcg_out32(s, encode_dvjuk1_insn(OPC_VPICKVE2GR_D, d, vj, uk1)); +} + +/* Emits the `vpickve2gr.bu d, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickve2gr_bu(TCGContext *s, TCGReg d, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_dvjuk4_insn(OPC_VPICKVE2GR_BU, d, vj, uk4)); +} + +/* Emits the `vpickve2gr.hu d, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickve2gr_hu(TCGContext *s, TCGReg d, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_dvjuk3_insn(OPC_VPICKVE2GR_HU, d, vj, uk3)); +} + +/* Emits the `vpickve2gr.wu d, vj, uk2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickve2gr_wu(TCGContext *s, TCGReg d, TCGReg vj, uint32_t uk2) +{ + tcg_out32(s, encode_dvjuk2_insn(OPC_VPICKVE2GR_WU, d, vj, uk2)); +} + +/* Emits the `vpickve2gr.du d, vj, uk1` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpickve2gr_du(TCGContext *s, TCGReg d, TCGReg vj, uint32_t uk1) +{ + tcg_out32(s, encode_dvjuk1_insn(OPC_VPICKVE2GR_DU, d, vj, uk1)); +} + +/* Emits the `vreplvei.b vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vreplvei_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VREPLVEI_B, vd, vj, uk4)); +} + +/* Emits the `vreplvei.h vd, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vreplvei_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_vdvjuk3_insn(OPC_VREPLVEI_H, vd, vj, uk3)); +} + +/* Emits the `vreplvei.w vd, vj, uk2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vreplvei_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk2) +{ + tcg_out32(s, encode_vdvjuk2_insn(OPC_VREPLVEI_W, vd, vj, uk2)); +} + +/* Emits the `vreplvei.d vd, vj, uk1` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vreplvei_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk1) +{ + tcg_out32(s, encode_vdvjuk1_insn(OPC_VREPLVEI_D, vd, vj, uk1)); +} + +/* Emits the `vsllwil.h.b vd, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsllwil_h_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_vdvjuk3_insn(OPC_VSLLWIL_H_B, vd, vj, uk3)); +} + +/* Emits the `vsllwil.w.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsllwil_w_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSLLWIL_W_H, vd, vj, uk4)); +} + +/* Emits the `vsllwil.d.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsllwil_d_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSLLWIL_D_W, vd, vj, uk5)); +} + +/* Emits the `vextl.q.d vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vextl_q_d(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VEXTL_Q_D, vd, vj)); +} + +/* Emits the `vsllwil.hu.bu vd, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsllwil_hu_bu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_vdvjuk3_insn(OPC_VSLLWIL_HU_BU, vd, vj, uk3)); +} + +/* Emits the `vsllwil.wu.hu vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsllwil_wu_hu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSLLWIL_WU_HU, vd, vj, uk4)); +} + +/* Emits the `vsllwil.du.wu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsllwil_du_wu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSLLWIL_DU_WU, vd, vj, uk5)); +} + +/* Emits the `vextl.qu.du vd, vj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vextl_qu_du(TCGContext *s, TCGReg vd, TCGReg vj) +{ + tcg_out32(s, encode_vdvj_insn(OPC_VEXTL_QU_DU, vd, vj)); +} + +/* Emits the `vbitclri.b vd, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitclri_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_vdvjuk3_insn(OPC_VBITCLRI_B, vd, vj, uk3)); +} + +/* Emits the `vbitclri.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitclri_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VBITCLRI_H, vd, vj, uk4)); +} + +/* Emits the `vbitclri.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitclri_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VBITCLRI_W, vd, vj, uk5)); +} + +/* Emits the `vbitclri.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitclri_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VBITCLRI_D, vd, vj, uk6)); +} + +/* Emits the `vbitseti.b vd, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitseti_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_vdvjuk3_insn(OPC_VBITSETI_B, vd, vj, uk3)); +} + +/* Emits the `vbitseti.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitseti_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VBITSETI_H, vd, vj, uk4)); +} + +/* Emits the `vbitseti.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitseti_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VBITSETI_W, vd, vj, uk5)); +} + +/* Emits the `vbitseti.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitseti_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VBITSETI_D, vd, vj, uk6)); +} + +/* Emits the `vbitrevi.b vd, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitrevi_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_vdvjuk3_insn(OPC_VBITREVI_B, vd, vj, uk3)); +} + +/* Emits the `vbitrevi.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitrevi_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VBITREVI_H, vd, vj, uk4)); +} + +/* Emits the `vbitrevi.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitrevi_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VBITREVI_W, vd, vj, uk5)); +} + +/* Emits the `vbitrevi.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitrevi_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VBITREVI_D, vd, vj, uk6)); +} + +/* Emits the `vsat.b vd, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsat_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_vdvjuk3_insn(OPC_VSAT_B, vd, vj, uk3)); +} + +/* Emits the `vsat.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsat_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSAT_H, vd, vj, uk4)); +} + +/* Emits the `vsat.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsat_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSAT_W, vd, vj, uk5)); +} + +/* Emits the `vsat.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsat_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSAT_D, vd, vj, uk6)); +} + +/* Emits the `vsat.bu vd, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsat_bu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_vdvjuk3_insn(OPC_VSAT_BU, vd, vj, uk3)); +} + +/* Emits the `vsat.hu vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsat_hu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSAT_HU, vd, vj, uk4)); +} + +/* Emits the `vsat.wu vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsat_wu(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSAT_WU, vd, vj, uk5)); +} + +/* Emits the `vsat.du vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsat_du(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSAT_DU, vd, vj, uk6)); +} + +/* Emits the `vslli.b vd, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslli_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_vdvjuk3_insn(OPC_VSLLI_B, vd, vj, uk3)); +} + +/* Emits the `vslli.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslli_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSLLI_H, vd, vj, uk4)); +} + +/* Emits the `vslli.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslli_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSLLI_W, vd, vj, uk5)); +} + +/* Emits the `vslli.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vslli_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSLLI_D, vd, vj, uk6)); +} + +/* Emits the `vsrli.b vd, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrli_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_vdvjuk3_insn(OPC_VSRLI_B, vd, vj, uk3)); +} + +/* Emits the `vsrli.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrli_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSRLI_H, vd, vj, uk4)); +} + +/* Emits the `vsrli.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrli_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSRLI_W, vd, vj, uk5)); +} + +/* Emits the `vsrli.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrli_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRLI_D, vd, vj, uk6)); +} + +/* Emits the `vsrai.b vd, vj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrai_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk3) +{ + tcg_out32(s, encode_vdvjuk3_insn(OPC_VSRAI_B, vd, vj, uk3)); +} + +/* Emits the `vsrai.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrai_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSRAI_H, vd, vj, uk4)); +} + +/* Emits the `vsrai.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrai_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSRAI_W, vd, vj, uk5)); +} + +/* Emits the `vsrai.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrai_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRAI_D, vd, vj, uk6)); +} + +/* Emits the `vsrlni.b.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlni_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSRLNI_B_H, vd, vj, uk4)); +} + +/* Emits the `vsrlni.h.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlni_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSRLNI_H_W, vd, vj, uk5)); +} + +/* Emits the `vsrlni.w.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlni_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRLNI_W_D, vd, vj, uk6)); +} + +/* Emits the `vsrlni.d.q vd, vj, uk7` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlni_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) +{ + tcg_out32(s, encode_vdvjuk7_insn(OPC_VSRLNI_D_Q, vd, vj, uk7)); +} + +/* Emits the `vsrlrni.b.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlrni_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSRLRNI_B_H, vd, vj, uk4)); +} + +/* Emits the `vsrlrni.h.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlrni_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSRLRNI_H_W, vd, vj, uk5)); +} + +/* Emits the `vsrlrni.w.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlrni_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRLRNI_W_D, vd, vj, uk6)); +} + +/* Emits the `vsrlrni.d.q vd, vj, uk7` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrlrni_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) +{ + tcg_out32(s, encode_vdvjuk7_insn(OPC_VSRLRNI_D_Q, vd, vj, uk7)); +} + +/* Emits the `vssrlni.b.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlni_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRLNI_B_H, vd, vj, uk4)); +} + +/* Emits the `vssrlni.h.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlni_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRLNI_H_W, vd, vj, uk5)); +} + +/* Emits the `vssrlni.w.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlni_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRLNI_W_D, vd, vj, uk6)); +} + +/* Emits the `vssrlni.d.q vd, vj, uk7` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlni_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) +{ + tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRLNI_D_Q, vd, vj, uk7)); +} + +/* Emits the `vssrlni.bu.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlni_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRLNI_BU_H, vd, vj, uk4)); +} + +/* Emits the `vssrlni.hu.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlni_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRLNI_HU_W, vd, vj, uk5)); +} + +/* Emits the `vssrlni.wu.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlni_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRLNI_WU_D, vd, vj, uk6)); +} + +/* Emits the `vssrlni.du.q vd, vj, uk7` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlni_du_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) +{ + tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRLNI_DU_Q, vd, vj, uk7)); +} + +/* Emits the `vssrlrni.b.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlrni_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRLRNI_B_H, vd, vj, uk4)); +} + +/* Emits the `vssrlrni.h.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlrni_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRLRNI_H_W, vd, vj, uk5)); +} + +/* Emits the `vssrlrni.w.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlrni_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRLRNI_W_D, vd, vj, uk6)); +} + +/* Emits the `vssrlrni.d.q vd, vj, uk7` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlrni_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) +{ + tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRLRNI_D_Q, vd, vj, uk7)); +} + +/* Emits the `vssrlrni.bu.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlrni_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRLRNI_BU_H, vd, vj, uk4)); +} + +/* Emits the `vssrlrni.hu.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlrni_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRLRNI_HU_W, vd, vj, uk5)); +} + +/* Emits the `vssrlrni.wu.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlrni_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRLRNI_WU_D, vd, vj, uk6)); +} + +/* Emits the `vssrlrni.du.q vd, vj, uk7` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrlrni_du_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) +{ + tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRLRNI_DU_Q, vd, vj, uk7)); +} + +/* Emits the `vsrani.b.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrani_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSRANI_B_H, vd, vj, uk4)); +} + +/* Emits the `vsrani.h.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrani_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSRANI_H_W, vd, vj, uk5)); +} + +/* Emits the `vsrani.w.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrani_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRANI_W_D, vd, vj, uk6)); +} + +/* Emits the `vsrani.d.q vd, vj, uk7` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrani_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) +{ + tcg_out32(s, encode_vdvjuk7_insn(OPC_VSRANI_D_Q, vd, vj, uk7)); +} + +/* Emits the `vsrarni.b.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrarni_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSRARNI_B_H, vd, vj, uk4)); +} + +/* Emits the `vsrarni.h.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrarni_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSRARNI_H_W, vd, vj, uk5)); +} + +/* Emits the `vsrarni.w.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrarni_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSRARNI_W_D, vd, vj, uk6)); +} + +/* Emits the `vsrarni.d.q vd, vj, uk7` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vsrarni_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) +{ + tcg_out32(s, encode_vdvjuk7_insn(OPC_VSRARNI_D_Q, vd, vj, uk7)); +} + +/* Emits the `vssrani.b.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrani_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRANI_B_H, vd, vj, uk4)); +} + +/* Emits the `vssrani.h.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrani_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRANI_H_W, vd, vj, uk5)); +} + +/* Emits the `vssrani.w.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrani_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRANI_W_D, vd, vj, uk6)); +} + +/* Emits the `vssrani.d.q vd, vj, uk7` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrani_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) +{ + tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRANI_D_Q, vd, vj, uk7)); +} + +/* Emits the `vssrani.bu.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrani_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRANI_BU_H, vd, vj, uk4)); +} + +/* Emits the `vssrani.hu.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrani_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRANI_HU_W, vd, vj, uk5)); +} + +/* Emits the `vssrani.wu.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrani_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRANI_WU_D, vd, vj, uk6)); +} + +/* Emits the `vssrani.du.q vd, vj, uk7` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrani_du_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) +{ + tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRANI_DU_Q, vd, vj, uk7)); +} + +/* Emits the `vssrarni.b.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrarni_b_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRARNI_B_H, vd, vj, uk4)); +} + +/* Emits the `vssrarni.h.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrarni_h_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRARNI_H_W, vd, vj, uk5)); +} + +/* Emits the `vssrarni.w.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrarni_w_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRARNI_W_D, vd, vj, uk6)); +} + +/* Emits the `vssrarni.d.q vd, vj, uk7` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrarni_d_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) +{ + tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRARNI_D_Q, vd, vj, uk7)); +} + +/* Emits the `vssrarni.bu.h vd, vj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrarni_bu_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk4) +{ + tcg_out32(s, encode_vdvjuk4_insn(OPC_VSSRARNI_BU_H, vd, vj, uk4)); +} + +/* Emits the `vssrarni.hu.w vd, vj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrarni_hu_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) +{ + tcg_out32(s, encode_vdvjuk5_insn(OPC_VSSRARNI_HU_W, vd, vj, uk5)); +} + +/* Emits the `vssrarni.wu.d vd, vj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrarni_wu_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk6) +{ + tcg_out32(s, encode_vdvjuk6_insn(OPC_VSSRARNI_WU_D, vd, vj, uk6)); +} + +/* Emits the `vssrarni.du.q vd, vj, uk7` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vssrarni_du_q(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk7) +{ + tcg_out32(s, encode_vdvjuk7_insn(OPC_VSSRARNI_DU_Q, vd, vj, uk7)); +} + +/* Emits the `vextrins.d vd, vj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vextrins_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_out32(s, encode_vdvjuk8_insn(OPC_VEXTRINS_D, vd, vj, uk8)); +} + +/* Emits the `vextrins.w vd, vj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vextrins_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_out32(s, encode_vdvjuk8_insn(OPC_VEXTRINS_W, vd, vj, uk8)); +} + +/* Emits the `vextrins.h vd, vj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vextrins_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_out32(s, encode_vdvjuk8_insn(OPC_VEXTRINS_H, vd, vj, uk8)); +} + +/* Emits the `vextrins.b vd, vj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vextrins_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_out32(s, encode_vdvjuk8_insn(OPC_VEXTRINS_B, vd, vj, uk8)); +} + +/* Emits the `vshuf4i.b vd, vj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vshuf4i_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_out32(s, encode_vdvjuk8_insn(OPC_VSHUF4I_B, vd, vj, uk8)); +} + +/* Emits the `vshuf4i.h vd, vj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vshuf4i_h(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_out32(s, encode_vdvjuk8_insn(OPC_VSHUF4I_H, vd, vj, uk8)); +} + +/* Emits the `vshuf4i.w vd, vj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vshuf4i_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_out32(s, encode_vdvjuk8_insn(OPC_VSHUF4I_W, vd, vj, uk8)); +} + +/* Emits the `vshuf4i.d vd, vj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vshuf4i_d(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_out32(s, encode_vdvjuk8_insn(OPC_VSHUF4I_D, vd, vj, uk8)); +} + +/* Emits the `vbitseli.b vd, vj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vbitseli_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_out32(s, encode_vdvjuk8_insn(OPC_VBITSELI_B, vd, vj, uk8)); +} + +/* Emits the `vandi.b vd, vj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vandi_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_out32(s, encode_vdvjuk8_insn(OPC_VANDI_B, vd, vj, uk8)); +} + +/* Emits the `vori.b vd, vj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vori_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_out32(s, encode_vdvjuk8_insn(OPC_VORI_B, vd, vj, uk8)); +} + +/* Emits the `vxori.b vd, vj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vxori_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_out32(s, encode_vdvjuk8_insn(OPC_VXORI_B, vd, vj, uk8)); +} + +/* Emits the `vnori.b vd, vj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vnori_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_out32(s, encode_vdvjuk8_insn(OPC_VNORI_B, vd, vj, uk8)); +} + +/* Emits the `vldi vd, sj13` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vldi(TCGContext *s, TCGReg vd, int32_t sj13) +{ + tcg_out32(s, encode_vdsj13_insn(OPC_VLDI, vd, sj13)); +} + +/* Emits the `vpermi.w vd, vj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_vpermi_w(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +{ + tcg_out32(s, encode_vdvjuk8_insn(OPC_VPERMI_W, vd, vj, uk8)); +} + /* End of generated code. */ diff --git a/tcg/loongarch64/tcg-target-con-set.h b/tcg/loongarch64/tcg-target-con-set.h index 349c672687..cae6c2aad6 100644 --- a/tcg/loongarch64/tcg-target-con-set.h +++ b/tcg/loongarch64/tcg-target-con-set.h @@ -17,15 +17,25 @@ C_O0_I1(r) C_O0_I2(rZ, r) C_O0_I2(rZ, rZ) -C_O0_I2(LZ, L) +C_O0_I2(w, r) +C_O0_I3(r, r, r) C_O1_I1(r, r) -C_O1_I1(r, L) +C_O1_I1(w, r) +C_O1_I1(w, w) C_O1_I2(r, r, rC) C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) +C_O1_I2(r, r, rJ) C_O1_I2(r, r, rU) C_O1_I2(r, r, rW) C_O1_I2(r, r, rZ) C_O1_I2(r, 0, rZ) -C_O1_I2(r, rZ, rN) +C_O1_I2(r, rZ, ri) +C_O1_I2(r, rZ, rJ) C_O1_I2(r, rZ, rZ) +C_O1_I2(w, w, w) +C_O1_I2(w, w, wM) +C_O1_I2(w, w, wA) +C_O1_I3(w, w, w, w) +C_O1_I4(r, rZ, rJ, rZ, rZ) +C_N2_I1(r, r, r) diff --git a/tcg/loongarch64/tcg-target-con-str.h b/tcg/loongarch64/tcg-target-con-str.h index c3986a4fd4..2ba9c135ac 100644 --- a/tcg/loongarch64/tcg-target-con-str.h +++ b/tcg/loongarch64/tcg-target-con-str.h @@ -14,15 +14,17 @@ * REGS(letter, register_mask) */ REGS('r', ALL_GENERAL_REGS) -REGS('L', ALL_GENERAL_REGS & ~SOFTMMU_RESERVE_REGS) +REGS('w', ALL_VECTOR_REGS) /* * Define constraint letters for constants: * CONST(letter, TCG_CT_CONST_* bit set) */ CONST('I', TCG_CT_CONST_S12) -CONST('N', TCG_CT_CONST_N12) +CONST('J', TCG_CT_CONST_S32) CONST('U', TCG_CT_CONST_U12) CONST('Z', TCG_CT_CONST_ZERO) CONST('C', TCG_CT_CONST_C12) CONST('W', TCG_CT_CONST_WSZ) +CONST('M', TCG_CT_CONST_VCMP) +CONST('A', TCG_CT_CONST_VADD) diff --git a/tcg/loongarch64/tcg-target-reg-bits.h b/tcg/loongarch64/tcg-target-reg-bits.h new file mode 100644 index 0000000000..51373ad70a --- /dev/null +++ b/tcg/loongarch64/tcg-target-reg-bits.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2021 WANG Xuerui + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +/* + * Loongson removed the (incomplete) 32-bit support from kernel and toolchain + * for the initial upstreaming of this architecture, so don't bother and just + * support the LP64* ABI for now. + */ +#if defined(__loongarch64) +# define TCG_TARGET_REG_BITS 64 +#else +# error unsupported LoongArch register size +#endif + +#endif diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index a3debf6da7..69c5b8ac4f 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -30,6 +30,7 @@ */ #include "../tcg-ldst.c.inc" +#include #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { @@ -64,7 +65,39 @@ static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "s5", "s6", "s7", - "s8" + "s8", + "vr0", + "vr1", + "vr2", + "vr3", + "vr4", + "vr5", + "vr6", + "vr7", + "vr8", + "vr9", + "vr10", + "vr11", + "vr12", + "vr13", + "vr14", + "vr15", + "vr16", + "vr17", + "vr18", + "vr19", + "vr20", + "vr21", + "vr22", + "vr23", + "vr24", + "vr25", + "vr26", + "vr27", + "vr28", + "vr29", + "vr30", + "vr31", }; #endif @@ -101,6 +134,15 @@ static const int tcg_target_reg_alloc_order[] = { TCG_REG_A2, TCG_REG_A1, TCG_REG_A0, + + /* Vector registers */ + TCG_REG_V0, TCG_REG_V1, TCG_REG_V2, TCG_REG_V3, + TCG_REG_V4, TCG_REG_V5, TCG_REG_V6, TCG_REG_V7, + TCG_REG_V8, TCG_REG_V9, TCG_REG_V10, TCG_REG_V11, + TCG_REG_V12, TCG_REG_V13, TCG_REG_V14, TCG_REG_V15, + TCG_REG_V16, TCG_REG_V17, TCG_REG_V18, TCG_REG_V19, + TCG_REG_V20, TCG_REG_V21, TCG_REG_V22, TCG_REG_V23, + /* V24 - V31 are caller-saved, and skipped. */ }; static const int tcg_target_call_iarg_regs[] = { @@ -114,35 +156,26 @@ static const int tcg_target_call_iarg_regs[] = { TCG_REG_A7, }; -static const int tcg_target_call_oarg_regs[] = { - TCG_REG_A0, - TCG_REG_A1, -}; +static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) +{ + tcg_debug_assert(kind == TCG_CALL_RET_NORMAL); + tcg_debug_assert(slot >= 0 && slot <= 1); + return TCG_REG_A0 + slot; +} -#ifndef CONFIG_SOFTMMU -#define USE_GUEST_BASE (guest_base != 0) #define TCG_GUEST_BASE_REG TCG_REG_S1 -#endif #define TCG_CT_CONST_ZERO 0x100 #define TCG_CT_CONST_S12 0x200 -#define TCG_CT_CONST_N12 0x400 +#define TCG_CT_CONST_S32 0x400 #define TCG_CT_CONST_U12 0x800 #define TCG_CT_CONST_C12 0x1000 #define TCG_CT_CONST_WSZ 0x2000 +#define TCG_CT_CONST_VCMP 0x4000 +#define TCG_CT_CONST_VADD 0x8000 -#define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) -/* - * For softmmu, we need to avoid conflicts with the first 5 - * argument registers to call the helper. Some of these are - * also used for the tlb lookup. - */ -#ifdef CONFIG_SOFTMMU -#define SOFTMMU_RESERVE_REGS MAKE_64BIT_MASK(TCG_REG_A0, 5) -#else -#define SOFTMMU_RESERVE_REGS 0 -#endif - +#define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) +#define ALL_VECTOR_REGS MAKE_64BIT_MASK(32, 32) static inline tcg_target_long sextreg(tcg_target_long val, int pos, int len) { @@ -150,7 +183,8 @@ static inline tcg_target_long sextreg(tcg_target_long val, int pos, int len) } /* test if a constant matches the constraint */ -static bool tcg_target_const_match(int64_t val, TCGType type, int ct) +static bool tcg_target_const_match(int64_t val, int ct, + TCGType type, TCGCond cond, int vece) { if (ct & TCG_CT_CONST) { return true; @@ -161,7 +195,7 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) if ((ct & TCG_CT_CONST_S12) && val == sextreg(val, 0, 12)) { return true; } - if ((ct & TCG_CT_CONST_N12) && -val == sextreg(-val, 0, 12)) { + if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { return true; } if ((ct & TCG_CT_CONST_U12) && val >= 0 && val <= 0xfff) { @@ -173,6 +207,13 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) if ((ct & TCG_CT_CONST_WSZ) && val == (type == TCG_TYPE_I32 ? 32 : 64)) { return true; } + int64_t vec_val = sextract64(val, 0, 8 << vece); + if ((ct & TCG_CT_CONST_VCMP) && -0x10 <= vec_val && vec_val <= 0x1f) { + return true; + } + if ((ct & TCG_CT_CONST_VADD) && -0x1f <= vec_val && vec_val <= 0x1f) { + return true; + } return false; } @@ -268,22 +309,15 @@ static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) */ tcg_out_opc_or(s, ret, arg, TCG_REG_ZERO); break; + case TCG_TYPE_V128: + tcg_out_opc_vori_b(s, ret, arg, 0); + break; default: g_assert_not_reached(); } return true; } -static bool imm_part_needs_loading(bool high_bits_are_ones, - tcg_target_long part) -{ - if (high_bits_are_ones) { - return part != -1; - } else { - return part != 0; - } -} - /* Loads a 32-bit immediate into rd, sign-extended. */ static void tcg_out_movi_i32(TCGContext *s, TCGReg rd, int32_t val) { @@ -291,16 +325,16 @@ static void tcg_out_movi_i32(TCGContext *s, TCGReg rd, int32_t val) tcg_target_long hi12 = sextreg(val, 12, 20); /* Single-instruction cases. */ - if (lo == val) { - /* val fits in simm12: addi.w rd, zero, val */ - tcg_out_opc_addi_w(s, rd, TCG_REG_ZERO, val); - return; - } - if (0x800 <= val && val <= 0xfff) { + if (hi12 == 0) { /* val fits in uimm12: ori rd, zero, val */ tcg_out_opc_ori(s, rd, TCG_REG_ZERO, val); return; } + if (hi12 == sextreg(lo, 12, 20)) { + /* val fits in simm12: addi.w rd, zero, val */ + tcg_out_opc_addi_w(s, rd, TCG_REG_ZERO, val); + return; + } /* High bits must be set; load with lu12i.w + optional ori. */ tcg_out_opc_lu12i_w(s, rd, hi12); @@ -334,8 +368,7 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, intptr_t pc_offset; tcg_target_long val_lo, val_hi, pc_hi, offset_hi; - tcg_target_long hi32, hi52; - bool rd_high_bits_are_ones; + tcg_target_long hi12, hi32, hi52; /* Value fits in signed i32. */ if (type == TCG_TYPE_I32 || val == (int32_t)val) { @@ -366,29 +399,80 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, return; } + hi12 = sextreg(val, 12, 20); hi32 = sextreg(val, 32, 20); hi52 = sextreg(val, 52, 12); /* Single cu52i.d case. */ - if (ctz64(val) >= 52) { + if ((hi52 != 0) && (ctz64(val) >= 52)) { tcg_out_opc_cu52i_d(s, rd, TCG_REG_ZERO, hi52); return; } /* Slow path. Initialize the low 32 bits, then concat high bits. */ tcg_out_movi_i32(s, rd, val); - rd_high_bits_are_ones = (int32_t)val < 0; - if (imm_part_needs_loading(rd_high_bits_are_ones, hi32)) { + /* Load hi32 and hi52 explicitly when they are unexpected values. */ + if (hi32 != sextreg(hi12, 20, 20)) { tcg_out_opc_cu32i_d(s, rd, hi32); - rd_high_bits_are_ones = hi32 < 0; } - if (imm_part_needs_loading(rd_high_bits_are_ones, hi52)) { + if (hi52 != sextreg(hi32, 20, 12)) { tcg_out_opc_cu52i_d(s, rd, rd, hi52); } } +static void tcg_out_addi(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rs, tcg_target_long imm) +{ + tcg_target_long lo12 = sextreg(imm, 0, 12); + tcg_target_long hi16 = sextreg(imm - lo12, 16, 16); + + /* + * Note that there's a hole in between hi16 and lo12: + * + * 3 2 1 0 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * ...+-------------------------------+-------+-----------------------+ + * | hi16 | | lo12 | + * ...+-------------------------------+-------+-----------------------+ + * + * For bits within that hole, it's more efficient to use LU12I and ADD. + */ + if (imm == (hi16 << 16) + lo12) { + if (hi16) { + tcg_out_opc_addu16i_d(s, rd, rs, hi16); + rs = rd; + } + if (type == TCG_TYPE_I32) { + tcg_out_opc_addi_w(s, rd, rs, lo12); + } else if (lo12) { + tcg_out_opc_addi_d(s, rd, rs, lo12); + } else { + tcg_out_mov(s, type, rd, rs); + } + } else { + tcg_out_movi(s, type, TCG_REG_TMP0, imm); + if (type == TCG_TYPE_I32) { + tcg_out_opc_add_w(s, rd, rs, TCG_REG_TMP0); + } else { + tcg_out_opc_add_d(s, rd, rs, TCG_REG_TMP0); + } + } +} + +static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2) +{ + return false; +} + +static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs, + tcg_target_long imm) +{ + /* This function is only used for passing structs by reference. */ + g_assert_not_reached(); +} + static void tcg_out_ext8u(TCGContext *s, TCGReg ret, TCGReg arg) { tcg_out_opc_andi(s, ret, arg, 0xff); @@ -404,12 +488,12 @@ static void tcg_out_ext32u(TCGContext *s, TCGReg ret, TCGReg arg) tcg_out_opc_bstrpick_d(s, ret, arg, 0, 31); } -static void tcg_out_ext8s(TCGContext *s, TCGReg ret, TCGReg arg) +static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { tcg_out_opc_sext_b(s, ret, arg); } -static void tcg_out_ext16s(TCGContext *s, TCGReg ret, TCGReg arg) +static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { tcg_out_opc_sext_h(s, ret, arg); } @@ -419,6 +503,23 @@ static void tcg_out_ext32s(TCGContext *s, TCGReg ret, TCGReg arg) tcg_out_opc_addi_w(s, ret, arg, 0); } +static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg ret, TCGReg arg) +{ + if (ret != arg) { + tcg_out_ext32s(s, ret, arg); + } +} + +static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg ret, TCGReg arg) +{ + tcg_out_ext32u(s, ret, arg); +} + +static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg ret, TCGReg arg) +{ + tcg_out_ext32s(s, ret, arg); +} + static void tcg_out_clzctz(TCGContext *s, LoongArchInsn opc, TCGReg a0, TCGReg a1, TCGReg a2, bool c2, bool is_32bit) @@ -441,64 +542,155 @@ static void tcg_out_clzctz(TCGContext *s, LoongArchInsn opc, tcg_out_opc_or(s, a0, TCG_REG_TMP0, a0); } -static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg arg1, TCGReg arg2, bool c2) -{ - TCGReg tmp; +#define SETCOND_INV TCG_TARGET_NB_REGS +#define SETCOND_NEZ (SETCOND_INV << 1) +#define SETCOND_FLAGS (SETCOND_INV | SETCOND_NEZ) - if (c2) { - tcg_debug_assert(arg2 == 0); +static int tcg_out_setcond_int(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg arg1, tcg_target_long arg2, bool c2) +{ + int flags = 0; + + switch (cond) { + case TCG_COND_EQ: /* -> NE */ + case TCG_COND_GE: /* -> LT */ + case TCG_COND_GEU: /* -> LTU */ + case TCG_COND_GT: /* -> LE */ + case TCG_COND_GTU: /* -> LEU */ + cond = tcg_invert_cond(cond); + flags ^= SETCOND_INV; + break; + default: + break; } switch (cond) { - case TCG_COND_EQ: - if (c2) { - tmp = arg1; - } else { - tcg_out_opc_sub_d(s, ret, arg1, arg2); - tmp = ret; - } - tcg_out_opc_sltui(s, ret, tmp, 1); - break; - case TCG_COND_NE: - if (c2) { - tmp = arg1; - } else { - tcg_out_opc_sub_d(s, ret, arg1, arg2); - tmp = ret; - } - tcg_out_opc_sltu(s, ret, TCG_REG_ZERO, tmp); - break; - case TCG_COND_LT: - tcg_out_opc_slt(s, ret, arg1, arg2); - break; - case TCG_COND_GE: - tcg_out_opc_slt(s, ret, arg1, arg2); - tcg_out_opc_xori(s, ret, ret, 1); - break; case TCG_COND_LE: - tcg_out_setcond(s, TCG_COND_GE, ret, arg2, arg1, false); - break; - case TCG_COND_GT: - tcg_out_setcond(s, TCG_COND_LT, ret, arg2, arg1, false); - break; - case TCG_COND_LTU: - tcg_out_opc_sltu(s, ret, arg1, arg2); - break; - case TCG_COND_GEU: - tcg_out_opc_sltu(s, ret, arg1, arg2); - tcg_out_opc_xori(s, ret, ret, 1); - break; case TCG_COND_LEU: - tcg_out_setcond(s, TCG_COND_GEU, ret, arg2, arg1, false); + /* + * If we have a constant input, the most efficient way to implement + * LE is by adding 1 and using LT. Watch out for wrap around for LEU. + * We don't need to care for this for LE because the constant input + * is still constrained to int32_t, and INT32_MAX+1 is representable + * in the 64-bit temporary register. + */ + if (c2) { + if (cond == TCG_COND_LEU) { + /* unsigned <= -1 is true */ + if (arg2 == -1) { + tcg_out_movi(s, TCG_TYPE_REG, ret, !(flags & SETCOND_INV)); + return ret; + } + cond = TCG_COND_LTU; + } else { + cond = TCG_COND_LT; + } + arg2 += 1; + } else { + TCGReg tmp = arg2; + arg2 = arg1; + arg1 = tmp; + cond = tcg_swap_cond(cond); /* LE -> GE */ + cond = tcg_invert_cond(cond); /* GE -> LT */ + flags ^= SETCOND_INV; + } break; - case TCG_COND_GTU: - tcg_out_setcond(s, TCG_COND_LTU, ret, arg2, arg1, false); + default: break; + } + + switch (cond) { + case TCG_COND_NE: + flags |= SETCOND_NEZ; + if (!c2) { + tcg_out_opc_xor(s, ret, arg1, arg2); + } else if (arg2 == 0) { + ret = arg1; + } else if (arg2 >= 0 && arg2 <= 0xfff) { + tcg_out_opc_xori(s, ret, arg1, arg2); + } else { + tcg_out_addi(s, TCG_TYPE_REG, ret, arg1, -arg2); + } + break; + + case TCG_COND_LT: + case TCG_COND_LTU: + if (c2) { + if (arg2 >= -0x800 && arg2 <= 0x7ff) { + if (cond == TCG_COND_LT) { + tcg_out_opc_slti(s, ret, arg1, arg2); + } else { + tcg_out_opc_sltui(s, ret, arg1, arg2); + } + break; + } + tcg_out_movi(s, TCG_TYPE_REG, TCG_REG_TMP0, arg2); + arg2 = TCG_REG_TMP0; + } + if (cond == TCG_COND_LT) { + tcg_out_opc_slt(s, ret, arg1, arg2); + } else { + tcg_out_opc_sltu(s, ret, arg1, arg2); + } + break; + default: g_assert_not_reached(); break; } + + return ret | flags; +} + +static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg arg1, tcg_target_long arg2, bool c2) +{ + int tmpflags = tcg_out_setcond_int(s, cond, ret, arg1, arg2, c2); + + if (tmpflags != ret) { + TCGReg tmp = tmpflags & ~SETCOND_FLAGS; + + switch (tmpflags & SETCOND_FLAGS) { + case SETCOND_INV: + /* Intermediate result is boolean: simply invert. */ + tcg_out_opc_xori(s, ret, tmp, 1); + break; + case SETCOND_NEZ: + /* Intermediate result is zero/non-zero: test != 0. */ + tcg_out_opc_sltu(s, ret, TCG_REG_ZERO, tmp); + break; + case SETCOND_NEZ | SETCOND_INV: + /* Intermediate result is zero/non-zero: test == 0. */ + tcg_out_opc_sltui(s, ret, tmp, 1); + break; + default: + g_assert_not_reached(); + } + } +} + +static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg c1, tcg_target_long c2, bool const2, + TCGReg v1, TCGReg v2) +{ + int tmpflags = tcg_out_setcond_int(s, cond, TCG_REG_TMP0, c1, c2, const2); + TCGReg t; + + /* Standardize the test below to t != 0. */ + if (tmpflags & SETCOND_INV) { + t = v1, v1 = v2, v2 = t; + } + + t = tmpflags & ~SETCOND_FLAGS; + if (v1 == TCG_REG_ZERO) { + tcg_out_opc_masknez(s, ret, v2, t); + } else if (v2 == TCG_REG_ZERO) { + tcg_out_opc_maskeqz(s, ret, v1, t); + } else { + tcg_out_opc_masknez(s, TCG_REG_TMP2, v2, t); /* t ? 0 : v2 */ + tcg_out_opc_maskeqz(s, TCG_REG_TMP1, v1, t); /* t ? v1 : 0 */ + tcg_out_opc_or(s, ret, TCG_REG_TMP1, TCG_REG_TMP2); + } } /* @@ -567,7 +759,8 @@ static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *arg, bool tail) } } -static void tcg_out_call(TCGContext *s, const tcg_insn_unit *arg) +static void tcg_out_call(TCGContext *s, const tcg_insn_unit *arg, + const TCGHelperInfo *info) { tcg_out_call_int(s, arg, false); } @@ -582,7 +775,7 @@ static void tcg_out_ldst(TCGContext *s, LoongArchInsn opc, TCGReg data, intptr_t imm12 = sextreg(offset, 0, 12); if (offset != imm12) { - intptr_t diff = offset - (uintptr_t)s->code_ptr; + intptr_t diff = tcg_pcrel_diff(s, (void *)offset); if (addr == TCG_REG_ZERO && diff == (int32_t)diff) { imm12 = sextreg(diff, 0, 12); @@ -643,392 +836,296 @@ static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, * Load/store helpers for SoftMMU, and qemu_ld/st implementations */ -#if defined(CONFIG_SOFTMMU) -/* - * helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, - * MemOpIdx oi, uintptr_t ra) - */ -static void * const qemu_ld_helpers[4] = { - [MO_8] = helper_ret_ldub_mmu, - [MO_16] = helper_le_lduw_mmu, - [MO_32] = helper_le_ldul_mmu, - [MO_64] = helper_le_ldq_mmu, -}; - -/* - * helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, - * uintxx_t val, MemOpIdx oi, - * uintptr_t ra) - */ -static void * const qemu_st_helpers[4] = { - [MO_8] = helper_ret_stb_mmu, - [MO_16] = helper_le_stw_mmu, - [MO_32] = helper_le_stl_mmu, - [MO_64] = helper_le_stq_mmu, -}; - -/* We expect to use a 12-bit negative offset from ENV. */ -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -(1 << 11)); - static bool tcg_out_goto(TCGContext *s, const tcg_insn_unit *target) { tcg_out_opc_b(s, 0); return reloc_br_sd10k16(s->code_ptr - 1, target); } -/* - * Emits common code for TLB addend lookup, that eventually loads the - * addend in TCG_REG_TMP2. - */ -static void tcg_out_tlb_load(TCGContext *s, TCGReg addrl, MemOpIdx oi, - tcg_insn_unit **label_ptr, bool is_load) -{ - MemOp opc = get_memop(oi); - unsigned s_bits = opc & MO_SIZE; - unsigned a_bits = get_alignment_bits(opc); - tcg_target_long compare_mask; - int mem_index = get_mmuidx(oi); - int fast_ofs = TLB_MASK_TABLE_OFS(mem_index); - int mask_ofs = fast_ofs + offsetof(CPUTLBDescFast, mask); - int table_ofs = fast_ofs + offsetof(CPUTLBDescFast, table); - - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, TCG_AREG0, mask_ofs); - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_AREG0, table_ofs); - - tcg_out_opc_srli_d(s, TCG_REG_TMP2, addrl, - TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - tcg_out_opc_and(s, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP0); - tcg_out_opc_add_d(s, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP1); - - /* Load the tlb comparator and the addend. */ - tcg_out_ld(s, TCG_TYPE_TL, TCG_REG_TMP0, TCG_REG_TMP2, - is_load ? offsetof(CPUTLBEntry, addr_read) - : offsetof(CPUTLBEntry, addr_write)); - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP2, TCG_REG_TMP2, - offsetof(CPUTLBEntry, addend)); - - /* We don't support unaligned accesses. */ - if (a_bits < s_bits) { - a_bits = s_bits; - } - /* Clear the non-page, non-alignment bits from the address. */ - compare_mask = (tcg_target_long)TARGET_PAGE_MASK | ((1 << a_bits) - 1); - tcg_out_movi(s, TCG_TYPE_TL, TCG_REG_TMP1, compare_mask); - tcg_out_opc_and(s, TCG_REG_TMP1, TCG_REG_TMP1, addrl); - - /* Compare masked address with the TLB entry. */ - label_ptr[0] = s->code_ptr; - tcg_out_opc_bne(s, TCG_REG_TMP0, TCG_REG_TMP1, 0); - - /* TLB Hit - addend in TCG_REG_TMP2, ready for use. */ -} - -static void add_qemu_ldst_label(TCGContext *s, int is_ld, MemOpIdx oi, - TCGType type, - TCGReg datalo, TCGReg addrlo, - void *raddr, tcg_insn_unit **label_ptr) -{ - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->oi = oi; - label->type = type; - label->datalo_reg = datalo; - label->datahi_reg = 0; /* unused */ - label->addrlo_reg = addrlo; - label->addrhi_reg = 0; /* unused */ - label->raddr = tcg_splitwx_to_rx(raddr); - label->label_ptr[0] = label_ptr[0]; -} +static const TCGLdstHelperParam ldst_helper_param = { + .ntmp = 1, .tmp = { TCG_REG_TMP0 } +}; static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { - MemOpIdx oi = l->oi; - MemOp opc = get_memop(oi); - MemOp size = opc & MO_SIZE; - TCGType type = l->type; + MemOp opc = get_memop(l->oi); /* resolve label address */ if (!reloc_br_sk16(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { return false; } - /* call load helper */ - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_A0, TCG_AREG0); - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_A1, l->addrlo_reg); - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_A2, oi); - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_A3, (tcg_target_long)l->raddr); + tcg_out_ld_helper_args(s, l, &ldst_helper_param); + tcg_out_call_int(s, qemu_ld_helpers[opc & MO_SIZE], false); + tcg_out_ld_helper_ret(s, l, false, &ldst_helper_param); + return tcg_out_goto(s, l->raddr); +} - tcg_out_call(s, qemu_ld_helpers[size]); +static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + MemOp opc = get_memop(l->oi); - switch (opc & MO_SSIZE) { - case MO_SB: - tcg_out_ext8s(s, l->datalo_reg, TCG_REG_A0); - break; - case MO_SW: - tcg_out_ext16s(s, l->datalo_reg, TCG_REG_A0); - break; - case MO_SL: - tcg_out_ext32s(s, l->datalo_reg, TCG_REG_A0); - break; - case MO_UL: - if (type == TCG_TYPE_I32) { - /* MO_UL loads of i32 should be sign-extended too */ - tcg_out_ext32s(s, l->datalo_reg, TCG_REG_A0); - break; + /* resolve label address */ + if (!reloc_br_sk16(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { + return false; + } + + tcg_out_st_helper_args(s, l, &ldst_helper_param); + tcg_out_call_int(s, qemu_st_helpers[opc & MO_SIZE], false); + return tcg_out_goto(s, l->raddr); +} + +typedef struct { + TCGReg base; + TCGReg index; + TCGAtomAlign aa; +} HostAddress; + +bool tcg_target_has_memory_bswap(MemOp memop) +{ + return false; +} + +/* We expect to use a 12-bit negative offset from ENV. */ +#define MIN_TLB_MASK_TABLE_OFS -(1 << 11) + +/* + * For system-mode, perform the TLB load and compare. + * For user-mode, perform any required alignment tests. + * In both cases, return a TCGLabelQemuLdst structure if the slow path + * is required and fill in @h with the host address for the fast path. + */ +static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, + TCGReg addr_reg, MemOpIdx oi, + bool is_ld) +{ + TCGType addr_type = s->addr_type; + TCGLabelQemuLdst *ldst = NULL; + MemOp opc = get_memop(oi); + MemOp a_bits; + + h->aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, false); + a_bits = h->aa.align; + + if (tcg_use_softmmu) { + unsigned s_bits = opc & MO_SIZE; + int mem_index = get_mmuidx(oi); + int fast_ofs = tlb_mask_table_ofs(s, mem_index); + int mask_ofs = fast_ofs + offsetof(CPUTLBDescFast, mask); + int table_ofs = fast_ofs + offsetof(CPUTLBDescFast, table); + + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addr_reg; + + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, TCG_AREG0, mask_ofs); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_AREG0, table_ofs); + + tcg_out_opc_srli_d(s, TCG_REG_TMP2, addr_reg, + s->page_bits - CPU_TLB_ENTRY_BITS); + tcg_out_opc_and(s, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP0); + tcg_out_opc_add_d(s, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP1); + + /* Load the tlb comparator and the addend. */ + QEMU_BUILD_BUG_ON(HOST_BIG_ENDIAN); + tcg_out_ld(s, addr_type, TCG_REG_TMP0, TCG_REG_TMP2, + is_ld ? offsetof(CPUTLBEntry, addr_read) + : offsetof(CPUTLBEntry, addr_write)); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP2, TCG_REG_TMP2, + offsetof(CPUTLBEntry, addend)); + + /* + * For aligned accesses, we check the first byte and include the + * alignment bits within the address. For unaligned access, we + * check that we don't cross pages using the address of the last + * byte of the access. + */ + if (a_bits < s_bits) { + unsigned a_mask = (1u << a_bits) - 1; + unsigned s_mask = (1u << s_bits) - 1; + tcg_out_addi(s, addr_type, TCG_REG_TMP1, addr_reg, s_mask - a_mask); + } else { + tcg_out_mov(s, addr_type, TCG_REG_TMP1, addr_reg); } - /* fallthrough */ - default: - tcg_out_mov(s, type, l->datalo_reg, TCG_REG_A0); - break; + tcg_out_opc_bstrins_d(s, TCG_REG_TMP1, TCG_REG_ZERO, + a_bits, s->page_bits - 1); + + /* Compare masked address with the TLB entry. */ + ldst->label_ptr[0] = s->code_ptr; + tcg_out_opc_bne(s, TCG_REG_TMP0, TCG_REG_TMP1, 0); + + h->index = TCG_REG_TMP2; + } else { + if (a_bits) { + ldst = new_ldst_label(s); + + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addr_reg; + + /* + * Without micro-architecture details, we don't know which of + * bstrpick or andi is faster, so use bstrpick as it's not + * constrained by imm field width. Not to say alignments >= 2^12 + * are going to happen any time soon. + */ + tcg_out_opc_bstrpick_d(s, TCG_REG_TMP1, addr_reg, 0, a_bits - 1); + + ldst->label_ptr[0] = s->code_ptr; + tcg_out_opc_bne(s, TCG_REG_TMP1, TCG_REG_ZERO, 0); + } + + h->index = guest_base ? TCG_GUEST_BASE_REG : TCG_REG_ZERO; } - return tcg_out_goto(s, l->raddr); -} - -static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - MemOpIdx oi = l->oi; - MemOp opc = get_memop(oi); - MemOp size = opc & MO_SIZE; - - /* resolve label address */ - if (!reloc_br_sk16(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { - return false; + if (addr_type == TCG_TYPE_I32) { + h->base = TCG_REG_TMP0; + tcg_out_ext32u(s, h->base, addr_reg); + } else { + h->base = addr_reg; } - /* call store helper */ - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_A0, TCG_AREG0); - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_A1, l->addrlo_reg); - switch (size) { - case MO_8: - tcg_out_ext8u(s, TCG_REG_A2, l->datalo_reg); - break; - case MO_16: - tcg_out_ext16u(s, TCG_REG_A2, l->datalo_reg); - break; - case MO_32: - tcg_out_ext32u(s, TCG_REG_A2, l->datalo_reg); - break; - case MO_64: - tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_A2, l->datalo_reg); - break; - default: - g_assert_not_reached(); - break; - } - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_A3, oi); - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_A4, (tcg_target_long)l->raddr); - - tcg_out_call(s, qemu_st_helpers[size]); - - return tcg_out_goto(s, l->raddr); -} -#else - -/* - * Alignment helpers for user-mode emulation - */ - -static void tcg_out_test_alignment(TCGContext *s, bool is_ld, TCGReg addr_reg, - unsigned a_bits) -{ - TCGLabelQemuLdst *l = new_ldst_label(s); - - l->is_ld = is_ld; - l->addrlo_reg = addr_reg; - - /* - * Without micro-architecture details, we don't know which of bstrpick or - * andi is faster, so use bstrpick as it's not constrained by imm field - * width. (Not to say alignments >= 2^12 are going to happen any time - * soon, though) - */ - tcg_out_opc_bstrpick_d(s, TCG_REG_TMP1, addr_reg, 0, a_bits - 1); - - l->label_ptr[0] = s->code_ptr; - tcg_out_opc_bne(s, TCG_REG_TMP1, TCG_REG_ZERO, 0); - - l->raddr = tcg_splitwx_to_rx(s->code_ptr); + return ldst; } -static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) -{ - /* resolve label address */ - if (!reloc_br_sk16(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { - return false; - } - - tcg_out_mov(s, TCG_TYPE_TL, TCG_REG_A1, l->addrlo_reg); - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_A0, TCG_AREG0); - - /* tail call, with the return address back inline. */ - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RA, (uintptr_t)l->raddr); - tcg_out_call_int(s, (const void *)(l->is_ld ? helper_unaligned_ld - : helper_unaligned_st), true); - return true; -} - -static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - return tcg_out_fail_alignment(s, l); -} - -static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - return tcg_out_fail_alignment(s, l); -} - -#endif /* CONFIG_SOFTMMU */ - -/* - * `ext32u` the address register into the temp register given, - * if target is 32-bit, no-op otherwise. - * - * Returns the address register ready for use with TLB addend. - */ -static TCGReg tcg_out_zext_addr_if_32_bit(TCGContext *s, - TCGReg addr, TCGReg tmp) -{ - if (TARGET_LONG_BITS == 32) { - tcg_out_ext32u(s, tmp, addr); - return tmp; - } - return addr; -} - -static void tcg_out_qemu_ld_indexed(TCGContext *s, TCGReg rd, TCGReg rj, - TCGReg rk, MemOp opc, TCGType type) +static void tcg_out_qemu_ld_indexed(TCGContext *s, MemOp opc, TCGType type, + TCGReg rd, HostAddress h) { /* Byte swapping is left to middle-end expansion. */ tcg_debug_assert((opc & MO_BSWAP) == 0); switch (opc & MO_SSIZE) { case MO_UB: - tcg_out_opc_ldx_bu(s, rd, rj, rk); + tcg_out_opc_ldx_bu(s, rd, h.base, h.index); break; case MO_SB: - tcg_out_opc_ldx_b(s, rd, rj, rk); + tcg_out_opc_ldx_b(s, rd, h.base, h.index); break; case MO_UW: - tcg_out_opc_ldx_hu(s, rd, rj, rk); + tcg_out_opc_ldx_hu(s, rd, h.base, h.index); break; case MO_SW: - tcg_out_opc_ldx_h(s, rd, rj, rk); + tcg_out_opc_ldx_h(s, rd, h.base, h.index); break; case MO_UL: if (type == TCG_TYPE_I64) { - tcg_out_opc_ldx_wu(s, rd, rj, rk); + tcg_out_opc_ldx_wu(s, rd, h.base, h.index); break; } /* fallthrough */ case MO_SL: - tcg_out_opc_ldx_w(s, rd, rj, rk); + tcg_out_opc_ldx_w(s, rd, h.base, h.index); break; case MO_UQ: - tcg_out_opc_ldx_d(s, rd, rj, rk); + tcg_out_opc_ldx_d(s, rd, h.base, h.index); break; default: g_assert_not_reached(); } } -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, TCGType type) +static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, + MemOpIdx oi, TCGType data_type) { - TCGReg addr_regl; - TCGReg data_regl; - MemOpIdx oi; - MemOp opc; -#if defined(CONFIG_SOFTMMU) - tcg_insn_unit *label_ptr[1]; -#else - unsigned a_bits; -#endif - TCGReg base; + TCGLabelQemuLdst *ldst; + HostAddress h; - data_regl = *args++; - addr_regl = *args++; - oi = *args++; - opc = get_memop(oi); + ldst = prepare_host_addr(s, &h, addr_reg, oi, true); + tcg_out_qemu_ld_indexed(s, get_memop(oi), data_type, data_reg, h); -#if defined(CONFIG_SOFTMMU) - tcg_out_tlb_load(s, addr_regl, oi, label_ptr, 1); - base = tcg_out_zext_addr_if_32_bit(s, addr_regl, TCG_REG_TMP0); - tcg_out_qemu_ld_indexed(s, data_regl, base, TCG_REG_TMP2, opc, type); - add_qemu_ldst_label(s, 1, oi, type, - data_regl, addr_regl, - s->code_ptr, label_ptr); -#else - a_bits = get_alignment_bits(opc); - if (a_bits) { - tcg_out_test_alignment(s, true, addr_regl, a_bits); + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = data_reg; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } - base = tcg_out_zext_addr_if_32_bit(s, addr_regl, TCG_REG_TMP0); - TCGReg guest_base_reg = USE_GUEST_BASE ? TCG_GUEST_BASE_REG : TCG_REG_ZERO; - tcg_out_qemu_ld_indexed(s, data_regl, base, guest_base_reg, opc, type); -#endif } -static void tcg_out_qemu_st_indexed(TCGContext *s, TCGReg data, - TCGReg rj, TCGReg rk, MemOp opc) +static void tcg_out_qemu_st_indexed(TCGContext *s, MemOp opc, + TCGReg rd, HostAddress h) { /* Byte swapping is left to middle-end expansion. */ tcg_debug_assert((opc & MO_BSWAP) == 0); switch (opc & MO_SIZE) { case MO_8: - tcg_out_opc_stx_b(s, data, rj, rk); + tcg_out_opc_stx_b(s, rd, h.base, h.index); break; case MO_16: - tcg_out_opc_stx_h(s, data, rj, rk); + tcg_out_opc_stx_h(s, rd, h.base, h.index); break; case MO_32: - tcg_out_opc_stx_w(s, data, rj, rk); + tcg_out_opc_stx_w(s, rd, h.base, h.index); break; case MO_64: - tcg_out_opc_stx_d(s, data, rj, rk); + tcg_out_opc_stx_d(s, rd, h.base, h.index); break; default: g_assert_not_reached(); } } -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args) +static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, + MemOpIdx oi, TCGType data_type) { - TCGReg addr_regl; - TCGReg data_regl; - MemOpIdx oi; - MemOp opc; -#if defined(CONFIG_SOFTMMU) - tcg_insn_unit *label_ptr[1]; -#else - unsigned a_bits; -#endif - TCGReg base; + TCGLabelQemuLdst *ldst; + HostAddress h; - data_regl = *args++; - addr_regl = *args++; - oi = *args++; - opc = get_memop(oi); + ldst = prepare_host_addr(s, &h, addr_reg, oi, false); + tcg_out_qemu_st_indexed(s, get_memop(oi), data_reg, h); -#if defined(CONFIG_SOFTMMU) - tcg_out_tlb_load(s, addr_regl, oi, label_ptr, 0); - base = tcg_out_zext_addr_if_32_bit(s, addr_regl, TCG_REG_TMP0); - tcg_out_qemu_st_indexed(s, data_regl, base, TCG_REG_TMP2, opc); - add_qemu_ldst_label(s, 0, oi, - 0, /* type param is unused for stores */ - data_regl, addr_regl, - s->code_ptr, label_ptr); -#else - a_bits = get_alignment_bits(opc); - if (a_bits) { - tcg_out_test_alignment(s, false, addr_regl, a_bits); + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = data_reg; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } +} + +static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg data_lo, TCGReg data_hi, + TCGReg addr_reg, MemOpIdx oi, bool is_ld) +{ + TCGLabelQemuLdst *ldst; + HostAddress h; + + ldst = prepare_host_addr(s, &h, addr_reg, oi, is_ld); + + if (h.aa.atom == MO_128) { + /* + * Use VLDX/VSTX when 128-bit atomicity is required. + * If address is aligned to 16-bytes, the 128-bit load/store is atomic. + */ + if (is_ld) { + tcg_out_opc_vldx(s, TCG_VEC_TMP0, h.base, h.index); + tcg_out_opc_vpickve2gr_d(s, data_lo, TCG_VEC_TMP0, 0); + tcg_out_opc_vpickve2gr_d(s, data_hi, TCG_VEC_TMP0, 1); + } else { + tcg_out_opc_vinsgr2vr_d(s, TCG_VEC_TMP0, data_lo, 0); + tcg_out_opc_vinsgr2vr_d(s, TCG_VEC_TMP0, data_hi, 1); + tcg_out_opc_vstx(s, TCG_VEC_TMP0, h.base, h.index); + } + } else { + /* Otherwise use a pair of LD/ST. */ + TCGReg base = h.base; + if (h.index != TCG_REG_ZERO) { + base = TCG_REG_TMP0; + tcg_out_opc_add_d(s, base, h.base, h.index); + } + if (is_ld) { + tcg_debug_assert(base != data_lo); + tcg_out_opc_ld_d(s, data_lo, base, 0); + tcg_out_opc_ld_d(s, data_hi, base, 8); + } else { + tcg_out_opc_st_d(s, data_lo, base, 0); + tcg_out_opc_st_d(s, data_hi, base, 8); + } + } + + if (ldst) { + ldst->type = TCG_TYPE_I128; + ldst->datalo_reg = data_lo; + ldst->datahi_reg = data_hi; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } - base = tcg_out_zext_addr_if_32_bit(s, addr_regl, TCG_REG_TMP0); - TCGReg guest_base_reg = USE_GUEST_BASE ? TCG_GUEST_BASE_REG : TCG_REG_ZERO; - tcg_out_qemu_st_indexed(s, data_regl, base, guest_base_reg, opc); -#endif } /* @@ -1037,6 +1134,57 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args) static const tcg_insn_unit *tb_ret_addr; +static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) +{ + /* Reuse the zeroing that exists for goto_ptr. */ + if (a0 == 0) { + tcg_out_call_int(s, tcg_code_gen_epilogue, true); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_A0, a0); + tcg_out_call_int(s, tb_ret_addr, true); + } +} + +static void tcg_out_goto_tb(TCGContext *s, int which) +{ + /* + * Direct branch, or load indirect address, to be patched + * by tb_target_set_jmp_target. Check indirect load offset + * in range early, regardless of direct branch distance, + * via assert within tcg_out_opc_pcaddu2i. + */ + uintptr_t i_addr = get_jmp_target_addr(s, which); + intptr_t i_disp = tcg_pcrel_diff(s, (void *)i_addr); + + set_jmp_insn_offset(s, which); + tcg_out_opc_pcaddu2i(s, TCG_REG_TMP0, i_disp >> 2); + + /* Finish the load and indirect branch. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, TCG_REG_TMP0, 0); + tcg_out_opc_jirl(s, TCG_REG_ZERO, TCG_REG_TMP0, 0); + set_jmp_reset_offset(s, which); +} + +void tb_target_set_jmp_target(const TranslationBlock *tb, int n, + uintptr_t jmp_rx, uintptr_t jmp_rw) +{ + uintptr_t d_addr = tb->jmp_target_addr[n]; + ptrdiff_t d_disp = (ptrdiff_t)(d_addr - jmp_rx) >> 2; + tcg_insn_unit insn; + + /* Either directly branch, or load slot address for indirect branch. */ + if (d_disp == sextreg(d_disp, 0, 26)) { + insn = encode_sd10k16_insn(OPC_B, d_disp); + } else { + uintptr_t i_addr = (uintptr_t)&tb->jmp_target_addr[n]; + intptr_t i_disp = i_addr - jmp_rx; + insn = encode_dsj20_insn(OPC_PCADDU2I, TCG_REG_TMP0, i_disp >> 2); + } + + qatomic_set((tcg_insn_unit *)jmp_rw, insn); + flush_idcache_range(jmp_rx, jmp_rw, 4); +} + static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1044,28 +1192,10 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGArg a0 = args[0]; TCGArg a1 = args[1]; TCGArg a2 = args[2]; + TCGArg a3 = args[3]; int c2 = const_args[2]; switch (opc) { - case INDEX_op_exit_tb: - /* Reuse the zeroing that exists for goto_ptr. */ - if (a0 == 0) { - tcg_out_call_int(s, tcg_code_gen_epilogue, true); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_A0, a0); - tcg_out_call_int(s, tb_ret_addr, true); - } - break; - - case INDEX_op_goto_tb: - assert(s->tb_jmp_insn_offset == 0); - /* indirect jump method */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, TCG_REG_ZERO, - (uintptr_t)(s->tb_jmp_target_addr + a0)); - tcg_out_opc_jirl(s, TCG_REG_ZERO, TCG_REG_TMP0, 0); - set_jmp_reset_offset(s, a0); - break; - case INDEX_op_mb: tcg_out_mb(s, a0); break; @@ -1085,37 +1215,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_brcond(s, a2, a0, a1, arg_label(args[3])); break; - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - tcg_out_ext8s(s, a0, a1); - break; - - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - tcg_out_ext8u(s, a0, a1); - break; - - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - tcg_out_ext16s(s, a0, a1); - break; - - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - tcg_out_ext16u(s, a0, a1); - break; - - case INDEX_op_ext32u_i64: - case INDEX_op_extu_i32_i64: - tcg_out_ext32u(s, a0, a1); - break; - - case INDEX_op_ext32s_i64: - case INDEX_op_extrl_i64_i32: - case INDEX_op_ext_i32_i64: - tcg_out_ext32s(s, a0, a1); - break; - case INDEX_op_extrh_i64_i32: tcg_out_opc_srai_d(s, a0, a1, 32); break; @@ -1200,7 +1299,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_bswap16_i64: tcg_out_opc_revb_2h(s, a0, a1); if (a2 & TCG_BSWAP_OS) { - tcg_out_ext16s(s, a0, a0); + tcg_out_ext16s(s, TCG_TYPE_REG, a0, a0); } else if ((a2 & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { tcg_out_ext16u(s, a0, a0); } @@ -1318,14 +1417,14 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_add_i32: if (c2) { - tcg_out_opc_addi_w(s, a0, a1, a2); + tcg_out_addi(s, TCG_TYPE_I32, a0, a1, a2); } else { tcg_out_opc_add_w(s, a0, a1, a2); } break; case INDEX_op_add_i64: if (c2) { - tcg_out_opc_addi_d(s, a0, a1, a2); + tcg_out_addi(s, TCG_TYPE_I64, a0, a1, a2); } else { tcg_out_opc_add_d(s, a0, a1, a2); } @@ -1333,19 +1432,26 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_sub_i32: if (c2) { - tcg_out_opc_addi_w(s, a0, a1, -a2); + tcg_out_addi(s, TCG_TYPE_I32, a0, a1, -a2); } else { tcg_out_opc_sub_w(s, a0, a1, a2); } break; case INDEX_op_sub_i64: if (c2) { - tcg_out_opc_addi_d(s, a0, a1, -a2); + tcg_out_addi(s, TCG_TYPE_I64, a0, a1, -a2); } else { tcg_out_opc_sub_d(s, a0, a1, a2); } break; + case INDEX_op_neg_i32: + tcg_out_opc_sub_w(s, a0, TCG_REG_ZERO, a1); + break; + case INDEX_op_neg_i64: + tcg_out_opc_sub_d(s, a0, TCG_REG_ZERO, a1); + break; + case INDEX_op_mul_i32: tcg_out_opc_mul_w(s, a0, a1, a2); break; @@ -1400,6 +1506,11 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_setcond(s, args[3], a0, a1, a2, c2); break; + case INDEX_op_movcond_i32: + case INDEX_op_movcond_i64: + tcg_out_movcond(s, args[5], a0, a1, a2, c2, args[3], args[4]); + break; + case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: tcg_out_ldst(s, OPC_LD_B, a0, a1, a2); @@ -1443,27 +1554,494 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_ldst(s, OPC_ST_D, a0, a1, a2); break; - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args, TCG_TYPE_I32); + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args, TCG_TYPE_I64); + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: + tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I64); break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args); + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: + tcg_out_qemu_ldst_i128(s, a0, a1, a2, a3, true); break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args); + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I32); + break; + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: + tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I64); + break; + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: + tcg_out_qemu_ldst_i128(s, a0, a1, a2, a3, false); break; case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ + case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ + case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ + case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ + case INDEX_op_ext8s_i64: + case INDEX_op_ext8u_i32: + case INDEX_op_ext8u_i64: + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + case INDEX_op_ext16u_i32: + case INDEX_op_ext16u_i64: + case INDEX_op_ext32s_i64: + case INDEX_op_ext32u_i64: + case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); } } +static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg rd, TCGReg rs) +{ + switch (vece) { + case MO_8: + tcg_out_opc_vreplgr2vr_b(s, rd, rs); + break; + case MO_16: + tcg_out_opc_vreplgr2vr_h(s, rd, rs); + break; + case MO_32: + tcg_out_opc_vreplgr2vr_w(s, rd, rs); + break; + case MO_64: + tcg_out_opc_vreplgr2vr_d(s, rd, rs); + break; + default: + g_assert_not_reached(); + } + return true; +} + +static bool tcg_out_dupm_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg r, TCGReg base, intptr_t offset) +{ + /* Handle imm overflow and division (vldrepl.d imm is divided by 8) */ + if (offset < -0x800 || offset > 0x7ff || \ + (offset & ((1 << vece) - 1)) != 0) { + tcg_out_addi(s, TCG_TYPE_I64, TCG_REG_TMP0, base, offset); + base = TCG_REG_TMP0; + offset = 0; + } + offset >>= vece; + + switch (vece) { + case MO_8: + tcg_out_opc_vldrepl_b(s, r, base, offset); + break; + case MO_16: + tcg_out_opc_vldrepl_h(s, r, base, offset); + break; + case MO_32: + tcg_out_opc_vldrepl_w(s, r, base, offset); + break; + case MO_64: + tcg_out_opc_vldrepl_d(s, r, base, offset); + break; + default: + g_assert_not_reached(); + } + return true; +} + +static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg rd, int64_t v64) +{ + /* Try vldi if imm can fit */ + int64_t value = sextract64(v64, 0, 8 << vece); + if (-0x200 <= value && value <= 0x1FF) { + uint32_t imm = (vece << 10) | ((uint32_t)v64 & 0x3FF); + tcg_out_opc_vldi(s, rd, imm); + return; + } + + /* TODO: vldi patterns when imm 12 is set */ + + /* Fallback to vreplgr2vr */ + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP0, value); + switch (vece) { + case MO_8: + tcg_out_opc_vreplgr2vr_b(s, rd, TCG_REG_TMP0); + break; + case MO_16: + tcg_out_opc_vreplgr2vr_h(s, rd, TCG_REG_TMP0); + break; + case MO_32: + tcg_out_opc_vreplgr2vr_w(s, rd, TCG_REG_TMP0); + break; + case MO_64: + tcg_out_opc_vreplgr2vr_d(s, rd, TCG_REG_TMP0); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_addsub_vec(TCGContext *s, unsigned vece, const TCGArg a0, + const TCGArg a1, const TCGArg a2, + bool a2_is_const, bool is_add) +{ + static const LoongArchInsn add_vec_insn[4] = { + OPC_VADD_B, OPC_VADD_H, OPC_VADD_W, OPC_VADD_D + }; + static const LoongArchInsn add_vec_imm_insn[4] = { + OPC_VADDI_BU, OPC_VADDI_HU, OPC_VADDI_WU, OPC_VADDI_DU + }; + static const LoongArchInsn sub_vec_insn[4] = { + OPC_VSUB_B, OPC_VSUB_H, OPC_VSUB_W, OPC_VSUB_D + }; + static const LoongArchInsn sub_vec_imm_insn[4] = { + OPC_VSUBI_BU, OPC_VSUBI_HU, OPC_VSUBI_WU, OPC_VSUBI_DU + }; + + if (a2_is_const) { + int64_t value = sextract64(a2, 0, 8 << vece); + if (!is_add) { + value = -value; + } + + /* Try vaddi/vsubi */ + if (0 <= value && value <= 0x1f) { + tcg_out32(s, encode_vdvjuk5_insn(add_vec_imm_insn[vece], a0, \ + a1, value)); + return; + } else if (-0x1f <= value && value < 0) { + tcg_out32(s, encode_vdvjuk5_insn(sub_vec_imm_insn[vece], a0, \ + a1, -value)); + return; + } + + /* constraint TCG_CT_CONST_VADD ensures unreachable */ + g_assert_not_reached(); + } + + if (is_add) { + tcg_out32(s, encode_vdvjvk_insn(add_vec_insn[vece], a0, a1, a2)); + } else { + tcg_out32(s, encode_vdvjvk_insn(sub_vec_insn[vece], a0, a1, a2)); + } +} + +static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, + unsigned vecl, unsigned vece, + const TCGArg args[TCG_MAX_OP_ARGS], + const int const_args[TCG_MAX_OP_ARGS]) +{ + TCGType type = vecl + TCG_TYPE_V64; + TCGArg a0, a1, a2, a3; + TCGReg temp = TCG_REG_TMP0; + TCGReg temp_vec = TCG_VEC_TMP0; + + static const LoongArchInsn cmp_vec_insn[16][4] = { + [TCG_COND_EQ] = {OPC_VSEQ_B, OPC_VSEQ_H, OPC_VSEQ_W, OPC_VSEQ_D}, + [TCG_COND_LE] = {OPC_VSLE_B, OPC_VSLE_H, OPC_VSLE_W, OPC_VSLE_D}, + [TCG_COND_LEU] = {OPC_VSLE_BU, OPC_VSLE_HU, OPC_VSLE_WU, OPC_VSLE_DU}, + [TCG_COND_LT] = {OPC_VSLT_B, OPC_VSLT_H, OPC_VSLT_W, OPC_VSLT_D}, + [TCG_COND_LTU] = {OPC_VSLT_BU, OPC_VSLT_HU, OPC_VSLT_WU, OPC_VSLT_DU}, + }; + static const LoongArchInsn cmp_vec_imm_insn[16][4] = { + [TCG_COND_EQ] = {OPC_VSEQI_B, OPC_VSEQI_H, OPC_VSEQI_W, OPC_VSEQI_D}, + [TCG_COND_LE] = {OPC_VSLEI_B, OPC_VSLEI_H, OPC_VSLEI_W, OPC_VSLEI_D}, + [TCG_COND_LEU] = {OPC_VSLEI_BU, OPC_VSLEI_HU, OPC_VSLEI_WU, OPC_VSLEI_DU}, + [TCG_COND_LT] = {OPC_VSLTI_B, OPC_VSLTI_H, OPC_VSLTI_W, OPC_VSLTI_D}, + [TCG_COND_LTU] = {OPC_VSLTI_BU, OPC_VSLTI_HU, OPC_VSLTI_WU, OPC_VSLTI_DU}, + }; + LoongArchInsn insn; + static const LoongArchInsn neg_vec_insn[4] = { + OPC_VNEG_B, OPC_VNEG_H, OPC_VNEG_W, OPC_VNEG_D + }; + static const LoongArchInsn mul_vec_insn[4] = { + OPC_VMUL_B, OPC_VMUL_H, OPC_VMUL_W, OPC_VMUL_D + }; + static const LoongArchInsn smin_vec_insn[4] = { + OPC_VMIN_B, OPC_VMIN_H, OPC_VMIN_W, OPC_VMIN_D + }; + static const LoongArchInsn umin_vec_insn[4] = { + OPC_VMIN_BU, OPC_VMIN_HU, OPC_VMIN_WU, OPC_VMIN_DU + }; + static const LoongArchInsn smax_vec_insn[4] = { + OPC_VMAX_B, OPC_VMAX_H, OPC_VMAX_W, OPC_VMAX_D + }; + static const LoongArchInsn umax_vec_insn[4] = { + OPC_VMAX_BU, OPC_VMAX_HU, OPC_VMAX_WU, OPC_VMAX_DU + }; + static const LoongArchInsn ssadd_vec_insn[4] = { + OPC_VSADD_B, OPC_VSADD_H, OPC_VSADD_W, OPC_VSADD_D + }; + static const LoongArchInsn usadd_vec_insn[4] = { + OPC_VSADD_BU, OPC_VSADD_HU, OPC_VSADD_WU, OPC_VSADD_DU + }; + static const LoongArchInsn sssub_vec_insn[4] = { + OPC_VSSUB_B, OPC_VSSUB_H, OPC_VSSUB_W, OPC_VSSUB_D + }; + static const LoongArchInsn ussub_vec_insn[4] = { + OPC_VSSUB_BU, OPC_VSSUB_HU, OPC_VSSUB_WU, OPC_VSSUB_DU + }; + static const LoongArchInsn shlv_vec_insn[4] = { + OPC_VSLL_B, OPC_VSLL_H, OPC_VSLL_W, OPC_VSLL_D + }; + static const LoongArchInsn shrv_vec_insn[4] = { + OPC_VSRL_B, OPC_VSRL_H, OPC_VSRL_W, OPC_VSRL_D + }; + static const LoongArchInsn sarv_vec_insn[4] = { + OPC_VSRA_B, OPC_VSRA_H, OPC_VSRA_W, OPC_VSRA_D + }; + static const LoongArchInsn shli_vec_insn[4] = { + OPC_VSLLI_B, OPC_VSLLI_H, OPC_VSLLI_W, OPC_VSLLI_D + }; + static const LoongArchInsn shri_vec_insn[4] = { + OPC_VSRLI_B, OPC_VSRLI_H, OPC_VSRLI_W, OPC_VSRLI_D + }; + static const LoongArchInsn sari_vec_insn[4] = { + OPC_VSRAI_B, OPC_VSRAI_H, OPC_VSRAI_W, OPC_VSRAI_D + }; + static const LoongArchInsn rotrv_vec_insn[4] = { + OPC_VROTR_B, OPC_VROTR_H, OPC_VROTR_W, OPC_VROTR_D + }; + + a0 = args[0]; + a1 = args[1]; + a2 = args[2]; + a3 = args[3]; + + /* Currently only supports V128 */ + tcg_debug_assert(type == TCG_TYPE_V128); + + switch (opc) { + case INDEX_op_st_vec: + /* Try to fit vst imm */ + if (-0x800 <= a2 && a2 <= 0x7ff) { + tcg_out_opc_vst(s, a0, a1, a2); + } else { + tcg_out_movi(s, TCG_TYPE_I64, temp, a2); + tcg_out_opc_vstx(s, a0, a1, temp); + } + break; + case INDEX_op_ld_vec: + /* Try to fit vld imm */ + if (-0x800 <= a2 && a2 <= 0x7ff) { + tcg_out_opc_vld(s, a0, a1, a2); + } else { + tcg_out_movi(s, TCG_TYPE_I64, temp, a2); + tcg_out_opc_vldx(s, a0, a1, temp); + } + break; + case INDEX_op_and_vec: + tcg_out_opc_vand_v(s, a0, a1, a2); + break; + case INDEX_op_andc_vec: + /* + * vandn vd, vj, vk: vd = vk & ~vj + * andc_vec vd, vj, vk: vd = vj & ~vk + * vk and vk are swapped + */ + tcg_out_opc_vandn_v(s, a0, a2, a1); + break; + case INDEX_op_or_vec: + tcg_out_opc_vor_v(s, a0, a1, a2); + break; + case INDEX_op_orc_vec: + tcg_out_opc_vorn_v(s, a0, a1, a2); + break; + case INDEX_op_xor_vec: + tcg_out_opc_vxor_v(s, a0, a1, a2); + break; + case INDEX_op_nor_vec: + tcg_out_opc_vnor_v(s, a0, a1, a2); + break; + case INDEX_op_not_vec: + tcg_out_opc_vnor_v(s, a0, a1, a1); + break; + case INDEX_op_cmp_vec: + { + TCGCond cond = args[3]; + if (const_args[2]) { + /* + * cmp_vec dest, src, value + * Try vseqi/vslei/vslti + */ + int64_t value = sextract64(a2, 0, 8 << vece); + if ((cond == TCG_COND_EQ || cond == TCG_COND_LE || \ + cond == TCG_COND_LT) && (-0x10 <= value && value <= 0x0f)) { + tcg_out32(s, encode_vdvjsk5_insn(cmp_vec_imm_insn[cond][vece], \ + a0, a1, value)); + break; + } else if ((cond == TCG_COND_LEU || cond == TCG_COND_LTU) && + (0x00 <= value && value <= 0x1f)) { + tcg_out32(s, encode_vdvjuk5_insn(cmp_vec_imm_insn[cond][vece], \ + a0, a1, value)); + break; + } + + /* + * Fallback to: + * dupi_vec temp, a2 + * cmp_vec a0, a1, temp, cond + */ + tcg_out_dupi_vec(s, type, vece, temp_vec, a2); + a2 = temp_vec; + } + + insn = cmp_vec_insn[cond][vece]; + if (insn == 0) { + TCGArg t; + t = a1, a1 = a2, a2 = t; + cond = tcg_swap_cond(cond); + insn = cmp_vec_insn[cond][vece]; + tcg_debug_assert(insn != 0); + } + tcg_out32(s, encode_vdvjvk_insn(insn, a0, a1, a2)); + } + break; + case INDEX_op_add_vec: + tcg_out_addsub_vec(s, vece, a0, a1, a2, const_args[2], true); + break; + case INDEX_op_sub_vec: + tcg_out_addsub_vec(s, vece, a0, a1, a2, const_args[2], false); + break; + case INDEX_op_neg_vec: + tcg_out32(s, encode_vdvj_insn(neg_vec_insn[vece], a0, a1)); + break; + case INDEX_op_mul_vec: + tcg_out32(s, encode_vdvjvk_insn(mul_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_smin_vec: + tcg_out32(s, encode_vdvjvk_insn(smin_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_smax_vec: + tcg_out32(s, encode_vdvjvk_insn(smax_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_umin_vec: + tcg_out32(s, encode_vdvjvk_insn(umin_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_umax_vec: + tcg_out32(s, encode_vdvjvk_insn(umax_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_ssadd_vec: + tcg_out32(s, encode_vdvjvk_insn(ssadd_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_usadd_vec: + tcg_out32(s, encode_vdvjvk_insn(usadd_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_sssub_vec: + tcg_out32(s, encode_vdvjvk_insn(sssub_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_ussub_vec: + tcg_out32(s, encode_vdvjvk_insn(ussub_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_shlv_vec: + tcg_out32(s, encode_vdvjvk_insn(shlv_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_shrv_vec: + tcg_out32(s, encode_vdvjvk_insn(shrv_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_sarv_vec: + tcg_out32(s, encode_vdvjvk_insn(sarv_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_shli_vec: + tcg_out32(s, encode_vdvjuk3_insn(shli_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_shri_vec: + tcg_out32(s, encode_vdvjuk3_insn(shri_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_sari_vec: + tcg_out32(s, encode_vdvjuk3_insn(sari_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_rotrv_vec: + tcg_out32(s, encode_vdvjvk_insn(rotrv_vec_insn[vece], a0, a1, a2)); + break; + case INDEX_op_rotlv_vec: + /* rotlv_vec a1, a2 = rotrv_vec a1, -a2 */ + tcg_out32(s, encode_vdvj_insn(neg_vec_insn[vece], temp_vec, a2)); + tcg_out32(s, encode_vdvjvk_insn(rotrv_vec_insn[vece], a0, a1, + temp_vec)); + break; + case INDEX_op_rotli_vec: + /* rotli_vec a1, a2 = rotri_vec a1, -a2 */ + a2 = extract32(-a2, 0, 3 + vece); + switch (vece) { + case MO_8: + tcg_out_opc_vrotri_b(s, a0, a1, a2); + break; + case MO_16: + tcg_out_opc_vrotri_h(s, a0, a1, a2); + break; + case MO_32: + tcg_out_opc_vrotri_w(s, a0, a1, a2); + break; + case MO_64: + tcg_out_opc_vrotri_d(s, a0, a1, a2); + break; + default: + g_assert_not_reached(); + } + break; + case INDEX_op_bitsel_vec: + /* vbitsel vd, vj, vk, va = bitsel_vec vd, va, vk, vj */ + tcg_out_opc_vbitsel_v(s, a0, a3, a2, a1); + break; + case INDEX_op_dupm_vec: + tcg_out_dupm_vec(s, type, vece, a0, a1, a2); + break; + default: + g_assert_not_reached(); + } +} + +int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) +{ + switch (opc) { + case INDEX_op_ld_vec: + case INDEX_op_st_vec: + case INDEX_op_dup_vec: + case INDEX_op_dupm_vec: + case INDEX_op_cmp_vec: + case INDEX_op_add_vec: + case INDEX_op_sub_vec: + case INDEX_op_and_vec: + case INDEX_op_andc_vec: + case INDEX_op_or_vec: + case INDEX_op_orc_vec: + case INDEX_op_xor_vec: + case INDEX_op_nor_vec: + case INDEX_op_not_vec: + case INDEX_op_neg_vec: + case INDEX_op_mul_vec: + case INDEX_op_smin_vec: + case INDEX_op_smax_vec: + case INDEX_op_umin_vec: + case INDEX_op_umax_vec: + case INDEX_op_ssadd_vec: + case INDEX_op_usadd_vec: + case INDEX_op_sssub_vec: + case INDEX_op_ussub_vec: + case INDEX_op_shlv_vec: + case INDEX_op_shrv_vec: + case INDEX_op_sarv_vec: + case INDEX_op_bitsel_vec: + return 1; + default: + return 0; + } +} + +void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, + TCGArg a0, ...) +{ + g_assert_not_reached(); +} + static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) { switch (op) { @@ -1477,16 +2055,24 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_st32_i64: case INDEX_op_st_i32: case INDEX_op_st_i64: + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: return C_O0_I2(rZ, r); + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: + return C_N2_I1(r, r, r); + + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: + return C_O0_I3(r, r, r); + case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: return C_O0_I2(rZ, rZ); - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: - return C_O0_I2(LZ, L); - case INDEX_op_ext8s_i32: case INDEX_op_ext8s_i64: case INDEX_op_ext8u_i32: @@ -1501,6 +2087,8 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: case INDEX_op_ext_i32_i64: + case INDEX_op_neg_i32: + case INDEX_op_neg_i64: case INDEX_op_not_i32: case INDEX_op_not_i64: case INDEX_op_extract_i32: @@ -1522,12 +2110,12 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_ld32u_i64: case INDEX_op_ld_i32: case INDEX_op_ld_i64: + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: return C_O1_I1(r, r); - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: - return C_O1_I1(r, L); - case INDEX_op_andc_i32: case INDEX_op_andc_i64: case INDEX_op_orc_i32: @@ -1552,8 +2140,9 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) return C_O1_I2(r, r, ri); case INDEX_op_add_i32: + return C_O1_I2(r, r, ri); case INDEX_op_add_i64: - return C_O1_I2(r, r, rI); + return C_O1_I2(r, r, rJ); case INDEX_op_and_i32: case INDEX_op_and_i64: @@ -1572,18 +2161,17 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_ctz_i64: return C_O1_I2(r, r, rW); - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - return C_O1_I2(r, r, rZ); - case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: /* Must deposit into the same register as input */ return C_O1_I2(r, 0, rZ); case INDEX_op_sub_i32: + case INDEX_op_setcond_i32: + return C_O1_I2(r, rZ, ri); case INDEX_op_sub_i64: - return C_O1_I2(r, rZ, rN); + case INDEX_op_setcond_i64: + return C_O1_I2(r, rZ, rJ); case INDEX_op_mul_i32: case INDEX_op_mul_i64: @@ -1601,6 +2189,58 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_remu_i64: return C_O1_I2(r, rZ, rZ); + case INDEX_op_movcond_i32: + case INDEX_op_movcond_i64: + return C_O1_I4(r, rZ, rJ, rZ, rZ); + + case INDEX_op_ld_vec: + case INDEX_op_dupm_vec: + case INDEX_op_dup_vec: + return C_O1_I1(w, r); + + case INDEX_op_st_vec: + return C_O0_I2(w, r); + + case INDEX_op_cmp_vec: + return C_O1_I2(w, w, wM); + + case INDEX_op_add_vec: + case INDEX_op_sub_vec: + return C_O1_I2(w, w, wA); + + case INDEX_op_and_vec: + case INDEX_op_andc_vec: + case INDEX_op_or_vec: + case INDEX_op_orc_vec: + case INDEX_op_xor_vec: + case INDEX_op_nor_vec: + case INDEX_op_mul_vec: + case INDEX_op_smin_vec: + case INDEX_op_smax_vec: + case INDEX_op_umin_vec: + case INDEX_op_umax_vec: + case INDEX_op_ssadd_vec: + case INDEX_op_usadd_vec: + case INDEX_op_sssub_vec: + case INDEX_op_ussub_vec: + case INDEX_op_shlv_vec: + case INDEX_op_shrv_vec: + case INDEX_op_sarv_vec: + case INDEX_op_rotrv_vec: + case INDEX_op_rotlv_vec: + return C_O1_I2(w, w, w); + + case INDEX_op_not_vec: + case INDEX_op_neg_vec: + case INDEX_op_shli_vec: + case INDEX_op_shri_vec: + case INDEX_op_sari_vec: + case INDEX_op_rotli_vec: + return C_O1_I1(w, w); + + case INDEX_op_bitsel_vec: + return C_O1_I3(w, w, w, w); + default: g_assert_not_reached(); } @@ -1646,12 +2286,10 @@ static void tcg_target_qemu_prologue(TCGContext *s) TCG_REG_SP, SAVE_OFS + i * REG_SIZE); } -#if !defined(CONFIG_SOFTMMU) - if (USE_GUEST_BASE) { + if (!tcg_use_softmmu && guest_base) { tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, guest_base); tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); } -#endif /* Call generated code */ tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); @@ -1672,12 +2310,25 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_opc_jirl(s, TCG_REG_ZERO, TCG_REG_RA, 0); } +static void tcg_out_tb_start(TCGContext *s) +{ + /* nothing to do */ +} + static void tcg_target_init(TCGContext *s) { + unsigned long hwcap = qemu_getauxval(AT_HWCAP); + + /* Server and desktop class cpus have UAL; embedded cpus do not. */ + if (!(hwcap & HWCAP_LOONGARCH_UAL)) { + error_report("TCG: unaligned access support required; exiting"); + exit(EXIT_FAILURE); + } + tcg_target_available_regs[TCG_TYPE_I32] = ALL_GENERAL_REGS; tcg_target_available_regs[TCG_TYPE_I64] = ALL_GENERAL_REGS; - tcg_target_call_clobber_regs = ALL_GENERAL_REGS; + tcg_target_call_clobber_regs = ALL_GENERAL_REGS | ALL_VECTOR_REGS; tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S0); tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S1); tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S2); @@ -1689,6 +2340,18 @@ static void tcg_target_init(TCGContext *s) tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S8); tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S9); + if (cpuinfo & CPUINFO_LSX) { + tcg_target_available_regs[TCG_TYPE_V128] = ALL_VECTOR_REGS; + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V24); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V25); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V26); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V27); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V28); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V29); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V30); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_V31); + } + s->reserved_regs = 0; tcg_regset_set_reg(s->reserved_regs, TCG_REG_ZERO); tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP0); @@ -1697,6 +2360,7 @@ static void tcg_target_init(TCGContext *s) tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); tcg_regset_set_reg(s->reserved_regs, TCG_REG_TP); tcg_regset_set_reg(s->reserved_regs, TCG_REG_RESERVED); + tcg_regset_set_reg(s->reserved_regs, TCG_VEC_TMP0); } typedef struct { diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index d58a6162f2..fede627bf7 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -29,20 +29,12 @@ #ifndef LOONGARCH_TCG_TARGET_H #define LOONGARCH_TCG_TARGET_H -/* - * Loongson removed the (incomplete) 32-bit support from kernel and toolchain - * for the initial upstreaming of this architecture, so don't bother and just - * support the LP64* ABI for now. - */ -#if defined(__loongarch64) -# define TCG_TARGET_REG_BITS 64 -#else -# error unsupported LoongArch register size -#endif +#include "host/cpuinfo.h" #define TCG_TARGET_INSN_UNIT_SIZE 4 -#define TCG_TARGET_NB_REGS 32 -#define MAX_CODE_GEN_BUFFER_SIZE SIZE_MAX +#define TCG_TARGET_NB_REGS 64 + +#define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) typedef enum { TCG_REG_ZERO, @@ -78,21 +70,34 @@ typedef enum { TCG_REG_S7, TCG_REG_S8, + TCG_REG_V0 = 32, TCG_REG_V1, TCG_REG_V2, TCG_REG_V3, + TCG_REG_V4, TCG_REG_V5, TCG_REG_V6, TCG_REG_V7, + TCG_REG_V8, TCG_REG_V9, TCG_REG_V10, TCG_REG_V11, + TCG_REG_V12, TCG_REG_V13, TCG_REG_V14, TCG_REG_V15, + TCG_REG_V16, TCG_REG_V17, TCG_REG_V18, TCG_REG_V19, + TCG_REG_V20, TCG_REG_V21, TCG_REG_V22, TCG_REG_V23, + TCG_REG_V24, TCG_REG_V25, TCG_REG_V26, TCG_REG_V27, + TCG_REG_V28, TCG_REG_V29, TCG_REG_V30, TCG_REG_V31, + /* aliases */ TCG_AREG0 = TCG_REG_S0, TCG_REG_TMP0 = TCG_REG_T8, TCG_REG_TMP1 = TCG_REG_T7, TCG_REG_TMP2 = TCG_REG_T6, + TCG_VEC_TMP0 = TCG_REG_V23, } TCGReg; /* used for function call generation */ #define TCG_REG_CALL_STACK TCG_REG_SP #define TCG_TARGET_STACK_ALIGN 16 -#define TCG_TARGET_CALL_ALIGN_ARGS 1 #define TCG_TARGET_CALL_STACK_OFFSET 0 +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL /* optional instructions */ -#define TCG_TARGET_HAS_movcond_i32 0 +#define TCG_TARGET_HAS_negsetcond_i32 0 #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_div2_i32 0 @@ -114,7 +119,6 @@ typedef enum { #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_neg_i32 0 #define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_eqv_i32 0 @@ -123,13 +127,12 @@ typedef enum { #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 0 -#define TCG_TARGET_HAS_direct_jump 0 #define TCG_TARGET_HAS_brcond2 0 #define TCG_TARGET_HAS_setcond2 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 /* 64-bit operations */ -#define TCG_TARGET_HAS_movcond_i64 0 +#define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_div2_i64 0 @@ -138,8 +141,7 @@ typedef enum { #define TCG_TARGET_HAS_extract_i64 1 #define TCG_TARGET_HAS_sextract_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_extrl_i64_i32 1 -#define TCG_TARGET_HAS_extrh_i64_i32 1 +#define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_ext8s_i64 1 #define TCG_TARGET_HAS_ext16s_i64 1 #define TCG_TARGET_HAS_ext32s_i64 1 @@ -150,7 +152,6 @@ typedef enum { #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_neg_i64 0 #define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_eqv_i64 0 @@ -166,13 +167,36 @@ typedef enum { #define TCG_TARGET_HAS_muluh_i64 1 #define TCG_TARGET_HAS_mulsh_i64 1 -/* not defined -- call should be eliminated at compile time */ -void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +#define TCG_TARGET_HAS_qemu_ldst_i128 (cpuinfo & CPUINFO_LSX) + +#define TCG_TARGET_HAS_tst 0 + +#define TCG_TARGET_HAS_v64 0 +#define TCG_TARGET_HAS_v128 (cpuinfo & CPUINFO_LSX) +#define TCG_TARGET_HAS_v256 0 + +#define TCG_TARGET_HAS_not_vec 1 +#define TCG_TARGET_HAS_neg_vec 1 +#define TCG_TARGET_HAS_abs_vec 0 +#define TCG_TARGET_HAS_andc_vec 1 +#define TCG_TARGET_HAS_orc_vec 1 +#define TCG_TARGET_HAS_nand_vec 0 +#define TCG_TARGET_HAS_nor_vec 1 +#define TCG_TARGET_HAS_eqv_vec 0 +#define TCG_TARGET_HAS_mul_vec 1 +#define TCG_TARGET_HAS_shi_vec 1 +#define TCG_TARGET_HAS_shs_vec 0 +#define TCG_TARGET_HAS_shv_vec 1 +#define TCG_TARGET_HAS_roti_vec 1 +#define TCG_TARGET_HAS_rots_vec 0 +#define TCG_TARGET_HAS_rotv_vec 1 +#define TCG_TARGET_HAS_sat_vec 1 +#define TCG_TARGET_HAS_minmax_vec 1 +#define TCG_TARGET_HAS_bitsel_vec 1 +#define TCG_TARGET_HAS_cmpsel_vec 0 #define TCG_TARGET_DEFAULT_MO (0) #define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_HAS_MEMORY_BSWAP 0 - #endif /* LOONGARCH_TCG_TARGET_H */ diff --git a/tcg/loongarch64/tcg-target.opc.h b/tcg/loongarch64/tcg-target.opc.h new file mode 100644 index 0000000000..fd1a40b7fd --- /dev/null +++ b/tcg/loongarch64/tcg-target.opc.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2023 Jiajie Chen + * + * 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 for details. + * + * Target-specific opcodes for host vector expansion. These will be + * emitted by tcg_expand_vec_op. For those familiar with GCC internals, + * consider these to be UNSPEC with names. + */ diff --git a/tcg/meson.build b/tcg/meson.build index c4c63b19d4..8251589fd4 100644 --- a/tcg/meson.build +++ b/tcg/meson.build @@ -1,3 +1,7 @@ +if not get_option('tcg').allowed() + subdir_done() +endif + tcg_ss = ss.source_set() tcg_ss.add(files( @@ -6,15 +10,41 @@ tcg_ss.add(files( 'tcg.c', 'tcg-common.c', 'tcg-op.c', + 'tcg-op-ldst.c', 'tcg-op-gvec.c', 'tcg-op-vec.c', )) if get_option('tcg_interpreter') libffi = dependency('libffi', version: '>=3.0', required: true, - method: 'pkg-config', kwargs: static_kwargs) - specific_ss.add(libffi) - specific_ss.add(files('tci.c')) + method: 'pkg-config') + tcg_ss.add(libffi) + tcg_ss.add(files('tci.c')) endif -specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss) +tcg_ss.add(when: libdw, if_true: files('debuginfo.c')) +if host_os == 'linux' + tcg_ss.add(files('perf.c')) +endif + +tcg_ss = tcg_ss.apply({}) + +libtcg_user = static_library('tcg_user', + tcg_ss.sources() + genh, + name_suffix: 'fa', + c_args: '-DCONFIG_USER_ONLY', + build_by_default: false) + +tcg_user = declare_dependency(link_with: libtcg_user, + dependencies: tcg_ss.dependencies()) +user_ss.add(tcg_user) + +libtcg_system = static_library('tcg_system', + tcg_ss.sources() + genh, + name_suffix: 'fa', + c_args: '-DCONFIG_SOFTMMU', + build_by_default: false) + +tcg_system = declare_dependency(link_with: libtcg_system, + dependencies: tcg_ss.dependencies()) +system_ss.add(tcg_system) diff --git a/tcg/mips/tcg-target-con-set.h b/tcg/mips/tcg-target-con-set.h index fe3e868a2f..864034f468 100644 --- a/tcg/mips/tcg-target-con-set.h +++ b/tcg/mips/tcg-target-con-set.h @@ -12,15 +12,13 @@ C_O0_I1(r) C_O0_I2(rZ, r) C_O0_I2(rZ, rZ) -C_O0_I2(SZ, S) -C_O0_I3(SZ, S, S) -C_O0_I3(SZ, SZ, S) +C_O0_I3(rZ, r, r) +C_O0_I3(rZ, rZ, r) C_O0_I4(rZ, rZ, rZ, rZ) -C_O0_I4(SZ, SZ, S, S) -C_O1_I1(r, L) +C_O0_I4(rZ, rZ, r, r) C_O1_I1(r, r) C_O1_I2(r, 0, rZ) -C_O1_I2(r, L, L) +C_O1_I2(r, r, r) C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) C_O1_I2(r, r, rIK) @@ -30,7 +28,6 @@ C_O1_I2(r, rZ, rN) C_O1_I2(r, rZ, rZ) C_O1_I4(r, rZ, rZ, rZ, 0) C_O1_I4(r, rZ, rZ, rZ, rZ) -C_O2_I1(r, r, L) -C_O2_I2(r, r, L, L) +C_O2_I1(r, r, r) C_O2_I2(r, r, r, r) C_O2_I4(r, r, rZ, rZ, rN, rN) diff --git a/tcg/mips/tcg-target-con-str.h b/tcg/mips/tcg-target-con-str.h index e4b2965c72..413c280a7a 100644 --- a/tcg/mips/tcg-target-con-str.h +++ b/tcg/mips/tcg-target-con-str.h @@ -9,8 +9,6 @@ * REGS(letter, register_mask) */ REGS('r', ALL_GENERAL_REGS) -REGS('L', ALL_QLOAD_REGS) -REGS('S', ALL_QSTORE_REGS) /* * Define constraint letters for constants: diff --git a/tcg/mips/tcg-target-reg-bits.h b/tcg/mips/tcg-target-reg-bits.h new file mode 100644 index 0000000000..56fe0a725e --- /dev/null +++ b/tcg/mips/tcg-target-reg-bits.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2008-2009 Arnaud Patard + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +#if _MIPS_SIM == _ABIO32 +# define TCG_TARGET_REG_BITS 32 +#elif _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 +# define TCG_TARGET_REG_BITS 64 +#else +# error "Unknown ABI" +#endif + +#endif diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index bd76f0c97f..3b5b5c6d5b 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -25,22 +25,15 @@ */ #include "../tcg-ldst.c.inc" - -#if HOST_BIG_ENDIAN -# define MIPS_BE 1 -#else -# define MIPS_BE 0 -#endif +#include "../tcg-pool.c.inc" #if TCG_TARGET_REG_BITS == 32 -# define LO_OFF (MIPS_BE * 4) +# define LO_OFF (HOST_BIG_ENDIAN * 4) # define HI_OFF (4 - LO_OFF) #else -/* To assert at compile-time that these values are never used - for TCG_TARGET_REG_BITS == 64. */ -int link_error(void); -# define LO_OFF link_error() -# define HI_OFF link_error() +/* Assert at compile-time that these values are never used for 64-bit. */ +# define LO_OFF ({ qemu_build_not_reached(); 0; }) +# define HI_OFF ({ qemu_build_not_reached(); 0; }) #endif #ifdef CONFIG_DEBUG_TCG @@ -85,8 +78,11 @@ static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { #define TCG_TMP2 TCG_REG_T8 #define TCG_TMP3 TCG_REG_T7 -#ifndef CONFIG_SOFTMMU -#define TCG_GUEST_BASE_REG TCG_REG_S1 +#define TCG_GUEST_BASE_REG TCG_REG_S7 +#if TCG_TARGET_REG_BITS == 64 +#define TCG_REG_TB TCG_REG_S6 +#else +#define TCG_REG_TB ({ qemu_build_not_reached(); TCG_REG_ZERO; }) #endif /* check if we really need so many registers :P */ @@ -136,10 +132,12 @@ static const TCGReg tcg_target_call_iarg_regs[] = { #endif }; -static const TCGReg tcg_target_call_oarg_regs[2] = { - TCG_REG_V0, - TCG_REG_V1 -}; +static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) +{ + tcg_debug_assert(kind == TCG_CALL_RET_NORMAL); + tcg_debug_assert(slot >= 0 && slot <= 1); + return TCG_REG_V0 + slot; +} static const tcg_insn_unit *tb_ret_addr; static const tcg_insn_unit *bswap32_addr; @@ -161,9 +159,18 @@ static bool reloc_pc16(tcg_insn_unit *src_rw, const tcg_insn_unit *target) static bool patch_reloc(tcg_insn_unit *code_ptr, int type, intptr_t value, intptr_t addend) { - tcg_debug_assert(type == R_MIPS_PC16); - tcg_debug_assert(addend == 0); - return reloc_pc16(code_ptr, (const tcg_insn_unit *)value); + value += addend; + switch (type) { + case R_MIPS_PC16: + return reloc_pc16(code_ptr, (const tcg_insn_unit *)value); + case R_MIPS_16: + if (value != (int16_t)value) { + return false; + } + *code_ptr = deposit32(*code_ptr, 0, 16, value); + return true; + } + g_assert_not_reached(); } #define TCG_CT_CONST_ZERO 0x100 @@ -174,20 +181,6 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, #define TCG_CT_CONST_WSZ 0x2000 /* word size */ #define ALL_GENERAL_REGS 0xffffffffu -#define NOA0_REGS (ALL_GENERAL_REGS & ~(1 << TCG_REG_A0)) - -#ifdef CONFIG_SOFTMMU -#define ALL_QLOAD_REGS \ - (NOA0_REGS & ~((TCG_TARGET_REG_BITS < TARGET_LONG_BITS) << TCG_REG_A2)) -#define ALL_QSTORE_REGS \ - (NOA0_REGS & ~(TCG_TARGET_REG_BITS < TARGET_LONG_BITS \ - ? (1 << TCG_REG_A2) | (1 << TCG_REG_A3) \ - : (1 << TCG_REG_A1))) -#else -#define ALL_QLOAD_REGS NOA0_REGS -#define ALL_QSTORE_REGS NOA0_REGS -#endif - static bool is_p2m1(tcg_target_long val) { @@ -195,7 +188,8 @@ static bool is_p2m1(tcg_target_long val) } /* test if a constant matches the constraint */ -static bool tcg_target_const_match(int64_t val, TCGType type, int ct) +static bool tcg_target_const_match(int64_t val, int ct, + TCGType type, TCGCond cond, int vece) { if (ct & TCG_CT_CONST) { return 1; @@ -366,8 +360,6 @@ typedef enum { /* Aliases for convenience. */ ALIAS_PADD = sizeof(void *) == 4 ? OPC_ADDU : OPC_DADDU, ALIAS_PADDI = sizeof(void *) == 4 ? OPC_ADDIU : OPC_DADDIU, - ALIAS_TSRL = TARGET_LONG_BITS == 32 || TCG_TARGET_REG_BITS == 32 - ? OPC_SRL : OPC_DSRL, } MIPSInsn; /* @@ -495,6 +487,11 @@ static void tcg_out_nop(TCGContext *s) tcg_out32(s, 0); } +static void tcg_out_nop_fill(tcg_insn_unit *p, int count) +{ + memset(p, 0, count * sizeof(tcg_insn_unit)); +} + static void tcg_out_dsll(TCGContext *s, TCGReg rd, TCGReg rt, TCGArg sa) { tcg_out_opc_sa64(s, OPC_DSLL, OPC_DSLL32, rd, rt, sa); @@ -519,35 +516,182 @@ static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) return true; } -static void tcg_out_movi(TCGContext *s, TCGType type, - TCGReg ret, tcg_target_long arg) +static bool tcg_out_movi_one(TCGContext *s, TCGReg ret, tcg_target_long arg) { - if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { - arg = (int32_t)arg; - } if (arg == (int16_t)arg) { tcg_out_opc_imm(s, OPC_ADDIU, ret, TCG_REG_ZERO, arg); - return; + return true; } if (arg == (uint16_t)arg) { tcg_out_opc_imm(s, OPC_ORI, ret, TCG_REG_ZERO, arg); + return true; + } + if (arg == (int32_t)arg && (arg & 0xffff) == 0) { + tcg_out_opc_imm(s, OPC_LUI, ret, TCG_REG_ZERO, arg >> 16); + return true; + } + return false; +} + +static bool tcg_out_movi_two(TCGContext *s, TCGReg ret, tcg_target_long arg) +{ + /* + * All signed 32-bit constants are loadable with two immediates, + * and everything else requires more work. + */ + if (arg == (int32_t)arg) { + if (!tcg_out_movi_one(s, ret, arg)) { + tcg_out_opc_imm(s, OPC_LUI, ret, TCG_REG_ZERO, arg >> 16); + tcg_out_opc_imm(s, OPC_ORI, ret, ret, arg & 0xffff); + } + return true; + } + return false; +} + +static void tcg_out_movi_pool(TCGContext *s, TCGReg ret, + tcg_target_long arg, TCGReg tbreg) +{ + new_pool_label(s, arg, R_MIPS_16, s->code_ptr, tcg_tbrel_diff(s, NULL)); + tcg_out_opc_imm(s, OPC_LD, ret, tbreg, 0); +} + +static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, + tcg_target_long arg, TCGReg tbreg) +{ + tcg_target_long tmp; + int sh, lo; + + if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { + arg = (int32_t)arg; + } + + /* Load all 32-bit constants. */ + if (tcg_out_movi_two(s, ret, arg)) { return; } - if (TCG_TARGET_REG_BITS == 32 || arg == (int32_t)arg) { - tcg_out_opc_imm(s, OPC_LUI, ret, TCG_REG_ZERO, arg >> 16); - } else { - tcg_out_movi(s, TCG_TYPE_I32, ret, arg >> 31 >> 1); - if (arg & 0xffff0000ull) { - tcg_out_dsll(s, ret, ret, 16); - tcg_out_opc_imm(s, OPC_ORI, ret, ret, arg >> 16); - tcg_out_dsll(s, ret, ret, 16); - } else { - tcg_out_dsll(s, ret, ret, 32); + assert(TCG_TARGET_REG_BITS == 64); + + /* Load addresses within 2GB of TB with 1 or 3 insns. */ + tmp = tcg_tbrel_diff(s, (void *)arg); + if (tmp == (int16_t)tmp) { + tcg_out_opc_imm(s, OPC_DADDIU, ret, tbreg, tmp); + return; + } + if (tcg_out_movi_two(s, ret, tmp)) { + tcg_out_opc_reg(s, OPC_DADDU, ret, ret, tbreg); + return; + } + + /* + * Load bitmasks with a right-shift. This is good for things + * like 0x0fff_ffff_ffff_fff0: ADDUI r,0,0xff00 + DSRL r,r,4. + * or similarly using LUI. For this to work, bit 31 must be set. + */ + if (arg > 0 && (int32_t)arg < 0) { + sh = clz64(arg); + if (tcg_out_movi_one(s, ret, arg << sh)) { + tcg_out_dsrl(s, ret, ret, sh); + return; } } - if (arg & 0xffff) { - tcg_out_opc_imm(s, OPC_ORI, ret, ret, arg & 0xffff); + + /* + * Load slightly larger constants using left-shift. + * Limit this sequence to 3 insns to avoid too much expansion. + */ + sh = ctz64(arg); + if (sh && tcg_out_movi_two(s, ret, arg >> sh)) { + tcg_out_dsll(s, ret, ret, sh); + return; } + + /* + * Load slightly larger constants using left-shift and add/or. + * Prefer addi with a negative immediate when that would produce + * a larger shift. For this to work, bits 15 and 16 must be set. + */ + lo = arg & 0xffff; + if (lo) { + if ((arg & 0x18000) == 0x18000) { + lo = (int16_t)arg; + } + tmp = arg - lo; + sh = ctz64(tmp); + tmp >>= sh; + if (tcg_out_movi_one(s, ret, tmp)) { + tcg_out_dsll(s, ret, ret, sh); + tcg_out_opc_imm(s, lo < 0 ? OPC_DADDIU : OPC_ORI, ret, ret, lo); + return; + } + } + + /* Otherwise, put 64-bit constants into the constant pool. */ + tcg_out_movi_pool(s, ret, arg, tbreg); +} + +static void tcg_out_movi(TCGContext *s, TCGType type, + TCGReg ret, tcg_target_long arg) +{ + TCGReg tbreg = TCG_TARGET_REG_BITS == 64 ? TCG_REG_TB : 0; + tcg_out_movi_int(s, type, ret, arg, tbreg); +} + +static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) +{ + tcg_debug_assert(TCG_TARGET_HAS_ext8s_i32); + tcg_out_opc_reg(s, OPC_SEB, rd, TCG_REG_ZERO, rs); +} + +static void tcg_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_opc_imm(s, OPC_ANDI, rd, rs, 0xff); +} + +static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) +{ + tcg_debug_assert(TCG_TARGET_HAS_ext16s_i32); + tcg_out_opc_reg(s, OPC_SEH, rd, TCG_REG_ZERO, rs); +} + +static void tcg_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_opc_imm(s, OPC_ANDI, rd, rs, 0xffff); +} + +static void tcg_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + tcg_out_opc_sa(s, OPC_SLL, rd, rs, 0); +} + +static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) +{ + if (rd != rs) { + tcg_out_ext32s(s, rd, rs); + } +} + +static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_ext32u(s, rd, rs); +} + +static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_ext32s(s, rd, rs); +} + +static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2) +{ + return false; +} + +static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs, + tcg_target_long imm) +{ + /* This function is only used for passing structs by reference. */ + g_assert_not_reached(); } static void tcg_out_bswap16(TCGContext *s, TCGReg ret, TCGReg arg, int flags) @@ -626,6 +770,7 @@ static void tcg_out_bswap64(TCGContext *s, TCGReg ret, TCGReg arg) static void tcg_out_ext32u(TCGContext *s, TCGReg ret, TCGReg arg) { + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); if (use_mips32r2_instructions) { tcg_out_opc_bf(s, OPC_DEXT, ret, arg, 31, 0); } else { @@ -727,71 +872,83 @@ static void tcg_out_addsub2(TCGContext *s, TCGReg rl, TCGReg rh, TCGReg al, } } -/* Bit 0 set if inversion required; bit 1 set if swapping required. */ -#define MIPS_CMP_INV 1 -#define MIPS_CMP_SWAP 2 +#define SETCOND_INV TCG_TARGET_NB_REGS +#define SETCOND_NEZ (SETCOND_INV << 1) +#define SETCOND_FLAGS (SETCOND_INV | SETCOND_NEZ) -static const uint8_t mips_cmp_map[16] = { - [TCG_COND_LT] = 0, - [TCG_COND_LTU] = 0, - [TCG_COND_GE] = MIPS_CMP_INV, - [TCG_COND_GEU] = MIPS_CMP_INV, - [TCG_COND_LE] = MIPS_CMP_INV | MIPS_CMP_SWAP, - [TCG_COND_LEU] = MIPS_CMP_INV | MIPS_CMP_SWAP, - [TCG_COND_GT] = MIPS_CMP_SWAP, - [TCG_COND_GTU] = MIPS_CMP_SWAP, -}; +static int tcg_out_setcond_int(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg arg1, TCGReg arg2) +{ + int flags = 0; + + switch (cond) { + case TCG_COND_EQ: /* -> NE */ + case TCG_COND_GE: /* -> LT */ + case TCG_COND_GEU: /* -> LTU */ + case TCG_COND_LE: /* -> GT */ + case TCG_COND_LEU: /* -> GTU */ + cond = tcg_invert_cond(cond); + flags ^= SETCOND_INV; + break; + default: + break; + } + + switch (cond) { + case TCG_COND_NE: + flags |= SETCOND_NEZ; + if (arg2 == 0) { + return arg1 | flags; + } + tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); + break; + case TCG_COND_LT: + tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2); + break; + case TCG_COND_LTU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2); + break; + case TCG_COND_GT: + tcg_out_opc_reg(s, OPC_SLT, ret, arg2, arg1); + break; + case TCG_COND_GTU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg2, arg1); + break; + default: + g_assert_not_reached(); + } + return ret | flags; +} + +static void tcg_out_setcond_end(TCGContext *s, TCGReg ret, int tmpflags) +{ + if (tmpflags != ret) { + TCGReg tmp = tmpflags & ~SETCOND_FLAGS; + + switch (tmpflags & SETCOND_FLAGS) { + case SETCOND_INV: + /* Intermediate result is boolean: simply invert. */ + tcg_out_opc_imm(s, OPC_XORI, ret, tmp, 1); + break; + case SETCOND_NEZ: + /* Intermediate result is zero/non-zero: test != 0. */ + tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, tmp); + break; + case SETCOND_NEZ | SETCOND_INV: + /* Intermediate result is zero/non-zero: test == 0. */ + tcg_out_opc_imm(s, OPC_SLTIU, ret, tmp, 1); + break; + default: + g_assert_not_reached(); + } + } +} static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg arg1, TCGReg arg2) { - MIPSInsn s_opc = OPC_SLTU; - int cmp_map; - - switch (cond) { - case TCG_COND_EQ: - if (arg2 != 0) { - tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); - arg1 = ret; - } - tcg_out_opc_imm(s, OPC_SLTIU, ret, arg1, 1); - break; - - case TCG_COND_NE: - if (arg2 != 0) { - tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); - arg1 = ret; - } - tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, arg1); - break; - - case TCG_COND_LT: - case TCG_COND_GE: - case TCG_COND_LE: - case TCG_COND_GT: - s_opc = OPC_SLT; - /* FALLTHRU */ - - case TCG_COND_LTU: - case TCG_COND_GEU: - case TCG_COND_LEU: - case TCG_COND_GTU: - cmp_map = mips_cmp_map[cond]; - if (cmp_map & MIPS_CMP_SWAP) { - TCGReg t = arg1; - arg1 = arg2; - arg2 = t; - } - tcg_out_opc_reg(s, s_opc, ret, arg1, arg2); - if (cmp_map & MIPS_CMP_INV) { - tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); - } - break; - - default: - tcg_abort(); - break; - } + int tmpflags = tcg_out_setcond_int(s, cond, ret, arg1, arg2); + tcg_out_setcond_end(s, ret, tmpflags); } static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, @@ -804,9 +961,7 @@ static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, [TCG_COND_GE] = OPC_BGEZ, }; - MIPSInsn s_opc = OPC_SLTU; - MIPSInsn b_opc; - int cmp_map; + MIPSInsn b_opc = 0; switch (cond) { case TCG_COND_EQ: @@ -815,7 +970,6 @@ static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, case TCG_COND_NE: b_opc = OPC_BNE; break; - case TCG_COND_LT: case TCG_COND_GT: case TCG_COND_LE: @@ -824,133 +978,76 @@ static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, b_opc = b_zero[cond]; arg2 = arg1; arg1 = 0; - break; } - s_opc = OPC_SLT; - /* FALLTHRU */ - - case TCG_COND_LTU: - case TCG_COND_GTU: - case TCG_COND_LEU: - case TCG_COND_GEU: - cmp_map = mips_cmp_map[cond]; - if (cmp_map & MIPS_CMP_SWAP) { - TCGReg t = arg1; - arg1 = arg2; - arg2 = t; - } - tcg_out_opc_reg(s, s_opc, TCG_TMP0, arg1, arg2); - b_opc = (cmp_map & MIPS_CMP_INV ? OPC_BEQ : OPC_BNE); - arg1 = TCG_TMP0; - arg2 = TCG_REG_ZERO; break; - default: - tcg_abort(); break; } + if (b_opc == 0) { + int tmpflags = tcg_out_setcond_int(s, cond, TCG_TMP0, arg1, arg2); + + arg2 = TCG_REG_ZERO; + arg1 = tmpflags & ~SETCOND_FLAGS; + b_opc = tmpflags & SETCOND_INV ? OPC_BEQ : OPC_BNE; + } + + tcg_out_reloc(s, s->code_ptr, R_MIPS_PC16, l, 0); tcg_out_opc_br(s, b_opc, arg1, arg2); - tcg_out_reloc(s, s->code_ptr - 1, R_MIPS_PC16, l, 0); tcg_out_nop(s); } -static TCGReg tcg_out_reduce_eq2(TCGContext *s, TCGReg tmp0, TCGReg tmp1, - TCGReg al, TCGReg ah, - TCGReg bl, TCGReg bh) +static int tcg_out_setcond2_int(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) { - /* Merge highpart comparison into AH. */ - if (bh != 0) { - if (ah != 0) { - tcg_out_opc_reg(s, OPC_XOR, tmp0, ah, bh); - ah = tmp0; - } else { - ah = bh; - } + int flags = 0; + + switch (cond) { + case TCG_COND_EQ: + flags |= SETCOND_INV; + /* fall through */ + case TCG_COND_NE: + flags |= SETCOND_NEZ; + tcg_out_opc_reg(s, OPC_XOR, TCG_TMP0, al, bl); + tcg_out_opc_reg(s, OPC_XOR, TCG_TMP1, ah, bh); + tcg_out_opc_reg(s, OPC_OR, ret, TCG_TMP0, TCG_TMP1); + break; + + default: + tcg_out_setcond(s, TCG_COND_EQ, TCG_TMP0, ah, bh); + tcg_out_setcond(s, tcg_unsigned_cond(cond), TCG_TMP1, al, bl); + tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, TCG_TMP0); + tcg_out_setcond(s, tcg_high_cond(cond), TCG_TMP0, ah, bh); + tcg_out_opc_reg(s, OPC_OR, ret, TCG_TMP0, TCG_TMP1); + break; } - /* Merge lowpart comparison into AL. */ - if (bl != 0) { - if (al != 0) { - tcg_out_opc_reg(s, OPC_XOR, tmp1, al, bl); - al = tmp1; - } else { - al = bl; - } - } - /* Merge high and low part comparisons into AL. */ - if (ah != 0) { - if (al != 0) { - tcg_out_opc_reg(s, OPC_OR, tmp0, ah, al); - al = tmp0; - } else { - al = ah; - } - } - return al; + return ret | flags; } static void tcg_out_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) { - TCGReg tmp0 = TCG_TMP0; - TCGReg tmp1 = ret; - - tcg_debug_assert(ret != TCG_TMP0); - if (ret == ah || ret == bh) { - tcg_debug_assert(ret != TCG_TMP1); - tmp1 = TCG_TMP1; - } - - switch (cond) { - case TCG_COND_EQ: - case TCG_COND_NE: - tmp1 = tcg_out_reduce_eq2(s, tmp0, tmp1, al, ah, bl, bh); - tcg_out_setcond(s, cond, ret, tmp1, TCG_REG_ZERO); - break; - - default: - tcg_out_setcond(s, TCG_COND_EQ, tmp0, ah, bh); - tcg_out_setcond(s, tcg_unsigned_cond(cond), tmp1, al, bl); - tcg_out_opc_reg(s, OPC_AND, tmp1, tmp1, tmp0); - tcg_out_setcond(s, tcg_high_cond(cond), tmp0, ah, bh); - tcg_out_opc_reg(s, OPC_OR, ret, tmp1, tmp0); - break; - } + int tmpflags = tcg_out_setcond2_int(s, cond, ret, al, ah, bl, bh); + tcg_out_setcond_end(s, ret, tmpflags); } static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh, TCGLabel *l) { - TCGCond b_cond = TCG_COND_NE; - TCGReg tmp = TCG_TMP1; + int tmpflags = tcg_out_setcond2_int(s, cond, TCG_TMP0, al, ah, bl, bh); + TCGReg tmp = tmpflags & ~SETCOND_FLAGS; + MIPSInsn b_opc = tmpflags & SETCOND_INV ? OPC_BEQ : OPC_BNE; - /* With branches, we emit between 4 and 9 insns with 2 or 3 branches. - With setcond, we emit between 3 and 10 insns and only 1 branch, - which ought to get better branch prediction. */ - switch (cond) { - case TCG_COND_EQ: - case TCG_COND_NE: - b_cond = cond; - tmp = tcg_out_reduce_eq2(s, TCG_TMP0, TCG_TMP1, al, ah, bl, bh); - break; - - default: - /* Minimize code size by preferring a compare not requiring INV. */ - if (mips_cmp_map[cond] & MIPS_CMP_INV) { - cond = tcg_invert_cond(cond); - b_cond = TCG_COND_EQ; - } - tcg_out_setcond2(s, cond, tmp, al, ah, bl, bh); - break; - } - - tcg_out_brcond(s, b_cond, tmp, TCG_REG_ZERO, l); + tcg_out_reloc(s, s->code_ptr, R_MIPS_PC16, l, 0); + tcg_out_opc_br(s, b_opc, tmp, TCG_REG_ZERO); + tcg_out_nop(s); } static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg c1, TCGReg c2, TCGReg v1, TCGReg v2) { - bool eqz = false; + int tmpflags; + bool eqz; /* If one of the values is zero, put it last to match SEL*Z instructions */ if (use_mips32r6_instructions && v1 == 0) { @@ -959,27 +1056,9 @@ static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, cond = tcg_invert_cond(cond); } - switch (cond) { - case TCG_COND_EQ: - eqz = true; - /* FALLTHRU */ - case TCG_COND_NE: - if (c2 != 0) { - tcg_out_opc_reg(s, OPC_XOR, TCG_TMP0, c1, c2); - c1 = TCG_TMP0; - } - break; - - default: - /* Minimize code size by preferring a compare not requiring INV. */ - if (mips_cmp_map[cond] & MIPS_CMP_INV) { - cond = tcg_invert_cond(cond); - eqz = true; - } - tcg_out_setcond(s, cond, TCG_TMP0, c1, c2); - c1 = TCG_TMP0; - break; - } + tmpflags = tcg_out_setcond_int(s, cond, TCG_TMP0, c1, c2); + c1 = tmpflags & ~SETCOND_FLAGS; + eqz = tmpflags & SETCOND_INV; if (use_mips32r6_instructions) { MIPSInsn m_opc_t = eqz ? OPC_SELEQZ : OPC_SELNEZ; @@ -992,21 +1071,40 @@ static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, if (v2 != 0) { tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP1); } - } else { + return; + } + + /* This should be guaranteed via constraints */ + tcg_debug_assert(v2 == ret); + + if (use_movnz_instructions) { MIPSInsn m_opc = eqz ? OPC_MOVZ : OPC_MOVN; - tcg_out_opc_reg(s, m_opc, ret, v1, c1); - - /* This should be guaranteed via constraints */ - tcg_debug_assert(v2 == ret); + } else { + /* Invert the condition in order to branch over the move. */ + MIPSInsn b_opc = eqz ? OPC_BNE : OPC_BEQ; + tcg_out_opc_imm(s, b_opc, c1, TCG_REG_ZERO, 2); + tcg_out_nop(s); + /* Open-code tcg_out_mov, without the nop-move check. */ + tcg_out_opc_reg(s, OPC_OR, ret, v1, TCG_REG_ZERO); } } static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *arg, bool tail) { - /* Note that the ABI requires the called function's address to be - loaded into T9, even if a direct branch is in range. */ - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T9, (uintptr_t)arg); + /* + * Note that __mips_abicalls requires the called function's address + * to be loaded into $25 (t9), even if a direct branch is in range. + * + * For n64, always drop the pointer into the constant pool. + * We can re-use helper addresses often and do not want any + * of the longer sequences tcg_out_movi may try. + */ + if (sizeof(uintptr_t) == 8) { + tcg_out_movi_pool(s, TCG_REG_T9, (uintptr_t)arg, TCG_REG_TB); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T9, (uintptr_t)arg); + } /* But do try a direct branch, allowing the cpu better insn prefetch. */ if (tail) { @@ -1020,258 +1118,36 @@ static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *arg, bool tail) } } -static void tcg_out_call(TCGContext *s, const tcg_insn_unit *arg) +static void tcg_out_call(TCGContext *s, const tcg_insn_unit *arg, + const TCGHelperInfo *info) { tcg_out_call_int(s, arg, false); tcg_out_nop(s); } -#if defined(CONFIG_SOFTMMU) -static void * const qemu_ld_helpers[(MO_SSIZE | MO_BSWAP) + 1] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_SB] = helper_ret_ldsb_mmu, - [MO_LEUW] = helper_le_lduw_mmu, - [MO_LESW] = helper_le_ldsw_mmu, - [MO_LEUL] = helper_le_ldul_mmu, - [MO_LEUQ] = helper_le_ldq_mmu, - [MO_BEUW] = helper_be_lduw_mmu, - [MO_BESW] = helper_be_ldsw_mmu, - [MO_BEUL] = helper_be_ldul_mmu, - [MO_BEUQ] = helper_be_ldq_mmu, -#if TCG_TARGET_REG_BITS == 64 - [MO_LESL] = helper_le_ldsl_mmu, - [MO_BESL] = helper_be_ldsl_mmu, -#endif +/* We have four temps, we might as well expose three of them. */ +static const TCGLdstHelperParam ldst_helper_param = { + .ntmp = 3, .tmp = { TCG_TMP0, TCG_TMP1, TCG_TMP2 } }; -static void * const qemu_st_helpers[(MO_SIZE | MO_BSWAP) + 1] = { - [MO_UB] = helper_ret_stb_mmu, - [MO_LEUW] = helper_le_stw_mmu, - [MO_LEUL] = helper_le_stl_mmu, - [MO_LEUQ] = helper_le_stq_mmu, - [MO_BEUW] = helper_be_stw_mmu, - [MO_BEUL] = helper_be_stl_mmu, - [MO_BEUQ] = helper_be_stq_mmu, -}; - -/* Helper routines for marshalling helper function arguments into - * the correct registers and stack. - * I is where we want to put this argument, and is updated and returned - * for the next call. ARG is the argument itself. - * - * We provide routines for arguments which are: immediate, 32 bit - * value in register, 16 and 8 bit values in register (which must be zero - * extended before use) and 64 bit value in a lo:hi register pair. - */ - -static int tcg_out_call_iarg_reg(TCGContext *s, int i, TCGReg arg) -{ - if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) { - tcg_out_mov(s, TCG_TYPE_REG, tcg_target_call_iarg_regs[i], arg); - } else { - /* For N32 and N64, the initial offset is different. But there - we also have 8 argument register so we don't run out here. */ - tcg_debug_assert(TCG_TARGET_REG_BITS == 32); - tcg_out_st(s, TCG_TYPE_REG, arg, TCG_REG_SP, 4 * i); - } - return i + 1; -} - -static int tcg_out_call_iarg_reg8(TCGContext *s, int i, TCGReg arg) -{ - TCGReg tmp = TCG_TMP0; - if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) { - tmp = tcg_target_call_iarg_regs[i]; - } - tcg_out_opc_imm(s, OPC_ANDI, tmp, arg, 0xff); - return tcg_out_call_iarg_reg(s, i, tmp); -} - -static int tcg_out_call_iarg_reg16(TCGContext *s, int i, TCGReg arg) -{ - TCGReg tmp = TCG_TMP0; - if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) { - tmp = tcg_target_call_iarg_regs[i]; - } - tcg_out_opc_imm(s, OPC_ANDI, tmp, arg, 0xffff); - return tcg_out_call_iarg_reg(s, i, tmp); -} - -static int tcg_out_call_iarg_imm(TCGContext *s, int i, TCGArg arg) -{ - TCGReg tmp = TCG_TMP0; - if (arg == 0) { - tmp = TCG_REG_ZERO; - } else { - if (i < ARRAY_SIZE(tcg_target_call_iarg_regs)) { - tmp = tcg_target_call_iarg_regs[i]; - } - tcg_out_movi(s, TCG_TYPE_REG, tmp, arg); - } - return tcg_out_call_iarg_reg(s, i, tmp); -} - -static int tcg_out_call_iarg_reg2(TCGContext *s, int i, TCGReg al, TCGReg ah) -{ - tcg_debug_assert(TCG_TARGET_REG_BITS == 32); - i = (i + 1) & ~1; - i = tcg_out_call_iarg_reg(s, i, (MIPS_BE ? ah : al)); - i = tcg_out_call_iarg_reg(s, i, (MIPS_BE ? al : ah)); - return i; -} - -/* We expect to use a 16-bit negative offset from ENV. */ -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -32768); - -/* - * Perform the tlb comparison operation. - * The complete host address is placed in BASE. - * Clobbers TMP0, TMP1, TMP2, TMP3. - */ -static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl, - TCGReg addrh, MemOpIdx oi, - tcg_insn_unit *label_ptr[2], bool is_load) -{ - MemOp opc = get_memop(oi); - unsigned a_bits = get_alignment_bits(opc); - unsigned s_bits = opc & MO_SIZE; - unsigned a_mask = (1 << a_bits) - 1; - unsigned s_mask = (1 << s_bits) - 1; - int mem_index = get_mmuidx(oi); - int fast_off = TLB_MASK_TABLE_OFS(mem_index); - int mask_off = fast_off + offsetof(CPUTLBDescFast, mask); - int table_off = fast_off + offsetof(CPUTLBDescFast, table); - int add_off = offsetof(CPUTLBEntry, addend); - int cmp_off = (is_load ? offsetof(CPUTLBEntry, addr_read) - : offsetof(CPUTLBEntry, addr_write)); - target_ulong tlb_mask; - - /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP0, TCG_AREG0, mask_off); - tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP1, TCG_AREG0, table_off); - - /* Extract the TLB index from the address into TMP3. */ - tcg_out_opc_sa(s, ALIAS_TSRL, TCG_TMP3, addrl, - TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - tcg_out_opc_reg(s, OPC_AND, TCG_TMP3, TCG_TMP3, TCG_TMP0); - - /* Add the tlb_table pointer, creating the CPUTLBEntry address in TMP3. */ - tcg_out_opc_reg(s, ALIAS_PADD, TCG_TMP3, TCG_TMP3, TCG_TMP1); - - /* Load the (low-half) tlb comparator. */ - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - tcg_out_ldst(s, OPC_LW, TCG_TMP0, TCG_TMP3, cmp_off + LO_OFF); - } else { - tcg_out_ldst(s, (TARGET_LONG_BITS == 64 ? OPC_LD - : TCG_TARGET_REG_BITS == 64 ? OPC_LWU : OPC_LW), - TCG_TMP0, TCG_TMP3, cmp_off); - } - - /* Zero extend a 32-bit guest address for a 64-bit host. */ - if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { - tcg_out_ext32u(s, base, addrl); - addrl = base; - } - - /* - * Mask the page bits, keeping the alignment bits to compare against. - * For unaligned accesses, compare against the end of the access to - * verify that it does not cross a page boundary. - */ - tlb_mask = (target_ulong)TARGET_PAGE_MASK | a_mask; - tcg_out_movi(s, TCG_TYPE_I32, TCG_TMP1, tlb_mask); - if (a_mask >= s_mask) { - tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, addrl); - } else { - tcg_out_opc_imm(s, ALIAS_PADDI, TCG_TMP2, addrl, s_mask - a_mask); - tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, TCG_TMP2); - } - - if (TCG_TARGET_REG_BITS >= TARGET_LONG_BITS) { - /* Load the tlb addend for the fast path. */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP2, TCG_TMP3, add_off); - } - - label_ptr[0] = s->code_ptr; - tcg_out_opc_br(s, OPC_BNE, TCG_TMP1, TCG_TMP0); - - /* Load and test the high half tlb comparator. */ - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - /* delay slot */ - tcg_out_ldst(s, OPC_LW, TCG_TMP0, TCG_TMP3, cmp_off + HI_OFF); - - /* Load the tlb addend for the fast path. */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP2, TCG_TMP3, add_off); - - label_ptr[1] = s->code_ptr; - tcg_out_opc_br(s, OPC_BNE, addrh, TCG_TMP0); - } - - /* delay slot */ - tcg_out_opc_reg(s, ALIAS_PADD, base, TCG_TMP2, addrl); -} - -static void add_qemu_ldst_label(TCGContext *s, int is_ld, MemOpIdx oi, - TCGType ext, - TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - void *raddr, tcg_insn_unit *label_ptr[2]) -{ - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->oi = oi; - label->type = ext; - label->datalo_reg = datalo; - label->datahi_reg = datahi; - label->addrlo_reg = addrlo; - label->addrhi_reg = addrhi; - label->raddr = tcg_splitwx_to_rx(raddr); - label->label_ptr[0] = label_ptr[0]; - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - label->label_ptr[1] = label_ptr[1]; - } -} - static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { const tcg_insn_unit *tgt_rx = tcg_splitwx_to_rx(s->code_ptr); - MemOpIdx oi = l->oi; - MemOp opc = get_memop(oi); - TCGReg v0; - int i; + MemOp opc = get_memop(l->oi); /* resolve label address */ if (!reloc_pc16(l->label_ptr[0], tgt_rx) - || (TCG_TARGET_REG_BITS < TARGET_LONG_BITS - && !reloc_pc16(l->label_ptr[1], tgt_rx))) { + || (l->label_ptr[1] && !reloc_pc16(l->label_ptr[1], tgt_rx))) { return false; } - i = 1; - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - i = tcg_out_call_iarg_reg2(s, i, l->addrlo_reg, l->addrhi_reg); - } else { - i = tcg_out_call_iarg_reg(s, i, l->addrlo_reg); - } - i = tcg_out_call_iarg_imm(s, i, oi); - i = tcg_out_call_iarg_imm(s, i, (intptr_t)l->raddr); - tcg_out_call_int(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SSIZE)], false); - /* delay slot */ - tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); + tcg_out_ld_helper_args(s, l, &ldst_helper_param); - v0 = l->datalo_reg; - if (TCG_TARGET_REG_BITS == 32 && (opc & MO_SIZE) == MO_64) { - /* We eliminated V0 from the possible output registers, so it - cannot be clobbered here. So we must move V1 first. */ - if (MIPS_BE) { - tcg_out_mov(s, TCG_TYPE_I32, v0, TCG_REG_V1); - v0 = l->datahi_reg; - } else { - tcg_out_mov(s, TCG_TYPE_I32, l->datahi_reg, TCG_REG_V1); - } - } + tcg_out_call_int(s, qemu_ld_helpers[opc & MO_SSIZE], false); + /* delay slot */ + tcg_out_nop(s); + + tcg_out_ld_helper_ret(s, l, true, &ldst_helper_param); tcg_out_opc_br(s, OPC_BEQ, TCG_REG_ZERO, TCG_REG_ZERO); if (!reloc_pc16(s->code_ptr - 1, l->raddr)) { @@ -1279,195 +1155,218 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) } /* delay slot */ - if (TCG_TARGET_REG_BITS == 64 && l->type == TCG_TYPE_I32) { - /* we always sign-extend 32-bit loads */ - tcg_out_opc_sa(s, OPC_SLL, v0, TCG_REG_V0, 0); - } else { - tcg_out_opc_reg(s, OPC_OR, v0, TCG_REG_V0, TCG_REG_ZERO); - } + tcg_out_nop(s); return true; } static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { const tcg_insn_unit *tgt_rx = tcg_splitwx_to_rx(s->code_ptr); - MemOpIdx oi = l->oi; - MemOp opc = get_memop(oi); - MemOp s_bits = opc & MO_SIZE; - int i; + MemOp opc = get_memop(l->oi); /* resolve label address */ if (!reloc_pc16(l->label_ptr[0], tgt_rx) - || (TCG_TARGET_REG_BITS < TARGET_LONG_BITS - && !reloc_pc16(l->label_ptr[1], tgt_rx))) { + || (l->label_ptr[1] && !reloc_pc16(l->label_ptr[1], tgt_rx))) { return false; } - i = 1; - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - i = tcg_out_call_iarg_reg2(s, i, l->addrlo_reg, l->addrhi_reg); - } else { - i = tcg_out_call_iarg_reg(s, i, l->addrlo_reg); - } - switch (s_bits) { - case MO_8: - i = tcg_out_call_iarg_reg8(s, i, l->datalo_reg); - break; - case MO_16: - i = tcg_out_call_iarg_reg16(s, i, l->datalo_reg); - break; - case MO_32: - i = tcg_out_call_iarg_reg(s, i, l->datalo_reg); - break; - case MO_64: - if (TCG_TARGET_REG_BITS == 32) { - i = tcg_out_call_iarg_reg2(s, i, l->datalo_reg, l->datahi_reg); - } else { - i = tcg_out_call_iarg_reg(s, i, l->datalo_reg); - } - break; - default: - tcg_abort(); - } - i = tcg_out_call_iarg_imm(s, i, oi); + tcg_out_st_helper_args(s, l, &ldst_helper_param); - /* Tail call to the store helper. Thus force the return address - computation to take place in the return address register. */ - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RA, (intptr_t)l->raddr); - i = tcg_out_call_iarg_reg(s, i, TCG_REG_RA); - tcg_out_call_int(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)], true); + tcg_out_call_int(s, qemu_st_helpers[opc & MO_SIZE], false); /* delay slot */ - tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); - return true; -} + tcg_out_nop(s); -#else - -static void tcg_out_test_alignment(TCGContext *s, bool is_ld, TCGReg addrlo, - TCGReg addrhi, unsigned a_bits) -{ - unsigned a_mask = (1 << a_bits) - 1; - TCGLabelQemuLdst *l = new_ldst_label(s); - - l->is_ld = is_ld; - l->addrlo_reg = addrlo; - l->addrhi_reg = addrhi; - - /* We are expecting a_bits to max out at 7, much lower than ANDI. */ - tcg_debug_assert(a_bits < 16); - tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP0, addrlo, a_mask); - - l->label_ptr[0] = s->code_ptr; - if (use_mips32r6_instructions) { - tcg_out_opc_br(s, OPC_BNEZALC_R6, TCG_REG_ZERO, TCG_TMP0); - } else { - tcg_out_opc_br(s, OPC_BNEL, TCG_TMP0, TCG_REG_ZERO); - tcg_out_nop(s); - } - - l->raddr = tcg_splitwx_to_rx(s->code_ptr); -} - -static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) -{ - void *target; - - if (!reloc_pc16(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { + tcg_out_opc_br(s, OPC_BEQ, TCG_REG_ZERO, TCG_REG_ZERO); + if (!reloc_pc16(s->code_ptr - 1, l->raddr)) { return false; } - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - /* A0 is env, A1 is skipped, A2:A3 is the uint64_t address. */ - TCGReg a2 = MIPS_BE ? l->addrhi_reg : l->addrlo_reg; - TCGReg a3 = MIPS_BE ? l->addrlo_reg : l->addrhi_reg; - - if (a3 != TCG_REG_A2) { - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_A2, a2); - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_A3, a3); - } else if (a2 != TCG_REG_A3) { - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_A3, a3); - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_A2, a2); - } else { - tcg_out_mov(s, TCG_TYPE_I32, TCG_TMP0, TCG_REG_A2); - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_A2, TCG_REG_A3); - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_A3, TCG_TMP0); - } - } else { - tcg_out_mov(s, TCG_TYPE_TL, TCG_REG_A1, l->addrlo_reg); - } - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_A0, TCG_AREG0); - - /* - * Tail call to the helper, with the return address back inline. - * We have arrived here via BNEL, so $31 is already set. - */ - target = (l->is_ld ? helper_unaligned_ld : helper_unaligned_st); - tcg_out_call_int(s, target, true); + /* delay slot */ + tcg_out_nop(s); return true; } -static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +typedef struct { + TCGReg base; + TCGAtomAlign aa; +} HostAddress; + +bool tcg_target_has_memory_bswap(MemOp memop) { - return tcg_out_fail_alignment(s, l); + return false; } -static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +/* We expect to use a 16-bit negative offset from ENV. */ +#define MIN_TLB_MASK_TABLE_OFS -32768 + +/* + * For system-mode, perform the TLB load and compare. + * For user-mode, perform any required alignment tests. + * In both cases, return a TCGLabelQemuLdst structure if the slow path + * is required and fill in @h with the host address for the fast path. + */ +static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, + TCGReg addrlo, TCGReg addrhi, + MemOpIdx oi, bool is_ld) { - return tcg_out_fail_alignment(s, l); + TCGType addr_type = s->addr_type; + TCGLabelQemuLdst *ldst = NULL; + MemOp opc = get_memop(oi); + MemOp a_bits; + unsigned s_bits = opc & MO_SIZE; + unsigned a_mask; + TCGReg base; + + h->aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, false); + a_bits = h->aa.align; + a_mask = (1 << a_bits) - 1; + + if (tcg_use_softmmu) { + unsigned s_mask = (1 << s_bits) - 1; + int mem_index = get_mmuidx(oi); + int fast_off = tlb_mask_table_ofs(s, mem_index); + int mask_off = fast_off + offsetof(CPUTLBDescFast, mask); + int table_off = fast_off + offsetof(CPUTLBDescFast, table); + int add_off = offsetof(CPUTLBEntry, addend); + int cmp_off = is_ld ? offsetof(CPUTLBEntry, addr_read) + : offsetof(CPUTLBEntry, addr_write); + + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addrlo; + ldst->addrhi_reg = addrhi; + + /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP0, TCG_AREG0, mask_off); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP1, TCG_AREG0, table_off); + + /* Extract the TLB index from the address into TMP3. */ + if (TCG_TARGET_REG_BITS == 32 || addr_type == TCG_TYPE_I32) { + tcg_out_opc_sa(s, OPC_SRL, TCG_TMP3, addrlo, + s->page_bits - CPU_TLB_ENTRY_BITS); + } else { + tcg_out_dsrl(s, TCG_TMP3, addrlo, + s->page_bits - CPU_TLB_ENTRY_BITS); + } + tcg_out_opc_reg(s, OPC_AND, TCG_TMP3, TCG_TMP3, TCG_TMP0); + + /* Add the tlb_table pointer, creating the CPUTLBEntry address. */ + tcg_out_opc_reg(s, ALIAS_PADD, TCG_TMP3, TCG_TMP3, TCG_TMP1); + + if (TCG_TARGET_REG_BITS == 32 || addr_type == TCG_TYPE_I32) { + /* Load the (low half) tlb comparator. */ + tcg_out_ld(s, TCG_TYPE_I32, TCG_TMP0, TCG_TMP3, + cmp_off + HOST_BIG_ENDIAN * 4); + } else { + tcg_out_ld(s, TCG_TYPE_I64, TCG_TMP0, TCG_TMP3, cmp_off); + } + + if (TCG_TARGET_REG_BITS == 64 || addr_type == TCG_TYPE_I32) { + /* Load the tlb addend for the fast path. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP3, TCG_TMP3, add_off); + } + + /* + * Mask the page bits, keeping the alignment bits to compare against. + * For unaligned accesses, compare against the end of the access to + * verify that it does not cross a page boundary. + */ + tcg_out_movi(s, addr_type, TCG_TMP1, s->page_mask | a_mask); + if (a_mask < s_mask) { + tcg_out_opc_imm(s, (TCG_TARGET_REG_BITS == 32 + || addr_type == TCG_TYPE_I32 + ? OPC_ADDIU : OPC_DADDIU), + TCG_TMP2, addrlo, s_mask - a_mask); + tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, TCG_TMP2); + } else { + tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, addrlo); + } + + /* Zero extend a 32-bit guest address for a 64-bit host. */ + if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { + tcg_out_ext32u(s, TCG_TMP2, addrlo); + addrlo = TCG_TMP2; + } + + ldst->label_ptr[0] = s->code_ptr; + tcg_out_opc_br(s, OPC_BNE, TCG_TMP1, TCG_TMP0); + + /* Load and test the high half tlb comparator. */ + if (TCG_TARGET_REG_BITS == 32 && addr_type != TCG_TYPE_I32) { + /* delay slot */ + tcg_out_ldst(s, OPC_LW, TCG_TMP0, TCG_TMP3, cmp_off + HI_OFF); + + /* Load the tlb addend for the fast path. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP3, TCG_TMP3, add_off); + + ldst->label_ptr[1] = s->code_ptr; + tcg_out_opc_br(s, OPC_BNE, addrhi, TCG_TMP0); + } + + /* delay slot */ + base = TCG_TMP3; + tcg_out_opc_reg(s, ALIAS_PADD, base, TCG_TMP3, addrlo); + } else { + if (a_mask && (use_mips32r6_instructions || a_bits != s_bits)) { + ldst = new_ldst_label(s); + + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addrlo; + ldst->addrhi_reg = addrhi; + + /* We are expecting a_bits to max out at 7, much lower than ANDI. */ + tcg_debug_assert(a_bits < 16); + tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP0, addrlo, a_mask); + + ldst->label_ptr[0] = s->code_ptr; + if (use_mips32r6_instructions) { + tcg_out_opc_br(s, OPC_BNEZALC_R6, TCG_REG_ZERO, TCG_TMP0); + } else { + tcg_out_opc_br(s, OPC_BNEL, TCG_TMP0, TCG_REG_ZERO); + tcg_out_nop(s); + } + } + + base = addrlo; + if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { + tcg_out_ext32u(s, TCG_REG_A0, base); + base = TCG_REG_A0; + } + if (guest_base) { + if (guest_base == (int16_t)guest_base) { + tcg_out_opc_imm(s, ALIAS_PADDI, TCG_REG_A0, base, guest_base); + } else { + tcg_out_opc_reg(s, ALIAS_PADD, TCG_REG_A0, base, + TCG_GUEST_BASE_REG); + } + base = TCG_REG_A0; + } + } + + h->base = base; + return ldst; } -#endif /* SOFTMMU */ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg lo, TCGReg hi, - TCGReg base, MemOp opc, bool is_64) + TCGReg base, MemOp opc, TCGType type) { - switch (opc & (MO_SSIZE | MO_BSWAP)) { + switch (opc & MO_SSIZE) { case MO_UB: tcg_out_opc_imm(s, OPC_LBU, lo, base, 0); break; case MO_SB: tcg_out_opc_imm(s, OPC_LB, lo, base, 0); break; - case MO_UW | MO_BSWAP: - tcg_out_opc_imm(s, OPC_LHU, TCG_TMP1, base, 0); - tcg_out_bswap16(s, lo, TCG_TMP1, TCG_BSWAP_IZ | TCG_BSWAP_OZ); - break; case MO_UW: tcg_out_opc_imm(s, OPC_LHU, lo, base, 0); break; - case MO_SW | MO_BSWAP: - tcg_out_opc_imm(s, OPC_LHU, TCG_TMP1, base, 0); - tcg_out_bswap16(s, lo, TCG_TMP1, TCG_BSWAP_IZ | TCG_BSWAP_OS); - break; case MO_SW: tcg_out_opc_imm(s, OPC_LH, lo, base, 0); break; - case MO_UL | MO_BSWAP: - if (TCG_TARGET_REG_BITS == 64 && is_64) { - if (use_mips32r2_instructions) { - tcg_out_opc_imm(s, OPC_LWU, lo, base, 0); - tcg_out_bswap32(s, lo, lo, TCG_BSWAP_IZ | TCG_BSWAP_OZ); - } else { - tcg_out_bswap_subr(s, bswap32u_addr); - /* delay slot */ - tcg_out_opc_imm(s, OPC_LWU, TCG_TMP0, base, 0); - tcg_out_mov(s, TCG_TYPE_I64, lo, TCG_TMP3); - } - break; - } - /* FALLTHRU */ - case MO_SL | MO_BSWAP: - if (use_mips32r2_instructions) { - tcg_out_opc_imm(s, OPC_LW, lo, base, 0); - tcg_out_bswap32(s, lo, lo, 0); - } else { - tcg_out_bswap_subr(s, bswap32_addr); - /* delay slot */ - tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, base, 0); - tcg_out_mov(s, TCG_TYPE_I32, lo, TCG_TMP3); - } - break; case MO_UL: - if (TCG_TARGET_REG_BITS == 64 && is_64) { + if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I64) { tcg_out_opc_imm(s, OPC_LWU, lo, base, 0); break; } @@ -1475,40 +1374,11 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg lo, TCGReg hi, case MO_SL: tcg_out_opc_imm(s, OPC_LW, lo, base, 0); break; - case MO_UQ | MO_BSWAP: - if (TCG_TARGET_REG_BITS == 64) { - if (use_mips32r2_instructions) { - tcg_out_opc_imm(s, OPC_LD, lo, base, 0); - tcg_out_bswap64(s, lo, lo); - } else { - tcg_out_bswap_subr(s, bswap64_addr); - /* delay slot */ - tcg_out_opc_imm(s, OPC_LD, TCG_TMP0, base, 0); - tcg_out_mov(s, TCG_TYPE_I64, lo, TCG_TMP3); - } - } else if (use_mips32r2_instructions) { - tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, base, 0); - tcg_out_opc_imm(s, OPC_LW, TCG_TMP1, base, 4); - tcg_out_opc_reg(s, OPC_WSBH, TCG_TMP0, 0, TCG_TMP0); - tcg_out_opc_reg(s, OPC_WSBH, TCG_TMP1, 0, TCG_TMP1); - tcg_out_opc_sa(s, OPC_ROTR, MIPS_BE ? lo : hi, TCG_TMP0, 16); - tcg_out_opc_sa(s, OPC_ROTR, MIPS_BE ? hi : lo, TCG_TMP1, 16); - } else { - tcg_out_bswap_subr(s, bswap32_addr); - /* delay slot */ - tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, base, 0); - tcg_out_opc_imm(s, OPC_LW, TCG_TMP0, base, 4); - tcg_out_bswap_subr(s, bswap32_addr); - /* delay slot */ - tcg_out_mov(s, TCG_TYPE_I32, MIPS_BE ? lo : hi, TCG_TMP3); - tcg_out_mov(s, TCG_TYPE_I32, MIPS_BE ? hi : lo, TCG_TMP3); - } - break; case MO_UQ: /* Prefer to load from offset 0 first, but allow for overlap. */ if (TCG_TARGET_REG_BITS == 64) { tcg_out_opc_imm(s, OPC_LD, lo, base, 0); - } else if (MIPS_BE ? hi != base : lo == base) { + } else if (HOST_BIG_ENDIAN ? hi != base : lo == base) { tcg_out_opc_imm(s, OPC_LW, hi, base, HI_OFF); tcg_out_opc_imm(s, OPC_LW, lo, base, LO_OFF); } else { @@ -1517,36 +1387,31 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg lo, TCGReg hi, } break; default: - tcg_abort(); + g_assert_not_reached(); } } static void tcg_out_qemu_ld_unalign(TCGContext *s, TCGReg lo, TCGReg hi, - TCGReg base, MemOp opc, bool is_64) + TCGReg base, MemOp opc, TCGType type) { - const MIPSInsn lw1 = MIPS_BE ? OPC_LWL : OPC_LWR; - const MIPSInsn lw2 = MIPS_BE ? OPC_LWR : OPC_LWL; - const MIPSInsn ld1 = MIPS_BE ? OPC_LDL : OPC_LDR; - const MIPSInsn ld2 = MIPS_BE ? OPC_LDR : OPC_LDL; + const MIPSInsn lw1 = HOST_BIG_ENDIAN ? OPC_LWL : OPC_LWR; + const MIPSInsn lw2 = HOST_BIG_ENDIAN ? OPC_LWR : OPC_LWL; + const MIPSInsn ld1 = HOST_BIG_ENDIAN ? OPC_LDL : OPC_LDR; + const MIPSInsn ld2 = HOST_BIG_ENDIAN ? OPC_LDR : OPC_LDL; + bool sgn = opc & MO_SIGN; - bool sgn = (opc & MO_SIGN); - - switch (opc & (MO_SSIZE | MO_BSWAP)) { - case MO_SW | MO_BE: - case MO_UW | MO_BE: - tcg_out_opc_imm(s, sgn ? OPC_LB : OPC_LBU, TCG_TMP0, base, 0); - tcg_out_opc_imm(s, OPC_LBU, lo, base, 1); - if (use_mips32r2_instructions) { - tcg_out_opc_bf(s, OPC_INS, lo, TCG_TMP0, 31, 8); - } else { - tcg_out_opc_sa(s, OPC_SLL, TCG_TMP0, TCG_TMP0, 8); - tcg_out_opc_reg(s, OPC_OR, lo, TCG_TMP0, TCG_TMP1); - } - break; - - case MO_SW | MO_LE: - case MO_UW | MO_LE: - if (use_mips32r2_instructions && lo != base) { + switch (opc & MO_SIZE) { + case MO_16: + if (HOST_BIG_ENDIAN) { + tcg_out_opc_imm(s, sgn ? OPC_LB : OPC_LBU, TCG_TMP0, base, 0); + tcg_out_opc_imm(s, OPC_LBU, lo, base, 1); + if (use_mips32r2_instructions) { + tcg_out_opc_bf(s, OPC_INS, lo, TCG_TMP0, 31, 8); + } else { + tcg_out_opc_sa(s, OPC_SLL, TCG_TMP0, TCG_TMP0, 8); + tcg_out_opc_reg(s, OPC_OR, lo, lo, TCG_TMP0); + } + } else if (use_mips32r2_instructions && lo != base) { tcg_out_opc_imm(s, OPC_LBU, lo, base, 0); tcg_out_opc_imm(s, sgn ? OPC_LB : OPC_LBU, TCG_TMP0, base, 1); tcg_out_opc_bf(s, OPC_INS, lo, TCG_TMP0, 31, 8); @@ -1558,81 +1423,23 @@ static void tcg_out_qemu_ld_unalign(TCGContext *s, TCGReg lo, TCGReg hi, } break; - case MO_SL: - case MO_UL: + case MO_32: tcg_out_opc_imm(s, lw1, lo, base, 0); tcg_out_opc_imm(s, lw2, lo, base, 3); - if (TCG_TARGET_REG_BITS == 64 && is_64 && !sgn) { + if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I64 && !sgn) { tcg_out_ext32u(s, lo, lo); } break; - case MO_UL | MO_BSWAP: - case MO_SL | MO_BSWAP: - if (use_mips32r2_instructions) { - tcg_out_opc_imm(s, lw1, lo, base, 0); - tcg_out_opc_imm(s, lw2, lo, base, 3); - tcg_out_bswap32(s, lo, lo, - TCG_TARGET_REG_BITS == 64 && is_64 - ? (sgn ? TCG_BSWAP_OS : TCG_BSWAP_OZ) : 0); - } else { - const tcg_insn_unit *subr = - (TCG_TARGET_REG_BITS == 64 && is_64 && !sgn - ? bswap32u_addr : bswap32_addr); - - tcg_out_opc_imm(s, lw1, TCG_TMP0, base, 0); - tcg_out_bswap_subr(s, subr); - /* delay slot */ - tcg_out_opc_imm(s, lw2, TCG_TMP0, base, 3); - tcg_out_mov(s, is_64 ? TCG_TYPE_I64 : TCG_TYPE_I32, lo, TCG_TMP3); - } - break; - - case MO_UQ: + case MO_64: if (TCG_TARGET_REG_BITS == 64) { tcg_out_opc_imm(s, ld1, lo, base, 0); tcg_out_opc_imm(s, ld2, lo, base, 7); } else { - tcg_out_opc_imm(s, lw1, MIPS_BE ? hi : lo, base, 0 + 0); - tcg_out_opc_imm(s, lw2, MIPS_BE ? hi : lo, base, 0 + 3); - tcg_out_opc_imm(s, lw1, MIPS_BE ? lo : hi, base, 4 + 0); - tcg_out_opc_imm(s, lw2, MIPS_BE ? lo : hi, base, 4 + 3); - } - break; - - case MO_UQ | MO_BSWAP: - if (TCG_TARGET_REG_BITS == 64) { - if (use_mips32r2_instructions) { - tcg_out_opc_imm(s, ld1, lo, base, 0); - tcg_out_opc_imm(s, ld2, lo, base, 7); - tcg_out_bswap64(s, lo, lo); - } else { - tcg_out_opc_imm(s, ld1, TCG_TMP0, base, 0); - tcg_out_bswap_subr(s, bswap64_addr); - /* delay slot */ - tcg_out_opc_imm(s, ld2, TCG_TMP0, base, 7); - tcg_out_mov(s, TCG_TYPE_I64, lo, TCG_TMP3); - } - } else if (use_mips32r2_instructions) { - tcg_out_opc_imm(s, lw1, TCG_TMP0, base, 0 + 0); - tcg_out_opc_imm(s, lw2, TCG_TMP0, base, 0 + 3); - tcg_out_opc_imm(s, lw1, TCG_TMP1, base, 4 + 0); - tcg_out_opc_imm(s, lw2, TCG_TMP1, base, 4 + 3); - tcg_out_opc_reg(s, OPC_WSBH, TCG_TMP0, 0, TCG_TMP0); - tcg_out_opc_reg(s, OPC_WSBH, TCG_TMP1, 0, TCG_TMP1); - tcg_out_opc_sa(s, OPC_ROTR, MIPS_BE ? lo : hi, TCG_TMP0, 16); - tcg_out_opc_sa(s, OPC_ROTR, MIPS_BE ? hi : lo, TCG_TMP1, 16); - } else { - tcg_out_opc_imm(s, lw1, TCG_TMP0, base, 0 + 0); - tcg_out_bswap_subr(s, bswap32_addr); - /* delay slot */ - tcg_out_opc_imm(s, lw2, TCG_TMP0, base, 0 + 3); - tcg_out_opc_imm(s, lw1, TCG_TMP0, base, 4 + 0); - tcg_out_mov(s, TCG_TYPE_I32, MIPS_BE ? lo : hi, TCG_TMP3); - tcg_out_bswap_subr(s, bswap32_addr); - /* delay slot */ - tcg_out_opc_imm(s, lw2, TCG_TMP0, base, 4 + 3); - tcg_out_mov(s, TCG_TYPE_I32, MIPS_BE ? hi : lo, TCG_TMP3); + tcg_out_opc_imm(s, lw1, HOST_BIG_ENDIAN ? hi : lo, base, 0 + 0); + tcg_out_opc_imm(s, lw2, HOST_BIG_ENDIAN ? hi : lo, base, 0 + 3); + tcg_out_opc_imm(s, lw1, HOST_BIG_ENDIAN ? lo : hi, base, 4 + 0); + tcg_out_opc_imm(s, lw2, HOST_BIG_ENDIAN ? lo : hi, base, 4 + 3); } break; @@ -1641,270 +1448,115 @@ static void tcg_out_qemu_ld_unalign(TCGContext *s, TCGReg lo, TCGReg hi, } } -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) +static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg addrlo, TCGReg addrhi, + MemOpIdx oi, TCGType data_type) { - TCGReg addr_regl, addr_regh __attribute__((unused)); - TCGReg data_regl, data_regh; - MemOpIdx oi; - MemOp opc; -#if defined(CONFIG_SOFTMMU) - tcg_insn_unit *label_ptr[2]; -#else -#endif - unsigned a_bits, s_bits; - TCGReg base = TCG_REG_A0; + MemOp opc = get_memop(oi); + TCGLabelQemuLdst *ldst; + HostAddress h; - data_regl = *args++; - data_regh = (TCG_TARGET_REG_BITS == 32 && is_64 ? *args++ : 0); - addr_regl = *args++; - addr_regh = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); - a_bits = get_alignment_bits(opc); - s_bits = opc & MO_SIZE; + ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, true); - /* - * R6 removes the left/right instructions but requires the - * system to support misaligned memory accesses. - */ -#if defined(CONFIG_SOFTMMU) - tcg_out_tlb_load(s, base, addr_regl, addr_regh, oi, label_ptr, 1); - if (use_mips32r6_instructions || a_bits >= s_bits) { - tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc, is_64); + if (use_mips32r6_instructions || h.aa.align >= (opc & MO_SIZE)) { + tcg_out_qemu_ld_direct(s, datalo, datahi, h.base, opc, data_type); } else { - tcg_out_qemu_ld_unalign(s, data_regl, data_regh, base, opc, is_64); + tcg_out_qemu_ld_unalign(s, datalo, datahi, h.base, opc, data_type); } - add_qemu_ldst_label(s, 1, oi, - (is_64 ? TCG_TYPE_I64 : TCG_TYPE_I32), - data_regl, data_regh, addr_regl, addr_regh, - s->code_ptr, label_ptr); -#else - if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { - tcg_out_ext32u(s, base, addr_regl); - addr_regl = base; + + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = datalo; + ldst->datahi_reg = datahi; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } - if (guest_base == 0 && data_regl != addr_regl) { - base = addr_regl; - } else if (guest_base == (int16_t)guest_base) { - tcg_out_opc_imm(s, ALIAS_PADDI, base, addr_regl, guest_base); - } else { - tcg_out_opc_reg(s, ALIAS_PADD, base, TCG_GUEST_BASE_REG, addr_regl); - } - if (use_mips32r6_instructions) { - if (a_bits) { - tcg_out_test_alignment(s, true, addr_regl, addr_regh, a_bits); - } - tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc, is_64); - } else { - if (a_bits && a_bits != s_bits) { - tcg_out_test_alignment(s, true, addr_regl, addr_regh, a_bits); - } - if (a_bits >= s_bits) { - tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc, is_64); - } else { - tcg_out_qemu_ld_unalign(s, data_regl, data_regh, base, opc, is_64); - } - } -#endif } static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg lo, TCGReg hi, TCGReg base, MemOp opc) { - /* Don't clutter the code below with checks to avoid bswapping ZERO. */ - if ((lo | hi) == 0) { - opc &= ~MO_BSWAP; - } - - switch (opc & (MO_SIZE | MO_BSWAP)) { + switch (opc & MO_SIZE) { case MO_8: tcg_out_opc_imm(s, OPC_SB, lo, base, 0); break; - - case MO_16 | MO_BSWAP: - tcg_out_bswap16(s, TCG_TMP1, lo, 0); - lo = TCG_TMP1; - /* FALLTHRU */ case MO_16: tcg_out_opc_imm(s, OPC_SH, lo, base, 0); break; - - case MO_32 | MO_BSWAP: - tcg_out_bswap32(s, TCG_TMP3, lo, 0); - lo = TCG_TMP3; - /* FALLTHRU */ case MO_32: tcg_out_opc_imm(s, OPC_SW, lo, base, 0); break; - - case MO_64 | MO_BSWAP: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_bswap64(s, TCG_TMP3, lo); - tcg_out_opc_imm(s, OPC_SD, TCG_TMP3, base, 0); - } else if (use_mips32r2_instructions) { - tcg_out_opc_reg(s, OPC_WSBH, TCG_TMP0, 0, MIPS_BE ? lo : hi); - tcg_out_opc_reg(s, OPC_WSBH, TCG_TMP1, 0, MIPS_BE ? hi : lo); - tcg_out_opc_sa(s, OPC_ROTR, TCG_TMP0, TCG_TMP0, 16); - tcg_out_opc_sa(s, OPC_ROTR, TCG_TMP1, TCG_TMP1, 16); - tcg_out_opc_imm(s, OPC_SW, TCG_TMP0, base, 0); - tcg_out_opc_imm(s, OPC_SW, TCG_TMP1, base, 4); - } else { - tcg_out_bswap32(s, TCG_TMP3, MIPS_BE ? lo : hi, 0); - tcg_out_opc_imm(s, OPC_SW, TCG_TMP3, base, 0); - tcg_out_bswap32(s, TCG_TMP3, MIPS_BE ? hi : lo, 0); - tcg_out_opc_imm(s, OPC_SW, TCG_TMP3, base, 4); - } - break; case MO_64: if (TCG_TARGET_REG_BITS == 64) { tcg_out_opc_imm(s, OPC_SD, lo, base, 0); } else { - tcg_out_opc_imm(s, OPC_SW, MIPS_BE ? hi : lo, base, 0); - tcg_out_opc_imm(s, OPC_SW, MIPS_BE ? lo : hi, base, 4); + tcg_out_opc_imm(s, OPC_SW, HOST_BIG_ENDIAN ? hi : lo, base, 0); + tcg_out_opc_imm(s, OPC_SW, HOST_BIG_ENDIAN ? lo : hi, base, 4); } break; - default: - tcg_abort(); + g_assert_not_reached(); } } static void tcg_out_qemu_st_unalign(TCGContext *s, TCGReg lo, TCGReg hi, TCGReg base, MemOp opc) { - const MIPSInsn sw1 = MIPS_BE ? OPC_SWL : OPC_SWR; - const MIPSInsn sw2 = MIPS_BE ? OPC_SWR : OPC_SWL; - const MIPSInsn sd1 = MIPS_BE ? OPC_SDL : OPC_SDR; - const MIPSInsn sd2 = MIPS_BE ? OPC_SDR : OPC_SDL; + const MIPSInsn sw1 = HOST_BIG_ENDIAN ? OPC_SWL : OPC_SWR; + const MIPSInsn sw2 = HOST_BIG_ENDIAN ? OPC_SWR : OPC_SWL; + const MIPSInsn sd1 = HOST_BIG_ENDIAN ? OPC_SDL : OPC_SDR; + const MIPSInsn sd2 = HOST_BIG_ENDIAN ? OPC_SDR : OPC_SDL; - /* Don't clutter the code below with checks to avoid bswapping ZERO. */ - if ((lo | hi) == 0) { - opc &= ~MO_BSWAP; - } - - switch (opc & (MO_SIZE | MO_BSWAP)) { - case MO_16 | MO_BE: + switch (opc & MO_SIZE) { + case MO_16: tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, lo, 8); - tcg_out_opc_imm(s, OPC_SB, TCG_TMP0, base, 0); - tcg_out_opc_imm(s, OPC_SB, lo, base, 1); + tcg_out_opc_imm(s, OPC_SB, HOST_BIG_ENDIAN ? TCG_TMP0 : lo, base, 0); + tcg_out_opc_imm(s, OPC_SB, HOST_BIG_ENDIAN ? lo : TCG_TMP0, base, 1); break; - case MO_16 | MO_LE: - tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, lo, 8); - tcg_out_opc_imm(s, OPC_SB, lo, base, 0); - tcg_out_opc_imm(s, OPC_SB, TCG_TMP0, base, 1); - break; - - case MO_32 | MO_BSWAP: - tcg_out_bswap32(s, TCG_TMP3, lo, 0); - lo = TCG_TMP3; - /* fall through */ case MO_32: tcg_out_opc_imm(s, sw1, lo, base, 0); tcg_out_opc_imm(s, sw2, lo, base, 3); break; - case MO_64 | MO_BSWAP: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_bswap64(s, TCG_TMP3, lo); - lo = TCG_TMP3; - } else if (use_mips32r2_instructions) { - tcg_out_opc_reg(s, OPC_WSBH, TCG_TMP0, 0, MIPS_BE ? hi : lo); - tcg_out_opc_reg(s, OPC_WSBH, TCG_TMP1, 0, MIPS_BE ? lo : hi); - tcg_out_opc_sa(s, OPC_ROTR, TCG_TMP0, TCG_TMP0, 16); - tcg_out_opc_sa(s, OPC_ROTR, TCG_TMP1, TCG_TMP1, 16); - hi = MIPS_BE ? TCG_TMP0 : TCG_TMP1; - lo = MIPS_BE ? TCG_TMP1 : TCG_TMP0; - } else { - tcg_out_bswap32(s, TCG_TMP3, MIPS_BE ? lo : hi, 0); - tcg_out_opc_imm(s, sw1, TCG_TMP3, base, 0 + 0); - tcg_out_opc_imm(s, sw2, TCG_TMP3, base, 0 + 3); - tcg_out_bswap32(s, TCG_TMP3, MIPS_BE ? hi : lo, 0); - tcg_out_opc_imm(s, sw1, TCG_TMP3, base, 4 + 0); - tcg_out_opc_imm(s, sw2, TCG_TMP3, base, 4 + 3); - break; - } - /* fall through */ case MO_64: if (TCG_TARGET_REG_BITS == 64) { tcg_out_opc_imm(s, sd1, lo, base, 0); tcg_out_opc_imm(s, sd2, lo, base, 7); } else { - tcg_out_opc_imm(s, sw1, MIPS_BE ? hi : lo, base, 0 + 0); - tcg_out_opc_imm(s, sw2, MIPS_BE ? hi : lo, base, 0 + 3); - tcg_out_opc_imm(s, sw1, MIPS_BE ? lo : hi, base, 4 + 0); - tcg_out_opc_imm(s, sw2, MIPS_BE ? lo : hi, base, 4 + 3); + tcg_out_opc_imm(s, sw1, HOST_BIG_ENDIAN ? hi : lo, base, 0 + 0); + tcg_out_opc_imm(s, sw2, HOST_BIG_ENDIAN ? hi : lo, base, 0 + 3); + tcg_out_opc_imm(s, sw1, HOST_BIG_ENDIAN ? lo : hi, base, 4 + 0); + tcg_out_opc_imm(s, sw2, HOST_BIG_ENDIAN ? lo : hi, base, 4 + 3); } break; default: - tcg_abort(); + g_assert_not_reached(); } } -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) + +static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg addrlo, TCGReg addrhi, + MemOpIdx oi, TCGType data_type) { - TCGReg addr_regl, addr_regh __attribute__((unused)); - TCGReg data_regl, data_regh; - MemOpIdx oi; - MemOp opc; -#if defined(CONFIG_SOFTMMU) - tcg_insn_unit *label_ptr[2]; -#endif - unsigned a_bits, s_bits; - TCGReg base = TCG_REG_A0; + MemOp opc = get_memop(oi); + TCGLabelQemuLdst *ldst; + HostAddress h; - data_regl = *args++; - data_regh = (TCG_TARGET_REG_BITS == 32 && is_64 ? *args++ : 0); - addr_regl = *args++; - addr_regh = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); - a_bits = get_alignment_bits(opc); - s_bits = opc & MO_SIZE; + ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, false); - /* - * R6 removes the left/right instructions but requires the - * system to support misaligned memory accesses. - */ -#if defined(CONFIG_SOFTMMU) - tcg_out_tlb_load(s, base, addr_regl, addr_regh, oi, label_ptr, 0); - if (use_mips32r6_instructions || a_bits >= s_bits) { - tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); + if (use_mips32r6_instructions || h.aa.align >= (opc & MO_SIZE)) { + tcg_out_qemu_st_direct(s, datalo, datahi, h.base, opc); } else { - tcg_out_qemu_st_unalign(s, data_regl, data_regh, base, opc); + tcg_out_qemu_st_unalign(s, datalo, datahi, h.base, opc); } - add_qemu_ldst_label(s, 0, oi, - (is_64 ? TCG_TYPE_I64 : TCG_TYPE_I32), - data_regl, data_regh, addr_regl, addr_regh, - s->code_ptr, label_ptr); -#else - if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { - tcg_out_ext32u(s, base, addr_regl); - addr_regl = base; + + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = datalo; + ldst->datahi_reg = datahi; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } - if (guest_base == 0) { - base = addr_regl; - } else if (guest_base == (int16_t)guest_base) { - tcg_out_opc_imm(s, ALIAS_PADDI, base, addr_regl, guest_base); - } else { - tcg_out_opc_reg(s, ALIAS_PADD, base, TCG_GUEST_BASE_REG, addr_regl); - } - if (use_mips32r6_instructions) { - if (a_bits) { - tcg_out_test_alignment(s, true, addr_regl, addr_regh, a_bits); - } - tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); - } else { - if (a_bits && a_bits != s_bits) { - tcg_out_test_alignment(s, true, addr_regl, addr_regh, a_bits); - } - if (a_bits >= s_bits) { - tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); - } else { - tcg_out_qemu_st_unalign(s, data_regl, data_regh, base, opc); - } - } -#endif } static void tcg_out_mb(TCGContext *s, TCGArg a0) @@ -1950,6 +1602,71 @@ static void tcg_out_clz(TCGContext *s, MIPSInsn opcv2, MIPSInsn opcv6, } } +static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) +{ + TCGReg base = TCG_REG_ZERO; + int16_t lo = 0; + + if (a0) { + intptr_t ofs; + if (TCG_TARGET_REG_BITS == 64) { + ofs = tcg_tbrel_diff(s, (void *)a0); + lo = ofs; + if (ofs == lo) { + base = TCG_REG_TB; + } else { + base = TCG_REG_V0; + tcg_out_movi(s, TCG_TYPE_PTR, base, ofs - lo); + tcg_out_opc_reg(s, ALIAS_PADD, base, base, TCG_REG_TB); + } + } else { + ofs = a0; + lo = ofs; + base = TCG_REG_V0; + tcg_out_movi(s, TCG_TYPE_PTR, base, ofs - lo); + } + } + if (!tcg_out_opc_jmp(s, OPC_J, tb_ret_addr)) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, (uintptr_t)tb_ret_addr); + tcg_out_opc_reg(s, OPC_JR, 0, TCG_TMP0, 0); + } + /* delay slot */ + tcg_out_opc_imm(s, ALIAS_PADDI, TCG_REG_V0, base, lo); +} + +static void tcg_out_goto_tb(TCGContext *s, int which) +{ + intptr_t ofs = get_jmp_target_addr(s, which); + TCGReg base, dest; + + /* indirect jump method */ + if (TCG_TARGET_REG_BITS == 64) { + dest = TCG_REG_TB; + base = TCG_REG_TB; + ofs = tcg_tbrel_diff(s, (void *)ofs); + } else { + dest = TCG_TMP0; + base = TCG_REG_ZERO; + } + tcg_out_ld(s, TCG_TYPE_PTR, dest, base, ofs); + tcg_out_opc_reg(s, OPC_JR, 0, dest, 0); + /* delay slot */ + tcg_out_nop(s); + + set_jmp_reset_offset(s, which); + if (TCG_TARGET_REG_BITS == 64) { + /* For the unlinked case, need to reset TCG_REG_TB. */ + tcg_out_ldst(s, ALIAS_PADDI, TCG_REG_TB, TCG_REG_TB, + -tcg_current_code_size(s)); + } +} + +void tb_target_set_jmp_target(const TranslationBlock *tb, int n, + uintptr_t jmp_rx, uintptr_t jmp_rw) +{ + /* Always indirect, nothing to do */ +} + static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1969,36 +1686,14 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, c2 = const_args[2]; switch (opc) { - case INDEX_op_exit_tb: - { - TCGReg b0 = TCG_REG_ZERO; - - a0 = (intptr_t)a0; - if (a0 & ~0xffff) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_V0, a0 & ~0xffff); - b0 = TCG_REG_V0; - } - if (!tcg_out_opc_jmp(s, OPC_J, tb_ret_addr)) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, - (uintptr_t)tb_ret_addr); - tcg_out_opc_reg(s, OPC_JR, 0, TCG_TMP0, 0); - } - tcg_out_opc_imm(s, OPC_ORI, TCG_REG_V0, b0, a0 & 0xffff); - } - break; - case INDEX_op_goto_tb: - /* indirect jump method */ - tcg_debug_assert(s->tb_jmp_insn_offset == 0); - tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP0, TCG_REG_ZERO, - (uintptr_t)(s->tb_jmp_target_addr + a0)); - tcg_out_opc_reg(s, OPC_JR, 0, TCG_TMP0, 0); - tcg_out_nop(s); - set_jmp_reset_offset(s, a0); - break; case INDEX_op_goto_ptr: /* jmp to the given host address (could be epilogue) */ tcg_out_opc_reg(s, OPC_JR, 0, a0, 0); - tcg_out_nop(s); + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_TB, a0); + } else { + tcg_out_nop(s); + } break; case INDEX_op_br: tcg_out_brcond(s, TCG_COND_EQ, TCG_REG_ZERO, TCG_REG_ZERO, @@ -2226,17 +1921,16 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_opc_reg(s, OPC_MFHI, a1, 0, 0); break; + case INDEX_op_neg_i32: + i1 = OPC_SUBU; + goto do_unary; + case INDEX_op_neg_i64: + i1 = OPC_DSUBU; + goto do_unary; case INDEX_op_not_i32: case INDEX_op_not_i64: i1 = OPC_NOR; goto do_unary; - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - i1 = OPC_SEB; - goto do_unary; - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - i1 = OPC_SEH; do_unary: tcg_out_opc_reg(s, i1, a0, TCG_REG_ZERO, a1); break; @@ -2257,15 +1951,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_extrh_i64_i32: tcg_out_dsra(s, a0, a1, 32); break; - case INDEX_op_ext32s_i64: - case INDEX_op_ext_i32_i64: - case INDEX_op_extrl_i64_i32: - tcg_out_opc_sa(s, OPC_SLL, a0, a1, 0); - break; - case INDEX_op_ext32u_i64: - case INDEX_op_extu_i32_i64: - tcg_out_ext32u(s, a0, a1); - break; case INDEX_op_sar_i32: i1 = OPC_SRAV, i2 = OPC_SRA; @@ -2374,17 +2059,52 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_setcond2(s, args[5], a0, a1, a2, args[3], args[4]); break; - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args, false); + case INDEX_op_qemu_ld_a64_i32: + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_qemu_ld(s, a0, 0, a1, a2, args[3], TCG_TYPE_I32); + break; + } + /* fall through */ + case INDEX_op_qemu_ld_a32_i32: + tcg_out_qemu_ld(s, a0, 0, a1, 0, a2, TCG_TYPE_I32); break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args, true); + case INDEX_op_qemu_ld_a32_i64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_qemu_ld(s, a0, 0, a1, 0, a2, TCG_TYPE_I64); + } else { + tcg_out_qemu_ld(s, a0, a1, a2, 0, args[3], TCG_TYPE_I64); + } break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args, false); + case INDEX_op_qemu_ld_a64_i64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_qemu_ld(s, a0, 0, a1, 0, a2, TCG_TYPE_I64); + } else { + tcg_out_qemu_ld(s, a0, a1, a2, args[3], args[4], TCG_TYPE_I64); + } break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args, true); + + case INDEX_op_qemu_st_a64_i32: + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_qemu_st(s, a0, 0, a1, a2, args[3], TCG_TYPE_I32); + break; + } + /* fall through */ + case INDEX_op_qemu_st_a32_i32: + tcg_out_qemu_st(s, a0, 0, a1, 0, a2, TCG_TYPE_I32); + break; + case INDEX_op_qemu_st_a32_i64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_qemu_st(s, a0, 0, a1, 0, a2, TCG_TYPE_I64); + } else { + tcg_out_qemu_st(s, a0, a1, a2, 0, args[3], TCG_TYPE_I64); + } + break; + case INDEX_op_qemu_st_a64_i64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_qemu_st(s, a0, 0, a1, 0, a2, TCG_TYPE_I64); + } else { + tcg_out_qemu_st(s, a0, a1, a2, args[3], args[4], TCG_TYPE_I64); + } break; case INDEX_op_add2_i32: @@ -2402,8 +2122,21 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ + case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ + case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ + case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ + case INDEX_op_ext8s_i64: + case INDEX_op_ext8u_i32: + case INDEX_op_ext8u_i64: + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + case INDEX_op_ext32s_i64: + case INDEX_op_ext32u_i64: + case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: default: - tcg_abort(); + g_assert_not_reached(); } } @@ -2418,6 +2151,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: + case INDEX_op_neg_i32: case INDEX_op_not_i32: case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: @@ -2431,6 +2165,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: + case INDEX_op_neg_i64: case INDEX_op_not_i64: case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i64: @@ -2527,20 +2262,23 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_brcond2_i32: return C_O0_I4(rZ, rZ, rZ, rZ); - case INDEX_op_qemu_ld_i32: - return (TCG_TARGET_REG_BITS == 64 || TARGET_LONG_BITS == 32 - ? C_O1_I1(r, L) : C_O1_I2(r, L, L)); - case INDEX_op_qemu_st_i32: - return (TCG_TARGET_REG_BITS == 64 || TARGET_LONG_BITS == 32 - ? C_O0_I2(SZ, S) : C_O0_I3(SZ, S, S)); - case INDEX_op_qemu_ld_i64: - return (TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, L) - : TARGET_LONG_BITS == 32 ? C_O2_I1(r, r, L) - : C_O2_I2(r, r, L, L)); - case INDEX_op_qemu_st_i64: - return (TCG_TARGET_REG_BITS == 64 ? C_O0_I2(SZ, S) - : TARGET_LONG_BITS == 32 ? C_O0_I3(SZ, SZ, S) - : C_O0_I4(SZ, SZ, S, S)); + case INDEX_op_qemu_ld_a32_i32: + return C_O1_I1(r, r); + case INDEX_op_qemu_ld_a64_i32: + return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O1_I2(r, r, r); + case INDEX_op_qemu_st_a32_i32: + return C_O0_I2(rZ, r); + case INDEX_op_qemu_st_a64_i32: + return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(rZ, r) : C_O0_I3(rZ, r, r); + case INDEX_op_qemu_ld_a32_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I1(r, r, r); + case INDEX_op_qemu_ld_a64_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I2(r, r, r, r); + case INDEX_op_qemu_st_a32_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(rZ, r) : C_O0_I3(rZ, rZ, r); + case INDEX_op_qemu_st_a64_i64: + return (TCG_TARGET_REG_BITS == 64 ? C_O0_I2(rZ, r) + : C_O0_I4(rZ, rZ, r, r)); default: g_assert_not_reached(); @@ -2548,15 +2286,15 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) } static const int tcg_target_callee_save_regs[] = { - TCG_REG_S0, /* used for the global env (TCG_AREG0) */ + TCG_REG_S0, TCG_REG_S1, TCG_REG_S2, TCG_REG_S3, TCG_REG_S4, TCG_REG_S5, - TCG_REG_S6, - TCG_REG_S7, - TCG_REG_S8, + TCG_REG_S6, /* used for the tb base (TCG_REG_TB) */ + TCG_REG_S7, /* used for guest_base */ + TCG_REG_S8, /* used for the global env (TCG_AREG0) */ TCG_REG_RA, /* should be last for ABI compliance */ }; @@ -2676,12 +2414,23 @@ static void tcg_target_qemu_prologue(TCGContext *s) TCG_REG_SP, SAVE_OFS + i * REG_SIZE); } -#ifndef CONFIG_SOFTMMU - if (guest_base) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, guest_base); + if (!tcg_use_softmmu && guest_base != (int16_t)guest_base) { + /* + * The function call abi for n32 and n64 will have loaded $25 (t9) + * with the address of the prologue, so we can use that instead + * of TCG_REG_TB. + */ +#if TCG_TARGET_REG_BITS == 64 && !defined(__mips_abicalls) +# error "Unknown mips abi" +#endif + tcg_out_movi_int(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, guest_base, + TCG_TARGET_REG_BITS == 64 ? TCG_REG_T9 : 0); tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); } -#endif + + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_TB, tcg_target_call_iarg_regs[1]); + } /* Call generated code */ tcg_out_opc_reg(s, OPC_JR, 0, tcg_target_call_iarg_regs[1], 0); @@ -2826,6 +2575,11 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_opc_reg(s, OPC_OR, TCG_TMP3, TCG_TMP3, TCG_TMP1); } +static void tcg_out_tb_start(TCGContext *s) +{ + /* nothing to do */ +} + static void tcg_target_init(TCGContext *s) { tcg_target_detect_isa(); @@ -2863,6 +2617,9 @@ static void tcg_target_init(TCGContext *s) tcg_regset_set_reg(s->reserved_regs, TCG_REG_RA); /* return address */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); /* stack pointer */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_GP); /* global pointer */ + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TB); /* tc->tc_ptr */ + } } typedef struct { diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index 7669213175..a996aa171d 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -27,16 +27,7 @@ #ifndef MIPS_TCG_TARGET_H #define MIPS_TCG_TARGET_H -#if _MIPS_SIM == _ABIO32 -# define TCG_TARGET_REG_BITS 32 -#elif _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 -# define TCG_TARGET_REG_BITS 64 -#else -# error "Unknown ABI" -#endif - #define TCG_TARGET_INSN_UNIT_SIZE 4 -#define TCG_TARGET_TLB_DISPLACEMENT_BITS 16 #define TCG_TARGET_NB_REGS 32 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) @@ -76,17 +67,22 @@ typedef enum { TCG_REG_RA, TCG_REG_CALL_STACK = TCG_REG_SP, - TCG_AREG0 = TCG_REG_S0, + TCG_AREG0 = TCG_REG_S8, } TCGReg; /* used for function call generation */ #define TCG_TARGET_STACK_ALIGN 16 #if _MIPS_SIM == _ABIO32 # define TCG_TARGET_CALL_STACK_OFFSET 16 +# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_EVEN +# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF #else # define TCG_TARGET_CALL_STACK_OFFSET 0 +# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL #endif -#define TCG_TARGET_CALL_ALIGN_ARGS 1 +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN /* MOVN/MOVZ instructions detection */ #if (defined(__mips_isa_rev) && (__mips_isa_rev >= 1)) || \ @@ -132,13 +128,12 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_muluh_i32 1 #define TCG_TARGET_HAS_mulsh_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_direct_jump 0 +#define TCG_TARGET_HAS_negsetcond_i32 0 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 -#define TCG_TARGET_HAS_extrl_i64_i32 1 -#define TCG_TARGET_HAS_extrh_i64_i32 1 +#define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_not_i64 1 @@ -155,10 +150,10 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_mulsh_i64 1 #define TCG_TARGET_HAS_ext32s_i64 1 #define TCG_TARGET_HAS_ext32u_i64 1 +#define TCG_TARGET_HAS_negsetcond_i64 0 #endif /* optional instructions detected at runtime */ -#define TCG_TARGET_HAS_movcond_i32 use_movnz_instructions #define TCG_TARGET_HAS_bswap16_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_deposit_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_extract_i32 use_mips32r2_instructions @@ -173,7 +168,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_movcond_i64 use_movnz_instructions #define TCG_TARGET_HAS_bswap16_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_bswap32_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_bswap64_i64 use_mips32r2_instructions @@ -190,23 +184,20 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions automatically implemented */ -#define TCG_TARGET_HAS_neg_i32 0 /* sub rd, zero, rt */ #define TCG_TARGET_HAS_ext8u_i32 0 /* andi rt, rs, 0xff */ #define TCG_TARGET_HAS_ext16u_i32 0 /* andi rt, rs, 0xffff */ #if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_neg_i64 0 /* sub rd, zero, rt */ #define TCG_TARGET_HAS_ext8u_i64 0 /* andi rt, rs, 0xff */ #define TCG_TARGET_HAS_ext16u_i64 0 /* andi rt, rs, 0xffff */ #endif -#define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_HAS_MEMORY_BSWAP 1 +#define TCG_TARGET_HAS_qemu_ldst_i128 0 -/* not defined -- call should be eliminated at compile time */ -void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t, uintptr_t) - QEMU_ERROR("code path is reachable"); +#define TCG_TARGET_HAS_tst 0 +#define TCG_TARGET_DEFAULT_MO 0 #define TCG_TARGET_NEED_LDST_LABELS +#define TCG_TARGET_NEED_POOL_LABELS #endif diff --git a/tcg/optimize.c b/tcg/optimize.c index ae081ab29c..2e9e5725a9 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -25,7 +25,8 @@ #include "qemu/osdep.h" #include "qemu/int128.h" -#include "tcg/tcg-op.h" +#include "qemu/interval-tree.h" +#include "tcg/tcg-op-common.h" #include "tcg-internal.h" #define CASE_OP_32_64(x) \ @@ -37,10 +38,18 @@ glue(glue(case INDEX_op_, x), _i64): \ glue(glue(case INDEX_op_, x), _vec) +typedef struct MemCopyInfo { + IntervalTreeNode itree; + QSIMPLEQ_ENTRY (MemCopyInfo) next; + TCGTemp *ts; + TCGType type; +} MemCopyInfo; + typedef struct TempOptInfo { bool is_const; TCGTemp *prev_copy; TCGTemp *next_copy; + QSIMPLEQ_HEAD(, MemCopyInfo) mem_copy; uint64_t val; uint64_t z_mask; /* mask bit is 0 if and only if value bit is 0 */ uint64_t s_mask; /* a left-aligned mask of clrsb(value) bits. */ @@ -51,6 +60,9 @@ typedef struct OptContext { TCGOp *prev_mb; TCGTempSet temps_used; + IntervalTreeRoot mem_copy; + QSIMPLEQ_HEAD(, MemCopyInfo) mem_free; + /* In flight values from optimization. */ uint64_t a_mask; /* mask bit is 0 iff value identical to first input */ uint64_t z_mask; /* mask bit is 0 iff value bit is 0 */ @@ -112,35 +124,30 @@ static inline bool ts_is_const(TCGTemp *ts) return ts_info(ts)->is_const; } +static inline bool ts_is_const_val(TCGTemp *ts, uint64_t val) +{ + TempOptInfo *ti = ts_info(ts); + return ti->is_const && ti->val == val; +} + static inline bool arg_is_const(TCGArg arg) { return ts_is_const(arg_temp(arg)); } +static inline bool arg_is_const_val(TCGArg arg, uint64_t val) +{ + return ts_is_const_val(arg_temp(arg), val); +} + static inline bool ts_is_copy(TCGTemp *ts) { return ts_info(ts)->next_copy != ts; } -/* Reset TEMP's state, possibly removing the temp for the list of copies. */ -static void reset_ts(TCGTemp *ts) +static TCGTemp *cmp_better_copy(TCGTemp *a, TCGTemp *b) { - TempOptInfo *ti = ts_info(ts); - TempOptInfo *pi = ts_info(ti->prev_copy); - TempOptInfo *ni = ts_info(ti->next_copy); - - ni->prev_copy = ti->prev_copy; - pi->next_copy = ti->next_copy; - ti->next_copy = ts; - ti->prev_copy = ts; - ti->is_const = false; - ti->z_mask = -1; - ti->s_mask = 0; -} - -static void reset_temp(TCGArg arg) -{ - reset_ts(arg_temp(arg)); + return a->kind < b->kind ? b : a; } /* Initialize and activate a temporary. */ @@ -162,6 +169,7 @@ static void init_ts_info(OptContext *ctx, TCGTemp *ts) ti->next_copy = ts; ti->prev_copy = ts; + QSIMPLEQ_INIT(&ti->mem_copy); if (ts->kind == TEMP_CONST) { ti->is_const = true; ti->val = ts->val; @@ -174,30 +182,133 @@ static void init_ts_info(OptContext *ctx, TCGTemp *ts) } } -static TCGTemp *find_better_copy(TCGContext *s, TCGTemp *ts) +static MemCopyInfo *mem_copy_first(OptContext *ctx, intptr_t s, intptr_t l) { - TCGTemp *i, *g, *l; + IntervalTreeNode *r = interval_tree_iter_first(&ctx->mem_copy, s, l); + return r ? container_of(r, MemCopyInfo, itree) : NULL; +} + +static MemCopyInfo *mem_copy_next(MemCopyInfo *mem, intptr_t s, intptr_t l) +{ + IntervalTreeNode *r = interval_tree_iter_next(&mem->itree, s, l); + return r ? container_of(r, MemCopyInfo, itree) : NULL; +} + +static void remove_mem_copy(OptContext *ctx, MemCopyInfo *mc) +{ + TCGTemp *ts = mc->ts; + TempOptInfo *ti = ts_info(ts); + + interval_tree_remove(&mc->itree, &ctx->mem_copy); + QSIMPLEQ_REMOVE(&ti->mem_copy, mc, MemCopyInfo, next); + QSIMPLEQ_INSERT_TAIL(&ctx->mem_free, mc, next); +} + +static void remove_mem_copy_in(OptContext *ctx, intptr_t s, intptr_t l) +{ + while (true) { + MemCopyInfo *mc = mem_copy_first(ctx, s, l); + if (!mc) { + break; + } + remove_mem_copy(ctx, mc); + } +} + +static void remove_mem_copy_all(OptContext *ctx) +{ + remove_mem_copy_in(ctx, 0, -1); + tcg_debug_assert(interval_tree_is_empty(&ctx->mem_copy)); +} + +static TCGTemp *find_better_copy(TCGTemp *ts) +{ + TCGTemp *i, *ret; /* If this is already readonly, we can't do better. */ if (temp_readonly(ts)) { return ts; } - g = l = NULL; + ret = ts; for (i = ts_info(ts)->next_copy; i != ts; i = ts_info(i)->next_copy) { - if (temp_readonly(i)) { - return i; - } else if (i->kind > ts->kind) { - if (i->kind == TEMP_GLOBAL) { - g = i; - } else if (i->kind == TEMP_LOCAL) { - l = i; + ret = cmp_better_copy(ret, i); + } + return ret; +} + +static void move_mem_copies(TCGTemp *dst_ts, TCGTemp *src_ts) +{ + TempOptInfo *si = ts_info(src_ts); + TempOptInfo *di = ts_info(dst_ts); + MemCopyInfo *mc; + + QSIMPLEQ_FOREACH(mc, &si->mem_copy, next) { + tcg_debug_assert(mc->ts == src_ts); + mc->ts = dst_ts; + } + QSIMPLEQ_CONCAT(&di->mem_copy, &si->mem_copy); +} + +/* Reset TEMP's state, possibly removing the temp for the list of copies. */ +static void reset_ts(OptContext *ctx, TCGTemp *ts) +{ + TempOptInfo *ti = ts_info(ts); + TCGTemp *pts = ti->prev_copy; + TCGTemp *nts = ti->next_copy; + TempOptInfo *pi = ts_info(pts); + TempOptInfo *ni = ts_info(nts); + + ni->prev_copy = ti->prev_copy; + pi->next_copy = ti->next_copy; + ti->next_copy = ts; + ti->prev_copy = ts; + ti->is_const = false; + ti->z_mask = -1; + ti->s_mask = 0; + + if (!QSIMPLEQ_EMPTY(&ti->mem_copy)) { + if (ts == nts) { + /* Last temp copy being removed, the mem copies die. */ + MemCopyInfo *mc; + QSIMPLEQ_FOREACH(mc, &ti->mem_copy, next) { + interval_tree_remove(&mc->itree, &ctx->mem_copy); } + QSIMPLEQ_CONCAT(&ctx->mem_free, &ti->mem_copy); + } else { + move_mem_copies(find_better_copy(nts), ts); } } +} - /* If we didn't find a better representation, return the same temp. */ - return g ? g : l ? l : ts; +static void reset_temp(OptContext *ctx, TCGArg arg) +{ + reset_ts(ctx, arg_temp(arg)); +} + +static void record_mem_copy(OptContext *ctx, TCGType type, + TCGTemp *ts, intptr_t start, intptr_t last) +{ + MemCopyInfo *mc; + TempOptInfo *ti; + + mc = QSIMPLEQ_FIRST(&ctx->mem_free); + if (mc) { + QSIMPLEQ_REMOVE_HEAD(&ctx->mem_free, next); + } else { + mc = tcg_malloc(sizeof(*mc)); + } + + memset(mc, 0, sizeof(*mc)); + mc->itree.start = start; + mc->itree.last = last; + mc->type = type; + interval_tree_insert(&mc->itree, &ctx->mem_copy); + + ts = find_better_copy(ts); + ti = ts_info(ts); + mc->ts = ts; + QSIMPLEQ_INSERT_TAIL(&ti->mem_copy, mc, next); } static bool ts_are_copies(TCGTemp *ts1, TCGTemp *ts2) @@ -226,6 +337,40 @@ static bool args_are_copies(TCGArg arg1, TCGArg arg2) return ts_are_copies(arg_temp(arg1), arg_temp(arg2)); } +static TCGTemp *find_mem_copy_for(OptContext *ctx, TCGType type, intptr_t s) +{ + MemCopyInfo *mc; + + for (mc = mem_copy_first(ctx, s, s); mc; mc = mem_copy_next(mc, s, s)) { + if (mc->itree.start == s && mc->type == type) { + return find_better_copy(mc->ts); + } + } + return NULL; +} + +static TCGArg arg_new_constant(OptContext *ctx, uint64_t val) +{ + TCGType type = ctx->type; + TCGTemp *ts; + + if (type == TCG_TYPE_I32) { + val = (int32_t)val; + } + + ts = tcg_constant_internal(type, val); + init_ts_info(ctx, ts); + + return temp_arg(ts); +} + +static TCGArg arg_new_temp(OptContext *ctx) +{ + TCGTemp *ts = tcg_temp_new_internal(ctx->type, TEMP_EBB); + init_ts_info(ctx, ts); + return temp_arg(ts); +} + static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) { TCGTemp *dst_ts = arg_temp(dst); @@ -239,7 +384,7 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) return true; } - reset_ts(dst_ts); + reset_ts(ctx, dst_ts); di = ts_info(dst_ts); si = ts_info(src_ts); @@ -275,6 +420,11 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) si->next_copy = dst_ts; di->is_const = si->is_const; di->val = si->val; + + if (!QSIMPLEQ_EMPTY(&si->mem_copy) + && cmp_better_copy(src_ts, dst_ts) == dst_ts) { + move_mem_copies(dst_ts, src_ts); + } } return true; } @@ -282,16 +432,8 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) static bool tcg_opt_gen_movi(OptContext *ctx, TCGOp *op, TCGArg dst, uint64_t val) { - TCGTemp *tv; - - if (ctx->type == TCG_TYPE_I32) { - val = (int32_t)val; - } - /* Convert movi to mov with constant temp. */ - tv = tcg_constant_internal(ctx->type, val); - init_ts_info(ctx, tv); - return tcg_opt_gen_mov(ctx, op, dst, temp_arg(tv)); + return tcg_opt_gen_mov(ctx, op, dst, arg_new_constant(ctx, val)); } static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) @@ -453,9 +595,7 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) return (uint64_t)x % ((uint64_t)y ? : 1); default: - fprintf(stderr, - "Unrecognized operation %d in do_constant_folding.\n", op); - tcg_abort(); + g_assert_not_reached(); } } @@ -492,9 +632,15 @@ static bool do_constant_folding_cond_32(uint32_t x, uint32_t y, TCGCond c) return x <= y; case TCG_COND_GTU: return x > y; - default: - tcg_abort(); + case TCG_COND_TSTEQ: + return (x & y) == 0; + case TCG_COND_TSTNE: + return (x & y) != 0; + case TCG_COND_ALWAYS: + case TCG_COND_NEVER: + break; } + g_assert_not_reached(); } static bool do_constant_folding_cond_64(uint64_t x, uint64_t y, TCGCond c) @@ -520,12 +666,18 @@ static bool do_constant_folding_cond_64(uint64_t x, uint64_t y, TCGCond c) return x <= y; case TCG_COND_GTU: return x > y; - default: - tcg_abort(); + case TCG_COND_TSTEQ: + return (x & y) == 0; + case TCG_COND_TSTNE: + return (x & y) != 0; + case TCG_COND_ALWAYS: + case TCG_COND_NEVER: + break; } + g_assert_not_reached(); } -static bool do_constant_folding_cond_eq(TCGCond c) +static int do_constant_folding_cond_eq(TCGCond c) { switch (c) { case TCG_COND_GT: @@ -540,9 +692,14 @@ static bool do_constant_folding_cond_eq(TCGCond c) case TCG_COND_LEU: case TCG_COND_EQ: return 1; - default: - tcg_abort(); + case TCG_COND_TSTEQ: + case TCG_COND_TSTNE: + return -1; + case TCG_COND_ALWAYS: + case TCG_COND_NEVER: + break; } + g_assert_not_reached(); } /* @@ -567,11 +724,13 @@ static int do_constant_folding_cond(TCGType type, TCGArg x, } } else if (args_are_copies(x, y)) { return do_constant_folding_cond_eq(c); - } else if (arg_is_const(y) && arg_info(y)->val == 0) { + } else if (arg_is_const_val(y, 0)) { switch (c) { case TCG_COND_LTU: + case TCG_COND_TSTNE: return 0; case TCG_COND_GEU: + case TCG_COND_TSTEQ: return 1; default: return -1; @@ -580,43 +739,6 @@ static int do_constant_folding_cond(TCGType type, TCGArg x, return -1; } -/* - * Return -1 if the condition can't be simplified, - * and the result of the condition (0 or 1) if it can. - */ -static int do_constant_folding_cond2(TCGArg *p1, TCGArg *p2, TCGCond c) -{ - TCGArg al = p1[0], ah = p1[1]; - TCGArg bl = p2[0], bh = p2[1]; - - if (arg_is_const(bl) && arg_is_const(bh)) { - tcg_target_ulong blv = arg_info(bl)->val; - tcg_target_ulong bhv = arg_info(bh)->val; - uint64_t b = deposit64(blv, 32, 32, bhv); - - if (arg_is_const(al) && arg_is_const(ah)) { - tcg_target_ulong alv = arg_info(al)->val; - tcg_target_ulong ahv = arg_info(ah)->val; - uint64_t a = deposit64(alv, 32, 32, ahv); - return do_constant_folding_cond_64(a, b, c); - } - if (b == 0) { - switch (c) { - case TCG_COND_LTU: - return 0; - case TCG_COND_GEU: - return 1; - default: - break; - } - } - } - if (args_are_copies(al, bl) && args_are_copies(ah, bh)) { - return do_constant_folding_cond_eq(c); - } - return -1; -} - /** * swap_commutative: * @dest: TCGArg of the destination argument, or NO_DEST. @@ -663,25 +785,181 @@ static bool swap_commutative2(TCGArg *p1, TCGArg *p2) return false; } +/* + * Return -1 if the condition can't be simplified, + * and the result of the condition (0 or 1) if it can. + */ +static int do_constant_folding_cond1(OptContext *ctx, TCGOp *op, TCGArg dest, + TCGArg *p1, TCGArg *p2, TCGArg *pcond) +{ + TCGCond cond; + bool swap; + int r; + + swap = swap_commutative(dest, p1, p2); + cond = *pcond; + if (swap) { + *pcond = cond = tcg_swap_cond(cond); + } + + r = do_constant_folding_cond(ctx->type, *p1, *p2, cond); + if (r >= 0) { + return r; + } + if (!is_tst_cond(cond)) { + return -1; + } + + /* + * TSTNE x,x -> NE x,0 + * TSTNE x,-1 -> NE x,0 + */ + if (args_are_copies(*p1, *p2) || arg_is_const_val(*p2, -1)) { + *p2 = arg_new_constant(ctx, 0); + *pcond = tcg_tst_eqne_cond(cond); + return -1; + } + + /* TSTNE x,sign -> LT x,0 */ + if (arg_is_const_val(*p2, (ctx->type == TCG_TYPE_I32 + ? INT32_MIN : INT64_MIN))) { + *p2 = arg_new_constant(ctx, 0); + *pcond = tcg_tst_ltge_cond(cond); + return -1; + } + + /* Expand to AND with a temporary if no backend support. */ + if (!TCG_TARGET_HAS_tst) { + TCGOpcode and_opc = (ctx->type == TCG_TYPE_I32 + ? INDEX_op_and_i32 : INDEX_op_and_i64); + TCGOp *op2 = tcg_op_insert_before(ctx->tcg, op, and_opc, 3); + TCGArg tmp = arg_new_temp(ctx); + + op2->args[0] = tmp; + op2->args[1] = *p1; + op2->args[2] = *p2; + + *p1 = tmp; + *p2 = arg_new_constant(ctx, 0); + *pcond = tcg_tst_eqne_cond(cond); + } + return -1; +} + +static int do_constant_folding_cond2(OptContext *ctx, TCGOp *op, TCGArg *args) +{ + TCGArg al, ah, bl, bh; + TCGCond c; + bool swap; + int r; + + swap = swap_commutative2(args, args + 2); + c = args[4]; + if (swap) { + args[4] = c = tcg_swap_cond(c); + } + + al = args[0]; + ah = args[1]; + bl = args[2]; + bh = args[3]; + + if (arg_is_const(bl) && arg_is_const(bh)) { + tcg_target_ulong blv = arg_info(bl)->val; + tcg_target_ulong bhv = arg_info(bh)->val; + uint64_t b = deposit64(blv, 32, 32, bhv); + + if (arg_is_const(al) && arg_is_const(ah)) { + tcg_target_ulong alv = arg_info(al)->val; + tcg_target_ulong ahv = arg_info(ah)->val; + uint64_t a = deposit64(alv, 32, 32, ahv); + + r = do_constant_folding_cond_64(a, b, c); + if (r >= 0) { + return r; + } + } + + if (b == 0) { + switch (c) { + case TCG_COND_LTU: + case TCG_COND_TSTNE: + return 0; + case TCG_COND_GEU: + case TCG_COND_TSTEQ: + return 1; + default: + break; + } + } + + /* TSTNE x,-1 -> NE x,0 */ + if (b == -1 && is_tst_cond(c)) { + args[3] = args[2] = arg_new_constant(ctx, 0); + args[4] = tcg_tst_eqne_cond(c); + return -1; + } + + /* TSTNE x,sign -> LT x,0 */ + if (b == INT64_MIN && is_tst_cond(c)) { + /* bl must be 0, so copy that to bh */ + args[3] = bl; + args[4] = tcg_tst_ltge_cond(c); + return -1; + } + } + + if (args_are_copies(al, bl) && args_are_copies(ah, bh)) { + r = do_constant_folding_cond_eq(c); + if (r >= 0) { + return r; + } + + /* TSTNE x,x -> NE x,0 */ + if (is_tst_cond(c)) { + args[3] = args[2] = arg_new_constant(ctx, 0); + args[4] = tcg_tst_eqne_cond(c); + return -1; + } + } + + /* Expand to AND with a temporary if no backend support. */ + if (!TCG_TARGET_HAS_tst && is_tst_cond(c)) { + TCGOp *op1 = tcg_op_insert_before(ctx->tcg, op, INDEX_op_and_i32, 3); + TCGOp *op2 = tcg_op_insert_before(ctx->tcg, op, INDEX_op_and_i32, 3); + TCGArg t1 = arg_new_temp(ctx); + TCGArg t2 = arg_new_temp(ctx); + + op1->args[0] = t1; + op1->args[1] = al; + op1->args[2] = bl; + op2->args[0] = t2; + op2->args[1] = ah; + op2->args[2] = bh; + + args[0] = t1; + args[1] = t2; + args[3] = args[2] = arg_new_constant(ctx, 0); + args[4] = tcg_tst_eqne_cond(c); + } + return -1; +} + static void init_arguments(OptContext *ctx, TCGOp *op, int nb_args) { for (int i = 0; i < nb_args; i++) { TCGTemp *ts = arg_temp(op->args[i]); - if (ts) { - init_ts_info(ctx, ts); - } + init_ts_info(ctx, ts); } } static void copy_propagate(OptContext *ctx, TCGOp *op, int nb_oargs, int nb_iargs) { - TCGContext *s = ctx->tcg; - for (int i = nb_oargs; i < nb_oargs + nb_iargs; i++) { TCGTemp *ts = arg_temp(op->args[i]); - if (ts && ts_is_copy(ts)) { - op->args[i] = temp_arg(find_better_copy(s, ts)); + if (ts_is_copy(ts)) { + op->args[i] = temp_arg(find_better_copy(ts)); } } } @@ -692,19 +970,22 @@ static void finish_folding(OptContext *ctx, TCGOp *op) int i, nb_oargs; /* - * For an opcode that ends a BB, reset all temp data. - * We do no cross-BB optimization. + * We only optimize extended basic blocks. If the opcode ends a BB + * and is not a conditional branch, reset all temp data. */ if (def->flags & TCG_OPF_BB_END) { - memset(&ctx->temps_used, 0, sizeof(ctx->temps_used)); ctx->prev_mb = NULL; + if (!(def->flags & TCG_OPF_COND_BRANCH)) { + memset(&ctx->temps_used, 0, sizeof(ctx->temps_used)); + remove_mem_copy_all(ctx); + } return; } nb_oargs = def->nb_oargs; for (i = 0; i < nb_oargs; i++) { TCGTemp *ts = arg_temp(op->args[i]); - reset_ts(ts); + reset_ts(ctx, ts); /* * Save the corresponding known-zero/sign bits mask for the * first output argument (only one supported so far). @@ -833,7 +1114,7 @@ static bool fold_to_not(OptContext *ctx, TCGOp *op, int idx) /* If the binary operation has first argument @i, fold to @i. */ static bool fold_ix_to_i(OptContext *ctx, TCGOp *op, uint64_t i) { - if (arg_is_const(op->args[1]) && arg_info(op->args[1])->val == i) { + if (arg_is_const_val(op->args[1], i)) { return tcg_opt_gen_movi(ctx, op, op->args[0], i); } return false; @@ -842,7 +1123,7 @@ static bool fold_ix_to_i(OptContext *ctx, TCGOp *op, uint64_t i) /* If the binary operation has first argument @i, fold to NOT. */ static bool fold_ix_to_not(OptContext *ctx, TCGOp *op, uint64_t i) { - if (arg_is_const(op->args[1]) && arg_info(op->args[1])->val == i) { + if (arg_is_const_val(op->args[1], i)) { return fold_to_not(ctx, op, 2); } return false; @@ -851,7 +1132,7 @@ static bool fold_ix_to_not(OptContext *ctx, TCGOp *op, uint64_t i) /* If the binary operation has second argument @i, fold to @i. */ static bool fold_xi_to_i(OptContext *ctx, TCGOp *op, uint64_t i) { - if (arg_is_const(op->args[2]) && arg_info(op->args[2])->val == i) { + if (arg_is_const_val(op->args[2], i)) { return tcg_opt_gen_movi(ctx, op, op->args[0], i); } return false; @@ -860,7 +1141,7 @@ static bool fold_xi_to_i(OptContext *ctx, TCGOp *op, uint64_t i) /* If the binary operation has second argument @i, fold to identity. */ static bool fold_xi_to_x(OptContext *ctx, TCGOp *op, uint64_t i) { - if (arg_is_const(op->args[2]) && arg_info(op->args[2])->val == i) { + if (arg_is_const_val(op->args[2], i)) { return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); } return false; @@ -869,7 +1150,7 @@ static bool fold_xi_to_x(OptContext *ctx, TCGOp *op, uint64_t i) /* If the binary operation has second argument @i, fold to NOT. */ static bool fold_xi_to_not(OptContext *ctx, TCGOp *op, uint64_t i) { - if (arg_is_const(op->args[2]) && arg_info(op->args[2])->val == i) { + if (arg_is_const_val(op->args[2], i)) { return fold_to_not(ctx, op, 1); } return false; @@ -923,8 +1204,10 @@ static bool fold_add_vec(OptContext *ctx, TCGOp *op) static bool fold_addsub2(OptContext *ctx, TCGOp *op, bool add) { - if (arg_is_const(op->args[2]) && arg_is_const(op->args[3]) && - arg_is_const(op->args[4]) && arg_is_const(op->args[5])) { + bool a_const = arg_is_const(op->args[2]) && arg_is_const(op->args[3]); + bool b_const = arg_is_const(op->args[4]) && arg_is_const(op->args[5]); + + if (a_const && b_const) { uint64_t al = arg_info(op->args[2])->val; uint64_t ah = arg_info(op->args[3])->val; uint64_t bl = arg_info(op->args[4])->val; @@ -962,12 +1245,27 @@ static bool fold_addsub2(OptContext *ctx, TCGOp *op, bool add) rh = op->args[1]; /* The proper opcode is supplied by tcg_opt_gen_mov. */ - op2 = tcg_op_insert_before(ctx->tcg, op, 0); + op2 = tcg_op_insert_before(ctx->tcg, op, 0, 2); tcg_opt_gen_movi(ctx, op, rl, al); tcg_opt_gen_movi(ctx, op2, rh, ah); return true; } + + /* Fold sub2 r,x,i to add2 r,x,-i */ + if (!add && b_const) { + uint64_t bl = arg_info(op->args[4])->val; + uint64_t bh = arg_info(op->args[5])->val; + + /* Negate the two parts without assembling and disassembling. */ + bl = -bl; + bh = ~bh + !bl; + + op->opc = (ctx->type == TCG_TYPE_I32 + ? INDEX_op_add2_i32 : INDEX_op_add2_i64); + op->args[4] = arg_new_constant(ctx, bl); + op->args[5] = arg_new_constant(ctx, bh); + } return false; } @@ -1044,14 +1342,8 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) static bool fold_brcond(OptContext *ctx, TCGOp *op) { - TCGCond cond = op->args[2]; - int i; - - if (swap_commutative(NO_DEST, &op->args[0], &op->args[1])) { - op->args[2] = cond = tcg_swap_cond(cond); - } - - i = do_constant_folding_cond(ctx->type, op->args[0], op->args[1], cond); + int i = do_constant_folding_cond1(ctx, op, NO_DEST, &op->args[0], + &op->args[1], &op->args[2]); if (i == 0) { tcg_op_remove(ctx->tcg, op); return true; @@ -1065,15 +1357,13 @@ static bool fold_brcond(OptContext *ctx, TCGOp *op) static bool fold_brcond2(OptContext *ctx, TCGOp *op) { - TCGCond cond = op->args[4]; - TCGArg label = op->args[5]; + TCGCond cond; + TCGArg label; int i, inv = 0; - if (swap_commutative2(&op->args[0], &op->args[2])) { - op->args[4] = cond = tcg_swap_cond(cond); - } - - i = do_constant_folding_cond2(&op->args[0], &op->args[2], cond); + i = do_constant_folding_cond2(ctx, op, &op->args[0]); + cond = op->args[4]; + label = op->args[5]; if (i >= 0) { goto do_brcond_const; } @@ -1085,8 +1375,8 @@ static bool fold_brcond2(OptContext *ctx, TCGOp *op) * Simplify LT/GE comparisons vs zero to a single compare * vs the high word of the input. */ - if (arg_is_const(op->args[2]) && arg_info(op->args[2])->val == 0 && - arg_is_const(op->args[3]) && arg_info(op->args[3])->val == 0) { + if (arg_is_const_val(op->args[2], 0) && + arg_is_const_val(op->args[3], 0)) { goto do_brcond_high; } break; @@ -1114,24 +1404,37 @@ static bool fold_brcond2(OptContext *ctx, TCGOp *op) case 0: goto do_brcond_const; case 1: - op->opc = INDEX_op_brcond_i32; - op->args[1] = op->args[2]; - op->args[2] = cond; - op->args[3] = label; - break; + goto do_brcond_low; + } + break; + + case TCG_COND_TSTEQ: + case TCG_COND_TSTNE: + if (arg_is_const_val(op->args[2], 0)) { + goto do_brcond_high; + } + if (arg_is_const_val(op->args[3], 0)) { + goto do_brcond_low; } break; default: break; + do_brcond_low: + op->opc = INDEX_op_brcond_i32; + op->args[1] = op->args[2]; + op->args[2] = cond; + op->args[3] = label; + return fold_brcond(ctx, op); + do_brcond_high: op->opc = INDEX_op_brcond_i32; op->args[0] = op->args[1]; op->args[1] = op->args[3]; op->args[2] = cond; op->args[3] = label; - break; + return fold_brcond(ctx, op); do_brcond_const: if (i == 0) { @@ -1217,14 +1520,19 @@ static bool fold_call(OptContext *ctx, TCGOp *op) for (i = 0; i < nb_globals; i++) { if (test_bit(i, ctx->temps_used.l)) { - reset_ts(&ctx->tcg->temps[i]); + reset_ts(ctx, &ctx->tcg->temps[i]); } } } + /* If the function has side effects, reset mem data. */ + if (!(flags & TCG_CALL_NO_SIDE_EFFECTS)) { + remove_mem_copy_all(ctx); + } + /* Reset temp data for outputs. */ for (i = 0; i < nb_oargs; i++) { - reset_temp(op->args[i]); + reset_temp(ctx, op->args[i]); } /* Stop optimizing MB across calls. */ @@ -1283,6 +1591,8 @@ static bool fold_ctpop(OptContext *ctx, TCGOp *op) static bool fold_deposit(OptContext *ctx, TCGOp *op) { + TCGOpcode and_opc; + if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { uint64_t t1 = arg_info(op->args[1])->val; uint64_t t2 = arg_info(op->args[2])->val; @@ -1291,6 +1601,38 @@ static bool fold_deposit(OptContext *ctx, TCGOp *op) return tcg_opt_gen_movi(ctx, op, op->args[0], t1); } + switch (ctx->type) { + case TCG_TYPE_I32: + and_opc = INDEX_op_and_i32; + break; + case TCG_TYPE_I64: + and_opc = INDEX_op_and_i64; + break; + default: + g_assert_not_reached(); + } + + /* Inserting a value into zero at offset 0. */ + if (arg_is_const_val(op->args[1], 0) && op->args[3] == 0) { + uint64_t mask = MAKE_64BIT_MASK(0, op->args[4]); + + op->opc = and_opc; + op->args[1] = op->args[2]; + op->args[2] = arg_new_constant(ctx, mask); + ctx->z_mask = mask & arg_info(op->args[1])->z_mask; + return false; + } + + /* Inserting zero into a value. */ + if (arg_is_const_val(op->args[2], 0)) { + uint64_t mask = deposit64(-1, op->args[3], op->args[4], 0); + + op->opc = and_opc; + op->args[2] = arg_new_constant(ctx, mask); + ctx->z_mask = mask & arg_info(op->args[1])->z_mask; + return false; + } + ctx->z_mask = deposit64(arg_info(op->args[1])->z_mask, op->args[3], op->args[4], arg_info(op->args[2])->z_mask); @@ -1507,21 +1849,18 @@ static bool fold_mov(OptContext *ctx, TCGOp *op) static bool fold_movcond(OptContext *ctx, TCGOp *op) { - TCGCond cond = op->args[5]; int i; - if (swap_commutative(NO_DEST, &op->args[1], &op->args[2])) { - op->args[5] = cond = tcg_swap_cond(cond); - } /* * Canonicalize the "false" input reg to match the destination reg so * that the tcg backend can implement a "move if true" operation. */ if (swap_commutative(op->args[0], &op->args[4], &op->args[3])) { - op->args[5] = cond = tcg_invert_cond(cond); + op->args[5] = tcg_invert_cond(op->args[5]); } - i = do_constant_folding_cond(ctx->type, op->args[1], op->args[2], cond); + i = do_constant_folding_cond1(ctx, op, NO_DEST, &op->args[1], + &op->args[2], &op->args[5]); if (i >= 0) { return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[4 - i]); } @@ -1534,14 +1873,23 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) if (arg_is_const(op->args[3]) && arg_is_const(op->args[4])) { uint64_t tv = arg_info(op->args[3])->val; uint64_t fv = arg_info(op->args[4])->val; - TCGOpcode opc; + TCGOpcode opc, negopc = 0; + TCGCond cond = op->args[5]; switch (ctx->type) { case TCG_TYPE_I32: opc = INDEX_op_setcond_i32; + if (TCG_TARGET_HAS_negsetcond_i32) { + negopc = INDEX_op_negsetcond_i32; + } + tv = (int32_t)tv; + fv = (int32_t)fv; break; case TCG_TYPE_I64: opc = INDEX_op_setcond_i64; + if (TCG_TARGET_HAS_negsetcond_i64) { + negopc = INDEX_op_negsetcond_i64; + } break; default: g_assert_not_reached(); @@ -1553,6 +1901,14 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) } else if (fv == 1 && tv == 0) { op->opc = opc; op->args[3] = tcg_invert_cond(cond); + } else if (negopc) { + if (tv == -1 && fv == 0) { + op->opc = negopc; + op->args[3] = cond; + } else if (fv == -1 && tv == 0) { + op->opc = negopc; + op->args[3] = tcg_invert_cond(cond); + } } } return false; @@ -1613,7 +1969,7 @@ static bool fold_multiply2(OptContext *ctx, TCGOp *op) rh = op->args[1]; /* The proper opcode is supplied by tcg_opt_gen_mov. */ - op2 = tcg_op_insert_before(ctx->tcg, op, 0); + op2 = tcg_op_insert_before(ctx->tcg, op, 0, 2); tcg_opt_gen_movi(ctx, op, rl, l); tcg_opt_gen_movi(ctx, op2, rh, h); @@ -1634,16 +1990,10 @@ static bool fold_nand(OptContext *ctx, TCGOp *op) return false; } -static bool fold_neg(OptContext *ctx, TCGOp *op) +static bool fold_neg_no_const(OptContext *ctx, TCGOp *op) { - uint64_t z_mask; - - if (fold_const1(ctx, op)) { - return true; - } - /* Set to 1 all bits to the left of the rightmost. */ - z_mask = arg_info(op->args[1])->z_mask; + uint64_t z_mask = arg_info(op->args[1])->z_mask; ctx->z_mask = -(z_mask & -z_mask); /* @@ -1654,6 +2004,11 @@ static bool fold_neg(OptContext *ctx, TCGOp *op) return true; } +static bool fold_neg(OptContext *ctx, TCGOp *op) +{ + return fold_const1(ctx, op) || fold_neg_no_const(ctx, op); +} + static bool fold_nor(OptContext *ctx, TCGOp *op) { if (fold_const2_commutative(ctx, op) || @@ -1744,35 +2099,135 @@ static bool fold_remainder(OptContext *ctx, TCGOp *op) return false; } -static bool fold_setcond(OptContext *ctx, TCGOp *op) +static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) { + TCGOpcode and_opc, sub_opc, xor_opc, neg_opc, shr_opc; + TCGOpcode uext_opc = 0, sext_opc = 0; TCGCond cond = op->args[3]; - int i; + TCGArg ret, src1, src2; + TCGOp *op2; + uint64_t val; + int sh; + bool inv; - if (swap_commutative(op->args[0], &op->args[1], &op->args[2])) { - op->args[3] = cond = tcg_swap_cond(cond); + if (!is_tst_cond(cond) || !arg_is_const(op->args[2])) { + return; } - i = do_constant_folding_cond(ctx->type, op->args[1], op->args[2], cond); + src2 = op->args[2]; + val = arg_info(src2)->val; + if (!is_power_of_2(val)) { + return; + } + sh = ctz64(val); + + switch (ctx->type) { + case TCG_TYPE_I32: + and_opc = INDEX_op_and_i32; + sub_opc = INDEX_op_sub_i32; + xor_opc = INDEX_op_xor_i32; + shr_opc = INDEX_op_shr_i32; + neg_opc = INDEX_op_neg_i32; + if (TCG_TARGET_extract_i32_valid(sh, 1)) { + uext_opc = TCG_TARGET_HAS_extract_i32 ? INDEX_op_extract_i32 : 0; + sext_opc = TCG_TARGET_HAS_sextract_i32 ? INDEX_op_sextract_i32 : 0; + } + break; + case TCG_TYPE_I64: + and_opc = INDEX_op_and_i64; + sub_opc = INDEX_op_sub_i64; + xor_opc = INDEX_op_xor_i64; + shr_opc = INDEX_op_shr_i64; + neg_opc = INDEX_op_neg_i64; + if (TCG_TARGET_extract_i64_valid(sh, 1)) { + uext_opc = TCG_TARGET_HAS_extract_i64 ? INDEX_op_extract_i64 : 0; + sext_opc = TCG_TARGET_HAS_sextract_i64 ? INDEX_op_sextract_i64 : 0; + } + break; + default: + g_assert_not_reached(); + } + + ret = op->args[0]; + src1 = op->args[1]; + inv = cond == TCG_COND_TSTEQ; + + if (sh && sext_opc && neg && !inv) { + op->opc = sext_opc; + op->args[1] = src1; + op->args[2] = sh; + op->args[3] = 1; + return; + } else if (sh && uext_opc) { + op->opc = uext_opc; + op->args[1] = src1; + op->args[2] = sh; + op->args[3] = 1; + } else { + if (sh) { + op2 = tcg_op_insert_before(ctx->tcg, op, shr_opc, 3); + op2->args[0] = ret; + op2->args[1] = src1; + op2->args[2] = arg_new_constant(ctx, sh); + src1 = ret; + } + op->opc = and_opc; + op->args[1] = src1; + op->args[2] = arg_new_constant(ctx, 1); + } + + if (neg && inv) { + op2 = tcg_op_insert_after(ctx->tcg, op, sub_opc, 3); + op2->args[0] = ret; + op2->args[1] = ret; + op2->args[2] = arg_new_constant(ctx, 1); + } else if (inv) { + op2 = tcg_op_insert_after(ctx->tcg, op, xor_opc, 3); + op2->args[0] = ret; + op2->args[1] = ret; + op2->args[2] = arg_new_constant(ctx, 1); + } else if (neg) { + op2 = tcg_op_insert_after(ctx->tcg, op, neg_opc, 2); + op2->args[0] = ret; + op2->args[1] = ret; + } +} + +static bool fold_setcond(OptContext *ctx, TCGOp *op) +{ + int i = do_constant_folding_cond1(ctx, op, op->args[0], &op->args[1], + &op->args[2], &op->args[3]); if (i >= 0) { return tcg_opt_gen_movi(ctx, op, op->args[0], i); } + fold_setcond_tst_pow2(ctx, op, false); ctx->z_mask = 1; ctx->s_mask = smask_from_zmask(1); return false; } +static bool fold_negsetcond(OptContext *ctx, TCGOp *op) +{ + int i = do_constant_folding_cond1(ctx, op, op->args[0], &op->args[1], + &op->args[2], &op->args[3]); + if (i >= 0) { + return tcg_opt_gen_movi(ctx, op, op->args[0], -i); + } + fold_setcond_tst_pow2(ctx, op, true); + + /* Value is {0,-1} so all bits are repetitions of the sign. */ + ctx->s_mask = -1; + return false; +} + static bool fold_setcond2(OptContext *ctx, TCGOp *op) { - TCGCond cond = op->args[5]; + TCGCond cond; int i, inv = 0; - if (swap_commutative2(&op->args[1], &op->args[3])) { - op->args[5] = cond = tcg_swap_cond(cond); - } - - i = do_constant_folding_cond2(&op->args[1], &op->args[3], cond); + i = do_constant_folding_cond2(ctx, op, &op->args[1]); + cond = op->args[5]; if (i >= 0) { goto do_setcond_const; } @@ -1784,8 +2239,8 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) * Simplify LT/GE comparisons vs zero to a single compare * vs the high word of the input. */ - if (arg_is_const(op->args[3]) && arg_info(op->args[3])->val == 0 && - arg_is_const(op->args[4]) && arg_info(op->args[4])->val == 0) { + if (arg_is_const_val(op->args[3], 0) && + arg_is_const_val(op->args[4], 0)) { goto do_setcond_high; } break; @@ -1813,22 +2268,35 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) case 0: goto do_setcond_const; case 1: - op->args[2] = op->args[3]; - op->args[3] = cond; - op->opc = INDEX_op_setcond_i32; - break; + goto do_setcond_low; + } + break; + + case TCG_COND_TSTEQ: + case TCG_COND_TSTNE: + if (arg_is_const_val(op->args[2], 0)) { + goto do_setcond_high; + } + if (arg_is_const_val(op->args[4], 0)) { + goto do_setcond_low; } break; default: break; + do_setcond_low: + op->args[2] = op->args[3]; + op->args[3] = cond; + op->opc = INDEX_op_setcond_i32; + return fold_setcond(ctx, op); + do_setcond_high: op->args[1] = op->args[2]; op->args[2] = op->args[4]; op->args[3] = cond; op->opc = INDEX_op_setcond_i32; - break; + return fold_setcond(ctx, op); } ctx->z_mask = 1; @@ -1907,7 +2375,7 @@ static bool fold_shift(OptContext *ctx, TCGOp *op) * will not reduced the number of input sign repetitions. */ sign = (s_mask & -s_mask) >> 1; - if (!(z_mask & sign)) { + if (sign && !(z_mask & sign)) { ctx->s_mask = s_mask; } break; @@ -1930,11 +2398,11 @@ static bool fold_sub_to_neg(OptContext *ctx, TCGOp *op) switch (ctx->type) { case TCG_TYPE_I32: neg_op = INDEX_op_neg_i32; - have_neg = TCG_TARGET_HAS_neg_i32; + have_neg = true; break; case TCG_TYPE_I64: neg_op = INDEX_op_neg_i64; - have_neg = TCG_TARGET_HAS_neg_i64; + have_neg = true; break; case TCG_TYPE_V64: case TCG_TYPE_V128: @@ -1949,7 +2417,7 @@ static bool fold_sub_to_neg(OptContext *ctx, TCGOp *op) if (have_neg) { op->opc = neg_op; op->args[1] = op->args[2]; - return fold_neg(ctx, op); + return fold_neg_no_const(ctx, op); } return false; } @@ -1967,7 +2435,19 @@ static bool fold_sub_vec(OptContext *ctx, TCGOp *op) static bool fold_sub(OptContext *ctx, TCGOp *op) { - return fold_const2(ctx, op) || fold_sub_vec(ctx, op); + if (fold_const2(ctx, op) || fold_sub_vec(ctx, op)) { + return true; + } + + /* Fold sub r,x,i to add r,x,-i */ + if (arg_is_const(op->args[2])) { + uint64_t val = arg_info(op->args[2])->val; + + op->opc = (ctx->type == TCG_TYPE_I32 + ? INDEX_op_add_i32 : INDEX_op_add_i64); + op->args[2] = arg_new_constant(ctx, -val); + } + return false; } static bool fold_sub2(OptContext *ctx, TCGOp *op) @@ -2006,6 +2486,96 @@ static bool fold_tcg_ld(OptContext *ctx, TCGOp *op) return false; } +static bool fold_tcg_ld_memcopy(OptContext *ctx, TCGOp *op) +{ + TCGTemp *dst, *src; + intptr_t ofs; + TCGType type; + + if (op->args[1] != tcgv_ptr_arg(tcg_env)) { + return false; + } + + type = ctx->type; + ofs = op->args[2]; + dst = arg_temp(op->args[0]); + src = find_mem_copy_for(ctx, type, ofs); + if (src && src->base_type == type) { + return tcg_opt_gen_mov(ctx, op, temp_arg(dst), temp_arg(src)); + } + + reset_ts(ctx, dst); + record_mem_copy(ctx, type, dst, ofs, ofs + tcg_type_size(type) - 1); + return true; +} + +static bool fold_tcg_st(OptContext *ctx, TCGOp *op) +{ + intptr_t ofs = op->args[2]; + intptr_t lm1; + + if (op->args[1] != tcgv_ptr_arg(tcg_env)) { + remove_mem_copy_all(ctx); + return false; + } + + switch (op->opc) { + CASE_OP_32_64(st8): + lm1 = 0; + break; + CASE_OP_32_64(st16): + lm1 = 1; + break; + case INDEX_op_st32_i64: + case INDEX_op_st_i32: + lm1 = 3; + break; + case INDEX_op_st_i64: + lm1 = 7; + break; + case INDEX_op_st_vec: + lm1 = tcg_type_size(ctx->type) - 1; + break; + default: + g_assert_not_reached(); + } + remove_mem_copy_in(ctx, ofs, ofs + lm1); + return false; +} + +static bool fold_tcg_st_memcopy(OptContext *ctx, TCGOp *op) +{ + TCGTemp *src; + intptr_t ofs, last; + TCGType type; + + if (op->args[1] != tcgv_ptr_arg(tcg_env)) { + fold_tcg_st(ctx, op); + return false; + } + + src = arg_temp(op->args[0]); + ofs = op->args[2]; + type = ctx->type; + + /* + * Eliminate duplicate stores of a constant. + * This happens frequently when the target ISA zero-extends. + */ + if (ts_is_const(src)) { + TCGTemp *prev = find_mem_copy_for(ctx, type, ofs); + if (src == prev) { + tcg_op_remove(ctx->tcg, op); + return true; + } + } + + last = ofs + tcg_type_size(type) - 1; + remove_mem_copy_in(ctx, ofs, last); + record_mem_copy(ctx, type, src, ofs, last); + return false; +} + static bool fold_xor(OptContext *ctx, TCGOp *op) { if (fold_const2_commutative(ctx, op) || @@ -2029,6 +2599,8 @@ void tcg_optimize(TCGContext *s) TCGOp *op, *op_next; OptContext ctx = { .tcg = s }; + QSIMPLEQ_INIT(&ctx.mem_free); + /* Array VALS has an element for each temp. If this temp holds a constant then its value is kept in VALS' element. If this temp is a copy of other ones then the other copies are @@ -2150,6 +2722,21 @@ void tcg_optimize(TCGContext *s) case INDEX_op_ld32u_i64: done = fold_tcg_ld(&ctx, op); break; + case INDEX_op_ld_i32: + case INDEX_op_ld_i64: + case INDEX_op_ld_vec: + done = fold_tcg_ld_memcopy(&ctx, op); + break; + CASE_OP_32_64(st8): + CASE_OP_32_64(st16): + case INDEX_op_st32_i64: + done = fold_tcg_st(&ctx, op); + break; + case INDEX_op_st_i32: + case INDEX_op_st_i64: + case INDEX_op_st_vec: + done = fold_tcg_st_memcopy(&ctx, op); + break; case INDEX_op_mb: done = fold_mb(&ctx, op); break; @@ -2188,13 +2775,22 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64_VEC(orc): done = fold_orc(&ctx, op); break; - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: done = fold_qemu_ld(&ctx, op); break; - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st8_i32: - case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_st8_a32_i32: + case INDEX_op_qemu_st8_a64_i32: + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: done = fold_qemu_st(&ctx, op); break; CASE_OP_32_64(rem): @@ -2211,6 +2807,9 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(setcond): done = fold_setcond(&ctx, op); break; + CASE_OP_32_64(negsetcond): + done = fold_negsetcond(&ctx, op); + break; case INDEX_op_setcond2_i32: done = fold_setcond2(&ctx, op); break; diff --git a/tcg/perf.c b/tcg/perf.c new file mode 100644 index 0000000000..412a987d95 --- /dev/null +++ b/tcg/perf.c @@ -0,0 +1,382 @@ +/* + * Linux perf perf-.map and jit-.dump integration. + * + * The jitdump spec can be found at [1]. + * + * [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/tools/perf/Documentation/jitdump-specification.txt + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "elf.h" +#include "exec/target_page.h" +#include "exec/translation-block.h" +#include "qemu/timer.h" +#include "tcg/debuginfo.h" +#include "tcg/perf.h" +#include "tcg/tcg.h" + +static FILE *safe_fopen_w(const char *path) +{ + int saved_errno; + FILE *f; + int fd; + + /* Delete the old file, if any. */ + unlink(path); + + /* Avoid symlink attacks by using O_CREAT | O_EXCL. */ + fd = open(path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + if (fd == -1) { + return NULL; + } + + /* Convert fd to FILE*. */ + f = fdopen(fd, "w"); + if (f == NULL) { + saved_errno = errno; + close(fd); + errno = saved_errno; + return NULL; + } + + return f; +} + +static FILE *perfmap; + +void perf_enable_perfmap(void) +{ + char map_file[32]; + + snprintf(map_file, sizeof(map_file), "/tmp/perf-%d.map", getpid()); + perfmap = safe_fopen_w(map_file); + if (perfmap == NULL) { + warn_report("Could not open %s: %s, proceeding without perfmap", + map_file, strerror(errno)); + } +} + +/* Get PC and size of code JITed for guest instruction #INSN. */ +static void get_host_pc_size(uintptr_t *host_pc, uint16_t *host_size, + const void *start, size_t insn) +{ + uint16_t start_off = insn ? tcg_ctx->gen_insn_end_off[insn - 1] : 0; + + if (host_pc) { + *host_pc = (uintptr_t)start + start_off; + } + if (host_size) { + *host_size = tcg_ctx->gen_insn_end_off[insn] - start_off; + } +} + +static const char *pretty_symbol(const struct debuginfo_query *q, size_t *len) +{ + static __thread char buf[64]; + int tmp; + + if (!q->symbol) { + tmp = snprintf(buf, sizeof(buf), "guest-0x%"PRIx64, q->address); + if (len) { + *len = MIN(tmp + 1, sizeof(buf)); + } + return buf; + } + + if (!q->offset) { + if (len) { + *len = strlen(q->symbol) + 1; + } + return q->symbol; + } + + tmp = snprintf(buf, sizeof(buf), "%s+0x%"PRIx64, q->symbol, q->offset); + if (len) { + *len = MIN(tmp + 1, sizeof(buf)); + } + return buf; +} + +static void write_perfmap_entry(const void *start, size_t insn, + const struct debuginfo_query *q) +{ + uint16_t host_size; + uintptr_t host_pc; + + get_host_pc_size(&host_pc, &host_size, start, insn); + fprintf(perfmap, "%"PRIxPTR" %"PRIx16" %s\n", + host_pc, host_size, pretty_symbol(q, NULL)); +} + +static FILE *jitdump; +static size_t perf_marker_size; +static void *perf_marker = MAP_FAILED; + +#define JITHEADER_MAGIC 0x4A695444 +#define JITHEADER_VERSION 1 + +struct jitheader { + uint32_t magic; + uint32_t version; + uint32_t total_size; + uint32_t elf_mach; + uint32_t pad1; + uint32_t pid; + uint64_t timestamp; + uint64_t flags; +}; + +enum jit_record_type { + JIT_CODE_LOAD = 0, + JIT_CODE_DEBUG_INFO = 2, +}; + +struct jr_prefix { + uint32_t id; + uint32_t total_size; + uint64_t timestamp; +}; + +struct jr_code_load { + struct jr_prefix p; + + uint32_t pid; + uint32_t tid; + uint64_t vma; + uint64_t code_addr; + uint64_t code_size; + uint64_t code_index; +}; + +struct debug_entry { + uint64_t addr; + int lineno; + int discrim; + const char name[]; +}; + +struct jr_code_debug_info { + struct jr_prefix p; + + uint64_t code_addr; + uint64_t nr_entry; + struct debug_entry entries[]; +}; + +static uint32_t get_e_machine(void) +{ + Elf64_Ehdr elf_header; + FILE *exe; + size_t n; + + QEMU_BUILD_BUG_ON(offsetof(Elf32_Ehdr, e_machine) != + offsetof(Elf64_Ehdr, e_machine)); + + exe = fopen("/proc/self/exe", "r"); + if (exe == NULL) { + return EM_NONE; + } + + n = fread(&elf_header, sizeof(elf_header), 1, exe); + fclose(exe); + if (n != 1) { + return EM_NONE; + } + + return elf_header.e_machine; +} + +void perf_enable_jitdump(void) +{ + struct jitheader header; + char jitdump_file[32]; + + if (!use_rt_clock) { + warn_report("CLOCK_MONOTONIC is not available, proceeding without jitdump"); + return; + } + + snprintf(jitdump_file, sizeof(jitdump_file), "jit-%d.dump", getpid()); + jitdump = safe_fopen_w(jitdump_file); + if (jitdump == NULL) { + warn_report("Could not open %s: %s, proceeding without jitdump", + jitdump_file, strerror(errno)); + return; + } + + /* + * `perf inject` will see that the mapped file name in the corresponding + * PERF_RECORD_MMAP or PERF_RECORD_MMAP2 event is of the form jit-%d.dump + * and will process it as a jitdump file. + */ + perf_marker_size = qemu_real_host_page_size(); + perf_marker = mmap(NULL, perf_marker_size, PROT_READ | PROT_EXEC, + MAP_PRIVATE, fileno(jitdump), 0); + if (perf_marker == MAP_FAILED) { + warn_report("Could not map %s: %s, proceeding without jitdump", + jitdump_file, strerror(errno)); + fclose(jitdump); + jitdump = NULL; + return; + } + + header.magic = JITHEADER_MAGIC; + header.version = JITHEADER_VERSION; + header.total_size = sizeof(header); + header.elf_mach = get_e_machine(); + header.pad1 = 0; + header.pid = getpid(); + header.timestamp = get_clock(); + header.flags = 0; + fwrite(&header, sizeof(header), 1, jitdump); +} + +void perf_report_prologue(const void *start, size_t size) +{ + if (perfmap) { + fprintf(perfmap, "%"PRIxPTR" %zx tcg-prologue-buffer\n", + (uintptr_t)start, size); + } +} + +/* Write a JIT_CODE_DEBUG_INFO jitdump entry. */ +static void write_jr_code_debug_info(const void *start, + const struct debuginfo_query *q, + size_t icount) +{ + struct jr_code_debug_info rec; + struct debug_entry ent; + uintptr_t host_pc; + int insn; + + /* Write the header. */ + rec.p.id = JIT_CODE_DEBUG_INFO; + rec.p.total_size = sizeof(rec) + sizeof(ent) + 1; + rec.p.timestamp = get_clock(); + rec.code_addr = (uintptr_t)start; + rec.nr_entry = 1; + for (insn = 0; insn < icount; insn++) { + if (q[insn].file) { + rec.p.total_size += sizeof(ent) + strlen(q[insn].file) + 1; + rec.nr_entry++; + } + } + fwrite(&rec, sizeof(rec), 1, jitdump); + + /* Write the main debug entries. */ + for (insn = 0; insn < icount; insn++) { + if (q[insn].file) { + get_host_pc_size(&host_pc, NULL, start, insn); + ent.addr = host_pc; + ent.lineno = q[insn].line; + ent.discrim = 0; + fwrite(&ent, sizeof(ent), 1, jitdump); + fwrite(q[insn].file, strlen(q[insn].file) + 1, 1, jitdump); + } + } + + /* Write the trailing debug_entry. */ + ent.addr = (uintptr_t)start + tcg_ctx->gen_insn_end_off[icount - 1]; + ent.lineno = 0; + ent.discrim = 0; + fwrite(&ent, sizeof(ent), 1, jitdump); + fwrite("", 1, 1, jitdump); +} + +/* Write a JIT_CODE_LOAD jitdump entry. */ +static void write_jr_code_load(const void *start, uint16_t host_size, + const struct debuginfo_query *q) +{ + static uint64_t code_index; + struct jr_code_load rec; + const char *symbol; + size_t symbol_size; + + symbol = pretty_symbol(q, &symbol_size); + rec.p.id = JIT_CODE_LOAD; + rec.p.total_size = sizeof(rec) + symbol_size + host_size; + rec.p.timestamp = get_clock(); + rec.pid = getpid(); + rec.tid = qemu_get_thread_id(); + rec.vma = (uintptr_t)start; + rec.code_addr = (uintptr_t)start; + rec.code_size = host_size; + rec.code_index = code_index++; + fwrite(&rec, sizeof(rec), 1, jitdump); + fwrite(symbol, symbol_size, 1, jitdump); + fwrite(start, host_size, 1, jitdump); +} + +void perf_report_code(uint64_t guest_pc, TranslationBlock *tb, + const void *start) +{ + struct debuginfo_query *q; + size_t insn, start_words; + uint64_t *gen_insn_data; + + if (!perfmap && !jitdump) { + return; + } + + q = g_try_malloc0_n(tb->icount, sizeof(*q)); + if (!q) { + return; + } + + debuginfo_lock(); + + /* Query debuginfo for each guest instruction. */ + gen_insn_data = tcg_ctx->gen_insn_data; + start_words = tcg_ctx->insn_start_words; + + for (insn = 0; insn < tb->icount; insn++) { + /* FIXME: This replicates the restore_state_to_opc() logic. */ + q[insn].address = gen_insn_data[insn * start_words + 0]; + if (tb_cflags(tb) & CF_PCREL) { + q[insn].address |= (guest_pc & qemu_target_page_mask()); + } + q[insn].flags = DEBUGINFO_SYMBOL | (jitdump ? DEBUGINFO_LINE : 0); + } + debuginfo_query(q, tb->icount); + + /* Emit perfmap entries if needed. */ + if (perfmap) { + flockfile(perfmap); + for (insn = 0; insn < tb->icount; insn++) { + write_perfmap_entry(start, insn, &q[insn]); + } + funlockfile(perfmap); + } + + /* Emit jitdump entries if needed. */ + if (jitdump) { + flockfile(jitdump); + write_jr_code_debug_info(start, q, tb->icount); + write_jr_code_load(start, tcg_ctx->gen_insn_end_off[tb->icount - 1], + q); + funlockfile(jitdump); + } + + debuginfo_unlock(); + g_free(q); +} + +void perf_exit(void) +{ + if (perfmap) { + fclose(perfmap); + perfmap = NULL; + } + + if (perf_marker != MAP_FAILED) { + munmap(perf_marker, perf_marker_size); + perf_marker = MAP_FAILED; + } + + if (jitdump) { + fclose(jitdump); + jitdump = NULL; + } +} diff --git a/tcg/ppc/tcg-target-con-set.h b/tcg/ppc/tcg-target-con-set.h index a1a345883d..9f99bde505 100644 --- a/tcg/ppc/tcg-target-con-set.h +++ b/tcg/ppc/tcg-target-con-set.h @@ -11,32 +11,32 @@ */ C_O0_I1(r) C_O0_I2(r, r) -C_O0_I2(r, ri) -C_O0_I2(S, S) +C_O0_I2(r, rC) C_O0_I2(v, r) -C_O0_I3(S, S, S) +C_O0_I3(r, r, r) +C_O0_I3(o, m, r) C_O0_I4(r, r, ri, ri) -C_O0_I4(S, S, S, S) -C_O1_I1(r, L) +C_O0_I4(r, r, r, r) C_O1_I1(r, r) C_O1_I1(v, r) C_O1_I1(v, v) C_O1_I1(v, vr) C_O1_I2(r, 0, rZ) -C_O1_I2(r, L, L) C_O1_I2(r, rI, ri) C_O1_I2(r, rI, rT) C_O1_I2(r, r, r) C_O1_I2(r, r, ri) +C_O1_I2(r, r, rC) C_O1_I2(r, r, rI) C_O1_I2(r, r, rT) C_O1_I2(r, r, rU) C_O1_I2(r, r, rZW) C_O1_I2(v, v, v) C_O1_I3(v, v, v, v) -C_O1_I4(r, r, ri, rZ, rZ) +C_O1_I4(r, r, rC, rZ, rZ) C_O1_I4(r, r, r, ri, ri) -C_O2_I1(L, L, L) -C_O2_I2(L, L, L, L) +C_O2_I1(r, r, r) +C_N1O1_I1(o, m, r) +C_O2_I2(r, r, r, r) C_O2_I4(r, r, rI, rZM, r, r) C_O2_I4(r, r, r, r, rI, rZM) diff --git a/tcg/ppc/tcg-target-con-str.h b/tcg/ppc/tcg-target-con-str.h index 298ca20d5b..16b687216e 100644 --- a/tcg/ppc/tcg-target-con-str.h +++ b/tcg/ppc/tcg-target-con-str.h @@ -9,20 +9,15 @@ * REGS(letter, register_mask) */ REGS('r', ALL_GENERAL_REGS) +REGS('o', ALL_GENERAL_REGS & 0xAAAAAAAAu) /* odd registers */ REGS('v', ALL_VECTOR_REGS) -REGS('A', 1u << TCG_REG_R3) -REGS('B', 1u << TCG_REG_R4) -REGS('C', 1u << TCG_REG_R5) -REGS('D', 1u << TCG_REG_R6) -REGS('L', ALL_QLOAD_REGS) -REGS('S', ALL_QSTORE_REGS) /* * Define constraint letters for constants: * CONST(letter, TCG_CT_CONST_* bit set) */ +CONST('C', TCG_CT_CONST_CMP) CONST('I', TCG_CT_CONST_S16) -CONST('J', TCG_CT_CONST_U16) CONST('M', TCG_CT_CONST_MONE) CONST('T', TCG_CT_CONST_S32) CONST('U', TCG_CT_CONST_U32) diff --git a/tcg/ppc/tcg-target-reg-bits.h b/tcg/ppc/tcg-target-reg-bits.h new file mode 100644 index 0000000000..0efa80e7e0 --- /dev/null +++ b/tcg/ppc/tcg-target-reg-bits.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +#ifdef _ARCH_PPC64 +# define TCG_TARGET_REG_BITS 64 +#else +# define TCG_TARGET_REG_BITS 32 +#endif + +#endif diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index de4483e43b..7f3829beeb 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -29,23 +29,43 @@ /* * Standardize on the _CALL_FOO symbols used by GCC: * Apple XCode does not define _CALL_DARWIN. - * Clang defines _CALL_ELF (64-bit) but not _CALL_SYSV (32-bit). + * Clang defines _CALL_ELF (64-bit) but not _CALL_SYSV or _CALL_AIX. */ -#if !defined(_CALL_SYSV) && \ - !defined(_CALL_DARWIN) && \ - !defined(_CALL_AIX) && \ - !defined(_CALL_ELF) -# if defined(__APPLE__) +#if TCG_TARGET_REG_BITS == 64 +# ifdef _CALL_AIX + /* ok */ +# elif defined(_CALL_ELF) && _CALL_ELF == 1 +# define _CALL_AIX +# elif defined(_CALL_ELF) && _CALL_ELF == 2 + /* ok */ +# else +# error "Unknown ABI" +# endif +#else +# if defined(_CALL_SYSV) || defined(_CALL_DARWIN) + /* ok */ +# elif defined(__APPLE__) # define _CALL_DARWIN -# elif defined(__ELF__) && TCG_TARGET_REG_BITS == 32 +# elif defined(__ELF__) # define _CALL_SYSV # else # error "Unknown ABI" # endif -#endif +#endif +#if TCG_TARGET_REG_BITS == 64 +# define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_EXTEND +# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL +#else +# define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +# define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF +#endif #ifdef _CALL_SYSV -# define TCG_TARGET_CALL_ALIGN_ARGS 1 +# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_EVEN +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_BY_REF +#else +# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL #endif /* For some memory operations, we need a scratch that isn't R0. For the AIX @@ -57,12 +77,13 @@ #else # define TCG_REG_TMP1 TCG_REG_R12 #endif +#define TCG_REG_TMP2 TCG_REG_R11 #define TCG_VEC_TMP1 TCG_REG_V0 #define TCG_VEC_TMP2 TCG_REG_V1 #define TCG_REG_TB TCG_REG_R31 -#define USE_REG_TB (TCG_TARGET_REG_BITS == 64) +#define USE_REG_TB (TCG_TARGET_REG_BITS == 64 && !have_isa_3_00) /* Shorthand for size of a pointer. Avoid promotion to unsigned. */ #define SZP ((int)sizeof(void *)) @@ -77,30 +98,18 @@ #define TCG_CT_CONST_ZERO 0x1000 #define TCG_CT_CONST_MONE 0x2000 #define TCG_CT_CONST_WSZ 0x4000 +#define TCG_CT_CONST_CMP 0x8000 #define ALL_GENERAL_REGS 0xffffffffu #define ALL_VECTOR_REGS 0xffffffff00000000ull -#ifdef CONFIG_SOFTMMU -#define ALL_QLOAD_REGS \ - (ALL_GENERAL_REGS & \ - ~((1 << TCG_REG_R3) | (1 << TCG_REG_R4) | (1 << TCG_REG_R5))) -#define ALL_QSTORE_REGS \ - (ALL_GENERAL_REGS & ~((1 << TCG_REG_R3) | (1 << TCG_REG_R4) | \ - (1 << TCG_REG_R5) | (1 << TCG_REG_R6))) -#else -#define ALL_QLOAD_REGS (ALL_GENERAL_REGS & ~(1 << TCG_REG_R3)) -#define ALL_QSTORE_REGS ALL_QLOAD_REGS +#ifndef R_PPC64_PCREL34 +#define R_PPC64_PCREL34 132 #endif -TCGPowerISA have_isa; -static bool have_isel; -bool have_altivec; -bool have_vsx; +#define have_isel (cpuinfo & CPUINFO_ISEL) -#ifndef CONFIG_SOFTMMU -#define TCG_GUEST_BASE_REG 30 -#endif +#define TCG_GUEST_BASE_REG TCG_REG_R30 #ifdef CONFIG_DEBUG_TCG static const char tcg_target_reg_names[TCG_TARGET_NB_REGS][4] = { @@ -179,10 +188,12 @@ static const int tcg_target_call_iarg_regs[] = { TCG_REG_R10 }; -static const int tcg_target_call_oarg_regs[] = { - TCG_REG_R3, - TCG_REG_R4 -}; +static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) +{ + tcg_debug_assert(kind == TCG_CALL_RET_NORMAL); + tcg_debug_assert(slot >= 0 && slot <= 1); + return TCG_REG_R3 + slot; +} static const int tcg_target_callee_save_regs[] = { #ifdef _CALL_DARWIN @@ -208,13 +219,19 @@ static const int tcg_target_callee_save_regs[] = { TCG_REG_R31 }; +/* For PPC, we use TB+4 instead of TB as the base. */ +static inline ptrdiff_t ppc_tbrel_diff(TCGContext *s, const void *target) +{ + return tcg_tbrel_diff(s, target) - 4; +} + static inline bool in_range_b(tcg_target_long target) { return target == sextract64(target, 0, 26); } static uint32_t reloc_pc24_val(const tcg_insn_unit *pc, - const tcg_insn_unit *target) + const tcg_insn_unit *target) { ptrdiff_t disp = tcg_ptr_byte_diff(target, pc); tcg_debug_assert(in_range_b(disp)); @@ -234,7 +251,7 @@ static bool reloc_pc24(tcg_insn_unit *src_rw, const tcg_insn_unit *target) } static uint16_t reloc_pc14_val(const tcg_insn_unit *pc, - const tcg_insn_unit *target) + const tcg_insn_unit *target) { ptrdiff_t disp = tcg_ptr_byte_diff(target, pc); tcg_debug_assert(disp == (int16_t) disp); @@ -253,33 +270,91 @@ static bool reloc_pc14(tcg_insn_unit *src_rw, const tcg_insn_unit *target) return false; } -/* test if a constant matches the constraint */ -static bool tcg_target_const_match(int64_t val, TCGType type, int ct) +static bool reloc_pc34(tcg_insn_unit *src_rw, const tcg_insn_unit *target) { + const tcg_insn_unit *src_rx = tcg_splitwx_to_rx(src_rw); + ptrdiff_t disp = tcg_ptr_byte_diff(target, src_rx); + + if (disp == sextract64(disp, 0, 34)) { + src_rw[0] = (src_rw[0] & ~0x3ffff) | ((disp >> 16) & 0x3ffff); + src_rw[1] = (src_rw[1] & ~0xffff) | (disp & 0xffff); + return true; + } + return false; +} + +static bool mask_operand(uint32_t c, int *mb, int *me); +static bool mask64_operand(uint64_t c, int *mb, int *me); + +/* test if a constant matches the constraint */ +static bool tcg_target_const_match(int64_t sval, int ct, + TCGType type, TCGCond cond, int vece) +{ + uint64_t uval = sval; + int mb, me; + if (ct & TCG_CT_CONST) { return 1; } - /* The only 32-bit constraint we use aside from - TCG_CT_CONST is TCG_CT_CONST_S16. */ if (type == TCG_TYPE_I32) { - val = (int32_t)val; + uval = (uint32_t)sval; + sval = (int32_t)sval; } - if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) { + if (ct & TCG_CT_CONST_CMP) { + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + ct |= TCG_CT_CONST_S16 | TCG_CT_CONST_U16; + break; + case TCG_COND_LT: + case TCG_COND_GE: + case TCG_COND_LE: + case TCG_COND_GT: + ct |= TCG_CT_CONST_S16; + break; + case TCG_COND_LTU: + case TCG_COND_GEU: + case TCG_COND_LEU: + case TCG_COND_GTU: + ct |= TCG_CT_CONST_U16; + break; + case TCG_COND_TSTEQ: + case TCG_COND_TSTNE: + if ((uval & ~0xffff) == 0 || (uval & ~0xffff0000ull) == 0) { + return 1; + } + if (TCG_TARGET_REG_BITS == 32 || type == TCG_TYPE_I32 + ? mask_operand(uval, &mb, &me) + : mask64_operand(uval << clz64(uval), &mb, &me)) { + return 1; + } + return 0; + default: + g_assert_not_reached(); + } + } + + if ((ct & TCG_CT_CONST_S16) && sval == (int16_t)sval) { return 1; - } else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val) { + } + if ((ct & TCG_CT_CONST_U16) && uval == (uint16_t)uval) { return 1; - } else if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { + } + if ((ct & TCG_CT_CONST_S32) && sval == (int32_t)sval) { return 1; - } else if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) { + } + if ((ct & TCG_CT_CONST_U32) && uval == (uint32_t)uval) { return 1; - } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) { + } + if ((ct & TCG_CT_CONST_ZERO) && sval == 0) { return 1; - } else if ((ct & TCG_CT_CONST_MONE) && val == -1) { + } + if ((ct & TCG_CT_CONST_MONE) && sval == -1) { return 1; - } else if ((ct & TCG_CT_CONST_WSZ) - && val == (type == TCG_TYPE_I32 ? 32 : 64)) { + } + if ((ct & TCG_CT_CONST_WSZ) && sval == (type == TCG_TYPE_I32 ? 32 : 64)) { return 1; } return 0; @@ -296,25 +371,36 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define B OPCD( 18) #define BC OPCD( 16) + #define LBZ OPCD( 34) #define LHZ OPCD( 40) #define LHA OPCD( 42) #define LWZ OPCD( 32) #define LWZUX XO31( 55) -#define STB OPCD( 38) -#define STH OPCD( 44) -#define STW OPCD( 36) - -#define STD XO62( 0) -#define STDU XO62( 1) -#define STDX XO31(149) - #define LD XO58( 0) #define LDX XO31( 21) #define LDU XO58( 1) #define LDUX XO31( 53) #define LWA XO58( 2) #define LWAX XO31(341) +#define LQ OPCD( 56) + +#define STB OPCD( 38) +#define STH OPCD( 44) +#define STW OPCD( 36) +#define STD XO62( 0) +#define STDU XO62( 1) +#define STDX XO31(149) +#define STQ XO62( 2) + +#define PLWA OPCD( 41) +#define PLD OPCD( 57) +#define PLXSD OPCD( 42) +#define PLXV OPCD(25 * 2 + 1) /* force tx=1 */ + +#define PSTD OPCD( 61) +#define PSTXSD OPCD( 46) +#define PSTXV OPCD(27 * 2 + 1) /* force sx=1 */ #define ADDIC OPCD( 12) #define ADDI OPCD( 14) @@ -349,6 +435,7 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define CRNAND XO19(225) #define CROR XO19(449) #define CRNOR XO19( 33) +#define ADDPCIS XO19( 2) #define EXTSB XO31(954) #define EXTSH XO31(922) @@ -371,6 +458,8 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define MULHWU XO31( 11) #define DIVW XO31(491) #define DIVWU XO31(459) +#define MODSW XO31(779) +#define MODUW XO31(267) #define CMP XO31( 0) #define CMPL XO31( 32) #define LHBRX XO31(790) @@ -403,6 +492,8 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define MULHDU XO31( 9) #define DIVD XO31(489) #define DIVDU XO31(457) +#define MODSD XO31(777) +#define MODUD XO31(265) #define LBZX XO31( 87) #define LHZX XO31(279) @@ -436,6 +527,11 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define TW XO31( 4) #define TRAP (TW | TO(31)) +#define SETBC XO31(384) /* v3.10 */ +#define SETBCR XO31(416) /* v3.10 */ +#define SETNBC XO31(448) /* v3.10 */ +#define SETNBCR XO31(480) /* v3.10 */ + #define NOP ORI /* ori 0,0,0 */ #define LVX XO31(103) @@ -622,31 +718,35 @@ enum { CR_SO }; -static const uint32_t tcg_to_bc[] = { - [TCG_COND_EQ] = BC | BI(7, CR_EQ) | BO_COND_TRUE, - [TCG_COND_NE] = BC | BI(7, CR_EQ) | BO_COND_FALSE, - [TCG_COND_LT] = BC | BI(7, CR_LT) | BO_COND_TRUE, - [TCG_COND_GE] = BC | BI(7, CR_LT) | BO_COND_FALSE, - [TCG_COND_LE] = BC | BI(7, CR_GT) | BO_COND_FALSE, - [TCG_COND_GT] = BC | BI(7, CR_GT) | BO_COND_TRUE, - [TCG_COND_LTU] = BC | BI(7, CR_LT) | BO_COND_TRUE, - [TCG_COND_GEU] = BC | BI(7, CR_LT) | BO_COND_FALSE, - [TCG_COND_LEU] = BC | BI(7, CR_GT) | BO_COND_FALSE, - [TCG_COND_GTU] = BC | BI(7, CR_GT) | BO_COND_TRUE, +static const uint32_t tcg_to_bc[16] = { + [TCG_COND_EQ] = BC | BI(0, CR_EQ) | BO_COND_TRUE, + [TCG_COND_NE] = BC | BI(0, CR_EQ) | BO_COND_FALSE, + [TCG_COND_TSTEQ] = BC | BI(0, CR_EQ) | BO_COND_TRUE, + [TCG_COND_TSTNE] = BC | BI(0, CR_EQ) | BO_COND_FALSE, + [TCG_COND_LT] = BC | BI(0, CR_LT) | BO_COND_TRUE, + [TCG_COND_GE] = BC | BI(0, CR_LT) | BO_COND_FALSE, + [TCG_COND_LE] = BC | BI(0, CR_GT) | BO_COND_FALSE, + [TCG_COND_GT] = BC | BI(0, CR_GT) | BO_COND_TRUE, + [TCG_COND_LTU] = BC | BI(0, CR_LT) | BO_COND_TRUE, + [TCG_COND_GEU] = BC | BI(0, CR_LT) | BO_COND_FALSE, + [TCG_COND_LEU] = BC | BI(0, CR_GT) | BO_COND_FALSE, + [TCG_COND_GTU] = BC | BI(0, CR_GT) | BO_COND_TRUE, }; /* The low bit here is set if the RA and RB fields must be inverted. */ -static const uint32_t tcg_to_isel[] = { - [TCG_COND_EQ] = ISEL | BC_(7, CR_EQ), - [TCG_COND_NE] = ISEL | BC_(7, CR_EQ) | 1, - [TCG_COND_LT] = ISEL | BC_(7, CR_LT), - [TCG_COND_GE] = ISEL | BC_(7, CR_LT) | 1, - [TCG_COND_LE] = ISEL | BC_(7, CR_GT) | 1, - [TCG_COND_GT] = ISEL | BC_(7, CR_GT), - [TCG_COND_LTU] = ISEL | BC_(7, CR_LT), - [TCG_COND_GEU] = ISEL | BC_(7, CR_LT) | 1, - [TCG_COND_LEU] = ISEL | BC_(7, CR_GT) | 1, - [TCG_COND_GTU] = ISEL | BC_(7, CR_GT), +static const uint32_t tcg_to_isel[16] = { + [TCG_COND_EQ] = ISEL | BC_(0, CR_EQ), + [TCG_COND_NE] = ISEL | BC_(0, CR_EQ) | 1, + [TCG_COND_TSTEQ] = ISEL | BC_(0, CR_EQ), + [TCG_COND_TSTNE] = ISEL | BC_(0, CR_EQ) | 1, + [TCG_COND_LT] = ISEL | BC_(0, CR_LT), + [TCG_COND_GE] = ISEL | BC_(0, CR_LT) | 1, + [TCG_COND_LE] = ISEL | BC_(0, CR_GT) | 1, + [TCG_COND_GT] = ISEL | BC_(0, CR_GT), + [TCG_COND_LTU] = ISEL | BC_(0, CR_LT), + [TCG_COND_GEU] = ISEL | BC_(0, CR_LT) | 1, + [TCG_COND_LEU] = ISEL | BC_(0, CR_GT) | 1, + [TCG_COND_GTU] = ISEL | BC_(0, CR_GT), }; static bool patch_reloc(tcg_insn_unit *code_ptr, int type, @@ -664,6 +764,8 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, return reloc_pc14(code_ptr, target); case R_PPC_REL24: return reloc_pc24(code_ptr, target); + case R_PPC64_PCREL34: + return reloc_pc34(code_ptr, target); case R_PPC_ADDR16: /* * We are (slightly) abusing this relocation type. In particular, @@ -696,6 +798,52 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, return true; } +/* Ensure that the prefixed instruction does not cross a 64-byte boundary. */ +static bool tcg_out_need_prefix_align(TCGContext *s) +{ + return ((uintptr_t)s->code_ptr & 0x3f) == 0x3c; +} + +static void tcg_out_prefix_align(TCGContext *s) +{ + if (tcg_out_need_prefix_align(s)) { + tcg_out32(s, NOP); + } +} + +static ptrdiff_t tcg_pcrel_diff_for_prefix(TCGContext *s, const void *target) +{ + return tcg_pcrel_diff(s, target) - (tcg_out_need_prefix_align(s) ? 4 : 0); +} + +/* Output Type 00 Prefix - 8-Byte Load/Store Form (8LS:D) */ +static void tcg_out_8ls_d(TCGContext *s, tcg_insn_unit opc, unsigned rt, + unsigned ra, tcg_target_long imm, bool r) +{ + tcg_insn_unit p, i; + + p = OPCD(1) | (r << 20) | ((imm >> 16) & 0x3ffff); + i = opc | TAI(rt, ra, imm); + + tcg_out_prefix_align(s); + tcg_out32(s, p); + tcg_out32(s, i); +} + +/* Output Type 10 Prefix - Modified Load/Store Form (MLS:D) */ +static void tcg_out_mls_d(TCGContext *s, tcg_insn_unit opc, unsigned rt, + unsigned ra, tcg_target_long imm, bool r) +{ + tcg_insn_unit p, i; + + p = OPCD(1) | (2 << 24) | (r << 20) | ((imm >> 16) & 0x3ffff); + i = opc | TAI(rt, ra, imm); + + tcg_out_prefix_align(s); + tcg_out32(s, p); + tcg_out32(s, i); +} + static void tcg_out_mem_long(TCGContext *s, int opi, int opx, TCGReg rt, TCGReg base, tcg_target_long offset); @@ -743,46 +891,81 @@ static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) return true; } -static inline void tcg_out_rld(TCGContext *s, int op, TCGReg ra, TCGReg rs, - int sh, int mb) +static void tcg_out_rld_rc(TCGContext *s, int op, TCGReg ra, TCGReg rs, + int sh, int mb, bool rc) { tcg_debug_assert(TCG_TARGET_REG_BITS == 64); sh = SH(sh & 0x1f) | (((sh >> 5) & 1) << 1); mb = MB64((mb >> 5) | ((mb << 1) & 0x3f)); - tcg_out32(s, op | RA(ra) | RS(rs) | sh | mb); + tcg_out32(s, op | RA(ra) | RS(rs) | sh | mb | rc); } -static inline void tcg_out_rlw(TCGContext *s, int op, TCGReg ra, TCGReg rs, - int sh, int mb, int me) +static void tcg_out_rld(TCGContext *s, int op, TCGReg ra, TCGReg rs, + int sh, int mb) { - tcg_out32(s, op | RA(ra) | RS(rs) | SH(sh) | MB(mb) | ME(me)); + tcg_out_rld_rc(s, op, ra, rs, sh, mb, false); } -static inline void tcg_out_ext8s(TCGContext *s, TCGReg dst, TCGReg src) +static void tcg_out_rlw_rc(TCGContext *s, int op, TCGReg ra, TCGReg rs, + int sh, int mb, int me, bool rc) +{ + tcg_out32(s, op | RA(ra) | RS(rs) | SH(sh) | MB(mb) | ME(me) | rc); +} + +static void tcg_out_rlw(TCGContext *s, int op, TCGReg ra, TCGReg rs, + int sh, int mb, int me) +{ + tcg_out_rlw_rc(s, op, ra, rs, sh, mb, me, false); +} + +static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg dst, TCGReg src) { tcg_out32(s, EXTSB | RA(dst) | RS(src)); } -static inline void tcg_out_ext16s(TCGContext *s, TCGReg dst, TCGReg src) +static void tcg_out_ext8u(TCGContext *s, TCGReg dst, TCGReg src) +{ + tcg_out32(s, ANDI | SAI(src, dst, 0xff)); +} + +static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg dst, TCGReg src) { tcg_out32(s, EXTSH | RA(dst) | RS(src)); } -static inline void tcg_out_ext16u(TCGContext *s, TCGReg dst, TCGReg src) +static void tcg_out_ext16u(TCGContext *s, TCGReg dst, TCGReg src) { tcg_out32(s, ANDI | SAI(src, dst, 0xffff)); } -static inline void tcg_out_ext32s(TCGContext *s, TCGReg dst, TCGReg src) +static void tcg_out_ext32s(TCGContext *s, TCGReg dst, TCGReg src) { + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out32(s, EXTSW | RA(dst) | RS(src)); } -static inline void tcg_out_ext32u(TCGContext *s, TCGReg dst, TCGReg src) +static void tcg_out_ext32u(TCGContext *s, TCGReg dst, TCGReg src) { + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out_rld(s, RLDICL, dst, src, 0, 32); } +static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg dst, TCGReg src) +{ + tcg_out_ext32s(s, dst, src); +} + +static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg dst, TCGReg src) +{ + tcg_out_ext32u(s, dst, src); +} + +static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rn) +{ + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + tcg_out_mov(s, TCG_TYPE_I32, rd, rn); +} + static inline void tcg_out_shli32(TCGContext *s, TCGReg dst, TCGReg src, int c) { tcg_out_rlw(s, RLWINM, dst, src, c, 0, 31 - c); @@ -814,6 +997,19 @@ static inline void tcg_out_sari64(TCGContext *s, TCGReg dst, TCGReg src, int c) tcg_out32(s, SRADI | RA(dst) | RS(src) | SH(c & 0x1f) | ((c >> 4) & 2)); } +static void tcg_out_addpcis(TCGContext *s, TCGReg dst, intptr_t imm) +{ + uint32_t d0, d1, d2; + + tcg_debug_assert((imm & 0xffff) == 0); + tcg_debug_assert(imm == (int32_t)imm); + + d2 = extract32(imm, 16, 1); + d1 = extract32(imm, 17, 5); + d0 = extract32(imm, 22, 10); + tcg_out32(s, ADDPCIS | RT(dst) | (d1 << 16) | (d0 << 6) | d2); +} + static void tcg_out_bswap16(TCGContext *s, TCGReg dst, TCGReg src, int flags) { TCGReg tmp = dst == src ? TCG_REG_R0 : dst; @@ -821,7 +1017,7 @@ static void tcg_out_bswap16(TCGContext *s, TCGReg dst, TCGReg src, int flags) if (have_isa_3_10) { tcg_out32(s, BRH | RA(dst) | RS(src)); if (flags & TCG_BSWAP_OS) { - tcg_out_ext16s(s, dst, dst); + tcg_out_ext16s(s, TCG_TYPE_REG, dst, dst); } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { tcg_out_ext16u(s, dst, dst); } @@ -840,7 +1036,7 @@ static void tcg_out_bswap16(TCGContext *s, TCGReg dst, TCGReg src, int flags) tcg_out_rlw(s, RLWIMI, tmp, src, 8, 16, 23); if (flags & TCG_BSWAP_OS) { - tcg_out_ext16s(s, dst, tmp); + tcg_out_ext16s(s, TCG_TYPE_REG, dst, tmp); } else { tcg_out_mov(s, TCG_TYPE_REG, dst, tmp); } @@ -952,12 +1148,31 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, } /* Load addresses within the TB with one insn. */ - tb_diff = tcg_tbrel_diff(s, (void *)arg); + tb_diff = ppc_tbrel_diff(s, (void *)arg); if (!in_prologue && USE_REG_TB && tb_diff == (int16_t)tb_diff) { tcg_out32(s, ADDI | TAI(ret, TCG_REG_TB, tb_diff)); return; } + /* + * Load values up to 34 bits, and pc-relative addresses, + * with one prefixed insn. + */ + if (have_isa_3_10) { + if (arg == sextract64(arg, 0, 34)) { + /* pli ret,value = paddi ret,0,value,0 */ + tcg_out_mls_d(s, ADDI, ret, 0, arg, 0); + return; + } + + tmp = tcg_pcrel_diff_for_prefix(s, (void *)arg); + if (tmp == sextract64(tmp, 0, 34)) { + /* pla ret,value = paddi ret,0,value,1 */ + tcg_out_mls_d(s, ADDI, ret, 0, tmp, 1); + return; + } + } + /* Load 32-bit immediates with two insns. Note that we've already eliminated bare ADDIS, so we know both insns are required. */ if (TCG_TARGET_REG_BITS == 32 || arg == (int32_t)arg) { @@ -996,6 +1211,19 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, return; } + /* Load addresses within 2GB with 2 insns. */ + if (have_isa_3_00) { + intptr_t hi = tcg_pcrel_diff(s, (void *)arg) - 4; + int16_t lo = hi; + + hi -= lo; + if (hi == (int32_t)hi) { + tcg_out_addpcis(s, TCG_REG_TMP2, hi); + tcg_out32(s, ADDI | TAI(ret, TCG_REG_TMP2, lo)); + return; + } + } + /* Load addresses within 2GB of TB with 2 (or rarely 3) insns. */ if (!in_prologue && USE_REG_TB && tb_diff == (int32_t)tb_diff) { tcg_out_mem_long(s, ADDI, ADD, ret, TCG_REG_TB, tb_diff); @@ -1005,10 +1233,21 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, /* Use the constant pool, if possible. */ if (!in_prologue && USE_REG_TB) { new_pool_label(s, arg, R_PPC_ADDR16, s->code_ptr, - tcg_tbrel_diff(s, NULL)); + ppc_tbrel_diff(s, NULL)); tcg_out32(s, LD | TAI(ret, TCG_REG_TB, 0)); return; } + if (have_isa_3_10) { + tcg_out_8ls_d(s, PLD, ret, 0, 0, 1); + new_pool_label(s, arg, R_PPC64_PCREL34, s->code_ptr - 2, 0); + return; + } + if (have_isa_3_00) { + tcg_out_addpcis(s, TCG_REG_TMP2, 0); + new_pool_label(s, arg, R_PPC_REL14, s->code_ptr, 0); + tcg_out32(s, LD | TAI(ret, TCG_REG_TMP2, 0)); + return; + } tmp = arg >> 31 >> 1; tcg_out_movi(s, TCG_TYPE_I32, ret, tmp); @@ -1065,7 +1304,20 @@ static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, */ if (USE_REG_TB) { rel = R_PPC_ADDR16; - add = tcg_tbrel_diff(s, NULL); + add = ppc_tbrel_diff(s, NULL); + } else if (have_isa_3_10) { + if (type == TCG_TYPE_V64) { + tcg_out_8ls_d(s, PLXSD, ret & 31, 0, 0, 1); + new_pool_label(s, val, R_PPC64_PCREL34, s->code_ptr - 2, 0); + } else { + tcg_out_8ls_d(s, PLXV, ret & 31, 0, 0, 1); + new_pool_l2(s, R_PPC64_PCREL34, s->code_ptr - 2, 0, val, val); + } + return; + } else if (have_isa_3_00) { + tcg_out_addpcis(s, TCG_REG_TMP1, 0); + rel = R_PPC_REL14; + add = 0; } else { rel = R_PPC_ADDR32; add = 0; @@ -1092,6 +1344,8 @@ static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, if (USE_REG_TB) { tcg_out32(s, ADDI | TAI(TCG_REG_TMP1, 0, 0)); load_insn |= RA(TCG_REG_TB); + } else if (have_isa_3_00) { + tcg_out32(s, ADDI | TAI(TCG_REG_TMP1, TCG_REG_TMP1, 0)); } else { tcg_out32(s, ADDIS | TAI(TCG_REG_TMP1, 0, 0)); tcg_out32(s, ADDI | TAI(TCG_REG_TMP1, TCG_REG_TMP1, 0)); @@ -1114,6 +1368,18 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg ret, } } +static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2) +{ + return false; +} + +static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs, + tcg_target_long imm) +{ + /* This function is only used for passing structs by reference. */ + g_assert_not_reached(); +} + static bool mask_operand(uint32_t c, int *mb, int *me) { uint32_t lsb, test; @@ -1271,6 +1537,49 @@ static void tcg_out_mem_long(TCGContext *s, int opi, int opx, TCGReg rt, break; } + /* For unaligned or large offsets, use the prefixed form. */ + if (have_isa_3_10 + && (offset != (int16_t)offset || (offset & align)) + && offset == sextract64(offset, 0, 34)) { + /* + * Note that the MLS:D insns retain their un-prefixed opcode, + * while the 8LS:D insns use a different opcode space. + */ + switch (opi) { + case LBZ: + case LHZ: + case LHA: + case LWZ: + case STB: + case STH: + case STW: + case ADDI: + tcg_out_mls_d(s, opi, rt, base, offset, 0); + return; + case LWA: + tcg_out_8ls_d(s, PLWA, rt, base, offset, 0); + return; + case LD: + tcg_out_8ls_d(s, PLD, rt, base, offset, 0); + return; + case STD: + tcg_out_8ls_d(s, PSTD, rt, base, offset, 0); + return; + case LXSD: + tcg_out_8ls_d(s, PLXSD, rt & 31, base, offset, 0); + return; + case STXSD: + tcg_out_8ls_d(s, PSTXSD, rt & 31, base, offset, 0); + return; + case LXV: + tcg_out_8ls_d(s, PLXV, rt & 31, base, offset, 0); + return; + case STXV: + tcg_out_8ls_d(s, PSTXV, rt & 31, base, offset, 0); + return; + } + } + /* For unaligned, or very large offsets, use the indexed form. */ if (offset & align || offset != (int32_t)offset || opi == 0) { if (rs == base) { @@ -1424,6 +1733,50 @@ static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, return false; } +/* + * Set dest non-zero if and only if (arg1 & arg2) is non-zero. + * If RC, then also set RC0. + */ +static void tcg_out_test(TCGContext *s, TCGReg dest, TCGReg arg1, TCGArg arg2, + bool const_arg2, TCGType type, bool rc) +{ + int mb, me; + + if (!const_arg2) { + tcg_out32(s, AND | SAB(arg1, dest, arg2) | rc); + return; + } + + if (type == TCG_TYPE_I32) { + arg2 = (uint32_t)arg2; + } else if (arg2 == (uint32_t)arg2) { + type = TCG_TYPE_I32; + } + + if ((arg2 & ~0xffff) == 0) { + tcg_out32(s, ANDI | SAI(arg1, dest, arg2)); + return; + } + if ((arg2 & ~0xffff0000ull) == 0) { + tcg_out32(s, ANDIS | SAI(arg1, dest, arg2 >> 16)); + return; + } + if (TCG_TARGET_REG_BITS == 32 || type == TCG_TYPE_I32) { + if (mask_operand(arg2, &mb, &me)) { + tcg_out_rlw_rc(s, RLWINM, dest, arg1, 0, mb, me, rc); + return; + } + } else { + int sh = clz64(arg2); + if (mask64_operand(arg2 << sh, &mb, &me)) { + tcg_out_rld_rc(s, RLDICR, dest, arg1, sh, me, rc); + return; + } + } + /* Constraints should satisfy this. */ + g_assert_not_reached(); +} + static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, int const_arg2, int cr, TCGType type) { @@ -1432,7 +1785,10 @@ static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); - /* Simplify the comparisons below wrt CMPI. */ + /* + * Simplify the comparisons below wrt CMPI. + * All of the tests are 16-bit, so a 32-bit sign extend always works. + */ if (type == TCG_TYPE_I32) { arg2 = (int32_t)arg2; } @@ -1455,6 +1811,12 @@ static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, imm = 0; break; + case TCG_COND_TSTEQ: + case TCG_COND_TSTNE: + tcg_debug_assert(cr == 0); + tcg_out_test(s, TCG_REG_R0, arg1, arg2, const_arg2, type, true); + return; + case TCG_COND_LT: case TCG_COND_GE: case TCG_COND_LE: @@ -1486,7 +1848,7 @@ static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, break; default: - tcg_abort(); + g_assert_not_reached(); } op |= BF(cr) | ((type == TCG_TYPE_I64) << 21); @@ -1502,8 +1864,20 @@ static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, } static void tcg_out_setcond_eq0(TCGContext *s, TCGType type, - TCGReg dst, TCGReg src) + TCGReg dst, TCGReg src, bool neg) { + if (neg && (TCG_TARGET_REG_BITS == 32 || type == TCG_TYPE_I64)) { + /* + * X != 0 implies X + -1 generates a carry. + * RT = (~X + X) + CA + * = -1 + CA + * = CA ? 0 : -1 + */ + tcg_out32(s, ADDIC | TAI(TCG_REG_R0, src, -1)); + tcg_out32(s, SUBFE | TAB(dst, src, src)); + return; + } + if (type == TCG_TYPE_I32) { tcg_out32(s, CNTLZW | RS(src) | RA(dst)); tcg_out_shri32(s, dst, dst, 5); @@ -1511,18 +1885,28 @@ static void tcg_out_setcond_eq0(TCGContext *s, TCGType type, tcg_out32(s, CNTLZD | RS(src) | RA(dst)); tcg_out_shri64(s, dst, dst, 6); } + if (neg) { + tcg_out32(s, NEG | RT(dst) | RA(dst)); + } } -static void tcg_out_setcond_ne0(TCGContext *s, TCGReg dst, TCGReg src) +static void tcg_out_setcond_ne0(TCGContext *s, TCGType type, + TCGReg dst, TCGReg src, bool neg) { - /* X != 0 implies X + -1 generates a carry. Extra addition - trickery means: R = X-1 + ~X + C = X-1 + (-X+1) + C = C. */ - if (dst != src) { - tcg_out32(s, ADDIC | TAI(dst, src, -1)); - tcg_out32(s, SUBFE | TAB(dst, dst, src)); - } else { + if (!neg && (TCG_TARGET_REG_BITS == 32 || type == TCG_TYPE_I64)) { + /* + * X != 0 implies X + -1 generates a carry. Extra addition + * trickery means: R = X-1 + ~X + C = X-1 + (-X+1) + C = C. + */ tcg_out32(s, ADDIC | TAI(TCG_REG_R0, src, -1)); tcg_out32(s, SUBFE | TAB(dst, TCG_REG_R0, src)); + return; + } + tcg_out_setcond_eq0(s, type, dst, src, false); + if (neg) { + tcg_out32(s, ADDI | TAI(dst, dst, -1)); + } else { + tcg_out_xori32(s, dst, dst, 1); } } @@ -1544,9 +1928,10 @@ static TCGReg tcg_gen_setcond_xor(TCGContext *s, TCGReg arg1, TCGArg arg2, static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, TCGArg arg0, TCGArg arg1, TCGArg arg2, - int const_arg2) + int const_arg2, bool neg) { - int crop, sh; + int sh; + bool inv; tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); @@ -1555,18 +1940,31 @@ static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, arg2 = (uint32_t)arg2; } + /* With SETBC/SETBCR, we can always implement with 2 insns. */ + if (have_isa_3_10) { + tcg_insn_unit bi, opc; + + tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 0, type); + + /* Re-use tcg_to_bc for BI and BO_COND_{TRUE,FALSE}. */ + bi = tcg_to_bc[cond] & (0x1f << 16); + if (tcg_to_bc[cond] & BO(8)) { + opc = neg ? SETNBC : SETBC; + } else { + opc = neg ? SETNBCR : SETBCR; + } + tcg_out32(s, opc | RT(arg0) | bi); + return; + } + /* Handle common and trivial cases before handling anything else. */ if (arg2 == 0) { switch (cond) { case TCG_COND_EQ: - tcg_out_setcond_eq0(s, type, arg0, arg1); + tcg_out_setcond_eq0(s, type, arg0, arg1, neg); return; case TCG_COND_NE: - if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { - tcg_out_ext32u(s, TCG_REG_R0, arg1); - arg1 = TCG_REG_R0; - } - tcg_out_setcond_ne0(s, arg0, arg1); + tcg_out_setcond_ne0(s, type, arg0, arg1, neg); return; case TCG_COND_GE: tcg_out32(s, NOR | SAB(arg1, arg0, arg1)); @@ -1575,9 +1973,17 @@ static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, case TCG_COND_LT: /* Extract the sign bit. */ if (type == TCG_TYPE_I32) { - tcg_out_shri32(s, arg0, arg1, 31); + if (neg) { + tcg_out_sari32(s, arg0, arg1, 31); + } else { + tcg_out_shri32(s, arg0, arg1, 31); + } } else { - tcg_out_shri64(s, arg0, arg1, 63); + if (neg) { + tcg_out_sari64(s, arg0, arg1, 63); + } else { + tcg_out_shri64(s, arg0, arg1, 63); + } } return; default: @@ -1591,11 +1997,11 @@ static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, if (have_isel) { int isel, tab; - tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); + tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 0, type); isel = tcg_to_isel[cond]; - tcg_out_movi(s, type, arg0, 1); + tcg_out_movi(s, type, arg0, neg ? -1 : 1); if (isel & 1) { /* arg0 = (bc ? 0 : 1) */ tab = TAB(arg0, 0, arg0); @@ -1609,74 +2015,86 @@ static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, return; } + inv = false; switch (cond) { case TCG_COND_EQ: arg1 = tcg_gen_setcond_xor(s, arg1, arg2, const_arg2); - tcg_out_setcond_eq0(s, type, arg0, arg1); - return; + tcg_out_setcond_eq0(s, type, arg0, arg1, neg); + break; case TCG_COND_NE: arg1 = tcg_gen_setcond_xor(s, arg1, arg2, const_arg2); - /* Discard the high bits only once, rather than both inputs. */ - if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { - tcg_out_ext32u(s, TCG_REG_R0, arg1); - arg1 = TCG_REG_R0; - } - tcg_out_setcond_ne0(s, arg0, arg1); - return; + tcg_out_setcond_ne0(s, type, arg0, arg1, neg); + break; + case TCG_COND_TSTEQ: + tcg_out_test(s, TCG_REG_R0, arg1, arg2, const_arg2, type, false); + tcg_out_setcond_eq0(s, type, arg0, TCG_REG_R0, neg); + break; + + case TCG_COND_TSTNE: + tcg_out_test(s, TCG_REG_R0, arg1, arg2, const_arg2, type, false); + tcg_out_setcond_ne0(s, type, arg0, TCG_REG_R0, neg); + break; + + case TCG_COND_LE: + case TCG_COND_LEU: + inv = true; + /* fall through */ case TCG_COND_GT: case TCG_COND_GTU: - sh = 30; - crop = 0; - goto crtest; - - case TCG_COND_LT: - case TCG_COND_LTU: - sh = 29; - crop = 0; + sh = 30; /* CR7 CR_GT */ goto crtest; case TCG_COND_GE: case TCG_COND_GEU: - sh = 31; - crop = CRNOR | BT(7, CR_EQ) | BA(7, CR_LT) | BB(7, CR_LT); + inv = true; + /* fall through */ + case TCG_COND_LT: + case TCG_COND_LTU: + sh = 29; /* CR7 CR_LT */ goto crtest; - case TCG_COND_LE: - case TCG_COND_LEU: - sh = 31; - crop = CRNOR | BT(7, CR_EQ) | BA(7, CR_GT) | BB(7, CR_GT); crtest: tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); - if (crop) { - tcg_out32(s, crop); - } tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(7)); tcg_out_rlw(s, RLWINM, arg0, TCG_REG_R0, sh, 31, 31); + if (neg && inv) { + tcg_out32(s, ADDI | TAI(arg0, arg0, -1)); + } else if (neg) { + tcg_out32(s, NEG | RT(arg0) | RA(arg0)); + } else if (inv) { + tcg_out_xori32(s, arg0, arg0, 1); + } break; default: - tcg_abort(); + g_assert_not_reached(); } } -static void tcg_out_bc(TCGContext *s, int bc, TCGLabel *l) +static void tcg_out_bc(TCGContext *s, TCGCond cond, int bd) { + tcg_out32(s, tcg_to_bc[cond] | bd); +} + +static void tcg_out_bc_lab(TCGContext *s, TCGCond cond, TCGLabel *l) +{ + int bd = 0; if (l->has_value) { - bc |= reloc_pc14_val(tcg_splitwx_to_rx(s->code_ptr), l->u.value_ptr); + bd = reloc_pc14_val(tcg_splitwx_to_rx(s->code_ptr), l->u.value_ptr); } else { tcg_out_reloc(s, s->code_ptr, R_PPC_REL14, l, 0); } - tcg_out32(s, bc); + tcg_out_bc(s, cond, bd); } static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGArg arg1, TCGArg arg2, int const_arg2, TCGLabel *l, TCGType type) { - tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); - tcg_out_bc(s, tcg_to_bc[cond], l); + tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 0, type); + tcg_out_bc_lab(s, cond, l); } static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, @@ -1689,7 +2107,7 @@ static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, return; } - tcg_out_cmp(s, cond, c1, c2, const_c2, 7, type); + tcg_out_cmp(s, cond, c1, c2, const_c2, 0, type); if (have_isel) { int isel = tcg_to_isel[cond]; @@ -1718,7 +2136,7 @@ static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, } } /* Branch forward over one insn */ - tcg_out32(s, tcg_to_bc[cond] | 8); + tcg_out_bc(s, cond, 8); if (v2 == 0) { tcg_out_movi(s, type, dest, 0); } else { @@ -1733,17 +2151,17 @@ static void tcg_out_cntxz(TCGContext *s, TCGType type, uint32_t opc, if (const_a2 && a2 == (type == TCG_TYPE_I32 ? 32 : 64)) { tcg_out32(s, opc | RA(a0) | RS(a1)); } else { - tcg_out_cmp(s, TCG_COND_EQ, a1, 0, 1, 7, type); + tcg_out_cmp(s, TCG_COND_EQ, a1, 0, 1, 0, type); /* Note that the only other valid constant for a2 is 0. */ if (have_isel) { tcg_out32(s, opc | RA(TCG_REG_R0) | RS(a1)); tcg_out32(s, tcg_to_isel[TCG_COND_EQ] | TAB(a0, a2, TCG_REG_R0)); } else if (!const_a2 && a0 == a2) { - tcg_out32(s, tcg_to_bc[TCG_COND_EQ] | 8); + tcg_out_bc(s, TCG_COND_EQ, 8); tcg_out32(s, opc | RA(a0) | RS(a1)); } else { tcg_out32(s, opc | RA(a0) | RS(a1)); - tcg_out32(s, tcg_to_bc[TCG_COND_NE] | 8); + tcg_out_bc(s, TCG_COND_NE, 8); if (const_a2) { tcg_out_movi(s, type, a0, 0); } else { @@ -1788,7 +2206,22 @@ static void tcg_out_cmp2(TCGContext *s, const TCGArg *args, do_equality: tcg_out_cmp(s, cond, al, bl, blconst, 6, TCG_TYPE_I32); tcg_out_cmp(s, cond, ah, bh, bhconst, 7, TCG_TYPE_I32); - tcg_out32(s, op | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); + tcg_out32(s, op | BT(0, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); + break; + + case TCG_COND_TSTEQ: + case TCG_COND_TSTNE: + if (blconst) { + tcg_out_andi32(s, TCG_REG_R0, al, bl); + } else { + tcg_out32(s, AND | SAB(al, TCG_REG_R0, bl)); + } + if (bhconst) { + tcg_out_andi32(s, TCG_REG_TMP1, ah, bh); + } else { + tcg_out32(s, AND | SAB(ah, TCG_REG_TMP1, bh)); + } + tcg_out32(s, OR | SAB(TCG_REG_R0, TCG_REG_R0, TCG_REG_TMP1) | 1); break; case TCG_COND_LT: @@ -1806,12 +2239,12 @@ static void tcg_out_cmp2(TCGContext *s, const TCGArg *args, tcg_out_cmp(s, cond, ah, bh, bhconst, 6, TCG_TYPE_I32); tcg_out_cmp(s, cond2, al, bl, blconst, 7, TCG_TYPE_I32); - tcg_out32(s, op | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, bit2)); - tcg_out32(s, CROR | BT(7, CR_EQ) | BA(6, bit1) | BB(7, CR_EQ)); + tcg_out32(s, op | BT(0, CR_EQ) | BA(6, CR_EQ) | BB(7, bit2)); + tcg_out32(s, CROR | BT(0, CR_EQ) | BA(6, bit1) | BB(0, CR_EQ)); break; default: - tcg_abort(); + g_assert_not_reached(); } } @@ -1819,15 +2252,15 @@ static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, const int *const_args) { tcg_out_cmp2(s, args + 1, const_args + 1); - tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(7)); - tcg_out_rlw(s, RLWINM, args[0], TCG_REG_R0, 31, 31, 31); + tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(0)); + tcg_out_rlw(s, RLWINM, args[0], TCG_REG_R0, CR_EQ + 0*4 + 1, 31, 31); } -static void tcg_out_brcond2 (TCGContext *s, const TCGArg *args, - const int *const_args) +static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, + const int *const_args) { tcg_out_cmp2(s, args, const_args); - tcg_out_bc(s, BC | BI(7, CR_EQ) | BO_COND_TRUE, arg_label(args[5])); + tcg_out_bc_lab(s, TCG_COND_EQ, arg_label(args[5])); } static void tcg_out_mb(TCGContext *s, TCGArg a0) @@ -1843,46 +2276,6 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) tcg_out32(s, insn); } -void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx, - uintptr_t jmp_rw, uintptr_t addr) -{ - if (TCG_TARGET_REG_BITS == 64) { - tcg_insn_unit i1, i2; - intptr_t tb_diff = addr - tc_ptr; - intptr_t br_diff = addr - (jmp_rx + 4); - uint64_t pair; - - /* This does not exercise the range of the branch, but we do - still need to be able to load the new value of TCG_REG_TB. - But this does still happen quite often. */ - if (tb_diff == (int16_t)tb_diff) { - i1 = ADDI | TAI(TCG_REG_TB, TCG_REG_TB, tb_diff); - i2 = B | (br_diff & 0x3fffffc); - } else { - intptr_t lo = (int16_t)tb_diff; - intptr_t hi = (int32_t)(tb_diff - lo); - assert(tb_diff == hi + lo); - i1 = ADDIS | TAI(TCG_REG_TB, TCG_REG_TB, hi >> 16); - i2 = ADDI | TAI(TCG_REG_TB, TCG_REG_TB, lo); - } -#if HOST_BIG_ENDIAN - pair = (uint64_t)i1 << 32 | i2; -#else - pair = (uint64_t)i2 << 32 | i1; -#endif - - /* As per the enclosing if, this is ppc64. Avoid the _Static_assert - within qatomic_set that would fail to build a ppc32 host. */ - qatomic_set__nocheck((uint64_t *)jmp_rw, pair); - flush_idcache_range(jmp_rx, jmp_rw, 8); - } else { - intptr_t diff = addr - jmp_rx; - tcg_debug_assert(in_range_b(diff)); - qatomic_set((uint32_t *)jmp_rw, B | (diff & 0x3fffffc)); - flush_idcache_range(jmp_rx, jmp_rw, 4); - } -} - static void tcg_out_call_int(TCGContext *s, int lk, const tcg_insn_unit *target) { @@ -1934,7 +2327,8 @@ static void tcg_out_call_int(TCGContext *s, int lk, #endif } -static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target) +static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target, + const TCGHelperInfo *info) { tcg_out_call_int(s, LK, target); } @@ -1963,211 +2357,36 @@ static const uint32_t qemu_stx_opc[(MO_SIZE + MO_BSWAP) + 1] = { [MO_BSWAP | MO_UQ] = STDBRX, }; -static const uint32_t qemu_exts_opc[4] = { - EXTSB, EXTSH, EXTSW, 0 -}; - -#if defined (CONFIG_SOFTMMU) -/* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr, - * int mmu_idx, uintptr_t ra) - */ -static void * const qemu_ld_helpers[(MO_SIZE | MO_BSWAP) + 1] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_LEUW] = helper_le_lduw_mmu, - [MO_LEUL] = helper_le_ldul_mmu, - [MO_LEUQ] = helper_le_ldq_mmu, - [MO_BEUW] = helper_be_lduw_mmu, - [MO_BEUL] = helper_be_ldul_mmu, - [MO_BEUQ] = helper_be_ldq_mmu, -}; - -/* helper signature: helper_st_mmu(CPUState *env, target_ulong addr, - * uintxx_t val, int mmu_idx, uintptr_t ra) - */ -static void * const qemu_st_helpers[(MO_SIZE | MO_BSWAP) + 1] = { - [MO_UB] = helper_ret_stb_mmu, - [MO_LEUW] = helper_le_stw_mmu, - [MO_LEUL] = helper_le_stl_mmu, - [MO_LEUQ] = helper_le_stq_mmu, - [MO_BEUW] = helper_be_stw_mmu, - [MO_BEUL] = helper_be_stl_mmu, - [MO_BEUQ] = helper_be_stq_mmu, -}; - -/* We expect to use a 16-bit negative offset from ENV. */ -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -32768); - -/* Perform the TLB load and compare. Places the result of the comparison - in CR7, loads the addend of the TLB into R3, and returns the register - containing the guest address (zero-extended into R4). Clobbers R0 and R2. */ - -static TCGReg tcg_out_tlb_read(TCGContext *s, MemOp opc, - TCGReg addrlo, TCGReg addrhi, - int mem_index, bool is_read) +static TCGReg ldst_ra_gen(TCGContext *s, const TCGLabelQemuLdst *l, int arg) { - int cmp_off - = (is_read - ? offsetof(CPUTLBEntry, addr_read) - : offsetof(CPUTLBEntry, addr_write)); - int fast_off = TLB_MASK_TABLE_OFS(mem_index); - int mask_off = fast_off + offsetof(CPUTLBDescFast, mask); - int table_off = fast_off + offsetof(CPUTLBDescFast, table); - unsigned s_bits = opc & MO_SIZE; - unsigned a_bits = get_alignment_bits(opc); - - /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R3, TCG_AREG0, mask_off); - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R4, TCG_AREG0, table_off); - - /* Extract the page index, shifted into place for tlb index. */ - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_shri32(s, TCG_REG_TMP1, addrlo, - TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - } else { - tcg_out_shri64(s, TCG_REG_TMP1, addrlo, - TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + if (arg < 0) { + arg = TCG_REG_TMP1; } - tcg_out32(s, AND | SAB(TCG_REG_R3, TCG_REG_R3, TCG_REG_TMP1)); - - /* Load the TLB comparator. */ - if (cmp_off == 0 && TCG_TARGET_REG_BITS >= TARGET_LONG_BITS) { - uint32_t lxu = (TCG_TARGET_REG_BITS == 32 || TARGET_LONG_BITS == 32 - ? LWZUX : LDUX); - tcg_out32(s, lxu | TAB(TCG_REG_TMP1, TCG_REG_R3, TCG_REG_R4)); - } else { - tcg_out32(s, ADD | TAB(TCG_REG_R3, TCG_REG_R3, TCG_REG_R4)); - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_TMP1, TCG_REG_R3, cmp_off + 4); - tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_R4, TCG_REG_R3, cmp_off); - } else { - tcg_out_ld(s, TCG_TYPE_TL, TCG_REG_TMP1, TCG_REG_R3, cmp_off); - } - } - - /* Load the TLB addend for use on the fast path. Do this asap - to minimize any load use delay. */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R3, TCG_REG_R3, - offsetof(CPUTLBEntry, addend)); - - /* Clear the non-page, non-alignment bits from the address */ - if (TCG_TARGET_REG_BITS == 32) { - /* We don't support unaligned accesses on 32-bits. - * Preserve the bottom bits and thus trigger a comparison - * failure on unaligned accesses. - */ - if (a_bits < s_bits) { - a_bits = s_bits; - } - tcg_out_rlw(s, RLWINM, TCG_REG_R0, addrlo, 0, - (32 - a_bits) & 31, 31 - TARGET_PAGE_BITS); - } else { - TCGReg t = addrlo; - - /* If the access is unaligned, we need to make sure we fail if we - * cross a page boundary. The trick is to add the access size-1 - * to the address before masking the low bits. That will make the - * address overflow to the next page if we cross a page boundary, - * which will then force a mismatch of the TLB compare. - */ - if (a_bits < s_bits) { - unsigned a_mask = (1 << a_bits) - 1; - unsigned s_mask = (1 << s_bits) - 1; - tcg_out32(s, ADDI | TAI(TCG_REG_R0, t, s_mask - a_mask)); - t = TCG_REG_R0; - } - - /* Mask the address for the requested alignment. */ - if (TARGET_LONG_BITS == 32) { - tcg_out_rlw(s, RLWINM, TCG_REG_R0, t, 0, - (32 - a_bits) & 31, 31 - TARGET_PAGE_BITS); - /* Zero-extend the address for use in the final address. */ - tcg_out_ext32u(s, TCG_REG_R4, addrlo); - addrlo = TCG_REG_R4; - } else if (a_bits == 0) { - tcg_out_rld(s, RLDICR, TCG_REG_R0, t, 0, 63 - TARGET_PAGE_BITS); - } else { - tcg_out_rld(s, RLDICL, TCG_REG_R0, t, - 64 - TARGET_PAGE_BITS, TARGET_PAGE_BITS - a_bits); - tcg_out_rld(s, RLDICL, TCG_REG_R0, TCG_REG_R0, TARGET_PAGE_BITS, 0); - } - } - - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP1, - 0, 7, TCG_TYPE_I32); - tcg_out_cmp(s, TCG_COND_EQ, addrhi, TCG_REG_R4, 0, 6, TCG_TYPE_I32); - tcg_out32(s, CRAND | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); - } else { - tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP1, - 0, 7, TCG_TYPE_TL); - } - - return addrlo; + tcg_out32(s, MFSPR | RT(arg) | LR); + return arg; } -/* Record the context of a call to the out of line helper code for the slow - path for a load or store, so that we can later generate the correct - helper code. */ -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, MemOpIdx oi, - TCGReg datalo_reg, TCGReg datahi_reg, - TCGReg addrlo_reg, TCGReg addrhi_reg, - tcg_insn_unit *raddr, tcg_insn_unit *lptr) -{ - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->oi = oi; - label->datalo_reg = datalo_reg; - label->datahi_reg = datahi_reg; - label->addrlo_reg = addrlo_reg; - label->addrhi_reg = addrhi_reg; - label->raddr = tcg_splitwx_to_rx(raddr); - label->label_ptr[0] = lptr; -} +/* + * For the purposes of ppc32 sorting 4 input registers into 4 argument + * registers, there is an outside chance we would require 3 temps. + */ +static const TCGLdstHelperParam ldst_helper_param = { + .ra_gen = ldst_ra_gen, + .ntmp = 3, + .tmp = { TCG_REG_TMP1, TCG_REG_TMP2, TCG_REG_R0 } +}; static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { - MemOpIdx oi = lb->oi; - MemOp opc = get_memop(oi); - TCGReg hi, lo, arg = TCG_REG_R3; + MemOp opc = get_memop(lb->oi); if (!reloc_pc14(lb->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { return false; } - tcg_out_mov(s, TCG_TYPE_PTR, arg++, TCG_AREG0); - - lo = lb->addrlo_reg; - hi = lb->addrhi_reg; - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { -#ifdef TCG_TARGET_CALL_ALIGN_ARGS - arg |= 1; -#endif - tcg_out_mov(s, TCG_TYPE_I32, arg++, hi); - tcg_out_mov(s, TCG_TYPE_I32, arg++, lo); - } else { - /* If the address needed to be zero-extended, we'll have already - placed it in R4. The only remaining case is 64-bit guest. */ - tcg_out_mov(s, TCG_TYPE_TL, arg++, lo); - } - - tcg_out_movi(s, TCG_TYPE_I32, arg++, oi); - tcg_out32(s, MFSPR | RT(arg) | LR); - - tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SIZE)]); - - lo = lb->datalo_reg; - hi = lb->datahi_reg; - if (TCG_TARGET_REG_BITS == 32 && (opc & MO_SIZE) == MO_64) { - tcg_out_mov(s, TCG_TYPE_I32, lo, TCG_REG_R4); - tcg_out_mov(s, TCG_TYPE_I32, hi, TCG_REG_R3); - } else if (opc & MO_SIGN) { - uint32_t insn = qemu_exts_opc[opc & MO_SIZE]; - tcg_out32(s, insn | RA(lo) | RS(TCG_REG_R3)); - } else { - tcg_out_mov(s, TCG_TYPE_REG, lo, TCG_REG_R3); - } + tcg_out_ld_helper_args(s, lb, &ldst_helper_param); + tcg_out_call_int(s, LK, qemu_ld_helpers[opc & MO_SIZE]); + tcg_out_ld_helper_ret(s, lb, false, &ldst_helper_param); tcg_out_b(s, 0, lb->raddr); return true; @@ -2175,283 +2394,386 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { - MemOpIdx oi = lb->oi; - MemOp opc = get_memop(oi); - MemOp s_bits = opc & MO_SIZE; - TCGReg hi, lo, arg = TCG_REG_R3; + MemOp opc = get_memop(lb->oi); if (!reloc_pc14(lb->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { return false; } - tcg_out_mov(s, TCG_TYPE_PTR, arg++, TCG_AREG0); - - lo = lb->addrlo_reg; - hi = lb->addrhi_reg; - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { -#ifdef TCG_TARGET_CALL_ALIGN_ARGS - arg |= 1; -#endif - tcg_out_mov(s, TCG_TYPE_I32, arg++, hi); - tcg_out_mov(s, TCG_TYPE_I32, arg++, lo); - } else { - /* If the address needed to be zero-extended, we'll have already - placed it in R4. The only remaining case is 64-bit guest. */ - tcg_out_mov(s, TCG_TYPE_TL, arg++, lo); - } - - lo = lb->datalo_reg; - hi = lb->datahi_reg; - if (TCG_TARGET_REG_BITS == 32) { - switch (s_bits) { - case MO_64: -#ifdef TCG_TARGET_CALL_ALIGN_ARGS - arg |= 1; -#endif - tcg_out_mov(s, TCG_TYPE_I32, arg++, hi); - /* FALLTHRU */ - case MO_32: - tcg_out_mov(s, TCG_TYPE_I32, arg++, lo); - break; - default: - tcg_out_rlw(s, RLWINM, arg++, lo, 0, 32 - (8 << s_bits), 31); - break; - } - } else { - if (s_bits == MO_64) { - tcg_out_mov(s, TCG_TYPE_I64, arg++, lo); - } else { - tcg_out_rld(s, RLDICL, arg++, lo, 0, 64 - (8 << s_bits)); - } - } - - tcg_out_movi(s, TCG_TYPE_I32, arg++, oi); - tcg_out32(s, MFSPR | RT(arg) | LR); - - tcg_out_call(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]); + tcg_out_st_helper_args(s, lb, &ldst_helper_param); + tcg_out_call_int(s, LK, qemu_st_helpers[opc & MO_SIZE]); tcg_out_b(s, 0, lb->raddr); return true; } -#else -static void tcg_out_test_alignment(TCGContext *s, bool is_ld, TCGReg addrlo, - TCGReg addrhi, unsigned a_bits) +typedef struct { + TCGReg base; + TCGReg index; + TCGAtomAlign aa; +} HostAddress; + +bool tcg_target_has_memory_bswap(MemOp memop) { - unsigned a_mask = (1 << a_bits) - 1; - TCGLabelQemuLdst *label = new_ldst_label(s); + TCGAtomAlign aa; - label->is_ld = is_ld; - label->addrlo_reg = addrlo; - label->addrhi_reg = addrhi; - - /* We are expecting a_bits to max out at 7, much lower than ANDI. */ - tcg_debug_assert(a_bits < 16); - tcg_out32(s, ANDI | SAI(addrlo, TCG_REG_R0, a_mask)); - - label->label_ptr[0] = s->code_ptr; - tcg_out32(s, BC | BI(0, CR_EQ) | BO_COND_FALSE | LK); - - label->raddr = tcg_splitwx_to_rx(s->code_ptr); -} - -static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) -{ - if (!reloc_pc14(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { - return false; + if ((memop & MO_SIZE) <= MO_64) { + return true; } - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - TCGReg arg = TCG_REG_R4; -#ifdef TCG_TARGET_CALL_ALIGN_ARGS - arg |= 1; -#endif - if (l->addrlo_reg != arg) { - tcg_out_mov(s, TCG_TYPE_I32, arg, l->addrhi_reg); - tcg_out_mov(s, TCG_TYPE_I32, arg + 1, l->addrlo_reg); - } else if (l->addrhi_reg != arg + 1) { - tcg_out_mov(s, TCG_TYPE_I32, arg + 1, l->addrlo_reg); - tcg_out_mov(s, TCG_TYPE_I32, arg, l->addrhi_reg); - } else { - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R0, arg); - tcg_out_mov(s, TCG_TYPE_I32, arg, arg + 1); - tcg_out_mov(s, TCG_TYPE_I32, arg + 1, TCG_REG_R0); - } - } else { - tcg_out_mov(s, TCG_TYPE_TL, TCG_REG_R4, l->addrlo_reg); - } - tcg_out_mov(s, TCG_TYPE_TL, TCG_REG_R3, TCG_AREG0); - - /* "Tail call" to the helper, with the return address back inline. */ - tcg_out_call_int(s, 0, (const void *)(l->is_ld ? helper_unaligned_ld - : helper_unaligned_st)); - return true; + /* + * Reject 16-byte memop with 16-byte atomicity, + * but do allow a pair of 64-bit operations. + */ + aa = atom_and_align_for_opc(tcg_ctx, memop, MO_ATOM_IFALIGN, true); + return aa.atom <= MO_64; } -static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +/* We expect to use a 16-bit negative offset from ENV. */ +#define MIN_TLB_MASK_TABLE_OFS -32768 + +/* + * For system-mode, perform the TLB load and compare. + * For user-mode, perform any required alignment tests. + * In both cases, return a TCGLabelQemuLdst structure if the slow path + * is required and fill in @h with the host address for the fast path. + */ +static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, + TCGReg addrlo, TCGReg addrhi, + MemOpIdx oi, bool is_ld) { - return tcg_out_fail_alignment(s, l); -} + TCGType addr_type = s->addr_type; + TCGLabelQemuLdst *ldst = NULL; + MemOp opc = get_memop(oi); + MemOp a_bits, s_bits; -static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - return tcg_out_fail_alignment(s, l); -} - -#endif /* SOFTMMU */ - -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) -{ - TCGReg datalo, datahi, addrlo, rbase; - TCGReg addrhi __attribute__((unused)); - MemOpIdx oi; - MemOp opc, s_bits; -#ifdef CONFIG_SOFTMMU - int mem_index; - tcg_insn_unit *label_ptr; -#else - unsigned a_bits; -#endif - - datalo = *args++; - datahi = (TCG_TARGET_REG_BITS == 32 && is_64 ? *args++ : 0); - addrlo = *args++; - addrhi = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); + /* + * Book II, Section 1.4, Single-Copy Atomicity, specifies: + * + * Before 3.0, "An access that is not atomic is performed as a set of + * smaller disjoint atomic accesses. In general, the number and alignment + * of these accesses are implementation-dependent." Thus MO_ATOM_IFALIGN. + * + * As of 3.0, "the non-atomic access is performed as described in + * the corresponding list", which matches MO_ATOM_SUBALIGN. + */ s_bits = opc & MO_SIZE; + h->aa = atom_and_align_for_opc(s, opc, + have_isa_3_00 ? MO_ATOM_SUBALIGN + : MO_ATOM_IFALIGN, + s_bits == MO_128); + a_bits = h->aa.align; -#ifdef CONFIG_SOFTMMU - mem_index = get_mmuidx(oi); - addrlo = tcg_out_tlb_read(s, opc, addrlo, addrhi, mem_index, true); + if (tcg_use_softmmu) { + int mem_index = get_mmuidx(oi); + int cmp_off = is_ld ? offsetof(CPUTLBEntry, addr_read) + : offsetof(CPUTLBEntry, addr_write); + int fast_off = tlb_mask_table_ofs(s, mem_index); + int mask_off = fast_off + offsetof(CPUTLBDescFast, mask); + int table_off = fast_off + offsetof(CPUTLBDescFast, table); - /* Load a pointer into the current opcode w/conditional branch-link. */ - label_ptr = s->code_ptr; - tcg_out32(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addrlo; + ldst->addrhi_reg = addrhi; - rbase = TCG_REG_R3; -#else /* !CONFIG_SOFTMMU */ - a_bits = get_alignment_bits(opc); - if (a_bits) { - tcg_out_test_alignment(s, true, addrlo, addrhi, a_bits); - } - rbase = guest_base ? TCG_GUEST_BASE_REG : 0; - if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { - tcg_out_ext32u(s, TCG_REG_TMP1, addrlo); - addrlo = TCG_REG_TMP1; - } -#endif + /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_AREG0, mask_off); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP2, TCG_AREG0, table_off); - if (TCG_TARGET_REG_BITS == 32 && s_bits == MO_64) { - if (opc & MO_BSWAP) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); - tcg_out32(s, LWBRX | TAB(datalo, rbase, addrlo)); - tcg_out32(s, LWBRX | TAB(datahi, rbase, TCG_REG_R0)); - } else if (rbase != 0) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); - tcg_out32(s, LWZX | TAB(datahi, rbase, addrlo)); - tcg_out32(s, LWZX | TAB(datalo, rbase, TCG_REG_R0)); - } else if (addrlo == datahi) { - tcg_out32(s, LWZ | TAI(datalo, addrlo, 4)); - tcg_out32(s, LWZ | TAI(datahi, addrlo, 0)); + /* Extract the page index, shifted into place for tlb index. */ + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_shri32(s, TCG_REG_R0, addrlo, + s->page_bits - CPU_TLB_ENTRY_BITS); } else { - tcg_out32(s, LWZ | TAI(datahi, addrlo, 0)); - tcg_out32(s, LWZ | TAI(datalo, addrlo, 4)); + tcg_out_shri64(s, TCG_REG_R0, addrlo, + s->page_bits - CPU_TLB_ENTRY_BITS); + } + tcg_out32(s, AND | SAB(TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_R0)); + + /* + * Load the (low part) TLB comparator into TMP2. + * For 64-bit host, always load the entire 64-bit slot for simplicity. + * We will ignore the high bits with tcg_out_cmp(..., addr_type). + */ + if (TCG_TARGET_REG_BITS == 64) { + if (cmp_off == 0) { + tcg_out32(s, LDUX | TAB(TCG_REG_TMP2, + TCG_REG_TMP1, TCG_REG_TMP2)); + } else { + tcg_out32(s, ADD | TAB(TCG_REG_TMP1, + TCG_REG_TMP1, TCG_REG_TMP2)); + tcg_out_ld(s, TCG_TYPE_I64, TCG_REG_TMP2, + TCG_REG_TMP1, cmp_off); + } + } else if (cmp_off == 0 && !HOST_BIG_ENDIAN) { + tcg_out32(s, LWZUX | TAB(TCG_REG_TMP2, + TCG_REG_TMP1, TCG_REG_TMP2)); + } else { + tcg_out32(s, ADD | TAB(TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_TMP2)); + tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_TMP2, TCG_REG_TMP1, + cmp_off + 4 * HOST_BIG_ENDIAN); + } + + /* + * Load the TLB addend for use on the fast path. + * Do this asap to minimize any load use delay. + */ + if (TCG_TARGET_REG_BITS == 64 || addr_type == TCG_TYPE_I32) { + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_REG_TMP1, + offsetof(CPUTLBEntry, addend)); + } + + /* Clear the non-page, non-alignment bits from the address in R0. */ + if (TCG_TARGET_REG_BITS == 32) { + /* + * We don't support unaligned accesses on 32-bits. + * Preserve the bottom bits and thus trigger a comparison + * failure on unaligned accesses. + */ + if (a_bits < s_bits) { + a_bits = s_bits; + } + tcg_out_rlw(s, RLWINM, TCG_REG_R0, addrlo, 0, + (32 - a_bits) & 31, 31 - s->page_bits); + } else { + TCGReg t = addrlo; + + /* + * If the access is unaligned, we need to make sure we fail if we + * cross a page boundary. The trick is to add the access size-1 + * to the address before masking the low bits. That will make the + * address overflow to the next page if we cross a page boundary, + * which will then force a mismatch of the TLB compare. + */ + if (a_bits < s_bits) { + unsigned a_mask = (1 << a_bits) - 1; + unsigned s_mask = (1 << s_bits) - 1; + tcg_out32(s, ADDI | TAI(TCG_REG_R0, t, s_mask - a_mask)); + t = TCG_REG_R0; + } + + /* Mask the address for the requested alignment. */ + if (addr_type == TCG_TYPE_I32) { + tcg_out_rlw(s, RLWINM, TCG_REG_R0, t, 0, + (32 - a_bits) & 31, 31 - s->page_bits); + } else if (a_bits == 0) { + tcg_out_rld(s, RLDICR, TCG_REG_R0, t, 0, 63 - s->page_bits); + } else { + tcg_out_rld(s, RLDICL, TCG_REG_R0, t, + 64 - s->page_bits, s->page_bits - a_bits); + tcg_out_rld(s, RLDICL, TCG_REG_R0, TCG_REG_R0, s->page_bits, 0); + } + } + + if (TCG_TARGET_REG_BITS == 32 && addr_type != TCG_TYPE_I32) { + /* Low part comparison into cr7. */ + tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP2, + 0, 7, TCG_TYPE_I32); + + /* Load the high part TLB comparator into TMP2. */ + tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_TMP2, TCG_REG_TMP1, + cmp_off + 4 * !HOST_BIG_ENDIAN); + + /* Load addend, deferred for this case. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_REG_TMP1, + offsetof(CPUTLBEntry, addend)); + + /* High part comparison into cr6. */ + tcg_out_cmp(s, TCG_COND_EQ, addrhi, TCG_REG_TMP2, + 0, 6, TCG_TYPE_I32); + + /* Combine comparisons into cr0. */ + tcg_out32(s, CRAND | BT(0, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); + } else { + /* Full comparison into cr0. */ + tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP2, + 0, 0, addr_type); + } + + /* Load a pointer into the current opcode w/conditional branch-link. */ + ldst->label_ptr[0] = s->code_ptr; + tcg_out_bc(s, TCG_COND_NE, LK); + + h->base = TCG_REG_TMP1; + } else { + if (a_bits) { + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addrlo; + ldst->addrhi_reg = addrhi; + + /* We are expecting a_bits to max out at 7, much lower than ANDI. */ + tcg_debug_assert(a_bits < 16); + tcg_out32(s, ANDI | SAI(addrlo, TCG_REG_R0, (1 << a_bits) - 1)); + + ldst->label_ptr[0] = s->code_ptr; + tcg_out32(s, BC | BI(0, CR_EQ) | BO_COND_FALSE | LK); + } + + h->base = guest_base ? TCG_GUEST_BASE_REG : 0; + } + + if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { + /* Zero-extend the guest address for use in the host address. */ + tcg_out_ext32u(s, TCG_REG_R0, addrlo); + h->index = TCG_REG_R0; + } else { + h->index = addrlo; + } + + return ldst; +} + +static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg addrlo, TCGReg addrhi, + MemOpIdx oi, TCGType data_type) +{ + MemOp opc = get_memop(oi); + TCGLabelQemuLdst *ldst; + HostAddress h; + + ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, true); + + if (TCG_TARGET_REG_BITS == 32 && (opc & MO_SIZE) == MO_64) { + if (opc & MO_BSWAP) { + tcg_out32(s, ADDI | TAI(TCG_REG_R0, h.index, 4)); + tcg_out32(s, LWBRX | TAB(datalo, h.base, h.index)); + tcg_out32(s, LWBRX | TAB(datahi, h.base, TCG_REG_R0)); + } else if (h.base != 0) { + tcg_out32(s, ADDI | TAI(TCG_REG_R0, h.index, 4)); + tcg_out32(s, LWZX | TAB(datahi, h.base, h.index)); + tcg_out32(s, LWZX | TAB(datalo, h.base, TCG_REG_R0)); + } else if (h.index == datahi) { + tcg_out32(s, LWZ | TAI(datalo, h.index, 4)); + tcg_out32(s, LWZ | TAI(datahi, h.index, 0)); + } else { + tcg_out32(s, LWZ | TAI(datahi, h.index, 0)); + tcg_out32(s, LWZ | TAI(datalo, h.index, 4)); } } else { uint32_t insn = qemu_ldx_opc[opc & (MO_BSWAP | MO_SSIZE)]; if (!have_isa_2_06 && insn == LDBRX) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); - tcg_out32(s, LWBRX | TAB(datalo, rbase, addrlo)); - tcg_out32(s, LWBRX | TAB(TCG_REG_R0, rbase, TCG_REG_R0)); + tcg_out32(s, ADDI | TAI(TCG_REG_R0, h.index, 4)); + tcg_out32(s, LWBRX | TAB(datalo, h.base, h.index)); + tcg_out32(s, LWBRX | TAB(TCG_REG_R0, h.base, TCG_REG_R0)); tcg_out_rld(s, RLDIMI, datalo, TCG_REG_R0, 32, 0); } else if (insn) { - tcg_out32(s, insn | TAB(datalo, rbase, addrlo)); + tcg_out32(s, insn | TAB(datalo, h.base, h.index)); } else { insn = qemu_ldx_opc[opc & (MO_SIZE | MO_BSWAP)]; - tcg_out32(s, insn | TAB(datalo, rbase, addrlo)); - insn = qemu_exts_opc[s_bits]; - tcg_out32(s, insn | RA(datalo) | RS(datalo)); + tcg_out32(s, insn | TAB(datalo, h.base, h.index)); + tcg_out_movext(s, TCG_TYPE_REG, datalo, + TCG_TYPE_REG, opc & MO_SSIZE, datalo); } } -#ifdef CONFIG_SOFTMMU - add_qemu_ldst_label(s, true, oi, datalo, datahi, addrlo, addrhi, - s->code_ptr, label_ptr); -#endif + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = datalo; + ldst->datahi_reg = datahi; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } } -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) +static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg addrlo, TCGReg addrhi, + MemOpIdx oi, TCGType data_type) { - TCGReg datalo, datahi, addrlo, rbase; - TCGReg addrhi __attribute__((unused)); - MemOpIdx oi; - MemOp opc, s_bits; -#ifdef CONFIG_SOFTMMU - int mem_index; - tcg_insn_unit *label_ptr; -#else - unsigned a_bits; -#endif + MemOp opc = get_memop(oi); + TCGLabelQemuLdst *ldst; + HostAddress h; - datalo = *args++; - datahi = (TCG_TARGET_REG_BITS == 32 && is_64 ? *args++ : 0); - addrlo = *args++; - addrhi = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); - s_bits = opc & MO_SIZE; + ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, false); -#ifdef CONFIG_SOFTMMU - mem_index = get_mmuidx(oi); - addrlo = tcg_out_tlb_read(s, opc, addrlo, addrhi, mem_index, false); - - /* Load a pointer into the current opcode w/conditional branch-link. */ - label_ptr = s->code_ptr; - tcg_out32(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); - - rbase = TCG_REG_R3; -#else /* !CONFIG_SOFTMMU */ - a_bits = get_alignment_bits(opc); - if (a_bits) { - tcg_out_test_alignment(s, false, addrlo, addrhi, a_bits); - } - rbase = guest_base ? TCG_GUEST_BASE_REG : 0; - if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { - tcg_out_ext32u(s, TCG_REG_TMP1, addrlo); - addrlo = TCG_REG_TMP1; - } -#endif - - if (TCG_TARGET_REG_BITS == 32 && s_bits == MO_64) { + if (TCG_TARGET_REG_BITS == 32 && (opc & MO_SIZE) == MO_64) { if (opc & MO_BSWAP) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); - tcg_out32(s, STWBRX | SAB(datalo, rbase, addrlo)); - tcg_out32(s, STWBRX | SAB(datahi, rbase, TCG_REG_R0)); - } else if (rbase != 0) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); - tcg_out32(s, STWX | SAB(datahi, rbase, addrlo)); - tcg_out32(s, STWX | SAB(datalo, rbase, TCG_REG_R0)); + tcg_out32(s, ADDI | TAI(TCG_REG_R0, h.index, 4)); + tcg_out32(s, STWBRX | SAB(datalo, h.base, h.index)); + tcg_out32(s, STWBRX | SAB(datahi, h.base, TCG_REG_R0)); + } else if (h.base != 0) { + tcg_out32(s, ADDI | TAI(TCG_REG_R0, h.index, 4)); + tcg_out32(s, STWX | SAB(datahi, h.base, h.index)); + tcg_out32(s, STWX | SAB(datalo, h.base, TCG_REG_R0)); } else { - tcg_out32(s, STW | TAI(datahi, addrlo, 0)); - tcg_out32(s, STW | TAI(datalo, addrlo, 4)); + tcg_out32(s, STW | TAI(datahi, h.index, 0)); + tcg_out32(s, STW | TAI(datalo, h.index, 4)); } } else { uint32_t insn = qemu_stx_opc[opc & (MO_BSWAP | MO_SIZE)]; if (!have_isa_2_06 && insn == STDBRX) { - tcg_out32(s, STWBRX | SAB(datalo, rbase, addrlo)); - tcg_out32(s, ADDI | TAI(TCG_REG_TMP1, addrlo, 4)); + tcg_out32(s, STWBRX | SAB(datalo, h.base, h.index)); + tcg_out32(s, ADDI | TAI(TCG_REG_TMP1, h.index, 4)); tcg_out_shri64(s, TCG_REG_R0, datalo, 32); - tcg_out32(s, STWBRX | SAB(TCG_REG_R0, rbase, TCG_REG_TMP1)); + tcg_out32(s, STWBRX | SAB(TCG_REG_R0, h.base, TCG_REG_TMP1)); } else { - tcg_out32(s, insn | SAB(datalo, rbase, addrlo)); + tcg_out32(s, insn | SAB(datalo, h.base, h.index)); } } -#ifdef CONFIG_SOFTMMU - add_qemu_ldst_label(s, false, oi, datalo, datahi, addrlo, addrhi, - s->code_ptr, label_ptr); -#endif + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = datalo; + ldst->datahi_reg = datahi; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } +} + +static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg addr_reg, MemOpIdx oi, bool is_ld) +{ + TCGLabelQemuLdst *ldst; + HostAddress h; + bool need_bswap; + uint32_t insn; + TCGReg index; + + ldst = prepare_host_addr(s, &h, addr_reg, -1, oi, is_ld); + + /* Compose the final address, as LQ/STQ have no indexing. */ + index = h.index; + if (h.base != 0) { + index = TCG_REG_TMP1; + tcg_out32(s, ADD | TAB(index, h.base, h.index)); + } + need_bswap = get_memop(oi) & MO_BSWAP; + + if (h.aa.atom == MO_128) { + tcg_debug_assert(!need_bswap); + tcg_debug_assert(datalo & 1); + tcg_debug_assert(datahi == datalo - 1); + tcg_debug_assert(!is_ld || datahi != index); + insn = is_ld ? LQ : STQ; + tcg_out32(s, insn | TAI(datahi, index, 0)); + } else { + TCGReg d1, d2; + + if (HOST_BIG_ENDIAN ^ need_bswap) { + d1 = datahi, d2 = datalo; + } else { + d1 = datalo, d2 = datahi; + } + + if (need_bswap) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R0, 8); + insn = is_ld ? LDBRX : STDBRX; + tcg_out32(s, insn | TAB(d1, 0, index)); + tcg_out32(s, insn | TAB(d2, index, TCG_REG_R0)); + } else { + insn = is_ld ? LD : STD; + tcg_out32(s, insn | TAI(d1, index, 0)); + tcg_out32(s, insn | TAI(d2, index, 8)); + } + } + + if (ldst) { + ldst->type = TCG_TYPE_I128; + ldst->datalo_reg = datalo; + ldst->datahi_reg = datahi; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } } static void tcg_out_nop_fill(tcg_insn_unit *p, int count) @@ -2464,7 +2786,6 @@ static void tcg_out_nop_fill(tcg_insn_unit *p, int count) /* Parameters for function call generation, used in tcg.c. */ #define TCG_TARGET_STACK_ALIGN 16 -#define TCG_TARGET_EXTEND_ARGS 1 #ifdef _CALL_AIX # define LINK_AREA_SIZE (6 * SZR) @@ -2528,18 +2849,13 @@ static void tcg_target_qemu_prologue(TCGContext *s) } tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_R0, TCG_REG_R1, FRAME_SIZE+LR_OFFSET); -#ifndef CONFIG_SOFTMMU - if (guest_base) { + if (!tcg_use_softmmu && guest_base) { tcg_out_movi_int(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, guest_base, true); tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); } -#endif tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); tcg_out32(s, MTSPR | RS(tcg_target_call_iarg_regs[1]) | CTR); - if (USE_REG_TB) { - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_TB, tcg_target_call_iarg_regs[1]); - } tcg_out32(s, BCCTR | BO_ALWAYS); /* Epilogue */ @@ -2555,6 +2871,76 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out32(s, BCLR | BO_ALWAYS); } +static void tcg_out_tb_start(TCGContext *s) +{ + /* Load TCG_REG_TB. */ + if (USE_REG_TB) { + if (have_isa_3_00) { + /* lnia REG_TB */ + tcg_out_addpcis(s, TCG_REG_TB, 0); + } else { + /* bcl 20,31,$+4 (preferred form for getting nia) */ + tcg_out32(s, BC | BO_ALWAYS | BI(7, CR_SO) | 0x4 | LK); + tcg_out32(s, MFSPR | RT(TCG_REG_TB) | LR); + } + } +} + +static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg) +{ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R3, arg); + tcg_out_b(s, 0, tcg_code_gen_epilogue); +} + +static void tcg_out_goto_tb(TCGContext *s, int which) +{ + uintptr_t ptr = get_jmp_target_addr(s, which); + int16_t lo; + + /* Direct branch will be patched by tb_target_set_jmp_target. */ + set_jmp_insn_offset(s, which); + tcg_out32(s, NOP); + + /* When branch is out of range, fall through to indirect. */ + if (USE_REG_TB) { + ptrdiff_t offset = ppc_tbrel_diff(s, (void *)ptr); + tcg_out_mem_long(s, LD, LDX, TCG_REG_TMP1, TCG_REG_TB, offset); + } else if (have_isa_3_10) { + ptrdiff_t offset = tcg_pcrel_diff_for_prefix(s, (void *)ptr); + tcg_out_8ls_d(s, PLD, TCG_REG_TMP1, 0, offset, 1); + } else if (have_isa_3_00) { + ptrdiff_t offset = tcg_pcrel_diff(s, (void *)ptr) - 4; + lo = offset; + tcg_out_addpcis(s, TCG_REG_TMP1, offset - lo); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_REG_TMP1, lo); + } else { + lo = ptr; + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP1, ptr - lo); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_REG_TMP1, lo); + } + + tcg_out32(s, MTSPR | RS(TCG_REG_TMP1) | CTR); + tcg_out32(s, BCCTR | BO_ALWAYS); + set_jmp_reset_offset(s, which); +} + +void tb_target_set_jmp_target(const TranslationBlock *tb, int n, + uintptr_t jmp_rx, uintptr_t jmp_rw) +{ + uintptr_t addr = tb->jmp_target_addr[n]; + intptr_t diff = addr - jmp_rx; + tcg_insn_unit insn; + + if (in_range_b(diff)) { + insn = B | (diff & 0x3fffffc); + } else { + insn = NOP; + } + + qatomic_set((uint32_t *)jmp_rw, insn); + flush_idcache_range(jmp_rx, jmp_rw, 4); +} + static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2562,47 +2948,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGArg a0, a1, a2; switch (opc) { - case INDEX_op_exit_tb: - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R3, args[0]); - tcg_out_b(s, 0, tcg_code_gen_epilogue); - break; - case INDEX_op_goto_tb: - if (s->tb_jmp_insn_offset) { - /* Direct jump. */ - if (TCG_TARGET_REG_BITS == 64) { - /* Ensure the next insns are 8-byte aligned. */ - if ((uintptr_t)s->code_ptr & 7) { - tcg_out32(s, NOP); - } - s->tb_jmp_insn_offset[args[0]] = tcg_current_code_size(s); - tcg_out32(s, ADDIS | TAI(TCG_REG_TB, TCG_REG_TB, 0)); - tcg_out32(s, ADDI | TAI(TCG_REG_TB, TCG_REG_TB, 0)); - } else { - s->tb_jmp_insn_offset[args[0]] = tcg_current_code_size(s); - tcg_out32(s, B); - s->tb_jmp_reset_offset[args[0]] = tcg_current_code_size(s); - break; - } - } else { - /* Indirect jump. */ - tcg_debug_assert(s->tb_jmp_insn_offset == NULL); - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TB, 0, - (intptr_t)(s->tb_jmp_insn_offset + args[0])); - } - tcg_out32(s, MTSPR | RS(TCG_REG_TB) | CTR); - tcg_out32(s, BCCTR | BO_ALWAYS); - set_jmp_reset_offset(s, args[0]); - if (USE_REG_TB) { - /* For the unlinked case, need to reset TCG_REG_TB. */ - tcg_out_mem_long(s, ADDI, ADD, TCG_REG_TB, TCG_REG_TB, - -tcg_current_code_size(s)); - } - break; case INDEX_op_goto_ptr: tcg_out32(s, MTSPR | RS(args[0]) | CTR); - if (USE_REG_TB) { - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_TB, args[0]); - } tcg_out32(s, ADDI | TAI(TCG_REG_R3, 0, 0)); tcg_out32(s, BCCTR | BO_ALWAYS); break; @@ -2627,7 +2974,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); - tcg_out_ext8s(s, args[0], args[0]); + tcg_out_ext8s(s, TCG_TYPE_REG, args[0], args[0]); break; case INDEX_op_ld16u_i32: case INDEX_op_ld16u_i64: @@ -2806,6 +3153,14 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out32(s, DIVWU | TAB(args[0], args[1], args[2])); break; + case INDEX_op_rem_i32: + tcg_out32(s, MODSW | TAB(args[0], args[1], args[2])); + break; + + case INDEX_op_remu_i32: + tcg_out32(s, MODUW | TAB(args[0], args[1], args[2])); + break; + case INDEX_op_shl_i32: if (const_args[2]) { /* Limit immediate shift count lest we create an illegal insn. */ @@ -2947,43 +3302,96 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_divu_i64: tcg_out32(s, DIVDU | TAB(args[0], args[1], args[2])); break; - - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args, false); + case INDEX_op_rem_i64: + tcg_out32(s, MODSD | TAB(args[0], args[1], args[2])); break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args, true); - break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args, false); - break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args, true); + case INDEX_op_remu_i64: + tcg_out32(s, MODUD | TAB(args[0], args[1], args[2])); break; - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - tcg_out_ext8s(s, args[0], args[1]); + case INDEX_op_qemu_ld_a64_i32: + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], + args[3], TCG_TYPE_I32); + break; + } + /* fall through */ + case INDEX_op_qemu_ld_a32_i32: + tcg_out_qemu_ld(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I32); break; - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - tcg_out_ext16s(s, args[0], args[1]); + case INDEX_op_qemu_ld_a32_i64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_qemu_ld(s, args[0], -1, args[1], -1, + args[2], TCG_TYPE_I64); + } else { + tcg_out_qemu_ld(s, args[0], args[1], args[2], -1, + args[3], TCG_TYPE_I64); + } break; - case INDEX_op_ext_i32_i64: - case INDEX_op_ext32s_i64: - tcg_out_ext32s(s, args[0], args[1]); + case INDEX_op_qemu_ld_a64_i64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_qemu_ld(s, args[0], -1, args[1], -1, + args[2], TCG_TYPE_I64); + } else { + tcg_out_qemu_ld(s, args[0], args[1], args[2], args[3], + args[4], TCG_TYPE_I64); + } break; - case INDEX_op_extu_i32_i64: - tcg_out_ext32u(s, args[0], args[1]); + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], true); + break; + + case INDEX_op_qemu_st_a64_i32: + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_qemu_st(s, args[0], -1, args[1], args[2], + args[3], TCG_TYPE_I32); + break; + } + /* fall through */ + case INDEX_op_qemu_st_a32_i32: + tcg_out_qemu_st(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I32); + break; + case INDEX_op_qemu_st_a32_i64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_qemu_st(s, args[0], -1, args[1], -1, + args[2], TCG_TYPE_I64); + } else { + tcg_out_qemu_st(s, args[0], args[1], args[2], -1, + args[3], TCG_TYPE_I64); + } + break; + case INDEX_op_qemu_st_a64_i64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_qemu_st(s, args[0], -1, args[1], -1, + args[2], TCG_TYPE_I64); + } else { + tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], + args[4], TCG_TYPE_I64); + } + break; + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; case INDEX_op_setcond_i32: tcg_out_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], args[2], - const_args[2]); + const_args[2], false); break; case INDEX_op_setcond_i64: tcg_out_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], args[2], - const_args[2]); + const_args[2], false); + break; + case INDEX_op_negsetcond_i32: + tcg_out_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], args[2], + const_args[2], true); + break; + case INDEX_op_negsetcond_i64: + tcg_out_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], args[2], + const_args[2], true); break; case INDEX_op_setcond2_i32: tcg_out_setcond2(s, args, const_args); @@ -3110,8 +3518,23 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ + case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ + case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ + case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ + case INDEX_op_ext8s_i64: + case INDEX_op_ext8u_i32: + case INDEX_op_ext8u_i64: + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + case INDEX_op_ext16u_i32: + case INDEX_op_ext16u_i64: + case INDEX_op_ext32s_i64: + case INDEX_op_ext32u_i64: + case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: default: - tcg_abort(); + g_assert_not_reached(); } } @@ -3569,7 +3992,7 @@ static void expand_vec_mul(TCGType type, unsigned vece, TCGv_vec v0, tcgv_vec_arg(t1), tcgv_vec_arg(t2)); vec_gen_3(INDEX_op_ppc_pkum_vec, type, vece, tcgv_vec_arg(v0), tcgv_vec_arg(v0), tcgv_vec_arg(t1)); - break; + break; case MO_32: tcg_debug_assert(!have_isa_2_07); @@ -3705,7 +4128,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: - case INDEX_op_setcond_i32: case INDEX_op_and_i64: case INDEX_op_andc_i64: case INDEX_op_shl_i64: @@ -3713,7 +4135,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_sar_i64: case INDEX_op_rotl_i64: case INDEX_op_rotr_i64: - case INDEX_op_setcond_i64: return C_O1_I2(r, r, ri); case INDEX_op_mul_i32: @@ -3722,6 +4143,8 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_div_i32: case INDEX_op_divu_i32: + case INDEX_op_rem_i32: + case INDEX_op_remu_i32: case INDEX_op_nand_i32: case INDEX_op_nor_i32: case INDEX_op_muluh_i32: @@ -3732,6 +4155,8 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_nor_i64: case INDEX_op_div_i64: case INDEX_op_divu_i64: + case INDEX_op_rem_i64: + case INDEX_op_remu_i64: case INDEX_op_mulsh_i64: case INDEX_op_muluh_i64: return C_O1_I2(r, r, r); @@ -3753,11 +4178,16 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: - return C_O0_I2(r, ri); - + return C_O0_I2(r, rC); + case INDEX_op_setcond_i32: + case INDEX_op_setcond_i64: + case INDEX_op_negsetcond_i32: + case INDEX_op_negsetcond_i64: + return C_O1_I2(r, r, rC); case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: - return C_O1_I4(r, r, ri, rZ, rZ); + return C_O1_I4(r, r, rC, rZ, rZ); + case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: return C_O1_I2(r, 0, rZ); @@ -3772,25 +4202,30 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_sub2_i32: return C_O2_I4(r, r, rI, rZM, r, r); - case INDEX_op_qemu_ld_i32: - return (TCG_TARGET_REG_BITS == 64 || TARGET_LONG_BITS == 32 - ? C_O1_I1(r, L) - : C_O1_I2(r, L, L)); + case INDEX_op_qemu_ld_a32_i32: + return C_O1_I1(r, r); + case INDEX_op_qemu_ld_a64_i32: + return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O1_I2(r, r, r); + case INDEX_op_qemu_ld_a32_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I1(r, r, r); + case INDEX_op_qemu_ld_a64_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I2(r, r, r, r); - case INDEX_op_qemu_st_i32: - return (TCG_TARGET_REG_BITS == 64 || TARGET_LONG_BITS == 32 - ? C_O0_I2(S, S) - : C_O0_I3(S, S, S)); + case INDEX_op_qemu_st_a32_i32: + return C_O0_I2(r, r); + case INDEX_op_qemu_st_a64_i32: + return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I3(r, r, r); + case INDEX_op_qemu_st_a32_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I3(r, r, r); + case INDEX_op_qemu_st_a64_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I4(r, r, r, r); - case INDEX_op_qemu_ld_i64: - return (TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, L) - : TARGET_LONG_BITS == 32 ? C_O2_I1(L, L, L) - : C_O2_I2(L, L, L, L)); - - case INDEX_op_qemu_st_i64: - return (TCG_TARGET_REG_BITS == 64 ? C_O0_I2(S, S) - : TARGET_LONG_BITS == 32 ? C_O0_I3(S, S, S) - : C_O0_I4(S, S, S, S)); + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: + return C_N1O1_I1(o, m, r); + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: + return C_O0_I3(o, m, r); case INDEX_op_add_vec: case INDEX_op_sub_vec: @@ -3850,45 +4285,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) static void tcg_target_init(TCGContext *s) { - unsigned long hwcap = qemu_getauxval(AT_HWCAP); - unsigned long hwcap2 = qemu_getauxval(AT_HWCAP2); - - have_isa = tcg_isa_base; - if (hwcap & PPC_FEATURE_ARCH_2_06) { - have_isa = tcg_isa_2_06; - } -#ifdef PPC_FEATURE2_ARCH_2_07 - if (hwcap2 & PPC_FEATURE2_ARCH_2_07) { - have_isa = tcg_isa_2_07; - } -#endif -#ifdef PPC_FEATURE2_ARCH_3_00 - if (hwcap2 & PPC_FEATURE2_ARCH_3_00) { - have_isa = tcg_isa_3_00; - } -#endif -#ifdef PPC_FEATURE2_ARCH_3_10 - if (hwcap2 & PPC_FEATURE2_ARCH_3_10) { - have_isa = tcg_isa_3_10; - } -#endif - -#ifdef PPC_FEATURE2_HAS_ISEL - /* Prefer explicit instruction from the kernel. */ - have_isel = (hwcap2 & PPC_FEATURE2_HAS_ISEL) != 0; -#else - /* Fall back to knowing Power7 (2.06) has ISEL. */ - have_isel = have_isa_2_06; -#endif - - if (hwcap & PPC_FEATURE_HAS_ALTIVEC) { - have_altivec = true; - /* We only care about the portion of VSX that overlaps Altivec. */ - if (hwcap & PPC_FEATURE_HAS_VSX) { - have_vsx = true; - } - } - tcg_target_available_regs[TCG_TYPE_I32] = 0xffffffff; tcg_target_available_regs[TCG_TYPE_I64] = 0xffffffff; if (have_altivec) { @@ -3940,7 +4336,8 @@ static void tcg_target_init(TCGContext *s) #if defined(_CALL_SYSV) || TCG_TARGET_REG_BITS == 64 tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); /* thread pointer */ #endif - tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP1); /* mem temp */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP1); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP2); tcg_regset_set_reg(s->reserved_regs, TCG_VEC_TMP1); tcg_regset_set_reg(s->reserved_regs, TCG_VEC_TMP2); if (USE_REG_TB) { diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index e6cf72503f..04a7aba4d3 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -25,17 +25,12 @@ #ifndef PPC_TCG_TARGET_H #define PPC_TCG_TARGET_H -#ifdef _ARCH_PPC64 -# define TCG_TARGET_REG_BITS 64 -# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB) -#else -# define TCG_TARGET_REG_BITS 32 -# define MAX_CODE_GEN_BUFFER_SIZE (32 * MiB) -#endif +#include "host/cpuinfo.h" + +#define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) #define TCG_TARGET_NB_REGS 64 #define TCG_TARGET_INSN_UNIT_SIZE 4 -#define TCG_TARGET_TLB_DISPLACEMENT_BITS 16 typedef enum { TCG_REG_R0, TCG_REG_R1, TCG_REG_R2, TCG_REG_R3, @@ -68,14 +63,12 @@ typedef enum { tcg_isa_3_10, } TCGPowerISA; -extern TCGPowerISA have_isa; -extern bool have_altivec; -extern bool have_vsx; - -#define have_isa_2_06 (have_isa >= tcg_isa_2_06) -#define have_isa_2_07 (have_isa >= tcg_isa_2_07) -#define have_isa_3_00 (have_isa >= tcg_isa_3_00) -#define have_isa_3_10 (have_isa >= tcg_isa_3_10) +#define have_isa_2_06 (cpuinfo & CPUINFO_V2_06) +#define have_isa_2_07 (cpuinfo & CPUINFO_V2_07) +#define have_isa_3_00 (cpuinfo & CPUINFO_V3_0) +#define have_isa_3_10 (cpuinfo & CPUINFO_V3_1) +#define have_altivec (cpuinfo & CPUINFO_ALTIVEC) +#define have_vsx (cpuinfo & CPUINFO_VSX) /* optional instructions automatically implemented */ #define TCG_TARGET_HAS_ext8u_i32 0 /* andi */ @@ -83,14 +76,13 @@ extern bool have_vsx; /* optional instructions */ #define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rem_i32 0 +#define TCG_TARGET_HAS_rem_i32 have_isa_3_00 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_ext8s_i32 1 #define TCG_TARGET_HAS_ext16s_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_eqv_i32 1 @@ -103,21 +95,19 @@ extern bool have_vsx; #define TCG_TARGET_HAS_extract_i32 1 #define TCG_TARGET_HAS_sextract_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_movcond_i32 1 +#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_muluh_i32 1 #define TCG_TARGET_HAS_mulsh_i32 1 -#define TCG_TARGET_HAS_direct_jump 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 -#define TCG_TARGET_HAS_extrl_i64_i32 0 -#define TCG_TARGET_HAS_extrh_i64_i32 0 +#define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_div_i64 1 -#define TCG_TARGET_HAS_rem_i64 0 +#define TCG_TARGET_HAS_rem_i64 have_isa_3_00 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_ext8s_i64 1 #define TCG_TARGET_HAS_ext16s_i64 1 @@ -129,7 +119,6 @@ extern bool have_vsx; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_eqv_i64 1 @@ -142,7 +131,7 @@ extern bool have_vsx; #define TCG_TARGET_HAS_extract_i64 1 #define TCG_TARGET_HAS_sextract_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_movcond_i64 1 +#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 @@ -151,6 +140,11 @@ extern bool have_vsx; #define TCG_TARGET_HAS_mulsh_i64 1 #endif +#define TCG_TARGET_HAS_qemu_ldst_i128 \ + (TCG_TARGET_REG_BITS == 64 && have_isa_2_07) + +#define TCG_TARGET_HAS_tst 1 + /* * While technically Altivec could support V64, it has no 64-bit store * instruction and substituting two 32-bit stores makes the generated @@ -180,11 +174,7 @@ extern bool have_vsx; #define TCG_TARGET_HAS_bitsel_vec have_vsx #define TCG_TARGET_HAS_cmpsel_vec 0 -void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t, uintptr_t); - #define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_HAS_MEMORY_BSWAP 1 - #define TCG_TARGET_NEED_LDST_LABELS #define TCG_TARGET_NEED_POOL_LABELS diff --git a/tcg/region.c b/tcg/region.c index 71ea81d671..478ec051c4 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -28,15 +28,27 @@ #include "qemu/mprotect.h" #include "qemu/memalign.h" #include "qemu/cacheinfo.h" +#include "qemu/qtree.h" #include "qapi/error.h" -#include "exec/exec-all.h" #include "tcg/tcg.h" +#include "exec/translation-block.h" #include "tcg-internal.h" +#include "host/cpuinfo.h" +/* + * Local source-level compatibility with Unix. + * Used by tcg_region_init below. + */ +#if defined(_WIN32) +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 +#endif + struct tcg_region_tree { QemuMutex lock; - GTree *tree; + QTree *tree; /* padding to avoid false sharing is computed at run-time */ }; @@ -82,6 +94,18 @@ bool in_code_gen_buffer(const void *p) return (size_t)(p - region.start_aligned) <= region.total_size; } +#ifndef CONFIG_TCG_INTERPRETER +static int host_prot_read_exec(void) +{ +#if defined(CONFIG_LINUX) && defined(HOST_AARCH64) && defined(PROT_BTI) + if (cpuinfo & CPUINFO_BTI) { + return PROT_READ | PROT_EXEC | PROT_BTI; + } +#endif + return PROT_READ | PROT_EXEC; +} +#endif + #ifdef CONFIG_DEBUG_TCG const void *tcg_splitwx_to_rx(void *rw) { @@ -163,7 +187,7 @@ static void tcg_region_trees_init(void) struct tcg_region_tree *rt = region_trees + i * tree_size; qemu_mutex_init(&rt->lock); - rt->tree = g_tree_new_full(tb_tc_cmp, NULL, NULL, tb_destroy); + rt->tree = q_tree_new_full(tb_tc_cmp, NULL, NULL, tb_destroy); } } @@ -202,7 +226,7 @@ void tcg_tb_insert(TranslationBlock *tb) g_assert(rt != NULL); qemu_mutex_lock(&rt->lock); - g_tree_insert(rt->tree, &tb->tc, tb); + q_tree_insert(rt->tree, &tb->tc, tb); qemu_mutex_unlock(&rt->lock); } @@ -212,7 +236,7 @@ void tcg_tb_remove(TranslationBlock *tb) g_assert(rt != NULL); qemu_mutex_lock(&rt->lock); - g_tree_remove(rt->tree, &tb->tc); + q_tree_remove(rt->tree, &tb->tc); qemu_mutex_unlock(&rt->lock); } @@ -232,7 +256,7 @@ TranslationBlock *tcg_tb_lookup(uintptr_t tc_ptr) } qemu_mutex_lock(&rt->lock); - tb = g_tree_lookup(rt->tree, &s); + tb = q_tree_lookup(rt->tree, &s); qemu_mutex_unlock(&rt->lock); return tb; } @@ -267,7 +291,7 @@ void tcg_tb_foreach(GTraverseFunc func, gpointer user_data) for (i = 0; i < region.n; i++) { struct tcg_region_tree *rt = region_trees + i * tree_size; - g_tree_foreach(rt->tree, func, user_data); + q_tree_foreach(rt->tree, func, user_data); } tcg_region_tree_unlock_all(); } @@ -281,7 +305,7 @@ size_t tcg_nb_tbs(void) for (i = 0; i < region.n; i++) { struct tcg_region_tree *rt = region_trees + i * tree_size; - nb_tbs += g_tree_nnodes(rt->tree); + nb_tbs += q_tree_nnodes(rt->tree); } tcg_region_tree_unlock_all(); return nb_tbs; @@ -296,8 +320,8 @@ static void tcg_region_tree_reset_all(void) struct tcg_region_tree *rt = region_trees + i * tree_size; /* Increment the refcount first so that destroy acts as a reset */ - g_tree_ref(rt->tree); - g_tree_destroy(rt->tree); + q_tree_ref(rt->tree); + q_tree_destroy(rt->tree); } tcg_region_tree_unlock_all(); } @@ -524,7 +548,7 @@ static int alloc_code_gen_buffer(size_t size, int splitwx, Error **errp) region.start_aligned = buf; region.total_size = size; - return PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return PROT_READ | PROT_WRITE | PROT_EXEC; } #else static int alloc_code_gen_buffer_anon(size_t size, int prot, @@ -548,7 +572,7 @@ static int alloc_code_gen_buffer_anon(size_t size, int prot, #ifdef CONFIG_POSIX #include "qemu/memfd.h" -static bool alloc_code_gen_buffer_splitwx_memfd(size_t size, Error **errp) +static int alloc_code_gen_buffer_splitwx_memfd(size_t size, Error **errp) { void *buf_rw = NULL, *buf_rx = MAP_FAILED; int fd = -1; @@ -558,9 +582,11 @@ static bool alloc_code_gen_buffer_splitwx_memfd(size_t size, Error **errp) goto fail; } - buf_rx = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0); + buf_rx = mmap(NULL, size, host_prot_read_exec(), MAP_SHARED, fd, 0); if (buf_rx == MAP_FAILED) { - goto fail_rx; + error_setg_errno(errp, errno, + "failed to map shared memory for execute"); + goto fail; } close(fd); @@ -570,12 +596,8 @@ static bool alloc_code_gen_buffer_splitwx_memfd(size_t size, Error **errp) return PROT_READ | PROT_WRITE; - fail_rx: - error_setg_errno(errp, errno, "failed to map shared memory for execute"); fail: - if (buf_rx != MAP_FAILED) { - munmap(buf_rx, size); - } + /* buf_rx is always equal to MAP_FAILED here and does not require cleanup */ if (buf_rw) { munmap(buf_rw, size); } @@ -633,7 +655,7 @@ static int alloc_code_gen_buffer_splitwx_vmremap(size_t size, Error **errp) return -1; } - if (mprotect((void *)buf_rx, size, PROT_READ | PROT_EXEC) != 0) { + if (mprotect((void *)buf_rx, size, host_prot_read_exec()) != 0) { error_setg_errno(errp, errno, "mprotect for jit splitwx"); munmap((void *)buf_rx, size); munmap((void *)buf_rw, size); @@ -709,7 +731,7 @@ static int alloc_code_gen_buffer(size_t size, int splitwx, Error **errp) * and then assigning regions to TCG threads so that the threads can translate * code in parallel without synchronization. * - * In softmmu the number of TCG threads is bounded by max_cpus, so we use at + * In system-mode the number of TCG threads is bounded by max_cpus, so we use at * least max_cpus regions in MTTCG. In !MTTCG we use a single region. * Note that the TCG options from the command-line (i.e. -accel accel=tcg,[...]) * must have been parsed before calling this function, since it calls @@ -725,7 +747,7 @@ static int alloc_code_gen_buffer(size_t size, int splitwx, Error **errp) * * However, this user-mode limitation is unlikely to be a significant problem * in practice. Multi-threaded guests share most if not all of their translated - * code, which makes parallel code generation less appealing than in softmmu. + * code, which makes parallel code generation less appealing than in system-mode */ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus) { @@ -793,10 +815,10 @@ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus) * buffer -- let that one use hugepages throughout. * Work with the page protections set up with the initial mapping. */ - need_prot = PAGE_READ | PAGE_WRITE; + need_prot = PROT_READ | PROT_WRITE; #ifndef CONFIG_TCG_INTERPRETER if (tcg_splitwx_diff == 0) { - need_prot |= PAGE_EXEC; + need_prot |= host_prot_read_exec(); } #endif for (size_t i = 0, n = region.n; i < n; i++) { @@ -806,12 +828,16 @@ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus) if (have_prot != need_prot) { int rc; - if (need_prot == (PAGE_READ | PAGE_WRITE | PAGE_EXEC)) { + if (need_prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) { rc = qemu_mprotect_rwx(start, end - start); - } else if (need_prot == (PAGE_READ | PAGE_WRITE)) { + } else if (need_prot == (PROT_READ | PROT_WRITE)) { rc = qemu_mprotect_rw(start, end - start); } else { +#ifdef CONFIG_POSIX + rc = mprotect(start, end - start, need_prot); +#else g_assert_not_reached(); +#endif } if (rc) { error_setg_errno(&error_fatal, errno, diff --git a/tcg/riscv/tcg-target-con-set.h b/tcg/riscv/tcg-target-con-set.h index cf0ac4d751..aac5ceee2b 100644 --- a/tcg/riscv/tcg-target-con-set.h +++ b/tcg/riscv/tcg-target-con-set.h @@ -10,21 +10,14 @@ * tcg-target-con-str.h; the constraint combination is inclusive or. */ C_O0_I1(r) -C_O0_I2(LZ, L) C_O0_I2(rZ, r) C_O0_I2(rZ, rZ) -C_O0_I3(LZ, L, L) -C_O0_I3(LZ, LZ, L) -C_O0_I4(LZ, LZ, L, L) -C_O0_I4(rZ, rZ, rZ, rZ) -C_O1_I1(r, L) C_O1_I1(r, r) -C_O1_I2(r, L, L) C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) +C_O1_I2(r, r, rJ) C_O1_I2(r, rZ, rN) C_O1_I2(r, rZ, rZ) -C_O1_I4(r, rZ, rZ, rZ, rZ) -C_O2_I1(r, r, L) -C_O2_I2(r, r, L, L) +C_N1_I2(r, r, rM) +C_O1_I4(r, r, rI, rM, rM) C_O2_I4(r, r, rZ, rZ, rM, rM) diff --git a/tcg/riscv/tcg-target-con-str.h b/tcg/riscv/tcg-target-con-str.h index 8d8afaee53..d5c419dff1 100644 --- a/tcg/riscv/tcg-target-con-str.h +++ b/tcg/riscv/tcg-target-con-str.h @@ -9,13 +9,13 @@ * REGS(letter, register_mask) */ REGS('r', ALL_GENERAL_REGS) -REGS('L', ALL_GENERAL_REGS & ~SOFTMMU_RESERVE_REGS) /* * Define constraint letters for constants: * CONST(letter, TCG_CT_CONST_* bit set) */ CONST('I', TCG_CT_CONST_S12) +CONST('J', TCG_CT_CONST_J12) CONST('N', TCG_CT_CONST_N12) CONST('M', TCG_CT_CONST_M12) CONST('Z', TCG_CT_CONST_ZERO) diff --git a/tcg/riscv/tcg-target-reg-bits.h b/tcg/riscv/tcg-target-reg-bits.h new file mode 100644 index 0000000000..761ca0d774 --- /dev/null +++ b/tcg/riscv/tcg-target-reg-bits.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2018 SiFive, Inc + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +/* + * We don't support oversize guests. + * Since we will only build tcg once, this in turn requires a 64-bit host. + */ +#if __riscv_xlen != 64 +#error "unsupported code generation mode" +#endif +#define TCG_TARGET_REG_BITS 64 + +#endif diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 81a83e45b1..639363039b 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -69,7 +69,7 @@ static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { static const int tcg_target_reg_alloc_order[] = { /* Call saved registers */ - /* TCG_REG_S0 reservered for TCG_AREG0 */ + /* TCG_REG_S0 reserved for TCG_AREG0 */ TCG_REG_S1, TCG_REG_S2, TCG_REG_S3, @@ -113,40 +113,40 @@ static const int tcg_target_call_iarg_regs[] = { TCG_REG_A7, }; -static const int tcg_target_call_oarg_regs[] = { - TCG_REG_A0, - TCG_REG_A1, -}; +#ifndef have_zbb +bool have_zbb; +#endif +#if defined(__riscv_arch_test) && defined(__riscv_zba) +# define have_zba true +#else +static bool have_zba; +#endif +#if defined(__riscv_arch_test) && defined(__riscv_zicond) +# define have_zicond true +#else +static bool have_zicond; +#endif + +static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) +{ + tcg_debug_assert(kind == TCG_CALL_RET_NORMAL); + tcg_debug_assert(slot >= 0 && slot <= 1); + return TCG_REG_A0 + slot; +} #define TCG_CT_CONST_ZERO 0x100 #define TCG_CT_CONST_S12 0x200 #define TCG_CT_CONST_N12 0x400 #define TCG_CT_CONST_M12 0x800 +#define TCG_CT_CONST_J12 0x1000 -#define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) -/* - * For softmmu, we need to avoid conflicts with the first 5 - * argument registers to call the helper. Some of these are - * also used for the tlb lookup. - */ -#ifdef CONFIG_SOFTMMU -#define SOFTMMU_RESERVE_REGS MAKE_64BIT_MASK(TCG_REG_A0, 5) -#else -#define SOFTMMU_RESERVE_REGS 0 -#endif +#define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) - -static inline tcg_target_long sextreg(tcg_target_long val, int pos, int len) -{ - if (TCG_TARGET_REG_BITS == 32) { - return sextract32(val, pos, len); - } else { - return sextract64(val, pos, len); - } -} +#define sextreg sextract64 /* test if a constant matches the constraint */ -static bool tcg_target_const_match(int64_t val, TCGType type, int ct) +static bool tcg_target_const_match(int64_t val, int ct, + TCGType type, TCGCond cond, int vece) { if (ct & TCG_CT_CONST) { return 1; @@ -154,13 +154,33 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) if ((ct & TCG_CT_CONST_ZERO) && val == 0) { return 1; } - if ((ct & TCG_CT_CONST_S12) && val == sextreg(val, 0, 12)) { + /* + * Sign extended from 12 bits: [-0x800, 0x7ff]. + * Used for most arithmetic, as this is the isa field. + */ + if ((ct & TCG_CT_CONST_S12) && val >= -0x800 && val <= 0x7ff) { return 1; } - if ((ct & TCG_CT_CONST_N12) && -val == sextreg(-val, 0, 12)) { + /* + * Sign extended from 12 bits, negated: [-0x7ff, 0x800]. + * Used for subtraction, where a constant must be handled by ADDI. + */ + if ((ct & TCG_CT_CONST_N12) && val >= -0x7ff && val <= 0x800) { return 1; } - if ((ct & TCG_CT_CONST_M12) && val >= -0xfff && val <= 0xfff) { + /* + * Sign extended from 12 bits, +/- matching: [-0x7ff, 0x7ff]. + * Used by addsub2 and movcond, which may need the negative value, + * and requires the modified constant to be representable. + */ + if ((ct & TCG_CT_CONST_M12) && val >= -0x7ff && val <= 0x7ff) { + return 1; + } + /* + * Inverse of sign extended from 12 bits: ~[-0x800, 0x7ff]. + * Used to map ANDN back to ANDI, etc. + */ + if ((ct & TCG_CT_CONST_J12) && ~val >= -0x800 && ~val <= 0x7ff) { return 1; } return 0; @@ -220,7 +240,6 @@ typedef enum { OPC_XOR = 0x4033, OPC_XORI = 0x4013, -#if TCG_TARGET_REG_BITS == 64 OPC_ADDIW = 0x1b, OPC_ADDW = 0x3b, OPC_DIVUW = 0x200503b, @@ -235,25 +254,37 @@ typedef enum { OPC_SRLIW = 0x501b, OPC_SRLW = 0x503b, OPC_SUBW = 0x4000003b, -#else - /* Simplify code throughout by defining aliases for RV32. */ - OPC_ADDIW = OPC_ADDI, - OPC_ADDW = OPC_ADD, - OPC_DIVUW = OPC_DIVU, - OPC_DIVW = OPC_DIV, - OPC_MULW = OPC_MUL, - OPC_REMUW = OPC_REMU, - OPC_REMW = OPC_REM, - OPC_SLLIW = OPC_SLLI, - OPC_SLLW = OPC_SLL, - OPC_SRAIW = OPC_SRAI, - OPC_SRAW = OPC_SRA, - OPC_SRLIW = OPC_SRLI, - OPC_SRLW = OPC_SRL, - OPC_SUBW = OPC_SUB, -#endif OPC_FENCE = 0x0000000f, + OPC_NOP = OPC_ADDI, /* nop = addi r0,r0,0 */ + + /* Zba: Bit manipulation extension, address generation */ + OPC_ADD_UW = 0x0800003b, + + /* Zbb: Bit manipulation extension, basic bit manipulation */ + OPC_ANDN = 0x40007033, + OPC_CLZ = 0x60001013, + OPC_CLZW = 0x6000101b, + OPC_CPOP = 0x60201013, + OPC_CPOPW = 0x6020101b, + OPC_CTZ = 0x60101013, + OPC_CTZW = 0x6010101b, + OPC_ORN = 0x40006033, + OPC_REV8 = 0x6b805013, + OPC_ROL = 0x60001033, + OPC_ROLW = 0x6000103b, + OPC_ROR = 0x60005033, + OPC_RORW = 0x6000503b, + OPC_RORI = 0x60005013, + OPC_RORIW = 0x6000501b, + OPC_SEXT_B = 0x60401013, + OPC_SEXT_H = 0x60501013, + OPC_XNOR = 0x40004033, + OPC_ZEXT_H = 0x0800403b, + + /* Zicond: integer conditional operations */ + OPC_CZERO_EQZ = 0x0e005033, + OPC_CZERO_NEZ = 0x0e007033, } RISCVInsn; /* @@ -390,7 +421,7 @@ static void tcg_out_nop_fill(tcg_insn_unit *p, int count) { int i; for (i = 0; i < count; ++i) { - p[i] = encode_i(OPC_ADDI, TCG_REG_ZERO, TCG_REG_ZERO, 0); + p[i] = OPC_NOP; } } @@ -484,7 +515,7 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, tcg_target_long lo, hi, tmp; int shift, ret; - if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { + if (type == TCG_TYPE_I32) { val = (int32_t)val; } @@ -495,7 +526,7 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, } hi = val - lo; - if (TCG_TARGET_REG_BITS == 32 || val == (int32_t)val) { + if (val == (int32_t)val) { tcg_out_opc_upper(s, OPC_LUI, rd, hi); if (lo != 0) { tcg_out_opc_imm(s, OPC_ADDIW, rd, rd, lo); @@ -503,7 +534,6 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, return; } - /* We can only be here if TCG_TARGET_REG_BITS != 32 */ tmp = tcg_pcrel_diff(s, (void *)val); if (tmp == (int32_t)tmp) { tcg_out_opc_upper(s, OPC_AUIPC, rd, 0); @@ -545,6 +575,18 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, tcg_out_opc_imm(s, OPC_LD, rd, rd, 0); } +static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2) +{ + return false; +} + +static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs, + tcg_target_long imm) +{ + /* This function is only used for passing structs by reference. */ + g_assert_not_reached(); +} + static void tcg_out_ext8u(TCGContext *s, TCGReg ret, TCGReg arg) { tcg_out_opc_imm(s, OPC_ANDI, ret, arg, 0xff); @@ -552,26 +594,42 @@ static void tcg_out_ext8u(TCGContext *s, TCGReg ret, TCGReg arg) static void tcg_out_ext16u(TCGContext *s, TCGReg ret, TCGReg arg) { - tcg_out_opc_imm(s, OPC_SLLIW, ret, arg, 16); - tcg_out_opc_imm(s, OPC_SRLIW, ret, ret, 16); + if (have_zbb) { + tcg_out_opc_reg(s, OPC_ZEXT_H, ret, arg, TCG_REG_ZERO); + } else { + tcg_out_opc_imm(s, OPC_SLLIW, ret, arg, 16); + tcg_out_opc_imm(s, OPC_SRLIW, ret, ret, 16); + } } static void tcg_out_ext32u(TCGContext *s, TCGReg ret, TCGReg arg) { - tcg_out_opc_imm(s, OPC_SLLI, ret, arg, 32); - tcg_out_opc_imm(s, OPC_SRLI, ret, ret, 32); + if (have_zba) { + tcg_out_opc_reg(s, OPC_ADD_UW, ret, arg, TCG_REG_ZERO); + } else { + tcg_out_opc_imm(s, OPC_SLLI, ret, arg, 32); + tcg_out_opc_imm(s, OPC_SRLI, ret, ret, 32); + } } -static void tcg_out_ext8s(TCGContext *s, TCGReg ret, TCGReg arg) +static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { - tcg_out_opc_imm(s, OPC_SLLIW, ret, arg, 24); - tcg_out_opc_imm(s, OPC_SRAIW, ret, ret, 24); + if (have_zbb) { + tcg_out_opc_imm(s, OPC_SEXT_B, ret, arg, 0); + } else { + tcg_out_opc_imm(s, OPC_SLLIW, ret, arg, 24); + tcg_out_opc_imm(s, OPC_SRAIW, ret, ret, 24); + } } -static void tcg_out_ext16s(TCGContext *s, TCGReg ret, TCGReg arg) +static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { - tcg_out_opc_imm(s, OPC_SLLIW, ret, arg, 16); - tcg_out_opc_imm(s, OPC_SRAIW, ret, ret, 16); + if (have_zbb) { + tcg_out_opc_imm(s, OPC_SEXT_H, ret, arg, 0); + } else { + tcg_out_opc_imm(s, OPC_SLLIW, ret, arg, 16); + tcg_out_opc_imm(s, OPC_SRAIW, ret, ret, 16); + } } static void tcg_out_ext32s(TCGContext *s, TCGReg ret, TCGReg arg) @@ -579,13 +637,30 @@ static void tcg_out_ext32s(TCGContext *s, TCGReg ret, TCGReg arg) tcg_out_opc_imm(s, OPC_ADDIW, ret, arg, 0); } +static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg ret, TCGReg arg) +{ + if (ret != arg) { + tcg_out_ext32s(s, ret, arg); + } +} + +static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg ret, TCGReg arg) +{ + tcg_out_ext32u(s, ret, arg); +} + +static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg ret, TCGReg arg) +{ + tcg_out_ext32s(s, ret, arg); +} + static void tcg_out_ldst(TCGContext *s, RISCVInsn opc, TCGReg data, TCGReg addr, intptr_t offset) { intptr_t imm12 = sextreg(offset, 0, 12); if (offset != imm12) { - intptr_t diff = offset - (uintptr_t)s->code_ptr; + intptr_t diff = tcg_pcrel_diff(s, (void *)offset); if (addr == TCG_REG_ZERO && diff == (int32_t)diff) { imm12 = sextreg(diff, 0, 12); @@ -623,15 +698,15 @@ static void tcg_out_ldst(TCGContext *s, RISCVInsn opc, TCGReg data, static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, intptr_t arg2) { - bool is32bit = (TCG_TARGET_REG_BITS == 32 || type == TCG_TYPE_I32); - tcg_out_ldst(s, is32bit ? OPC_LW : OPC_LD, arg, arg1, arg2); + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_LW : OPC_LD; + tcg_out_ldst(s, insn, arg, arg1, arg2); } static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, intptr_t arg2) { - bool is32bit = (TCG_TARGET_REG_BITS == 32 || type == TCG_TYPE_I32); - tcg_out_ldst(s, is32bit ? OPC_SW : OPC_SD, arg, arg1, arg2); + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SW : OPC_SD; + tcg_out_ldst(s, insn, arg, arg1, arg2); } static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, @@ -687,9 +762,15 @@ static void tcg_out_addsub2(TCGContext *s, if (cbl) { tcg_out_opc_imm(s, opc_addi, rl, al, bl); tcg_out_opc_imm(s, OPC_SLTIU, TCG_REG_TMP0, rl, bl); - } else if (rl == al && rl == bl) { + } else if (al == bl) { + /* + * If the input regs overlap, this is a simple doubling + * and carry-out is the input msb. This special case is + * required when the output reg overlaps the input, + * but we might as well use it always. + */ tcg_out_opc_imm(s, OPC_SLTI, TCG_REG_TMP0, al, 0); - tcg_out_opc_reg(s, opc_addi, rl, al, bl); + tcg_out_opc_reg(s, opc_add, rl, al, al); } else { tcg_out_opc_reg(s, opc_add, rl, al, bl); tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_TMP0, @@ -732,64 +813,309 @@ static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, tcg_out_opc_branch(s, op, arg1, arg2, 0); } -static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg arg1, TCGReg arg2) +#define SETCOND_INV TCG_TARGET_NB_REGS +#define SETCOND_NEZ (SETCOND_INV << 1) +#define SETCOND_FLAGS (SETCOND_INV | SETCOND_NEZ) + +static int tcg_out_setcond_int(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg arg1, tcg_target_long arg2, bool c2) { + int flags = 0; + switch (cond) { - case TCG_COND_EQ: - tcg_out_opc_reg(s, OPC_SUB, ret, arg1, arg2); - tcg_out_opc_imm(s, OPC_SLTIU, ret, ret, 1); - break; - case TCG_COND_NE: - tcg_out_opc_reg(s, OPC_SUB, ret, arg1, arg2); - tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, ret); - break; - case TCG_COND_LT: - tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2); - break; - case TCG_COND_GE: - tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2); - tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); - break; - case TCG_COND_LE: - tcg_out_opc_reg(s, OPC_SLT, ret, arg2, arg1); - tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); - break; - case TCG_COND_GT: - tcg_out_opc_reg(s, OPC_SLT, ret, arg2, arg1); - break; - case TCG_COND_LTU: - tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2); - break; - case TCG_COND_GEU: - tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2); - tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); - break; - case TCG_COND_LEU: - tcg_out_opc_reg(s, OPC_SLTU, ret, arg2, arg1); - tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); - break; - case TCG_COND_GTU: - tcg_out_opc_reg(s, OPC_SLTU, ret, arg2, arg1); + case TCG_COND_EQ: /* -> NE */ + case TCG_COND_GE: /* -> LT */ + case TCG_COND_GEU: /* -> LTU */ + case TCG_COND_GT: /* -> LE */ + case TCG_COND_GTU: /* -> LEU */ + cond = tcg_invert_cond(cond); + flags ^= SETCOND_INV; break; default: - g_assert_not_reached(); - break; - } + break; + } + + switch (cond) { + case TCG_COND_LE: + case TCG_COND_LEU: + /* + * If we have a constant input, the most efficient way to implement + * LE is by adding 1 and using LT. Watch out for wrap around for LEU. + * We don't need to care for this for LE because the constant input + * is constrained to signed 12-bit, and 0x800 is representable in the + * temporary register. + */ + if (c2) { + if (cond == TCG_COND_LEU) { + /* unsigned <= -1 is true */ + if (arg2 == -1) { + tcg_out_movi(s, TCG_TYPE_REG, ret, !(flags & SETCOND_INV)); + return ret; + } + cond = TCG_COND_LTU; + } else { + cond = TCG_COND_LT; + } + tcg_debug_assert(arg2 <= 0x7ff); + if (++arg2 == 0x800) { + tcg_out_movi(s, TCG_TYPE_REG, TCG_REG_TMP0, arg2); + arg2 = TCG_REG_TMP0; + c2 = false; + } + } else { + TCGReg tmp = arg2; + arg2 = arg1; + arg1 = tmp; + cond = tcg_swap_cond(cond); /* LE -> GE */ + cond = tcg_invert_cond(cond); /* GE -> LT */ + flags ^= SETCOND_INV; + } + break; + default: + break; + } + + switch (cond) { + case TCG_COND_NE: + flags |= SETCOND_NEZ; + if (!c2) { + tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); + } else if (arg2 == 0) { + ret = arg1; + } else { + tcg_out_opc_imm(s, OPC_XORI, ret, arg1, arg2); + } + break; + + case TCG_COND_LT: + if (c2) { + tcg_out_opc_imm(s, OPC_SLTI, ret, arg1, arg2); + } else { + tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2); + } + break; + + case TCG_COND_LTU: + if (c2) { + tcg_out_opc_imm(s, OPC_SLTIU, ret, arg1, arg2); + } else { + tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2); + } + break; + + default: + g_assert_not_reached(); + } + + return ret | flags; } -static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, - TCGReg bl, TCGReg bh, TCGLabel *l) +static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg arg1, tcg_target_long arg2, bool c2) { - /* todo */ - g_assert_not_reached(); + int tmpflags = tcg_out_setcond_int(s, cond, ret, arg1, arg2, c2); + + if (tmpflags != ret) { + TCGReg tmp = tmpflags & ~SETCOND_FLAGS; + + switch (tmpflags & SETCOND_FLAGS) { + case SETCOND_INV: + /* Intermediate result is boolean: simply invert. */ + tcg_out_opc_imm(s, OPC_XORI, ret, tmp, 1); + break; + case SETCOND_NEZ: + /* Intermediate result is zero/non-zero: test != 0. */ + tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, tmp); + break; + case SETCOND_NEZ | SETCOND_INV: + /* Intermediate result is zero/non-zero: test == 0. */ + tcg_out_opc_imm(s, OPC_SLTIU, ret, tmp, 1); + break; + default: + g_assert_not_reached(); + } + } } -static void tcg_out_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) +static void tcg_out_negsetcond(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg arg1, tcg_target_long arg2, bool c2) { - /* todo */ - g_assert_not_reached(); + int tmpflags; + TCGReg tmp; + + /* For LT/GE comparison against 0, replicate the sign bit. */ + if (c2 && arg2 == 0) { + switch (cond) { + case TCG_COND_GE: + tcg_out_opc_imm(s, OPC_XORI, ret, arg1, -1); + arg1 = ret; + /* fall through */ + case TCG_COND_LT: + tcg_out_opc_imm(s, OPC_SRAI, ret, arg1, TCG_TARGET_REG_BITS - 1); + return; + default: + break; + } + } + + tmpflags = tcg_out_setcond_int(s, cond, ret, arg1, arg2, c2); + tmp = tmpflags & ~SETCOND_FLAGS; + + /* If intermediate result is zero/non-zero: test != 0. */ + if (tmpflags & SETCOND_NEZ) { + tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, tmp); + tmp = ret; + } + + /* Produce the 0/-1 result. */ + if (tmpflags & SETCOND_INV) { + tcg_out_opc_imm(s, OPC_ADDI, ret, tmp, -1); + } else { + tcg_out_opc_reg(s, OPC_SUB, ret, TCG_REG_ZERO, tmp); + } +} + +static void tcg_out_movcond_zicond(TCGContext *s, TCGReg ret, TCGReg test_ne, + int val1, bool c_val1, + int val2, bool c_val2) +{ + if (val1 == 0) { + if (c_val2) { + tcg_out_movi(s, TCG_TYPE_REG, TCG_REG_TMP1, val2); + val2 = TCG_REG_TMP1; + } + tcg_out_opc_reg(s, OPC_CZERO_NEZ, ret, val2, test_ne); + return; + } + + if (val2 == 0) { + if (c_val1) { + tcg_out_movi(s, TCG_TYPE_REG, TCG_REG_TMP1, val1); + val1 = TCG_REG_TMP1; + } + tcg_out_opc_reg(s, OPC_CZERO_EQZ, ret, val1, test_ne); + return; + } + + if (c_val2) { + if (c_val1) { + tcg_out_movi(s, TCG_TYPE_REG, TCG_REG_TMP1, val1 - val2); + } else { + tcg_out_opc_imm(s, OPC_ADDI, TCG_REG_TMP1, val1, -val2); + } + tcg_out_opc_reg(s, OPC_CZERO_EQZ, ret, TCG_REG_TMP1, test_ne); + tcg_out_opc_imm(s, OPC_ADDI, ret, ret, val2); + return; + } + + if (c_val1) { + tcg_out_opc_imm(s, OPC_ADDI, TCG_REG_TMP1, val2, -val1); + tcg_out_opc_reg(s, OPC_CZERO_NEZ, ret, TCG_REG_TMP1, test_ne); + tcg_out_opc_imm(s, OPC_ADDI, ret, ret, val1); + return; + } + + tcg_out_opc_reg(s, OPC_CZERO_NEZ, TCG_REG_TMP1, val2, test_ne); + tcg_out_opc_reg(s, OPC_CZERO_EQZ, TCG_REG_TMP0, val1, test_ne); + tcg_out_opc_reg(s, OPC_OR, ret, TCG_REG_TMP0, TCG_REG_TMP1); +} + +static void tcg_out_movcond_br1(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg cmp1, TCGReg cmp2, + int val, bool c_val) +{ + RISCVInsn op; + int disp = 8; + + tcg_debug_assert((unsigned)cond < ARRAY_SIZE(tcg_brcond_to_riscv)); + op = tcg_brcond_to_riscv[cond].op; + tcg_debug_assert(op != 0); + + if (tcg_brcond_to_riscv[cond].swap) { + tcg_out_opc_branch(s, op, cmp2, cmp1, disp); + } else { + tcg_out_opc_branch(s, op, cmp1, cmp2, disp); + } + if (c_val) { + tcg_out_opc_imm(s, OPC_ADDI, ret, TCG_REG_ZERO, val); + } else { + tcg_out_opc_imm(s, OPC_ADDI, ret, val, 0); + } +} + +static void tcg_out_movcond_br2(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg cmp1, TCGReg cmp2, + int val1, bool c_val1, + int val2, bool c_val2) +{ + TCGReg tmp; + + /* TCG optimizer reorders to prefer ret matching val2. */ + if (!c_val2 && ret == val2) { + cond = tcg_invert_cond(cond); + tcg_out_movcond_br1(s, cond, ret, cmp1, cmp2, val1, c_val1); + return; + } + + if (!c_val1 && ret == val1) { + tcg_out_movcond_br1(s, cond, ret, cmp1, cmp2, val2, c_val2); + return; + } + + tmp = (ret == cmp1 || ret == cmp2 ? TCG_REG_TMP1 : ret); + if (c_val1) { + tcg_out_movi(s, TCG_TYPE_REG, tmp, val1); + } else { + tcg_out_mov(s, TCG_TYPE_REG, tmp, val1); + } + tcg_out_movcond_br1(s, cond, tmp, cmp1, cmp2, val2, c_val2); + tcg_out_mov(s, TCG_TYPE_REG, ret, tmp); +} + +static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg cmp1, int cmp2, bool c_cmp2, + TCGReg val1, bool c_val1, + TCGReg val2, bool c_val2) +{ + int tmpflags; + TCGReg t; + + if (!have_zicond && (!c_cmp2 || cmp2 == 0)) { + tcg_out_movcond_br2(s, cond, ret, cmp1, cmp2, + val1, c_val1, val2, c_val2); + return; + } + + tmpflags = tcg_out_setcond_int(s, cond, TCG_REG_TMP0, cmp1, cmp2, c_cmp2); + t = tmpflags & ~SETCOND_FLAGS; + + if (have_zicond) { + if (tmpflags & SETCOND_INV) { + tcg_out_movcond_zicond(s, ret, t, val2, c_val2, val1, c_val1); + } else { + tcg_out_movcond_zicond(s, ret, t, val1, c_val1, val2, c_val2); + } + } else { + cond = tmpflags & SETCOND_INV ? TCG_COND_EQ : TCG_COND_NE; + tcg_out_movcond_br2(s, cond, ret, t, TCG_REG_ZERO, + val1, c_val1, val2, c_val2); + } +} + +static void tcg_out_cltz(TCGContext *s, TCGType type, RISCVInsn insn, + TCGReg ret, TCGReg src1, int src2, bool c_src2) +{ + tcg_out_opc_imm(s, insn, ret, src1, 0); + + if (!c_src2 || src2 != (type == TCG_TYPE_I32 ? 32 : 64)) { + /* + * The requested zero result does not match the insn, so adjust. + * Note that constraints put 'ret' in a new register, so the + * computation above did not clobber either 'src1' or 'src2'. + */ + tcg_out_movcond(s, TCG_COND_EQ, ret, src1, 0, true, + src2, c_src2, ret, false); + } } static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *arg, bool tail) @@ -802,24 +1128,23 @@ static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *arg, bool tail) if (offset == sextreg(offset, 0, 20)) { /* short jump: -2097150 to 2097152 */ tcg_out_opc_jump(s, OPC_JAL, link, offset); - } else if (TCG_TARGET_REG_BITS == 32 || offset == (int32_t)offset) { + } else if (offset == (int32_t)offset) { /* long jump: -2147483646 to 2147483648 */ tcg_out_opc_upper(s, OPC_AUIPC, TCG_REG_TMP0, 0); tcg_out_opc_imm(s, OPC_JALR, link, TCG_REG_TMP0, 0); ret = reloc_call(s->code_ptr - 2, arg); tcg_debug_assert(ret == true); - } else if (TCG_TARGET_REG_BITS == 64) { + } else { /* far jump: 64-bit */ tcg_target_long imm = sextreg((tcg_target_long)arg, 0, 12); tcg_target_long base = (tcg_target_long)arg - imm; tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, base); tcg_out_opc_imm(s, OPC_JALR, link, TCG_REG_TMP0, imm); - } else { - g_assert_not_reached(); } } -static void tcg_out_call(TCGContext *s, const tcg_insn_unit *arg) +static void tcg_out_call(TCGContext *s, const tcg_insn_unit *arg, + const TCGHelperInfo *info) { tcg_out_call_int(s, arg, false); } @@ -847,56 +1172,6 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) * Load/store and TLB */ -#if defined(CONFIG_SOFTMMU) -/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, - * MemOpIdx oi, uintptr_t ra) - */ -static void * const qemu_ld_helpers[MO_SSIZE + 1] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_SB] = helper_ret_ldsb_mmu, -#if HOST_BIG_ENDIAN - [MO_UW] = helper_be_lduw_mmu, - [MO_SW] = helper_be_ldsw_mmu, - [MO_UL] = helper_be_ldul_mmu, -#if TCG_TARGET_REG_BITS == 64 - [MO_SL] = helper_be_ldsl_mmu, -#endif - [MO_UQ] = helper_be_ldq_mmu, -#else - [MO_UW] = helper_le_lduw_mmu, - [MO_SW] = helper_le_ldsw_mmu, - [MO_UL] = helper_le_ldul_mmu, -#if TCG_TARGET_REG_BITS == 64 - [MO_SL] = helper_le_ldsl_mmu, -#endif - [MO_UQ] = helper_le_ldq_mmu, -#endif -}; - -/* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, - * uintxx_t val, MemOpIdx oi, - * uintptr_t ra) - */ -static void * const qemu_st_helpers[MO_SIZE + 1] = { - [MO_8] = helper_ret_stb_mmu, -#if HOST_BIG_ENDIAN - [MO_16] = helper_be_stw_mmu, - [MO_32] = helper_be_stl_mmu, - [MO_64] = helper_be_stq_mmu, -#else - [MO_16] = helper_le_stw_mmu, - [MO_32] = helper_le_stl_mmu, - [MO_64] = helper_le_stq_mmu, -#endif -}; - -/* We don't support oversize guests */ -QEMU_BUILD_BUG_ON(TCG_TARGET_REG_BITS < TARGET_LONG_BITS); - -/* We expect to use a 12-bit negative offset from ENV. */ -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -(1 << 11)); - static void tcg_out_goto(TCGContext *s, const tcg_insn_unit *target) { tcg_out_opc_jump(s, OPC_JAL, TCG_REG_ZERO, 0); @@ -904,92 +1179,19 @@ static void tcg_out_goto(TCGContext *s, const tcg_insn_unit *target) tcg_debug_assert(ok); } -static void tcg_out_tlb_load(TCGContext *s, TCGReg addrl, - TCGReg addrh, MemOpIdx oi, - tcg_insn_unit **label_ptr, bool is_load) +bool tcg_target_has_memory_bswap(MemOp memop) { - MemOp opc = get_memop(oi); - unsigned s_bits = opc & MO_SIZE; - unsigned a_bits = get_alignment_bits(opc); - tcg_target_long compare_mask; - int mem_index = get_mmuidx(oi); - int fast_ofs = TLB_MASK_TABLE_OFS(mem_index); - int mask_ofs = fast_ofs + offsetof(CPUTLBDescFast, mask); - int table_ofs = fast_ofs + offsetof(CPUTLBDescFast, table); - TCGReg mask_base = TCG_AREG0, table_base = TCG_AREG0; - - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, mask_base, mask_ofs); - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, table_base, table_ofs); - - tcg_out_opc_imm(s, OPC_SRLI, TCG_REG_TMP2, addrl, - TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - tcg_out_opc_reg(s, OPC_AND, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP0); - tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP1); - - /* Load the tlb comparator and the addend. */ - tcg_out_ld(s, TCG_TYPE_TL, TCG_REG_TMP0, TCG_REG_TMP2, - is_load ? offsetof(CPUTLBEntry, addr_read) - : offsetof(CPUTLBEntry, addr_write)); - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP2, TCG_REG_TMP2, - offsetof(CPUTLBEntry, addend)); - - /* We don't support unaligned accesses. */ - if (a_bits < s_bits) { - a_bits = s_bits; - } - /* Clear the non-page, non-alignment bits from the address. */ - compare_mask = (tcg_target_long)TARGET_PAGE_MASK | ((1 << a_bits) - 1); - if (compare_mask == sextreg(compare_mask, 0, 12)) { - tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_TMP1, addrl, compare_mask); - } else { - tcg_out_movi(s, TCG_TYPE_TL, TCG_REG_TMP1, compare_mask); - tcg_out_opc_reg(s, OPC_AND, TCG_REG_TMP1, TCG_REG_TMP1, addrl); - } - - /* Compare masked address with the TLB entry. */ - label_ptr[0] = s->code_ptr; - tcg_out_opc_branch(s, OPC_BNE, TCG_REG_TMP0, TCG_REG_TMP1, 0); - - /* TLB Hit - translate address using addend. */ - if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { - tcg_out_ext32u(s, TCG_REG_TMP0, addrl); - addrl = TCG_REG_TMP0; - } - tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP0, TCG_REG_TMP2, addrl); + return false; } -static void add_qemu_ldst_label(TCGContext *s, int is_ld, MemOpIdx oi, - TCGType ext, - TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - void *raddr, tcg_insn_unit **label_ptr) -{ - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->oi = oi; - label->type = ext; - label->datalo_reg = datalo; - label->datahi_reg = datahi; - label->addrlo_reg = addrlo; - label->addrhi_reg = addrhi; - label->raddr = tcg_splitwx_to_rx(raddr); - label->label_ptr[0] = label_ptr[0]; -} +/* We have three temps, we might as well expose them. */ +static const TCGLdstHelperParam ldst_helper_param = { + .ntmp = 3, .tmp = { TCG_REG_TMP0, TCG_REG_TMP1, TCG_REG_TMP2 } +}; static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { - MemOpIdx oi = l->oi; - MemOp opc = get_memop(oi); - TCGReg a0 = tcg_target_call_iarg_regs[0]; - TCGReg a1 = tcg_target_call_iarg_regs[1]; - TCGReg a2 = tcg_target_call_iarg_regs[2]; - TCGReg a3 = tcg_target_call_iarg_regs[3]; - - /* We don't support oversize guests */ - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - g_assert_not_reached(); - } + MemOp opc = get_memop(l->oi); /* resolve label address */ if (!reloc_sbimm12(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { @@ -997,13 +1199,9 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) } /* call load helper */ - tcg_out_mov(s, TCG_TYPE_PTR, a0, TCG_AREG0); - tcg_out_mov(s, TCG_TYPE_PTR, a1, l->addrlo_reg); - tcg_out_movi(s, TCG_TYPE_PTR, a2, oi); - tcg_out_movi(s, TCG_TYPE_PTR, a3, (tcg_target_long)l->raddr); - - tcg_out_call(s, qemu_ld_helpers[opc & MO_SSIZE]); - tcg_out_mov(s, (opc & MO_SIZE) == MO_64, l->datalo_reg, a0); + tcg_out_ld_helper_args(s, l, &ldst_helper_param); + tcg_out_call_int(s, qemu_ld_helpers[opc & MO_SSIZE], false); + tcg_out_ld_helper_ret(s, l, true, &ldst_helper_param); tcg_out_goto(s, l->raddr); return true; @@ -1011,19 +1209,7 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) { - MemOpIdx oi = l->oi; - MemOp opc = get_memop(oi); - MemOp s_bits = opc & MO_SIZE; - TCGReg a0 = tcg_target_call_iarg_regs[0]; - TCGReg a1 = tcg_target_call_iarg_regs[1]; - TCGReg a2 = tcg_target_call_iarg_regs[2]; - TCGReg a3 = tcg_target_call_iarg_regs[3]; - TCGReg a4 = tcg_target_call_iarg_regs[4]; - - /* We don't support oversize guests */ - if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - g_assert_not_reached(); - } + MemOp opc = get_memop(l->oi); /* resolve label address */ if (!reloc_sbimm12(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { @@ -1031,166 +1217,196 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) } /* call store helper */ - tcg_out_mov(s, TCG_TYPE_PTR, a0, TCG_AREG0); - tcg_out_mov(s, TCG_TYPE_PTR, a1, l->addrlo_reg); - tcg_out_mov(s, TCG_TYPE_PTR, a2, l->datalo_reg); - switch (s_bits) { - case MO_8: - tcg_out_ext8u(s, a2, a2); - break; - case MO_16: - tcg_out_ext16u(s, a2, a2); - break; - default: - break; - } - tcg_out_movi(s, TCG_TYPE_PTR, a3, oi); - tcg_out_movi(s, TCG_TYPE_PTR, a4, (tcg_target_long)l->raddr); - - tcg_out_call(s, qemu_st_helpers[opc & MO_SIZE]); + tcg_out_st_helper_args(s, l, &ldst_helper_param); + tcg_out_call_int(s, qemu_st_helpers[opc & MO_SIZE], false); tcg_out_goto(s, l->raddr); return true; } -#else -static void tcg_out_test_alignment(TCGContext *s, bool is_ld, TCGReg addr_reg, - unsigned a_bits) +/* We expect to use a 12-bit negative offset from ENV. */ +#define MIN_TLB_MASK_TABLE_OFS -(1 << 11) + +/* + * For system-mode, perform the TLB load and compare. + * For user-mode, perform any required alignment tests. + * In both cases, return a TCGLabelQemuLdst structure if the slow path + * is required and fill in @h with the host address for the fast path. + */ +static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, + TCGReg addr_reg, MemOpIdx oi, + bool is_ld) { - unsigned a_mask = (1 << a_bits) - 1; - TCGLabelQemuLdst *l = new_ldst_label(s); + TCGType addr_type = s->addr_type; + TCGLabelQemuLdst *ldst = NULL; + MemOp opc = get_memop(oi); + TCGAtomAlign aa; + unsigned a_mask; - l->is_ld = is_ld; - l->addrlo_reg = addr_reg; + aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, false); + a_mask = (1u << aa.align) - 1; - /* We are expecting a_bits to max out at 7, so we can always use andi. */ - tcg_debug_assert(a_bits < 12); - tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_TMP1, addr_reg, a_mask); + if (tcg_use_softmmu) { + unsigned s_bits = opc & MO_SIZE; + unsigned s_mask = (1u << s_bits) - 1; + int mem_index = get_mmuidx(oi); + int fast_ofs = tlb_mask_table_ofs(s, mem_index); + int mask_ofs = fast_ofs + offsetof(CPUTLBDescFast, mask); + int table_ofs = fast_ofs + offsetof(CPUTLBDescFast, table); + int compare_mask; + TCGReg addr_adj; - l->label_ptr[0] = s->code_ptr; - tcg_out_opc_branch(s, OPC_BNE, TCG_REG_TMP1, TCG_REG_ZERO, 0); + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addr_reg; - l->raddr = tcg_splitwx_to_rx(s->code_ptr); -} + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, TCG_AREG0, mask_ofs); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_AREG0, table_ofs); -static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) -{ - /* resolve label address */ - if (!reloc_sbimm12(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { - return false; + tcg_out_opc_imm(s, OPC_SRLI, TCG_REG_TMP2, addr_reg, + s->page_bits - CPU_TLB_ENTRY_BITS); + tcg_out_opc_reg(s, OPC_AND, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP0); + tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP1); + + /* + * For aligned accesses, we check the first byte and include the + * alignment bits within the address. For unaligned access, we + * check that we don't cross pages using the address of the last + * byte of the access. + */ + addr_adj = addr_reg; + if (a_mask < s_mask) { + addr_adj = TCG_REG_TMP0; + tcg_out_opc_imm(s, addr_type == TCG_TYPE_I32 ? OPC_ADDIW : OPC_ADDI, + addr_adj, addr_reg, s_mask - a_mask); + } + compare_mask = s->page_mask | a_mask; + if (compare_mask == sextreg(compare_mask, 0, 12)) { + tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_TMP1, addr_adj, compare_mask); + } else { + tcg_out_movi(s, addr_type, TCG_REG_TMP1, compare_mask); + tcg_out_opc_reg(s, OPC_AND, TCG_REG_TMP1, TCG_REG_TMP1, addr_adj); + } + + /* Load the tlb comparator and the addend. */ + QEMU_BUILD_BUG_ON(HOST_BIG_ENDIAN); + tcg_out_ld(s, addr_type, TCG_REG_TMP0, TCG_REG_TMP2, + is_ld ? offsetof(CPUTLBEntry, addr_read) + : offsetof(CPUTLBEntry, addr_write)); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP2, TCG_REG_TMP2, + offsetof(CPUTLBEntry, addend)); + + /* Compare masked address with the TLB entry. */ + ldst->label_ptr[0] = s->code_ptr; + tcg_out_opc_branch(s, OPC_BNE, TCG_REG_TMP0, TCG_REG_TMP1, 0); + + /* TLB Hit - translate address using addend. */ + if (addr_type != TCG_TYPE_I32) { + tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP0, addr_reg, TCG_REG_TMP2); + } else if (have_zba) { + tcg_out_opc_reg(s, OPC_ADD_UW, TCG_REG_TMP0, + addr_reg, TCG_REG_TMP2); + } else { + tcg_out_ext32u(s, TCG_REG_TMP0, addr_reg); + tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP0, + TCG_REG_TMP0, TCG_REG_TMP2); + } + *pbase = TCG_REG_TMP0; + } else { + TCGReg base; + + if (a_mask) { + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addr_reg; + + /* We are expecting alignment max 7, so we can always use andi. */ + tcg_debug_assert(a_mask == sextreg(a_mask, 0, 12)); + tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_TMP1, addr_reg, a_mask); + + ldst->label_ptr[0] = s->code_ptr; + tcg_out_opc_branch(s, OPC_BNE, TCG_REG_TMP1, TCG_REG_ZERO, 0); + } + + if (guest_base != 0) { + base = TCG_REG_TMP0; + if (addr_type != TCG_TYPE_I32) { + tcg_out_opc_reg(s, OPC_ADD, base, addr_reg, + TCG_GUEST_BASE_REG); + } else if (have_zba) { + tcg_out_opc_reg(s, OPC_ADD_UW, base, addr_reg, + TCG_GUEST_BASE_REG); + } else { + tcg_out_ext32u(s, base, addr_reg); + tcg_out_opc_reg(s, OPC_ADD, base, base, TCG_GUEST_BASE_REG); + } + } else if (addr_type != TCG_TYPE_I32) { + base = addr_reg; + } else { + base = TCG_REG_TMP0; + tcg_out_ext32u(s, base, addr_reg); + } + *pbase = base; } - tcg_out_mov(s, TCG_TYPE_TL, TCG_REG_A1, l->addrlo_reg); - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_A0, TCG_AREG0); - - /* tail call, with the return address back inline. */ - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RA, (uintptr_t)l->raddr); - tcg_out_call_int(s, (const void *)(l->is_ld ? helper_unaligned_ld - : helper_unaligned_st), true); - return true; + return ldst; } -static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - return tcg_out_fail_alignment(s, l); -} - -static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - return tcg_out_fail_alignment(s, l); -} - -#endif /* CONFIG_SOFTMMU */ - -static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg lo, TCGReg hi, - TCGReg base, MemOp opc, bool is_64) +static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg val, + TCGReg base, MemOp opc, TCGType type) { /* Byte swapping is left to middle-end expansion. */ tcg_debug_assert((opc & MO_BSWAP) == 0); switch (opc & (MO_SSIZE)) { case MO_UB: - tcg_out_opc_imm(s, OPC_LBU, lo, base, 0); + tcg_out_opc_imm(s, OPC_LBU, val, base, 0); break; case MO_SB: - tcg_out_opc_imm(s, OPC_LB, lo, base, 0); + tcg_out_opc_imm(s, OPC_LB, val, base, 0); break; case MO_UW: - tcg_out_opc_imm(s, OPC_LHU, lo, base, 0); + tcg_out_opc_imm(s, OPC_LHU, val, base, 0); break; case MO_SW: - tcg_out_opc_imm(s, OPC_LH, lo, base, 0); + tcg_out_opc_imm(s, OPC_LH, val, base, 0); break; case MO_UL: - if (TCG_TARGET_REG_BITS == 64 && is_64) { - tcg_out_opc_imm(s, OPC_LWU, lo, base, 0); + if (type == TCG_TYPE_I64) { + tcg_out_opc_imm(s, OPC_LWU, val, base, 0); break; } /* FALLTHRU */ case MO_SL: - tcg_out_opc_imm(s, OPC_LW, lo, base, 0); + tcg_out_opc_imm(s, OPC_LW, val, base, 0); break; case MO_UQ: - /* Prefer to load from offset 0 first, but allow for overlap. */ - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_opc_imm(s, OPC_LD, lo, base, 0); - } else if (lo != base) { - tcg_out_opc_imm(s, OPC_LW, lo, base, 0); - tcg_out_opc_imm(s, OPC_LW, hi, base, 4); - } else { - tcg_out_opc_imm(s, OPC_LW, hi, base, 4); - tcg_out_opc_imm(s, OPC_LW, lo, base, 0); - } + tcg_out_opc_imm(s, OPC_LD, val, base, 0); break; default: g_assert_not_reached(); } } -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) +static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, + MemOpIdx oi, TCGType data_type) { - TCGReg addr_regl, addr_regh __attribute__((unused)); - TCGReg data_regl, data_regh; - MemOpIdx oi; - MemOp opc; -#if defined(CONFIG_SOFTMMU) - tcg_insn_unit *label_ptr[1]; -#else - unsigned a_bits; -#endif - TCGReg base = TCG_REG_TMP0; + TCGLabelQemuLdst *ldst; + TCGReg base; - data_regl = *args++; - data_regh = (TCG_TARGET_REG_BITS == 32 && is_64 ? *args++ : 0); - addr_regl = *args++; - addr_regh = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); + ldst = prepare_host_addr(s, &base, addr_reg, oi, true); + tcg_out_qemu_ld_direct(s, data_reg, base, get_memop(oi), data_type); -#if defined(CONFIG_SOFTMMU) - tcg_out_tlb_load(s, addr_regl, addr_regh, oi, label_ptr, 1); - tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc, is_64); - add_qemu_ldst_label(s, 1, oi, - (is_64 ? TCG_TYPE_I64 : TCG_TYPE_I32), - data_regl, data_regh, addr_regl, addr_regh, - s->code_ptr, label_ptr); -#else - if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { - tcg_out_ext32u(s, base, addr_regl); - addr_regl = base; + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = data_reg; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } - a_bits = get_alignment_bits(opc); - if (a_bits) { - tcg_out_test_alignment(s, true, addr_regl, a_bits); - } - if (guest_base != 0) { - tcg_out_opc_reg(s, OPC_ADD, base, TCG_GUEST_BASE_REG, addr_regl); - } - tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc, is_64); -#endif } -static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg lo, TCGReg hi, +static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg val, TCGReg base, MemOp opc) { /* Byte swapping is left to middle-end expansion. */ @@ -1198,72 +1414,81 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg lo, TCGReg hi, switch (opc & (MO_SSIZE)) { case MO_8: - tcg_out_opc_store(s, OPC_SB, base, lo, 0); + tcg_out_opc_store(s, OPC_SB, base, val, 0); break; case MO_16: - tcg_out_opc_store(s, OPC_SH, base, lo, 0); + tcg_out_opc_store(s, OPC_SH, base, val, 0); break; case MO_32: - tcg_out_opc_store(s, OPC_SW, base, lo, 0); + tcg_out_opc_store(s, OPC_SW, base, val, 0); break; case MO_64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_opc_store(s, OPC_SD, base, lo, 0); - } else { - tcg_out_opc_store(s, OPC_SW, base, lo, 0); - tcg_out_opc_store(s, OPC_SW, base, hi, 4); - } + tcg_out_opc_store(s, OPC_SD, base, val, 0); break; default: g_assert_not_reached(); } } -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) +static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, + MemOpIdx oi, TCGType data_type) { - TCGReg addr_regl, addr_regh __attribute__((unused)); - TCGReg data_regl, data_regh; - MemOpIdx oi; - MemOp opc; -#if defined(CONFIG_SOFTMMU) - tcg_insn_unit *label_ptr[1]; -#else - unsigned a_bits; -#endif - TCGReg base = TCG_REG_TMP0; + TCGLabelQemuLdst *ldst; + TCGReg base; - data_regl = *args++; - data_regh = (TCG_TARGET_REG_BITS == 32 && is_64 ? *args++ : 0); - addr_regl = *args++; - addr_regh = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); - oi = *args++; - opc = get_memop(oi); + ldst = prepare_host_addr(s, &base, addr_reg, oi, false); + tcg_out_qemu_st_direct(s, data_reg, base, get_memop(oi)); -#if defined(CONFIG_SOFTMMU) - tcg_out_tlb_load(s, addr_regl, addr_regh, oi, label_ptr, 0); - tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); - add_qemu_ldst_label(s, 0, oi, - (is_64 ? TCG_TYPE_I64 : TCG_TYPE_I32), - data_regl, data_regh, addr_regl, addr_regh, - s->code_ptr, label_ptr); -#else - if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { - tcg_out_ext32u(s, base, addr_regl); - addr_regl = base; + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = data_reg; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } - a_bits = get_alignment_bits(opc); - if (a_bits) { - tcg_out_test_alignment(s, false, addr_regl, a_bits); - } - if (guest_base != 0) { - tcg_out_opc_reg(s, OPC_ADD, base, TCG_GUEST_BASE_REG, addr_regl); - } - tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); -#endif } static const tcg_insn_unit *tb_ret_addr; +static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) +{ + /* Reuse the zeroing that exists for goto_ptr. */ + if (a0 == 0) { + tcg_out_call_int(s, tcg_code_gen_epilogue, true); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_A0, a0); + tcg_out_call_int(s, tb_ret_addr, true); + } +} + +static void tcg_out_goto_tb(TCGContext *s, int which) +{ + /* Direct branch will be patched by tb_target_set_jmp_target. */ + set_jmp_insn_offset(s, which); + tcg_out32(s, OPC_JAL); + + /* When branch is out of range, fall through to indirect. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, TCG_REG_ZERO, + get_jmp_target_addr(s, which)); + tcg_out_opc_imm(s, OPC_JALR, TCG_REG_ZERO, TCG_REG_TMP0, 0); + set_jmp_reset_offset(s, which); +} + +void tb_target_set_jmp_target(const TranslationBlock *tb, int n, + uintptr_t jmp_rx, uintptr_t jmp_rw) +{ + uintptr_t addr = tb->jmp_target_addr[n]; + ptrdiff_t offset = addr - jmp_rx; + tcg_insn_unit insn; + + /* Either directly branch, or fall through to indirect branch. */ + if (offset == sextreg(offset, 0, 20)) { + insn = encode_uj(OPC_JAL, TCG_REG_ZERO, offset); + } else { + insn = OPC_NOP; + } + qatomic_set((uint32_t *)jmp_rw, insn); + flush_idcache_range(jmp_rx, jmp_rw, 4); +} + static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1274,25 +1499,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, int c2 = const_args[2]; switch (opc) { - case INDEX_op_exit_tb: - /* Reuse the zeroing that exists for goto_ptr. */ - if (a0 == 0) { - tcg_out_call_int(s, tcg_code_gen_epilogue, true); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_A0, a0); - tcg_out_call_int(s, tb_ret_addr, true); - } - break; - - case INDEX_op_goto_tb: - assert(s->tb_jmp_insn_offset == 0); - /* indirect jump method */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, TCG_REG_ZERO, - (uintptr_t)(s->tb_jmp_target_addr + a0)); - tcg_out_opc_imm(s, OPC_JALR, TCG_REG_ZERO, TCG_REG_TMP0, 0); - set_jmp_reset_offset(s, a0); - break; - case INDEX_op_goto_ptr: tcg_out_opc_imm(s, OPC_JALR, TCG_REG_ZERO, a0, 0); break; @@ -1402,6 +1608,31 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, } break; + case INDEX_op_andc_i32: + case INDEX_op_andc_i64: + if (c2) { + tcg_out_opc_imm(s, OPC_ANDI, a0, a1, ~a2); + } else { + tcg_out_opc_reg(s, OPC_ANDN, a0, a1, a2); + } + break; + case INDEX_op_orc_i32: + case INDEX_op_orc_i64: + if (c2) { + tcg_out_opc_imm(s, OPC_ORI, a0, a1, ~a2); + } else { + tcg_out_opc_reg(s, OPC_ORN, a0, a1, a2); + } + break; + case INDEX_op_eqv_i32: + case INDEX_op_eqv_i64: + if (c2) { + tcg_out_opc_imm(s, OPC_XORI, a0, a1, ~a2); + } else { + tcg_out_opc_reg(s, OPC_XNOR, a0, a1, a2); + } + break; + case INDEX_op_not_i32: case INDEX_op_not_i64: tcg_out_opc_imm(s, OPC_XORI, a0, a1, -1); @@ -1494,6 +1725,80 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, } break; + case INDEX_op_rotl_i32: + if (c2) { + tcg_out_opc_imm(s, OPC_RORIW, a0, a1, -a2 & 0x1f); + } else { + tcg_out_opc_reg(s, OPC_ROLW, a0, a1, a2); + } + break; + case INDEX_op_rotl_i64: + if (c2) { + tcg_out_opc_imm(s, OPC_RORI, a0, a1, -a2 & 0x3f); + } else { + tcg_out_opc_reg(s, OPC_ROL, a0, a1, a2); + } + break; + + case INDEX_op_rotr_i32: + if (c2) { + tcg_out_opc_imm(s, OPC_RORIW, a0, a1, a2 & 0x1f); + } else { + tcg_out_opc_reg(s, OPC_RORW, a0, a1, a2); + } + break; + case INDEX_op_rotr_i64: + if (c2) { + tcg_out_opc_imm(s, OPC_RORI, a0, a1, a2 & 0x3f); + } else { + tcg_out_opc_reg(s, OPC_ROR, a0, a1, a2); + } + break; + + case INDEX_op_bswap64_i64: + tcg_out_opc_imm(s, OPC_REV8, a0, a1, 0); + break; + case INDEX_op_bswap32_i32: + a2 = 0; + /* fall through */ + case INDEX_op_bswap32_i64: + tcg_out_opc_imm(s, OPC_REV8, a0, a1, 0); + if (a2 & TCG_BSWAP_OZ) { + tcg_out_opc_imm(s, OPC_SRLI, a0, a0, 32); + } else { + tcg_out_opc_imm(s, OPC_SRAI, a0, a0, 32); + } + break; + case INDEX_op_bswap16_i64: + case INDEX_op_bswap16_i32: + tcg_out_opc_imm(s, OPC_REV8, a0, a1, 0); + if (a2 & TCG_BSWAP_OZ) { + tcg_out_opc_imm(s, OPC_SRLI, a0, a0, 48); + } else { + tcg_out_opc_imm(s, OPC_SRAI, a0, a0, 48); + } + break; + + case INDEX_op_ctpop_i32: + tcg_out_opc_imm(s, OPC_CPOPW, a0, a1, 0); + break; + case INDEX_op_ctpop_i64: + tcg_out_opc_imm(s, OPC_CPOP, a0, a1, 0); + break; + + case INDEX_op_clz_i32: + tcg_out_cltz(s, TCG_TYPE_I32, OPC_CLZW, a0, a1, a2, c2); + break; + case INDEX_op_clz_i64: + tcg_out_cltz(s, TCG_TYPE_I64, OPC_CLZ, a0, a1, a2, c2); + break; + case INDEX_op_ctz_i32: + tcg_out_cltz(s, TCG_TYPE_I32, OPC_CTZW, a0, a1, a2, c2); + break; + case INDEX_op_ctz_i64: + tcg_out_cltz(s, TCG_TYPE_I64, OPC_CTZ, a0, a1, a2, c2); + break; + case INDEX_op_add2_i32: tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], const_args[4], const_args[5], false, true); @@ -1515,60 +1820,38 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_brcond_i64: tcg_out_brcond(s, a2, a0, a1, arg_label(args[3])); break; - case INDEX_op_brcond2_i32: - tcg_out_brcond2(s, args[4], a0, a1, a2, args[3], arg_label(args[5])); - break; case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: - tcg_out_setcond(s, args[3], a0, a1, a2); - break; - case INDEX_op_setcond2_i32: - tcg_out_setcond2(s, args[5], a0, a1, a2, args[3], args[4]); + tcg_out_setcond(s, args[3], a0, a1, a2, c2); break; - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args, false); - break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args, true); - break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args, false); - break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args, true); + case INDEX_op_negsetcond_i32: + case INDEX_op_negsetcond_i64: + tcg_out_negsetcond(s, args[3], a0, a1, a2, c2); break; - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - tcg_out_ext8u(s, a0, a1); + case INDEX_op_movcond_i32: + case INDEX_op_movcond_i64: + tcg_out_movcond(s, args[5], a0, a1, a2, c2, + args[3], const_args[3], args[4], const_args[4]); break; - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - tcg_out_ext16u(s, a0, a1); + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); break; - - case INDEX_op_ext32u_i64: - case INDEX_op_extu_i32_i64: - tcg_out_ext32u(s, a0, a1); + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: + tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I64); break; - - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - tcg_out_ext8s(s, a0, a1); + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I32); break; - - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - tcg_out_ext16s(s, a0, a1); - break; - - case INDEX_op_ext32s_i64: - case INDEX_op_extrl_i64_i32: - case INDEX_op_ext_i32_i64: - tcg_out_ext32s(s, a0, a1); + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: + tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I64); break; case INDEX_op_extrh_i64_i32: @@ -1592,6 +1875,21 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ + case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ + case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ + case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ + case INDEX_op_ext8s_i64: + case INDEX_op_ext8u_i32: + case INDEX_op_ext8u_i64: + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + case INDEX_op_ext16u_i32: + case INDEX_op_ext16u_i64: + case INDEX_op_ext32s_i64: + case INDEX_op_ext32u_i64: + case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); } @@ -1633,6 +1931,13 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: case INDEX_op_ext_i32_i64: + case INDEX_op_bswap16_i32: + case INDEX_op_bswap32_i32: + case INDEX_op_bswap16_i64: + case INDEX_op_bswap32_i64: + case INDEX_op_bswap64_i64: + case INDEX_op_ctpop_i32: + case INDEX_op_ctpop_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: @@ -1652,8 +1957,20 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_and_i64: case INDEX_op_or_i64: case INDEX_op_xor_i64: + case INDEX_op_setcond_i32: + case INDEX_op_setcond_i64: + case INDEX_op_negsetcond_i32: + case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); + case INDEX_op_andc_i32: + case INDEX_op_andc_i64: + case INDEX_op_orc_i32: + case INDEX_op_orc_i64: + case INDEX_op_eqv_i32: + case INDEX_op_eqv_i64: + return C_O1_I2(r, r, rJ); + case INDEX_op_sub_i32: case INDEX_op_sub_i64: return C_O1_I2(r, rZ, rN); @@ -1665,7 +1982,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: - case INDEX_op_setcond_i32: case INDEX_op_mul_i64: case INDEX_op_mulsh_i64: case INDEX_op_muluh_i64: @@ -1673,47 +1989,50 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_divu_i64: case INDEX_op_rem_i64: case INDEX_op_remu_i64: - case INDEX_op_setcond_i64: return C_O1_I2(r, rZ, rZ); case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: + case INDEX_op_rotl_i32: + case INDEX_op_rotr_i32: case INDEX_op_shl_i64: case INDEX_op_shr_i64: case INDEX_op_sar_i64: + case INDEX_op_rotl_i64: + case INDEX_op_rotr_i64: return C_O1_I2(r, r, ri); + case INDEX_op_clz_i32: + case INDEX_op_clz_i64: + case INDEX_op_ctz_i32: + case INDEX_op_ctz_i64: + return C_N1_I2(r, r, rM); + case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: return C_O0_I2(rZ, rZ); + case INDEX_op_movcond_i32: + case INDEX_op_movcond_i64: + return C_O1_I4(r, r, rI, rM, rM); + case INDEX_op_add2_i32: case INDEX_op_add2_i64: case INDEX_op_sub2_i32: case INDEX_op_sub2_i64: return C_O2_I4(r, r, rZ, rZ, rM, rM); - case INDEX_op_brcond2_i32: - return C_O0_I4(rZ, rZ, rZ, rZ); - - case INDEX_op_setcond2_i32: - return C_O1_I4(r, rZ, rZ, rZ, rZ); - - case INDEX_op_qemu_ld_i32: - return (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS - ? C_O1_I1(r, L) : C_O1_I2(r, L, L)); - case INDEX_op_qemu_st_i32: - return (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS - ? C_O0_I2(LZ, L) : C_O0_I3(LZ, L, L)); - case INDEX_op_qemu_ld_i64: - return (TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, L) - : TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? C_O2_I1(r, r, L) - : C_O2_I2(r, r, L, L)); - case INDEX_op_qemu_st_i64: - return (TCG_TARGET_REG_BITS == 64 ? C_O0_I2(LZ, L) - : TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? C_O0_I3(LZ, LZ, L) - : C_O0_I4(LZ, LZ, L, L)); + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: + return C_O1_I1(r, r); + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: + return C_O0_I2(rZ, r); default: g_assert_not_reached(); @@ -1762,10 +2081,10 @@ static void tcg_target_qemu_prologue(TCGContext *s) TCG_REG_SP, SAVE_OFS + i * REG_SIZE); } -#if !defined(CONFIG_SOFTMMU) - tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, guest_base); - tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); -#endif + if (!tcg_use_softmmu && guest_base) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, guest_base); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } /* Call generated code */ tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); @@ -1786,12 +2105,69 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_opc_imm(s, OPC_JALR, TCG_REG_ZERO, TCG_REG_RA, 0); } +static void tcg_out_tb_start(TCGContext *s) +{ + /* nothing to do */ +} + +static volatile sig_atomic_t got_sigill; + +static void sigill_handler(int signo, siginfo_t *si, void *data) +{ + /* Skip the faulty instruction */ + ucontext_t *uc = (ucontext_t *)data; + uc->uc_mcontext.__gregs[REG_PC] += 4; + + got_sigill = 1; +} + +static void tcg_target_detect_isa(void) +{ +#if !defined(have_zba) || !defined(have_zbb) || !defined(have_zicond) + /* + * TODO: It is expected that this will be determinable via + * linux riscv_hwprobe syscall, not yet merged. + * In the meantime, test via sigill. + */ + + struct sigaction sa_old, sa_new; + + memset(&sa_new, 0, sizeof(sa_new)); + sa_new.sa_flags = SA_SIGINFO; + sa_new.sa_sigaction = sigill_handler; + sigaction(SIGILL, &sa_new, &sa_old); + +#ifndef have_zba + /* Probe for Zba: add.uw zero,zero,zero. */ + got_sigill = 0; + asm volatile(".insn r 0x3b, 0, 0x04, zero, zero, zero" : : : "memory"); + have_zba = !got_sigill; +#endif + +#ifndef have_zbb + /* Probe for Zba: andn zero,zero,zero. */ + got_sigill = 0; + asm volatile(".insn r 0x33, 7, 0x20, zero, zero, zero" : : : "memory"); + have_zbb = !got_sigill; +#endif + +#ifndef have_zicond + /* Probe for Zicond: czero.eqz zero,zero,zero. */ + got_sigill = 0; + asm volatile(".insn r 0x33, 5, 0x07, zero, zero, zero" : : : "memory"); + have_zicond = !got_sigill; +#endif + + sigaction(SIGILL, &sa_old, NULL); +#endif +} + static void tcg_target_init(TCGContext *s) { + tcg_target_detect_isa(); + tcg_target_available_regs[TCG_TYPE_I32] = 0xffffffff; - if (TCG_TARGET_REG_BITS == 64) { - tcg_target_available_regs[TCG_TYPE_I64] = 0xffffffff; - } + tcg_target_available_regs[TCG_TYPE_I64] = 0xffffffff; tcg_target_call_clobber_regs = -1u; tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S0); diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index 11c9b3e4f4..2c1b680b93 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -25,14 +25,7 @@ #ifndef RISCV_TCG_TARGET_H #define RISCV_TCG_TARGET_H -#if __riscv_xlen == 32 -# define TCG_TARGET_REG_BITS 32 -#elif __riscv_xlen == 64 -# define TCG_TARGET_REG_BITS 64 -#endif - #define TCG_TARGET_INSN_UNIT_SIZE 4 -#define TCG_TARGET_TLB_DISPLACEMENT_BITS 20 #define TCG_TARGET_NB_REGS 32 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) @@ -81,15 +74,24 @@ typedef enum { /* used for function call generation */ #define TCG_REG_CALL_STACK TCG_REG_SP #define TCG_TARGET_STACK_ALIGN 16 -#define TCG_TARGET_CALL_ALIGN_ARGS 1 #define TCG_TARGET_CALL_STACK_OFFSET 0 +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL + +#if defined(__riscv_arch_test) && defined(__riscv_zbb) +# define have_zbb true +#else +extern bool have_zbb; +#endif /* optional instructions */ -#define TCG_TARGET_HAS_movcond_i32 0 +#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_div2_i32 0 -#define TCG_TARGET_HAS_rot_i32 0 +#define TCG_TARGET_HAS_rot_i32 have_zbb #define TCG_TARGET_HAS_deposit_i32 0 #define TCG_TARGET_HAS_extract_i32 0 #define TCG_TARGET_HAS_sextract_i32 0 @@ -98,76 +100,69 @@ typedef enum { #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_muluh_i32 (TCG_TARGET_REG_BITS == 32) -#define TCG_TARGET_HAS_mulsh_i32 (TCG_TARGET_REG_BITS == 32) +#define TCG_TARGET_HAS_muluh_i32 0 +#define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_ext8s_i32 1 #define TCG_TARGET_HAS_ext16s_i32 1 #define TCG_TARGET_HAS_ext8u_i32 1 #define TCG_TARGET_HAS_ext16u_i32 1 -#define TCG_TARGET_HAS_bswap16_i32 0 -#define TCG_TARGET_HAS_bswap32_i32 0 +#define TCG_TARGET_HAS_bswap16_i32 have_zbb +#define TCG_TARGET_HAS_bswap32_i32 have_zbb #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 -#define TCG_TARGET_HAS_andc_i32 0 -#define TCG_TARGET_HAS_orc_i32 0 -#define TCG_TARGET_HAS_eqv_i32 0 +#define TCG_TARGET_HAS_andc_i32 have_zbb +#define TCG_TARGET_HAS_orc_i32 have_zbb +#define TCG_TARGET_HAS_eqv_i32 have_zbb #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 -#define TCG_TARGET_HAS_clz_i32 0 -#define TCG_TARGET_HAS_ctz_i32 0 -#define TCG_TARGET_HAS_ctpop_i32 0 -#define TCG_TARGET_HAS_direct_jump 0 +#define TCG_TARGET_HAS_clz_i32 have_zbb +#define TCG_TARGET_HAS_ctz_i32 have_zbb +#define TCG_TARGET_HAS_ctpop_i32 have_zbb #define TCG_TARGET_HAS_brcond2 1 #define TCG_TARGET_HAS_setcond2 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_movcond_i64 0 +#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_div2_i64 0 -#define TCG_TARGET_HAS_rot_i64 0 +#define TCG_TARGET_HAS_rot_i64 have_zbb #define TCG_TARGET_HAS_deposit_i64 0 #define TCG_TARGET_HAS_extract_i64 0 #define TCG_TARGET_HAS_sextract_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_extrl_i64_i32 1 -#define TCG_TARGET_HAS_extrh_i64_i32 1 +#define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_ext8s_i64 1 #define TCG_TARGET_HAS_ext16s_i64 1 #define TCG_TARGET_HAS_ext32s_i64 1 #define TCG_TARGET_HAS_ext8u_i64 1 #define TCG_TARGET_HAS_ext16u_i64 1 #define TCG_TARGET_HAS_ext32u_i64 1 -#define TCG_TARGET_HAS_bswap16_i64 0 -#define TCG_TARGET_HAS_bswap32_i64 0 -#define TCG_TARGET_HAS_bswap64_i64 0 +#define TCG_TARGET_HAS_bswap16_i64 have_zbb +#define TCG_TARGET_HAS_bswap32_i64 have_zbb +#define TCG_TARGET_HAS_bswap64_i64 have_zbb #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_neg_i64 1 -#define TCG_TARGET_HAS_andc_i64 0 -#define TCG_TARGET_HAS_orc_i64 0 -#define TCG_TARGET_HAS_eqv_i64 0 +#define TCG_TARGET_HAS_andc_i64 have_zbb +#define TCG_TARGET_HAS_orc_i64 have_zbb +#define TCG_TARGET_HAS_eqv_i64 have_zbb #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 -#define TCG_TARGET_HAS_clz_i64 0 -#define TCG_TARGET_HAS_ctz_i64 0 -#define TCG_TARGET_HAS_ctpop_i64 0 +#define TCG_TARGET_HAS_clz_i64 have_zbb +#define TCG_TARGET_HAS_ctz_i64 have_zbb +#define TCG_TARGET_HAS_ctpop_i64 have_zbb #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_muls2_i64 0 #define TCG_TARGET_HAS_muluh_i64 1 #define TCG_TARGET_HAS_mulsh_i64 1 -#endif -/* not defined -- call should be eliminated at compile time */ -void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +#define TCG_TARGET_HAS_qemu_ldst_i128 0 + +#define TCG_TARGET_HAS_tst 0 #define TCG_TARGET_DEFAULT_MO (0) #define TCG_TARGET_NEED_LDST_LABELS #define TCG_TARGET_NEED_POOL_LABELS -#define TCG_TARGET_HAS_MEMORY_BSWAP 0 - #endif diff --git a/tcg/s390x/tcg-target-con-set.h b/tcg/s390x/tcg-target-con-set.h index 426dd92e51..f75955eaa8 100644 --- a/tcg/s390x/tcg-target-con-set.h +++ b/tcg/s390x/tcg-target-con-set.h @@ -8,13 +8,16 @@ * C_On_Im(...) defines a constraint set with outputs and inputs. * Each operand should be a sequence of constraint letters as defined by * tcg-target-con-str.h; the constraint combination is inclusive or. + * + * C_Nn_Om_Ik(...) defines a constraint set with outputs and + * inputs, except that the first outputs must use new registers. */ C_O0_I1(r) -C_O0_I2(L, L) C_O0_I2(r, r) C_O0_I2(r, ri) +C_O0_I2(r, rC) C_O0_I2(v, r) -C_O1_I1(r, L) +C_O0_I3(o, m, r) C_O1_I1(r, r) C_O1_I1(v, r) C_O1_I1(v, v) @@ -22,15 +25,24 @@ C_O1_I1(v, vr) C_O1_I2(r, 0, ri) C_O1_I2(r, 0, rI) C_O1_I2(r, 0, rJ) +C_O1_I2(r, r, r) C_O1_I2(r, r, ri) +C_O1_I2(r, r, rC) +C_O1_I2(r, r, rI) +C_O1_I2(r, r, rJ) +C_O1_I2(r, r, rK) +C_O1_I2(r, r, rKR) +C_O1_I2(r, r, rNK) +C_O1_I2(r, r, rNKR) C_O1_I2(r, rZ, r) C_O1_I2(v, v, r) C_O1_I2(v, v, v) C_O1_I3(v, v, v, v) -C_O1_I4(r, r, ri, r, 0) -C_O1_I4(r, r, ri, rI, 0) -C_O2_I2(b, a, 0, r) -C_O2_I3(b, a, 0, 1, r) -C_O2_I4(r, r, 0, 1, rA, r) -C_O2_I4(r, r, 0, 1, ri, r) -C_O2_I4(r, r, 0, 1, r, r) +C_O1_I4(r, r, ri, rI, r) +C_O1_I4(r, r, rC, rI, r) +C_O2_I1(o, m, r) +C_O2_I2(o, m, 0, r) +C_O2_I2(o, m, r, r) +C_O2_I3(o, m, 0, 1, r) +C_N1_O1_I4(r, r, 0, 1, ri, r) +C_N1_O1_I4(r, r, 0, 1, rJU, r) diff --git a/tcg/s390x/tcg-target-con-str.h b/tcg/s390x/tcg-target-con-str.h index 8bb0358ae5..745f6c0df5 100644 --- a/tcg/s390x/tcg-target-con-str.h +++ b/tcg/s390x/tcg-target-con-str.h @@ -9,21 +9,18 @@ * REGS(letter, register_mask) */ REGS('r', ALL_GENERAL_REGS) -REGS('L', ALL_GENERAL_REGS & ~SOFTMMU_RESERVE_REGS) REGS('v', ALL_VECTOR_REGS) -/* - * A (single) even/odd pair for division. - * TODO: Add something to the register allocator to allow - * this kind of regno+1 pairing to be done more generally. - */ -REGS('a', 1u << TCG_REG_R2) -REGS('b', 1u << TCG_REG_R3) +REGS('o', 0xaaaa) /* odd numbered general regs */ /* * Define constraint letters for constants: * CONST(letter, TCG_CT_CONST_* bit set) */ -CONST('A', TCG_CT_CONST_S33) +CONST('C', TCG_CT_CONST_CMP) CONST('I', TCG_CT_CONST_S16) CONST('J', TCG_CT_CONST_S32) +CONST('K', TCG_CT_CONST_P32) +CONST('N', TCG_CT_CONST_INV) +CONST('R', TCG_CT_CONST_INVRISBG) +CONST('U', TCG_CT_CONST_U32) CONST('Z', TCG_CT_CONST_ZERO) diff --git a/tcg/s390x/tcg-target-reg-bits.h b/tcg/s390x/tcg-target-reg-bits.h new file mode 100644 index 0000000000..b01414e09d --- /dev/null +++ b/tcg/s390x/tcg-target-reg-bits.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2009 Ulrich Hecht + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +/* We only support generating code for 64-bit mode. */ +#if UINTPTR_MAX == UINT64_MAX +# define TCG_TARGET_REG_BITS 64 +#else +# error "unsupported code generation mode" +#endif + +#endif diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 33becd7694..ad587325fc 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -24,40 +24,22 @@ * THE SOFTWARE. */ -/* We only support generating code for 64-bit mode. */ -#if TCG_TARGET_REG_BITS != 64 -#error "unsupported code generation mode" -#endif - #include "../tcg-ldst.c.inc" #include "../tcg-pool.c.inc" #include "elf.h" -/* ??? The translation blocks produced by TCG are generally small enough to - be entirely reachable with a 16-bit displacement. Leaving the option for - a 32-bit displacement here Just In Case. */ -#define USE_LONG_BRANCHES 0 - -#define TCG_CT_CONST_S16 0x100 -#define TCG_CT_CONST_S32 0x200 -#define TCG_CT_CONST_S33 0x400 -#define TCG_CT_CONST_ZERO 0x800 +#define TCG_CT_CONST_S16 (1 << 8) +#define TCG_CT_CONST_S32 (1 << 9) +#define TCG_CT_CONST_U32 (1 << 10) +#define TCG_CT_CONST_ZERO (1 << 11) +#define TCG_CT_CONST_P32 (1 << 12) +#define TCG_CT_CONST_INV (1 << 13) +#define TCG_CT_CONST_INVRISBG (1 << 14) +#define TCG_CT_CONST_CMP (1 << 15) #define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 16) #define ALL_VECTOR_REGS MAKE_64BIT_MASK(32, 32) -/* - * For softmmu, we need to avoid conflicts with the first 3 - * argument registers to perform the tlb lookup, and to call - * the helper function. - */ -#ifdef CONFIG_SOFTMMU -#define SOFTMMU_RESERVE_REGS MAKE_64BIT_MASK(TCG_REG_R2, 3) -#else -#define SOFTMMU_RESERVE_REGS 0 -#endif - - /* Several places within the instruction set 0 means "no register" rather than TCG_REG_R0. */ #define TCG_REG_NONE 0 @@ -65,15 +47,7 @@ /* A scratch register that may be be used throughout the backend. */ #define TCG_TMP0 TCG_REG_R1 -/* A scratch register that holds a pointer to the beginning of the TB. - We don't need this when we have pc-relative loads with the general - instructions extension facility. */ -#define TCG_REG_TB TCG_REG_R12 -#define USE_REG_TB (!HAVE_FACILITY(GEN_INST_EXT)) - -#ifndef CONFIG_SOFTMMU #define TCG_GUEST_BASE_REG TCG_REG_R13 -#endif /* All of the following instructions are prefixed with their instruction format, and are defined as 8- or 16-bit quantities, even when the two @@ -138,22 +112,29 @@ typedef enum S390Opcode { RI_OILH = 0xa50a, RI_OILL = 0xa50b, RI_TMLL = 0xa701, + RI_TMLH = 0xa700, + RI_TMHL = 0xa703, + RI_TMHH = 0xa702, - RIE_CGIJ = 0xec7c, - RIE_CGRJ = 0xec64, - RIE_CIJ = 0xec7e, - RIE_CLGRJ = 0xec65, - RIE_CLIJ = 0xec7f, - RIE_CLGIJ = 0xec7d, - RIE_CLRJ = 0xec77, - RIE_CRJ = 0xec76, - RIE_LOCGHI = 0xec46, - RIE_RISBG = 0xec55, + RIEb_CGRJ = 0xec64, + RIEb_CLGRJ = 0xec65, + RIEb_CLRJ = 0xec77, + RIEb_CRJ = 0xec76, + + RIEc_CGIJ = 0xec7c, + RIEc_CIJ = 0xec7e, + RIEc_CLGIJ = 0xec7d, + RIEc_CLIJ = 0xec7f, + + RIEf_RISBG = 0xec55, + + RIEg_LOCGHI = 0xec46, RRE_AGR = 0xb908, RRE_ALGR = 0xb90a, RRE_ALCR = 0xb998, RRE_ALCGR = 0xb988, + RRE_ALGFR = 0xb91a, RRE_CGR = 0xb920, RRE_CLGR = 0xb921, RRE_DLGR = 0xb987, @@ -183,18 +164,35 @@ typedef enum S390Opcode { RRE_SLBGR = 0xb989, RRE_XGR = 0xb982, - RRF_LOCR = 0xb9f2, - RRF_LOCGR = 0xb9e2, - RRF_NRK = 0xb9f4, - RRF_NGRK = 0xb9e4, - RRF_ORK = 0xb9f6, - RRF_OGRK = 0xb9e6, - RRF_SRK = 0xb9f9, - RRF_SGRK = 0xb9e9, - RRF_SLRK = 0xb9fb, - RRF_SLGRK = 0xb9eb, - RRF_XRK = 0xb9f7, - RRF_XGRK = 0xb9e7, + RRFa_MGRK = 0xb9ec, + RRFa_MSRKC = 0xb9fd, + RRFa_MSGRKC = 0xb9ed, + RRFa_NCRK = 0xb9f5, + RRFa_NCGRK = 0xb9e5, + RRFa_NNRK = 0xb974, + RRFa_NNGRK = 0xb964, + RRFa_NORK = 0xb976, + RRFa_NOGRK = 0xb966, + RRFa_NRK = 0xb9f4, + RRFa_NGRK = 0xb9e4, + RRFa_NXRK = 0xb977, + RRFa_NXGRK = 0xb967, + RRFa_OCRK = 0xb975, + RRFa_OCGRK = 0xb965, + RRFa_ORK = 0xb9f6, + RRFa_OGRK = 0xb9e6, + RRFa_SRK = 0xb9f9, + RRFa_SGRK = 0xb9e9, + RRFa_SLRK = 0xb9fb, + RRFa_SLGRK = 0xb9eb, + RRFa_XRK = 0xb9f7, + RRFa_XGRK = 0xb9e7, + + RRFam_SELGR = 0xb9e3, + + RRFc_LOCR = 0xb9f2, + RRFc_LOCGR = 0xb9e2, + RRFc_POPCNT = 0xb9e1, RR_AR = 0x1a, RR_ALR = 0x1e, @@ -242,6 +240,7 @@ typedef enum S390Opcode { RXY_LLGF = 0xe316, RXY_LLGH = 0xe391, RXY_LMG = 0xeb04, + RXY_LPQ = 0xe38f, RXY_LRV = 0xe31e, RXY_LRVG = 0xe30f, RXY_LRVH = 0xe31f, @@ -252,6 +251,7 @@ typedef enum S390Opcode { RXY_STG = 0xe324, RXY_STHY = 0xe370, RXY_STMG = 0xeb24, + RXY_STPQ = 0xe38e, RXY_STRV = 0xe33e, RXY_STRVG = 0xe32f, RXY_STRVH = 0xe33f, @@ -390,9 +390,12 @@ static const int tcg_target_call_iarg_regs[] = { TCG_REG_R6, }; -static const int tcg_target_call_oarg_regs[] = { - TCG_REG_R2, -}; +static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) +{ + tcg_debug_assert(kind == TCG_CALL_RET_NORMAL); + tcg_debug_assert(slot == 0); + return TCG_REG_R2; +} #define S390_CC_EQ 8 #define S390_CC_LT 4 @@ -404,10 +407,15 @@ static const int tcg_target_call_oarg_regs[] = { #define S390_CC_NEVER 0 #define S390_CC_ALWAYS 15 +#define S390_TM_EQ 8 /* CC == 0 */ +#define S390_TM_NE 7 /* CC in {1,2,3} */ + /* Condition codes that result from a COMPARE and COMPARE LOGICAL. */ -static const uint8_t tcg_cond_to_s390_cond[] = { +static const uint8_t tcg_cond_to_s390_cond[16] = { [TCG_COND_EQ] = S390_CC_EQ, [TCG_COND_NE] = S390_CC_NE, + [TCG_COND_TSTEQ] = S390_CC_EQ, + [TCG_COND_TSTNE] = S390_CC_NE, [TCG_COND_LT] = S390_CC_LT, [TCG_COND_LE] = S390_CC_LE, [TCG_COND_GT] = S390_CC_GT, @@ -421,9 +429,11 @@ static const uint8_t tcg_cond_to_s390_cond[] = { /* Condition codes that result from a LOAD AND TEST. Here, we have no unsigned instruction variation, however since the test is vs zero we can re-map the outcomes appropriately. */ -static const uint8_t tcg_cond_to_ltr_cond[] = { +static const uint8_t tcg_cond_to_ltr_cond[16] = { [TCG_COND_EQ] = S390_CC_EQ, [TCG_COND_NE] = S390_CC_NE, + [TCG_COND_TSTEQ] = S390_CC_ALWAYS, + [TCG_COND_TSTNE] = S390_CC_NEVER, [TCG_COND_LT] = S390_CC_LT, [TCG_COND_LE] = S390_CC_LE, [TCG_COND_GT] = S390_CC_GT, @@ -434,33 +444,6 @@ static const uint8_t tcg_cond_to_ltr_cond[] = { [TCG_COND_GEU] = S390_CC_ALWAYS, }; -#ifdef CONFIG_SOFTMMU -static void * const qemu_ld_helpers[(MO_SSIZE | MO_BSWAP) + 1] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_SB] = helper_ret_ldsb_mmu, - [MO_LEUW] = helper_le_lduw_mmu, - [MO_LESW] = helper_le_ldsw_mmu, - [MO_LEUL] = helper_le_ldul_mmu, - [MO_LESL] = helper_le_ldsl_mmu, - [MO_LEUQ] = helper_le_ldq_mmu, - [MO_BEUW] = helper_be_lduw_mmu, - [MO_BESW] = helper_be_ldsw_mmu, - [MO_BEUL] = helper_be_ldul_mmu, - [MO_BESL] = helper_be_ldsl_mmu, - [MO_BEUQ] = helper_be_ldq_mmu, -}; - -static void * const qemu_st_helpers[(MO_SIZE | MO_BSWAP) + 1] = { - [MO_UB] = helper_ret_stb_mmu, - [MO_LEUW] = helper_le_stw_mmu, - [MO_LEUL] = helper_le_stl_mmu, - [MO_LEUQ] = helper_le_stq_mmu, - [MO_BEUW] = helper_be_stw_mmu, - [MO_BEUL] = helper_be_stl_mmu, - [MO_BEUQ] = helper_be_stq_mmu, -}; -#endif - static const tcg_insn_unit *tb_ret_addr; uint64_t s390_facilities[3]; @@ -511,29 +494,129 @@ static bool patch_reloc(tcg_insn_unit *src_rw, int type, return false; } -/* Test if a constant matches the constraint. */ -static bool tcg_target_const_match(int64_t val, TCGType type, int ct) +static int is_const_p16(uint64_t val) { - if (ct & TCG_CT_CONST) { + for (int i = 0; i < 4; ++i) { + uint64_t mask = 0xffffull << (i * 16); + if ((val & ~mask) == 0) { + return i; + } + } + return -1; +} + +static int is_const_p32(uint64_t val) +{ + if ((val & 0xffffffff00000000ull) == 0) { + return 0; + } + if ((val & 0x00000000ffffffffull) == 0) { return 1; } + return -1; +} +/* + * Accept bit patterns like these: + * 0....01....1 + * 1....10....0 + * 1..10..01..1 + * 0..01..10..0 + * Copied from gcc sources. + */ +static bool risbg_mask(uint64_t c) +{ + uint64_t lsb; + /* We don't change the number of transitions by inverting, + so make sure we start with the LSB zero. */ + if (c & 1) { + c = ~c; + } + /* Reject all zeros or all ones. */ + if (c == 0) { + return false; + } + /* Find the first transition. */ + lsb = c & -c; + /* Invert to look for a second transition. */ + c = ~c; + /* Erase the first transition. */ + c &= -lsb; + /* Find the second transition, if any. */ + lsb = c & -c; + /* Match if all the bits are 1's, or if c is zero. */ + return c == -lsb; +} + +/* Test if a constant matches the constraint. */ +static bool tcg_target_const_match(int64_t val, int ct, + TCGType type, TCGCond cond, int vece) +{ + uint64_t uval = val; + + if (ct & TCG_CT_CONST) { + return true; + } if (type == TCG_TYPE_I32) { + uval = (uint32_t)val; val = (int32_t)val; } - /* The following are mutually exclusive. */ - if (ct & TCG_CT_CONST_S16) { - return val == (int16_t)val; - } else if (ct & TCG_CT_CONST_S32) { - return val == (int32_t)val; - } else if (ct & TCG_CT_CONST_S33) { - return val >= -0xffffffffll && val <= 0xffffffffll; - } else if (ct & TCG_CT_CONST_ZERO) { - return val == 0; + if (ct & TCG_CT_CONST_CMP) { + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + ct |= TCG_CT_CONST_S32 | TCG_CT_CONST_U32; /* CGFI or CLGFI */ + break; + case TCG_COND_LT: + case TCG_COND_GE: + case TCG_COND_LE: + case TCG_COND_GT: + ct |= TCG_CT_CONST_S32; /* CGFI */ + break; + case TCG_COND_LTU: + case TCG_COND_GEU: + case TCG_COND_LEU: + case TCG_COND_GTU: + ct |= TCG_CT_CONST_U32; /* CLGFI */ + break; + case TCG_COND_TSTNE: + case TCG_COND_TSTEQ: + if (is_const_p16(uval) >= 0) { + return true; /* TMxx */ + } + if (risbg_mask(uval)) { + return true; /* RISBG */ + } + break; + default: + g_assert_not_reached(); + } } - return 0; + if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { + return true; + } + if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) { + return true; + } + if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) { + return true; + } + if ((ct & TCG_CT_CONST_ZERO) && val == 0) { + return true; + } + + if (ct & TCG_CT_CONST_INV) { + val = ~val; + } + if ((ct & TCG_CT_CONST_P32) && is_const_p32(val) >= 0) { + return true; + } + if ((ct & TCG_CT_CONST_INVRISBG) && risbg_mask(~val)) { + return true; + } + return false; } /* Emit instructions according to the given instruction format. */ @@ -549,8 +632,22 @@ static void tcg_out_insn_RRE(TCGContext *s, S390Opcode op, tcg_out32(s, (op << 16) | (r1 << 4) | r2); } -static void tcg_out_insn_RRF(TCGContext *s, S390Opcode op, - TCGReg r1, TCGReg r2, int m3) +/* RRF-a without the m4 field */ +static void tcg_out_insn_RRFa(TCGContext *s, S390Opcode op, + TCGReg r1, TCGReg r2, TCGReg r3) +{ + tcg_out32(s, (op << 16) | (r3 << 12) | (r1 << 4) | r2); +} + +/* RRF-a with the m4 field */ +static void tcg_out_insn_RRFam(TCGContext *s, S390Opcode op, + TCGReg r1, TCGReg r2, TCGReg r3, int m4) +{ + tcg_out32(s, (op << 16) | (r3 << 12) | (m4 << 8) | (r1 << 4) | r2); +} + +static void tcg_out_insn_RRFc(TCGContext *s, S390Opcode op, + TCGReg r1, TCGReg r2, int m3) { tcg_out32(s, (op << 16) | (m3 << 12) | (r1 << 4) | r2); } @@ -560,7 +657,7 @@ static void tcg_out_insn_RI(TCGContext *s, S390Opcode op, TCGReg r1, int i2) tcg_out32(s, (op << 16) | (r1 << 20) | (i2 & 0xffff)); } -static void tcg_out_insn_RIE(TCGContext *s, S390Opcode op, TCGReg r1, +static void tcg_out_insn_RIEg(TCGContext *s, S390Opcode op, TCGReg r1, int i2, int m3) { tcg_out16(s, (op & 0xff00) | (r1 << 4) | m3); @@ -629,7 +726,7 @@ static void tcg_out_insn_VRIc(TCGContext *s, S390Opcode op, tcg_debug_assert(is_vector_reg(v3)); tcg_out16(s, (op & 0xff00) | ((v1 & 0xf) << 4) | (v3 & 0xf)); tcg_out16(s, i2); - tcg_out16(s, (op & 0x00ff) | RXB(v1, 0, v3, 0) | (m4 << 12)); + tcg_out16(s, (op & 0x00ff) | RXB(v1, v3, 0, 0) | (m4 << 12)); } static void tcg_out_insn_VRRa(TCGContext *s, S390Opcode op, @@ -684,7 +781,7 @@ static void tcg_out_insn_VRSa(TCGContext *s, S390Opcode op, TCGReg v1, tcg_debug_assert(is_vector_reg(v3)); tcg_out16(s, (op & 0xff00) | ((v1 & 0xf) << 4) | (v3 & 0xf)); tcg_out16(s, b2 << 12 | d2); - tcg_out16(s, (op & 0x00ff) | RXB(v1, 0, v3, 0) | (m4 << 12)); + tcg_out16(s, (op & 0x00ff) | RXB(v1, v3, 0, 0) | (m4 << 12)); } static void tcg_out_insn_VRSb(TCGContext *s, S390Opcode op, TCGReg v1, @@ -708,7 +805,7 @@ static void tcg_out_insn_VRSc(TCGContext *s, S390Opcode op, TCGReg r1, tcg_debug_assert(is_vector_reg(v3)); tcg_out16(s, (op & 0xff00) | (r1 << 4) | (v3 & 0xf)); tcg_out16(s, b2 << 12 | d2); - tcg_out16(s, (op & 0x00ff) | RXB(0, 0, v3, 0) | (m4 << 12)); + tcg_out16(s, (op & 0x00ff) | RXB(0, v3, 0, 0) | (m4 << 12)); } static void tcg_out_insn_VRX(TCGContext *s, S390Opcode op, TCGReg v1, @@ -780,14 +877,25 @@ static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg dst, TCGReg src) return true; } -static const S390Opcode lli_insns[4] = { +static const S390Opcode li_insns[4] = { RI_LLILL, RI_LLILH, RI_LLIHL, RI_LLIHH }; +static const S390Opcode oi_insns[4] = { + RI_OILL, RI_OILH, RI_OIHL, RI_OIHH +}; +static const S390Opcode lif_insns[2] = { + RIL_LLILF, RIL_LLIHF, +}; +static const S390Opcode tm_insns[4] = { + RI_TMLL, RI_TMLH, RI_TMHL, RI_TMHH +}; -static bool maybe_out_small_movi(TCGContext *s, TCGType type, - TCGReg ret, tcg_target_long sval) +/* load a register with an immediate value */ +static void tcg_out_movi(TCGContext *s, TCGType type, + TCGReg ret, tcg_target_long sval) { tcg_target_ulong uval = sval; + ptrdiff_t pc_off; int i; if (type == TCG_TYPE_I32) { @@ -798,100 +906,51 @@ static bool maybe_out_small_movi(TCGContext *s, TCGType type, /* Try all 32-bit insns that can load it in one go. */ if (sval >= -0x8000 && sval < 0x8000) { tcg_out_insn(s, RI, LGHI, ret, sval); - return true; - } - - for (i = 0; i < 4; i++) { - tcg_target_long mask = 0xffffull << i*16; - if ((uval & mask) == uval) { - tcg_out_insn_RI(s, lli_insns[i], ret, uval >> i*16); - return true; - } - } - - return false; -} - -/* load a register with an immediate value */ -static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, - tcg_target_long sval, bool in_prologue) -{ - tcg_target_ulong uval; - - /* Try all 32-bit insns that can load it in one go. */ - if (maybe_out_small_movi(s, type, ret, sval)) { return; } - uval = sval; - if (type == TCG_TYPE_I32) { - uval = (uint32_t)sval; - sval = (int32_t)sval; + i = is_const_p16(uval); + if (i >= 0) { + tcg_out_insn_RI(s, li_insns[i], ret, uval >> (i * 16)); + return; } /* Try all 48-bit insns that can load it in one go. */ - if (HAVE_FACILITY(EXT_IMM)) { - if (sval == (int32_t)sval) { - tcg_out_insn(s, RIL, LGFI, ret, sval); - return; - } - if (uval <= 0xffffffff) { - tcg_out_insn(s, RIL, LLILF, ret, uval); - return; - } - if ((uval & 0xffffffff) == 0) { - tcg_out_insn(s, RIL, LLIHF, ret, uval >> 32); - return; - } - } - - /* Try for PC-relative address load. For odd addresses, - attempt to use an offset from the start of the TB. */ - if ((sval & 1) == 0) { - ptrdiff_t off = tcg_pcrel_diff(s, (void *)sval) >> 1; - if (off == (int32_t)off) { - tcg_out_insn(s, RIL, LARL, ret, off); - return; - } - } else if (USE_REG_TB && !in_prologue) { - ptrdiff_t off = tcg_tbrel_diff(s, (void *)sval); - if (off == sextract64(off, 0, 20)) { - /* This is certain to be an address within TB, and therefore - OFF will be negative; don't try RX_LA. */ - tcg_out_insn(s, RXY, LAY, ret, TCG_REG_TB, TCG_REG_NONE, off); - return; - } - } - - /* A 32-bit unsigned value can be loaded in 2 insns. And given - that LLILL, LLIHL, LLILF above did not succeed, we know that - both insns are required. */ - if (uval <= 0xffffffff) { - tcg_out_insn(s, RI, LLILL, ret, uval); - tcg_out_insn(s, RI, IILH, ret, uval >> 16); + if (sval == (int32_t)sval) { + tcg_out_insn(s, RIL, LGFI, ret, sval); return; } - /* Otherwise, stuff it in the constant pool. */ - if (HAVE_FACILITY(GEN_INST_EXT)) { - tcg_out_insn(s, RIL, LGRL, ret, 0); - new_pool_label(s, sval, R_390_PC32DBL, s->code_ptr - 2, 2); - } else if (USE_REG_TB && !in_prologue) { - tcg_out_insn(s, RXY, LG, ret, TCG_REG_TB, TCG_REG_NONE, 0); - new_pool_label(s, sval, R_390_20, s->code_ptr - 2, - tcg_tbrel_diff(s, NULL)); - } else { - TCGReg base = ret ? ret : TCG_TMP0; - tcg_out_insn(s, RIL, LARL, base, 0); - new_pool_label(s, sval, R_390_PC32DBL, s->code_ptr - 2, 2); - tcg_out_insn(s, RXY, LG, ret, base, TCG_REG_NONE, 0); + i = is_const_p32(uval); + if (i >= 0) { + tcg_out_insn_RIL(s, lif_insns[i], ret, uval >> (i * 32)); + return; } -} -static void tcg_out_movi(TCGContext *s, TCGType type, - TCGReg ret, tcg_target_long sval) -{ - tcg_out_movi_int(s, type, ret, sval, false); + /* Try for PC-relative address load. For odd addresses, add one. */ + pc_off = tcg_pcrel_diff(s, (void *)sval) >> 1; + if (pc_off == (int32_t)pc_off) { + tcg_out_insn(s, RIL, LARL, ret, pc_off); + if (sval & 1) { + tcg_out_insn(s, RI, AGHI, ret, 1); + } + return; + } + + /* Otherwise, load it by parts. */ + i = is_const_p16((uint32_t)uval); + if (i >= 0) { + tcg_out_insn_RI(s, li_insns[i], ret, uval >> (i * 16)); + } else { + tcg_out_insn(s, RIL, LLILF, ret, uval); + } + uval >>= 32; + i = is_const_p16(uval); + if (i >= 0) { + tcg_out_insn_RI(s, oi_insns[i + 2], ret, uval >> (i * 16)); + } else { + tcg_out_insn(s, RIL, OIHF, ret, uval); + } } /* Emit a load/store type instruction. Inputs are: @@ -1020,162 +1079,70 @@ static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, return false; } -/* load data from an absolute host address */ -static void tcg_out_ld_abs(TCGContext *s, TCGType type, - TCGReg dest, const void *abs) +static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2) { - intptr_t addr = (intptr_t)abs; + return false; +} - if (HAVE_FACILITY(GEN_INST_EXT) && !(addr & 1)) { - ptrdiff_t disp = tcg_pcrel_diff(s, abs) >> 1; - if (disp == (int32_t)disp) { - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RIL, LRL, dest, disp); - } else { - tcg_out_insn(s, RIL, LGRL, dest, disp); - } - return; - } - } - if (USE_REG_TB) { - ptrdiff_t disp = tcg_tbrel_diff(s, abs); - if (disp == sextract64(disp, 0, 20)) { - tcg_out_ld(s, type, dest, TCG_REG_TB, disp); - return; - } - } - - tcg_out_movi(s, TCG_TYPE_PTR, dest, addr & ~0xffff); - tcg_out_ld(s, type, dest, dest, addr & 0xffff); +static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs, + tcg_target_long imm) +{ + /* This function is only used for passing structs by reference. */ + tcg_out_mem(s, RX_LA, RXY_LAY, rd, rs, TCG_REG_NONE, imm); } static inline void tcg_out_risbg(TCGContext *s, TCGReg dest, TCGReg src, int msb, int lsb, int ofs, int z) { /* Format RIE-f */ - tcg_out16(s, (RIE_RISBG & 0xff00) | (dest << 4) | src); + tcg_out16(s, (RIEf_RISBG & 0xff00) | (dest << 4) | src); tcg_out16(s, (msb << 8) | (z << 7) | lsb); - tcg_out16(s, (ofs << 8) | (RIE_RISBG & 0xff)); + tcg_out16(s, (ofs << 8) | (RIEf_RISBG & 0xff)); } -static void tgen_ext8s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) { - if (HAVE_FACILITY(EXT_IMM)) { - tcg_out_insn(s, RRE, LGBR, dest, src); - return; - } - - if (type == TCG_TYPE_I32) { - if (dest == src) { - tcg_out_sh32(s, RS_SLL, dest, TCG_REG_NONE, 24); - } else { - tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 24); - } - tcg_out_sh32(s, RS_SRA, dest, TCG_REG_NONE, 24); - } else { - tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 56); - tcg_out_sh64(s, RSY_SRAG, dest, dest, TCG_REG_NONE, 56); - } + tcg_out_insn(s, RRE, LGBR, dest, src); } -static void tgen_ext8u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +static void tcg_out_ext8u(TCGContext *s, TCGReg dest, TCGReg src) { - if (HAVE_FACILITY(EXT_IMM)) { - tcg_out_insn(s, RRE, LLGCR, dest, src); - return; - } - - if (dest == src) { - tcg_out_movi(s, type, TCG_TMP0, 0xff); - src = TCG_TMP0; - } else { - tcg_out_movi(s, type, dest, 0xff); - } - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RR, NR, dest, src); - } else { - tcg_out_insn(s, RRE, NGR, dest, src); - } + tcg_out_insn(s, RRE, LLGCR, dest, src); } -static void tgen_ext16s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) { - if (HAVE_FACILITY(EXT_IMM)) { - tcg_out_insn(s, RRE, LGHR, dest, src); - return; - } - - if (type == TCG_TYPE_I32) { - if (dest == src) { - tcg_out_sh32(s, RS_SLL, dest, TCG_REG_NONE, 16); - } else { - tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 16); - } - tcg_out_sh32(s, RS_SRA, dest, TCG_REG_NONE, 16); - } else { - tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 48); - tcg_out_sh64(s, RSY_SRAG, dest, dest, TCG_REG_NONE, 48); - } + tcg_out_insn(s, RRE, LGHR, dest, src); } -static void tgen_ext16u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +static void tcg_out_ext16u(TCGContext *s, TCGReg dest, TCGReg src) { - if (HAVE_FACILITY(EXT_IMM)) { - tcg_out_insn(s, RRE, LLGHR, dest, src); - return; - } - - if (dest == src) { - tcg_out_movi(s, type, TCG_TMP0, 0xffff); - src = TCG_TMP0; - } else { - tcg_out_movi(s, type, dest, 0xffff); - } - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RR, NR, dest, src); - } else { - tcg_out_insn(s, RRE, NGR, dest, src); - } + tcg_out_insn(s, RRE, LLGHR, dest, src); } -static inline void tgen_ext32s(TCGContext *s, TCGReg dest, TCGReg src) +static void tcg_out_ext32s(TCGContext *s, TCGReg dest, TCGReg src) { tcg_out_insn(s, RRE, LGFR, dest, src); } -static inline void tgen_ext32u(TCGContext *s, TCGReg dest, TCGReg src) +static void tcg_out_ext32u(TCGContext *s, TCGReg dest, TCGReg src) { tcg_out_insn(s, RRE, LLGFR, dest, src); } -/* Accept bit patterns like these: - 0....01....1 - 1....10....0 - 1..10..01..1 - 0..01..10..0 - Copied from gcc sources. */ -static inline bool risbg_mask(uint64_t c) +static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg dest, TCGReg src) { - uint64_t lsb; - /* We don't change the number of transitions by inverting, - so make sure we start with the LSB zero. */ - if (c & 1) { - c = ~c; - } - /* Reject all zeros or all ones. */ - if (c == 0) { - return false; - } - /* Find the first transition. */ - lsb = c & -c; - /* Invert to look for a second transition. */ - c = ~c; - /* Erase the first transition. */ - c &= -lsb; - /* Find the second transition, if any. */ - lsb = c & -c; - /* Match if all the bits are 1's, or if c is zero. */ - return c == -lsb; + tcg_out_ext32s(s, dest, src); +} + +static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg dest, TCGReg src) +{ + tcg_out_ext32u(s, dest, src); +} + +static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg dest, TCGReg src) +{ + tcg_out_mov(s, TCG_TYPE_I32, dest, src); } static void tgen_andi_risbg(TCGContext *s, TCGReg out, TCGReg in, uint64_t val) @@ -1205,162 +1172,113 @@ static void tgen_andi(TCGContext *s, TCGType type, TCGReg dest, uint64_t val) /* Look for the zero-extensions. */ if ((val & valid) == 0xffffffff) { - tgen_ext32u(s, dest, dest); + tcg_out_ext32u(s, dest, dest); return; } - if (HAVE_FACILITY(EXT_IMM)) { - if ((val & valid) == 0xff) { - tgen_ext8u(s, TCG_TYPE_I64, dest, dest); - return; - } - if ((val & valid) == 0xffff) { - tgen_ext16u(s, TCG_TYPE_I64, dest, dest); - return; - } + if ((val & valid) == 0xff) { + tcg_out_ext8u(s, dest, dest); + return; + } + if ((val & valid) == 0xffff) { + tcg_out_ext16u(s, dest, dest); + return; } - /* Try all 32-bit insns that can perform it in one go. */ - for (i = 0; i < 4; i++) { - tcg_target_ulong mask = ~(0xffffull << i*16); - if (((val | ~valid) & mask) == mask) { - tcg_out_insn_RI(s, ni_insns[i], dest, val >> i*16); - return; - } + i = is_const_p16(~val & valid); + if (i >= 0) { + tcg_out_insn_RI(s, ni_insns[i], dest, val >> (i * 16)); + return; } - /* Try all 48-bit insns that can perform it in one go. */ - if (HAVE_FACILITY(EXT_IMM)) { - for (i = 0; i < 2; i++) { - tcg_target_ulong mask = ~(0xffffffffull << i*32); - if (((val | ~valid) & mask) == mask) { - tcg_out_insn_RIL(s, nif_insns[i], dest, val >> i*32); - return; - } - } + i = is_const_p32(~val & valid); + tcg_debug_assert(i == 0 || type != TCG_TYPE_I32); + if (i >= 0) { + tcg_out_insn_RIL(s, nif_insns[i], dest, val >> (i * 32)); + return; } - if (HAVE_FACILITY(GEN_INST_EXT) && risbg_mask(val)) { + + if (risbg_mask(val)) { tgen_andi_risbg(s, dest, dest, val); return; } - /* Use the constant pool if USE_REG_TB, but not for small constants. */ - if (USE_REG_TB) { - if (!maybe_out_small_movi(s, type, TCG_TMP0, val)) { - tcg_out_insn(s, RXY, NG, dest, TCG_REG_TB, TCG_REG_NONE, 0); - new_pool_label(s, val & valid, R_390_20, s->code_ptr - 2, - tcg_tbrel_diff(s, NULL)); - return; - } - } else { - tcg_out_movi(s, type, TCG_TMP0, val); - } - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RR, NR, dest, TCG_TMP0); - } else { - tcg_out_insn(s, RRE, NGR, dest, TCG_TMP0); - } + g_assert_not_reached(); } -static void tgen_ori(TCGContext *s, TCGType type, TCGReg dest, uint64_t val) +static void tgen_ori(TCGContext *s, TCGReg dest, uint64_t val) { - static const S390Opcode oi_insns[4] = { - RI_OILL, RI_OILH, RI_OIHL, RI_OIHH - }; static const S390Opcode oif_insns[2] = { RIL_OILF, RIL_OIHF }; int i; - /* Look for no-op. */ - if (unlikely(val == 0)) { + i = is_const_p16(val); + if (i >= 0) { + tcg_out_insn_RI(s, oi_insns[i], dest, val >> (i * 16)); return; } - /* Try all 32-bit insns that can perform it in one go. */ - for (i = 0; i < 4; i++) { - tcg_target_ulong mask = (0xffffull << i*16); - if ((val & mask) != 0 && (val & ~mask) == 0) { - tcg_out_insn_RI(s, oi_insns[i], dest, val >> i*16); - return; - } + i = is_const_p32(val); + if (i >= 0) { + tcg_out_insn_RIL(s, oif_insns[i], dest, val >> (i * 32)); + return; } - /* Try all 48-bit insns that can perform it in one go. */ - if (HAVE_FACILITY(EXT_IMM)) { - for (i = 0; i < 2; i++) { - tcg_target_ulong mask = (0xffffffffull << i*32); - if ((val & mask) != 0 && (val & ~mask) == 0) { - tcg_out_insn_RIL(s, oif_insns[i], dest, val >> i*32); - return; - } - } - } - - /* Use the constant pool if USE_REG_TB, but not for small constants. */ - if (maybe_out_small_movi(s, type, TCG_TMP0, val)) { - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RR, OR, dest, TCG_TMP0); - } else { - tcg_out_insn(s, RRE, OGR, dest, TCG_TMP0); - } - } else if (USE_REG_TB) { - tcg_out_insn(s, RXY, OG, dest, TCG_REG_TB, TCG_REG_NONE, 0); - new_pool_label(s, val, R_390_20, s->code_ptr - 2, - tcg_tbrel_diff(s, NULL)); - } else { - /* Perform the OR via sequential modifications to the high and - low parts. Do this via recursion to handle 16-bit vs 32-bit - masks in each half. */ - tcg_debug_assert(HAVE_FACILITY(EXT_IMM)); - tgen_ori(s, type, dest, val & 0x00000000ffffffffull); - tgen_ori(s, type, dest, val & 0xffffffff00000000ull); - } + g_assert_not_reached(); } -static void tgen_xori(TCGContext *s, TCGType type, TCGReg dest, uint64_t val) +static void tgen_xori(TCGContext *s, TCGReg dest, uint64_t val) { - /* Try all 48-bit insns that can perform it in one go. */ - if (HAVE_FACILITY(EXT_IMM)) { - if ((val & 0xffffffff00000000ull) == 0) { - tcg_out_insn(s, RIL, XILF, dest, val); - return; - } - if ((val & 0x00000000ffffffffull) == 0) { - tcg_out_insn(s, RIL, XIHF, dest, val >> 32); - return; - } - } - - /* Use the constant pool if USE_REG_TB, but not for small constants. */ - if (maybe_out_small_movi(s, type, TCG_TMP0, val)) { - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RR, XR, dest, TCG_TMP0); - } else { - tcg_out_insn(s, RRE, XGR, dest, TCG_TMP0); - } - } else if (USE_REG_TB) { - tcg_out_insn(s, RXY, XG, dest, TCG_REG_TB, TCG_REG_NONE, 0); - new_pool_label(s, val, R_390_20, s->code_ptr - 2, - tcg_tbrel_diff(s, NULL)); - } else { - /* Perform the xor by parts. */ - tcg_debug_assert(HAVE_FACILITY(EXT_IMM)); - if (val & 0xffffffff) { - tcg_out_insn(s, RIL, XILF, dest, val); - } - if (val > 0xffffffff) { - tcg_out_insn(s, RIL, XIHF, dest, val >> 32); - } + switch (is_const_p32(val)) { + case 0: + tcg_out_insn(s, RIL, XILF, dest, val); + break; + case 1: + tcg_out_insn(s, RIL, XIHF, dest, val >> 32); + break; + default: + g_assert_not_reached(); } } -static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, - TCGArg c2, bool c2const, bool need_carry) +static int tgen_cmp2(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, + TCGArg c2, bool c2const, bool need_carry, int *inv_cc) { bool is_unsigned = is_unsigned_cond(c); + TCGCond inv_c = tcg_invert_cond(c); S390Opcode op; + if (is_tst_cond(c)) { + tcg_debug_assert(!need_carry); + + if (!c2const) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, NRK, TCG_REG_R0, r1, c2); + } else { + tcg_out_insn(s, RRFa, NGRK, TCG_REG_R0, r1, c2); + } + goto exit; + } + + if (type == TCG_TYPE_I32) { + c2 = (uint32_t)c2; + } + + int i = is_const_p16(c2); + if (i >= 0) { + tcg_out_insn_RI(s, tm_insns[i], r1, c2 >> (i * 16)); + *inv_cc = c == TCG_COND_TSTEQ ? S390_TM_NE : S390_TM_EQ; + return *inv_cc ^ 15; + } + + if (risbg_mask(c2)) { + tgen_andi_risbg(s, TCG_REG_R0, r1, c2); + goto exit; + } + g_assert_not_reached(); + } + if (c2const) { if (c2 == 0) { if (!(is_unsigned && need_carry)) { @@ -1369,6 +1287,7 @@ static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, } else { tcg_out_insn(s, RRE, LTGR, r1, r1); } + *inv_cc = tcg_cond_to_ltr_cond[inv_c]; return tcg_cond_to_ltr_cond[c]; } } @@ -1379,51 +1298,40 @@ static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, goto exit; } - if (HAVE_FACILITY(EXT_IMM)) { - if (type == TCG_TYPE_I32) { - op = (is_unsigned ? RIL_CLFI : RIL_CFI); - tcg_out_insn_RIL(s, op, r1, c2); - goto exit; - } else if (c2 == (is_unsigned ? (TCGArg)(uint32_t)c2 : (TCGArg)(int32_t)c2)) { - op = (is_unsigned ? RIL_CLGFI : RIL_CGFI); - tcg_out_insn_RIL(s, op, r1, c2); - goto exit; - } - } - - /* Use the constant pool, but not for small constants. */ - if (maybe_out_small_movi(s, type, TCG_TMP0, c2)) { - c2 = TCG_TMP0; - /* fall through to reg-reg */ - } else if (USE_REG_TB) { - if (type == TCG_TYPE_I32) { - op = (is_unsigned ? RXY_CLY : RXY_CY); - tcg_out_insn_RXY(s, op, r1, TCG_REG_TB, TCG_REG_NONE, 0); - new_pool_label(s, (uint32_t)c2, R_390_20, s->code_ptr - 2, - 4 - tcg_tbrel_diff(s, NULL)); - } else { - op = (is_unsigned ? RXY_CLG : RXY_CG); - tcg_out_insn_RXY(s, op, r1, TCG_REG_TB, TCG_REG_NONE, 0); - new_pool_label(s, c2, R_390_20, s->code_ptr - 2, - tcg_tbrel_diff(s, NULL)); - } - goto exit; - } else { - if (type == TCG_TYPE_I32) { - op = (is_unsigned ? RIL_CLRL : RIL_CRL); - tcg_out_insn_RIL(s, op, r1, 0); - new_pool_label(s, (uint32_t)c2, R_390_PC32DBL, - s->code_ptr - 2, 2 + 4); - } else { - op = (is_unsigned ? RIL_CLGRL : RIL_CGRL); - tcg_out_insn_RIL(s, op, r1, 0); - new_pool_label(s, c2, R_390_PC32DBL, s->code_ptr - 2, 2); - } + if (type == TCG_TYPE_I32) { + op = (is_unsigned ? RIL_CLFI : RIL_CFI); + tcg_out_insn_RIL(s, op, r1, c2); goto exit; } - } - if (type == TCG_TYPE_I32) { + /* Should match TCG_CT_CONST_CMP. */ + switch (c) { + case TCG_COND_LT: + case TCG_COND_GE: + case TCG_COND_LE: + case TCG_COND_GT: + tcg_debug_assert(c2 == (int32_t)c2); + op = RIL_CGFI; + break; + case TCG_COND_EQ: + case TCG_COND_NE: + if (c2 == (int32_t)c2) { + op = RIL_CGFI; + break; + } + /* fall through */ + case TCG_COND_LTU: + case TCG_COND_GEU: + case TCG_COND_LEU: + case TCG_COND_GTU: + tcg_debug_assert(c2 == (uint32_t)c2); + op = RIL_CLGFI; + break; + default: + g_assert_not_reached(); + } + tcg_out_insn_RIL(s, op, r1, c2); + } else if (type == TCG_TYPE_I32) { op = (is_unsigned ? RR_CLR : RR_CR); tcg_out_insn_RR(s, op, r1, c2); } else { @@ -1432,28 +1340,49 @@ static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, } exit: + *inv_cc = tcg_cond_to_s390_cond[inv_c]; return tcg_cond_to_s390_cond[c]; } +static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, + TCGArg c2, bool c2const, bool need_carry) +{ + int inv_cc; + return tgen_cmp2(s, type, c, r1, c2, c2const, need_carry, &inv_cc); +} + static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, - TCGReg dest, TCGReg c1, TCGArg c2, int c2const) + TCGReg dest, TCGReg c1, TCGArg c2, + bool c2const, bool neg) { int cc; - bool have_loc; /* With LOC2, we can always emit the minimum 3 insns. */ if (HAVE_FACILITY(LOAD_ON_COND2)) { /* Emit: d = 0, d = (cc ? 1 : d). */ cc = tgen_cmp(s, type, cond, c1, c2, c2const, false); tcg_out_movi(s, TCG_TYPE_I64, dest, 0); - tcg_out_insn(s, RIE, LOCGHI, dest, 1, cc); + tcg_out_insn(s, RIEg, LOCGHI, dest, neg ? -1 : 1, cc); return; } - have_loc = HAVE_FACILITY(LOAD_ON_COND); + switch (cond) { + case TCG_COND_GEU: + case TCG_COND_LTU: + case TCG_COND_LT: + case TCG_COND_GE: + /* Swap operands so that we can use LEU/GTU/GT/LE. */ + if (!c2const) { + TCGReg t = c1; + c1 = c2; + c2 = t; + cond = tcg_swap_cond(cond); + } + break; + default: + break; + } - /* For HAVE_LOC, only the paths through GTU/GT/LEU/LE are smaller. */ - restart: switch (cond) { case TCG_COND_NE: /* X != 0 is X > 0. */ @@ -1466,11 +1395,20 @@ static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, case TCG_COND_GTU: case TCG_COND_GT: - /* The result of a compare has CC=2 for GT and CC=3 unused. - ADD LOGICAL WITH CARRY considers (CC & 2) the carry bit. */ + /* + * The result of a compare has CC=2 for GT and CC=3 unused. + * ADD LOGICAL WITH CARRY considers (CC & 2) the carry bit. + */ tgen_cmp(s, type, cond, c1, c2, c2const, true); tcg_out_movi(s, type, dest, 0); tcg_out_insn(s, RRE, ALCGR, dest, dest); + if (neg) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, LCR, dest, dest); + } else { + tcg_out_insn(s, RRE, LCGR, dest, dest); + } + } return; case TCG_COND_EQ: @@ -1484,73 +1422,77 @@ static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, case TCG_COND_LEU: case TCG_COND_LE: - /* As above, but we're looking for borrow, or !carry. - The second insn computes d - d - borrow, or -1 for true - and 0 for false. So we must mask to 1 bit afterward. */ + /* + * As above, but we're looking for borrow, or !carry. + * The second insn computes d - d - borrow, or -1 for true + * and 0 for false. So we must mask to 1 bit afterward. + */ tgen_cmp(s, type, cond, c1, c2, c2const, true); tcg_out_insn(s, RRE, SLBGR, dest, dest); - tgen_andi(s, type, dest, 1); - return; - - case TCG_COND_GEU: - case TCG_COND_LTU: - case TCG_COND_LT: - case TCG_COND_GE: - /* Swap operands so that we can use LEU/GTU/GT/LE. */ - if (c2const) { - if (have_loc) { - break; - } - tcg_out_movi(s, type, TCG_TMP0, c2); - c2 = c1; - c2const = 0; - c1 = TCG_TMP0; - } else { - TCGReg t = c1; - c1 = c2; - c2 = t; + if (!neg) { + tgen_andi(s, type, dest, 1); } - cond = tcg_swap_cond(cond); - goto restart; + return; default: g_assert_not_reached(); } cc = tgen_cmp(s, type, cond, c1, c2, c2const, false); - if (have_loc) { - /* Emit: d = 0, t = 1, d = (cc ? t : d). */ - tcg_out_movi(s, TCG_TYPE_I64, dest, 0); - tcg_out_movi(s, TCG_TYPE_I64, TCG_TMP0, 1); - tcg_out_insn(s, RRF, LOCGR, dest, TCG_TMP0, cc); + /* Emit: d = 0, t = 1, d = (cc ? t : d). */ + tcg_out_movi(s, TCG_TYPE_I64, dest, 0); + tcg_out_movi(s, TCG_TYPE_I64, TCG_TMP0, neg ? -1 : 1); + tcg_out_insn(s, RRFc, LOCGR, dest, TCG_TMP0, cc); +} + +static void tgen_movcond_int(TCGContext *s, TCGType type, TCGReg dest, + TCGArg v3, int v3const, TCGReg v4, + int cc, int inv_cc) +{ + TCGReg src; + + if (v3const) { + if (dest == v4) { + if (HAVE_FACILITY(LOAD_ON_COND2)) { + /* Emit: if (cc) dest = v3. */ + tcg_out_insn(s, RIEg, LOCGHI, dest, v3, cc); + return; + } + tcg_out_insn(s, RI, LGHI, TCG_TMP0, v3); + src = TCG_TMP0; + } else { + /* LGR+LOCGHI is larger than LGHI+LOCGR. */ + tcg_out_insn(s, RI, LGHI, dest, v3); + cc = inv_cc; + src = v4; + } } else { - /* Emit: d = 1; if (cc) goto over; d = 0; over: */ - tcg_out_movi(s, type, dest, 1); - tcg_out_insn(s, RI, BRC, cc, (4 + 4) >> 1); - tcg_out_movi(s, type, dest, 0); + if (HAVE_FACILITY(MISC_INSN_EXT3)) { + /* Emit: dest = cc ? v3 : v4. */ + tcg_out_insn(s, RRFam, SELGR, dest, v3, v4, cc); + return; + } + if (dest == v4) { + src = v3; + } else { + tcg_out_mov(s, type, dest, v3); + cc = inv_cc; + src = v4; + } } + + /* Emit: if (cc) dest = src. */ + tcg_out_insn(s, RRFc, LOCGR, dest, src, cc); } static void tgen_movcond(TCGContext *s, TCGType type, TCGCond c, TCGReg dest, TCGReg c1, TCGArg c2, int c2const, - TCGArg v3, int v3const) + TCGArg v3, int v3const, TCGReg v4) { - int cc; - if (HAVE_FACILITY(LOAD_ON_COND)) { - cc = tgen_cmp(s, type, c, c1, c2, c2const, false); - if (v3const) { - tcg_out_insn(s, RIE, LOCGHI, dest, v3, cc); - } else { - tcg_out_insn(s, RRF, LOCGR, dest, v3, cc); - } - } else { - c = tcg_invert_cond(c); - cc = tgen_cmp(s, type, c, c1, c2, c2const, false); + int cc, inv_cc; - /* Emit: if (cc) goto over; dest = r3; over: */ - tcg_out_insn(s, RI, BRC, cc, (4 + 4) >> 1); - tcg_out_insn(s, RRE, LGR, dest, v3); - } + cc = tgen_cmp2(s, type, c, c1, c2, c2const, false, &inv_cc); + tgen_movcond_int(s, type, dest, v3, v3const, v4, cc, inv_cc); } static void tgen_clz(TCGContext *s, TCGReg dest, TCGReg a1, @@ -1563,20 +1505,40 @@ static void tgen_clz(TCGContext *s, TCGReg dest, TCGReg a1, if (a2const && a2 == 64) { tcg_out_mov(s, TCG_TYPE_I64, dest, TCG_REG_R0); + return; + } + + /* + * Conditions from FLOGR are: + * 2 -> one bit found + * 8 -> no one bit found + */ + tgen_movcond_int(s, TCG_TYPE_I64, dest, a2, a2const, TCG_REG_R0, 8, 2); +} + +static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +{ + /* With MIE3, and bit 0 of m4 set, we get the complete result. */ + if (HAVE_FACILITY(MISC_INSN_EXT3)) { + if (type == TCG_TYPE_I32) { + tcg_out_ext32u(s, dest, src); + src = dest; + } + tcg_out_insn(s, RRFc, POPCNT, dest, src, 8); + return; + } + + /* Without MIE3, each byte gets the count of bits for the byte. */ + tcg_out_insn(s, RRFc, POPCNT, dest, src, 0); + + /* Multiply to sum each byte at the top of the word. */ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, MSFI, dest, 0x01010101); + tcg_out_sh32(s, RS_SRL, dest, TCG_REG_NONE, 24); } else { - if (a2const) { - tcg_out_movi(s, TCG_TYPE_I64, dest, a2); - } else { - tcg_out_mov(s, TCG_TYPE_I64, dest, a2); - } - if (HAVE_FACILITY(LOAD_ON_COND)) { - /* Emit: if (one bit found) dest = r0. */ - tcg_out_insn(s, RRF, LOCGR, dest, TCG_REG_R0, 2); - } else { - /* Emit: if (no one bit found) goto over; dest = r0; over: */ - tcg_out_insn(s, RI, BRC, 8, (4 + 4) >> 1); - tcg_out_insn(s, RRE, LGR, dest, TCG_REG_R0); - } + tcg_out_movi(s, TCG_TYPE_I64, TCG_TMP0, 0x0101010101010101ull); + tcg_out_insn(s, RRE, MSGR, dest, TCG_TMP0); + tcg_out_sh64(s, RSY_SRLG, dest, dest, TCG_REG_NONE, 56); } } @@ -1611,10 +1573,6 @@ static void tgen_branch(TCGContext *s, int cc, TCGLabel *l) { if (l->has_value) { tgen_gotoi(s, cc, l->u.value_ptr); - } else if (USE_LONG_BRANCHES) { - tcg_out16(s, RIL_BRCL | (cc << 4)); - tcg_out_reloc(s, s->code_ptr, R_390_PC32DBL, l, 2); - s->code_ptr += 2; } else { tcg_out16(s, RI_BRC | (cc << 4)); tcg_out_reloc(s, s->code_ptr, R_390_PC16DBL, l, 2); @@ -1626,6 +1584,7 @@ static void tgen_compare_branch(TCGContext *s, S390Opcode opc, int cc, TCGReg r1, TCGReg r2, TCGLabel *l) { tcg_out_reloc(s, s->code_ptr + 1, R_390_PC16DBL, l, 2); + /* Format RIE-b */ tcg_out16(s, (opc & 0xff00) | (r1 << 4) | r2); tcg_out16(s, 0); tcg_out16(s, cc << 12 | (opc & 0xff)); @@ -1635,6 +1594,7 @@ static void tgen_compare_imm_branch(TCGContext *s, S390Opcode opc, int cc, TCGReg r1, int i2, TCGLabel *l) { tcg_out_reloc(s, s->code_ptr + 1, R_390_PC16DBL, l, 2); + /* Format RIE-c */ tcg_out16(s, (opc & 0xff00) | (r1 << 4) | cc); tcg_out16(s, 0); tcg_out16(s, (i2 << 8) | (opc & 0xff)); @@ -1645,7 +1605,7 @@ static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, { int cc; - if (HAVE_FACILITY(GEN_INST_EXT)) { + if (!is_tst_cond(c)) { bool is_unsigned = is_unsigned_cond(c); bool in_range; S390Opcode opc; @@ -1654,30 +1614,32 @@ static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, if (!c2const) { opc = (type == TCG_TYPE_I32 - ? (is_unsigned ? RIE_CLRJ : RIE_CRJ) - : (is_unsigned ? RIE_CLGRJ : RIE_CGRJ)); + ? (is_unsigned ? RIEb_CLRJ : RIEb_CRJ) + : (is_unsigned ? RIEb_CLGRJ : RIEb_CGRJ)); tgen_compare_branch(s, opc, cc, r1, c2, l); return; } - /* COMPARE IMMEDIATE AND BRANCH RELATIVE has an 8-bit immediate field. - If the immediate we've been given does not fit that range, we'll - fall back to separate compare and branch instructions using the - larger comparison range afforded by COMPARE IMMEDIATE. */ + /* + * COMPARE IMMEDIATE AND BRANCH RELATIVE has an 8-bit immediate field. + * If the immediate we've been given does not fit that range, we'll + * fall back to separate compare and branch instructions using the + * larger comparison range afforded by COMPARE IMMEDIATE. + */ if (type == TCG_TYPE_I32) { if (is_unsigned) { - opc = RIE_CLIJ; + opc = RIEc_CLIJ; in_range = (uint32_t)c2 == (uint8_t)c2; } else { - opc = RIE_CIJ; + opc = RIEc_CIJ; in_range = (int32_t)c2 == (int8_t)c2; } } else { if (is_unsigned) { - opc = RIE_CLGIJ; + opc = RIEc_CLGIJ; in_range = (uint64_t)c2 == (uint8_t)c2; } else { - opc = RIE_CGIJ; + opc = RIEc_CGIJ; in_range = (int64_t)c2 == (int8_t)c2; } } @@ -1691,7 +1653,7 @@ static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, tgen_branch(s, cc, l); } -static void tcg_out_call(TCGContext *s, const tcg_insn_unit *dest) +static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *dest) { ptrdiff_t off = tcg_pcrel_diff(s, dest) >> 1; if (off == (int32_t)off) { @@ -1702,203 +1664,156 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *dest) } } +static void tcg_out_call(TCGContext *s, const tcg_insn_unit *dest, + const TCGHelperInfo *info) +{ + tcg_out_call_int(s, dest); +} + +typedef struct { + TCGReg base; + TCGReg index; + int disp; + TCGAtomAlign aa; +} HostAddress; + +bool tcg_target_has_memory_bswap(MemOp memop) +{ + TCGAtomAlign aa; + + if ((memop & MO_SIZE) <= MO_64) { + return true; + } + + /* + * Reject 16-byte memop with 16-byte atomicity, + * but do allow a pair of 64-bit operations. + */ + aa = atom_and_align_for_opc(tcg_ctx, memop, MO_ATOM_IFALIGN, true); + return aa.atom <= MO_64; +} + static void tcg_out_qemu_ld_direct(TCGContext *s, MemOp opc, TCGReg data, - TCGReg base, TCGReg index, int disp) + HostAddress h) { switch (opc & (MO_SSIZE | MO_BSWAP)) { case MO_UB: - tcg_out_insn(s, RXY, LLGC, data, base, index, disp); + tcg_out_insn(s, RXY, LLGC, data, h.base, h.index, h.disp); break; case MO_SB: - tcg_out_insn(s, RXY, LGB, data, base, index, disp); + tcg_out_insn(s, RXY, LGB, data, h.base, h.index, h.disp); break; case MO_UW | MO_BSWAP: /* swapped unsigned halfword load with upper bits zeroed */ - tcg_out_insn(s, RXY, LRVH, data, base, index, disp); - tgen_ext16u(s, TCG_TYPE_I64, data, data); + tcg_out_insn(s, RXY, LRVH, data, h.base, h.index, h.disp); + tcg_out_ext16u(s, data, data); break; case MO_UW: - tcg_out_insn(s, RXY, LLGH, data, base, index, disp); + tcg_out_insn(s, RXY, LLGH, data, h.base, h.index, h.disp); break; case MO_SW | MO_BSWAP: /* swapped sign-extended halfword load */ - tcg_out_insn(s, RXY, LRVH, data, base, index, disp); - tgen_ext16s(s, TCG_TYPE_I64, data, data); + tcg_out_insn(s, RXY, LRVH, data, h.base, h.index, h.disp); + tcg_out_ext16s(s, TCG_TYPE_REG, data, data); break; case MO_SW: - tcg_out_insn(s, RXY, LGH, data, base, index, disp); + tcg_out_insn(s, RXY, LGH, data, h.base, h.index, h.disp); break; case MO_UL | MO_BSWAP: /* swapped unsigned int load with upper bits zeroed */ - tcg_out_insn(s, RXY, LRV, data, base, index, disp); - tgen_ext32u(s, data, data); + tcg_out_insn(s, RXY, LRV, data, h.base, h.index, h.disp); + tcg_out_ext32u(s, data, data); break; case MO_UL: - tcg_out_insn(s, RXY, LLGF, data, base, index, disp); + tcg_out_insn(s, RXY, LLGF, data, h.base, h.index, h.disp); break; case MO_SL | MO_BSWAP: /* swapped sign-extended int load */ - tcg_out_insn(s, RXY, LRV, data, base, index, disp); - tgen_ext32s(s, data, data); + tcg_out_insn(s, RXY, LRV, data, h.base, h.index, h.disp); + tcg_out_ext32s(s, data, data); break; case MO_SL: - tcg_out_insn(s, RXY, LGF, data, base, index, disp); + tcg_out_insn(s, RXY, LGF, data, h.base, h.index, h.disp); break; case MO_UQ | MO_BSWAP: - tcg_out_insn(s, RXY, LRVG, data, base, index, disp); + tcg_out_insn(s, RXY, LRVG, data, h.base, h.index, h.disp); break; case MO_UQ: - tcg_out_insn(s, RXY, LG, data, base, index, disp); + tcg_out_insn(s, RXY, LG, data, h.base, h.index, h.disp); break; default: - tcg_abort(); + g_assert_not_reached(); } } static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg data, - TCGReg base, TCGReg index, int disp) + HostAddress h) { switch (opc & (MO_SIZE | MO_BSWAP)) { case MO_UB: - if (disp >= 0 && disp < 0x1000) { - tcg_out_insn(s, RX, STC, data, base, index, disp); + if (h.disp >= 0 && h.disp < 0x1000) { + tcg_out_insn(s, RX, STC, data, h.base, h.index, h.disp); } else { - tcg_out_insn(s, RXY, STCY, data, base, index, disp); + tcg_out_insn(s, RXY, STCY, data, h.base, h.index, h.disp); } break; case MO_UW | MO_BSWAP: - tcg_out_insn(s, RXY, STRVH, data, base, index, disp); + tcg_out_insn(s, RXY, STRVH, data, h.base, h.index, h.disp); break; case MO_UW: - if (disp >= 0 && disp < 0x1000) { - tcg_out_insn(s, RX, STH, data, base, index, disp); + if (h.disp >= 0 && h.disp < 0x1000) { + tcg_out_insn(s, RX, STH, data, h.base, h.index, h.disp); } else { - tcg_out_insn(s, RXY, STHY, data, base, index, disp); + tcg_out_insn(s, RXY, STHY, data, h.base, h.index, h.disp); } break; case MO_UL | MO_BSWAP: - tcg_out_insn(s, RXY, STRV, data, base, index, disp); + tcg_out_insn(s, RXY, STRV, data, h.base, h.index, h.disp); break; case MO_UL: - if (disp >= 0 && disp < 0x1000) { - tcg_out_insn(s, RX, ST, data, base, index, disp); + if (h.disp >= 0 && h.disp < 0x1000) { + tcg_out_insn(s, RX, ST, data, h.base, h.index, h.disp); } else { - tcg_out_insn(s, RXY, STY, data, base, index, disp); + tcg_out_insn(s, RXY, STY, data, h.base, h.index, h.disp); } break; case MO_UQ | MO_BSWAP: - tcg_out_insn(s, RXY, STRVG, data, base, index, disp); + tcg_out_insn(s, RXY, STRVG, data, h.base, h.index, h.disp); break; case MO_UQ: - tcg_out_insn(s, RXY, STG, data, base, index, disp); + tcg_out_insn(s, RXY, STG, data, h.base, h.index, h.disp); break; default: - tcg_abort(); + g_assert_not_reached(); } } -#if defined(CONFIG_SOFTMMU) -/* 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); -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -(1 << 19)); - -/* Load and compare a TLB entry, leaving the flags set. Loads the TLB - addend into R2. Returns a register with the santitized guest address. */ -static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addr_reg, MemOp opc, - int mem_index, bool is_ld) -{ - unsigned s_bits = opc & MO_SIZE; - unsigned a_bits = get_alignment_bits(opc); - unsigned s_mask = (1 << s_bits) - 1; - unsigned a_mask = (1 << a_bits) - 1; - int fast_off = TLB_MASK_TABLE_OFS(mem_index); - int mask_off = fast_off + offsetof(CPUTLBDescFast, mask); - int table_off = fast_off + offsetof(CPUTLBDescFast, table); - int ofs, a_off; - uint64_t tlb_mask; - - tcg_out_sh64(s, RSY_SRLG, TCG_REG_R2, addr_reg, TCG_REG_NONE, - TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - tcg_out_insn(s, RXY, NG, TCG_REG_R2, TCG_AREG0, TCG_REG_NONE, mask_off); - tcg_out_insn(s, RXY, AG, TCG_REG_R2, TCG_AREG0, TCG_REG_NONE, table_off); - - /* For aligned accesses, we check the first byte and include the alignment - bits within the address. For unaligned access, we check that we don't - cross pages using the address of the last byte of the access. */ - a_off = (a_bits >= s_bits ? 0 : s_mask - a_mask); - tlb_mask = (uint64_t)TARGET_PAGE_MASK | a_mask; - if (HAVE_FACILITY(GEN_INST_EXT) && a_off == 0) { - tgen_andi_risbg(s, TCG_REG_R3, addr_reg, tlb_mask); - } else { - tcg_out_insn(s, RX, LA, TCG_REG_R3, addr_reg, TCG_REG_NONE, a_off); - tgen_andi(s, TCG_TYPE_TL, TCG_REG_R3, tlb_mask); - } - - if (is_ld) { - ofs = offsetof(CPUTLBEntry, addr_read); - } else { - ofs = offsetof(CPUTLBEntry, addr_write); - } - if (TARGET_LONG_BITS == 32) { - tcg_out_insn(s, RX, C, TCG_REG_R3, TCG_REG_R2, TCG_REG_NONE, ofs); - } else { - tcg_out_insn(s, RXY, CG, TCG_REG_R3, TCG_REG_R2, TCG_REG_NONE, ofs); - } - - tcg_out_insn(s, RXY, LG, TCG_REG_R2, TCG_REG_R2, TCG_REG_NONE, - offsetof(CPUTLBEntry, addend)); - - if (TARGET_LONG_BITS == 32) { - tgen_ext32u(s, TCG_REG_R3, addr_reg); - return TCG_REG_R3; - } - return addr_reg; -} - -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, MemOpIdx oi, - TCGReg data, TCGReg addr, - tcg_insn_unit *raddr, tcg_insn_unit *label_ptr) -{ - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->oi = oi; - label->datalo_reg = data; - label->addrlo_reg = addr; - label->raddr = tcg_splitwx_to_rx(raddr); - label->label_ptr[0] = label_ptr; -} +static const TCGLdstHelperParam ldst_helper_param = { + .ntmp = 1, .tmp = { TCG_TMP0 } +}; static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { - TCGReg addr_reg = lb->addrlo_reg; - TCGReg data_reg = lb->datalo_reg; - MemOpIdx oi = lb->oi; - MemOp opc = get_memop(oi); + MemOp opc = get_memop(lb->oi); if (!patch_reloc(lb->label_ptr[0], R_390_PC16DBL, (intptr_t)tcg_splitwx_to_rx(s->code_ptr), 2)) { return false; } - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R2, TCG_AREG0); - if (TARGET_LONG_BITS == 64) { - tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R3, addr_reg); - } - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R4, oi); - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R5, (uintptr_t)lb->raddr); - tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SSIZE)]); - tcg_out_mov(s, TCG_TYPE_I64, data_reg, TCG_REG_R2); + tcg_out_ld_helper_args(s, lb, &ldst_helper_param); + tcg_out_call_int(s, qemu_ld_helpers[opc & MO_SIZE]); + tcg_out_ld_helper_ret(s, lb, false, &ldst_helper_param); tgen_gotoi(s, S390_CC_ALWAYS, lb->raddr); return true; @@ -1906,168 +1821,277 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { - TCGReg addr_reg = lb->addrlo_reg; - TCGReg data_reg = lb->datalo_reg; - MemOpIdx oi = lb->oi; - MemOp opc = get_memop(oi); + MemOp opc = get_memop(lb->oi); if (!patch_reloc(lb->label_ptr[0], R_390_PC16DBL, (intptr_t)tcg_splitwx_to_rx(s->code_ptr), 2)) { return false; } - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R2, TCG_AREG0); - if (TARGET_LONG_BITS == 64) { - tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R3, addr_reg); - } - switch (opc & MO_SIZE) { - case MO_UB: - tgen_ext8u(s, TCG_TYPE_I64, TCG_REG_R4, data_reg); - break; - case MO_UW: - tgen_ext16u(s, TCG_TYPE_I64, TCG_REG_R4, data_reg); - break; - case MO_UL: - tgen_ext32u(s, TCG_REG_R4, data_reg); - break; - case MO_UQ: - tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R4, data_reg); - break; - default: - tcg_abort(); - } - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R5, oi); - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R6, (uintptr_t)lb->raddr); - tcg_out_call(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]); + tcg_out_st_helper_args(s, lb, &ldst_helper_param); + tcg_out_call_int(s, qemu_st_helpers[opc & MO_SIZE]); tgen_gotoi(s, S390_CC_ALWAYS, lb->raddr); return true; } -#else -static void tcg_out_test_alignment(TCGContext *s, bool is_ld, - TCGReg addrlo, unsigned a_bits) + +/* We're expecting to use a 20-bit negative offset on the tlb memory ops. */ +#define MIN_TLB_MASK_TABLE_OFS -(1 << 19) + +/* + * For system-mode, perform the TLB load and compare. + * For user-mode, perform any required alignment tests. + * In both cases, return a TCGLabelQemuLdst structure if the slow path + * is required and fill in @h with the host address for the fast path. + */ +static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, + TCGReg addr_reg, MemOpIdx oi, + bool is_ld) { - unsigned a_mask = (1 << a_bits) - 1; - TCGLabelQemuLdst *l = new_ldst_label(s); + TCGType addr_type = s->addr_type; + TCGLabelQemuLdst *ldst = NULL; + MemOp opc = get_memop(oi); + MemOp s_bits = opc & MO_SIZE; + unsigned a_mask; - l->is_ld = is_ld; - l->addrlo_reg = addrlo; + h->aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, s_bits == MO_128); + a_mask = (1 << h->aa.align) - 1; - /* We are expecting a_bits to max out at 7, much lower than TMLL. */ - tcg_debug_assert(a_bits < 16); - tcg_out_insn(s, RI, TMLL, addrlo, a_mask); + if (tcg_use_softmmu) { + unsigned s_mask = (1 << s_bits) - 1; + int mem_index = get_mmuidx(oi); + int fast_off = tlb_mask_table_ofs(s, mem_index); + int mask_off = fast_off + offsetof(CPUTLBDescFast, mask); + int table_off = fast_off + offsetof(CPUTLBDescFast, table); + int ofs, a_off; + uint64_t tlb_mask; - tcg_out16(s, RI_BRC | (7 << 4)); /* CC in {1,2,3} */ - l->label_ptr[0] = s->code_ptr; - s->code_ptr += 1; + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addr_reg; - l->raddr = tcg_splitwx_to_rx(s->code_ptr); -} + tcg_out_sh64(s, RSY_SRLG, TCG_TMP0, addr_reg, TCG_REG_NONE, + s->page_bits - CPU_TLB_ENTRY_BITS); -static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) -{ - if (!patch_reloc(l->label_ptr[0], R_390_PC16DBL, - (intptr_t)tcg_splitwx_to_rx(s->code_ptr), 2)) { - return false; - } + tcg_out_insn(s, RXY, NG, TCG_TMP0, TCG_AREG0, TCG_REG_NONE, mask_off); + tcg_out_insn(s, RXY, AG, TCG_TMP0, TCG_AREG0, TCG_REG_NONE, table_off); - tcg_out_mov(s, TCG_TYPE_TL, TCG_REG_R3, l->addrlo_reg); - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R2, TCG_AREG0); + /* + * For aligned accesses, we check the first byte and include the + * alignment bits within the address. For unaligned access, we + * check that we don't cross pages using the address of the last + * byte of the access. + */ + a_off = (a_mask >= s_mask ? 0 : s_mask - a_mask); + tlb_mask = (uint64_t)s->page_mask | a_mask; + if (a_off == 0) { + tgen_andi_risbg(s, TCG_REG_R0, addr_reg, tlb_mask); + } else { + tcg_out_insn(s, RX, LA, TCG_REG_R0, addr_reg, TCG_REG_NONE, a_off); + tgen_andi(s, addr_type, TCG_REG_R0, tlb_mask); + } - /* "Tail call" to the helper, with the return address back inline. */ - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R14, (uintptr_t)l->raddr); - tgen_gotoi(s, S390_CC_ALWAYS, (const void *)(l->is_ld ? helper_unaligned_ld - : helper_unaligned_st)); - return true; -} + if (is_ld) { + ofs = offsetof(CPUTLBEntry, addr_read); + } else { + ofs = offsetof(CPUTLBEntry, addr_write); + } + if (addr_type == TCG_TYPE_I32) { + ofs += HOST_BIG_ENDIAN * 4; + tcg_out_insn(s, RX, C, TCG_REG_R0, TCG_TMP0, TCG_REG_NONE, ofs); + } else { + tcg_out_insn(s, RXY, CG, TCG_REG_R0, TCG_TMP0, TCG_REG_NONE, ofs); + } -static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - return tcg_out_fail_alignment(s, l); -} + tcg_out16(s, RI_BRC | (S390_CC_NE << 4)); + ldst->label_ptr[0] = s->code_ptr++; -static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) -{ - return tcg_out_fail_alignment(s, l); -} + h->index = TCG_TMP0; + tcg_out_insn(s, RXY, LG, h->index, TCG_TMP0, TCG_REG_NONE, + offsetof(CPUTLBEntry, addend)); -static void tcg_prepare_user_ldst(TCGContext *s, TCGReg *addr_reg, - TCGReg *index_reg, tcg_target_long *disp) -{ - if (TARGET_LONG_BITS == 32) { - tgen_ext32u(s, TCG_TMP0, *addr_reg); - *addr_reg = TCG_TMP0; - } - if (guest_base < 0x80000) { - *index_reg = TCG_REG_NONE; - *disp = guest_base; + if (addr_type == TCG_TYPE_I32) { + tcg_out_insn(s, RRE, ALGFR, h->index, addr_reg); + h->base = TCG_REG_NONE; + } else { + h->base = addr_reg; + } + h->disp = 0; } else { - *index_reg = TCG_GUEST_BASE_REG; - *disp = 0; + if (a_mask) { + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addr_reg; + + tcg_debug_assert(a_mask <= 0xffff); + tcg_out_insn(s, RI, TMLL, addr_reg, a_mask); + + tcg_out16(s, RI_BRC | (S390_TM_NE << 4)); + ldst->label_ptr[0] = s->code_ptr++; + } + + h->base = addr_reg; + if (addr_type == TCG_TYPE_I32) { + tcg_out_ext32u(s, TCG_TMP0, addr_reg); + h->base = TCG_TMP0; + } + if (guest_base < 0x80000) { + h->index = TCG_REG_NONE; + h->disp = guest_base; + } else { + h->index = TCG_GUEST_BASE_REG; + h->disp = 0; + } } + + return ldst; } -#endif /* CONFIG_SOFTMMU */ static void tcg_out_qemu_ld(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi) + MemOpIdx oi, TCGType data_type) { - MemOp opc = get_memop(oi); -#ifdef CONFIG_SOFTMMU - unsigned mem_index = get_mmuidx(oi); - tcg_insn_unit *label_ptr; - TCGReg base_reg; + TCGLabelQemuLdst *ldst; + HostAddress h; - base_reg = tcg_out_tlb_read(s, addr_reg, opc, mem_index, 1); + ldst = prepare_host_addr(s, &h, addr_reg, oi, true); + tcg_out_qemu_ld_direct(s, get_memop(oi), data_reg, h); - tcg_out16(s, RI_BRC | (S390_CC_NE << 4)); - label_ptr = s->code_ptr; - s->code_ptr += 1; - - tcg_out_qemu_ld_direct(s, opc, data_reg, base_reg, TCG_REG_R2, 0); - - add_qemu_ldst_label(s, 1, oi, data_reg, addr_reg, s->code_ptr, label_ptr); -#else - TCGReg index_reg; - tcg_target_long disp; - unsigned a_bits = get_alignment_bits(opc); - - if (a_bits) { - tcg_out_test_alignment(s, true, addr_reg, a_bits); + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = data_reg; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } - tcg_prepare_user_ldst(s, &addr_reg, &index_reg, &disp); - tcg_out_qemu_ld_direct(s, opc, data_reg, addr_reg, index_reg, disp); -#endif } static void tcg_out_qemu_st(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi) + MemOpIdx oi, TCGType data_type) { - MemOp opc = get_memop(oi); -#ifdef CONFIG_SOFTMMU - unsigned mem_index = get_mmuidx(oi); - tcg_insn_unit *label_ptr; - TCGReg base_reg; + TCGLabelQemuLdst *ldst; + HostAddress h; - base_reg = tcg_out_tlb_read(s, addr_reg, opc, mem_index, 0); + ldst = prepare_host_addr(s, &h, addr_reg, oi, false); + tcg_out_qemu_st_direct(s, get_memop(oi), data_reg, h); - tcg_out16(s, RI_BRC | (S390_CC_NE << 4)); - label_ptr = s->code_ptr; - s->code_ptr += 1; - - tcg_out_qemu_st_direct(s, opc, data_reg, base_reg, TCG_REG_R2, 0); - - add_qemu_ldst_label(s, 0, oi, data_reg, addr_reg, s->code_ptr, label_ptr); -#else - TCGReg index_reg; - tcg_target_long disp; - unsigned a_bits = get_alignment_bits(opc); - - if (a_bits) { - tcg_out_test_alignment(s, false, addr_reg, a_bits); + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = data_reg; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } - tcg_prepare_user_ldst(s, &addr_reg, &index_reg, &disp); - tcg_out_qemu_st_direct(s, opc, data_reg, addr_reg, index_reg, disp); -#endif +} + +static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, + TCGReg addr_reg, MemOpIdx oi, bool is_ld) +{ + TCGLabel *l1 = NULL, *l2 = NULL; + TCGLabelQemuLdst *ldst; + HostAddress h; + bool need_bswap; + bool use_pair; + S390Opcode insn; + + ldst = prepare_host_addr(s, &h, addr_reg, oi, is_ld); + + use_pair = h.aa.atom < MO_128; + need_bswap = get_memop(oi) & MO_BSWAP; + + if (!use_pair) { + /* + * Atomicity requires we use LPQ. If we've already checked for + * 16-byte alignment, that's all we need. If we arrive with + * lesser alignment, we have determined that less than 16-byte + * alignment can be satisfied with two 8-byte loads. + */ + if (h.aa.align < MO_128) { + use_pair = true; + l1 = gen_new_label(); + l2 = gen_new_label(); + + tcg_out_insn(s, RI, TMLL, addr_reg, 15); + tgen_branch(s, S390_TM_NE, l1); + } + + tcg_debug_assert(!need_bswap); + tcg_debug_assert(datalo & 1); + tcg_debug_assert(datahi == datalo - 1); + insn = is_ld ? RXY_LPQ : RXY_STPQ; + tcg_out_insn_RXY(s, insn, datahi, h.base, h.index, h.disp); + + if (use_pair) { + tgen_branch(s, S390_CC_ALWAYS, l2); + tcg_out_label(s, l1); + } + } + if (use_pair) { + TCGReg d1, d2; + + if (need_bswap) { + d1 = datalo, d2 = datahi; + insn = is_ld ? RXY_LRVG : RXY_STRVG; + } else { + d1 = datahi, d2 = datalo; + insn = is_ld ? RXY_LG : RXY_STG; + } + + if (h.base == d1 || h.index == d1) { + tcg_out_insn(s, RXY, LAY, TCG_TMP0, h.base, h.index, h.disp); + h.base = TCG_TMP0; + h.index = TCG_REG_NONE; + h.disp = 0; + } + tcg_out_insn_RXY(s, insn, d1, h.base, h.index, h.disp); + tcg_out_insn_RXY(s, insn, d2, h.base, h.index, h.disp + 8); + } + if (l2) { + tcg_out_label(s, l2); + } + + if (ldst) { + ldst->type = TCG_TYPE_I128; + ldst->datalo_reg = datalo; + ldst->datahi_reg = datahi; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } +} + +static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) +{ + /* Reuse the zeroing that exists for goto_ptr. */ + if (a0 == 0) { + tgen_gotoi(s, S390_CC_ALWAYS, tcg_code_gen_epilogue); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, a0); + tgen_gotoi(s, S390_CC_ALWAYS, tb_ret_addr); + } +} + +static void tcg_out_goto_tb(TCGContext *s, int which) +{ + /* + * Branch displacement must be aligned for atomic patching; + * see if we need to add extra nop before branch + */ + if (!QEMU_PTR_IS_ALIGNED(s->code_ptr + 1, 4)) { + tcg_out16(s, NOP); + } + tcg_out16(s, RIL_BRCL | (S390_CC_ALWAYS << 4)); + set_jmp_insn_offset(s, which); + s->code_ptr += 2; + set_jmp_reset_offset(s, which); +} + +void tb_target_set_jmp_target(const TranslationBlock *tb, int n, + uintptr_t jmp_rx, uintptr_t jmp_rw) +{ + if (!HAVE_FACILITY(GEN_INST_EXT)) { + return; + } + /* patch the branch destination */ + uintptr_t addr = tb->jmp_target_addr[n]; + intptr_t disp = addr - (jmp_rx - 2); + qatomic_set((int32_t *)jmp_rw, disp / 2); + /* no need to flush icache explicitly */ } # define OP_32_64(x) \ @@ -2082,56 +2106,8 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGArg a0, a1, a2; switch (opc) { - case INDEX_op_exit_tb: - /* Reuse the zeroing that exists for goto_ptr. */ - a0 = args[0]; - if (a0 == 0) { - tgen_gotoi(s, S390_CC_ALWAYS, tcg_code_gen_epilogue); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, a0); - tgen_gotoi(s, S390_CC_ALWAYS, tb_ret_addr); - } - break; - - case INDEX_op_goto_tb: - a0 = args[0]; - if (s->tb_jmp_insn_offset) { - /* - * branch displacement must be aligned for atomic patching; - * see if we need to add extra nop before branch - */ - if (!QEMU_PTR_IS_ALIGNED(s->code_ptr + 1, 4)) { - tcg_out16(s, NOP); - } - tcg_debug_assert(!USE_REG_TB); - tcg_out16(s, RIL_BRCL | (S390_CC_ALWAYS << 4)); - s->tb_jmp_insn_offset[a0] = tcg_current_code_size(s); - s->code_ptr += 2; - } else { - /* load address stored at s->tb_jmp_target_addr + a0 */ - tcg_out_ld_abs(s, TCG_TYPE_PTR, TCG_REG_TB, - tcg_splitwx_to_rx(s->tb_jmp_target_addr + a0)); - /* and go there */ - tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_TB); - } - set_jmp_reset_offset(s, a0); - - /* For the unlinked path of goto_tb, we need to reset - TCG_REG_TB to the beginning of this TB. */ - if (USE_REG_TB) { - int ofs = -tcg_current_code_size(s); - /* All TB are restricted to 64KiB by unwind info. */ - tcg_debug_assert(ofs == sextract64(ofs, 0, 20)); - tcg_out_insn(s, RXY, LAY, TCG_REG_TB, - TCG_REG_TB, TCG_REG_NONE, ofs); - } - break; - case INDEX_op_goto_ptr: a0 = args[0]; - if (USE_REG_TB) { - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_TB, a0); - } tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, a0); break; @@ -2183,10 +2159,8 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_insn(s, RI, AHI, a0, a2); break; } - if (HAVE_FACILITY(EXT_IMM)) { - tcg_out_insn(s, RIL, AFI, a0, a2); - break; - } + tcg_out_insn(s, RIL, AFI, a0, a2); + break; } tcg_out_mem(s, RX_LA, RXY_LAY, a0, a1, TCG_REG_NONE, a2); } else if (a0 == a1) { @@ -2203,7 +2177,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, } else if (a0 == a1) { tcg_out_insn(s, RR, SR, a0, a2); } else { - tcg_out_insn(s, RRF, SRK, a0, a1, a2); + tcg_out_insn(s, RRFa, SRK, a0, a1, a2); } break; @@ -2215,53 +2189,102 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, } else if (a0 == a1) { tcg_out_insn(s, RR, NR, a0, a2); } else { - tcg_out_insn(s, RRF, NRK, a0, a1, a2); + tcg_out_insn(s, RRFa, NRK, a0, a1, a2); } break; case INDEX_op_or_i32: a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; if (const_args[2]) { tcg_out_mov(s, TCG_TYPE_I32, a0, a1); - tgen_ori(s, TCG_TYPE_I32, a0, a2); + tgen_ori(s, a0, a2); } else if (a0 == a1) { tcg_out_insn(s, RR, OR, a0, a2); } else { - tcg_out_insn(s, RRF, ORK, a0, a1, a2); + tcg_out_insn(s, RRFa, ORK, a0, a1, a2); } break; case INDEX_op_xor_i32: a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; if (const_args[2]) { tcg_out_mov(s, TCG_TYPE_I32, a0, a1); - tgen_xori(s, TCG_TYPE_I32, a0, a2); + tcg_out_insn(s, RIL, XILF, a0, a2); } else if (a0 == a1) { tcg_out_insn(s, RR, XR, args[0], args[2]); } else { - tcg_out_insn(s, RRF, XRK, a0, a1, a2); + tcg_out_insn(s, RRFa, XRK, a0, a1, a2); } break; + case INDEX_op_andc_i32: + a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; + if (const_args[2]) { + tcg_out_mov(s, TCG_TYPE_I32, a0, a1); + tgen_andi(s, TCG_TYPE_I32, a0, (uint32_t)~a2); + } else { + tcg_out_insn(s, RRFa, NCRK, a0, a1, a2); + } + break; + case INDEX_op_orc_i32: + a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; + if (const_args[2]) { + tcg_out_mov(s, TCG_TYPE_I32, a0, a1); + tgen_ori(s, a0, (uint32_t)~a2); + } else { + tcg_out_insn(s, RRFa, OCRK, a0, a1, a2); + } + break; + case INDEX_op_eqv_i32: + a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; + if (const_args[2]) { + tcg_out_mov(s, TCG_TYPE_I32, a0, a1); + tcg_out_insn(s, RIL, XILF, a0, ~a2); + } else { + tcg_out_insn(s, RRFa, NXRK, a0, a1, a2); + } + break; + case INDEX_op_nand_i32: + tcg_out_insn(s, RRFa, NNRK, args[0], args[1], args[2]); + break; + case INDEX_op_nor_i32: + tcg_out_insn(s, RRFa, NORK, args[0], args[1], args[2]); + break; + case INDEX_op_neg_i32: tcg_out_insn(s, RR, LCR, args[0], args[1]); break; + case INDEX_op_not_i32: + tcg_out_insn(s, RRFa, NORK, args[0], args[1], args[1]); + break; case INDEX_op_mul_i32: + a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; if (const_args[2]) { - if ((int32_t)args[2] == (int16_t)args[2]) { - tcg_out_insn(s, RI, MHI, args[0], args[2]); + tcg_out_mov(s, TCG_TYPE_I32, a0, a1); + if (a2 == (int16_t)a2) { + tcg_out_insn(s, RI, MHI, a0, a2); } else { - tcg_out_insn(s, RIL, MSFI, args[0], args[2]); + tcg_out_insn(s, RIL, MSFI, a0, a2); } + } else if (a0 == a1) { + tcg_out_insn(s, RRE, MSR, a0, a2); } else { - tcg_out_insn(s, RRE, MSR, args[0], args[2]); + tcg_out_insn(s, RRFa, MSRKC, a0, a1, a2); } break; case INDEX_op_div2_i32: - tcg_out_insn(s, RR, DR, TCG_REG_R2, args[4]); + tcg_debug_assert(args[0] == args[2]); + tcg_debug_assert(args[1] == args[3]); + tcg_debug_assert((args[1] & 1) == 0); + tcg_debug_assert(args[0] == args[1] + 1); + tcg_out_insn(s, RR, DR, args[1], args[4]); break; case INDEX_op_divu2_i32: - tcg_out_insn(s, RRE, DLR, TCG_REG_R2, args[4]); + tcg_debug_assert(args[0] == args[2]); + tcg_debug_assert(args[1] == args[3]); + tcg_debug_assert((args[1] & 1) == 0); + tcg_debug_assert(args[0] == args[1] + 1); + tcg_out_insn(s, RRE, DLR, args[1], args[4]); break; case INDEX_op_shl_i32: @@ -2311,19 +2334,6 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, } break; - case INDEX_op_ext8s_i32: - tgen_ext8s(s, TCG_TYPE_I32, args[0], args[1]); - break; - case INDEX_op_ext16s_i32: - tgen_ext16s(s, TCG_TYPE_I32, args[0], args[1]); - break; - case INDEX_op_ext8u_i32: - tgen_ext8u(s, TCG_TYPE_I32, args[0], args[1]); - break; - case INDEX_op_ext16u_i32: - tgen_ext16u(s, TCG_TYPE_I32, args[0], args[1]); - break; - case INDEX_op_bswap16_i32: a0 = args[0], a1 = args[1], a2 = args[2]; tcg_out_insn(s, RRE, LRVR, a0, a1); @@ -2350,9 +2360,9 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, a0 = args[0], a1 = args[1], a2 = args[2]; tcg_out_insn(s, RRE, LRVR, a0, a1); if (a2 & TCG_BSWAP_OS) { - tgen_ext32s(s, a0, a0); + tcg_out_ext32s(s, a0, a0); } else if ((a2 & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - tgen_ext32u(s, a0, a0); + tcg_out_ext32u(s, a0, a0); } break; @@ -2383,21 +2393,40 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_setcond_i32: tgen_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], - args[2], const_args[2]); + args[2], const_args[2], false); + break; + case INDEX_op_negsetcond_i32: + tgen_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], + args[2], const_args[2], true); break; case INDEX_op_movcond_i32: tgen_movcond(s, TCG_TYPE_I32, args[5], args[0], args[1], - args[2], const_args[2], args[3], const_args[3]); + args[2], const_args[2], args[3], const_args[3], args[4]); break; - case INDEX_op_qemu_ld_i32: - /* ??? Technically we can use a non-extending instruction. */ - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args[0], args[1], args[2]); + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + tcg_out_qemu_ld(s, args[0], args[1], args[2], TCG_TYPE_I32); break; - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args[0], args[1], args[2]); + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: + tcg_out_qemu_ld(s, args[0], args[1], args[2], TCG_TYPE_I64); + break; + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + tcg_out_qemu_st(s, args[0], args[1], args[2], TCG_TYPE_I32); + break; + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: + tcg_out_qemu_st(s, args[0], args[1], args[2], TCG_TYPE_I64); + break; + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: + tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], true); + break; + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: + tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; case INDEX_op_ld16s_i64: @@ -2429,17 +2458,17 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_insn(s, RI, AGHI, a0, a2); break; } - if (HAVE_FACILITY(EXT_IMM)) { - if (a2 == (int32_t)a2) { - tcg_out_insn(s, RIL, AGFI, a0, a2); - break; - } else if (a2 == (uint32_t)a2) { - tcg_out_insn(s, RIL, ALGFI, a0, a2); - break; - } else if (-a2 == (uint32_t)-a2) { - tcg_out_insn(s, RIL, SLGFI, a0, -a2); - break; - } + if (a2 == (int32_t)a2) { + tcg_out_insn(s, RIL, AGFI, a0, a2); + break; + } + if (a2 == (uint32_t)a2) { + tcg_out_insn(s, RIL, ALGFI, a0, a2); + break; + } + if (-a2 == (uint32_t)-a2) { + tcg_out_insn(s, RIL, SLGFI, a0, -a2); + break; } } tcg_out_mem(s, RX_LA, RXY_LAY, a0, a1, TCG_REG_NONE, a2); @@ -2454,10 +2483,8 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, if (const_args[2]) { a2 = -a2; goto do_addi_64; - } else if (a0 == a1) { - tcg_out_insn(s, RRE, SGR, a0, a2); } else { - tcg_out_insn(s, RRF, SGRK, a0, a1, a2); + tcg_out_insn(s, RRFa, SGRK, a0, a1, a2); } break; @@ -2466,66 +2493,119 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, if (const_args[2]) { tcg_out_mov(s, TCG_TYPE_I64, a0, a1); tgen_andi(s, TCG_TYPE_I64, args[0], args[2]); - } else if (a0 == a1) { - tcg_out_insn(s, RRE, NGR, args[0], args[2]); } else { - tcg_out_insn(s, RRF, NGRK, a0, a1, a2); + tcg_out_insn(s, RRFa, NGRK, a0, a1, a2); } break; case INDEX_op_or_i64: a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { tcg_out_mov(s, TCG_TYPE_I64, a0, a1); - tgen_ori(s, TCG_TYPE_I64, a0, a2); - } else if (a0 == a1) { - tcg_out_insn(s, RRE, OGR, a0, a2); + tgen_ori(s, a0, a2); } else { - tcg_out_insn(s, RRF, OGRK, a0, a1, a2); + tcg_out_insn(s, RRFa, OGRK, a0, a1, a2); } break; case INDEX_op_xor_i64: a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { tcg_out_mov(s, TCG_TYPE_I64, a0, a1); - tgen_xori(s, TCG_TYPE_I64, a0, a2); - } else if (a0 == a1) { - tcg_out_insn(s, RRE, XGR, a0, a2); + tgen_xori(s, a0, a2); } else { - tcg_out_insn(s, RRF, XGRK, a0, a1, a2); + tcg_out_insn(s, RRFa, XGRK, a0, a1, a2); } break; + case INDEX_op_andc_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_mov(s, TCG_TYPE_I64, a0, a1); + tgen_andi(s, TCG_TYPE_I64, a0, ~a2); + } else { + tcg_out_insn(s, RRFa, NCGRK, a0, a1, a2); + } + break; + case INDEX_op_orc_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_mov(s, TCG_TYPE_I64, a0, a1); + tgen_ori(s, a0, ~a2); + } else { + tcg_out_insn(s, RRFa, OCGRK, a0, a1, a2); + } + break; + case INDEX_op_eqv_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_mov(s, TCG_TYPE_I64, a0, a1); + tgen_xori(s, a0, ~a2); + } else { + tcg_out_insn(s, RRFa, NXGRK, a0, a1, a2); + } + break; + case INDEX_op_nand_i64: + tcg_out_insn(s, RRFa, NNGRK, args[0], args[1], args[2]); + break; + case INDEX_op_nor_i64: + tcg_out_insn(s, RRFa, NOGRK, args[0], args[1], args[2]); + break; + case INDEX_op_neg_i64: tcg_out_insn(s, RRE, LCGR, args[0], args[1]); break; + case INDEX_op_not_i64: + tcg_out_insn(s, RRFa, NOGRK, args[0], args[1], args[1]); + break; case INDEX_op_bswap64_i64: tcg_out_insn(s, RRE, LRVGR, args[0], args[1]); break; case INDEX_op_mul_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { - if (args[2] == (int16_t)args[2]) { - tcg_out_insn(s, RI, MGHI, args[0], args[2]); + tcg_out_mov(s, TCG_TYPE_I64, a0, a1); + if (a2 == (int16_t)a2) { + tcg_out_insn(s, RI, MGHI, a0, a2); } else { - tcg_out_insn(s, RIL, MSGFI, args[0], args[2]); + tcg_out_insn(s, RIL, MSGFI, a0, a2); } + } else if (a0 == a1) { + tcg_out_insn(s, RRE, MSGR, a0, a2); } else { - tcg_out_insn(s, RRE, MSGR, args[0], args[2]); + tcg_out_insn(s, RRFa, MSGRKC, a0, a1, a2); } break; case INDEX_op_div2_i64: - /* ??? We get an unnecessary sign-extension of the dividend - into R3 with this definition, but as we do in fact always - produce both quotient and remainder using INDEX_op_div_i64 - instead requires jumping through even more hoops. */ - tcg_out_insn(s, RRE, DSGR, TCG_REG_R2, args[4]); + /* + * ??? We get an unnecessary sign-extension of the dividend + * into op0 with this definition, but as we do in fact always + * produce both quotient and remainder using INDEX_op_div_i64 + * instead requires jumping through even more hoops. + */ + tcg_debug_assert(args[0] == args[2]); + tcg_debug_assert(args[1] == args[3]); + tcg_debug_assert((args[1] & 1) == 0); + tcg_debug_assert(args[0] == args[1] + 1); + tcg_out_insn(s, RRE, DSGR, args[1], args[4]); break; case INDEX_op_divu2_i64: - tcg_out_insn(s, RRE, DLGR, TCG_REG_R2, args[4]); + tcg_debug_assert(args[0] == args[2]); + tcg_debug_assert(args[1] == args[3]); + tcg_debug_assert((args[1] & 1) == 0); + tcg_debug_assert(args[0] == args[1] + 1); + tcg_out_insn(s, RRE, DLGR, args[1], args[4]); break; case INDEX_op_mulu2_i64: - tcg_out_insn(s, RRE, MLGR, TCG_REG_R2, args[3]); + tcg_debug_assert(args[0] == args[2]); + tcg_debug_assert((args[1] & 1) == 0); + tcg_debug_assert(args[0] == args[1] + 1); + tcg_out_insn(s, RRE, MLGR, args[1], args[3]); + break; + case INDEX_op_muls2_i64: + tcg_debug_assert((args[1] & 1) == 0); + tcg_debug_assert(args[0] == args[1] + 1); + tcg_out_insn(s, RRFa, MGRK, args[1], args[2], args[3]); break; case INDEX_op_shl_i64: @@ -2564,27 +2644,6 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, } break; - case INDEX_op_ext8s_i64: - tgen_ext8s(s, TCG_TYPE_I64, args[0], args[1]); - break; - case INDEX_op_ext16s_i64: - tgen_ext16s(s, TCG_TYPE_I64, args[0], args[1]); - break; - case INDEX_op_ext_i32_i64: - case INDEX_op_ext32s_i64: - tgen_ext32s(s, args[0], args[1]); - break; - case INDEX_op_ext8u_i64: - tgen_ext8u(s, TCG_TYPE_I64, args[0], args[1]); - break; - case INDEX_op_ext16u_i64: - tgen_ext16u(s, TCG_TYPE_I64, args[0], args[1]); - break; - case INDEX_op_extu_i32_i64: - case INDEX_op_ext32u_i64: - tgen_ext32u(s, args[0], args[1]); - break; - case INDEX_op_add2_i64: if (const_args[4]) { if ((int64_t)args[4] >= 0) { @@ -2616,11 +2675,15 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_setcond_i64: tgen_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], - args[2], const_args[2]); + args[2], const_args[2], false); + break; + case INDEX_op_negsetcond_i64: + tgen_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], + args[2], const_args[2], true); break; case INDEX_op_movcond_i64: tgen_movcond(s, TCG_TYPE_I64, args[5], args[0], args[1], - args[2], const_args[2], args[3], const_args[3]); + args[2], const_args[2], args[3], const_args[3], args[4]); break; OP_32_64(deposit): @@ -2650,19 +2713,42 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tgen_clz(s, args[0], args[1], args[2], const_args[2]); break; + case INDEX_op_ctpop_i32: + tgen_ctpop(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_ctpop_i64: + tgen_ctpop(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_mb: /* The host memory model is quite strong, we simply need to serialize the instruction stream. */ if (args[0] & TCG_MO_ST_LD) { - tcg_out_insn(s, RR, BCR, HAVE_FACILITY(FAST_BCR_SER) ? 14 : 15, 0); + /* fast-bcr-serialization facility (45) is present */ + tcg_out_insn(s, RR, BCR, 14, 0); } break; case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ + case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ + case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ + case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ + case INDEX_op_ext8s_i64: + case INDEX_op_ext8u_i32: + case INDEX_op_ext8u_i64: + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + case INDEX_op_ext16u_i32: + case INDEX_op_ext16u_i64: + case INDEX_op_ext32s_i64: + case INDEX_op_ext32u_i64: + case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: default: - tcg_abort(); + g_assert_not_reached(); } } @@ -3135,46 +3221,62 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_rotl_i64: case INDEX_op_rotr_i32: case INDEX_op_rotr_i64: - case INDEX_op_clz_i64: case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: + case INDEX_op_negsetcond_i32: return C_O1_I2(r, r, ri); + case INDEX_op_setcond_i64: + case INDEX_op_negsetcond_i64: + return C_O1_I2(r, r, rC); + + case INDEX_op_clz_i64: + return C_O1_I2(r, r, rI); case INDEX_op_sub_i32: case INDEX_op_sub_i64: case INDEX_op_and_i32: - case INDEX_op_and_i64: case INDEX_op_or_i32: - case INDEX_op_or_i64: case INDEX_op_xor_i32: + return C_O1_I2(r, r, ri); + case INDEX_op_and_i64: + return C_O1_I2(r, r, rNKR); + case INDEX_op_or_i64: case INDEX_op_xor_i64: - return (HAVE_FACILITY(DISTINCT_OPS) - ? C_O1_I2(r, r, ri) - : C_O1_I2(r, 0, ri)); + return C_O1_I2(r, r, rK); + + case INDEX_op_andc_i32: + case INDEX_op_orc_i32: + case INDEX_op_eqv_i32: + return C_O1_I2(r, r, ri); + case INDEX_op_andc_i64: + return C_O1_I2(r, r, rKR); + case INDEX_op_orc_i64: + case INDEX_op_eqv_i64: + return C_O1_I2(r, r, rNK); + + case INDEX_op_nand_i32: + case INDEX_op_nand_i64: + case INDEX_op_nor_i32: + case INDEX_op_nor_i64: + return C_O1_I2(r, r, r); case INDEX_op_mul_i32: - /* If we have the general-instruction-extensions, then we have - MULTIPLY SINGLE IMMEDIATE with a signed 32-bit, otherwise we - have only MULTIPLY HALFWORD IMMEDIATE, with a signed 16-bit. */ - return (HAVE_FACILITY(GEN_INST_EXT) - ? C_O1_I2(r, 0, ri) - : C_O1_I2(r, 0, rI)); - + return (HAVE_FACILITY(MISC_INSN_EXT2) + ? C_O1_I2(r, r, ri) + : C_O1_I2(r, 0, ri)); case INDEX_op_mul_i64: - return (HAVE_FACILITY(GEN_INST_EXT) - ? C_O1_I2(r, 0, rJ) - : C_O1_I2(r, 0, rI)); + return (HAVE_FACILITY(MISC_INSN_EXT2) + ? C_O1_I2(r, r, rJ) + : C_O1_I2(r, 0, rJ)); case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: - return (HAVE_FACILITY(DISTINCT_OPS) - ? C_O1_I2(r, r, ri) - : C_O1_I2(r, 0, ri)); + return C_O1_I2(r, r, ri); case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: return C_O0_I2(r, ri); + case INDEX_op_brcond_i64: + return C_O0_I2(r, rC); case INDEX_op_bswap16_i32: case INDEX_op_bswap16_i64: @@ -3183,6 +3285,8 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_bswap64_i64: case INDEX_op_neg_i32: case INDEX_op_neg_i64: + case INDEX_op_not_i32: + case INDEX_op_not_i64: case INDEX_op_ext8s_i32: case INDEX_op_ext8s_i64: case INDEX_op_ext8u_i32: @@ -3197,45 +3301,54 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_extu_i32_i64: case INDEX_op_extract_i32: case INDEX_op_extract_i64: + case INDEX_op_ctpop_i32: + case INDEX_op_ctpop_i64: return C_O1_I1(r, r); - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: - return C_O1_I1(r, L); - case INDEX_op_qemu_st_i64: - case INDEX_op_qemu_st_i32: - return C_O0_I2(L, L); + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: + return C_O1_I1(r, r); + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + return C_O0_I2(r, r); + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: + return C_O2_I1(o, m, r); + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: + return C_O0_I3(o, m, r); case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: return C_O1_I2(r, rZ, r); case INDEX_op_movcond_i32: + return C_O1_I4(r, r, ri, rI, r); case INDEX_op_movcond_i64: - return (HAVE_FACILITY(LOAD_ON_COND2) - ? C_O1_I4(r, r, ri, rI, 0) - : C_O1_I4(r, r, ri, r, 0)); + return C_O1_I4(r, r, rC, rI, r); case INDEX_op_div2_i32: case INDEX_op_div2_i64: case INDEX_op_divu2_i32: case INDEX_op_divu2_i64: - return C_O2_I3(b, a, 0, 1, r); + return C_O2_I3(o, m, 0, 1, r); case INDEX_op_mulu2_i64: - return C_O2_I2(b, a, 0, r); + return C_O2_I2(o, m, 0, r); + case INDEX_op_muls2_i64: + return C_O2_I2(o, m, r, r); case INDEX_op_add2_i32: case INDEX_op_sub2_i32: - return (HAVE_FACILITY(EXT_IMM) - ? C_O2_I4(r, r, 0, 1, ri, r) - : C_O2_I4(r, r, 0, 1, r, r)); + return C_N1_O1_I4(r, r, 0, 1, ri, r); case INDEX_op_add2_i64: case INDEX_op_sub2_i64: - return (HAVE_FACILITY(EXT_IMM) - ? C_O2_I4(r, r, 0, 1, rA, r) - : C_O2_I4(r, r, 0, 1, r, r)); + return C_N1_O1_I4(r, r, 0, 1, rJU, r); case INDEX_op_st_vec: return C_O0_I2(v, r); @@ -3301,6 +3414,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) static void query_s390_facilities(void) { unsigned long hwcap = qemu_getauxval(AT_HWCAP); + const char *which; /* Is STORE FACILITY LIST EXTENDED available? Honestly, I believe this is present on all 64-bit systems, but let's check for it anyway. */ @@ -3322,6 +3436,38 @@ static void query_s390_facilities(void) if (!(hwcap & HWCAP_S390_VXRS)) { s390_facilities[2] = 0; } + + /* + * Minimum supported cpu revision is z196. + * Check for all required facilities. + * ZARCH_ACTIVE is done via preprocessor check for 64-bit. + */ + if (!HAVE_FACILITY(LONG_DISP)) { + which = "long-displacement"; + goto fail; + } + if (!HAVE_FACILITY(EXT_IMM)) { + which = "extended-immediate"; + goto fail; + } + if (!HAVE_FACILITY(GEN_INST_EXT)) { + which = "general-instructions-extension"; + goto fail; + } + /* + * Facility 45 is a big bin that contains: distinct-operands, + * fast-BCR-serialization, high-word, population-count, + * interlocked-access-1, and load/store-on-condition-1 + */ + if (!HAVE_FACILITY(45)) { + which = "45"; + goto fail; + } + return; + + fail: + error_report("%s: missing required facility %s", __func__, which); + exit(EXIT_FAILURE); } static void tcg_target_init(TCGContext *s) @@ -3378,9 +3524,6 @@ static void tcg_target_init(TCGContext *s) /* XXX many insns can't be used with R0, so we better avoid it for now */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); - if (USE_REG_TB) { - tcg_regset_set_reg(s->reserved_regs, TCG_REG_TB); - } } #define FRAME_SIZE ((int)(TCG_TARGET_CALL_STACK_OFFSET \ @@ -3399,18 +3542,12 @@ static void tcg_target_qemu_prologue(TCGContext *s) TCG_STATIC_CALL_ARGS_SIZE + TCG_TARGET_CALL_STACK_OFFSET, CPU_TEMP_BUF_NLONGS * sizeof(long)); -#ifndef CONFIG_SOFTMMU - if (guest_base >= 0x80000) { - tcg_out_movi_int(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, guest_base, true); + if (!tcg_use_softmmu && guest_base >= 0x80000) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, guest_base); tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); } -#endif tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); - if (USE_REG_TB) { - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_TB, - tcg_target_call_iarg_regs[1]); - } /* br %r3 (go to TB) */ tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, tcg_target_call_iarg_regs[1]); @@ -3433,6 +3570,11 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_R14); } +static void tcg_out_tb_start(TCGContext *s) +{ + /* nothing to do */ +} + static void tcg_out_nop_fill(tcg_insn_unit *p, int count) { memset(p, 0x07, count * sizeof(tcg_insn_unit)); diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index 23e2063667..ae448c3a3a 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -26,7 +26,6 @@ #define S390_TCG_TARGET_H #define TCG_TARGET_INSN_UNIT_SIZE 2 -#define TCG_TARGET_TLB_DISPLACEMENT_BITS 19 /* We have a +- 4GB range on the branches; leave some slop. */ #define MAX_CODE_GEN_BUFFER_SIZE (3 * GiB) @@ -52,17 +51,19 @@ typedef enum TCGReg { #define TCG_TARGET_NB_REGS 64 -/* A list of relevant facilities used by this translator. Some of these - are required for proper operation, and these are checked at startup. */ +/* Facilities required for proper operation; checked at startup. */ #define FACILITY_ZARCH_ACTIVE 2 #define FACILITY_LONG_DISP 18 #define FACILITY_EXT_IMM 21 #define FACILITY_GEN_INST_EXT 34 -#define FACILITY_LOAD_ON_COND 45 -#define FACILITY_FAST_BCR_SER FACILITY_LOAD_ON_COND -#define FACILITY_DISTINCT_OPS FACILITY_LOAD_ON_COND +#define FACILITY_45 45 + +/* Facilities that are checked at runtime. */ + #define FACILITY_LOAD_ON_COND2 53 +#define FACILITY_MISC_INSN_EXT2 58 +#define FACILITY_MISC_INSN_EXT3 61 #define FACILITY_VECTOR 129 #define FACILITY_VECTOR_ENH1 135 @@ -80,30 +81,27 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_ext16u_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 0 -#define TCG_TARGET_HAS_neg_i32 1 -#define TCG_TARGET_HAS_andc_i32 0 -#define TCG_TARGET_HAS_orc_i32 0 -#define TCG_TARGET_HAS_eqv_i32 0 -#define TCG_TARGET_HAS_nand_i32 0 -#define TCG_TARGET_HAS_nor_i32 0 +#define TCG_TARGET_HAS_not_i32 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_andc_i32 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_orc_i32 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_eqv_i32 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_nand_i32 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_nor_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_clz_i32 0 #define TCG_TARGET_HAS_ctz_i32 0 -#define TCG_TARGET_HAS_ctpop_i32 0 -#define TCG_TARGET_HAS_deposit_i32 HAVE_FACILITY(GEN_INST_EXT) -#define TCG_TARGET_HAS_extract_i32 HAVE_FACILITY(GEN_INST_EXT) +#define TCG_TARGET_HAS_ctpop_i32 1 +#define TCG_TARGET_HAS_deposit_i32 1 +#define TCG_TARGET_HAS_extract_i32 1 #define TCG_TARGET_HAS_sextract_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_movcond_i32 1 +#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_extrl_i64_i32 0 -#define TCG_TARGET_HAS_extrh_i64_i32 0 -#define TCG_TARGET_HAS_direct_jump HAVE_FACILITY(GEN_INST_EXT) +#define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_div2_i64 1 @@ -117,28 +115,31 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_not_i64 0 -#define TCG_TARGET_HAS_neg_i64 1 -#define TCG_TARGET_HAS_andc_i64 0 -#define TCG_TARGET_HAS_orc_i64 0 -#define TCG_TARGET_HAS_eqv_i64 0 -#define TCG_TARGET_HAS_nand_i64 0 -#define TCG_TARGET_HAS_nor_i64 0 -#define TCG_TARGET_HAS_clz_i64 HAVE_FACILITY(EXT_IMM) +#define TCG_TARGET_HAS_not_i64 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_andc_i64 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_orc_i64 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_eqv_i64 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_nand_i64 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_nor_i64 HAVE_FACILITY(MISC_INSN_EXT3) +#define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 0 -#define TCG_TARGET_HAS_ctpop_i64 0 -#define TCG_TARGET_HAS_deposit_i64 HAVE_FACILITY(GEN_INST_EXT) -#define TCG_TARGET_HAS_extract_i64 HAVE_FACILITY(GEN_INST_EXT) +#define TCG_TARGET_HAS_ctpop_i64 1 +#define TCG_TARGET_HAS_deposit_i64 1 +#define TCG_TARGET_HAS_extract_i64 1 #define TCG_TARGET_HAS_sextract_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_movcond_i64 1 +#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 1 -#define TCG_TARGET_HAS_muls2_i64 0 +#define TCG_TARGET_HAS_muls2_i64 HAVE_FACILITY(MISC_INSN_EXT2) #define TCG_TARGET_HAS_muluh_i64 0 #define TCG_TARGET_HAS_mulsh_i64 0 +#define TCG_TARGET_HAS_qemu_ldst_i128 1 + +#define TCG_TARGET_HAS_tst 1 + #define TCG_TARGET_HAS_v64 HAVE_FACILITY(VECTOR) #define TCG_TARGET_HAS_v128 HAVE_FACILITY(VECTOR) #define TCG_TARGET_HAS_v256 0 @@ -166,21 +167,12 @@ extern uint64_t s390_facilities[3]; /* used for function call generation */ #define TCG_TARGET_STACK_ALIGN 8 #define TCG_TARGET_CALL_STACK_OFFSET 160 - -#define TCG_TARGET_EXTEND_ARGS 1 -#define TCG_TARGET_HAS_MEMORY_BSWAP 1 +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_EXTEND +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_BY_REF +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_BY_REF #define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) - -static inline void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx, - uintptr_t jmp_rw, uintptr_t addr) -{ - /* patch the branch destination */ - intptr_t disp = addr - (jmp_rx - 2); - qatomic_set((int32_t *)jmp_rw, disp / 2); - /* no need to flush icache explicitly */ -} - #define TCG_TARGET_NEED_LDST_LABELS #define TCG_TARGET_NEED_POOL_LABELS diff --git a/tcg/sparc/tcg-target-con-set.h b/tcg/sparc64/tcg-target-con-set.h similarity index 69% rename from tcg/sparc/tcg-target-con-set.h rename to tcg/sparc64/tcg-target-con-set.h index 3b751dc3fb..434bf25072 100644 --- a/tcg/sparc/tcg-target-con-set.h +++ b/tcg/sparc64/tcg-target-con-set.h @@ -11,22 +11,10 @@ */ C_O0_I1(r) C_O0_I2(rZ, r) -C_O0_I2(RZ, r) C_O0_I2(rZ, rJ) -C_O0_I2(RZ, RJ) -C_O0_I2(sZ, A) -C_O0_I2(SZ, A) -C_O1_I1(r, A) -C_O1_I1(R, A) C_O1_I1(r, r) -C_O1_I1(r, R) -C_O1_I1(R, r) -C_O1_I1(R, R) -C_O1_I2(R, R, R) +C_O1_I2(r, r, r) C_O1_I2(r, rZ, rJ) -C_O1_I2(R, RZ, RJ) C_O1_I4(r, rZ, rJ, rI, 0) -C_O1_I4(R, RZ, RJ, RI, 0) C_O2_I2(r, r, rZ, rJ) -C_O2_I4(R, R, RZ, RZ, RJ, RI) C_O2_I4(r, r, rZ, rZ, rJ, rJ) diff --git a/tcg/sparc/tcg-target-con-str.h b/tcg/sparc64/tcg-target-con-str.h similarity index 73% rename from tcg/sparc/tcg-target-con-str.h rename to tcg/sparc64/tcg-target-con-str.h index fdb25d9313..0577ec4942 100644 --- a/tcg/sparc/tcg-target-con-str.h +++ b/tcg/sparc64/tcg-target-con-str.h @@ -9,10 +9,6 @@ * REGS(letter, register_mask) */ REGS('r', ALL_GENERAL_REGS) -REGS('R', ALL_GENERAL_REGS64) -REGS('s', ALL_QLDST_REGS) -REGS('S', ALL_QLDST_REGS64) -REGS('A', TARGET_LONG_BITS == 64 ? ALL_QLDST_REGS64 : ALL_QLDST_REGS) /* * Define constraint letters for constants: diff --git a/tcg/sparc64/tcg-target-reg-bits.h b/tcg/sparc64/tcg-target-reg-bits.h new file mode 100644 index 0000000000..34a6711013 --- /dev/null +++ b/tcg/sparc64/tcg-target-reg-bits.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2023 Linaro + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +#define TCG_TARGET_REG_BITS 64 + +#endif diff --git a/tcg/sparc/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc similarity index 55% rename from tcg/sparc/tcg-target.c.inc rename to tcg/sparc64/tcg-target.c.inc index 72d9552fd0..176c98740b 100644 --- a/tcg/sparc/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -22,6 +22,12 @@ * THE SOFTWARE. */ +/* We only support generating code for 64-bit mode. */ +#ifndef __arch64__ +#error "unsupported code generation mode" +#endif + +#include "../tcg-ldst.c.inc" #include "../tcg-pool.c.inc" #ifdef CONFIG_DEBUG_TCG @@ -61,54 +67,22 @@ static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { }; #endif -#ifdef __arch64__ -# define SPARC64 1 -#else -# define SPARC64 0 -#endif - #define TCG_CT_CONST_S11 0x100 #define TCG_CT_CONST_S13 0x200 #define TCG_CT_CONST_ZERO 0x400 -/* - * For softmmu, we need to avoid conflicts with the first 3 - * argument registers to perform the tlb lookup, and to call - * the helper function. - */ -#ifdef CONFIG_SOFTMMU -#define SOFTMMU_RESERVE_REGS MAKE_64BIT_MASK(TCG_REG_O0, 3) -#else -#define SOFTMMU_RESERVE_REGS 0 -#endif +#define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) -/* - * Note that sparcv8plus can only hold 64 bit quantities in %g and %o - * registers. These are saved manually by the kernel in full 64-bit - * slots. The %i and %l registers are saved by the register window - * mechanism, which only allocates space for 32 bits. Given that this - * window spill/fill can happen on any signal, we must consider the - * high bits of the %i and %l registers garbage at all times. - */ -#define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) -#if SPARC64 -# define ALL_GENERAL_REGS64 ALL_GENERAL_REGS -#else -# define ALL_GENERAL_REGS64 MAKE_64BIT_MASK(0, 16) -#endif -#define ALL_QLDST_REGS (ALL_GENERAL_REGS & ~SOFTMMU_RESERVE_REGS) -#define ALL_QLDST_REGS64 (ALL_GENERAL_REGS64 & ~SOFTMMU_RESERVE_REGS) - -/* Define some temporary registers. T2 is used for constant generation. */ +/* Define some temporary registers. T3 is used for constant generation. */ #define TCG_REG_T1 TCG_REG_G1 -#define TCG_REG_T2 TCG_REG_O7 +#define TCG_REG_T2 TCG_REG_G2 +#define TCG_REG_T3 TCG_REG_O7 #ifndef CONFIG_SOFTMMU # define TCG_GUEST_BASE_REG TCG_REG_I5 #endif #define TCG_REG_TB TCG_REG_I1 -#define USE_REG_TB (sizeof(void *) > 4) static const int tcg_target_reg_alloc_order[] = { TCG_REG_L0, @@ -127,7 +101,6 @@ static const int tcg_target_reg_alloc_order[] = { TCG_REG_I4, TCG_REG_I5, - TCG_REG_G2, TCG_REG_G3, TCG_REG_G4, TCG_REG_G5, @@ -149,12 +122,12 @@ static const int tcg_target_call_iarg_regs[6] = { TCG_REG_O5, }; -static const int tcg_target_call_oarg_regs[] = { - TCG_REG_O0, - TCG_REG_O1, - TCG_REG_O2, - TCG_REG_O3, -}; +static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) +{ + tcg_debug_assert(kind == TCG_CALL_RET_NORMAL); + tcg_debug_assert(slot >= 0 && slot <= 3); + return TCG_REG_O0 + slot; +} #define INSN_OP(x) ((x) << 30) #define INSN_OP2(x) ((x) << 22) @@ -306,11 +279,7 @@ static bool check_fit_i32(int32_t val, unsigned int bits) } #define check_fit_tl check_fit_i64 -#if SPARC64 -# define check_fit_ptr check_fit_i64 -#else -# define check_fit_ptr check_fit_i32 -#endif +#define check_fit_ptr check_fit_i64 static bool patch_reloc(tcg_insn_unit *src_rw, int type, intptr_t value, intptr_t addend) @@ -353,7 +322,8 @@ static bool patch_reloc(tcg_insn_unit *src_rw, int type, } /* test if a constant matches the constraint */ -static bool tcg_target_const_match(int64_t val, TCGType type, int ct) +static bool tcg_target_const_match(int64_t val, int ct, + TCGType type, TCGCond cond, int vece) { if (ct & TCG_CT_CONST) { return 1; @@ -420,22 +390,25 @@ static void tcg_out_sethi(TCGContext *s, TCGReg ret, uint32_t arg) tcg_out32(s, SETHI | INSN_RD(ret) | ((arg & 0xfffffc00) >> 10)); } -static void tcg_out_movi_imm13(TCGContext *s, TCGReg ret, int32_t arg) +/* A 13-bit constant sign-extended to 64 bits. */ +static void tcg_out_movi_s13(TCGContext *s, TCGReg ret, int32_t arg) { tcg_out_arithi(s, ret, TCG_REG_G0, arg, ARITH_OR); } -static void tcg_out_movi_imm32(TCGContext *s, TCGReg ret, int32_t arg) +/* A 32-bit constant sign-extended to 64 bits. */ +static void tcg_out_movi_s32(TCGContext *s, TCGReg ret, int32_t arg) { - if (check_fit_i32(arg, 13)) { - /* A 13-bit constant sign-extended to 64-bits. */ - tcg_out_movi_imm13(s, ret, arg); - } else { - /* A 32-bit constant zero-extended to 64 bits. */ - tcg_out_sethi(s, ret, arg); - if (arg & 0x3ff) { - tcg_out_arithi(s, ret, ret, arg & 0x3ff, ARITH_OR); - } + tcg_out_sethi(s, ret, ~arg); + tcg_out_arithi(s, ret, ret, (arg & 0x3ff) | -0x400, ARITH_XOR); +} + +/* A 32-bit constant zero-extended to 64 bits. */ +static void tcg_out_movi_u32(TCGContext *s, TCGReg ret, uint32_t arg) +{ + tcg_out_sethi(s, ret, arg); + if (arg & 0x3ff) { + tcg_out_arithi(s, ret, ret, arg & 0x3ff, ARITH_OR); } } @@ -446,20 +419,20 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, tcg_target_long hi, lo = (int32_t)arg; tcg_target_long test, lsb; - /* A 32-bit constant, or 32-bit zero-extended to 64-bits. */ - if (type == TCG_TYPE_I32 || arg == (uint32_t)arg) { - tcg_out_movi_imm32(s, ret, arg); + /* A 13-bit constant sign-extended to 64-bits. */ + if (check_fit_tl(arg, 13)) { + tcg_out_movi_s13(s, ret, arg); return; } - /* A 13-bit constant sign-extended to 64-bits. */ - if (check_fit_tl(arg, 13)) { - tcg_out_movi_imm13(s, ret, arg); + /* A 32-bit constant, or 32-bit zero-extended to 64-bits. */ + if (type == TCG_TYPE_I32 || arg == (uint32_t)arg) { + tcg_out_movi_u32(s, ret, arg); return; } /* A 13-bit constant relative to the TB. */ - if (!in_prologue && USE_REG_TB) { + if (!in_prologue) { test = tcg_tbrel_diff(s, (void *)arg); if (check_fit_ptr(test, 13)) { tcg_out_arithi(s, ret, TCG_REG_TB, test, ARITH_ADD); @@ -469,8 +442,7 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, /* A 32-bit constant sign-extended to 64-bits. */ if (arg == lo) { - tcg_out_sethi(s, ret, ~arg); - tcg_out_arithi(s, ret, ret, (arg & 0x3ff) | -0x400, ARITH_XOR); + tcg_out_movi_s32(s, ret, arg); return; } @@ -488,7 +460,7 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, } /* Use the constant pool, if possible. */ - if (!in_prologue && USE_REG_TB) { + if (!in_prologue) { new_pool_label(s, arg, R_SPARC_13, s->code_ptr, tcg_tbrel_diff(s, NULL)); tcg_out32(s, LDX | INSN_RD(ret) | INSN_RS1(TCG_REG_TB)); @@ -498,13 +470,13 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, /* A 64-bit constant decomposed into 2 32-bit pieces. */ if (check_fit_i32(lo, 13)) { hi = (arg - lo) >> 32; - tcg_out_movi_imm32(s, ret, hi); + tcg_out_movi_u32(s, ret, hi); tcg_out_arithi(s, ret, ret, 32, SHIFT_SLLX); tcg_out_arithi(s, ret, ret, lo, ARITH_ADD); } else { hi = arg >> 32; - tcg_out_movi_imm32(s, ret, hi); - tcg_out_movi_imm32(s, scratch, lo); + tcg_out_movi_u32(s, ret, hi); + tcg_out_movi_u32(s, scratch, lo); tcg_out_arithi(s, ret, ret, 32, SHIFT_SLLX); tcg_out_arith(s, ret, ret, scratch, ARITH_OR); } @@ -513,8 +485,66 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg ret, tcg_target_long arg) { - tcg_debug_assert(ret != TCG_REG_T2); - tcg_out_movi_int(s, type, ret, arg, false, TCG_REG_T2); + tcg_debug_assert(ret != TCG_REG_T3); + tcg_out_movi_int(s, type, ret, arg, false, TCG_REG_T3); +} + +static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) +{ + g_assert_not_reached(); +} + +static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) +{ + g_assert_not_reached(); +} + +static void tcg_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_arithi(s, rd, rs, 0xff, ARITH_AND); +} + +static void tcg_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_arithi(s, rd, rs, 16, SHIFT_SLL); + tcg_out_arithi(s, rd, rd, 16, SHIFT_SRL); +} + +static void tcg_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_arithi(s, rd, rs, 0, SHIFT_SRA); +} + +static void tcg_out_ext32u(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_arithi(s, rd, rs, 0, SHIFT_SRL); +} + +static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_ext32s(s, rd, rs); +} + +static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_ext32u(s, rd, rs); +} + +static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_ext32u(s, rd, rs); +} + +static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2) +{ + return false; +} + +static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs, + tcg_target_long imm) +{ + /* This function is only used for passing structs by reference. */ + g_assert_not_reached(); } static void tcg_out_ldst_rr(TCGContext *s, TCGReg data, TCGReg a1, @@ -557,27 +587,11 @@ static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, return false; } -static void tcg_out_ld_ptr(TCGContext *s, TCGReg ret, const void *arg) -{ - intptr_t diff = tcg_tbrel_diff(s, arg); - if (USE_REG_TB && check_fit_ptr(diff, 13)) { - tcg_out_ld(s, TCG_TYPE_PTR, ret, TCG_REG_TB, diff); - return; - } - tcg_out_movi(s, TCG_TYPE_PTR, ret, (uintptr_t)arg & ~0x3ff); - tcg_out_ld(s, TCG_TYPE_PTR, ret, ret, (uintptr_t)arg & 0x3ff); -} - static void tcg_out_sety(TCGContext *s, TCGReg rs) { tcg_out32(s, WRY | INSN_RS1(TCG_REG_G0) | INSN_RS2(rs)); } -static void tcg_out_rdy(TCGContext *s, TCGReg rd) -{ - tcg_out32(s, RDY | INSN_RD(rd)); -} - static void tcg_out_div32(TCGContext *s, TCGReg rd, TCGReg rs1, int32_t val2, int val2const, int uns) { @@ -593,9 +607,11 @@ static void tcg_out_div32(TCGContext *s, TCGReg rd, TCGReg rs1, uns ? ARITH_UDIV : ARITH_SDIV); } -static const uint8_t tcg_cond_to_bcond[] = { +static const uint8_t tcg_cond_to_bcond[16] = { [TCG_COND_EQ] = COND_E, [TCG_COND_NE] = COND_NE, + [TCG_COND_TSTEQ] = COND_E, + [TCG_COND_TSTNE] = COND_NE, [TCG_COND_LT] = COND_L, [TCG_COND_GE] = COND_GE, [TCG_COND_LE] = COND_LE, @@ -606,7 +622,7 @@ static const uint8_t tcg_cond_to_bcond[] = { [TCG_COND_GTU] = COND_GU, }; -static const uint8_t tcg_cond_to_rcond[] = { +static const uint8_t tcg_cond_to_rcond[16] = { [TCG_COND_EQ] = RCOND_Z, [TCG_COND_NE] = RCOND_NZ, [TCG_COND_LT] = RCOND_LZ, @@ -632,15 +648,17 @@ static void tcg_out_bpcc(TCGContext *s, int scond, int flags, TCGLabel *l) tcg_out_bpcc0(s, scond, flags, off19); } -static void tcg_out_cmp(TCGContext *s, TCGReg c1, int32_t c2, int c2const) +static void tcg_out_cmp(TCGContext *s, TCGCond cond, + TCGReg c1, int32_t c2, int c2const) { - tcg_out_arithc(s, TCG_REG_G0, c1, c2, c2const, ARITH_SUBCC); + tcg_out_arithc(s, TCG_REG_G0, c1, c2, c2const, + is_tst_cond(cond) ? ARITH_ANDCC : ARITH_SUBCC); } static void tcg_out_brcond_i32(TCGContext *s, TCGCond cond, TCGReg arg1, int32_t arg2, int const_arg2, TCGLabel *l) { - tcg_out_cmp(s, arg1, arg2, const_arg2); + tcg_out_cmp(s, cond, arg1, arg2, const_arg2); tcg_out_bpcc(s, tcg_cond_to_bcond[cond], BPCC_ICC | BPCC_PT, l); tcg_out_nop(s); } @@ -657,7 +675,7 @@ static void tcg_out_movcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg c1, int32_t c2, int c2const, int32_t v1, int v1const) { - tcg_out_cmp(s, c1, c2, c2const); + tcg_out_cmp(s, cond, c1, c2, c2const); tcg_out_movcc(s, cond, MOVCC_ICC, ret, v1, v1const); } @@ -665,7 +683,8 @@ static void tcg_out_brcond_i64(TCGContext *s, TCGCond cond, TCGReg arg1, int32_t arg2, int const_arg2, TCGLabel *l) { /* For 64-bit signed comparisons vs zero, we can avoid the compare. */ - if (arg2 == 0 && !is_unsigned_cond(cond)) { + int rcond = tcg_cond_to_rcond[cond]; + if (arg2 == 0 && rcond) { int off16 = 0; if (l->has_value) { @@ -674,19 +693,18 @@ static void tcg_out_brcond_i64(TCGContext *s, TCGCond cond, TCGReg arg1, tcg_out_reloc(s, s->code_ptr, R_SPARC_WDISP16, l, 0); } tcg_out32(s, INSN_OP(0) | INSN_OP2(3) | BPR_PT | INSN_RS1(arg1) - | INSN_COND(tcg_cond_to_rcond[cond]) | off16); + | INSN_COND(rcond) | off16); } else { - tcg_out_cmp(s, arg1, arg2, const_arg2); + tcg_out_cmp(s, cond, arg1, arg2, const_arg2); tcg_out_bpcc(s, tcg_cond_to_bcond[cond], BPCC_XCC | BPCC_PT, l); } tcg_out_nop(s); } -static void tcg_out_movr(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg c1, +static void tcg_out_movr(TCGContext *s, int rcond, TCGReg ret, TCGReg c1, int32_t v1, int v1const) { - tcg_out32(s, ARITH_MOVR | INSN_RD(ret) | INSN_RS1(c1) - | (tcg_cond_to_rcond[cond] << 10) + tcg_out32(s, ARITH_MOVR | INSN_RD(ret) | INSN_RS1(c1) | (rcond << 10) | (v1const ? INSN_IMM10(v1) : INSN_RS2(v1))); } @@ -697,17 +715,17 @@ static void tcg_out_movcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, /* For 64-bit signed comparisons vs zero, we can avoid the compare. Note that the immediate range is one bit smaller, so we must check for that as well. */ - if (c2 == 0 && !is_unsigned_cond(cond) - && (!v1const || check_fit_i32(v1, 10))) { - tcg_out_movr(s, cond, ret, c1, v1, v1const); + int rcond = tcg_cond_to_rcond[cond]; + if (c2 == 0 && rcond && (!v1const || check_fit_i32(v1, 10))) { + tcg_out_movr(s, rcond, ret, c1, v1, v1const); } else { - tcg_out_cmp(s, c1, c2, c2const); + tcg_out_cmp(s, cond, c1, c2, c2const); tcg_out_movcc(s, cond, MOVCC_XCC, ret, v1, v1const); } } static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg c1, int32_t c2, int c2const) + TCGReg c1, int32_t c2, int c2const, bool neg) { /* For 32-bit comparisons, we can play games with ADDC/SUBC. */ switch (cond) { @@ -729,6 +747,15 @@ static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, cond = (cond == TCG_COND_EQ ? TCG_COND_GEU : TCG_COND_LTU); break; + case TCG_COND_TSTEQ: + case TCG_COND_TSTNE: + /* Transform to inequality vs zero. */ + tcg_out_arithc(s, TCG_REG_T1, c1, c2, c2const, ARITH_AND); + c1 = TCG_REG_G0; + c2 = TCG_REG_T1, c2const = 0; + cond = (cond == TCG_COND_TSTEQ ? TCG_COND_GEU : TCG_COND_LTU); + break; + case TCG_COND_GTU: case TCG_COND_LEU: /* If we don't need to load a constant into a register, we can @@ -745,24 +772,38 @@ static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, /* FALLTHRU */ default: - tcg_out_cmp(s, c1, c2, c2const); - tcg_out_movi_imm13(s, ret, 0); - tcg_out_movcc(s, cond, MOVCC_ICC, ret, 1, 1); + tcg_out_cmp(s, cond, c1, c2, c2const); + tcg_out_movi_s13(s, ret, 0); + tcg_out_movcc(s, cond, MOVCC_ICC, ret, neg ? -1 : 1, 1); return; } - tcg_out_cmp(s, c1, c2, c2const); + tcg_out_cmp(s, cond, c1, c2, c2const); if (cond == TCG_COND_LTU) { - tcg_out_arithi(s, ret, TCG_REG_G0, 0, ARITH_ADDC); + if (neg) { + /* 0 - 0 - C = -C = (C ? -1 : 0) */ + tcg_out_arithi(s, ret, TCG_REG_G0, 0, ARITH_SUBC); + } else { + /* 0 + 0 + C = C = (C ? 1 : 0) */ + tcg_out_arithi(s, ret, TCG_REG_G0, 0, ARITH_ADDC); + } } else { - tcg_out_arithi(s, ret, TCG_REG_G0, -1, ARITH_SUBC); + if (neg) { + /* 0 + -1 + C = C - 1 = (C ? 0 : -1) */ + tcg_out_arithi(s, ret, TCG_REG_G0, -1, ARITH_ADDC); + } else { + /* 0 - -1 - C = 1 - C = (C ? 0 : 1) */ + tcg_out_arithi(s, ret, TCG_REG_G0, -1, ARITH_SUBC); + } } } static void tcg_out_setcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg c1, int32_t c2, int c2const) + TCGReg c1, int32_t c2, int c2const, bool neg) { - if (use_vis3_instructions) { + int rcond; + + if (use_vis3_instructions && !neg) { switch (cond) { case TCG_COND_NE: if (c2 != 0) { @@ -771,7 +812,7 @@ static void tcg_out_setcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, c2 = c1, c2const = 0, c1 = TCG_REG_G0; /* FALLTHRU */ case TCG_COND_LTU: - tcg_out_cmp(s, c1, c2, c2const); + tcg_out_cmp(s, cond, c1, c2, c2const); tcg_out_arith(s, ret, TCG_REG_G0, TCG_REG_G0, ARITH_ADDXC); return; default: @@ -781,13 +822,14 @@ static void tcg_out_setcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, /* For 64-bit signed comparisons vs zero, we can avoid the compare if the input does not overlap the output. */ - if (c2 == 0 && !is_unsigned_cond(cond) && c1 != ret) { - tcg_out_movi_imm13(s, ret, 0); - tcg_out_movr(s, cond, ret, c1, 1, 1); + rcond = tcg_cond_to_rcond[cond]; + if (c2 == 0 && rcond && c1 != ret) { + tcg_out_movi_s13(s, ret, 0); + tcg_out_movr(s, rcond, ret, c1, neg ? -1 : 1, 1); } else { - tcg_out_cmp(s, c1, c2, c2const); - tcg_out_movi_imm13(s, ret, 0); - tcg_out_movcc(s, cond, MOVCC_XCC, ret, 1, 1); + tcg_out_cmp(s, cond, c1, c2, c2const); + tcg_out_movi_s13(s, ret, 0); + tcg_out_movcc(s, cond, MOVCC_XCC, ret, neg ? -1 : 1, 1); } } @@ -823,7 +865,7 @@ static void tcg_out_addsub2_i64(TCGContext *s, TCGReg rl, TCGReg rh, if (use_vis3_instructions && !is_sub) { /* Note that ADDXC doesn't accept immediates. */ if (bhconst && bh != 0) { - tcg_out_movi_imm13(s, TCG_REG_T2, bh); + tcg_out_movi_s13(s, TCG_REG_T2, bh); bh = TCG_REG_T2; } tcg_out_arith(s, rh, ah, bh, ARITH_ADDXC); @@ -845,7 +887,7 @@ static void tcg_out_addsub2_i64(TCGContext *s, TCGReg rl, TCGReg rh, * so the adjustment fits 12 bits. */ if (bhconst) { - tcg_out_movi_imm13(s, TCG_REG_T2, bh + (is_sub ? -1 : 1)); + tcg_out_movi_s13(s, TCG_REG_T2, bh + (is_sub ? -1 : 1)); } else { tcg_out_arithi(s, TCG_REG_T2, bh, 1, is_sub ? ARITH_SUB : ARITH_ADD); @@ -864,10 +906,8 @@ static void tcg_out_jmpl_const(TCGContext *s, const tcg_insn_unit *dest, { uintptr_t desti = (uintptr_t)dest; - /* Be careful not to clobber %o7 for a tail call. */ tcg_out_movi_int(s, TCG_TYPE_PTR, TCG_REG_T1, - desti & ~0xfff, in_prologue, - tail_call ? TCG_REG_G2 : TCG_REG_O7); + desti & ~0xfff, in_prologue, TCG_REG_T2); tcg_out_arithi(s, tail_call ? TCG_REG_G0 : TCG_REG_O7, TCG_REG_T1, desti & 0xfff, JMPL); } @@ -884,7 +924,8 @@ static void tcg_out_call_nodelay(TCGContext *s, const tcg_insn_unit *dest, } } -static void tcg_out_call(TCGContext *s, const tcg_insn_unit *dest) +static void tcg_out_call(TCGContext *s, const tcg_insn_unit *dest, + const TCGHelperInfo *info) { tcg_out_call_nodelay(s, dest, false); tcg_out_nop(s); @@ -896,170 +937,6 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) tcg_out32(s, MEMBAR | (a0 & TCG_MO_ALL)); } -#ifdef CONFIG_SOFTMMU -static const tcg_insn_unit *qemu_ld_trampoline[(MO_SSIZE | MO_BSWAP) + 1]; -static const tcg_insn_unit *qemu_st_trampoline[(MO_SIZE | MO_BSWAP) + 1]; - -static void emit_extend(TCGContext *s, TCGReg r, int op) -{ - /* Emit zero extend of 8, 16 or 32 bit data as - * required by the MO_* value op; do nothing for 64 bit. - */ - switch (op & MO_SIZE) { - case MO_8: - tcg_out_arithi(s, r, r, 0xff, ARITH_AND); - break; - case MO_16: - tcg_out_arithi(s, r, r, 16, SHIFT_SLL); - tcg_out_arithi(s, r, r, 16, SHIFT_SRL); - break; - case MO_32: - if (SPARC64) { - tcg_out_arith(s, r, r, 0, SHIFT_SRL); - } - break; - case MO_64: - break; - } -} - -static void build_trampolines(TCGContext *s) -{ - static void * const qemu_ld_helpers[] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_SB] = helper_ret_ldsb_mmu, - [MO_LEUW] = helper_le_lduw_mmu, - [MO_LESW] = helper_le_ldsw_mmu, - [MO_LEUL] = helper_le_ldul_mmu, - [MO_LEUQ] = helper_le_ldq_mmu, - [MO_BEUW] = helper_be_lduw_mmu, - [MO_BESW] = helper_be_ldsw_mmu, - [MO_BEUL] = helper_be_ldul_mmu, - [MO_BEUQ] = helper_be_ldq_mmu, - }; - static void * const qemu_st_helpers[] = { - [MO_UB] = helper_ret_stb_mmu, - [MO_LEUW] = helper_le_stw_mmu, - [MO_LEUL] = helper_le_stl_mmu, - [MO_LEUQ] = helper_le_stq_mmu, - [MO_BEUW] = helper_be_stw_mmu, - [MO_BEUL] = helper_be_stl_mmu, - [MO_BEUQ] = helper_be_stq_mmu, - }; - - int i; - TCGReg ra; - - for (i = 0; i < ARRAY_SIZE(qemu_ld_helpers); ++i) { - if (qemu_ld_helpers[i] == NULL) { - continue; - } - - /* May as well align the trampoline. */ - while ((uintptr_t)s->code_ptr & 15) { - tcg_out_nop(s); - } - qemu_ld_trampoline[i] = tcg_splitwx_to_rx(s->code_ptr); - - if (SPARC64 || TARGET_LONG_BITS == 32) { - ra = TCG_REG_O3; - } else { - /* Install the high part of the address. */ - tcg_out_arithi(s, TCG_REG_O1, TCG_REG_O2, 32, SHIFT_SRLX); - ra = TCG_REG_O4; - } - - /* Set the retaddr operand. */ - tcg_out_mov(s, TCG_TYPE_PTR, ra, TCG_REG_O7); - /* Tail call. */ - tcg_out_jmpl_const(s, qemu_ld_helpers[i], true, true); - /* delay slot -- set the env argument */ - tcg_out_mov_delay(s, TCG_REG_O0, TCG_AREG0); - } - - for (i = 0; i < ARRAY_SIZE(qemu_st_helpers); ++i) { - if (qemu_st_helpers[i] == NULL) { - continue; - } - - /* May as well align the trampoline. */ - while ((uintptr_t)s->code_ptr & 15) { - tcg_out_nop(s); - } - qemu_st_trampoline[i] = tcg_splitwx_to_rx(s->code_ptr); - - if (SPARC64) { - emit_extend(s, TCG_REG_O2, i); - ra = TCG_REG_O4; - } else { - ra = TCG_REG_O1; - if (TARGET_LONG_BITS == 64) { - /* Install the high part of the address. */ - tcg_out_arithi(s, ra, ra + 1, 32, SHIFT_SRLX); - ra += 2; - } else { - ra += 1; - } - if ((i & MO_SIZE) == MO_64) { - /* Install the high part of the data. */ - tcg_out_arithi(s, ra, ra + 1, 32, SHIFT_SRLX); - ra += 2; - } else { - emit_extend(s, ra, i); - ra += 1; - } - /* Skip the oi argument. */ - ra += 1; - } - - /* Set the retaddr operand. */ - if (ra >= TCG_REG_O6) { - tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_O7, TCG_REG_CALL_STACK, - TCG_TARGET_CALL_STACK_OFFSET); - } else { - tcg_out_mov(s, TCG_TYPE_PTR, ra, TCG_REG_O7); - } - - /* Tail call. */ - tcg_out_jmpl_const(s, qemu_st_helpers[i], true, true); - /* delay slot -- set the env argument */ - tcg_out_mov_delay(s, TCG_REG_O0, TCG_AREG0); - } -} -#else -static const tcg_insn_unit *qemu_unalign_ld_trampoline; -static const tcg_insn_unit *qemu_unalign_st_trampoline; - -static void build_trampolines(TCGContext *s) -{ - for (int ld = 0; ld < 2; ++ld) { - void *helper; - - while ((uintptr_t)s->code_ptr & 15) { - tcg_out_nop(s); - } - - if (ld) { - helper = helper_unaligned_ld; - qemu_unalign_ld_trampoline = tcg_splitwx_to_rx(s->code_ptr); - } else { - helper = helper_unaligned_st; - qemu_unalign_st_trampoline = tcg_splitwx_to_rx(s->code_ptr); - } - - if (!SPARC64 && TARGET_LONG_BITS == 64) { - /* Install the high part of the address. */ - tcg_out_arithi(s, TCG_REG_O1, TCG_REG_O2, 32, SHIFT_SRLX); - } - - /* Tail call. */ - tcg_out_jmpl_const(s, helper, true, true); - /* delay slot -- set the env argument */ - tcg_out_mov_delay(s, TCG_REG_O0, TCG_AREG0); - } -} -#endif - /* Generate global QEMU prologue and epilogue code */ static void tcg_target_qemu_prologue(TCGContext *s) { @@ -1093,10 +970,8 @@ static void tcg_target_qemu_prologue(TCGContext *s) #endif /* We choose TCG_REG_TB such that no move is required. */ - if (USE_REG_TB) { - QEMU_BUILD_BUG_ON(TCG_REG_TB != TCG_REG_I1); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_TB); - } + QEMU_BUILD_BUG_ON(TCG_REG_TB != TCG_REG_I1); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TB); tcg_out_arithi(s, TCG_REG_G0, TCG_REG_I1, 0, JMPL); /* delay slot */ @@ -1106,9 +981,12 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_code_gen_epilogue = tcg_splitwx_to_rx(s->code_ptr); tcg_out_arithi(s, TCG_REG_G0, TCG_REG_I7, 8, RETURN); /* delay slot */ - tcg_out_movi_imm13(s, TCG_REG_O0, 0); + tcg_out_movi_s13(s, TCG_REG_O0, 0); +} - build_trampolines(s); +static void tcg_out_tb_start(TCGContext *s) +{ + /* nothing to do */ } static void tcg_out_nop_fill(tcg_insn_unit *p, int count) @@ -1119,413 +997,288 @@ static void tcg_out_nop_fill(tcg_insn_unit *p, int count) } } -#if defined(CONFIG_SOFTMMU) +static const TCGLdstHelperParam ldst_helper_param = { + .ntmp = 1, .tmp = { TCG_REG_T1 } +}; + +static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) +{ + MemOp opc = get_memop(lb->oi); + MemOp sgn; + + if (!patch_reloc(lb->label_ptr[0], R_SPARC_WDISP19, + (intptr_t)tcg_splitwx_to_rx(s->code_ptr), 0)) { + return false; + } + + /* Use inline tcg_out_ext32s; otherwise let the helper sign-extend. */ + sgn = (opc & MO_SIZE) < MO_32 ? MO_SIGN : 0; + + tcg_out_ld_helper_args(s, lb, &ldst_helper_param); + tcg_out_call(s, qemu_ld_helpers[opc & (MO_SIZE | sgn)], NULL); + tcg_out_ld_helper_ret(s, lb, sgn, &ldst_helper_param); + + tcg_out_bpcc0(s, COND_A, BPCC_A | BPCC_PT, 0); + return patch_reloc(s->code_ptr - 1, R_SPARC_WDISP19, + (intptr_t)lb->raddr, 0); +} + +static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) +{ + MemOp opc = get_memop(lb->oi); + + if (!patch_reloc(lb->label_ptr[0], R_SPARC_WDISP19, + (intptr_t)tcg_splitwx_to_rx(s->code_ptr), 0)) { + return false; + } + + tcg_out_st_helper_args(s, lb, &ldst_helper_param); + tcg_out_call(s, qemu_st_helpers[opc & MO_SIZE], NULL); + + tcg_out_bpcc0(s, COND_A, BPCC_A | BPCC_PT, 0); + return patch_reloc(s->code_ptr - 1, R_SPARC_WDISP19, + (intptr_t)lb->raddr, 0); +} + +typedef struct { + TCGReg base; + TCGReg index; + TCGAtomAlign aa; +} HostAddress; + +bool tcg_target_has_memory_bswap(MemOp memop) +{ + return true; +} /* We expect to use a 13-bit negative offset from ENV. */ -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); -QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -(1 << 12)); +#define MIN_TLB_MASK_TABLE_OFS -(1 << 12) -/* Perform the TLB load and compare. - - Inputs: - ADDRLO and ADDRHI contain the possible two parts of the address. - - MEM_INDEX and S_BITS are the memory context and log2 size of the load. - - WHICH is the offset into the CPUTLBEntry structure of the slot to read. - This should be offsetof addr_read or addr_write. - - The result of the TLB comparison is in %[ix]cc. The sanitized address - is in the returned register, maybe %o0. The TLB addend is in %o1. */ - -static TCGReg tcg_out_tlb_load(TCGContext *s, TCGReg addr, int mem_index, - MemOp opc, int which) +/* + * For system-mode, perform the TLB load and compare. + * For user-mode, perform any required alignment tests. + * In both cases, return a TCGLabelQemuLdst structure if the slow path + * is required and fill in @h with the host address for the fast path. + */ +static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, + TCGReg addr_reg, MemOpIdx oi, + bool is_ld) { - int fast_off = TLB_MASK_TABLE_OFS(mem_index); - int mask_off = fast_off + offsetof(CPUTLBDescFast, mask); - int table_off = fast_off + offsetof(CPUTLBDescFast, table); - const TCGReg r0 = TCG_REG_O0; - const TCGReg r1 = TCG_REG_O1; - const TCGReg r2 = TCG_REG_O2; - unsigned s_bits = opc & MO_SIZE; - unsigned a_bits = get_alignment_bits(opc); - tcg_target_long compare_mask; + TCGType addr_type = s->addr_type; + TCGLabelQemuLdst *ldst = NULL; + MemOp opc = get_memop(oi); + MemOp s_bits = opc & MO_SIZE; + unsigned a_mask; - /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ - tcg_out_ld(s, TCG_TYPE_PTR, r0, TCG_AREG0, mask_off); - tcg_out_ld(s, TCG_TYPE_PTR, r1, TCG_AREG0, table_off); - - /* Extract the page index, shifted into place for tlb index. */ - tcg_out_arithi(s, r2, addr, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS, - SHIFT_SRL); - tcg_out_arith(s, r2, r2, r0, ARITH_AND); - - /* Add the tlb_table pointer, creating the CPUTLBEntry address into R2. */ - tcg_out_arith(s, r2, r2, r1, ARITH_ADD); - - /* Load the tlb comparator and the addend. */ - tcg_out_ld(s, TCG_TYPE_TL, r0, r2, which); - tcg_out_ld(s, TCG_TYPE_PTR, r1, r2, offsetof(CPUTLBEntry, addend)); - - /* Mask out the page offset, except for the required alignment. - We don't support unaligned accesses. */ - if (a_bits < s_bits) { - a_bits = s_bits; - } - compare_mask = (tcg_target_ulong)TARGET_PAGE_MASK | ((1 << a_bits) - 1); - if (check_fit_tl(compare_mask, 13)) { - tcg_out_arithi(s, r2, addr, compare_mask, ARITH_AND); - } else { - tcg_out_movi(s, TCG_TYPE_TL, r2, compare_mask); - tcg_out_arith(s, r2, addr, r2, ARITH_AND); - } - tcg_out_cmp(s, r0, r2, 0); - - /* If the guest address must be zero-extended, do so now. */ - if (SPARC64 && TARGET_LONG_BITS == 32) { - tcg_out_arithi(s, r0, addr, 0, SHIFT_SRL); - return r0; - } - return addr; -} -#endif /* CONFIG_SOFTMMU */ - -static const int qemu_ld_opc[(MO_SSIZE | MO_BSWAP) + 1] = { - [MO_UB] = LDUB, - [MO_SB] = LDSB, - [MO_UB | MO_LE] = LDUB, - [MO_SB | MO_LE] = LDSB, - - [MO_BEUW] = LDUH, - [MO_BESW] = LDSH, - [MO_BEUL] = LDUW, - [MO_BESL] = LDSW, - [MO_BEUQ] = LDX, - [MO_BESQ] = LDX, - - [MO_LEUW] = LDUH_LE, - [MO_LESW] = LDSH_LE, - [MO_LEUL] = LDUW_LE, - [MO_LESL] = LDSW_LE, - [MO_LEUQ] = LDX_LE, - [MO_LESQ] = LDX_LE, -}; - -static const int qemu_st_opc[(MO_SIZE | MO_BSWAP) + 1] = { - [MO_UB] = STB, - - [MO_BEUW] = STH, - [MO_BEUL] = STW, - [MO_BEUQ] = STX, - - [MO_LEUW] = STH_LE, - [MO_LEUL] = STW_LE, - [MO_LEUQ] = STX_LE, -}; - -static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr, - MemOpIdx oi, bool is_64) -{ - MemOp memop = get_memop(oi); - tcg_insn_unit *label_ptr; + /* We don't support unaligned accesses. */ + h->aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, false); + h->aa.align = MAX(h->aa.align, s_bits); + a_mask = (1u << h->aa.align) - 1; #ifdef CONFIG_SOFTMMU - unsigned memi = get_mmuidx(oi); - TCGReg addrz, param; - const tcg_insn_unit *func; + int mem_index = get_mmuidx(oi); + int fast_off = tlb_mask_table_ofs(s, mem_index); + int mask_off = fast_off + offsetof(CPUTLBDescFast, mask); + int table_off = fast_off + offsetof(CPUTLBDescFast, table); + int cmp_off = is_ld ? offsetof(CPUTLBEntry, addr_read) + : offsetof(CPUTLBEntry, addr_write); + int add_off = offsetof(CPUTLBEntry, addend); + int compare_mask; + int cc; - addrz = tcg_out_tlb_load(s, addr, memi, memop, - offsetof(CPUTLBEntry, addr_read)); + /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_T2, TCG_AREG0, mask_off); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_T3, TCG_AREG0, table_off); - /* The fast path is exactly one insn. Thus we can perform the - entire TLB Hit in the (annulled) delay slot of the branch - over the TLB Miss case. */ + /* Extract the page index, shifted into place for tlb index. */ + tcg_out_arithi(s, TCG_REG_T1, addr_reg, + s->page_bits - CPU_TLB_ENTRY_BITS, SHIFT_SRL); + tcg_out_arith(s, TCG_REG_T1, TCG_REG_T1, TCG_REG_T2, ARITH_AND); - /* beq,a,pt %[xi]cc, label0 */ - label_ptr = s->code_ptr; - tcg_out_bpcc0(s, COND_E, BPCC_A | BPCC_PT - | (TARGET_LONG_BITS == 64 ? BPCC_XCC : BPCC_ICC), 0); - /* delay slot */ - tcg_out_ldst_rr(s, data, addrz, TCG_REG_O1, - qemu_ld_opc[memop & (MO_BSWAP | MO_SSIZE)]); + /* Add the tlb_table pointer, creating the CPUTLBEntry address into R2. */ + tcg_out_arith(s, TCG_REG_T1, TCG_REG_T1, TCG_REG_T3, ARITH_ADD); - /* TLB Miss. */ + /* + * Load the tlb comparator and the addend. + * Always load the entire 64-bit comparator for simplicity. + * We will ignore the high bits via BPCC_ICC below. + */ + tcg_out_ld(s, TCG_TYPE_I64, TCG_REG_T2, TCG_REG_T1, cmp_off); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_T1, TCG_REG_T1, add_off); + h->base = TCG_REG_T1; - param = TCG_REG_O1; - if (!SPARC64 && TARGET_LONG_BITS == 64) { - /* Skip the high-part; we'll perform the extract in the trampoline. */ - param++; - } - tcg_out_mov(s, TCG_TYPE_REG, param++, addrz); - - /* We use the helpers to extend SB and SW data, leaving the case - of SL needing explicit extending below. */ - if ((memop & MO_SSIZE) == MO_SL) { - func = qemu_ld_trampoline[memop & (MO_BSWAP | MO_SIZE)]; + /* Mask out the page offset, except for the required alignment. */ + compare_mask = s->page_mask | a_mask; + if (check_fit_tl(compare_mask, 13)) { + tcg_out_arithi(s, TCG_REG_T3, addr_reg, compare_mask, ARITH_AND); } else { - func = qemu_ld_trampoline[memop & (MO_BSWAP | MO_SSIZE)]; + tcg_out_movi_s32(s, TCG_REG_T3, compare_mask); + tcg_out_arith(s, TCG_REG_T3, addr_reg, TCG_REG_T3, ARITH_AND); } - tcg_debug_assert(func != NULL); - tcg_out_call_nodelay(s, func, false); - /* delay slot */ - tcg_out_movi(s, TCG_TYPE_I32, param, oi); + tcg_out_cmp(s, TCG_COND_NE, TCG_REG_T2, TCG_REG_T3, 0); - /* Recall that all of the helpers return 64-bit results. - Which complicates things for sparcv8plus. */ - if (SPARC64) { - /* We let the helper sign-extend SB and SW, but leave SL for here. */ - if (is_64 && (memop & MO_SSIZE) == MO_SL) { - tcg_out_arithi(s, data, TCG_REG_O0, 0, SHIFT_SRA); - } else { - tcg_out_mov(s, TCG_TYPE_REG, data, TCG_REG_O0); - } - } else { - if ((memop & MO_SIZE) == MO_64) { - tcg_out_arithi(s, TCG_REG_O0, TCG_REG_O0, 32, SHIFT_SLLX); - tcg_out_arithi(s, TCG_REG_O1, TCG_REG_O1, 0, SHIFT_SRL); - tcg_out_arith(s, data, TCG_REG_O0, TCG_REG_O1, ARITH_OR); - } else if (is_64) { - /* Re-extend from 32-bit rather than reassembling when we - know the high register must be an extension. */ - tcg_out_arithi(s, data, TCG_REG_O1, 0, - memop & MO_SIGN ? SHIFT_SRA : SHIFT_SRL); - } else { - tcg_out_mov(s, TCG_TYPE_I32, data, TCG_REG_O1); - } - } + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addr_reg; + ldst->label_ptr[0] = s->code_ptr; - *label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr)); + /* bne,pn %[xi]cc, label0 */ + cc = addr_type == TCG_TYPE_I32 ? BPCC_ICC : BPCC_XCC; + tcg_out_bpcc0(s, COND_NE, BPCC_PN | cc, 0); #else - TCGReg index = (guest_base ? TCG_GUEST_BASE_REG : TCG_REG_G0); - unsigned a_bits = get_alignment_bits(memop); - unsigned s_bits = memop & MO_SIZE; - unsigned t_bits; - - if (SPARC64 && TARGET_LONG_BITS == 32) { - tcg_out_arithi(s, TCG_REG_T1, addr, 0, SHIFT_SRL); - addr = TCG_REG_T1; - } - /* - * Normal case: alignment equal to access size. + * If the size equals the required alignment, we can skip the test + * and allow host SIGBUS to deliver SIGBUS to the guest. + * Otherwise, test for at least natural alignment and defer + * everything else to the helper functions. */ - if (a_bits == s_bits) { - tcg_out_ldst_rr(s, data, addr, index, - qemu_ld_opc[memop & (MO_BSWAP | MO_SSIZE)]); - return; + if (s_bits != get_alignment_bits(opc)) { + tcg_debug_assert(check_fit_tl(a_mask, 13)); + tcg_out_arithi(s, TCG_REG_G0, addr_reg, a_mask, ARITH_ANDCC); + + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addrlo_reg = addr_reg; + ldst->label_ptr[0] = s->code_ptr; + + /* bne,pn %icc, label0 */ + tcg_out_bpcc0(s, COND_NE, BPCC_PN | BPCC_ICC, 0); } + h->base = guest_base ? TCG_GUEST_BASE_REG : TCG_REG_G0; +#endif - /* - * Test for at least natural alignment, and assume most accesses - * will be aligned -- perform a straight load in the delay slot. - * This is required to preserve atomicity for aligned accesses. - */ - t_bits = MAX(a_bits, s_bits); - tcg_debug_assert(t_bits < 13); - tcg_out_arithi(s, TCG_REG_G0, addr, (1u << t_bits) - 1, ARITH_ANDCC); - - /* beq,a,pt %icc, label */ - label_ptr = s->code_ptr; - tcg_out_bpcc0(s, COND_E, BPCC_A | BPCC_PT | BPCC_ICC, 0); - /* delay slot */ - tcg_out_ldst_rr(s, data, addr, index, - qemu_ld_opc[memop & (MO_BSWAP | MO_SSIZE)]); - - if (a_bits >= s_bits) { - /* - * Overalignment: A successful alignment test will perform the memory - * operation in the delay slot, and failure need only invoke the - * handler for SIGBUS. - */ - TCGReg arg_low = TCG_REG_O1 + (!SPARC64 && TARGET_LONG_BITS == 64); - tcg_out_call_nodelay(s, qemu_unalign_ld_trampoline, false); - /* delay slot -- move to low part of argument reg */ - tcg_out_mov_delay(s, arg_low, addr); + /* If the guest address must be zero-extended, do in the delay slot. */ + if (addr_type == TCG_TYPE_I32) { + tcg_out_ext32u(s, TCG_REG_T2, addr_reg); + h->index = TCG_REG_T2; } else { - /* Underalignment: load by pieces of minimum alignment. */ - int ld_opc, a_size, s_size, i; - - /* - * Force full address into T1 early; avoids problems with - * overlap between @addr and @data. - */ - tcg_out_arith(s, TCG_REG_T1, addr, index, ARITH_ADD); - - a_size = 1 << a_bits; - s_size = 1 << s_bits; - if ((memop & MO_BSWAP) == MO_BE) { - ld_opc = qemu_ld_opc[a_bits | MO_BE | (memop & MO_SIGN)]; - tcg_out_ldst(s, data, TCG_REG_T1, 0, ld_opc); - ld_opc = qemu_ld_opc[a_bits | MO_BE]; - for (i = a_size; i < s_size; i += a_size) { - tcg_out_ldst(s, TCG_REG_T2, TCG_REG_T1, i, ld_opc); - tcg_out_arithi(s, data, data, a_size, SHIFT_SLLX); - tcg_out_arith(s, data, data, TCG_REG_T2, ARITH_OR); - } - } else if (a_bits == 0) { - ld_opc = LDUB; - tcg_out_ldst(s, data, TCG_REG_T1, 0, ld_opc); - for (i = a_size; i < s_size; i += a_size) { - if ((memop & MO_SIGN) && i == s_size - a_size) { - ld_opc = LDSB; - } - tcg_out_ldst(s, TCG_REG_T2, TCG_REG_T1, i, ld_opc); - tcg_out_arithi(s, TCG_REG_T2, TCG_REG_T2, i * 8, SHIFT_SLLX); - tcg_out_arith(s, data, data, TCG_REG_T2, ARITH_OR); - } - } else { - ld_opc = qemu_ld_opc[a_bits | MO_LE]; - tcg_out_ldst_rr(s, data, TCG_REG_T1, TCG_REG_G0, ld_opc); - for (i = a_size; i < s_size; i += a_size) { - tcg_out_arithi(s, TCG_REG_T1, TCG_REG_T1, a_size, ARITH_ADD); - if ((memop & MO_SIGN) && i == s_size - a_size) { - ld_opc = qemu_ld_opc[a_bits | MO_LE | MO_SIGN]; - } - tcg_out_ldst_rr(s, TCG_REG_T2, TCG_REG_T1, TCG_REG_G0, ld_opc); - tcg_out_arithi(s, TCG_REG_T2, TCG_REG_T2, i * 8, SHIFT_SLLX); - tcg_out_arith(s, data, data, TCG_REG_T2, ARITH_OR); - } + if (ldst) { + tcg_out_nop(s); } + h->index = addr_reg; } + return ldst; +} - *label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr)); -#endif /* CONFIG_SOFTMMU */ +static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr, + MemOpIdx oi, TCGType data_type) +{ + static const int ld_opc[(MO_SSIZE | MO_BSWAP) + 1] = { + [MO_UB] = LDUB, + [MO_SB] = LDSB, + [MO_UB | MO_LE] = LDUB, + [MO_SB | MO_LE] = LDSB, + + [MO_BEUW] = LDUH, + [MO_BESW] = LDSH, + [MO_BEUL] = LDUW, + [MO_BESL] = LDSW, + [MO_BEUQ] = LDX, + [MO_BESQ] = LDX, + + [MO_LEUW] = LDUH_LE, + [MO_LESW] = LDSH_LE, + [MO_LEUL] = LDUW_LE, + [MO_LESL] = LDSW_LE, + [MO_LEUQ] = LDX_LE, + [MO_LESQ] = LDX_LE, + }; + + TCGLabelQemuLdst *ldst; + HostAddress h; + + ldst = prepare_host_addr(s, &h, addr, oi, true); + + tcg_out_ldst_rr(s, data, h.base, h.index, + ld_opc[get_memop(oi) & (MO_BSWAP | MO_SSIZE)]); + + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = data; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } } static void tcg_out_qemu_st(TCGContext *s, TCGReg data, TCGReg addr, - MemOpIdx oi) + MemOpIdx oi, TCGType data_type) { - MemOp memop = get_memop(oi); - tcg_insn_unit *label_ptr; + static const int st_opc[(MO_SIZE | MO_BSWAP) + 1] = { + [MO_UB] = STB, -#ifdef CONFIG_SOFTMMU - unsigned memi = get_mmuidx(oi); - TCGReg addrz, param; - const tcg_insn_unit *func; + [MO_BEUW] = STH, + [MO_BEUL] = STW, + [MO_BEUQ] = STX, - addrz = tcg_out_tlb_load(s, addr, memi, memop, - offsetof(CPUTLBEntry, addr_write)); + [MO_LEUW] = STH_LE, + [MO_LEUL] = STW_LE, + [MO_LEUQ] = STX_LE, + }; - /* The fast path is exactly one insn. Thus we can perform the entire - TLB Hit in the (annulled) delay slot of the branch over TLB Miss. */ - /* beq,a,pt %[xi]cc, label0 */ - label_ptr = s->code_ptr; - tcg_out_bpcc0(s, COND_E, BPCC_A | BPCC_PT - | (TARGET_LONG_BITS == 64 ? BPCC_XCC : BPCC_ICC), 0); - /* delay slot */ - tcg_out_ldst_rr(s, data, addrz, TCG_REG_O1, - qemu_st_opc[memop & (MO_BSWAP | MO_SIZE)]); + TCGLabelQemuLdst *ldst; + HostAddress h; - /* TLB Miss. */ + ldst = prepare_host_addr(s, &h, addr, oi, false); - param = TCG_REG_O1; - if (!SPARC64 && TARGET_LONG_BITS == 64) { - /* Skip the high-part; we'll perform the extract in the trampoline. */ - param++; + tcg_out_ldst_rr(s, data, h.base, h.index, + st_opc[get_memop(oi) & (MO_BSWAP | MO_SIZE)]); + + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = data; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } - tcg_out_mov(s, TCG_TYPE_REG, param++, addrz); - if (!SPARC64 && (memop & MO_SIZE) == MO_64) { - /* Skip the high-part; we'll perform the extract in the trampoline. */ - param++; - } - tcg_out_mov(s, TCG_TYPE_REG, param++, data); +} - func = qemu_st_trampoline[memop & (MO_BSWAP | MO_SIZE)]; - tcg_debug_assert(func != NULL); - tcg_out_call_nodelay(s, func, false); - /* delay slot */ - tcg_out_movi(s, TCG_TYPE_I32, param, oi); - - *label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr)); -#else - TCGReg index = (guest_base ? TCG_GUEST_BASE_REG : TCG_REG_G0); - unsigned a_bits = get_alignment_bits(memop); - unsigned s_bits = memop & MO_SIZE; - unsigned t_bits; - - if (SPARC64 && TARGET_LONG_BITS == 32) { - tcg_out_arithi(s, TCG_REG_T1, addr, 0, SHIFT_SRL); - addr = TCG_REG_T1; - } - - /* - * Normal case: alignment equal to access size. - */ - if (a_bits == s_bits) { - tcg_out_ldst_rr(s, data, addr, index, - qemu_st_opc[memop & (MO_BSWAP | MO_SIZE)]); +static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) +{ + if (check_fit_ptr(a0, 13)) { + tcg_out_arithi(s, TCG_REG_G0, TCG_REG_I7, 8, RETURN); + tcg_out_movi_s13(s, TCG_REG_O0, a0); return; - } - - /* - * Test for at least natural alignment, and assume most accesses - * will be aligned -- perform a straight store in the delay slot. - * This is required to preserve atomicity for aligned accesses. - */ - t_bits = MAX(a_bits, s_bits); - tcg_debug_assert(t_bits < 13); - tcg_out_arithi(s, TCG_REG_G0, addr, (1u << t_bits) - 1, ARITH_ANDCC); - - /* beq,a,pt %icc, label */ - label_ptr = s->code_ptr; - tcg_out_bpcc0(s, COND_E, BPCC_A | BPCC_PT | BPCC_ICC, 0); - /* delay slot */ - tcg_out_ldst_rr(s, data, addr, index, - qemu_st_opc[memop & (MO_BSWAP | MO_SIZE)]); - - if (a_bits >= s_bits) { - /* - * Overalignment: A successful alignment test will perform the memory - * operation in the delay slot, and failure need only invoke the - * handler for SIGBUS. - */ - TCGReg arg_low = TCG_REG_O1 + (!SPARC64 && TARGET_LONG_BITS == 64); - tcg_out_call_nodelay(s, qemu_unalign_st_trampoline, false); - /* delay slot -- move to low part of argument reg */ - tcg_out_mov_delay(s, arg_low, addr); } else { - /* Underalignment: store by pieces of minimum alignment. */ - int st_opc, a_size, s_size, i; - - /* - * Force full address into T1 early; avoids problems with - * overlap between @addr and @data. - */ - tcg_out_arith(s, TCG_REG_T1, addr, index, ARITH_ADD); - - a_size = 1 << a_bits; - s_size = 1 << s_bits; - if ((memop & MO_BSWAP) == MO_BE) { - st_opc = qemu_st_opc[a_bits | MO_BE]; - for (i = 0; i < s_size; i += a_size) { - TCGReg d = data; - int shift = (s_size - a_size - i) * 8; - if (shift) { - d = TCG_REG_T2; - tcg_out_arithi(s, d, data, shift, SHIFT_SRLX); - } - tcg_out_ldst(s, d, TCG_REG_T1, i, st_opc); - } - } else if (a_bits == 0) { - tcg_out_ldst(s, data, TCG_REG_T1, 0, STB); - for (i = 1; i < s_size; i++) { - tcg_out_arithi(s, TCG_REG_T2, data, i * 8, SHIFT_SRLX); - tcg_out_ldst(s, TCG_REG_T2, TCG_REG_T1, i, STB); - } - } else { - /* Note that ST*A with immediate asi must use indexed address. */ - st_opc = qemu_st_opc[a_bits + MO_LE]; - tcg_out_ldst_rr(s, data, TCG_REG_T1, TCG_REG_G0, st_opc); - for (i = a_size; i < s_size; i += a_size) { - tcg_out_arithi(s, TCG_REG_T2, data, i * 8, SHIFT_SRLX); - tcg_out_arithi(s, TCG_REG_T1, TCG_REG_T1, a_size, ARITH_ADD); - tcg_out_ldst_rr(s, TCG_REG_T2, TCG_REG_T1, TCG_REG_G0, st_opc); - } + intptr_t tb_diff = tcg_tbrel_diff(s, (void *)a0); + if (check_fit_ptr(tb_diff, 13)) { + tcg_out_arithi(s, TCG_REG_G0, TCG_REG_I7, 8, RETURN); + /* Note that TCG_REG_TB has been unwound to O1. */ + tcg_out_arithi(s, TCG_REG_O0, TCG_REG_O1, tb_diff, ARITH_ADD); + return; } } + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I0, a0 & ~0x3ff); + tcg_out_arithi(s, TCG_REG_G0, TCG_REG_I7, 8, RETURN); + tcg_out_arithi(s, TCG_REG_O0, TCG_REG_O0, a0 & 0x3ff, ARITH_OR); +} - *label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr)); -#endif /* CONFIG_SOFTMMU */ +static void tcg_out_goto_tb(TCGContext *s, int which) +{ + ptrdiff_t off = tcg_tbrel_diff(s, (void *)get_jmp_target_addr(s, which)); + + /* Load link and indirect branch. */ + set_jmp_insn_offset(s, which); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TB, TCG_REG_TB, off); + tcg_out_arithi(s, TCG_REG_G0, TCG_REG_TB, 0, JMPL); + /* delay slot */ + tcg_out_nop(s); + set_jmp_reset_offset(s, which); + + /* + * For the unlinked path of goto_tb, we need to reset TCG_REG_TB + * to the beginning of this TB. + */ + off = -tcg_current_code_size(s); + if (check_fit_i32(off, 13)) { + tcg_out_arithi(s, TCG_REG_TB, TCG_REG_TB, off, ARITH_ADD); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T1, off); + tcg_out_arith(s, TCG_REG_TB, TCG_REG_TB, TCG_REG_T1, ARITH_ADD); + } +} + +void tb_target_set_jmp_target(const TranslationBlock *tb, int n, + uintptr_t jmp_rx, uintptr_t jmp_rw) +{ } static void tcg_out_op(TCGContext *s, TCGOpcode opc, @@ -1542,70 +1295,9 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, c2 = const_args[2]; switch (opc) { - case INDEX_op_exit_tb: - if (check_fit_ptr(a0, 13)) { - tcg_out_arithi(s, TCG_REG_G0, TCG_REG_I7, 8, RETURN); - tcg_out_movi_imm13(s, TCG_REG_O0, a0); - break; - } else if (USE_REG_TB) { - intptr_t tb_diff = tcg_tbrel_diff(s, (void *)a0); - if (check_fit_ptr(tb_diff, 13)) { - tcg_out_arithi(s, TCG_REG_G0, TCG_REG_I7, 8, RETURN); - /* Note that TCG_REG_TB has been unwound to O1. */ - tcg_out_arithi(s, TCG_REG_O0, TCG_REG_O1, tb_diff, ARITH_ADD); - break; - } - } - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I0, a0 & ~0x3ff); - tcg_out_arithi(s, TCG_REG_G0, TCG_REG_I7, 8, RETURN); - tcg_out_arithi(s, TCG_REG_O0, TCG_REG_O0, a0 & 0x3ff, ARITH_OR); - break; - case INDEX_op_goto_tb: - if (s->tb_jmp_insn_offset) { - /* direct jump method */ - if (USE_REG_TB) { - /* make sure the patch is 8-byte aligned. */ - if ((intptr_t)s->code_ptr & 4) { - tcg_out_nop(s); - } - s->tb_jmp_insn_offset[a0] = tcg_current_code_size(s); - tcg_out_sethi(s, TCG_REG_T1, 0); - tcg_out_arithi(s, TCG_REG_T1, TCG_REG_T1, 0, ARITH_OR); - tcg_out_arith(s, TCG_REG_G0, TCG_REG_TB, TCG_REG_T1, JMPL); - tcg_out_arith(s, TCG_REG_TB, TCG_REG_TB, TCG_REG_T1, ARITH_ADD); - } else { - s->tb_jmp_insn_offset[a0] = tcg_current_code_size(s); - tcg_out32(s, CALL); - tcg_out_nop(s); - } - } else { - /* indirect jump method */ - tcg_out_ld_ptr(s, TCG_REG_TB, s->tb_jmp_target_addr + a0); - tcg_out_arithi(s, TCG_REG_G0, TCG_REG_TB, 0, JMPL); - tcg_out_nop(s); - } - set_jmp_reset_offset(s, a0); - - /* For the unlinked path of goto_tb, we need to reset - TCG_REG_TB to the beginning of this TB. */ - if (USE_REG_TB) { - c = -tcg_current_code_size(s); - if (check_fit_i32(c, 13)) { - tcg_out_arithi(s, TCG_REG_TB, TCG_REG_TB, c, ARITH_ADD); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_T1, c); - tcg_out_arith(s, TCG_REG_TB, TCG_REG_TB, - TCG_REG_T1, ARITH_ADD); - } - } - break; case INDEX_op_goto_ptr: tcg_out_arithi(s, TCG_REG_G0, a0, 0, JMPL); - if (USE_REG_TB) { - tcg_out_mov_delay(s, TCG_REG_TB, a0); - } else { - tcg_out_nop(s); - } + tcg_out_mov_delay(s, TCG_REG_TB, a0); break; case INDEX_op_br: tcg_out_bpcc(s, COND_A, BPCC_PT, arg_label(a0)); @@ -1697,7 +1389,10 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_brcond_i32(s, a2, a0, a1, const_args[1], arg_label(args[3])); break; case INDEX_op_setcond_i32: - tcg_out_setcond_i32(s, args[3], a0, a1, a2, c2); + tcg_out_setcond_i32(s, args[3], a0, a1, a2, c2, false); + break; + case INDEX_op_negsetcond_i32: + tcg_out_setcond_i32(s, args[3], a0, a1, a2, c2, true); break; case INDEX_op_movcond_i32: tcg_out_movcond_i32(s, args[5], a0, a1, a2, c2, args[3], const_args[3]); @@ -1719,25 +1414,26 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_muls2_i32: c = ARITH_SMUL; do_mul2: - /* The 32-bit multiply insns produce a full 64-bit result. If the - destination register can hold it, we can avoid the slower RDY. */ + /* The 32-bit multiply insns produce a full 64-bit result. */ tcg_out_arithc(s, a0, a2, args[3], const_args[3], c); - if (SPARC64 || a0 <= TCG_REG_O7) { - tcg_out_arithi(s, a1, a0, 32, SHIFT_SRLX); - } else { - tcg_out_rdy(s, a1); - } + tcg_out_arithi(s, a1, a0, 32, SHIFT_SRLX); break; - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, a0, a1, a2, false); + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, a0, a1, a2, true); + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: + tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I64); break; - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, a0, a1, a2); + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I32); + break; + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: + tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I64); break; case INDEX_op_ld32s_i64: @@ -1770,26 +1466,15 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_divu_i64: c = ARITH_UDIVX; goto gen_arith; - case INDEX_op_ext_i32_i64: - case INDEX_op_ext32s_i64: - tcg_out_arithi(s, a0, a1, 0, SHIFT_SRA); - break; - case INDEX_op_extu_i32_i64: - case INDEX_op_ext32u_i64: - tcg_out_arithi(s, a0, a1, 0, SHIFT_SRL); - break; - case INDEX_op_extrl_i64_i32: - tcg_out_mov(s, TCG_TYPE_I32, a0, a1); - break; - case INDEX_op_extrh_i64_i32: - tcg_out_arithi(s, a0, a1, 32, SHIFT_SRLX); - break; case INDEX_op_brcond_i64: tcg_out_brcond_i64(s, a2, a0, a1, const_args[1], arg_label(args[3])); break; case INDEX_op_setcond_i64: - tcg_out_setcond_i64(s, args[3], a0, a1, a2, c2); + tcg_out_setcond_i64(s, args[3], a0, a1, a2, c2, false); + break; + case INDEX_op_negsetcond_i64: + tcg_out_setcond_i64(s, args[3], a0, a1, a2, c2, true); break; case INDEX_op_movcond_i64: tcg_out_movcond_i64(s, args[5], a0, a1, a2, c2, args[3], const_args[3]); @@ -1821,8 +1506,22 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ + case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ + case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ + case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ + case INDEX_op_ext8s_i64: + case INDEX_op_ext8u_i32: + case INDEX_op_ext8u_i64: + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + case INDEX_op_ext16u_i32: + case INDEX_op_ext16u_i64: + case INDEX_op_ext32s_i64: + case INDEX_op_ext32u_i64: + case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: default: - tcg_abort(); + g_assert_not_reached(); } } @@ -1833,107 +1532,92 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) return C_O0_I1(r); case INDEX_op_ld8u_i32: + case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i32: + case INDEX_op_ld8s_i64: case INDEX_op_ld16u_i32: + case INDEX_op_ld16u_i64: case INDEX_op_ld16s_i32: + case INDEX_op_ld16s_i64: case INDEX_op_ld_i32: + case INDEX_op_ld32u_i64: + case INDEX_op_ld32s_i64: + case INDEX_op_ld_i64: case INDEX_op_neg_i32: + case INDEX_op_neg_i64: case INDEX_op_not_i32: + case INDEX_op_not_i64: + case INDEX_op_ext32s_i64: + case INDEX_op_ext32u_i64: + case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: + case INDEX_op_st8_i64: case INDEX_op_st16_i32: + case INDEX_op_st16_i64: case INDEX_op_st_i32: + case INDEX_op_st32_i64: + case INDEX_op_st_i64: + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: return C_O0_I2(rZ, r); case INDEX_op_add_i32: + case INDEX_op_add_i64: case INDEX_op_mul_i32: + case INDEX_op_mul_i64: case INDEX_op_div_i32: + case INDEX_op_div_i64: case INDEX_op_divu_i32: + case INDEX_op_divu_i64: case INDEX_op_sub_i32: + case INDEX_op_sub_i64: case INDEX_op_and_i32: + case INDEX_op_and_i64: case INDEX_op_andc_i32: + case INDEX_op_andc_i64: case INDEX_op_or_i32: + case INDEX_op_or_i64: case INDEX_op_orc_i32: + case INDEX_op_orc_i64: case INDEX_op_xor_i32: + case INDEX_op_xor_i64: case INDEX_op_shl_i32: + case INDEX_op_shl_i64: case INDEX_op_shr_i32: + case INDEX_op_shr_i64: case INDEX_op_sar_i32: + case INDEX_op_sar_i64: case INDEX_op_setcond_i32: + case INDEX_op_setcond_i64: + case INDEX_op_negsetcond_i32: + case INDEX_op_negsetcond_i64: return C_O1_I2(r, rZ, rJ); case INDEX_op_brcond_i32: + case INDEX_op_brcond_i64: return C_O0_I2(rZ, rJ); case INDEX_op_movcond_i32: + case INDEX_op_movcond_i64: return C_O1_I4(r, rZ, rJ, rI, 0); case INDEX_op_add2_i32: + case INDEX_op_add2_i64: case INDEX_op_sub2_i32: + case INDEX_op_sub2_i64: return C_O2_I4(r, r, rZ, rZ, rJ, rJ); case INDEX_op_mulu2_i32: case INDEX_op_muls2_i32: return C_O2_I2(r, r, rZ, rJ); - - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: - case INDEX_op_ext_i32_i64: - case INDEX_op_extu_i32_i64: - return C_O1_I1(R, r); - - case INDEX_op_st8_i64: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - return C_O0_I2(RZ, r); - - case INDEX_op_add_i64: - case INDEX_op_mul_i64: - case INDEX_op_div_i64: - case INDEX_op_divu_i64: - case INDEX_op_sub_i64: - case INDEX_op_and_i64: - case INDEX_op_andc_i64: - case INDEX_op_or_i64: - case INDEX_op_orc_i64: - case INDEX_op_xor_i64: - case INDEX_op_shl_i64: - case INDEX_op_shr_i64: - case INDEX_op_sar_i64: - case INDEX_op_setcond_i64: - return C_O1_I2(R, RZ, RJ); - - case INDEX_op_neg_i64: - case INDEX_op_not_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: - return C_O1_I1(R, R); - - case INDEX_op_extrl_i64_i32: - case INDEX_op_extrh_i64_i32: - return C_O1_I1(r, R); - - case INDEX_op_brcond_i64: - return C_O0_I2(RZ, RJ); - case INDEX_op_movcond_i64: - return C_O1_I4(R, RZ, RJ, RI, 0); - case INDEX_op_add2_i64: - case INDEX_op_sub2_i64: - return C_O2_I4(R, R, RZ, RZ, RJ, RI); case INDEX_op_muluh_i64: - return C_O1_I2(R, R, R); - - case INDEX_op_qemu_ld_i32: - return C_O1_I1(r, A); - case INDEX_op_qemu_ld_i64: - return C_O1_I1(R, A); - case INDEX_op_qemu_st_i32: - return C_O0_I2(sZ, A); - case INDEX_op_qemu_st_i64: - return C_O0_I2(SZ, A); + return C_O1_I2(r, r, r); default: g_assert_not_reached(); @@ -1954,7 +1638,7 @@ static void tcg_target_init(TCGContext *s) #endif tcg_target_available_regs[TCG_TYPE_I32] = ALL_GENERAL_REGS; - tcg_target_available_regs[TCG_TYPE_I64] = ALL_GENERAL_REGS64; + tcg_target_available_regs[TCG_TYPE_I64] = ALL_GENERAL_REGS; tcg_target_call_clobber_regs = 0; tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_G1); @@ -1982,18 +1666,14 @@ static void tcg_target_init(TCGContext *s) tcg_regset_set_reg(s->reserved_regs, TCG_REG_O6); /* stack pointer */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_T1); /* for internal use */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_T2); /* for internal use */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_T3); /* for internal use */ } -#if SPARC64 -# define ELF_HOST_MACHINE EM_SPARCV9 -#else -# define ELF_HOST_MACHINE EM_SPARC32PLUS -# define ELF_HOST_FLAGS EF_SPARC_32PLUS -#endif +#define ELF_HOST_MACHINE EM_SPARCV9 typedef struct { DebugFrameHeader h; - uint8_t fde_def_cfa[SPARC64 ? 4 : 2]; + uint8_t fde_def_cfa[4]; uint8_t fde_win_save; uint8_t fde_ret_save[3]; } DebugFrame; @@ -2010,12 +1690,8 @@ static const DebugFrame debug_frame = { .h.fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, h.fde.cie_offset), .fde_def_cfa = { -#if SPARC64 12, 30, /* DW_CFA_def_cfa i6, 2047 */ (2047 & 0x7f) | 0x80, (2047 >> 7) -#else - 13, 30 /* DW_CFA_def_cfa_register i6 */ -#endif }, .fde_win_save = 0x2d, /* DW_CFA_GNU_window_save */ .fde_ret_save = { 9, 15, 31 }, /* DW_CFA_register o7, i7 */ @@ -2025,45 +1701,3 @@ void tcg_register_jit(const void *buf, size_t buf_size) { tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); } - -void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx, - uintptr_t jmp_rw, uintptr_t addr) -{ - intptr_t tb_disp = addr - tc_ptr; - intptr_t br_disp = addr - jmp_rx; - tcg_insn_unit i1, i2; - - /* We can reach the entire address space for ILP32. - For LP64, the code_gen_buffer can't be larger than 2GB. */ - tcg_debug_assert(tb_disp == (int32_t)tb_disp); - tcg_debug_assert(br_disp == (int32_t)br_disp); - - if (!USE_REG_TB) { - qatomic_set((uint32_t *)jmp_rw, - deposit32(CALL, 0, 30, br_disp >> 2)); - flush_idcache_range(jmp_rx, jmp_rw, 4); - return; - } - - /* This does not exercise the range of the branch, but we do - still need to be able to load the new value of TCG_REG_TB. - But this does still happen quite often. */ - if (check_fit_ptr(tb_disp, 13)) { - /* ba,pt %icc, addr */ - i1 = (INSN_OP(0) | INSN_OP2(1) | INSN_COND(COND_A) - | BPCC_ICC | BPCC_PT | INSN_OFF19(br_disp)); - i2 = (ARITH_ADD | INSN_RD(TCG_REG_TB) | INSN_RS1(TCG_REG_TB) - | INSN_IMM13(tb_disp)); - } else if (tb_disp >= 0) { - i1 = SETHI | INSN_RD(TCG_REG_T1) | ((tb_disp & 0xfffffc00) >> 10); - i2 = (ARITH_OR | INSN_RD(TCG_REG_T1) | INSN_RS1(TCG_REG_T1) - | INSN_IMM13(tb_disp & 0x3ff)); - } else { - i1 = SETHI | INSN_RD(TCG_REG_T1) | ((~tb_disp & 0xfffffc00) >> 10); - i2 = (ARITH_XOR | INSN_RD(TCG_REG_T1) | INSN_RS1(TCG_REG_T1) - | INSN_IMM13((tb_disp & 0x3ff) | -0x400)); - } - - qatomic_set((uint64_t *)jmp_rw, deposit64(i2, 32, 32, i1)); - flush_idcache_range(jmp_rx, jmp_rw, 8); -} diff --git a/tcg/sparc/tcg-target.h b/tcg/sparc64/tcg-target.h similarity index 87% rename from tcg/sparc/tcg-target.h rename to tcg/sparc64/tcg-target.h index c050763049..a18906a14e 100644 --- a/tcg/sparc/tcg-target.h +++ b/tcg/sparc64/tcg-target.h @@ -25,10 +25,7 @@ #ifndef SPARC_TCG_TARGET_H #define SPARC_TCG_TARGET_H -#define TCG_TARGET_REG_BITS 64 - #define TCG_TARGET_INSN_UNIT_SIZE 4 -#define TCG_TARGET_TLB_DISPLACEMENT_BITS 32 #define TCG_TARGET_NB_REGS 32 #define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB) @@ -70,19 +67,13 @@ typedef enum { /* used for function call generation */ #define TCG_REG_CALL_STACK TCG_REG_O6 -#ifdef __arch64__ #define TCG_TARGET_STACK_BIAS 2047 #define TCG_TARGET_STACK_ALIGN 16 #define TCG_TARGET_CALL_STACK_OFFSET (128 + 6*8 + TCG_TARGET_STACK_BIAS) -#else -#define TCG_TARGET_STACK_BIAS 0 -#define TCG_TARGET_STACK_ALIGN 8 -#define TCG_TARGET_CALL_STACK_OFFSET (64 + 4 + 6*4) -#endif - -#ifdef __arch64__ -#define TCG_TARGET_EXTEND_ARGS 1 -#endif +#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_EXTEND +#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL #if defined(__VIS__) && __VIS__ >= 0x300 #define use_vis3_instructions 1 @@ -100,7 +91,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_ext16u_i32 0 #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 1 @@ -114,18 +104,16 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_extract_i32 0 #define TCG_TARGET_HAS_sextract_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_movcond_i32 1 +#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_muls2_i32 1 #define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_direct_jump 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_extrl_i64_i32 1 -#define TCG_TARGET_HAS_extrh_i64_i32 1 +#define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 0 #define TCG_TARGET_HAS_rot_i64 0 @@ -138,7 +126,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 -#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_orc_i64 1 @@ -152,7 +139,7 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_extract_i64 0 #define TCG_TARGET_HAS_sextract_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_movcond_i64 1 +#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 @@ -160,13 +147,14 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_muluh_i64 use_vis3_instructions #define TCG_TARGET_HAS_mulsh_i64 0 +#define TCG_TARGET_HAS_qemu_ldst_i128 0 + +#define TCG_TARGET_HAS_tst 1 + #define TCG_AREG0 TCG_REG_I0 #define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_HAS_MEMORY_BSWAP 1 - -void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t, uintptr_t); - +#define TCG_TARGET_NEED_LDST_LABELS #define TCG_TARGET_NEED_POOL_LABELS #endif diff --git a/tcg/tcg-common.c b/tcg/tcg-common.c index aa0c4f60c9..35e7616ae9 100644 --- a/tcg/tcg-common.c +++ b/tcg/tcg-common.c @@ -27,7 +27,7 @@ TCGOpDef tcg_op_defs[] = { #define DEF(s, oargs, iargs, cargs, flags) \ - { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags }, + { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags, NULL }, #include "tcg/tcg-opc.h" #undef DEF }; diff --git a/tcg/tcg-internal.h b/tcg/tcg-internal.h index cc82088d52..9b0d982f65 100644 --- a/tcg/tcg-internal.h +++ b/tcg/tcg-internal.h @@ -25,14 +25,9 @@ #ifndef TCG_INTERNAL_H #define TCG_INTERNAL_H -#define TCG_HIGHWATER 1024 +#include "tcg/helper-info.h" -typedef struct TCGHelperInfo { - void *func; - const char *name; - unsigned flags; - unsigned typemask; -} TCGHelperInfo; +#define TCG_HIGHWATER 1024 extern TCGContext tcg_init_ctx; extern TCGContext **tcg_ctxs; @@ -59,4 +54,53 @@ static inline unsigned tcg_call_flags(TCGOp *op) return tcg_call_info(op)->flags; } +#if TCG_TARGET_REG_BITS == 32 +static inline TCGv_i32 TCGV_LOW(TCGv_i64 t) +{ + return temp_tcgv_i32(tcgv_i64_temp(t) + HOST_BIG_ENDIAN); +} +static inline TCGv_i32 TCGV_HIGH(TCGv_i64 t) +{ + return temp_tcgv_i32(tcgv_i64_temp(t) + !HOST_BIG_ENDIAN); +} +#else +TCGv_i32 TCGV_LOW(TCGv_i64) QEMU_ERROR("32-bit code path is reachable"); +TCGv_i32 TCGV_HIGH(TCGv_i64) QEMU_ERROR("32-bit code path is reachable"); +#endif + +static inline TCGv_i64 TCGV128_LOW(TCGv_i128 t) +{ + /* For 32-bit, offset by 2, which may then have TCGV_{LOW,HIGH} applied. */ + int o = HOST_BIG_ENDIAN ? 64 / TCG_TARGET_REG_BITS : 0; + return temp_tcgv_i64(tcgv_i128_temp(t) + o); +} + +static inline TCGv_i64 TCGV128_HIGH(TCGv_i128 t) +{ + int o = HOST_BIG_ENDIAN ? 0 : 64 / TCG_TARGET_REG_BITS; + return temp_tcgv_i64(tcgv_i128_temp(t) + o); +} + +bool tcg_target_has_memory_bswap(MemOp memop); + +TCGTemp *tcg_temp_new_internal(TCGType type, TCGTempKind kind); + +/* + * Locate or create a read-only temporary that is a constant. + * This kind of temporary need not be freed, but for convenience + * will be silently ignored by tcg_temp_free_*. + */ +TCGTemp *tcg_constant_internal(TCGType type, int64_t val); + +void tcg_gen_op1(TCGOpcode, TCGArg); +void tcg_gen_op2(TCGOpcode, TCGArg, TCGArg); +void tcg_gen_op3(TCGOpcode, TCGArg, TCGArg, TCGArg); +void tcg_gen_op4(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg); +void tcg_gen_op5(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); +void tcg_gen_op6(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); + +void vec_gen_2(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg); +void vec_gen_3(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg); +void vec_gen_4(TCGOpcode, TCGType, unsigned, TCGArg, TCGArg, TCGArg, TCGArg); + #endif /* TCG_INTERNAL_H */ diff --git a/tcg/tcg-ldst.c.inc b/tcg/tcg-ldst.c.inc index 6c6848d034..ffada04af0 100644 --- a/tcg/tcg-ldst.c.inc +++ b/tcg/tcg-ldst.c.inc @@ -20,20 +20,6 @@ * THE SOFTWARE. */ -typedef struct TCGLabelQemuLdst { - bool is_ld; /* qemu_ld: true, qemu_st: false */ - MemOpIdx oi; - TCGType type; /* result type of a load */ - TCGReg addrlo_reg; /* reg index for low word of guest virtual addr */ - TCGReg addrhi_reg; /* reg index for high word of guest virtual addr */ - TCGReg datalo_reg; /* reg index for low word to be loaded or stored */ - TCGReg datahi_reg; /* reg index for high word to be loaded or stored */ - const tcg_insn_unit *raddr; /* addr of the next IR of qemu_ld/st IR */ - tcg_insn_unit *label_ptr[2]; /* label pointers to be updated */ - QSIMPLEQ_ENTRY(TCGLabelQemuLdst) next; -} TCGLabelQemuLdst; - - /* * Generate TB finalization at the end of block */ @@ -72,6 +58,7 @@ static inline TCGLabelQemuLdst *new_ldst_label(TCGContext *s) { TCGLabelQemuLdst *l = tcg_malloc(sizeof(*l)); + memset(l, 0, sizeof(*l)); QSIMPLEQ_INSERT_TAIL(&s->ldst_labels, l, next); return l; diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 079a761b04..bb88943f79 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -19,9 +19,9 @@ #include "qemu/osdep.h" #include "tcg/tcg.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "qemu/main-loop.h" +#include "tcg/tcg-temp-internal.h" +#include "tcg/tcg-op-common.h" +#include "tcg/tcg-op-gvec-common.h" #include "tcg/tcg-gvec-desc.h" #define MAX_UNROLL 4 @@ -117,11 +117,11 @@ void tcg_gen_gvec_2_ool(uint32_t dofs, uint32_t aofs, TCGv_ptr a0, a1; TCGv_i32 desc = tcg_constant_i32(simd_desc(oprsz, maxsz, data)); - a0 = tcg_temp_new_ptr(); - a1 = tcg_temp_new_ptr(); + a0 = tcg_temp_ebb_new_ptr(); + a1 = tcg_temp_ebb_new_ptr(); - tcg_gen_addi_ptr(a0, cpu_env, dofs); - tcg_gen_addi_ptr(a1, cpu_env, aofs); + tcg_gen_addi_ptr(a0, tcg_env, dofs); + tcg_gen_addi_ptr(a1, tcg_env, aofs); fn(a0, a1, desc); @@ -138,11 +138,11 @@ void tcg_gen_gvec_2i_ool(uint32_t dofs, uint32_t aofs, TCGv_i64 c, TCGv_ptr a0, a1; TCGv_i32 desc = tcg_constant_i32(simd_desc(oprsz, maxsz, data)); - a0 = tcg_temp_new_ptr(); - a1 = tcg_temp_new_ptr(); + a0 = tcg_temp_ebb_new_ptr(); + a1 = tcg_temp_ebb_new_ptr(); - tcg_gen_addi_ptr(a0, cpu_env, dofs); - tcg_gen_addi_ptr(a1, cpu_env, aofs); + tcg_gen_addi_ptr(a0, tcg_env, dofs); + tcg_gen_addi_ptr(a1, tcg_env, aofs); fn(a0, a1, c, desc); @@ -158,13 +158,13 @@ void tcg_gen_gvec_3_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, TCGv_ptr a0, a1, a2; TCGv_i32 desc = tcg_constant_i32(simd_desc(oprsz, maxsz, data)); - a0 = tcg_temp_new_ptr(); - a1 = tcg_temp_new_ptr(); - a2 = tcg_temp_new_ptr(); + a0 = tcg_temp_ebb_new_ptr(); + a1 = tcg_temp_ebb_new_ptr(); + a2 = tcg_temp_ebb_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(a0, tcg_env, dofs); + tcg_gen_addi_ptr(a1, tcg_env, aofs); + tcg_gen_addi_ptr(a2, tcg_env, bofs); fn(a0, a1, a2, desc); @@ -181,15 +181,15 @@ void tcg_gen_gvec_4_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, TCGv_ptr a0, a1, a2, a3; TCGv_i32 desc = tcg_constant_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(); + a0 = tcg_temp_ebb_new_ptr(); + a1 = tcg_temp_ebb_new_ptr(); + a2 = tcg_temp_ebb_new_ptr(); + a3 = tcg_temp_ebb_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(a0, tcg_env, dofs); + tcg_gen_addi_ptr(a1, tcg_env, aofs); + tcg_gen_addi_ptr(a2, tcg_env, bofs); + tcg_gen_addi_ptr(a3, tcg_env, cofs); fn(a0, a1, a2, a3, desc); @@ -207,17 +207,17 @@ void tcg_gen_gvec_5_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, TCGv_ptr a0, a1, a2, a3, a4; TCGv_i32 desc = tcg_constant_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(); + a0 = tcg_temp_ebb_new_ptr(); + a1 = tcg_temp_ebb_new_ptr(); + a2 = tcg_temp_ebb_new_ptr(); + a3 = tcg_temp_ebb_new_ptr(); + a4 = tcg_temp_ebb_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, xofs); + tcg_gen_addi_ptr(a0, tcg_env, dofs); + tcg_gen_addi_ptr(a1, tcg_env, aofs); + tcg_gen_addi_ptr(a2, tcg_env, bofs); + tcg_gen_addi_ptr(a3, tcg_env, cofs); + tcg_gen_addi_ptr(a4, tcg_env, xofs); fn(a0, a1, a2, a3, a4, desc); @@ -237,11 +237,11 @@ void tcg_gen_gvec_2_ptr(uint32_t dofs, uint32_t aofs, TCGv_ptr a0, a1; TCGv_i32 desc = tcg_constant_i32(simd_desc(oprsz, maxsz, data)); - a0 = tcg_temp_new_ptr(); - a1 = tcg_temp_new_ptr(); + a0 = tcg_temp_ebb_new_ptr(); + a1 = tcg_temp_ebb_new_ptr(); - tcg_gen_addi_ptr(a0, cpu_env, dofs); - tcg_gen_addi_ptr(a1, cpu_env, aofs); + tcg_gen_addi_ptr(a0, tcg_env, dofs); + tcg_gen_addi_ptr(a1, tcg_env, aofs); fn(a0, a1, ptr, desc); @@ -258,13 +258,13 @@ void tcg_gen_gvec_3_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, TCGv_ptr a0, a1, a2; TCGv_i32 desc = tcg_constant_i32(simd_desc(oprsz, maxsz, data)); - a0 = tcg_temp_new_ptr(); - a1 = tcg_temp_new_ptr(); - a2 = tcg_temp_new_ptr(); + a0 = tcg_temp_ebb_new_ptr(); + a1 = tcg_temp_ebb_new_ptr(); + a2 = tcg_temp_ebb_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(a0, tcg_env, dofs); + tcg_gen_addi_ptr(a1, tcg_env, aofs); + tcg_gen_addi_ptr(a2, tcg_env, bofs); fn(a0, a1, a2, ptr, desc); @@ -283,15 +283,15 @@ void tcg_gen_gvec_4_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, TCGv_ptr a0, a1, a2, a3; TCGv_i32 desc = tcg_constant_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(); + a0 = tcg_temp_ebb_new_ptr(); + a1 = tcg_temp_ebb_new_ptr(); + a2 = tcg_temp_ebb_new_ptr(); + a3 = tcg_temp_ebb_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(a0, tcg_env, dofs); + tcg_gen_addi_ptr(a1, tcg_env, aofs); + tcg_gen_addi_ptr(a2, tcg_env, bofs); + tcg_gen_addi_ptr(a3, tcg_env, cofs); fn(a0, a1, a2, a3, ptr, desc); @@ -311,17 +311,17 @@ void tcg_gen_gvec_5_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, TCGv_ptr a0, a1, a2, a3, a4; TCGv_i32 desc = tcg_constant_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(); + a0 = tcg_temp_ebb_new_ptr(); + a1 = tcg_temp_ebb_new_ptr(); + a2 = tcg_temp_ebb_new_ptr(); + a3 = tcg_temp_ebb_new_ptr(); + a4 = tcg_temp_ebb_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); + tcg_gen_addi_ptr(a0, tcg_env, dofs); + tcg_gen_addi_ptr(a1, tcg_env, aofs); + tcg_gen_addi_ptr(a2, tcg_env, bofs); + tcg_gen_addi_ptr(a3, tcg_env, cofs); + tcg_gen_addi_ptr(a4, tcg_env, eofs); fn(a0, a1, a2, a3, a4, ptr, desc); @@ -482,7 +482,7 @@ static void do_dup_store(TCGType type, uint32_t dofs, uint32_t oprsz, * are misaligned wrt the maximum vector size, so do that first. */ if (dofs & 8) { - tcg_gen_stl_vec(t_vec, cpu_env, dofs + i, TCG_TYPE_V64); + tcg_gen_stl_vec(t_vec, tcg_env, dofs + i, TCG_TYPE_V64); i += 8; } @@ -494,17 +494,17 @@ static void do_dup_store(TCGType type, uint32_t dofs, uint32_t oprsz, * that e.g. size == 80 would be expanded with 2x32 + 1x16. */ for (; i + 32 <= oprsz; i += 32) { - tcg_gen_stl_vec(t_vec, cpu_env, dofs + i, TCG_TYPE_V256); + tcg_gen_stl_vec(t_vec, tcg_env, dofs + i, TCG_TYPE_V256); } /* fallthru */ case TCG_TYPE_V128: for (; i + 16 <= oprsz; i += 16) { - tcg_gen_stl_vec(t_vec, cpu_env, dofs + i, TCG_TYPE_V128); + tcg_gen_stl_vec(t_vec, tcg_env, dofs + i, TCG_TYPE_V128); } break; case TCG_TYPE_V64: for (; i < oprsz; i += 8) { - tcg_gen_stl_vec(t_vec, cpu_env, dofs + i, TCG_TYPE_V64); + tcg_gen_stl_vec(t_vec, tcg_env, dofs + i, TCG_TYPE_V64); } break; default: @@ -561,7 +561,6 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, tcg_gen_dupi_vec(vece, t_vec, in_c); } do_dup_store(type, dofs, oprsz, maxsz, t_vec); - tcg_temp_free_vec(t_vec); return; } @@ -576,16 +575,16 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, be simple enough. */ if (TCG_TARGET_REG_BITS == 64 && (vece != MO_32 || !check_size_impl(oprsz, 4))) { - t_64 = tcg_temp_new_i64(); + t_64 = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t_64, in_32); tcg_gen_dup_i64(vece, t_64, t_64); } else { - t_32 = tcg_temp_new_i32(); + t_32 = tcg_temp_ebb_new_i32(); tcg_gen_dup_i32(vece, t_32, in_32); } } else if (in_64) { /* We are given a 64-bit variable input. */ - t_64 = tcg_temp_new_i64(); + t_64 = tcg_temp_ebb_new_i64(); tcg_gen_dup_i64(vece, t_64, in_64); } else { /* We are given a constant input. */ @@ -605,14 +604,14 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, /* Implement inline if we picked an implementation size above. */ if (t_32) { for (i = 0; i < oprsz; i += 4) { - tcg_gen_st_i32(t_32, cpu_env, dofs + i); + tcg_gen_st_i32(t_32, tcg_env, dofs + i); } tcg_temp_free_i32(t_32); goto done; } if (t_64) { for (i = 0; i < oprsz; i += 8) { - tcg_gen_st_i64(t_64, cpu_env, dofs + i); + tcg_gen_st_i64(t_64, tcg_env, dofs + i); } tcg_temp_free_i64(t_64); goto done; @@ -620,8 +619,8 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, } /* Otherwise implement out of line. */ - t_ptr = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(t_ptr, cpu_env, dofs); + t_ptr = tcg_temp_ebb_new_ptr(); + tcg_gen_addi_ptr(t_ptr, tcg_env, dofs); /* * This may be expand_clr for the tail of an operation, e.g. @@ -630,13 +629,13 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, * stores through to memset. */ if (oprsz == maxsz && vece == MO_8) { - TCGv_ptr t_size = tcg_const_ptr(oprsz); + TCGv_ptr t_size = tcg_constant_ptr(oprsz); TCGv_i32 t_val; if (in_32) { t_val = in_32; } else if (in_64) { - t_val = tcg_temp_new_i32(); + t_val = tcg_temp_ebb_new_i32(); tcg_gen_extrl_i64_i32(t_val, in_64); } else { t_val = tcg_constant_i32(in_c); @@ -646,7 +645,6 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, if (in_64) { tcg_temp_free_i32(t_val); } - tcg_temp_free_ptr(t_size); tcg_temp_free_ptr(t_ptr); return; } @@ -671,7 +669,7 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, if (in_32) { fns[vece](t_ptr, t_desc, in_32); } else if (in_64) { - t_32 = tcg_temp_new_i32(); + t_32 = tcg_temp_ebb_new_i32(); tcg_gen_extrl_i64_i32(t_32, in_64); fns[vece](t_ptr, t_desc, t_32); tcg_temp_free_i32(t_32); @@ -710,12 +708,12 @@ static void expand_2_i32(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t i; for (i = 0; i < oprsz; i += 4) { - tcg_gen_ld_i32(t0, cpu_env, aofs + i); + tcg_gen_ld_i32(t0, tcg_env, aofs + i); if (load_dest) { - tcg_gen_ld_i32(t1, cpu_env, dofs + i); + tcg_gen_ld_i32(t1, tcg_env, dofs + i); } fni(t1, t0); - tcg_gen_st_i32(t1, cpu_env, dofs + i); + tcg_gen_st_i32(t1, tcg_env, dofs + i); } tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); @@ -730,12 +728,12 @@ static void expand_2i_i32(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t i; for (i = 0; i < oprsz; i += 4) { - tcg_gen_ld_i32(t0, cpu_env, aofs + i); + tcg_gen_ld_i32(t0, tcg_env, aofs + i); if (load_dest) { - tcg_gen_ld_i32(t1, cpu_env, dofs + i); + tcg_gen_ld_i32(t1, tcg_env, dofs + i); } fni(t1, t0, c); - tcg_gen_st_i32(t1, cpu_env, dofs + i); + tcg_gen_st_i32(t1, tcg_env, dofs + i); } tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); @@ -750,13 +748,13 @@ static void expand_2s_i32(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t i; for (i = 0; i < oprsz; i += 4) { - tcg_gen_ld_i32(t0, cpu_env, aofs + i); + tcg_gen_ld_i32(t0, tcg_env, aofs + i); if (scalar_first) { fni(t1, c, t0); } else { fni(t1, t0, c); } - tcg_gen_st_i32(t1, cpu_env, dofs + i); + tcg_gen_st_i32(t1, tcg_env, dofs + i); } tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); @@ -773,13 +771,13 @@ static void expand_3_i32(uint32_t dofs, uint32_t aofs, uint32_t i; for (i = 0; i < oprsz; i += 4) { - tcg_gen_ld_i32(t0, cpu_env, aofs + i); - tcg_gen_ld_i32(t1, cpu_env, bofs + i); + tcg_gen_ld_i32(t0, tcg_env, aofs + i); + tcg_gen_ld_i32(t1, tcg_env, bofs + i); if (load_dest) { - tcg_gen_ld_i32(t2, cpu_env, dofs + i); + tcg_gen_ld_i32(t2, tcg_env, dofs + i); } fni(t2, t0, t1); - tcg_gen_st_i32(t2, cpu_env, dofs + i); + tcg_gen_st_i32(t2, tcg_env, dofs + i); } tcg_temp_free_i32(t2); tcg_temp_free_i32(t1); @@ -796,13 +794,13 @@ static void expand_3i_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t i; for (i = 0; i < oprsz; i += 4) { - tcg_gen_ld_i32(t0, cpu_env, aofs + i); - tcg_gen_ld_i32(t1, cpu_env, bofs + i); + tcg_gen_ld_i32(t0, tcg_env, aofs + i); + tcg_gen_ld_i32(t1, tcg_env, bofs + i); if (load_dest) { - tcg_gen_ld_i32(t2, cpu_env, dofs + i); + tcg_gen_ld_i32(t2, tcg_env, dofs + i); } fni(t2, t0, t1, c); - tcg_gen_st_i32(t2, cpu_env, dofs + i); + tcg_gen_st_i32(t2, tcg_env, dofs + i); } tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); @@ -821,13 +819,13 @@ static void expand_4_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t i; for (i = 0; i < oprsz; i += 4) { - tcg_gen_ld_i32(t1, cpu_env, aofs + i); - tcg_gen_ld_i32(t2, cpu_env, bofs + i); - tcg_gen_ld_i32(t3, cpu_env, cofs + i); + tcg_gen_ld_i32(t1, tcg_env, aofs + i); + tcg_gen_ld_i32(t2, tcg_env, bofs + i); + tcg_gen_ld_i32(t3, tcg_env, cofs + i); fni(t0, t1, t2, t3); - tcg_gen_st_i32(t0, cpu_env, dofs + i); + tcg_gen_st_i32(t0, tcg_env, dofs + i); if (write_aofs) { - tcg_gen_st_i32(t1, cpu_env, aofs + i); + tcg_gen_st_i32(t1, tcg_env, aofs + i); } } tcg_temp_free_i32(t3); @@ -848,11 +846,11 @@ static void expand_4i_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t i; for (i = 0; i < oprsz; i += 4) { - tcg_gen_ld_i32(t1, cpu_env, aofs + i); - tcg_gen_ld_i32(t2, cpu_env, bofs + i); - tcg_gen_ld_i32(t3, cpu_env, cofs + i); + tcg_gen_ld_i32(t1, tcg_env, aofs + i); + tcg_gen_ld_i32(t2, tcg_env, bofs + i); + tcg_gen_ld_i32(t3, tcg_env, cofs + i); fni(t0, t1, t2, t3, c); - tcg_gen_st_i32(t0, cpu_env, dofs + i); + tcg_gen_st_i32(t0, tcg_env, dofs + i); } tcg_temp_free_i32(t3); tcg_temp_free_i32(t2); @@ -869,12 +867,12 @@ static void expand_2_i64(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t i; for (i = 0; i < oprsz; i += 8) { - tcg_gen_ld_i64(t0, cpu_env, aofs + i); + tcg_gen_ld_i64(t0, tcg_env, aofs + i); if (load_dest) { - tcg_gen_ld_i64(t1, cpu_env, dofs + i); + tcg_gen_ld_i64(t1, tcg_env, dofs + i); } fni(t1, t0); - tcg_gen_st_i64(t1, cpu_env, dofs + i); + tcg_gen_st_i64(t1, tcg_env, dofs + i); } tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); @@ -889,12 +887,12 @@ static void expand_2i_i64(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t i; for (i = 0; i < oprsz; i += 8) { - tcg_gen_ld_i64(t0, cpu_env, aofs + i); + tcg_gen_ld_i64(t0, tcg_env, aofs + i); if (load_dest) { - tcg_gen_ld_i64(t1, cpu_env, dofs + i); + tcg_gen_ld_i64(t1, tcg_env, dofs + i); } fni(t1, t0, c); - tcg_gen_st_i64(t1, cpu_env, dofs + i); + tcg_gen_st_i64(t1, tcg_env, dofs + i); } tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); @@ -909,13 +907,13 @@ static void expand_2s_i64(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t i; for (i = 0; i < oprsz; i += 8) { - tcg_gen_ld_i64(t0, cpu_env, aofs + i); + tcg_gen_ld_i64(t0, tcg_env, aofs + i); if (scalar_first) { fni(t1, c, t0); } else { fni(t1, t0, c); } - tcg_gen_st_i64(t1, cpu_env, dofs + i); + tcg_gen_st_i64(t1, tcg_env, dofs + i); } tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); @@ -932,13 +930,13 @@ static void expand_3_i64(uint32_t dofs, uint32_t aofs, uint32_t i; for (i = 0; i < oprsz; i += 8) { - tcg_gen_ld_i64(t0, cpu_env, aofs + i); - tcg_gen_ld_i64(t1, cpu_env, bofs + i); + tcg_gen_ld_i64(t0, tcg_env, aofs + i); + tcg_gen_ld_i64(t1, tcg_env, bofs + i); if (load_dest) { - tcg_gen_ld_i64(t2, cpu_env, dofs + i); + tcg_gen_ld_i64(t2, tcg_env, dofs + i); } fni(t2, t0, t1); - tcg_gen_st_i64(t2, cpu_env, dofs + i); + tcg_gen_st_i64(t2, tcg_env, dofs + i); } tcg_temp_free_i64(t2); tcg_temp_free_i64(t1); @@ -955,13 +953,13 @@ static void expand_3i_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t i; for (i = 0; i < oprsz; i += 8) { - tcg_gen_ld_i64(t0, cpu_env, aofs + i); - tcg_gen_ld_i64(t1, cpu_env, bofs + i); + tcg_gen_ld_i64(t0, tcg_env, aofs + i); + tcg_gen_ld_i64(t1, tcg_env, bofs + i); if (load_dest) { - tcg_gen_ld_i64(t2, cpu_env, dofs + i); + tcg_gen_ld_i64(t2, tcg_env, dofs + i); } fni(t2, t0, t1, c); - tcg_gen_st_i64(t2, cpu_env, dofs + i); + tcg_gen_st_i64(t2, tcg_env, dofs + i); } tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); @@ -980,13 +978,13 @@ static void expand_4_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t i; for (i = 0; i < oprsz; i += 8) { - tcg_gen_ld_i64(t1, cpu_env, aofs + i); - tcg_gen_ld_i64(t2, cpu_env, bofs + i); - tcg_gen_ld_i64(t3, cpu_env, cofs + i); + tcg_gen_ld_i64(t1, tcg_env, aofs + i); + tcg_gen_ld_i64(t2, tcg_env, bofs + i); + tcg_gen_ld_i64(t3, tcg_env, cofs + i); fni(t0, t1, t2, t3); - tcg_gen_st_i64(t0, cpu_env, dofs + i); + tcg_gen_st_i64(t0, tcg_env, dofs + i); if (write_aofs) { - tcg_gen_st_i64(t1, cpu_env, aofs + i); + tcg_gen_st_i64(t1, tcg_env, aofs + i); } } tcg_temp_free_i64(t3); @@ -1007,11 +1005,11 @@ static void expand_4i_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t i; for (i = 0; i < oprsz; i += 8) { - tcg_gen_ld_i64(t1, cpu_env, aofs + i); - tcg_gen_ld_i64(t2, cpu_env, bofs + i); - tcg_gen_ld_i64(t3, cpu_env, cofs + i); + tcg_gen_ld_i64(t1, tcg_env, aofs + i); + tcg_gen_ld_i64(t2, tcg_env, bofs + i); + tcg_gen_ld_i64(t3, tcg_env, cofs + i); fni(t0, t1, t2, t3, c); - tcg_gen_st_i64(t0, cpu_env, dofs + i); + tcg_gen_st_i64(t0, tcg_env, dofs + i); } tcg_temp_free_i64(t3); tcg_temp_free_i64(t2); @@ -1025,20 +1023,17 @@ static void expand_2_vec(unsigned vece, uint32_t dofs, uint32_t aofs, bool load_dest, void (*fni)(unsigned, TCGv_vec, TCGv_vec)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { - tcg_gen_ld_vec(t0, cpu_env, aofs + i); + tcg_gen_ld_vec(t0, tcg_env, aofs + i); if (load_dest) { - tcg_gen_ld_vec(t1, cpu_env, dofs + i); + tcg_gen_ld_vec(t1, tcg_env, dofs + i); } fni(vece, t1, t0); - tcg_gen_st_vec(t1, cpu_env, dofs + i); + tcg_gen_st_vec(t1, tcg_env, dofs + i); } - tcg_temp_free_vec(t0); - tcg_temp_free_vec(t1); } /* Expand OPSZ bytes worth of two-vector operands and an immediate operand @@ -1048,20 +1043,17 @@ static void expand_2i_vec(unsigned vece, uint32_t dofs, uint32_t aofs, int64_t c, bool load_dest, void (*fni)(unsigned, TCGv_vec, TCGv_vec, int64_t)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { - tcg_gen_ld_vec(t0, cpu_env, aofs + i); + tcg_gen_ld_vec(t0, tcg_env, aofs + i); if (load_dest) { - tcg_gen_ld_vec(t1, cpu_env, dofs + i); + tcg_gen_ld_vec(t1, tcg_env, dofs + i); } fni(vece, t1, t0, c); - tcg_gen_st_vec(t1, cpu_env, dofs + i); + tcg_gen_st_vec(t1, tcg_env, dofs + i); } - tcg_temp_free_vec(t0); - tcg_temp_free_vec(t1); } static void expand_2s_vec(unsigned vece, uint32_t dofs, uint32_t aofs, @@ -1069,21 +1061,18 @@ static void expand_2s_vec(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_vec c, bool scalar_first, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { - tcg_gen_ld_vec(t0, cpu_env, aofs + i); + tcg_gen_ld_vec(t0, tcg_env, aofs + i); if (scalar_first) { fni(vece, t1, c, t0); } else { fni(vece, t1, t0, c); } - tcg_gen_st_vec(t1, cpu_env, dofs + i); + tcg_gen_st_vec(t1, tcg_env, dofs + i); } - tcg_temp_free_vec(t0); - tcg_temp_free_vec(t1); } /* Expand OPSZ bytes worth of three-operand operations using host vectors. */ @@ -1092,23 +1081,19 @@ static void expand_3_vec(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t tysz, TCGType type, bool load_dest, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - TCGv_vec t2 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); + TCGv_vec t2 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { - tcg_gen_ld_vec(t0, cpu_env, aofs + i); - tcg_gen_ld_vec(t1, cpu_env, bofs + i); + tcg_gen_ld_vec(t0, tcg_env, aofs + i); + tcg_gen_ld_vec(t1, tcg_env, bofs + i); if (load_dest) { - tcg_gen_ld_vec(t2, cpu_env, dofs + i); + tcg_gen_ld_vec(t2, tcg_env, dofs + i); } fni(vece, t2, t0, t1); - tcg_gen_st_vec(t2, cpu_env, dofs + i); + tcg_gen_st_vec(t2, tcg_env, dofs + i); } - tcg_temp_free_vec(t2); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t0); } /* @@ -1121,23 +1106,19 @@ static void expand_3i_vec(unsigned vece, uint32_t dofs, uint32_t aofs, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, int64_t)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - TCGv_vec t2 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); + TCGv_vec t2 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { - tcg_gen_ld_vec(t0, cpu_env, aofs + i); - tcg_gen_ld_vec(t1, cpu_env, bofs + i); + tcg_gen_ld_vec(t0, tcg_env, aofs + i); + tcg_gen_ld_vec(t1, tcg_env, bofs + i); if (load_dest) { - tcg_gen_ld_vec(t2, cpu_env, dofs + i); + tcg_gen_ld_vec(t2, tcg_env, dofs + i); } fni(vece, t2, t0, t1, c); - tcg_gen_st_vec(t2, cpu_env, dofs + i); + tcg_gen_st_vec(t2, tcg_env, dofs + i); } - tcg_temp_free_vec(t0); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t2); } /* Expand OPSZ bytes worth of four-operand operations using host vectors. */ @@ -1147,26 +1128,21 @@ static void expand_4_vec(unsigned vece, uint32_t dofs, uint32_t aofs, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, TCGv_vec)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - TCGv_vec t2 = tcg_temp_new_vec(type); - TCGv_vec t3 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); + TCGv_vec t2 = tcg_temp_new_vec(type); + TCGv_vec t3 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { - tcg_gen_ld_vec(t1, cpu_env, aofs + i); - tcg_gen_ld_vec(t2, cpu_env, bofs + i); - tcg_gen_ld_vec(t3, cpu_env, cofs + i); + tcg_gen_ld_vec(t1, tcg_env, aofs + i); + tcg_gen_ld_vec(t2, tcg_env, bofs + i); + tcg_gen_ld_vec(t3, tcg_env, cofs + i); fni(vece, t0, t1, t2, t3); - tcg_gen_st_vec(t0, cpu_env, dofs + i); + tcg_gen_st_vec(t0, tcg_env, dofs + i); if (write_aofs) { - tcg_gen_st_vec(t1, cpu_env, aofs + i); + tcg_gen_st_vec(t1, tcg_env, aofs + i); } } - tcg_temp_free_vec(t3); - tcg_temp_free_vec(t2); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t0); } /* @@ -1179,23 +1155,18 @@ static void expand_4i_vec(unsigned vece, uint32_t dofs, uint32_t aofs, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, TCGv_vec, int64_t)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - TCGv_vec t2 = tcg_temp_new_vec(type); - TCGv_vec t3 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); + TCGv_vec t2 = tcg_temp_new_vec(type); + TCGv_vec t3 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { - tcg_gen_ld_vec(t1, cpu_env, aofs + i); - tcg_gen_ld_vec(t2, cpu_env, bofs + i); - tcg_gen_ld_vec(t3, cpu_env, cofs + i); + tcg_gen_ld_vec(t1, tcg_env, aofs + i); + tcg_gen_ld_vec(t2, tcg_env, bofs + i); + tcg_gen_ld_vec(t3, tcg_env, cofs + i); fni(vece, t0, t1, t2, t3, c); - tcg_gen_st_vec(t0, cpu_env, dofs + i); + tcg_gen_st_vec(t0, tcg_env, dofs + i); } - tcg_temp_free_vec(t3); - tcg_temp_free_vec(t2); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t0); } /* Expand a vector two-operand operation. */ @@ -1731,27 +1702,26 @@ void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, TCGType type = choose_vector_type(NULL, vece, oprsz, 0); if (type != 0) { TCGv_vec t_vec = tcg_temp_new_vec(type); - tcg_gen_dup_mem_vec(vece, t_vec, cpu_env, aofs); + tcg_gen_dup_mem_vec(vece, t_vec, tcg_env, aofs); do_dup_store(type, dofs, oprsz, maxsz, t_vec); - tcg_temp_free_vec(t_vec); } else if (vece <= MO_32) { - TCGv_i32 in = tcg_temp_new_i32(); + TCGv_i32 in = tcg_temp_ebb_new_i32(); switch (vece) { case MO_8: - tcg_gen_ld8u_i32(in, cpu_env, aofs); + tcg_gen_ld8u_i32(in, tcg_env, aofs); break; case MO_16: - tcg_gen_ld16u_i32(in, cpu_env, aofs); + tcg_gen_ld16u_i32(in, tcg_env, aofs); break; default: - tcg_gen_ld_i32(in, cpu_env, aofs); + tcg_gen_ld_i32(in, tcg_env, aofs); break; } do_dup(vece, dofs, oprsz, maxsz, in, NULL, 0); tcg_temp_free_i32(in); } else { - TCGv_i64 in = tcg_temp_new_i64(); - tcg_gen_ld_i64(in, cpu_env, aofs); + TCGv_i64 in = tcg_temp_ebb_new_i64(); + tcg_gen_ld_i64(in, tcg_env, aofs); do_dup(vece, dofs, oprsz, maxsz, NULL, in, 0); tcg_temp_free_i64(in); } @@ -1763,20 +1733,19 @@ void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, if (TCG_TARGET_HAS_v128) { TCGv_vec in = tcg_temp_new_vec(TCG_TYPE_V128); - tcg_gen_ld_vec(in, cpu_env, aofs); + tcg_gen_ld_vec(in, tcg_env, aofs); for (i = (aofs == dofs) * 16; i < oprsz; i += 16) { - tcg_gen_st_vec(in, cpu_env, dofs + i); + tcg_gen_st_vec(in, tcg_env, dofs + i); } - tcg_temp_free_vec(in); } else { - TCGv_i64 in0 = tcg_temp_new_i64(); - TCGv_i64 in1 = tcg_temp_new_i64(); + TCGv_i64 in0 = tcg_temp_ebb_new_i64(); + TCGv_i64 in1 = tcg_temp_ebb_new_i64(); - tcg_gen_ld_i64(in0, cpu_env, aofs); - tcg_gen_ld_i64(in1, cpu_env, aofs + 8); + tcg_gen_ld_i64(in0, tcg_env, aofs); + tcg_gen_ld_i64(in1, tcg_env, aofs + 8); for (i = (aofs == dofs) * 16; i < oprsz; i += 16) { - tcg_gen_st_i64(in0, cpu_env, dofs + i); - tcg_gen_st_i64(in1, cpu_env, dofs + i + 8); + tcg_gen_st_i64(in0, tcg_env, dofs + i); + tcg_gen_st_i64(in1, tcg_env, dofs + i + 8); } tcg_temp_free_i64(in0); tcg_temp_free_i64(in1); @@ -1793,34 +1762,31 @@ void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, if (TCG_TARGET_HAS_v256) { TCGv_vec in = tcg_temp_new_vec(TCG_TYPE_V256); - tcg_gen_ld_vec(in, cpu_env, aofs); + tcg_gen_ld_vec(in, tcg_env, aofs); for (i = (aofs == dofs) * 32; i < oprsz; i += 32) { - tcg_gen_st_vec(in, cpu_env, dofs + i); + tcg_gen_st_vec(in, tcg_env, dofs + i); } - tcg_temp_free_vec(in); } else if (TCG_TARGET_HAS_v128) { TCGv_vec in0 = tcg_temp_new_vec(TCG_TYPE_V128); TCGv_vec in1 = tcg_temp_new_vec(TCG_TYPE_V128); - tcg_gen_ld_vec(in0, cpu_env, aofs); - tcg_gen_ld_vec(in1, cpu_env, aofs + 16); + tcg_gen_ld_vec(in0, tcg_env, aofs); + tcg_gen_ld_vec(in1, tcg_env, aofs + 16); for (i = (aofs == dofs) * 32; i < oprsz; i += 32) { - tcg_gen_st_vec(in0, cpu_env, dofs + i); - tcg_gen_st_vec(in1, cpu_env, dofs + i + 16); + tcg_gen_st_vec(in0, tcg_env, dofs + i); + tcg_gen_st_vec(in1, tcg_env, dofs + i + 16); } - tcg_temp_free_vec(in0); - tcg_temp_free_vec(in1); } else { TCGv_i64 in[4]; int j; for (j = 0; j < 4; ++j) { - in[j] = tcg_temp_new_i64(); - tcg_gen_ld_i64(in[j], cpu_env, aofs + j * 8); + in[j] = tcg_temp_ebb_new_i64(); + tcg_gen_ld_i64(in[j], tcg_env, aofs + j * 8); } for (i = (aofs == dofs) * 32; i < oprsz; i += 32) { for (j = 0; j < 4; ++j) { - tcg_gen_st_i64(in[j], cpu_env, dofs + i + j * 8); + tcg_gen_st_i64(in[j], tcg_env, dofs + i + j * 8); } } for (j = 0; j < 4; ++j) { @@ -1860,9 +1826,9 @@ void tcg_gen_gvec_not(unsigned vece, uint32_t dofs, uint32_t aofs, the 64-bit operation. */ static void gen_addv_mask(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b, TCGv_i64 m) { - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); - TCGv_i64 t3 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t2 = tcg_temp_ebb_new_i64(); + TCGv_i64 t3 = tcg_temp_ebb_new_i64(); tcg_gen_andc_i64(t1, a, m); tcg_gen_andc_i64(t2, b, m); @@ -1885,9 +1851,9 @@ void tcg_gen_vec_add8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) void tcg_gen_vec_add8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { TCGv_i32 m = tcg_constant_i32((int32_t)dup_const(MO_8, 0x80)); - TCGv_i32 t1 = tcg_temp_new_i32(); - TCGv_i32 t2 = tcg_temp_new_i32(); - TCGv_i32 t3 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 t2 = tcg_temp_ebb_new_i32(); + TCGv_i32 t3 = tcg_temp_ebb_new_i32(); tcg_gen_andc_i32(t1, a, m); tcg_gen_andc_i32(t2, b, m); @@ -1909,8 +1875,8 @@ void tcg_gen_vec_add16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) void tcg_gen_vec_add16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { - TCGv_i32 t1 = tcg_temp_new_i32(); - TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 t2 = tcg_temp_ebb_new_i32(); tcg_gen_andi_i32(t1, a, ~0xffff); tcg_gen_add_i32(t2, a, b); @@ -1923,8 +1889,8 @@ void tcg_gen_vec_add16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) void tcg_gen_vec_add32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) { - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t2 = tcg_temp_ebb_new_i64(); tcg_gen_andi_i64(t1, a, ~0xffffffffull); tcg_gen_add_i64(t2, a, b); @@ -2043,9 +2009,9 @@ void tcg_gen_gvec_subs(unsigned vece, uint32_t dofs, uint32_t aofs, Compare gen_addv_mask above. */ static void gen_subv_mask(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b, TCGv_i64 m) { - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); - TCGv_i64 t3 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t2 = tcg_temp_ebb_new_i64(); + TCGv_i64 t3 = tcg_temp_ebb_new_i64(); tcg_gen_or_i64(t1, a, m); tcg_gen_andc_i64(t2, b, m); @@ -2068,9 +2034,9 @@ void tcg_gen_vec_sub8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) void tcg_gen_vec_sub8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { TCGv_i32 m = tcg_constant_i32((int32_t)dup_const(MO_8, 0x80)); - TCGv_i32 t1 = tcg_temp_new_i32(); - TCGv_i32 t2 = tcg_temp_new_i32(); - TCGv_i32 t3 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 t2 = tcg_temp_ebb_new_i32(); + TCGv_i32 t3 = tcg_temp_ebb_new_i32(); tcg_gen_or_i32(t1, a, m); tcg_gen_andc_i32(t2, b, m); @@ -2092,8 +2058,8 @@ void tcg_gen_vec_sub16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) void tcg_gen_vec_sub16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { - TCGv_i32 t1 = tcg_temp_new_i32(); - TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 t2 = tcg_temp_ebb_new_i32(); tcg_gen_andi_i32(t1, b, ~0xffff); tcg_gen_sub_i32(t2, a, b); @@ -2106,8 +2072,8 @@ void tcg_gen_vec_sub16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) void tcg_gen_vec_sub32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) { - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t2 = tcg_temp_ebb_new_i64(); tcg_gen_andi_i64(t1, b, ~0xffffffffull); tcg_gen_sub_i64(t2, a, b); @@ -2468,8 +2434,8 @@ void tcg_gen_gvec_umax(unsigned vece, uint32_t dofs, uint32_t aofs, Compare gen_subv_mask above. */ static void gen_negv_mask(TCGv_i64 d, TCGv_i64 b, TCGv_i64 m) { - TCGv_i64 t2 = tcg_temp_new_i64(); - TCGv_i64 t3 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_ebb_new_i64(); + TCGv_i64 t3 = tcg_temp_ebb_new_i64(); tcg_gen_andc_i64(t3, m, b); tcg_gen_andc_i64(t2, b, m); @@ -2494,8 +2460,8 @@ void tcg_gen_vec_neg16_i64(TCGv_i64 d, TCGv_i64 b) void tcg_gen_vec_neg32_i64(TCGv_i64 d, TCGv_i64 b) { - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t2 = tcg_temp_ebb_new_i64(); tcg_gen_andi_i64(t1, b, ~0xffffffffull); tcg_gen_neg_i64(t2, b); @@ -2540,7 +2506,7 @@ void tcg_gen_gvec_neg(unsigned vece, uint32_t dofs, uint32_t aofs, static void gen_absv_mask(TCGv_i64 d, TCGv_i64 b, unsigned vece) { - TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_ebb_new_i64(); int nbit = 8 << vece; /* Create -1 for each negative element. */ @@ -2749,7 +2715,7 @@ static const GVecGen2s gop_ands = { void tcg_gen_gvec_ands(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_i64 c, uint32_t oprsz, uint32_t maxsz) { - TCGv_i64 tmp = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_ebb_new_i64(); tcg_gen_dup_i64(vece, tmp, c); tcg_gen_gvec_2s(dofs, aofs, oprsz, maxsz, tmp, &gop_ands); tcg_temp_free_i64(tmp); @@ -2762,6 +2728,23 @@ void tcg_gen_gvec_andi(unsigned vece, uint32_t dofs, uint32_t aofs, tcg_gen_gvec_2s(dofs, aofs, oprsz, maxsz, tmp, &gop_ands); } +void tcg_gen_gvec_andcs(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i64 c, uint32_t oprsz, uint32_t maxsz) +{ + static GVecGen2s g = { + .fni8 = tcg_gen_andc_i64, + .fniv = tcg_gen_andc_vec, + .fno = gen_helper_gvec_andcs, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .vece = MO_64 + }; + + TCGv_i64 tmp = tcg_temp_ebb_new_i64(); + tcg_gen_dup_i64(vece, tmp, c); + tcg_gen_gvec_2s(dofs, aofs, oprsz, maxsz, tmp, &g); + tcg_temp_free_i64(tmp); +} + static const GVecGen2s gop_xors = { .fni8 = tcg_gen_xor_i64, .fniv = tcg_gen_xor_vec, @@ -2773,7 +2756,7 @@ static const GVecGen2s gop_xors = { void tcg_gen_gvec_xors(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_i64 c, uint32_t oprsz, uint32_t maxsz) { - TCGv_i64 tmp = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_ebb_new_i64(); tcg_gen_dup_i64(vece, tmp, c); tcg_gen_gvec_2s(dofs, aofs, oprsz, maxsz, tmp, &gop_xors); tcg_temp_free_i64(tmp); @@ -2797,7 +2780,7 @@ static const GVecGen2s gop_ors = { void tcg_gen_gvec_ors(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_i64 c, uint32_t oprsz, uint32_t maxsz) { - TCGv_i64 tmp = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_ebb_new_i64(); tcg_gen_dup_i64(vece, tmp, c); tcg_gen_gvec_2s(dofs, aofs, oprsz, maxsz, tmp, &gop_ors); tcg_temp_free_i64(tmp); @@ -2944,7 +2927,7 @@ void tcg_gen_vec_sar8i_i64(TCGv_i64 d, TCGv_i64 a, int64_t c) { uint64_t s_mask = dup_const(MO_8, 0x80 >> c); uint64_t c_mask = dup_const(MO_8, 0xff >> c); - TCGv_i64 s = tcg_temp_new_i64(); + TCGv_i64 s = tcg_temp_ebb_new_i64(); tcg_gen_shri_i64(d, a, c); tcg_gen_andi_i64(s, d, s_mask); /* isolate (shifted) sign bit */ @@ -2958,7 +2941,7 @@ void tcg_gen_vec_sar16i_i64(TCGv_i64 d, TCGv_i64 a, int64_t c) { uint64_t s_mask = dup_const(MO_16, 0x8000 >> c); uint64_t c_mask = dup_const(MO_16, 0xffff >> c); - TCGv_i64 s = tcg_temp_new_i64(); + TCGv_i64 s = tcg_temp_ebb_new_i64(); tcg_gen_shri_i64(d, a, c); tcg_gen_andi_i64(s, d, s_mask); /* isolate (shifted) sign bit */ @@ -2972,7 +2955,7 @@ void tcg_gen_vec_sar8i_i32(TCGv_i32 d, TCGv_i32 a, int32_t c) { uint32_t s_mask = dup_const(MO_8, 0x80 >> c); uint32_t c_mask = dup_const(MO_8, 0xff >> c); - TCGv_i32 s = tcg_temp_new_i32(); + TCGv_i32 s = tcg_temp_ebb_new_i32(); tcg_gen_shri_i32(d, a, c); tcg_gen_andi_i32(s, d, s_mask); /* isolate (shifted) sign bit */ @@ -2986,7 +2969,7 @@ void tcg_gen_vec_sar16i_i32(TCGv_i32 d, TCGv_i32 a, int32_t c) { uint32_t s_mask = dup_const(MO_16, 0x8000 >> c); uint32_t c_mask = dup_const(MO_16, 0xffff >> c); - TCGv_i32 s = tcg_temp_new_i32(); + TCGv_i32 s = tcg_temp_ebb_new_i32(); tcg_gen_shri_i32(d, a, c); tcg_gen_andi_i32(s, d, s_mask); /* isolate (shifted) sign bit */ @@ -3120,15 +3103,14 @@ static void expand_2sh_vec(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_i32 shift, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_i32)) { - TCGv_vec t0 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { - tcg_gen_ld_vec(t0, cpu_env, aofs + i); - fni(vece, t0, t0, shift); - tcg_gen_st_vec(t0, cpu_env, dofs + i); + tcg_gen_ld_vec(t0, tcg_env, aofs + i); + fni(vece, t1, t0, shift); + tcg_gen_st_vec(t1, tcg_env, dofs + i); } - tcg_temp_free_vec(t0); } static void @@ -3180,7 +3162,7 @@ do_gvec_shifts(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_i32 shift, TCGv_vec v_shift = tcg_temp_new_vec(type); if (vece == MO_64) { - TCGv_i64 sh64 = tcg_temp_new_i64(); + TCGv_i64 sh64 = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(sh64, shift); tcg_gen_dup_i64_vec(MO_64, v_shift, sh64); tcg_temp_free_i64(sh64); @@ -3221,19 +3203,19 @@ do_gvec_shifts(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_i32 shift, if (vece == MO_32 && check_size_impl(oprsz, 4)) { expand_2s_i32(dofs, aofs, oprsz, shift, false, g->fni4); } else if (vece == MO_64 && check_size_impl(oprsz, 8)) { - TCGv_i64 sh64 = tcg_temp_new_i64(); + TCGv_i64 sh64 = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(sh64, shift); expand_2s_i64(dofs, aofs, oprsz, sh64, false, g->fni8); tcg_temp_free_i64(sh64); } else { - TCGv_ptr a0 = tcg_temp_new_ptr(); - TCGv_ptr a1 = tcg_temp_new_ptr(); - TCGv_i32 desc = tcg_temp_new_i32(); + TCGv_ptr a0 = tcg_temp_ebb_new_ptr(); + TCGv_ptr a1 = tcg_temp_ebb_new_ptr(); + TCGv_i32 desc = tcg_temp_ebb_new_i32(); tcg_gen_shli_i32(desc, shift, SIMD_DATA_SHIFT); tcg_gen_ori_i32(desc, desc, simd_desc(oprsz, maxsz, 0)); - tcg_gen_addi_ptr(a0, cpu_env, dofs); - tcg_gen_addi_ptr(a1, cpu_env, aofs); + tcg_gen_addi_ptr(a0, tcg_env, dofs); + tcg_gen_addi_ptr(a1, tcg_env, aofs); g->fno[vece](a0, a1, desc); @@ -3337,6 +3319,17 @@ void tcg_gen_gvec_rotls(unsigned vece, uint32_t dofs, uint32_t aofs, do_gvec_shifts(vece, dofs, aofs, shift, oprsz, maxsz, &g); } +void tcg_gen_gvec_rotrs(unsigned vece, uint32_t dofs, uint32_t aofs, + TCGv_i32 shift, uint32_t oprsz, uint32_t maxsz) +{ + TCGv_i32 tmp = tcg_temp_ebb_new_i32(); + + tcg_gen_neg_i32(tmp, shift); + tcg_gen_andi_i32(tmp, tmp, (8 << vece) - 1); + tcg_gen_gvec_rotls(vece, dofs, aofs, tmp, oprsz, maxsz); + tcg_temp_free_i32(tmp); +} + /* * Expand D = A << (B % element bits) * @@ -3360,7 +3353,7 @@ static void tcg_gen_shlv_mod_vec(unsigned vece, TCGv_vec d, static void tcg_gen_shl_mod_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { - TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_andi_i32(t, b, 31); tcg_gen_shl_i32(d, a, t); @@ -3369,7 +3362,7 @@ static void tcg_gen_shl_mod_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) static void tcg_gen_shl_mod_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) { - TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_andi_i64(t, b, 63); tcg_gen_shl_i64(d, a, t); @@ -3423,7 +3416,7 @@ static void tcg_gen_shrv_mod_vec(unsigned vece, TCGv_vec d, static void tcg_gen_shr_mod_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { - TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_andi_i32(t, b, 31); tcg_gen_shr_i32(d, a, t); @@ -3432,7 +3425,7 @@ static void tcg_gen_shr_mod_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) static void tcg_gen_shr_mod_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) { - TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_andi_i64(t, b, 63); tcg_gen_shr_i64(d, a, t); @@ -3486,7 +3479,7 @@ static void tcg_gen_sarv_mod_vec(unsigned vece, TCGv_vec d, static void tcg_gen_sar_mod_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { - TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_andi_i32(t, b, 31); tcg_gen_sar_i32(d, a, t); @@ -3495,7 +3488,7 @@ static void tcg_gen_sar_mod_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) static void tcg_gen_sar_mod_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) { - TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_andi_i64(t, b, 63); tcg_gen_sar_i64(d, a, t); @@ -3549,7 +3542,7 @@ static void tcg_gen_rotlv_mod_vec(unsigned vece, TCGv_vec d, static void tcg_gen_rotl_mod_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { - TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_andi_i32(t, b, 31); tcg_gen_rotl_i32(d, a, t); @@ -3558,7 +3551,7 @@ static void tcg_gen_rotl_mod_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) static void tcg_gen_rotl_mod_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) { - TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_andi_i64(t, b, 63); tcg_gen_rotl_i64(d, a, t); @@ -3608,7 +3601,7 @@ static void tcg_gen_rotrv_mod_vec(unsigned vece, TCGv_vec d, static void tcg_gen_rotr_mod_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { - TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_andi_i32(t, b, 31); tcg_gen_rotr_i32(d, a, t); @@ -3617,7 +3610,7 @@ static void tcg_gen_rotr_mod_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) static void tcg_gen_rotr_mod_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) { - TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_andi_i64(t, b, 63); tcg_gen_rotr_i64(d, a, t); @@ -3658,16 +3651,15 @@ void tcg_gen_gvec_rotrv(unsigned vece, uint32_t dofs, uint32_t aofs, static void expand_cmp_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, TCGCond cond) { - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); uint32_t i; for (i = 0; i < oprsz; i += 4) { - tcg_gen_ld_i32(t0, cpu_env, aofs + i); - tcg_gen_ld_i32(t1, cpu_env, bofs + i); - tcg_gen_setcond_i32(cond, t0, t0, t1); - tcg_gen_neg_i32(t0, t0); - tcg_gen_st_i32(t0, cpu_env, dofs + i); + tcg_gen_ld_i32(t0, tcg_env, aofs + i); + tcg_gen_ld_i32(t1, tcg_env, bofs + i); + tcg_gen_negsetcond_i32(cond, t0, t0, t1); + tcg_gen_st_i32(t0, tcg_env, dofs + i); } tcg_temp_free_i32(t1); tcg_temp_free_i32(t0); @@ -3676,16 +3668,15 @@ static void expand_cmp_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs, static void expand_cmp_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, TCGCond cond) { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); uint32_t i; for (i = 0; i < oprsz; i += 8) { - tcg_gen_ld_i64(t0, cpu_env, aofs + i); - tcg_gen_ld_i64(t1, cpu_env, bofs + i); - tcg_gen_setcond_i64(cond, t0, t0, t1); - tcg_gen_neg_i64(t0, t0); - tcg_gen_st_i64(t0, cpu_env, dofs + i); + tcg_gen_ld_i64(t0, tcg_env, aofs + i); + tcg_gen_ld_i64(t1, tcg_env, bofs + i); + tcg_gen_negsetcond_i64(cond, t0, t0, t1); + tcg_gen_st_i64(t0, tcg_env, dofs + i); } tcg_temp_free_i64(t1); tcg_temp_free_i64(t0); @@ -3695,18 +3686,16 @@ static void expand_cmp_vec(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t tysz, TCGType type, TCGCond cond) { - TCGv_vec t0 = tcg_temp_new_vec(type); - TCGv_vec t1 = tcg_temp_new_vec(type); - uint32_t i; + for (uint32_t i = 0; i < oprsz; i += tysz) { + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); + TCGv_vec t2 = tcg_temp_new_vec(type); - for (i = 0; i < oprsz; i += tysz) { - tcg_gen_ld_vec(t0, cpu_env, aofs + i); - tcg_gen_ld_vec(t1, cpu_env, bofs + i); - tcg_gen_cmp_vec(cond, vece, t0, t0, t1); - tcg_gen_st_vec(t0, cpu_env, dofs + i); + tcg_gen_ld_vec(t0, tcg_env, aofs + i); + tcg_gen_ld_vec(t1, tcg_env, bofs + i); + tcg_gen_cmp_vec(cond, vece, t2, t0, t1); + tcg_gen_st_vec(t2, tcg_env, dofs + i); } - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t0); } void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs, @@ -3821,9 +3810,158 @@ void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs, } } +static void expand_cmps_vec(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t tysz, TCGType type, + TCGCond cond, TCGv_vec c) +{ + TCGv_vec t0 = tcg_temp_new_vec(type); + TCGv_vec t1 = tcg_temp_new_vec(type); + uint32_t i; + + for (i = 0; i < oprsz; i += tysz) { + tcg_gen_ld_vec(t1, tcg_env, aofs + i); + tcg_gen_cmp_vec(cond, vece, t0, t1, c); + tcg_gen_st_vec(t0, tcg_env, dofs + i); + } +} + +void tcg_gen_gvec_cmps(TCGCond cond, unsigned vece, uint32_t dofs, + uint32_t aofs, TCGv_i64 c, + uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode cmp_list[] = { INDEX_op_cmp_vec, 0 }; + static gen_helper_gvec_2i * const eq_fn[4] = { + gen_helper_gvec_eqs8, gen_helper_gvec_eqs16, + gen_helper_gvec_eqs32, gen_helper_gvec_eqs64 + }; + static gen_helper_gvec_2i * const lt_fn[4] = { + gen_helper_gvec_lts8, gen_helper_gvec_lts16, + gen_helper_gvec_lts32, gen_helper_gvec_lts64 + }; + static gen_helper_gvec_2i * const le_fn[4] = { + gen_helper_gvec_les8, gen_helper_gvec_les16, + gen_helper_gvec_les32, gen_helper_gvec_les64 + }; + static gen_helper_gvec_2i * const ltu_fn[4] = { + gen_helper_gvec_ltus8, gen_helper_gvec_ltus16, + gen_helper_gvec_ltus32, gen_helper_gvec_ltus64 + }; + static gen_helper_gvec_2i * const leu_fn[4] = { + gen_helper_gvec_leus8, gen_helper_gvec_leus16, + gen_helper_gvec_leus32, gen_helper_gvec_leus64 + }; + static gen_helper_gvec_2i * const * const fns[16] = { + [TCG_COND_EQ] = eq_fn, + [TCG_COND_LT] = lt_fn, + [TCG_COND_LE] = le_fn, + [TCG_COND_LTU] = ltu_fn, + [TCG_COND_LEU] = leu_fn, + }; + + TCGType type; + + check_size_align(oprsz, maxsz, dofs | aofs); + check_overlap_2(dofs, aofs, maxsz); + + if (cond == TCG_COND_NEVER || cond == TCG_COND_ALWAYS) { + do_dup(MO_8, dofs, oprsz, maxsz, + NULL, NULL, -(cond == TCG_COND_ALWAYS)); + return; + } + + /* + * Implement inline with a vector type, if possible. + * Prefer integer when 64-bit host and 64-bit comparison. + */ + type = choose_vector_type(cmp_list, vece, oprsz, + TCG_TARGET_REG_BITS == 64 && vece == MO_64); + if (type != 0) { + const TCGOpcode *hold_list = tcg_swap_vecop_list(cmp_list); + TCGv_vec t_vec = tcg_temp_new_vec(type); + uint32_t some; + + tcg_gen_dup_i64_vec(vece, t_vec, c); + switch (type) { + case TCG_TYPE_V256: + some = QEMU_ALIGN_DOWN(oprsz, 32); + expand_cmps_vec(vece, dofs, aofs, some, 32, + TCG_TYPE_V256, cond, t_vec); + aofs += some; + dofs += some; + oprsz -= some; + maxsz -= some; + /* fallthru */ + + case TCG_TYPE_V128: + some = QEMU_ALIGN_DOWN(oprsz, 16); + expand_cmps_vec(vece, dofs, aofs, some, 16, + TCG_TYPE_V128, cond, t_vec); + break; + + case TCG_TYPE_V64: + some = QEMU_ALIGN_DOWN(oprsz, 8); + expand_cmps_vec(vece, dofs, aofs, some, 8, + TCG_TYPE_V64, cond, t_vec); + break; + + default: + g_assert_not_reached(); + } + tcg_temp_free_vec(t_vec); + tcg_swap_vecop_list(hold_list); + } else if (vece == MO_64 && check_size_impl(oprsz, 8)) { + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + uint32_t i; + + for (i = 0; i < oprsz; i += 8) { + tcg_gen_ld_i64(t0, tcg_env, aofs + i); + tcg_gen_negsetcond_i64(cond, t0, t0, c); + tcg_gen_st_i64(t0, tcg_env, dofs + i); + } + tcg_temp_free_i64(t0); + } else if (vece == MO_32 && check_size_impl(oprsz, 4)) { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + uint32_t i; + + tcg_gen_extrl_i64_i32(t1, c); + for (i = 0; i < oprsz; i += 8) { + tcg_gen_ld_i32(t0, tcg_env, aofs + i); + tcg_gen_negsetcond_i32(cond, t0, t0, t1); + tcg_gen_st_i32(t0, tcg_env, dofs + i); + } + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + } else { + gen_helper_gvec_2i * const *fn = fns[cond]; + bool inv = false; + + if (fn == NULL) { + cond = tcg_invert_cond(cond); + fn = fns[cond]; + assert(fn != NULL); + inv = true; + } + tcg_gen_gvec_2i_ool(dofs, aofs, c, oprsz, maxsz, inv, fn[vece]); + return; + } + + if (oprsz < maxsz) { + expand_clr(dofs + oprsz, maxsz - oprsz); + } +} + +void tcg_gen_gvec_cmpi(TCGCond cond, unsigned vece, uint32_t dofs, + uint32_t aofs, int64_t c, + uint32_t oprsz, uint32_t maxsz) +{ + TCGv_i64 tmp = tcg_constant_i64(c); + tcg_gen_gvec_cmps(cond, vece, dofs, aofs, tmp, oprsz, maxsz); +} + static void tcg_gen_bitsel_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b, TCGv_i64 c) { - TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_and_i64(t, b, a); tcg_gen_andc_i64(d, c, a); diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c new file mode 100644 index 0000000000..f11043b449 --- /dev/null +++ b/tcg/tcg-op-ldst.c @@ -0,0 +1,1268 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 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 "tcg/tcg.h" +#include "tcg/tcg-temp-internal.h" +#include "tcg/tcg-op-common.h" +#include "tcg/tcg-mo.h" +#include "exec/translation-block.h" +#include "exec/plugin-gen.h" +#include "tcg-internal.h" + + +static void check_max_alignment(unsigned a_bits) +{ + /* + * The requested alignment cannot overlap the TLB flags. + * FIXME: Must keep the count up-to-date with "exec/cpu-all.h". + */ + if (tcg_use_softmmu) { + tcg_debug_assert(a_bits + 5 <= tcg_ctx->page_bits); + } +} + +static MemOp tcg_canonicalize_memop(MemOp op, bool is64, bool st) +{ + unsigned a_bits = get_alignment_bits(op); + + check_max_alignment(a_bits); + + /* Prefer MO_ALIGN+MO_XX over MO_ALIGN_XX+MO_XX */ + if (a_bits == (op & MO_SIZE)) { + op = (op & ~MO_AMASK) | MO_ALIGN; + } + + switch (op & MO_SIZE) { + case MO_8: + op &= ~MO_BSWAP; + break; + case MO_16: + break; + case MO_32: + if (!is64) { + op &= ~MO_SIGN; + } + break; + case MO_64: + if (is64) { + op &= ~MO_SIGN; + break; + } + /* fall through */ + default: + g_assert_not_reached(); + } + if (st) { + op &= ~MO_SIGN; + } + + /* In serial mode, reduce atomicity. */ + if (!(tcg_ctx->gen_tb->cflags & CF_PARALLEL)) { + op &= ~MO_ATOM_MASK; + op |= MO_ATOM_NONE; + } + + return op; +} + +static void gen_ldst(TCGOpcode opc, TCGTemp *vl, TCGTemp *vh, + TCGTemp *addr, MemOpIdx oi) +{ + if (TCG_TARGET_REG_BITS == 64 || tcg_ctx->addr_type == TCG_TYPE_I32) { + if (vh) { + tcg_gen_op4(opc, temp_arg(vl), temp_arg(vh), temp_arg(addr), oi); + } else { + tcg_gen_op3(opc, temp_arg(vl), temp_arg(addr), oi); + } + } else { + /* See TCGV_LOW/HIGH. */ + TCGTemp *al = addr + HOST_BIG_ENDIAN; + TCGTemp *ah = addr + !HOST_BIG_ENDIAN; + + if (vh) { + tcg_gen_op5(opc, temp_arg(vl), temp_arg(vh), + temp_arg(al), temp_arg(ah), oi); + } else { + tcg_gen_op4(opc, temp_arg(vl), temp_arg(al), temp_arg(ah), oi); + } + } +} + +static void gen_ldst_i64(TCGOpcode opc, TCGv_i64 v, TCGTemp *addr, MemOpIdx oi) +{ + if (TCG_TARGET_REG_BITS == 32) { + TCGTemp *vl = tcgv_i32_temp(TCGV_LOW(v)); + TCGTemp *vh = tcgv_i32_temp(TCGV_HIGH(v)); + gen_ldst(opc, vl, vh, addr, oi); + } else { + gen_ldst(opc, tcgv_i64_temp(v), NULL, addr, oi); + } +} + +static void tcg_gen_req_mo(TCGBar type) +{ + type &= tcg_ctx->guest_mo; + type &= ~TCG_TARGET_DEFAULT_MO; + if (type) { + tcg_gen_mb(type | TCG_BAR_SC); + } +} + +/* Only required for loads, where value might overlap addr. */ +static TCGv_i64 plugin_maybe_preserve_addr(TCGTemp *addr) +{ +#ifdef CONFIG_PLUGIN + if (tcg_ctx->plugin_insn != NULL) { + /* Save a copy of the vaddr for use after a load. */ + TCGv_i64 temp = tcg_temp_ebb_new_i64(); + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + tcg_gen_extu_i32_i64(temp, temp_tcgv_i32(addr)); + } else { + tcg_gen_mov_i64(temp, temp_tcgv_i64(addr)); + } + return temp; + } +#endif + return NULL; +} + +static void +plugin_gen_mem_callbacks(TCGv_i64 copy_addr, TCGTemp *orig_addr, MemOpIdx oi, + enum qemu_plugin_mem_rw rw) +{ +#ifdef CONFIG_PLUGIN + if (tcg_ctx->plugin_insn != NULL) { + qemu_plugin_meminfo_t info = make_plugin_meminfo(oi, rw); + + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + if (!copy_addr) { + copy_addr = tcg_temp_ebb_new_i64(); + tcg_gen_extu_i32_i64(copy_addr, temp_tcgv_i32(orig_addr)); + } + plugin_gen_empty_mem_callback(copy_addr, info); + tcg_temp_free_i64(copy_addr); + } else { + if (copy_addr) { + plugin_gen_empty_mem_callback(copy_addr, info); + tcg_temp_free_i64(copy_addr); + } else { + plugin_gen_empty_mem_callback(temp_tcgv_i64(orig_addr), info); + } + } + } +#endif +} + +static void tcg_gen_qemu_ld_i32_int(TCGv_i32 val, TCGTemp *addr, + TCGArg idx, MemOp memop) +{ + MemOp orig_memop; + MemOpIdx orig_oi, oi; + TCGv_i64 copy_addr; + TCGOpcode opc; + + tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + orig_memop = memop = tcg_canonicalize_memop(memop, 0, 0); + orig_oi = oi = make_memop_idx(memop, idx); + + if ((memop & MO_BSWAP) && !tcg_target_has_memory_bswap(memop)) { + memop &= ~MO_BSWAP; + /* The bswap primitive benefits from zero-extended input. */ + if ((memop & MO_SSIZE) == MO_SW) { + memop &= ~MO_SIGN; + } + oi = make_memop_idx(memop, idx); + } + + copy_addr = plugin_maybe_preserve_addr(addr); + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + opc = INDEX_op_qemu_ld_a32_i32; + } else { + opc = INDEX_op_qemu_ld_a64_i32; + } + gen_ldst(opc, tcgv_i32_temp(val), NULL, addr, oi); + plugin_gen_mem_callbacks(copy_addr, addr, orig_oi, QEMU_PLUGIN_MEM_R); + + if ((orig_memop ^ memop) & MO_BSWAP) { + switch (orig_memop & MO_SIZE) { + case MO_16: + tcg_gen_bswap16_i32(val, val, (orig_memop & MO_SIGN + ? TCG_BSWAP_IZ | TCG_BSWAP_OS + : TCG_BSWAP_IZ | TCG_BSWAP_OZ)); + break; + case MO_32: + tcg_gen_bswap32_i32(val, val); + break; + default: + g_assert_not_reached(); + } + } +} + +void tcg_gen_qemu_ld_i32_chk(TCGv_i32 val, TCGTemp *addr, TCGArg idx, + MemOp memop, TCGType addr_type) +{ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); + tcg_debug_assert((memop & MO_SIZE) <= MO_32); + tcg_gen_qemu_ld_i32_int(val, addr, idx, memop); +} + +static void tcg_gen_qemu_st_i32_int(TCGv_i32 val, TCGTemp *addr, + TCGArg idx, MemOp memop) +{ + TCGv_i32 swap = NULL; + MemOpIdx orig_oi, oi; + TCGOpcode opc; + + tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + memop = tcg_canonicalize_memop(memop, 0, 1); + orig_oi = oi = make_memop_idx(memop, idx); + + if ((memop & MO_BSWAP) && !tcg_target_has_memory_bswap(memop)) { + swap = tcg_temp_ebb_new_i32(); + switch (memop & MO_SIZE) { + case MO_16: + tcg_gen_bswap16_i32(swap, val, 0); + break; + case MO_32: + tcg_gen_bswap32_i32(swap, val); + break; + default: + g_assert_not_reached(); + } + val = swap; + memop &= ~MO_BSWAP; + oi = make_memop_idx(memop, idx); + } + + if (TCG_TARGET_HAS_qemu_st8_i32 && (memop & MO_SIZE) == MO_8) { + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + opc = INDEX_op_qemu_st8_a32_i32; + } else { + opc = INDEX_op_qemu_st8_a64_i32; + } + } else { + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + opc = INDEX_op_qemu_st_a32_i32; + } else { + opc = INDEX_op_qemu_st_a64_i32; + } + } + gen_ldst(opc, tcgv_i32_temp(val), NULL, addr, oi); + plugin_gen_mem_callbacks(NULL, addr, orig_oi, QEMU_PLUGIN_MEM_W); + + if (swap) { + tcg_temp_free_i32(swap); + } +} + +void tcg_gen_qemu_st_i32_chk(TCGv_i32 val, TCGTemp *addr, TCGArg idx, + MemOp memop, TCGType addr_type) +{ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); + tcg_debug_assert((memop & MO_SIZE) <= MO_32); + tcg_gen_qemu_st_i32_int(val, addr, idx, memop); +} + +static void tcg_gen_qemu_ld_i64_int(TCGv_i64 val, TCGTemp *addr, + TCGArg idx, MemOp memop) +{ + MemOp orig_memop; + MemOpIdx orig_oi, oi; + TCGv_i64 copy_addr; + TCGOpcode opc; + + if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { + tcg_gen_qemu_ld_i32_int(TCGV_LOW(val), addr, idx, memop); + if (memop & MO_SIGN) { + tcg_gen_sari_i32(TCGV_HIGH(val), TCGV_LOW(val), 31); + } else { + tcg_gen_movi_i32(TCGV_HIGH(val), 0); + } + return; + } + + tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + orig_memop = memop = tcg_canonicalize_memop(memop, 1, 0); + orig_oi = oi = make_memop_idx(memop, idx); + + if ((memop & MO_BSWAP) && !tcg_target_has_memory_bswap(memop)) { + memop &= ~MO_BSWAP; + /* The bswap primitive benefits from zero-extended input. */ + if ((memop & MO_SIGN) && (memop & MO_SIZE) < MO_64) { + memop &= ~MO_SIGN; + } + oi = make_memop_idx(memop, idx); + } + + copy_addr = plugin_maybe_preserve_addr(addr); + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + opc = INDEX_op_qemu_ld_a32_i64; + } else { + opc = INDEX_op_qemu_ld_a64_i64; + } + gen_ldst_i64(opc, val, addr, oi); + plugin_gen_mem_callbacks(copy_addr, addr, orig_oi, QEMU_PLUGIN_MEM_R); + + if ((orig_memop ^ memop) & MO_BSWAP) { + int flags = (orig_memop & MO_SIGN + ? TCG_BSWAP_IZ | TCG_BSWAP_OS + : TCG_BSWAP_IZ | TCG_BSWAP_OZ); + switch (orig_memop & MO_SIZE) { + case MO_16: + tcg_gen_bswap16_i64(val, val, flags); + break; + case MO_32: + tcg_gen_bswap32_i64(val, val, flags); + break; + case MO_64: + tcg_gen_bswap64_i64(val, val); + break; + default: + g_assert_not_reached(); + } + } +} + +void tcg_gen_qemu_ld_i64_chk(TCGv_i64 val, TCGTemp *addr, TCGArg idx, + MemOp memop, TCGType addr_type) +{ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); + tcg_debug_assert((memop & MO_SIZE) <= MO_64); + tcg_gen_qemu_ld_i64_int(val, addr, idx, memop); +} + +static void tcg_gen_qemu_st_i64_int(TCGv_i64 val, TCGTemp *addr, + TCGArg idx, MemOp memop) +{ + TCGv_i64 swap = NULL; + MemOpIdx orig_oi, oi; + TCGOpcode opc; + + if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { + tcg_gen_qemu_st_i32_int(TCGV_LOW(val), addr, idx, memop); + return; + } + + tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + memop = tcg_canonicalize_memop(memop, 1, 1); + orig_oi = oi = make_memop_idx(memop, idx); + + if ((memop & MO_BSWAP) && !tcg_target_has_memory_bswap(memop)) { + swap = tcg_temp_ebb_new_i64(); + switch (memop & MO_SIZE) { + case MO_16: + tcg_gen_bswap16_i64(swap, val, 0); + break; + case MO_32: + tcg_gen_bswap32_i64(swap, val, 0); + break; + case MO_64: + tcg_gen_bswap64_i64(swap, val); + break; + default: + g_assert_not_reached(); + } + val = swap; + memop &= ~MO_BSWAP; + oi = make_memop_idx(memop, idx); + } + + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + opc = INDEX_op_qemu_st_a32_i64; + } else { + opc = INDEX_op_qemu_st_a64_i64; + } + gen_ldst_i64(opc, val, addr, oi); + plugin_gen_mem_callbacks(NULL, addr, orig_oi, QEMU_PLUGIN_MEM_W); + + if (swap) { + tcg_temp_free_i64(swap); + } +} + +void tcg_gen_qemu_st_i64_chk(TCGv_i64 val, TCGTemp *addr, TCGArg idx, + MemOp memop, TCGType addr_type) +{ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); + tcg_debug_assert((memop & MO_SIZE) <= MO_64); + tcg_gen_qemu_st_i64_int(val, addr, idx, memop); +} + +/* + * Return true if @mop, without knowledge of the pointer alignment, + * does not require 16-byte atomicity, and it would be adventagous + * to avoid a call to a helper function. + */ +static bool use_two_i64_for_i128(MemOp mop) +{ + /* Two softmmu tlb lookups is larger than one function call. */ + if (tcg_use_softmmu) { + return false; + } + + /* + * For user-only, two 64-bit operations may well be smaller than a call. + * Determine if that would be legal for the requested atomicity. + */ + switch (mop & MO_ATOM_MASK) { + case MO_ATOM_NONE: + case MO_ATOM_IFALIGN_PAIR: + return true; + case MO_ATOM_IFALIGN: + case MO_ATOM_SUBALIGN: + case MO_ATOM_WITHIN16: + case MO_ATOM_WITHIN16_PAIR: + return false; + default: + g_assert_not_reached(); + } +} + +static void canonicalize_memop_i128_as_i64(MemOp ret[2], MemOp orig) +{ + MemOp mop_1 = orig, mop_2; + + /* Reduce the size to 64-bit. */ + mop_1 = (mop_1 & ~MO_SIZE) | MO_64; + + /* Retain the alignment constraints of the original. */ + switch (orig & MO_AMASK) { + case MO_UNALN: + case MO_ALIGN_2: + case MO_ALIGN_4: + mop_2 = mop_1; + break; + case MO_ALIGN_8: + /* Prefer MO_ALIGN+MO_64 to MO_ALIGN_8+MO_64. */ + mop_1 = (mop_1 & ~MO_AMASK) | MO_ALIGN; + mop_2 = mop_1; + break; + case MO_ALIGN: + /* Second has 8-byte alignment; first has 16-byte alignment. */ + mop_2 = mop_1; + mop_1 = (mop_1 & ~MO_AMASK) | MO_ALIGN_16; + break; + case MO_ALIGN_16: + case MO_ALIGN_32: + case MO_ALIGN_64: + /* Second has 8-byte alignment; first retains original. */ + mop_2 = (mop_1 & ~MO_AMASK) | MO_ALIGN; + break; + default: + g_assert_not_reached(); + } + + /* Use a memory ordering implemented by the host. */ + if ((orig & MO_BSWAP) && !tcg_target_has_memory_bswap(mop_1)) { + mop_1 &= ~MO_BSWAP; + mop_2 &= ~MO_BSWAP; + } + + ret[0] = mop_1; + ret[1] = mop_2; +} + +static TCGv_i64 maybe_extend_addr64(TCGTemp *addr) +{ + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + TCGv_i64 a64 = tcg_temp_ebb_new_i64(); + tcg_gen_extu_i32_i64(a64, temp_tcgv_i32(addr)); + return a64; + } + return temp_tcgv_i64(addr); +} + +static void maybe_free_addr64(TCGv_i64 a64) +{ + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + tcg_temp_free_i64(a64); + } +} + +static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, + TCGArg idx, MemOp memop) +{ + MemOpIdx orig_oi; + TCGv_i64 ext_addr = NULL; + TCGOpcode opc; + + check_max_alignment(get_alignment_bits(memop)); + tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + + /* In serial mode, reduce atomicity. */ + if (!(tcg_ctx->gen_tb->cflags & CF_PARALLEL)) { + memop &= ~MO_ATOM_MASK; + memop |= MO_ATOM_NONE; + } + orig_oi = make_memop_idx(memop, idx); + + /* TODO: For now, force 32-bit hosts to use the helper. */ + if (TCG_TARGET_HAS_qemu_ldst_i128 && TCG_TARGET_REG_BITS == 64) { + TCGv_i64 lo, hi; + bool need_bswap = false; + MemOpIdx oi = orig_oi; + + if ((memop & MO_BSWAP) && !tcg_target_has_memory_bswap(memop)) { + lo = TCGV128_HIGH(val); + hi = TCGV128_LOW(val); + oi = make_memop_idx(memop & ~MO_BSWAP, idx); + need_bswap = true; + } else { + lo = TCGV128_LOW(val); + hi = TCGV128_HIGH(val); + } + + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + opc = INDEX_op_qemu_ld_a32_i128; + } else { + opc = INDEX_op_qemu_ld_a64_i128; + } + gen_ldst(opc, tcgv_i64_temp(lo), tcgv_i64_temp(hi), addr, oi); + + if (need_bswap) { + tcg_gen_bswap64_i64(lo, lo); + tcg_gen_bswap64_i64(hi, hi); + } + } else if (use_two_i64_for_i128(memop)) { + MemOp mop[2]; + TCGTemp *addr_p8; + TCGv_i64 x, y; + bool need_bswap; + + canonicalize_memop_i128_as_i64(mop, memop); + need_bswap = (mop[0] ^ memop) & MO_BSWAP; + + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + opc = INDEX_op_qemu_ld_a32_i64; + } else { + opc = INDEX_op_qemu_ld_a64_i64; + } + + /* + * Since there are no global TCGv_i128, there is no visible state + * changed if the second load faults. Load directly into the two + * subwords. + */ + if ((memop & MO_BSWAP) == MO_LE) { + x = TCGV128_LOW(val); + y = TCGV128_HIGH(val); + } else { + x = TCGV128_HIGH(val); + y = TCGV128_LOW(val); + } + + gen_ldst_i64(opc, x, addr, make_memop_idx(mop[0], idx)); + + if (need_bswap) { + tcg_gen_bswap64_i64(x, x); + } + + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + TCGv_i32 t = tcg_temp_ebb_new_i32(); + tcg_gen_addi_i32(t, temp_tcgv_i32(addr), 8); + addr_p8 = tcgv_i32_temp(t); + } else { + TCGv_i64 t = tcg_temp_ebb_new_i64(); + tcg_gen_addi_i64(t, temp_tcgv_i64(addr), 8); + addr_p8 = tcgv_i64_temp(t); + } + + gen_ldst_i64(opc, y, addr_p8, make_memop_idx(mop[1], idx)); + tcg_temp_free_internal(addr_p8); + + if (need_bswap) { + tcg_gen_bswap64_i64(y, y); + } + } else { + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + ext_addr = tcg_temp_ebb_new_i64(); + tcg_gen_extu_i32_i64(ext_addr, temp_tcgv_i32(addr)); + addr = tcgv_i64_temp(ext_addr); + } + gen_helper_ld_i128(val, tcg_env, temp_tcgv_i64(addr), + tcg_constant_i32(orig_oi)); + } + + plugin_gen_mem_callbacks(ext_addr, addr, orig_oi, QEMU_PLUGIN_MEM_R); +} + +void tcg_gen_qemu_ld_i128_chk(TCGv_i128 val, TCGTemp *addr, TCGArg idx, + MemOp memop, TCGType addr_type) +{ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); + tcg_debug_assert((memop & MO_SIZE) == MO_128); + tcg_debug_assert((memop & MO_SIGN) == 0); + tcg_gen_qemu_ld_i128_int(val, addr, idx, memop); +} + +static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, + TCGArg idx, MemOp memop) +{ + MemOpIdx orig_oi; + TCGv_i64 ext_addr = NULL; + TCGOpcode opc; + + check_max_alignment(get_alignment_bits(memop)); + tcg_gen_req_mo(TCG_MO_ST_LD | TCG_MO_ST_ST); + + /* In serial mode, reduce atomicity. */ + if (!(tcg_ctx->gen_tb->cflags & CF_PARALLEL)) { + memop &= ~MO_ATOM_MASK; + memop |= MO_ATOM_NONE; + } + orig_oi = make_memop_idx(memop, idx); + + /* TODO: For now, force 32-bit hosts to use the helper. */ + + if (TCG_TARGET_HAS_qemu_ldst_i128 && TCG_TARGET_REG_BITS == 64) { + TCGv_i64 lo, hi; + MemOpIdx oi = orig_oi; + bool need_bswap = false; + + if ((memop & MO_BSWAP) && !tcg_target_has_memory_bswap(memop)) { + lo = tcg_temp_ebb_new_i64(); + hi = tcg_temp_ebb_new_i64(); + tcg_gen_bswap64_i64(lo, TCGV128_HIGH(val)); + tcg_gen_bswap64_i64(hi, TCGV128_LOW(val)); + oi = make_memop_idx(memop & ~MO_BSWAP, idx); + need_bswap = true; + } else { + lo = TCGV128_LOW(val); + hi = TCGV128_HIGH(val); + } + + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + opc = INDEX_op_qemu_st_a32_i128; + } else { + opc = INDEX_op_qemu_st_a64_i128; + } + gen_ldst(opc, tcgv_i64_temp(lo), tcgv_i64_temp(hi), addr, oi); + + if (need_bswap) { + tcg_temp_free_i64(lo); + tcg_temp_free_i64(hi); + } + } else if (use_two_i64_for_i128(memop)) { + MemOp mop[2]; + TCGTemp *addr_p8; + TCGv_i64 x, y, b = NULL; + + canonicalize_memop_i128_as_i64(mop, memop); + + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + opc = INDEX_op_qemu_st_a32_i64; + } else { + opc = INDEX_op_qemu_st_a64_i64; + } + + if ((memop & MO_BSWAP) == MO_LE) { + x = TCGV128_LOW(val); + y = TCGV128_HIGH(val); + } else { + x = TCGV128_HIGH(val); + y = TCGV128_LOW(val); + } + + if ((mop[0] ^ memop) & MO_BSWAP) { + b = tcg_temp_ebb_new_i64(); + tcg_gen_bswap64_i64(b, x); + x = b; + } + + gen_ldst_i64(opc, x, addr, make_memop_idx(mop[0], idx)); + + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + TCGv_i32 t = tcg_temp_ebb_new_i32(); + tcg_gen_addi_i32(t, temp_tcgv_i32(addr), 8); + addr_p8 = tcgv_i32_temp(t); + } else { + TCGv_i64 t = tcg_temp_ebb_new_i64(); + tcg_gen_addi_i64(t, temp_tcgv_i64(addr), 8); + addr_p8 = tcgv_i64_temp(t); + } + + if (b) { + tcg_gen_bswap64_i64(b, y); + gen_ldst_i64(opc, b, addr_p8, make_memop_idx(mop[1], idx)); + tcg_temp_free_i64(b); + } else { + gen_ldst_i64(opc, y, addr_p8, make_memop_idx(mop[1], idx)); + } + tcg_temp_free_internal(addr_p8); + } else { + if (tcg_ctx->addr_type == TCG_TYPE_I32) { + ext_addr = tcg_temp_ebb_new_i64(); + tcg_gen_extu_i32_i64(ext_addr, temp_tcgv_i32(addr)); + addr = tcgv_i64_temp(ext_addr); + } + gen_helper_st_i128(tcg_env, temp_tcgv_i64(addr), val, + tcg_constant_i32(orig_oi)); + } + + plugin_gen_mem_callbacks(ext_addr, addr, orig_oi, QEMU_PLUGIN_MEM_W); +} + +void tcg_gen_qemu_st_i128_chk(TCGv_i128 val, TCGTemp *addr, TCGArg idx, + MemOp memop, TCGType addr_type) +{ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); + tcg_debug_assert((memop & MO_SIZE) == MO_128); + tcg_debug_assert((memop & MO_SIGN) == 0); + tcg_gen_qemu_st_i128_int(val, addr, idx, memop); +} + +void tcg_gen_ext_i32(TCGv_i32 ret, TCGv_i32 val, MemOp opc) +{ + switch (opc & MO_SSIZE) { + case MO_SB: + tcg_gen_ext8s_i32(ret, val); + break; + case MO_UB: + tcg_gen_ext8u_i32(ret, val); + break; + case MO_SW: + tcg_gen_ext16s_i32(ret, val); + break; + case MO_UW: + tcg_gen_ext16u_i32(ret, val); + break; + case MO_UL: + case MO_SL: + tcg_gen_mov_i32(ret, val); + break; + default: + g_assert_not_reached(); + } +} + +void tcg_gen_ext_i64(TCGv_i64 ret, TCGv_i64 val, MemOp opc) +{ + switch (opc & MO_SSIZE) { + case MO_SB: + tcg_gen_ext8s_i64(ret, val); + break; + case MO_UB: + tcg_gen_ext8u_i64(ret, val); + break; + case MO_SW: + tcg_gen_ext16s_i64(ret, val); + break; + case MO_UW: + tcg_gen_ext16u_i64(ret, val); + break; + case MO_SL: + tcg_gen_ext32s_i64(ret, val); + break; + case MO_UL: + tcg_gen_ext32u_i64(ret, val); + break; + case MO_UQ: + case MO_SQ: + tcg_gen_mov_i64(ret, val); + break; + default: + g_assert_not_reached(); + } +} + +typedef void (*gen_atomic_cx_i32)(TCGv_i32, TCGv_env, TCGv_i64, + TCGv_i32, TCGv_i32, TCGv_i32); +typedef void (*gen_atomic_cx_i64)(TCGv_i64, TCGv_env, TCGv_i64, + TCGv_i64, TCGv_i64, TCGv_i32); +typedef void (*gen_atomic_cx_i128)(TCGv_i128, TCGv_env, TCGv_i64, + TCGv_i128, TCGv_i128, TCGv_i32); +typedef void (*gen_atomic_op_i32)(TCGv_i32, TCGv_env, TCGv_i64, + TCGv_i32, TCGv_i32); +typedef void (*gen_atomic_op_i64)(TCGv_i64, TCGv_env, TCGv_i64, + TCGv_i64, TCGv_i32); + +#ifdef CONFIG_ATOMIC64 +# define WITH_ATOMIC64(X) X, +#else +# define WITH_ATOMIC64(X) +#endif +#if HAVE_CMPXCHG128 +# define WITH_ATOMIC128(X) X, +#else +# define WITH_ATOMIC128(X) +#endif + +static void * const table_cmpxchg[(MO_SIZE | MO_BSWAP) + 1] = { + [MO_8] = gen_helper_atomic_cmpxchgb, + [MO_16 | MO_LE] = gen_helper_atomic_cmpxchgw_le, + [MO_16 | MO_BE] = gen_helper_atomic_cmpxchgw_be, + [MO_32 | MO_LE] = gen_helper_atomic_cmpxchgl_le, + [MO_32 | MO_BE] = gen_helper_atomic_cmpxchgl_be, + WITH_ATOMIC64([MO_64 | MO_LE] = gen_helper_atomic_cmpxchgq_le) + WITH_ATOMIC64([MO_64 | MO_BE] = gen_helper_atomic_cmpxchgq_be) + WITH_ATOMIC128([MO_128 | MO_LE] = gen_helper_atomic_cmpxchgo_le) + WITH_ATOMIC128([MO_128 | MO_BE] = gen_helper_atomic_cmpxchgo_be) +}; + +static void tcg_gen_nonatomic_cmpxchg_i32_int(TCGv_i32 retv, TCGTemp *addr, + TCGv_i32 cmpv, TCGv_i32 newv, + TCGArg idx, MemOp memop) +{ + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 t2 = tcg_temp_ebb_new_i32(); + + tcg_gen_ext_i32(t2, cmpv, memop & MO_SIZE); + + tcg_gen_qemu_ld_i32_int(t1, addr, idx, memop & ~MO_SIGN); + tcg_gen_movcond_i32(TCG_COND_EQ, t2, t1, t2, newv, t1); + tcg_gen_qemu_st_i32_int(t2, addr, idx, memop); + tcg_temp_free_i32(t2); + + if (memop & MO_SIGN) { + tcg_gen_ext_i32(retv, t1, memop); + } else { + tcg_gen_mov_i32(retv, t1); + } + tcg_temp_free_i32(t1); +} + +void tcg_gen_nonatomic_cmpxchg_i32_chk(TCGv_i32 retv, TCGTemp *addr, + TCGv_i32 cmpv, TCGv_i32 newv, + TCGArg idx, MemOp memop, + TCGType addr_type) +{ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); + tcg_debug_assert((memop & MO_SIZE) <= MO_32); + tcg_gen_nonatomic_cmpxchg_i32_int(retv, addr, cmpv, newv, idx, memop); +} + +static void tcg_gen_atomic_cmpxchg_i32_int(TCGv_i32 retv, TCGTemp *addr, + TCGv_i32 cmpv, TCGv_i32 newv, + TCGArg idx, MemOp memop) +{ + gen_atomic_cx_i32 gen; + TCGv_i64 a64; + MemOpIdx oi; + + if (!(tcg_ctx->gen_tb->cflags & CF_PARALLEL)) { + tcg_gen_nonatomic_cmpxchg_i32_int(retv, addr, cmpv, newv, idx, memop); + return; + } + + memop = tcg_canonicalize_memop(memop, 0, 0); + gen = table_cmpxchg[memop & (MO_SIZE | MO_BSWAP)]; + tcg_debug_assert(gen != NULL); + + oi = make_memop_idx(memop & ~MO_SIGN, idx); + a64 = maybe_extend_addr64(addr); + gen(retv, tcg_env, a64, cmpv, newv, tcg_constant_i32(oi)); + maybe_free_addr64(a64); + + if (memop & MO_SIGN) { + tcg_gen_ext_i32(retv, retv, memop); + } +} + +void tcg_gen_atomic_cmpxchg_i32_chk(TCGv_i32 retv, TCGTemp *addr, + TCGv_i32 cmpv, TCGv_i32 newv, + TCGArg idx, MemOp memop, + TCGType addr_type) +{ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); + tcg_debug_assert((memop & MO_SIZE) <= MO_32); + tcg_gen_atomic_cmpxchg_i32_int(retv, addr, cmpv, newv, idx, memop); +} + +static void tcg_gen_nonatomic_cmpxchg_i64_int(TCGv_i64 retv, TCGTemp *addr, + TCGv_i64 cmpv, TCGv_i64 newv, + TCGArg idx, MemOp memop) +{ + TCGv_i64 t1, t2; + + if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { + tcg_gen_nonatomic_cmpxchg_i32_int(TCGV_LOW(retv), addr, TCGV_LOW(cmpv), + TCGV_LOW(newv), idx, memop); + if (memop & MO_SIGN) { + tcg_gen_sari_i32(TCGV_HIGH(retv), TCGV_LOW(retv), 31); + } else { + tcg_gen_movi_i32(TCGV_HIGH(retv), 0); + } + return; + } + + t1 = tcg_temp_ebb_new_i64(); + t2 = tcg_temp_ebb_new_i64(); + + tcg_gen_ext_i64(t2, cmpv, memop & MO_SIZE); + + tcg_gen_qemu_ld_i64_int(t1, addr, idx, memop & ~MO_SIGN); + tcg_gen_movcond_i64(TCG_COND_EQ, t2, t1, t2, newv, t1); + tcg_gen_qemu_st_i64_int(t2, addr, idx, memop); + tcg_temp_free_i64(t2); + + if (memop & MO_SIGN) { + tcg_gen_ext_i64(retv, t1, memop); + } else { + tcg_gen_mov_i64(retv, t1); + } + tcg_temp_free_i64(t1); +} + +void tcg_gen_nonatomic_cmpxchg_i64_chk(TCGv_i64 retv, TCGTemp *addr, + TCGv_i64 cmpv, TCGv_i64 newv, + TCGArg idx, MemOp memop, + TCGType addr_type) +{ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); + tcg_debug_assert((memop & MO_SIZE) <= MO_64); + tcg_gen_nonatomic_cmpxchg_i64_int(retv, addr, cmpv, newv, idx, memop); +} + +static void tcg_gen_atomic_cmpxchg_i64_int(TCGv_i64 retv, TCGTemp *addr, + TCGv_i64 cmpv, TCGv_i64 newv, + TCGArg idx, MemOp memop) +{ + if (!(tcg_ctx->gen_tb->cflags & CF_PARALLEL)) { + tcg_gen_nonatomic_cmpxchg_i64_int(retv, addr, cmpv, newv, idx, memop); + return; + } + + if ((memop & MO_SIZE) == MO_64) { + gen_atomic_cx_i64 gen; + + memop = tcg_canonicalize_memop(memop, 1, 0); + gen = table_cmpxchg[memop & (MO_SIZE | MO_BSWAP)]; + if (gen) { + MemOpIdx oi = make_memop_idx(memop, idx); + TCGv_i64 a64 = maybe_extend_addr64(addr); + gen(retv, tcg_env, a64, cmpv, newv, tcg_constant_i32(oi)); + maybe_free_addr64(a64); + return; + } + + gen_helper_exit_atomic(tcg_env); + + /* + * Produce a result for a well-formed opcode stream. This satisfies + * liveness for set before used, which happens before this dead code + * is removed. + */ + tcg_gen_movi_i64(retv, 0); + return; + } + + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_atomic_cmpxchg_i32_int(TCGV_LOW(retv), addr, TCGV_LOW(cmpv), + TCGV_LOW(newv), idx, memop); + if (memop & MO_SIGN) { + tcg_gen_sari_i32(TCGV_HIGH(retv), TCGV_LOW(retv), 31); + } else { + tcg_gen_movi_i32(TCGV_HIGH(retv), 0); + } + } else { + TCGv_i32 c32 = tcg_temp_ebb_new_i32(); + TCGv_i32 n32 = tcg_temp_ebb_new_i32(); + TCGv_i32 r32 = tcg_temp_ebb_new_i32(); + + tcg_gen_extrl_i64_i32(c32, cmpv); + tcg_gen_extrl_i64_i32(n32, newv); + tcg_gen_atomic_cmpxchg_i32_int(r32, addr, c32, n32, + idx, memop & ~MO_SIGN); + tcg_temp_free_i32(c32); + tcg_temp_free_i32(n32); + + tcg_gen_extu_i32_i64(retv, r32); + tcg_temp_free_i32(r32); + + if (memop & MO_SIGN) { + tcg_gen_ext_i64(retv, retv, memop); + } + } +} + +void tcg_gen_atomic_cmpxchg_i64_chk(TCGv_i64 retv, TCGTemp *addr, + TCGv_i64 cmpv, TCGv_i64 newv, + TCGArg idx, MemOp memop, TCGType addr_type) +{ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); + tcg_debug_assert((memop & MO_SIZE) <= MO_64); + tcg_gen_atomic_cmpxchg_i64_int(retv, addr, cmpv, newv, idx, memop); +} + +static void tcg_gen_nonatomic_cmpxchg_i128_int(TCGv_i128 retv, TCGTemp *addr, + TCGv_i128 cmpv, TCGv_i128 newv, + TCGArg idx, MemOp memop) +{ + if (TCG_TARGET_REG_BITS == 32) { + /* Inline expansion below is simply too large for 32-bit hosts. */ + MemOpIdx oi = make_memop_idx(memop, idx); + TCGv_i64 a64 = maybe_extend_addr64(addr); + + gen_helper_nonatomic_cmpxchgo(retv, tcg_env, a64, cmpv, newv, + tcg_constant_i32(oi)); + maybe_free_addr64(a64); + } else { + TCGv_i128 oldv = tcg_temp_ebb_new_i128(); + TCGv_i128 tmpv = tcg_temp_ebb_new_i128(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 z = tcg_constant_i64(0); + + tcg_gen_qemu_ld_i128_int(oldv, addr, idx, memop); + + /* Compare i128 */ + tcg_gen_xor_i64(t0, TCGV128_LOW(oldv), TCGV128_LOW(cmpv)); + tcg_gen_xor_i64(t1, TCGV128_HIGH(oldv), TCGV128_HIGH(cmpv)); + tcg_gen_or_i64(t0, t0, t1); + + /* tmpv = equal ? newv : oldv */ + tcg_gen_movcond_i64(TCG_COND_EQ, TCGV128_LOW(tmpv), t0, z, + TCGV128_LOW(newv), TCGV128_LOW(oldv)); + tcg_gen_movcond_i64(TCG_COND_EQ, TCGV128_HIGH(tmpv), t0, z, + TCGV128_HIGH(newv), TCGV128_HIGH(oldv)); + + /* Unconditional writeback. */ + tcg_gen_qemu_st_i128_int(tmpv, addr, idx, memop); + tcg_gen_mov_i128(retv, oldv); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i128(tmpv); + tcg_temp_free_i128(oldv); + } +} + +void tcg_gen_nonatomic_cmpxchg_i128_chk(TCGv_i128 retv, TCGTemp *addr, + TCGv_i128 cmpv, TCGv_i128 newv, + TCGArg idx, MemOp memop, + TCGType addr_type) +{ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); + tcg_debug_assert((memop & (MO_SIZE | MO_SIGN)) == MO_128); + tcg_gen_nonatomic_cmpxchg_i128_int(retv, addr, cmpv, newv, idx, memop); +} + +static void tcg_gen_atomic_cmpxchg_i128_int(TCGv_i128 retv, TCGTemp *addr, + TCGv_i128 cmpv, TCGv_i128 newv, + TCGArg idx, MemOp memop) +{ + gen_atomic_cx_i128 gen; + + if (!(tcg_ctx->gen_tb->cflags & CF_PARALLEL)) { + tcg_gen_nonatomic_cmpxchg_i128_int(retv, addr, cmpv, newv, idx, memop); + return; + } + + gen = table_cmpxchg[memop & (MO_SIZE | MO_BSWAP)]; + if (gen) { + MemOpIdx oi = make_memop_idx(memop, idx); + TCGv_i64 a64 = maybe_extend_addr64(addr); + gen(retv, tcg_env, a64, cmpv, newv, tcg_constant_i32(oi)); + maybe_free_addr64(a64); + return; + } + + gen_helper_exit_atomic(tcg_env); + + /* + * Produce a result for a well-formed opcode stream. This satisfies + * liveness for set before used, which happens before this dead code + * is removed. + */ + tcg_gen_movi_i64(TCGV128_LOW(retv), 0); + tcg_gen_movi_i64(TCGV128_HIGH(retv), 0); +} + +void tcg_gen_atomic_cmpxchg_i128_chk(TCGv_i128 retv, TCGTemp *addr, + TCGv_i128 cmpv, TCGv_i128 newv, + TCGArg idx, MemOp memop, + TCGType addr_type) +{ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); + tcg_debug_assert((memop & (MO_SIZE | MO_SIGN)) == MO_128); + tcg_gen_atomic_cmpxchg_i128_int(retv, addr, cmpv, newv, idx, memop); +} + +static void do_nonatomic_op_i32(TCGv_i32 ret, TCGTemp *addr, TCGv_i32 val, + TCGArg idx, MemOp memop, bool new_val, + void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 t2 = tcg_temp_ebb_new_i32(); + + memop = tcg_canonicalize_memop(memop, 0, 0); + + tcg_gen_qemu_ld_i32_int(t1, addr, idx, memop); + tcg_gen_ext_i32(t2, val, memop); + gen(t2, t1, t2); + tcg_gen_qemu_st_i32_int(t2, addr, idx, memop); + + tcg_gen_ext_i32(ret, (new_val ? t2 : t1), memop); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); +} + +static void do_atomic_op_i32(TCGv_i32 ret, TCGTemp *addr, TCGv_i32 val, + TCGArg idx, MemOp memop, void * const table[]) +{ + gen_atomic_op_i32 gen; + TCGv_i64 a64; + MemOpIdx oi; + + memop = tcg_canonicalize_memop(memop, 0, 0); + + gen = table[memop & (MO_SIZE | MO_BSWAP)]; + tcg_debug_assert(gen != NULL); + + oi = make_memop_idx(memop & ~MO_SIGN, idx); + a64 = maybe_extend_addr64(addr); + gen(ret, tcg_env, a64, val, tcg_constant_i32(oi)); + maybe_free_addr64(a64); + + if (memop & MO_SIGN) { + tcg_gen_ext_i32(ret, ret, memop); + } +} + +static void do_nonatomic_op_i64(TCGv_i64 ret, TCGTemp *addr, TCGv_i64 val, + TCGArg idx, MemOp memop, bool new_val, + void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t2 = tcg_temp_ebb_new_i64(); + + memop = tcg_canonicalize_memop(memop, 1, 0); + + tcg_gen_qemu_ld_i64_int(t1, addr, idx, memop); + tcg_gen_ext_i64(t2, val, memop); + gen(t2, t1, t2); + tcg_gen_qemu_st_i64_int(t2, addr, idx, memop); + + tcg_gen_ext_i64(ret, (new_val ? t2 : t1), memop); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); +} + +static void do_atomic_op_i64(TCGv_i64 ret, TCGTemp *addr, TCGv_i64 val, + TCGArg idx, MemOp memop, void * const table[]) +{ + memop = tcg_canonicalize_memop(memop, 1, 0); + + if ((memop & MO_SIZE) == MO_64) { + gen_atomic_op_i64 gen = table[memop & (MO_SIZE | MO_BSWAP)]; + + if (gen) { + MemOpIdx oi = make_memop_idx(memop & ~MO_SIGN, idx); + TCGv_i64 a64 = maybe_extend_addr64(addr); + gen(ret, tcg_env, a64, val, tcg_constant_i32(oi)); + maybe_free_addr64(a64); + return; + } + + gen_helper_exit_atomic(tcg_env); + /* Produce a result, so that we have a well-formed opcode stream + with respect to uses of the result in the (dead) code following. */ + tcg_gen_movi_i64(ret, 0); + } else { + TCGv_i32 v32 = tcg_temp_ebb_new_i32(); + TCGv_i32 r32 = tcg_temp_ebb_new_i32(); + + tcg_gen_extrl_i64_i32(v32, val); + do_atomic_op_i32(r32, addr, v32, idx, memop & ~MO_SIGN, table); + tcg_temp_free_i32(v32); + + tcg_gen_extu_i32_i64(ret, r32); + tcg_temp_free_i32(r32); + + if (memop & MO_SIGN) { + tcg_gen_ext_i64(ret, ret, memop); + } + } +} + +#define GEN_ATOMIC_HELPER(NAME, OP, NEW) \ +static void * const table_##NAME[(MO_SIZE | MO_BSWAP) + 1] = { \ + [MO_8] = gen_helper_atomic_##NAME##b, \ + [MO_16 | MO_LE] = gen_helper_atomic_##NAME##w_le, \ + [MO_16 | MO_BE] = gen_helper_atomic_##NAME##w_be, \ + [MO_32 | MO_LE] = gen_helper_atomic_##NAME##l_le, \ + [MO_32 | MO_BE] = gen_helper_atomic_##NAME##l_be, \ + WITH_ATOMIC64([MO_64 | MO_LE] = gen_helper_atomic_##NAME##q_le) \ + WITH_ATOMIC64([MO_64 | MO_BE] = gen_helper_atomic_##NAME##q_be) \ +}; \ +void tcg_gen_atomic_##NAME##_i32_chk(TCGv_i32 ret, TCGTemp *addr, \ + TCGv_i32 val, TCGArg idx, \ + MemOp memop, TCGType addr_type) \ +{ \ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); \ + tcg_debug_assert((memop & MO_SIZE) <= MO_32); \ + if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) { \ + do_atomic_op_i32(ret, addr, val, idx, memop, table_##NAME); \ + } else { \ + do_nonatomic_op_i32(ret, addr, val, idx, memop, NEW, \ + tcg_gen_##OP##_i32); \ + } \ +} \ +void tcg_gen_atomic_##NAME##_i64_chk(TCGv_i64 ret, TCGTemp *addr, \ + TCGv_i64 val, TCGArg idx, \ + MemOp memop, TCGType addr_type) \ +{ \ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); \ + tcg_debug_assert((memop & MO_SIZE) <= MO_64); \ + if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) { \ + do_atomic_op_i64(ret, addr, val, idx, memop, table_##NAME); \ + } else { \ + do_nonatomic_op_i64(ret, addr, val, idx, memop, NEW, \ + tcg_gen_##OP##_i64); \ + } \ +} + +GEN_ATOMIC_HELPER(fetch_add, add, 0) +GEN_ATOMIC_HELPER(fetch_and, and, 0) +GEN_ATOMIC_HELPER(fetch_or, or, 0) +GEN_ATOMIC_HELPER(fetch_xor, xor, 0) +GEN_ATOMIC_HELPER(fetch_smin, smin, 0) +GEN_ATOMIC_HELPER(fetch_umin, umin, 0) +GEN_ATOMIC_HELPER(fetch_smax, smax, 0) +GEN_ATOMIC_HELPER(fetch_umax, umax, 0) + +GEN_ATOMIC_HELPER(add_fetch, add, 1) +GEN_ATOMIC_HELPER(and_fetch, and, 1) +GEN_ATOMIC_HELPER(or_fetch, or, 1) +GEN_ATOMIC_HELPER(xor_fetch, xor, 1) +GEN_ATOMIC_HELPER(smin_fetch, smin, 1) +GEN_ATOMIC_HELPER(umin_fetch, umin, 1) +GEN_ATOMIC_HELPER(smax_fetch, smax, 1) +GEN_ATOMIC_HELPER(umax_fetch, umax, 1) + +static void tcg_gen_mov2_i32(TCGv_i32 r, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_mov_i32(r, b); +} + +static void tcg_gen_mov2_i64(TCGv_i64 r, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_mov_i64(r, b); +} + +GEN_ATOMIC_HELPER(xchg, mov2, 0) + +#undef GEN_ATOMIC_HELPER diff --git a/tcg/tcg-op-vec.c b/tcg/tcg-op-vec.c index 463dabf515..094298bb27 100644 --- a/tcg/tcg-op-vec.c +++ b/tcg/tcg-op-vec.c @@ -19,18 +19,10 @@ #include "qemu/osdep.h" #include "tcg/tcg.h" -#include "tcg/tcg-op.h" +#include "tcg/tcg-temp-internal.h" +#include "tcg/tcg-op-common.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 - the compiler can eliminate. */ -#if TCG_TARGET_REG_BITS == 64 -extern TCGv_i32 TCGV_LOW_link_error(TCGv_i64); -extern TCGv_i32 TCGV_HIGH_link_error(TCGv_i64); -#define TCGV_LOW TCGV_LOW_link_error -#define TCGV_HIGH TCGV_HIGH_link_error -#endif +#include "tcg-internal.h" /* * Vector optional opcode tracking. @@ -50,9 +42,9 @@ extern TCGv_i32 TCGV_HIGH_link_error(TCGv_i64); * tcg_ctx->vec_opt_opc is non-NULL, the tcg_gen_*_vec expanders * will validate that their opcode is present in the list. */ -#ifdef CONFIG_DEBUG_TCG -void tcg_assert_listed_vecop(TCGOpcode op) +static void tcg_assert_listed_vecop(TCGOpcode op) { +#ifdef CONFIG_DEBUG_TCG const TCGOpcode *p = tcg_ctx->vecop_list; if (p) { for (; *p; ++p) { @@ -62,8 +54,8 @@ void tcg_assert_listed_vecop(TCGOpcode op) } g_assert_not_reached(); } -} #endif +} bool tcg_can_emit_vecop_list(const TCGOpcode *list, TCGType type, unsigned vece) @@ -150,7 +142,7 @@ bool tcg_can_emit_vecop_list(const TCGOpcode *list, void vec_gen_2(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, TCGArg a) { - TCGOp *op = tcg_emit_op(opc); + TCGOp *op = tcg_emit_op(opc, 2); TCGOP_VECL(op) = type - TCG_TYPE_V64; TCGOP_VECE(op) = vece; op->args[0] = r; @@ -160,7 +152,7 @@ void vec_gen_2(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, TCGArg a) void vec_gen_3(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, TCGArg a, TCGArg b) { - TCGOp *op = tcg_emit_op(opc); + TCGOp *op = tcg_emit_op(opc, 3); TCGOP_VECL(op) = type - TCG_TYPE_V64; TCGOP_VECE(op) = vece; op->args[0] = r; @@ -171,7 +163,7 @@ void vec_gen_3(TCGOpcode opc, TCGType type, unsigned vece, void vec_gen_4(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, TCGArg a, TCGArg b, TCGArg c) { - TCGOp *op = tcg_emit_op(opc); + TCGOp *op = tcg_emit_op(opc, 4); TCGOP_VECL(op) = type - TCG_TYPE_V64; TCGOP_VECE(op) = vece; op->args[0] = r; @@ -183,7 +175,7 @@ void vec_gen_4(TCGOpcode opc, TCGType type, unsigned vece, static void vec_gen_6(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, TCGArg a, TCGArg b, TCGArg c, TCGArg d, TCGArg e) { - TCGOp *op = tcg_emit_op(opc); + TCGOp *op = tcg_emit_op(opc, 6); TCGOP_VECL(op) = type - TCG_TYPE_V64; TCGOP_VECE(op) = vece; op->args[0] = r; @@ -226,32 +218,6 @@ void tcg_gen_mov_vec(TCGv_vec r, TCGv_vec a) } } -TCGv_vec tcg_const_zeros_vec(TCGType type) -{ - TCGv_vec ret = tcg_temp_new_vec(type); - tcg_gen_dupi_vec(MO_64, ret, 0); - return ret; -} - -TCGv_vec tcg_const_ones_vec(TCGType type) -{ - TCGv_vec ret = tcg_temp_new_vec(type); - tcg_gen_dupi_vec(MO_64, ret, -1); - return ret; -} - -TCGv_vec tcg_const_zeros_vec_matching(TCGv_vec m) -{ - TCGTemp *t = tcgv_vec_temp(m); - return tcg_const_zeros_vec(t->base_type); -} - -TCGv_vec tcg_const_ones_vec_matching(TCGv_vec m) -{ - TCGTemp *t = tcgv_vec_temp(m); - return tcg_const_ones_vec(t->base_type); -} - void tcg_gen_dupi_vec(unsigned vece, TCGv_vec r, uint64_t a) { TCGTemp *rt = tcgv_vec_temp(r); @@ -425,14 +391,11 @@ static bool do_op2(unsigned vece, TCGv_vec r, TCGv_vec a, TCGOpcode opc) void tcg_gen_not_vec(unsigned vece, TCGv_vec r, TCGv_vec a) { - const TCGOpcode *hold_list = tcg_swap_vecop_list(NULL); - - if (!TCG_TARGET_HAS_not_vec || !do_op2(vece, r, a, INDEX_op_not_vec)) { - TCGv_vec t = tcg_const_ones_vec_matching(r); - tcg_gen_xor_vec(0, r, a, t); - tcg_temp_free_vec(t); + if (TCG_TARGET_HAS_not_vec) { + vec_gen_op2(INDEX_op_not_vec, 0, r, a); + } else { + tcg_gen_xor_vec(0, r, a, tcg_constant_vec_matching(r, 0, -1)); } - tcg_swap_vecop_list(hold_list); } void tcg_gen_neg_vec(unsigned vece, TCGv_vec r, TCGv_vec a) @@ -443,9 +406,7 @@ void tcg_gen_neg_vec(unsigned vece, TCGv_vec r, TCGv_vec a) hold_list = tcg_swap_vecop_list(NULL); if (!TCG_TARGET_HAS_neg_vec || !do_op2(vece, r, a, INDEX_op_neg_vec)) { - TCGv_vec t = tcg_const_zeros_vec_matching(r); - tcg_gen_sub_vec(vece, r, t, a); - tcg_temp_free_vec(t); + tcg_gen_sub_vec(vece, r, tcg_constant_vec_matching(r, vece, 0), a); } tcg_swap_vecop_list(hold_list); } diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 019fab00cc..aa6bc6f57d 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -23,56 +23,54 @@ */ #include "qemu/osdep.h" -#include "exec/exec-all.h" #include "tcg/tcg.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-mo.h" +#include "tcg/tcg-temp-internal.h" +#include "tcg/tcg-op-common.h" +#include "exec/translation-block.h" #include "exec/plugin-gen.h" +#include "tcg-internal.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 - the compiler can eliminate. */ -#if TCG_TARGET_REG_BITS == 64 -extern TCGv_i32 TCGV_LOW_link_error(TCGv_i64); -extern TCGv_i32 TCGV_HIGH_link_error(TCGv_i64); -#define TCGV_LOW TCGV_LOW_link_error -#define TCGV_HIGH TCGV_HIGH_link_error -#endif -void tcg_gen_op1(TCGOpcode opc, TCGArg a1) +/* + * Encourage the compiler to tail-call to a function, rather than inlining. + * Minimizes code size across 99 bottles of beer on the wall. + */ +#define NI __attribute__((noinline)) + +void NI tcg_gen_op1(TCGOpcode opc, TCGArg a1) { - TCGOp *op = tcg_emit_op(opc); + TCGOp *op = tcg_emit_op(opc, 1); op->args[0] = a1; } -void tcg_gen_op2(TCGOpcode opc, TCGArg a1, TCGArg a2) +void NI tcg_gen_op2(TCGOpcode opc, TCGArg a1, TCGArg a2) { - TCGOp *op = tcg_emit_op(opc); + TCGOp *op = tcg_emit_op(opc, 2); op->args[0] = a1; op->args[1] = a2; } -void tcg_gen_op3(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3) +void NI tcg_gen_op3(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3) { - TCGOp *op = tcg_emit_op(opc); + TCGOp *op = tcg_emit_op(opc, 3); op->args[0] = a1; op->args[1] = a2; op->args[2] = a3; } -void tcg_gen_op4(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, TCGArg a4) +void NI tcg_gen_op4(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, TCGArg a4) { - TCGOp *op = tcg_emit_op(opc); + TCGOp *op = tcg_emit_op(opc, 4); op->args[0] = a1; op->args[1] = a2; op->args[2] = a3; op->args[3] = a4; } -void tcg_gen_op5(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, - TCGArg a4, TCGArg a5) +void NI tcg_gen_op5(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, + TCGArg a4, TCGArg a5) { - TCGOp *op = tcg_emit_op(opc); + TCGOp *op = tcg_emit_op(opc, 5); op->args[0] = a1; op->args[1] = a2; op->args[2] = a3; @@ -80,10 +78,10 @@ void tcg_gen_op5(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, op->args[4] = a5; } -void tcg_gen_op6(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, - TCGArg a4, TCGArg a5, TCGArg a6) +void NI tcg_gen_op6(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, + TCGArg a4, TCGArg a5, TCGArg a6) { - TCGOp *op = tcg_emit_op(opc); + TCGOp *op = tcg_emit_op(opc, 6); op->args[0] = a1; op->args[1] = a2; op->args[2] = a3; @@ -92,20 +90,262 @@ void tcg_gen_op6(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, op->args[5] = a6; } +/* + * With CONFIG_DEBUG_TCG, tcgv_*_tmp via tcgv_*_arg, is an out-of-line + * assertion check. Force tail calls to avoid too much code expansion. + */ +#ifdef CONFIG_DEBUG_TCG +# define DNI NI +#else +# define DNI +#endif + +static void DNI tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 a1) +{ + tcg_gen_op1(opc, tcgv_i32_arg(a1)); +} + +static void DNI tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 a1) +{ + tcg_gen_op1(opc, tcgv_i64_arg(a1)); +} + +static void DNI tcg_gen_op1i(TCGOpcode opc, TCGArg a1) +{ + tcg_gen_op1(opc, a1); +} + +static void DNI tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2) +{ + tcg_gen_op2(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2)); +} + +static void DNI tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2) +{ + tcg_gen_op2(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2)); +} + +static void DNI tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 a1, + TCGv_i32 a2, TCGv_i32 a3) +{ + tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), tcgv_i32_arg(a3)); +} + +static void DNI tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 a1, + TCGv_i64 a2, TCGv_i64 a3) +{ + tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), tcgv_i64_arg(a3)); +} + +static void DNI tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 a1, + TCGv_i32 a2, TCGArg a3) +{ + tcg_gen_op3(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3); +} + +static void DNI tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 a1, + TCGv_i64 a2, TCGArg a3) +{ + tcg_gen_op3(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3); +} + +static void DNI tcg_gen_ldst_op_i32(TCGOpcode opc, TCGv_i32 val, + TCGv_ptr base, TCGArg offset) +{ + tcg_gen_op3(opc, tcgv_i32_arg(val), tcgv_ptr_arg(base), offset); +} + +static void DNI tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val, + TCGv_ptr base, TCGArg offset) +{ + tcg_gen_op3(opc, tcgv_i64_arg(val), tcgv_ptr_arg(base), offset); +} + +static void DNI tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4) +{ + tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4)); +} + +static void DNI tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4) +{ + tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4)); +} + +static void DNI tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGArg a4) +{ + tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), a4); +} + +static void DNI tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGArg a4) +{ + tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), a4); +} + +static void DNI tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGArg a3, TCGArg a4) +{ + tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3, a4); +} + +static void DNI tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGArg a3, TCGArg a4) +{ + tcg_gen_op4(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), a3, a4); +} + +static void DNI tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, TCGv_i32 a5) +{ + tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5)); +} + +static void DNI tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, TCGv_i64 a5) +{ + tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5)); +} + +static void DNI tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGArg a4, TCGArg a5) +{ + tcg_gen_op5(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), a4, a5); +} + +static void DNI tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGArg a4, TCGArg a5) +{ + tcg_gen_op5(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), a4, a5); +} + +static void DNI tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, + TCGv_i32 a5, TCGv_i32 a6) +{ + tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), + tcgv_i32_arg(a6)); +} + +static void DNI tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, + TCGv_i64 a5, TCGv_i64 a6) +{ + tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), + tcgv_i64_arg(a6)); +} + +static void DNI tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, + TCGv_i32 a5, TCGArg a6) +{ + tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), a6); +} + +static void DNI tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGv_i64 a3, TCGv_i64 a4, + TCGv_i64 a5, TCGArg a6) +{ + tcg_gen_op6(opc, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), a6); +} + +static void DNI tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, + TCGArg a5, TCGArg a6) +{ + tcg_gen_op6(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5, a6); +} + +/* Generic ops. */ + +void gen_set_label(TCGLabel *l) +{ + l->present = 1; + tcg_gen_op1(INDEX_op_set_label, label_arg(l)); +} + +static void add_last_as_label_use(TCGLabel *l) +{ + TCGLabelUse *u = tcg_malloc(sizeof(TCGLabelUse)); + + u->op = tcg_last_op(); + QSIMPLEQ_INSERT_TAIL(&l->branches, u, next); +} + +void tcg_gen_br(TCGLabel *l) +{ + tcg_gen_op1(INDEX_op_br, label_arg(l)); + add_last_as_label_use(l); +} + void tcg_gen_mb(TCGBar mb_type) { - if (tcg_ctx->tb_cflags & CF_PARALLEL) { +#ifdef CONFIG_USER_ONLY + bool parallel = tcg_ctx->gen_tb->cflags & CF_PARALLEL; +#else + /* + * It is tempting to elide the barrier in a uniprocessor context. + * However, even with a single cpu we have i/o threads running in + * parallel, and lack of memory order can result in e.g. virtio + * queue entries being read incorrectly. + */ + bool parallel = true; +#endif + + if (parallel) { tcg_gen_op1(INDEX_op_mb, mb_type); } } +void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr) +{ + tcg_gen_op3(INDEX_op_plugin_cb_start, from, type, wr); +} + +void tcg_gen_plugin_cb_end(void) +{ + tcg_emit_op(INDEX_op_plugin_cb_end, 0); +} + /* 32 bit ops */ +void tcg_gen_discard_i32(TCGv_i32 arg) +{ + tcg_gen_op1_i32(INDEX_op_discard, arg); +} + +void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (ret != arg) { + tcg_gen_op2_i32(INDEX_op_mov_i32, ret, arg); + } +} + void tcg_gen_movi_i32(TCGv_i32 ret, int32_t arg) { tcg_gen_mov_i32(ret, tcg_constant_i32(arg)); } +void tcg_gen_add_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_add_i32, ret, arg1, arg2); +} + void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { /* some cases can be optimized here */ @@ -116,11 +356,15 @@ void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) } } +void tcg_gen_sub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_sub_i32, ret, arg1, arg2); +} + void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2) { - if (arg1 == 0 && TCG_TARGET_HAS_neg_i32) { - /* Don't recurse with tcg_gen_neg_i32. */ - tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg2); + if (arg1 == 0) { + tcg_gen_neg_i32(ret, arg2); } else { tcg_gen_sub_i32(ret, tcg_constant_i32(arg1), arg2); } @@ -128,12 +372,17 @@ void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2) void tcg_gen_subi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i32(ret, arg1); - } else { - tcg_gen_sub_i32(ret, arg1, tcg_constant_i32(arg2)); - } + tcg_gen_addi_i32(ret, arg1, -arg2); +} + +void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg); +} + +void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_and_i32, ret, arg1, arg2); } void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) @@ -164,6 +413,11 @@ void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) tcg_gen_and_i32(ret, arg1, tcg_constant_i32(arg2)); } +void tcg_gen_or_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_or_i32, ret, arg1, arg2); +} + void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { /* Some cases can be optimized here. */ @@ -176,6 +430,11 @@ void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) } } +void tcg_gen_xor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_xor_i32, ret, arg1, arg2); +} + void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { /* Some cases can be optimized here. */ @@ -189,6 +448,20 @@ void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) } } +void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (TCG_TARGET_HAS_not_i32) { + tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg); + } else { + tcg_gen_xori_i32(ret, arg, -1); + } +} + +void tcg_gen_shl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_shl_i32, ret, arg1, arg2); +} + void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { tcg_debug_assert(arg2 >= 0 && arg2 < 32); @@ -199,6 +472,11 @@ void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) } } +void tcg_gen_shr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_shr_i32, ret, arg1, arg2); +} + void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { tcg_debug_assert(arg2 >= 0 && arg2 < 32); @@ -209,6 +487,11 @@ void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) } } +void tcg_gen_sar_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_sar_i32, ret, arg1, arg2); +} + void tcg_gen_sari_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { tcg_debug_assert(arg2 >= 0 && arg2 < 32); @@ -224,8 +507,8 @@ void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, TCGv_i32 arg2, TCGLabel *l) if (cond == TCG_COND_ALWAYS) { tcg_gen_br(l); } else if (cond != TCG_COND_NEVER) { - l->refs++; tcg_gen_op4ii_i32(INDEX_op_brcond_i32, arg1, arg2, cond, label_arg(l)); + add_last_as_label_use(l); } } @@ -256,6 +539,32 @@ void tcg_gen_setcondi_i32(TCGCond cond, TCGv_i32 ret, tcg_gen_setcond_i32(cond, ret, arg1, tcg_constant_i32(arg2)); } +void tcg_gen_negsetcond_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (cond == TCG_COND_ALWAYS) { + tcg_gen_movi_i32(ret, -1); + } else if (cond == TCG_COND_NEVER) { + tcg_gen_movi_i32(ret, 0); + } else if (TCG_TARGET_HAS_negsetcond_i32) { + tcg_gen_op4i_i32(INDEX_op_negsetcond_i32, ret, arg1, arg2, cond); + } else { + tcg_gen_setcond_i32(cond, ret, arg1, arg2); + tcg_gen_neg_i32(ret, ret); + } +} + +void tcg_gen_negsetcondi_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, int32_t arg2) +{ + tcg_gen_negsetcond_i32(cond, ret, arg1, tcg_constant_i32(arg2)); +} + +void tcg_gen_mul_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_mul_i32, ret, arg1, arg2); +} + void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { if (arg2 == 0) { @@ -272,7 +581,7 @@ void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) if (TCG_TARGET_HAS_div_i32) { tcg_gen_op3_i32(INDEX_op_div_i32, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div2_i32) { - TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_sari_i32(t0, arg1, 31); tcg_gen_op5_i32(INDEX_op_div2_i32, ret, t0, arg1, t0, arg2); tcg_temp_free_i32(t0); @@ -286,13 +595,13 @@ void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) if (TCG_TARGET_HAS_rem_i32) { tcg_gen_op3_i32(INDEX_op_rem_i32, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div_i32) { - TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_op3_i32(INDEX_op_div_i32, t0, arg1, arg2); tcg_gen_mul_i32(t0, t0, arg2); tcg_gen_sub_i32(ret, arg1, t0); tcg_temp_free_i32(t0); } else if (TCG_TARGET_HAS_div2_i32) { - TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_sari_i32(t0, arg1, 31); tcg_gen_op5_i32(INDEX_op_div2_i32, t0, ret, arg1, t0, arg2); tcg_temp_free_i32(t0); @@ -306,9 +615,9 @@ void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) if (TCG_TARGET_HAS_div_i32) { tcg_gen_op3_i32(INDEX_op_divu_i32, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div2_i32) { - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_movi_i32(t0, 0); - tcg_gen_op5_i32(INDEX_op_divu2_i32, ret, t0, arg1, t0, arg2); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); + tcg_gen_op5_i32(INDEX_op_divu2_i32, ret, t0, arg1, zero, arg2); tcg_temp_free_i32(t0); } else { gen_helper_divu_i32(ret, arg1, arg2); @@ -320,15 +629,15 @@ void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) if (TCG_TARGET_HAS_rem_i32) { tcg_gen_op3_i32(INDEX_op_remu_i32, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div_i32) { - TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_op3_i32(INDEX_op_divu_i32, t0, arg1, arg2); tcg_gen_mul_i32(t0, t0, arg2); tcg_gen_sub_i32(ret, arg1, t0); tcg_temp_free_i32(t0); } else if (TCG_TARGET_HAS_div2_i32) { - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_movi_i32(t0, 0); - tcg_gen_op5_i32(INDEX_op_divu2_i32, t0, ret, arg1, t0, arg2); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); + tcg_gen_op5_i32(INDEX_op_divu2_i32, t0, ret, arg1, zero, arg2); tcg_temp_free_i32(t0); } else { gen_helper_remu_i32(ret, arg1, arg2); @@ -340,7 +649,7 @@ void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) if (TCG_TARGET_HAS_andc_i32) { tcg_gen_op3_i32(INDEX_op_andc_i32, ret, arg1, arg2); } else { - TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_not_i32(t0, arg2); tcg_gen_and_i32(ret, arg1, t0); tcg_temp_free_i32(t0); @@ -382,7 +691,7 @@ void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) if (TCG_TARGET_HAS_orc_i32) { tcg_gen_op3_i32(INDEX_op_orc_i32, ret, arg1, arg2); } else { - TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_not_i32(t0, arg2); tcg_gen_or_i32(ret, arg1, t0); tcg_temp_free_i32(t0); @@ -394,8 +703,8 @@ void tcg_gen_clz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) if (TCG_TARGET_HAS_clz_i32) { tcg_gen_op3_i32(INDEX_op_clz_i32, ret, arg1, arg2); } else if (TCG_TARGET_HAS_clz_i64) { - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t2 = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t1, arg1); tcg_gen_extu_i32_i64(t2, arg2); tcg_gen_addi_i64(t2, t2, 32); @@ -419,8 +728,8 @@ void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) if (TCG_TARGET_HAS_ctz_i32) { tcg_gen_op3_i32(INDEX_op_ctz_i32, ret, arg1, arg2); } else if (TCG_TARGET_HAS_ctz_i64) { - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t2 = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t1, arg1); tcg_gen_extu_i32_i64(t2, arg2); tcg_gen_ctz_i64(t1, t1, t2); @@ -431,7 +740,7 @@ void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) || TCG_TARGET_HAS_ctpop_i64 || TCG_TARGET_HAS_clz_i32 || TCG_TARGET_HAS_clz_i64) { - TCGv_i32 z, t = tcg_temp_new_i32(); + TCGv_i32 z, t = tcg_temp_ebb_new_i32(); if (TCG_TARGET_HAS_ctpop_i32 || TCG_TARGET_HAS_ctpop_i64) { tcg_gen_subi_i32(t, arg1, 1); @@ -456,7 +765,7 @@ void tcg_gen_ctzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2) { if (!TCG_TARGET_HAS_ctz_i32 && TCG_TARGET_HAS_ctpop_i32 && arg2 == 32) { /* This equivalence has the advantage of not requiring a fixup. */ - TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_subi_i32(t, arg1, 1); tcg_gen_andc_i32(t, t, arg1); tcg_gen_ctpop_i32(ret, t); @@ -469,7 +778,7 @@ void tcg_gen_ctzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2) void tcg_gen_clrsb_i32(TCGv_i32 ret, TCGv_i32 arg) { if (TCG_TARGET_HAS_clz_i32) { - TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_sari_i32(t, arg, 31); tcg_gen_xor_i32(t, t, arg); tcg_gen_clzi_i32(t, t, 32); @@ -485,7 +794,7 @@ void tcg_gen_ctpop_i32(TCGv_i32 ret, TCGv_i32 arg1) if (TCG_TARGET_HAS_ctpop_i32) { tcg_gen_op2_i32(INDEX_op_ctpop_i32, ret, arg1); } else if (TCG_TARGET_HAS_ctpop_i64) { - TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t, arg1); tcg_gen_ctpop_i64(t, t); tcg_gen_extrl_i64_i32(ret, t); @@ -502,8 +811,8 @@ void tcg_gen_rotl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) } else { TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); + t0 = tcg_temp_ebb_new_i32(); + t1 = tcg_temp_ebb_new_i32(); tcg_gen_shl_i32(t0, arg1, arg2); tcg_gen_subfi_i32(t1, 32, arg2); tcg_gen_shr_i32(t1, arg1, t1); @@ -523,8 +832,8 @@ void tcg_gen_rotli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) tcg_gen_rotl_i32(ret, arg1, tcg_constant_i32(arg2)); } else { TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); + t0 = tcg_temp_ebb_new_i32(); + t1 = tcg_temp_ebb_new_i32(); tcg_gen_shli_i32(t0, arg1, arg2); tcg_gen_shri_i32(t1, arg1, 32 - arg2); tcg_gen_or_i32(ret, t0, t1); @@ -540,8 +849,8 @@ void tcg_gen_rotr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) } else { TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); + t0 = tcg_temp_ebb_new_i32(); + t1 = tcg_temp_ebb_new_i32(); tcg_gen_shr_i32(t0, arg1, arg2); tcg_gen_subfi_i32(t1, 32, arg2); tcg_gen_shl_i32(t1, arg1, t1); @@ -582,7 +891,7 @@ void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, return; } - t1 = tcg_temp_new_i32(); + t1 = tcg_temp_ebb_new_i32(); if (TCG_TARGET_HAS_extract2_i32) { if (ofs + len == 32) { @@ -809,7 +1118,7 @@ void tcg_gen_extract2_i32(TCGv_i32 ret, TCGv_i32 al, TCGv_i32 ah, } else if (TCG_TARGET_HAS_extract2_i32) { tcg_gen_op4i_i32(INDEX_op_extract2_i32, ret, al, ah, ofs); } else { - TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_shri_i32(t0, al, ofs); tcg_gen_deposit_i32(ret, t0, ah, 32 - ofs, ofs); tcg_temp_free_i32(t0); @@ -823,18 +1132,8 @@ void tcg_gen_movcond_i32(TCGCond cond, TCGv_i32 ret, TCGv_i32 c1, tcg_gen_mov_i32(ret, v1); } else if (cond == TCG_COND_NEVER) { tcg_gen_mov_i32(ret, v2); - } else if (TCG_TARGET_HAS_movcond_i32) { - tcg_gen_op6i_i32(INDEX_op_movcond_i32, ret, c1, c2, v1, v2, cond); } else { - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); - tcg_gen_setcond_i32(cond, t0, c1, c2); - tcg_gen_neg_i32(t0, t0); - tcg_gen_and_i32(t1, v1, t0); - tcg_gen_andc_i32(ret, v2, t0); - tcg_gen_or_i32(ret, ret, t1); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); + tcg_gen_op6i_i32(INDEX_op_movcond_i32, ret, c1, c2, v1, v2, cond); } } @@ -844,8 +1143,8 @@ void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, if (TCG_TARGET_HAS_add2_i32) { tcg_gen_op6_i32(INDEX_op_add2_i32, rl, rh, al, ah, bl, bh); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); tcg_gen_concat_i32_i64(t0, al, ah); tcg_gen_concat_i32_i64(t1, bl, bh); tcg_gen_add_i64(t0, t0, t1); @@ -861,8 +1160,8 @@ void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, if (TCG_TARGET_HAS_sub2_i32) { tcg_gen_op6_i32(INDEX_op_sub2_i32, rl, rh, al, ah, bl, bh); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); tcg_gen_concat_i32_i64(t0, al, ah); tcg_gen_concat_i32_i64(t1, bl, bh); tcg_gen_sub_i64(t0, t0, t1); @@ -877,20 +1176,22 @@ void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) if (TCG_TARGET_HAS_mulu2_i32) { tcg_gen_op4_i32(INDEX_op_mulu2_i32, rl, rh, arg1, arg2); } else if (TCG_TARGET_HAS_muluh_i32) { - TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_op3_i32(INDEX_op_mul_i32, t, arg1, arg2); tcg_gen_op3_i32(INDEX_op_muluh_i32, rh, arg1, arg2); tcg_gen_mov_i32(rl, t); tcg_temp_free_i32(t); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); + } else if (TCG_TARGET_REG_BITS == 64) { + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t0, arg1); tcg_gen_extu_i32_i64(t1, arg2); tcg_gen_mul_i64(t0, t0, t1); tcg_gen_extr_i64_i32(rl, rh, t0); tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); + } else { + qemu_build_not_reached(); } } @@ -899,16 +1200,16 @@ void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) if (TCG_TARGET_HAS_muls2_i32) { tcg_gen_op4_i32(INDEX_op_muls2_i32, rl, rh, arg1, arg2); } else if (TCG_TARGET_HAS_mulsh_i32) { - TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_op3_i32(INDEX_op_mul_i32, t, arg1, arg2); tcg_gen_op3_i32(INDEX_op_mulsh_i32, rh, arg1, arg2); tcg_gen_mov_i32(rl, t); tcg_temp_free_i32(t); } else if (TCG_TARGET_REG_BITS == 32) { - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); - TCGv_i32 t2 = tcg_temp_new_i32(); - TCGv_i32 t3 = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 t2 = tcg_temp_ebb_new_i32(); + TCGv_i32 t3 = tcg_temp_ebb_new_i32(); tcg_gen_mulu2_i32(t0, t1, arg1, arg2); /* Adjust for negative inputs. */ tcg_gen_sari_i32(t2, arg1, 31); @@ -923,8 +1224,8 @@ void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) tcg_temp_free_i32(t2); tcg_temp_free_i32(t3); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); tcg_gen_ext_i32_i64(t0, arg1); tcg_gen_ext_i32_i64(t1, arg2); tcg_gen_mul_i64(t0, t0, t1); @@ -937,9 +1238,9 @@ void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_mulsu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) { if (TCG_TARGET_REG_BITS == 32) { - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); - TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 t2 = tcg_temp_ebb_new_i32(); tcg_gen_mulu2_i32(t0, t1, arg1, arg2); /* Adjust for negative input for the signed arg1. */ tcg_gen_sari_i32(t2, arg1, 31); @@ -950,8 +1251,8 @@ void tcg_gen_mulsu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); tcg_gen_ext_i32_i64(t0, arg1); tcg_gen_extu_i32_i64(t1, arg2); tcg_gen_mul_i64(t0, t0, t1); @@ -999,6 +1300,14 @@ void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg) } } +/* + * bswap16_i32: 16-bit byte swap on the low bits of a 32-bit value. + * + * Byte pattern: xxab -> yyba + * + * With TCG_BSWAP_IZ, x == zero, else undefined. + * With TCG_BSWAP_OZ, y == zero, with TCG_BSWAP_OS y == sign, else undefined. + */ void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg, int flags) { /* Only one extension flag may be present. */ @@ -1007,37 +1316,45 @@ void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg, int flags) if (TCG_TARGET_HAS_bswap16_i32) { tcg_gen_op3i_i32(INDEX_op_bswap16_i32, ret, arg, flags); } else { - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); - tcg_gen_shri_i32(t0, arg, 8); + /* arg = ..ab (IZ) xxab (!IZ) */ + tcg_gen_shri_i32(t0, arg, 8); /* t0 = ...a (IZ) .xxa (!IZ) */ if (!(flags & TCG_BSWAP_IZ)) { - tcg_gen_ext8u_i32(t0, t0); + tcg_gen_ext8u_i32(t0, t0); /* t0 = ...a */ } if (flags & TCG_BSWAP_OS) { - tcg_gen_shli_i32(t1, arg, 24); - tcg_gen_sari_i32(t1, t1, 16); + tcg_gen_shli_i32(t1, arg, 24); /* t1 = b... */ + tcg_gen_sari_i32(t1, t1, 16); /* t1 = ssb. */ } else if (flags & TCG_BSWAP_OZ) { - tcg_gen_ext8u_i32(t1, arg); - tcg_gen_shli_i32(t1, t1, 8); + tcg_gen_ext8u_i32(t1, arg); /* t1 = ...b */ + tcg_gen_shli_i32(t1, t1, 8); /* t1 = ..b. */ } else { - tcg_gen_shli_i32(t1, arg, 8); + tcg_gen_shli_i32(t1, arg, 8); /* t1 = xab. */ } - tcg_gen_or_i32(ret, t0, t1); + tcg_gen_or_i32(ret, t0, t1); /* ret = ..ba (OZ) */ + /* = ssba (OS) */ + /* = xaba (no flag) */ tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); } } +/* + * bswap32_i32: 32-bit byte swap on a 32-bit value. + * + * Byte pattern: abcd -> dcba + */ void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg) { if (TCG_TARGET_HAS_bswap32_i32) { tcg_gen_op3i_i32(INDEX_op_bswap32_i32, ret, arg, 0); } else { - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); TCGv_i32 t2 = tcg_constant_i32(0x00ff00ff); /* arg = abcd */ @@ -1056,6 +1373,11 @@ void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg) } } +/* + * hswap_i32: Swap 16-bit halfwords within a 32-bit value. + * + * Byte pattern: abcd -> cdab + */ void tcg_gen_hswap_i32(TCGv_i32 ret, TCGv_i32 arg) { /* Swapping 2 16-bit elements is a rotate. */ @@ -1084,7 +1406,7 @@ void tcg_gen_umax_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) void tcg_gen_abs_i32(TCGv_i32 ret, TCGv_i32 a) { - TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_sari_i32(t, a, 31); tcg_gen_xor_i32(ret, a, t); @@ -1092,127 +1414,281 @@ void tcg_gen_abs_i32(TCGv_i32 ret, TCGv_i32 a) tcg_temp_free_i32(t); } -/* 64-bit ops */ +void tcg_gen_ld8u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld8u_i32, ret, arg2, offset); +} -#if TCG_TARGET_REG_BITS == 32 -/* These are all inline for TCG_TARGET_REG_BITS == 64. */ +void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld8s_i32, ret, arg2, offset); +} + +void tcg_gen_ld16u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld16u_i32, ret, arg2, offset); +} + +void tcg_gen_ld16s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld16s_i32, ret, arg2, offset); +} + +void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld_i32, ret, arg2, offset); +} + +void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_st8_i32, arg1, arg2, offset); +} + +void tcg_gen_st16_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_st16_i32, arg1, arg2, offset); +} + +void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_st_i32, arg1, arg2, offset); +} + + +/* 64-bit ops */ void tcg_gen_discard_i64(TCGv_i64 arg) { - tcg_gen_discard_i32(TCGV_LOW(arg)); - tcg_gen_discard_i32(TCGV_HIGH(arg)); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op1_i64(INDEX_op_discard, arg); + } else { + tcg_gen_discard_i32(TCGV_LOW(arg)); + tcg_gen_discard_i32(TCGV_HIGH(arg)); + } } void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg) { - TCGTemp *ts = tcgv_i64_temp(arg); - - /* Canonicalize TCGv_i64 TEMP_CONST into TCGv_i32 TEMP_CONST. */ - if (ts->kind == TEMP_CONST) { - tcg_gen_movi_i64(ret, ts->val); + if (ret == arg) { + return; + } + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op2_i64(INDEX_op_mov_i64, ret, arg); } else { - tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); + TCGTemp *ts = tcgv_i64_temp(arg); + + /* Canonicalize TCGv_i64 TEMP_CONST into TCGv_i32 TEMP_CONST. */ + if (ts->kind == TEMP_CONST) { + tcg_gen_movi_i64(ret, ts->val); + } else { + tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); + } } } void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg) { - tcg_gen_movi_i32(TCGV_LOW(ret), arg); - tcg_gen_movi_i32(TCGV_HIGH(ret), arg >> 32); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_mov_i64(ret, tcg_constant_i64(arg)); + } else { + tcg_gen_movi_i32(TCGV_LOW(ret), arg); + tcg_gen_movi_i32(TCGV_HIGH(ret), arg >> 32); + } } void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld8u_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_ld8u_i64, ret, arg2, offset); + } else { + tcg_gen_ld8u_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } } void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld8s_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_ld8s_i64, ret, arg2, offset); + } else { + tcg_gen_ld8s_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + } } void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld16u_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_ld16u_i64, ret, arg2, offset); + } else { + tcg_gen_ld16u_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } } void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld16s_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_ld16s_i64, ret, arg2, offset); + } else { + tcg_gen_ld16s_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + } } void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_ld32u_i64, ret, arg2, offset); + } else { + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } } void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_ld32s_i64, ret, arg2, offset); + } else { + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); + } } void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { - /* Since arg2 and ret have different types, - they cannot be the same temporary */ -#if HOST_BIG_ENDIAN - tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset); - tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset + 4); -#else - tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); - tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset + 4); -#endif + /* + * For 32-bit host, since arg2 and ret have different types, + * they cannot be the same temporary -- no chance of overlap. + */ + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_ld_i64, ret, arg2, offset); + } else if (HOST_BIG_ENDIAN) { + tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset); + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset + 4); + } else { + tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); + tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset + 4); + } +} + +void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) +{ + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_st8_i64, arg1, arg2, offset); + } else { + tcg_gen_st8_i32(TCGV_LOW(arg1), arg2, offset); + } +} + +void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) +{ + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_st16_i64, arg1, arg2, offset); + } else { + tcg_gen_st16_i32(TCGV_LOW(arg1), arg2, offset); + } +} + +void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) +{ + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_st32_i64, arg1, arg2, offset); + } else { + tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); + } } void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { -#if HOST_BIG_ENDIAN - tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset); - tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset + 4); -#else - tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); - tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset + 4); -#endif + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_st_i64, arg1, arg2, offset); + } else if (HOST_BIG_ENDIAN) { + tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset); + tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset + 4); + } else { + tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); + tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset + 4); + } +} + +void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_add_i64, ret, arg1, arg2); + } else { + tcg_gen_add2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1), + TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2)); + } +} + +void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_sub_i64, ret, arg1, arg2); + } else { + tcg_gen_sub2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1), + TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2)); + } } void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_and_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_and_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_and_i64, ret, arg1, arg2); + } else { + tcg_gen_and_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_and_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + } } void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_or_i64, ret, arg1, arg2); + } else { + tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + } } void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_xor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); - tcg_gen_xor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_xor_i64, ret, arg1, arg2); + } else { + tcg_gen_xor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_xor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); + } } void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - gen_helper_shl_i64(ret, arg1, arg2); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_shl_i64, ret, arg1, arg2); + } else { + gen_helper_shl_i64(ret, arg1, arg2); + } } void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - gen_helper_shr_i64(ret, arg1, arg2); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_shr_i64, ret, arg1, arg2); + } else { + gen_helper_shr_i64(ret, arg1, arg2); + } } void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - gen_helper_sar_i64(ret, arg1, arg2); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_sar_i64, ret, arg1, arg2); + } else { + gen_helper_sar_i64(ret, arg1, arg2); + } } void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) @@ -1220,8 +1696,14 @@ void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) TCGv_i64 t0; TCGv_i32 t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i32(); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_mul_i64, ret, arg1, arg2); + return; + } + + + t0 = tcg_temp_ebb_new_i64(); + t1 = tcg_temp_ebb_new_i32(); tcg_gen_mulu2_i32(TCGV_LOW(t0), TCGV_HIGH(t0), TCGV_LOW(arg1), TCGV_LOW(arg2)); @@ -1236,15 +1718,6 @@ void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) tcg_temp_free_i32(t1); } -#else - -void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg) -{ - tcg_gen_mov_i64(ret, tcg_constant_i64(arg)); -} - -#endif /* TCG_TARGET_REG_SIZE == 32 */ - void tcg_gen_addi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) { /* some cases can be optimized here */ @@ -1261,9 +1734,8 @@ void tcg_gen_addi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) void tcg_gen_subfi_i64(TCGv_i64 ret, int64_t arg1, TCGv_i64 arg2) { - if (arg1 == 0 && TCG_TARGET_HAS_neg_i64) { - /* Don't recurse with tcg_gen_neg_i64. */ - tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg2); + if (arg1 == 0) { + tcg_gen_neg_i64(ret, arg2); } else if (TCG_TARGET_REG_BITS == 64) { tcg_gen_sub_i64(ret, tcg_constant_i64(arg1), arg2); } else { @@ -1275,15 +1747,17 @@ void tcg_gen_subfi_i64(TCGv_i64 ret, int64_t arg1, TCGv_i64 arg2) void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) { - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i64(ret, arg1); - } else if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_sub_i64(ret, arg1, tcg_constant_i64(arg2)); + tcg_gen_addi_i64(ret, arg1, -arg2); +} + +void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg); } else { + TCGv_i32 zero = tcg_constant_i32(0); tcg_gen_sub2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), - TCGV_LOW(arg1), TCGV_HIGH(arg1), - tcg_constant_i32(arg2), tcg_constant_i32(arg2 >> 32)); + zero, zero, TCGV_LOW(arg), TCGV_HIGH(arg)); } } @@ -1402,7 +1876,7 @@ static inline void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, tcg_gen_extract2_i32(TCGV_HIGH(ret), TCGV_LOW(arg1), TCGV_HIGH(arg1), 32 - c); } else { - TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_shri_i32(t0, TCGV_LOW(arg1), 32 - c); tcg_gen_deposit_i32(TCGV_HIGH(ret), t0, TCGV_HIGH(arg1), c, 32 - c); @@ -1453,7 +1927,6 @@ void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, TCGv_i64 arg2, TCGLabel *l) if (cond == TCG_COND_ALWAYS) { tcg_gen_br(l); } else if (cond != TCG_COND_NEVER) { - l->refs++; if (TCG_TARGET_REG_BITS == 32) { tcg_gen_op6ii_i32(INDEX_op_brcond2_i32, TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2), @@ -1462,6 +1935,7 @@ void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, TCGv_i64 arg2, TCGLabel *l) tcg_gen_op4ii_i64(INDEX_op_brcond_i64, arg1, arg2, cond, label_arg(l)); } + add_last_as_label_use(l); } } @@ -1472,12 +1946,12 @@ void tcg_gen_brcondi_i64(TCGCond cond, TCGv_i64 arg1, int64_t arg2, TCGLabel *l) } else if (cond == TCG_COND_ALWAYS) { tcg_gen_br(l); } else if (cond != TCG_COND_NEVER) { - l->refs++; tcg_gen_op6ii_i32(INDEX_op_brcond2_i32, TCGV_LOW(arg1), TCGV_HIGH(arg1), tcg_constant_i32(arg2), tcg_constant_i32(arg2 >> 32), cond, label_arg(l)); + add_last_as_label_use(l); } } @@ -1518,6 +1992,33 @@ void tcg_gen_setcondi_i64(TCGCond cond, TCGv_i64 ret, } } +void tcg_gen_negsetcondi_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, int64_t arg2) +{ + tcg_gen_negsetcond_i64(cond, ret, arg1, tcg_constant_i64(arg2)); +} + +void tcg_gen_negsetcond_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (cond == TCG_COND_ALWAYS) { + tcg_gen_movi_i64(ret, -1); + } else if (cond == TCG_COND_NEVER) { + tcg_gen_movi_i64(ret, 0); + } else if (TCG_TARGET_HAS_negsetcond_i64) { + tcg_gen_op4i_i64(INDEX_op_negsetcond_i64, ret, arg1, arg2, cond); + } else if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_op6i_i32(INDEX_op_setcond2_i32, TCGV_LOW(ret), + TCGV_LOW(arg1), TCGV_HIGH(arg1), + TCGV_LOW(arg2), TCGV_HIGH(arg2), cond); + tcg_gen_neg_i32(TCGV_LOW(ret), TCGV_LOW(ret)); + tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_LOW(ret)); + } else { + tcg_gen_setcond_i64(cond, ret, arg1, arg2); + tcg_gen_neg_i64(ret, ret); + } +} + void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) { if (arg2 == 0) { @@ -1525,9 +2026,7 @@ void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) } else if (is_power_of_2(arg2)) { tcg_gen_shli_i64(ret, arg1, ctz64(arg2)); } else { - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_mul_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); + tcg_gen_mul_i64(ret, arg1, tcg_constant_i64(arg2)); } } @@ -1536,7 +2035,7 @@ void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_HAS_div_i64) { tcg_gen_op3_i64(INDEX_op_div_i64, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div2_i64) { - TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_sari_i64(t0, arg1, 63); tcg_gen_op5_i64(INDEX_op_div2_i64, ret, t0, arg1, t0, arg2); tcg_temp_free_i64(t0); @@ -1550,13 +2049,13 @@ void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_HAS_rem_i64) { tcg_gen_op3_i64(INDEX_op_rem_i64, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div_i64) { - TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_op3_i64(INDEX_op_div_i64, t0, arg1, arg2); tcg_gen_mul_i64(t0, t0, arg2); tcg_gen_sub_i64(ret, arg1, t0); tcg_temp_free_i64(t0); } else if (TCG_TARGET_HAS_div2_i64) { - TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_sari_i64(t0, arg1, 63); tcg_gen_op5_i64(INDEX_op_div2_i64, t0, ret, arg1, t0, arg2); tcg_temp_free_i64(t0); @@ -1570,9 +2069,9 @@ void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_HAS_div_i64) { tcg_gen_op3_i64(INDEX_op_divu_i64, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div2_i64) { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_movi_i64(t0, 0); - tcg_gen_op5_i64(INDEX_op_divu2_i64, ret, t0, arg1, t0, arg2); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 zero = tcg_constant_i64(0); + tcg_gen_op5_i64(INDEX_op_divu2_i64, ret, t0, arg1, zero, arg2); tcg_temp_free_i64(t0); } else { gen_helper_divu_i64(ret, arg1, arg2); @@ -1584,15 +2083,15 @@ void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_HAS_rem_i64) { tcg_gen_op3_i64(INDEX_op_remu_i64, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div_i64) { - TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_op3_i64(INDEX_op_divu_i64, t0, arg1, arg2); tcg_gen_mul_i64(t0, t0, arg2); tcg_gen_sub_i64(ret, arg1, t0); tcg_temp_free_i64(t0); } else if (TCG_TARGET_HAS_div2_i64) { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_movi_i64(t0, 0); - tcg_gen_op5_i64(INDEX_op_divu2_i64, t0, ret, arg1, t0, arg2); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 zero = tcg_constant_i64(0); + tcg_gen_op5_i64(INDEX_op_divu2_i64, t0, ret, arg1, zero, arg2); tcg_temp_free_i64(t0); } else { gen_helper_remu_i64(ret, arg1, arg2); @@ -1674,6 +2173,14 @@ void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg) } } +/* + * bswap16_i64: 16-bit byte swap on the low bits of a 64-bit value. + * + * Byte pattern: xxxxxxxxab -> yyyyyyyyba + * + * With TCG_BSWAP_IZ, x == zero, else undefined. + * With TCG_BSWAP_OZ, y == zero, with TCG_BSWAP_OS y == sign, else undefined. + */ void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) { /* Only one extension flag may be present. */ @@ -1689,30 +2196,41 @@ void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) } else if (TCG_TARGET_HAS_bswap16_i64) { tcg_gen_op3i_i64(INDEX_op_bswap16_i64, ret, arg, flags); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); - tcg_gen_shri_i64(t0, arg, 8); + /* arg = ......ab or xxxxxxab */ + tcg_gen_shri_i64(t0, arg, 8); /* t0 = .......a or .xxxxxxa */ if (!(flags & TCG_BSWAP_IZ)) { - tcg_gen_ext8u_i64(t0, t0); + tcg_gen_ext8u_i64(t0, t0); /* t0 = .......a */ } if (flags & TCG_BSWAP_OS) { - tcg_gen_shli_i64(t1, arg, 56); - tcg_gen_sari_i64(t1, t1, 48); + tcg_gen_shli_i64(t1, arg, 56); /* t1 = b....... */ + tcg_gen_sari_i64(t1, t1, 48); /* t1 = ssssssb. */ } else if (flags & TCG_BSWAP_OZ) { - tcg_gen_ext8u_i64(t1, arg); - tcg_gen_shli_i64(t1, t1, 8); + tcg_gen_ext8u_i64(t1, arg); /* t1 = .......b */ + tcg_gen_shli_i64(t1, t1, 8); /* t1 = ......b. */ } else { - tcg_gen_shli_i64(t1, arg, 8); + tcg_gen_shli_i64(t1, arg, 8); /* t1 = xxxxxab. */ } - tcg_gen_or_i64(ret, t0, t1); + tcg_gen_or_i64(ret, t0, t1); /* ret = ......ba (OZ) */ + /* ssssssba (OS) */ + /* xxxxxaba (no flag) */ tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); } } +/* + * bswap32_i64: 32-bit byte swap on the low bits of a 64-bit value. + * + * Byte pattern: xxxxabcd -> yyyydcba + * + * With TCG_BSWAP_IZ, x == zero, else undefined. + * With TCG_BSWAP_OZ, y == zero, with TCG_BSWAP_OS y == sign, else undefined. + */ void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) { /* Only one extension flag may be present. */ @@ -1728,8 +2246,8 @@ void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) } else if (TCG_TARGET_HAS_bswap32_i64) { tcg_gen_op3i_i64(INDEX_op_bswap32_i64, ret, arg, flags); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); TCGv_i64 t2 = tcg_constant_i64(0x00ff00ff); /* arg = xxxxabcd */ @@ -1746,19 +2264,25 @@ void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) } else { tcg_gen_shri_i64(t1, t1, 32); /* t1 = ....dc.. */ } - tcg_gen_or_i64(ret, t0, t1); /* ret = ssssdcba */ + tcg_gen_or_i64(ret, t0, t1); /* ret = ssssdcba (OS) */ + /* ....dcba (else) */ tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); } } +/* + * bswap64_i64: 64-bit byte swap on a 64-bit value. + * + * Byte pattern: abcdefgh -> hgfedcba + */ void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) { if (TCG_TARGET_REG_BITS == 32) { TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); + t0 = tcg_temp_ebb_new_i32(); + t1 = tcg_temp_ebb_new_i32(); tcg_gen_bswap32_i32(t0, TCGV_LOW(arg)); tcg_gen_bswap32_i32(t1, TCGV_HIGH(arg)); @@ -1769,9 +2293,9 @@ void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) } else if (TCG_TARGET_HAS_bswap64_i64) { tcg_gen_op3i_i64(INDEX_op_bswap64_i64, ret, arg, 0); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t2 = tcg_temp_ebb_new_i64(); /* arg = abcdefgh */ tcg_gen_movi_i64(t2, 0x00ff00ff00ff00ffull); @@ -1798,24 +2322,35 @@ void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) } } +/* + * hswap_i64: Swap 16-bit halfwords within a 64-bit value. + * See also include/qemu/bitops.h, hswap64. + * + * Byte pattern: abcdefgh -> ghefcdab + */ void tcg_gen_hswap_i64(TCGv_i64 ret, TCGv_i64 arg) { uint64_t m = 0x0000ffff0000ffffull; - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); - /* See include/qemu/bitops.h, hswap64. */ - tcg_gen_rotli_i64(t1, arg, 32); - tcg_gen_andi_i64(t0, t1, m); - tcg_gen_shli_i64(t0, t0, 16); - tcg_gen_shri_i64(t1, t1, 16); - tcg_gen_andi_i64(t1, t1, m); - tcg_gen_or_i64(ret, t0, t1); + /* arg = abcdefgh */ + tcg_gen_rotli_i64(t1, arg, 32); /* t1 = efghabcd */ + tcg_gen_andi_i64(t0, t1, m); /* t0 = ..gh..cd */ + tcg_gen_shli_i64(t0, t0, 16); /* t0 = gh..cd.. */ + tcg_gen_shri_i64(t1, t1, 16); /* t1 = ..efghab */ + tcg_gen_andi_i64(t1, t1, m); /* t1 = ..ef..ab */ + tcg_gen_or_i64(ret, t0, t1); /* ret = ghefcdab */ tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); } +/* + * wswap_i64: Swap 32-bit words within a 64-bit value. + * + * Byte pattern: abcdefgh -> efghabcd + */ void tcg_gen_wswap_i64(TCGv_i64 ret, TCGv_i64 arg) { /* Swapping 2 32-bit elements is a rotate. */ @@ -1842,7 +2377,7 @@ void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) } else if (TCG_TARGET_HAS_andc_i64) { tcg_gen_op3_i64(INDEX_op_andc_i64, ret, arg1, arg2); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_not_i64(t0, arg2); tcg_gen_and_i64(ret, arg1, t0); tcg_temp_free_i64(t0); @@ -1896,7 +2431,7 @@ void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) } else if (TCG_TARGET_HAS_orc_i64) { tcg_gen_op3_i64(INDEX_op_orc_i64, ret, arg1, arg2); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_not_i64(t0, arg2); tcg_gen_or_i64(ret, arg1, t0); tcg_temp_free_i64(t0); @@ -1917,16 +2452,14 @@ void tcg_gen_clzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) if (TCG_TARGET_REG_BITS == 32 && TCG_TARGET_HAS_clz_i32 && arg2 <= 0xffffffffu) { - TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_clzi_i32(t, TCGV_LOW(arg1), arg2 - 32); tcg_gen_addi_i32(t, t, 32); tcg_gen_clz_i32(TCGV_LOW(ret), TCGV_HIGH(arg1), t); tcg_gen_movi_i32(TCGV_HIGH(ret), 0); tcg_temp_free_i32(t); } else { - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_clz_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); + tcg_gen_clz_i64(ret, arg1, tcg_constant_i64(arg2)); } } @@ -1935,7 +2468,7 @@ void tcg_gen_ctz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_HAS_ctz_i64) { tcg_gen_op3_i64(INDEX_op_ctz_i64, ret, arg1, arg2); } else if (TCG_TARGET_HAS_ctpop_i64 || TCG_TARGET_HAS_clz_i64) { - TCGv_i64 z, t = tcg_temp_new_i64(); + TCGv_i64 z, t = tcg_temp_ebb_new_i64(); if (TCG_TARGET_HAS_ctpop_i64) { tcg_gen_subi_i64(t, arg1, 1); @@ -1962,7 +2495,7 @@ void tcg_gen_ctzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) if (TCG_TARGET_REG_BITS == 32 && TCG_TARGET_HAS_ctz_i32 && arg2 <= 0xffffffffu) { - TCGv_i32 t32 = tcg_temp_new_i32(); + TCGv_i32 t32 = tcg_temp_ebb_new_i32(); tcg_gen_ctzi_i32(t32, TCGV_HIGH(arg1), arg2 - 32); tcg_gen_addi_i32(t32, t32, 32); tcg_gen_ctz_i32(TCGV_LOW(ret), TCGV_LOW(arg1), t32); @@ -1972,22 +2505,20 @@ void tcg_gen_ctzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) && TCG_TARGET_HAS_ctpop_i64 && arg2 == 64) { /* This equivalence has the advantage of not requiring a fixup. */ - TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_subi_i64(t, arg1, 1); tcg_gen_andc_i64(t, t, arg1); tcg_gen_ctpop_i64(ret, t); tcg_temp_free_i64(t); } else { - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_ctz_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); + tcg_gen_ctz_i64(ret, arg1, tcg_constant_i64(arg2)); } } void tcg_gen_clrsb_i64(TCGv_i64 ret, TCGv_i64 arg) { if (TCG_TARGET_HAS_clz_i64 || TCG_TARGET_HAS_clz_i32) { - TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_sari_i64(t, arg, 63); tcg_gen_xor_i64(t, t, arg); tcg_gen_clzi_i64(t, t, 64); @@ -2018,8 +2549,8 @@ void tcg_gen_rotl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_op3_i64(INDEX_op_rotl_i64, ret, arg1, arg2); } else { TCGv_i64 t0, t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); + t0 = tcg_temp_ebb_new_i64(); + t1 = tcg_temp_ebb_new_i64(); tcg_gen_shl_i64(t0, arg1, arg2); tcg_gen_subfi_i64(t1, 64, arg2); tcg_gen_shr_i64(t1, arg1, t1); @@ -2039,8 +2570,8 @@ void tcg_gen_rotli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) tcg_gen_rotl_i64(ret, arg1, tcg_constant_i64(arg2)); } else { TCGv_i64 t0, t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); + t0 = tcg_temp_ebb_new_i64(); + t1 = tcg_temp_ebb_new_i64(); tcg_gen_shli_i64(t0, arg1, arg2); tcg_gen_shri_i64(t1, arg1, 64 - arg2); tcg_gen_or_i64(ret, t0, t1); @@ -2055,8 +2586,8 @@ void tcg_gen_rotr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_op3_i64(INDEX_op_rotr_i64, ret, arg1, arg2); } else { TCGv_i64 t0, t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); + t0 = tcg_temp_ebb_new_i64(); + t1 = tcg_temp_ebb_new_i64(); tcg_gen_shr_i64(t0, arg1, arg2); tcg_gen_subfi_i64(t1, 64, arg2); tcg_gen_shl_i64(t1, arg1, t1); @@ -2112,7 +2643,7 @@ void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, } } - t1 = tcg_temp_new_i64(); + t1 = tcg_temp_ebb_new_i64(); if (TCG_TARGET_HAS_extract2_i64) { if (ofs + len == 64) { @@ -2344,7 +2875,7 @@ void tcg_gen_sextract_i64(TCGv_i64 ret, TCGv_i64 arg, tcg_gen_sextract_i32(TCGV_HIGH(ret), TCGV_HIGH(arg), 0, len - 32); return; } else if (len > 32) { - TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_ebb_new_i32(); /* Extract the bits for the high word normally. */ tcg_gen_sextract_i32(t, TCGV_HIGH(arg), ofs + 32, len - 32); /* Shift the field down for the low part. */ @@ -2439,7 +2970,7 @@ void tcg_gen_extract2_i64(TCGv_i64 ret, TCGv_i64 al, TCGv_i64 ah, } else if (TCG_TARGET_HAS_extract2_i64) { tcg_gen_op4i_i64(INDEX_op_extract2_i64, ret, al, ah, ofs); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_shri_i64(t0, al, ofs); tcg_gen_deposit_i64(ret, t0, ah, 64 - ofs, ofs); tcg_temp_free_i64(t0); @@ -2453,44 +2984,22 @@ void tcg_gen_movcond_i64(TCGCond cond, TCGv_i64 ret, TCGv_i64 c1, tcg_gen_mov_i64(ret, v1); } else if (cond == TCG_COND_NEVER) { tcg_gen_mov_i64(ret, v2); - } else if (TCG_TARGET_REG_BITS == 32) { - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); + } else if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op6i_i64(INDEX_op_movcond_i64, ret, c1, c2, v1, v2, cond); + } else { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); + tcg_gen_op6i_i32(INDEX_op_setcond2_i32, t0, TCGV_LOW(c1), TCGV_HIGH(c1), TCGV_LOW(c2), TCGV_HIGH(c2), cond); - if (TCG_TARGET_HAS_movcond_i32) { - tcg_gen_movi_i32(t1, 0); - tcg_gen_movcond_i32(TCG_COND_NE, TCGV_LOW(ret), t0, t1, - TCGV_LOW(v1), TCGV_LOW(v2)); - tcg_gen_movcond_i32(TCG_COND_NE, TCGV_HIGH(ret), t0, t1, - TCGV_HIGH(v1), TCGV_HIGH(v2)); - } else { - tcg_gen_neg_i32(t0, t0); + tcg_gen_movcond_i32(TCG_COND_NE, TCGV_LOW(ret), t0, zero, + TCGV_LOW(v1), TCGV_LOW(v2)); + tcg_gen_movcond_i32(TCG_COND_NE, TCGV_HIGH(ret), t0, zero, + TCGV_HIGH(v1), TCGV_HIGH(v2)); - tcg_gen_and_i32(t1, TCGV_LOW(v1), t0); - tcg_gen_andc_i32(TCGV_LOW(ret), TCGV_LOW(v2), t0); - tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(ret), t1); - - tcg_gen_and_i32(t1, TCGV_HIGH(v1), t0); - tcg_gen_andc_i32(TCGV_HIGH(ret), TCGV_HIGH(v2), t0); - tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), t1); - } tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - } else if (TCG_TARGET_HAS_movcond_i64) { - tcg_gen_op6i_i64(INDEX_op_movcond_i64, ret, c1, c2, v1, v2, cond); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - tcg_gen_setcond_i64(cond, t0, c1, c2); - tcg_gen_neg_i64(t0, t0); - tcg_gen_and_i64(t1, v1, t0); - tcg_gen_andc_i64(ret, v2, t0); - tcg_gen_or_i64(ret, ret, t1); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); } } @@ -2500,8 +3009,8 @@ void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, if (TCG_TARGET_HAS_add2_i64) { tcg_gen_op6_i64(INDEX_op_add2_i64, rl, rh, al, ah, bl, bh); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); tcg_gen_add_i64(t0, al, bl); tcg_gen_setcond_i64(TCG_COND_LTU, t1, t0, al); tcg_gen_add_i64(rh, ah, bh); @@ -2518,8 +3027,8 @@ void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, if (TCG_TARGET_HAS_sub2_i64) { tcg_gen_op6_i64(INDEX_op_sub2_i64, rl, rh, al, ah, bl, bh); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); tcg_gen_sub_i64(t0, al, bl); tcg_gen_setcond_i64(TCG_COND_LTU, t1, al, bl); tcg_gen_sub_i64(rh, ah, bh); @@ -2535,13 +3044,13 @@ void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_HAS_mulu2_i64) { tcg_gen_op4_i64(INDEX_op_mulu2_i64, rl, rh, arg1, arg2); } else if (TCG_TARGET_HAS_muluh_i64) { - TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_op3_i64(INDEX_op_mul_i64, t, arg1, arg2); tcg_gen_op3_i64(INDEX_op_muluh_i64, rh, arg1, arg2); tcg_gen_mov_i64(rl, t); tcg_temp_free_i64(t); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_mul_i64(t0, arg1, arg2); gen_helper_muluh_i64(rh, arg1, arg2); tcg_gen_mov_i64(rl, t0); @@ -2554,16 +3063,16 @@ void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_HAS_muls2_i64) { tcg_gen_op4_i64(INDEX_op_muls2_i64, rl, rh, arg1, arg2); } else if (TCG_TARGET_HAS_mulsh_i64) { - TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_op3_i64(INDEX_op_mul_i64, t, arg1, arg2); tcg_gen_op3_i64(INDEX_op_mulsh_i64, rh, arg1, arg2); tcg_gen_mov_i64(rl, t); tcg_temp_free_i64(t); } else if (TCG_TARGET_HAS_mulu2_i64 || TCG_TARGET_HAS_muluh_i64) { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); - TCGv_i64 t3 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t2 = tcg_temp_ebb_new_i64(); + TCGv_i64 t3 = tcg_temp_ebb_new_i64(); tcg_gen_mulu2_i64(t0, t1, arg1, arg2); /* Adjust for negative inputs. */ tcg_gen_sari_i64(t2, arg1, 63); @@ -2578,7 +3087,7 @@ void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) tcg_temp_free_i64(t2); tcg_temp_free_i64(t3); } else { - TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_mul_i64(t0, arg1, arg2); gen_helper_mulsh_i64(rh, arg1, arg2); tcg_gen_mov_i64(rl, t0); @@ -2588,9 +3097,9 @@ void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_mulsu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) { - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t2 = tcg_temp_ebb_new_i64(); tcg_gen_mulu2_i64(t0, t1, arg1, arg2); /* Adjust for negative input for the signed arg1. */ tcg_gen_sari_i64(t2, arg1, 63); @@ -2624,7 +3133,7 @@ void tcg_gen_umax_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) void tcg_gen_abs_i64(TCGv_i64 ret, TCGv_i64 a) { - TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_sari_i64(t, a, 63); tcg_gen_xor_i64(ret, a, t); @@ -2638,7 +3147,7 @@ void tcg_gen_extrl_i64_i32(TCGv_i32 ret, TCGv_i64 arg) { if (TCG_TARGET_REG_BITS == 32) { tcg_gen_mov_i32(ret, TCGV_LOW(arg)); - } else if (TCG_TARGET_HAS_extrl_i64_i32) { + } else if (TCG_TARGET_HAS_extr_i64_i32) { tcg_gen_op2(INDEX_op_extrl_i64_i32, tcgv_i32_arg(ret), tcgv_i64_arg(arg)); } else { @@ -2650,11 +3159,11 @@ void tcg_gen_extrh_i64_i32(TCGv_i32 ret, TCGv_i64 arg) { if (TCG_TARGET_REG_BITS == 32) { tcg_gen_mov_i32(ret, TCGV_HIGH(arg)); - } else if (TCG_TARGET_HAS_extrh_i64_i32) { + } else if (TCG_TARGET_HAS_extr_i64_i32) { tcg_gen_op2(INDEX_op_extrh_i64_i32, tcgv_i32_arg(ret), tcgv_i64_arg(arg)); } else { - TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_shri_i64(t, arg, 32); tcg_gen_mov_i32(ret, (TCGv_i32)t); tcg_temp_free_i64(t); @@ -2693,7 +3202,7 @@ void tcg_gen_concat_i32_i64(TCGv_i64 dest, TCGv_i32 low, TCGv_i32 high) return; } - tmp = tcg_temp_new_i64(); + tmp = tcg_temp_ebb_new_i64(); /* These extensions are only needed for type correctness. We may be able to do better given target specific information. */ tcg_gen_extu_i32_i64(tmp, high); @@ -2726,6 +3235,53 @@ void tcg_gen_extr32_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i64 arg) tcg_gen_shri_i64(hi, arg, 32); } +void tcg_gen_concat32_i64(TCGv_i64 ret, TCGv_i64 lo, TCGv_i64 hi) +{ + tcg_gen_deposit_i64(ret, lo, hi, 32, 32); +} + +void tcg_gen_extr_i128_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i128 arg) +{ + tcg_gen_mov_i64(lo, TCGV128_LOW(arg)); + tcg_gen_mov_i64(hi, TCGV128_HIGH(arg)); +} + +void tcg_gen_concat_i64_i128(TCGv_i128 ret, TCGv_i64 lo, TCGv_i64 hi) +{ + tcg_gen_mov_i64(TCGV128_LOW(ret), lo); + tcg_gen_mov_i64(TCGV128_HIGH(ret), hi); +} + +void tcg_gen_mov_i128(TCGv_i128 dst, TCGv_i128 src) +{ + if (dst != src) { + tcg_gen_mov_i64(TCGV128_LOW(dst), TCGV128_LOW(src)); + tcg_gen_mov_i64(TCGV128_HIGH(dst), TCGV128_HIGH(src)); + } +} + +void tcg_gen_ld_i128(TCGv_i128 ret, TCGv_ptr base, tcg_target_long offset) +{ + if (HOST_BIG_ENDIAN) { + tcg_gen_ld_i64(TCGV128_HIGH(ret), base, offset); + tcg_gen_ld_i64(TCGV128_LOW(ret), base, offset + 8); + } else { + tcg_gen_ld_i64(TCGV128_LOW(ret), base, offset); + tcg_gen_ld_i64(TCGV128_HIGH(ret), base, offset + 8); + } +} + +void tcg_gen_st_i128(TCGv_i128 val, TCGv_ptr base, tcg_target_long offset) +{ + if (HOST_BIG_ENDIAN) { + tcg_gen_st_i64(TCGV128_HIGH(val), base, offset); + tcg_gen_st_i64(TCGV128_LOW(val), base, offset + 8); + } else { + tcg_gen_st_i64(TCGV128_LOW(val), base, offset); + tcg_gen_st_i64(TCGV128_HIGH(val), base, offset + 8); + } +} + /* QEMU specific operations. */ void tcg_gen_exit_tb(const TranslationBlock *tb, unsigned idx) @@ -2756,14 +3312,13 @@ void tcg_gen_exit_tb(const TranslationBlock *tb, unsigned idx) tcg_debug_assert(idx == TB_EXIT_REQUESTED); } - plugin_gen_disable_mem_helpers(); tcg_gen_op1i(INDEX_op_exit_tb, val); } void tcg_gen_goto_tb(unsigned idx) { /* We tested CF_NO_GOTO_TB in translator_use_goto_tb. */ - tcg_debug_assert(!(tcg_ctx->tb_cflags & CF_NO_GOTO_TB)); + tcg_debug_assert(!(tcg_ctx->gen_tb->cflags & CF_NO_GOTO_TB)); /* We only support two chained exits. */ tcg_debug_assert(idx <= TB_EXIT_IDXMAX); #ifdef CONFIG_DEBUG_TCG @@ -2779,618 +3334,14 @@ void tcg_gen_lookup_and_goto_ptr(void) { TCGv_ptr ptr; - if (tcg_ctx->tb_cflags & CF_NO_GOTO_PTR) { + if (tcg_ctx->gen_tb->cflags & CF_NO_GOTO_PTR) { tcg_gen_exit_tb(NULL, 0); return; } plugin_gen_disable_mem_helpers(); - ptr = tcg_temp_new_ptr(); - gen_helper_lookup_tb_ptr(ptr, cpu_env); + ptr = tcg_temp_ebb_new_ptr(); + gen_helper_lookup_tb_ptr(ptr, tcg_env); tcg_gen_op1i(INDEX_op_goto_ptr, tcgv_ptr_arg(ptr)); tcg_temp_free_ptr(ptr); } - -static inline MemOp tcg_canonicalize_memop(MemOp op, bool is64, bool st) -{ - /* Trigger the asserts within as early as possible. */ - unsigned a_bits = get_alignment_bits(op); - - /* Prefer MO_ALIGN+MO_XX over MO_ALIGN_XX+MO_XX */ - if (a_bits == (op & MO_SIZE)) { - op = (op & ~MO_AMASK) | MO_ALIGN; - } - - switch (op & MO_SIZE) { - case MO_8: - op &= ~MO_BSWAP; - break; - case MO_16: - break; - case MO_32: - if (!is64) { - op &= ~MO_SIGN; - } - break; - case MO_64: - if (is64) { - op &= ~MO_SIGN; - break; - } - /* fall through */ - default: - g_assert_not_reached(); - } - if (st) { - op &= ~MO_SIGN; - } - return op; -} - -static void gen_ldst_i32(TCGOpcode opc, TCGv_i32 val, TCGv addr, - MemOp memop, TCGArg idx) -{ - MemOpIdx oi = make_memop_idx(memop, idx); -#if TARGET_LONG_BITS == 32 - tcg_gen_op3i_i32(opc, val, addr, oi); -#else - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_op4i_i32(opc, val, TCGV_LOW(addr), TCGV_HIGH(addr), oi); - } else { - tcg_gen_op3(opc, tcgv_i32_arg(val), tcgv_i64_arg(addr), oi); - } -#endif -} - -static void gen_ldst_i64(TCGOpcode opc, TCGv_i64 val, TCGv addr, - MemOp memop, TCGArg idx) -{ - MemOpIdx oi = make_memop_idx(memop, idx); -#if TARGET_LONG_BITS == 32 - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_op4i_i32(opc, TCGV_LOW(val), TCGV_HIGH(val), addr, oi); - } else { - tcg_gen_op3(opc, tcgv_i64_arg(val), tcgv_i32_arg(addr), oi); - } -#else - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_op5i_i32(opc, TCGV_LOW(val), TCGV_HIGH(val), - TCGV_LOW(addr), TCGV_HIGH(addr), oi); - } else { - tcg_gen_op3i_i64(opc, val, addr, oi); - } -#endif -} - -static void tcg_gen_req_mo(TCGBar type) -{ -#ifdef TCG_GUEST_DEFAULT_MO - type &= TCG_GUEST_DEFAULT_MO; -#endif - type &= ~TCG_TARGET_DEFAULT_MO; - if (type) { - tcg_gen_mb(type | TCG_BAR_SC); - } -} - -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 void plugin_gen_mem_callbacks(TCGv vaddr, MemOpIdx oi, - enum qemu_plugin_mem_rw rw) -{ -#ifdef CONFIG_PLUGIN - if (tcg_ctx->plugin_insn != NULL) { - qemu_plugin_meminfo_t info = make_plugin_meminfo(oi, rw); - plugin_gen_empty_mem_callback(vaddr, info); - tcg_temp_free(vaddr); - } -#endif -} - -void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) -{ - MemOp orig_memop; - MemOpIdx oi; - - tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); - memop = tcg_canonicalize_memop(memop, 0, 0); - oi = make_memop_idx(memop, idx); - - orig_memop = memop; - if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { - memop &= ~MO_BSWAP; - /* The bswap primitive benefits from zero-extended input. */ - if ((memop & MO_SSIZE) == MO_SW) { - memop &= ~MO_SIGN; - } - } - - addr = plugin_prep_mem_callbacks(addr); - gen_ldst_i32(INDEX_op_qemu_ld_i32, val, addr, memop, idx); - plugin_gen_mem_callbacks(addr, oi, QEMU_PLUGIN_MEM_R); - - if ((orig_memop ^ memop) & MO_BSWAP) { - switch (orig_memop & MO_SIZE) { - case MO_16: - tcg_gen_bswap16_i32(val, val, (orig_memop & MO_SIGN - ? TCG_BSWAP_IZ | TCG_BSWAP_OS - : TCG_BSWAP_IZ | TCG_BSWAP_OZ)); - break; - case MO_32: - tcg_gen_bswap32_i32(val, val); - break; - default: - g_assert_not_reached(); - } - } -} - -void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) -{ - TCGv_i32 swap = NULL; - MemOpIdx oi; - - tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); - memop = tcg_canonicalize_memop(memop, 0, 1); - oi = make_memop_idx(memop, idx); - - if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { - swap = tcg_temp_new_i32(); - switch (memop & MO_SIZE) { - case MO_16: - tcg_gen_bswap16_i32(swap, val, 0); - break; - case MO_32: - tcg_gen_bswap32_i32(swap, val); - break; - default: - g_assert_not_reached(); - } - val = swap; - memop &= ~MO_BSWAP; - } - - addr = plugin_prep_mem_callbacks(addr); - if (TCG_TARGET_HAS_qemu_st8_i32 && (memop & MO_SIZE) == MO_8) { - gen_ldst_i32(INDEX_op_qemu_st8_i32, val, addr, memop, idx); - } else { - gen_ldst_i32(INDEX_op_qemu_st_i32, val, addr, memop, idx); - } - plugin_gen_mem_callbacks(addr, oi, QEMU_PLUGIN_MEM_W); - - if (swap) { - tcg_temp_free_i32(swap); - } -} - -void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) -{ - MemOp orig_memop; - MemOpIdx oi; - - if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { - tcg_gen_qemu_ld_i32(TCGV_LOW(val), addr, idx, memop); - if (memop & MO_SIGN) { - tcg_gen_sari_i32(TCGV_HIGH(val), TCGV_LOW(val), 31); - } else { - tcg_gen_movi_i32(TCGV_HIGH(val), 0); - } - return; - } - - tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); - memop = tcg_canonicalize_memop(memop, 1, 0); - oi = make_memop_idx(memop, idx); - - orig_memop = memop; - if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { - memop &= ~MO_BSWAP; - /* The bswap primitive benefits from zero-extended input. */ - if ((memop & MO_SIGN) && (memop & MO_SIZE) < MO_64) { - memop &= ~MO_SIGN; - } - } - - addr = plugin_prep_mem_callbacks(addr); - gen_ldst_i64(INDEX_op_qemu_ld_i64, val, addr, memop, idx); - plugin_gen_mem_callbacks(addr, oi, QEMU_PLUGIN_MEM_R); - - if ((orig_memop ^ memop) & MO_BSWAP) { - int flags = (orig_memop & MO_SIGN - ? TCG_BSWAP_IZ | TCG_BSWAP_OS - : TCG_BSWAP_IZ | TCG_BSWAP_OZ); - switch (orig_memop & MO_SIZE) { - case MO_16: - tcg_gen_bswap16_i64(val, val, flags); - break; - case MO_32: - tcg_gen_bswap32_i64(val, val, flags); - break; - case MO_64: - tcg_gen_bswap64_i64(val, val); - break; - default: - g_assert_not_reached(); - } - } -} - -void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) -{ - TCGv_i64 swap = NULL; - MemOpIdx oi; - - if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { - tcg_gen_qemu_st_i32(TCGV_LOW(val), addr, idx, memop); - return; - } - - tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); - memop = tcg_canonicalize_memop(memop, 1, 1); - oi = make_memop_idx(memop, idx); - - if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { - swap = tcg_temp_new_i64(); - switch (memop & MO_SIZE) { - case MO_16: - tcg_gen_bswap16_i64(swap, val, 0); - break; - case MO_32: - tcg_gen_bswap32_i64(swap, val, 0); - break; - case MO_64: - tcg_gen_bswap64_i64(swap, val); - break; - default: - g_assert_not_reached(); - } - val = swap; - 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, oi, QEMU_PLUGIN_MEM_W); - - if (swap) { - tcg_temp_free_i64(swap); - } -} - -static void tcg_gen_ext_i32(TCGv_i32 ret, TCGv_i32 val, MemOp opc) -{ - switch (opc & MO_SSIZE) { - case MO_SB: - tcg_gen_ext8s_i32(ret, val); - break; - case MO_UB: - tcg_gen_ext8u_i32(ret, val); - break; - case MO_SW: - tcg_gen_ext16s_i32(ret, val); - break; - case MO_UW: - tcg_gen_ext16u_i32(ret, val); - break; - default: - tcg_gen_mov_i32(ret, val); - break; - } -} - -static void tcg_gen_ext_i64(TCGv_i64 ret, TCGv_i64 val, MemOp opc) -{ - switch (opc & MO_SSIZE) { - case MO_SB: - tcg_gen_ext8s_i64(ret, val); - break; - case MO_UB: - tcg_gen_ext8u_i64(ret, val); - break; - case MO_SW: - tcg_gen_ext16s_i64(ret, val); - break; - case MO_UW: - tcg_gen_ext16u_i64(ret, val); - break; - case MO_SL: - tcg_gen_ext32s_i64(ret, val); - break; - case MO_UL: - tcg_gen_ext32u_i64(ret, val); - break; - default: - tcg_gen_mov_i64(ret, val); - break; - } -} - -typedef void (*gen_atomic_cx_i32)(TCGv_i32, TCGv_env, TCGv, - TCGv_i32, TCGv_i32, TCGv_i32); -typedef void (*gen_atomic_cx_i64)(TCGv_i64, TCGv_env, TCGv, - TCGv_i64, TCGv_i64, TCGv_i32); -typedef void (*gen_atomic_op_i32)(TCGv_i32, TCGv_env, TCGv, - TCGv_i32, TCGv_i32); -typedef void (*gen_atomic_op_i64)(TCGv_i64, TCGv_env, TCGv, - TCGv_i64, TCGv_i32); - -#ifdef CONFIG_ATOMIC64 -# define WITH_ATOMIC64(X) X, -#else -# define WITH_ATOMIC64(X) -#endif - -static void * const table_cmpxchg[(MO_SIZE | MO_BSWAP) + 1] = { - [MO_8] = gen_helper_atomic_cmpxchgb, - [MO_16 | MO_LE] = gen_helper_atomic_cmpxchgw_le, - [MO_16 | MO_BE] = gen_helper_atomic_cmpxchgw_be, - [MO_32 | MO_LE] = gen_helper_atomic_cmpxchgl_le, - [MO_32 | MO_BE] = gen_helper_atomic_cmpxchgl_be, - WITH_ATOMIC64([MO_64 | MO_LE] = gen_helper_atomic_cmpxchgq_le) - WITH_ATOMIC64([MO_64 | MO_BE] = gen_helper_atomic_cmpxchgq_be) -}; - -void tcg_gen_atomic_cmpxchg_i32(TCGv_i32 retv, TCGv addr, TCGv_i32 cmpv, - TCGv_i32 newv, TCGArg idx, MemOp memop) -{ - memop = tcg_canonicalize_memop(memop, 0, 0); - - if (!(tcg_ctx->tb_cflags & CF_PARALLEL)) { - TCGv_i32 t1 = tcg_temp_new_i32(); - TCGv_i32 t2 = tcg_temp_new_i32(); - - tcg_gen_ext_i32(t2, cmpv, memop & MO_SIZE); - - tcg_gen_qemu_ld_i32(t1, addr, idx, memop & ~MO_SIGN); - tcg_gen_movcond_i32(TCG_COND_EQ, t2, t1, t2, newv, t1); - tcg_gen_qemu_st_i32(t2, addr, idx, memop); - tcg_temp_free_i32(t2); - - if (memop & MO_SIGN) { - tcg_gen_ext_i32(retv, t1, memop); - } else { - tcg_gen_mov_i32(retv, t1); - } - tcg_temp_free_i32(t1); - } else { - gen_atomic_cx_i32 gen; - MemOpIdx oi; - - gen = table_cmpxchg[memop & (MO_SIZE | MO_BSWAP)]; - tcg_debug_assert(gen != NULL); - - oi = make_memop_idx(memop & ~MO_SIGN, idx); - gen(retv, cpu_env, addr, cmpv, newv, tcg_constant_i32(oi)); - - if (memop & MO_SIGN) { - tcg_gen_ext_i32(retv, retv, memop); - } - } -} - -void tcg_gen_atomic_cmpxchg_i64(TCGv_i64 retv, TCGv addr, TCGv_i64 cmpv, - TCGv_i64 newv, TCGArg idx, MemOp memop) -{ - memop = tcg_canonicalize_memop(memop, 1, 0); - - if (!(tcg_ctx->tb_cflags & CF_PARALLEL)) { - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); - - tcg_gen_ext_i64(t2, cmpv, memop & MO_SIZE); - - tcg_gen_qemu_ld_i64(t1, addr, idx, memop & ~MO_SIGN); - tcg_gen_movcond_i64(TCG_COND_EQ, t2, t1, t2, newv, t1); - tcg_gen_qemu_st_i64(t2, addr, idx, memop); - tcg_temp_free_i64(t2); - - if (memop & MO_SIGN) { - tcg_gen_ext_i64(retv, t1, memop); - } else { - tcg_gen_mov_i64(retv, t1); - } - tcg_temp_free_i64(t1); - } else if ((memop & MO_SIZE) == MO_64) { -#ifdef CONFIG_ATOMIC64 - gen_atomic_cx_i64 gen; - MemOpIdx oi; - - gen = table_cmpxchg[memop & (MO_SIZE | MO_BSWAP)]; - tcg_debug_assert(gen != NULL); - - oi = make_memop_idx(memop, idx); - gen(retv, cpu_env, addr, cmpv, newv, tcg_constant_i32(oi)); -#else - gen_helper_exit_atomic(cpu_env); - /* Produce a result, so that we have a well-formed opcode stream - with respect to uses of the result in the (dead) code following. */ - tcg_gen_movi_i64(retv, 0); -#endif /* CONFIG_ATOMIC64 */ - } else { - TCGv_i32 c32 = tcg_temp_new_i32(); - TCGv_i32 n32 = tcg_temp_new_i32(); - TCGv_i32 r32 = tcg_temp_new_i32(); - - tcg_gen_extrl_i64_i32(c32, cmpv); - tcg_gen_extrl_i64_i32(n32, newv); - tcg_gen_atomic_cmpxchg_i32(r32, addr, c32, n32, idx, memop & ~MO_SIGN); - tcg_temp_free_i32(c32); - tcg_temp_free_i32(n32); - - tcg_gen_extu_i32_i64(retv, r32); - tcg_temp_free_i32(r32); - - if (memop & MO_SIGN) { - tcg_gen_ext_i64(retv, retv, memop); - } - } -} - -static void do_nonatomic_op_i32(TCGv_i32 ret, TCGv addr, TCGv_i32 val, - TCGArg idx, MemOp memop, bool new_val, - void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32)) -{ - TCGv_i32 t1 = tcg_temp_new_i32(); - TCGv_i32 t2 = tcg_temp_new_i32(); - - memop = tcg_canonicalize_memop(memop, 0, 0); - - tcg_gen_qemu_ld_i32(t1, addr, idx, memop); - tcg_gen_ext_i32(t2, val, memop); - gen(t2, t1, t2); - tcg_gen_qemu_st_i32(t2, addr, idx, memop); - - tcg_gen_ext_i32(ret, (new_val ? t2 : t1), memop); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); -} - -static void do_atomic_op_i32(TCGv_i32 ret, TCGv addr, TCGv_i32 val, - TCGArg idx, MemOp memop, void * const table[]) -{ - gen_atomic_op_i32 gen; - MemOpIdx oi; - - memop = tcg_canonicalize_memop(memop, 0, 0); - - gen = table[memop & (MO_SIZE | MO_BSWAP)]; - tcg_debug_assert(gen != NULL); - - oi = make_memop_idx(memop & ~MO_SIGN, idx); - gen(ret, cpu_env, addr, val, tcg_constant_i32(oi)); - - if (memop & MO_SIGN) { - tcg_gen_ext_i32(ret, ret, memop); - } -} - -static void do_nonatomic_op_i64(TCGv_i64 ret, TCGv addr, TCGv_i64 val, - TCGArg idx, MemOp memop, bool new_val, - void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64)) -{ - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); - - memop = tcg_canonicalize_memop(memop, 1, 0); - - tcg_gen_qemu_ld_i64(t1, addr, idx, memop); - tcg_gen_ext_i64(t2, val, memop); - gen(t2, t1, t2); - tcg_gen_qemu_st_i64(t2, addr, idx, memop); - - tcg_gen_ext_i64(ret, (new_val ? t2 : t1), memop); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); -} - -static void do_atomic_op_i64(TCGv_i64 ret, TCGv addr, TCGv_i64 val, - TCGArg idx, MemOp memop, void * const table[]) -{ - memop = tcg_canonicalize_memop(memop, 1, 0); - - if ((memop & MO_SIZE) == MO_64) { -#ifdef CONFIG_ATOMIC64 - gen_atomic_op_i64 gen; - MemOpIdx oi; - - gen = table[memop & (MO_SIZE | MO_BSWAP)]; - tcg_debug_assert(gen != NULL); - - oi = make_memop_idx(memop & ~MO_SIGN, idx); - gen(ret, cpu_env, addr, val, tcg_constant_i32(oi)); -#else - gen_helper_exit_atomic(cpu_env); - /* Produce a result, so that we have a well-formed opcode stream - with respect to uses of the result in the (dead) code following. */ - tcg_gen_movi_i64(ret, 0); -#endif /* CONFIG_ATOMIC64 */ - } else { - TCGv_i32 v32 = tcg_temp_new_i32(); - TCGv_i32 r32 = tcg_temp_new_i32(); - - tcg_gen_extrl_i64_i32(v32, val); - do_atomic_op_i32(r32, addr, v32, idx, memop & ~MO_SIGN, table); - tcg_temp_free_i32(v32); - - tcg_gen_extu_i32_i64(ret, r32); - tcg_temp_free_i32(r32); - - if (memop & MO_SIGN) { - tcg_gen_ext_i64(ret, ret, memop); - } - } -} - -#define GEN_ATOMIC_HELPER(NAME, OP, NEW) \ -static void * const table_##NAME[(MO_SIZE | MO_BSWAP) + 1] = { \ - [MO_8] = gen_helper_atomic_##NAME##b, \ - [MO_16 | MO_LE] = gen_helper_atomic_##NAME##w_le, \ - [MO_16 | MO_BE] = gen_helper_atomic_##NAME##w_be, \ - [MO_32 | MO_LE] = gen_helper_atomic_##NAME##l_le, \ - [MO_32 | MO_BE] = gen_helper_atomic_##NAME##l_be, \ - WITH_ATOMIC64([MO_64 | MO_LE] = gen_helper_atomic_##NAME##q_le) \ - WITH_ATOMIC64([MO_64 | MO_BE] = gen_helper_atomic_##NAME##q_be) \ -}; \ -void tcg_gen_atomic_##NAME##_i32 \ - (TCGv_i32 ret, TCGv addr, TCGv_i32 val, TCGArg idx, MemOp memop) \ -{ \ - if (tcg_ctx->tb_cflags & CF_PARALLEL) { \ - do_atomic_op_i32(ret, addr, val, idx, memop, table_##NAME); \ - } else { \ - do_nonatomic_op_i32(ret, addr, val, idx, memop, NEW, \ - tcg_gen_##OP##_i32); \ - } \ -} \ -void tcg_gen_atomic_##NAME##_i64 \ - (TCGv_i64 ret, TCGv addr, TCGv_i64 val, TCGArg idx, MemOp memop) \ -{ \ - if (tcg_ctx->tb_cflags & CF_PARALLEL) { \ - do_atomic_op_i64(ret, addr, val, idx, memop, table_##NAME); \ - } else { \ - do_nonatomic_op_i64(ret, addr, val, idx, memop, NEW, \ - tcg_gen_##OP##_i64); \ - } \ -} - -GEN_ATOMIC_HELPER(fetch_add, add, 0) -GEN_ATOMIC_HELPER(fetch_and, and, 0) -GEN_ATOMIC_HELPER(fetch_or, or, 0) -GEN_ATOMIC_HELPER(fetch_xor, xor, 0) -GEN_ATOMIC_HELPER(fetch_smin, smin, 0) -GEN_ATOMIC_HELPER(fetch_umin, umin, 0) -GEN_ATOMIC_HELPER(fetch_smax, smax, 0) -GEN_ATOMIC_HELPER(fetch_umax, umax, 0) - -GEN_ATOMIC_HELPER(add_fetch, add, 1) -GEN_ATOMIC_HELPER(and_fetch, and, 1) -GEN_ATOMIC_HELPER(or_fetch, or, 1) -GEN_ATOMIC_HELPER(xor_fetch, xor, 1) -GEN_ATOMIC_HELPER(smin_fetch, smin, 1) -GEN_ATOMIC_HELPER(umin_fetch, umin, 1) -GEN_ATOMIC_HELPER(smax_fetch, smax, 1) -GEN_ATOMIC_HELPER(umax_fetch, umax, 1) - -static void tcg_gen_mov2_i32(TCGv_i32 r, TCGv_i32 a, TCGv_i32 b) -{ - tcg_gen_mov_i32(r, b); -} - -static void tcg_gen_mov2_i64(TCGv_i64 r, TCGv_i64 a, TCGv_i64 b) -{ - tcg_gen_mov_i64(r, b); -} - -GEN_ATOMIC_HELPER(xchg, mov2, 0) - -#undef GEN_ATOMIC_HELPER diff --git a/tcg/tcg.c b/tcg/tcg.c index 0f9cfe96f2..0c0bb9d169 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -22,9 +22,6 @@ * THE SOFTWARE. */ -/* define it to use liveness analysis (better code) */ -#define USE_TCG_OPTIMIZATIONS - #include "qemu/osdep.h" /* Define to jump the ELF file used to communicate with GDB. */ @@ -34,17 +31,13 @@ #include "qemu/cutils.h" #include "qemu/host-utils.h" #include "qemu/qemu-print.h" -#include "qemu/timer.h" #include "qemu/cacheflush.h" #include "qemu/cacheinfo.h" - -/* Note: the long term plan is to reduce the dependencies on the QEMU - CPU definitions. Currently they are used for qemu_ld/st - instructions */ -#define NO_CPU_IO_DEFS - -#include "exec/exec-all.h" -#include "tcg/tcg-op.h" +#include "qemu/timer.h" +#include "exec/translation-block.h" +#include "exec/tlb-common.h" +#include "tcg/startup.h" +#include "tcg/tcg-op-common.h" #if UINTPTR_MAX == UINT32_MAX # define ELF_CLASS ELFCLASS32 @@ -60,10 +53,11 @@ #include "elf.h" #include "exec/log.h" #include "tcg/tcg-ldst.h" +#include "tcg/tcg-temp-internal.h" #include "tcg-internal.h" - -#ifdef CONFIG_TCG_INTERPRETER -#include +#include "tcg/perf.h" +#ifdef CONFIG_USER_ONLY +#include "exec/user/guest-base.h" #endif /* Forward declarations for functions declared in tcg-target.c.inc and @@ -96,17 +90,44 @@ typedef struct QEMU_PACKED { DebugFrameFDEHeader fde; } DebugFrameHeader; +typedef struct TCGLabelQemuLdst { + bool is_ld; /* qemu_ld: true, qemu_st: false */ + MemOpIdx oi; + TCGType type; /* result type of a load */ + TCGReg addrlo_reg; /* reg index for low word of guest virtual addr */ + TCGReg addrhi_reg; /* reg index for high word of guest virtual addr */ + TCGReg datalo_reg; /* reg index for low word to be loaded or stored */ + TCGReg datahi_reg; /* reg index for high word to be loaded or stored */ + const tcg_insn_unit *raddr; /* addr of the next IR of qemu_ld/st IR */ + tcg_insn_unit *label_ptr[2]; /* label pointers to be updated */ + QSIMPLEQ_ENTRY(TCGLabelQemuLdst) next; +} TCGLabelQemuLdst; + static void tcg_register_jit_int(const void *buf, size_t size, const void *debug_frame, size_t debug_frame_size) __attribute__((unused)); /* Forward declarations for functions declared and used in tcg-target.c.inc. */ +static void tcg_out_tb_start(TCGContext *s); static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, intptr_t arg2); static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg); static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg ret, tcg_target_long arg); +static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg); +static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg); +static void tcg_out_ext8u(TCGContext *s, TCGReg ret, TCGReg arg); +static void tcg_out_ext16u(TCGContext *s, TCGReg ret, TCGReg arg); +static void tcg_out_ext32s(TCGContext *s, TCGReg ret, TCGReg arg); +static void tcg_out_ext32u(TCGContext *s, TCGReg ret, TCGReg arg); +static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg ret, TCGReg arg); +static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg ret, TCGReg arg); +static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg ret, TCGReg arg); +static void tcg_out_addi_ptr(TCGContext *s, TCGReg, TCGReg, tcg_target_long); +static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2); +static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg); +static void tcg_out_goto_tb(TCGContext *s, int which); static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]); @@ -149,24 +170,78 @@ static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, intptr_t arg2); static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, TCGReg base, intptr_t ofs); -#ifdef CONFIG_TCG_INTERPRETER static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target, - ffi_cif *cif); -#else -static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target); -#endif -static bool tcg_target_const_match(int64_t val, TCGType type, int ct); + const TCGHelperInfo *info); +static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot); +static bool tcg_target_const_match(int64_t val, int ct, + TCGType type, TCGCond cond, int vece); #ifdef TCG_TARGET_NEED_LDST_LABELS static int tcg_out_ldst_finalize(TCGContext *s); #endif +#ifndef CONFIG_USER_ONLY +#define guest_base ({ qemu_build_not_reached(); (uintptr_t)0; }) +#endif + +typedef struct TCGLdstHelperParam { + TCGReg (*ra_gen)(TCGContext *s, const TCGLabelQemuLdst *l, int arg_reg); + unsigned ntmp; + int tmp[3]; +} TCGLdstHelperParam; + +static void tcg_out_ld_helper_args(TCGContext *s, const TCGLabelQemuLdst *l, + const TCGLdstHelperParam *p) + __attribute__((unused)); +static void tcg_out_ld_helper_ret(TCGContext *s, const TCGLabelQemuLdst *l, + bool load_sign, const TCGLdstHelperParam *p) + __attribute__((unused)); +static void tcg_out_st_helper_args(TCGContext *s, const TCGLabelQemuLdst *l, + const TCGLdstHelperParam *p) + __attribute__((unused)); + +static void * const qemu_ld_helpers[MO_SSIZE + 1] __attribute__((unused)) = { + [MO_UB] = helper_ldub_mmu, + [MO_SB] = helper_ldsb_mmu, + [MO_UW] = helper_lduw_mmu, + [MO_SW] = helper_ldsw_mmu, + [MO_UL] = helper_ldul_mmu, + [MO_UQ] = helper_ldq_mmu, +#if TCG_TARGET_REG_BITS == 64 + [MO_SL] = helper_ldsl_mmu, + [MO_128] = helper_ld16_mmu, +#endif +}; + +static void * const qemu_st_helpers[MO_SIZE + 1] __attribute__((unused)) = { + [MO_8] = helper_stb_mmu, + [MO_16] = helper_stw_mmu, + [MO_32] = helper_stl_mmu, + [MO_64] = helper_stq_mmu, +#if TCG_TARGET_REG_BITS == 64 + [MO_128] = helper_st16_mmu, +#endif +}; + +typedef struct { + MemOp atom; /* lg2 bits of atomicity required */ + MemOp align; /* lg2 bits of alignment to use */ +} TCGAtomAlign; + +static TCGAtomAlign atom_and_align_for_opc(TCGContext *s, MemOp opc, + MemOp host_atom, bool allow_two_ops) + __attribute__((unused)); + +#ifdef CONFIG_USER_ONLY +bool tcg_use_softmmu; +#endif + TCGContext tcg_init_ctx; __thread TCGContext *tcg_ctx; TCGContext **tcg_ctxs; unsigned int tcg_cur_ctxs; unsigned int tcg_max_ctxs; -TCGv_env cpu_env = 0; +TCGv_env tcg_env; const void *tcg_code_gen_epilogue; uintptr_t tcg_splitwx_diff; @@ -286,6 +361,7 @@ TCGLabel *gen_new_label(void) memset(l, 0, sizeof(TCGLabel)); l->id = s->nb_labels++; + QSIMPLEQ_INIT(&l->branches); QSIMPLEQ_INIT(&l->relocs); QSIMPLEQ_INSERT_TAIL(&s->labels, l, next); @@ -316,7 +392,32 @@ static void set_jmp_reset_offset(TCGContext *s, int which) * We will check for overflow at the end of the opcode loop in * tcg_gen_code, where we bound tcg_current_code_size to UINT16_MAX. */ - s->tb_jmp_reset_offset[which] = tcg_current_code_size(s); + s->gen_tb->jmp_reset_offset[which] = tcg_current_code_size(s); +} + +static void G_GNUC_UNUSED set_jmp_insn_offset(TCGContext *s, int which) +{ + /* + * We will check for overflow at the end of the opcode loop in + * tcg_gen_code, where we bound tcg_current_code_size to UINT16_MAX. + */ + s->gen_tb->jmp_insn_offset[which] = tcg_current_code_size(s); +} + +static uintptr_t G_GNUC_UNUSED get_jmp_target_addr(TCGContext *s, int which) +{ + /* + * Return the read-execute version of the pointer, for the benefit + * of any pc-relative addressing mode. + */ + return (uintptr_t)tcg_splitwx_to_rx(&s->gen_tb->jmp_target_addr[which]); +} + +static int __attribute__((unused)) +tlb_mask_table_ofs(TCGContext *s, int which) +{ + return (offsetof(CPUNegativeOffsetState, tlb.f[which]) - + sizeof(CPUNegativeOffsetState)); } /* Signal overflow, starting over with fewer guest insns. */ @@ -326,6 +427,213 @@ void tcg_raise_tb_overflow(TCGContext *s) siglongjmp(s->jmp_trans, -2); } +/* + * Used by tcg_out_movext{1,2} to hold the arguments for tcg_out_movext. + * By the time we arrive at tcg_out_movext1, @dst is always a TCGReg. + * + * However, tcg_out_helper_load_slots reuses this field to hold an + * argument slot number (which may designate a argument register or an + * argument stack slot), converting to TCGReg once all arguments that + * are destined for the stack are processed. + */ +typedef struct TCGMovExtend { + unsigned dst; + TCGReg src; + TCGType dst_type; + TCGType src_type; + MemOp src_ext; +} TCGMovExtend; + +/** + * tcg_out_movext -- move and extend + * @s: tcg context + * @dst_type: integral type for destination + * @dst: destination register + * @src_type: integral type for source + * @src_ext: extension to apply to source + * @src: source register + * + * Move or extend @src into @dst, depending on @src_ext and the types. + */ +static void tcg_out_movext(TCGContext *s, TCGType dst_type, TCGReg dst, + TCGType src_type, MemOp src_ext, TCGReg src) +{ + switch (src_ext) { + case MO_UB: + tcg_out_ext8u(s, dst, src); + break; + case MO_SB: + tcg_out_ext8s(s, dst_type, dst, src); + break; + case MO_UW: + tcg_out_ext16u(s, dst, src); + break; + case MO_SW: + tcg_out_ext16s(s, dst_type, dst, src); + break; + case MO_UL: + case MO_SL: + if (dst_type == TCG_TYPE_I32) { + if (src_type == TCG_TYPE_I32) { + tcg_out_mov(s, TCG_TYPE_I32, dst, src); + } else { + tcg_out_extrl_i64_i32(s, dst, src); + } + } else if (src_type == TCG_TYPE_I32) { + if (src_ext & MO_SIGN) { + tcg_out_exts_i32_i64(s, dst, src); + } else { + tcg_out_extu_i32_i64(s, dst, src); + } + } else { + if (src_ext & MO_SIGN) { + tcg_out_ext32s(s, dst, src); + } else { + tcg_out_ext32u(s, dst, src); + } + } + break; + case MO_UQ: + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + if (dst_type == TCG_TYPE_I32) { + tcg_out_extrl_i64_i32(s, dst, src); + } else { + tcg_out_mov(s, TCG_TYPE_I64, dst, src); + } + break; + default: + g_assert_not_reached(); + } +} + +/* Minor variations on a theme, using a structure. */ +static void tcg_out_movext1_new_src(TCGContext *s, const TCGMovExtend *i, + TCGReg src) +{ + tcg_out_movext(s, i->dst_type, i->dst, i->src_type, i->src_ext, src); +} + +static void tcg_out_movext1(TCGContext *s, const TCGMovExtend *i) +{ + tcg_out_movext1_new_src(s, i, i->src); +} + +/** + * tcg_out_movext2 -- move and extend two pair + * @s: tcg context + * @i1: first move description + * @i2: second move description + * @scratch: temporary register, or -1 for none + * + * As tcg_out_movext, for both @i1 and @i2, caring for overlap + * between the sources and destinations. + */ + +static void tcg_out_movext2(TCGContext *s, const TCGMovExtend *i1, + const TCGMovExtend *i2, int scratch) +{ + TCGReg src1 = i1->src; + TCGReg src2 = i2->src; + + if (i1->dst != src2) { + tcg_out_movext1(s, i1); + tcg_out_movext1(s, i2); + return; + } + if (i2->dst == src1) { + TCGType src1_type = i1->src_type; + TCGType src2_type = i2->src_type; + + if (tcg_out_xchg(s, MAX(src1_type, src2_type), src1, src2)) { + /* The data is now in the correct registers, now extend. */ + src1 = i2->src; + src2 = i1->src; + } else { + tcg_debug_assert(scratch >= 0); + tcg_out_mov(s, src1_type, scratch, src1); + src1 = scratch; + } + } + tcg_out_movext1_new_src(s, i2, src2); + tcg_out_movext1_new_src(s, i1, src1); +} + +/** + * tcg_out_movext3 -- move and extend three pair + * @s: tcg context + * @i1: first move description + * @i2: second move description + * @i3: third move description + * @scratch: temporary register, or -1 for none + * + * As tcg_out_movext, for all of @i1, @i2 and @i3, caring for overlap + * between the sources and destinations. + */ + +static void tcg_out_movext3(TCGContext *s, const TCGMovExtend *i1, + const TCGMovExtend *i2, const TCGMovExtend *i3, + int scratch) +{ + TCGReg src1 = i1->src; + TCGReg src2 = i2->src; + TCGReg src3 = i3->src; + + if (i1->dst != src2 && i1->dst != src3) { + tcg_out_movext1(s, i1); + tcg_out_movext2(s, i2, i3, scratch); + return; + } + if (i2->dst != src1 && i2->dst != src3) { + tcg_out_movext1(s, i2); + tcg_out_movext2(s, i1, i3, scratch); + return; + } + if (i3->dst != src1 && i3->dst != src2) { + tcg_out_movext1(s, i3); + tcg_out_movext2(s, i1, i2, scratch); + return; + } + + /* + * There is a cycle. Since there are only 3 nodes, the cycle is + * either "clockwise" or "anti-clockwise", and can be solved with + * a single scratch or two xchg. + */ + if (i1->dst == src2 && i2->dst == src3 && i3->dst == src1) { + /* "Clockwise" */ + if (tcg_out_xchg(s, MAX(i1->src_type, i2->src_type), src1, src2)) { + tcg_out_xchg(s, MAX(i2->src_type, i3->src_type), src2, src3); + /* The data is now in the correct registers, now extend. */ + tcg_out_movext1_new_src(s, i1, i1->dst); + tcg_out_movext1_new_src(s, i2, i2->dst); + tcg_out_movext1_new_src(s, i3, i3->dst); + } else { + tcg_debug_assert(scratch >= 0); + tcg_out_mov(s, i1->src_type, scratch, src1); + tcg_out_movext1(s, i3); + tcg_out_movext1(s, i2); + tcg_out_movext1_new_src(s, i1, scratch); + } + } else if (i1->dst == src3 && i2->dst == src1 && i3->dst == src2) { + /* "Anti-clockwise" */ + if (tcg_out_xchg(s, MAX(i2->src_type, i3->src_type), src2, src3)) { + tcg_out_xchg(s, MAX(i1->src_type, i2->src_type), src1, src2); + /* The data is now in the correct registers, now extend. */ + tcg_out_movext1_new_src(s, i1, i1->dst); + tcg_out_movext1_new_src(s, i2, i2->dst); + tcg_out_movext1_new_src(s, i3, i3->dst); + } else { + tcg_debug_assert(scratch >= 0); + tcg_out_mov(s, i1->src_type, scratch, src1); + tcg_out_movext1(s, i2); + tcg_out_movext1(s, i3); + tcg_out_movext1_new_src(s, i1, scratch); + } + } else { + g_assert_not_reached(); + } +} + #define C_PFX1(P, A) P##A #define C_PFX2(P, A, B) P##A##_##B #define C_PFX3(P, A, B, C) P##A##_##B##_##C @@ -346,11 +654,14 @@ void tcg_raise_tb_overflow(TCGContext *s) #define C_O1_I4(O1, I1, I2, I3, I4) C_PFX5(c_o1_i4_, O1, I1, I2, I3, I4), #define C_N1_I2(O1, I1, I2) C_PFX3(c_n1_i2_, O1, I1, I2), +#define C_N1O1_I1(O1, O2, I1) C_PFX3(c_n1o1_i1_, O1, O2, I1), +#define C_N2_I1(O1, O2, I1) C_PFX3(c_n2_i1_, O1, O2, I1), #define C_O2_I1(O1, O2, I1) C_PFX3(c_o2_i1_, O1, O2, I1), #define C_O2_I2(O1, O2, I1, I2) C_PFX4(c_o2_i2_, O1, O2, I1, I2), #define C_O2_I3(O1, O2, I1, I2, I3) C_PFX5(c_o2_i3_, O1, O2, I1, I2, I3), #define C_O2_I4(O1, O2, I1, I2, I3, I4) C_PFX6(c_o2_i4_, O1, O2, I1, I2, I3, I4), +#define C_N1_O1_I4(O1, O2, I1, I2, I3, I4) C_PFX6(c_n1_o1_i4_, O1, O2, I1, I2, I3, I4), typedef enum { #include "tcg-target-con-set.h" @@ -367,10 +678,13 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode); #undef C_O1_I3 #undef C_O1_I4 #undef C_N1_I2 +#undef C_N1O1_I1 +#undef C_N2_I1 #undef C_O2_I1 #undef C_O2_I2 #undef C_O2_I3 #undef C_O2_I4 +#undef C_N1_O1_I4 /* Put all of the constraint sets into an array, indexed by the enum. */ @@ -385,11 +699,14 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode); #define C_O1_I4(O1, I1, I2, I3, I4) { .args_ct_str = { #O1, #I1, #I2, #I3, #I4 } }, #define C_N1_I2(O1, I1, I2) { .args_ct_str = { "&" #O1, #I1, #I2 } }, +#define C_N1O1_I1(O1, O2, I1) { .args_ct_str = { "&" #O1, #O2, #I1 } }, +#define C_N2_I1(O1, O2, I1) { .args_ct_str = { "&" #O1, "&" #O2, #I1 } }, #define C_O2_I1(O1, O2, I1) { .args_ct_str = { #O1, #O2, #I1 } }, #define C_O2_I2(O1, O2, I1, I2) { .args_ct_str = { #O1, #O2, #I1, #I2 } }, #define C_O2_I3(O1, O2, I1, I2, I3) { .args_ct_str = { #O1, #O2, #I1, #I2, #I3 } }, #define C_O2_I4(O1, O2, I1, I2, I3, I4) { .args_ct_str = { #O1, #O2, #I1, #I2, #I3, #I4 } }, +#define C_N1_O1_I4(O1, O2, I1, I2, I3, I4) { .args_ct_str = { "&" #O1, #O2, #I1, #I2, #I3, #I4 } }, static const TCGTargetOpDef constraint_sets[] = { #include "tcg-target-con-set.h" @@ -405,10 +722,13 @@ static const TCGTargetOpDef constraint_sets[] = { #undef C_O1_I3 #undef C_O1_I4 #undef C_N1_I2 +#undef C_N1O1_I1 +#undef C_N2_I1 #undef C_O2_I1 #undef C_O2_I2 #undef C_O2_I3 #undef C_O2_I4 +#undef C_N1_O1_I4 /* Expand the enumerator to be returned from tcg_target_op_def(). */ @@ -423,14 +743,24 @@ static const TCGTargetOpDef constraint_sets[] = { #define C_O1_I4(O1, I1, I2, I3, I4) C_PFX5(c_o1_i4_, O1, I1, I2, I3, I4) #define C_N1_I2(O1, I1, I2) C_PFX3(c_n1_i2_, O1, I1, I2) +#define C_N1O1_I1(O1, O2, I1) C_PFX3(c_n1o1_i1_, O1, O2, I1) +#define C_N2_I1(O1, O2, I1) C_PFX3(c_n2_i1_, O1, O2, I1) #define C_O2_I1(O1, O2, I1) C_PFX3(c_o2_i1_, O1, O2, I1) #define C_O2_I2(O1, O2, I1, I2) C_PFX4(c_o2_i2_, O1, O2, I1, I2) #define C_O2_I3(O1, O2, I1, I2, I3) C_PFX5(c_o2_i3_, O1, O2, I1, I2, I3) #define C_O2_I4(O1, O2, I1, I2, I3, I4) C_PFX6(c_o2_i4_, O1, O2, I1, I2, I3, I4) +#define C_N1_O1_I4(O1, O2, I1, I2, I3, I4) C_PFX6(c_n1_o1_i4_, O1, O2, I1, I2, I3, I4) #include "tcg-target.c.inc" +#ifndef CONFIG_TCG_INTERPRETER +/* Validate CPUTLBDescFast placement. */ +QEMU_BUILD_BUG_ON((int)(offsetof(CPUNegativeOffsetState, tlb.f[0]) - + sizeof(CPUNegativeOffsetState)) + < MIN_TLB_MASK_TABLE_OFS); +#endif + static void alloc_tcg_plugin_context(TCGContext *s) { #ifdef CONFIG_PLUGIN @@ -448,12 +778,13 @@ static void alloc_tcg_plugin_context(TCGContext *s) * In user-mode we just point tcg_ctx to tcg_init_ctx. See the documentation * of tcg_region_init() for the reasoning behind this. * - * In softmmu each caller registers its context in tcg_ctxs[]. Note that in - * softmmu tcg_ctxs[] does not track tcg_ctx_init, since the initial context + * In system-mode each caller registers its context in tcg_ctxs[]. Note that in + * system-mode tcg_ctxs[] does not track tcg_ctx_init, since the initial context * is not used anymore for translation once this function is called. * - * Not tracking tcg_init_ctx in tcg_ctxs[] in softmmu keeps code that iterates - * over the array (e.g. tcg_code_size() the same for both softmmu and user-mode. + * Not tracking tcg_init_ctx in tcg_ctxs[] in system-mode keeps code that + * iterates over the array (e.g. tcg_code_size() the same for both system/user + * modes. */ #ifdef CONFIG_USER_ONLY void tcg_register_thread(void) @@ -496,7 +827,7 @@ void *tcg_malloc_internal(TCGContext *s, int size) { TCGPool *p; int pool_size; - + if (size > TCG_POOL_CHUNK_SIZE) { /* big malloc: insert a new pool (XXX: could optimize) */ p = g_malloc(sizeof(TCGPool) + size); @@ -517,10 +848,11 @@ void *tcg_malloc_internal(TCGContext *s, int size) p = g_malloc(sizeof(TCGPool) + pool_size); p->size = pool_size; p->next = NULL; - if (s->pool_current) + if (s->pool_current) { s->pool_current->next = p; - else + } else { s->pool_first = p; + } } else { p = p->next; } @@ -544,25 +876,431 @@ void tcg_pool_reset(TCGContext *s) s->pool_current = NULL; } -#include "exec/helper-proto.h" +/* + * Create TCGHelperInfo structures for "tcg/tcg-ldst.h" functions, + * akin to what "exec/helper-tcg.h" does with DEF_HELPER_FLAGS_N. + * We only use these for layout in tcg_out_ld_helper_ret and + * tcg_out_st_helper_args, and share them between several of + * the helpers, with the end result that it's easier to build manually. + */ -static const TCGHelperInfo all_helpers[] = { -#include "exec/helper-tcg.h" +#if TCG_TARGET_REG_BITS == 32 +# define dh_typecode_ttl dh_typecode_i32 +#else +# define dh_typecode_ttl dh_typecode_i64 +#endif + +static TCGHelperInfo info_helper_ld32_mmu = { + .flags = TCG_CALL_NO_WG, + .typemask = dh_typemask(ttl, 0) /* return tcg_target_ulong */ + | dh_typemask(env, 1) + | dh_typemask(i64, 2) /* uint64_t addr */ + | dh_typemask(i32, 3) /* unsigned oi */ + | dh_typemask(ptr, 4) /* uintptr_t ra */ +}; + +static TCGHelperInfo info_helper_ld64_mmu = { + .flags = TCG_CALL_NO_WG, + .typemask = dh_typemask(i64, 0) /* return uint64_t */ + | dh_typemask(env, 1) + | dh_typemask(i64, 2) /* uint64_t addr */ + | dh_typemask(i32, 3) /* unsigned oi */ + | dh_typemask(ptr, 4) /* uintptr_t ra */ +}; + +static TCGHelperInfo info_helper_ld128_mmu = { + .flags = TCG_CALL_NO_WG, + .typemask = dh_typemask(i128, 0) /* return Int128 */ + | dh_typemask(env, 1) + | dh_typemask(i64, 2) /* uint64_t addr */ + | dh_typemask(i32, 3) /* unsigned oi */ + | dh_typemask(ptr, 4) /* uintptr_t ra */ +}; + +static TCGHelperInfo info_helper_st32_mmu = { + .flags = TCG_CALL_NO_WG, + .typemask = dh_typemask(void, 0) + | dh_typemask(env, 1) + | dh_typemask(i64, 2) /* uint64_t addr */ + | dh_typemask(i32, 3) /* uint32_t data */ + | dh_typemask(i32, 4) /* unsigned oi */ + | dh_typemask(ptr, 5) /* uintptr_t ra */ +}; + +static TCGHelperInfo info_helper_st64_mmu = { + .flags = TCG_CALL_NO_WG, + .typemask = dh_typemask(void, 0) + | dh_typemask(env, 1) + | dh_typemask(i64, 2) /* uint64_t addr */ + | dh_typemask(i64, 3) /* uint64_t data */ + | dh_typemask(i32, 4) /* unsigned oi */ + | dh_typemask(ptr, 5) /* uintptr_t ra */ +}; + +static TCGHelperInfo info_helper_st128_mmu = { + .flags = TCG_CALL_NO_WG, + .typemask = dh_typemask(void, 0) + | dh_typemask(env, 1) + | dh_typemask(i64, 2) /* uint64_t addr */ + | dh_typemask(i128, 3) /* Int128 data */ + | dh_typemask(i32, 4) /* unsigned oi */ + | dh_typemask(ptr, 5) /* uintptr_t ra */ }; -static GHashTable *helper_table; #ifdef CONFIG_TCG_INTERPRETER -static GHashTable *ffi_table; +static ffi_type *typecode_to_ffi(int argmask) +{ + /* + * libffi does not support __int128_t, so we have forced Int128 + * to use the structure definition instead of the builtin type. + */ + static ffi_type *ffi_type_i128_elements[3] = { + &ffi_type_uint64, + &ffi_type_uint64, + NULL + }; + static ffi_type ffi_type_i128 = { + .size = 16, + .alignment = __alignof__(Int128), + .type = FFI_TYPE_STRUCT, + .elements = ffi_type_i128_elements, + }; -static ffi_type * const typecode_to_ffi[8] = { - [dh_typecode_void] = &ffi_type_void, - [dh_typecode_i32] = &ffi_type_uint32, - [dh_typecode_s32] = &ffi_type_sint32, - [dh_typecode_i64] = &ffi_type_uint64, - [dh_typecode_s64] = &ffi_type_sint64, - [dh_typecode_ptr] = &ffi_type_pointer, -}; -#endif + switch (argmask) { + case dh_typecode_void: + return &ffi_type_void; + case dh_typecode_i32: + return &ffi_type_uint32; + case dh_typecode_s32: + return &ffi_type_sint32; + case dh_typecode_i64: + return &ffi_type_uint64; + case dh_typecode_s64: + return &ffi_type_sint64; + case dh_typecode_ptr: + return &ffi_type_pointer; + case dh_typecode_i128: + return &ffi_type_i128; + } + g_assert_not_reached(); +} + +static ffi_cif *init_ffi_layout(TCGHelperInfo *info) +{ + unsigned typemask = info->typemask; + struct { + ffi_cif cif; + ffi_type *args[]; + } *ca; + ffi_status status; + int nargs; + + /* Ignoring the return type, find the last non-zero field. */ + nargs = 32 - clz32(typemask >> 3); + nargs = DIV_ROUND_UP(nargs, 3); + assert(nargs <= MAX_CALL_IARGS); + + ca = g_malloc0(sizeof(*ca) + nargs * sizeof(ffi_type *)); + ca->cif.rtype = typecode_to_ffi(typemask & 7); + ca->cif.nargs = nargs; + + if (nargs != 0) { + ca->cif.arg_types = ca->args; + for (int j = 0; j < nargs; ++j) { + int typecode = extract32(typemask, (j + 1) * 3, 3); + ca->args[j] = typecode_to_ffi(typecode); + } + } + + status = ffi_prep_cif(&ca->cif, FFI_DEFAULT_ABI, nargs, + ca->cif.rtype, ca->cif.arg_types); + assert(status == FFI_OK); + + return &ca->cif; +} + +#define HELPER_INFO_INIT(I) (&(I)->cif) +#define HELPER_INFO_INIT_VAL(I) init_ffi_layout(I) +#else +#define HELPER_INFO_INIT(I) (&(I)->init) +#define HELPER_INFO_INIT_VAL(I) 1 +#endif /* CONFIG_TCG_INTERPRETER */ + +static inline bool arg_slot_reg_p(unsigned arg_slot) +{ + /* + * Split the sizeof away from the comparison to avoid Werror from + * "unsigned < 0 is always false", when iarg_regs is empty. + */ + unsigned nreg = ARRAY_SIZE(tcg_target_call_iarg_regs); + return arg_slot < nreg; +} + +static inline int arg_slot_stk_ofs(unsigned arg_slot) +{ + unsigned max = TCG_STATIC_CALL_ARGS_SIZE / sizeof(tcg_target_long); + unsigned stk_slot = arg_slot - ARRAY_SIZE(tcg_target_call_iarg_regs); + + tcg_debug_assert(stk_slot < max); + return TCG_TARGET_CALL_STACK_OFFSET + stk_slot * sizeof(tcg_target_long); +} + +typedef struct TCGCumulativeArgs { + int arg_idx; /* tcg_gen_callN args[] */ + int info_in_idx; /* TCGHelperInfo in[] */ + int arg_slot; /* regs+stack slot */ + int ref_slot; /* stack slots for references */ +} TCGCumulativeArgs; + +static void layout_arg_even(TCGCumulativeArgs *cum) +{ + cum->arg_slot += cum->arg_slot & 1; +} + +static void layout_arg_1(TCGCumulativeArgs *cum, TCGHelperInfo *info, + TCGCallArgumentKind kind) +{ + TCGCallArgumentLoc *loc = &info->in[cum->info_in_idx]; + + *loc = (TCGCallArgumentLoc){ + .kind = kind, + .arg_idx = cum->arg_idx, + .arg_slot = cum->arg_slot, + }; + cum->info_in_idx++; + cum->arg_slot++; +} + +static void layout_arg_normal_n(TCGCumulativeArgs *cum, + TCGHelperInfo *info, int n) +{ + TCGCallArgumentLoc *loc = &info->in[cum->info_in_idx]; + + for (int i = 0; i < n; ++i) { + /* Layout all using the same arg_idx, adjusting the subindex. */ + loc[i] = (TCGCallArgumentLoc){ + .kind = TCG_CALL_ARG_NORMAL, + .arg_idx = cum->arg_idx, + .tmp_subindex = i, + .arg_slot = cum->arg_slot + i, + }; + } + cum->info_in_idx += n; + cum->arg_slot += n; +} + +static void layout_arg_by_ref(TCGCumulativeArgs *cum, TCGHelperInfo *info) +{ + TCGCallArgumentLoc *loc = &info->in[cum->info_in_idx]; + int n = 128 / TCG_TARGET_REG_BITS; + + /* The first subindex carries the pointer. */ + layout_arg_1(cum, info, TCG_CALL_ARG_BY_REF); + + /* + * The callee is allowed to clobber memory associated with + * structure pass by-reference. Therefore we must make copies. + * Allocate space from "ref_slot", which will be adjusted to + * follow the parameters on the stack. + */ + loc[0].ref_slot = cum->ref_slot; + + /* + * Subsequent words also go into the reference slot, but + * do not accumulate into the regular arguments. + */ + for (int i = 1; i < n; ++i) { + loc[i] = (TCGCallArgumentLoc){ + .kind = TCG_CALL_ARG_BY_REF_N, + .arg_idx = cum->arg_idx, + .tmp_subindex = i, + .ref_slot = cum->ref_slot + i, + }; + } + cum->info_in_idx += n - 1; /* i=0 accounted for in layout_arg_1 */ + cum->ref_slot += n; +} + +static void init_call_layout(TCGHelperInfo *info) +{ + int max_reg_slots = ARRAY_SIZE(tcg_target_call_iarg_regs); + int max_stk_slots = TCG_STATIC_CALL_ARGS_SIZE / sizeof(tcg_target_long); + unsigned typemask = info->typemask; + unsigned typecode; + TCGCumulativeArgs cum = { }; + + /* + * Parse and place any function return value. + */ + typecode = typemask & 7; + switch (typecode) { + case dh_typecode_void: + info->nr_out = 0; + break; + case dh_typecode_i32: + case dh_typecode_s32: + case dh_typecode_ptr: + info->nr_out = 1; + info->out_kind = TCG_CALL_RET_NORMAL; + break; + case dh_typecode_i64: + case dh_typecode_s64: + info->nr_out = 64 / TCG_TARGET_REG_BITS; + info->out_kind = TCG_CALL_RET_NORMAL; + /* Query the last register now to trigger any assert early. */ + tcg_target_call_oarg_reg(info->out_kind, info->nr_out - 1); + break; + case dh_typecode_i128: + info->nr_out = 128 / TCG_TARGET_REG_BITS; + info->out_kind = TCG_TARGET_CALL_RET_I128; + switch (TCG_TARGET_CALL_RET_I128) { + case TCG_CALL_RET_NORMAL: + /* Query the last register now to trigger any assert early. */ + tcg_target_call_oarg_reg(info->out_kind, info->nr_out - 1); + break; + case TCG_CALL_RET_BY_VEC: + /* Query the single register now to trigger any assert early. */ + tcg_target_call_oarg_reg(TCG_CALL_RET_BY_VEC, 0); + break; + case TCG_CALL_RET_BY_REF: + /* + * Allocate the first argument to the output. + * We don't need to store this anywhere, just make it + * unavailable for use in the input loop below. + */ + cum.arg_slot = 1; + break; + default: + qemu_build_not_reached(); + } + break; + default: + g_assert_not_reached(); + } + + /* + * Parse and place function arguments. + */ + for (typemask >>= 3; typemask; typemask >>= 3, cum.arg_idx++) { + TCGCallArgumentKind kind; + TCGType type; + + typecode = typemask & 7; + switch (typecode) { + case dh_typecode_i32: + case dh_typecode_s32: + type = TCG_TYPE_I32; + break; + case dh_typecode_i64: + case dh_typecode_s64: + type = TCG_TYPE_I64; + break; + case dh_typecode_ptr: + type = TCG_TYPE_PTR; + break; + case dh_typecode_i128: + type = TCG_TYPE_I128; + break; + default: + g_assert_not_reached(); + } + + switch (type) { + case TCG_TYPE_I32: + switch (TCG_TARGET_CALL_ARG_I32) { + case TCG_CALL_ARG_EVEN: + layout_arg_even(&cum); + /* fall through */ + case TCG_CALL_ARG_NORMAL: + layout_arg_1(&cum, info, TCG_CALL_ARG_NORMAL); + break; + case TCG_CALL_ARG_EXTEND: + kind = TCG_CALL_ARG_EXTEND_U + (typecode & 1); + layout_arg_1(&cum, info, kind); + break; + default: + qemu_build_not_reached(); + } + break; + + case TCG_TYPE_I64: + switch (TCG_TARGET_CALL_ARG_I64) { + case TCG_CALL_ARG_EVEN: + layout_arg_even(&cum); + /* fall through */ + case TCG_CALL_ARG_NORMAL: + if (TCG_TARGET_REG_BITS == 32) { + layout_arg_normal_n(&cum, info, 2); + } else { + layout_arg_1(&cum, info, TCG_CALL_ARG_NORMAL); + } + break; + default: + qemu_build_not_reached(); + } + break; + + case TCG_TYPE_I128: + switch (TCG_TARGET_CALL_ARG_I128) { + case TCG_CALL_ARG_EVEN: + layout_arg_even(&cum); + /* fall through */ + case TCG_CALL_ARG_NORMAL: + layout_arg_normal_n(&cum, info, 128 / TCG_TARGET_REG_BITS); + break; + case TCG_CALL_ARG_BY_REF: + layout_arg_by_ref(&cum, info); + break; + default: + qemu_build_not_reached(); + } + break; + + default: + g_assert_not_reached(); + } + } + info->nr_in = cum.info_in_idx; + + /* Validate that we didn't overrun the input array. */ + assert(cum.info_in_idx <= ARRAY_SIZE(info->in)); + /* Validate the backend has enough argument space. */ + assert(cum.arg_slot <= max_reg_slots + max_stk_slots); + + /* + * Relocate the "ref_slot" area to the end of the parameters. + * Minimizing this stack offset helps code size for x86, + * which has a signed 8-bit offset encoding. + */ + if (cum.ref_slot != 0) { + int ref_base = 0; + + if (cum.arg_slot > max_reg_slots) { + int align = __alignof(Int128) / sizeof(tcg_target_long); + + ref_base = cum.arg_slot - max_reg_slots; + if (align > 1) { + ref_base = ROUND_UP(ref_base, align); + } + } + assert(ref_base + cum.ref_slot <= max_stk_slots); + ref_base += max_reg_slots; + + if (ref_base != 0) { + for (int i = cum.info_in_idx - 1; i >= 0; --i) { + TCGCallArgumentLoc *loc = &info->in[i]; + switch (loc->kind) { + case TCG_CALL_ARG_BY_REF: + case TCG_CALL_ARG_BY_REF_N: + loc->ref_slot += ref_base; + break; + default: + break; + } + } + } + } +} static int indirect_reg_alloc_order[ARRAY_SIZE(tcg_target_reg_alloc_order)]; static void process_op_defs(TCGContext *s); @@ -598,55 +1336,12 @@ static void tcg_context_init(unsigned max_cpus) args_ct += n; } - /* Register helpers. */ - /* Use g_direct_hash/equal for direct pointer comparisons on func. */ - helper_table = g_hash_table_new(NULL, NULL); - - for (i = 0; i < ARRAY_SIZE(all_helpers); ++i) { - g_hash_table_insert(helper_table, (gpointer)all_helpers[i].func, - (gpointer)&all_helpers[i]); - } - -#ifdef CONFIG_TCG_INTERPRETER - /* g_direct_hash/equal for direct comparisons on uint32_t. */ - ffi_table = g_hash_table_new(NULL, NULL); - for (i = 0; i < ARRAY_SIZE(all_helpers); ++i) { - struct { - ffi_cif cif; - ffi_type *args[]; - } *ca; - uint32_t typemask = all_helpers[i].typemask; - gpointer hash = (gpointer)(uintptr_t)typemask; - ffi_status status; - int nargs; - - if (g_hash_table_lookup(ffi_table, hash)) { - continue; - } - - /* Ignoring the return type, find the last non-zero field. */ - nargs = 32 - clz32(typemask >> 3); - nargs = DIV_ROUND_UP(nargs, 3); - - ca = g_malloc0(sizeof(*ca) + nargs * sizeof(ffi_type *)); - ca->cif.rtype = typecode_to_ffi[typemask & 7]; - ca->cif.nargs = nargs; - - if (nargs != 0) { - ca->cif.arg_types = ca->args; - for (i = 0; i < nargs; ++i) { - int typecode = extract32(typemask, (i + 1) * 3, 3); - ca->args[i] = typecode_to_ffi[typecode]; - } - } - - status = ffi_prep_cif(&ca->cif, FFI_DEFAULT_ABI, nargs, - ca->cif.rtype, ca->cif.arg_types); - assert(status == FFI_OK); - - g_hash_table_insert(ffi_table, hash, (gpointer)&ca->cif); - } -#endif + init_call_layout(&info_helper_ld32_mmu); + init_call_layout(&info_helper_ld64_mmu); + init_call_layout(&info_helper_ld128_mmu); + init_call_layout(&info_helper_st32_mmu); + init_call_layout(&info_helper_st64_mmu); + init_call_layout(&info_helper_st128_mmu); tcg_target_init(s); process_op_defs(s); @@ -673,7 +1368,7 @@ static void tcg_context_init(unsigned max_cpus) * In user-mode we simply share the init context among threads, since we * use a single region. See the documentation tcg_region_init() for the * reasoning behind this. - * In softmmu we will have at most max_cpus TCG threads. + * In system-mode we will have at most max_cpus TCG threads. */ #ifdef CONFIG_USER_ONLY tcg_ctxs = &tcg_ctx; @@ -686,7 +1381,7 @@ static void tcg_context_init(unsigned max_cpus) tcg_debug_assert(!tcg_regset_test_reg(s->reserved_regs, TCG_AREG0)); ts = tcg_global_reg_new_internal(s, TCG_TYPE_PTR, TCG_AREG0, "env"); - cpu_env = temp_tcgv_ptr(ts); + tcg_env = temp_tcgv_ptr(ts); } void tcg_init(size_t tb_size, int splitwx, unsigned max_cpus) @@ -720,8 +1415,9 @@ TranslationBlock *tcg_tb_alloc(TCGContext *s) return tb; } -void tcg_prologue_init(TCGContext *s) +void tcg_prologue_init(void) { + TCGContext *s = tcg_ctx; size_t prologue_size; s->code_ptr = s->code_gen_ptr; @@ -749,13 +1445,13 @@ void tcg_prologue_init(TCGContext *s) #endif prologue_size = tcg_current_code_size(s); + perf_report_prologue(s->code_gen_ptr, prologue_size); #ifndef CONFIG_TCG_INTERPRETER flush_idcache_range((uintptr_t)tcg_splitwx_to_rx(s->code_buf), (uintptr_t)s->code_buf, prologue_size); #endif -#ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM)) { FILE *logfile = qemu_log_trylock(); if (logfile) { @@ -787,7 +1483,6 @@ void tcg_prologue_init(TCGContext *s) qemu_log_unlock(logfile); } } -#endif #ifndef CONFIG_TCG_INTERPRETER /* @@ -826,7 +1521,13 @@ void tcg_func_start(TCGContext *s) QTAILQ_INIT(&s->ops); QTAILQ_INIT(&s->free_ops); + s->emit_before_op = NULL; QSIMPLEQ_INIT(&s->labels); + + tcg_debug_assert(s->addr_type == TCG_TYPE_I32 || + s->addr_type == TCG_TYPE_I64); + + tcg_debug_assert(s->insn_start_words > 0); } static TCGTemp *tcg_temp_alloc(TCGContext *s) @@ -857,9 +1558,7 @@ static TCGTemp *tcg_global_reg_new_internal(TCGContext *s, TCGType type, { TCGTemp *ts; - if (TCG_TARGET_REG_BITS == 32 && type != TCG_TYPE_I32) { - tcg_abort(); - } + tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); ts = tcg_global_alloc(s); ts->base_type = type; @@ -880,16 +1579,13 @@ void tcg_set_frame(TCGContext *s, TCGReg reg, intptr_t start, intptr_t size) = tcg_global_reg_new_internal(s, TCG_TYPE_PTR, reg, "_frame"); } -TCGTemp *tcg_global_mem_new_internal(TCGType type, TCGv_ptr base, - intptr_t offset, const char *name) +static TCGTemp *tcg_global_mem_new_internal(TCGv_ptr base, intptr_t offset, + const char *name, TCGType type) { TCGContext *s = tcg_ctx; TCGTemp *base_ts = tcgv_ptr_temp(base); TCGTemp *ts = tcg_global_alloc(s); - int indirect_reg = 0, bigendian = 0; -#if HOST_BIG_ENDIAN - bigendian = 1; -#endif + int indirect_reg = 0; switch (base_ts->kind) { case TEMP_FIXED: @@ -915,7 +1611,7 @@ TCGTemp *tcg_global_mem_new_internal(TCGType type, TCGv_ptr base, ts->indirect_reg = indirect_reg; ts->mem_allocated = 1; ts->mem_base = base_ts; - ts->mem_offset = offset + bigendian * 4; + ts->mem_offset = offset; pstrcpy(buf, sizeof(buf), name); pstrcat(buf, sizeof(buf), "_0"); ts->name = strdup(buf); @@ -926,7 +1622,8 @@ TCGTemp *tcg_global_mem_new_internal(TCGType type, TCGv_ptr base, ts2->indirect_reg = indirect_reg; ts2->mem_allocated = 1; ts2->mem_base = base_ts; - ts2->mem_offset = offset + (1 - bigendian) * 4; + ts2->mem_offset = offset + 4; + ts2->temp_subindex = 1; pstrcpy(buf, sizeof(buf), name); pstrcat(buf, sizeof(buf), "_1"); ts2->name = strdup(buf); @@ -942,52 +1639,128 @@ TCGTemp *tcg_global_mem_new_internal(TCGType type, TCGv_ptr base, return ts; } -TCGTemp *tcg_temp_new_internal(TCGType type, bool temp_local) +TCGv_i32 tcg_global_mem_new_i32(TCGv_ptr reg, intptr_t off, const char *name) +{ + TCGTemp *ts = tcg_global_mem_new_internal(reg, off, name, TCG_TYPE_I32); + return temp_tcgv_i32(ts); +} + +TCGv_i64 tcg_global_mem_new_i64(TCGv_ptr reg, intptr_t off, const char *name) +{ + TCGTemp *ts = tcg_global_mem_new_internal(reg, off, name, TCG_TYPE_I64); + return temp_tcgv_i64(ts); +} + +TCGv_ptr tcg_global_mem_new_ptr(TCGv_ptr reg, intptr_t off, const char *name) +{ + TCGTemp *ts = tcg_global_mem_new_internal(reg, off, name, TCG_TYPE_PTR); + return temp_tcgv_ptr(ts); +} + +TCGTemp *tcg_temp_new_internal(TCGType type, TCGTempKind kind) { TCGContext *s = tcg_ctx; - TCGTempKind kind = temp_local ? TEMP_LOCAL : TEMP_NORMAL; TCGTemp *ts; - int idx, k; + int n; - k = type + (temp_local ? TCG_TYPE_COUNT : 0); - idx = find_first_bit(s->free_temps[k].l, TCG_MAX_TEMPS); - if (idx < TCG_MAX_TEMPS) { - /* There is already an available temp with the right type. */ - clear_bit(idx, s->free_temps[k].l); + if (kind == TEMP_EBB) { + int idx = find_first_bit(s->free_temps[type].l, TCG_MAX_TEMPS); - ts = &s->temps[idx]; - ts->temp_allocated = 1; - tcg_debug_assert(ts->base_type == type); - tcg_debug_assert(ts->kind == kind); - } else { - ts = tcg_temp_alloc(s); - if (TCG_TARGET_REG_BITS == 32 && type == TCG_TYPE_I64) { - TCGTemp *ts2 = tcg_temp_alloc(s); + if (idx < TCG_MAX_TEMPS) { + /* There is already an available temp with the right type. */ + clear_bit(idx, s->free_temps[type].l); - ts->base_type = type; - ts->type = TCG_TYPE_I32; + ts = &s->temps[idx]; ts->temp_allocated = 1; - ts->kind = kind; - - tcg_debug_assert(ts2 == ts + 1); - ts2->base_type = TCG_TYPE_I64; - ts2->type = TCG_TYPE_I32; - ts2->temp_allocated = 1; - ts2->kind = kind; - } else { - ts->base_type = type; - ts->type = type; - ts->temp_allocated = 1; - ts->kind = kind; + tcg_debug_assert(ts->base_type == type); + tcg_debug_assert(ts->kind == kind); + return ts; } + } else { + tcg_debug_assert(kind == TEMP_TB); } -#if defined(CONFIG_DEBUG_TCG) - s->temps_in_use++; -#endif + switch (type) { + case TCG_TYPE_I32: + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + n = 1; + break; + case TCG_TYPE_I64: + n = 64 / TCG_TARGET_REG_BITS; + break; + case TCG_TYPE_I128: + n = 128 / TCG_TARGET_REG_BITS; + break; + default: + g_assert_not_reached(); + } + + ts = tcg_temp_alloc(s); + ts->base_type = type; + ts->temp_allocated = 1; + ts->kind = kind; + + if (n == 1) { + ts->type = type; + } else { + ts->type = TCG_TYPE_REG; + + for (int i = 1; i < n; ++i) { + TCGTemp *ts2 = tcg_temp_alloc(s); + + tcg_debug_assert(ts2 == ts + i); + ts2->base_type = type; + ts2->type = TCG_TYPE_REG; + ts2->temp_allocated = 1; + ts2->temp_subindex = i; + ts2->kind = kind; + } + } return ts; } +TCGv_i32 tcg_temp_new_i32(void) +{ + return temp_tcgv_i32(tcg_temp_new_internal(TCG_TYPE_I32, TEMP_TB)); +} + +TCGv_i32 tcg_temp_ebb_new_i32(void) +{ + return temp_tcgv_i32(tcg_temp_new_internal(TCG_TYPE_I32, TEMP_EBB)); +} + +TCGv_i64 tcg_temp_new_i64(void) +{ + return temp_tcgv_i64(tcg_temp_new_internal(TCG_TYPE_I64, TEMP_TB)); +} + +TCGv_i64 tcg_temp_ebb_new_i64(void) +{ + return temp_tcgv_i64(tcg_temp_new_internal(TCG_TYPE_I64, TEMP_EBB)); +} + +TCGv_ptr tcg_temp_new_ptr(void) +{ + return temp_tcgv_ptr(tcg_temp_new_internal(TCG_TYPE_PTR, TEMP_TB)); +} + +TCGv_ptr tcg_temp_ebb_new_ptr(void) +{ + return temp_tcgv_ptr(tcg_temp_new_internal(TCG_TYPE_PTR, TEMP_EBB)); +} + +TCGv_i128 tcg_temp_new_i128(void) +{ + return temp_tcgv_i128(tcg_temp_new_internal(TCG_TYPE_I128, TEMP_TB)); +} + +TCGv_i128 tcg_temp_ebb_new_i128(void) +{ + return temp_tcgv_i128(tcg_temp_new_internal(TCG_TYPE_I128, TEMP_EBB)); +} + TCGv_vec tcg_temp_new_vec(TCGType type) { TCGTemp *t; @@ -1008,7 +1781,7 @@ TCGv_vec tcg_temp_new_vec(TCGType type) } #endif - t = tcg_temp_new_internal(type, 0); + t = tcg_temp_new_internal(type, TEMP_EBB); return temp_tcgv_vec(t); } @@ -1019,42 +1792,53 @@ TCGv_vec tcg_temp_new_vec_matching(TCGv_vec match) tcg_debug_assert(t->temp_allocated != 0); - t = tcg_temp_new_internal(t->base_type, 0); + t = tcg_temp_new_internal(t->base_type, TEMP_EBB); return temp_tcgv_vec(t); } void tcg_temp_free_internal(TCGTemp *ts) { TCGContext *s = tcg_ctx; - int k, idx; switch (ts->kind) { case TEMP_CONST: - /* - * In order to simplify users of tcg_constant_*, - * silently ignore free. - */ - return; - case TEMP_NORMAL: - case TEMP_LOCAL: + case TEMP_TB: + /* Silently ignore free. */ + break; + case TEMP_EBB: + tcg_debug_assert(ts->temp_allocated != 0); + ts->temp_allocated = 0; + set_bit(temp_idx(ts), s->free_temps[ts->base_type].l); break; default: + /* It never made sense to free TEMP_FIXED or TEMP_GLOBAL. */ g_assert_not_reached(); } +} -#if defined(CONFIG_DEBUG_TCG) - s->temps_in_use--; - if (s->temps_in_use < 0) { - fprintf(stderr, "More temporaries freed than allocated!\n"); - } -#endif +void tcg_temp_free_i32(TCGv_i32 arg) +{ + tcg_temp_free_internal(tcgv_i32_temp(arg)); +} - tcg_debug_assert(ts->temp_allocated != 0); - ts->temp_allocated = 0; +void tcg_temp_free_i64(TCGv_i64 arg) +{ + tcg_temp_free_internal(tcgv_i64_temp(arg)); +} - idx = temp_idx(ts); - k = ts->base_type + (ts->kind == TEMP_NORMAL ? 0 : TCG_TYPE_COUNT); - set_bit(idx, s->free_temps[k].l); +void tcg_temp_free_i128(TCGv_i128 arg) +{ + tcg_temp_free_internal(tcgv_i128_temp(arg)); +} + +void tcg_temp_free_ptr(TCGv_ptr arg) +{ + tcg_temp_free_internal(tcgv_ptr_temp(arg)); +} + +void tcg_temp_free_vec(TCGv_vec arg) +{ + tcg_temp_free_internal(tcgv_vec_temp(arg)); } TCGTemp *tcg_constant_internal(TCGType type, int64_t val) @@ -1070,41 +1854,63 @@ TCGTemp *tcg_constant_internal(TCGType type, int64_t val) ts = g_hash_table_lookup(h, &val); if (ts == NULL) { + int64_t *val_ptr; + ts = tcg_temp_alloc(s); if (TCG_TARGET_REG_BITS == 32 && type == TCG_TYPE_I64) { TCGTemp *ts2 = tcg_temp_alloc(s); + tcg_debug_assert(ts2 == ts + 1); + ts->base_type = TCG_TYPE_I64; ts->type = TCG_TYPE_I32; ts->kind = TEMP_CONST; ts->temp_allocated = 1; + + ts2->base_type = TCG_TYPE_I64; + ts2->type = TCG_TYPE_I32; + ts2->kind = TEMP_CONST; + ts2->temp_allocated = 1; + ts2->temp_subindex = 1; + /* * Retain the full value of the 64-bit constant in the low * part, so that the hash table works. Actual uses will * truncate the value to the low part. */ - ts->val = val; - - tcg_debug_assert(ts2 == ts + 1); - ts2->base_type = TCG_TYPE_I64; - ts2->type = TCG_TYPE_I32; - ts2->kind = TEMP_CONST; - ts2->temp_allocated = 1; - ts2->val = val >> 32; + ts[HOST_BIG_ENDIAN].val = val; + ts[!HOST_BIG_ENDIAN].val = val >> 32; + val_ptr = &ts[HOST_BIG_ENDIAN].val; } else { ts->base_type = type; ts->type = type; ts->kind = TEMP_CONST; ts->temp_allocated = 1; ts->val = val; + val_ptr = &ts->val; } - g_hash_table_insert(h, &ts->val, ts); + g_hash_table_insert(h, val_ptr, ts); } return ts; } +TCGv_i32 tcg_constant_i32(int32_t val) +{ + return temp_tcgv_i32(tcg_constant_internal(TCG_TYPE_I32, val)); +} + +TCGv_i64 tcg_constant_i64(int64_t val) +{ + return temp_tcgv_i64(tcg_constant_internal(TCG_TYPE_I64, val)); +} + +TCGv_ptr tcg_constant_ptr_int(intptr_t val) +{ + return temp_tcgv_ptr(tcg_constant_internal(TCG_TYPE_PTR, val)); +} + TCGv_vec tcg_constant_vec(TCGType type, unsigned vece, int64_t val) { val = dup_const(vece, val); @@ -1119,58 +1925,24 @@ TCGv_vec tcg_constant_vec_matching(TCGv_vec match, unsigned vece, int64_t val) return tcg_constant_vec(t->base_type, vece, val); } -TCGv_i32 tcg_const_i32(int32_t val) +#ifdef CONFIG_DEBUG_TCG +size_t temp_idx(TCGTemp *ts) { - TCGv_i32 t0; - t0 = tcg_temp_new_i32(); - tcg_gen_movi_i32(t0, val); - return t0; + ptrdiff_t n = ts - tcg_ctx->temps; + assert(n >= 0 && n < tcg_ctx->nb_temps); + return n; } -TCGv_i64 tcg_const_i64(int64_t val) +TCGTemp *tcgv_i32_temp(TCGv_i32 v) { - TCGv_i64 t0; - t0 = tcg_temp_new_i64(); - tcg_gen_movi_i64(t0, val); - return t0; -} + uintptr_t o = (uintptr_t)v - offsetof(TCGContext, temps); -TCGv_i32 tcg_const_local_i32(int32_t val) -{ - TCGv_i32 t0; - t0 = tcg_temp_local_new_i32(); - tcg_gen_movi_i32(t0, val); - return t0; -} + assert(o < sizeof(TCGTemp) * tcg_ctx->nb_temps); + assert(o % sizeof(TCGTemp) == 0); -TCGv_i64 tcg_const_local_i64(int64_t val) -{ - TCGv_i64 t0; - t0 = tcg_temp_local_new_i64(); - tcg_gen_movi_i64(t0, val); - return t0; + return (void *)tcg_ctx + (uintptr_t)v; } - -#if defined(CONFIG_DEBUG_TCG) -void tcg_clear_temp_count(void) -{ - TCGContext *s = tcg_ctx; - s->temps_in_use = 0; -} - -int tcg_check_temp_count(void) -{ - TCGContext *s = tcg_ctx; - if (s->temps_in_use) { - /* Clear the count so that we don't give another - * warning immediately next time around. - */ - s->temps_in_use = 0; - return 1; - } - return 0; -} -#endif +#endif /* CONFIG_DEBUG_TCG */ /* Return true if OP may appear in the opcode stream. Test the runtime variable that controls each opcode. */ @@ -1189,18 +1961,30 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_exit_tb: case INDEX_op_goto_tb: case INDEX_op_goto_ptr: - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_ld_i64: - case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: return true; - case INDEX_op_qemu_st8_i32: + case INDEX_op_qemu_st8_a32_i32: + case INDEX_op_qemu_st8_a64_i32: return TCG_TARGET_HAS_qemu_st8_i32; + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: + return TCG_TARGET_HAS_qemu_ldst_i128; + case INDEX_op_mov_i32: case INDEX_op_setcond_i32: case INDEX_op_brcond_i32: + case INDEX_op_movcond_i32: case INDEX_op_ld8u_i32: case INDEX_op_ld8s_i32: case INDEX_op_ld16u_i32: @@ -1211,6 +1995,7 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_st_i32: case INDEX_op_add_i32: case INDEX_op_sub_i32: + case INDEX_op_neg_i32: case INDEX_op_mul_i32: case INDEX_op_and_i32: case INDEX_op_or_i32: @@ -1220,8 +2005,8 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_sar_i32: return true; - case INDEX_op_movcond_i32: - return TCG_TARGET_HAS_movcond_i32; + case INDEX_op_negsetcond_i32: + return TCG_TARGET_HAS_negsetcond_i32; case INDEX_op_div_i32: case INDEX_op_divu_i32: return TCG_TARGET_HAS_div_i32; @@ -1268,8 +2053,6 @@ bool tcg_op_supported(TCGOpcode op) return TCG_TARGET_HAS_bswap32_i32; case INDEX_op_not_i32: return TCG_TARGET_HAS_not_i32; - case INDEX_op_neg_i32: - return TCG_TARGET_HAS_neg_i32; case INDEX_op_andc_i32: return TCG_TARGET_HAS_andc_i32; case INDEX_op_orc_i32: @@ -1294,6 +2077,7 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_mov_i64: case INDEX_op_setcond_i64: case INDEX_op_brcond_i64: + case INDEX_op_movcond_i64: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i64: case INDEX_op_ld16u_i64: @@ -1307,6 +2091,7 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_st_i64: case INDEX_op_add_i64: case INDEX_op_sub_i64: + case INDEX_op_neg_i64: case INDEX_op_mul_i64: case INDEX_op_and_i64: case INDEX_op_or_i64: @@ -1318,8 +2103,8 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_extu_i32_i64: return TCG_TARGET_REG_BITS == 64; - case INDEX_op_movcond_i64: - return TCG_TARGET_HAS_movcond_i64; + case INDEX_op_negsetcond_i64: + return TCG_TARGET_HAS_negsetcond_i64; case INDEX_op_div_i64: case INDEX_op_divu_i64: return TCG_TARGET_HAS_div_i64; @@ -1341,9 +2126,8 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_extract2_i64: return TCG_TARGET_HAS_extract2_i64; case INDEX_op_extrl_i64_i32: - return TCG_TARGET_HAS_extrl_i64_i32; case INDEX_op_extrh_i64_i32: - return TCG_TARGET_HAS_extrh_i64_i32; + return TCG_TARGET_HAS_extr_i64_i32; case INDEX_op_ext8s_i64: return TCG_TARGET_HAS_ext8s_i64; case INDEX_op_ext16s_i64: @@ -1364,8 +2148,6 @@ bool tcg_op_supported(TCGOpcode op) return TCG_TARGET_HAS_bswap64_i64; case INDEX_op_not_i64: return TCG_TARGET_HAS_not_i64; - case INDEX_op_neg_i64: - return TCG_TARGET_HAS_neg_i64; case INDEX_op_andc_i64: return TCG_TARGET_HAS_andc_i64; case INDEX_op_orc_i64: @@ -1467,205 +2249,152 @@ bool tcg_op_supported(TCGOpcode op) } } -/* Note: we convert the 64 bit args to 32 bit and do some alignment - and endian swap. Maybe it would be better to do the alignment - and endian swap in tcg_reg_alloc_call(). */ -void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args) -{ - int i, real_args, nb_rets, pi; - unsigned typemask; - const TCGHelperInfo *info; - TCGOp *op; +static TCGOp *tcg_op_alloc(TCGOpcode opc, unsigned nargs); - info = g_hash_table_lookup(helper_table, (gpointer)func); - typemask = info->typemask; +static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args) +{ + TCGv_i64 extend_free[MAX_CALL_IARGS]; + int n_extend = 0; + TCGOp *op; + int i, n, pi = 0, total_args; + + if (unlikely(g_once_init_enter(HELPER_INFO_INIT(info)))) { + init_call_layout(info); + g_once_init_leave(HELPER_INFO_INIT(info), HELPER_INFO_INIT_VAL(info)); + } + + total_args = info->nr_out + info->nr_in + 2; + op = tcg_op_alloc(INDEX_op_call, total_args); #ifdef CONFIG_PLUGIN - /* detect non-plugin helpers */ - if (tcg_ctx->plugin_insn && unlikely(strncmp(info->name, "plugin_", 7))) { + /* Flag helpers that may affect guest state */ + if (tcg_ctx->plugin_insn && + !(info->flags & TCG_CALL_PLUGIN) && + !(info->flags & TCG_CALL_NO_SIDE_EFFECTS)) { tcg_ctx->plugin_insn->calls_helpers = true; } #endif -#if defined(__sparc__) && !defined(__arch64__) \ - && !defined(CONFIG_TCG_INTERPRETER) - /* We have 64-bit values in one register, but need to pass as two - separate parameters. Split them. */ - int orig_typemask = typemask; - int orig_nargs = nargs; - TCGv_i64 retl, reth; - TCGTemp *split_args[MAX_OPC_PARAM]; - - retl = NULL; - reth = NULL; - typemask = 0; - for (i = real_args = 0; i < nargs; ++i) { - int argtype = extract32(orig_typemask, (i + 1) * 3, 3); - bool is_64bit = (argtype & ~1) == dh_typecode_i64; - - if (is_64bit) { - TCGv_i64 orig = temp_tcgv_i64(args[i]); - TCGv_i32 h = tcg_temp_new_i32(); - TCGv_i32 l = tcg_temp_new_i32(); - tcg_gen_extr_i64_i32(l, h, orig); - split_args[real_args++] = tcgv_i32_temp(h); - typemask |= dh_typecode_i32 << (real_args * 3); - split_args[real_args++] = tcgv_i32_temp(l); - typemask |= dh_typecode_i32 << (real_args * 3); - } else { - split_args[real_args++] = args[i]; - typemask |= argtype << (real_args * 3); + TCGOP_CALLO(op) = n = info->nr_out; + switch (n) { + case 0: + tcg_debug_assert(ret == NULL); + break; + case 1: + tcg_debug_assert(ret != NULL); + op->args[pi++] = temp_arg(ret); + break; + case 2: + case 4: + tcg_debug_assert(ret != NULL); + tcg_debug_assert(ret->base_type == ret->type + ctz32(n)); + tcg_debug_assert(ret->temp_subindex == 0); + for (i = 0; i < n; ++i) { + op->args[pi++] = temp_arg(ret + i); } + break; + default: + g_assert_not_reached(); } - nargs = real_args; - args = split_args; -#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64 - for (i = 0; i < nargs; ++i) { - int argtype = extract32(typemask, (i + 1) * 3, 3); - bool is_32bit = (argtype & ~1) == dh_typecode_i32; - bool is_signed = argtype & 1; - if (is_32bit) { - TCGv_i64 temp = tcg_temp_new_i64(); - TCGv_i32 orig = temp_tcgv_i32(args[i]); - if (is_signed) { - tcg_gen_ext_i32_i64(temp, orig); - } else { - tcg_gen_extu_i32_i64(temp, orig); + TCGOP_CALLI(op) = n = info->nr_in; + for (i = 0; i < n; i++) { + const TCGCallArgumentLoc *loc = &info->in[i]; + TCGTemp *ts = args[loc->arg_idx] + loc->tmp_subindex; + + switch (loc->kind) { + case TCG_CALL_ARG_NORMAL: + case TCG_CALL_ARG_BY_REF: + case TCG_CALL_ARG_BY_REF_N: + op->args[pi++] = temp_arg(ts); + break; + + case TCG_CALL_ARG_EXTEND_U: + case TCG_CALL_ARG_EXTEND_S: + { + TCGv_i64 temp = tcg_temp_ebb_new_i64(); + TCGv_i32 orig = temp_tcgv_i32(ts); + + if (loc->kind == TCG_CALL_ARG_EXTEND_S) { + tcg_gen_ext_i32_i64(temp, orig); + } else { + tcg_gen_extu_i32_i64(temp, orig); + } + op->args[pi++] = tcgv_i64_arg(temp); + extend_free[n_extend++] = temp; } - args[i] = tcgv_i64_temp(temp); + break; + + default: + g_assert_not_reached(); } } -#endif /* TCG_TARGET_EXTEND_ARGS */ - - op = tcg_emit_op(INDEX_op_call); - - pi = 0; - if (ret != NULL) { -#if defined(__sparc__) && !defined(__arch64__) \ - && !defined(CONFIG_TCG_INTERPRETER) - if ((typemask & 6) == dh_typecode_i64) { - /* The 32-bit ABI is going to return the 64-bit value in - the %o0/%o1 register pair. Prepare for this by using - two return temporaries, and reassemble below. */ - retl = tcg_temp_new_i64(); - reth = tcg_temp_new_i64(); - op->args[pi++] = tcgv_i64_arg(reth); - op->args[pi++] = tcgv_i64_arg(retl); - nb_rets = 2; - } else { - op->args[pi++] = temp_arg(ret); - nb_rets = 1; - } -#else - if (TCG_TARGET_REG_BITS < 64 && (typemask & 6) == dh_typecode_i64) { -#if HOST_BIG_ENDIAN - op->args[pi++] = temp_arg(ret + 1); - op->args[pi++] = temp_arg(ret); -#else - op->args[pi++] = temp_arg(ret); - op->args[pi++] = temp_arg(ret + 1); -#endif - nb_rets = 2; - } else { - op->args[pi++] = temp_arg(ret); - nb_rets = 1; - } -#endif - } else { - nb_rets = 0; - } - TCGOP_CALLO(op) = nb_rets; - - real_args = 0; - for (i = 0; i < nargs; i++) { - int argtype = extract32(typemask, (i + 1) * 3, 3); - bool is_64bit = (argtype & ~1) == dh_typecode_i64; - bool want_align = false; - -#if defined(CONFIG_TCG_INTERPRETER) - /* - * Align all arguments, so that they land in predictable places - * for passing off to ffi_call. - */ - want_align = true; -#elif defined(TCG_TARGET_CALL_ALIGN_ARGS) - /* Some targets want aligned 64 bit args */ - want_align = is_64bit; -#endif - - if (TCG_TARGET_REG_BITS < 64 && want_align && (real_args & 1)) { - op->args[pi++] = TCG_CALL_DUMMY_ARG; - real_args++; - } - - if (TCG_TARGET_REG_BITS < 64 && is_64bit) { - /* - * If stack grows up, then we will be placing successive - * arguments at lower addresses, which means we need to - * reverse the order compared to how we would normally - * treat either big or little-endian. For those arguments - * that will wind up in registers, this still works for - * HPPA (the only current STACK_GROWSUP target) since the - * argument registers are *also* allocated in decreasing - * order. If another such target is added, this logic may - * have to get more complicated to differentiate between - * stack arguments and register arguments. - */ -#if HOST_BIG_ENDIAN != defined(TCG_TARGET_STACK_GROWSUP) - op->args[pi++] = temp_arg(args[i] + 1); - op->args[pi++] = temp_arg(args[i]); -#else - op->args[pi++] = temp_arg(args[i]); - op->args[pi++] = temp_arg(args[i] + 1); -#endif - real_args += 2; - continue; - } - - op->args[pi++] = temp_arg(args[i]); - real_args++; - } - op->args[pi++] = (uintptr_t)func; + op->args[pi++] = (uintptr_t)info->func; op->args[pi++] = (uintptr_t)info; - TCGOP_CALLI(op) = real_args; + tcg_debug_assert(pi == total_args); - /* Make sure the fields didn't overflow. */ - tcg_debug_assert(TCGOP_CALLI(op) == real_args); - tcg_debug_assert(pi <= ARRAY_SIZE(op->args)); - -#if defined(__sparc__) && !defined(__arch64__) \ - && !defined(CONFIG_TCG_INTERPRETER) - /* Free all of the parts we allocated above. */ - for (i = real_args = 0; i < orig_nargs; ++i) { - int argtype = extract32(orig_typemask, (i + 1) * 3, 3); - bool is_64bit = (argtype & ~1) == dh_typecode_i64; - - if (is_64bit) { - tcg_temp_free_internal(args[real_args++]); - tcg_temp_free_internal(args[real_args++]); - } else { - real_args++; - } + if (tcg_ctx->emit_before_op) { + QTAILQ_INSERT_BEFORE(tcg_ctx->emit_before_op, op, link); + } else { + QTAILQ_INSERT_TAIL(&tcg_ctx->ops, op, link); } - if ((orig_typemask & 6) == dh_typecode_i64) { - /* The 32-bit ABI returned two 32-bit pieces. Re-assemble them. - Note that describing these as TCGv_i64 eliminates an unnecessary - zero-extension that tcg_gen_concat_i32_i64 would create. */ - tcg_gen_concat32_i64(temp_tcgv_i64(ret), retl, reth); - tcg_temp_free_i64(retl); - tcg_temp_free_i64(reth); - } -#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64 - for (i = 0; i < nargs; ++i) { - int argtype = extract32(typemask, (i + 1) * 3, 3); - bool is_32bit = (argtype & ~1) == dh_typecode_i32; - if (is_32bit) { - tcg_temp_free_internal(args[i]); - } + tcg_debug_assert(n_extend < ARRAY_SIZE(extend_free)); + for (i = 0; i < n_extend; ++i) { + tcg_temp_free_i64(extend_free[i]); } -#endif /* TCG_TARGET_EXTEND_ARGS */ +} + +void tcg_gen_call0(TCGHelperInfo *info, TCGTemp *ret) +{ + tcg_gen_callN(info, ret, NULL); +} + +void tcg_gen_call1(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1) +{ + tcg_gen_callN(info, ret, &t1); +} + +void tcg_gen_call2(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, TCGTemp *t2) +{ + TCGTemp *args[2] = { t1, t2 }; + tcg_gen_callN(info, ret, args); +} + +void tcg_gen_call3(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, + TCGTemp *t2, TCGTemp *t3) +{ + TCGTemp *args[3] = { t1, t2, t3 }; + tcg_gen_callN(info, ret, args); +} + +void tcg_gen_call4(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, + TCGTemp *t2, TCGTemp *t3, TCGTemp *t4) +{ + TCGTemp *args[4] = { t1, t2, t3, t4 }; + tcg_gen_callN(info, ret, args); +} + +void tcg_gen_call5(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, + TCGTemp *t2, TCGTemp *t3, TCGTemp *t4, TCGTemp *t5) +{ + TCGTemp *args[5] = { t1, t2, t3, t4, t5 }; + tcg_gen_callN(info, ret, args); +} + +void tcg_gen_call6(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, TCGTemp *t2, + TCGTemp *t3, TCGTemp *t4, TCGTemp *t5, TCGTemp *t6) +{ + TCGTemp *args[6] = { t1, t2, t3, t4, t5, t6 }; + tcg_gen_callN(info, ret, args); +} + +void tcg_gen_call7(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, + TCGTemp *t2, TCGTemp *t3, TCGTemp *t4, + TCGTemp *t5, TCGTemp *t6, TCGTemp *t7) +{ + TCGTemp *args[7] = { t1, t2, t3, t4, t5, t6, t7 }; + tcg_gen_callN(info, ret, args); } static void tcg_reg_alloc_start(TCGContext *s) @@ -1685,11 +2414,10 @@ static void tcg_reg_alloc_start(TCGContext *s) break; case TEMP_GLOBAL: break; - case TEMP_NORMAL: case TEMP_EBB: val = TEMP_VAL_DEAD; /* fall through */ - case TEMP_LOCAL: + case TEMP_TB: ts->mem_allocated = 0; break; default: @@ -1711,13 +2439,10 @@ static char *tcg_get_arg_str_ptr(TCGContext *s, char *buf, int buf_size, case TEMP_GLOBAL: pstrcpy(buf, buf_size, ts->name); break; - case TEMP_LOCAL: + case TEMP_TB: snprintf(buf, buf_size, "loc%d", idx - s->nb_globals); break; case TEMP_EBB: - snprintf(buf, buf_size, "ebb%d", idx - s->nb_globals); - break; - case TEMP_NORMAL: snprintf(buf, buf_size, "tmp%d", idx - s->nb_globals); break; case TEMP_CONST: @@ -1763,10 +2488,12 @@ static const char * const cond_name[] = [TCG_COND_LTU] = "ltu", [TCG_COND_GEU] = "geu", [TCG_COND_LEU] = "leu", - [TCG_COND_GTU] = "gtu" + [TCG_COND_GTU] = "gtu", + [TCG_COND_TSTEQ] = "tsteq", + [TCG_COND_TSTNE] = "tstne", }; -static const char * const ldst_name[] = +static const char * const ldst_name[(MO_BSWAP | MO_SSIZE) + 1] = { [MO_UB] = "ub", [MO_SB] = "sb", @@ -1780,16 +2507,13 @@ static const char * const ldst_name[] = [MO_BEUL] = "beul", [MO_BESL] = "besl", [MO_BEUQ] = "beq", + [MO_128 + MO_BE] = "beo", + [MO_128 + MO_LE] = "leo", }; static const char * const alignment_name[(MO_AMASK >> MO_ASHIFT) + 1] = { -#ifdef TARGET_ALIGNED_ONLY [MO_UNALN >> MO_ASHIFT] = "un+", - [MO_ALIGN >> MO_ASHIFT] = "", -#else - [MO_UNALN >> MO_ASHIFT] = "", [MO_ALIGN >> MO_ASHIFT] = "al+", -#endif [MO_ALIGN_2 >> MO_ASHIFT] = "al2+", [MO_ALIGN_4 >> MO_ASHIFT] = "al4+", [MO_ALIGN_8 >> MO_ASHIFT] = "al8+", @@ -1798,6 +2522,15 @@ static const char * const alignment_name[(MO_AMASK >> MO_ASHIFT) + 1] = { [MO_ALIGN_64 >> MO_ASHIFT] = "al64+", }; +static const char * const atom_name[(MO_ATOM_MASK >> MO_ATOM_SHIFT) + 1] = { + [MO_ATOM_IFALIGN >> MO_ATOM_SHIFT] = "", + [MO_ATOM_IFALIGN_PAIR >> MO_ATOM_SHIFT] = "pair+", + [MO_ATOM_WITHIN16 >> MO_ATOM_SHIFT] = "w16+", + [MO_ATOM_WITHIN16_PAIR >> MO_ATOM_SHIFT] = "w16p+", + [MO_ATOM_SUBALIGN >> MO_ATOM_SHIFT] = "sub+", + [MO_ATOM_NONE >> MO_ATOM_SHIFT] = "noat+", +}; + static const char bswap_flag_name[][6] = { [TCG_BSWAP_IZ] = "iz", [TCG_BSWAP_OZ] = "oz", @@ -1842,14 +2575,9 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) nb_oargs = 0; col += ne_fprintf(f, "\n ----"); - for (i = 0; i < TARGET_INSN_START_WORDS; ++i) { - target_ulong a; -#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS - a = deposit64(op->args[i * 2], 32, 32, op->args[i * 2 + 1]); -#else - a = op->args[i]; -#endif - col += ne_fprintf(f, " " TARGET_FMT_lx, a); + for (i = 0, k = s->insn_start_words; i < k; ++i) { + col += ne_fprintf(f, " %016" PRIx64, + tcg_get_insn_start_param(op, i)); } } else if (c == INDEX_op_call) { const TCGHelperInfo *info = tcg_call_info(op); @@ -1880,10 +2608,7 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) } for (i = 0; i < nb_iargs; i++) { TCGArg arg = op->args[nb_oargs + i]; - const char *t = ""; - if (arg != TCG_CALL_DUMMY_ARG) { - t = tcg_get_arg_str(s, buf, sizeof(buf), arg); - } + const char *t = tcg_get_arg_str(s, buf, sizeof(buf), arg); col += ne_fprintf(f, ",%s", t); } } else { @@ -1914,11 +2639,13 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) switch (c) { case INDEX_op_brcond_i32: case INDEX_op_setcond_i32: + case INDEX_op_negsetcond_i32: case INDEX_op_movcond_i32: case INDEX_op_brcond2_i32: case INDEX_op_setcond2_i32: case INDEX_op_brcond_i64: case INDEX_op_setcond_i64: + case INDEX_op_negsetcond_i64: case INDEX_op_movcond_i64: case INDEX_op_cmp_vec: case INDEX_op_cmpsel_vec: @@ -1930,23 +2657,38 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) } i = 1; break; - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st8_i32: - case INDEX_op_qemu_ld_i64: - case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_st8_a32_i32: + case INDEX_op_qemu_st8_a64_i32: + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_st_a64_i64: + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: { + const char *s_al, *s_op, *s_at; MemOpIdx oi = op->args[k++]; - MemOp op = get_memop(oi); + MemOp mop = get_memop(oi); unsigned ix = get_mmuidx(oi); - if (op & ~(MO_AMASK | MO_BSWAP | MO_SSIZE)) { - col += ne_fprintf(f, ",$0x%x,%u", op, ix); + s_al = alignment_name[(mop & MO_AMASK) >> MO_ASHIFT]; + s_op = ldst_name[mop & (MO_BSWAP | MO_SSIZE)]; + s_at = atom_name[(mop & MO_ATOM_MASK) >> MO_ATOM_SHIFT]; + mop &= ~(MO_AMASK | MO_BSWAP | MO_SSIZE | MO_ATOM_MASK); + + /* If all fields are accounted for, print symbolically. */ + if (!mop && s_al && s_op && s_at) { + col += ne_fprintf(f, ",%s%s%s,%u", + s_at, s_al, s_op, ix); } else { - const char *s_al, *s_op; - s_al = alignment_name[(op & MO_AMASK) >> MO_ASHIFT]; - s_op = ldst_name[op & (MO_BSWAP | MO_SSIZE)]; - col += ne_fprintf(f, ",%s%s,%u", s_al, s_op, ix); + mop = get_memop(oi); + col += ne_fprintf(f, ",$0x%x,%u", mop, ix); } i = 1; } @@ -1985,6 +2727,85 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) arg_label(op->args[k])->id); i++, k++; break; + case INDEX_op_mb: + { + TCGBar membar = op->args[k]; + const char *b_op, *m_op; + + switch (membar & TCG_BAR_SC) { + case 0: + b_op = "none"; + break; + case TCG_BAR_LDAQ: + b_op = "acq"; + break; + case TCG_BAR_STRL: + b_op = "rel"; + break; + case TCG_BAR_SC: + b_op = "seq"; + break; + default: + g_assert_not_reached(); + } + + switch (membar & TCG_MO_ALL) { + case 0: + m_op = "none"; + break; + case TCG_MO_LD_LD: + m_op = "rr"; + break; + case TCG_MO_LD_ST: + m_op = "rw"; + break; + case TCG_MO_ST_LD: + m_op = "wr"; + break; + case TCG_MO_ST_ST: + m_op = "ww"; + break; + case TCG_MO_LD_LD | TCG_MO_LD_ST: + m_op = "rr+rw"; + break; + case TCG_MO_LD_LD | TCG_MO_ST_LD: + m_op = "rr+wr"; + break; + case TCG_MO_LD_LD | TCG_MO_ST_ST: + m_op = "rr+ww"; + break; + case TCG_MO_LD_ST | TCG_MO_ST_LD: + m_op = "rw+wr"; + break; + case TCG_MO_LD_ST | TCG_MO_ST_ST: + m_op = "rw+ww"; + break; + case TCG_MO_ST_LD | TCG_MO_ST_ST: + m_op = "wr+ww"; + break; + case TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_LD: + m_op = "rr+rw+wr"; + break; + case TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST: + m_op = "rr+rw+ww"; + break; + case TCG_MO_LD_LD | TCG_MO_ST_LD | TCG_MO_ST_ST: + m_op = "rr+wr+ww"; + break; + case TCG_MO_LD_ST | TCG_MO_ST_LD | TCG_MO_ST_ST: + m_op = "rw+wr+ww"; + break; + case TCG_MO_ALL: + m_op = "all"; + break; + default: + g_assert_not_reached(); + } + + col += ne_fprintf(f, "%s%s:%s", (k ? "," : ""), b_op, m_op); + i++, k++; + } + break; default: break; } @@ -2024,7 +2845,7 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) if (have_prefs) { for (i = 0; i < nb_oargs; ++i) { - TCGRegSet set = op->output_pref[i]; + TCGRegSet set = output_pref(op, i); if (i == 0) { ne_fprintf(f, " pref="); @@ -2056,15 +2877,32 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) static int get_constraint_priority(const TCGOpDef *def, int k) { const TCGArgConstraint *arg_ct = &def->args_ct[k]; - int n; + int n = ctpop64(arg_ct->regs); - if (arg_ct->oalias) { - /* an alias is equivalent to a single register */ - n = 1; - } else { - n = ctpop64(arg_ct->regs); + /* + * Sort constraints of a single register first, which includes output + * aliases (which must exactly match the input already allocated). + */ + if (n == 1 || arg_ct->oalias) { + return INT_MAX; } - return TCG_TARGET_NB_REGS - n + 1; + + /* + * Sort register pairs next, first then second immediately after. + * Arbitrarily sort multiple pairs by the index of the first reg; + * there shouldn't be many pairs. + */ + switch (arg_ct->pair) { + case 1: + case 3: + return (k + 1) * 2; + case 2: + return (arg_ct->pair_index + 1) * 2 - 1; + } + + /* Finally, sort by decreasing register count. */ + assert(n > 1); + return -n; } /* sort from highest priority to lowest */ @@ -2099,7 +2937,8 @@ static void process_op_defs(TCGContext *s) for (op = 0; op < NB_OPS; op++) { TCGOpDef *def = &tcg_op_defs[op]; const TCGTargetOpDef *tdefs; - int i, nb_args; + bool saw_alias_pair = false; + int i, o, i2, o2, nb_args; if (def->flags & TCG_OPF_NOT_PRESENT) { continue; @@ -2121,81 +2960,209 @@ static void process_op_defs(TCGContext *s) for (i = 0; i < nb_args; i++) { const char *ct_str = tdefs->args_ct_str[i]; + bool input_p = i >= def->nb_oargs; + /* Incomplete TCGTargetOpDef entry. */ tcg_debug_assert(ct_str != NULL); - while (*ct_str != '\0') { - switch(*ct_str) { - case '0' ... '9': - { - int oarg = *ct_str - '0'; - tcg_debug_assert(ct_str == tdefs->args_ct_str[i]); - tcg_debug_assert(oarg < def->nb_oargs); - tcg_debug_assert(def->args_ct[oarg].regs != 0); - def->args_ct[i] = def->args_ct[oarg]; - /* The output sets oalias. */ - def->args_ct[oarg].oalias = true; - def->args_ct[oarg].alias_index = i; - /* The input sets ialias. */ - def->args_ct[i].ialias = true; - def->args_ct[i].alias_index = oarg; - } - ct_str++; - break; - case '&': - def->args_ct[i].newreg = true; - ct_str++; - break; + switch (*ct_str) { + case '0' ... '9': + o = *ct_str - '0'; + tcg_debug_assert(input_p); + tcg_debug_assert(o < def->nb_oargs); + tcg_debug_assert(def->args_ct[o].regs != 0); + tcg_debug_assert(!def->args_ct[o].oalias); + def->args_ct[i] = def->args_ct[o]; + /* The output sets oalias. */ + def->args_ct[o].oalias = 1; + def->args_ct[o].alias_index = i; + /* The input sets ialias. */ + def->args_ct[i].ialias = 1; + def->args_ct[i].alias_index = o; + if (def->args_ct[i].pair) { + saw_alias_pair = true; + } + tcg_debug_assert(ct_str[1] == '\0'); + continue; + + case '&': + tcg_debug_assert(!input_p); + def->args_ct[i].newreg = true; + ct_str++; + break; + + case 'p': /* plus */ + /* Allocate to the register after the previous. */ + tcg_debug_assert(i > (input_p ? def->nb_oargs : 0)); + o = i - 1; + tcg_debug_assert(!def->args_ct[o].pair); + tcg_debug_assert(!def->args_ct[o].ct); + def->args_ct[i] = (TCGArgConstraint){ + .pair = 2, + .pair_index = o, + .regs = def->args_ct[o].regs << 1, + .newreg = def->args_ct[o].newreg, + }; + def->args_ct[o].pair = 1; + def->args_ct[o].pair_index = i; + tcg_debug_assert(ct_str[1] == '\0'); + continue; + + case 'm': /* minus */ + /* Allocate to the register before the previous. */ + tcg_debug_assert(i > (input_p ? def->nb_oargs : 0)); + o = i - 1; + tcg_debug_assert(!def->args_ct[o].pair); + tcg_debug_assert(!def->args_ct[o].ct); + def->args_ct[i] = (TCGArgConstraint){ + .pair = 1, + .pair_index = o, + .regs = def->args_ct[o].regs >> 1, + .newreg = def->args_ct[o].newreg, + }; + def->args_ct[o].pair = 2; + def->args_ct[o].pair_index = i; + tcg_debug_assert(ct_str[1] == '\0'); + continue; + } + + do { + switch (*ct_str) { case 'i': def->args_ct[i].ct |= TCG_CT_CONST; - ct_str++; break; /* Include all of the target-specific constraints. */ #undef CONST #define CONST(CASE, MASK) \ - case CASE: def->args_ct[i].ct |= MASK; ct_str++; break; + case CASE: def->args_ct[i].ct |= MASK; break; #define REGS(CASE, MASK) \ - case CASE: def->args_ct[i].regs |= MASK; ct_str++; break; + case CASE: def->args_ct[i].regs |= MASK; break; #include "tcg-target-con-str.h" #undef REGS #undef CONST default: + case '0' ... '9': + case '&': + case 'p': + case 'm': /* Typo in TCGTargetOpDef constraint. */ g_assert_not_reached(); } - } + } while (*++ct_str != '\0'); } /* TCGTargetOpDef entry with too much information? */ tcg_debug_assert(i == TCG_MAX_OP_ARGS || tdefs->args_ct_str[i] == NULL); + /* + * Fix up output pairs that are aliased with inputs. + * When we created the alias, we copied pair from the output. + * There are three cases: + * (1a) Pairs of inputs alias pairs of outputs. + * (1b) One input aliases the first of a pair of outputs. + * (2) One input aliases the second of a pair of outputs. + * + * Case 1a is handled by making sure that the pair_index'es are + * properly updated so that they appear the same as a pair of inputs. + * + * Case 1b is handled by setting the pair_index of the input to + * itself, simply so it doesn't point to an unrelated argument. + * Since we don't encounter the "second" during the input allocation + * phase, nothing happens with the second half of the input pair. + * + * Case 2 is handled by setting the second input to pair=3, the + * first output to pair=3, and the pair_index'es to match. + */ + if (saw_alias_pair) { + for (i = def->nb_oargs; i < nb_args; i++) { + /* + * Since [0-9pm] must be alone in the constraint string, + * the only way they can both be set is if the pair comes + * from the output alias. + */ + if (!def->args_ct[i].ialias) { + continue; + } + switch (def->args_ct[i].pair) { + case 0: + break; + case 1: + o = def->args_ct[i].alias_index; + o2 = def->args_ct[o].pair_index; + tcg_debug_assert(def->args_ct[o].pair == 1); + tcg_debug_assert(def->args_ct[o2].pair == 2); + if (def->args_ct[o2].oalias) { + /* Case 1a */ + i2 = def->args_ct[o2].alias_index; + tcg_debug_assert(def->args_ct[i2].pair == 2); + def->args_ct[i2].pair_index = i; + def->args_ct[i].pair_index = i2; + } else { + /* Case 1b */ + def->args_ct[i].pair_index = i; + } + break; + case 2: + o = def->args_ct[i].alias_index; + o2 = def->args_ct[o].pair_index; + tcg_debug_assert(def->args_ct[o].pair == 2); + tcg_debug_assert(def->args_ct[o2].pair == 1); + if (def->args_ct[o2].oalias) { + /* Case 1a */ + i2 = def->args_ct[o2].alias_index; + tcg_debug_assert(def->args_ct[i2].pair == 1); + def->args_ct[i2].pair_index = i; + def->args_ct[i].pair_index = i2; + } else { + /* Case 2 */ + def->args_ct[i].pair = 3; + def->args_ct[o2].pair = 3; + def->args_ct[i].pair_index = o2; + def->args_ct[o2].pair_index = i; + } + break; + default: + g_assert_not_reached(); + } + } + } + /* sort the constraints (XXX: this is just an heuristic) */ sort_constraints(def, 0, def->nb_oargs); sort_constraints(def, def->nb_oargs, def->nb_iargs); } } +static void remove_label_use(TCGOp *op, int idx) +{ + TCGLabel *label = arg_label(op->args[idx]); + TCGLabelUse *use; + + QSIMPLEQ_FOREACH(use, &label->branches, next) { + if (use->op == op) { + QSIMPLEQ_REMOVE(&label->branches, use, TCGLabelUse, next); + return; + } + } + g_assert_not_reached(); +} + void tcg_op_remove(TCGContext *s, TCGOp *op) { - TCGLabel *label; - switch (op->opc) { case INDEX_op_br: - label = arg_label(op->args[0]); - label->refs--; + remove_label_use(op, 0); break; case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: - label = arg_label(op->args[3]); - label->refs--; + remove_label_use(op, 3); break; case INDEX_op_brcond2_i32: - label = arg_label(op->args[5]); - label->refs--; + remove_label_use(op, 5); break; default: break; @@ -2204,10 +3171,6 @@ void tcg_op_remove(TCGContext *s, TCGOp *op) QTAILQ_REMOVE(&s->ops, op, link); QTAILQ_INSERT_TAIL(&s->free_ops, op, link); s->nb_ops--; - -#ifdef CONFIG_PROFILER - qatomic_set(&s->prof.del_op_count, s->prof.del_op_count + 1); -#endif } void tcg_remove_ops_after(TCGOp *op) @@ -2223,49 +3186,95 @@ void tcg_remove_ops_after(TCGOp *op) } } -static TCGOp *tcg_op_alloc(TCGOpcode opc) +static TCGOp *tcg_op_alloc(TCGOpcode opc, unsigned nargs) { TCGContext *s = tcg_ctx; - TCGOp *op; + TCGOp *op = NULL; - if (likely(QTAILQ_EMPTY(&s->free_ops))) { - op = tcg_malloc(sizeof(TCGOp)); - } else { - op = QTAILQ_FIRST(&s->free_ops); - QTAILQ_REMOVE(&s->free_ops, op, link); + if (unlikely(!QTAILQ_EMPTY(&s->free_ops))) { + QTAILQ_FOREACH(op, &s->free_ops, link) { + if (nargs <= op->nargs) { + QTAILQ_REMOVE(&s->free_ops, op, link); + nargs = op->nargs; + goto found; + } + } } + + /* Most opcodes have 3 or 4 operands: reduce fragmentation. */ + nargs = MAX(4, nargs); + op = tcg_malloc(sizeof(TCGOp) + sizeof(TCGArg) * nargs); + + found: memset(op, 0, offsetof(TCGOp, link)); op->opc = opc; + op->nargs = nargs; + + /* Check for bitfield overflow. */ + tcg_debug_assert(op->nargs == nargs); + s->nb_ops++; - return op; } -TCGOp *tcg_emit_op(TCGOpcode opc) +TCGOp *tcg_emit_op(TCGOpcode opc, unsigned nargs) { - TCGOp *op = tcg_op_alloc(opc); - QTAILQ_INSERT_TAIL(&tcg_ctx->ops, op, link); + TCGOp *op = tcg_op_alloc(opc, nargs); + + if (tcg_ctx->emit_before_op) { + QTAILQ_INSERT_BEFORE(tcg_ctx->emit_before_op, op, link); + } else { + QTAILQ_INSERT_TAIL(&tcg_ctx->ops, op, link); + } return op; } -TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *old_op, TCGOpcode opc) +TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *old_op, + TCGOpcode opc, unsigned nargs) { - TCGOp *new_op = tcg_op_alloc(opc); + TCGOp *new_op = tcg_op_alloc(opc, nargs); QTAILQ_INSERT_BEFORE(old_op, new_op, link); return new_op; } -TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *old_op, TCGOpcode opc) +TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *old_op, + TCGOpcode opc, unsigned nargs) { - TCGOp *new_op = tcg_op_alloc(opc); + TCGOp *new_op = tcg_op_alloc(opc, nargs); QTAILQ_INSERT_AFTER(&s->ops, old_op, new_op, link); return new_op; } -/* Reachable analysis : remove unreachable code. */ -static void reachable_code_pass(TCGContext *s) +static void move_label_uses(TCGLabel *to, TCGLabel *from) { - TCGOp *op, *op_next; + TCGLabelUse *u; + + QSIMPLEQ_FOREACH(u, &from->branches, next) { + TCGOp *op = u->op; + switch (op->opc) { + case INDEX_op_br: + op->args[0] = label_arg(to); + break; + case INDEX_op_brcond_i32: + case INDEX_op_brcond_i64: + op->args[3] = label_arg(to); + break; + case INDEX_op_brcond2_i32: + op->args[5] = label_arg(to); + break; + default: + g_assert_not_reached(); + } + } + + QSIMPLEQ_CONCAT(&to->branches, &from->branches); +} + +/* Reachable analysis : remove unreachable code. */ +static void __attribute__((noinline)) +reachable_code_pass(TCGContext *s) +{ + TCGOp *op, *op_next, *op_prev; bool dead = false; QTAILQ_FOREACH_SAFE(op, &s->ops, link, op_next) { @@ -2275,7 +3284,40 @@ static void reachable_code_pass(TCGContext *s) switch (op->opc) { case INDEX_op_set_label: label = arg_label(op->args[0]); - if (label->refs == 0) { + + /* + * Note that the first op in the TB is always a load, + * so there is always something before a label. + */ + op_prev = QTAILQ_PREV(op, link); + + /* + * If we find two sequential labels, move all branches to + * reference the second label and remove the first label. + * Do this before branch to next optimization, so that the + * middle label is out of the way. + */ + if (op_prev->opc == INDEX_op_set_label) { + move_label_uses(label, arg_label(op_prev->args[0])); + tcg_op_remove(s, op_prev); + op_prev = QTAILQ_PREV(op, link); + } + + /* + * Optimization can fold conditional branches to unconditional. + * If we find a label which is preceded by an unconditional + * branch to next, remove the branch. We couldn't do this when + * processing the branch because any dead code between the branch + * and label had not yet been removed. + */ + if (op_prev->opc == INDEX_op_br && + label == arg_label(op_prev->args[0])) { + tcg_op_remove(s, op_prev); + /* Fall through means insns become live again. */ + dead = false; + } + + if (QSIMPLEQ_EMPTY(&label->branches)) { /* * While there is an occasional backward branch, virtually * all branches generated by the translators are forward. @@ -2288,21 +3330,6 @@ static void reachable_code_pass(TCGContext *s) /* Once we see a label, insns become live again. */ dead = false; remove = false; - - /* - * Optimization can fold conditional branches to unconditional. - * If we find a label with one reference which is preceded by - * an unconditional branch to it, remove both. This needed to - * wait until the dead code in between them was removed. - */ - if (label->refs == 1) { - TCGOp *op_prev = QTAILQ_PREV(op, link); - if (op_prev->opc == INDEX_op_br && - label == arg_label(op_prev->args[0])) { - tcg_op_remove(s, op_prev); - remove = true; - } - } } break; @@ -2385,10 +3412,9 @@ static void la_bb_end(TCGContext *s, int ng, int nt) switch (ts->kind) { case TEMP_FIXED: case TEMP_GLOBAL: - case TEMP_LOCAL: + case TEMP_TB: state = TS_DEAD | TS_MEM; break; - case TEMP_NORMAL: case TEMP_EBB: case TEMP_CONST: state = TS_DEAD; @@ -2430,16 +3456,13 @@ static void la_bb_sync(TCGContext *s, int ng, int nt) int state; switch (ts->kind) { - case TEMP_LOCAL: + case TEMP_TB: state = ts->state; ts->state = state | TS_MEM; if (state != TS_DEAD) { continue; } break; - case TEMP_NORMAL: - s->temps[i].state = TS_DEAD; - break; case TEMP_EBB: case TEMP_CONST: continue; @@ -2483,10 +3506,80 @@ static void la_cross_call(TCGContext *s, int nt) } } +/* + * Liveness analysis: Verify the lifetime of TEMP_TB, and reduce + * to TEMP_EBB, if possible. + */ +static void __attribute__((noinline)) +liveness_pass_0(TCGContext *s) +{ + void * const multiple_ebb = (void *)(uintptr_t)-1; + int nb_temps = s->nb_temps; + TCGOp *op, *ebb; + + for (int i = s->nb_globals; i < nb_temps; ++i) { + s->temps[i].state_ptr = NULL; + } + + /* + * Represent each EBB by the op at which it begins. In the case of + * the first EBB, this is the first op, otherwise it is a label. + * Collect the uses of each TEMP_TB: NULL for unused, EBB for use + * within a single EBB, else MULTIPLE_EBB. + */ + ebb = QTAILQ_FIRST(&s->ops); + QTAILQ_FOREACH(op, &s->ops, link) { + const TCGOpDef *def; + int nb_oargs, nb_iargs; + + switch (op->opc) { + case INDEX_op_set_label: + ebb = op; + continue; + case INDEX_op_discard: + continue; + case INDEX_op_call: + nb_oargs = TCGOP_CALLO(op); + nb_iargs = TCGOP_CALLI(op); + break; + default: + def = &tcg_op_defs[op->opc]; + nb_oargs = def->nb_oargs; + nb_iargs = def->nb_iargs; + break; + } + + for (int i = 0; i < nb_oargs + nb_iargs; ++i) { + TCGTemp *ts = arg_temp(op->args[i]); + + if (ts->kind != TEMP_TB) { + continue; + } + if (ts->state_ptr == NULL) { + ts->state_ptr = ebb; + } else if (ts->state_ptr != ebb) { + ts->state_ptr = multiple_ebb; + } + } + } + + /* + * For TEMP_TB that turned out not to be used beyond one EBB, + * reduce the liveness to TEMP_EBB. + */ + for (int i = s->nb_globals; i < nb_temps; ++i) { + TCGTemp *ts = &s->temps[i]; + if (ts->kind == TEMP_TB && ts->state_ptr != multiple_ebb) { + ts->kind = TEMP_EBB; + } + } +} + /* Liveness analysis : update the opc_arg_life array to tell if a given input arguments is dead. Instructions updating dead temporaries are removed. */ -static void liveness_pass_1(TCGContext *s) +static void __attribute__((noinline)) +liveness_pass_1(TCGContext *s) { int nb_globals = s->nb_globals; int nb_temps = s->nb_temps; @@ -2514,12 +3607,11 @@ static void liveness_pass_1(TCGContext *s) switch (opc) { case INDEX_op_call: { - int call_flags; - int nb_call_regs; + const TCGHelperInfo *info = tcg_call_info(op); + int call_flags = tcg_call_flags(op); nb_oargs = TCGOP_CALLO(op); nb_iargs = TCGOP_CALLI(op); - call_flags = tcg_call_flags(op); /* pure functions can be removed if their result is unused */ if (call_flags & TCG_CALL_NO_SIDE_EFFECTS) { @@ -2544,11 +3636,11 @@ static void liveness_pass_1(TCGContext *s) } ts->state = TS_DEAD; la_reset_pref(ts); - - /* Not used -- it will be tcg_target_call_oarg_regs[i]. */ - op->output_pref[i] = 0; } + /* Not used -- it will be tcg_target_call_oarg_reg(). */ + memset(op->output_pref, 0, sizeof(op->output_pref)); + if (!(call_flags & (TCG_CALL_NO_WRITE_GLOBALS | TCG_CALL_NO_READ_GLOBALS))) { la_global_kill(s, nb_globals); @@ -2559,7 +3651,7 @@ static void liveness_pass_1(TCGContext *s) /* Record arguments that die in this helper. */ for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) { ts = arg_temp(op->args[i]); - if (ts && ts->state & TS_DEAD) { + if (ts->state & TS_DEAD) { arg_life |= DEAD_ARG << i; } } @@ -2567,31 +3659,59 @@ static void liveness_pass_1(TCGContext *s) /* For all live registers, remove call-clobbered prefs. */ la_cross_call(s, nb_temps); - nb_call_regs = ARRAY_SIZE(tcg_target_call_iarg_regs); + /* + * Input arguments are live for preceding opcodes. + * + * For those arguments that die, and will be allocated in + * registers, clear the register set for that arg, to be + * filled in below. For args that will be on the stack, + * reset to any available reg. Process arguments in reverse + * order so that if a temp is used more than once, the stack + * reset to max happens before the register reset to 0. + */ + for (i = nb_iargs - 1; i >= 0; i--) { + const TCGCallArgumentLoc *loc = &info->in[i]; + ts = arg_temp(op->args[nb_oargs + i]); - /* Input arguments are live for preceding opcodes. */ - for (i = 0; i < nb_iargs; i++) { - ts = arg_temp(op->args[i + nb_oargs]); - if (ts && ts->state & TS_DEAD) { - /* For those arguments that die, and will be allocated - * in registers, clear the register set for that arg, - * to be filled in below. For args that will be on - * the stack, reset to any available reg. - */ - *la_temp_pref(ts) - = (i < nb_call_regs ? 0 : - tcg_target_available_regs[ts->type]); + if (ts->state & TS_DEAD) { + switch (loc->kind) { + case TCG_CALL_ARG_NORMAL: + case TCG_CALL_ARG_EXTEND_U: + case TCG_CALL_ARG_EXTEND_S: + if (arg_slot_reg_p(loc->arg_slot)) { + *la_temp_pref(ts) = 0; + break; + } + /* fall through */ + default: + *la_temp_pref(ts) = + tcg_target_available_regs[ts->type]; + break; + } ts->state &= ~TS_DEAD; } } - /* For each input argument, add its input register to prefs. - If a temp is used once, this produces a single set bit. */ - for (i = 0; i < MIN(nb_call_regs, nb_iargs); i++) { - ts = arg_temp(op->args[i + nb_oargs]); - if (ts) { - tcg_regset_set_reg(*la_temp_pref(ts), - tcg_target_call_iarg_regs[i]); + /* + * For each input argument, add its input register to prefs. + * If a temp is used once, this produces a single set bit; + * if a temp is used multiple times, this produces a set. + */ + for (i = 0; i < nb_iargs; i++) { + const TCGCallArgumentLoc *loc = &info->in[i]; + ts = arg_temp(op->args[nb_oargs + i]); + + switch (loc->kind) { + case TCG_CALL_ARG_NORMAL: + case TCG_CALL_ARG_EXTEND_U: + case TCG_CALL_ARG_EXTEND_S: + if (arg_slot_reg_p(loc->arg_slot)) { + tcg_regset_set_reg(*la_temp_pref(ts), + tcg_target_call_iarg_regs[loc->arg_slot]); + } + break; + default: + break; } } } @@ -2710,7 +3830,9 @@ static void liveness_pass_1(TCGContext *s) ts = arg_temp(op->args[i]); /* Remember the preference of the uses that followed. */ - op->output_pref[i] = *la_temp_pref(ts); + if (i < ARRAY_SIZE(op->output_pref)) { + op->output_pref[i] = *la_temp_pref(ts); + } /* Output args are dead. */ if (ts->state & TS_DEAD) { @@ -2780,7 +3902,7 @@ static void liveness_pass_1(TCGContext *s) set &= ct->regs; if (ct->ialias) { - set &= op->output_pref[ct->alias_index]; + set &= output_pref(op, ct->alias_index); } /* If the combination is not possible, restart. */ if (set == 0) { @@ -2797,7 +3919,8 @@ static void liveness_pass_1(TCGContext *s) } /* Liveness analysis: Convert indirect regs to direct temporaries. */ -static bool liveness_pass_2(TCGContext *s) +static bool __attribute__((noinline)) +liveness_pass_2(TCGContext *s) { int nb_globals = s->nb_globals; int nb_temps, i; @@ -2811,6 +3934,7 @@ static bool liveness_pass_2(TCGContext *s) TCGTemp *dts = tcg_temp_alloc(s); dts->type = its->type; dts->base_type = its->base_type; + dts->temp_subindex = its->temp_subindex; dts->kind = TEMP_EBB; its->state_ptr = dts; } else { @@ -2860,21 +3984,19 @@ static bool liveness_pass_2(TCGContext *s) /* Make sure that input arguments are available. */ for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) { arg_ts = arg_temp(op->args[i]); - if (arg_ts) { - dir_ts = arg_ts->state_ptr; - if (dir_ts && arg_ts->state == TS_DEAD) { - TCGOpcode lopc = (arg_ts->type == TCG_TYPE_I32 - ? INDEX_op_ld_i32 - : INDEX_op_ld_i64); - TCGOp *lop = tcg_op_insert_before(s, op, lopc); + dir_ts = arg_ts->state_ptr; + if (dir_ts && arg_ts->state == TS_DEAD) { + TCGOpcode lopc = (arg_ts->type == TCG_TYPE_I32 + ? INDEX_op_ld_i32 + : INDEX_op_ld_i64); + TCGOp *lop = tcg_op_insert_before(s, op, lopc, 3); - lop->args[0] = temp_arg(dir_ts); - lop->args[1] = temp_arg(arg_ts->mem_base); - lop->args[2] = arg_ts->mem_offset; + lop->args[0] = temp_arg(dir_ts); + lop->args[1] = temp_arg(arg_ts->mem_base); + lop->args[2] = arg_ts->mem_offset; - /* Loaded, but synced with memory. */ - arg_ts->state = TS_MEM; - } + /* Loaded, but synced with memory. */ + arg_ts->state = TS_MEM; } } @@ -2883,14 +4005,12 @@ static bool liveness_pass_2(TCGContext *s) so that we reload when needed. */ for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) { arg_ts = arg_temp(op->args[i]); - if (arg_ts) { - dir_ts = arg_ts->state_ptr; - if (dir_ts) { - op->args[i] = temp_arg(dir_ts); - changes = true; - if (IS_DEAD_ARG(i)) { - arg_ts->state = TS_DEAD; - } + dir_ts = arg_ts->state_ptr; + if (dir_ts) { + op->args[i] = temp_arg(dir_ts); + changes = true; + if (IS_DEAD_ARG(i)) { + arg_ts->state = TS_DEAD; } } } @@ -2932,7 +4052,7 @@ static bool liveness_pass_2(TCGContext *s) TCGOpcode sopc = (arg_ts->type == TCG_TYPE_I32 ? INDEX_op_st_i32 : INDEX_op_st_i64); - TCGOp *sop = tcg_op_insert_after(s, op, sopc); + TCGOp *sop = tcg_op_insert_after(s, op, sopc, 3); TCGTemp *out_ts = dir_ts; if (IS_DEAD_ARG(0)) { @@ -2968,7 +4088,7 @@ static bool liveness_pass_2(TCGContext *s) TCGOpcode sopc = (arg_ts->type == TCG_TYPE_I32 ? INDEX_op_st_i32 : INDEX_op_st_i64); - TCGOp *sop = tcg_op_insert_after(s, op, sopc); + TCGOp *sop = tcg_op_insert_after(s, op, sopc, 3); sop->args[0] = temp_arg(dir_ts); sop->args[1] = temp_arg(arg_ts->mem_base); @@ -2987,97 +4107,30 @@ static bool liveness_pass_2(TCGContext *s) return changes; } -#ifdef CONFIG_DEBUG_TCG -static void dump_regs(TCGContext *s) -{ - TCGTemp *ts; - int i; - char buf[64]; - - for(i = 0; i < s->nb_temps; i++) { - ts = &s->temps[i]; - printf(" %10s: ", tcg_get_arg_str_ptr(s, buf, sizeof(buf), ts)); - switch(ts->val_type) { - case TEMP_VAL_REG: - printf("%s", tcg_target_reg_names[ts->reg]); - break; - case TEMP_VAL_MEM: - printf("%d(%s)", (int)ts->mem_offset, - tcg_target_reg_names[ts->mem_base->reg]); - break; - case TEMP_VAL_CONST: - printf("$0x%" PRIx64, ts->val); - break; - case TEMP_VAL_DEAD: - printf("D"); - break; - default: - printf("???"); - break; - } - printf("\n"); - } - - for(i = 0; i < TCG_TARGET_NB_REGS; i++) { - if (s->reg_to_temp[i] != NULL) { - printf("%s: %s\n", - tcg_target_reg_names[i], - tcg_get_arg_str_ptr(s, buf, sizeof(buf), s->reg_to_temp[i])); - } - } -} - -static void check_regs(TCGContext *s) -{ - int reg; - int k; - TCGTemp *ts; - char buf[64]; - - for (reg = 0; reg < TCG_TARGET_NB_REGS; reg++) { - ts = s->reg_to_temp[reg]; - if (ts != NULL) { - if (ts->val_type != TEMP_VAL_REG || ts->reg != reg) { - printf("Inconsistency for register %s:\n", - tcg_target_reg_names[reg]); - goto fail; - } - } - } - for (k = 0; k < s->nb_temps; k++) { - ts = &s->temps[k]; - if (ts->val_type == TEMP_VAL_REG - && ts->kind != TEMP_FIXED - && s->reg_to_temp[ts->reg] != ts) { - printf("Inconsistency for temp %s:\n", - tcg_get_arg_str_ptr(s, buf, sizeof(buf), ts)); - fail: - printf("reg state:\n"); - dump_regs(s); - tcg_abort(); - } - } -} -#endif - static void temp_allocate_frame(TCGContext *s, TCGTemp *ts) { - intptr_t off, size, align; + intptr_t off; + int size, align; - switch (ts->type) { + /* When allocating an object, look at the full type. */ + size = tcg_type_size(ts->base_type); + switch (ts->base_type) { case TCG_TYPE_I32: - size = align = 4; + align = 4; break; case TCG_TYPE_I64: case TCG_TYPE_V64: - size = align = 8; + align = 8; break; + case TCG_TYPE_I128: case TCG_TYPE_V128: - size = align = 16; - break; case TCG_TYPE_V256: - /* Note that we do not require aligned storage for V256. */ - size = 32, align = 16; + /* + * Note that we do not require aligned storage for V256, + * and that we provide alignment for I128 to match V128, + * even if that's above what the host ABI requires. + */ + align = 16; break; default: g_assert_not_reached(); @@ -3097,13 +4150,59 @@ static void temp_allocate_frame(TCGContext *s, TCGTemp *ts) tcg_raise_tb_overflow(s); } s->current_frame_offset = off + size; - - ts->mem_offset = off; #if defined(__sparc__) - ts->mem_offset += TCG_TARGET_STACK_BIAS; + off += TCG_TARGET_STACK_BIAS; #endif - ts->mem_base = s->frame_temp; - ts->mem_allocated = 1; + + /* If the object was subdivided, assign memory to all the parts. */ + if (ts->base_type != ts->type) { + int part_size = tcg_type_size(ts->type); + int part_count = size / part_size; + + /* + * Each part is allocated sequentially in tcg_temp_new_internal. + * Jump back to the first part by subtracting the current index. + */ + ts -= ts->temp_subindex; + for (int i = 0; i < part_count; ++i) { + ts[i].mem_offset = off + i * part_size; + ts[i].mem_base = s->frame_temp; + ts[i].mem_allocated = 1; + } + } else { + ts->mem_offset = off; + ts->mem_base = s->frame_temp; + ts->mem_allocated = 1; + } +} + +/* Assign @reg to @ts, and update reg_to_temp[]. */ +static void set_temp_val_reg(TCGContext *s, TCGTemp *ts, TCGReg reg) +{ + if (ts->val_type == TEMP_VAL_REG) { + TCGReg old = ts->reg; + tcg_debug_assert(s->reg_to_temp[old] == ts); + if (old == reg) { + return; + } + s->reg_to_temp[old] = NULL; + } + tcg_debug_assert(s->reg_to_temp[reg] == NULL); + s->reg_to_temp[reg] = ts; + ts->val_type = TEMP_VAL_REG; + ts->reg = reg; +} + +/* Assign a non-register value type to @ts, and update reg_to_temp[]. */ +static void set_temp_val_nonreg(TCGContext *s, TCGTemp *ts, TCGTempVal type) +{ + tcg_debug_assert(type != TEMP_VAL_REG); + if (ts->val_type == TEMP_VAL_REG) { + TCGReg reg = ts->reg; + tcg_debug_assert(s->reg_to_temp[reg] == ts); + s->reg_to_temp[reg] = NULL; + } + ts->val_type = type; } static void temp_load(TCGContext *, TCGTemp *, TCGRegSet, TCGRegSet, TCGRegSet); @@ -3118,10 +4217,9 @@ static void temp_free_or_dead(TCGContext *s, TCGTemp *ts, int free_or_dead) case TEMP_FIXED: return; case TEMP_GLOBAL: - case TEMP_LOCAL: + case TEMP_TB: new_type = TEMP_VAL_MEM; break; - case TEMP_NORMAL: case TEMP_EBB: new_type = free_or_dead < 0 ? TEMP_VAL_MEM : TEMP_VAL_DEAD; break; @@ -3131,10 +4229,7 @@ static void temp_free_or_dead(TCGContext *s, TCGTemp *ts, int free_or_dead) default: g_assert_not_reached(); } - if (ts->val_type == TEMP_VAL_REG) { - s->reg_to_temp[ts->reg] = NULL; - } - ts->val_type = new_type; + set_temp_val_nonreg(s, ts, new_type); } /* Mark a temporary as dead. */ @@ -3178,7 +4273,7 @@ static void temp_sync(TCGContext *s, TCGTemp *ts, TCGRegSet allocated_regs, case TEMP_VAL_DEAD: default: - tcg_abort(); + g_assert_not_reached(); } ts->mem_coherent = 1; } @@ -3265,7 +4360,53 @@ static TCGReg tcg_reg_alloc(TCGContext *s, TCGRegSet required_regs, } } - tcg_abort(); + g_assert_not_reached(); +} + +static TCGReg tcg_reg_alloc_pair(TCGContext *s, TCGRegSet required_regs, + TCGRegSet allocated_regs, + TCGRegSet preferred_regs, bool rev) +{ + int i, j, k, fmin, n = ARRAY_SIZE(tcg_target_reg_alloc_order); + TCGRegSet reg_ct[2]; + const int *order; + + /* Ensure that if I is not in allocated_regs, I+1 is not either. */ + reg_ct[1] = required_regs & ~(allocated_regs | (allocated_regs >> 1)); + tcg_debug_assert(reg_ct[1] != 0); + reg_ct[0] = reg_ct[1] & preferred_regs; + + order = rev ? indirect_reg_alloc_order : tcg_target_reg_alloc_order; + + /* + * Skip the preferred_regs option if it cannot be satisfied, + * or if the preference made no difference. + */ + k = reg_ct[0] == 0 || reg_ct[0] == reg_ct[1]; + + /* + * Minimize the number of flushes by looking for 2 free registers first, + * then a single flush, then two flushes. + */ + for (fmin = 2; fmin >= 0; fmin--) { + for (j = k; j < 2; j++) { + TCGRegSet set = reg_ct[j]; + + for (i = 0; i < n; i++) { + TCGReg reg = order[i]; + + if (tcg_regset_test_reg(set, reg)) { + int f = !s->reg_to_temp[reg] + !s->reg_to_temp[reg + 1]; + if (f >= fmin) { + tcg_reg_free(s, reg, allocated_regs); + tcg_reg_free(s, reg + 1, allocated_regs); + return reg; + } + } + } + } + } + g_assert_not_reached(); } /* Make sure the temporary is in a register. If needed, allocate the register @@ -3312,11 +4453,9 @@ static void temp_load(TCGContext *s, TCGTemp *ts, TCGRegSet desired_regs, break; case TEMP_VAL_DEAD: default: - tcg_abort(); + g_assert_not_reached(); } - ts->reg = reg; - ts->val_type = TEMP_VAL_REG; - s->reg_to_temp[reg] = ts; + set_temp_val_reg(s, ts, reg); } /* Save a temporary to memory. 'allocated_regs' is used in case a @@ -3365,10 +4504,9 @@ static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs) TCGTemp *ts = &s->temps[i]; switch (ts->kind) { - case TEMP_LOCAL: + case TEMP_TB: temp_save(s, ts, allocated_regs); break; - case TEMP_NORMAL: case TEMP_EBB: /* The liveness analysis already ensures that temps are dead. Keep an tcg_debug_assert for safety. */ @@ -3402,12 +4540,9 @@ static void tcg_reg_alloc_cbranch(TCGContext *s, TCGRegSet allocated_regs) * Keep tcg_debug_asserts for safety. */ switch (ts->kind) { - case TEMP_LOCAL: + case TEMP_TB: tcg_debug_assert(ts->val_type != TEMP_VAL_REG || ts->mem_coherent); break; - case TEMP_NORMAL: - tcg_debug_assert(ts->val_type == TEMP_VAL_DEAD); - break; case TEMP_EBB: case TEMP_CONST: break; @@ -3428,10 +4563,7 @@ static void tcg_reg_alloc_do_movi(TCGContext *s, TCGTemp *ots, tcg_debug_assert(!temp_readonly(ots)); /* The movi is not explicitly generated here. */ - if (ots->val_type == TEMP_VAL_REG) { - s->reg_to_temp[ots->reg] = NULL; - } - ots->val_type = TEMP_VAL_CONST; + set_temp_val_nonreg(s, ots, TEMP_VAL_CONST); ots->val = val; ots->mem_coherent = 0; if (NEED_SYNC_ARG(0)) { @@ -3450,9 +4582,10 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp *op) TCGRegSet allocated_regs, preferred_regs; TCGTemp *ts, *ots; TCGType otype, itype; + TCGReg oreg, ireg; allocated_regs = s->reserved_regs; - preferred_regs = op->output_pref[0]; + preferred_regs = output_pref(op, 0); ots = arg_temp(op->args[0]); ts = arg_temp(op->args[1]); @@ -3481,8 +4614,9 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp *op) temp_load(s, ts, tcg_target_available_regs[itype], allocated_regs, preferred_regs); } - tcg_debug_assert(ts->val_type == TEMP_VAL_REG); + ireg = ts->reg; + if (IS_DEAD_ARG(0)) { /* mov to a non-saved dead register makes no sense (even with liveness analysis disabled). */ @@ -3490,52 +4624,53 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp *op) if (!ots->mem_allocated) { temp_allocate_frame(s, ots); } - tcg_out_st(s, otype, ts->reg, ots->mem_base->reg, ots->mem_offset); + tcg_out_st(s, otype, ireg, ots->mem_base->reg, ots->mem_offset); if (IS_DEAD_ARG(1)) { temp_dead(s, ts); } temp_dead(s, ots); + return; + } + + if (IS_DEAD_ARG(1) && ts->kind != TEMP_FIXED) { + /* + * The mov can be suppressed. Kill input first, so that it + * is unlinked from reg_to_temp, then set the output to the + * reg that we saved from the input. + */ + temp_dead(s, ts); + oreg = ireg; } else { - if (IS_DEAD_ARG(1) && ts->kind != TEMP_FIXED) { - /* the mov can be suppressed */ - if (ots->val_type == TEMP_VAL_REG) { - s->reg_to_temp[ots->reg] = NULL; - } - ots->reg = ts->reg; - temp_dead(s, ts); + if (ots->val_type == TEMP_VAL_REG) { + oreg = ots->reg; } else { - if (ots->val_type != TEMP_VAL_REG) { - /* When allocating a new register, make sure to not spill the - input one. */ - tcg_regset_set_reg(allocated_regs, ts->reg); - ots->reg = tcg_reg_alloc(s, tcg_target_available_regs[otype], - allocated_regs, preferred_regs, - ots->indirect_base); - } - if (!tcg_out_mov(s, otype, ots->reg, ts->reg)) { - /* - * Cross register class move not supported. - * Store the source register into the destination slot - * and leave the destination temp as TEMP_VAL_MEM. - */ - assert(!temp_readonly(ots)); - if (!ts->mem_allocated) { - temp_allocate_frame(s, ots); - } - tcg_out_st(s, ts->type, ts->reg, - ots->mem_base->reg, ots->mem_offset); - ots->mem_coherent = 1; - temp_free_or_dead(s, ots, -1); - return; - } + /* Make sure to not spill the input register during allocation. */ + oreg = tcg_reg_alloc(s, tcg_target_available_regs[otype], + allocated_regs | ((TCGRegSet)1 << ireg), + preferred_regs, ots->indirect_base); } - ots->val_type = TEMP_VAL_REG; - ots->mem_coherent = 0; - s->reg_to_temp[ots->reg] = ots; - if (NEED_SYNC_ARG(0)) { - temp_sync(s, ots, allocated_regs, 0, 0); + if (!tcg_out_mov(s, otype, oreg, ireg)) { + /* + * Cross register class move not supported. + * Store the source register into the destination slot + * and leave the destination temp as TEMP_VAL_MEM. + */ + assert(!temp_readonly(ots)); + if (!ts->mem_allocated) { + temp_allocate_frame(s, ots); + } + tcg_out_st(s, ts->type, ireg, ots->mem_base->reg, ots->mem_offset); + set_temp_val_nonreg(s, ts, TEMP_VAL_MEM); + ots->mem_coherent = 1; + return; } } + set_temp_val_reg(s, ots, oreg); + ots->mem_coherent = 0; + + if (NEED_SYNC_ARG(0)) { + temp_sync(s, ots, allocated_regs, 0, 0); + } } /* @@ -3547,8 +4682,8 @@ static void tcg_reg_alloc_dup(TCGContext *s, const TCGOp *op) TCGRegSet dup_out_regs, dup_in_regs; TCGTemp *its, *ots; TCGType itype, vtype; - intptr_t endian_fixup; unsigned vece; + int lowpart_ofs; bool ok; ots = arg_temp(op->args[0]); @@ -3567,7 +4702,7 @@ static void tcg_reg_alloc_dup(TCGContext *s, const TCGOp *op) if (IS_DEAD_ARG(1)) { temp_dead(s, its); } - tcg_reg_alloc_do_movi(s, ots, val, arg_life, op->output_pref[0]); + tcg_reg_alloc_do_movi(s, ots, val, arg_life, output_pref(op, 0)); return; } @@ -3577,16 +4712,15 @@ static void tcg_reg_alloc_dup(TCGContext *s, const TCGOp *op) /* Allocate the output register now. */ if (ots->val_type != TEMP_VAL_REG) { TCGRegSet allocated_regs = s->reserved_regs; + TCGReg oreg; if (!IS_DEAD_ARG(1) && its->val_type == TEMP_VAL_REG) { /* Make sure to not spill the input register. */ tcg_regset_set_reg(allocated_regs, its->reg); } - ots->reg = tcg_reg_alloc(s, dup_out_regs, allocated_regs, - op->output_pref[0], ots->indirect_base); - ots->val_type = TEMP_VAL_REG; - ots->mem_coherent = 0; - s->reg_to_temp[ots->reg] = ots; + oreg = tcg_reg_alloc(s, dup_out_regs, allocated_regs, + output_pref(op, 0), ots->indirect_base); + set_temp_val_reg(s, ots, oreg); } switch (its->val_type) { @@ -3617,16 +4751,15 @@ static void tcg_reg_alloc_dup(TCGContext *s, const TCGOp *op) /* fall through */ case TEMP_VAL_MEM: -#if HOST_BIG_ENDIAN - endian_fixup = itype == TCG_TYPE_I32 ? 4 : 8; - endian_fixup -= 1 << vece; -#else - endian_fixup = 0; -#endif + lowpart_ofs = 0; + if (HOST_BIG_ENDIAN) { + lowpart_ofs = tcg_type_size(itype) - (1 << vece); + } if (tcg_out_dupm_vec(s, vtype, vece, ots->reg, its->mem_base->reg, - its->mem_offset + endian_fixup)) { + its->mem_offset + lowpart_ofs)) { goto done; } + /* Load the input into the destination vector register. */ tcg_out_ld(s, itype, ots->reg, its->mem_base->reg, its->mem_offset); break; @@ -3639,6 +4772,7 @@ static void tcg_reg_alloc_dup(TCGContext *s, const TCGOp *op) tcg_debug_assert(ok); done: + ots->mem_coherent = 0; if (IS_DEAD_ARG(1)) { temp_dead(s, its); } @@ -3663,21 +4797,52 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) TCGTemp *ts; TCGArg new_args[TCG_MAX_OP_ARGS]; int const_args[TCG_MAX_OP_ARGS]; + TCGCond op_cond; nb_oargs = def->nb_oargs; nb_iargs = def->nb_iargs; /* copy constants */ - memcpy(new_args + nb_oargs + nb_iargs, + memcpy(new_args + nb_oargs + nb_iargs, op->args + nb_oargs + nb_iargs, sizeof(TCGArg) * def->nb_cargs); i_allocated_regs = s->reserved_regs; o_allocated_regs = s->reserved_regs; - /* satisfy input constraints */ + switch (op->opc) { + case INDEX_op_brcond_i32: + case INDEX_op_brcond_i64: + op_cond = op->args[2]; + break; + case INDEX_op_setcond_i32: + case INDEX_op_setcond_i64: + case INDEX_op_negsetcond_i32: + case INDEX_op_negsetcond_i64: + case INDEX_op_cmp_vec: + op_cond = op->args[3]; + break; + case INDEX_op_brcond2_i32: + op_cond = op->args[4]; + break; + case INDEX_op_movcond_i32: + case INDEX_op_movcond_i64: + case INDEX_op_setcond2_i32: + case INDEX_op_cmpsel_vec: + op_cond = op->args[5]; + break; + default: + /* No condition within opcode. */ + op_cond = TCG_COND_ALWAYS; + break; + } + + /* satisfy input constraints */ for (k = 0; k < nb_iargs; k++) { - TCGRegSet i_preferred_regs, o_preferred_regs; + TCGRegSet i_preferred_regs, i_required_regs; + bool allocate_new_reg, copyto_new_reg; + TCGTemp *ts2; + int i1, i2; i = def->args_ct[nb_oargs + k].sort_index; arg = op->args[i]; @@ -3685,56 +4850,175 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) ts = arg_temp(arg); if (ts->val_type == TEMP_VAL_CONST - && tcg_target_const_match(ts->val, ts->type, arg_ct->ct)) { + && tcg_target_const_match(ts->val, arg_ct->ct, ts->type, + op_cond, TCGOP_VECE(op))) { /* constant is OK for instruction */ const_args[i] = 1; new_args[i] = ts->val; continue; } - i_preferred_regs = o_preferred_regs = 0; - if (arg_ct->ialias) { - o_preferred_regs = op->output_pref[arg_ct->alias_index]; + reg = ts->reg; + i_preferred_regs = 0; + i_required_regs = arg_ct->regs; + allocate_new_reg = false; + copyto_new_reg = false; - /* - * If the input is readonly, then it cannot also be an - * output and aliased to itself. If the input is not - * dead after the instruction, we must allocate a new - * register and move it. - */ - if (temp_readonly(ts) || !IS_DEAD_ARG(i)) { - goto allocate_in_reg; - } + switch (arg_ct->pair) { + case 0: /* not paired */ + if (arg_ct->ialias) { + i_preferred_regs = output_pref(op, arg_ct->alias_index); - /* - * Check if the current register has already been allocated - * for another input aliased to an output. - */ - if (ts->val_type == TEMP_VAL_REG) { - reg = ts->reg; - for (int k2 = 0; k2 < k; k2++) { - int i2 = def->args_ct[nb_oargs + k2].sort_index; - if (def->args_ct[i2].ialias && reg == new_args[i2]) { - goto allocate_in_reg; - } + /* + * If the input is readonly, then it cannot also be an + * output and aliased to itself. If the input is not + * dead after the instruction, we must allocate a new + * register and move it. + */ + if (temp_readonly(ts) || !IS_DEAD_ARG(i) + || def->args_ct[arg_ct->alias_index].newreg) { + allocate_new_reg = true; + } else if (ts->val_type == TEMP_VAL_REG) { + /* + * Check if the current register has already been + * allocated for another input. + */ + allocate_new_reg = + tcg_regset_test_reg(i_allocated_regs, reg); } } - i_preferred_regs = o_preferred_regs; + if (!allocate_new_reg) { + temp_load(s, ts, i_required_regs, i_allocated_regs, + i_preferred_regs); + reg = ts->reg; + allocate_new_reg = !tcg_regset_test_reg(i_required_regs, reg); + } + if (allocate_new_reg) { + /* + * Allocate a new register matching the constraint + * and move the temporary register into it. + */ + temp_load(s, ts, tcg_target_available_regs[ts->type], + i_allocated_regs, 0); + reg = tcg_reg_alloc(s, i_required_regs, i_allocated_regs, + i_preferred_regs, ts->indirect_base); + copyto_new_reg = true; + } + break; + + case 1: + /* First of an input pair; if i1 == i2, the second is an output. */ + i1 = i; + i2 = arg_ct->pair_index; + ts2 = i1 != i2 ? arg_temp(op->args[i2]) : NULL; + + /* + * It is easier to default to allocating a new pair + * and to identify a few cases where it's not required. + */ + if (arg_ct->ialias) { + i_preferred_regs = output_pref(op, arg_ct->alias_index); + if (IS_DEAD_ARG(i1) && + IS_DEAD_ARG(i2) && + !temp_readonly(ts) && + ts->val_type == TEMP_VAL_REG && + ts->reg < TCG_TARGET_NB_REGS - 1 && + tcg_regset_test_reg(i_required_regs, reg) && + !tcg_regset_test_reg(i_allocated_regs, reg) && + !tcg_regset_test_reg(i_allocated_regs, reg + 1) && + (ts2 + ? ts2->val_type == TEMP_VAL_REG && + ts2->reg == reg + 1 && + !temp_readonly(ts2) + : s->reg_to_temp[reg + 1] == NULL)) { + break; + } + } else { + /* Without aliasing, the pair must also be an input. */ + tcg_debug_assert(ts2); + if (ts->val_type == TEMP_VAL_REG && + ts2->val_type == TEMP_VAL_REG && + ts2->reg == reg + 1 && + tcg_regset_test_reg(i_required_regs, reg)) { + break; + } + } + reg = tcg_reg_alloc_pair(s, i_required_regs, i_allocated_regs, + 0, ts->indirect_base); + goto do_pair; + + case 2: /* pair second */ + reg = new_args[arg_ct->pair_index] + 1; + goto do_pair; + + case 3: /* ialias with second output, no first input */ + tcg_debug_assert(arg_ct->ialias); + i_preferred_regs = output_pref(op, arg_ct->alias_index); + + if (IS_DEAD_ARG(i) && + !temp_readonly(ts) && + ts->val_type == TEMP_VAL_REG && + reg > 0 && + s->reg_to_temp[reg - 1] == NULL && + tcg_regset_test_reg(i_required_regs, reg) && + !tcg_regset_test_reg(i_allocated_regs, reg) && + !tcg_regset_test_reg(i_allocated_regs, reg - 1)) { + tcg_regset_set_reg(i_allocated_regs, reg - 1); + break; + } + reg = tcg_reg_alloc_pair(s, i_required_regs >> 1, + i_allocated_regs, 0, + ts->indirect_base); + tcg_regset_set_reg(i_allocated_regs, reg); + reg += 1; + goto do_pair; + + do_pair: + /* + * If an aliased input is not dead after the instruction, + * we must allocate a new register and move it. + */ + if (arg_ct->ialias && (!IS_DEAD_ARG(i) || temp_readonly(ts))) { + TCGRegSet t_allocated_regs = i_allocated_regs; + + /* + * Because of the alias, and the continued life, make sure + * that the temp is somewhere *other* than the reg pair, + * and we get a copy in reg. + */ + tcg_regset_set_reg(t_allocated_regs, reg); + tcg_regset_set_reg(t_allocated_regs, reg + 1); + if (ts->val_type == TEMP_VAL_REG && ts->reg == reg) { + /* If ts was already in reg, copy it somewhere else. */ + TCGReg nr; + bool ok; + + tcg_debug_assert(ts->kind != TEMP_FIXED); + nr = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], + t_allocated_regs, 0, ts->indirect_base); + ok = tcg_out_mov(s, ts->type, nr, reg); + tcg_debug_assert(ok); + + set_temp_val_reg(s, ts, nr); + } else { + temp_load(s, ts, tcg_target_available_regs[ts->type], + t_allocated_regs, 0); + copyto_new_reg = true; + } + } else { + /* Preferably allocate to reg, otherwise copy. */ + i_required_regs = (TCGRegSet)1 << reg; + temp_load(s, ts, i_required_regs, i_allocated_regs, + i_preferred_regs); + copyto_new_reg = ts->reg != reg; + } + break; + + default: + g_assert_not_reached(); } - temp_load(s, ts, arg_ct->regs, i_allocated_regs, i_preferred_regs); - reg = ts->reg; - - if (!tcg_regset_test_reg(arg_ct->regs, reg)) { - allocate_in_reg: - /* - * Allocate a new register matching the constraint - * and move the temporary register into it. - */ - temp_load(s, ts, tcg_target_available_regs[ts->type], - i_allocated_regs, 0); - reg = tcg_reg_alloc(s, arg_ct->regs, i_allocated_regs, - o_preferred_regs, ts->indirect_base); + if (copyto_new_reg) { if (!tcg_out_mov(s, ts->type, reg, ts->reg)) { /* * Cross register class move not supported. Sync the @@ -3749,7 +5033,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) const_args[i] = 0; tcg_regset_set_reg(i_allocated_regs, reg); } - + /* mark dead temporaries and free the associated registers */ for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { if (IS_DEAD_ARG(i)) { @@ -3763,7 +5047,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) tcg_reg_alloc_bb_end(s, i_allocated_regs); } else { if (def->flags & TCG_OPF_CALL_CLOBBER) { - /* XXX: permit generic clobber register list ? */ + /* XXX: permit generic clobber register list ? */ for (i = 0; i < TCG_TARGET_NB_REGS; i++) { if (tcg_regset_test_reg(tcg_target_call_clobber_regs, i)) { tcg_reg_free(s, i, i_allocated_regs); @@ -3775,7 +5059,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) an exception. */ sync_globals(s, i_allocated_regs); } - + /* satisfy the output constraints */ for(k = 0; k < nb_oargs; k++) { i = def->args_ct[k].sort_index; @@ -3786,38 +5070,103 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) /* ENV should not be modified. */ tcg_debug_assert(!temp_readonly(ts)); - if (arg_ct->oalias && !const_args[arg_ct->alias_index]) { - reg = new_args[arg_ct->alias_index]; - } else if (arg_ct->newreg) { - reg = tcg_reg_alloc(s, arg_ct->regs, - i_allocated_regs | o_allocated_regs, - op->output_pref[k], ts->indirect_base); - } else { - reg = tcg_reg_alloc(s, arg_ct->regs, o_allocated_regs, - op->output_pref[k], ts->indirect_base); + switch (arg_ct->pair) { + case 0: /* not paired */ + if (arg_ct->oalias && !const_args[arg_ct->alias_index]) { + reg = new_args[arg_ct->alias_index]; + } else if (arg_ct->newreg) { + reg = tcg_reg_alloc(s, arg_ct->regs, + i_allocated_regs | o_allocated_regs, + output_pref(op, k), ts->indirect_base); + } else { + reg = tcg_reg_alloc(s, arg_ct->regs, o_allocated_regs, + output_pref(op, k), ts->indirect_base); + } + break; + + case 1: /* first of pair */ + if (arg_ct->oalias) { + reg = new_args[arg_ct->alias_index]; + } else if (arg_ct->newreg) { + reg = tcg_reg_alloc_pair(s, arg_ct->regs, + i_allocated_regs | o_allocated_regs, + output_pref(op, k), + ts->indirect_base); + } else { + reg = tcg_reg_alloc_pair(s, arg_ct->regs, o_allocated_regs, + output_pref(op, k), + ts->indirect_base); + } + break; + + case 2: /* second of pair */ + if (arg_ct->oalias) { + reg = new_args[arg_ct->alias_index]; + } else { + reg = new_args[arg_ct->pair_index] + 1; + } + break; + + case 3: /* first of pair, aliasing with a second input */ + tcg_debug_assert(!arg_ct->newreg); + reg = new_args[arg_ct->pair_index] - 1; + break; + + default: + g_assert_not_reached(); } tcg_regset_set_reg(o_allocated_regs, reg); - if (ts->val_type == TEMP_VAL_REG) { - s->reg_to_temp[ts->reg] = NULL; - } - ts->val_type = TEMP_VAL_REG; - ts->reg = reg; - /* - * Temp value is modified, so the value kept in memory is - * potentially not the same. - */ + set_temp_val_reg(s, ts, reg); ts->mem_coherent = 0; - s->reg_to_temp[reg] = ts; new_args[i] = reg; } } /* emit instruction */ - if (def->flags & TCG_OPF_VECTOR) { - tcg_out_vec_op(s, op->opc, TCGOP_VECL(op), TCGOP_VECE(op), - new_args, const_args); - } else { - tcg_out_op(s, op->opc, new_args, const_args); + switch (op->opc) { + case INDEX_op_ext8s_i32: + tcg_out_ext8s(s, TCG_TYPE_I32, new_args[0], new_args[1]); + break; + case INDEX_op_ext8s_i64: + tcg_out_ext8s(s, TCG_TYPE_I64, new_args[0], new_args[1]); + break; + case INDEX_op_ext8u_i32: + case INDEX_op_ext8u_i64: + tcg_out_ext8u(s, new_args[0], new_args[1]); + break; + case INDEX_op_ext16s_i32: + tcg_out_ext16s(s, TCG_TYPE_I32, new_args[0], new_args[1]); + break; + case INDEX_op_ext16s_i64: + tcg_out_ext16s(s, TCG_TYPE_I64, new_args[0], new_args[1]); + break; + case INDEX_op_ext16u_i32: + case INDEX_op_ext16u_i64: + tcg_out_ext16u(s, new_args[0], new_args[1]); + break; + case INDEX_op_ext32s_i64: + tcg_out_ext32s(s, new_args[0], new_args[1]); + break; + case INDEX_op_ext32u_i64: + tcg_out_ext32u(s, new_args[0], new_args[1]); + break; + case INDEX_op_ext_i32_i64: + tcg_out_exts_i32_i64(s, new_args[0], new_args[1]); + break; + case INDEX_op_extu_i32_i64: + tcg_out_extu_i32_i64(s, new_args[0], new_args[1]); + break; + case INDEX_op_extrl_i64_i32: + tcg_out_extrl_i64_i32(s, new_args[0], new_args[1]); + break; + default: + if (def->flags & TCG_OPF_VECTOR) { + tcg_out_vec_op(s, op->opc, TCGOP_VECL(op), TCGOP_VECE(op), + new_args, const_args); + } else { + tcg_out_op(s, op->opc, new_args, const_args); + } + break; } /* move the outputs in the correct register if needed */ @@ -3857,6 +5206,7 @@ static bool tcg_reg_alloc_dup2(TCGContext *s, const TCGOp *op) TCGRegSet allocated_regs = s->reserved_regs; TCGRegSet dup_out_regs = tcg_op_defs[INDEX_op_dup_vec].args_ct[0].regs; + TCGReg oreg; /* Make sure to not spill the input registers. */ if (!IS_DEAD_ARG(1) && itsl->val_type == TEMP_VAL_REG) { @@ -3866,11 +5216,9 @@ static bool tcg_reg_alloc_dup2(TCGContext *s, const TCGOp *op) tcg_regset_set_reg(allocated_regs, itsh->reg); } - ots->reg = tcg_reg_alloc(s, dup_out_regs, allocated_regs, - op->output_pref[0], ots->indirect_base); - ots->val_type = TEMP_VAL_REG; - ots->mem_coherent = 0; - s->reg_to_temp[ots->reg] = ots; + oreg = tcg_reg_alloc(s, dup_out_regs, allocated_regs, + output_pref(op, 0), ots->indirect_base); + set_temp_val_reg(s, ots, oreg); } /* Promote dup2 of immediates to dupi_vec. */ @@ -3891,18 +5239,14 @@ static bool tcg_reg_alloc_dup2(TCGContext *s, const TCGOp *op) } /* If the two inputs form one 64-bit value, try dupm_vec. */ - if (itsl + 1 == itsh && itsl->base_type == TCG_TYPE_I64) { - if (!itsl->mem_coherent) { - temp_sync(s, itsl, s->reserved_regs, 0, 0); - } - if (!itsh->mem_coherent) { - temp_sync(s, itsh, s->reserved_regs, 0, 0); - } -#if HOST_BIG_ENDIAN - TCGTemp *its = itsh; -#else - TCGTemp *its = itsl; -#endif + if (itsl->temp_subindex == HOST_BIG_ENDIAN && + itsh->temp_subindex == !HOST_BIG_ENDIAN && + itsl == itsh + (HOST_BIG_ENDIAN ? 1 : -1)) { + TCGTemp *its = itsl - HOST_BIG_ENDIAN; + + temp_sync(s, its + 0, s->reserved_regs, 0, 0); + temp_sync(s, its + 1, s->reserved_regs, 0, 0); + if (tcg_out_dupm_vec(s, vtype, MO_64, ots->reg, its->mem_base->reg, its->mem_offset)) { goto done; @@ -3913,6 +5257,7 @@ static bool tcg_reg_alloc_dup2(TCGContext *s, const TCGOp *op) return false; done: + ots->mem_coherent = 0; if (IS_DEAD_ARG(1)) { temp_dead(s, itsl); } @@ -3927,298 +5272,813 @@ static bool tcg_reg_alloc_dup2(TCGContext *s, const TCGOp *op) return true; } -#ifdef TCG_TARGET_STACK_GROWSUP -#define STACK_DIR(x) (-(x)) -#else -#define STACK_DIR(x) (x) -#endif +static void load_arg_reg(TCGContext *s, TCGReg reg, TCGTemp *ts, + TCGRegSet allocated_regs) +{ + if (ts->val_type == TEMP_VAL_REG) { + if (ts->reg != reg) { + tcg_reg_free(s, reg, allocated_regs); + if (!tcg_out_mov(s, ts->type, reg, ts->reg)) { + /* + * Cross register class move not supported. Sync the + * temp back to its slot and load from there. + */ + temp_sync(s, ts, allocated_regs, 0, 0); + tcg_out_ld(s, ts->type, reg, + ts->mem_base->reg, ts->mem_offset); + } + } + } else { + TCGRegSet arg_set = 0; + + tcg_reg_free(s, reg, allocated_regs); + tcg_regset_set_reg(arg_set, reg); + temp_load(s, ts, arg_set, allocated_regs, 0); + } +} + +static void load_arg_stk(TCGContext *s, unsigned arg_slot, TCGTemp *ts, + TCGRegSet allocated_regs) +{ + /* + * When the destination is on the stack, load up the temp and store. + * If there are many call-saved registers, the temp might live to + * see another use; otherwise it'll be discarded. + */ + temp_load(s, ts, tcg_target_available_regs[ts->type], allocated_regs, 0); + tcg_out_st(s, ts->type, ts->reg, TCG_REG_CALL_STACK, + arg_slot_stk_ofs(arg_slot)); +} + +static void load_arg_normal(TCGContext *s, const TCGCallArgumentLoc *l, + TCGTemp *ts, TCGRegSet *allocated_regs) +{ + if (arg_slot_reg_p(l->arg_slot)) { + TCGReg reg = tcg_target_call_iarg_regs[l->arg_slot]; + load_arg_reg(s, reg, ts, *allocated_regs); + tcg_regset_set_reg(*allocated_regs, reg); + } else { + load_arg_stk(s, l->arg_slot, ts, *allocated_regs); + } +} + +static void load_arg_ref(TCGContext *s, unsigned arg_slot, TCGReg ref_base, + intptr_t ref_off, TCGRegSet *allocated_regs) +{ + TCGReg reg; + + if (arg_slot_reg_p(arg_slot)) { + reg = tcg_target_call_iarg_regs[arg_slot]; + tcg_reg_free(s, reg, *allocated_regs); + tcg_out_addi_ptr(s, reg, ref_base, ref_off); + tcg_regset_set_reg(*allocated_regs, reg); + } else { + reg = tcg_reg_alloc(s, tcg_target_available_regs[TCG_TYPE_PTR], + *allocated_regs, 0, false); + tcg_out_addi_ptr(s, reg, ref_base, ref_off); + tcg_out_st(s, TCG_TYPE_PTR, reg, TCG_REG_CALL_STACK, + arg_slot_stk_ofs(arg_slot)); + } +} static void tcg_reg_alloc_call(TCGContext *s, TCGOp *op) { const int nb_oargs = TCGOP_CALLO(op); const int nb_iargs = TCGOP_CALLI(op); const TCGLifeData arg_life = op->life; - const TCGHelperInfo *info; - int flags, nb_regs, i; - TCGReg reg; - TCGArg arg; - TCGTemp *ts; - intptr_t stack_offset; - size_t call_stack_size; - tcg_insn_unit *func_addr; - int allocate_args; - TCGRegSet allocated_regs; + const TCGHelperInfo *info = tcg_call_info(op); + TCGRegSet allocated_regs = s->reserved_regs; + int i; - func_addr = tcg_call_func(op); - info = tcg_call_info(op); - flags = info->flags; + /* + * Move inputs into place in reverse order, + * so that we place stacked arguments first. + */ + for (i = nb_iargs - 1; i >= 0; --i) { + const TCGCallArgumentLoc *loc = &info->in[i]; + TCGTemp *ts = arg_temp(op->args[nb_oargs + i]); - nb_regs = ARRAY_SIZE(tcg_target_call_iarg_regs); - if (nb_regs > nb_iargs) { - nb_regs = nb_iargs; - } - - /* assign stack slots first */ - call_stack_size = (nb_iargs - nb_regs) * sizeof(tcg_target_long); - call_stack_size = (call_stack_size + TCG_TARGET_STACK_ALIGN - 1) & - ~(TCG_TARGET_STACK_ALIGN - 1); - allocate_args = (call_stack_size > TCG_STATIC_CALL_ARGS_SIZE); - if (allocate_args) { - /* XXX: if more than TCG_STATIC_CALL_ARGS_SIZE is needed, - preallocate call stack */ - tcg_abort(); - } - - stack_offset = TCG_TARGET_CALL_STACK_OFFSET; - for (i = nb_regs; i < nb_iargs; i++) { - arg = op->args[nb_oargs + i]; -#ifdef TCG_TARGET_STACK_GROWSUP - stack_offset -= sizeof(tcg_target_long); -#endif - if (arg != TCG_CALL_DUMMY_ARG) { - ts = arg_temp(arg); - temp_load(s, ts, tcg_target_available_regs[ts->type], - s->reserved_regs, 0); - tcg_out_st(s, ts->type, ts->reg, TCG_REG_CALL_STACK, stack_offset); - } -#ifndef TCG_TARGET_STACK_GROWSUP - stack_offset += sizeof(tcg_target_long); -#endif - } - - /* assign input registers */ - allocated_regs = s->reserved_regs; - for (i = 0; i < nb_regs; i++) { - arg = op->args[nb_oargs + i]; - if (arg != TCG_CALL_DUMMY_ARG) { - ts = arg_temp(arg); - reg = tcg_target_call_iarg_regs[i]; - - if (ts->val_type == TEMP_VAL_REG) { - if (ts->reg != reg) { - tcg_reg_free(s, reg, allocated_regs); - if (!tcg_out_mov(s, ts->type, reg, ts->reg)) { - /* - * Cross register class move not supported. Sync the - * temp back to its slot and load from there. - */ - temp_sync(s, ts, allocated_regs, 0, 0); - tcg_out_ld(s, ts->type, reg, - ts->mem_base->reg, ts->mem_offset); - } - } - } else { - TCGRegSet arg_set = 0; - - tcg_reg_free(s, reg, allocated_regs); - tcg_regset_set_reg(arg_set, reg); - temp_load(s, ts, arg_set, allocated_regs, 0); - } - - tcg_regset_set_reg(allocated_regs, reg); + switch (loc->kind) { + case TCG_CALL_ARG_NORMAL: + case TCG_CALL_ARG_EXTEND_U: + case TCG_CALL_ARG_EXTEND_S: + load_arg_normal(s, loc, ts, &allocated_regs); + break; + case TCG_CALL_ARG_BY_REF: + load_arg_stk(s, loc->ref_slot, ts, allocated_regs); + load_arg_ref(s, loc->arg_slot, TCG_REG_CALL_STACK, + arg_slot_stk_ofs(loc->ref_slot), + &allocated_regs); + break; + case TCG_CALL_ARG_BY_REF_N: + load_arg_stk(s, loc->ref_slot, ts, allocated_regs); + break; + default: + g_assert_not_reached(); } } - - /* mark dead temporaries and free the associated registers */ + + /* Mark dead temporaries and free the associated registers. */ for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) { if (IS_DEAD_ARG(i)) { temp_dead(s, arg_temp(op->args[i])); } } - - /* clobber call registers */ + + /* Clobber call registers. */ for (i = 0; i < TCG_TARGET_NB_REGS; i++) { if (tcg_regset_test_reg(tcg_target_call_clobber_regs, i)) { tcg_reg_free(s, i, allocated_regs); } } - /* Save globals if they might be written by the helper, sync them if - they might be read. */ - if (flags & TCG_CALL_NO_READ_GLOBALS) { + /* + * Save globals if they might be written by the helper, + * sync them if they might be read. + */ + if (info->flags & TCG_CALL_NO_READ_GLOBALS) { /* Nothing to do */ - } else if (flags & TCG_CALL_NO_WRITE_GLOBALS) { + } else if (info->flags & TCG_CALL_NO_WRITE_GLOBALS) { sync_globals(s, allocated_regs); } else { save_globals(s, allocated_regs); } -#ifdef CONFIG_TCG_INTERPRETER - { - gpointer hash = (gpointer)(uintptr_t)info->typemask; - ffi_cif *cif = g_hash_table_lookup(ffi_table, hash); - assert(cif != NULL); - tcg_out_call(s, func_addr, cif); - } -#else - tcg_out_call(s, func_addr); -#endif + /* + * If the ABI passes a pointer to the returned struct as the first + * argument, load that now. Pass a pointer to the output home slot. + */ + if (info->out_kind == TCG_CALL_RET_BY_REF) { + TCGTemp *ts = arg_temp(op->args[0]); - /* assign output registers and emit moves if needed */ - for(i = 0; i < nb_oargs; i++) { - arg = op->args[i]; - ts = arg_temp(arg); - - /* ENV should not be modified. */ - tcg_debug_assert(!temp_readonly(ts)); - - reg = tcg_target_call_oarg_regs[i]; - tcg_debug_assert(s->reg_to_temp[reg] == NULL); - if (ts->val_type == TEMP_VAL_REG) { - s->reg_to_temp[ts->reg] = NULL; + if (!ts->mem_allocated) { + temp_allocate_frame(s, ts); } - ts->val_type = TEMP_VAL_REG; - ts->reg = reg; - ts->mem_coherent = 0; - s->reg_to_temp[reg] = ts; + load_arg_ref(s, 0, ts->mem_base->reg, ts->mem_offset, &allocated_regs); + } + + tcg_out_call(s, tcg_call_func(op), info); + + /* Assign output registers and emit moves if needed. */ + switch (info->out_kind) { + case TCG_CALL_RET_NORMAL: + for (i = 0; i < nb_oargs; i++) { + TCGTemp *ts = arg_temp(op->args[i]); + TCGReg reg = tcg_target_call_oarg_reg(TCG_CALL_RET_NORMAL, i); + + /* ENV should not be modified. */ + tcg_debug_assert(!temp_readonly(ts)); + + set_temp_val_reg(s, ts, reg); + ts->mem_coherent = 0; + } + break; + + case TCG_CALL_RET_BY_VEC: + { + TCGTemp *ts = arg_temp(op->args[0]); + + tcg_debug_assert(ts->base_type == TCG_TYPE_I128); + tcg_debug_assert(ts->temp_subindex == 0); + if (!ts->mem_allocated) { + temp_allocate_frame(s, ts); + } + tcg_out_st(s, TCG_TYPE_V128, + tcg_target_call_oarg_reg(TCG_CALL_RET_BY_VEC, 0), + ts->mem_base->reg, ts->mem_offset); + } + /* fall through to mark all parts in memory */ + + case TCG_CALL_RET_BY_REF: + /* The callee has performed a write through the reference. */ + for (i = 0; i < nb_oargs; i++) { + TCGTemp *ts = arg_temp(op->args[i]); + ts->val_type = TEMP_VAL_MEM; + } + break; + + default: + g_assert_not_reached(); + } + + /* Flush or discard output registers as needed. */ + for (i = 0; i < nb_oargs; i++) { + TCGTemp *ts = arg_temp(op->args[i]); if (NEED_SYNC_ARG(i)) { - temp_sync(s, ts, allocated_regs, 0, IS_DEAD_ARG(i)); + temp_sync(s, ts, s->reserved_regs, 0, IS_DEAD_ARG(i)); } else if (IS_DEAD_ARG(i)) { temp_dead(s, ts); } } } -#ifdef CONFIG_PROFILER - -/* avoid copy/paste errors */ -#define PROF_ADD(to, from, field) \ - do { \ - (to)->field += qatomic_read(&((from)->field)); \ - } while (0) - -#define PROF_MAX(to, from, field) \ - do { \ - typeof((from)->field) val__ = qatomic_read(&((from)->field)); \ - if (val__ > (to)->field) { \ - (to)->field = val__; \ - } \ - } while (0) - -/* Pass in a zero'ed @prof */ -static inline -void tcg_profile_snapshot(TCGProfile *prof, bool counters, bool table) +/** + * atom_and_align_for_opc: + * @s: tcg context + * @opc: memory operation code + * @host_atom: MO_ATOM_{IFALIGN,WITHIN16,SUBALIGN} for host operations + * @allow_two_ops: true if we are prepared to issue two operations + * + * Return the alignment and atomicity to use for the inline fast path + * for the given memory operation. The alignment may be larger than + * that specified in @opc, and the correct alignment will be diagnosed + * by the slow path helper. + * + * If @allow_two_ops, the host is prepared to test for 2x alignment, + * and issue two loads or stores for subalignment. + */ +static TCGAtomAlign atom_and_align_for_opc(TCGContext *s, MemOp opc, + MemOp host_atom, bool allow_two_ops) { - unsigned int n_ctxs = qatomic_read(&tcg_cur_ctxs); - unsigned int i; + MemOp align = get_alignment_bits(opc); + MemOp size = opc & MO_SIZE; + MemOp half = size ? size - 1 : 0; + MemOp atom = opc & MO_ATOM_MASK; + MemOp atmax; - for (i = 0; i < n_ctxs; i++) { - TCGContext *s = qatomic_read(&tcg_ctxs[i]); - const TCGProfile *orig = &s->prof; + switch (atom) { + case MO_ATOM_NONE: + /* The operation requires no specific atomicity. */ + atmax = MO_8; + break; - if (counters) { - PROF_ADD(prof, orig, cpu_exec_time); - PROF_ADD(prof, orig, tb_count1); - PROF_ADD(prof, orig, tb_count); - PROF_ADD(prof, orig, op_count); - PROF_MAX(prof, orig, op_count_max); - PROF_ADD(prof, orig, temp_count); - PROF_MAX(prof, orig, temp_count_max); - PROF_ADD(prof, orig, del_op_count); - PROF_ADD(prof, orig, code_in_len); - PROF_ADD(prof, orig, code_out_len); - PROF_ADD(prof, orig, search_out_len); - PROF_ADD(prof, orig, interm_time); - PROF_ADD(prof, orig, code_time); - PROF_ADD(prof, orig, la_time); - PROF_ADD(prof, orig, opt_time); - PROF_ADD(prof, orig, restore_count); - PROF_ADD(prof, orig, restore_time); + case MO_ATOM_IFALIGN: + atmax = size; + break; + + case MO_ATOM_IFALIGN_PAIR: + atmax = half; + break; + + case MO_ATOM_WITHIN16: + atmax = size; + if (size == MO_128) { + /* Misalignment implies !within16, and therefore no atomicity. */ + } else if (host_atom != MO_ATOM_WITHIN16) { + /* The host does not implement within16, so require alignment. */ + align = MAX(align, size); } - if (table) { - int i; + break; - for (i = 0; i < NB_OPS; i++) { - PROF_ADD(prof, orig, table_op_count[i]); + case MO_ATOM_WITHIN16_PAIR: + atmax = size; + /* + * Misalignment implies !within16, and therefore half atomicity. + * Any host prepared for two operations can implement this with + * half alignment. + */ + if (host_atom != MO_ATOM_WITHIN16 && allow_two_ops) { + align = MAX(align, half); + } + break; + + case MO_ATOM_SUBALIGN: + atmax = size; + if (host_atom != MO_ATOM_SUBALIGN) { + /* If unaligned but not odd, there are subobjects up to half. */ + if (allow_two_ops) { + align = MAX(align, half); + } else { + align = MAX(align, size); } } + break; + + default: + g_assert_not_reached(); + } + + return (TCGAtomAlign){ .atom = atmax, .align = align }; +} + +/* + * Similarly for qemu_ld/st slow path helpers. + * We must re-implement tcg_gen_callN and tcg_reg_alloc_call simultaneously, + * using only the provided backend tcg_out_* functions. + */ + +static int tcg_out_helper_stk_ofs(TCGType type, unsigned slot) +{ + int ofs = arg_slot_stk_ofs(slot); + + /* + * Each stack slot is TCG_TARGET_LONG_BITS. If the host does not + * require extension to uint64_t, adjust the address for uint32_t. + */ + if (HOST_BIG_ENDIAN && + TCG_TARGET_REG_BITS == 64 && + type == TCG_TYPE_I32) { + ofs += 4; + } + return ofs; +} + +static void tcg_out_helper_load_slots(TCGContext *s, + unsigned nmov, TCGMovExtend *mov, + const TCGLdstHelperParam *parm) +{ + unsigned i; + TCGReg dst3; + + /* + * Start from the end, storing to the stack first. + * This frees those registers, so we need not consider overlap. + */ + for (i = nmov; i-- > 0; ) { + unsigned slot = mov[i].dst; + + if (arg_slot_reg_p(slot)) { + goto found_reg; + } + + TCGReg src = mov[i].src; + TCGType dst_type = mov[i].dst_type; + MemOp dst_mo = dst_type == TCG_TYPE_I32 ? MO_32 : MO_64; + + /* The argument is going onto the stack; extend into scratch. */ + if ((mov[i].src_ext & MO_SIZE) != dst_mo) { + tcg_debug_assert(parm->ntmp != 0); + mov[i].dst = src = parm->tmp[0]; + tcg_out_movext1(s, &mov[i]); + } + + tcg_out_st(s, dst_type, src, TCG_REG_CALL_STACK, + tcg_out_helper_stk_ofs(dst_type, slot)); + } + return; + + found_reg: + /* + * The remaining arguments are in registers. + * Convert slot numbers to argument registers. + */ + nmov = i + 1; + for (i = 0; i < nmov; ++i) { + mov[i].dst = tcg_target_call_iarg_regs[mov[i].dst]; + } + + switch (nmov) { + case 4: + /* The backend must have provided enough temps for the worst case. */ + tcg_debug_assert(parm->ntmp >= 2); + + dst3 = mov[3].dst; + for (unsigned j = 0; j < 3; ++j) { + if (dst3 == mov[j].src) { + /* + * Conflict. Copy the source to a temporary, perform the + * remaining moves, then the extension from our scratch + * on the way out. + */ + TCGReg scratch = parm->tmp[1]; + + tcg_out_mov(s, mov[3].src_type, scratch, mov[3].src); + tcg_out_movext3(s, mov, mov + 1, mov + 2, parm->tmp[0]); + tcg_out_movext1_new_src(s, &mov[3], scratch); + break; + } + } + + /* No conflicts: perform this move and continue. */ + tcg_out_movext1(s, &mov[3]); + /* fall through */ + + case 3: + tcg_out_movext3(s, mov, mov + 1, mov + 2, + parm->ntmp ? parm->tmp[0] : -1); + break; + case 2: + tcg_out_movext2(s, mov, mov + 1, + parm->ntmp ? parm->tmp[0] : -1); + break; + case 1: + tcg_out_movext1(s, mov); + break; + default: + g_assert_not_reached(); } } -#undef PROF_ADD -#undef PROF_MAX - -static void tcg_profile_snapshot_counters(TCGProfile *prof) +static void tcg_out_helper_load_imm(TCGContext *s, unsigned slot, + TCGType type, tcg_target_long imm, + const TCGLdstHelperParam *parm) { - tcg_profile_snapshot(prof, true, false); -} - -static void tcg_profile_snapshot_table(TCGProfile *prof) -{ - tcg_profile_snapshot(prof, false, true); -} - -void tcg_dump_op_count(GString *buf) -{ - TCGProfile prof = {}; - int i; - - tcg_profile_snapshot_table(&prof); - for (i = 0; i < NB_OPS; i++) { - g_string_append_printf(buf, "%s %" PRId64 "\n", tcg_op_defs[i].name, - prof.table_op_count[i]); + if (arg_slot_reg_p(slot)) { + tcg_out_movi(s, type, tcg_target_call_iarg_regs[slot], imm); + } else { + int ofs = tcg_out_helper_stk_ofs(type, slot); + if (!tcg_out_sti(s, type, imm, TCG_REG_CALL_STACK, ofs)) { + tcg_debug_assert(parm->ntmp != 0); + tcg_out_movi(s, type, parm->tmp[0], imm); + tcg_out_st(s, type, parm->tmp[0], TCG_REG_CALL_STACK, ofs); + } } } -int64_t tcg_cpu_exec_time(void) +static void tcg_out_helper_load_common_args(TCGContext *s, + const TCGLabelQemuLdst *ldst, + const TCGLdstHelperParam *parm, + const TCGHelperInfo *info, + unsigned next_arg) { - unsigned int n_ctxs = qatomic_read(&tcg_cur_ctxs); - unsigned int i; - int64_t ret = 0; + TCGMovExtend ptr_mov = { + .dst_type = TCG_TYPE_PTR, + .src_type = TCG_TYPE_PTR, + .src_ext = sizeof(void *) == 4 ? MO_32 : MO_64 + }; + const TCGCallArgumentLoc *loc = &info->in[0]; + TCGType type; + unsigned slot; + tcg_target_ulong imm; - for (i = 0; i < n_ctxs; i++) { - const TCGContext *s = qatomic_read(&tcg_ctxs[i]); - const TCGProfile *prof = &s->prof; + /* + * Handle env, which is always first. + */ + ptr_mov.dst = loc->arg_slot; + ptr_mov.src = TCG_AREG0; + tcg_out_helper_load_slots(s, 1, &ptr_mov, parm); - ret += qatomic_read(&prof->cpu_exec_time); + /* + * Handle oi. + */ + imm = ldst->oi; + loc = &info->in[next_arg]; + type = TCG_TYPE_I32; + switch (loc->kind) { + case TCG_CALL_ARG_NORMAL: + break; + case TCG_CALL_ARG_EXTEND_U: + case TCG_CALL_ARG_EXTEND_S: + /* No extension required for MemOpIdx. */ + tcg_debug_assert(imm <= INT32_MAX); + type = TCG_TYPE_REG; + break; + default: + g_assert_not_reached(); + } + tcg_out_helper_load_imm(s, loc->arg_slot, type, imm, parm); + next_arg++; + + /* + * Handle ra. + */ + loc = &info->in[next_arg]; + slot = loc->arg_slot; + if (parm->ra_gen) { + int arg_reg = -1; + TCGReg ra_reg; + + if (arg_slot_reg_p(slot)) { + arg_reg = tcg_target_call_iarg_regs[slot]; + } + ra_reg = parm->ra_gen(s, ldst, arg_reg); + + ptr_mov.dst = slot; + ptr_mov.src = ra_reg; + tcg_out_helper_load_slots(s, 1, &ptr_mov, parm); + } else { + imm = (uintptr_t)ldst->raddr; + tcg_out_helper_load_imm(s, slot, TCG_TYPE_PTR, imm, parm); } - return ret; -} -#else -void tcg_dump_op_count(GString *buf) -{ - g_string_append_printf(buf, "[TCG profiler not compiled]\n"); } -int64_t tcg_cpu_exec_time(void) +static unsigned tcg_out_helper_add_mov(TCGMovExtend *mov, + const TCGCallArgumentLoc *loc, + TCGType dst_type, TCGType src_type, + TCGReg lo, TCGReg hi) { - error_report("%s: TCG profiler not compiled", __func__); - exit(EXIT_FAILURE); + MemOp reg_mo; + + if (dst_type <= TCG_TYPE_REG) { + MemOp src_ext; + + switch (loc->kind) { + case TCG_CALL_ARG_NORMAL: + src_ext = src_type == TCG_TYPE_I32 ? MO_32 : MO_64; + break; + case TCG_CALL_ARG_EXTEND_U: + dst_type = TCG_TYPE_REG; + src_ext = MO_UL; + break; + case TCG_CALL_ARG_EXTEND_S: + dst_type = TCG_TYPE_REG; + src_ext = MO_SL; + break; + default: + g_assert_not_reached(); + } + + mov[0].dst = loc->arg_slot; + mov[0].dst_type = dst_type; + mov[0].src = lo; + mov[0].src_type = src_type; + mov[0].src_ext = src_ext; + return 1; + } + + if (TCG_TARGET_REG_BITS == 32) { + assert(dst_type == TCG_TYPE_I64); + reg_mo = MO_32; + } else { + assert(dst_type == TCG_TYPE_I128); + reg_mo = MO_64; + } + + mov[0].dst = loc[HOST_BIG_ENDIAN].arg_slot; + mov[0].src = lo; + mov[0].dst_type = TCG_TYPE_REG; + mov[0].src_type = TCG_TYPE_REG; + mov[0].src_ext = reg_mo; + + mov[1].dst = loc[!HOST_BIG_ENDIAN].arg_slot; + mov[1].src = hi; + mov[1].dst_type = TCG_TYPE_REG; + mov[1].src_type = TCG_TYPE_REG; + mov[1].src_ext = reg_mo; + + return 2; } -#endif - -int tcg_gen_code(TCGContext *s, TranslationBlock *tb) +static void tcg_out_ld_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst, + const TCGLdstHelperParam *parm) { -#ifdef CONFIG_PROFILER - TCGProfile *prof = &s->prof; -#endif - int i, num_insns; + const TCGHelperInfo *info; + const TCGCallArgumentLoc *loc; + TCGMovExtend mov[2]; + unsigned next_arg, nmov; + MemOp mop = get_memop(ldst->oi); + + switch (mop & MO_SIZE) { + case MO_8: + case MO_16: + case MO_32: + info = &info_helper_ld32_mmu; + break; + case MO_64: + info = &info_helper_ld64_mmu; + break; + case MO_128: + info = &info_helper_ld128_mmu; + break; + default: + g_assert_not_reached(); + } + + /* Defer env argument. */ + next_arg = 1; + + loc = &info->in[next_arg]; + if (TCG_TARGET_REG_BITS == 32 && s->addr_type == TCG_TYPE_I32) { + /* + * 32-bit host with 32-bit guest: zero-extend the guest address + * to 64-bits for the helper by storing the low part, then + * load a zero for the high part. + */ + tcg_out_helper_add_mov(mov, loc + HOST_BIG_ENDIAN, + TCG_TYPE_I32, TCG_TYPE_I32, + ldst->addrlo_reg, -1); + tcg_out_helper_load_slots(s, 1, mov, parm); + + tcg_out_helper_load_imm(s, loc[!HOST_BIG_ENDIAN].arg_slot, + TCG_TYPE_I32, 0, parm); + next_arg += 2; + } else { + nmov = tcg_out_helper_add_mov(mov, loc, TCG_TYPE_I64, s->addr_type, + ldst->addrlo_reg, ldst->addrhi_reg); + tcg_out_helper_load_slots(s, nmov, mov, parm); + next_arg += nmov; + } + + switch (info->out_kind) { + case TCG_CALL_RET_NORMAL: + case TCG_CALL_RET_BY_VEC: + break; + case TCG_CALL_RET_BY_REF: + /* + * The return reference is in the first argument slot. + * We need memory in which to return: re-use the top of stack. + */ + { + int ofs_slot0 = TCG_TARGET_CALL_STACK_OFFSET; + + if (arg_slot_reg_p(0)) { + tcg_out_addi_ptr(s, tcg_target_call_iarg_regs[0], + TCG_REG_CALL_STACK, ofs_slot0); + } else { + tcg_debug_assert(parm->ntmp != 0); + tcg_out_addi_ptr(s, parm->tmp[0], + TCG_REG_CALL_STACK, ofs_slot0); + tcg_out_st(s, TCG_TYPE_PTR, parm->tmp[0], + TCG_REG_CALL_STACK, ofs_slot0); + } + } + break; + default: + g_assert_not_reached(); + } + + tcg_out_helper_load_common_args(s, ldst, parm, info, next_arg); +} + +static void tcg_out_ld_helper_ret(TCGContext *s, const TCGLabelQemuLdst *ldst, + bool load_sign, + const TCGLdstHelperParam *parm) +{ + MemOp mop = get_memop(ldst->oi); + TCGMovExtend mov[2]; + int ofs_slot0; + + switch (ldst->type) { + case TCG_TYPE_I64: + if (TCG_TARGET_REG_BITS == 32) { + break; + } + /* fall through */ + + case TCG_TYPE_I32: + mov[0].dst = ldst->datalo_reg; + mov[0].src = tcg_target_call_oarg_reg(TCG_CALL_RET_NORMAL, 0); + mov[0].dst_type = ldst->type; + mov[0].src_type = TCG_TYPE_REG; + + /* + * If load_sign, then we allowed the helper to perform the + * appropriate sign extension to tcg_target_ulong, and all + * we need now is a plain move. + * + * If they do not, then we expect the relevant extension + * instruction to be no more expensive than a move, and + * we thus save the icache etc by only using one of two + * helper functions. + */ + if (load_sign || !(mop & MO_SIGN)) { + if (TCG_TARGET_REG_BITS == 32 || ldst->type == TCG_TYPE_I32) { + mov[0].src_ext = MO_32; + } else { + mov[0].src_ext = MO_64; + } + } else { + mov[0].src_ext = mop & MO_SSIZE; + } + tcg_out_movext1(s, mov); + return; + + case TCG_TYPE_I128: + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + ofs_slot0 = TCG_TARGET_CALL_STACK_OFFSET; + switch (TCG_TARGET_CALL_RET_I128) { + case TCG_CALL_RET_NORMAL: + break; + case TCG_CALL_RET_BY_VEC: + tcg_out_st(s, TCG_TYPE_V128, + tcg_target_call_oarg_reg(TCG_CALL_RET_BY_VEC, 0), + TCG_REG_CALL_STACK, ofs_slot0); + /* fall through */ + case TCG_CALL_RET_BY_REF: + tcg_out_ld(s, TCG_TYPE_I64, ldst->datalo_reg, + TCG_REG_CALL_STACK, ofs_slot0 + 8 * HOST_BIG_ENDIAN); + tcg_out_ld(s, TCG_TYPE_I64, ldst->datahi_reg, + TCG_REG_CALL_STACK, ofs_slot0 + 8 * !HOST_BIG_ENDIAN); + return; + default: + g_assert_not_reached(); + } + break; + + default: + g_assert_not_reached(); + } + + mov[0].dst = ldst->datalo_reg; + mov[0].src = + tcg_target_call_oarg_reg(TCG_CALL_RET_NORMAL, HOST_BIG_ENDIAN); + mov[0].dst_type = TCG_TYPE_REG; + mov[0].src_type = TCG_TYPE_REG; + mov[0].src_ext = TCG_TARGET_REG_BITS == 32 ? MO_32 : MO_64; + + mov[1].dst = ldst->datahi_reg; + mov[1].src = + tcg_target_call_oarg_reg(TCG_CALL_RET_NORMAL, !HOST_BIG_ENDIAN); + mov[1].dst_type = TCG_TYPE_REG; + mov[1].src_type = TCG_TYPE_REG; + mov[1].src_ext = TCG_TARGET_REG_BITS == 32 ? MO_32 : MO_64; + + tcg_out_movext2(s, mov, mov + 1, parm->ntmp ? parm->tmp[0] : -1); +} + +static void tcg_out_st_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst, + const TCGLdstHelperParam *parm) +{ + const TCGHelperInfo *info; + const TCGCallArgumentLoc *loc; + TCGMovExtend mov[4]; + TCGType data_type; + unsigned next_arg, nmov, n; + MemOp mop = get_memop(ldst->oi); + + switch (mop & MO_SIZE) { + case MO_8: + case MO_16: + case MO_32: + info = &info_helper_st32_mmu; + data_type = TCG_TYPE_I32; + break; + case MO_64: + info = &info_helper_st64_mmu; + data_type = TCG_TYPE_I64; + break; + case MO_128: + info = &info_helper_st128_mmu; + data_type = TCG_TYPE_I128; + break; + default: + g_assert_not_reached(); + } + + /* Defer env argument. */ + next_arg = 1; + nmov = 0; + + /* Handle addr argument. */ + loc = &info->in[next_arg]; + if (TCG_TARGET_REG_BITS == 32 && s->addr_type == TCG_TYPE_I32) { + /* + * 32-bit host with 32-bit guest: zero-extend the guest address + * to 64-bits for the helper by storing the low part. Later, + * after we have processed the register inputs, we will load a + * zero for the high part. + */ + tcg_out_helper_add_mov(mov, loc + HOST_BIG_ENDIAN, + TCG_TYPE_I32, TCG_TYPE_I32, + ldst->addrlo_reg, -1); + next_arg += 2; + nmov += 1; + } else { + n = tcg_out_helper_add_mov(mov, loc, TCG_TYPE_I64, s->addr_type, + ldst->addrlo_reg, ldst->addrhi_reg); + next_arg += n; + nmov += n; + } + + /* Handle data argument. */ + loc = &info->in[next_arg]; + switch (loc->kind) { + case TCG_CALL_ARG_NORMAL: + case TCG_CALL_ARG_EXTEND_U: + case TCG_CALL_ARG_EXTEND_S: + n = tcg_out_helper_add_mov(mov + nmov, loc, data_type, ldst->type, + ldst->datalo_reg, ldst->datahi_reg); + next_arg += n; + nmov += n; + tcg_out_helper_load_slots(s, nmov, mov, parm); + break; + + case TCG_CALL_ARG_BY_REF: + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + tcg_debug_assert(data_type == TCG_TYPE_I128); + tcg_out_st(s, TCG_TYPE_I64, + HOST_BIG_ENDIAN ? ldst->datahi_reg : ldst->datalo_reg, + TCG_REG_CALL_STACK, arg_slot_stk_ofs(loc[0].ref_slot)); + tcg_out_st(s, TCG_TYPE_I64, + HOST_BIG_ENDIAN ? ldst->datalo_reg : ldst->datahi_reg, + TCG_REG_CALL_STACK, arg_slot_stk_ofs(loc[1].ref_slot)); + + tcg_out_helper_load_slots(s, nmov, mov, parm); + + if (arg_slot_reg_p(loc->arg_slot)) { + tcg_out_addi_ptr(s, tcg_target_call_iarg_regs[loc->arg_slot], + TCG_REG_CALL_STACK, + arg_slot_stk_ofs(loc->ref_slot)); + } else { + tcg_debug_assert(parm->ntmp != 0); + tcg_out_addi_ptr(s, parm->tmp[0], TCG_REG_CALL_STACK, + arg_slot_stk_ofs(loc->ref_slot)); + tcg_out_st(s, TCG_TYPE_PTR, parm->tmp[0], + TCG_REG_CALL_STACK, arg_slot_stk_ofs(loc->arg_slot)); + } + next_arg += 2; + break; + + default: + g_assert_not_reached(); + } + + if (TCG_TARGET_REG_BITS == 32 && s->addr_type == TCG_TYPE_I32) { + /* Zero extend the address by loading a zero for the high part. */ + loc = &info->in[1 + !HOST_BIG_ENDIAN]; + tcg_out_helper_load_imm(s, loc->arg_slot, TCG_TYPE_I32, 0, parm); + } + + tcg_out_helper_load_common_args(s, ldst, parm, info, next_arg); +} + +int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) +{ + int i, start_words, num_insns; TCGOp *op; -#ifdef CONFIG_PROFILER - { - int n = 0; - - QTAILQ_FOREACH(op, &s->ops, link) { - n++; - } - qatomic_set(&prof->op_count, prof->op_count + n); - if (n > prof->op_count_max) { - qatomic_set(&prof->op_count_max, n); - } - - n = s->nb_temps; - qatomic_set(&prof->temp_count, prof->temp_count + n); - if (n > prof->temp_count_max) { - qatomic_set(&prof->temp_count_max, n); - } - } -#endif - -#ifdef DEBUG_DISAS if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP) - && qemu_log_in_addr_range(tb->pc))) { + && qemu_log_in_addr_range(pc_start))) { FILE *logfile = qemu_log_trylock(); if (logfile) { fprintf(logfile, "OP:\n"); @@ -4227,7 +6087,6 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) qemu_log_unlock(logfile); } } -#endif #ifdef CONFIG_DEBUG_TCG /* Ensure all labels referenced have been emitted. */ @@ -4236,7 +6095,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) bool error = false; QSIMPLEQ_FOREACH(l, &s->labels, next) { - if (unlikely(!l->present) && l->refs) { + if (unlikely(!l->present) && !QSIMPLEQ_EMPTY(&l->branches)) { qemu_log_mask(CPU_LOG_TB_OP, "$L%d referenced but not present.\n", l->id); error = true; @@ -4246,26 +6105,15 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) } #endif -#ifdef CONFIG_PROFILER - qatomic_set(&prof->opt_time, prof->opt_time - profile_getclock()); -#endif - -#ifdef USE_TCG_OPTIMIZATIONS tcg_optimize(s); -#endif - -#ifdef CONFIG_PROFILER - qatomic_set(&prof->opt_time, prof->opt_time + profile_getclock()); - qatomic_set(&prof->la_time, prof->la_time - profile_getclock()); -#endif reachable_code_pass(s); + liveness_pass_0(s); liveness_pass_1(s); if (s->nb_indirects > 0) { -#ifdef DEBUG_DISAS if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_IND) - && qemu_log_in_addr_range(tb->pc))) { + && qemu_log_in_addr_range(pc_start))) { FILE *logfile = qemu_log_trylock(); if (logfile) { fprintf(logfile, "OP before indirect lowering:\n"); @@ -4274,7 +6122,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) qemu_log_unlock(logfile); } } -#endif + /* Replace indirect temps with direct temps. */ if (liveness_pass_2(s)) { /* If changes were made, re-run liveness. */ @@ -4282,13 +6130,8 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) } } -#ifdef CONFIG_PROFILER - qatomic_set(&prof->la_time, prof->la_time + profile_getclock()); -#endif - -#ifdef DEBUG_DISAS if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_OPT) - && qemu_log_in_addr_range(tb->pc))) { + && qemu_log_in_addr_range(pc_start))) { FILE *logfile = qemu_log_trylock(); if (logfile) { fprintf(logfile, "OP after optimization and liveness analysis:\n"); @@ -4297,7 +6140,12 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) qemu_log_unlock(logfile); } } -#endif + + /* Initialize goto_tb jump offsets. */ + tb->jmp_reset_offset[0] = TB_JMP_OFFSET_INVALID; + tb->jmp_reset_offset[1] = TB_JMP_OFFSET_INVALID; + tb->jmp_insn_offset[0] = TB_JMP_OFFSET_INVALID; + tb->jmp_insn_offset[1] = TB_JMP_OFFSET_INVALID; tcg_reg_alloc_start(s); @@ -4316,14 +6164,16 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) s->pool_labels = NULL; #endif + start_words = s->insn_start_words; + s->gen_insn_data = + tcg_malloc(sizeof(uint64_t) * s->gen_tb->icount * start_words); + + tcg_out_tb_start(s); + num_insns = -1; QTAILQ_FOREACH(op, &s->ops, link) { TCGOpcode opc = op->opc; -#ifdef CONFIG_PROFILER - qatomic_set(&prof->table_op_count[opc], prof->table_op_count[opc] + 1); -#endif - switch (opc) { case INDEX_op_mov_i32: case INDEX_op_mov_i64: @@ -4341,14 +6191,9 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) assert(s->gen_insn_end_off[num_insns] == off); } num_insns++; - for (i = 0; i < TARGET_INSN_START_WORDS; ++i) { - target_ulong a; -#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS - a = deposit64(op->args[i * 2], 32, 32, op->args[i * 2 + 1]); -#else - a = op->args[i]; -#endif - s->gen_insn_data[num_insns][i] = a; + for (i = 0; i < start_words; ++i) { + s->gen_insn_data[num_insns * start_words + i] = + tcg_get_insn_start_param(op, i); } break; case INDEX_op_discard: @@ -4361,6 +6206,12 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) case INDEX_op_call: tcg_reg_alloc_call(s, op); break; + case INDEX_op_exit_tb: + tcg_out_exit_tb(s, op->args[0]); + break; + case INDEX_op_goto_tb: + tcg_out_goto_tb(s, op->args[0]); + break; case INDEX_op_dup2_vec: if (tcg_reg_alloc_dup2(s, op)) { break; @@ -4375,9 +6226,6 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) tcg_reg_alloc_op(s, op); break; } -#ifdef CONFIG_DEBUG_TCG - check_regs(s); -#endif /* Test for (pending) buffer overflow. The assumption is that any one operation beginning below the high water mark cannot overrun the buffer completely. Thus we can test for overflow after @@ -4390,7 +6238,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) return -2; } } - tcg_debug_assert(num_insns >= 0); + tcg_debug_assert(num_insns + 1 == s->gen_tb->icount); s->gen_insn_end_off[num_insns] = tcg_current_code_size(s); /* Generate TB finalization at the end of block */ @@ -4420,77 +6268,6 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) return tcg_current_code_size(s); } -#ifdef CONFIG_PROFILER -void tcg_dump_info(GString *buf) -{ - TCGProfile prof = {}; - const TCGProfile *s; - int64_t tb_count; - int64_t tb_div_count; - int64_t tot; - - tcg_profile_snapshot_counters(&prof); - s = &prof; - tb_count = s->tb_count; - tb_div_count = tb_count ? tb_count : 1; - tot = s->interm_time + s->code_time; - - g_string_append_printf(buf, "JIT cycles %" PRId64 - " (%0.3f s at 2.4 GHz)\n", - tot, tot / 2.4e9); - g_string_append_printf(buf, "translated TBs %" PRId64 - " (aborted=%" PRId64 " %0.1f%%)\n", - tb_count, s->tb_count1 - tb_count, - (double)(s->tb_count1 - s->tb_count) - / (s->tb_count1 ? s->tb_count1 : 1) * 100.0); - g_string_append_printf(buf, "avg ops/TB %0.1f max=%d\n", - (double)s->op_count / tb_div_count, s->op_count_max); - g_string_append_printf(buf, "deleted ops/TB %0.2f\n", - (double)s->del_op_count / tb_div_count); - g_string_append_printf(buf, "avg temps/TB %0.2f max=%d\n", - (double)s->temp_count / tb_div_count, - s->temp_count_max); - g_string_append_printf(buf, "avg host code/TB %0.1f\n", - (double)s->code_out_len / tb_div_count); - g_string_append_printf(buf, "avg search data/TB %0.1f\n", - (double)s->search_out_len / tb_div_count); - - g_string_append_printf(buf, "cycles/op %0.1f\n", - s->op_count ? (double)tot / s->op_count : 0); - g_string_append_printf(buf, "cycles/in byte %0.1f\n", - s->code_in_len ? (double)tot / s->code_in_len : 0); - g_string_append_printf(buf, "cycles/out byte %0.1f\n", - s->code_out_len ? (double)tot / s->code_out_len : 0); - g_string_append_printf(buf, "cycles/search byte %0.1f\n", - s->search_out_len ? - (double)tot / s->search_out_len : 0); - if (tot == 0) { - tot = 1; - } - g_string_append_printf(buf, " gen_interm time %0.1f%%\n", - (double)s->interm_time / tot * 100.0); - g_string_append_printf(buf, " gen_code time %0.1f%%\n", - (double)s->code_time / tot * 100.0); - g_string_append_printf(buf, "optim./code time %0.1f%%\n", - (double)s->opt_time / (s->code_time ? - s->code_time : 1) - * 100.0); - g_string_append_printf(buf, "liveness/code time %0.1f%%\n", - (double)s->la_time / (s->code_time ? - s->code_time : 1) * 100.0); - g_string_append_printf(buf, "cpu_restore count %" PRId64 "\n", - s->restore_count); - g_string_append_printf(buf, " avg cycles %0.1f\n", - s->restore_count ? - (double)s->restore_time / s->restore_count : 0); -} -#else -void tcg_dump_info(GString *buf) -{ - g_string_append_printf(buf, "[TCG profiler not compiled]\n"); -} -#endif - #ifdef ELF_HOST_MACHINE /* In order to use this feature, the backend needs to do three things: @@ -4729,7 +6506,8 @@ static void tcg_register_jit_int(const void *buf_ptr, size_t buf_size, /* Enable this block to be able to debug the ELF image file creation. One can use readelf, objdump, or other inspection utilities. */ { - FILE *f = fopen("/tmp/qemu.jit", "w+b"); + g_autofree char *jit = g_strdup_printf("%s/qemu.jit", g_get_tmp_dir()); + FILE *f = fopen(jit, "w+b"); if (f) { if (fwrite(img, img_size, 1, f) != img_size) { /* Avoid stupid unused return value warning for fwrite. */ diff --git a/tcg/tci.c b/tcg/tci.c index bdfac83492..39adcb7d82 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -18,11 +18,8 @@ */ #include "qemu/osdep.h" -#include "tcg/tcg.h" /* MAX_OPC_PARAM_IARGS */ -#include "exec/cpu_ldst.h" -#include "tcg/tcg-op.h" +#include "tcg/tcg.h" #include "tcg/tcg-ldst.h" -#include "qemu/compiler.h" #include @@ -109,7 +106,7 @@ static void tci_args_rrm(uint32_t insn, TCGReg *r0, { *r0 = extract32(insn, 8, 4); *r1 = extract32(insn, 12, 4); - *m2 = extract32(insn, 20, 12); + *m2 = extract32(insn, 16, 16); } static void tci_args_rrr(uint32_t insn, TCGReg *r0, TCGReg *r1, TCGReg *r2) @@ -144,15 +141,6 @@ static void tci_args_rrrc(uint32_t insn, *c3 = extract32(insn, 20, 4); } -static void tci_args_rrrm(uint32_t insn, - TCGReg *r0, TCGReg *r1, TCGReg *r2, MemOpIdx *m3) -{ - *r0 = extract32(insn, 8, 4); - *r1 = extract32(insn, 12, 4); - *r2 = extract32(insn, 16, 4); - *m3 = extract32(insn, 20, 12); -} - static void tci_args_rrrbb(uint32_t insn, TCGReg *r0, TCGReg *r1, TCGReg *r2, uint8_t *i3, uint8_t *i4) { @@ -240,6 +228,12 @@ static bool tci_compare32(uint32_t u0, uint32_t u1, TCGCond condition) case TCG_COND_GTU: result = (u0 > u1); break; + case TCG_COND_TSTEQ: + result = (u0 & u1) == 0; + break; + case TCG_COND_TSTNE: + result = (u0 & u1) != 0; + break; default: g_assert_not_reached(); } @@ -282,168 +276,66 @@ static bool tci_compare64(uint64_t u0, uint64_t u1, TCGCond condition) case TCG_COND_GTU: result = (u0 > u1); break; + case TCG_COND_TSTEQ: + result = (u0 & u1) == 0; + break; + case TCG_COND_TSTNE: + result = (u0 & u1) != 0; + break; default: g_assert_not_reached(); } return result; } -static uint64_t tci_qemu_ld(CPUArchState *env, target_ulong taddr, +static uint64_t tci_qemu_ld(CPUArchState *env, uint64_t taddr, MemOpIdx oi, const void *tb_ptr) { MemOp mop = get_memop(oi); uintptr_t ra = (uintptr_t)tb_ptr; -#ifdef CONFIG_SOFTMMU - switch (mop & (MO_BSWAP | MO_SSIZE)) { + switch (mop & MO_SSIZE) { case MO_UB: - return helper_ret_ldub_mmu(env, taddr, oi, ra); + return helper_ldub_mmu(env, taddr, oi, ra); case MO_SB: - return helper_ret_ldsb_mmu(env, taddr, oi, ra); - case MO_LEUW: - return helper_le_lduw_mmu(env, taddr, oi, ra); - case MO_LESW: - return helper_le_ldsw_mmu(env, taddr, oi, ra); - case MO_LEUL: - return helper_le_ldul_mmu(env, taddr, oi, ra); - case MO_LESL: - return helper_le_ldsl_mmu(env, taddr, oi, ra); - case MO_LEUQ: - return helper_le_ldq_mmu(env, taddr, oi, ra); - case MO_BEUW: - return helper_be_lduw_mmu(env, taddr, oi, ra); - case MO_BESW: - return helper_be_ldsw_mmu(env, taddr, oi, ra); - case MO_BEUL: - return helper_be_ldul_mmu(env, taddr, oi, ra); - case MO_BESL: - return helper_be_ldsl_mmu(env, taddr, oi, ra); - case MO_BEUQ: - return helper_be_ldq_mmu(env, taddr, oi, ra); + return helper_ldsb_mmu(env, taddr, oi, ra); + case MO_UW: + return helper_lduw_mmu(env, taddr, oi, ra); + case MO_SW: + return helper_ldsw_mmu(env, taddr, oi, ra); + case MO_UL: + return helper_ldul_mmu(env, taddr, oi, ra); + case MO_SL: + return helper_ldsl_mmu(env, taddr, oi, ra); + case MO_UQ: + return helper_ldq_mmu(env, taddr, oi, ra); default: g_assert_not_reached(); } -#else - void *haddr = g2h(env_cpu(env), taddr); - unsigned a_mask = (1u << get_alignment_bits(mop)) - 1; - uint64_t ret; - - set_helper_retaddr(ra); - if (taddr & a_mask) { - helper_unaligned_ld(env, taddr); - } - switch (mop & (MO_BSWAP | MO_SSIZE)) { - case MO_UB: - ret = ldub_p(haddr); - break; - case MO_SB: - ret = ldsb_p(haddr); - break; - case MO_LEUW: - ret = lduw_le_p(haddr); - break; - case MO_LESW: - ret = ldsw_le_p(haddr); - break; - case MO_LEUL: - ret = (uint32_t)ldl_le_p(haddr); - break; - case MO_LESL: - ret = (int32_t)ldl_le_p(haddr); - break; - case MO_LEUQ: - ret = ldq_le_p(haddr); - break; - case MO_BEUW: - ret = lduw_be_p(haddr); - break; - case MO_BESW: - ret = ldsw_be_p(haddr); - break; - case MO_BEUL: - ret = (uint32_t)ldl_be_p(haddr); - break; - case MO_BESL: - ret = (int32_t)ldl_be_p(haddr); - break; - case MO_BEUQ: - ret = ldq_be_p(haddr); - break; - default: - g_assert_not_reached(); - } - clear_helper_retaddr(); - return ret; -#endif } -static void tci_qemu_st(CPUArchState *env, target_ulong taddr, uint64_t val, +static void tci_qemu_st(CPUArchState *env, uint64_t taddr, uint64_t val, MemOpIdx oi, const void *tb_ptr) { MemOp mop = get_memop(oi); uintptr_t ra = (uintptr_t)tb_ptr; -#ifdef CONFIG_SOFTMMU - switch (mop & (MO_BSWAP | MO_SIZE)) { + switch (mop & MO_SIZE) { case MO_UB: - helper_ret_stb_mmu(env, taddr, val, oi, ra); + helper_stb_mmu(env, taddr, val, oi, ra); break; - case MO_LEUW: - helper_le_stw_mmu(env, taddr, val, oi, ra); + case MO_UW: + helper_stw_mmu(env, taddr, val, oi, ra); break; - case MO_LEUL: - helper_le_stl_mmu(env, taddr, val, oi, ra); + case MO_UL: + helper_stl_mmu(env, taddr, val, oi, ra); break; - case MO_LEUQ: - helper_le_stq_mmu(env, taddr, val, oi, ra); - break; - case MO_BEUW: - helper_be_stw_mmu(env, taddr, val, oi, ra); - break; - case MO_BEUL: - helper_be_stl_mmu(env, taddr, val, oi, ra); - break; - case MO_BEUQ: - helper_be_stq_mmu(env, taddr, val, oi, ra); + case MO_UQ: + helper_stq_mmu(env, taddr, val, oi, ra); break; default: g_assert_not_reached(); } -#else - void *haddr = g2h(env_cpu(env), taddr); - unsigned a_mask = (1u << get_alignment_bits(mop)) - 1; - - set_helper_retaddr(ra); - if (taddr & a_mask) { - helper_unaligned_st(env, taddr); - } - switch (mop & (MO_BSWAP | MO_SIZE)) { - case MO_UB: - stb_p(haddr, val); - break; - case MO_LEUW: - stw_le_p(haddr, val); - break; - case MO_LEUL: - stl_le_p(haddr, val); - break; - case MO_LEUQ: - stq_le_p(haddr, val); - break; - case MO_BEUW: - stw_be_p(haddr, val); - break; - case MO_BEUL: - stl_be_p(haddr, val); - break; - case MO_BEUQ: - stq_be_p(haddr, val); - break; - default: - g_assert_not_reached(); - } - clear_helper_retaddr(); -#endif } #if TCG_TARGET_REG_BITS == 64 @@ -471,12 +363,9 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tcg_target_ulong regs[TCG_TARGET_NB_REGS]; uint64_t stack[(TCG_STATIC_CALL_ARGS_SIZE + TCG_STATIC_FRAME_SIZE) / sizeof(uint64_t)]; - void *call_slots[TCG_STATIC_CALL_ARGS_SIZE / sizeof(uint64_t)]; regs[TCG_AREG0] = (tcg_target_ulong)env; regs[TCG_REG_CALL_STACK] = (uintptr_t)stack; - /* Other call_slots entries initialized at first use (see below). */ - call_slots[0] = NULL; tci_assert(tb_ptr); for (;;) { @@ -485,10 +374,9 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, TCGReg r0, r1, r2, r3, r4, r5; tcg_target_ulong t1; TCGCond condition; - target_ulong taddr; uint8_t pos, len; uint32_t tmp32; - uint64_t tmp64; + uint64_t tmp64, taddr; uint64_t T1, T2; MemOpIdx oi; int32_t ofs; @@ -499,49 +387,53 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, switch (opc) { case INDEX_op_call: - /* - * Set up the ffi_avalue array once, delayed until now - * because many TB's do not make any calls. In tcg_gen_callN, - * we arranged for every real argument to be "left-aligned" - * in each 64-bit slot. - */ - if (unlikely(call_slots[0] == NULL)) { - for (int i = 0; i < ARRAY_SIZE(call_slots); ++i) { - call_slots[i] = &stack[i]; - } - } - - tci_args_nl(insn, tb_ptr, &len, &ptr); - - /* Helper functions may need to access the "return address" */ - tci_tb_ptr = (uintptr_t)tb_ptr; - { - void **pptr = ptr; - ffi_call(pptr[1], pptr[0], stack, call_slots); + void *call_slots[MAX_CALL_IARGS]; + ffi_cif *cif; + void *func; + unsigned i, s, n; + + tci_args_nl(insn, tb_ptr, &len, &ptr); + func = ((void **)ptr)[0]; + cif = ((void **)ptr)[1]; + + n = cif->nargs; + for (i = s = 0; i < n; ++i) { + ffi_type *t = cif->arg_types[i]; + call_slots[i] = &stack[s]; + s += DIV_ROUND_UP(t->size, 8); + } + + /* Helper functions may need to access the "return address" */ + tci_tb_ptr = (uintptr_t)tb_ptr; + ffi_call(cif, func, stack, call_slots); } - /* Any result winds up "left-aligned" in the stack[0] slot. */ switch (len) { case 0: /* void */ break; case 1: /* uint32_t */ /* + * The result winds up "left-aligned" in the stack[0] slot. * Note that libffi has an odd special case in that it will * always widen an integral result to ffi_arg. */ - if (sizeof(ffi_arg) == 4) { - regs[TCG_REG_R0] = *(uint32_t *)stack; - break; - } - /* fall through */ - case 2: /* uint64_t */ - if (TCG_TARGET_REG_BITS == 32) { - tci_write_reg64(regs, TCG_REG_R1, TCG_REG_R0, stack[0]); + if (sizeof(ffi_arg) == 8) { + regs[TCG_REG_R0] = (uint32_t)stack[0]; } else { - regs[TCG_REG_R0] = stack[0]; + regs[TCG_REG_R0] = *(uint32_t *)stack; } break; + case 2: /* uint64_t */ + /* + * For TCG_TARGET_REG_BITS == 32, the register pair + * must stay in host memory order. + */ + memcpy(®s[TCG_REG_R0], stack, 8); + break; + case 3: /* Int128 */ + memcpy(®s[TCG_REG_R0], stack, 16); + break; default: g_assert_not_reached(); } @@ -853,12 +745,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, regs[r0] = ~regs[r1]; break; #endif -#if TCG_TARGET_HAS_neg_i32 || TCG_TARGET_HAS_neg_i64 CASE_32_64(neg) tci_args_rr(insn, &r0, &r1); regs[r0] = -regs[r1]; break; -#endif #if TCG_TARGET_REG_BITS == 64 /* Load/store operations (64 bit). */ @@ -1031,30 +921,43 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tb_ptr = ptr; break; - case INDEX_op_qemu_ld_i32: - if (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS) { - tci_args_rrm(insn, &r0, &r1, &oi); - taddr = regs[r1]; - } else { - tci_args_rrrm(insn, &r0, &r1, &r2, &oi); - taddr = tci_uint64(regs[r2], regs[r1]); - } - tmp32 = tci_qemu_ld(env, taddr, oi, tb_ptr); - regs[r0] = tmp32; - break; - - case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_ld_a32_i32: + tci_args_rrm(insn, &r0, &r1, &oi); + taddr = (uint32_t)regs[r1]; + goto do_ld_i32; + case INDEX_op_qemu_ld_a64_i32: + if (TCG_TARGET_REG_BITS == 64) { + tci_args_rrm(insn, &r0, &r1, &oi); + taddr = regs[r1]; + } else { + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); + taddr = tci_uint64(regs[r2], regs[r1]); + oi = regs[r3]; + } + do_ld_i32: + regs[r0] = tci_qemu_ld(env, taddr, oi, tb_ptr); + break; + + case INDEX_op_qemu_ld_a32_i64: + if (TCG_TARGET_REG_BITS == 64) { + tci_args_rrm(insn, &r0, &r1, &oi); + taddr = (uint32_t)regs[r1]; + } else { + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); + taddr = (uint32_t)regs[r2]; + oi = regs[r3]; + } + goto do_ld_i64; + case INDEX_op_qemu_ld_a64_i64: if (TCG_TARGET_REG_BITS == 64) { tci_args_rrm(insn, &r0, &r1, &oi); taddr = regs[r1]; - } else if (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS) { - tci_args_rrrm(insn, &r0, &r1, &r2, &oi); - taddr = regs[r2]; } else { tci_args_rrrrr(insn, &r0, &r1, &r2, &r3, &r4); taddr = tci_uint64(regs[r3], regs[r2]); oi = regs[r4]; } + do_ld_i64: tmp64 = tci_qemu_ld(env, taddr, oi, tb_ptr); if (TCG_TARGET_REG_BITS == 32) { tci_write_reg64(regs, r1, r0, tmp64); @@ -1063,34 +966,47 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, } break; - case INDEX_op_qemu_st_i32: - if (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS) { - tci_args_rrm(insn, &r0, &r1, &oi); - taddr = regs[r1]; - } else { - tci_args_rrrm(insn, &r0, &r1, &r2, &oi); - taddr = tci_uint64(regs[r2], regs[r1]); - } - tmp32 = regs[r0]; - tci_qemu_st(env, taddr, tmp32, oi, tb_ptr); - break; - - case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_st_a32_i32: + tci_args_rrm(insn, &r0, &r1, &oi); + taddr = (uint32_t)regs[r1]; + goto do_st_i32; + case INDEX_op_qemu_st_a64_i32: if (TCG_TARGET_REG_BITS == 64) { tci_args_rrm(insn, &r0, &r1, &oi); taddr = regs[r1]; - tmp64 = regs[r0]; } else { - if (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS) { - tci_args_rrrm(insn, &r0, &r1, &r2, &oi); - taddr = regs[r2]; - } else { - tci_args_rrrrr(insn, &r0, &r1, &r2, &r3, &r4); - taddr = tci_uint64(regs[r3], regs[r2]); - oi = regs[r4]; - } - tmp64 = tci_uint64(regs[r1], regs[r0]); + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); + taddr = tci_uint64(regs[r2], regs[r1]); + oi = regs[r3]; } + do_st_i32: + tci_qemu_st(env, taddr, regs[r0], oi, tb_ptr); + break; + + case INDEX_op_qemu_st_a32_i64: + if (TCG_TARGET_REG_BITS == 64) { + tci_args_rrm(insn, &r0, &r1, &oi); + tmp64 = regs[r0]; + taddr = (uint32_t)regs[r1]; + } else { + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); + tmp64 = tci_uint64(regs[r1], regs[r0]); + taddr = (uint32_t)regs[r2]; + oi = regs[r3]; + } + goto do_st_i64; + case INDEX_op_qemu_st_a64_i64: + if (TCG_TARGET_REG_BITS == 64) { + tci_args_rrm(insn, &r0, &r1, &oi); + tmp64 = regs[r0]; + taddr = regs[r1]; + } else { + tci_args_rrrrr(insn, &r0, &r1, &r2, &r3, &r4); + tmp64 = tci_uint64(regs[r1], regs[r0]); + taddr = tci_uint64(regs[r3], regs[r2]); + oi = regs[r4]; + } + do_st_i64: tci_qemu_st(env, taddr, tmp64, oi, tb_ptr); break; @@ -1137,6 +1053,8 @@ static const char *str_c(TCGCond c) [TCG_COND_GEU] = "geu", [TCG_COND_LEU] = "leu", [TCG_COND_GTU] = "gtu", + [TCG_COND_TSTEQ] = "tsteq", + [TCG_COND_TSTNE] = "tstne", }; assert((unsigned)c < ARRAY_SIZE(cond)); @@ -1360,15 +1278,21 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) str_r(r3), str_r(r4), str_r(r5)); break; - case INDEX_op_qemu_ld_i64: - case INDEX_op_qemu_st_i64: - len = DIV_ROUND_UP(64, TCG_TARGET_REG_BITS); + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_st_a32_i32: + len = 1 + 1; + goto do_qemu_ldst; + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_st_a32_i64: + case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_st_a64_i32: + len = 1 + DIV_ROUND_UP(64, TCG_TARGET_REG_BITS); + goto do_qemu_ldst; + case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_st_a64_i64: + len = 2 * DIV_ROUND_UP(64, TCG_TARGET_REG_BITS); goto do_qemu_ldst; - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_st_i32: - len = 1; do_qemu_ldst: - len += DIV_ROUND_UP(TARGET_LONG_BITS, TCG_TARGET_REG_BITS); switch (len) { case 2: tci_args_rrm(insn, &r0, &r1, &oi); @@ -1376,9 +1300,10 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) op_name, str_r(r0), str_r(r1), oi); break; case 3: - tci_args_rrrm(insn, &r0, &r1, &r2, &oi); - info->fprintf_func(info->stream, "%-12s %s, %s, %s, %x", - op_name, str_r(r0), str_r(r1), str_r(r2), oi); + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); + info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s", + op_name, str_r(r0), str_r(r1), + str_r(r2), str_r(r3)); break; case 4: tci_args_rrrrr(insn, &r0, &r1, &r2, &r3, &r4); diff --git a/tcg/tci/README b/tcg/tci/README index f72a40a395..4a8b5b5401 100644 --- a/tcg/tci/README +++ b/tcg/tci/README @@ -49,7 +49,7 @@ The only difference from running QEMU with TCI to running without TCI should be speed. Especially during development of TCI, it was very useful to compare runs with and without TCI. Create /tmp/qemu.log by - qemu-system-i386 -d in_asm,op_opt,cpu -D /tmp/qemu.log -singlestep + qemu-system-i386 -d in_asm,op_opt,cpu -D /tmp/qemu.log -accel tcg,one-insn-per-tb=on once with interpreter and once without interpreter and compare the resulting qemu.log files. This is also useful to see the effects of additional diff --git a/tcg/tci/tcg-target-reg-bits.h b/tcg/tci/tcg-target-reg-bits.h new file mode 100644 index 0000000000..dcb1a203f8 --- /dev/null +++ b/tcg/tci/tcg-target-reg-bits.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific register size + * Copyright (c) 2009, 2011 Stefan Weil + */ + +#ifndef TCG_TARGET_REG_BITS_H +#define TCG_TARGET_REG_BITS_H + +#if UINTPTR_MAX == UINT32_MAX +# define TCG_TARGET_REG_BITS 32 +#elif UINTPTR_MAX == UINT64_MAX +# define TCG_TARGET_REG_BITS 64 +#else +# error Unknown pointer size for tci target +#endif + +#endif diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 98337c567a..c740864b96 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -156,22 +156,22 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_setcond2_i32: return C_O1_I4(r, r, r, r, r); - case INDEX_op_qemu_ld_i32: - return (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS - ? C_O1_I1(r, r) - : C_O1_I2(r, r, r)); - case INDEX_op_qemu_ld_i64: - return (TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) - : TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? C_O2_I1(r, r, r) - : C_O2_I2(r, r, r, r)); - case INDEX_op_qemu_st_i32: - return (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS - ? C_O0_I2(r, r) - : C_O0_I3(r, r, r)); - case INDEX_op_qemu_st_i64: - return (TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) - : TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? C_O0_I3(r, r, r) - : C_O0_I4(r, r, r, r)); + case INDEX_op_qemu_ld_a32_i32: + return C_O1_I1(r, r); + case INDEX_op_qemu_ld_a64_i32: + return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O1_I2(r, r, r); + case INDEX_op_qemu_ld_a32_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I1(r, r, r); + case INDEX_op_qemu_ld_a64_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I2(r, r, r, r); + case INDEX_op_qemu_st_a32_i32: + return C_O0_I2(r, r); + case INDEX_op_qemu_st_a64_i32: + return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I3(r, r, r); + case INDEX_op_qemu_st_a32_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I3(r, r, r); + case INDEX_op_qemu_st_a64_i64: + return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I4(r, r, r, r); default: g_assert_not_reached(); @@ -179,8 +179,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) } static const int tcg_target_reg_alloc_order[] = { - TCG_REG_R2, - TCG_REG_R3, TCG_REG_R4, TCG_REG_R5, TCG_REG_R6, @@ -193,23 +191,22 @@ static const int tcg_target_reg_alloc_order[] = { TCG_REG_R13, TCG_REG_R14, TCG_REG_R15, + /* Either 2 or 4 of these are call clobbered, so use them last. */ + TCG_REG_R3, + TCG_REG_R2, TCG_REG_R1, TCG_REG_R0, }; -#if MAX_OPC_PARAM_IARGS != 7 -# error Fix needed, number of supported input arguments changed! -#endif - /* No call arguments via registers. All will be stored on the "stack". */ static const int tcg_target_call_iarg_regs[] = { }; -static const int tcg_target_call_oarg_regs[] = { - TCG_REG_R0, -#if TCG_TARGET_REG_BITS == 32 - TCG_REG_R1 -#endif -}; +static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) +{ + tcg_debug_assert(kind == TCG_CALL_RET_NORMAL); + tcg_debug_assert(slot >= 0 && slot < 128 / TCG_TARGET_REG_BITS); + return TCG_REG_R0 + slot; +} #ifdef CONFIG_DEBUG_TCG static const char *const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { @@ -247,7 +244,7 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, return false; } -static void stack_bounds_check(TCGReg base, target_long offset) +static void stack_bounds_check(TCGReg base, intptr_t offset) { if (base == TCG_REG_CALL_STACK) { tcg_debug_assert(offset >= 0); @@ -335,11 +332,11 @@ static void tcg_out_op_rrm(TCGContext *s, TCGOpcode op, { tcg_insn_unit insn = 0; - tcg_debug_assert(m2 == extract32(m2, 0, 12)); + tcg_debug_assert(m2 == extract32(m2, 0, 16)); insn = deposit32(insn, 0, 8, op); insn = deposit32(insn, 8, 4, r0); insn = deposit32(insn, 12, 4, r1); - insn = deposit32(insn, 20, 12, m2); + insn = deposit32(insn, 16, 16, m2); tcg_out32(s, insn); } @@ -396,20 +393,6 @@ static void tcg_out_op_rrrc(TCGContext *s, TCGOpcode op, tcg_out32(s, insn); } -static void tcg_out_op_rrrm(TCGContext *s, TCGOpcode op, - TCGReg r0, TCGReg r1, TCGReg r2, TCGArg m3) -{ - tcg_insn_unit insn = 0; - - tcg_debug_assert(m3 == extract32(m3, 0, 12)); - insn = deposit32(insn, 0, 8, op); - insn = deposit32(insn, 8, 4, r0); - insn = deposit32(insn, 12, 4, r1); - insn = deposit32(insn, 16, 4, r2); - insn = deposit32(insn, 20, 12, m3); - tcg_out32(s, insn); -} - static void tcg_out_op_rrrbb(TCGContext *s, TCGOpcode op, TCGReg r0, TCGReg r1, TCGReg r2, uint8_t b3, uint8_t b4) { @@ -561,19 +544,120 @@ static void tcg_out_movi(TCGContext *s, TCGType type, } } -static void tcg_out_call(TCGContext *s, const tcg_insn_unit *func, - ffi_cif *cif) +static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) { + switch (type) { + case TCG_TYPE_I32: + tcg_debug_assert(TCG_TARGET_HAS_ext8s_i32); + tcg_out_op_rr(s, INDEX_op_ext8s_i32, rd, rs); + break; +#if TCG_TARGET_REG_BITS == 64 + case TCG_TYPE_I64: + tcg_debug_assert(TCG_TARGET_HAS_ext8s_i64); + tcg_out_op_rr(s, INDEX_op_ext8s_i64, rd, rs); + break; +#endif + default: + g_assert_not_reached(); + } +} + +static void tcg_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rs) +{ + if (TCG_TARGET_REG_BITS == 64) { + tcg_debug_assert(TCG_TARGET_HAS_ext8u_i64); + tcg_out_op_rr(s, INDEX_op_ext8u_i64, rd, rs); + } else { + tcg_debug_assert(TCG_TARGET_HAS_ext8u_i32); + tcg_out_op_rr(s, INDEX_op_ext8u_i32, rd, rs); + } +} + +static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) +{ + switch (type) { + case TCG_TYPE_I32: + tcg_debug_assert(TCG_TARGET_HAS_ext16s_i32); + tcg_out_op_rr(s, INDEX_op_ext16s_i32, rd, rs); + break; +#if TCG_TARGET_REG_BITS == 64 + case TCG_TYPE_I64: + tcg_debug_assert(TCG_TARGET_HAS_ext16s_i64); + tcg_out_op_rr(s, INDEX_op_ext16s_i64, rd, rs); + break; +#endif + default: + g_assert_not_reached(); + } +} + +static void tcg_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rs) +{ + if (TCG_TARGET_REG_BITS == 64) { + tcg_debug_assert(TCG_TARGET_HAS_ext16u_i64); + tcg_out_op_rr(s, INDEX_op_ext16u_i64, rd, rs); + } else { + tcg_debug_assert(TCG_TARGET_HAS_ext16u_i32); + tcg_out_op_rr(s, INDEX_op_ext16u_i32, rd, rs); + } +} + +static void tcg_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + tcg_debug_assert(TCG_TARGET_HAS_ext32s_i64); + tcg_out_op_rr(s, INDEX_op_ext32s_i64, rd, rs); +} + +static void tcg_out_ext32u(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + tcg_debug_assert(TCG_TARGET_HAS_ext32u_i64); + tcg_out_op_rr(s, INDEX_op_ext32u_i64, rd, rs); +} + +static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_ext32s(s, rd, rs); +} + +static void tcg_out_extu_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_out_ext32u(s, rd, rs); +} + +static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rs) +{ + tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + tcg_out_mov(s, TCG_TYPE_I32, rd, rs); +} + +static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2) +{ + return false; +} + +static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs, + tcg_target_long imm) +{ + /* This function is only used for passing structs by reference. */ + g_assert_not_reached(); +} + +static void tcg_out_call(TCGContext *s, const tcg_insn_unit *func, + const TCGHelperInfo *info) +{ + ffi_cif *cif = info->cif; tcg_insn_unit insn = 0; uint8_t which; if (cif->rtype == &ffi_type_void) { which = 0; - } else if (cif->rtype->size == 4) { - which = 1; } else { - tcg_debug_assert(cif->rtype->size == 8); - which = 2; + tcg_debug_assert(cif->rtype->size == 4 || + cif->rtype->size == 8 || + cif->rtype->size == 16); + which = ctz32(cif->rtype->size) - 1; } new_pool_l2(s, 20, s->code_ptr, 0, (uintptr_t)func, (uintptr_t)cif); insn = deposit32(insn, 0, 8, INDEX_op_call); @@ -593,6 +677,24 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *func, # define CASE_64(x) #endif +static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg) +{ + tcg_out_op_p(s, INDEX_op_exit_tb, (void *)arg); +} + +static void tcg_out_goto_tb(TCGContext *s, int which) +{ + /* indirect jump method. */ + tcg_out_op_p(s, INDEX_op_goto_tb, (void *)get_jmp_target_addr(s, which)); + set_jmp_reset_offset(s, which); +} + +void tb_target_set_jmp_target(const TranslationBlock *tb, int n, + uintptr_t jmp_rx, uintptr_t jmp_rw) +{ + /* Always indirect, nothing to do */ +} + static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -600,17 +702,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGOpcode exts; switch (opc) { - case INDEX_op_exit_tb: - tcg_out_op_p(s, opc, (void *)args[0]); - break; - - case INDEX_op_goto_tb: - tcg_debug_assert(s->tb_jmp_insn_offset == 0); - /* indirect jump method. */ - tcg_out_op_p(s, opc, s->tb_jmp_target_addr + args[0]); - set_jmp_reset_offset(s, args[0]); - break; - case INDEX_op_goto_ptr: tcg_out_op_r(s, opc, args[0]); break; @@ -704,14 +795,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, CASE_32_64(neg) /* Optional (TCG_TARGET_HAS_neg_*). */ CASE_32_64(not) /* Optional (TCG_TARGET_HAS_not_*). */ - CASE_32_64(ext8s) /* Optional (TCG_TARGET_HAS_ext8s_*). */ - CASE_32_64(ext8u) /* Optional (TCG_TARGET_HAS_ext8u_*). */ - CASE_32_64(ext16s) /* Optional (TCG_TARGET_HAS_ext16s_*). */ - CASE_32_64(ext16u) /* Optional (TCG_TARGET_HAS_ext16u_*). */ - CASE_64(ext32s) /* Optional (TCG_TARGET_HAS_ext32s_i64). */ - CASE_64(ext32u) /* Optional (TCG_TARGET_HAS_ext32u_i64). */ - CASE_64(ext_i32) - CASE_64(extu_i32) CASE_32_64(ctpop) /* Optional (TCG_TARGET_HAS_ctpop_*). */ case INDEX_op_bswap32_i32: /* Optional (TCG_TARGET_HAS_bswap32_i32). */ case INDEX_op_bswap64_i64: /* Optional (TCG_TARGET_HAS_bswap64_i64). */ @@ -753,21 +836,25 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_op_rrrr(s, opc, args[0], args[1], args[2], args[3]); break; - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_st_i32: - if (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS) { - tcg_out_op_rrm(s, opc, args[0], args[1], args[2]); - } else { - tcg_out_op_rrrm(s, opc, args[0], args[1], args[2], args[3]); - } + case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_st_a32_i32: + tcg_out_op_rrm(s, opc, args[0], args[1], args[2]); break; - - case INDEX_op_qemu_ld_i64: - case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_ld_a64_i32: + case INDEX_op_qemu_st_a64_i32: + case INDEX_op_qemu_ld_a32_i64: + case INDEX_op_qemu_st_a32_i64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_op_rrm(s, opc, args[0], args[1], args[2]); + } else { + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, args[3]); + tcg_out_op_rrrr(s, opc, args[0], args[1], args[2], TCG_REG_TMP); + } + break; + case INDEX_op_qemu_ld_a64_i64: + case INDEX_op_qemu_st_a64_i64: if (TCG_TARGET_REG_BITS == 64) { tcg_out_op_rrm(s, opc, args[0], args[1], args[2]); - } else if (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS) { - tcg_out_op_rrrm(s, opc, args[0], args[1], args[2], args[3]); } else { tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, args[4]); tcg_out_op_rrrrr(s, opc, args[0], args[1], @@ -782,8 +869,23 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ + case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ + case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ + case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ + case INDEX_op_ext8s_i64: + case INDEX_op_ext8u_i32: + case INDEX_op_ext8u_i64: + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + case INDEX_op_ext16u_i32: + case INDEX_op_ext16u_i64: + case INDEX_op_ext32s_i64: + case INDEX_op_ext32u_i64: + case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: default: - tcg_abort(); + g_assert_not_reached(); } } @@ -811,7 +913,8 @@ static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, } /* Test if a constant matches the constraint. */ -static bool tcg_target_const_match(int64_t val, TCGType type, int ct) +static bool tcg_target_const_match(int64_t val, int ct, + TCGType type, TCGCond cond, int vece) { return ct & TCG_CT_CONST; } @@ -823,13 +926,6 @@ static void tcg_out_nop_fill(tcg_insn_unit *p, int count) static void tcg_target_init(TCGContext *s) { -#if defined(CONFIG_DEBUG_TCG_INTERPRETER) - const char *envval = getenv("DEBUG_TCG"); - if (envval) { - qemu_set_log(strtol(envval, NULL, 0)); - } -#endif - /* The current code uses uint8_t for tcg operations. */ tcg_debug_assert(tcg_op_defs_max <= UINT8_MAX); @@ -840,11 +936,11 @@ static void tcg_target_init(TCGContext *s) /* * The interpreter "registers" are in the local stack frame and * cannot be clobbered by the called helper functions. However, - * the interpreter assumes a 64-bit return value and assigns to + * the interpreter assumes a 128-bit return value and assigns to * the return value registers. */ tcg_target_call_clobber_regs = - MAKE_64BIT_MASK(TCG_REG_R0, 64 / TCG_TARGET_REG_BITS); + MAKE_64BIT_MASK(TCG_REG_R0, 128 / TCG_TARGET_REG_BITS); s->reserved_regs = 0; tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP); @@ -859,3 +955,13 @@ static void tcg_target_init(TCGContext *s) static inline void tcg_target_qemu_prologue(TCGContext *s) { } + +static void tcg_out_tb_start(TCGContext *s) +{ + /* nothing to do */ +} + +bool tcg_target_has_memory_bswap(MemOp memop) +{ + return true; +} diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index 033e613f24..a076f401d2 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -42,22 +42,8 @@ #define TCG_TARGET_INTERPRETER 1 #define TCG_TARGET_INSN_UNIT_SIZE 4 -#define TCG_TARGET_TLB_DISPLACEMENT_BITS 32 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) -#if UINTPTR_MAX == UINT32_MAX -# define TCG_TARGET_REG_BITS 32 -#elif UINTPTR_MAX == UINT64_MAX -# define TCG_TARGET_REG_BITS 64 -#else -# error Unknown pointer size for tci target -#endif - -#ifdef CONFIG_DEBUG_TCG -/* Enable debug output. */ -#define CONFIG_DEBUG_TCG_INTERPRETER -#endif - /* Optional instructions. */ #define TCG_TARGET_HAS_bswap16_i32 1 @@ -79,20 +65,17 @@ #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_movcond_i32 1 +#define TCG_TARGET_HAS_negsetcond_i32 0 #define TCG_TARGET_HAS_muls2_i32 1 #define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_direct_jump 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_extrl_i64_i32 0 -#define TCG_TARGET_HAS_extrh_i64_i32 0 +#define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 @@ -115,11 +98,10 @@ #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 1 -#define TCG_TARGET_HAS_neg_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_movcond_i64 1 +#define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_muls2_i64 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 @@ -133,6 +115,10 @@ #define TCG_TARGET_HAS_mulu2_i32 1 #endif /* TCG_TARGET_REG_BITS == 64 */ +#define TCG_TARGET_HAS_qemu_ldst_i128 0 + +#define TCG_TARGET_HAS_tst 1 + /* Number of registers available. */ #define TCG_TARGET_NB_REGS 16 @@ -163,6 +149,16 @@ typedef enum { /* Used for function call generation. */ #define TCG_TARGET_CALL_STACK_OFFSET 0 #define TCG_TARGET_STACK_ALIGN 8 +#if TCG_TARGET_REG_BITS == 32 +# define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_EVEN +# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_EVEN +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN +#else +# define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL +# define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL +# define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL +#endif +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL #define HAVE_TCG_QEMU_TB_EXEC #define TCG_TARGET_NEED_POOL_LABELS @@ -172,9 +168,4 @@ typedef enum { We prefer consistency across hosts on this. */ #define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_HAS_MEMORY_BSWAP 1 - -/* not defined -- call should be eliminated at compile time */ -void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t, uintptr_t); - #endif /* TCG_TARGET_H */ diff --git a/tests/Makefile.include b/tests/Makefile.include index 3accb83b13..c9d1674bd0 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -36,12 +36,10 @@ export SRC_PATH SPEED = quick --include tests/tcg/Makefile.prereqs -tests/tcg/Makefile.prereqs: config-host.mak - # Per guest TCG tests BUILD_TCG_TARGET_RULES=$(patsubst %,build-tcg-tests-%, $(TCG_TESTS_TARGETS)) CLEAN_TCG_TARGET_RULES=$(patsubst %,clean-tcg-tests-%, $(TCG_TESTS_TARGETS)) +DISTCLEAN_TCG_TARGET_RULES=$(patsubst %,distclean-tcg-tests-%, $(TCG_TESTS_TARGETS)) RUN_TCG_TARGET_RULES=$(patsubst %,run-tcg-tests-%, $(TCG_TESTS_TARGETS)) $(foreach TARGET,$(TCG_TESTS_TARGETS), \ @@ -50,35 +48,40 @@ $(foreach TARGET,$(TCG_TESTS_TARGETS), \ .PHONY: $(TCG_TESTS_TARGETS:%=build-tcg-tests-%) $(TCG_TESTS_TARGETS:%=build-tcg-tests-%): build-tcg-tests-%: $(BUILD_DIR)/tests/tcg/config-%.mak $(call quiet-command, \ - $(MAKE) -C tests/tcg/$* -f ../Makefile.target $(SUBDIR_MAKEFLAGS) \ - DOCKER_SCRIPT="$(DOCKER_SCRIPT)" \ - TARGET="$*" SRC_PATH="$(SRC_PATH)", \ + $(MAKE) -C tests/tcg/$* $(SUBDIR_MAKEFLAGS), \ "BUILD","$* guest-tests") .PHONY: $(TCG_TESTS_TARGETS:%=run-tcg-tests-%) $(TCG_TESTS_TARGETS:%=run-tcg-tests-%): run-tcg-tests-%: build-tcg-tests-% $(call quiet-command, \ - $(MAKE) -C tests/tcg/$* -f ../Makefile.target $(SUBDIR_MAKEFLAGS) \ - TARGET="$*" SRC_PATH="$(SRC_PATH)" SPEED=$(SPEED) run, \ + $(MAKE) -C tests/tcg/$* $(SUBDIR_MAKEFLAGS) SPEED=$(SPEED) run, \ "RUN", "$* guest-tests") .PHONY: $(TCG_TESTS_TARGETS:%=clean-tcg-tests-%) $(TCG_TESTS_TARGETS:%=clean-tcg-tests-%): clean-tcg-tests-%: $(call quiet-command, \ - $(MAKE) -C tests/tcg/$* -f ../Makefile.target $(SUBDIR_MAKEFLAGS) \ - TARGET="$*" SRC_PATH="$(SRC_PATH)" clean, \ + $(MAKE) -C tests/tcg/$* $(SUBDIR_MAKEFLAGS) clean, \ + "CLEAN", "$* guest-tests") + +.PHONY: $(TCG_TESTS_TARGETS:%=distclean-tcg-tests-%) +$(TCG_TESTS_TARGETS:%=distclean-tcg-tests-%): distclean-tcg-tests-%: + $(call quiet-command, \ + $(MAKE) -C tests/tcg/$* $(SUBDIR_MAKEFLAGS) distclean, \ "CLEAN", "$* guest-tests") .PHONY: build-tcg build-tcg: $(BUILD_TCG_TARGET_RULES) .PHONY: check-tcg -.ninja-goals.check-tcg = all $(if $(CONFIG_PLUGIN),test-plugins) +.ninja-goals.check-tcg = all test-plugins check-tcg: $(RUN_TCG_TARGET_RULES) .PHONY: clean-tcg clean-tcg: $(CLEAN_TCG_TARGET_RULES) +.PHONY: distclean-tcg +distclean-tcg: $(DISTCLEAN_TCG_TARGET_RULES) + # Python venv for running tests .PHONY: check-venv check-avocado check-acceptance check-acceptance-deprecated-warning @@ -86,10 +89,8 @@ clean-tcg: $(CLEAN_TCG_TARGET_RULES) # Build up our target list from the filtered list of ninja targets TARGETS=$(patsubst libqemu-%.fa, %, $(filter libqemu-%.fa, $(ninja-targets))) -TESTS_VENV_DIR=$(BUILD_DIR)/tests/venv -TESTS_VENV_REQ=$(SRC_PATH)/tests/requirements.txt +TESTS_VENV_TOKEN=$(BUILD_DIR)/pyvenv/tests.group TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results -TESTS_PYTHON=$(TESTS_VENV_DIR)/bin/python3 ifndef AVOCADO_TESTS AVOCADO_TESTS=tests/avocado endif @@ -105,20 +106,19 @@ else endif quiet-venv-pip = $(quiet-@)$(call quiet-command-run, \ - $(TESTS_PYTHON) -m pip -q --disable-pip-version-check $1, \ + $(PYTHON) -m pip -q --disable-pip-version-check $1, \ "VENVPIP","$1") -$(TESTS_VENV_DIR): $(TESTS_VENV_REQ) - $(call quiet-command, $(PYTHON) -m venv $@, VENV, $@) +$(TESTS_VENV_TOKEN): $(SRC_PATH)/pythondeps.toml $(call quiet-venv-pip,install -e "$(SRC_PATH)/python/") - $(call quiet-venv-pip,install -r $(TESTS_VENV_REQ)) + $(MKVENV_ENSUREGROUP) $< avocado $(call quiet-command, touch $@) $(TESTS_RESULTS_DIR): $(call quiet-command, mkdir -p $@, \ MKDIR, $@) -check-venv: $(TESTS_VENV_DIR) +check-venv: $(TESTS_VENV_TOKEN) FEDORA_31_ARCHES_TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGETS))) FEDORA_31_ARCHES_CANDIDATES=$(patsubst ppc64,ppc64le,$(FEDORA_31_ARCHES_TARGETS)) @@ -128,7 +128,7 @@ 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_PYTHON) -m avocado vmimage get \ + $(PYTHON) -m avocado vmimage get \ --distro=fedora --distro-version=31 --arch=$*, \ "AVOCADO", "Downloading avocado tests VM image for $*") @@ -137,7 +137,7 @@ get-vm-images: check-venv $(patsubst %,get-vm-image-fedora-31-%, $(FEDORA_31_DOW check-avocado: check-venv $(TESTS_RESULTS_DIR) get-vm-images $(call quiet-command, \ - $(TESTS_PYTHON) -m avocado \ + $(PYTHON) -m avocado \ --show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \ $(if $(AVOCADO_TAGS),, --filter-by-tags-include-empty \ --filter-by-tags-include-empty-key) \ @@ -160,8 +160,9 @@ check: check-build: run-ninja check-clean: - rm -rf $(TESTS_VENV_DIR) $(TESTS_RESULTS_DIR) + rm -rf $(TESTS_RESULTS_DIR) clean: check-clean clean-tcg +distclean: distclean-tcg endif diff --git a/tests/avocado/acpi-bits.py b/tests/avocado/acpi-bits.py new file mode 100644 index 0000000000..efe4f52ee0 --- /dev/null +++ b/tests/avocado/acpi-bits.py @@ -0,0 +1,409 @@ +#!/usr/bin/env python3 +# group: rw quick +# Exercise QEMU generated ACPI/SMBIOS tables using biosbits, +# https://biosbits.org/ +# +# 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 . +# +# +# Author: +# Ani Sinha + +# pylint: disable=invalid-name +# pylint: disable=consider-using-f-string + +""" +This is QEMU ACPI/SMBIOS avocado tests using biosbits. +Biosbits is available originally at https://biosbits.org/. +This test uses a fork of the upstream bits and has numerous fixes +including an upgraded acpica. The fork is located here: +https://gitlab.com/qemu-project/biosbits-bits . +""" + +import logging +import os +import platform +import re +import shutil +import subprocess +import tarfile +import tempfile +import time +import zipfile +from typing import ( + List, + Optional, + Sequence, +) +from qemu.machine import QEMUMachine +from avocado import skipIf +from avocado.utils import datadrainer as drainer +from avocado_qemu import QemuBaseTest + +deps = ["xorriso", "mformat"] # dependent tools needed in the test setup/box. +supported_platforms = ['x86_64'] # supported test platforms. + +# default timeout of 120 secs is sometimes not enough for bits test. +BITS_TIMEOUT = 200 + +def which(tool): + """ looks up the full path for @tool, returns None if not found + or if @tool does not have executable permissions. + """ + paths=os.getenv('PATH') + for p in paths.split(os.path.pathsep): + p = os.path.join(p, tool) + if os.path.exists(p) and os.access(p, os.X_OK): + return p + return None + +def missing_deps(): + """ returns True if any of the test dependent tools are absent. + """ + for dep in deps: + if which(dep) is None: + return True + return False + +def supported_platform(): + """ checks if the test is running on a supported platform. + """ + return platform.machine() in supported_platforms + +class QEMUBitsMachine(QEMUMachine): # pylint: disable=too-few-public-methods + """ + A QEMU VM, with isa-debugcon enabled and bits iso passed + using -cdrom to QEMU commandline. + + """ + def __init__(self, + binary: str, + args: Sequence[str] = (), + wrapper: Sequence[str] = (), + name: Optional[str] = None, + base_temp_dir: str = "/var/tmp", + debugcon_log: str = "debugcon-log.txt", + debugcon_addr: str = "0x403", + qmp_timer: Optional[float] = None): + # pylint: disable=too-many-arguments + + if name is None: + name = "qemu-bits-%d" % os.getpid() + super().__init__(binary, args, wrapper=wrapper, name=name, + base_temp_dir=base_temp_dir, + qmp_timer=qmp_timer) + self.debugcon_log = debugcon_log + self.debugcon_addr = debugcon_addr + self.base_temp_dir = base_temp_dir + + @property + def _base_args(self) -> List[str]: + args = super()._base_args + args.extend([ + '-chardev', + 'file,path=%s,id=debugcon' %os.path.join(self.base_temp_dir, + self.debugcon_log), + '-device', + 'isa-debugcon,iobase=%s,chardev=debugcon' %self.debugcon_addr, + ]) + return args + + def base_args(self): + """return the base argument to QEMU binary""" + return self._base_args + +@skipIf(not supported_platform() or missing_deps(), + 'unsupported platform or dependencies (%s) not installed' \ + % ','.join(deps)) +class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes + """ + ACPI and SMBIOS tests using biosbits. + + :avocado: tags=arch:x86_64 + :avocado: tags=acpi + + """ + # in slower systems the test can take as long as 3 minutes to complete. + timeout = BITS_TIMEOUT + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._vm = None + self._workDir = None + self._baseDir = None + + # following are some standard configuration constants + self._bitsInternalVer = 2020 # gitlab CI does shallow clones of depth 20 + self._bitsCommitHash = 'c7920d2b' # commit hash must match + # the artifact tag below + self._bitsTag = "qemu-bits-10262023" # this is the latest bits + # release as of today. + self._bitsArtSHA1Hash = 'b22cdfcfc7453875297d06d626f5474ee36a343f' + self._bitsArtURL = ("https://gitlab.com/qemu-project/" + "biosbits-bits/-/jobs/artifacts/%s/" + "download?job=qemu-bits-build" %self._bitsTag) + self._debugcon_addr = '0x403' + self._debugcon_log = 'debugcon-log.txt' + logging.basicConfig(level=logging.INFO) + self.logger = logging.getLogger('acpi-bits') + + def _print_log(self, log): + self.logger.info('\nlogs from biosbits follows:') + self.logger.info('==========================================\n') + self.logger.info(log) + self.logger.info('==========================================\n') + + def copy_bits_config(self): + """ copies the bios bits config file into bits. + """ + config_file = 'bits-cfg.txt' + bits_config_dir = os.path.join(self._baseDir, 'acpi-bits', + 'bits-config') + target_config_dir = os.path.join(self._workDir, + 'bits-%d' %self._bitsInternalVer, + 'boot') + self.assertTrue(os.path.exists(bits_config_dir)) + self.assertTrue(os.path.exists(target_config_dir)) + self.assertTrue(os.access(os.path.join(bits_config_dir, + config_file), os.R_OK)) + shutil.copy2(os.path.join(bits_config_dir, config_file), + target_config_dir) + self.logger.info('copied config file %s to %s', + config_file, target_config_dir) + + def copy_test_scripts(self): + """copies the python test scripts into bits. """ + + bits_test_dir = os.path.join(self._baseDir, 'acpi-bits', + 'bits-tests') + target_test_dir = os.path.join(self._workDir, + 'bits-%d' %self._bitsInternalVer, + 'boot', 'python') + + self.assertTrue(os.path.exists(bits_test_dir)) + self.assertTrue(os.path.exists(target_test_dir)) + + for filename in os.listdir(bits_test_dir): + if os.path.isfile(os.path.join(bits_test_dir, filename)) and \ + filename.endswith('.py2'): + # all test scripts are named with extension .py2 so that + # avocado does not try to load them. These scripts are + # written for python 2.7 not python 3 and hence if avocado + # loaded them, it would complain about python 3 specific + # syntaxes. + newfilename = os.path.splitext(filename)[0] + '.py' + shutil.copy2(os.path.join(bits_test_dir, filename), + os.path.join(target_test_dir, newfilename)) + self.logger.info('copied test file %s to %s', + filename, target_test_dir) + + # now remove the pyc test file if it exists, otherwise the + # changes in the python test script won't be executed. + testfile_pyc = os.path.splitext(filename)[0] + '.pyc' + if os.access(os.path.join(target_test_dir, testfile_pyc), + os.F_OK): + os.remove(os.path.join(target_test_dir, testfile_pyc)) + self.logger.info('removed compiled file %s', + os.path.join(target_test_dir, + testfile_pyc)) + + def fix_mkrescue(self, mkrescue): + """ grub-mkrescue is a bash script with two variables, 'prefix' and + 'libdir'. They must be pointed to the right location so that the + iso can be generated appropriately. We point the two variables to + the directory where we have extracted our pre-built bits grub + tarball. + """ + grub_x86_64_mods = os.path.join(self._workDir, 'grub-inst-x86_64-efi') + grub_i386_mods = os.path.join(self._workDir, 'grub-inst') + + self.assertTrue(os.path.exists(grub_x86_64_mods)) + self.assertTrue(os.path.exists(grub_i386_mods)) + + new_script = "" + with open(mkrescue, 'r', encoding='utf-8') as filehandle: + orig_script = filehandle.read() + new_script = re.sub('(^prefix=)(.*)', + r'\1"%s"' %grub_x86_64_mods, + orig_script, flags=re.M) + new_script = re.sub('(^libdir=)(.*)', r'\1"%s/lib"' %grub_i386_mods, + new_script, flags=re.M) + + with open(mkrescue, 'w', encoding='utf-8') as filehandle: + filehandle.write(new_script) + + def generate_bits_iso(self): + """ Uses grub-mkrescue to generate a fresh bits iso with the python + test scripts + """ + bits_dir = os.path.join(self._workDir, + 'bits-%d' %self._bitsInternalVer) + iso_file = os.path.join(self._workDir, + 'bits-%d.iso' %self._bitsInternalVer) + mkrescue_script = os.path.join(self._workDir, + 'grub-inst-x86_64-efi', 'bin', + 'grub-mkrescue') + + self.assertTrue(os.access(mkrescue_script, + os.R_OK | os.W_OK | os.X_OK)) + + self.fix_mkrescue(mkrescue_script) + + self.logger.info('using grub-mkrescue for generating biosbits iso ...') + + try: + if os.getenv('V') or os.getenv('BITS_DEBUG'): + subprocess.check_call([mkrescue_script, '-o', iso_file, + bits_dir], stderr=subprocess.STDOUT) + else: + subprocess.check_call([mkrescue_script, '-o', + iso_file, bits_dir], + stderr=subprocess.DEVNULL, + stdout=subprocess.DEVNULL) + except Exception as e: # pylint: disable=broad-except + self.skipTest("Error while generating the bits iso. " + "Pass V=1 in the environment to get more details. " + + str(e)) + + self.assertTrue(os.access(iso_file, os.R_OK)) + + self.logger.info('iso file %s successfully generated.', iso_file) + + def setUp(self): # pylint: disable=arguments-differ + super().setUp('qemu-system-') + + self._baseDir = os.getenv('AVOCADO_TEST_BASEDIR') + + # workdir could also be avocado's own workdir in self.workdir. + # At present, I prefer to maintain my own temporary working + # directory. It gives us more control over the generated bits + # log files and also for debugging, we may chose not to remove + # this working directory so that the logs and iso can be + # inspected manually and archived if needed. + self._workDir = tempfile.mkdtemp(prefix='acpi-bits-', + suffix='.tmp') + self.logger.info('working dir: %s', self._workDir) + + prebuiltDir = os.path.join(self._workDir, 'prebuilt') + if not os.path.isdir(prebuiltDir): + os.mkdir(prebuiltDir, mode=0o775) + + bits_zip_file = os.path.join(prebuiltDir, 'bits-%d-%s.zip' + %(self._bitsInternalVer, + self._bitsCommitHash)) + grub_tar_file = os.path.join(prebuiltDir, + 'bits-%d-%s-grub.tar.gz' + %(self._bitsInternalVer, + self._bitsCommitHash)) + + bitsLocalArtLoc = self.fetch_asset(self._bitsArtURL, + asset_hash=self._bitsArtSHA1Hash) + self.logger.info("downloaded bits artifacts to %s", bitsLocalArtLoc) + + # extract the bits artifact in the temp working directory + with zipfile.ZipFile(bitsLocalArtLoc, 'r') as zref: + zref.extractall(prebuiltDir) + + # extract the bits software in the temp working directory + with zipfile.ZipFile(bits_zip_file, 'r') as zref: + zref.extractall(self._workDir) + + with tarfile.open(grub_tar_file, 'r', encoding='utf-8') as tarball: + tarball.extractall(self._workDir) + + self.copy_test_scripts() + self.copy_bits_config() + self.generate_bits_iso() + + def parse_log(self): + """parse the log generated by running bits tests and + check for failures. + """ + debugconf = os.path.join(self._workDir, self._debugcon_log) + log = "" + with open(debugconf, 'r', encoding='utf-8') as filehandle: + log = filehandle.read() + + matchiter = re.finditer(r'(.*Summary: )(\d+ passed), (\d+ failed).*', + log) + for match in matchiter: + # verify that no test cases failed. + try: + self.assertEqual(match.group(3).split()[0], '0', + 'Some bits tests seems to have failed. ' \ + 'Please check the test logs for more info.') + except AssertionError as e: + self._print_log(log) + raise e + else: + if os.getenv('V') or os.getenv('BITS_DEBUG'): + self._print_log(log) + + def tearDown(self): + """ + Lets do some cleanups. + """ + if self._vm: + self.assertFalse(not self._vm.is_running) + if not os.getenv('BITS_DEBUG') and self._workDir: + self.logger.info('removing the work directory %s', self._workDir) + shutil.rmtree(self._workDir) + else: + self.logger.info('not removing the work directory %s ' \ + 'as BITS_DEBUG is ' \ + 'passed in the environment', self._workDir) + super().tearDown() + + def test_acpi_smbios_bits(self): + """The main test case implementation.""" + + iso_file = os.path.join(self._workDir, + 'bits-%d.iso' %self._bitsInternalVer) + + self.assertTrue(os.access(iso_file, os.R_OK)) + + self._vm = QEMUBitsMachine(binary=self.qemu_bin, + base_temp_dir=self._workDir, + debugcon_log=self._debugcon_log, + debugcon_addr=self._debugcon_addr) + + self._vm.add_args('-cdrom', '%s' %iso_file) + # the vm needs to be run under icount so that TCG emulation is + # consistent in terms of timing. smilatency tests have consistent + # timing requirements. + self._vm.add_args('-icount', 'auto') + # currently there is no support in bits for recognizing 64-bit SMBIOS + # entry points. QEMU defaults to 64-bit entry points since the + # upstream commit bf376f3020 ("hw/i386/pc: Default to use SMBIOS 3.0 + # for newer machine models"). Therefore, enforce 32-bit entry point. + self._vm.add_args('-machine', 'smbios-entry-point-type=32') + + # enable console logging + self._vm.set_console() + self._vm.launch() + + self.logger.debug("Console output from bits VM follows ...") + c_drainer = drainer.LineLogger(self._vm.console_socket.fileno(), + logger=self.logger.getChild("console"), + stop_check=(lambda : + not self._vm.is_running())) + c_drainer.start() + + # biosbits has been configured to run all the specified test suites + # in batch mode and then automatically initiate a vm shutdown. + # Set timeout to BITS_TIMEOUT for SHUTDOWN event from bits VM at par + # with the avocado test timeout. + self._vm.event_wait('SHUTDOWN', timeout=BITS_TIMEOUT) + self._vm.wait(timeout=None) + self.parse_log() diff --git a/tests/avocado/acpi-bits/bits-config/bits-cfg.txt b/tests/avocado/acpi-bits/bits-config/bits-cfg.txt new file mode 100644 index 0000000000..8010804453 --- /dev/null +++ b/tests/avocado/acpi-bits/bits-config/bits-cfg.txt @@ -0,0 +1,18 @@ +# BITS configuration file +[bits] + +# To run BITS in batch mode, set batch to a list of one or more of the +# following keywords; BITS will then run all of the requested operations, then +# save the log file to disk. +# +# test: Run the full BITS testsuite. +# acpi: Dump all ACPI structures. +# smbios: Dump all SMBIOS structures. +# +# Leave batch set to an empty string to disable batch mode. +# batch = + +# Uncomment the following to run all available batch operations +# please take a look at boot/python/init.py in bits zip file +# to see how these options are parsed and used. +batch = test acpi smbios diff --git a/tests/avocado/acpi-bits/bits-tests/smbios.py2 b/tests/avocado/acpi-bits/bits-tests/smbios.py2 new file mode 100644 index 0000000000..5868a7137a --- /dev/null +++ b/tests/avocado/acpi-bits/bits-tests/smbios.py2 @@ -0,0 +1,2434 @@ +# Copyright (c) 2015, Intel Corporation +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 script runs only from the biosbits VM. + +"""SMBIOS/DMI module.""" + +import bits +import bitfields +import ctypes +import redirect +import struct +import uuid +import unpack +import ttypager +import sys + +class SMBIOS(unpack.Struct): + def __new__(cls): + if sys.platform == "BITS-EFI": + import efi + sm_ptr = efi.system_table.ConfigurationTableDict.get(efi.SMBIOS_TABLE_GUID) + else: + address = 0xF0000 + mem = bits.memory(0xF0000, 0x10000) + for offset in range(0, len(mem), 16): + signature = (ctypes.c_char * 4).from_address(address + offset).value + if signature == "_SM_": + entry_point_length = ctypes.c_ubyte.from_address(address + offset + 5).value + csum = sum(map(ord, mem[offset:offset + entry_point_length])) & 0xff + if csum == 0: + sm_ptr = address + offset + break + else: + return None + + if not sm_ptr: + return None + + sm = super(SMBIOS, cls).__new__(cls) + sm._header_memory = bits.memory(sm_ptr, 0x1f) + return sm + + def __init__(self): + super(SMBIOS, self).__init__() + u = unpack.Unpackable(self._header_memory) + self.add_field('header', Header(u)) + self._structure_memory = bits.memory(self.header.structure_table_address, self.header.structure_table_length) + u = unpack.Unpackable(self._structure_memory) + self.add_field('structures', unpack.unpack_all(u, _smbios_structures, self), unpack.format_each("\n\n{!r}")) + + def structure_type(self, num): + '''Dumps structure of given Type if present''' + try: + types_present = [self.structures[x].smbios_structure_type for x in range(len(self.structures))] + matrix = dict() + for index in range(len(types_present)): + if types_present.count(types_present[index]) == 1: + matrix[types_present[index]] = self.structures[index] + else: # if multiple structures of the same type, return a list of structures for the type number + if matrix.has_key(types_present[index]): + matrix[types_present[index]].append(self.structures[index]) + else: + matrix[types_present[index]] = [self.structures[index]] + return matrix[num] + except: + print "Failure: Type {} - not found".format(num) + +class Header(unpack.Struct): + def __new__(cls, u): + return super(Header, cls).__new__(cls) + + def __init__(self, u): + super(Header, self).__init__() + self.raw_data = u.unpack_rest() + u = unpack.Unpackable(self.raw_data) + self.add_field('anchor_string', u.unpack_one("4s")) + self.add_field('checksum', u.unpack_one("B")) + self.add_field('length', u.unpack_one("B")) + self.add_field('major_version', u.unpack_one("B")) + self.add_field('minor_version', u.unpack_one("B")) + self.add_field('max_structure_size', u.unpack_one(" len(self.strings): + return "(error: string index out of range)" + return self.strings[i - 1] + +class BIOSInformation(SmbiosBaseStructure): + smbios_structure_type = 0 + + def __init__(self, u, sm): + super(BIOSInformation, self).__init__(u, sm) + u = self.u + try: + self.add_field('vendor', u.unpack_one("B"), self.fmtstr) + self.add_field('version', u.unpack_one("B"), self.fmtstr) + self.add_field('starting_address_segment', u.unpack_one("= (2,"4"): + characteristic_bytes = 2 + else: + characteristic_bytes = self.length - 0x12 + self.add_field('characteristics_extensions', [u.unpack_one("B") for b in range(characteristic_bytes)]) + if (sm.header.major_version, minor_version_str) >= (2,"4"): + self.add_field('major_release', u.unpack_one("B")) + self.add_field('minor_release', u.unpack_one("B")) + self.add_field('ec_major_release', u.unpack_one("B")) + self.add_field('ec_minor_release', u.unpack_one("B")) + except: + self.decode_failure = True + print "Error parsing BIOSInformation" + import traceback + traceback.print_exc() + self.fini() + +class SystemInformation(SmbiosBaseStructure): + smbios_structure_type = 1 + + def __init__(self, u, sm): + super(SystemInformation, self).__init__(u, sm) + u = self.u + try: + self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr) + self.add_field('product_name', u.unpack_one("B"), self.fmtstr) + self.add_field('version', u.unpack_one("B"), self.fmtstr) + self.add_field('serial_number', u.unpack_one("B"), self.fmtstr) + if self.length > 0x8: + self.add_field('uuid', uuid.UUID(bytes_le=u.unpack_one("16s"))) + wakeup_types = { + 0: 'Reserved', + 1: 'Other', + 2: 'Unknown', + 3: 'APM Timer', + 4: 'Modem Ring', + 5: 'LAN Remote', + 6: 'Power Switch', + 7: 'PCI PME#', + 8: 'AC Power Restored' + } + self.add_field('wakeup_type', u.unpack_one("B"), unpack.format_table("{}", wakeup_types)) + if self.length > 0x19: + self.add_field('sku_number', u.unpack_one("B"), self.fmtstr) + self.add_field('family', u.unpack_one("B"), self.fmtstr) + except: + self.decode_failure = True + print "Error parsing SystemInformation" + import traceback + traceback.print_exc() + self.fini() + +_board_types = { + 1: 'Unknown', + 2: 'Other', + 3: 'Server Blade', + 4: 'Connectivity Switch', + 5: 'System Management Module', + 6: 'Processor Module', + 7: 'I/O Module', + 8: 'Memory Module', + 9: 'Daughter Board', + 0xA: 'Motherboard', + 0xB: 'Processor/Memory Module', + 0xC: 'Processor/IO Module', + 0xD: 'Interconnect Board' +} + +class BaseboardInformation(SmbiosBaseStructure): + smbios_structure_type = 2 + + def __init__(self, u, sm): + super(BaseboardInformation, self).__init__(u, sm) + u = self.u + try: + self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr) + self.add_field('product', u.unpack_one("B"), self.fmtstr) + self.add_field('version', u.unpack_one("B"), self.fmtstr) + self.add_field('serial_number', u.unpack_one("B"), self.fmtstr) + + if self.length > 0x8: + self.add_field('asset_tag', u.unpack_one("B"), self.fmtstr) + + if self.length > 0x9: + self.add_field('feature_flags', u.unpack_one("B")) + self.add_field('hosting_board', bool(bitfields.getbits(self.feature_flags, 0)), "feature_flags[0]={}") + self.add_field('requires_daughter_card', bool(bitfields.getbits(self.feature_flags, 1)), "feature_flags[1]={}") + self.add_field('removable', bool(bitfields.getbits(self.feature_flags, 2)), "feature_flags[2]={}") + self.add_field('replaceable', bool(bitfields.getbits(self.feature_flags, 3)), "feature_flags[3]={}") + self.add_field('hot_swappable', bool(bitfields.getbits(self.feature_flags, 4)), "feature_flags[4]={}") + + if self.length > 0xA: + self.add_field('location', u.unpack_one("B"), self.fmtstr) + + if self.length > 0xB: + self.add_field('chassis_handle', u.unpack_one(" 0xD: + self.add_field('board_type', u.unpack_one("B"), unpack.format_table("{}", _board_types)) + + if self.length > 0xE: + self.add_field('handle_count', u.unpack_one("B")) + if self.handle_count > 0: + self.add_field('contained_object_handles', tuple(u.unpack_one(" 9: + chassis_states = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'Safe', + 0x04: 'Warning', + 0x05: 'Critical', + 0x06: 'Non-recoverable', + } + self.add_field('bootup_state', u.unpack_one("B"), unpack.format_table("{}", chassis_states)) + self.add_field('power_supply_state', u.unpack_one("B"), unpack.format_table("{}", chassis_states)) + self.add_field('thermal_state', u.unpack_one("B"), unpack.format_table("{}", chassis_states)) + security_states = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'None', + 0x04: 'External interface locked out', + 0x05: 'External interface enabled', + } + self.add_field('security_status', u.unpack_one("B"), unpack.format_table("{}", security_states)) + if self.length > 0xd: + self.add_field('oem_defined', u.unpack_one(" 0x11: + self.add_field('height', u.unpack_one("B")) + self.add_field('num_power_cords', u.unpack_one("B")) + self.add_field('contained_element_count', u.unpack_one("B")) + self.add_field('contained_element_length', u.unpack_one("B")) + if getattr(self, 'contained_element_count', 0): + self.add_field('contained_elements', tuple(SystemEnclosureContainedElement(u, self.contained_element_length) for i in range(self.contained_element_count))) + if self.length > (0x15 + (getattr(self, 'contained_element_count', 0) * getattr(self, 'contained_element_length', 0))): + self.add_field('sku_number', u.unpack_one("B"), self.fmtstr) + except: + self.decode_failure = True + print "Error parsing SystemEnclosure" + import traceback + traceback.print_exc() + self.fini() + +class SystemEnclosureContainedElement(unpack.Struct): + def __init__(self, u, length): + super(SystemEnclosureContainedElement, self).__init__() + self.start_offset = u.offset + self.raw_data = u.unpack_raw(length) + self.u = unpack.Unpackable(self.raw_data) + u = self.u + self.add_field('contained_element_type', u.unpack_one("B")) + type_selections = { + 0: 'SMBIOS baseboard type enumeration', + 1: 'SMBIOS structure type enumeration', + } + self.add_field('type_select', bitfields.getbits(self.contained_element_type, 7), unpack.format_table("contained_element_type[7]={}", type_selections)) + self.add_field('type', bitfields.getbits(self.contained_element_type, 6, 0)) + if self.type_select == 0: + self.add_field('smbios_board_type', self.type, unpack.format_table("{}", _board_types)) + else: + self.add_field('smbios_structure_type', self.type) + self.add_field('minimum', u.unpack_one("B")) + self.add_field('maximum', u.unpack_one("B")) + if not u.at_end(): + self.add_field('data', u.unpack_rest()) + del self.u + +class ProcessorInformation(SmbiosBaseStructure): + smbios_structure_type = 4 + + def __init__(self, u, sm): + super(ProcessorInformation, self).__init__(u, sm) + u = self.u + try: + self.add_field('socket_designation', u.unpack_one("B"), self.fmtstr) + processor_types = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'Central Processor', + 0x04: 'Math Processor', + 0x05: 'DSP Processor', + 0x06: 'Video Processor', + } + self.add_field('processor_type', u.unpack_one("B"), unpack.format_table("{}", processor_types)) + self.add_field('processor_family', u.unpack_one("B")) + self.add_field('processor_manufacturer', u.unpack_one("B"), self.fmtstr) + self.add_field('processor_id', u.unpack_one(" 0x1A: + self.add_field('l1_cache_handle', u.unpack_one(" 0x20: + self.add_field('serial_number', u.unpack_one("B"), self.fmtstr) + self.add_field('asset_tag', u.unpack_one("B"), self.fmtstr) + self.add_field('part_number', u.unpack_one("B"), self.fmtstr) + if self.length > 0x24: + self.add_field('core_count', u.unpack_one("B")) + self.add_field('core_enabled', u.unpack_one("B")) + self.add_field('thread_count', u.unpack_one("B")) + self.add_field('processor_characteristics', u.unpack_one(" 0x28: + self.add_field('processor_family_2', u.unpack_one(" 0x2A: + self.add_field('core_count2', u.unpack_one(" 0x0F: + self.add_field('cache_speed', u.unpack_one("B")) + if self.length > 0x10: + _error_correction = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'None', + 0x04: 'Parity', + 0x05: 'Single-bit ECC', + 0x06: 'Multi-bit ECC' + } + self.add_field('error_correction', u.unpack_one("B"), unpack.format_table("{}", _error_correction)) + if self.length > 0x10: + _system_cache_type = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'Instruction', + 0x04: 'Data', + 0x05: 'Unified' + } + self.add_field('system_cache_type', u.unpack_one("B"), unpack.format_table("{}", _system_cache_type)) + if self.length > 0x12: + _associativity = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'Direct Mapped', + 0x04: '2-way Set-Associative', + 0x05: '4-way Set-Associative', + 0x06: 'Fully Associative', + 0x07: '8-way Set-Associative', + 0x08: '16-way Set-Associative', + 0x09: '12-way Set-Associative', + 0x0A: '24-way Set-Associative', + 0x0B: '32-way Set-Associative', + 0x0C: '48-way Set-Associative', + 0x0D: '64-way Set-Associative', + 0x0E: '20-way Set-Associative' + } + self.add_field('associativity', u.unpack_one("B"), unpack.format_table("{}", _associativity)) + + except: + self.decode_failure = True + print "Error parsing CacheInformation" + import traceback + traceback.print_exc() + self.fini() + +class PortConnectorInfo(SmbiosBaseStructure): + smbios_structure_type = 8 + + def __init__(self, u, sm): + super(PortConnectorInfo, self).__init__(u, sm) + u = self.u + try: + self.add_field('internal_reference_designator', u.unpack_one("B"), self.fmtstr) + connector_types = { + 0x00: 'None', + 0x01: 'Centronics', + 0x02: 'Mini Centronics', + 0x03: 'Proprietary', + 0x04: 'DB-25 pin male', + 0x05: 'DB-25 pin female', + 0x06: 'DB-15 pin male', + 0x07: 'DB-15 pin female', + 0x08: 'DB-9 pin male', + 0x09: 'DB-9 pin female', + 0x0A: 'RJ-11', + 0x0B: 'RJ-45', + 0x0C: '50-pin MiniSCSI', + 0x0D: 'Mini-DIN', + 0x0E: 'Micro-DIN', + 0x0F: 'PS/2', + 0x10: 'Infrared', + 0x11: 'HP-HIL', + 0x12: 'Access Bus (USB)', + 0x13: 'SSA SCSI', + 0x14: 'Circular DIN-8 male', + 0x15: 'Circular DIN-8 female', + 0x16: 'On Board IDE', + 0x17: 'On Board Floppy', + 0x18: '9-pin Dual Inline (pin 10 cut)', + 0x19: '25-pin Dual Inline (pin 26 cut)', + 0x1A: '50-pin Dual Inline', + 0x1B: '68-pin Dual Inline', + 0x1C: 'On Board Sound Input from CD-ROM', + 0x1D: 'Mini-Centronics Type-14', + 0x1E: 'Mini-Centronics Type-26', + 0x1F: 'Mini-jack (headphones)', + 0x20: 'BNC', + 0x21: '1394', + 0x22: 'SAS/SATA Plug Receptacle', + 0xA0: 'PC-98', + 0xA1: 'PC-98Hireso', + 0xA2: 'PC-H98', + 0xA3: 'PC-98Note', + 0xA4: 'PC-98Full', + 0xFF: 'Other', + } + self.add_field('internal_connector_type', u.unpack_one("B"), unpack.format_table("{}", connector_types)) + self.add_field('external_reference_designator', u.unpack_one("B"), self.fmtstr) + self.add_field('external_connector_type', u.unpack_one("B"), unpack.format_table("{}", connector_types)) + port_types = { + 0x00: 'None', + 0x01: 'Parallel Port XT/AT Compatible', + 0x02: 'Parallel Port PS/2', + 0x03: 'Parallel Port ECP', + 0x04: 'Parallel Port EPP', + 0x05: 'Parallel Port ECP/EPP', + 0x06: 'Serial Port XT/AT Compatible', + 0x07: 'Serial Port 16450 Compatible', + 0x08: 'Serial Port 16550 Compatible', + 0x09: 'Serial Port 16550A Compatible', + 0x0A: 'SCSI Port', + 0x0B: 'MIDI Port', + 0x0C: 'Joy Stick Port', + 0x0D: 'Keyboard Port', + 0x0E: 'Mouse Port', + 0x0F: 'SSA SCSI', + 0x10: 'USB', + 0x11: 'FireWire (IEEE P1394)', + 0x12: 'PCMCIA Type I2', + 0x13: 'PCMCIA Type II', + 0x14: 'PCMCIA Type III', + 0x15: 'Cardbus', + 0x16: 'Access Bus Port', + 0x17: 'SCSI II', + 0x18: 'SCSI Wide', + 0x19: 'PC-98', + 0x1A: 'PC-98-Hireso', + 0x1B: 'PC-H98', + 0x1C: 'Video Port', + 0x1D: 'Audio Port', + 0x1E: 'Modem Port', + 0x1F: 'Network Port', + 0x20: 'SATA', + 0x21: 'SAS', + 0xA0: '8251 Compatible', + 0xA1: '8251 FIFO Compatible', + 0xFF: 'Other', + } + self.add_field('port_type', u.unpack_one("B"), unpack.format_table("{}", port_types)) + except: + self.decodeFailure = True + print "Error parsing PortConnectorInfo" + import traceback + traceback.print_exc() + self.fini() + +class SystemSlots(SmbiosBaseStructure): + smbios_structure_type = 9 + + def __init__(self, u, sm): + super(SystemSlots, self).__init__(u, sm) + u = self.u + try: + self.add_field('designation', u.unpack_one("B"), self.fmtstr) + _slot_types = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'ISA', + 0x04: 'MCA', + 0x05: 'EISA', + 0x06: 'PCI', + 0x07: 'PC Card (PCMCIA)', + 0x08: 'VL-VESA', + 0x09: 'Proprietary', + 0x0A: 'Processor Card Slot', + 0x0B: 'Proprietary Memory Card Slot', + 0x0C: 'I/O Riser Card Slot', + 0x0D: 'NuBus', + 0x0E: 'PCI 66MHz Capable', + 0x0F: 'AGP', + 0x10: 'AGP 2X', + 0x11: 'AGP 4X', + 0x12: 'PCI-X', + 0x13: 'AGP 8X', + 0xA0: 'PC-98/C20', + 0xA1: 'PC-98/C24', + 0xA2: 'PC-98/E', + 0xA3: 'PC-98/Local Bus', + 0xA4: 'PC-98/Card', + 0xA5: 'PCI Express', + 0xA6: 'PCI Express x1', + 0xA7: 'PCI Express x2', + 0xA8: 'PCI Express x4', + 0xA9: 'PCI Express x8', + 0xAA: 'PCI Express x16', + 0xAB: 'PCI Express Gen 2', + 0xAC: 'PCI Express Gen 2 x1', + 0xAD: 'PCI Express Gen 2 x2', + 0xAE: 'PCI Express Gen 2 x4', + 0xAF: 'PCI Express Gen 2 x8', + 0xB0: 'PCI Express Gen 2 x16', + 0xB1: 'PCI Express Gen 3', + 0xB2: 'PCI Express Gen 3 x1', + 0xB3: 'PCI Express Gen 3 x2', + 0xB4: 'PCI Express Gen 3 x4', + 0xB5: 'PCI Express Gen 3 x8', + 0xB6: 'PCI Express Gen 3 x16', + } + self.add_field('slot_type', u.unpack_one("B"), unpack.format_table("{}", _slot_types)) + _slot_data_bus_widths = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: '8 bit', + 0x04: '16 bit', + 0x05: '32 bit', + 0x06: '64 bit', + 0x07: '128 bit', + 0x08: '1x or x1', + 0x09: '2x or x2', + 0x0A: '4x or x4', + 0x0B: '8x or x8', + 0x0C: '12x or x12', + 0x0D: '16x or x16', + 0x0E: '32x or x32', + } + self.add_field('slot_data_bus_width', u.unpack_one('B'), unpack.format_table("{}", _slot_data_bus_widths)) + _current_usages = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'Available', + 0x04: 'In use', + } + self.add_field('current_usage', u.unpack_one('B'), unpack.format_table("{}", _current_usages)) + _slot_lengths = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'Short Length', + 0x04: 'Long Length', + } + self.add_field('slot_length', u.unpack_one('B'), unpack.format_table("{}", _slot_lengths)) + self.add_field('slot_id', u.unpack_one(' 0x0C: + self.add_field('characteristics2', u.unpack_one('B')) + self.add_field('supports_PME', bool(bitfields.getbits(self.characteristics2, 0)), "characteristics2[0]={}") + self.add_field('supports_hot_plug', bool(bitfields.getbits(self.characteristics2, 1)), "characteristics2[1]={}") + self.add_field('supports_smbus', bool(bitfields.getbits(self.characteristics2, 2)), "characteristics2[2]={}") + if self.length > 0x0D: + self.add_field('segment_group_number', u.unpack_one(' 0x05: + self.add_field('flags', u.unpack_one('B')) + self.add_field('abbreviated_format', bool(bitfields.getbits(self.flags, 0)), "flags[0]={}") + if self.length > 0x6: + u.skip(15) + self.add_field('current_language', u.unpack_one('B'), self.fmtstr) + except: + self.decodeFailure = True + print "Error parsing BIOSLanguageInformation" + import traceback + traceback.print_exc() + self.fini() + +class GroupAssociations(SmbiosBaseStructure): + smbios_structure_type = 14 + + def __init__(self, u, sm): + super(GroupAssociations, self).__init__(u, sm) + u = self.u + try: + self.add_field('group_name', u.unpack_one("B"), self.fmtstr) + self.add_field('item_type', u.unpack_one('B')) + self.add_field('item_handle', u.unpack_one(' 0x14: + _log_header_formats = { + 0: 'No header', + 1: 'Type 1 log header', + xrange(2, 0x7f): 'Available for future assignment', + xrange(0x80, 0xff): 'BIOS vendor or OEM-specific format' + } + self.add_field('log_header_format', u.unpack_one("B"), unpack.format_table("{}", _log_header_formats)) + if self.length > 0x15: + self.add_field('num_supported_log_type_descriptors', u.unpack_one('B')) + if self.length > 0x16: + self.add_field('length_log_type_descriptor', u.unpack_one('B')) + if self.length != (0x17 + (self.num_supported_log_type_descriptors * self.length_log_type_descriptor)): + print "Error: structure length ({}) != 0x17 + (num_supported_log_type_descriptors ({}) * length_log_type_descriptor({}))".format(self.length, self.num_supported_log_type_descriptors, self.length_log_type_descriptor) + print "structure length = {}".format(self.length) + print "num_supported_log_type_descriptors = {}".format(self.num_supported_log_type_descriptors) + print "length_log_type_descriptor = {}".format(self.length_log_type_descriptor) + self.decodeFailure = True + self.add_field('descriptors', tuple(EventLogDescriptor.unpack(u) for i in range(self.num_supported_log_type_descriptors)), unpack.format_each("\n{!r}")) + except: + self.decodeFailure = True + print "Error parsing SystemEventLog" + import traceback + traceback.print_exc() + self.fini() + +class EventLogDescriptor(unpack.Struct): + @staticmethod + def _unpack(u): + _event_log_type_descriptors = { + 0x00: 'Reserved', + 0x01: 'Single-bit ECC memory error', + 0x02: 'Multi-bit ECC memory error', + 0x03: 'Parity memory error', + 0x04: 'Bus time-out', + 0x05: 'I/O Channel Check', + 0x06: 'Software NMI', + 0x07: 'POST Memory Resize', + 0x08: 'POST Error', + 0x09: 'PCI Parity Error', + 0x0A: 'PCI System Error', + 0x0B: 'CPU Failure', + 0x0C: 'EISA FailSafe Timer time-out', + 0x0D: 'Correctable memory log disabled', + 0x0E: 'Logging disabled for a specific Event Type - too many errors of the same type received in a short amount of time', + 0x0F: 'Reserved', + 0x10: 'System Limit Exceeded', + 0x11: 'Asynchronous hardware timer expired and issued a system reset', + 0x12: 'System configuration information', + 0x13: 'Hard-disk information', + 0x14: 'System reconfigured', + 0x15: 'Uncorrectable CPU-complex error', + 0x16: 'Log Area Reset/Cleared', + 0x17: 'System boot', + xrange(0x18, 0x7F): 'Unused, available for assignment', + xrange(0x80, 0xFE): 'Available for system- and OEM-specific assignments', + 0xFF: 'End of log' + } + yield 'log_type', u.unpack_one('B'), unpack.format_table("{}", _event_log_type_descriptors) + _event_log_format = { + 0x00: 'None', + 0x01: 'Handle', + 0x02: 'Multiple-Event', + 0x03: 'Multiple-Event Handle', + 0x04: 'POST Results Bitmap', + 0x05: 'System Management Type', + 0x06: 'Multiple-Event System Management Type', + xrange(0x80, 0xFF): 'OEM assigned' + } + yield 'variable_data_format_type', u.unpack_one('B'), unpack.format_table("{}", _event_log_format) + +class PhysicalMemoryArray(SmbiosBaseStructure): + smbios_structure_type = 16 + + def __init__(self, u, sm): + super(PhysicalMemoryArray, self).__init__(u, sm) + u = self.u + try: + if self.length > 0x4: + _location_field = { + 0x01: "Other", + 0x02: "Unknown", + 0x03: "System board or motherboard", + 0x04: "ISA add-on card", + 0x05: "EISA add-on card", + 0x06: "PCI add-on card", + 0x07: "MCA add-on card", + 0x08: "PCMCIA add-on card", + 0x09: "Proprietary add-on card", + 0x0A: "NuBus", + 0xA0: "PC-98/C20 add-on card", + 0xA1: "PC-98/C24 add-on card", + 0xA2: "PC-98/E add-on card", + 0xA3: "PC-98/Local bus add-on card" + } + self.add_field('location', u.unpack_one("B"), unpack.format_table("{}", _location_field)) + if self.length > 0x05: + _use = { + 0x01: "Other", + 0x02: "Unknown", + 0x03: "System memory", + 0x04: "Video memory", + 0x05: "Flash memory", + 0x06: "Non-volatile RAM", + 0x07: "Cache memory" + } + self.add_field('use', u.unpack_one('B'), unpack.format_table("{}", _use)) + if self.length > 0x06: + _error_correction = { + 0x01: "Other", + 0x02: "Unknown", + 0x03: "None", + 0x04: "Parity", + 0x05: "Single-bit ECC", + 0x06: "Multi-bit ECC", + 0x07: "CRC" + } + self.add_field('memory_error_correction', u.unpack_one('B'), unpack.format_table("{}", _error_correction)) + if self.length > 0x07: + self.add_field('maximum_capacity', u.unpack_one(' 0x0B: + self.add_field('memory_error_information_handle', u.unpack_one(' 0x0D: + self.add_field('num_memory_devices', u.unpack_one(' 0x0F: + self.add_field('extended_maximum_capacity', u.unpack_one(' 0x4: + self.add_field('physical_memory_array_handle', u.unpack_one(" 0x6: + self.add_field('memory_error_information_handle', u.unpack_one(" 0x8: + self.add_field('total_width', u.unpack_one(" 0xA: + self.add_field('data_width', u.unpack_one(" 0xC: + self.add_field('size', u.unpack_one(" 0xE: + _form_factors = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'SIMM', + 0x04: 'SIP', + 0x05: 'Chip', + 0x06: 'DIP', + 0x07: 'ZIP', + 0x08: 'Proprietary Card', + 0x09: 'DIMM', + 0x0A: 'TSOP', + 0x0B: 'Row of chips', + 0x0C: 'RIMM', + 0x0D: 'SODIMM', + 0x0E: 'SRIMM', + 0x0F: 'FB-DIMM' + } + self.add_field('form_factor', u.unpack_one("B"), unpack.format_table("{}", _form_factors)) + if self.length > 0xF: + self.add_field('device_set', u.unpack_one("B")) + if self.length > 0x10: + self.add_field('device_locator', u.unpack_one("B"), self.fmtstr) + if self.length > 0x11: + self.add_field('bank_locator', u.unpack_one("B"), self.fmtstr) + if self.length > 0x12: + _memory_types = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'DRAM', + 0x04: 'EDRAM', + 0x05: 'VRAM', + 0x06: 'SRAM', + 0x07: 'RAM', + 0x08: 'ROM', + 0x09: 'FLASH', + 0x0A: 'EEPROM', + 0x0B: 'FEPROM', + 0x0C: 'EPROM', + 0x0D: 'CDRAM', + 0x0E: '3DRAM', + 0x0F: 'SDRAM', + 0x10: 'SGRAM', + 0x11: 'RDRAM', + 0x12: 'DDR', + 0x13: 'DDR2', + 0x14: 'DDR2 FB-DIMM', + xrange(0x15, 0x17): 'Reserved', + 0x18: 'DDR3', + 0x19: 'FBD2' + } + self.add_field('memory_type', u.unpack_one("B"), unpack.format_table("{}", _memory_types)) + if self.length > 0x13: + self.add_field('type_detail', u.unpack_one(' 0x15: + self.add_field('speed', u.unpack_one(" 0x17: + self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr) + if self.length > 0x18: + self.add_field('serial_number', u.unpack_one("B"), self.fmtstr) + if self.length > 0x19: + self.add_field('asset_tag', u.unpack_one("B"), self.fmtstr) + if self.length > 0x1A: + self.add_field('part_number', u.unpack_one("B"), self.fmtstr) + if self.length > 0x1B: + self.add_field('attributes', u.unpack_one("B")) + self.add_field('rank', bitfields.getbits(self.attributes, 3, 0), "attributes[3:0]={}") + if self.length > 0x1C: + if self.size == 0x7FFF: + self.add_field('extended_size', u.unpack_one(' 0x20: + self.add_field('configured_memory_clock_speed', u.unpack_one(" 0x22: + self.add_field('minimum_voltage', u.unpack_one(" 0x24: + self.add_field('maximum_voltage', u.unpack_one(" 0x26: + self.add_field('configured_voltage', u.unpack_one(" 0x4: + _error_types = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'OK', + 0x04: 'Bad read', + 0x05: 'Parity error', + 0x06: 'Single-bit error', + 0x07: 'Double-bit error', + 0x08: 'Multi-bit error', + 0x09: 'Nibble error', + 0x0A: 'Checksum error', + 0x0B: 'CRC error', + 0x0C: 'Corrected single-bit error', + 0x0D: 'Corrected error', + 0x0E: 'Uncorrectable error' + } + self.add_field('error_type', u.unpack_one("B"), unpack.format_table("{}", _error_types)) + if self.length > 0x5: + _error_granularity_field = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'Device level', + 0x04: 'Memory partition level' + } + self.add_field('error_granularity', u.unpack_one("B"), unpack.format_table("{}", _error_granularity_field)) + if self.length > 0x6: + _error_operation_field = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'Read', + 0x04: 'Write', + 0x05: 'Partial write' + } + self.add_field('error_operation', u.unpack_one("B"), unpack.format_table("{}", _error_operation_field)) + if self.length > 0x7: + self.add_field('vendor_syndrome', u.unpack_one(" 0xB: + self.add_field('memory_array_error_address', u.unpack_one(" 0xF: + self.add_field('device_error_address', u.unpack_one(" 0x13: + self.add_field('error_resolution', u.unpack_one(" 0x4: + self.add_field('starting_address', u.unpack_one(" 0x8: + self.add_field('ending_address', u.unpack_one(" 0xC: + self.add_field('memory_array_handle', u.unpack_one(" 0xE: + self.add_field('partition_width', u.unpack_one("B")) + if self.length > 0xF: + # valid if starting_address = FFFF FFFF + if self.starting_address == 0xFFFFFFFF: + self.add_field('extended_starting_address', u.unpack_one(" 0x17: + self.add_field('extended_ending_address', u.unpack_one(" 0x4: + self.add_field('starting_address', u.unpack_one(" 0x8: + self.add_field('ending_address', u.unpack_one(" 0xC: + self.add_field('memory_device_handle', u.unpack_one(" 0xE: + self.add_field('memory_array_mapped_address_handle', u.unpack_one(" 0x10: + self.add_field('partition_row_position', u.unpack_one("B")) + if self.length > 0x11: + self.add_field('interleave_position', u.unpack_one("B")) + if self.length > 0x12: + self.add_field('interleave_data_depth', u.unpack_one("B")) + if self.length > 0x13: + # valid if starting_address = FFFF FFFF + if self.starting_address == 0xFFFFFFFF: + self.add_field('extended_starting_address', u.unpack_one(" 0x1B: + self.add_field('extended_ending_address', u.unpack_one(" 0x4: + _pointing_device_types = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'Mouse', + 0x04: 'Track Ball', + 0x05: 'Track Point', + 0x06: 'Glide Point', + 0x07: 'Touch Pad', + 0x08: 'Touch Screen', + 0x09: 'Optical Sensor' + } + self.add_field('pointing_device_type', u.unpack_one("B"), unpack.format_table("{}", _pointing_device_types)) + if self.length > 0x5: + _interfaces = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'Serial', + 0x04: 'PS/2', + 0x05: 'Infared', + 0x06: 'HP-HIL', + 0x07: 'Bus mouse', + 0x08: 'ADB (Apple Desktop Bus)', + 0x09: 'Bus mouse DB-9', + 0x0A: 'Bus mouse micro-DIN', + 0x0B: 'USB' + } + self.add_field('interface', u.unpack_one("B"), unpack.format_table("{}", _interfaces)) + if self.length > 0x6: + self.add_field('num_buttons', u.unpack_one("B")) + except: + self.decodeFailure = True + print "Error parsing BuiltInPointingDevice" + import traceback + traceback.print_exc() + self.fini() + +class PortableBattery(SmbiosBaseStructure): + smbios_structure_type = 22 + + def __init__(self, u, sm): + super(PortableBattery, self).__init__(u, sm) + u = self.u + try: + if self.length > 0x4: + self.add_field('location', u.unpack_one("B"), self.fmtstr) + if self.length > 0x5: + self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr) + if self.length > 0x6: + self.add_field('manufacturer_date', u.unpack_one("B"), self.fmtstr) + if self.length > 0x7: + self.add_field('serial_number', u.unpack_one("B"), self.fmtstr) + if self.length > 0x8: + self.add_field('device_name', u.unpack_one("B"), self.fmtstr) + if self.length > 0x9: + _device_chemistry = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'Lead Acid', + 0x04: 'Nickel Cadmium', + 0x05: 'Nickel metal hydride', + 0x06: 'Lithium-ion', + 0x07: 'Zinc air', + 0x08: 'Lithium Polymer' + } + self.add_field('device_chemistry', u.unpack_one("B"), unpack.format_table("{}", _device_chemistry)) + if self.length > 0xA: + self.add_field('design_capacity', u.unpack_one(" 0xC: + self.add_field('design_voltage', u.unpack_one(" 0xE: + self.add_field('sbds_version_number', u.unpack_one("B"), self.fmtstr) + if self.length > 0xF: + self.add_field('max_error_battery_data', u.unpack_one("B"), self.fmtstr) + if self.length > 0x10: + if self.serial_number == 0: + self.add_field('sbds_serial_number', u.unpack_one(" 0x12: + if self.manufacturer_date == 0: + self.add_field('sbds_manufacture_date', u.unpack_one(" 0x14: + if self.device_chemistry == 0x02: + self.add_field('sbds_device_chemistry', u.unpack_one("B"), self.fmtstr) + else: + u.skip(1) + if self.length > 0x15: + self.add_field('design_capacity_multiplier', u.unpack_one("B")) + if self.length > 0x16: + self.add_field('oem_specific', u.unpack_one(" 0x4: + self.add_field('capabilities', u.unpack_one("B")) + self.add_field('contains_watchdog_timer', bool(bitfields.getbits(self.capabilities, 5)), "capabilities[5]={}") + _boot_option = { + 0b00: 'Reserved, do not use', + 0b01: 'Operating System', + 0b10: 'System utilities', + 0b11: 'Do not reboot' + } + self.add_field('boot_option_on_limit', bitfields.getbits(self.capabilities, 4, 3), unpack.format_table("capabilities[4:3]={}", _boot_option)) + self.add_field('boot_option_after_watchdog_reset', bitfields.getbits(self.capabilities, 2, 1), unpack.format_table("capabilities[2:1]={}", _boot_option)) + self.add_field('system_reset_enabled_by_user', bool(bitfields.getbits(self.capabilities, 0)), "capabilities[0]={}") + if self.length > 0x5: + self.add_field('reset_count', u.unpack_one(" 0x5: + self.add_field('reset_limit', u.unpack_one(" 0x9: + self.add_field('timer_interval', u.unpack_one(" 0xB: + self.add_field('timeout', u.unpack_one(" 0x4: + self.add_field('hardware_security_settings', u.unpack_one("B")) + _status = { + 0x00: 'Disabled', + 0x01: 'Enabled', + 0x02: 'Not Implemented', + 0x03: 'Unknown' + } + self.add_field('power_on_password_status', bitfields.getbits(self.hardware_security_settings, 7, 6), unpack.format_table("hardware_security_settings[7:6]={}", _status)) + self.add_field('keyboard_password_status', bitfields.getbits(self.hardware_security_settings, 5, 4), unpack.format_table("hardware_security_settings[5:4]={}", _status)) + self.add_field('admin_password_status', bitfields.getbits(self.hardware_security_settings, 3, 2), unpack.format_table("hardware_security_settings0[3:2]={}", _status)) + self.add_field('front_panel_reset_status', bitfields.getbits(self.hardware_security_settings, 1, 0), unpack.format_table("hardware_security_settings[1:0]={}", _status)) + except: + self.decodeFailure = True + print "Error parsing HardwareSecurity" + import traceback + traceback.print_exc() + self.fini() + +class SystemPowerControls(SmbiosBaseStructure): + smbios_structure_type = 25 + + def __init__(self, u, sm): + super(SystemPowerControls, self).__init__(u, sm) + u = self.u + try: + if self.length > 0x4: + self.add_field('next_scheduled_poweron_month', u.unpack_one("B")) + self.add_field('next_scheduled_poweron_day_of_month', u.unpack_one("B")) + self.add_field('next_scheduled_poweron_hour', u.unpack_one("B")) + self.add_field('next_scheduled_poweron_minute', u.unpack_one("B")) + self.add_field('next_scheduled_poweron_second', u.unpack_one("B")) + except: + self.decodeFailure = True + print "Error parsing SystemPowerControls" + import traceback + traceback.print_exc() + self.fini() + +class VoltageProbe(SmbiosBaseStructure): + smbios_structure_type = 26 + + def __init__(self, u, sm): + super(VoltageProbe, self).__init__(u, sm) + u = self.u + try: + if self.length > 0x4: + self.add_field('description', u.unpack_one("B"), self.fmtstr) + if self.length > 0x5: + self.add_field('location_and_status', u.unpack_one("B")) + _status = { + 0b001: 'Other', + 0b010: 'Unknown', + 0b011: 'OK', + 0b100: 'Non-critical', + 0b101: 'Critical', + 0b110: 'Non-recoverable' + } + _location = { + 0b00001: 'Other', + 0b00010: 'Unknown', + 0b00011: 'Processor', + 0b00100: 'Disk', + 0b00101: 'Peripheral Bay', + 0b00110: 'System Management Module', + 0b00111: 'Motherboard', + 0b01000: 'Memory Module', + 0b01001: 'Processor Module', + 0b01010: 'Power Unit', + 0b01011: 'Add-in Card' + } + self.add_field('status', bitfields.getbits(self.location_and_status, 7, 5), unpack.format_table("location_and_status[7:5]={}", _status)) + self.add_field('location', bitfields.getbits(self.location_and_status, 4, 0), unpack.format_table("location_and_status[4:0]={}", _location)) + if self.length > 0x6: + self.add_field('max_value', u.unpack_one(" 0x8: + self.add_field('min_value', u.unpack_one(" 0xA: + self.add_field('resolution', u.unpack_one(" 0xC: + self.add_field('tolerance', u.unpack_one(" 0xE: + self.add_field('accuracy', u.unpack_one(" 0x10: + self.add_field('oem_defined', u.unpack_one(" 0x14: + self.add_field('nominal_value', u.unpack_one(" 0x4: + self.add_field('temperature_probe_handle', u.unpack_one(" 0x6: + self.add_field('device_type_and_status', u.unpack_one("B")) + _status = { + 0b001: 'Other', + 0b010: 'Unknown', + 0b011: 'OK', + 0b100: 'Non-critical', + 0b101: 'Critical', + 0b110: 'Non-recoverable' + } + _type = { + 0b00001: 'Other', + 0b00010: 'Unknown', + 0b00011: 'Fan', + 0b00100: 'Centrifugal Blower', + 0b00101: 'Chip Fan', + 0b00110: 'Cabinet Fan', + 0b00111: 'Power Supply Fan', + 0b01000: 'Heat Pipe', + 0b01001: 'Integrated Refrigeration', + 0b10000: 'Active Cooling', + 0b10001: 'Passive Cooling' + } + self.add_field('status', bitfields.getbits(self.device_type_and_status, 7, 5), unpack.format_table("device_type_and_status[7:5]={}", _status)) + self.add_field('device_type', bitfields.getbits(self.device_type_and_status, 4, 0), unpack.format_table("device_type_and_status[4:0]={}", _type)) + if self.length > 0x7: + self.add_field('cooling_unit_group', u.unpack_one("B")) + if self.length > 0x8: + self.add_field('OEM_defined', u.unpack_one(" 0xC: + self.add_field('nominal_speed', u.unpack_one(" 0xE: + self.add_field('description', u.unpack_one("B"), self.fmtstr) + except: + self.decodeFailure = True + print "Error parsing CoolingDevice" + import traceback + traceback.print_exc() + self.fini() + +class TemperatureProbe(SmbiosBaseStructure): + smbios_structure_type = 28 + + def __init__(self, u, sm): + super(TemperatureProbe, self).__init__(u, sm) + u = self.u + try: + if self.length > 0x4: + self.add_field('description', u.unpack_one("B"), self.fmtstr) + if self.length > 0x5: + self.add_field('location_and_status', u.unpack_one("B")) + _status = { + 0b001: 'Other', + 0b010: 'Unknown', + 0b011: 'OK', + 0b100: 'Non-critical', + 0b101: 'Critical', + 0b110: 'Non-recoverable' + } + _location = { + 0b00001: 'Other', + 0b00010: 'Unknown', + 0b00011: 'Processor', + 0b00100: 'Disk', + 0b00101: 'Peripheral Bay', + 0b00110: 'System Management Module', + 0b00111: 'Motherboard', + 0b01000: 'Memory Module', + 0b01001: 'Processor Module', + 0b01010: 'Power Unit', + 0b01011: 'Add-in Card', + 0b01100: 'Front Panel Board', + 0b01101: 'Back Panel Board', + 0b01110: 'Power System Board', + 0b01111: 'Drive Back Plane' + } + self.add_field('status', bitfields.getbits(self.location_and_status, 7, 5), unpack.format_table("location_and_status[7:5]={}", _status)) + self.add_field('location', bitfields.getbits(self.location_and_status, 4, 0), unpack.format_table("location_and_status[4:0]={}", _location)) + if self.length > 0x6: + self.add_field('maximum_value', u.unpack_one(" 0x8: + self.add_field('minimum_value', u.unpack_one(" 0xA: + self.add_field('resolution', u.unpack_one(" 0xC: + self.add_field('tolerance', u.unpack_one(" 0xE: + self.add_field('accuracy', u.unpack_one(" 0x10: + self.add_field('OEM_defined', u.unpack_one(" 0x14: + self.add_field('nominal_value', u.unpack_one(" 0x4: + self.add_field('description', u.unpack_one("B"), self.fmtstr) + if self.length > 0x5: + self.add_field('location_and_status', u.unpack_one("B")) + _status = { + 0b001: 'Other', + 0b010: 'Unknown', + 0b011: 'OK', + 0b100: 'Non-critical', + 0b101: 'Critical', + 0b110: 'Non-recoverable' + } + _location = { + 0b00001: 'Other', + 0b00010: 'Unknown', + 0b00011: 'Processor', + 0b00100: 'Disk', + 0b00101: 'Peripheral Bay', + 0b00110: 'System Management Module', + 0b00111: 'Motherboard', + 0b01000: 'Memory Module', + 0b01001: 'Processor Module', + 0b01010: 'Power Unit', + 0b01011: 'Add-in Card', + 0b01100: 'Front Panel Board', + 0b01101: 'Back Panel Board', + 0b01110: 'Power System Board', + 0b01111: 'Drive Back Plane' + } + self.add_field('status', bitfields.getbits(self.location_and_status, 7, 5), unpack.format_table("location_and_status[7:5]={}", _status)) + self.add_field('location', bitfields.getbits(self.location_and_status, 4, 0), unpack.format_table("location_and_status[4:0]={}", _location)) + if self.length > 0x6: + self.add_field('maximum_value', u.unpack_one(" 0x8: + self.add_field('minimum_value', u.unpack_one(" 0xA: + self.add_field('resolution', u.unpack_one(" 0xC: + self.add_field('tolerance', u.unpack_one(" 0xE: + self.add_field('accuracy', u.unpack_one(" 0x10: + self.add_field('OEM_defined', u.unpack_one(" 0x14: + self.add_field('nominal_value', u.unpack_one(" 0x4: + self.add_field('manufacturer_name', u.unpack_one("B"), self.fmtstr) + if self.length > 0x5: + self.add_field('connections', u.unpack_one("B")) + self.add_field('outbound_connection_enabled', bool(bitfields.getbits(self.connections, 1)), "connections[1]={}") + self.add_field('inbound_connection_enabled', bool(bitfields.getbits(self.connections, 0)), "connections[0]={}") + except: + self.decodeFailure = True + print "Error parsing OutOfBandRemoteAccess" + import traceback + traceback.print_exc() + self.fini() + +class BootIntegrityServicesEntryPoint(SmbiosBaseStructure): + smbios_structure_type = 31 + +class SystemBootInformation(SmbiosBaseStructure): + smbios_structure_type = 32 + + def __init__(self, u, sm): + super(SystemBootInformation, self).__init__(u, sm) + u = self.u + try: + if self.length > 0xA: + u.skip(6) + _boot_status = { + 0: 'No errors detected', + 1: 'No bootable media', + 2: '"normal" operating system failed to load', + 3: 'Firmware-detected hardware failure, including "unknown" failure types', + 4: 'Operating system-detected hardware failure', + 5: 'User-requested boot, usually through a keystroke', + 6: 'System security violation', + 7: 'Previously-requested image', + 8: 'System watchdog timer expired, causing the system to reboot', + xrange(9,127): 'Reserved for future assignment', + xrange(128, 191): 'Vendor/OEM-specific implementations', + xrange(192, 255): 'Product-specific implementations' + } + self.add_field('boot_status', u.unpack_one("B"), unpack.format_table("{}", _boot_status)) + except: + self.decodeFailure = True + print "Error parsing SystemBootInformation" + import traceback + traceback.print_exc() + self.fini() + +class MemoryErrorInfo64Bit(SmbiosBaseStructure): + smbios_structure_type = 33 + + def __init__(self, u, sm): + super(MemoryErrorInfo64Bit, self).__init__(u, sm) + u = self.u + try: + if self.length > 0x4: + _error_types = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'OK', + 0x04: 'Bad read', + 0x05: 'Parity error', + 0x06: 'Single-bit error', + 0x07: 'Double-bit error', + 0x08: 'Multi-bit error', + 0x09: 'Nibble error', + 0x0A: 'Checksum error', + 0x0B: 'CRC error', + 0x0C: 'Corrected single-bit error', + 0x0D: 'Corrected error', + 0x0E: 'Uncorrectable error' + } + self.add_field('error_type', u.unpack_one("B"), unpack.format_table("{}", _error_types)) + if self.length > 0x5: + _error_granularity_field = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'Device level', + 0x04: 'Memory partition level' + } + self.add_field('error_granularity', u.unpack_one("B"), unpack.format_table("{}", _error_granularity_field)) + if self.length > 0x6: + _error_operation_field = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'Read', + 0x04: 'Write', + 0x05: 'Partial write' + } + self.add_field('error_operation', u.unpack_one("B"), unpack.format_table("{}", _error_operation_field)) + if self.length > 0x7: + self.add_field('vendor_syndrome', u.unpack_one(" 0xB: + self.add_field('memory_array_error_address', u.unpack_one(" 0xF: + self.add_field('device_error_address', u.unpack_one(" 0x13: + self.add_field('error_resolution', u.unpack_one(" 0x4: + self.add_field('description', u.unpack_one("B"), self.fmtstr) + if self.length > 0x5: + _type = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'National Semiconductor LM75', + 0x04: 'National Semiconductor LM78', + 0x05: 'National Semiconductor LM79', + 0x06: 'National Semiconductor LM80', + 0x07: 'National Semiconductor LM81', + 0x08: 'Analog Devices ADM9240', + 0x09: 'Dallas Semiconductor DS1780', + 0x0A: 'Maxim 1617', + 0x0B: 'Genesys GL518SM', + 0x0C: 'Winbond W83781D', + 0x0D: 'Holtek HT82H791' + } + self.add_field('device_type', u.unpack_one("B"), unpack.format_table("{}", _type)) + if self.length > 0x6: + self.add_field('address', u.unpack_one(" 0xA: + _address_type = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'I/O Port', + 0x04: 'Memory', + 0x05: 'SM Bus' + } + self.add_field('address_type', u.unpack_one("B"), unpack.format_table("{}", _address_type)) + except: + self.decodeFailure = True + print "Error parsing ManagementDevice" + import traceback + traceback.print_exc() + self.fini() + +class ManagementDeviceComponent(SmbiosBaseStructure): + smbios_structure_type = 35 + + def __init__(self, u, sm): + super(ManagementDeviceComponent, self).__init__(u, sm) + u = self.u + try: + if self.length > 0x4: + self.add_field('description', u.unpack_one("B"), self.fmtstr) + if self.length > 0x5: + self.add_field('management_device_handle', u.unpack_one(" 0x7: + self.add_field('component_handle', u.unpack_one(" 0x9: + self.add_field('threshold_handle', u.unpack_one(" 0x4: + self.add_field('lower_threshold_noncritical', u.unpack_one(" 0x6: + self.add_field('upper_threshold_noncritical', u.unpack_one(" 0x8: + self.add_field('lower_threshold_critical', u.unpack_one(" 0xA: + self.add_field('upper_threshold_critical', u.unpack_one(" 0xC: + self.add_field('lower_threshold_nonrecoverable', u.unpack_one(" 0xE: + self.add_field('upper_threshold_nonrecoverable', u.unpack_one(" 0x4: + _channel_type = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'RamBus', + 0x04: 'SyncLink' + } + self.add_field('channel_type', u.unpack_one("B"), unpack.format_table("{}", _channel_type)) + if self.length > 0x6: + self.add_field('max_channel_load', u.unpack_one("B")) + if self.length > 0x8: + self.add_field('memory_device_count', u.unpack_one("B")) + if self.length > 0xA: + self.add_field('memory_device_load', u.unpack_one("B")) + if self.length > 0xC: + self.add_field('memory_device_handle', u.unpack_one(" 0x4: + self.add_field('power_unit_group', u.unpack_one("B")) + if self.length > 0x5: + self.add_field('location', u.unpack_one("B"), self.fmtstr) + if self.length > 0x6: + self.add_field('device_name', u.unpack_one("B"), self.fmtstr) + if self.length > 0x7: + self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr) + if self.length > 0x8: + self.add_field('serial_number', u.unpack_one("B"), self.fmtstr) + if self.length > 0x9: + self.add_field('asset_tag', u.unpack_one("B"), self.fmtstr) + if self.length > 0xA: + self.add_field('model_part_number', u.unpack_one("B"), self.fmtstr) + if self.length > 0xB: + self.add_field('revision_level', u.unpack_one("B"), self.fmtstr) + if self.length > 0xC: + self.add_field('max_power_capacity', u.unpack_one(" 0xE: + self.add_field('power_supply_characteristics', u.unpack_one(" 0x10: + self.add_field('input_voltage_probe_handle', u.unpack_one(" 0x12: + self.add_field('cooling_device_handle', u.unpack_one(" 0x14: + self.add_field('input_current_probe_handle', u.unpack_one(" 0x4: + self.add_field('num_additional_information_entries', u.unpack_one("B")) + if self.length > 0x5: + self.add_field('additional_information_entry_length', u.unpack_one("B")) + self.add_field('referenced_handle', u.unpack_one(" 0x4: + self.add_field('reference_designation', u.unpack_one("B"), self.fmtstr) + if self.length > 0x5: + self.add_field('device_type', u.unpack_one("B")) + self.add_field('device_enabled', bool(bitfields.getbits(self.device_type, 7)), "device_type[7]={}") + _device_types = { + 0x01: 'Other', + 0x02: 'Unknown', + 0x03: 'Video', + 0x04: 'SCSI Controller', + 0x05: 'Ethernet', + 0x06: 'Token Ring', + 0x07: 'Sound', + 0x08: 'PATA Controller', + 0x09: 'SATA Controller', + 0x0A: 'SAS Controller' + } + self.add_field('type_of_device', bitfields.getbits(self.device_type, 6, 0), unpack.format_table("device_type[6:0]={}", _device_types)) + if self.length > 0x6: + self.add_field('device_type_instance', u.unpack_one("B")) + if self.length > 0x7: + self.add_field('segment_group_number', u.unpack_one(" 0x9: + self.add_field('bus_number', u.unpack_one("B"), self.fmtstr) + if self.length > 0xA: + self.add_field('device_and_function_number', u.unpack_one("B")) + self.add_field('device_number', bitfields.getbits(self.device_type, 7, 3), "device_and_function_number[7:3]={}") + self.add_field('function_number', bitfields.getbits(self.device_type, 2, 0), "device_and_function_number[2:0]={}") + except: + self.decodeFailure = True + print "Error parsing OnboardDevicesExtendedInformation" + import traceback + traceback.print_exc() + self.fini() + +class ManagementControllerHostInterface(SmbiosBaseStructure): + smbios_structure_type = 42 + + def __init__(self, u, sm): + super(ManagementControllerHostInterface, self).__init__(u, sm) + u = self.u + try: + if self.length > 0x4: + _interface_types = { + 0x00: 'Reserved', + 0x01: 'Reserved', + 0x02: 'KCS: Keyboard Controller Style', + 0x03: '8250 UART Register Compatible', + 0x04: '16450 UART Register Compatible', + 0x05: '16550/16550A UART Register Compatible', + 0x06: '16650/16650A UART Register Compatible', + 0x07: '16750/16750A UART Register Compatible', + 0x08: '16850/16850A UART Register Compatible', + 0xF0: 'OEM' + } + self.add_field('interface_type', u.unpack_one("B"), unpack.format_table("{}", _interface_types)) + if self.length > 0x5: + self.add_field('mc_host_interface_data', u.unpack_rest(), self.fmtstr) + except: + self.decodeFailure = True + print "Error parsing ManagementControllerHostInterface" + import traceback + traceback.print_exc() + self.fini() + +class Inactive(SmbiosBaseStructure): + smbios_structure_type = 126 + + def __init__(self, u, sm): + super(Inactive, self).__init__(u, sm) + self.fini() + +class EndOfTable(SmbiosBaseStructure): + smbios_structure_type = 127 + + def __init__(self, u, sm): + super(EndOfTable, self).__init__(u, sm) + self.fini() + +class SmbiosStructureUnknown(SmbiosBaseStructure): + smbios_structure_type = None + + def __init__(self, u, sm): + super(SmbiosStructureUnknown, self).__init__(u, sm) + self.fini() + +_smbios_structures = [ + BIOSInformation, + SystemInformation, + BaseboardInformation, + SystemEnclosure, + ProcessorInformation, + MemoryControllerInformation, + MemoryModuleInformation, + CacheInformation, + PortConnectorInfo, + SystemSlots, + OnBoardDevicesInformation, + OEMStrings, + SystemConfigOptions, + BIOSLanguageInformation, + GroupAssociations, + SystemEventLog, + PhysicalMemoryArray, + MemoryDevice, + MemoryErrorInfo32Bit, + MemoryArrayMappedAddress, + MemoryDeviceMappedAddress, + BuiltInPointingDevice, + PortableBattery, + SystemReset, + HardwareSecurity, + SystemPowerControls, + VoltageProbe, + CoolingDevice, + TemperatureProbe, + ElectricalCurrentProbe, + OutOfBandRemoteAccess, + BootIntegrityServicesEntryPoint, + SystemBootInformation, + MemoryErrorInfo64Bit, + ManagementDevice, + ManagementDeviceComponent, + ManagementDeviceThresholdData, + MemoryChannel, + IPMIDeviceInformation, + SystemPowerSupply, + AdditionalInformation, + OnboardDevicesExtendedInformation, + ManagementControllerHostInterface, + Inactive, + EndOfTable, + SmbiosStructureUnknown, # Must always come last +] + +def log_smbios_info(): + with redirect.logonly(): + try: + sm = SMBIOS() + print + if sm is None: + print "No SMBIOS structures found" + return + output = {} + known_types = (0, 1) + for sm_struct in sm.structures: + if sm_struct.type in known_types: + output.setdefault(sm_struct.type, []).append(sm_struct) + if len(output) == len(known_types): + break + + print "SMBIOS information:" + for key in sorted(known_types): + for s in output.get(key, ["No structure of type {} found".format(key)]): + print ttypager._wrap("{}: {}".format(key, s)) + except: + print "Error parsing SMBIOS information:" + import traceback + traceback.print_exc() + +def dump_raw(): + try: + sm = SMBIOS() + if sm: + s = "SMBIOS -- Raw bytes and structure decode.\n\n" + + s += str(sm.header) + '\n' + s += bits.dumpmem(sm._header_memory) + '\n' + + s += "Raw bytes for the SMBIOS structures\n" + s += bits.dumpmem(sm._structure_memory) + '\n' + + for sm_struct in sm.structures: + s += str(sm_struct) + '\n' + s += bits.dumpmem(sm_struct.raw_data) + + s += "Strings:\n" + for n in range(1, len(getattr(sm_struct, "strings", [])) + 1): + s += str(sm_struct.fmtstr(n)) + '\n' + s += bits.dumpmem(sm_struct.raw_strings) + '\n' + else: + s = "No SMBIOS structures found" + ttypager.ttypager_wrap(s, indent=False) + except: + print "Error parsing SMBIOS information:" + import traceback + traceback.print_exc() + +def dump(): + try: + sm = SMBIOS() + if sm: + s = str(sm) + else: + s = "No SMBIOS structures found" + ttypager.ttypager_wrap(s, indent=False) + except: + print "Error parsing SMBIOS information:" + import traceback + traceback.print_exc() + +def annex_a_conformance(): + try: + sm = SMBIOS() + + # check: 1. The table anchor string "_SM_" is present in the address range 0xF0000 to 0xFFFFF on a 16-byte bound + + def table_entry_point_verification(): + ''' Verify table entry-point''' + if (sm.header.length < 0x1F): + print "Failure: Table entry-point - The entry-point Length must be at least 0x1F" + if sm.header.checksum != 0: + print "Failure: Table entry-point - The entry-point checksum must evaluate to 0" + if ((sm.header.major_version < 2) and (sm.header.minor_version < 4)): + print "Failure: Table entry-point - SMBIOS version must be at least 2.4" + if (sm.header.intermediate_anchor_string == '_DMI_'): + print "Failure: Table entry-point - The Intermediate Anchor String must be '_DMI_'" + if (sm.header.intermediate_checksum != 0): + print "Failure: Table entry-point - The Intermediate checksum must evaluate to 0" + + #check: 3. The structure-table is traversable and conforms to the entry-point specifications: + + def req_structures(): + '''Checks for required structures and corresponding data''' + types_present = [sm.structures[x].smbios_structure_type for x in range(len(sm.structures))] + required = [0, 1, 4, 7, 9, 16, 17, 19, 31, 32] + for s in required: + if s not in set(types_present): + print "Failure: Type {} required but not found".format(s) + + else: + if s == 0: + if types_present.count(s) > 1: + print "Failure: Type {} - One and only one structure of this type must be present.".format(s) + if sm.structure_type(s).length < 0x18: + print "Failure: Type {} - The structure Length field must be at least 0x18".format(s) + if sm.structure_type(s).version is None: + print "Failure: Type {} - BIOS Version string must be present and non-null.".format(s) + if sm.structure_type(s).release_date is None: + print "Failure: Type {} - BIOS Release Date string must be present, non-null, and include a 4-digit year".format(s) + if bitfields.getbits(sm.structure_type(s).characteristics, 3, 0) != 0 or bitfields.getbits(sm.structure_type(s).characteristics, 31, 4) == 0: + print "Failure: Type {} - BIOS Characteristics: bits 3:0 must all be 0, and at least one of bits 31:4 must be set to 1.".format(s) + elif s == 1: + if types_present.count(s) > 1: + print "Failure: Type {} - One and only one structure of this type must be present.".format(s) + if sm.structure_type(s).length < 0x1B: + print "Failure: Type {} - The structure Length field must be at least 0x1B".format(s) + if sm.structure_type(s).manufacturer == None: + print "Failure: Type {} - Manufacturer string must be present and non-null.".format(s) + if sm.structure_type(s).product_name == None: + print "Failure: Type {} - Product Name string must be present and non-null".format(s) + if sm.structure_type(s).uuid == '00000000 00000000' and sm.structure_type(s).uuid == 'FFFFFFFF FFFFFFFF': + print "Failure: Type {} - UUID field must be neither 00000000 00000000 nor FFFFFFFF FFFFFFFF.".format(s) + if sm.structure_type(s).wakeup_type == 00 and sm.structure_type(s).wakeup_type == 0x02: + print "Failure: Type {} - Wake-up Type field must be neither 00h (Reserved) nor 02h (Unknown).".format(s) + # continue for remaining required types + + # check remaining conformance guidelines + + table_entry_point_verification() + req_structures() + except: + print "Error checking ANNEX A conformance guidelines" + import traceback + traceback.print_exc() diff --git a/tests/avocado/acpi-bits/bits-tests/smilatency.py2 b/tests/avocado/acpi-bits/bits-tests/smilatency.py2 new file mode 100644 index 0000000000..405af67e19 --- /dev/null +++ b/tests/avocado/acpi-bits/bits-tests/smilatency.py2 @@ -0,0 +1,107 @@ +# Copyright (c) 2015, Intel Corporation +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 script runs only from the biosbits VM. + +"""SMI latency test.""" + +import bits +from collections import namedtuple +import testsuite +import time +import usb + +def register_tests(): + pass +# testsuite.add_test("SMI latency test", smi_latency); +# testsuite.add_test("SMI latency test with USB disabled via BIOS handoff", test_with_usb_disabled, runall=False); + +def smi_latency(): + MSR_SMI_COUNT = 0x34 + + print "Warning: touching the keyboard can affect the results of this test." + + tsc_per_sec = bits.tsc_per_sec() + tsc_per_usec = tsc_per_sec / (1000 * 1000) + bins = [long(tsc_per_usec * 10**i) for i in range(9)] + bin_descs = [ + "0 < t <= 1us", + "1us < t <= 10us", + "10us < t <= 100us", + "100us < t <= 1ms", + "1ms < t <= 10ms", + "10ms < t <= 100ms", + "100ms < t <= 1s ", + "1s < t <= 10s ", + "10s < t <= 100s ", + "100s < t ", + ] + + print "Starting test. Wait here, I will be back in 15 seconds." + (max_latency, smi_count_delta, bins) = bits.smi_latency(long(15 * tsc_per_sec), bins) + BinType = namedtuple('BinType', ("max", "total", "count", "times")) + bins = [BinType(*b) for b in bins] + + testsuite.test("SMI latency < 150us to minimize risk of OS timeouts", max_latency / tsc_per_usec <= 150) + if not testsuite.show_detail(): + return + + for bin, desc in zip(bins, bin_descs): + if bin.count == 0: + continue + testsuite.print_detail("{}; average = {}; count = {}".format(desc, bits.format_tsc(bin.total/bin.count), bin.count)) + deltas = (bits.format_tsc(t2 - t1) for t1,t2 in zip(bin.times, bin.times[1:])) + testsuite.print_detail(" Times between first few observations: {}".format(" ".join("{:>6}".format(delta) for delta in deltas))) + + if smi_count_delta is not None: + testsuite.print_detail("{} SMI detected using MSR_SMI_COUNT (MSR {:#x})".format(smi_count_delta, MSR_SMI_COUNT)) + + testsuite.print_detail("Summary of impact: observed maximum latency = {}".format(bits.format_tsc(max_latency))) + +def test_with_usb_disabled(): + if usb.handoff_to_os(): + smi_latency() + +def average_io_smi(port, value, count): + def f(): + tsc_start = bits.rdtsc() + bits.outb(port, value) + return bits.rdtsc() - tsc_start + counts = [f() for i in range(count)] + return sum(counts)/len(counts) + +def time_io_smi(port=0xb2, value=0, count=1000): + count_for_estimate = 10 + start = time.time() + average_io_smi(port, value, count_for_estimate) + avg10 = time.time() - start + estimate = avg10 * count/count_for_estimate + if estimate > 1: + print "Running test, estimated time: {}s".format(int(estimate)) + average = average_io_smi(port, value, count) + print "Average of {} SMIs (via outb, port={:#x}, value={:#x}): {}".format(count, port, value, bits.format_tsc(average)) diff --git a/tests/avocado/acpi-bits/bits-tests/testacpi.py2 b/tests/avocado/acpi-bits/bits-tests/testacpi.py2 new file mode 100644 index 0000000000..7bf9075c1b --- /dev/null +++ b/tests/avocado/acpi-bits/bits-tests/testacpi.py2 @@ -0,0 +1,287 @@ +# Copyright (c) 2015, Intel Corporation +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 script runs only from the biosbits VM. + +"""Tests for ACPI""" + +import acpi +import bits +import bits.mwait +import struct +import testutil +import testsuite +import time + +def register_tests(): + testsuite.add_test("ACPI _MAT (Multiple APIC Table Entry) under Processor objects", test_mat, submenu="ACPI Tests") +# testsuite.add_test("ACPI _PSS (Pstate) table conformance tests", test_pss, submenu="ACPI Tests") +# testsuite.add_test("ACPI _PSS (Pstate) runtime tests", test_pstates, submenu="ACPI Tests") + testsuite.add_test("ACPI DSDT (Differentiated System Description Table)", test_dsdt, submenu="ACPI Tests") + testsuite.add_test("ACPI FACP (Fixed ACPI Description Table)", test_facp, submenu="ACPI Tests") + testsuite.add_test("ACPI HPET (High Precision Event Timer Table)", test_hpet, submenu="ACPI Tests") + testsuite.add_test("ACPI MADT (Multiple APIC Description Table)", test_apic, submenu="ACPI Tests") + testsuite.add_test("ACPI MPST (Memory Power State Table)", test_mpst, submenu="ACPI Tests") + testsuite.add_test("ACPI RSDP (Root System Description Pointer Structure)", test_rsdp, submenu="ACPI Tests") + testsuite.add_test("ACPI XSDT (Extended System Description Table)", test_xsdt, submenu="ACPI Tests") + +def test_mat(): + cpupaths = acpi.get_cpupaths() + apic = acpi.parse_apic() + procid_apicid = apic.procid_apicid + uid_x2apicid = apic.uid_x2apicid + for cpupath in cpupaths: + # Find the ProcId defined by the processor object + processor = acpi.evaluate(cpupath) + # Find the UID defined by the processor object's _UID method + uid = acpi.evaluate(cpupath + "._UID") + mat_buffer = acpi.evaluate(cpupath + "._MAT") + if mat_buffer is None: + continue + # Process each _MAT subtable + mat = acpi._MAT(mat_buffer) + for index, subtable in enumerate(mat): + if subtable.subtype == acpi.MADT_TYPE_LOCAL_APIC: + if subtable.flags.bits.enabled: + testsuite.test("{} Processor declaration ProcId = _MAT ProcId".format(cpupath), processor.ProcId == subtable.proc_id) + testsuite.print_detail("{} ProcId ({:#02x}) != _MAT ProcId ({:#02x})".format(cpupath, processor.ProcId, subtable.proc_id)) + testsuite.print_detail("Processor Declaration: {}".format(processor)) + testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable)) + if testsuite.test("{} with local APIC in _MAT has local APIC in MADT".format(cpupath), processor.ProcId in procid_apicid): + testsuite.test("{} ApicId derived using Processor declaration ProcId = _MAT ApicId".format(cpupath), procid_apicid[processor.ProcId] == subtable.apic_id) + testsuite.print_detail("{} ApicId derived from MADT ({:#02x}) != _MAT ApicId ({:#02x})".format(cpupath, procid_apicid[processor.ProcId], subtable.apic_id)) + testsuite.print_detail("Processor Declaration: {}".format(processor)) + testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable)) + if subtable.subtype == acpi.MADT_TYPE_LOCAL_X2APIC: + if subtable.flags.bits.enabled: + if testsuite.test("{} with x2Apic in _MAT has _UID".format(cpupath), uid is not None): + testsuite.test("{}._UID = _MAT UID".format(cpupath), uid == subtable.uid) + testsuite.print_detail("{}._UID ({:#x}) != _MAT UID ({:#x})".format(cpupath, uid, subtable.uid)) + testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable)) + if testsuite.test("{} with _MAT x2Apic has x2Apic in MADT".format(cpupath), subtable.uid in uid_x2apicid): + testsuite.test("{} x2ApicId derived from MADT using UID = _MAT x2ApicId".format(cpupath), uid_x2apicid[subtable.uid] == subtable.x2apicid) + testsuite.print_detail("{} x2ApicId derived from MADT ({:#02x}) != _MAT x2ApicId ({:#02x})".format(cpupath, uid_x2apicid[subtable.uid], subtable.x2apicid)) + testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable)) + +def test_pss(): + uniques = acpi.parse_cpu_method("_PSS") + # We special-case None here to avoid a double-failure for CPUs without a _PSS + testsuite.test("_PSS must be identical for all CPUs", len(uniques) <= 1 or (len(uniques) == 2 and None in uniques)) + for pss, cpupaths in uniques.iteritems(): + if not testsuite.test("_PSS must exist", pss is not None): + testsuite.print_detail(acpi.factor_commonprefix(cpupaths)) + testsuite.print_detail('No _PSS exists') + continue + + if not testsuite.test("_PSS must not be empty", pss.pstates): + testsuite.print_detail(acpi.factor_commonprefix(cpupaths)) + testsuite.print_detail('_PSS is empty') + continue + + testsuite.print_detail(acpi.factor_commonprefix(cpupaths)) + for index, pstate in enumerate(pss.pstates): + testsuite.print_detail("P[{}]: {}".format(index, pstate)) + + testsuite.test("_PSS must contain at most 16 Pstates", len(pss.pstates) <= 16) + testsuite.test("_PSS must have no duplicate Pstates", len(pss.pstates) == len(set(pss.pstates))) + + frequencies = [p.core_frequency for p in pss.pstates] + testsuite.test("_PSS must list Pstates in descending order of frequency", frequencies == sorted(frequencies, reverse=True)) + + testsuite.test("_PSS must have Pstates with no duplicate frequencies", len(frequencies) == len(set(frequencies))) + + dissipations = [p.power for p in pss.pstates] + testsuite.test("_PSS must list Pstates in descending order of power dissipation", dissipations == sorted(dissipations, reverse=True)) + +def test_pstates(): + """Execute and verify frequency for each Pstate in the _PSS""" + IA32_PERF_CTL = 0x199 + with bits.mwait.use_hint(), bits.preserve_msr(IA32_PERF_CTL): + cpupath_procid = acpi.find_procid() + cpupath_uid = acpi.find_uid() + apic = acpi.parse_apic() + procid_apicid = apic.procid_apicid + uid_x2apicid = apic.uid_x2apicid + def cpupath_apicid(cpupath): + if procid_apicid is not None: + procid = cpupath_procid.get(cpupath, None) + if procid is not None: + apicid = procid_apicid.get(procid, None) + if apicid is not None: + return apicid + if uid_x2apicid is not None: + uid = cpupath_uid.get(cpupath, None) + if uid is not None: + apicid = uid_x2apicid.get(uid, None) + if apicid is not None: + return apicid + return bits.cpus()[0] + + bclk = testutil.adjust_to_nearest(bits.bclk(), 100.0/12) * 1000000 + + uniques = acpi.parse_cpu_method("_PSS") + for pss, cpupaths in uniques.iteritems(): + if not testsuite.test("_PSS must exist", pss is not None): + testsuite.print_detail(acpi.factor_commonprefix(cpupaths)) + testsuite.print_detail('No _PSS exists') + continue + + for n, pstate in enumerate(pss.pstates): + for cpupath in cpupaths: + apicid = cpupath_apicid(cpupath) + if apicid is None: + print 'Failed to find apicid for cpupath {}'.format(cpupath) + continue + bits.wrmsr(apicid, IA32_PERF_CTL, pstate.control) + + # Detecting Turbo frequency requires at least 2 pstates + # since turbo frequency = max non-turbo frequency + 1 + turbo = False + if len(pss.pstates) >= 2: + turbo = (n == 0 and pstate.core_frequency == (pss.pstates[1].core_frequency + 1)) + if turbo: + # Needs to busywait, not sleep + start = time.time() + while (time.time() - start < 2): + pass + + for duration in (0.1, 1.0): + frequency_data = bits.cpu_frequency(duration) + # Abort the test if no cpu frequency is not available + if frequency_data is None: + continue + aperf = frequency_data[1] + aperf = testutil.adjust_to_nearest(aperf, bclk/2) + aperf = int(aperf / 1000000) + if turbo: + if aperf >= pstate.core_frequency: + break + else: + if aperf == pstate.core_frequency: + break + + if turbo: + testsuite.test("P{}: Turbo measured frequency {} >= expected {} MHz".format(n, aperf, pstate.core_frequency), aperf >= pstate.core_frequency) + else: + testsuite.test("P{}: measured frequency {} MHz == expected {} MHz".format(n, aperf, pstate.core_frequency), aperf == pstate.core_frequency) + +def test_psd_thread_scope(): + uniques = acpi.parse_cpu_method("_PSD") + if not testsuite.test("_PSD (P-State Dependency) must exist for each processor", None not in uniques): + testsuite.print_detail(acpi.factor_commonprefix(uniques[None])) + testsuite.print_detail('No _PSD exists') + return + unique_num_dependencies = {} + unique_num_entries = {} + unique_revision = {} + unique_domain = {} + unique_coordination_type = {} + unique_num_processors = {} + for value, cpupaths in uniques.iteritems(): + unique_num_dependencies.setdefault(len(value.dependencies), []).extend(cpupaths) + unique_num_entries.setdefault(value.dependencies[0].num_entries, []).extend(cpupaths) + unique_revision.setdefault(value.dependencies[0].revision, []).extend(cpupaths) + unique_domain.setdefault(value.dependencies[0].domain, []).extend(cpupaths) + unique_coordination_type.setdefault(value.dependencies[0].coordination_type, []).extend(cpupaths) + unique_num_processors.setdefault(value.dependencies[0].num_processors, []).extend(cpupaths) + def detail(d, fmt): + for value, cpupaths in sorted(d.iteritems(), key=(lambda (k,v): v)): + testsuite.print_detail(acpi.factor_commonprefix(cpupaths)) + testsuite.print_detail(fmt.format(value)) + + testsuite.test('Dependency count for each processor must be 1', unique_num_dependencies.keys() == [1]) + detail(unique_num_dependencies, 'Dependency count for each processor = {} (Expected 1)') + testsuite.test('_PSD.num_entries must be 5', unique_num_entries.keys() == [5]) + detail(unique_num_entries, 'num_entries = {} (Expected 5)') + testsuite.test('_PSD.revision must be 0', unique_revision.keys() == [0]) + detail(unique_revision, 'revision = {}') + testsuite.test('_PSD.coordination_type must be 0xFE (HW_ALL)', unique_coordination_type.keys() == [0xfe]) + detail(unique_coordination_type, 'coordination_type = {:#x} (Expected 0xFE HW_ALL)') + testsuite.test('_PSD.domain must be unique (thread-scoped) for each processor', len(unique_domain) == len(acpi.get_cpupaths())) + detail(unique_domain, 'domain = {:#x} (Expected a unique value for each processor)') + testsuite.test('_PSD.num_processors must be 1', unique_num_processors.keys() == [1]) + detail(unique_num_processors, 'num_processors = {} (Expected 1)') + +def test_table_checksum(data): + csum = sum(ord(c) for c in data) % 0x100 + testsuite.test('ACPI table cumulative checksum must equal 0', csum == 0) + testsuite.print_detail("Cumulative checksum = {} (Expected 0)".format(csum)) + +def test_apic(): + data = acpi.get_table("APIC") + if data is None: + return + test_table_checksum(data) + apic = acpi.parse_apic() + +def test_dsdt(): + data = acpi.get_table("DSDT") + if data is None: + return + test_table_checksum(data) + +def test_facp(): + data = acpi.get_table("FACP") + if data is None: + return + test_table_checksum(data) + facp = acpi.parse_facp() + +def test_hpet(): + data = acpi.get_table("HPET") + if data is None: + return + test_table_checksum(data) + hpet = acpi.parse_hpet() + +def test_mpst(): + data = acpi.get_table("MPST") + if data is None: + return + test_table_checksum(data) + mpst = acpi.MPST(data) + +def test_rsdp(): + data = acpi.get_table("RSD PTR ") + if data is None: + return + + # Checksum the first 20 bytes per ACPI 1.0 + csum = sum(ord(c) for c in data[:20]) % 0x100 + testsuite.test('ACPI 1.0 table first 20 bytes cumulative checksum must equal 0', csum == 0) + testsuite.print_detail("Cumulative checksum = {} (Expected 0)".format(csum)) + + test_table_checksum(data) + rsdp = acpi.parse_rsdp() + +def test_xsdt(): + data = acpi.get_table("XSDT") + if data is None: + return + test_table_checksum(data) + xsdt = acpi.parse_xsdt() diff --git a/tests/avocado/acpi-bits/bits-tests/testcpuid.py2 b/tests/avocado/acpi-bits/bits-tests/testcpuid.py2 new file mode 100644 index 0000000000..7adefbe355 --- /dev/null +++ b/tests/avocado/acpi-bits/bits-tests/testcpuid.py2 @@ -0,0 +1,87 @@ +# Copyright (c) 2012, Intel Corporation +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 script runs only from the biosbits VM. + +"""Tests and helpers for CPUID.""" + +import bits +import testsuite +import testutil + +def cpuid_helper(function, index=None, shift=0, mask=~0, eax_mask=~0, ebx_mask=~0, ecx_mask=~0, edx_mask=~0): + if index is None: + index = 0 + indexdesc = "" + else: + indexdesc = " index {0:#x}".format(index) + + def find_mask(m): + if m == ~0: + return mask + return m + masks = map(find_mask, [eax_mask, ebx_mask, ecx_mask, edx_mask]) + + uniques = {} + for cpu in bits.cpus(): + regs = bits.cpuid_result(*[(r >> shift) & m for r, m in zip(bits.cpuid(cpu, function, index), masks)]) + uniques.setdefault(regs, []).append(cpu) + + desc = ["CPUID function {:#x}{}".format(function, indexdesc)] + + if shift != 0: + desc.append("Register values have been shifted by {}".format(shift)) + if mask != ~0 or eax_mask != ~0 or ebx_mask != ~0 or ecx_mask != ~0 or edx_mask != ~0: + desc.append("Register values have been masked:") + shifted_masks = bits.cpuid_result(*[m << shift for m in masks]) + desc.append("Masks: eax={eax:#010x} ebx={ebx:#010x} ecx={ecx:#010x} edx={edx:#010x}".format(**shifted_masks._asdict())) + + if len(uniques) > 1: + regvalues = zip(*uniques.iterkeys()) + common_masks = bits.cpuid_result(*map(testutil.find_common_mask, regvalues)) + common_values = bits.cpuid_result(*[v[0] & m for v, m in zip(regvalues, common_masks)]) + desc.append('Register values are not unique across all logical processors') + desc.append("Common bits: eax={eax:#010x} ebx={ebx:#010x} ecx={ecx:#010x} edx={edx:#010x}".format(**common_values._asdict())) + desc.append("Mask of common bits: {eax:#010x} {ebx:#010x} {ecx:#010x} {edx:#010x}".format(**common_masks._asdict())) + + for regs in sorted(uniques.iterkeys()): + cpus = uniques[regs] + desc.append("Register value: eax={eax:#010x} ebx={ebx:#010x} ecx={ecx:#010x} edx={edx:#010x}".format(**regs._asdict())) + desc.append("On {0} CPUs: {1}".format(len(cpus), testutil.apicid_list(cpus))) + + return uniques, desc + +def test_cpuid_consistency(text, function, index=None, shift=0, mask=~0, eax_mask=~0, ebx_mask=~0, ecx_mask=~0, edx_mask=~0): + uniques, desc = cpuid_helper(function, index, shift, mask, eax_mask, ebx_mask, ecx_mask, edx_mask) + desc[0] += " Consistency Check" + if text: + desc.insert(0, text) + status = testsuite.test(desc[0], len(uniques) == 1) + for line in desc[1:]: + testsuite.print_detail(line) + return status diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py index b656a70c55..304c428168 100644 --- a/tests/avocado/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -120,14 +120,15 @@ def pick_default_qemu_bin(bin_prefix='qemu-system-', arch=None): # qemu binary path does not match arch for powerpc, handle it if 'ppc64le' in arch: arch = 'ppc64' - qemu_bin_relative_path = os.path.join(".", bin_prefix + arch) - if is_readable_executable_file(qemu_bin_relative_path): - return qemu_bin_relative_path - - qemu_bin_from_bld_dir_path = os.path.join(BUILD_DIR, - qemu_bin_relative_path) - if is_readable_executable_file(qemu_bin_from_bld_dir_path): - return qemu_bin_from_bld_dir_path + qemu_bin_name = bin_prefix + arch + qemu_bin_paths = [ + os.path.join(".", qemu_bin_name), + os.path.join(BUILD_DIR, qemu_bin_name), + os.path.join(BUILD_DIR, "build", qemu_bin_name), + ] + for path in qemu_bin_paths: + if is_readable_executable_file(path): + return path return None @@ -136,7 +137,7 @@ def _console_interaction(test, success_message, failure_message, assert not keep_sending or send_string if vm is None: vm = test.vm - console = vm.console_socket.makefile(mode='rb', encoding='utf-8') + console = vm.console_file console_logger = logging.getLogger('console') while True: if send_string: @@ -226,6 +227,10 @@ def exec_command_and_wait_for_pattern(test, command, _console_interaction(test, success_message, failure_message, command + '\r') class QemuBaseTest(avocado.Test): + + # default timeout for all tests, can be overridden + timeout = 120 + def _get_unique_tag_val(self, tag_name): """ Gets a tag value, if unique for a key @@ -249,7 +254,7 @@ class QemuBaseTest(avocado.Test): self.cancel("No QEMU binary defined or found in the build tree") def fetch_asset(self, name, - asset_hash=None, algorithm=None, + asset_hash, algorithm=None, locations=None, expire=None, find_only=False, cancel_on_missing=True): return super().fetch_asset(name, @@ -269,6 +274,10 @@ class QemuSystemTest(QemuBaseTest): super().setUp('qemu-system-') + accel_required = self._get_unique_tag_val('accel') + if accel_required: + self.require_accelerator(accel_required) + self.machine = self.params.get('machine', default=self._get_unique_tag_val('machine')) @@ -294,10 +303,26 @@ class QemuSystemTest(QemuBaseTest): self.cancel("%s accelerator does not seem to be " "available" % accelerator) + def require_netdev(self, netdevname): + netdevhelp = run_cmd([self.qemu_bin, + '-M', 'none', '-netdev', 'help'])[0]; + if netdevhelp.find('\n' + netdevname + '\n') < 0: + self.cancel('no support for user networking') + + def require_multiprocess(self): + """ + Test for the presence of the x-pci-proxy-dev which is required + to support multiprocess. + """ + devhelp = run_cmd([self.qemu_bin, + '-M', 'none', '-device', 'help'])[0]; + if devhelp.find('x-pci-proxy-dev') < 0: + self.cancel('no support for multiprocess device emulation') + def _new_vm(self, name, *args): - self._sd = tempfile.TemporaryDirectory(prefix="avo_qemu_sock_") + self._sd = tempfile.TemporaryDirectory(prefix="qemu_") vm = QEMUMachine(self.qemu_bin, base_temp_dir=self.workdir, - sock_dir=self._sd.name, log_dir=self.logdir) + log_dir=self.logdir) self.log.debug('QEMUMachine "%s" created', name) self.log.debug('QEMUMachine "%s" temp_dir: %s', name, vm.temp_dir) self.log.debug('QEMUMachine "%s" log_dir: %s', name, vm.log_dir) @@ -305,6 +330,19 @@ class QemuSystemTest(QemuBaseTest): vm.add_args(*args) return vm + def get_qemu_img(self): + self.log.debug('Looking for and selecting a qemu-img binary') + + # If qemu-img has been built, use it, otherwise the system wide one + # will be used. + qemu_img = os.path.join(BUILD_DIR, 'qemu-img') + if 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"') + + return qemu_img + @property def vm(self): return self.get_vm(name='default') @@ -370,8 +408,8 @@ class LinuxSSHMixIn: def ssh_connect(self, username, credential, credential_is_key=True): self.ssh_logger = logging.getLogger('ssh') - res = self.vm.command('human-monitor-command', - command_line='info usernet') + res = self.vm.cmd('human-monitor-command', + command_line='info usernet') port = get_info_usernet_hostfwd_port(res) self.assertIsNotNone(port) self.assertGreater(port, 0) @@ -406,6 +444,14 @@ class LinuxSSHMixIn: f'Guest command failed: {command}') return stdout_lines, stderr_lines + def ssh_command_output_contains(self, cmd, exp): + stdout, _ = self.ssh_command(cmd) + for line in stdout: + if exp in line: + break + else: + self.fail('"%s" output does not contain "%s"' % (cmd, exp)) + class LinuxDistro: """Represents a Linux distribution @@ -507,11 +553,10 @@ class LinuxDistro: class LinuxTest(LinuxSSHMixIn, QemuSystemTest): """Facilitates having a cloud-image Linux based available. - For tests that indent to interact with guests, this is a better choice + For tests that intend to interact with guests, this is a better choice to start with than the more vanilla `QemuSystemTest` class. """ - timeout = 900 distro = None username = 'root' password = 'password' @@ -546,6 +591,7 @@ class LinuxTest(LinuxSSHMixIn, QemuSystemTest): def setUp(self, ssh_pubkey=None, network_device_type='virtio-net'): super().setUp() + self.require_netdev('user') self._set_distro() self.vm.add_args('-smp', self.smp) self.vm.add_args('-m', self.memory) @@ -569,17 +615,9 @@ class LinuxTest(LinuxSSHMixIn, QemuSystemTest): return (ssh_public_key, ssh_private_key) def download_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 + # Set the qemu-img binary. + # If none is available, the test will cancel. + vmimage.QEMU_IMG = super().get_qemu_img() self.log.info('Downloading/preparing boot image') # Fedora 31 only provides ppc64le images diff --git a/tests/avocado/boot_linux.py b/tests/avocado/boot_linux.py index ee584d2fdf..cdce4cbcba 100644 --- a/tests/avocado/boot_linux.py +++ b/tests/avocado/boot_linux.py @@ -12,13 +12,14 @@ import os from avocado_qemu import LinuxTest, BUILD_DIR -from avocado import skipIf +from avocado import skipUnless class BootLinuxX8664(LinuxTest): """ :avocado: tags=arch:x86_64 """ + timeout = 480 def test_pc_i440fx_tcg(self): """ @@ -57,45 +58,15 @@ class BootLinuxX8664(LinuxTest): self.launch_and_wait(set_up_ssh_connection=False) +# For Aarch64 we only boot KVM tests in CI as booting the current +# Fedora OS in TCG tests is very heavyweight. There are lighter weight +# distros which we use in the machine_aarch64_virt.py tests. class BootLinuxAarch64(LinuxTest): """ :avocado: tags=arch:aarch64 :avocado: tags=machine:virt - :avocado: tags=machine:gic-version=2 """ - - 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_gicv2(self): - """ - :avocado: tags=accel:tcg - :avocado: tags=cpu:max - :avocado: tags=device:gicv2 - """ - self.require_accelerator("tcg") - self.vm.add_args("-accel", "tcg") - self.vm.add_args("-cpu", "max,lpa2=off") - self.vm.add_args("-machine", "virt,gic-version=2") - self.add_common_args() - self.launch_and_wait(set_up_ssh_connection=False) - - def test_virt_tcg_gicv3(self): - """ - :avocado: tags=accel:tcg - :avocado: tags=cpu:max - :avocado: tags=device:gicv3 - """ - self.require_accelerator("tcg") - self.vm.add_args("-accel", "tcg") - self.vm.add_args("-cpu", "max,lpa2=off") - self.vm.add_args("-machine", "virt,gic-version=3") - self.add_common_args() - self.launch_and_wait(set_up_ssh_connection=False) + timeout = 720 def test_virt_kvm(self): """ @@ -105,15 +76,24 @@ class BootLinuxAarch64(LinuxTest): self.require_accelerator("kvm") self.vm.add_args("-accel", "kvm") self.vm.add_args("-machine", "virt,gic-version=host") - self.add_common_args() + 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') self.launch_and_wait(set_up_ssh_connection=False) +# See the tux_baseline.py tests for almost the same coverage in a lot +# less time. class BootLinuxPPC64(LinuxTest): """ :avocado: tags=arch:ppc64 """ + timeout = 360 + + @skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited') def test_pseries_tcg(self): """ :avocado: tags=machine:pseries @@ -123,13 +103,24 @@ class BootLinuxPPC64(LinuxTest): self.vm.add_args("-accel", "tcg") self.launch_and_wait(set_up_ssh_connection=False) + def test_pseries_kvm(self): + """ + :avocado: tags=machine:pseries + :avocado: tags=accel:kvm + """ + self.require_accelerator("kvm") + self.vm.add_args("-accel", "kvm") + self.vm.add_args("-machine", "cap-ccf-assist=off") + self.launch_and_wait(set_up_ssh_connection=False) class BootLinuxS390X(LinuxTest): """ :avocado: tags=arch:s390x """ - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') + timeout = 240 + + @skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited') def test_s390_ccw_virtio_tcg(self): """ :avocado: tags=machine:s390-ccw-virtio diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index 45a2ceda22..989b65111c 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -15,6 +15,7 @@ import shutil from avocado import skip from avocado import skipUnless +from avocado import skipUnless from avocado_qemu import QemuSystemTest from avocado_qemu import exec_command from avocado_qemu import exec_command_and_wait_for_pattern @@ -29,6 +30,11 @@ Round up to next power of 2 def pow2ceil(x): return 1 if x == 0 else 2**(x - 1).bit_length() +def file_truncate(path, size): + if size != os.path.getsize(path): + with open(path, 'ab+') as fd: + fd.truncate(size) + """ Expand file size to next power of 2 """ @@ -335,13 +341,13 @@ class BootLinuxConsole(LinuxKernelTest): """ images_url = ('http://ports.ubuntu.com/ubuntu-ports/dists/' 'bionic-updates/main/installer-arm64/' - '20101020ubuntu543.15/images/') + '20101020ubuntu543.19/images/') kernel_url = images_url + 'netboot/ubuntu-installer/arm64/linux' - kernel_hash = '5bfc54cf7ed8157d93f6e5b0241e727b6dc22c50' + kernel_hash = 'e167757620640eb26de0972f578741924abb3a82' kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) initrd_url = images_url + 'netboot/ubuntu-installer/arm64/initrd.gz' - initrd_hash = 'd385d3e88d53e2004c5d43cbe668b458a094f772' + initrd_hash = 'cab5cb3fcefca8408aa5aae57f24574bfce8bdb9' initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) self.vm.set_console() @@ -381,6 +387,8 @@ class BootLinuxConsole(LinuxKernelTest): :avocado: tags=u-boot :avocado: tags=accel:tcg """ + self.require_netdev('user') + uboot_url = ('https://raw.githubusercontent.com/' 'Subbaraya-Sundeep/qemu-test-binaries/' 'fe371d32e50ca682391e1e70ab98c2942aeffb01/u-boot') @@ -392,6 +400,8 @@ class BootLinuxConsole(LinuxKernelTest): spi_hash = '65523a1835949b6f4553be96dec1b6a38fb05501' spi_path = self.fetch_asset(spi_url, asset_hash=spi_hash) + file_truncate(spi_path, 16 << 20) # Spansion S25FL128SDPBHICO is 16 MiB + self.vm.set_console() kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE self.vm.add_args('-kernel', uboot_path, @@ -487,10 +497,107 @@ class BootLinuxConsole(LinuxKernelTest): 'BCM2835') exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', '/soc/cprman@7e101000') - exec_command(self, 'halt') + exec_command_and_wait_for_pattern(self, 'halt', 'reboot: System halted') # Wait for VM to shut down gracefully self.vm.wait() + def test_arm_raspi4(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:raspi4b + :avocado: tags=device:pl011 + :avocado: tags=accel:tcg + :avocado: tags=rpi4b + + The kernel can be rebuilt using the kernel source referenced + and following the instructions on the on: + https://www.raspberrypi.org/documentation/linux/kernel/building.md + """ + + deb_url = ('http://archive.raspberrypi.org/debian/' + 'pool/main/r/raspberrypi-firmware/' + 'raspberrypi-kernel_1.20230106-1_arm64.deb') + deb_hash = '08dc55696535b18a6d4fe6fa10d4c0d905cbb2ed' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, '/boot/kernel8.img') + dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2711-rpi-4-b.dtb') + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'earlycon=pl011,mmio32,0xfe201000 ' + + 'console=ttyAMA0,115200 ' + + 'root=/dev/mmcblk1p2 rootwait ' + + 'dwc_otg.fiq_fsm_enable=0') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-append', kernel_command_line) + # When PCI is supported we can add a USB controller: + # '-device', 'qemu-xhci,bus=pcie.1,id=xhci', + # '-device', 'usb-kbd,bus=xhci.0', + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + # When USB is enabled we can look for this + # console_pattern = 'Product: QEMU USB Keyboard' + # self.wait_for_console_pattern(console_pattern) + console_pattern = 'Waiting for root device' + self.wait_for_console_pattern(console_pattern) + + + def test_arm_raspi4_initrd(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:raspi4b + :avocado: tags=device:pl011 + :avocado: tags=accel:tcg + :avocado: tags=rpi4b + + The kernel can be rebuilt using the kernel source referenced + and following the instructions on the on: + https://www.raspberrypi.org/documentation/linux/kernel/building.md + """ + deb_url = ('http://archive.raspberrypi.org/debian/' + 'pool/main/r/raspberrypi-firmware/' + 'raspberrypi-kernel_1.20230106-1_arm64.deb') + deb_hash = '08dc55696535b18a6d4fe6fa10d4c0d905cbb2ed' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, '/boot/kernel8.img') + dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2711-rpi-4-b.dtb') + + initrd_url = ('https://github.com/groeck/linux-build-test/raw/' + '86b2be1384d41c8c388e63078a847f1e1c4cb1de/rootfs/' + 'arm64/rootfs.cpio.gz') + initrd_hash = 'f3d4f9fa92a49aa542f1b44d34be77bbf8ca5b9d' + 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 + + 'earlycon=pl011,mmio32,0xfe201000 ' + + 'console=ttyAMA0,115200 ' + + 'panic=-1 noreboot ' + + 'dwc_otg.fiq_fsm_enable=0') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + # When PCI is supported we can add a USB controller: + # '-device', 'qemu-xhci,bus=pcie.1,id=xhci', + # '-device', 'usb-kbd,bus=xhci.0', + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'BCM2835') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + 'cprman@7e101000') + exec_command_and_wait_for_pattern(self, 'halt', 'reboot: System halted') + # TODO: Raspberry Pi4 doesn't shut down properly with recent kernels + # Wait for VM to shut down gracefully + #self.vm.wait() + def test_arm_exynos4210_initrd(self): """ :avocado: tags=arch:arm @@ -571,7 +678,10 @@ class BootLinuxConsole(LinuxKernelTest): 'Allwinner sun4i/sun5i') exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', 'system-control@1c00000') - # cubieboard's reboot is not functioning; omit reboot test. + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() def test_arm_cubieboard_sata(self): """ @@ -615,7 +725,60 @@ class BootLinuxConsole(LinuxKernelTest): 'Allwinner sun4i/sun5i') exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', 'sda') - # cubieboard's reboot is not functioning; omit reboot test. + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + + @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') + def test_arm_cubieboard_openwrt_22_03_2(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:cubieboard + :avocado: tags=device:sd + """ + + # This test download a 7.5 MiB compressed image and expand it + # to 126 MiB. + image_url = ('https://downloads.openwrt.org/releases/22.03.2/targets/' + 'sunxi/cortexa8/openwrt-22.03.2-sunxi-cortexa8-' + 'cubietech_a10-cubieboard-ext4-sdcard.img.gz') + image_hash = ('94b5ecbfbc0b3b56276e5146b899eafa' + '2ac5dc2d08733d6705af9f144f39f554') + image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + image_path = archive.extract(image_path_gz, self.workdir) + image_pow2ceil_expand(image_path) + + 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 + + 'usbcore.nousb ' + 'noreboot') + + self.wait_for_console_pattern('U-Boot SPL') + + interrupt_interactive_console_until_pattern( + self, 'Hit any key to stop autoboot:', '=>') + 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( + 'Please press Enter to activate this console.') + + exec_command_and_wait_for_pattern(self, ' ', 'root@') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun4i/sun5i') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') def test_arm_quanta_gsj(self): @@ -703,6 +866,182 @@ class BootLinuxConsole(LinuxKernelTest): self.wait_for_console_pattern( 'Give root password for system maintenance') + def test_arm_bpim2u(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:bpim2u + :avocado: tags=accel:tcg + """ + deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/' + 'linux-image-current-sunxi_21.02.2_armhf.deb') + deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-5.10.16-sunxi') + dtb_path = ('/usr/lib/linux-image-current-sunxi/' + 'sun8i-r40-bananapi-m2-ultra.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_bpim2u_initrd(self): + """ + :avocado: tags=arch:arm + :avocado: tags=accel:tcg + :avocado: tags=machine:bpim2u + """ + deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/' + 'linux-image-current-sunxi_21.02.2_armhf.deb') + deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-5.10.16-sunxi') + dtb_path = ('/usr/lib/linux-image-current-sunxi/' + 'sun8i-r40-bananapi-m2-ultra.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') + # Wait for VM to shut down gracefully + self.vm.wait() + + def test_arm_bpim2u_gmac(self): + """ + :avocado: tags=arch:arm + :avocado: tags=accel:tcg + :avocado: tags=machine:bpim2u + :avocado: tags=device:sd + """ + self.require_netdev('user') + + deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/' + 'linux-image-current-sunxi_21.02.2_armhf.deb') + deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-5.10.16-sunxi') + dtb_path = ('/usr/lib/linux-image-current-sunxi/' + 'sun8i-r40-bananapi-m2-ultra.dtb') + dtb_path = self.extract_from_deb(deb_path, dtb_path) + rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/' + 'buildroot-baseline/20221116.0/armel/rootfs.ext2.xz') + rootfs_hash = 'fae32f337c7b87547b10f42599acf109da8b6d9a' + 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) + image_pow2ceil_expand(rootfs_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'root=b300 rootwait rw ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-drive', 'file=' + rootfs_path + ',if=sd,format=raw', + '-net', 'nic,model=gmac,netdev=host_gmac', + '-netdev', 'user,id=host_gmac', + '-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', + 'mmcblk') + 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') + # Wait for VM to shut down gracefully + self.vm.wait() + + @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') + def test_arm_bpim2u_openwrt_22_03_3(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:bpim2u + :avocado: tags=device:sd + """ + + # This test download a 8.9 MiB compressed image and expand it + # to 127 MiB. + image_url = ('https://downloads.openwrt.org/releases/22.03.3/targets/' + 'sunxi/cortexa7/openwrt-22.03.3-sunxi-cortexa7-' + 'sinovoip_bananapi-m2-ultra-ext4-sdcard.img.gz') + image_hash = ('5b41b4e11423e562c6011640f9a7cd3b' + 'dd0a3d42b83430f7caa70a432e6cd82c') + image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + image_path = archive.extract(image_path_gz, self.workdir) + image_pow2ceil_expand(image_path) + + 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 + + 'usbcore.nousb ' + 'noreboot') + + self.wait_for_console_pattern('U-Boot SPL') + + interrupt_interactive_console_until_pattern( + self, 'Hit any key to stop autoboot:', '=>') + 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( + 'Please press Enter to activate this console.') + + exec_command_and_wait_for_pattern(self, ' ', 'root@') + + 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') + def test_arm_orangepi(self): """ :avocado: tags=arch:arm @@ -779,6 +1118,8 @@ class BootLinuxConsole(LinuxKernelTest): :avocado: tags=machine:orangepi-pc :avocado: tags=device:sd """ + self.require_netdev('user') + deb_url = ('https://apt.armbian.com/pool/main/l/' 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb') deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' @@ -788,8 +1129,8 @@ class BootLinuxConsole(LinuxKernelTest): dtb_path = '/usr/lib/linux-image-current-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' + 'buildroot-baseline/20221116.0/armel/rootfs.ext2.xz') + rootfs_hash = 'fae32f337c7b87547b10f42599acf109da8b6d9a' 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) @@ -938,6 +1279,7 @@ class BootLinuxConsole(LinuxKernelTest): def test_aarch64_raspi3_atf(self): """ + :avocado: tags=accel:tcg :avocado: tags=arch:aarch64 :avocado: tags=machine:raspi3b :avocado: tags=cpu:cortex-a53 @@ -1024,8 +1366,8 @@ class BootLinuxConsole(LinuxKernelTest): self.wait_for_console_pattern(console_pattern) def do_test_advcal_2018(self, day, tar_hash, kernel_name, console=0): - tar_url = ('https://www.qemu-advent-calendar.org' - '/2018/download/day' + day + '.tar.xz') + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/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(console_index=console) @@ -1043,64 +1385,21 @@ class BootLinuxConsole(LinuxKernelTest): self.vm.add_args('-dtb', self.workdir + '/day16/vexpress-v2p-ca9.dtb') self.do_test_advcal_2018('16', tar_hash, 'winter.zImage') - def test_arm_ast2400_palmetto_openbmc_v2_9_0(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:palmetto-bmc - """ - - image_url = ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' - 'obmc-phosphor-image-palmetto.static.mtd') - image_hash = ('3e13bbbc28e424865dc42f35ad672b10f2e82cdb11846bb28fa625b48beafd0d') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - - self.do_test_arm_aspeed(image_path) - - def test_arm_ast2500_romulus_openbmc_v2_9_0(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:romulus-bmc - """ - - image_url = ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' - 'obmc-phosphor-image-romulus.static.mtd') - image_hash = ('820341076803f1955bc31e647a512c79f9add4f5233d0697678bab4604c7bb25') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - - self.do_test_arm_aspeed(image_path) - - def do_test_arm_aspeed(self, image): - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', - '-net', 'nic') - self.vm.launch() - - self.wait_for_console_pattern("U-Boot 2016.07") - self.wait_for_console_pattern("## Loading kernel from FIT Image at 20080000") - self.wait_for_console_pattern("Starting kernel ...") - self.wait_for_console_pattern("Booting Linux on physical CPU 0x0") - self.wait_for_console_pattern( - "aspeed-smc 1e620000.spi: read control register: 203b0641") - self.wait_for_console_pattern("ftgmac100 1e660000.ethernet eth0: irq ") - self.wait_for_console_pattern("systemd[1]: Set hostname to") - def test_arm_ast2600_debian(self): """ :avocado: tags=arch:arm - :avocado: tags=machine:tacoma-bmc + :avocado: tags=machine:rainier-bmc """ deb_url = ('http://snapshot.debian.org/archive/debian/' - '20210302T203551Z/' + '20220606T211338Z/' 'pool/main/l/linux/' - 'linux-image-5.10.0-3-armmp_5.10.13-1_armhf.deb') - deb_hash = 'db40d32fe39255d05482bea48d72467b67d6225bb2a2a4d6f618cb8976f1e09e' + 'linux-image-5.17.0-2-armmp_5.17.6-1%2Bb1_armhf.deb') + deb_hash = '8acb2b4439faedc2f3ed4bdb2847ad4f6e0491f73debaeb7f660c8abe4dcdc0e' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash, algorithm='sha256') - kernel_path = self.extract_from_deb(deb_path, '/boot/vmlinuz-5.10.0-3-armmp') + kernel_path = self.extract_from_deb(deb_path, '/boot/vmlinuz-5.17.0-2-armmp') dtb_path = self.extract_from_deb(deb_path, - '/usr/lib/linux-image-5.10.0-3-armmp/aspeed-bmc-opp-tacoma.dtb') + '/usr/lib/linux-image-5.17.0-2-armmp/aspeed-bmc-ibm-rainier.dtb') self.vm.set_console() self.vm.add_args('-kernel', kernel_path, @@ -1166,7 +1465,8 @@ class BootLinuxConsole(LinuxKernelTest): self.wait_for_console_pattern("CPU: " + proc + " generation processor") self.wait_for_console_pattern("zImage starting: loaded") self.wait_for_console_pattern("Run /init as init process") - self.wait_for_console_pattern("Creating 1 MTD partitions") + # Device detection output driven by udev probing is sometimes cut off + # from console output, suspect S14silence-console init script. def test_ppc_powernv8(self): """ @@ -1184,6 +1484,14 @@ class BootLinuxConsole(LinuxKernelTest): """ self.do_test_ppc64_powernv('P9') + def test_ppc_powernv10(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:powernv10 + :avocado: tags=accel:tcg + """ + self.do_test_ppc64_powernv('P10') + def test_ppc_g3beige(self): """ :avocado: tags=arch:ppc @@ -1214,10 +1522,16 @@ class BootLinuxConsole(LinuxKernelTest): self.vm.add_args('-M', 'graphics=off') self.do_test_advcal_2018('15', tar_hash, 'invaders.elf') + # This test has a 6-10% failure rate on various hosts that look + # like issues with a buggy kernel. As a result we don't want it + # gating releases on Gitlab. + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + def test_sh4_r2d(self): """ :avocado: tags=arch:sh4 :avocado: tags=machine:r2d + :avocado: tags=flaky """ tar_hash = 'fe06a4fd8ccbf2e27928d64472939d47829d4c7e' self.vm.add_args('-append', 'console=ttySC1') diff --git a/tests/avocado/cpu_queries.py b/tests/avocado/cpu_queries.py index cf69f69b11..d3faa14720 100644 --- a/tests/avocado/cpu_queries.py +++ b/tests/avocado/cpu_queries.py @@ -23,12 +23,13 @@ class QueryCPUModelExpansion(QemuSystemTest): self.vm.add_args('-S') self.vm.launch() - cpus = self.vm.command('query-cpu-definitions') + cpus = self.vm.cmd('query-cpu-definitions') for c in cpus: self.log.info("Checking CPU: %s", c) self.assertNotIn('', c['unavailable-features'], c['name']) for c in cpus: model = {'name': c['name']} - e = self.vm.command('query-cpu-model-expansion', model=model, type='full') - self.assertEquals(e['model']['name'], c['name']) + e = self.vm.cmd('query-cpu-model-expansion', model=model, + type='full') + self.assertEqual(e['model']['name'], c['name']) diff --git a/tests/avocado/empty_cpu_model.py b/tests/avocado/empty_cpu_model.py index 22f504418d..d906ef3d3c 100644 --- a/tests/avocado/empty_cpu_model.py +++ b/tests/avocado/empty_cpu_model.py @@ -15,5 +15,5 @@ class EmptyCPUModel(QemuSystemTest): 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.assertEqual(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/avocado/hotplug_cpu.py b/tests/avocado/hotplug_cpu.py index 6374bf1b54..292bb43e4d 100644 --- a/tests/avocado/hotplug_cpu.py +++ b/tests/avocado/hotplug_cpu.py @@ -29,9 +29,9 @@ class HotPlugCPU(LinuxTest): with self.assertRaises(AssertionError): self.ssh_command('test -e /sys/devices/system/cpu/cpu1') - self.vm.command('device_add', - driver='Haswell-x86_64-cpu', - socket_id=0, - core_id=1, - thread_id=0) + self.vm.cmd('device_add', + driver='Haswell-x86_64-cpu', + socket_id=0, + core_id=1, + thread_id=0) self.ssh_command('test -e /sys/devices/system/cpu/cpu1') diff --git a/tests/avocado/info_usernet.py b/tests/avocado/info_usernet.py index dc01f74150..e1aa7a6e0a 100644 --- a/tests/avocado/info_usernet.py +++ b/tests/avocado/info_usernet.py @@ -14,12 +14,16 @@ from qemu.utils import get_info_usernet_hostfwd_port class InfoUsernet(QemuSystemTest): + """ + :avocado: tags=machine:none + """ def test_hostfwd(self): + self.require_netdev('user') self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22') self.vm.launch() - res = self.vm.command('human-monitor-command', - command_line='info usernet') + res = self.vm.cmd('human-monitor-command', + command_line='info usernet') port = get_info_usernet_hostfwd_port(res) self.assertIsNotNone(port, ('"info usernet" output content does not seem to ' diff --git a/tests/avocado/intel_iommu.py b/tests/avocado/intel_iommu.py index 474d62f6bf..f04ee1cf9d 100644 --- a/tests/avocado/intel_iommu.py +++ b/tests/avocado/intel_iommu.py @@ -9,10 +9,11 @@ # later. See the COPYING file in the top-level directory. import os -from avocado import skipIf +from avocado import skipUnless from avocado_qemu import LinuxTest -@skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') +@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + class IntelIOMMU(LinuxTest): """ :avocado: tags=arch:x86_64 @@ -21,6 +22,7 @@ class IntelIOMMU(LinuxTest): :avocado: tags=machine:q35 :avocado: tags=accel:kvm :avocado: tags=intel_iommu + :avocado: tags=flaky """ IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' @@ -54,9 +56,11 @@ class IntelIOMMU(LinuxTest): return kernel_url = self.distro.pxeboot_url + 'vmlinuz' + kernel_hash = '5b6f6876e1b5bda314f93893271da0d5777b1f3c' initrd_url = self.distro.pxeboot_url + 'initrd.img' - self.kernel_path = self.fetch_asset(kernel_url) - self.initrd_path = self.fetch_asset(initrd_url) + initrd_hash = 'dd0340a1b39bd28f88532babd4581c67649ec5b1' + self.kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + self.initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) def run_and_check(self): if self.kernel_path: diff --git a/tests/avocado/kvm_xen_guest.py b/tests/avocado/kvm_xen_guest.py new file mode 100644 index 0000000000..f8cb458d5d --- /dev/null +++ b/tests/avocado/kvm_xen_guest.py @@ -0,0 +1,171 @@ +# KVM Xen guest functional tests +# +# Copyright © 2021 Red Hat, Inc. +# Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Author: +# David Woodhouse +# Alex Bennée +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu.machine import machine + +from avocado_qemu import LinuxSSHMixIn +from avocado_qemu import QemuSystemTest +from avocado_qemu import wait_for_console_pattern + +class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): + """ + :avocado: tags=arch:x86_64 + :avocado: tags=machine:q35 + :avocado: tags=accel:kvm + :avocado: tags=kvm_xen_guest + """ + + KERNEL_DEFAULT = 'printk.time=0 root=/dev/xvda console=ttyS0' + + kernel_path = None + kernel_params = None + + # Fetch assets from the kvm-xen-guest subdir of my shared test + # images directory on fileserver.linaro.org where you can find + # build instructions for how they where assembled. + def get_asset(self, name, sha1): + base_url = ('https://fileserver.linaro.org/s/' + 'kE4nCFLdQcoBF9t/download?' + 'path=%2Fkvm-xen-guest&files=' ) + url = base_url + name + # use explicit name rather than failing to neatly parse the + # URL into a unique one + return self.fetch_asset(name=name, locations=(url), asset_hash=sha1) + + def common_vm_setup(self): + # We also catch lack of KVM_XEN support if we fail to launch + self.require_accelerator("kvm") + + self.vm.set_console() + + self.vm.add_args("-accel", "kvm,xen-version=0x4000a,kernel-irqchip=split") + self.vm.add_args("-smp", "2") + + self.kernel_path = self.get_asset("bzImage", + "367962983d0d32109998a70b45dcee4672d0b045") + self.rootfs = self.get_asset("rootfs.ext4", + "f1478401ea4b3fa2ea196396be44315bab2bb5e4") + + def run_and_check(self): + self.vm.add_args('-kernel', self.kernel_path, + '-append', self.kernel_params, + '-drive', f"file={self.rootfs},if=none,snapshot=on,format=raw,id=drv0", + '-device', 'xen-disk,drive=drv0,vdev=xvda', + '-device', 'virtio-net-pci,netdev=unet', + '-netdev', 'user,id=unet,hostfwd=:127.0.0.1:0-:22') + + try: + self.vm.launch() + except machine.VMLaunchFailure as e: + if "Xen HVM guest support not present" in e.output: + self.cancel("KVM Xen support is not present " + "(need v5.12+ kernel with CONFIG_KVM_XEN)") + elif "Property 'kvm-accel.xen-version' not found" in e.output: + self.cancel("QEMU not built with CONFIG_XEN_EMU support") + else: + raise e + + self.log.info('VM launched, waiting for sshd') + console_pattern = 'Starting dropbear sshd: OK' + wait_for_console_pattern(self, console_pattern, 'Oops') + self.log.info('sshd ready') + self.ssh_connect('root', '', False) + + self.ssh_command('cat /proc/cmdline') + self.ssh_command('dmesg | grep -e "Grant table initialized"') + + def test_kvm_xen_guest(self): + """ + :avocado: tags=kvm_xen_guest + """ + + self.common_vm_setup() + + self.kernel_params = (self.KERNEL_DEFAULT + + ' xen_emul_unplug=ide-disks') + self.run_and_check() + self.ssh_command('grep xen-pirq.*msi /proc/interrupts') + + def test_kvm_xen_guest_nomsi(self): + """ + :avocado: tags=kvm_xen_guest_nomsi + """ + + self.common_vm_setup() + + self.kernel_params = (self.KERNEL_DEFAULT + + ' xen_emul_unplug=ide-disks pci=nomsi') + self.run_and_check() + self.ssh_command('grep xen-pirq.* /proc/interrupts') + + def test_kvm_xen_guest_noapic_nomsi(self): + """ + :avocado: tags=kvm_xen_guest_noapic_nomsi + """ + + self.common_vm_setup() + + self.kernel_params = (self.KERNEL_DEFAULT + + ' xen_emul_unplug=ide-disks noapic pci=nomsi') + self.run_and_check() + self.ssh_command('grep xen-pirq /proc/interrupts') + + def test_kvm_xen_guest_vapic(self): + """ + :avocado: tags=kvm_xen_guest_vapic + """ + + self.common_vm_setup() + self.vm.add_args('-cpu', 'host,+xen-vapic') + self.kernel_params = (self.KERNEL_DEFAULT + + ' xen_emul_unplug=ide-disks') + self.run_and_check() + self.ssh_command('grep xen-pirq /proc/interrupts') + self.ssh_command('grep PCI-MSI /proc/interrupts') + + def test_kvm_xen_guest_novector(self): + """ + :avocado: tags=kvm_xen_guest_novector + """ + + self.common_vm_setup() + self.kernel_params = (self.KERNEL_DEFAULT + + ' xen_emul_unplug=ide-disks' + + ' xen_no_vector_callback') + self.run_and_check() + self.ssh_command('grep xen-platform-pci /proc/interrupts') + + def test_kvm_xen_guest_novector_nomsi(self): + """ + :avocado: tags=kvm_xen_guest_novector_nomsi + """ + + self.common_vm_setup() + + self.kernel_params = (self.KERNEL_DEFAULT + + ' xen_emul_unplug=ide-disks pci=nomsi' + + ' xen_no_vector_callback') + self.run_and_check() + self.ssh_command('grep xen-platform-pci /proc/interrupts') + + def test_kvm_xen_guest_novector_noapic(self): + """ + :avocado: tags=kvm_xen_guest_novector_noapic + """ + + self.common_vm_setup() + self.kernel_params = (self.KERNEL_DEFAULT + + ' xen_emul_unplug=ide-disks' + + ' xen_no_vector_callback noapic') + self.run_and_check() + self.ssh_command('grep xen-platform-pci /proc/interrupts') diff --git a/tests/avocado/linux_initrd.py b/tests/avocado/linux_initrd.py index ba02e5a563..aad5b19bd9 100644 --- a/tests/avocado/linux_initrd.py +++ b/tests/avocado/linux_initrd.py @@ -13,7 +13,7 @@ import logging import tempfile from avocado_qemu import QemuSystemTest -from avocado import skipIf +from avocado import skipUnless class LinuxInitrd(QemuSystemTest): @@ -53,9 +53,12 @@ class LinuxInitrd(QemuSystemTest): max_size + 1) self.assertRegex(self.vm.get_log(), expected_msg) - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + def test_with_2gib_file_should_work_with_linux_v4_16(self): """ + :avocado: tags=flaky + QEMU has supported up to 4 GiB initrd for recent kernel Expect guest can reach 'Unpacking initramfs...' """ diff --git a/tests/avocado/linux_ssh_mips_malta.py b/tests/avocado/linux_ssh_mips_malta.py index 0179d8a6ca..d9bb525ad9 100644 --- a/tests/avocado/linux_ssh_mips_malta.py +++ b/tests/avocado/linux_ssh_mips_malta.py @@ -101,14 +101,6 @@ class LinuxSSH(QemuSystemTest, LinuxSSHMixIn): self.ssh_disconnect_vm() wait_for_console_pattern(self, 'Power down', 'Oops') - def ssh_command_output_contains(self, cmd, exp): - stdout, _ = self.ssh_command(cmd) - for line in stdout: - if exp in line: - break - else: - self.fail('"%s" output does not contain "%s"' % (cmd, exp)) - def run_common_commands(self, wordsize): self.ssh_command_output_contains( 'cat /proc/cpuinfo', diff --git a/tests/avocado/machine_aarch64_sbsaref.py b/tests/avocado/machine_aarch64_sbsaref.py new file mode 100644 index 0000000000..98c76c1ff7 --- /dev/null +++ b/tests/avocado/machine_aarch64_sbsaref.py @@ -0,0 +1,238 @@ +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-FileCopyrightText: 2023-2024 Linaro Ltd. +# SPDX-FileContributor: Philippe Mathieu-Daudé +# SPDX-FileContributor: Marcin Juszkiewicz +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from avocado import skipUnless +from avocado.utils import archive + +from avocado_qemu import QemuSystemTest +from avocado_qemu import wait_for_console_pattern +from avocado_qemu import interrupt_interactive_console_until_pattern + + +class Aarch64SbsarefMachine(QemuSystemTest): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:sbsa-ref + :avocado: tags=accel:tcg + + As firmware runs at a higher privilege level than the hypervisor we + can only run these tests under TCG emulation. + """ + + timeout = 180 + + def fetch_firmware(self): + """ + Flash volumes generated using: + + Toolchain from Debian: + aarch64-linux-gnu-gcc (Debian 12.2.0-14) 12.2.0 + + Used components: + + - Trusted Firmware 2.10.2 + - Tianocore EDK2 stable202402 + - Tianocore EDK2-platforms commit 085c2fb + + """ + + # Secure BootRom (TF-A code) + fs0_xz_url = ( + "https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/" + "20240313-116475/edk2/SBSA_FLASH0.fd.xz" + ) + fs0_xz_hash = "637593749cc307dea7dc13265c32e5d020267552f22b18a31850b8429fc5e159" + tar_xz_path = self.fetch_asset(fs0_xz_url, asset_hash=fs0_xz_hash, + algorithm='sha256') + archive.extract(tar_xz_path, self.workdir) + fs0_path = os.path.join(self.workdir, "SBSA_FLASH0.fd") + + # Non-secure rom (UEFI and EFI variables) + fs1_xz_url = ( + "https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/" + "20240313-116475/edk2/SBSA_FLASH1.fd.xz" + ) + fs1_xz_hash = "cb0a5e8cf5e303c5d3dc106cfd5943ffe9714b86afddee7164c69ee1dd41991c" + tar_xz_path = self.fetch_asset(fs1_xz_url, asset_hash=fs1_xz_hash, + algorithm='sha256') + archive.extract(tar_xz_path, self.workdir) + fs1_path = os.path.join(self.workdir, "SBSA_FLASH1.fd") + + for path in [fs0_path, fs1_path]: + with open(path, "ab+") as fd: + fd.truncate(256 << 20) # Expand volumes to 256MiB + + self.vm.set_console() + self.vm.add_args( + "-drive", + f"if=pflash,file={fs0_path},format=raw", + "-drive", + f"if=pflash,file={fs1_path},format=raw", + "-smp", + "1", + "-machine", + "sbsa-ref", + ) + + def test_sbsaref_edk2_firmware(self): + """ + :avocado: tags=cpu:cortex-a57 + """ + + self.fetch_firmware() + self.vm.launch() + + # TF-A boot sequence: + # + # https://github.com/ARM-software/arm-trusted-firmware/blob/v2.8.0/\ + # docs/design/trusted-board-boot.rst#trusted-board-boot-sequence + # https://trustedfirmware-a.readthedocs.io/en/v2.8/\ + # design/firmware-design.html#cold-boot + + # AP Trusted ROM + wait_for_console_pattern(self, "Booting Trusted Firmware") + wait_for_console_pattern(self, "BL1: v2.10.2(release):") + wait_for_console_pattern(self, "BL1: Booting BL2") + + # Trusted Boot Firmware + wait_for_console_pattern(self, "BL2: v2.10.2(release)") + wait_for_console_pattern(self, "Booting BL31") + + # EL3 Runtime Software + wait_for_console_pattern(self, "BL31: v2.10.2(release)") + + # Non-trusted Firmware + wait_for_console_pattern(self, "UEFI firmware (version 1.0") + interrupt_interactive_console_until_pattern(self, "QEMU SBSA-REF Machine") + + # This tests the whole boot chain from EFI to Userspace + # We only boot a whole OS for the current top level CPU and GIC + # Other test profiles should use more minimal boots + def boot_alpine_linux(self, cpu): + self.fetch_firmware() + + iso_url = ( + "https://dl-cdn.alpinelinux.org/" + "alpine/v3.17/releases/aarch64/alpine-standard-3.17.2-aarch64.iso" + ) + + iso_hash = "5a36304ecf039292082d92b48152a9ec21009d3a62f459de623e19c4bd9dc027" + iso_path = self.fetch_asset(iso_url, algorithm="sha256", asset_hash=iso_hash) + + self.vm.set_console() + self.vm.add_args( + "-cpu", + cpu, + "-drive", + f"file={iso_path},format=raw", + ) + + self.vm.launch() + wait_for_console_pattern(self, "Welcome to Alpine Linux 3.17") + + def test_sbsaref_alpine_linux_cortex_a57(self): + """ + :avocado: tags=cpu:cortex-a57 + :avocado: tags=os:linux + """ + self.boot_alpine_linux("cortex-a57") + + def test_sbsaref_alpine_linux_neoverse_n1(self): + """ + :avocado: tags=cpu:neoverse-n1 + :avocado: tags=os:linux + """ + self.boot_alpine_linux("neoverse-n1") + + def test_sbsaref_alpine_linux_max_pauth_off(self): + """ + :avocado: tags=cpu:max + :avocado: tags=os:linux + """ + self.boot_alpine_linux("max,pauth=off") + + def test_sbsaref_alpine_linux_max_pauth_impdef(self): + """ + :avocado: tags=cpu:max + :avocado: tags=os:linux + """ + self.boot_alpine_linux("max,pauth-impdef=on") + + @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') + def test_sbsaref_alpine_linux_max(self): + """ + :avocado: tags=cpu:max + :avocado: tags=os:linux + """ + self.boot_alpine_linux("max") + + + # This tests the whole boot chain from EFI to Userspace + # We only boot a whole OS for the current top level CPU and GIC + # Other test profiles should use more minimal boots + def boot_openbsd73(self, cpu): + self.fetch_firmware() + + img_url = ( + "https://cdn.openbsd.org/pub/OpenBSD/7.3/arm64/miniroot73.img" + ) + + img_hash = "7fc2c75401d6f01fbfa25f4953f72ad7d7c18650056d30755c44b9c129b707e5" + img_path = self.fetch_asset(img_url, algorithm="sha256", asset_hash=img_hash) + + self.vm.set_console() + self.vm.add_args( + "-cpu", + cpu, + "-drive", + f"file={img_path},format=raw", + ) + + self.vm.launch() + wait_for_console_pattern(self, + "Welcome to the OpenBSD/arm64" + " 7.3 installation program.") + + def test_sbsaref_openbsd73_cortex_a57(self): + """ + :avocado: tags=cpu:cortex-a57 + :avocado: tags=os:openbsd + """ + self.boot_openbsd73("cortex-a57") + + def test_sbsaref_openbsd73_neoverse_n1(self): + """ + :avocado: tags=cpu:neoverse-n1 + :avocado: tags=os:openbsd + """ + self.boot_openbsd73("neoverse-n1") + + def test_sbsaref_openbsd73_max_pauth_off(self): + """ + :avocado: tags=cpu:max + :avocado: tags=os:openbsd + """ + self.boot_openbsd73("max,pauth=off") + + @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') + def test_sbsaref_openbsd73_max_pauth_impdef(self): + """ + :avocado: tags=cpu:max + :avocado: tags=os:openbsd + """ + self.boot_openbsd73("max,pauth-impdef=on") + + @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') + def test_sbsaref_openbsd73_max(self): + """ + :avocado: tags=cpu:max + :avocado: tags=os:openbsd + """ + self.boot_openbsd73("max") diff --git a/tests/avocado/machine_aarch64_virt.py b/tests/avocado/machine_aarch64_virt.py index 21848cba70..a90dc6ff4b 100644 --- a/tests/avocado/machine_aarch64_virt.py +++ b/tests/avocado/machine_aarch64_virt.py @@ -1,4 +1,5 @@ -# Functional test that boots a Linux kernel and checks the console +# Functional test that boots a various Linux systems and checks the +# console output. # # Copyright (c) 2022 Linaro Ltd. # @@ -8,29 +9,74 @@ # SPDX-License-Identifier: GPL-2.0-or-later import time +import os +import logging from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern from avocado_qemu import exec_command +from avocado_qemu import BUILD_DIR +from avocado.utils import process +from avocado.utils.path import find_command class Aarch64VirtMachine(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + timeout = 360 def wait_for_console_pattern(self, success_message, vm=None): wait_for_console_pattern(self, success_message, failure_message='Kernel panic - not syncing', vm=vm) - def test_aarch64_virt(self): + # This tests the whole boot chain from EFI to Userspace + # We only boot a whole OS for the current top level CPU and GIC + # Other test profiles should use more minimal boots + def test_alpine_virt_tcg_gic_max(self): """ :avocado: tags=arch:aarch64 :avocado: tags=machine:virt :avocado: tags=accel:tcg - :avocado: tags=cpu:max """ + iso_url = ('https://dl-cdn.alpinelinux.org/' + 'alpine/v3.17/releases/aarch64/' + 'alpine-standard-3.17.2-aarch64.iso') + + # Alpine use sha256 so I recalculated this myself + iso_sha1 = '76284fcd7b41fe899b0c2375ceb8470803eea839' + iso_path = self.fetch_asset(iso_url, asset_hash=iso_sha1) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + self.require_accelerator("tcg") + + self.vm.add_args("-accel", "tcg") + self.vm.add_args("-cpu", "max,pauth-impdef=on") + self.vm.add_args("-machine", + "virt,acpi=on," + "virtualization=on," + "mte=on," + "gic-version=max,iommu=smmuv3") + self.vm.add_args("-smp", "2", "-m", "1024") + self.vm.add_args('-bios', os.path.join(BUILD_DIR, 'pc-bios', + 'edk2-aarch64-code.fd')) + self.vm.add_args("-drive", f"file={iso_path},format=raw") + self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') + self.vm.add_args('-object', 'rng-random,id=rng0,filename=/dev/urandom') + + self.vm.launch() + self.wait_for_console_pattern('Welcome to Alpine Linux 3.17') + + + def common_aarch64_virt(self, machine): + """ + Common code to launch basic virt machine with kernel+initrd + and a scratch disk. + """ + logger = logging.getLogger('aarch64_virt') + kernel_url = ('https://fileserver.linaro.org/s/' 'z6B2ARM7DQT3HWN/download') - kernel_hash = 'ed11daab50c151dde0e1e9c9cb8b2d9bd3215347' kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) @@ -39,13 +85,62 @@ class Aarch64VirtMachine(QemuSystemTest): 'console=ttyAMA0') self.require_accelerator("tcg") self.vm.add_args('-cpu', 'max,pauth-impdef=on', + '-machine', machine, '-accel', 'tcg', '-kernel', kernel_path, '-append', kernel_command_line) + + # A RNG offers an easy way to generate a few IRQs + self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') + self.vm.add_args('-object', + 'rng-random,id=rng0,filename=/dev/urandom') + + # Also add a scratch block device + logger.info('creating scratch qcow2 image') + image_path = os.path.join(self.workdir, 'scratch.qcow2') + qemu_img = os.path.join(BUILD_DIR, 'qemu-img') + if not os.path.exists(qemu_img): + qemu_img = find_command('qemu-img', False) + if qemu_img is False: + self.cancel('Could not find "qemu-img", which is required to ' + 'create the temporary qcow2 image') + cmd = '%s create -f qcow2 %s 8M' % (qemu_img, image_path) + process.run(cmd) + + # Add the device + self.vm.add_args('-blockdev', + f"driver=qcow2,file.driver=file,file.filename={image_path},node-name=scratch") + self.vm.add_args('-device', + 'virtio-blk-device,drive=scratch') + self.vm.launch() self.wait_for_console_pattern('Welcome to Buildroot') time.sleep(0.1) exec_command(self, 'root') time.sleep(0.1) + exec_command(self, 'dd if=/dev/hwrng of=/dev/vda bs=512 count=4') + time.sleep(0.1) + exec_command(self, 'md5sum /dev/vda') + time.sleep(0.1) + exec_command(self, 'cat /proc/interrupts') + time.sleep(0.1) exec_command(self, 'cat /proc/self/maps') time.sleep(0.1) + + def test_aarch64_virt_gicv3(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:virt + :avocado: tags=accel:tcg + :avocado: tags=cpu:max + """ + self.common_aarch64_virt("virt,gic_version=3") + + def test_aarch64_virt_gicv2(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:virt + :avocado: tags=accel:tcg + :avocado: tags=cpu:max + """ + self.common_aarch64_virt("virt,gic-version=2") diff --git a/tests/avocado/machine_arm_canona1100.py b/tests/avocado/machine_arm_canona1100.py index 182a0b0513..a42d8b0f2b 100644 --- a/tests/avocado/machine_arm_canona1100.py +++ b/tests/avocado/machine_arm_canona1100.py @@ -23,8 +23,8 @@ class CanonA1100Machine(QemuSystemTest): :avocado: tags=machine:canon-a1100 :avocado: tags=device:pflash_cfi02 """ - tar_url = ('https://www.qemu-advent-calendar.org' - '/2018/download/day18.tar.xz') + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day18.tar.xz') tar_hash = '068b5fc4242b29381acee94713509f8a876e9db6' file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) archive.extract(file_path, self.workdir) diff --git a/tests/avocado/machine_arm_integratorcp.py b/tests/avocado/machine_arm_integratorcp.py index 1ffe1073ef..87f5cf3953 100644 --- a/tests/avocado/machine_arm_integratorcp.py +++ b/tests/avocado/machine_arm_integratorcp.py @@ -81,9 +81,9 @@ class IntegratorMachine(QemuSystemTest): 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) + self.vm.cmd('human-monitor-command', command_line='stop') + self.vm.cmd('human-monitor-command', + command_line='screendump %s' % screendump_path) logger = logging.getLogger('framebuffer') cpu_count = 1 diff --git a/tests/avocado/machine_aspeed.py b/tests/avocado/machine_aspeed.py index 33090af199..cec0181424 100644 --- a/tests/avocado/machine_aspeed.py +++ b/tests/avocado/machine_aspeed.py @@ -5,10 +5,21 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. +import time +import os +import tempfile +import subprocess + +from avocado_qemu import LinuxSSHMixIn from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern +from avocado_qemu import exec_command from avocado_qemu import exec_command_and_wait_for_pattern +from avocado_qemu import interrupt_interactive_console_until_pattern +from avocado_qemu import has_cmd from avocado.utils import archive +from avocado import skipUnless +from avocado import skipUnless class AST1030Machine(QemuSystemTest): @@ -16,10 +27,11 @@ class AST1030Machine(QemuSystemTest): timeout = 10 - def test_ast1030_zephyros(self): + def test_ast1030_zephyros_1_04(self): """ :avocado: tags=arch:arm :avocado: tags=machine:ast1030-evb + :avocado: tags=os:zephyr """ tar_url = ('https://github.com/AspeedTech-BMC' '/zephyr/releases/download/v00.01.04/ast1030-evb-demo.zip') @@ -34,3 +46,332 @@ class AST1030Machine(QemuSystemTest): wait_for_console_pattern(self, "Booting Zephyr OS") exec_command_and_wait_for_pattern(self, "help", "Available commands") + + def test_ast1030_zephyros_1_07(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:ast1030-evb + :avocado: tags=os:zephyr + """ + tar_url = ('https://github.com/AspeedTech-BMC' + '/zephyr/releases/download/v00.01.07/ast1030-evb-demo.zip') + tar_hash = '40ac87eabdcd3b3454ce5aad11fedc72a33ecda2' + tar_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + archive.extract(tar_path, self.workdir) + kernel_file = self.workdir + "/ast1030-evb-demo/zephyr.bin" + self.vm.set_console() + self.vm.add_args('-kernel', kernel_file, + '-nographic') + self.vm.launch() + wait_for_console_pattern(self, "Booting Zephyr OS") + for shell_cmd in [ + 'kernel stacks', + 'otp info conf', + 'otp info scu', + 'hwinfo devid', + 'crypto aes256_cbc_vault', + 'random get', + 'jtag JTAG1 sw_xfer high TMS', + 'adc ADC0 resolution 12', + 'adc ADC0 read 42', + 'adc ADC1 read 69', + 'i2c scan I2C_0', + 'i3c attach I3C_0', + 'hash test', + 'kernel uptime', + 'kernel reboot warm', + 'kernel uptime', + 'kernel reboot cold', + 'kernel uptime', + ]: exec_command_and_wait_for_pattern(self, shell_cmd, "uart:~$") + +class AST2x00Machine(QemuSystemTest): + + timeout = 90 + + def wait_for_console_pattern(self, success_message, vm=None): + wait_for_console_pattern(self, success_message, + failure_message='Kernel panic - not syncing', + vm=vm) + + def do_test_arm_aspeed(self, image): + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', + '-net', 'nic') + self.vm.launch() + + self.wait_for_console_pattern("U-Boot 2016.07") + self.wait_for_console_pattern("## Loading kernel from FIT Image at 20080000") + self.wait_for_console_pattern("Starting kernel ...") + self.wait_for_console_pattern("Booting Linux on physical CPU 0x0") + wait_for_console_pattern(self, + "aspeed-smc 1e620000.spi: read control register: 203b0641") + self.wait_for_console_pattern("ftgmac100 1e660000.ethernet eth0: irq ") + self.wait_for_console_pattern("systemd[1]: Set hostname to") + + def test_arm_ast2400_palmetto_openbmc_v2_9_0(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:palmetto-bmc + """ + + image_url = ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' + 'obmc-phosphor-image-palmetto.static.mtd') + image_hash = ('3e13bbbc28e424865dc42f35ad672b10f2e82cdb11846bb28fa625b48beafd0d') + image_path = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + + self.do_test_arm_aspeed(image_path) + + def test_arm_ast2500_romulus_openbmc_v2_9_0(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:romulus-bmc + """ + + image_url = ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' + 'obmc-phosphor-image-romulus.static.mtd') + image_hash = ('820341076803f1955bc31e647a512c79f9add4f5233d0697678bab4604c7bb25') + image_path = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + + self.do_test_arm_aspeed(image_path) + + def do_test_arm_aspeed_buildroot_start(self, image, cpu_id, pattern='Aspeed EVB'): + self.require_netdev('user') + + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', + '-net', 'nic', '-net', 'user') + self.vm.launch() + + self.wait_for_console_pattern('U-Boot 2019.04') + self.wait_for_console_pattern('## Loading kernel from FIT Image') + self.wait_for_console_pattern('Starting kernel ...') + self.wait_for_console_pattern('Booting Linux on physical CPU ' + cpu_id) + self.wait_for_console_pattern('lease of 10.0.2.15') + # the line before login: + self.wait_for_console_pattern(pattern) + time.sleep(0.1) + exec_command(self, 'root') + time.sleep(0.1) + exec_command(self, "passw0rd") + + def do_test_arm_aspeed_buildroot_poweroff(self): + exec_command_and_wait_for_pattern(self, 'poweroff', + 'reboot: System halted'); + + def test_arm_ast2500_evb_buildroot(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:ast2500-evb + """ + + image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2500-evb/buildroot-2023.11/flash.img') + image_hash = ('c23db6160cf77d0258397eb2051162c8473a56c441417c52a91ba217186e715f') + image_path = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); + self.do_test_arm_aspeed_buildroot_start(image_path, '0x0', 'Aspeed AST2500 EVB') + + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device', + 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d'); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon1/temp1_input', '0') + self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon1/temp1_input', '18000') + + self.do_test_arm_aspeed_buildroot_poweroff() + + def test_arm_ast2600_evb_buildroot(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:ast2600-evb + """ + + image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2600-evb/buildroot-2023.11/flash.img') + image_hash = ('b62808daef48b438d0728ee07662290490ecfa65987bb91294cafb1bb7ad1a68') + image_path = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); + self.vm.add_args('-device', + 'ds1338,bus=aspeed.i2c.bus.3,address=0x32'); + self.vm.add_args('-device', + 'i2c-echo,bus=aspeed.i2c.bus.3,address=0x42'); + self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB') + + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device', + 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d'); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon1/temp1_input', '0') + self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon1/temp1_input', '18000') + + exec_command_and_wait_for_pattern(self, + 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-3/device/new_device', + 'i2c i2c-3: new_device: Instantiated device ds1307 at 0x32'); + year = time.strftime("%Y") + exec_command_and_wait_for_pattern(self, 'hwclock -f /dev/rtc1', year); + + exec_command_and_wait_for_pattern(self, + 'echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-3/new_device', + 'i2c i2c-3: new_device: Instantiated device slave-24c02 at 0x64'); + exec_command(self, 'i2cset -y 3 0x42 0x64 0x00 0xaa i'); + time.sleep(0.1) + exec_command_and_wait_for_pattern(self, + 'hexdump /sys/bus/i2c/devices/3-1064/slave-eeprom', + '0000000 ffaa ffff ffff ffff ffff ffff ffff ffff'); + self.do_test_arm_aspeed_buildroot_poweroff() + + @skipUnless(*has_cmd('swtpm')) + def test_arm_ast2600_evb_buildroot_tpm(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:ast2600-evb + """ + + image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2600-evb/buildroot-2023.02-tpm/flash.img') + image_hash = ('a46009ae8a5403a0826d607215e731a8c68d27c14c41e55331706b8f9c7bd997') + image_path = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + + # force creation of VM object, which also defines self._sd + vm = self.vm + + socket = os.path.join(self._sd.name, 'swtpm-socket') + + subprocess.run(['swtpm', 'socket', '-d', '--tpm2', + '--tpmstate', f'dir={self.vm.temp_dir}', + '--ctrl', f'type=unixio,path={socket}']) + + self.vm.add_args('-chardev', f'socket,id=chrtpm,path={socket}') + self.vm.add_args('-tpmdev', 'emulator,id=tpm0,chardev=chrtpm') + self.vm.add_args('-device', + 'tpm-tis-i2c,tpmdev=tpm0,bus=aspeed.i2c.bus.12,address=0x2e') + self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB') + + exec_command_and_wait_for_pattern(self, + 'echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device', + 'tpm_tis_i2c 12-002e: 2.0 TPM (device-id 0x1, rev-id 1)'); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/tpm/tpm0/pcr-sha256/0', + 'B804724EA13F52A9072BA87FE8FDCC497DFC9DF9AA15B9088694639C431688E0'); + + self.do_test_arm_aspeed_buildroot_poweroff() + +class AST2x00MachineSDK(QemuSystemTest, LinuxSSHMixIn): + + EXTRA_BOOTARGS = ( + 'quiet ' + 'systemd.mask=org.openbmc.HostIpmi.service ' + 'systemd.mask=xyz.openbmc_project.Chassis.Control.Power@0.service ' + 'systemd.mask=modprobe@fuse.service ' + 'systemd.mask=rngd.service ' + 'systemd.mask=obmc-console@ttyS2.service ' + ) + + # FIXME: Although these tests boot a whole distro they are still + # slower than comparable machine models. There may be some + # optimisations which bring down the runtime. In the meantime they + # have generous timeouts and are disable for CI which aims for all + # tests to run in less than 60 seconds. + timeout = 240 + + def wait_for_console_pattern(self, success_message, vm=None): + wait_for_console_pattern(self, success_message, + failure_message='Kernel panic - not syncing', + vm=vm) + + def do_test_arm_aspeed_sdk_start(self, image): + self.require_netdev('user') + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', + '-net', 'nic', '-net', 'user,hostfwd=:127.0.0.1:0-:22') + self.vm.launch() + + self.wait_for_console_pattern('U-Boot 2019.04') + interrupt_interactive_console_until_pattern( + self, 'Hit any key to stop autoboot:', 'ast#') + exec_command_and_wait_for_pattern( + self, 'setenv bootargs ${bootargs} ' + self.EXTRA_BOOTARGS, 'ast#') + exec_command_and_wait_for_pattern( + self, 'boot', '## Loading kernel from FIT Image') + self.wait_for_console_pattern('Starting kernel ...') + + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + + def test_arm_ast2500_evb_sdk(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:ast2500-evb + :avocado: tags=flaky + """ + + image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/' + 'download/v08.06/ast2500-default-obmc.tar.gz') + image_hash = ('e1755f3cadff69190438c688d52dd0f0d399b70a1e14b1d3d5540fc4851d38ca') + image_path = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + archive.extract(image_path, self.workdir) + + self.do_test_arm_aspeed_sdk_start( + self.workdir + '/ast2500-default/image-bmc') + self.wait_for_console_pattern('nodistro.0 ast2500-default ttyS4') + + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + + def test_arm_ast2600_evb_sdk(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:ast2600-evb + :avocado: tags=flaky + """ + + image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/' + 'download/v08.06/ast2600-a2-obmc.tar.gz') + image_hash = ('9083506135f622d5e7351fcf7d4e1c7125cee5ba16141220c0ba88931f3681a4') + image_path = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + archive.extract(image_path, self.workdir) + + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test'); + self.vm.add_args('-device', + 'ds1338,bus=aspeed.i2c.bus.5,address=0x32'); + self.do_test_arm_aspeed_sdk_start( + self.workdir + '/ast2600-a2/image-bmc') + self.wait_for_console_pattern('nodistro.0 ast2600-a2 ttyS4') + + self.ssh_connect('root', '0penBmc', False) + self.ssh_command('dmesg -c > /dev/null') + + self.ssh_command_output_contains( + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-5/device/new_device ; ' + 'dmesg -c', + 'i2c i2c-5: new_device: Instantiated device lm75 at 0x4d'); + self.ssh_command_output_contains( + 'cat /sys/class/hwmon/hwmon19/temp1_input', '0') + self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000); + self.ssh_command_output_contains( + 'cat /sys/class/hwmon/hwmon19/temp1_input', '18000') + + self.ssh_command_output_contains( + 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-5/device/new_device ; ' + 'dmesg -c', + 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32'); + year = time.strftime("%Y") + self.ssh_command_output_contains('/sbin/hwclock -f /dev/rtc1', year); diff --git a/tests/avocado/machine_loongarch.py b/tests/avocado/machine_loongarch.py new file mode 100644 index 0000000000..7d8a3c1fa5 --- /dev/null +++ b/tests/avocado/machine_loongarch.py @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# LoongArch virt test. +# +# Copyright (c) 2023 Loongson Technology Corporation Limited +# + +from avocado_qemu import QemuSystemTest +from avocado_qemu import exec_command_and_wait_for_pattern +from avocado_qemu import wait_for_console_pattern + +class LoongArchMachine(QemuSystemTest): + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + + timeout = 120 + + def wait_for_console_pattern(self, success_message, vm=None): + wait_for_console_pattern(self, success_message, + failure_message='Kernel panic - not syncing', + vm=vm) + + def test_loongarch64_devices(self): + + """ + :avocado: tags=arch:loongarch64 + :avocado: tags=machine:virt + """ + + kernel_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/' + 'releases/download/binary-files/vmlinuz.efi') + kernel_hash = '951b485b16e3788b6db03a3e1793c067009e31a2' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + initrd_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/' + 'releases/download/binary-files/ramdisk') + initrd_hash = 'c67658d9b2a447ce7db2f73ba3d373c9b2b90ab2' + initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) + + bios_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/' + 'releases/download/binary-files/QEMU_EFI.fd') + bios_hash = ('dfc1bfba4853cd763b9d392d0031827e8addbca8') + bios_path = self.fetch_asset(bios_url, asset_hash=bios_hash) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'root=/dev/ram rdinit=/sbin/init console=ttyS0,115200') + self.vm.add_args('-nographic', + '-smp', '4', + '-m', '1024', + '-cpu', 'la464', + '-kernel', kernel_path, + '-initrd', initrd_path, + '-bios', bios_path, + '-append', kernel_command_line) + self.vm.launch() + self.wait_for_console_pattern('Run /sbin/init as init process') + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'processor : 3') diff --git a/tests/avocado/machine_m68k_nextcube.py b/tests/avocado/machine_m68k_nextcube.py index 6790e7d9cd..1f3c883910 100644 --- a/tests/avocado/machine_m68k_nextcube.py +++ b/tests/avocado/machine_m68k_nextcube.py @@ -30,8 +30,8 @@ class NextCubeMachine(QemuSystemTest): timeout = 15 def check_bootrom_framebuffer(self, screenshot_path): - rom_url = ('http://www.nextcomputers.org/NeXTfiles/Software/ROM_Files/' - '68040_Non-Turbo_Chipset/Rev_2.5_v66.BIN') + rom_url = ('https://sourceforge.net/p/previous/code/1350/tree/' + 'trunk/src/Rev_2.5_v66.BIN?format=raw') rom_hash = 'b3534796abae238a0111299fc406a9349f7fee24' rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash) @@ -43,8 +43,8 @@ class NextCubeMachine(QemuSystemTest): # 'displaysurface_create 1120x832' trace-event. time.sleep(2) - self.vm.command('human-monitor-command', - command_line='screendump %s' % screenshot_path) + self.vm.cmd('human-monitor-command', + command_line='screendump %s' % screenshot_path) @skipUnless(PIL_AVAILABLE, 'Python PIL not installed') def test_bootrom_framebuffer_size(self): @@ -55,25 +55,16 @@ class NextCubeMachine(QemuSystemTest): self.assertEqual(width, 1120) self.assertEqual(height, 832) - @skipUnless(tesseract_available(3), 'tesseract v3 OCR tool not available') - def test_bootrom_framebuffer_ocr_with_tesseract_v3(self): - screenshot_path = os.path.join(self.workdir, "dump.ppm") - self.check_bootrom_framebuffer(screenshot_path) - lines = tesseract_ocr(screenshot_path, tesseract_version=3) - text = '\n'.join(lines) - self.assertIn('Backplane', text) - self.assertIn('Ethernet address', text) - # Tesseract 4 adds a new OCR engine based on LSTM neural networks. The # new version is faster and more accurate than version 3. The drawback is # 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): + @skipUnless(tesseract_available(4), 'tesseract OCR tool not available') + def test_bootrom_framebuffer_ocr_with_tesseract(self): screenshot_path = os.path.join(self.workdir, "dump.ppm") self.check_bootrom_framebuffer(screenshot_path) lines = tesseract_ocr(screenshot_path, tesseract_version=4) text = '\n'.join(lines) - self.assertIn('Testing the FPU, SCC', text) + self.assertIn('Testing the FPU', text) self.assertIn('System test failed. Error code', text) self.assertIn('Boot command', text) self.assertIn('Next>', text) diff --git a/tests/avocado/machine_microblaze.py b/tests/avocado/machine_microblaze.py index 4928920f96..807709cd11 100644 --- a/tests/avocado/machine_microblaze.py +++ b/tests/avocado/machine_microblaze.py @@ -5,6 +5,8 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. +import time +from avocado_qemu import exec_command, exec_command_and_wait_for_pattern from avocado_qemu import QemuSystemTest from avocado_qemu import wait_for_console_pattern from avocado.utils import archive @@ -19,8 +21,8 @@ class MicroblazeMachine(QemuSystemTest): :avocado: tags=machine:petalogix-s3adsp1800 """ - tar_url = ('https://www.qemu-advent-calendar.org' - '/2018/download/day17.tar.xz') + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day17.tar.xz') tar_hash = '08bf3e3bfb6b6c7ce1e54ab65d54e189f2caf13f' file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) archive.extract(file_path, self.workdir) @@ -33,3 +35,27 @@ class MicroblazeMachine(QemuSystemTest): # The kernel sometimes gets stuck after the "This architecture ..." # message, that's why we don't test for a later string here. This # needs some investigation by a microblaze wizard one day... + + def test_microblazeel_s3adsp1800(self): + """ + :avocado: tags=arch:microblazeel + :avocado: tags=machine:petalogix-s3adsp1800 + """ + + self.require_netdev('user') + tar_url = ('http://www.qemu-advent-calendar.org/2023/download/' + 'day13.tar.gz') + tar_hash = '6623d5fff5f84cfa8f34e286f32eff6a26546f44' + file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + archive.extract(file_path, self.workdir) + self.vm.set_console() + self.vm.add_args('-kernel', self.workdir + '/day13/xmaton.bin') + self.vm.add_args('-nic', 'user,tftp=' + self.workdir + '/day13/') + self.vm.launch() + wait_for_console_pattern(self, 'QEMU Advent Calendar 2023') + time.sleep(0.1) + exec_command(self, 'root') + time.sleep(0.1) + exec_command_and_wait_for_pattern(self, + 'tftp -g -r xmaton.png 10.0.2.2 ; md5sum xmaton.png', + '821cd3cab8efd16ad6ee5acc3642a8ea') diff --git a/tests/avocado/machine_mips_malta.py b/tests/avocado/machine_mips_malta.py index f1895d59f3..8cf84bd805 100644 --- a/tests/avocado/machine_mips_malta.py +++ b/tests/avocado/machine_mips_malta.py @@ -12,10 +12,12 @@ import gzip import logging from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern +from avocado import skipUnless from avocado.utils import archive -from avocado import skipIf +from avocado_qemu import QemuSystemTest +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 NUMPY_AVAILABLE = True @@ -69,9 +71,9 @@ class MaltaMachineFramebuffer(QemuSystemTest): 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) + self.vm.cmd('human-monitor-command', command_line='stop') + self.vm.cmd('human-monitor-command', + command_line='screendump %s' % screendump_path) logger = logging.getLogger('framebuffer') match_threshold = 0.95 @@ -99,22 +101,64 @@ class MaltaMachineFramebuffer(QemuSystemTest): """ self.do_test_i6400_framebuffer_logo(1) - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + 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 + :avocado: tags=flaky """ self.do_test_i6400_framebuffer_logo(7) - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + 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 + :avocado: tags=flaky """ self.do_test_i6400_framebuffer_logo(8) + +class MaltaMachine(QemuSystemTest): + + def do_test_yamon(self): + rom_url = ('https://s3-eu-west-1.amazonaws.com/' + 'downloads-mips/mips-downloads/' + 'YAMON/yamon-bin-02.22.zip') + rom_hash = '8da7ecddbc5312704b8b324341ee238189bde480' + zip_path = self.fetch_asset(rom_url, asset_hash=rom_hash) + + archive.extract(zip_path, self.workdir) + yamon_path = os.path.join(self.workdir, 'yamon-02.22.bin') + + self.vm.set_console() + self.vm.add_args('-bios', yamon_path) + self.vm.launch() + + prompt = 'YAMON>' + pattern = 'YAMON ROM Monitor' + interrupt_interactive_console_until_pattern(self, pattern, prompt) + wait_for_console_pattern(self, prompt) + self.vm.shutdown() + + def test_mipsel_malta_yamon(self): + """ + :avocado: tags=arch:mipsel + :avocado: tags=machine:malta + :avocado: tags=endian:little + """ + self.do_test_yamon() + + def test_mips64el_malta_yamon(self): + """ + :avocado: tags=arch:mips64el + :avocado: tags=machine:malta + :avocado: tags=endian:little + """ + self.do_test_yamon() diff --git a/tests/avocado/machine_rx_gdbsim.py b/tests/avocado/machine_rx_gdbsim.py index 6cd8704b01..412a7a5089 100644 --- a/tests/avocado/machine_rx_gdbsim.py +++ b/tests/avocado/machine_rx_gdbsim.py @@ -10,7 +10,7 @@ import os -from avocado import skipIf +from avocado import skipUnless from avocado_qemu import QemuSystemTest from avocado_qemu import exec_command_and_wait_for_pattern from avocado_qemu import wait_for_console_pattern @@ -22,7 +22,8 @@ class RxGdbSimMachine(QemuSystemTest): timeout = 30 KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + def test_uboot(self): """ U-Boot and checks that the console is operational. @@ -30,6 +31,7 @@ class RxGdbSimMachine(QemuSystemTest): :avocado: tags=arch:rx :avocado: tags=machine:gdbsim-r5f562n8 :avocado: tags=endian:little + :avocado: tags=flaky """ uboot_url = ('https://acc.dl.osdn.jp/users/23/23888/u-boot.bin.gz') uboot_hash = '9b78dbd43b40b2526848c0b1ce9de02c24f4dcdb' @@ -46,7 +48,8 @@ class RxGdbSimMachine(QemuSystemTest): # FIXME limit baudrate on chardev, else we type too fast #exec_command_and_wait_for_pattern(self, 'version', gcc_version) - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + def test_linux_sash(self): """ Boots a Linux kernel and checks that the console is operational. @@ -54,6 +57,7 @@ class RxGdbSimMachine(QemuSystemTest): :avocado: tags=arch:rx :avocado: tags=machine:gdbsim-r5f562n7 :avocado: tags=endian:little + :avocado: tags=flaky """ dtb_url = ('https://acc.dl.osdn.jp/users/23/23887/rx-virt.dtb') dtb_hash = '7b4e4e2c71905da44e86ce47adee2210b026ac18' diff --git a/tests/avocado/machine_s390_ccw_virtio.py b/tests/avocado/machine_s390_ccw_virtio.py index 438a6f4321..26e938c9e9 100644 --- a/tests/avocado/machine_s390_ccw_virtio.py +++ b/tests/avocado/machine_s390_ccw_virtio.py @@ -12,7 +12,7 @@ import os import tempfile -from avocado import skipIf +from avocado import skipUnless from avocado_qemu import QemuSystemTest from avocado_qemu import exec_command_and_wait_for_pattern from avocado_qemu import wait_for_console_pattern @@ -36,8 +36,8 @@ class S390CCWVirtioMachine(QemuSystemTest): dmesg_clear_count = 1 def clear_guest_dmesg(self): exec_command_and_wait_for_pattern(self, 'dmesg -c > /dev/null; ' - 'echo dm_clear\ ' + str(self.dmesg_clear_count), - 'dm_clear ' + str(self.dmesg_clear_count)) + r'echo dm_clear\ ' + str(self.dmesg_clear_count), + r'dm_clear ' + str(self.dmesg_clear_count)) self.dmesg_clear_count += 1 def test_s390x_devices(self): @@ -66,6 +66,7 @@ class S390CCWVirtioMachine(QemuSystemTest): '-kernel', kernel_path, '-initrd', initrd_path, '-append', kernel_command_line, + '-cpu', 'max,prno-trng=off', '-device', 'virtio-net-ccw,devno=fe.1.1111', '-device', 'virtio-rng-ccw,devno=fe.2.0000,max_revision=0,id=rn1', @@ -106,10 +107,10 @@ class S390CCWVirtioMachine(QemuSystemTest): 'dd if=/dev/hwrng of=/dev/null bs=1k count=10', '10+0 records out') self.clear_guest_dmesg() - self.vm.command('device_del', id='rn1') + self.vm.cmd('device_del', id='rn1') self.wait_for_crw_reports() self.clear_guest_dmesg() - self.vm.command('device_del', id='rn2') + self.vm.cmd('device_del', id='rn2') self.wait_for_crw_reports() exec_command_and_wait_for_pattern(self, 'dd if=/dev/hwrng of=/dev/null bs=1k count=10', @@ -120,19 +121,19 @@ class S390CCWVirtioMachine(QemuSystemTest): 'cat /sys/bus/ccw/devices/0.1.1111/cutype', '3832/01') exec_command_and_wait_for_pattern(self, - 'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_vendor', - '0x1af4') + r'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_vendor', + r'0x1af4') exec_command_and_wait_for_pattern(self, - 'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_device', - '0x0001') + r'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_device', + r'0x0001') # check fid propagation exec_command_and_wait_for_pattern(self, - 'cat /sys/bus/pci/devices/000a\:00\:00.0/function_id', - '0x0000000c') + r'cat /sys/bus/pci/devices/000a\:00\:00.0/function_id', + r'0x0000000c') # add another device self.clear_guest_dmesg() - self.vm.command('device_add', driver='virtio-net-ccw', - devno='fe.0.4711', id='net_4711') + self.vm.cmd('device_add', driver='virtio-net-ccw', + devno='fe.0.4711', id='net_4711') self.wait_for_crw_reports() exec_command_and_wait_for_pattern(self, 'for i in 1 2 3 4 5 6 7 ; do ' 'if [ -e /sys/bus/ccw/devices/*4711 ]; then break; fi ;' @@ -140,7 +141,7 @@ class S390CCWVirtioMachine(QemuSystemTest): '0.0.4711') # and detach it again self.clear_guest_dmesg() - self.vm.command('device_del', id='net_4711') + self.vm.cmd('device_del', id='net_4711') self.vm.event_wait(name='DEVICE_DELETED', match={'data': {'device': 'net_4711'}}) self.wait_for_crw_reports() @@ -150,15 +151,14 @@ class S390CCWVirtioMachine(QemuSystemTest): # test the virtio-balloon device exec_command_and_wait_for_pattern(self, 'head -n 1 /proc/meminfo', 'MemTotal: 115640 kB') - self.vm.command('human-monitor-command', command_line='balloon 96') + self.vm.cmd('human-monitor-command', command_line='balloon 96') exec_command_and_wait_for_pattern(self, 'head -n 1 /proc/meminfo', 'MemTotal: 82872 kB') - self.vm.command('human-monitor-command', command_line='balloon 128') + self.vm.cmd('human-monitor-command', command_line='balloon 128') exec_command_and_wait_for_pattern(self, 'head -n 1 /proc/meminfo', 'MemTotal: 115640 kB') - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') def test_s390x_fedora(self): """ @@ -167,6 +167,7 @@ class S390CCWVirtioMachine(QemuSystemTest): :avocado: tags=device:virtio-gpu :avocado: tags=device:virtio-crypto :avocado: tags=device:virtio-net + :avocado: tags=flaky """ kernel_url = ('https://archives.fedoraproject.org/pub/archive' @@ -228,45 +229,49 @@ class S390CCWVirtioMachine(QemuSystemTest): # writing to the framebuffer. Since the PPM is uncompressed, we then # can simply read the written "magic bytes" back from the PPM file to # check whether the framebuffer is working as expected. - self.log.info("Test screendump of virtio-gpu device") - exec_command_and_wait_for_pattern(self, + # Unfortunately, this test is flaky, so we don't run it by default + if os.getenv('QEMU_TEST_FLAKY_TESTS'): + self.log.info("Test screendump of virtio-gpu device") + exec_command_and_wait_for_pattern(self, 'while ! (dmesg | grep gpudrmfb) ; do sleep 1 ; done', 'virtio_gpudrmfb frame buffer device') - exec_command_and_wait_for_pattern(self, - 'echo -e "\e[?25l" > /dev/tty0', ':/#') - exec_command_and_wait_for_pattern(self, 'for ((i=0;i<250;i++)); do ' - 'echo " The qu ick fo x j ump s o ver a laz y d og" >> fox.txt;' - 'done', - ':/#') - exec_command_and_wait_for_pattern(self, - 'dd if=fox.txt of=/dev/fb0 bs=1000 oflag=sync,nocache ; rm fox.txt', - '12+0 records out') - with tempfile.NamedTemporaryFile(suffix='.ppm', - prefix='qemu-scrdump-') as ppmfile: - self.vm.command('screendump', filename=ppmfile.name) - ppmfile.seek(0) - line = ppmfile.readline() - self.assertEqual(line, b"P6\n") - line = ppmfile.readline() - self.assertEqual(line, b"1280 800\n") - line = ppmfile.readline() - self.assertEqual(line, b"255\n") - line = ppmfile.readline(256) - self.assertEqual(line, b"The quick fox jumps over a lazy dog\n") + exec_command_and_wait_for_pattern(self, + r'echo -e "\e[?25l" > /dev/tty0', ':/#') + exec_command_and_wait_for_pattern(self, 'for ((i=0;i<250;i++)); do ' + 'echo " The qu ick fo x j ump s o ver a laz y d og" >> fox.txt;' + 'done', + ':/#') + exec_command_and_wait_for_pattern(self, + 'dd if=fox.txt of=/dev/fb0 bs=1000 oflag=sync,nocache ; rm fox.txt', + '12+0 records out') + with tempfile.NamedTemporaryFile(suffix='.ppm', + prefix='qemu-scrdump-') as ppmfile: + self.vm.cmd('screendump', filename=ppmfile.name) + ppmfile.seek(0) + line = ppmfile.readline() + self.assertEqual(line, b"P6\n") + line = ppmfile.readline() + self.assertEqual(line, b"1280 800\n") + line = ppmfile.readline() + self.assertEqual(line, b"255\n") + line = ppmfile.readline(256) + self.assertEqual(line, b"The quick fox jumps over a lazy dog\n") + else: + self.log.info("Skipped flaky screendump of virtio-gpu device test") # Hot-plug a virtio-crypto device and see whether it gets accepted self.log.info("Test hot-plug virtio-crypto device") self.clear_guest_dmesg() - self.vm.command('object-add', qom_type='cryptodev-backend-builtin', - id='cbe0') - self.vm.command('device_add', driver='virtio-crypto-ccw', id='crypdev0', - cryptodev='cbe0', devno='fe.0.2342') + self.vm.cmd('object-add', qom_type='cryptodev-backend-builtin', + id='cbe0') + self.vm.cmd('device_add', driver='virtio-crypto-ccw', id='crypdev0', + cryptodev='cbe0', devno='fe.0.2342') exec_command_and_wait_for_pattern(self, 'while ! (dmesg -c | grep Accelerator.device) ; do' ' sleep 1 ; done', 'Accelerator device is ready') exec_command_and_wait_for_pattern(self, 'lscss', '0.0.2342') - self.vm.command('device_del', id='crypdev0') - self.vm.command('object-del', id='cbe0') + self.vm.cmd('device_del', id='crypdev0') + self.vm.cmd('object-del', id='cbe0') exec_command_and_wait_for_pattern(self, 'while ! (dmesg -c | grep Start.virtcrypto_remove) ; do' ' sleep 1 ; done', 'Start virtcrypto_remove.') diff --git a/tests/avocado/machine_sparc64_sun4u.py b/tests/avocado/machine_sparc64_sun4u.py index 458165500e..d333c0ae91 100644 --- a/tests/avocado/machine_sparc64_sun4u.py +++ b/tests/avocado/machine_sparc64_sun4u.py @@ -24,8 +24,8 @@ class Sun4uMachine(LinuxKernelTest): :avocado: tags=arch:sparc64 :avocado: tags=machine:sun4u """ - tar_url = ('https://www.qemu-advent-calendar.org' - '/2018/download/day23.tar.xz') + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day23.tar.xz') tar_hash = '142db83cd974ffadc4f75c8a5cad5bcc5722c240' file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) archive.extract(file_path, self.workdir) diff --git a/tests/avocado/mem-addr-space-check.py b/tests/avocado/mem-addr-space-check.py new file mode 100644 index 0000000000..af019969c0 --- /dev/null +++ b/tests/avocado/mem-addr-space-check.py @@ -0,0 +1,356 @@ +# Check for crash when using memory beyond the available guest processor +# address space. +# +# Copyright (c) 2023 Red Hat, Inc. +# +# Author: +# Ani Sinha +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from avocado_qemu import QemuSystemTest +import signal +import time + +class MemAddrCheck(QemuSystemTest): + # after launch, in order to generate the logs from QEMU we need to + # wait for some time. Launching and then immediately shutting down + # the VM generates empty logs. A delay of 1 second is added for + # this reason. + DELAY_Q35_BOOT_SEQUENCE = 1 + + # first, lets test some 32-bit processors. + # for all 32-bit cases, pci64_hole_size is 0. + def test_phybits_low_pse36(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + With pse36 feature ON, a processor has 36 bits of addressing. So it can + access up to a maximum of 64GiB of memory. Memory hotplug region begins + at 4 GiB boundary when "above_4g_mem_size" is 0 (this would be true when + we have 0.5 GiB of VM memory, see pc_q35_init()). This means total + hotpluggable memory size is 60 GiB. Per slot, we reserve 1 GiB of memory + for dimm alignment for all newer machines (see enforce_aligned_dimm + property for pc machines and pc_get_device_memory_range()). That leaves + total hotpluggable actual memory size of 59 GiB. If the VM is started + with 0.5 GiB of memory, maxmem should be set to a maximum value of + 59.5 GiB to ensure that the processor can address all memory directly. + Note that 64-bit pci hole size is 0 in this case. If maxmem is set to + 59.6G, QEMU should fail to start with a message "phy-bits are too low". + If maxmem is set to 59.5G with all other QEMU parameters identical, QEMU + should start fine. + """ + self.vm.add_args('-S', '-machine', 'q35', '-m', + '512,slots=1,maxmem=59.6G', + '-cpu', 'pentium,pse36=on', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_pae(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + With pae feature ON, a processor has 36 bits of addressing. So it can + access up to a maximum of 64GiB of memory. Rest is the same as the case + with pse36 above. + """ + self.vm.add_args('-S', '-machine', 'q35', '-m', + '512,slots=1,maxmem=59.6G', + '-cpu', 'pentium,pae=on', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_pentium_pse36(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Setting maxmem to 59.5G and making sure that QEMU can start with the + same options as the failing case above with pse36 cpu feature. + """ + self.vm.add_args('-machine', 'q35', '-m', + '512,slots=1,maxmem=59.5G', + '-cpu', 'pentium,pse36=on', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_pentium_pae(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Test is same as above but now with pae cpu feature turned on. + Setting maxmem to 59.5G and making sure that QEMU can start fine + with the same options as the case above. + """ + self.vm.add_args('-machine', 'q35', '-m', + '512,slots=1,maxmem=59.5G', + '-cpu', 'pentium,pae=on', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_pentium2(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Pentium2 has 36 bits of addressing, so its same as pentium + with pse36 ON. + """ + self.vm.add_args('-machine', 'q35', '-m', + '512,slots=1,maxmem=59.5G', + '-cpu', 'pentium2', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_nonpse36(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Pentium processor has 32 bits of addressing without pse36 or pae + so it can access physical address up to 4 GiB. Setting maxmem to + 4 GiB should make QEMU fail to start with "phys-bits too low" + message because the region for memory hotplug is always placed + above 4 GiB due to the PCI hole and simplicity. + """ + self.vm.add_args('-S', '-machine', 'q35', '-m', + '512,slots=1,maxmem=4G', + '-cpu', 'pentium', '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + # now lets test some 64-bit CPU cases. + def test_phybits_low_tcg_q35_70_amd(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + For q35 7.1 machines and above, there is a HT window that starts at + 1024 GiB and ends at 1 TiB - 1. If the max GPA falls in this range, + "above_4G" memory is adjusted to start at 1 TiB boundary for AMD cpus + in the default case. Lets test without that case for machines 7.0. + For q35-7.0 machines, "above 4G" memory starts are 4G. + pci64_hole size is 32 GiB. Since TCG_PHYS_ADDR_BITS is defined to + be 40, TCG emulated CPUs have maximum of 1 TiB (1024 GiB) of + directly addressable memory. + Hence, maxmem value at most can be + 1024 GiB - 4 GiB - 1 GiB per slot for alignment - 32 GiB + 0.5 GiB + which is equal to 987.5 GiB. Setting the value to 988 GiB should + make QEMU fail with the error message. + """ + self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m', + '512,slots=1,maxmem=988G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_tcg_q35_71_amd(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + AMD_HT_START is defined to be at 1012 GiB. So for q35 machines + version > 7.0 and AMD cpus, instead of 1024 GiB limit for 40 bit + processor address space, it has to be 1012 GiB , that is 12 GiB + less than the case above in order to accommodate HT hole. + Make sure QEMU fails when maxmem size is 976 GiB (12 GiB less + than 988 GiB). + """ + self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=976G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_70_amd(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Same as q35-7.0 AMD case except that here we check that QEMU can + successfully start when maxmem is < 988G. + """ + self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m', + '512,slots=1,maxmem=987.5G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_71_amd(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Same as q35-7.1 AMD case except that here we check that QEMU can + successfully start when maxmem is < 976G. + """ + self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=975.5G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_71_intel(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Same parameters as test_phybits_low_tcg_q35_71_amd() but use + Intel cpu instead. QEMU should start fine in this case as + "above_4G" memory starts at 4G. + """ + self.vm.add_args('-S', '-cpu', 'Skylake-Server', + '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=976G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_tcg_q35_71_amd_41bits(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + AMD processor with 41 bits. Max cpu hw address = 2 TiB. + By setting maxram above 1012 GiB - 32 GiB - 4 GiB = 976 GiB, we can + force "above_4G" memory to start at 1 TiB for q35-7.1 machines + (max GPA will be above AMD_HT_START which is defined as 1012 GiB). + + With pci_64_hole size at 32 GiB, in this case, maxmem should be 991.5 + GiB with 1 GiB per slot for alignment and 0.5 GiB as non-hotplug + memory for the VM (1024 - 32 - 1 + 0.5). With 992 GiB, QEMU should + fail to start. + """ + self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', + '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=992G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_71_amd_41bits(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + AMD processor with 41 bits. Max cpu hw address = 2 TiB. + Same as above but by setting maxram between 976 GiB and 992 Gib, + QEMU should start fine. + """ + self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', + '-machine', 'pc-q35-7.1', '-m', + '512,slots=1,maxmem=990G', + '-display', 'none', + '-object', 'memory-backend-ram,id=mem1,size=1G', + '-device', 'pc-dimm,id=vm0,memdev=mem1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_low_tcg_q35_intel_cxl(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + cxl memory window starts after memory device range. Here, we use 1 GiB + of cxl window memory. 4G_mem end aligns at 4G. pci64_hole is 32 GiB and + starts after the cxl memory window. + So maxmem here should be at most 986 GiB considering all memory boundary + alignment constraints with 40 bits (1 TiB) of processor physical bits. + """ + self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', + '-machine', 'q35,cxl=on', '-m', + '512,slots=1,maxmem=987G', + '-display', 'none', + '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1', + '-M', 'cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=1G') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'phys-bits too low') + + def test_phybits_ok_tcg_q35_intel_cxl(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=arch:x86_64 + + Same as above but here we do not reserve any cxl memory window. Hence, + with the exact same parameters as above, QEMU should start fine even + with cxl enabled. + """ + self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', + '-machine', 'q35,cxl=on', '-m', + '512,slots=1,maxmem=987G', + '-display', 'none', + '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') diff --git a/tests/avocado/migration.py b/tests/avocado/migration.py index 584d6ef53f..be6234b3c2 100644 --- a/tests/avocado/migration.py +++ b/tests/avocado/migration.py @@ -11,15 +11,17 @@ import tempfile +import os + from avocado_qemu import QemuSystemTest from avocado import skipUnless -from avocado.utils import network +from avocado.utils.network import ports from avocado.utils import wait from avocado.utils.path import find_command -class Migration(QemuSystemTest): +class MigrationTest(QemuSystemTest): """ :avocado: tags=migration """ @@ -28,7 +30,7 @@ class Migration(QemuSystemTest): @staticmethod def migration_finished(vm): - return vm.command('query-migrate')['status'] in ('completed', 'failed') + return vm.cmd('query-migrate')['status'] in ('completed', 'failed') def assert_migration(self, src_vm, dst_vm): wait.wait_for(self.migration_finished, @@ -39,10 +41,10 @@ class Migration(QemuSystemTest): timeout=self.timeout, step=0.1, args=(dst_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') + self.assertEqual(src_vm.cmd('query-migrate')['status'], 'completed') + self.assertEqual(dst_vm.cmd('query-migrate')['status'], 'completed') + self.assertEqual(dst_vm.cmd('query-status')['status'], 'running') + self.assertEqual(src_vm.cmd('query-status')['status'],'postmigrate') def do_migrate(self, dest_uri, src_uri=None): dest_vm = self.get_vm('-incoming', dest_uri) @@ -57,25 +59,77 @@ class Migration(QemuSystemTest): self.assert_migration(source_vm, dest_vm) def _get_free_port(self): - port = network.find_free_port() + port = ports.find_free_port() if port is None: self.cancel('Failed to find a free port') return port - - def test_migration_with_tcp_localhost(self): + def migration_with_tcp_localhost(self): dest_uri = 'tcp:localhost:%u' % self._get_free_port() self.do_migrate(dest_uri) - def test_migration_with_unix(self): + def 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): + def 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) + + +@skipUnless('aarch64' in os.uname()[4], "host != target") +class Aarch64(MigrationTest): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:virt + :avocado: tags=cpu:max + """ + + def test_migration_with_tcp_localhost(self): + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.migration_with_unix() + + def test_migration_with_exec(self): + self.migration_with_exec() + + +@skipUnless('x86_64' in os.uname()[4], "host != target") +class X86_64(MigrationTest): + """ + :avocado: tags=arch:x86_64 + :avocado: tags=machine:pc + :avocado: tags=cpu:qemu64 + """ + + def test_migration_with_tcp_localhost(self): + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.migration_with_unix() + + def test_migration_with_exec(self): + self.migration_with_exec() + + +@skipUnless('ppc64le' in os.uname()[4], "host != target") +class PPC64(MigrationTest): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + """ + + def test_migration_with_tcp_localhost(self): + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.migration_with_unix() + + def test_migration_with_exec(self): + self.migration_with_exec() diff --git a/tests/avocado/multiprocess.py b/tests/avocado/multiprocess.py index 80a3b8f442..ee7490ae08 100644 --- a/tests/avocado/multiprocess.py +++ b/tests/avocado/multiprocess.py @@ -18,10 +18,11 @@ class Multiprocess(QemuSystemTest): """ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - def do_test(self, kernel_url, initrd_url, kernel_command_line, - machine_type): + def do_test(self, kernel_url, kernel_hash, initrd_url, initrd_hash, + kernel_command_line, machine_type): """Main test method""" self.require_accelerator('kvm') + self.require_multiprocess() # Create socketpair to connect proxy and remote processes proxy_sock, remote_sock = socket.socketpair(socket.AF_UNIX, @@ -29,8 +30,8 @@ class Multiprocess(QemuSystemTest): os.set_inheritable(proxy_sock.fileno(), True) os.set_inheritable(remote_sock.fileno(), True) - kernel_path = self.fetch_asset(kernel_url) - initrd_path = self.fetch_asset(initrd_url) + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) # Create remote process remote_vm = self.get_vm() @@ -71,13 +72,16 @@ class Multiprocess(QemuSystemTest): kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' '/linux/releases/31/Everything/x86_64/os/images' '/pxeboot/vmlinuz') + kernel_hash = '5b6f6876e1b5bda314f93893271da0d5777b1f3c' initrd_url = ('https://archives.fedoraproject.org/pub/archive/fedora' '/linux/releases/31/Everything/x86_64/os/images' '/pxeboot/initrd.img') + initrd_hash = 'dd0340a1b39bd28f88532babd4581c67649ec5b1' kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0 rdinit=/bin/bash') machine_type = 'pc' - self.do_test(kernel_url, initrd_url, kernel_command_line, machine_type) + self.do_test(kernel_url, kernel_hash, initrd_url, initrd_hash, + kernel_command_line, machine_type) def test_multiprocess_aarch64(self): """ @@ -86,10 +90,13 @@ class Multiprocess(QemuSystemTest): kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' '/linux/releases/31/Everything/aarch64/os/images' '/pxeboot/vmlinuz') + kernel_hash = '3505f2751e2833c681de78cee8dda1e49cabd2e8' initrd_url = ('https://archives.fedoraproject.org/pub/archive/fedora' '/linux/releases/31/Everything/aarch64/os/images' '/pxeboot/initrd.img') + initrd_hash = '519a1962daf17d67fc3a9c89d45affcb399607db' kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'rdinit=/bin/bash console=ttyAMA0') machine_type = 'virt,gic-version=3' - self.do_test(kernel_url, initrd_url, kernel_command_line, machine_type) + self.do_test(kernel_url, kernel_hash, initrd_url, initrd_hash, + kernel_command_line, machine_type) diff --git a/tests/avocado/netdev-ethtool.py b/tests/avocado/netdev-ethtool.py new file mode 100644 index 0000000000..5f33288f81 --- /dev/null +++ b/tests/avocado/netdev-ethtool.py @@ -0,0 +1,101 @@ +# ethtool tests for emulated network devices +# +# This test leverages ethtool's --test sequence to validate network +# device behaviour. +# +# SPDX-License-Identifier: GPL-2.0-or-late + +from avocado import skip +from avocado_qemu import QemuSystemTest +from avocado_qemu import wait_for_console_pattern + +class NetDevEthtool(QemuSystemTest): + """ + :avocado: tags=arch:x86_64 + :avocado: tags=machine:q35 + """ + + # Runs in about 17s under KVM, 19s under TCG, 25s under GCOV + timeout = 45 + + # Fetch assets from the netdev-ethtool subdir of my shared test + # images directory on fileserver.linaro.org. + def get_asset(self, name, sha1): + base_url = ('https://fileserver.linaro.org/s/' + 'kE4nCFLdQcoBF9t/download?' + 'path=%2Fnetdev-ethtool&files=' ) + url = base_url + name + # use explicit name rather than failing to neatly parse the + # URL into a unique one + return self.fetch_asset(name=name, locations=(url), asset_hash=sha1) + + def common_test_code(self, netdev, extra_args=None): + + # This custom kernel has drivers for all the supported network + # devices we can emulate in QEMU + kernel = self.get_asset("bzImage", + "33469d7802732d5815226166581442395cb289e2") + + rootfs = self.get_asset("rootfs.squashfs", + "9793cea7021414ae844bda51f558bd6565b50cdc") + + append = 'printk.time=0 console=ttyS0 ' + append += 'root=/dev/sr0 rootfstype=squashfs ' + + # any additional kernel tweaks for the test + if extra_args: + append += extra_args + + # finally invoke ethtool directly + append += ' init=/usr/sbin/ethtool -- -t eth1 offline' + + # add the rootfs via a readonly cdrom image + drive = f"file={rootfs},if=ide,index=0,media=cdrom" + + self.vm.add_args('-kernel', kernel, + '-append', append, + '-drive', drive, + '-device', netdev) + + self.vm.set_console(console_index=0) + self.vm.launch() + + wait_for_console_pattern(self, + "The test result is PASS", + "The test result is FAIL", + vm=None) + # no need to gracefully shutdown, just finish + self.vm.kill() + + def test_igb(self): + """ + :avocado: tags=device:igb + """ + self.common_test_code("igb") + + def test_igb_nomsi(self): + """ + :avocado: tags=device:igb + """ + self.common_test_code("igb", "pci=nomsi") + + # It seems the other popular cards we model in QEMU currently fail + # the pattern test with: + # + # pattern test failed (reg 0x00178): got 0x00000000 expected 0x00005A5A + # + # So for now we skip them. + + @skip("Incomplete reg 0x00178 support") + def test_e1000(self): + """ + :avocado: tags=device:e1000 + """ + self.common_test_code("e1000") + + @skip("Incomplete reg 0x00178 support") + def test_i82550(self): + """ + :avocado: tags=device:i82550 + """ + self.common_test_code("i82550") diff --git a/tests/avocado/pc_cpu_hotplug_props.py b/tests/avocado/pc_cpu_hotplug_props.py index 52b878188e..4bd3e02665 100644 --- a/tests/avocado/pc_cpu_hotplug_props.py +++ b/tests/avocado/pc_cpu_hotplug_props.py @@ -32,4 +32,4 @@ class OmittedCPUProps(QemuSystemTest): self.vm.add_args('-smp', '1,sockets=2,cores=2,threads=2,maxcpus=8') self.vm.add_args('-device', 'qemu64-x86_64-cpu,socket-id=1,core-id=0,thread-id=0') self.vm.launch() - self.assertEquals(len(self.vm.command('query-cpus-fast')), 2) + self.assertEqual(len(self.vm.cmd('query-cpus-fast')), 2) diff --git a/tests/avocado/ppc_amiga.py b/tests/avocado/ppc_amiga.py new file mode 100644 index 0000000000..b6f866f91d --- /dev/null +++ b/tests/avocado/ppc_amiga.py @@ -0,0 +1,38 @@ +# Test AmigaNG boards +# +# Copyright (c) 2023 BALATON Zoltan +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado.utils import archive +from avocado.utils import process +from avocado_qemu import QemuSystemTest +from avocado_qemu import wait_for_console_pattern + +class AmigaOneMachine(QemuSystemTest): + + timeout = 90 + + def test_ppc_amigaone(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:amigaone + :avocado: tags=device:articia + :avocado: tags=accel:tcg + """ + self.require_accelerator("tcg") + tar_name = 'A1Firmware_Floppy_05-Mar-2005.zip' + tar_url = ('https://www.hyperion-entertainment.com/index.php/' + 'downloads?view=download&format=raw&file=25') + tar_hash = 'c52e59bc73e31d8bcc3cc2106778f7ac84f6c755' + zip_file = self.fetch_asset(tar_name, locations=tar_url, + asset_hash=tar_hash) + archive.extract(zip_file, self.workdir) + cmd = f"tail -c 524288 {self.workdir}/floppy_edition/updater.image >{self.workdir}/u-boot-amigaone.bin" + process.run(cmd, shell=True) + + self.vm.set_console() + self.vm.add_args('-bios', self.workdir + '/u-boot-amigaone.bin') + self.vm.launch() + wait_for_console_pattern(self, 'FLASH:') diff --git a/tests/avocado/ppc_bamboo.py b/tests/avocado/ppc_bamboo.py index 102ff252df..a81be3d608 100644 --- a/tests/avocado/ppc_bamboo.py +++ b/tests/avocado/ppc_bamboo.py @@ -23,6 +23,7 @@ class BambooMachine(QemuSystemTest): :avocado: tags=accel:tcg """ self.require_accelerator("tcg") + self.require_netdev('user') tar_url = ('http://landley.net/aboriginal/downloads/binaries/' 'system-image-powerpc-440fp.tar.gz') tar_hash = '53e5f16414b195b82d2c70272f81c2eedb39bad9' diff --git a/tests/avocado/ppc_hv_tests.py b/tests/avocado/ppc_hv_tests.py new file mode 100644 index 0000000000..bf8822bb97 --- /dev/null +++ b/tests/avocado/ppc_hv_tests.py @@ -0,0 +1,206 @@ +# Tests that specifically try to exercise hypervisor features of the +# target machines. powernv supports the Power hypervisor ISA, and +# pseries supports the nested-HV hypervisor spec. +# +# Copyright (c) 2023 IBM Corporation +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado import skipIf, skipUnless +from avocado.utils import archive +from avocado_qemu import QemuSystemTest +from avocado_qemu import wait_for_console_pattern, exec_command +import os +import time +import subprocess +from datetime import datetime + +deps = ["xorriso"] # dependent tools needed in the test setup/box. + +def which(tool): + """ looks up the full path for @tool, returns None if not found + or if @tool does not have executable permissions. + """ + paths=os.getenv('PATH') + for p in paths.split(os.path.pathsep): + p = os.path.join(p, tool) + if os.path.exists(p) and os.access(p, os.X_OK): + return p + return None + +def missing_deps(): + """ returns True if any of the test dependent tools are absent. + """ + for dep in deps: + if which(dep) is None: + return True + return False + +# Alpine is a light weight distro that supports QEMU. These tests boot +# that on the machine then run a QEMU guest inside it in KVM mode, +# that runs the same Alpine distro image. +# QEMU packages are downloaded and installed on each test. That's not a +# large download, but it may be more polite to create qcow2 image with +# QEMU already installed and use that. +# XXX: The order of these tests seems to matter, see git blame. +@skipIf(missing_deps(), 'dependencies (%s) not installed' % ','.join(deps)) +@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test sometimes gets stuck due to console handling problem') +@skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') +@skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited') +class HypervisorTest(QemuSystemTest): + + timeout = 1000 + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 ' + panic_message = 'Kernel panic - not syncing' + good_message = 'VFS: Cannot open root device' + + def extract_from_iso(self, iso, path): + """ + Extracts a file from an iso file into the test workdir + + :param iso: path to the iso file + :param path: path within the iso file of the file to be extracted + :returns: path of the extracted file + """ + filename = os.path.basename(path) + + cwd = os.getcwd() + os.chdir(self.workdir) + + with open(filename, "w") as outfile: + cmd = "xorriso -osirrox on -indev %s -cpx %s %s" % (iso, path, filename) + subprocess.run(cmd.split(), + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + os.chdir(cwd) + + # Return complete path to extracted file. Because callers to + # extract_from_iso() 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, filename)) + + def setUp(self): + super().setUp() + + iso_url = ('https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/ppc64le/alpine-standard-3.18.4-ppc64le.iso') + + # Alpine use sha256 so I recalculated this myself + iso_sha256 = 'c26b8d3e17c2f3f0fed02b4b1296589c2390e6d5548610099af75300edd7b3ff' + iso_path = self.fetch_asset(iso_url, asset_hash=iso_sha256, + algorithm = "sha256") + + self.iso_path = iso_path + self.vmlinuz = self.extract_from_iso(iso_path, '/boot/vmlinuz-lts') + self.initramfs = self.extract_from_iso(iso_path, '/boot/initramfs-lts') + + def do_start_alpine(self): + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + self.vm.add_args("-kernel", self.vmlinuz) + self.vm.add_args("-initrd", self.initramfs) + self.vm.add_args("-smp", "4", "-m", "2g") + self.vm.add_args("-drive", f"file={self.iso_path},format=raw,if=none,id=drive0") + + self.vm.launch() + wait_for_console_pattern(self, 'Welcome to Alpine Linux 3.18') + exec_command(self, 'root') + wait_for_console_pattern(self, 'localhost login:') + wait_for_console_pattern(self, 'You may change this message by editing /etc/motd.') + # If the time is wrong, SSL certificates can fail. + exec_command(self, 'date -s "' + datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S' + '"')) + exec_command(self, 'setup-alpine -qe') + wait_for_console_pattern(self, 'Updating repository indexes... done.') + + def do_stop_alpine(self): + exec_command(self, 'poweroff') + wait_for_console_pattern(self, 'alpine:~#') + self.vm.wait() + + def do_setup_kvm(self): + exec_command(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/main > /etc/apk/repositories') + wait_for_console_pattern(self, 'alpine:~#') + exec_command(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/community >> /etc/apk/repositories') + wait_for_console_pattern(self, 'alpine:~#') + exec_command(self, 'apk update') + wait_for_console_pattern(self, 'alpine:~#') + exec_command(self, 'apk add qemu-system-ppc64') + wait_for_console_pattern(self, 'alpine:~#') + exec_command(self, 'modprobe kvm-hv') + wait_for_console_pattern(self, 'alpine:~#') + + # This uses the host's block device as the source file for guest block + # device for install media. This is a bit hacky but allows reuse of the + # iso without having a passthrough filesystem configured. + def do_test_kvm(self, hpt=False): + if hpt: + append = 'disable_radix' + else: + append = '' + exec_command(self, 'qemu-system-ppc64 -nographic -smp 2 -m 1g ' + '-machine pseries,x-vof=on,accel=kvm ' + '-machine cap-cfpc=broken,cap-sbbc=broken,' + 'cap-ibs=broken,cap-ccf-assist=off ' + '-drive file=/dev/nvme0n1,format=raw,readonly=on ' + '-initrd /media/nvme0n1/boot/initramfs-lts ' + '-kernel /media/nvme0n1/boot/vmlinuz-lts ' + '-append \'usbcore.nousb ' + append + '\'') + # Alpine 3.18 kernel seems to crash in XHCI USB driver. + wait_for_console_pattern(self, 'Welcome to Alpine Linux 3.18') + exec_command(self, 'root') + wait_for_console_pattern(self, 'localhost login:') + wait_for_console_pattern(self, 'You may change this message by editing /etc/motd.') + exec_command(self, 'poweroff >& /dev/null') + wait_for_console_pattern(self, 'localhost:~#') + wait_for_console_pattern(self, 'reboot: Power down') + time.sleep(1) + exec_command(self, '') + wait_for_console_pattern(self, 'alpine:~#') + + def test_hv_pseries(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + :avocado: tags=accel:tcg + """ + self.require_accelerator("tcg") + self.vm.add_args("-accel", "tcg,thread=multi") + self.vm.add_args('-device', 'nvme,serial=1234,drive=drive0') + self.vm.add_args("-machine", "x-vof=on,cap-nested-hv=on") + self.do_start_alpine() + self.do_setup_kvm() + self.do_test_kvm() + self.do_stop_alpine() + + def test_hv_pseries_kvm(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + :avocado: tags=accel:kvm + """ + self.require_accelerator("kvm") + self.vm.add_args("-accel", "kvm") + self.vm.add_args('-device', 'nvme,serial=1234,drive=drive0') + self.vm.add_args("-machine", "x-vof=on,cap-nested-hv=on,cap-ccf-assist=off") + self.do_start_alpine() + self.do_setup_kvm() + self.do_test_kvm() + self.do_stop_alpine() + + def test_hv_powernv(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:powernv + :avocado: tags=accel:tcg + """ + self.require_accelerator("tcg") + self.vm.add_args("-accel", "tcg,thread=multi") + self.vm.add_args('-device', 'nvme,bus=pcie.2,addr=0x0,serial=1234,drive=drive0', + '-device', 'e1000e,netdev=net0,mac=C0:FF:EE:00:00:02,bus=pcie.0,addr=0x0', + '-netdev', 'user,id=net0,hostfwd=::20022-:22,hostname=alpine') + self.do_start_alpine() + self.do_setup_kvm() + self.do_test_kvm() + self.do_test_kvm(True) + self.do_stop_alpine() diff --git a/tests/avocado/ppc_mpc8544ds.py b/tests/avocado/ppc_mpc8544ds.py index 8d6a749201..b599fb1cc9 100644 --- a/tests/avocado/ppc_mpc8544ds.py +++ b/tests/avocado/ppc_mpc8544ds.py @@ -22,9 +22,9 @@ class Mpc8544dsMachine(QemuSystemTest): :avocado: tags=accel:tcg """ self.require_accelerator("tcg") - tar_url = ('https://www.qemu-advent-calendar.org' - '/2020/download/day17.tar.gz') - tar_hash = '7a5239542a7c4257aa4d3b7f6ddf08fb6775c494' + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day04.tar.xz') + tar_hash = 'f46724d281a9f30fa892d458be7beb7d34dc25f9' file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) archive.extract(file_path, self.workdir) self.vm.set_console() diff --git a/tests/avocado/ppc_powernv.py b/tests/avocado/ppc_powernv.py new file mode 100644 index 0000000000..4342941d5d --- /dev/null +++ b/tests/avocado/ppc_powernv.py @@ -0,0 +1,102 @@ +# Test that Linux kernel boots on ppc powernv machines and check the console +# +# Copyright (c) 2018, 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. + +from avocado.utils import archive +from avocado_qemu import QemuSystemTest +from avocado_qemu import wait_for_console_pattern + +class powernvMachine(QemuSystemTest): + + timeout = 90 + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 ' + panic_message = 'Kernel panic - not syncing' + good_message = 'VFS: Cannot open root device' + + def do_test_linux_boot(self, command_line = KERNEL_COMMON_COMMAND_LINE): + self.require_accelerator("tcg") + kernel_url = ('https://archives.fedoraproject.org/pub/archive' + '/fedora-secondary/releases/29/Everything/ppc64le/os' + '/ppc/ppc64/vmlinuz') + kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + self.vm.set_console() + self.vm.add_args('-kernel', kernel_path, + '-append', command_line) + self.vm.launch() + + def test_linux_boot(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:powernv + :avocado: tags=accel:tcg + """ + + self.do_test_linux_boot() + console_pattern = 'VFS: Cannot open root device' + wait_for_console_pattern(self, console_pattern, self.panic_message) + + def test_linux_smp_boot(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:powernv + :avocado: tags=accel:tcg + """ + + self.vm.add_args('-smp', '4') + self.do_test_linux_boot() + console_pattern = 'smp: Brought up 1 node, 4 CPUs' + wait_for_console_pattern(self, console_pattern, self.panic_message) + wait_for_console_pattern(self, self.good_message, self.panic_message) + + def test_linux_smp_hpt_boot(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:powernv + :avocado: tags=accel:tcg + """ + + self.vm.add_args('-smp', '4') + self.do_test_linux_boot(self.KERNEL_COMMON_COMMAND_LINE + + 'disable_radix') + console_pattern = 'smp: Brought up 1 node, 4 CPUs' + wait_for_console_pattern(self, 'hash-mmu: Initializing hash mmu', + self.panic_message) + wait_for_console_pattern(self, console_pattern, self.panic_message) + wait_for_console_pattern(self, self.good_message, self.panic_message) + + def test_linux_smt_boot(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:powernv + :avocado: tags=accel:tcg + """ + + self.vm.add_args('-smp', '4,threads=4') + self.do_test_linux_boot() + console_pattern = 'CPU maps initialized for 4 threads per core' + wait_for_console_pattern(self, console_pattern, self.panic_message) + console_pattern = 'smp: Brought up 1 node, 4 CPUs' + wait_for_console_pattern(self, console_pattern, self.panic_message) + wait_for_console_pattern(self, self.good_message, self.panic_message) + + def test_linux_big_boot(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:powernv + :avocado: tags=accel:tcg + """ + + self.vm.add_args('-smp', '16,threads=4,cores=2,sockets=2') + + # powernv does not support NUMA + self.do_test_linux_boot() + console_pattern = 'CPU maps initialized for 4 threads per core' + wait_for_console_pattern(self, console_pattern, self.panic_message) + console_pattern = 'smp: Brought up 2 nodes, 16 CPUs' + wait_for_console_pattern(self, console_pattern, self.panic_message) + wait_for_console_pattern(self, self.good_message, self.panic_message) diff --git a/tests/avocado/ppc_pseries.py b/tests/avocado/ppc_pseries.py index d8b04dc3ea..74aaa4ac4a 100644 --- a/tests/avocado/ppc_pseries.py +++ b/tests/avocado/ppc_pseries.py @@ -12,14 +12,11 @@ from avocado_qemu import wait_for_console_pattern class pseriesMachine(QemuSystemTest): timeout = 90 - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 ' panic_message = 'Kernel panic - not syncing' + good_message = 'VFS: Cannot open root device' - def test_ppc64_pseries(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ + def do_test_ppc64_linux_boot(self, kernel_command_line = KERNEL_COMMON_COMMAND_LINE): kernel_url = ('https://archives.fedoraproject.org/pub/archive' '/fedora-secondary/releases/29/Everything/ppc64le/os' '/ppc/ppc64/vmlinuz') @@ -27,9 +24,87 @@ class pseriesMachine(QemuSystemTest): kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0' self.vm.add_args('-kernel', kernel_path, '-append', kernel_command_line) self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line + + def test_ppc64_vof_linux_boot(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + """ + + self.vm.add_args('-machine', 'x-vof=on') + self.do_test_ppc64_linux_boot() + console_pattern = 'VFS: Cannot open root device' wait_for_console_pattern(self, console_pattern, self.panic_message) + + def test_ppc64_linux_boot(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + """ + + self.do_test_ppc64_linux_boot() + console_pattern = 'VFS: Cannot open root device' + wait_for_console_pattern(self, console_pattern, self.panic_message) + + def test_ppc64_linux_smp_boot(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + """ + + self.vm.add_args('-smp', '4') + self.do_test_ppc64_linux_boot() + console_pattern = 'smp: Brought up 1 node, 4 CPUs' + wait_for_console_pattern(self, console_pattern, self.panic_message) + wait_for_console_pattern(self, self.good_message, self.panic_message) + + def test_ppc64_linux_hpt_smp_boot(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + """ + + self.vm.add_args('-smp', '4') + self.do_test_ppc64_linux_boot(self.KERNEL_COMMON_COMMAND_LINE + + 'disable_radix') + console_pattern = 'smp: Brought up 1 node, 4 CPUs' + wait_for_console_pattern(self, 'hash-mmu: Initializing hash mmu', + self.panic_message) + wait_for_console_pattern(self, console_pattern, self.panic_message) + wait_for_console_pattern(self, self.good_message, self.panic_message) + + def test_ppc64_linux_smt_boot(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + """ + + self.vm.add_args('-smp', '4,threads=4') + self.do_test_ppc64_linux_boot() + console_pattern = 'CPU maps initialized for 4 threads per core' + wait_for_console_pattern(self, console_pattern, self.panic_message) + console_pattern = 'smp: Brought up 1 node, 4 CPUs' + wait_for_console_pattern(self, console_pattern, self.panic_message) + wait_for_console_pattern(self, self.good_message, self.panic_message) + + def test_ppc64_linux_big_boot(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + """ + + self.vm.add_args('-smp', '16,threads=4,cores=2,sockets=2') + self.vm.add_args('-m', '512M', + '-object', 'memory-backend-ram,size=256M,id=m0', + '-object', 'memory-backend-ram,size=256M,id=m1') + self.vm.add_args('-numa', 'node,nodeid=0,memdev=m0') + self.vm.add_args('-numa', 'node,nodeid=1,memdev=m1') + self.do_test_ppc64_linux_boot() + console_pattern = 'CPU maps initialized for 4 threads per core' + wait_for_console_pattern(self, console_pattern, self.panic_message) + console_pattern = 'smp: Brought up 2 nodes, 16 CPUs' + wait_for_console_pattern(self, console_pattern, self.panic_message) + wait_for_console_pattern(self, self.good_message, self.panic_message) diff --git a/tests/avocado/ppc_virtex_ml507.py b/tests/avocado/ppc_virtex_ml507.py index 6b07686b56..a73f8ae396 100644 --- a/tests/avocado/ppc_virtex_ml507.py +++ b/tests/avocado/ppc_virtex_ml507.py @@ -22,9 +22,9 @@ class VirtexMl507Machine(QemuSystemTest): :avocado: tags=accel:tcg """ self.require_accelerator("tcg") - tar_url = ('https://www.qemu-advent-calendar.org' - '/2020/download/hippo.tar.gz') - tar_hash = '306b95bfe7d147f125aa176a877e266db8ef914a' + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day08.tar.xz') + tar_hash = '74c68f5af7a7b8f21c03097b298f3bb77ff52c1f' file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) archive.extract(file_path, self.workdir) self.vm.set_console() diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index 0b2b0dc692..10d99403a4 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -15,7 +15,7 @@ import logging import time from avocado import skip -from avocado import skipIf +from avocado import skipUnless from avocado import skipUnless from avocado_qemu import wait_for_console_pattern from avocado.utils import archive @@ -81,11 +81,30 @@ class ReplayKernelBase(LinuxKernelTest): logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) class ReplayKernelNormal(ReplayKernelBase): - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') + + def test_i386_pc(self): + """ + :avocado: tags=arch:i386 + :avocado: tags=machine:pc + """ + kernel_url = ('https://storage.tuxboot.com/20230331/i386/bzImage') + kernel_hash = 'a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956' + kernel_path = self.fetch_asset(kernel_url, + asset_hash=kernel_hash, + algorithm = "sha256") + + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'VFS: Cannot open root device' + + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + # See https://gitlab.com/qemu-project/qemu/-/issues/2094 + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test sometimes gets stuck') def test_x86_64_pc(self): """ :avocado: tags=arch:x86_64 :avocado: tags=machine:pc + :avocado: tags=flaky """ kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' '/linux/releases/29/Everything/x86_64/os/images/pxeboot' @@ -178,7 +197,6 @@ class ReplayKernelNormal(ReplayKernelBase): self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1) - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') def test_arm_cubieboard_initrd(self): """ :avocado: tags=arch:arm @@ -255,8 +273,24 @@ class ReplayKernelNormal(ReplayKernelBase): kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0' - # icount is not good enough for PPC64 for complete boot yet - console_pattern = 'Kernel command line: %s' % kernel_command_line + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern) + + def test_ppc64_powernv(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:powernv + :avocado: tags=accel:tcg + """ + kernel_url = ('https://archives.fedoraproject.org/pub/archive' + '/fedora-secondary/releases/29/Everything/ppc64le/os' + '/ppc/ppc64/vmlinuz') + kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + \ + 'console=tty0 console=hvc0' + console_pattern = 'VFS: Cannot open root device' self.run_rr(kernel_path, kernel_command_line, console_pattern) def test_m68k_q800(self): @@ -296,8 +330,8 @@ class ReplayKernelNormal(ReplayKernelBase): :avocado: tags=machine:vexpress-a9 """ tar_hash = '32b7677ce8b6f1471fb0059865f451169934245b' - tar_url = ('https://www.qemu-advent-calendar.org' - '/2018/download/day16.tar.xz') + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day16.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) dtb_path = self.workdir + '/day16/vexpress-v2p-ca9.dtb' self.do_test_advcal_2018(file_path, 'winter.zImage', @@ -309,20 +343,19 @@ class ReplayKernelNormal(ReplayKernelBase): :avocado: tags=machine:mcf5208evb """ tar_hash = 'ac688fd00561a2b6ce1359f9ff6aa2b98c9a570c' - tar_url = ('https://www.qemu-advent-calendar.org' - '/2018/download/day07.tar.xz') + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day07.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'sanity-clause.elf') - @skip("Test currently broken") # Console stuck as of 5.2-rc1 def test_microblaze_s3adsp1800(self): """ :avocado: tags=arch:microblaze :avocado: tags=machine:petalogix-s3adsp1800 """ tar_hash = '08bf3e3bfb6b6c7ce1e54ab65d54e189f2caf13f' - tar_url = ('https://www.qemu-advent-calendar.org' - '/2018/download/day17.tar.xz') + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day17.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'ballerina.bin') @@ -333,8 +366,8 @@ class ReplayKernelNormal(ReplayKernelBase): :avocado: tags=cpu:e5500 """ tar_hash = '6951d86d644b302898da2fd701739c9406527fe1' - tar_url = ('https://www.qemu-advent-calendar.org' - '/2018/download/day19.tar.xz') + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day19.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'uImage') @@ -344,8 +377,8 @@ class ReplayKernelNormal(ReplayKernelBase): :avocado: tags=machine:or1k-sim """ tar_hash = '20334cdaf386108c530ff0badaecc955693027dd' - tar_url = ('https://www.qemu-advent-calendar.org' - '/2018/download/day20.tar.xz') + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day20.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'vmlinux') @@ -355,8 +388,8 @@ class ReplayKernelNormal(ReplayKernelBase): :avocado: tags=machine:10m50-ghrd """ tar_hash = 'e4251141726c412ac0407c5a6bceefbbff018918' - tar_url = ('https://www.qemu-advent-calendar.org' - '/2018/download/day14.tar.xz') + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day14.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'vmlinux.elf') @@ -366,8 +399,8 @@ class ReplayKernelNormal(ReplayKernelBase): :avocado: tags=machine:g3beige """ tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' - tar_url = ('https://www.qemu-advent-calendar.org' - '/2018/download/day15.tar.xz') + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day15.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'invaders.elf', args=('-M', 'graphics=off')) @@ -378,8 +411,8 @@ class ReplayKernelNormal(ReplayKernelBase): :avocado: tags=machine:mac99 """ tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' - tar_url = ('https://www.qemu-advent-calendar.org' - '/2018/download/day15.tar.xz') + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day15.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'invaders.elf', args=('-M', 'graphics=off')) @@ -390,8 +423,8 @@ class ReplayKernelNormal(ReplayKernelBase): :avocado: tags=machine:SS-20 """ tar_hash = 'b18550d5d61c7615d989a06edace051017726a9f' - tar_url = ('https://www.qemu-advent-calendar.org' - '/2018/download/day11.tar.xz') + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day11.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'zImage.elf') @@ -402,8 +435,8 @@ class ReplayKernelNormal(ReplayKernelBase): :avocado: tags=cpu:dc233c """ tar_hash = '49e88d9933742f0164b60839886c9739cb7a0d34' - tar_url = ('https://www.qemu-advent-calendar.org' - '/2018/download/day02.tar.xz') + tar_url = ('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day02.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'santas-sleigh-ride.elf') @@ -492,7 +525,7 @@ class ReplayKernelSlow(ReplayKernelBase): :avocado: tags=endian:little :avocado: tags=cpu:I7200 """ - kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/' + kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' 'generic_nano32r6el_page4k.xz') kernel_hash = '477456aafd2a0f1ddc9482727f20fe9575565dd6' @@ -506,7 +539,7 @@ class ReplayKernelSlow(ReplayKernelBase): :avocado: tags=endian:little :avocado: tags=cpu:I7200 """ - kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/' + kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' 'generic_nano32r6el_page16k_up.xz') kernel_hash = 'e882868f944c71c816e832e2303b7874d044a7bc' @@ -520,7 +553,7 @@ class ReplayKernelSlow(ReplayKernelBase): :avocado: tags=endian:little :avocado: tags=cpu:I7200 """ - kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/' + kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' 'generic_nano32r6el_page64k_dbg.xz') kernel_hash = '18d1c68f2e23429e266ca39ba5349ccd0aeb7180' diff --git a/tests/avocado/replay_linux.py b/tests/avocado/replay_linux.py index 40e4f6908e..f3a43dc98c 100644 --- a/tests/avocado/replay_linux.py +++ b/tests/avocado/replay_linux.py @@ -48,13 +48,17 @@ class ReplayLinux(LinuxTest): bus_string = '' if self.bus: bus_string = ',bus=%s.%d' % (self.bus, id,) - vm.add_args('-drive', 'file=%s,snapshot,id=disk%s,if=none' % (path, id)) + vm.add_args('-drive', 'file=%s,snapshot=on,id=disk%s,if=none' % (path, id)) vm.add_args('-drive', 'driver=blkreplay,id=disk%s-rr,if=none,image=disk%s' % (id, id)) vm.add_args('-device', '%s,drive=disk%s-rr%s' % (device, id, bus_string)) + def vm_add_cdrom(self, vm, path, id, device): + vm.add_args('-drive', 'file=%s,id=disk%s,if=none,media=cdrom' % (path, id)) + def launch_and_wait(self, record, args, shift): + self.require_netdev('user') vm = self.get_vm() vm.add_args('-smp', '1') vm.add_args('-m', '1024') @@ -64,7 +68,7 @@ class ReplayLinux(LinuxTest): if args: vm.add_args(*args) self.vm_add_disk(vm, self.boot_path, 0, self.hdd) - self.vm_add_disk(vm, self.cloudinit_path, 1, self.cd) + self.vm_add_cdrom(vm, self.cloudinit_path, 1, self.cd) logger = logging.getLogger('replay') if record: logger.info('recording the execution...') @@ -92,8 +96,8 @@ class ReplayLinux(LinuxTest): % os.path.getsize(replay_path)) else: vm.event_wait('SHUTDOWN', self.timeout) - vm.shutdown(True) - logger.info('successfully fihished the replay') + vm.wait() + logger.info('successfully finished the replay') elapsed = time.time() - start_time logger.info('elapsed time %.2f sec' % elapsed) return elapsed @@ -189,3 +193,4 @@ class ReplayLinuxAarch64(ReplayLinux): self.run_rr(shift=3, args=(*self.get_common_args(), + "-machine", "virt,gic-version=3")) diff --git a/tests/avocado/reverse_debugging.py b/tests/avocado/reverse_debugging.py index d2921e70c3..92855a02a5 100644 --- a/tests/avocado/reverse_debugging.py +++ b/tests/avocado/reverse_debugging.py @@ -10,8 +10,9 @@ import os import logging -from avocado import skipIf +from avocado import skipUnless from avocado_qemu import BUILD_DIR +from avocado.utils import datadrainer from avocado.utils import gdb from avocado.utils import process from avocado.utils.network.ports import find_free_port @@ -52,6 +53,10 @@ class ReverseDebugging(LinuxKernelTest): if args: vm.add_args(*args) vm.launch() + console_drainer = datadrainer.LineLogger(vm.console_socket.fileno(), + logger=self.log.getChild('console'), + stop_check=(lambda : not vm.is_running())) + console_drainer.start() return vm @staticmethod @@ -150,16 +155,33 @@ class ReverseDebugging(LinuxKernelTest): self.check_pc(g, addr) logger.info('found position %x' % addr) - logger.info('seeking to the end (icount %s)' % (last_icount - 1)) - vm.qmp('replay-break', icount=last_icount - 1) - # continue - will return after pausing - g.cmd(b'c', b'T02thread:01;') + # visit the recorded instruction in forward order + logger.info('stepping forward') + for addr in steps: + self.check_pc(g, addr) + self.gdb_step(g) + logger.info('found position %x' % addr) + # set breakpoints for the instructions just stepped over logger.info('setting breakpoints') for addr in steps: # hardware breakpoint at addr with len=1 g.cmd(b'Z1,%x,1' % addr, b'OK') + # this may hit a breakpoint if first instructions are executed + # again + logger.info('continuing execution') + vm.qmp('replay-break', icount=last_icount - 1) + # continue - will return after pausing + # This could stop at the end and get a T02 return, or by + # re-executing one of the breakpoints and get a T05 return. + g.cmd(b'c') + if self.vm_get_icount(vm) == last_icount - 1: + logger.info('reached the end (icount %s)' % (last_icount - 1)) + else: + logger.info('hit a breakpoint again at %x (icount %s)' % + (self.get_pc(g), self.vm_get_icount(vm))) + logger.info('running reverse continue to reach %x' % steps[-1]) # reverse continue - will return after stopping at the breakpoint g.cmd(b'bc', b'T05thread:01;') @@ -169,10 +191,14 @@ class ReverseDebugging(LinuxKernelTest): self.check_pc(g, steps[-1]) logger.info('successfully reached %x' % steps[-1]) - logger.info('exitting gdb and qemu') + logger.info('exiting gdb and qemu') vm.shutdown() class ReverseDebugging_X86_64(ReverseDebugging): + """ + :avocado: tags=accel:tcg + """ + REG_PC = 0x10 REG_CS = 0x12 def get_pc(self, g): @@ -180,7 +206,8 @@ class ReverseDebugging_X86_64(ReverseDebugging): + self.get_reg_le(g, self.REG_CS) * 0x10 # unidentified gitlab timeout problem - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + def test_x86_64_pc(self): """ :avocado: tags=arch:x86_64 @@ -190,10 +217,15 @@ class ReverseDebugging_X86_64(ReverseDebugging): self.reverse_debugging() class ReverseDebugging_AArch64(ReverseDebugging): + """ + :avocado: tags=accel:tcg + """ + REG_PC = 32 # unidentified gitlab timeout problem - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + def test_aarch64_virt(self): """ :avocado: tags=arch:aarch64 @@ -208,3 +240,37 @@ class ReverseDebugging_AArch64(ReverseDebugging): self.reverse_debugging( args=('-kernel', kernel_path)) + +class ReverseDebugging_ppc64(ReverseDebugging): + """ + :avocado: tags=accel:tcg + """ + + REG_PC = 0x40 + + # unidentified gitlab timeout problem + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + + def test_ppc64_pseries(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + :avocado: tags=flaky + """ + # SLOF branches back to its entry point, which causes this test + # to take the 'hit a breakpoint again' path. That's not a problem, + # just slightly different than the other machines. + self.endian_is_le = False + self.reverse_debugging() + + # See https://gitlab.com/qemu-project/qemu/-/issues/1992 + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + + def test_ppc64_powernv(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:powernv + :avocado: tags=flaky + """ + self.endian_is_le = False + self.reverse_debugging() diff --git a/tests/avocado/riscv_opensbi.py b/tests/avocado/riscv_opensbi.py new file mode 100644 index 0000000000..bfff9cc3c3 --- /dev/null +++ b/tests/avocado/riscv_opensbi.py @@ -0,0 +1,63 @@ +# OpenSBI boot test for RISC-V machines +# +# Copyright (c) 2022, Ventana Micro +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado_qemu import QemuSystemTest +from avocado_qemu import wait_for_console_pattern + +class RiscvOpenSBI(QemuSystemTest): + """ + :avocado: tags=accel:tcg + """ + timeout = 5 + + def boot_opensbi(self): + self.vm.set_console() + self.vm.launch() + wait_for_console_pattern(self, 'Platform Name') + wait_for_console_pattern(self, 'Boot HART MEDELEG') + + def test_riscv32_spike(self): + """ + :avocado: tags=arch:riscv32 + :avocado: tags=machine:spike + """ + self.boot_opensbi() + + def test_riscv64_spike(self): + """ + :avocado: tags=arch:riscv64 + :avocado: tags=machine:spike + """ + self.boot_opensbi() + + def test_riscv32_sifive_u(self): + """ + :avocado: tags=arch:riscv32 + :avocado: tags=machine:sifive_u + """ + self.boot_opensbi() + + def test_riscv64_sifive_u(self): + """ + :avocado: tags=arch:riscv64 + :avocado: tags=machine:sifive_u + """ + self.boot_opensbi() + + def test_riscv32_virt(self): + """ + :avocado: tags=arch:riscv32 + :avocado: tags=machine:virt + """ + self.boot_opensbi() + + def test_riscv64_virt(self): + """ + :avocado: tags=arch:riscv64 + :avocado: tags=machine:virt + """ + self.boot_opensbi() diff --git a/tests/avocado/s390_topology.py b/tests/avocado/s390_topology.py new file mode 100644 index 0000000000..9154ac8776 --- /dev/null +++ b/tests/avocado/s390_topology.py @@ -0,0 +1,439 @@ +# Functional test that boots a Linux kernel and checks the console +# +# Copyright IBM Corp. 2023 +# +# Author: +# Pierre Morel +# +# 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 shutil +import time + +from avocado_qemu import QemuSystemTest +from avocado_qemu import exec_command +from avocado_qemu import exec_command_and_wait_for_pattern +from avocado_qemu import interrupt_interactive_console_until_pattern +from avocado_qemu import wait_for_console_pattern +from avocado.utils import process +from avocado.utils import archive + + +class S390CPUTopology(QemuSystemTest): + """ + S390x CPU topology consists of 4 topology layers, from bottom to top, + the cores, sockets, books and drawers and 2 modifiers attributes, + the entitlement and the dedication. + See: docs/system/s390x/cpu-topology.rst. + + S390x CPU topology is setup in different ways: + - implicitly from the '-smp' argument by completing each topology + level one after the other beginning with drawer 0, book 0 and + socket 0. + - explicitly from the '-device' argument on the QEMU command line + - explicitly by hotplug of a new CPU using QMP or HMP + - it is modified by using QMP 'set-cpu-topology' + + The S390x modifier attribute entitlement depends on the machine + polarization, which can be horizontal or vertical. + The polarization is changed on a request from the guest. + """ + timeout = 90 + event_timeout = 10 + + KERNEL_COMMON_COMMAND_LINE = ('printk.time=0 ' + 'root=/dev/ram ' + 'selinux=0 ' + 'rdinit=/bin/sh') + + def wait_until_booted(self): + wait_for_console_pattern(self, 'no job control', + failure_message='Kernel panic - not syncing', + vm=None) + + def check_topology(self, c, s, b, d, e, t): + res = self.vm.qmp('query-cpus-fast') + cpus = res['return'] + for cpu in cpus: + core = cpu['props']['core-id'] + socket = cpu['props']['socket-id'] + book = cpu['props']['book-id'] + drawer = cpu['props']['drawer-id'] + entitlement = cpu.get('entitlement') + dedicated = cpu.get('dedicated') + if core == c: + self.assertEqual(drawer, d) + self.assertEqual(book, b) + self.assertEqual(socket, s) + self.assertEqual(entitlement, e) + self.assertEqual(dedicated, t) + + def kernel_init(self): + """ + We need a VM that supports CPU topology, + currently this only the case when using KVM, not TCG. + We need a kernel supporting the CPU topology. + We need a minimal root filesystem with a shell. + """ + self.require_accelerator("kvm") + kernel_url = ('https://archives.fedoraproject.org/pub/archive' + '/fedora-secondary/releases/35/Server/s390x/os' + '/images/kernel.img') + kernel_hash = '0d1aaaf303f07cf0160c8c48e56fe638' + kernel_path = self.fetch_asset(kernel_url, algorithm='md5', + asset_hash=kernel_hash) + + initrd_url = ('https://archives.fedoraproject.org/pub/archive' + '/fedora-secondary/releases/35/Server/s390x/os' + '/images/initrd.img') + initrd_hash = 'a122057d95725ac030e2ec51df46e172' + initrd_path_xz = self.fetch_asset(initrd_url, algorithm='md5', + asset_hash=initrd_hash) + initrd_path = os.path.join(self.workdir, 'initrd-raw.img') + archive.lzma_uncompress(initrd_path_xz, initrd_path) + + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + self.vm.add_args('-nographic', + '-enable-kvm', + '-cpu', 'max,ctop=on', + '-m', '512', + '-kernel', kernel_path, + '-initrd', initrd_path, + '-append', kernel_command_line) + + def system_init(self): + self.log.info("System init") + exec_command_and_wait_for_pattern(self, + """ mount proc -t proc /proc; + mount sys -t sysfs /sys; + cat /sys/devices/system/cpu/dispatching """, + '0') + + def test_single(self): + """ + This test checks the simplest topology with a single CPU. + + :avocado: tags=arch:s390x + :avocado: tags=machine:s390-ccw-virtio + """ + self.kernel_init() + self.vm.launch() + self.wait_until_booted() + self.check_topology(0, 0, 0, 0, 'medium', False) + + def test_default(self): + """ + This test checks the implicit topology. + + :avocado: tags=arch:s390x + :avocado: tags=machine:s390-ccw-virtio + """ + self.kernel_init() + self.vm.add_args('-smp', + '13,drawers=2,books=2,sockets=3,cores=2,maxcpus=24') + self.vm.launch() + self.wait_until_booted() + self.check_topology(0, 0, 0, 0, 'medium', False) + self.check_topology(1, 0, 0, 0, 'medium', False) + self.check_topology(2, 1, 0, 0, 'medium', False) + self.check_topology(3, 1, 0, 0, 'medium', False) + self.check_topology(4, 2, 0, 0, 'medium', False) + self.check_topology(5, 2, 0, 0, 'medium', False) + self.check_topology(6, 0, 1, 0, 'medium', False) + self.check_topology(7, 0, 1, 0, 'medium', False) + self.check_topology(8, 1, 1, 0, 'medium', False) + self.check_topology(9, 1, 1, 0, 'medium', False) + self.check_topology(10, 2, 1, 0, 'medium', False) + self.check_topology(11, 2, 1, 0, 'medium', False) + self.check_topology(12, 0, 0, 1, 'medium', False) + + def test_move(self): + """ + This test checks the topology modification by moving a CPU + to another socket: CPU 0 is moved from socket 0 to socket 2. + + :avocado: tags=arch:s390x + :avocado: tags=machine:s390-ccw-virtio + """ + self.kernel_init() + self.vm.add_args('-smp', + '1,drawers=2,books=2,sockets=3,cores=2,maxcpus=24') + self.vm.launch() + self.wait_until_booted() + + self.check_topology(0, 0, 0, 0, 'medium', False) + res = self.vm.qmp('set-cpu-topology', + {'core-id': 0, 'socket-id': 2, 'entitlement': 'low'}) + self.assertEqual(res['return'], {}) + self.check_topology(0, 2, 0, 0, 'low', False) + + def test_dash_device(self): + """ + This test verifies that a CPU defined with the '-device' + command line option finds its right place inside the topology. + + :avocado: tags=arch:s390x + :avocado: tags=machine:s390-ccw-virtio + """ + self.kernel_init() + self.vm.add_args('-smp', + '1,drawers=2,books=2,sockets=3,cores=2,maxcpus=24') + self.vm.add_args('-device', 'max-s390x-cpu,core-id=10') + self.vm.add_args('-device', + 'max-s390x-cpu,' + 'core-id=1,socket-id=0,book-id=1,drawer-id=1,entitlement=low') + self.vm.add_args('-device', + 'max-s390x-cpu,' + 'core-id=2,socket-id=0,book-id=1,drawer-id=1,entitlement=medium') + self.vm.add_args('-device', + 'max-s390x-cpu,' + 'core-id=3,socket-id=1,book-id=1,drawer-id=1,entitlement=high') + self.vm.add_args('-device', + 'max-s390x-cpu,' + 'core-id=4,socket-id=1,book-id=1,drawer-id=1') + self.vm.add_args('-device', + 'max-s390x-cpu,' + 'core-id=5,socket-id=2,book-id=1,drawer-id=1,dedicated=true') + + self.vm.launch() + self.wait_until_booted() + + self.check_topology(10, 2, 1, 0, 'medium', False) + self.check_topology(1, 0, 1, 1, 'low', False) + self.check_topology(2, 0, 1, 1, 'medium', False) + self.check_topology(3, 1, 1, 1, 'high', False) + self.check_topology(4, 1, 1, 1, 'medium', False) + self.check_topology(5, 2, 1, 1, 'high', True) + + + def guest_set_dispatching(self, dispatching): + exec_command(self, + f'echo {dispatching} > /sys/devices/system/cpu/dispatching') + self.vm.event_wait('CPU_POLARIZATION_CHANGE', self.event_timeout) + exec_command_and_wait_for_pattern(self, + 'cat /sys/devices/system/cpu/dispatching', dispatching) + + + def test_polarization(self): + """ + This test verifies that QEMU modifies the entitlement change after + several guest polarization change requests. + + :avocado: tags=arch:s390x + :avocado: tags=machine:s390-ccw-virtio + """ + self.kernel_init() + self.vm.launch() + self.wait_until_booted() + + self.system_init() + res = self.vm.qmp('query-s390x-cpu-polarization') + self.assertEqual(res['return']['polarization'], 'horizontal') + self.check_topology(0, 0, 0, 0, 'medium', False) + + self.guest_set_dispatching('1'); + res = self.vm.qmp('query-s390x-cpu-polarization') + self.assertEqual(res['return']['polarization'], 'vertical') + self.check_topology(0, 0, 0, 0, 'medium', False) + + self.guest_set_dispatching('0'); + res = self.vm.qmp('query-s390x-cpu-polarization') + self.assertEqual(res['return']['polarization'], 'horizontal') + self.check_topology(0, 0, 0, 0, 'medium', False) + + + def check_polarization(self, polarization): + #We need to wait for the change to have been propagated to the kernel + exec_command_and_wait_for_pattern(self, + "\n".join([ + "timeout 1 sh -c 'while true", + 'do', + ' syspath="/sys/devices/system/cpu/cpu0/polarization"', + ' polarization="$(cat "$syspath")" || exit', + f' if [ "$polarization" = "{polarization}" ]; then', + ' exit 0', + ' fi', + ' sleep 0.01', + #searched for strings mustn't show up in command, '' to obfuscate + "done' && echo succ''ess || echo fail''ure", + ]), + "success", "failure") + + + def test_entitlement(self): + """ + This test verifies that QEMU modifies the entitlement + after a guest request and that the guest sees the change. + + :avocado: tags=arch:s390x + :avocado: tags=machine:s390-ccw-virtio + """ + self.kernel_init() + self.vm.launch() + self.wait_until_booted() + + self.system_init() + + self.check_polarization('horizontal') + self.check_topology(0, 0, 0, 0, 'medium', False) + + self.guest_set_dispatching('1') + self.check_polarization('vertical:medium') + self.check_topology(0, 0, 0, 0, 'medium', False) + + res = self.vm.qmp('set-cpu-topology', + {'core-id': 0, 'entitlement': 'low'}) + self.assertEqual(res['return'], {}) + self.check_polarization('vertical:low') + self.check_topology(0, 0, 0, 0, 'low', False) + + res = self.vm.qmp('set-cpu-topology', + {'core-id': 0, 'entitlement': 'medium'}) + self.assertEqual(res['return'], {}) + self.check_polarization('vertical:medium') + self.check_topology(0, 0, 0, 0, 'medium', False) + + res = self.vm.qmp('set-cpu-topology', + {'core-id': 0, 'entitlement': 'high'}) + self.assertEqual(res['return'], {}) + self.check_polarization('vertical:high') + self.check_topology(0, 0, 0, 0, 'high', False) + + self.guest_set_dispatching('0'); + self.check_polarization("horizontal") + self.check_topology(0, 0, 0, 0, 'high', False) + + + def test_dedicated(self): + """ + This test verifies that QEMU adjusts the entitlement correctly when a + CPU is made dedicated. + QEMU retains the entitlement value when horizontal polarization is in effect. + For the guest, the field shows the effective value of the entitlement. + + :avocado: tags=arch:s390x + :avocado: tags=machine:s390-ccw-virtio + """ + self.kernel_init() + self.vm.launch() + self.wait_until_booted() + + self.system_init() + + self.check_polarization("horizontal") + + res = self.vm.qmp('set-cpu-topology', + {'core-id': 0, 'dedicated': True}) + self.assertEqual(res['return'], {}) + self.check_topology(0, 0, 0, 0, 'high', True) + self.check_polarization("horizontal") + + self.guest_set_dispatching('1'); + self.check_topology(0, 0, 0, 0, 'high', True) + self.check_polarization("vertical:high") + + self.guest_set_dispatching('0'); + self.check_topology(0, 0, 0, 0, 'high', True) + self.check_polarization("horizontal") + + + def test_socket_full(self): + """ + This test verifies that QEMU does not accept to overload a socket. + The socket-id 0 on book-id 0 already contains CPUs 0 and 1 and can + not accept any new CPU while socket-id 0 on book-id 1 is free. + + :avocado: tags=arch:s390x + :avocado: tags=machine:s390-ccw-virtio + """ + self.kernel_init() + self.vm.add_args('-smp', + '3,drawers=2,books=2,sockets=3,cores=2,maxcpus=24') + self.vm.launch() + self.wait_until_booted() + + self.system_init() + + res = self.vm.qmp('set-cpu-topology', + {'core-id': 2, 'socket-id': 0, 'book-id': 0}) + self.assertEqual(res['error']['class'], 'GenericError') + + res = self.vm.qmp('set-cpu-topology', + {'core-id': 2, 'socket-id': 0, 'book-id': 1}) + self.assertEqual(res['return'], {}) + + def test_dedicated_error(self): + """ + This test verifies that QEMU refuses to lower the entitlement + of a dedicated CPU + + :avocado: tags=arch:s390x + :avocado: tags=machine:s390-ccw-virtio + """ + self.kernel_init() + self.vm.launch() + self.wait_until_booted() + + self.system_init() + + res = self.vm.qmp('set-cpu-topology', + {'core-id': 0, 'dedicated': True}) + self.assertEqual(res['return'], {}) + + self.check_topology(0, 0, 0, 0, 'high', True) + + self.guest_set_dispatching('1'); + + self.check_topology(0, 0, 0, 0, 'high', True) + + res = self.vm.qmp('set-cpu-topology', + {'core-id': 0, 'entitlement': 'low', 'dedicated': True}) + self.assertEqual(res['error']['class'], 'GenericError') + + res = self.vm.qmp('set-cpu-topology', + {'core-id': 0, 'entitlement': 'low'}) + self.assertEqual(res['error']['class'], 'GenericError') + + res = self.vm.qmp('set-cpu-topology', + {'core-id': 0, 'entitlement': 'medium', 'dedicated': True}) + self.assertEqual(res['error']['class'], 'GenericError') + + res = self.vm.qmp('set-cpu-topology', + {'core-id': 0, 'entitlement': 'medium'}) + self.assertEqual(res['error']['class'], 'GenericError') + + res = self.vm.qmp('set-cpu-topology', + {'core-id': 0, 'entitlement': 'low', 'dedicated': False}) + self.assertEqual(res['return'], {}) + + res = self.vm.qmp('set-cpu-topology', + {'core-id': 0, 'entitlement': 'medium', 'dedicated': False}) + self.assertEqual(res['return'], {}) + + def test_move_error(self): + """ + This test verifies that QEMU refuses to move a CPU to an + nonexistent location + + :avocado: tags=arch:s390x + :avocado: tags=machine:s390-ccw-virtio + """ + self.kernel_init() + self.vm.launch() + self.wait_until_booted() + + self.system_init() + + res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'drawer-id': 1}) + self.assertEqual(res['error']['class'], 'GenericError') + + res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'book-id': 1}) + self.assertEqual(res['error']['class'], 'GenericError') + + res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'socket-id': 1}) + self.assertEqual(res['error']['class'], 'GenericError') + + self.check_topology(0, 0, 0, 0, 'medium', False) diff --git a/tests/avocado/smmu.py b/tests/avocado/smmu.py index b3c4de6bf4..21ff030ca7 100644 --- a/tests/avocado/smmu.py +++ b/tests/avocado/smmu.py @@ -9,10 +9,11 @@ # later. See the COPYING file in the top-level directory. import os -from avocado import skipIf +from avocado import skipUnless from avocado_qemu import LinuxTest, BUILD_DIR -@skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') +@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + class SMMU(LinuxTest): """ :avocado: tags=accel:kvm @@ -21,6 +22,7 @@ class SMMU(LinuxTest): :avocado: tags=machine:virt :avocado: tags=distro:fedora :avocado: tags=smmu + :avocado: tags=flaky """ IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' diff --git a/tests/avocado/tcg_plugins.py b/tests/avocado/tcg_plugins.py index 642d2e49e3..15fd87b2c1 100644 --- a/tests/avocado/tcg_plugins.py +++ b/tests/avocado/tcg_plugins.py @@ -54,13 +54,11 @@ class PluginKernelBase(LinuxKernelTest): class PluginKernelNormal(PluginKernelBase): def _grab_aarch64_kernel(self): - kernel_url = ('http://security.debian.org/' - 'debian-security/pool/updates/main/l/linux-signed-arm64/' - 'linux-image-4.19.0-12-arm64_4.19.152-1_arm64.deb') - kernel_sha1 = '2036c2792f80ac9c4ccaae742b2e0a28385b6010' - kernel_deb = self.fetch_asset(kernel_url, asset_hash=kernel_sha1) - kernel_path = self.extract_from_deb(kernel_deb, - "/boot/vmlinuz-4.19.0-12-arm64") + kernel_url = ('https://storage.tuxboot.com/20230331/arm64/Image') + kernel_sha256 = 'ce95a7101a5fecebe0fe630deee6bd97b32ba41bc8754090e9ad8961ea8674c7' + kernel_path = self.fetch_asset(kernel_url, + asset_hash=kernel_sha256, + algorithm = "sha256") return kernel_path def test_aarch64_virt_insn(self): @@ -88,6 +86,10 @@ class PluginKernelNormal(PluginKernelBase): m = re.search(br"insns: (?P\d+)", s) if "count" not in m.groupdict(): self.fail("Failed to find instruction count") + else: + count = int(m.group("count")) + self.log.info(f"Counted: {count} instructions") + def test_aarch64_virt_insn_icount(self): """ @@ -111,9 +113,13 @@ class PluginKernelNormal(PluginKernelBase): with plugin_log as lf, \ mmap.mmap(lf.fileno(), 0, access=mmap.ACCESS_READ) as s: - m = re.search(br"detected repeat execution @ (?P0x[0-9A-Fa-f]+)", s) - if m is not None and "addr" in m.groupdict(): - self.fail("detected repeated instructions") + + m = re.search(br"insns: (?P\d+)", s) + if "count" not in m.groupdict(): + self.fail("Failed to find instruction count") + else: + count = int(m.group("count")) + self.log.info(f"Counted: {count} instructions") def test_aarch64_virt_mem_icount(self): """ @@ -145,3 +151,5 @@ class PluginKernelNormal(PluginKernelBase): callback = int(m[1]) if inline != callback: self.fail("mismatched access counts") + else: + self.log.info(f"Counted {inline} memory accesses") diff --git a/tests/avocado/tesseract_utils.py b/tests/avocado/tesseract_utils.py index 72cd9ab798..476f528147 100644 --- a/tests/avocado/tesseract_utils.py +++ b/tests/avocado/tesseract_utils.py @@ -21,13 +21,13 @@ def tesseract_available(expected_version): version = res.stdout_text.split()[1] except IndexError: version = res.stderr_text.split()[1] - return int(version.split('.')[0]) == expected_version + return int(version.split('.')[0]) >= expected_version match = re.match(r'tesseract\s(\d)', res) if match is None: return False # now this is guaranteed to be a digit - return int(match.groups()[0]) == expected_version + return int(match.groups()[0]) >= expected_version def tesseract_ocr(image_path, tesseract_args='', tesseract_version=3): diff --git a/tests/avocado/tuxrun_baselines.py b/tests/avocado/tuxrun_baselines.py new file mode 100644 index 0000000000..a936a3b780 --- /dev/null +++ b/tests/avocado/tuxrun_baselines.py @@ -0,0 +1,620 @@ +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import time +import tempfile + +from avocado import skip, skipUnless +from avocado_qemu import QemuSystemTest +from avocado_qemu import exec_command, exec_command_and_wait_for_pattern +from avocado_qemu import wait_for_console_pattern +from avocado.utils import process +from avocado.utils.path import find_command + +class TuxRunBaselineTest(QemuSystemTest): + """ + :avocado: tags=accel:tcg + """ + + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0' + # Tests are ~10-40s, allow for --debug/--enable-gcov overhead + timeout = 100 + + def get_tag(self, tagname, default=None): + """ + Get the metadata tag or return the default. + """ + utag = self._get_unique_tag_val(tagname) + print(f"{tagname}/{default} -> {utag}") + if utag: + return utag + + return default + + def setUp(self): + super().setUp() + + # We need zstd for all the tuxrun tests + # See https://github.com/avocado-framework/avocado/issues/5609 + zstd = find_command('zstd', False) + if zstd is False: + self.cancel('Could not find "zstd", which is required to ' + 'decompress rootfs') + self.zstd = zstd + + # Process the TuxRun specific tags, most machines work with + # reasonable defaults but we sometimes need to tweak the + # config. To avoid open coding everything we store all these + # details in the metadata for each test. + + # The tuxboot tag matches the root directory + self.tuxboot = self.get_tag('tuxboot') + + # Most Linux's use ttyS0 for their serial port + self.console = self.get_tag('console', "ttyS0") + + # Does the machine shutdown QEMU nicely on "halt" + self.shutdown = self.get_tag('shutdown') + + # The name of the kernel Image file + self.image = self.get_tag('image', "Image") + + self.root = self.get_tag('root', "vda") + + # Occasionally we need extra devices to hook things up + self.extradev = self.get_tag('extradev') + + self.qemu_img = super().get_qemu_img() + + def wait_for_console_pattern(self, success_message, vm=None): + wait_for_console_pattern(self, success_message, + failure_message='Kernel panic - not syncing', + vm=vm) + + def fetch_tuxrun_assets(self, csums=None, dt=None): + """ + Fetch the TuxBoot assets. They are stored in a standard way so we + use the per-test tags to fetch details. + """ + base_url = f"https://storage.tuxboot.com/20230331/{self.tuxboot}/" + + # empty hash if we weren't passed one + csums = {} if csums is None else csums + ksum = csums.get(self.image, None) + isum = csums.get("rootfs.ext4.zst", None) + + kernel_image = self.fetch_asset(base_url + self.image, + asset_hash = ksum, + algorithm = "sha256") + disk_image_zst = self.fetch_asset(base_url + "rootfs.ext4.zst", + asset_hash = isum, + algorithm = "sha256") + + cmd = f"{self.zstd} -d {disk_image_zst} -o {self.workdir}/rootfs.ext4" + process.run(cmd) + + if dt: + dsum = csums.get(dt, None) + dtb = self.fetch_asset(base_url + dt, + asset_hash = dsum, + algorithm = "sha256") + else: + dtb = None + + return (kernel_image, self.workdir + "/rootfs.ext4", dtb) + + def prepare_run(self, kernel, disk, drive, dtb=None, console_index=0): + """ + Setup to run and add the common parameters to the system + """ + self.vm.set_console(console_index=console_index) + + # all block devices are raw ext4's + blockdev = "driver=raw,file.driver=file," \ + + f"file.filename={disk},node-name=hd0" + + kcmd_line = self.KERNEL_COMMON_COMMAND_LINE + kcmd_line += f" root=/dev/{self.root}" + kcmd_line += f" console={self.console}" + + self.vm.add_args('-kernel', kernel, + '-append', kcmd_line, + '-blockdev', blockdev) + + # Sometimes we need extra devices attached + if self.extradev: + self.vm.add_args('-device', self.extradev) + + self.vm.add_args('-device', + f"{drive},drive=hd0") + + # Some machines need an explicit DTB + if dtb: + self.vm.add_args('-dtb', dtb) + + def run_tuxtest_tests(self, haltmsg): + """ + Wait for the system to boot up, wait for the login prompt and + then do a few things on the console. Trigger a shutdown and + wait to exit cleanly. + """ + self.wait_for_console_pattern("Welcome to TuxTest") + time.sleep(0.2) + exec_command(self, 'root') + time.sleep(0.2) + exec_command(self, 'cat /proc/interrupts') + time.sleep(0.1) + exec_command(self, 'cat /proc/self/maps') + time.sleep(0.1) + exec_command(self, 'uname -a') + time.sleep(0.1) + exec_command_and_wait_for_pattern(self, 'halt', haltmsg) + + # Wait for VM to shut down gracefully if it can + if self.shutdown == "nowait": + self.vm.shutdown() + else: + self.vm.wait() + + def common_tuxrun(self, + csums=None, + dt=None, + drive="virtio-blk-device", + haltmsg="reboot: System halted", + console_index=0): + """ + Common path for LKFT tests. Unless we need to do something + special with the command line we can process most things using + the tag metadata. + """ + (kernel, disk, dtb) = self.fetch_tuxrun_assets(csums, dt) + + self.prepare_run(kernel, disk, drive, dtb, console_index) + self.vm.launch() + self.run_tuxtest_tests(haltmsg) + + def ppc64_common_tuxrun(self, sums, prefix): + # add device args to command line. + self.require_netdev('user') + self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', + '-device', 'virtio-net,netdev=vnet') + self.vm.add_args('-netdev', '{"type":"user","id":"hostnet0"}', + '-device', '{"driver":"virtio-net-pci","netdev":' + '"hostnet0","id":"net0","mac":"52:54:00:4c:e3:86",' + '"bus":"pci.0","addr":"0x9"}') + self.vm.add_args('-device', '{"driver":"qemu-xhci","p2":15,"p3":15,' + '"id":"usb","bus":"pci.0","addr":"0x2"}') + self.vm.add_args('-device', '{"driver":"virtio-scsi-pci","id":"scsi0"' + ',"bus":"pci.0","addr":"0x3"}') + self.vm.add_args('-device', '{"driver":"virtio-serial-pci","id":' + '"virtio-serial0","bus":"pci.0","addr":"0x4"}') + self.vm.add_args('-device', '{"driver":"scsi-cd","bus":"scsi0.0"' + ',"channel":0,"scsi-id":0,"lun":0,"device_id":' + '"drive-scsi0-0-0-0","id":"scsi0-0-0-0"}') + self.vm.add_args('-device', '{"driver":"virtio-balloon-pci",' + '"id":"balloon0","bus":"pci.0","addr":"0x6"}') + self.vm.add_args('-audiodev', '{"id":"audio1","driver":"none"}') + self.vm.add_args('-device', '{"driver":"usb-tablet","id":"input0"' + ',"bus":"usb.0","port":"1"}') + self.vm.add_args('-device', '{"driver":"usb-kbd","id":"input1"' + ',"bus":"usb.0","port":"2"}') + self.vm.add_args('-device', '{"driver":"VGA","id":"video0",' + '"vgamem_mb":16,"bus":"pci.0","addr":"0x7"}') + self.vm.add_args('-object', '{"qom-type":"rng-random","id":"objrng0"' + ',"filename":"/dev/urandom"}', + '-device', '{"driver":"virtio-rng-pci","rng":"objrng0"' + ',"id":"rng0","bus":"pci.0","addr":"0x8"}') + self.vm.add_args('-object', '{"qom-type":"cryptodev-backend-builtin",' + '"id":"objcrypto0","queues":1}', + '-device', '{"driver":"virtio-crypto-pci",' + '"cryptodev":"objcrypto0","id":"crypto0","bus"' + ':"pci.0","addr":"0xa"}') + self.vm.add_args('-device', '{"driver":"spapr-pci-host-bridge"' + ',"index":1,"id":"pci.1"}') + self.vm.add_args('-device', '{"driver":"spapr-vscsi","id":"scsi1"' + ',"reg":12288}') + self.vm.add_args('-m', '2G,slots=32,maxmem=4G', + '-object', 'memory-backend-ram,id=ram1,size=1G', + '-device', 'pc-dimm,id=dimm1,memdev=ram1') + + # Create a temporary qcow2 and launch the test-case + with tempfile.NamedTemporaryFile(prefix=prefix, + suffix='.qcow2') as qcow2: + process.run(self.qemu_img + ' create -f qcow2 ' + + qcow2.name + ' 1G') + + self.vm.add_args('-drive', 'file=' + qcow2.name + + ',format=qcow2,if=none,id=' + 'drive-virtio-disk1', + '-device', 'virtio-blk-pci,scsi=off,bus=pci.0,' + 'addr=0xb,drive=drive-virtio-disk1,id=virtio-disk1' + ',bootindex=2') + self.common_tuxrun(csums=sums, drive="scsi-hd") + + # + # The tests themselves. The configuration is derived from how + # tuxrun invokes qemu (with minor tweaks like using -blockdev + # consistently). The tuxrun equivalent is something like: + # + # tuxrun --device qemu-{ARCH} \ + # --kernel https://storage.tuxboot.com/{TUXBOOT}/{IMAGE} + # + + def test_arm64(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=cpu:cortex-a57 + :avocado: tags=machine:virt + :avocado: tags=tuxboot:arm64 + :avocado: tags=console:ttyAMA0 + :avocado: tags=shutdown:nowait + """ + sums = {"Image" : + "ce95a7101a5fecebe0fe630deee6bd97b32ba41bc8754090e9ad8961ea8674c7", + "rootfs.ext4.zst" : + "bbd5ed4b9c7d3f4ca19ba71a323a843c6b585e880115df3b7765769dbd9dd061"} + self.common_tuxrun(csums=sums) + + def test_arm64be(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=cpu:cortex-a57 + :avocado: tags=endian:big + :avocado: tags=machine:virt + :avocado: tags=tuxboot:arm64be + :avocado: tags=console:ttyAMA0 + :avocado: tags=shutdown:nowait + """ + sums = { "Image" : + "e0df4425eb2cd9ea9a283e808037f805641c65d8fcecc8f6407d8f4f339561b4", + "rootfs.ext4.zst" : + "e6ffd8813c8a335bc15728f2835f90539c84be7f8f5f691a8b01451b47fb4bd7"} + self.common_tuxrun(csums=sums) + + def test_armv5(self): + """ + :avocado: tags=arch:arm + :avocado: tags=cpu:arm926 + :avocado: tags=machine:versatilepb + :avocado: tags=tuxboot:armv5 + :avocado: tags=image:zImage + :avocado: tags=console:ttyAMA0 + :avocado: tags=shutdown:nowait + """ + sums = { "rootfs.ext4.zst" : + "17177afa74e7294da0642861f08c88ca3c836764299a54bf6d1ce276cb9712a5", + "versatile-pb.dtb" : + "0bc0c0b0858cefd3c32b385c0d66d97142ded29472a496f4f490e42fc7615b25", + "zImage" : + "c95af2f27647c12265d75e9df44c22ff5228c59855f54aaa70f41ec2842e3a4d" } + + self.common_tuxrun(csums=sums, + drive="virtio-blk-pci", + dt="versatile-pb.dtb") + + def test_armv7(self): + """ + :avocado: tags=arch:arm + :avocado: tags=cpu:cortex-a15 + :avocado: tags=machine:virt + :avocado: tags=tuxboot:armv7 + :avocado: tags=image:zImage + :avocado: tags=console:ttyAMA0 + :avocado: tags=shutdown:nowait + """ + sums = { "rootfs.ext4.zst" : + "ab1fbbeaddda1ffdd45c9405a28cd5370c20f23a7cbc809cc90dc9f243a8eb5a", + "zImage" : + "4c7a22e9f15875bec06bd2a29d822496571eb297d4f22694099ffcdb19077572" } + + self.common_tuxrun(csums=sums) + + def test_armv7be(self): + """ + :avocado: tags=arch:arm + :avocado: tags=cpu:cortex-a15 + :avocado: tags=endian:big + :avocado: tags=machine:virt + :avocado: tags=tuxboot:armv7be + :avocado: tags=image:zImage + :avocado: tags=console:ttyAMA0 + :avocado: tags=shutdown:nowait + """ + sums = {"rootfs.ext4.zst" : + "42ed46dd2d59986206c5b1f6cf35eab58fe3fd20c96b41aaa16b32f3f90a9835", + "zImage" : + "7facc62082b57af12015b08f7fdbaf2f123ba07a478367853ae12b219afc9f2f" } + + self.common_tuxrun(csums=sums) + + def test_i386(self): + """ + :avocado: tags=arch:i386 + :avocado: tags=cpu:coreduo + :avocado: tags=machine:q35 + :avocado: tags=tuxboot:i386 + :avocado: tags=image:bzImage + :avocado: tags=shutdown:nowait + """ + sums = {"bzImage" : + "a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956", + "rootfs.ext4.zst" : + "f15e66b2bf673a210ec2a4b2e744a80530b36289e04f5388aab812b97f69754a" } + + self.common_tuxrun(csums=sums, drive="virtio-blk-pci") + + def test_mips32(self): + """ + :avocado: tags=arch:mips + :avocado: tags=machine:malta + :avocado: tags=cpu:mips32r6-generic + :avocado: tags=endian:big + :avocado: tags=tuxboot:mips32 + :avocado: tags=image:vmlinux + :avocado: tags=root:sda + :avocado: tags=shutdown:nowait + """ + sums = { "rootfs.ext4.zst" : + "fc3da0b4c2f38d74c6d705123bb0f633c76ed953128f9d0859378c328a6d11a0", + "vmlinux" : + "bfd2172f8b17fb32970ca0c8c58f59c5a4ca38aa5855d920be3a69b5d16e52f0" } + + self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") + + def test_mips32el(self): + """ + :avocado: tags=arch:mipsel + :avocado: tags=machine:malta + :avocado: tags=cpu:mips32r6-generic + :avocado: tags=tuxboot:mips32el + :avocado: tags=image:vmlinux + :avocado: tags=root:sda + :avocado: tags=shutdown:nowait + """ + sums = { "rootfs.ext4.zst" : + "e799768e289fd69209c21f4dacffa11baea7543d5db101e8ce27e3bc2c41d90e", + "vmlinux" : + "8573867c68a8443db8de6d08bb33fb291c189ca2ca671471d3973a3e712096a3" } + + self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") + + def test_mips64(self): + """ + :avocado: tags=arch:mips64 + :avocado: tags=machine:malta + :avocado: tags=tuxboot:mips64 + :avocado: tags=endian:big + :avocado: tags=image:vmlinux + :avocado: tags=root:sda + :avocado: tags=shutdown:nowait + """ + sums = { "rootfs.ext4.zst" : + "69d91eeb04df3d8d172922c6993bb37d4deeb6496def75d8580f6f9de3e431da", + "vmlinux" : + "09010e51e4b8bcbbd2494786ffb48eca78f228e96e5c5438344b0eac4029dc61" } + + self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") + + def test_mips64el(self): + """ + :avocado: tags=arch:mips64el + :avocado: tags=machine:malta + :avocado: tags=tuxboot:mips64el + :avocado: tags=image:vmlinux + :avocado: tags=root:sda + :avocado: tags=shutdown:nowait + """ + sums = { "rootfs.ext4.zst" : + "fba585368f5915b1498ed081863474b2d7ec4e97cdd46d21bdcb2f9698f83de4", + "vmlinux" : + "d4e08965e2155c4cccce7c5f34d18fe34c636cda2f2c9844387d614950155266" } + + self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") + + def test_ppc32(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:ppce500 + :avocado: tags=cpu:e500mc + :avocado: tags=tuxboot:ppc32 + :avocado: tags=image:uImage + :avocado: tags=shutdown:nowait + """ + sums = { "rootfs.ext4.zst" : + "8885b9d999cc24d679542a02e9b6aaf48f718f2050ece6b8347074b6ee41dd09", + "uImage" : + "1a68f74b860fda022fb12e03c5efece8c2b8b590d96cca37a8481a3ae0b3f81f" } + + self.common_tuxrun(csums=sums, drive="virtio-blk-pci") + + def test_ppc64(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + :avocado: tags=cpu:POWER10 + :avocado: tags=endian:big + :avocado: tags=console:hvc0 + :avocado: tags=tuxboot:ppc64 + :avocado: tags=image:vmlinux + :avocado: tags=extradev:driver=spapr-vscsi + :avocado: tags=root:sda + """ + sums = { "rootfs.ext4.zst" : + "1d953e81a4379e537fc8e41e05a0a59d9b453eef97aa03d47866c6c45b00bdff", + "vmlinux" : + "f22a9b9e924174a4c199f4c7e5d91a2339fcfe51c6eafd0907dc3e09b64ab728" } + self.ppc64_common_tuxrun(sums, prefix='tuxrun_ppc64_') + + def test_ppc64le(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + :avocado: tags=cpu:POWER10 + :avocado: tags=console:hvc0 + :avocado: tags=tuxboot:ppc64le + :avocado: tags=image:vmlinux + :avocado: tags=extradev:driver=spapr-vscsi + :avocado: tags=root:sda + """ + sums = { "rootfs.ext4.zst" : + "b442678c93fb8abe1f7d3bfa20556488de6b475c22c8fed363f42cf81a0a3906", + "vmlinux" : + "979eb61b445a010fb13e2b927126991f8ceef9c590fa2be0996c00e293e80cf2" } + self.ppc64_common_tuxrun(sums, prefix='tuxrun_ppc64le_') + + def test_riscv32(self): + """ + :avocado: tags=arch:riscv32 + :avocado: tags=machine:virt + :avocado: tags=tuxboot:riscv32 + """ + sums = { "Image" : + "89599407d7334de629a40e7ad6503c73670359eb5f5ae9d686353a3d6deccbd5", + "fw_jump.elf" : + "f2ef28a0b77826f79d085d3e4aa686f1159b315eff9099a37046b18936676985", + "rootfs.ext4.zst" : + "7168d296d0283238ea73cd5a775b3dd608e55e04c7b92b76ecce31bb13108cba" } + + self.common_tuxrun(csums=sums) + + def test_riscv64(self): + """ + :avocado: tags=arch:riscv64 + :avocado: tags=machine:virt + :avocado: tags=tuxboot:riscv64 + """ + sums = { "Image" : + "cd634badc65e52fb63465ec99e309c0de0369f0841b7d9486f9729e119bac25e", + "fw_jump.elf" : + "6e3373abcab4305fe151b564a4c71110d833c21f2c0a1753b7935459e36aedcf", + "rootfs.ext4.zst" : + "b18e3a3bdf27be03da0b285e84cb71bf09eca071c3a087b42884b6982ed679eb" } + + self.common_tuxrun(csums=sums) + + def test_riscv32_maxcpu(self): + """ + :avocado: tags=arch:riscv32 + :avocado: tags=machine:virt + :avocado: tags=cpu:max + :avocado: tags=tuxboot:riscv32 + """ + sums = { "Image" : + "89599407d7334de629a40e7ad6503c73670359eb5f5ae9d686353a3d6deccbd5", + "fw_jump.elf" : + "f2ef28a0b77826f79d085d3e4aa686f1159b315eff9099a37046b18936676985", + "rootfs.ext4.zst" : + "7168d296d0283238ea73cd5a775b3dd608e55e04c7b92b76ecce31bb13108cba" } + + self.common_tuxrun(csums=sums) + + def test_riscv64_maxcpu(self): + """ + :avocado: tags=arch:riscv64 + :avocado: tags=machine:virt + :avocado: tags=cpu:max + :avocado: tags=tuxboot:riscv64 + """ + sums = { "Image" : + "cd634badc65e52fb63465ec99e309c0de0369f0841b7d9486f9729e119bac25e", + "fw_jump.elf" : + "6e3373abcab4305fe151b564a4c71110d833c21f2c0a1753b7935459e36aedcf", + "rootfs.ext4.zst" : + "b18e3a3bdf27be03da0b285e84cb71bf09eca071c3a087b42884b6982ed679eb" } + + self.common_tuxrun(csums=sums) + + def test_s390(self): + """ + :avocado: tags=arch:s390x + :avocado: tags=endian:big + :avocado: tags=tuxboot:s390 + :avocado: tags=image:bzImage + :avocado: tags=shutdown:nowait + """ + sums = { "bzImage" : + "0414e98dd1c3dafff8496c9cd9c28a5f8d04553bb5ba37e906a812b48d442ef0", + "rootfs.ext4.zst" : + "88c37c32276677f873a25ab9ec6247895b8e3e6f8259134de2a616080b8ab3fc" } + + self.common_tuxrun(csums=sums, + drive="virtio-blk-ccw", + haltmsg="Requesting system halt") + + # Note: some segfaults caused by unaligned userspace access + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + def test_sh4(self): + """ + :avocado: tags=arch:sh4 + :avocado: tags=machine:r2d + :avocado: tags=cpu:sh7785 + :avocado: tags=tuxboot:sh4 + :avocado: tags=image:zImage + :avocado: tags=root:sda + :avocado: tags=console:ttySC1 + :avocado: tags=flaky + """ + sums = { "rootfs.ext4.zst" : + "3592a7a3d5a641e8b9821449e77bc43c9904a56c30d45da0694349cfd86743fd", + "zImage" : + "29d9b2aba604a0f53a5dc3b5d0f2b8e35d497de1129f8ee5139eb6fdf0db692f" } + + # The test is currently too unstable to do much in userspace + # so we skip common_tuxrun and do a minimal boot and shutdown. + (kernel, disk, dtb) = self.fetch_tuxrun_assets(csums=sums) + + # the console comes on the second serial port + self.prepare_run(kernel, disk, + "driver=ide-hd,bus=ide.0,unit=0", + console_index=1) + self.vm.launch() + + self.wait_for_console_pattern("Welcome to TuxTest") + time.sleep(0.1) + exec_command(self, 'root') + time.sleep(0.1) + exec_command_and_wait_for_pattern(self, 'halt', + "reboot: System halted") + + def test_sparc64(self): + """ + :avocado: tags=arch:sparc64 + :avocado: tags=tuxboot:sparc64 + :avocado: tags=image:vmlinux + :avocado: tags=root:sda + :avocado: tags=shutdown:nowait + """ + + sums = { "rootfs.ext4.zst" : + "ad2f1dc436ab51583543d25d2c210cab478645d47078d30d129a66ab0e281d76", + "vmlinux" : + "e34313e4325ff21deaa3d38a502aa09a373ef62b9bd4d7f8f29388b688225c55" } + + self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") + + def test_x86_64(self): + """ + :avocado: tags=arch:x86_64 + :avocado: tags=machine:q35 + :avocado: tags=cpu:Nehalem + :avocado: tags=tuxboot:x86_64 + :avocado: tags=image:bzImage + :avocado: tags=root:sda + :avocado: tags=shutdown:nowait + """ + sums = { "bzImage" : + "2bc7480a669ee9b6b82500a236aba0c54233debe98cb968268fa230f52f03461", + "rootfs.ext4.zst" : + "b72ac729769b8f51c6dffb221113c9a063c774dbe1d66af30eb593c4e9999b4b" } + + self.common_tuxrun(csums=sums, + drive="driver=ide-hd,bus=ide.0,unit=0") diff --git a/tests/avocado/version.py b/tests/avocado/version.py index ded7f039c1..c6139568a1 100644 --- a/tests/avocado/version.py +++ b/tests/avocado/version.py @@ -15,10 +15,11 @@ from avocado_qemu import QemuSystemTest class Version(QemuSystemTest): """ :avocado: tags=quick + :avocado: tags=machine:none """ 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') - self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)') + res = self.vm.cmd('human-monitor-command', + command_line='info version') + self.assertRegex(res, r'^(\d+\.\d+\.\d)') diff --git a/tests/avocado/virtio-gpu.py b/tests/avocado/virtio-gpu.py index 2a249a3a2c..6091f614a4 100644 --- a/tests/avocado/virtio-gpu.py +++ b/tests/avocado/virtio-gpu.py @@ -36,13 +36,13 @@ class VirtioGPUx86(QemuSystemTest): KERNEL_COMMAND_LINE = "printk.time=0 console=ttyS0 rdinit=/bin/bash" KERNEL_URL = ( - "https://archives.fedoraproject.org/pub/fedora" + "https://archives.fedoraproject.org/pub/archive/fedora" "/linux/releases/33/Everything/x86_64/os/images" "/pxeboot/vmlinuz" ) KERNEL_HASH = '1433cfe3f2ffaa44de4ecfb57ec25dc2399cdecf' INITRD_URL = ( - "https://archives.fedoraproject.org/pub/fedora" + "https://archives.fedoraproject.org/pub/archive/fedora" "/linux/releases/33/Everything/x86_64/os/images" "/pxeboot/initrd.img" ) @@ -143,12 +143,14 @@ class VirtioGPUx86(QemuSystemTest): "-append", self.KERNEL_COMMAND_LINE, ) - self.vm.launch() + try: + self.vm.launch() + except: + # TODO: probably fails because we are missing the VirGL features + self.cancel("VirGL not enabled?") self.wait_for_console_pattern("as init process") - exec_command_and_wait_for_pattern( - self, "/usr/sbin/modprobe virtio_gpu", "" - ) - self.wait_for_console_pattern("features: +virgl -edid") + exec_command_and_wait_for_pattern(self, "/usr/sbin/modprobe virtio_gpu", + "features: +virgl +edid") self.vm.shutdown() qemu_sock.close() vugp.terminate() diff --git a/tests/avocado/virtio_check_params.py b/tests/avocado/virtio_check_params.py index 4093da8a67..5fe370a179 100644 --- a/tests/avocado/virtio_check_params.py +++ b/tests/avocado/virtio_check_params.py @@ -43,7 +43,7 @@ VM_DEV_PARAMS = {'virtio-scsi-pci': ['-device', 'virtio-scsi-pci,id=scsi0'], class VirtioMaxSegSettingsCheck(QemuSystemTest): @staticmethod def make_pattern(props): - pattern_items = ['{0} = \w+'.format(prop) for prop in props] + pattern_items = [r'{0} = \w+'.format(prop) for prop in props] return '|'.join(pattern_items) def query_virtqueue(self, vm, dev_type_name): @@ -51,8 +51,8 @@ class VirtioMaxSegSettingsCheck(QemuSystemTest): error = None props = None - output = vm.command('human-monitor-command', - command_line = 'info qtree') + output = vm.cmd('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) @@ -121,7 +121,7 @@ class VirtioMaxSegSettingsCheck(QemuSystemTest): # 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')] + machines = [m['name'] for m in vm.cmd('query-machines')] vm.shutdown() machines.remove('none') machines.remove('isapc') diff --git a/tests/avocado/virtio_version.py b/tests/avocado/virtio_version.py index c84e48813a..afe5e828b5 100644 --- a/tests/avocado/virtio_version.py +++ b/tests/avocado/virtio_version.py @@ -48,7 +48,8 @@ def pci_modern_device_id(virtio_devid): return virtio_devid + 0x1040 def devtype_implements(vm, devtype, implements): - return devtype in [d['name'] for d in vm.command('qom-list-types', implements=implements)] + return devtype in [d['name'] for d in + vm.cmd('qom-list-types', implements=implements)] def get_pci_interfaces(vm, devtype): interfaces = ('pci-express-device', 'conventional-pci-device') @@ -78,7 +79,7 @@ class VirtioVersionCheck(QemuSystemTest): vm.add_args('-S') vm.launch() - pcibuses = vm.command('query-pci') + pcibuses = vm.cmd('query-pci') alldevs = [dev for bus in pcibuses for dev in bus['devices']] devfortest = [dev for dev in alldevs if dev['qdev_id'] == 'devfortest'] diff --git a/tests/avocado/virtiofs_submounts.py b/tests/avocado/virtiofs_submounts.py deleted file mode 100644 index e6dc32ffd4..0000000000 --- a/tests/avocado/virtiofs_submounts.py +++ /dev/null @@ -1,217 +0,0 @@ -import logging -import re -import os -import subprocess -import time - -from avocado import skipUnless -from avocado_qemu import LinuxTest, BUILD_DIR -from avocado_qemu import has_cmds -from avocado_qemu import run_cmd -from avocado_qemu import wait_for_console_pattern -from avocado.utils import ssh - - -class VirtiofsSubmountsTest(LinuxTest): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=accel:kvm - """ - - def run(self, args, ignore_error=False): - stdout, stderr, ret = run_cmd(args) - - if ret != 0: - cmdline = ' '.join(args) - if not ignore_error: - self.fail(f'{cmdline}: Returned {ret}: {stderr}') - else: - self.log.warn(f'{cmdline}: Returned {ret}: {stderr}') - - return (stdout, stderr, ret) - - def set_up_shared_dir(self): - self.shared_dir = os.path.join(self.workdir, 'virtiofs-shared') - - os.mkdir(self.shared_dir) - - self.run(('cp', self.get_data('guest.sh'), - os.path.join(self.shared_dir, 'check.sh'))) - - self.run(('cp', self.get_data('guest-cleanup.sh'), - os.path.join(self.shared_dir, 'cleanup.sh'))) - - def set_up_virtiofs(self): - attmp = os.getenv('AVOCADO_TESTS_COMMON_TMPDIR') - self.vfsdsock = os.path.join(attmp, 'vfsdsock') - - self.run(('sudo', '-n', 'rm', '-f', self.vfsdsock), ignore_error=True) - - self.virtiofsd = \ - subprocess.Popen(('sudo', '-n', - 'tools/virtiofsd/virtiofsd', - f'--socket-path={self.vfsdsock}', - '-o', f'source={self.shared_dir}', - '-o', 'cache=always', - '-o', 'xattr', - '-o', 'announce_submounts', - '-f'), - stdout=subprocess.DEVNULL, - stderr=subprocess.PIPE, - universal_newlines=True) - - while not os.path.exists(self.vfsdsock): - if self.virtiofsd.poll() is not None: - self.fail('virtiofsd exited prematurely: ' + - self.virtiofsd.communicate()[1]) - time.sleep(0.1) - - self.run(('sudo', '-n', 'chmod', 'go+rw', self.vfsdsock)) - - self.vm.add_args('-chardev', - f'socket,id=vfsdsock,path={self.vfsdsock}', - '-device', - 'vhost-user-fs-pci,queue-size=1024,chardev=vfsdsock' \ - ',tag=host', - '-object', - 'memory-backend-file,id=mem,size=1G,' \ - 'mem-path=/dev/shm,share=on', - '-numa', - 'node,memdev=mem') - - def set_up_nested_mounts(self): - scratch_dir = os.path.join(self.shared_dir, 'scratch') - try: - os.mkdir(scratch_dir) - except FileExistsError: - pass - - args = ['bash', self.get_data('host.sh'), scratch_dir] - if self.seed: - args += [self.seed] - - out, _, _ = self.run(args) - seed = re.search(r'^Seed: \d+', out) - self.log.info(seed[0]) - - def mount_in_guest(self): - self.ssh_command('mkdir -p /mnt/host') - self.ssh_command('mount -t virtiofs host /mnt/host') - - def check_in_guest(self): - self.ssh_command('bash /mnt/host/check.sh /mnt/host/scratch/share') - - def live_cleanup(self): - self.ssh_command('bash /mnt/host/cleanup.sh /mnt/host/scratch') - - # It would be nice if the above was sufficient to make virtiofsd clear - # all references to the mounted directories (so they can be unmounted - # on the host), but unfortunately it is not. To do so, we have to - # resort to a remount. - self.ssh_command('mount -o remount /mnt/host') - - scratch_dir = os.path.join(self.shared_dir, 'scratch') - self.run(('bash', self.get_data('cleanup.sh'), scratch_dir)) - - @skipUnless(*has_cmds(('sudo -n', ('sudo', '-n', 'true')), - 'ssh-keygen', 'bash', 'losetup', 'mkfs.xfs', 'mount')) - def setUp(self): - vmlinuz = self.params.get('vmlinuz') - if vmlinuz is None: - """ - The Linux kernel supports FUSE auto-submounts only as of 5.10. - boot_linux.py currently provides Fedora 31, whose kernel is too - old, so this test cannot pass with the on-image kernel (you are - welcome to try, hence the option to force such a test with - -p vmlinuz=''). Therefore, for now the user must provide a - sufficiently new custom kernel, or effectively explicitly - request failure with -p vmlinuz=''. - Once an image with a sufficiently new kernel is available - (probably Fedora 34), we can make -p vmlinuz='' the default, so - that this parameter no longer needs to be specified. - """ - self.cancel('vmlinuz parameter not set; you must point it to a ' - 'Linux kernel binary to test (to run this test with ' \ - 'the on-image kernel, set it to an empty string)') - - self.seed = self.params.get('seed') - - self.ssh_key = os.path.join(self.workdir, 'id_ed25519') - - self.run(('ssh-keygen', '-N', '', '-t', 'ed25519', '-f', self.ssh_key)) - - pubkey = self.ssh_key + '.pub' - - super(VirtiofsSubmountsTest, self).setUp(pubkey) - - if vmlinuz: - self.vm.add_args('-kernel', vmlinuz, - '-append', 'console=ttyS0 root=/dev/sda1') - - self.require_accelerator("kvm") - self.vm.add_args('-accel', 'kvm') - - def tearDown(self): - try: - self.vm.shutdown() - except: - pass - - scratch_dir = os.path.join(self.shared_dir, 'scratch') - self.run(('bash', self.get_data('cleanup.sh'), scratch_dir), - ignore_error=True) - - def test_pre_virtiofsd_set_up(self): - self.set_up_shared_dir() - - self.set_up_nested_mounts() - - self.set_up_virtiofs() - self.launch_and_wait() - self.mount_in_guest() - self.check_in_guest() - - def test_pre_launch_set_up(self): - self.set_up_shared_dir() - self.set_up_virtiofs() - - self.set_up_nested_mounts() - - self.launch_and_wait() - self.mount_in_guest() - self.check_in_guest() - - def test_post_launch_set_up(self): - self.set_up_shared_dir() - self.set_up_virtiofs() - self.launch_and_wait() - - self.set_up_nested_mounts() - - self.mount_in_guest() - self.check_in_guest() - - def test_post_mount_set_up(self): - self.set_up_shared_dir() - self.set_up_virtiofs() - self.launch_and_wait() - self.mount_in_guest() - - self.set_up_nested_mounts() - - self.check_in_guest() - - def test_two_runs(self): - self.set_up_shared_dir() - - self.set_up_nested_mounts() - - self.set_up_virtiofs() - self.launch_and_wait() - self.mount_in_guest() - self.check_in_guest() - - self.live_cleanup() - self.set_up_nested_mounts() - - self.check_in_guest() diff --git a/tests/avocado/vnc.py b/tests/avocado/vnc.py index 187fd3febc..862c8996a8 100644 --- a/tests/avocado/vnc.py +++ b/tests/avocado/vnc.py @@ -53,6 +53,7 @@ def find_free_ports(count: int) -> List[int]: class Vnc(QemuSystemTest): """ :avocado: tags=vnc,quick + :avocado: tags=machine:none """ def test_no_vnc(self): self.vm.add_args('-nodefaults', '-S') @@ -87,9 +88,8 @@ class Vnc(QemuSystemTest): self.vm.add_args('-nodefaults', '-S', '-vnc', ':0,password=on') self.vm.launch() self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) - set_password_response = self.vm.qmp('change-vnc-password', - password='new_password') - self.assertEqual(set_password_response['return'], {}) + self.vm.cmd('change-vnc-password', + password='new_password') def test_change_listen(self): a, b, c = find_free_ports(3) @@ -104,12 +104,11 @@ class Vnc(QemuSystemTest): self.assertFalse(check_connect(b)) self.assertFalse(check_connect(c)) - res = self.vm.qmp('display-update', type='vnc', - addresses=[{'type': 'inet', 'host': VNC_ADDR, - 'port': str(b)}, - {'type': 'inet', 'host': VNC_ADDR, - 'port': str(c)}]) - self.assertEqual(res['return'], {}) + self.vm.cmd('display-update', type='vnc', + addresses=[{'type': 'inet', 'host': VNC_ADDR, + 'port': str(b)}, + {'type': 'inet', 'host': VNC_ADDR, + 'port': str(c)}]) self.assertEqual(self.vm.qmp('query-vnc')['return']['service'], str(b)) self.assertFalse(check_connect(a)) self.assertTrue(check_connect(b)) diff --git a/tests/avocado/x86_cpu_model_versions.py b/tests/avocado/x86_cpu_model_versions.py index a6edf74c1c..11101e02b9 100644 --- a/tests/avocado/x86_cpu_model_versions.py +++ b/tests/avocado/x86_cpu_model_versions.py @@ -84,7 +84,8 @@ class X86CPUModelAliases(avocado_qemu.QemuSystemTest): # with older QEMU versions that didn't have the versioned CPU model self.vm.add_args('-S') self.vm.launch() - cpus = dict((m['name'], m) for m in self.vm.command('query-cpu-definitions')) + cpus = dict((m['name'], m) for m in + self.vm.cmd('query-cpu-definitions')) self.assertFalse(cpus['Cascadelake-Server']['static'], 'unversioned Cascadelake-Server CPU model must not be static') @@ -115,98 +116,100 @@ class X86CPUModelAliases(avocado_qemu.QemuSystemTest): self.vm.add_args('-S') self.vm.launch() - cpus = dict((m['name'], m) for m in self.vm.command('query-cpu-definitions')) + cpus = dict((m['name'], m) for m in + self.vm.cmd('query-cpu-definitions')) self.assertFalse(cpus['Cascadelake-Server']['static'], 'unversioned Cascadelake-Server CPU model must not be static') - self.assertEquals(cpus['Cascadelake-Server'].get('alias-of'), 'Cascadelake-Server-v1', - 'Cascadelake-Server must be an alias of Cascadelake-Server-v1') + self.assertEqual(cpus['Cascadelake-Server'].get('alias-of'), + 'Cascadelake-Server-v1', + 'Cascadelake-Server must be an alias of Cascadelake-Server-v1') self.assertNotIn('alias-of', cpus['Cascadelake-Server-v1'], 'Cascadelake-Server-v1 must not be an alias') self.assertFalse(cpus['qemu64']['static'], 'unversioned qemu64 CPU model must not be static') - self.assertEquals(cpus['qemu64'].get('alias-of'), 'qemu64-v1', - 'qemu64 must be an alias of qemu64-v1') + self.assertEqual(cpus['qemu64'].get('alias-of'), 'qemu64-v1', + 'qemu64 must be an alias of qemu64-v1') self.assertNotIn('alias-of', cpus['qemu64-v1'], 'qemu64-v1 must not be an alias') self.validate_variant_aliases(cpus) # On pc-*-4.1, -noTSX and -IBRS models should be aliases: - self.assertEquals(cpus["Haswell"].get('alias-of'), - "Haswell-v1", + self.assertEqual(cpus["Haswell"].get('alias-of'), + "Haswell-v1", "Haswell must be an alias") - self.assertEquals(cpus["Haswell-noTSX"].get('alias-of'), - "Haswell-v2", + self.assertEqual(cpus["Haswell-noTSX"].get('alias-of'), + "Haswell-v2", "Haswell-noTSX must be an alias") - self.assertEquals(cpus["Haswell-IBRS"].get('alias-of'), - "Haswell-v3", + self.assertEqual(cpus["Haswell-IBRS"].get('alias-of'), + "Haswell-v3", "Haswell-IBRS must be an alias") - self.assertEquals(cpus["Haswell-noTSX-IBRS"].get('alias-of'), - "Haswell-v4", + self.assertEqual(cpus["Haswell-noTSX-IBRS"].get('alias-of'), + "Haswell-v4", "Haswell-noTSX-IBRS must be an alias") - self.assertEquals(cpus["Broadwell"].get('alias-of'), - "Broadwell-v1", + self.assertEqual(cpus["Broadwell"].get('alias-of'), + "Broadwell-v1", "Broadwell must be an alias") - self.assertEquals(cpus["Broadwell-noTSX"].get('alias-of'), - "Broadwell-v2", + self.assertEqual(cpus["Broadwell-noTSX"].get('alias-of'), + "Broadwell-v2", "Broadwell-noTSX must be an alias") - self.assertEquals(cpus["Broadwell-IBRS"].get('alias-of'), - "Broadwell-v3", + self.assertEqual(cpus["Broadwell-IBRS"].get('alias-of'), + "Broadwell-v3", "Broadwell-IBRS must be an alias") - self.assertEquals(cpus["Broadwell-noTSX-IBRS"].get('alias-of'), - "Broadwell-v4", + self.assertEqual(cpus["Broadwell-noTSX-IBRS"].get('alias-of'), + "Broadwell-v4", "Broadwell-noTSX-IBRS must be an alias") - self.assertEquals(cpus["Nehalem"].get('alias-of'), - "Nehalem-v1", + self.assertEqual(cpus["Nehalem"].get('alias-of'), + "Nehalem-v1", "Nehalem must be an alias") - self.assertEquals(cpus["Nehalem-IBRS"].get('alias-of'), - "Nehalem-v2", + self.assertEqual(cpus["Nehalem-IBRS"].get('alias-of'), + "Nehalem-v2", "Nehalem-IBRS must be an alias") - self.assertEquals(cpus["Westmere"].get('alias-of'), - "Westmere-v1", + self.assertEqual(cpus["Westmere"].get('alias-of'), + "Westmere-v1", "Westmere must be an alias") - self.assertEquals(cpus["Westmere-IBRS"].get('alias-of'), - "Westmere-v2", + self.assertEqual(cpus["Westmere-IBRS"].get('alias-of'), + "Westmere-v2", "Westmere-IBRS must be an alias") - self.assertEquals(cpus["SandyBridge"].get('alias-of'), - "SandyBridge-v1", + self.assertEqual(cpus["SandyBridge"].get('alias-of'), + "SandyBridge-v1", "SandyBridge must be an alias") - self.assertEquals(cpus["SandyBridge-IBRS"].get('alias-of'), - "SandyBridge-v2", + self.assertEqual(cpus["SandyBridge-IBRS"].get('alias-of'), + "SandyBridge-v2", "SandyBridge-IBRS must be an alias") - self.assertEquals(cpus["IvyBridge"].get('alias-of'), - "IvyBridge-v1", + self.assertEqual(cpus["IvyBridge"].get('alias-of'), + "IvyBridge-v1", "IvyBridge must be an alias") - self.assertEquals(cpus["IvyBridge-IBRS"].get('alias-of'), - "IvyBridge-v2", + self.assertEqual(cpus["IvyBridge-IBRS"].get('alias-of'), + "IvyBridge-v2", "IvyBridge-IBRS must be an alias") - self.assertEquals(cpus["Skylake-Client"].get('alias-of'), - "Skylake-Client-v1", + self.assertEqual(cpus["Skylake-Client"].get('alias-of'), + "Skylake-Client-v1", "Skylake-Client must be an alias") - self.assertEquals(cpus["Skylake-Client-IBRS"].get('alias-of'), - "Skylake-Client-v2", + self.assertEqual(cpus["Skylake-Client-IBRS"].get('alias-of'), + "Skylake-Client-v2", "Skylake-Client-IBRS must be an alias") - self.assertEquals(cpus["Skylake-Server"].get('alias-of'), - "Skylake-Server-v1", + self.assertEqual(cpus["Skylake-Server"].get('alias-of'), + "Skylake-Server-v1", "Skylake-Server must be an alias") - self.assertEquals(cpus["Skylake-Server-IBRS"].get('alias-of'), - "Skylake-Server-v2", + self.assertEqual(cpus["Skylake-Server-IBRS"].get('alias-of'), + "Skylake-Server-v2", "Skylake-Server-IBRS must be an alias") - self.assertEquals(cpus["EPYC"].get('alias-of'), - "EPYC-v1", + self.assertEqual(cpus["EPYC"].get('alias-of'), + "EPYC-v1", "EPYC must be an alias") - self.assertEquals(cpus["EPYC-IBPB"].get('alias-of'), - "EPYC-v2", + self.assertEqual(cpus["EPYC-IBPB"].get('alias-of'), + "EPYC-v2", "EPYC-IBPB must be an alias") self.validate_aliases(cpus) @@ -220,7 +223,8 @@ class X86CPUModelAliases(avocado_qemu.QemuSystemTest): self.vm.add_args('-S') self.vm.launch() - cpus = dict((m['name'], m) for m in self.vm.command('query-cpu-definitions')) + cpus = dict((m['name'], m) for m in + self.vm.cmd('query-cpu-definitions')) self.assertFalse(cpus['Cascadelake-Server']['static'], 'unversioned Cascadelake-Server CPU model must not be static') @@ -246,8 +250,8 @@ class CascadelakeArchCapabilities(avocado_qemu.QemuSystemTest): :avocado: tags=arch:x86_64 """ def get_cpu_prop(self, prop): - cpu_path = self.vm.command('query-cpus-fast')[0].get('qom-path') - return self.vm.command('qom-get', path=cpu_path, property=prop) + cpu_path = self.vm.cmd('query-cpus-fast')[0].get('qom-path') + return self.vm.cmd('qom-get', path=cpu_path, property=prop) def test_4_1(self): """ diff --git a/tests/bench/benchmark-crypto-akcipher.c b/tests/bench/benchmark-crypto-akcipher.c index 15e69557ed..5e68cb0a1c 100644 --- a/tests/bench/benchmark-crypto-akcipher.c +++ b/tests/bench/benchmark-crypto-akcipher.c @@ -24,14 +24,12 @@ static QCryptoAkCipher *create_rsa_akcipher(const uint8_t *priv_key, QCryptoHashAlgorithm hash) { QCryptoAkCipherOptions opt; - QCryptoAkCipher *rsa; opt.alg = QCRYPTO_AKCIPHER_ALG_RSA; opt.u.rsa.padding_alg = padding; opt.u.rsa.hash_alg = hash; - rsa = qcrypto_akcipher_new(&opt, QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, - priv_key, keylen, &error_abort); - return rsa; + return qcrypto_akcipher_new(&opt, QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + priv_key, keylen, &error_abort); } static void test_rsa_speed(const uint8_t *priv_key, size_t keylen, diff --git a/tests/bench/meson.build b/tests/bench/meson.build index 279a8fcc33..7e76338a52 100644 --- a/tests/bench/meson.build +++ b/tests/bench/meson.build @@ -3,6 +3,10 @@ qht_bench = executable('qht-bench', sources: 'qht-bench.c', dependencies: [qemuutil]) +executable('qtree-bench', + sources: 'qtree-bench.c', + dependencies: [qemuutil]) + executable('atomic_add-bench', sources: files('atomic_add-bench.c'), dependencies: [qemuutil], diff --git a/tests/bench/qtree-bench.c b/tests/bench/qtree-bench.c new file mode 100644 index 0000000000..f3d7edc76d --- /dev/null +++ b/tests/bench/qtree-bench.c @@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "qemu/osdep.h" +#include "qemu/qtree.h" +#include "qemu/timer.h" + +enum tree_op { + OP_LOOKUP, + OP_INSERT, + OP_REMOVE, + OP_REMOVE_ALL, + OP_TRAVERSE, +}; + +struct benchmark { + const char * const name; + enum tree_op op; + bool fill_on_init; +}; + +enum impl_type { + IMPL_GTREE, + IMPL_QTREE, +}; + +struct tree_implementation { + const char * const name; + enum impl_type type; +}; + +static const struct benchmark benchmarks[] = { + { + .name = "Lookup", + .op = OP_LOOKUP, + .fill_on_init = true, + }, + { + .name = "Insert", + .op = OP_INSERT, + .fill_on_init = false, + }, + { + .name = "Remove", + .op = OP_REMOVE, + .fill_on_init = true, + }, + { + .name = "RemoveAll", + .op = OP_REMOVE_ALL, + .fill_on_init = true, + }, + { + .name = "Traverse", + .op = OP_TRAVERSE, + .fill_on_init = true, + }, +}; + +static const struct tree_implementation impls[] = { + { + .name = "GTree", + .type = IMPL_GTREE, + }, + { + .name = "QTree", + .type = IMPL_QTREE, + }, +}; + +static int compare_func(const void *ap, const void *bp) +{ + const size_t *a = ap; + const size_t *b = bp; + + return *a - *b; +} + +static void init_empty_tree_and_keys(enum impl_type impl, + void **ret_tree, size_t **ret_keys, + size_t n_elems) +{ + size_t *keys = g_malloc_n(n_elems, sizeof(*keys)); + for (size_t i = 0; i < n_elems; i++) { + keys[i] = i; + } + + void *tree; + switch (impl) { + case IMPL_GTREE: + tree = g_tree_new(compare_func); + break; + case IMPL_QTREE: + tree = q_tree_new(compare_func); + break; + default: + g_assert_not_reached(); + } + + *ret_tree = tree; + *ret_keys = keys; +} + +static gboolean traverse_func(gpointer key, gpointer value, gpointer data) +{ + return FALSE; +} + +static inline void remove_all(void *tree, enum impl_type impl) +{ + switch (impl) { + case IMPL_GTREE: + g_tree_destroy(tree); + break; + case IMPL_QTREE: + q_tree_destroy(tree); + break; + default: + g_assert_not_reached(); + } +} + +static int64_t run_benchmark(const struct benchmark *bench, + enum impl_type impl, + size_t n_elems) +{ + void *tree; + size_t *keys; + + init_empty_tree_and_keys(impl, &tree, &keys, n_elems); + if (bench->fill_on_init) { + for (size_t i = 0; i < n_elems; i++) { + switch (impl) { + case IMPL_GTREE: + g_tree_insert(tree, &keys[i], &keys[i]); + break; + case IMPL_QTREE: + q_tree_insert(tree, &keys[i], &keys[i]); + break; + default: + g_assert_not_reached(); + } + } + } + + int64_t start_ns = get_clock(); + switch (bench->op) { + case OP_LOOKUP: + for (size_t i = 0; i < n_elems; i++) { + void *value; + switch (impl) { + case IMPL_GTREE: + value = g_tree_lookup(tree, &keys[i]); + break; + case IMPL_QTREE: + value = q_tree_lookup(tree, &keys[i]); + break; + default: + g_assert_not_reached(); + } + (void)value; + } + break; + case OP_INSERT: + for (size_t i = 0; i < n_elems; i++) { + switch (impl) { + case IMPL_GTREE: + g_tree_insert(tree, &keys[i], &keys[i]); + break; + case IMPL_QTREE: + q_tree_insert(tree, &keys[i], &keys[i]); + break; + default: + g_assert_not_reached(); + } + } + break; + case OP_REMOVE: + for (size_t i = 0; i < n_elems; i++) { + switch (impl) { + case IMPL_GTREE: + g_tree_remove(tree, &keys[i]); + break; + case IMPL_QTREE: + q_tree_remove(tree, &keys[i]); + break; + default: + g_assert_not_reached(); + } + } + break; + case OP_REMOVE_ALL: + remove_all(tree, impl); + break; + case OP_TRAVERSE: + switch (impl) { + case IMPL_GTREE: + g_tree_foreach(tree, traverse_func, NULL); + break; + case IMPL_QTREE: + q_tree_foreach(tree, traverse_func, NULL); + break; + default: + g_assert_not_reached(); + } + break; + default: + g_assert_not_reached(); + } + int64_t ns = get_clock() - start_ns; + + if (bench->op != OP_REMOVE_ALL) { + remove_all(tree, impl); + } + g_free(keys); + + return ns; +} + +int main(int argc, char *argv[]) +{ + size_t sizes[] = { + 32, + 1024, + 1024 * 4, + 1024 * 128, + 1024 * 1024, + }; + + double res[ARRAY_SIZE(benchmarks)][ARRAY_SIZE(impls)][ARRAY_SIZE(sizes)]; + for (int i = 0; i < ARRAY_SIZE(sizes); i++) { + size_t size = sizes[i]; + for (int j = 0; j < ARRAY_SIZE(impls); j++) { + const struct tree_implementation *impl = &impls[j]; + for (int k = 0; k < ARRAY_SIZE(benchmarks); k++) { + const struct benchmark *bench = &benchmarks[k]; + + /* warm-up run */ + run_benchmark(bench, impl->type, size); + + int64_t total_ns = 0; + int64_t n_runs = 0; + while (total_ns < 2e8 || n_runs < 5) { + total_ns += run_benchmark(bench, impl->type, size); + n_runs++; + } + double ns_per_run = (double)total_ns / n_runs; + + /* Throughput, in Mops/s */ + res[k][j][i] = size / ns_per_run * 1e3; + } + } + } + + printf("# Results' breakdown: Tree, Op and #Elements. Units: Mops/s\n"); + printf("%5s %10s ", "Tree", "Op"); + for (int i = 0; i < ARRAY_SIZE(sizes); i++) { + printf("%7zu ", sizes[i]); + } + printf("\n"); + char separator[97]; + for (int i = 0; i < ARRAY_SIZE(separator) - 1; i++) { + separator[i] = '-'; + } + separator[ARRAY_SIZE(separator) - 1] = '\0'; + printf("%s\n", separator); + for (int i = 0; i < ARRAY_SIZE(benchmarks); i++) { + for (int j = 0; j < ARRAY_SIZE(impls); j++) { + printf("%5s %10s ", impls[j].name, benchmarks[i].name); + for (int k = 0; k < ARRAY_SIZE(sizes); k++) { + printf("%7.2f ", res[i][j][k]); + if (j == 0) { + printf(" "); + } else { + if (res[i][0][k] != 0) { + double speedup = res[i][j][k] / res[i][0][k]; + printf("(%4.2fx) ", speedup); + } else { + printf("( ) "); + } + } + } + printf("\n"); + } + } + printf("%s\n", separator); + return 0; +} diff --git a/tests/check-block.sh b/tests/check-block.sh deleted file mode 100755 index 5de2c1ba0b..0000000000 --- a/tests/check-block.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/sh - -if [ "$#" -eq 0 ]; then - echo "Usage: $0 fmt..." >&2 - exit 99 -fi - -# Honor the SPEED environment variable, just like we do it for "meson test" -format_list="$@" -if [ "$SPEED" = "slow" ] || [ "$SPEED" = "thorough" ]; then - group= -else - group="-g auto" -fi - -skip() { - echo "1..0 #SKIP $*" - exit 0 -} - -if [ -z "$(find . -name 'qemu-system-*' -print)" ]; then - skip "No qemu-system binary available ==> Not running the qemu-iotests." -fi - -cd tests/qemu-iotests - -# QEMU_CHECK_BLOCK_AUTO is used to disable some unstable sub-tests -export QEMU_CHECK_BLOCK_AUTO=1 -export PYTHONUTF8=1 -# If make was called with -jN we want to call ./check with -j N. Extract the -# flag from MAKEFLAGS, so that if it absent (or MAKEFLAGS is not defined), JOBS -# would be an empty line otherwise JOBS is prepared string of flag with value: -# "-j N" -# Note, that the following works even if make was called with "-j N" or even -# "--jobs N", as all these variants becomes simply "-jN" in MAKEFLAGS variable. -JOBS=$(echo "$MAKEFLAGS" | sed -n 's/\(^\|.* \)-j\([0-9]\+\)\( .*\|$\)/-j \2/p') - -ret=0 -for fmt in $format_list ; do - ${PYTHON} ./check $JOBS -tap -$fmt $group || ret=1 -done - -exit $ret diff --git a/tests/data/acpi/microvm/APIC b/tests/data/acpi/microvm/APIC index 68dbd44a7e..672764e711 100644 Binary files a/tests/data/acpi/microvm/APIC and b/tests/data/acpi/microvm/APIC differ diff --git a/tests/data/acpi/microvm/APIC.ioapic2 b/tests/data/acpi/microvm/APIC.ioapic2 index 3063c52cd3..6f24fdb12c 100644 Binary files a/tests/data/acpi/microvm/APIC.ioapic2 and b/tests/data/acpi/microvm/APIC.ioapic2 differ diff --git a/tests/data/acpi/microvm/APIC.pcie b/tests/data/acpi/microvm/APIC.pcie index 4e8f6ed8d6..2239ca76a6 100644 Binary files a/tests/data/acpi/microvm/APIC.pcie and b/tests/data/acpi/microvm/APIC.pcie differ diff --git a/tests/data/acpi/pc/APIC b/tests/data/acpi/pc/APIC index 208331db53..868a3432f0 100644 Binary files a/tests/data/acpi/pc/APIC and b/tests/data/acpi/pc/APIC differ diff --git a/tests/data/acpi/pc/APIC.acpihmat b/tests/data/acpi/pc/APIC.acpihmat index 812c4603f2..125d1ff087 100644 Binary files a/tests/data/acpi/pc/APIC.acpihmat and b/tests/data/acpi/pc/APIC.acpihmat differ diff --git a/tests/data/acpi/pc/APIC.cphp b/tests/data/acpi/pc/APIC.cphp index 65cc4f4a9a..a2c2a24e5e 100644 Binary files a/tests/data/acpi/pc/APIC.cphp and b/tests/data/acpi/pc/APIC.cphp differ diff --git a/tests/data/acpi/pc/APIC.dimmpxm b/tests/data/acpi/pc/APIC.dimmpxm index d904d4a70d..9b5922bc72 100644 Binary files a/tests/data/acpi/pc/APIC.dimmpxm and b/tests/data/acpi/pc/APIC.dimmpxm differ diff --git a/tests/data/acpi/pc/DSDT b/tests/data/acpi/pc/DSDT index cc1223773e..c93ad6b7f8 100644 Binary files a/tests/data/acpi/pc/DSDT and b/tests/data/acpi/pc/DSDT differ diff --git a/tests/data/acpi/pc/DSDT.acpierst b/tests/data/acpi/pc/DSDT.acpierst index bb0593eeb8..f643fa2d03 100644 Binary files a/tests/data/acpi/pc/DSDT.acpierst and b/tests/data/acpi/pc/DSDT.acpierst differ diff --git a/tests/data/acpi/pc/DSDT.acpihmat b/tests/data/acpi/pc/DSDT.acpihmat index 2d0678eb83..9d3695ff28 100644 Binary files a/tests/data/acpi/pc/DSDT.acpihmat and b/tests/data/acpi/pc/DSDT.acpihmat differ diff --git a/tests/data/acpi/pc/DSDT.bridge b/tests/data/acpi/pc/DSDT.bridge index 77778c3a69..840b45f354 100644 Binary files a/tests/data/acpi/pc/DSDT.bridge and b/tests/data/acpi/pc/DSDT.bridge differ diff --git a/tests/data/acpi/pc/DSDT.cphp b/tests/data/acpi/pc/DSDT.cphp index af046b40b0..dbc0141b2b 100644 Binary files a/tests/data/acpi/pc/DSDT.cphp and b/tests/data/acpi/pc/DSDT.cphp differ diff --git a/tests/data/acpi/pc/DSDT.dimmpxm b/tests/data/acpi/pc/DSDT.dimmpxm index b56b2e0890..1294f655d4 100644 Binary files a/tests/data/acpi/pc/DSDT.dimmpxm and b/tests/data/acpi/pc/DSDT.dimmpxm differ diff --git a/tests/data/acpi/pc/DSDT.hpbridge b/tests/data/acpi/pc/DSDT.hpbridge index bb0593eeb8..8012b5eb31 100644 Binary files a/tests/data/acpi/pc/DSDT.hpbridge and b/tests/data/acpi/pc/DSDT.hpbridge differ diff --git a/tests/data/acpi/pc/DSDT.hpbrroot b/tests/data/acpi/pc/DSDT.hpbrroot index 6ff6f198c7..4fa0c6fe72 100644 Binary files a/tests/data/acpi/pc/DSDT.hpbrroot and b/tests/data/acpi/pc/DSDT.hpbrroot differ diff --git a/tests/data/acpi/pc/DSDT.ipmikcs b/tests/data/acpi/pc/DSDT.ipmikcs index 2e618e49d3..0a891baf45 100644 Binary files a/tests/data/acpi/pc/DSDT.ipmikcs and b/tests/data/acpi/pc/DSDT.ipmikcs differ diff --git a/tests/data/acpi/pc/DSDT.memhp b/tests/data/acpi/pc/DSDT.memhp index c32d28575b..9b442a64cf 100644 Binary files a/tests/data/acpi/pc/DSDT.memhp and b/tests/data/acpi/pc/DSDT.memhp differ diff --git a/tests/data/acpi/pc/DSDT.nohpet b/tests/data/acpi/pc/DSDT.nohpet index 623f06a900..1754c68788 100644 Binary files a/tests/data/acpi/pc/DSDT.nohpet and b/tests/data/acpi/pc/DSDT.nohpet differ diff --git a/tests/data/acpi/pc/DSDT.numamem b/tests/data/acpi/pc/DSDT.numamem index f0a3fa92de..9fc731d3d2 100644 Binary files a/tests/data/acpi/pc/DSDT.numamem and b/tests/data/acpi/pc/DSDT.numamem differ diff --git a/tests/data/acpi/pc/DSDT.roothp b/tests/data/acpi/pc/DSDT.roothp index cee3b4d80b..e654c83ebe 100644 Binary files a/tests/data/acpi/pc/DSDT.roothp and b/tests/data/acpi/pc/DSDT.roothp differ diff --git a/tests/data/acpi/pc/SSDT.dimmpxm b/tests/data/acpi/pc/SSDT.dimmpxm index ac55387d57..70f133412f 100644 Binary files a/tests/data/acpi/pc/SSDT.dimmpxm and b/tests/data/acpi/pc/SSDT.dimmpxm differ diff --git a/tests/data/acpi/q35/APIC b/tests/data/acpi/q35/APIC index 208331db53..868a3432f0 100644 Binary files a/tests/data/acpi/q35/APIC and b/tests/data/acpi/q35/APIC differ diff --git a/tests/data/acpi/q35/APIC.acpihmat b/tests/data/acpi/q35/APIC.acpihmat index 812c4603f2..125d1ff087 100644 Binary files a/tests/data/acpi/q35/APIC.acpihmat and b/tests/data/acpi/q35/APIC.acpihmat differ diff --git a/tests/data/acpi/q35/APIC.acpihmat-noinitiator b/tests/data/acpi/q35/APIC.acpihmat-noinitiator new file mode 100644 index 0000000000..9b5922bc72 Binary files /dev/null and b/tests/data/acpi/q35/APIC.acpihmat-noinitiator differ diff --git a/tests/data/acpi/q35/APIC.core-count b/tests/data/acpi/q35/APIC.core-count new file mode 100644 index 0000000000..d9d7ca9a89 Binary files /dev/null and b/tests/data/acpi/q35/APIC.core-count differ diff --git a/tests/data/acpi/q35/APIC.core-count2 b/tests/data/acpi/q35/APIC.core-count2 new file mode 100644 index 0000000000..4f24284434 Binary files /dev/null and b/tests/data/acpi/q35/APIC.core-count2 differ diff --git a/tests/data/acpi/q35/APIC.cphp b/tests/data/acpi/q35/APIC.cphp index 65cc4f4a9a..a2c2a24e5e 100644 Binary files a/tests/data/acpi/q35/APIC.cphp and b/tests/data/acpi/q35/APIC.cphp differ diff --git a/tests/data/acpi/q35/APIC.dimmpxm b/tests/data/acpi/q35/APIC.dimmpxm index d904d4a70d..9b5922bc72 100644 Binary files a/tests/data/acpi/q35/APIC.dimmpxm and b/tests/data/acpi/q35/APIC.dimmpxm differ diff --git a/tests/data/acpi/q35/APIC.thread-count b/tests/data/acpi/q35/APIC.thread-count new file mode 100644 index 0000000000..c27e87fcf1 Binary files /dev/null and b/tests/data/acpi/q35/APIC.thread-count differ diff --git a/tests/data/acpi/q35/APIC.thread-count2 b/tests/data/acpi/q35/APIC.thread-count2 new file mode 100644 index 0000000000..ac200ab7aa Binary files /dev/null and b/tests/data/acpi/q35/APIC.thread-count2 differ diff --git a/tests/data/acpi/q35/APIC.type4-count b/tests/data/acpi/q35/APIC.type4-count new file mode 100644 index 0000000000..ab60a6ef06 Binary files /dev/null and b/tests/data/acpi/q35/APIC.type4-count differ diff --git a/tests/data/acpi/q35/APIC.xapic b/tests/data/acpi/q35/APIC.xapic index c1969c35aa..83bd28325a 100644 Binary files a/tests/data/acpi/q35/APIC.xapic and b/tests/data/acpi/q35/APIC.xapic differ diff --git a/tests/data/acpi/q35/CEDT.cxl b/tests/data/acpi/q35/CEDT.cxl index b8fa06b00e..ff8203af07 100644 Binary files a/tests/data/acpi/q35/CEDT.cxl and b/tests/data/acpi/q35/CEDT.cxl differ diff --git a/tests/data/acpi/q35/DSDT b/tests/data/acpi/q35/DSDT index c1965f6051..fb89ae0ac6 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.acpierst b/tests/data/acpi/q35/DSDT.acpierst index cad26e3f0c..46fd25400b 100644 Binary files a/tests/data/acpi/q35/DSDT.acpierst and b/tests/data/acpi/q35/DSDT.acpierst differ diff --git a/tests/data/acpi/q35/DSDT.acpihmat b/tests/data/acpi/q35/DSDT.acpihmat index f24d4874bf..61c5bd52a4 100644 Binary files a/tests/data/acpi/q35/DSDT.acpihmat and b/tests/data/acpi/q35/DSDT.acpihmat differ diff --git a/tests/data/acpi/q35/DSDT.acpihmat-noinitiator b/tests/data/acpi/q35/DSDT.acpihmat-noinitiator new file mode 100644 index 0000000000..3aaa2bbdf5 Binary files /dev/null and b/tests/data/acpi/q35/DSDT.acpihmat-noinitiator differ diff --git a/tests/data/acpi/q35/DSDT.applesmc b/tests/data/acpi/q35/DSDT.applesmc new file mode 100644 index 0000000000..944209adea Binary files /dev/null and b/tests/data/acpi/q35/DSDT.applesmc differ diff --git a/tests/data/acpi/q35/DSDT.bridge b/tests/data/acpi/q35/DSDT.bridge index 424d51bd1c..d9938dba8f 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.core-count b/tests/data/acpi/q35/DSDT.core-count new file mode 100644 index 0000000000..a24b04cbdb Binary files /dev/null and b/tests/data/acpi/q35/DSDT.core-count differ diff --git a/tests/data/acpi/q35/DSDT.core-count2 b/tests/data/acpi/q35/DSDT.core-count2 new file mode 100644 index 0000000000..3a0cb8c581 Binary files /dev/null and b/tests/data/acpi/q35/DSDT.core-count2 differ diff --git a/tests/data/acpi/q35/DSDT.cphp b/tests/data/acpi/q35/DSDT.cphp index f1275606f6..20955d0aa3 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.cxl b/tests/data/acpi/q35/DSDT.cxl index c1206defed..afcdc0d0ba 100644 Binary files a/tests/data/acpi/q35/DSDT.cxl and b/tests/data/acpi/q35/DSDT.cxl differ diff --git a/tests/data/acpi/q35/DSDT.dimmpxm b/tests/data/acpi/q35/DSDT.dimmpxm index 76e451e829..228374b55b 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 6ad2411d0e..45f911ada5 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.ipmismbus b/tests/data/acpi/q35/DSDT.ipmismbus new file mode 100644 index 0000000000..e5d6811bee Binary files /dev/null and b/tests/data/acpi/q35/DSDT.ipmismbus differ diff --git a/tests/data/acpi/q35/DSDT.ivrs b/tests/data/acpi/q35/DSDT.ivrs index cad26e3f0c..46fd25400b 100644 Binary files a/tests/data/acpi/q35/DSDT.ivrs and b/tests/data/acpi/q35/DSDT.ivrs differ diff --git a/tests/data/acpi/q35/DSDT.memhp b/tests/data/acpi/q35/DSDT.memhp index 4e9cb3dc68..5ce081187a 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 eb5a1c7171..bdf36c4d57 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.multi-bridge b/tests/data/acpi/q35/DSDT.multi-bridge index 45808eb03b..1db43a69e4 100644 Binary files a/tests/data/acpi/q35/DSDT.multi-bridge and b/tests/data/acpi/q35/DSDT.multi-bridge differ diff --git a/tests/data/acpi/q35/DSDT.noacpihp b/tests/data/acpi/q35/DSDT.noacpihp new file mode 100644 index 0000000000..8bc16887e1 Binary files /dev/null and b/tests/data/acpi/q35/DSDT.noacpihp differ diff --git a/tests/data/acpi/q35/DSDT.nohpet b/tests/data/acpi/q35/DSDT.nohpet index 83d1aa00ac..c13e45e361 100644 Binary files a/tests/data/acpi/q35/DSDT.nohpet and b/tests/data/acpi/q35/DSDT.nohpet differ diff --git a/tests/data/acpi/q35/DSDT.numamem b/tests/data/acpi/q35/DSDT.numamem index 050aaa237b..ba6669437e 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/DSDT.pvpanic-isa b/tests/data/acpi/q35/DSDT.pvpanic-isa new file mode 100644 index 0000000000..6ad42873e9 Binary files /dev/null and b/tests/data/acpi/q35/DSDT.pvpanic-isa differ diff --git a/tests/data/acpi/q35/DSDT.thread-count b/tests/data/acpi/q35/DSDT.thread-count new file mode 100644 index 0000000000..a24b04cbdb Binary files /dev/null and b/tests/data/acpi/q35/DSDT.thread-count differ diff --git a/tests/data/acpi/q35/DSDT.thread-count2 b/tests/data/acpi/q35/DSDT.thread-count2 new file mode 100644 index 0000000000..3a0cb8c581 Binary files /dev/null and b/tests/data/acpi/q35/DSDT.thread-count2 differ diff --git a/tests/data/acpi/q35/DSDT.tis.tpm12 b/tests/data/acpi/q35/DSDT.tis.tpm12 index fb9dd1f059..e381ce4cbf 100644 Binary files a/tests/data/acpi/q35/DSDT.tis.tpm12 and b/tests/data/acpi/q35/DSDT.tis.tpm12 differ diff --git a/tests/data/acpi/q35/DSDT.tis.tpm2 b/tests/data/acpi/q35/DSDT.tis.tpm2 index 00d732e46f..a09253042c 100644 Binary files a/tests/data/acpi/q35/DSDT.tis.tpm2 and b/tests/data/acpi/q35/DSDT.tis.tpm2 differ diff --git a/tests/data/acpi/q35/DSDT.type4-count b/tests/data/acpi/q35/DSDT.type4-count new file mode 100644 index 0000000000..edc23198cd Binary files /dev/null and b/tests/data/acpi/q35/DSDT.type4-count differ diff --git a/tests/data/acpi/q35/DSDT.viot b/tests/data/acpi/q35/DSDT.viot index 1c3b4da5cb..64e81f5711 100644 Binary files a/tests/data/acpi/q35/DSDT.viot and b/tests/data/acpi/q35/DSDT.viot differ diff --git a/tests/data/acpi/q35/DSDT.xapic b/tests/data/acpi/q35/DSDT.xapic index 17552ce363..d4acd851c6 100644 Binary files a/tests/data/acpi/q35/DSDT.xapic and b/tests/data/acpi/q35/DSDT.xapic differ diff --git a/tests/data/acpi/q35/FACP.core-count b/tests/data/acpi/q35/FACP.core-count new file mode 100644 index 0000000000..31fa5dd19c Binary files /dev/null and b/tests/data/acpi/q35/FACP.core-count differ diff --git a/tests/data/acpi/q35/FACP.core-count2 b/tests/data/acpi/q35/FACP.core-count2 new file mode 100644 index 0000000000..31fa5dd19c Binary files /dev/null and b/tests/data/acpi/q35/FACP.core-count2 differ diff --git a/tests/data/acpi/q35/FACP.thread-count b/tests/data/acpi/q35/FACP.thread-count new file mode 100644 index 0000000000..31fa5dd19c Binary files /dev/null and b/tests/data/acpi/q35/FACP.thread-count differ diff --git a/tests/data/acpi/q35/FACP.thread-count2 b/tests/data/acpi/q35/FACP.thread-count2 new file mode 100644 index 0000000000..31fa5dd19c Binary files /dev/null and b/tests/data/acpi/q35/FACP.thread-count2 differ diff --git a/tests/data/acpi/q35/FACP.type4-count b/tests/data/acpi/q35/FACP.type4-count new file mode 100644 index 0000000000..31fa5dd19c Binary files /dev/null and b/tests/data/acpi/q35/FACP.type4-count differ diff --git a/tests/data/acpi/q35/HMAT.acpihmat-noinitiator b/tests/data/acpi/q35/HMAT.acpihmat-noinitiator new file mode 100644 index 0000000000..6494d11b9f Binary files /dev/null and b/tests/data/acpi/q35/HMAT.acpihmat-noinitiator differ diff --git a/tests/data/acpi/q35/IVRS.ivrs b/tests/data/acpi/q35/IVRS.ivrs index 17611202e5..7f9e91aabc 100644 Binary files a/tests/data/acpi/q35/IVRS.ivrs and b/tests/data/acpi/q35/IVRS.ivrs differ diff --git a/tests/data/acpi/q35/SRAT.acpihmat-noinitiator b/tests/data/acpi/q35/SRAT.acpihmat-noinitiator new file mode 100644 index 0000000000..a11d3119ab Binary files /dev/null and b/tests/data/acpi/q35/SRAT.acpihmat-noinitiator differ diff --git a/tests/data/acpi/q35/SSDT.dimmpxm b/tests/data/acpi/q35/SSDT.dimmpxm index 98e6f0e3f3..9ea4e0d0ce 100644 Binary files a/tests/data/acpi/q35/SSDT.dimmpxm and b/tests/data/acpi/q35/SSDT.dimmpxm differ diff --git a/tests/data/acpi/q35/VIOT.viot b/tests/data/acpi/q35/VIOT.viot index 9b179266cc..275c78fbe8 100644 Binary files a/tests/data/acpi/q35/VIOT.viot and b/tests/data/acpi/q35/VIOT.viot differ diff --git a/tests/data/acpi/virt/APIC b/tests/data/acpi/virt/APIC index 023f15f12e..179d274770 100644 Binary files a/tests/data/acpi/virt/APIC and b/tests/data/acpi/virt/APIC differ diff --git a/tests/data/acpi/virt/APIC.acpihmatvirt b/tests/data/acpi/virt/APIC.acpihmatvirt new file mode 100644 index 0000000000..68200204c6 Binary files /dev/null and b/tests/data/acpi/virt/APIC.acpihmatvirt differ diff --git a/tests/data/acpi/virt/APIC.memhp b/tests/data/acpi/virt/APIC.memhp deleted file mode 100644 index 023f15f12e..0000000000 Binary files a/tests/data/acpi/virt/APIC.memhp and /dev/null differ diff --git a/tests/data/acpi/virt/APIC.numamem b/tests/data/acpi/virt/APIC.numamem deleted file mode 100644 index 023f15f12e..0000000000 Binary files a/tests/data/acpi/virt/APIC.numamem and /dev/null differ diff --git a/tests/data/acpi/virt/APIC.topology b/tests/data/acpi/virt/APIC.topology new file mode 100644 index 0000000000..3a6ac525e7 Binary files /dev/null and b/tests/data/acpi/virt/APIC.topology differ diff --git a/tests/data/acpi/virt/DBG2 b/tests/data/acpi/virt/DBG2 index 86e6314f7b..0a05e1a47f 100644 Binary files a/tests/data/acpi/virt/DBG2 and b/tests/data/acpi/virt/DBG2 differ diff --git a/tests/data/acpi/virt/DSDT.numamem b/tests/data/acpi/virt/DSDT.acpihmatvirt similarity index 95% rename from tests/data/acpi/virt/DSDT.numamem rename to tests/data/acpi/virt/DSDT.acpihmatvirt index c475039907..aee6ba017c 100644 Binary files a/tests/data/acpi/virt/DSDT.numamem and b/tests/data/acpi/virt/DSDT.acpihmatvirt differ diff --git a/tests/data/acpi/virt/DSDT.topology b/tests/data/acpi/virt/DSDT.topology new file mode 100644 index 0000000000..501314c91b Binary files /dev/null and b/tests/data/acpi/virt/DSDT.topology differ diff --git a/tests/data/acpi/virt/FACP b/tests/data/acpi/virt/FACP index 1f764220f8..da0c3644cc 100644 Binary files a/tests/data/acpi/virt/FACP and b/tests/data/acpi/virt/FACP differ diff --git a/tests/data/acpi/virt/FACP.memhp b/tests/data/acpi/virt/FACP.memhp deleted file mode 100644 index 1f764220f8..0000000000 Binary files a/tests/data/acpi/virt/FACP.memhp and /dev/null differ diff --git a/tests/data/acpi/virt/FACP.numamem b/tests/data/acpi/virt/FACP.numamem deleted file mode 100644 index 1f764220f8..0000000000 Binary files a/tests/data/acpi/virt/FACP.numamem and /dev/null differ diff --git a/tests/data/acpi/virt/GTDT b/tests/data/acpi/virt/GTDT index 9408b71b59..7f330e04d1 100644 Binary files a/tests/data/acpi/virt/GTDT and b/tests/data/acpi/virt/GTDT differ diff --git a/tests/data/acpi/virt/GTDT.memhp b/tests/data/acpi/virt/GTDT.memhp deleted file mode 100644 index 9408b71b59..0000000000 Binary files a/tests/data/acpi/virt/GTDT.memhp and /dev/null differ diff --git a/tests/data/acpi/virt/GTDT.numamem b/tests/data/acpi/virt/GTDT.numamem deleted file mode 100644 index 9408b71b59..0000000000 Binary files a/tests/data/acpi/virt/GTDT.numamem and /dev/null differ diff --git a/tests/data/acpi/virt/HMAT.acpihmatvirt b/tests/data/acpi/virt/HMAT.acpihmatvirt new file mode 100644 index 0000000000..6494d11b9f Binary files /dev/null and b/tests/data/acpi/virt/HMAT.acpihmatvirt differ diff --git a/tests/data/acpi/virt/IORT.memhp b/tests/data/acpi/virt/IORT.memhp deleted file mode 100644 index 7efd0ce8a6..0000000000 Binary files a/tests/data/acpi/virt/IORT.memhp and /dev/null differ diff --git a/tests/data/acpi/virt/IORT.numamem b/tests/data/acpi/virt/IORT.numamem deleted file mode 100644 index 7efd0ce8a6..0000000000 Binary files a/tests/data/acpi/virt/IORT.numamem and /dev/null differ diff --git a/tests/data/acpi/virt/IORT.pxb b/tests/data/acpi/virt/IORT.pxb deleted file mode 100644 index 7efd0ce8a6..0000000000 Binary files a/tests/data/acpi/virt/IORT.pxb and /dev/null differ diff --git a/tests/data/acpi/virt/MCFG.memhp b/tests/data/acpi/virt/MCFG.memhp deleted file mode 100644 index f4ae3203a4..0000000000 Binary files a/tests/data/acpi/virt/MCFG.memhp and /dev/null differ diff --git a/tests/data/acpi/virt/MCFG.numamem b/tests/data/acpi/virt/MCFG.numamem deleted file mode 100644 index f4ae3203a4..0000000000 Binary files a/tests/data/acpi/virt/MCFG.numamem and /dev/null differ diff --git a/tests/data/acpi/virt/PPTT b/tests/data/acpi/virt/PPTT index f56ea63b36..7a1258ecf1 100644 Binary files a/tests/data/acpi/virt/PPTT and b/tests/data/acpi/virt/PPTT differ diff --git a/tests/data/acpi/virt/PPTT.acpihmatvirt b/tests/data/acpi/virt/PPTT.acpihmatvirt new file mode 100644 index 0000000000..4eef303a5b Binary files /dev/null and b/tests/data/acpi/virt/PPTT.acpihmatvirt differ diff --git a/tests/data/acpi/virt/PPTT.topology b/tests/data/acpi/virt/PPTT.topology new file mode 100644 index 0000000000..3fbcae5ff0 Binary files /dev/null and b/tests/data/acpi/virt/PPTT.topology differ diff --git a/tests/data/acpi/virt/SPCR b/tests/data/acpi/virt/SPCR index 24e0a579e7..cf0f2b7522 100644 Binary files a/tests/data/acpi/virt/SPCR and b/tests/data/acpi/virt/SPCR differ diff --git a/tests/data/acpi/virt/SPCR.memhp b/tests/data/acpi/virt/SPCR.memhp deleted file mode 100644 index 24e0a579e7..0000000000 Binary files a/tests/data/acpi/virt/SPCR.memhp and /dev/null differ diff --git a/tests/data/acpi/virt/SPCR.numamem b/tests/data/acpi/virt/SPCR.numamem deleted file mode 100644 index 24e0a579e7..0000000000 Binary files a/tests/data/acpi/virt/SPCR.numamem and /dev/null differ diff --git a/tests/data/acpi/virt/SRAT.acpihmatvirt b/tests/data/acpi/virt/SRAT.acpihmatvirt new file mode 100644 index 0000000000..6fe55dd7d0 Binary files /dev/null and b/tests/data/acpi/virt/SRAT.acpihmatvirt differ diff --git a/tests/data/acpi/virt/SSDT.memhp b/tests/data/acpi/virt/SSDT.memhp index 4c363a6d95..fb3dcde5a1 100644 Binary files a/tests/data/acpi/virt/SSDT.memhp and b/tests/data/acpi/virt/SSDT.memhp differ diff --git a/tests/data/qobject/qdict.txt b/tests/data/qobject/qdict.txt index 122fda4524..e2edc88161 100644 --- a/tests/data/qobject/qdict.txt +++ b/tests/data/qobject/qdict.txt @@ -1866,10 +1866,6 @@ blackfin: 4096 blackfin.c: 7552 blackfin.h: 1089 blackfin_sram.h: 1207 -blacklist.c: 8658 -blacklist.h: 108 -blackstamp.c: 9838 -BlackStamp_defconfig: 27434 blinken.h: 617 blizzard.c: 41338 blizzard.h: 249 diff --git a/tests/data/smbios/type11_blob b/tests/data/smbios/type11_blob new file mode 100644 index 0000000000..1d8fea4b0c Binary files /dev/null and b/tests/data/smbios/type11_blob differ diff --git a/tests/data/smbios/type11_blob.legacy b/tests/data/smbios/type11_blob.legacy new file mode 100644 index 0000000000..aef463aab9 Binary files /dev/null and b/tests/data/smbios/type11_blob.legacy differ diff --git a/tests/data/test-qga-config b/tests/data/test-qga-config index 4bb721a4a1..b6b7bc9dfd 100644 --- a/tests/data/test-qga-config +++ b/tests/data/test-qga-config @@ -5,4 +5,4 @@ path=/path/to/org.qemu.guest_agent.0 pidfile=/var/foo/qemu-ga.pid statedir=/var/state verbose=true -blacklist=guest-ping;guest-get-time +block-rpcs=guest-ping;guest-get-time diff --git a/tests/decode/check.sh b/tests/decode/check.sh deleted file mode 100755 index 95445a0115..0000000000 --- a/tests/decode/check.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# This work is licensed under the terms of the GNU LGPL, version 2 or later. -# See the COPYING.LIB file in the top-level directory. - -PYTHON=$1 -DECODETREE=$2 -E=0 - -# All of these tests should produce errors -for i in err_*.decode; do - if $PYTHON $DECODETREE $i > /dev/null 2> /dev/null; then - # Pass, aka failed to fail. - echo FAIL: $i 1>&2 - E=1 - fi -done - -for i in succ_*.decode; do - if ! $PYTHON $DECODETREE $i > /dev/null 2> /dev/null; then - echo FAIL:$i 1>&2 - fi -done - -exit $E diff --git a/tests/decode/err_field10.decode b/tests/decode/err_field10.decode new file mode 100644 index 0000000000..3e672b7459 --- /dev/null +++ b/tests/decode/err_field10.decode @@ -0,0 +1,7 @@ +# This work is licensed under the terms of the GNU LGPL, version 2 or later. +# See the COPYING.LIB file in the top-level directory. + +# Diagnose formats which refer to undefined fields +%field1 field2:3 +@fmt ........ ........ ........ ........ %field1 +insn 00000000 00000000 00000000 00000000 @fmt diff --git a/tests/decode/err_field7.decode b/tests/decode/err_field7.decode new file mode 100644 index 0000000000..51fad7ccea --- /dev/null +++ b/tests/decode/err_field7.decode @@ -0,0 +1,7 @@ +# This work is licensed under the terms of the GNU LGPL, version 2 or later. +# See the COPYING.LIB file in the top-level directory. + +# Diagnose fields whose definitions form a loop +%field1 field2:3 +%field2 field1:4 +insn 00000000 00000000 00000000 00000000 %field1 %field2 diff --git a/tests/decode/err_field8.decode b/tests/decode/err_field8.decode new file mode 100644 index 0000000000..cc47c08a45 --- /dev/null +++ b/tests/decode/err_field8.decode @@ -0,0 +1,8 @@ +# This work is licensed under the terms of the GNU LGPL, version 2 or later. +# See the COPYING.LIB file in the top-level directory. + +# Diagnose patterns which refer to undefined fields +&f1 f1 a +%field1 field2:3 +@fmt ........ ........ ........ .... a:4 &f1 +insn 00000000 00000000 00000000 0000 .... @fmt f1=%field1 diff --git a/tests/decode/err_field9.decode b/tests/decode/err_field9.decode new file mode 100644 index 0000000000..e7361d521b --- /dev/null +++ b/tests/decode/err_field9.decode @@ -0,0 +1,14 @@ +# This work is licensed under the terms of the GNU LGPL, version 2 or later. +# See the COPYING.LIB file in the top-level directory. + +# Diagnose fields where the format refers to a field defined in the +# pattern and the pattern refers to a field defined in the format. +# This is theoretically not impossible to implement, but is not +# supported by the script at this time. +&abcd a b c d +%refa a:3 +%refc c:4 +# Format defines 'c' and sets 'b' to an indirect ref to 'a' +@fmt ........ ........ ........ c:8 &abcd b=%refa +# Pattern defines 'a' and sets 'd' to an indirect ref to 'c' +insn 00000000 00000000 00000000 ........ @fmt d=%refc a=6 diff --git a/tests/decode/err_pattern_group_ident2.decode b/tests/decode/err_pattern_group_ident2.decode index bc859233b1..0abb7513e9 100644 --- a/tests/decode/err_pattern_group_ident2.decode +++ b/tests/decode/err_pattern_group_ident2.decode @@ -7,5 +7,5 @@ { top 00000000 00000000 00000000 00000000 sub1 00000000 00000000 00000000 ........ %sub1 -# comments are suposed to be indented +# comments are supposed to be indented } diff --git a/tests/decode/meson.build b/tests/decode/meson.build new file mode 100644 index 0000000000..b13fada980 --- /dev/null +++ b/tests/decode/meson.build @@ -0,0 +1,64 @@ +err_tests = [ + 'err_argset1.decode', + 'err_argset2.decode', + 'err_field1.decode', + 'err_field2.decode', + 'err_field3.decode', + 'err_field4.decode', + 'err_field5.decode', + 'err_field6.decode', + 'err_field7.decode', + 'err_field8.decode', + 'err_field9.decode', + 'err_field10.decode', + 'err_init1.decode', + 'err_init2.decode', + 'err_init3.decode', + 'err_init4.decode', + 'err_overlap1.decode', + 'err_overlap2.decode', + 'err_overlap3.decode', + 'err_overlap4.decode', + 'err_overlap5.decode', + 'err_overlap6.decode', + 'err_overlap7.decode', + 'err_overlap8.decode', + 'err_overlap9.decode', + 'err_pattern_group_empty.decode', + 'err_pattern_group_ident1.decode', + 'err_pattern_group_ident2.decode', + 'err_pattern_group_nest1.decode', + 'err_pattern_group_nest2.decode', + 'err_pattern_group_nest3.decode', + 'err_pattern_group_overlap1.decode', + 'err_width1.decode', + 'err_width2.decode', + 'err_width3.decode', + 'err_width4.decode', +] + +succ_tests = [ + 'succ_argset_type1.decode', + 'succ_function.decode', + 'succ_ident1.decode', + 'succ_named_field.decode', + 'succ_pattern_group_nest1.decode', + 'succ_pattern_group_nest2.decode', + 'succ_pattern_group_nest3.decode', + 'succ_pattern_group_nest4.decode', +] + +suite = 'decodetree' +decodetree = find_program(meson.project_source_root() / 'scripts/decodetree.py') + +foreach t: err_tests + test(fs.replace_suffix(t, ''), + decodetree, args: ['--output-null', '--test-for-error', files(t)], + suite: suite) +endforeach + +foreach t: succ_tests + test(fs.replace_suffix(t, ''), + decodetree, args: ['--output-null', files(t)], + suite: suite) +endforeach diff --git a/tests/decode/succ_named_field.decode b/tests/decode/succ_named_field.decode new file mode 100644 index 0000000000..e64b3f9356 --- /dev/null +++ b/tests/decode/succ_named_field.decode @@ -0,0 +1,19 @@ +# This work is licensed under the terms of the GNU LGPL, version 2 or later. +# See the COPYING.LIB file in the top-level directory. + +# field using a named_field +%imm_sz 8:8 sz:3 +insn 00000000 00000000 ........ 00000000 imm_sz=%imm_sz sz=1 + +# Ditto, via a format. Here a field in the format +# references a named field defined in the insn pattern: +&imm_a imm alpha +%foo 0:16 alpha:4 +@foo 00000001 ........ ........ ........ &imm_a imm=%foo +i1 ........ 00000000 ........ ........ @foo alpha=1 +i2 ........ 00000001 ........ ........ @foo alpha=2 + +# Here the named field is defined in the format and referenced +# from the insn pattern: +@bar 00000010 ........ ........ ........ &imm_a alpha=4 +i3 ........ 00000000 ........ ........ @bar imm=%foo diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index e68f91b853..5ba5b50ab9 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -6,7 +6,9 @@ NULL := SPACE := $(NULL) # COMMA := , -HOST_ARCH = $(if $(ARCH),$(ARCH),$(shell uname -m)) +HOST_ARCH = $(shell uname -m) +USER = $(if $(NOUSER),,$(shell id -un)) +UID = $(if $(NOUSER),,$(shell id -u)) DOCKER_FILES_DIR := $(SRC_PATH)/tests/docker/dockerfiles ifeq ($(HOST_ARCH),x86_64) @@ -14,8 +16,8 @@ DOCKER_DEFAULT_REGISTRY := registry.gitlab.com/qemu-project/qemu endif DOCKER_REGISTRY := $(if $(REGISTRY),$(REGISTRY),$(DOCKER_DEFAULT_REGISTRY)) -ENGINE := auto -DOCKER_SCRIPT=$(SRC_PATH)/tests/docker/docker.py --engine $(ENGINE) +RUNC ?= $(if $(shell command -v docker), docker, podman) +DOCKER_SCRIPT=$(SRC_PATH)/tests/docker/docker.py --engine $(RUNC) CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$) DOCKER_SRC_COPY := $(BUILD_DIR)/docker-src.$(CUR_TIME) @@ -35,15 +37,17 @@ docker-qemu-src: $(DOCKER_SRC_COPY) # General rule for building docker images. docker-image-%: $(DOCKER_FILES_DIR)/%.docker - $(call quiet-command,\ - $(DOCKER_SCRIPT) build -t qemu/$* -f $< \ - $(if $V,,--quiet) \ - $(if $(NOCACHE),--no-cache, \ - $(if $(DOCKER_REGISTRY),--registry $(DOCKER_REGISTRY))) \ - $(if $(NOUSER),,--add-current-user) \ - $(if $(EXTRA_FILES),--extra-files $(EXTRA_FILES))\ - $(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)),\ - "BUILD","$*") + $(call quiet-command, \ + DOCKER_BUILDKIT=1 $(RUNC) build \ + $(if $V,,--quiet) \ + $(if $(NOCACHE),--no-cache, \ + $(if $(DOCKER_REGISTRY),--cache-from $(DOCKER_REGISTRY)/qemu/$*)) \ + --build-arg BUILDKIT_INLINE_CACHE=1 \ + $(if $(NOUSER),, \ + --build-arg USER=$(USER) \ + --build-arg UID=$(UID)) \ + -t qemu/$* - < $< $(if $V,,> /dev/null),\ + "BUILD", $*) # Special rule for debootstraped binfmt linux-user images docker-binfmt-image-debian-%: $(DOCKER_FILES_DIR)/debian-bootstrap.docker @@ -69,40 +73,21 @@ docker-binfmt-image-debian-%: $(DOCKER_FILES_DIR)/debian-bootstrap.docker { echo "You will need to build $(EXECUTABLE)"; exit 1;},\ "CHECK", "debian-$* exists")) -# Enforce dependencies for composite images -# we don't run tests on intermediate images (used as base by another image) -DOCKER_PARTIAL_IMAGES := debian10 debian11 +# Special case cross-compiling x86_64 on non-x86_64 systems. ifeq ($(HOST_ARCH),x86_64) -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 # For non-x86 hosts not all cross-compilers have been packaged ifneq ($(HOST_ARCH),x86_64) -DOCKER_PARTIAL_IMAGES += debian-mips-cross debian-mipsel-cross debian-mips64el-cross +DOCKER_PARTIAL_IMAGES += debian-mipsel-cross debian-mips64el-cross DOCKER_PARTIAL_IMAGES += debian-ppc64el-cross DOCKER_PARTIAL_IMAGES += debian-s390x-cross DOCKER_PARTIAL_IMAGES += fedora endif -docker-image-debian-alpha-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-sh4-cross: docker-image-debian10 -docker-image-debian-sparc64-cross: docker-image-debian10 - -# The native build should never use the registry -docker-image-debian-native: DOCKER_REGISTRY= - -# base images should not add a local user -docker-image-debian10: NOUSER=1 -docker-image-debian11: NOUSER=1 - # alpine has no adduser docker-image-alpine: NOUSER=1 @@ -125,10 +110,6 @@ debian-toolchain-run = \ "PREPARE", $1)) debian-toolchain = $(call debian-toolchain-run,$(patsubst docker-image-%,%,$1)) -docker-image-debian-hexagon-cross: $(DOCKER_FILES_DIR)/debian-hexagon-cross.docker \ - $(DOCKER_FILES_DIR)/debian-hexagon-cross.d/build-toolchain.sh - $(call debian-toolchain, $@) - docker-image-debian-microblaze-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ $(DOCKER_FILES_DIR)/debian-microblaze-cross.d/build-toolchain.sh $(call debian-toolchain, $@) @@ -137,24 +118,9 @@ docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ $(DOCKER_FILES_DIR)/debian-nios2-cross.d/build-toolchain.sh $(call debian-toolchain, $@) -# Specialist build images, sometimes very limited tools -docker-image-debian-tricore-cross: docker-image-debian10 -docker-image-debian-all-test-cross: docker-image-debian10 -docker-image-debian-microblaze-cross: docker-image-debian10 -docker-image-debian-nios2-cross: docker-image-debian10 -docker-image-debian-powerpc-test-cross: docker-image-debian11 -docker-image-debian-riscv64-test-cross: docker-image-debian11 - # These images may be good enough for building tests but not for test builds -DOCKER_PARTIAL_IMAGES += debian-alpha-cross -DOCKER_PARTIAL_IMAGES += debian-powerpc-test-cross -DOCKER_PARTIAL_IMAGES += debian-hppa-cross -DOCKER_PARTIAL_IMAGES += debian-m68k-cross debian-mips64-cross DOCKER_PARTIAL_IMAGES += debian-microblaze-cross DOCKER_PARTIAL_IMAGES += debian-nios2-cross -DOCKER_PARTIAL_IMAGES += debian-riscv64-test-cross -DOCKER_PARTIAL_IMAGES += debian-sh4-cross debian-sparc64-cross -DOCKER_PARTIAL_IMAGES += debian-tricore-cross DOCKER_PARTIAL_IMAGES += debian-xtensa-cross DOCKER_PARTIAL_IMAGES += fedora-cris-cross @@ -171,7 +137,7 @@ DOCKER_TESTS := $(if $(TESTS), $(filter $(TESTS), $(__TESTS)), $(__TESTS)) $(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES),$(DOCKER_IMAGES)), \ $(foreach t,$(DOCKER_TESTS), \ $(eval .PHONY: docker-$t@$i) \ - $(eval docker-$t@$i: docker-image-$i; @$(MAKE) docker-run TEST=$t IMAGE=$i) \ + $(eval docker-$t@$i: docker-image-$i; @$(MAKE) docker-run TEST=$t IMAGE=qemu/$i) \ ) \ $(foreach t,$(DOCKER_TESTS), \ $(eval docker-all-tests: docker-$t@$i) \ @@ -180,7 +146,7 @@ $(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES),$(DOCKER_IMAGES)), \ ) docker: - @echo 'Build QEMU and run tests inside Docker or Podman containers' + @echo 'Build QEMU and run tests inside $(RUNC) containers' @echo @echo 'Available targets:' @echo @@ -206,6 +172,7 @@ docker: @echo ' TARGET_LIST=a,b,c Override target list in builds.' @echo ' EXTRA_CONFIGURE_OPTS="..."' @echo ' Extra configure options.' + @echo ' TEST_COMMAND="..." Override the default `make check` target.' @echo ' IMAGES="a b c ..": Restrict available images to subset.' @echo ' TESTS="x y z .." Restrict available tests to subset.' @echo ' J=[0..9]* Overrides the -jN parameter for make commands' @@ -219,8 +186,6 @@ docker: @echo ' EXECUTABLE= Include executable in image.' @echo ' EXTRA_FILES=" [... ]"' @echo ' Include extra files in image.' - @echo ' ENGINE=auto/docker/podman' - @echo ' Specify which container engine to run.' @echo ' REGISTRY=url Cache builds from registry (default:$(DOCKER_REGISTRY))' docker-help: docker @@ -245,13 +210,15 @@ docker-run: docker-qemu-src $(IMAGE) --executable $(EXECUTABLE), \ " COPYING $(EXECUTABLE) to $(IMAGE)")) $(call quiet-command, \ - $(DOCKER_SCRIPT) run \ - $(if $(NOUSER),,--run-as-current-user) \ + $(RUNC) run \ + --rm \ + $(if $(NOUSER),,-u $(UID)) \ --security-opt seccomp=unconfined \ $(if $(DEBUG),-ti,) \ $(if $(NETWORK),$(if $(subst $(NETWORK),,1),--net=$(NETWORK)),--net=none) \ -e TARGET_LIST=$(subst $(SPACE),$(COMMA),$(TARGET_LIST)) \ -e EXTRA_CONFIGURE_OPTS="$(EXTRA_CONFIGURE_OPTS)" \ + -e TEST_COMMAND="$(TEST_COMMAND)" \ -e V=$V -e J=$J -e DEBUG=$(DEBUG) \ -e SHOW_ENV=$(SHOW_ENV) \ $(if $(NOUSER),, \ diff --git a/tests/docker/common.rc b/tests/docker/common.rc index e6f8cee0d6..a611e6adf9 100755 --- a/tests/docker/common.rc +++ b/tests/docker/common.rc @@ -12,7 +12,7 @@ # the top-level directory. # This might be set by ENV of a docker container... it is always -# overriden by TARGET_LIST if the user sets it. We special case +# overridden by TARGET_LIST if the user sets it. We special case # "none" to allow for other options like --disable-tcg to restrict the # builds we eventually do. if test "$DEF_TARGET_LIST" = "none"; then @@ -63,12 +63,12 @@ check_qemu() { # default to make check unless the caller specifies if [ $# = 0 ]; then - INVOCATION="check" + INVOCATION="${TEST_COMMAND:-make $MAKEFLAGS check}" else - INVOCATION="$@" + INVOCATION="make $MAKEFLAGS $@" fi - make $MAKEFLAGS $INVOCATION + $INVOCATION } test_fail() diff --git a/tests/docker/docker.py b/tests/docker/docker.py index d0af2861b8..3b8a26704d 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -23,10 +23,10 @@ import enum import tempfile import re import signal +import getpass from tarfile import TarFile, TarInfo from io import StringIO, BytesIO from shutil import copy, rmtree -from pwd import getpwuid from datetime import datetime, timedelta @@ -186,7 +186,7 @@ def _check_binfmt_misc(executable): (binary)) return None, True - m = re.search("interpreter (\S+)\n", entry) + m = re.search(r"interpreter (\S+)\n", entry) interp = m.group(1) if interp and interp != executable: print("binfmt_misc for %s does not point to %s, using %s" % @@ -205,22 +205,17 @@ def _read_qemu_dockerfile(img_name): return _read_dockerfile(df) -def _dockerfile_preprocess(df): - out = "" +def _dockerfile_verify_flat(df): + "Verify we do not include other qemu/ layers" for l in df.splitlines(): if len(l.strip()) == 0 or l.startswith("#"): continue from_pref = "FROM qemu/" if l.startswith(from_pref): - # TODO: Alternatively we could replace this line with "FROM $ID" - # where $ID is the image's hex id obtained with - # $ docker images $IMAGE --format="{{.Id}}" - # but unfortunately that's not supported by RHEL 7. - inlining = _read_qemu_dockerfile(l[len(from_pref):]) - out += _dockerfile_preprocess(inlining) - continue - out += l + "\n" - return out + print("We no longer support multiple QEMU layers.") + print("Dockerfiles should be flat, ideally created by lcitool") + return False + return True class Docker(object): @@ -309,23 +304,10 @@ class Docker(object): if argv is None: argv = [] - # pre-calculate the docker checksum before any - # substitutions we make for caching - checksum = _text_checksum(_dockerfile_preprocess(dockerfile)) + if not _dockerfile_verify_flat(dockerfile): + return -1 - if registry is not None: - sources = re.findall("FROM qemu\/(.*)", dockerfile) - # Fetch any cache layers we can, may fail - for s in sources: - pull_args = ["pull", "%s/qemu/%s" % (registry, s)] - if self._do(pull_args, quiet=quiet) != 0: - registry = None - break - # Make substitutions - if registry is not None: - dockerfile = dockerfile.replace("FROM qemu/", - "FROM %s/qemu/" % - (registry)) + checksum = _text_checksum(dockerfile) tmp_df = tempfile.NamedTemporaryFile(mode="w+t", encoding='utf-8', @@ -334,7 +316,7 @@ class Docker(object): if user: uid = os.getuid() - uname = getpwuid(uid).pw_name + uname = getpass.getuser() tmp_df.write("\n") tmp_df.write("RUN id %s 2>/dev/null || useradd -u %d -U %s" % (uname, uid, uname)) @@ -371,7 +353,7 @@ class Docker(object): checksum = self.get_image_dockerfile_checksum(tag) except Exception: return False - return checksum == _text_checksum(_dockerfile_preprocess(dockerfile)) + return checksum == _text_checksum(dockerfile) def run(self, cmd, keep, quiet, as_user=False): label = uuid.uuid4().hex @@ -588,7 +570,7 @@ class UpdateCommand(SubCommand): if args.user: uid = os.getuid() - uname = getpwuid(uid).pw_name + uname = getpass.getuser() df.write("\n") df.write("RUN id %s 2>/dev/null || useradd -u %d -U %s" % (uname, uid, uname)) diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index 3f4c0f95cb..42f6928627 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all alpine-edge qemu +# $ lcitool dockerfile --layers all alpine-318 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/alpine:edge +FROM docker.io/library/alpine:3.18 RUN apk update && \ apk upgrade && \ @@ -13,14 +13,15 @@ RUN apk update && \ attr-dev \ bash \ bc \ + bison \ bzip2 \ bzip2-dev \ ca-certificates \ capstone-dev \ ccache \ - cdrkit \ ceph-dev \ clang \ + cmocka-dev \ ctags \ curl-dev \ cyrus-sasl-dev \ @@ -29,6 +30,7 @@ RUN apk update && \ dtc-dev \ eudev-dev \ findutils \ + flex \ fuse3-dev \ g++ \ gcc \ @@ -39,6 +41,7 @@ RUN apk update && \ glib-static \ gnutls-dev \ gtk+3.0-dev \ + json-c-dev \ libaio-dev \ libbpf-dev \ libcap-ng-dev \ @@ -56,14 +59,17 @@ RUN apk update && \ libtasn1-dev \ liburing-dev \ libusb-dev \ + libxdp-dev \ linux-pam-dev \ - llvm11 \ + llvm \ lttng-ust-dev \ lzo-dev \ make \ mesa-dev \ meson \ + mtools \ multipath-tools \ + musl-dev \ ncurses-dev \ ndctl-dev \ net-tools \ @@ -72,7 +78,7 @@ RUN apk update && \ numactl-dev \ openssh-client \ pcre-dev \ - perl \ + pipewire-dev \ pixman-dev \ pkgconf \ pulseaudio-dev \ @@ -81,7 +87,6 @@ RUN apk update && \ py3-pip \ py3-sphinx \ py3-sphinx_rtd_theme \ - py3-virtualenv \ py3-yaml \ python3 \ rpm2cpio \ @@ -90,12 +95,14 @@ RUN apk update && \ sdl2_image-dev \ sed \ snappy-dev \ + sndio-dev \ + socat \ sparse \ spice-dev \ spice-protocol \ + swtpm \ tar \ tesseract-ocr \ - texinfo \ usbredir-dev \ util-linux \ vde2-dev \ @@ -104,8 +111,10 @@ RUN apk update && \ which \ xen-dev \ xfsprogs-dev \ + xorriso \ zlib-dev \ zlib-static \ + zstd \ zstd-dev && \ apk list | sort > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ @@ -115,8 +124,13 @@ RUN apk update && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker index 4b20925bbf..d97c30e96a 100644 --- a/tests/docker/dockerfiles/centos8.docker +++ b/tests/docker/dockerfiles/centos8.docker @@ -6,16 +6,18 @@ FROM quay.io/centos/centos:stream8 -RUN dnf update -y && \ +RUN dnf distro-sync -y && \ dnf install 'dnf-command(config-manager)' -y && \ dnf config-manager --set-enabled -y powertools && \ dnf install -y centos-release-advanced-virtualization && \ dnf install -y epel-release && \ + dnf install -y epel-next-release && \ dnf install -y \ SDL2-devel \ alsa-lib-devel \ bash \ bc \ + bison \ brlapi-devel \ bzip2 \ bzip2-devel \ @@ -30,10 +32,10 @@ RUN dnf update -y && \ device-mapper-multipath-devel \ diffutils \ findutils \ + flex \ fuse3-devel \ gcc \ gcc-c++ \ - genisoimage \ gettext \ git \ glib2-devel \ @@ -45,12 +47,14 @@ RUN dnf update -y && \ gtk3-devel \ hostname \ jemalloc-devel \ + json-c-devel \ libaio-devel \ libasan \ libattr-devel \ libbpf-devel \ libcacard-devel \ libcap-ng-devel \ + libcmocka-devel \ libcurl-devel \ libdrm-devel \ libepoxy-devel \ @@ -71,13 +75,14 @@ RUN dnf update -y && \ libubsan \ liburing-devel \ libusbx-devel \ + libxdp-devel \ libzstd-devel \ llvm \ lttng-ust-devel \ lzo-devel \ make \ mesa-libgbm-devel \ - meson \ + mtools \ ncurses-devel \ nettle-devel \ ninja-build \ @@ -86,36 +91,36 @@ RUN dnf update -y && \ openssh-clients \ pam-devel \ pcre-static \ - perl \ + pipewire-devel \ pixman-devel \ pkgconfig \ pulseaudio-libs-devel \ - python3 \ - python3-PyYAML \ - python3-numpy \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx_rtd_theme \ - python3-virtualenv \ + python38 \ + python38-PyYAML \ + python38-numpy \ + python38-pip \ + python38-setuptools \ + python38-wheel \ rdma-core-devel \ - rpm \ sed \ snappy-devel \ + socat \ spice-protocol \ spice-server-devel \ + swtpm \ systemd-devel \ systemtap-sdt-devel \ tar \ - texinfo \ usbredir-devel \ util-linux \ virglrenderer-devel \ vte291-devel \ which \ xfsprogs-devel \ + xorriso \ zlib-devel \ - zlib-static && \ + zlib-static \ + zstd && \ dnf autoremove -y && \ dnf clean all -y && \ rpm -qa | sort > /packages.txt && \ @@ -126,8 +131,20 @@ RUN dnf update -y && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc +RUN /usr/bin/pip3.8 install \ + meson==0.63.2 \ + pillow \ + sphinx \ + sphinx-rtd-theme \ + tomli + +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" -ENV PYTHON "/usr/bin/python3" -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" +ENV PYTHON "/usr/bin/python3.8" +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index dedcea58b4..2cc7a24d4d 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -6,20 +6,37 @@ # basic compilers for as many targets as possible. We shall use this # to build and run linux-user tests on GitLab # -FROM qemu/debian10 +FROM docker.io/library/debian:12-slim -# What we need to build QEMU itself -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ +# Duplicate deb line as deb-src +RUN sed -in "s/Types: deb/Types: deb deb-src/g" /etc/apt/sources.list.d/debian.sources + +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y eatmydata && \ + eatmydata apt-get dist-upgrade -y && \ apt build-dep -yy qemu -# Add the foreign architecture we want and install dependencies +# Add extra build tools and as many cross compilers as we can for testing +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + bison \ + ccache \ + clang \ + flex \ + git \ + libclang-rt-dev \ + ninja-build \ + python3-pip \ + python3-setuptools \ + python3-tomli \ + python3-venv \ + python3-wheel + RUN DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends \ gcc-aarch64-linux-gnu \ libc6-dev-arm64-cross \ - gcc-alpha-linux-gnu \ - libc6.1-dev-alpha-cross \ gcc-arm-linux-gnueabihf \ libc6-dev-armhf-cross \ gcc-hppa-linux-gnu \ @@ -44,10 +61,14 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ libc6-dev-riscv64-cross \ gcc-s390x-linux-gnu \ libc6-dev-s390x-cross \ - gcc-sh4-linux-gnu \ - libc6-dev-sh4-cross \ gcc-sparc64-linux-gnu \ libc6-dev-sparc64-cross + ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools -ENV DEF_TARGET_LIST aarch64-linux-user,alpha-linux-user,arm-linux-user,hppa-linux-user,i386-linux-user,m68k-linux-user,mips-linux-user,mips64-linux-user,mips64el-linux-user,mipsel-linux-user,ppc-linux-user,ppc64-linux-user,ppc64le-linux-user,riscv64-linux-user,s390x-linux-user,sh4-linux-user,sparc64-linux-user +ENV DEF_TARGET_LIST aarch64-linux-user,arm-linux-user,hppa-linux-user,i386-linux-user,m68k-linux-user,mips-linux-user,mips64-linux-user,mips64el-linux-user,mipsel-linux-user,ppc-linux-user,ppc64-linux-user,ppc64le-linux-user,riscv64-linux-user,s390x-linux-user,sparc64-linux-user +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-alpha-cross.docker b/tests/docker/dockerfiles/debian-alpha-cross.docker deleted file mode 100644 index 10fe30df0d..0000000000 --- a/tests/docker/dockerfiles/debian-alpha-cross.docker +++ /dev/null @@ -1,12 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the debian Buster base image. -# -FROM qemu/debian10 - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-alpha-linux-gnu \ - libc6.1-dev-alpha-cross diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index 870109ef6a..00bdc06021 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -1,22 +1,182 @@ +# THIS FILE WAS AUTO-GENERATED # -# Docker x86_64 cross target +# $ lcitool dockerfile --layers all --cross-arch x86_64 debian-12 qemu # -# This docker target is used on non-x86_64 machines which need the -# x86_64 cross compilers installed. -# -FROM qemu/debian10 -MAINTAINER Alex Bennée +# https://gitlab.com/libvirt/libvirt-ci -# Add the foreign architecture we want and install dependencies -RUN dpkg --add-architecture amd64 -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - crossbuild-essential-amd64 -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt build-dep -yy -a amd64 --arch-only qemu +FROM docker.io/library/debian:12-slim -# Specify the cross prefix for this image (see tests/docker/common.rc) +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y eatmydata && \ + eatmydata apt-get dist-upgrade -y && \ + eatmydata apt-get install --no-install-recommends -y \ + bash \ + bc \ + bison \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + flex \ + gcc \ + gcovr \ + gettext \ + git \ + hostname \ + libglib2.0-dev \ + libpcre2-dev \ + libsndio-dev \ + libspice-protocol-dev \ + llvm \ + locales \ + make \ + meson \ + mtools \ + ncat \ + ninja-build \ + openssh-client \ + pkgconf \ + python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-venv \ + python3-yaml \ + rpm2cpio \ + sed \ + socat \ + sparse \ + swtpm \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + xorriso \ + zstd && \ + eatmydata apt-get autoremove -y && \ + eatmydata apt-get autoclean -y && \ + sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ + dpkg-reconfigure locales + +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" +ENV LANG "en_US.UTF-8" +ENV MAKE "/usr/bin/make" +ENV NINJA "/usr/bin/ninja" +ENV PYTHON "/usr/bin/python3" + +RUN export DEBIAN_FRONTEND=noninteractive && \ + dpkg --add-architecture amd64 && \ + eatmydata apt-get update && \ + eatmydata apt-get dist-upgrade -y && \ + eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ + eatmydata apt-get install --no-install-recommends -y \ + g++-x86-64-linux-gnu \ + gcc-x86-64-linux-gnu \ + libaio-dev:amd64 \ + libasan6:amd64 \ + libasound2-dev:amd64 \ + libattr1-dev:amd64 \ + libbpf-dev:amd64 \ + libbrlapi-dev:amd64 \ + libbz2-dev:amd64 \ + libc6-dev:amd64 \ + libcacard-dev:amd64 \ + libcap-ng-dev:amd64 \ + libcapstone-dev:amd64 \ + libcmocka-dev:amd64 \ + libcurl4-gnutls-dev:amd64 \ + libdaxctl-dev:amd64 \ + libdrm-dev:amd64 \ + libepoxy-dev:amd64 \ + libfdt-dev:amd64 \ + libffi-dev:amd64 \ + libfuse3-dev:amd64 \ + libgbm-dev:amd64 \ + libgcrypt20-dev:amd64 \ + libglib2.0-dev:amd64 \ + libglusterfs-dev:amd64 \ + libgnutls28-dev:amd64 \ + libgtk-3-dev:amd64 \ + libibumad-dev:amd64 \ + libibverbs-dev:amd64 \ + libiscsi-dev:amd64 \ + libjemalloc-dev:amd64 \ + libjpeg62-turbo-dev:amd64 \ + libjson-c-dev:amd64 \ + liblttng-ust-dev:amd64 \ + liblzo2-dev:amd64 \ + libncursesw5-dev:amd64 \ + libnfs-dev:amd64 \ + libnuma-dev:amd64 \ + libpam0g-dev:amd64 \ + libpipewire-0.3-dev:amd64 \ + libpixman-1-dev:amd64 \ + libpmem-dev:amd64 \ + libpng-dev:amd64 \ + libpulse-dev:amd64 \ + librbd-dev:amd64 \ + librdmacm-dev:amd64 \ + libsasl2-dev:amd64 \ + libsdl2-dev:amd64 \ + libsdl2-image-dev:amd64 \ + libseccomp-dev:amd64 \ + libselinux1-dev:amd64 \ + libslirp-dev:amd64 \ + libsnappy-dev:amd64 \ + libspice-server-dev:amd64 \ + libssh-gcrypt-dev:amd64 \ + libsystemd-dev:amd64 \ + libtasn1-6-dev:amd64 \ + libubsan1:amd64 \ + libudev-dev:amd64 \ + liburing-dev:amd64 \ + libusb-1.0-0-dev:amd64 \ + libusbredirhost-dev:amd64 \ + libvdeplug-dev:amd64 \ + libvirglrenderer-dev:amd64 \ + libvte-2.91-dev:amd64 \ + libxdp-dev:amd64 \ + libxen-dev:amd64 \ + libzstd-dev:amd64 \ + nettle-dev:amd64 \ + systemtap-sdt-dev:amd64 \ + xfslibs-dev:amd64 \ + zlib1g-dev:amd64 && \ + eatmydata apt-get autoremove -y && \ + eatmydata apt-get autoclean -y && \ + mkdir -p /usr/local/share/meson/cross && \ + printf "[binaries]\n\ +c = '/usr/bin/x86_64-linux-gnu-gcc'\n\ +ar = '/usr/bin/x86_64-linux-gnu-gcc-ar'\n\ +strip = '/usr/bin/x86_64-linux-gnu-strip'\n\ +pkgconfig = '/usr/bin/x86_64-linux-gnu-pkg-config'\n\ +\n\ +[host_machine]\n\ +system = 'linux'\n\ +cpu_family = 'x86_64'\n\ +cpu = 'x86_64'\n\ +endian = 'little'\n" > /usr/local/share/meson/cross/x86_64-linux-gnu && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ + mkdir -p /usr/libexec/ccache-wrappers && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-c++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-cc && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-g++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-gcc + +ENV ABI "x86_64-linux-gnu" +ENV MESON_OPTS "--cross-file=x86_64-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=x86_64-linux-gnu- ENV DEF_TARGET_LIST x86_64-softmmu,x86_64-linux-user,i386-softmmu,i386-linux-user +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker deleted file mode 100644 index 503e282802..0000000000 --- a/tests/docker/dockerfiles/debian-amd64.docker +++ /dev/null @@ -1,153 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool dockerfile --layers all debian-11 qemu -# -# https://gitlab.com/libvirt/libvirt-ci - -FROM docker.io/library/debian:11-slim - -RUN export DEBIAN_FRONTEND=noninteractive && \ - apt-get update && \ - apt-get install -y eatmydata && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y \ - bash \ - bc \ - bsdextrautils \ - bzip2 \ - ca-certificates \ - ccache \ - clang \ - dbus \ - debianutils \ - diffutils \ - exuberant-ctags \ - findutils \ - g++ \ - gcc \ - gcovr \ - genisoimage \ - gettext \ - git \ - hostname \ - libaio-dev \ - libasan5 \ - libasound2-dev \ - libattr1-dev \ - libbpf-dev \ - libbrlapi-dev \ - libbz2-dev \ - libc6-dev \ - libcacard-dev \ - libcap-ng-dev \ - libcapstone-dev \ - libcurl4-gnutls-dev \ - libdaxctl-dev \ - libdrm-dev \ - libepoxy-dev \ - libfdt-dev \ - libffi-dev \ - libfuse3-dev \ - libgbm-dev \ - libgcrypt20-dev \ - libglib2.0-dev \ - libglusterfs-dev \ - libgnutls28-dev \ - libgtk-3-dev \ - libibumad-dev \ - libibverbs-dev \ - libiscsi-dev \ - libjemalloc-dev \ - libjpeg62-turbo-dev \ - liblttng-ust-dev \ - liblzo2-dev \ - libncursesw5-dev \ - libnfs-dev \ - libnuma-dev \ - libpam0g-dev \ - libpcre2-dev \ - libpixman-1-dev \ - libpmem-dev \ - libpng-dev \ - libpulse-dev \ - librbd-dev \ - librdmacm-dev \ - libsasl2-dev \ - libsdl2-dev \ - libsdl2-image-dev \ - libseccomp-dev \ - libselinux1-dev \ - libslirp-dev \ - libsnappy-dev \ - libspice-protocol-dev \ - libspice-server-dev \ - libssh-gcrypt-dev \ - libsystemd-dev \ - libtasn1-6-dev \ - libubsan1 \ - libudev-dev \ - liburing-dev \ - libusb-1.0-0-dev \ - libusbredirhost-dev \ - libvdeplug-dev \ - libvirglrenderer-dev \ - libvte-2.91-dev \ - libxen-dev \ - libzstd-dev \ - llvm \ - locales \ - make \ - meson \ - multipath-tools \ - ncat \ - nettle-dev \ - ninja-build \ - openssh-client \ - perl-base \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-yaml \ - rpm2cpio \ - sed \ - sparse \ - systemtap-sdt-dev \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - texinfo \ - xfslibs-dev \ - zlib1g-dev && \ - eatmydata apt-get autoremove -y && \ - eatmydata apt-get autoclean -y && \ - sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales && \ - dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ - mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc - -ENV LANG "en_US.UTF-8" -ENV MAKE "/usr/bin/make" -ENV NINJA "/usr/bin/ninja" -ENV PYTHON "/usr/bin/python3" -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" -# netmap/cscope/global -RUN DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - cscope\ - global\ - linux-headers-amd64 -RUN git clone https://github.com/luigirizzo/netmap.git /usr/src/netmap -RUN cd /usr/src/netmap && git checkout v11.3 -RUN cd /usr/src/netmap/LINUX && ./configure --no-drivers --no-apps --kernel-dir=$(ls -d /usr/src/linux-headers-*-amd64) && make install -ENV QEMU_CONFIGURE_OPTS --enable-netmap diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index b60426834c..2dae3777f7 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -1,69 +1,76 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross aarch64 debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch aarch64 debian-12 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:12-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get install -y eatmydata && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y \ - bash \ - bc \ - bsdextrautils \ - bzip2 \ - ca-certificates \ - ccache \ - dbus \ - debianutils \ - diffutils \ - exuberant-ctags \ - findutils \ - gcovr \ - genisoimage \ - gettext \ - git \ - hostname \ - libpcre2-dev \ - libspice-protocol-dev \ - llvm \ - locales \ - make \ - meson \ - ncat \ - ninja-build \ - openssh-client \ - perl-base \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-yaml \ - rpm2cpio \ - sed \ - sparse \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + bash \ + bc \ + bison \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + flex \ + gcc \ + gcovr \ + gettext \ + git \ + hostname \ + libglib2.0-dev \ + libpcre2-dev \ + libsndio-dev \ + libspice-protocol-dev \ + llvm \ + locales \ + make \ + meson \ + mtools \ + ncat \ + ninja-build \ + openssh-client \ + pkgconf \ + python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-venv \ + python3-yaml \ + rpm2cpio \ + sed \ + socat \ + sparse \ + swtpm \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + xorriso \ + zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ dpkg-reconfigure locales +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg --add-architecture arm64 && \ @@ -71,77 +78,81 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-aarch64-linux-gnu \ - gcc-aarch64-linux-gnu \ - libaio-dev:arm64 \ - libasan5:arm64 \ - libasound2-dev:arm64 \ - libattr1-dev:arm64 \ - libbpf-dev:arm64 \ - libbrlapi-dev:arm64 \ - libbz2-dev:arm64 \ - libc6-dev:arm64 \ - libcacard-dev:arm64 \ - libcap-ng-dev:arm64 \ - libcapstone-dev:arm64 \ - libcurl4-gnutls-dev:arm64 \ - libdaxctl-dev:arm64 \ - libdrm-dev:arm64 \ - libepoxy-dev:arm64 \ - libfdt-dev:arm64 \ - libffi-dev:arm64 \ - libfuse3-dev:arm64 \ - libgbm-dev:arm64 \ - libgcrypt20-dev:arm64 \ - libglib2.0-dev:arm64 \ - libglusterfs-dev:arm64 \ - libgnutls28-dev:arm64 \ - libgtk-3-dev:arm64 \ - libibumad-dev:arm64 \ - libibverbs-dev:arm64 \ - libiscsi-dev:arm64 \ - libjemalloc-dev:arm64 \ - libjpeg62-turbo-dev:arm64 \ - liblttng-ust-dev:arm64 \ - liblzo2-dev:arm64 \ - libncursesw5-dev:arm64 \ - libnfs-dev:arm64 \ - libnuma-dev:arm64 \ - libpam0g-dev:arm64 \ - libpixman-1-dev:arm64 \ - libpng-dev:arm64 \ - libpulse-dev:arm64 \ - librbd-dev:arm64 \ - librdmacm-dev:arm64 \ - libsasl2-dev:arm64 \ - libsdl2-dev:arm64 \ - libsdl2-image-dev:arm64 \ - libseccomp-dev:arm64 \ - libselinux1-dev:arm64 \ - libslirp-dev:arm64 \ - libsnappy-dev:arm64 \ - libspice-server-dev:arm64 \ - libssh-gcrypt-dev:arm64 \ - libsystemd-dev:arm64 \ - libtasn1-6-dev:arm64 \ - libubsan1:arm64 \ - libudev-dev:arm64 \ - liburing-dev:arm64 \ - libusb-1.0-0-dev:arm64 \ - libusbredirhost-dev:arm64 \ - libvdeplug-dev:arm64 \ - libvirglrenderer-dev:arm64 \ - libvte-2.91-dev:arm64 \ - libxen-dev:arm64 \ - libzstd-dev:arm64 \ - nettle-dev:arm64 \ - systemtap-sdt-dev:arm64 \ - xfslibs-dev:arm64 \ - zlib1g-dev:arm64 && \ + g++-aarch64-linux-gnu \ + gcc-aarch64-linux-gnu \ + libaio-dev:arm64 \ + libasan6:arm64 \ + libasound2-dev:arm64 \ + libattr1-dev:arm64 \ + libbpf-dev:arm64 \ + libbrlapi-dev:arm64 \ + libbz2-dev:arm64 \ + libc6-dev:arm64 \ + libcacard-dev:arm64 \ + libcap-ng-dev:arm64 \ + libcapstone-dev:arm64 \ + libcmocka-dev:arm64 \ + libcurl4-gnutls-dev:arm64 \ + libdaxctl-dev:arm64 \ + libdrm-dev:arm64 \ + libepoxy-dev:arm64 \ + libfdt-dev:arm64 \ + libffi-dev:arm64 \ + libfuse3-dev:arm64 \ + libgbm-dev:arm64 \ + libgcrypt20-dev:arm64 \ + libglib2.0-dev:arm64 \ + libglusterfs-dev:arm64 \ + libgnutls28-dev:arm64 \ + libgtk-3-dev:arm64 \ + libibumad-dev:arm64 \ + libibverbs-dev:arm64 \ + libiscsi-dev:arm64 \ + libjemalloc-dev:arm64 \ + libjpeg62-turbo-dev:arm64 \ + libjson-c-dev:arm64 \ + liblttng-ust-dev:arm64 \ + liblzo2-dev:arm64 \ + libncursesw5-dev:arm64 \ + libnfs-dev:arm64 \ + libnuma-dev:arm64 \ + libpam0g-dev:arm64 \ + libpipewire-0.3-dev:arm64 \ + libpixman-1-dev:arm64 \ + libpng-dev:arm64 \ + libpulse-dev:arm64 \ + librbd-dev:arm64 \ + librdmacm-dev:arm64 \ + libsasl2-dev:arm64 \ + libsdl2-dev:arm64 \ + libsdl2-image-dev:arm64 \ + libseccomp-dev:arm64 \ + libselinux1-dev:arm64 \ + libslirp-dev:arm64 \ + libsnappy-dev:arm64 \ + libspice-server-dev:arm64 \ + libssh-gcrypt-dev:arm64 \ + libsystemd-dev:arm64 \ + libtasn1-6-dev:arm64 \ + libubsan1:arm64 \ + libudev-dev:arm64 \ + liburing-dev:arm64 \ + libusb-1.0-0-dev:arm64 \ + libusbredirhost-dev:arm64 \ + libvdeplug-dev:arm64 \ + libvirglrenderer-dev:arm64 \ + libvte-2.91-dev:arm64 \ + libxdp-dev:arm64 \ + libxen-dev:arm64 \ + libzstd-dev:arm64 \ + nettle-dev:arm64 \ + systemtap-sdt-dev:arm64 \ + xfslibs-dev:arm64 \ + zlib1g-dev:arm64 && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ mkdir -p /usr/local/share/meson/cross && \ - echo "[binaries]\n\ + printf "[binaries]\n\ c = '/usr/bin/aarch64-linux-gnu-gcc'\n\ ar = '/usr/bin/aarch64-linux-gnu-gcc-ar'\n\ strip = '/usr/bin/aarch64-linux-gnu-strip'\n\ @@ -151,7 +162,7 @@ pkgconfig = '/usr/bin/aarch64-linux-gnu-pkg-config'\n\ system = 'linux'\n\ cpu_family = 'aarch64'\n\ cpu = 'aarch64'\n\ -endian = 'little'" > /usr/local/share/meson/cross/aarch64-linux-gnu && \ +endian = 'little'\n" > /usr/local/share/meson/cross/aarch64-linux-gnu && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-c++ && \ @@ -163,3 +174,8 @@ ENV ABI "aarch64-linux-gnu" ENV MESON_OPTS "--cross-file=aarch64-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=aarch64-linux-gnu- ENV DEF_TARGET_LIST aarch64-softmmu,aarch64-linux-user +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-armel-cross.docker b/tests/docker/dockerfiles/debian-armel-cross.docker index a6153e5a83..75342c09b0 100644 --- a/tests/docker/dockerfiles/debian-armel-cross.docker +++ b/tests/docker/dockerfiles/debian-armel-cross.docker @@ -1,6 +1,6 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross armv6l debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch armv6l debian-11 qemu # # https://gitlab.com/libvirt/libvirt-ci @@ -11,59 +11,69 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get install -y eatmydata && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y \ - bash \ - bc \ - bsdextrautils \ - bzip2 \ - ca-certificates \ - ccache \ - dbus \ - debianutils \ - diffutils \ - exuberant-ctags \ - findutils \ - gcovr \ - genisoimage \ - gettext \ - git \ - hostname \ - libpcre2-dev \ - libspice-protocol-dev \ - llvm \ - locales \ - make \ - meson \ - ncat \ - ninja-build \ - openssh-client \ - perl-base \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-yaml \ - rpm2cpio \ - sed \ - sparse \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + bash \ + bc \ + bison \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + flex \ + gcc \ + gcovr \ + gettext \ + git \ + hostname \ + libglib2.0-dev \ + libpcre2-dev \ + libsndio-dev \ + libspice-protocol-dev \ + llvm \ + locales \ + make \ + meson \ + mtools \ + ncat \ + ninja-build \ + openssh-client \ + pkgconf \ + python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-setuptools \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-venv \ + python3-wheel \ + python3-yaml \ + rpm2cpio \ + sed \ + socat \ + sparse \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + xorriso \ + zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ dpkg-reconfigure locales +RUN /usr/bin/pip3 install tomli + +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg --add-architecture armel && \ @@ -71,76 +81,79 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-arm-linux-gnueabi \ - gcc-arm-linux-gnueabi \ - libaio-dev:armel \ - libasan5:armel \ - libasound2-dev:armel \ - libattr1-dev:armel \ - libbpf-dev:armel \ - libbrlapi-dev:armel \ - libbz2-dev:armel \ - libc6-dev:armel \ - libcacard-dev:armel \ - libcap-ng-dev:armel \ - libcapstone-dev:armel \ - libcurl4-gnutls-dev:armel \ - libdaxctl-dev:armel \ - libdrm-dev:armel \ - libepoxy-dev:armel \ - libfdt-dev:armel \ - libffi-dev:armel \ - libfuse3-dev:armel \ - libgbm-dev:armel \ - libgcrypt20-dev:armel \ - libglib2.0-dev:armel \ - libglusterfs-dev:armel \ - libgnutls28-dev:armel \ - libgtk-3-dev:armel \ - libibumad-dev:armel \ - libibverbs-dev:armel \ - libiscsi-dev:armel \ - libjemalloc-dev:armel \ - libjpeg62-turbo-dev:armel \ - liblttng-ust-dev:armel \ - liblzo2-dev:armel \ - libncursesw5-dev:armel \ - libnfs-dev:armel \ - libnuma-dev:armel \ - libpam0g-dev:armel \ - libpixman-1-dev:armel \ - libpng-dev:armel \ - libpulse-dev:armel \ - librbd-dev:armel \ - librdmacm-dev:armel \ - libsasl2-dev:armel \ - libsdl2-dev:armel \ - libsdl2-image-dev:armel \ - libseccomp-dev:armel \ - libselinux1-dev:armel \ - libslirp-dev:armel \ - libsnappy-dev:armel \ - libspice-server-dev:armel \ - libssh-gcrypt-dev:armel \ - libsystemd-dev:armel \ - libtasn1-6-dev:armel \ - libubsan1:armel \ - libudev-dev:armel \ - liburing-dev:armel \ - libusb-1.0-0-dev:armel \ - libusbredirhost-dev:armel \ - libvdeplug-dev:armel \ - libvirglrenderer-dev:armel \ - libvte-2.91-dev:armel \ - libzstd-dev:armel \ - nettle-dev:armel \ - systemtap-sdt-dev:armel \ - xfslibs-dev:armel \ - zlib1g-dev:armel && \ + g++-arm-linux-gnueabi \ + gcc-arm-linux-gnueabi \ + libaio-dev:armel \ + libasan6:armel \ + libasound2-dev:armel \ + libattr1-dev:armel \ + libbpf-dev:armel \ + libbrlapi-dev:armel \ + libbz2-dev:armel \ + libc6-dev:armel \ + libcacard-dev:armel \ + libcap-ng-dev:armel \ + libcapstone-dev:armel \ + libcmocka-dev:armel \ + libcurl4-gnutls-dev:armel \ + libdaxctl-dev:armel \ + libdrm-dev:armel \ + libepoxy-dev:armel \ + libfdt-dev:armel \ + libffi-dev:armel \ + libfuse3-dev:armel \ + libgbm-dev:armel \ + libgcrypt20-dev:armel \ + libglib2.0-dev:armel \ + libglusterfs-dev:armel \ + libgnutls28-dev:armel \ + libgtk-3-dev:armel \ + libibumad-dev:armel \ + libibverbs-dev:armel \ + libiscsi-dev:armel \ + libjemalloc-dev:armel \ + libjpeg62-turbo-dev:armel \ + libjson-c-dev:armel \ + liblttng-ust-dev:armel \ + liblzo2-dev:armel \ + libncursesw5-dev:armel \ + libnfs-dev:armel \ + libnuma-dev:armel \ + libpam0g-dev:armel \ + libpipewire-0.3-dev:armel \ + libpixman-1-dev:armel \ + libpng-dev:armel \ + libpulse-dev:armel \ + librbd-dev:armel \ + librdmacm-dev:armel \ + libsasl2-dev:armel \ + libsdl2-dev:armel \ + libsdl2-image-dev:armel \ + libseccomp-dev:armel \ + libselinux1-dev:armel \ + libslirp-dev:armel \ + libsnappy-dev:armel \ + libspice-server-dev:armel \ + libssh-gcrypt-dev:armel \ + libsystemd-dev:armel \ + libtasn1-6-dev:armel \ + libubsan1:armel \ + libudev-dev:armel \ + liburing-dev:armel \ + libusb-1.0-0-dev:armel \ + libusbredirhost-dev:armel \ + libvdeplug-dev:armel \ + libvirglrenderer-dev:armel \ + libvte-2.91-dev:armel \ + libzstd-dev:armel \ + nettle-dev:armel \ + systemtap-sdt-dev:armel \ + xfslibs-dev:armel \ + zlib1g-dev:armel && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ mkdir -p /usr/local/share/meson/cross && \ - echo "[binaries]\n\ + printf "[binaries]\n\ c = '/usr/bin/arm-linux-gnueabi-gcc'\n\ ar = '/usr/bin/arm-linux-gnueabi-gcc-ar'\n\ strip = '/usr/bin/arm-linux-gnueabi-strip'\n\ @@ -150,7 +163,7 @@ pkgconfig = '/usr/bin/arm-linux-gnueabi-pkg-config'\n\ system = 'linux'\n\ cpu_family = 'arm'\n\ cpu = 'arm'\n\ -endian = 'little'" > /usr/local/share/meson/cross/arm-linux-gnueabi && \ +endian = 'little'\n" > /usr/local/share/meson/cross/arm-linux-gnueabi && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-c++ && \ @@ -162,3 +175,8 @@ ENV ABI "arm-linux-gnueabi" ENV MESON_OPTS "--cross-file=arm-linux-gnueabi" ENV QEMU_CONFIGURE_OPTS --cross-prefix=arm-linux-gnueabi- ENV DEF_TARGET_LIST arm-softmmu,arm-linux-user,armeb-linux-user +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index a2ebce96f8..180ed836e6 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -1,69 +1,76 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross armv7l debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch armv7l debian-12 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:12-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get install -y eatmydata && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y \ - bash \ - bc \ - bsdextrautils \ - bzip2 \ - ca-certificates \ - ccache \ - dbus \ - debianutils \ - diffutils \ - exuberant-ctags \ - findutils \ - gcovr \ - genisoimage \ - gettext \ - git \ - hostname \ - libpcre2-dev \ - libspice-protocol-dev \ - llvm \ - locales \ - make \ - meson \ - ncat \ - ninja-build \ - openssh-client \ - perl-base \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-yaml \ - rpm2cpio \ - sed \ - sparse \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + bash \ + bc \ + bison \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + flex \ + gcc \ + gcovr \ + gettext \ + git \ + hostname \ + libglib2.0-dev \ + libpcre2-dev \ + libsndio-dev \ + libspice-protocol-dev \ + llvm \ + locales \ + make \ + meson \ + mtools \ + ncat \ + ninja-build \ + openssh-client \ + pkgconf \ + python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-venv \ + python3-yaml \ + rpm2cpio \ + sed \ + socat \ + sparse \ + swtpm \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + xorriso \ + zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ dpkg-reconfigure locales +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg --add-architecture armhf && \ @@ -71,77 +78,81 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-arm-linux-gnueabihf \ - gcc-arm-linux-gnueabihf \ - libaio-dev:armhf \ - libasan5:armhf \ - libasound2-dev:armhf \ - libattr1-dev:armhf \ - libbpf-dev:armhf \ - libbrlapi-dev:armhf \ - libbz2-dev:armhf \ - libc6-dev:armhf \ - libcacard-dev:armhf \ - libcap-ng-dev:armhf \ - libcapstone-dev:armhf \ - libcurl4-gnutls-dev:armhf \ - libdaxctl-dev:armhf \ - libdrm-dev:armhf \ - libepoxy-dev:armhf \ - libfdt-dev:armhf \ - libffi-dev:armhf \ - libfuse3-dev:armhf \ - libgbm-dev:armhf \ - libgcrypt20-dev:armhf \ - libglib2.0-dev:armhf \ - libglusterfs-dev:armhf \ - libgnutls28-dev:armhf \ - libgtk-3-dev:armhf \ - libibumad-dev:armhf \ - libibverbs-dev:armhf \ - libiscsi-dev:armhf \ - libjemalloc-dev:armhf \ - libjpeg62-turbo-dev:armhf \ - liblttng-ust-dev:armhf \ - liblzo2-dev:armhf \ - libncursesw5-dev:armhf \ - libnfs-dev:armhf \ - libnuma-dev:armhf \ - libpam0g-dev:armhf \ - libpixman-1-dev:armhf \ - libpng-dev:armhf \ - libpulse-dev:armhf \ - librbd-dev:armhf \ - librdmacm-dev:armhf \ - libsasl2-dev:armhf \ - libsdl2-dev:armhf \ - libsdl2-image-dev:armhf \ - libseccomp-dev:armhf \ - libselinux1-dev:armhf \ - libslirp-dev:armhf \ - libsnappy-dev:armhf \ - libspice-server-dev:armhf \ - libssh-gcrypt-dev:armhf \ - libsystemd-dev:armhf \ - libtasn1-6-dev:armhf \ - libubsan1:armhf \ - libudev-dev:armhf \ - liburing-dev:armhf \ - libusb-1.0-0-dev:armhf \ - libusbredirhost-dev:armhf \ - libvdeplug-dev:armhf \ - libvirglrenderer-dev:armhf \ - libvte-2.91-dev:armhf \ - libxen-dev:armhf \ - libzstd-dev:armhf \ - nettle-dev:armhf \ - systemtap-sdt-dev:armhf \ - xfslibs-dev:armhf \ - zlib1g-dev:armhf && \ + g++-arm-linux-gnueabihf \ + gcc-arm-linux-gnueabihf \ + libaio-dev:armhf \ + libasan6:armhf \ + libasound2-dev:armhf \ + libattr1-dev:armhf \ + libbpf-dev:armhf \ + libbrlapi-dev:armhf \ + libbz2-dev:armhf \ + libc6-dev:armhf \ + libcacard-dev:armhf \ + libcap-ng-dev:armhf \ + libcapstone-dev:armhf \ + libcmocka-dev:armhf \ + libcurl4-gnutls-dev:armhf \ + libdaxctl-dev:armhf \ + libdrm-dev:armhf \ + libepoxy-dev:armhf \ + libfdt-dev:armhf \ + libffi-dev:armhf \ + libfuse3-dev:armhf \ + libgbm-dev:armhf \ + libgcrypt20-dev:armhf \ + libglib2.0-dev:armhf \ + libglusterfs-dev:armhf \ + libgnutls28-dev:armhf \ + libgtk-3-dev:armhf \ + libibumad-dev:armhf \ + libibverbs-dev:armhf \ + libiscsi-dev:armhf \ + libjemalloc-dev:armhf \ + libjpeg62-turbo-dev:armhf \ + libjson-c-dev:armhf \ + liblttng-ust-dev:armhf \ + liblzo2-dev:armhf \ + libncursesw5-dev:armhf \ + libnfs-dev:armhf \ + libnuma-dev:armhf \ + libpam0g-dev:armhf \ + libpipewire-0.3-dev:armhf \ + libpixman-1-dev:armhf \ + libpng-dev:armhf \ + libpulse-dev:armhf \ + librbd-dev:armhf \ + librdmacm-dev:armhf \ + libsasl2-dev:armhf \ + libsdl2-dev:armhf \ + libsdl2-image-dev:armhf \ + libseccomp-dev:armhf \ + libselinux1-dev:armhf \ + libslirp-dev:armhf \ + libsnappy-dev:armhf \ + libspice-server-dev:armhf \ + libssh-gcrypt-dev:armhf \ + libsystemd-dev:armhf \ + libtasn1-6-dev:armhf \ + libubsan1:armhf \ + libudev-dev:armhf \ + liburing-dev:armhf \ + libusb-1.0-0-dev:armhf \ + libusbredirhost-dev:armhf \ + libvdeplug-dev:armhf \ + libvirglrenderer-dev:armhf \ + libvte-2.91-dev:armhf \ + libxdp-dev:armhf \ + libxen-dev:armhf \ + libzstd-dev:armhf \ + nettle-dev:armhf \ + systemtap-sdt-dev:armhf \ + xfslibs-dev:armhf \ + zlib1g-dev:armhf && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ mkdir -p /usr/local/share/meson/cross && \ - echo "[binaries]\n\ + printf "[binaries]\n\ c = '/usr/bin/arm-linux-gnueabihf-gcc'\n\ ar = '/usr/bin/arm-linux-gnueabihf-gcc-ar'\n\ strip = '/usr/bin/arm-linux-gnueabihf-strip'\n\ @@ -151,7 +162,7 @@ pkgconfig = '/usr/bin/arm-linux-gnueabihf-pkg-config'\n\ system = 'linux'\n\ cpu_family = 'arm'\n\ cpu = 'armhf'\n\ -endian = 'little'" > /usr/local/share/meson/cross/arm-linux-gnueabihf && \ +endian = 'little'\n" > /usr/local/share/meson/cross/arm-linux-gnueabihf && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabihf-c++ && \ @@ -163,3 +174,8 @@ ENV ABI "arm-linux-gnueabihf" ENV MESON_OPTS "--cross-file=arm-linux-gnueabihf" ENV QEMU_CONFIGURE_OPTS --cross-prefix=arm-linux-gnueabihf- ENV DEF_TARGET_LIST arm-softmmu,arm-linux-user +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-hexagon-cross.d/build-toolchain.sh b/tests/docker/dockerfiles/debian-hexagon-cross.d/build-toolchain.sh deleted file mode 100755 index 19b1c9f83e..0000000000 --- a/tests/docker/dockerfiles/debian-hexagon-cross.d/build-toolchain.sh +++ /dev/null @@ -1,141 +0,0 @@ -#!/bin/bash - -set -e - -BASE=$(readlink -f ${PWD}) - -TOOLCHAIN_INSTALL=$(readlink -f "$TOOLCHAIN_INSTALL") -ROOTFS=$(readlink -f "$ROOTFS") - -TOOLCHAIN_BIN=${TOOLCHAIN_INSTALL}/bin -HEX_SYSROOT=${TOOLCHAIN_INSTALL}/hexagon-unknown-linux-musl -HEX_TOOLS_TARGET_BASE=${HEX_SYSROOT}/usr - -function cdp() { - DIR="$1" - mkdir -p "$DIR" - cd "$DIR" -} - -function fetch() { - DIR="$1" - URL="$2" - TEMP="$(readlink -f "$PWD/tmp.tar.gz")" - wget --quiet "$URL" -O "$TEMP" - cdp "$DIR" - tar xaf "$TEMP" --strip-components=1 - rm "$TEMP" - cd - -} - -build_llvm_clang() { - fetch "$BASE/llvm-project" "$LLVM_URL" - cdp "$BASE/build-llvm" - - cmake -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=${TOOLCHAIN_INSTALL} \ - -DLLVM_ENABLE_LLD=ON \ - -DLLVM_TARGETS_TO_BUILD="Hexagon" \ - -DLLVM_ENABLE_PROJECTS="clang;lld" \ - "$BASE/llvm-project/llvm" - ninja all install - cd ${TOOLCHAIN_BIN} - ln -sf clang hexagon-unknown-linux-musl-clang - ln -sf clang++ hexagon-unknown-linux-musl-clang++ - ln -sf llvm-ar hexagon-unknown-linux-musl-ar - ln -sf llvm-objdump hexagon-unknown-linux-musl-objdump - ln -sf llvm-objcopy hexagon-unknown-linux-musl-objcopy - ln -sf llvm-readelf hexagon-unknown-linux-musl-readelf - ln -sf llvm-ranlib hexagon-unknown-linux-musl-ranlib - - # workaround for now: - cat < hexagon-unknown-linux-musl.cfg --G0 --sysroot=${HEX_SYSROOT} -EOF -} - -build_clang_rt() { - cdp "$BASE/build-clang_rt" - cmake -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DLLVM_CONFIG_PATH="$BASE/build-llvm/bin/llvm-config" \ - -DCMAKE_ASM_FLAGS="-G0 -mlong-calls -fno-pic --target=hexagon-unknown-linux-musl " \ - -DCMAKE_SYSTEM_NAME=Linux \ - -DCMAKE_C_COMPILER="${TOOLCHAIN_BIN}/hexagon-unknown-linux-musl-clang" \ - -DCMAKE_ASM_COMPILER="${TOOLCHAIN_BIN}/hexagon-unknown-linux-musl-clang" \ - -DCMAKE_INSTALL_PREFIX=${HEX_TOOLS_TARGET_BASE} \ - -DCMAKE_CROSSCOMPILING=ON \ - -DCMAKE_C_COMPILER_FORCED=ON \ - -DCMAKE_CXX_COMPILER_FORCED=ON \ - -DCOMPILER_RT_BUILD_BUILTINS=ON \ - -DCOMPILER_RT_BUILTINS_ENABLE_PIC=OFF \ - -DCMAKE_SIZEOF_VOID_P=4 \ - -DCOMPILER_RT_OS_DIR= \ - -DCAN_TARGET_hexagon=1 \ - -DCAN_TARGET_x86_64=0 \ - -DCOMPILER_RT_SUPPORTED_ARCH=hexagon \ - -DLLVM_ENABLE_PROJECTS="compiler-rt" \ - "$BASE/llvm-project/compiler-rt" - ninja install-compiler-rt -} - -build_musl_headers() { - fetch "$BASE/musl" "$MUSL_URL" - cd "$BASE/musl" - make clean - CC=${TOOLCHAIN_BIN}/hexagon-unknown-linux-musl-clang \ - CROSS_COMPILE=hexagon-unknown-linux-musl \ - LIBCC=${HEX_TOOLS_TARGET_BASE}/lib/libclang_rt.builtins-hexagon.a \ - CROSS_CFLAGS="-G0 -O0 -mv65 -fno-builtin -fno-rounding-math --target=hexagon-unknown-linux-musl" \ - ./configure --target=hexagon --prefix=${HEX_TOOLS_TARGET_BASE} - PATH=${TOOLCHAIN_BIN}:$PATH make CROSS_COMPILE= install-headers - - cd ${HEX_SYSROOT}/.. - ln -sf hexagon-unknown-linux-musl hexagon -} - -build_kernel_headers() { - fetch "$BASE/linux" "$LINUX_URL" - mkdir -p "$BASE/build-linux" - cd "$BASE/linux" - make O=../build-linux ARCH=hexagon \ - KBUILD_CFLAGS_KERNEL="-mlong-calls" \ - CC=${TOOLCHAIN_BIN}/hexagon-unknown-linux-musl-clang \ - LD=${TOOLCHAIN_BIN}/ld.lld \ - KBUILD_VERBOSE=1 comet_defconfig - make mrproper - - cd "$BASE/build-linux" - make \ - ARCH=hexagon \ - CC=${TOOLCHAIN_BIN}/clang \ - INSTALL_HDR_PATH=${HEX_TOOLS_TARGET_BASE} \ - V=1 \ - headers_install -} - -build_musl() { - cd "$BASE/musl" - make clean - CROSS_COMPILE=hexagon-unknown-linux-musl- \ - AR=llvm-ar \ - RANLIB=llvm-ranlib \ - STRIP=llvm-strip \ - CC=clang \ - LIBCC=${HEX_TOOLS_TARGET_BASE}/lib/libclang_rt.builtins-hexagon.a \ - CFLAGS="-G0 -O0 -mv65 -fno-builtin -fno-rounding-math --target=hexagon-unknown-linux-musl" \ - ./configure --target=hexagon --prefix=${HEX_TOOLS_TARGET_BASE} - PATH=${TOOLCHAIN_BIN}/:$PATH make CROSS_COMPILE= install - cd ${HEX_TOOLS_TARGET_BASE}/lib - ln -sf libc.so ld-musl-hexagon.so - ln -sf ld-musl-hexagon.so ld-musl-hexagon.so.1 - cdp ${HEX_TOOLS_TARGET_BASE}/../lib - ln -sf ../usr/lib/ld-musl-hexagon.so.1 -} - -build_llvm_clang -build_kernel_headers -build_musl_headers -build_clang_rt -build_musl diff --git a/tests/docker/dockerfiles/debian-hexagon-cross.docker b/tests/docker/dockerfiles/debian-hexagon-cross.docker index d5dc299dc1..60bd8faa20 100644 --- a/tests/docker/dockerfiles/debian-hexagon-cross.docker +++ b/tests/docker/dockerfiles/debian-hexagon-cross.docker @@ -2,44 +2,51 @@ # Docker Hexagon cross-compiler target # # This docker target is used for building hexagon tests. As it also -# needs to be able to build QEMU itself in CI we include it's -# build-deps. It is also a "stand-alone" image so as not to be -# triggered by re-builds on other base images given it takes a long -# time to build. +# needs to be able to build QEMU itself in CI we include its +# build-deps. # -FROM qemu/debian10 +FROM docker.io/library/debian:11-slim -# Install common build utilities -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - bison \ - cmake \ - flex \ - lld \ - rsync \ - wget - -ENV TOOLCHAIN_INSTALL /usr/local -ENV ROOTFS /usr/local - -ENV LLVM_URL https://github.com/llvm/llvm-project/archive/bfcd21876adc3498065e4da92799f613e730d475.tar.gz -ENV MUSL_URL https://github.com/quic/musl/archive/aff74b395fbf59cd7e93b3691905aa1af6c0778c.tar.gz -ENV LINUX_URL https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.6.18.tar.xz - -ADD build-toolchain.sh /root/hexagon-toolchain/build-toolchain.sh - -RUN cd /root/hexagon-toolchain && ./build-toolchain.sh - -FROM debian:buster-slim # Duplicate deb line as deb-src RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list -# Install QEMU build deps for use in CI -RUN apt update && \ +RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ - DEBIAN_FRONTEND=noninteractive eatmydata apt install -yy git ninja-build && \ DEBIAN_FRONTEND=noninteractive eatmydata \ - apt build-dep -yy --arch-only qemu -COPY --from=0 /usr/local /usr/local -ENV PATH $PATH:/usr/local/bin/ +# Install common build utilities + apt-get install -y --no-install-recommends \ + curl \ + ccache \ + xz-utils \ + ca-certificates \ + bison \ + flex \ + git \ + ninja-build \ + python3-pip \ + python3-setuptools \ + python3-venv \ + python3-wheel && \ +# Install QEMU build deps for use in CI + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt build-dep -yy --arch-only qemu && \ + mkdir -p /usr/libexec/ccache-wrappers && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc + +RUN /usr/bin/pip3 install tomli + +ENV TOOLCHAIN_INSTALL /opt +ENV TOOLCHAIN_RELEASE 12.Dec.2023 +ENV TOOLCHAIN_BASENAME "clang+llvm-${TOOLCHAIN_RELEASE}-cross-hexagon-unknown-linux-musl" +ENV TOOLCHAIN_URL https://codelinaro.jfrog.io/artifactory/codelinaro-toolchain-for-hexagon/${TOOLCHAIN_RELEASE}/${TOOLCHAIN_BASENAME}.tar.xz +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" + +RUN curl -#SL "$TOOLCHAIN_URL" | tar -xJC "$TOOLCHAIN_INSTALL" +ENV PATH $PATH:${TOOLCHAIN_INSTALL}/${TOOLCHAIN_BASENAME}/x86_64-linux-gnu/bin +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-hppa-cross.docker b/tests/docker/dockerfiles/debian-hppa-cross.docker deleted file mode 100644 index 3d6c65a3ef..0000000000 --- a/tests/docker/dockerfiles/debian-hppa-cross.docker +++ /dev/null @@ -1,12 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the debian Buster base image. -# -FROM qemu/debian10 - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-hppa-linux-gnu \ - libc6-dev-hppa-cross diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker new file mode 100644 index 0000000000..3fc4e15acd --- /dev/null +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -0,0 +1,182 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool dockerfile --layers all --cross-arch i686 debian-11 qemu +# +# https://gitlab.com/libvirt/libvirt-ci + +FROM docker.io/library/debian:11-slim + +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y eatmydata && \ + eatmydata apt-get dist-upgrade -y && \ + eatmydata apt-get install --no-install-recommends -y \ + bash \ + bc \ + bison \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + flex \ + gcc \ + gcovr \ + gettext \ + git \ + hostname \ + libglib2.0-dev \ + libpcre2-dev \ + libsndio-dev \ + libspice-protocol-dev \ + llvm \ + locales \ + make \ + meson \ + mtools \ + ncat \ + ninja-build \ + openssh-client \ + pkgconf \ + python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-setuptools \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-venv \ + python3-wheel \ + python3-yaml \ + rpm2cpio \ + sed \ + socat \ + sparse \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + xorriso \ + zstd && \ + eatmydata apt-get autoremove -y && \ + eatmydata apt-get autoclean -y && \ + sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ + dpkg-reconfigure locales + +RUN /usr/bin/pip3 install tomli + +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" +ENV LANG "en_US.UTF-8" +ENV MAKE "/usr/bin/make" +ENV NINJA "/usr/bin/ninja" +ENV PYTHON "/usr/bin/python3" + +RUN export DEBIAN_FRONTEND=noninteractive && \ + dpkg --add-architecture i386 && \ + eatmydata apt-get update && \ + eatmydata apt-get dist-upgrade -y && \ + eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ + eatmydata apt-get install --no-install-recommends -y \ + g++-i686-linux-gnu \ + gcc-i686-linux-gnu \ + libaio-dev:i386 \ + libasan6:i386 \ + libasound2-dev:i386 \ + libattr1-dev:i386 \ + libbpf-dev:i386 \ + libbrlapi-dev:i386 \ + libbz2-dev:i386 \ + libc6-dev:i386 \ + libcacard-dev:i386 \ + libcap-ng-dev:i386 \ + libcapstone-dev:i386 \ + libcmocka-dev:i386 \ + libcurl4-gnutls-dev:i386 \ + libdaxctl-dev:i386 \ + libdrm-dev:i386 \ + libepoxy-dev:i386 \ + libfdt-dev:i386 \ + libffi-dev:i386 \ + libfuse3-dev:i386 \ + libgbm-dev:i386 \ + libgcrypt20-dev:i386 \ + libglib2.0-dev:i386 \ + libglusterfs-dev:i386 \ + libgnutls28-dev:i386 \ + libgtk-3-dev:i386 \ + libibumad-dev:i386 \ + libibverbs-dev:i386 \ + libiscsi-dev:i386 \ + libjemalloc-dev:i386 \ + libjpeg62-turbo-dev:i386 \ + libjson-c-dev:i386 \ + liblttng-ust-dev:i386 \ + liblzo2-dev:i386 \ + libncursesw5-dev:i386 \ + libnfs-dev:i386 \ + libnuma-dev:i386 \ + libpam0g-dev:i386 \ + libpipewire-0.3-dev:i386 \ + libpixman-1-dev:i386 \ + libpng-dev:i386 \ + libpulse-dev:i386 \ + librbd-dev:i386 \ + librdmacm-dev:i386 \ + libsasl2-dev:i386 \ + libsdl2-dev:i386 \ + libsdl2-image-dev:i386 \ + libseccomp-dev:i386 \ + libselinux1-dev:i386 \ + libslirp-dev:i386 \ + libsnappy-dev:i386 \ + libspice-server-dev:i386 \ + libssh-gcrypt-dev:i386 \ + libsystemd-dev:i386 \ + libtasn1-6-dev:i386 \ + libubsan1:i386 \ + libudev-dev:i386 \ + liburing-dev:i386 \ + libusb-1.0-0-dev:i386 \ + libusbredirhost-dev:i386 \ + libvdeplug-dev:i386 \ + libvirglrenderer-dev:i386 \ + libvte-2.91-dev:i386 \ + libzstd-dev:i386 \ + nettle-dev:i386 \ + systemtap-sdt-dev:i386 \ + xfslibs-dev:i386 \ + zlib1g-dev:i386 && \ + eatmydata apt-get autoremove -y && \ + eatmydata apt-get autoclean -y && \ + mkdir -p /usr/local/share/meson/cross && \ + printf "[binaries]\n\ +c = '/usr/bin/i686-linux-gnu-gcc'\n\ +ar = '/usr/bin/i686-linux-gnu-gcc-ar'\n\ +strip = '/usr/bin/i686-linux-gnu-strip'\n\ +pkgconfig = '/usr/bin/i686-linux-gnu-pkg-config'\n\ +\n\ +[host_machine]\n\ +system = 'linux'\n\ +cpu_family = 'x86'\n\ +cpu = 'i686'\n\ +endian = 'little'\n" > /usr/local/share/meson/cross/i686-linux-gnu && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ + mkdir -p /usr/libexec/ccache-wrappers && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-linux-gnu-c++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-linux-gnu-cc && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-linux-gnu-g++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-linux-gnu-gcc + +ENV ABI "i686-linux-gnu" +ENV MESON_OPTS "--cross-file=i686-linux-gnu" +ENV QEMU_CONFIGURE_OPTS --cross-prefix=x86_64-linux-gnu- +ENV DEF_TARGET_LIST x86_64-softmmu,x86_64-linux-user,i386-softmmu,i386-linux-user +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-legacy-test-cross.docker b/tests/docker/dockerfiles/debian-legacy-test-cross.docker new file mode 100644 index 0000000000..8cc68bc912 --- /dev/null +++ b/tests/docker/dockerfiles/debian-legacy-test-cross.docker @@ -0,0 +1,49 @@ +# Docker legacy cross-compiler target (tests and minimal qemu) +# +# Compilers for some of our older targets which we cant currently +# upgrade. Currently: +# +# libc6.1-dev-alpha-cross: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1054412 +# sh4-linux-user: binaries don't run with bookworm compiler +# +# As we are targeting check-tcg here we only need minimal qemu +# dependencies and the relevant cross compilers. + +FROM docker.io/library/debian:11-slim + +# Duplicate deb line as deb-src +RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list + +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y eatmydata && \ + eatmydata apt-get dist-upgrade -y && \ + apt build-dep -yy qemu + +# Add extra build tools and as many cross compilers as we can for testing +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + bison \ + ccache \ + clang \ + flex \ + git \ + ninja-build \ + gcc-alpha-linux-gnu \ + libc6.1-dev-alpha-cross \ + gcc-sh4-linux-gnu \ + libc6-dev-sh4-cross \ + python3-pip \ + python3-setuptools \ + python3-venv \ + python3-wheel + +RUN /usr/bin/pip3 install tomli + +ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools +ENV DEF_TARGET_LIST alpha-linux-user,sh4-linux-user +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-loongarch-cross.docker b/tests/docker/dockerfiles/debian-loongarch-cross.docker new file mode 100644 index 0000000000..b25e779a2c --- /dev/null +++ b/tests/docker/dockerfiles/debian-loongarch-cross.docker @@ -0,0 +1,52 @@ +# +# Docker cross-compiler target +# +# This docker target uses prebuilt toolchains for LoongArch64 from: +# https://github.com/loongson/build-tools/releases +# +FROM docker.io/library/debian:11-slim + +# Duplicate deb line as deb-src +RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list + +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y eatmydata && \ + eatmydata apt-get dist-upgrade -y && \ + apt build-dep -yy qemu + +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + build-essential \ + bison \ + ca-certificates \ + ccache \ + clang \ + flex \ + curl \ + gettext \ + git \ + ninja-build \ + python3-pip \ + python3-setuptools \ + python3-venv \ + python3-wheel + +RUN /usr/bin/pip3 install tomli + +RUN curl -#SL https://github.com/loongson/build-tools/releases/download/2023.08.08/CLFS-loongarch64-8.1-x86_64-cross-tools-gcc-glibc.tar.xz \ + | tar -xJC /opt + +ENV PATH $PATH:/opt/cross-tools/bin +ENV LD_LIBRARY_PATH /opt/cross-tools/lib:/opt/cross-tools/loongarch64-unknown-linux-gnu/lib:$LD_LIBRARY_PATH + +ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools +ENV DEF_TARGET_LIST loongarch64-linux-user,loongarch-softmmu + +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-m68k-cross.docker b/tests/docker/dockerfiles/debian-m68k-cross.docker deleted file mode 100644 index fcb10e3534..0000000000 --- a/tests/docker/dockerfiles/debian-m68k-cross.docker +++ /dev/null @@ -1,12 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the debian Buster base image. -# -FROM qemu/debian10 - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-m68k-linux-gnu \ - libc6-dev-m68k-cross diff --git a/tests/docker/dockerfiles/debian-mips-cross.docker b/tests/docker/dockerfiles/debian-mips-cross.docker deleted file mode 100644 index 26c154014d..0000000000 --- a/tests/docker/dockerfiles/debian-mips-cross.docker +++ /dev/null @@ -1,32 +0,0 @@ -# -# Docker mips cross-compiler target -# -# This docker target builds on the debian Buster base image. -# -FROM qemu/debian10 - -MAINTAINER Philippe Mathieu-Daudé - -# Add the foreign architecture we want and install dependencies -RUN dpkg --add-architecture mips -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-mips-linux-gnu - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt build-dep -yy -a mips --arch-only qemu - -# Specify the cross prefix for this image (see tests/docker/common.rc) -ENV QEMU_CONFIGURE_OPTS --cross-prefix=mips-linux-gnu- -ENV DEF_TARGET_LIST mips-softmmu,mipsel-linux-user - -# Install extra libraries to increase code coverage -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - libbz2-dev:mips \ - liblzo2-dev:mips \ - librdmacm-dev:mips \ - libsnappy-dev:mips diff --git a/tests/docker/dockerfiles/debian-mips64-cross.docker b/tests/docker/dockerfiles/debian-mips64-cross.docker deleted file mode 100644 index 09c2ba584e..0000000000 --- a/tests/docker/dockerfiles/debian-mips64-cross.docker +++ /dev/null @@ -1,12 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the debian Buster base image. -# -FROM qemu/debian10 - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-mips64-linux-gnuabi64 \ - libc6-dev-mips64-cross diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index b02dcb7fd9..17d3e01ecc 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -1,6 +1,6 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross mips64el debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch mips64el debian-11 qemu # # https://gitlab.com/libvirt/libvirt-ci @@ -11,59 +11,69 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get install -y eatmydata && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y \ - bash \ - bc \ - bsdextrautils \ - bzip2 \ - ca-certificates \ - ccache \ - dbus \ - debianutils \ - diffutils \ - exuberant-ctags \ - findutils \ - gcovr \ - genisoimage \ - gettext \ - git \ - hostname \ - libpcre2-dev \ - libspice-protocol-dev \ - llvm \ - locales \ - make \ - meson \ - ncat \ - ninja-build \ - openssh-client \ - perl-base \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-yaml \ - rpm2cpio \ - sed \ - sparse \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + bash \ + bc \ + bison \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + flex \ + gcc \ + gcovr \ + gettext \ + git \ + hostname \ + libglib2.0-dev \ + libpcre2-dev \ + libsndio-dev \ + libspice-protocol-dev \ + llvm \ + locales \ + make \ + meson \ + mtools \ + ncat \ + ninja-build \ + openssh-client \ + pkgconf \ + python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-setuptools \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-venv \ + python3-wheel \ + python3-yaml \ + rpm2cpio \ + sed \ + socat \ + sparse \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + xorriso \ + zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ dpkg-reconfigure locales +RUN /usr/bin/pip3 install tomli + +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg --add-architecture mips64el && \ @@ -71,74 +81,77 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-mips64el-linux-gnuabi64 \ - gcc-mips64el-linux-gnuabi64 \ - libaio-dev:mips64el \ - libasound2-dev:mips64el \ - libattr1-dev:mips64el \ - libbpf-dev:mips64el \ - libbrlapi-dev:mips64el \ - libbz2-dev:mips64el \ - libc6-dev:mips64el \ - libcacard-dev:mips64el \ - libcap-ng-dev:mips64el \ - libcapstone-dev:mips64el \ - libcurl4-gnutls-dev:mips64el \ - libdaxctl-dev:mips64el \ - libdrm-dev:mips64el \ - libepoxy-dev:mips64el \ - libfdt-dev:mips64el \ - libffi-dev:mips64el \ - libfuse3-dev:mips64el \ - libgbm-dev:mips64el \ - libgcrypt20-dev:mips64el \ - libglib2.0-dev:mips64el \ - libglusterfs-dev:mips64el \ - libgnutls28-dev:mips64el \ - libgtk-3-dev:mips64el \ - libibumad-dev:mips64el \ - libibverbs-dev:mips64el \ - libiscsi-dev:mips64el \ - libjemalloc-dev:mips64el \ - libjpeg62-turbo-dev:mips64el \ - liblttng-ust-dev:mips64el \ - liblzo2-dev:mips64el \ - libncursesw5-dev:mips64el \ - libnfs-dev:mips64el \ - libnuma-dev:mips64el \ - libpam0g-dev:mips64el \ - libpixman-1-dev:mips64el \ - libpng-dev:mips64el \ - libpulse-dev:mips64el \ - librbd-dev:mips64el \ - librdmacm-dev:mips64el \ - libsasl2-dev:mips64el \ - libsdl2-dev:mips64el \ - libsdl2-image-dev:mips64el \ - libseccomp-dev:mips64el \ - libselinux1-dev:mips64el \ - libslirp-dev:mips64el \ - libsnappy-dev:mips64el \ - libspice-server-dev:mips64el \ - libssh-gcrypt-dev:mips64el \ - libsystemd-dev:mips64el \ - libtasn1-6-dev:mips64el \ - libudev-dev:mips64el \ - liburing-dev:mips64el \ - libusb-1.0-0-dev:mips64el \ - libusbredirhost-dev:mips64el \ - libvdeplug-dev:mips64el \ - libvirglrenderer-dev:mips64el \ - libvte-2.91-dev:mips64el \ - libzstd-dev:mips64el \ - nettle-dev:mips64el \ - systemtap-sdt-dev:mips64el \ - xfslibs-dev:mips64el \ - zlib1g-dev:mips64el && \ + g++-mips64el-linux-gnuabi64 \ + gcc-mips64el-linux-gnuabi64 \ + libaio-dev:mips64el \ + libasound2-dev:mips64el \ + libattr1-dev:mips64el \ + libbpf-dev:mips64el \ + libbrlapi-dev:mips64el \ + libbz2-dev:mips64el \ + libc6-dev:mips64el \ + libcacard-dev:mips64el \ + libcap-ng-dev:mips64el \ + libcapstone-dev:mips64el \ + libcmocka-dev:mips64el \ + libcurl4-gnutls-dev:mips64el \ + libdaxctl-dev:mips64el \ + libdrm-dev:mips64el \ + libepoxy-dev:mips64el \ + libfdt-dev:mips64el \ + libffi-dev:mips64el \ + libfuse3-dev:mips64el \ + libgbm-dev:mips64el \ + libgcrypt20-dev:mips64el \ + libglib2.0-dev:mips64el \ + libglusterfs-dev:mips64el \ + libgnutls28-dev:mips64el \ + libgtk-3-dev:mips64el \ + libibumad-dev:mips64el \ + libibverbs-dev:mips64el \ + libiscsi-dev:mips64el \ + libjemalloc-dev:mips64el \ + libjpeg62-turbo-dev:mips64el \ + libjson-c-dev:mips64el \ + liblttng-ust-dev:mips64el \ + liblzo2-dev:mips64el \ + libncursesw5-dev:mips64el \ + libnfs-dev:mips64el \ + libnuma-dev:mips64el \ + libpam0g-dev:mips64el \ + libpipewire-0.3-dev:mips64el \ + libpixman-1-dev:mips64el \ + libpng-dev:mips64el \ + libpulse-dev:mips64el \ + librbd-dev:mips64el \ + librdmacm-dev:mips64el \ + libsasl2-dev:mips64el \ + libsdl2-dev:mips64el \ + libsdl2-image-dev:mips64el \ + libseccomp-dev:mips64el \ + libselinux1-dev:mips64el \ + libslirp-dev:mips64el \ + libsnappy-dev:mips64el \ + libspice-server-dev:mips64el \ + libssh-gcrypt-dev:mips64el \ + libsystemd-dev:mips64el \ + libtasn1-6-dev:mips64el \ + libudev-dev:mips64el \ + liburing-dev:mips64el \ + libusb-1.0-0-dev:mips64el \ + libusbredirhost-dev:mips64el \ + libvdeplug-dev:mips64el \ + libvirglrenderer-dev:mips64el \ + libvte-2.91-dev:mips64el \ + libzstd-dev:mips64el \ + nettle-dev:mips64el \ + systemtap-sdt-dev:mips64el \ + xfslibs-dev:mips64el \ + zlib1g-dev:mips64el && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ mkdir -p /usr/local/share/meson/cross && \ - echo "[binaries]\n\ + printf "[binaries]\n\ c = '/usr/bin/mips64el-linux-gnuabi64-gcc'\n\ ar = '/usr/bin/mips64el-linux-gnuabi64-gcc-ar'\n\ strip = '/usr/bin/mips64el-linux-gnuabi64-strip'\n\ @@ -148,7 +161,7 @@ pkgconfig = '/usr/bin/mips64el-linux-gnuabi64-pkg-config'\n\ system = 'linux'\n\ cpu_family = 'mips64'\n\ cpu = 'mips64el'\n\ -endian = 'little'" > /usr/local/share/meson/cross/mips64el-linux-gnuabi64 && \ +endian = 'little'\n" > /usr/local/share/meson/cross/mips64el-linux-gnuabi64 && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mips64el-linux-gnuabi64-c++ && \ @@ -160,3 +173,8 @@ ENV ABI "mips64el-linux-gnuabi64" ENV MESON_OPTS "--cross-file=mips64el-linux-gnuabi64" ENV QEMU_CONFIGURE_OPTS --cross-prefix=mips64el-linux-gnuabi64- ENV DEF_TARGET_LIST mips64el-softmmu,mips64el-linux-user +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index b6d99ae324..5fcd641f15 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -1,6 +1,6 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross mipsel debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch mipsel debian-11 qemu # # https://gitlab.com/libvirt/libvirt-ci @@ -11,59 +11,69 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get install -y eatmydata && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y \ - bash \ - bc \ - bsdextrautils \ - bzip2 \ - ca-certificates \ - ccache \ - dbus \ - debianutils \ - diffutils \ - exuberant-ctags \ - findutils \ - gcovr \ - genisoimage \ - gettext \ - git \ - hostname \ - libpcre2-dev \ - libspice-protocol-dev \ - llvm \ - locales \ - make \ - meson \ - ncat \ - ninja-build \ - openssh-client \ - perl-base \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-yaml \ - rpm2cpio \ - sed \ - sparse \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + bash \ + bc \ + bison \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + flex \ + gcc \ + gcovr \ + gettext \ + git \ + hostname \ + libglib2.0-dev \ + libpcre2-dev \ + libsndio-dev \ + libspice-protocol-dev \ + llvm \ + locales \ + make \ + meson \ + mtools \ + ncat \ + ninja-build \ + openssh-client \ + pkgconf \ + python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-setuptools \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-venv \ + python3-wheel \ + python3-yaml \ + rpm2cpio \ + sed \ + socat \ + sparse \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + xorriso \ + zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ dpkg-reconfigure locales +RUN /usr/bin/pip3 install tomli + +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg --add-architecture mipsel && \ @@ -71,74 +81,77 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-mipsel-linux-gnu \ - gcc-mipsel-linux-gnu \ - libaio-dev:mipsel \ - libasound2-dev:mipsel \ - libattr1-dev:mipsel \ - libbpf-dev:mipsel \ - libbrlapi-dev:mipsel \ - libbz2-dev:mipsel \ - libc6-dev:mipsel \ - libcacard-dev:mipsel \ - libcap-ng-dev:mipsel \ - libcapstone-dev:mipsel \ - libcurl4-gnutls-dev:mipsel \ - libdaxctl-dev:mipsel \ - libdrm-dev:mipsel \ - libepoxy-dev:mipsel \ - libfdt-dev:mipsel \ - libffi-dev:mipsel \ - libfuse3-dev:mipsel \ - libgbm-dev:mipsel \ - libgcrypt20-dev:mipsel \ - libglib2.0-dev:mipsel \ - libglusterfs-dev:mipsel \ - libgnutls28-dev:mipsel \ - libgtk-3-dev:mipsel \ - libibumad-dev:mipsel \ - libibverbs-dev:mipsel \ - libiscsi-dev:mipsel \ - libjemalloc-dev:mipsel \ - libjpeg62-turbo-dev:mipsel \ - liblttng-ust-dev:mipsel \ - liblzo2-dev:mipsel \ - libncursesw5-dev:mipsel \ - libnfs-dev:mipsel \ - libnuma-dev:mipsel \ - libpam0g-dev:mipsel \ - libpixman-1-dev:mipsel \ - libpng-dev:mipsel \ - libpulse-dev:mipsel \ - librbd-dev:mipsel \ - librdmacm-dev:mipsel \ - libsasl2-dev:mipsel \ - libsdl2-dev:mipsel \ - libsdl2-image-dev:mipsel \ - libseccomp-dev:mipsel \ - libselinux1-dev:mipsel \ - libslirp-dev:mipsel \ - libsnappy-dev:mipsel \ - libspice-server-dev:mipsel \ - libssh-gcrypt-dev:mipsel \ - libsystemd-dev:mipsel \ - libtasn1-6-dev:mipsel \ - libudev-dev:mipsel \ - liburing-dev:mipsel \ - libusb-1.0-0-dev:mipsel \ - libusbredirhost-dev:mipsel \ - libvdeplug-dev:mipsel \ - libvirglrenderer-dev:mipsel \ - libvte-2.91-dev:mipsel \ - libzstd-dev:mipsel \ - nettle-dev:mipsel \ - systemtap-sdt-dev:mipsel \ - xfslibs-dev:mipsel \ - zlib1g-dev:mipsel && \ + g++-mipsel-linux-gnu \ + gcc-mipsel-linux-gnu \ + libaio-dev:mipsel \ + libasound2-dev:mipsel \ + libattr1-dev:mipsel \ + libbpf-dev:mipsel \ + libbrlapi-dev:mipsel \ + libbz2-dev:mipsel \ + libc6-dev:mipsel \ + libcacard-dev:mipsel \ + libcap-ng-dev:mipsel \ + libcapstone-dev:mipsel \ + libcmocka-dev:mipsel \ + libcurl4-gnutls-dev:mipsel \ + libdaxctl-dev:mipsel \ + libdrm-dev:mipsel \ + libepoxy-dev:mipsel \ + libfdt-dev:mipsel \ + libffi-dev:mipsel \ + libfuse3-dev:mipsel \ + libgbm-dev:mipsel \ + libgcrypt20-dev:mipsel \ + libglib2.0-dev:mipsel \ + libglusterfs-dev:mipsel \ + libgnutls28-dev:mipsel \ + libgtk-3-dev:mipsel \ + libibumad-dev:mipsel \ + libibverbs-dev:mipsel \ + libiscsi-dev:mipsel \ + libjemalloc-dev:mipsel \ + libjpeg62-turbo-dev:mipsel \ + libjson-c-dev:mipsel \ + liblttng-ust-dev:mipsel \ + liblzo2-dev:mipsel \ + libncursesw5-dev:mipsel \ + libnfs-dev:mipsel \ + libnuma-dev:mipsel \ + libpam0g-dev:mipsel \ + libpipewire-0.3-dev:mipsel \ + libpixman-1-dev:mipsel \ + libpng-dev:mipsel \ + libpulse-dev:mipsel \ + librbd-dev:mipsel \ + librdmacm-dev:mipsel \ + libsasl2-dev:mipsel \ + libsdl2-dev:mipsel \ + libsdl2-image-dev:mipsel \ + libseccomp-dev:mipsel \ + libselinux1-dev:mipsel \ + libslirp-dev:mipsel \ + libsnappy-dev:mipsel \ + libspice-server-dev:mipsel \ + libssh-gcrypt-dev:mipsel \ + libsystemd-dev:mipsel \ + libtasn1-6-dev:mipsel \ + libudev-dev:mipsel \ + liburing-dev:mipsel \ + libusb-1.0-0-dev:mipsel \ + libusbredirhost-dev:mipsel \ + libvdeplug-dev:mipsel \ + libvirglrenderer-dev:mipsel \ + libvte-2.91-dev:mipsel \ + libzstd-dev:mipsel \ + nettle-dev:mipsel \ + systemtap-sdt-dev:mipsel \ + xfslibs-dev:mipsel \ + zlib1g-dev:mipsel && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ mkdir -p /usr/local/share/meson/cross && \ - echo "[binaries]\n\ + printf "[binaries]\n\ c = '/usr/bin/mipsel-linux-gnu-gcc'\n\ ar = '/usr/bin/mipsel-linux-gnu-gcc-ar'\n\ strip = '/usr/bin/mipsel-linux-gnu-strip'\n\ @@ -148,7 +161,7 @@ pkgconfig = '/usr/bin/mipsel-linux-gnu-pkg-config'\n\ system = 'linux'\n\ cpu_family = 'mips'\n\ cpu = 'mipsel'\n\ -endian = 'little'" > /usr/local/share/meson/cross/mipsel-linux-gnu && \ +endian = 'little'\n" > /usr/local/share/meson/cross/mipsel-linux-gnu && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mipsel-linux-gnu-c++ && \ @@ -160,3 +173,8 @@ ENV ABI "mipsel-linux-gnu" ENV MESON_OPTS "--cross-file=mipsel-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=mipsel-linux-gnu- ENV DEF_TARGET_LIST mipsel-softmmu,mipsel-linux-user +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-native.docker b/tests/docker/dockerfiles/debian-native.docker deleted file mode 100644 index efd55cb6e0..0000000000 --- a/tests/docker/dockerfiles/debian-native.docker +++ /dev/null @@ -1,49 +0,0 @@ -# -# Docker Debian Native -# -# This this intended to build QEMU on native host systems. Debian is -# chosen due to the broadest range on supported host systems for QEMU. -# -# This docker target is based on the docker.io Debian Bullseye base -# image rather than QEMU's base because we would otherwise confuse the -# build grabbing stuff from the registry built for other -# architectures. -# -FROM docker.io/library/debian:bullseye-slim -MAINTAINER Alex Bennée - -# Duplicate deb line as deb-src -RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list - -# Install common build utilities -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt build-dep -yy --arch-only qemu - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - cscope \ - genisoimage \ - exuberant-ctags \ - global \ - libbz2-dev \ - liblzo2-dev \ - libgcrypt20-dev \ - libfdt-dev \ - librdmacm-dev \ - libsasl2-dev \ - libsnappy-dev \ - libvte-dev \ - netcat-openbsd \ - ninja-build \ - openssh-client \ - python3-numpy \ - python3-opencv \ - python3-venv - -ENV QEMU_CONFIGURE_OPTS $QEMU_CONFIGURE_OPTS -ENV DEF_TARGET_LIST "none" diff --git a/tests/docker/dockerfiles/debian-powerpc-test-cross.docker b/tests/docker/dockerfiles/debian-powerpc-test-cross.docker deleted file mode 100644 index 36b336f709..0000000000 --- a/tests/docker/dockerfiles/debian-powerpc-test-cross.docker +++ /dev/null @@ -1,17 +0,0 @@ -# -# Docker powerpc/ppc64/ppc64le cross-compiler target -# -# This docker target builds on the debian Bullseye base image. -# -FROM qemu/debian11 - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-powerpc-linux-gnu \ - libc6-dev-powerpc-cross \ - gcc-10-powerpc64-linux-gnu \ - libc6-dev-ppc64-cross \ - gcc-10-powerpc64le-linux-gnu \ - libc6-dev-ppc64el-cross - diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index bcf04bc90b..d6be2f0cc5 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -1,69 +1,76 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross ppc64le debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch ppc64le debian-12 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:12-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get install -y eatmydata && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y \ - bash \ - bc \ - bsdextrautils \ - bzip2 \ - ca-certificates \ - ccache \ - dbus \ - debianutils \ - diffutils \ - exuberant-ctags \ - findutils \ - gcovr \ - genisoimage \ - gettext \ - git \ - hostname \ - libpcre2-dev \ - libspice-protocol-dev \ - llvm \ - locales \ - make \ - meson \ - ncat \ - ninja-build \ - openssh-client \ - perl-base \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-yaml \ - rpm2cpio \ - sed \ - sparse \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + bash \ + bc \ + bison \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + flex \ + gcc \ + gcovr \ + gettext \ + git \ + hostname \ + libglib2.0-dev \ + libpcre2-dev \ + libsndio-dev \ + libspice-protocol-dev \ + llvm \ + locales \ + make \ + meson \ + mtools \ + ncat \ + ninja-build \ + openssh-client \ + pkgconf \ + python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-venv \ + python3-yaml \ + rpm2cpio \ + sed \ + socat \ + sparse \ + swtpm \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + xorriso \ + zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ dpkg-reconfigure locales +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg --add-architecture ppc64el && \ @@ -71,76 +78,80 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-powerpc64le-linux-gnu \ - gcc-powerpc64le-linux-gnu \ - libaio-dev:ppc64el \ - libasan5:ppc64el \ - libasound2-dev:ppc64el \ - libattr1-dev:ppc64el \ - libbpf-dev:ppc64el \ - libbrlapi-dev:ppc64el \ - libbz2-dev:ppc64el \ - libc6-dev:ppc64el \ - libcacard-dev:ppc64el \ - libcap-ng-dev:ppc64el \ - libcapstone-dev:ppc64el \ - libcurl4-gnutls-dev:ppc64el \ - libdaxctl-dev:ppc64el \ - libdrm-dev:ppc64el \ - libepoxy-dev:ppc64el \ - libfdt-dev:ppc64el \ - libffi-dev:ppc64el \ - libfuse3-dev:ppc64el \ - libgbm-dev:ppc64el \ - libgcrypt20-dev:ppc64el \ - libglib2.0-dev:ppc64el \ - libglusterfs-dev:ppc64el \ - libgnutls28-dev:ppc64el \ - libgtk-3-dev:ppc64el \ - libibumad-dev:ppc64el \ - libibverbs-dev:ppc64el \ - libiscsi-dev:ppc64el \ - libjemalloc-dev:ppc64el \ - libjpeg62-turbo-dev:ppc64el \ - liblttng-ust-dev:ppc64el \ - liblzo2-dev:ppc64el \ - libncursesw5-dev:ppc64el \ - libnfs-dev:ppc64el \ - libnuma-dev:ppc64el \ - libpam0g-dev:ppc64el \ - libpixman-1-dev:ppc64el \ - libpng-dev:ppc64el \ - libpulse-dev:ppc64el \ - librbd-dev:ppc64el \ - librdmacm-dev:ppc64el \ - libsasl2-dev:ppc64el \ - libsdl2-dev:ppc64el \ - libsdl2-image-dev:ppc64el \ - libseccomp-dev:ppc64el \ - libselinux1-dev:ppc64el \ - libslirp-dev:ppc64el \ - libsnappy-dev:ppc64el \ - libspice-server-dev:ppc64el \ - libssh-gcrypt-dev:ppc64el \ - libsystemd-dev:ppc64el \ - libtasn1-6-dev:ppc64el \ - libubsan1:ppc64el \ - libudev-dev:ppc64el \ - liburing-dev:ppc64el \ - libusb-1.0-0-dev:ppc64el \ - libusbredirhost-dev:ppc64el \ - libvdeplug-dev:ppc64el \ - libvirglrenderer-dev:ppc64el \ - libvte-2.91-dev:ppc64el \ - libzstd-dev:ppc64el \ - nettle-dev:ppc64el \ - systemtap-sdt-dev:ppc64el \ - xfslibs-dev:ppc64el \ - zlib1g-dev:ppc64el && \ + g++-powerpc64le-linux-gnu \ + gcc-powerpc64le-linux-gnu \ + libaio-dev:ppc64el \ + libasan6:ppc64el \ + libasound2-dev:ppc64el \ + libattr1-dev:ppc64el \ + libbpf-dev:ppc64el \ + libbrlapi-dev:ppc64el \ + libbz2-dev:ppc64el \ + libc6-dev:ppc64el \ + libcacard-dev:ppc64el \ + libcap-ng-dev:ppc64el \ + libcapstone-dev:ppc64el \ + libcmocka-dev:ppc64el \ + libcurl4-gnutls-dev:ppc64el \ + libdaxctl-dev:ppc64el \ + libdrm-dev:ppc64el \ + libepoxy-dev:ppc64el \ + libfdt-dev:ppc64el \ + libffi-dev:ppc64el \ + libfuse3-dev:ppc64el \ + libgbm-dev:ppc64el \ + libgcrypt20-dev:ppc64el \ + libglib2.0-dev:ppc64el \ + libglusterfs-dev:ppc64el \ + libgnutls28-dev:ppc64el \ + libgtk-3-dev:ppc64el \ + libibumad-dev:ppc64el \ + libibverbs-dev:ppc64el \ + libiscsi-dev:ppc64el \ + libjemalloc-dev:ppc64el \ + libjpeg62-turbo-dev:ppc64el \ + libjson-c-dev:ppc64el \ + liblttng-ust-dev:ppc64el \ + liblzo2-dev:ppc64el \ + libncursesw5-dev:ppc64el \ + libnfs-dev:ppc64el \ + libnuma-dev:ppc64el \ + libpam0g-dev:ppc64el \ + libpipewire-0.3-dev:ppc64el \ + libpixman-1-dev:ppc64el \ + libpng-dev:ppc64el \ + libpulse-dev:ppc64el \ + librbd-dev:ppc64el \ + librdmacm-dev:ppc64el \ + libsasl2-dev:ppc64el \ + libsdl2-dev:ppc64el \ + libsdl2-image-dev:ppc64el \ + libseccomp-dev:ppc64el \ + libselinux1-dev:ppc64el \ + libslirp-dev:ppc64el \ + libsnappy-dev:ppc64el \ + libspice-server-dev:ppc64el \ + libssh-gcrypt-dev:ppc64el \ + libsystemd-dev:ppc64el \ + libtasn1-6-dev:ppc64el \ + libubsan1:ppc64el \ + libudev-dev:ppc64el \ + liburing-dev:ppc64el \ + libusb-1.0-0-dev:ppc64el \ + libusbredirhost-dev:ppc64el \ + libvdeplug-dev:ppc64el \ + libvirglrenderer-dev:ppc64el \ + libvte-2.91-dev:ppc64el \ + libxdp-dev:ppc64el \ + libzstd-dev:ppc64el \ + nettle-dev:ppc64el \ + systemtap-sdt-dev:ppc64el \ + xfslibs-dev:ppc64el \ + zlib1g-dev:ppc64el && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ mkdir -p /usr/local/share/meson/cross && \ - echo "[binaries]\n\ + printf "[binaries]\n\ c = '/usr/bin/powerpc64le-linux-gnu-gcc'\n\ ar = '/usr/bin/powerpc64le-linux-gnu-gcc-ar'\n\ strip = '/usr/bin/powerpc64le-linux-gnu-strip'\n\ @@ -150,7 +161,7 @@ pkgconfig = '/usr/bin/powerpc64le-linux-gnu-pkg-config'\n\ system = 'linux'\n\ cpu_family = 'ppc64'\n\ cpu = 'powerpc64le'\n\ -endian = 'little'" > /usr/local/share/meson/cross/powerpc64le-linux-gnu && \ +endian = 'little'\n" > /usr/local/share/meson/cross/powerpc64le-linux-gnu && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/powerpc64le-linux-gnu-c++ && \ @@ -162,3 +173,8 @@ ENV ABI "powerpc64le-linux-gnu" ENV MESON_OPTS "--cross-file=powerpc64le-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=powerpc64le-linux-gnu- ENV DEF_TARGET_LIST ppc64-softmmu,ppc64-linux-user +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker index 594d97982c..a26637ec4f 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -1,48 +1,89 @@ +# THIS FILE WAS AUTO-GENERATED # -# Docker cross-compiler target for riscv64 -# -# Currently the only distro that gets close to cross compiling riscv64 -# images is Debian Sid (with unofficial ports). As this is a moving -# target we keep the library list minimal and are aiming to migrate -# from this hack as soon as we are able. +# $ lcitool dockerfile --layers all --cross-arch riscv64 debian-sid qemu-minimal # +# https://gitlab.com/libvirt/libvirt-ci + FROM docker.io/library/debian:sid-slim -# Add ports -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ - DEBIAN_FRONTEND=noninteractive eatmydata apt update -yy && \ - DEBIAN_FRONTEND=noninteractive eatmydata apt upgrade -yy +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y eatmydata && \ + eatmydata apt-get dist-upgrade -y && \ + eatmydata apt-get install --no-install-recommends -y \ + bash \ + bc \ + bison \ + ca-certificates \ + ccache \ + findutils \ + flex \ + gcc \ + git \ + libglib2.0-dev \ + locales \ + make \ + meson \ + ninja-build \ + pkgconf \ + python3 \ + python3-venv \ + sed \ + tar && \ + eatmydata apt-get autoremove -y && \ + eatmydata apt-get autoclean -y && \ + sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ + dpkg-reconfigure locales -# Install common build utilities -RUN DEBIAN_FRONTEND=noninteractive eatmydata apt install -yy \ - bc \ - build-essential \ - ca-certificates \ - debian-ports-archive-keyring \ - dpkg-dev \ - gettext \ - git \ - ninja-build \ - pkg-config \ - python3 +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" +ENV LANG "en_US.UTF-8" +ENV MAKE "/usr/bin/make" +ENV NINJA "/usr/bin/ninja" +ENV PYTHON "/usr/bin/python3" -# Add ports and riscv64 architecture -RUN echo "deb http://ftp.ports.debian.org/debian-ports/ sid main" >> /etc/apt/sources.list -RUN dpkg --add-architecture riscv64 +RUN export DEBIAN_FRONTEND=noninteractive && \ + dpkg --add-architecture riscv64 && \ + eatmydata apt-get install debian-ports-archive-keyring && \ + eatmydata echo 'deb http://ftp.ports.debian.org/debian-ports/ sid main' > /etc/apt/sources.list.d/ports.list && \ + eatmydata echo 'deb http://ftp.ports.debian.org/debian-ports/ unreleased main' >> /etc/apt/sources.list.d/ports.list && \ + eatmydata apt-get update && \ + eatmydata apt-get dist-upgrade -y && \ + eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ + eatmydata apt-get install --no-install-recommends -y \ + g++-riscv64-linux-gnu \ + gcc-riscv64-linux-gnu \ + libc6-dev:riscv64 \ + libfdt-dev:riscv64 \ + libffi-dev:riscv64 \ + libglib2.0-dev:riscv64 \ + libpixman-1-dev:riscv64 && \ + eatmydata apt-get autoremove -y && \ + eatmydata apt-get autoclean -y && \ + mkdir -p /usr/local/share/meson/cross && \ + printf "[binaries]\n\ +c = '/usr/bin/riscv64-linux-gnu-gcc'\n\ +ar = '/usr/bin/riscv64-linux-gnu-gcc-ar'\n\ +strip = '/usr/bin/riscv64-linux-gnu-strip'\n\ +pkgconfig = '/usr/bin/riscv64-linux-gnu-pkg-config'\n\ +\n\ +[host_machine]\n\ +system = 'linux'\n\ +cpu_family = 'riscv64'\n\ +cpu = 'riscv64'\n\ +endian = 'little'\n" > /usr/local/share/meson/cross/riscv64-linux-gnu && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ + mkdir -p /usr/libexec/ccache-wrappers && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-c++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-cc && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-g++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-gcc -# Duplicate deb line as deb-src -RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-riscv64-linux-gnu \ - libc6-dev-riscv64-cross \ - libffi-dev:riscv64 \ - libglib2.0-dev:riscv64 \ - libpixman-1-dev:riscv64 - -# Specify the cross prefix for this image (see tests/docker/common.rc) +ENV ABI "riscv64-linux-gnu" +ENV MESON_OPTS "--cross-file=riscv64-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=riscv64-linux-gnu- ENV DEF_TARGET_LIST riscv64-softmmu,riscv64-linux-user +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-riscv64-test-cross.docker b/tests/docker/dockerfiles/debian-riscv64-test-cross.docker deleted file mode 100644 index 1d90901298..0000000000 --- a/tests/docker/dockerfiles/debian-riscv64-test-cross.docker +++ /dev/null @@ -1,12 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -FROM qemu/debian11 - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-riscv64-linux-gnu \ - libc6-dev-riscv64-cross diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index ff79a2cc4f..ec0041d6aa 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -1,69 +1,76 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross s390x debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch s390x debian-12 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:12-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get install -y eatmydata && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y \ - bash \ - bc \ - bsdextrautils \ - bzip2 \ - ca-certificates \ - ccache \ - dbus \ - debianutils \ - diffutils \ - exuberant-ctags \ - findutils \ - gcovr \ - genisoimage \ - gettext \ - git \ - hostname \ - libpcre2-dev \ - libspice-protocol-dev \ - llvm \ - locales \ - make \ - meson \ - ncat \ - ninja-build \ - openssh-client \ - perl-base \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-yaml \ - rpm2cpio \ - sed \ - sparse \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ + bash \ + bc \ + bison \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + flex \ + gcc \ + gcovr \ + gettext \ + git \ + hostname \ + libglib2.0-dev \ + libpcre2-dev \ + libsndio-dev \ + libspice-protocol-dev \ + llvm \ + locales \ + make \ + meson \ + mtools \ + ncat \ + ninja-build \ + openssh-client \ + pkgconf \ + python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-venv \ + python3-yaml \ + rpm2cpio \ + sed \ + socat \ + sparse \ + swtpm \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + xorriso \ + zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ dpkg-reconfigure locales +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg --add-architecture s390x && \ @@ -71,75 +78,79 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ eatmydata apt-get install --no-install-recommends -y \ - g++-s390x-linux-gnu \ - gcc-s390x-linux-gnu \ - libaio-dev:s390x \ - libasan5:s390x \ - libasound2-dev:s390x \ - libattr1-dev:s390x \ - libbpf-dev:s390x \ - libbrlapi-dev:s390x \ - libbz2-dev:s390x \ - libc6-dev:s390x \ - libcacard-dev:s390x \ - libcap-ng-dev:s390x \ - libcapstone-dev:s390x \ - libcurl4-gnutls-dev:s390x \ - libdaxctl-dev:s390x \ - libdrm-dev:s390x \ - libepoxy-dev:s390x \ - libfdt-dev:s390x \ - libffi-dev:s390x \ - libfuse3-dev:s390x \ - libgbm-dev:s390x \ - libgcrypt20-dev:s390x \ - libglib2.0-dev:s390x \ - libglusterfs-dev:s390x \ - libgnutls28-dev:s390x \ - libgtk-3-dev:s390x \ - libibumad-dev:s390x \ - libibverbs-dev:s390x \ - libiscsi-dev:s390x \ - libjemalloc-dev:s390x \ - libjpeg62-turbo-dev:s390x \ - liblttng-ust-dev:s390x \ - liblzo2-dev:s390x \ - libncursesw5-dev:s390x \ - libnfs-dev:s390x \ - libnuma-dev:s390x \ - libpam0g-dev:s390x \ - libpixman-1-dev:s390x \ - libpng-dev:s390x \ - libpulse-dev:s390x \ - librbd-dev:s390x \ - librdmacm-dev:s390x \ - libsasl2-dev:s390x \ - libsdl2-dev:s390x \ - libsdl2-image-dev:s390x \ - libseccomp-dev:s390x \ - libselinux1-dev:s390x \ - libslirp-dev:s390x \ - libsnappy-dev:s390x \ - libssh-gcrypt-dev:s390x \ - libsystemd-dev:s390x \ - libtasn1-6-dev:s390x \ - libubsan1:s390x \ - libudev-dev:s390x \ - liburing-dev:s390x \ - libusb-1.0-0-dev:s390x \ - libusbredirhost-dev:s390x \ - libvdeplug-dev:s390x \ - libvirglrenderer-dev:s390x \ - libvte-2.91-dev:s390x \ - libzstd-dev:s390x \ - nettle-dev:s390x \ - systemtap-sdt-dev:s390x \ - xfslibs-dev:s390x \ - zlib1g-dev:s390x && \ + g++-s390x-linux-gnu \ + gcc-s390x-linux-gnu \ + libaio-dev:s390x \ + libasan6:s390x \ + libasound2-dev:s390x \ + libattr1-dev:s390x \ + libbpf-dev:s390x \ + libbrlapi-dev:s390x \ + libbz2-dev:s390x \ + libc6-dev:s390x \ + libcacard-dev:s390x \ + libcap-ng-dev:s390x \ + libcapstone-dev:s390x \ + libcmocka-dev:s390x \ + libcurl4-gnutls-dev:s390x \ + libdaxctl-dev:s390x \ + libdrm-dev:s390x \ + libepoxy-dev:s390x \ + libfdt-dev:s390x \ + libffi-dev:s390x \ + libfuse3-dev:s390x \ + libgbm-dev:s390x \ + libgcrypt20-dev:s390x \ + libglib2.0-dev:s390x \ + libglusterfs-dev:s390x \ + libgnutls28-dev:s390x \ + libgtk-3-dev:s390x \ + libibumad-dev:s390x \ + libibverbs-dev:s390x \ + libiscsi-dev:s390x \ + libjemalloc-dev:s390x \ + libjpeg62-turbo-dev:s390x \ + libjson-c-dev:s390x \ + liblttng-ust-dev:s390x \ + liblzo2-dev:s390x \ + libncursesw5-dev:s390x \ + libnfs-dev:s390x \ + libnuma-dev:s390x \ + libpam0g-dev:s390x \ + libpipewire-0.3-dev:s390x \ + libpixman-1-dev:s390x \ + libpng-dev:s390x \ + libpulse-dev:s390x \ + librbd-dev:s390x \ + librdmacm-dev:s390x \ + libsasl2-dev:s390x \ + libsdl2-dev:s390x \ + libsdl2-image-dev:s390x \ + libseccomp-dev:s390x \ + libselinux1-dev:s390x \ + libslirp-dev:s390x \ + libsnappy-dev:s390x \ + libssh-gcrypt-dev:s390x \ + libsystemd-dev:s390x \ + libtasn1-6-dev:s390x \ + libubsan1:s390x \ + libudev-dev:s390x \ + liburing-dev:s390x \ + libusb-1.0-0-dev:s390x \ + libusbredirhost-dev:s390x \ + libvdeplug-dev:s390x \ + libvirglrenderer-dev:s390x \ + libvte-2.91-dev:s390x \ + libxdp-dev:s390x \ + libzstd-dev:s390x \ + nettle-dev:s390x \ + systemtap-sdt-dev:s390x \ + xfslibs-dev:s390x \ + zlib1g-dev:s390x && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ mkdir -p /usr/local/share/meson/cross && \ - echo "[binaries]\n\ + printf "[binaries]\n\ c = '/usr/bin/s390x-linux-gnu-gcc'\n\ ar = '/usr/bin/s390x-linux-gnu-gcc-ar'\n\ strip = '/usr/bin/s390x-linux-gnu-strip'\n\ @@ -149,7 +160,7 @@ pkgconfig = '/usr/bin/s390x-linux-gnu-pkg-config'\n\ system = 'linux'\n\ cpu_family = 's390x'\n\ cpu = 's390x'\n\ -endian = 'big'" > /usr/local/share/meson/cross/s390x-linux-gnu && \ +endian = 'big'\n" > /usr/local/share/meson/cross/s390x-linux-gnu && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-c++ && \ @@ -161,3 +172,8 @@ ENV ABI "s390x-linux-gnu" ENV MESON_OPTS "--cross-file=s390x-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=s390x-linux-gnu- ENV DEF_TARGET_LIST s390x-softmmu,s390x-linux-user +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-sh4-cross.docker b/tests/docker/dockerfiles/debian-sh4-cross.docker deleted file mode 100644 index fd3af89575..0000000000 --- a/tests/docker/dockerfiles/debian-sh4-cross.docker +++ /dev/null @@ -1,12 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the debian Buster base image. -# -FROM qemu/debian10 - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-sh4-linux-gnu \ - libc6-dev-sh4-cross diff --git a/tests/docker/dockerfiles/debian-sparc64-cross.docker b/tests/docker/dockerfiles/debian-sparc64-cross.docker deleted file mode 100644 index f4bb9b561c..0000000000 --- a/tests/docker/dockerfiles/debian-sparc64-cross.docker +++ /dev/null @@ -1,12 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the debian Buster base image. -# -FROM qemu/debian10 - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-sparc64-linux-gnu \ - libc6-dev-sparc64-cross diff --git a/tests/docker/dockerfiles/debian-toolchain.docker b/tests/docker/dockerfiles/debian-toolchain.docker index 738d808aa6..687a97fec4 100644 --- a/tests/docker/dockerfiles/debian-toolchain.docker +++ b/tests/docker/dockerfiles/debian-toolchain.docker @@ -4,7 +4,7 @@ # This dockerfile is used for building a cross-compiler toolchain. # The script for building the toolchain is supplied via extra-files. # -FROM qemu/debian10 +FROM docker.io/library/debian:11-slim # Install build utilities for building gcc and glibc. # ??? The build-dep isn't working, missing a number of @@ -15,12 +15,12 @@ RUN apt update && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends \ bison \ + ca-certificates \ flex \ gawk \ libmpc-dev \ libmpfr-dev \ rsync \ - texinfo \ wget && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt build-dep -yy --arch-only gcc glibc @@ -30,7 +30,12 @@ ADD build-toolchain.sh /root/build-toolchain.sh RUN cd /root && ./build-toolchain.sh # Throw away the extra toolchain build deps, the downloaded source, -# and the build trees by restoring the original debian10 image, +# and the build trees by restoring the original image, # then copying the built toolchain from stage 0. -FROM qemu/debian10 +FROM docker.io/library/debian:11-slim COPY --from=0 /usr/local /usr/local +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker index b573b9ded2..c597f8e16b 100644 --- a/tests/docker/dockerfiles/debian-tricore-cross.docker +++ b/tests/docker/dockerfiles/debian-tricore-cross.docker @@ -9,7 +9,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later # -FROM docker.io/library/debian:buster-slim +FROM docker.io/library/debian:11-slim MAINTAINER Philippe Mathieu-Daudé @@ -20,6 +20,8 @@ RUN apt update && \ bzip2 \ ca-certificates \ ccache \ + curl \ + flex \ g++ \ gcc \ git \ @@ -28,20 +30,22 @@ RUN apt update && \ locales \ make \ ninja-build \ - perl-base \ pkgconf \ python3-pip \ python3-setuptools \ - python3-wheel + python3-wheel \ + python3-venv -RUN git clone --single-branch \ - https://github.com/bkoppelmann/tricore-binutils.git \ - /usr/src/binutils && \ - cd /usr/src/binutils && chmod +x missing && \ - CFLAGS=-w ./configure --prefix=/usr/local --disable-nls --target=tricore && \ - make && make install && \ - rm -rf /usr/src/binutils +RUN /usr/bin/pip3 install tomli + +RUN curl -#SL https://github.com/bkoppelmann/package_940/releases/download/tricore-toolchain-9.40/tricore-toolchain-9.4.0.tar.gz \ + | tar -xzC /usr/local/ # This image can only build a very minimal QEMU as well as the tests ENV DEF_TARGET_LIST tricore-softmmu ENV QEMU_CONFIGURE_OPTS --disable-user --disable-tools --disable-fdt +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-xtensa-cross.docker b/tests/docker/dockerfiles/debian-xtensa-cross.docker index 2f11b3b7bc..72c25d63d9 100644 --- a/tests/docker/dockerfiles/debian-xtensa-cross.docker +++ b/tests/docker/dockerfiles/debian-xtensa-cross.docker @@ -5,7 +5,7 @@ # using a prebuilt toolchains for Xtensa cores from: # https://github.com/foss-xtensa/toolchain/releases # -FROM docker.io/library/debian:stretch-slim +FROM docker.io/library/debian:11-slim RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ @@ -27,3 +27,8 @@ RUN for cpu in $CPU_LIST; do \ done ENV PATH $PATH:/opt/$TOOLCHAIN_RELEASE/xtensa-dc232b-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-dc233c-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-de233_fpu-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-dsp3400-elf/bin +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker new file mode 100644 index 0000000000..b5e642d5b6 --- /dev/null +++ b/tests/docker/dockerfiles/debian.docker @@ -0,0 +1,170 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool dockerfile --layers all debian-12 qemu +# +# https://gitlab.com/libvirt/libvirt-ci + +FROM docker.io/library/debian:12-slim + +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y eatmydata && \ + eatmydata apt-get dist-upgrade -y && \ + eatmydata apt-get install --no-install-recommends -y \ + bash \ + bc \ + bison \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + clang \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + flex \ + g++ \ + gcc \ + gcovr \ + gettext \ + git \ + hostname \ + libaio-dev \ + libasan6 \ + libasound2-dev \ + libattr1-dev \ + libbpf-dev \ + libbrlapi-dev \ + libbz2-dev \ + libc6-dev \ + libcacard-dev \ + libcap-ng-dev \ + libcapstone-dev \ + libcmocka-dev \ + libcurl4-gnutls-dev \ + libdaxctl-dev \ + libdrm-dev \ + libepoxy-dev \ + libfdt-dev \ + libffi-dev \ + libfuse3-dev \ + libgbm-dev \ + libgcrypt20-dev \ + libglib2.0-dev \ + libglusterfs-dev \ + libgnutls28-dev \ + libgtk-3-dev \ + libibumad-dev \ + libibverbs-dev \ + libiscsi-dev \ + libjemalloc-dev \ + libjpeg62-turbo-dev \ + libjson-c-dev \ + liblttng-ust-dev \ + liblzo2-dev \ + libncursesw5-dev \ + libnfs-dev \ + libnuma-dev \ + libpam0g-dev \ + libpcre2-dev \ + libpipewire-0.3-dev \ + libpixman-1-dev \ + libpmem-dev \ + libpng-dev \ + libpulse-dev \ + librbd-dev \ + librdmacm-dev \ + libsasl2-dev \ + libsdl2-dev \ + libsdl2-image-dev \ + libseccomp-dev \ + libselinux1-dev \ + libslirp-dev \ + libsnappy-dev \ + libsndio-dev \ + libspice-protocol-dev \ + libspice-server-dev \ + libssh-gcrypt-dev \ + libsystemd-dev \ + libtasn1-6-dev \ + libubsan1 \ + libudev-dev \ + liburing-dev \ + libusb-1.0-0-dev \ + libusbredirhost-dev \ + libvdeplug-dev \ + libvirglrenderer-dev \ + libvte-2.91-dev \ + libxdp-dev \ + libxen-dev \ + libzstd-dev \ + llvm \ + locales \ + make \ + meson \ + mtools \ + multipath-tools \ + ncat \ + nettle-dev \ + ninja-build \ + openssh-client \ + pkgconf \ + python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-venv \ + python3-yaml \ + rpm2cpio \ + sed \ + socat \ + sparse \ + swtpm \ + systemtap-sdt-dev \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + xfslibs-dev \ + xorriso \ + zlib1g-dev \ + zstd && \ + eatmydata apt-get autoremove -y && \ + eatmydata apt-get autoclean -y && \ + sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ + dpkg-reconfigure locales && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ + mkdir -p /usr/libexec/ccache-wrappers && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc + +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" +ENV LANG "en_US.UTF-8" +ENV MAKE "/usr/bin/make" +ENV NINJA "/usr/bin/ninja" +ENV PYTHON "/usr/bin/python3" +# netmap/cscope/global +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + cscope\ + global\ + linux-headers-generic +RUN git clone https://github.com/luigirizzo/netmap.git /usr/src/netmap +RUN cd /usr/src/netmap && git checkout v11.3 +RUN cd /usr/src/netmap/LINUX && \ + ./configure --no-drivers --no-apps \ + --kernel-dir=$(ls -d /usr/src/linux-headers-*-$(dpkg --print-architecture)) \ + && make install +ENV QEMU_CONFIGURE_OPTS --enable-netmap +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian10.docker b/tests/docker/dockerfiles/debian10.docker deleted file mode 100644 index 03be923066..0000000000 --- a/tests/docker/dockerfiles/debian10.docker +++ /dev/null @@ -1,38 +0,0 @@ -# -# Docker multiarch cross-compiler target -# -# This docker target is builds on Debian cross compiler targets to build distro -# with a selection of cross compilers for building test binaries. -# -# On its own you can't build much but the docker-foo-cross targets -# build on top of the base debian image. -# -FROM docker.io/library/debian:buster-slim - -# Duplicate deb line as deb-src -RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list - -# Install common build utilities -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - bc \ - build-essential \ - ca-certificates \ - ccache \ - clang \ - dbus \ - gdb-multiarch \ - gettext \ - git \ - libffi-dev \ - libncurses5-dev \ - ninja-build \ - pkg-config \ - psmisc \ - python3 \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - $(apt-get -s build-dep --arch-only qemu | egrep ^Inst | fgrep '[all]' | cut -d\ -f2) diff --git a/tests/docker/dockerfiles/debian11.docker b/tests/docker/dockerfiles/debian11.docker deleted file mode 100644 index febf884f8f..0000000000 --- a/tests/docker/dockerfiles/debian11.docker +++ /dev/null @@ -1,18 +0,0 @@ -# -# Docker multiarch cross-compiler target -# -# This docker target uses the current development version of Debian as -# a base for cross compilers for building test binaries. We won't -# attempt to build QEMU on it yet given it is still in development. -# -# On its own you can't build much but the docker-foo-cross targets -# build on top of the base debian image. -# -FROM docker.io/library/debian:bullseye-slim - -# Duplicate deb line as deb-src -RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list - -# Install common build utilities -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata diff --git a/tests/docker/dockerfiles/fedora-cris-cross.docker b/tests/docker/dockerfiles/fedora-cris-cross.docker index 91c373fdd3..f2899af410 100644 --- a/tests/docker/dockerfiles/fedora-cris-cross.docker +++ b/tests/docker/dockerfiles/fedora-cris-cross.docker @@ -6,3 +6,8 @@ FROM registry.fedoraproject.org/fedora:33 ENV PACKAGES gcc-cris-linux-gnu RUN dnf install -y $PACKAGES RUN rpm -q $PACKAGES | sort > /packages.txt +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/fedora-i386-cross.docker b/tests/docker/dockerfiles/fedora-i386-cross.docker deleted file mode 100644 index 0a3ec346e6..0000000000 --- a/tests/docker/dockerfiles/fedora-i386-cross.docker +++ /dev/null @@ -1,31 +0,0 @@ -FROM registry.fedoraproject.org/fedora:34 - -ENV PACKAGES \ - bzip2 \ - ccache \ - diffutils \ - findutils \ - gcc \ - git \ - libffi-devel.i686 \ - libselinux-devel.i686 \ - libtasn1-devel.i686 \ - libzstd-devel.i686 \ - make \ - meson \ - ninja-build \ - glib2-devel.i686 \ - glibc-devel.i686 \ - glibc-static.i686 \ - gnutls-devel.i686 \ - nettle-devel.i686 \ - pcre-devel.i686 \ - pixman-devel.i686 \ - sysprof-capture-devel.i686 \ - zlib-devel.i686 - -ENV QEMU_CONFIGURE_OPTS --cpu=i386 --disable-vhost-user -ENV PKG_CONFIG_LIBDIR /usr/lib/pkgconfig - -RUN dnf update -y && dnf install -y $PACKAGES -RUN rpm -q $PACKAGES | sort > /packages.txt diff --git a/tests/docker/dockerfiles/fedora-win32-cross.docker b/tests/docker/dockerfiles/fedora-win32-cross.docker deleted file mode 100644 index a06bd29e8e..0000000000 --- a/tests/docker/dockerfiles/fedora-win32-cross.docker +++ /dev/null @@ -1,43 +0,0 @@ -FROM registry.fedoraproject.org/fedora:35 - -# Please keep this list sorted alphabetically -ENV PACKAGES \ - bc \ - bzip2 \ - ccache \ - diffutils \ - findutils \ - gcc \ - gettext \ - git \ - hostname \ - make \ - meson \ - mingw32-bzip2 \ - mingw32-curl \ - mingw32-glib2 \ - mingw32-gmp \ - mingw32-gnutls \ - mingw32-gtk3 \ - mingw32-libffi \ - mingw32-libjpeg-turbo \ - mingw32-libpng \ - mingw32-libtasn1 \ - mingw32-libusbx \ - mingw32-nettle \ - mingw32-nsis \ - mingw32-pixman \ - mingw32-pkg-config \ - mingw32-SDL2 \ - msitools \ - perl \ - python3 \ - python3-PyYAML \ - tar \ - which - -RUN dnf install -y $PACKAGES -RUN rpm -q $PACKAGES | sort > /packages.txt - -# Specify the cross prefix for this image (see tests/docker/common.rc) -ENV QEMU_CONFIGURE_OPTS --cross-prefix=i686-w64-mingw32- diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index b71624330f..f8e4cb70d3 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -1,40 +1,111 @@ -FROM registry.fedoraproject.org/fedora:35 +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool dockerfile --layers all --cross-arch mingw64 fedora-38 qemu +# +# https://gitlab.com/libvirt/libvirt-ci -# Please keep this list sorted alphabetically -ENV PACKAGES \ - bc \ - bzip2 \ - ccache \ - diffutils \ - findutils \ - gcc \ - gettext \ - git \ - hostname \ - make \ - meson \ - mingw32-nsis \ - mingw64-bzip2 \ - mingw64-curl \ - mingw64-glib2 \ - mingw64-gmp \ - mingw64-gtk3 \ - mingw64-libffi \ - mingw64-libjpeg-turbo \ - mingw64-libpng \ - mingw64-libtasn1 \ - mingw64-libusbx \ - mingw64-pixman \ - mingw64-pkg-config \ - msitools \ - perl \ - python3 \ - python3-PyYAML \ - tar \ - which +FROM registry.fedoraproject.org/fedora:38 -RUN dnf install -y $PACKAGES -RUN rpm -q $PACKAGES | sort > /packages.txt +RUN dnf install -y nosync && \ + printf '#!/bin/sh\n\ +if test -d /usr/lib64\n\ +then\n\ + export LD_PRELOAD=/usr/lib64/nosync/nosync.so\n\ +else\n\ + export LD_PRELOAD=/usr/lib/nosync/nosync.so\n\ +fi\n\ +exec "$@"\n' > /usr/bin/nosync && \ + chmod +x /usr/bin/nosync && \ + nosync dnf update -y && \ + nosync dnf install -y \ + bash \ + bc \ + bison \ + bzip2 \ + ca-certificates \ + ccache \ + ctags \ + dbus-daemon \ + diffutils \ + findutils \ + flex \ + gcc \ + gcovr \ + git \ + glib2-devel \ + glibc-langpack-en \ + hostname \ + llvm \ + make \ + meson \ + mtools \ + ninja-build \ + nmap-ncat \ + openssh-clients \ + pcre-static \ + python3 \ + python3-PyYAML \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx_rtd_theme \ + sed \ + socat \ + sparse \ + spice-protocol \ + swtpm \ + tar \ + tesseract \ + tesseract-langpack-eng \ + util-linux \ + which \ + xorriso \ + zstd && \ + nosync dnf autoremove -y && \ + nosync dnf clean all -y -# Specify the cross prefix for this image (see tests/docker/common.rc) -ENV QEMU_CONFIGURE_OPTS --cross-prefix=x86_64-w64-mingw32- --disable-capstone +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" +ENV LANG "en_US.UTF-8" +ENV MAKE "/usr/bin/make" +ENV NINJA "/usr/bin/ninja" +ENV PYTHON "/usr/bin/python3" + +RUN nosync dnf install -y \ + mingw32-nsis \ + mingw64-SDL2 \ + mingw64-SDL2_image \ + mingw64-bzip2 \ + mingw64-curl \ + mingw64-gcc \ + mingw64-gcc-c++ \ + mingw64-gettext \ + mingw64-glib2 \ + mingw64-gnutls \ + mingw64-gtk3 \ + mingw64-libepoxy \ + mingw64-libgcrypt \ + mingw64-libjpeg-turbo \ + mingw64-libpng \ + mingw64-libtasn1 \ + mingw64-nettle \ + mingw64-pixman \ + mingw64-pkg-config && \ + nosync dnf clean all -y && \ + rpm -qa | sort > /packages.txt && \ + mkdir -p /usr/libexec/ccache-wrappers && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-w64-mingw32-c++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-w64-mingw32-cc && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-w64-mingw32-g++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-w64-mingw32-gcc + +ENV ABI "x86_64-w64-mingw32" +ENV MESON_OPTS "--cross-file=/usr/share/mingw/toolchain-mingw64.meson" +ENV QEMU_CONFIGURE_OPTS --cross-prefix=x86_64-w64-mingw32- +ENV DEF_TARGET_LIST x86_64-softmmu +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 2e6a84abfd..9e9c71fa94 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -1,134 +1,140 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all fedora-35 qemu +# $ lcitool dockerfile --layers all fedora-38 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.fedoraproject.org/fedora:35 +FROM registry.fedoraproject.org/fedora:38 RUN dnf install -y nosync && \ - echo -e '#!/bin/sh\n\ + printf '#!/bin/sh\n\ if test -d /usr/lib64\n\ then\n\ export LD_PRELOAD=/usr/lib64/nosync/nosync.so\n\ else\n\ export LD_PRELOAD=/usr/lib/nosync/nosync.so\n\ fi\n\ -exec "$@"' > /usr/bin/nosync && \ +exec "$@"\n' > /usr/bin/nosync && \ chmod +x /usr/bin/nosync && \ nosync dnf update -y && \ nosync dnf install -y \ - SDL2-devel \ - SDL2_image-devel \ - alsa-lib-devel \ - bash \ - bc \ - brlapi-devel \ - bzip2 \ - bzip2-devel \ - ca-certificates \ - capstone-devel \ - ccache \ - clang \ - ctags \ - cyrus-sasl-devel \ - daxctl-devel \ - dbus-daemon \ - device-mapper-multipath-devel \ - diffutils \ - findutils \ - fuse3-devel \ - gcc \ - gcc-c++ \ - gcovr \ - genisoimage \ - gettext \ - git \ - glib2-devel \ - glib2-static \ - glibc-langpack-en \ - glibc-static \ - glusterfs-api-devel \ - gnutls-devel \ - gtk3-devel \ - hostname \ - jemalloc-devel \ - libaio-devel \ - libasan \ - libattr-devel \ - libbpf-devel \ - libcacard-devel \ - libcap-ng-devel \ - libcurl-devel \ - libdrm-devel \ - libepoxy-devel \ - libfdt-devel \ - libffi-devel \ - libgcrypt-devel \ - libiscsi-devel \ - libjpeg-devel \ - libnfs-devel \ - libpmem-devel \ - libpng-devel \ - librbd-devel \ - libseccomp-devel \ - libselinux-devel \ - libslirp-devel \ - libssh-devel \ - libtasn1-devel \ - libubsan \ - liburing-devel \ - libusbx-devel \ - libzstd-devel \ - llvm \ - lttng-ust-devel \ - lzo-devel \ - make \ - mesa-libgbm-devel \ - meson \ - ncurses-devel \ - nettle-devel \ - ninja-build \ - nmap-ncat \ - numactl-devel \ - openssh-clients \ - pam-devel \ - pcre-static \ - perl-base \ - pixman-devel \ - pkgconfig \ - pulseaudio-libs-devel \ - python3 \ - python3-PyYAML \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx_rtd_theme \ - python3-virtualenv \ - rdma-core-devel \ - rpm \ - sed \ - snappy-devel \ - sparse \ - spice-protocol \ - spice-server-devel \ - systemd-devel \ - systemtap-sdt-devel \ - tar \ - tesseract \ - tesseract-langpack-eng \ - texinfo \ - usbredir-devel \ - util-linux \ - virglrenderer-devel \ - vte291-devel \ - which \ - xen-devel \ - xfsprogs-devel \ - zlib-devel \ - zlib-static && \ + SDL2-devel \ + SDL2_image-devel \ + alsa-lib-devel \ + bash \ + bc \ + bison \ + brlapi-devel \ + bzip2 \ + bzip2-devel \ + ca-certificates \ + capstone-devel \ + ccache \ + clang \ + ctags \ + cyrus-sasl-devel \ + daxctl-devel \ + dbus-daemon \ + device-mapper-multipath-devel \ + diffutils \ + findutils \ + flex \ + fuse3-devel \ + gcc \ + gcc-c++ \ + gcovr \ + gettext \ + git \ + glib2-devel \ + glib2-static \ + glibc-langpack-en \ + glibc-static \ + glusterfs-api-devel \ + gnutls-devel \ + gtk3-devel \ + hostname \ + jemalloc-devel \ + json-c-devel \ + libaio-devel \ + libasan \ + libattr-devel \ + libbpf-devel \ + libcacard-devel \ + libcap-ng-devel \ + libcmocka-devel \ + libcurl-devel \ + libdrm-devel \ + libepoxy-devel \ + libfdt-devel \ + libffi-devel \ + libgcrypt-devel \ + libiscsi-devel \ + libjpeg-devel \ + libnfs-devel \ + libpmem-devel \ + libpng-devel \ + librbd-devel \ + libseccomp-devel \ + libselinux-devel \ + libslirp-devel \ + libssh-devel \ + libtasn1-devel \ + libubsan \ + liburing-devel \ + libusbx-devel \ + libxdp-devel \ + libzstd-devel \ + llvm \ + lttng-ust-devel \ + lzo-devel \ + make \ + mesa-libgbm-devel \ + meson \ + mtools \ + ncurses-devel \ + nettle-devel \ + ninja-build \ + nmap-ncat \ + numactl-devel \ + openssh-clients \ + pam-devel \ + pcre-static \ + pipewire-devel \ + pixman-devel \ + pkgconfig \ + pulseaudio-libs-devel \ + python3 \ + python3-PyYAML \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx_rtd_theme \ + rdma-core-devel \ + sed \ + snappy-devel \ + socat \ + sparse \ + spice-protocol \ + spice-server-devel \ + swtpm \ + systemd-devel \ + systemtap-sdt-devel \ + tar \ + tesseract \ + tesseract-langpack-eng \ + usbredir-devel \ + util-linux \ + virglrenderer-devel \ + vte291-devel \ + which \ + xen-devel \ + xfsprogs-devel \ + xorriso \ + zlib-devel \ + zlib-static \ + zstd && \ nosync dnf autoremove -y && \ nosync dnf clean all -y && \ rpm -qa | sort > /packages.txt && \ @@ -139,8 +145,13 @@ exec "$@"' > /usr/bin/nosync && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index 6f5993d602..cf753383a4 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all opensuse-leap-152 qemu +# $ lcitool dockerfile --layers all opensuse-leap-15 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.opensuse.org/opensuse/leap:15.2 +FROM registry.opensuse.org/opensuse/leap:15.5 RUN zypper update -y && \ zypper install -y \ @@ -12,6 +12,7 @@ RUN zypper update -y && \ alsa-lib-devel \ bash \ bc \ + bison \ brlapi-devel \ bzip2 \ ca-certificates \ @@ -22,6 +23,7 @@ RUN zypper update -y && \ dbus-1 \ diffutils \ findutils \ + flex \ fuse3-devel \ gcc \ gcc-c++ \ @@ -38,12 +40,13 @@ RUN zypper update -y && \ libSDL2-devel \ libSDL2_image-devel \ libaio-devel \ - libasan6 \ + libasan8 \ libattr-devel \ libbpf-devel \ libbz2-devel \ libcacard-devel \ libcap-ng-devel \ + libcmocka-devel \ libcurl-devel \ libdrm-devel \ libepoxy-devel \ @@ -53,6 +56,7 @@ RUN zypper update -y && \ libgnutls-devel \ libiscsi-devel \ libjpeg8-devel \ + libjson-c-devel \ libndctl-devel \ libnettle-devel \ libnfs-devel \ @@ -64,6 +68,7 @@ RUN zypper update -y && \ librbd-devel \ libseccomp-devel \ libselinux-devel \ + libslirp-devel \ libspice-server-devel \ libssh-devel \ libtasn1-devel \ @@ -76,38 +81,32 @@ RUN zypper update -y && \ lttng-ust-devel \ lzo-devel \ make \ - mkisofs \ + mtools \ ncat \ ncurses-devel \ ninja \ openssh \ pam-devel \ pcre-devel-static \ - perl-base \ + pipewire-devel \ pkgconfig \ - python3-Pillow \ - python3-PyYAML \ - python3-Sphinx \ - python3-base \ - python3-numpy \ - python3-opencv \ - python3-pip \ - python3-setuptools \ - python3-sphinx_rtd_theme \ - python3-virtualenv \ - python3-wheel \ + python311 \ + python311-base \ + python311-pip \ + python311-setuptools \ rdma-core-devel \ - rpm \ sed \ snappy-devel \ + sndio-devel \ + socat \ sparse \ spice-protocol-devel \ + swtpm \ systemd-devel \ systemtap-sdt-devel \ tar \ tesseract-ocr \ tesseract-ocr-traineddata-english \ - texinfo \ usbredir-devel \ util-linux \ virglrenderer-devel \ @@ -115,8 +114,10 @@ RUN zypper update -y && \ which \ xen-devel \ xfsprogs-devel \ + xorriso \ zlib-devel \ - zlib-devel-static && \ + zlib-devel-static \ + zstd && \ zypper clean --all && \ rpm -qa | sort > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ @@ -126,10 +127,20 @@ RUN zypper update -y && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc -RUN pip3 install meson==0.56.0 +RUN /usr/bin/pip3.11 install \ + PyYAML \ + meson==0.63.2 \ + pillow \ + sphinx \ + sphinx-rtd-theme +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" -ENV PYTHON "/usr/bin/python3" -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" +ENV PYTHON "/usr/bin/python3.11" +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/python.docker b/tests/docker/dockerfiles/python.docker index 56d88417df..a3c1321190 100644 --- a/tests/docker/dockerfiles/python.docker +++ b/tests/docker/dockerfiles/python.docker @@ -7,12 +7,20 @@ MAINTAINER John Snow ENV PACKAGES \ gcc \ make \ - pipenv \ python3 \ python3-pip \ python3-tox \ python3-virtualenv \ - python3.10 + python3.10 \ + python3.11 \ + python3.12 \ + python3.8 \ + python3.9 RUN dnf install -y $PACKAGES RUN rpm -q $PACKAGES | sort > /packages.txt +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/ubuntu2004.docker b/tests/docker/dockerfiles/ubuntu2004.docker index a3b38884e3..d3e212060c 100644 --- a/tests/docker/dockerfiles/ubuntu2004.docker +++ b/tests/docker/dockerfiles/ubuntu2004.docker @@ -11,118 +11,124 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get install -y eatmydata && \ eatmydata apt-get dist-upgrade -y && \ eatmydata apt-get install --no-install-recommends -y \ - bash \ - bc \ - bsdmainutils \ - bzip2 \ - ca-certificates \ - ccache \ - clang \ - dbus \ - debianutils \ - diffutils \ - exuberant-ctags \ - findutils \ - g++ \ - gcc \ - gcovr \ - genisoimage \ - gettext \ - git \ - hostname \ - libaio-dev \ - libasan5 \ - libasound2-dev \ - libattr1-dev \ - libbrlapi-dev \ - libbz2-dev \ - libc6-dev \ - libcacard-dev \ - libcap-ng-dev \ - libcapstone-dev \ - libcurl4-gnutls-dev \ - libdaxctl-dev \ - libdrm-dev \ - libepoxy-dev \ - libfdt-dev \ - libffi-dev \ - libfuse3-dev \ - libgbm-dev \ - libgcrypt20-dev \ - libglib2.0-dev \ - libglusterfs-dev \ - libgnutls28-dev \ - libgtk-3-dev \ - libibumad-dev \ - libibverbs-dev \ - libiscsi-dev \ - libjemalloc-dev \ - libjpeg-turbo8-dev \ - liblttng-ust-dev \ - liblzo2-dev \ - libncursesw5-dev \ - libnfs-dev \ - libnuma-dev \ - libpam0g-dev \ - libpcre2-dev \ - libpixman-1-dev \ - libpmem-dev \ - libpng-dev \ - libpulse-dev \ - librbd-dev \ - librdmacm-dev \ - libsasl2-dev \ - libsdl2-dev \ - libsdl2-image-dev \ - libseccomp-dev \ - libselinux1-dev \ - libslirp-dev \ - libsnappy-dev \ - libspice-protocol-dev \ - libspice-server-dev \ - libssh-dev \ - libsystemd-dev \ - libtasn1-6-dev \ - libubsan1 \ - libudev-dev \ - libusb-1.0-0-dev \ - libusbredirhost-dev \ - libvdeplug-dev \ - libvirglrenderer-dev \ - libvte-2.91-dev \ - libxen-dev \ - libzstd-dev \ - llvm \ - locales \ - make \ - multipath-tools \ - ncat \ - nettle-dev \ - ninja-build \ - openssh-client \ - perl-base \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-setuptools \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-wheel \ - python3-yaml \ - rpm2cpio \ - sed \ - sparse \ - systemtap-sdt-dev \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - texinfo \ - xfslibs-dev \ - zlib1g-dev && \ + bash \ + bc \ + bison \ + bsdmainutils \ + bzip2 \ + ca-certificates \ + ccache \ + clang \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + flex \ + g++ \ + gcc \ + gcovr \ + gettext \ + git \ + hostname \ + libaio-dev \ + libasan6 \ + libasound2-dev \ + libattr1-dev \ + libbrlapi-dev \ + libbz2-dev \ + libc6-dev \ + libcacard-dev \ + libcap-ng-dev \ + libcapstone-dev \ + libcmocka-dev \ + libcurl4-gnutls-dev \ + libdaxctl-dev \ + libdrm-dev \ + libepoxy-dev \ + libfdt-dev \ + libffi-dev \ + libfuse3-dev \ + libgbm-dev \ + libgcrypt20-dev \ + libglib2.0-dev \ + libglusterfs-dev \ + libgnutls28-dev \ + libgtk-3-dev \ + libibumad-dev \ + libibverbs-dev \ + libiscsi-dev \ + libjemalloc-dev \ + libjpeg-turbo8-dev \ + libjson-c-dev \ + liblttng-ust-dev \ + liblzo2-dev \ + libncursesw5-dev \ + libnfs-dev \ + libnuma-dev \ + libpam0g-dev \ + libpcre2-dev \ + libpixman-1-dev \ + libpmem-dev \ + libpng-dev \ + libpulse-dev \ + librbd-dev \ + librdmacm-dev \ + libsasl2-dev \ + libsdl2-dev \ + libsdl2-image-dev \ + libseccomp-dev \ + libselinux1-dev \ + libslirp-dev \ + libsnappy-dev \ + libsndio-dev \ + libspice-protocol-dev \ + libspice-server-dev \ + libssh-dev \ + libsystemd-dev \ + libtasn1-6-dev \ + libubsan1 \ + libudev-dev \ + libusb-1.0-0-dev \ + libusbredirhost-dev \ + libvdeplug-dev \ + libvirglrenderer-dev \ + libvte-2.91-dev \ + libxen-dev \ + libzstd-dev \ + llvm \ + locales \ + make \ + mtools \ + multipath-tools \ + ncat \ + nettle-dev \ + ninja-build \ + openssh-client \ + pkgconf \ + python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-setuptools \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-venv \ + python3-wheel \ + python3-yaml \ + rpm2cpio \ + sed \ + socat \ + sparse \ + systemtap-sdt-dev \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + xfslibs-dev \ + xorriso \ + zlib1g-dev \ + zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ @@ -135,13 +141,17 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc -RUN pip3 install meson==0.56.0 +RUN /usr/bin/pip3 install \ + meson==0.63.2 \ + tomli +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" -# Apply patch https://reviews.llvm.org/D75820 -# This is required for TSan in clang-10 to compile with QEMU. -RUN sed -i 's/^const/static const/g' /usr/lib/llvm-10/lib/clang/10.0.0/include/sanitizer/tsan_interface.h +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker new file mode 100644 index 0000000000..2ca9cff79c --- /dev/null +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -0,0 +1,157 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool dockerfile --layers all ubuntu-2204 qemu +# +# https://gitlab.com/libvirt/libvirt-ci + +FROM docker.io/library/ubuntu:22.04 + +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y eatmydata && \ + eatmydata apt-get dist-upgrade -y && \ + eatmydata apt-get install --no-install-recommends -y \ + bash \ + bc \ + bison \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + clang \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + flex \ + g++ \ + gcc \ + gcovr \ + gettext \ + git \ + hostname \ + libaio-dev \ + libasan6 \ + libasound2-dev \ + libattr1-dev \ + libbpf-dev \ + libbrlapi-dev \ + libbz2-dev \ + libc6-dev \ + libcacard-dev \ + libcap-ng-dev \ + libcapstone-dev \ + libcmocka-dev \ + libcurl4-gnutls-dev \ + libdaxctl-dev \ + libdrm-dev \ + libepoxy-dev \ + libfdt-dev \ + libffi-dev \ + libfuse3-dev \ + libgbm-dev \ + libgcrypt20-dev \ + libglib2.0-dev \ + libglusterfs-dev \ + libgnutls28-dev \ + libgtk-3-dev \ + libibumad-dev \ + libibverbs-dev \ + libiscsi-dev \ + libjemalloc-dev \ + libjpeg-turbo8-dev \ + libjson-c-dev \ + liblttng-ust-dev \ + liblzo2-dev \ + libncursesw5-dev \ + libnfs-dev \ + libnuma-dev \ + libpam0g-dev \ + libpcre2-dev \ + libpipewire-0.3-dev \ + libpixman-1-dev \ + libpmem-dev \ + libpng-dev \ + libpulse-dev \ + librbd-dev \ + librdmacm-dev \ + libsasl2-dev \ + libsdl2-dev \ + libsdl2-image-dev \ + libseccomp-dev \ + libselinux1-dev \ + libslirp-dev \ + libsnappy-dev \ + libsndio-dev \ + libspice-protocol-dev \ + libspice-server-dev \ + libssh-dev \ + libsystemd-dev \ + libtasn1-6-dev \ + libubsan1 \ + libudev-dev \ + liburing-dev \ + libusb-1.0-0-dev \ + libusbredirhost-dev \ + libvdeplug-dev \ + libvirglrenderer-dev \ + libvte-2.91-dev \ + libxen-dev \ + libzstd-dev \ + llvm \ + locales \ + make \ + meson \ + mtools \ + multipath-tools \ + ncat \ + nettle-dev \ + ninja-build \ + openssh-client \ + pkgconf \ + python3 \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx-rtd-theme \ + python3-tomli \ + python3-venv \ + python3-yaml \ + rpm2cpio \ + sed \ + socat \ + sparse \ + swtpm \ + systemtap-sdt-dev \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + xfslibs-dev \ + xorriso \ + zlib1g-dev \ + zstd && \ + eatmydata apt-get autoremove -y && \ + eatmydata apt-get autoclean -y && \ + sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ + dpkg-reconfigure locales && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ + mkdir -p /usr/libexec/ccache-wrappers && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc + +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" +ENV LANG "en_US.UTF-8" +ENV MAKE "/usr/bin/make" +ENV NINJA "/usr/bin/ninja" +ENV PYTHON "/usr/bin/python3" +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/run b/tests/docker/run index 421393046b..9eb96129da 100755 --- a/tests/docker/run +++ b/tests/docker/run @@ -15,7 +15,7 @@ if test -n "$V"; then set -x fi -BASE="$(dirname $(readlink -e $0))" +BASE="$(dirname $(realpath $0))" # Prepare the environment export PATH=/usr/lib/ccache:/usr/lib64/ccache:$PATH diff --git a/tests/docker/test-fuzz b/tests/docker/test-fuzz new file mode 100755 index 0000000000..7e506ae1f6 --- /dev/null +++ b/tests/docker/test-fuzz @@ -0,0 +1,28 @@ +#!/bin/bash -e +# +# Compile and check with oss-fuzz. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Authors: +# Alex Bennée +# +# SPDX-License-Identifier: GPL-2.0-or-later + +. common.rc + +requires_binary clang + +# the build script runs out of $src so we need to copy across +cd "$BUILD_DIR" +cp -a $QEMU_SRC . +cd src +mkdir build-oss-fuzz +export LSAN_OPTIONS=suppressions=scripts/oss-fuzz/lsan_suppressions.txt +env CC="clang" CXX="clang++" CFLAGS="-fsanitize=address" ./scripts/oss-fuzz/build.sh +export ASAN_OPTIONS="fast_unwind_on_malloc=0" +for fuzzer in $(find ./build-oss-fuzz/DEST_DIR/ -executable -type f | grep -v slirp); do + grep "LLVMFuzzerTestOneInput" ${fuzzer} > /dev/null 2>&1 || continue ; + echo Testing ${fuzzer} ... ; + "${fuzzer}" -runs=1 -seed=1 || exit 1 ; +done diff --git a/tests/docker/test-mingw b/tests/docker/test-mingw index 0bc6d78872..18366972eb 100755 --- a/tests/docker/test-mingw +++ b/tests/docker/test-mingw @@ -13,14 +13,12 @@ . common.rc -requires_binary x86_64-w64-mingw32-gcc -requires_binary i686-w64-mingw32-gcc +requires_binary x86_64-w64-mingw32-gcc i686-w64-mingw32-gcc cd "$BUILD_DIR" -for prefix in x86_64-w64-mingw32- i686-w64-mingw32-; do - TARGET_LIST=${TARGET_LIST:-$DEF_TARGET_LIST} \ - build_qemu --cross-prefix=$prefix \ +TARGET_LIST=${TARGET_LIST:-$DEF_TARGET_LIST} \ +build_qemu \ --enable-trace-backends=simple \ --enable-gnutls \ --enable-nettle \ @@ -29,8 +27,6 @@ for prefix in x86_64-w64-mingw32- i686-w64-mingw32-; do --enable-bzip2 \ --enable-guest-agent \ --enable-docs - install_qemu - make installer - make clean - -done +install_qemu +make installer +make clean diff --git a/tests/docker/test-tsan b/tests/docker/test-tsan index 53d90d2f79..f6d6590e39 100755 --- a/tests/docker/test-tsan +++ b/tests/docker/test-tsan @@ -21,7 +21,7 @@ setup_tsan() tsan_log_dir="/tmp/qemu-test/build/tsan" mkdir -p $tsan_log_dir > /dev/null || true EXTRA_CONFIGURE_OPTS="${EXTRA_CONFIGURE_OPTS} --enable-tsan \ - --cc=clang-10 --cxx=clang++-10 \ + --cc=clang --cxx=clang++ \ --disable-werror --extra-cflags=-O0" # detect deadlocks is false currently simply because # TSan crashes immediately with deadlock detector enabled. diff --git a/tests/fp/berkeley-softfloat-3 b/tests/fp/berkeley-softfloat-3 deleted file mode 160000 index b64af41c32..0000000000 --- a/tests/fp/berkeley-softfloat-3 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b64af41c3276f97f0e181920400ee056b9c88037 diff --git a/tests/fp/berkeley-testfloat-3 b/tests/fp/berkeley-testfloat-3 deleted file mode 160000 index 5a59dcec19..0000000000 --- a/tests/fp/berkeley-testfloat-3 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5a59dcec19327396a011a17fd924aed4fec416b3 diff --git a/tests/fp/fp-test.c b/tests/fp/fp-test.c index 35829ad5f7..36b5712cda 100644 --- a/tests/fp/fp-test.c +++ b/tests/fp/fp-test.c @@ -106,7 +106,8 @@ static const char commands_string[] = " -l = thoroughness level (1 (default), 2)\n" " -r = rounding mode (even (default), zero, down, up, tieaway, odd)\n" " Set to 'all' to test all rounding modes, if applicable\n" - " -s = stop when a test fails"; + " -s = stop when a test fails\n" + " -q = minimise noise when testing, just show each function being tested"; static void usage_complete(int argc, char *argv[]) { @@ -190,9 +191,11 @@ static void do_testfloat(int op, int rmode, bool exact) ab_f128M_z_bool true_ab_f128M_z_bool; ab_f128M_z_bool subj_ab_f128M_z_bool; - fputs(">> Testing ", stderr); - verCases_writeFunctionName(stderr); - fputs("\n", stderr); + if (verCases_verbosity) { + fputs(">> Testing ", stderr); + verCases_writeFunctionName(stderr); + fputs("\n", stderr); + } if (!is_allowed(op, rmode)) { not_implemented(); @@ -837,7 +840,7 @@ static void parse_args(int argc, char *argv[]) int c; for (;;) { - c = getopt(argc, argv, "he:f:l:r:s"); + c = getopt(argc, argv, "he:f:l:r:sq"); if (c < 0) { break; } @@ -874,9 +877,15 @@ static void parse_args(int argc, char *argv[]) } } break; + /* + * The following flags are declared in testfloat/source/verCases_common.c + */ case 's': verCases_errorStop = true; break; + case 'q': + verCases_verbosity = 0; + break; case '?': /* invalid option or missing argument; getopt prints error info */ exit(EXIT_FAILURE); diff --git a/tests/fp/meson.build b/tests/fp/meson.build index 8bd0979f67..114b4b483e 100644 --- a/tests/fp/meson.build +++ b/tests/fp/meson.build @@ -1,16 +1,21 @@ +if 'CONFIG_TCG' not in config_all_accel + subdir_done() +endif # There are namespace pollution issues on Windows, due to osdep.h # bringing in Windows headers that define a FLOAT128 type. -if targetos == 'windows' +if host_os == 'windows' subdir_done() endif -fpcflags = [ +sfcflags = [ # softfloat defines '-DSOFTFLOAT_ROUND_ODD', '-DINLINE_LEVEL=5', '-DSOFTFLOAT_FAST_DIV32TO16', '-DSOFTFLOAT_FAST_DIV64TO32', '-DSOFTFLOAT_FAST_INT64', +] +tfcflags = [ # testfloat defines '-DFLOAT16', '-DFLOAT64', @@ -20,522 +25,16 @@ fpcflags = [ '-DLONG_DOUBLE_IS_EXTFLOAT80', ] -sfdir = 'berkeley-softfloat-3/source' -sfspedir = sfdir / '8086-SSE' -tfdir = 'berkeley-testfloat-3/source' +libsoftfloat_proj = subproject('berkeley-softfloat-3', required: true, + default_options: 'defines=' + ','.join(sfcflags)) +libsoftfloat = libsoftfloat_proj.get_variable('libsoftfloat_dep') -sfinc = include_directories(sfdir / 'include', sfspedir) +libtestfloat_proj = subproject('berkeley-testfloat-3', required: true, + default_options: 'defines=' + ','.join(tfcflags)) +libtestfloat = libtestfloat_proj.get_variable('libtestfloat_dep') +libslowfloat = libtestfloat_proj.get_variable('libslowfloat_dep') -tfcflags = [ - '-Wno-implicit-fallthrough', - '-Wno-strict-prototypes', - '-Wno-unknown-pragmas', - '-Wno-uninitialized', - '-Wno-missing-prototypes', - '-Wno-return-type', - '-Wno-unused-function', - '-Wno-error', -] - -if cc.get_id() == 'clang' - # Clang does not support '#pragma STDC FENV_ACCESS' - tfcflags += [ '-Wno-ignored-pragmas' ] -endif - -tfgencases = [ - tfdir / 'genCases_ui32.c', - tfdir / 'genCases_ui64.c', - tfdir / 'genCases_i32.c', - tfdir / 'genCases_i64.c', - tfdir / 'genCases_f16.c', - tfdir / 'genCases_f32.c', - tfdir / 'genCases_f64.c', - tfdir / 'genCases_extF80.c', - tfdir / 'genCases_f128.c', -] - -tfwritecase = [ - tfdir / 'writeCase_a_ui32.c', - tfdir / 'writeCase_a_ui64.c', - tfdir / 'writeCase_a_f16.c', - tfdir / 'writeCase_ab_f16.c', - tfdir / 'writeCase_abc_f16.c', - tfdir / 'writeCase_a_f32.c', - tfdir / 'writeCase_ab_f32.c', - tfdir / 'writeCase_abc_f32.c', - tfdir / 'writeCase_a_f64.c', - tfdir / 'writeCase_ab_f64.c', - tfdir / 'writeCase_abc_f64.c', - tfdir / 'writeCase_a_extF80M.c', - tfdir / 'writeCase_ab_extF80M.c', - tfdir / 'writeCase_a_f128M.c', - tfdir / 'writeCase_ab_f128M.c', - tfdir / 'writeCase_abc_f128M.c', - tfdir / 'writeCase_z_bool.c', - tfdir / 'writeCase_z_ui32.c', - tfdir / 'writeCase_z_ui64.c', - tfdir / 'writeCase_z_f16.c', - tfdir / 'writeCase_z_f32.c', - tfdir / 'writeCase_z_f64.c', - tfdir / 'writeCase_z_extF80M.c', - tfdir / 'writeCase_z_f128M.c', -] - -tftest = [ - tfdir / 'test_a_ui32_z_f16.c', - tfdir / 'test_a_ui32_z_f32.c', - tfdir / 'test_a_ui32_z_f64.c', - tfdir / 'test_a_ui32_z_extF80.c', - tfdir / 'test_a_ui32_z_f128.c', - tfdir / 'test_a_ui64_z_f16.c', - tfdir / 'test_a_ui64_z_f32.c', - tfdir / 'test_a_ui64_z_f64.c', - tfdir / 'test_a_ui64_z_extF80.c', - tfdir / 'test_a_ui64_z_f128.c', - tfdir / 'test_a_i32_z_f16.c', - tfdir / 'test_a_i32_z_f32.c', - tfdir / 'test_a_i32_z_f64.c', - tfdir / 'test_a_i32_z_extF80.c', - tfdir / 'test_a_i32_z_f128.c', - tfdir / 'test_a_i64_z_f16.c', - tfdir / 'test_a_i64_z_f32.c', - tfdir / 'test_a_i64_z_f64.c', - tfdir / 'test_a_i64_z_extF80.c', - tfdir / 'test_a_i64_z_f128.c', - tfdir / 'test_a_f16_z_ui32_rx.c', - tfdir / 'test_a_f16_z_ui64_rx.c', - tfdir / 'test_a_f16_z_i32_rx.c', - tfdir / 'test_a_f16_z_i64_rx.c', - tfdir / 'test_a_f16_z_ui32_x.c', - tfdir / 'test_a_f16_z_ui64_x.c', - tfdir / 'test_a_f16_z_i32_x.c', - tfdir / 'test_a_f16_z_i64_x.c', - tfdir / 'test_a_f16_z_f32.c', - tfdir / 'test_a_f16_z_f64.c', - tfdir / 'test_a_f16_z_extF80.c', - tfdir / 'test_a_f16_z_f128.c', - tfdir / 'test_az_f16.c', - tfdir / 'test_az_f16_rx.c', - tfdir / 'test_abz_f16.c', - tfdir / 'test_abcz_f16.c', - tfdir / 'test_ab_f16_z_bool.c', - tfdir / 'test_a_f32_z_ui32_rx.c', - tfdir / 'test_a_f32_z_ui64_rx.c', - tfdir / 'test_a_f32_z_i32_rx.c', - tfdir / 'test_a_f32_z_i64_rx.c', - tfdir / 'test_a_f32_z_ui32_x.c', - tfdir / 'test_a_f32_z_ui64_x.c', - tfdir / 'test_a_f32_z_i32_x.c', - tfdir / 'test_a_f32_z_i64_x.c', - tfdir / 'test_a_f32_z_f16.c', - tfdir / 'test_a_f32_z_f64.c', - tfdir / 'test_a_f32_z_extF80.c', - tfdir / 'test_a_f32_z_f128.c', - tfdir / 'test_az_f32.c', - tfdir / 'test_az_f32_rx.c', - tfdir / 'test_abz_f32.c', - tfdir / 'test_abcz_f32.c', - tfdir / 'test_ab_f32_z_bool.c', - tfdir / 'test_a_f64_z_ui32_rx.c', - tfdir / 'test_a_f64_z_ui64_rx.c', - tfdir / 'test_a_f64_z_i32_rx.c', - tfdir / 'test_a_f64_z_i64_rx.c', - tfdir / 'test_a_f64_z_ui32_x.c', - tfdir / 'test_a_f64_z_ui64_x.c', - tfdir / 'test_a_f64_z_i32_x.c', - tfdir / 'test_a_f64_z_i64_x.c', - tfdir / 'test_a_f64_z_f16.c', - tfdir / 'test_a_f64_z_f32.c', - tfdir / 'test_a_f64_z_extF80.c', - tfdir / 'test_a_f64_z_f128.c', - tfdir / 'test_az_f64.c', - tfdir / 'test_az_f64_rx.c', - tfdir / 'test_abz_f64.c', - tfdir / 'test_abcz_f64.c', - tfdir / 'test_ab_f64_z_bool.c', - tfdir / 'test_a_extF80_z_ui32_rx.c', - tfdir / 'test_a_extF80_z_ui64_rx.c', - tfdir / 'test_a_extF80_z_i32_rx.c', - tfdir / 'test_a_extF80_z_i64_rx.c', - tfdir / 'test_a_extF80_z_ui32_x.c', - tfdir / 'test_a_extF80_z_ui64_x.c', - tfdir / 'test_a_extF80_z_i32_x.c', - tfdir / 'test_a_extF80_z_i64_x.c', - tfdir / 'test_a_extF80_z_f16.c', - tfdir / 'test_a_extF80_z_f32.c', - tfdir / 'test_a_extF80_z_f64.c', - tfdir / 'test_a_extF80_z_f128.c', - tfdir / 'test_az_extF80.c', - tfdir / 'test_az_extF80_rx.c', - tfdir / 'test_abz_extF80.c', - tfdir / 'test_ab_extF80_z_bool.c', - tfdir / 'test_a_f128_z_ui32_rx.c', - tfdir / 'test_a_f128_z_ui64_rx.c', - tfdir / 'test_a_f128_z_i32_rx.c', - tfdir / 'test_a_f128_z_i64_rx.c', - tfdir / 'test_a_f128_z_ui32_x.c', - tfdir / 'test_a_f128_z_ui64_x.c', - tfdir / 'test_a_f128_z_i32_x.c', - tfdir / 'test_a_f128_z_i64_x.c', - tfdir / 'test_a_f128_z_f16.c', - tfdir / 'test_a_f128_z_f32.c', - tfdir / 'test_a_f128_z_f64.c', - tfdir / 'test_a_f128_z_extF80.c', - tfdir / 'test_az_f128.c', - tfdir / 'test_az_f128_rx.c', - tfdir / 'test_abz_f128.c', - tfdir / 'test_abcz_f128.c', - tfdir / 'test_ab_f128_z_bool.c', -] - -libtestfloat = static_library( - 'testfloat', - files( - tfdir / 'uint128_inline.c', - tfdir / 'uint128.c', - tfdir / 'fail.c', - tfdir / 'functions_common.c', - tfdir / 'functionInfos.c', - tfdir / 'standardFunctionInfos.c', - tfdir / 'random.c', - tfdir / 'genCases_common.c', - tfgencases, - tfdir / 'genCases_writeTestsTotal.c', - tfdir / 'verCases_inline.c', - tfdir / 'verCases_common.c', - tfdir / 'verCases_writeFunctionName.c', - tfdir / 'readHex.c', - tfdir / 'writeHex.c', - tfwritecase, - tfdir / 'testLoops_common.c', - tftest, - ), - include_directories: sfinc, - c_args: tfcflags + fpcflags, -) - -sfcflags = [ - '-Wno-implicit-fallthrough', - '-Wno-missing-prototypes', - '-Wno-redundant-decls', - '-Wno-return-type', - '-Wno-error', -] - -libsoftfloat = static_library( - 'softfloat', - files( - # primitives - sfdir / 's_eq128.c', - sfdir / 's_le128.c', - sfdir / 's_lt128.c', - sfdir / 's_shortShiftLeft128.c', - sfdir / 's_shortShiftRight128.c', - sfdir / 's_shortShiftRightJam64.c', - sfdir / 's_shortShiftRightJam64Extra.c', - sfdir / 's_shortShiftRightJam128.c', - sfdir / 's_shortShiftRightJam128Extra.c', - sfdir / 's_shiftRightJam32.c', - sfdir / 's_shiftRightJam64.c', - sfdir / 's_shiftRightJam64Extra.c', - sfdir / 's_shiftRightJam128.c', - sfdir / 's_shiftRightJam128Extra.c', - sfdir / 's_shiftRightJam256M.c', - sfdir / 's_countLeadingZeros8.c', - sfdir / 's_countLeadingZeros16.c', - sfdir / 's_countLeadingZeros32.c', - sfdir / 's_countLeadingZeros64.c', - sfdir / 's_add128.c', - sfdir / 's_add256M.c', - sfdir / 's_sub128.c', - sfdir / 's_sub256M.c', - sfdir / 's_mul64ByShifted32To128.c', - sfdir / 's_mul64To128.c', - sfdir / 's_mul128By32.c', - sfdir / 's_mul128To256M.c', - sfdir / 's_approxRecip_1Ks.c', - sfdir / 's_approxRecip32_1.c', - sfdir / 's_approxRecipSqrt_1Ks.c', - sfdir / 's_approxRecipSqrt32_1.c', - # others - sfdir / 's_roundToUI32.c', - sfdir / 's_roundToUI64.c', - sfdir / 's_roundToI32.c', - sfdir / 's_roundToI64.c', - sfdir / 's_normSubnormalF16Sig.c', - sfdir / 's_roundPackToF16.c', - sfdir / 's_normRoundPackToF16.c', - sfdir / 's_addMagsF16.c', - sfdir / 's_subMagsF16.c', - sfdir / 's_mulAddF16.c', - sfdir / 's_normSubnormalF32Sig.c', - sfdir / 's_roundPackToF32.c', - sfdir / 's_normRoundPackToF32.c', - sfdir / 's_addMagsF32.c', - sfdir / 's_subMagsF32.c', - sfdir / 's_mulAddF32.c', - sfdir / 's_normSubnormalF64Sig.c', - sfdir / 's_roundPackToF64.c', - sfdir / 's_normRoundPackToF64.c', - sfdir / 's_addMagsF64.c', - sfdir / 's_subMagsF64.c', - sfdir / 's_mulAddF64.c', - sfdir / 's_normSubnormalExtF80Sig.c', - sfdir / 's_roundPackToExtF80.c', - sfdir / 's_normRoundPackToExtF80.c', - sfdir / 's_addMagsExtF80.c', - sfdir / 's_subMagsExtF80.c', - sfdir / 's_normSubnormalF128Sig.c', - sfdir / 's_roundPackToF128.c', - sfdir / 's_normRoundPackToF128.c', - sfdir / 's_addMagsF128.c', - sfdir / 's_subMagsF128.c', - sfdir / 's_mulAddF128.c', - sfdir / 'softfloat_state.c', - sfdir / 'ui32_to_f16.c', - sfdir / 'ui32_to_f32.c', - sfdir / 'ui32_to_f64.c', - sfdir / 'ui32_to_extF80.c', - sfdir / 'ui32_to_extF80M.c', - sfdir / 'ui32_to_f128.c', - sfdir / 'ui32_to_f128M.c', - sfdir / 'ui64_to_f16.c', - sfdir / 'ui64_to_f32.c', - sfdir / 'ui64_to_f64.c', - sfdir / 'ui64_to_extF80.c', - sfdir / 'ui64_to_extF80M.c', - sfdir / 'ui64_to_f128.c', - sfdir / 'ui64_to_f128M.c', - sfdir / 'i32_to_f16.c', - sfdir / 'i32_to_f32.c', - sfdir / 'i32_to_f64.c', - sfdir / 'i32_to_extF80.c', - sfdir / 'i32_to_extF80M.c', - sfdir / 'i32_to_f128.c', - sfdir / 'i32_to_f128M.c', - sfdir / 'i64_to_f16.c', - sfdir / 'i64_to_f32.c', - sfdir / 'i64_to_f64.c', - sfdir / 'i64_to_extF80.c', - sfdir / 'i64_to_extF80M.c', - sfdir / 'i64_to_f128.c', - sfdir / 'i64_to_f128M.c', - sfdir / 'f16_to_ui32.c', - sfdir / 'f16_to_ui64.c', - sfdir / 'f16_to_i32.c', - sfdir / 'f16_to_i64.c', - sfdir / 'f16_to_ui32_r_minMag.c', - sfdir / 'f16_to_ui64_r_minMag.c', - sfdir / 'f16_to_i32_r_minMag.c', - sfdir / 'f16_to_i64_r_minMag.c', - sfdir / 'f16_to_f32.c', - sfdir / 'f16_to_f64.c', - sfdir / 'f16_to_extF80.c', - sfdir / 'f16_to_extF80M.c', - sfdir / 'f16_to_f128.c', - sfdir / 'f16_to_f128M.c', - sfdir / 'f16_roundToInt.c', - sfdir / 'f16_add.c', - sfdir / 'f16_sub.c', - sfdir / 'f16_mul.c', - sfdir / 'f16_mulAdd.c', - sfdir / 'f16_div.c', - sfdir / 'f16_rem.c', - sfdir / 'f16_sqrt.c', - sfdir / 'f16_eq.c', - sfdir / 'f16_le.c', - sfdir / 'f16_lt.c', - sfdir / 'f16_eq_signaling.c', - sfdir / 'f16_le_quiet.c', - sfdir / 'f16_lt_quiet.c', - sfdir / 'f16_isSignalingNaN.c', - sfdir / 'f32_to_ui32.c', - sfdir / 'f32_to_ui64.c', - sfdir / 'f32_to_i32.c', - sfdir / 'f32_to_i64.c', - sfdir / 'f32_to_ui32_r_minMag.c', - sfdir / 'f32_to_ui64_r_minMag.c', - sfdir / 'f32_to_i32_r_minMag.c', - sfdir / 'f32_to_i64_r_minMag.c', - sfdir / 'f32_to_f16.c', - sfdir / 'f32_to_f64.c', - sfdir / 'f32_to_extF80.c', - sfdir / 'f32_to_extF80M.c', - sfdir / 'f32_to_f128.c', - sfdir / 'f32_to_f128M.c', - sfdir / 'f32_roundToInt.c', - sfdir / 'f32_add.c', - sfdir / 'f32_sub.c', - sfdir / 'f32_mul.c', - sfdir / 'f32_mulAdd.c', - sfdir / 'f32_div.c', - sfdir / 'f32_rem.c', - sfdir / 'f32_sqrt.c', - sfdir / 'f32_eq.c', - sfdir / 'f32_le.c', - sfdir / 'f32_lt.c', - sfdir / 'f32_eq_signaling.c', - sfdir / 'f32_le_quiet.c', - sfdir / 'f32_lt_quiet.c', - sfdir / 'f32_isSignalingNaN.c', - sfdir / 'f64_to_ui32.c', - sfdir / 'f64_to_ui64.c', - sfdir / 'f64_to_i32.c', - sfdir / 'f64_to_i64.c', - sfdir / 'f64_to_ui32_r_minMag.c', - sfdir / 'f64_to_ui64_r_minMag.c', - sfdir / 'f64_to_i32_r_minMag.c', - sfdir / 'f64_to_i64_r_minMag.c', - sfdir / 'f64_to_f16.c', - sfdir / 'f64_to_f32.c', - sfdir / 'f64_to_extF80.c', - sfdir / 'f64_to_extF80M.c', - sfdir / 'f64_to_f128.c', - sfdir / 'f64_to_f128M.c', - sfdir / 'f64_roundToInt.c', - sfdir / 'f64_add.c', - sfdir / 'f64_sub.c', - sfdir / 'f64_mul.c', - sfdir / 'f64_mulAdd.c', - sfdir / 'f64_div.c', - sfdir / 'f64_rem.c', - sfdir / 'f64_sqrt.c', - sfdir / 'f64_eq.c', - sfdir / 'f64_le.c', - sfdir / 'f64_lt.c', - sfdir / 'f64_eq_signaling.c', - sfdir / 'f64_le_quiet.c', - sfdir / 'f64_lt_quiet.c', - sfdir / 'f64_isSignalingNaN.c', - sfdir / 'extF80_to_ui32.c', - sfdir / 'extF80_to_ui64.c', - sfdir / 'extF80_to_i32.c', - sfdir / 'extF80_to_i64.c', - sfdir / 'extF80_to_ui32_r_minMag.c', - sfdir / 'extF80_to_ui64_r_minMag.c', - sfdir / 'extF80_to_i32_r_minMag.c', - sfdir / 'extF80_to_i64_r_minMag.c', - sfdir / 'extF80_to_f16.c', - sfdir / 'extF80_to_f32.c', - sfdir / 'extF80_to_f64.c', - sfdir / 'extF80_to_f128.c', - sfdir / 'extF80_roundToInt.c', - sfdir / 'extF80_add.c', - sfdir / 'extF80_sub.c', - sfdir / 'extF80_mul.c', - sfdir / 'extF80_div.c', - sfdir / 'extF80_rem.c', - sfdir / 'extF80_sqrt.c', - sfdir / 'extF80_eq.c', - sfdir / 'extF80_le.c', - sfdir / 'extF80_lt.c', - sfdir / 'extF80_eq_signaling.c', - sfdir / 'extF80_le_quiet.c', - sfdir / 'extF80_lt_quiet.c', - sfdir / 'extF80_isSignalingNaN.c', - sfdir / 'extF80M_to_ui32.c', - sfdir / 'extF80M_to_ui64.c', - sfdir / 'extF80M_to_i32.c', - sfdir / 'extF80M_to_i64.c', - sfdir / 'extF80M_to_ui32_r_minMag.c', - sfdir / 'extF80M_to_ui64_r_minMag.c', - sfdir / 'extF80M_to_i32_r_minMag.c', - sfdir / 'extF80M_to_i64_r_minMag.c', - sfdir / 'extF80M_to_f16.c', - sfdir / 'extF80M_to_f32.c', - sfdir / 'extF80M_to_f64.c', - sfdir / 'extF80M_to_f128M.c', - sfdir / 'extF80M_roundToInt.c', - sfdir / 'extF80M_add.c', - sfdir / 'extF80M_sub.c', - sfdir / 'extF80M_mul.c', - sfdir / 'extF80M_div.c', - sfdir / 'extF80M_rem.c', - sfdir / 'extF80M_sqrt.c', - sfdir / 'extF80M_eq.c', - sfdir / 'extF80M_le.c', - sfdir / 'extF80M_lt.c', - sfdir / 'extF80M_eq_signaling.c', - sfdir / 'extF80M_le_quiet.c', - sfdir / 'extF80M_lt_quiet.c', - sfdir / 'f128_to_ui32.c', - sfdir / 'f128_to_ui64.c', - sfdir / 'f128_to_i32.c', - sfdir / 'f128_to_i64.c', - sfdir / 'f128_to_ui32_r_minMag.c', - sfdir / 'f128_to_ui64_r_minMag.c', - sfdir / 'f128_to_i32_r_minMag.c', - sfdir / 'f128_to_i64_r_minMag.c', - sfdir / 'f128_to_f16.c', - sfdir / 'f128_to_f32.c', - sfdir / 'f128_to_extF80.c', - sfdir / 'f128_to_f64.c', - sfdir / 'f128_roundToInt.c', - sfdir / 'f128_add.c', - sfdir / 'f128_sub.c', - sfdir / 'f128_mul.c', - sfdir / 'f128_mulAdd.c', - sfdir / 'f128_div.c', - sfdir / 'f128_rem.c', - sfdir / 'f128_sqrt.c', - sfdir / 'f128_eq.c', - sfdir / 'f128_le.c', - sfdir / 'f128_lt.c', - sfdir / 'f128_eq_signaling.c', - sfdir / 'f128_le_quiet.c', - sfdir / 'f128_lt_quiet.c', - sfdir / 'f128_isSignalingNaN.c', - sfdir / 'f128M_to_ui32.c', - sfdir / 'f128M_to_ui64.c', - sfdir / 'f128M_to_i32.c', - sfdir / 'f128M_to_i64.c', - sfdir / 'f128M_to_ui32_r_minMag.c', - sfdir / 'f128M_to_ui64_r_minMag.c', - sfdir / 'f128M_to_i32_r_minMag.c', - sfdir / 'f128M_to_i64_r_minMag.c', - sfdir / 'f128M_to_f16.c', - sfdir / 'f128M_to_f32.c', - sfdir / 'f128M_to_extF80M.c', - sfdir / 'f128M_to_f64.c', - sfdir / 'f128M_roundToInt.c', - sfdir / 'f128M_add.c', - sfdir / 'f128M_sub.c', - sfdir / 'f128M_mul.c', - sfdir / 'f128M_mulAdd.c', - sfdir / 'f128M_div.c', - sfdir / 'f128M_rem.c', - sfdir / 'f128M_sqrt.c', - sfdir / 'f128M_eq.c', - sfdir / 'f128M_le.c', - sfdir / 'f128M_lt.c', - sfdir / 'f128M_eq_signaling.c', - sfdir / 'f128M_le_quiet.c', - sfdir / 'f128M_lt_quiet.c', - # spe - sfspedir / 'softfloat_raiseFlags.c', - sfspedir / 's_f16UIToCommonNaN.c', - sfspedir / 's_commonNaNToF16UI.c', - sfspedir / 's_propagateNaNF16UI.c', - sfspedir / 's_f32UIToCommonNaN.c', - sfspedir / 's_commonNaNToF32UI.c', - sfspedir / 's_propagateNaNF32UI.c', - sfspedir / 's_f64UIToCommonNaN.c', - sfspedir / 's_commonNaNToF64UI.c', - sfspedir / 's_propagateNaNF64UI.c', - sfspedir / 'extF80M_isSignalingNaN.c', - sfspedir / 's_extF80UIToCommonNaN.c', - sfspedir / 's_commonNaNToExtF80UI.c', - sfspedir / 's_propagateNaNExtF80UI.c', - sfspedir / 'f128M_isSignalingNaN.c', - sfspedir / 's_f128UIToCommonNaN.c', - sfspedir / 's_commonNaNToF128UI.c', - sfspedir / 's_propagateNaNF128UI.c', - ), - include_directories: sfinc, - c_args: sfcflags + fpcflags, -) - -fpcflags += [ +fpcflags = [ # work around TARGET_* poisoning '-DHW_POISON_H', # define a target to match testfloat's implementation-defined choices, such as @@ -547,10 +46,8 @@ fpcflags += [ fptest = executable( 'fp-test', - ['fp-test.c', tfdir / 'slowfloat.c', '../../fpu/softfloat.c'], - link_with: [libtestfloat, libsoftfloat], - dependencies: [qemuutil], - include_directories: [sfinc, include_directories(tfdir)], + ['fp-test.c', '../../fpu/softfloat.c'], + dependencies: [qemuutil, libsoftfloat, libtestfloat, libslowfloat], c_args: fpcflags, ) softfloat_conv_tests = { @@ -605,7 +102,7 @@ softfloat_tests = { # The full test suite can take a bit of time, default to a quick run # "-l 2 -r all" can take more than a day for some operations and is best # run manually -fptest_args = ['-s', '-l', '1'] +fptest_args = ['-q', '-s', '-l', '1'] fptest_rounding_args = ['-r', 'all'] # Conversion Routines: @@ -627,23 +124,19 @@ test('fp-test-mulAdd', fptest, # no fptest_rounding_args args: fptest_args + ['f16_mulAdd', 'f32_mulAdd', 'f64_mulAdd', 'f128_mulAdd'], - suite: ['softfloat-slow', 'softfloat-ops-slow', 'slow'], timeout: 90) + suite: ['softfloat-slow', 'softfloat-ops-slow', 'slow'], timeout: 180) -fpbench = executable( +executable( 'fp-bench', ['fp-bench.c', '../../fpu/softfloat.c'], - link_with: [libtestfloat, libsoftfloat], - dependencies: [qemuutil], - include_directories: [sfinc, include_directories(tfdir)], + dependencies: [qemuutil, libtestfloat, libsoftfloat], c_args: fpcflags, ) fptestlog2 = executable( 'fp-test-log2', ['fp-test-log2.c', '../../fpu/softfloat.c'], - link_with: [libsoftfloat], - dependencies: [qemuutil], - include_directories: [sfinc], + dependencies: [qemuutil, libsoftfloat], c_args: fpcflags, ) test('fp-test-log2', fptestlog2, diff --git a/tests/guest-debug/run-test.py b/tests/guest-debug/run-test.py index d865e46ecd..368ff8a890 100755 --- a/tests/guest-debug/run-test.py +++ b/tests/guest-debug/run-test.py @@ -26,11 +26,12 @@ def get_args(): 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("--test", help="GDB test script") parser.add_argument("--gdb", help="The gdb binary to use", default=None) + parser.add_argument("--gdb-args", help="Additional gdb arguments") parser.add_argument("--output", help="A file to redirect output to") + parser.add_argument("--stderr", help="A file to redirect stderr to") return parser.parse_args() @@ -58,39 +59,50 @@ if __name__ == '__main__': output = open(args.output, "w") else: output = None + if args.stderr: + stderr = open(args.stderr, "w") + else: + stderr = None socket_dir = TemporaryDirectory("qemu-gdbstub") socket_name = os.path.join(socket_dir.name, "gdbstub.socket") # Launch QEMU with binary if "system" in args.qemu: - cmd = "%s %s %s -gdb unix:path=%s,server=on" % (args.qemu, - args.qargs, - args.binary, - socket_name) + cmd = f'{args.qemu} {args.qargs} {args.binary}' \ + f' -S -gdb unix:path={socket_name},server=on' else: - cmd = "%s %s -g %s %s" % (args.qemu, args.qargs, socket_name, - args.binary) + cmd = f'{args.qemu} {args.qargs} -g {socket_name} {args.binary}' log(output, "QEMU CMD: %s" % (cmd)) inferior = subprocess.Popen(shlex.split(cmd)) # Now launch gdb with our test and collect the result gdb_cmd = "%s %s" % (args.gdb, args.binary) + if args.gdb_args: + gdb_cmd += " %s" % (args.gdb_args) # run quietly and ignore .gdbinit gdb_cmd += " -q -n -batch" + # disable pagination + gdb_cmd += " -ex 'set pagination off'" # disable prompts in case of crash gdb_cmd += " -ex 'set confirm off'" # connect to remote gdb_cmd += " -ex 'target remote %s'" % (socket_name) # finally the test script itself - gdb_cmd += " -x %s" % (args.test) + if args.test: + gdb_cmd += " -x %s" % (args.test) sleep(1) log(output, "GDB CMD: %s" % (gdb_cmd)) - result = subprocess.call(gdb_cmd, shell=True, stdout=output) + gdb_env = dict(os.environ) + gdb_pythonpath = gdb_env.get("PYTHONPATH", "").split(os.pathsep) + gdb_pythonpath.append(os.path.dirname(os.path.realpath(__file__))) + gdb_env["PYTHONPATH"] = os.pathsep.join(gdb_pythonpath) + result = subprocess.call(gdb_cmd, shell=True, stdout=output, stderr=stderr, + env=gdb_env) # A result of greater than 128 indicates a fatal signal (likely a # crash due to gdb internal failure). That's a problem for GDB and diff --git a/tests/guest-debug/test-gdbstub.py b/tests/guest-debug/test-gdbstub.py deleted file mode 100644 index 98a5df4d42..0000000000 --- a/tests/guest-debug/test-gdbstub.py +++ /dev/null @@ -1,177 +0,0 @@ -# -# This script needs to be run on startup -# qemu -kernel ${KERNEL} -s -S -# and then: -# gdb ${KERNEL}.vmlinux -x ${QEMU_SRC}/tests/guest-debug/test-gdbstub.py - -import gdb - -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 check_step(): - "Step an instruction, check it moved." - start_pc = gdb.parse_and_eval('$pc') - gdb.execute("si") - end_pc = gdb.parse_and_eval('$pc') - - return not (start_pc == end_pc) - - -def check_break(sym_name): - "Setup breakpoint, continue and check we stopped." - sym, ok = gdb.lookup_symbol(sym_name) - bp = gdb.Breakpoint(sym_name) - - gdb.execute("c") - - # hopefully we came back - end_pc = gdb.parse_and_eval('$pc') - print ("%s == %s %d" % (end_pc, sym.value(), bp.hit_count)) - bp.delete() - - # can we test we hit bp? - return end_pc == sym.value() - - -# We need to do hbreak manually as the python interface doesn't export it -def check_hbreak(sym_name): - "Setup hardware breakpoint, continue and check we stopped." - sym, ok = gdb.lookup_symbol(sym_name) - gdb.execute("hbreak %s" % (sym_name)) - gdb.execute("c") - - # hopefully we came back - end_pc = gdb.parse_and_eval('$pc') - print ("%s == %s" % (end_pc, sym.value())) - - if end_pc == sym.value(): - gdb.execute("d 1") - return True - else: - return False - - -class WatchPoint(gdb.Breakpoint): - - def get_wpstr(self, sym_name): - "Setup sym and wp_str for given symbol." - self.sym, ok = gdb.lookup_symbol(sym_name) - wp_addr = gdb.parse_and_eval(sym_name).address - self.wp_str = '*(%(type)s)(&%(address)s)' % dict( - type = wp_addr.type, address = sym_name) - - return(self.wp_str) - - def __init__(self, sym_name, type): - wp_str = self.get_wpstr(sym_name) - super(WatchPoint, self).__init__(wp_str, gdb.BP_WATCHPOINT, type) - - def stop(self): - end_pc = gdb.parse_and_eval('$pc') - print ("HIT WP @ %s" % (end_pc)) - return True - - -def do_one_watch(sym, wtype, text): - - wp = WatchPoint(sym, wtype) - gdb.execute("c") - report_str = "%s for %s (%s)" % (text, sym, wp.sym.value()) - - if wp.hit_count > 0: - report(True, report_str) - wp.delete() - else: - report(False, report_str) - - -def check_watches(sym_name): - "Watch a symbol for any access." - - # Should hit for any read - do_one_watch(sym_name, gdb.WP_ACCESS, "awatch") - - # Again should hit for reads - do_one_watch(sym_name, gdb.WP_READ, "rwatch") - - # Finally when it is written - do_one_watch(sym_name, gdb.WP_WRITE, "watch") - - -class CatchBreakpoint(gdb.Breakpoint): - def __init__(self, sym_name): - super(CatchBreakpoint, self).__init__(sym_name) - self.sym, ok = gdb.lookup_symbol(sym_name) - - def stop(self): - end_pc = gdb.parse_and_eval('$pc') - print ("CB: %s == %s" % (end_pc, self.sym.value())) - if end_pc == self.sym.value(): - report(False, "Hit final catchpoint") - - -def run_test(): - "Run through the tests one by one" - - print ("Checking we can step the first few instructions") - step_ok = 0 - for i in range(3): - if check_step(): - step_ok += 1 - - report(step_ok == 3, "single step in boot code") - - print ("Checking HW breakpoint works") - break_ok = check_hbreak("kernel_init") - report(break_ok, "hbreak @ kernel_init") - - # Can't set this up until we are in the kernel proper - # if we make it to run_init_process we've over-run and - # one of the tests failed - print ("Setup catch-all for run_init_process") - cbp = CatchBreakpoint("run_init_process") - cpb2 = CatchBreakpoint("try_to_run_init_process") - - print ("Checking Normal breakpoint works") - break_ok = check_break("wait_for_completion") - report(break_ok, "break @ wait_for_completion") - - print ("Checking watchpoint works") - check_watches("system_state") - -# -# This runs as the script it sourced (via -x) -# - -try: - print ("Connecting to remote") - gdb.execute("target remote localhost:1234") - - # 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 - -# Finally kill the inferior and exit gdb with a count of failures -gdb.execute("kill") -exit(failcount) diff --git a/tests/guest-debug/test_gdbstub.py b/tests/guest-debug/test_gdbstub.py new file mode 100644 index 0000000000..7f71d34da1 --- /dev/null +++ b/tests/guest-debug/test_gdbstub.py @@ -0,0 +1,60 @@ +"""Helper functions for gdbstub testing + +""" +from __future__ import print_function +import gdb +import os +import sys +import traceback + +fail_count = 0 + + +def report(cond, msg): + """Report success/fail of a test""" + if cond: + print("PASS: {}".format(msg)) + else: + print("FAIL: {}".format(msg)) + global fail_count + fail_count += 1 + + +def main(test, expected_arch=None): + """Run a test function + + This runs as the script it sourced (via -x, via run-test.py).""" + try: + inferior = gdb.selected_inferior() + arch = inferior.architecture() + print("ATTACHED: {}".format(arch.name())) + if expected_arch is not None: + report(arch.name() == expected_arch, + "connected to {}".format(expected_arch)) + except (gdb.error, AttributeError): + print("SKIP: not connected") + exit(0) + + if gdb.parse_and_eval("$pc") == 0: + print("SKIP: PC not set") + exit(0) + + try: + test() + except: + print("GDB Exception:") + traceback.print_exc(file=sys.stdout) + global fail_count + fail_count += 1 + if "QEMU_TEST_INTERACTIVE" in os.environ: + import code + code.InteractiveConsole(locals=globals()).interact() + raise + + try: + gdb.execute("kill") + except gdb.error: + pass + + print("All tests complete: {} failures".format(fail_count)) + exit(fail_count) diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci index f83b916d5e..77c800186f 160000 --- a/tests/lcitool/libvirt-ci +++ b/tests/lcitool/libvirt-ci @@ -1 +1 @@ -Subproject commit f83b916d5efa4bd33fbf4b7ea41bf6d535cc63fb +Subproject commit 77c800186f34b21be7660750577cc5582a914deb diff --git a/tests/lcitool/mappings.yml b/tests/lcitool/mappings.yml new file mode 100644 index 0000000000..407c03301b --- /dev/null +++ b/tests/lcitool/mappings.yml @@ -0,0 +1,97 @@ +mappings: + flake8: + CentOSStream8: + OpenSUSELeap15: + + meson: + CentOSStream8: + OpenSUSELeap15: + + python3: + CentOSStream8: python38 + OpenSUSELeap15: python311-base + + python3-PyYAML: + CentOSStream8: python38-PyYAML + OpenSUSELeap15: + + python3-devel: + CentOSStream8: python38-devel + OpenSUSELeap15: python311-devel + + python3-docutils: + CentOSStream8: + OpenSUSELeap15: + + python3-numpy: + CentOSStream8: python38-numpy + OpenSUSELeap15: + + python3-opencv: + CentOSStream8: + OpenSUSELeap15: + + python3-pillow: + CentOSStream8: + OpenSUSELeap15: + + python3-pip: + CentOSStream8: python38-pip + OpenSUSELeap15: python311-pip + + python3-pillow: + CentOSStream8: + OpenSUSELeap15: + + python3-selinux: + CentOSStream8: + OpenSUSELeap15: + + python3-setuptools: + CentOSStream8: python38-setuptools + OpenSUSELeap15: python311-setuptools + + python3-sphinx: + CentOSStream8: + OpenSUSELeap15: + + python3-sphinx-rtd-theme: + CentOSStream8: + OpenSUSELeap15: + + python3-sqlite3: + CentOSStream8: python38 + OpenSUSELeap15: python311 + + python3-tomli: + # test using tomllib + apk: + Fedora: + Debian12: + OpenSUSELeap15: + # Not available for Python 3.8 + CentOSStream8: + + python3-venv: + CentOSStream8: python38 + OpenSUSELeap15: python311-base + + python3-wheel: + CentOSStream8: python38-wheel + OpenSUSELeap15: python311-pip + +pypi_mappings: + # Request more recent version + meson: + default: meson==0.63.2 + + # Drop packages that need devel headers + python3-numpy: + OpenSUSELeap15: + + # see above + python3-tomli: + apk: + Fedora: + Debian12: + OpenSUSELeap15: diff --git a/tests/lcitool/projects/qemu-minimal.yml b/tests/lcitool/projects/qemu-minimal.yml new file mode 100644 index 0000000000..d44737dc1d --- /dev/null +++ b/tests/lcitool/projects/qemu-minimal.yml @@ -0,0 +1,27 @@ +# Very minimal set of qemu packages, used for minimal cross-compile sanity checks +--- +packages: + - bash + - bc + - bison + - ccache + - findutils + - flex + - g++ + - gcc + - gcc-native + - glib2 + - glib2-native + - glib2-static + - libc-static + - libfdt + - libffi + - make + - meson + - ninja + - pixman + - pkg-config + - python3 + - python3-venv + - sed + - tar diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index d068a7a8de..149b15de57 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -3,12 +3,14 @@ packages: - alsa - bash - bc + - bison - brlapi - bzip2 - bzip2-libs - capstone - ccache - clang + - cmocka - column - ctags - cyrus-sasl @@ -18,23 +20,26 @@ packages: - diffutils - dtrace - findutils + - flex - fuse3 - g++ - gcc + - gcc-native - gcovr - gettext - - genisoimage - glib2 + - glib2-native - glib2-static - - glibc-static - glusterfs - gnutls - gtk3 - hostname + - json-c - libaio - libattr - libasan - libbpf + - libc-static - libcacard - libcap-ng - libcurl @@ -64,10 +69,12 @@ packages: - liburing - libusbx - libvdeplug + - libxdp - libzstd - llvm - lttng-ust - lzo + - mtools - netcat - nettle - ninja @@ -78,8 +85,8 @@ packages: - ncursesw - pam - pcre-static - - perl - pixman + - libpipewire-dev - pkg-config - pulseaudio - python3 @@ -90,26 +97,32 @@ packages: - python3-pip - python3-sphinx - python3-sphinx-rtd-theme - - python3-virtualenv + - python3-sqlite3 + - python3-tomli + - python3-venv - rpm2cpio - sdl2 - sdl2-image - sed - snappy + - sndio + - socat - sparse - spice-protocol - spice-server - ssh-client + - swtpm - systemd - tar - tesseract - tesseract-eng - - texinfo - usbredir - virglrenderer - vte - which - xen - xfsprogs + - xorriso + - zstdtools - zlib - zlib-static diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 5e260f8cd6..fe7692c500 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -25,7 +25,7 @@ self_dir = Path(__file__).parent src_dir = self_dir.parent.parent dockerfiles_dir = Path(src_dir, "tests", "docker", "dockerfiles") -lcitool_path = Path(self_dir, "libvirt-ci", "lcitool") +lcitool_path = Path(self_dir, "libvirt-ci", "bin", "lcitool") lcitool_cmd = [lcitool_path, "--data-dir", self_dir] @@ -53,13 +53,28 @@ def generate(filename, cmd, trailer): content += trailer atomic_write(filename, content) +# Optional user setting, this will always be the last thing added +# so maximise the number of layers that are cached +add_user_mapping = [ + "# As a final step configure the user (if env is defined)", + "ARG USER", + "ARG UID", + "RUN if [ \"${USER}\" ]; then \\", + " id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi\n" +] -def generate_dockerfile(host, target, cross=None, trailer=None): +def generate_dockerfile(host, target, project="qemu", cross=None, trailer=None): filename = Path(src_dir, "tests", "docker", "dockerfiles", host + ".docker") cmd = lcitool_cmd + ["dockerfile"] if cross is not None: cmd.extend(["--cross", cross]) - cmd.extend([target, "qemu"]) + cmd.extend([target, project]) + + if trailer is not None: + trailer += "\n".join(add_user_mapping) + else: + trailer = "\n".join(add_user_mapping) + generate(filename, cmd, trailer) @@ -69,31 +84,33 @@ def generate_cirrus(target, trailer=None): generate(filename, cmd, trailer) -ubuntu2004_tsanhack = [ - "# Apply patch https://reviews.llvm.org/D75820\n", - "# This is required for TSan in clang-10 to compile with QEMU.\n", - "RUN sed -i 's/^const/static const/g' /usr/lib/llvm-10/lib/clang/10.0.0/include/sanitizer/tsan_interface.h\n" -] +def generate_pkglist(vm, target): + filename = Path(src_dir, "tests", "vm", "generated", vm + ".json") + cmd = lcitool_cmd + ["variables", "--format", "json", target, "qemu"] + generate(filename, cmd, None) # Netmap still needs to be manually built as it is yet to be packaged # into a distro. We also add cscope and gtags which are used in the CI # test -debian11_extras = [ +debian12_extras = [ "# netmap/cscope/global\n", "RUN DEBIAN_FRONTEND=noninteractive eatmydata \\\n", " apt install -y --no-install-recommends \\\n", " cscope\\\n", " global\\\n", - " linux-headers-amd64\n", + " linux-headers-generic\n", "RUN git clone https://github.com/luigirizzo/netmap.git /usr/src/netmap\n", "RUN cd /usr/src/netmap && git checkout v11.3\n", - "RUN cd /usr/src/netmap/LINUX && ./configure --no-drivers --no-apps --kernel-dir=$(ls -d /usr/src/linux-headers-*-amd64) && make install\n", + "RUN cd /usr/src/netmap/LINUX && \\\n", + " ./configure --no-drivers --no-apps \\\n", + " --kernel-dir=$(ls -d /usr/src/linux-headers-*-$(dpkg --print-architecture)) \\\n", + " && make install\n", "ENV QEMU_CONFIGURE_OPTS --enable-netmap\n" ] -def debian_cross_build(prefix, targets): +def cross_build(prefix, targets): conf = "ENV QEMU_CONFIGURE_OPTS --cross-prefix=%s\n" % (prefix) targets = "ENV DEF_TARGET_LIST %s\n" % (targets) return "".join([conf, targets]) @@ -107,59 +124,90 @@ try: # # Standard native builds # - generate_dockerfile("alpine", "alpine-edge") + generate_dockerfile("alpine", "alpine-318") generate_dockerfile("centos8", "centos-stream-8") - generate_dockerfile("debian-amd64", "debian-11", - trailer="".join(debian11_extras)) - generate_dockerfile("fedora", "fedora-35") - generate_dockerfile("opensuse-leap", "opensuse-leap-152") - generate_dockerfile("ubuntu2004", "ubuntu-2004", - trailer="".join(ubuntu2004_tsanhack)) + generate_dockerfile("debian", "debian-12", + trailer="".join(debian12_extras)) + generate_dockerfile("fedora", "fedora-38") + generate_dockerfile("opensuse-leap", "opensuse-leap-15") + generate_dockerfile("ubuntu2004", "ubuntu-2004") + generate_dockerfile("ubuntu2204", "ubuntu-2204") # # Cross compiling builds # - generate_dockerfile("debian-arm64-cross", "debian-11", - cross="aarch64", - trailer=debian_cross_build("aarch64-linux-gnu-", - "aarch64-softmmu,aarch64-linux-user")) + generate_dockerfile("debian-amd64-cross", "debian-12", + cross="x86_64", + trailer=cross_build("x86_64-linux-gnu-", + "x86_64-softmmu," + "x86_64-linux-user," + "i386-softmmu,i386-linux-user")) + generate_dockerfile("debian-arm64-cross", "debian-12", + cross="aarch64", + trailer=cross_build("aarch64-linux-gnu-", + "aarch64-softmmu,aarch64-linux-user")) + + # migration to bookworm stalled: https://lists.debian.org/debian-arm/2023/09/msg00006.html generate_dockerfile("debian-armel-cross", "debian-11", cross="armv6l", - trailer=debian_cross_build("arm-linux-gnueabi-", - "arm-softmmu,arm-linux-user,armeb-linux-user")) + trailer=cross_build("arm-linux-gnueabi-", + "arm-softmmu,arm-linux-user,armeb-linux-user")) - generate_dockerfile("debian-armhf-cross", "debian-11", + generate_dockerfile("debian-armhf-cross", "debian-12", cross="armv7l", - trailer=debian_cross_build("arm-linux-gnueabihf-", - "arm-softmmu,arm-linux-user")) + trailer=cross_build("arm-linux-gnueabihf-", + "arm-softmmu,arm-linux-user")) + + generate_dockerfile("debian-i686-cross", "debian-11", + cross="i686", + trailer=cross_build("x86_64-linux-gnu-", + "x86_64-softmmu," + "x86_64-linux-user," + "i386-softmmu,i386-linux-user")) generate_dockerfile("debian-mips64el-cross", "debian-11", cross="mips64el", - trailer=debian_cross_build("mips64el-linux-gnuabi64-", - "mips64el-softmmu,mips64el-linux-user")) + trailer=cross_build("mips64el-linux-gnuabi64-", + "mips64el-softmmu,mips64el-linux-user")) generate_dockerfile("debian-mipsel-cross", "debian-11", cross="mipsel", - trailer=debian_cross_build("mipsel-linux-gnu-", - "mipsel-softmmu,mipsel-linux-user")) + trailer=cross_build("mipsel-linux-gnu-", + "mipsel-softmmu,mipsel-linux-user")) - generate_dockerfile("debian-ppc64el-cross", "debian-11", + generate_dockerfile("debian-ppc64el-cross", "debian-12", cross="ppc64le", - trailer=debian_cross_build("powerpc64le-linux-gnu-", - "ppc64-softmmu,ppc64-linux-user")) + trailer=cross_build("powerpc64le-linux-gnu-", + "ppc64-softmmu,ppc64-linux-user")) - generate_dockerfile("debian-s390x-cross", "debian-11", + generate_dockerfile("debian-riscv64-cross", "debian-sid", + project="qemu-minimal", + cross="riscv64", + trailer=cross_build("riscv64-linux-gnu-", + "riscv64-softmmu,riscv64-linux-user")) + + generate_dockerfile("debian-s390x-cross", "debian-12", cross="s390x", - trailer=debian_cross_build("s390x-linux-gnu-", - "s390x-softmmu,s390x-linux-user")) + trailer=cross_build("s390x-linux-gnu-", + "s390x-softmmu,s390x-linux-user")) + + generate_dockerfile("fedora-win64-cross", "fedora-38", + cross="mingw64", + trailer=cross_build("x86_64-w64-mingw32-", + "x86_64-softmmu")) # # Cirrus packages lists for GitLab # - generate_cirrus("freebsd-12") generate_cirrus("freebsd-13") - generate_cirrus("macos-11") + generate_cirrus("macos-13") + generate_cirrus("macos-14") + + # + # VM packages lists + # + generate_pkglist("freebsd", "freebsd-13") sys.exit(0) except Exception as ex: diff --git a/tests/lcitool/targets/centos-stream-8.yml b/tests/lcitool/targets/centos-stream-8.yml new file mode 100644 index 0000000000..6b11160fd1 --- /dev/null +++ b/tests/lcitool/targets/centos-stream-8.yml @@ -0,0 +1,3 @@ +paths: + pip3: /usr/bin/pip3.8 + python: /usr/bin/python3.8 diff --git a/tests/lcitool/targets/opensuse-leap-15.yml b/tests/lcitool/targets/opensuse-leap-15.yml new file mode 100644 index 0000000000..c2d87f6cb4 --- /dev/null +++ b/tests/lcitool/targets/opensuse-leap-15.yml @@ -0,0 +1,3 @@ +paths: + pip3: /usr/bin/pip3.11 + python: /usr/bin/python3.11 diff --git a/tests/meson.build b/tests/meson.build index 8e318ec513..0a6f96f8f8 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -68,27 +68,19 @@ test_deps = { 'test-qht-par': qht_bench, } -if have_tools and have_vhost_user and 'CONFIG_LINUX' in config_host +if have_tools and have_vhost_user and host_os == 'linux' executable('vhost-user-bridge', sources: files('vhost-user-bridge.c'), dependencies: [qemuutil, vhost_user]) endif -test('decodetree', sh, - args: [ files('decode/check.sh'), config_host['PYTHON'], files('../scripts/decodetree.py') ], - workdir: meson.current_source_dir() / 'decode', - suite: 'decodetree') +subdir('decode') -if 'CONFIG_TCG' in config_all +if 'CONFIG_TCG' in config_all_accel subdir('fp') endif -if get_option('tcg').allowed() - if 'CONFIG_PLUGIN' in config_host - subdir('plugin') - endif -endif - +subdir('plugin') subdir('unit') subdir('qapi-schema') subdir('qtest') diff --git a/tests/migration/aarch64/a-b-kernel.S b/tests/migration/aarch64/a-b-kernel.S index 0225945348..a4103ecb71 100644 --- a/tests/migration/aarch64/a-b-kernel.S +++ b/tests/migration/aarch64/a-b-kernel.S @@ -53,7 +53,6 @@ innerloop: /* increment the first byte of each page by 1 */ ldrb w3, [x4] add w3, w3, #1 - and w3, w3, #0xff strb w3, [x4] /* make sure QEMU user space can see consistent data as MMU is off */ @@ -64,7 +63,7 @@ innerloop: blt innerloop add w5, w5, #1 - and w5, w5, #0xff + and w5, w5, #0x1f cmp w5, #0 bne mainloop diff --git a/tests/migration/aarch64/a-b-kernel.h b/tests/migration/aarch64/a-b-kernel.h index 0a9b01137e..34e518d061 100644 --- a/tests/migration/aarch64/a-b-kernel.h +++ b/tests/migration/aarch64/a-b-kernel.h @@ -10,9 +10,9 @@ unsigned char aarch64_kernel[] = { 0x03, 0x00, 0x80, 0x52, 0xe4, 0x03, 0x00, 0xaa, 0x83, 0x00, 0x00, 0x39, 0x84, 0x04, 0x40, 0x91, 0x9f, 0x00, 0x01, 0xeb, 0xad, 0xff, 0xff, 0x54, 0x05, 0x00, 0x80, 0x52, 0xe4, 0x03, 0x00, 0xaa, 0x83, 0x00, 0x40, 0x39, - 0x63, 0x04, 0x00, 0x11, 0x63, 0x1c, 0x00, 0x12, 0x83, 0x00, 0x00, 0x39, - 0x24, 0x7e, 0x0b, 0xd5, 0x84, 0x04, 0x40, 0x91, 0x9f, 0x00, 0x01, 0xeb, - 0x2b, 0xff, 0xff, 0x54, 0xa5, 0x04, 0x00, 0x11, 0xa5, 0x1c, 0x00, 0x12, - 0xbf, 0x00, 0x00, 0x71, 0x81, 0xfe, 0xff, 0x54, 0x43, 0x08, 0x80, 0x52, - 0x43, 0x00, 0x00, 0x39, 0xf1, 0xff, 0xff, 0x17 + 0x63, 0x04, 0x00, 0x11, 0x83, 0x00, 0x00, 0x39, 0x24, 0x7e, 0x0b, 0xd5, + 0x84, 0x04, 0x40, 0x91, 0x9f, 0x00, 0x01, 0xeb, 0x4b, 0xff, 0xff, 0x54, + 0xa5, 0x04, 0x00, 0x11, 0xa5, 0x10, 0x00, 0x12, 0xbf, 0x00, 0x00, 0x71, + 0xa1, 0xfe, 0xff, 0x54, 0x43, 0x08, 0x80, 0x52, 0x43, 0x00, 0x00, 0x39, + 0xf2, 0xff, 0xff, 0x17 }; diff --git a/tests/migration/guestperf-batch.py b/tests/migration/guestperf-batch.py index ab6bdb9d38..9485eefe49 100755 --- a/tests/migration/guestperf-batch.py +++ b/tests/migration/guestperf-batch.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Migration test batch comparison invokation +# Migration test batch comparison invocation # # Copyright (c) 2016 Red Hat, Inc. # diff --git a/tests/migration/guestperf.py b/tests/migration/guestperf.py index e8cc127fd0..07182f211e 100755 --- a/tests/migration/guestperf.py +++ b/tests/migration/guestperf.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Migration test direct invokation command +# Migration test direct invocation command # # Copyright (c) 2016 Red Hat, Inc. # diff --git a/tests/migration/guestperf/comparison.py b/tests/migration/guestperf/comparison.py index c03b3f6d7e..42cc0372d1 100644 --- a/tests/migration/guestperf/comparison.py +++ b/tests/migration/guestperf/comparison.py @@ -135,4 +135,27 @@ COMPARISONS = [ Scenario("compr-multifd-channels-64", multifd=True, multifd_channels=64), ]), + + # Looking at effect of dirty-limit with + # varying x_vcpu_dirty_limit_period + Comparison("compr-dirty-limit-period", scenarios = [ + Scenario("compr-dirty-limit-period-500", + dirty_limit=True, x_vcpu_dirty_limit_period=500), + Scenario("compr-dirty-limit-period-800", + dirty_limit=True, x_vcpu_dirty_limit_period=800), + Scenario("compr-dirty-limit-period-1000", + dirty_limit=True, x_vcpu_dirty_limit_period=1000), + ]), + + + # Looking at effect of dirty-limit with + # varying vcpu_dirty_limit + Comparison("compr-dirty-limit", scenarios = [ + Scenario("compr-dirty-limit-10MB", + dirty_limit=True, vcpu_dirty_limit=10), + Scenario("compr-dirty-limit-20MB", + dirty_limit=True, vcpu_dirty_limit=20), + Scenario("compr-dirty-limit-50MB", + dirty_limit=True, vcpu_dirty_limit=50), + ]), ] diff --git a/tests/migration/guestperf/engine.py b/tests/migration/guestperf/engine.py index 87a6ab2009..608d7270f6 100644 --- a/tests/migration/guestperf/engine.py +++ b/tests/migration/guestperf/engine.py @@ -65,7 +65,6 @@ class Engine(object): return records def _cpu_timing(self, pid): - records = [] now = time.time() jiffies_per_sec = os.sysconf(os.sysconf_names['SC_CLK_TCK']) @@ -78,7 +77,7 @@ class Engine(object): return TimingRecord(pid, now, 1000 * (stime + utime) / jiffies_per_sec) def _migrate_progress(self, vm): - info = vm.command("query-migrate") + info = vm.cmd("query-migrate") if "ram" not in info: info["ram"] = {} @@ -103,6 +102,8 @@ class Engine(object): info.get("expected-downtime", 0), info.get("setup-time", 0), info.get("cpu-throttle-percentage", 0), + info.get("dirty-limit-throttle-time-per-round", 0), + info.get("dirty-limit-ring-full-time", 0), ) def _migrate(self, hardware, scenario, src, dst, connect_uri): @@ -110,7 +111,7 @@ class Engine(object): src_vcpu_time = [] src_pid = src.get_pid() - vcpus = src.command("query-cpus-fast") + vcpus = src.cmd("query-cpus-fast") src_threads = [] for vcpu in vcpus: src_threads.append(vcpu["thread-id"]) @@ -129,82 +130,97 @@ class Engine(object): if self._verbose: print("Starting migration") if scenario._auto_converge: - resp = src.command("migrate-set-capabilities", - capabilities = [ - { "capability": "auto-converge", - "state": True } - ]) - resp = src.command("migrate-set-parameters", - cpu_throttle_increment=scenario._auto_converge_step) + resp = src.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "auto-converge", + "state": True } + ]) + resp = src.cmd("migrate-set-parameters", + cpu_throttle_increment=scenario._auto_converge_step) if scenario._post_copy: - resp = src.command("migrate-set-capabilities", - capabilities = [ - { "capability": "postcopy-ram", - "state": True } - ]) - resp = dst.command("migrate-set-capabilities", - capabilities = [ - { "capability": "postcopy-ram", - "state": True } - ]) + resp = src.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "postcopy-ram", + "state": True } + ]) + resp = dst.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "postcopy-ram", + "state": True } + ]) - resp = src.command("migrate-set-parameters", - max_bandwidth=scenario._bandwidth * 1024 * 1024) + resp = src.cmd("migrate-set-parameters", + max_bandwidth=scenario._bandwidth * 1024 * 1024) - resp = src.command("migrate-set-parameters", - downtime_limit=scenario._downtime) + resp = src.cmd("migrate-set-parameters", + downtime_limit=scenario._downtime) if scenario._compression_mt: - resp = src.command("migrate-set-capabilities", - capabilities = [ - { "capability": "compress", - "state": True } - ]) - resp = src.command("migrate-set-parameters", - compress_threads=scenario._compression_mt_threads) - resp = dst.command("migrate-set-capabilities", - capabilities = [ - { "capability": "compress", - "state": True } - ]) - resp = dst.command("migrate-set-parameters", - decompress_threads=scenario._compression_mt_threads) + resp = src.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "compress", + "state": True } + ]) + resp = src.cmd("migrate-set-parameters", + compress_threads=scenario._compression_mt_threads) + resp = dst.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "compress", + "state": True } + ]) + resp = dst.cmd("migrate-set-parameters", + decompress_threads=scenario._compression_mt_threads) if scenario._compression_xbzrle: - resp = src.command("migrate-set-capabilities", - capabilities = [ - { "capability": "xbzrle", - "state": True } - ]) - resp = dst.command("migrate-set-capabilities", - capabilities = [ - { "capability": "xbzrle", - "state": True } - ]) - resp = src.command("migrate-set-parameters", - xbzrle_cache_size=( - hardware._mem * - 1024 * 1024 * 1024 / 100 * - scenario._compression_xbzrle_cache)) + resp = src.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "xbzrle", + "state": True } + ]) + resp = dst.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "xbzrle", + "state": True } + ]) + resp = src.cmd("migrate-set-parameters", + xbzrle_cache_size=( + hardware._mem * + 1024 * 1024 * 1024 / 100 * + scenario._compression_xbzrle_cache)) if scenario._multifd: - resp = src.command("migrate-set-capabilities", - capabilities = [ - { "capability": "multifd", - "state": True } - ]) - resp = src.command("migrate-set-parameters", - multifd_channels=scenario._multifd_channels) - resp = dst.command("migrate-set-capabilities", - capabilities = [ - { "capability": "multifd", - "state": True } - ]) - resp = dst.command("migrate-set-parameters", - multifd_channels=scenario._multifd_channels) + resp = src.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "multifd", + "state": True } + ]) + resp = src.cmd("migrate-set-parameters", + multifd_channels=scenario._multifd_channels) + resp = dst.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "multifd", + "state": True } + ]) + resp = dst.cmd("migrate-set-parameters", + multifd_channels=scenario._multifd_channels) - resp = src.command("migrate", uri=connect_uri) + if scenario._dirty_limit: + if not hardware._dirty_ring_size: + raise Exception("dirty ring size must be configured when " + "testing dirty limit migration") + + resp = src.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "dirty-limit", + "state": True } + ]) + resp = src.cmd("migrate-set-parameters", + x_vcpu_dirty_limit_period=scenario._x_vcpu_dirty_limit_period) + resp = src.cmd("migrate-set-parameters", + vcpu_dirty_limit=scenario._vcpu_dirty_limit) + + resp = src.cmd("migrate", uri=connect_uri) post_copy = False paused = False @@ -229,7 +245,7 @@ class Engine(object): if progress._status in ("completed", "failed", "cancelled"): if progress._status == "completed" and paused: - dst.command("cont") + dst.cmd("cont") if progress_history[-1] != progress: progress_history.append(progress) @@ -257,13 +273,13 @@ class Engine(object): if progress._ram._iterations > scenario._max_iters: if self._verbose: print("No completion after %d iterations over RAM" % scenario._max_iters) - src.command("migrate_cancel") + src.cmd("migrate_cancel") continue if time.time() > (start + scenario._max_time): if self._verbose: print("No completion after %d seconds" % scenario._max_time) - src.command("migrate_cancel") + src.cmd("migrate_cancel") continue if (scenario._post_copy and @@ -271,7 +287,7 @@ class Engine(object): not post_copy): if self._verbose: print("Switching to post-copy after %d iterations" % scenario._post_copy_iters) - resp = src.command("migrate-start-postcopy") + resp = src.cmd("migrate-start-postcopy") post_copy = True if (scenario._pause and @@ -279,9 +295,29 @@ class Engine(object): not paused): if self._verbose: print("Pausing VM after %d iterations" % scenario._pause_iters) - resp = src.command("stop") + resp = src.cmd("stop") paused = True + def _is_ppc64le(self): + _, _, _, _, machine = os.uname() + if machine == "ppc64le": + return True + return False + + def _get_guest_console_args(self): + if self._is_ppc64le(): + return "console=hvc0" + else: + return "console=ttyS0" + + def _get_qemu_serial_args(self): + if self._is_ppc64le(): + return ["-chardev", "stdio,id=cdev0", + "-device", "spapr-vty,chardev=cdev0"] + else: + return ["-chardev", "stdio,id=cdev0", + "-device", "isa-serial,chardev=cdev0"] + def _get_common_args(self, hardware, tunnelled=False): args = [ "noapic", @@ -290,8 +326,10 @@ class Engine(object): "noreplace-smp", "cgroup_disable=memory", "pci=noearly", - "console=ttyS0", ] + + args.append(self._get_guest_console_args()) + if self._debug: args.append("debug") else: @@ -304,19 +342,23 @@ class Engine(object): cmdline = "'" + cmdline + "'" argv = [ - "-accel", "kvm", "-cpu", "host", "-kernel", self._kernel, "-initrd", self._initrd, "-append", cmdline, - "-chardev", "stdio,id=cdev0", - "-device", "isa-serial,chardev=cdev0", "-m", str((hardware._mem * 1024) + 512), "-smp", str(hardware._cpus), ] + if hardware._dirty_ring_size: + argv.extend(["-accel", "kvm,dirty-ring-size=%s" % + hardware._dirty_ring_size]) + else: + argv.extend(["-accel", "kvm"]) + + argv.extend(self._get_qemu_serial_args()) if self._debug: - argv.extend(["-device", "sga"]) + argv.extend(["-machine", "graphics=off"]) if hardware._prealloc_pages: argv_source += ["-mem-path", "/dev/shm", diff --git a/tests/migration/guestperf/hardware.py b/tests/migration/guestperf/hardware.py index 3145785ffd..f779cc050b 100644 --- a/tests/migration/guestperf/hardware.py +++ b/tests/migration/guestperf/hardware.py @@ -23,7 +23,8 @@ class Hardware(object): src_cpu_bind=None, src_mem_bind=None, dst_cpu_bind=None, dst_mem_bind=None, prealloc_pages = False, - huge_pages=False, locked_pages=False): + huge_pages=False, locked_pages=False, + dirty_ring_size=0): self._cpus = cpus self._mem = mem # GiB self._src_mem_bind = src_mem_bind # List of NUMA nodes @@ -33,6 +34,7 @@ class Hardware(object): self._prealloc_pages = prealloc_pages self._huge_pages = huge_pages self._locked_pages = locked_pages + self._dirty_ring_size = dirty_ring_size def serialize(self): @@ -46,6 +48,7 @@ class Hardware(object): "prealloc_pages": self._prealloc_pages, "huge_pages": self._huge_pages, "locked_pages": self._locked_pages, + "dirty_ring_size": self._dirty_ring_size, } @classmethod @@ -59,4 +62,5 @@ class Hardware(object): data["dst_mem_bind"], data["prealloc_pages"], data["huge_pages"], - data["locked_pages"]) + data["locked_pages"], + data["dirty_ring_size"]) diff --git a/tests/migration/guestperf/progress.py b/tests/migration/guestperf/progress.py index ab1ee57273..d490584217 100644 --- a/tests/migration/guestperf/progress.py +++ b/tests/migration/guestperf/progress.py @@ -81,7 +81,9 @@ class Progress(object): downtime, downtime_expected, setup_time, - throttle_pcent): + throttle_pcent, + dirty_limit_throttle_time_per_round, + dirty_limit_ring_full_time): self._status = status self._ram = ram @@ -91,6 +93,10 @@ class Progress(object): self._downtime_expected = downtime_expected self._setup_time = setup_time self._throttle_pcent = throttle_pcent + self._dirty_limit_throttle_time_per_round = \ + dirty_limit_throttle_time_per_round + self._dirty_limit_ring_full_time = \ + dirty_limit_ring_full_time def serialize(self): return { @@ -102,6 +108,10 @@ class Progress(object): "downtime_expected": self._downtime_expected, "setup_time": self._setup_time, "throttle_pcent": self._throttle_pcent, + "dirty_limit_throttle_time_per_round": + self._dirty_limit_throttle_time_per_round, + "dirty_limit_ring_full_time": + self._dirty_limit_ring_full_time, } @classmethod @@ -114,4 +124,6 @@ class Progress(object): data["downtime"], data["downtime_expected"], data["setup_time"], - data["throttle_pcent"]) + data["throttle_pcent"], + data["dirty_limit_throttle_time_per_round"], + data["dirty_limit_ring_full_time"]) diff --git a/tests/migration/guestperf/scenario.py b/tests/migration/guestperf/scenario.py index de70d9b2f5..154c4f5d5f 100644 --- a/tests/migration/guestperf/scenario.py +++ b/tests/migration/guestperf/scenario.py @@ -30,7 +30,9 @@ class Scenario(object): auto_converge=False, auto_converge_step=10, compression_mt=False, compression_mt_threads=1, compression_xbzrle=False, compression_xbzrle_cache=10, - multifd=False, multifd_channels=2): + multifd=False, multifd_channels=2, + dirty_limit=False, x_vcpu_dirty_limit_period=500, + vcpu_dirty_limit=1): self._name = name @@ -60,6 +62,10 @@ class Scenario(object): self._multifd = multifd self._multifd_channels = multifd_channels + self._dirty_limit = dirty_limit + self._x_vcpu_dirty_limit_period = x_vcpu_dirty_limit_period + self._vcpu_dirty_limit = vcpu_dirty_limit + def serialize(self): return { "name": self._name, @@ -79,6 +85,9 @@ class Scenario(object): "compression_xbzrle_cache": self._compression_xbzrle_cache, "multifd": self._multifd, "multifd_channels": self._multifd_channels, + "dirty_limit": self._dirty_limit, + "x_vcpu_dirty_limit_period": self._x_vcpu_dirty_limit_period, + "vcpu_dirty_limit": self._vcpu_dirty_limit, } @classmethod diff --git a/tests/migration/guestperf/shell.py b/tests/migration/guestperf/shell.py index 8a809e3dda..c85d89efec 100644 --- a/tests/migration/guestperf/shell.py +++ b/tests/migration/guestperf/shell.py @@ -60,6 +60,8 @@ class BaseShell(object): parser.add_argument("--prealloc-pages", dest="prealloc_pages", default=False) parser.add_argument("--huge-pages", dest="huge_pages", default=False) parser.add_argument("--locked-pages", dest="locked_pages", default=False) + parser.add_argument("--dirty-ring-size", dest="dirty_ring_size", + default=0, type=int) self._parser = parser @@ -89,7 +91,9 @@ class BaseShell(object): locked_pages=args.locked_pages, huge_pages=args.huge_pages, - prealloc_pages=args.prealloc_pages) + prealloc_pages=args.prealloc_pages, + + dirty_ring_size=args.dirty_ring_size) class Shell(BaseShell): @@ -127,6 +131,17 @@ class Shell(BaseShell): parser.add_argument("--multifd-channels", dest="multifd_channels", default=2, type=int) + parser.add_argument("--dirty-limit", dest="dirty_limit", default=False, + action="store_true") + + parser.add_argument("--x-vcpu-dirty-limit-period", + dest="x_vcpu_dirty_limit_period", + default=500, type=int) + + parser.add_argument("--vcpu-dirty-limit", + dest="vcpu_dirty_limit", + default=1, type=int) + def get_scenario(self, args): return Scenario(name="perfreport", downtime=args.downtime, @@ -150,7 +165,12 @@ class Shell(BaseShell): compression_xbzrle_cache=args.compression_xbzrle_cache, multifd=args.multifd, - multifd_channels=args.multifd_channels) + multifd_channels=args.multifd_channels, + + dirty_limit=args.dirty_limit, + x_vcpu_dirty_limit_period=\ + args.x_vcpu_dirty_limit_period, + vcpu_dirty_limit=args.vcpu_dirty_limit) def run(self, argv): args = self._parser.parse_args(argv) diff --git a/tests/migration/i386/Makefile b/tests/migration/i386/Makefile index 5c0324134a..37a72ae353 100644 --- a/tests/migration/i386/Makefile +++ b/tests/migration/i386/Makefile @@ -4,9 +4,10 @@ .PHONY: all clean all: a-b-bootblock.h -a-b-bootblock.h: x86.bootsect +a-b-bootblock.h: x86.bootsect x86.o echo "$$__note" > header.tmp xxd -i $< | sed -e 's/.*int.*//' >> header.tmp + nm x86.o | awk '{print "#define SYM_"$$3" 0x"$$1}' >> header.tmp mv header.tmp $@ x86.bootsect: x86.boot @@ -16,7 +17,7 @@ x86.boot: x86.o $(CROSS_PREFIX)objcopy -O binary $< $@ x86.o: a-b-bootblock.S - $(CROSS_PREFIX)gcc -m32 -march=i486 -c $< -o $@ + $(CROSS_PREFIX)gcc -I.. -m32 -march=i486 -c $< -o $@ clean: @rm -rf *.boot *.o *.bootsect diff --git a/tests/migration/i386/a-b-bootblock.S b/tests/migration/i386/a-b-bootblock.S index 3f97f28023..6f39eb6051 100644 --- a/tests/migration/i386/a-b-bootblock.S +++ b/tests/migration/i386/a-b-bootblock.S @@ -9,6 +9,23 @@ # # Author: dgilbert@redhat.com +#include "migration-test.h" + +#define ACPI_ENABLE 0xf1 +#define ACPI_PORT_SMI_CMD 0xb2 +#define ACPI_PM_BASE 0x600 +#define PM1A_CNT_OFFSET 4 + +#define ACPI_SCI_ENABLE 0x0001 +#define ACPI_SLEEP_TYPE 0x0400 +#define ACPI_SLEEP_ENABLE 0x2000 +#define SLEEP (ACPI_SCI_ENABLE + ACPI_SLEEP_TYPE + ACPI_SLEEP_ENABLE) + +#define LOW_ADDR X86_TEST_MEM_START +#define HIGH_ADDR X86_TEST_MEM_END + +/* Save the suspended status at an address that is not written in the loop. */ +#define suspended (X86_TEST_MEM_START + 4) .code16 .org 0x7c00 @@ -34,29 +51,65 @@ start: # at 0x7c00 ? mov $16,%eax mov %eax,%ds +# Start from 1MB +.set TEST_MEM_START, X86_TEST_MEM_START +.set TEST_MEM_END, X86_TEST_MEM_END + mov $65,%ax mov $0x3f8,%dx outb %al,%dx # bl keeps a counter so we limit the output speed mov $0, %bl + +pre_zero: + mov $TEST_MEM_START,%eax +do_zero: + movb $0, (%eax) + add $4096,%eax + cmp $TEST_MEM_END,%eax + jl do_zero + mainloop: - # Start from 1MB - mov $(1024*1024),%eax + mov $TEST_MEM_START,%eax innerloop: incb (%eax) add $4096,%eax - cmp $(100*1024*1024),%eax + cmp $TEST_MEM_END,%eax jl innerloop inc %bl + andb $0x3f,%bl jnz mainloop mov $66,%ax mov $0x3f8,%dx outb %al,%dx - jmp mainloop + # should this test suspend? + mov (suspend_me),%eax + cmp $0,%eax + je mainloop + + # are we waking after suspend? do not suspend again. + mov $suspended,%eax + mov (%eax),%eax + cmp $1,%eax + je mainloop + + # enable acpi + mov $ACPI_ENABLE,%al + outb %al,$ACPI_PORT_SMI_CMD + + # suspend to ram + mov $suspended,%eax + movl $1,(%eax) + mov $SLEEP,%ax + mov $(ACPI_PM_BASE + PM1A_CNT_OFFSET),%dx + outw %ax,%dx + # not reached. The wakeup causes reset and restart at 0x7c00, and we + # do not save and restore registers as a real kernel would do. + # GDT magic from old (GPLv2) Grub startup.S .p2align 2 /* force 4-byte alignment */ @@ -82,6 +135,10 @@ gdtdesc: .word 0x27 /* limit */ .long gdt /* addr */ + /* test launcher can poke a 1 here to exercise suspend */ +suspend_me: + .int 0 + /* I'm a bootable disk */ .org 0x7dfe .byte 0x55 diff --git a/tests/migration/i386/a-b-bootblock.h b/tests/migration/i386/a-b-bootblock.h index 7d459d4fde..c83f8711db 100644 --- a/tests/migration/i386/a-b-bootblock.h +++ b/tests/migration/i386/a-b-bootblock.h @@ -4,22 +4,22 @@ * the header and the assembler differences in your patch submission. */ unsigned char x86_bootsect[] = { - 0xfa, 0x0f, 0x01, 0x16, 0x74, 0x7c, 0x66, 0xb8, 0x01, 0x00, 0x00, 0x00, + 0xfa, 0x0f, 0x01, 0x16, 0xb8, 0x7c, 0x66, 0xb8, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x22, 0xc0, 0x66, 0xea, 0x20, 0x7c, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x92, 0x0c, 0x02, 0xe6, 0x92, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x8e, 0xd8, 0x66, 0xb8, 0x41, 0x00, 0x66, 0xba, 0xf8, 0x03, 0xee, 0xb3, 0x00, 0xb8, 0x00, 0x00, 0x10, - 0x00, 0xfe, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x40, - 0x06, 0x7c, 0xf2, 0xfe, 0xc3, 0x75, 0xe9, 0x66, 0xb8, 0x42, 0x00, 0x66, - 0xba, 0xf8, 0x03, 0xee, 0xeb, 0xde, 0x66, 0x90, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00, - 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00, 0x27, 0x00, 0x5c, 0x7c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x00, + 0x40, 0x06, 0x7c, 0xf1, 0xb8, 0x00, 0x00, 0x10, 0x00, 0xfe, 0x00, 0x05, + 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x40, 0x06, 0x7c, 0xf2, 0xfe, + 0xc3, 0x80, 0xe3, 0x3f, 0x75, 0xe6, 0x66, 0xb8, 0x42, 0x00, 0x66, 0xba, + 0xf8, 0x03, 0xee, 0xa1, 0xbe, 0x7c, 0x00, 0x00, 0x83, 0xf8, 0x00, 0x74, + 0xd3, 0xb8, 0x04, 0x00, 0x10, 0x00, 0x8b, 0x00, 0x83, 0xf8, 0x01, 0x74, + 0xc7, 0xb0, 0xf1, 0xe6, 0xb2, 0xb8, 0x04, 0x00, 0x10, 0x00, 0xc7, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x66, 0xb8, 0x01, 0x24, 0x66, 0xba, 0x04, 0x06, + 0x66, 0xef, 0x66, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x92, 0xcf, 0x00, 0x27, 0x00, 0xa0, 0x7c, 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, @@ -49,3 +49,13 @@ unsigned char x86_bootsect[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa }; +#define SYM_do_zero 0x00007c3d +#define SYM_gdt 0x00007ca0 +#define SYM_gdtdesc 0x00007cb8 +#define SYM_innerloop 0x00007c51 +#define SYM_mainloop 0x00007c4c +#define SYM_pre_zero 0x00007c38 +#define SYM_start 0x00007c00 +#define SYM_suspend_me 0x00007cbe +#define SYM_TEST_MEM_END 0x06400000 +#define SYM_TEST_MEM_START 0x00100000 diff --git a/tests/migration/meson.build b/tests/migration/meson.build index f215ee7d3a..a91aa61c65 100644 --- a/tests/migration/meson.build +++ b/tests/migration/meson.build @@ -1,7 +1,11 @@ +sysprof = dependency('sysprof-capture-4', method: 'pkg-config', required: false) +glib_static = dependency('glib-2.0', version: glib_req_ver, required: false, + method: 'pkg-config', static: true) + stress = executable( 'stress', files('stress.c'), - dependencies: [glib], + dependencies: [glib_static, sysprof], link_args: ['-static'], build_by_default: false, ) diff --git a/tests/migration/s390x/Makefile b/tests/migration/s390x/Makefile index 6393c3e5b9..6671de2efc 100644 --- a/tests/migration/s390x/Makefile +++ b/tests/migration/s390x/Makefile @@ -6,8 +6,8 @@ all: a-b-bios.h fwdir=../../../pc-bios/s390-ccw CFLAGS+=-ffreestanding -fno-delete-null-pointer-checks -fPIE -Os \ - -msoft-float -march=z900 -fno-asynchronous-unwind-tables -Wl,-pie \ - -Wl,--build-id=none -nostdlib + -msoft-float -march=z900 -fno-asynchronous-unwind-tables \ + -fno-stack-protector -Wl,-pie -Wl,--build-id=none -nostdlib a-b-bios.h: s390x.elf echo "$$__note" > header.tmp diff --git a/tests/migration/s390x/a-b-bios.c b/tests/migration/s390x/a-b-bios.c index a0327cd153..ff99a3ef57 100644 --- a/tests/migration/s390x/a-b-bios.c +++ b/tests/migration/s390x/a-b-bios.c @@ -27,6 +27,14 @@ void main(void) sclp_setup(); sclp_print("A"); + /* + * Make sure all of the pages have consistent contents before incrementing + * the first byte below. + */ + for (addr = START_ADDRESS; addr < END_ADDRESS; addr += 4096) { + *(volatile char *)addr = 0; + } + while (1) { for (addr = START_ADDRESS; addr < END_ADDRESS; addr += 4096) { *(volatile char *)addr += 1; /* Change pages */ diff --git a/tests/migration/s390x/a-b-bios.h b/tests/migration/s390x/a-b-bios.h index e722dc7c40..96103dadbb 100644 --- a/tests/migration/s390x/a-b-bios.h +++ b/tests/migration/s390x/a-b-bios.h @@ -6,10 +6,10 @@ unsigned char s390x_elf[] = { 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x78, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x07, 0x00, 0x40, - 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x0d, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, @@ -21,140 +21,154 @@ unsigned char s390x_elf[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 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, 0x07, 0x0c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xac, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x98, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x18, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, + 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x64, 0x74, 0xe5, 0x51, 0x00, 0x00, 0x00, 0x07, 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, 0x10, 0x64, 0x74, 0xe5, 0x52, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x64, 0x36, 0x34, 0x2e, 0x73, 0x6f, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 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, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0xef, 0xf0, 0x70, - 0x00, 0x24, 0xa7, 0xfb, 0xff, 0x60, 0xc0, 0xe5, 0x00, 0x00, 0x01, 0x1f, - 0xc0, 0x20, 0x00, 0x00, 0x02, 0x64, 0xc0, 0xe5, 0x00, 0x00, 0x01, 0x35, - 0xa5, 0x1e, 0x00, 0x10, 0xa7, 0x29, 0x63, 0x00, 0xe3, 0x30, 0x10, 0x00, - 0x00, 0x90, 0xa7, 0x3a, 0x00, 0x01, 0x42, 0x30, 0x10, 0x00, 0xa7, 0x1b, - 0x10, 0x00, 0xa7, 0x27, 0xff, 0xf7, 0xc0, 0x20, 0x00, 0x00, 0x02, 0x50, - 0xa7, 0xf4, 0xff, 0xeb, 0x07, 0x07, 0x07, 0x07, 0xc0, 0xf0, 0x00, 0x00, - 0x56, 0xc4, 0xc0, 0x20, 0x00, 0x00, 0x0a, 0xbd, 0xc0, 0x30, 0x00, 0x00, - 0x56, 0xbe, 0xb9, 0x0b, 0x00, 0x32, 0xb9, 0x02, 0x00, 0x33, 0xa7, 0x84, - 0x00, 0x19, 0xa7, 0x3b, 0xff, 0xff, 0xeb, 0x43, 0x00, 0x08, 0x00, 0x0c, - 0xb9, 0x02, 0x00, 0x44, 0xb9, 0x04, 0x00, 0x12, 0xa7, 0x84, 0x00, 0x09, - 0xd7, 0xff, 0x10, 0x00, 0x10, 0x00, 0x41, 0x10, 0x11, 0x00, 0xa7, 0x47, - 0xff, 0xfb, 0xc0, 0x20, 0x00, 0x00, 0x00, 0x07, 0x44, 0x30, 0x20, 0x00, - 0xa7, 0xf4, 0xff, 0xb6, 0xd7, 0x00, 0x10, 0x00, 0x10, 0x00, 0xc0, 0x10, - 0x00, 0x00, 0x00, 0x29, 0xb2, 0xb2, 0x10, 0x00, 0xeb, 0x00, 0xf0, 0x00, - 0x00, 0x25, 0x96, 0x02, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x2f, - 0xc0, 0x10, 0x00, 0x00, 0x00, 0x11, 0xe3, 0x10, 0x01, 0xb8, 0x00, 0x24, - 0xc0, 0x10, 0x00, 0x00, 0x00, 0x26, 0xd2, 0x07, 0x01, 0xb0, 0x10, 0x00, - 0xc0, 0x10, 0x00, 0x00, 0x00, 0x18, 0xb2, 0xb2, 0x10, 0x00, 0xeb, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf0, 0xeb, 0xef, 0xf0, 0x70, + 0x00, 0x24, 0xa7, 0xfb, 0xff, 0x60, 0xc0, 0xe5, 0x00, 0x00, 0x01, 0x5f, + 0xc0, 0x20, 0x00, 0x00, 0x02, 0xa8, 0xc0, 0xe5, 0x00, 0x00, 0x01, 0x75, + 0xa5, 0x2e, 0x00, 0x10, 0xa7, 0x19, 0x63, 0x00, 0x92, 0x00, 0x20, 0x00, + 0xa7, 0x2b, 0x10, 0x00, 0xa7, 0x17, 0xff, 0xfc, 0xa5, 0x1e, 0x00, 0x10, + 0xa7, 0x29, 0x63, 0x00, 0xe3, 0x30, 0x10, 0x00, 0x00, 0x90, 0xa7, 0x3a, + 0x00, 0x01, 0x42, 0x30, 0x10, 0x00, 0xa7, 0x1b, 0x10, 0x00, 0xa7, 0x27, + 0xff, 0xf7, 0xc0, 0x20, 0x00, 0x00, 0x02, 0x8a, 0xc0, 0xe5, 0x00, 0x00, + 0x01, 0x56, 0xa7, 0xf4, 0xff, 0xeb, 0x07, 0x07, 0xc0, 0xf0, 0x00, 0x00, + 0x4e, 0x5c, 0xc0, 0x20, 0x00, 0x00, 0x00, 0x7d, 0xe3, 0x20, 0x20, 0x00, + 0x00, 0x04, 0xc0, 0x30, 0x00, 0x00, 0x96, 0xa3, 0xb9, 0x0b, 0x00, 0x32, + 0xb9, 0x02, 0x00, 0x33, 0xa7, 0x84, 0x00, 0x19, 0xa7, 0x3b, 0xff, 0xff, + 0xeb, 0x43, 0x00, 0x08, 0x00, 0x0c, 0xb9, 0x02, 0x00, 0x44, 0xb9, 0x04, + 0x00, 0x12, 0xa7, 0x84, 0x00, 0x09, 0xd7, 0xff, 0x10, 0x00, 0x10, 0x00, + 0x41, 0x10, 0x11, 0x00, 0xa7, 0x47, 0xff, 0xfb, 0xc0, 0x20, 0x00, 0x00, + 0x00, 0x0d, 0x44, 0x30, 0x20, 0x00, 0xc0, 0x20, 0x00, 0x00, 0x00, 0x5b, + 0xd2, 0x0f, 0x01, 0xd0, 0x20, 0x00, 0xa7, 0xf4, 0xff, 0xa1, 0xd7, 0x00, + 0x10, 0x00, 0x10, 0x00, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x50, 0xb2, 0xb2, + 0x10, 0x00, 0xa7, 0xf4, 0x00, 0x00, 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x25, + 0x96, 0x02, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x2f, 0xc0, 0x10, + 0x00, 0x00, 0x00, 0x2a, 0xe3, 0x10, 0x01, 0xb8, 0x00, 0x24, 0xc0, 0x10, + 0x00, 0x00, 0x00, 0x4b, 0xd2, 0x07, 0x01, 0xb0, 0x10, 0x00, 0xc0, 0x10, + 0x00, 0x00, 0x00, 0x3d, 0xb2, 0xb2, 0x10, 0x00, 0xeb, 0x66, 0xf0, 0x00, + 0x00, 0x25, 0x96, 0xff, 0xf0, 0x04, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x2f, + 0xc0, 0x10, 0x00, 0x00, 0x00, 0x1a, 0xe3, 0x10, 0x01, 0xf8, 0x00, 0x24, + 0xc0, 0x10, 0x00, 0x00, 0x00, 0x36, 0xd2, 0x07, 0x01, 0xf0, 0x10, 0x00, + 0xc0, 0x10, 0x00, 0x00, 0x00, 0x24, 0xb2, 0xb2, 0x10, 0x00, 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x25, 0x94, 0xfd, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00, - 0x00, 0x2f, 0x07, 0xfe, 0x07, 0x07, 0x07, 0x07, 0x00, 0x02, 0x00, 0x01, + 0x00, 0x2f, 0x07, 0xfe, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x25, 0x94, 0x00, + 0xf0, 0x04, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x2f, 0x07, 0xfe, 0x07, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf0, 0x00, 0x02, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, - 0xeb, 0xbf, 0xf0, 0x58, 0x00, 0x24, 0xc0, 0x10, 0x00, 0x00, 0x0e, 0x59, - 0xa7, 0xfb, 0xff, 0x60, 0xb2, 0x20, 0x00, 0x21, 0xb2, 0x22, 0x00, 0xb0, - 0x88, 0xb0, 0x00, 0x1c, 0xc0, 0xe5, 0xff, 0xff, 0xff, 0xba, 0xa7, 0xbe, - 0x00, 0x03, 0xa7, 0x84, 0x00, 0x13, 0xa7, 0xbe, 0x00, 0x02, 0xa7, 0x28, - 0x00, 0x00, 0xa7, 0x74, 0x00, 0x04, 0xa7, 0x28, 0xff, 0xfe, 0xe3, 0x40, - 0xf1, 0x10, 0x00, 0x04, 0xb9, 0x14, 0x00, 0x22, 0xeb, 0xbf, 0xf0, 0xf8, - 0x00, 0x04, 0x07, 0xf4, 0xa7, 0x28, 0xff, 0xff, 0xa7, 0xf4, 0xff, 0xf5, - 0x07, 0x07, 0x07, 0x07, 0xeb, 0xbf, 0xf0, 0x58, 0x00, 0x24, 0xc0, 0xd0, - 0x00, 0x00, 0x01, 0x21, 0xa7, 0xfb, 0xff, 0x60, 0xa7, 0xb9, 0x00, 0x00, - 0xa7, 0x19, 0x00, 0x00, 0xc0, 0x40, 0x00, 0x00, 0x0e, 0x24, 0xa7, 0x3b, - 0x00, 0x01, 0xa7, 0x37, 0x00, 0x23, 0xc0, 0x20, 0x00, 0x00, 0x0e, 0x1d, - 0x18, 0x31, 0xa7, 0x1a, 0x00, 0x06, 0x40, 0x10, 0x20, 0x08, 0xa7, 0x3a, - 0x00, 0x0e, 0xa7, 0x18, 0x1a, 0x00, 0x40, 0x30, 0x20, 0x00, 0x92, 0x00, - 0x20, 0x02, 0x40, 0x10, 0x20, 0x0a, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, - 0xc0, 0xe5, 0xff, 0xff, 0xff, 0xac, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, - 0xb9, 0x04, 0x00, 0x2b, 0xeb, 0xbf, 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, - 0xb9, 0x04, 0x00, 0x51, 0xa7, 0x5b, 0x00, 0x01, 0xa7, 0x09, 0x0f, 0xf7, - 0xb9, 0x21, 0x00, 0x50, 0xa7, 0x24, 0xff, 0xd7, 0x41, 0xeb, 0x20, 0x00, - 0x95, 0x0a, 0xe0, 0x00, 0xa7, 0x74, 0x00, 0x08, 0x41, 0x11, 0x40, 0x0e, - 0x92, 0x0d, 0x10, 0x00, 0xb9, 0x04, 0x00, 0x15, 0x43, 0x5b, 0x20, 0x00, - 0x42, 0x51, 0x40, 0x0e, 0xa7, 0xbb, 0x00, 0x01, 0x41, 0x10, 0x10, 0x01, - 0xa7, 0xf4, 0xff, 0xbf, 0xc0, 0x50, 0x00, 0x00, 0x00, 0xd4, 0xc0, 0x10, - 0x00, 0x00, 0x0d, 0xd9, 0xa7, 0x48, 0x00, 0x1c, 0x40, 0x40, 0x10, 0x00, - 0x50, 0x20, 0x10, 0x0c, 0xa7, 0x48, 0x00, 0x04, 0xe3, 0x20, 0x50, 0x00, - 0x00, 0x04, 0x40, 0x40, 0x10, 0x0a, 0x50, 0x30, 0x10, 0x10, 0xc0, 0xf4, - 0xff, 0xff, 0xff, 0x6b, 0xa7, 0x39, 0x00, 0x40, 0xa7, 0x29, 0x00, 0x00, - 0xc0, 0xf4, 0xff, 0xff, 0xff, 0xe4, 0x07, 0x07, 0xb9, 0x04, 0x00, 0x13, - 0xa7, 0x2a, 0xff, 0xff, 0xb9, 0x04, 0x00, 0x34, 0xa7, 0x48, 0x00, 0x01, - 0x15, 0x24, 0xa7, 0x24, 0x00, 0x07, 0xb9, 0x04, 0x00, 0x21, 0xc0, 0xf4, - 0xff, 0xff, 0xff, 0x7f, 0xa7, 0x29, 0xff, 0xff, 0x07, 0xfe, 0x07, 0x07, - 0xa7, 0x39, 0x00, 0x00, 0x41, 0x13, 0x20, 0x00, 0x95, 0x00, 0x10, 0x00, - 0xa7, 0x74, 0x00, 0x05, 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x70, 0xa7, 0x3b, - 0x00, 0x01, 0xa7, 0xf4, 0xff, 0xf5, 0x07, 0x07, 0xeb, 0xbf, 0xf0, 0x58, - 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x00, 0x91, 0xa7, 0xfb, 0xff, 0x60, - 0xb9, 0x04, 0x00, 0xb2, 0xa7, 0x19, 0x00, 0x20, 0xc0, 0x20, 0x00, 0x00, - 0x0d, 0x8c, 0x92, 0x00, 0x20, 0x00, 0xa7, 0x2b, 0x00, 0x01, 0xa7, 0x17, - 0xff, 0xfc, 0xc0, 0x10, 0x00, 0x00, 0x0d, 0x83, 0xa7, 0x28, 0x00, 0x20, - 0x40, 0x20, 0x10, 0x00, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, - 0xff, 0xff, 0xff, 0x1d, 0x12, 0x22, 0xa7, 0x74, 0x00, 0x17, 0xa7, 0x19, - 0x00, 0x00, 0xc0, 0x40, 0x00, 0x00, 0x00, 0x75, 0xc0, 0x50, 0x00, 0x00, - 0x0d, 0x7a, 0xa7, 0x29, 0x00, 0x08, 0xe3, 0x31, 0x50, 0x00, 0x00, 0x90, - 0x43, 0x33, 0x40, 0x00, 0x42, 0x31, 0xb0, 0x00, 0xa7, 0x1b, 0x00, 0x01, - 0xa7, 0x27, 0xff, 0xf7, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, 0xeb, 0xbf, - 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0xeb, 0xaf, 0xf0, 0x50, 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x00, 0x51, - 0xa7, 0xfb, 0xff, 0x60, 0xa7, 0x19, 0x0f, 0xf8, 0xb9, 0x21, 0x00, 0x31, - 0xb9, 0x04, 0x00, 0xa2, 0xa7, 0xc4, 0x00, 0x2d, 0xa7, 0xb9, 0x0f, 0xf8, - 0xc0, 0x10, 0x00, 0x00, 0x0d, 0x42, 0xa7, 0x28, 0x10, 0x00, 0x40, 0x20, - 0x10, 0x00, 0x92, 0x00, 0x10, 0x02, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, - 0xc0, 0xe5, 0xff, 0xff, 0xfe, 0xda, 0xa7, 0xbb, 0x00, 0x01, 0xa7, 0x19, - 0x00, 0x00, 0xc0, 0x20, 0x00, 0x00, 0x0d, 0x2f, 0xa7, 0xb7, 0x00, 0x17, - 0xc0, 0x10, 0x00, 0x00, 0x0d, 0x2a, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, - 0xe3, 0x20, 0x10, 0x08, 0x00, 0x91, 0xa7, 0x2a, 0xff, 0xf9, 0xb9, 0x14, - 0x00, 0x22, 0xeb, 0xaf, 0xf0, 0xf0, 0x00, 0x04, 0x07, 0xf4, 0xb9, 0x04, - 0x00, 0xb3, 0xa7, 0xf4, 0xff, 0xd5, 0x43, 0x31, 0x20, 0x0f, 0x42, 0x31, - 0xa0, 0x00, 0xa7, 0x1b, 0x00, 0x01, 0xa7, 0xf4, 0xff, 0xe3, 0x07, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x2e, 0x2e, 0x2e, 0x2e, + 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0xeb, 0xbf, 0xf0, 0x58, + 0x00, 0x24, 0xc0, 0x10, 0x00, 0x00, 0x4e, 0x0d, 0xa7, 0xfb, 0xff, 0x60, + 0xb2, 0x20, 0x00, 0x21, 0xb2, 0x22, 0x00, 0xb0, 0x88, 0xb0, 0x00, 0x1c, + 0xc0, 0xe5, 0xff, 0xff, 0xff, 0x91, 0xa7, 0xbe, 0x00, 0x03, 0xa7, 0x84, + 0x00, 0x13, 0xa7, 0xbe, 0x00, 0x02, 0xa7, 0x28, 0x00, 0x00, 0xa7, 0x74, + 0x00, 0x04, 0xa7, 0x28, 0xff, 0xfe, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, + 0xb9, 0x14, 0x00, 0x22, 0xeb, 0xbf, 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, + 0xa7, 0x28, 0xff, 0xff, 0xa7, 0xf4, 0xff, 0xf5, 0x07, 0x07, 0x07, 0x07, + 0xeb, 0xbf, 0xf0, 0x58, 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x01, 0x25, + 0xa7, 0xfb, 0xff, 0x60, 0xa7, 0xb9, 0x00, 0x00, 0xa7, 0x19, 0x00, 0x00, + 0xc0, 0x40, 0x00, 0x00, 0x4d, 0xd8, 0xa7, 0x3b, 0x00, 0x01, 0xa7, 0x37, + 0x00, 0x23, 0xc0, 0x20, 0x00, 0x00, 0x4d, 0xd1, 0x18, 0x31, 0xa7, 0x1a, + 0x00, 0x06, 0x40, 0x10, 0x20, 0x08, 0xa7, 0x3a, 0x00, 0x0e, 0xa7, 0x18, + 0x1a, 0x00, 0x40, 0x30, 0x20, 0x00, 0x92, 0x00, 0x20, 0x02, 0x40, 0x10, + 0x20, 0x0a, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff, + 0xff, 0xac, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, 0xb9, 0x04, 0x00, 0x2b, + 0xeb, 0xbf, 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, 0xb9, 0x04, 0x00, 0x51, + 0xa7, 0x5b, 0x00, 0x01, 0xa7, 0x09, 0x0f, 0xf7, 0xb9, 0x21, 0x00, 0x50, + 0xa7, 0x24, 0xff, 0xd7, 0x41, 0xeb, 0x20, 0x00, 0x95, 0x0a, 0xe0, 0x00, + 0xa7, 0x74, 0x00, 0x08, 0x41, 0x11, 0x40, 0x0e, 0x92, 0x0d, 0x10, 0x00, + 0xb9, 0x04, 0x00, 0x15, 0x43, 0x5b, 0x20, 0x00, 0x42, 0x51, 0x40, 0x0e, + 0xa7, 0xbb, 0x00, 0x01, 0x41, 0x10, 0x10, 0x01, 0xa7, 0xf4, 0xff, 0xbf, + 0xc0, 0x50, 0x00, 0x00, 0x00, 0xd8, 0xc0, 0x10, 0x00, 0x00, 0x4d, 0x8d, + 0xa7, 0x48, 0x00, 0x1c, 0x40, 0x40, 0x10, 0x00, 0x50, 0x20, 0x10, 0x0c, + 0xa7, 0x48, 0x00, 0x04, 0xe3, 0x20, 0x50, 0x00, 0x00, 0x04, 0x40, 0x40, + 0x10, 0x0a, 0x50, 0x30, 0x10, 0x10, 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x6b, + 0xa7, 0x39, 0x00, 0x40, 0xa7, 0x29, 0x00, 0x00, 0xc0, 0xf4, 0xff, 0xff, + 0xff, 0xe4, 0x07, 0x07, 0xb9, 0x04, 0x00, 0x13, 0xa7, 0x2a, 0xff, 0xff, + 0xb9, 0x04, 0x00, 0x34, 0xa7, 0x48, 0x00, 0x01, 0x15, 0x24, 0xa7, 0x24, + 0x00, 0x07, 0xb9, 0x04, 0x00, 0x21, 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x7f, + 0xa7, 0x29, 0xff, 0xff, 0x07, 0xfe, 0x07, 0x07, 0xa7, 0x39, 0x00, 0x00, + 0x41, 0x13, 0x20, 0x00, 0x95, 0x00, 0x10, 0x00, 0xa7, 0x74, 0x00, 0x05, + 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x70, 0xa7, 0x3b, 0x00, 0x01, 0xa7, 0xf4, + 0xff, 0xf5, 0x07, 0x07, 0xeb, 0xbf, 0xf0, 0x58, 0x00, 0x24, 0xc0, 0xd0, + 0x00, 0x00, 0x00, 0x95, 0xa7, 0xfb, 0xff, 0x60, 0xb9, 0x04, 0x00, 0xb2, + 0xa7, 0x19, 0x00, 0x20, 0xc0, 0x20, 0x00, 0x00, 0x4d, 0x40, 0x92, 0x00, + 0x20, 0x00, 0xa7, 0x2b, 0x00, 0x01, 0xa7, 0x17, 0xff, 0xfc, 0xc0, 0x10, + 0x00, 0x00, 0x4d, 0x37, 0xa7, 0x28, 0x10, 0x00, 0x40, 0x20, 0x10, 0x00, + 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff, 0xff, 0x1d, + 0x12, 0x22, 0xa7, 0x74, 0x00, 0x19, 0xa7, 0x19, 0x00, 0x00, 0xc0, 0x40, + 0x00, 0x00, 0x00, 0x79, 0xa7, 0x39, 0x00, 0x08, 0xc0, 0x20, 0x00, 0x00, + 0x4d, 0x2c, 0x41, 0x21, 0x20, 0x00, 0xe3, 0x20, 0x20, 0x00, 0x00, 0x90, + 0x43, 0x22, 0x40, 0x00, 0x42, 0x21, 0xb0, 0x00, 0xa7, 0x1b, 0x00, 0x01, + 0xa7, 0x37, 0xff, 0xf2, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, 0xeb, 0xbf, + 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, 0x07, 0x07, 0xeb, 0xaf, 0xf0, 0x50, + 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x00, 0x55, 0xa7, 0xfb, 0xff, 0x60, + 0xa7, 0x19, 0x0f, 0xf8, 0xb9, 0x21, 0x00, 0x31, 0xb9, 0x04, 0x00, 0xa2, + 0xa7, 0xc4, 0x00, 0x2a, 0xa7, 0xb9, 0x0f, 0xf8, 0xc0, 0x10, 0x00, 0x00, + 0x4c, 0xf6, 0xa7, 0x28, 0x10, 0x00, 0x40, 0x20, 0x10, 0x00, 0x92, 0x00, + 0x10, 0x02, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff, + 0xfe, 0xda, 0xa7, 0xbb, 0x00, 0x01, 0xa7, 0x19, 0x00, 0x00, 0xa7, 0xb7, + 0x00, 0x17, 0xc0, 0x10, 0x00, 0x00, 0x4c, 0xe1, 0xe3, 0x40, 0xf1, 0x10, + 0x00, 0x04, 0xe3, 0x20, 0x10, 0x08, 0x00, 0x91, 0xa7, 0x2a, 0xff, 0xf9, + 0xb9, 0x14, 0x00, 0x22, 0xeb, 0xaf, 0xf0, 0xf0, 0x00, 0x04, 0x07, 0xf4, + 0xb9, 0x04, 0x00, 0xb3, 0xa7, 0xf4, 0xff, 0xd8, 0xc0, 0x20, 0x00, 0x00, + 0x4c, 0xcc, 0x41, 0x31, 0xa0, 0x00, 0x41, 0x21, 0x20, 0x00, 0xa7, 0x1b, + 0x00, 0x01, 0xd2, 0x00, 0x30, 0x00, 0x20, 0x0f, 0xa7, 0xf4, 0xff, 0xdd, + 0x07, 0x07, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x20, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x3c, 0x28, 0x2b, 0x7c, 0x26, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x2e, 0x2d, 0x2f, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x60, 0x3a, 0x23, - 0x40, 0x27, 0x3d, 0x22, 0x2e, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x6a, 0x6b, 0x6c, - 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x20, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, 0x26, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x2e, + 0x2d, 0x2f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2c, + 0x25, 0x5f, 0x3e, 0x3f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, 0x2e, 0x61, 0x62, 0x63, + 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x41, 0x42, 0x43, - 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x30, 0x31, 0x32, 0x33, - 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x41, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2e, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x41, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xfe, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, @@ -163,7 +177,15 @@ unsigned char s390x_elf[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x6f, 0xff, 0xff, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xfb, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x6f, 0xff, 0xff, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 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, @@ -171,83 +193,87 @@ unsigned char s390x_elf[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x38, - 0x2e, 0x32, 0x2e, 0x31, 0x20, 0x32, 0x30, 0x31, 0x38, 0x30, 0x39, 0x30, - 0x35, 0x20, 0x28, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, 0x38, - 0x2e, 0x32, 0x2e, 0x31, 0x2d, 0x33, 0x29, 0x00, 0x00, 0x2e, 0x73, 0x68, - 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x70, 0x00, 0x2e, 0x67, 0x6e, 0x75, 0x2e, 0x68, 0x61, 0x73, 0x68, - 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x79, 0x6d, 0x00, 0x2e, 0x64, 0x79, - 0x6e, 0x73, 0x74, 0x72, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, - 0x72, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x61, - 0x6d, 0x69, 0x63, 0x00, 0x2e, 0x67, 0x6f, 0x74, 0x00, 0x2e, 0x62, 0x73, - 0x73, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, + 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, + 0x20, 0x28, 0x55, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x20, 0x31, 0x31, 0x2e, + 0x34, 0x2e, 0x30, 0x2d, 0x31, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x31, + 0x7e, 0x32, 0x32, 0x2e, 0x30, 0x34, 0x29, 0x20, 0x31, 0x31, 0x2e, 0x34, + 0x2e, 0x30, 0x00, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, + 0x62, 0x00, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x00, 0x2e, 0x67, + 0x6e, 0x75, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, 0x64, 0x79, 0x6e, + 0x73, 0x79, 0x6d, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00, + 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x2e, 0x64, 0x79, 0x6e, 0x00, 0x2e, 0x74, + 0x65, 0x78, 0x74, 0x00, 0x2e, 0x72, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x00, + 0x2e, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x00, 0x2e, 0x67, 0x6f, + 0x74, 0x00, 0x2e, 0x62, 0x73, 0x73, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x13, 0x6f, 0xff, 0xff, 0xf6, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x0b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x25, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x05, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xe8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, - 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x24, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x6f, 0xff, 0xff, 0xf6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x37, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4e, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xd8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x09, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/tests/migration/stress.c b/tests/migration/stress.c index b7240a15c8..88acf8dc25 100644 --- a/tests/migration/stress.c +++ b/tests/migration/stress.c @@ -232,7 +232,7 @@ static void stress(unsigned long long ramsizeGB, int ncpus) static int mount_misc(const char *fstype, const char *dir) { - if (mkdir(dir, 0755) < 0 && errno != EEXIST) { + if (g_mkdir_with_parents(dir, 0755) < 0 && errno != EEXIST) { fprintf(stderr, "%s (%05d): ERROR: cannot create %s: %s\n", argv0, gettid(), dir, strerror(errno)); return -1; diff --git a/tests/plugin/bb.c b/tests/plugin/bb.c index 7d470a1011..36776dee1e 100644 --- a/tests/plugin/bb.c +++ b/tests/plugin/bb.c @@ -17,27 +17,25 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; typedef struct { - GMutex lock; - int index; uint64_t bb_count; uint64_t insn_count; } CPUCount; -/* Used by the inline & linux-user counts */ -static bool do_inline; -static CPUCount inline_count; +static struct qemu_plugin_scoreboard *counts; +static qemu_plugin_u64 bb_count; +static qemu_plugin_u64 insn_count; +static bool do_inline; /* Dump running CPU total on idle? */ static bool idle_report; -static GPtrArray *counts; -static int max_cpus; -static void gen_one_cpu_report(CPUCount *count, GString *report) +static void gen_one_cpu_report(CPUCount *count, GString *report, + unsigned int cpu_index) { if (count->bb_count) { g_string_append_printf(report, "CPU%d: " "bb's: %" PRIu64", insns: %" PRIu64 "\n", - count->index, + cpu_index, count->bb_count, count->insn_count); } } @@ -46,20 +44,23 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) { g_autoptr(GString) report = g_string_new(""); - if (do_inline || !max_cpus) { - g_string_printf(report, "bb's: %" PRIu64", insns: %" PRIu64 "\n", - inline_count.bb_count, inline_count.insn_count); - } else { - g_ptr_array_foreach(counts, (GFunc) gen_one_cpu_report, report); + for (int i = 0; i < qemu_plugin_num_vcpus(); ++i) { + CPUCount *count = qemu_plugin_scoreboard_find(counts, i); + gen_one_cpu_report(count, report, i); } + g_string_append_printf(report, "Total: " + "bb's: %" PRIu64", insns: %" PRIu64 "\n", + qemu_plugin_u64_sum(bb_count), + qemu_plugin_u64_sum(insn_count)); qemu_plugin_outs(report->str); + qemu_plugin_scoreboard_free(counts); } static void vcpu_idle(qemu_plugin_id_t id, unsigned int cpu_index) { - CPUCount *count = g_ptr_array_index(counts, cpu_index); + CPUCount *count = qemu_plugin_scoreboard_find(counts, cpu_index); g_autoptr(GString) report = g_string_new(""); - gen_one_cpu_report(count, report); + gen_one_cpu_report(count, report, cpu_index); if (report->len > 0) { g_string_prepend(report, "Idling "); @@ -69,14 +70,11 @@ static void vcpu_idle(qemu_plugin_id_t id, unsigned int cpu_index) static void vcpu_tb_exec(unsigned int cpu_index, void *udata) { - CPUCount *count = max_cpus ? - g_ptr_array_index(counts, cpu_index) : &inline_count; + CPUCount *count = qemu_plugin_scoreboard_find(counts, cpu_index); uintptr_t n_insns = (uintptr_t)udata; - g_mutex_lock(&count->lock); count->insn_count += n_insns; count->bb_count++; - g_mutex_unlock(&count->lock); } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) @@ -84,11 +82,10 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) size_t n_insns = qemu_plugin_tb_n_insns(tb); if (do_inline) { - qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64, - &inline_count.bb_count, 1); - qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64, - &inline_count.insn_count, - n_insns); + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, bb_count, 1); + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, n_insns); } else { qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, @@ -104,7 +101,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, for (i = 0; i < argc; i++) { char *opt = argv[i]; - g_autofree char **tokens = g_strsplit(opt, "=", 2); + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); if (g_strcmp0(tokens[0], "inline") == 0) { if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) { fprintf(stderr, "boolean argument parsing failed: %s\n", opt); @@ -121,18 +118,10 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, } } - if (info->system_emulation && !do_inline) { - max_cpus = info->system.max_vcpus; - counts = g_ptr_array_new(); - for (i = 0; i < max_cpus; i++) { - CPUCount *count = g_new0(CPUCount, 1); - g_mutex_init(&count->lock); - count->index = i; - g_ptr_array_add(counts, count); - } - } else if (!do_inline) { - g_mutex_init(&inline_count.lock); - } + counts = qemu_plugin_scoreboard_new(sizeof(CPUCount)); + bb_count = qemu_plugin_scoreboard_u64_in_struct(counts, CPUCount, bb_count); + insn_count = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, insn_count); if (idle_report) { qemu_plugin_register_vcpu_idle_cb(id, vcpu_idle); diff --git a/tests/plugin/inline.c b/tests/plugin/inline.c new file mode 100644 index 0000000000..0163e9b51c --- /dev/null +++ b/tests/plugin/inline.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2023, Pierrick Bouvier + * + * Demonstrates and tests usage of inline ops. + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include +#include + +#include + +typedef struct { + uint64_t count_tb; + uint64_t count_tb_inline; + uint64_t count_insn; + uint64_t count_insn_inline; + uint64_t count_mem; + uint64_t count_mem_inline; +} CPUCount; + +static struct qemu_plugin_scoreboard *counts; +static qemu_plugin_u64 count_tb; +static qemu_plugin_u64 count_tb_inline; +static qemu_plugin_u64 count_insn; +static qemu_plugin_u64 count_insn_inline; +static qemu_plugin_u64 count_mem; +static qemu_plugin_u64 count_mem_inline; + +static uint64_t global_count_tb; +static uint64_t global_count_insn; +static uint64_t global_count_mem; +static unsigned int max_cpu_index; +static GMutex tb_lock; +static GMutex insn_lock; +static GMutex mem_lock; + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +static void stats_insn(void) +{ + const uint64_t expected = global_count_insn; + const uint64_t per_vcpu = qemu_plugin_u64_sum(count_insn); + const uint64_t inl_per_vcpu = + qemu_plugin_u64_sum(count_insn_inline); + printf("insn: %" PRIu64 "\n", expected); + printf("insn: %" PRIu64 " (per vcpu)\n", per_vcpu); + printf("insn: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + g_assert(expected > 0); + g_assert(per_vcpu == expected); + g_assert(inl_per_vcpu == expected); +} + +static void stats_tb(void) +{ + const uint64_t expected = global_count_tb; + const uint64_t per_vcpu = qemu_plugin_u64_sum(count_tb); + const uint64_t inl_per_vcpu = + qemu_plugin_u64_sum(count_tb_inline); + printf("tb: %" PRIu64 "\n", expected); + printf("tb: %" PRIu64 " (per vcpu)\n", per_vcpu); + printf("tb: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + g_assert(expected > 0); + g_assert(per_vcpu == expected); + g_assert(inl_per_vcpu == expected); +} + +static void stats_mem(void) +{ + const uint64_t expected = global_count_mem; + const uint64_t per_vcpu = qemu_plugin_u64_sum(count_mem); + const uint64_t inl_per_vcpu = + qemu_plugin_u64_sum(count_mem_inline); + printf("mem: %" PRIu64 "\n", expected); + printf("mem: %" PRIu64 " (per vcpu)\n", per_vcpu); + printf("mem: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + g_assert(expected > 0); + g_assert(per_vcpu == expected); + g_assert(inl_per_vcpu == expected); +} + +static void plugin_exit(qemu_plugin_id_t id, void *udata) +{ + const unsigned int num_cpus = qemu_plugin_num_vcpus(); + g_assert(num_cpus == max_cpu_index + 1); + + for (int i = 0; i < num_cpus ; ++i) { + const uint64_t tb = qemu_plugin_u64_get(count_tb, i); + const uint64_t tb_inline = qemu_plugin_u64_get(count_tb_inline, i); + const uint64_t insn = qemu_plugin_u64_get(count_insn, i); + const uint64_t insn_inline = qemu_plugin_u64_get(count_insn_inline, i); + const uint64_t mem = qemu_plugin_u64_get(count_mem, i); + const uint64_t mem_inline = qemu_plugin_u64_get(count_mem_inline, i); + printf("cpu %d: tb (%" PRIu64 ", %" PRIu64 ") | " + "insn (%" PRIu64 ", %" PRIu64 ") | " + "mem (%" PRIu64 ", %" PRIu64 ")" + "\n", + i, tb, tb_inline, insn, insn_inline, mem, mem_inline); + g_assert(tb == tb_inline); + g_assert(insn == insn_inline); + g_assert(mem == mem_inline); + } + + stats_tb(); + stats_insn(); + stats_mem(); + + qemu_plugin_scoreboard_free(counts); +} + +static void vcpu_tb_exec(unsigned int cpu_index, void *udata) +{ + qemu_plugin_u64_add(count_tb, cpu_index, 1); + g_mutex_lock(&tb_lock); + max_cpu_index = MAX(max_cpu_index, cpu_index); + global_count_tb++; + g_mutex_unlock(&tb_lock); +} + +static void vcpu_insn_exec(unsigned int cpu_index, void *udata) +{ + qemu_plugin_u64_add(count_insn, cpu_index, 1); + g_mutex_lock(&insn_lock); + global_count_insn++; + g_mutex_unlock(&insn_lock); +} + +static void vcpu_mem_access(unsigned int cpu_index, + qemu_plugin_meminfo_t info, + uint64_t vaddr, + void *userdata) +{ + qemu_plugin_u64_add(count_mem, cpu_index, 1); + g_mutex_lock(&mem_lock); + global_count_mem++; + g_mutex_unlock(&mem_lock); +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + qemu_plugin_register_vcpu_tb_exec_cb( + tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, 0); + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, count_tb_inline, 1); + + for (int idx = 0; idx < qemu_plugin_tb_n_insns(tb); ++idx) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, idx); + qemu_plugin_register_vcpu_insn_exec_cb( + insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, 0); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_ADD_U64, count_insn_inline, 1); + qemu_plugin_register_vcpu_mem_cb(insn, &vcpu_mem_access, + QEMU_PLUGIN_CB_NO_REGS, + QEMU_PLUGIN_MEM_RW, 0); + qemu_plugin_register_vcpu_mem_inline_per_vcpu( + insn, QEMU_PLUGIN_MEM_RW, + QEMU_PLUGIN_INLINE_ADD_U64, + count_mem_inline, 1); + } +} + +QEMU_PLUGIN_EXPORT +int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, + int argc, char **argv) +{ + counts = qemu_plugin_scoreboard_new(sizeof(CPUCount)); + count_tb = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, count_tb); + count_insn = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, count_insn); + count_mem = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, count_mem); + count_tb_inline = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, count_tb_inline); + count_insn_inline = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, count_insn_inline); + count_mem_inline = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, count_mem_inline); + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); + qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); + + return 0; +} diff --git a/tests/plugin/insn.c b/tests/plugin/insn.c index cd5ea5d4ae..5e0aa03223 100644 --- a/tests/plugin/insn.c +++ b/tests/plugin/insn.c @@ -16,26 +16,21 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; -#define MAX_CPUS 8 /* lets not go nuts */ - -typedef struct { - uint64_t last_pc; - uint64_t insn_count; -} InstructionCount; - -static InstructionCount counts[MAX_CPUS]; -static uint64_t inline_insn_count; +static qemu_plugin_u64 insn_count; static bool do_inline; static bool do_size; static GArray *sizes; +typedef struct { + uint64_t hits; + uint64_t last_hit; + uint64_t total_delta; +} MatchCount; + typedef struct { char *match_string; - uint64_t hits[MAX_CPUS]; - uint64_t last_hit[MAX_CPUS]; - uint64_t total_delta[MAX_CPUS]; - GPtrArray *history[MAX_CPUS]; + struct qemu_plugin_scoreboard *counts; /* MatchCount */ } Match; static GArray *matches; @@ -47,49 +42,61 @@ typedef struct { char *disas; } Instruction; +/* + * Initialise a new vcpu with reading the register list + */ +static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) +{ + g_autoptr(GArray) reg_list = qemu_plugin_get_registers(); + g_autoptr(GByteArray) reg_value = g_byte_array_new(); + + if (reg_list) { + for (int i = 0; i < reg_list->len; i++) { + qemu_plugin_reg_descriptor *rd = &g_array_index( + reg_list, qemu_plugin_reg_descriptor, i); + int count = qemu_plugin_read_register(rd->handle, reg_value); + g_assert(count > 0); + } + } +} + + static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata) { - unsigned int i = cpu_index % MAX_CPUS; - InstructionCount *c = &counts[i]; - uint64_t this_pc = GPOINTER_TO_UINT(udata); - if (this_pc == c->last_pc) { - g_autofree gchar *out = g_strdup_printf("detected repeat execution @ 0x%" - PRIx64 "\n", this_pc); - qemu_plugin_outs(out); - } - c->last_pc = this_pc; - c->insn_count++; + qemu_plugin_u64_add(insn_count, cpu_index, 1); } static void vcpu_insn_matched_exec_before(unsigned int cpu_index, void *udata) { - unsigned int i = cpu_index % MAX_CPUS; Instruction *insn = (Instruction *) udata; - Match *match = insn->match; + Match *insn_match = insn->match; + MatchCount *match = qemu_plugin_scoreboard_find(insn_match->counts, + cpu_index); + g_autoptr(GString) ts = g_string_new(""); insn->hits++; g_string_append_printf(ts, "0x%" PRIx64 ", '%s', %"PRId64 " hits", insn->vaddr, insn->disas, insn->hits); - uint64_t icount = counts[i].insn_count; - uint64_t delta = icount - match->last_hit[i]; + uint64_t icount = qemu_plugin_u64_get(insn_count, cpu_index); + uint64_t delta = icount - match->last_hit; - match->hits[i]++; - match->total_delta[i] += delta; + match->hits++; + match->total_delta += delta; g_string_append_printf(ts, - ", %"PRId64" match hits, " - "Δ+%"PRId64 " since last match," + " , cpu %u," + " %"PRId64" match hits," + " Δ+%"PRId64 " since last match," " %"PRId64 " avg insns/match\n", - match->hits[i], delta, - match->total_delta[i] / match->hits[i]); + cpu_index, + match->hits, delta, + match->total_delta / match->hits); - match->last_hit[i] = icount; + match->last_hit = icount; qemu_plugin_outs(ts->str); - - g_ptr_array_add(match->history[i], insn); } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) @@ -101,8 +108,8 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); if (do_inline) { - qemu_plugin_register_vcpu_insn_exec_inline( - insn, QEMU_PLUGIN_INLINE_ADD_U64, &inline_insn_count, 1); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1); } else { uint64_t vaddr = qemu_plugin_insn_vaddr(insn); qemu_plugin_register_vcpu_insn_exec_cb( @@ -124,10 +131,9 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) * information about the instruction which we also need to * save if there is a hit. */ - if (matches) { + if (matches->len) { char *insn_disas = qemu_plugin_insn_disas(insn); - int j; - for (j = 0; j < matches->len; j++) { + for (int j = 0; j < matches->len; j++) { Match *m = &g_array_index(matches, Match, j); if (g_str_has_prefix(insn_disas, m->match_string)) { Instruction *rec = g_new0(Instruction, 1); @@ -157,36 +163,33 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) "len %d bytes: %ld insns\n", i, *cnt); } } - } else if (do_inline) { - g_string_append_printf(out, "insns: %" PRIu64 "\n", inline_insn_count); } else { - uint64_t total_insns = 0; - for (i = 0; i < MAX_CPUS; i++) { - InstructionCount *c = &counts[i]; - if (c->insn_count) { - g_string_append_printf(out, "cpu %d insns: %" PRIu64 "\n", - i, c->insn_count); - total_insns += c->insn_count; - } + for (i = 0; i < qemu_plugin_num_vcpus(); i++) { + g_string_append_printf(out, "cpu %d insns: %" PRIu64 "\n", + i, qemu_plugin_u64_get(insn_count, i)); } g_string_append_printf(out, "total insns: %" PRIu64 "\n", - total_insns); + qemu_plugin_u64_sum(insn_count)); } qemu_plugin_outs(out->str); + + qemu_plugin_scoreboard_free(insn_count.score); + for (i = 0; i < matches->len; ++i) { + Match *m = &g_array_index(matches, Match, i); + g_free(m->match_string); + qemu_plugin_scoreboard_free(m->counts); + } + g_array_free(matches, TRUE); + g_array_free(sizes, TRUE); } /* Add a match to the array of matches */ static void parse_match(char *match) { - Match new_match = { .match_string = match }; - int i; - for (i = 0; i < MAX_CPUS; i++) { - new_match.history[i] = g_ptr_array_new(); - } - if (!matches) { - matches = g_array_new(false, true, sizeof(Match)); - } + Match new_match = { + .match_string = g_strdup(match), + .counts = qemu_plugin_scoreboard_new(sizeof(MatchCount)) }; g_array_append_val(matches, new_match); } @@ -194,9 +197,13 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { + matches = g_array_new(false, true, sizeof(Match)); + /* null terminated so 0 is not a special case */ + sizes = g_array_new(true, true, sizeof(unsigned long)); + for (int i = 0; i < argc; i++) { char *opt = argv[i]; - g_autofree char **tokens = g_strsplit(opt, "=", 2); + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); if (g_strcmp0(tokens[0], "inline") == 0) { if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) { fprintf(stderr, "boolean argument parsing failed: %s\n", opt); @@ -215,10 +222,11 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, } } - if (do_size) { - sizes = g_array_new(true, true, sizeof(unsigned long)); - } + insn_count = qemu_plugin_scoreboard_u64( + qemu_plugin_scoreboard_new(sizeof(uint64_t))); + /* Register init, translation block and exit callbacks */ + qemu_plugin_register_vcpu_init_cb(id, vcpu_init); qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); return 0; diff --git a/tests/plugin/mem.c b/tests/plugin/mem.c index 4570f7d815..b650dddcce 100644 --- a/tests/plugin/mem.c +++ b/tests/plugin/mem.c @@ -16,9 +16,14 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; -static uint64_t inline_mem_count; -static uint64_t cb_mem_count; -static uint64_t io_count; +typedef struct { + uint64_t mem_count; + uint64_t io_count; +} CPUCount; + +static struct qemu_plugin_scoreboard *counts; +static qemu_plugin_u64 mem_count; +static qemu_plugin_u64 io_count; static bool do_inline, do_callback; static bool do_haddr; static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW; @@ -27,16 +32,16 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) { g_autoptr(GString) out = g_string_new(""); - if (do_inline) { - g_string_printf(out, "inline mem accesses: %" PRIu64 "\n", inline_mem_count); - } - if (do_callback) { - g_string_append_printf(out, "callback mem accesses: %" PRIu64 "\n", cb_mem_count); + if (do_inline || do_callback) { + g_string_printf(out, "mem accesses: %" PRIu64 "\n", + qemu_plugin_u64_sum(mem_count)); } if (do_haddr) { - g_string_append_printf(out, "io accesses: %" PRIu64 "\n", io_count); + g_string_append_printf(out, "io accesses: %" PRIu64 "\n", + qemu_plugin_u64_sum(io_count)); } qemu_plugin_outs(out->str); + qemu_plugin_scoreboard_free(counts); } static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, @@ -46,12 +51,12 @@ static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, struct qemu_plugin_hwaddr *hwaddr; hwaddr = qemu_plugin_get_hwaddr(meminfo, vaddr); if (qemu_plugin_hwaddr_is_io(hwaddr)) { - io_count++; + qemu_plugin_u64_add(io_count, cpu_index, 1); } else { - cb_mem_count++; + qemu_plugin_u64_add(mem_count, cpu_index, 1); } } else { - cb_mem_count++; + qemu_plugin_u64_add(mem_count, cpu_index, 1); } } @@ -64,9 +69,10 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); if (do_inline) { - qemu_plugin_register_vcpu_mem_inline(insn, rw, - QEMU_PLUGIN_INLINE_ADD_U64, - &inline_mem_count, 1); + qemu_plugin_register_vcpu_mem_inline_per_vcpu( + insn, rw, + QEMU_PLUGIN_INLINE_ADD_U64, + mem_count, 1); } if (do_callback) { qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem, @@ -83,7 +89,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, for (int i = 0; i < argc; i++) { char *opt = argv[i]; - g_autofree char **tokens = g_strsplit(opt, "=", 2); + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); if (g_strcmp0(tokens[0], "haddr") == 0) { if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_haddr)) { @@ -98,7 +104,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, } else if (g_strcmp0(tokens[1], "rw") == 0) { rw = QEMU_PLUGIN_MEM_RW; } else { - fprintf(stderr, "invaild value for argument track: %s\n", opt); + fprintf(stderr, "invalid value for argument track: %s\n", opt); return -1; } } else if (g_strcmp0(tokens[0], "inline") == 0) { @@ -117,6 +123,16 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, } } + if (do_inline && do_callback) { + fprintf(stderr, + "can't enable inline and callback counting at the same time\n"); + return -1; + } + + counts = qemu_plugin_scoreboard_new(sizeof(CPUCount)); + mem_count = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, mem_count); + io_count = qemu_plugin_scoreboard_u64_in_struct(counts, CPUCount, io_count); qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); return 0; diff --git a/tests/plugin/meson.build b/tests/plugin/meson.build index 2bbfc4b19e..9eece5bab5 100644 --- a/tests/plugin/meson.build +++ b/tests/plugin/meson.build @@ -1,7 +1,22 @@ t = [] -foreach i : ['bb', 'empty', 'insn', 'mem', 'syscall'] - t += shared_module(i, files(i + '.c'), - include_directories: '../../include/qemu', - dependencies: glib) -endforeach -alias_target('test-plugins', t) +if get_option('plugins') + foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'syscall'] + if host_os == 'windows' + t += shared_module(i, files(i + '.c') + '../../contrib/plugins/win32_linker.c', + include_directories: '../../include/qemu', + link_depends: [win32_qemu_plugin_api_lib], + link_args: ['-Lplugins', '-lqemu_plugin_api'], + dependencies: glib) + + else + t += shared_module(i, files(i + '.c'), + include_directories: '../../include/qemu', + dependencies: glib) + endif + endforeach +endif +if t.length() > 0 + alias_target('test-plugins', t) +else + run_target('test-plugins', command: find_program('true')) +endif diff --git a/tests/plugin/syscall.c b/tests/plugin/syscall.c index 96040c578f..72e1a5bf90 100644 --- a/tests/plugin/syscall.c +++ b/tests/plugin/syscall.c @@ -121,7 +121,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, for (int i = 0; i < argc; i++) { char *opt = argv[i]; - g_autofree char **tokens = g_strsplit(opt, "=", 2); + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); if (g_strcmp0(tokens[0], "print") == 0) { if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_print)) { diff --git a/tests/qapi-schema/args-if-implicit.err b/tests/qapi-schema/args-if-implicit.err new file mode 100644 index 0000000000..da2447d397 --- /dev/null +++ b/tests/qapi-schema/args-if-implicit.err @@ -0,0 +1,2 @@ +args-if-implicit.json: In command 'test-if-cmd': +args-if-implicit.json:1: conditional command arguments require 'boxed': true diff --git a/tests/qapi-schema/args-if-implicit.json b/tests/qapi-schema/args-if-implicit.json new file mode 100644 index 0000000000..1eda39cb1e --- /dev/null +++ b/tests/qapi-schema/args-if-implicit.json @@ -0,0 +1,4 @@ +{ 'command': 'test-if-cmd', + 'data': { + 'foo': 'int', + 'bar': { 'type': 'str', 'if': 'TEST_IF_CMD_ARG' } } } diff --git a/tests/qapi-schema/args-if-implicit.out b/tests/qapi-schema/args-if-implicit.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/args-if-unboxed.err b/tests/qapi-schema/args-if-unboxed.err new file mode 100644 index 0000000000..3d2fc836ef --- /dev/null +++ b/tests/qapi-schema/args-if-unboxed.err @@ -0,0 +1,2 @@ +args-if-unboxed.json: In command 'test-if-cmd': +args-if-unboxed.json:5: conditional command arguments require 'boxed': true diff --git a/tests/qapi-schema/args-if-unboxed.json b/tests/qapi-schema/args-if-unboxed.json new file mode 100644 index 0000000000..6e04c13e72 --- /dev/null +++ b/tests/qapi-schema/args-if-unboxed.json @@ -0,0 +1,6 @@ +{ 'struct': 'TestIfCmdArgs', + 'data': { + 'foo': 'int', + 'bar': { 'type': 'str', 'if': 'TEST_IF_CMD_ARG' } } } +{ 'command': 'test-if-cmd', + 'data': 'TestIfCmdArgs' } diff --git a/tests/qapi-schema/args-if-unboxed.out b/tests/qapi-schema/args-if-unboxed.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/bad-data.err b/tests/qapi-schema/bad-data.err index 7991c8898d..a987df4108 100644 --- a/tests/qapi-schema/bad-data.err +++ b/tests/qapi-schema/bad-data.err @@ -1,2 +1,2 @@ bad-data.json: In command 'oops': -bad-data.json:2: 'data' cannot be an array +bad-data.json:2: 'data' should be an object or type name diff --git a/tests/qapi-schema/bad-if-not.json b/tests/qapi-schema/bad-if-not.json index 9fdaacc47b..660fc4feb2 100644 --- a/tests/qapi-schema/bad-if-not.json +++ b/tests/qapi-schema/bad-if-not.json @@ -1,3 +1,3 @@ -# check 'if not' with empy argument +# check 'if not' with empty argument { 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, 'if': { 'not': '' } } diff --git a/tests/qapi-schema/doc-bad-alternate-member.err b/tests/qapi-schema/doc-bad-alternate-member.err index d7286bb57c..1f6b7d2fe4 100644 --- a/tests/qapi-schema/doc-bad-alternate-member.err +++ b/tests/qapi-schema/doc-bad-alternate-member.err @@ -1 +1 @@ -doc-bad-alternate-member.json:3: documented members 'aa', 'bb' do not exist +doc-bad-alternate-member.json:7: documented members 'aa', 'bb' do not exist diff --git a/tests/qapi-schema/doc-bad-alternate-member.json b/tests/qapi-schema/doc-bad-alternate-member.json index fa4143da4c..37593b6698 100644 --- a/tests/qapi-schema/doc-bad-alternate-member.json +++ b/tests/qapi-schema/doc-bad-alternate-member.json @@ -2,6 +2,8 @@ ## # @AorB: +# @a: a +# @b: b # @aa: a # @bb: b ## diff --git a/tests/qapi-schema/doc-bad-boxed-command-arg.err b/tests/qapi-schema/doc-bad-boxed-command-arg.err index 7137af3ec9..d6793e7fd8 100644 --- a/tests/qapi-schema/doc-bad-boxed-command-arg.err +++ b/tests/qapi-schema/doc-bad-boxed-command-arg.err @@ -1 +1 @@ -doc-bad-boxed-command-arg.json:9: documented member 'a' does not exist +doc-bad-boxed-command-arg.json:11: documented member 'a' does not exist diff --git a/tests/qapi-schema/doc-bad-command-arg.err b/tests/qapi-schema/doc-bad-command-arg.err index 18ed076cef..b76167ec60 100644 --- a/tests/qapi-schema/doc-bad-command-arg.err +++ b/tests/qapi-schema/doc-bad-command-arg.err @@ -1 +1 @@ -doc-bad-command-arg.json:3: documented member 'b' does not exist +doc-bad-command-arg.json:6: documented member 'b' does not exist diff --git a/tests/qapi-schema/doc-bad-enum-member.err b/tests/qapi-schema/doc-bad-enum-member.err index 7efeb47363..0aa8d8e8e2 100644 --- a/tests/qapi-schema/doc-bad-enum-member.err +++ b/tests/qapi-schema/doc-bad-enum-member.err @@ -1 +1 @@ -doc-bad-enum-member.json:3: documented member 'a' does not exist +doc-bad-enum-member.json:5: documented member 'a' does not exist diff --git a/tests/qapi-schema/doc-bad-event-arg.err b/tests/qapi-schema/doc-bad-event-arg.err index d13cacf21f..90527d5f82 100644 --- a/tests/qapi-schema/doc-bad-event-arg.err +++ b/tests/qapi-schema/doc-bad-event-arg.err @@ -1 +1 @@ -doc-bad-event-arg.json:3: documented member 'a' does not exist +doc-bad-event-arg.json:5: documented member 'a' does not exist diff --git a/tests/qapi-schema/doc-bad-feature.err b/tests/qapi-schema/doc-bad-feature.err index 49d1746c3d..3166c6a305 100644 --- a/tests/qapi-schema/doc-bad-feature.err +++ b/tests/qapi-schema/doc-bad-feature.err @@ -1 +1 @@ -doc-bad-feature.json:3: documented feature 'a' does not exist +doc-bad-feature.json:7: documented feature 'a' does not exist diff --git a/tests/qapi-schema/doc-bad-indent.err b/tests/qapi-schema/doc-bad-indent.err index 67844539bd..3c9699a8e0 100644 --- a/tests/qapi-schema/doc-bad-indent.err +++ b/tests/qapi-schema/doc-bad-indent.err @@ -1 +1 @@ -doc-bad-indent.json:6:1: unexpected de-indent (expected at least 4 spaces) +doc-bad-indent.json:7:1: unexpected de-indent (expected at least 2 spaces) diff --git a/tests/qapi-schema/doc-bad-indent.json b/tests/qapi-schema/doc-bad-indent.json index edde8f21dc..3f22a27717 100644 --- a/tests/qapi-schema/doc-bad-indent.json +++ b/tests/qapi-schema/doc-bad-indent.json @@ -3,6 +3,7 @@ ## # @foo: # @a: line one -# line two is wrongly indented +# line two +# line three is wrongly indented ## { 'command': 'foo', 'data': { 'a': 'int' } } diff --git a/tests/qapi-schema/doc-bad-union-member.err b/tests/qapi-schema/doc-bad-union-member.err index 6dd2726a65..cdf1225cab 100644 --- a/tests/qapi-schema/doc-bad-union-member.err +++ b/tests/qapi-schema/doc-bad-union-member.err @@ -1 +1 @@ -doc-bad-union-member.json:3: documented members 'a', 'b' do not exist +doc-bad-union-member.json:5: documented members 'a', 'b' do not exist diff --git a/tests/qapi-schema/doc-duplicate-features.err b/tests/qapi-schema/doc-duplicate-features.err new file mode 100644 index 0000000000..cadb2957a6 --- /dev/null +++ b/tests/qapi-schema/doc-duplicate-features.err @@ -0,0 +1 @@ +doc-duplicate-features.json:9:1: duplicated 'Features:' line diff --git a/tests/qapi-schema/doc-duplicate-features.json b/tests/qapi-schema/doc-duplicate-features.json new file mode 100644 index 0000000000..a4d559e740 --- /dev/null +++ b/tests/qapi-schema/doc-duplicate-features.json @@ -0,0 +1,11 @@ +# Duplicate 'Features:' line + +## +# @foo: +# +# Features: +# @feat: mumble +# +# Features: +## +{ 'command': 'foo', 'features': ['feat'] } diff --git a/tests/qapi-schema/doc-duplicate-features.out b/tests/qapi-schema/doc-duplicate-features.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/doc-duplicated-arg.err b/tests/qapi-schema/doc-duplicated-arg.err index 0d0d777a1f..d876312734 100644 --- a/tests/qapi-schema/doc-duplicated-arg.err +++ b/tests/qapi-schema/doc-duplicated-arg.err @@ -1 +1 @@ -doc-duplicated-arg.json:6:1: 'a' parameter name duplicated +doc-duplicated-arg.json:6: 'a' parameter name duplicated diff --git a/tests/qapi-schema/doc-duplicated-return.err b/tests/qapi-schema/doc-duplicated-return.err index fe97e3db8d..503b916b25 100644 --- a/tests/qapi-schema/doc-duplicated-return.err +++ b/tests/qapi-schema/doc-duplicated-return.err @@ -1 +1 @@ -doc-duplicated-return.json:7:1: duplicated 'Returns' section +doc-duplicated-return.json:8: duplicated 'Returns' section diff --git a/tests/qapi-schema/doc-duplicated-return.json b/tests/qapi-schema/doc-duplicated-return.json index b44b5ae979..4e1ec2ef42 100644 --- a/tests/qapi-schema/doc-duplicated-return.json +++ b/tests/qapi-schema/doc-duplicated-return.json @@ -4,5 +4,6 @@ # @foo: # # Returns: 0 +# # Returns: 1 ## diff --git a/tests/qapi-schema/doc-duplicated-since.err b/tests/qapi-schema/doc-duplicated-since.err index abca141a2c..a9b60c0c3d 100644 --- a/tests/qapi-schema/doc-duplicated-since.err +++ b/tests/qapi-schema/doc-duplicated-since.err @@ -1 +1 @@ -doc-duplicated-since.json:7:1: duplicated 'Since' section +doc-duplicated-since.json:8: duplicated 'Since' section diff --git a/tests/qapi-schema/doc-duplicated-since.json b/tests/qapi-schema/doc-duplicated-since.json index 343cd872cb..2755f95719 100644 --- a/tests/qapi-schema/doc-duplicated-since.json +++ b/tests/qapi-schema/doc-duplicated-since.json @@ -4,5 +4,6 @@ # @foo: # # Since: 0 +# # Since: 1 ## diff --git a/tests/qapi-schema/doc-empty-arg.err b/tests/qapi-schema/doc-empty-arg.err index 2d0f35f310..83f4fc66d5 100644 --- a/tests/qapi-schema/doc-empty-arg.err +++ b/tests/qapi-schema/doc-empty-arg.err @@ -1 +1 @@ -doc-empty-arg.json:5:1: invalid parameter name +doc-empty-arg.json:5: invalid parameter name diff --git a/tests/qapi-schema/doc-empty-features.err b/tests/qapi-schema/doc-empty-features.err new file mode 100644 index 0000000000..2709a18d8f --- /dev/null +++ b/tests/qapi-schema/doc-empty-features.err @@ -0,0 +1 @@ +doc-empty-features.json:8:1: feature descriptions expected diff --git a/tests/qapi-schema/doc-empty-features.json b/tests/qapi-schema/doc-empty-features.json new file mode 100644 index 0000000000..06f814e45d --- /dev/null +++ b/tests/qapi-schema/doc-empty-features.json @@ -0,0 +1,10 @@ +# 'Features:' line not followed by feature descriptions + +## +# @foo: +# +# Features: +# +# not a description +## +{ 'command': 'foo' } diff --git a/tests/qapi-schema/doc-empty-features.out b/tests/qapi-schema/doc-empty-features.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/doc-empty-section.err b/tests/qapi-schema/doc-empty-section.err index ba7ba70125..5f03a6d733 100644 --- a/tests/qapi-schema/doc-empty-section.err +++ b/tests/qapi-schema/doc-empty-section.err @@ -1 +1 @@ -doc-empty-section.json:7:1: empty doc section 'Note' +doc-empty-section.json:6: text required after 'Note:' diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 74745fb405..de38a386e8 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -3,11 +3,15 @@ # # Positive QAPI doc comment tests -{ 'pragma': { 'doc-required': true } } +{ 'pragma': { + 'doc-required': true, + 'documentation-exceptions': [ 'Enum', 'Variant1', 'Alternate', 'cmd' ] } } ## # = Section -# +## + +## # == Subsection # # *with emphasis* @@ -54,7 +58,7 @@ ## # @Enum: # -# @one: The _one_ {and only} +# @one: The _one_ {and only}, description on the same line # # Features: # @enum-feat: Also _one_ {and only} @@ -73,7 +77,8 @@ # @Base: # # @base1: -# the first member +# description starts on a new line, +# minimally indented ## { 'struct': 'Base', 'data': { 'base1': 'Enum' }, 'if': { 'all': ['IFALL1', 'IFALL2'] } } @@ -83,7 +88,9 @@ # # A paragraph # -# Another paragraph (but no @var: line) +# Another paragraph +# +# @var1 is undocumented # # Features: # @variant1-feat: a feature @@ -118,7 +125,8 @@ ## # @Alternate: # -# @i: an integer +# @i: description starts on the same line +# remainder indented the same # @b is undocumented # # Features: @@ -136,30 +144,41 @@ ## # @cmd: # -# @arg1: the first argument +# @arg1: +# description starts on a new line, +# indented # -# @arg2: the second -# argument +# @arg2: description starts on the same line +# remainder indented differently # # Features: # @cmd-feat1: a feature # @cmd-feat2: another feature +# # Note: @arg3 is undocumented +# # Returns: @Object +# +# Errors: some +# # TODO: frobnicate +# # Notes: # -# - Lorem ipsum dolor sit amet -# - Ut enim ad minim veniam +# - Lorem ipsum dolor sit amet +# - Ut enim ad minim veniam +# +# Duis aute irure dolor # -# Duis aute irure dolor # Example: # -# -> in -# <- out +# -> in +# <- out +# # Examples: -# - *verbatim* -# - {braces} +# - *verbatim* +# - {braces} +# # Since: 2.10 ## { 'command': 'cmd', @@ -170,14 +189,16 @@ ## # @cmd-boxed: # If you're bored enough to read this, go see a video of boxed cats +# # Features: # @cmd-feat1: a feature # @cmd-feat2: another feature +# # Example: # -# -> in +# -> in # -# <- out +# <- out ## { 'command': 'cmd-boxed', 'boxed': true, 'data': 'Object', diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 9dd65b9d92..716a9a4102 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -104,7 +104,7 @@ doc symbol=Enum body= arg=one -The _one_ {and only} +The _one_ {and only}, description on the same line arg=two feature=enum-feat @@ -117,12 +117,15 @@ doc symbol=Base body= arg=base1 -the first member +description starts on a new line, +minimally indented doc symbol=Variant1 body= A paragraph -Another paragraph (but no @var: line) +Another paragraph + +@var1 is undocumented arg=var1 feature=variant1-feat @@ -141,7 +144,8 @@ doc symbol=Alternate body= arg=i -an integer +description starts on the same line +remainder indented the same @b is undocumented arg=b @@ -154,10 +158,11 @@ doc symbol=cmd body= arg=arg1 -the first argument +description starts on a new line, +indented arg=arg2 -the second -argument +description starts on the same line +remainder indented differently arg=arg3 feature=cmd-feat1 @@ -168,6 +173,8 @@ another feature @arg3 is undocumented section=Returns @Object + section=Errors +some section=TODO frobnicate section=Notes diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt index b3b76bd43f..847db70412 100644 --- a/tests/qapi-schema/doc-good.txt +++ b/tests/qapi-schema/doc-good.txt @@ -44,7 +44,7 @@ Values ~~~~~~ "one" (**If: **"IFONE") - The _one_ {and only} + The _one_ {and only}, description on the same line "two" Not documented @@ -76,7 +76,7 @@ Members ~~~~~~~ "base1": "Enum" - the first member + description starts on a new line, minimally indented If @@ -90,7 +90,9 @@ If A paragraph -Another paragraph (but no "var": line) +Another paragraph + +"var1" is undocumented Members @@ -141,7 +143,8 @@ Members ~~~~~~~ "i": "int" - an integer "b" is undocumented + description starts on the same line remainder indented the same "b" + is undocumented "b": "boolean" Not documented @@ -172,10 +175,10 @@ Arguments ~~~~~~~~~ "arg1": "int" - the first argument + description starts on a new line, indented "arg2": "string" (optional) - the second argument + description starts on the same line remainder indented differently "arg3": "boolean" Not documented @@ -203,10 +206,10 @@ Returns "Object" -TODO -~~~~ +Errors +~~~~~~ -frobnicate +some Notes diff --git a/tests/qapi-schema/doc-interleaved-section.err b/tests/qapi-schema/doc-interleaved-section.err index 715d58cd31..e5d1ef54c1 100644 --- a/tests/qapi-schema/doc-interleaved-section.err +++ b/tests/qapi-schema/doc-interleaved-section.err @@ -1 +1 @@ -doc-interleaved-section.json:15:1: '@foobar:' can't follow 'Note' section +doc-interleaved-section.json:15:1: description of '@foobar:' follows a section diff --git a/tests/qapi-schema/doc-invalid-return.err b/tests/qapi-schema/doc-invalid-return.err index 2ad89c5941..aafd57b135 100644 --- a/tests/qapi-schema/doc-invalid-return.err +++ b/tests/qapi-schema/doc-invalid-return.err @@ -1 +1 @@ -doc-invalid-return.json:3: 'Returns:' is only valid for commands +doc-invalid-return.json:6: 'Returns' section is only valid for commands diff --git a/tests/qapi-schema/doc-invalid-return.json b/tests/qapi-schema/doc-invalid-return.json index 95e7583930..1aabef3482 100644 --- a/tests/qapi-schema/doc-invalid-return.json +++ b/tests/qapi-schema/doc-invalid-return.json @@ -2,6 +2,7 @@ ## # @FOO: +# # Returns: blah ## { 'event': 'FOO' } diff --git a/tests/qapi-schema/doc-invalid-return2.err b/tests/qapi-schema/doc-invalid-return2.err new file mode 100644 index 0000000000..c3d0c7a452 --- /dev/null +++ b/tests/qapi-schema/doc-invalid-return2.err @@ -0,0 +1 @@ +doc-invalid-return2.json:5: 'Returns' section, but command doesn't return anything diff --git a/tests/qapi-schema/doc-invalid-return2.json b/tests/qapi-schema/doc-invalid-return2.json new file mode 100644 index 0000000000..37883d4fea --- /dev/null +++ b/tests/qapi-schema/doc-invalid-return2.json @@ -0,0 +1,7 @@ +# Command doesn't return anything + +## +# @foo: +# Returns: blah +## +{ 'command': 'foo' } diff --git a/tests/qapi-schema/doc-invalid-return2.out b/tests/qapi-schema/doc-invalid-return2.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/doc-non-first-section.err b/tests/qapi-schema/doc-non-first-section.err new file mode 100644 index 0000000000..eeced2bca7 --- /dev/null +++ b/tests/qapi-schema/doc-non-first-section.err @@ -0,0 +1 @@ +doc-non-first-section.json:5:1: '=' heading must come first in a comment block diff --git a/tests/qapi-schema/doc-non-first-section.json b/tests/qapi-schema/doc-non-first-section.json new file mode 100644 index 0000000000..1590876061 --- /dev/null +++ b/tests/qapi-schema/doc-non-first-section.json @@ -0,0 +1,6 @@ +# = section must be first line + +## +# +# = Not first +## diff --git a/tests/qapi-schema/doc-non-first-section.out b/tests/qapi-schema/doc-non-first-section.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/event-args-if-unboxed.err b/tests/qapi-schema/event-args-if-unboxed.err new file mode 100644 index 0000000000..41ac64c6f3 --- /dev/null +++ b/tests/qapi-schema/event-args-if-unboxed.err @@ -0,0 +1,2 @@ +tests/qapi-schema/event-args-if-unboxed.json: In event 'TEST_IF_EVENT': +tests/qapi-schema/event-args-if-unboxed.json:1: event's 'data' members may have 'if' conditions only with 'boxed': true diff --git a/tests/qapi-schema/event-args-if-unboxed.json b/tests/qapi-schema/event-args-if-unboxed.json new file mode 100644 index 0000000000..ca42a74e3a --- /dev/null +++ b/tests/qapi-schema/event-args-if-unboxed.json @@ -0,0 +1,4 @@ + { 'event': 'TEST_IF_EVENT', + 'data': { + 'foo': 'int', + 'bar': { 'type': 'str', 'if': 'TEST_IF_CMD_ARG' } } } diff --git a/tests/qapi-schema/event-args-if-unboxed.out b/tests/qapi-schema/event-args-if-unboxed.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/event-nest-struct.err b/tests/qapi-schema/event-nest-struct.err index 8c5f6ed311..15fc1406f8 100644 --- a/tests/qapi-schema/event-nest-struct.err +++ b/tests/qapi-schema/event-nest-struct.err @@ -1,2 +1,2 @@ event-nest-struct.json: In event 'EVENT_A': -event-nest-struct.json:1: 'data' member 'a' should be a type name +event-nest-struct.json:1: 'data' member 'a' should be a type name or array diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build index c18dd7d02f..0f479d9317 100644 --- a/tests/qapi-schema/meson.build +++ b/tests/qapi-schema/meson.build @@ -27,6 +27,8 @@ schemas = [ 'args-bad-boxed.json', 'args-boxed-anon.json', 'args-boxed-string.json', + 'args-if-implicit.json', + 'args-if-unboxed.json', 'args-int.json', 'args-invalid.json', 'args-member-array-bad.json', @@ -64,10 +66,12 @@ schemas = [ 'doc-bad-union-member.json', 'doc-before-include.json', 'doc-before-pragma.json', + 'doc-duplicate-features.json', 'doc-duplicated-arg.json', 'doc-duplicated-return.json', 'doc-duplicated-since.json', 'doc-empty-arg.json', + 'doc-empty-features.json', 'doc-empty-section.json', 'doc-empty-symbol.json', 'doc-good.json', @@ -75,6 +79,7 @@ schemas = [ 'doc-invalid-end.json', 'doc-invalid-end2.json', 'doc-invalid-return.json', + 'doc-invalid-return2.json', 'doc-invalid-section.json', 'doc-invalid-start.json', 'doc-missing-colon.json', @@ -164,6 +169,7 @@ schemas = [ 'struct-base-clash-deep.json', 'struct-base-clash.json', 'struct-data-invalid.json', + 'struct-data-typename.json', 'struct-member-if-invalid.json', 'struct-member-invalid-dict.json', 'struct-member-invalid.json', @@ -194,6 +200,8 @@ schemas = [ 'union-invalid-data.json', 'union-invalid-discriminator.json', 'union-invalid-if-discriminator.json', + 'union-invalid-union-subfield.json', + 'union-invalid-union-subtype.json', 'union-no-base.json', 'union-optional-discriminator.json', 'union-string-discriminator.json', @@ -215,18 +223,18 @@ test('QAPI schema regression tests', python, diff = find_program('diff') -qapi_doc = custom_target('QAPI doc', - output: ['doc-good-qapi-commands.c', 'doc-good-qapi-commands.h', - 'doc-good-qapi-emit-events.c', 'doc-good-qapi-emit-events.h', - 'doc-good-qapi-events.c', 'doc-good-qapi-events.h', - 'doc-good-qapi-init-commands.c', 'doc-good-qapi-init-commands.h', - 'doc-good-qapi-introspect.c', 'doc-good-qapi-introspect.h', - 'doc-good-qapi-types.c', 'doc-good-qapi-types.h', - 'doc-good-qapi-visit.c', 'doc-good-qapi-visit.h' ], - input: files('doc-good.json'), - command: [ qapi_gen, '-o', meson.current_build_dir(), - '-p', 'doc-good-', '@INPUT0@' ], - depend_files: qapi_gen_depends) +custom_target('QAPI doc', + output: ['doc-good-qapi-commands.c', 'doc-good-qapi-commands.h', + 'doc-good-qapi-emit-events.c', 'doc-good-qapi-emit-events.h', + 'doc-good-qapi-events.c', 'doc-good-qapi-events.h', + 'doc-good-qapi-init-commands.c', 'doc-good-qapi-init-commands.h', + 'doc-good-qapi-introspect.c', 'doc-good-qapi-introspect.h', + 'doc-good-qapi-types.c', 'doc-good-qapi-types.h', + 'doc-good-qapi-visit.c', 'doc-good-qapi-visit.h' ], + input: files('doc-good.json'), + command: [ qapi_gen, '-o', meson.current_build_dir(), + '-p', 'doc-good-', '@INPUT0@' ], + depend_files: qapi_gen_depends) if build_docs # Test the document-comment document generation code by running a test schema @@ -259,28 +267,27 @@ if build_docs # Fix possible inconsistency in line endings in generated output and # in the golden reference (which could otherwise cause test failures # on Windows hosts). Unfortunately diff --strip-trailing-cr - # is GNU-diff only. The odd-looking perl is because we must avoid + # is GNU-diff only. The odd-looking python is because we must avoid # using an explicit '\' character in the command arguments to # a custom_target(), as Meson will unhelpfully replace it with a '/' # (https://github.com/mesonbuild/meson/issues/1564) + remove_cr = [python, '-c', 'import sys;[sys.stdout.write(line.replace(chr(13), "")) for line in sys.stdin]'] qapi_doc_out_nocr = custom_target('QAPI rST doc newline-sanitized', output: ['doc-good.txt.nocr'], input: qapi_doc_out[0], build_by_default: true, - command: ['perl', '-pe', '$x = chr 13; s/$x$//', '@INPUT@'], - capture: true) + command: [remove_cr], + capture: true, + feed: true) qapi_doc_ref_nocr = custom_target('QAPI rST doc reference newline-sanitized', output: ['doc-good.ref.nocr'], input: files('doc-good.txt'), build_by_default: true, - command: ['perl', '-pe', '$x = chr 13; s/$x$//', '@INPUT@'], - capture: true) + command: [remove_cr], + capture: true, + feed: true) - # "full_path()" needed here to work around - # https://github.com/mesonbuild/meson/issues/7585 - test('QAPI rST doc', diff, args: ['-u', qapi_doc_ref_nocr[0].full_path(), - qapi_doc_out_nocr[0].full_path()], - depends: [qapi_doc_ref_nocr, qapi_doc_out_nocr], + test('QAPI rST doc', diff, args: ['-u', qapi_doc_ref_nocr[0], qapi_doc_out_nocr[0]], suite: ['qapi-schema', 'qapi-doc']) endif diff --git a/tests/qapi-schema/nested-struct-data.err b/tests/qapi-schema/nested-struct-data.err index c7258a0182..7dc5c7cb2d 100644 --- a/tests/qapi-schema/nested-struct-data.err +++ b/tests/qapi-schema/nested-struct-data.err @@ -1,2 +1,2 @@ nested-struct-data.json: In command 'foo': -nested-struct-data.json:2: 'data' member 'a' should be a type name +nested-struct-data.json:2: 'data' member 'a' should be a type name or array diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index ba7302f42b..8ca977c49d 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -114,6 +114,38 @@ { 'struct': 'UserDefC', 'data': { 'string1': 'str', 'string2': 'str' } } +# this tests that unions can contain other unions in their branches +{ 'enum': 'TestUnionEnum', + 'data': [ 'value-a', 'value-b' ] } + +{ 'enum': 'TestUnionEnumA', + 'data': [ 'value-a1', 'value-a2' ] } + +{ 'struct': 'TestUnionTypeA1', + 'data': { 'integer': 'int', + 'name': 'str'} } + +{ 'struct': 'TestUnionTypeA2', + 'data': { 'integer': 'int', + 'size': 'int' } } + +{ 'union': 'TestUnionTypeA', + 'base': { 'type-a': 'TestUnionEnumA' }, + 'discriminator': 'type-a', + 'data': { 'value-a1': 'TestUnionTypeA1', + 'value-a2': 'TestUnionTypeA2' } } + +{ 'struct': 'TestUnionTypeB', + 'data': { 'integer': 'int', + 'onoff': 'bool' } } + +{ 'union': 'TestUnionInUnion', + 'base': { 'type': 'TestUnionEnum' }, + 'discriminator': 'type', + 'data': { 'value-a': 'TestUnionTypeA', + 'value-b': 'TestUnionTypeB' } } + + # for testing use of 'number' within alternates { 'alternate': 'AltEnumBool', 'data': { 'e': 'EnumOne', 'b': 'bool' } } { 'alternate': 'AltEnumNum', 'data': { 'e': 'EnumOne', 'n': 'number' } } @@ -220,18 +252,19 @@ { 'struct': 'TestIfStruct', 'data': { 'foo': 'int', - 'bar': { 'type': 'int', 'if': 'TEST_IF_STRUCT_BAR'} }, + 'bar': { 'type': 'int', 'if': 'TEST_IF_STRUCT_MEMBER'}, + '*baz': { 'type': 'str', 'if': 'TEST_IF_STRUCT_MEMBER'} }, 'if': 'TEST_IF_STRUCT' } { 'enum': 'TestIfEnum', - 'data': [ 'foo', { 'name' : 'bar', 'if': 'TEST_IF_ENUM_BAR' } ], - 'if': 'TEST_IF_ENUM' } + 'data': [ 'foo', { 'name' : 'bar', 'if': 'TEST_IF_ENUM_MEMBER' } ], + 'if': 'TEST_IF_UNION' } { 'union': 'TestIfUnion', 'base': { 'type': 'TestIfEnum' }, 'discriminator': 'type', 'data': { 'foo': 'TestStruct', - 'bar': { 'type': 'UserDefZero', 'if': 'TEST_IF_ENUM_BAR'} }, + 'bar': { 'type': 'UserDefZero', 'if': 'TEST_IF_ENUM_MEMBER'} }, 'if': { 'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT'] } } { 'command': 'test-if-union-cmd', @@ -240,7 +273,7 @@ { 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', - 'bar': { 'type': 'TestStruct', 'if': 'TEST_IF_ALT_BAR'} }, + 'bar': { 'type': 'TestStruct', 'if': 'TEST_IF_ALT_MEMBER'} }, 'if': { 'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT'] } } { 'command': 'test-if-alternate-cmd', @@ -248,17 +281,16 @@ 'if': { 'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT'] } } { 'command': 'test-if-cmd', - 'data': { - 'foo': 'TestIfStruct', - 'bar': { 'type': 'TestIfEnum', 'if': 'TEST_IF_CMD_BAR' } }, + 'boxed': true, + 'data': 'TestIfStruct', 'returns': 'UserDefThree', 'if': { 'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT'] } } { 'command': 'test-cmd-return-def-three', 'returns': 'UserDefThree' } { 'event': 'TEST_IF_EVENT', - 'data': { 'foo': 'TestIfStruct', - 'bar': { 'type': ['TestIfEnum'], 'if': 'TEST_IF_EVT_BAR' } }, + 'boxed': true, + 'data': 'TestIfStruct', 'if': { 'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT'] } } { 'event': 'TEST_IF_EVENT2', 'data': {}, diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 043d75c655..e2f0981348 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -105,6 +105,35 @@ alternate UserDefAlternate object UserDefC member string1: str optional=False member string2: str optional=False +enum TestUnionEnum + member value-a + member value-b +enum TestUnionEnumA + member value-a1 + member value-a2 +object TestUnionTypeA1 + member integer: int optional=False + member name: str optional=False +object TestUnionTypeA2 + member integer: int optional=False + member size: int optional=False +object q_obj_TestUnionTypeA-base + member type-a: TestUnionEnumA optional=False +object TestUnionTypeA + base q_obj_TestUnionTypeA-base + tag type-a + case value-a1: TestUnionTypeA1 + case value-a2: TestUnionTypeA2 +object TestUnionTypeB + member integer: int optional=False + member onoff: bool optional=False +object q_obj_TestUnionInUnion-base + member type: TestUnionEnum optional=False +object TestUnionInUnion + base q_obj_TestUnionInUnion-base + tag type + case value-a: TestUnionTypeA + case value-b: TestUnionTypeB alternate AltEnumBool tag type case e: EnumOne @@ -246,13 +275,15 @@ command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> None object TestIfStruct member foo: int optional=False member bar: int optional=False - if TEST_IF_STRUCT_BAR + if TEST_IF_STRUCT_MEMBER + member baz: str optional=True + if TEST_IF_STRUCT_MEMBER if TEST_IF_STRUCT enum TestIfEnum member foo member bar - if TEST_IF_ENUM_BAR - if TEST_IF_ENUM + if TEST_IF_ENUM_MEMBER + if TEST_IF_UNION object q_obj_TestIfUnion-base member type: TestIfEnum optional=False if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']} @@ -261,7 +292,7 @@ object TestIfUnion tag type case foo: TestStruct case bar: UserDefZero - if TEST_IF_ENUM_BAR + if TEST_IF_ENUM_MEMBER if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']} object q_obj_test-if-union-cmd-arg member union-cmd-arg: TestIfUnion optional=False @@ -273,7 +304,7 @@ alternate TestIfAlternate tag type case foo: int case bar: TestStruct - if TEST_IF_ALT_BAR + if TEST_IF_ALT_MEMBER if {'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT']} object q_obj_test-if-alternate-cmd-arg member alt-cmd-arg: TestIfAlternate optional=False @@ -281,25 +312,13 @@ object q_obj_test-if-alternate-cmd-arg command test-if-alternate-cmd q_obj_test-if-alternate-cmd-arg -> None gen=True success_response=True boxed=False oob=False preconfig=False if {'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT']} -object q_obj_test-if-cmd-arg - member foo: TestIfStruct optional=False - member bar: TestIfEnum optional=False - if TEST_IF_CMD_BAR - if {'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT']} -command test-if-cmd q_obj_test-if-cmd-arg -> UserDefThree - gen=True success_response=True boxed=False oob=False preconfig=False +command test-if-cmd TestIfStruct -> UserDefThree + gen=True success_response=True boxed=True oob=False preconfig=False if {'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT']} command test-cmd-return-def-three None -> UserDefThree gen=True success_response=True boxed=False oob=False preconfig=False -array TestIfEnumList TestIfEnum - if TEST_IF_ENUM -object q_obj_TEST_IF_EVENT-arg - member foo: TestIfStruct optional=False - member bar: TestIfEnumList optional=False - if TEST_IF_EVT_BAR - if {'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT']} -event TEST_IF_EVENT q_obj_TEST_IF_EVENT-arg - boxed=False +event TEST_IF_EVENT TestIfStruct + boxed=True if {'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT']} event TEST_IF_EVENT2 None boxed=False diff --git a/tests/qapi-schema/returns-dict.err b/tests/qapi-schema/returns-dict.err index 9b2d90c010..bf160e754b 100644 --- a/tests/qapi-schema/returns-dict.err +++ b/tests/qapi-schema/returns-dict.err @@ -1,2 +1,2 @@ returns-dict.json: In command 'oops': -returns-dict.json:2: 'returns' should be a type name +returns-dict.json:2: 'returns' should be a type name or array diff --git a/tests/qapi-schema/struct-data-typename.err b/tests/qapi-schema/struct-data-typename.err new file mode 100644 index 0000000000..8fbfe99a42 --- /dev/null +++ b/tests/qapi-schema/struct-data-typename.err @@ -0,0 +1,2 @@ +struct-data-typename.json: In struct 'Stru2': +struct-data-typename.json:2: 'data' should be an object or type name diff --git a/tests/qapi-schema/struct-data-typename.json b/tests/qapi-schema/struct-data-typename.json new file mode 100644 index 0000000000..70fbad0ee4 --- /dev/null +++ b/tests/qapi-schema/struct-data-typename.json @@ -0,0 +1,2 @@ +{ 'struct': 'Stru1', 'data': {} } +{ 'struct': 'Stru2', 'data': 'Stru1' } diff --git a/tests/qapi-schema/struct-data-typename.out b/tests/qapi-schema/struct-data-typename.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/struct-member-invalid.err b/tests/qapi-schema/struct-member-invalid.err index 7e01a41d7c..3130d69d9f 100644 --- a/tests/qapi-schema/struct-member-invalid.err +++ b/tests/qapi-schema/struct-member-invalid.err @@ -1,2 +1,2 @@ struct-member-invalid.json: In struct 'Foo': -struct-member-invalid.json:1: 'data' member 'a' should be a type name +struct-member-invalid.json:1: 'data' member 'a' should be a type name or array diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 2160cef082..40095431ae 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -130,18 +130,17 @@ def test_frontend(fname): for feat, section in doc.features.items(): print(' feature=%s\n%s' % (feat, section.text)) for section in doc.sections: - print(' section=%s\n%s' % (section.name, section.text)) + print(' section=%s\n%s' % (section.tag, section.text)) def open_test_result(dir_name, file_name, update): mode = 'r+' if update else 'r' try: - fp = open(os.path.join(dir_name, file_name), mode) + return open(os.path.join(dir_name, file_name), mode, encoding='utf-8') except FileNotFoundError: if not update: raise - fp = open(os.path.join(dir_name, file_name), 'w+') - return fp + return open(os.path.join(dir_name, file_name), 'w+', encoding='utf-8') def test_and_diff(test_name, dir_name, update): @@ -206,6 +205,7 @@ def main(argv): parser.add_argument('-d', '--dir', action='store', default='', help="directory containing tests") parser.add_argument('-u', '--update', action='store_true', + default='QAPI_TEST_UPDATE' in os.environ, help="update expected test results") parser.add_argument('tests', nargs='*', metavar='TEST', action='store') args = parser.parse_args() @@ -217,9 +217,9 @@ def main(argv): test_name = os.path.splitext(base_name)[0] status |= test_and_diff(test_name, dir_name, args.update) - exit(status) + sys.exit(status) if __name__ == '__main__': main(sys.argv) - exit(0) + sys.exit(0) diff --git a/tests/qapi-schema/union-array-branch.err b/tests/qapi-schema/union-array-branch.err index 5db9c17481..2aa146261a 100644 --- a/tests/qapi-schema/union-array-branch.err +++ b/tests/qapi-schema/union-array-branch.err @@ -1,2 +1,2 @@ union-array-branch.json: In union 'TestUnion': -union-array-branch.json:8: 'data' member 'value1' cannot be an array +union-array-branch.json:8: 'data' member 'value1' should be a type name diff --git a/tests/qapi-schema/union-invalid-discriminator.err b/tests/qapi-schema/union-invalid-discriminator.err index 38efb24b98..6bd774c156 100644 --- a/tests/qapi-schema/union-invalid-discriminator.err +++ b/tests/qapi-schema/union-invalid-discriminator.err @@ -1,2 +1,2 @@ union-invalid-discriminator.json: In union 'TestUnion': -union-invalid-discriminator.json:10: discriminator 'enum_wrong' is not a member of 'base' +union-invalid-discriminator.json:10: discriminator 'type_tag' is not a member of 'base' diff --git a/tests/qapi-schema/union-invalid-discriminator.json b/tests/qapi-schema/union-invalid-discriminator.json index c4fce97362..f315f36e37 100644 --- a/tests/qapi-schema/union-invalid-discriminator.json +++ b/tests/qapi-schema/union-invalid-discriminator.json @@ -8,7 +8,7 @@ 'data': { 'integer': 'int' } } { 'union': 'TestUnion', - 'base': { 'enum1': 'TestEnum' }, - 'discriminator': 'enum_wrong', + 'base': { 'type-tag': 'TestEnum' }, + 'discriminator': 'type_tag', 'data': { 'value1': 'TestTypeA', 'value2': 'TestTypeB' } } diff --git a/tests/qapi-schema/union-invalid-union-subfield.err b/tests/qapi-schema/union-invalid-union-subfield.err new file mode 100644 index 0000000000..91aa87bcd8 --- /dev/null +++ b/tests/qapi-schema/union-invalid-union-subfield.err @@ -0,0 +1,2 @@ +union-invalid-union-subfield.json: In union 'TestUnion': +union-invalid-union-subfield.json:25: member 'teeth' of type 'TestTypeFish' collides with base member 'teeth' diff --git a/tests/qapi-schema/union-invalid-union-subfield.json b/tests/qapi-schema/union-invalid-union-subfield.json new file mode 100644 index 0000000000..e1639d3a96 --- /dev/null +++ b/tests/qapi-schema/union-invalid-union-subfield.json @@ -0,0 +1,30 @@ +# Clash between common member and union variant's variant member +# Base's member 'teeth' clashes with TestTypeFish's + +{ 'enum': 'TestEnum', + 'data': [ 'animals', 'plants' ] } + +{ 'enum': 'TestAnimals', + 'data': [ 'fish', 'birds'] } + +{ 'struct': 'TestTypeFish', + 'data': { 'scales': 'int', 'teeth': 'int' } } + +{ 'struct': 'TestTypeBirds', + 'data': { 'feathers': 'int' } } + +{ 'union': 'TestTypeAnimals', + 'base': { 'atype': 'TestAnimals' }, + 'discriminator': 'atype', + 'data': { 'fish': 'TestTypeFish', + 'birds': 'TestTypeBirds' } } + +{ 'struct': 'TestTypePlants', + 'data': { 'integer': 'int' } } + +{ 'union': 'TestUnion', + 'base': { 'type': 'TestEnum', + 'teeth': 'int' }, + 'discriminator': 'type', + 'data': { 'animals': 'TestTypeAnimals', + 'plants': 'TestTypePlants' } } diff --git a/tests/qapi-schema/union-invalid-union-subfield.out b/tests/qapi-schema/union-invalid-union-subfield.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/union-invalid-union-subtype.err b/tests/qapi-schema/union-invalid-union-subtype.err new file mode 100644 index 0000000000..3538dc2e70 --- /dev/null +++ b/tests/qapi-schema/union-invalid-union-subtype.err @@ -0,0 +1,2 @@ +union-invalid-union-subtype.json: In union 'TestUnion': +union-invalid-union-subtype.json:25: base member 'type' of type 'TestTypeA' collides with base member 'type' diff --git a/tests/qapi-schema/union-invalid-union-subtype.json b/tests/qapi-schema/union-invalid-union-subtype.json new file mode 100644 index 0000000000..ce1de51d8d --- /dev/null +++ b/tests/qapi-schema/union-invalid-union-subtype.json @@ -0,0 +1,29 @@ +# Clash between common member and union variant's common member +# Base's member 'type' clashes with TestTypeA's + +{ 'enum': 'TestEnum', + 'data': [ 'value-a', 'value-b' ] } + +{ 'enum': 'TestEnumA', + 'data': [ 'value-a1', 'value-a2' ] } + +{ 'struct': 'TestTypeA1', + 'data': { 'integer': 'int' } } + +{ 'struct': 'TestTypeA2', + 'data': { 'integer': 'int' } } + +{ 'union': 'TestTypeA', + 'base': { 'type': 'TestEnumA' }, + 'discriminator': 'type', + 'data': { 'value-a1': 'TestTypeA1', + 'value-a2': 'TestTypeA2' } } + +{ 'struct': 'TestTypeB', + 'data': { 'integer': 'int' } } + +{ 'union': 'TestUnion', + 'base': { 'type': 'TestEnum' }, + 'discriminator': 'type', + 'data': { 'value-a': 'TestTypeA', + 'value-b': 'TestTypeB' } } diff --git a/tests/qapi-schema/union-invalid-union-subtype.out b/tests/qapi-schema/union-invalid-union-subtype.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qemu-iotests/022 b/tests/qemu-iotests/022 index a116cfe255..d98d1ea90f 100755 --- a/tests/qemu-iotests/022 +++ b/tests/qemu-iotests/022 @@ -16,9 +16,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA +# along with this program. If not, see . # # creator diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024 index 25a564a150..285f17e79f 100755 --- a/tests/qemu-iotests/024 +++ b/tests/qemu-iotests/024 @@ -199,6 +199,123 @@ echo # $BASE_OLD and $BASE_NEW) $QEMU_IMG map "$OVERLAY" | _filter_qemu_img_map +# Check that rebase within the chain is working when +# overlay_size > old_backing_size +# +# base_new <-- base_old <-- overlay +# +# Backing (new): 11 11 11 11 11 +# Backing (old): 22 22 22 22 +# Overlay: -- -- -- -- -- +# +# As a result, overlay should contain data identical to base_old, with the +# last cluster remaining unallocated. + +echo +echo "=== Test rebase within one backing chain ===" +echo + +echo "Creating backing chain" +echo + +TEST_IMG=$BASE_NEW _make_test_img $(( CLUSTER_SIZE * 5 )) +TEST_IMG=$BASE_OLD _make_test_img -b "$BASE_NEW" -F $IMGFMT \ + $(( CLUSTER_SIZE * 4 )) +TEST_IMG=$OVERLAY _make_test_img -b "$BASE_OLD" -F $IMGFMT \ + $(( CLUSTER_SIZE * 5 )) + +echo +echo "Fill backing files with data" +echo + +$QEMU_IO "$BASE_NEW" -c "write -P 0x11 0 $(( CLUSTER_SIZE * 5 ))" \ + | _filter_qemu_io +$QEMU_IO "$BASE_OLD" -c "write -P 0x22 0 $(( CLUSTER_SIZE * 4 ))" \ + | _filter_qemu_io + +echo +echo "Check the last cluster is zeroed in overlay before the rebase" +echo +$QEMU_IO "$OVERLAY" -c "read -P 0x00 $(( CLUSTER_SIZE * 4 )) $CLUSTER_SIZE" \ + | _filter_qemu_io + +echo +echo "Rebase onto another image in the same chain" +echo + +$QEMU_IMG rebase -b "$BASE_NEW" -F $IMGFMT "$OVERLAY" + +echo "Verify that data is read the same before and after rebase" +echo + +# Verify the first 4 clusters are still read the same as in the old base +$QEMU_IO "$OVERLAY" -c "read -P 0x22 0 $(( CLUSTER_SIZE * 4 ))" \ + | _filter_qemu_io +# Verify the last cluster still reads as zeroes +$QEMU_IO "$OVERLAY" -c "read -P 0x00 $(( CLUSTER_SIZE * 4 )) $CLUSTER_SIZE" \ + | _filter_qemu_io + +echo + +# Check that rebase within the chain is working when +# overlay cluster size > backings cluster size +# (here overlay cluster size == 2 * backings cluster size) +# +# base_new <-- base_old <-- overlay +# +# Backing (new): -- -- -- -- -- -- +# Backing (old): -- 11 -- -- 22 -- +# Overlay: |-- --|-- --|-- --| +# +# We should end up having 1st and 3rd cluster allocated, and their halves +# being read as zeroes. + +echo +echo "=== Test rebase with different cluster sizes ===" +echo + +echo "Creating backing chain" +echo + +TEST_IMG=$BASE_NEW _make_test_img $(( CLUSTER_SIZE * 6 )) +TEST_IMG=$BASE_OLD _make_test_img -b "$BASE_NEW" -F $IMGFMT \ + $(( CLUSTER_SIZE * 6 )) +CLUSTER_SIZE=$(( CLUSTER_SIZE * 2 )) TEST_IMG=$OVERLAY \ + _make_test_img -b "$BASE_OLD" -F $IMGFMT $(( CLUSTER_SIZE * 6 )) + +TEST_IMG=$OVERLAY _img_info + +echo +echo "Fill backing files with data" +echo + +$QEMU_IO "$BASE_OLD" -c "write -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" \ + -c "write -P 0x22 $(( CLUSTER_SIZE * 4 )) $CLUSTER_SIZE" \ + | _filter_qemu_io + +echo +echo "Rebase onto another image in the same chain" +echo + +$QEMU_IMG rebase -b "$BASE_NEW" -F $IMGFMT "$OVERLAY" + +echo "Verify that data is read the same before and after rebase" +echo + +$QEMU_IO "$OVERLAY" -c "read -P 0x00 0 $CLUSTER_SIZE" \ + -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" \ + -c "read -P 0x00 $(( CLUSTER_SIZE * 2 )) $(( CLUSTER_SIZE * 2 ))" \ + -c "read -P 0x22 $(( CLUSTER_SIZE * 4 )) $CLUSTER_SIZE" \ + -c "read -P 0x00 $(( CLUSTER_SIZE * 5 )) $CLUSTER_SIZE" \ + | _filter_qemu_io + +echo +echo "Verify that untouched cluster remains unallocated" +echo + +$QEMU_IMG map "$OVERLAY" | _filter_qemu_img_map + +echo # success, all done echo "*** done" diff --git a/tests/qemu-iotests/024.out b/tests/qemu-iotests/024.out index 973a5a3711..e1e8eea863 100644 --- a/tests/qemu-iotests/024.out +++ b/tests/qemu-iotests/024.out @@ -171,4 +171,77 @@ read 65536/65536 bytes at offset 196608 Offset Length File 0 0x30000 TEST_DIR/subdir/t.IMGFMT 0x30000 0x10000 TEST_DIR/subdir/t.IMGFMT.base_new + +=== Test rebase within one backing chain === + +Creating backing chain + +Formatting 'TEST_DIR/subdir/t.IMGFMT.base_new', fmt=IMGFMT size=327680 +Formatting 'TEST_DIR/subdir/t.IMGFMT.base_old', fmt=IMGFMT size=262144 backing_file=TEST_DIR/subdir/t.IMGFMT.base_new backing_fmt=IMGFMT +Formatting 'TEST_DIR/subdir/t.IMGFMT', fmt=IMGFMT size=327680 backing_file=TEST_DIR/subdir/t.IMGFMT.base_old backing_fmt=IMGFMT + +Fill backing files with data + +wrote 327680/327680 bytes at offset 0 +320 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 262144/262144 bytes at offset 0 +256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Check the last cluster is zeroed in overlay before the rebase + +read 65536/65536 bytes at offset 262144 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Rebase onto another image in the same chain + +Verify that data is read the same before and after rebase + +read 262144/262144 bytes at offset 0 +256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 262144 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + + +=== Test rebase with different cluster sizes === + +Creating backing chain + +Formatting 'TEST_DIR/subdir/t.IMGFMT.base_new', fmt=IMGFMT size=393216 +Formatting 'TEST_DIR/subdir/t.IMGFMT.base_old', fmt=IMGFMT size=393216 backing_file=TEST_DIR/subdir/t.IMGFMT.base_new backing_fmt=IMGFMT +Formatting 'TEST_DIR/subdir/t.IMGFMT', fmt=IMGFMT size=393216 backing_file=TEST_DIR/subdir/t.IMGFMT.base_old backing_fmt=IMGFMT +image: TEST_DIR/subdir/t.IMGFMT +file format: IMGFMT +virtual size: 384 KiB (393216 bytes) +cluster_size: 131072 +backing file: TEST_DIR/subdir/t.IMGFMT.base_old +backing file format: IMGFMT + +Fill backing files with data + +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 262144 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Rebase onto another image in the same chain + +Verify that data is read the same before and after rebase + +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 131072 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 262144 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 327680 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Verify that untouched cluster remains unallocated + +Offset Length File +0 0x20000 TEST_DIR/subdir/t.IMGFMT +0x40000 0x20000 TEST_DIR/subdir/t.IMGFMT + *** done diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029 index bd71dd2f22..7f4849b97b 100755 --- a/tests/qemu-iotests/029 +++ b/tests/qemu-iotests/029 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter . ./common.pattern -# Any format supporting intenal snapshots +# Any format supporting internal snapshots _supported_fmt qcow2 _supported_proto generic # Internal snapshots are (currently) impossible with refcount_bits=1, diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 98595d47fe..0e6a39d103 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -56,8 +56,7 @@ class TestSingleDrive(iotests.QMPTestCase): def test_stream(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('block-stream', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0') self.wait_until_completed() @@ -77,8 +76,7 @@ class TestSingleDrive(iotests.QMPTestCase): qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', mid_img).stdout, 'image file map matches backing file before streaming') - result = self.vm.qmp('block-stream', device='mid', job_id='stream-mid') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='mid', job_id='stream-mid') self.wait_until_completed(drive='stream-mid') @@ -94,8 +92,7 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp('block-stream', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0') self.pause_job('drive0', wait=False) self.vm.resume_drive('drive0') @@ -108,8 +105,7 @@ class TestSingleDrive(iotests.QMPTestCase): result = self.vm.qmp('query-block-jobs') self.assert_qmp(result, 'return[0]/offset', offset) - result = self.vm.qmp('block-job-resume', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-resume', device='drive0') self.wait_until_completed() @@ -129,8 +125,7 @@ class TestSingleDrive(iotests.QMPTestCase): '-f', iotests.imgfmt, '-rU', '-c', 'map', test_img).stdout # This is a no-op: no data should ever be copied from the base image - result = self.vm.qmp('block-stream', device='drive0', base=mid_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0', base=mid_img) self.wait_until_completed() @@ -144,8 +139,7 @@ class TestSingleDrive(iotests.QMPTestCase): def test_stream_partial(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('block-stream', device='drive0', base=backing_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0', base=backing_img) self.wait_until_completed() @@ -172,24 +166,22 @@ class TestSingleDrive(iotests.QMPTestCase): qemu_img('create', '-f', iotests.imgfmt, ro_top_path, str(self.image_len)) - result = self.vm.qmp('blockdev-add', - node_name='ro-top', - driver=iotests.imgfmt, - read_only=True, - file={ - 'driver': 'file', - 'filename': ro_top_path, - 'read-only': True - }, - backing='mid') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', + node_name='ro-top', + driver=iotests.imgfmt, + read_only=True, + file={ + 'driver': 'file', + 'filename': ro_top_path, + 'read-only': True + }, + backing='mid') result = self.vm.qmp('block-stream', job_id='stream', device='ro-top', base_node='base') self.assert_qmp(result, 'error/desc', 'Block node is read-only') - result = self.vm.qmp('blockdev-del', node_name='ro-top') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', node_name='ro-top') class TestParallelOps(iotests.QMPTestCase): @@ -254,10 +246,9 @@ class TestParallelOps(iotests.QMPTestCase): node_name = 'node%d' % i job_id = 'stream-%s' % node_name pending_jobs.append(job_id) - result = self.vm.qmp('block-stream', device=node_name, - job_id=job_id, bottom=f'node{i-1}', - speed=1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device=node_name, + job_id=job_id, bottom=f'node{i-1}', + speed=1024) # Do this in reverse: After unthrottling them, some jobs may finish # before we have unthrottled all of them. This will drain their @@ -269,8 +260,7 @@ class TestParallelOps(iotests.QMPTestCase): # Starting from the top (i.e. in reverse) does not have this problem: # When a job finishes, the ones below it are not advanced. for job in reversed(pending_jobs): - result = self.vm.qmp('block-job-set-speed', device=job, speed=0) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-set-speed', device=job, speed=0) # Wait for all jobs to be finished. while len(pending_jobs) > 0: @@ -297,10 +287,9 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_no_active_block_jobs() # Set a speed limit to make sure that this job blocks the rest - result = self.vm.qmp('block-stream', device='node4', - job_id='stream-node4', base=self.imgs[1], - filter_node_name='stream-filter', speed=1024*1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='node4', + job_id='stream-node4', base=self.imgs[1], + filter_node_name='stream-filter', speed=1024*1024) result = self.vm.qmp('block-stream', device='node5', job_id='stream-node5', base=self.imgs[2]) self.assert_qmp(result, 'error/desc', @@ -328,8 +317,7 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_qmp(result, 'error/desc', "Node 'node2' is busy: block device is in use by block job: stream") - result = self.vm.qmp('block-job-set-speed', device='stream-node4', speed=0) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-set-speed', device='stream-node4', speed=0) self.wait_until_completed(drive='stream-node4') self.assert_no_active_block_jobs() @@ -341,8 +329,7 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_no_active_block_jobs() # Set a speed limit to make sure that this job blocks the rest - result = self.vm.qmp('block-commit', device='drive0', top=self.imgs[5], base=self.imgs[3], job_id='commit-node3', speed=1024*1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='drive0', top=self.imgs[5], base=self.imgs[3], job_id='commit-node3', speed=1024*1024) result = self.vm.qmp('block-stream', device='node3', job_id='stream-node3') self.assert_qmp(result, 'error/desc', @@ -365,8 +352,7 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_qmp(result, 'error/desc', "Node 'drive0' is busy: block device is in use by block job: commit") - result = self.vm.qmp('block-job-set-speed', device='commit-node3', speed=0) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-set-speed', device='commit-node3', speed=0) self.wait_until_completed(drive='commit-node3') @@ -377,23 +363,20 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_no_active_block_jobs() # Set a speed limit to make sure that this job blocks the rest - result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[3], job_id='commit-drive0', speed=1024*1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='drive0', base=self.imgs[3], job_id='commit-drive0', speed=1024*1024) result = self.vm.qmp('block-stream', device='node5', base=self.imgs[3], job_id='stream-node6') self.assert_qmp(result, 'error/desc', "Node 'node5' is busy: block device is in use by block job: commit") - result = self.vm.qmp('block-job-set-speed', device='commit-drive0', speed=0) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-set-speed', device='commit-drive0', speed=0) event = self.vm.event_wait(name='BLOCK_JOB_READY') self.assert_qmp(event, 'data/device', 'commit-drive0') self.assert_qmp(event, 'data/type', 'commit') self.assert_qmp_absent(event, 'data/error') - result = self.vm.qmp('block-job-complete', device='commit-drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-complete', device='commit-drive0') self.wait_until_completed(drive='commit-drive0') @@ -404,18 +387,16 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_no_active_block_jobs() # Commit from node2 into node0 - result = self.vm.qmp('block-commit', device='drive0', - top=self.imgs[2], base=self.imgs[0], - filter_node_name='commit-filter', speed=1024*1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='drive0', + top=self.imgs[2], base=self.imgs[0], + filter_node_name='commit-filter', speed=1024*1024) # Stream from node2 into node4 result = self.vm.qmp('block-stream', device='node4', base_node='node2', job_id='node4') self.assert_qmp(result, 'error/desc', "Cannot freeze 'backing' link to 'commit-filter'") - result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-set-speed', device='drive0', speed=0) self.wait_until_completed() self.assert_no_active_block_jobs() @@ -428,18 +409,15 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_no_active_block_jobs() # Commit from node2 into node0 - result = self.vm.qmp('block-commit', device='drive0', - top_node='node2', base_node='node0', - filter_node_name='commit-filter', speed=1024*1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='drive0', + top_node='node2', base_node='node0', + filter_node_name='commit-filter', speed=1024*1024) # Stream from node2 into node4 - result = self.vm.qmp('block-stream', device='node4', - base_node='commit-filter', job_id='node4') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='node4', + base_node='commit-filter', job_id='node4') - result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-set-speed', device='drive0', speed=0) self.vm.run_job(job='drive0', auto_dismiss=True) self.vm.run_job(job='node4', auto_dismiss=True) @@ -458,12 +436,10 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_no_active_block_jobs() # Stream from node0 into node2 - result = self.vm.qmp('block-stream', device='node2', base_node='node0', job_id='node2') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='node2', base_node='node0', job_id='node2') # Commit from the active layer into node3 - result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[3]) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='drive0', base=self.imgs[3]) # Wait for all jobs to be finished. pending_jobs = ['node2', 'drive0'] @@ -490,16 +466,13 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_no_active_block_jobs() # Stream from node0 into node4 - result = self.vm.qmp('block-stream', device='node4', base_node='node0', job_id='node4', speed=1024*1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='node4', base_node='node0', job_id='node4', speed=1024*1024) # Commit from the active layer into node5 - result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[5], speed=1024*1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='drive0', base=self.imgs[5], speed=1024*1024) for job in ['drive0', 'node4']: - result = self.vm.qmp('block-job-set-speed', device=job, speed=0) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-set-speed', device=job, speed=0) # Wait for all jobs to be finished. pending_jobs = ['node4', 'drive0'] @@ -549,8 +522,7 @@ class TestParallelOps(iotests.QMPTestCase): "'base' and 'base-node' cannot be specified at the same time") # Success: the base node is a backing file of the top node - result = self.vm.qmp('block-stream', device='node4', base_node='node2', job_id='stream') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='node4', base_node='node2', job_id='stream') self.wait_until_completed(drive='stream') @@ -606,8 +578,7 @@ class TestQuorum(iotests.QMPTestCase): self.assert_no_active_block_jobs() - result = self.vm.qmp('block-stream', device='node0', job_id='stream-node0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='node0', job_id='stream-node0') self.wait_until_completed(drive='stream-node0') @@ -636,8 +607,7 @@ class TestSmallerBackingFile(iotests.QMPTestCase): def test_stream(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('block-stream', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0') self.wait_until_completed() @@ -694,8 +664,7 @@ class TestEIO(TestErrors): def test_report(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('block-stream', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0') completed = False error = False @@ -722,8 +691,7 @@ class TestEIO(TestErrors): def test_ignore(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('block-stream', device='drive0', on_error='ignore') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0', on_error='ignore') error = False completed = False @@ -756,8 +724,7 @@ class TestEIO(TestErrors): def test_stop(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('block-stream', device='drive0', on_error='stop') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0', on_error='stop') error = False completed = False @@ -779,8 +746,7 @@ class TestEIO(TestErrors): self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE) self.assert_qmp(result, 'return[0]/io-status', 'failed') - result = self.vm.qmp('block-job-resume', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-resume', device='drive0') result = self.vm.qmp('query-block-jobs') if result == {'return': []}: @@ -806,8 +772,7 @@ class TestEIO(TestErrors): def test_enospc(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('block-stream', device='drive0', on_error='enospc') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0', on_error='enospc') completed = False error = False @@ -852,8 +817,7 @@ class TestENOSPC(TestErrors): def test_enospc(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('block-stream', device='drive0', on_error='enospc') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0', on_error='enospc') error = False completed = False @@ -875,8 +839,7 @@ class TestENOSPC(TestErrors): self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE) self.assert_qmp(result, 'return[0]/io-status', 'nospace') - result = self.vm.qmp('block-job-resume', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-resume', device='drive0') result = self.vm.qmp('query-block-jobs') if result == {'return': []}: @@ -921,8 +884,7 @@ class TestStreamStop(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp('block-stream', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0') time.sleep(0.1) events = self.vm.get_qmp_events(wait=False) @@ -955,11 +917,9 @@ class TestSetSpeed(iotests.QMPTestCase): def perf_test_throughput(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('block-stream', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0') - result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) self.wait_until_completed() @@ -969,16 +929,14 @@ class TestSetSpeed(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp('block-stream', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0') # Default speed is 0 result = self.vm.qmp('query-block-jobs') self.assert_qmp(result, 'return[0]/device', 'drive0') self.assert_qmp(result, 'return[0]/speed', 0) - result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) # Ensure the speed we set was accepted result = self.vm.qmp('query-block-jobs') @@ -989,8 +947,7 @@ class TestSetSpeed(iotests.QMPTestCase): self.vm.pause_drive('drive0') # Check setting speed in block-stream works - result = self.vm.qmp('block-stream', device='drive0', speed=4 * 1024 * 1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0', speed=4 * 1024 * 1024) result = self.vm.qmp('query-block-jobs') self.assert_qmp(result, 'return[0]/device', 'drive0') @@ -1007,8 +964,7 @@ class TestSetSpeed(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp('block-stream', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', device='drive0') result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) self.assert_qmp(result, 'error/desc', "Parameter 'speed' expects a non-negative value") diff --git a/tests/qemu-iotests/033 b/tests/qemu-iotests/033 index da9133c44b..4bc7a071bd 100755 --- a/tests/qemu-iotests/033 +++ b/tests/qemu-iotests/033 @@ -123,9 +123,9 @@ do_test 512 "write -P 1 0 0x200" "$TEST_IMG" | _filter_qemu_io # next L2 table do_test 512 "write -P 1 $L2_COVERAGE 0x200" "$TEST_IMG" | _filter_qemu_io -# only interested in qcow2 here; also other formats might respond with -# "not supported" error message -if [ $IMGFMT = "qcow2" ]; then +# only interested in qcow2 with file protocol here; also other formats +# might respond with "not supported" error message +if [ $IMGFMT = "qcow2" ] && [ $IMGPROTO = "file" ]; then do_test 512 "truncate $L2_COVERAGE" "$TEST_IMG" | _filter_qemu_io fi diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index 30eb97829e..5c18e413ec 100755 --- a/tests/qemu-iotests/040 +++ b/tests/qemu-iotests/040 @@ -61,16 +61,14 @@ class ImageCommitTestCase(iotests.QMPTestCase): def run_commit_test(self, top, base, need_ready=False, node_names=False): self.assert_no_active_block_jobs() if node_names: - result = self.vm.qmp('block-commit', device='drive0', top_node=top, base_node=base) + self.vm.cmd('block-commit', device='drive0', top_node=top, base_node=base) else: - result = self.vm.qmp('block-commit', device='drive0', top=top, base=base) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='drive0', top=top, base=base) self.wait_for_complete(need_ready) def run_default_commit_test(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('block-commit', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='drive0') self.wait_for_complete() class TestSingleDrive(ImageCommitTestCase): @@ -117,38 +115,30 @@ class TestSingleDrive(ImageCommitTestCase): @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', {}) + self.vm.cmd('object-add', qom_type='throttle-group', id='tg') # Add a filter outside of the backing chain - result = self.vm.qmp('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid') - result = self.vm.qmp('block-commit', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='drive0') # Quit immediately, thus forcing a simultaneous cancel of the # block job and a bdrv_drain_all() - result = self.vm.qmp('quit') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('quit') # 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', {}) + self.vm.cmd('object-add', qom_type='throttle-group', id='tg') - result = self.vm.qmp('block-commit', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='drive0') # Add a filter outside of the backing chain - result = self.vm.qmp('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid') # Quit immediately, thus forcing a simultaneous cancel of the # block job and a bdrv_drain_all() - result = self.vm.qmp('quit') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('quit') def test_device_not_found(self): result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img) @@ -225,8 +215,7 @@ class TestSingleDrive(ImageCommitTestCase): def test_top_node_in_wrong_chain(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('blockdev-add', driver='null-co', node_name='null') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', driver='null-co', node_name='null') result = self.vm.qmp('block-commit', device='drive0', top_node='null', base_node='base') self.assert_qmp(result, 'error/class', 'GenericError') @@ -239,11 +228,9 @@ class TestSingleDrive(ImageCommitTestCase): return self.assert_no_active_block_jobs() - result = self.vm.qmp('block-commit', device='drive0', top=mid_img, - base=backing_img, speed=(self.image_len // 4)) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('device_del', id='scsi0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='drive0', top=mid_img, + base=backing_img, speed=(self.image_len // 4)) + self.vm.cmd('device_del', id='scsi0') cancelled = False deleted = False @@ -269,9 +256,8 @@ class TestSingleDrive(ImageCommitTestCase): return self.assert_no_active_block_jobs() - result = self.vm.qmp('block-commit', device='drive0', top=mid_img, - base=backing_img, speed=(self.image_len // 4)) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='drive0', top=mid_img, + base=backing_img, speed=(self.image_len // 4)) result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', test_img) @@ -406,8 +392,7 @@ class TestSetSpeed(ImageCommitTestCase): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp('block-commit', device='drive0', top=mid_img, speed=1024 * 1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='drive0', top=mid_img, speed=1024 * 1024) # Ensure the speed we set was accepted result = self.vm.qmp('query-block-jobs') @@ -480,8 +465,7 @@ class TestErrorHandling(iotests.QMPTestCase): os.remove(backing_img) def blockdev_add(self, **kwargs): - result = self.vm.qmp('blockdev-add', **kwargs) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', **kwargs) def add_block_nodes(self, base_debug=None, mid_debug=None, top_debug=None): self.blockdev_add(node_name='base-file', driver='file', @@ -527,11 +511,9 @@ class TestErrorHandling(iotests.QMPTestCase): 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', {}) + self.vm.cmd('block-job-resume', device='job0') elif ev['event'] == 'BLOCK_JOB_READY': - result = self.vm.qmp('block-job-complete', device='job0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-complete', device='job0') else: self.fail("Unexpected event: %s" % ev) log.append(iotests.filter_qmp_event(ev)) @@ -594,11 +576,10 @@ class TestErrorHandling(iotests.QMPTestCase): 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', {}) + self.vm.cmd('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) def testActiveReadErrorReport(self): self.prepare_and_start_job('report', top_event='read_aio') @@ -770,10 +751,9 @@ class TestCommitWithFilters(iotests.QMPTestCase): self.vm = iotests.VM().add_device('virtio-scsi,id=vio-scsi') self.vm.launch() - result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('object-add', qom_type='throttle-group', id='tg') - result = self.vm.qmp('blockdev-add', **{ + self.vm.cmd('blockdev-add', { 'node-name': 'top-filter', 'driver': 'throttle', 'throttle-group': 'tg', @@ -815,7 +795,6 @@ class TestCommitWithFilters(iotests.QMPTestCase): } } }) - self.assert_qmp(result, 'return', {}) def tearDown(self): self.vm.shutdown() @@ -832,30 +811,28 @@ class TestCommitWithFilters(iotests.QMPTestCase): return self.vm.node_info(node)['image']['filename'] def test_filterless_commit(self): - result = self.vm.qmp('block-commit', - job_id='commit', - device='top-filter', - top_node='cow-2', - base_node='cow-1', - backing_file=self.img1) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', + job_id='commit', + device='top-filter', + top_node='cow-2', + base_node='cow-1', + backing_file=self.img1) self.wait_until_completed(drive='commit') self.assertIsNotNone(self.vm.node_info('cow-3')) self.assertIsNone(self.vm.node_info('cow-2')) self.assertIsNotNone(self.vm.node_info('cow-1')) - # 2 has been comitted into 1 + # 2 has been committed into 1 self.pattern_files[2] = self.img1 def test_commit_through_filter(self): - result = self.vm.qmp('block-commit', - job_id='commit', - device='top-filter', - top_node='cow-1', - base_node='cow-0', - backing_file=self.img0) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', + job_id='commit', + device='top-filter', + top_node='cow-1', + base_node='cow-0', + backing_file=self.img0) self.wait_until_completed(drive='commit') self.assertIsNotNone(self.vm.node_info('cow-2')) @@ -863,16 +840,15 @@ class TestCommitWithFilters(iotests.QMPTestCase): self.assertIsNone(self.vm.node_info('bottom-filter')) self.assertIsNotNone(self.vm.node_info('cow-0')) - # 1 has been comitted into 0 + # 1 has been committed into 0 self.pattern_files[1] = self.img0 def test_filtered_active_commit_with_filter(self): # Add a device, so the commit job finds a parent it can change # to point to the base node (so we can test that top-filter is # dropped from the graph) - result = self.vm.qmp('device_add', id='drv0', driver='scsi-hd', - bus='vio-scsi.0', drive='top-filter') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('device_add', id='drv0', driver='scsi-hd', + bus='vio-scsi.0', drive='top-filter') # Try to release our reference to top-filter; that should not # work because drv0 uses it @@ -880,16 +856,14 @@ class TestCommitWithFilters(iotests.QMPTestCase): self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/desc', 'Node top-filter is in use') - result = self.vm.qmp('block-commit', - job_id='commit', - device='top-filter', - base_node='cow-2') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', + job_id='commit', + device='top-filter', + base_node='cow-2') self.complete_and_wait(drive='commit') # Try to release our reference to top-filter again - result = self.vm.qmp('blockdev-del', node_name='top-filter') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', node_name='top-filter') self.assertIsNone(self.vm.node_info('top-filter')) self.assertIsNone(self.vm.node_info('cow-3')) @@ -900,23 +874,22 @@ class TestCommitWithFilters(iotests.QMPTestCase): drv0 = next(dev for dev in blockdevs if dev['qdev'] == 'drv0') self.assertEqual(drv0['inserted']['node-name'], 'cow-2') - # 3 has been comitted into 2 + # 3 has been committed into 2 self.pattern_files[3] = self.img2 def test_filtered_active_commit_without_filter(self): - result = self.vm.qmp('block-commit', - job_id='commit', - device='top-filter', - top_node='cow-3', - base_node='cow-2') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', + job_id='commit', + device='top-filter', + top_node='cow-3', + base_node='cow-2') self.complete_and_wait(drive='commit') self.assertIsNotNone(self.vm.node_info('top-filter')) self.assertIsNone(self.vm.node_info('cow-3')) self.assertIsNotNone(self.vm.node_info('cow-2')) - # 3 has been comitted into 2 + # 3 has been committed into 2 self.pattern_files[3] = self.img2 class TestCommitWithOverriddenBacking(iotests.QMPTestCase): @@ -934,23 +907,22 @@ class TestCommitWithOverriddenBacking(iotests.QMPTestCase): self.vm.launch() # Use base_b instead of base_a as the backing of top - result = self.vm.qmp('blockdev-add', **{ - 'node-name': 'top', - 'driver': iotests.imgfmt, - 'file': { - 'driver': 'file', - 'filename': self.img_top - }, - 'backing': { - 'node-name': 'base', - 'driver': iotests.imgfmt, - 'file': { - 'driver': 'file', - 'filename': self.img_base_b - } - } - }) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', { + 'node-name': 'top', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': self.img_top + }, + 'backing': { + 'node-name': 'base', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': self.img_base_b + } + } + }) def tearDown(self): self.vm.shutdown() @@ -970,11 +942,10 @@ class TestCommitWithOverriddenBacking(iotests.QMPTestCase): def test_commit_to_b(self): # Try committing to base_b (which should work, since that is # actually top's backing image) - result = self.vm.qmp('block-commit', - job_id='commit', - device='top', - base=self.img_base_b) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', + job_id='commit', + device='top', + base=self.img_base_b) self.vm.event_wait('BLOCK_JOB_READY') self.vm.qmp('block-job-complete', device='commit') diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index 8429958bf0..98d17b1388 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -65,9 +65,8 @@ class TestSingleDrive(iotests.QMPTestCase): def test_complete(self): self.assert_no_active_block_jobs() - result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', - target=self.qmp_target) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(self.qmp_cmd, device='drive0', sync='full', + target=self.qmp_target) self.complete_and_wait() result = self.vm.qmp('query-block') @@ -79,9 +78,8 @@ class TestSingleDrive(iotests.QMPTestCase): def test_cancel(self): self.assert_no_active_block_jobs() - result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', - target=self.qmp_target) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(self.qmp_cmd, device='drive0', sync='full', + target=self.qmp_target) self.cancel_and_wait(force=True) result = self.vm.qmp('query-block') @@ -90,9 +88,8 @@ class TestSingleDrive(iotests.QMPTestCase): def test_cancel_after_ready(self): self.assert_no_active_block_jobs() - result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', - target=self.qmp_target) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(self.qmp_cmd, device='drive0', sync='full', + target=self.qmp_target) self.wait_ready_and_cancel() result = self.vm.qmp('query-block') @@ -104,9 +101,8 @@ class TestSingleDrive(iotests.QMPTestCase): def test_pause(self): self.assert_no_active_block_jobs() - result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', - target=self.qmp_target) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(self.qmp_cmd, device='drive0', sync='full', + target=self.qmp_target) self.pause_job('drive0') @@ -117,8 +113,7 @@ class TestSingleDrive(iotests.QMPTestCase): result = self.vm.qmp('query-block-jobs') self.assert_qmp(result, 'return[0]/offset', offset) - result = self.vm.qmp('block-job-resume', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-resume', device='drive0') self.complete_and_wait() self.vm.shutdown() @@ -129,9 +124,8 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_no_active_block_jobs() # A small buffer is rounded up automatically - result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', - buf_size=4096, target=self.qmp_target) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(self.qmp_cmd, device='drive0', sync='full', + buf_size=4096, target=self.qmp_target) self.complete_and_wait() result = self.vm.qmp('query-block') @@ -145,9 +139,8 @@ class TestSingleDrive(iotests.QMPTestCase): qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d' % (self.image_len, self.image_len), target_img) - result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', - buf_size=65536, mode='existing', target=self.qmp_target) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(self.qmp_cmd, device='drive0', sync='full', + buf_size=65536, mode='existing', target=self.qmp_target) self.complete_and_wait() result = self.vm.qmp('query-block') @@ -162,9 +155,8 @@ class TestSingleDrive(iotests.QMPTestCase): qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s' % (self.image_len, backing_img), '-F', 'raw', target_img) - result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', - mode='existing', target=self.qmp_target) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(self.qmp_cmd, device='drive0', sync='full', + mode='existing', target=self.qmp_target) self.complete_and_wait() result = self.vm.qmp('query-block') @@ -178,9 +170,8 @@ class TestSingleDrive(iotests.QMPTestCase): def test_implicit_node(self): self.assert_no_active_block_jobs() - result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', - target=self.qmp_target) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(self.qmp_cmd, device='drive0', sync='full', + target=self.qmp_target) result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', test_img) @@ -236,8 +227,7 @@ class TestSingleBlockdev(TestSingleDrive): args = {'driver': iotests.imgfmt, 'node-name': self.qmp_target, 'file': { 'filename': target_img, 'driver': 'file' } } - result = self.vm.qmp("blockdev-add", **args) - self.assert_qmp(result, 'return', {}) + self.vm.cmd("blockdev-add", args) def test_mirror_to_self(self): result = self.vm.qmp(self.qmp_cmd, job_id='job0', @@ -254,10 +244,9 @@ class TestSingleBlockdev(TestSingleDrive): result = self.vm.qmp('block_resize', node_name=node, size=65536) self.assert_qmp(result, 'error/class', 'GenericError') - result = self.vm.qmp(self.qmp_cmd, job_id='job0', device='drive0', - sync='full', target=self.qmp_target, - auto_finalize=False, auto_dismiss=False) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(self.qmp_cmd, job_id='job0', device='drive0', + sync='full', target=self.qmp_target, + auto_finalize=False, auto_dismiss=False) result = self.vm.run_job('job0', auto_finalize=False, pre_finalize=pre_finalize) @@ -270,14 +259,12 @@ class TestSingleBlockdev(TestSingleDrive): self.do_test_resize(None, self.qmp_target) def do_test_target_size(self, size): - result = self.vm.qmp('block_resize', node_name=self.qmp_target, - size=size) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block_resize', node_name=self.qmp_target, + size=size) - result = self.vm.qmp(self.qmp_cmd, job_id='job0', - device='drive0', sync='full', auto_dismiss=False, - target=self.qmp_target) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(self.qmp_cmd, job_id='job0', + device='drive0', sync='full', auto_dismiss=False, + target=self.qmp_target) result = self.vm.run_job('job0') self.assertEqual(result, 'Source and target image have different sizes') @@ -337,9 +324,8 @@ class TestMirrorNoBacking(iotests.QMPTestCase): qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, '-F', 'raw', target_img) - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - mode='existing', target=target_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + mode='existing', target=target_img) self.complete_and_wait() result = self.vm.qmp('query-block') @@ -353,9 +339,8 @@ class TestMirrorNoBacking(iotests.QMPTestCase): qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, '-F', 'raw', target_img) - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - mode='existing', target=target_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + mode='existing', target=target_img) self.wait_ready_and_cancel() result = self.vm.qmp('query-block') @@ -374,9 +359,8 @@ class TestMirrorNoBacking(iotests.QMPTestCase): % (TestMirrorNoBacking.image_len, target_backing_img), '-F', iotests.imgfmt, target_img) - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - mode='existing', target=target_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + mode='existing', target=target_img) self.complete_and_wait() result = self.vm.qmp('query-block') @@ -409,9 +393,8 @@ class TestMirrorResized(iotests.QMPTestCase): def test_complete_top(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', sync='top', - target=target_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='top', + target=target_img) self.complete_and_wait() result = self.vm.qmp('query-block') @@ -423,9 +406,8 @@ class TestMirrorResized(iotests.QMPTestCase): def test_complete_full(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - target=target_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + target=target_img) self.complete_and_wait() result = self.vm.qmp('query-block') @@ -488,9 +470,8 @@ new_state = "1" def test_report_read(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - target=target_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + target=target_img) completed = False error = False @@ -516,9 +497,8 @@ new_state = "1" def test_ignore_read(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - target=target_img, on_source_error='ignore') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + target=target_img, on_source_error='ignore') event = self.vm.get_qmp_event(wait=True) while event['event'] == 'JOB_STATUS_CHANGE': @@ -541,10 +521,9 @@ new_state = "1" qemu_img('create', '-f', iotests.imgfmt, '-ocluster_size=131072,backing_file=%s' %(backing_img), '-F', 'raw', target_img) - result = self.vm.qmp('drive-mirror', device='drive0', sync='top', - on_source_error='ignore', - mode='existing', target=target_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='top', + on_source_error='ignore', + mode='existing', target=target_img) event = self.vm.get_qmp_event(wait=True) while event['event'] == 'JOB_STATUS_CHANGE': @@ -568,9 +547,8 @@ new_state = "1" def test_stop_read(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - target=target_img, on_source_error='stop') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + target=target_img, on_source_error='stop') error = False ready = False @@ -590,8 +568,7 @@ new_state = "1" self.assert_qmp(result, 'return[0]/status', 'paused') self.assert_qmp(result, 'return[0]/io-status', 'failed') - result = self.vm.qmp('block-job-resume', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-resume', device='drive0') error = True elif event['event'] == 'BLOCK_JOB_READY': self.assertTrue(error, 'job completed unexpectedly') @@ -656,9 +633,8 @@ new_state = "1" def test_report_write(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - mode='existing', target=self.target_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + mode='existing', target=self.target_img) completed = False error = False @@ -682,10 +658,9 @@ new_state = "1" def test_ignore_write(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - mode='existing', target=self.target_img, - on_target_error='ignore') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + mode='existing', target=self.target_img, + on_target_error='ignore') event = self.vm.event_wait(name='BLOCK_JOB_ERROR') self.assertEqual(event['event'], 'BLOCK_JOB_ERROR') @@ -698,10 +673,9 @@ new_state = "1" def test_stop_write(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - mode='existing', target=self.target_img, - on_target_error='stop') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + mode='existing', target=self.target_img, + on_target_error='stop') error = False ready = False @@ -721,8 +695,7 @@ new_state = "1" self.assert_qmp(result, 'return[0]/status', 'paused') self.assert_qmp(result, 'return[0]/io-status', 'failed') - result = self.vm.qmp('block-job-resume', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-resume', device='drive0') result = self.vm.qmp('query-block-jobs') self.assertIn(result['return'][0]['status'], ['running', 'ready']) @@ -755,17 +728,15 @@ class TestSetSpeed(iotests.QMPTestCase): def test_set_speed(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - target=target_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + target=target_img) # Default speed is 0 result = self.vm.qmp('query-block-jobs') self.assert_qmp(result, 'return[0]/device', 'drive0') self.assert_qmp(result, 'return[0]/speed', 0) - result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) # Ensure the speed we set was accepted result = self.vm.qmp('query-block-jobs') @@ -775,9 +746,8 @@ class TestSetSpeed(iotests.QMPTestCase): self.wait_ready_and_cancel() # Check setting speed in drive-mirror works - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - target=target_img, speed=4*1024*1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + target=target_img, speed=4*1024*1024) result = self.vm.qmp('query-block-jobs') self.assert_qmp(result, 'return[0]/device', 'drive0') @@ -794,9 +764,8 @@ class TestSetSpeed(iotests.QMPTestCase): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - target=target_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + target=target_img) result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) self.assert_qmp(result, 'error/class', 'GenericError') @@ -811,13 +780,12 @@ class TestUnbackedSource(iotests.QMPTestCase): str(TestUnbackedSource.image_len)) self.vm = iotests.VM() self.vm.launch() - result = self.vm.qmp('blockdev-add', node_name='drive0', - driver=iotests.imgfmt, - file={ - 'driver': 'file', - 'filename': test_img, - }) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', node_name='drive0', + driver=iotests.imgfmt, + file={ + 'driver': 'file', + 'filename': test_img, + }) def tearDown(self): self.vm.shutdown() @@ -826,28 +794,25 @@ class TestUnbackedSource(iotests.QMPTestCase): def test_absolute_paths_full(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0', - sync='full', target=target_img, - mode='absolute-paths') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', job_id='drive0', device='drive0', + sync='full', target=target_img, + mode='absolute-paths') self.complete_and_wait() self.assert_no_active_block_jobs() def test_absolute_paths_top(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0', - sync='top', target=target_img, - mode='absolute-paths') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', job_id='drive0', device='drive0', + sync='top', target=target_img, + mode='absolute-paths') self.complete_and_wait() self.assert_no_active_block_jobs() def test_absolute_paths_none(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0', - sync='none', target=target_img, - mode='absolute-paths') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', job_id='drive0', device='drive0', + sync='none', target=target_img, + mode='absolute-paths') self.complete_and_wait() self.assert_no_active_block_jobs() @@ -857,14 +822,12 @@ class TestUnbackedSource(iotests.QMPTestCase): qemu_io('-c', 'write -P 42 0 64k', target_img) self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0', - sync='full', target=target_img, mode='existing') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', job_id='drive0', device='drive0', + sync='full', target=target_img, mode='existing') self.complete_and_wait() self.assert_no_active_block_jobs() - result = self.vm.qmp('blockdev-del', node_name='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', node_name='drive0') self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') @@ -874,26 +837,22 @@ class TestUnbackedSource(iotests.QMPTestCase): str(self.image_len)) qemu_io('-c', 'write -P 42 0 64k', target_img) - result = self.vm.qmp('blockdev-add', node_name='target', - driver=iotests.imgfmt, - file={ - 'driver': 'file', - 'filename': target_img, - }) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', node_name='target', + driver=iotests.imgfmt, + file={ + 'driver': 'file', + 'filename': target_img, + }) self.assert_no_active_block_jobs() - result = self.vm.qmp('blockdev-mirror', job_id='drive0', device='drive0', - sync='full', target='target') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-mirror', job_id='drive0', device='drive0', + sync='full', target='target') self.complete_and_wait() self.assert_no_active_block_jobs() - result = self.vm.qmp('blockdev-del', node_name='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', node_name='drive0') - result = self.vm.qmp('blockdev-del', node_name='target') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', node_name='target') self.assertTrue(iotests.compare_images(test_img, target_img), 'target image does not match source after mirroring') @@ -918,10 +877,9 @@ class TestGranularity(iotests.QMPTestCase): def test_granularity(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-mirror', device='drive0', - sync='full', target=target_img, - mode='absolute-paths', granularity=8192) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', + sync='full', target=target_img, + mode='absolute-paths', granularity=8192) event = self.vm.get_qmp_event(wait=60.0) while event['event'] == 'JOB_STATUS_CHANGE': @@ -963,8 +921,7 @@ class TestRepairQuorum(iotests.QMPTestCase): #assemble the quorum block device from the individual files args = { "driver": "quorum", "node-name": "quorum0", "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } - result = self.vm.qmp("blockdev-add", **args) - self.assert_qmp(result, 'return', {}) + self.vm.cmd("blockdev-add", args) def tearDown(self): @@ -978,10 +935,9 @@ class TestRepairQuorum(iotests.QMPTestCase): pass def test_complete(self): - result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', - sync='full', node_name="repair0", replaces="img1", - target=quorum_repair_img, format=iotests.imgfmt) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', job_id='job0', device='quorum0', + sync='full', node_name="repair0", replaces="img1", + target=quorum_repair_img, format=iotests.imgfmt) self.complete_and_wait(drive="job0") self.assert_has_block_node("repair0", quorum_repair_img) @@ -991,10 +947,9 @@ class TestRepairQuorum(iotests.QMPTestCase): 'target image does not match source after mirroring') def test_cancel(self): - result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', - sync='full', node_name="repair0", replaces="img1", - target=quorum_repair_img, format=iotests.imgfmt) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', job_id='job0', device='quorum0', + sync='full', node_name="repair0", replaces="img1", + target=quorum_repair_img, format=iotests.imgfmt) self.cancel_and_wait(drive="job0", force=True) # here we check that the last registered quorum file has not been @@ -1002,10 +957,9 @@ class TestRepairQuorum(iotests.QMPTestCase): self.assert_has_block_node(None, quorum_img3) def test_cancel_after_ready(self): - result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', - sync='full', node_name="repair0", replaces="img1", - target=quorum_repair_img, format=iotests.imgfmt) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', job_id='job0', device='quorum0', + sync='full', node_name="repair0", replaces="img1", + target=quorum_repair_img, format=iotests.imgfmt) self.wait_ready_and_cancel(drive="job0") # here we check that the last registered quorum file has not been @@ -1016,10 +970,9 @@ class TestRepairQuorum(iotests.QMPTestCase): 'target image does not match source after mirroring') def test_pause(self): - result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', - sync='full', node_name="repair0", replaces="img1", - target=quorum_repair_img, format=iotests.imgfmt) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', job_id='job0', device='quorum0', + sync='full', node_name="repair0", replaces="img1", + target=quorum_repair_img, format=iotests.imgfmt) self.pause_job('job0') @@ -1030,8 +983,7 @@ class TestRepairQuorum(iotests.QMPTestCase): result = self.vm.qmp('query-block-jobs') self.assert_qmp(result, 'return[0]/offset', offset) - result = self.vm.qmp('block-job-resume', device='job0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-resume', device='job0') self.complete_and_wait(drive="job0") self.vm.shutdown() @@ -1084,19 +1036,18 @@ class TestRepairQuorum(iotests.QMPTestCase): self.assert_qmp(result, 'error/class', 'GenericError') def test_after_a_quorum_snapshot(self): - result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1', - snapshot_file=quorum_snapshot_file, - snapshot_node_name="snap1"); + self.vm.cmd('blockdev-snapshot-sync', node_name='img1', + snapshot_file=quorum_snapshot_file, + snapshot_node_name="snap1") result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', sync='full', node_name='repair0', replaces="img1", target=quorum_repair_img, format=iotests.imgfmt) self.assert_qmp(result, 'error/class', 'GenericError') - result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', - sync='full', node_name='repair0', replaces="snap1", - target=quorum_repair_img, format=iotests.imgfmt) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', job_id='job0', device='quorum0', + sync='full', node_name='repair0', replaces="snap1", + target=quorum_repair_img, format=iotests.imgfmt) self.complete_and_wait('job0') self.assert_has_block_node("repair0", quorum_repair_img) @@ -1107,15 +1058,13 @@ class TestRepairQuorum(iotests.QMPTestCase): 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', {}) + self.vm.cmd('nbd-server-start', + addr={ + 'type': 'unix', + 'data': {'path': nbd_sock_path} + }) - result = self.vm.qmp('nbd-server-add', device='img1') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('nbd-server-add', device='img1') result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0', sync='full', node_name='repair0', replaces='img1', @@ -1130,20 +1079,17 @@ class TestRepairQuorum(iotests.QMPTestCase): 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', {}) + self.vm.cmd('nbd-server-start', + addr={ + 'type': 'unix', + 'data': {'path': nbd_sock_path} + }) - 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', {}) + self.vm.cmd('drive-mirror', job_id='mirror', device='quorum0', + sync='full', node_name='repair0', replaces='img1', + target=quorum_repair_img, format=iotests.imgfmt) - result = self.vm.qmp('nbd-server-add', device='img1') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('nbd-server-add', device='img1') # The full error message goes to stderr, we will check it later self.complete_and_wait('mirror', @@ -1199,9 +1145,8 @@ class TestOrphanedSource(iotests.QMPTestCase): def test_success(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('blockdev-mirror', job_id='job', device='src', - sync='full', target='dest') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-mirror', job_id='job', device='src', + sync='full', target='dest') self.complete_and_wait('job') @@ -1217,27 +1162,24 @@ class TestOrphanedSource(iotests.QMPTestCase): # 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', {}) + self.vm.cmd('blockdev-add', + driver='blkdebug', + node_name='dest-perm', + image='dest', + unshare_child_perms=['consistent-read']) - 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', {}) + self.vm.cmd('blockdev-mirror', job_id='job', device='src', + sync='full', target='dest', + filter_node_name='mirror-filter') # 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', {}) + self.vm.cmd('blockdev-add', + driver='blkdebug', + node_name='src-perm', + image='src', + take_child_perms=['consistent-read']) # While completing, mirror will attempt to replace src by # dest, which must fail because src-perm requires @@ -1277,26 +1219,23 @@ class TestReplaces(iotests.QMPTestCase): """ 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', {}) + self.vm.cmd('blockdev-add', { + 'driver': 'copy-on-read', + 'node-name': 'filter0', + 'file': { + 'driver': 'copy-on-read', + 'node-name': 'filter1', + 'file': { + 'driver': 'null-co' + } + } + }) - result = self.vm.qmp('blockdev-add', - node_name='target', driver='null-co') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', + node_name='target', driver='null-co') - result = self.vm.qmp('blockdev-mirror', job_id='mirror', device='filter0', - target='target', sync='full', replaces='filter1') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-mirror', job_id='mirror', device='filter0', + target='target', sync='full', replaces='filter1') self.complete_and_wait('mirror') @@ -1318,16 +1257,15 @@ class TestFilters(iotests.QMPTestCase): self.vm = iotests.VM().add_device('virtio-scsi,id=vio-scsi') self.vm.launch() - result = self.vm.qmp('blockdev-add', **{ - 'node-name': 'target', - 'driver': iotests.imgfmt, - 'file': { - 'driver': 'file', - 'filename': target_img - }, - 'backing': None - }) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', { + 'node-name': 'target', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': target_img + }, + 'backing': None + }) self.filterless_chain = { 'node-name': 'source', @@ -1354,19 +1292,17 @@ class TestFilters(iotests.QMPTestCase): os.remove(backing_img) def test_cor(self): - result = self.vm.qmp('blockdev-add', **{ - 'node-name': 'filter', - 'driver': 'copy-on-read', - 'file': self.filterless_chain - }) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', { + 'node-name': 'filter', + 'driver': 'copy-on-read', + 'file': self.filterless_chain + }) - result = self.vm.qmp('blockdev-mirror', - job_id='mirror', - device='filter', - target='target', - sync='top') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-mirror', + job_id='mirror', + device='filter', + target='target', + sync='top') self.complete_and_wait('mirror') @@ -1383,23 +1319,20 @@ class TestFilters(iotests.QMPTestCase): assert target_map[1]['depth'] == 0 def test_implicit_mirror_filter(self): - result = self.vm.qmp('blockdev-add', **self.filterless_chain) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', self.filterless_chain) # We need this so we can query from above the mirror node - result = self.vm.qmp('device_add', - driver='scsi-hd', - id='virtio', - bus='vio-scsi.0', - drive='source') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('device_add', + driver='scsi-hd', + id='virtio', + bus='vio-scsi.0', + drive='source') - result = self.vm.qmp('blockdev-mirror', - job_id='mirror', - device='source', - target='target', - sync='top') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-mirror', + job_id='mirror', + device='source', + target='target', + sync='top') # The mirror filter is now an implicit node, so it should be # invisible when querying the backing chain @@ -1417,24 +1350,21 @@ class TestFilters(iotests.QMPTestCase): def test_explicit_mirror_filter(self): # Same test as above, but this time we give the mirror filter # a node-name so it will not be invisible - result = self.vm.qmp('blockdev-add', **self.filterless_chain) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', self.filterless_chain) # We need this so we can query from above the mirror node - result = self.vm.qmp('device_add', - driver='scsi-hd', - id='virtio', - bus='vio-scsi.0', - drive='source') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('device_add', + driver='scsi-hd', + id='virtio', + bus='vio-scsi.0', + drive='source') - result = self.vm.qmp('blockdev-mirror', - job_id='mirror', - device='source', - target='target', - sync='top', - filter_node_name='mirror-filter') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-mirror', + job_id='mirror', + device='source', + target='target', + sync='top', + filter_node_name='mirror-filter') # With a node-name given to it, the mirror filter should now # be visible diff --git a/tests/qemu-iotests/045 b/tests/qemu-iotests/045 index 45eb239baa..a341f21cd7 100755 --- a/tests/qemu-iotests/045 +++ b/tests/qemu-iotests/045 @@ -77,8 +77,7 @@ class TestFdSets(iotests.QMPTestCase): self.vm.shutdown() def test_remove_fdset(self): - result = self.vm.qmp('remove-fd', fdset_id=2) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('remove-fd', fdset_id=2) result = self.vm.qmp('query-fdsets') self.assert_qmp(result, 'return[0]/fdset-id', 1) self.assert_qmp(result, 'return[1]/fdset-id', 0) @@ -90,8 +89,7 @@ class TestFdSets(iotests.QMPTestCase): def test_remove_fd(self): result = self.vm.qmp('query-fdsets') fd_image3 = result['return'][0]['fds'][0]['fd'] - result = self.vm.qmp('remove-fd', fdset_id=2, fd=fd_image3) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('remove-fd', fdset_id=2, fd=fd_image3) result = self.vm.qmp('query-fdsets') self.assert_qmp(result, 'return[0]/fdset-id', 2) self.assert_qmp(result, 'return[1]/fdset-id', 1) @@ -151,8 +149,7 @@ class TestSCMFd(iotests.QMPTestCase): def test_getfd(self): self._send_fd_by_SCM() - result = self.vm.qmp('getfd', fdname='image0:r') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('getfd', fdname='image0:r') def test_getfd_invalid_fdname(self): self._send_fd_by_SCM() @@ -163,10 +160,8 @@ class TestSCMFd(iotests.QMPTestCase): def test_closefd(self): self._send_fd_by_SCM() - result = self.vm.qmp('getfd', fdname='image0:r') - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('closefd', fdname='image0:r') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('getfd', fdname='image0:r') + self.vm.cmd('closefd', fdname='image0:r') def test_closefd_fd_not_found(self): fdname = 'image0:r' diff --git a/tests/qemu-iotests/046 b/tests/qemu-iotests/046 index 517b162508..4c9ed4d26e 100755 --- a/tests/qemu-iotests/046 +++ b/tests/qemu-iotests/046 @@ -125,7 +125,7 @@ aio_flush EOF # Sequential write, but the next cluster is already allocated -# and phyiscally in the right position +# and physically in the right position cat <file, not on the data_file, -# so thie test does not work with external data files +# so this test does not work with external data files _unsupported_imgopts data_file do_run_qemu() diff --git a/tests/qemu-iotests/071.out b/tests/qemu-iotests/071.out index bca0c02f5c..a2923b05c2 100644 --- a/tests/qemu-iotests/071.out +++ b/tests/qemu-iotests/071.out @@ -45,8 +45,8 @@ QMP_VERSION {"return": {}} read failed: Input/output error {"return": ""} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} === Testing blkverify on existing block device === @@ -84,9 +84,9 @@ wrote 512/512 bytes at offset 0 {"return": ""} read failed: Input/output error {"return": ""} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} QEMU_PROG: Failed to flush the L2 table cache: Input/output error QEMU_PROG: Failed to flush the refcount block cache: Input/output error +{"return": {}} *** done diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out index 45ab01db8e..d8acb3e723 100644 --- a/tests/qemu-iotests/080.out +++ b/tests/qemu-iotests/080.out @@ -33,7 +33,7 @@ qemu-io: can't open device TEST_DIR/t.qcow2: Snapshot table offset invalid == Hitting snapshot table size limit == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -qemu-img: Could not create snapshot 'test': -27 (File too large) +qemu-img: Could not create snapshot 'test': File too large read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -56,8 +56,8 @@ qemu-io: can't open device TEST_DIR/t.qcow2: Backing file name too long Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qemu-img: Could not create snapshot 'test': -27 (File too large) -qemu-img: Could not create snapshot 'test': -11 (Resource temporarily unavailable) +qemu-img: Could not create snapshot 'test': File too large +qemu-img: Could not create snapshot 'test': Resource temporarily unavailable == Invalid snapshot L1 table offset == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 diff --git a/tests/qemu-iotests/081.out b/tests/qemu-iotests/081.out index 615c083549..aba85ea564 100644 --- a/tests/qemu-iotests/081.out +++ b/tests/qemu-iotests/081.out @@ -35,8 +35,8 @@ QMP_VERSION read 10485760/10485760 bytes at offset 0 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": ""} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} == using quorum rewrite corrupted mode == @@ -67,8 +67,8 @@ QMP_VERSION read 10485760/10485760 bytes at offset 0 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": ""} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} -- checking that the image has been corrected -- read 10485760/10485760 bytes at offset 0 @@ -106,8 +106,8 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} Testing: QMP_VERSION @@ -115,8 +115,8 @@ QMP_VERSION {"return": {}} {"return": {}} {"error": {"class": "GenericError", "desc": "Cannot add a child to a quorum in blkverify mode"}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} == dynamically removing a child from a quorum == @@ -125,31 +125,31 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} Testing: QMP_VERSION {"return": {}} {"return": {}} {"error": {"class": "GenericError", "desc": "The number of children cannot be lower than the vote threshold 2"}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} Testing: QMP_VERSION {"return": {}} {"error": {"class": "GenericError", "desc": "blkverify=on can only be set if there are exactly two files and vote-threshold is 2"}} {"error": {"class": "GenericError", "desc": "Cannot find device='drive0-quorum' nor node-name='drive0-quorum'"}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} Testing: QMP_VERSION {"return": {}} {"return": {}} {"error": {"class": "GenericError", "desc": "The number of children cannot be lower than the vote threshold 2"}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} *** done diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out index e1c23a6983..97b6d8036d 100644 --- a/tests/qemu-iotests/087.out +++ b/tests/qemu-iotests/087.out @@ -7,8 +7,8 @@ Testing: QMP_VERSION {"return": {}} {"error": {"class": "GenericError", "desc": "'node-name' must be specified for the root node"}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} === Duplicate ID === @@ -18,8 +18,8 @@ QMP_VERSION {"return": {}} {"error": {"class": "GenericError", "desc": "node-name=disk is conflicting with a device id"}} {"error": {"class": "GenericError", "desc": "Duplicate nodes with node-name='test-node'"}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} === aio=native without O_DIRECT === @@ -28,8 +28,8 @@ Testing: QMP_VERSION {"return": {}} {"error": {"class": "GenericError", "desc": "aio=native was specified, but it requires cache.direct=on, which was not specified."}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} === Encrypted image QCow === @@ -40,8 +40,8 @@ QMP_VERSION {"return": {}} {"return": {}} {"error": {"class": "GenericError", "desc": "Use of AES-CBC encrypted IMGFMT images is no longer supported in system emulators"}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} === Encrypted image LUKS === @@ -52,8 +52,8 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} === Missing driver === @@ -63,7 +63,7 @@ Testing: -S QMP_VERSION {"return": {}} {"error": {"class": "GenericError", "desc": "Parameter 'driver' is missing"}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} *** done diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093 index 93274dc8cb..4f9e224e8a 100755 --- a/tests/qemu-iotests/093 +++ b/tests/qemu-iotests/093 @@ -55,8 +55,7 @@ class ThrottleTestCase(iotests.QMPTestCase): # Set the I/O throttling parameters to all drives for i in range(0, ndrives): params['device'] = 'drive%d' % i - result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params) - self.assert_qmp(result, 'return', {}) + self.vm.cmd("block_set_io_throttle", conv_keys=False, **params) def do_test_throttle(self, ndrives, seconds, params, first_drive = 0): def check_limit(limit, num): @@ -253,8 +252,7 @@ class ThrottleTestCase(iotests.QMPTestCase): # drive1 remains in the group with a throttled request. params['bps_rd'] = 0 params['device'] = 'drive0' - result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params) - self.assert_qmp(result, 'return', {}) + self.vm.cmd("block_set_io_throttle", conv_keys=False, **params) # Removing the I/O limits from drive0 drains its two pending requests. # The read request in drive1 is still throttled. @@ -286,8 +284,7 @@ class ThrottleTestGroupNames(iotests.QMPTestCase): def set_io_throttle(self, device, params): params["device"] = device - result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params) - self.assert_qmp(result, 'return', {}) + self.vm.cmd("block_set_io_throttle", conv_keys=False, **params) def verify_name(self, device, name): result = self.vm.qmp("query-block") @@ -379,23 +376,19 @@ class ThrottleTestRemovableMedia(iotests.QMPTestCase): def test_removable_media(self): # Add a couple of dummy nodes named cd0 and cd1 - result = self.vm.qmp("blockdev-add", driver="null-co", - read_zeroes=True, node_name="cd0") - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp("blockdev-add", driver="null-co", - read_zeroes=True, node_name="cd1") - self.assert_qmp(result, 'return', {}) + self.vm.cmd("blockdev-add", driver="null-co", + read_zeroes=True, node_name="cd0") + self.vm.cmd("blockdev-add", driver="null-co", + read_zeroes=True, node_name="cd1") # Attach a CD drive with cd0 inserted - result = self.vm.qmp("device_add", driver="scsi-cd", - id="dev0", drive="cd0") - self.assert_qmp(result, 'return', {}) + self.vm.cmd("device_add", driver="scsi-cd", + id="dev0", drive="cd0") # Set I/O limits args = { "id": "dev0", "iops": 100, "iops_rd": 0, "iops_wr": 0, "bps": 50, "bps_rd": 0, "bps_wr": 0 } - result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **args) - self.assert_qmp(result, 'return', {}) + self.vm.cmd("block_set_io_throttle", conv_keys=False, **args) # Check that the I/O limits have been set result = self.vm.qmp("query-block") @@ -403,12 +396,9 @@ class ThrottleTestRemovableMedia(iotests.QMPTestCase): self.assert_qmp(result, 'return[0]/inserted/bps', 50) # Now eject cd0 and insert cd1 - result = self.vm.qmp("blockdev-open-tray", id='dev0') - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp("blockdev-remove-medium", id='dev0') - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp("blockdev-insert-medium", id='dev0', node_name='cd1') - self.assert_qmp(result, 'return', {}) + self.vm.cmd("blockdev-open-tray", id='dev0') + self.vm.cmd("blockdev-remove-medium", id='dev0') + self.vm.cmd("blockdev-insert-medium", id='dev0', node_name='cd1') # Check that the I/O limits are still the same result = self.vm.qmp("query-block") @@ -416,16 +406,14 @@ class ThrottleTestRemovableMedia(iotests.QMPTestCase): self.assert_qmp(result, 'return[0]/inserted/bps', 50) # Eject cd1 - result = self.vm.qmp("blockdev-remove-medium", id='dev0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd("blockdev-remove-medium", id='dev0') # Check that we can't set limits if the device has no medium result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **args) self.assert_qmp(result, 'error/class', 'GenericError') # Remove the CD drive - result = self.vm.qmp("device_del", id='dev0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd("device_del", id='dev0') if __name__ == '__main__': diff --git a/tests/qemu-iotests/106 b/tests/qemu-iotests/106 index 9d6adb542d..ae0fc46691 100755 --- a/tests/qemu-iotests/106 +++ b/tests/qemu-iotests/106 @@ -66,7 +66,7 @@ for create_mode in off falloc full; do expected_size=$((expected_size + $GROWTH_SIZE)) fi - actual_size=$($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep 'disk size') + actual_size=$($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep 'disk size' | head -n 1) actual_size=$(echo "$actual_size" | sed -e 's/^[^0-9]*\([0-9]\+\).*$/\1/') # The actual size may exceed the expected size, depending on the file @@ -105,7 +105,7 @@ for growth_mode in falloc full; do _make_test_img -o "extent_size_hint=0" 2G $QEMU_IMG resize -f "$IMGFMT" --preallocation=$growth_mode "$TEST_IMG" +${GROWTH_SIZE}K - actual_size=$($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep 'disk size') + actual_size=$($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep 'disk size' | head -n 1) actual_size=$(echo "$actual_size" | sed -e 's/^[^0-9]*\([0-9]\+\).*$/\1/') if [ $actual_size -lt $GROWTH_SIZE ]; then diff --git a/tests/qemu-iotests/108 b/tests/qemu-iotests/108 index 9e923d6a59..54e935acf2 100755 --- a/tests/qemu-iotests/108 +++ b/tests/qemu-iotests/108 @@ -60,6 +60,11 @@ if sudo -n losetup &>/dev/null; then else loopdev=false + # Check for usable FUSE in the host environment: + if test ! -c "/dev/fuse"; then + _notrun 'No passwordless sudo nor usable /dev/fuse' + fi + # QSD --export fuse will either yield "Parameter 'id' is missing" # or "Invalid parameter 'fuse'", depending on whether there is # FUSE support or not. diff --git a/tests/qemu-iotests/108.out b/tests/qemu-iotests/108.out index b5401d788d..b9c876b394 100644 --- a/tests/qemu-iotests/108.out +++ b/tests/qemu-iotests/108.out @@ -173,8 +173,8 @@ OK: Reftable is where we expect it {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "create"}} {"return": {}} { "execute": "quit" } -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 index e207a555f3..0fb580f9a5 100755 --- a/tests/qemu-iotests/109 +++ b/tests/qemu-iotests/109 @@ -57,13 +57,13 @@ run_qemu() _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 \ + capture_events="$qmp_event" _send_qemu_cmd $QEMU_HANDLE \ "{'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': '$raw_img', $qmp_format 'mode': 'existing', 'sync': 'full'}}" \ "return" - _send_qemu_cmd $QEMU_HANDLE '' "$qmp_event" + capture_events="$qmp_event JOB_STATUS_CHANGE" _wait_event $QEMU_HANDLE "$qmp_event" if test "$qmp_event" = BLOCK_JOB_ERROR; then _send_qemu_cmd $QEMU_HANDLE '' '"status": "null"' fi diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out index e29280015e..3ae8552ff7 100644 --- a/tests/qemu-iotests/109.out +++ b/tests/qemu-iotests/109.out @@ -7,7 +7,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ - 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -23,8 +23,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"query-block-jobs"} {"return": []} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } @@ -35,19 +35,20 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} +{"return": {}} Images are identical. === Writing a qcow2 header into raw === @@ -57,7 +58,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ - 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -73,8 +74,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"query-block-jobs"} {"return": []} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } @@ -85,19 +86,20 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} +{"return": {}} Images are identical. === Writing a qed header into raw === @@ -107,7 +109,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ - 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -123,8 +125,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"query-block-jobs"} {"return": []} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } @@ -135,19 +137,20 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} +{"return": {}} Images are identical. === Writing a vdi header into raw === @@ -157,7 +160,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ - 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -173,8 +176,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"query-block-jobs"} {"return": []} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } @@ -185,19 +188,20 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} +{"return": {}} Images are identical. === Writing a vmdk header into raw === @@ -207,7 +211,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ - 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -223,8 +227,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"query-block-jobs"} {"return": []} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } @@ -235,19 +239,20 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} +{"return": {}} Images are identical. === Writing a vpc header into raw === @@ -257,7 +262,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ - 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -273,8 +278,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"query-block-jobs"} {"return": []} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } @@ -285,19 +290,20 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} +{"return": {}} Images are identical. === Copying sample image empty.bochs into raw === @@ -306,7 +312,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ - 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -322,8 +328,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"query-block-jobs"} {"return": []} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } @@ -334,19 +340,20 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} +{"return": {}} Images are identical. === Copying sample image iotest-dirtylog-10G-4M.vhdx into raw === @@ -355,7 +362,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ - 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -371,8 +378,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"query-block-jobs"} {"return": []} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } @@ -383,19 +390,20 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} +{"return": {}} Images are identical. === Copying sample image parallels-v1 into raw === @@ -404,7 +412,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ - 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -420,8 +428,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"query-block-jobs"} {"return": []} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } @@ -432,19 +440,20 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} +{"return": {}} Images are identical. === Copying sample image simple-pattern.cloop into raw === @@ -453,7 +462,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ - 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -469,8 +478,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"query-block-jobs"} {"return": []} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } @@ -481,19 +490,20 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} +{"return": {}} Images are identical. === Write legitimate MBR into raw === @@ -502,7 +512,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ - 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -510,19 +520,20 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} +{"return": {}} Images are identical. { 'execute': 'qmp_capabilities' } {"return": {}} @@ -532,18 +543,19 @@ Images are identical. {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} +{"return": {}} Images are identical. *** done diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out index dd3cc4383c..ebf426febc 100644 --- a/tests/qemu-iotests/112.out +++ b/tests/qemu-iotests/112.out @@ -32,7 +32,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 refcount bits: 1 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qemu-img: Could not create snapshot 'foo': -22 (Invalid argument) +qemu-img: Could not create snapshot 'foo': Invalid argument Leaked cluster 6 refcount=1 reference=0 1 leaked clusters were found on the image. @@ -44,7 +44,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 refcount bits: 2 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qemu-img: Could not create snapshot 'baz': -22 (Invalid argument) +qemu-img: Could not create snapshot 'baz': Invalid argument Leaked cluster 7 refcount=1 reference=0 1 leaked clusters were found on the image. @@ -75,7 +75,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 refcount bits: 64 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qemu-img: Could not create snapshot 'foo': -22 (Invalid argument) +qemu-img: Could not create snapshot 'foo': Invalid argument Leaked cluster 5 refcount=18446744073709551615 reference=1 Leaked cluster 6 refcount=1 reference=0 diff --git a/tests/qemu-iotests/114 b/tests/qemu-iotests/114 index de6fd327ee..dccc71008b 100755 --- a/tests/qemu-iotests/114 +++ b/tests/qemu-iotests/114 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 -_supported_proto generic +_supported_proto file # At least OpenBSD doesn't seem to have truncate _supported_os Linux # qcow2.py does not work too well with external data files diff --git a/tests/qemu-iotests/117.out b/tests/qemu-iotests/117.out index 735ffd25c6..1cea9e0217 100644 --- a/tests/qemu-iotests/117.out +++ b/tests/qemu-iotests/117.out @@ -18,8 +18,8 @@ wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": ""} { 'execute': 'quit' } -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} No errors were found on the image. read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118 index cae52ffa5e..6a4210c219 100755 --- a/tests/qemu-iotests/118 +++ b/tests/qemu-iotests/118 @@ -74,11 +74,9 @@ class ChangeBaseClass(iotests.QMPTestCase): class GeneralChangeTestsBaseClass(ChangeBaseClass): def test_blockdev_change_medium(self): - result = self.vm.qmp('blockdev-change-medium', - id=self.device_name, filename=new_img, - format=iotests.imgfmt) - - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-change-medium', + id=self.device_name, filename=new_img, + format=iotests.imgfmt) self.wait_for_open() self.wait_for_close() @@ -89,8 +87,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) def test_eject(self): - result = self.vm.qmp('eject', id=self.device_name, force=True) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('eject', id=self.device_name, force=True) self.wait_for_open() @@ -100,8 +97,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): self.assert_qmp_absent(result, 'return[0]/inserted') def test_tray_eject_change(self): - result = self.vm.qmp('eject', id=self.device_name, force=True) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('eject', id=self.device_name, force=True) self.wait_for_open() @@ -110,9 +106,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): self.assert_qmp(result, 'return[0]/tray_open', True) self.assert_qmp_absent(result, 'return[0]/inserted') - result = self.vm.qmp('blockdev-change-medium', id=self.device_name, - filename=new_img, format=iotests.imgfmt) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-change-medium', id=self.device_name, + filename=new_img, format=iotests.imgfmt) self.wait_for_close() @@ -122,9 +117,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) def test_tray_open_close(self): - result = self.vm.qmp('blockdev-open-tray', - id=self.device_name, force=True) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-open-tray', + id=self.device_name, force=True) self.wait_for_open() @@ -136,8 +130,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): else: self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) - result = self.vm.qmp('blockdev-close-tray', id=self.device_name) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-close-tray', id=self.device_name) if self.has_real_tray or not self.was_empty: self.wait_for_close() @@ -151,8 +144,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) def test_tray_eject_close(self): - result = self.vm.qmp('eject', id=self.device_name, force=True) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('eject', id=self.device_name, force=True) self.wait_for_open() @@ -161,8 +153,7 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): self.assert_qmp(result, 'return[0]/tray_open', True) self.assert_qmp_absent(result, 'return[0]/inserted') - result = self.vm.qmp('blockdev-close-tray', id=self.device_name) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-close-tray', id=self.device_name) self.wait_for_close() @@ -172,9 +163,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): self.assert_qmp_absent(result, 'return[0]/inserted') def test_tray_open_change(self): - result = self.vm.qmp('blockdev-open-tray', id=self.device_name, - force=True) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-open-tray', id=self.device_name, + force=True) self.wait_for_open() @@ -186,10 +176,9 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): else: self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) - result = self.vm.qmp('blockdev-change-medium', id=self.device_name, - filename=new_img, - format=iotests.imgfmt) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-change-medium', id=self.device_name, + filename=new_img, + format=iotests.imgfmt) self.wait_for_close() @@ -199,17 +188,15 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) def test_cycle(self, read_only_node=False): - result = self.vm.qmp('blockdev-add', - node_name='new', - driver=iotests.imgfmt, - read_only=read_only_node, - file={'filename': new_img, - 'driver': 'file'}) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', + node_name='new', + driver=iotests.imgfmt, + read_only=read_only_node, + file={'filename': new_img, + 'driver': 'file'}) - result = self.vm.qmp('blockdev-open-tray', - id=self.device_name, force=True) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-open-tray', + id=self.device_name, force=True) self.wait_for_open() @@ -221,26 +208,23 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): else: self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) - result = self.vm.qmp('blockdev-remove-medium', - id=self.device_name) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-remove-medium', + id=self.device_name) result = self.vm.qmp('query-block') if self.has_real_tray: self.assert_qmp(result, 'return[0]/tray_open', True) self.assert_qmp_absent(result, 'return[0]/inserted') - result = self.vm.qmp('blockdev-insert-medium', - id=self.device_name, node_name='new') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-insert-medium', + id=self.device_name, node_name='new') result = self.vm.qmp('query-block') if self.has_real_tray: self.assert_qmp(result, 'return[0]/tray_open', True) self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) - result = self.vm.qmp('blockdev-close-tray', id=self.device_name) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-close-tray', id=self.device_name) self.wait_for_close() @@ -253,9 +237,8 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): self.test_cycle(True) def test_close_on_closed(self): - result = self.vm.qmp('blockdev-close-tray', id=self.device_name) # Should be a no-op - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-close-tray', id=self.device_name) self.assertEqual(self.vm.get_qmp_events(wait=False), []) def test_remove_on_closed(self): @@ -269,12 +252,11 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): if not self.has_real_tray: return - result = self.vm.qmp('blockdev-add', - node_name='new', - driver=iotests.imgfmt, - file={'filename': new_img, - 'driver': 'file'}) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', + node_name='new', + driver=iotests.imgfmt, + file={'filename': new_img, + 'driver': 'file'}) result = self.vm.qmp('blockdev-insert-medium', id=self.device_name, node_name='new') @@ -295,7 +277,8 @@ class TestInitiallyFilled(GeneralChangeTestsBaseClass): 'file.driver=file', 'file.filename=%s' % old_img ]) if self.interface == 'scsi': - self.vm.add_device('virtio-scsi-pci') + self.vm.add_object('iothread,id=iothread0') + self.vm.add_device('virtio-scsi-pci,iothread=iothread0') self.vm.add_device('%s,drive=drive0,id=%s' % (interface_to_device_name(self.interface), self.device_name)) @@ -307,15 +290,13 @@ class TestInitiallyFilled(GeneralChangeTestsBaseClass): os.remove(new_img) def test_insert_on_filled(self): - result = self.vm.qmp('blockdev-add', - node_name='new', - driver=iotests.imgfmt, - file={'filename': new_img, - 'driver': 'file'}) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', + node_name='new', + driver=iotests.imgfmt, + file={'filename': new_img, + 'driver': 'file'}) - result = self.vm.qmp('blockdev-open-tray', id=self.device_name) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-open-tray', id=self.device_name) self.wait_for_open() @@ -332,7 +313,8 @@ class TestInitiallyEmpty(GeneralChangeTestsBaseClass): if self.use_drive: self.vm.add_drive(None, 'media=%s' % self.media, 'none') if self.interface == 'scsi': - self.vm.add_device('virtio-scsi-pci') + self.vm.add_object('iothread,id=iothread0') + self.vm.add_device('virtio-scsi-pci,iothread=iothread0') self.vm.add_device('%s,%sid=%s' % (interface_to_device_name(self.interface), 'drive=drive0,' if self.use_drive else '', @@ -344,14 +326,12 @@ class TestInitiallyEmpty(GeneralChangeTestsBaseClass): os.remove(new_img) def test_remove_on_empty(self): - result = self.vm.qmp('blockdev-open-tray', id=self.device_name) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-open-tray', id=self.device_name) self.wait_for_open() - result = self.vm.qmp('blockdev-remove-medium', id=self.device_name) # Should be a no-op - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-remove-medium', id=self.device_name) # Do this in a function to avoid leaking variables like case into the global # name space (otherwise tests would be run for the abstract base classes) @@ -399,11 +379,10 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/ro', True) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) - result = self.vm.qmp('blockdev-change-medium', id=self.device_name, - filename=new_img, - format=iotests.imgfmt, - read_only_mode='retain') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-change-medium', id=self.device_name, + filename=new_img, + format=iotests.imgfmt, + read_only_mode='retain') result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/ro', True) @@ -419,11 +398,10 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/ro', True) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) - result = self.vm.qmp('blockdev-change-medium', id=self.device_name, - filename=new_img, - format=iotests.imgfmt, - read_only_mode='retain') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-change-medium', id=self.device_name, + filename=new_img, + format=iotests.imgfmt, + read_only_mode='retain') result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/ro', True) @@ -462,12 +440,11 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/ro', True) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) - result = self.vm.qmp('blockdev-change-medium', - id=self.device_name, - filename=new_img, - format=iotests.imgfmt, - read_only_mode='read-write') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-change-medium', + id=self.device_name, + filename=new_img, + format=iotests.imgfmt, + read_only_mode='read-write') result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/ro', False) @@ -483,12 +460,11 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/ro', False) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) - result = self.vm.qmp('blockdev-change-medium', - id=self.device_name, - filename=new_img, - format=iotests.imgfmt, - read_only_mode='read-only') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-change-medium', + id=self.device_name, + filename=new_img, + format=iotests.imgfmt, + read_only_mode='read-only') result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/ro', True) @@ -503,12 +479,11 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/ro', False) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) - result = self.vm.qmp('blockdev-change-medium', - id=self.device_name, - filename=new_img, - format=iotests.imgfmt, - read_only_mode='read-only') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-change-medium', + id=self.device_name, + filename=new_img, + format=iotests.imgfmt, + read_only_mode='read-only') result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/ro', True) @@ -546,11 +521,10 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/ro', True) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) - result = self.vm.qmp('blockdev-change-medium', id=self.device_name, - filename=new_img, - format=iotests.imgfmt, - read_only_mode='retain') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-change-medium', id=self.device_name, + filename=new_img, + format=iotests.imgfmt, + read_only_mode='retain') result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/ro', True) @@ -587,27 +561,24 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/ro', False) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) - result = self.vm.qmp('blockdev-add', - node_name='new', - driver=iotests.imgfmt, - read_only=True, - file={'filename': new_img, - 'driver': 'file'}) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', + node_name='new', + driver=iotests.imgfmt, + read_only=True, + file={'filename': new_img, + 'driver': 'file'}) result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/ro', False) self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) - result = self.vm.qmp('blockdev-remove-medium', id=self.device_name) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-remove-medium', id=self.device_name) result = self.vm.qmp('query-block') self.assert_qmp_absent(result, 'return[0]/inserted') - result = self.vm.qmp('blockdev-insert-medium', id=self.device_name, - node_name='new') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-insert-medium', id=self.device_name, + node_name='new') result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/ro', True) @@ -638,22 +609,19 @@ class TestBlockJobsAfterCycle(ChangeBaseClass): # For device-less BBs, calling blockdev-open-tray or blockdev-close-tray # is not necessary - result = self.vm.qmp('blockdev-remove-medium', id=self.device_name) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-remove-medium', id=self.device_name) result = self.vm.qmp('query-block') self.assert_qmp_absent(result, 'return[0]/inserted') - result = self.vm.qmp('blockdev-add', - node_name='node0', - driver=iotests.imgfmt, - file={'filename': old_img, - 'driver': 'file'}) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', + node_name='node0', + driver=iotests.imgfmt, + file={'filename': old_img, + 'driver': 'file'}) - result = self.vm.qmp('blockdev-insert-medium', id=self.device_name, - node_name='node0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-insert-medium', id=self.device_name, + node_name='node0') result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) @@ -670,10 +638,9 @@ class TestBlockJobsAfterCycle(ChangeBaseClass): @iotests.skip_for_formats(('vpc', 'parallels', 'qcow', 'vdi', 'vmdk', 'raw', 'vhdx')) def test_snapshot_and_commit(self): - result = self.vm.qmp('blockdev-snapshot-sync', device='drive0', - snapshot_file=new_img, - format=iotests.imgfmt) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-snapshot-sync', device='drive0', + snapshot_file=new_img, + format=iotests.imgfmt) result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) @@ -681,16 +648,14 @@ class TestBlockJobsAfterCycle(ChangeBaseClass): 'return[0]/inserted/image/backing-image/filename', old_img) - result = self.vm.qmp('block-commit', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='drive0') self.vm.event_wait(name='BLOCK_JOB_READY') result = self.vm.qmp('query-block-jobs') self.assert_qmp(result, 'return[0]/device', 'drive0') - result = self.vm.qmp('block-job-complete', device='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-complete', device='drive0') self.vm.event_wait(name='BLOCK_JOB_COMPLETED') diff --git a/tests/qemu-iotests/120.out b/tests/qemu-iotests/120.out index 0744c1f136..35d84a5bc5 100644 --- a/tests/qemu-iotests/120.out +++ b/tests/qemu-iotests/120.out @@ -5,8 +5,8 @@ QMP_VERSION wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": ""} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 65536/65536 bytes at offset 0 diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out index e18766e167..6a1aa3fe2b 100644 --- a/tests/qemu-iotests/122.out +++ b/tests/qemu-iotests/122.out @@ -67,12 +67,12 @@ read 65536/65536 bytes at offset 4194304 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 65536/65536 bytes at offset 8388608 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true}, -{ "start": 65536, "length": 4128768, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 4194304, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true}, -{ "start": 4259840, "length": 4128768, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 8388608, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true}, -{ "start": 8454144, "length": 4128768, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, +{ "start": 65536, "length": 4128768, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 4194304, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, +{ "start": 4259840, "length": 4128768, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 8388608, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, +{ "start": 8454144, "length": 4128768, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 65536/65536 bytes at offset 4194304 @@ -94,12 +94,12 @@ wrote 1024/1024 bytes at offset 1046528 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 1024/1024 bytes at offset 0 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true}, -{ "start": 65536, "length": 65536, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 131072, "length": 196608, "depth": 0, "present": true, "zero": false, "data": true}, -{ "start": 327680, "length": 655360, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 983040, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true}, -{ "start": 1048576, "length": 1046528, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, +{ "start": 65536, "length": 65536, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 131072, "length": 196608, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, +{ "start": 327680, "length": 655360, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 983040, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, +{ "start": 1048576, "length": 1046528, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] read 16384/16384 bytes at offset 0 16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 16384/16384 bytes at offset 16384 @@ -130,14 +130,14 @@ read 3145728/3145728 bytes at offset 0 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 63963136/63963136 bytes at offset 3145728 61 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] convert -c -S 0: read 3145728/3145728 bytes at offset 0 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 63963136/63963136 bytes at offset 3145728 61 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true}] +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}] Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 wrote 33554432/33554432 bytes at offset 0 32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -152,7 +152,7 @@ read 30408704/30408704 bytes at offset 3145728 29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 33554432/33554432 bytes at offset 33554432 32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] convert -c -S 0 with source backing file: read 3145728/3145728 bytes at offset 0 @@ -161,7 +161,7 @@ read 30408704/30408704 bytes at offset 3145728 29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 33554432/33554432 bytes at offset 33554432 32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true}] +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}] convert -S 0 -B ... read 3145728/3145728 bytes at offset 0 @@ -170,7 +170,7 @@ read 30408704/30408704 bytes at offset 3145728 29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 33554432/33554432 bytes at offset 33554432 32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] convert -c -S 0 -B ... read 3145728/3145728 bytes at offset 0 @@ -179,7 +179,7 @@ read 30408704/30408704 bytes at offset 3145728 29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 33554432/33554432 bytes at offset 33554432 32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true}] +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}] === Non-zero -S === @@ -196,32 +196,32 @@ wrote 1024/1024 bytes at offset 66560 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) convert -S 4k -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 4096, "length": 4096, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 8192, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 12288, "length": 4096, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 16384, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 20480, "length": 67088384, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 4096, "length": 4096, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 8192, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 12288, "length": 4096, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 16384, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 20480, "length": 67088384, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] convert -c -S 4k -[{ "start": 0, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true}, -{ "start": 1024, "length": 7168, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 8192, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true}, -{ "start": 9216, "length": 8192, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 17408, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true}, -{ "start": 18432, "length": 67090432, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, +{ "start": 1024, "length": 7168, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 8192, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, +{ "start": 9216, "length": 8192, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 17408, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, +{ "start": 18432, "length": 67090432, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] convert -S 8k -[{ "start": 0, "length": 24576, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 24576, "length": 67084288, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 24576, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 24576, "length": 67084288, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] convert -c -S 8k -[{ "start": 0, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true}, -{ "start": 1024, "length": 7168, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 8192, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true}, -{ "start": 9216, "length": 8192, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 17408, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true}, -{ "start": 18432, "length": 67090432, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, +{ "start": 1024, "length": 7168, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 8192, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, +{ "start": 9216, "length": 8192, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 17408, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, +{ "start": 18432, "length": 67090432, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] === -n to a non-zero image === @@ -235,18 +235,18 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -[{ "start": 0, "length": 67108864, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 67108864, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] === -n to an empty image with a backing file === Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}] +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}] === -n -B to an image without a backing file === diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124 index 845ab5303c..b2f4328e34 100755 --- a/tests/qemu-iotests/124 +++ b/tests/qemu-iotests/124 @@ -24,6 +24,7 @@ import os import iotests from iotests import try_remove +from qemu.qmp.qmp_client import ExecuteError def io_write_patterns(img, patterns): @@ -141,8 +142,7 @@ class TestIncrementalBackupBase(iotests.QMPTestCase): def do_qmp_backup(self, error='Input/output error', **kwargs): - res = self.vm.qmp('drive-backup', **kwargs) - self.assert_qmp(res, 'return', {}) + self.vm.cmd('drive-backup', **kwargs) return self.wait_qmp_backup(kwargs['device'], error) @@ -201,9 +201,8 @@ class TestIncrementalBackupBase(iotests.QMPTestCase): def add_bitmap(self, name, drive, **kwargs): bitmap = Bitmap(name, drive) self.bitmaps.append(bitmap) - result = self.vm.qmp('block-dirty-bitmap-add', node=drive['id'], - name=bitmap.name, **kwargs) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-dirty-bitmap-add', node=drive['id'], + name=bitmap.name, **kwargs) return bitmap @@ -388,13 +387,12 @@ class TestIncrementalBackup(TestIncrementalBackupBase): ('0x64', '32736k', '64k'))) bitmap1 = self.add_bitmap('bitmap1', drive0) - result = self.vm.qmp('transaction', actions=[ + self.vm.cmd('transaction', actions=[ transaction_bitmap_clear(bitmap0.drive['id'], bitmap0.name), transaction_bitmap_clear(bitmap1.drive['id'], bitmap1.name), transaction_drive_backup(drive0['id'], drive0['backup'], sync='full', format=drive0['fmt']) ]) - self.assert_qmp(result, 'return', {}) self.wait_until_completed(drive0['id']) self.files.append(drive0['backup']) @@ -417,7 +415,7 @@ class TestIncrementalBackup(TestIncrementalBackupBase): ('0xcd', '32M', '124k'))) # Create a blkdebug interface to this img as 'drive1' - result = self.vm.qmp('blockdev-add', + self.vm.cmd('blockdev-add', node_name=drive1['id'], driver=drive1['fmt'], file={ @@ -440,7 +438,6 @@ class TestIncrementalBackup(TestIncrementalBackupBase): }], } ) - self.assert_qmp(result, 'return', {}) # Create bitmaps and full backups for both drives drive0 = self.drives[0] @@ -475,9 +472,8 @@ class TestIncrementalBackup(TestIncrementalBackupBase): format=drive1['fmt'], mode='existing', bitmap=dr1bm0.name) ] - result = self.vm.qmp('transaction', actions=transaction, - properties={'completion-mode': 'grouped'} ) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('transaction', actions=transaction, + properties={'completion-mode': 'grouped'} ) # Observe that drive0's backup is cancelled and drive1 completes with # an error. @@ -504,9 +500,8 @@ class TestIncrementalBackup(TestIncrementalBackupBase): target1 = self.prepare_backup(dr1bm0) # Re-run the exact same transaction. - result = self.vm.qmp('transaction', actions=transaction, - properties={'completion-mode':'grouped'}) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('transaction', actions=transaction, + properties={'completion-mode':'grouped'}) # Both should complete successfully this time. self.assertTrue(self.wait_qmp_backup(drive0['id'])) @@ -567,7 +562,7 @@ class TestIncrementalBackup(TestIncrementalBackupBase): The granularity must always be a power of 2. ''' self.assert_no_active_block_jobs() - self.assertRaises(AssertionError, self.add_bitmap, + self.assertRaises(ExecuteError, self.add_bitmap, 'bitmap0', self.drives[0], granularity=64000) @@ -585,9 +580,8 @@ class TestIncrementalBackup(TestIncrementalBackupBase): self.add_bitmap('bitmap0', self.drives[0]) - res = self.vm.qmp('block_resize', device=self.drives[0]['id'], - size=(65 * 1048576)) - self.assert_qmp(res, 'return', {}) + self.vm.cmd('block_resize', device=self.drives[0]['id'], + size=(65 * 1048576)) # Dirty the image past the old end self.vm.hmp_qemu_io(self.drives[0]['id'], 'write 64M 64k') @@ -617,7 +611,7 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): ''' drive0 = self.drives[0] - result = self.vm.qmp('blockdev-add', + self.vm.cmd('blockdev-add', node_name=drive0['id'], driver=drive0['fmt'], file={ @@ -640,7 +634,6 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): }], } ) - self.assert_qmp(result, 'return', {}) self.create_anchor_backup(drive0) self.add_bitmap('bitmap0', drive0) @@ -668,29 +661,28 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): drive0 = self.drives[0] # NB: The blkdebug script here looks for a "flush, read" pattern. # The flush occurs in hmp_io_writes, and the read during the block job. - result = self.vm.qmp('blockdev-add', - node_name=drive0['id'], - driver=drive0['fmt'], - file={ - 'driver': 'blkdebug', - 'image': { - 'driver': 'file', - 'filename': drive0['file'] - }, - 'set-state': [{ - 'event': 'flush_to_disk', - 'state': 1, - 'new_state': 2 - }], - 'inject-error': [{ - 'event': 'read_aio', - 'errno': 5, - 'state': 2, - 'immediately': False, - 'once': True - }], - }) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', + node_name=drive0['id'], + driver=drive0['fmt'], + file={ + 'driver': 'blkdebug', + 'image': { + 'driver': 'file', + 'filename': drive0['file'] + }, + 'set-state': [{ + 'event': 'flush_to_disk', + 'state': 1, + 'new_state': 2 + }], + 'inject-error': [{ + 'event': 'read_aio', + 'errno': 5, + 'state': 2, + 'immediately': False, + 'once': True + }], + }) self.create_anchor_backup(drive0) bitmap = self.add_bitmap('bitmap0', drive0) @@ -711,16 +703,15 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): # Start backup parent, _ = bitmap.last_target() target = self.prepare_backup(bitmap, parent) - res = self.vm.qmp('drive-backup', - job_id=bitmap.drive['id'], - device=bitmap.drive['id'], - sync='incremental', - bitmap=bitmap.name, - format=bitmap.drive['fmt'], - target=target, - mode='existing', - on_source_error='stop') - self.assert_qmp(res, 'return', {}) + self.vm.cmd('drive-backup', + job_id=bitmap.drive['id'], + device=bitmap.drive['id'], + sync='incremental', + bitmap=bitmap.name, + format=bitmap.drive['fmt'], + target=target, + mode='existing', + on_source_error='stop') # Wait for the error event = self.vm.event_wait(name="BLOCK_JOB_ERROR", @@ -739,8 +730,7 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): })) # Resume and check incremental backup for consistency - res = self.vm.qmp('block-job-resume', device=bitmap.drive['id']) - self.assert_qmp(res, 'return', {}) + self.vm.cmd('block-job-resume', device=bitmap.drive['id']) self.wait_qmp_backup(bitmap.drive['id']) # Bitmap Status Check diff --git a/tests/qemu-iotests/127.out b/tests/qemu-iotests/127.out index 1685c4850a..dd8c4a8aa9 100644 --- a/tests/qemu-iotests/127.out +++ b/tests/qemu-iotests/127.out @@ -28,6 +28,6 @@ wrote 42/42 bytes at offset 0 { 'execute': 'quit' } {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "mirror"}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} *** done diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129 index c75ec62ecf..97773cd96d 100755 --- a/tests/qemu-iotests/129 +++ b/tests/qemu-iotests/129 @@ -55,11 +55,9 @@ class TestStopWithBlockJob(iotests.QMPTestCase): def do_test_stop(self, cmd, **args): """Test 'stop' while block job is running on a throttled drive. The 'stop' command shouldn't drain the job""" - result = self.vm.qmp(cmd, **args) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(cmd, **args) - result = self.vm.qmp("stop") - self.assert_qmp(result, 'return', {}) + self.vm.cmd("stop") result = self.vm.qmp("query-block-jobs") self.assert_qmp(result, 'return[0]/status', 'running') @@ -87,7 +85,7 @@ class TestStopWithBlockJob(iotests.QMPTestCase): iotests.qemu_img('create', '-f', iotests.imgfmt, self.overlay_img, '1G') - result = self.vm.qmp('blockdev-add', **{ + self.vm.cmd('blockdev-add', { 'node-name': 'overlay', 'driver': iotests.imgfmt, 'file': { @@ -95,11 +93,9 @@ class TestStopWithBlockJob(iotests.QMPTestCase): 'filename': self.overlay_img } }) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('blockdev-snapshot', - node='source', overlay='overlay') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-snapshot', + node='source', overlay='overlay') self.do_test_stop('block-commit', device='drive0', top_node='source') diff --git a/tests/qemu-iotests/130 b/tests/qemu-iotests/130 index 7257f09677..7af85d20a8 100755 --- a/tests/qemu-iotests/130 +++ b/tests/qemu-iotests/130 @@ -42,7 +42,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.qemu _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux # We are going to use lazy-refcounts _unsupported_imgopts 'compat=0.10' diff --git a/tests/qemu-iotests/131 b/tests/qemu-iotests/131 index d7456cae5b..3119100e78 100755 --- a/tests/qemu-iotests/131 +++ b/tests/qemu-iotests/131 @@ -43,32 +43,121 @@ _supported_os Linux inuse_offset=$((0x2c)) -size=64M -CLUSTER_SIZE=64k +size=$((64 * 1024 * 1024)) IMGFMT=parallels _make_test_img $size -echo == read empty image == -{ $QEMU_IO -c "read -P 0 32k 64k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir -echo == write more than 1 block in a row == -{ $QEMU_IO -c "write -P 0x11 32k 128k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir -echo == read less than block == -{ $QEMU_IO -c "read -P 0x11 32k 32k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir -echo == read exactly 1 block == -{ $QEMU_IO -c "read -P 0x11 64k 64k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir -echo == read more than 1 block == -{ $QEMU_IO -c "read -P 0x11 32k 128k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir -echo == check that there is no trash after written == -{ $QEMU_IO -c "read -P 0 160k 32k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir -echo == check that there is no trash before written == -{ $QEMU_IO -c "read -P 0 0 32k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +# get cluster size in sectors from "tracks" header field +CLUSTER_SIZE_OFFSET=28 +CLUSTER_SIZE=$(peek_file_le $TEST_IMG $CLUSTER_SIZE_OFFSET 4) +CLUSTER_SIZE=$((CLUSTER_SIZE * 512)) +CLUSTER_HALF_SIZE=$((CLUSTER_SIZE / 2)) +CLUSTER_DBL_SIZE=$((CLUSTER_SIZE * 2)) -echo "== Corrupt image ==" +echo == read empty image == +{ $QEMU_IO -c "read -P 0 $CLUSTER_HALF_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +echo == write more than 1 block in a row == +{ $QEMU_IO -c "write -P 0x11 $CLUSTER_HALF_SIZE $CLUSTER_DBL_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +echo == read less than block == +{ $QEMU_IO -c "read -P 0x11 $CLUSTER_HALF_SIZE $CLUSTER_HALF_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +echo == read exactly 1 block == +{ $QEMU_IO -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +echo == read more than 1 block == +{ $QEMU_IO -c "read -P 0x11 $CLUSTER_HALF_SIZE $CLUSTER_DBL_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +echo == check that there is no trash after written == +{ $QEMU_IO -c "read -P 0 $((CLUSTER_HALF_SIZE + CLUSTER_DBL_SIZE)) $CLUSTER_HALF_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +echo == check that there is no trash before written == +{ $QEMU_IO -c "read -P 0 0 $CLUSTER_HALF_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== corrupt image ==" poke_file "$TEST_IMG" "$inuse_offset" "\x59\x6e\x6f\x74" -{ $QEMU_IO -c "read -P 0x11 64k 64k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir -_check_test_img -_check_test_img -r all -{ $QEMU_IO -c "read -P 0x11 64k 64k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +echo "== read corrupted image with repairing ==" +{ $QEMU_IO -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check discard ==" + +# Clear image +_make_test_img $size + +{ $QEMU_IO -c "write -P 0x11 0 $CLUSTER_DBL_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG map "$TEST_IMG"; } 2>&1 | _filter_qemu_img_map +{ $QEMU_IO -c "discard 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG map "$TEST_IMG"; } 2>&1 | _filter_qemu_img_map +{ $QEMU_IO -c "read -P 0 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check simple allocation over the discarded hole ==" + +{ $QEMU_IO -c "write -P 0x11 $CLUSTER_DBL_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG map "$TEST_IMG"; } 2>&1 | _filter_qemu_img_map +{ $QEMU_IO -c "read -P 0x11 $CLUSTER_DBL_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check more complex allocation over the discard hole ==" + +# Clear image +_make_test_img $size + +{ $QEMU_IO -c "write -P 0x11 $CLUSTER_DBL_SIZE $CLUSTER_DBL_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "discard $CLUSTER_DBL_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +# There is 1 cluster hole. Fill it fully and allocate 1 cluster at the end +{ $QEMU_IO -c "write -P 0x12 $CLUSTER_HALF_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG map "$TEST_IMG"; } 2>&1 | _filter_qemu_img_map +{ $QEMU_IO -c "read -P 0x12 $CLUSTER_HALF_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "read -P 0 0 $CLUSTER_HALF_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "read -P 0 $((CLUSTER_SIZE + CLUSTER_HALF_SIZE)) $CLUSTER_HALF_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check write-zeroes ==" + +# Clear image +_make_test_img $size + +{ $QEMU_IO -c "write -P 0x11 0 $CLUSTER_DBL_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "write -z 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG map "$TEST_IMG"; } 2>&1 | _filter_qemu_img_map +{ $QEMU_IO -c "read -P 0 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check cluster-partial write-zeroes ==" + +# Clear image +_make_test_img $size + +{ $QEMU_IO -c "write -P 0x11 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "write -z 0 $CLUSTER_HALF_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "read -P 0 0 $CLUSTER_HALF_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "read -P 0x11 $CLUSTER_HALF_SIZE $CLUSTER_HALF_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== allocate with backing ==" +# Verify that allocating clusters works fine even when there is a backing image. +# Regression test for a bug where we would pass a buffer read from the backing +# node as a QEMUIOVector object, which could cause anything from I/O errors over +# assertion failures to invalid reads from memory. + +# Clear image +_make_test_img $size +# Create base image +TEST_IMG="$TEST_IMG.base" _make_test_img $size + +# Write some data to the base image (which would trigger an assertion failure if +# interpreted as a QEMUIOVector) +$QEMU_IO -c "write -P 42 0 $CLUSTER_SIZE" "$TEST_IMG.base" | _filter_qemu_io + +# Parallels does not seem to support storing a backing filename in the image +# itself, so we need to build our backing chain on the command line +imgopts="driver=$IMGFMT,file.driver=$IMGPROTO,file.filename=$TEST_IMG" +imgopts+=",backing.driver=$IMGFMT" +imgopts+=",backing.file.driver=$IMGPROTO,backing.file.filename=$TEST_IMG.base" + +# Cause allocation in the top image +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT \ + $QEMU_IO --image-opts "$imgopts" -c 'write -P 1 0 64' | _filter_qemu_io + +# Verify +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT \ + $QEMU_IO --image-opts "$imgopts" \ + -c 'read -P 1 0 64' \ + -c "read -P 42 64 $((CLUSTER_SIZE - 64))" \ + -c "read -P 0 $CLUSTER_SIZE $((size - CLUSTER_SIZE))" \ + | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/131.out b/tests/qemu-iotests/131.out index 70da03dee5..86a2d2a49b 100644 --- a/tests/qemu-iotests/131.out +++ b/tests/qemu-iotests/131.out @@ -1,40 +1,102 @@ QA output created by 131 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 == read empty image == -read 65536/65536 bytes at offset 32768 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 524288 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == write more than 1 block in a row == -wrote 131072/131072 bytes at offset 32768 -128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 524288 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == read less than block == -read 32768/32768 bytes at offset 32768 -32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 524288 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == read exactly 1 block == -read 65536/65536 bytes at offset 65536 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == read more than 1 block == -read 131072/131072 bytes at offset 32768 -128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2097152/2097152 bytes at offset 524288 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == check that there is no trash after written == -read 32768/32768 bytes at offset 163840 -32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 2621440 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == check that there is no trash before written == -read 32768/32768 bytes at offset 0 -32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -== Corrupt image == -qemu-io: can't open device TEST_DIR/t.parallels: parallels: Image was not closed correctly; cannot be opened read/write -ERROR image was not closed correctly - -1 errors were found on the image. -Data may be corrupted, or further writes to the image may corrupt it. +read 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== corrupt image == +== read corrupted image with repairing == Repairing image was not closed correctly -The following inconsistencies were found and repaired: - - 0 leaked clusters - 1 corruptions - -Double checking the fixed image now... -No errors were found on the image. -read 65536/65536 bytes at offset 65536 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== check discard == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 2097152/2097152 bytes at offset 0 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0 0x200000 TEST_DIR/t.IMGFMT +discard 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0x100000 0x100000 TEST_DIR/t.IMGFMT +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== check simple allocation over the discarded hole == +wrote 1048576/1048576 bytes at offset 2097152 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0x100000 0x100000 TEST_DIR/t.IMGFMT +0x200000 0x100000 TEST_DIR/t.IMGFMT +read 1048576/1048576 bytes at offset 2097152 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== check more complex allocation over the discard hole == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 2097152/2097152 bytes at offset 2097152 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 1048576/1048576 bytes at offset 2097152 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1048576/1048576 bytes at offset 524288 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0 0x100000 TEST_DIR/t.IMGFMT +0x100000 0x100000 TEST_DIR/t.IMGFMT +0x300000 0x100000 TEST_DIR/t.IMGFMT +read 1048576/1048576 bytes at offset 524288 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 1572864 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== check write-zeroes == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 2097152/2097152 bytes at offset 0 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0x100000 0x100000 TEST_DIR/t.IMGFMT +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== check cluster-partial write-zeroes == +Formatting 'TEST_DIR/t.IMGFMT', 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) +wrote 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 524288 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== allocate with backing == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.base', 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) +wrote 64/64 bytes at offset 0 +64 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 64/64 bytes at offset 0 +64 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048512/1048512 bytes at offset 64 +1023.938 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 66060288/66060288 bytes at offset 1048576 +63 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done diff --git a/tests/qemu-iotests/132 b/tests/qemu-iotests/132 index 367ea08036..12a64b3d95 100755 --- a/tests/qemu-iotests/132 +++ b/tests/qemu-iotests/132 @@ -47,9 +47,8 @@ class TestSingleDrive(iotests.QMPTestCase): pass def test_mirror_discard(self): - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - target=target_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + target=target_img) self.vm.hmp_qemu_io('drive0', 'discard 0 64k') self.complete_and_wait('drive0') self.vm.shutdown() diff --git a/tests/qemu-iotests/134 b/tests/qemu-iotests/134 index ded153c0b9..b2c3c03f08 100755 --- a/tests/qemu-iotests/134 +++ b/tests/qemu-iotests/134 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow qcow2 -_supported_proto generic +_supported_proto file size=128M diff --git a/tests/qemu-iotests/139 b/tests/qemu-iotests/139 index 178b1ee230..ebb4cd62b6 100755 --- a/tests/qemu-iotests/139 +++ b/tests/qemu-iotests/139 @@ -58,8 +58,7 @@ class TestBlockdevDel(iotests.QMPTestCase): 'file': {'driver': 'file', 'node-name': file_node, 'filename': base_img}} - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) self.checkBlockDriverState(node) self.checkBlockDriverState(file_node) @@ -73,8 +72,7 @@ class TestBlockdevDel(iotests.QMPTestCase): 'backing': None, 'file': {'driver': 'file', 'filename': new_img}} - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) self.checkBlockDriverState(node) # Delete a BlockDriverState @@ -89,17 +87,14 @@ class TestBlockdevDel(iotests.QMPTestCase): # Add a device model def addDeviceModel(self, device, backend, driver = 'virtio-blk'): - result = self.vm.qmp('device_add', id = device, - driver = driver, drive = backend) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('device_add', id = device, + driver = driver, drive = backend) # Delete a device model def delDeviceModel(self, device, is_virtio_blk = True): - result = self.vm.qmp('device_del', id = device) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('device_del', id = device) - result = self.vm.qmp('system_reset') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('system_reset') if is_virtio_blk: device_path = '/machine/peripheral/%s/virtio-backend' % device @@ -126,9 +121,8 @@ class TestBlockdevDel(iotests.QMPTestCase): # Insert a BlockDriverState def insertDrive(self, device, node): self.checkBlockDriverState(node) - result = self.vm.qmp('blockdev-insert-medium', - id = device, node_name = node) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-insert-medium', + id = device, node_name = node) self.checkBlockDriverState(node) # Create a snapshot using 'blockdev-snapshot-sync' @@ -139,8 +133,7 @@ class TestBlockdevDel(iotests.QMPTestCase): 'snapshot-file': new_img, 'snapshot-node-name': overlay, 'format': iotests.imgfmt} - result = self.vm.qmp('blockdev-snapshot-sync', conv_keys=False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-snapshot-sync', conv_keys=False, **opts) self.checkBlockDriverState(node) self.checkBlockDriverState(overlay) @@ -148,9 +141,8 @@ class TestBlockdevDel(iotests.QMPTestCase): def createSnapshot(self, node, overlay): self.checkBlockDriverState(node) self.checkBlockDriverState(overlay) - result = self.vm.qmp('blockdev-snapshot', - node = node, overlay = overlay) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-snapshot', + node = node, overlay = overlay) self.checkBlockDriverState(node) self.checkBlockDriverState(overlay) @@ -163,14 +155,12 @@ class TestBlockdevDel(iotests.QMPTestCase): 'node-name': new_node, 'sync': 'top', 'format': iotests.imgfmt} - result = self.vm.qmp('drive-mirror', conv_keys=False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-mirror', conv_keys=False, **opts) self.checkBlockDriverState(new_node) # Complete an existing block job def completeBlockJob(self, id, node_before, node_after): - result = self.vm.qmp('block-job-complete', device=id) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-complete', device=id) self.wait_until_completed(id) # Add a BlkDebug node @@ -186,8 +176,7 @@ class TestBlockdevDel(iotests.QMPTestCase): opts = {'driver': 'blkdebug', 'node-name': debug, 'image': image} - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) self.checkBlockDriverState(node) self.checkBlockDriverState(debug) @@ -211,8 +200,7 @@ class TestBlockdevDel(iotests.QMPTestCase): 'node-name': blkverify, 'test': node_0, 'raw': node_1} - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) self.checkBlockDriverState(test) self.checkBlockDriverState(raw) self.checkBlockDriverState(blkverify) @@ -235,8 +223,7 @@ class TestBlockdevDel(iotests.QMPTestCase): 'node-name': quorum, 'vote-threshold': 1, 'children': [ child_0, child_1 ]} - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) self.checkBlockDriverState(child0) self.checkBlockDriverState(child1) self.checkBlockDriverState(quorum) diff --git a/tests/qemu-iotests/140.out b/tests/qemu-iotests/140.out index 312f76d5da..32866440ae 100644 --- a/tests/qemu-iotests/140.out +++ b/tests/qemu-iotests/140.out @@ -19,6 +19,6 @@ read 65536/65536 bytes at offset 0 qemu-io: can't open device nbd+unix:///drv?socket=SOCK_DIR/nbd: Requested export not available server reported: export 'drv' not present { 'execute': 'quit' } -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} *** done diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141 index a37030ee17..a7d3985a02 100755 --- a/tests/qemu-iotests/141 +++ b/tests/qemu-iotests/141 @@ -1,9 +1,12 @@ -#!/usr/bin/env bash +#!/usr/bin/env python3 # group: rw auto quick # # Test case for ejecting BDSs with block jobs still running on them # -# Copyright (C) 2016 Red Hat, Inc. +# Originally written in bash by Hanna Czenczek, ported to Python by Stefan +# Hajnoczi. +# +# Copyright Red Hat # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,177 +22,129 @@ # along with this program. If not, see . # -# creator -owner=hreitz@redhat.com +import iotests -seq="$(basename $0)" -echo "QA output created by $seq" - -status=1 # failure is the default! - -_cleanup() -{ - _cleanup_qemu - _cleanup_test_img - for img in "$TEST_DIR"/{b,m,o}.$IMGFMT; do - _rm_test_img "$img" - done -} -trap "_cleanup; exit \$status" 0 1 2 3 15 - -# get standard environment, filters and checks -. ./common.rc -. ./common.filter -. ./common.qemu - -# Needs backing file and backing format support -_supported_fmt qcow2 qed -_supported_proto file -_supported_os Linux +# Common filters to mask values that vary in the test output +QMP_FILTERS = [iotests.filter_qmp_testfiles, \ + iotests.filter_qmp_imgfmt] -test_blockjob() -{ - _send_qemu_cmd $QEMU_HANDLE \ - "{'execute': 'blockdev-add', - 'arguments': { - 'node-name': 'drv0', - 'driver': '$IMGFMT', - 'file': { - 'driver': 'file', - 'filename': '$TEST_IMG' - }}}" \ - 'return' +class TestCase: + def __init__(self, name, vm, image_path, cancel_event): + self.name = name + self.vm = vm + self.image_path = image_path + self.cancel_event = cancel_event - # If "$2" is an event, we may or may not see it before the - # {"return": {}}. Therefore, filter the {"return": {}} out both - # here and in the next command. (Naturally, if we do not see it - # here, we will see it before the next command can be executed, - # so it will appear in the next _send_qemu_cmd's output.) - _send_qemu_cmd $QEMU_HANDLE \ - "$1" \ - "$2" \ - | _filter_img_create | _filter_qmp_empty_return + def __enter__(self): + iotests.log(f'=== Testing {self.name} ===') + self.vm.qmp_log('blockdev-add', \ + node_name='drv0', \ + driver=iotests.imgfmt, \ + file={'driver': 'file', 'filename': self.image_path}, \ + filters=QMP_FILTERS) - # We want this to return an error because the block job is still running - _send_qemu_cmd $QEMU_HANDLE \ - "{'execute': 'blockdev-del', - 'arguments': {'node-name': 'drv0'}}" \ - 'error' | _filter_generated_node_ids | _filter_qmp_empty_return + def __exit__(self, *exc_details): + # This is expected to fail because the job still exists + self.vm.qmp_log('blockdev-del', node_name='drv0', \ + filters=[iotests.filter_qmp_generated_node_ids]) - _send_qemu_cmd $QEMU_HANDLE \ - "{'execute': 'block-job-cancel', - 'arguments': {'device': 'job0'}}" \ - "$3" + self.vm.qmp_log('block-job-cancel', device='job0') + event = self.vm.event_wait(self.cancel_event) + iotests.log(event, filters=[iotests.filter_qmp_event]) - _send_qemu_cmd $QEMU_HANDLE \ - "{'execute': 'blockdev-del', - 'arguments': {'node-name': 'drv0'}}" \ - 'return' -} + # This time it succeeds + self.vm.qmp_log('blockdev-del', node_name='drv0') + + # Separate test cases in output + iotests.log('') -TEST_IMG="$TEST_DIR/b.$IMGFMT" _make_test_img 1M -TEST_IMG="$TEST_DIR/m.$IMGFMT" _make_test_img -b "$TEST_DIR/b.$IMGFMT" -F $IMGFMT 1M -_make_test_img -b "$TEST_DIR/m.$IMGFMT" 1M -F $IMGFMT +def main() -> None: + with iotests.FilePath('bottom', 'middle', 'top', 'target') as \ + (bottom_path, middle_path, top_path, target_path), \ + iotests.VM() as vm: -_launch_qemu -nodefaults + iotests.log('Creating bottom <- middle <- top backing file chain...') + IMAGE_SIZE='1M' + iotests.qemu_img_create('-f', iotests.imgfmt, bottom_path, IMAGE_SIZE) + iotests.qemu_img_create('-f', iotests.imgfmt, \ + '-F', iotests.imgfmt, \ + '-b', bottom_path, \ + middle_path, \ + IMAGE_SIZE) + iotests.qemu_img_create('-f', iotests.imgfmt, \ + '-F', iotests.imgfmt, \ + '-b', middle_path, \ + top_path, \ + IMAGE_SIZE) -_send_qemu_cmd $QEMU_HANDLE \ - "{'execute': 'qmp_capabilities'}" \ - 'return' + iotests.log('Starting VM...') + vm.add_args('-nodefaults') + vm.launch() -echo -echo '=== Testing drive-backup ===' -echo + # drive-backup will not send BLOCK_JOB_READY by itself, and cancelling + # the job will consequently result in BLOCK_JOB_CANCELLED being + # emitted. + with TestCase('drive-backup', vm, top_path, 'BLOCK_JOB_CANCELLED'): + vm.qmp_log('drive-backup', \ + job_id='job0', \ + device='drv0', \ + target=target_path, \ + format=iotests.imgfmt, \ + sync='none', \ + filters=QMP_FILTERS) -# drive-backup will not send BLOCK_JOB_READY by itself, and cancelling the job -# will consequently result in BLOCK_JOB_CANCELLED being emitted. + # drive-mirror will send BLOCK_JOB_READY basically immediately, and + # cancelling the job will consequently result in BLOCK_JOB_COMPLETED + # being emitted. + with TestCase('drive-mirror', vm, top_path, 'BLOCK_JOB_COMPLETED'): + vm.qmp_log('drive-mirror', \ + job_id='job0', \ + device='drv0', \ + target=target_path, \ + format=iotests.imgfmt, \ + sync='none', \ + filters=QMP_FILTERS) + event = vm.event_wait('BLOCK_JOB_READY') + assert event is not None # silence mypy + iotests.log(event, filters=[iotests.filter_qmp_event]) -test_blockjob \ - "{'execute': 'drive-backup', - 'arguments': {'job-id': 'job0', - 'device': 'drv0', - 'target': '$TEST_DIR/o.$IMGFMT', - 'format': '$IMGFMT', - 'sync': 'none'}}" \ - 'return' \ - '"status": "null"' + # An active block-commit will send BLOCK_JOB_READY basically + # immediately, and cancelling the job will consequently result in + # BLOCK_JOB_COMPLETED being emitted. + with TestCase('active block-commit', vm, top_path, \ + 'BLOCK_JOB_COMPLETED'): + vm.qmp_log('block-commit', \ + job_id='job0', \ + device='drv0') + event = vm.event_wait('BLOCK_JOB_READY') + assert event is not None # silence mypy + iotests.log(event, filters=[iotests.filter_qmp_event]) -echo -echo '=== Testing drive-mirror ===' -echo + # Give block-commit something to work on, otherwise it would be done + # immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would + # work just fine without the block job still running. + iotests.qemu_io(middle_path, '-c', f'write 0 {IMAGE_SIZE}') + with TestCase('non-active block-commit', vm, top_path, \ + 'BLOCK_JOB_CANCELLED'): + vm.qmp_log('block-commit', \ + job_id='job0', \ + device='drv0', \ + top=middle_path, \ + speed=1, \ + filters=[iotests.filter_qmp_testfiles]) -# drive-mirror will send BLOCK_JOB_READY basically immediately, and cancelling -# the job will consequently result in BLOCK_JOB_COMPLETED being emitted. + # Give block-stream something to work on, otherwise it would be done + # immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would + # work just fine without the block job still running. + iotests.qemu_io(bottom_path, '-c', f'write 0 {IMAGE_SIZE}') + with TestCase('block-stream', vm, top_path, 'BLOCK_JOB_CANCELLED'): + vm.qmp_log('block-stream', \ + job_id='job0', \ + device='drv0', \ + speed=1) -test_blockjob \ - "{'execute': 'drive-mirror', - 'arguments': {'job-id': 'job0', - 'device': 'drv0', - 'target': '$TEST_DIR/o.$IMGFMT', - 'format': '$IMGFMT', - 'sync': 'none'}}" \ - 'BLOCK_JOB_READY' \ - '"status": "null"' - -echo -echo '=== Testing active block-commit ===' -echo - -# An active block-commit will send BLOCK_JOB_READY basically immediately, and -# cancelling the job will consequently result in BLOCK_JOB_COMPLETED being -# emitted. - -test_blockjob \ - "{'execute': 'block-commit', - 'arguments': {'job-id': 'job0', 'device': 'drv0'}}" \ - 'BLOCK_JOB_READY' \ - '"status": "null"' - -echo -echo '=== Testing non-active block-commit ===' -echo - -# Give block-commit something to work on, otherwise it would be done -# immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would work just -# fine without the block job still running. - -$QEMU_IO -c 'write 0 1M' "$TEST_DIR/m.$IMGFMT" | _filter_qemu_io - -test_blockjob \ - "{'execute': 'block-commit', - 'arguments': {'job-id': 'job0', - 'device': 'drv0', - 'top': '$TEST_DIR/m.$IMGFMT', - 'speed': 1}}" \ - 'return' \ - '"status": "null"' - -echo -echo '=== Testing block-stream ===' -echo - -# Give block-stream something to work on, otherwise it would be done -# immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would work just -# fine without the block job still running. - -$QEMU_IO -c 'write 0 1M' "$TEST_DIR/b.$IMGFMT" | _filter_qemu_io - -# With some data to stream (and @speed set to 1), block-stream will not complete -# until we send the block-job-cancel command. - -test_blockjob \ - "{'execute': 'block-stream', - 'arguments': {'job-id': 'job0', - 'device': 'drv0', - 'speed': 1}}" \ - 'return' \ - '"status": "null"' - -_cleanup_qemu - -# success, all done -echo "*** done" -rm -f $seq.full -status=0 +if __name__ == '__main__': + iotests.script_main(main, supported_fmts=['qcow2', 'qed'], + supported_protocols=['file']) diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out index 63203d9944..91b7ba50af 100644 --- a/tests/qemu-iotests/141.out +++ b/tests/qemu-iotests/141.out @@ -1,179 +1,69 @@ -QA output created by 141 -Formatting 'TEST_DIR/b.IMGFMT', fmt=IMGFMT size=1048576 -Formatting 'TEST_DIR/m.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/b.IMGFMT backing_fmt=IMGFMT -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m.IMGFMT backing_fmt=IMGFMT -{'execute': 'qmp_capabilities'} -{"return": {}} - +Creating bottom <- middle <- top backing file chain... +Starting VM... === Testing drive-backup === - -{'execute': 'blockdev-add', - 'arguments': { - 'node-name': 'drv0', - 'driver': 'IMGFMT', - 'file': { - 'driver': 'file', - 'filename': 'TEST_DIR/t.IMGFMT' - }}} +{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} {"return": {}} -{'execute': 'drive-backup', -'arguments': {'job-id': 'job0', -'device': 'drv0', -'target': 'TEST_DIR/o.IMGFMT', -'format': 'IMGFMT', -'sync': 'none'}} -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'}} +{"execute": "drive-backup", "arguments": {"device": "drv0", "format": "IMGFMT", "job-id": "job0", "sync": "none", "target": "TEST_DIR/PID-target"}} +{"return": {}} +{"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'}} +{"execute": "block-job-cancel", "arguments": {"device": "job0"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} -{'execute': 'blockdev-del', - 'arguments': {'node-name': 'drv0'}} +{"data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} {"return": {}} === Testing drive-mirror === - -{'execute': 'blockdev-add', - 'arguments': { - 'node-name': 'drv0', - 'driver': 'IMGFMT', - 'file': { - 'driver': 'file', - 'filename': 'TEST_DIR/t.IMGFMT' - }}} +{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} {"return": {}} -{'execute': 'drive-mirror', -'arguments': {'job-id': 'job0', -'device': 'drv0', -'target': 'TEST_DIR/o.IMGFMT', -'format': 'IMGFMT', -'sync': 'none'}} -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": "ready", "id": "job0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} -{'execute': 'blockdev-del', - 'arguments': {'node-name': 'drv0'}} +{"execute": "drive-mirror", "arguments": {"device": "drv0", "format": "IMGFMT", "job-id": "job0", "sync": "none", "target": "TEST_DIR/PID-target"}} +{"return": {}} +{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: mirror"}} -{'execute': 'block-job-cancel', - 'arguments': {'device': 'job0'}} +{"execute": "block-job-cancel", "arguments": {"device": "job0"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} -{'execute': 'blockdev-del', - 'arguments': {'node-name': 'drv0'}} +{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} {"return": {}} === Testing active block-commit === - -{'execute': 'blockdev-add', - 'arguments': { - 'node-name': 'drv0', - 'driver': 'IMGFMT', - 'file': { - 'driver': 'file', - 'filename': 'TEST_DIR/t.IMGFMT' - }}} +{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} {"return": {}} -{'execute': 'block-commit', -'arguments': {'job-id': 'job0', 'device': 'drv0'}} -{"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": "ready", "id": "job0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} -{'execute': 'blockdev-del', - 'arguments': {'node-name': 'drv0'}} +{"execute": "block-commit", "arguments": {"device": "drv0", "job-id": "job0"}} +{"return": {}} +{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}} -{'execute': 'block-job-cancel', - 'arguments': {'device': 'job0'}} +{"execute": "block-job-cancel", "arguments": {"device": "job0"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} -{'execute': 'blockdev-del', - 'arguments': {'node-name': 'drv0'}} +{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} {"return": {}} === Testing non-active block-commit === - -wrote 1048576/1048576 bytes at offset 0 -1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -{'execute': 'blockdev-add', - 'arguments': { - 'node-name': 'drv0', - 'driver': 'IMGFMT', - 'file': { - 'driver': 'file', - 'filename': 'TEST_DIR/t.IMGFMT' - }}} +{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} {"return": {}} -{'execute': 'block-commit', -'arguments': {'job-id': 'job0', -'device': 'drv0', -'top': 'TEST_DIR/m.IMGFMT', -'speed': 1}} -{"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"}} -{'execute': 'blockdev-del', - 'arguments': {'node-name': 'drv0'}} +{"execute": "block-commit", "arguments": {"device": "drv0", "job-id": "job0", "speed": 1, "top": "TEST_DIR/PID-middle"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}} -{'execute': 'block-job-cancel', - 'arguments': {'device': 'job0'}} +{"execute": "block-job-cancel", "arguments": {"device": "job0"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "commit"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} -{'execute': 'blockdev-del', - 'arguments': {'node-name': 'drv0'}} +{"data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "commit"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} {"return": {}} === Testing block-stream === - -wrote 1048576/1048576 bytes at offset 0 -1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -{'execute': 'blockdev-add', - 'arguments': { - 'node-name': 'drv0', - 'driver': 'IMGFMT', - 'file': { - 'driver': 'file', - 'filename': 'TEST_DIR/t.IMGFMT' - }}} +{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} {"return": {}} -{'execute': 'block-stream', -'arguments': {'job-id': 'job0', -'device': 'drv0', -'speed': 1}} -{"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"}} -{'execute': 'blockdev-del', - 'arguments': {'node-name': 'drv0'}} +{"execute": "block-stream", "arguments": {"device": "drv0", "job-id": "job0", "speed": 1}} +{"return": {}} +{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: stream"}} -{'execute': 'block-job-cancel', - 'arguments': {'device': 'job0'}} +{"execute": "block-job-cancel", "arguments": {"device": "job0"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "stream"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} -{'execute': 'blockdev-del', - 'arguments': {'node-name': 'drv0'}} +{"data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "stream"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} {"return": {}} -*** done + diff --git a/tests/qemu-iotests/143.out b/tests/qemu-iotests/143.out index 9ec5888e0e..d6afa32abc 100644 --- a/tests/qemu-iotests/143.out +++ b/tests/qemu-iotests/143.out @@ -10,6 +10,6 @@ server reported: export 'no_such_export' not present qemu-io: can't open device nbd+unix:///aa--aa1?socket=SOCK_DIR/nbd: Requested export not available server reported: export 'aa--aa...' not present { 'execute': 'quit' } -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} *** done diff --git a/tests/qemu-iotests/144 b/tests/qemu-iotests/144 index bdcc498fa2..d284a0e442 100755 --- a/tests/qemu-iotests/144 +++ b/tests/qemu-iotests/144 @@ -83,12 +83,22 @@ echo echo === Performing block-commit on active layer === echo +capture_events="BLOCK_JOB_READY JOB_STATUS_CHANGE" + # Block commit on active layer, push the new overlay into base _send_qemu_cmd $h "{ 'execute': 'block-commit', 'arguments': { 'device': 'virtio0' } - }" "READY" + }" "return" + +_wait_event $h "JOB_STATUS_CHANGE" +_wait_event $h "JOB_STATUS_CHANGE" +_wait_event $h "JOB_STATUS_CHANGE" + +_wait_event $h "BLOCK_JOB_READY" + +capture_events= _send_qemu_cmd $h "{ 'execute': 'block-job-complete', 'arguments': { diff --git a/tests/qemu-iotests/144.out b/tests/qemu-iotests/144.out index b3b4812015..2245ddfa10 100644 --- a/tests/qemu-iotests/144.out +++ b/tests/qemu-iotests/144.out @@ -25,9 +25,9 @@ Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off co 'device': 'virtio0' } } +{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "virtio0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "virtio0"}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "virtio0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} { 'execute': 'block-job-complete', diff --git a/tests/qemu-iotests/146.out b/tests/qemu-iotests/146.out index dfd6c77140..a48804154e 100644 --- a/tests/qemu-iotests/146.out +++ b/tests/qemu-iotests/146.out @@ -2,414 +2,414 @@ QA output created by 146 === Testing VPC Autodetect === -[{ "start": 0, "length": 136363130880, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 136363130880, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] === Testing VPC with current_size force === -[{ "start": 0, "length": 136365211648, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 136365211648, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] === Testing VPC with chs force === -[{ "start": 0, "length": 136363130880, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 136363130880, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] === Testing Hyper-V Autodetect === -[{ "start": 0, "length": 136365211648, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 136365211648, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] === Testing Hyper-V with current_size force === -[{ "start": 0, "length": 136365211648, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 136365211648, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] === Testing Hyper-V with chs force === -[{ "start": 0, "length": 136363130880, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 136363130880, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] === Testing d2v Autodetect === -[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 8388608, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 12582912, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 16777216, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 33554432, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 35651584, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 37748736, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 39845888, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 41943040, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 44040192, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 46137344, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 48234496, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 50331648, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 52428800, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 54525952, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 56623104, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 58720256, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 60817408, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 62914560, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 65011712, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 67108864, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 69206016, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 71303168, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 73400320, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 75497472, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 77594624, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 79691776, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 81788928, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 83886080, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 85983232, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 88080384, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 90177536, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 92274688, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 94371840, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 96468992, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 98566144, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 100663296, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 102760448, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 104857600, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 106954752, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 109051904, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 111149056, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 113246208, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 115343360, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 117440512, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 119537664, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 121634816, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 123731968, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 125829120, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 127926272, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 130023424, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 132120576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 134217728, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 136314880, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 138412032, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 140509184, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 142606336, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 144703488, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 146800640, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 148897792, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 150994944, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 153092096, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 155189248, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 157286400, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 159383552, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 161480704, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 163577856, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 165675008, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 167772160, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 169869312, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 171966464, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 174063616, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 176160768, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 178257920, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 180355072, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 182452224, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 184549376, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 186646528, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 188743680, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 190840832, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 192937984, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 195035136, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 197132288, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 199229440, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 201326592, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 203423744, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 205520896, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 207618048, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 209715200, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 211812352, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 213909504, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 216006656, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 218103808, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 220200960, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 222298112, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 224395264, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 226492416, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 228589568, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 230686720, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 232783872, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 234881024, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 236978176, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 239075328, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 241172480, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 243269632, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 245366784, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 247463936, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 249561088, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 251658240, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 253755392, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 255852544, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 257949696, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 260046848, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 262144000, "length": 1310720, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 8388608, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 12582912, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 16777216, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 33554432, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 35651584, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 37748736, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 39845888, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 41943040, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 44040192, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 46137344, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 48234496, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 50331648, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 52428800, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 54525952, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 56623104, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 58720256, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 60817408, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 62914560, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 65011712, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 67108864, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 69206016, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 71303168, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 73400320, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 75497472, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 77594624, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 79691776, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 81788928, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 83886080, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 85983232, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 88080384, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 90177536, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 92274688, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 94371840, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 96468992, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 98566144, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 100663296, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 102760448, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 104857600, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 106954752, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 109051904, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 111149056, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 113246208, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 115343360, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 117440512, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 119537664, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 121634816, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 123731968, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 125829120, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 127926272, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 130023424, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 132120576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 134217728, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 136314880, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 138412032, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 140509184, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 142606336, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 144703488, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 146800640, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 148897792, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 150994944, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 153092096, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 155189248, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 157286400, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 159383552, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 161480704, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 163577856, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 165675008, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 167772160, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 169869312, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 171966464, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 174063616, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 176160768, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 178257920, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 180355072, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 182452224, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 184549376, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 186646528, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 188743680, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 190840832, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 192937984, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 195035136, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 197132288, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 199229440, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 201326592, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 203423744, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 205520896, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 207618048, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 209715200, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 211812352, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 213909504, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 216006656, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 218103808, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 220200960, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 222298112, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 224395264, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 226492416, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 228589568, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 230686720, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 232783872, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 234881024, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 236978176, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 239075328, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 241172480, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 243269632, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 245366784, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 247463936, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 249561088, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 251658240, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 253755392, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 255852544, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 257949696, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 260046848, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 262144000, "length": 1310720, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] === Testing d2v with current_size force === -[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 8388608, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 12582912, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 16777216, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 33554432, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 35651584, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 37748736, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 39845888, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 41943040, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 44040192, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 46137344, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 48234496, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 50331648, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 52428800, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 54525952, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 56623104, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 58720256, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 60817408, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 62914560, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 65011712, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 67108864, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 69206016, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 71303168, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 73400320, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 75497472, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 77594624, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 79691776, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 81788928, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 83886080, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 85983232, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 88080384, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 90177536, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 92274688, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 94371840, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 96468992, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 98566144, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 100663296, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 102760448, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 104857600, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 106954752, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 109051904, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 111149056, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 113246208, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 115343360, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 117440512, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 119537664, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 121634816, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 123731968, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 125829120, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 127926272, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 130023424, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 132120576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 134217728, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 136314880, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 138412032, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 140509184, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 142606336, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 144703488, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 146800640, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 148897792, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 150994944, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 153092096, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 155189248, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 157286400, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 159383552, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 161480704, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 163577856, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 165675008, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 167772160, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 169869312, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 171966464, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 174063616, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 176160768, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 178257920, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 180355072, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 182452224, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 184549376, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 186646528, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 188743680, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 190840832, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 192937984, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 195035136, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 197132288, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 199229440, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 201326592, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 203423744, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 205520896, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 207618048, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 209715200, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 211812352, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 213909504, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 216006656, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 218103808, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 220200960, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 222298112, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 224395264, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 226492416, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 228589568, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 230686720, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 232783872, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 234881024, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 236978176, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 239075328, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 241172480, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 243269632, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 245366784, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 247463936, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 249561088, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 251658240, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 253755392, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 255852544, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 257949696, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 260046848, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 262144000, "length": 1310720, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 8388608, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 12582912, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 16777216, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 33554432, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 35651584, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 37748736, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 39845888, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 41943040, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 44040192, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 46137344, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 48234496, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 50331648, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 52428800, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 54525952, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 56623104, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 58720256, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 60817408, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 62914560, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 65011712, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 67108864, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 69206016, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 71303168, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 73400320, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 75497472, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 77594624, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 79691776, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 81788928, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 83886080, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 85983232, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 88080384, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 90177536, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 92274688, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 94371840, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 96468992, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 98566144, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 100663296, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 102760448, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 104857600, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 106954752, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 109051904, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 111149056, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 113246208, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 115343360, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 117440512, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 119537664, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 121634816, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 123731968, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 125829120, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 127926272, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 130023424, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 132120576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 134217728, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 136314880, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 138412032, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 140509184, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 142606336, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 144703488, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 146800640, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 148897792, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 150994944, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 153092096, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 155189248, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 157286400, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 159383552, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 161480704, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 163577856, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 165675008, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 167772160, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 169869312, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 171966464, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 174063616, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 176160768, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 178257920, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 180355072, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 182452224, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 184549376, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 186646528, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 188743680, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 190840832, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 192937984, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 195035136, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 197132288, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 199229440, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 201326592, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 203423744, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 205520896, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 207618048, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 209715200, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 211812352, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 213909504, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 216006656, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 218103808, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 220200960, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 222298112, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 224395264, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 226492416, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 228589568, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 230686720, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 232783872, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 234881024, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 236978176, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 239075328, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 241172480, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 243269632, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 245366784, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 247463936, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 249561088, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 251658240, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 253755392, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 255852544, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 257949696, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 260046848, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 262144000, "length": 1310720, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] === Testing d2v with chs force === -[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 8388608, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 12582912, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 16777216, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 33554432, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 35651584, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 37748736, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 39845888, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 41943040, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 44040192, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 46137344, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 48234496, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 50331648, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 52428800, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 54525952, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 56623104, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 58720256, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 60817408, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 62914560, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 65011712, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 67108864, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 69206016, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 71303168, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 73400320, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 75497472, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 77594624, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 79691776, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 81788928, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 83886080, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 85983232, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 88080384, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 90177536, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 92274688, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 94371840, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 96468992, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 98566144, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 100663296, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 102760448, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 104857600, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 106954752, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 109051904, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 111149056, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 113246208, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 115343360, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 117440512, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 119537664, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 121634816, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 123731968, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 125829120, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 127926272, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 130023424, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 132120576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 134217728, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 136314880, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 138412032, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 140509184, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 142606336, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 144703488, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 146800640, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 148897792, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 150994944, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 153092096, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 155189248, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 157286400, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 159383552, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 161480704, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 163577856, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 165675008, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 167772160, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 169869312, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 171966464, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 174063616, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 176160768, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 178257920, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 180355072, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 182452224, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 184549376, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 186646528, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 188743680, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 190840832, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 192937984, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 195035136, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 197132288, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 199229440, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 201326592, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 203423744, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 205520896, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 207618048, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 209715200, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 211812352, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 213909504, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 216006656, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 218103808, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 220200960, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 222298112, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 224395264, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 226492416, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 228589568, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 230686720, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 232783872, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 234881024, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 236978176, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 239075328, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 241172480, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 243269632, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 245366784, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 247463936, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 249561088, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 251658240, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 253755392, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 255852544, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 257949696, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 260046848, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 262144000, "length": 1310720, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 8388608, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 12582912, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 16777216, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 33554432, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 35651584, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 37748736, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 39845888, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 41943040, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 44040192, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 46137344, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 48234496, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 50331648, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 52428800, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 54525952, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 56623104, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 58720256, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 60817408, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 62914560, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 65011712, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 67108864, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 69206016, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 71303168, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 73400320, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 75497472, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 77594624, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 79691776, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 81788928, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 83886080, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 85983232, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 88080384, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 90177536, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 92274688, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 94371840, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 96468992, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 98566144, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 100663296, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 102760448, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 104857600, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 106954752, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 109051904, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 111149056, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 113246208, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 115343360, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 117440512, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 119537664, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 121634816, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 123731968, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 125829120, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 127926272, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 130023424, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 132120576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 134217728, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 136314880, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 138412032, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 140509184, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 142606336, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 144703488, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 146800640, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 148897792, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 150994944, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 153092096, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 155189248, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 157286400, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 159383552, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 161480704, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 163577856, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 165675008, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 167772160, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 169869312, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 171966464, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 174063616, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 176160768, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 178257920, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 180355072, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 182452224, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 184549376, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 186646528, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 188743680, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 190840832, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 192937984, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 195035136, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 197132288, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 199229440, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 201326592, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 203423744, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 205520896, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 207618048, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 209715200, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 211812352, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 213909504, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 216006656, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 218103808, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 220200960, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 222298112, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 224395264, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 226492416, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 228589568, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 230686720, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 232783872, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 234881024, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 236978176, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 239075328, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 241172480, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 243269632, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 245366784, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 247463936, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 249561088, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 251658240, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 253755392, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 255852544, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 257949696, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 260046848, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 262144000, "length": 1310720, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] === Testing Image create, default === @@ -417,15 +417,15 @@ Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296 === Read created image, default opts ==== -[{ "start": 0, "length": 4295467008, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 4295467008, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] === Read created image, force_size_calc=chs ==== -[{ "start": 0, "length": 4295467008, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 4295467008, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] === Read created image, force_size_calc=current_size ==== -[{ "start": 0, "length": 4295467008, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 4295467008, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] === Testing Image create, force_size === @@ -433,13 +433,13 @@ Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296 === Read created image, default opts ==== -[{ "start": 0, "length": 4294967296, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 4294967296, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] === Read created image, force_size_calc=chs ==== -[{ "start": 0, "length": 4294967296, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 4294967296, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] === Read created image, force_size_calc=current_size ==== -[{ "start": 0, "length": 4294967296, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 4294967296, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] *** done diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 index 47dfa62e6b..6d6f077a14 100755 --- a/tests/qemu-iotests/147 +++ b/tests/qemu-iotests/147 @@ -58,8 +58,7 @@ class NBDBlockdevAddBase(iotests.QMPTestCase): def client_test(self, filename, address, export=None, node_name='nbd-blockdev', delete=True): bao = self.blockdev_add_options(address, export, node_name) - result = self.vm.qmp('blockdev-add', **bao) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', bao) found = False result = self.vm.qmp('query-named-block-nodes') @@ -75,8 +74,7 @@ class NBDBlockdevAddBase(iotests.QMPTestCase): self.assertTrue(found) if delete: - result = self.vm.qmp('blockdev-del', node_name=node_name) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', node_name=node_name) class QemuNBD(NBDBlockdevAddBase): @@ -158,16 +156,14 @@ class BuiltinNBD(NBDBlockdevAddBase): self.assert_qmp(result, 'return', {}) if export_name is None: - result = self.server.qmp('nbd-server-add', device='nbd-export') + self.server.cmd('nbd-server-add', device='nbd-export') else: - result = self.server.qmp('nbd-server-add', device='nbd-export', - name=export_name) - self.assert_qmp(result, 'return', {}) + self.server.cmd('nbd-server-add', device='nbd-export', + name=export_name) if export_name2 is not None: - result = self.server.qmp('nbd-server-add', device='nbd-export', - name=export_name2) - self.assert_qmp(result, 'return', {}) + self.server.cmd('nbd-server-add', device='nbd-export', + name=export_name2) return True @@ -175,8 +171,7 @@ class BuiltinNBD(NBDBlockdevAddBase): self.assertTrue(self._try_server_up(address, export_name, export_name2)) def _server_down(self): - result = self.server.qmp('nbd-server-stop') - self.assert_qmp(result, 'return', {}) + self.server.cmd('nbd-server-stop') def do_test_inet(self, export_name=None): while True: @@ -218,10 +213,8 @@ class BuiltinNBD(NBDBlockdevAddBase): flatten_sock_addr(address), 'exp1', 'node1', False) self.client_test('nbd://localhost:%i/%s' % (nbd_port, 'exp2'), flatten_sock_addr(address), 'exp2', 'node2', False) - result = self.vm.qmp('blockdev-del', node_name='node1') - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('blockdev-del', node_name='node2') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', node_name='node1') + self.vm.cmd('blockdev-del', node_name='node2') self._server_down() def test_inet6(self): @@ -272,8 +265,7 @@ class BuiltinNBD(NBDBlockdevAddBase): result = self.vm.send_fd_scm(fd=sockfd.fileno()) self.assertEqual(result, 0, 'Failed to send socket FD') - result = self.vm.qmp('getfd', fdname='nbd-fifo') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('getfd', fdname='nbd-fifo') address = { 'type': 'fd', 'data': { 'str': 'nbd-fifo' } } diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149 index 2ae318f16f..c13343d7ef 100755 --- a/tests/qemu-iotests/149 +++ b/tests/qemu-iotests/149 @@ -518,7 +518,7 @@ configs = [ ] -blacklist = [ +unsupported_configs = [ # We don't have a cast-6 cipher impl for QEMU yet "cast6-256-xts-plain64-sha1", "cast6-128-xts-plain64-sha1", @@ -528,17 +528,19 @@ blacklist = [ "twofish-192-xts-plain64-sha1", ] -whitelist = [] +# Optionally test only the configurations in the LUKS_CONFIG +# environment variable +tested_configs = None if "LUKS_CONFIG" in os.environ: - whitelist = os.environ["LUKS_CONFIG"].split(",") + tested_configs = os.environ["LUKS_CONFIG"].split(",") for config in configs: - if config.name in blacklist: - iotests.log("Skipping %s in blacklist" % config.name) + if config.name in unsupported_configs: + iotests.log("Skipping %s (config not supported)" % config.name) continue - if len(whitelist) > 0 and config.name not in whitelist: - iotests.log("Skipping %s not in whitelist" % config.name) + if tested_configs is not None and config.name not in tested_configs: + iotests.log("Skipping %s (by user request)" % config.name) continue test_once(config, qemu_img=False) diff --git a/tests/qemu-iotests/149.out b/tests/qemu-iotests/149.out index 2cc5b82f7c..72ca847159 100644 --- a/tests/qemu-iotests/149.out +++ b/tests/qemu-iotests/149.out @@ -470,7 +470,7 @@ sudo cryptsetup -q -v luksClose qiotest-145-cast5-128-cbc-plain64-sha1 # Delete image unlink TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img -Skipping cast6-256-xts-plain64-sha1 in blacklist +Skipping cast6-256-xts-plain64-sha1 (config not supported) # ================= dm-crypt aes-256-cbc-plain-sha1 ================= # Create image truncate TEST_DIR/luks-aes-256-cbc-plain-sha1.img --size 4194304MB @@ -1297,7 +1297,7 @@ sudo cryptsetup -q -v luksClose qiotest-145-twofish-128-xts-plain64-sha1 # Delete image unlink TEST_DIR/luks-twofish-128-xts-plain64-sha1.img -Skipping twofish-192-xts-plain64-sha1 in blacklist +Skipping twofish-192-xts-plain64-sha1 (config not supported) # ================= dm-crypt serpent-128-xts-plain64-sha1 ================= # Create image truncate TEST_DIR/luks-serpent-128-xts-plain64-sha1.img --size 4194304MB @@ -1534,8 +1534,8 @@ sudo cryptsetup -q -v luksClose qiotest-145-serpent-192-xts-plain64-sha1 # Delete image unlink TEST_DIR/luks-serpent-192-xts-plain64-sha1.img -Skipping cast6-128-xts-plain64-sha1 in blacklist -Skipping cast6-192-xts-plain64-sha1 in blacklist +Skipping cast6-128-xts-plain64-sha1 (config not supported) +Skipping cast6-192-xts-plain64-sha1 (config not supported) # ================= dm-crypt aes-256-xts-plain64-sha224 ================= # Create image truncate TEST_DIR/luks-aes-256-xts-plain64-sha224.img --size 4194304MB diff --git a/tests/qemu-iotests/151 b/tests/qemu-iotests/151 index 93d14193d0..f2ff9c5dac 100755 --- a/tests/qemu-iotests/151 +++ b/tests/qemu-iotests/151 @@ -19,7 +19,11 @@ # along with this program. If not, see . # +import math import os +import subprocess +import time +from typing import List, Optional import iotests from iotests import qemu_img @@ -50,7 +54,7 @@ class TestActiveMirror(iotests.QMPTestCase): self.vm = iotests.VM() self.vm.add_drive_raw(self.vm.qmp_to_opts(blk_source)) self.vm.add_blockdev(self.vm.qmp_to_opts(blk_target)) - self.vm.add_device('virtio-blk,drive=source') + self.vm.add_device('virtio-blk,id=vblk,drive=source') self.vm.launch() def tearDown(self): @@ -75,14 +79,13 @@ class TestActiveMirror(iotests.QMPTestCase): self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) # Start the block job - result = self.vm.qmp('blockdev-mirror', - job_id='mirror', - filter_node_name='mirror-node', - device='source-node', - target='target-node', - sync='full', - copy_mode='write-blocking') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-mirror', + job_id='mirror', + filter_node_name='mirror-node', + device='source-node', + target='target-node', + sync='full', + copy_mode='write-blocking') # Start some more requests for offset in range(3 * self.image_len // 8, 5 * self.image_len // 8, 1024 * 1024): @@ -121,23 +124,21 @@ class TestActiveMirror(iotests.QMPTestCase): result = self.vm.hmp_qemu_io('source', 'write -P 1 0 2M') # Start the block job (very slowly) - result = self.vm.qmp('blockdev-mirror', - job_id='mirror', - filter_node_name='mirror-node', - device='source-node', - target='target-node', - sync='full', - copy_mode='write-blocking', - buf_size=(1048576 // 4), - speed=1) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-mirror', + job_id='mirror', + filter_node_name='mirror-node', + device='source-node', + target='target-node', + sync='full', + copy_mode='write-blocking', + buf_size=(1048576 // 4), + speed=1) # Start an unaligned request to a dirty area result = self.vm.hmp_qemu_io('source', 'write -P 2 %i 1' % (1048576 + 42)) # Let the job finish - result = self.vm.qmp('block-job-set-speed', device='mirror', speed=0) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-set-speed', device='mirror', speed=0) self.complete_and_wait(drive='mirror') self.potential_writes_in_flight = False @@ -147,14 +148,14 @@ class TestActiveMirror(iotests.QMPTestCase): result = self.vm.hmp_qemu_io('source', 'write -P 1 0 2M') # Start the block job (very slowly) - result = self.vm.qmp('blockdev-mirror', - job_id='mirror', - filter_node_name='mirror-node', - device='source-node', - target='target-node', - sync='full', - copy_mode='write-blocking', - speed=1) + self.vm.cmd('blockdev-mirror', + job_id='mirror', + filter_node_name='mirror-node', + device='source-node', + target='target-node', + sync='full', + copy_mode='write-blocking', + speed=1) self.vm.hmp_qemu_io('source', 'break write_aio A') self.vm.hmp_qemu_io('source', 'aio_write 0 1M') # 1 @@ -185,13 +186,226 @@ class TestActiveMirror(iotests.QMPTestCase): # After resuming 4, one of 2 and 3 goes first and set in_flight_bitmap, # so the other will wait for it. - result = self.vm.qmp('block-job-set-speed', device='mirror', speed=0) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-set-speed', device='mirror', speed=0) self.complete_and_wait(drive='mirror') self.potential_writes_in_flight = False +class TestThrottledWithNbdExportBase(iotests.QMPTestCase): + image_len = 128 * 1024 * 1024 # MB + iops: Optional[int] = None + background_processes: List['subprocess.Popen[str]'] = [] + + def setUp(self): + # Must be set by subclasses + self.assertIsNotNone(self.iops) + + qemu_img('create', '-f', iotests.imgfmt, source_img, '128M') + qemu_img('create', '-f', iotests.imgfmt, target_img, '128M') + + self.vm = iotests.VM() + self.vm.launch() + + self.vm.cmd('object-add', **{ + 'qom-type': 'throttle-group', + 'id': 'thrgr', + 'limits': { + 'iops-total': self.iops, + 'iops-total-max': self.iops + } + }) + + self.vm.cmd('blockdev-add', **{ + 'node-name': 'source-node', + 'driver': 'throttle', + 'throttle-group': 'thrgr', + 'file': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': source_img + } + } + }) + + self.vm.cmd('blockdev-add', **{ + 'node-name': 'target-node', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': target_img + } + }) + + self.nbd_sock = iotests.file_path('nbd.sock', + base_dir=iotests.sock_dir) + self.nbd_url = f'nbd+unix:///source-node?socket={self.nbd_sock}' + + self.vm.cmd('nbd-server-start', addr={ + 'type': 'unix', + 'data': { + 'path': self.nbd_sock + } + }) + + self.vm.cmd('block-export-add', id='exp0', type='nbd', + node_name='source-node', writable=True) + + def tearDown(self): + # Wait for background requests to settle + try: + while True: + p = self.background_processes.pop() + while True: + try: + p.wait(timeout=0.0) + break + except subprocess.TimeoutExpired: + self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') + except IndexError: + pass + + # Cancel ongoing block jobs + for job in self.vm.qmp('query-jobs')['return']: + self.vm.qmp('block-job-cancel', device=job['id'], force=True) + + while True: + self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') + if len(self.vm.qmp('query-jobs')['return']) == 0: + break + + self.vm.shutdown() + os.remove(source_img) + os.remove(target_img) + + +class TestLowThrottledWithNbdExport(TestThrottledWithNbdExportBase): + iops = 16 + + def testUnderLoad(self): + ''' + Throttle the source node, then issue a whole bunch of external requests + while the mirror job (in write-blocking mode) is running. We want to + see background requests being issued even while the source is under + full load by active writes, so that progress can be made towards READY. + ''' + + # Fill the first half of the source image; do not fill the second half, + # that is where we will have active requests occur. This ensures that + # active mirroring itself will not directly contribute to the job's + # progress (because when the job was started, those areas were not + # intended to be copied, so active mirroring will only lead to not + # losing progress, but also not making any). + self.vm.hmp_qemu_io('source-node', + f'aio_write -P 1 0 {self.image_len // 2}') + self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') + + # Launch the mirror job + mirror_buf_size = 65536 + self.vm.cmd('blockdev-mirror', + job_id='mirror', + filter_node_name='mirror-node', + device='source-node', + target='target-node', + sync='full', + copy_mode='write-blocking', + buf_size=mirror_buf_size) + + # We create the external requests via qemu-io processes on the NBD + # server. Have their offset start in the middle of the image so they + # do not overlap with the background requests (which start from the + # beginning). + active_request_offset = self.image_len // 2 + active_request_len = 4096 + + # Create enough requests to saturate the node for 5 seconds + for _ in range(0, 5 * self.iops): + req = f'write -P 42 {active_request_offset} {active_request_len}' + active_request_offset += active_request_len + p = iotests.qemu_io_popen('-f', 'nbd', self.nbd_url, '-c', req) + self.background_processes += [p] + + # Now advance the clock one I/O operation at a time by the 4 seconds + # (i.e. one less than 5). We expect the mirror job to issue background + # operations here, even though active requests are still in flight. + # The active requests will take precedence, however, because they have + # been issued earlier than mirror's background requests. + # Once the active requests we have started above are done (i.e. after 5 + # virtual seconds), we expect those background requests to be worked + # on. We only advance 4 seconds here to avoid race conditions. + for _ in range(0, 4 * self.iops): + step = math.ceil(1 * 1000 * 1000 * 1000 / self.iops) + self.vm.qtest(f'clock_step {step}') + + # Note how much remains to be done until the mirror job is finished + job_status = self.vm.qmp('query-jobs')['return'][0] + start_remaining = job_status['total-progress'] - \ + job_status['current-progress'] + + # Create a whole bunch of more active requests + for _ in range(0, 10 * self.iops): + req = f'write -P 42 {active_request_offset} {active_request_len}' + active_request_offset += active_request_len + p = iotests.qemu_io_popen('-f', 'nbd', self.nbd_url, '-c', req) + self.background_processes += [p] + + # Let the clock advance more. After 1 second, as noted above, we + # expect the background requests to be worked on. Give them a couple + # of seconds (specifically 4) to see their impact. + for _ in range(0, 5 * self.iops): + step = math.ceil(1 * 1000 * 1000 * 1000 / self.iops) + self.vm.qtest(f'clock_step {step}') + + # Note how much remains to be done now. We expect this number to be + # reduced thanks to those background requests. + job_status = self.vm.qmp('query-jobs')['return'][0] + end_remaining = job_status['total-progress'] - \ + job_status['current-progress'] + + # See that indeed progress was being made on the job, even while the + # node was saturated with active requests + self.assertGreater(start_remaining - end_remaining, 0) + + +class TestHighThrottledWithNbdExport(TestThrottledWithNbdExportBase): + iops = 1024 + + def testActiveOnCreation(self): + ''' + Issue requests on the mirror source node right as the mirror is + instated. It's possible that requests occur before the actual job is + created, but after the node has been put into the graph. Write + requests across the node must in that case be forwarded to the source + node without attempting to mirror them (there is no job object yet, so + attempting to access it would cause a segfault). + We do this with a lightly throttled node (i.e. quite high IOPS limit). + Using throttling seems to increase reproductivity, but if the limit is + too low, all requests allowed per second will be submitted before + mirror_start_job() gets to the problematic point. + ''' + + # Let qemu-img bench create write requests (enough for two seconds on + # the virtual clock) + bench_args = ['bench', '-w', '-d', '1024', '-f', 'nbd', + '-c', str(self.iops * 2), self.nbd_url] + p = iotests.qemu_tool_popen(iotests.qemu_img_args + bench_args) + self.background_processes += [p] + + # Give qemu-img bench time to start up and issue requests + time.sleep(1.0) + # Flush the request queue, so new requests can come in right as we + # start blockdev-mirror + self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') + + self.vm.cmd('blockdev-mirror', + job_id='mirror', + device='source-node', + target='target-node', + sync='full', + copy_mode='write-blocking') + + if __name__ == '__main__': iotests.main(supported_fmts=['qcow2', 'raw'], supported_protocols=['file']) diff --git a/tests/qemu-iotests/151.out b/tests/qemu-iotests/151.out index 89968f35d7..3f8a935a08 100644 --- a/tests/qemu-iotests/151.out +++ b/tests/qemu-iotests/151.out @@ -1,5 +1,5 @@ -.... +...... ---------------------------------------------------------------------- -Ran 4 tests +Ran 6 tests OK diff --git a/tests/qemu-iotests/152 b/tests/qemu-iotests/152 index 4e179c340f..197bea9e77 100755 --- a/tests/qemu-iotests/152 +++ b/tests/qemu-iotests/152 @@ -41,16 +41,16 @@ class TestUnaligned(iotests.QMPTestCase): pass def test_unaligned(self): - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - granularity=65536, target=target_img) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + granularity=65536, target=target_img) self.complete_and_wait() self.vm.shutdown() self.assertEqual(iotests.image_size(test_img), iotests.image_size(target_img), "Target size doesn't match source when granularity when unaligend") def test_unaligned_with_update(self): - result = self.vm.qmp('drive-mirror', device='drive0', sync='full', - granularity=65536, target=target_img) + self.vm.cmd('drive-mirror', device='drive0', sync='full', + granularity=65536, target=target_img) self.wait_ready() self.vm.hmp_qemu_io('drive0', 'write 0 512') self.complete_and_wait(wait_ready=False) diff --git a/tests/qemu-iotests/154.out b/tests/qemu-iotests/154.out index 1fa7ffc475..0199269add 100644 --- a/tests/qemu-iotests/154.out +++ b/tests/qemu-iotests/154.out @@ -11,14 +11,14 @@ wrote 2048/2048 bytes at offset 17408 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 2048/2048 bytes at offset 27648 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 4096, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 8192, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 12288, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 16384, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 20480, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 24576, "length": 8192, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 32768, "length": 134184960, "depth": 1, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 4096, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 8192, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 12288, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 16384, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 20480, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 24576, "length": 8192, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 32768, "length": 134184960, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] == backing file contains non-zero data before write_zeroes == Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 @@ -41,11 +41,11 @@ read 1024/1024 bytes at offset 65536 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 2048/2048 bytes at offset 67584 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 36864, "length": 28672, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 69632, "length": 134148096, "depth": 1, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 36864, "length": 28672, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 69632, "length": 134148096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] == backing file contains non-zero data after write_zeroes == Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 @@ -68,11 +68,11 @@ read 1024/1024 bytes at offset 44032 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 3072/3072 bytes at offset 40960 3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 36864, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 40960, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 45056, "length": 134172672, "depth": 1, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 36864, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 40960, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 45056, "length": 134172672, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] == write_zeroes covers non-zero data == Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 @@ -101,15 +101,15 @@ wrote 2048/2048 bytes at offset 29696 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 4096/4096 bytes at offset 28672 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 4096, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 8192, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 12288, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 16384, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 20480, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 24576, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 28672, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 32768, "length": 134184960, "depth": 1, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 4096, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 8192, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 12288, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 16384, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 20480, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 24576, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 28672, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 32768, "length": 134184960, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] == spanning two clusters, non-zero before request == Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 @@ -142,16 +142,16 @@ read 1024/1024 bytes at offset 67584 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 5120/5120 bytes at offset 68608 5 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 36864, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 40960, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 49152, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 53248, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 57344, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 69632, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 73728, "length": 134144000, "depth": 1, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 36864, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 40960, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 49152, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 53248, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 57344, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 69632, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 73728, "length": 134144000, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] == spanning two clusters, non-zero after request == Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 @@ -184,16 +184,16 @@ read 7168/7168 bytes at offset 65536 7 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 1024/1024 bytes at offset 72704 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 36864, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 40960, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 49152, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 53248, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 57344, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 69632, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 73728, "length": 134144000, "depth": 1, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 36864, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 40960, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 49152, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 53248, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 57344, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 69632, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 73728, "length": 134144000, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] == spanning two clusters, partially overwriting backing file == Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 @@ -212,8 +212,8 @@ read 1024/1024 bytes at offset 5120 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 2048/2048 bytes at offset 6144 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 8192, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 8192, "length": 134209536, "depth": 1, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 8192, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 8192, "length": 134209536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] == spanning multiple clusters, non-zero in first cluster == Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 @@ -226,10 +226,10 @@ read 2048/2048 bytes at offset 65536 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 10240/10240 bytes at offset 67584 10 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 69632, "length": 8192, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 69632, "length": 8192, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] == spanning multiple clusters, non-zero in intermediate cluster == Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 @@ -240,9 +240,9 @@ wrote 7168/7168 bytes at offset 67584 7 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 12288/12288 bytes at offset 65536 12 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 65536, "length": 12288, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 12288, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] == spanning multiple clusters, non-zero in final cluster == Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 @@ -255,10 +255,10 @@ read 10240/10240 bytes at offset 65536 10 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 2048/2048 bytes at offset 75776 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 65536, "length": 8192, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 73728, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 8192, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 73728, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] == spanning multiple clusters, partially overwriting backing file == Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 @@ -277,88 +277,88 @@ read 2048/2048 bytes at offset 74752 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 1024/1024 bytes at offset 76800 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 69632, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 73728, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 69632, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 73728, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] == unaligned image tail cluster, no allocation needed == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 wrote 512/512 bytes at offset 134217728 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2048/2048 bytes allocated at offset 128 MiB -[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 wrote 512/512 bytes at offset 134219264 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2048/2048 bytes allocated at offset 128 MiB -[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 wrote 1024/1024 bytes at offset 134218240 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2048/2048 bytes allocated at offset 128 MiB -[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 wrote 2048/2048 bytes at offset 134217728 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2048/2048 bytes allocated at offset 128 MiB -[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT wrote 512/512 bytes at offset 134217728 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2048/2048 bytes allocated at offset 128 MiB -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT wrote 512/512 bytes at offset 134219264 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2048/2048 bytes allocated at offset 128 MiB -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT wrote 1024/1024 bytes at offset 134218240 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2048/2048 bytes allocated at offset 128 MiB -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT wrote 2048/2048 bytes at offset 134217728 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2048/2048 bytes allocated at offset 128 MiB -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] wrote 512/512 bytes at offset 134217728 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT wrote 512/512 bytes at offset 134217728 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2048/2048 bytes allocated at offset 128 MiB -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT wrote 512/512 bytes at offset 134219264 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2048/2048 bytes allocated at offset 128 MiB -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT wrote 1024/1024 bytes at offset 134218240 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2048/2048 bytes allocated at offset 128 MiB -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT wrote 2048/2048 bytes at offset 134217728 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2048/2048 bytes allocated at offset 128 MiB -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134218752 wrote 1024/1024 bytes at offset 134217728 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -369,15 +369,15 @@ read 512/512 bytes at offset 134217728 read 512/512 bytes at offset 134218240 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 1024/1024 bytes allocated at offset 128 MiB -[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] wrote 1024/1024 bytes at offset 134217728 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 1024/1024 bytes allocated at offset 128 MiB read 1024/1024 bytes at offset 134217728 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 1024, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] +[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 1024, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] == unaligned image tail cluster, allocation required == Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752 @@ -390,8 +390,8 @@ read 512/512 bytes at offset 134217728 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 1536/1536 bytes at offset 134218240 1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT wrote 512/512 bytes at offset 134218240 @@ -412,6 +412,6 @@ read 512/512 bytes at offset 134218240 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 1024/1024 bytes at offset 134218752 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] *** done diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 index eadda52615..38eacb4127 100755 --- a/tests/qemu-iotests/155 +++ b/tests/qemu-iotests/155 @@ -110,8 +110,7 @@ class BaseClass(iotests.QMPTestCase): elif self.target_blockdev_backing: options['backing'] = self.target_blockdev_backing - result = self.vm.qmp('blockdev-add', **options) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', options) def tearDown(self): self.vm.shutdown() @@ -178,20 +177,18 @@ class MirrorBaseClass(BaseClass): 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', - auto_finalize=False) + self.vm.cmd(self.cmd, job_id='mirror-job', device='source', + sync=sync, target='target', + auto_finalize=False) else: if self.existing: mode = 'existing' else: mode = 'absolute-paths' - 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', auto_finalize=False) - - self.assert_qmp(result, 'return', {}) + self.vm.cmd(self.cmd, job_id='mirror-job', device='source', + sync=sync, target=target_img, + format=iotests.imgfmt, mode=mode, + node_name='target', auto_finalize=False) self.vm.run_job('mirror-job', auto_finalize=False, pre_finalize=self.openBacking, auto_dismiss=True) @@ -258,16 +255,14 @@ class TestBlockdevMirrorReopen(MirrorBaseClass): 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-reopen', options=[{ - 'node-name': "target", - 'driver': iotests.imgfmt, - 'file': "target-file", - 'backing': "backing" - }]) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', node_name="backing", + driver="null-co") + self.vm.cmd('blockdev-reopen', options=[{ + 'node-name': "target", + 'driver': iotests.imgfmt, + 'file': "target-file", + 'backing': "backing" + }]) class TestBlockdevMirrorReopenIothread(TestBlockdevMirrorReopen): use_iothread = True @@ -281,12 +276,10 @@ class TestBlockdevMirrorSnapshot(MirrorBaseClass): 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', {}) + self.vm.cmd('blockdev-add', node_name="backing", + driver="null-co") + self.vm.cmd('blockdev-snapshot', node="backing", + overlay="target") class TestBlockdevMirrorSnapshotIothread(TestBlockdevMirrorSnapshot): use_iothread = True @@ -295,14 +288,12 @@ class TestCommit(BaseClass): existing = False def testCommit(self): - result = self.vm.qmp('block-commit', job_id='commit-job', - device='source', base=back1_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', job_id='commit-job', + device='source', base=back1_img) self.vm.event_wait('BLOCK_JOB_READY') - result = self.vm.qmp('block-job-complete', device='commit-job') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-complete', device='commit-job') self.vm.event_wait('BLOCK_JOB_COMPLETED') diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156 index a9540bd80d..97c2d86ce5 100755 --- a/tests/qemu-iotests/156 +++ b/tests/qemu-iotests/156 @@ -50,7 +50,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.qemu _supported_fmt qcow2 qed -_supported_proto generic +_supported_proto file # Copying files around with cp does not work with external data files _unsupported_imgopts data_file diff --git a/tests/qemu-iotests/156.out b/tests/qemu-iotests/156.out index 4a22f0c41a..07e5e83f5d 100644 --- a/tests/qemu-iotests/156.out +++ b/tests/qemu-iotests/156.out @@ -72,8 +72,8 @@ read 65536/65536 bytes at offset 196608 {"return": ""} { 'execute': 'quit' } -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/157 b/tests/qemu-iotests/157 index 0dc9ba68d2..aa2ebbfb4b 100755 --- a/tests/qemu-iotests/157 +++ b/tests/qemu-iotests/157 @@ -40,6 +40,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt generic _supported_proto file +_require_devices virtio-blk + do_run_qemu() { ( diff --git a/tests/qemu-iotests/158 b/tests/qemu-iotests/158 index a95878e4ce..3a9ad7eed0 100755 --- a/tests/qemu-iotests/158 +++ b/tests/qemu-iotests/158 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow qcow2 -_supported_proto generic +_supported_proto file size=128M diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165 index e3ef28e2ee..b24907a62f 100755 --- a/tests/qemu-iotests/165 +++ b/tests/qemu-iotests/165 @@ -116,9 +116,8 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): sha256_2 = self.getSha256() assert sha256_1 != sha256_2 # Otherwise, it's not very interesting. - result = self.vm.qmp('block-dirty-bitmap-clear', node='drive0', - name='bitmap0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-dirty-bitmap-clear', node='drive0', + name='bitmap0') # Start with regions1 @@ -137,7 +136,7 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): assert sha256_1 == self.getSha256() # Reopen to RW - result = self.vm.qmp('blockdev-reopen', options=[{ + self.vm.cmd('blockdev-reopen', options=[{ 'node-name': 'node0', 'driver': iotests.imgfmt, 'file': { @@ -146,7 +145,6 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): }, 'read-only': False }]) - self.assert_qmp(result, 'return', {}) # Check that bitmap is reopened to RW and we can write to it. self.writeRegions(regions2) diff --git a/tests/qemu-iotests/172 b/tests/qemu-iotests/172 index ff269ca7b5..4da0e0f2e2 100755 --- a/tests/qemu-iotests/172 +++ b/tests/qemu-iotests/172 @@ -56,7 +56,7 @@ do_run_qemu() done fi echo quit - ) | $QEMU -accel qtest -nographic -monitor stdio -serial none "$@" + ) | $QEMU -accel qtest -nographic -monitor stdio -serial none -vga none -nic none "$@" echo } diff --git a/tests/qemu-iotests/172.out b/tests/qemu-iotests/172.out index 9479b92185..07eebf3583 100644 --- a/tests/qemu-iotests/172.out +++ b/tests/qemu-iotests/172.out @@ -28,6 +28,8 @@ Testing: discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "288" @@ -55,6 +57,8 @@ Testing: -fda TEST_DIR/t.qcow2 discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -92,6 +96,8 @@ Testing: -fdb TEST_DIR/t.qcow2 discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -104,6 +110,8 @@ Testing: -fdb TEST_DIR/t.qcow2 discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "288" floppy1 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -145,6 +153,8 @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2.2 discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -157,6 +167,8 @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2.2 discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -199,6 +211,8 @@ Testing: -fdb discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "288" dev: floppy, id "" unit = 0 (0x0) @@ -211,6 +225,8 @@ Testing: -fdb discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "288" @@ -238,6 +254,8 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -275,6 +293,8 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1 discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -287,6 +307,8 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1 discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "288" floppy1 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -328,6 +350,8 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -340,6 +364,8 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -385,6 +411,8 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] @@ -422,6 +450,8 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1 discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] @@ -459,6 +489,8 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -471,6 +503,8 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] @@ -522,6 +556,8 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -534,6 +570,8 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -576,6 +614,8 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -588,6 +628,8 @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -630,6 +672,8 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" dev: floppy, id "" unit = 1 (0x1) @@ -642,6 +686,8 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" floppy1 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -684,6 +730,8 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" dev: floppy, id "" unit = 1 (0x1) @@ -696,6 +744,8 @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" floppy1 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -747,6 +797,8 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -759,6 +811,8 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -801,6 +855,8 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" dev: floppy, id "" unit = 0 (0x0) @@ -813,6 +869,8 @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/unattached/device[N] @@ -861,6 +919,8 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global floppy.drive=none0 -device discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] @@ -928,6 +988,8 @@ Testing: -device floppy discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "288" Testing: -device floppy,drive-type=120 @@ -952,6 +1014,8 @@ Testing: -device floppy,drive-type=120 discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "120" Testing: -device floppy,drive-type=144 @@ -976,6 +1040,8 @@ Testing: -device floppy,drive-type=144 discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" Testing: -device floppy,drive-type=288 @@ -1000,6 +1066,8 @@ Testing: -device floppy,drive-type=288 discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "288" @@ -1027,6 +1095,8 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "120" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] @@ -1064,6 +1134,8 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "288" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] @@ -1104,6 +1176,8 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,logical discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] @@ -1141,6 +1215,8 @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,physica discard_granularity = 4294967295 (4 GiB) write-cache = "auto" share-rw = false + account-invalid = "auto" + account-failed = "auto" drive-type = "144" none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) Attached to: /machine/peripheral-anon/device[N] diff --git a/tests/qemu-iotests/176.out b/tests/qemu-iotests/176.out index 9d09b60452..9c73ef2eea 100644 --- a/tests/qemu-iotests/176.out +++ b/tests/qemu-iotests/176.out @@ -37,8 +37,8 @@ Offset Length File 0x7ffe0000 0x20000 TEST_DIR/t.IMGFMT.itmd 0x83400000 0x200 TEST_DIR/t.IMGFMT.itmd Snapshot list: -ID TAG -1 snap +ID TAG +1 snap === Test pass snapshot.1 === @@ -78,8 +78,8 @@ Offset Length File 0x7fff0000 0x10000 TEST_DIR/t.IMGFMT 0x83400000 0x200 TEST_DIR/t.IMGFMT Snapshot list: -ID TAG -1 snap +ID TAG +1 snap === Test pass snapshot.2 === @@ -119,8 +119,8 @@ Offset Length File 0x7fff0000 0x10000 TEST_DIR/t.IMGFMT 0x83400000 0x200 TEST_DIR/t.IMGFMT Snapshot list: -ID TAG -1 snap +ID TAG +1 snap === Test pass snapshot.3 === @@ -157,8 +157,8 @@ Offset Length File 0x7fff0000 0x10000 TEST_DIR/t.IMGFMT 0x83400000 0x200 TEST_DIR/t.IMGFMT Snapshot list: -ID TAG -1 snap +ID TAG +1 snap === Test pass bitmap.0 === @@ -169,8 +169,8 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} wrote 196608/196608 bytes at offset 2147287040 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 2147352576 @@ -206,8 +206,8 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {"sha256": HASH}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} === Test pass bitmap.1 === @@ -218,8 +218,8 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} wrote 196608/196608 bytes at offset 2147287040 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 2147352576 @@ -256,8 +256,8 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {"sha256": HASH}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} === Test pass bitmap.2 === @@ -268,8 +268,8 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} wrote 196608/196608 bytes at offset 2147287040 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 2147352576 @@ -306,8 +306,8 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {"sha256": HASH}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} === Test pass bitmap.3 === @@ -318,8 +318,8 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} wrote 196608/196608 bytes at offset 2147287040 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 2147352576 @@ -353,6 +353,6 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {"sha256": HASH}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} *** done diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2 index 0d51fe401e..fe193fd5f4 100644 --- a/tests/qemu-iotests/178.out.qcow2 +++ b/tests/qemu-iotests/178.out.qcow2 @@ -13,8 +13,7 @@ qemu-img: Invalid option list: , qemu-img: Invalid parameter 'snapshot.foo' qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar' qemu-img: --output must be used with human or json as argument. -qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for -qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. +qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807. qemu-img: Unknown file format 'foo' == Size calculation for a new file (human) == diff --git a/tests/qemu-iotests/178.out.raw b/tests/qemu-iotests/178.out.raw index 116241ddef..445e460fad 100644 --- a/tests/qemu-iotests/178.out.raw +++ b/tests/qemu-iotests/178.out.raw @@ -13,8 +13,7 @@ qemu-img: Invalid option list: , qemu-img: Invalid parameter 'snapshot.foo' qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar' qemu-img: --output must be used with human or json as argument. -qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for -qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. +qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807. qemu-img: Unknown file format 'foo' == Size calculation for a new file (human) == diff --git a/tests/qemu-iotests/179.out b/tests/qemu-iotests/179.out index 7cf22cd75f..65b909ebc2 100644 --- a/tests/qemu-iotests/179.out +++ b/tests/qemu-iotests/179.out @@ -13,11 +13,11 @@ wrote 2097152/2097152 bytes at offset 6291456 2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000) 2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000) 56 MiB (0x3800000) bytes not allocated at offset 8 MiB (0x800000) -[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 8388608, "length": 58720256, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 8388608, "length": 58720256, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] wrote 2097150/2097150 bytes at offset 10485761 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 2097150/2097150 bytes at offset 14680065 @@ -31,15 +31,15 @@ wrote 2097150/2097150 bytes at offset 14680065 2 MiB (0x200000) bytes not allocated at offset 12 MiB (0xc00000) 2 MiB (0x200000) bytes allocated at offset 14 MiB (0xe00000) 48 MiB (0x3000000) bytes not allocated at offset 16 MiB (0x1000000) -[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 16777216, "length": 50331648, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 16777216, "length": 50331648, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] wrote 14680064/14680064 bytes at offset 18874368 14 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 2097152/2097152 bytes at offset 20971520 @@ -57,21 +57,21 @@ wrote 6291456/6291456 bytes at offset 25165824 2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000) 14 MiB (0xe00000) bytes allocated at offset 18 MiB (0x1200000) 32 MiB (0x2000000) bytes not allocated at offset 32 MiB (0x2000000) -[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 16777216, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 25165824, "length": 6291456, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 33554432, "length": 33554432, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 16777216, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 25165824, "length": 6291456, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, +{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 33554432, "length": 33554432, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] wrote 2097152/2097152 bytes at offset 27262976 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 2097152/2097152 bytes at offset 29360128 @@ -87,23 +87,23 @@ wrote 2097152/2097152 bytes at offset 29360128 2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000) 14 MiB (0xe00000) bytes allocated at offset 18 MiB (0x1200000) 32 MiB (0x2000000) bytes not allocated at offset 32 MiB (0x2000000) -[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 16777216, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 33554432, "length": 33554432, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 16777216, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, +{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, +{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 33554432, "length": 33554432, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] wrote 8388608/8388608 bytes at offset 33554432 8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 2097152/2097152 bytes at offset 35651584 @@ -121,24 +121,24 @@ wrote 2097152/2097152 bytes at offset 37748736 2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000) 22 MiB (0x1600000) bytes allocated at offset 18 MiB (0x1200000) 24 MiB (0x1800000) bytes not allocated at offset 40 MiB (0x2800000) -[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 16777216, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 33554432, "length": 8388608, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 41943040, "length": 25165824, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 16777216, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, +{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, +{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 33554432, "length": 8388608, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 41943040, "length": 25165824, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] wrote 8388608/8388608 bytes at offset 41943040 8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 8388608/8388608 bytes at offset 50331648 @@ -162,31 +162,31 @@ wrote 2097152/2097152 bytes at offset 62914560 4 MiB (0x400000) bytes not allocated at offset 54 MiB (0x3600000) 4 MiB (0x400000) bytes allocated at offset 58 MiB (0x3a00000) 2 MiB (0x200000) bytes not allocated at offset 62 MiB (0x3e00000) -[{ "start": 0, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 2097152, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false}, -{ "start": 4194304, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 6291456, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false}, -{ "start": 8388608, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 10485760, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false}, -{ "start": 12582912, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 14680064, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false}, -{ "start": 16777216, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 18874368, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 20971520, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false}, -{ "start": 23068672, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 25165824, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 27262976, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false}, -{ "start": 29360128, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 31457280, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 33554432, "length": 10485760, "depth": 1, "present": true, "zero": true, "data": false}, -{ "start": 44040192, "length": 4194304, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 48234496, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false}, -{ "start": 50331648, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 52428800, "length": 4194304, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 56623104, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 58720256, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 60817408, "length": 4194304, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 65011712, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 2097152, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 4194304, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 6291456, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 8388608, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 10485760, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 12582912, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 14680064, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 16777216, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 18874368, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 20971520, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 23068672, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 25165824, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, +{ "start": 27262976, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 29360128, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, +{ "start": 31457280, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 33554432, "length": 10485760, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 44040192, "length": 4194304, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 48234496, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 50331648, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 52428800, "length": 4194304, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 56623104, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 58720256, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 60817408, "length": 4194304, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 65011712, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] No errors were found on the image. No errors were found on the image. diff --git a/tests/qemu-iotests/181 b/tests/qemu-iotests/181 index cb96d09ae5..dc90a10757 100755 --- a/tests/qemu-iotests/181 +++ b/tests/qemu-iotests/181 @@ -109,7 +109,7 @@ if [ ${QEMU_STATUS[$dest]} -lt 0 ]; then _notrun 'Postcopy is not supported' fi -_send_qemu_cmd $src 'migrate_set_parameter max_bandwidth 4k' "(qemu)" +_send_qemu_cmd $src 'migrate_set_parameter max-bandwidth 4k' "(qemu)" _send_qemu_cmd $src 'migrate_set_capability postcopy-ram on' "(qemu)" _send_qemu_cmd $src "migrate -d unix:${MIG_SOCKET}" "(qemu)" _send_qemu_cmd $src 'migrate_start_postcopy' "(qemu)" diff --git a/tests/qemu-iotests/182.out b/tests/qemu-iotests/182.out index 57f7265458..83fc1a4797 100644 --- a/tests/qemu-iotests/182.out +++ b/tests/qemu-iotests/182.out @@ -53,6 +53,6 @@ Formatting 'TEST_DIR/t.qcow2.overlay', fmt=qcow2 cluster_size=65536 extended_l2= {'execute': 'qmp_capabilities'} {"return": {}} {'execute': 'quit'} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} *** done diff --git a/tests/qemu-iotests/183 b/tests/qemu-iotests/183 index ee62939e72..b85770458e 100755 --- a/tests/qemu-iotests/183 +++ b/tests/qemu-iotests/183 @@ -90,7 +90,7 @@ echo reply="$(_send_qemu_cmd $src \ "{ 'execute': 'migrate', 'arguments': { 'uri': 'unix:${MIG_SOCKET}', 'blk': true } }" \ - 'return\|error')" + 'return\|error' | _filter_migration_block_deprecated)" echo "$reply" if echo "$reply" | grep "compiled without old-style" > /dev/null; then _notrun "migrate -b support not compiled in" diff --git a/tests/qemu-iotests/183.out b/tests/qemu-iotests/183.out index fd9c2e52a5..8aef74a25d 100644 --- a/tests/qemu-iotests/183.out +++ b/tests/qemu-iotests/183.out @@ -30,13 +30,13 @@ read 65536/65536 bytes at offset 0 'arguments': { 'uri': 'unix:SOCK_DIR/migrate', 'blk': true } } {"return": {}} { 'execute': 'query-status' } -{"return": {"status": "postmigrate", "singlestep": false, "running": false}} +{"return": {"status": "postmigrate", "running": false}} === Do some I/O on the destination === { 'execute': 'query-status' } {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"} -{"return": {"status": "running", "singlestep": false, "running": true}} +{"return": {"status": "running", "running": true}} { 'execute': 'human-monitor-command', 'arguments': { 'command-line': 'qemu-io disk "read -P 0x55 0 64k"' } } @@ -53,11 +53,11 @@ wrote 65536/65536 bytes at offset 1048576 === Shut down and check image === {"execute":"quit"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"return": {}} {"execute":"quit"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} No errors were found on the image. No errors were found on the image. wrote 65536/65536 bytes at offset 1048576 diff --git a/tests/qemu-iotests/184.out b/tests/qemu-iotests/184.out index 77e5489d65..e8f631f853 100644 --- a/tests/qemu-iotests/184.out +++ b/tests/qemu-iotests/184.out @@ -89,10 +89,6 @@ Testing: "return": [ ] } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -104,6 +100,10 @@ Testing: "reason": "host-qmp-quit" } } +{ + "return": { + } +} == property changes in ThrottleGroup == @@ -169,10 +169,6 @@ Testing: "iops-total-max": 0 } } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -184,6 +180,10 @@ Testing: "reason": "host-qmp-quit" } } +{ + "return": { + } +} == object creation/set errors == @@ -211,10 +211,6 @@ Testing: "desc": "bps/iops/max total values and read/write values cannot be used at the same time" } } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -226,6 +222,10 @@ Testing: "reason": "host-qmp-quit" } } +{ + "return": { + } +} == don't specify group == @@ -247,10 +247,6 @@ Testing: "desc": "Parameter 'throttle-group' is missing" } } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -262,6 +258,10 @@ Testing: "reason": "host-qmp-quit" } } +{ + "return": { + } +} *** done diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 index 8b1143dc16..17489fb91c 100755 --- a/tests/qemu-iotests/185 +++ b/tests/qemu-iotests/185 @@ -344,14 +344,16 @@ wait_for_job_and_quit() { sleep 1 + # List of expected events + capture_events='BLOCK_JOB_CANCELLED JOB_STATUS_CHANGE SHUTDOWN' + _send_qemu_cmd $h \ '{"execute": "quit"}' \ 'return' - # List of expected events - capture_events='BLOCK_JOB_CANCELLED JOB_STATUS_CHANGE SHUTDOWN' _wait_event $h 'SHUTDOWN' - QEMU_EVENTS= # Ignore all JOB_STATUS_CHANGE events that came before SHUTDOWN + _wait_event $h 'JOB_STATUS_CHANGE' # standby + _wait_event $h 'JOB_STATUS_CHANGE' # ready _wait_event $h 'JOB_STATUS_CHANGE' # standby _wait_event $h 'JOB_STATUS_CHANGE' # ready _wait_event $h 'JOB_STATUS_CHANGE' # aborting diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out index 70e8dd6c87..6af0953c4d 100644 --- a/tests/qemu-iotests/185.out +++ b/tests/qemu-iotests/185.out @@ -40,9 +40,16 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} { 'execute': 'quit' } -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "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"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}} +{"return": {}} === Start active commit job and exit qemu === @@ -56,9 +63,16 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} { 'execute': 'quit' } -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "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"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}} +{"return": {}} === Start mirror job and exit qemu === @@ -75,9 +89,16 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} { 'execute': 'quit' } -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "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"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}} +{"return": {}} === Start backup job and exit qemu === @@ -97,9 +118,16 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} { 'execute': 'quit' } -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "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"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 65536, "speed": 65536, "type": "backup"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}} +{"return": {}} === Start streaming job and exit qemu === @@ -112,9 +140,16 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} { 'execute': 'quit' } -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "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"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "stream"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}} +{"return": {}} No errors were found on the image. === Start mirror to throttled QSD and exit qemu === @@ -137,6 +172,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "mirror", "len": 33554432, "offset": (filtered), "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "mirror"}} @@ -160,6 +197,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "commit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "commit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "commit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "commit", "len": 33554432, "offset": (filtered), "speed": 0, "type": "commit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "commit"}} diff --git a/tests/qemu-iotests/186 b/tests/qemu-iotests/186 index 072e54e62b..eaf13c7a33 100755 --- a/tests/qemu-iotests/186 +++ b/tests/qemu-iotests/186 @@ -40,6 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file fuse _require_drivers null-co +_require_devices virtio-scsi-pci if [ "$QEMU_DEFAULT_MACHINE" != "pc" ]; then _notrun "Requires a PC machine" diff --git a/tests/qemu-iotests/188 b/tests/qemu-iotests/188 index ce087d1873..2950b1dc31 100755 --- a/tests/qemu-iotests/188 +++ b/tests/qemu-iotests/188 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux _require_working_luks diff --git a/tests/qemu-iotests/189 b/tests/qemu-iotests/189 index 801494c6b9..008f73b07d 100755 --- a/tests/qemu-iotests/189 +++ b/tests/qemu-iotests/189 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux _require_working_luks diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out index ea88777374..c3309e4bc6 100644 --- a/tests/qemu-iotests/191.out +++ b/tests/qemu-iotests/191.out @@ -378,10 +378,6 @@ wrote 65536/65536 bytes at offset 1048576 ] } { 'execute': 'quit' } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -393,6 +389,10 @@ wrote 65536/65536 bytes at offset 1048576 "reason": "host-qmp-quit" } } +{ + "return": { + } +} image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64 MiB (67108864 bytes) @@ -796,10 +796,6 @@ wrote 65536/65536 bytes at offset 1048576 ] } { 'execute': 'quit' } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -811,6 +807,10 @@ wrote 65536/65536 bytes at offset 1048576 "reason": "host-qmp-quit" } } +{ + "return": { + } +} image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64 MiB (67108864 bytes) diff --git a/tests/qemu-iotests/194 b/tests/qemu-iotests/194 index 68894371f5..c0ce82dd25 100755 --- a/tests/qemu-iotests/194 +++ b/tests/qemu-iotests/194 @@ -74,6 +74,11 @@ with iotests.FilePath('source.img') as source_img_path, \ while True: event1 = source_vm.event_wait('MIGRATION') + if event1['data']['status'] == 'postcopy-active': + # This event is racy, it depends do we really do postcopy or bitmap + # was migrated during downtime (and no data to migrate in postcopy + # phase). So, don't log it. + continue iotests.log(event1, filters=[iotests.filter_qmp_event]) if event1['data']['status'] in ('completed', 'failed'): iotests.log('Gracefully ending the `drive-mirror` job on source...') diff --git a/tests/qemu-iotests/194.out b/tests/qemu-iotests/194.out index 4e6df1565a..376ed1d2e6 100644 --- a/tests/qemu-iotests/194.out +++ b/tests/qemu-iotests/194.out @@ -14,7 +14,6 @@ Starting migration... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} -{"data": {"status": "postcopy-active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Gracefully ending the `drive-mirror` job on source... {"return": {}} diff --git a/tests/qemu-iotests/195.out b/tests/qemu-iotests/195.out index ec84df5012..91717d302e 100644 --- a/tests/qemu-iotests/195.out +++ b/tests/qemu-iotests/195.out @@ -17,10 +17,6 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,backing.node-name=mid "return": { } } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -32,6 +28,10 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,backing.node-name=mid "reason": "host-qmp-quit" } } +{ + "return": { + } +} image: TEST_DIR/t.IMGFMT.mid file format: IMGFMT @@ -55,10 +55,6 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,node-name=top "return": { } } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -70,6 +66,10 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,node-name=top "reason": "host-qmp-quit" } } +{ + "return": { + } +} image: TEST_DIR/t.IMGFMT file format: IMGFMT diff --git a/tests/qemu-iotests/196 b/tests/qemu-iotests/196 index 76509a5ad1..e5105b1354 100755 --- a/tests/qemu-iotests/196 +++ b/tests/qemu-iotests/196 @@ -45,8 +45,7 @@ class TestInvalidateAutoclear(iotests.QMPTestCase): self.vm_b.add_incoming("exec: cat '" + migfile + "'") def test_migration(self): - result = self.vm_a.qmp('migrate', uri='exec:cat>' + migfile) - self.assert_qmp(result, 'return', {}); + self.vm_a.cmd('migrate', uri='exec:cat>' + migfile) self.assertNotEqual(self.vm_a.event_wait("STOP"), None) with open(disk, 'r+b') as f: diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197 index a2547bc280..69849c800e 100755 --- a/tests/qemu-iotests/197 +++ b/tests/qemu-iotests/197 @@ -93,7 +93,7 @@ output=$($QEMU_IO -f qcow2 -C -c "read -P 0 1k $((2*1024*1024*1024 - 512))" \ "$TEST_WRAP" 2>&1 | _filter_qemu_io) case $output in *allocate*) - _notrun "Insufficent memory to run test" ;; + _notrun "Insufficient memory to run test" ;; *) printf '%s\n' "$output" ;; esac $QEMU_IO -f qcow2 -C -c "read -P 0 $((3*1024*1024*1024 + 1024)) 1k" \ @@ -122,6 +122,35 @@ $QEMU_IO -f qcow2 -C -c 'read 0 1024' "$TEST_WRAP" | _filter_qemu_io $QEMU_IO -f qcow2 -c map "$TEST_WRAP" _check_test_img +echo +echo '=== Copy-on-read with subclusters ===' +echo + +# Create base and top images 64K (1 cluster) each. Make subclusters enabled +# for the top image +_make_test_img 64K +IMGPROTO=file IMGFMT=qcow2 TEST_IMG_FILE="$TEST_WRAP" \ + _make_test_img --no-opts -o extended_l2=true -F "$IMGFMT" -b "$TEST_IMG" \ + 64K | _filter_img_create + +$QEMU_IO -c "write -P 0xaa 0 64k" "$TEST_IMG" | _filter_qemu_io + +# Allocate individual subclusters in the top image, and not the whole cluster +$QEMU_IO -f qcow2 -c "write -P 0xbb 28K 2K" -c "write -P 0xcc 34K 2K" "$TEST_WRAP" \ + | _filter_qemu_io + +# Only 2 subclusters should be allocated in the top image at this point +$QEMU_IO -f qcow2 -c map "$TEST_WRAP" + +# Actual copy-on-read operation +$QEMU_IO -f qcow2 -C -c "read -P 0xaa 30K 4K" "$TEST_WRAP" | _filter_qemu_io + +# And here we should have 4 subclusters allocated right in the middle of the +# top image. Make sure the whole cluster remains unallocated +$QEMU_IO -f qcow2 -c map "$TEST_WRAP" + +_check_test_img + # success, all done echo '*** done' status=0 diff --git a/tests/qemu-iotests/197.out b/tests/qemu-iotests/197.out index ad414c3b0e..86c57b51d3 100644 --- a/tests/qemu-iotests/197.out +++ b/tests/qemu-iotests/197.out @@ -31,4 +31,26 @@ read 1024/1024 bytes at offset 0 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) No errors were found on the image. + +=== Copy-on-read with subclusters === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 +Formatting 'TEST_DIR/t.wrap.IMGFMT', fmt=IMGFMT size=65536 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2048/2048 bytes at offset 28672 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2048/2048 bytes at offset 34816 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +28 KiB (0x7000) bytes not allocated at offset 0 bytes (0x0) +2 KiB (0x800) bytes allocated at offset 28 KiB (0x7000) +4 KiB (0x1000) bytes not allocated at offset 30 KiB (0x7800) +2 KiB (0x800) bytes allocated at offset 34 KiB (0x8800) +28 KiB (0x7000) bytes not allocated at offset 36 KiB (0x9000) +read 4096/4096 bytes at offset 30720 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +28 KiB (0x7000) bytes not allocated at offset 0 bytes (0x0) +8 KiB (0x2000) bytes allocated at offset 28 KiB (0x7000) +28 KiB (0x7000) bytes not allocated at offset 36 KiB (0x9000) +No errors were found on the image. *** done diff --git a/tests/qemu-iotests/198 b/tests/qemu-iotests/198 index 1c93dea1f7..6ddeffddd2 100755 --- a/tests/qemu-iotests/198 +++ b/tests/qemu-iotests/198 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux _require_working_luks diff --git a/tests/qemu-iotests/198.out b/tests/qemu-iotests/198.out index 805494916f..62fb73fa3e 100644 --- a/tests/qemu-iotests/198.out +++ b/tests/qemu-iotests/198.out @@ -39,6 +39,7 @@ Format specific information: compression type: COMPRESSION_TYPE encrypt: ivgen alg: plain64 + detached header: false hash alg: sha256 cipher alg: aes-256 uuid: 00000000-0000-0000-0000-000000000000 @@ -84,6 +85,7 @@ Format specific information: compression type: COMPRESSION_TYPE encrypt: ivgen alg: plain64 + detached header: false hash alg: sha256 cipher alg: aes-256 uuid: 00000000-0000-0000-0000-000000000000 diff --git a/tests/qemu-iotests/202 b/tests/qemu-iotests/202 index b784dcd791..13304242e5 100755 --- a/tests/qemu-iotests/202 +++ b/tests/qemu-iotests/202 @@ -21,7 +21,7 @@ # Check that QMP 'transaction' blockdev-snapshot-sync with multiple drives on a # single IOThread completes successfully. This particular command triggered a # hang due to recursive AioContext locking and BDRV_POLL_WHILE(). Protect -# against regressions. +# against regressions even though the AioContext lock no longer exists. import iotests diff --git a/tests/qemu-iotests/203 b/tests/qemu-iotests/203 index ab80fd0e44..1ba878522b 100755 --- a/tests/qemu-iotests/203 +++ b/tests/qemu-iotests/203 @@ -21,7 +21,8 @@ # Check that QMP 'migrate' with multiple drives on a single IOThread completes # successfully. This particular command triggered a hang in the source QEMU # process due to recursive AioContext locking in bdrv_invalidate_all() and -# BDRV_POLL_WHILE(). +# BDRV_POLL_WHILE(). Protect against regressions even though the AioContext +# lock no longer exists. import iotests diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205 index 15f798288a..2370e1a138 100755 --- a/tests/qemu-iotests/205 +++ b/tests/qemu-iotests/205 @@ -44,10 +44,8 @@ class TestNbdServerRemove(iotests.QMPTestCase): } } - result = self.vm.qmp('nbd-server-start', addr=address) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('nbd-server-add', device='drive0', name='exp') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('nbd-server-start', addr=address) + self.vm.cmd('nbd-server-add', device='drive0', name='exp') def tearDown(self): self.vm.shutdown() diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out index 7e95694777..979f00f9bf 100644 --- a/tests/qemu-iotests/206.out +++ b/tests/qemu-iotests/206.out @@ -114,6 +114,7 @@ Format specific information: refcount bits: 16 encrypt: ivgen alg: plain64 + detached header: false hash alg: sha1 cipher alg: aes-128 uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX diff --git a/tests/qemu-iotests/209.out b/tests/qemu-iotests/209.out index 515906ac7a..79c4103a22 100644 --- a/tests/qemu-iotests/209.out +++ b/tests/qemu-iotests/209.out @@ -1,4 +1,4 @@ -[{ "start": 0, "length": 524288, "depth": 0, "present": true, "zero": false, "data": true, "offset": 0}, -{ "start": 524288, "length": 524288, "depth": 0, "present": true, "zero": true, "data": false, "offset": 524288}] +[{ "start": 0, "length": 524288, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 0}, +{ "start": 524288, "length": 524288, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 524288}] done. diff --git a/tests/qemu-iotests/210.out b/tests/qemu-iotests/210.out index 96d9f749dd..94b29b2120 100644 --- a/tests/qemu-iotests/210.out +++ b/tests/qemu-iotests/210.out @@ -18,6 +18,7 @@ virtual size: 128 MiB (134217728 bytes) encrypted: yes Format specific information: ivgen alg: plain64 + detached header: false hash alg: sha256 cipher alg: aes-256 uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX @@ -70,6 +71,7 @@ virtual size: 64 MiB (67108864 bytes) encrypted: yes Format specific information: ivgen alg: plain64 + detached header: false hash alg: sha1 cipher alg: aes-128 uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX @@ -125,6 +127,7 @@ virtual size: 0 B (0 bytes) encrypted: yes Format specific information: ivgen alg: plain64 + detached header: false hash alg: sha256 cipher alg: aes-256 uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX @@ -195,6 +198,7 @@ virtual size: 0 B (0 bytes) encrypted: yes Format specific information: ivgen alg: plain64 + detached header: false hash alg: sha256 cipher alg: aes-256 uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX diff --git a/tests/qemu-iotests/214 b/tests/qemu-iotests/214 index c66e246ba2..55ffcd7f44 100755 --- a/tests/qemu-iotests/214 +++ b/tests/qemu-iotests/214 @@ -102,7 +102,8 @@ let data_size="8 * $cluster_size" $QEMU_IO -c "write -P 0xaa 0 $data_size" "$TEST_IMG" \ 2>&1 | _filter_qemu_io | _filter_testdir sizeA=$($QEMU_IMG info --output=json "$TEST_IMG" | - sed -n '/"actual-size":/ s/[^0-9]//gp') + sed -n '/"actual-size":/ s/[^0-9]//gp' | + head -n 1) _make_test_img 2M -o cluster_size=$cluster_size echo "Write compressed data:" @@ -124,7 +125,8 @@ $QEMU_IO -c "write -P 0xcc $offset $data_size" "json:{\ _filter_qemu_io | _filter_testdir sizeB=$($QEMU_IMG info --output=json "$TEST_IMG" | - sed -n '/"actual-size":/ s/[^0-9]//gp') + sed -n '/"actual-size":/ s/[^0-9]//gp' | + head -n 1) if [ $sizeA -lt $sizeB ] then diff --git a/tests/qemu-iotests/215 b/tests/qemu-iotests/215 index d464596f14..6babbcdc1f 100755 --- a/tests/qemu-iotests/215 +++ b/tests/qemu-iotests/215 @@ -95,7 +95,7 @@ output=$($QEMU_IO \ 2>&1 | _filter_qemu_io) case $output in *allocate*) - _notrun "Insufficent memory to run test" ;; + _notrun "Insufficient memory to run test" ;; *) printf '%s\n' "$output" ;; esac $QEMU_IO \ diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218 index 6320c4cb56..81aa68806f 100755 --- a/tests/qemu-iotests/218 +++ b/tests/qemu-iotests/218 @@ -41,34 +41,30 @@ iotests.script_initialize(supported_fmts=['qcow2', 'raw']) def start_mirror(vm, speed=None, buf_size=None): vm.launch() - ret = vm.qmp('blockdev-add', - node_name='source', - driver='null-co', - size=1048576) - assert ret['return'] == {} + vm.cmd('blockdev-add', + node_name='source', + driver='null-co', + size=1048576) - ret = vm.qmp('blockdev-add', - node_name='target', - driver='null-co', - size=1048576) - assert ret['return'] == {} + vm.cmd('blockdev-add', + node_name='target', + driver='null-co', + size=1048576) if speed is not None: - ret = vm.qmp('blockdev-mirror', - job_id='mirror', - device='source', - target='target', - sync='full', - speed=speed, - buf_size=buf_size) + vm.cmd('blockdev-mirror', + job_id='mirror', + device='source', + target='target', + sync='full', + speed=speed, + buf_size=buf_size) else: - ret = vm.qmp('blockdev-mirror', - job_id='mirror', - device='source', - target='target', - sync='full') - - assert ret['return'] == {} + vm.cmd('blockdev-mirror', + job_id='mirror', + device='source', + target='target', + sync='full') log('') @@ -150,38 +146,33 @@ with iotests.VM() as vm, \ vm.launch() - ret = vm.qmp('object-add', qom_type='throttle-group', id='tg', - limits={'bps-read': 4096}) - assert ret['return'] == {} + vm.cmd('object-add', qom_type='throttle-group', id='tg', + limits={'bps-read': 4096}) - ret = vm.qmp('blockdev-add', - node_name='source', - driver=iotests.imgfmt, - file={ - 'driver': 'file', - 'filename': src_img_path - }) - assert ret['return'] == {} + vm.cmd('blockdev-add', + node_name='source', + driver=iotests.imgfmt, + file={ + 'driver': 'file', + 'filename': src_img_path + }) - ret = vm.qmp('blockdev-add', - node_name='throttled-source', - driver='throttle', - throttle_group='tg', - file='source') - assert ret['return'] == {} + vm.cmd('blockdev-add', + node_name='throttled-source', + driver='throttle', + throttle_group='tg', + file='source') - ret = vm.qmp('blockdev-add', - node_name='target', - driver='null-co', - size=(64 * 1048576)) - assert ret['return'] == {} + vm.cmd('blockdev-add', + node_name='target', + driver='null-co', + size=(64 * 1048576)) - ret = vm.qmp('blockdev-mirror', - job_id='mirror', - device='throttled-source', - target='target', - sync='full') - assert ret['return'] == {} + vm.cmd('blockdev-mirror', + job_id='mirror', + device='throttled-source', + target='target', + sync='full') log(vm.qmp('quit')) diff --git a/tests/qemu-iotests/221.out b/tests/qemu-iotests/221.out index 9cdd171a2d..df231c4e3d 100644 --- a/tests/qemu-iotests/221.out +++ b/tests/qemu-iotests/221.out @@ -5,14 +5,14 @@ QA output created by 221 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65537 discard 65537/65537 bytes at offset 0 64.001 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 66048, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] -[{ "start": 0, "length": 66048, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] +[{ "start": 0, "length": 66048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] +[{ "start": 0, "length": 66048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] wrote 1/1 bytes at offset 65536 1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 65536, "length": 1, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 65537, "length": 511, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] -[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 65536, "length": 1, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 65537, "length": 511, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] +[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, +{ "start": 65536, "length": 1, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 65537, "length": 511, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] +[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, +{ "start": 65536, "length": 1, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 65537, "length": 511, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] *** done diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out index 0647941531..5f5b42e2dc 100644 --- a/tests/qemu-iotests/223.out +++ b/tests/qemu-iotests/223.out @@ -11,8 +11,8 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} === Write part of the file under active bitmap === @@ -83,29 +83,32 @@ exports available: 0 exports available: 3 export: 'n' size: 4194304 - flags: 0x58f ( readonly flush fua df multi cache ) + flags: 0x158f ( readonly flush fua df multi cache block-status-payload ) min block: 1 opt block: 4096 max block: 33554432 + transaction size: 64-bit available meta contexts: 2 base:allocation qemu:dirty-bitmap:b export: 'n2' description: some text size: 4194304 - flags: 0xced ( flush fua trim zeroes df cache fast-zero ) + flags: 0x1ded ( flush fua trim zeroes df multi cache fast-zero block-status-payload ) min block: 1 opt block: 4096 max block: 33554432 + transaction size: 64-bit available meta contexts: 2 base:allocation qemu:dirty-bitmap:b2 export: 'n3' size: 4194304 - flags: 0x58f ( readonly flush fua df multi cache ) + flags: 0x158f ( readonly flush fua df multi cache block-status-payload ) min block: 1 opt block: 4096 max block: 33554432 + transaction size: 64-bit available meta contexts: 2 base:allocation qemu:dirty-bitmap:b3 @@ -120,36 +123,36 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 2097152/2097152 bytes at offset 2097152 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 1048576, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] -[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false}, -{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}] +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, +{ "start": 1048576, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] +[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, +{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}] === Contrast to small granularity dirty-bitmap === -[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false}, -{ "start": 1024, "length": 2096128, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}] +[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, +{ "start": 1024, "length": 2096128, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}] === Check bitmap taken from another node === -[{ "start": 0, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] === End qemu NBD server === {"execute":"nbd-server-remove", "arguments":{"name":"n"}} -{"return": {}} -{"execute":"nbd-server-remove", - "arguments":{"name":"n2"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}} {"return": {}} {"execute":"nbd-server-remove", "arguments":{"name":"n2"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}} +{"return": {}} +{"execute":"nbd-server-remove", + "arguments":{"name":"n2"}} {"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}} {"execute":"nbd-server-stop"} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n3"}} @@ -202,29 +205,32 @@ exports available: 0 exports available: 3 export: 'n' size: 4194304 - flags: 0x58f ( readonly flush fua df multi cache ) + flags: 0x158f ( readonly flush fua df multi cache block-status-payload ) min block: 1 opt block: 4096 max block: 33554432 + transaction size: 64-bit available meta contexts: 2 base:allocation qemu:dirty-bitmap:b export: 'n2' description: some text size: 4194304 - flags: 0xced ( flush fua trim zeroes df cache fast-zero ) + flags: 0x1ded ( flush fua trim zeroes df multi cache fast-zero block-status-payload ) min block: 1 opt block: 4096 max block: 33554432 + transaction size: 64-bit available meta contexts: 2 base:allocation qemu:dirty-bitmap:b2 export: 'n3' size: 4194304 - flags: 0x58f ( readonly flush fua df multi cache ) + flags: 0x158f ( readonly flush fua df multi cache block-status-payload ) min block: 1 opt block: 4096 max block: 33554432 + transaction size: 64-bit available meta contexts: 2 base:allocation qemu:dirty-bitmap:b3 @@ -239,36 +245,36 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 2097152/2097152 bytes at offset 2097152 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 1048576, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] -[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false}, -{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}] +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, +{ "start": 1048576, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] +[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, +{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}] === Contrast to small granularity dirty-bitmap === -[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false}, -{ "start": 1024, "length": 2096128, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}] +[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, +{ "start": 1024, "length": 2096128, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}] === Check bitmap taken from another node === -[{ "start": 0, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] === End qemu NBD server === {"execute":"nbd-server-remove", "arguments":{"name":"n"}} -{"return": {}} -{"execute":"nbd-server-remove", - "arguments":{"name":"n2"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}} {"return": {}} {"execute":"nbd-server-remove", "arguments":{"name":"n2"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}} +{"return": {}} +{"execute":"nbd-server-remove", + "arguments":{"name":"n2"}} {"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}} {"execute":"nbd-server-stop"} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n3"}} @@ -276,17 +282,17 @@ read 2097152/2097152 bytes at offset 2097152 {"execute":"nbd-server-stop"} {"error": {"class": "GenericError", "desc": "NBD server not running"}} {"execute":"quit"} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} === Use qemu-nbd as server === -[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false}, -{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}] -[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false}, -{ "start": 1024, "length": 11321, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] -[{ "start": 12345, "length": 2084807, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}] +[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, +{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}] +[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, +{ "start": 1024, "length": 11321, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] +[{ "start": 12345, "length": 2084807, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}] *** done diff --git a/tests/qemu-iotests/227 b/tests/qemu-iotests/227 index 7e45a47ac6..eddaad679e 100755 --- a/tests/qemu-iotests/227 +++ b/tests/qemu-iotests/227 @@ -40,6 +40,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt generic _supported_proto file +_require_devices virtio-blk + do_run_qemu() { echo Testing: "$@" diff --git a/tests/qemu-iotests/227.out b/tests/qemu-iotests/227.out index 9c09ee3917..d6a1d4ecb6 100644 --- a/tests/qemu-iotests/227.out +++ b/tests/qemu-iotests/227.out @@ -17,6 +17,7 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio "stats": { "unmap_operations": 0, "unmap_merged": 0, + "failed_zone_append_operations": 0, "flush_total_time_ns": 0, "wr_highest_offset": 0, "wr_total_time_ns": 0, @@ -27,6 +28,7 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio "timed_stats": [ ], "failed_unmap_operations": 0, + "zone_append_merged": 0, "failed_flush_operations": 0, "account_invalid": true, "rd_total_time_ns": 0, @@ -39,7 +41,11 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio "unmap_total_time_ns": 0, "invalid_flush_operations": 0, "account_failed": true, + "zone_append_total_time_ns": 0, + "zone_append_operations": 0, "rd_operations": 0, + "zone_append_bytes": 0, + "invalid_zone_append_operations": 0, "invalid_wr_operations": 0, "invalid_rd_operations": 0 }, @@ -48,10 +54,6 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio } ] } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -63,6 +65,10 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio "reason": "host-qmp-quit" } } +{ + "return": { + } +} === blockstats with -drive if=none === @@ -82,6 +88,7 @@ Testing: -drive driver=null-co,if=none "stats": { "unmap_operations": 0, "unmap_merged": 0, + "failed_zone_append_operations": 0, "flush_total_time_ns": 0, "wr_highest_offset": 0, "wr_total_time_ns": 0, @@ -92,6 +99,7 @@ Testing: -drive driver=null-co,if=none "timed_stats": [ ], "failed_unmap_operations": 0, + "zone_append_merged": 0, "failed_flush_operations": 0, "account_invalid": true, "rd_total_time_ns": 0, @@ -104,7 +112,11 @@ Testing: -drive driver=null-co,if=none "unmap_total_time_ns": 0, "invalid_flush_operations": 0, "account_failed": true, + "zone_append_total_time_ns": 0, + "zone_append_operations": 0, "rd_operations": 0, + "zone_append_bytes": 0, + "invalid_zone_append_operations": 0, "invalid_wr_operations": 0, "invalid_rd_operations": 0 }, @@ -112,10 +124,6 @@ Testing: -drive driver=null-co,if=none } ] } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -127,6 +135,10 @@ Testing: -drive driver=null-co,if=none "reason": "host-qmp-quit" } } +{ + "return": { + } +} === blockstats with -blockdev === @@ -143,10 +155,6 @@ Testing: -blockdev driver=null-co,node-name=null "return": [ ] } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -158,6 +166,10 @@ Testing: -blockdev driver=null-co,node-name=null "reason": "host-qmp-quit" } } +{ + "return": { + } +} === blockstats with -blockdev and -device === @@ -177,6 +189,7 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b "stats": { "unmap_operations": 0, "unmap_merged": 0, + "failed_zone_append_operations": 0, "flush_total_time_ns": 0, "wr_highest_offset": 0, "wr_total_time_ns": 0, @@ -187,8 +200,9 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b "timed_stats": [ ], "failed_unmap_operations": 0, + "zone_append_merged": 0, "failed_flush_operations": 0, - "account_invalid": false, + "account_invalid": true, "rd_total_time_ns": 0, "invalid_unmap_operations": 0, "flush_operations": 0, @@ -198,8 +212,12 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b "rd_bytes": 0, "unmap_total_time_ns": 0, "invalid_flush_operations": 0, - "account_failed": false, + "account_failed": true, + "zone_append_total_time_ns": 0, + "zone_append_operations": 0, "rd_operations": 0, + "zone_append_bytes": 0, + "invalid_zone_append_operations": 0, "invalid_wr_operations": 0, "invalid_rd_operations": 0 }, @@ -208,10 +226,6 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b } ] } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -223,5 +237,9 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b "reason": "host-qmp-quit" } } +{ + "return": { + } +} *** done diff --git a/tests/qemu-iotests/233.out b/tests/qemu-iotests/233.out index 237c82767e..1910f7df20 100644 --- a/tests/qemu-iotests/233.out +++ b/tests/qemu-iotests/233.out @@ -39,6 +39,7 @@ exports available: 1 export: '' size: 67108864 min block: 1 + transaction size: 64-bit == check TLS fail over TCP with mismatched hostname == qemu-img: Could not open 'driver=nbd,host=localhost,port=PORT,tls-creds=tls0': Certificate does not match the hostname localhost @@ -53,6 +54,7 @@ exports available: 1 export: '' size: 67108864 min block: 1 + transaction size: 64-bit == check TLS with different CA fails == qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': The certificate hasn't got a known issuer @@ -83,6 +85,7 @@ exports available: 1 export: '' size: 67108864 min block: 1 + transaction size: 64-bit == check TLS works over UNIX with PSK == image: nbd+unix://?socket=SOCK_DIR/qemu-nbd.sock @@ -93,6 +96,7 @@ exports available: 1 export: '' size: 67108864 min block: 1 + transaction size: 64-bit == check TLS fails over UNIX with mismatch PSK == qemu-img: Could not open 'driver=nbd,path=SOCK_DIR/qemu-nbd.sock,tls-creds=tls0': TLS handshake failed: The TLS connection was non-properly terminated. diff --git a/tests/qemu-iotests/234.out b/tests/qemu-iotests/234.out index 692976d1c6..ac8b64350c 100644 --- a/tests/qemu-iotests/234.out +++ b/tests/qemu-iotests/234.out @@ -15,8 +15,8 @@ Starting migration to B... {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} completed completed -{"return": {"running": false, "singlestep": false, "status": "postmigrate"}} -{"return": {"running": true, "singlestep": false, "status": "running"}} +{"return": {"running": false, "status": "postmigrate"}} +{"return": {"running": true, "status": "running"}} Add a second parent to drive0-file... {"return": {}} Restart A with -incoming and second parent... @@ -32,5 +32,5 @@ Starting migration back to A... {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} completed completed -{"return": {"running": true, "singlestep": false, "status": "running"}} -{"return": {"running": false, "singlestep": false, "status": "postmigrate"}} +{"return": {"running": true, "status": "running"}} +{"return": {"running": false, "status": "postmigrate"}} diff --git a/tests/qemu-iotests/241.out b/tests/qemu-iotests/241.out index 88e8cfcd7e..7267cd2997 100644 --- a/tests/qemu-iotests/241.out +++ b/tests/qemu-iotests/241.out @@ -6,8 +6,9 @@ exports available: 1 export: '' size: 1024 min block: 1 -[{ "start": 0, "length": 1000, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 1000, "length": 24, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] + transaction size: 64-bit +[{ "start": 0, "length": 1000, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 1000, "length": 24, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) === Exporting unaligned raw image, forced server sector alignment === @@ -16,7 +17,8 @@ exports available: 1 export: '' size: 1024 min block: 512 -[{ "start": 0, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] + transaction size: 64-bit +[{ "start": 0, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. @@ -28,7 +30,8 @@ exports available: 1 export: '' size: 1024 min block: 1 -[{ "start": 0, "length": 1000, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 1000, "length": 24, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] + transaction size: 64-bit +[{ "start": 0, "length": 1000, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 1000, "length": 24, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) *** done diff --git a/tests/qemu-iotests/244.out b/tests/qemu-iotests/244.out index 5e03add054..f46cfe93f1 100644 --- a/tests/qemu-iotests/244.out +++ b/tests/qemu-iotests/244.out @@ -41,7 +41,7 @@ write failed: Operation not supported No errors were found on the image. Take an internal snapshot: -qemu-img: Could not create snapshot 'test': -95 (Operation not supported) +qemu-img: Could not create snapshot 'test': Operation not supported No errors were found on the image. === Standalone image with external data file (efficient) === @@ -57,12 +57,12 @@ wrote 3145728/3145728 bytes at offset 3145728 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. -[{ "start": 0, "length": 1048576, "depth": 0, "present": false, "zero": true, "data": false}, -{ "start": 1048576, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "offset": 1048576}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 4194304, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": false, "offset": 4194304}, -{ "start": 5242880, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 6291456, "length": 60817408, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 1048576, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 1048576, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 1048576}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 4194304, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 4194304}, +{ "start": 5242880, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 6291456, "length": 60817408, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] read 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -94,10 +94,10 @@ wrote 3145728/3145728 bytes at offset 3145728 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. -[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": 0}, -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "offset": 4194304}, -{ "start": 6291456, "length": 60817408, "depth": 0, "present": true, "zero": false, "data": true, "offset": 6291456}] +[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 0}, +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 4194304}, +{ "start": 6291456, "length": 60817408, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 6291456}] read 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -123,8 +123,8 @@ read 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Offset Length Mapped to File 0 0x100000 0 TEST_DIR/t.qcow2.data -[{ "start": 0, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "offset": 0}, -{ "start": 1048576, "length": 66060288, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 0}, +{ "start": 1048576, "length": 66060288, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] === Copy offloading === diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 index edaf29094b..a934c9d1e6 100755 --- a/tests/qemu-iotests/245 +++ b/tests/qemu-iotests/245 @@ -136,8 +136,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): def test_incorrect_parameters_single_file(self): # Open 'hd0' only (no backing files) opts = hd_opts(0) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) original_graph = self.vm.qmp('query-named-block-nodes') # We can reopen the image passing the same options @@ -171,8 +170,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.check_node_graph(original_graph) # Remove the node - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'hd0') # This test opens an image with a backing file and tries to reopen # it with illegal / incorrect parameters. @@ -180,8 +178,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): # Open hd1 omitting the backing options (hd0 will be opened # with the default options) opts = hd_opts(1) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) original_graph = self.vm.qmp('query-named-block-nodes') # We can't reopen the image passing the same options, 'backing' is mandatory @@ -213,8 +210,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.check_node_graph(original_graph) # Remove the node - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'hd1') # Reopen an image several times changing some of its options def test_reopen(self): @@ -230,8 +226,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): # Open the hd1 image passing all backing options opts = hd_opts(1) opts['backing'] = hd_opts(0) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) original_graph = self.vm.qmp('query-named-block-nodes') # We can reopen the image passing the same options @@ -306,8 +301,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.assert_qmp_absent(self.get_node('hd1'), 'image/backing-image') # Open the 'hd0' image - result = self.vm.qmp('blockdev-add', conv_keys = False, **hd_opts(0)) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **hd_opts(0)) # Reopen the hd1 image setting 'hd0' as its backing image self.reopen(opts, {'backing': 'hd0'}) @@ -326,10 +320,8 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.assert_qmp(result, 'error/desc', "Node 'hd0' is busy: node is used as backing hd of 'hd1'") # But we can remove both nodes if done in the proper order - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1') - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'hd1') + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'hd0') # Reopen a raw image and see the effect of changing the 'offset' option def test_reopen_raw(self): @@ -345,8 +337,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): qemu_io('-f', 'raw', '-c', 'write -P 0xa1 1M 1M', hd_path[0]) # Open the raw file with QEMU - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # Read 1MB from offset 0 self.run_qemu_io("hd0", "read -P 0xa0 0 1M") @@ -362,8 +353,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.run_qemu_io("hd0", "read -P 0xa0 0 1M") # Remove the block device - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'hd0') # Omitting an option should reset it to the default value, but if # an option cannot be changed it shouldn't be possible to reset it @@ -377,8 +367,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): 'node-name': 'hd0-file' } } # Open the file with QEMU - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # file.x-check-cache-dropped can be changed... self.reopen(opts, { 'file.x-check-cache-dropped': False }) @@ -394,8 +383,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.reopen(opts, { 'file.locking': 'off' }) # Remove the block device - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'hd0') # This test modifies the node graph a few times by changing the # 'backing' option on reopen and verifies that the guest data that @@ -407,8 +395,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): for i in range(3): opts.append(hd_opts(i)) opts[i]['backing'] = None - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts[i]) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts[i]) # hd0 self.run_qemu_io("hd0", "read -P 0xa0 0 1M") @@ -499,8 +486,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): for i in range(3): opts.append(hd_opts(i)) opts[i]['backing'] = None - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts[i]) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts[i]) # hd1 <- hd0, hd1 <- hd2 self.reopen(opts[0], {'backing': 'hd1'}) @@ -532,8 +518,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): 'node-name': 'bv', 'test': 'hd0', 'raw': 'hd1'} - result = self.vm.qmp('blockdev-add', conv_keys = False, **bvopts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **bvopts) # blkverify doesn't currently allow reopening. TODO: implement this self.reopen(bvopts, {}, "Block format 'blkverify' used by node 'bv'" + @@ -544,8 +529,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): "Making 'bv' a backing child of 'hd0' would create a cycle") # Delete the blkverify node - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bv') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'bv') # Replace the protocol layer ('file' parameter) of a disk image def test_replace_file(self): @@ -556,16 +540,13 @@ class TestBlockdevReopen(iotests.QMPTestCase): hd0_opts = {'driver': 'file', 'node-name': 'hd0-file', 'filename': hd_path[0] } hd1_opts = {'driver': 'file', 'node-name': 'hd1-file', 'filename': hd_path[1] } - result = self.vm.qmp('blockdev-add', conv_keys = False, **hd0_opts) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('blockdev-add', conv_keys = False, **hd1_opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **hd0_opts) + self.vm.cmd('blockdev-add', conv_keys = False, **hd1_opts) # Add a raw format layer that uses hd0-file as its protocol layer opts = {'driver': 'raw', 'node-name': 'hd', 'file': 'hd0-file'} - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # Fill the image with data self.run_qemu_io("hd", "read -P 0 0 10k") @@ -588,21 +569,18 @@ class TestBlockdevReopen(iotests.QMPTestCase): def test_insert_throttle_filter(self): # Add an image to the VM hd0_opts = hd_opts(0) - result = self.vm.qmp('blockdev-add', conv_keys = False, **hd0_opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **hd0_opts) # Create a throttle-group object opts = { 'qom-type': 'throttle-group', 'id': 'group0', 'limits': { 'iops-total': 1000 } } - result = self.vm.qmp('object-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('object-add', conv_keys = False, **opts) # Add a throttle filter with the group that we just created. # The filter is not used by anyone yet opts = { 'driver': 'throttle', 'node-name': 'throttle0', 'throttle-group': 'group0', 'file': 'hd0-file' } - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # Insert the throttle filter between hd0 and hd0-file self.reopen(hd0_opts, {'file': 'throttle0'}) @@ -611,18 +589,17 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.reopen(hd0_opts, {'file': 'hd0-file'}) # Insert (and remove) a compress filter + @iotests.skip_if_unsupported(['compress']) def test_insert_compress_filter(self): # Add an image to the VM: hd (raw) -> hd0 (qcow2) -> hd0-file (file) opts = {'driver': 'raw', 'node-name': 'hd', 'file': hd_opts(0)} - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # Add a 'compress' filter filter_opts = {'driver': 'compress', 'node-name': 'compress0', 'file': 'hd0'} - result = self.vm.qmp('blockdev-add', conv_keys = False, **filter_opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **filter_opts) # Unmap the beginning of the image (we cannot write compressed # data to an allocated cluster) @@ -650,20 +627,18 @@ class TestBlockdevReopen(iotests.QMPTestCase): # Check the first byte of the first three L2 entries and verify that # the second one is compressed (0x40) while the others are not (0x80) - iotests.qemu_io_log('-f', 'raw', '-c', 'read -P 0x80 0x40000 1', - '-c', 'read -P 0x40 0x40008 1', - '-c', 'read -P 0x80 0x40010 1', hd_path[0]) + iotests.qemu_io('-f', 'raw', '-c', 'read -P 0x80 0x40000 1', + '-c', 'read -P 0x40 0x40008 1', + '-c', 'read -P 0x80 0x40010 1', hd_path[0]) # Swap the disk images of two active block devices def test_swap_files(self): # Add hd0 and hd2 (none of them with backing files) opts0 = hd_opts(0) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts0) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts0) opts2 = hd_opts(2) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts2) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts2) # Write different data to both block devices self.run_qemu_io("hd0", "write -P 0xa0 0 1k") @@ -711,15 +686,13 @@ class TestBlockdevReopen(iotests.QMPTestCase): opts = hd_opts(i) # Open all three images without backing file opts['backing'] = None - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) opts = {'driver': 'quorum', 'node-name': 'quorum0', 'children': ['hd0', 'hd1', 'hd2'], 'vote-threshold': 2} - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # Quorum doesn't currently allow reopening. TODO: implement this self.reopen(opts, {}, "Block format 'quorum' used by node 'quorum0'" + @@ -731,14 +704,12 @@ class TestBlockdevReopen(iotests.QMPTestCase): "Making 'quorum0' a backing child of 'hd0' would create a cycle") # Delete quorum0 - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'quorum0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'quorum0') # Delete hd0, hd1 and hd2 for i in range(3): - result = self.vm.qmp('blockdev-del', conv_keys = True, - node_name = 'hd%d' % i) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, + node_name = 'hd%d' % i) ###################### ###### blkdebug ###### @@ -747,8 +718,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): 'node-name': 'bd', 'config': '/dev/null', 'image': hd_opts(0)} - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # blkdebug allows reopening if we keep the same options self.reopen(opts) @@ -761,16 +731,14 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.reopen(opts, {}, "Option 'config' cannot be reset to its default value") # Delete the blkdebug node - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bd') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'bd') ################## ###### null ###### ################## opts = {'driver': 'null-co', 'node-name': 'root', 'size': 1024} - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # 1 << 30 is the default value, but we cannot change it explicitly self.reopen(opts, {'size': (1 << 30)}, "Cannot change the option 'size'") @@ -779,16 +747,14 @@ class TestBlockdevReopen(iotests.QMPTestCase): del opts['size'] self.reopen(opts, {}, "Option 'size' cannot be reset to its default value") - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'root') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'root') ################## ###### file ###### ################## opts = hd_opts(0) opts['file']['locking'] = 'on' - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # 'locking' cannot be changed del opts['file']['locking'] @@ -802,27 +768,23 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.reopen(opts, {'locking': 'off'}, "Cannot change the option 'locking'") self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value") - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'hd0') ###################### ###### throttle ###### ###################### opts = { 'qom-type': 'throttle-group', 'id': 'group0', 'limits': { 'iops-total': 1000 } } - result = self.vm.qmp('object-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('object-add', conv_keys = False, **opts) opts = { 'qom-type': 'throttle-group', 'id': 'group1', 'limits': { 'iops-total': 2000 } } - result = self.vm.qmp('object-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('object-add', conv_keys = False, **opts) # Add a throttle filter with group = group0 opts = { 'driver': 'throttle', 'node-name': 'throttle0', 'throttle-group': 'group0', 'file': hd_opts(0) } - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # We can reopen it if we keep the same options self.reopen(opts) @@ -850,16 +812,13 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.assert_qmp(result, 'error/desc', "object 'group0' is in use, can not be deleted") # But group1 is free this time, and it can be deleted - result = self.vm.qmp('object-del', id = 'group1') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('object-del', id = 'group1') # Let's delete the filter node - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'throttle0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'throttle0') # And we can finally get rid of group0 - result = self.vm.qmp('object-del', id = 'group0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('object-del', id = 'group0') # If an image has a backing file then the 'backing' option must be # passed on reopen. We don't allow leaving the option out in this @@ -867,13 +826,11 @@ class TestBlockdevReopen(iotests.QMPTestCase): def test_missing_backing_options_1(self): # hd2 opts = hd_opts(2) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # hd0 opts = hd_opts(0) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # hd0 has no backing file: we can omit the 'backing' option self.reopen(opts) @@ -896,11 +853,9 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.reopen(opts) # Remove both hd0 and hd2 - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'hd0') - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'hd2') # If an image has default backing file (as part of its metadata) # then the 'backing' option must be passed on reopen. We don't @@ -910,8 +865,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): # hd0 <- hd1 # (hd0 is hd1's default backing file) opts = hd_opts(1) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # hd1 has a backing file: we can't omit the 'backing' option self.reopen(opts, {}, "backing is missing for 'hd1'") @@ -922,8 +876,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): # No backing file attached to hd1 now, but we still can't omit the 'backing' option self.reopen(opts, {}, "backing is missing for 'hd1'") - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'hd1') # Test that making 'backing' a reference to an existing child # keeps its current options @@ -936,8 +889,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): opts['detect-zeroes'] = 'on' opts['backing']['detect-zeroes'] = 'on' opts['backing']['backing']['detect-zeroes'] = 'on' - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # Reopen the chain passing the minimum amount of required options. # By making 'backing' a reference to hd1 (instead of a sub-dict) @@ -960,12 +912,10 @@ class TestBlockdevReopen(iotests.QMPTestCase): opts = hd_opts(0) opts['backing'] = hd_opts(1) opts['backing']['backing'] = None - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # Stream hd1 into hd0 and wait until it's done - result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', device = 'hd0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', conv_keys = True, job_id = 'stream0', device = 'hd0') self.wait_until_completed(drive = 'stream0') # Now we have only hd0 @@ -981,8 +931,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): # We can also reopen hd0 if we set 'backing' to null self.reopen(opts, {'backing': None}) - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'hd0') # Another block_stream test def test_block_stream_2(self): @@ -990,13 +939,11 @@ class TestBlockdevReopen(iotests.QMPTestCase): opts = hd_opts(0) opts['backing'] = hd_opts(1) opts['backing']['backing'] = hd_opts(2) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # Stream hd1 into hd0 and wait until it's done - result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', - device = 'hd0', base_node = 'hd2') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', conv_keys = True, job_id = 'stream0', + device = 'hd0', base_node = 'hd2') self.wait_until_completed(drive = 'stream0') # The chain is hd2 <- hd0 now. hd1 is missing @@ -1018,8 +965,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.reopen(opts, {}, "backing is missing for 'hd0'") # Now we can delete hd0 (and hd2) - result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', conv_keys = True, node_name = 'hd0') self.assertEqual(self.get_node('hd2'), None) # Reopen the chain during a block-stream job (from hd1 to hd0) @@ -1028,14 +974,12 @@ class TestBlockdevReopen(iotests.QMPTestCase): opts = hd_opts(0) opts['backing'] = hd_opts(1) opts['backing']['backing'] = hd_opts(2) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # hd2 <- hd0 - result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', - device = 'hd0', base_node = 'hd2', - auto_finalize = False) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', conv_keys = True, job_id = 'stream0', + device = 'hd0', base_node = 'hd2', + auto_finalize = False) # We can remove hd2 while the stream job is ongoing opts['backing']['backing'] = None @@ -1053,14 +997,12 @@ class TestBlockdevReopen(iotests.QMPTestCase): opts = hd_opts(0) opts['backing'] = hd_opts(1) opts['backing']['backing'] = hd_opts(2) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) # hd1 <- hd0 - result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', - device = 'hd1', filter_node_name='cor', - auto_finalize = False) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', conv_keys = True, job_id = 'stream0', + device = 'hd1', filter_node_name='cor', + auto_finalize = False) # We can't reopen with the original options because there is a filter # inserted by stream job above hd1. @@ -1089,12 +1031,10 @@ class TestBlockdevReopen(iotests.QMPTestCase): opts = hd_opts(0) opts['backing'] = hd_opts(1) opts['backing']['backing'] = hd_opts(2) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) - result = self.vm.qmp('block-commit', conv_keys = True, job_id = 'commit0', - device = 'hd0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', conv_keys = True, job_id = 'commit0', + device = 'hd0') # We can't remove hd2 while the commit job is ongoing opts['backing']['backing'] = None @@ -1109,8 +1049,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.assert_qmp(event, 'data/type', 'commit') self.assert_qmp_absent(event, 'data/error') - result = self.vm.qmp('block-job-complete', device='commit0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-complete', device='commit0') self.wait_until_completed(drive = 'commit0') @@ -1120,13 +1059,11 @@ class TestBlockdevReopen(iotests.QMPTestCase): opts = hd_opts(0) opts['backing'] = hd_opts(1) opts['backing']['backing'] = hd_opts(2) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) - result = self.vm.qmp('block-commit', conv_keys = True, job_id = 'commit0', - device = 'hd0', top_node = 'hd1', - auto_finalize = False) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', conv_keys = True, job_id = 'commit0', + device = 'hd0', top_node = 'hd1', + auto_finalize = False) # We can't remove hd2 while the commit job is ongoing opts['backing']['backing'] = None @@ -1146,36 +1083,28 @@ class TestBlockdevReopen(iotests.QMPTestCase): def run_test_iothreads(self, iothread_a, iothread_b, errmsg = None, opts_a = None, opts_b = None): opts = opts_a or hd_opts(0) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts) opts2 = opts_b or hd_opts(2) - result = self.vm.qmp('blockdev-add', conv_keys = False, **opts2) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', conv_keys = False, **opts2) - result = self.vm.qmp('object-add', qom_type='iothread', id='iothread0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('object-add', qom_type='iothread', id='iothread0') - result = self.vm.qmp('object-add', qom_type='iothread', id='iothread1') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('object-add', qom_type='iothread', id='iothread1') - result = self.vm.qmp('device_add', driver='virtio-scsi', id='scsi0', - iothread=iothread_a) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('device_add', driver='virtio-scsi', id='scsi0', + iothread=iothread_a) - result = self.vm.qmp('device_add', driver='virtio-scsi', id='scsi1', - iothread=iothread_b) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('device_add', driver='virtio-scsi', id='scsi1', + iothread=iothread_b) 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', {}) + self.vm.cmd('device_add', driver='scsi-hd', drive='hd0', + share_rw=True, bus="scsi0.0") 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.vm.cmd('device_add', driver='scsi-hd', drive='hd2', + share_rw=True, bus="scsi1.0") # Attaching the backing file may or may not work self.reopen(opts, {'backing': 'hd2'}, errmsg) @@ -1204,8 +1133,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): # Create a throttle-group object opts = { 'qom-type': 'throttle-group', 'id': 'group0', 'limits': { 'iops-total': 1000 } } - result = self.vm.qmp('object-add', conv_keys = False, **opts) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('object-add', conv_keys = False, **opts) # Options with a throttle filter between format and protocol opts = [ diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out index a4e04a3266..0970ced62a 100644 --- a/tests/qemu-iotests/245.out +++ b/tests/qemu-iotests/245.out @@ -10,14 +10,7 @@ {"return": {}} {"data": {"id": "stream0", "type": "stream"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"device": "stream0", "len": 3145728, "offset": 3145728, "speed": 0, "type": "stream"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} -....read 1/1 bytes at offset 262144 -1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 1/1 bytes at offset 262152 -1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 1/1 bytes at offset 262160 -1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - -................ +.................... ---------------------------------------------------------------------- Ran 26 tests diff --git a/tests/qemu-iotests/247.out b/tests/qemu-iotests/247.out index e909e83994..7d252e7fe4 100644 --- a/tests/qemu-iotests/247.out +++ b/tests/qemu-iotests/247.out @@ -17,6 +17,6 @@ QMP_VERSION {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 134217728, "offset": 134217728, "speed": 0, "type": "commit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} *** done diff --git a/tests/qemu-iotests/252.out b/tests/qemu-iotests/252.out index c578129c25..b1aa94cb05 100644 --- a/tests/qemu-iotests/252.out +++ b/tests/qemu-iotests/252.out @@ -23,8 +23,8 @@ read 131072/131072 bytes at offset 131072 read 131072/131072 bytes at offset 262144 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 262144, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 262144, "length": 131072, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 262144, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 262144, "length": 131072, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] read 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -33,7 +33,7 @@ read 131072/131072 bytes at offset 131072 read 131072/131072 bytes at offset 262144 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 262144, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 262144, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false}, -{ "start": 327680, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 262144, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 262144, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 327680, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] *** done diff --git a/tests/qemu-iotests/253.out b/tests/qemu-iotests/253.out index b3dca75a89..b458085adb 100644 --- a/tests/qemu-iotests/253.out +++ b/tests/qemu-iotests/253.out @@ -3,16 +3,16 @@ QA output created by 253 === Check mapping of unaligned raw image === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048575 -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] wrote 65535/65535 bytes at offset 983040 63.999 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 4096, "length": 978944, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 983040, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 4096, "length": 978944, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, -{ "start": 983040, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 4096, "length": 978944, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, +{ "start": 983040, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 4096, "length": 978944, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, +{ "start": 983040, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] *** done diff --git a/tests/qemu-iotests/256 b/tests/qemu-iotests/256 index 13666813bd..f34af6cef7 100755 --- a/tests/qemu-iotests/256 +++ b/tests/qemu-iotests/256 @@ -24,7 +24,7 @@ import os import iotests from iotests import log -iotests._verify_virtio_scsi_pci_or_ccw() +iotests.verify_virtio_scsi_pci_or_ccw() iotests.script_initialize(supported_fmts=['qcow2']) size = 64 * 1024 * 1024 @@ -40,25 +40,25 @@ with iotests.FilePath('img0') as img0_path, \ def create_target(filepath, name, size): basename = os.path.basename(filepath) nodename = "file_{}".format(basename) - log(vm.command('blockdev-create', job_id='job1', - options={ - 'driver': 'file', - 'filename': filepath, - 'size': 0, - })) + log(vm.cmd('blockdev-create', job_id='job1', + options={ + 'driver': 'file', + 'filename': filepath, + 'size': 0, + })) vm.run_job('job1') - log(vm.command('blockdev-add', driver='file', - node_name=nodename, filename=filepath)) - log(vm.command('blockdev-create', job_id='job2', - options={ - 'driver': iotests.imgfmt, - 'file': nodename, - 'size': size, - })) + log(vm.cmd('blockdev-add', driver='file', + node_name=nodename, filename=filepath)) + log(vm.cmd('blockdev-create', job_id='job2', + options={ + 'driver': iotests.imgfmt, + 'file': nodename, + 'size': size, + })) vm.run_job('job2') - log(vm.command('blockdev-add', driver=iotests.imgfmt, - node_name=name, - file=nodename)) + log(vm.cmd('blockdev-add', driver=iotests.imgfmt, + node_name=name, + file=nodename)) log('--- Preparing images & VM ---\n') vm.add_object('iothread,id=iothread0') diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257 index e7e7a2317e..7d3720b8e5 100755 --- a/tests/qemu-iotests/257 +++ b/tests/qemu-iotests/257 @@ -160,26 +160,26 @@ class Drive: file_node_name = "file_{}".format(basename) vm = self.vm - log(vm.command('blockdev-create', job_id='bdc-file-job', - options={ - 'driver': 'file', - 'filename': self.path, - 'size': 0, - })) + log(vm.cmd('blockdev-create', job_id='bdc-file-job', + options={ + 'driver': 'file', + 'filename': self.path, + 'size': 0, + })) vm.run_job('bdc-file-job') - log(vm.command('blockdev-add', driver='file', - node_name=file_node_name, filename=self.path)) + log(vm.cmd('blockdev-add', driver='file', + node_name=file_node_name, filename=self.path)) - log(vm.command('blockdev-create', job_id='bdc-fmt-job', - options={ - 'driver': fmt, - 'file': file_node_name, - 'size': size, - })) + log(vm.cmd('blockdev-create', job_id='bdc-fmt-job', + options={ + 'driver': fmt, + 'file': file_node_name, + 'size': size, + })) vm.run_job('bdc-fmt-job') - log(vm.command('blockdev-add', driver=fmt, - node_name=name, - file=file_node_name)) + log(vm.cmd('blockdev-add', driver=fmt, + node_name=name, + file=file_node_name)) self.fmt = fmt self.size = size self.node = name diff --git a/tests/qemu-iotests/261 b/tests/qemu-iotests/261 index b73da565da..22b969d310 100755 --- a/tests/qemu-iotests/261 +++ b/tests/qemu-iotests/261 @@ -393,7 +393,7 @@ _check_test_img -r all echo echo "$((sn_count - 1)) snapshots should remain:" -echo " qemu-img info reports $(_img_info | grep -c '^ \{32\}') snapshots" +echo " qemu-img info reports $(_img_info | grep -c '^ \{30\}') snapshots" echo " Image header reports $(peek_file_be "$TEST_IMG" 60 4) snapshots" echo @@ -520,7 +520,7 @@ _check_test_img -r all echo echo '65536 snapshots should remain:' -echo " qemu-img info reports $(_img_info | grep -c '^ \{32\}') snapshots" +echo " qemu-img info reports $(_img_info | grep -c '^ \{30\}') snapshots" echo " Image header reports $(peek_file_be "$TEST_IMG" 60 4) snapshots" # success, all done diff --git a/tests/qemu-iotests/262 b/tests/qemu-iotests/262 index 2294fd5ecb..a4a92de45a 100755 --- a/tests/qemu-iotests/262 +++ b/tests/qemu-iotests/262 @@ -25,7 +25,8 @@ import iotests import os iotests.script_initialize(supported_fmts=['qcow2'], - supported_platforms=['linux']) + supported_platforms=['linux'], + required_fmts=['blkverify']) with iotests.FilePath('img') as img_path, \ iotests.FilePath('mig_fifo') as fifo, \ diff --git a/tests/qemu-iotests/262.out b/tests/qemu-iotests/262.out index 8e04c496c4..b8a2d3598d 100644 --- a/tests/qemu-iotests/262.out +++ b/tests/qemu-iotests/262.out @@ -13,5 +13,5 @@ Starting migration to B... {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} completed completed -{"return": {"running": false, "singlestep": false, "status": "postmigrate"}} -{"return": {"running": true, "singlestep": false, "status": "running"}} +{"return": {"running": false, "status": "postmigrate"}} +{"return": {"running": true, "status": "running"}} diff --git a/tests/qemu-iotests/263 b/tests/qemu-iotests/263 index ec09b41405..44fdada0d6 100755 --- a/tests/qemu-iotests/263 +++ b/tests/qemu-iotests/263 @@ -34,6 +34,8 @@ _cleanup() } trap "_cleanup; exit \$status" 0 1 2 3 15 +IMGOPTSSYNTAX=true + # get standard environment, filters and checks . ./common.rc . ./common.filter @@ -73,7 +75,7 @@ echo "testing LUKS qcow2 encryption" echo _make_test_img --object $SECRET -o "encrypt.format=luks,encrypt.key-secret=sec0,encrypt.iter-time=10,cluster_size=64K" $size -_run_test "driver=$IMGFMT,encrypt.key-secret=sec0,file.filename=$TEST_IMG" +_run_test "$TEST_IMG,encrypt.key-secret=sec0" _cleanup_test_img echo @@ -82,7 +84,7 @@ echo _make_test_img --object $SECRET -o "encrypt.format=aes,encrypt.key-secret=sec0,cluster_size=64K" $size -_run_test "driver=$IMGFMT,encrypt.key-secret=sec0,file.filename=$TEST_IMG" +_run_test "$TEST_IMG,encrypt.key-secret=sec0" _cleanup_test_img diff --git a/tests/qemu-iotests/264 b/tests/qemu-iotests/264 index bc431d1a19..c6ba2754e2 100755 --- a/tests/qemu-iotests/264 +++ b/tests/qemu-iotests/264 @@ -25,7 +25,8 @@ import os import iotests from iotests import qemu_img_create, file_path, qemu_nbd_popen -disk_a, disk_b, nbd_sock = file_path('disk_a', 'disk_b', 'nbd-sock') +disk_a, disk_b = file_path('disk_a', 'disk_b') +nbd_sock = file_path('nbd-sock', base_dir=iotests.sock_dir) nbd_uri = 'nbd+unix:///?socket=' + nbd_sock wait_limit = 3.0 wait_step = 0.2 @@ -48,18 +49,16 @@ class TestNbdReconnect(iotests.QMPTestCase): """Stat job with nbd target and kill the server""" assert job in ('blockdev-backup', 'blockdev-mirror') with qemu_nbd_popen('-k', nbd_sock, '-f', iotests.imgfmt, disk_b): - result = self.vm.qmp('blockdev-add', - **{'node_name': 'backup0', - 'driver': 'raw', - 'file': {'driver': 'nbd', - 'server': {'type': 'unix', - 'path': nbd_sock}, - 'reconnect-delay': 10}}) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp(job, device='drive0', - sync='full', target='backup0', - speed=(1 * 1024 * 1024)) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', + {'node-name': 'backup0', + 'driver': 'raw', + 'file': {'driver': 'nbd', + 'server': {'type': 'unix', + 'path': nbd_sock}, + 'reconnect-delay': 10}}) + self.vm.cmd(job, device='drive0', + sync='full', target='backup0', + speed=(1 * 1024 * 1024)) # Wait for some progress t = 0.0 @@ -77,8 +76,7 @@ class TestNbdReconnect(iotests.QMPTestCase): self.assertTrue(jobs) self.assertTrue(jobs[0]['offset'] < jobs[0]['len']) - result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-set-speed', device='drive0', speed=0) # Emulate server down time for 1 second time.sleep(1) @@ -91,17 +89,15 @@ class TestNbdReconnect(iotests.QMPTestCase): with qemu_nbd_popen('-k', nbd_sock, '-f', iotests.imgfmt, disk_b): e = self.vm.event_wait('BLOCK_JOB_COMPLETED') self.assertEqual(e['data']['offset'], size) - result = self.vm.qmp('blockdev-del', node_name='backup0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', node_name='backup0') def cancel_job(self): - result = self.vm.qmp('block-job-cancel', device='drive0', force=True) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-cancel', device='drive0', force=True) start_t = time.time() self.vm.event_wait('BLOCK_JOB_CANCELLED') delta_t = time.time() - start_t - self.assertTrue(delta_t < 2.0) + self.assertTrue(delta_t < 5.0) def test_mirror_cancel(self): # Mirror speed limit doesn't work well enough, it seems that mirror diff --git a/tests/qemu-iotests/267.out b/tests/qemu-iotests/267.out index 7176e376e1..f6f5d8715a 100644 --- a/tests/qemu-iotests/267.out +++ b/tests/qemu-iotests/267.out @@ -33,8 +33,8 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) savevm snap0 (qemu) info snapshots List of snapshots present on all disks: -ID TAG VM SIZE DATE VM CLOCK ICOUNT --- snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +-- snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- (qemu) loadvm snap0 (qemu) quit @@ -44,8 +44,8 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) savevm snap0 (qemu) info snapshots List of snapshots present on all disks: -ID TAG VM SIZE DATE VM CLOCK ICOUNT --- snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +-- snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- (qemu) loadvm snap0 (qemu) quit @@ -69,8 +69,8 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) savevm snap0 (qemu) info snapshots List of snapshots present on all disks: -ID TAG VM SIZE DATE VM CLOCK ICOUNT --- snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +-- snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- (qemu) loadvm snap0 (qemu) quit @@ -94,8 +94,8 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) savevm snap0 (qemu) info snapshots List of snapshots present on all disks: -ID TAG VM SIZE DATE VM CLOCK ICOUNT --- snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +-- snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- (qemu) loadvm snap0 (qemu) quit @@ -105,8 +105,8 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) savevm snap0 (qemu) info snapshots List of snapshots present on all disks: -ID TAG VM SIZE DATE VM CLOCK ICOUNT --- snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +-- snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- (qemu) loadvm snap0 (qemu) quit @@ -119,8 +119,8 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) savevm snap0 (qemu) info snapshots List of snapshots present on all disks: -ID TAG VM SIZE DATE VM CLOCK ICOUNT --- snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +-- snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- (qemu) loadvm snap0 (qemu) quit @@ -134,8 +134,8 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) savevm snap0 (qemu) info snapshots List of snapshots present on all disks: -ID TAG VM SIZE DATE VM CLOCK ICOUNT --- snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +-- snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- (qemu) loadvm snap0 (qemu) quit @@ -145,15 +145,15 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) savevm snap0 (qemu) info snapshots List of snapshots present on all disks: -ID TAG VM SIZE DATE VM CLOCK ICOUNT --- snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +-- snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- (qemu) loadvm snap0 (qemu) quit Internal snapshots on overlay: Snapshot list: -ID TAG VM SIZE DATE VM CLOCK ICOUNT -1 snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +1 snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- Internal snapshots on backing file: === -blockdev with NBD server on the backing file === @@ -166,17 +166,17 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) savevm snap0 (qemu) info snapshots List of snapshots present on all disks: -ID TAG VM SIZE DATE VM CLOCK ICOUNT --- snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +-- snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- (qemu) loadvm snap0 (qemu) quit Internal snapshots on overlay: Snapshot list: -ID TAG VM SIZE DATE VM CLOCK ICOUNT -1 snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +1 snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- Internal snapshots on backing file: Snapshot list: -ID TAG VM SIZE DATE VM CLOCK ICOUNT -1 snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +1 snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- *** done diff --git a/tests/qemu-iotests/271 b/tests/qemu-iotests/271 index c7c2cadda0..59a6fafa2f 100755 --- a/tests/qemu-iotests/271 +++ b/tests/qemu-iotests/271 @@ -899,6 +899,137 @@ _concurrent_io | $QEMU_IO | _filter_qemu_io | \ sed -e 's/\(20480\|40960\)/OFFSET/' _concurrent_verify | $QEMU_IO | _filter_qemu_io +############################################################ +############################################################ +############################################################ + +echo +echo "### Rebase of qcow2 images with subclusters ###" +echo + +l2_offset=$((0x400000)) + +# Check that rebase operation preserve holes between allocated subclusters +# within one cluster (i.e. does not allocate extra space). Check that the +# data is preserved as well. +# +# Base (new backing): -- -- -- ... -- -- -- +# Mid (old backing): -- 11 -- ... -- 22 -- +# Top: -- -- -- ... -- -- -- + +echo "### Preservation of unallocated holes after rebase ###" +echo + +echo "# create backing chain" +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img -o cluster_size=1M,extended_l2=on 1M +TEST_IMG="$TEST_IMG.mid" _make_test_img -o cluster_size=1M,extended_l2=on \ + -b "$TEST_IMG.base" -F qcow2 1M +TEST_IMG="$TEST_IMG.top" _make_test_img -o cluster_size=1M,extended_l2=on \ + -b "$TEST_IMG.mid" -F qcow2 1M + +echo +echo "# fill old backing with data (separate subclusters within cluster)" +echo + +$QEMU_IO -c "write -P 0x11 32k 32k" \ + -c "write -P 0x22 $(( 30 * 32 ))k 32k" \ + "$TEST_IMG.mid" | _filter_qemu_io + +echo +echo "# rebase topmost image onto the new backing" +echo + +$QEMU_IMG rebase -b "$TEST_IMG.base" -F qcow2 "$TEST_IMG.top" + +echo "# verify that data is read the same before and after rebase" +echo + +$QEMU_IO -c "read -P 0x00 0 32k" \ + -c "read -P 0x11 32k 32k" \ + -c "read -P 0x00 64k $(( 28 * 32 ))k" \ + -c "read -P 0x22 $(( 30 * 32 ))k 32k" \ + -c "read -P 0x00 $(( 31 * 32 ))k 32k" \ + "$TEST_IMG.top" | _filter_qemu_io + +echo +echo "# verify that only selected subclusters remain allocated" +echo + +$QEMU_IMG map "$TEST_IMG.top" | _filter_testdir + +echo +echo "# verify image bitmap" +echo + +TEST_IMG="$TEST_IMG.top" alloc="1 30" zero="" _verify_l2_bitmap 0 + +# Check that rebase with compression works correctly with images containing +# subclusters. When compression is enabled and we allocate a new +# subcluster within the target (overlay) image, we expect the entire cluster +# containing that subcluster to become compressed. +# +# Here we expect 1st and 3rd clusters of the top (overlay) image to become +# compressed after the rebase, while cluster 2 to remain unallocated and +# be read from the base (new backing) image. +# +# Base (new backing): |-- -- .. -- --|11 11 .. 11 11|-- -- .. -- --| +# Mid (old backing): |-- -- .. -- 22|-- -- .. -- --|33 -- .. -- --| +# Top: |-- -- .. -- --|-- -- -- -- --|-- -- .. -- --| + +echo +echo "### Rebase with compression for images with subclusters ###" +echo + +echo "# create backing chain" +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img -o cluster_size=1M,extended_l2=on 3M +TEST_IMG="$TEST_IMG.mid" _make_test_img -o cluster_size=1M,extended_l2=on \ + -b "$TEST_IMG.base" -F qcow2 3M +TEST_IMG="$TEST_IMG.top" _make_test_img -o cluster_size=1M,extended_l2=on \ + -b "$TEST_IMG.mid" -F qcow2 3M + +echo +echo "# fill old and new backing with data" +echo + +$QEMU_IO -c "write -P 0x11 1M 1M" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -P 0x22 $(( 31 * 32 ))k 32k" \ + -c "write -P 0x33 $(( 64 * 32 ))k 32k" \ + "$TEST_IMG.mid" | _filter_qemu_io + +echo +echo "# rebase topmost image onto the new backing, with compression" +echo + +$QEMU_IMG rebase -c -b "$TEST_IMG.base" -F qcow2 "$TEST_IMG.top" + +echo "# verify that the 1st and 3rd clusters've become compressed" +echo + +$QEMU_IMG map --output=json "$TEST_IMG.top" | _filter_testdir + +echo +echo "# verify that data is read the same before and after rebase" +echo + +$QEMU_IO -c "read -P 0x22 $(( 31 * 32 ))k 32k" \ + -c "read -P 0x11 1M 1M" \ + -c "read -P 0x33 $(( 64 * 32 ))k 32k" \ + "$TEST_IMG.top" | _filter_qemu_io + +echo +echo "# verify image bitmap" +echo + +# For compressed clusters bitmap is always 0. For unallocated cluster +# there should be no entry at all, thus bitmap is also 0. +TEST_IMG="$TEST_IMG.top" alloc="" zero="" _verify_l2_bitmap 0 +TEST_IMG="$TEST_IMG.top" alloc="" zero="" _verify_l2_bitmap 1 +TEST_IMG="$TEST_IMG.top" alloc="" zero="" _verify_l2_bitmap 2 + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/271.out b/tests/qemu-iotests/271.out index 5be780de76..0b24d50159 100644 --- a/tests/qemu-iotests/271.out +++ b/tests/qemu-iotests/271.out @@ -723,4 +723,86 @@ wrote 2048/2048 bytes at offset OFFSET 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 2048/2048 bytes at offset OFFSET 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +### Rebase of qcow2 images with subclusters ### + +### Preservation of unallocated holes after rebase ### + +# create backing chain + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT +Formatting 'TEST_DIR/t.IMGFMT.top', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.mid backing_fmt=IMGFMT + +# fill old backing with data (separate subclusters within cluster) + +wrote 32768/32768 bytes at offset 32768 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 32768/32768 bytes at offset 983040 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# rebase topmost image onto the new backing + +# verify that data is read the same before and after rebase + +read 32768/32768 bytes at offset 0 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 32768/32768 bytes at offset 32768 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 917504/917504 bytes at offset 65536 +896 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 32768/32768 bytes at offset 983040 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 32768/32768 bytes at offset 1015808 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# verify that only selected subclusters remain allocated + +Offset Length Mapped to File +0x8000 0x8000 0x508000 TEST_DIR/t.qcow2.top +0xf0000 0x8000 0x5f0000 TEST_DIR/t.qcow2.top + +# verify image bitmap + +L2 entry #0: 0x8000000000500000 0000000040000002 + +### Rebase with compression for images with subclusters ### + +# create backing chain + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=3145728 +Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=3145728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT +Formatting 'TEST_DIR/t.IMGFMT.top', fmt=IMGFMT size=3145728 backing_file=TEST_DIR/t.IMGFMT.mid backing_fmt=IMGFMT + +# fill old and new backing with data + +wrote 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 32768/32768 bytes at offset 1015808 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 32768/32768 bytes at offset 2097152 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# rebase topmost image onto the new backing, with compression + +# verify that the 1st and 3rd clusters've become compressed + +[{ "start": 0, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, +{ "start": 1048576, "length": 1048576, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": 5242880}, +{ "start": 2097152, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}] + +# verify that data is read the same before and after rebase + +read 32768/32768 bytes at offset 1015808 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 32768/32768 bytes at offset 2097152 +32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# verify image bitmap + +L2 entry #0: 0x4008000000500000 0000000000000000 +L2 entry #1: 0x0000000000000000 0000000000000000 +L2 entry #2: 0x400800000050040b 0000000000000000 *** done diff --git a/tests/qemu-iotests/273.out b/tests/qemu-iotests/273.out index 6a74a8138b..71843f02de 100644 --- a/tests/qemu-iotests/273.out +++ b/tests/qemu-iotests/273.out @@ -282,10 +282,6 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev ] } } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -297,5 +293,9 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev "reason": "host-qmp-quit" } } +{ + "return": { + } +} *** done diff --git a/tests/qemu-iotests/274.out b/tests/qemu-iotests/274.out index acd8b166a6..c2967335ca 100644 --- a/tests/qemu-iotests/274.out +++ b/tests/qemu-iotests/274.out @@ -20,18 +20,18 @@ read 1048576/1048576 bytes at offset 1048576 0/1048576 bytes allocated at offset 1 MiB === Checking map === -[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}] +[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}] Offset Length Mapped to File 0 0x200000 0x50000 TEST_DIR/PID-base -[{ "start": 0, "length": 1048576, "depth": 1, "present": true, "zero": false, "data": true, "offset": 327680}] +[{ "start": 0, "length": 1048576, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}] Offset Length Mapped to File 0 0x100000 0x50000 TEST_DIR/PID-base -[{ "start": 0, "length": 1048576, "depth": 2, "present": true, "zero": false, "data": true, "offset": 327680}, -{ "start": 1048576, "length": 1048576, "depth": 0, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 1048576, "depth": 2, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}, +{ "start": 1048576, "length": 1048576, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] Offset Length Mapped to File 0 0x100000 0x50000 TEST_DIR/PID-base @@ -186,8 +186,8 @@ read 65536/65536 bytes at offset 5368709120 1 GiB (0x40000000) bytes not allocated at offset 0 bytes (0x0) 7 GiB (0x1c0000000) bytes allocated at offset 1 GiB (0x40000000) -[{ "start": 0, "length": 1073741824, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 1073741824, "length": 7516192768, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 1073741824, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 1073741824, "length": 7516192768, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] === preallocation=metadata === wrote 65536/65536 bytes at offset 33285996544 @@ -201,13 +201,13 @@ read 65536/65536 bytes at offset 33285996544 30 GiB (0x780000000) bytes not allocated at offset 0 bytes (0x0) 3 GiB (0xc0000000) bytes allocated at offset 30 GiB (0x780000000) -[{ "start": 0, "length": 32212254720, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 32212254720, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 327680}, -{ "start": 32749125632, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 537264128}, -{ "start": 33285996544, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 1074200576}, -{ "start": 33822867456, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 1611137024}, -{ "start": 34359738368, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 2148139008}, -{ "start": 34896609280, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 2685075456}] +[{ "start": 0, "length": 32212254720, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 32212254720, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 327680}, +{ "start": 32749125632, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 537264128}, +{ "start": 33285996544, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 1074200576}, +{ "start": 33822867456, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 1611137024}, +{ "start": 34359738368, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 2148139008}, +{ "start": 34896609280, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 2685075456}] === preallocation=falloc === wrote 65536/65536 bytes at offset 9437184 @@ -221,8 +221,8 @@ read 65536/65536 bytes at offset 9437184 5 MiB (0x500000) bytes not allocated at offset 0 bytes (0x0) 10 MiB (0xa00000) bytes allocated at offset 5 MiB (0x500000) -[{ "start": 0, "length": 5242880, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 5242880, "length": 10485760, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}] +[{ "start": 0, "length": 5242880, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 5242880, "length": 10485760, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}] === preallocation=full === wrote 65536/65536 bytes at offset 11534336 @@ -236,8 +236,8 @@ read 65536/65536 bytes at offset 11534336 8 MiB (0x800000) bytes not allocated at offset 0 bytes (0x0) 4 MiB (0x400000) bytes allocated at offset 8 MiB (0x800000) -[{ "start": 0, "length": 8388608, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 8388608, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}] +[{ "start": 0, "length": 8388608, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 8388608, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}] === preallocation=off === wrote 65536/65536 bytes at offset 259072 @@ -251,9 +251,9 @@ read 65536/65536 bytes at offset 259072 192 KiB (0x30000) bytes not allocated at offset 0 bytes (0x0) 320 KiB (0x50000) bytes allocated at offset 192 KiB (0x30000) -[{ "start": 0, "length": 196608, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 196608, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}, -{ "start": 262144, "length": 262144, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 196608, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 196608, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}, +{ "start": 262144, "length": 262144, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] === preallocation=off === wrote 65536/65536 bytes at offset 344064 @@ -267,8 +267,8 @@ read 65536/65536 bytes at offset 344064 256 KiB (0x40000) bytes not allocated at offset 0 bytes (0x0) 256 KiB (0x40000) bytes allocated at offset 256 KiB (0x40000) -[{ "start": 0, "length": 262144, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 262144, "length": 262144, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 262144, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 262144, "length": 262144, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] === preallocation=off === wrote 65536/65536 bytes at offset 446464 @@ -282,6 +282,6 @@ read 65536/65536 bytes at offset 446464 256 KiB (0x40000) bytes not allocated at offset 0 bytes (0x0) 244 KiB (0x3d000) bytes allocated at offset 256 KiB (0x40000) -[{ "start": 0, "length": 262144, "depth": 1, "present": false, "zero": true, "data": false}, -{ "start": 262144, "length": 249856, "depth": 0, "present": true, "zero": true, "data": false}] +[{ "start": 0, "length": 262144, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 262144, "length": 249856, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] diff --git a/tests/qemu-iotests/277 b/tests/qemu-iotests/277 index 24833e7eb6..4224202ac2 100755 --- a/tests/qemu-iotests/277 +++ b/tests/qemu-iotests/277 @@ -27,7 +27,8 @@ from iotests import file_path, log iotests.script_initialize() -nbd_sock, conf_file = file_path('nbd-sock', 'nbd-fault-injector.conf') +conf_file = file_path('nbd-fault-injector.conf') +nbd_sock = file_path('nbd-sock', base_dir=iotests.sock_dir) def make_conf_file(event): diff --git a/tests/qemu-iotests/280.out b/tests/qemu-iotests/280.out index c75f437c00..546dbb4a68 100644 --- a/tests/qemu-iotests/280.out +++ b/tests/qemu-iotests/280.out @@ -12,7 +12,7 @@ Enabling migration QMP events on VM... VM is now stopped: completed {"execute": "query-status", "arguments": {}} -{"return": {"running": false, "singlestep": false, "status": "postmigrate"}} +{"return": {"running": 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}}} diff --git a/tests/qemu-iotests/281 b/tests/qemu-iotests/281 index 5e1339bd75..f6746a12e8 100755 --- a/tests/qemu-iotests/281 +++ b/tests/qemu-iotests/281 @@ -56,15 +56,13 @@ class TestDirtyBitmapIOThread(iotests.QMPTestCase): os.remove(self.images[name]) def test_add_dirty_bitmap(self): - result = self.vm.qmp( + self.vm.cmd( '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): @@ -105,23 +103,21 @@ class TestNBDMirrorIOThread(iotests.QMPTestCase): os.remove(self.images[name]) def test_nbd_mirror(self): - result = self.vm_tgt.qmp( + self.vm_tgt.cmd( 'nbd-server-start', addr={ 'type': 'unix', 'data': { 'path': self.nbd_sock } } ) - self.assert_qmp(result, 'return', {}) - result = self.vm_tgt.qmp( + self.vm_tgt.cmd( 'nbd-server-add', device='drive0', writable=True ) - self.assert_qmp(result, 'return', {}) - result = self.vm_src.qmp( + self.vm_src.cmd( 'drive-mirror', device='drive0', target='nbd+unix:///drive0?socket=' + self.nbd_sock, @@ -130,7 +126,6 @@ class TestNBDMirrorIOThread(iotests.QMPTestCase): speed=64*1024*1024, job_id='j1' ) - self.assert_qmp(result, 'return', {}) self.vm_src.event_wait(name="BLOCK_JOB_READY") @@ -290,8 +285,7 @@ class TestYieldingAndTimers(iotests.QMPTestCase): # they will remain active, fire later, and then access freed data. # (Or, with "block/nbd: Assert there are no timers when closed" # applied, the assertions added in that patch will fail.) - result = self.vm.qmp('blockdev-del', node_name='nbd') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', node_name='nbd') # Give the timers some time to fire (both have a timeout of 1 s). # (Sleeping in an iotest may ring some alarm bells, but note that if @@ -303,9 +297,8 @@ class TestYieldingAndTimers(iotests.QMPTestCase): def test_yield_in_iothread(self): # Move the NBD node to the I/O thread; the NBD block driver should # attach the connection's QIOChannel to that thread's AioContext, too - result = self.vm.qmp('x-blockdev-set-iothread', - node_name='nbd', iothread='iothr') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('x-blockdev-set-iothread', + node_name='nbd', iothread='iothr') # Do some I/O that will be throttled by the QSD, so that the network # connection hopefully will yield here. When it is resumed, it must diff --git a/tests/qemu-iotests/284 b/tests/qemu-iotests/284 index 5a82639e7f..722267486d 100755 --- a/tests/qemu-iotests/284 +++ b/tests/qemu-iotests/284 @@ -33,6 +33,8 @@ _cleanup() } trap "_cleanup; exit \$status" 0 1 2 3 15 +IMGOPTSSYNTAX=true + # get standard environment, filters and checks . ./common.rc . ./common.filter @@ -47,14 +49,12 @@ 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" + TEST_IMG="$TEST_IMG,encrypt.key-secret=sec0" QEMU_IMG_EXTRA_ARGS="--image-opts --object $SECRET" echo @@ -78,7 +78,6 @@ _run_test() TEST_IMG="$OLD_TEST_IMG" QEMU_IMG_EXTRA_ARGS= - IMGOPTSSYNTAX= } diff --git a/tests/qemu-iotests/286 b/tests/qemu-iotests/286 index 120a8375b7..38216c2a0e 100755 --- a/tests/qemu-iotests/286 +++ b/tests/qemu-iotests/286 @@ -69,7 +69,8 @@ $QEMU_IMG snapshot -l "$TEST_IMG" | tail -n 1 | tr -s ' ' \ -e 's/\./(VM state size unit)/' \ -e 's/\./(snapshot date)/' \ -e 's/\./(snapshot time)/' \ - -e 's/\./(VM clock)/' + -e 's/\./(VM clock)/' \ + -e 's/\./(icount)/' # success, all done echo "*** done" diff --git a/tests/qemu-iotests/286.out b/tests/qemu-iotests/286.out index 39ff07e12c..bb04748e08 100644 --- a/tests/qemu-iotests/286.out +++ b/tests/qemu-iotests/286.out @@ -4,5 +4,5 @@ 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) +(snapshot ID) (snapshot name) (VM state size value) (VM state size unit) (snapshot date) (snapshot time) (VM clock) (icount) *** done diff --git a/tests/qemu-iotests/295 b/tests/qemu-iotests/295 index 270ad3999f..04818af264 100755 --- a/tests/qemu-iotests/295 +++ b/tests/qemu-iotests/295 @@ -57,8 +57,7 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): # create the secrets and load 'em into the VM self.secrets = [ Secret(i) for i in range(0, 6) ] for secret in self.secrets: - result = self.vm.qmp("object-add", **secret.to_qmp_object()) - self.assert_qmp(result, 'return', {}) + self.vm.cmd("object-add", **secret.to_qmp_object()) if iotests.imgfmt == "qcow2": self.pfx = "encrypt." @@ -102,8 +101,7 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): } } - result = self.vm.qmp('blockdev-add', ** - { + self.vm.cmd('blockdev-add', { 'driver': iotests.imgfmt, 'node-name': id, 'read-only': read_only, @@ -116,12 +114,10 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): } } ) - self.assert_qmp(result, 'return', {}) # close the encrypted block device def closeImageQmp(self, id): - result = self.vm.qmp('blockdev-del', **{ 'node-name': id }) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-del', {'node-name': id}) ########################################################################### # add a key to an encrypted block device @@ -160,8 +156,7 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): args['force'] = True #TODO: check what jobs return - result = self.vm.qmp('x-blockdev-amend', **args) - assert result['return'] == {} + self.vm.cmd('x-blockdev-amend', **args) self.vm.run_job('job_add_key') # erase a key from an encrypted block device @@ -194,8 +189,7 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): if force == True: args['force'] = True - result = self.vm.qmp('x-blockdev-amend', **args) - assert result['return'] == {} + self.vm.cmd('x-blockdev-amend', **args) self.vm.run_job('job_erase_key') ########################################################################### diff --git a/tests/qemu-iotests/296 b/tests/qemu-iotests/296 index 0d21b740a7..2b63cefff0 100755 --- a/tests/qemu-iotests/296 +++ b/tests/qemu-iotests/296 @@ -42,7 +42,7 @@ class Secret: return [ "secret,id=" + self._id + ",data=" + self._secret] def to_qmp_object(self): - return { "qom_type" : "secret", "id": self.id(), + return { "qom-type" : "secret", "id": self.id(), "data": self.secret() } ################################################################################ @@ -61,10 +61,8 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): # create the secrets and load 'em into the VMs self.secrets = [ Secret(i) for i in range(0, 4) ] for secret in self.secrets: - result = self.vm1.qmp("object-add", **secret.to_qmp_object()) - self.assert_qmp(result, 'return', {}) - result = self.vm2.qmp("object-add", **secret.to_qmp_object()) - self.assert_qmp(result, 'return', {}) + self.vm1.cmd("object-add", secret.to_qmp_object()) + self.vm2.cmd("object-add", secret.to_qmp_object()) # test case shutdown def tearDown(self): @@ -132,17 +130,15 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): } if reOpen: - result = vm.qmp(command, options=[opts]) + vm.cmd(command, options=[opts]) else: - result = vm.qmp(command, **opts) - self.assert_qmp(result, 'return', {}) + vm.cmd(command, opts) ########################################################################### # add virtio-blk consumer for a block device def addImageUser(self, vm, id, disk_id, share_rw=False): - result = vm.qmp('device_add', ** - { + result = vm.qmp('device_add', { 'driver': 'virtio-blk', 'id': id, 'drive': disk_id, @@ -154,8 +150,7 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): # close the encrypted block device def closeImageQmp(self, vm, id): - result = vm.qmp('blockdev-del', **{ 'node-name': id }) - self.assert_qmp(result, 'return', {}) + vm.cmd('blockdev-del', {'node-name': id}) ########################################################################### @@ -173,7 +168,7 @@ class EncryptionSetupTestCase(iotests.QMPTestCase): }, } - result = vm.qmp('x-blockdev-amend', **args) + result = vm.qmp('x-blockdev-amend', args) iotests.log(result) # Run the job only if it was created event = ('JOB_STATUS_CHANGE', diff --git a/tests/qemu-iotests/298 b/tests/qemu-iotests/298 index ad560e2941..09c9290711 100755 --- a/tests/qemu-iotests/298 +++ b/tests/qemu-iotests/298 @@ -80,25 +80,23 @@ class TestPreallocateFilter(TestPreallocateBase): def test_external_snapshot(self): self.test_prealloc() - result = self.vm.qmp('blockdev-snapshot-sync', node_name='disk', - snapshot_file=overlay, - snapshot_node_name='overlay') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-snapshot-sync', node_name='disk', + snapshot_file=overlay, + snapshot_node_name='overlay') # on reopen to r-o base preallocation should be dropped self.check_small() self.vm.hmp_qemu_io('drive0', 'write 1M 1M') - result = self.vm.qmp('block-commit', device='overlay') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-commit', device='overlay') self.complete_and_wait() # commit of new megabyte should trigger preallocation self.check_big() def test_reopen_opts(self): - result = self.vm.qmp('blockdev-reopen', options=[{ + self.vm.cmd('blockdev-reopen', options=[{ 'node-name': 'disk', 'driver': iotests.imgfmt, 'file': { @@ -113,7 +111,6 @@ class TestPreallocateFilter(TestPreallocateBase): } } }]) - self.assert_qmp(result, 'return', {}) self.vm.hmp_qemu_io('drive0', 'write 0 1M') self.assertTrue(os.path.getsize(disk) == 25 * MiB) @@ -140,8 +137,8 @@ class TestTruncate(iotests.QMPTestCase): stat = os.stat(disk) refstat = os.stat(refdisk) - # Probably we'll want preallocate filter to keep align to cluster when - # shrink preallocation, so, ignore small differece + # The preallocate filter may keep cluster alignment when shrinking, + # so ignore small differences self.assertLess(abs(stat.st_size - refstat.st_size), 64 * 1024) # Preallocate filter may leak some internal clusters (for example, if diff --git a/tests/qemu-iotests/300 b/tests/qemu-iotests/300 index dbd28384ec..e46616d7b1 100755 --- a/tests/qemu-iotests/300 +++ b/tests/qemu-iotests/300 @@ -50,10 +50,9 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): self.vm_b.add_incoming(f'unix:{mig_sock}') self.vm_b.launch() - result = self.vm_a.qmp('block-dirty-bitmap-add', - node=self.src_node_name, - name=self.src_bmap_name) - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('block-dirty-bitmap-add', + node=self.src_node_name, + name=self.src_bmap_name) # Dirty some random megabytes for _ in range(9): @@ -69,8 +68,7 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): for name in ('dirty-bitmaps', 'events')] for vm in (self.vm_a, self.vm_b): - result = vm.qmp('migrate-set-capabilities', capabilities=caps) - self.assert_qmp(result, 'return', {}) + vm.cmd('migrate-set-capabilities', capabilities=caps) def tearDown(self) -> None: self.vm_a.shutdown() @@ -93,8 +91,7 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): def migrate(self, bitmap_name_valid: bool = True, migration_success: bool = True) -> None: - result = self.vm_a.qmp('migrate', uri=f'unix:{mig_sock}') - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('migrate', uri=f'unix:{mig_sock}') with iotests.Timeout(5, 'Timeout waiting for migration to complete'): self.assertEqual(self.vm_a.wait_migration('postmigrate'), @@ -442,10 +439,9 @@ class TestBlockBitmapMappingErrors(TestDirtyBitmapMigration): def test_bitmap_name_too_long(self) -> None: name = 'a' * 256 - result = self.vm_a.qmp('block-dirty-bitmap-add', - node=self.src_node_name, - name=name) - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('block-dirty-bitmap-add', + node=self.src_node_name, + name=name) self.migrate(False, False) @@ -517,22 +513,19 @@ class TestCrossAliasMigration(TestDirtyBitmapMigration): TestDirtyBitmapMigration.setUp(self) # Now create another block device and let both have two bitmaps each - result = self.vm_a.qmp('blockdev-add', - node_name='node-b', driver='null-co') - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('blockdev-add', + node_name='node-b', driver='null-co') - result = self.vm_b.qmp('blockdev-add', - node_name='node-a', driver='null-co') - self.assert_qmp(result, 'return', {}) + self.vm_b.cmd('blockdev-add', + node_name='node-a', driver='null-co') bmaps_to_add = (('node-a', 'bmap-b'), ('node-b', 'bmap-a'), ('node-b', 'bmap-b')) for (node, bmap) in bmaps_to_add: - result = self.vm_a.qmp('block-dirty-bitmap-add', - node=node, name=bmap) - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('block-dirty-bitmap-add', + node=node, name=bmap) @staticmethod def cross_mapping() -> BlockBitmapMapping: @@ -611,24 +604,21 @@ class TestAliasTransformMigration(TestDirtyBitmapMigration): TestDirtyBitmapMigration.setUp(self) # Now create another block device and let both have two bitmaps each - result = self.vm_a.qmp('blockdev-add', - node_name='node-b', driver='null-co', - read_zeroes=False) - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('blockdev-add', + node_name='node-b', driver='null-co', + read_zeroes=False) - result = self.vm_b.qmp('blockdev-add', - node_name='node-a', driver='null-co', - read_zeroes=False) - self.assert_qmp(result, 'return', {}) + self.vm_b.cmd('blockdev-add', + node_name='node-a', driver='null-co', + read_zeroes=False) bmaps_to_add = (('node-a', 'bmap-b'), ('node-b', 'bmap-a'), ('node-b', 'bmap-b')) for (node, bmap) in bmaps_to_add: - result = self.vm_a.qmp('block-dirty-bitmap-add', - node=node, name=bmap) - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('block-dirty-bitmap-add', + node=node, name=bmap) @staticmethod def transform_mapping() -> BlockBitmapMapping: diff --git a/tests/qemu-iotests/302.out b/tests/qemu-iotests/302.out index 3e7c281b91..7b5014cdd8 100644 --- a/tests/qemu-iotests/302.out +++ b/tests/qemu-iotests/302.out @@ -4,6 +4,11 @@ image: nbd+unix:///exp?socket=SOCK_DIR/PID-nbd-sock file format: raw virtual size: 448 KiB (458752 bytes) disk size: unavailable +Child node '/file': + filename: nbd+unix:///exp?socket=SOCK_DIR/PID-nbd-sock + protocol type: nbd + file length: 448 KiB (458752 bytes) + disk size: unavailable === Converted image info === image: TEST_IMG diff --git a/tests/qemu-iotests/307.out b/tests/qemu-iotests/307.out index ec8d2be0e0..f645f3315f 100644 --- a/tests/qemu-iotests/307.out +++ b/tests/qemu-iotests/307.out @@ -15,10 +15,11 @@ wrote 4096/4096 bytes at offset 0 exports available: 1 export: 'fmt' size: 67108864 - flags: 0x58f ( readonly flush fua df multi cache ) + flags: 0x158f ( readonly flush fua df multi cache block-status-payload ) min block: XXX opt block: XXX max block: XXX + transaction size: 64-bit available meta contexts: 1 base:allocation @@ -43,10 +44,11 @@ exports available: 1 exports available: 1 export: 'fmt' size: 67108864 - flags: 0x58f ( readonly flush fua df multi cache ) + flags: 0x158f ( readonly flush fua df multi cache block-status-payload ) min block: XXX opt block: XXX max block: XXX + transaction size: 64-bit available meta contexts: 1 base:allocation @@ -74,19 +76,21 @@ exports available: 1 exports available: 2 export: 'fmt' size: 67108864 - flags: 0x58f ( readonly flush fua df multi cache ) + flags: 0x158f ( readonly flush fua df multi cache block-status-payload ) min block: XXX opt block: XXX max block: XXX + transaction size: 64-bit available meta contexts: 1 base:allocation export: 'export1' description: This is the writable second export size: 67108864 - flags: 0xced ( flush fua trim zeroes df cache fast-zero ) + flags: 0x1ded ( flush fua trim zeroes df multi cache fast-zero block-status-payload ) min block: XXX opt block: XXX max block: XXX + transaction size: 64-bit available meta contexts: 1 base:allocation @@ -109,10 +113,11 @@ exports available: 1 export: 'export1' description: This is the writable second export size: 67108864 - flags: 0xced ( flush fua trim zeroes df cache fast-zero ) + flags: 0x1ded ( flush fua trim zeroes df multi cache fast-zero block-status-payload ) min block: XXX opt block: XXX max block: XXX + transaction size: 64-bit available meta contexts: 1 base:allocation diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 index bde4aac2fa..ea81dc496a 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -77,6 +77,7 @@ fuse_export_add() # $1: Export ID fuse_export_del() { + capture_events="BLOCK_EXPORT_DELETED" \ _send_qemu_cmd $QEMU_HANDLE \ "{'execute': 'block-export-del', 'arguments': { @@ -84,8 +85,7 @@ fuse_export_del() } }" \ 'return' - _send_qemu_cmd $QEMU_HANDLE \ - '' \ + _wait_event $QEMU_HANDLE \ 'BLOCK_EXPORT_DELETED' } @@ -217,12 +217,12 @@ echo echo '=== Remove export ===' # Double-check that $EXT_MP appears as a non-empty file (the raw image) -$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size' +$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size' | head -n 1 fuse_export_del 'export-mp' # See that the file appears empty again -$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size' +$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size' | head -n 1 echo echo '=== Writable export ===' @@ -370,6 +370,49 @@ echo echo '=== Compare copy with original ===' $QEMU_IMG compare -f raw -F $IMGFMT "$COPIED_IMG" "$TEST_IMG" +_cleanup_test_img + +echo +echo '=== Writing zeroes while unmapping ===' +# Regression test for https://gitlab.com/qemu-project/qemu/-/issues/1507 +_make_test_img 64M +$QEMU_IO -c 'write -s /dev/urandom 0 64M' "$TEST_IMG" | _filter_qemu_io + +_launch_qemu +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'qmp_capabilities'}" \ + 'return' + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'blockdev-add', + 'arguments': { + 'driver': '$IMGFMT', + 'node-name': 'node-format', + 'file': { + 'driver': 'file', + 'filename': '$TEST_IMG' + } + } }" \ + 'return' + +fuse_export_add 'export' "'mountpoint': '$EXT_MP', 'writable': true" + +# Try writing zeroes by unmapping +$QEMU_IO -f raw -c 'write -zu 0 64M' "$EXT_MP" | _filter_qemu_io + +# Check the result +$QEMU_IO -f raw -c 'read -P 0 0 64M' "$EXT_MP" | _filter_qemu_io + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'quit'}" \ + 'return' + +wait=yes _cleanup_qemu + +# Check the original image +$QEMU_IO -c 'read -P 0 0 64M' "$TEST_IMG" | _filter_qemu_io + +_cleanup_test_img # success, all done echo "*** done" diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out index e4467a10cf..e5e233691d 100644 --- a/tests/qemu-iotests/308.out +++ b/tests/qemu-iotests/308.out @@ -165,10 +165,45 @@ OK: Post-truncate image size is as expected === Tear down === {'execute': 'quit'} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export-mp"}} +{"return": {}} === Compare copy with original === Images are identical. + +=== Writing zeroes while unmapping === +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 67108864/67108864 bytes at offset 0 +64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{'execute': 'qmp_capabilities'} +{"return": {}} +{'execute': 'blockdev-add', + 'arguments': { + 'driver': 'IMGFMT', + 'node-name': 'node-format', + 'file': { + 'driver': 'file', + 'filename': 'TEST_DIR/t.IMGFMT' + } + } } +{"return": {}} +{'execute': 'block-export-add', + 'arguments': { + 'type': 'fuse', + 'id': 'export', + 'node-name': 'node-format', + 'mountpoint': 'TEST_DIR/t.IMGFMT.fuse', 'writable': true + } } +{"return": {}} +wrote 67108864/67108864 bytes at offset 0 +64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 67108864/67108864 bytes at offset 0 +64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{'execute': 'quit'} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export"}} +{"return": {}} +read 67108864/67108864 bytes at offset 0 +64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done diff --git a/tests/qemu-iotests/312 b/tests/qemu-iotests/312 index 4139745f0e..0d9ea09a31 100755 --- a/tests/qemu-iotests/312 +++ b/tests/qemu-iotests/312 @@ -52,6 +52,7 @@ _supported_fmt qcow2 _supported_proto file _supported_os Linux _unsupported_imgopts cluster_size data_file +_require_drivers quorum echo echo '### Create all images' # three source (quorum), one destination diff --git a/tests/qemu-iotests/314 b/tests/qemu-iotests/314 new file mode 100755 index 0000000000..96d7b4d258 --- /dev/null +++ b/tests/qemu-iotests/314 @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +# group: rw backing auto quick +# +# Test qemu-img rebase with compression +# +# Copyright (c) 2023 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 . +# + +# creator +owner=andrey.drobyshev@virtuozzo.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + _rm_test_img "$TEST_IMG.base" + _rm_test_img "$TEST_IMG.itmd" +} +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 + +# Want the size divisible by 2 and 3 +size=$(( 48 * 1024 * 1024 )) +half_size=$(( size / 2 )) +third_size=$(( size / 3 )) + +# 1. "qemu-img rebase -c" should refuse working with any format which doesn't +# support compression. We only check "-f raw" here. +echo +echo "=== Testing compressed rebase format compatibility ===" +echo + +$QEMU_IMG create -f raw "$TEST_IMG" "$size" | _filter_img_create +$QEMU_IMG rebase -c -f raw -b "" "$TEST_IMG" + +# 2. Write the 1st half of $size to backing file (compressed), 2nd half -- to +# the top image (also compressed). Rebase the top image onto no backing file, +# with compression (i.e. "qemu-img -c -b ''"). Check that the resulting image +# has the written data preserved, and "qemu-img check" reports 100% clusters +# as compressed. +echo +echo "=== Testing rebase with compression onto no backing file ===" +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img $size +_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $size + +$QEMU_IO -c "write -c -P 0xaa 0 $half_size" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -c -P 0xbb $half_size $half_size" "$TEST_IMG" \ + | _filter_qemu_io + +$QEMU_IMG rebase -c -f $IMGFMT -b "" "$TEST_IMG" + +$QEMU_IO -c "read -P 0xaa 0 $half_size" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xbb $half_size $half_size" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG check "$TEST_IMG" | _filter_testdir + +# 3. Same as the previous one, but with raw backing file (hence write to +# the backing is uncompressed). +echo +echo "=== Testing rebase with compression with raw backing file ===" +echo + +$QEMU_IMG create -f raw "$TEST_IMG.base" "$half_size" | _filter_img_create +_make_test_img -b "$TEST_IMG.base" -F raw $size + +$QEMU_IO -f raw -c "write -P 0xaa 0 $half_size" "$TEST_IMG.base" \ + | _filter_qemu_io +$QEMU_IO -c "write -c -P 0xbb $half_size $half_size" \ + "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG rebase -c -f $IMGFMT -b "" "$TEST_IMG" + +$QEMU_IO -c "read -P 0xaa 0 $half_size" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xbb $half_size $half_size" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG check "$TEST_IMG" | _filter_testdir + +# 4. Create a backing chain base<--itmd<--img, filling 1st, 2nd and 3rd +# thirds of them, respectively (with compression). Rebase img onto base, +# effectively deleting itmd from the chain, and check that written data is +# preserved in the resulting image. Also check that "qemu-img check" reports +# 100% clusters as compressed. +echo +echo "=== Testing compressed rebase removing single delta from the chain ===" +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img $size +TEST_IMG="$TEST_IMG.itmd" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT $size +_make_test_img -b "$TEST_IMG.itmd" -F $IMGFMT $size + +$QEMU_IO -c "write -c -P 0xaa 0 $third_size" \ + "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -c -P 0xbb $third_size $third_size" \ + "$TEST_IMG.itmd" | _filter_qemu_io +$QEMU_IO -c "write -c -P 0xcc $((third_size * 2 )) $third_size" \ + "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG rebase -c -f $IMGFMT -b "$TEST_IMG.base" -F $IMGFMT "$TEST_IMG" + +$QEMU_IO -c "read -P 0xaa 0 $third_size" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xbb $third_size $third_size" \ + "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 0xcc $(( third_size * 2 )) $third_size" \ + "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG check "$TEST_IMG" | _filter_testdir + +# 5. Create one-cluster backing and overlay images, and fill only the first +# (half - 1) bytes of the backing with data (uncompressed). Rebase the +# overlay onto no backing file with compression. Check that data is still +# read correctly, and that cluster is now really compressed ("qemu-img check" +# reports 100% clusters as compressed. +echo +echo "=== Testing compressed rebase with unaligned unmerged data ===" +echo + +CLUSTER_SIZE=65536 + +TEST_IMG="$TEST_IMG.base" _make_test_img $CLUSTER_SIZE +_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $CLUSTER_SIZE + +$QEMU_IO -c "write -P 0xaa 0 $(( CLUSTER_SIZE / 2 - 1 ))" $TEST_IMG.base \ + | _filter_qemu_io + +$QEMU_IMG rebase -c -f $IMGFMT -b "" "$TEST_IMG" + +$QEMU_IO -c "read -P 0xaa 0 $(( CLUSTER_SIZE / 2 - 1 ))" "$TEST_IMG" \ + | _filter_qemu_io +$QEMU_IO -c \ + "read -P 0x00 $(( CLUSTER_SIZE / 2 - 1 )) $(( CLUSTER_SIZE / 2 + 1 ))" \ + "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG check "$TEST_IMG" | _filter_testdir + +# success, all done +echo +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/314.out b/tests/qemu-iotests/314.out new file mode 100644 index 0000000000..ac9337a543 --- /dev/null +++ b/tests/qemu-iotests/314.out @@ -0,0 +1,75 @@ +QA output created by 314 + +=== Testing compressed rebase format compatibility === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=raw size=50331648 +qemu-img: Compression not supported for this file format + +=== Testing rebase with compression onto no backing file === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=50331648 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=50331648 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT +wrote 25165824/25165824 bytes at offset 0 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 25165824/25165824 bytes at offset 25165824 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 25165824/25165824 bytes at offset 0 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 25165824/25165824 bytes at offset 25165824 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +768/768 = 100.00% allocated, 100.00% fragmented, 100.00% compressed clusters +Image end offset: 458752 + +=== Testing rebase with compression with raw backing file === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=raw size=25165824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=50331648 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw +wrote 25165824/25165824 bytes at offset 0 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 25165824/25165824 bytes at offset 25165824 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 25165824/25165824 bytes at offset 0 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 25165824/25165824 bytes at offset 25165824 +24 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +768/768 = 100.00% allocated, 100.00% fragmented, 100.00% compressed clusters +Image end offset: 458752 + +=== Testing compressed rebase removing single delta from the chain === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=50331648 +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=50331648 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=50331648 backing_file=TEST_DIR/t.IMGFMT.itmd backing_fmt=IMGFMT +wrote 16777216/16777216 bytes at offset 0 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 16777216/16777216 bytes at offset 16777216 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 16777216/16777216 bytes at offset 33554432 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 16777216/16777216 bytes at offset 0 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 16777216/16777216 bytes at offset 16777216 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 16777216/16777216 bytes at offset 33554432 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +512/768 = 66.67% allocated, 100.00% fragmented, 100.00% compressed clusters +Image end offset: 458752 + +=== Testing compressed rebase with unaligned unmerged data === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT +wrote 32767/32767 bytes at offset 0 +31.999 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 32767/32767 bytes at offset 0 +31.999 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 32769/32769 bytes at offset 32767 +32.001 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +1/1 = 100.00% allocated, 100.00% fragmented, 100.00% compressed clusters +Image end offset: 393216 + +*** done diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index 75de1b4691..56d88ca423 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -26,9 +26,23 @@ from findtests import TestFinder from testenv import TestEnv from testrunner import TestRunner +def get_default_path(follow_link=False): + """ + Try to automagically figure out the path we are running from. + """ + # called from the build tree? + if os.path.islink(sys.argv[0]): + if follow_link: + return os.path.dirname(os.readlink(sys.argv[0])) + else: + return os.path.dirname(os.path.abspath(sys.argv[0])) + else: # or source tree? + return os.getcwd() def make_argparser() -> argparse.ArgumentParser: - p = argparse.ArgumentParser(description="Test run options") + p = argparse.ArgumentParser( + description="Test run options", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) p.add_argument('-n', '--dry-run', action='store_true', help='show me, do not run tests') @@ -113,6 +127,11 @@ def make_argparser() -> argparse.ArgumentParser: 'middle of the process.') g_sel.add_argument('tests', metavar='TEST_FILES', nargs='*', help='tests to run, or "--" followed by a command') + g_sel.add_argument('--build-dir', default=get_default_path(), + help='Path to iotests build directory') + g_sel.add_argument('--source-dir', + default=get_default_path(follow_link=True), + help='Path to iotests build directory') return p @@ -120,11 +139,14 @@ def make_argparser() -> argparse.ArgumentParser: if __name__ == '__main__': args = make_argparser().parse_args() - env = TestEnv(imgfmt=args.imgfmt, imgproto=args.imgproto, + env = TestEnv(source_dir=args.source_dir, + build_dir=args.build_dir, + imgfmt=args.imgfmt, imgproto=args.imgproto, aiomode=args.aiomode, cachemode=args.cachemode, imgopts=args.imgopts, misalign=args.misalign, debug=args.debug, valgrind=args.valgrind, - gdb=args.gdb, qprint=args.print) + gdb=args.gdb, qprint=args.print, + dry_run=args.dry_run) if len(sys.argv) > 1 and sys.argv[-len(args.tests)-1] == '--': if not args.tests: @@ -159,10 +181,11 @@ if __name__ == '__main__': if not tests: raise ValueError('No tests selected') except ValueError as e: - sys.exit(e) + sys.exit(str(e)) if args.dry_run: - print('\n'.join(tests)) + with env: + print('\n'.join([os.path.basename(t) for t in tests])) else: with TestRunner(env, tap=args.tap, color=args.color) as tr: diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index cc9f1a5891..2846c83808 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Copyright (C) 2009 Red Hat, Inc. +# Copyright Red Hat # Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved. # # This program is free software; you can redistribute it and/or @@ -131,7 +131,6 @@ _filter_img_create_filenames() -e "s#$SOCK_DIR#SOCK_DIR#g" \ -e 's#SOCK_DIR/fuse-#TEST_DIR/#g' \ -e "s#$IMGFMT#IMGFMT#g" \ - -e 's#nbd:127.0.0.1:[0-9]\\+#TEST_DIR/t.IMGFMT#g' \ -e 's#nbd+unix:///\??socket=SOCK_DIR/nbd#TEST_DIR/t.IMGFMT#g' } @@ -223,12 +222,12 @@ _filter_img_info() discard=0 regex_json_spec_start='^ *"format-specific": \{' + regex_json_child_start='^ *"children": \[' gsed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$SOCK_DIR#SOCK_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ - -e 's#nbd://127.0.0.1:[0-9]\\+$#TEST_DIR/t.IMGFMT#g' \ -e 's#nbd+unix:///\??socket=SOCK_DIR/nbd#TEST_DIR/t.IMGFMT#g' \ -e 's#SOCK_DIR/fuse-#TEST_DIR/#g' \ -e "/encrypted: yes/d" \ @@ -251,20 +250,25 @@ _filter_img_info() -e 's/\(compression type: \)\(zlib\|zstd\)/\1COMPRESSION_TYPE/' \ -e "s/uuid: [-a-f0-9]\\+/uuid: 00000000-0000-0000-0000-000000000000/" | \ while IFS='' read -r line; do - if [[ $format_specific == 1 ]]; then - discard=0 - elif [[ $line == "Format specific information:" ]]; then - discard=1 - elif [[ $line =~ $regex_json_spec_start ]]; then - discard=2 - regex_json_spec_end="^${line%%[^ ]*}\\},? *$" + if [[ $discard == 0 ]]; then + if [[ $format_specific == 0 && $line == "Format specific information:" ]]; then + discard=1 + elif [[ $line =~ "Child node '/" ]]; then + discard=1 + elif [[ $line =~ $regex_json_spec_start ]]; then + discard=2 + regex_json_end="^${line%%[^ ]*}\\},? *$" + elif [[ $line =~ $regex_json_child_start ]]; then + discard=2 + regex_json_end="^${line%%[^ ]*}\\],? *$" + fi fi if [[ $discard == 0 ]]; then echo "$line" elif [[ $discard == 1 && ! $line ]]; then echo discard=0 - elif [[ $discard == 2 && $line =~ $regex_json_spec_end ]]; then + elif [[ $discard == 2 && $line =~ $regex_json_end ]]; then discard=0 fi done @@ -355,5 +359,12 @@ _filter_qcow2_compression_type_bit() -e 's/\(incompatible_features.*\), 3\(,.*\)/\1\2/' } +# filter warnings caused for block migration deprecation +_filter_migration_block_deprecated() +{ + gsed -e '/warning: parameter .blk. is deprecated; use blockdev-mirror with NBD instead/d' \ + -e '/warning: block migration is deprecated; use blockdev-mirror with NBD instead/d' +} + # make sure this script returns success true diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 165b54a61e..95c12577dd 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Copyright (C) 2009 Red Hat, Inc. +# Copyright Red Hat # Copyright (c) 2000-2006 Silicon Graphics, Inc. All Rights Reserved. # # This program is free software; you can redistribute it and/or modify @@ -711,30 +711,37 @@ _img_info() discard=0 regex_json_spec_start='^ *"format-specific": \{' + regex_json_child_start='^ *"children": \[' $QEMU_IMG info $QEMU_IMG_EXTRA_ARGS "$@" "$TEST_IMG" 2>&1 | \ sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$SOCK_DIR/fuse-#TEST_DIR/#g" \ + -e "s#$SOCK_DIR/#SOCK_DIR/#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e 's/\(compression type: \)\(zlib\|zstd\)/\1COMPRESSION_TYPE/' \ -e "/^disk size:/ D" \ -e "/actual-size/ D" | \ while IFS='' read -r line; do - if [[ $format_specific == 1 ]]; then - discard=0 - elif [[ $line == "Format specific information:" ]]; then - discard=1 - elif [[ $line =~ $regex_json_spec_start ]]; then - discard=2 - regex_json_spec_end="^${line%%[^ ]*}\\},? *$" + if [[ $discard == 0 ]]; then + if [[ $format_specific == 0 && $line == "Format specific information:" ]]; then + discard=1 + elif [[ $line =~ "Child node '/" ]]; then + discard=1 + elif [[ $format_specific == 0 && $line =~ $regex_json_spec_start ]]; then + discard=2 + regex_json_end="^${line%%[^ ]*}\\},? *$" + elif [[ $line =~ $regex_json_child_start ]]; then + discard=2 + regex_json_end="^${line%%[^ ]*}\\],? *$" + fi fi if [[ $discard == 0 ]]; then echo "$line" elif [[ $discard == 1 && ! $line ]]; then echo discard=0 - elif [[ $discard == 2 && $line =~ $regex_json_spec_end ]]; then + elif [[ $discard == 2 && $line =~ $regex_json_end ]]; then discard=0 fi done @@ -972,17 +979,22 @@ _require_drivers() # _require_large_file() { - if ! truncate --size="$1" "$TEST_IMG"; then + if [ -z "$TEST_IMG_FILE" ]; then + FILENAME="$TEST_IMG" + else + FILENAME="$TEST_IMG_FILE" + fi + if ! truncate --size="$1" "$FILENAME"; then _notrun "file system on $TEST_DIR does not support large enough files" fi - rm "$TEST_IMG" + rm "$FILENAME" } # Check that a set of devices is available in the QEMU binary # _require_devices() { - available=$($QEMU -M none -device help | \ + available=$($QEMU -M none -device help 2> /dev/null | \ grep ^name | sed -e 's/^name "//' -e 's/".*$//') for device do @@ -994,7 +1006,7 @@ _require_devices() _require_one_device_of() { - available=$($QEMU -M none -device help | \ + available=$($QEMU -M none -device help 2> /dev/null | \ grep ^name | sed -e 's/^name "//' -e 's/".*$//') for device do diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index da7d6637e1..ea48af4a7b 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -38,7 +38,7 @@ import unittest from contextlib import contextmanager from qemu.machine import qtest -from qemu.qmp.legacy import QMPMessage, QEMUMonitorProtocol +from qemu.qmp.legacy import QMPMessage, QMPReturnValue, QEMUMonitorProtocol from qemu.utils import VerboseProcessError # Use this logger for logging messages directly from the iotests module @@ -329,7 +329,7 @@ def qemu_img_log(*args: str, check: bool = True def img_info_log(filename: str, filter_path: Optional[str] = None, use_image_opts: bool = False, extra_args: Sequence[str] = (), - check: bool = True, + check: bool = True, drop_child_info: bool = True, ) -> None: args = ['info'] if use_image_opts: @@ -342,7 +342,7 @@ def img_info_log(filename: str, filter_path: Optional[str] = None, output = qemu_img(*args, check=check).stdout if not filter_path: filter_path = filename - log(filter_img_info(output, filter_path)) + log(filter_img_info(output, filter_path, drop_child_info)) def qemu_io_wrap_args(args: Sequence[str]) -> List[str]: if '-f' in args or '--image-opts' in args: @@ -460,7 +460,16 @@ class QemuStorageDaemon: def qmp(self, cmd: str, args: Optional[Dict[str, object]] = None) \ -> QMPMessage: assert self._qmp is not None - return self._qmp.cmd(cmd, args) + return self._qmp.cmd_raw(cmd, args) + + def get_qmp(self) -> QEMUMonitorProtocol: + assert self._qmp is not None + return self._qmp + + def cmd(self, cmd: str, args: Optional[Dict[str, object]] = None) \ + -> QMPReturnValue: + assert self._qmp is not None + return self._qmp.cmd(cmd, **(args or {})) def stop(self, kill_signal=15): self._p.send_signal(kill_signal) @@ -642,11 +651,30 @@ def filter_qmp_virtio_scsi(qmsg): def filter_generated_node_ids(msg): return re.sub("#block[0-9]+", "NODE_NAME", msg) -def filter_img_info(output, filename): +def filter_qmp_generated_node_ids(qmsg): + def _filter(_key, value): + if is_str(value): + return filter_generated_node_ids(value) + return value + return filter_qmp(qmsg, _filter) + +def filter_img_info(output: str, filename: str, + drop_child_info: bool = True) -> str: lines = [] + drop_indented = False for line in output.split('\n'): if 'disk size' in line or 'actual-size' in line: continue + + # Drop child node info + if drop_indented: + if line.startswith(' '): + continue + drop_indented = False + if drop_child_info and "Child node '/" in line: + drop_indented = True + continue + line = line.replace(filename, 'TEST_IMG') line = filter_testfiles(line) line = line.replace(imgfmt, 'IMGFMT') @@ -708,7 +736,7 @@ class Timeout: signal.setitimer(signal.ITIMER_REAL, 0) return False def timeout(self, signum, frame): - raise Exception(self.errmsg) + raise TimeoutError(self.errmsg) def file_pattern(name): return "{0}-{1}".format(os.getpid(), name) @@ -792,7 +820,7 @@ def remote_filename(path): elif imgproto == 'ssh': return "ssh://%s@127.0.0.1:22%s" % (os.environ.get('USER'), path) else: - raise Exception("Protocol %s not supported" % (imgproto)) + raise ValueError("Protocol %s not supported" % (imgproto)) class VM(qtest.QEMUQtestMachine): '''A QEMU VM''' @@ -807,7 +835,7 @@ class VM(qtest.QEMUQtestMachine): super().__init__(qemu_prog, qemu_opts, wrapper=wrapper, name=name, base_temp_dir=test_dir, - sock_dir=sock_dir, qmp_timer=timer) + qmp_timer=timer) self._num_drives = 0 def _post_shutdown(self) -> None: @@ -1231,8 +1259,7 @@ class QMPTestCase(unittest.TestCase): def cancel_and_wait(self, drive='drive0', force=False, resume=False, wait=60.0): '''Cancel a block job and wait for it to finish, returning the event''' - result = self.vm.qmp('block-job-cancel', device=drive, force=force) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-cancel', device=drive, force=force) if resume: self.vm.resume_drive(drive) @@ -1294,8 +1321,7 @@ class QMPTestCase(unittest.TestCase): if wait_ready: self.wait_ready(drive=drive) - result = self.vm.qmp('block-job-complete', device=drive) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-complete', device=drive) event = self.wait_until_completed(drive=drive, error=completion_error) self.assertTrue(event['data']['type'] in ['mirror', 'commit']) @@ -1314,11 +1340,9 @@ class QMPTestCase(unittest.TestCase): assert found def pause_job(self, job_id='job0', wait=True): - result = self.vm.qmp('block-job-pause', device=job_id) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-pause', device=job_id) if wait: - return self.pause_wait(job_id) - return result + self.pause_wait(job_id) def case_skip(self, reason): '''Skip this test case''' @@ -1405,7 +1429,7 @@ def _verify_virtio_blk() -> None: if 'virtio-blk' not in out: notrun('Missing virtio-blk in QEMU binary') -def _verify_virtio_scsi_pci_or_ccw() -> None: +def verify_virtio_scsi_pci_or_ccw() -> None: out = qemu_pipe('-M', 'none', '-device', 'help') if 'virtio-scsi-pci' not in out and 'virtio-scsi-ccw' not in out: notrun('Missing virtio-scsi-pci or virtio-scsi-ccw in QEMU binary') diff --git a/tests/qemu-iotests/linters.py b/tests/qemu-iotests/linters.py index 65c4c4e827..9fb3fd1449 100644 --- a/tests/qemu-iotests/linters.py +++ b/tests/qemu-iotests/linters.py @@ -68,7 +68,7 @@ def run_linter( :raise CalledProcessError: If the linter process exits with failure. """ subprocess.run( - ('python3', '-m', tool, *args), + (sys.executable, '-m', tool, *args), env=env, check=True, stdout=subprocess.PIPE if suppress_output else None, diff --git a/tests/qemu-iotests/meson.build b/tests/qemu-iotests/meson.build index 323a4acb6a..fad340ad59 100644 --- a/tests/qemu-iotests/meson.build +++ b/tests/qemu-iotests/meson.build @@ -1,8 +1,8 @@ -if not have_tools or targetos == 'windows' or get_option('gprof') +if not have_tools or host_os == 'windows' subdir_done() endif -foreach cflag: config_host['QEMU_CFLAGS'].split() +foreach cflag: qemu_ldflags if cflag.startswith('-fsanitize') and \ not cflag.contains('safe-stack') and not cflag.contains('cfi-icall') message('Sanitizers are enabled ==> Disabled the qemu-iotests.') @@ -32,16 +32,40 @@ foreach k, v : emulators endif endforeach +qemu_iotests_check_cmd = files('check') + foreach format, speed: qemu_iotests_formats if speed == 'quick' suites = 'block' else suites = ['block-' + speed, speed] endif - test('qemu-iotests ' + format, sh, args: [files('../check-block.sh'), format], - depends: qemu_iotests_binaries, env: qemu_iotests_env, - protocol: 'tap', - suite: suites, - timeout: 0, - is_parallel: false) + + args = ['-tap', '-' + format] + if speed == 'quick' + args += ['-g', 'auto'] + endif + + rc = run_command( + [python, qemu_iotests_check_cmd] + args + ['-n'], + check: true, + ) + + foreach item: rc.stdout().strip().split() + args = [qemu_iotests_check_cmd, + '-tap', '-' + format, item, + '--source-dir', meson.current_source_dir(), + '--build-dir', meson.current_build_dir()] + # Some individual tests take as long as 45 seconds + # Bump the timeout to 3 minutes for some headroom + # on slow machines to minimize spurious failures + test('io-' + format + '-' + item, + python, + args: args, + depends: qemu_iotests_binaries, + env: qemu_iotests_env, + protocol: 'tap', + timeout: 180, + suite: suites) + endforeach endforeach diff --git a/tests/qemu-iotests/mypy.ini b/tests/qemu-iotests/mypy.ini index 4c0339f558..d66ffc2e3c 100644 --- a/tests/qemu-iotests/mypy.ini +++ b/tests/qemu-iotests/mypy.ini @@ -9,4 +9,4 @@ no_implicit_optional = True scripts_are_modules = True warn_redundant_casts = True warn_unused_configs = True -warn_unused_ignores = True +warn_unused_ignores = False diff --git a/tests/qemu-iotests/pylintrc b/tests/qemu-iotests/pylintrc index 32ab77b8bb..de2e0c2781 100644 --- a/tests/qemu-iotests/pylintrc +++ b/tests/qemu-iotests/pylintrc @@ -19,7 +19,7 @@ disable=invalid-name, too-many-public-methods, # pylint warns about Optional[] etc. as unsubscriptable in 3.9 unsubscriptable-object, - # pylint's static analysis causes false positivies for file_path(); + # pylint's static analysis causes false positives for file_path(); # If we really care to make it statically knowable, we'll use mypy. unbalanced-tuple-unpacking, # Sometimes we need to disable a newly introduced pylint warning. @@ -51,3 +51,8 @@ notes=FIXME, # Maximum number of characters on a single line. max-line-length=79 + + +[SIMILARITIES] + +min-similarity-lines=6 diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index a864c74b12..588f30a4f1 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -40,7 +40,7 @@ def get_default_machine(qemu_prog: str) -> str: machines = outp.split('\n') try: - default_machine = next(m for m in machines if m.endswith(' (default)')) + default_machine = next(m for m in machines if ' (default)' in m) except StopIteration: return '' default_machine = default_machine.split(' ', 1)[0] @@ -126,7 +126,7 @@ class TestEnv(ContextManager['TestEnv']): self.tmp_sock_dir = False Path(self.sock_dir).mkdir(parents=True, exist_ok=True) except KeyError: - self.sock_dir = tempfile.mkdtemp() + self.sock_dir = tempfile.mkdtemp(prefix="qemu-iotests-") self.tmp_sock_dir = True self.sample_img_dir = os.getenv('SAMPLE_IMG_DIR', @@ -170,14 +170,16 @@ class TestEnv(ContextManager['TestEnv']): if not isxfile(b): sys.exit('Not executable: ' + b) - def __init__(self, imgfmt: str, imgproto: str, aiomode: str, + def __init__(self, source_dir: str, build_dir: str, + imgfmt: str, imgproto: str, aiomode: str, cachemode: Optional[str] = None, imgopts: Optional[str] = None, misalign: bool = False, debug: bool = False, valgrind: bool = False, gdb: bool = False, - qprint: bool = False) -> None: + qprint: bool = False, + dry_run: bool = False) -> None: self.imgfmt = imgfmt self.imgproto = imgproto self.aiomode = aiomode @@ -211,18 +213,16 @@ class TestEnv(ContextManager['TestEnv']): # which are needed to initialize some environment variables. They are # used by init_*() functions as well. - if os.path.islink(sys.argv[0]): - # called from the build tree - self.source_iotests = os.path.dirname(os.readlink(sys.argv[0])) - self.build_iotests = os.path.dirname(os.path.abspath(sys.argv[0])) - else: - # called from the source tree - self.source_iotests = os.getcwd() - self.build_iotests = self.source_iotests + self.source_iotests = source_dir + self.build_iotests = build_dir - self.build_root = os.path.join(self.build_iotests, '..', '..') + self.build_root = Path(self.build_iotests).parent.parent self.init_directories() + + if dry_run: + return + self.init_binaries() self.malloc_perturb_ = os.getenv('MALLOC_PERTURB_', diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py index 5a771da86e..7b322272e9 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py @@ -24,12 +24,10 @@ import difflib import subprocess import contextlib import json -import termios import shutil import sys from multiprocessing import Pool -from contextlib import contextmanager -from typing import List, Optional, Iterator, Any, Sequence, Dict, \ +from typing import List, Optional, Any, Sequence, Dict, \ ContextManager from testenv import TestEnv @@ -56,22 +54,6 @@ def file_diff(file1: str, file2: str) -> List[str]: return res -# We want to save current tty settings during test run, -# since an aborting qemu call may leave things screwed up. -@contextmanager -def savetty() -> Iterator[None]: - isterm = sys.stdin.isatty() - if isterm: - fd = sys.stdin.fileno() - attr = termios.tcgetattr(fd) - - try: - yield - finally: - if isterm: - termios.tcsetattr(fd, termios.TCSADRAIN, attr) - - class LastElapsedTime(ContextManager['LastElapsedTime']): """ Cache for elapsed time for tests, to show it during new test run @@ -169,7 +151,6 @@ class TestRunner(ContextManager['TestRunner']): self._stack = contextlib.ExitStack() self._stack.enter_context(self.env) self._stack.enter_context(self.last_elapsed) - self._stack.enter_context(savetty()) return self def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: @@ -247,13 +228,11 @@ class TestRunner(ContextManager['TestRunner']): return f'{test}.out' - def do_run_test(self, test: str, mp: bool) -> TestResult: + def do_run_test(self, test: str) -> TestResult: """ Run one test :param test: test file path - :param mp: if true, we are in a multiprocessing environment, use - personal subdirectories for test run Note: this method may be called from subprocess, so it does not change ``self`` object in any way! @@ -276,12 +255,14 @@ class TestRunner(ContextManager['TestRunner']): args = [str(f_test.resolve())] env = self.env.prepare_subprocess(args) - if mp: - # Split test directories, so that tests running in parallel don't - # break each other. - for d in ['TEST_DIR', 'SOCK_DIR']: - env[d] = os.path.join(env[d], f_test.name) - Path(env[d]).mkdir(parents=True, exist_ok=True) + + # Split test directories, so that tests running in parallel don't + # break each other. + for d in ['TEST_DIR', 'SOCK_DIR']: + env[d] = os.path.join( + env[d], + f"{self.env.imgfmt}-{self.env.imgproto}-{f_test.name}") + Path(env[d]).mkdir(parents=True, exist_ok=True) test_dir = env['TEST_DIR'] f_bad = Path(test_dir, f_test.name + '.out.bad') @@ -294,6 +275,7 @@ class TestRunner(ContextManager['TestRunner']): t0 = time.time() with f_bad.open('w', encoding="utf-8") as f: with subprocess.Popen(args, cwd=str(f_test.parent), env=env, + stdin=subprocess.DEVNULL, stdout=f, stderr=subprocess.STDOUT) as proc: try: proc.wait() @@ -365,7 +347,7 @@ class TestRunner(ContextManager['TestRunner']): testname = os.path.basename(test) print(f'# running {self.env.imgfmt} {testname}') - res = self.do_run_test(test, mp) + res = self.do_run_test(test) end = datetime.datetime.now().strftime('%H:%M:%S') self.test_print_one_line(test=test, @@ -391,6 +373,7 @@ class TestRunner(ContextManager['TestRunner']): casenotrun = [] if self.tap: + print('TAP version 13') self.env.print_env('# ') print('1..%d' % len(tests)) else: diff --git a/tests/qemu-iotests/tests/backing-file-invalidation b/tests/qemu-iotests/tests/backing-file-invalidation new file mode 100755 index 0000000000..b0e19839db --- /dev/null +++ b/tests/qemu-iotests/tests/backing-file-invalidation @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# group: rw migration +# +# Migrate a VM with a BDS with backing nodes, which runs +# bdrv_invalidate_cache(), which for qcow2 and qed triggers reading the +# backing file string from the image header. Check whether this +# interferes with bdrv_backing_overridden(). +# +# Copyright (C) 2022 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import json +import os +from typing import Optional + +import iotests +from iotests import qemu_img_create, qemu_img_info + + +image_size = 1 * 1024 * 1024 +imgs = [os.path.join(iotests.test_dir, f'{i}.img') for i in range(0, 4)] + +mig_sock = os.path.join(iotests.sock_dir, 'mig.sock') + + +class TestPostMigrateFilename(iotests.QMPTestCase): + vm_s: Optional[iotests.VM] = None + vm_d: Optional[iotests.VM] = None + + def setUp(self) -> None: + # Create backing chain of three images, where the backing file strings + # are json:{} filenames + qemu_img_create('-f', iotests.imgfmt, imgs[0], str(image_size)) + for i in range(1, 3): + backing = { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': imgs[i - 1] + } + } + qemu_img_create('-f', iotests.imgfmt, '-F', iotests.imgfmt, + '-b', 'json:' + json.dumps(backing), + imgs[i], str(image_size)) + + def tearDown(self) -> None: + if self.vm_s is not None: + self.vm_s.shutdown() + if self.vm_d is not None: + self.vm_d.shutdown() + + for img in imgs: + try: + os.remove(img) + except OSError: + pass + try: + os.remove(mig_sock) + except OSError: + pass + + def test_migration(self) -> None: + """ + Migrate a VM with the backing chain created in setUp() attached. At + the end of the migration process, the destination will run + bdrv_invalidate_cache(), which for some image formats (qcow2 and qed) + means the backing file string is re-read from the image header. If + this overwrites bs->auto_backing_file, doing so may cause + bdrv_backing_overridden() to become true: The image header reports a + json:{} filename, but when opening it, bdrv_refresh_filename() will + simplify it to a plain simple filename; and when bs->auto_backing_file + and bs->backing->bs->filename differ, bdrv_backing_overridden() becomes + true. + If bdrv_backing_overridden() is true, the BDS will be forced to get a + json:{} filename, which in general is not the end of the world, but not + great. Check whether that happens, i.e. whether migration changes the + node's filename. + """ + + blockdev = { + 'node-name': 'node0', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': imgs[2] + } + } + + self.vm_s = iotests.VM(path_suffix='a') \ + .add_blockdev(json.dumps(blockdev)) + self.vm_d = iotests.VM(path_suffix='b') \ + .add_blockdev(json.dumps(blockdev)) \ + .add_incoming(f'unix:{mig_sock}') + + assert self.vm_s is not None + assert self.vm_d is not None + + self.vm_s.launch() + self.vm_d.launch() + + pre_mig_filename = self.vm_s.node_info('node0')['file'] + + self.vm_s.qmp('migrate', uri=f'unix:{mig_sock}') + + # Wait for migration to be done + self.vm_s.event_wait('STOP') + self.vm_d.event_wait('RESUME') + + post_mig_filename = self.vm_d.node_info('node0')['file'] + + # Verify that the filename hasn't changed from before the migration + self.assertEqual(pre_mig_filename, post_mig_filename) + + self.vm_s.shutdown() + self.vm_s = None + + # For good measure, try creating an overlay and check its backing + # chain below. This is how the issue was originally found. + self.vm_d.cmd('blockdev-snapshot-sync', + format=iotests.imgfmt, + snapshot_file=imgs[3], + node_name='node0', + snapshot_node_name='node0-overlay') + + self.vm_d.shutdown() + self.vm_d = None + + # Check the newly created overlay's backing chain + chain = qemu_img_info('--backing-chain', imgs[3]) + for index, image in enumerate(chain): + self.assertEqual(image['filename'], imgs[3 - index]) + + +if __name__ == '__main__': + # These are the image formats that run their open() function from their + # .bdrv_co_invaliate_cache() implementations, so test them + iotests.main(supported_fmts=['qcow2', 'qed'], + supported_protocols=['file']) diff --git a/tests/qemu-iotests/tests/backing-file-invalidation.out b/tests/qemu-iotests/tests/backing-file-invalidation.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/tests/backing-file-invalidation.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK diff --git a/tests/qemu-iotests/tests/copy-before-write b/tests/qemu-iotests/tests/copy-before-write new file mode 100755 index 0000000000..d33bea577d --- /dev/null +++ b/tests/qemu-iotests/tests/copy-before-write @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +# group: auto backup +# +# Copyright (c) 2022 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 os +import re + +from qemu.machine import QEMUMachine + +import iotests +from iotests import qemu_img_create, qemu_io + + +temp_img = os.path.join(iotests.test_dir, 'temp') +source_img = os.path.join(iotests.test_dir, 'source') +size = '1M' + + +class TestCbwError(iotests.QMPTestCase): + def tearDown(self): + self.vm.shutdown() + os.remove(temp_img) + os.remove(source_img) + + def setUp(self): + qemu_img_create('-f', iotests.imgfmt, source_img, size) + qemu_img_create('-f', iotests.imgfmt, temp_img, size) + qemu_io('-c', 'write 0 1M', source_img) + + opts = ['-nodefaults', '-display', 'none', '-machine', 'none'] + self.vm = QEMUMachine(iotests.qemu_prog, opts, + base_temp_dir=iotests.test_dir) + self.vm.launch() + + def do_cbw_error(self, on_cbw_error): + self.vm.cmd('blockdev-add', { + 'node-name': 'cbw', + 'driver': 'copy-before-write', + 'on-cbw-error': on_cbw_error, + 'file': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': source_img, + } + }, + 'target': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'blkdebug', + 'image': { + 'driver': 'file', + 'filename': temp_img + }, + 'inject-error': [ + { + 'event': 'write_aio', + 'errno': 5, + 'immediately': False, + 'once': True + } + ] + } + } + }) + + self.vm.cmd('blockdev-add', { + 'node-name': 'access', + 'driver': 'snapshot-access', + 'file': 'cbw' + }) + + result = self.vm.qmp('human-monitor-command', + command_line='qemu-io cbw "write 0 1M"') + self.assert_qmp(result, 'return', '') + + result = self.vm.qmp('human-monitor-command', + command_line='qemu-io access "read 0 1M"') + self.assert_qmp(result, 'return', '') + + self.vm.shutdown() + log = self.vm.get_log() + log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) + log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) + log = iotests.filter_qemu_io(log) + return log + + def test_break_snapshot_on_cbw_error(self): + """break-snapshot behavior: + Guest write succeed, but further snapshot-read fails, as snapshot is + broken. + """ + log = self.do_cbw_error('break-snapshot') + + self.assertEqual(log, """\ +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read failed: Permission denied +""") + + def test_break_guest_write_on_cbw_error(self): + """break-guest-write behavior: + Guest write fails, but snapshot-access continues working and further + snapshot-read succeeds. + """ + log = self.do_cbw_error('break-guest-write') + + self.assertEqual(log, """\ +write failed: Input/output error +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +""") + + def do_cbw_timeout(self, on_cbw_error): + self.vm.cmd('object-add', { + 'qom-type': 'throttle-group', + 'id': 'group0', + 'limits': {'bps-write': 300 * 1024} + }) + + self.vm.cmd('blockdev-add', { + 'node-name': 'cbw', + 'driver': 'copy-before-write', + 'on-cbw-error': on_cbw_error, + 'cbw-timeout': 1, + 'file': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': source_img, + } + }, + 'target': { + 'driver': 'throttle', + 'throttle-group': 'group0', + 'file': { + 'driver': 'qcow2', + 'file': { + 'driver': 'file', + 'filename': temp_img + } + } + } + }) + + self.vm.cmd('blockdev-add', { + 'node-name': 'access', + 'driver': 'snapshot-access', + 'file': 'cbw' + }) + + result = self.vm.qmp('human-monitor-command', + command_line='qemu-io cbw "write 0 512K"') + self.assert_qmp(result, 'return', '') + + # We need second write to trigger throttling + result = self.vm.qmp('human-monitor-command', + command_line='qemu-io cbw "write 512K 512K"') + self.assert_qmp(result, 'return', '') + + result = self.vm.qmp('human-monitor-command', + command_line='qemu-io access "read 0 1M"') + self.assert_qmp(result, 'return', '') + + self.vm.shutdown() + log = self.vm.get_log() + log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) + log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) + log = iotests.filter_qemu_io(log) + return log + + def test_timeout_break_guest(self): + log = self.do_cbw_timeout('break-guest-write') + # macOS and FreeBSD tend to represent ETIMEDOUT as + # "Operation timed out", when Linux prefer + # "Connection timed out" + log = log.replace('Operation timed out', + 'Connection timed out') + self.assertEqual(log, """\ +wrote 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +write failed: Connection timed out +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +""") + + def test_timeout_break_snapshot(self): + log = self.do_cbw_timeout('break-snapshot') + self.assertEqual(log, """\ +wrote 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 524288/524288 bytes at offset 524288 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read failed: Permission denied +""") + + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2'], + supported_protocols=['file'], + required_fmts=['copy-before-write']) diff --git a/tests/qemu-iotests/tests/copy-before-write.out b/tests/qemu-iotests/tests/copy-before-write.out new file mode 100644 index 0000000000..89968f35d7 --- /dev/null +++ b/tests/qemu-iotests/tests/copy-before-write.out @@ -0,0 +1,5 @@ +.... +---------------------------------------------------------------------- +Ran 4 tests + +OK diff --git a/tests/qemu-iotests/tests/detect-zeroes-registered-buf b/tests/qemu-iotests/tests/detect-zeroes-registered-buf new file mode 100755 index 0000000000..5eaf34e5a6 --- /dev/null +++ b/tests/qemu-iotests/tests/detect-zeroes-registered-buf @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# group: rw auto quick +# +# Check that detect-zeroes=unmap works on writes with registered I/O buffers. +# This is a regression test for +# https://gitlab.com/qemu-project/qemu/-/issues/1404 where I/O requests failed +# unexpectedly. +# +# Copyright Red Hat +# +# 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 +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +IMGOPTSSYNTAX=true + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto generic + +size=128M +_make_test_img $size +IMGSPEC="$TEST_IMG,discard=unmap,detect-zeroes=unmap" + +echo +echo "== writing zero buffer to image ==" +QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS_NO_FMT" $QEMU_IO -c "write -r -P 0 0 4k" --image-opts "$IMGSPEC" | _filter_qemu_io + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/detect-zeroes-registered-buf.out b/tests/qemu-iotests/tests/detect-zeroes-registered-buf.out new file mode 100644 index 0000000000..42c56fcc8d --- /dev/null +++ b/tests/qemu-iotests/tests/detect-zeroes-registered-buf.out @@ -0,0 +1,7 @@ +QA output created by detect-zeroes-registered-buf +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 + +== writing zero buffer to image == +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/tests/export-incoming-iothread b/tests/qemu-iotests/tests/export-incoming-iothread index 7679e49103..d36d6194e0 100755 --- a/tests/qemu-iotests/tests/export-incoming-iothread +++ b/tests/qemu-iotests/tests/export-incoming-iothread @@ -55,7 +55,7 @@ class TestExportIncomingIothread(iotests.QMPTestCase): os.remove(test_img) def test_export_add(self): - result = self.vm.qmp('nbd-server-start', { + self.vm.cmd('nbd-server-start', { 'addr': { 'type': 'unix', 'data': { @@ -63,16 +63,14 @@ class TestExportIncomingIothread(iotests.QMPTestCase): } } }) - self.assert_qmp(result, 'return', {}) # Regression test for issue 945: This should not fail an assertion - result = self.vm.qmp('block-export-add', { + self.vm.cmd('block-export-add', { 'type': 'nbd', 'id': 'exp0', 'node-name': node_name, 'iothread': iothread_id }) - self.assert_qmp(result, 'return', {}) if __name__ == '__main__': diff --git a/tests/qemu-iotests/tests/file-io-error b/tests/qemu-iotests/tests/file-io-error new file mode 100755 index 0000000000..fb8db73b31 --- /dev/null +++ b/tests/qemu-iotests/tests/file-io-error @@ -0,0 +1,118 @@ +#!/usr/bin/env bash +# group: rw +# +# Produce an I/O error in file-posix, and hope that it is not catastrophic. +# Regression test for: https://bugzilla.redhat.com/show_bug.cgi?id=2234374 +# +# Copyright (C) 2023 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +seq=$(basename "$0") +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_qemu + rm -f "$TEST_DIR/fuse-export" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ../common.rc +. ../common.filter +. ../common.qemu + +# Format-agnostic (we do not use any), but we do test the file protocol +_supported_proto file +_require_drivers blkdebug null-co + +if [ "$IMGOPTSSYNTAX" = "true" ]; then + # We need `$QEMU_IO -f file` to work; IMGOPTSSYNTAX uses --image-opts, + # breaking -f. + _unsupported_fmt $IMGFMT +fi + +# This is a regression test of a bug in which flie-posix would access zone +# information in case of an I/O error even when there is no zone information, +# resulting in a division by zero. +# To reproduce the problem, we need to trigger an I/O error inside of +# file-posix, which can be done (rootless) by providing a FUSE export that +# presents only errors when accessed. + +_launch_qemu +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'qmp_capabilities'}" \ + 'return' + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'blockdev-add', + 'arguments': { + 'driver': 'blkdebug', + 'node-name': 'node0', + 'inject-error': [{'event': 'none'}], + 'image': { + 'driver': 'null-co' + } + }}" \ + 'return' + +# FUSE mountpoint must exist and be a regular file +touch "$TEST_DIR/fuse-export" + +# The grep -v to filter fusermount's (benign) error when /etc/fuse.conf does +# not contain user_allow_other and the subsequent check for missing FUSE support +# have both been taken from iotest 308. +output=$(_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'block-export-add', + 'arguments': { + 'id': 'exp0', + 'type': 'fuse', + 'node-name': 'node0', + 'mountpoint': '$TEST_DIR/fuse-export', + 'writable': true + }}" \ + 'return' \ + | grep -v 'option allow_other only allowed if') + +if echo "$output" | grep -q "Parameter 'type' does not accept value 'fuse'"; then + _notrun 'No FUSE support' +fi +echo "$output" + +echo +# This should fail, but gracefully, i.e. just print an I/O error, not crash. +$QEMU_IO -f file -c 'write 0 64M' "$TEST_DIR/fuse-export" | _filter_qemu_io +echo + +capture_events=BLOCK_EXPORT_DELETED _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'block-export-del', + 'arguments': {'id': 'exp0'}}" \ + 'return' + +_wait_event $QEMU_HANDLE \ + 'BLOCK_EXPORT_DELETED' + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'blockdev-del', + 'arguments': {'node-name': 'node0'}}" \ + 'return' + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/file-io-error.out b/tests/qemu-iotests/tests/file-io-error.out new file mode 100644 index 0000000000..0f46455a94 --- /dev/null +++ b/tests/qemu-iotests/tests/file-io-error.out @@ -0,0 +1,33 @@ +QA output created by file-io-error +{'execute': 'qmp_capabilities'} +{"return": {}} +{'execute': 'blockdev-add', + 'arguments': { + 'driver': 'blkdebug', + 'node-name': 'node0', + 'inject-error': [{'event': 'none'}], + 'image': { + 'driver': 'null-co' + } + }} +{"return": {}} +{'execute': 'block-export-add', + 'arguments': { + 'id': 'exp0', + 'type': 'fuse', + 'node-name': 'node0', + 'mountpoint': 'TEST_DIR/fuse-export', + 'writable': true + }} +{"return": {}} + +write failed: Input/output error + +{'execute': 'block-export-del', + 'arguments': {'id': 'exp0'}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "exp0"}} +{'execute': 'blockdev-del', + 'arguments': {'node-name': 'node0'}} +{"return": {}} +*** done diff --git a/tests/qemu-iotests/tests/graph-changes-while-io b/tests/qemu-iotests/tests/graph-changes-while-io index 7664f33689..194fda500e 100755 --- a/tests/qemu-iotests/tests/graph-changes-while-io +++ b/tests/qemu-iotests/tests/graph-changes-while-io @@ -22,19 +22,19 @@ import os from threading import Thread import iotests -from iotests import imgfmt, qemu_img, qemu_img_create, QMPTestCase, \ - QemuStorageDaemon +from iotests import imgfmt, qemu_img, qemu_img_create, qemu_io, \ + QMPTestCase, QemuStorageDaemon top = os.path.join(iotests.test_dir, 'top.img') nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock') -def do_qemu_img_bench() -> None: +def do_qemu_img_bench(count: int = 2000000) -> None: """ Do some I/O requests on `nbd_sock`. """ - qemu_img('bench', '-f', 'raw', '-c', '2000000', + qemu_img('bench', '-f', 'raw', '-c', str(count), f'nbd+unix:///node0?socket={nbd_sock}') @@ -66,7 +66,7 @@ class TestGraphChangesWhileIO(QMPTestCase): # While qemu-img bench is running, repeatedly add and remove an # overlay to/from node0 while bench_thr.is_alive(): - result = self.qsd.qmp('blockdev-add', { + self.qsd.cmd('blockdev-add', { 'driver': imgfmt, 'node-name': 'overlay', 'backing': 'node0', @@ -75,12 +75,54 @@ class TestGraphChangesWhileIO(QMPTestCase): 'filename': top } }) - self.assert_qmp(result, 'return', {}) - result = self.qsd.qmp('blockdev-del', { + self.qsd.cmd('blockdev-del', { 'node-name': 'overlay' }) - self.assert_qmp(result, 'return', {}) + + bench_thr.join() + + def test_commit_while_io(self) -> None: + # Run qemu-img bench in the background + bench_thr = Thread(target=do_qemu_img_bench, args=(200000, )) + bench_thr.start() + + qemu_io('-c', 'write 0 64k', top) + qemu_io('-c', 'write 128k 64k', top) + + self.qsd.cmd('blockdev-add', { + 'driver': imgfmt, + 'node-name': 'overlay', + 'backing': None, + 'file': { + 'driver': 'file', + 'filename': top + } + }) + + self.qsd.cmd('blockdev-snapshot', { + 'node': 'node0', + 'overlay': 'overlay', + }) + + # While qemu-img bench is running, repeatedly commit overlay to node0 + while bench_thr.is_alive(): + self.qsd.cmd('block-commit', { + 'job-id': 'job0', + 'device': 'overlay', + }) + + self.qsd.cmd('block-job-cancel', { + 'device': 'job0', + }) + + cancelled = False + while not cancelled: + for event in self.qsd.get_qmp().get_events(wait=10.0): + if event['event'] != 'JOB_STATUS_CHANGE': + continue + if event['data']['status'] == 'null': + cancelled = True bench_thr.join() diff --git a/tests/qemu-iotests/tests/graph-changes-while-io.out b/tests/qemu-iotests/tests/graph-changes-while-io.out index ae1213e6f8..fbc63e62f8 100644 --- a/tests/qemu-iotests/tests/graph-changes-while-io.out +++ b/tests/qemu-iotests/tests/graph-changes-while-io.out @@ -1,5 +1,5 @@ -. +.. ---------------------------------------------------------------------- -Ran 1 tests +Ran 2 tests OK diff --git a/tests/qemu-iotests/tests/image-fleecing b/tests/qemu-iotests/tests/image-fleecing index f6e449d071..5e3b2c7e46 100755 --- a/tests/qemu-iotests/tests/image-fleecing +++ b/tests/qemu-iotests/tests/image-fleecing @@ -213,8 +213,7 @@ def do_test(vm, use_cbw, use_snapshot_access_filter, base_img_path, result = vm.qmp('query-block-jobs') assert len(result['return']) == 1 - result = vm.qmp('block-job-set-speed', device='push-backup', speed=0) - assert result == {'return': {}} + vm.cmd('block-job-set-speed', device='push-backup', speed=0) log(vm.event_wait(name='BLOCK_JOB_COMPLETED', match={'data': {'device': 'push-backup'}}), diff --git a/tests/qemu-iotests/tests/iothreads-commit-active b/tests/qemu-iotests/tests/iothreads-commit-active new file mode 100755 index 0000000000..4010a4871f --- /dev/null +++ b/tests/qemu-iotests/tests/iothreads-commit-active @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# group: rw quick auto +# +# Copyright (C) 2023 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Creator/Owner: Kevin Wolf + +import asyncio +import iotests + +iotests.script_initialize(supported_fmts=['qcow2'], + supported_platforms=['linux']) +iotests.verify_virtio_scsi_pci_or_ccw() + +with iotests.FilePath('disk0.img') as img_path, \ + iotests.FilePath('disk0-snap.img') as snap_path, \ + iotests.FilePath('mirror-src.img') as src_path, \ + iotests.FilePath('mirror-dst.img') as dst_path, \ + iotests.VM() as vm: + + img_size = '10M' + iotests.qemu_img_create('-f', iotests.imgfmt, img_path, img_size) + iotests.qemu_img_create('-f', iotests.imgfmt, '-b', img_path, + '-F', iotests.imgfmt, snap_path) + iotests.qemu_img_create('-f', iotests.imgfmt, src_path, img_size) + iotests.qemu_img_create('-f', iotests.imgfmt, dst_path, img_size) + + iotests.qemu_io_log('-c', 'write 0 64k', img_path) + iotests.qemu_io_log('-c', 'write 1M 64k', snap_path) + iotests.qemu_io_log('-c', 'write 3M 64k', snap_path) + + iotests.qemu_io_log('-c', f'write 0 {img_size}', src_path) + + iotests.log('Launching VM...') + vm.add_object('iothread,id=iothread0') + vm.add_object('throttle-group,x-bps-write=1048576,id=tg0') + vm.add_blockdev(f'file,node-name=disk0-file,filename={img_path}') + vm.add_blockdev('qcow2,node-name=disk0-fmt,file=disk0-file') + vm.add_drive(snap_path, 'backing=disk0-fmt,node-name=disk0', + interface='none') + vm.add_device('virtio-scsi,iothread=iothread0') + vm.add_device('scsi-hd,drive=drive0') + + vm.add_blockdev(f'file,filename={src_path},node-name=mirror-src-file') + vm.add_blockdev('qcow2,file=mirror-src-file,node-name=mirror-src') + vm.add_blockdev(f'file,filename={dst_path},node-name=mirror-dst-file') + vm.add_blockdev('qcow2,file=mirror-dst-file,node-name=mirror-dst-fmt') + vm.add_blockdev('throttle,throttle-group=tg0,file=mirror-dst-fmt,' + 'node-name=mirror-dst') + vm.add_device('scsi-hd,drive=mirror-src') + + vm.launch() + + # The background I/O is created on unrelated nodes (so that they won't be + # drained together with the other ones), but on the same iothread + iotests.log('Creating some background I/O...') + iotests.log(vm.qmp('blockdev-mirror', job_id='job0', sync='full', + device='mirror-src', target='mirror-dst', + auto_dismiss=False)) + + iotests.log('Starting active commit...') + iotests.log(vm.qmp('block-commit', device='disk0', job_id='job1', + auto_dismiss=False)) + + # Should succeed and not time out + try: + vm.run_job('job1', wait=5.0) + vm.shutdown() + except asyncio.TimeoutError: + # VM may be stuck, kill it + vm.kill() + raise diff --git a/tests/qemu-iotests/tests/iothreads-commit-active.out b/tests/qemu-iotests/tests/iothreads-commit-active.out new file mode 100644 index 0000000000..4afd50b8d3 --- /dev/null +++ b/tests/qemu-iotests/tests/iothreads-commit-active.out @@ -0,0 +1,23 @@ +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 1048576 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +wrote 65536/65536 bytes at offset 3145728 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +wrote 10485760/10485760 bytes at offset 0 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Launching VM... +Creating some background I/O... +{"return": {}} +Starting active commit... +{"return": {}} +{"execute": "job-complete", "arguments": {"id": "job1"}} +{"return": {}} +{"data": {"device": "job1", "len": 131072, "offset": 131072, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"device": "job1", "len": 131072, "offset": 131072, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"execute": "job-dismiss", "arguments": {"id": "job1"}} +{"return": {}} diff --git a/tests/qemu-iotests/tests/iothreads-create b/tests/qemu-iotests/tests/iothreads-create new file mode 100755 index 0000000000..0c862d73f2 --- /dev/null +++ b/tests/qemu-iotests/tests/iothreads-create @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# group: rw quick +# +# Copyright (C) 2023 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Creator/Owner: Kevin Wolf + +import asyncio +import iotests + +iotests.script_initialize(supported_fmts=['qcow2', 'qcow', 'qed', 'vdi', + 'vmdk', 'parallels']) +iotests.verify_virtio_scsi_pci_or_ccw() + +with iotests.FilePath('disk.img') as img_path, \ + iotests.VM() as vm: + + iotests.qemu_img_create('-f', 'raw', img_path, '0') + + vm.add_object('iothread,id=iothread0') + vm.add_blockdev(f'file,node-name=img-file,read-only=on,' + f'filename={img_path}') + vm.add_device('virtio-scsi,iothread=iothread0') + vm.add_device('scsi-hd,drive=img-file,share-rw=on') + + vm.launch() + + iotests.log(vm.qmp( + 'blockdev-reopen', + options=[{ + 'driver': 'file', + 'filename': img_path, + 'node-name': 'img-file', + 'read-only': False, + }], + )) + iotests.log(vm.qmp( + 'blockdev-create', + job_id='job0', + options={ + 'driver': iotests.imgfmt, + 'file': 'img-file', + 'size': 1024 * 1024, + }, + )) + + # Should succeed and not time out + try: + vm.run_job('job0', wait=5.0) + vm.shutdown() + except asyncio.TimeoutError: + # VM may be stuck, kill it + vm.kill() + raise diff --git a/tests/qemu-iotests/tests/iothreads-create.out b/tests/qemu-iotests/tests/iothreads-create.out new file mode 100644 index 0000000000..5c974ff77e --- /dev/null +++ b/tests/qemu-iotests/tests/iothreads-create.out @@ -0,0 +1,4 @@ +{"return": {}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} diff --git a/tests/qemu-iotests/tests/iothreads-nbd-export b/tests/qemu-iotests/tests/iothreads-nbd-export new file mode 100755 index 0000000000..037260729c --- /dev/null +++ b/tests/qemu-iotests/tests/iothreads-nbd-export @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# group: rw quick +# +# Copyright (C) 2024 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 + +import time +import qemu +import iotests + +iotests.script_initialize(supported_fmts=['qcow2'], + supported_platforms=['linux']) + +with iotests.FilePath('disk1.img') as path, \ + iotests.FilePath('nbd.sock', base_dir=iotests.sock_dir) as nbd_sock, \ + qemu.machine.QEMUMachine(iotests.qemu_prog) as vm: + + img_size = '10M' + + iotests.log('Preparing disk...') + iotests.qemu_img_create('-f', iotests.imgfmt, path, img_size) + vm.add_args('-blockdev', f'file,node-name=disk-file,filename={path}') + vm.add_args('-blockdev', 'qcow2,node-name=disk,file=disk-file') + vm.add_args('-object', 'iothread,id=iothread0') + vm.add_args('-device', + 'virtio-blk,drive=disk,iothread=iothread0,share-rw=on') + + iotests.log('Launching VM...') + vm.add_args('-accel', 'kvm', '-accel', 'tcg') + #vm.add_args('-accel', 'qtest') + vm.launch() + + iotests.log('Exporting to NBD...') + iotests.log(vm.qmp('nbd-server-start', + addr={'type': 'unix', 'data': {'path': nbd_sock}})) + iotests.log(vm.qmp('block-export-add', type='nbd', id='exp0', + node_name='disk', writable=True)) + + iotests.log('Connecting qemu-img...') + qemu_io = iotests.QemuIoInteractive('-f', 'raw', + f'nbd+unix:///disk?socket={nbd_sock}') + + iotests.log('Moving the NBD export to a different iothread...') + for i in range(0, 10): + iotests.log(vm.qmp('system_reset')) + time.sleep(0.1) + + iotests.log('Checking that it is still alive...') + iotests.log(vm.qmp('query-status')) + + qemu_io.close() + vm.shutdown() diff --git a/tests/qemu-iotests/tests/iothreads-nbd-export.out b/tests/qemu-iotests/tests/iothreads-nbd-export.out new file mode 100644 index 0000000000..bc514e35e5 --- /dev/null +++ b/tests/qemu-iotests/tests/iothreads-nbd-export.out @@ -0,0 +1,19 @@ +Preparing disk... +Launching VM... +Exporting to NBD... +{"return": {}} +{"return": {}} +Connecting qemu-img... +Moving the NBD export to a different iothread... +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +Checking that it is still alive... +{"return": {"running": true, "status": "running"}} diff --git a/tests/qemu-iotests/tests/iothreads-resize b/tests/qemu-iotests/tests/iothreads-resize new file mode 100755 index 0000000000..36e4598c62 --- /dev/null +++ b/tests/qemu-iotests/tests/iothreads-resize @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# group: rw auto quick +# +# Test resizing an image that is attached to a separate iothread +# +# Copyright (C) 2023 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter + +# Resizing images is only supported by a few block drivers +_supported_fmt raw qcow2 qed +_supported_proto file +_require_devices virtio-scsi-pci + +size=64M +_make_test_img $size + +qmp() { +cat <. +# +# Creator/Owner: Kevin Wolf + +import asyncio +import iotests + +iotests.script_initialize(supported_fmts=['qcow2'], + supported_platforms=['linux']) +iotests.verify_virtio_scsi_pci_or_ccw() + +with iotests.FilePath('disk1.img') as base1_path, \ + iotests.FilePath('disk1-snap.img') as snap1_path, \ + iotests.FilePath('disk2.img') as base2_path, \ + iotests.FilePath('disk2-snap.img') as snap2_path, \ + iotests.VM() as vm: + + img_size = '10M' + + # Only one iothread for both disks + vm.add_object('iothread,id=iothread0') + vm.add_device('virtio-scsi,iothread=iothread0') + + iotests.log('Preparing disks...') + for i, base_path, snap_path in ((0, base1_path, snap1_path), + (1, base2_path, snap2_path)): + iotests.qemu_img_create('-f', iotests.imgfmt, base_path, img_size) + iotests.qemu_img_create('-f', iotests.imgfmt, '-b', base_path, + '-F', iotests.imgfmt, snap_path) + + iotests.qemu_io_log('-c', f'write 0 {img_size}', base_path) + + vm.add_blockdev(f'file,node-name=disk{i}-base-file,' + f'filename={base_path}') + vm.add_blockdev(f'qcow2,node-name=disk{i}-base,file=disk{i}-base-file') + vm.add_blockdev(f'file,node-name=disk{i}-file,filename={snap_path}') + vm.add_blockdev(f'qcow2,node-name=disk{i},file=disk{i}-file,' + f'backing=disk{i}-base') + vm.add_device(f'scsi-hd,drive=disk{i}') + + iotests.log('Launching VM...') + vm.launch() + + iotests.log('Starting stream jobs...') + iotests.log(vm.qmp('block-stream', device='disk0', job_id='job0')) + iotests.log(vm.qmp('block-stream', device='disk1', job_id='job1')) + + finished = 0 + while True: + try: + ev = vm.event_wait('JOB_STATUS_CHANGE', timeout=0.1) + if ev is not None and ev['data']['status'] == 'null': + finished += 1 + # The test is done once both jobs are gone + if finished == 2: + break + except asyncio.TimeoutError: + pass + vm.cmd('query-jobs') diff --git a/tests/qemu-iotests/tests/iothreads-stream.out b/tests/qemu-iotests/tests/iothreads-stream.out new file mode 100644 index 0000000000..ef134165e5 --- /dev/null +++ b/tests/qemu-iotests/tests/iothreads-stream.out @@ -0,0 +1,11 @@ +Preparing disks... +wrote 10485760/10485760 bytes at offset 0 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +wrote 10485760/10485760 bytes at offset 0 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Launching VM... +Starting stream jobs... +{"return": {}} +{"return": {}} diff --git a/tests/qemu-iotests/tests/iov-padding b/tests/qemu-iotests/tests/iov-padding new file mode 100755 index 0000000000..b9604900c7 --- /dev/null +++ b/tests/qemu-iotests/tests/iov-padding @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# group: rw quick +# +# Check the interaction of request padding (to fit alignment restrictions) with +# vectored I/O from the guest +# +# Copyright Red Hat +# +# 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 +cd .. +. ./common.rc +. ./common.filter + +_supported_fmt raw +_supported_proto file + +_make_test_img 1M + +IMGSPEC="driver=blkdebug,align=4096,image.driver=file,image.filename=$TEST_IMG" + +# Four combinations: +# - Offset 4096, length 1023 * 512 + 512: Fully aligned to 4k +# - Offset 4096, length 1023 * 512 + 4096: Head is aligned, tail is not +# - Offset 512, length 1023 * 512 + 512: Neither head nor tail are aligned +# - Offset 512, length 1023 * 512 + 4096: Tail is aligned, head is not +for start_offset in 4096 512; do + for last_element_length in 512 4096; do + length=$((1023 * 512 + $last_element_length)) + + echo + echo "== performing 1024-element vectored requests to image (offset: $start_offset; length: $length) ==" + + # Fill with data for testing + $QEMU_IO -c 'write -P 1 0 1M' "$TEST_IMG" | _filter_qemu_io + + # 1023 512-byte buffers, and then one with length $last_element_length + cmd_params="-P 2 $start_offset $(yes 512 | head -n 1023 | tr '\n' ' ') $last_element_length" + QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS_NO_FMT" $QEMU_IO \ + -c "writev $cmd_params" \ + --image-opts \ + "$IMGSPEC" \ + | _filter_qemu_io + + # Read all patterns -- read the part we just wrote with writev twice, + # once "normally", and once with a readv, so we see that that works, too + QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS_NO_FMT" $QEMU_IO \ + -c "read -P 1 0 $start_offset" \ + -c "read -P 2 $start_offset $length" \ + -c "readv $cmd_params" \ + -c "read -P 1 $((start_offset + length)) $((1024 * 1024 - length - start_offset))" \ + --image-opts \ + "$IMGSPEC" \ + | _filter_qemu_io + done +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/iov-padding.out b/tests/qemu-iotests/tests/iov-padding.out new file mode 100644 index 0000000000..e07a91fac7 --- /dev/null +++ b/tests/qemu-iotests/tests/iov-padding.out @@ -0,0 +1,59 @@ +QA output created by iov-padding +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 + +== performing 1024-element vectored requests to image (offset: 4096; length: 524288) == +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 524288/524288 bytes at offset 4096 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 4096 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 4096 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 520192/520192 bytes at offset 528384 +508 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== performing 1024-element vectored requests to image (offset: 4096; length: 527872) == +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 527872/527872 bytes at offset 4096 +515.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 527872/527872 bytes at offset 4096 +515.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 527872/527872 bytes at offset 4096 +515.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 516608/516608 bytes at offset 531968 +504.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== performing 1024-element vectored requests to image (offset: 512; length: 524288) == +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 524288/524288 bytes at offset 512 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 512 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 524288/524288 bytes at offset 512 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 523776/523776 bytes at offset 524800 +511.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== performing 1024-element vectored requests to image (offset: 512; length: 527872) == +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 527872/527872 bytes at offset 512 +515.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 527872/527872 bytes at offset 512 +515.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 527872/527872 bytes at offset 512 +515.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 520192/520192 bytes at offset 528384 +508 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/tests/luks-detached-header b/tests/qemu-iotests/tests/luks-detached-header new file mode 100755 index 0000000000..3455fd8de1 --- /dev/null +++ b/tests/qemu-iotests/tests/luks-detached-header @@ -0,0 +1,316 @@ +#!/usr/bin/env python3 +# group: rw auto +# +# Test LUKS volume with detached header +# +# Copyright (C) 2024 SmartX Inc. +# +# Authors: +# Hyman Huang +# +# 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 json +import iotests +from iotests import ( + imgfmt, + qemu_img_create, + qemu_img_info, + QMPTestCase, +) + + +image_size = 128 * 1024 * 1024 + +luks_img = os.path.join(iotests.test_dir, "luks.img") +detached_header_img1 = os.path.join(iotests.test_dir, "detached_header.img1") +detached_header_img2 = os.path.join(iotests.test_dir, "detached_header.img2") +detached_payload_raw_img = os.path.join( + iotests.test_dir, "detached_payload_raw.img" +) +detached_payload_qcow2_img = os.path.join( + iotests.test_dir, "detached_payload_qcow2.img" +) +detached_header_raw_img = "json:" + json.dumps( + { + "driver": "luks", + "file": {"filename": detached_payload_raw_img}, + "header": { + "filename": detached_header_img1, + }, + } +) +detached_header_qcow2_img = "json:" + json.dumps( + { + "driver": "luks", + "file": {"filename": detached_payload_qcow2_img}, + "header": {"filename": detached_header_img2}, + } +) + +secret_obj = "secret,id=sec0,data=foo" +luks_opts = "key-secret=sec0" + + +class TestDetachedLUKSHeader(QMPTestCase): + def setUp(self) -> None: + self.vm = iotests.VM() + self.vm.add_object(secret_obj) + self.vm.launch() + + # 1. Create the normal LUKS disk with 128M size + self.vm.blockdev_create( + {"driver": "file", "filename": luks_img, "size": 0} + ) + self.vm.qmp_log( + "blockdev-add", + driver="file", + filename=luks_img, + node_name="luks-1-storage", + ) + result = self.vm.blockdev_create( + { + "driver": imgfmt, + "file": "luks-1-storage", + "key-secret": "sec0", + "size": image_size, + "iter-time": 10, + } + ) + # None is expected + self.assertEqual(result, None) + + # 2. Create the LUKS disk with detached header (raw) + + # Create detached LUKS header + self.vm.blockdev_create( + {"driver": "file", "filename": detached_header_img1, "size": 0} + ) + self.vm.qmp_log( + "blockdev-add", + driver="file", + filename=detached_header_img1, + node_name="luks-2-header-storage", + ) + + # Create detached LUKS raw payload + self.vm.blockdev_create( + {"driver": "file", "filename": detached_payload_raw_img, "size": 0} + ) + self.vm.qmp_log( + "blockdev-add", + driver="file", + filename=detached_payload_raw_img, + node_name="luks-2-payload-storage", + ) + + # Format LUKS disk with detached header + result = self.vm.blockdev_create( + { + "driver": imgfmt, + "header": "luks-2-header-storage", + "file": "luks-2-payload-storage", + "key-secret": "sec0", + "preallocation": "full", + "size": image_size, + "iter-time": 10, + } + ) + self.assertEqual(result, None) + + self.vm.shutdown() + + # 3. Create the LUKS disk with detached header (qcow2) + + # Create detached LUKS header using qemu-img + res = qemu_img_create( + "-f", + "luks", + "--object", + secret_obj, + "-o", + luks_opts, + "-o", + "detached-header=true", + detached_header_img2, + ) + assert res.returncode == 0 + + # Create detached LUKS qcow2 payload + res = qemu_img_create( + "-f", "qcow2", detached_payload_qcow2_img, str(image_size) + ) + assert res.returncode == 0 + + def tearDown(self) -> None: + os.remove(luks_img) + os.remove(detached_header_img1) + os.remove(detached_header_img2) + os.remove(detached_payload_raw_img) + os.remove(detached_payload_qcow2_img) + + # Check if there was any qemu-io run that failed + if "Pattern verification failed" in self.vm.get_log(): + print("ERROR: Pattern verification failed:") + print(self.vm.get_log()) + self.fail("qemu-io pattern verification failed") + + def test_img_creation(self) -> None: + # Check if the images created above are expected + + data = qemu_img_info(luks_img)["format-specific"] + self.assertEqual(data["type"], imgfmt) + self.assertEqual(data["data"]["detached-header"], False) + + data = qemu_img_info(detached_header_raw_img)["format-specific"] + self.assertEqual(data["type"], imgfmt) + self.assertEqual(data["data"]["detached-header"], True) + + data = qemu_img_info(detached_header_qcow2_img)["format-specific"] + self.assertEqual(data["type"], imgfmt) + self.assertEqual(data["data"]["detached-header"], True) + + # Check if preallocation works + size = qemu_img_info(detached_payload_raw_img)["actual-size"] + self.assertGreaterEqual(size, image_size) + + def test_detached_luks_header(self) -> None: + self.vm.launch() + + # 1. Add the disk created above + + # Add normal LUKS disk + self.vm.qmp_log( + "blockdev-add", + driver="file", + filename=luks_img, + node_name="luks-1-storage", + ) + result = self.vm.qmp_log( + "blockdev-add", + driver="luks", + file="luks-1-storage", + key_secret="sec0", + node_name="luks-1-format", + ) + + # Expected result{ "return": {} } + self.assert_qmp(result, "return", {}) + + # Add detached LUKS header with raw payload + self.vm.qmp_log( + "blockdev-add", + driver="file", + filename=detached_header_img1, + node_name="luks-header1-storage", + ) + + self.vm.qmp_log( + "blockdev-add", + driver="file", + filename=detached_payload_raw_img, + node_name="luks-2-payload-raw-storage", + ) + + result = self.vm.qmp_log( + "blockdev-add", + driver=imgfmt, + header="luks-header1-storage", + file="luks-2-payload-raw-storage", + key_secret="sec0", + node_name="luks-2-payload-raw-format", + ) + self.assert_qmp(result, "return", {}) + + # Add detached LUKS header with qcow2 payload + self.vm.qmp_log( + "blockdev-add", + driver="file", + filename=detached_header_img2, + node_name="luks-header2-storage", + ) + + self.vm.qmp_log( + "blockdev-add", + driver="file", + filename=detached_payload_qcow2_img, + node_name="luks-3-payload-qcow2-storage", + ) + + result = self.vm.qmp_log( + "blockdev-add", + driver=imgfmt, + header="luks-header2-storage", + file="luks-3-payload-qcow2-storage", + key_secret="sec0", + node_name="luks-3-payload-qcow2-format", + ) + self.assert_qmp(result, "return", {}) + + # 2. Do I/O test + + # Do some I/O to the image to see whether it still works + # (Pattern verification will be checked by tearDown()) + + # Normal LUKS disk + result = self.vm.qmp_log( + "human-monitor-command", + command_line='qemu-io luks-1-format "write -P 40 0 64k"', + ) + self.assert_qmp(result, "return", "") + + result = self.vm.qmp_log( + "human-monitor-command", + command_line='qemu-io luks-1-format "read -P 40 0 64k"', + ) + self.assert_qmp(result, "return", "") + + # Detached LUKS header with raw payload + cmd = 'qemu-io luks-2-payload-raw-format "write -P 41 0 64k"' + result = self.vm.qmp( + "human-monitor-command", + command_line=cmd + ) + self.assert_qmp(result, "return", "") + + cmd = 'qemu-io luks-2-payload-raw-format "read -P 41 0 64k"' + result = self.vm.qmp( + "human-monitor-command", + command_line=cmd + ) + self.assert_qmp(result, "return", "") + + # Detached LUKS header with qcow2 payload + cmd = 'qemu-io luks-3-payload-qcow2-format "write -P 42 0 64k"' + result = self.vm.qmp( + "human-monitor-command", + command_line=cmd + ) + self.assert_qmp(result, "return", "") + + cmd = 'qemu-io luks-3-payload-qcow2-format "read -P 42 0 64k"' + result = self.vm.qmp( + "human-monitor-command", + command_line=cmd + ) + self.assert_qmp(result, "return", "") + + self.vm.shutdown() + + +if __name__ == "__main__": + # Test image creation and I/O + iotests.main(supported_fmts=["luks"], supported_protocols=["file"]) diff --git a/tests/qemu-iotests/tests/luks-detached-header.out b/tests/qemu-iotests/tests/luks-detached-header.out new file mode 100644 index 0000000000..fbc63e62f8 --- /dev/null +++ b/tests/qemu-iotests/tests/luks-detached-header.out @@ -0,0 +1,5 @@ +.. +---------------------------------------------------------------------- +Ran 2 tests + +OK diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test index fc9c4b4ef4..c519e6db8c 100755 --- a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test +++ b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test @@ -84,7 +84,7 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase): e['vm'] = 'SRC' for e in self.vm_b_events: e['vm'] = 'DST' - events = (self.vm_a_events + self.vm_b_events) + events = self.vm_a_events + self.vm_b_events events = [(e['timestamp']['seconds'], e['timestamp']['microseconds'], e['vm'], @@ -118,11 +118,10 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase): def start_postcopy(self): """ Run migration until RESUME event on target. Return this event. """ for i in range(nb_bitmaps): - result = self.vm_a.qmp('block-dirty-bitmap-add', node='drive0', - name='bitmap{}'.format(i), - granularity=granularity, - persistent=True) - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('block-dirty-bitmap-add', node='drive0', + name='bitmap{}'.format(i), + granularity=granularity, + persistent=True) result = self.vm_a.qmp('x-debug-block-dirty-bitmap-sha256', node='drive0', name='bitmap0') @@ -140,9 +139,8 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase): # We want to calculate resulting sha256. Do it in bitmap0, so, disable # other bitmaps for i in range(1, nb_bitmaps): - result = self.vm_a.qmp('block-dirty-bitmap-disable', node='drive0', - name='bitmap{}'.format(i)) - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('block-dirty-bitmap-disable', node='drive0', + name='bitmap{}'.format(i)) apply_discards(self.vm_a, discards2) @@ -152,24 +150,19 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase): # Now, enable some bitmaps, to be updated during migration for i in range(2, nb_bitmaps, 2): - result = self.vm_a.qmp('block-dirty-bitmap-enable', node='drive0', - name='bitmap{}'.format(i)) - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('block-dirty-bitmap-enable', node='drive0', + name='bitmap{}'.format(i)) caps = [{'capability': 'dirty-bitmaps', 'state': True}, {'capability': 'events', 'state': True}] - result = self.vm_a.qmp('migrate-set-capabilities', capabilities=caps) - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('migrate-set-capabilities', capabilities=caps) - result = self.vm_b.qmp('migrate-set-capabilities', capabilities=caps) - self.assert_qmp(result, 'return', {}) + self.vm_b.cmd('migrate-set-capabilities', capabilities=caps) - result = self.vm_a.qmp('migrate', uri='exec:cat>' + fifo) - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('migrate', uri='exec:cat>' + fifo) - result = self.vm_a.qmp('migrate-start-postcopy') - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('migrate-start-postcopy') event_resume = self.vm_b.event_wait('RESUME') self.vm_b_events.append(event_resume) diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test b/tests/qemu-iotests/tests/migrate-bitmaps-test index 59f3357580..f98e721e97 100755 --- a/tests/qemu-iotests/tests/migrate-bitmaps-test +++ b/tests/qemu-iotests/tests/migrate-bitmaps-test @@ -67,8 +67,7 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): if persistent: params['persistent'] = True - result = vm.qmp('block-dirty-bitmap-add', **params) - self.assert_qmp(result, 'return', {}) + vm.cmd('block-dirty-bitmap-add', params) def check_bitmap(self, vm, sha256): result = vm.qmp('x-debug-block-dirty-bitmap-sha256', @@ -91,16 +90,15 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): if migrate_bitmaps: mig_caps.append({'capability': 'dirty-bitmaps', 'state': True}) - result = self.vm_a.qmp('migrate-set-capabilities', - capabilities=mig_caps) - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('migrate-set-capabilities', + capabilities=mig_caps) self.add_bitmap(self.vm_a, granularity, persistent) for r in regions: self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % r) sha256 = get_bitmap_hash(self.vm_a) - result = self.vm_a.qmp('migrate', uri=mig_cmd) + self.vm_a.cmd('migrate', uri=mig_cmd) while True: event = self.vm_a.event_wait('MIGRATION') if event['data']['status'] == 'completed': @@ -114,8 +112,7 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): removed = (not migrate_bitmaps) and persistent self.check_bitmap(self.vm_a, False if removed else sha256) - result = self.vm_a.qmp('cont') - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('cont') # test that bitmap is still here after invalidation self.check_bitmap(self.vm_a, sha256) @@ -158,9 +155,8 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): if online: os.mkfifo(mig_file) self.vm_b.launch() - result = self.vm_b.qmp('migrate-set-capabilities', - capabilities=mig_caps) - self.assert_qmp(result, 'return', {}) + self.vm_b.cmd('migrate-set-capabilities', + capabilities=mig_caps) self.add_bitmap(self.vm_a, granularity, persistent) for r in regions: @@ -171,11 +167,10 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): self.vm_a.shutdown() self.vm_a.launch() - result = self.vm_a.qmp('migrate-set-capabilities', - capabilities=mig_caps) - self.assert_qmp(result, 'return', {}) + self.vm_a.cmd('migrate-set-capabilities', + capabilities=mig_caps) - result = self.vm_a.qmp('migrate', uri=mig_cmd) + self.vm_a.cmd('migrate', uri=mig_cmd) while True: event = self.vm_a.event_wait('MIGRATION') if event['data']['status'] == 'completed': @@ -184,11 +179,9 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): if not online: self.vm_a.shutdown() self.vm_b.launch() - result = self.vm_b.qmp('migrate-set-capabilities', - capabilities=mig_caps) - self.assert_qmp(result, 'return', {}) - result = self.vm_b.qmp('migrate-incoming', uri=incoming_cmd) - self.assert_qmp(result, 'return', {}) + self.vm_b.cmd('migrate-set-capabilities', + capabilities=mig_caps) + self.vm_b.cmd('migrate-incoming', uri=incoming_cmd) while True: event = self.vm_b.event_wait('MIGRATION') @@ -254,8 +247,7 @@ class TestDirtyBitmapBackingMigration(iotests.QMPTestCase): self.vm = iotests.VM() self.vm.launch() - result = self.vm.qmp('blockdev-add', **blockdev) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', blockdev) # Check that the bitmaps are there nodes = self.vm.qmp('query-named-block-nodes', flat=True)['return'] @@ -264,8 +256,7 @@ class TestDirtyBitmapBackingMigration(iotests.QMPTestCase): self.assert_qmp(node, 'dirty-bitmaps[0]/name', 'bmap0') caps = [{'capability': 'events', 'state': True}] - result = self.vm.qmp('migrate-set-capabilities', capabilities=caps) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('migrate-set-capabilities', capabilities=caps) def tearDown(self): self.vm.shutdown() @@ -276,14 +267,12 @@ class TestDirtyBitmapBackingMigration(iotests.QMPTestCase): """ Continue the source after migration. """ - result = self.vm.qmp('migrate', uri='exec: cat > /dev/null') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('migrate', uri='exec: cat > /dev/null') with Timeout(10, 'Migration timeout'): self.vm.wait_migration('postmigrate') - result = self.vm.qmp('cont') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('cont') def main() -> None: diff --git a/tests/qemu-iotests/tests/migrate-during-backup b/tests/qemu-iotests/tests/migrate-during-backup index 34103229ee..afb2277896 100755 --- a/tests/qemu-iotests/tests/migrate-during-backup +++ b/tests/qemu-iotests/tests/migrate-during-backup @@ -43,7 +43,7 @@ class TestMigrateDuringBackup(iotests.QMPTestCase): self.vm = iotests.VM().add_drive(disk_a) self.vm.launch() - result = self.vm.qmp('blockdev-add', { + self.vm.cmd('blockdev-add', { 'node-name': 'target', 'driver': iotests.imgfmt, 'file': { @@ -51,26 +51,21 @@ class TestMigrateDuringBackup(iotests.QMPTestCase): 'filename': disk_b } }) - self.assert_qmp(result, 'return', {}) def test_migrate(self): - result = self.vm.qmp('blockdev-backup', device='drive0', - target='target', sync='full', - speed=1, x_perf={ - 'max-workers': 1, - 'max-chunk': 64 * 1024 - }) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-backup', device='drive0', + target='target', sync='full', + speed=1, x_perf={ + 'max-workers': 1, + 'max-chunk': 64 * 1024 + }) - result = self.vm.qmp('job-pause', id='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('job-pause', id='drive0') - result = self.vm.qmp('migrate-set-capabilities', - capabilities=[{'capability': 'events', - 'state': True}]) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('migrate', uri=mig_cmd) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('migrate-set-capabilities', + capabilities=[{'capability': 'events', + 'state': True}]) + self.vm.cmd('migrate', uri=mig_cmd) e = self.vm.events_wait((('MIGRATION', {'data': {'status': 'completed'}}), @@ -80,11 +75,9 @@ class TestMigrateDuringBackup(iotests.QMPTestCase): # Don't assert that e is 'failed' now: this way we'll miss # possible crash when backup continues :) - result = self.vm.qmp('block-job-set-speed', device='drive0', - speed=0) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('job-resume', id='drive0') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-set-speed', device='drive0', + speed=0) + self.vm.cmd('job-resume', id='drive0') # For future: if something changes so that both migration # and backup pass, let's not miss that moment, as it may diff --git a/tests/qemu-iotests/tests/migration-permissions b/tests/qemu-iotests/tests/migration-permissions index 4e1da369c9..0deaad2d3a 100755 --- a/tests/qemu-iotests/tests/migration-permissions +++ b/tests/qemu-iotests/tests/migration-permissions @@ -47,11 +47,10 @@ class TestMigrationPermissions(iotests.QMPTestCase): vms[i].launch() - result = vms[i].qmp('migrate-set-capabilities', - capabilities=[ - {'capability': 'events', 'state': True} - ]) - self.assert_qmp(result, 'return', {}) + vms[i].cmd('migrate-set-capabilities', + capabilities=[ + {'capability': 'events', 'state': True} + ]) self.vm_s = vms[0] self.vm_d = vms[1] diff --git a/tests/qemu-iotests/tests/mirror-change-copy-mode b/tests/qemu-iotests/tests/mirror-change-copy-mode new file mode 100755 index 0000000000..51788b85c7 --- /dev/null +++ b/tests/qemu-iotests/tests/mirror-change-copy-mode @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +# group: rw +# +# Test for changing mirror copy mode from background to active +# +# Copyright (C) 2023 Proxmox Server Solutions 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 os +import time + +import iotests +from iotests import qemu_img, QemuStorageDaemon + +iops_target = 8 +iops_source = iops_target * 2 +image_size = 1 * 1024 * 1024 +source_img = os.path.join(iotests.test_dir, 'source.' + iotests.imgfmt) +target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt) +nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock') + +class TestMirrorChangeCopyMode(iotests.QMPTestCase): + + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, source_img, str(image_size)) + qemu_img('create', '-f', iotests.imgfmt, target_img, str(image_size)) + + self.qsd = QemuStorageDaemon('--nbd-server', + f'addr.type=unix,addr.path={nbd_sock}', + qmp=True) + + self.qsd.cmd('object-add', { + 'qom-type': 'throttle-group', + 'id': 'thrgr-target', + 'limits': { + 'iops-write': iops_target, + 'iops-write-max': iops_target + } + }) + + self.qsd.cmd('blockdev-add', { + 'node-name': 'target', + 'driver': 'throttle', + 'throttle-group': 'thrgr-target', + 'file': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': target_img + } + } + }) + + self.qsd.cmd('block-export-add', { + 'id': 'exp0', + 'type': 'nbd', + 'node-name': 'target', + 'writable': True + }) + + self.vm = iotests.VM() + self.vm.add_args('-drive', + f'file={source_img},if=none,format={iotests.imgfmt},' + f'iops_wr={iops_source},' + f'iops_wr_max={iops_source},' + 'id=source') + self.vm.launch() + + self.vm.cmd('blockdev-add', { + 'node-name': 'target', + 'driver': 'nbd', + 'export': 'target', + 'server': { + 'type': 'unix', + 'path': nbd_sock + } + }) + + + def tearDown(self): + self.vm.shutdown() + self.qsd.stop() + self.check_qemu_io_errors() + self.check_images_identical() + os.remove(source_img) + os.remove(target_img) + + # Once the VM is shut down we can parse the log and see if qemu-io ran + # without errors. + def check_qemu_io_errors(self): + self.assertFalse(self.vm.is_running()) + log = self.vm.get_log() + for line in log.split("\n"): + assert not line.startswith("Pattern verification failed") + + def check_images_identical(self): + qemu_img('compare', '-f', iotests.imgfmt, source_img, target_img) + + def start_mirror(self): + self.vm.cmd('blockdev-mirror', + job_id='mirror', + device='source', + target='target', + filter_node_name='mirror-top', + sync='full', + copy_mode='background') + + def test_background_to_active(self): + self.vm.hmp_qemu_io('source', f'write 0 {image_size}') + self.vm.hmp_qemu_io('target', f'write 0 {image_size}') + + self.start_mirror() + + result = self.vm.cmd('query-block-jobs') + assert not result[0]['actively-synced'] + + self.vm.event_wait('BLOCK_JOB_READY') + + result = self.vm.cmd('query-block-jobs') + assert not result[0]['actively-synced'] + + # Start some background requests. + reqs = 4 * iops_source + req_size = image_size // reqs + for i in range(0, reqs): + req = f'aio_write -P 7 {req_size * i} {req_size}' + self.vm.hmp_qemu_io('source', req) + + # Wait for the first few requests. + time.sleep(1) + self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') + + result = self.vm.cmd('query-block-jobs') + # There should've been new requests. + assert result[0]['len'] > image_size + # To verify later that not all requests were completed at this point. + len_before_change = result[0]['len'] + + # Change the copy mode while requests are happening. + self.vm.cmd('block-job-change', + id='mirror', + type='mirror', + copy_mode='write-blocking') + + # Wait until image is actively synced. + while True: + time.sleep(0.1) + self.vm.qtest(f'clock_step {100 * 1000 * 1000}') + result = self.vm.cmd('query-block-jobs') + if result[0]['actively-synced']: + break + + # Because of throttling, not all requests should have been completed + # above. + result = self.vm.cmd('query-block-jobs') + assert result[0]['len'] > len_before_change + + # Issue enough requests for a few seconds only touching the first half + # of the image. + reqs = 4 * iops_target + req_size = image_size // 2 // reqs + for i in range(0, reqs): + req = f'aio_write -P 19 {req_size * i} {req_size}' + self.vm.hmp_qemu_io('source', req) + + # Now issue a synchronous write in the second half of the image and + # immediately verify that it was written to the target too. This would + # fail without switching the copy mode. Note that this only produces a + # log line and the actual checking happens during tearDown(). + req_args = f'-P 37 {3 * (image_size // 4)} {req_size}' + self.vm.hmp_qemu_io('source', f'write {req_args}') + self.vm.hmp_qemu_io('target', f'read {req_args}') + + self.vm.cmd('block-job-cancel', device='mirror') + while len(self.vm.cmd('query-block-jobs')) > 0: + time.sleep(0.1) + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2', 'raw'], + supported_protocols=['file']) diff --git a/tests/qemu-iotests/tests/mirror-change-copy-mode.out b/tests/qemu-iotests/tests/mirror-change-copy-mode.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/tests/mirror-change-copy-mode.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK diff --git a/tests/qemu-iotests/tests/mirror-ready-cancel-error b/tests/qemu-iotests/tests/mirror-ready-cancel-error index 01217459b9..ed2e46447e 100755 --- a/tests/qemu-iotests/tests/mirror-ready-cancel-error +++ b/tests/qemu-iotests/tests/mirror-ready-cancel-error @@ -48,51 +48,48 @@ class TestMirrorReadyCancelError(iotests.QMPTestCase): os.remove(target) def add_blockdevs(self, once: bool) -> None: - res = self.vm.qmp('blockdev-add', - **{'node-name': 'source', - 'driver': iotests.imgfmt, - 'file': { - 'driver': 'file', - 'filename': source - }}) - self.assert_qmp(res, 'return', {}) + self.vm.cmd('blockdev-add', + {'node-name': 'source', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': source + }}) # blkdebug notes: # Enter state 2 on the first flush, which happens before the # job enters the READY state. The second flush will happen # when the job is about to complete, and we want that one to # fail. - res = self.vm.qmp('blockdev-add', - **{'node-name': 'target', - 'driver': iotests.imgfmt, - 'file': { - 'driver': 'blkdebug', - 'image': { - 'driver': 'file', - 'filename': target - }, - 'set-state': [{ - 'event': 'flush_to_disk', - 'state': 1, - 'new_state': 2 - }], - 'inject-error': [{ - 'event': 'flush_to_disk', - 'once': once, - 'immediately': True, - 'state': 2 - }]}}) - self.assert_qmp(res, 'return', {}) + self.vm.cmd('blockdev-add', + {'node-name': 'target', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'blkdebug', + 'image': { + 'driver': 'file', + 'filename': target + }, + 'set-state': [{ + 'event': 'flush_to_disk', + 'state': 1, + 'new_state': 2 + }], + 'inject-error': [{ + 'event': 'flush_to_disk', + 'once': once, + 'immediately': True, + 'state': 2 + }]}}) def start_mirror(self) -> None: - res = self.vm.qmp('blockdev-mirror', - job_id='mirror', - device='source', - target='target', - filter_node_name='mirror-top', - sync='full', - on_target_error='stop') - self.assert_qmp(res, 'return', {}) + self.vm.cmd('blockdev-mirror', + job_id='mirror', + device='source', + target='target', + filter_node_name='mirror-top', + sync='full', + on_target_error='stop') def cancel_mirror_with_error(self) -> None: self.vm.event_wait('BLOCK_JOB_READY') @@ -107,8 +104,7 @@ class TestMirrorReadyCancelError(iotests.QMPTestCase): while self.vm.event_wait('JOB_STATUS_CHANGE', timeout=0.0) is not None: pass - res = self.vm.qmp('block-job-cancel', device='mirror') - self.assert_qmp(res, 'return', {}) + self.vm.cmd('block-job-cancel', device='mirror') self.vm.event_wait('BLOCK_JOB_ERROR') diff --git a/tests/qemu-iotests/tests/mirror-top-perms b/tests/qemu-iotests/tests/mirror-top-perms index 8bca592708..fab9907e92 100755 --- a/tests/qemu-iotests/tests/mirror-top-perms +++ b/tests/qemu-iotests/tests/mirror-top-perms @@ -78,12 +78,11 @@ class TestMirrorTopPerms(iotests.QMPTestCase): difficult to let some other qemu process open the image.) """ - result = self.vm.qmp('blockdev-mirror', - job_id='mirror', - device='drive0', - target='null', - sync='full') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-mirror', + job_id='mirror', + device='drive0', + target='null', + sync='full') self.vm.event_wait('BLOCK_JOB_READY') @@ -105,9 +104,8 @@ class TestMirrorTopPerms(iotests.QMPTestCase): except machine.VMLaunchFailure as exc: assert 'Is another process using the image' in exc.output - result = self.vm.qmp('block-job-cancel', - device='mirror') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-cancel', + device='mirror') self.vm.event_wait('BLOCK_JOB_COMPLETED') diff --git a/tests/qemu-iotests/tests/nbd-multiconn b/tests/qemu-iotests/tests/nbd-multiconn index b121f2e363..479e872f2a 100755 --- a/tests/qemu-iotests/tests/nbd-multiconn +++ b/tests/qemu-iotests/tests/nbd-multiconn @@ -20,6 +20,8 @@ import os from contextlib import contextmanager +from types import ModuleType + import iotests from iotests import qemu_img_create, qemu_io @@ -28,7 +30,7 @@ disk = os.path.join(iotests.test_dir, 'disk') size = '4M' nbd_sock = os.path.join(iotests.sock_dir, 'nbd_sock') nbd_uri = 'nbd+unix:///{}?socket=' + nbd_sock - +nbd: ModuleType @contextmanager def open_nbd(export_name): @@ -46,12 +48,11 @@ class TestNbdMulticonn(iotests.QMPTestCase): self.vm = iotests.VM() self.vm.launch() - result = self.vm.qmp('blockdev-add', { + self.vm.cmd('blockdev-add', { 'driver': 'qcow2', 'node-name': 'n', 'file': {'driver': 'file', 'filename': disk} }) - self.assert_qmp(result, 'return', {}) def tearDown(self): self.vm.shutdown() @@ -72,12 +73,10 @@ class TestNbdMulticonn(iotests.QMPTestCase): if max_connections is not None: args['max-connections'] = max_connections - result = self.vm.qmp('nbd-server-start', args) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('nbd-server-start', args) yield - result = self.vm.qmp('nbd-server-stop') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('nbd-server-stop') def add_export(self, name, writable=None): args = { @@ -89,8 +88,7 @@ class TestNbdMulticonn(iotests.QMPTestCase): if writable is not None: args['writable'] = writable - result = self.vm.qmp('block-export-add', args) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-export-add', args) def test_default_settings(self): with self.run_server(): @@ -142,4 +140,4 @@ if __name__ == '__main__': iotests.main(supported_fmts=['qcow2']) except ImportError: - iotests.notrun('libnbd not installed') + iotests.notrun('Python bindings to libnbd are not installed') diff --git a/tests/qemu-iotests/tests/nbd-qemu-allocation.out b/tests/qemu-iotests/tests/nbd-qemu-allocation.out index 9d938db24e..56b57c69ed 100644 --- a/tests/qemu-iotests/tests/nbd-qemu-allocation.out +++ b/tests/qemu-iotests/tests/nbd-qemu-allocation.out @@ -11,22 +11,23 @@ wrote 2097152/2097152 bytes at offset 1048576 === Check allocation over NBD === -[{ "start": 0, "length": 1048576, "depth": 1, "present": true, "zero": false, "data": true, "offset": 327680}, -{ "start": 1048576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}, -{ "start": 3145728, "length": 1048576, "depth": 1, "present": false, "zero": true, "data": false}] +[{ "start": 0, "length": 1048576, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}, +{ "start": 1048576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}, +{ "start": 3145728, "length": 1048576, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] exports available: 1 export: '' size: 4194304 - flags: 0x48f ( readonly flush fua df cache ) + flags: 0x148f ( readonly flush fua df cache block-status-payload ) min block: 1 opt block: 4096 max block: 33554432 + transaction size: 64-bit available meta contexts: 2 base:allocation qemu:allocation-depth -[{ "start": 0, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 3145728, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] -[{ "start": 0, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": true, "offset": OFFSET}, -{ "start": 1048576, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}, -{ "start": 3145728, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 3145728, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] +[{ "start": 0, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 1048576, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, +{ "start": 3145728, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] *** done diff --git a/tests/qemu-iotests/tests/nbd-reconnect-on-open b/tests/qemu-iotests/tests/nbd-reconnect-on-open index d0b401b060..3ce52021c3 100755 --- a/tests/qemu-iotests/tests/nbd-reconnect-on-open +++ b/tests/qemu-iotests/tests/nbd-reconnect-on-open @@ -26,7 +26,8 @@ from iotests import qemu_img_create, file_path, qemu_io_popen, qemu_nbd, \ iotests.script_initialize(supported_fmts=['qcow2']) -disk, nbd_sock = file_path('disk', 'nbd-sock') +disk = file_path('disk') +nbd_sock = file_path('nbd-sock', base_dir=iotests.sock_dir) def create_args(open_timeout): diff --git a/tests/qemu-iotests/tests/nbd-reconnect-on-open.out b/tests/qemu-iotests/tests/nbd-reconnect-on-open.out index a35ae30ea4..b3dd90f2a3 100644 --- a/tests/qemu-iotests/tests/nbd-reconnect-on-open.out +++ b/tests/qemu-iotests/tests/nbd-reconnect-on-open.out @@ -2,10 +2,10 @@ read 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Check fail to connect with 0 seconds of timeout -qemu-io: can't open: Failed to connect to 'TEST_DIR/PID-nbd-sock': No such file or directory +qemu-io: can't open: Failed to connect to 'SOCK_DIR/PID-nbd-sock': No such file or directory qemu_io finished in 0..0.2 seconds, OK Check fail to connect with 1 seconds of timeout -qemu-io: can't open: Failed to connect to 'TEST_DIR/PID-nbd-sock': No such file or directory +qemu-io: can't open: Failed to connect to 'SOCK_DIR/PID-nbd-sock': No such file or directory qemu_io finished in 1..1.2 seconds, OK diff --git a/tests/qemu-iotests/tests/parallels-checks b/tests/qemu-iotests/tests/parallels-checks new file mode 100755 index 0000000000..b281246a42 --- /dev/null +++ b/tests/qemu-iotests/tests/parallels-checks @@ -0,0 +1,205 @@ +#!/usr/bin/env bash +# group: rw quick +# +# Test qemu-img check for parallels format +# +# Copyright (C) 2022 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 . +# + +# creator +owner=alexander.ivanov@virtuozzo.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 parallels +_supported_proto file +_supported_os Linux + +SIZE=$((4 * 1024 * 1024)) +IMGFMT=parallels +CLUSTER_SIZE_OFFSET=28 +DATA_OFF_OFFSET=48 +BAT_OFFSET=64 + +_make_test_img $SIZE + +CLUSTER_SIZE=$(peek_file_le $TEST_IMG $CLUSTER_SIZE_OFFSET 4) +CLUSTER_SIZE=$((CLUSTER_SIZE * 512)) +LAST_CLUSTER_OFF=$((SIZE - CLUSTER_SIZE)) +LAST_CLUSTER=$((LAST_CLUSTER_OFF/CLUSTER_SIZE)) + +echo "== TEST OUT OF IMAGE CHECK ==" + +echo "== write pattern ==" +{ $QEMU_IO -c "write -P 0x11 0 $SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== corrupt image ==" +cluster=$(($LAST_CLUSTER + 2)) +poke_file "$TEST_IMG" "$BAT_OFFSET" "\x$cluster\x00\x00\x00" + +echo "== read corrupted image with repairing ==" +{ $QEMU_IO -c "read -P 0x00 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +# Clear image +_make_test_img $SIZE + +echo "== TEST LEAK CHECK ==" + +echo "== write pattern to last cluster ==" +echo "write -P 0x11 $LAST_CLUSTER_OFF $CLUSTER_SIZE" +{ $QEMU_IO -c "write -P 0x11 $LAST_CLUSTER_OFF $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +file_size=`stat --printf="%s" "$TEST_IMG"` +echo "file size: $file_size" + +echo "== extend image by 1 cluster ==" +fallocate -xl $((file_size + CLUSTER_SIZE)) "$TEST_IMG" + +file_size=`stat --printf="%s" "$TEST_IMG"` +echo "file size: $file_size" + +echo "== repair image ==" +_check_test_img -r all + +file_size=`stat --printf="%s" "$TEST_IMG"` +echo "file size: $file_size" + +echo "== check last cluster ==" +{ $QEMU_IO -r -c "read -P 0x11 $LAST_CLUSTER_OFF $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +# Clear image +_make_test_img $SIZE + +echo "== TEST DUPLICATION CHECK ==" + +echo "== write pattern to whole image ==" +{ $QEMU_IO -c "write -P 0x11 0 $SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== write another pattern to second cluster ==" +{ $QEMU_IO -c "write -P 0x55 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check second cluster ==" +{ $QEMU_IO -r -c "read -P 0x55 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + + +echo "== corrupt image ==" +poke_file "$TEST_IMG" "$(($BAT_OFFSET + 4))" "\x01\x00\x00\x00" + +echo "== check second cluster ==" +{ $QEMU_IO -r -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== repair image ==" +_check_test_img -r all + +echo "== check the first cluster ==" +{ $QEMU_IO -r -c "read -P 0x11 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check second cluster ==" +{ $QEMU_IO -r -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== write another pattern to the first clusters ==" +{ $QEMU_IO -c "write -P 0x66 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check the first cluster ==" +{ $QEMU_IO -r -c "read -P 0x66 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check the second cluster (deduplicated) ==" +{ $QEMU_IO -r -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +# Clear image +_make_test_img $SIZE + +echo "== TEST DUPLICATION SELF-CURE ==" + +echo "== write pattern to whole image ==" +{ $QEMU_IO -c "write -P 0x11 0 $SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== write another pattern to second cluster ==" +{ $QEMU_IO -c "write -P 0x55 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check second cluster ==" +{ $QEMU_IO -r -c "read -P 0x55 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + + +echo "== corrupt image ==" +poke_file "$TEST_IMG" "$(($BAT_OFFSET + 4))" "\x01\x00\x00\x00" + +echo "== check second cluster ==" +{ $QEMU_IO -r -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check the first cluster with self-repair ==" +{ $QEMU_IO -c "read -P 0x11 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check second cluster ==" +{ $QEMU_IO -r -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== write another pattern to the first clusters ==" +{ $QEMU_IO -c "write -P 0x66 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check the first cluster ==" +{ $QEMU_IO -r -c "read -P 0x66 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check the second cluster (deduplicated) ==" +{ $QEMU_IO -r -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +# Clear image +_make_test_img $SIZE + +echo "== TEST DATA_OFF CHECK ==" + +echo "== write pattern to first cluster ==" +{ $QEMU_IO -c "write -P 0x55 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== spoil data_off field ==" +poke_file "$TEST_IMG" "$DATA_OFF_OFFSET" "\xff\xff\xff\xff" + +echo "== check first cluster ==" +{ $QEMU_IO -c "read -P 0x55 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +# Clear image +_make_test_img $SIZE + +echo "== TEST DATA_OFF THROUGH REPAIR ==" + +echo "== write pattern to first cluster ==" +{ $QEMU_IO -c "write -P 0x55 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== spoil data_off field ==" +poke_file "$TEST_IMG" "$DATA_OFF_OFFSET" "\xff\xff\xff\xff" + +echo "== repair image ==" +_check_test_img -r all + +echo "== check first cluster ==" +{ $QEMU_IO -r -c "read -P 0x55 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/parallels-checks.out b/tests/qemu-iotests/tests/parallels-checks.out new file mode 100644 index 0000000000..9793423111 --- /dev/null +++ b/tests/qemu-iotests/tests/parallels-checks.out @@ -0,0 +1,132 @@ +QA output created by parallels-checks +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +== TEST OUT OF IMAGE CHECK == +== write pattern == +wrote 4194304/4194304 bytes at offset 0 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== corrupt image == +== read corrupted image with repairing == +Repairing cluster 0 is outside image +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +== TEST LEAK CHECK == +== write pattern to last cluster == +write -P 0x11 3145728 1048576 +wrote 1048576/1048576 bytes at offset 3145728 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +file size: 2097152 +== extend image by 1 cluster == +file size: 3145728 +== repair image == +Repairing space leaked at the end of the image 1048576 +The following inconsistencies were found and repaired: + + 1 leaked clusters + 0 corruptions + +Double checking the fixed image now... +No errors were found on the image. +file size: 2097152 +== check last cluster == +read 1048576/1048576 bytes at offset 3145728 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +== TEST DUPLICATION CHECK == +== write pattern to whole image == +wrote 4194304/4194304 bytes at offset 0 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== write another pattern to second cluster == +wrote 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== check second cluster == +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== corrupt image == +== check second cluster == +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== repair image == +Repairing duplicate offset in BAT entry 1 +The following inconsistencies were found and repaired: + + 0 leaked clusters + 1 corruptions + +Double checking the fixed image now... +No errors were found on the image. +== check the first cluster == +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== check second cluster == +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== write another pattern to the first clusters == +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== check the first cluster == +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== check the second cluster (deduplicated) == +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +== TEST DUPLICATION SELF-CURE == +== write pattern to whole image == +wrote 4194304/4194304 bytes at offset 0 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== write another pattern to second cluster == +wrote 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== check second cluster == +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== corrupt image == +== check second cluster == +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== check the first cluster with self-repair == +Repairing duplicate offset in BAT entry 1 +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== check second cluster == +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== write another pattern to the first clusters == +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== check the first cluster == +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== check the second cluster (deduplicated) == +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +== TEST DATA_OFF CHECK == +== write pattern to first cluster == +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== spoil data_off field == +== check first cluster == +Repairing data_off field has incorrect value +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +== TEST DATA_OFF THROUGH REPAIR == +== write pattern to first cluster == +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== spoil data_off field == +== repair image == +Repairing data_off field has incorrect value +The following inconsistencies were found and repaired: + + 0 leaked clusters + 1 corruptions + +Double checking the fixed image now... +No errors were found on the image. +== check first cluster == +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/tests/qcow2-internal-snapshots b/tests/qemu-iotests/tests/qcow2-internal-snapshots new file mode 100755 index 0000000000..9f83aa8903 --- /dev/null +++ b/tests/qemu-iotests/tests/qcow2-internal-snapshots @@ -0,0 +1,170 @@ +#!/usr/bin/env bash +# group: rw quick +# +# Test case for internal snapshots in qcow2 +# +# Copyright (C) 2023 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ../common.rc +. ../common.filter + +# This tests qcow2-specific low-level functionality +_supported_fmt qcow2 +_supported_proto file +# 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 + +IMG_SIZE=64M + +_qemu() +{ + $QEMU -no-shutdown -nographic -monitor stdio -serial none \ + -blockdev file,filename="$TEST_IMG",node-name=disk0-file \ + -blockdev "$IMGFMT",file=disk0-file,node-name=disk0 \ + -object iothread,id=iothread0 \ + -device virtio-scsi,iothread=iothread0 \ + -device scsi-hd,drive=disk0,share-rw=on \ + "$@" 2>&1 |\ + _filter_qemu | _filter_hmp | _filter_qemu_io +} + +_make_test_img $IMG_SIZE + +echo +echo "=== Write some data, take a snapshot and overwrite part of it ===" +echo + +{ + echo 'qemu-io disk0 "write -P0x11 0 1M"' + # Give qemu some time to boot before saving the VM state + sleep 0.5 + echo "savevm snap0" + echo 'qemu-io disk0 "write -P0x22 0 512k"' + echo "quit" +} | _qemu + +echo +$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size +_check_test_img + +echo +echo "=== Verify that loading the snapshot reverts to the old content ===" +echo + +{ + # -loadvm reverted the write from the previous QEMU instance + echo 'qemu-io disk0 "read -P0x11 0 1M"' + + # Verify that it works without restarting QEMU, too + echo 'qemu-io disk0 "write -P0x33 512k 512k"' + echo "loadvm snap0" + echo 'qemu-io disk0 "read -P0x11 0 1M"' + + # Verify COW by writing a partial cluster + echo 'qemu-io disk0 "write -P0x33 63k 2k"' + echo 'qemu-io disk0 "read -P0x11 0 63k"' + echo 'qemu-io disk0 "read -P0x33 63k 2k"' + echo 'qemu-io disk0 "read -P0x11 65k 63k"' + + # Take a second snapshot + echo "savevm snap1" + + echo "quit" +} | _qemu -loadvm snap0 + +echo +$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size +_check_test_img + +echo +echo "=== qemu-img snapshot can revert to snapshots ===" +echo + +$QEMU_IMG snapshot -a snap0 "$TEST_IMG" +$QEMU_IO -c "read -P0x11 0 1M" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG snapshot -a snap1 "$TEST_IMG" +$QEMU_IO \ + -c "read -P0x11 0 63k" \ + -c "read -P0x33 63k 2k" \ + -c "read -P0x11 65k 63k" \ + "$TEST_IMG" | _filter_qemu_io + +echo +echo "=== Deleting snapshots ===" +echo +{ + # The active layer stays unaffected by deleting the snapshot + echo "delvm snap1" + echo 'qemu-io disk0 "read -P0x11 0 63k"' + echo 'qemu-io disk0 "read -P0x33 63k 2k"' + echo 'qemu-io disk0 "read -P0x11 65k 63k"' + + echo "quit" +} | _qemu + + +echo +$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size +_check_test_img + +echo +echo "=== Error cases ===" +echo + +# snap1 should not exist any more +_qemu -loadvm snap1 + +echo +{ + echo "loadvm snap1" + echo "quit" +} | _qemu + +# Snapshot operations and inactive images are incompatible +echo +_qemu -loadvm snap0 -incoming defer +{ + echo "loadvm snap0" + echo "delvm snap0" + echo "savevm snap1" + echo "quit" +} | _qemu -incoming defer + +# -loadvm and -preconfig are incompatible +echo +_qemu -loadvm snap0 -preconfig + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/qcow2-internal-snapshots.out b/tests/qemu-iotests/tests/qcow2-internal-snapshots.out new file mode 100644 index 0000000000..fedb09224e --- /dev/null +++ b/tests/qemu-iotests/tests/qcow2-internal-snapshots.out @@ -0,0 +1,107 @@ +QA output created by qcow2-internal-snapshots +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + +=== Write some data, take a snapshot and overwrite part of it === + +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io disk0 "write -P0x11 0 1M" +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) savevm snap0 +(qemu) qemu-io disk0 "write -P0x22 0 512k" +wrote 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) quit + +Snapshot list: +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +1 snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- +No errors were found on the image. + +=== Verify that loading the snapshot reverts to the old content === + +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io disk0 "read -P0x11 0 1M" +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qemu-io disk0 "write -P0x33 512k 512k" +wrote 524288/524288 bytes at offset 524288 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) loadvm snap0 +(qemu) qemu-io disk0 "read -P0x11 0 1M" +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qemu-io disk0 "write -P0x33 63k 2k" +wrote 2048/2048 bytes at offset 64512 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qemu-io disk0 "read -P0x11 0 63k" +read 64512/64512 bytes at offset 0 +63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qemu-io disk0 "read -P0x33 63k 2k" +read 2048/2048 bytes at offset 64512 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qemu-io disk0 "read -P0x11 65k 63k" +read 64512/64512 bytes at offset 66560 +63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) savevm snap1 +(qemu) quit + +Snapshot list: +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +1 snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- +2 snap1 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- +No errors were found on the image. + +=== qemu-img snapshot can revert to snapshots === + +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 64512/64512 bytes at offset 0 +63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2048/2048 bytes at offset 64512 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 64512/64512 bytes at offset 66560 +63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Deleting snapshots === + +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) delvm snap1 +(qemu) qemu-io disk0 "read -P0x11 0 63k" +read 64512/64512 bytes at offset 0 +63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qemu-io disk0 "read -P0x33 63k 2k" +read 2048/2048 bytes at offset 64512 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) qemu-io disk0 "read -P0x11 65k 63k" +read 64512/64512 bytes at offset 66560 +63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) quit + +Snapshot list: +ID TAG VM_SIZE DATE VM_CLOCK ICOUNT +1 snap0 SIZE yyyy-mm-dd hh:mm:ss 0000:00:00.000 -- +No errors were found on the image. + +=== Error cases === + +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) QEMU_PROG: Snapshot 'snap1' does not exist in one or more devices + +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) loadvm snap1 +Error: Snapshot 'snap1' does not exist in one or more devices +(qemu) quit + +QEMU_PROG: 'incoming' and 'loadvm' options are mutually exclusive +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) loadvm snap0 +Error: Device 'disk0' is writable but does not support snapshots +(qemu) delvm snap0 +Error: Device 'disk0' is writable but does not support snapshots +(qemu) savevm snap1 +Error: Device 'disk0' is writable but does not support snapshots +(qemu) quit + +QEMU_PROG: 'preconfig' and 'loadvm' options are mutually exclusive +*** done diff --git a/tests/qemu-iotests/tests/qemu-img-bitmaps.out b/tests/qemu-iotests/tests/qemu-img-bitmaps.out index e851f0320e..74b81f703b 100644 --- a/tests/qemu-iotests/tests/qemu-img-bitmaps.out +++ b/tests/qemu-iotests/tests/qemu-img-bitmaps.out @@ -103,18 +103,18 @@ Format specific information: === Check bitmap contents === -[{ "start": 0, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 3145728, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false}, -{ "start": 4194304, "length": 6291456, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] -[{ "start": 0, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 1048576, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false}, -{ "start": 2097152, "length": 8388608, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] -[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 2097152, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false}, -{ "start": 3145728, "length": 7340032, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] -[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 2097152, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false}, -{ "start": 3145728, "length": 7340032, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 3145728, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, +{ "start": 4194304, "length": 6291456, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] +[{ "start": 0, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 1048576, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, +{ "start": 2097152, "length": 8388608, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] +[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 2097152, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, +{ "start": 3145728, "length": 7340032, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] +[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 2097152, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, +{ "start": 3145728, "length": 7340032, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] === Check handling of inconsistent bitmap === diff --git a/tests/qemu-iotests/tests/qemu-img-close-errors b/tests/qemu-iotests/tests/qemu-img-close-errors new file mode 100755 index 0000000000..50bfb6cfa2 --- /dev/null +++ b/tests/qemu-iotests/tests/qemu-img-close-errors @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# group: rw auto quick +# +# Check that errors while closing the image, in particular writing back dirty +# bitmaps, is correctly reported with a failing qemu-img exit code. +# +# Copyright (C) 2023 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +size=1G + +# The error we are going to use is ENOSPC. Depending on how many bitmaps we +# create in the backing file (and therefore increase the used up space), we get +# failures in different places. With a low number, only merging the bitmap +# fails, whereas with a higher number, already 'qemu-img commit' fails. +for max_bitmap in 6 7; do + echo + echo "=== Test with $max_bitmap bitmaps ===" + + TEST_IMG="$TEST_IMG.base" _make_test_img -q $size + for i in $(seq 1 $max_bitmap); do + $QEMU_IMG bitmap --add "$TEST_IMG.base" "stale-bitmap-$i" + done + + # Simulate a block device of 128 MB by resizing the image file accordingly + # and then enforcing the size with the raw driver + $QEMU_IO -f raw -c "truncate 128M" "$TEST_IMG.base" + BASE_JSON='json:{ + "driver": "qcow2", + "file": { + "driver": "raw", + "size": 134217728, + "file": { + "driver": "file", + "filename":"'"$TEST_IMG.base"'" + } + } + }' + + _make_test_img -q -b "$BASE_JSON" -F $IMGFMT + $QEMU_IMG bitmap --add "$TEST_IMG" "good-bitmap" + + $QEMU_IO -c 'write 0 126m' "$TEST_IMG" | _filter_qemu_io + + $QEMU_IMG commit -d "$TEST_IMG" 2>&1 | _filter_generated_node_ids + echo "qemu-img commit exit code: ${PIPESTATUS[0]}" + + $QEMU_IMG bitmap --add "$BASE_JSON" "good-bitmap" + echo "qemu-img bitmap --add exit code: $?" + + $QEMU_IMG bitmap --merge "good-bitmap" -b "$TEST_IMG" "$BASE_JSON" \ + "good-bitmap" 2>&1 | _filter_generated_node_ids + echo "qemu-img bitmap --merge exit code: ${PIPESTATUS[0]}" +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 + diff --git a/tests/qemu-iotests/tests/qemu-img-close-errors.out b/tests/qemu-iotests/tests/qemu-img-close-errors.out new file mode 100644 index 0000000000..1bfe88f176 --- /dev/null +++ b/tests/qemu-iotests/tests/qemu-img-close-errors.out @@ -0,0 +1,23 @@ +QA output created by qemu-img-close-errors + +=== Test with 6 bitmaps === +wrote 132120576/132120576 bytes at offset 0 +126 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Image committed. +qemu-img commit exit code: 0 +qemu-img bitmap --add exit code: 0 +qemu-img: Lost persistent bitmaps during inactivation of node 'NODE_NAME': Failed to write bitmap 'good-bitmap' to file: No space left on device +qemu-img: Error while closing the image: Invalid argument +qemu-img: Lost persistent bitmaps during inactivation of node 'NODE_NAME': Failed to write bitmap 'good-bitmap' to file: No space left on device +qemu-img bitmap --merge exit code: 1 + +=== Test with 7 bitmaps === +wrote 132120576/132120576 bytes at offset 0 +126 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Lost persistent bitmaps during inactivation of node 'NODE_NAME': Failed to write bitmap 'stale-bitmap-7' to file: No space left on device +qemu-img: Lost persistent bitmaps during inactivation of node 'NODE_NAME': Failed to write bitmap 'stale-bitmap-7' to file: No space left on device +qemu-img: Error while closing the image: Invalid argument +qemu-img commit exit code: 1 +qemu-img bitmap --add exit code: 0 +qemu-img bitmap --merge exit code: 0 +*** done diff --git a/tests/qemu-iotests/tests/qsd-jobs b/tests/qemu-iotests/tests/qsd-jobs index 510bf0a9dc..9b843af631 100755 --- a/tests/qemu-iotests/tests/qsd-jobs +++ b/tests/qemu-iotests/tests/qsd-jobs @@ -40,7 +40,7 @@ cd .. . ./common.filter _supported_fmt qcow2 -_supported_proto generic +_supported_proto file size=128M diff --git a/tests/qemu-iotests/tests/qsd-jobs.out b/tests/qemu-iotests/tests/qsd-jobs.out index c1bc9b8356..aa6b6d1aef 100644 --- a/tests/qemu-iotests/tests/qsd-jobs.out +++ b/tests/qemu-iotests/tests/qsd-jobs.out @@ -7,8 +7,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/ QMP_VERSION {"return": {}} {"return": {}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} +{"return": {}} === Streaming can't get permission on base node === @@ -17,6 +17,6 @@ QMP_VERSION {"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": "null", "id": "job0"}} {"error": {"class": "GenericError", "desc": "Permission conflict on node 'fmt_base': permissions 'write' are both required by an unnamed block device (uses node 'fmt_base' as 'root' child) and unshared by stream job 'job0' (uses node 'fmt_base' as 'intermediate node' child)."}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export1"}} +{"return": {}} *** done diff --git a/tests/qemu-iotests/tests/regression-vhdx-log b/tests/qemu-iotests/tests/regression-vhdx-log new file mode 100755 index 0000000000..ca264e93d6 --- /dev/null +++ b/tests/qemu-iotests/tests/regression-vhdx-log @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# group: rw auto quick +# +# vhdx regression test: Updating the first entry of a BAT sector corrupted the +# following entries. +# +# Copyright (C) 2023 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter + +_supported_fmt generic +_supported_proto generic +_unsupported_imgopts "subformat=streamOptimized" + +size=64M +_make_test_img $size + +echo +echo "creating pattern" +$QEMU_IO -c "write -P 1 32M 4k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 2 0 4k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -P 1 32M 4k" "$TEST_IMG" | _filter_qemu_io + +echo +echo "checking image for errors" +_check_test_img + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/regression-vhdx-log.out b/tests/qemu-iotests/tests/regression-vhdx-log.out new file mode 100644 index 0000000000..350c257354 --- /dev/null +++ b/tests/qemu-iotests/tests/regression-vhdx-log.out @@ -0,0 +1,14 @@ +QA output created by regression-vhdx-log +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + +creating pattern +wrote 4096/4096 bytes at offset 33554432 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 33554432 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +checking image for errors +No errors were found on the image. +*** done diff --git a/tests/qemu-iotests/tests/reopen-file b/tests/qemu-iotests/tests/reopen-file index 8590a94d53..5a50794ffc 100755 --- a/tests/qemu-iotests/tests/reopen-file +++ b/tests/qemu-iotests/tests/reopen-file @@ -64,12 +64,11 @@ class TestReopenFile(QMPTestCase): self.fail('qemu-io pattern verification failed') def test_reopen_file(self) -> None: - result = self.vm.qmp('blockdev-reopen', options=[{ + self.vm.cmd('blockdev-reopen', options=[{ 'driver': imgfmt, 'node-name': 'format', 'file': 'raw' }]) - self.assert_qmp(result, 'return', {}) # Do some I/O to the image to see whether it still works # (Pattern verification will be checked by tearDown()) diff --git a/tests/qemu-iotests/tests/stream-error-on-reset b/tests/qemu-iotests/tests/stream-error-on-reset index 5a8c3a9e8d..b60aabb68e 100755 --- a/tests/qemu-iotests/tests/stream-error-on-reset +++ b/tests/qemu-iotests/tests/stream-error-on-reset @@ -115,8 +115,7 @@ class TestStreamErrorOnReset(QMPTestCase): # Launch a stream job, which will take at least a second to # complete, because the base image is throttled (so we can # get in between it having started and it having completed) - res = self.vm.qmp('block-stream', job_id='stream', device='top') - self.assert_qmp(res, 'return', {}) + self.vm.cmd('block-stream', job_id='stream', device='top') while True: ev = self.vm.event_wait('JOB_STATUS_CHANGE') @@ -125,8 +124,7 @@ class TestStreamErrorOnReset(QMPTestCase): # forces the virtio-scsi device to be reset, thus draining # the stream job, and making it complete. Completing # inside of that drain should not result in a segfault. - res = self.vm.qmp('system_reset') - self.assert_qmp(res, 'return', {}) + self.vm.cmd('system_reset') elif ev['data']['status'] == 'null': # The test is done once the job is gone break diff --git a/tests/qemu-iotests/tests/stream-unaligned-prefetch b/tests/qemu-iotests/tests/stream-unaligned-prefetch new file mode 100755 index 0000000000..546db1d369 --- /dev/null +++ b/tests/qemu-iotests/tests/stream-unaligned-prefetch @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +# group: rw quick +# +# Test what happens when a stream job does an unaligned prefetch read +# which requires padding while having a NULL qiov. +# +# Copyright (C) Proxmox Server Solutions 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 os +import iotests +from iotests import imgfmt, qemu_img_create, qemu_io, QMPTestCase + +image_size = 1 * 1024 * 1024 +cluster_size = 64 * 1024 +base = os.path.join(iotests.test_dir, 'base.img') +top = os.path.join(iotests.test_dir, 'top.img') + +class TestStreamUnalignedPrefetch(QMPTestCase): + def setUp(self) -> None: + """ + Create two images: + - base image {base} with {cluster_size // 2} bytes allocated + - top image {top} without any data allocated and coarser + cluster size + + Attach a compress filter for the top image, because that + requires that the request alignment is the top image's cluster + size. + """ + qemu_img_create('-f', imgfmt, + '-o', 'cluster_size={}'.format(cluster_size // 2), + base, str(image_size)) + qemu_io('-c', f'write 0 {cluster_size // 2}', base) + qemu_img_create('-f', imgfmt, + '-o', 'cluster_size={}'.format(cluster_size), + top, str(image_size)) + + self.vm = iotests.VM() + self.vm.add_blockdev(self.vm.qmp_to_opts({ + 'driver': imgfmt, + 'node-name': 'base', + 'file': { + 'driver': 'file', + 'filename': base + } + })) + self.vm.add_blockdev(self.vm.qmp_to_opts({ + 'driver': 'compress', + 'node-name': 'compress-top', + 'file': { + 'driver': imgfmt, + 'node-name': 'top', + 'file': { + 'driver': 'file', + 'filename': top + }, + 'backing': 'base' + } + })) + self.vm.launch() + + def tearDown(self) -> None: + self.vm.shutdown() + os.remove(top) + os.remove(base) + + def test_stream_unaligned_prefetch(self) -> None: + self.vm.cmd('block-stream', job_id='stream', device='compress-top') + + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2'], supported_protocols=['file']) diff --git a/tests/qemu-iotests/tests/stream-unaligned-prefetch.out b/tests/qemu-iotests/tests/stream-unaligned-prefetch.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/tests/stream-unaligned-prefetch.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK diff --git a/tests/qemu-iotests/tests/stream-under-throttle b/tests/qemu-iotests/tests/stream-under-throttle new file mode 100755 index 0000000000..1a50b682fc --- /dev/null +++ b/tests/qemu-iotests/tests/stream-under-throttle @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +# group: rw +# +# Test streaming with throttle nodes on top +# +# Copyright (C) 2022 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import asyncio +import os +from typing import List +import iotests +from iotests import qemu_img_create, qemu_io + + +image_size = 256 * 1024 * 1024 +base_img = os.path.join(iotests.test_dir, 'base.img') +top_img = os.path.join(iotests.test_dir, 'top.img') + + +class TcgVM(iotests.VM): + ''' + Variant of iotests.VM that uses -accel tcg. Simply using + iotests.VM.add_args('-accel', 'tcg') is not sufficient, because that will + put -accel qtest before -accel tcg, and -accel arguments are prioritized in + the order they appear. + ''' + @property + def _base_args(self) -> List[str]: + # Put -accel tcg first so it takes precedence + return ['-accel', 'tcg'] + super()._base_args + + +class TestStreamWithThrottle(iotests.QMPTestCase): + def setUp(self) -> None: + ''' + Create a simple backing chain between two images, write something to + the base image. Attach them to the VM underneath two throttle nodes, + one of which has actually no limits set, but the other does. Then put + a virtio-blk device on top. + This test configuration has been taken from + https://gitlab.com/qemu-project/qemu/-/issues/1215 + ''' + qemu_img_create('-f', iotests.imgfmt, base_img, str(image_size)) + qemu_img_create('-f', iotests.imgfmt, '-b', base_img, '-F', + iotests.imgfmt, top_img, str(image_size)) + + # Write something to stream + qemu_io(base_img, '-c', f'write 0 {image_size}') + + blockdev = { + 'driver': 'throttle', + 'node-name': 'throttled-node', + 'throttle-group': 'thrgr-limited', + 'file': { + 'driver': 'throttle', + 'throttle-group': 'thrgr-unlimited', + 'file': { + 'driver': iotests.imgfmt, + 'node-name': 'unthrottled-node', + 'file': { + 'driver': 'file', + 'filename': top_img + } + } + } + } + + # Issue 1215 is not reproducible in qtest mode, which is why we need to + # create an -accel tcg VM + self.vm = TcgVM() + self.vm.add_object('iothread,id=iothr0') + self.vm.add_object('throttle-group,id=thrgr-unlimited') + self.vm.add_object('throttle-group,id=thrgr-limited,' + 'x-iops-total=10000,x-bps-total=104857600') + self.vm.add_blockdev(self.vm.qmp_to_opts(blockdev)) + self.vm.add_device('virtio-blk,iothread=iothr0,drive=throttled-node') + if iotests.qemu_default_machine == 's390-ccw-virtio': + self.vm.add_args('-no-shutdown') + self.vm.launch() + + def tearDown(self) -> None: + self.vm.shutdown() + os.remove(top_img) + os.remove(base_img) + + def test_stream(self) -> None: + ''' + Do a simple stream beneath the two throttle nodes. Should complete + with no problems. + ''' + self.vm.cmd('block-stream', + job_id='stream', + device='unthrottled-node') + + # Should succeed and not time out + try: + self.vm.run_job('stream') + except asyncio.TimeoutError: + # VM may be stuck, kill it before tearDown() + self.vm.kill() + raise + + +if __name__ == '__main__': + # Must support backing images + iotests.main(supported_fmts=['qcow', 'qcow2', 'qed'], + supported_protocols=['file'], + required_fmts=['throttle']) diff --git a/tests/qemu-iotests/tests/stream-under-throttle.out b/tests/qemu-iotests/tests/stream-under-throttle.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/tests/stream-under-throttle.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK diff --git a/tests/qemu-iotests/tests/zoned b/tests/qemu-iotests/tests/zoned new file mode 100755 index 0000000000..3d23ce9cc1 --- /dev/null +++ b/tests/qemu-iotests/tests/zoned @@ -0,0 +1,105 @@ +#!/usr/bin/env bash +# +# Test zone management operations. +# + +seq="$(basename $0)" +echo "QA output created by $seq" +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + sudo -n rmmod null_blk +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ../common.rc +. ../common.filter +. ../common.qemu + +# This test only runs on Linux hosts with raw image files. +_supported_fmt raw +_supported_proto file +_supported_os Linux + +sudo -n true || \ + _notrun 'Password-less sudo required' + +IMG="--image-opts -n driver=host_device,filename=/dev/nullb0" +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT + +echo "Testing a null_blk device:" +echo "case 1: if the operations work" +sudo -n modprobe null_blk nr_devices=1 zoned=1 +sudo -n chmod 0666 /dev/nullb0 + +echo "(1) report the first zone:" +$QEMU_IO $IMG -c "zrp 0 1" +echo +echo "report the first 10 zones" +$QEMU_IO $IMG -c "zrp 0 10" +echo +echo "report the last zone:" +$QEMU_IO $IMG -c "zrp 0x3e70000000 2" # 0x3e70000000 / 512 = 0x1f380000 +echo +echo +echo "(2) opening the first zone" +$QEMU_IO $IMG -c "zo 0 268435456" # 268435456 / 512 = 524288 +echo "report after:" +$QEMU_IO $IMG -c "zrp 0 1" +echo +echo "opening the second zone" +$QEMU_IO $IMG -c "zo 268435456 268435456" # +echo "report after:" +$QEMU_IO $IMG -c "zrp 268435456 1" +echo +echo "opening the last zone" +$QEMU_IO $IMG -c "zo 0x3e70000000 268435456" +echo "report after:" +$QEMU_IO $IMG -c "zrp 0x3e70000000 2" +echo +echo +echo "(3) closing the first zone" +$QEMU_IO $IMG -c "zc 0 268435456" +echo "report after:" +$QEMU_IO $IMG -c "zrp 0 1" +echo +echo "closing the last zone" +$QEMU_IO $IMG -c "zc 0x3e70000000 268435456" +echo "report after:" +$QEMU_IO $IMG -c "zrp 0x3e70000000 2" +echo +echo +echo "(4) finishing the second zone" +$QEMU_IO $IMG -c "zf 268435456 268435456" +echo "After finishing a zone:" +$QEMU_IO $IMG -c "zrp 268435456 1" +echo +echo +echo "(5) resetting the second zone" +$QEMU_IO $IMG -c "zrs 268435456 268435456" +echo "After resetting a zone:" +$QEMU_IO $IMG -c "zrp 268435456 1" +echo +echo +echo "(6) append write" # the physical block size of the device is 4096 +$QEMU_IO $IMG -c "zrp 0 1" +$QEMU_IO $IMG -c "zap -p 0 0x1000 0x2000" +echo "After appending the first zone firstly:" +$QEMU_IO $IMG -c "zrp 0 1" +$QEMU_IO $IMG -c "zap -p 0 0x1000 0x2000" +echo "After appending the first zone secondly:" +$QEMU_IO $IMG -c "zrp 0 1" +$QEMU_IO $IMG -c "zap -p 268435456 0x1000 0x2000" +echo "After appending the second zone firstly:" +$QEMU_IO $IMG -c "zrp 268435456 1" +$QEMU_IO $IMG -c "zap -p 268435456 0x1000 0x2000" +echo "After appending the second zone secondly:" +$QEMU_IO $IMG -c "zrp 268435456 1" + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/zoned.out b/tests/qemu-iotests/tests/zoned.out new file mode 100644 index 0000000000..fe53ba4744 --- /dev/null +++ b/tests/qemu-iotests/tests/zoned.out @@ -0,0 +1,69 @@ +QA output created by zoned +Testing a null_blk device: +case 1: if the operations work +(1) report the first zone: +start: 0x0, len 0x80000, cap 0x80000, wptr 0x0, zcond:1, [type: 2] + +report the first 10 zones +start: 0x0, len 0x80000, cap 0x80000, wptr 0x0, zcond:1, [type: 2] +start: 0x80000, len 0x80000, cap 0x80000, wptr 0x80000, zcond:1, [type: 2] +start: 0x100000, len 0x80000, cap 0x80000, wptr 0x100000, zcond:1, [type: 2] +start: 0x180000, len 0x80000, cap 0x80000, wptr 0x180000, zcond:1, [type: 2] +start: 0x200000, len 0x80000, cap 0x80000, wptr 0x200000, zcond:1, [type: 2] +start: 0x280000, len 0x80000, cap 0x80000, wptr 0x280000, zcond:1, [type: 2] +start: 0x300000, len 0x80000, cap 0x80000, wptr 0x300000, zcond:1, [type: 2] +start: 0x380000, len 0x80000, cap 0x80000, wptr 0x380000, zcond:1, [type: 2] +start: 0x400000, len 0x80000, cap 0x80000, wptr 0x400000, zcond:1, [type: 2] +start: 0x480000, len 0x80000, cap 0x80000, wptr 0x480000, zcond:1, [type: 2] + +report the last zone: +start: 0x1f380000, len 0x80000, cap 0x80000, wptr 0x1f380000, zcond:1, [type: 2] + + +(2) opening the first zone +report after: +start: 0x0, len 0x80000, cap 0x80000, wptr 0x0, zcond:3, [type: 2] + +opening the second zone +report after: +start: 0x80000, len 0x80000, cap 0x80000, wptr 0x80000, zcond:3, [type: 2] + +opening the last zone +report after: +start: 0x1f380000, len 0x80000, cap 0x80000, wptr 0x1f380000, zcond:3, [type: 2] + + +(3) closing the first zone +report after: +start: 0x0, len 0x80000, cap 0x80000, wptr 0x0, zcond:1, [type: 2] + +closing the last zone +report after: +start: 0x1f380000, len 0x80000, cap 0x80000, wptr 0x1f380000, zcond:1, [type: 2] + + +(4) finishing the second zone +After finishing a zone: +start: 0x80000, len 0x80000, cap 0x80000, wptr 0x100000, zcond:14, [type: 2] + + +(5) resetting the second zone +After resetting a zone: +start: 0x80000, len 0x80000, cap 0x80000, wptr 0x80000, zcond:1, [type: 2] + + +(6) append write +start: 0x0, len 0x80000, cap 0x80000, wptr 0x0, zcond:1, [type: 2] +After zap done, the append sector is 0x0 +After appending the first zone firstly: +start: 0x0, len 0x80000, cap 0x80000, wptr 0x18, zcond:2, [type: 2] +After zap done, the append sector is 0x18 +After appending the first zone secondly: +start: 0x0, len 0x80000, cap 0x80000, wptr 0x30, zcond:2, [type: 2] +After zap done, the append sector is 0x80000 +After appending the second zone firstly: +start: 0x80000, len 0x80000, cap 0x80000, wptr 0x80018, zcond:2, [type: 2] +After zap done, the append sector is 0x80018 +After appending the second zone secondly: +start: 0x80000, len 0x80000, cap 0x80000, wptr 0x80030, zcond:2, [type: 2] +*** done diff --git a/tests/qtest/ac97-test.c b/tests/qtest/ac97-test.c index b084e31bff..b71bd60a8a 100644 --- a/tests/qtest/ac97-test.c +++ b/tests/qtest/ac97-test.c @@ -28,7 +28,7 @@ static void *ac97_get_driver(void *obj, const char *interface) return &ac97->dev; } - fprintf(stderr, "%s not present in e1000e\n", interface); + fprintf(stderr, "%s not present in ac97\n", interface); g_assert_not_reached(); } @@ -42,16 +42,54 @@ static void *ac97_create(void *pci_bus, QGuestAllocator *alloc, void *addr) return &ac97->obj; } +/* + * This is rather a test of the audio subsystem and not an AC97 test. Test if + * the audio subsystem can handle a 44100/1 upsample ratio. For some time this + * used to trigger QEMU aborts. + */ +static void ac97_playback_upsample(void *obj, void *data, QGuestAllocator *alloc) +{ + QAC97 *ac97 = obj; + QPCIDevice *dev = &ac97->dev; + QPCIBar bar0; + + qpci_device_enable(dev); + bar0 = qpci_iomap(dev, 0, NULL); + /* IOBAR0 offset 0x2c: PCM Front DAC Rate */ + qpci_io_writew(dev, bar0, 0x2c, 0x1); +} + +/* + * This test is similar to the playback upsample test. QEMU shouldn't abort if + * asked for a 1/44100 downsample ratio. + */ +static void ac97_record_downsample(void *obj, void *data, QGuestAllocator *alloc) +{ + QAC97 *ac97 = obj; + QPCIDevice *dev = &ac97->dev; + QPCIBar bar0; + + qpci_device_enable(dev); + bar0 = qpci_iomap(dev, 0, NULL); + /* IOBAR0 offset 0x32: PCM L/R ADC Rate */ + qpci_io_writew(dev, bar0, 0x32, 0x1); +} + static void ac97_register_nodes(void) { QOSGraphEdgeOptions opts = { - .extra_device_opts = "addr=04.0", + .extra_device_opts = "addr=04.0,audiodev=snd0", + .after_cmd_line = "-audiodev none,id=snd0" + ",out.frequency=44100,in.frequency=44100", }; add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); qos_node_create_driver("AC97", ac97_create); qos_node_produces("AC97", "pci-device"); qos_node_consumes("AC97", "pci-bus", &opts); + + qos_add_test("playback_upsample", "AC97", ac97_playback_upsample, NULL); + qos_add_test("record_downsample", "AC97", ac97_record_downsample, NULL); } libqos_init(ac97_register_nodes); diff --git a/tests/qtest/adm1266-test.c b/tests/qtest/adm1266-test.c new file mode 100644 index 0000000000..6c312c499f --- /dev/null +++ b/tests/qtest/adm1266-test.c @@ -0,0 +1,122 @@ +/* + * Analog Devices ADM1266 Cascadable Super Sequencer with Margin Control and + * Fault Recording with PMBus + * + * Copyright 2022 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include +#include "hw/i2c/pmbus_device.h" +#include "libqtest-single.h" +#include "libqos/qgraph.h" +#include "libqos/i2c.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qnum.h" +#include "qemu/bitops.h" + +#define TEST_ID "adm1266-test" +#define TEST_ADDR (0x12) + +#define ADM1266_BLACKBOX_CONFIG 0xD3 +#define ADM1266_PDIO_CONFIG 0xD4 +#define ADM1266_READ_STATE 0xD9 +#define ADM1266_READ_BLACKBOX 0xDE +#define ADM1266_SET_RTC 0xDF +#define ADM1266_GPIO_SYNC_CONFIGURATION 0xE1 +#define ADM1266_BLACKBOX_INFORMATION 0xE6 +#define ADM1266_PDIO_STATUS 0xE9 +#define ADM1266_GPIO_STATUS 0xEA + +/* Defaults */ +#define ADM1266_OPERATION_DEFAULT 0x80 +#define ADM1266_CAPABILITY_DEFAULT 0xA0 +#define ADM1266_CAPABILITY_NO_PEC 0x20 +#define ADM1266_PMBUS_REVISION_DEFAULT 0x22 +#define ADM1266_MFR_ID_DEFAULT "ADI" +#define ADM1266_MFR_ID_DEFAULT_LEN 32 +#define ADM1266_MFR_MODEL_DEFAULT "ADM1266-A1" +#define ADM1266_MFR_MODEL_DEFAULT_LEN 32 +#define ADM1266_MFR_REVISION_DEFAULT "25" +#define ADM1266_MFR_REVISION_DEFAULT_LEN 8 +#define TEST_STRING_A "a sample" +#define TEST_STRING_B "b sample" +#define TEST_STRING_C "rev c" + +static void compare_string(QI2CDevice *i2cdev, uint8_t reg, + const char *test_str) +{ + uint8_t len = i2c_get8(i2cdev, reg); + char i2c_str[SMBUS_DATA_MAX_LEN] = {0}; + + i2c_read_block(i2cdev, reg, (uint8_t *)i2c_str, len); + g_assert_cmpstr(i2c_str, ==, test_str); +} + +static void write_and_compare_string(QI2CDevice *i2cdev, uint8_t reg, + const char *test_str, uint8_t len) +{ + char buf[SMBUS_DATA_MAX_LEN] = {0}; + buf[0] = len; + strncpy(buf + 1, test_str, len); + i2c_write_block(i2cdev, reg, (uint8_t *)buf, len + 1); + compare_string(i2cdev, reg, test_str); +} + +static void test_defaults(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION); + g_assert_cmphex(i2c_value, ==, ADM1266_OPERATION_DEFAULT); + + i2c_value = i2c_get8(i2cdev, PMBUS_REVISION); + g_assert_cmphex(i2c_value, ==, ADM1266_PMBUS_REVISION_DEFAULT); + + compare_string(i2cdev, PMBUS_MFR_ID, ADM1266_MFR_ID_DEFAULT); + compare_string(i2cdev, PMBUS_MFR_MODEL, ADM1266_MFR_MODEL_DEFAULT); + compare_string(i2cdev, PMBUS_MFR_REVISION, ADM1266_MFR_REVISION_DEFAULT); +} + +/* test r/w registers */ +static void test_rw_regs(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + /* empty strings */ + i2c_set8(i2cdev, PMBUS_MFR_ID, 0); + compare_string(i2cdev, PMBUS_MFR_ID, ""); + + i2c_set8(i2cdev, PMBUS_MFR_MODEL, 0); + compare_string(i2cdev, PMBUS_MFR_MODEL, ""); + + i2c_set8(i2cdev, PMBUS_MFR_REVISION, 0); + compare_string(i2cdev, PMBUS_MFR_REVISION, ""); + + /* test strings */ + write_and_compare_string(i2cdev, PMBUS_MFR_ID, TEST_STRING_A, + sizeof(TEST_STRING_A)); + write_and_compare_string(i2cdev, PMBUS_MFR_ID, TEST_STRING_B, + sizeof(TEST_STRING_B)); + write_and_compare_string(i2cdev, PMBUS_MFR_ID, TEST_STRING_C, + sizeof(TEST_STRING_C)); +} + +static void adm1266_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "id=" TEST_ID ",address=0x12" + }; + add_qi2c_address(&opts, &(QI2CAddress) { TEST_ADDR }); + + qos_node_create_driver("adm1266", i2c_device_create); + qos_node_consumes("adm1266", "i2c-bus", &opts); + + qos_add_test("test_defaults", "adm1266", test_defaults, NULL); + qos_add_test("test_rw_regs", "adm1266", test_rw_regs, NULL); +} + +libqos_init(adm1266_register_nodes); diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index f1e510b0ac..5a1923f721 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -36,17 +36,14 @@ #include "hw/pci/pci_ids.h" #include "hw/pci/pci_regs.h" -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(s, ...) qobject_unref(qtest_qmp(s, __VA_ARGS__)) - /* Test images sizes in MB */ #define TEST_IMAGE_SIZE_MB_LARGE (200 * 1024) #define TEST_IMAGE_SIZE_MB_SMALL 64 /*** Globals ***/ -static char tmp_path[] = "/tmp/qtest.XXXXXX"; -static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX"; -static char mig_socket[] = "/tmp/qtest-migration.XXXXXX"; +static char *tmp_path; +static char *debug_path; +static char *mig_socket; static bool ahci_pedantic; static const char *imgfmt; static unsigned test_image_size_mb; @@ -154,6 +151,7 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri) /** * Start a Q35 machine and bookmark a handle to the AHCI device. */ +G_GNUC_PRINTF(1, 0) static AHCIQState *ahci_vboot(const char *cli, va_list ap) { AHCIQState *s; @@ -171,6 +169,7 @@ static AHCIQState *ahci_vboot(const char *cli, va_list ap) /** * Start a Q35 machine and bookmark a handle to the AHCI device. */ +G_GNUC_PRINTF(1, 2) static AHCIQState *ahci_boot(const char *cli, ...) { AHCIQState *s; @@ -209,6 +208,7 @@ static void ahci_shutdown(AHCIQState *ahci) * Boot and fully enable the HBA device. * @see ahci_boot, ahci_pci_enable and ahci_hba_enable. */ +G_GNUC_PRINTF(1, 2) static AHCIQState *ahci_boot_and_enable(const char *cli, ...) { AHCIQState *ahci; @@ -330,7 +330,7 @@ static void ahci_test_pci_spec(AHCIQState *ahci) ASSERT_BIT_CLEAR(datal, ~0xFF); g_assert_cmphex(datal, !=, 0); - /* Check specification adherence for capability extenstions. */ + /* Check specification adherence for capability extensions. */ data = qpci_config_readw(ahci->dev, datal); switch (ahci->fingerprint) { @@ -1424,6 +1424,89 @@ static void test_reset(void) ahci_shutdown(ahci); } +static void test_reset_pending_callback(void) +{ + AHCIQState *ahci; + AHCICommand *cmd; + uint8_t port; + uint64_t ptr1; + uint64_t ptr2; + + int bufsize = 4 * 1024; + int speed = bufsize + (bufsize / 2); + int offset1 = 0; + int offset2 = bufsize / AHCI_SECTOR_SIZE; + + g_autofree unsigned char *tx1 = g_malloc(bufsize); + g_autofree unsigned char *tx2 = g_malloc(bufsize); + g_autofree unsigned char *rx1 = g_malloc0(bufsize); + g_autofree unsigned char *rx2 = g_malloc0(bufsize); + + /* Uses throttling to make test independent of specific environment. */ + ahci = ahci_boot_and_enable("-drive if=none,id=drive0,file=%s," + "cache=writeback,format=%s," + "throttling.bps-write=%d " + "-M q35 " + "-device ide-hd,drive=drive0 ", + tmp_path, imgfmt, speed); + + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + ptr1 = ahci_alloc(ahci, bufsize); + ptr2 = ahci_alloc(ahci, bufsize); + + g_assert(ptr1 && ptr2); + + /* Need two different patterns. */ + do { + generate_pattern(tx1, bufsize, AHCI_SECTOR_SIZE); + generate_pattern(tx2, bufsize, AHCI_SECTOR_SIZE); + } while (memcmp(tx1, tx2, bufsize) == 0); + + qtest_bufwrite(ahci->parent->qts, ptr1, tx1, bufsize); + qtest_bufwrite(ahci->parent->qts, ptr2, tx2, bufsize); + + /* Write to beginning of disk to check it wasn't overwritten later. */ + ahci_guest_io(ahci, port, CMD_WRITE_DMA_EXT, ptr1, bufsize, offset1); + + /* Issue asynchronously to get a pending callback during reset. */ + cmd = ahci_command_create(CMD_WRITE_DMA_EXT); + ahci_command_adjust(cmd, offset2, ptr2, bufsize, 0); + ahci_command_commit(ahci, cmd, port); + ahci_command_issue_async(ahci, cmd); + + ahci_set(ahci, AHCI_GHC, AHCI_GHC_HR); + + ahci_command_free(cmd); + + /* Wait for throttled write to finish. */ + sleep(1); + + /* Start again. */ + ahci_clean_mem(ahci); + ahci_pci_enable(ahci); + ahci_hba_enable(ahci); + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + /* Read and verify. */ + ahci_guest_io(ahci, port, CMD_READ_DMA_EXT, ptr1, bufsize, offset1); + qtest_bufread(ahci->parent->qts, ptr1, rx1, bufsize); + g_assert_cmphex(memcmp(tx1, rx1, bufsize), ==, 0); + + ahci_guest_io(ahci, port, CMD_READ_DMA_EXT, ptr2, bufsize, offset2); + qtest_bufread(ahci->parent->qts, ptr2, rx2, bufsize); + g_assert_cmphex(memcmp(tx2, rx2, bufsize), ==, 0); + + ahci_free(ahci, ptr1); + ahci_free(ahci, ptr2); + + ahci_clean_mem(ahci); + + ahci_shutdown(ahci); +} + static void test_ncq_simple(void) { AHCIQState *ahci; @@ -1437,10 +1520,10 @@ static void test_ncq_simple(void) static int prepare_iso(size_t size, unsigned char **buf, char **name) { - char cdrom_path[] = "/tmp/qtest.iso.XXXXXX"; + g_autofree char *cdrom_path = NULL; unsigned char *patt; ssize_t ret; - int fd = mkstemp(cdrom_path); + int fd = g_file_open_tmp("qtest.iso.XXXXXX", &cdrom_path, NULL); g_assert(fd != -1); g_assert(buf); @@ -1592,9 +1675,9 @@ static void test_atapi_tray(void) rsp = qtest_qmp_receive(ahci->parent->qts); qobject_unref(rsp); - qmp_discard_response(ahci->parent->qts, - "{'execute': 'blockdev-remove-medium', " - "'arguments': {'id': 'cd0'}}"); + qtest_qmp_assert_success(ahci->parent->qts, + "{'execute': 'blockdev-remove-medium', " + "'arguments': {'id': 'cd0'}}"); /* Test the tray without a medium */ ahci_atapi_load(ahci, port); @@ -1604,16 +1687,18 @@ static void test_atapi_tray(void) atapi_wait_tray(ahci, true); /* Re-insert media */ - qmp_discard_response(ahci->parent->qts, - "{'execute': 'blockdev-add', " - "'arguments': {'node-name': 'node0', " - "'driver': 'raw', " - "'file': { 'driver': 'file', " - "'filename': %s }}}", iso); - qmp_discard_response(ahci->parent->qts, - "{'execute': 'blockdev-insert-medium'," - "'arguments': { 'id': 'cd0', " - "'node-name': 'node0' }}"); + qtest_qmp_assert_success( + ahci->parent->qts, + "{'execute': 'blockdev-add', " + "'arguments': {'node-name': 'node0', " + "'driver': 'raw', " + "'file': { 'driver': 'file', " + "'filename': %s }}}", iso); + qtest_qmp_assert_success( + ahci->parent->qts, + "{'execute': 'blockdev-insert-medium'," + "'arguments': { 'id': 'cd0', " + "'node-name': 'node0' }}"); /* Again, the event shows up first */ qtest_qmp_send(ahci->parent->qts, "{'execute': 'blockdev-close-tray', " @@ -1833,7 +1918,7 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, int main(int argc, char **argv) { - const char *arch; + const char *arch, *base; int ret; int fd; int c; @@ -1871,8 +1956,22 @@ int main(int argc, char **argv) return 0; } + /* + * "base" stores the starting point where we create temporary files. + * + * On Windows, this is set to the relative path of current working + * directory, because the absolute path causes the blkdebug filename + * parser fail to parse "blkdebug:path/to/config:path/to/image". + */ +#ifndef _WIN32 + base = g_get_tmp_dir(); +#else + base = "."; +#endif + /* Create a temporary image */ - fd = mkstemp(tmp_path); + tmp_path = g_strdup_printf("%s/qtest.XXXXXX", base); + fd = g_mkstemp(tmp_path); g_assert(fd >= 0); if (have_qemu_img()) { imgfmt = "qcow2"; @@ -1889,12 +1988,13 @@ int main(int argc, char **argv) close(fd); /* Create temporary blkdebug instructions */ - fd = mkstemp(debug_path); + debug_path = g_strdup_printf("%s/qtest-blkdebug.XXXXXX", base); + fd = g_mkstemp(debug_path); g_assert(fd >= 0); close(fd); /* Reserve a hollow file to use as a socket for migration tests */ - fd = mkstemp(mig_socket); + fd = g_file_open_tmp("qtest-migration.XXXXXX", &mig_socket, NULL); g_assert(fd >= 0); close(fd); @@ -1928,7 +2028,8 @@ int main(int argc, char **argv) qtest_add_func("/ahci/migrate/dma/halted", test_migrate_halted_dma); qtest_add_func("/ahci/max", test_max); - qtest_add_func("/ahci/reset", test_reset); + qtest_add_func("/ahci/reset/simple", test_reset); + qtest_add_func("/ahci/reset/pending_callback", test_reset_pending_callback); qtest_add_func("/ahci/io/ncq/simple", test_ncq_simple); qtest_add_func("/ahci/migrate/ncq/simple", test_migrate_ncq); @@ -1947,8 +2048,11 @@ int main(int argc, char **argv) /* Cleanup */ unlink(tmp_path); + g_free(tmp_path); unlink(debug_path); + g_free(debug_path); unlink(mig_socket); + g_free(mig_socket); return ret; } diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c index 5a14527386..9d6e6190d5 100644 --- a/tests/qtest/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -21,7 +21,7 @@ #define SVE_MAX_VQ 16 #define MACHINE "-machine virt,gic-version=max -accel tcg " -#define MACHINE_KVM "-machine virt,gic-version=max -accel kvm -accel tcg " +#define MACHINE_KVM "-machine virt,gic-version=max -accel kvm " #define QUERY_HEAD "{ 'execute': 'query-cpu-model-expansion', " \ " 'arguments': { 'type': 'full', " #define QUERY_TAIL "}}" @@ -32,6 +32,7 @@ static QDict *do_query_no_props(QTestState *qts, const char *cpu_type) QUERY_TAIL, cpu_type); } +G_GNUC_PRINTF(3, 4) static QDict *do_query(QTestState *qts, const char *cpu_type, const char *fmt, ...) { @@ -78,7 +79,7 @@ static const char *resp_get_error(QDict *resp) g_assert(_resp); \ _error = resp_get_error(_resp); \ g_assert(_error); \ - g_assert(g_str_equal(_error, expected_error)); \ + g_assert_cmpstr(_error, ==, expected_error); \ qobject_unref(_resp); \ }) @@ -193,8 +194,8 @@ static void assert_type_full(QTestState *qts) g_assert(resp); error = resp_get_error(resp); g_assert(error); - g_assert(g_str_equal(error, - "The requested expansion type is not supported")); + g_assert_cmpstr(error, ==, + "The requested expansion type is not supported"); qobject_unref(resp); } @@ -211,8 +212,9 @@ static void assert_bad_props(QTestState *qts, const char *cpu_type) g_assert(resp); error = resp_get_error(resp); g_assert(error); - g_assert(g_str_equal(error, - "Invalid parameter type for 'props', expected: dict")); + g_assert_cmpstr(error, ==, + "Invalid parameter type for 'model.props'," + " expected: object"); qobject_unref(resp); } @@ -416,12 +418,22 @@ static void pauth_tests_default(QTestState *qts, const char *cpu_type) { assert_has_feature_enabled(qts, cpu_type, "pauth"); assert_has_feature_disabled(qts, cpu_type, "pauth-impdef"); + assert_has_feature_disabled(qts, cpu_type, "pauth-qarma3"); assert_set_feature(qts, cpu_type, "pauth", false); assert_set_feature(qts, cpu_type, "pauth", true); assert_set_feature(qts, cpu_type, "pauth-impdef", true); assert_set_feature(qts, cpu_type, "pauth-impdef", false); - assert_error(qts, cpu_type, "cannot enable pauth-impdef without pauth", + assert_set_feature(qts, cpu_type, "pauth-qarma3", true); + assert_set_feature(qts, cpu_type, "pauth-qarma3", false); + assert_error(qts, cpu_type, + "cannot enable pauth-impdef or pauth-qarma3 without pauth", "{ 'pauth': false, 'pauth-impdef': true }"); + assert_error(qts, cpu_type, + "cannot enable pauth-impdef or pauth-qarma3 without pauth", + "{ 'pauth': false, 'pauth-qarma3': true }"); + assert_error(qts, cpu_type, + "cannot enable both pauth-impdef and pauth-qarma3", + "{ 'pauth': true, 'pauth-impdef': true, 'pauth-qarma3': true }"); } static void test_query_cpu_model_expansion(const void *data) @@ -435,7 +447,7 @@ static void test_query_cpu_model_expansion(const void *data) assert_bad_props(qts, "max"); assert_error(qts, "foo", "The CPU type 'foo' is not a recognized " "ARM CPU type", NULL); - assert_error(qts, "max", "Parameter 'not-a-prop' is unexpected", + assert_error(qts, "max", "Parameter 'model.props.not-a-prop' is unexpected", "{ 'not-a-prop': false }"); assert_error(qts, "host", "The CPU type 'host' requires KVM", NULL); @@ -505,9 +517,23 @@ static void test_query_cpu_model_expansion_kvm(const void *data) QDict *resp; char *error; - assert_error(qts, "cortex-a15", - "We cannot guarantee the CPU type 'cortex-a15' works " - "with KVM on this host", NULL); + /* + * When using KVM, only the 'host' and 'max' CPU models are + * supported. Test that we're emitting a suitable error for + * unsupported CPU models. + */ + if (qtest_has_accel("tcg")) { + assert_error(qts, "cortex-a7", + "We cannot guarantee the CPU type 'cortex-a7' works " + "with KVM on this host", NULL); + } else { + /* + * With a KVM-only build the 32-bit CPUs are not present. + */ + assert_error(qts, "cortex-a7", + "The CPU type 'cortex-a7' is not a " + "recognized ARM CPU type", NULL); + } assert_has_feature_enabled(qts, "host", "aarch64"); @@ -606,31 +632,39 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - qtest_add_data_func("/arm/query-cpu-model-expansion", - NULL, test_query_cpu_model_expansion); + if (qtest_has_accel("tcg")) { + qtest_add_data_func("/arm/query-cpu-model-expansion", + NULL, test_query_cpu_model_expansion); + } + + if (!g_str_equal(qtest_get_arch(), "aarch64")) { + goto out; + } /* * For now we only run KVM specific tests with AArch64 QEMU in * order avoid attempting to run an AArch32 QEMU with KVM on * AArch64 hosts. That won't work and isn't easy to detect. */ - if (g_str_equal(qtest_get_arch(), "aarch64") && qtest_has_accel("kvm")) { + if (qtest_has_accel("kvm")) { /* * This tests target the 'host' CPU type, so register it only if * KVM is available. */ qtest_add_data_func("/arm/kvm/query-cpu-model-expansion", NULL, test_query_cpu_model_expansion_kvm); - } - if (g_str_equal(qtest_get_arch(), "aarch64")) { - qtest_add_data_func("/arm/max/query-cpu-model-expansion/sve-max-vq-8", - NULL, sve_tests_sve_max_vq_8); - qtest_add_data_func("/arm/max/query-cpu-model-expansion/sve-off", - NULL, sve_tests_sve_off); qtest_add_data_func("/arm/kvm/query-cpu-model-expansion/sve-off", NULL, sve_tests_sve_off_kvm); } + if (qtest_has_accel("tcg")) { + qtest_add_data_func("/arm/max/query-cpu-model-expansion/sve-max-vq-8", + NULL, sve_tests_sve_max_vq_8); + qtest_add_data_func("/arm/max/query-cpu-model-expansion/sve-off", + NULL, sve_tests_sve_off); + } + +out: return g_test_run(); } diff --git a/tests/qtest/aspeed_fsi-test.c b/tests/qtest/aspeed_fsi-test.c new file mode 100644 index 0000000000..b3020dd821 --- /dev/null +++ b/tests/qtest/aspeed_fsi-test.c @@ -0,0 +1,205 @@ +/* + * QTest testcases for IBM's Flexible Service Interface (FSI) + * + * Copyright (c) 2023 IBM Corporation + * + * Authors: + * Ninad Palsule + * + * 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/module.h" +#include "libqtest-single.h" + +/* Registers from ast2600 specifications */ +#define ASPEED_FSI_ENGINER_TRIGGER 0x04 +#define ASPEED_FSI_OPB0_BUS_SELECT 0x10 +#define ASPEED_FSI_OPB1_BUS_SELECT 0x28 +#define ASPEED_FSI_OPB0_RW_DIRECTION 0x14 +#define ASPEED_FSI_OPB1_RW_DIRECTION 0x2c +#define ASPEED_FSI_OPB0_XFER_SIZE 0x18 +#define ASPEED_FSI_OPB1_XFER_SIZE 0x30 +#define ASPEED_FSI_OPB0_BUS_ADDR 0x1c +#define ASPEED_FSI_OPB1_BUS_ADDR 0x34 +#define ASPEED_FSI_INTRRUPT_CLEAR 0x40 +#define ASPEED_FSI_INTRRUPT_STATUS 0x48 +#define ASPEED_FSI_OPB0_BUS_STATUS 0x80 +#define ASPEED_FSI_OPB1_BUS_STATUS 0x8c +#define ASPEED_FSI_OPB0_READ_DATA 0x84 +#define ASPEED_FSI_OPB1_READ_DATA 0x90 + +/* + * FSI Base addresses from the ast2600 specifications. + */ +#define AST2600_OPB_FSI0_BASE_ADDR 0x1e79b000 +#define AST2600_OPB_FSI1_BASE_ADDR 0x1e79b100 + +static uint32_t aspeed_fsi_base_addr; + +static uint32_t aspeed_fsi_readl(QTestState *s, uint32_t reg) +{ + return qtest_readl(s, aspeed_fsi_base_addr + reg); +} + +static void aspeed_fsi_writel(QTestState *s, uint32_t reg, uint32_t val) +{ + qtest_writel(s, aspeed_fsi_base_addr + reg, val); +} + +/* Setup base address and select register */ +static void test_fsi_setup(QTestState *s, uint32_t base_addr) +{ + uint32_t curval; + + aspeed_fsi_base_addr = base_addr; + + /* Set the base select register */ + if (base_addr == AST2600_OPB_FSI0_BASE_ADDR) { + /* Unselect FSI1 */ + aspeed_fsi_writel(s, ASPEED_FSI_OPB1_BUS_SELECT, 0x0); + curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB1_BUS_SELECT); + g_assert_cmpuint(curval, ==, 0x0); + + /* Select FSI0 */ + aspeed_fsi_writel(s, ASPEED_FSI_OPB0_BUS_SELECT, 0x1); + curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB0_BUS_SELECT); + g_assert_cmpuint(curval, ==, 0x1); + } else if (base_addr == AST2600_OPB_FSI1_BASE_ADDR) { + /* Unselect FSI0 */ + aspeed_fsi_writel(s, ASPEED_FSI_OPB0_BUS_SELECT, 0x0); + curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB0_BUS_SELECT); + g_assert_cmpuint(curval, ==, 0x0); + + /* Select FSI1 */ + aspeed_fsi_writel(s, ASPEED_FSI_OPB1_BUS_SELECT, 0x1); + curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB1_BUS_SELECT); + g_assert_cmpuint(curval, ==, 0x1); + } else { + g_assert_not_reached(); + } +} + +static void test_fsi_reg_change(QTestState *s, uint32_t reg, uint32_t newval) +{ + uint32_t base; + uint32_t curval; + + base = aspeed_fsi_readl(s, reg); + aspeed_fsi_writel(s, reg, newval); + curval = aspeed_fsi_readl(s, reg); + g_assert_cmpuint(curval, ==, newval); + aspeed_fsi_writel(s, reg, base); + curval = aspeed_fsi_readl(s, reg); + g_assert_cmpuint(curval, ==, base); +} + +static void test_fsi0_master_regs(const void *data) +{ + QTestState *s = (QTestState *)data; + + test_fsi_setup(s, AST2600_OPB_FSI0_BASE_ADDR); + + test_fsi_reg_change(s, ASPEED_FSI_OPB0_RW_DIRECTION, 0xF3F4F514); + test_fsi_reg_change(s, ASPEED_FSI_OPB0_XFER_SIZE, 0xF3F4F518); + test_fsi_reg_change(s, ASPEED_FSI_OPB0_BUS_ADDR, 0xF3F4F51c); + test_fsi_reg_change(s, ASPEED_FSI_INTRRUPT_CLEAR, 0xF3F4F540); + test_fsi_reg_change(s, ASPEED_FSI_INTRRUPT_STATUS, 0xF3F4F548); + test_fsi_reg_change(s, ASPEED_FSI_OPB0_BUS_STATUS, 0xF3F4F580); + test_fsi_reg_change(s, ASPEED_FSI_OPB0_READ_DATA, 0xF3F4F584); +} + +static void test_fsi1_master_regs(const void *data) +{ + QTestState *s = (QTestState *)data; + + test_fsi_setup(s, AST2600_OPB_FSI1_BASE_ADDR); + + test_fsi_reg_change(s, ASPEED_FSI_OPB1_RW_DIRECTION, 0xF3F4F514); + test_fsi_reg_change(s, ASPEED_FSI_OPB1_XFER_SIZE, 0xF3F4F518); + test_fsi_reg_change(s, ASPEED_FSI_OPB1_BUS_ADDR, 0xF3F4F51c); + test_fsi_reg_change(s, ASPEED_FSI_INTRRUPT_CLEAR, 0xF3F4F540); + test_fsi_reg_change(s, ASPEED_FSI_INTRRUPT_STATUS, 0xF3F4F548); + test_fsi_reg_change(s, ASPEED_FSI_OPB1_BUS_STATUS, 0xF3F4F580); + test_fsi_reg_change(s, ASPEED_FSI_OPB1_READ_DATA, 0xF3F4F584); +} + +static void test_fsi0_getcfam_addr0(const void *data) +{ + QTestState *s = (QTestState *)data; + uint32_t curval; + + test_fsi_setup(s, AST2600_OPB_FSI0_BASE_ADDR); + + /* Master access direction read */ + aspeed_fsi_writel(s, ASPEED_FSI_OPB0_RW_DIRECTION, 0x1); + /* word */ + aspeed_fsi_writel(s, ASPEED_FSI_OPB0_XFER_SIZE, 0x3); + /* Address */ + aspeed_fsi_writel(s, ASPEED_FSI_OPB0_BUS_ADDR, 0xa0000000); + aspeed_fsi_writel(s, ASPEED_FSI_INTRRUPT_CLEAR, 0x1); + aspeed_fsi_writel(s, ASPEED_FSI_ENGINER_TRIGGER, 0x1); + + curval = aspeed_fsi_readl(s, ASPEED_FSI_INTRRUPT_STATUS); + g_assert_cmpuint(curval, ==, 0x10000); + curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB0_BUS_STATUS); + g_assert_cmpuint(curval, ==, 0x0); + curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB0_READ_DATA); + g_assert_cmpuint(curval, ==, 0x152d02c0); +} + +static void test_fsi1_getcfam_addr0(const void *data) +{ + QTestState *s = (QTestState *)data; + uint32_t curval; + + test_fsi_setup(s, AST2600_OPB_FSI1_BASE_ADDR); + + /* Master access direction read */ + aspeed_fsi_writel(s, ASPEED_FSI_OPB1_RW_DIRECTION, 0x1); + + aspeed_fsi_writel(s, ASPEED_FSI_OPB1_XFER_SIZE, 0x3); + aspeed_fsi_writel(s, ASPEED_FSI_OPB1_BUS_ADDR, 0xa0000000); + aspeed_fsi_writel(s, ASPEED_FSI_INTRRUPT_CLEAR, 0x1); + aspeed_fsi_writel(s, ASPEED_FSI_ENGINER_TRIGGER, 0x1); + + curval = aspeed_fsi_readl(s, ASPEED_FSI_INTRRUPT_STATUS); + g_assert_cmpuint(curval, ==, 0x20000); + curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB1_BUS_STATUS); + g_assert_cmpuint(curval, ==, 0x0); + curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB1_READ_DATA); + g_assert_cmpuint(curval, ==, 0x152d02c0); +} + +int main(int argc, char **argv) +{ + int ret = -1; + QTestState *s; + + g_test_init(&argc, &argv, NULL); + + s = qtest_init("-machine ast2600-evb "); + + /* Tests for OPB/FSI0 */ + qtest_add_data_func("/aspeed-fsi-test/test_fsi0_master_regs", s, + test_fsi0_master_regs); + + qtest_add_data_func("/aspeed-fsi-test/test_fsi0_getcfam_addr0", s, + test_fsi0_getcfam_addr0); + + /* Tests for OPB/FSI1 */ + qtest_add_data_func("/aspeed-fsi-test/test_fsi1_master_regs", s, + test_fsi1_master_regs); + + qtest_add_data_func("/aspeed-fsi-test/test_fsi1_getcfam_addr0", s, + test_fsi1_getcfam_addr0); + + ret = g_test_run(); + qtest_quit(s); + + return ret; +} diff --git a/tests/qtest/aspeed_gpio-test.c b/tests/qtest/aspeed_gpio-test.c index bac63e8742..d38f51d719 100644 --- a/tests/qtest/aspeed_gpio-test.c +++ b/tests/qtest/aspeed_gpio-test.c @@ -28,6 +28,11 @@ #include "qapi/qmp/qdict.h" #include "libqtest-single.h" +#define AST2600_GPIO_BASE 0x1E780000 + +#define GPIO_ABCD_DATA_VALUE 0x000 +#define GPIO_ABCD_DIRECTION 0x004 + static void test_set_colocated_pins(const void *data) { QTestState *s = (QTestState *)data; @@ -46,6 +51,27 @@ static void test_set_colocated_pins(const void *data) g_assert(!qtest_qom_get_bool(s, "/machine/soc/gpio", "gpioV7")); } +static void test_set_input_pins(const void *data) +{ + QTestState *s = (QTestState *)data; + char name[16]; + uint32_t value; + + qtest_writel(s, AST2600_GPIO_BASE + GPIO_ABCD_DIRECTION, 0x00000000); + for (char c = 'A'; c <= 'D'; c++) { + for (int i = 0; i < 8; i++) { + sprintf(name, "gpio%c%d", c, i); + qtest_qom_set_bool(s, "/machine/soc/gpio", name, true); + } + } + value = qtest_readl(s, AST2600_GPIO_BASE + GPIO_ABCD_DATA_VALUE); + g_assert_cmphex(value, ==, 0xffffffff); + + qtest_writel(s, AST2600_GPIO_BASE + GPIO_ABCD_DATA_VALUE, 0x00000000); + value = qtest_readl(s, AST2600_GPIO_BASE + GPIO_ABCD_DATA_VALUE); + g_assert_cmphex(value, ==, 0xffffffff); +} + int main(int argc, char **argv) { QTestState *s; @@ -56,6 +82,7 @@ int main(int argc, char **argv) s = qtest_init("-machine ast2600-evb"); qtest_add_data_func("/ast2600/gpio/set_colocated_pins", s, test_set_colocated_pins); + qtest_add_data_func("/ast2600/gpio/set_input_pins", s, test_set_input_pins); r = g_test_run(); qtest_quit(s); diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index ec233315e6..c713a3700b 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -56,7 +56,9 @@ enum { BULK_ERASE = 0xc7, READ = 0x03, PP = 0x02, + WRSR = 0x1, WREN = 0x6, + SRWD = 0x80, RESET_ENABLE = 0x66, RESET_MEMORY = 0x99, EN_4BYTE_ADDR = 0xB7, @@ -135,6 +137,9 @@ static void flash_reset(void) spi_ctrl_start_user(); writeb(ASPEED_FLASH_BASE, RESET_ENABLE); writeb(ASPEED_FLASH_BASE, RESET_MEMORY); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, BULK_ERASE); + writeb(ASPEED_FLASH_BASE, WRDI); spi_ctrl_stop_user(); spi_conf_remove(CONF_ENABLE_W0); @@ -187,6 +192,24 @@ static void read_page_mem(uint32_t addr, uint32_t *page) } } +static void write_page_mem(uint32_t addr, uint32_t write_value) +{ + spi_ctrl_setmode(CTRL_WRITEMODE, PP); + + for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + writel(ASPEED_FLASH_BASE + addr + i * 4, write_value); + } +} + +static void assert_page_mem(uint32_t addr, uint32_t expected_value) +{ + uint32_t page[FLASH_PAGE_SIZE / 4]; + read_page_mem(addr, page); + for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, expected_value); + } +} + static void test_erase_sector(void) { uint32_t some_page_addr = 0x600 * FLASH_PAGE_SIZE; @@ -195,6 +218,33 @@ static void test_erase_sector(void) spi_conf(CONF_ENABLE_W0); + /* + * Previous page should be full of 0xffs after backend is + * initialized + */ + read_page(some_page_addr - FLASH_PAGE_SIZE, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, PP); + writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4)); + } + spi_ctrl_stop_user(); + + /* Check the page is correctly written */ + read_page(some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, some_page_addr + i * 4); + } + spi_ctrl_start_user(); writeb(ASPEED_FLASH_BASE, WREN); writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); @@ -202,14 +252,7 @@ static void test_erase_sector(void) writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); spi_ctrl_stop_user(); - /* Previous page should be full of zeroes as backend is not - * initialized */ - read_page(some_page_addr - FLASH_PAGE_SIZE, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0x0); - } - - /* But this one was erased */ + /* Check the page is erased */ read_page(some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, 0xffffffff); @@ -226,11 +269,31 @@ static void test_erase_all(void) spi_conf(CONF_ENABLE_W0); - /* Check some random page. Should be full of zeroes as backend is - * not initialized */ + /* + * Previous page should be full of 0xffs after backend is + * initialized + */ + read_page(some_page_addr - FLASH_PAGE_SIZE, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, PP); + writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4)); + } + spi_ctrl_stop_user(); + + /* Check the page is correctly written */ read_page(some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0x0); + g_assert_cmphex(page[i], ==, some_page_addr + i * 4); } spi_ctrl_start_user(); @@ -238,7 +301,7 @@ static void test_erase_all(void) writeb(ASPEED_FLASH_BASE, BULK_ERASE); spi_ctrl_stop_user(); - /* Recheck that some random page */ + /* Check the page is erased */ read_page(some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, 0xffffffff); @@ -299,6 +362,14 @@ static void test_read_page_mem(void) spi_conf(CONF_ENABLE_W0); spi_ctrl_start_user(); writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, PP); + writel(ASPEED_FLASH_BASE, make_be32(my_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4)); + } spi_ctrl_stop_user(); spi_conf_remove(CONF_ENABLE_W0); @@ -390,16 +461,162 @@ static void test_read_status_reg(void) flash_reset(); } -static char tmp_path[] = "/tmp/qtest.m25p80.XXXXXX"; +static void test_status_reg_write_protection(void) +{ + uint8_t r; + + spi_conf(CONF_ENABLE_W0); + + /* default case: WP# is high and SRWD is low -> status register writable */ + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + /* test ability to write SRWD */ + writeb(ASPEED_FLASH_BASE, WRSR); + writeb(ASPEED_FLASH_BASE, SRWD); + writeb(ASPEED_FLASH_BASE, RDSR); + r = readb(ASPEED_FLASH_BASE); + spi_ctrl_stop_user(); + g_assert_cmphex(r & SRWD, ==, SRWD); + + /* WP# high and SRWD high -> status register writable */ + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + /* test ability to write SRWD */ + writeb(ASPEED_FLASH_BASE, WRSR); + writeb(ASPEED_FLASH_BASE, 0); + writeb(ASPEED_FLASH_BASE, RDSR); + r = readb(ASPEED_FLASH_BASE); + spi_ctrl_stop_user(); + g_assert_cmphex(r & SRWD, ==, 0); + + /* WP# low and SRWD low -> status register writable */ + qtest_set_irq_in(global_qtest, + "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 0); + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + /* test ability to write SRWD */ + writeb(ASPEED_FLASH_BASE, WRSR); + writeb(ASPEED_FLASH_BASE, SRWD); + writeb(ASPEED_FLASH_BASE, RDSR); + r = readb(ASPEED_FLASH_BASE); + spi_ctrl_stop_user(); + g_assert_cmphex(r & SRWD, ==, SRWD); + + /* WP# low and SRWD high -> status register NOT writable */ + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + /* test ability to write SRWD */ + writeb(ASPEED_FLASH_BASE, WRSR); + writeb(ASPEED_FLASH_BASE, 0); + writeb(ASPEED_FLASH_BASE, RDSR); + r = readb(ASPEED_FLASH_BASE); + spi_ctrl_stop_user(); + /* write is not successful */ + g_assert_cmphex(r & SRWD, ==, SRWD); + + qtest_set_irq_in(global_qtest, + "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 1); + flash_reset(); +} + +static void test_write_block_protect(void) +{ + uint32_t sector_size = 65536; + uint32_t n_sectors = 512; + + spi_ce_ctrl(1 << CRTL_EXTENDED0); + spi_conf(CONF_ENABLE_W0); + + uint32_t bp_bits = 0b0; + + for (int i = 0; i < 16; i++) { + bp_bits = ((i & 0b1000) << 3) | ((i & 0b0111) << 2); + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, BULK_ERASE); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, WRSR); + writeb(ASPEED_FLASH_BASE, bp_bits); + writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); + writeb(ASPEED_FLASH_BASE, WREN); + spi_ctrl_stop_user(); + + uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; + uint32_t protection_start = n_sectors - num_protected_sectors; + uint32_t protection_end = n_sectors; + + for (int sector = 0; sector < n_sectors; sector++) { + uint32_t addr = sector * sector_size; + + assert_page_mem(addr, 0xffffffff); + write_page_mem(addr, make_be32(0xabcdef12)); + + uint32_t expected_value = protection_start <= sector + && sector < protection_end + ? 0xffffffff : 0xabcdef12; + + assert_page_mem(addr, expected_value); + } + } + + flash_reset(); +} + +static void test_write_block_protect_bottom_bit(void) +{ + uint32_t sector_size = 65536; + uint32_t n_sectors = 512; + + spi_ce_ctrl(1 << CRTL_EXTENDED0); + spi_conf(CONF_ENABLE_W0); + + /* top bottom bit is enabled */ + uint32_t bp_bits = 0b00100 << 3; + + for (int i = 0; i < 16; i++) { + bp_bits = (((i & 0b1000) | 0b0100) << 3) | ((i & 0b0111) << 2); + + spi_ctrl_start_user(); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, BULK_ERASE); + writeb(ASPEED_FLASH_BASE, WREN); + writeb(ASPEED_FLASH_BASE, WRSR); + writeb(ASPEED_FLASH_BASE, bp_bits); + writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); + writeb(ASPEED_FLASH_BASE, WREN); + spi_ctrl_stop_user(); + + uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; + uint32_t protection_start = 0; + uint32_t protection_end = num_protected_sectors; + + for (int sector = 0; sector < n_sectors; sector++) { + uint32_t addr = sector * sector_size; + + assert_page_mem(addr, 0xffffffff); + write_page_mem(addr, make_be32(0xabcdef12)); + + uint32_t expected_value = protection_start <= sector + && sector < protection_end + ? 0xffffffff : 0xabcdef12; + + assert_page_mem(addr, expected_value); + } + } + + flash_reset(); +} int main(int argc, char **argv) { + g_autofree char *tmp_path = NULL; int ret; int fd; g_test_init(&argc, &argv, NULL); - fd = mkstemp(tmp_path); + fd = g_file_open_tmp("qtest.m25p80.XXXXXX", &tmp_path, NULL); g_assert(fd >= 0); ret = ftruncate(fd, FLASH_SIZE); g_assert(ret == 0); @@ -416,7 +633,14 @@ int main(int argc, char **argv) qtest_add_func("/ast2400/smc/read_page_mem", test_read_page_mem); qtest_add_func("/ast2400/smc/write_page_mem", test_write_page_mem); qtest_add_func("/ast2400/smc/read_status_reg", test_read_status_reg); + qtest_add_func("/ast2400/smc/status_reg_write_protection", + test_status_reg_write_protection); + qtest_add_func("/ast2400/smc/write_block_protect", + test_write_block_protect); + qtest_add_func("/ast2400/smc/write_block_protect_bottom_bit", + test_write_block_protect_bottom_bit); + flash_reset(); ret = g_test_run(); qtest_quit(global_qtest); diff --git a/tests/qtest/bcm2835-dma-test.c b/tests/qtest/bcm2835-dma-test.c new file mode 100644 index 0000000000..18901b76d2 --- /dev/null +++ b/tests/qtest/bcm2835-dma-test.c @@ -0,0 +1,118 @@ +/* + * QTest testcase for BCM283x DMA engine (on Raspberry Pi 3) + * and its interrupts coming to Interrupt Controller. + * + * Copyright (c) 2022 Auriga LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +/* Offsets in raspi3b platform: */ +#define RASPI3_DMA_BASE 0x3f007000 +#define RASPI3_IC_BASE 0x3f00b200 + +/* Used register/fields definitions */ + +/* DMA engine registers: */ +#define BCM2708_DMA_CS 0 +#define BCM2708_DMA_ACTIVE (1 << 0) +#define BCM2708_DMA_INT (1 << 2) + +#define BCM2708_DMA_ADDR 0x04 + +#define BCM2708_DMA_INT_STATUS 0xfe0 + +/* DMA Transfer Info fields: */ +#define BCM2708_DMA_INT_EN (1 << 0) +#define BCM2708_DMA_D_INC (1 << 4) +#define BCM2708_DMA_S_INC (1 << 8) + +/* Interrupt controller registers: */ +#define IRQ_PENDING_BASIC 0x00 +#define IRQ_GPU_PENDING1_AGGR (1 << 8) +#define IRQ_PENDING_1 0x04 +#define IRQ_ENABLE_1 0x10 + +/* Data for the test: */ +#define SCB_ADDR 256 +#define S_ADDR 32 +#define D_ADDR 64 +#define TXFR_LEN 32 +const uint32_t check_data = 0x12345678; + +static void bcm2835_dma_test_interrupt(int dma_c, int irq_line) +{ + uint64_t dma_base = RASPI3_DMA_BASE + dma_c * 0x100; + int gpu_irq_line = 16 + irq_line; + + /* Check that interrupts are silent by default: */ + writel(RASPI3_IC_BASE + IRQ_ENABLE_1, 1 << gpu_irq_line); + int isr = readl(dma_base + BCM2708_DMA_INT_STATUS); + g_assert_cmpint(isr, ==, 0); + uint32_t reg0 = readl(dma_base + BCM2708_DMA_CS); + g_assert_cmpint(reg0, ==, 0); + uint32_t ic_pending = readl(RASPI3_IC_BASE + IRQ_PENDING_BASIC); + g_assert_cmpint(ic_pending, ==, 0); + uint32_t gpu_pending1 = readl(RASPI3_IC_BASE + IRQ_PENDING_1); + g_assert_cmpint(gpu_pending1, ==, 0); + + /* Prepare Control Block: */ + writel(SCB_ADDR + 0, BCM2708_DMA_S_INC | BCM2708_DMA_D_INC | + BCM2708_DMA_INT_EN); /* transfer info */ + writel(SCB_ADDR + 4, S_ADDR); /* source address */ + writel(SCB_ADDR + 8, D_ADDR); /* destination address */ + writel(SCB_ADDR + 12, TXFR_LEN); /* transfer length */ + writel(dma_base + BCM2708_DMA_ADDR, SCB_ADDR); + + writel(S_ADDR, check_data); + for (int word = S_ADDR + 4; word < S_ADDR + TXFR_LEN; word += 4) { + writel(word, ~check_data); + } + /* Perform the transfer: */ + writel(dma_base + BCM2708_DMA_CS, BCM2708_DMA_ACTIVE); + + /* Check that destination == source: */ + uint32_t data = readl(D_ADDR); + g_assert_cmpint(data, ==, check_data); + for (int word = D_ADDR + 4; word < D_ADDR + TXFR_LEN; word += 4) { + data = readl(word); + g_assert_cmpint(data, ==, ~check_data); + } + + /* Check that interrupt status is set both in DMA and IC controllers: */ + isr = readl(RASPI3_DMA_BASE + BCM2708_DMA_INT_STATUS); + g_assert_cmpint(isr, ==, 1 << dma_c); + + ic_pending = readl(RASPI3_IC_BASE + IRQ_PENDING_BASIC); + g_assert_cmpint(ic_pending, ==, IRQ_GPU_PENDING1_AGGR); + + gpu_pending1 = readl(RASPI3_IC_BASE + IRQ_PENDING_1); + g_assert_cmpint(gpu_pending1, ==, 1 << gpu_irq_line); + + /* Clean up, clear interrupt: */ + writel(dma_base + BCM2708_DMA_CS, BCM2708_DMA_INT); +} + +static void bcm2835_dma_test_interrupts(void) +{ + /* DMA engines 0--10 have separate IRQ lines, 11--14 - only one: */ + bcm2835_dma_test_interrupt(0, 0); + bcm2835_dma_test_interrupt(10, 10); + bcm2835_dma_test_interrupt(11, 11); + bcm2835_dma_test_interrupt(14, 11); +} + +int main(int argc, char **argv) +{ + int ret; + g_test_init(&argc, &argv, NULL); + qtest_add_func("/bcm2835/dma/test_interrupts", + bcm2835_dma_test_interrupts); + qtest_start("-machine raspi3b"); + ret = g_test_run(); + qtest_end(); + return ret; +} diff --git a/tests/qtest/bcm2835-i2c-test.c b/tests/qtest/bcm2835-i2c-test.c new file mode 100644 index 0000000000..513ecce61d --- /dev/null +++ b/tests/qtest/bcm2835-i2c-test.c @@ -0,0 +1,115 @@ +/* + * QTest testcase for Broadcom Serial Controller (BSC) + * + * Copyright (c) 2024 Rayhan Faizel + * + * 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 "libqtest-single.h" + +#include "hw/i2c/bcm2835_i2c.h" +#include "hw/sensor/tmp105_regs.h" + +static const uint32_t bsc_base_addrs[] = { + 0x3f205000, /* I2C0 */ + 0x3f804000, /* I2C1 */ + 0x3f805000, /* I2C2 */ +}; + +static void bcm2835_i2c_init_transfer(uint32_t base_addr, bool read) +{ + /* read flag is bit 0 so we can write it directly */ + int interrupt = read ? BCM2835_I2C_C_INTR : BCM2835_I2C_C_INTT; + + writel(base_addr + BCM2835_I2C_C, + BCM2835_I2C_C_I2CEN | BCM2835_I2C_C_INTD | + BCM2835_I2C_C_ST | BCM2835_I2C_C_CLEAR | interrupt | read); +} + +static void test_i2c_read_write(gconstpointer data) +{ + uint32_t i2cdata; + intptr_t index = (intptr_t) data; + uint32_t base_addr = bsc_base_addrs[index]; + + /* Write to TMP105 register */ + writel(base_addr + BCM2835_I2C_A, 0x50); + writel(base_addr + BCM2835_I2C_DLEN, 3); + + bcm2835_i2c_init_transfer(base_addr, 0); + + writel(base_addr + BCM2835_I2C_FIFO, TMP105_REG_T_HIGH); + writel(base_addr + BCM2835_I2C_FIFO, 0xde); + writel(base_addr + BCM2835_I2C_FIFO, 0xad); + + /* Clear flags */ + writel(base_addr + BCM2835_I2C_S, BCM2835_I2C_S_DONE | BCM2835_I2C_S_ERR | + BCM2835_I2C_S_CLKT); + + /* Read from TMP105 register */ + writel(base_addr + BCM2835_I2C_A, 0x50); + writel(base_addr + BCM2835_I2C_DLEN, 1); + + bcm2835_i2c_init_transfer(base_addr, 0); + + writel(base_addr + BCM2835_I2C_FIFO, TMP105_REG_T_HIGH); + + writel(base_addr + BCM2835_I2C_DLEN, 2); + bcm2835_i2c_init_transfer(base_addr, 1); + + i2cdata = readl(base_addr + BCM2835_I2C_FIFO); + g_assert_cmpint(i2cdata, ==, 0xde); + + i2cdata = readl(base_addr + BCM2835_I2C_FIFO); + g_assert_cmpint(i2cdata, ==, 0xad); + + /* Clear flags */ + writel(base_addr + BCM2835_I2C_S, BCM2835_I2C_S_DONE | BCM2835_I2C_S_ERR | + BCM2835_I2C_S_CLKT); + +} + +int main(int argc, char **argv) +{ + int ret; + int i; + + g_test_init(&argc, &argv, NULL); + + for (i = 0; i < 3; i++) { + g_autofree char *test_name = + g_strdup_printf("/bcm2835/bcm2835-i2c%d/read_write", i); + qtest_add_data_func(test_name, (void *)(intptr_t) i, + test_i2c_read_write); + } + + /* Run I2C tests with TMP105 slaves on all three buses */ + qtest_start("-M raspi3b " + "-device tmp105,address=0x50,bus=i2c-bus.0 " + "-device tmp105,address=0x50,bus=i2c-bus.1 " + "-device tmp105,address=0x50,bus=i2c-bus.2"); + ret = g_test_run(); + qtest_end(); + + return ret; +} diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index a4a46e97f0..d1ff4db7a2 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -24,9 +24,9 @@ * You will also notice that tests/qtest/bios-tables-test-allowed-diff.h lists * a bunch of files. This is your hint that you need to do the below: * 4. Run - * make check V=1 + * make check V=2 * this will produce a bunch of warnings about differences - * beween actual and expected ACPI tables. If you have IASL installed, + * between actual and expected ACPI tables. If you have IASL installed, * they will also be disassembled so you can look at the disassembled * output. If not - disassemble them yourself in any way you like. * Look at the differences - make sure they make sense and match what the @@ -78,6 +78,7 @@ typedef struct { bool tcg_only; const char *machine; + const char *machine_param; const char *variant; const char *uefi_fl1; const char *uefi_fl2; @@ -88,12 +89,17 @@ typedef struct { uint64_t rsdp_addr; uint8_t rsdp_table[36 /* ACPI 2.0+ RSDP size */]; GArray *tables; - uint32_t smbios_ep_addr; - struct smbios_21_entry_point smbios_ep_table; + uint64_t smbios_ep_addr[SMBIOS_ENTRY_POINT_TYPE__MAX]; + SmbiosEntryPoint smbios_ep_table; uint16_t smbios_cpu_max_speed; uint16_t smbios_cpu_curr_speed; + uint8_t smbios_core_count; + uint16_t smbios_core_count2; + uint8_t smbios_thread_count; + uint16_t smbios_thread_count2; uint8_t *required_struct_types; int required_struct_types_len; + int type4_count; QTestState *qts; } test_data; @@ -105,6 +111,9 @@ static const char *iasl = CONFIG_IASL; static const char *iasl; #endif +static int verbosity_level; +static GArray *load_expected_aml(test_data *data); + static bool compare_signature(const AcpiSdtTable *sdt, const char *signature) { return !memcmp(sdt->aml, signature, 4); @@ -236,21 +245,32 @@ static void test_acpi_fadt_table(test_data *data) static void dump_aml_files(test_data *data, bool rebuild) { - AcpiSdtTable *sdt; + AcpiSdtTable *sdt, *exp_sdt; GError *error = NULL; gchar *aml_file = NULL; + test_data exp_data = {}; gint fd; ssize_t ret; int i; + exp_data.tables = load_expected_aml(data); for (i = 0; i < data->tables->len; ++i) { const char *ext = data->variant ? data->variant : ""; sdt = &g_array_index(data->tables, AcpiSdtTable, i); + exp_sdt = &g_array_index(exp_data.tables, AcpiSdtTable, i); g_assert(sdt->aml); + g_assert(exp_sdt->aml); if (rebuild) { aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, sdt->aml, ext); + if (!g_file_test(aml_file, G_FILE_TEST_EXISTS) && + sdt->aml_len == exp_sdt->aml_len && + !memcmp(sdt->aml, exp_sdt->aml, sdt->aml_len)) { + /* identical tables, no need to write new files */ + g_free(aml_file); + continue; + } fd = g_open(aml_file, O_WRONLY|O_TRUNC|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH); if (fd < 0) { @@ -365,7 +385,7 @@ static GArray *load_expected_aml(test_data *data) gsize aml_len; GArray *exp_tables = g_array_new(false, true, sizeof(AcpiSdtTable)); - if (getenv("V")) { + if (verbosity_level >= 2) { fputc('\n', stderr); } for (i = 0; i < data->tables->len; ++i) { @@ -380,7 +400,7 @@ static GArray *load_expected_aml(test_data *data) try_again: aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, sdt->aml, ext); - if (getenv("V")) { + if (verbosity_level >= 2) { fprintf(stderr, "Looking for expected file '%s'\n", aml_file); } if (g_file_test(aml_file, G_FILE_TEST_EXISTS)) { @@ -392,7 +412,7 @@ try_again: goto try_again; } g_assert(exp_sdt.aml_file); - if (getenv("V")) { + if (verbosity_level >= 2) { fprintf(stderr, "Using expected file '%s'\n", aml_file); } ret = g_file_get_contents(aml_file, (gchar **)&exp_sdt.aml, @@ -433,10 +453,9 @@ static void test_acpi_asl(test_data *data) { int i; AcpiSdtTable *sdt, *exp_sdt; - test_data exp_data; + test_data exp_data = {}; gboolean exp_err, err, all_tables_match = true; - memset(&exp_data, 0, sizeof(exp_data)); exp_data.tables = load_expected_aml(data); dump_aml_files(data, false); for (i = 0; i < data->tables->len; ++i) { @@ -500,7 +519,7 @@ static void test_acpi_asl(test_data *data) exp_sdt->aml, sdt->asl_file, sdt->aml_file, exp_sdt->asl_file, exp_sdt->aml_file); fflush(stderr); - if (getenv("V")) { + if (verbosity_level >= 1) { const char *diff_env = getenv("DIFF"); const char *diff_cmd = diff_env ? diff_env : "diff -U 16"; char *diff = g_strdup_printf("%s %s %s", diff_cmd, @@ -533,10 +552,9 @@ static void test_acpi_asl(test_data *data) free_test_data(&exp_data); } -static bool smbios_ep_table_ok(test_data *data) +static bool smbios_ep2_table_ok(test_data *data, uint32_t addr) { - struct smbios_21_entry_point *ep_table = &data->smbios_ep_table; - uint32_t addr = data->smbios_ep_addr; + struct smbios_21_entry_point *ep_table = &data->smbios_ep_table.ep21; qtest_memread(data->qts, addr, ep_table, sizeof(*ep_table)); if (memcmp(ep_table->anchor_string, "_SM_", 4)) { @@ -559,13 +577,29 @@ static bool smbios_ep_table_ok(test_data *data) return true; } -static void test_smbios_entry_point(test_data *data) +static bool smbios_ep3_table_ok(test_data *data, uint64_t addr) +{ + struct smbios_30_entry_point *ep_table = &data->smbios_ep_table.ep30; + + qtest_memread(data->qts, addr, ep_table, sizeof(*ep_table)); + if (memcmp(ep_table->anchor_string, "_SM3_", 5)) { + return false; + } + + if (acpi_calc_checksum((uint8_t *)ep_table, sizeof *ep_table)) { + return false; + } + + return true; +} + +static SmbiosEntryPointType test_smbios_entry_point(test_data *data) { uint32_t off; /* find smbios entry point structure */ for (off = 0xf0000; off < 0x100000; off += 0x10) { - uint8_t sig[] = "_SM_"; + uint8_t sig[] = "_SM_", sig3[] = "_SM3_"; int i; for (i = 0; i < sizeof sig - 1; ++i) { @@ -574,14 +608,30 @@ static void test_smbios_entry_point(test_data *data) if (!memcmp(sig, "_SM_", sizeof sig)) { /* signature match, but is this a valid entry point? */ - data->smbios_ep_addr = off; - if (smbios_ep_table_ok(data)) { + if (smbios_ep2_table_ok(data, off)) { + data->smbios_ep_addr[SMBIOS_ENTRY_POINT_TYPE_32] = off; + } + } + + for (i = 0; i < sizeof sig3 - 1; ++i) { + sig3[i] = qtest_readb(data->qts, off + i); + } + + if (!memcmp(sig3, "_SM3_", sizeof sig3)) { + if (smbios_ep3_table_ok(data, off)) { + data->smbios_ep_addr[SMBIOS_ENTRY_POINT_TYPE_64] = off; + /* found 64-bit entry point, no need to look for 32-bit one */ break; } } } - g_assert_cmphex(off, <, 0x100000); + /* found at least one entry point */ + g_assert_true(data->smbios_ep_addr[SMBIOS_ENTRY_POINT_TYPE_32] || + data->smbios_ep_addr[SMBIOS_ENTRY_POINT_TYPE_64]); + + return data->smbios_ep_addr[SMBIOS_ENTRY_POINT_TYPE_64] ? + SMBIOS_ENTRY_POINT_TYPE_64 : SMBIOS_ENTRY_POINT_TYPE_32; } static inline bool smbios_single_instance(uint8_t type) @@ -600,41 +650,88 @@ static inline bool smbios_single_instance(uint8_t type) } } -static bool smbios_cpu_test(test_data *data, uint32_t addr) +static void smbios_cpu_test(test_data *data, uint32_t addr, + SmbiosEntryPointType ep_type) { - uint16_t expect_speed[2]; - uint16_t real; + uint8_t core_count, expected_core_count = data->smbios_core_count; + uint8_t thread_count, expected_thread_count = data->smbios_thread_count; + uint16_t speed, expected_speed[2]; + uint16_t core_count2, expected_core_count2 = data->smbios_core_count2; + uint16_t thread_count2, expected_thread_count2 = data->smbios_thread_count2; int offset[2]; int i; /* Check CPU speed for backward compatibility */ offset[0] = offsetof(struct smbios_type_4, max_speed); offset[1] = offsetof(struct smbios_type_4, current_speed); - expect_speed[0] = data->smbios_cpu_max_speed ? : 2000; - expect_speed[1] = data->smbios_cpu_curr_speed ? : 2000; + expected_speed[0] = data->smbios_cpu_max_speed ? : 2000; + expected_speed[1] = data->smbios_cpu_curr_speed ? : 2000; for (i = 0; i < 2; i++) { - real = qtest_readw(data->qts, addr + offset[i]); - if (real != expect_speed[i]) { - fprintf(stderr, "Unexpected SMBIOS CPU speed: real %u expect %u\n", - real, expect_speed[i]); - return false; - } + speed = qtest_readw(data->qts, addr + offset[i]); + g_assert_cmpuint(speed, ==, expected_speed[i]); } - return true; + core_count = qtest_readb(data->qts, + addr + offsetof(struct smbios_type_4, core_count)); + + if (expected_core_count) { + g_assert_cmpuint(core_count, ==, expected_core_count); + } + + thread_count = qtest_readb(data->qts, + addr + offsetof(struct smbios_type_4, thread_count)); + + if (expected_thread_count) { + g_assert_cmpuint(thread_count, ==, expected_thread_count); + } + + if (ep_type == SMBIOS_ENTRY_POINT_TYPE_64) { + core_count2 = qtest_readw(data->qts, + addr + offsetof(struct smbios_type_4, core_count2)); + + /* Core Count has reached its limit, checking Core Count 2 */ + if (expected_core_count == 0xFF && expected_core_count2) { + g_assert_cmpuint(core_count2, ==, expected_core_count2); + } + + thread_count2 = qtest_readw(data->qts, + addr + offsetof(struct smbios_type_4, + thread_count2)); + + /* Thread Count has reached its limit, checking Thread Count 2 */ + if (expected_thread_count == 0xFF && expected_thread_count2) { + g_assert_cmpuint(thread_count2, ==, expected_thread_count2); + } + } } -static void test_smbios_structs(test_data *data) +static void smbios_type4_count_test(test_data *data, int type4_count) +{ + int expected_type4_count = data->type4_count; + + if (expected_type4_count) { + g_assert_cmpuint(type4_count, ==, expected_type4_count); + } +} + +static void test_smbios_structs(test_data *data, SmbiosEntryPointType ep_type) { DECLARE_BITMAP(struct_bitmap, SMBIOS_MAX_TYPE+1) = { 0 }; - struct smbios_21_entry_point *ep_table = &data->smbios_ep_table; - uint32_t addr = le32_to_cpu(ep_table->structure_table_address); - int i, len, max_len = 0; + + SmbiosEntryPoint *ep_table = &data->smbios_ep_table; + int i = 0, len, max_len = 0, type4_count = 0; uint8_t type, prv, crt; + uint64_t addr; + + if (ep_type == SMBIOS_ENTRY_POINT_TYPE_32) { + addr = le32_to_cpu(ep_table->ep21.structure_table_address); + } else { + addr = le64_to_cpu(ep_table->ep30.structure_table_address); + } /* walk the smbios tables */ - for (i = 0; i < le16_to_cpu(ep_table->number_of_structures); i++) { + do { /* grab type and formatted area length from struct header */ type = qtest_readb(data->qts, addr); @@ -648,7 +745,8 @@ static void test_smbios_structs(test_data *data) set_bit(type, struct_bitmap); if (type == 4) { - g_assert(smbios_cpu_test(data, addr)); + smbios_cpu_test(data, addr, ep_type); + type4_count++; } /* seek to end of unformatted string area of this struct ("\0\0") */ @@ -660,29 +758,45 @@ static void test_smbios_structs(test_data *data) } /* keep track of max. struct size */ - if (max_len < len) { + if (ep_type == SMBIOS_ENTRY_POINT_TYPE_32 && max_len < len) { max_len = len; - g_assert_cmpuint(max_len, <=, ep_table->max_structure_size); + g_assert_cmpuint(max_len, <=, ep_table->ep21.max_structure_size); } /* start of next structure */ addr += len; - } - /* total table length and max struct size must match entry point values */ - g_assert_cmpuint(le16_to_cpu(ep_table->structure_table_length), ==, - addr - le32_to_cpu(ep_table->structure_table_address)); - g_assert_cmpuint(le16_to_cpu(ep_table->max_structure_size), ==, max_len); + /* + * Until all structures have been scanned (ep21) + * or an EOF structure is found (ep30) + */ + } while (ep_type == SMBIOS_ENTRY_POINT_TYPE_32 ? + ++i < le16_to_cpu(ep_table->ep21.number_of_structures) : + type != 127); + + if (ep_type == SMBIOS_ENTRY_POINT_TYPE_32) { + /* + * Total table length and max struct size + * must match entry point values + */ + g_assert_cmpuint(le16_to_cpu(ep_table->ep21.structure_table_length), ==, + addr - le32_to_cpu(ep_table->ep21.structure_table_address)); + + g_assert_cmpuint(le16_to_cpu(ep_table->ep21.max_structure_size), ==, + max_len); + } /* required struct types must all be present */ for (i = 0; i < data->required_struct_types_len; i++) { g_assert(test_bit(data->required_struct_types[i], struct_bitmap)); } + + smbios_type4_count_test(data, type4_count); } -static void test_acpi_load_tables(test_data *data, bool use_uefi) +static void test_acpi_load_tables(test_data *data) { - if (use_uefi) { + if (data->uefi_fl1 && data->uefi_fl2) { /* use UEFI */ g_assert(data->scan_len); data->rsdp_addr = acpi_find_rsdp_address_uefi(data->qts, data->ram_start, data->scan_len); @@ -698,51 +812,76 @@ static void test_acpi_load_tables(test_data *data, bool use_uefi) test_acpi_fadt_table(data); } -static char *test_acpi_create_args(test_data *data, const char *params, - bool use_uefi) +static char *test_acpi_create_args(test_data *data, const char *params) { char *args; - if (use_uefi) { + if (data->uefi_fl1 && data->uefi_fl2) { /* use UEFI */ /* * TODO: convert '-drive if=pflash' to new syntax (see e33763be7cd3) * when arm/virt boad starts to support it. */ if (data->cd) { - args = g_strdup_printf("-machine %s %s -accel tcg " + args = g_strdup_printf("-machine %s%s %s -accel tcg " "-nodefaults -nographic " "-drive if=pflash,format=raw,file=%s,readonly=on " "-drive if=pflash,format=raw,file=%s,snapshot=on -cdrom %s %s", - data->machine, data->tcg_only ? "" : "-accel kvm", + data->machine, data->machine_param ?: "", + data->tcg_only ? "" : "-accel kvm", data->uefi_fl1, data->uefi_fl2, data->cd, params ? params : ""); } else { - args = g_strdup_printf("-machine %s %s -accel tcg " + args = g_strdup_printf("-machine %s%s %s -accel tcg " "-nodefaults -nographic " "-drive if=pflash,format=raw,file=%s,readonly=on " "-drive if=pflash,format=raw,file=%s,snapshot=on %s", - data->machine, data->tcg_only ? "" : "-accel kvm", + data->machine, data->machine_param ?: "", + data->tcg_only ? "" : "-accel kvm", data->uefi_fl1, data->uefi_fl2, params ? params : ""); } } else { - args = g_strdup_printf("-machine %s %s -accel tcg " - "-net none -display none %s " + args = g_strdup_printf("-machine %s%s %s -accel tcg " + "-net none %s " "-drive id=hd0,if=none,file=%s,format=raw " "-device %s,drive=hd0 ", - data->machine, data->tcg_only ? "" : "-accel kvm", + data->machine, data->machine_param ?: "", + data->tcg_only ? "" : "-accel kvm", params ? params : "", disk, data->blkdev ?: "ide-hd"); } return args; } -static void test_acpi_one(const char *params, test_data *data) +static void test_vm_prepare(const char *params, test_data *data) { - char *args; - bool use_uefi = data->uefi_fl1 && data->uefi_fl2; - - args = test_acpi_create_args(data, params, use_uefi); + char *args = test_acpi_create_args(data, params); data->qts = qtest_init(args); - test_acpi_load_tables(data, use_uefi); + g_free(args); +} + +static void process_smbios_tables_noexit(test_data *data) +{ + /* + * TODO: make SMBIOS tests work with UEFI firmware, + * Bug on uefi-test-tools to provide entry point: + * https://bugs.launchpad.net/qemu/+bug/1821884 + */ + if (!(data->uefi_fl1 && data->uefi_fl2)) { + SmbiosEntryPointType ep_type = test_smbios_entry_point(data); + test_smbios_structs(data, ep_type); + } +} + +static void test_smbios(const char *params, test_data *data) +{ + test_vm_prepare(params, data); + boot_sector_test(data->qts); + process_smbios_tables_noexit(data); + qtest_quit(data->qts); +} + +static void process_acpi_tables_noexit(test_data *data) +{ + test_acpi_load_tables(data); if (getenv(ACPI_REBUILD_EXPECTED_AML)) { dump_aml_files(data, true); @@ -750,18 +889,19 @@ static void test_acpi_one(const char *params, test_data *data) test_acpi_asl(data); } - /* - * TODO: make SMBIOS tests work with UEFI firmware, - * Bug on uefi-test-tools to provide entry point: - * https://bugs.launchpad.net/qemu/+bug/1821884 - */ - if (!use_uefi) { - test_smbios_entry_point(data); - test_smbios_structs(data); - } + process_smbios_tables_noexit(data); +} +static void process_acpi_tables(test_data *data) +{ + process_acpi_tables_noexit(data); qtest_quit(data->qts); - g_free(args); +} + +static void test_acpi_one(const char *params, test_data *data) +{ + test_vm_prepare(params, data); + process_acpi_tables(data); } static uint8_t base_required_struct_types[] = { @@ -770,12 +910,11 @@ static uint8_t base_required_struct_types[] = { static void test_acpi_piix4_tcg(void) { - test_data data; + test_data data = {}; /* Supplying -machine accel argument overrides the default (qtest). * This is to make guest actually run. */ - memset(&data, 0, sizeof(data)); data.machine = MACHINE_PC; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); @@ -785,65 +924,98 @@ static void test_acpi_piix4_tcg(void) static void test_acpi_piix4_tcg_bridge(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_PC; data.variant = ".bridge"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); - test_acpi_one("-device pci-bridge,chassis_nr=1", &data); + test_vm_prepare("-S" + " -device pci-bridge,chassis_nr=1" + " -device pci-bridge,bus=pci.1,addr=1.0,chassis_nr=2" + " -device pci-testdev,bus=pci.0,addr=5.0" + " -device pci-testdev,bus=pci.1", &data); + + /* hotplugged bridges section */ + qtest_qmp_device_add(data.qts, "pci-bridge", "hpbr", + "{'bus': 'pci.1', 'addr': '2.0', 'chassis_nr': 3 }"); + qtest_qmp_device_add(data.qts, "pci-bridge", "hpbr_multifunc", + "{'bus': 'pci.1', 'addr': '0xf.1', 'chassis_nr': 4 }"); + qtest_qmp_device_add(data.qts, "pci-bridge", "hpbrhost", + "{'bus': 'pci.0', 'addr': '4.0', 'chassis_nr': 5 }"); + qtest_qmp_device_add(data.qts, "pci-testdev", "d1", "{'bus': 'pci.0' }"); + qtest_qmp_device_add(data.qts, "pci-testdev", "d2", "{'bus': 'pci.1' }"); + qtest_qmp_device_add(data.qts, "pci-testdev", "d3", "{'bus': 'hpbr', " + "'addr': '1.0' }"); + qtest_qmp_send(data.qts, "{'execute':'cont' }"); + qtest_qmp_eventwait(data.qts, "RESUME"); + + process_acpi_tables_noexit(&data); + free_test_data(&data); + + /* check that reboot/reset doesn't change any ACPI tables */ + qtest_qmp_send(data.qts, "{'execute':'system_reset' }"); + process_acpi_tables(&data); free_test_data(&data); } static void test_acpi_piix4_no_root_hotplug(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_PC; data.variant = ".roothp"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); test_acpi_one("-global PIIX4_PM.acpi-root-pci-hotplug=off " - "-device pci-bridge,chassis_nr=1", &data); + "-device pci-bridge,chassis_nr=1 " + "-device pci-bridge,bus=pci.1,addr=1.0,chassis_nr=2 " + "-device pci-testdev,bus=pci.0 " + "-device pci-testdev,bus=pci.1", &data); free_test_data(&data); } static void test_acpi_piix4_no_bridge_hotplug(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_PC; data.variant = ".hpbridge"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); test_acpi_one("-global PIIX4_PM.acpi-pci-hotplug-with-bridge-support=off " - "-device pci-bridge,chassis_nr=1", &data); + "-device pci-bridge,chassis_nr=1 " + "-device pci-bridge,bus=pci.1,addr=1.0,chassis_nr=2 " + "-device pci-testdev,bus=pci.0 " + "-device pci-testdev,bus=pci.1,addr=2.0", &data); free_test_data(&data); } static void test_acpi_piix4_no_acpi_pci_hotplug(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_PC; data.variant = ".hpbrroot"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); test_acpi_one("-global PIIX4_PM.acpi-root-pci-hotplug=off " "-global PIIX4_PM.acpi-pci-hotplug-with-bridge-support=off " - "-device pci-bridge,chassis_nr=1", &data); + "-device pci-bridge,chassis_nr=1,addr=4.0 " + "-device pci-testdev,bus=pci.0,addr=5.0 " + "-device pci-testdev,bus=pci.0,addr=6.0,acpi-index=101 " + "-device pci-testdev,bus=pci.1,addr=1.0 " + "-device pci-testdev,bus=pci.1,addr=2.0,acpi-index=201 " + "-device pci-bridge,id=nhpbr,chassis_nr=2,shpc=off,addr=7.0 " + "-device pci-testdev,bus=nhpbr,addr=1.0,acpi-index=301 " + , &data); free_test_data(&data); } static void test_acpi_q35_tcg(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); @@ -856,17 +1028,130 @@ static void test_acpi_q35_tcg(void) free_test_data(&data); } +static void test_acpi_q35_kvm_type4_count(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".type4-count", + .required_struct_types = base_required_struct_types, + .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), + .type4_count = 5, + }; + + test_acpi_one("-machine smbios-entry-point-type=64 " + "-smp cpus=100,maxcpus=120,sockets=5," + "dies=2,cores=4,threads=3", &data); + free_test_data(&data); +} + +static void test_acpi_q35_kvm_core_count(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".core-count", + .required_struct_types = base_required_struct_types, + .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), + .smbios_core_count = 9, + .smbios_core_count2 = 9, + }; + + test_acpi_one("-machine smbios-entry-point-type=64 " + "-smp 54,sockets=2,dies=3,cores=3,threads=3", + &data); + free_test_data(&data); +} + +static void test_acpi_q35_kvm_core_count2(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".core-count2", + .required_struct_types = base_required_struct_types, + .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), + .smbios_core_count = 0xFF, + .smbios_core_count2 = 260, + }; + + test_acpi_one("-machine smbios-entry-point-type=64 " + "-smp 260,dies=2,cores=130,threads=1", + &data); + free_test_data(&data); +} + +static void test_acpi_q35_kvm_thread_count(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".thread-count", + .required_struct_types = base_required_struct_types, + .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), + .smbios_thread_count = 27, + .smbios_thread_count2 = 27, + }; + + test_acpi_one("-machine smbios-entry-point-type=64 " + "-smp cpus=15,maxcpus=54,sockets=2,dies=3,cores=3,threads=3", + &data); + free_test_data(&data); +} + +static void test_acpi_q35_kvm_thread_count2(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".thread-count2", + .required_struct_types = base_required_struct_types, + .required_struct_types_len = ARRAY_SIZE(base_required_struct_types), + .smbios_thread_count = 0xFF, + .smbios_thread_count2 = 260, + }; + + test_acpi_one("-machine smbios-entry-point-type=64 " + "-smp cpus=210,maxcpus=260,dies=2,cores=65,threads=2", + &data); + free_test_data(&data); +} + static void test_acpi_q35_tcg_bridge(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; data.variant = ".bridge"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); - test_acpi_one("-device pci-bridge,chassis_nr=1", - &data); + test_acpi_one("-device pci-bridge,chassis_nr=1,id=br1" + " -device pci-testdev,bus=pcie.0" + " -device pci-testdev,bus=br1", &data); + free_test_data(&data); +} + +static void test_acpi_q35_tcg_no_acpi_hotplug(void) +{ + test_data data = {}; + + data.machine = MACHINE_Q35; + data.variant = ".noacpihp"; + data.required_struct_types = base_required_struct_types; + data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); + test_acpi_one("-global ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off" + " -device pci-testdev,bus=pcie.0,acpi-index=101,addr=3.0" + " -device pci-bridge,chassis_nr=1,id=shpcbr,addr=4.0" + " -device pci-testdev,bus=shpcbr,addr=1.0,acpi-index=201" + " -device pci-bridge,chassis_nr=2,shpc=off,id=noshpcbr,addr=5.0" + " -device pci-testdev,bus=noshpcbr,addr=1.0,acpi-index=301" + " -device pcie-root-port,id=hprp,port=0x0,chassis=1,addr=6.0" + " -device pci-testdev,bus=hprp,acpi-index=401" + " -device pcie-root-port,id=nohprp,port=0x0,chassis=2,hotplug=off," + "addr=7.0" + " -device pci-testdev,bus=nohprp,acpi-index=501" + " -device pcie-root-port,id=nohprpint,port=0x0,chassis=3,hotplug=off," + "multifunction=on,addr=8.0" + " -device pci-testdev,bus=nohprpint,acpi-index=601,addr=0.1" + " -device pcie-root-port,id=hprp2,port=0x0,chassis=4,bus=nohprpint," + "addr=0.2" + " -device pci-testdev,bus=hprp2,acpi-index=602" + , &data); free_test_data(&data); } @@ -876,14 +1161,46 @@ static void test_acpi_q35_multif_bridge(void) .machine = MACHINE_Q35, .variant = ".multi-bridge", }; - test_acpi_one("-device pcie-root-port,id=pcie-root-port-0," - "multifunction=on," - "port=0x0,chassis=1,addr=0x2,bus=pcie.0 " - "-device pcie-root-port,id=pcie-root-port-1," - "port=0x1,chassis=2,addr=0x3.0x1,bus=pcie.0 " - "-device virtio-balloon,id=balloon0," - "bus=pcie.0,addr=0x4.0x2", - &data); + test_vm_prepare("-S" + " -device virtio-balloon,id=balloon0,addr=0x4.0x2" + " -device pcie-root-port,id=rp0,multifunction=on," + "port=0x0,chassis=1,addr=0x2" + " -device pcie-root-port,id=rp1,port=0x1,chassis=2,addr=0x3.0x1" + " -device pcie-root-port,id=rp2,port=0x0,chassis=3,bus=rp1,addr=0.0" + " -device pci-bridge,bus=rp2,chassis_nr=4,id=br1" + " -device pcie-root-port,id=rphptgt1,port=0x0,chassis=5,addr=2.1" + " -device pcie-root-port,id=rphptgt2,port=0x0,chassis=6,addr=2.2" + " -device pcie-root-port,id=rphptgt3,port=0x0,chassis=7,addr=2.3" + " -device pci-testdev,bus=pcie.0,addr=2.4" + " -device pci-testdev,bus=pcie.0,addr=2.5,acpi-index=102" + " -device pci-testdev,bus=pcie.0,addr=5.0" + " -device pci-testdev,bus=pcie.0,addr=0xf.0,acpi-index=101" + " -device pci-testdev,bus=rp0,addr=0.0" + " -device pci-testdev,bus=br1" + " -device pcie-root-port,id=rpnohp,chassis=8,addr=0xA.0,hotplug=off" + " -device pcie-root-port,id=rp3,chassis=9,bus=rpnohp" + , &data); + + /* hotplugged bridges section */ + qtest_qmp_device_add(data.qts, "pci-bridge", "hpbr1", + "{'bus': 'br1', 'addr': '6.0', 'chassis_nr': 128 }"); + qtest_qmp_device_add(data.qts, "pci-bridge", "hpbr2-multiif", + "{ 'bus': 'br1', 'addr': '2.2', 'chassis_nr': 129 }"); + qtest_qmp_device_add(data.qts, "pcie-pci-bridge", "hpbr3", + "{'bus': 'rphptgt1', 'addr': '0.0' }"); + qtest_qmp_device_add(data.qts, "pcie-root-port", "hprp", + "{'bus': 'rphptgt2', 'addr': '0.0' }"); + qtest_qmp_device_add(data.qts, "pci-testdev", "hpnic", + "{'bus': 'rphptgt3', 'addr': '0.0' }"); + qtest_qmp_send(data.qts, "{'execute':'cont' }"); + qtest_qmp_eventwait(data.qts, "RESUME"); + + process_acpi_tables_noexit(&data); + free_test_data(&data); + + /* check that reboot/reset doesn't change any ACPI tables */ + qtest_qmp_send(data.qts, "{'execute':'system_reset' }"); + process_acpi_tables(&data); free_test_data(&data); } @@ -892,11 +1209,13 @@ static void test_acpi_q35_tcg_mmio64(void) test_data data = { .machine = MACHINE_Q35, .variant = ".mmio64", + .tcg_only = true, .required_struct_types = base_required_struct_types, .required_struct_types_len = ARRAY_SIZE(base_required_struct_types) }; test_acpi_one("-m 128M,slots=1,maxmem=2G " + "-cpu Opteron_G1 " "-object memory-backend-ram,id=ram0,size=128M " "-numa node,memdev=ram0 " "-device pci-testdev,membar=2G", @@ -906,9 +1225,8 @@ static void test_acpi_q35_tcg_mmio64(void) static void test_acpi_piix4_tcg_cphp(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_PC; data.variant = ".cphp"; test_acpi_one("-smp 2,cores=3,sockets=2,maxcpus=6" @@ -922,9 +1240,8 @@ static void test_acpi_piix4_tcg_cphp(void) static void test_acpi_q35_tcg_cphp(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; data.variant = ".cphp"; test_acpi_one(" -smp 2,cores=3,sockets=2,maxcpus=6" @@ -942,9 +1259,8 @@ static uint8_t ipmi_required_struct_types[] = { static void test_acpi_q35_tcg_ipmi(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; data.variant = ".ipmibt"; data.required_struct_types = ipmi_required_struct_types; @@ -955,14 +1271,27 @@ static void test_acpi_q35_tcg_ipmi(void) free_test_data(&data); } +static void test_acpi_q35_tcg_smbus_ipmi(void) +{ + test_data data = {}; + + data.machine = MACHINE_Q35; + data.variant = ".ipmismbus"; + data.required_struct_types = ipmi_required_struct_types; + data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types); + test_acpi_one("-device ipmi-bmc-sim,id=bmc0" + " -device smbus-ipmi,bmc=bmc0", + &data); + free_test_data(&data); +} + static void test_acpi_piix4_tcg_ipmi(void) { - test_data data; + test_data data = {}; /* Supplying -machine accel argument overrides the default (qtest). * This is to make guest actually run. */ - memset(&data, 0, sizeof(data)); data.machine = MACHINE_PC; data.variant = ".ipmikcs"; data.required_struct_types = ipmi_required_struct_types; @@ -975,9 +1304,8 @@ static void test_acpi_piix4_tcg_ipmi(void) static void test_acpi_q35_tcg_memhp(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; data.variant = ".memhp"; test_acpi_one(" -m 128,slots=3,maxmem=1G" @@ -991,9 +1319,8 @@ static void test_acpi_q35_tcg_memhp(void) static void test_acpi_piix4_tcg_memhp(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_PC; data.variant = ".memhp"; test_acpi_one(" -m 128,slots=3,maxmem=1G" @@ -1007,9 +1334,8 @@ static void test_acpi_piix4_tcg_memhp(void) static void test_acpi_piix4_tcg_nosmm(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_PC; data.variant = ".nosmm"; test_acpi_one("-machine smm=off", &data); @@ -1018,9 +1344,8 @@ static void test_acpi_piix4_tcg_nosmm(void) static void test_acpi_piix4_tcg_smm_compat(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_PC; data.variant = ".smm-compat"; test_acpi_one("-global PIIX4_PM.smm-compat=on", &data); @@ -1029,9 +1354,8 @@ static void test_acpi_piix4_tcg_smm_compat(void) static void test_acpi_piix4_tcg_smm_compat_nosmm(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_PC; data.variant = ".smm-compat-nosmm"; test_acpi_one("-global PIIX4_PM.smm-compat=on -machine smm=off", &data); @@ -1040,20 +1364,19 @@ static void test_acpi_piix4_tcg_smm_compat_nosmm(void) static void test_acpi_piix4_tcg_nohpet(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_PC; + data.machine_param = ",hpet=off"; data.variant = ".nohpet"; - test_acpi_one("-no-hpet", &data); + test_acpi_one(NULL, &data); free_test_data(&data); } static void test_acpi_q35_tcg_numamem(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; data.variant = ".numamem"; test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" @@ -1063,9 +1386,8 @@ static void test_acpi_q35_tcg_numamem(void) static void test_acpi_q35_kvm_xapic(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; data.variant = ".xapic"; test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" @@ -1076,9 +1398,8 @@ static void test_acpi_q35_kvm_xapic(void) static void test_acpi_q35_tcg_nosmm(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; data.variant = ".nosmm"; test_acpi_one("-machine smm=off", &data); @@ -1087,9 +1408,8 @@ static void test_acpi_q35_tcg_nosmm(void) static void test_acpi_q35_tcg_smm_compat(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; data.variant = ".smm-compat"; test_acpi_one("-global ICH9-LPC.smm-compat=on", &data); @@ -1098,9 +1418,8 @@ static void test_acpi_q35_tcg_smm_compat(void) static void test_acpi_q35_tcg_smm_compat_nosmm(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; data.variant = ".smm-compat-nosmm"; test_acpi_one("-global ICH9-LPC.smm-compat=on -machine smm=off", &data); @@ -1109,20 +1428,19 @@ static void test_acpi_q35_tcg_smm_compat_nosmm(void) static void test_acpi_q35_tcg_nohpet(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; + data.machine_param = ",hpet=off"; data.variant = ".nohpet"; - test_acpi_one("-no-hpet", &data); + test_acpi_one(NULL, &data); free_test_data(&data); } static void test_acpi_q35_kvm_dmar(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; data.variant = ".dmar"; test_acpi_one("-machine kernel-irqchip=split -accel kvm" @@ -1132,9 +1450,8 @@ static void test_acpi_q35_kvm_dmar(void) static void test_acpi_q35_tcg_ivrs(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; data.variant = ".ivrs"; data.tcg_only = true, @@ -1144,9 +1461,8 @@ static void test_acpi_q35_tcg_ivrs(void) static void test_acpi_piix4_tcg_numamem(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_PC; data.variant = ".numamem"; test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" @@ -1163,7 +1479,7 @@ static void test_acpi_tcg_tpm(const char *machine, const char *tpm_if, machine, tpm_if); char *tmp_path = g_dir_make_tmp(tmp_dir_name, NULL); TPMTestState test; - test_data data; + test_data data = {}; GThread *thread; const char *suffix = tpm_version == TPM_VERSION_2_0 ? "tpm2" : "tpm12"; char *args, *variant = g_strdup_printf(".%s.%s", tpm_if, suffix); @@ -1183,7 +1499,6 @@ static void test_acpi_tcg_tpm(const char *machine, const char *tpm_if, thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test); tpm_emu_test_wait_cond(&test); - memset(&data, 0, sizeof(data)); data.machine = machine; data.variant = variant; @@ -1218,9 +1533,8 @@ static void test_acpi_q35_tcg_tpm12_tis(void) static void test_acpi_tcg_dimm_pxm(const char *machine) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = machine; data.variant = ".dimmpxm"; test_acpi_one(" -machine nvdimm=on,nvdimm-persistence=cpu" @@ -1288,7 +1602,6 @@ static void test_acpi_virt_tcg_memhp(void) static void test_acpi_microvm_prepare(test_data *data) { - memset(data, 0, sizeof(*data)); data->machine = "microvm"; data->required_struct_types = NULL; /* no smbios */ data->required_struct_types_len = 0; @@ -1297,7 +1610,7 @@ static void test_acpi_microvm_prepare(test_data *data) static void test_acpi_microvm_tcg(void) { - test_data data; + test_data data = {}; test_acpi_microvm_prepare(&data); test_acpi_one(" -machine microvm,acpi=on,ioapic2=off,rtc=off", @@ -1307,7 +1620,7 @@ static void test_acpi_microvm_tcg(void) static void test_acpi_microvm_usb_tcg(void) { - test_data data; + test_data data = {}; test_acpi_microvm_prepare(&data); data.variant = ".usb"; @@ -1318,7 +1631,7 @@ static void test_acpi_microvm_usb_tcg(void) static void test_acpi_microvm_rtc_tcg(void) { - test_data data; + test_data data = {}; test_acpi_microvm_prepare(&data); data.variant = ".rtc"; @@ -1329,7 +1642,7 @@ static void test_acpi_microvm_rtc_tcg(void) static void test_acpi_microvm_pcie_tcg(void) { - test_data data; + test_data data = {}; test_acpi_microvm_prepare(&data); data.variant = ".pcie"; @@ -1341,7 +1654,7 @@ static void test_acpi_microvm_pcie_tcg(void) static void test_acpi_microvm_ioapic2_tcg(void) { - test_data data; + test_data data = {}; test_acpi_microvm_prepare(&data); data.variant = ".ioapic2"; @@ -1406,9 +1719,8 @@ static void test_acpi_virt_tcg_pxb(void) static void test_acpi_tcg_acpi_hmat(const char *machine) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = machine; data.variant = ".acpihmat"; test_acpi_one(" -machine hmat=on" @@ -1446,13 +1758,117 @@ static void test_acpi_piix4_tcg_acpi_hmat(void) test_acpi_tcg_acpi_hmat(MACHINE_PC); } +static void test_acpi_virt_tcg_acpi_hmat(void) +{ + test_data data = { + .machine = "virt", + .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", + .ram_start = 0x40000000ULL, + .scan_len = 128ULL * 1024 * 1024, + }; + + data.variant = ".acpihmatvirt"; + + test_acpi_one(" -machine hmat=on" + " -cpu cortex-a57" + " -smp 4,sockets=2" + " -m 384M" + " -object memory-backend-ram,size=128M,id=ram0" + " -object memory-backend-ram,size=128M,id=ram1" + " -object memory-backend-ram,size=128M,id=ram2" + " -numa node,nodeid=0,memdev=ram0" + " -numa node,nodeid=1,memdev=ram1" + " -numa node,nodeid=2,memdev=ram2" + " -numa cpu,node-id=0,socket-id=0" + " -numa cpu,node-id=0,socket-id=0" + " -numa cpu,node-id=1,socket-id=1" + " -numa cpu,node-id=1,socket-id=1" + " -numa hmat-lb,initiator=0,target=0,hierarchy=memory," + "data-type=access-latency,latency=10" + " -numa hmat-lb,initiator=0,target=0,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=10485760" + " -numa hmat-lb,initiator=0,target=1,hierarchy=memory," + "data-type=access-latency,latency=20" + " -numa hmat-lb,initiator=0,target=1,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=5242880" + " -numa hmat-lb,initiator=0,target=2,hierarchy=memory," + "data-type=access-latency,latency=30" + " -numa hmat-lb,initiator=0,target=2,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=1048576" + " -numa hmat-lb,initiator=1,target=0,hierarchy=memory," + "data-type=access-latency,latency=20" + " -numa hmat-lb,initiator=1,target=0,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=5242880" + " -numa hmat-lb,initiator=1,target=1,hierarchy=memory," + "data-type=access-latency,latency=10" + " -numa hmat-lb,initiator=1,target=1,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=10485760" + " -numa hmat-lb,initiator=1,target=2,hierarchy=memory," + "data-type=access-latency,latency=30" + " -numa hmat-lb,initiator=1,target=2,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=1048576", + &data); + + free_test_data(&data); +} + +static void test_acpi_q35_tcg_acpi_hmat_noinitiator(void) +{ + test_data data = {}; + + data.machine = MACHINE_Q35; + data.variant = ".acpihmat-noinitiator"; + test_acpi_one(" -machine hmat=on" + " -smp 4,sockets=2" + " -m 128M" + " -object memory-backend-ram,size=32M,id=ram0" + " -object memory-backend-ram,size=32M,id=ram1" + " -object memory-backend-ram,size=64M,id=ram2" + " -numa node,nodeid=0,memdev=ram0" + " -numa node,nodeid=1,memdev=ram1" + " -numa node,nodeid=2,memdev=ram2" + " -numa cpu,node-id=0,socket-id=0" + " -numa cpu,node-id=0,socket-id=0" + " -numa cpu,node-id=1,socket-id=1" + " -numa cpu,node-id=1,socket-id=1" + " -numa hmat-lb,initiator=0,target=0,hierarchy=memory," + "data-type=access-latency,latency=10" + " -numa hmat-lb,initiator=0,target=0,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=10485760" + " -numa hmat-lb,initiator=0,target=1,hierarchy=memory," + "data-type=access-latency,latency=20" + " -numa hmat-lb,initiator=0,target=1,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=5242880" + " -numa hmat-lb,initiator=0,target=2,hierarchy=memory," + "data-type=access-latency,latency=30" + " -numa hmat-lb,initiator=0,target=2,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=1048576" + " -numa hmat-lb,initiator=1,target=0,hierarchy=memory," + "data-type=access-latency,latency=20" + " -numa hmat-lb,initiator=1,target=0,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=5242880" + " -numa hmat-lb,initiator=1,target=1,hierarchy=memory," + "data-type=access-latency,latency=10" + " -numa hmat-lb,initiator=1,target=1,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=10485760" + " -numa hmat-lb,initiator=1,target=2,hierarchy=memory," + "data-type=access-latency,latency=30" + " -numa hmat-lb,initiator=1,target=2,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=1048576", + &data); + free_test_data(&data); +} + +#ifdef CONFIG_POSIX static void test_acpi_erst(const char *machine) { gchar *tmp_path = g_dir_make_tmp("qemu-test-erst.XXXXXX", NULL); gchar *params; - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = machine; data.variant = ".acpierst"; params = g_strdup_printf( @@ -1480,7 +1896,7 @@ static void test_acpi_microvm_acpi_erst(void) { gchar *tmp_path = g_dir_make_tmp("qemu-test-erst.XXXXXX", NULL); gchar *params; - test_data data; + test_data data = {}; test_acpi_microvm_prepare(&data); data.variant = ".pcie"; @@ -1496,6 +1912,7 @@ static void test_acpi_microvm_acpi_erst(void) g_free(tmp_path); free_test_data(&data); } +#endif /* CONFIG_POSIX */ static void test_acpi_virt_tcg(void) { @@ -1516,6 +1933,24 @@ static void test_acpi_virt_tcg(void) free_test_data(&data); } +static void test_acpi_virt_tcg_topology(void) +{ + test_data data = { + .machine = "virt", + .variant = ".topology", + .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", + .ram_start = 0x40000000ULL, + .scan_len = 128ULL * 1024 * 1024, + }; + + test_acpi_one("-cpu cortex-a57 " + "-smp sockets=1,clusters=2,cores=2,threads=2", &data); + free_test_data(&data); +} + static void test_acpi_q35_viot(void) { test_data data = { @@ -1536,6 +1971,7 @@ static void test_acpi_q35_viot(void) free_test_data(&data); } +#ifdef CONFIG_POSIX static void test_acpi_q35_cxl(void) { gchar *tmp_path = g_dir_make_tmp("qemu-test-cxl.XXXXXX", NULL); @@ -1560,15 +1996,15 @@ static void test_acpi_q35_cxl(void) " -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1" " -device pxb-cxl,bus_nr=222,bus=pcie.0,id=cxl.2" " -device cxl-rp,port=0,bus=cxl.1,id=rp1,chassis=0,slot=2" - " -device cxl-type3,bus=rp1,memdev=cxl-mem1,lsa=lsa1" + " -device cxl-type3,bus=rp1,persistent-memdev=cxl-mem1,lsa=lsa1" " -device cxl-rp,port=1,bus=cxl.1,id=rp2,chassis=0,slot=3" - " -device cxl-type3,bus=rp2,memdev=cxl-mem2,lsa=lsa2" + " -device cxl-type3,bus=rp2,persistent-memdev=cxl-mem2,lsa=lsa2" " -device cxl-rp,port=0,bus=cxl.2,id=rp3,chassis=0,slot=5" - " -device cxl-type3,bus=rp3,memdev=cxl-mem3,lsa=lsa3" + " -device cxl-type3,bus=rp3,persistent-memdev=cxl-mem3,lsa=lsa3" " -device cxl-rp,port=1,bus=cxl.2,id=rp4,chassis=0,slot=6" - " -device cxl-type3,bus=rp4,memdev=cxl-mem4,lsa=lsa4" - " -cxl-fixed-memory-window targets.0=cxl.1,size=4G,interleave-granularity=8k" - " -cxl-fixed-memory-window targets.0=cxl.1,targets.1=cxl.2,size=4G,interleave-granularity=8k", + " -device cxl-type3,bus=rp4,persistent-memdev=cxl-mem4,lsa=lsa4" + " -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=8k," + "cxl-fmw.1.targets.0=cxl.1,cxl-fmw.1.targets.1=cxl.2,cxl-fmw.1.size=4G,cxl-fmw.1.interleave-granularity=8k", tmp_path, tmp_path, tmp_path, tmp_path, tmp_path, tmp_path, tmp_path, tmp_path); test_acpi_one(params, &data); @@ -1578,6 +2014,7 @@ static void test_acpi_q35_cxl(void) g_free(tmp_path); free_test_data(&data); } +#endif /* CONFIG_POSIX */ static void test_acpi_virt_viot(void) { @@ -1596,6 +2033,12 @@ static void test_acpi_virt_viot(void) free_test_data(&data); } +#ifndef _WIN32 +# define DEV_NULL "/dev/null" +#else +# define DEV_NULL "nul" +#endif + static void test_acpi_q35_slic(void) { test_data data = { @@ -1603,13 +2046,81 @@ static void test_acpi_q35_slic(void) .variant = ".slic", }; - test_acpi_one("-acpitable sig=SLIC,oem_id='CRASH ',oem_table_id='ME'," - "oem_rev=00002210,asl_compiler_id='qemu'," - "asl_compiler_rev=00000000,data=/dev/null", + test_acpi_one("-acpitable sig=SLIC,oem_id=\"CRASH \",oem_table_id=ME," + "oem_rev=00002210,asl_compiler_id=qemu," + "asl_compiler_rev=00000000,data=" DEV_NULL, &data); free_test_data(&data); } +static void test_acpi_q35_applesmc(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".applesmc", + }; + + /* supply fake 64-byte OSK to silence missing key warning */ + test_acpi_one("-device isa-applesmc,osk=any64characterfakeoskisenough" + "topreventinvalidkeywarningsonstderr", &data); + free_test_data(&data); +} + +static void test_acpi_q35_pvpanic_isa(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".pvpanic-isa", + }; + + test_acpi_one("-device pvpanic", &data); + free_test_data(&data); +} + +static void test_acpi_pc_smbios_options(void) +{ + uint8_t req_type11[] = { 11 }; + test_data data = { + .machine = MACHINE_PC, + .variant = ".pc_smbios_options", + .required_struct_types = req_type11, + .required_struct_types_len = ARRAY_SIZE(req_type11), + }; + + test_smbios("-smbios type=11,value=TEST", &data); + free_test_data(&data); +} + +static void test_acpi_pc_smbios_blob(void) +{ + uint8_t req_type11[] = { 11 }; + test_data data = { + .machine = MACHINE_PC, + .variant = ".pc_smbios_blob", + .required_struct_types = req_type11, + .required_struct_types_len = ARRAY_SIZE(req_type11), + }; + + test_smbios("-machine smbios-entry-point-type=32 " + "-smbios file=tests/data/smbios/type11_blob", &data); + free_test_data(&data); +} + +static void test_acpi_isapc_smbios_legacy(void) +{ + uint8_t req_type11[] = { 1, 11 }; + test_data data = { + .machine = "isapc", + .variant = ".pc_smbios_legacy", + .required_struct_types = req_type11, + .required_struct_types_len = ARRAY_SIZE(req_type11), + }; + + test_smbios("-smbios file=tests/data/smbios/type11_blob.legacy " + "-smbios type=1,family=TEST", &data); + free_test_data(&data); +} + static void test_oem_fields(test_data *data) { int i; @@ -1628,64 +2139,60 @@ static void test_oem_fields(test_data *data) } } -static void test_acpi_oem_fields_pc(void) +static void test_acpi_piix4_oem_fields(void) { - test_data data; char *args; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_PC; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); - args = test_acpi_create_args(&data, - OEM_TEST_ARGS, false); + args = test_acpi_create_args(&data, OEM_TEST_ARGS); data.qts = qtest_init(args); - test_acpi_load_tables(&data, false); + test_acpi_load_tables(&data); test_oem_fields(&data); qtest_quit(data.qts); free_test_data(&data); g_free(args); } -static void test_acpi_oem_fields_q35(void) +static void test_acpi_q35_oem_fields(void) { - test_data data; char *args; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); - args = test_acpi_create_args(&data, - OEM_TEST_ARGS, false); + args = test_acpi_create_args(&data, OEM_TEST_ARGS); data.qts = qtest_init(args); - test_acpi_load_tables(&data, false); + test_acpi_load_tables(&data); test_oem_fields(&data); qtest_quit(data.qts); free_test_data(&data); g_free(args); } -static void test_acpi_oem_fields_microvm(void) +static void test_acpi_microvm_oem_fields(void) { - test_data data; + test_data data = {}; char *args; test_acpi_microvm_prepare(&data); args = test_acpi_create_args(&data, - OEM_TEST_ARGS",acpi=on", false); + OEM_TEST_ARGS",acpi=on"); data.qts = qtest_init(args); - test_acpi_load_tables(&data, false); + test_acpi_load_tables(&data); test_oem_fields(&data); qtest_quit(data.qts); free_test_data(&data); g_free(args); } -static void test_acpi_oem_fields_virt(void) +static void test_acpi_virt_oem_fields(void) { test_data data = { .machine = "virt", @@ -1698,10 +2205,9 @@ static void test_acpi_oem_fields_virt(void) }; char *args; - args = test_acpi_create_args(&data, - "-cpu cortex-a57 "OEM_TEST_ARGS, true); + args = test_acpi_create_args(&data, "-cpu cortex-a57 "OEM_TEST_ARGS); data.qts = qtest_init(args); - test_acpi_load_tables(&data, true); + test_acpi_load_tables(&data); test_oem_fields(&data); qtest_quit(data.qts); free_test_data(&data); @@ -1712,88 +2218,163 @@ static void test_acpi_oem_fields_virt(void) int main(int argc, char *argv[]) { const char *arch = qtest_get_arch(); - const bool has_kvm = qtest_has_accel("kvm"); - const bool has_tcg = qtest_has_accel("tcg"); + bool has_kvm, has_tcg; + char *v_env = getenv("V"); int ret; + if (v_env) { + verbosity_level = atoi(v_env); + } + g_test_init(&argc, &argv, NULL); + has_kvm = qtest_has_accel("kvm"); + has_tcg = qtest_has_accel("tcg"); + + if (!has_tcg && !has_kvm) { + g_test_skip("No KVM or TCG accelerator available"); + return 0; + } + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { ret = boot_sector_init(disk); if (ret) { return ret; } - qtest_add_func("acpi/q35/oem-fields", test_acpi_oem_fields_q35); - if (tpm_model_is_available("-machine q35", "tpm-tis")) { - qtest_add_func("acpi/q35/tpm2-tis", test_acpi_q35_tcg_tpm2_tis); - qtest_add_func("acpi/q35/tpm12-tis", test_acpi_q35_tcg_tpm12_tis); + if (qtest_has_machine(MACHINE_PC)) { + qtest_add_func("acpi/piix4", test_acpi_piix4_tcg); + qtest_add_func("acpi/piix4/oem-fields", test_acpi_piix4_oem_fields); + qtest_add_func("acpi/piix4/bridge", test_acpi_piix4_tcg_bridge); + qtest_add_func("acpi/piix4/pci-hotplug/no_root_hotplug", + test_acpi_piix4_no_root_hotplug); + qtest_add_func("acpi/piix4/pci-hotplug/no_bridge_hotplug", + test_acpi_piix4_no_bridge_hotplug); + qtest_add_func("acpi/piix4/pci-hotplug/off", + test_acpi_piix4_no_acpi_pci_hotplug); + qtest_add_func("acpi/piix4/ipmi", test_acpi_piix4_tcg_ipmi); + qtest_add_func("acpi/piix4/cpuhp", test_acpi_piix4_tcg_cphp); + qtest_add_func("acpi/piix4/numamem", test_acpi_piix4_tcg_numamem); + qtest_add_func("acpi/piix4/nosmm", test_acpi_piix4_tcg_nosmm); + qtest_add_func("acpi/piix4/smm-compat", + test_acpi_piix4_tcg_smm_compat); + qtest_add_func("acpi/piix4/smm-compat-nosmm", + test_acpi_piix4_tcg_smm_compat_nosmm); + qtest_add_func("acpi/piix4/nohpet", test_acpi_piix4_tcg_nohpet); + + /* i386 does not support memory hotplug */ + if (strcmp(arch, "i386")) { + qtest_add_func("acpi/piix4/memhp", test_acpi_piix4_tcg_memhp); + qtest_add_func("acpi/piix4/dimmpxm", + test_acpi_piix4_tcg_dimm_pxm); + qtest_add_func("acpi/piix4/acpihmat", + test_acpi_piix4_tcg_acpi_hmat); + } +#ifdef CONFIG_POSIX + qtest_add_func("acpi/piix4/acpierst", test_acpi_piix4_acpi_erst); +#endif + qtest_add_func("acpi/piix4/smbios-options", + test_acpi_pc_smbios_options); + qtest_add_func("acpi/piix4/smbios-blob", + test_acpi_pc_smbios_blob); + qtest_add_func("acpi/piix4/smbios-legacy", + test_acpi_isapc_smbios_legacy); } - qtest_add_func("acpi/piix4", test_acpi_piix4_tcg); - qtest_add_func("acpi/oem-fields", test_acpi_oem_fields_pc); - qtest_add_func("acpi/piix4/bridge", test_acpi_piix4_tcg_bridge); - qtest_add_func("acpi/piix4/pci-hotplug/no_root_hotplug", - test_acpi_piix4_no_root_hotplug); - qtest_add_func("acpi/piix4/pci-hotplug/no_bridge_hotplug", - test_acpi_piix4_no_bridge_hotplug); - qtest_add_func("acpi/piix4/pci-hotplug/off", - test_acpi_piix4_no_acpi_pci_hotplug); - qtest_add_func("acpi/q35", test_acpi_q35_tcg); - qtest_add_func("acpi/q35/bridge", test_acpi_q35_tcg_bridge); - qtest_add_func("acpi/q35/multif-bridge", test_acpi_q35_multif_bridge); - qtest_add_func("acpi/q35/mmio64", test_acpi_q35_tcg_mmio64); - qtest_add_func("acpi/piix4/ipmi", test_acpi_piix4_tcg_ipmi); - qtest_add_func("acpi/q35/ipmi", test_acpi_q35_tcg_ipmi); - qtest_add_func("acpi/piix4/cpuhp", test_acpi_piix4_tcg_cphp); - qtest_add_func("acpi/q35/cpuhp", test_acpi_q35_tcg_cphp); - qtest_add_func("acpi/piix4/memhp", test_acpi_piix4_tcg_memhp); - qtest_add_func("acpi/q35/memhp", test_acpi_q35_tcg_memhp); - qtest_add_func("acpi/piix4/numamem", test_acpi_piix4_tcg_numamem); - qtest_add_func("acpi/q35/numamem", test_acpi_q35_tcg_numamem); - qtest_add_func("acpi/piix4/nosmm", test_acpi_piix4_tcg_nosmm); - qtest_add_func("acpi/piix4/smm-compat", - test_acpi_piix4_tcg_smm_compat); - qtest_add_func("acpi/piix4/smm-compat-nosmm", - test_acpi_piix4_tcg_smm_compat_nosmm); - qtest_add_func("acpi/piix4/nohpet", test_acpi_piix4_tcg_nohpet); - qtest_add_func("acpi/q35/nosmm", test_acpi_q35_tcg_nosmm); - qtest_add_func("acpi/q35/smm-compat", - test_acpi_q35_tcg_smm_compat); - qtest_add_func("acpi/q35/smm-compat-nosmm", - test_acpi_q35_tcg_smm_compat_nosmm); - qtest_add_func("acpi/q35/nohpet", test_acpi_q35_tcg_nohpet); - 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); - qtest_add_func("acpi/piix4/acpierst", test_acpi_piix4_acpi_erst); - qtest_add_func("acpi/q35/acpierst", test_acpi_q35_acpi_erst); - qtest_add_func("acpi/microvm", test_acpi_microvm_tcg); - qtest_add_func("acpi/microvm/usb", test_acpi_microvm_usb_tcg); - qtest_add_func("acpi/microvm/rtc", test_acpi_microvm_rtc_tcg); - qtest_add_func("acpi/microvm/ioapic2", test_acpi_microvm_ioapic2_tcg); - qtest_add_func("acpi/microvm/oem-fields", test_acpi_oem_fields_microvm); - if (has_tcg) { - qtest_add_func("acpi/q35/ivrs", test_acpi_q35_tcg_ivrs); - if (strcmp(arch, "x86_64") == 0) { - qtest_add_func("acpi/microvm/pcie", test_acpi_microvm_pcie_tcg); - qtest_add_func("acpi/microvm/acpierst", test_acpi_microvm_acpi_erst); + if (qtest_has_machine(MACHINE_Q35)) { + qtest_add_func("acpi/q35", test_acpi_q35_tcg); + qtest_add_func("acpi/q35/oem-fields", test_acpi_q35_oem_fields); + if (tpm_model_is_available("-machine q35", "tpm-tis")) { + qtest_add_func("acpi/q35/tpm2-tis", test_acpi_q35_tcg_tpm2_tis); + qtest_add_func("acpi/q35/tpm12-tis", + test_acpi_q35_tcg_tpm12_tis); + } + qtest_add_func("acpi/q35/bridge", test_acpi_q35_tcg_bridge); + qtest_add_func("acpi/q35/no-acpi-hotplug", + test_acpi_q35_tcg_no_acpi_hotplug); + qtest_add_func("acpi/q35/multif-bridge", + test_acpi_q35_multif_bridge); + qtest_add_func("acpi/q35/ipmi", test_acpi_q35_tcg_ipmi); + qtest_add_func("acpi/q35/smbus/ipmi", test_acpi_q35_tcg_smbus_ipmi); + qtest_add_func("acpi/q35/cpuhp", test_acpi_q35_tcg_cphp); + qtest_add_func("acpi/q35/numamem", test_acpi_q35_tcg_numamem); + qtest_add_func("acpi/q35/nosmm", test_acpi_q35_tcg_nosmm); + qtest_add_func("acpi/q35/smm-compat", + test_acpi_q35_tcg_smm_compat); + qtest_add_func("acpi/q35/smm-compat-nosmm", + test_acpi_q35_tcg_smm_compat_nosmm); + qtest_add_func("acpi/q35/nohpet", test_acpi_q35_tcg_nohpet); + qtest_add_func("acpi/q35/acpihmat-noinitiator", + test_acpi_q35_tcg_acpi_hmat_noinitiator); + + /* i386 does not support memory hotplug */ + if (strcmp(arch, "i386")) { + qtest_add_func("acpi/q35/memhp", test_acpi_q35_tcg_memhp); + qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm); + qtest_add_func("acpi/q35/acpihmat", + test_acpi_q35_tcg_acpi_hmat); + qtest_add_func("acpi/q35/mmio64", test_acpi_q35_tcg_mmio64); + } +#ifdef CONFIG_POSIX + qtest_add_func("acpi/q35/acpierst", test_acpi_q35_acpi_erst); +#endif + qtest_add_func("acpi/q35/applesmc", test_acpi_q35_applesmc); + qtest_add_func("acpi/q35/pvpanic-isa", test_acpi_q35_pvpanic_isa); + if (has_tcg) { + qtest_add_func("acpi/q35/ivrs", test_acpi_q35_tcg_ivrs); + } + if (has_kvm) { + qtest_add_func("acpi/q35/kvm/xapic", test_acpi_q35_kvm_xapic); + qtest_add_func("acpi/q35/kvm/dmar", test_acpi_q35_kvm_dmar); + qtest_add_func("acpi/q35/type4-count", + test_acpi_q35_kvm_type4_count); + qtest_add_func("acpi/q35/core-count", + test_acpi_q35_kvm_core_count); + qtest_add_func("acpi/q35/core-count2", + test_acpi_q35_kvm_core_count2); + qtest_add_func("acpi/q35/thread-count", + test_acpi_q35_kvm_thread_count); + qtest_add_func("acpi/q35/thread-count2", + test_acpi_q35_kvm_thread_count2); + } + if (qtest_has_device("virtio-iommu-pci")) { + qtest_add_func("acpi/q35/viot", test_acpi_q35_viot); + } +#ifdef CONFIG_POSIX + qtest_add_func("acpi/q35/cxl", test_acpi_q35_cxl); +#endif + qtest_add_func("acpi/q35/slic", test_acpi_q35_slic); + } + if (qtest_has_machine("microvm")) { + qtest_add_func("acpi/microvm", test_acpi_microvm_tcg); + qtest_add_func("acpi/microvm/usb", test_acpi_microvm_usb_tcg); + qtest_add_func("acpi/microvm/rtc", test_acpi_microvm_rtc_tcg); + qtest_add_func("acpi/microvm/ioapic2", + test_acpi_microvm_ioapic2_tcg); + qtest_add_func("acpi/microvm/oem-fields", + test_acpi_microvm_oem_fields); + if (has_tcg) { + if (strcmp(arch, "x86_64") == 0) { + qtest_add_func("acpi/microvm/pcie", + test_acpi_microvm_pcie_tcg); +#ifdef CONFIG_POSIX + qtest_add_func("acpi/microvm/acpierst", + test_acpi_microvm_acpi_erst); +#endif + } } } - if (has_kvm) { - qtest_add_func("acpi/q35/kvm/xapic", test_acpi_q35_kvm_xapic); - qtest_add_func("acpi/q35/kvm/dmar", test_acpi_q35_kvm_dmar); - } - qtest_add_func("acpi/q35/viot", test_acpi_q35_viot); - qtest_add_func("acpi/q35/cxl", test_acpi_q35_cxl); - qtest_add_func("acpi/q35/slic", test_acpi_q35_slic); } else if (strcmp(arch, "aarch64") == 0) { - if (has_tcg) { + if (has_tcg && qtest_has_device("virtio-blk-pci")) { qtest_add_func("acpi/virt", test_acpi_virt_tcg); + qtest_add_func("acpi/virt/acpihmatvirt", + test_acpi_virt_tcg_acpi_hmat); + qtest_add_func("acpi/virt/topology", test_acpi_virt_tcg_topology); qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem); qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp); qtest_add_func("acpi/virt/pxb", test_acpi_virt_tcg_pxb); - qtest_add_func("acpi/virt/oem-fields", test_acpi_oem_fields_virt); - qtest_add_func("acpi/virt/viot", test_acpi_virt_viot); + qtest_add_func("acpi/virt/oem-fields", test_acpi_virt_oem_fields); + if (qtest_has_device("virtio-iommu-pci")) { + qtest_add_func("acpi/virt/viot", test_acpi_virt_viot); + } } } ret = g_test_run(); diff --git a/tests/qtest/boot-order-test.c b/tests/qtest/boot-order-test.c index 0680d79d6d..8f2b6ef05a 100644 --- a/tests/qtest/boot-order-test.c +++ b/tests/qtest/boot-order-test.c @@ -16,9 +16,6 @@ #include "qapi/qmp/qdict.h" #include "standard-headers/linux/qemu_fw_cfg.h" -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__)) - typedef struct { const char *args; uint64_t expected_boot; @@ -43,7 +40,7 @@ static void test_a_boot_order(const char *machine, machine ?: "", test_args); actual = read_boot_order(qts); g_assert_cmphex(actual, ==, expected_boot); - qmp_discard_response(qts, "{ 'execute': 'system_reset' }"); + qtest_qmp_assert_success(qts, "{ 'execute': 'system_reset' }"); /* * system_reset only requests reset. We get a RESET event after * the actual reset completes. Need to wait for that. diff --git a/tests/qtest/boot-sector.c b/tests/qtest/boot-sector.c index 44a109abd8..679ee17e2a 100644 --- a/tests/qtest/boot-sector.c +++ b/tests/qtest/boot-sector.c @@ -153,6 +153,8 @@ void boot_sector_test(QTestState *qts) signature_high = qtest_readb(qts, SIGNATURE_ADDR + 1); signature = (signature_high << 8) | signature_low; if (signature == SIGNATURE) { + /* wipe signature */ + qtest_writeb(qts, SIGNATURE_ADDR, 0x00); break; } @@ -160,7 +162,9 @@ void boot_sector_test(QTestState *qts) qrsp = qtest_qmp(qts, "{ 'execute': 'query-status' }"); qret = qdict_get_qdict(qrsp, "return"); g_assert_nonnull(qret); - g_assert_cmpstr(qdict_get_try_str(qret, "status"), ==, "running"); + if (qdict_get_try_str(qret, "status")) { + g_assert_cmpstr(qdict_get_try_str(qret, "status"), ==, "running"); + } qobject_unref(qrsp); g_usleep(TEST_DELAY); diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index 2f99d71cab..e3b7d65fe5 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -139,7 +139,7 @@ typedef struct testdef { const uint8_t *bios; /* Set in case we use our own mini bios */ } testdef_t; -static testdef_t tests[] = { +static const testdef_t tests[] = { { "alpha", "clipper", "", "PCI:" }, { "avr", "arduino-duemilanove", "", "T", sizeof(bios_avr), NULL, bios_avr }, { "avr", "arduino-mega-2560-v3", "", "T", sizeof(bios_avr), NULL, bios_avr}, @@ -156,7 +156,7 @@ static testdef_t tests[] = { "Open Firmware" }, { "ppc64", "powernv8", "", "OPAL" }, { "ppc64", "powernv9", "", "OPAL" }, - { "ppc64", "sam460ex", "-device e1000", "8086 100e" }, + { "ppc64", "sam460ex", "-device pci-bridge,chassis_nr=2", "1b36 0001" }, { "i386", "isapc", "-cpu qemu32 -M graphics=off", "SeaBIOS" }, { "i386", "pc", "-M graphics=off", "SeaBIOS" }, { "i386", "q35", "-M graphics=off", "SeaBIOS" }, @@ -224,15 +224,16 @@ static bool check_guest_output(QTestState *qts, const testdef_t *test, int fd) static void test_machine(const void *data) { const testdef_t *test = data; - char serialtmp[] = "/tmp/qtest-boot-serial-sXXXXXX"; - char codetmp[] = "/tmp/qtest-boot-serial-cXXXXXX"; + g_autofree char *serialtmp = NULL; + g_autofree char *codetmp = NULL; const char *codeparam = ""; const uint8_t *code = NULL; QTestState *qts; int ser_fd; - ser_fd = mkstemp(serialtmp); + ser_fd = g_file_open_tmp("qtest-boot-serial-sXXXXXX", &serialtmp, NULL); g_assert(ser_fd != -1); + close(ser_fd); if (test->kernel) { code = test->kernel; @@ -246,7 +247,7 @@ static void test_machine(const void *data) ssize_t wlen; int code_fd; - code_fd = mkstemp(codetmp); + code_fd = g_file_open_tmp("qtest-boot-serial-cXXXXXX", &codetmp, NULL); g_assert(code_fd != -1); wlen = write(code_fd, code, test->codesize); g_assert(wlen == test->codesize); @@ -266,6 +267,8 @@ static void test_machine(const void *data) unlink(codetmp); } + ser_fd = open(serialtmp, O_RDONLY); + g_assert(ser_fd != -1); if (!check_guest_output(qts, test, ser_fd)) { g_error("Failed to find expected string. Please check '%s'", serialtmp); @@ -284,6 +287,11 @@ int main(int argc, char *argv[]) g_test_init(&argc, &argv, NULL); + if (!qtest_has_accel("tcg") && !qtest_has_accel("kvm")) { + g_test_skip("No KVM or TCG accelerator available"); + return 0; + } + for (i = 0; tests[i].arch != NULL; i++) { if (g_str_equal(arch, tests[i].arch) && qtest_has_machine(tests[i].machine)) { diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c index a7766a9e65..5d89e62515 100644 --- a/tests/qtest/cdrom-test.c +++ b/tests/qtest/cdrom-test.c @@ -17,7 +17,7 @@ static char isoimage[] = "cdrom-boot-iso-XXXXXX"; -static int exec_genisoimg(const char **args) +static int exec_xorrisofs(const char **args) { gchar *out_err = NULL; gint exit_status = -1; @@ -37,22 +37,22 @@ static int exec_genisoimg(const char **args) return exit_status; } -static int prepare_image(const char *arch, char *isoimage) +static int prepare_image(const char *arch, char *isoimagepath) { char srcdir[] = "cdrom-test-dir-XXXXXX"; char *codefile = NULL; int ifh, ret = -1; const char *args[] = { - "genisoimage", "-quiet", "-l", "-no-emul-boot", - "-b", NULL, "-o", isoimage, srcdir, NULL + "xorrisofs", "-quiet", "-l", "-no-emul-boot", + "-b", NULL, "-o", isoimagepath, srcdir, NULL }; - ifh = mkstemp(isoimage); + ifh = mkstemp(isoimagepath); if (ifh < 0) { perror("Error creating temporary iso image file"); return -1; } - if (!mkdtemp(srcdir)) { + if (!g_mkdtemp(srcdir)) { perror("Error creating temporary directory"); goto cleanup; } @@ -75,9 +75,9 @@ static int prepare_image(const char *arch, char *isoimage) } args[5] = strchr(codefile, '/') + 1; - ret = exec_genisoimg(args); + ret = exec_xorrisofs(args); if (ret) { - fprintf(stderr, "genisoimage failed: %i\n", ret); + fprintf(stderr, "xorrisofs failed: %i\n", ret); } unlink(codefile); @@ -130,10 +130,18 @@ static void test_cdboot(gconstpointer data) static void add_x86_tests(void) { + if (!qtest_has_accel("tcg") && !qtest_has_accel("kvm")) { + g_test_skip("No KVM or TCG accelerator available, skipping boot tests"); + return; + } + qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot); - qtest_add_data_func("cdrom/boot/virtio-scsi", - "-device virtio-scsi -device scsi-cd,drive=cdr " - "-blockdev file,node-name=cdr,filename=", test_cdboot); + if (qtest_has_device("virtio-scsi-ccw")) { + qtest_add_data_func("cdrom/boot/virtio-scsi", + "-device virtio-scsi -device scsi-cd,drive=cdr " + "-blockdev file,node-name=cdr,filename=", + test_cdboot); + } /* * Unstable CI test under load * See https://lists.gnu.org/archive/html/qemu-devel/2019-02/msg05509.html @@ -176,7 +184,19 @@ static void add_x86_tests(void) static void add_s390x_tests(void) { + if (!qtest_has_accel("tcg") && !qtest_has_accel("kvm")) { + g_test_skip("No KVM or TCG accelerator available, skipping boot tests"); + } + if (!qtest_has_device("virtio-blk-ccw")) { + return; + } + qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot); + + if (!qtest_has_device("virtio-scsi-ccw")) { + return; + } + qtest_add_data_func("cdrom/boot/virtio-scsi", "-device virtio-scsi -device scsi-cd,drive=cdr " "-blockdev file,node-name=cdr,filename=", test_cdboot); @@ -201,12 +221,12 @@ int main(int argc, char **argv) { int ret; const char *arch = qtest_get_arch(); - const char *genisocheck[] = { "genisoimage", "-version", NULL }; + const char *xorrisocheck[] = { "xorrisofs", "-version", NULL }; g_test_init(&argc, &argv, NULL); - if (exec_genisoimg(genisocheck)) { - /* genisoimage not available - so can't run tests */ + if (exec_xorrisofs(xorrisocheck)) { + /* xorrisofs not available - so can't run tests */ return g_test_run(); } @@ -244,9 +264,18 @@ int main(int argc, char **argv) const char *armmachines[] = { "realview-eb", "realview-eb-mpcore", "realview-pb-a8", "realview-pbx-a9", "versatileab", "versatilepb", "vexpress-a15", - "vexpress-a9", "virt", NULL + "vexpress-a9", NULL }; add_cdrom_param_tests(armmachines); + if (qtest_has_device("virtio-blk-pci")) { + const char *virtmachine[] = { "virt", NULL }; + add_cdrom_param_tests(virtmachine); + } + } else if (g_str_equal(arch, "loongarch64")) { + if (qtest_has_device("virtio-blk-pci")) { + const char *virtmachine[] = { "virt", NULL }; + add_cdrom_param_tests(virtmachine); + } } else { const char *nonemachine[] = { "none", NULL }; add_cdrom_param_tests(nonemachine); diff --git a/tests/qtest/cxl-test.c b/tests/qtest/cxl-test.c index 079011af6a..a600331843 100644 --- a/tests/qtest/cxl-test.c +++ b/tests/qtest/cxl-test.c @@ -8,50 +8,72 @@ #include "qemu/osdep.h" #include "libqtest-single.h" -#define QEMU_PXB_CMD "-machine q35,cxl=on " \ - "-device pxb-cxl,id=cxl.0,bus=pcie.0,bus_nr=52 " \ - "-cxl-fixed-memory-window targets.0=cxl.0,size=4G " +#define QEMU_PXB_CMD \ + "-machine q35,cxl=on " \ + "-device pxb-cxl,id=cxl.0,bus=pcie.0,bus_nr=52 " \ + "-M cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.size=4G " -#define QEMU_2PXB_CMD "-machine q35,cxl=on " \ - "-device pxb-cxl,id=cxl.0,bus=pcie.0,bus_nr=52 " \ - "-device pxb-cxl,id=cxl.1,bus=pcie.0,bus_nr=53 " \ - "-cxl-fixed-memory-window targets.0=cxl.0,targets.1=cxl.1,size=4G " +#define QEMU_2PXB_CMD \ + "-machine q35,cxl=on " \ + "-device pxb-cxl,id=cxl.0,bus=pcie.0,bus_nr=52 " \ + "-device pxb-cxl,id=cxl.1,bus=pcie.0,bus_nr=53 " \ + "-M cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.targets.1=cxl.1,cxl-fmw.0.size=4G " -#define QEMU_RP "-device cxl-rp,id=rp0,bus=cxl.0,chassis=0,slot=0 " +#define QEMU_RP \ + "-device cxl-rp,id=rp0,bus=cxl.0,chassis=0,slot=0 " /* Dual ports on first pxb */ -#define QEMU_2RP "-device cxl-rp,id=rp0,bus=cxl.0,chassis=0,slot=0 " \ - "-device cxl-rp,id=rp1,bus=cxl.0,chassis=0,slot=1 " +#define QEMU_2RP \ + "-device cxl-rp,id=rp0,bus=cxl.0,chassis=0,slot=0 " \ + "-device cxl-rp,id=rp1,bus=cxl.0,chassis=0,slot=1 " /* Dual ports on each of the pxb instances */ -#define QEMU_4RP "-device cxl-rp,id=rp0,bus=cxl.0,chassis=0,slot=0 " \ - "-device cxl-rp,id=rp1,bus=cxl.0,chassis=0,slot=1 " \ - "-device cxl-rp,id=rp2,bus=cxl.1,chassis=0,slot=2 " \ - "-device cxl-rp,id=rp3,bus=cxl.1,chassis=0,slot=3 " +#define QEMU_4RP \ + "-device cxl-rp,id=rp0,bus=cxl.0,chassis=0,slot=0 " \ + "-device cxl-rp,id=rp1,bus=cxl.0,chassis=0,slot=1 " \ + "-device cxl-rp,id=rp2,bus=cxl.1,chassis=0,slot=2 " \ + "-device cxl-rp,id=rp3,bus=cxl.1,chassis=0,slot=3 " -#define QEMU_T3D "-object memory-backend-file,id=cxl-mem0,mem-path=%s,size=256M " \ - "-object memory-backend-file,id=lsa0,mem-path=%s,size=256M " \ - "-device cxl-type3,bus=rp0,memdev=cxl-mem0,lsa=lsa0,id=cxl-pmem0 " +#define QEMU_T3D_DEPRECATED \ + "-object memory-backend-file,id=cxl-mem0,mem-path=%s,size=256M " \ + "-object memory-backend-file,id=lsa0,mem-path=%s,size=256M " \ + "-device cxl-type3,bus=rp0,memdev=cxl-mem0,lsa=lsa0,id=cxl-pmem0 " -#define QEMU_2T3D "-object memory-backend-file,id=cxl-mem0,mem-path=%s,size=256M " \ - "-object memory-backend-file,id=lsa0,mem-path=%s,size=256M " \ - "-device cxl-type3,bus=rp0,memdev=cxl-mem0,lsa=lsa0,id=cxl-pmem0 " \ - "-object memory-backend-file,id=cxl-mem1,mem-path=%s,size=256M " \ - "-object memory-backend-file,id=lsa1,mem-path=%s,size=256M " \ - "-device cxl-type3,bus=rp1,memdev=cxl-mem1,lsa=lsa1,id=cxl-pmem1 " +#define QEMU_T3D_PMEM \ + "-object memory-backend-file,id=cxl-mem0,mem-path=%s,size=256M " \ + "-object memory-backend-file,id=lsa0,mem-path=%s,size=256M " \ + "-device cxl-type3,bus=rp0,persistent-memdev=cxl-mem0,lsa=lsa0,id=pmem0 " -#define QEMU_4T3D "-object memory-backend-file,id=cxl-mem0,mem-path=%s,size=256M " \ - "-object memory-backend-file,id=lsa0,mem-path=%s,size=256M " \ - "-device cxl-type3,bus=rp0,memdev=cxl-mem0,lsa=lsa0,id=cxl-pmem0 " \ - "-object memory-backend-file,id=cxl-mem1,mem-path=%s,size=256M " \ - "-object memory-backend-file,id=lsa1,mem-path=%s,size=256M " \ - "-device cxl-type3,bus=rp1,memdev=cxl-mem1,lsa=lsa1,id=cxl-pmem1 " \ - "-object memory-backend-file,id=cxl-mem2,mem-path=%s,size=256M " \ - "-object memory-backend-file,id=lsa2,mem-path=%s,size=256M " \ - "-device cxl-type3,bus=rp2,memdev=cxl-mem2,lsa=lsa2,id=cxl-pmem2 " \ - "-object memory-backend-file,id=cxl-mem3,mem-path=%s,size=256M " \ - "-object memory-backend-file,id=lsa3,mem-path=%s,size=256M " \ - "-device cxl-type3,bus=rp3,memdev=cxl-mem3,lsa=lsa3,id=cxl-pmem3 " +#define QEMU_T3D_VMEM \ + "-object memory-backend-ram,id=cxl-mem0,size=256M " \ + "-device cxl-type3,bus=rp0,volatile-memdev=cxl-mem0,id=mem0 " + +#define QEMU_T3D_VMEM_LSA \ + "-object memory-backend-ram,id=cxl-mem0,size=256M " \ + "-object memory-backend-file,id=lsa0,mem-path=%s,size=256M " \ + "-device cxl-type3,bus=rp0,volatile-memdev=cxl-mem0,lsa=lsa0,id=mem0 " + +#define QEMU_2T3D \ + "-object memory-backend-file,id=cxl-mem0,mem-path=%s,size=256M " \ + "-object memory-backend-file,id=lsa0,mem-path=%s,size=256M " \ + "-device cxl-type3,bus=rp0,persistent-memdev=cxl-mem0,lsa=lsa0,id=pmem0 " \ + "-object memory-backend-file,id=cxl-mem1,mem-path=%s,size=256M " \ + "-object memory-backend-file,id=lsa1,mem-path=%s,size=256M " \ + "-device cxl-type3,bus=rp1,persistent-memdev=cxl-mem1,lsa=lsa1,id=pmem1 " + +#define QEMU_4T3D \ + "-object memory-backend-file,id=cxl-mem0,mem-path=%s,size=256M " \ + "-object memory-backend-file,id=lsa0,mem-path=%s,size=256M " \ + "-device cxl-type3,bus=rp0,persistent-memdev=cxl-mem0,lsa=lsa0,id=pmem0 " \ + "-object memory-backend-file,id=cxl-mem1,mem-path=%s,size=256M " \ + "-object memory-backend-file,id=lsa1,mem-path=%s,size=256M " \ + "-device cxl-type3,bus=rp1,persistent-memdev=cxl-mem1,lsa=lsa1,id=pmem1 " \ + "-object memory-backend-file,id=cxl-mem2,mem-path=%s,size=256M " \ + "-object memory-backend-file,id=lsa2,mem-path=%s,size=256M " \ + "-device cxl-type3,bus=rp2,persistent-memdev=cxl-mem2,lsa=lsa2,id=pmem2 " \ + "-object memory-backend-file,id=cxl-mem3,mem-path=%s,size=256M " \ + "-object memory-backend-file,id=lsa3,mem-path=%s,size=256M " \ + "-device cxl-type3,bus=rp3,persistent-memdev=cxl-mem3,lsa=lsa3,id=pmem3 " static void cxl_basic_hb(void) { @@ -89,42 +111,83 @@ static void cxl_2root_port(void) qtest_end(); } -static void cxl_t3d(void) +#ifdef CONFIG_POSIX +static void cxl_t3d_deprecated(void) { g_autoptr(GString) cmdline = g_string_new(NULL); - char template[] = "/tmp/cxl-test-XXXXXX"; - const char *tmpfs; + g_autofree const char *tmpfs = NULL; - tmpfs = mkdtemp(template); + tmpfs = g_dir_make_tmp("cxl-test-XXXXXX", NULL); - g_string_printf(cmdline, QEMU_PXB_CMD QEMU_RP QEMU_T3D, tmpfs, tmpfs); + g_string_printf(cmdline, QEMU_PXB_CMD QEMU_RP QEMU_T3D_DEPRECATED, + tmpfs, tmpfs); + + qtest_start(cmdline->str); + qtest_end(); + rmdir(tmpfs); +} + +static void cxl_t3d_persistent(void) +{ + g_autoptr(GString) cmdline = g_string_new(NULL); + g_autofree const char *tmpfs = NULL; + + tmpfs = g_dir_make_tmp("cxl-test-XXXXXX", NULL); + + g_string_printf(cmdline, QEMU_PXB_CMD QEMU_RP QEMU_T3D_PMEM, + tmpfs, tmpfs); + + qtest_start(cmdline->str); + qtest_end(); + rmdir(tmpfs); +} + +static void cxl_t3d_volatile(void) +{ + g_autoptr(GString) cmdline = g_string_new(NULL); + + g_string_printf(cmdline, QEMU_PXB_CMD QEMU_RP QEMU_T3D_VMEM); qtest_start(cmdline->str); qtest_end(); } +static void cxl_t3d_volatile_lsa(void) +{ + g_autoptr(GString) cmdline = g_string_new(NULL); + g_autofree const char *tmpfs = NULL; + + tmpfs = g_dir_make_tmp("cxl-test-XXXXXX", NULL); + + g_string_printf(cmdline, QEMU_PXB_CMD QEMU_RP QEMU_T3D_VMEM_LSA, + tmpfs); + + qtest_start(cmdline->str); + qtest_end(); + rmdir(tmpfs); +} + static void cxl_1pxb_2rp_2t3d(void) { g_autoptr(GString) cmdline = g_string_new(NULL); - char template[] = "/tmp/cxl-test-XXXXXX"; - const char *tmpfs; + g_autofree const char *tmpfs = NULL; - tmpfs = mkdtemp(template); + tmpfs = g_dir_make_tmp("cxl-test-XXXXXX", NULL); g_string_printf(cmdline, QEMU_PXB_CMD QEMU_2RP QEMU_2T3D, tmpfs, tmpfs, tmpfs, tmpfs); qtest_start(cmdline->str); qtest_end(); + rmdir(tmpfs); } static void cxl_2pxb_4rp_4t3d(void) { g_autoptr(GString) cmdline = g_string_new(NULL); - char template[] = "/tmp/cxl-test-XXXXXX"; - const char *tmpfs; + g_autofree const char *tmpfs = NULL; - tmpfs = mkdtemp(template); + tmpfs = g_dir_make_tmp("cxl-test-XXXXXX", NULL); g_string_printf(cmdline, QEMU_2PXB_CMD QEMU_4RP QEMU_4T3D, tmpfs, tmpfs, tmpfs, tmpfs, tmpfs, tmpfs, @@ -132,7 +195,9 @@ static void cxl_2pxb_4rp_4t3d(void) qtest_start(cmdline->str); qtest_end(); + rmdir(tmpfs); } +#endif /* CONFIG_POSIX */ int main(int argc, char **argv) { @@ -144,8 +209,13 @@ int main(int argc, char **argv) qtest_add_func("/pci/cxl/pxb_x2_with_window", cxl_2pxb_with_window); qtest_add_func("/pci/cxl/rp", cxl_root_port); qtest_add_func("/pci/cxl/rp_x2", cxl_2root_port); - qtest_add_func("/pci/cxl/type3_device", cxl_t3d); +#ifdef CONFIG_POSIX + qtest_add_func("/pci/cxl/type3_device", cxl_t3d_deprecated); + qtest_add_func("/pci/cxl/type3_device_pmem", cxl_t3d_persistent); + qtest_add_func("/pci/cxl/type3_device_vmem", cxl_t3d_volatile); + qtest_add_func("/pci/cxl/type3_device_vmem_lsa", cxl_t3d_volatile_lsa); qtest_add_func("/pci/cxl/rp_x2_type3_x2", cxl_1pxb_2rp_2t3d); qtest_add_func("/pci/cxl/pxb_x2_root_port_x4_type3_x4", cxl_2pxb_4rp_4t3d); +#endif return g_test_run(); } diff --git a/tests/qtest/dbus-display-test.c b/tests/qtest/dbus-display-test.c index 8be5974763..0390bdcb41 100644 --- a/tests/qtest/dbus-display-test.c +++ b/tests/qtest/dbus-display-test.c @@ -1,9 +1,11 @@ #include "qemu/osdep.h" +#include "qemu/sockets.h" #include "qemu/dbus.h" +#include "qemu/sockets.h" #include #include #include "libqtest.h" -#include "dbus-display1.h" +#include "ui/dbus-display1.h" static GDBusConnection* test_dbus_p2p_from_fd(int fd) @@ -13,7 +15,11 @@ test_dbus_p2p_from_fd(int fd) g_autoptr(GSocketConnection) socketc = NULL; GDBusConnection *conn; +#ifdef WIN32 + socket = g_socket_new_from_fd(_get_osfhandle(fd), &err); +#else socket = g_socket_new_from_fd(fd, &err); +#endif g_assert_no_error(err); socketc = g_socket_connection_factory_create_connection(socket); @@ -36,7 +42,7 @@ test_setup(QTestState **qts, GDBusConnection **conn) *qts = qtest_init("-display dbus,p2p=yes -name dbus-test"); - g_assert_cmpint(socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0); + g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0); qtest_qmp_add_client(*qts, "@dbus-display", pair[1]); @@ -125,7 +131,17 @@ test_dbus_console_registered(GObject *source_object, qemu_dbus_display1_console_call_register_listener_finish( QEMU_DBUS_DISPLAY1_CONSOLE(source_object), - NULL, res, &err); +#ifndef WIN32 + NULL, +#endif + res, &err); + + if (g_error_matches(err, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { + g_test_skip("The VM doesn't have a console!"); + g_main_loop_quit(test->loop); + return; + } + g_assert_no_error(err); test->listener_conn = g_thread_join(test->thread); @@ -144,17 +160,25 @@ test_dbus_display_console(void) g_autoptr(GError) err = NULL; g_autoptr(GDBusConnection) conn = NULL; g_autoptr(QemuDBusDisplay1ConsoleProxy) console = NULL; - g_autoptr(GUnixFDList) fd_list = NULL; g_autoptr(GMainLoop) loop = NULL; QTestState *qts = NULL; - int pair[2], idx; - TestDBusConsoleRegister test; + int pair[2]; + TestDBusConsoleRegister test = { 0, }; +#ifdef WIN32 + WSAPROTOCOL_INFOW info; + g_autoptr(GVariant) listener = NULL; +#else + g_autoptr(GUnixFDList) fd_list = NULL; + int idx; +#endif test_setup(&qts, &conn); - g_assert_cmpint(socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0); + g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0); +#ifndef WIN32 fd_list = g_unix_fd_list_new(); idx = g_unix_fd_list_append(fd_list, pair[1], NULL); +#endif console = QEMU_DBUS_DISPLAY1_CONSOLE_PROXY( qemu_dbus_display1_console_proxy_new_sync( @@ -170,12 +194,33 @@ test_dbus_display_console(void) test.thread = g_thread_new(NULL, test_dbus_p2p_server_setup_thread, GINT_TO_POINTER(pair[0])); +#ifdef WIN32 + if (WSADuplicateSocketW(_get_osfhandle(pair[1]), + GetProcessId((HANDLE) qtest_pid(qts)), + &info) == SOCKET_ERROR) + { + g_autofree char *emsg = g_win32_error_message(WSAGetLastError()); + g_error("WSADuplicateSocket failed: %s", emsg); + } + close(pair[1]); + listener = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + &info, + sizeof(info), + 1); +#endif + qemu_dbus_display1_console_call_register_listener( QEMU_DBUS_DISPLAY1_CONSOLE(console), +#ifdef WIN32 + listener, +#else g_variant_new_handle(idx), +#endif G_DBUS_CALL_FLAGS_NONE, -1, +#ifndef WIN32 fd_list, +#endif NULL, test_dbus_console_registered, &test); @@ -207,7 +252,6 @@ test_dbus_display_keyboard(void) &err)); g_assert_no_error(err); - g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 0); g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0); @@ -218,6 +262,12 @@ test_dbus_display_keyboard(void) -1, NULL, &err); + if (g_error_matches(err, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { + g_test_skip("The VM doesn't have a console!"); + qtest_quit(qts); + return; + } + g_assert_no_error(err); /* may be should wait for interrupt? */ diff --git a/tests/qtest/dbus-vmstate-test.c b/tests/qtest/dbus-vmstate-test.c index 74ede651f6..6c990864e3 100644 --- a/tests/qtest/dbus-vmstate-test.c +++ b/tests/qtest/dbus-vmstate-test.c @@ -233,7 +233,7 @@ test_dbus_vmstate(Test *test) test->src_qemu = src_qemu; if (test->migrate_fail) { wait_for_migration_fail(src_qemu, true); - qtest_set_expected_status(dst_qemu, 1); + qtest_set_expected_status(dst_qemu, EXIT_FAILURE); } else { wait_for_migration_complete(src_qemu); } diff --git a/tests/qtest/device-plug-test.c b/tests/qtest/device-plug-test.c index 2e3137843e..c6f33153eb 100644 --- a/tests/qtest/device-plug-test.c +++ b/tests/qtest/device-plug-test.c @@ -15,17 +15,6 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" -static void device_del(QTestState *qtest, const char *id) -{ - QDict *resp; - - resp = qtest_qmp(qtest, - "{'execute': 'device_del', 'arguments': { 'id': %s } }", id); - - g_assert(qdict_haskey(resp, "return")); - qobject_unref(resp); -} - static void system_reset(QTestState *qtest) { QDict *resp; @@ -61,60 +50,122 @@ static void wait_device_deleted_event(QTestState *qtest, const char *id) } } -static void test_pci_unplug_request(void) +static void process_device_remove(QTestState *qtest, const char *id) { - const char *arch = qtest_get_arch(); - const char *machine_addition = ""; - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - machine_addition = "-machine pc"; - } - - QTestState *qtest = qtest_initf("%s -device virtio-mouse-pci,id=dev0", - machine_addition); - /* * Request device removal. As the guest is not running, the request won't * be processed. However during system reset, the removal will be * handled, removing the device. */ - device_del(qtest, "dev0"); + qtest_qmp_device_del_send(qtest, id); system_reset(qtest); - wait_device_deleted_event(qtest, "dev0"); + wait_device_deleted_event(qtest, id); +} + +static void test_pci_unplug_request(void) +{ + QTestState *qtest; + const char *arch = qtest_get_arch(); + const char *machine_addition = ""; + + if (!qtest_has_device("virtio-mouse-pci")) { + g_test_skip("Device virtio-mouse-pci not available"); + return; + } + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + machine_addition = "-machine pc"; + } + + qtest = qtest_initf("%s -device virtio-mouse-pci,id=dev0", + machine_addition); + + process_device_remove(qtest, "dev0"); + + qtest_quit(qtest); +} + +static void test_q35_pci_unplug_request(void) +{ + QTestState *qtest; + + if (!qtest_has_device("virtio-mouse-pci")) { + g_test_skip("Device virtio-mouse-pci not available"); + return; + } + + qtest = qtest_initf("-machine q35 " + "-device pcie-root-port,id=p1 " + "-device pcie-pci-bridge,bus=p1,id=b1 " + "-device virtio-mouse-pci,bus=b1,id=dev0"); + + process_device_remove(qtest, "dev0"); qtest_quit(qtest); } static void test_pci_unplug_json_request(void) { + QTestState *qtest; const char *arch = qtest_get_arch(); const char *machine_addition = ""; + if (!qtest_has_device("virtio-mouse-pci")) { + g_test_skip("Device virtio-mouse-pci not available"); + return; + } + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { machine_addition = "-machine pc"; } - QTestState *qtest = qtest_initf( - "%s -device '{\"driver\": \"virtio-mouse-pci\", \"id\": \"dev0\"}'", + qtest = qtest_initf( + "%s -device \"{'driver': 'virtio-mouse-pci', 'id': 'dev0'}\"", machine_addition); - /* - * Request device removal. As the guest is not running, the request won't - * be processed. However during system reset, the removal will be - * handled, removing the device. - */ - device_del(qtest, "dev0"); - system_reset(qtest); - wait_device_deleted_event(qtest, "dev0"); + process_device_remove(qtest, "dev0"); + + qtest_quit(qtest); +} + +static void test_q35_pci_unplug_json_request(void) +{ + QTestState *qtest; + const char *port = "-device \"{'driver': 'pcie-root-port', " + "'id': 'p1'}\""; + + const char *bridge = "-device \"{'driver': 'pcie-pci-bridge', " + "'id': 'b1', " + "'bus': 'p1'}\""; + + const char *device = "-device \"{'driver': 'virtio-mouse-pci', " + "'bus': 'b1', " + "'id': 'dev0'}\""; + + if (!qtest_has_device("virtio-mouse-pci")) { + g_test_skip("Device virtio-mouse-pci not available"); + return; + } + + qtest = qtest_initf("-machine q35 %s %s %s", port, bridge, device); + + process_device_remove(qtest, "dev0"); qtest_quit(qtest); } static void test_ccw_unplug(void) { - QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0"); + QTestState *qtest; - device_del(qtest, "dev0"); + if (!qtest_has_device("virtio-balloon-ccw")) { + g_test_skip("Device virtio-balloon-ccw not available"); + return; + } + + qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0"); + + qtest_qmp_device_del_send(qtest, "dev0"); wait_device_deleted_event(qtest, "dev0"); qtest_quit(qtest); @@ -124,13 +175,11 @@ static void test_spapr_cpu_unplug_request(void) { QTestState *qtest; - qtest = qtest_initf("-cpu power9_v2.0 -smp 1,maxcpus=2 " - "-device power9_v2.0-spapr-cpu-core,core-id=1,id=dev0"); + qtest = qtest_initf("-cpu power9_v2.2 -smp 1,maxcpus=2 " + "-device power9_v2.2-spapr-cpu-core,core-id=1,id=dev0"); /* similar to test_pci_unplug_request */ - device_del(qtest, "dev0"); - system_reset(qtest); - wait_device_deleted_event(qtest, "dev0"); + process_device_remove(qtest, "dev0"); qtest_quit(qtest); } @@ -144,9 +193,7 @@ static void test_spapr_memory_unplug_request(void) "-device pc-dimm,id=dev0,memdev=mem0"); /* similar to test_pci_unplug_request */ - device_del(qtest, "dev0"); - system_reset(qtest); - wait_device_deleted_event(qtest, "dev0"); + process_device_remove(qtest, "dev0"); qtest_quit(qtest); } @@ -158,9 +205,7 @@ static void test_spapr_phb_unplug_request(void) qtest = qtest_initf("-device spapr-pci-host-bridge,index=1,id=dev0"); /* similar to test_pci_unplug_request */ - device_del(qtest, "dev0"); - system_reset(qtest); - wait_device_deleted_event(qtest, "dev0"); + process_device_remove(qtest, "dev0"); qtest_quit(qtest); } @@ -195,5 +240,12 @@ int main(int argc, char **argv) test_spapr_phb_unplug_request); } + if (!strcmp(arch, "x86_64") && qtest_has_machine("q35")) { + qtest_add_func("/device-plug/q35-pci-unplug-request", + test_q35_pci_unplug_request); + qtest_add_func("/device-plug/q35-pci-unplug-json-request", + test_q35_pci_unplug_json_request); + } + return g_test_run(); } diff --git a/tests/qtest/display-vga-test.c b/tests/qtest/display-vga-test.c index ace3bb28e0..75b341a9c6 100644 --- a/tests/qtest/display-vga-test.c +++ b/tests/qtest/display-vga-test.c @@ -8,61 +8,46 @@ */ #include "qemu/osdep.h" -#include "libqtest-single.h" - -static void pci_cirrus(void) -{ - qtest_start("-vga none -device cirrus-vga"); - qtest_end(); -} - -static void pci_stdvga(void) -{ - qtest_start("-vga none -device VGA"); - qtest_end(); -} - -static void pci_secondary(void) -{ - qtest_start("-vga none -device secondary-vga"); - qtest_end(); -} +#include "libqtest.h" static void pci_multihead(void) { - qtest_start("-vga none -device VGA -device secondary-vga"); - qtest_end(); + QTestState *qts; + + qts = qtest_init("-vga none -device VGA -device secondary-vga"); + qtest_quit(qts); } -static void pci_virtio_gpu(void) +static void test_vga(gconstpointer data) { - qtest_start("-vga none -device virtio-gpu-pci"); - qtest_end(); -} + QTestState *qts; -static void pci_virtio_vga(void) -{ - qtest_start("-vga none -device virtio-vga"); - qtest_end(); + qts = qtest_initf("-vga none -device %s", (const char *)data); + qtest_quit(qts); } int main(int argc, char **argv) { - const char *arch = qtest_get_arch(); + static const char *devices[] = { + "cirrus-vga", + "VGA", + "secondary-vga", + "virtio-gpu-pci", + "virtio-vga" + }; g_test_init(&argc, &argv, NULL); - if (strcmp(arch, "alpha") == 0 || strcmp(arch, "i386") == 0 || - strcmp(arch, "mips") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_add_func("/display/pci/cirrus", pci_cirrus); + for (int i = 0; i < ARRAY_SIZE(devices); i++) { + if (qtest_has_device(devices[i])) { + char *testpath = g_strdup_printf("/display/pci/%s", devices[i]); + qtest_add_data_func(testpath, devices[i], test_vga); + g_free(testpath); + } } - qtest_add_func("/display/pci/stdvga", pci_stdvga); - qtest_add_func("/display/pci/secondary", pci_secondary); - qtest_add_func("/display/pci/multihead", pci_multihead); - qtest_add_func("/display/pci/virtio-gpu", pci_virtio_gpu); - if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64") || - g_str_equal(arch, "hppa") || g_str_equal(arch, "ppc64")) { - qtest_add_func("/display/pci/virtio-vga", pci_virtio_vga); + + if (qtest_has_device("secondary-vga")) { + qtest_add_func("/display/pci/multihead", pci_multihead); } return g_test_run(); diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c index 5e6d58b4dd..8a6f3ac963 100644 --- a/tests/qtest/drive_del-test.c +++ b/tests/qtest/drive_del-test.c @@ -16,6 +16,8 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" +static const char *qvirtio_get_dev_type(void); + static bool look_for_drive0(QTestState *qts, const char *command, const char *key) { QDict *response; @@ -40,6 +42,19 @@ static bool look_for_drive0(QTestState *qts, const char *command, const char *ke return found; } +/* + * This covers the possible absence of a device due to QEMU build + * options. + */ +static bool has_device_builtin(const char *dev) +{ + gchar *device = g_strdup_printf("%s-%s", dev, qvirtio_get_dev_type()); + bool rc = qtest_has_device(device); + + g_free(device); + return rc; +} + static bool has_drive(QTestState *qts) { return look_for_drive0(qts, "query-block", "device"); @@ -123,12 +138,10 @@ static const char *qvirtio_get_dev_type(void) static void device_add(QTestState *qts) { - QDict *response; - char driver[32]; - snprintf(driver, sizeof(driver), "virtio-blk-%s", - qvirtio_get_dev_type()); - - response = qtest_qmp(qts, "{'execute': 'device_add'," + g_autofree char *driver = g_strdup_printf("virtio-blk-%s", + qvirtio_get_dev_type()); + QDict *response = + qtest_qmp(qts, "{'execute': 'device_add'," " 'arguments': {" " 'driver': %s," " 'drive': 'drive0'," @@ -143,11 +156,7 @@ static void device_del(QTestState *qts, bool and_reset) { QDict *response; - response = qtest_qmp(qts, "{'execute': 'device_del'," - " 'arguments': { 'id': 'dev0' } }"); - g_assert(response); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); + qtest_qmp_device_del_send(qts, "dev0"); if (and_reset) { response = qtest_qmp(qts, "{'execute': 'system_reset' }"); @@ -214,6 +223,11 @@ static void test_drive_del_device_del(void) { QTestState *qts; + if (!has_device_builtin("virtio-scsi")) { + g_test_skip("Device virtio-scsi is not available"); + return; + } + /* Start with a drive used by a device that unplugs instantaneously */ qts = qtest_initf("-drive if=none,id=drive0,file=null-co://," "file.read-zeroes=on,format=raw" @@ -238,6 +252,11 @@ static void test_cli_device_del(void) const char *arch = qtest_get_arch(); const char *machine_addition = ""; + if (!has_device_builtin("virtio-blk")) { + g_test_skip("Device virtio-blk is not available"); + return; + } + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { machine_addition = "-machine pc"; } @@ -258,10 +277,41 @@ static void test_cli_device_del(void) qtest_quit(qts); } +static void test_cli_device_del_q35(void) +{ + QTestState *qts; + + if (!has_device_builtin("virtio-blk")) { + g_test_skip("Device virtio-blk is not available"); + return; + } + + /* + * -drive/-device and device_del. Start with a drive used by a + * device that unplugs after reset. + */ + qts = qtest_initf("-drive if=none,id=drive0,file=null-co://," + "file.read-zeroes=on,format=raw " + "-machine q35 -device pcie-root-port,id=p1 " + "-device pcie-pci-bridge,bus=p1,id=b1 " + "-device virtio-blk-%s,drive=drive0,bus=b1,id=dev0", + qvirtio_get_dev_type()); + + device_del(qts, true); + g_assert(!has_drive(qts)); + + qtest_quit(qts); +} + static void test_empty_device_del(void) { QTestState *qts; + if (!has_device_builtin("virtio-scsi")) { + g_test_skip("Device virtio-scsi is not available"); + return; + } + /* device_del with no drive plugged. */ qts = qtest_initf("-device virtio-scsi-%s -device scsi-cd,id=dev0", qvirtio_get_dev_type()); @@ -276,6 +326,11 @@ static void test_device_add_and_del(void) const char *arch = qtest_get_arch(); const char *machine_addition = ""; + if (!has_device_builtin("virtio-blk")) { + g_test_skip("Device virtio-blk is not available"); + return; + } + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { machine_addition = "-machine pc"; } @@ -294,12 +349,59 @@ static void test_device_add_and_del(void) qtest_quit(qts); } +static void device_add_q35(QTestState *qts) +{ + g_autofree char *driver = g_strdup_printf("virtio-blk-%s", + qvirtio_get_dev_type()); + QDict *response = + qtest_qmp(qts, "{'execute': 'device_add'," + " 'arguments': {" + " 'driver': %s," + " 'drive': 'drive0'," + " 'id': 'dev0'," + " 'bus': 'b1'" + "}}", driver); + g_assert(response); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); +} + +static void test_device_add_and_del_q35(void) +{ + QTestState *qts; + + if (!has_device_builtin("virtio-blk")) { + g_test_skip("Device virtio-blk is not available"); + return; + } + + /* + * -drive/device_add and device_del. Start with a drive used by a + * device that unplugs after reset. + */ + qts = qtest_initf("-machine q35 -device pcie-root-port,id=p1 " + "-device pcie-pci-bridge,bus=p1,id=b1 " + "-drive if=none,id=drive0,file=null-co://," + "file.read-zeroes=on,format=raw"); + + device_add_q35(qts); + device_del(qts, true); + g_assert(!has_drive(qts)); + + qtest_quit(qts); +} + static void test_drive_add_device_add_and_del(void) { QTestState *qts; const char *arch = qtest_get_arch(); const char *machine_addition = ""; + if (!has_device_builtin("virtio-blk")) { + g_test_skip("Device virtio-blk is not available"); + return; + } + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { machine_addition = "-machine pc"; } @@ -318,12 +420,41 @@ static void test_drive_add_device_add_and_del(void) qtest_quit(qts); } +static void test_drive_add_device_add_and_del_q35(void) +{ + QTestState *qts; + + if (!has_device_builtin("virtio-blk")) { + g_test_skip("Device virtio-blk is not available"); + return; + } + + qts = qtest_init("-machine q35 -device pcie-root-port,id=p1 " + "-device pcie-pci-bridge,bus=p1,id=b1"); + + /* + * drive_add/device_add and device_del. The drive is used by a + * device that unplugs after reset. + */ + drive_add_with_media(qts); + device_add_q35(qts); + device_del(qts, true); + g_assert(!has_drive(qts)); + + qtest_quit(qts); +} + static void test_blockdev_add_device_add_and_del(void) { QTestState *qts; const char *arch = qtest_get_arch(); const char *machine_addition = ""; + if (!has_device_builtin("virtio-blk")) { + g_test_skip("Device virtio-blk is not available"); + return; + } + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { machine_addition = "-machine pc"; } @@ -331,7 +462,7 @@ static void test_blockdev_add_device_add_and_del(void) qts = qtest_init(machine_addition); /* - * blockdev_add/device_add and device_del. The it drive is used by a + * blockdev_add/device_add and device_del. The drive is used by a * device that unplugs after reset, but it doesn't go away. */ blockdev_add_with_media(qts); @@ -342,6 +473,30 @@ static void test_blockdev_add_device_add_and_del(void) qtest_quit(qts); } +static void test_blockdev_add_device_add_and_del_q35(void) +{ + QTestState *qts; + + if (!has_device_builtin("virtio-blk")) { + g_test_skip("Device virtio-blk is not available"); + return; + } + + qts = qtest_init("-machine q35 -device pcie-root-port,id=p1 " + "-device pcie-pci-bridge,bus=p1,id=b1"); + + /* + * blockdev_add/device_add and device_del. The drive is used by a + * device that unplugs after reset, but it doesn't go away. + */ + blockdev_add_with_media(qts); + device_add_q35(qts); + device_del(qts, true); + g_assert(has_blockdev(qts)); + + qtest_quit(qts); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -363,6 +518,17 @@ int main(int argc, char **argv) test_empty_device_del); qtest_add_func("/device_del/blockdev", test_blockdev_add_device_add_and_del); + + if (qtest_has_machine("q35")) { + qtest_add_func("/device_del/drive/cli_device_q35", + test_cli_device_del_q35); + qtest_add_func("/device_del/drive/device_add_q35", + test_device_add_and_del_q35); + qtest_add_func("/device_del/drive/drive_add_device_add_q35", + test_drive_add_device_add_and_del_q35); + qtest_add_func("/device_del/blockdev_q35", + test_blockdev_add_device_add_and_del_q35); + } } return g_test_run(); diff --git a/tests/qtest/ds1338-test.c b/tests/qtest/ds1338-test.c index f6ade9a050..d12424d27f 100644 --- a/tests/qtest/ds1338-test.c +++ b/tests/qtest/ds1338-test.c @@ -38,7 +38,7 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) i2c_read_block(i2cdev, 0, resp, sizeof(resp)); - /* check retrieved time againt local time */ + /* check retrieved time against local time */ g_assert_cmpuint(bcd2bin(resp[4]), == , tm_ptr->tm_mday); g_assert_cmpuint(bcd2bin(resp[5]), == , 1 + tm_ptr->tm_mon); g_assert_cmpuint(2000 + bcd2bin(resp[6]), == , 1900 + tm_ptr->tm_year); diff --git a/tests/qtest/e1000-test.c b/tests/qtest/e1000-test.c index c387984ef6..4e0d7a5607 100644 --- a/tests/qtest/e1000-test.c +++ b/tests/qtest/e1000-test.c @@ -35,7 +35,7 @@ static void *e1000_get_driver(void *obj, const char *interface) return &e1000->dev; } - fprintf(stderr, "%s not present in e1000e\n", interface); + fprintf(stderr, "%s not present in e1000\n", interface); g_assert_not_reached(); } diff --git a/tests/qtest/e1000e-test.c b/tests/qtest/e1000e-test.c index c98779c7c0..de9738fdb7 100644 --- a/tests/qtest/e1000e-test.c +++ b/tests/qtest/e1000e-test.c @@ -1,4 +1,4 @@ - /* +/* * QTest testcase for e1000e NIC * * Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) @@ -27,57 +27,39 @@ #include "qemu/osdep.h" #include "libqtest-single.h" #include "libqos/pci-pc.h" +#include "net/eth.h" #include "qemu/sockets.h" #include "qemu/iov.h" #include "qemu/module.h" #include "qemu/bitops.h" -#include "libqos/malloc.h" +#include "libqos/libqos-malloc.h" #include "libqos/e1000e.h" +#include "hw/net/e1000_regs.h" + +static const struct eth_header packet = { + .h_dest = E1000E_ADDRESS, + .h_source = E1000E_ADDRESS, +}; static void e1000e_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) { - struct { - uint64_t buffer_addr; - union { - uint32_t data; - struct { - uint16_t length; - uint8_t cso; - uint8_t cmd; - } flags; - } lower; - union { - uint32_t data; - struct { - uint8_t status; - uint8_t css; - uint16_t special; - } fields; - } upper; - } descr; - - static const uint32_t dtyp_data = BIT(20); - static const uint32_t dtyp_ext = BIT(29); - static const uint32_t dcmd_rs = BIT(27); - static const uint32_t dcmd_eop = BIT(24); - static const uint32_t dsta_dd = BIT(0); - static const int data_len = 64; + struct e1000_tx_desc descr; char buffer[64]; int ret; uint32_t recv_len; /* Prepare test data buffer */ - uint64_t data = guest_alloc(alloc, data_len); - memwrite(data, "TEST", 5); + uint64_t data = guest_alloc(alloc, sizeof(buffer)); + memwrite(data, &packet, sizeof(packet)); /* Prepare TX descriptor */ memset(&descr, 0, sizeof(descr)); descr.buffer_addr = cpu_to_le64(data); - descr.lower.data = cpu_to_le32(dcmd_rs | - dcmd_eop | - dtyp_ext | - dtyp_data | - data_len); + descr.lower.data = cpu_to_le32(E1000_TXD_CMD_RS | + E1000_TXD_CMD_EOP | + E1000_TXD_CMD_DEXT | + E1000_TXD_DTYP_D | + sizeof(buffer)); /* Put descriptor to the ring */ e1000e_tx_ring_push(d, &descr); @@ -86,14 +68,15 @@ static void e1000e_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *a e1000e_wait_isr(d, E1000E_TX0_MSG_ID); /* Check DD bit */ - g_assert_cmphex(le32_to_cpu(descr.upper.data) & dsta_dd, ==, dsta_dd); + g_assert_cmphex(le32_to_cpu(descr.upper.data) & E1000_TXD_STAT_DD, ==, + E1000_TXD_STAT_DD); /* Check data sent to the backend */ ret = recv(test_sockets[0], &recv_len, sizeof(recv_len), 0); g_assert_cmpint(ret, == , sizeof(recv_len)); - ret = recv(test_sockets[0], buffer, 64, 0); - g_assert_cmpint(ret, >=, 5); - g_assert_cmpstr(buffer, == , "TEST"); + ret = recv(test_sockets[0], buffer, sizeof(buffer), 0); + g_assert_cmpint(ret, ==, sizeof(buffer)); + g_assert_false(memcmp(buffer, &packet, sizeof(packet))); /* Free test data buffer */ guest_free(alloc, data); @@ -101,54 +84,29 @@ static void e1000e_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *a static void e1000e_receive_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) { - union { - struct { - uint64_t buffer_addr; - uint64_t reserved; - } read; - struct { - struct { - uint32_t mrq; - union { - uint32_t rss; - struct { - uint16_t ip_id; - uint16_t csum; - } csum_ip; - } hi_dword; - } lower; - struct { - uint32_t status_error; - uint16_t length; - uint16_t vlan; - } upper; - } wb; - } descr; + union e1000_rx_desc_extended descr; - static const uint32_t esta_dd = BIT(0); - - char test[] = "TEST"; - int len = htonl(sizeof(test)); + struct eth_header test_iov = packet; + int len = htonl(sizeof(packet)); struct iovec iov[] = { { .iov_base = &len, .iov_len = sizeof(len), },{ - .iov_base = test, - .iov_len = sizeof(test), + .iov_base = &test_iov, + .iov_len = sizeof(packet), }, }; - static const int data_len = 64; char buffer[64]; int ret; /* Send a dummy packet to device's socket*/ - ret = iov_send(test_sockets[0], iov, 2, 0, sizeof(len) + sizeof(test)); - g_assert_cmpint(ret, == , sizeof(test) + sizeof(len)); + ret = iov_send(test_sockets[0], iov, 2, 0, sizeof(len) + sizeof(packet)); + g_assert_cmpint(ret, == , sizeof(packet) + sizeof(len)); /* Prepare test data buffer */ - uint64_t data = guest_alloc(alloc, data_len); + uint64_t data = guest_alloc(alloc, sizeof(buffer)); /* Prepare RX descriptor */ memset(&descr, 0, sizeof(descr)); @@ -162,11 +120,11 @@ static void e1000e_receive_verify(QE1000E *d, int *test_sockets, QGuestAllocator /* Check DD bit */ g_assert_cmphex(le32_to_cpu(descr.wb.upper.status_error) & - esta_dd, ==, esta_dd); + E1000_RXD_STAT_DD, ==, E1000_RXD_STAT_DD); /* Check data sent to the backend */ memread(data, buffer, sizeof(buffer)); - g_assert_cmpstr(buffer, == , "TEST"); + g_assert_false(memcmp(buffer, &packet, sizeof(packet))); /* Free test data buffer */ guest_free(alloc, data); diff --git a/tests/qtest/erst-test.c b/tests/qtest/erst-test.c index 4e768a126f..c45bee7f05 100644 --- a/tests/qtest/erst-test.c +++ b/tests/qtest/erst-test.c @@ -98,7 +98,7 @@ static void setup_vm_cmd(ERSTState *s, const char *cmd) const char *arch = qtest_get_arch(); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - s->qs = qtest_pc_boot(cmd); + s->qs = qtest_pc_boot("%s", cmd); } else { g_printerr("erst-test tests are only available on x86\n"); exit(EXIT_FAILURE); @@ -154,10 +154,7 @@ static void test_acpi_erst_basic(void) int main(int argc, char **argv) { - int ret; - g_test_init(&argc, &argv, NULL); qtest_add_func("/acpi-erst/basic", test_acpi_erst_basic); - ret = g_test_run(); - return ret; + return g_test_run(); } diff --git a/tests/qtest/es1370-test.c b/tests/qtest/es1370-test.c index adccdac1be..8387e74193 100644 --- a/tests/qtest/es1370-test.c +++ b/tests/qtest/es1370-test.c @@ -28,7 +28,7 @@ static void *es1370_get_driver(void *obj, const char *interface) return &es1370->dev; } - fprintf(stderr, "%s not present in e1000e\n", interface); + fprintf(stderr, "%s not present in es1370\n", interface); g_assert_not_reached(); } @@ -46,7 +46,8 @@ static void *es1370_create(void *pci_bus, QGuestAllocator *alloc, void *addr) static void es1370_register_nodes(void) { QOSGraphEdgeOptions opts = { - .extra_device_opts = "addr=04.0", + .extra_device_opts = "addr=04.0,audiodev=audio0", + .before_cmd_line = "-audiodev driver=none,id=audio0", }; add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); diff --git a/tests/qtest/fdc-test.c b/tests/qtest/fdc-test.c index 52ade90a7d..5e8fbda9df 100644 --- a/tests/qtest/fdc-test.c +++ b/tests/qtest/fdc-test.c @@ -28,9 +28,6 @@ #include "libqtest-single.h" #include "qapi/qmp/qdict.h" -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) - #define DRIVE_FLOPPY_BLANK \ "-drive if=floppy,file=null-co://,file.read-zeroes=on,format=raw,size=1440k" @@ -68,7 +65,7 @@ enum { DSKCHG = 0x80, }; -static char test_image[] = "/tmp/qtest.XXXXXX"; +static char *test_image; #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) @@ -304,9 +301,10 @@ static void test_media_insert(void) /* Insert media in drive. DSKCHK should not be reset until a step pulse * is sent. */ - qmp_discard_response("{'execute':'blockdev-change-medium', 'arguments':{" - " 'id':'floppy0', 'filename': %s, 'format': 'raw' }}", - test_image); + qtest_qmp_assert_success(global_qtest, + "{'execute':'blockdev-change-medium', 'arguments':{" + " 'id':'floppy0', 'filename': %s, 'format': 'raw' }}", + test_image); dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); @@ -335,8 +333,9 @@ static void test_media_change(void) /* Eject the floppy and check that DSKCHG is set. Reading it out doesn't * reset the bit. */ - qmp_discard_response("{'execute':'eject', 'arguments':{" - " 'id':'floppy0' }}"); + qtest_qmp_assert_success(global_qtest, + "{'execute':'eject', 'arguments':{" + " 'id':'floppy0' }}"); dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); @@ -608,7 +607,7 @@ int main(int argc, char **argv) int ret; /* Create a temporary raw image */ - fd = mkstemp(test_image); + fd = g_file_open_tmp("qtest.XXXXXX", &test_image, NULL); g_assert(fd >= 0); ret = ftruncate(fd, TEST_IMAGE_SIZE); g_assert(ret == 0); @@ -640,6 +639,7 @@ int main(int argc, char **argv) /* Cleanup */ qtest_end(); unlink(test_image); + g_free(test_image); return ret; } diff --git a/tests/qtest/fuzz-lsi53c895a-test.c b/tests/qtest/fuzz-lsi53c895a-test.c index 2e8e67859e..1b55928b9f 100644 --- a/tests/qtest/fuzz-lsi53c895a-test.c +++ b/tests/qtest/fuzz-lsi53c895a-test.c @@ -8,6 +8,109 @@ #include "qemu/osdep.h" #include "libqtest.h" +/* + * This used to trigger a DMA reentrancy issue + * leading to memory corruption bugs like stack + * overflow or use-after-free + * https://gitlab.com/qemu-project/qemu/-/issues/1563 + */ +static void test_lsi_dma_reentrancy(void) +{ + QTestState *s; + + s = qtest_init("-M q35 -m 512M -nodefaults " + "-blockdev driver=null-co,node-name=null0 " + "-device lsi53c810 -device scsi-cd,drive=null0"); + + qtest_outl(s, 0xcf8, 0x80000804); /* PCI Command Register */ + qtest_outw(s, 0xcfc, 0x7); /* Enables accesses */ + qtest_outl(s, 0xcf8, 0x80000814); /* Memory Bar 1 */ + qtest_outl(s, 0xcfc, 0xff100000); /* Set MMIO Address*/ + qtest_outl(s, 0xcf8, 0x80000818); /* Memory Bar 2 */ + qtest_outl(s, 0xcfc, 0xff000000); /* Set RAM Address*/ + qtest_writel(s, 0xff000000, 0xc0000024); + qtest_writel(s, 0xff000114, 0x00000080); + qtest_writel(s, 0xff00012c, 0xff000000); + qtest_writel(s, 0xff000004, 0xff000114); + qtest_writel(s, 0xff000008, 0xff100014); + qtest_writel(s, 0xff10002f, 0x000000ff); + + qtest_quit(s); +} + +/* + * This used to trigger a UAF in lsi_do_msgout() + * https://gitlab.com/qemu-project/qemu/-/issues/972 + */ +static void test_lsi_do_msgout_cancel_req(void) +{ + QTestState *s; + + if (sizeof(void *) == 4) { + g_test_skip("memory size too big for 32-bit build"); + return; + } + + s = qtest_init("-M q35 -m 2G -nodefaults " + "-device lsi53c895a,id=scsi " + "-device scsi-hd,drive=disk0 " + "-drive file=null-co://,id=disk0,if=none,format=raw"); + + qtest_outl(s, 0xcf8, 0x80000810); + qtest_outl(s, 0xcf8, 0xc000); + qtest_outl(s, 0xcf8, 0x80000810); + qtest_outw(s, 0xcfc, 0x7); + qtest_outl(s, 0xcf8, 0x80000810); + qtest_outl(s, 0xcfc, 0xc000); + qtest_outl(s, 0xcf8, 0x80000804); + qtest_outw(s, 0xcfc, 0x05); + qtest_writeb(s, 0x69736c10, 0x08); + qtest_writeb(s, 0x69736c13, 0x58); + qtest_writeb(s, 0x69736c1a, 0x01); + qtest_writeb(s, 0x69736c1b, 0x06); + qtest_writeb(s, 0x69736c22, 0x01); + qtest_writeb(s, 0x69736c23, 0x07); + qtest_writeb(s, 0x69736c2b, 0x02); + qtest_writeb(s, 0x69736c48, 0x08); + qtest_writeb(s, 0x69736c4b, 0x58); + qtest_writeb(s, 0x69736c52, 0x04); + qtest_writeb(s, 0x69736c53, 0x06); + qtest_writeb(s, 0x69736c5b, 0x02); + qtest_outl(s, 0xc02d, 0x697300); + qtest_writeb(s, 0x5a554662, 0x01); + qtest_writeb(s, 0x5a554663, 0x07); + qtest_writeb(s, 0x5a55466a, 0x10); + qtest_writeb(s, 0x5a55466b, 0x22); + qtest_writeb(s, 0x5a55466c, 0x5a); + qtest_writeb(s, 0x5a55466d, 0x5a); + qtest_writeb(s, 0x5a55466e, 0x34); + qtest_writeb(s, 0x5a55466f, 0x5a); + qtest_writeb(s, 0x5a345a5a, 0x77); + qtest_writeb(s, 0x5a345a5b, 0x55); + qtest_writeb(s, 0x5a345a5c, 0x51); + qtest_writeb(s, 0x5a345a5d, 0x27); + qtest_writeb(s, 0x27515577, 0x41); + qtest_outl(s, 0xc02d, 0x5a5500); + qtest_writeb(s, 0x364001d0, 0x08); + qtest_writeb(s, 0x364001d3, 0x58); + qtest_writeb(s, 0x364001da, 0x01); + qtest_writeb(s, 0x364001db, 0x26); + qtest_writeb(s, 0x364001dc, 0x0d); + qtest_writeb(s, 0x364001dd, 0xae); + qtest_writeb(s, 0x364001de, 0x41); + qtest_writeb(s, 0x364001df, 0x5a); + qtest_writeb(s, 0x5a41ae0d, 0xf8); + qtest_writeb(s, 0x5a41ae0e, 0x36); + qtest_writeb(s, 0x5a41ae0f, 0xd7); + qtest_writeb(s, 0x5a41ae10, 0x36); + qtest_writeb(s, 0x36d736f8, 0x0c); + qtest_writeb(s, 0x36d736f9, 0x80); + qtest_writeb(s, 0x36d736fa, 0x0d); + qtest_outl(s, 0xc02d, 0x364000); + + qtest_quit(s); +} + /* * This used to trigger the assert in lsi_do_dma() * https://bugs.launchpad.net/qemu/+bug/697510 @@ -41,8 +144,18 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); + if (!qtest_has_device("lsi53c895a")) { + return 0; + } + qtest_add_func("fuzz/lsi53c895a/lsi_do_dma_empty_queue", test_lsi_do_dma_empty_queue); + qtest_add_func("fuzz/lsi53c895a/lsi_do_msgout_cancel_req", + test_lsi_do_msgout_cancel_req); + + qtest_add_func("fuzz/lsi53c895a/lsi_dma_reentrancy", + test_lsi_dma_reentrancy); + return g_test_run(); } diff --git a/tests/qtest/fuzz-megasas-test.c b/tests/qtest/fuzz-megasas-test.c index 287fe19fc7..8d7ed3723a 100644 --- a/tests/qtest/fuzz-megasas-test.c +++ b/tests/qtest/fuzz-megasas-test.c @@ -40,7 +40,7 @@ static void test_lp1878263_megasas_zero_iov_cnt(void) */ static void test_gitlab_issue521_megasas_sgl_ovf(void) { - QTestState *s = qtest_init("-display none -m 32M -machine q35 " + QTestState *s = qtest_init("-m 32M -machine q35 " "-nodefaults -device megasas " "-device scsi-cd,drive=null0 " "-blockdev " diff --git a/tests/qtest/fuzz-sb16-test.c b/tests/qtest/fuzz-sb16-test.c index a65826b943..fc445b1871 100644 --- a/tests/qtest/fuzz-sb16-test.c +++ b/tests/qtest/fuzz-sb16-test.c @@ -15,7 +15,7 @@ */ static void test_fuzz_sb16_0x1c(void) { - QTestState *s = qtest_init("-M q35 -display none " + QTestState *s = qtest_init("-M q35 " "-device sb16,audiodev=snd0 " "-audiodev none,id=snd0"); qtest_outw(s, 0x22c, 0x41); @@ -27,7 +27,7 @@ static void test_fuzz_sb16_0x1c(void) static void test_fuzz_sb16_0x91(void) { - QTestState *s = qtest_init("-M pc -display none " + QTestState *s = qtest_init("-M pc " "-device sb16,audiodev=none " "-audiodev id=none,driver=none"); qtest_outw(s, 0x22c, 0xf141); @@ -43,7 +43,7 @@ static void test_fuzz_sb16_0x91(void) */ static void test_fuzz_sb16_0xd4(void) { - QTestState *s = qtest_init("-M pc -display none " + QTestState *s = qtest_init("-M pc " "-device sb16,audiodev=none " "-audiodev id=none,driver=none"); qtest_outb(s, 0x22c, 0x41); @@ -57,9 +57,13 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - qtest_add_func("fuzz/test_fuzz_sb16/1c", test_fuzz_sb16_0x1c); - qtest_add_func("fuzz/test_fuzz_sb16/91", test_fuzz_sb16_0x91); - qtest_add_func("fuzz/test_fuzz_sb16/d4", test_fuzz_sb16_0xd4); + if (qtest_has_machine("q35")) { + qtest_add_func("fuzz/test_fuzz_sb16/1c", test_fuzz_sb16_0x1c); + } + if (qtest_has_machine("pc")) { + qtest_add_func("fuzz/test_fuzz_sb16/91", test_fuzz_sb16_0x91); + qtest_add_func("fuzz/test_fuzz_sb16/d4", test_fuzz_sb16_0xd4); + } return g_test_run(); } diff --git a/tests/qtest/fuzz-sdcard-test.c b/tests/qtest/fuzz-sdcard-test.c index e7fd818148..cd134cdf55 100644 --- a/tests/qtest/fuzz-sdcard-test.c +++ b/tests/qtest/fuzz-sdcard-test.c @@ -18,7 +18,7 @@ static void oss_fuzz_29225(void) { QTestState *s; - s = qtest_init(" -display none -m 512m -nodefaults -nographic" + s = qtest_init(" -m 512m -nodefaults -nographic" " -device sdhci-pci,sd-spec-version=3" " -device sd-card,drive=d0" " -drive if=none,index=0,file=null-co://,format=raw,id=d0"); @@ -61,7 +61,7 @@ static void oss_fuzz_36217(void) { QTestState *s; - s = qtest_init(" -display none -m 32 -nodefaults -nographic" + s = qtest_init(" -m 32 -nodefaults -nographic" " -device sdhci-pci,sd-spec-version=3 " "-device sd-card,drive=d0 " "-drive if=none,index=0,file=null-co://,format=raw,id=d0"); @@ -95,7 +95,7 @@ static void oss_fuzz_36391(void) { QTestState *s; - s = qtest_init(" -display none -m 512M -nodefaults -nographic" + s = qtest_init(" -m 512M -nodefaults -nographic" " -device sdhci-pci,sd-spec-version=3" " -device sd-card,drive=drv" " -drive if=none,index=0,file=null-co://,format=raw,id=drv"); diff --git a/tests/qtest/fuzz-virtio-scsi-test.c b/tests/qtest/fuzz-virtio-scsi-test.c index 71c91b0356..e37b48b2cc 100644 --- a/tests/qtest/fuzz-virtio-scsi-test.c +++ b/tests/qtest/fuzz-virtio-scsi-test.c @@ -19,7 +19,7 @@ static void test_mmio_oob_from_memory_region_cache(void) { QTestState *s; - s = qtest_init("-M pc-q35-5.2 -display none -m 512M " + s = qtest_init("-M pc-q35-5.2 -m 512M " "-device virtio-scsi,num_queues=8,addr=03.0 "); qtest_outl(s, 0xcf8, 0x80001811); diff --git a/tests/qtest/fuzz-xlnx-dp-test.c b/tests/qtest/fuzz-xlnx-dp-test.c index 51e9a37300..e8c483965f 100644 --- a/tests/qtest/fuzz-xlnx-dp-test.c +++ b/tests/qtest/fuzz-xlnx-dp-test.c @@ -14,7 +14,7 @@ */ static void test_fuzz_xlnx_dp_0x3ac(void) { - QTestState *s = qtest_init("-M xlnx-zcu102 -display none "); + QTestState *s = qtest_init("-M xlnx-zcu102 "); qtest_readl(s, 0xfd4a03ac); qtest_quit(s); } diff --git a/tests/qtest/fuzz/fork_fuzz.c b/tests/qtest/fuzz/fork_fuzz.c deleted file mode 100644 index 6ffb2a7937..0000000000 --- a/tests/qtest/fuzz/fork_fuzz.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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) -{ - /* Copy what's in the counter region to a temporary buffer.. */ - void *copy = malloc(&__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START); - memcpy(copy, - &__FUZZ_COUNTERS_START, - &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START); - - /* Map a shared region over the counter region */ - if (mmap(&__FUZZ_COUNTERS_START, - &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START, - PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, - 0, 0) == MAP_FAILED) { - perror("Error: "); - exit(1); - } - - /* Copy the original data back to the counter-region */ - memcpy(&__FUZZ_COUNTERS_START, copy, - &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START); - free(copy); -} - - diff --git a/tests/qtest/fuzz/fork_fuzz.h b/tests/qtest/fuzz/fork_fuzz.h deleted file mode 100644 index 9ecb8b58ef..0000000000 --- a/tests/qtest/fuzz/fork_fuzz.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 deleted file mode 100644 index cfb88b7fdb..0000000000 --- a/tests/qtest/fuzz/fork_fuzz.ld +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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); - } -} -INSERT AFTER .data; - -SECTIONS -{ - .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*); - /* - * In case the above line fails, explicitly specify the (mangled) name of - * the object we care about - */ - *(.bss._ZN6fuzzer3TPCE); - } -} -INSERT AFTER .data.fuzz_start; - -SECTIONS -{ - .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.fuzz_ordered; diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c index 0ad4ba9e94..9b9c9f9c36 100644 --- a/tests/qtest/fuzz/fuzz.c +++ b/tests/qtest/fuzz/fuzz.c @@ -51,6 +51,12 @@ void flush_events(QTestState *s) } } +void fuzz_reset(QTestState *s) +{ + qemu_system_reset(SHUTDOWN_CAUSE_GUEST_RESET); + main_loop_wait(true); +} + static QTestState *qtest_setup(void) { qtest_server_set_send_handler(&qtest_client_inproc_recv, &fuzz_qts); @@ -158,8 +164,6 @@ int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp) { char *target_name; - const char *bindir; - char *datadir; GString *cmd_line; gchar *pretty_cmd_line; bool serialize = false; @@ -174,22 +178,6 @@ int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp) target_name = strstr(**argv, "-target-"); if (target_name) { /* The binary name specifies the target */ target_name += strlen("-target-"); - /* - * With oss-fuzz, the executable is kept in the root of a directory (we - * cannot assume the path). All data (including bios binaries) must be - * in the same dir, or a subdir. Thus, we cannot place the pc-bios so - * that it would be in exec_dir/../pc-bios. - * As a workaround, oss-fuzz allows us to use argv[0] to get the - * location of the executable. Using this we add exec_dir/pc-bios to - * the datadirs. - */ - bindir = qemu_get_exec_dir(); - datadir = g_build_filename(bindir, "pc-bios", NULL); - if (g_file_test(datadir, G_FILE_TEST_IS_DIR)) { - qemu_add_data_dir(datadir); - } else { - g_free(datadir); - } } else if (*argc > 1) { /* The target is specified as an argument */ target_name = (*argv)[1]; if (!strstr(target_name, "--fuzz-target=")) { @@ -219,7 +207,7 @@ int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp) fuzz_target->pre_vm_init(); } - /* Run QEMU's softmmu main with the fuzz-target dependent arguments */ + /* Run QEMU's system main with the fuzz-target dependent arguments */ cmd_line = fuzz_target->get_init_cmdline(fuzz_target); g_string_append_printf(cmd_line, " %s -qtest /dev/null ", getenv("QTEST_LOG") ? "" : "-qtest-log none"); @@ -236,7 +224,7 @@ int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp) g_free(pretty_cmd_line); } - qemu_init(result.we_wordc, result.we_wordv, NULL); + qemu_init(result.we_wordc, result.we_wordv); /* re-enable the rcu atfork, which was previously disabled in qemu_init */ rcu_enable_atfork(); diff --git a/tests/qtest/fuzz/fuzz.h b/tests/qtest/fuzz/fuzz.h index 327c1c5a55..7da0bc3d7e 100644 --- a/tests/qtest/fuzz/fuzz.h +++ b/tests/qtest/fuzz/fuzz.h @@ -49,13 +49,13 @@ typedef struct FuzzTarget { /* - * Returns the arguments that are passed to qemu/softmmu init(). Freed by + * Returns the arguments that are passed to qemu/system init(). Freed by * the caller. */ GString *(*get_init_cmdline)(struct FuzzTarget *); /* - * will run once, prior to running qemu/softmmu init. + * will run once, prior to running qemu/system init. * eg: set up shared-memory for communication with the child-process * Can be NULL */ @@ -103,7 +103,7 @@ typedef struct FuzzTarget { } FuzzTarget; void flush_events(QTestState *); -void reboot(QTestState *); +void fuzz_reset(QTestState *); /* Use the QTest ASCII protocol or call address_space API directly?*/ void fuzz_qtest_set_serialize(bool option); diff --git a/tests/qtest/fuzz/generic_fuzz.c b/tests/qtest/fuzz/generic_fuzz.c index 25df19fd5a..ec842e03c5 100644 --- a/tests/qtest/fuzz/generic_fuzz.c +++ b/tests/qtest/fuzz/generic_fuzz.c @@ -18,16 +18,18 @@ #include "tests/qtest/libqtest.h" #include "tests/qtest/libqos/pci-pc.h" #include "fuzz.h" -#include "fork_fuzz.h" #include "string.h" #include "exec/memory.h" #include "exec/ramblock.h" #include "hw/qdev-core.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/boards.h" #include "generic_fuzz_configs.h" #include "hw/mem/sparse-mem.h" +static void pci_enum(gpointer pcidev, gpointer bus); + /* * SEPARATOR is used to separate "operations" in the fuzz input */ @@ -46,10 +48,10 @@ enum cmds { OP_CLOCK_STEP, }; -#define DEFAULT_TIMEOUT_US 100000 #define USEC_IN_SEC 1000000000 #define MAX_DMA_FILL_SIZE 0x10000 +#define MAX_TOTAL_DMA_SIZE 0x10000000 #define PCI_HOST_BRIDGE_CFG 0xcf8 #define PCI_HOST_BRIDGE_DATA 0xcfc @@ -59,9 +61,8 @@ typedef struct { ram_addr_t size; /* The number of bytes until the end of the I/O region */ } address_range; -static useconds_t timeout = DEFAULT_TIMEOUT_US; - static bool qtest_log_enabled; +size_t dma_bytes_written; MemoryRegion *sparse_mem_mr; @@ -144,7 +145,7 @@ static void *pattern_alloc(pattern p, size_t len) return buf; } -static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) +static int fuzz_memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) { unsigned access_size_max = mr->ops->valid.max_access_size; @@ -195,6 +196,7 @@ void fuzz_dma_read_cb(size_t addr, size_t len, MemoryRegion *mr) */ if (dma_patterns->len == 0 || len == 0 + || dma_bytes_written + len > MAX_TOTAL_DMA_SIZE || (mr != current_machine->ram && mr != sparse_mem_mr)) { return; } @@ -242,11 +244,12 @@ void fuzz_dma_read_cb(size_t addr, size_t len, MemoryRegion *mr) /* * If mr1 isn't RAM, address_space_translate doesn't update l. Use - * memory_access_size to identify the number of bytes that it is safe - * to write without accidentally writing to another MemoryRegion. + * fuzz_memory_access_size to identify the number of bytes that it + * is safe to write without accidentally writing to another + * MemoryRegion. */ if (!memory_region_is_ram(mr1)) { - l = memory_access_size(mr1, l, addr1); + l = fuzz_memory_access_size(mr1, l, addr1); } if (memory_region_is_ram(mr1) || memory_region_is_romd(mr1) || @@ -266,6 +269,7 @@ void fuzz_dma_read_cb(size_t addr, size_t len, MemoryRegion *mr) fflush(stderr); } qtest_memwrite(qts_global, addr, buf, l); + dma_bytes_written += l; } len -= l; buf += l; @@ -587,30 +591,6 @@ static void op_disable_pci(QTestState *s, const unsigned char *data, size_t len) pci_disabled = true; } -static void handle_timeout(int sig) -{ - if (qtest_log_enabled) { - fprintf(stderr, "[Timeout]\n"); - fflush(stderr); - } - - /* - * If there is a crash, libfuzzer/ASAN forks a child to run an - * "llvm-symbolizer" process for printing out a pretty stacktrace. It - * communicates with this child using a pipe. If we timeout+Exit, while - * libfuzzer is still communicating with the llvm-symbolizer child, we will - * be left with an orphan llvm-symbolizer process. Sometimes, this appears - * to lead to a deadlock in the forkserver. Use waitpid to check if there - * are any waitable children. If so, exit out of the signal-handler, and - * let libfuzzer finish communicating with the child, and exit, on its own. - */ - if (waitpid(-1, NULL, WNOHANG) == 0) { - return; - } - - _Exit(0); -} - /* * Here, we interpret random bytes from the fuzzer, as a sequence of commands. * Some commands can be variable-width, so we use a separator, SEPARATOR, to @@ -667,64 +647,33 @@ static void generic_fuzz(QTestState *s, const unsigned char *Data, size_t Size) size_t cmd_len; uint8_t op; - if (fork() == 0) { - struct sigaction sact; - struct itimerval timer; - sigset_t set; - /* - * Sometimes the fuzzer will find inputs that take quite a long time to - * process. Often times, these inputs do not result in new coverage. - * Even if these inputs might be interesting, they can slow down the - * fuzzer, overall. Set a timeout for each command to avoid hurting - * performance, too much - */ - if (timeout) { + op_clear_dma_patterns(s, NULL, 0); + pci_disabled = false; + dma_bytes_written = 0; - sigemptyset(&sact.sa_mask); - sact.sa_flags = SA_NODEFER; - sact.sa_handler = handle_timeout; - sigaction(SIGALRM, &sact, NULL); + QPCIBus *pcibus = qpci_new_pc(s, NULL); + g_ptr_array_foreach(fuzzable_pci_devices, pci_enum, pcibus); + qpci_free_pc(pcibus); - sigemptyset(&set); - sigaddset(&set, SIGALRM); - pthread_sigmask(SIG_UNBLOCK, &set, NULL); + while (cmd && Size) { + /* Get the length until the next command or end of input */ + nextcmd = memmem(cmd, Size, SEPARATOR, strlen(SEPARATOR)); + cmd_len = nextcmd ? nextcmd - cmd : Size; - memset(&timer, 0, sizeof(timer)); - timer.it_value.tv_sec = timeout / USEC_IN_SEC; - timer.it_value.tv_usec = timeout % USEC_IN_SEC; + if (cmd_len > 0) { + /* Interpret the first byte of the command as an opcode */ + op = *cmd % (sizeof(ops) / sizeof((ops)[0])); + ops[op](s, cmd + 1, cmd_len - 1); + + /* Run the main loop */ + flush_events(s); } - - op_clear_dma_patterns(s, NULL, 0); - pci_disabled = false; - - while (cmd && Size) { - /* Reset the timeout, each time we run a new command */ - if (timeout) { - setitimer(ITIMER_REAL, &timer, NULL); - } - - /* Get the length until the next command or end of input */ - nextcmd = memmem(cmd, Size, SEPARATOR, strlen(SEPARATOR)); - cmd_len = nextcmd ? nextcmd - cmd : Size; - - if (cmd_len > 0) { - /* Interpret the first byte of the command as an opcode */ - op = *cmd % (sizeof(ops) / sizeof((ops)[0])); - ops[op](s, cmd + 1, cmd_len - 1); - - /* Run the main loop */ - flush_events(s); - } - /* Advance to the next command */ - cmd = nextcmd ? nextcmd + sizeof(SEPARATOR) - 1 : nextcmd; - Size = Size - (cmd_len + sizeof(SEPARATOR) - 1); - g_array_set_size(dma_regions, 0); - } - _Exit(0); - } else { - flush_events(s); - wait(0); + /* Advance to the next command */ + cmd = nextcmd ? nextcmd + sizeof(SEPARATOR) - 1 : nextcmd; + Size = Size - (cmd_len + sizeof(SEPARATOR) - 1); + g_array_set_size(dma_regions, 0); } + fuzz_reset(s); } static void usage(void) @@ -736,8 +685,6 @@ static void usage(void) printf("Optionally: QEMU_AVOID_DOUBLE_FETCH= " "Try to avoid racy DMA double fetch bugs? %d by default\n", avoid_double_fetches); - printf("Optionally: QEMU_FUZZ_TIMEOUT= Specify a custom timeout (us). " - "0 to disable. %d by default\n", timeout); exit(0); } @@ -823,7 +770,6 @@ static void generic_pre_fuzz(QTestState *s) { GHashTableIter iter; MemoryRegion *mr; - QPCIBus *pcibus; char **result; GString *name_pattern; @@ -836,9 +782,6 @@ static void generic_pre_fuzz(QTestState *s) if (getenv("QEMU_AVOID_DOUBLE_FETCH")) { avoid_double_fetches = 1; } - if (getenv("QEMU_FUZZ_TIMEOUT")) { - timeout = g_ascii_strtoll(getenv("QEMU_FUZZ_TIMEOUT"), NULL, 0); - } qts_global = s; /* @@ -881,12 +824,6 @@ static void generic_pre_fuzz(QTestState *s) printf("No fuzzable memory regions found...\n"); exit(1); } - - pcibus = qpci_new_pc(s, NULL); - g_ptr_array_foreach(fuzzable_pci_devices, pci_enum, pcibus); - qpci_free_pc(pcibus); - - counter_shm_init(); } /* @@ -909,9 +846,9 @@ static void generic_pre_fuzz(QTestState *s) * functionality B * * This function attempts to produce an input that: - * Ouptut: maps a device's BARs, set up three DMA patterns, triggers - * functionality A device, replaces the DMA patterns with a single - * patten, and triggers device functionality B. + * Output: maps a device's BARs, set up three DMA patterns, triggers + * device functionality A, replaces the DMA patterns with a single + * pattern, and triggers device functionality B. */ static size_t generic_fuzz_crossover(const uint8_t *data1, size_t size1, const uint8_t *data2, size_t size2, uint8_t *out, @@ -993,16 +930,16 @@ static GString *generic_fuzz_predefined_config_cmdline(FuzzTarget *t) g_assert(t->opaque); config = t->opaque; - setenv("QEMU_AVOID_DOUBLE_FETCH", "1", 1); + g_setenv("QEMU_AVOID_DOUBLE_FETCH", "1", 1); if (config->argfunc) { args = config->argfunc(); - setenv("QEMU_FUZZ_ARGS", args, 1); + g_setenv("QEMU_FUZZ_ARGS", args, 1); g_free(args); } else { g_assert_nonnull(config->args); - setenv("QEMU_FUZZ_ARGS", config->args, 1); + g_setenv("QEMU_FUZZ_ARGS", config->args, 1); } - setenv("QEMU_FUZZ_OBJECTS", config->objects, 1); + g_setenv("QEMU_FUZZ_OBJECTS", config->objects, 1); return generic_fuzz_cmdline(t); } @@ -1017,17 +954,10 @@ static void register_generic_fuzz_targets(void) .crossover = generic_fuzz_crossover }); - GString *name; - const generic_fuzz_config *config; - - for (int i = 0; - i < sizeof(predefined_configs) / sizeof(generic_fuzz_config); - i++) { - config = predefined_configs + i; - name = g_string_new("generic-fuzz"); - g_string_append_printf(name, "-%s", config->name); + for (int i = 0; i < ARRAY_SIZE(predefined_configs); i++) { + const generic_fuzz_config *config = predefined_configs + i; fuzz_add_target(&(FuzzTarget){ - .name = name->str, + .name = g_strconcat("generic-fuzz-", config->name, NULL), .description = "Predefined generic-fuzz config.", .get_init_cmdline = generic_fuzz_predefined_config_cmdline, .pre_fuzz = generic_pre_fuzz, diff --git a/tests/qtest/fuzz/generic_fuzz_configs.h b/tests/qtest/fuzz/generic_fuzz_configs.h index 004c701915..4d7c8ca4ec 100644 --- a/tests/qtest/fuzz/generic_fuzz_configs.h +++ b/tests/qtest/fuzz/generic_fuzz_configs.h @@ -20,8 +20,8 @@ typedef struct generic_fuzz_config { } generic_fuzz_config; static inline gchar *generic_fuzzer_virtio_9p_args(void){ - char tmpdir[] = "/tmp/qemu-fuzz.XXXXXX"; - g_assert_nonnull(mkdtemp(tmpdir)); + g_autofree char *tmpdir = g_dir_make_tmp("qemu-fuzz.XXXXXX", NULL); + g_assert_nonnull(tmpdir); return g_strdup_printf("-machine q35 -nodefaults " "-device virtio-9p,fsdev=hshare,mount_tag=hshare " @@ -90,6 +90,11 @@ const generic_fuzz_config predefined_configs[] = { .args = "-M q35 -nodefaults " "-device e1000e,netdev=net0 -netdev user,id=net0", .objects = "e1000e", + },{ + .name = "igb", + .args = "-M q35 -nodefaults " + "-device igb,netdev=net0 -netdev user,id=net0", + .objects = "igb", },{ .name = "cirrus-vga", .args = "-machine q35 -nodefaults -device cirrus-vga", @@ -101,8 +106,10 @@ const generic_fuzz_config predefined_configs[] = { },{ .name = "intel-hda", .args = "-machine q35 -nodefaults -device intel-hda,id=hda0 " - "-device hda-output,bus=hda0.0 -device hda-micro,bus=hda0.0 " - "-device hda-duplex,bus=hda0.0", + "-audiodev driver=none,id=audio0", + "-device hda-output,bus=hda0.0,audiodev=audio0 " + "-device hda-micro,bus=hda0.0,audiodev=audio0 " + "-device hda-duplex,bus=hda0.0,audiodev=audio0", .objects = "intel-hda", },{ .name = "ide-hd", diff --git a/tests/qtest/fuzz/i440fx_fuzz.c b/tests/qtest/fuzz/i440fx_fuzz.c index b17fc725df..155fe018f8 100644 --- a/tests/qtest/fuzz/i440fx_fuzz.c +++ b/tests/qtest/fuzz/i440fx_fuzz.c @@ -18,7 +18,6 @@ #include "tests/qtest/libqos/pci-pc.h" #include "fuzz.h" #include "qos_fuzz.h" -#include "fork_fuzz.h" #define I440FX_PCI_HOST_BRIDGE_CFG 0xcf8 @@ -89,6 +88,7 @@ static void i440fx_fuzz_qtest(QTestState *s, size_t Size) { ioport_fuzz_qtest(s, Data, Size); + fuzz_reset(s); } static void pciconfig_fuzz_qos(QTestState *s, QPCIBus *bus, @@ -145,17 +145,6 @@ static void i440fx_fuzz_qos(QTestState *s, pciconfig_fuzz_qos(s, bus, Data, Size); } -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 { - flush_events(s); - wait(NULL); - } -} - static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest" " -m 0 -display none"; static GString *i440fx_argv(FuzzTarget *t) @@ -163,10 +152,6 @@ static GString *i440fx_argv(FuzzTarget *t) return g_string_new(i440fx_qtest_argv); } -static void fork_init(void) -{ - counter_shm_init(); -} static void register_pci_fuzz_targets(void) { @@ -178,16 +163,6 @@ static void register_pci_fuzz_targets(void) .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 diff --git a/tests/qtest/fuzz/meson.build b/tests/qtest/fuzz/meson.build index 189901d4a2..4d10b47b8f 100644 --- a/tests/qtest/fuzz/meson.build +++ b/tests/qtest/fuzz/meson.build @@ -2,7 +2,7 @@ if not get_option('fuzzing') subdir_done() endif -specific_fuzz_ss.add(files('fuzz.c', 'fork_fuzz.c', 'qos_fuzz.c', +specific_fuzz_ss.add(files('fuzz.c', 'qos_fuzz.c', 'qtest_wrappers.c'), qos) # Targets @@ -12,7 +12,7 @@ specific_fuzz_ss.add(when: 'CONFIG_VIRTIO_SCSI', if_true: files('virtio_scsi_fuz specific_fuzz_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio_blk_fuzz.c')) specific_fuzz_ss.add(files('generic_fuzz.c')) -fork_fuzz = declare_dependency( +fuzz_ld = declare_dependency( link_args: fuzz_exe_ldflags + ['-Wl,-wrap,qtest_inb', '-Wl,-wrap,qtest_inw', @@ -35,4 +35,4 @@ fork_fuzz = declare_dependency( '-Wl,-wrap,qtest_memset'] ) -specific_fuzz_ss.add(fork_fuzz) +specific_fuzz_ss.add(fuzz_ld) diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c index c856d3d500..e403d373a0 100644 --- a/tests/qtest/fuzz/qos_fuzz.c +++ b/tests/qtest/fuzz/qos_fuzz.c @@ -23,7 +23,7 @@ #include "qemu/main-loop.h" #include "tests/qtest/libqtest.h" -#include "tests/qtest/libqos/malloc.h" +#include "tests/qtest/libqos/libqos-malloc.h" #include "tests/qtest/libqos/qgraph.h" #include "tests/qtest/libqos/qgraph_internal.h" #include "tests/qtest/libqos/qos_external.h" @@ -50,8 +50,7 @@ static void qos_set_machines_devices_available(void) machines_apply_to_node(mach_info); qapi_free_MachineInfoList(mach_info); - type_info = qmp_qom_list_types(true, "device", true, true, - &error_abort); + type_info = qmp_qom_list_types("device", true, true, &error_abort); types_apply_to_node(type_info); qapi_free_ObjectTypeInfoList(type_info); } diff --git a/tests/qtest/fuzz/virtio_blk_fuzz.c b/tests/qtest/fuzz/virtio_blk_fuzz.c index 236d078cc8..651fd4f043 100644 --- a/tests/qtest/fuzz/virtio_blk_fuzz.c +++ b/tests/qtest/fuzz/virtio_blk_fuzz.c @@ -19,7 +19,6 @@ #include "standard-headers/linux/virtio_pci.h" #include "standard-headers/linux/virtio_blk.h" #include "fuzz.h" -#include "fork_fuzz.h" #include "qos_fuzz.h" #define TEST_IMAGE_SIZE (64 * 1024 * 1024) @@ -128,48 +127,24 @@ static void virtio_blk_fuzz(QTestState *s, QVirtioBlkQueues* queues, } } -static void virtio_blk_fork_fuzz(QTestState *s, - const unsigned char *Data, size_t Size) -{ - QVirtioBlk *blk = fuzz_qos_obj; - static QVirtioBlkQueues *queues; - if (!queues) { - queues = qvirtio_blk_init(blk->vdev, 0); - } - if (fork() == 0) { - virtio_blk_fuzz(s, queues, Data, Size); - flush_events(s); - _Exit(0); - } else { - flush_events(s); - wait(NULL); - } -} - static void virtio_blk_with_flag_fuzz(QTestState *s, const unsigned char *Data, size_t Size) { QVirtioBlk *blk = fuzz_qos_obj; static QVirtioBlkQueues *queues; - if (fork() == 0) { - if (Size >= sizeof(uint64_t)) { - queues = qvirtio_blk_init(blk->vdev, *(uint64_t *)Data); - virtio_blk_fuzz(s, queues, - Data + sizeof(uint64_t), Size - sizeof(uint64_t)); - flush_events(s); - } - _Exit(0); - } else { + if (Size >= sizeof(uint64_t)) { + queues = qvirtio_blk_init(blk->vdev, *(uint64_t *)Data); + virtio_blk_fuzz(s, queues, + Data + sizeof(uint64_t), Size - sizeof(uint64_t)); flush_events(s); - wait(NULL); } + fuzz_reset(s); } static void virtio_blk_pre_fuzz(QTestState *s) { qos_init_path(s); - counter_shm_init(); } static void drive_destroy(void *path) @@ -181,10 +156,10 @@ static void drive_destroy(void *path) static char *drive_create(void) { int fd, ret; - char *t_path = g_strdup("/tmp/qtest.XXXXXX"); + char *t_path; /* Create a temporary raw image */ - fd = mkstemp(t_path); + fd = g_file_open_tmp("qtest.XXXXXX", &t_path, NULL); g_assert_cmpint(fd, >=, 0); ret = ftruncate(fd, TEST_IMAGE_SIZE); g_assert_cmpint(ret, ==, 0); @@ -208,22 +183,10 @@ static void *virtio_blk_test_setup(GString *cmd_line, void *arg) static void register_virtio_blk_fuzz_targets(void) { - fuzz_add_qos_target(&(FuzzTarget){ - .name = "virtio-blk-fuzz", - .description = "Fuzz the virtio-blk virtual queues, forking " - "for each fuzz run", - .pre_vm_init = &counter_shm_init, - .pre_fuzz = &virtio_blk_pre_fuzz, - .fuzz = virtio_blk_fork_fuzz,}, - "virtio-blk", - &(QOSGraphTestOptions){.before = virtio_blk_test_setup} - ); - fuzz_add_qos_target(&(FuzzTarget){ .name = "virtio-blk-flags-fuzz", - .description = "Fuzz the virtio-blk virtual queues, forking " - "for each fuzz run (also fuzzes the virtio flags)", - .pre_vm_init = &counter_shm_init, + .description = "Fuzz the virtio-blk virtual queues. " + "Also fuzzes the virtio flags)", .pre_fuzz = &virtio_blk_pre_fuzz, .fuzz = virtio_blk_with_flag_fuzz,}, "virtio-blk", diff --git a/tests/qtest/fuzz/virtio_net_fuzz.c b/tests/qtest/fuzz/virtio_net_fuzz.c index c2c15f07f0..e239875e3b 100644 --- a/tests/qtest/fuzz/virtio_net_fuzz.c +++ b/tests/qtest/fuzz/virtio_net_fuzz.c @@ -16,7 +16,6 @@ #include "tests/qtest/libqtest.h" #include "tests/qtest/libqos/virtio-net.h" #include "fuzz.h" -#include "fork_fuzz.h" #include "qos_fuzz.h" @@ -115,36 +114,18 @@ static void virtio_net_fuzz_multi(QTestState *s, } } -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 { - flush_events(s); - wait(NULL); - } -} -static void virtio_net_fork_fuzz_check_used(QTestState *s, +static void virtio_net_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 { - flush_events(s); - wait(NULL); - } + virtio_net_fuzz_multi(s, Data, Size, true); + flush_events(s); + fuzz_reset(s); } 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) @@ -158,23 +139,8 @@ static void *virtio_net_test_setup_socket(GString *cmd_line, void *arg) 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", @@ -182,20 +148,10 @@ static void register_virtio_net_fuzz_targets(void) "descriptors to be used. Timeout may indicate improperly handled " "input", .pre_fuzz = &virtio_net_pre_fuzz, - .fuzz = virtio_net_fork_fuzz_check_used,}, + .fuzz = virtio_net_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 index b3220ef6cb..b6268efd59 100644 --- a/tests/qtest/fuzz/virtio_scsi_fuzz.c +++ b/tests/qtest/fuzz/virtio_scsi_fuzz.c @@ -20,7 +20,6 @@ #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 @@ -132,48 +131,24 @@ static void virtio_scsi_fuzz(QTestState *s, QVirtioSCSIQueues* queues, } } -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 { - flush_events(s); - 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 { + 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); - wait(NULL); } + fuzz_reset(s); } 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) @@ -189,22 +164,10 @@ static void *virtio_scsi_test_setup(GString *cmd_line, void *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, + .description = "Fuzz the virtio-scsi virtual queues. " + "Also fuzzes the virtio flags", .pre_fuzz = &virtio_scsi_pre_fuzz, .fuzz = virtio_scsi_with_flag_fuzz,}, "virtio-scsi", diff --git a/tests/qtest/hd-geo-test.c b/tests/qtest/hd-geo-test.c index 413cf964c0..d08bffad91 100644 --- a/tests/qtest/hd-geo-test.c +++ b/tests/qtest/hd-geo-test.c @@ -27,16 +27,16 @@ static char *create_test_img(int secs) { - char *template = strdup("/tmp/qtest.XXXXXX"); + char *template; int fd, ret; - fd = mkstemp(template); + fd = g_file_open_tmp("qtest.XXXXXX", &template, NULL); g_assert(fd >= 0); ret = ftruncate(fd, (off_t)secs * 512); close(fd); if (ret) { - free(template); + g_free(template); template = NULL; } @@ -422,9 +422,8 @@ static MBRpartitions empty_mbr = { {false, 0, 0, 0, 0, 0, 0, 0, 0}, static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors) { - const char *template = "/tmp/qtest.XXXXXX"; - char *raw_path = strdup(template); - char *qcow2_path = strdup(template); + g_autofree char *raw_path = NULL; + char *qcow2_path; char cmd[100 + 2 * PATH_MAX]; uint8_t buf[512] = {}; int i, ret, fd, offset; @@ -468,7 +467,7 @@ static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors) offset += 0x10; } - fd = mkstemp(raw_path); + fd = g_file_open_tmp("qtest.XXXXXX", &raw_path, NULL); g_assert(fd >= 0); close(fd); @@ -478,7 +477,7 @@ static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors) g_assert(ret == sizeof(buf)); close(fd); - fd = mkstemp(qcow2_path); + fd = g_file_open_tmp("qtest.XXXXXX", &qcow2_path, NULL); g_assert(fd >= 0); close(fd); @@ -506,7 +505,6 @@ static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors) free(qemu_img_abs_path); unlink(raw_path); - free(raw_path); return qcow2_path; } @@ -693,7 +691,8 @@ static void add_virtio_disk(TestArgs *args, args->n_virtio_disks++; } -static void test_override(TestArgs *args, CHSResult expected[]) +static void test_override(TestArgs *args, const char *arch, + CHSResult expected[]) { QTestState *qts; char *joined_args; @@ -702,7 +701,7 @@ static void test_override(TestArgs *args, CHSResult expected[]) joined_args = g_strjoinv(" ", args->argv); - qts = qtest_initf("-machine pc %s", joined_args); + qts = qtest_initf("-machine %s %s", arch, joined_args); fw_cfg = pc_fw_cfg_init(qts); read_bootdevices(fw_cfg, expected); @@ -714,7 +713,7 @@ static void test_override(TestArgs *args, CHSResult expected[]) for (i = 0; i < args->n_drives; i++) { unlink(args->drives[i]); - free(args->drives[i]); + g_free(args->drives[i]); } g_free(args->drives); g_strfreev(args->argv); @@ -739,7 +738,28 @@ static void test_override_ide(void) add_ide_disk(args, 1, 0, 1, 9000, 120, 30); add_ide_disk(args, 2, 1, 0, 0, 1, 1); add_ide_disk(args, 3, 1, 1, 1, 0, 0); - test_override(args, expected); + test_override(args, "pc", expected); +} + +static void test_override_sata(void) +{ + TestArgs *args = create_args(); + CHSResult expected[] = { + {"/pci@i0cf8/pci8086,2922@1f,2/drive@0/disk@0", {10000, 120, 30} }, + {"/pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0", {9000, 120, 30} }, + {"/pci@i0cf8/pci8086,2922@1f,2/drive@2/disk@0", {0, 1, 1} }, + {"/pci@i0cf8/pci8086,2922@1f,2/drive@3/disk@0", {1, 0, 0} }, + {NULL, {0, 0, 0} } + }; + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_ide_disk(args, 0, 0, 0, 10000, 120, 30); + add_ide_disk(args, 1, 1, 0, 9000, 120, 30); + add_ide_disk(args, 2, 2, 0, 0, 1, 1); + add_ide_disk(args, 3, 3, 0, 1, 0, 0); + test_override(args, "q35", expected); } static void test_override_scsi(void) @@ -761,7 +781,41 @@ static void test_override_scsi(void) add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30); add_scsi_disk(args, 2, 0, 0, 2, 0, 1, 0, 0); add_scsi_disk(args, 3, 0, 0, 3, 0, 0, 1, 0); - test_override(args, expected); + test_override(args, "pc", expected); +} + +static void setup_pci_bridge(TestArgs *args, const char *id) +{ + + char *br; + br = g_strdup_printf("-device pcie-pci-bridge,bus=pcie.0,id=%s", id); + + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, br); +} + +static void test_override_scsi_q35(void) +{ + TestArgs *args = create_args(); + CHSResult expected[] = { + { "/pci@i0cf8/pci-bridge@1/scsi@3/channel@0/disk@0,0", + {10000, 120, 30} + }, + {"/pci@i0cf8/pci-bridge@1/scsi@3/channel@0/disk@1,0", {9000, 120, 30} }, + {"/pci@i0cf8/pci-bridge@1/scsi@3/channel@0/disk@2,0", {1, 0, 0} }, + {"/pci@i0cf8/pci-bridge@1/scsi@3/channel@0/disk@3,0", {0, 1, 0} }, + {NULL, {0, 0, 0} } + }; + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + setup_pci_bridge(args, "pcie-pci-br"); + add_scsi_controller(args, "lsi53c895a", "pcie-pci-br", 3); + add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30); + add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30); + add_scsi_disk(args, 2, 0, 0, 2, 0, 1, 0, 0); + add_scsi_disk(args, 3, 0, 0, 3, 0, 0, 1, 0); + test_override(args, "q35", expected); } static void test_override_scsi_2_controllers(void) @@ -784,7 +838,7 @@ static void test_override_scsi_2_controllers(void) add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30); add_scsi_disk(args, 2, 1, 0, 0, 1, 1, 0, 0); add_scsi_disk(args, 3, 1, 0, 1, 2, 0, 1, 0); - test_override(args, expected); + test_override(args, "pc", expected); } static void test_override_virtio_blk(void) @@ -799,7 +853,23 @@ static void test_override_virtio_blk(void) add_drive_with_mbr(args, empty_mbr, 1); add_virtio_disk(args, 0, "pci.0", 3, 10000, 120, 30); add_virtio_disk(args, 1, "pci.0", 4, 9000, 120, 30); - test_override(args, expected); + test_override(args, "pc", expected); +} + +static void test_override_virtio_blk_q35(void) +{ + TestArgs *args = create_args(); + CHSResult expected[] = { + {"/pci@i0cf8/pci-bridge@1/scsi@3/disk@0,0", {10000, 120, 30} }, + {"/pci@i0cf8/pci-bridge@1/scsi@4/disk@0,0", {9000, 120, 30} }, + {NULL, {0, 0, 0} } + }; + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + setup_pci_bridge(args, "pcie-pci-br"); + add_virtio_disk(args, 0, "pcie-pci-br", 3, 10000, 120, 30); + add_virtio_disk(args, 1, "pcie-pci-br", 4, 9000, 120, 30); + test_override(args, "q35", expected); } static void test_override_zero_chs(void) @@ -810,16 +880,65 @@ static void test_override_zero_chs(void) }; add_drive_with_mbr(args, empty_mbr, 1); add_ide_disk(args, 0, 1, 1, 0, 0, 0); - test_override(args, expected); + test_override(args, "pc", expected); } -static void test_override_scsi_hot_unplug(void) +static void test_override_zero_chs_q35(void) +{ + TestArgs *args = create_args(); + CHSResult expected[] = { + {NULL, {0, 0, 0} } + }; + add_drive_with_mbr(args, empty_mbr, 1); + add_ide_disk(args, 0, 0, 0, 0, 0, 0); + test_override(args, "q35", expected); +} + +static void test_override_hot_unplug(TestArgs *args, const char *devid, + CHSResult expected[], CHSResult expected2[]) { QTestState *qts; char *joined_args; QFWCFG *fw_cfg; QDict *response; int i; + + joined_args = g_strjoinv(" ", args->argv); + + qts = qtest_initf("%s", joined_args); + fw_cfg = pc_fw_cfg_init(qts); + + read_bootdevices(fw_cfg, expected); + + /* unplug device an restart */ + qtest_qmp_device_del_send(qts, devid); + + response = qtest_qmp(qts, + "{ 'execute': 'system_reset', 'arguments': { }}"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + qtest_qmp_eventwait(qts, "RESET"); + + read_bootdevices(fw_cfg, expected2); + + g_free(joined_args); + qtest_quit(qts); + + g_free(fw_cfg); + + for (i = 0; i < args->n_drives; i++) { + unlink(args->drives[i]); + g_free(args->drives[i]); + } + g_free(args->drives); + g_strfreev(args->argv); + g_free(args); +} + +static void test_override_scsi_hot_unplug(void) +{ TestArgs *args = create_args(); CHSResult expected[] = { {"/pci@i0cf8/scsi@2/channel@0/disk@0,0", {10000, 120, 30} }, @@ -836,51 +955,50 @@ static void test_override_scsi_hot_unplug(void) add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30); add_scsi_disk(args, 1, 0, 0, 1, 0, 20, 20, 20); - joined_args = g_strjoinv(" ", args->argv); + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, + g_strdup("-machine pc")); - qts = qtest_initf("-machine pc %s", joined_args); - fw_cfg = pc_fw_cfg_init(qts); + test_override_hot_unplug(args, "scsi-disk0", expected, expected2); +} - read_bootdevices(fw_cfg, expected); +static void test_override_scsi_hot_unplug_q35(void) +{ + TestArgs *args = create_args(); + CHSResult expected[] = { + { + "/pci@i0cf8/pci-bridge@1/pci-bridge@0/scsi@2/channel@0/disk@0,0", + {10000, 120, 30} + }, + { + "/pci@i0cf8/pci-bridge@1/pci-bridge@0/scsi@2/channel@0/disk@1,0", + {20, 20, 20} + }, + {NULL, {0, 0, 0} } + }; + CHSResult expected2[] = { + { + "/pci@i0cf8/pci-bridge@1/pci-bridge@0/scsi@2/channel@0/disk@1,0", + {20, 20, 20} + }, + {NULL, {0, 0, 0} } + }; - /* unplug device an restart */ - response = qtest_qmp(qts, - "{ 'execute': 'device_del'," - " 'arguments': {'id': 'scsi-disk0' }}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - response = qtest_qmp(qts, - "{ 'execute': 'system_reset', 'arguments': { }}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, + g_strdup("-device pcie-root-port,id=p0 " + "-device pcie-pci-bridge,bus=p0,id=b1 " + "-machine q35")); - qtest_qmp_eventwait(qts, "RESET"); + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_scsi_controller(args, "virtio-scsi-pci", "b1", 2); + add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30); + add_scsi_disk(args, 1, 0, 0, 1, 0, 20, 20, 20); - read_bootdevices(fw_cfg, expected2); - - g_free(joined_args); - qtest_quit(qts); - - g_free(fw_cfg); - - for (i = 0; i < args->n_drives; i++) { - unlink(args->drives[i]); - free(args->drives[i]); - } - g_free(args->drives); - g_strfreev(args->argv); - g_free(args); + test_override_hot_unplug(args, "scsi-disk0", expected, expected2); } static void test_override_virtio_hot_unplug(void) { - QTestState *qts; - char *joined_args; - QFWCFG *fw_cfg; - QDict *response; - int i; TestArgs *args = create_args(); CHSResult expected[] = { {"/pci@i0cf8/scsi@2/disk@0,0", {10000, 120, 30} }, @@ -896,42 +1014,45 @@ static void test_override_virtio_hot_unplug(void) add_virtio_disk(args, 0, "pci.0", 2, 10000, 120, 30); add_virtio_disk(args, 1, "pci.0", 3, 20, 20, 20); - joined_args = g_strjoinv(" ", args->argv); + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, + g_strdup("-machine pc")); - qts = qtest_initf("-machine pc %s", joined_args); - fw_cfg = pc_fw_cfg_init(qts); + test_override_hot_unplug(args, "virtio-disk0", expected, expected2); +} - read_bootdevices(fw_cfg, expected); +static void test_override_virtio_hot_unplug_q35(void) +{ + TestArgs *args = create_args(); + CHSResult expected[] = { + { + "/pci@i0cf8/pci-bridge@1/pci-bridge@0/scsi@2/disk@0,0", + {10000, 120, 30} + }, + { + "/pci@i0cf8/pci-bridge@1/pci-bridge@0/scsi@3/disk@0,0", + {20, 20, 20} + }, + {NULL, {0, 0, 0} } + }; + CHSResult expected2[] = { + { + "/pci@i0cf8/pci-bridge@1/pci-bridge@0/scsi@3/disk@0,0", + {20, 20, 20} + }, + {NULL, {0, 0, 0} } + }; - /* unplug device an restart */ - response = qtest_qmp(qts, - "{ 'execute': 'device_del'," - " 'arguments': {'id': 'virtio-disk0' }}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - response = qtest_qmp(qts, - "{ 'execute': 'system_reset', 'arguments': { }}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, + g_strdup("-device pcie-root-port,id=p0 " + "-device pcie-pci-bridge,bus=p0,id=b1 " + "-machine q35")); - qtest_qmp_eventwait(qts, "RESET"); + add_drive_with_mbr(args, empty_mbr, 1); + add_drive_with_mbr(args, empty_mbr, 1); + add_virtio_disk(args, 0, "b1", 2, 10000, 120, 30); + add_virtio_disk(args, 1, "b1", 3, 20, 20, 20); - read_bootdevices(fw_cfg, expected2); - - g_free(joined_args); - qtest_quit(qts); - - g_free(fw_cfg); - - for (i = 0; i < args->n_drives; i++) { - unlink(args->drives[i]); - free(args->drives[i]); - } - g_free(args->drives); - g_strfreev(args->argv); - g_free(args); + test_override_hot_unplug(args, "virtio-disk0", expected, expected2); } int main(int argc, char **argv) @@ -967,15 +1088,43 @@ int main(int argc, char **argv) qtest_add_func("hd-geo/override/ide", test_override_ide); if (qtest_has_device("lsi53c895a")) { qtest_add_func("hd-geo/override/scsi", test_override_scsi); - qtest_add_func("hd-geo/override/scsi_2_controllers", - test_override_scsi_2_controllers); + if (qtest_has_device("virtio-scsi-pci")) { + qtest_add_func("hd-geo/override/scsi_2_controllers", + test_override_scsi_2_controllers); + } } - qtest_add_func("hd-geo/override/virtio_blk", test_override_virtio_blk); qtest_add_func("hd-geo/override/zero_chs", test_override_zero_chs); - qtest_add_func("hd-geo/override/scsi_hot_unplug", - test_override_scsi_hot_unplug); - qtest_add_func("hd-geo/override/virtio_hot_unplug", - test_override_virtio_hot_unplug); + if (qtest_has_device("virtio-scsi-pci")) { + qtest_add_func("hd-geo/override/scsi_hot_unplug", + test_override_scsi_hot_unplug); + } + if (qtest_has_device("virtio-blk-pci")) { + qtest_add_func("hd-geo/override/virtio_hot_unplug", + test_override_virtio_hot_unplug); + qtest_add_func("hd-geo/override/virtio_blk", + test_override_virtio_blk); + } + + if (qtest_has_machine("q35")) { + qtest_add_func("hd-geo/override/sata", test_override_sata); + qtest_add_func("hd-geo/override/zero_chs_q35", + test_override_zero_chs_q35); + if (qtest_has_device("lsi53c895a")) { + qtest_add_func("hd-geo/override/scsi_q35", + test_override_scsi_q35); + } + if (qtest_has_device("virtio-scsi-pci")) { + qtest_add_func("hd-geo/override/scsi_hot_unplug_q35", + test_override_scsi_hot_unplug_q35); + } + if (qtest_has_device("virtio-blk-pci")) { + qtest_add_func("hd-geo/override/virtio_hot_unplug_q35", + test_override_virtio_hot_unplug_q35); + qtest_add_func("hd-geo/override/virtio_blk_q35", + test_override_virtio_blk_q35); + } + + } } else { g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; " "skipping hd-geo/override/* tests"); @@ -987,7 +1136,7 @@ test_add_done: for (i = 0; i < backend_last; i++) { if (img_file_name[i]) { unlink(img_file_name[i]); - free(img_file_name[i]); + g_free(img_file_name[i]); } } diff --git a/tests/qtest/hexloader-test.c b/tests/qtest/hexloader-test.c index 8b7aa2d72d..3023548041 100644 --- a/tests/qtest/hexloader-test.c +++ b/tests/qtest/hexloader-test.c @@ -34,12 +34,8 @@ static void hex_loader_test(void) int main(int argc, char **argv) { - int ret; - g_test_init(&argc, &argv, NULL); qtest_add_func("/tmp/hex_loader", hex_loader_test); - ret = g_test_run(); - - return ret; + return g_test_run(); } diff --git a/tests/qtest/i440fx-test.c b/tests/qtest/i440fx-test.c index 6d7d4d8d8f..795fd85343 100644 --- a/tests/qtest/i440fx-test.c +++ b/tests/qtest/i440fx-test.c @@ -281,51 +281,31 @@ static void test_i440fx_pam(gconstpointer opaque) #define BLOB_SIZE ((size_t)65536) #define ISA_BIOS_MAXSZ ((size_t)(128 * 1024)) -/* Create a blob file, and return its absolute pathname as a dynamically +/* + * Create a blob file, and return its absolute pathname as a dynamically * allocated string. * The file is closed before the function returns. - * In case of error, NULL is returned. The function prints the error message. + * In case of error, the function aborts and prints the error message. */ static char *create_blob_file(void) { - int ret, fd; + int i, fd; char *pathname; GError *error = NULL; + g_autofree uint8_t *buf = g_malloc(BLOB_SIZE); - ret = -1; fd = g_file_open_tmp("blob_XXXXXX", &pathname, &error); - if (fd == -1) { - fprintf(stderr, "unable to create blob file: %s\n", error->message); - g_error_free(error); - } else { - if (ftruncate(fd, BLOB_SIZE) == -1) { - fprintf(stderr, "ftruncate(\"%s\", %zu): %s\n", pathname, - BLOB_SIZE, strerror(errno)); - } else { - void *buf; + g_assert_no_error(error); + close(fd); - buf = mmap(NULL, BLOB_SIZE, PROT_WRITE, MAP_SHARED, fd, 0); - if (buf == MAP_FAILED) { - fprintf(stderr, "mmap(\"%s\", %zu): %s\n", pathname, BLOB_SIZE, - strerror(errno)); - } else { - size_t i; - - for (i = 0; i < BLOB_SIZE; ++i) { - ((uint8_t *)buf)[i] = i; - } - munmap(buf, BLOB_SIZE); - ret = 0; - } - } - close(fd); - if (ret == -1) { - unlink(pathname); - g_free(pathname); - } + for (i = 0; i < BLOB_SIZE; i++) { + buf[i] = i; } - return ret == -1 ? NULL : pathname; + g_file_set_contents(pathname, (char *)buf, BLOB_SIZE, &error); + g_assert_no_error(error); + + return pathname; } static void test_i440fx_firmware(FirmwareTestFixture *fixture, diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index 5bcb75a7e5..d6b4f6e36a 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -34,9 +34,6 @@ #include "hw/pci/pci_ids.h" #include "hw/pci/pci_regs.h" -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(q, ...) qobject_unref(qtest_qmp(q, __VA_ARGS__)) - #define TEST_IMAGE_SIZE 64 * 1024 * 1024 #define IDE_PCI_DEV 1 @@ -90,6 +87,7 @@ enum { enum { CMD_DSM = 0x06, + CMD_DIAGNOSE = 0x90, CMD_READ_DMA = 0xc8, CMD_WRITE_DMA = 0xca, CMD_FLUSH_CACHE = 0xe7, @@ -121,9 +119,10 @@ enum { static QPCIBus *pcibus = NULL; static QGuestAllocator guest_malloc; -static char tmp_path[] = "/tmp/qtest.XXXXXX"; -static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX"; +static char *tmp_path[2]; +static char *debug_path; +G_GNUC_PRINTF(1, 2) static QTestState *ide_test_start(const char *cmdline_fmt, ...) { QTestState *qts; @@ -310,7 +309,7 @@ static QTestState *test_bmdma_setup(void) qts = ide_test_start( "-drive file=%s,if=ide,cache=writeback,format=raw " "-global ide-hd.serial=%s -global ide-hd.ver=%s", - tmp_path, "testdisk", "version"); + tmp_path[0], "testdisk", "version"); qtest_irq_intercept_in(qts, "ioapic"); return qts; @@ -574,7 +573,7 @@ static void test_identify(void) qts = ide_test_start( "-drive file=%s,if=ide,cache=writeback,format=raw " "-global ide-hd.serial=%s -global ide-hd.ver=%s", - tmp_path, "testdisk", "version"); + tmp_path[0], "testdisk", "version"); dev = get_pci_device(qts, &bmdma_bar, &ide_bar); @@ -614,6 +613,36 @@ static void test_identify(void) free_pci_device(dev); } +static void test_diagnostic(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint8_t data; + + qts = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-blockdev driver=file,node-name=hdb,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 " + "-device ide-hd,drive=hdb,bus=ide.0,unit=1 ", + tmp_path[0], tmp_path[1]); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* DIAGNOSE command on device 1 */ + qpci_io_writeb(dev, ide_bar, reg_device, DEV); + data = qpci_io_readb(dev, ide_bar, reg_device); + g_assert_cmphex(data & DEV, ==, DEV); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_DIAGNOSE); + + /* Verify that DEVICE is now 0 */ + data = qpci_io_readb(dev, ide_bar, reg_device); + g_assert_cmphex(data & DEV, ==, 0); + + ide_test_quit(qts); + free_pci_device(dev); +} + /* * Write sector 1 with random data to make IDE storage dirty * Needed for flush tests so that flushes actually go though the block layer @@ -662,7 +691,7 @@ static void test_flush(void) qts = ide_test_start( "-drive file=blkdebug::%s,if=ide,cache=writeback,format=raw", - tmp_path); + tmp_path[0]); dev = get_pci_device(qts, &bmdma_bar, &ide_bar); @@ -713,7 +742,7 @@ static void test_pci_retry_flush(void) qts = ide_test_start( "-drive file=blkdebug:%s:%s,if=ide,cache=writeback,format=raw," "rerror=stop,werror=stop", - debug_path, tmp_path); + debug_path, tmp_path[0]); dev = get_pci_device(qts, &bmdma_bar, &ide_bar); @@ -734,7 +763,7 @@ static void test_pci_retry_flush(void) qtest_qmp_eventwait(qts, "STOP"); /* Complete the command */ - qmp_discard_response(qts, "{'execute':'cont' }"); + qtest_qmp_assert_success(qts, "{'execute':'cont' }"); /* Check registers */ data = qpci_io_readb(dev, ide_bar, reg_device); @@ -757,7 +786,7 @@ static void test_flush_nodev(void) QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; - qts = ide_test_start(""); + qts = ide_test_start("%s", ""); dev = get_pci_device(qts, &bmdma_bar, &ide_bar); @@ -892,14 +921,14 @@ static void cdrom_pio_impl(int nblocks) /* Prepopulate the CDROM with an interesting pattern */ generate_pattern(pattern, patt_len, ATAPI_BLOCK_SIZE); - fh = fopen(tmp_path, "w+"); + fh = fopen(tmp_path[0], "wb+"); ret = fwrite(pattern, ATAPI_BLOCK_SIZE, patt_blocks, fh); g_assert_cmpint(ret, ==, patt_blocks); fclose(fh); qts = ide_test_start( "-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " - "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); + "-device ide-cd,drive=sr0,bus=ide.0", tmp_path[0]); dev = get_pci_device(qts, &bmdma_bar, &ide_bar); qtest_irq_intercept_in(qts, "ioapic"); @@ -985,7 +1014,7 @@ static void test_cdrom_dma(void) qts = ide_test_start( "-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " - "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); + "-device ide-cd,drive=sr0,bus=ide.0", tmp_path[0]); qtest_irq_intercept_in(qts, "ioapic"); guest_buf = guest_alloc(&guest_malloc, len); @@ -993,7 +1022,7 @@ static void test_cdrom_dma(void) prdt[0].size = cpu_to_le32(len | PRDT_EOT); generate_pattern(pattern, ATAPI_BLOCK_SIZE * 16, ATAPI_BLOCK_SIZE); - fh = fopen(tmp_path, "w+"); + fh = fopen(tmp_path[0], "wb+"); ret = fwrite(pattern, ATAPI_BLOCK_SIZE, 16, fh); g_assert_cmpint(ret, ==, 16); fclose(fh); @@ -1011,26 +1040,47 @@ static void test_cdrom_dma(void) int main(int argc, char **argv) { + const char *base; + int i; int fd; int ret; + /* + * "base" stores the starting point where we create temporary files. + * + * On Windows, this is set to the relative path of current working + * directory, because the absolute path causes the blkdebug filename + * parser fail to parse "blkdebug:path/to/config:path/to/image". + */ +#ifndef _WIN32 + base = g_get_tmp_dir(); +#else + base = "."; +#endif + /* Create temporary blkdebug instructions */ - fd = mkstemp(debug_path); + debug_path = g_strdup_printf("%s/qtest-blkdebug.XXXXXX", base); + fd = g_mkstemp(debug_path); g_assert(fd >= 0); close(fd); /* Create a temporary raw image */ - fd = mkstemp(tmp_path); - g_assert(fd >= 0); - ret = ftruncate(fd, TEST_IMAGE_SIZE); - g_assert(ret == 0); - close(fd); + for (i = 0; i < 2; ++i) { + tmp_path[i] = g_strdup_printf("%s/qtest.XXXXXX", base); + fd = g_mkstemp(tmp_path[i]); + g_assert(fd >= 0); + ret = ftruncate(fd, TEST_IMAGE_SIZE); + g_assert(ret == 0); + close(fd); + } /* Run the tests */ g_test_init(&argc, &argv, NULL); qtest_add_func("/ide/identify", test_identify); + qtest_add_func("/ide/diagnostic", test_diagnostic); + 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/various_prdts", test_bmdma_various_prdts); @@ -1048,8 +1098,12 @@ int main(int argc, char **argv) ret = g_test_run(); /* Cleanup */ - unlink(tmp_path); + for (i = 0; i < 2; ++i) { + unlink(tmp_path[i]); + g_free(tmp_path[i]); + } unlink(debug_path); + g_free(debug_path); return ret; } diff --git a/tests/qtest/igb-test.c b/tests/qtest/igb-test.c new file mode 100644 index 0000000000..3d397ea697 --- /dev/null +++ b/tests/qtest/igb-test.c @@ -0,0 +1,256 @@ +/* + * QTest testcase for igb NIC + * + * Copyright (c) 2022-2023 Red Hat, Inc. + * Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Akihiko Odaki + * Dmitry Fleytman + * Leonid Bloch + * Yan Vugenfirer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "libqtest-single.h" +#include "libqos/pci-pc.h" +#include "net/eth.h" +#include "qemu/sockets.h" +#include "qemu/iov.h" +#include "qemu/module.h" +#include "qemu/bitops.h" +#include "libqos/libqos-malloc.h" +#include "libqos/e1000e.h" +#include "hw/net/igb_regs.h" + +#ifndef _WIN32 + +static const struct eth_header packet = { + .h_dest = E1000E_ADDRESS, + .h_source = E1000E_ADDRESS, +}; + +static void igb_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) +{ + union e1000_adv_tx_desc descr; + char buffer[64]; + int ret; + uint32_t recv_len; + + /* Prepare test data buffer */ + uint64_t data = guest_alloc(alloc, sizeof(buffer)); + memwrite(data, &packet, sizeof(packet)); + + /* Prepare TX descriptor */ + memset(&descr, 0, sizeof(descr)); + descr.read.buffer_addr = cpu_to_le64(data); + descr.read.cmd_type_len = cpu_to_le32(E1000_TXD_CMD_RS | + E1000_TXD_CMD_EOP | + E1000_TXD_DTYP_D | + sizeof(buffer)); + + /* Put descriptor to the ring */ + e1000e_tx_ring_push(d, &descr); + + /* Wait for TX WB interrupt */ + e1000e_wait_isr(d, E1000E_TX0_MSG_ID); + + /* Check DD bit */ + g_assert_cmphex(le32_to_cpu(descr.wb.status) & E1000_TXD_STAT_DD, ==, + E1000_TXD_STAT_DD); + + /* Check data sent to the backend */ + ret = recv(test_sockets[0], &recv_len, sizeof(recv_len), 0); + g_assert_cmpint(ret, == , sizeof(recv_len)); + ret = recv(test_sockets[0], buffer, sizeof(buffer), 0); + g_assert_cmpint(ret, ==, sizeof(buffer)); + g_assert_false(memcmp(buffer, &packet, sizeof(packet))); + + /* Free test data buffer */ + guest_free(alloc, data); +} + +static void igb_receive_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) +{ + union e1000_adv_rx_desc descr; + + struct eth_header test_iov = packet; + int len = htonl(sizeof(packet)); + struct iovec iov[] = { + { + .iov_base = &len, + .iov_len = sizeof(len), + },{ + .iov_base = &test_iov, + .iov_len = sizeof(packet), + }, + }; + + char buffer[64]; + int ret; + + /* Send a dummy packet to device's socket*/ + ret = iov_send(test_sockets[0], iov, 2, 0, sizeof(len) + sizeof(packet)); + g_assert_cmpint(ret, == , sizeof(packet) + sizeof(len)); + + /* Prepare test data buffer */ + uint64_t data = guest_alloc(alloc, sizeof(buffer)); + + /* Prepare RX descriptor */ + memset(&descr, 0, sizeof(descr)); + descr.read.pkt_addr = cpu_to_le64(data); + + /* Put descriptor to the ring */ + e1000e_rx_ring_push(d, &descr); + + /* Wait for TX WB interrupt */ + e1000e_wait_isr(d, E1000E_RX0_MSG_ID); + + /* Check DD bit */ + g_assert_cmphex(le32_to_cpu(descr.wb.upper.status_error) & + E1000_RXD_STAT_DD, ==, E1000_RXD_STAT_DD); + + /* Check data sent to the backend */ + memread(data, buffer, sizeof(buffer)); + g_assert_false(memcmp(buffer, &packet, sizeof(packet))); + + /* Free test data buffer */ + guest_free(alloc, data); +} + +static void test_e1000e_init(void *obj, void *data, QGuestAllocator * alloc) +{ + /* init does nothing */ +} + +static void test_igb_tx(void *obj, void *data, QGuestAllocator * alloc) +{ + QE1000E_PCI *e1000e = obj; + QE1000E *d = &e1000e->e1000e; + QOSGraphObject *e_object = obj; + QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); + + /* FIXME: add spapr support */ + if (qpci_check_buggy_msi(dev)) { + return; + } + + igb_send_verify(d, data, alloc); +} + +static void test_igb_rx(void *obj, void *data, QGuestAllocator * alloc) +{ + QE1000E_PCI *e1000e = obj; + QE1000E *d = &e1000e->e1000e; + QOSGraphObject *e_object = obj; + QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); + + /* FIXME: add spapr support */ + if (qpci_check_buggy_msi(dev)) { + return; + } + + igb_receive_verify(d, data, alloc); +} + +static void test_igb_multiple_transfers(void *obj, void *data, + QGuestAllocator *alloc) +{ + static const long iterations = 4 * 1024; + long i; + + QE1000E_PCI *e1000e = obj; + QE1000E *d = &e1000e->e1000e; + QOSGraphObject *e_object = obj; + QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); + + /* FIXME: add spapr support */ + if (qpci_check_buggy_msi(dev)) { + return; + } + + for (i = 0; i < iterations; i++) { + igb_send_verify(d, data, alloc); + igb_receive_verify(d, data, alloc); + } + +} + +static void data_test_clear(void *sockets) +{ + int *test_sockets = sockets; + + close(test_sockets[0]); + qos_invalidate_command_line(); + close(test_sockets[1]); + g_free(test_sockets); +} + +static void *data_test_init(GString *cmd_line, void *arg) +{ + int *test_sockets = g_new(int, 2); + int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, test_sockets); + g_assert_cmpint(ret, != , -1); + + g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", + test_sockets[1]); + + g_test_queue_destroy(data_test_clear, test_sockets); + return test_sockets; +} + +#endif + +static void *data_test_init_no_socket(GString *cmd_line, void *arg) +{ + g_string_append(cmd_line, " -netdev hubport,hubid=0,id=hs0 "); + return arg; +} + +static void test_igb_hotplug(void *obj, void *data, QGuestAllocator * alloc) +{ + QTestState *qts = global_qtest; /* TODO: get rid of global_qtest here */ + QE1000E_PCI *dev = obj; + + if (dev->pci_dev.bus->not_hotpluggable) { + g_test_skip("pci bus does not support hotplug"); + return; + } + + qtest_qmp_device_add(qts, "igb", "igb_net", "{'addr': '0x06'}"); + qpci_unplug_acpi_device_test(qts, "igb_net", 0x06); +} + +static void register_igb_test(void) +{ + QOSGraphTestOptions opts = { 0 }; + +#ifndef _WIN32 + opts.before = data_test_init, + qos_add_test("init", "igb", test_e1000e_init, &opts); + qos_add_test("tx", "igb", test_igb_tx, &opts); + qos_add_test("rx", "igb", test_igb_rx, &opts); + qos_add_test("multiple_transfers", "igb", + test_igb_multiple_transfers, &opts); +#endif + + opts.before = data_test_init_no_socket; + qos_add_test("hotplug", "igb", test_igb_hotplug, &opts); +} + +libqos_init(register_igb_test); diff --git a/tests/qtest/intel-hda-test.c b/tests/qtest/intel-hda-test.c index a58c98e4d1..663bb6c485 100644 --- a/tests/qtest/intel-hda-test.c +++ b/tests/qtest/intel-hda-test.c @@ -11,20 +11,24 @@ #include "libqtest-single.h" #define HDA_ID "hda0" -#define CODEC_DEVICES " -device hda-output,bus=" HDA_ID ".0" \ - " -device hda-micro,bus=" HDA_ID ".0" \ - " -device hda-duplex,bus=" HDA_ID ".0" +#define AUDIODEV " -audiodev driver=none,id=audio0 " +#define AUDIODEV_REF "audiodev=audio0" +#define CODEC_DEVICES " -device hda-output,bus=" HDA_ID ".0," AUDIODEV_REF \ + " -device hda-micro,bus=" HDA_ID ".0," AUDIODEV_REF \ + " -device hda-duplex,bus=" HDA_ID ".0," AUDIODEV_REF /* Tests only initialization so far. TODO: Replace with functional tests */ static void ich6_test(void) { - qtest_start("-device intel-hda,id=" HDA_ID CODEC_DEVICES); + qtest_start(AUDIODEV "-machine pc -device intel-hda,id=" HDA_ID CODEC_DEVICES); qtest_end(); } static void ich9_test(void) { - qtest_start("-machine q35 -device ich9-intel-hda,bus=pcie.0,addr=1b.0,id=" + qtest_start("-machine q35" + AUDIODEV + "-device ich9-intel-hda,bus=pcie.0,addr=1b.0,id=" HDA_ID CODEC_DEVICES); qtest_end(); } @@ -39,6 +43,7 @@ static void test_issue542_ich6(void) QTestState *s; s = qtest_init("-nographic -nodefaults -M pc-q35-6.2 " + AUDIODEV "-device intel-hda,id=" HDA_ID CODEC_DEVICES); qtest_outl(s, 0xcf8, 0x80000804); @@ -65,9 +70,12 @@ static void test_issue542_ich6(void) int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - qtest_add_func("/intel-hda/ich6", ich6_test); - qtest_add_func("/intel-hda/ich9", ich9_test); - qtest_add_func("/intel-hda/fuzz/issue542", test_issue542_ich6); - + if (qtest_has_machine("pc")) { + qtest_add_func("/intel-hda/ich6", ich6_test); + } + if (qtest_has_machine("q35")) { + qtest_add_func("/intel-hda/ich9", ich9_test); + qtest_add_func("/intel-hda/fuzz/issue542", test_issue542_ich6); + } return g_test_run(); } diff --git a/tests/qtest/ipmi-bt-test.c b/tests/qtest/ipmi-bt-test.c index ed431e34e6..383239bcd4 100644 --- a/tests/qtest/ipmi-bt-test.c +++ b/tests/qtest/ipmi-bt-test.c @@ -411,7 +411,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); global_qtest = qtest_initf( - " -chardev socket,id=ipmi0,host=localhost,port=%d,reconnect=10" + " -chardev socket,id=ipmi0,host=127.0.0.1,port=%d,reconnect=10" " -device ipmi-bmc-extern,chardev=ipmi0,id=bmc0" " -device isa-ipmi-bt,bmc=bmc0", emu_port); qtest_irq_intercept_in(global_qtest, "ioapic"); diff --git a/tests/qtest/ivshmem-test.c b/tests/qtest/ivshmem-test.c index e23a97fa8e..9bf8e78df6 100644 --- a/tests/qtest/ivshmem-test.c +++ b/tests/qtest/ivshmem-test.c @@ -109,9 +109,9 @@ static void setup_vm_cmd(IVState *s, const char *cmd, bool msix) const char *arch = qtest_get_arch(); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - s->qs = qtest_pc_boot(cmd); + s->qs = qtest_pc_boot("%s", cmd); } else if (strcmp(arch, "ppc64") == 0) { - s->qs = qtest_spapr_boot(cmd); + s->qs = qtest_spapr_boot("%s", cmd); } else { g_printerr("ivshmem-test tests are only available on x86 or ppc64\n"); exit(EXIT_FAILURE); @@ -378,6 +378,20 @@ static void test_ivshmem_server(void) close(thread.pipe[0]); } +static void test_ivshmem_hotplug_q35(void) +{ + QTestState *qts = qtest_init("-object memory-backend-ram,size=1M,id=mb1 " + "-device pcie-root-port,id=p1 " + "-device pcie-pci-bridge,bus=p1,id=b1 " + "-machine q35"); + + qtest_qmp_device_add(qts, "ivshmem-plain", "iv1", + "{'memdev': 'mb1', 'bus': 'b1'}"); + qtest_qmp_device_del_send(qts, "iv1"); + + qtest_quit(qts); +} + #define PCI_SLOT_HP 0x06 static void test_ivshmem_hotplug(void) @@ -469,6 +483,7 @@ int main(int argc, char **argv) { int ret, fd; gchar dir[] = "/tmp/ivshmem-test.XXXXXX"; + const char *arch = qtest_get_arch(); g_test_init(&argc, &argv, NULL); @@ -481,8 +496,8 @@ int main(int argc, char **argv) tmpshmem = mmap(0, TMPSHMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); g_assert(tmpshmem != MAP_FAILED); /* server */ - if (mkdtemp(dir) == NULL) { - g_error("mkdtemp: %s", g_strerror(errno)); + if (g_mkdtemp(dir) == NULL) { + g_error("g_mkdtemp: %s", g_strerror(errno)); } tmpdir = dir; tmpserver = g_strconcat(tmpdir, "/server", NULL); @@ -494,6 +509,9 @@ int main(int argc, char **argv) qtest_add_func("/ivshmem/pair", test_ivshmem_pair); qtest_add_func("/ivshmem/server", test_ivshmem_server); } + if (!strcmp(arch, "x86_64") && qtest_has_machine("q35")) { + qtest_add_func("/ivshmem/hotplug-q35", test_ivshmem_hotplug_q35); + } out: ret = g_test_run(); diff --git a/tests/qtest/libqmp.c b/tests/qtest/libqmp.c index ade26c15f0..a89cab03c3 100644 --- a/tests/qtest/libqmp.c +++ b/tests/qtest/libqmp.c @@ -23,6 +23,7 @@ #endif #include "qemu/cutils.h" +#include "qemu/sockets.h" #include "qapi/error.h" #include "qapi/qmp/json-parser.h" #include "qapi/qmp/qjson.h" @@ -36,7 +37,7 @@ typedef struct { static void socket_send(int fd, const char *buf, size_t size) { - size_t res = qemu_write_full(fd, buf, size); + ssize_t res = qemu_send_full(fd, buf, size); assert(res == size); } @@ -69,7 +70,7 @@ QDict *qmp_fd_receive(int fd) ssize_t len; char c; - len = read(fd, &c, 1); + len = recv(fd, &c, 1, 0); if (len == -1 && errno == EINTR) { continue; } @@ -133,7 +134,7 @@ static void socket_send_fds(int socket_fd, int *fds, size_t fds_num, * in the case that they choose to discard all replies up until * a particular EVENT is received. */ -static void +static G_GNUC_PRINTF(4, 0) void _qmp_fd_vsend_fds(int fd, int *fds, size_t fds_num, const char *fmt, va_list ap) { diff --git a/tests/qtest/libqos/aarch64-xlnx-zcu102-machine.c b/tests/qtest/libqos/aarch64-xlnx-zcu102-machine.c index c8a3ea11eb..ab24add8eb 100644 --- a/tests/qtest/libqos/aarch64-xlnx-zcu102-machine.c +++ b/tests/qtest/libqos/aarch64-xlnx-zcu102-machine.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "../libqtest.h" #include "qemu/module.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qgraph.h" #include "sdhci.h" diff --git a/tests/qtest/libqos/ahci.c b/tests/qtest/libqos/ahci.c index f53f12aa99..6d59c7551a 100644 --- a/tests/qtest/libqos/ahci.c +++ b/tests/qtest/libqos/ahci.c @@ -404,57 +404,110 @@ void ahci_port_clear(AHCIQState *ahci, uint8_t port) /** * Check a port for errors. */ -void ahci_port_check_error(AHCIQState *ahci, uint8_t port, - uint32_t imask, uint8_t emask) +void ahci_port_check_error(AHCIQState *ahci, AHCICommand *cmd) { + uint8_t port = cmd->port; uint32_t reg; - /* The upper 9 bits of the IS register all indicate errors. */ - reg = ahci_px_rreg(ahci, port, AHCI_PX_IS); - reg &= ~imask; - reg >>= 23; - g_assert_cmphex(reg, ==, 0); + /* If expecting TF error, ensure that TFES is set. */ + if (cmd->errors) { + reg = ahci_px_rreg(ahci, port, AHCI_PX_IS); + ASSERT_BIT_SET(reg, AHCI_PX_IS_TFES); + } else { + /* The upper 9 bits of the IS register all indicate errors. */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_IS); + reg &= ~cmd->interrupts; + reg >>= 23; + g_assert_cmphex(reg, ==, 0); + } - /* The Sata Error Register should be empty. */ + /* The Sata Error Register should be empty, even when expecting TF error. */ reg = ahci_px_rreg(ahci, port, AHCI_PX_SERR); g_assert_cmphex(reg, ==, 0); + /* If expecting TF error, and TFES was set, perform error recovery + * (see AHCI 1.3 section 6.2.2.1) such that we can send new commands. */ + if (cmd->errors) { + /* This will clear PxCI. */ + ahci_px_clr(ahci, port, AHCI_PX_CMD, AHCI_PX_CMD_ST); + + /* The port has 500ms to disengage. */ + usleep(500000); + reg = ahci_px_rreg(ahci, port, AHCI_PX_CMD); + ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CR); + + /* Clear PxIS. */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_IS); + ahci_px_wreg(ahci, port, AHCI_PX_IS, reg); + + /* Check if we need to perform a COMRESET. + * Not implemented right now, as there is no reason why our QEMU model + * should need a COMRESET when expecting TF error. */ + reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD); + ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_BSY | AHCI_PX_TFD_STS_DRQ); + + /* Enable issuing new commands. */ + ahci_px_set(ahci, port, AHCI_PX_CMD, AHCI_PX_CMD_ST); + } + /* The TFD also has two error sections. */ reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD); - if (!emask) { + if (!cmd->errors) { ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR); } else { ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_ERR); } - ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR & (~emask << 8)); - ASSERT_BIT_SET(reg, AHCI_PX_TFD_ERR & (emask << 8)); + ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR & (~cmd->errors << 8)); + ASSERT_BIT_SET(reg, AHCI_PX_TFD_ERR & (cmd->errors << 8)); } -void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port, - uint32_t intr_mask) +void ahci_port_check_interrupts(AHCIQState *ahci, AHCICommand *cmd) { + uint8_t port = cmd->port; uint32_t reg; + /* If we expect errors, error handling in ahci_port_check_error() will + * already have cleared PxIS, so in that case this function cannot verify + * and clear expected interrupts. */ + if (cmd->errors) { + return; + } + /* Check for expected interrupts */ reg = ahci_px_rreg(ahci, port, AHCI_PX_IS); - ASSERT_BIT_SET(reg, intr_mask); + ASSERT_BIT_SET(reg, cmd->interrupts); /* Clear expected interrupts and assert all interrupts now cleared. */ - ahci_px_wreg(ahci, port, AHCI_PX_IS, intr_mask); + ahci_px_wreg(ahci, port, AHCI_PX_IS, cmd->interrupts); g_assert_cmphex(ahci_px_rreg(ahci, port, AHCI_PX_IS), ==, 0); } -void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot) +void ahci_port_check_nonbusy(AHCIQState *ahci, AHCICommand *cmd) { + uint8_t slot = cmd->slot; + uint8_t port = cmd->port; uint32_t reg; - /* Assert that the command slot is no longer busy (NCQ) */ + /* For NCQ command with error PxSACT bit should still be set. + * For NCQ command without error, PxSACT bit should be cleared. + * For non-NCQ command, PxSACT bit should always be cleared. */ reg = ahci_px_rreg(ahci, port, AHCI_PX_SACT); - ASSERT_BIT_CLEAR(reg, (1 << slot)); + if (cmd->props->ncq && cmd->errors) { + ASSERT_BIT_SET(reg, (1 << slot)); + } else { + ASSERT_BIT_CLEAR(reg, (1 << slot)); + } - /* Non-NCQ */ + /* For non-NCQ command with error, PxCI bit should still be set. + * For non-NCQ command without error, PxCI bit should be cleared. + * For NCQ command without error, PxCI bit should be cleared. + * For NCQ command with error, PxCI bit may or may not be cleared. */ reg = ahci_px_rreg(ahci, port, AHCI_PX_CI); - ASSERT_BIT_CLEAR(reg, (1 << slot)); + if (!cmd->props->ncq && cmd->errors) { + ASSERT_BIT_SET(reg, (1 << slot)); + } else if (!cmd->errors) { + ASSERT_BIT_CLEAR(reg, (1 << slot)); + } /* And assert that we are generally not busy. */ reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD); @@ -609,7 +662,7 @@ unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port) g_assert_not_reached(); } -inline unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd) +static unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd) { /* Each PRD can describe up to 4MiB */ g_assert_cmphex(bytes_per_prd, <=, 4096 * 1024); @@ -1207,9 +1260,10 @@ void ahci_command_wait(AHCIQState *ahci, AHCICommand *cmd) #define RSET(REG, MASK) (BITSET(ahci_px_rreg(ahci, cmd->port, (REG)), (MASK))) - while (RSET(AHCI_PX_TFD, AHCI_PX_TFD_STS_BSY) || - RSET(AHCI_PX_CI, 1 << cmd->slot) || - (cmd->props->ncq && RSET(AHCI_PX_SACT, 1 << cmd->slot))) { + while (!RSET(AHCI_PX_TFD, AHCI_PX_TFD_STS_ERR) && + (RSET(AHCI_PX_TFD, AHCI_PX_TFD_STS_BSY) || + RSET(AHCI_PX_CI, 1 << cmd->slot) || + (cmd->props->ncq && RSET(AHCI_PX_SACT, 1 << cmd->slot)))) { usleep(50); } @@ -1226,9 +1280,9 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd) uint8_t slot = cmd->slot; uint8_t port = cmd->port; - ahci_port_check_error(ahci, port, cmd->interrupts, cmd->errors); - ahci_port_check_interrupts(ahci, port, cmd->interrupts); - ahci_port_check_nonbusy(ahci, port, slot); + ahci_port_check_nonbusy(ahci, cmd); + ahci_port_check_error(ahci, cmd); + ahci_port_check_interrupts(ahci, cmd); ahci_port_check_cmd_sanity(ahci, cmd); if (cmd->interrupts & AHCI_PX_IS_DHRS) { ahci_port_check_d2h_sanity(ahci, port, slot); diff --git a/tests/qtest/libqos/ahci.h b/tests/qtest/libqos/ahci.h index 88835b6228..a0487a1557 100644 --- a/tests/qtest/libqos/ahci.h +++ b/tests/qtest/libqos/ahci.h @@ -590,18 +590,15 @@ void ahci_set_command_header(AHCIQState *ahci, uint8_t port, void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot); /* AHCI sanity check routines */ -void ahci_port_check_error(AHCIQState *ahci, uint8_t port, - uint32_t imask, uint8_t emask); -void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port, - uint32_t intr_mask); -void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot); +void ahci_port_check_error(AHCIQState *ahci, AHCICommand *cmd); +void ahci_port_check_interrupts(AHCIQState *ahci, AHCICommand *cmd); +void ahci_port_check_nonbusy(AHCIQState *ahci, AHCICommand *cmd); void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot); void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd); void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd); /* Misc */ bool is_atapi(AHCIQState *ahci, uint8_t port); -unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd); /* Command: Macro level execution */ void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, diff --git a/tests/qtest/libqos/arm-imx25-pdk-machine.c b/tests/qtest/libqos/arm-imx25-pdk-machine.c index 54d0c95330..8fe128fae8 100644 --- a/tests/qtest/libqos/arm-imx25-pdk-machine.c +++ b/tests/qtest/libqos/arm-imx25-pdk-machine.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "../libqtest.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qgraph.h" #include "i2c.h" diff --git a/tests/qtest/libqos/arm-n800-machine.c b/tests/qtest/libqos/arm-n800-machine.c index ecd46b1daf..4e5afe0164 100644 --- a/tests/qtest/libqos/arm-n800-machine.c +++ b/tests/qtest/libqos/arm-n800-machine.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "../libqtest.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qgraph.h" #include "i2c.h" diff --git a/tests/qtest/libqos/arm-raspi2-machine.c b/tests/qtest/libqos/arm-raspi2-machine.c index 0a2943440b..367c6c17a5 100644 --- a/tests/qtest/libqos/arm-raspi2-machine.c +++ b/tests/qtest/libqos/arm-raspi2-machine.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "../libqtest.h" #include "qemu/module.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qgraph.h" #include "sdhci.h" diff --git a/tests/qtest/libqos/arm-sabrelite-machine.c b/tests/qtest/libqos/arm-sabrelite-machine.c index ec19a01660..94f6a20fc7 100644 --- a/tests/qtest/libqos/arm-sabrelite-machine.c +++ b/tests/qtest/libqos/arm-sabrelite-machine.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "../libqtest.h" #include "qemu/module.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qgraph.h" #include "sdhci.h" diff --git a/tests/qtest/libqos/arm-smdkc210-machine.c b/tests/qtest/libqos/arm-smdkc210-machine.c index 4bff249ee8..9bbce924ea 100644 --- a/tests/qtest/libqos/arm-smdkc210-machine.c +++ b/tests/qtest/libqos/arm-smdkc210-machine.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "../libqtest.h" #include "qemu/module.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qgraph.h" #include "sdhci.h" diff --git a/tests/qtest/libqos/arm-virt-machine.c b/tests/qtest/libqos/arm-virt-machine.c index 139eaba142..4e87405b58 100644 --- a/tests/qtest/libqos/arm-virt-machine.c +++ b/tests/qtest/libqos/arm-virt-machine.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "../libqtest.h" #include "qemu/module.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qgraph.h" #include "virtio-mmio.h" #include "generic-pcihost.h" diff --git a/tests/qtest/libqos/arm-xilinx-zynq-a9-machine.c b/tests/qtest/libqos/arm-xilinx-zynq-a9-machine.c index 3be80020a6..daac762a06 100644 --- a/tests/qtest/libqos/arm-xilinx-zynq-a9-machine.c +++ b/tests/qtest/libqos/arm-xilinx-zynq-a9-machine.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "../libqtest.h" #include "qemu/module.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qgraph.h" #include "sdhci.h" diff --git a/tests/qtest/libqos/e1000e.c b/tests/qtest/libqos/e1000e.c index f87e0e84b2..925654c7fd 100644 --- a/tests/qtest/libqos/e1000e.c +++ b/tests/qtest/libqos/e1000e.c @@ -17,100 +17,57 @@ */ #include "qemu/osdep.h" +#include "hw/net/e1000_regs.h" +#include "hw/pci/pci_ids.h" #include "../libqtest.h" #include "pci-pc.h" #include "qemu/sockets.h" #include "qemu/iov.h" #include "qemu/module.h" #include "qemu/bitops.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qgraph.h" #include "e1000e.h" -#define E1000E_IMS (0x00d0) +#define E1000E_IVAR_TEST_CFG \ + (((E1000E_RX0_MSG_ID | E1000_IVAR_INT_ALLOC_VALID) << E1000_IVAR_RXQ0_SHIFT) | \ + ((E1000E_TX0_MSG_ID | E1000_IVAR_INT_ALLOC_VALID) << E1000_IVAR_TXQ0_SHIFT) | \ + E1000_IVAR_TX_INT_EVERY_WB) -#define E1000E_STATUS (0x0008) -#define E1000E_STATUS_LU BIT(1) -#define E1000E_STATUS_ASDV1000 BIT(9) - -#define E1000E_CTRL (0x0000) -#define E1000E_CTRL_RESET BIT(26) - -#define E1000E_RCTL (0x0100) -#define E1000E_RCTL_EN BIT(1) -#define E1000E_RCTL_UPE BIT(3) -#define E1000E_RCTL_MPE BIT(4) - -#define E1000E_RFCTL (0x5008) -#define E1000E_RFCTL_EXTEN BIT(15) - -#define E1000E_TCTL (0x0400) -#define E1000E_TCTL_EN BIT(1) - -#define E1000E_CTRL_EXT (0x0018) -#define E1000E_CTRL_EXT_DRV_LOAD BIT(28) -#define E1000E_CTRL_EXT_TXLSFLOW BIT(22) - -#define E1000E_IVAR (0x00E4) -#define E1000E_IVAR_TEST_CFG ((E1000E_RX0_MSG_ID << 0) | BIT(3) | \ - (E1000E_TX0_MSG_ID << 8) | BIT(11) | \ - (E1000E_OTHER_MSG_ID << 16) | BIT(19) | \ - BIT(31)) - -#define E1000E_RING_LEN (0x1000) - -#define E1000E_TDBAL (0x3800) - -#define E1000E_TDBAH (0x3804) -#define E1000E_TDH (0x3810) - -#define E1000E_RDBAL (0x2800) -#define E1000E_RDBAH (0x2804) -#define E1000E_RDH (0x2810) - -#define E1000E_TXD_LEN (16) -#define E1000E_RXD_LEN (16) - -static void e1000e_macreg_write(QE1000E *d, uint32_t reg, uint32_t val) -{ - QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); - qpci_io_writel(&d_pci->pci_dev, d_pci->mac_regs, reg, val); -} - -static uint32_t e1000e_macreg_read(QE1000E *d, uint32_t reg) -{ - QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); - return qpci_io_readl(&d_pci->pci_dev, d_pci->mac_regs, reg); -} +#define E1000E_RING_LEN (0x1000) void e1000e_tx_ring_push(QE1000E *d, void *descr) { QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); - uint32_t tail = e1000e_macreg_read(d, E1000E_TDT); - uint32_t len = e1000e_macreg_read(d, E1000E_TDLEN) / E1000E_TXD_LEN; + uint32_t tail = e1000e_macreg_read(d, E1000_TDT); + uint32_t len = e1000e_macreg_read(d, E1000_TDLEN) / E1000_RING_DESC_LEN; - qtest_memwrite(d_pci->pci_dev.bus->qts, d->tx_ring + tail * E1000E_TXD_LEN, - descr, E1000E_TXD_LEN); - e1000e_macreg_write(d, E1000E_TDT, (tail + 1) % len); + qtest_memwrite(d_pci->pci_dev.bus->qts, + d->tx_ring + tail * E1000_RING_DESC_LEN, + descr, E1000_RING_DESC_LEN); + e1000e_macreg_write(d, E1000_TDT, (tail + 1) % len); /* Read WB data for the packet transmitted */ - qtest_memread(d_pci->pci_dev.bus->qts, d->tx_ring + tail * E1000E_TXD_LEN, - descr, E1000E_TXD_LEN); + qtest_memread(d_pci->pci_dev.bus->qts, + d->tx_ring + tail * E1000_RING_DESC_LEN, + descr, E1000_RING_DESC_LEN); } void e1000e_rx_ring_push(QE1000E *d, void *descr) { QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); - uint32_t tail = e1000e_macreg_read(d, E1000E_RDT); - uint32_t len = e1000e_macreg_read(d, E1000E_RDLEN) / E1000E_RXD_LEN; + uint32_t tail = e1000e_macreg_read(d, E1000_RDT); + uint32_t len = e1000e_macreg_read(d, E1000_RDLEN) / E1000_RING_DESC_LEN; - qtest_memwrite(d_pci->pci_dev.bus->qts, d->rx_ring + tail * E1000E_RXD_LEN, - descr, E1000E_RXD_LEN); - e1000e_macreg_write(d, E1000E_RDT, (tail + 1) % len); + qtest_memwrite(d_pci->pci_dev.bus->qts, + d->rx_ring + tail * E1000_RING_DESC_LEN, + descr, E1000_RING_DESC_LEN); + e1000e_macreg_write(d, E1000_RDT, (tail + 1) % len); /* Read WB data for the packet received */ - qtest_memread(d_pci->pci_dev.bus->qts, d->rx_ring + tail * E1000E_RXD_LEN, - descr, E1000E_RXD_LEN); + qtest_memread(d_pci->pci_dev.bus->qts, + d->rx_ring + tail * E1000_RING_DESC_LEN, + descr, E1000_RING_DESC_LEN); } static void e1000e_foreach_callback(QPCIDevice *dev, int devfn, void *data) @@ -151,53 +108,54 @@ static void e1000e_pci_start_hw(QOSGraphObject *obj) qpci_device_enable(&d->pci_dev); /* Reset the device */ - val = e1000e_macreg_read(&d->e1000e, E1000E_CTRL); - e1000e_macreg_write(&d->e1000e, E1000E_CTRL, val | E1000E_CTRL_RESET); + val = e1000e_macreg_read(&d->e1000e, E1000_CTRL); + e1000e_macreg_write(&d->e1000e, E1000_CTRL, val | E1000_CTRL_RST | E1000_CTRL_SLU); /* Enable and configure MSI-X */ qpci_msix_enable(&d->pci_dev); - e1000e_macreg_write(&d->e1000e, E1000E_IVAR, E1000E_IVAR_TEST_CFG); + e1000e_macreg_write(&d->e1000e, E1000_IVAR, E1000E_IVAR_TEST_CFG); /* Check the device status - link and speed */ - val = e1000e_macreg_read(&d->e1000e, E1000E_STATUS); - g_assert_cmphex(val & (E1000E_STATUS_LU | E1000E_STATUS_ASDV1000), - ==, E1000E_STATUS_LU | E1000E_STATUS_ASDV1000); + val = e1000e_macreg_read(&d->e1000e, E1000_STATUS); + g_assert_cmphex(val & (E1000_STATUS_LU | E1000_STATUS_ASDV_1000), + ==, E1000_STATUS_LU | E1000_STATUS_ASDV_1000); /* Initialize TX/RX logic */ - e1000e_macreg_write(&d->e1000e, E1000E_RCTL, 0); - e1000e_macreg_write(&d->e1000e, E1000E_TCTL, 0); + e1000e_macreg_write(&d->e1000e, E1000_RCTL, 0); + e1000e_macreg_write(&d->e1000e, E1000_TCTL, 0); /* Notify the device that the driver is ready */ - val = e1000e_macreg_read(&d->e1000e, E1000E_CTRL_EXT); - e1000e_macreg_write(&d->e1000e, E1000E_CTRL_EXT, - val | E1000E_CTRL_EXT_DRV_LOAD | E1000E_CTRL_EXT_TXLSFLOW); + val = e1000e_macreg_read(&d->e1000e, E1000_CTRL_EXT); + e1000e_macreg_write(&d->e1000e, E1000_CTRL_EXT, + val | E1000_CTRL_EXT_DRV_LOAD); - e1000e_macreg_write(&d->e1000e, E1000E_TDBAL, + e1000e_macreg_write(&d->e1000e, E1000_TDBAL, (uint32_t) d->e1000e.tx_ring); - e1000e_macreg_write(&d->e1000e, E1000E_TDBAH, + e1000e_macreg_write(&d->e1000e, E1000_TDBAH, (uint32_t) (d->e1000e.tx_ring >> 32)); - e1000e_macreg_write(&d->e1000e, E1000E_TDLEN, E1000E_RING_LEN); - e1000e_macreg_write(&d->e1000e, E1000E_TDT, 0); - e1000e_macreg_write(&d->e1000e, E1000E_TDH, 0); + e1000e_macreg_write(&d->e1000e, E1000_TDLEN, E1000E_RING_LEN); + e1000e_macreg_write(&d->e1000e, E1000_TDT, 0); + e1000e_macreg_write(&d->e1000e, E1000_TDH, 0); /* Enable transmit */ - e1000e_macreg_write(&d->e1000e, E1000E_TCTL, E1000E_TCTL_EN); - e1000e_macreg_write(&d->e1000e, E1000E_RDBAL, + e1000e_macreg_write(&d->e1000e, E1000_TCTL, E1000_TCTL_EN); + + e1000e_macreg_write(&d->e1000e, E1000_RDBAL, (uint32_t)d->e1000e.rx_ring); - e1000e_macreg_write(&d->e1000e, E1000E_RDBAH, + e1000e_macreg_write(&d->e1000e, E1000_RDBAH, (uint32_t)(d->e1000e.rx_ring >> 32)); - e1000e_macreg_write(&d->e1000e, E1000E_RDLEN, E1000E_RING_LEN); - e1000e_macreg_write(&d->e1000e, E1000E_RDT, 0); - e1000e_macreg_write(&d->e1000e, E1000E_RDH, 0); + e1000e_macreg_write(&d->e1000e, E1000_RDLEN, E1000E_RING_LEN); + e1000e_macreg_write(&d->e1000e, E1000_RDT, 0); + e1000e_macreg_write(&d->e1000e, E1000_RDH, 0); /* Enable receive */ - e1000e_macreg_write(&d->e1000e, E1000E_RFCTL, E1000E_RFCTL_EXTEN); - e1000e_macreg_write(&d->e1000e, E1000E_RCTL, E1000E_RCTL_EN | - E1000E_RCTL_UPE | - E1000E_RCTL_MPE); + e1000e_macreg_write(&d->e1000e, E1000_RFCTL, E1000_RFCTL_EXTEN); + e1000e_macreg_write(&d->e1000e, E1000_RCTL, E1000_RCTL_EN | + E1000_RCTL_UPE | + E1000_RCTL_MPE); /* Enable all interrupts */ - e1000e_macreg_write(&d->e1000e, E1000E_IMS, 0xFFFFFFFF); + e1000e_macreg_write(&d->e1000e, E1000_IMS, 0xFFFFFFFF); } @@ -248,12 +206,14 @@ static void *e1000e_pci_create(void *pci_bus, QGuestAllocator *alloc, static void e1000e_register_nodes(void) { QPCIAddress addr = { - .vendor_id = 0x8086, - .device_id = 0x10D3, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = E1000_DEV_ID_82574L, }; - /* FIXME: every test using this node needs to setup a -netdev socket,id=hs0 - * otherwise QEMU is not going to start */ + /* + * FIXME: every test using this node needs to setup a -netdev socket,id=hs0 + * otherwise QEMU is not going to start + */ QOSGraphEdgeOptions opts = { .extra_device_opts = "netdev=hs0", }; diff --git a/tests/qtest/libqos/e1000e.h b/tests/qtest/libqos/e1000e.h index a22f5fdbad..30643c8094 100644 --- a/tests/qtest/libqos/e1000e.h +++ b/tests/qtest/libqos/e1000e.h @@ -24,12 +24,8 @@ #define E1000E_RX0_MSG_ID (0) #define E1000E_TX0_MSG_ID (1) -#define E1000E_OTHER_MSG_ID (2) -#define E1000E_TDLEN (0x3808) -#define E1000E_TDT (0x3818) -#define E1000E_RDLEN (0x2808) -#define E1000E_RDT (0x2818) +#define E1000E_ADDRESS { 0x52, 0x54, 0x00, 0x12, 0x34, 0x56 } typedef struct QE1000E QE1000E; typedef struct QE1000E_PCI QE1000E_PCI; @@ -46,6 +42,18 @@ struct QE1000E_PCI { QE1000E e1000e; }; +static inline void e1000e_macreg_write(QE1000E *d, uint32_t reg, uint32_t val) +{ + QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); + qpci_io_writel(&d_pci->pci_dev, d_pci->mac_regs, reg, val); +} + +static inline uint32_t e1000e_macreg_read(QE1000E *d, uint32_t reg) +{ + QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); + return qpci_io_readl(&d_pci->pci_dev, d_pci->mac_regs, reg); +} + void e1000e_wait_isr(QE1000E *d, uint16_t msg_id); void e1000e_tx_ring_push(QE1000E *d, void *descr); void e1000e_rx_ring_push(QE1000E *d, void *descr); diff --git a/tests/qtest/libqos/generic-pcihost.h b/tests/qtest/libqos/generic-pcihost.h index c693c769df..6493a8712a 100644 --- a/tests/qtest/libqos/generic-pcihost.h +++ b/tests/qtest/libqos/generic-pcihost.h @@ -14,7 +14,7 @@ #define LIBQOS_GENERIC_PCIHOST_H #include "pci.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qgraph.h" typedef struct QGenericPCIBus { diff --git a/tests/qtest/libqos/igb.c b/tests/qtest/libqos/igb.c new file mode 100644 index 0000000000..f40c4ec4cd --- /dev/null +++ b/tests/qtest/libqos/igb.c @@ -0,0 +1,191 @@ +/* + * libqos driver framework + * + * Copyright (c) 2022-2023 Red Hat, Inc. + * 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.1 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 "hw/net/igb_regs.h" +#include "hw/net/mii.h" +#include "hw/pci/pci_ids.h" +#include "../libqtest.h" +#include "pci-pc.h" +#include "qemu/sockets.h" +#include "qemu/iov.h" +#include "qemu/module.h" +#include "qemu/bitops.h" +#include "libqos-malloc.h" +#include "qgraph.h" +#include "e1000e.h" + +#define IGB_IVAR_TEST_CFG \ + ((E1000E_RX0_MSG_ID | E1000_IVAR_VALID) << (igb_ivar_entry_rx(0) * 8) | \ + ((E1000E_TX0_MSG_ID | E1000_IVAR_VALID) << (igb_ivar_entry_tx(0) * 8))) + +#define E1000E_RING_LEN (0x1000) + +static void e1000e_foreach_callback(QPCIDevice *dev, int devfn, void *data) +{ + QPCIDevice *res = data; + memcpy(res, dev, sizeof(QPCIDevice)); + g_free(dev); +} + +static void e1000e_pci_destructor(QOSGraphObject *obj) +{ + QE1000E_PCI *epci = (QE1000E_PCI *) obj; + qpci_iounmap(&epci->pci_dev, epci->mac_regs); + qpci_msix_disable(&epci->pci_dev); +} + +static void igb_pci_start_hw(QOSGraphObject *obj) +{ + static const uint8_t address[] = E1000E_ADDRESS; + QE1000E_PCI *d = (QE1000E_PCI *) obj; + uint32_t val; + + /* Enable the device */ + qpci_device_enable(&d->pci_dev); + + /* Reset the device */ + val = e1000e_macreg_read(&d->e1000e, E1000_CTRL); + e1000e_macreg_write(&d->e1000e, E1000_CTRL, val | E1000_CTRL_RST | E1000_CTRL_SLU); + + /* Setup link */ + e1000e_macreg_write(&d->e1000e, E1000_MDIC, + MII_BMCR_AUTOEN | MII_BMCR_ANRESTART | + (MII_BMCR << E1000_MDIC_REG_SHIFT) | + (1 << E1000_MDIC_PHY_SHIFT) | + E1000_MDIC_OP_WRITE); + + qtest_clock_step(d->pci_dev.bus->qts, 900000000); + + /* Enable and configure MSI-X */ + qpci_msix_enable(&d->pci_dev); + e1000e_macreg_write(&d->e1000e, E1000_IVAR0, IGB_IVAR_TEST_CFG); + + /* Check the device link status */ + val = e1000e_macreg_read(&d->e1000e, E1000_STATUS); + g_assert_cmphex(val & E1000_STATUS_LU, ==, E1000_STATUS_LU); + + /* Initialize TX/RX logic */ + e1000e_macreg_write(&d->e1000e, E1000_RCTL, 0); + e1000e_macreg_write(&d->e1000e, E1000_TCTL, 0); + + e1000e_macreg_write(&d->e1000e, E1000_TDBAL(0), + (uint32_t) d->e1000e.tx_ring); + e1000e_macreg_write(&d->e1000e, E1000_TDBAH(0), + (uint32_t) (d->e1000e.tx_ring >> 32)); + e1000e_macreg_write(&d->e1000e, E1000_TDLEN(0), E1000E_RING_LEN); + e1000e_macreg_write(&d->e1000e, E1000_TDT(0), 0); + e1000e_macreg_write(&d->e1000e, E1000_TDH(0), 0); + + /* Enable transmit */ + e1000e_macreg_write(&d->e1000e, E1000_TCTL, E1000_TCTL_EN); + + e1000e_macreg_write(&d->e1000e, E1000_RDBAL(0), + (uint32_t)d->e1000e.rx_ring); + e1000e_macreg_write(&d->e1000e, E1000_RDBAH(0), + (uint32_t)(d->e1000e.rx_ring >> 32)); + e1000e_macreg_write(&d->e1000e, E1000_RDLEN(0), E1000E_RING_LEN); + e1000e_macreg_write(&d->e1000e, E1000_RDT(0), 0); + e1000e_macreg_write(&d->e1000e, E1000_RDH(0), 0); + e1000e_macreg_write(&d->e1000e, E1000_RA, + le32_to_cpu(*(uint32_t *)address)); + e1000e_macreg_write(&d->e1000e, E1000_RA + 4, + E1000_RAH_AV | E1000_RAH_POOL_1 | + le16_to_cpu(*(uint16_t *)(address + 4))); + + /* Set supported receive descriptor mode */ + e1000e_macreg_write(&d->e1000e, + E1000_SRRCTL(0), + E1000_SRRCTL_DESCTYPE_ADV_ONEBUF); + + /* Enable receive */ + e1000e_macreg_write(&d->e1000e, E1000_RFCTL, E1000_RFCTL_EXTEN); + e1000e_macreg_write(&d->e1000e, E1000_RCTL, E1000_RCTL_EN); + + /* Enable all interrupts */ + e1000e_macreg_write(&d->e1000e, E1000_GPIE, E1000_GPIE_MSIX_MODE); + e1000e_macreg_write(&d->e1000e, E1000_IMS, 0xFFFFFFFF); + e1000e_macreg_write(&d->e1000e, E1000_EIMS, 0xFFFFFFFF); + +} + +static void *igb_pci_get_driver(void *obj, const char *interface) +{ + QE1000E_PCI *epci = obj; + if (!g_strcmp0(interface, "igb-if")) { + return &epci->e1000e; + } + + /* implicit contains */ + if (!g_strcmp0(interface, "pci-device")) { + return &epci->pci_dev; + } + + fprintf(stderr, "%s not present in igb\n", interface); + g_assert_not_reached(); +} + +static void *igb_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QE1000E_PCI *d = g_new0(QE1000E_PCI, 1); + QPCIBus *bus = pci_bus; + QPCIAddress *address = addr; + + qpci_device_foreach(bus, address->vendor_id, address->device_id, + e1000e_foreach_callback, &d->pci_dev); + + /* Map BAR0 (mac registers) */ + d->mac_regs = qpci_iomap(&d->pci_dev, 0, NULL); + + /* Allocate and setup TX ring */ + d->e1000e.tx_ring = guest_alloc(alloc, E1000E_RING_LEN); + g_assert(d->e1000e.tx_ring != 0); + + /* Allocate and setup RX ring */ + d->e1000e.rx_ring = guest_alloc(alloc, E1000E_RING_LEN); + g_assert(d->e1000e.rx_ring != 0); + + d->obj.get_driver = igb_pci_get_driver; + d->obj.start_hw = igb_pci_start_hw; + d->obj.destructor = e1000e_pci_destructor; + + return &d->obj; +} + +static void igb_register_nodes(void) +{ + QPCIAddress addr = { + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = E1000_DEV_ID_82576, + }; + + /* + * FIXME: every test using this node needs to setup a -netdev socket,id=hs0 + * otherwise QEMU is not going to start + */ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "netdev=hs0", + }; + add_qpci_address(&opts, &addr); + + qos_node_create_driver("igb", igb_pci_create); + qos_node_consumes("igb", "pci-bus", &opts); +} + +libqos_init(igb_register_nodes); diff --git a/tests/qtest/libqos/malloc.c b/tests/qtest/libqos/libqos-malloc.c similarity index 99% rename from tests/qtest/libqos/malloc.c rename to tests/qtest/libqos/libqos-malloc.c index f0c8f950c8..d7566972c4 100644 --- a/tests/qtest/libqos/malloc.c +++ b/tests/qtest/libqos/libqos-malloc.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qemu/host-utils.h" typedef struct MemBlock { diff --git a/tests/qtest/libqos/malloc.h b/tests/qtest/libqos/libqos-malloc.h similarity index 100% rename from tests/qtest/libqos/malloc.h rename to tests/qtest/libqos/libqos-malloc.h diff --git a/tests/qtest/libqos/libqos-pc.h b/tests/qtest/libqos/libqos-pc.h index 1a9923ead4..a2e4209a49 100644 --- a/tests/qtest/libqos/libqos-pc.h +++ b/tests/qtest/libqos/libqos-pc.h @@ -3,8 +3,10 @@ #include "libqos.h" -QOSState *qtest_pc_vboot(const char *cmdline_fmt, va_list ap); -QOSState *qtest_pc_boot(const char *cmdline_fmt, ...); +QOSState *qtest_pc_vboot(const char *cmdline_fmt, va_list ap) + G_GNUC_PRINTF(1, 0); +QOSState *qtest_pc_boot(const char *cmdline_fmt, ...) + G_GNUC_PRINTF(1, 2); void qtest_pc_shutdown(QOSState *qs); #endif diff --git a/tests/qtest/libqos/libqos-spapr.h b/tests/qtest/libqos/libqos-spapr.h index c61338917a..e4483c14f8 100644 --- a/tests/qtest/libqos/libqos-spapr.h +++ b/tests/qtest/libqos/libqos-spapr.h @@ -3,8 +3,10 @@ #include "libqos.h" -QOSState *qtest_spapr_vboot(const char *cmdline_fmt, va_list ap); -QOSState *qtest_spapr_boot(const char *cmdline_fmt, ...); +QOSState *qtest_spapr_vboot(const char *cmdline_fmt, va_list ap) + G_GNUC_PRINTF(1, 0); +QOSState *qtest_spapr_boot(const char *cmdline_fmt, ...) + G_GNUC_PRINTF(1, 2); void qtest_spapr_shutdown(QOSState *qs); /* List of capabilities needed to silence warnings with TCG */ diff --git a/tests/qtest/libqos/libqos.c b/tests/qtest/libqos/libqos.c index 85c7641add..5c0fa1f7c5 100644 --- a/tests/qtest/libqos/libqos.c +++ b/tests/qtest/libqos/libqos.c @@ -1,6 +1,4 @@ #include "qemu/osdep.h" -#include - #include "../libqtest.h" #include "libqos.h" #include "pci.h" @@ -139,56 +137,9 @@ void migrate(QOSState *from, QOSState *to, const char *uri) migrate_allocator(&from->alloc, &to->alloc); } -bool have_qemu_img(void) -{ - char *rpath; - const char *path = getenv("QTEST_QEMU_IMG"); - if (!path) { - return false; - } - - rpath = realpath(path, NULL); - if (!rpath) { - return false; - } else { - free(rpath); - return true; - } -} - -void mkimg(const char *file, const char *fmt, unsigned size_mb) -{ - gchar *cli; - bool ret; - int rc; - GError *err = NULL; - char *qemu_img_path; - gchar *out, *out2; - char *qemu_img_abs_path; - - qemu_img_path = getenv("QTEST_QEMU_IMG"); - g_assert(qemu_img_path); - qemu_img_abs_path = realpath(qemu_img_path, NULL); - g_assert(qemu_img_abs_path); - - cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path, - fmt, file, size_mb); - ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err); - if (err || !g_spawn_check_exit_status(rc, &err)) { - fprintf(stderr, "%s\n", err->message); - g_error_free(err); - } - g_assert(ret && !err); - - g_free(out); - g_free(out2); - g_free(cli); - free(qemu_img_abs_path); -} - void mkqcow2(const char *file, unsigned size_mb) { - return mkimg(file, "qcow2", size_mb); + g_assert_true(mkimg(file, "qcow2", size_mb)); } void prepare_blkdebug_script(const char *debug_fn, const char *event) diff --git a/tests/qtest/libqos/libqos.h b/tests/qtest/libqos/libqos.h index ba7df448ca..c04950e2b1 100644 --- a/tests/qtest/libqos/libqos.h +++ b/tests/qtest/libqos/libqos.h @@ -3,7 +3,7 @@ #include "../libqtest.h" #include "pci.h" -#include "malloc.h" +#include "libqos-malloc.h" typedef struct QOSState QOSState; @@ -21,12 +21,12 @@ struct QOSState { QOSOps *ops; }; -QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap); -QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...); +QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap) + G_GNUC_PRINTF(2, 0); +QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...) + G_GNUC_PRINTF(2, 3); void qtest_common_shutdown(QOSState *qs); void qtest_shutdown(QOSState *qs); -bool have_qemu_img(void); -void mkimg(const char *file, const char *fmt, unsigned size_mb); void mkqcow2(const char *file, unsigned size_mb); void migrate(QOSState *from, QOSState *to, const char *uri); void prepare_blkdebug_script(const char *debug_fn, const char *event); diff --git a/tests/qtest/libqos/malloc-pc.h b/tests/qtest/libqos/malloc-pc.h index d8d79853c8..e531473601 100644 --- a/tests/qtest/libqos/malloc-pc.h +++ b/tests/qtest/libqos/malloc-pc.h @@ -13,7 +13,7 @@ #ifndef LIBQOS_MALLOC_PC_H #define LIBQOS_MALLOC_PC_H -#include "malloc.h" +#include "libqos-malloc.h" void pc_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags); diff --git a/tests/qtest/libqos/malloc-spapr.h b/tests/qtest/libqos/malloc-spapr.h index f99572fd71..f544c0d611 100644 --- a/tests/qtest/libqos/malloc-spapr.h +++ b/tests/qtest/libqos/malloc-spapr.h @@ -8,7 +8,7 @@ #ifndef LIBQOS_MALLOC_SPAPR_H #define LIBQOS_MALLOC_SPAPR_H -#include "malloc.h" +#include "libqos-malloc.h" void spapr_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags); diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build index fd5d6e5ae1..3aed6efcb8 100644 --- a/tests/qtest/libqos/meson.build +++ b/tests/qtest/libqos/meson.build @@ -6,7 +6,7 @@ libqos_srcs = files( 'qos_external.c', 'pci.c', 'fw_cfg.c', - 'malloc.c', + 'libqos-malloc.c', 'libqos.c', 'sdhci-cmd.c', @@ -30,10 +30,10 @@ libqos_srcs = files( 'i2c.c', 'i2c-imx.c', 'i2c-omap.c', + 'igb.c', 'sdhci.c', 'tpci200.c', 'virtio.c', - 'virtio-9p.c', 'virtio-balloon.c', 'virtio-blk.c', 'vhost-user-blk.c', @@ -45,6 +45,8 @@ libqos_srcs = files( 'virtio-scsi.c', 'virtio-serial.c', 'virtio-iommu.c', + 'virtio-gpio.c', + 'virtio-scmi.c', 'generic-pcihost.c', # qgraph machines: @@ -58,8 +60,13 @@ libqos_srcs = files( 'arm-xilinx-zynq-a9-machine.c', 'ppc64_pseries-machine.c', 'x86_64_pc-machine.c', + 'riscv-virt-machine.c', ) +if have_virtfs + libqos_srcs += files('virtio-9p.c', 'virtio-9p-client.c') +endif + libqos = static_library('qos', libqos_srcs + genh, name_suffix: 'fa', build_by_default: false) diff --git a/tests/qtest/libqos/pci-pc.c b/tests/qtest/libqos/pci-pc.c index 81c2c055ca..96046287ac 100644 --- a/tests/qtest/libqos/pci-pc.c +++ b/tests/qtest/libqos/pci-pc.c @@ -179,13 +179,7 @@ void qpci_free_pc(QPCIBus *bus) void qpci_unplug_acpi_device_test(QTestState *qts, const char *id, uint8_t slot) { - QDict *response; - - response = qtest_qmp(qts, "{'execute': 'device_del'," - " 'arguments': {'id': %s}}", id); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); + qtest_qmp_device_del_send(qts, id); qtest_outl(qts, ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot); diff --git a/tests/qtest/libqos/pci-pc.h b/tests/qtest/libqos/pci-pc.h index 49ec9507f2..849bd493de 100644 --- a/tests/qtest/libqos/pci-pc.h +++ b/tests/qtest/libqos/pci-pc.h @@ -14,7 +14,7 @@ #define LIBQOS_PCI_PC_H #include "pci.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qgraph.h" typedef struct QPCIBusPC { diff --git a/tests/qtest/libqos/pci-spapr.h b/tests/qtest/libqos/pci-spapr.h index 20a43718b7..3dbf1e58ae 100644 --- a/tests/qtest/libqos/pci-spapr.h +++ b/tests/qtest/libqos/pci-spapr.h @@ -8,7 +8,7 @@ #ifndef LIBQOS_PCI_SPAPR_H #define LIBQOS_PCI_SPAPR_H -#include "malloc.h" +#include "libqos-malloc.h" #include "pci.h" #include "qgraph.h" diff --git a/tests/qtest/libqos/qgraph.c b/tests/qtest/libqos/qgraph.c index 0a2dddfafa..2029bf9804 100644 --- a/tests/qtest/libqos/qgraph.c +++ b/tests/qtest/libqos/qgraph.c @@ -54,7 +54,7 @@ struct QOSStackElement { int length; }; -/* Each enty in these hash table will consist of pair. */ +/* Each entry in these hash table will consist of pair. */ static GHashTable *edge_table; static GHashTable *node_table; @@ -214,7 +214,7 @@ static QOSGraphEdge *search_list_edges(QOSGraphEdgeList *edgelist, /** * search_machine(): search for a machine @name in the node hash * table. A machine is the child of the root node. - * This function forces the research in the childs of the root, + * This function forces the research in the children of the root, * to check the node is a proper machine * * Returns: on success: the %QOSGraphNode diff --git a/tests/qtest/libqos/qgraph.h b/tests/qtest/libqos/qgraph.h index 871740c0dc..1b5de02e7b 100644 --- a/tests/qtest/libqos/qgraph.h +++ b/tests/qtest/libqos/qgraph.h @@ -21,10 +21,10 @@ #include #include "qemu/module.h" -#include "malloc.h" +#include "libqos-malloc.h" /* maximum path length */ -#define QOS_PATH_MAX_ELEMENT_SIZE 50 +#define QOS_PATH_MAX_ELEMENT_SIZE 128 typedef struct QOSGraphObject QOSGraphObject; typedef struct QOSGraphNode QOSGraphNode; @@ -381,7 +381,7 @@ QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent, * mind: only tests with a path down from the actual test case node (leaf) up * to the graph's root node are actually executed by the qtest framework. And * the qtest framework uses QMP to automatically check which QEMU drivers are - * actually currently available, and accordingly qos marks certain pathes as + * actually currently available, and accordingly qos marks certain paths as * 'unavailable' in such cases (e.g. when QEMU was compiled without support for * a certain feature). */ diff --git a/tests/qtest/libqos/qgraph_internal.h b/tests/qtest/libqos/qgraph_internal.h index 7d62fd17af..87fab1f9f0 100644 --- a/tests/qtest/libqos/qgraph_internal.h +++ b/tests/qtest/libqos/qgraph_internal.h @@ -197,7 +197,7 @@ char *qos_graph_edge_get_name(QOSGraphEdge *edge); * qos_graph_get_machine(): returns the machine assigned * to that @node name. * - * It performs a search only trough the list of machines + * It performs a search only through the list of machines * (i.e. the QOS_ROOT child). * * Returns: on success: the %QOSGraphNode diff --git a/tests/qtest/libqos/qos_external.c b/tests/qtest/libqos/qos_external.c index b7a0b873a3..c6bb8bff09 100644 --- a/tests/qtest/libqos/qos_external.c +++ b/tests/qtest/libqos/qos_external.c @@ -24,7 +24,7 @@ #include "qapi/qmp/qstring.h" #include "qemu/module.h" #include "qapi/qmp/qlist.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qgraph.h" #include "qgraph_internal.h" #include "qos_external.h" diff --git a/tests/qtest/libqos/qos_external.h b/tests/qtest/libqos/qos_external.h index 8446e3df0b..ea37364887 100644 --- a/tests/qtest/libqos/qos_external.h +++ b/tests/qtest/libqos/qos_external.h @@ -21,7 +21,7 @@ #include "qgraph.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qapi/qapi-types-machine.h" #include "qapi/qapi-types-qom.h" diff --git a/tests/qtest/libqos/riscv-virt-machine.c b/tests/qtest/libqos/riscv-virt-machine.c new file mode 100644 index 0000000000..c4364c9c5d --- /dev/null +++ b/tests/qtest/libqos/riscv-virt-machine.c @@ -0,0 +1,137 @@ +/* + * libqos driver framework for risc-v + * + * Initial version based on arm-virt-machine.c + * + * Copyright (c) 2024 Ventana Micro + * + * 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.1 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 "../libqtest.h" +#include "qemu/module.h" +#include "libqos-malloc.h" +#include "qgraph.h" +#include "virtio-mmio.h" +#include "generic-pcihost.h" +#include "hw/pci/pci_regs.h" + +#define RISCV_PAGE_SIZE 4096 + +/* VIRT_DRAM */ +#define RISCV_VIRT_RAM_ADDR 0x80000000 +#define RISCV_VIRT_RAM_SIZE 0x20000000 + +/* + * VIRT_VIRTIO. BASE_ADDR points to the last + * virtio_mmio device. + */ +#define VIRTIO_MMIO_BASE_ADDR 0x10008000 +#define VIRTIO_MMIO_SIZE 0x00001000 + +/* VIRT_PCIE_PIO */ +#define RISCV_GPEX_PIO_BASE 0x3000000 +#define RISCV_BUS_PIO_LIMIT 0x10000 + +/* VIRT_PCIE_MMIO */ +#define RISCV_BUS_MMIO_ALLOC_PTR 0x40000000 +#define RISCV_BUS_MMIO_LIMIT 0x80000000 + +/* VIRT_PCIE_ECAM */ +#define RISCV_ECAM_ALLOC_PTR 0x30000000 + +typedef struct QVirtMachine QVirtMachine; + +struct QVirtMachine { + QOSGraphObject obj; + QGuestAllocator alloc; + QVirtioMMIODevice virtio_mmio; + QGenericPCIHost bridge; +}; + +static void virt_destructor(QOSGraphObject *obj) +{ + QVirtMachine *machine = (QVirtMachine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *virt_get_driver(void *object, const char *interface) +{ + QVirtMachine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in riscv/virtio\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *virt_get_device(void *obj, const char *device) +{ + QVirtMachine *machine = obj; + if (!g_strcmp0(device, "generic-pcihost")) { + return &machine->bridge.obj; + } else if (!g_strcmp0(device, "virtio-mmio")) { + return &machine->virtio_mmio.obj; + } + + fprintf(stderr, "%s not present in riscv/virt\n", device); + g_assert_not_reached(); +} + +static void riscv_config_qpci_bus(QGenericPCIBus *qpci) +{ + qpci->gpex_pio_base = RISCV_GPEX_PIO_BASE; + qpci->bus.pio_limit = RISCV_BUS_PIO_LIMIT; + + qpci->bus.mmio_alloc_ptr = RISCV_BUS_MMIO_ALLOC_PTR; + qpci->bus.mmio_limit = RISCV_BUS_MMIO_LIMIT; + + qpci->ecam_alloc_ptr = RISCV_ECAM_ALLOC_PTR; +} + +static void *qos_create_machine_riscv_virt(QTestState *qts) +{ + QVirtMachine *machine = g_new0(QVirtMachine, 1); + + alloc_init(&machine->alloc, 0, + RISCV_VIRT_RAM_ADDR, + RISCV_VIRT_RAM_ADDR + RISCV_VIRT_RAM_SIZE, + RISCV_PAGE_SIZE); + qvirtio_mmio_init_device(&machine->virtio_mmio, qts, VIRTIO_MMIO_BASE_ADDR, + VIRTIO_MMIO_SIZE); + + qos_create_generic_pcihost(&machine->bridge, qts, &machine->alloc); + riscv_config_qpci_bus(&machine->bridge.pci); + + machine->obj.get_device = virt_get_device; + machine->obj.get_driver = virt_get_driver; + machine->obj.destructor = virt_destructor; + return machine; +} + +static void virt_machine_register_nodes(void) +{ + qos_node_create_machine_args("riscv32/virt", qos_create_machine_riscv_virt, + "aclint=on,aia=aplic-imsic"); + qos_node_contains("riscv32/virt", "virtio-mmio", NULL); + qos_node_contains("riscv32/virt", "generic-pcihost", NULL); + + qos_node_create_machine_args("riscv64/virt", qos_create_machine_riscv_virt, + "aclint=on,aia=aplic-imsic"); + qos_node_contains("riscv64/virt", "virtio-mmio", NULL); + qos_node_contains("riscv64/virt", "generic-pcihost", NULL); +} + +libqos_init(virt_machine_register_nodes); diff --git a/tests/qtest/libqos/rtas.h b/tests/qtest/libqos/rtas.h index f38f99dfab..be8353d505 100644 --- a/tests/qtest/libqos/rtas.h +++ b/tests/qtest/libqos/rtas.h @@ -5,7 +5,7 @@ #ifndef LIBQOS_RTAS_H #define LIBQOS_RTAS_H -#include "malloc.h" +#include "libqos-malloc.h" int qrtas_get_time_of_day(QTestState *qts, QGuestAllocator *alloc, struct tm *tm, uint32_t *ns); diff --git a/tests/qtest/libqos/virtio-9p-client.c b/tests/qtest/libqos/virtio-9p-client.c new file mode 100644 index 0000000000..b8adc8d4b9 --- /dev/null +++ b/tests/qtest/libqos/virtio-9p-client.c @@ -0,0 +1,1054 @@ +/* + * 9P network client for VirtIO 9P test cases (based on QTest) + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * Not so fast! You might want to read the 9p developer docs first: + * https://wiki.qemu.org/Documentation/9p + */ + +#include "qemu/osdep.h" +#include "virtio-9p-client.h" + +#define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000) +static QGuestAllocator *alloc; + +void v9fs_set_allocator(QGuestAllocator *t_alloc) +{ + alloc = t_alloc; +} + +/* + * Used to auto generate new fids. Start with arbitrary high value to avoid + * collision with hard coded fids in basic test code. + */ +static uint32_t fid_generator = 1000; + +static uint32_t genfid(void) +{ + return fid_generator++; +} + +/** + * Splits the @a in string by @a delim into individual (non empty) strings + * and outputs them to @a out. The output array @a out is NULL terminated. + * + * Output array @a out must be freed by calling split_free(). + * + * @returns number of individual elements in output array @a out (without the + * final NULL terminating element) + */ +static int split(const char *in, const char *delim, char ***out) +{ + int n = 0, i = 0; + char *tmp, *p; + + tmp = g_strdup(in); + for (p = strtok(tmp, delim); p != NULL; p = strtok(NULL, delim)) { + if (strlen(p) > 0) { + ++n; + } + } + g_free(tmp); + + *out = g_new0(char *, n + 1); /* last element NULL delimiter */ + + tmp = g_strdup(in); + for (p = strtok(tmp, delim); p != NULL; p = strtok(NULL, delim)) { + if (strlen(p) > 0) { + (*out)[i++] = g_strdup(p); + } + } + g_free(tmp); + + return n; +} + +static void split_free(char ***out) +{ + int i; + if (!*out) { + return; + } + for (i = 0; (*out)[i]; ++i) { + g_free((*out)[i]); + } + g_free(*out); + *out = NULL; +} + +void v9fs_memwrite(P9Req *req, const void *addr, size_t len) +{ + qtest_memwrite(req->qts, req->t_msg + req->t_off, addr, len); + req->t_off += len; +} + +void v9fs_memskip(P9Req *req, size_t len) +{ + req->r_off += len; +} + +void v9fs_memread(P9Req *req, void *addr, size_t len) +{ + qtest_memread(req->qts, req->r_msg + req->r_off, addr, len); + req->r_off += len; +} + +void v9fs_uint8_read(P9Req *req, uint8_t *val) +{ + v9fs_memread(req, val, 1); +} + +void v9fs_uint16_write(P9Req *req, uint16_t val) +{ + uint16_t le_val = cpu_to_le16(val); + + v9fs_memwrite(req, &le_val, 2); +} + +void v9fs_uint16_read(P9Req *req, uint16_t *val) +{ + v9fs_memread(req, val, 2); + le16_to_cpus(val); +} + +void v9fs_uint32_write(P9Req *req, uint32_t val) +{ + uint32_t le_val = cpu_to_le32(val); + + v9fs_memwrite(req, &le_val, 4); +} + +void v9fs_uint64_write(P9Req *req, uint64_t val) +{ + uint64_t le_val = cpu_to_le64(val); + + v9fs_memwrite(req, &le_val, 8); +} + +void v9fs_uint32_read(P9Req *req, uint32_t *val) +{ + v9fs_memread(req, val, 4); + le32_to_cpus(val); +} + +void v9fs_uint64_read(P9Req *req, uint64_t *val) +{ + v9fs_memread(req, val, 8); + le64_to_cpus(val); +} + +/* len[2] string[len] */ +uint16_t v9fs_string_size(const char *string) +{ + size_t len = strlen(string); + + g_assert_cmpint(len, <=, UINT16_MAX - 2); + + return 2 + len; +} + +void v9fs_string_write(P9Req *req, const char *string) +{ + int len = strlen(string); + + g_assert_cmpint(len, <=, UINT16_MAX); + + v9fs_uint16_write(req, (uint16_t) len); + v9fs_memwrite(req, string, len); +} + +void v9fs_string_read(P9Req *req, uint16_t *len, char **string) +{ + uint16_t local_len; + + v9fs_uint16_read(req, &local_len); + if (len) { + *len = local_len; + } + if (string) { + *string = g_malloc(local_len + 1); + v9fs_memread(req, *string, local_len); + (*string)[local_len] = 0; + } else { + v9fs_memskip(req, local_len); + } +} + +typedef struct { + uint32_t size; + uint8_t id; + uint16_t tag; +} QEMU_PACKED P9Hdr; + +P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id, + uint16_t tag) +{ + P9Req *req = g_new0(P9Req, 1); + uint32_t total_size = 7; /* 9P header has well-known size of 7 bytes */ + P9Hdr hdr = { + .id = id, + .tag = cpu_to_le16(tag) + }; + + g_assert_cmpint(total_size, <=, UINT32_MAX - size); + total_size += size; + hdr.size = cpu_to_le32(total_size); + + g_assert_cmpint(total_size, <=, P9_MAX_SIZE); + + req->qts = global_qtest; + req->v9p = v9p; + req->t_size = total_size; + req->t_msg = guest_alloc(alloc, req->t_size); + v9fs_memwrite(req, &hdr, 7); + req->tag = tag; + return req; +} + +void v9fs_req_send(P9Req *req) +{ + QVirtio9P *v9p = req->v9p; + + req->r_msg = guest_alloc(alloc, P9_MAX_SIZE); + req->free_head = qvirtqueue_add(req->qts, v9p->vq, req->t_msg, req->t_size, + false, true); + qvirtqueue_add(req->qts, v9p->vq, req->r_msg, P9_MAX_SIZE, true, false); + qvirtqueue_kick(req->qts, v9p->vdev, v9p->vq, req->free_head); + req->t_off = 0; +} + +static const char *rmessage_name(uint8_t id) +{ + return + id == P9_RLERROR ? "RLERROR" : + id == P9_RVERSION ? "RVERSION" : + id == P9_RATTACH ? "RATTACH" : + id == P9_RWALK ? "RWALK" : + id == P9_RLOPEN ? "RLOPEN" : + id == P9_RWRITE ? "RWRITE" : + id == P9_RMKDIR ? "RMKDIR" : + id == P9_RLCREATE ? "RLCREATE" : + id == P9_RSYMLINK ? "RSYMLINK" : + id == P9_RLINK ? "RLINK" : + id == P9_RUNLINKAT ? "RUNLINKAT" : + id == P9_RFLUSH ? "RFLUSH" : + id == P9_RREADDIR ? "READDIR" : + ""; +} + +void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len) +{ + QVirtio9P *v9p = req->v9p; + + qvirtio_wait_used_elem(req->qts, v9p->vdev, v9p->vq, req->free_head, len, + QVIRTIO_9P_TIMEOUT_US); +} + +void v9fs_req_recv(P9Req *req, uint8_t id) +{ + P9Hdr hdr; + + v9fs_memread(req, &hdr, 7); + hdr.size = ldl_le_p(&hdr.size); + hdr.tag = lduw_le_p(&hdr.tag); + + g_assert_cmpint(hdr.size, >=, 7); + g_assert_cmpint(hdr.size, <=, P9_MAX_SIZE); + g_assert_cmpint(hdr.tag, ==, req->tag); + + if (hdr.id != id) { + g_printerr("Received response %d (%s) instead of %d (%s)\n", + hdr.id, rmessage_name(hdr.id), id, rmessage_name(id)); + + if (hdr.id == P9_RLERROR) { + uint32_t err; + v9fs_uint32_read(req, &err); + g_printerr("Rlerror has errno %d (%s)\n", err, strerror(err)); + } + } + g_assert_cmpint(hdr.id, ==, id); +} + +void v9fs_req_free(P9Req *req) +{ + guest_free(alloc, req->t_msg); + guest_free(alloc, req->r_msg); + g_free(req); +} + +/* size[4] Rlerror tag[2] ecode[4] */ +void v9fs_rlerror(P9Req *req, uint32_t *err) +{ + v9fs_req_recv(req, P9_RLERROR); + v9fs_uint32_read(req, err); + v9fs_req_free(req); +} + +/* size[4] Tversion tag[2] msize[4] version[s] */ +TVersionRes v9fs_tversion(TVersionOpt opt) +{ + P9Req *req; + uint32_t err; + uint32_t body_size = 4; + uint16_t string_size; + uint16_t server_len; + g_autofree char *server_version = NULL; + + g_assert(opt.client); + + if (!opt.msize) { + opt.msize = P9_MAX_SIZE; + } + + if (!opt.tag) { + opt.tag = P9_NOTAG; + } + + if (!opt.version) { + opt.version = "9P2000.L"; + } + + string_size = v9fs_string_size(opt.version); + g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); + body_size += string_size; + req = v9fs_req_init(opt.client, body_size, P9_TVERSION, opt.tag); + + v9fs_uint32_write(req, opt.msize); + v9fs_string_write(req, opt.version); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rversion(req, &server_len, &server_version); + g_assert_cmpmem(server_version, server_len, + opt.version, strlen(opt.version)); + } + req = NULL; /* request was freed */ + } + + return (TVersionRes) { + .req = req, + }; +} + +/* size[4] Rversion tag[2] msize[4] version[s] */ +void v9fs_rversion(P9Req *req, uint16_t *len, char **version) +{ + uint32_t msize; + + v9fs_req_recv(req, P9_RVERSION); + v9fs_uint32_read(req, &msize); + + g_assert_cmpint(msize, ==, P9_MAX_SIZE); + + if (len || version) { + v9fs_string_read(req, len, version); + } + + v9fs_req_free(req); +} + +/* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] */ +TAttachRes v9fs_tattach(TAttachOpt opt) +{ + uint32_t err; + const char *uname = ""; /* ignored by QEMU */ + const char *aname = ""; /* ignored by QEMU */ + + g_assert(opt.client); + /* expecting either Rattach or Rlerror, but obviously not both */ + g_assert(!opt.expectErr || !opt.rattach.qid); + + if (!opt.requestOnly) { + v9fs_tversion((TVersionOpt) { .client = opt.client }); + } + + if (!opt.n_uname) { + opt.n_uname = getuid(); + } + + P9Req *req = v9fs_req_init(opt.client, 4 + 4 + 2 + 2 + 4, P9_TATTACH, + opt.tag); + + v9fs_uint32_write(req, opt.fid); + v9fs_uint32_write(req, P9_NOFID); + v9fs_string_write(req, uname); + v9fs_string_write(req, aname); + v9fs_uint32_write(req, opt.n_uname); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rattach(req, opt.rattach.qid); + } + req = NULL; /* request was freed */ + } + + return (TAttachRes) { + .req = req, + }; +} + +/* size[4] Rattach tag[2] qid[13] */ +void v9fs_rattach(P9Req *req, v9fs_qid *qid) +{ + v9fs_req_recv(req, P9_RATTACH); + if (qid) { + v9fs_memread(req, qid, 13); + } + v9fs_req_free(req); +} + +/* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) */ +TWalkRes v9fs_twalk(TWalkOpt opt) +{ + P9Req *req; + int i; + uint32_t body_size = 4 + 4 + 2; + uint32_t err; + char **wnames = NULL; + + g_assert(opt.client); + /* expecting either high- or low-level path, both not both */ + g_assert(!opt.path || !(opt.nwname || opt.wnames)); + /* expecting either Rwalk or Rlerror, but obviously not both */ + g_assert(!opt.expectErr || !(opt.rwalk.nwqid || opt.rwalk.wqid)); + + if (!opt.newfid) { + opt.newfid = genfid(); + } + + if (opt.path) { + opt.nwname = split(opt.path, "/", &wnames); + opt.wnames = wnames; + } + + for (i = 0; i < opt.nwname; i++) { + uint16_t wname_size = v9fs_string_size(opt.wnames[i]); + + g_assert_cmpint(body_size, <=, UINT32_MAX - wname_size); + body_size += wname_size; + } + req = v9fs_req_init(opt.client, body_size, P9_TWALK, opt.tag); + v9fs_uint32_write(req, opt.fid); + v9fs_uint32_write(req, opt.newfid); + v9fs_uint16_write(req, opt.nwname); + for (i = 0; i < opt.nwname; i++) { + v9fs_string_write(req, opt.wnames[i]); + } + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rwalk(req, opt.rwalk.nwqid, opt.rwalk.wqid); + } + req = NULL; /* request was freed */ + } + + split_free(&wnames); + + return (TWalkRes) { + .newfid = opt.newfid, + .req = req, + }; +} + +/* size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) */ +void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid) +{ + uint16_t local_nwqid; + + v9fs_req_recv(req, P9_RWALK); + v9fs_uint16_read(req, &local_nwqid); + if (nwqid) { + *nwqid = local_nwqid; + } + if (wqid) { + *wqid = g_malloc(local_nwqid * 13); + v9fs_memread(req, *wqid, local_nwqid * 13); + } + v9fs_req_free(req); +} + +/* size[4] Tgetattr tag[2] fid[4] request_mask[8] */ +TGetAttrRes v9fs_tgetattr(TGetAttrOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + /* expecting either Rgetattr or Rlerror, but obviously not both */ + g_assert(!opt.expectErr || !opt.rgetattr.attr); + + if (!opt.request_mask) { + opt.request_mask = P9_GETATTR_ALL; + } + + req = v9fs_req_init(opt.client, 4 + 8, P9_TGETATTR, opt.tag); + v9fs_uint32_write(req, opt.fid); + v9fs_uint64_write(req, opt.request_mask); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rgetattr(req, opt.rgetattr.attr); + } + req = NULL; /* request was freed */ + } + + return (TGetAttrRes) { .req = req }; +} + +/* + * size[4] Rgetattr tag[2] valid[8] qid[13] mode[4] uid[4] gid[4] nlink[8] + * rdev[8] size[8] blksize[8] blocks[8] + * atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8] + * ctime_sec[8] ctime_nsec[8] btime_sec[8] btime_nsec[8] + * gen[8] data_version[8] + */ +void v9fs_rgetattr(P9Req *req, v9fs_attr *attr) +{ + v9fs_req_recv(req, P9_RGETATTR); + + v9fs_uint64_read(req, &attr->valid); + v9fs_memread(req, &attr->qid, 13); + v9fs_uint32_read(req, &attr->mode); + v9fs_uint32_read(req, &attr->uid); + v9fs_uint32_read(req, &attr->gid); + v9fs_uint64_read(req, &attr->nlink); + v9fs_uint64_read(req, &attr->rdev); + v9fs_uint64_read(req, &attr->size); + v9fs_uint64_read(req, &attr->blksize); + v9fs_uint64_read(req, &attr->blocks); + v9fs_uint64_read(req, &attr->atime_sec); + v9fs_uint64_read(req, &attr->atime_nsec); + v9fs_uint64_read(req, &attr->mtime_sec); + v9fs_uint64_read(req, &attr->mtime_nsec); + v9fs_uint64_read(req, &attr->ctime_sec); + v9fs_uint64_read(req, &attr->ctime_nsec); + v9fs_uint64_read(req, &attr->btime_sec); + v9fs_uint64_read(req, &attr->btime_nsec); + v9fs_uint64_read(req, &attr->gen); + v9fs_uint64_read(req, &attr->data_version); + + v9fs_req_free(req); +} + +/* size[4] Treaddir tag[2] fid[4] offset[8] count[4] */ +TReadDirRes v9fs_treaddir(TReadDirOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + /* expecting either Rreaddir or Rlerror, but obviously not both */ + g_assert(!opt.expectErr || !(opt.rreaddir.count || + opt.rreaddir.nentries || opt.rreaddir.entries)); + + req = v9fs_req_init(opt.client, 4 + 8 + 4, P9_TREADDIR, opt.tag); + v9fs_uint32_write(req, opt.fid); + v9fs_uint64_write(req, opt.offset); + v9fs_uint32_write(req, opt.count); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rreaddir(req, opt.rreaddir.count, opt.rreaddir.nentries, + opt.rreaddir.entries); + } + req = NULL; /* request was freed */ + } + + return (TReadDirRes) { .req = req }; +} + +/* size[4] Rreaddir tag[2] count[4] data[count] */ +void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries, + struct V9fsDirent **entries) +{ + uint32_t local_count; + struct V9fsDirent *e = NULL; + /* only used to avoid a leak if entries was NULL */ + struct V9fsDirent *unused_entries = 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_new(struct V9fsDirent, 1); + if (entries) { + *entries = e; + } else { + unused_entries = e; + } + } else { + e = e->next = g_new(struct V9fsDirent, 1); + } + 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_free_dirents(unused_entries); + v9fs_req_free(req); +} + +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] */ +TLOpenRes v9fs_tlopen(TLOpenOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + /* expecting either Rlopen or Rlerror, but obviously not both */ + g_assert(!opt.expectErr || !(opt.rlopen.qid || opt.rlopen.iounit)); + + req = v9fs_req_init(opt.client, 4 + 4, P9_TLOPEN, opt.tag); + v9fs_uint32_write(req, opt.fid); + v9fs_uint32_write(req, opt.flags); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rlopen(req, opt.rlopen.qid, opt.rlopen.iounit); + } + req = NULL; /* request was freed */ + } + + return (TLOpenRes) { .req = req }; +} + +/* size[4] Rlopen tag[2] qid[13] iounit[4] */ +void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit) +{ + v9fs_req_recv(req, P9_RLOPEN); + if (qid) { + v9fs_memread(req, qid, 13); + } else { + v9fs_memskip(req, 13); + } + if (iounit) { + v9fs_uint32_read(req, iounit); + } + v9fs_req_free(req); +} + +/* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] */ +TWriteRes v9fs_twrite(TWriteOpt opt) +{ + P9Req *req; + uint32_t err; + uint32_t body_size = 4 + 8 + 4; + uint32_t written = 0; + + g_assert(opt.client); + + g_assert_cmpint(body_size, <=, UINT32_MAX - opt.count); + body_size += opt.count; + req = v9fs_req_init(opt.client, body_size, P9_TWRITE, opt.tag); + v9fs_uint32_write(req, opt.fid); + v9fs_uint64_write(req, opt.offset); + v9fs_uint32_write(req, opt.count); + v9fs_memwrite(req, opt.data, opt.count); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rwrite(req, &written); + } + req = NULL; /* request was freed */ + } + + return (TWriteRes) { + .req = req, + .count = written + }; +} + +/* size[4] Rwrite tag[2] count[4] */ +void v9fs_rwrite(P9Req *req, uint32_t *count) +{ + v9fs_req_recv(req, P9_RWRITE); + if (count) { + v9fs_uint32_read(req, count); + } + v9fs_req_free(req); +} + +/* size[4] Tflush tag[2] oldtag[2] */ +TFlushRes v9fs_tflush(TFlushOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + + req = v9fs_req_init(opt.client, 2, P9_TFLUSH, opt.tag); + v9fs_uint32_write(req, opt.oldtag); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rflush(req); + } + req = NULL; /* request was freed */ + } + + return (TFlushRes) { .req = req }; +} + +/* size[4] Rflush tag[2] */ +void v9fs_rflush(P9Req *req) +{ + v9fs_req_recv(req, P9_RFLUSH); + v9fs_req_free(req); +} + +/* size[4] Tmkdir tag[2] dfid[4] name[s] mode[4] gid[4] */ +TMkdirRes v9fs_tmkdir(TMkdirOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + /* expecting either hi-level atPath or low-level dfid, but not both */ + g_assert(!opt.atPath || !opt.dfid); + /* expecting either Rmkdir or Rlerror, but obviously not both */ + g_assert(!opt.expectErr || !opt.rmkdir.qid); + + if (opt.atPath) { + opt.dfid = v9fs_twalk((TWalkOpt) { .client = opt.client, + .path = opt.atPath }).newfid; + } + + if (!opt.mode) { + opt.mode = 0750; + } + + uint32_t body_size = 4 + 4 + 4; + uint16_t string_size = v9fs_string_size(opt.name); + + g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); + body_size += string_size; + + req = v9fs_req_init(opt.client, body_size, P9_TMKDIR, opt.tag); + v9fs_uint32_write(req, opt.dfid); + v9fs_string_write(req, opt.name); + v9fs_uint32_write(req, opt.mode); + v9fs_uint32_write(req, opt.gid); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rmkdir(req, opt.rmkdir.qid); + } + req = NULL; /* request was freed */ + } + + return (TMkdirRes) { .req = req }; +} + +/* size[4] Rmkdir tag[2] qid[13] */ +void v9fs_rmkdir(P9Req *req, v9fs_qid *qid) +{ + v9fs_req_recv(req, P9_RMKDIR); + if (qid) { + v9fs_memread(req, qid, 13); + } else { + v9fs_memskip(req, 13); + } + v9fs_req_free(req); +} + +/* size[4] Tlcreate tag[2] fid[4] name[s] flags[4] mode[4] gid[4] */ +TlcreateRes v9fs_tlcreate(TlcreateOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + /* expecting either hi-level atPath or low-level fid, but not both */ + g_assert(!opt.atPath || !opt.fid); + /* expecting either Rlcreate or Rlerror, but obviously not both */ + g_assert(!opt.expectErr || !(opt.rlcreate.qid || opt.rlcreate.iounit)); + + if (opt.atPath) { + opt.fid = v9fs_twalk((TWalkOpt) { .client = opt.client, + .path = opt.atPath }).newfid; + } + + if (!opt.mode) { + opt.mode = 0750; + } + + uint32_t body_size = 4 + 4 + 4 + 4; + uint16_t string_size = v9fs_string_size(opt.name); + + g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); + body_size += string_size; + + req = v9fs_req_init(opt.client, body_size, P9_TLCREATE, opt.tag); + v9fs_uint32_write(req, opt.fid); + v9fs_string_write(req, opt.name); + v9fs_uint32_write(req, opt.flags); + v9fs_uint32_write(req, opt.mode); + v9fs_uint32_write(req, opt.gid); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rlcreate(req, opt.rlcreate.qid, opt.rlcreate.iounit); + } + req = NULL; /* request was freed */ + } + + return (TlcreateRes) { .req = req }; +} + +/* size[4] Rlcreate tag[2] qid[13] iounit[4] */ +void v9fs_rlcreate(P9Req *req, v9fs_qid *qid, uint32_t *iounit) +{ + v9fs_req_recv(req, P9_RLCREATE); + if (qid) { + v9fs_memread(req, qid, 13); + } else { + v9fs_memskip(req, 13); + } + if (iounit) { + v9fs_uint32_read(req, iounit); + } + v9fs_req_free(req); +} + +/* size[4] Tsymlink tag[2] fid[4] name[s] symtgt[s] gid[4] */ +TsymlinkRes v9fs_tsymlink(TsymlinkOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + /* expecting either hi-level atPath or low-level fid, but not both */ + g_assert(!opt.atPath || !opt.fid); + /* expecting either Rsymlink or Rlerror, but obviously not both */ + g_assert(!opt.expectErr || !opt.rsymlink.qid); + + if (opt.atPath) { + opt.fid = v9fs_twalk((TWalkOpt) { .client = opt.client, + .path = opt.atPath }).newfid; + } + + uint32_t body_size = 4 + 4; + uint16_t string_size = v9fs_string_size(opt.name) + + v9fs_string_size(opt.symtgt); + + g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); + body_size += string_size; + + req = v9fs_req_init(opt.client, body_size, P9_TSYMLINK, opt.tag); + v9fs_uint32_write(req, opt.fid); + v9fs_string_write(req, opt.name); + v9fs_string_write(req, opt.symtgt); + v9fs_uint32_write(req, opt.gid); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rsymlink(req, opt.rsymlink.qid); + } + req = NULL; /* request was freed */ + } + + return (TsymlinkRes) { .req = req }; +} + +/* size[4] Rsymlink tag[2] qid[13] */ +void v9fs_rsymlink(P9Req *req, v9fs_qid *qid) +{ + v9fs_req_recv(req, P9_RSYMLINK); + if (qid) { + v9fs_memread(req, qid, 13); + } else { + v9fs_memskip(req, 13); + } + v9fs_req_free(req); +} + +/* size[4] Tlink tag[2] dfid[4] fid[4] name[s] */ +TlinkRes v9fs_tlink(TlinkOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + /* expecting either hi-level atPath or low-level dfid, but not both */ + g_assert(!opt.atPath || !opt.dfid); + /* expecting either hi-level toPath or low-level fid, but not both */ + g_assert(!opt.toPath || !opt.fid); + + if (opt.atPath) { + opt.dfid = v9fs_twalk((TWalkOpt) { .client = opt.client, + .path = opt.atPath }).newfid; + } + if (opt.toPath) { + opt.fid = v9fs_twalk((TWalkOpt) { .client = opt.client, + .path = opt.toPath }).newfid; + } + + uint32_t body_size = 4 + 4; + uint16_t string_size = v9fs_string_size(opt.name); + + g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); + body_size += string_size; + + req = v9fs_req_init(opt.client, body_size, P9_TLINK, opt.tag); + v9fs_uint32_write(req, opt.dfid); + v9fs_uint32_write(req, opt.fid); + v9fs_string_write(req, opt.name); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rlink(req); + } + req = NULL; /* request was freed */ + } + + return (TlinkRes) { .req = req }; +} + +/* size[4] Rlink tag[2] */ +void v9fs_rlink(P9Req *req) +{ + v9fs_req_recv(req, P9_RLINK); + v9fs_req_free(req); +} + +/* size[4] Tunlinkat tag[2] dirfd[4] name[s] flags[4] */ +TunlinkatRes v9fs_tunlinkat(TunlinkatOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + /* expecting either hi-level atPath or low-level dirfd, but not both */ + g_assert(!opt.atPath || !opt.dirfd); + + if (opt.atPath) { + opt.dirfd = v9fs_twalk((TWalkOpt) { .client = opt.client, + .path = opt.atPath }).newfid; + } + + uint32_t body_size = 4 + 4; + uint16_t string_size = v9fs_string_size(opt.name); + + g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); + body_size += string_size; + + req = v9fs_req_init(opt.client, body_size, P9_TUNLINKAT, opt.tag); + v9fs_uint32_write(req, opt.dirfd); + v9fs_string_write(req, opt.name); + v9fs_uint32_write(req, opt.flags); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_runlinkat(req); + } + req = NULL; /* request was freed */ + } + + return (TunlinkatRes) { .req = req }; +} + +/* size[4] Runlinkat tag[2] */ +void v9fs_runlinkat(P9Req *req) +{ + v9fs_req_recv(req, P9_RUNLINKAT); + v9fs_req_free(req); +} diff --git a/tests/qtest/libqos/virtio-9p-client.h b/tests/qtest/libqos/virtio-9p-client.h new file mode 100644 index 0000000000..78228eb97d --- /dev/null +++ b/tests/qtest/libqos/virtio-9p-client.h @@ -0,0 +1,494 @@ +/* + * 9P network client for VirtIO 9P test cases (based on QTest) + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * Not so fast! You might want to read the 9p developer docs first: + * https://wiki.qemu.org/Documentation/9p + */ + +#ifndef TESTS_LIBQOS_VIRTIO_9P_CLIENT_H +#define TESTS_LIBQOS_VIRTIO_9P_CLIENT_H + +#include "hw/9pfs/9p.h" +#include "hw/9pfs/9p-synth.h" +#include "virtio-9p.h" +#include "qgraph.h" +#include "tests/qtest/libqtest-single.h" + +#define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */ + +typedef struct { + QTestState *qts; + QVirtio9P *v9p; + uint16_t tag; + uint64_t t_msg; + uint32_t t_size; + uint64_t r_msg; + /* No r_size, it is hardcoded to P9_MAX_SIZE */ + size_t t_off; + size_t r_off; + uint32_t free_head; +} P9Req; + +/* type[1] version[4] path[8] */ +typedef char v9fs_qid[13]; + +typedef struct v9fs_attr { + uint64_t valid; + v9fs_qid qid; + uint32_t mode; + uint32_t uid; + uint32_t gid; + uint64_t nlink; + uint64_t rdev; + uint64_t size; + uint64_t blksize; + uint64_t blocks; + uint64_t atime_sec; + uint64_t atime_nsec; + uint64_t mtime_sec; + uint64_t mtime_nsec; + uint64_t ctime_sec; + uint64_t ctime_nsec; + uint64_t btime_sec; + uint64_t btime_nsec; + uint64_t gen; + uint64_t data_version; +} v9fs_attr; + +#define P9_GETATTR_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */ +#define P9_GETATTR_ALL 0x00003fffULL /* Mask for ALL fields */ + +struct V9fsDirent { + v9fs_qid qid; + uint64_t offset; + uint8_t type; + char *name; + struct V9fsDirent *next; +}; + +/* options for 'Twalk' 9p request */ +typedef struct TWalkOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID of directory from where walk should start (optional) */ + uint32_t fid; + /* file ID for target directory being walked to (optional) */ + uint32_t newfid; + /* low level variant of path to walk to (optional) */ + uint16_t nwname; + char **wnames; + /* high level variant of path to walk to (optional) */ + const char *path; + /* data being received from 9p server as 'Rwalk' response (optional) */ + struct { + uint16_t *nwqid; + v9fs_qid **wqid; + } rwalk; + /* only send Twalk request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TWalkOpt; + +/* result of 'Twalk' 9p request */ +typedef struct TWalkRes { + /* file ID of target directory been walked to */ + uint32_t newfid; + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TWalkRes; + +/* options for 'Tversion' 9p request */ +typedef struct TVersionOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* maximum message size that can be handled by client (optional) */ + uint32_t msize; + /* protocol version (optional) */ + const char *version; + /* only send Tversion request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TVersionOpt; + +/* result of 'Tversion' 9p request */ +typedef struct TVersionRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TVersionRes; + +/* options for 'Tattach' 9p request */ +typedef struct TAttachOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID to be associated with root of file tree (optional) */ + uint32_t fid; + /* numerical uid of user being introduced to server (optional) */ + uint32_t n_uname; + /* data being received from 9p server as 'Rattach' response (optional) */ + struct { + /* server's idea of the root of the file tree */ + v9fs_qid *qid; + } rattach; + /* only send Tattach request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TAttachOpt; + +/* result of 'Tattach' 9p request */ +typedef struct TAttachRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TAttachRes; + +/* options for 'Tgetattr' 9p request */ +typedef struct TGetAttrOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID of file/dir whose attributes shall be retrieved (required) */ + uint32_t fid; + /* bitmask indicating attribute fields to be retrieved (optional) */ + uint64_t request_mask; + /* data being received from 9p server as 'Rgetattr' response (optional) */ + struct { + v9fs_attr *attr; + } rgetattr; + /* only send Tgetattr request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TGetAttrOpt; + +/* result of 'Tgetattr' 9p request */ +typedef struct TGetAttrRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TGetAttrRes; + +/* options for 'Treaddir' 9p request */ +typedef struct TReadDirOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID of directory whose entries shall be retrieved (required) */ + uint32_t fid; + /* offset in entries stream, i.e. for multiple requests (optional) */ + uint64_t offset; + /* maximum bytes to be returned by server (required) */ + uint32_t count; + /* data being received from 9p server as 'Rreaddir' response (optional) */ + struct { + uint32_t *count; + uint32_t *nentries; + struct V9fsDirent **entries; + } rreaddir; + /* only send Treaddir request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TReadDirOpt; + +/* result of 'Treaddir' 9p request */ +typedef struct TReadDirRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TReadDirRes; + +/* options for 'Tlopen' 9p request */ +typedef struct TLOpenOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID of file / directory to be opened (required) */ + uint32_t fid; + /* Linux open(2) flags such as O_RDONLY, O_RDWR, O_WRONLY (optional) */ + uint32_t flags; + /* data being received from 9p server as 'Rlopen' response (optional) */ + struct { + v9fs_qid *qid; + uint32_t *iounit; + } rlopen; + /* only send Tlopen request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TLOpenOpt; + +/* result of 'Tlopen' 9p request */ +typedef struct TLOpenRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TLOpenRes; + +/* options for 'Twrite' 9p request */ +typedef struct TWriteOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID of file to write to (required) */ + uint32_t fid; + /* start position of write from beginning of file (optional) */ + uint64_t offset; + /* how many bytes to write */ + uint32_t count; + /* data to be written */ + const void *data; + /* only send Twrite request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TWriteOpt; + +/* result of 'Twrite' 9p request */ +typedef struct TWriteRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; + /* amount of bytes written */ + uint32_t count; +} TWriteRes; + +/* options for 'Tflush' 9p request */ +typedef struct TFlushOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* message to flush (required) */ + uint16_t oldtag; + /* only send Tflush request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TFlushOpt; + +/* result of 'Tflush' 9p request */ +typedef struct TFlushRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TFlushRes; + +/* options for 'Tmkdir' 9p request */ +typedef struct TMkdirOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* low level variant of directory where new one shall be created */ + uint32_t dfid; + /* high-level variant of directory where new one shall be created */ + const char *atPath; + /* New directory's name (required) */ + const char *name; + /* Linux mkdir(2) mode bits (optional) */ + uint32_t mode; + /* effective group ID of caller */ + uint32_t gid; + /* data being received from 9p server as 'Rmkdir' response (optional) */ + struct { + /* QID of newly created directory */ + v9fs_qid *qid; + } rmkdir; + /* only send Tmkdir request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TMkdirOpt; + +/* result of 'TMkdir' 9p request */ +typedef struct TMkdirRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TMkdirRes; + +/* options for 'Tlcreate' 9p request */ +typedef struct TlcreateOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* low-level variant of directory where new file shall be created */ + uint32_t fid; + /* high-level variant of directory where new file shall be created */ + const char *atPath; + /* name of new file (required) */ + const char *name; + /* Linux kernel intent bits */ + uint32_t flags; + /* Linux create(2) mode bits */ + uint32_t mode; + /* effective group ID of caller */ + uint32_t gid; + /* data being received from 9p server as 'Rlcreate' response (optional) */ + struct { + v9fs_qid *qid; + uint32_t *iounit; + } rlcreate; + /* only send Tlcreate request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TlcreateOpt; + +/* result of 'Tlcreate' 9p request */ +typedef struct TlcreateRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TlcreateRes; + +/* options for 'Tsymlink' 9p request */ +typedef struct TsymlinkOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* low-level variant of directory where symlink shall be created */ + uint32_t fid; + /* high-level variant of directory where symlink shall be created */ + const char *atPath; + /* name of symlink (required) */ + const char *name; + /* where symlink will point to (required) */ + const char *symtgt; + /* effective group ID of caller */ + uint32_t gid; + /* data being received from 9p server as 'Rsymlink' response (optional) */ + struct { + v9fs_qid *qid; + } rsymlink; + /* only send Tsymlink request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TsymlinkOpt; + +/* result of 'Tsymlink' 9p request */ +typedef struct TsymlinkRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TsymlinkRes; + +/* options for 'Tlink' 9p request */ +typedef struct TlinkOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* low-level variant of directory where hard link shall be created */ + uint32_t dfid; + /* high-level variant of directory where hard link shall be created */ + const char *atPath; + /* low-level variant of target referenced by new hard link */ + uint32_t fid; + /* high-level variant of target referenced by new hard link */ + const char *toPath; + /* name of hard link (required) */ + const char *name; + /* only send Tlink request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TlinkOpt; + +/* result of 'Tlink' 9p request */ +typedef struct TlinkRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TlinkRes; + +/* options for 'Tunlinkat' 9p request */ +typedef struct TunlinkatOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* low-level variant of directory where name shall be unlinked */ + uint32_t dirfd; + /* high-level variant of directory where name shall be unlinked */ + const char *atPath; + /* name of directory entry to be unlinked (required) */ + const char *name; + /* Linux unlinkat(2) flags */ + uint32_t flags; + /* only send Tunlinkat request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TunlinkatOpt; + +/* result of 'Tunlinkat' 9p request */ +typedef struct TunlinkatRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TunlinkatRes; + +void v9fs_set_allocator(QGuestAllocator *t_alloc); +void v9fs_memwrite(P9Req *req, const void *addr, size_t len); +void v9fs_memskip(P9Req *req, size_t len); +void v9fs_memread(P9Req *req, void *addr, size_t len); +void v9fs_uint8_read(P9Req *req, uint8_t *val); +void v9fs_uint16_write(P9Req *req, uint16_t val); +void v9fs_uint16_read(P9Req *req, uint16_t *val); +void v9fs_uint32_write(P9Req *req, uint32_t val); +void v9fs_uint64_write(P9Req *req, uint64_t val); +void v9fs_uint32_read(P9Req *req, uint32_t *val); +void v9fs_uint64_read(P9Req *req, uint64_t *val); +uint16_t v9fs_string_size(const char *string); +void v9fs_string_write(P9Req *req, const char *string); +void v9fs_string_read(P9Req *req, uint16_t *len, char **string); +P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id, + uint16_t tag); +void v9fs_req_send(P9Req *req); +void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len); +void v9fs_req_recv(P9Req *req, uint8_t id); +void v9fs_req_free(P9Req *req); +void v9fs_rlerror(P9Req *req, uint32_t *err); +TVersionRes v9fs_tversion(TVersionOpt); +void v9fs_rversion(P9Req *req, uint16_t *len, char **version); +TAttachRes v9fs_tattach(TAttachOpt); +void v9fs_rattach(P9Req *req, v9fs_qid *qid); +TWalkRes v9fs_twalk(TWalkOpt opt); +void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid); +TGetAttrRes v9fs_tgetattr(TGetAttrOpt); +void v9fs_rgetattr(P9Req *req, v9fs_attr *attr); +TReadDirRes v9fs_treaddir(TReadDirOpt); +void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries, + struct V9fsDirent **entries); +void v9fs_free_dirents(struct V9fsDirent *e); +TLOpenRes v9fs_tlopen(TLOpenOpt); +void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit); +TWriteRes v9fs_twrite(TWriteOpt); +void v9fs_rwrite(P9Req *req, uint32_t *count); +TFlushRes v9fs_tflush(TFlushOpt); +void v9fs_rflush(P9Req *req); +TMkdirRes v9fs_tmkdir(TMkdirOpt); +void v9fs_rmkdir(P9Req *req, v9fs_qid *qid); +TlcreateRes v9fs_tlcreate(TlcreateOpt); +void v9fs_rlcreate(P9Req *req, v9fs_qid *qid, uint32_t *iounit); +TsymlinkRes v9fs_tsymlink(TsymlinkOpt); +void v9fs_rsymlink(P9Req *req, v9fs_qid *qid); +TlinkRes v9fs_tlink(TlinkOpt); +void v9fs_rlink(P9Req *req); +TunlinkatRes v9fs_tunlinkat(TunlinkatOpt); +void v9fs_runlinkat(P9Req *req); + +#endif diff --git a/tests/qtest/libqos/virtio-9p.c b/tests/qtest/libqos/virtio-9p.c index 70aea8bf62..186fcc1141 100644 --- a/tests/qtest/libqos/virtio-9p.c +++ b/tests/qtest/libqos/virtio-9p.c @@ -31,7 +31,7 @@ static QGuestAllocator *alloc; static char *local_test_path; -/* Concatenates the passed 2 pathes. Returned result must be freed. */ +/* Concatenates the passed 2 paths. Returned result must be freed. */ static char *concat_path(const char* a, const char* b) { return g_build_filename(a, b, NULL); @@ -48,9 +48,9 @@ void virtio_9p_create_local_test_dir(void) */ char *template = concat_path(pwd, "qtest-9p-local-XXXXXX"); - local_test_path = mkdtemp(template); + local_test_path = g_mkdtemp(template); if (!local_test_path) { - g_test_message("mkdtemp('%s') failed: %s", template, strerror(errno)); + g_test_message("g_mkdtemp('%s') failed: %s", template, strerror(errno)); } g_assert(local_test_path != NULL); @@ -211,6 +211,7 @@ static void *virtio_9p_pci_create(void *pci_bus, QGuestAllocator *t_alloc, * variable arguments of this function to this * replacement string */ +G_GNUC_PRINTF(3, 4) static void regex_replace(GString *haystack, const char *pattern, const char *replace_fmt, ...) { diff --git a/tests/qtest/libqos/virtio-gpio.c b/tests/qtest/libqos/virtio-gpio.c new file mode 100644 index 0000000000..9220d287fe --- /dev/null +++ b/tests/qtest/libqos/virtio-gpio.c @@ -0,0 +1,172 @@ +/* + * virtio-gpio nodes for testing + * + * Copyright (c) 2022 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "standard-headers/linux/virtio_config.h" +#include "../libqtest.h" +#include "qemu/module.h" +#include "qgraph.h" +#include "virtio-gpio.h" + +static QGuestAllocator *alloc; + +static void virtio_gpio_cleanup(QVhostUserGPIO *gpio) +{ + QVirtioDevice *vdev = gpio->vdev; + int i; + + for (i = 0; i < 2; i++) { + qvirtqueue_cleanup(vdev->bus, gpio->queues[i], alloc); + } + g_free(gpio->queues); +} + +/* + * This handles the VirtIO setup from the point of view of the driver + * frontend and therefore doesn't present any vhost specific features + * and in fact masks of the re-used bit. + */ +static void virtio_gpio_setup(QVhostUserGPIO *gpio) +{ + QVirtioDevice *vdev = gpio->vdev; + uint64_t features; + int i; + + features = qvirtio_get_features(vdev); + features &= ~QVIRTIO_F_BAD_FEATURE; + qvirtio_set_features(vdev, features); + + gpio->queues = g_new(QVirtQueue *, 2); + for (i = 0; i < 2; i++) { + gpio->queues[i] = qvirtqueue_setup(vdev, alloc, i); + } + qvirtio_set_driver_ok(vdev); +} + +static void *qvirtio_gpio_get_driver(QVhostUserGPIO *v_gpio, + const char *interface) +{ + if (!g_strcmp0(interface, "vhost-user-gpio")) { + return v_gpio; + } + if (!g_strcmp0(interface, "virtio")) { + return v_gpio->vdev; + } + + g_assert_not_reached(); +} + +static void *qvirtio_gpio_device_get_driver(void *object, + const char *interface) +{ + QVhostUserGPIODevice *v_gpio = object; + return qvirtio_gpio_get_driver(&v_gpio->gpio, interface); +} + +/* virtio-gpio (mmio) */ +static void qvirtio_gpio_device_destructor(QOSGraphObject *obj) +{ + QVhostUserGPIODevice *gpio_dev = (QVhostUserGPIODevice *) obj; + virtio_gpio_cleanup(&gpio_dev->gpio); +} + +static void qvirtio_gpio_device_start_hw(QOSGraphObject *obj) +{ + QVhostUserGPIODevice *gpio_dev = (QVhostUserGPIODevice *) obj; + virtio_gpio_setup(&gpio_dev->gpio); +} + +static void *virtio_gpio_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVhostUserGPIODevice *virtio_device = g_new0(QVhostUserGPIODevice, 1); + QVhostUserGPIO *interface = &virtio_device->gpio; + + interface->vdev = virtio_dev; + alloc = t_alloc; + + virtio_device->obj.get_driver = qvirtio_gpio_device_get_driver; + virtio_device->obj.start_hw = qvirtio_gpio_device_start_hw; + virtio_device->obj.destructor = qvirtio_gpio_device_destructor; + + return &virtio_device->obj; +} + +/* virtio-gpio-pci */ +static void qvirtio_gpio_pci_destructor(QOSGraphObject *obj) +{ + QVhostUserGPIOPCI *gpio_pci = (QVhostUserGPIOPCI *) obj; + QOSGraphObject *pci_vobj = &gpio_pci->pci_vdev.obj; + + virtio_gpio_cleanup(&gpio_pci->gpio); + qvirtio_pci_destructor(pci_vobj); +} + +static void qvirtio_gpio_pci_start_hw(QOSGraphObject *obj) +{ + QVhostUserGPIOPCI *gpio_pci = (QVhostUserGPIOPCI *) obj; + QOSGraphObject *pci_vobj = &gpio_pci->pci_vdev.obj; + + qvirtio_pci_start_hw(pci_vobj); + virtio_gpio_setup(&gpio_pci->gpio); +} + +static void *qvirtio_gpio_pci_get_driver(void *object, const char *interface) +{ + QVhostUserGPIOPCI *v_gpio = object; + + if (!g_strcmp0(interface, "pci-device")) { + return v_gpio->pci_vdev.pdev; + } + return qvirtio_gpio_get_driver(&v_gpio->gpio, interface); +} + +static void *virtio_gpio_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVhostUserGPIOPCI *virtio_spci = g_new0(QVhostUserGPIOPCI, 1); + QVhostUserGPIO *interface = &virtio_spci->gpio; + QOSGraphObject *obj = &virtio_spci->pci_vdev.obj; + + virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr); + interface->vdev = &virtio_spci->pci_vdev.vdev; + alloc = t_alloc; + + obj->get_driver = qvirtio_gpio_pci_get_driver; + obj->start_hw = qvirtio_gpio_pci_start_hw; + obj->destructor = qvirtio_gpio_pci_destructor; + + return obj; +} + +static void virtio_gpio_register_nodes(void) +{ + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + }; + + QOSGraphEdgeOptions edge_opts = { }; + + /* vhost-user-gpio-device */ + edge_opts.extra_device_opts = "id=gpio0,chardev=chr-vhost-user-test " + "-global virtio-mmio.force-legacy=false"; + qos_node_create_driver("vhost-user-gpio-device", + virtio_gpio_device_create); + qos_node_consumes("vhost-user-gpio-device", "virtio-bus", &edge_opts); + qos_node_produces("vhost-user-gpio-device", "vhost-user-gpio"); + + /* virtio-gpio-pci */ + edge_opts.extra_device_opts = "id=gpio0,addr=04.0,chardev=chr-vhost-user-test"; + add_qpci_address(&edge_opts, &addr); + qos_node_create_driver("vhost-user-gpio-pci", virtio_gpio_pci_create); + qos_node_consumes("vhost-user-gpio-pci", "pci-bus", &edge_opts); + qos_node_produces("vhost-user-gpio-pci", "vhost-user-gpio"); +} + +libqos_init(virtio_gpio_register_nodes); diff --git a/tests/qtest/libqos/virtio-gpio.h b/tests/qtest/libqos/virtio-gpio.h new file mode 100644 index 0000000000..f11d41bd19 --- /dev/null +++ b/tests/qtest/libqos/virtio-gpio.h @@ -0,0 +1,35 @@ +/* + * virtio-gpio structures + * + * Copyright (c) 2022 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TESTS_LIBQOS_VIRTIO_GPIO_H +#define TESTS_LIBQOS_VIRTIO_GPIO_H + +#include "qgraph.h" +#include "virtio.h" +#include "virtio-pci.h" + +typedef struct QVhostUserGPIO QVhostUserGPIO; +typedef struct QVhostUserGPIOPCI QVhostUserGPIOPCI; +typedef struct QVhostUserGPIODevice QVhostUserGPIODevice; + +struct QVhostUserGPIO { + QVirtioDevice *vdev; + QVirtQueue **queues; +}; + +struct QVhostUserGPIOPCI { + QVirtioPCIDevice pci_vdev; + QVhostUserGPIO gpio; +}; + +struct QVhostUserGPIODevice { + QOSGraphObject obj; + QVhostUserGPIO gpio; +}; + +#endif diff --git a/tests/qtest/libqos/virtio-mmio.c b/tests/qtest/libqos/virtio-mmio.c index a6cca8613b..bd0b1d890b 100644 --- a/tests/qtest/libqos/virtio-mmio.c +++ b/tests/qtest/libqos/virtio-mmio.c @@ -12,7 +12,7 @@ #include "qemu/module.h" #include "virtio.h" #include "virtio-mmio.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "qgraph.h" #include "standard-headers/linux/virtio_ring.h" diff --git a/tests/qtest/libqos/virtio-pci.c b/tests/qtest/libqos/virtio-pci.c index 67c2498c84..485b8f6b7e 100644 --- a/tests/qtest/libqos/virtio-pci.c +++ b/tests/qtest/libqos/virtio-pci.c @@ -13,7 +13,7 @@ #include "virtio-pci.h" #include "pci.h" #include "pci-pc.h" -#include "malloc.h" +#include "libqos-malloc.h" #include "malloc-pc.h" #include "qgraph.h" #include "standard-headers/linux/virtio_ring.h" diff --git a/tests/qtest/libqos/virtio-scmi.c b/tests/qtest/libqos/virtio-scmi.c new file mode 100644 index 0000000000..ce8f4d5c06 --- /dev/null +++ b/tests/qtest/libqos/virtio-scmi.c @@ -0,0 +1,174 @@ +/* + * virtio-scmi nodes for testing + * + * SPDX-FileCopyrightText: Linaro Ltd + * SPDX-FileCopyrightText: Red Hat, Inc. + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Based on virtio-gpio.c, doing basically the same thing. + */ + +#include "qemu/osdep.h" +#include "standard-headers/linux/virtio_config.h" +#include "../libqtest.h" +#include "qemu/module.h" +#include "qgraph.h" +#include "virtio-scmi.h" + +static QGuestAllocator *alloc; + +static void virtio_scmi_cleanup(QVhostUserSCMI *scmi) +{ + QVirtioDevice *vdev = scmi->vdev; + int i; + + for (i = 0; i < 2; i++) { + qvirtqueue_cleanup(vdev->bus, scmi->queues[i], alloc); + } + g_free(scmi->queues); +} + +/* + * This handles the VirtIO setup from the point of view of the driver + * frontend and therefore doesn't present any vhost specific features + * and in fact masks of the re-used bit. + */ +static void virtio_scmi_setup(QVhostUserSCMI *scmi) +{ + QVirtioDevice *vdev = scmi->vdev; + uint64_t features; + int i; + + features = qvirtio_get_features(vdev); + features &= ~QVIRTIO_F_BAD_FEATURE; + qvirtio_set_features(vdev, features); + + scmi->queues = g_new(QVirtQueue *, 2); + for (i = 0; i < 2; i++) { + scmi->queues[i] = qvirtqueue_setup(vdev, alloc, i); + } + qvirtio_set_driver_ok(vdev); +} + +static void *qvirtio_scmi_get_driver(QVhostUserSCMI *v_scmi, + const char *interface) +{ + if (!g_strcmp0(interface, "vhost-user-scmi")) { + return v_scmi; + } + if (!g_strcmp0(interface, "virtio")) { + return v_scmi->vdev; + } + + g_assert_not_reached(); +} + +static void *qvirtio_scmi_device_get_driver(void *object, + const char *interface) +{ + QVhostUserSCMIDevice *v_scmi = object; + return qvirtio_scmi_get_driver(&v_scmi->scmi, interface); +} + +/* virtio-scmi (mmio) */ +static void qvirtio_scmi_device_destructor(QOSGraphObject *obj) +{ + QVhostUserSCMIDevice *scmi_dev = (QVhostUserSCMIDevice *) obj; + virtio_scmi_cleanup(&scmi_dev->scmi); +} + +static void qvirtio_scmi_device_start_hw(QOSGraphObject *obj) +{ + QVhostUserSCMIDevice *scmi_dev = (QVhostUserSCMIDevice *) obj; + virtio_scmi_setup(&scmi_dev->scmi); +} + +static void *virtio_scmi_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVhostUserSCMIDevice *virtio_device = g_new0(QVhostUserSCMIDevice, 1); + QVhostUserSCMI *interface = &virtio_device->scmi; + + interface->vdev = virtio_dev; + alloc = t_alloc; + + virtio_device->obj.get_driver = qvirtio_scmi_device_get_driver; + virtio_device->obj.start_hw = qvirtio_scmi_device_start_hw; + virtio_device->obj.destructor = qvirtio_scmi_device_destructor; + + return &virtio_device->obj; +} + +/* virtio-scmi-pci */ +static void qvirtio_scmi_pci_destructor(QOSGraphObject *obj) +{ + QVhostUserSCMIPCI *scmi_pci = (QVhostUserSCMIPCI *) obj; + QOSGraphObject *pci_vobj = &scmi_pci->pci_vdev.obj; + + virtio_scmi_cleanup(&scmi_pci->scmi); + qvirtio_pci_destructor(pci_vobj); +} + +static void qvirtio_scmi_pci_start_hw(QOSGraphObject *obj) +{ + QVhostUserSCMIPCI *scmi_pci = (QVhostUserSCMIPCI *) obj; + QOSGraphObject *pci_vobj = &scmi_pci->pci_vdev.obj; + + qvirtio_pci_start_hw(pci_vobj); + virtio_scmi_setup(&scmi_pci->scmi); +} + +static void *qvirtio_scmi_pci_get_driver(void *object, const char *interface) +{ + QVhostUserSCMIPCI *v_scmi = object; + + if (!g_strcmp0(interface, "pci-device")) { + return v_scmi->pci_vdev.pdev; + } + return qvirtio_scmi_get_driver(&v_scmi->scmi, interface); +} + +static void *virtio_scmi_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVhostUserSCMIPCI *virtio_spci = g_new0(QVhostUserSCMIPCI, 1); + QVhostUserSCMI *interface = &virtio_spci->scmi; + QOSGraphObject *obj = &virtio_spci->pci_vdev.obj; + + virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr); + interface->vdev = &virtio_spci->pci_vdev.vdev; + alloc = t_alloc; + + obj->get_driver = qvirtio_scmi_pci_get_driver; + obj->start_hw = qvirtio_scmi_pci_start_hw; + obj->destructor = qvirtio_scmi_pci_destructor; + + return obj; +} + +static void virtio_scmi_register_nodes(void) +{ + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + }; + + QOSGraphEdgeOptions edge_opts = { }; + + /* vhost-user-scmi-device */ + edge_opts.extra_device_opts = "id=scmi,chardev=chr-vhost-user-test " + "-global virtio-mmio.force-legacy=false"; + qos_node_create_driver("vhost-user-scmi-device", + virtio_scmi_device_create); + qos_node_consumes("vhost-user-scmi-device", "virtio-bus", &edge_opts); + qos_node_produces("vhost-user-scmi-device", "vhost-user-scmi"); + + /* virtio-scmi-pci */ + edge_opts.extra_device_opts = "id=scmi,addr=04.0,chardev=chr-vhost-user-test"; + add_qpci_address(&edge_opts, &addr); + qos_node_create_driver("vhost-user-scmi-pci", virtio_scmi_pci_create); + qos_node_consumes("vhost-user-scmi-pci", "pci-bus", &edge_opts); + qos_node_produces("vhost-user-scmi-pci", "vhost-user-scmi"); +} + +libqos_init(virtio_scmi_register_nodes); diff --git a/tests/qtest/libqos/virtio-scmi.h b/tests/qtest/libqos/virtio-scmi.h new file mode 100644 index 0000000000..cb5670da6e --- /dev/null +++ b/tests/qtest/libqos/virtio-scmi.h @@ -0,0 +1,34 @@ +/* + * virtio-scmi structures + * + * SPDX-FileCopyrightText: Red Hat, Inc. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TESTS_LIBQOS_VIRTIO_SCMI_H +#define TESTS_LIBQOS_VIRTIO_SCMI_H + +#include "qgraph.h" +#include "virtio.h" +#include "virtio-pci.h" + +typedef struct QVhostUserSCMI QVhostUserSCMI; +typedef struct QVhostUserSCMIPCI QVhostUserSCMIPCI; +typedef struct QVhostUserSCMIDevice QVhostUserSCMIDevice; + +struct QVhostUserSCMI { + QVirtioDevice *vdev; + QVirtQueue **queues; +}; + +struct QVhostUserSCMIPCI { + QVirtioPCIDevice pci_vdev; + QVhostUserSCMI scmi; +}; + +struct QVhostUserSCMIDevice { + QOSGraphObject obj; + QVhostUserSCMI scmi; +}; + +#endif diff --git a/tests/qtest/libqos/virtio.c b/tests/qtest/libqos/virtio.c index 09ec09b655..a21b6eee9c 100644 --- a/tests/qtest/libqos/virtio.c +++ b/tests/qtest/libqos/virtio.c @@ -101,6 +101,8 @@ uint64_t qvirtio_get_features(QVirtioDevice *d) void qvirtio_set_features(QVirtioDevice *d, uint64_t features) { + g_assert(!(features & QVIRTIO_F_BAD_FEATURE)); + d->features = features; d->bus->set_features(d, features); @@ -108,7 +110,7 @@ void qvirtio_set_features(QVirtioDevice *d, uint64_t features) * This could be a separate function for drivers that want to access * configuration space before setting FEATURES_OK, but no existing users * need that and it's less code for callers if this is done implicitly. - */ + */ if (features & (1ull << VIRTIO_F_VERSION_1)) { uint8_t status = d->bus->get_status(d) | VIRTIO_CONFIG_S_FEATURES_OK; @@ -263,7 +265,7 @@ void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq, /* vq->used->idx */ qvirtio_writew(vq->vdev, qts, vq->used + 2, 0); /* vq->used->avail_event */ - qvirtio_writew(vq->vdev, qts, vq->used + 2 + + qvirtio_writew(vq->vdev, qts, vq->used + 4 + sizeof(struct vring_used_elem) * vq->size, 0); } @@ -278,14 +280,27 @@ QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d, indirect->elem = elem; indirect->desc = guest_alloc(alloc, sizeof(struct vring_desc) * elem); - for (i = 0; i < elem - 1; ++i) { + for (i = 0; i < elem; ++i) { /* indirect->desc[i].addr */ qvirtio_writeq(d, qs, indirect->desc + (16 * i), 0); - /* indirect->desc[i].flags */ - qvirtio_writew(d, qs, indirect->desc + (16 * i) + 12, - VRING_DESC_F_NEXT); - /* indirect->desc[i].next */ - qvirtio_writew(d, qs, indirect->desc + (16 * i) + 14, i + 1); + + /* + * If it's not the last element of the ring, set + * the chain (VRING_DESC_F_NEXT) flag and + * desc->next. Clear the last element - there's + * no guarantee that guest_alloc() will do it. + */ + if (i != elem - 1) { + /* indirect->desc[i].flags */ + qvirtio_writew(d, qs, indirect->desc + (16 * i) + 12, + VRING_DESC_F_NEXT); + + /* indirect->desc[i].next */ + qvirtio_writew(d, qs, indirect->desc + (16 * i) + 14, i + 1); + } else { + qvirtio_writew(d, qs, indirect->desc + (16 * i) + 12, 0); + qvirtio_writew(d, qs, indirect->desc + (16 * i) + 14, 0); + } } return indirect; @@ -379,7 +394,7 @@ void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq, qvirtio_writew(d, qts, vq->avail + 2, idx + 1); /* Must read after idx is updated */ - flags = qvirtio_readw(d, qts, vq->avail); + flags = qvirtio_readw(d, qts, vq->used); avail_event = qvirtio_readw(d, qts, vq->used + 4 + sizeof(struct vring_used_elem) * vq->size); diff --git a/tests/qtest/libqos/virtio.h b/tests/qtest/libqos/virtio.h index b8bd06e1b8..7adc7cbd10 100644 --- a/tests/qtest/libqos/virtio.h +++ b/tests/qtest/libqos/virtio.h @@ -10,7 +10,7 @@ #ifndef LIBQOS_VIRTIO_H #define LIBQOS_VIRTIO_H -#include "malloc.h" +#include "libqos-malloc.h" #include "standard-headers/linux/virtio_ring.h" #define QVIRTIO_F_BAD_FEATURE 0x40000000ull diff --git a/tests/qtest/libqtest-single.h b/tests/qtest/libqtest-single.h index 4e7d0ae1dc..851724cbcb 100644 --- a/tests/qtest/libqtest-single.h +++ b/tests/qtest/libqtest-single.h @@ -13,7 +13,11 @@ #include "libqtest.h" +#ifndef _WIN32 QTestState *global_qtest __attribute__((common, weak)); +#else +__declspec(selectany) QTestState *global_qtest; +#endif /** * qtest_start: diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 8c159eacf5..d8f80d335e 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -16,24 +16,43 @@ #include "qemu/osdep.h" +#ifndef _WIN32 #include #include #include +#endif /* _WIN32 */ #ifdef __linux__ #include #endif /* __linux__ */ +#ifdef __FreeBSD__ +#include +#endif /* __FreeBSD__ */ #include "libqtest.h" #include "libqmp.h" #include "qemu/ctype.h" #include "qemu/cutils.h" +#include "qemu/sockets.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qlist.h" #include "qapi/qmp/qstring.h" #define MAX_IRQ 256 -#define SOCKET_TIMEOUT 50 + +#ifndef _WIN32 +# define SOCKET_TIMEOUT 50 +# define CMD_EXEC "exec " +# define DEV_STDERR "/dev/fd/2" +# define DEV_NULL "/dev/null" +#else +# define SOCKET_TIMEOUT 50000 +# define CMD_EXEC "" +# define DEV_STDERR "2" +# define DEV_NULL "nul" +#endif + +#define WAITPID_TIMEOUT 30 typedef void (*QTestSendFn)(QTestState *s, const char *buf); typedef void (*ExternalSendFn)(void *s, const char *buf); @@ -57,16 +76,22 @@ struct QTestState int qmp_fd; pid_t qemu_pid; /* our child QEMU process */ int wstatus; +#ifdef _WIN32 + DWORD exit_code; +#endif int expected_status; bool big_endian; bool irq_level[MAX_IRQ]; GString *rx; QTestTransportOps ops; GList *pending_events; + QTestQMPEventCallback eventCB; + void *eventData; }; static GHookList abrt_hooks; -static struct sigaction sigact_old; +static void (*sighandler_old)(int); +static bool silence_spawn_log; static int qtest_query_target_endianness(QTestState *s); @@ -90,8 +115,16 @@ static int socket_accept(int sock) struct sockaddr_un addr; socklen_t addrlen; int ret; + /* + * timeout unit of blocking receive calls is different among platforms. + * It's in seconds on non-Windows platforms but milliseconds on Windows. + */ +#ifndef _WIN32 struct timeval timeout = { .tv_sec = SOCKET_TIMEOUT, .tv_usec = 0 }; +#else + DWORD timeout = SOCKET_TIMEOUT; +#endif if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout, sizeof(timeout))) { @@ -113,16 +146,30 @@ static int socket_accept(int sock) return ret; } +pid_t qtest_pid(QTestState *s) +{ + return s->qemu_pid; +} + bool qtest_probe_child(QTestState *s) { pid_t pid = s->qemu_pid; if (pid != -1) { +#ifndef _WIN32 pid = waitpid(pid, &s->wstatus, WNOHANG); if (pid == 0) { return true; } +#else + GetExitCodeProcess((HANDLE)pid, &s->exit_code); + if (s->exit_code == STILL_ACTIVE) { + return true; + } + CloseHandle((HANDLE)pid); +#endif s->qemu_pid = -1; + qtest_remove_abrt_handler(s); } return false; } @@ -132,24 +179,16 @@ void qtest_set_expected_status(QTestState *s, int status) s->expected_status = status; } -void qtest_kill_qemu(QTestState *s) +static void qtest_check_status(QTestState *s) { - pid_t pid = s->qemu_pid; - int wstatus; - - /* Skip wait if qtest_probe_child already reaped. */ - if (pid != -1) { - kill(pid, SIGTERM); - TFR(pid = waitpid(s->qemu_pid, &s->wstatus, 0)); - assert(pid == s->qemu_pid); - s->qemu_pid = -1; - } + assert(s->qemu_pid == -1); /* * Check whether qemu exited with expected exit status; anything else is * fishy and should be logged with as much detail as possible. */ - wstatus = s->wstatus; +#ifndef _WIN32 + int wstatus = s->wstatus; if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != s->expected_status) { fprintf(stderr, "%s:%d: kill_qemu() tried to terminate QEMU " "process but encountered exit status %d (expected %d)\n", @@ -165,6 +204,69 @@ void qtest_kill_qemu(QTestState *s) __FILE__, __LINE__, sig, signame, dump); abort(); } +#else + if (s->exit_code != s->expected_status) { + fprintf(stderr, "%s:%d: kill_qemu() tried to terminate QEMU " + "process but encountered exit status %ld (expected %d)\n", + __FILE__, __LINE__, s->exit_code, s->expected_status); + abort(); + } +#endif +} + +void qtest_wait_qemu(QTestState *s) +{ + if (s->qemu_pid != -1) { +#ifndef _WIN32 + pid_t pid; + uint64_t end; + + /* poll for a while until sending SIGKILL */ + end = g_get_monotonic_time() + WAITPID_TIMEOUT * G_TIME_SPAN_SECOND; + + do { + pid = waitpid(s->qemu_pid, &s->wstatus, WNOHANG); + if (pid != 0) { + break; + } + g_usleep(100 * 1000); + } while (g_get_monotonic_time() < end); + + if (pid == 0) { + kill(s->qemu_pid, SIGKILL); + pid = RETRY_ON_EINTR(waitpid(s->qemu_pid, &s->wstatus, 0)); + } + + assert(pid == s->qemu_pid); +#else + DWORD ret; + + ret = WaitForSingleObject((HANDLE)s->qemu_pid, INFINITE); + assert(ret == WAIT_OBJECT_0); + GetExitCodeProcess((HANDLE)s->qemu_pid, &s->exit_code); + CloseHandle((HANDLE)s->qemu_pid); +#endif + + s->qemu_pid = -1; + qtest_remove_abrt_handler(s); + } + qtest_check_status(s); +} + +void qtest_kill_qemu(QTestState *s) +{ + /* Skip wait if qtest_probe_child() already reaped */ + if (s->qemu_pid != -1) { +#ifndef _WIN32 + kill(s->qemu_pid, SIGTERM); +#else + TerminateProcess((HANDLE)s->qemu_pid, s->expected_status); +#endif + qtest_wait_qemu(s); + return; + } + + qtest_check_status(s); } static void kill_qemu_hook_func(void *s) @@ -179,20 +281,12 @@ static void sigabrt_handler(int signo) static void setup_sigabrt_handler(void) { - struct sigaction sigact; - - /* Catch SIGABRT to clean up on g_assert() failure */ - sigact = (struct sigaction){ - .sa_handler = sigabrt_handler, - .sa_flags = SA_RESETHAND, - }; - sigemptyset(&sigact.sa_mask); - sigaction(SIGABRT, &sigact, &sigact_old); + sighandler_old = signal(SIGABRT, sigabrt_handler); } static void cleanup_sigabrt_handler(void) { - sigaction(SIGABRT, &sigact_old, NULL); + signal(SIGABRT, sighandler_old); } static bool hook_list_is_empty(GHookList *hook_list) @@ -230,6 +324,11 @@ void qtest_add_abrt_handler(GHookFunc fn, const void *data) void qtest_remove_abrt_handler(void *data) { GHook *hook = g_hook_find_data(&abrt_hooks, TRUE, data); + + if (!hook) { + return; + } + g_hook_destroy_link(&abrt_hooks, hook); /* Uninstall SIGABRT handler on last instance */ @@ -238,10 +337,17 @@ void qtest_remove_abrt_handler(void *data) } } -static const char *qtest_qemu_binary(void) +static const char *qtest_qemu_binary(const char *var) { const char *qemu_bin; + if (var) { + qemu_bin = getenv(var); + if (qemu_bin) { + return qemu_bin; + } + } + qemu_bin = getenv("QTEST_QEMU_BINARY"); if (!qemu_bin) { fprintf(stderr, "Environment variable QTEST_QEMU_BINARY required\n"); @@ -251,57 +357,60 @@ static const char *qtest_qemu_binary(void) return qemu_bin; } -QTestState *qtest_init_without_qmp_handshake(const char *extra_args) +#ifdef _WIN32 +static pid_t qtest_create_process(char *cmd) { - QTestState *s; - int sock, qmpsock, i; - gchar *socket_path; - gchar *qmp_socket_path; - gchar *command; - const char *qemu_binary = qtest_qemu_binary(); + STARTUPINFO si; + PROCESS_INFORMATION pi; + BOOL ret; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + ret = CreateProcess(NULL, /* module name */ + cmd, /* command line */ + NULL, /* process handle not inheritable */ + NULL, /* thread handle not inheritable */ + FALSE, /* set handle inheritance to FALSE */ + 0, /* No creation flags */ + NULL, /* use parent's environment block */ + NULL, /* use parent's starting directory */ + &si, /* pointer to STARTUPINFO structure */ + &pi /* pointer to PROCESS_INFORMATION structure */ + ); + if (ret == 0) { + fprintf(stderr, "%s:%d: unable to create a new process (%s)\n", + __FILE__, __LINE__, strerror(GetLastError())); + abort(); + } + + return (pid_t)pi.hProcess; +} +#endif /* _WIN32 */ + +static QTestState *G_GNUC_PRINTF(2, 3) qtest_spawn_qemu(const char *qemu_bin, + const char *fmt, ...) +{ + va_list ap; + QTestState *s = g_new0(QTestState, 1); const char *trace = g_getenv("QTEST_TRACE"); g_autofree char *tracearg = trace ? g_strdup_printf("-trace %s ", trace) : g_strdup(""); + g_autoptr(GString) command = g_string_new(""); - s = g_new(QTestState, 1); - - socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid()); - qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid()); - - /* It's possible that if an earlier test run crashed it might - * have left a stale unix socket lying around. Delete any - * stale old socket to avoid spurious test failures with - * tests/libqtest.c:70:init_socket: assertion failed (ret != -1): (-1 != -1) - */ - unlink(socket_path); - unlink(qmp_socket_path); - - 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); + va_start(ap, fmt); + g_string_append_printf(command, CMD_EXEC "%s %s", qemu_bin, tracearg); + g_string_append_vprintf(command, fmt, ap); + va_end(ap); qtest_add_abrt_handler(kill_qemu_hook_func, s); - command = g_strdup_printf("exec %s %s" - "-qtest unix:%s " - "-qtest-log %s " - "-chardev socket,path=%s,id=char0 " - "-mon chardev=char0,mode=control " - "-display none " - "%s" - " -accel qtest", - qemu_binary, tracearg, socket_path, - getenv("QTEST_LOG") ? "/dev/fd/2" : "/dev/null", - qmp_socket_path, - extra_args ?: ""); + if (!silence_spawn_log) { + g_test_message("starting QEMU: %s", command->str); + } - g_test_message("starting QEMU: %s", command); - - s->pending_events = NULL; - s->wstatus = 0; - s->expected_status = 0; +#ifndef _WIN32 s->qemu_pid = fork(); if (s->qemu_pid == 0) { #ifdef __linux__ @@ -318,14 +427,63 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) */ prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); #endif /* __linux__ */ - if (!g_setenv("QEMU_AUDIO_DRV", "none", true)) { - exit(1); - } - execlp("/bin/sh", "sh", "-c", command, NULL); +#ifdef __FreeBSD__ + int sig = SIGKILL; + procctl(P_PID, getpid(), PROC_PDEATHSIG_CTL, &sig); +#endif /* __FreeBSD__ */ + execlp("/bin/sh", "sh", "-c", command->str, NULL); exit(1); } +#else + s->qemu_pid = qtest_create_process(command->str); +#endif /* _WIN32 */ + + return s; +} + +static QTestState *qtest_init_internal(const char *qemu_bin, + const char *extra_args) +{ + QTestState *s; + int sock, qmpsock, i; + gchar *socket_path; + gchar *qmp_socket_path; + + socket_path = g_strdup_printf("%s/qtest-%d.sock", + g_get_tmp_dir(), getpid()); + qmp_socket_path = g_strdup_printf("%s/qtest-%d.qmp", + g_get_tmp_dir(), getpid()); + + /* + * It's possible that if an earlier test run crashed it might + * have left a stale unix socket lying around. Delete any + * stale old socket to avoid spurious test failures with + * tests/libqtest.c:70:init_socket: assertion failed (ret != -1): (-1 != -1) + */ + unlink(socket_path); + unlink(qmp_socket_path); + + socket_init(); + sock = init_socket(socket_path); + qmpsock = init_socket(qmp_socket_path); + + s = qtest_spawn_qemu(qemu_bin, + "-qtest unix:%s " + "-qtest-log %s " + "-chardev socket,path=%s,id=char0 " + "-mon chardev=char0,mode=control " + "-display none " + "-audio none " + "%s" + " -accel qtest", + socket_path, + getenv("QTEST_LOG") ? DEV_STDERR : DEV_NULL, + qmp_socket_path, + extra_args ?: ""); + + qtest_client_set_rx_handler(s, qtest_client_socket_recv_line); + qtest_client_set_tx_handler(s, qtest_client_socket_send); - g_free(command); s->fd = socket_accept(sock); if (s->fd >= 0) { s->qmp_fd = socket_accept(qmpsock); @@ -342,9 +500,19 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) s->irq_level[i] = false; } + /* + * Stopping QEMU for debugging is not supported on Windows. + * + * Using DebugActiveProcess() API can suspend the QEMU process, + * but gdb cannot attach to the process. Using the undocumented + * NtSuspendProcess() can suspend the QEMU process and gdb can + * attach to the process, but gdb cannot resume it. + */ +#ifndef _WIN32 if (getenv("QTEST_STOP")) { kill(s->qemu_pid, SIGSTOP); } +#endif /* ask endianness of the target */ @@ -353,9 +521,14 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) return s; } -QTestState *qtest_init(const char *extra_args) +QTestState *qtest_init_without_qmp_handshake(const char *extra_args) { - QTestState *s = qtest_init_without_qmp_handshake(extra_args); + return qtest_init_internal(qtest_qemu_binary(NULL), extra_args); +} + +QTestState *qtest_init_with_env(const char *var, const char *extra_args) +{ + QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args); QDict *greeting; /* Read the QMP greeting and then do the handshake */ @@ -366,6 +539,11 @@ QTestState *qtest_init(const char *extra_args) return s; } +QTestState *qtest_init(const char *extra_args) +{ + return qtest_init_with_env(NULL, extra_args); +} + QTestState *qtest_vinitf(const char *fmt, va_list ap) { char *args = g_strdup_vprintf(fmt, ap); @@ -390,12 +568,15 @@ QTestState *qtest_initf(const char *fmt, ...) QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd) { int sock_fd_init; - char *sock_path, sock_dir[] = "/tmp/qtest-serial-XXXXXX"; + g_autofree char *sock_dir = NULL; + char *sock_path; QTestState *qts; - g_assert_true(mkdtemp(sock_dir) != NULL); + sock_dir = g_dir_make_tmp("qtest-serial-XXXXXX", NULL); + g_assert_true(sock_dir != NULL); sock_path = g_strdup_printf("%s/sock", sock_dir); + socket_init(); sock_fd_init = init_socket(sock_path); qts = qtest_initf("-chardev socket,id=s0,path=%s -serial chardev:s0 %s", @@ -432,7 +613,7 @@ void qtest_quit(QTestState *s) static void socket_send(int fd, const char *buf, size_t size) { - size_t res = qemu_write_full(fd, buf, size); + ssize_t res = qemu_send_full(fd, buf, size); assert(res == size); } @@ -464,7 +645,7 @@ static GString *qtest_client_socket_recv_line(QTestState *s) ssize_t len; char buffer[1024]; - len = read(s->fd, buffer, sizeof(buffer)); + len = recv(s->fd, buffer, sizeof(buffer), 0); if (len == -1 && errno == EINTR) { continue; } @@ -556,8 +737,13 @@ QDict *qtest_qmp_receive(QTestState *s) if (!qdict_get_try_str(response, "event")) { return response; } - /* Stash the event for a later consumption */ - s->pending_events = g_list_append(s->pending_events, response); + + if (!s->eventCB || + !s->eventCB(s, qdict_get_str(response, "event"), + response, s->eventData)) { + /* Stash the event for a later consumption */ + s->pending_events = g_list_append(s->pending_events, response); + } } } @@ -578,9 +764,7 @@ int qtest_socket_server(const char *socket_path) addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path); - do { - ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); - } while (ret == -1 && errno == EINTR); + ret = RETRY_ON_EINTR(bind(sock, (struct sockaddr *)&addr, sizeof(addr))); g_assert_cmpint(ret, !=, -1); ret = listen(sock, 1); g_assert_cmpint(ret, !=, -1); @@ -588,17 +772,20 @@ int qtest_socket_server(const char *socket_path) return sock; } +#ifndef _WIN32 void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, va_list ap) { qmp_fd_vsend_fds(s->qmp_fd, fds, fds_num, fmt, ap); } +#endif void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap) { - qmp_fd_vsend_fds(s->qmp_fd, NULL, 0, fmt, ap); + qmp_fd_vsend(s->qmp_fd, fmt, ap); } +#ifndef _WIN32 QDict *qtest_vqmp_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, va_list ap) { @@ -607,6 +794,7 @@ QDict *qtest_vqmp_fds(QTestState *s, int *fds, size_t fds_num, /* Receive reply */ return qtest_qmp_receive(s); } +#endif QDict *qtest_vqmp(QTestState *s, const char *fmt, va_list ap) { @@ -616,6 +804,7 @@ QDict *qtest_vqmp(QTestState *s, const char *fmt, va_list ap) return qtest_qmp_receive(s); } +#ifndef _WIN32 QDict *qtest_qmp_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, ...) { @@ -627,6 +816,7 @@ QDict *qtest_qmp_fds(QTestState *s, int *fds, size_t fds_num, va_end(ap); return response; } +#endif QDict *qtest_qmp(QTestState *s, const char *fmt, ...) { @@ -657,6 +847,13 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) va_end(ap); } +void qtest_qmp_set_event_callback(QTestState *s, + QTestQMPEventCallback cb, void *opaque) +{ + s->eventCB = cb; + s->eventData = opaque; +} + QDict *qtest_qmp_event_ref(QTestState *s, const char *event) { while (s->pending_events) { @@ -730,7 +927,7 @@ char *qtest_hmp(QTestState *s, const char *fmt, ...) const char *qtest_get_arch(void) { - const char *qemu = qtest_qemu_binary(); + const char *qemu = qtest_qemu_binary(NULL); const char *end = strrchr(qemu, '-'); if (!end) { @@ -823,6 +1020,12 @@ void qtest_irq_intercept_out(QTestState *s, const char *qom_path) qtest_rsp(s); } +void qtest_irq_intercept_out_named(QTestState *s, const char *qom_path, const char *name) +{ + qtest_sendf(s, "irq_intercept_out %s %s\n", qom_path, name); + qtest_rsp(s); +} + void qtest_irq_intercept_in(QTestState *s, const char *qom_path) { qtest_sendf(s, "irq_intercept_in %s\n", qom_path); @@ -1078,25 +1281,141 @@ void qtest_memset(QTestState *s, uint64_t addr, uint8_t pattern, size_t size) qtest_rsp(s); } -void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) +QDict *qtest_vqmp_assert_failure_ref(QTestState *qts, + const char *fmt, va_list args) { - va_list ap; QDict *response; + QDict *ret; - va_start(ap, fmt); - response = qtest_vqmp(qts, fmt, ap); - va_end(ap); + response = qtest_vqmp(qts, fmt, args); + + g_assert(response); + if (!qdict_haskey(response, "error")) { + g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(response), true); + g_test_message("%s", s->str); + } + g_assert(qdict_haskey(response, "error")); + g_assert(!qdict_haskey(response, "return")); + ret = qdict_get_qdict(response, "error"); + qobject_ref(ret); + qobject_unref(response); + + return ret; +} + +QDict *qtest_vqmp_assert_success_ref(QTestState *qts, + const char *fmt, va_list args) +{ + QDict *response; + QDict *ret; + + response = qtest_vqmp(qts, fmt, args); g_assert(response); if (!qdict_haskey(response, "return")) { - GString *s = qobject_to_json_pretty(QOBJECT(response), true); + g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(response), true); g_test_message("%s", s->str); - g_string_free(s, true); } g_assert(qdict_haskey(response, "return")); + ret = qdict_get_qdict(response, "return"); + qobject_ref(ret); + qobject_unref(response); + + return ret; +} + +void qtest_vqmp_assert_success(QTestState *qts, + const char *fmt, va_list args) +{ + QDict *response; + + response = qtest_vqmp_assert_success_ref(qts, fmt, args); + qobject_unref(response); } +#ifndef _WIN32 +QDict *qtest_vqmp_fds_assert_success_ref(QTestState *qts, int *fds, size_t nfds, + const char *fmt, va_list args) +{ + QDict *response; + QDict *ret; + + response = qtest_vqmp_fds(qts, fds, nfds, fmt, args); + + g_assert(response); + if (!qdict_haskey(response, "return")) { + g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(response), true); + g_test_message("%s", s->str); + } + g_assert(qdict_haskey(response, "return")); + ret = qdict_get_qdict(response, "return"); + qobject_ref(ret); + qobject_unref(response); + + return ret; +} + +void qtest_vqmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds, + const char *fmt, va_list args) +{ + QDict *response; + response = qtest_vqmp_fds_assert_success_ref(qts, fds, nfds, fmt, args); + qobject_unref(response); +} +#endif /* !_WIN32 */ + +QDict *qtest_qmp_assert_failure_ref(QTestState *qts, const char *fmt, ...) +{ + QDict *response; + va_list ap; + + va_start(ap, fmt); + response = qtest_vqmp_assert_failure_ref(qts, fmt, ap); + va_end(ap); + return response; +} + +QDict *qtest_qmp_assert_success_ref(QTestState *qts, const char *fmt, ...) +{ + QDict *response; + va_list ap; + va_start(ap, fmt); + response = qtest_vqmp_assert_success_ref(qts, fmt, ap); + va_end(ap); + return response; +} + +void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + qtest_vqmp_assert_success(qts, fmt, ap); + va_end(ap); +} + +#ifndef _WIN32 +QDict *qtest_qmp_fds_assert_success_ref(QTestState *qts, int *fds, size_t nfds, + const char *fmt, ...) +{ + QDict *response; + va_list ap; + va_start(ap, fmt); + response = qtest_vqmp_fds_assert_success_ref(qts, fds, nfds, fmt, ap); + va_end(ap); + return response; +} + +void qtest_qmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds, + const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + qtest_vqmp_fds_assert_success(qts, fds, nfds, fmt, ap); + va_end(ap); +} +#endif /* !_WIN32 */ + bool qtest_big_endian(QTestState *s) { return s->big_endian; @@ -1152,13 +1471,26 @@ struct MachInfo { char *alias; }; +static void qtest_free_machine_list(struct MachInfo *machines) +{ + if (machines) { + for (int i = 0; machines[i].name != NULL; i++) { + g_free(machines[i].name); + g_free(machines[i].alias); + } + + g_free(machines); + } +} + /* * Returns an array with pointers to the available machine names. * The terminating entry has the name set to NULL. */ -static struct MachInfo *qtest_get_machines(void) +static struct MachInfo *qtest_get_machines(const char *var) { static struct MachInfo *machines; + static char *qemu_var; QDict *response, *minfo; QList *list; const QListEntry *p; @@ -1167,11 +1499,21 @@ static struct MachInfo *qtest_get_machines(void) QTestState *qts; int idx; + if (g_strcmp0(qemu_var, var)) { + qemu_var = g_strdup(var); + + /* new qemu, clear the cache */ + qtest_free_machine_list(machines); + machines = NULL; + } + if (machines) { return machines; } - qts = qtest_init("-machine none"); + silence_spawn_log = !g_test_verbose(); + + qts = qtest_init_with_env(qemu_var, "-machine none"); response = qtest_qmp(qts, "{ 'execute': 'query-machines' }"); g_assert(response); list = qdict_get_qlist(response, "return"); @@ -1202,6 +1544,8 @@ static struct MachInfo *qtest_get_machines(void) qtest_quit(qts); qobject_unref(response); + silence_spawn_log = false; + memset(&machines[idx], 0, sizeof(struct MachInfo)); /* Terminating entry */ return machines; } @@ -1212,12 +1556,13 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine), struct MachInfo *machines; int i; - machines = qtest_get_machines(); + machines = qtest_get_machines(NULL); for (i = 0; machines[i].name != NULL; i++) { /* Ignore machines that cannot be used for qtests */ if (!strncmp("xenfv", machines[i].name, 5) || - g_str_equal("xenpv", machines[i].name)) { + g_str_equal("xenpv", machines[i].name) || + g_str_equal("xenpvh", machines[i].name)) { continue; } if (!skip_old_versioned || @@ -1227,12 +1572,28 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine), } } -bool qtest_has_machine(const char *machine) +char *qtest_resolve_machine_alias(const char *var, const char *alias) { struct MachInfo *machines; int i; - machines = qtest_get_machines(); + machines = qtest_get_machines(var); + + for (i = 0; machines[i].name != NULL; i++) { + if (machines[i].alias && g_str_equal(alias, machines[i].alias)) { + return g_strdup(machines[i].name); + } + } + + return NULL; +} + +bool qtest_has_machine_with_env(const char *var, const char *machine) +{ + struct MachInfo *machines; + int i; + + machines = qtest_get_machines(var); for (i = 0; machines[i].name != NULL; i++) { if (g_str_equal(machine, machines[i].name) || @@ -1244,6 +1605,11 @@ bool qtest_has_machine(const char *machine) return false; } +bool qtest_has_machine(const char *machine) +{ + return qtest_has_machine_with_env(NULL, machine); +} + bool qtest_has_device(const char *device) { static QList *list; @@ -1302,6 +1668,10 @@ void qtest_qmp_device_add_qdict(QTestState *qts, const char *drv, resp = qtest_qmp(qts, "{'execute': 'device_add', 'arguments': %p}", args); g_assert(resp); g_assert(!qdict_haskey(resp, "event")); /* We don't expect any events */ + if (qdict_haskey(resp, "error")) { + fprintf(stderr, "error: %s\n", + qdict_get_str(qdict_get_qdict(resp, "error"), "desc")); + } g_assert(!qdict_haskey(resp, "error")); qobject_unref(resp); } @@ -1327,8 +1697,24 @@ void qtest_qmp_add_client(QTestState *qts, const char *protocol, int fd) { QDict *resp; +#ifdef WIN32 + WSAPROTOCOL_INFOW info; + g_autofree char *info64 = NULL; + SOCKET s; + + assert(fd_is_socket(fd)); + s = _get_osfhandle(fd); + if (WSADuplicateSocketW(s, GetProcessId((HANDLE)qts->qemu_pid), &info) == SOCKET_ERROR) { + g_autofree char *emsg = g_win32_error_message(WSAGetLastError()); + g_error("WSADuplicateSocketW failed: %s", emsg); + } + info64 = g_base64_encode((guchar *)&info, sizeof(info)); + resp = qtest_qmp(qts, "{'execute': 'get-win32-socket'," + "'arguments': {'fdname': 'fdname', 'info': %s}}", info64); +#else resp = qtest_qmp_fds(qts, &fd, 1, "{'execute': 'getfd'," "'arguments': {'fdname': 'fdname'}}"); +#endif g_assert(resp); g_assert(!qdict_haskey(resp, "event")); /* We don't expect any events */ g_assert(!qdict_haskey(resp, "error")); @@ -1359,15 +1745,19 @@ void qtest_qmp_add_client(QTestState *qts, const char *protocol, int fd) * * {"return": {}} */ +void qtest_qmp_device_del_send(QTestState *qts, const char *id) +{ + QDict *rsp = qtest_qmp(qts, "{'execute': 'device_del', " + "'arguments': {'id': %s}}", id); + g_assert(rsp); + g_assert(qdict_haskey(rsp, "return")); + g_assert(!qdict_haskey(rsp, "error")); + qobject_unref(rsp); +} + void qtest_qmp_device_del(QTestState *qts, const char *id) { - QDict *rsp; - - rsp = qtest_qmp(qts, "{'execute': 'device_del', 'arguments': {'id': %s}}", - id); - - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); + qtest_qmp_device_del_send(qts, id); qtest_qmp_eventwait(qts, "DEVICE_DELETED"); } @@ -1413,7 +1803,7 @@ QTestState *qtest_inproc_init(QTestState **s, bool log, const char* arch, qtest_client_set_rx_handler(qts, qtest_client_inproc_recv_line); - /* send() may not have a matching protoype, so use a type-safe wrapper */ + /* send() may not have a matching prototype, so use a type-safe wrapper */ qts->ops.external_send = send; qtest_client_set_tx_handler(qts, send_wrapper); @@ -1424,7 +1814,11 @@ QTestState *qtest_inproc_init(QTestState **s, bool log, const char* arch, * way, qtest_get_arch works for inproc qtest. */ gchar *bin_path = g_strconcat("/qemu-system-", arch, NULL); - setenv("QTEST_QEMU_BINARY", bin_path, 0); + if (!g_setenv("QTEST_QEMU_BINARY", bin_path, 0)) { + fprintf(stderr, + "Could not set environment variable QTEST_QEMU_BINARY\n"); + exit(1); + } g_free(bin_path); return qts; @@ -1464,3 +1858,55 @@ bool qtest_qom_get_bool(QTestState *s, const char *path, const char *property) return b; } + +bool have_qemu_img(void) +{ + char *rpath; + const char *path = getenv("QTEST_QEMU_IMG"); + if (!path) { + return false; + } + + rpath = realpath(path, NULL); + if (!rpath) { + return false; + } else { + free(rpath); + return true; + } +} + +bool mkimg(const char *file, const char *fmt, unsigned size_mb) +{ + gchar *cli; + bool ret; + int rc; + GError *err = NULL; + char *qemu_img_path; + gchar *out, *out2; + char *qemu_img_abs_path; + + qemu_img_path = getenv("QTEST_QEMU_IMG"); + if (!qemu_img_path) { + return false; + } + qemu_img_abs_path = realpath(qemu_img_path, NULL); + if (!qemu_img_abs_path) { + return false; + } + + cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path, + fmt, file, size_mb); + ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err); + if (err || !g_spawn_check_exit_status(rc, &err)) { + fprintf(stderr, "%s\n", err->message); + g_error_free(err); + } + + g_free(out); + g_free(out2); + g_free(cli); + free(qemu_img_abs_path); + + return ret && !err; +} diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index 94b187837d..6e3d3525bf 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -55,6 +55,19 @@ QTestState *qtest_vinitf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); */ QTestState *qtest_init(const char *extra_args); +/** + * qtest_init_with_env: + * @var: Environment variable from where to take the QEMU binary + * @extra_args: Other arguments to pass to QEMU. CAUTION: these + * arguments are subject to word splitting and shell evaluation. + * + * Like qtest_init(), but use a different environment variable for the + * QEMU binary. + * + * Returns: #QTestState instance. + */ +QTestState *qtest_init_with_env(const char *var, const char *extra_args); + /** * qtest_init_without_qmp_handshake: * @extra_args: other arguments to pass to QEMU. CAUTION: these @@ -75,6 +88,15 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args); */ QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd); +/** + * qtest_wait_qemu: + * @s: #QTestState instance to operate on. + * + * Wait for the QEMU process to terminate. It is safe to call this function + * multiple times. + */ +void qtest_wait_qemu(QTestState *s); + /** * qtest_kill_qemu: * @s: #QTestState instance to operate on. @@ -94,6 +116,7 @@ void qtest_kill_qemu(QTestState *s); */ void qtest_quit(QTestState *s); +#ifndef _WIN32 /** * qtest_qmp_fds: * @s: #QTestState instance to operate on. @@ -108,6 +131,7 @@ void qtest_quit(QTestState *s); QDict *qtest_qmp_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, ...) G_GNUC_PRINTF(4, 5); +#endif /* _WIN32 */ /** * qtest_qmp: @@ -152,6 +176,7 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) */ int qtest_socket_server(const char *socket_path); +#ifndef _WIN32 /** * qtest_vqmp_fds: * @s: #QTestState instance to operate on. @@ -167,6 +192,7 @@ int qtest_socket_server(const char *socket_path); QDict *qtest_vqmp_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, va_list ap) G_GNUC_PRINTF(4, 0); +#endif /* _WIN32 */ /** * qtest_vqmp: @@ -181,6 +207,7 @@ QDict *qtest_vqmp_fds(QTestState *s, int *fds, size_t fds_num, QDict *qtest_vqmp(QTestState *s, const char *fmt, va_list ap) G_GNUC_PRINTF(2, 0); +#ifndef _WIN32 /** * qtest_qmp_vsend_fds: * @s: #QTestState instance to operate on. @@ -196,6 +223,7 @@ QDict *qtest_vqmp(QTestState *s, const char *fmt, va_list ap) void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, va_list ap) G_GNUC_PRINTF(4, 0); +#endif /* _WIN32 */ /** * qtest_qmp_vsend: @@ -223,17 +251,52 @@ QDict *qtest_qmp_receive_dict(QTestState *s); * @s: #QTestState instance to operate on. * * Reads a QMP message from QEMU and returns the response. - * Buffers all the events received meanwhile, until a - * call to qtest_qmp_eventwait + * + * If a callback is registered with qtest_qmp_set_event_callback, + * it will be invoked for every event seen, otherwise events + * will be buffered until a call to one of the qtest_qmp_eventwait + * family of functions. */ QDict *qtest_qmp_receive(QTestState *s); +/* + * QTestQMPEventCallback: + * @s: #QTestState instance event was received on + * @name: name of the event type + * @event: #QDict for the event details + * @opaque: opaque data from time of callback registration + * + * This callback will be invoked whenever an event is received. + * If the callback returns true the event will be consumed, + * otherwise it will be put on the list of pending events. + * Pending events can be later handled by calling either + * qtest_qmp_eventwait or qtest_qmp_eventwait_ref. + * + * Return: true to consume the event, false to let it be queued + */ +typedef bool (*QTestQMPEventCallback)(QTestState *s, const char *name, + QDict *event, void *opaque); + +/** + * qtest_qmp_set_event_callback: + * @s: #QTestSTate instance to operate on + * @cb: callback to invoke for events + * @opaque: data to pass to @cb + * + * Register a callback to be invoked whenever an event arrives + */ +void qtest_qmp_set_event_callback(QTestState *s, + QTestQMPEventCallback cb, void *opaque); + /** * qtest_qmp_eventwait: * @s: #QTestState instance to operate on. * @event: event to wait for. * * Continuously polls for QMP responses until it receives the desired event. + * + * Any callback registered with qtest_qmp_set_event_callback will + * be invoked for every event seen. */ void qtest_qmp_eventwait(QTestState *s, const char *event); @@ -243,6 +306,10 @@ void qtest_qmp_eventwait(QTestState *s, const char *event); * @event: event to wait for. * * Continuously polls for QMP responses until it receives the desired event. + * + * Any callback registered with qtest_qmp_set_event_callback will + * be invoked for every event seen. + * * Returns a copy of the event for further investigation. */ QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event); @@ -317,6 +384,17 @@ void qtest_irq_intercept_in(QTestState *s, const char *string); */ void qtest_irq_intercept_out(QTestState *s, const char *string); +/** + * qtest_irq_intercept_out_named: + * @s: #QTestState instance to operate on. + * @qom_path: QOM path of a device. + * @name: Name of the GPIO out pin + * + * Associate a qtest irq with the named GPIO-out pin of the device + * whose path is specified by @string and whose name is @name. + */ +void qtest_irq_intercept_out_named(QTestState *s, const char *qom_path, const char *name); + /** * qtest_set_irq_in: * @s: QTestState instance to operate on. @@ -678,6 +756,114 @@ void qtest_add_abrt_handler(GHookFunc fn, const void *data); */ void qtest_remove_abrt_handler(void *data); +/** + * qtest_vqmp_assert_success_ref: + * @qts: QTestState instance to operate on + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * @args: variable arguments for @fmt + * + * Sends a QMP message to QEMU, asserts that a 'return' key is present in + * the response, and returns the response. + */ +QDict *qtest_vqmp_assert_success_ref(QTestState *qts, + const char *fmt, va_list args) + G_GNUC_PRINTF(2, 0); + +/** + * qtest_vqmp_assert_success: + * @qts: QTestState instance to operate on + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * @args: variable arguments for @fmt + * + * Sends a QMP message to QEMU and asserts that a 'return' key is present in + * the response. + */ +void qtest_vqmp_assert_success(QTestState *qts, + const char *fmt, va_list args) + G_GNUC_PRINTF(2, 0); + +#ifndef _WIN32 +/** + * qtest_vqmp_fds_assert_success_ref: + * @qts: QTestState instance to operate on + * @fds: the file descriptors to send + * @nfds: number of @fds to send + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * @args: variable arguments for @fmt + * + * Sends a QMP message with file descriptors to QEMU, + * asserts that a 'return' key is present in the response, + * and returns the response. + */ +QDict *qtest_vqmp_fds_assert_success_ref(QTestState *qts, int *fds, size_t nfds, + const char *fmt, va_list args) + G_GNUC_PRINTF(4, 0); + +/** + * qtest_vqmp_fds_assert_success: + * @qts: QTestState instance to operate on + * @fds: the file descriptors to send + * @nfds: number of @fds to send + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * @args: variable arguments for @fmt + * + * Sends a QMP message with file descriptors to QEMU and + * asserts that a 'return' key is present in the response. + */ +void qtest_vqmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds, + const char *fmt, va_list args) + G_GNUC_PRINTF(4, 0); +#endif /* !_WIN32 */ + +/** + * qtest_qmp_assert_failure_ref: + * @qts: QTestState instance to operate on + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * + * Sends a QMP message to QEMU, asserts that an 'error' key is present in + * the response, and returns the response. + */ +QDict *qtest_qmp_assert_failure_ref(QTestState *qts, const char *fmt, ...) + G_GNUC_PRINTF(2, 3); + +/** + * qtest_vqmp_assert_failure_ref: + * @qts: QTestState instance to operate on + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * @args: variable arguments for @fmt + * + * Sends a QMP message to QEMU, asserts that an 'error' key is present in + * the response, and returns the response. + */ +QDict *qtest_vqmp_assert_failure_ref(QTestState *qts, + const char *fmt, va_list args) + G_GNUC_PRINTF(2, 0); + +/** + * qtest_qmp_assert_success_ref: + * @qts: QTestState instance to operate on + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * + * Sends a QMP message to QEMU, asserts that a 'return' key is present in + * the response, and returns the response. + */ +QDict *qtest_qmp_assert_success_ref(QTestState *qts, const char *fmt, ...) + G_GNUC_PRINTF(2, 3); + /** * qtest_qmp_assert_success: * @qts: QTestState instance to operate on @@ -691,6 +877,41 @@ void qtest_remove_abrt_handler(void *data); void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) G_GNUC_PRINTF(2, 3); +#ifndef _WIN32 +/** + * qtest_qmp_fd_assert_success_ref: + * @qts: QTestState instance to operate on + * @fds: the file descriptors to send + * @nfds: number of @fds to send + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * + * Sends a QMP message with file descriptors to QEMU, + * asserts that a 'return' key is present in the response, + * and returns the response. + */ +QDict *qtest_qmp_fds_assert_success_ref(QTestState *qts, int *fds, size_t nfds, + const char *fmt, ...) + G_GNUC_PRINTF(4, 5); + +/** + * qtest_qmp_fd_assert_success: + * @qts: QTestState instance to operate on + * @fds: the file descriptors to send + * @nfds: number of @fds to send + * @fmt: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_interpolation() for what's + * supported after '%'. + * + * Sends a QMP message with file descriptors to QEMU and + * asserts that a 'return' key is present in the response. + */ +void qtest_qmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds, + const char *fmt, ...) + G_GNUC_PRINTF(4, 5); +#endif /* !_WIN32 */ + /** * qtest_cb_for_every_machine: * @cb: Pointer to the callback function @@ -701,6 +922,16 @@ void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) void qtest_cb_for_every_machine(void (*cb)(const char *machine), bool skip_old_versioned); +/** + * qtest_resolve_machine_alias: + * @var: Environment variable from where to take the QEMU binary + * @alias: The alias to resolve + * + * Returns: the machine type corresponding to the alias if any, + * otherwise NULL. + */ +char *qtest_resolve_machine_alias(const char *var, const char *alias); + /** * qtest_has_machine: * @machine: The machine to look for @@ -709,6 +940,15 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine), */ bool qtest_has_machine(const char *machine); +/** + * qtest_has_machine_with_env: + * @var: Environment variable from where to take the QEMU binary + * @machine: The machine to look for + * + * Returns: true if the machine is available in the specified binary. + */ +bool qtest_has_machine_with_env(const char *var, const char *machine); + /** * qtest_has_device: * @device: The device to look for @@ -721,7 +961,7 @@ bool qtest_has_device(const char *device); * qtest_qmp_device_add_qdict: * @qts: QTestState instance to operate on * @drv: Name of the device that should be added - * @arguments: QDict with properties for the device to intialize + * @arguments: QDict with properties for the device to initialize * * Generic hot-plugging test via the device_add QMP command with properties * supplied in form of QDict. Use NULL for empty properties list. @@ -749,16 +989,27 @@ void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id, * @protocol: the protocol to add to * @fd: the client file-descriptor * - * Call QMP ``getfd`` followed by ``add_client`` with the given @fd. + * Call QMP ``getfd`` (on Windows ``get-win32-socket``) followed by + * ``add_client`` with the given @fd. */ void qtest_qmp_add_client(QTestState *qts, const char *protocol, int fd); +/** + * qtest_qmp_device_del_send: + * @qts: QTestState instance to operate on + * @id: Identification string + * + * Generic hot-unplugging test via the device_del QMP command. + */ +void qtest_qmp_device_del_send(QTestState *qts, const char *id); + /** * qtest_qmp_device_del: * @qts: QTestState instance to operate on * @id: Identification string * * Generic hot-unplugging test via the device_del QMP command. + * Waiting for command completion event. */ void qtest_qmp_device_del(QTestState *qts, const char *id); @@ -805,4 +1056,33 @@ void qtest_qom_set_bool(QTestState *s, const char *path, const char *property, * Returns: Value retrieved from property. */ bool qtest_qom_get_bool(QTestState *s, const char *path, const char *property); + +/** + * qtest_pid: + * @s: QTestState instance to operate on. + * + * Returns: the PID of the QEMU process, or <= 0 + */ +pid_t qtest_pid(QTestState *s); + +/** + * have_qemu_img: + * + * Returns: true if "qemu-img" is available. + */ +bool have_qemu_img(void); + +/** + * mkimg: + * @file: File name of the image that should be created + * @fmt: Format, e.g. "qcow2" or "raw" + * @size_mb: Size of the image in megabytes + * + * Create a disk image with qemu-img. Note that the QTEST_QEMU_IMG + * environment variable must point to the qemu-img file. + * + * Returns: true if the image has been created successfully. + */ +bool mkimg(const char *file, const char *fmt, unsigned size_mb); + #endif diff --git a/tests/qtest/m48t59-test.c b/tests/qtest/m48t59-test.c index b94a1230f7..b9cd209165 100644 --- a/tests/qtest/m48t59-test.c +++ b/tests/qtest/m48t59-test.c @@ -137,7 +137,7 @@ static void cmos_get_date_time(QTestState *s, struct tm *date) date->tm_mday = mday; date->tm_mon = mon - 1; date->tm_year = base_year + year - 1900; -#ifndef __sun__ +#if !defined(__sun__) && !defined(_WIN32) date->tm_gmtoff = 0; #endif @@ -155,7 +155,7 @@ static void bcd_check_time(void) struct tm *datep; time_t ts; const int wiggle = 2; - QTestState *s = m48t59_qtest_start(); + QTestState *qts = m48t59_qtest_start(); /* * This check assumes a few things. First, we cannot guarantee that we get @@ -173,10 +173,10 @@ static void bcd_check_time(void) ts = time(NULL); gmtime_r(&ts, &start); - cmos_get_date_time(s, &date[0]); - cmos_get_date_time(s, &date[1]); - cmos_get_date_time(s, &date[2]); - cmos_get_date_time(s, &date[3]); + cmos_get_date_time(qts, &date[0]); + cmos_get_date_time(qts, &date[1]); + cmos_get_date_time(qts, &date[2]); + cmos_get_date_time(qts, &date[3]); ts = time(NULL); gmtime_r(&ts, &end); @@ -192,22 +192,25 @@ static void bcd_check_time(void) } if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) { - long t, s; + long date_s, start_s; + unsigned long diff; start.tm_isdst = datep->tm_isdst; - t = (long)mktime(datep); - s = (long)mktime(&start); - if (t < s) { - g_test_message("RTC is %ld second(s) behind wall-clock", (s - t)); + date_s = (long)mktime(datep); + start_s = (long)mktime(&start); + if (date_s < start_s) { + diff = start_s - date_s; + g_test_message("RTC is %ld second(s) behind wall-clock", diff); } else { - g_test_message("RTC is %ld second(s) ahead of wall-clock", (t - s)); + diff = date_s - start_s; + g_test_message("RTC is %ld second(s) ahead of wall-clock", diff); } - g_assert_cmpint(ABS(t - s), <=, wiggle); + g_assert_cmpint(diff, <=, wiggle); } - qtest_quit(s); + qtest_quit(qts); } /* success if no crash or abort */ diff --git a/tests/qtest/machine-none-test.c b/tests/qtest/machine-none-test.c index d0f8cd9902..31cc0bfb01 100644 --- a/tests/qtest/machine-none-test.c +++ b/tests/qtest/machine-none-test.c @@ -54,6 +54,7 @@ static struct arch2cpu cpus_map[] = { { "riscv64", "rv64" }, { "riscv32", "rv32" }, { "rx", "rx62n" }, + { "loongarch64", "la464"}, }; static const char *get_cpu_model_by_arch(const char *arch) @@ -80,7 +81,7 @@ static void test_machine_cpu_cli(void) " add it to cpus_map\n", arch); return; /* TODO: die here to force all targets have a test */ } - qts = qtest_initf("-machine none -cpu '%s'", cpu_model); + qts = qtest_initf("-machine none -cpu \"%s\"", cpu_model); response = qtest_qmp(qts, "{ 'execute': 'quit' }"); g_assert(qdict_haskey(response, "return")); diff --git a/tests/qtest/max34451-test.c b/tests/qtest/max34451-test.c index 0c98d0764c..dbf6ddc829 100644 --- a/tests/qtest/max34451-test.c +++ b/tests/qtest/max34451-test.c @@ -18,6 +18,7 @@ #define TEST_ID "max34451-test" #define TEST_ADDR (0x4e) +#define MAX34451_MFR_MODE 0xD1 #define MAX34451_MFR_VOUT_PEAK 0xD4 #define MAX34451_MFR_IOUT_PEAK 0xD5 #define MAX34451_MFR_TEMPERATURE_PEAK 0xD6 @@ -315,6 +316,28 @@ static void test_ot_faults(void *obj, void *data, QGuestAllocator *alloc) } } +#define RAND_ON_OFF_CONFIG 0x12 +#define RAND_MFR_MODE 0x3456 + +/* test writes to all pages */ +static void test_all_pages(void *obj, void *data, QGuestAllocator *alloc) +{ + uint16_t i2c_value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + i2c_set8(i2cdev, PMBUS_PAGE, PB_ALL_PAGES); + i2c_set8(i2cdev, PMBUS_ON_OFF_CONFIG, RAND_ON_OFF_CONFIG); + max34451_i2c_set16(i2cdev, MAX34451_MFR_MODE, RAND_MFR_MODE); + + for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES + MAX34451_NUM_PWR_DEVICES; + i++) { + i2c_value = i2c_get8(i2cdev, PMBUS_ON_OFF_CONFIG); + g_assert_cmphex(i2c_value, ==, RAND_ON_OFF_CONFIG); + i2c_value = max34451_i2c_get16(i2cdev, MAX34451_MFR_MODE); + g_assert_cmphex(i2c_value, ==, RAND_MFR_MODE); + } +} + static void max34451_register_nodes(void) { QOSGraphEdgeOptions opts = { @@ -332,5 +355,6 @@ static void max34451_register_nodes(void) qos_add_test("test_ro_regs", "max34451", test_ro_regs, NULL); qos_add_test("test_ov_faults", "max34451", test_ov_faults, NULL); qos_add_test("test_ot_faults", "max34451", test_ot_faults, NULL); + qos_add_test("test_all_pages", "max34451", test_all_pages, NULL); } libqos_init(max34451_register_nodes); diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 31287a9173..36c5c13a7b 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -1,20 +1,18 @@ -# All QTests for now are POSIX-only, but the dependencies are -# really in libqtest, not in the testcases themselves. -if not config_host.has_key('CONFIG_POSIX') - subdir_done() -endif - slow_qtests = { - 'ahci-test' : 60, - 'bios-tables-test' : 120, - 'boot-serial-test' : 60, - 'migration-test' : 150, - 'npcm7xx_pwm-test': 150, - 'prom-env-test' : 60, - 'pxe-test' : 60, - 'qos-test' : 60, - 'qom-test' : 300, - 'test-hmp' : 120, + 'aspeed_smc-test': 360, + 'bios-tables-test' : 610, + 'cdrom-test' : 610, + 'device-introspect-test' : 720, + 'migration-test' : 480, + 'npcm7xx_pwm-test': 300, + 'npcm7xx_watchdog_timer-test': 120, + 'qom-test' : 900, + 'test-hmp' : 240, + 'pxe-test': 610, + 'prom-env-test': 360, + 'boot-serial-test': 360, + 'qos-test': 120, + 'vmgenid-test': 610, } qtests_generic = [ @@ -26,8 +24,10 @@ qtests_generic = [ 'qom-test', 'test-hmp', 'qos-test', + 'readconfig-test', + 'netdev-socket', ] -if config_host.has_key('CONFIG_MODULES') +if enable_modules qtests_generic += [ 'modules-test' ] endif @@ -38,15 +38,23 @@ qtests_pci = \ qtests_cxl = \ (config_all_devices.has_key('CONFIG_CXL') ? ['cxl-test'] : []) +# FIXME: Get rid of get_option('default_devices') here and check +# for the availability of the default NICs in the tests +qtests_filter = \ + (get_option('default_devices') and slirp.found() ? ['test-netfilter'] : []) + \ + (get_option('default_devices') and host_os != 'windows' ? ['test-filter-mirror'] : []) + \ + (get_option('default_devices') and host_os != 'windows' ? ['test-filter-redirector'] : []) + qtests_i386 = \ - (slirp.found() ? ['pxe-test', 'test-netfilter'] : []) + \ - (config_host.has_key('CONFIG_POSIX') ? ['test-filter-mirror'] : []) + \ + (slirp.found() ? ['pxe-test'] : []) + \ + qtests_filter + \ (have_tools ? ['ahci-test'] : []) + \ (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \ (config_all_devices.has_key('CONFIG_SGA') ? ['boot-serial-test'] : []) + \ (config_all_devices.has_key('CONFIG_ISA_IPMI_KCS') ? ['ipmi-kcs-test'] : []) + \ - (config_host.has_key('CONFIG_LINUX') and \ - config_all_devices.has_key('CONFIG_ISA_IPMI_BT') ? ['ipmi-bt-test'] : []) + \ + (host_os == 'linux' and \ + config_all_devices.has_key('CONFIG_ISA_IPMI_BT') and + config_all_devices.has_key('CONFIG_IPMI_EXTERN') ? ['ipmi-bt-test'] : []) + \ (config_all_devices.has_key('CONFIG_WDT_IB700') ? ['wdt_ib700-test'] : []) + \ (config_all_devices.has_key('CONFIG_PVPANIC_ISA') ? ['pvpanic-test'] : []) + \ (config_all_devices.has_key('CONFIG_PVPANIC_PCI') ? ['pvpanic-pci-test'] : []) + \ @@ -70,12 +78,16 @@ qtests_i386 = \ (config_all_devices.has_key('CONFIG_SB16') ? ['fuzz-sb16-test'] : []) + \ (config_all_devices.has_key('CONFIG_SDHCI_PCI') ? ['fuzz-sdcard-test'] : []) + \ (config_all_devices.has_key('CONFIG_ESP_PCI') ? ['am53c974-test'] : []) + \ - (config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \ - (config_all_devices.has_key('CONFIG_VIRTIO_NET') and \ + (host_os != 'windows' and \ + config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \ + (config_all_devices.has_key('CONFIG_PCIE_PORT') and \ + config_all_devices.has_key('CONFIG_VIRTIO_NET') and \ config_all_devices.has_key('CONFIG_Q35') and \ config_all_devices.has_key('CONFIG_VIRTIO_PCI') and \ slirp.found() ? ['virtio-net-failover'] : []) + \ - (unpack_edk2_blobs ? ['bios-tables-test'] : []) + \ + (unpack_edk2_blobs and \ + config_all_devices.has_key('CONFIG_HPET') and \ + config_all_devices.has_key('CONFIG_PARALLEL') ? ['bios-tables-test'] : []) + \ qtests_pci + \ qtests_cxl + \ ['fdc-test', @@ -93,8 +105,7 @@ qtests_i386 = \ 'vmgenid-test', 'migration-test', 'test-x86-cpuid-compat', - 'numa-test', - 'test-filter-redirector' + 'numa-test' ] if dbus_display @@ -118,30 +129,25 @@ endif qtests_x86_64 = qtests_i386 qtests_alpha = ['boot-serial-test'] + \ - ['test-filter-mirror', 'test-filter-redirector'] + \ - (slirp.found() ? ['test-netfilter'] : []) + \ + qtests_filter + \ (config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) qtests_avr = [ 'boot-serial-test' ] qtests_hppa = ['boot-serial-test'] + \ - ['test-filter-mirror', 'test-filter-redirector'] + \ - (slirp.found() ? ['test-netfilter'] : []) + \ + qtests_filter + \ (config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) qtests_m68k = ['boot-serial-test'] + \ - ['test-filter-mirror', 'test-filter-redirector'] + \ - (slirp.found() ? ['test-netfilter'] : []) + qtests_filter qtests_microblaze = ['boot-serial-test'] + \ - ['test-filter-mirror', 'test-filter-redirector'] + \ - (slirp.found() ? ['test-netfilter'] : []) + qtests_filter qtests_microblazeel = qtests_microblaze qtests_mips = \ - ['test-filter-mirror', 'test-filter-redirector'] + \ - (slirp.found() ? ['test-netfilter'] : []) + \ + qtests_filter + \ (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \ (config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) @@ -150,18 +156,18 @@ qtests_mips64 = qtests_mips qtests_mips64el = qtests_mips qtests_ppc = \ - ['test-filter-mirror', 'test-filter-redirector'] + \ - (slirp.found() ? ['test-netfilter'] : []) + \ + qtests_filter + \ (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \ (config_all_devices.has_key('CONFIG_M48T59') ? ['m48t59-test'] : []) + \ - (config_all_devices.has_key('CONFIG_TCG') ? ['prom-env-test'] : []) + \ - (config_all_devices.has_key('CONFIG_TCG') ? ['boot-serial-test'] : []) + \ + (config_all_accel.has_key('CONFIG_TCG') ? ['prom-env-test'] : []) + \ + (config_all_accel.has_key('CONFIG_TCG') ? ['boot-serial-test'] : []) + \ ['boot-order-test'] qtests_ppc64 = \ qtests_ppc + \ (config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + \ (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) + \ + (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) + \ (config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) + \ (slirp.found() ? ['pxe-test'] : []) + \ (config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) + \ @@ -172,13 +178,11 @@ qtests_sh4 = (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-te qtests_sh4eb = (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) qtests_sparc = ['prom-env-test', 'm48t59-test', 'boot-serial-test'] + \ - ['test-filter-mirror', 'test-filter-redirector'] + \ - (slirp.found() ? ['test-netfilter'] : []) + qtests_filter qtests_sparc64 = \ (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \ - (slirp.found() ? ['test-netfilter'] : []) + \ - ['test-filter-mirror', 'test-filter-redirector'] + \ + qtests_filter + \ ['prom-env-test', 'boot-serial-test'] qtests_npcm7xx = \ @@ -189,41 +193,55 @@ qtests_npcm7xx = \ 'npcm7xx_sdhci-test', 'npcm7xx_smbus-test', 'npcm7xx_timer-test', - 'npcm7xx_watchdog_timer-test'] + \ + 'npcm7xx_watchdog_timer-test', + 'npcm_gmac-test'] + \ (slirp.found() ? ['npcm7xx_emc-test'] : []) qtests_aspeed = \ ['aspeed_hace-test', 'aspeed_smc-test', 'aspeed_gpio-test'] + +qtests_stm32l4x5 = \ + ['stm32l4x5_exti-test', + 'stm32l4x5_syscfg-test', + 'stm32l4x5_rcc-test', + 'stm32l4x5_gpio-test'] + qtests_arm = \ (config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \ (config_all_devices.has_key('CONFIG_CMSDK_APB_DUALTIMER') ? ['cmsdk-apb-dualtimer-test'] : []) + \ (config_all_devices.has_key('CONFIG_CMSDK_APB_TIMER') ? ['cmsdk-apb-timer-test'] : []) + \ (config_all_devices.has_key('CONFIG_CMSDK_APB_WATCHDOG') ? ['cmsdk-apb-watchdog-test'] : []) + \ - (config_all_devices.has_key('CONFIG_PFLASH_CFI02') ? ['pflash-cfi02-test'] : []) + \ + (config_all_devices.has_key('CONFIG_PFLASH_CFI02') and + config_all_devices.has_key('CONFIG_MUSICPAL') ? ['pflash-cfi02-test'] : []) + \ (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed : []) + \ (config_all_devices.has_key('CONFIG_NPCM7XX') ? qtests_npcm7xx : []) + \ + (config_all_devices.has_key('CONFIG_GENERIC_LOADER') ? ['hexloader-test'] : []) + \ + (config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \ + (config_all_devices.has_key('CONFIG_VEXPRESS') ? ['test-arm-mptimer'] : []) + \ + (config_all_devices.has_key('CONFIG_MICROBIT') ? ['microbit-test'] : []) + \ + (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') ? qtests_stm32l4x5 : []) + \ + (config_all_devices.has_key('CONFIG_FSI_APB2OPB_ASPEED') ? ['aspeed_fsi-test'] : []) + \ ['arm-cpu-features', - 'microbit-test', - 'test-arm-mptimer', - 'boot-serial-test', - 'hexloader-test'] + 'boot-serial-test'] # TODO: once aarch64 TCG is fixed on ARM 32 bit host, make bios-tables-test unconditional qtests_aarch64 = \ (cpu != 'arm' and unpack_edk2_blobs ? ['bios-tables-test'] : []) + \ - (config_all_devices.has_key('CONFIG_TPM_TIS_SYSBUS') ? ['tpm-tis-device-test'] : []) + \ - (config_all_devices.has_key('CONFIG_TPM_TIS_SYSBUS') ? ['tpm-tis-device-swtpm-test'] : []) + \ + (config_all_accel.has_key('CONFIG_TCG') and config_all_devices.has_key('CONFIG_TPM_TIS_SYSBUS') ? \ + ['tpm-tis-device-test', 'tpm-tis-device-swtpm-test'] : []) + \ (config_all_devices.has_key('CONFIG_XLNX_ZYNQMP_ARM') ? ['xlnx-can-test', 'fuzz-xlnx-dp-test'] : []) + \ + (config_all_devices.has_key('CONFIG_XLNX_VERSAL') ? ['xlnx-canfd-test', 'xlnx-versal-trng-test'] : []) + \ + (config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test', 'bcm2835-i2c-test'] : []) + \ + (config_all_accel.has_key('CONFIG_TCG') and \ + config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \ ['arm-cpu-features', 'numa-test', 'boot-serial-test', 'migration-test'] qtests_s390x = \ - (slirp.found() ? ['pxe-test', 'test-netfilter'] : []) + \ - (config_host.has_key('CONFIG_POSIX') ? ['test-filter-mirror'] : []) + \ - (config_host.has_key('CONFIG_POSIX') ? ['test-filter-redirector'] : []) + \ + qtests_filter + \ ['boot-serial-test', 'drive_del-test', 'device-plug-test', @@ -231,13 +249,16 @@ qtests_s390x = \ 'cpu-plug-test', 'migration-test'] +qtests_riscv32 = \ + (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : []) + qos_test_ss = ss.source_set() qos_test_ss.add( 'ac97-test.c', 'adm1272-test.c', + 'adm1266-test.c', 'ds1338-test.c', 'e1000-test.c', - 'e1000e-test.c', 'eepro100-test.c', 'es1370-test.c', 'ipoctal232-test.c', @@ -261,10 +282,19 @@ qos_test_ss.add( 'virtio-net-test.c', 'virtio-rng-test.c', 'virtio-scsi-test.c', - 'virtio-serial-test.c', 'virtio-iommu-test.c', 'vmxnet3-test.c', + 'igb-test.c', + 'ufs-test.c', ) + +if config_all_devices.has_key('CONFIG_VIRTIO_SERIAL') + qos_test_ss.add(files('virtio-serial-test.c')) +endif + +if host_os != 'windows' + qos_test_ss.add(files('e1000e-test.c')) +endif if have_virtfs qos_test_ss.add(files('virtio-9p-test.c')) endif @@ -295,18 +325,29 @@ qtests = { 'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'], 'migration-test': migration_files, 'pxe-test': files('boot-sector.c'), - 'qos-test': [chardev, io, qos_test_ss.apply(config_host, strict: false).sources()], + 'qos-test': [chardev, io, qos_test_ss.apply({}).sources()], 'tpm-crb-swtpm-test': [io, tpmemu_files], 'tpm-crb-test': [io, tpmemu_files], 'tpm-tis-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'], 'tpm-tis-test': [io, tpmemu_files, 'tpm-tis-util.c'], + 'tpm-tis-i2c-test': [io, tpmemu_files, 'qtest_aspeed.c'], 'tpm-tis-device-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'], 'tpm-tis-device-test': [io, tpmemu_files, 'tpm-tis-util.c'], + 'virtio-net-failover': files('migration-helpers.c'), 'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'), + 'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'), } +if vnc.found() + gvnc = dependency('gvnc-1.0', method: 'pkg-config', required: false) + if gvnc.found() + qtests += {'vnc-display-test': [gvnc]} + qtests_generic += [ 'vnc-display-test' ] + endif +endif + if dbus_display -qtests += {'dbus-display-test': [dbus_display1, gio]} + qtests += {'dbus-display-test': [dbus_display1_dep, gio]} endif qtest_executables = {} @@ -332,6 +373,8 @@ foreach dir : target_dirs test_deps += [qsd] endif + qtest_env.set('PYTHON', python.full_path()) + foreach test : target_qtests # Executables are shared across targets, declare them only the first time we # encounter them @@ -355,8 +398,8 @@ foreach dir : target_dirs env: qtest_env, args: ['--tap', '-k'], protocol: 'tap', - timeout: slow_qtests.get(test, 30), - priority: slow_qtests.get(test, 30), + timeout: slow_qtests.get(test, 60), + priority: slow_qtests.get(test, 60), suite: ['qtest', 'qtest-' + target_base]) endforeach endforeach diff --git a/tests/qtest/microbit-test.c b/tests/qtest/microbit-test.c index 04e199ec33..72190d38f7 100644 --- a/tests/qtest/microbit-test.c +++ b/tests/qtest/microbit-test.c @@ -51,7 +51,7 @@ static void uart_rw_to_rxd(QTestState *qts, int sock_fd, const char *in, { int i, in_len = strlen(in); - g_assert_true(write(sock_fd, in, in_len) == in_len); + g_assert_true(send(sock_fd, in, in_len, 0) == in_len); for (i = 0; i < in_len; i++) { g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + A_UART_RXDRDY)); @@ -77,7 +77,7 @@ static void test_nrf51_uart(void) char s[10]; QTestState *qts = qtest_init_with_serial("-M microbit", &sock_fd); - g_assert_true(write(sock_fd, "c", 1) == 1); + g_assert_true(send(sock_fd, "c", 1, 0) == 1); g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD), ==, 0x00); qtest_writel(qts, NRF51_UART_BASE + A_UART_ENABLE, 0x04); @@ -97,14 +97,14 @@ static void test_nrf51_uart(void) qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTTX, 0x01); uart_w_to_txd(qts, "d"); - g_assert_true(read(sock_fd, s, 10) == 1); + g_assert_true(recv(sock_fd, s, 10, 0) == 1); g_assert_cmphex(s[0], ==, 'd'); qtest_writel(qts, NRF51_UART_BASE + A_UART_SUSPEND, 0x01); qtest_writel(qts, NRF51_UART_BASE + A_UART_TXD, 'h'); qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTTX, 0x01); uart_w_to_txd(qts, "world"); - g_assert_true(read(sock_fd, s, 10) == 5); + g_assert_true(recv(sock_fd, s, 10, 0) == 5); g_assert_true(memcmp(s, "world", 5) == 0); close(sock_fd); @@ -393,6 +393,51 @@ static void test_nrf51_gpio(void) qtest_quit(qts); } +static void test_nrf51_gpio_detect(void) +{ + QTestState *qts = qtest_init("-M microbit"); + int i; + + /* Connect input buffer on pins 1-7, configure SENSE for high level */ + for (i = 1; i <= 7; i++) { + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START + i * 4, + deposit32(0, 16, 2, 2)); + } + + qtest_irq_intercept_out_named(qts, "/machine/nrf51/gpio", "detect"); + + for (i = 1; i <= 7; i++) { + /* Set pin high */ + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", i, 1); + uint32_t actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN); + g_assert_cmpuint(actual, ==, 1 << i); + + /* Check that DETECT is high */ + g_assert_true(qtest_get_irq(qts, 0)); + + /* Set pin low, check that DETECT goes low. */ + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", i, 0); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN); + g_assert_cmpuint(actual, ==, 0x0); + g_assert_false(qtest_get_irq(qts, 0)); + } + + /* Set pin 0 high, check that DETECT doesn't fire */ + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1); + g_assert_false(qtest_get_irq(qts, 0)); + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); + + /* Set pins 1, 2, and 3 high, then set 3 low. Check DETECT is still high */ + for (i = 1; i <= 3; i++) { + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", i, 1); + } + g_assert_true(qtest_get_irq(qts, 0)); + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 3, 0); + g_assert_true(qtest_get_irq(qts, 0)); + + qtest_quit(qts); +} + static void timer_task(QTestState *qts, hwaddr task) { qtest_writel(qts, NRF51_TIMER_BASE + task, NRF51_TRIGGER_TASK); @@ -447,11 +492,11 @@ static void test_nrf51_timer(void) timer_set_bitmode(qts, NRF51_TIMER_WIDTH_16); /* 16 MHz Timer */ timer_set_prescaler(qts, 0); - /* Swept over in first step */ + /* Swept over, during the first step */ timer_set_cc(qts, 0, 2); - /* Barely miss on first step */ + /* Barely miss, after the second step */ timer_set_cc(qts, 1, 162); - /* Spot on on third step */ + /* Spot on, after the third step */ timer_set_cc(qts, 2, 480); timer_assert_events(qts, 0, 0, 0, 0); @@ -499,6 +544,7 @@ int main(int argc, char **argv) qtest_add_func("/microbit/nrf51/uart", test_nrf51_uart); qtest_add_func("/microbit/nrf51/gpio", test_nrf51_gpio); + qtest_add_func("/microbit/nrf51/gpio_detect", test_nrf51_gpio_detect); qtest_add_func("/microbit/nrf51/nvmc", test_nrf51_nvmc); qtest_add_func("/microbit/nrf51/timer", test_nrf51_timer); qtest_add_func("/microbit/microbit/i2c", test_microbit_i2c); diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index a6aa59e4e6..e451dbdbed 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -11,68 +11,56 @@ */ #include "qemu/osdep.h" +#include "qemu/ctype.h" #include "qapi/qmp/qjson.h" #include "migration-helpers.h" -bool got_stop; +/* + * Number of seconds we wait when looking for migration + * status changes, to avoid test suite hanging forever + * when things go wrong. Needs to be higher enough to + * avoid false positives on loaded hosts. + */ +#define MIGRATION_STATUS_WAIT_TIMEOUT 120 -static void check_stop_event(QTestState *who) +bool migrate_watch_for_events(QTestState *who, const char *name, + QDict *event, void *opaque) { - QDict *event = qtest_qmp_event_ref(who, "STOP"); - if (event) { - got_stop = true; - qobject_unref(event); + QTestMigrationState *state = opaque; + + if (g_str_equal(name, "STOP")) { + state->stop_seen = true; + return true; + } else if (g_str_equal(name, "SUSPEND")) { + state->suspend_seen = true; + return true; + } else if (g_str_equal(name, "RESUME")) { + state->resume_seen = true; + return true; } + + return false; } -/* - * Events can get in the way of responses we are actually waiting for. - */ -QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) +void migrate_qmp_fail(QTestState *who, const char *uri, const char *fmt, ...) { va_list ap; - QDict *resp, *ret; + QDict *args, *err; - va_start(ap, command); - qtest_qmp_vsend_fds(who, &fd, 1, command, ap); + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); va_end(ap); - resp = qtest_qmp_receive(who); - check_stop_event(who); + g_assert(!qdict_haskey(args, "uri")); + qdict_put_str(args, "uri", uri); - g_assert(!qdict_haskey(resp, "error")); - g_assert(qdict_haskey(resp, "return")); + err = qtest_qmp_assert_failure_ref( + who, "{ 'execute': 'migrate', 'arguments': %p}", args); - ret = qdict_get_qdict(resp, "return"); - qobject_ref(ret); - qobject_unref(resp); + g_assert(qdict_haskey(err, "desc")); - return ret; -} - -/* - * Events can get in the way of responses we are actually waiting for. - */ -QDict *wait_command(QTestState *who, const char *command, ...) -{ - va_list ap; - QDict *resp, *ret; - - va_start(ap, command); - resp = qtest_vqmp(who, command, ap); - va_end(ap); - - check_stop_event(who); - - g_assert(!qdict_haskey(resp, "error")); - g_assert(qdict_haskey(resp, "return")); - - ret = qdict_get_qdict(resp, "return"); - qobject_ref(ret); - qobject_unref(resp); - - return ret; + qobject_unref(err); } /* @@ -83,7 +71,7 @@ QDict *wait_command(QTestState *who, const char *command, ...) void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) { va_list ap; - QDict *args, *rsp; + QDict *args; va_start(ap, fmt); args = qdict_from_vjsonf_nofail(fmt, ap); @@ -92,10 +80,54 @@ void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) g_assert(!qdict_haskey(args, "uri")); qdict_put_str(args, "uri", uri); - rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args); + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate', 'arguments': %p}", args); +} + +void migrate_set_capability(QTestState *who, const char *capability, + bool value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-capabilities'," + "'arguments': { " + "'capabilities': [ { " + "'capability': %s, 'state': %i } ] } }", + capability, value); +} + +void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) +{ + va_list ap; + QDict *args, *rsp, *data; + + 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); + + migrate_set_capability(to, "events", true); + + rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}", + args); + + if (!qdict_haskey(rsp, "return")) { + g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true); + g_test_message("%s", s->str); + } g_assert(qdict_haskey(rsp, "return")); qobject_unref(rsp); + + rsp = qtest_qmp_eventwait_ref(to, "MIGRATION"); + g_assert(qdict_haskey(rsp, "data")); + + data = qdict_get_qdict(rsp, "data"); + g_assert(qdict_haskey(data, "status")); + g_assert_cmpstr(qdict_get_str(data, "status"), ==, "setup"); + + qobject_unref(rsp); } /* @@ -104,7 +136,7 @@ void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) */ QDict *migrate_query(QTestState *who) { - return wait_command(who, "{ 'execute': 'query-migrate' }"); + return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }"); } QDict *migrate_query_not_failed(QTestState *who) @@ -166,8 +198,11 @@ static bool check_migration_status(QTestState *who, const char *goal, void wait_for_migration_status(QTestState *who, const char *goal, const char **ungoals) { + g_test_timer_start(); while (!check_migration_status(who, goal, ungoals)) { usleep(1000); + + g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); } } @@ -178,6 +213,7 @@ void wait_for_migration_complete(QTestState *who) void wait_for_migration_fail(QTestState *from, bool allow_active) { + g_test_timer_start(); QDict *rsp_return; char *status; bool failed; @@ -193,11 +229,97 @@ void wait_for_migration_fail(QTestState *from, bool allow_active) g_assert(result); failed = !strcmp(status, "failed"); g_free(status); + + g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); } while (!failed); /* Is the machine currently running? */ - rsp_return = wait_command(from, "{ 'execute': 'query-status' }"); + rsp_return = qtest_qmp_assert_success_ref(from, + "{ 'execute': 'query-status' }"); g_assert(qdict_haskey(rsp_return, "running")); g_assert(qdict_get_bool(rsp_return, "running")); qobject_unref(rsp_return); } + +char *find_common_machine_version(const char *mtype, const char *var1, + const char *var2) +{ + g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype); + g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype); + + g_assert(type1 && type2); + + if (g_str_equal(type1, type2)) { + /* either can be used */ + return g_strdup(type1); + } + + if (qtest_has_machine_with_env(var2, type1)) { + return g_strdup(type1); + } + + if (qtest_has_machine_with_env(var1, type2)) { + return g_strdup(type2); + } + + g_test_message("No common machine version for machine type '%s' between " + "binaries %s and %s", mtype, getenv(var1), getenv(var2)); + g_assert_not_reached(); +} + +char *resolve_machine_version(const char *alias, const char *var1, + const char *var2) +{ + const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE"); + g_autofree char *machine_name = NULL; + + if (mname) { + const char *dash = strrchr(mname, '-'); + const char *dot = strrchr(mname, '.'); + + machine_name = g_strdup(mname); + + if (dash && dot) { + assert(qtest_has_machine(machine_name)); + return g_steal_pointer(&machine_name); + } + /* else: probably an alias, let it be resolved below */ + } else { + /* use the hardcoded alias */ + machine_name = g_strdup(alias); + } + + return find_common_machine_version(machine_name, var1, var2); +} + +typedef struct { + char *name; + void (*func)(void); +} MigrationTest; + +static void migration_test_destroy(gpointer data) +{ + MigrationTest *test = (MigrationTest *)data; + + g_free(test->name); + g_free(test); +} + +static void migration_test_wrapper(const void *data) +{ + MigrationTest *test = (MigrationTest *)data; + + g_test_message("Running /%s%s", qtest_get_arch(), test->name); + test->func(); +} + +void migration_test_add(const char *path, void (*fn)(void)) +{ + MigrationTest *test = g_new0(MigrationTest, 1); + + test->func = fn; + test->name = g_strdup(path); + + qtest_add_data_func_full(path, test, migration_test_wrapper, + migration_test_destroy); +} diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index 78587c2b82..3bf7ded1b9 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -15,17 +15,29 @@ #include "libqtest.h" -extern bool got_stop; +typedef struct QTestMigrationState { + bool stop_seen; + bool resume_seen; + bool suspend_seen; + bool suspend_me; +} QTestMigrationState; -G_GNUC_PRINTF(3, 4) -QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...); - -G_GNUC_PRINTF(2, 3) -QDict *wait_command(QTestState *who, const char *command, ...); +bool migrate_watch_for_events(QTestState *who, const char *name, + QDict *event, void *opaque); G_GNUC_PRINTF(3, 4) void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...); +G_GNUC_PRINTF(3, 4) +void migrate_incoming_qmp(QTestState *who, const char *uri, + const char *fmt, ...); + +G_GNUC_PRINTF(3, 4) +void migrate_qmp_fail(QTestState *who, const char *uri, const char *fmt, ...); + +void migrate_set_capability(QTestState *who, const char *capability, + bool value); + QDict *migrate_query(QTestState *who); QDict *migrate_query_not_failed(QTestState *who); @@ -36,4 +48,9 @@ void wait_for_migration_complete(QTestState *who); void wait_for_migration_fail(QTestState *from, bool allow_active); +char *find_common_machine_version(const char *mtype, const char *var1, + const char *var2); +char *resolve_machine_version(const char *alias, const char *var1, + const char *var2); +void migration_test_add(const char *path, void (*fn)(void)); #endif /* MIGRATION_HELPERS_H */ diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index d33e8060f9..1d2cee87ea 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -24,6 +24,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "crypto/tlscredspsk.h" +#include "qapi/qmp/qlist.h" #include "migration-helpers.h" #include "tests/migration/migration-test.h" @@ -39,15 +40,39 @@ #include "linux/kvm.h" #endif -/* TODO actually test the results and get rid of this */ -#define qtest_qmp_discard_response(...) qobject_unref(qtest_qmp(__VA_ARGS__)) - unsigned start_address; unsigned end_address; static bool uffd_feature_thread_id; +static QTestMigrationState src_state; +static QTestMigrationState dst_state; -/* A downtime where the test really should converge */ -#define CONVERGE_DOWNTIME 1000 +/* + * An initial 3 MB offset is used as that corresponds + * to ~1 sec of data transfer with our bandwidth setting. + */ +#define MAGIC_OFFSET_BASE (3 * 1024 * 1024) +/* + * A further 1k is added to ensure we're not a multiple + * of TEST_MEM_PAGE_SIZE, thus avoid clash with writes + * from the migration guest workload. + */ +#define MAGIC_OFFSET_SHUFFLE 1024 +#define MAGIC_OFFSET (MAGIC_OFFSET_BASE + MAGIC_OFFSET_SHUFFLE) +#define MAGIC_MARKER 0xFEED12345678CAFEULL + +/* + * Dirtylimit stop working if dirty page rate error + * value less than DIRTYLIMIT_TOLERANCE_RANGE + */ +#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ + +#define ANALYZE_SCRIPT "scripts/analyze-migration.py" + +#define QEMU_VM_FILE_MAGIC 0x5145564d +#define FILE_TEST_FILENAME "migfile" +#define FILE_TEST_OFFSET 0x1000 +#define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC" +#define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST" #if defined(__linux__) #include @@ -57,14 +82,14 @@ static bool uffd_feature_thread_id; #if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) #include #include -#include +#include "qemu/userfaultfd.h" static bool ufd_version_check(void) { struct uffdio_api api_struct; uint64_t ioctl_mask; - int ufd = syscall(__NR_userfaultfd, O_CLOEXEC); + int ufd = uffd_open(O_CLOEXEC); if (ufd == -1) { g_test_message("Skipping test: userfaultfd not available"); @@ -79,8 +104,8 @@ static bool ufd_version_check(void) } uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID; - ioctl_mask = (__u64)1 << _UFFDIO_REGISTER | - (__u64)1 << _UFFDIO_UNREGISTER; + ioctl_mask = 1ULL << _UFFDIO_REGISTER | + 1ULL << _UFFDIO_UNREGISTER; if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) { g_test_message("Skipping test: Missing userfault feature"); return false; @@ -98,7 +123,8 @@ static bool ufd_version_check(void) #endif -static const char *tmpfs; +static char *tmpfs; +static char *bootpath; /* The boot file modifies memory area in [start_address, end_address) * repeatedly. It outputs a 'B' at a fixed rate while it's still running. @@ -107,18 +133,52 @@ static const char *tmpfs; #include "tests/migration/aarch64/a-b-kernel.h" #include "tests/migration/s390x/a-b-bios.h" -static void init_bootfile(const char *bootpath, void *content, size_t len) +static void bootfile_create(char *dir, bool suspend_me) { + const char *arch = qtest_get_arch(); + unsigned char *content; + size_t len; + + bootpath = g_strdup_printf("%s/bootsect", dir); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + /* the assembled x86 boot sector should be exactly one sector large */ + g_assert(sizeof(x86_bootsect) == 512); + x86_bootsect[SYM_suspend_me - SYM_start] = suspend_me; + content = x86_bootsect; + len = sizeof(x86_bootsect); + } else if (g_str_equal(arch, "s390x")) { + content = s390x_elf; + len = sizeof(s390x_elf); + } else if (strcmp(arch, "ppc64") == 0) { + /* + * sane architectures can be programmed at the boot prompt + */ + return; + } else if (strcmp(arch, "aarch64") == 0) { + content = aarch64_kernel; + len = sizeof(aarch64_kernel); + g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE); + } else { + g_assert_not_reached(); + } + FILE *bootfile = fopen(bootpath, "wb"); g_assert_cmpint(fwrite(content, len, 1, bootfile), ==, 1); fclose(bootfile); } +static void bootfile_delete(void) +{ + unlink(bootpath); + g_free(bootpath); + bootpath = NULL; +} + /* * Wait for some output in the serial output file, * we get an 'A' followed by an endless string of 'B's - * but on the destination we won't have the A. + * but on the destination we won't have the A (unless we enabled suspend/resume) */ static void wait_for_serial(const char *side) { @@ -171,6 +231,27 @@ static void wait_for_serial(const char *side) } while (true); } +static void wait_for_stop(QTestState *who, QTestMigrationState *state) +{ + if (!state->stop_seen) { + qtest_qmp_eventwait(who, "STOP"); + } +} + +static void wait_for_resume(QTestState *who, QTestMigrationState *state) +{ + if (!state->resume_seen) { + qtest_qmp_eventwait(who, "RESUME"); + } +} + +static void wait_for_suspend(QTestState *who, QTestMigrationState *state) +{ + if (state->suspend_me && !state->suspend_seen) { + qtest_qmp_eventwait(who, "SUSPEND"); + } +} + /* * It's tricky to use qemu's migration event capability with qtest, * events suddenly appearing confuse the qmp()/hmp() responses. @@ -218,21 +299,19 @@ static void read_blocktime(QTestState *who) qobject_unref(rsp_return); } +/* + * Wait for two changes in the migration pass count, but bail if we stop. + */ static void wait_for_migration_pass(QTestState *who) { - uint64_t initial_pass = get_migration_pass(who); - uint64_t pass; + uint64_t pass, prev_pass = 0, changes = 0; - /* Wait for the 1st sync */ - while (!got_stop && !initial_pass) { - usleep(1000); - initial_pass = get_migration_pass(who); - } - - do { + while (changes < 2 && !src_state.stop_seen && !src_state.suspend_seen) { usleep(1000); pass = get_migration_pass(who); - } while (pass == initial_pass && !got_stop); + changes += (pass != prev_pass); + prev_pass = pass; + } } static void check_guests_ram(QTestState *who) @@ -340,7 +419,8 @@ static long long migrate_get_parameter_int(QTestState *who, QDict *rsp; long long result; - rsp = wait_command(who, "{ 'execute': 'query-migrate-parameters' }"); + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); result = qdict_get_int(rsp, parameter); qobject_unref(rsp); return result; @@ -358,14 +438,10 @@ static void migrate_check_parameter_int(QTestState *who, const char *parameter, static void migrate_set_parameter_int(QTestState *who, const char *parameter, long long value) { - QDict *rsp; - - rsp = qtest_qmp(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %lld } }", - parameter, value); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %lld } }", + parameter, value); migrate_check_parameter_int(who, parameter, value); } @@ -375,7 +451,8 @@ static char *migrate_get_parameter_str(QTestState *who, QDict *rsp; char *result; - rsp = wait_command(who, "{ 'execute': 'query-migrate-parameters' }"); + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); result = g_strdup(qdict_get_str(rsp, parameter)); qobject_unref(rsp); return result; @@ -391,82 +468,182 @@ static void migrate_check_parameter_str(QTestState *who, const char *parameter, 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); + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %s } }", + parameter, value); migrate_check_parameter_str(who, parameter, value); } -static void migrate_pause(QTestState *who) +static long long migrate_get_parameter_bool(QTestState *who, + const char *parameter) { QDict *rsp; + int result; - rsp = wait_command(who, "{ 'execute': 'migrate-pause' }"); + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); + result = qdict_get_bool(rsp, parameter); qobject_unref(rsp); + return !!result; +} + +static void migrate_check_parameter_bool(QTestState *who, const char *parameter, + int value) +{ + int result; + + result = migrate_get_parameter_bool(who, parameter); + g_assert_cmpint(result, ==, value); +} + +static void migrate_set_parameter_bool(QTestState *who, const char *parameter, + int value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %i } }", + parameter, value); + migrate_check_parameter_bool(who, parameter, value); +} + +static void migrate_ensure_non_converge(QTestState *who) +{ + /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */ + migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000); + migrate_set_parameter_int(who, "downtime-limit", 1); +} + +static void migrate_ensure_converge(QTestState *who) +{ + /* Should converge with 30s downtime + 1 gbs bandwidth limit */ + migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000); + migrate_set_parameter_int(who, "downtime-limit", 30 * 1000); +} + +/* + * Our goal is to ensure that we run a single full migration + * iteration, and also dirty memory, ensuring that at least + * one further iteration is required. + * + * We can't directly synchronize with the start of a migration + * so we have to apply some tricks monitoring memory that is + * transferred. + * + * Initially we set the migration bandwidth to an insanely + * low value, with tiny max downtime too. This basically + * guarantees migration will never complete. + * + * This will result in a test that is unacceptably slow though, + * so we can't let the entire migration pass run at this speed. + * Our intent is to let it run just long enough that we can + * prove data prior to the marker has been transferred *AND* + * also prove this transferred data is dirty again. + * + * Before migration starts, we write a 64-bit magic marker + * into a fixed location in the src VM RAM. + * + * Then watch dst memory until the marker appears. This is + * proof that start_address -> MAGIC_OFFSET_BASE has been + * transferred. + * + * Finally we go back to the source and read a byte just + * before the marker until we see it flip in value. This + * is proof that start_address -> MAGIC_OFFSET_BASE + * is now dirty again. + * + * IOW, we're guaranteed at least a 2nd migration pass + * at this point. + * + * We can now let migration run at full speed to finish + * the test + */ +static void migrate_prepare_for_dirty_mem(QTestState *from) +{ + /* + * The guest workflow iterates from start_address to + * end_address, writing 1 byte every TEST_MEM_PAGE_SIZE + * bytes. + * + * IOW, if we write to mem at a point which is NOT + * a multiple of TEST_MEM_PAGE_SIZE, our write won't + * conflict with the migration workflow. + * + * We put in a marker here, that we'll use to determine + * when the data has been transferred to the dst. + */ + qtest_writeq(from, start_address + MAGIC_OFFSET, MAGIC_MARKER); +} + +static void migrate_wait_for_dirty_mem(QTestState *from, + QTestState *to) +{ + uint64_t watch_address = start_address + MAGIC_OFFSET_BASE; + uint64_t marker_address = start_address + MAGIC_OFFSET; + uint8_t watch_byte; + + /* + * Wait for the MAGIC_MARKER to get transferred, as an + * indicator that a migration pass has made some known + * amount of progress. + */ + do { + usleep(1000 * 10); + } while (qtest_readq(to, marker_address) != MAGIC_MARKER); + + + /* If suspended, src only iterates once, and watch_byte may never change */ + if (src_state.suspend_me) { + return; + } + + /* + * Now ensure that already transferred bytes are + * dirty again from the guest workload. Note the + * guest byte value will wrap around and by chance + * match the original watch_byte. This is harmless + * as we'll eventually see a different value if we + * keep watching + */ + watch_byte = qtest_readb(from, watch_address); + do { + usleep(1000 * 10); + } while (qtest_readb(from, watch_address) == watch_byte); +} + + +static void migrate_pause(QTestState *who) +{ + qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }"); } static void migrate_continue(QTestState *who, const char *state) { - QDict *rsp; - - rsp = wait_command(who, - "{ 'execute': 'migrate-continue'," - " 'arguments': { 'state': %s } }", - state); - qobject_unref(rsp); + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-continue'," + " 'arguments': { 'state': %s } }", + state); } static void migrate_recover(QTestState *who, const char *uri) { - QDict *rsp; - - rsp = wait_command(who, - "{ 'execute': 'migrate-recover', " - " 'id': 'recover-cmd', " - " 'arguments': { 'uri': %s } }", - uri); - qobject_unref(rsp); + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-recover', " + " 'id': 'recover-cmd', " + " 'arguments': { 'uri': %s } }", + uri); } 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) -{ - QDict *rsp; - - rsp = qtest_qmp(who, - "{ 'execute': 'migrate-set-capabilities'," - "'arguments': { " - "'capabilities': [ { " - "'capability': %s, 'state': %i } ] } }", - capability, value); - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); + qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }"); } static void migrate_postcopy_start(QTestState *from, QTestState *to) { - QDict *rsp; - - rsp = wait_command(from, "{ 'execute': 'migrate-start-postcopy' }"); - qobject_unref(rsp); - - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); - } + qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }"); + wait_for_stop(from, &src_state); qtest_qmp_eventwait(to, "RESUME"); } @@ -483,22 +660,119 @@ typedef struct { bool use_dirty_ring; const char *opts_source; const char *opts_target; + /* suspend the src before migrating to dest. */ + bool suspend_me; } MigrateStart; +/* + * A hook that runs after the src and dst QEMUs have been + * created, but before the migration is started. This can + * be used to set migration parameters and capabilities. + * + * Returns: NULL, or a pointer to opaque state to be + * later passed to the TestMigrateFinishHook + */ +typedef void * (*TestMigrateStartHook)(QTestState *from, + QTestState *to); + +/* + * A hook that runs after the migration has finished, + * regardless of whether it succeeded or failed, but + * before QEMU has terminated (unless it self-terminated + * due to migration error) + * + * @opaque is a pointer to state previously returned + * by the TestMigrateStartHook if any, or NULL. + */ +typedef void (*TestMigrateFinishHook)(QTestState *from, + QTestState *to, + void *opaque); + +typedef struct { + /* Optional: fine tune start parameters */ + MigrateStart start; + + /* Required: the URI for the dst QEMU to listen on */ + const char *listen_uri; + + /* + * Optional: the URI for the src QEMU to connect to + * If NULL, then it will query the dst QEMU for its actual + * listening address and use that as the connect address. + * This allows for dynamically picking a free TCP port. + */ + const char *connect_uri; + + /* Optional: callback to run at start to set migration parameters */ + TestMigrateStartHook start_hook; + /* Optional: callback to run at finish to cleanup */ + TestMigrateFinishHook finish_hook; + + /* + * Optional: normally we expect the migration process to complete. + * + * There can be a variety of reasons and stages in which failure + * can happen during tests. + * + * If a failure is expected to happen at time of establishing + * the connection, then MIG_TEST_FAIL will indicate that the dst + * QEMU is expected to stay running and accept future migration + * connections. + * + * If a failure is expected to happen while processing the + * migration stream, then MIG_TEST_FAIL_DEST_QUIT_ERR will indicate + * that the dst QEMU is expected to quit with non-zero exit status + */ + enum { + /* This test should succeed, the default */ + MIG_TEST_SUCCEED = 0, + /* This test should fail, dest qemu should keep alive */ + MIG_TEST_FAIL, + /* This test should fail, dest qemu should fail with abnormal status */ + MIG_TEST_FAIL_DEST_QUIT_ERR, + /* The QMP command for this migration should fail with an error */ + MIG_TEST_QMP_ERROR, + } result; + + /* + * Optional: set number of migration passes to wait for, if live==true. + * If zero, then merely wait for a few MB of dirty data + */ + unsigned int iterations; + + /* + * Optional: whether the guest CPUs should be running during a precopy + * migration test. We used to always run with live but it took much + * longer so we reduced live tests to only the ones that have solid + * reason to be tested live-only. For each of the new test cases for + * precopy please provide justifications to use live explicitly (please + * refer to existing ones with live=true), or use live=off by default. + */ + bool live; + + /* Postcopy specific fields */ + void *postcopy_data; + bool postcopy_preempt; + bool postcopy_recovery_test_fail; +} MigrateCommon; + static int test_migrate_start(QTestState **from, QTestState **to, const char *uri, MigrateStart *args) { g_autofree gchar *arch_source = NULL; g_autofree gchar *arch_target = NULL; + /* options for source and target */ + g_autofree gchar *arch_opts = NULL; g_autofree gchar *cmd_source = NULL; g_autofree gchar *cmd_target = NULL; const gchar *ignore_stderr; - g_autofree char *bootpath = NULL; g_autofree char *shmem_opts = NULL; g_autofree char *shmem_path = NULL; + const char *kvm_opts = NULL; const char *arch = qtest_get_arch(); - const char *machine_opts = NULL; const char *memory_size; + const char *machine_alias, *machine_opts = ""; + g_autofree char *machine = NULL; if (args->use_shmem) { if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { @@ -507,53 +781,63 @@ static int test_migrate_start(QTestState **from, QTestState **to, } } - got_stop = false; - bootpath = g_strdup_printf("%s/bootsect", tmpfs); + dst_state = (QTestMigrationState) { }; + src_state = (QTestMigrationState) { }; + bootfile_create(tmpfs, args->suspend_me); + src_state.suspend_me = args->suspend_me; + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - /* the assembled x86 boot sector should be exactly one sector large */ - assert(sizeof(x86_bootsect) == 512); - init_bootfile(bootpath, x86_bootsect, sizeof(x86_bootsect)); memory_size = "150M"; - arch_source = g_strdup_printf("-drive file=%s,format=raw", bootpath); - arch_target = g_strdup(arch_source); + + if (g_str_equal(arch, "i386")) { + machine_alias = "pc"; + } else { + machine_alias = "q35"; + } + arch_opts = g_strdup_printf( + "-drive if=none,id=d0,file=%s,format=raw " + "-device ide-hd,drive=d0,secs=1,cyls=1,heads=1", bootpath); 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)); memory_size = "128M"; - arch_source = g_strdup_printf("-bios %s", bootpath); - arch_target = g_strdup(arch_source); + machine_alias = "s390-ccw-virtio"; + arch_opts = g_strdup_printf("-bios %s", bootpath); start_address = S390_TEST_MEM_START; end_address = S390_TEST_MEM_END; } else if (strcmp(arch, "ppc64") == 0) { - 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 " + arch_source = g_strdup_printf("-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(""); + machine_alias = "pseries"; + machine_opts = "vsmt=8"; + arch_opts = g_strdup("-nodefaults"); } else if (strcmp(arch, "aarch64") == 0) { - init_bootfile(bootpath, aarch64_kernel, sizeof(aarch64_kernel)); - 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); + machine_alias = "virt"; + machine_opts = "gic-version=3"; + arch_opts = g_strdup_printf("-cpu max -kernel %s", bootpath); start_address = ARM_TEST_MEM_START; end_address = ARM_TEST_MEM_END; - - g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE); } else { g_assert_not_reached(); } if (!getenv("QTEST_LOG") && args->hide_stderr) { +#ifndef _WIN32 ignore_stderr = "2>/dev/null"; +#else + /* + * On Windows the QEMU executable is created via CreateProcess() and + * IO redirection does not work, so don't bother adding IO redirection + * to the command line. + */ + ignore_stderr = ""; +#endif } else { ignore_stderr = ""; } @@ -564,47 +848,61 @@ static int test_migrate_start(QTestState **from, QTestState **to, "-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(""); } - cmd_source = g_strdup_printf("-accel kvm%s -accel tcg%s%s " + if (args->use_dirty_ring) { + kvm_opts = ",dirty-ring-size=4096"; + } + + machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC, + QEMU_ENV_DST); + + g_test_message("Using machine type: %s", machine); + + cmd_source = g_strdup_printf("-accel kvm%s -accel tcg " + "-machine %s,%s " "-name source,debug-threads=on " "-m %s " "-serial file:%s/src_serial " - "%s %s %s %s", - args->use_dirty_ring ? - ",dirty-ring-size=4096" : "", - machine_opts ? " -machine " : "", - machine_opts ? machine_opts : "", + "%s %s %s %s %s", + kvm_opts ? kvm_opts : "", + machine, machine_opts, memory_size, tmpfs, - arch_source, shmem_opts, + arch_opts ? arch_opts : "", + arch_source ? arch_source : "", + shmem_opts ? shmem_opts : "", args->opts_source ? args->opts_source : "", ignore_stderr); if (!args->only_target) { - *from = qtest_init(cmd_source); + *from = qtest_init_with_env(QEMU_ENV_SRC, cmd_source); + qtest_qmp_set_event_callback(*from, + migrate_watch_for_events, + &src_state); } - cmd_target = g_strdup_printf("-accel kvm%s -accel tcg%s%s " + cmd_target = g_strdup_printf("-accel kvm%s -accel tcg " + "-machine %s,%s " "-name target,debug-threads=on " "-m %s " "-serial file:%s/dest_serial " "-incoming %s " - "%s %s %s %s", - args->use_dirty_ring ? - ",dirty-ring-size=4096" : "", - machine_opts ? " -machine " : "", - machine_opts ? machine_opts : "", + "%s %s %s %s %s", + kvm_opts ? kvm_opts : "", + machine, machine_opts, memory_size, tmpfs, uri, - arch_target, shmem_opts, + arch_opts ? arch_opts : "", + arch_target ? arch_target : "", + shmem_opts ? shmem_opts : "", args->opts_target ? args->opts_target : "", ignore_stderr); - *to = qtest_init(cmd_target); + *to = qtest_init_with_env(QEMU_ENV_DST, cmd_target); + qtest_qmp_set_event_callback(*to, + migrate_watch_for_events, + &dst_state); /* * Remove shmem file immediately to avoid memory leak in test failed case. - * It's valid becase QEMU has already opened this file + * It's valid because QEMU has already opened this file */ if (args->use_shmem) { unlink(shmem_path); @@ -628,7 +926,7 @@ static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest) usleep(1000 * 10); } while (dest_byte_a == dest_byte_b); - qtest_qmp_discard_response(to, "{ 'execute' : 'stop'}"); + qtest_qmp_assert_success(to, "{ 'execute' : 'stop'}"); /* With it stopped, check nothing changes */ qtest_memread(to, start_address, &dest_byte_c, 1); @@ -641,10 +939,10 @@ static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest) qtest_quit(to); - cleanup("bootsect"); cleanup("migsocket"); cleanup("src_serial"); cleanup("dest_serial"); + cleanup(FILE_TEST_FILENAME); } #ifdef CONFIG_GNUTLS @@ -662,40 +960,37 @@ test_migrate_tls_psk_start_common(QTestState *from, { struct TestMigrateTLSPSKData *data = g_new0(struct TestMigrateTLSPSKData, 1); - QDict *rsp; data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs); data->pskfile = g_strdup_printf("%s/%s", data->workdir, QCRYPTO_TLS_CREDS_PSKFILE); - mkdir(data->workdir, 0700); + g_mkdir_with_parents(data->workdir, 0700); test_tls_psk_init(data->pskfile); if (mismatch) { data->workdiralt = g_strdup_printf("%s/tlscredspskalt0", tmpfs); data->pskfilealt = g_strdup_printf("%s/%s", data->workdiralt, QCRYPTO_TLS_CREDS_PSKFILE); - mkdir(data->workdiralt, 0700); + g_mkdir_with_parents(data->workdiralt, 0700); test_tls_psk_init_alt(data->pskfilealt); } - rsp = wait_command(from, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-psk'," - " 'id': 'tlscredspsk0'," - " 'endpoint': 'client'," - " 'dir': %s," - " 'username': 'qemu'} }", - data->workdir); - qobject_unref(rsp); + qtest_qmp_assert_success(from, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-psk'," + " 'id': 'tlscredspsk0'," + " 'endpoint': 'client'," + " 'dir': %s," + " 'username': 'qemu'} }", + data->workdir); - rsp = wait_command(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-psk'," - " 'id': 'tlscredspsk0'," - " 'endpoint': 'server'," - " 'dir': %s } }", - mismatch ? data->workdiralt : data->workdir); - qobject_unref(rsp); + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-psk'," + " 'id': 'tlscredspsk0'," + " 'endpoint': 'server'," + " 'dir': %s } }", + mismatch ? data->workdiralt : data->workdir); migrate_set_parameter_str(from, "tls-creds", "tlscredspsk0"); migrate_set_parameter_str(to, "tls-creds", "tlscredspsk0"); @@ -766,7 +1061,6 @@ test_migrate_tls_x509_start_common(QTestState *from, TestMigrateTLSX509 *args) { TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1); - QDict *rsp; data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs); data->keyfile = g_strdup_printf("%s/key.pem", data->workdir); @@ -779,12 +1073,20 @@ test_migrate_tls_x509_start_common(QTestState *from, data->clientcert = g_strdup_printf("%s/client-cert.pem", data->workdir); } - mkdir(data->workdir, 0700); + g_mkdir_with_parents(data->workdir, 0700); test_tls_init(data->keyfile); +#ifndef _WIN32 g_assert(link(data->keyfile, data->serverkey) == 0); +#else + g_assert(CreateHardLink(data->serverkey, data->keyfile, NULL) != 0); +#endif if (args->clientcert) { +#ifndef _WIN32 g_assert(link(data->keyfile, data->clientkey) == 0); +#else + g_assert(CreateHardLink(data->clientkey, data->keyfile, NULL) != 0); +#endif } TLS_ROOT_REQ_SIMPLE(cacertreq, data->cacert); @@ -801,40 +1103,38 @@ test_migrate_tls_x509_start_common(QTestState *from, args->certhostname, args->certipaddr); - rsp = wait_command(from, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-x509'," - " 'id': 'tlscredsx509client0'," - " 'endpoint': 'client'," - " 'dir': %s," - " 'sanity-check': true," - " 'verify-peer': true} }", - data->workdir); - qobject_unref(rsp); + qtest_qmp_assert_success(from, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-x509'," + " 'id': 'tlscredsx509client0'," + " 'endpoint': 'client'," + " 'dir': %s," + " 'sanity-check': true," + " 'verify-peer': true} }", + data->workdir); migrate_set_parameter_str(from, "tls-creds", "tlscredsx509client0"); if (args->certhostname) { migrate_set_parameter_str(from, "tls-hostname", args->certhostname); } - rsp = wait_command(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-x509'," - " 'id': 'tlscredsx509server0'," - " 'endpoint': 'server'," - " 'dir': %s," - " 'sanity-check': true," - " 'verify-peer': %i} }", - data->workdir, args->verifyclient); - qobject_unref(rsp); + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-x509'," + " 'id': 'tlscredsx509server0'," + " 'endpoint': 'server'," + " 'dir': %s," + " 'sanity-check': true," + " 'verify-peer': %i} }", + data->workdir, args->verifyclient); migrate_set_parameter_str(to, "tls-creds", "tlscredsx509server0"); if (args->authzclient) { - rsp = wait_command(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'authz-simple'," - " 'id': 'tlsauthz0'," - " 'identity': %s} }", - "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME); + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'authz-simple'," + " 'id': 'tlsauthz0'," + " 'identity': %s} }", + "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME); migrate_set_parameter_str(to, "tls-authz", "tlsauthz0"); } @@ -955,48 +1255,104 @@ test_migrate_tls_x509_finish(QTestState *from, TestMigrateTLSX509Data *data = opaque; test_tls_cleanup(data->keyfile); - unlink(data->cacert); - unlink(data->servercert); - unlink(data->serverkey); - unlink(data->clientcert); - unlink(data->clientkey); - rmdir(data->workdir); - - g_free(data->workdir); g_free(data->keyfile); + + unlink(data->cacert); + g_free(data->cacert); + unlink(data->servercert); + g_free(data->servercert); + unlink(data->serverkey); + g_free(data->serverkey); + + if (data->clientcert) { + unlink(data->clientcert); + g_free(data->clientcert); + } + if (data->clientkey) { + unlink(data->clientkey); + g_free(data->clientkey); + } + + rmdir(data->workdir); + g_free(data->workdir); + g_free(data); } #endif /* CONFIG_TASN1 */ #endif /* CONFIG_GNUTLS */ +static void * +test_migrate_compress_start(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "compress-level", 1); + migrate_set_parameter_int(from, "compress-threads", 4); + migrate_set_parameter_bool(from, "compress-wait-thread", true); + migrate_set_parameter_int(to, "decompress-threads", 4); + + migrate_set_capability(from, "compress", true); + migrate_set_capability(to, "compress", true); + + return NULL; +} + +static void * +test_migrate_compress_nowait_start(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "compress-level", 9); + migrate_set_parameter_int(from, "compress-threads", 1); + migrate_set_parameter_bool(from, "compress-wait-thread", false); + migrate_set_parameter_int(to, "decompress-threads", 1); + + migrate_set_capability(from, "compress", true); + migrate_set_capability(to, "compress", true); + + return NULL; +} + static int migrate_postcopy_prepare(QTestState **from_ptr, QTestState **to_ptr, - MigrateStart *args) + MigrateCommon *args) { - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, args)) { + if (test_migrate_start(&from, &to, "defer", &args->start)) { return -1; } + if (args->start_hook) { + args->postcopy_data = args->start_hook(from, to); + } + migrate_set_capability(from, "postcopy-ram", true); migrate_set_capability(to, "postcopy-ram", true); migrate_set_capability(to, "postcopy-blocktime", true); - /* 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. - */ - migrate_set_parameter_int(from, "max-bandwidth", 30000000); - migrate_set_parameter_int(from, "downtime-limit", 1); + if (args->postcopy_preempt) { + migrate_set_capability(from, "postcopy-preempt", true); + migrate_set_capability(to, "postcopy-preempt", true); + } + + migrate_ensure_non_converge(from); + + migrate_prepare_for_dirty_mem(from); + qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," + " 'arguments': { " + " 'channels': [ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ] } }"); /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); + wait_for_suspend(from, &src_state); + g_autofree char *uri = migrate_get_socket_address(to, "socket-address"); migrate_qmp(from, uri, "{}"); - wait_for_migration_pass(from); + migrate_wait_for_dirty_mem(from, to); *from_ptr = from; *to_ptr = to; @@ -1004,10 +1360,16 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, return 0; } -static void migrate_postcopy_complete(QTestState *from, QTestState *to) +static void migrate_postcopy_complete(QTestState *from, QTestState *to, + MigrateCommon *args) { wait_for_migration_complete(from); + if (args->start.suspend_me) { + /* wakeup succeeds only if guest is suspended */ + qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); + } + /* Make sure we get at least one "B" on destination */ wait_for_serial("dest_serial"); @@ -1015,30 +1377,165 @@ static void migrate_postcopy_complete(QTestState *from, QTestState *to) read_blocktime(to); } + if (args->finish_hook) { + args->finish_hook(from, to, args->postcopy_data); + args->postcopy_data = NULL; + } + test_migrate_end(from, to, true); } +static void test_postcopy_common(MigrateCommon *args) +{ + QTestState *from, *to; + + if (migrate_postcopy_prepare(&from, &to, args)) { + return; + } + migrate_postcopy_start(from, to); + migrate_postcopy_complete(from, to, args); +} + static void test_postcopy(void) { - MigrateStart args = {}; - QTestState *from, *to; + MigrateCommon args = { }; - if (migrate_postcopy_prepare(&from, &to, &args)) { - return; - } - migrate_postcopy_start(from, to); - migrate_postcopy_complete(from, to); + test_postcopy_common(&args); } -static void test_postcopy_recovery(void) +static void test_postcopy_suspend(void) { - MigrateStart args = { - .hide_stderr = true, + MigrateCommon args = { + .start.suspend_me = true, }; + + test_postcopy_common(&args); +} + +static void test_postcopy_compress(void) +{ + MigrateCommon args = { + .start_hook = test_migrate_compress_start + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_preempt(void) +{ + MigrateCommon args = { + .postcopy_preempt = true, + }; + + test_postcopy_common(&args); +} + +#ifdef CONFIG_GNUTLS +static void test_postcopy_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = test_migrate_tls_psk_start_match, + .finish_hook = test_migrate_tls_psk_finish, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_preempt_tls_psk(void) +{ + MigrateCommon args = { + .postcopy_preempt = true, + .start_hook = test_migrate_tls_psk_start_match, + .finish_hook = test_migrate_tls_psk_finish, + }; + + test_postcopy_common(&args); +} +#endif + +static void wait_for_postcopy_status(QTestState *one, const char *status) +{ + wait_for_migration_status(one, status, + (const char * []) { "failed", "active", + "completed", NULL }); +} + +#ifndef _WIN32 +static void postcopy_recover_fail(QTestState *from, QTestState *to) +{ + int ret, pair1[2], pair2[2]; + char c; + + /* Create two unrelated socketpairs */ + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair1); + g_assert_cmpint(ret, ==, 0); + + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair2); + g_assert_cmpint(ret, ==, 0); + + /* + * Give the guests unpaired ends of the sockets, so they'll all blocked + * at reading. This mimics a wrong channel established. + */ + qtest_qmp_fds_assert_success(from, &pair1[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + qtest_qmp_fds_assert_success(to, &pair2[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + + /* + * Write the 1st byte as QEMU_VM_COMMAND (0x8) for the dest socket, to + * emulate the 1st byte of a real recovery, but stops from there to + * keep dest QEMU in RECOVER. This is needed so that we can kick off + * the recover process on dest QEMU (by triggering the G_IO_IN event). + * + * NOTE: this trick is not needed on src QEMUs, because src doesn't + * rely on an pre-existing G_IO_IN event, so it will always trigger the + * upcoming recovery anyway even if it can read nothing. + */ +#define QEMU_VM_COMMAND 0x08 + c = QEMU_VM_COMMAND; + ret = send(pair2[1], &c, 1, 0); + g_assert_cmpint(ret, ==, 1); + + migrate_recover(to, "fd:fd-mig"); + migrate_qmp(from, "fd:fd-mig", "{'resume': true}"); + + /* + * Make sure both QEMU instances will go into RECOVER stage, then test + * kicking them out using migrate-pause. + */ + wait_for_postcopy_status(from, "postcopy-recover"); + wait_for_postcopy_status(to, "postcopy-recover"); + + /* + * This would be issued by the admin upon noticing the hang, we should + * make sure we're able to kick this out. + */ + migrate_pause(from); + wait_for_postcopy_status(from, "postcopy-paused"); + + /* Do the same test on dest */ + migrate_pause(to); + wait_for_postcopy_status(to, "postcopy-paused"); + + close(pair1[0]); + close(pair1[1]); + close(pair2[0]); + close(pair2[1]); +} +#endif /* _WIN32 */ + +static void test_postcopy_recovery_common(MigrateCommon *args) +{ QTestState *from, *to; g_autofree char *uri = NULL; - if (migrate_postcopy_prepare(&from, &to, &args)) { + /* Always hide errors for postcopy recover tests since they're expected */ + args->start.hide_stderr = true; + + if (migrate_postcopy_prepare(&from, &to, args)) { return; } @@ -1065,9 +1562,19 @@ static void test_postcopy_recovery(void) * migrate-recover command can only succeed if destination machine * is in the paused state */ - wait_for_migration_status(to, "postcopy-paused", - (const char * []) { "failed", "active", - "completed", NULL }); + wait_for_postcopy_status(to, "postcopy-paused"); + wait_for_postcopy_status(from, "postcopy-paused"); + +#ifndef _WIN32 + if (args->postcopy_recovery_test_fail) { + /* + * Test when a wrong socket specified for recover, and then the + * ability to kick it out, and continue with a correct socket. + */ + postcopy_recover_fail(from, to); + /* continue with a good recovery */ + } +#endif /* _WIN32 */ /* * Create a new socket to emulate a new channel that is different @@ -1081,17 +1588,77 @@ static void test_postcopy_recovery(void) * Try to rebuild the migration channel using the resume flag and * the newly created channel */ - wait_for_migration_status(from, "postcopy-paused", - (const char * []) { "failed", "active", - "completed", NULL }); migrate_qmp(from, uri, "{'resume': true}"); /* Restore the postcopy bandwidth to unlimited */ migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); - migrate_postcopy_complete(from, to); + migrate_postcopy_complete(from, to, args); } +static void test_postcopy_recovery(void) +{ + MigrateCommon args = { }; + + test_postcopy_recovery_common(&args); +} + +static void test_postcopy_recovery_compress(void) +{ + MigrateCommon args = { + .start_hook = test_migrate_compress_start + }; + + test_postcopy_recovery_common(&args); +} + +#ifndef _WIN32 +static void test_postcopy_recovery_double_fail(void) +{ + MigrateCommon args = { + .postcopy_recovery_test_fail = true, + }; + + test_postcopy_recovery_common(&args); +} +#endif /* _WIN32 */ + +#ifdef CONFIG_GNUTLS +static void test_postcopy_recovery_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = test_migrate_tls_psk_start_match, + .finish_hook = test_migrate_tls_psk_finish, + }; + + test_postcopy_recovery_common(&args); +} +#endif + +static void test_postcopy_preempt_recovery(void) +{ + MigrateCommon args = { + .postcopy_preempt = true, + }; + + test_postcopy_recovery_common(&args); +} + +#ifdef CONFIG_GNUTLS +/* This contains preempt+recovery+tls test altogether */ +static void test_postcopy_preempt_all(void) +{ + MigrateCommon args = { + .postcopy_preempt = true, + .start_hook = test_migrate_tls_psk_start_match, + .finish_hook = test_migrate_tls_psk_finish, + }; + + test_postcopy_recovery_common(&args); +} + +#endif + static void test_baddest(void) { MigrateStart args = { @@ -1107,141 +1674,223 @@ static void test_baddest(void) test_migrate_end(from, to, false); } -/* - * A hook that runs after the src and dst QEMUs have been - * created, but before the migration is started. This can - * be used to set migration parameters and capabilities. - * - * Returns: NULL, or a pointer to opaque state to be - * later passed to the TestMigrateFinishHook - */ -typedef void * (*TestMigrateStartHook)(QTestState *from, - QTestState *to); +#ifndef _WIN32 +static void test_analyze_script(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + }; + QTestState *from, *to; + g_autofree char *uri = NULL; + g_autofree char *file = NULL; + int pid, wstatus; + const char *python = g_getenv("PYTHON"); -/* - * A hook that runs after the migration has finished, - * regardless of whether it succeeded or failed, but - * before QEMU has terminated (unless it self-terminated - * due to migration error) - * - * @opaque is a pointer to state previously returned - * by the TestMigrateStartHook if any, or NULL. - */ -typedef void (*TestMigrateFinishHook)(QTestState *from, - QTestState *to, - void *opaque); + if (!python) { + g_test_skip("PYTHON variable not set"); + return; + } -typedef struct { - /* Optional: fine tune start parameters */ - MigrateStart start; - - /* Required: the URI for the dst QEMU to listen on */ - const char *listen_uri; + /* dummy url */ + if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + return; + } /* - * Optional: the URI for the src QEMU to connect to - * If NULL, then it will query the dst QEMU for its actual - * listening address and use that as the connect address. - * This allows for dynamically picking a free TCP port. + * Setting these two capabilities causes the "configuration" + * vmstate to include subsections for them. The script needs to + * parse those subsections properly. */ - const char *connect_uri; + migrate_set_capability(from, "validate-uuid", true); + migrate_set_capability(from, "x-ignore-shared", true); - /* Optional: callback to run at start to set migration parameters */ - TestMigrateStartHook start_hook; - /* Optional: callback to run at finish to cleanup */ - TestMigrateFinishHook finish_hook; + file = g_strdup_printf("%s/migfile", tmpfs); + uri = g_strdup_printf("exec:cat > %s", file); - /* - * Optional: normally we expect the migration process to complete. - * - * There can be a variety of reasons and stages in which failure - * can happen during tests. - * - * If a failure is expected to happen at time of establishing - * the connection, then MIG_TEST_FAIL will indicate that the dst - * QEMU is expected to stay running and accept future migration - * connections. - * - * If a failure is expected to happen while processing the - * migration stream, then MIG_TEST_FAIL_DEST_QUIT_ERR will indicate - * that the dst QEMU is expected to quit with non-zero exit status - */ - enum { - /* This test should succeed, the default */ - MIG_TEST_SUCCEED = 0, - /* This test should fail, dest qemu should keep alive */ - MIG_TEST_FAIL, - /* This test should fail, dest qemu should fail with abnormal status */ - MIG_TEST_FAIL_DEST_QUIT_ERR, - } result; + migrate_ensure_converge(from); + migrate_qmp(from, uri, "{}"); + wait_for_migration_complete(from); - /* Optional: set number of migration passes to wait for */ - unsigned int iterations; -} MigrateCommon; + pid = fork(); + if (!pid) { + close(1); + open("/dev/null", O_WRONLY); + execl(python, python, ANALYZE_SCRIPT, "-f", file, NULL); + g_assert_not_reached(); + } + + g_assert(waitpid(pid, &wstatus, 0) == pid); + if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0) { + g_test_message("Failed to analyze the migration stream"); + g_test_fail(); + } + test_migrate_end(from, to, false); + cleanup("migfile"); +} +#endif static void test_precopy_common(MigrateCommon *args) { QTestState *from, *to; void *data_hook = NULL; + g_autofree char *connect_uri = NULL; if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { 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); - if (args->start_hook) { data_hook = args->start_hook(from, to); } /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - if (!args->connect_uri) { - g_autofree char *local_connect_uri = - migrate_get_socket_address(to, "socket-address"); - migrate_qmp(from, local_connect_uri, "{}"); - } else { - migrate_qmp(from, args->connect_uri, "{}"); + if (args->result == MIG_TEST_SUCCEED) { + wait_for_serial("src_serial"); + wait_for_suspend(from, &src_state); } + if (args->live) { + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + } else { + /* + * Testing non-live migration, we allow it to run at + * full speed to ensure short test case duration. + * For tests expected to fail, we don't need to + * change anything. + */ + if (args->result == MIG_TEST_SUCCEED) { + qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); + wait_for_stop(from, &src_state); + migrate_ensure_converge(from); + } + } + + if (!args->connect_uri) { + connect_uri = migrate_get_socket_address(to, "socket-address"); + } else { + connect_uri = g_strdup(args->connect_uri); + } + + if (args->result == MIG_TEST_QMP_ERROR) { + migrate_qmp_fail(from, connect_uri, "{}"); + goto finish; + } + + migrate_qmp(from, connect_uri, "{}"); if (args->result != MIG_TEST_SUCCEED) { bool allow_active = args->result == MIG_TEST_FAIL; wait_for_migration_fail(from, allow_active); if (args->result == MIG_TEST_FAIL_DEST_QUIT_ERR) { - qtest_set_expected_status(to, 1); + qtest_set_expected_status(to, EXIT_FAILURE); } } else { - if (args->iterations) { - while (args->iterations--) { + if (args->live) { + /* + * For initial iteration(s) we must do a full pass, + * but for the final iteration, we need only wait + * for some dirty mem before switching to converge + */ + while (args->iterations > 1) { wait_for_migration_pass(from); + args->iterations--; } + migrate_wait_for_dirty_mem(from, to); + + migrate_ensure_converge(from); + + /* + * We do this first, as it has a timeout to stop us + * hanging forever if migration didn't converge + */ + wait_for_migration_complete(from); + + wait_for_stop(from, &src_state); + } else { - wait_for_migration_pass(from); + wait_for_migration_complete(from); + /* + * Must wait for dst to finish reading all incoming + * data on the socket before issuing 'cont' otherwise + * it'll be ignored + */ + wait_for_migration_complete(to); + + qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); } - migrate_set_parameter_int(from, "downtime-limit", CONVERGE_DOWNTIME); + wait_for_resume(to, &dst_state); - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); + if (args->start.suspend_me) { + /* wakeup succeeds only if guest is suspended */ + qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); } - qtest_qmp_eventwait(to, "RESUME"); - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); } +finish: + if (args->finish_hook) { + args->finish_hook(from, to, data_hook); + } + + test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); +} + +static void test_file_common(MigrateCommon *args, bool stop_src) +{ + QTestState *from, *to; + void *data_hook = NULL; + g_autofree char *connect_uri = g_strdup(args->connect_uri); + + if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { + return; + } + + /* + * File migration is never live. We can keep the source VM running + * during migration, but the destination will not be running + * concurrently. + */ + g_assert_false(args->live); + + if (args->start_hook) { + data_hook = args->start_hook(from, to); + } + + migrate_ensure_converge(from); + wait_for_serial("src_serial"); + + if (stop_src) { + qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); + wait_for_stop(from, &src_state); + } + + if (args->result == MIG_TEST_QMP_ERROR) { + migrate_qmp_fail(from, connect_uri, "{}"); + goto finish; + } + + migrate_qmp(from, connect_uri, "{}"); + wait_for_migration_complete(from); + + /* + * We need to wait for the source to finish before starting the + * destination. + */ + migrate_incoming_qmp(to, connect_uri, "{}"); + wait_for_migration_complete(to); + + if (stop_src) { + qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); + } + wait_for_resume(to, &dst_state); + + wait_for_serial("dest_serial"); + +finish: if (args->finish_hook) { args->finish_hook(from, to, data_hook); } @@ -1255,11 +1904,44 @@ static void test_precopy_unix_plain(void) MigrateCommon args = { .listen_uri = uri, .connect_uri = uri, + /* + * The simplest use case of precopy, covering smoke tests of + * get-dirty-log dirty tracking. + */ + .live = true, }; test_precopy_common(&args); } +static void test_precopy_unix_suspend_live(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + /* + * despite being live, the test is fast because the src + * suspends immediately. + */ + .live = true, + .start.suspend_me = true, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_suspend_notlive(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + .start.suspend_me = true, + }; + + test_precopy_common(&args); +} static void test_precopy_unix_dirty_ring(void) { @@ -1270,6 +1952,11 @@ static void test_precopy_unix_dirty_ring(void) }, .listen_uri = uri, .connect_uri = uri, + /* + * Besides the precopy/unix basic test, cover dirty ring interface + * rather than get-dirty-log. + */ + .live = true, }; test_precopy_common(&args); @@ -1333,6 +2020,9 @@ static void test_ignore_shared(void) return; } + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + migrate_set_capability(from, "x-ignore-shared", true); migrate_set_capability(to, "x-ignore-shared", true); @@ -1341,11 +2031,9 @@ static void test_ignore_shared(void) migrate_qmp(from, uri, "{}"); - wait_for_migration_pass(from); + migrate_wait_for_dirty_mem(from, to); - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); - } + wait_for_stop(from, &src_state); qtest_qmp_eventwait(to, "RESUME"); @@ -1377,15 +2065,229 @@ static void test_precopy_unix_xbzrle(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = uri, - .start_hook = test_migrate_xbzrle_start, - .iterations = 2, + /* + * XBZRLE needs pages to be modified when doing the 2nd+ round + * iteration to have real data pushed to the stream. + */ + .live = true, }; test_precopy_common(&args); } +static void test_precopy_unix_compress(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = test_migrate_compress_start, + /* + * Test that no invalid thread state is left over from + * the previous iteration. + */ + .iterations = 2, + /* + * We make sure the compressor can always work well even if guest + * memory is changing. See commit 34ab9e9743 where we used to fix + * a bug when only trigger-able with guest memory changing. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_compress_nowait(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = test_migrate_compress_nowait_start, + /* + * Test that no invalid thread state is left over from + * the previous iteration. + */ + .iterations = 2, + /* Same reason for the wait version of precopy compress test */ + .live = true, + }; + + test_precopy_common(&args); +} + +static void test_precopy_file(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + }; + + test_file_common(&args, true); +} + +static void file_offset_finish_hook(QTestState *from, QTestState *to, + void *opaque) +{ +#if defined(__linux__) + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + size_t size = FILE_TEST_OFFSET + sizeof(QEMU_VM_FILE_MAGIC); + uintptr_t *addr, *p; + int fd; + + fd = open(path, O_RDONLY); + g_assert(fd != -1); + addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + g_assert(addr != MAP_FAILED); + + /* + * Ensure the skipped offset contains zeros and the migration + * stream starts at the right place. + */ + p = addr; + while (p < addr + FILE_TEST_OFFSET / sizeof(uintptr_t)) { + g_assert(*p == 0); + p++; + } + g_assert_cmpint(cpu_to_be64(*p) >> 32, ==, QEMU_VM_FILE_MAGIC); + + munmap(addr, size); + close(fd); +#endif +} + +static void test_precopy_file_offset(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=%d", tmpfs, + FILE_TEST_FILENAME, + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .finish_hook = file_offset_finish_hook, + }; + + test_file_common(&args, false); +} + +static void test_precopy_file_offset_bad(void) +{ + /* using a value not supported by qemu_strtosz() */ + g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=0x20M", + tmpfs, FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .result = MIG_TEST_QMP_ERROR, + }; + + test_file_common(&args, false); +} + +static void *test_mode_reboot_start(QTestState *from, QTestState *to) +{ + migrate_set_parameter_str(from, "mode", "cpr-reboot"); + migrate_set_parameter_str(to, "mode", "cpr-reboot"); + + migrate_set_capability(from, "x-ignore-shared", true); + migrate_set_capability(to, "x-ignore-shared", true); + + return NULL; +} + +static void *migrate_mapped_ram_start(QTestState *from, QTestState *to) +{ + migrate_set_capability(from, "mapped-ram", true); + migrate_set_capability(to, "mapped-ram", true); + + return NULL; +} + +static void test_mode_reboot(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .start.use_shmem = true, + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = test_mode_reboot_start + }; + + test_file_common(&args, true); +} + +static void test_precopy_file_mapped_ram_live(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_mapped_ram_start, + }; + + test_file_common(&args, false); +} + +static void test_precopy_file_mapped_ram(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_mapped_ram_start, + }; + + test_file_common(&args, true); +} + +static void *migrate_multifd_mapped_ram_start(QTestState *from, QTestState *to) +{ + migrate_mapped_ram_start(from, to); + + migrate_set_parameter_int(from, "multifd-channels", 4); + migrate_set_parameter_int(to, "multifd-channels", 4); + + migrate_set_capability(from, "multifd", true); + migrate_set_capability(to, "multifd", true); + + return NULL; +} + +static void test_multifd_file_mapped_ram_live(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_multifd_mapped_ram_start, + }; + + test_file_common(&args, false); +} + +static void test_multifd_file_mapped_ram(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_multifd_mapped_ram_start, + }; + + test_file_common(&args, true); +} + + static void test_precopy_tcp_plain(void) { MigrateCommon args = { @@ -1395,6 +2297,33 @@ static void test_precopy_tcp_plain(void) test_precopy_common(&args); } +static void *test_migrate_switchover_ack_start(QTestState *from, QTestState *to) +{ + + migrate_set_capability(from, "return-path", true); + migrate_set_capability(to, "return-path", true); + + migrate_set_capability(from, "switchover-ack", true); + migrate_set_capability(to, "switchover-ack", true); + + return NULL; +} + +static void test_precopy_tcp_switchover_ack(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = test_migrate_switchover_ack_start, + /* + * Source VM must be running in order to consider the switchover ACK + * when deciding to do switchover or not. + */ + .live = true, + }; + + test_precopy_common(&args); +} + #ifdef CONFIG_GNUTLS static void test_precopy_tcp_tls_psk_match(void) { @@ -1514,34 +2443,30 @@ static void test_precopy_tcp_tls_x509_reject_anon_client(void) #endif /* CONFIG_TASN1 */ #endif /* CONFIG_GNUTLS */ +#ifndef _WIN32 static void *test_migrate_fd_start_hook(QTestState *from, QTestState *to) { - QDict *rsp; int ret; int pair[2]; /* Create two connected sockets for migration */ - ret = socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); g_assert_cmpint(ret, ==, 0); /* Send the 1st socket to the target */ - rsp = wait_command_fd(to, pair[0], - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - qobject_unref(rsp); + qtest_qmp_fds_assert_success(to, &pair[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); close(pair[0]); /* Start incoming migration from the 1st socket */ - rsp = wait_command(to, "{ 'execute': 'migrate-incoming'," - " 'arguments': { 'uri': 'fd:fd-mig' }}"); - qobject_unref(rsp); + migrate_incoming_qmp(to, "fd:fd-mig", "{}"); /* Send the 2nd socket to the target */ - rsp = wait_command_fd(from, pair[1], - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - qobject_unref(rsp); + qtest_qmp_fds_assert_success(from, &pair[1], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); close(pair[1]); return NULL; @@ -1572,7 +2497,7 @@ static void test_migrate_fd_finish_hook(QTestState *from, qobject_unref(rsp); } -static void test_migrate_fd_proto(void) +static void test_migrate_precopy_fd_socket(void) { MigrateCommon args = { .listen_uri = "defer", @@ -1583,6 +2508,46 @@ static void test_migrate_fd_proto(void) test_precopy_common(&args); } +static void *migrate_precopy_fd_file_start(QTestState *from, QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + int src_flags = O_CREAT | O_RDWR; + int dst_flags = O_CREAT | O_RDWR; + int fds[2]; + + fds[0] = open(file, src_flags, 0660); + assert(fds[0] != -1); + + fds[1] = open(file, dst_flags, 0660); + assert(fds[1] != -1); + + + qtest_qmp_fds_assert_success(to, &fds[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + + qtest_qmp_fds_assert_success(from, &fds[1], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + + close(fds[0]); + close(fds[1]); + + return NULL; +} + +static void test_migrate_precopy_fd_file(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .connect_uri = "fd:fd-mig", + .start_hook = migrate_precopy_fd_file_start, + .finish_hook = test_migrate_fd_finish_hook + }; + test_file_common(&args, true); +} +#endif /* _WIN32 */ + static void do_test_validate_uuid(MigrateStart *args, bool should_fail) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); @@ -1606,7 +2571,7 @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail) migrate_qmp(from, uri, "{}"); if (should_fail) { - qtest_set_expected_status(to, 1); + qtest_set_expected_status(to, EXIT_FAILURE); wait_for_migration_fail(from, true); } else { wait_for_migration_complete(from); @@ -1656,27 +2621,34 @@ static void test_validate_uuid_dst_not_set(void) do_test_validate_uuid(&args, false); } +/* + * The way auto_converge works, we need to do too many passes to + * run this test. Auto_converge logic is only run once every + * three iterations, so: + * + * - 3 iterations without auto_converge enabled + * - 3 iterations with pct = 5 + * - 3 iterations with pct = 30 + * - 3 iterations with pct = 55 + * - 3 iterations with pct = 80 + * - 3 iterations with pct = 95 (max(95, 80 + 25)) + * + * To make things even worse, we need to run the initial stage at + * 3MB/s so we enter autoconverge even when host is (over)loaded. + */ static void test_migrate_auto_converge(void) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); MigrateStart args = {}; QTestState *from, *to; - int64_t remaining, percentage; + int64_t percentage; /* * We want the test to be stable and as fast as possible. - * E.g., with 1Gb/s bandwith migration may pass without throttling, + * E.g., with 1Gb/s bandwidth migration may pass without throttling, * so we need to decrease a bandwidth. */ - const int64_t init_pct = 5, inc_pct = 50, max_pct = 95; - const int64_t max_bandwidth = 400000000; /* ~400Mb/s */ - const int64_t downtime_limit = 250; /* 250ms */ - /* - * We migrate through unix-socket (> 500Mb/s). - * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s). - * So, we can predict expected_threshold - */ - const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; + const int64_t init_pct = 5, inc_pct = 25, max_pct = 95; if (test_migrate_start(&from, &to, uri, &args)) { return; @@ -1691,8 +2663,7 @@ static void test_migrate_auto_converge(void) * Set the initial parameters so that the migration could not converge * without throttling. */ - migrate_set_parameter_int(from, "downtime-limit", 1); - migrate_set_parameter_int(from, "max-bandwidth", 100000000); /* ~100Mb/s */ + migrate_ensure_non_converge(from); /* To check remaining size after precopy */ migrate_set_capability(from, "pause-before-switchover", true); @@ -1704,16 +2675,18 @@ static void test_migrate_auto_converge(void) /* Wait for throttling begins */ percentage = 0; - while (percentage == 0) { + do { percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); - usleep(100); - g_assert_false(got_stop); - } - /* The first percentage of throttling should be equal to init_pct */ - g_assert_cmpint(percentage, ==, init_pct); + if (percentage != 0) { + break; + } + usleep(20); + g_assert_false(src_state.stop_seen); + } while (true); + /* The first percentage of throttling should be at least init_pct */ + g_assert_cmpint(percentage, >=, init_pct); /* Now, when we tested that throttling works, let it converge */ - migrate_set_parameter_int(from, "downtime-limit", downtime_limit); - migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth); + migrate_ensure_converge(from); /* * Wait for pre-switchover status to check last throttle percentage @@ -1724,11 +2697,6 @@ static void test_migrate_auto_converge(void) /* The final percentage of throttling shouldn't be greater than max_pct */ percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); g_assert_cmpint(percentage, <=, max_pct); - - remaining = read_ram_property_int(from, "remaining"); - g_assert_cmpint(remaining, <, - (expected_threshold + expected_threshold / 100)); - migrate_continue(from, "pre-switchover"); qtest_qmp_eventwait(to, "RESUME"); @@ -1736,7 +2704,6 @@ static void test_migrate_auto_converge(void) wait_for_serial("dest_serial"); wait_for_migration_complete(from); - test_migrate_end(from, to, true); } @@ -1745,8 +2712,6 @@ test_migrate_precopy_tcp_multifd_start_common(QTestState *from, QTestState *to, const char *method) { - QDict *rsp; - migrate_set_parameter_int(from, "multifd-channels", 16); migrate_set_parameter_int(to, "multifd-channels", 16); @@ -1757,9 +2722,7 @@ test_migrate_precopy_tcp_multifd_start_common(QTestState *from, 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); + migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); return NULL; } @@ -1771,10 +2734,35 @@ test_migrate_precopy_tcp_multifd_start(QTestState *from, return test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); } +static void * +test_migrate_precopy_tcp_multifd_start_zero_page_legacy(QTestState *from, + QTestState *to) +{ + test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); + migrate_set_parameter_str(from, "zero-page-detection", "legacy"); + return NULL; +} + +static void * +test_migration_precopy_tcp_multifd_start_no_zero_page(QTestState *from, + QTestState *to) +{ + test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); + migrate_set_parameter_str(from, "zero-page-detection", "none"); + return NULL; +} + static void * test_migrate_precopy_tcp_multifd_zlib_start(QTestState *from, QTestState *to) { + /* + * Overloading this test to also check that set_parameter does not error. + * This is also done in the tests for the other compression methods. + */ + migrate_set_parameter_int(from, "multifd-zlib-level", 2); + migrate_set_parameter_int(to, "multifd-zlib-level", 2); + return test_migrate_precopy_tcp_multifd_start_common(from, to, "zlib"); } @@ -1783,6 +2771,9 @@ static void * test_migrate_precopy_tcp_multifd_zstd_start(QTestState *from, QTestState *to) { + migrate_set_parameter_int(from, "multifd-zstd-level", 2); + migrate_set_parameter_int(to, "multifd-zstd-level", 2); + return test_migrate_precopy_tcp_multifd_start_common(from, to, "zstd"); } #endif /* CONFIG_ZSTD */ @@ -1792,6 +2783,42 @@ static void test_multifd_tcp_none(void) MigrateCommon args = { .listen_uri = "defer", .start_hook = test_migrate_precopy_tcp_multifd_start, + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + .live = true, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_zero_page_legacy(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = test_migrate_precopy_tcp_multifd_start_zero_page_legacy, + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + .live = true, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_no_zero_page(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = test_migration_precopy_tcp_multifd_start_no_zero_page, + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + .live = true, }; test_precopy_common(&args); } @@ -1990,22 +3017,14 @@ static void test_multifd_tcp_cancel(void) .hide_stderr = true, }; QTestState *from, *to, *to2; - QDict *rsp; g_autofree char *uri = NULL; 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_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); migrate_set_parameter_int(from, "multifd-channels", 16); migrate_set_parameter_int(to, "multifd-channels", 16); @@ -2014,9 +3033,7 @@ static void test_multifd_tcp_cancel(void) 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); + migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); @@ -2025,10 +3042,14 @@ static void test_multifd_tcp_cancel(void) migrate_qmp(from, uri, "{}"); - wait_for_migration_pass(from); + migrate_wait_for_dirty_mem(from, to); migrate_cancel(from); + /* Make sure QEMU process "to" exited */ + qtest_set_expected_status(to, EXIT_FAILURE); + qtest_wait_qemu(to); + args = (MigrateStart){ .only_target = true, }; @@ -2042,27 +3063,22 @@ static void test_multifd_tcp_cancel(void) 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); + migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", "{}"); 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_ensure_non_converge(from); migrate_qmp(from, uri, "{}"); - wait_for_migration_pass(from); + migrate_wait_for_dirty_mem(from, to2); - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); - } + migrate_ensure_converge(from); + + wait_for_stop(from, &src_state); qtest_qmp_eventwait(to2, "RESUME"); wait_for_serial("dest_serial"); @@ -2070,6 +3086,407 @@ static void test_multifd_tcp_cancel(void) test_migrate_end(from, to2, true); } +static void calc_dirty_rate(QTestState *who, uint64_t calc_time) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'calc-dirty-rate'," + "'arguments': { " + "'calc-time': %" PRIu64 "," + "'mode': 'dirty-ring' }}", + calc_time); +} + +static QDict *query_dirty_rate(QTestState *who) +{ + return qtest_qmp_assert_success_ref(who, + "{ 'execute': 'query-dirty-rate' }"); +} + +static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'set-vcpu-dirty-limit'," + "'arguments': { " + "'dirty-rate': %" PRIu64 " } }", + dirtyrate); +} + +static void cancel_vcpu_dirty_limit(QTestState *who) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'cancel-vcpu-dirty-limit' }"); +} + +static QDict *query_vcpu_dirty_limit(QTestState *who) +{ + QDict *rsp; + + rsp = qtest_qmp(who, "{ 'execute': 'query-vcpu-dirty-limit' }"); + g_assert(!qdict_haskey(rsp, "error")); + g_assert(qdict_haskey(rsp, "return")); + + return rsp; +} + +static bool calc_dirtyrate_ready(QTestState *who) +{ + QDict *rsp_return; + gchar *status; + + rsp_return = query_dirty_rate(who); + g_assert(rsp_return); + + status = g_strdup(qdict_get_str(rsp_return, "status")); + g_assert(status); + + return g_strcmp0(status, "measuring"); +} + +static void wait_for_calc_dirtyrate_complete(QTestState *who, + int64_t time_s) +{ + int max_try_count = 10000; + usleep(time_s * 1000000); + + while (!calc_dirtyrate_ready(who) && max_try_count--) { + usleep(1000); + } + + /* + * Set the timeout with 10 s(max_try_count * 1000us), + * if dirtyrate measurement not complete, fail test. + */ + g_assert_cmpint(max_try_count, !=, 0); +} + +static int64_t get_dirty_rate(QTestState *who) +{ + QDict *rsp_return; + gchar *status; + QList *rates; + const QListEntry *entry; + QDict *rate; + int64_t dirtyrate; + + rsp_return = query_dirty_rate(who); + g_assert(rsp_return); + + status = g_strdup(qdict_get_str(rsp_return, "status")); + g_assert(status); + g_assert_cmpstr(status, ==, "measured"); + + rates = qdict_get_qlist(rsp_return, "vcpu-dirty-rate"); + g_assert(rates && !qlist_empty(rates)); + + entry = qlist_first(rates); + g_assert(entry); + + rate = qobject_to(QDict, qlist_entry_obj(entry)); + g_assert(rate); + + dirtyrate = qdict_get_try_int(rate, "dirty-rate", -1); + + qobject_unref(rsp_return); + return dirtyrate; +} + +static int64_t get_limit_rate(QTestState *who) +{ + QDict *rsp_return; + QList *rates; + const QListEntry *entry; + QDict *rate; + int64_t dirtyrate; + + rsp_return = query_vcpu_dirty_limit(who); + g_assert(rsp_return); + + rates = qdict_get_qlist(rsp_return, "return"); + g_assert(rates && !qlist_empty(rates)); + + entry = qlist_first(rates); + g_assert(entry); + + rate = qobject_to(QDict, qlist_entry_obj(entry)); + g_assert(rate); + + dirtyrate = qdict_get_try_int(rate, "limit-rate", -1); + + qobject_unref(rsp_return); + return dirtyrate; +} + +static QTestState *dirtylimit_start_vm(void) +{ + QTestState *vm = NULL; + g_autofree gchar *cmd = NULL; + + bootfile_create(tmpfs, false); + cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " + "-name dirtylimit-test,debug-threads=on " + "-m 150M -smp 1 " + "-serial file:%s/vm_serial " + "-drive file=%s,format=raw ", + tmpfs, bootpath); + + vm = qtest_init(cmd); + return vm; +} + +static void dirtylimit_stop_vm(QTestState *vm) +{ + qtest_quit(vm); + cleanup("vm_serial"); +} + +static void test_vcpu_dirty_limit(void) +{ + QTestState *vm; + int64_t origin_rate; + int64_t quota_rate; + int64_t rate ; + int max_try_count = 20; + int hit = 0; + + /* Start vm for vcpu dirtylimit test */ + vm = dirtylimit_start_vm(); + + /* Wait for the first serial output from the vm*/ + wait_for_serial("vm_serial"); + + /* Do dirtyrate measurement with calc time equals 1s */ + calc_dirty_rate(vm, 1); + + /* Sleep calc time and wait for calc dirtyrate complete */ + wait_for_calc_dirtyrate_complete(vm, 1); + + /* Query original dirty page rate */ + origin_rate = get_dirty_rate(vm); + + /* VM booted from bootsect should dirty memory steadily */ + assert(origin_rate != 0); + + /* Setup quota dirty page rate at half of origin */ + quota_rate = origin_rate / 2; + + /* Set dirtylimit */ + dirtylimit_set_all(vm, quota_rate); + + /* + * Check if set-vcpu-dirty-limit and query-vcpu-dirty-limit + * works literally + */ + g_assert_cmpint(quota_rate, ==, get_limit_rate(vm)); + + /* Sleep a bit to check if it take effect */ + usleep(2000000); + + /* + * Check if dirtylimit take effect realistically, set the + * timeout with 20 s(max_try_count * 1s), if dirtylimit + * doesn't take effect, fail test. + */ + while (--max_try_count) { + calc_dirty_rate(vm, 1); + wait_for_calc_dirtyrate_complete(vm, 1); + rate = get_dirty_rate(vm); + + /* + * Assume hitting if current rate is less + * than quota rate (within accepting error) + */ + if (rate < (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { + hit = 1; + break; + } + } + + g_assert_cmpint(hit, ==, 1); + + hit = 0; + max_try_count = 20; + + /* Check if dirtylimit cancellation take effect */ + cancel_vcpu_dirty_limit(vm); + while (--max_try_count) { + calc_dirty_rate(vm, 1); + wait_for_calc_dirtyrate_complete(vm, 1); + rate = get_dirty_rate(vm); + + /* + * Assume dirtylimit be canceled if current rate is + * greater than quota rate (within accepting error) + */ + if (rate > (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { + hit = 1; + break; + } + } + + g_assert_cmpint(hit, ==, 1); + dirtylimit_stop_vm(vm); +} + +static void migrate_dirty_limit_wait_showup(QTestState *from, + const int64_t period, + const int64_t value) +{ + /* Enable dirty limit capability */ + migrate_set_capability(from, "dirty-limit", true); + + /* Set dirty limit parameters */ + migrate_set_parameter_int(from, "x-vcpu-dirty-limit-period", period); + migrate_set_parameter_int(from, "vcpu-dirty-limit", value); + + /* Make sure migrate can't converge */ + migrate_ensure_non_converge(from); + + /* To check limit rate after precopy */ + migrate_set_capability(from, "pause-before-switchover", true); + + /* Wait for the serial output from the source */ + wait_for_serial("src_serial"); +} + +/* + * This test does: + * source destination + * start vm + * start incoming vm + * migrate + * wait dirty limit to begin + * cancel migrate + * cancellation check + * restart incoming vm + * migrate + * wait dirty limit to begin + * wait pre-switchover event + * convergence condition check + * + * And see if dirty limit migration works correctly. + * This test case involves many passes, so it runs in slow mode only. + */ +static void test_migrate_dirty_limit(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + int64_t remaining; + uint64_t throttle_us_per_full; + /* + * We want the test to be stable and as fast as possible. + * E.g., with 1Gb/s bandwidth migration may pass without dirty limit, + * so we need to decrease a bandwidth. + */ + const int64_t dirtylimit_period = 1000, dirtylimit_value = 50; + const int64_t max_bandwidth = 400000000; /* ~400Mb/s */ + const int64_t downtime_limit = 250; /* 250ms */ + /* + * We migrate through unix-socket (> 500Mb/s). + * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s). + * So, we can predict expected_threshold + */ + const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; + int max_try_count = 10; + MigrateCommon args = { + .start = { + .hide_stderr = true, + .use_dirty_ring = true, + }, + .listen_uri = uri, + .connect_uri = uri, + }; + + /* Start src, dst vm */ + if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) { + return; + } + + /* Prepare for dirty limit migration and wait src vm show up */ + migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value); + + /* Start migrate */ + migrate_qmp(from, uri, "{}"); + + /* Wait for dirty limit throttle begin */ + throttle_us_per_full = 0; + while (throttle_us_per_full == 0) { + throttle_us_per_full = + read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(src_state.stop_seen); + } + + /* Now cancel migrate and wait for dirty limit throttle switch off */ + migrate_cancel(from); + wait_for_migration_status(from, "cancelled", NULL); + + /* Check if dirty limit throttle switched off, set timeout 1ms */ + do { + throttle_us_per_full = + read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(src_state.stop_seen); + } while (throttle_us_per_full != 0 && --max_try_count); + + /* Assert dirty limit is not in service */ + g_assert_cmpint(throttle_us_per_full, ==, 0); + + args = (MigrateCommon) { + .start = { + .only_target = true, + .use_dirty_ring = true, + }, + .listen_uri = uri, + .connect_uri = uri, + }; + + /* Restart dst vm, src vm already show up so we needn't wait anymore */ + if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) { + return; + } + + /* Start migrate */ + migrate_qmp(from, uri, "{}"); + + /* Wait for dirty limit throttle begin */ + throttle_us_per_full = 0; + while (throttle_us_per_full == 0) { + throttle_us_per_full = + read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(src_state.stop_seen); + } + + /* + * The dirty limit rate should equals the return value of + * query-vcpu-dirty-limit if dirty limit cap set + */ + g_assert_cmpint(dirtylimit_value, ==, get_limit_rate(from)); + + /* Now, we have tested if dirty limit works, let it converge */ + migrate_set_parameter_int(from, "downtime-limit", downtime_limit); + migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth); + + /* + * Wait for pre-switchover status to check if migration + * satisfy the convergence condition + */ + wait_for_migration_status(from, "pre-switchover", NULL); + + remaining = read_ram_property_int(from, "remaining"); + g_assert_cmpint(remaining, <, + (expected_threshold + expected_threshold / 100)); + + migrate_continue(from, "pre-switchover"); + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + test_migrate_end(from, to, true); +} + static bool kvm_dirty_ring_supported(void) { #if defined(__linux__) && defined(HOST_X86_64) @@ -2095,22 +3512,45 @@ static bool kvm_dirty_ring_supported(void) int main(int argc, char **argv) { - char template[] = "/tmp/migration-test-XXXXXX"; - const bool has_kvm = qtest_has_accel("kvm"); + bool has_kvm, has_tcg; + bool has_uffd, is_x86; + const char *arch; + g_autoptr(GError) err = NULL; + const char *qemu_src = getenv(QEMU_ENV_SRC); + const char *qemu_dst = getenv(QEMU_ENV_DST); int ret; g_test_init(&argc, &argv, NULL); - if (!ufd_version_check()) { - return g_test_run(); + /* + * The default QTEST_QEMU_BINARY must always be provided because + * that is what helpers use to query the accel type and + * architecture. + */ + if (qemu_src && qemu_dst) { + g_test_message("Only one of %s, %s is allowed", + QEMU_ENV_SRC, QEMU_ENV_DST); + exit(1); } + has_kvm = qtest_has_accel("kvm"); + has_tcg = qtest_has_accel("tcg"); + + if (!has_tcg && !has_kvm) { + g_test_skip("No KVM or TCG accelerator available"); + return 0; + } + + has_uffd = ufd_version_check(); + arch = qtest_get_arch(); + is_x86 = !strcmp(arch, "i386") || !strcmp(arch, "x86_64"); + /* * On ppc64, the test only works with kvm-hv, but not with kvm-pr and TCG * is touchy due to race conditions on dirty bits (especially on PPC for * some reason) */ - if (g_str_equal(qtest_get_arch(), "ppc64") && + if (g_str_equal(arch, "ppc64") && (!has_kvm || access("/sys/module/kvm_hv", F_OK))) { g_test_message("Skipping test: kvm_hv not available"); return g_test_run(); @@ -2120,112 +3560,229 @@ int main(int argc, char **argv) * Similar to ppc64, s390x seems to be touchy with TCG, so disable it * there until the problems are resolved */ - if (g_str_equal(qtest_get_arch(), "s390x") && !has_kvm) { + if (g_str_equal(arch, "s390x") && !has_kvm) { g_test_message("Skipping test: s390x host with KVM is required"); return g_test_run(); } - tmpfs = mkdtemp(template); + tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); if (!tmpfs) { - g_test_message("mkdtemp on path (%s): %s", template, strerror(errno)); + g_test_message("Can't create temporary directory in %s: %s", + g_get_tmp_dir(), err->message); } g_assert(tmpfs); module_call_init(MODULE_INIT_QOM); - qtest_add_func("/migration/postcopy/unix", test_postcopy); - qtest_add_func("/migration/postcopy/recovery", test_postcopy_recovery); - qtest_add_func("/migration/bad_dest", test_baddest); - qtest_add_func("/migration/precopy/unix/plain", test_precopy_unix_plain); - qtest_add_func("/migration/precopy/unix/xbzrle", test_precopy_unix_xbzrle); + if (is_x86) { + migration_test_add("/migration/precopy/unix/suspend/live", + test_precopy_unix_suspend_live); + migration_test_add("/migration/precopy/unix/suspend/notlive", + test_precopy_unix_suspend_notlive); + } + + if (has_uffd) { + migration_test_add("/migration/postcopy/plain", test_postcopy); + migration_test_add("/migration/postcopy/recovery/plain", + test_postcopy_recovery); + migration_test_add("/migration/postcopy/preempt/plain", + test_postcopy_preempt); + migration_test_add("/migration/postcopy/preempt/recovery/plain", + test_postcopy_preempt_recovery); + if (getenv("QEMU_TEST_FLAKY_TESTS")) { + migration_test_add("/migration/postcopy/compress/plain", + test_postcopy_compress); + migration_test_add("/migration/postcopy/recovery/compress/plain", + test_postcopy_recovery_compress); + } +#ifndef _WIN32 + migration_test_add("/migration/postcopy/recovery/double-failures", + test_postcopy_recovery_double_fail); +#endif /* _WIN32 */ + if (is_x86) { + migration_test_add("/migration/postcopy/suspend", + test_postcopy_suspend); + } + } + + migration_test_add("/migration/bad_dest", test_baddest); +#ifndef _WIN32 + if (!g_str_equal(arch, "s390x")) { + migration_test_add("/migration/analyze-script", test_analyze_script); + } +#endif + migration_test_add("/migration/precopy/unix/plain", + test_precopy_unix_plain); + migration_test_add("/migration/precopy/unix/xbzrle", + test_precopy_unix_xbzrle); + /* + * Compression fails from time to time. + * Put test here but don't enable it until everything is fixed. + */ + if (getenv("QEMU_TEST_FLAKY_TESTS")) { + migration_test_add("/migration/precopy/unix/compress/wait", + test_precopy_unix_compress); + migration_test_add("/migration/precopy/unix/compress/nowait", + test_precopy_unix_compress_nowait); + } + + migration_test_add("/migration/precopy/file", + test_precopy_file); + migration_test_add("/migration/precopy/file/offset", + test_precopy_file_offset); + migration_test_add("/migration/precopy/file/offset/bad", + test_precopy_file_offset_bad); + + /* + * Our CI system has problems with shared memory. + * Don't run this test until we find a workaround. + */ + if (getenv("QEMU_TEST_FLAKY_TESTS")) { + migration_test_add("/migration/mode/reboot", test_mode_reboot); + } + + migration_test_add("/migration/precopy/file/mapped-ram", + test_precopy_file_mapped_ram); + migration_test_add("/migration/precopy/file/mapped-ram/live", + test_precopy_file_mapped_ram_live); + + migration_test_add("/migration/multifd/file/mapped-ram", + test_multifd_file_mapped_ram); + migration_test_add("/migration/multifd/file/mapped-ram/live", + test_multifd_file_mapped_ram_live); + #ifdef CONFIG_GNUTLS - qtest_add_func("/migration/precopy/unix/tls/psk", - test_precopy_unix_tls_psk); + migration_test_add("/migration/precopy/unix/tls/psk", + test_precopy_unix_tls_psk); + + if (has_uffd) { + /* + * NOTE: psk test is enough for postcopy, as other types of TLS + * channels are tested under precopy. Here what we want to test is the + * general postcopy path that has TLS channel enabled. + */ + migration_test_add("/migration/postcopy/tls/psk", + test_postcopy_tls_psk); + migration_test_add("/migration/postcopy/recovery/tls/psk", + test_postcopy_recovery_tls_psk); + migration_test_add("/migration/postcopy/preempt/tls/psk", + test_postcopy_preempt_tls_psk); + migration_test_add("/migration/postcopy/preempt/recovery/tls/psk", + test_postcopy_preempt_all); + } #ifdef CONFIG_TASN1 - qtest_add_func("/migration/precopy/unix/tls/x509/default-host", - test_precopy_unix_tls_x509_default_host); - qtest_add_func("/migration/precopy/unix/tls/x509/override-host", - test_precopy_unix_tls_x509_override_host); + migration_test_add("/migration/precopy/unix/tls/x509/default-host", + test_precopy_unix_tls_x509_default_host); + migration_test_add("/migration/precopy/unix/tls/x509/override-host", + test_precopy_unix_tls_x509_override_host); #endif /* CONFIG_TASN1 */ #endif /* CONFIG_GNUTLS */ - qtest_add_func("/migration/precopy/tcp/plain", test_precopy_tcp_plain); + migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain); + + migration_test_add("/migration/precopy/tcp/plain/switchover-ack", + test_precopy_tcp_switchover_ack); + #ifdef CONFIG_GNUTLS - qtest_add_func("/migration/precopy/tcp/tls/psk/match", - test_precopy_tcp_tls_psk_match); - qtest_add_func("/migration/precopy/tcp/tls/psk/mismatch", - test_precopy_tcp_tls_psk_mismatch); + migration_test_add("/migration/precopy/tcp/tls/psk/match", + test_precopy_tcp_tls_psk_match); + migration_test_add("/migration/precopy/tcp/tls/psk/mismatch", + test_precopy_tcp_tls_psk_mismatch); #ifdef CONFIG_TASN1 - qtest_add_func("/migration/precopy/tcp/tls/x509/default-host", - test_precopy_tcp_tls_x509_default_host); - qtest_add_func("/migration/precopy/tcp/tls/x509/override-host", - test_precopy_tcp_tls_x509_override_host); - qtest_add_func("/migration/precopy/tcp/tls/x509/mismatch-host", - test_precopy_tcp_tls_x509_mismatch_host); - qtest_add_func("/migration/precopy/tcp/tls/x509/friendly-client", - test_precopy_tcp_tls_x509_friendly_client); - qtest_add_func("/migration/precopy/tcp/tls/x509/hostile-client", - test_precopy_tcp_tls_x509_hostile_client); - qtest_add_func("/migration/precopy/tcp/tls/x509/allow-anon-client", - test_precopy_tcp_tls_x509_allow_anon_client); - qtest_add_func("/migration/precopy/tcp/tls/x509/reject-anon-client", - test_precopy_tcp_tls_x509_reject_anon_client); + migration_test_add("/migration/precopy/tcp/tls/x509/default-host", + test_precopy_tcp_tls_x509_default_host); + migration_test_add("/migration/precopy/tcp/tls/x509/override-host", + test_precopy_tcp_tls_x509_override_host); + migration_test_add("/migration/precopy/tcp/tls/x509/mismatch-host", + test_precopy_tcp_tls_x509_mismatch_host); + migration_test_add("/migration/precopy/tcp/tls/x509/friendly-client", + test_precopy_tcp_tls_x509_friendly_client); + migration_test_add("/migration/precopy/tcp/tls/x509/hostile-client", + test_precopy_tcp_tls_x509_hostile_client); + migration_test_add("/migration/precopy/tcp/tls/x509/allow-anon-client", + test_precopy_tcp_tls_x509_allow_anon_client); + migration_test_add("/migration/precopy/tcp/tls/x509/reject-anon-client", + test_precopy_tcp_tls_x509_reject_anon_client); #endif /* CONFIG_TASN1 */ #endif /* CONFIG_GNUTLS */ - /* qtest_add_func("/migration/ignore_shared", test_ignore_shared); */ - qtest_add_func("/migration/fd_proto", test_migrate_fd_proto); - qtest_add_func("/migration/validate_uuid", test_validate_uuid); - qtest_add_func("/migration/validate_uuid_error", test_validate_uuid_error); - qtest_add_func("/migration/validate_uuid_src_not_set", - test_validate_uuid_src_not_set); - qtest_add_func("/migration/validate_uuid_dst_not_set", - test_validate_uuid_dst_not_set); - - qtest_add_func("/migration/auto_converge", test_migrate_auto_converge); - qtest_add_func("/migration/multifd/tcp/plain/none", - test_multifd_tcp_none); - qtest_add_func("/migration/multifd/tcp/plain/cancel", - test_multifd_tcp_cancel); - qtest_add_func("/migration/multifd/tcp/plain/zlib", - test_multifd_tcp_zlib); + /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */ +#ifndef _WIN32 + migration_test_add("/migration/precopy/fd/tcp", + test_migrate_precopy_fd_socket); + migration_test_add("/migration/precopy/fd/file", + test_migrate_precopy_fd_file); +#endif + migration_test_add("/migration/validate_uuid", test_validate_uuid); + migration_test_add("/migration/validate_uuid_error", + test_validate_uuid_error); + migration_test_add("/migration/validate_uuid_src_not_set", + test_validate_uuid_src_not_set); + migration_test_add("/migration/validate_uuid_dst_not_set", + test_validate_uuid_dst_not_set); + /* + * See explanation why this test is slow on function definition + */ + if (g_test_slow()) { + migration_test_add("/migration/auto_converge", + test_migrate_auto_converge); + if (g_str_equal(arch, "x86_64") && + has_kvm && kvm_dirty_ring_supported()) { + migration_test_add("/migration/dirty_limit", + test_migrate_dirty_limit); + } + } + migration_test_add("/migration/multifd/tcp/plain/none", + test_multifd_tcp_none); + migration_test_add("/migration/multifd/tcp/plain/zero-page/legacy", + test_multifd_tcp_zero_page_legacy); + migration_test_add("/migration/multifd/tcp/plain/zero-page/none", + test_multifd_tcp_no_zero_page); + migration_test_add("/migration/multifd/tcp/plain/cancel", + test_multifd_tcp_cancel); + migration_test_add("/migration/multifd/tcp/plain/zlib", + test_multifd_tcp_zlib); #ifdef CONFIG_ZSTD - qtest_add_func("/migration/multifd/tcp/plain/zstd", - test_multifd_tcp_zstd); + migration_test_add("/migration/multifd/tcp/plain/zstd", + test_multifd_tcp_zstd); #endif #ifdef CONFIG_GNUTLS - qtest_add_func("/migration/multifd/tcp/tls/psk/match", - test_multifd_tcp_tls_psk_match); - qtest_add_func("/migration/multifd/tcp/tls/psk/mismatch", - test_multifd_tcp_tls_psk_mismatch); + migration_test_add("/migration/multifd/tcp/tls/psk/match", + test_multifd_tcp_tls_psk_match); + migration_test_add("/migration/multifd/tcp/tls/psk/mismatch", + test_multifd_tcp_tls_psk_mismatch); #ifdef CONFIG_TASN1 - qtest_add_func("/migration/multifd/tcp/tls/x509/default-host", - test_multifd_tcp_tls_x509_default_host); - qtest_add_func("/migration/multifd/tcp/tls/x509/override-host", - test_multifd_tcp_tls_x509_override_host); - qtest_add_func("/migration/multifd/tcp/tls/x509/mismatch-host", - test_multifd_tcp_tls_x509_mismatch_host); - qtest_add_func("/migration/multifd/tcp/tls/x509/allow-anon-client", - test_multifd_tcp_tls_x509_allow_anon_client); - qtest_add_func("/migration/multifd/tcp/tls/x509/reject-anon-client", - test_multifd_tcp_tls_x509_reject_anon_client); + migration_test_add("/migration/multifd/tcp/tls/x509/default-host", + test_multifd_tcp_tls_x509_default_host); + migration_test_add("/migration/multifd/tcp/tls/x509/override-host", + test_multifd_tcp_tls_x509_override_host); + migration_test_add("/migration/multifd/tcp/tls/x509/mismatch-host", + test_multifd_tcp_tls_x509_mismatch_host); + migration_test_add("/migration/multifd/tcp/tls/x509/allow-anon-client", + test_multifd_tcp_tls_x509_allow_anon_client); + migration_test_add("/migration/multifd/tcp/tls/x509/reject-anon-client", + test_multifd_tcp_tls_x509_reject_anon_client); #endif /* CONFIG_TASN1 */ #endif /* CONFIG_GNUTLS */ - if (kvm_dirty_ring_supported()) { - qtest_add_func("/migration/dirty_ring", - test_precopy_unix_dirty_ring); + if (g_str_equal(arch, "x86_64") && has_kvm && kvm_dirty_ring_supported()) { + migration_test_add("/migration/dirty_ring", + test_precopy_unix_dirty_ring); + migration_test_add("/migration/vcpu_dirty_limit", + test_vcpu_dirty_limit); } ret = g_test_run(); g_assert_cmpint(ret, ==, 0); + bootfile_delete(); ret = rmdir(tmpfs); if (ret != 0) { g_test_message("unable to rmdir: path (%s): %s", tmpfs, strerror(errno)); } + g_free(tmpfs); return ret; } diff --git a/tests/qtest/modules-test.c b/tests/qtest/modules-test.c index 88217686e1..be2575ae6d 100644 --- a/tests/qtest/modules-test.c +++ b/tests/qtest/modules-test.c @@ -16,6 +16,9 @@ static void test_modules_load(const void *data) int main(int argc, char *argv[]) { const char *modules[] = { +#ifdef CONFIG_BLKIO + "block-", "blkio", +#endif #ifdef CONFIG_CURL "block-", "curl", #endif diff --git a/tests/qtest/netdev-socket.c b/tests/qtest/netdev-socket.c new file mode 100644 index 0000000000..fc7d11961e --- /dev/null +++ b/tests/qtest/netdev-socket.c @@ -0,0 +1,548 @@ +/* + * QTest testcase for netdev stream and dgram + * + * Copyright (c) 2022 Red Hat, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/sockets.h" +#include +#include "../unit/socket-helpers.h" +#include "libqtest.h" +#include "qapi/qmp/qstring.h" +#include "qemu/sockets.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qapi-visit-sockets.h" + +#define CONNECTION_TIMEOUT 60 + +#define EXPECT_STATE(q, e, t) \ +do { \ + char *resp = NULL; \ + g_test_timer_start(); \ + do { \ + g_free(resp); \ + resp = qtest_hmp(q, "info network"); \ + if (t) { \ + strrchr(resp, t)[0] = 0; \ + } \ + if (g_str_equal(resp, e)) { \ + break; \ + } \ + } while (g_test_timer_elapsed() < CONNECTION_TIMEOUT); \ + g_assert_cmpstr(resp, ==, e); \ + g_free(resp); \ +} while (0) + +static gchar *tmpdir; + +static int inet_get_free_port_socket_ipv4(int sock) +{ + struct sockaddr_in addr; + socklen_t len; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = 0; + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + return -1; + } + + len = sizeof(addr); + if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) { + return -1; + } + + return ntohs(addr.sin_port); +} + +static int inet_get_free_port_socket_ipv6(int sock) +{ + struct sockaddr_in6 addr; + socklen_t len; + + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_addr = in6addr_any; + addr.sin6_port = 0; + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + return -1; + } + + len = sizeof(addr); + if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) { + return -1; + } + + return ntohs(addr.sin6_port); +} + +static int inet_get_free_port_multiple(int nb, int *port, bool ipv6) +{ + g_autofree int *sock = g_new(int, nb); + int i; + + for (i = 0; i < nb; i++) { + sock[i] = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0); + if (sock[i] < 0) { + break; + } + port[i] = ipv6 ? inet_get_free_port_socket_ipv6(sock[i]) : + inet_get_free_port_socket_ipv4(sock[i]); + if (port[i] == -1) { + break; + } + } + + nb = i; + for (i = 0; i < nb; i++) { + close(sock[i]); + } + + return nb; +} + +static int inet_get_free_port(bool ipv6) +{ + int nb, port; + + nb = inet_get_free_port_multiple(1, &port, ipv6); + g_assert_cmpint(nb, ==, 1); + + return port; +} + +static void test_stream_inet_ipv4(void) +{ + QTestState *qts0, *qts1; + char *expect; + int port; + + port = inet_get_free_port(false); + qts0 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=true,addr.type=inet," + "addr.ipv4=on,addr.ipv6=off," + "addr.host=127.0.0.1,addr.port=%d", port); + + EXPECT_STATE(qts0, "st0: index=0,type=stream,listening\r\n", 0); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev stream,server=false,id=st0,addr.type=inet," + "addr.ipv4=on,addr.ipv6=off," + "addr.host=127.0.0.1,addr.port=%d", port); + + expect = g_strdup_printf("st0: index=0,type=stream,tcp:127.0.0.1:%d\r\n", + port); + EXPECT_STATE(qts1, expect, 0); + g_free(expect); + + /* the port is unknown, check only the address */ + EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:127.0.0.1", ':'); + + qtest_quit(qts1); + qtest_quit(qts0); +} + +static void wait_stream_connected(QTestState *qts, const char *id, + SocketAddress **addr) +{ + QDict *resp, *data; + QString *qstr; + QObject *obj; + Visitor *v = NULL; + + resp = qtest_qmp_eventwait_ref(qts, "NETDEV_STREAM_CONNECTED"); + g_assert_nonnull(resp); + data = qdict_get_qdict(resp, "data"); + g_assert_nonnull(data); + + qstr = qobject_to(QString, qdict_get(data, "netdev-id")); + g_assert_nonnull(data); + + g_assert(!strcmp(qstring_get_str(qstr), id)); + + obj = qdict_get(data, "addr"); + + v = qobject_input_visitor_new(obj); + visit_type_SocketAddress(v, NULL, addr, NULL); + visit_free(v); + qobject_unref(resp); +} + +static void wait_stream_disconnected(QTestState *qts, const char *id) +{ + QDict *resp, *data; + QString *qstr; + + resp = qtest_qmp_eventwait_ref(qts, "NETDEV_STREAM_DISCONNECTED"); + g_assert_nonnull(resp); + data = qdict_get_qdict(resp, "data"); + g_assert_nonnull(data); + + qstr = qobject_to(QString, qdict_get(data, "netdev-id")); + g_assert_nonnull(data); + + g_assert(!strcmp(qstring_get_str(qstr), id)); + qobject_unref(resp); +} + +static void test_stream_unix_reconnect(void) +{ + QTestState *qts0, *qts1; + SocketAddress *addr; + gchar *path; + + path = g_strconcat(tmpdir, "/stream_unix_reconnect", NULL); + qts0 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=true,addr.type=unix," + "addr.path=%s", path); + + EXPECT_STATE(qts0, "st0: index=0,type=stream,listening\r\n", 0); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev stream,server=false,id=st0,addr.type=unix," + "addr.path=%s,reconnect=1", path); + + wait_stream_connected(qts0, "st0", &addr); + g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX); + g_assert_cmpstr(addr->u.q_unix.path, ==, path); + qapi_free_SocketAddress(addr); + + /* kill server */ + qtest_quit(qts0); + + /* check client has been disconnected */ + wait_stream_disconnected(qts1, "st0"); + + /* restart server */ + qts0 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=true,addr.type=unix," + "addr.path=%s", path); + + /* wait connection events*/ + wait_stream_connected(qts0, "st0", &addr); + g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX); + g_assert_cmpstr(addr->u.q_unix.path, ==, path); + qapi_free_SocketAddress(addr); + + wait_stream_connected(qts1, "st0", &addr); + g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX); + g_assert_cmpstr(addr->u.q_unix.path, ==, path); + qapi_free_SocketAddress(addr); + + qtest_quit(qts1); + qtest_quit(qts0); + g_free(path); +} + +static void test_stream_inet_ipv6(void) +{ + QTestState *qts0, *qts1; + char *expect; + int port; + + port = inet_get_free_port(true); + qts0 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=true,addr.type=inet," + "addr.ipv4=off,addr.ipv6=on," + "addr.host=::1,addr.port=%d", port); + + EXPECT_STATE(qts0, "st0: index=0,type=stream,listening\r\n", 0); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev stream,server=false,id=st0,addr.type=inet," + "addr.ipv4=off,addr.ipv6=on," + "addr.host=::1,addr.port=%d", port); + + expect = g_strdup_printf("st0: index=0,type=stream,tcp:::1:%d\r\n", + port); + EXPECT_STATE(qts1, expect, 0); + g_free(expect); + + /* the port is unknown, check only the address */ + EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:::1", ':'); + + qtest_quit(qts1); + qtest_quit(qts0); +} + +static void test_stream_unix(void) +{ + QTestState *qts0, *qts1; + char *expect; + gchar *path; + + path = g_strconcat(tmpdir, "/stream_unix", NULL); + + qts0 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=true," + "addr.type=unix,addr.path=%s,", + path); + + EXPECT_STATE(qts0, "st0: index=0,type=stream,listening\r\n", 0); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=false," + "addr.type=unix,addr.path=%s", + path); + + expect = g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path); + EXPECT_STATE(qts1, expect, 0); + EXPECT_STATE(qts0, expect, 0); + g_free(expect); + g_free(path); + + qtest_quit(qts1); + qtest_quit(qts0); +} + +#ifdef CONFIG_LINUX +static void test_stream_unix_abstract(void) +{ + QTestState *qts0, *qts1; + char *expect; + gchar *path; + + path = g_strconcat(tmpdir, "/stream_unix_abstract", NULL); + + qts0 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=true," + "addr.type=unix,addr.path=%s," + "addr.abstract=on", + path); + + EXPECT_STATE(qts0, "st0: index=0,type=stream,listening\r\n", 0); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,server=false," + "addr.type=unix,addr.path=%s,addr.abstract=on", + path); + + expect = g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path); + EXPECT_STATE(qts1, expect, 0); + EXPECT_STATE(qts0, expect, 0); + g_free(expect); + g_free(path); + + qtest_quit(qts1); + qtest_quit(qts0); +} +#endif + +#ifndef _WIN32 +static void test_stream_fd(void) +{ + QTestState *qts0, *qts1; + int sock[2]; + int ret; + + ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, sock); + g_assert_true(ret == 0); + + qts0 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,addr.type=fd,addr.str=%d", + sock[0]); + + EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev stream,id=st0,addr.type=fd,addr.str=%d", + sock[1]); + + EXPECT_STATE(qts1, "st0: index=0,type=stream,unix:\r\n", 0); + EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0); + + qtest_quit(qts1); + qtest_quit(qts0); + + close(sock[0]); + close(sock[1]); +} +#endif + +static void test_dgram_inet(void) +{ + QTestState *qts0, *qts1; + char *expect; + int port[2]; + int nb; + + nb = inet_get_free_port_multiple(2, port, false); + g_assert_cmpint(nb, ==, 2); + + qts0 = qtest_initf("-nodefaults -M none " + "-netdev dgram,id=st0," + "local.type=inet,local.host=127.0.0.1,local.port=%d," + "remote.type=inet,remote.host=127.0.0.1,remote.port=%d", + port[0], port[1]); + + expect = g_strdup_printf("st0: index=0,type=dgram," + "udp=127.0.0.1:%d/127.0.0.1:%d\r\n", + port[0], port[1]); + EXPECT_STATE(qts0, expect, 0); + g_free(expect); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev dgram,id=st0," + "local.type=inet,local.host=127.0.0.1,local.port=%d," + "remote.type=inet,remote.host=127.0.0.1,remote.port=%d", + port[1], port[0]); + + expect = g_strdup_printf("st0: index=0,type=dgram," + "udp=127.0.0.1:%d/127.0.0.1:%d\r\n", + port[1], port[0]); + EXPECT_STATE(qts1, expect, 0); + g_free(expect); + + qtest_quit(qts1); + qtest_quit(qts0); +} + +#if !defined(_WIN32) && !defined(CONFIG_DARWIN) +static void test_dgram_mcast(void) +{ + QTestState *qts; + + qts = qtest_initf("-nodefaults -M none " + "-netdev dgram,id=st0," + "remote.type=inet,remote.host=230.0.0.1,remote.port=1234"); + + EXPECT_STATE(qts, "st0: index=0,type=dgram,mcast=230.0.0.1:1234\r\n", 0); + + qtest_quit(qts); +} +#endif + +#ifndef _WIN32 +static void test_dgram_unix(void) +{ + QTestState *qts0, *qts1; + char *expect; + gchar *path0, *path1; + + path0 = g_strconcat(tmpdir, "/dgram_unix0", NULL); + path1 = g_strconcat(tmpdir, "/dgram_unix1", NULL); + + qts0 = qtest_initf("-nodefaults -M none " + "-netdev dgram,id=st0,local.type=unix,local.path=%s," + "remote.type=unix,remote.path=%s", + path0, path1); + + expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n", + path0, path1); + EXPECT_STATE(qts0, expect, 0); + g_free(expect); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev dgram,id=st0,local.type=unix,local.path=%s," + "remote.type=unix,remote.path=%s", + path1, path0); + + + expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n", + path1, path0); + EXPECT_STATE(qts1, expect, 0); + g_free(expect); + + unlink(path0); + g_free(path0); + unlink(path1); + g_free(path1); + + qtest_quit(qts1); + qtest_quit(qts0); +} + +static void test_dgram_fd(void) +{ + QTestState *qts0, *qts1; + char *expect; + int ret; + int sv[2]; + + ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, sv); + g_assert_cmpint(ret, !=, -1); + + qts0 = qtest_initf("-nodefaults -M none " + "-netdev dgram,id=st0,local.type=fd,local.str=%d", + sv[0]); + + expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[0]); + EXPECT_STATE(qts0, expect, 0); + g_free(expect); + + qts1 = qtest_initf("-nodefaults -M none " + "-netdev dgram,id=st0,local.type=fd,local.str=%d", + sv[1]); + + + expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[1]); + EXPECT_STATE(qts1, expect, 0); + g_free(expect); + + qtest_quit(qts1); + qtest_quit(qts0); + + close(sv[0]); + close(sv[1]); +} +#endif + +int main(int argc, char **argv) +{ + int ret; + bool has_ipv4, has_ipv6, has_afunix; + g_autoptr(GError) err = NULL; + + socket_init(); + g_test_init(&argc, &argv, NULL); + + if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) { + g_error("socket_check_protocol_support() failed\n"); + } + + tmpdir = g_dir_make_tmp("netdev-socket.XXXXXX", &err); + if (tmpdir == NULL) { + g_error("Can't create temporary directory in %s: %s", + g_get_tmp_dir(), err->message); + } + + if (has_ipv4) { + qtest_add_func("/netdev/stream/inet/ipv4", test_stream_inet_ipv4); + qtest_add_func("/netdev/dgram/inet", test_dgram_inet); +#if !defined(_WIN32) && !defined(CONFIG_DARWIN) + qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast); +#endif + } + if (has_ipv6) { + qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6); + } + + socket_check_afunix_support(&has_afunix); + if (has_afunix) { +#ifndef _WIN32 + qtest_add_func("/netdev/dgram/unix", test_dgram_unix); +#endif + qtest_add_func("/netdev/stream/unix/oneshot", test_stream_unix); + qtest_add_func("/netdev/stream/unix/reconnect", + test_stream_unix_reconnect); +#ifdef CONFIG_LINUX + qtest_add_func("/netdev/stream/unix/abstract", + test_stream_unix_abstract); +#endif +#ifndef _WIN32 + qtest_add_func("/netdev/stream/fd", test_stream_fd); + qtest_add_func("/netdev/dgram/fd", test_dgram_fd); +#endif + } + + ret = g_test_run(); + + g_rmdir(tmpdir); + g_free(tmpdir); + + return ret; +} diff --git a/tests/qtest/npcm7xx_adc-test.c b/tests/qtest/npcm7xx_adc-test.c index 3fa6d9ece0..e751a72e36 100644 --- a/tests/qtest/npcm7xx_adc-test.c +++ b/tests/qtest/npcm7xx_adc-test.c @@ -50,7 +50,7 @@ #define CON_INT BIT(18) #define CON_EN BIT(17) #define CON_RST BIT(16) -#define CON_CONV BIT(14) +#define CON_CONV BIT(13) #define CON_DIV(rv) extract32(rv, 1, 8) #define FST_RDST BIT(1) @@ -90,7 +90,7 @@ typedef struct ADC { uint64_t base_addr; } ADC; -ADC adc = { +ADC adc_defs = { .irq = 0, .base_addr = 0xf000c000 }; @@ -367,12 +367,12 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - add_test(init, &adc); - add_test(convert_internal, &adc); - add_test(convert_external, &adc); - add_test(interrupt, &adc); - add_test(reset, &adc); - add_test(calibrate, &adc); + add_test(init, &adc_defs); + add_test(convert_internal, &adc_defs); + add_test(convert_external, &adc_defs); + add_test(interrupt, &adc_defs); + add_test(reset, &adc_defs); + add_test(calibrate, &adc_defs); return g_test_run(); } diff --git a/tests/qtest/npcm7xx_emc-test.c b/tests/qtest/npcm7xx_emc-test.c index a353fef0ca..2e1a1a6d70 100644 --- a/tests/qtest/npcm7xx_emc-test.c +++ b/tests/qtest/npcm7xx_emc-test.c @@ -209,6 +209,7 @@ static int emc_module_index(const EMCModule *mod) return diff; } +#ifndef _WIN32 static void packet_test_clear(void *sockets) { int *test_sockets = sockets; @@ -224,25 +225,19 @@ static int *packet_test_init(int module_num, GString *cmd_line) g_assert_cmpint(ret, != , -1); /* - * KISS and use -nic. We specify two nics (both emc{0,1}) because there's - * currently no way to specify only emc1: The driver implicitly relies on - * emc[i] == nd_table[i]. + * KISS and use -nic. The driver accepts 'emc0' and 'emc1' as aliases + * in the 'model' field to specify the device to match. */ - if (module_num == 0) { - g_string_append_printf(cmd_line, - " -nic socket,fd=%d,model=" TYPE_NPCM7XX_EMC " " - " -nic user,model=" TYPE_NPCM7XX_EMC " ", - test_sockets[1]); - } else { - g_string_append_printf(cmd_line, - " -nic user,model=" TYPE_NPCM7XX_EMC " " - " -nic socket,fd=%d,model=" TYPE_NPCM7XX_EMC " ", - test_sockets[1]); - } + g_string_append_printf(cmd_line, " -nic socket,fd=%d,model=emc%d " + "-nic user,model=npcm7xx-emc " + "-nic user,model=npcm-gmac " + "-nic user,model=npcm-gmac", + test_sockets[1], module_num); g_test_queue_destroy(packet_test_clear, test_sockets); return test_sockets; } +#endif /* _WIN32 */ static uint32_t emc_read(QTestState *qts, const EMCModule *mod, NPCM7xxPWMRegister regno) @@ -250,6 +245,7 @@ static uint32_t emc_read(QTestState *qts, const EMCModule *mod, return qtest_readl(qts, mod->base_addr + regno * sizeof(uint32_t)); } +#ifndef _WIN32 static void emc_write(QTestState *qts, const EMCModule *mod, NPCM7xxPWMRegister regno, uint32_t value) { @@ -339,6 +335,7 @@ static bool emc_soft_reset(QTestState *qts, const EMCModule *mod) g_message("%s: Timeout expired", __func__); return false; } +#endif /* _WIN32 */ /* Check emc registers are reset to default value. */ static void test_init(gconstpointer test_data) @@ -377,7 +374,8 @@ static void test_init(gconstpointer test_data) #undef CHECK_REG - for (i = 0; i < NUM_CAMML_REGS; ++i) { + /* Skip over the MAC address registers, which is BASE+0 */ + for (i = 1; i < NUM_CAMML_REGS; ++i) { g_assert_cmpuint(emc_read(qts, mod, REG_CAMM_BASE + i * 2), ==, 0); g_assert_cmpuint(emc_read(qts, mod, REG_CAML_BASE + i * 2), ==, @@ -387,6 +385,7 @@ static void test_init(gconstpointer test_data) qtest_quit(qts); } +#ifndef _WIN32 static bool emc_wait_irq(QTestState *qts, const EMCModule *mod, int step, bool is_tx) { @@ -790,7 +789,7 @@ static void emc_test_ptle(QTestState *qts, const EMCModule *mod, int fd) static void test_tx(gconstpointer test_data) { const TestData *td = test_data; - GString *cmd_line = g_string_new("-machine quanta-gsj"); + g_autoptr(GString) cmd_line = g_string_new("-machine quanta-gsj"); int *test_sockets = packet_test_init(emc_module_index(td->module), cmd_line); QTestState *qts = qtest_init(cmd_line->str); @@ -815,7 +814,7 @@ static void test_tx(gconstpointer test_data) static void test_rx(gconstpointer test_data) { const TestData *td = test_data; - GString *cmd_line = g_string_new("-machine quanta-gsj"); + g_autoptr(GString) cmd_line = g_string_new("-machine quanta-gsj"); int *test_sockets = packet_test_init(emc_module_index(td->module), cmd_line); QTestState *qts = qtest_init(cmd_line->str); @@ -843,6 +842,7 @@ static void test_rx(gconstpointer test_data) qtest_quit(qts); } +#endif /* _WIN32 */ static void emc_add_test(const char *name, const TestData* td, GTestDataFunc fn) @@ -865,8 +865,10 @@ int main(int argc, char **argv) td->module = &emc_module_list[i]; add_test(init, td); +#ifndef _WIN32 add_test(tx, td); add_test(rx, td); +#endif } return g_test_run(); diff --git a/tests/qtest/npcm7xx_pwm-test.c b/tests/qtest/npcm7xx_pwm-test.c index e320a625c4..b53a43c417 100644 --- a/tests/qtest/npcm7xx_pwm-test.c +++ b/tests/qtest/npcm7xx_pwm-test.c @@ -20,6 +20,8 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qnum.h" +static int verbosity_level; + #define REF_HZ 25000000 /* Register field definitions. */ @@ -221,7 +223,9 @@ static uint64_t pwm_qom_get(QTestState *qts, const char *path, const char *name) QDict *response; uint64_t val; - g_test_message("Getting properties %s from %s", name, path); + if (verbosity_level >= 2) { + g_test_message("Getting properties %s from %s", name, path); + } response = qtest_qmp(qts, "{ 'execute': 'qom-get'," " 'arguments': { 'path': %s, 'property': %s}}", path, name); @@ -260,8 +264,10 @@ static void mft_qom_set(QTestState *qts, int index, const char *name, QDict *response; char *path = g_strdup_printf("/machine/soc/mft[%d]", index); - g_test_message("Setting properties %s of mft[%d] with value %u", - name, index, value); + if (verbosity_level >= 2) { + g_test_message("Setting properties %s of mft[%d] with value %u", + name, index, value); + } response = qtest_qmp(qts, "{ 'execute': 'qom-set'," " 'arguments': { 'path': %s, " " 'property': %s, 'value': %u}}", @@ -506,9 +512,12 @@ static void mft_verify_rpm(QTestState *qts, const TestData *td, uint64_t duty) int32_t expected_cnt = mft_compute_cnt(rpm, clk); qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); - g_test_message( - "verifying rpm for mft[%d]: clk: %" PRIu64 ", duty: %" PRIu64 ", rpm: %u, cnt: %d", - index, clk, duty, rpm, expected_cnt); + if (verbosity_level >= 2) { + g_test_message( + "verifying rpm for mft[%d]: clk: %" PRIu64 ", duty: %" PRIu64 + ", rpm: %u, cnt: %d", + index, clk, duty, rpm, expected_cnt); + } /* Verify rpm for fan A */ /* Stop capture */ @@ -597,6 +606,7 @@ static void test_toggle(gconstpointer test_data) uint32_t ppr, csr, pcr, cnr, cmr; int i, j, k, l; uint64_t expected_freq, expected_duty; + int cnr_step = g_test_quick() ? 2 : 1; mft_init(qts, td); @@ -609,7 +619,7 @@ static void test_toggle(gconstpointer test_data) csr = csr_list[j]; pwm_write_csr(qts, td, csr); - for (k = 0; k < ARRAY_SIZE(cnr_list); ++k) { + for (k = 0; k < ARRAY_SIZE(cnr_list); k += cnr_step) { cnr = cnr_list[k]; pwm_write_cnr(qts, td, cnr); @@ -669,11 +679,23 @@ static void pwm_add_test(const char *name, const TestData* td, int main(int argc, char **argv) { TestData test_data_list[ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)]; + int pwm_module_list_cnt = 1, pwm_list_cnt = 1; + + char *v_env = getenv("V"); + + if (v_env) { + verbosity_level = atoi(v_env); + } g_test_init(&argc, &argv, NULL); - for (int i = 0; i < ARRAY_SIZE(pwm_module_list); ++i) { - for (int j = 0; j < ARRAY_SIZE(pwm_list); ++j) { + if (!g_test_quick()) { + pwm_module_list_cnt = ARRAY_SIZE(pwm_module_list); + pwm_list_cnt = ARRAY_SIZE(pwm_list); + } + + for (int i = 0; i < pwm_module_list_cnt; ++i) { + for (int j = 0; j < pwm_list_cnt; ++j) { TestData *td = &test_data_list[i * ARRAY_SIZE(pwm_list) + j]; td->module = &pwm_module_list[i]; diff --git a/tests/qtest/npcm7xx_sdhci-test.c b/tests/qtest/npcm7xx_sdhci-test.c index aa35a77e8d..5d68540e52 100644 --- a/tests/qtest/npcm7xx_sdhci-test.c +++ b/tests/qtest/npcm7xx_sdhci-test.c @@ -24,7 +24,7 @@ #define NPCM7XX_REG_SIZE 0x100 #define NPCM7XX_MMC_BA 0xF0842000 #define NPCM7XX_BLK_SIZE 512 -#define NPCM7XX_TEST_IMAGE_SIZE (1 << 30) +#define NPCM7XX_TEST_IMAGE_SIZE (1 << 20) char *sd_path; diff --git a/tests/qtest/npcm7xx_timer-test.c b/tests/qtest/npcm7xx_timer-test.c index 83774a5b90..58f58c2f71 100644 --- a/tests/qtest/npcm7xx_timer-test.c +++ b/tests/qtest/npcm7xx_timer-test.c @@ -384,7 +384,7 @@ static void test_pause_resume(gconstpointer test_data) g_assert_true(qtest_get_irq(global_qtest, tim_timer_irq(td))); } -/* Verifies that the prescaler can be changed while the timer is runnin. */ +/* Verifies that the prescaler can be changed while the timer is running. */ static void test_prescaler_change(gconstpointer test_data) { const TestData *td = test_data; @@ -465,6 +465,7 @@ static void test_periodic_interrupt(gconstpointer test_data) int i; tim_reset(td); + clock_step_next(); tim_write_ticr(td, count); tim_write_tcsr(td, CEN | IE | MODE_PERIODIC | PRESCALE(ps)); diff --git a/tests/qtest/npcm7xx_watchdog_timer-test.c b/tests/qtest/npcm7xx_watchdog_timer-test.c index 4773a673b2..981b853c99 100644 --- a/tests/qtest/npcm7xx_watchdog_timer-test.c +++ b/tests/qtest/npcm7xx_watchdog_timer-test.c @@ -172,9 +172,10 @@ static void test_reset_action(gconstpointer watchdog) static void test_prescaler(gconstpointer watchdog) { const Watchdog *wd = watchdog; + int inc = g_test_quick() ? 3 : 1; - for (int wtclk = 0; wtclk < 4; ++wtclk) { - for (int wtis = 0; wtis < 4; ++wtis) { + for (int wtclk = 0; wtclk < 4; wtclk += inc) { + for (int wtis = 0; wtis < 4; wtis += inc) { QTestState *qts = qtest_init("-machine quanta-gsj"); qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); diff --git a/tests/qtest/npcm_gmac-test.c b/tests/qtest/npcm_gmac-test.c new file mode 100644 index 0000000000..c28b471ab2 --- /dev/null +++ b/tests/qtest/npcm_gmac-test.c @@ -0,0 +1,264 @@ +/* + * QTests for Nuvoton NPCM7xx/8xx GMAC Modules. + * + * Copyright 2024 Google LLC + * Authors: + * Hao Wu + * Nabih Estefan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "libqos/libqos.h" + +/* Name of the GMAC Device */ +#define TYPE_NPCM_GMAC "npcm-gmac" + +/* Address of the PCS Module */ +#define PCS_BASE_ADDRESS 0xf0780000 +#define NPCM_PCS_IND_AC_BA 0x1fe + +typedef struct GMACModule { + int irq; + uint64_t base_addr; +} GMACModule; + +typedef struct TestData { + const GMACModule *module; +} TestData; + +/* Values extracted from hw/arm/npcm7xx.c */ +static const GMACModule gmac_module_list[] = { + { + .irq = 14, + .base_addr = 0xf0802000 + }, + { + .irq = 15, + .base_addr = 0xf0804000 + }, +}; + +/* Returns the index of the GMAC module. */ +static int gmac_module_index(const GMACModule *mod) +{ + ptrdiff_t diff = mod - gmac_module_list; + + g_assert_true(diff >= 0 && diff < ARRAY_SIZE(gmac_module_list)); + + return diff; +} + +/* 32-bit register indices. Taken from npcm_gmac.c */ +typedef enum NPCMRegister { + /* DMA Registers */ + NPCM_DMA_BUS_MODE = 0x1000, + NPCM_DMA_XMT_POLL_DEMAND = 0x1004, + NPCM_DMA_RCV_POLL_DEMAND = 0x1008, + NPCM_DMA_RCV_BASE_ADDR = 0x100c, + NPCM_DMA_TX_BASE_ADDR = 0x1010, + NPCM_DMA_STATUS = 0x1014, + NPCM_DMA_CONTROL = 0x1018, + NPCM_DMA_INTR_ENA = 0x101c, + NPCM_DMA_MISSED_FRAME_CTR = 0x1020, + NPCM_DMA_HOST_TX_DESC = 0x1048, + NPCM_DMA_HOST_RX_DESC = 0x104c, + NPCM_DMA_CUR_TX_BUF_ADDR = 0x1050, + NPCM_DMA_CUR_RX_BUF_ADDR = 0x1054, + NPCM_DMA_HW_FEATURE = 0x1058, + + /* GMAC Registers */ + NPCM_GMAC_MAC_CONFIG = 0x0, + NPCM_GMAC_FRAME_FILTER = 0x4, + NPCM_GMAC_HASH_HIGH = 0x8, + NPCM_GMAC_HASH_LOW = 0xc, + NPCM_GMAC_MII_ADDR = 0x10, + NPCM_GMAC_MII_DATA = 0x14, + NPCM_GMAC_FLOW_CTRL = 0x18, + NPCM_GMAC_VLAN_FLAG = 0x1c, + NPCM_GMAC_VERSION = 0x20, + NPCM_GMAC_WAKEUP_FILTER = 0x28, + NPCM_GMAC_PMT = 0x2c, + NPCM_GMAC_LPI_CTRL = 0x30, + NPCM_GMAC_TIMER_CTRL = 0x34, + NPCM_GMAC_INT_STATUS = 0x38, + NPCM_GMAC_INT_MASK = 0x3c, + NPCM_GMAC_MAC0_ADDR_HI = 0x40, + NPCM_GMAC_MAC0_ADDR_LO = 0x44, + NPCM_GMAC_MAC1_ADDR_HI = 0x48, + NPCM_GMAC_MAC1_ADDR_LO = 0x4c, + NPCM_GMAC_MAC2_ADDR_HI = 0x50, + NPCM_GMAC_MAC2_ADDR_LO = 0x54, + NPCM_GMAC_MAC3_ADDR_HI = 0x58, + NPCM_GMAC_MAC3_ADDR_LO = 0x5c, + NPCM_GMAC_RGMII_STATUS = 0xd8, + NPCM_GMAC_WATCHDOG = 0xdc, + NPCM_GMAC_PTP_TCR = 0x700, + NPCM_GMAC_PTP_SSIR = 0x704, + NPCM_GMAC_PTP_STSR = 0x708, + NPCM_GMAC_PTP_STNSR = 0x70c, + NPCM_GMAC_PTP_STSUR = 0x710, + NPCM_GMAC_PTP_STNSUR = 0x714, + NPCM_GMAC_PTP_TAR = 0x718, + NPCM_GMAC_PTP_TTSR = 0x71c, + + /* PCS Registers */ + NPCM_PCS_SR_CTL_ID1 = 0x3c0008, + NPCM_PCS_SR_CTL_ID2 = 0x3c000a, + NPCM_PCS_SR_CTL_STS = 0x3c0010, + + NPCM_PCS_SR_MII_CTRL = 0x3e0000, + NPCM_PCS_SR_MII_STS = 0x3e0002, + NPCM_PCS_SR_MII_DEV_ID1 = 0x3e0004, + NPCM_PCS_SR_MII_DEV_ID2 = 0x3e0006, + NPCM_PCS_SR_MII_AN_ADV = 0x3e0008, + NPCM_PCS_SR_MII_LP_BABL = 0x3e000a, + NPCM_PCS_SR_MII_AN_EXPN = 0x3e000c, + NPCM_PCS_SR_MII_EXT_STS = 0x3e001e, + + NPCM_PCS_SR_TIM_SYNC_ABL = 0x3e0e10, + NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR = 0x3e0e12, + NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_UPR = 0x3e0e14, + NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR = 0x3e0e16, + NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_UPR = 0x3e0e18, + NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR = 0x3e0e1a, + NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_UPR = 0x3e0e1c, + NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR = 0x3e0e1e, + NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_UPR = 0x3e0e20, + + NPCM_PCS_VR_MII_MMD_DIG_CTRL1 = 0x3f0000, + NPCM_PCS_VR_MII_AN_CTRL = 0x3f0002, + NPCM_PCS_VR_MII_AN_INTR_STS = 0x3f0004, + NPCM_PCS_VR_MII_TC = 0x3f0006, + NPCM_PCS_VR_MII_DBG_CTRL = 0x3f000a, + NPCM_PCS_VR_MII_EEE_MCTRL0 = 0x3f000c, + NPCM_PCS_VR_MII_EEE_TXTIMER = 0x3f0010, + NPCM_PCS_VR_MII_EEE_RXTIMER = 0x3f0012, + NPCM_PCS_VR_MII_LINK_TIMER_CTRL = 0x3f0014, + NPCM_PCS_VR_MII_EEE_MCTRL1 = 0x3f0016, + NPCM_PCS_VR_MII_DIG_STS = 0x3f0020, + NPCM_PCS_VR_MII_ICG_ERRCNT1 = 0x3f0022, + NPCM_PCS_VR_MII_MISC_STS = 0x3f0030, + NPCM_PCS_VR_MII_RX_LSTS = 0x3f0040, + NPCM_PCS_VR_MII_MP_TX_BSTCTRL0 = 0x3f0070, + NPCM_PCS_VR_MII_MP_TX_LVLCTRL0 = 0x3f0074, + NPCM_PCS_VR_MII_MP_TX_GENCTRL0 = 0x3f007a, + NPCM_PCS_VR_MII_MP_TX_GENCTRL1 = 0x3f007c, + NPCM_PCS_VR_MII_MP_TX_STS = 0x3f0090, + NPCM_PCS_VR_MII_MP_RX_GENCTRL0 = 0x3f00b0, + NPCM_PCS_VR_MII_MP_RX_GENCTRL1 = 0x3f00b2, + NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0 = 0x3f00ba, + NPCM_PCS_VR_MII_MP_MPLL_CTRL0 = 0x3f00f0, + NPCM_PCS_VR_MII_MP_MPLL_CTRL1 = 0x3f00f2, + NPCM_PCS_VR_MII_MP_MPLL_STS = 0x3f0110, + NPCM_PCS_VR_MII_MP_MISC_CTRL2 = 0x3f0126, + NPCM_PCS_VR_MII_MP_LVL_CTRL = 0x3f0130, + NPCM_PCS_VR_MII_MP_MISC_CTRL0 = 0x3f0132, + NPCM_PCS_VR_MII_MP_MISC_CTRL1 = 0x3f0134, + NPCM_PCS_VR_MII_DIG_CTRL2 = 0x3f01c2, + NPCM_PCS_VR_MII_DIG_ERRCNT_SEL = 0x3f01c4, +} NPCMRegister; + +static uint32_t gmac_read(QTestState *qts, const GMACModule *mod, + NPCMRegister regno) +{ + return qtest_readl(qts, mod->base_addr + regno); +} + +/* Check that GMAC registers are reset to default value */ +static void test_init(gconstpointer test_data) +{ + const TestData *td = test_data; + const GMACModule *mod = td->module; + QTestState *qts = qtest_init("-machine npcm750-evb"); + +#define CHECK_REG32(regno, value) \ + do { \ + g_assert_cmphex(gmac_read(qts, mod, (regno)), ==, (value)); \ + } while (0) + + CHECK_REG32(NPCM_DMA_BUS_MODE, 0x00020100); + CHECK_REG32(NPCM_DMA_XMT_POLL_DEMAND, 0); + CHECK_REG32(NPCM_DMA_RCV_POLL_DEMAND, 0); + CHECK_REG32(NPCM_DMA_RCV_BASE_ADDR, 0); + CHECK_REG32(NPCM_DMA_TX_BASE_ADDR, 0); + CHECK_REG32(NPCM_DMA_STATUS, 0); + CHECK_REG32(NPCM_DMA_CONTROL, 0); + CHECK_REG32(NPCM_DMA_INTR_ENA, 0); + CHECK_REG32(NPCM_DMA_MISSED_FRAME_CTR, 0); + CHECK_REG32(NPCM_DMA_HOST_TX_DESC, 0); + CHECK_REG32(NPCM_DMA_HOST_RX_DESC, 0); + CHECK_REG32(NPCM_DMA_CUR_TX_BUF_ADDR, 0); + CHECK_REG32(NPCM_DMA_CUR_RX_BUF_ADDR, 0); + CHECK_REG32(NPCM_DMA_HW_FEATURE, 0x100d4f37); + + CHECK_REG32(NPCM_GMAC_MAC_CONFIG, 0); + CHECK_REG32(NPCM_GMAC_FRAME_FILTER, 0); + CHECK_REG32(NPCM_GMAC_HASH_HIGH, 0); + CHECK_REG32(NPCM_GMAC_HASH_LOW, 0); + CHECK_REG32(NPCM_GMAC_MII_ADDR, 0); + CHECK_REG32(NPCM_GMAC_MII_DATA, 0); + CHECK_REG32(NPCM_GMAC_FLOW_CTRL, 0); + CHECK_REG32(NPCM_GMAC_VLAN_FLAG, 0); + CHECK_REG32(NPCM_GMAC_VERSION, 0x00001032); + CHECK_REG32(NPCM_GMAC_WAKEUP_FILTER, 0); + CHECK_REG32(NPCM_GMAC_PMT, 0); + CHECK_REG32(NPCM_GMAC_LPI_CTRL, 0); + CHECK_REG32(NPCM_GMAC_TIMER_CTRL, 0x03e80000); + CHECK_REG32(NPCM_GMAC_INT_STATUS, 0); + CHECK_REG32(NPCM_GMAC_INT_MASK, 0); + CHECK_REG32(NPCM_GMAC_MAC0_ADDR_HI, 0x8000ffff); + CHECK_REG32(NPCM_GMAC_MAC0_ADDR_LO, 0xffffffff); + CHECK_REG32(NPCM_GMAC_MAC1_ADDR_HI, 0x0000ffff); + CHECK_REG32(NPCM_GMAC_MAC1_ADDR_LO, 0xffffffff); + CHECK_REG32(NPCM_GMAC_MAC2_ADDR_HI, 0x0000ffff); + CHECK_REG32(NPCM_GMAC_MAC2_ADDR_LO, 0xffffffff); + CHECK_REG32(NPCM_GMAC_MAC3_ADDR_HI, 0x0000ffff); + CHECK_REG32(NPCM_GMAC_MAC3_ADDR_LO, 0xffffffff); + CHECK_REG32(NPCM_GMAC_RGMII_STATUS, 0); + CHECK_REG32(NPCM_GMAC_WATCHDOG, 0); + CHECK_REG32(NPCM_GMAC_PTP_TCR, 0x00002000); + CHECK_REG32(NPCM_GMAC_PTP_SSIR, 0); + CHECK_REG32(NPCM_GMAC_PTP_STSR, 0); + CHECK_REG32(NPCM_GMAC_PTP_STNSR, 0); + CHECK_REG32(NPCM_GMAC_PTP_STSUR, 0); + CHECK_REG32(NPCM_GMAC_PTP_STNSUR, 0); + CHECK_REG32(NPCM_GMAC_PTP_TAR, 0); + CHECK_REG32(NPCM_GMAC_PTP_TTSR, 0); + + qtest_quit(qts); +} + +static void gmac_add_test(const char *name, const TestData* td, + GTestDataFunc fn) +{ + g_autofree char *full_name = g_strdup_printf( + "npcm7xx_gmac/gmac[%d]/%s", gmac_module_index(td->module), name); + qtest_add_data_func(full_name, td, fn); +} + +int main(int argc, char **argv) +{ + TestData test_data_list[ARRAY_SIZE(gmac_module_list)]; + + g_test_init(&argc, &argv, NULL); + + for (int i = 0; i < ARRAY_SIZE(gmac_module_list); ++i) { + TestData *td = &test_data_list[i]; + + td->module = &gmac_module_list[i]; + + gmac_add_test("init", td, test_init); + } + + return g_test_run(); +} diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c index c5eb13f349..4f4404a4b1 100644 --- a/tests/qtest/numa-test.c +++ b/tests/qtest/numa-test.c @@ -568,7 +568,7 @@ int main(int argc, char **argv) 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")) { + if (!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); @@ -576,6 +576,11 @@ int main(int argc, char **argv) qtest_add_data_func("/numa/pc/hmat/erange", args, pc_hmat_erange_cfg); } + if (!strcmp(arch, "i386")) { + 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); } diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c index d80ed93cd3..7474957692 100644 --- a/tests/qtest/pca9552-test.c +++ b/tests/qtest/pca9552-test.c @@ -12,7 +12,7 @@ #include "libqtest.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "hw/misc/pca9552_regs.h" +#include "hw/gpio/pca9552_regs.h" #define PCA9552_TEST_ID "pca9552-test" #define PCA9552_TEST_ADDR 0x60 @@ -60,7 +60,7 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmphex(value, ==, 0x55); value = i2c_get8(i2cdev, PCA9552_INPUT0); - g_assert_cmphex(value, ==, 0x0); + g_assert_cmphex(value, ==, 0xFF); pca9552_init(i2cdev); @@ -68,13 +68,13 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmphex(value, ==, 0x54); value = i2c_get8(i2cdev, PCA9552_INPUT0); - g_assert_cmphex(value, ==, 0x01); + g_assert_cmphex(value, ==, 0xFE); value = i2c_get8(i2cdev, PCA9552_LS3); g_assert_cmphex(value, ==, 0x54); value = i2c_get8(i2cdev, PCA9552_INPUT1); - g_assert_cmphex(value, ==, 0x10); + g_assert_cmphex(value, ==, 0xEF); } static void pca9552_register_nodes(void) diff --git a/tests/qtest/pflash-cfi02-test.c b/tests/qtest/pflash-cfi02-test.c index 7fce614b64..8c073efcb4 100644 --- a/tests/qtest/pflash-cfi02-test.c +++ b/tests/qtest/pflash-cfi02-test.c @@ -56,7 +56,7 @@ typedef struct { QTestState *qtest; } FlashConfig; -static char image_path[] = "/tmp/qtest.XXXXXX"; +static char *image_path; /* * The pflash implementation allows some parameters to be unspecified. We want @@ -406,7 +406,7 @@ static void test_geometry(const void *opaque) for (int region = 0; region < nb_erase_regions; ++region) { for (uint32_t i = 0; i < c->nb_blocs[region]; ++i) { - uint64_t byte_addr = (uint64_t)i * c->sector_len[region]; + byte_addr = (uint64_t)i * c->sector_len[region]; g_assert_cmphex(flash_read(c, byte_addr), ==, bank_mask(c)); } } @@ -608,6 +608,7 @@ static void test_cfi_in_autoselect(const void *opaque) static void cleanup(void *opaque) { unlink(image_path); + g_free(image_path); } /* @@ -635,16 +636,14 @@ static const FlashConfig configuration[] = { int main(int argc, char **argv) { - int fd = mkstemp(image_path); - if (fd == -1) { - g_printerr("Failed to create temporary file %s: %s\n", image_path, - strerror(errno)); - exit(EXIT_FAILURE); - } + GError *err = NULL; + int fd = g_file_open_tmp("qtest.XXXXXX", &image_path, &err); + g_assert_no_error(err); + if (ftruncate(fd, UNIFORM_FLASH_SIZE) < 0) { int error_code = errno; close(fd); - unlink(image_path); + cleanup(NULL); g_printerr("Failed to truncate file %s to %u MB: %s\n", image_path, UNIFORM_FLASH_SIZE, strerror(error_code)); exit(EXIT_FAILURE); diff --git a/tests/qtest/pnv-host-i2c-test.c b/tests/qtest/pnv-host-i2c-test.c new file mode 100644 index 0000000000..7f64d597ac --- /dev/null +++ b/tests/qtest/pnv-host-i2c-test.c @@ -0,0 +1,491 @@ +/* + * QTest testcase for PowerNV 10 Host I2C Communications + * + * Copyright (c) 2023, IBM Corporation. + * + * 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 "hw/gpio/pca9554_regs.h" +#include "hw/gpio/pca9552_regs.h" +#include "pnv-xscom.h" + +#define PPC_BIT(bit) (0x8000000000000000ULL >> (bit)) +#define PPC_BIT32(bit) (0x80000000 >> (bit)) +#define PPC_BIT8(bit) (0x80 >> (bit)) +#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs)) +#define PPC_BITMASK32(bs, be) ((PPC_BIT32(bs) - PPC_BIT32(be)) | \ + PPC_BIT32(bs)) + +#define MASK_TO_LSH(m) (__builtin_ffsll(m) - 1) +#define GETFIELD(m, v) (((v) & (m)) >> MASK_TO_LSH(m)) +#define SETFIELD(m, v, val) \ + (((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) & (m))) + +#define PNV10_XSCOM_I2CM_BASE 0xa0000 +#define PNV10_XSCOM_I2CM_SIZE 0x1000 + +#include "hw/i2c/pnv_i2c_regs.h" + +typedef struct { + QTestState *qts; + const PnvChip *chip; + int engine; +} PnvI2cCtlr; + +typedef struct { + PnvI2cCtlr *ctlr; + int port; + uint8_t addr; +} PnvI2cDev; + + +static uint64_t pnv_i2c_xscom_addr(PnvI2cCtlr *ctlr, uint32_t reg) +{ + return pnv_xscom_addr(ctlr->chip, PNV10_XSCOM_I2CM_BASE + + (PNV10_XSCOM_I2CM_SIZE * ctlr->engine) + reg); +} + +static uint64_t pnv_i2c_xscom_read(PnvI2cCtlr *ctlr, uint32_t reg) +{ + return qtest_readq(ctlr->qts, pnv_i2c_xscom_addr(ctlr, reg)); +} + +static void pnv_i2c_xscom_write(PnvI2cCtlr *ctlr, uint32_t reg, uint64_t val) +{ + qtest_writeq(ctlr->qts, pnv_i2c_xscom_addr(ctlr, reg), val); +} + +/* Write len bytes from buf to i2c device with given addr and port */ +static void pnv_i2c_send(PnvI2cDev *dev, const uint8_t *buf, uint16_t len) +{ + int byte_num; + uint64_t reg64; + + /* select requested port */ + reg64 = SETFIELD(I2C_MODE_BIT_RATE_DIV, 0ull, 0x2be); + reg64 = SETFIELD(I2C_MODE_PORT_NUM, reg64, dev->port); + pnv_i2c_xscom_write(dev->ctlr, I2C_MODE_REG, reg64); + + /* check status for cmd complete and bus idle */ + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG); + g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0); + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG); + g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==, + I2C_STAT_CMD_COMP); + + /* Send start, with stop, with address and len bytes of data */ + reg64 = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR | I2C_CMD_WITH_STOP; + reg64 = SETFIELD(I2C_CMD_DEV_ADDR, reg64, dev->addr); + reg64 = SETFIELD(I2C_CMD_LEN_BYTES, reg64, len); + pnv_i2c_xscom_write(dev->ctlr, I2C_CMD_REG, reg64); + + /* check status for errors */ + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG); + g_assert_cmphex(reg64 & I2C_STAT_ANY_ERR, ==, 0); + + /* write data bytes to fifo register */ + for (byte_num = 0; byte_num < len; byte_num++) { + reg64 = SETFIELD(I2C_FIFO, 0ull, buf[byte_num]); + pnv_i2c_xscom_write(dev->ctlr, I2C_FIFO_REG, reg64); + } + + /* check status for cmd complete and bus idle */ + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG); + g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0); + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG); + g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==, + I2C_STAT_CMD_COMP); +} + +/* Recieve len bytes into buf from i2c device with given addr and port */ +static void pnv_i2c_recv(PnvI2cDev *dev, uint8_t *buf, uint16_t len) +{ + int byte_num; + uint64_t reg64; + + /* select requested port */ + reg64 = SETFIELD(I2C_MODE_BIT_RATE_DIV, 0ull, 0x2be); + reg64 = SETFIELD(I2C_MODE_PORT_NUM, reg64, dev->port); + pnv_i2c_xscom_write(dev->ctlr, I2C_MODE_REG, reg64); + + /* check status for cmd complete and bus idle */ + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG); + g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0); + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG); + g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==, + I2C_STAT_CMD_COMP); + + /* Send start, with stop, with address and len bytes of data */ + reg64 = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR | + I2C_CMD_WITH_STOP | I2C_CMD_READ_NOT_WRITE; + reg64 = SETFIELD(I2C_CMD_DEV_ADDR, reg64, dev->addr); + reg64 = SETFIELD(I2C_CMD_LEN_BYTES, reg64, len); + pnv_i2c_xscom_write(dev->ctlr, I2C_CMD_REG, reg64); + + /* check status for errors */ + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG); + g_assert_cmphex(reg64 & I2C_STAT_ANY_ERR, ==, 0); + + /* Read data bytes from fifo register */ + for (byte_num = 0; byte_num < len; byte_num++) { + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_FIFO_REG); + buf[byte_num] = GETFIELD(I2C_FIFO, reg64); + } + + /* check status for cmd complete and bus idle */ + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG); + g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0); + reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG); + g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==, + I2C_STAT_CMD_COMP); +} + +static void pnv_i2c_pca9554_default_cfg(PnvI2cDev *dev) +{ + uint8_t buf[2]; + + /* input register bits are not inverted */ + buf[0] = PCA9554_POLARITY; + buf[1] = 0; + pnv_i2c_send(dev, buf, 2); + + /* All pins are inputs */ + buf[0] = PCA9554_CONFIG; + buf[1] = 0xff; + pnv_i2c_send(dev, buf, 2); + + /* Output value for when pins are outputs */ + buf[0] = PCA9554_OUTPUT; + buf[1] = 0xff; + pnv_i2c_send(dev, buf, 2); +} + +static void pnv_i2c_pca9554_set_pin(PnvI2cDev *dev, int pin, bool high) +{ + uint8_t send_buf[2]; + uint8_t recv_buf[2]; + uint8_t mask = 0x1 << pin; + uint8_t new_value = ((high) ? 1 : 0) << pin; + + /* read current OUTPUT value */ + send_buf[0] = PCA9554_OUTPUT; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + + /* write new OUTPUT value */ + send_buf[1] = (recv_buf[0] & ~mask) | new_value; + pnv_i2c_send(dev, send_buf, 2); + + /* Update config bit for output */ + send_buf[0] = PCA9554_CONFIG; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + send_buf[1] = recv_buf[0] & ~mask; + pnv_i2c_send(dev, send_buf, 2); +} + +static uint8_t pnv_i2c_pca9554_read_pins(PnvI2cDev *dev) +{ + uint8_t send_buf[1]; + uint8_t recv_buf[1]; + uint8_t inputs; + send_buf[0] = PCA9554_INPUT; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + inputs = recv_buf[0]; + return inputs; +} + +static void pnv_i2c_pca9554_flip_polarity(PnvI2cDev *dev) +{ + uint8_t recv_buf[1]; + uint8_t send_buf[2]; + + send_buf[0] = PCA9554_POLARITY; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + send_buf[1] = recv_buf[0] ^ 0xff; + pnv_i2c_send(dev, send_buf, 2); +} + +static void pnv_i2c_pca9554_default_inputs(PnvI2cDev *dev) +{ + uint8_t pin_values = pnv_i2c_pca9554_read_pins(dev); + g_assert_cmphex(pin_values, ==, 0xff); +} + +/* Check that setting pin values and polarity changes inputs as expected */ +static void pnv_i2c_pca554_set_pins(PnvI2cDev *dev) +{ + uint8_t pin_values; + pnv_i2c_pca9554_set_pin(dev, 0, 0); + pin_values = pnv_i2c_pca9554_read_pins(dev); + g_assert_cmphex(pin_values, ==, 0xfe); + pnv_i2c_pca9554_flip_polarity(dev); + pin_values = pnv_i2c_pca9554_read_pins(dev); + g_assert_cmphex(pin_values, ==, 0x01); + pnv_i2c_pca9554_set_pin(dev, 2, 0); + pin_values = pnv_i2c_pca9554_read_pins(dev); + g_assert_cmphex(pin_values, ==, 0x05); + pnv_i2c_pca9554_flip_polarity(dev); + pin_values = pnv_i2c_pca9554_read_pins(dev); + g_assert_cmphex(pin_values, ==, 0xfa); + pnv_i2c_pca9554_default_cfg(dev); + pin_values = pnv_i2c_pca9554_read_pins(dev); + g_assert_cmphex(pin_values, ==, 0xff); +} + +static void pnv_i2c_pca9552_default_cfg(PnvI2cDev *dev) +{ + uint8_t buf[2]; + /* configure pwm/psc regs */ + buf[0] = PCA9552_PSC0; + buf[1] = 0xff; + pnv_i2c_send(dev, buf, 2); + buf[0] = PCA9552_PWM0; + buf[1] = 0x80; + pnv_i2c_send(dev, buf, 2); + buf[0] = PCA9552_PSC1; + buf[1] = 0xff; + pnv_i2c_send(dev, buf, 2); + buf[0] = PCA9552_PWM1; + buf[1] = 0x80; + pnv_i2c_send(dev, buf, 2); + + /* configure all pins as inputs */ + buf[0] = PCA9552_LS0; + buf[1] = 0x55; + pnv_i2c_send(dev, buf, 2); + buf[0] = PCA9552_LS1; + buf[1] = 0x55; + pnv_i2c_send(dev, buf, 2); + buf[0] = PCA9552_LS2; + buf[1] = 0x55; + pnv_i2c_send(dev, buf, 2); + buf[0] = PCA9552_LS3; + buf[1] = 0x55; + pnv_i2c_send(dev, buf, 2); +} + +static void pnv_i2c_pca9552_set_pin(PnvI2cDev *dev, int pin, bool high) +{ + uint8_t send_buf[2]; + uint8_t recv_buf[2]; + uint8_t reg = PCA9552_LS0 + (pin / 4); + uint8_t shift = (pin % 4) * 2; + uint8_t mask = ~(0x3 << shift); + uint8_t new_value = ((high) ? 1 : 0) << shift; + + /* read current LSx value */ + send_buf[0] = reg; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + + /* write new value to LSx */ + send_buf[1] = (recv_buf[0] & mask) | new_value; + pnv_i2c_send(dev, send_buf, 2); +} + +static uint16_t pnv_i2c_pca9552_read_pins(PnvI2cDev *dev) +{ + uint8_t send_buf[2]; + uint8_t recv_buf[2]; + uint16_t inputs; + send_buf[0] = PCA9552_INPUT0; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + inputs = recv_buf[0]; + send_buf[0] = PCA9552_INPUT1; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + inputs |= recv_buf[0] << 8; + return inputs; +} + +static void pnv_i2c_pca9552_default_inputs(PnvI2cDev *dev) +{ + uint16_t pin_values = pnv_i2c_pca9552_read_pins(dev); + g_assert_cmphex(pin_values, ==, 0xffff); +} + +/* + * Set pins 0-4 one at a time and verify that pins 5-9 are + * set to the same value + */ +static void pnv_i2c_pca552_set_pins(PnvI2cDev *dev) +{ + uint16_t pin_values; + + /* set pin 0 low */ + pnv_i2c_pca9552_set_pin(dev, 0, 0); + pin_values = pnv_i2c_pca9552_read_pins(dev); + + /* pins 0 and 5 should be low */ + g_assert_cmphex(pin_values, ==, 0xffde); + + /* set pin 1 low */ + pnv_i2c_pca9552_set_pin(dev, 1, 0); + pin_values = pnv_i2c_pca9552_read_pins(dev); + + /* pins 0, 1, 5 and 6 should be low */ + g_assert_cmphex(pin_values, ==, 0xff9c); + + /* set pin 2 low */ + pnv_i2c_pca9552_set_pin(dev, 2, 0); + pin_values = pnv_i2c_pca9552_read_pins(dev); + + /* pins 0, 1, 2, 5, 6 and 7 should be low */ + g_assert_cmphex(pin_values, ==, 0xff18); + + /* set pin 3 low */ + pnv_i2c_pca9552_set_pin(dev, 3, 0); + pin_values = pnv_i2c_pca9552_read_pins(dev); + + /* pins 0, 1, 2, 3, 5, 6, 7 and 8 should be low */ + g_assert_cmphex(pin_values, ==, 0xfe10); + + /* set pin 4 low */ + pnv_i2c_pca9552_set_pin(dev, 4, 0); + pin_values = pnv_i2c_pca9552_read_pins(dev); + + /* pins 0, 1, 2, 3, 5, 6, 7, 8 and 9 should be low */ + g_assert_cmphex(pin_values, ==, 0xfc00); + + /* reset all pins to the high state */ + pnv_i2c_pca9552_default_cfg(dev); + pin_values = pnv_i2c_pca9552_read_pins(dev); + + /* verify all pins went back to the high state */ + g_assert_cmphex(pin_values, ==, 0xffff); +} + +static void reset_engine(PnvI2cCtlr *ctlr) +{ + pnv_i2c_xscom_write(ctlr, I2C_RESET_I2C_REG, 0); +} + +static void check_i2cm_por_regs(QTestState *qts, const PnvChip *chip) +{ + int engine; + for (engine = 0; engine < chip->num_i2c; engine++) { + PnvI2cCtlr ctlr; + ctlr.qts = qts; + ctlr.chip = chip; + ctlr.engine = engine; + + /* Check version in Extended Status Register */ + uint64_t value = pnv_i2c_xscom_read(&ctlr, I2C_EXTD_STAT_REG); + g_assert_cmphex(value & I2C_EXTD_STAT_I2C_VERSION, ==, 0x1700000000); + + /* Check for command complete and bus idle in Status Register */ + value = pnv_i2c_xscom_read(&ctlr, I2C_STAT_REG); + g_assert_cmphex(value & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), + ==, + I2C_STAT_CMD_COMP); + } +} + +static void reset_all(QTestState *qts, const PnvChip *chip) +{ + int engine; + for (engine = 0; engine < chip->num_i2c; engine++) { + PnvI2cCtlr ctlr; + ctlr.qts = qts; + ctlr.chip = chip; + ctlr.engine = engine; + reset_engine(&ctlr); + pnv_i2c_xscom_write(&ctlr, I2C_MODE_REG, 0x02be040000000000); + } +} + +static void test_host_i2c(const void *data) +{ + const PnvChip *chip = data; + QTestState *qts; + const char *machine = "powernv8"; + PnvI2cCtlr ctlr; + PnvI2cDev pca9552; + PnvI2cDev pca9554; + + if (chip->chip_type == PNV_CHIP_POWER9) { + machine = "powernv9"; + } else if (chip->chip_type == PNV_CHIP_POWER10) { + machine = "powernv10-rainier"; + } + + qts = qtest_initf("-M %s -smp %d,cores=1,threads=%d -nographic " + "-nodefaults -serial mon:stdio -S " + "-d guest_errors", + machine, SMT, SMT); + + /* Check the I2C master status registers after POR */ + check_i2cm_por_regs(qts, chip); + + /* Now do a forced "immediate" reset on all engines */ + reset_all(qts, chip); + + /* Check that the status values are still good */ + check_i2cm_por_regs(qts, chip); + + /* P9 doesn't have any i2c devices attached at this time */ + if (chip->chip_type != PNV_CHIP_POWER10) { + qtest_quit(qts); + return; + } + + /* Initialize for a P10 pca9552 hotplug device */ + ctlr.qts = qts; + ctlr.chip = chip; + ctlr.engine = 2; + pca9552.ctlr = &ctlr; + pca9552.port = 1; + pca9552.addr = 0x63; + + /* Set all pca9552 pins as inputs */ + pnv_i2c_pca9552_default_cfg(&pca9552); + + /* Check that all pins of the pca9552 are high */ + pnv_i2c_pca9552_default_inputs(&pca9552); + + /* perform individual pin tests */ + pnv_i2c_pca552_set_pins(&pca9552); + + /* Initialize for a P10 pca9554 CableCard Presence detection device */ + pca9554.ctlr = &ctlr; + pca9554.port = 1; + pca9554.addr = 0x25; + + /* Set all pca9554 pins as inputs */ + pnv_i2c_pca9554_default_cfg(&pca9554); + + /* Check that all pins of the pca9554 are high */ + pnv_i2c_pca9554_default_inputs(&pca9554); + + /* perform individual pin tests */ + pnv_i2c_pca554_set_pins(&pca9554); + + qtest_quit(qts); +} + +static void add_test(const char *name, void (*test)(const void *data)) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pnv_chips); i++) { + char *tname = g_strdup_printf("pnv-xscom/%s/%s", name, + pnv_chips[i].cpu_model); + qtest_add_data_func(tname, &pnv_chips[i], test); + g_free(tname); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + add_test("host-i2c", test_host_i2c); + return g_test_run(); +} diff --git a/tests/qtest/pnv-xscom-test.c b/tests/qtest/pnv-xscom-test.c index 2c46d5cf6d..c814c0f4f5 100644 --- a/tests/qtest/pnv-xscom-test.c +++ b/tests/qtest/pnv-xscom-test.c @@ -10,56 +10,7 @@ #include "libqtest.h" -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 { - PnvChipType chip_type; - const char *cpu_model; - uint64_t xscom_base; - uint64_t cfam_id; - uint32_t first_core; -} PnvChip; - -static const PnvChip pnv_chips[] = { - { - .chip_type = PNV_CHIP_POWER8, - .cpu_model = "POWER8", - .xscom_base = 0x0003fc0000000000ull, - .cfam_id = 0x220ea04980000000ull, - .first_core = 0x1, - }, { - .chip_type = PNV_CHIP_POWER8NVL, - .cpu_model = "POWER8NVL", - .xscom_base = 0x0003fc0000000000ull, - .cfam_id = 0x120d304980000000ull, - .first_core = 0x1, - }, - { - .chip_type = PNV_CHIP_POWER9, - .cpu_model = "POWER9", - .xscom_base = 0x000603fc00000000ull, - .cfam_id = 0x220d104900008000ull, - .first_core = 0x0, - }, -}; - -static uint64_t pnv_xscom_addr(const PnvChip *chip, uint32_t pcba) -{ - uint64_t addr = chip->xscom_base; - - if (chip->chip_type == PNV_CHIP_POWER9) { - addr |= ((uint64_t) pcba << 3); - } else { - addr |= (((uint64_t) pcba << 4) & ~0xffull) | - (((uint64_t) pcba << 3) & 0x78); - } - return addr; -} +#include "pnv-xscom.h" static uint64_t pnv_xscom_read(QTestState *qts, const PnvChip *chip, uint32_t pcba) @@ -82,6 +33,8 @@ static void test_cfam_id(const void *data) if (chip->chip_type == PNV_CHIP_POWER9) { machine = "powernv9"; + } else if (chip->chip_type == PNV_CHIP_POWER10) { + machine = "powernv10"; } qts = qtest_initf("-M %s -accel tcg -cpu %s", @@ -96,23 +49,36 @@ static void test_cfam_id(const void *data) (PNV_XSCOM_EX_CORE_BASE | ((uint64_t)(core) << 24)) #define PNV_XSCOM_P9_EC_BASE(core) \ ((uint64_t)(((core) & 0x1F) + 0x20) << 24) +#define PNV_XSCOM_P10_EC_BASE(core) \ + ((uint64_t)((((core) & ~0x3) + 0x20) << 24) + 0x20000 + \ + (0x1000 << (3 - (core & 0x3)))) #define PNV_XSCOM_EX_DTS_RESULT0 0x50000 static void test_xscom_core(QTestState *qts, const PnvChip *chip) { - uint32_t first_core_dts0 = PNV_XSCOM_EX_DTS_RESULT0; - uint64_t dts0; + if (chip->chip_type == PNV_CHIP_POWER10) { + uint32_t first_core_thread_state = + PNV_XSCOM_P10_EC_BASE(chip->first_core) + 0x412; + uint64_t thread_state; - if (chip->chip_type != PNV_CHIP_POWER9) { - first_core_dts0 |= PNV_XSCOM_EX_BASE(chip->first_core); + thread_state = pnv_xscom_read(qts, chip, first_core_thread_state); + + g_assert_cmphex(thread_state, ==, 0); } else { - first_core_dts0 |= PNV_XSCOM_P9_EC_BASE(chip->first_core); + uint32_t first_core_dts0 = PNV_XSCOM_EX_DTS_RESULT0; + uint64_t dts0; + + if (chip->chip_type == PNV_CHIP_POWER9) { + first_core_dts0 |= PNV_XSCOM_P9_EC_BASE(chip->first_core); + } else { /* POWER8 */ + first_core_dts0 |= PNV_XSCOM_EX_BASE(chip->first_core); + } + + dts0 = pnv_xscom_read(qts, chip, first_core_dts0); + + g_assert_cmphex(dts0, ==, 0x26f024f023f0000ull); } - - dts0 = pnv_xscom_read(qts, chip, first_core_dts0); - - g_assert_cmphex(dts0, ==, 0x26f024f023f0000ull); } static void test_core(const void *data) @@ -123,6 +89,8 @@ static void test_core(const void *data) if (chip->chip_type == PNV_CHIP_POWER9) { machine = "powernv9"; + } else if (chip->chip_type == PNV_CHIP_POWER10) { + machine = "powernv10"; } qts = qtest_initf("-M %s -accel tcg -cpu %s", diff --git a/tests/qtest/pnv-xscom.h b/tests/qtest/pnv-xscom.h new file mode 100644 index 0000000000..6f62941744 --- /dev/null +++ b/tests/qtest/pnv-xscom.h @@ -0,0 +1,80 @@ +/* + * PowerNV XSCOM Bus + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PNV_XSCOM_H +#define PNV_XSCOM_H + +#define SMT 4 /* some tests will break if less than 4 */ + +typedef enum PnvChipType { + PNV_CHIP_POWER8E, /* AKA Murano (default) */ + PNV_CHIP_POWER8, /* AKA Venice */ + PNV_CHIP_POWER8NVL, /* AKA Naples */ + PNV_CHIP_POWER9, /* AKA Nimbus */ + PNV_CHIP_POWER10, +} PnvChipType; + +typedef struct PnvChip { + PnvChipType chip_type; + const char *cpu_model; + uint64_t xscom_base; + uint64_t cfam_id; + uint32_t first_core; + uint32_t num_i2c; +} PnvChip; + +static const PnvChip pnv_chips[] = { + { + .chip_type = PNV_CHIP_POWER8, + .cpu_model = "POWER8", + .xscom_base = 0x0003fc0000000000ull, + .cfam_id = 0x220ea04980000000ull, + .first_core = 0x1, + .num_i2c = 0, + }, { + .chip_type = PNV_CHIP_POWER8NVL, + .cpu_model = "POWER8NVL", + .xscom_base = 0x0003fc0000000000ull, + .cfam_id = 0x120d304980000000ull, + .first_core = 0x1, + .num_i2c = 0, + }, + { + .chip_type = PNV_CHIP_POWER9, + .cpu_model = "POWER9", + .xscom_base = 0x000603fc00000000ull, + .cfam_id = 0x220d104900008000ull, + .first_core = 0x0, + .num_i2c = 4, + }, + { + .chip_type = PNV_CHIP_POWER10, + .cpu_model = "POWER10", + .xscom_base = 0x000603fc00000000ull, + .cfam_id = 0x120da04900008000ull, + .first_core = 0x0, + .num_i2c = 4, + }, +}; + +static inline uint64_t pnv_xscom_addr(const PnvChip *chip, uint32_t pcba) +{ + uint64_t addr = chip->xscom_base; + + if (chip->chip_type == PNV_CHIP_POWER10) { + addr |= ((uint64_t) pcba << 3); + } else if (chip->chip_type == PNV_CHIP_POWER9) { + addr |= ((uint64_t) pcba << 3); + } else { + addr |= (((uint64_t) pcba << 4) & ~0xffull) | + (((uint64_t) pcba << 3) & 0x78); + } + return addr; +} + +#endif /* PNV_XSCOM_H */ diff --git a/tests/qtest/prom-env-test.c b/tests/qtest/prom-env-test.c index c2b0448e55..39ccb59797 100644 --- a/tests/qtest/prom-env-test.c +++ b/tests/qtest/prom-env-test.c @@ -58,8 +58,8 @@ static void test_machine(const void *machine) " -machine " PSERIES_DEFAULT_CAPABILITIES; } - qts = qtest_initf("-M %s -accel tcg %s -prom-env 'use-nvramrc?=true' " - "-prom-env 'nvramrc=%x %x l!' ", (const char *)machine, + 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); qtest_quit(qts); diff --git a/tests/qtest/pvpanic-pci-test.c b/tests/qtest/pvpanic-pci-test.c index c82c365c26..2c05b376ba 100644 --- a/tests/qtest/pvpanic-pci-test.c +++ b/tests/qtest/pvpanic-pci-test.c @@ -86,13 +86,9 @@ static void test_panic(void) int main(int argc, char **argv) { - int ret; - g_test_init(&argc, &argv, NULL); qtest_add_func("/pvpanic-pci/panic", test_panic); qtest_add_func("/pvpanic-pci/panic-nopause", test_panic_nopause); - ret = g_test_run(); - - return ret; + return g_test_run(); } diff --git a/tests/qtest/pvpanic-test.c b/tests/qtest/pvpanic-test.c index bc7b7dfc39..78f1cf8186 100644 --- a/tests/qtest/pvpanic-test.c +++ b/tests/qtest/pvpanic-test.c @@ -59,13 +59,9 @@ static void test_panic(void) int main(int argc, char **argv) { - int ret; - g_test_init(&argc, &argv, NULL); qtest_add_func("/pvpanic/panic", test_panic); qtest_add_func("/pvpanic/panic-nopause", test_panic_nopause); - ret = g_test_run(); - - return ret; + return g_test_run(); } diff --git a/tests/qtest/pxe-test.c b/tests/qtest/pxe-test.c index 52f0b5c67c..e4b48225a5 100644 --- a/tests/qtest/pxe-test.c +++ b/tests/qtest/pxe-test.c @@ -108,6 +108,10 @@ static void test_batch(const testdef_t *tests, bool ipv6) const testdef_t *test = &tests[i]; char *testname; + if (!qtest_has_device(test->model)) { + continue; + } + testname = g_strdup_printf("pxe/ipv4/%s/%s", test->machine, test->model); qtest_add_data_func(testname, test, test_pxe_ipv4); @@ -127,11 +131,17 @@ int main(int argc, char *argv[]) int ret; const char *arch = qtest_get_arch(); + g_test_init(&argc, &argv, NULL); + + if (!qtest_has_accel("tcg") && !qtest_has_accel("kvm")) { + g_test_skip("No KVM or TCG accelerator available"); + return 0; + } + ret = boot_sector_init(disk); if(ret) return ret; - g_test_init(&argc, &argv, NULL); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { test_batch(x86_tests, false); diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 056b40e67f..2c15f60958 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -45,15 +45,14 @@ static int query_error_class(const char *cmd) { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR }, { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, + { "query-hv-balloon-status-report", ERROR_CLASS_GENERIC_ERROR }, { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, -#ifndef CONFIG_PROFILER - { "x-query-profile", ERROR_CLASS_GENERIC_ERROR }, -#endif /* Only valid with a USB bus added */ { "x-query-usb", ERROR_CLASS_GENERIC_ERROR }, /* Only valid with accel=tcg */ { "x-query-jit", ERROR_CLASS_GENERIC_ERROR }, { "x-query-opcount", ERROR_CLASS_GENERIC_ERROR }, + { "xen-event-list", ERROR_CLASS_GENERIC_ERROR }, { NULL, -1 } }; int i; @@ -103,6 +102,7 @@ static bool query_is_ignored(const char *cmd) "query-gic-capabilities", /* arm */ /* Success depends on target-specific build configuration: */ "query-pci", /* CONFIG_PCI */ + "x-query-virtio", /* CONFIG_VIRTIO */ /* Success depends on launching SEV guest */ "query-sev-launch-measure", /* Success depends on Host or Hypervisor SEV support */ @@ -110,6 +110,8 @@ static bool query_is_ignored(const char *cmd) "query-sev-capabilities", "query-sgx", "query-sgx-capabilities", + /* Success depends on enabling dirty page rate limit */ + "query-vcpu-dirty-limit", NULL }; int i; @@ -171,7 +173,7 @@ static bool object_type_has_mandatory_members(SchemaInfo *type) g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT); for (tail = type->u.object.members; tail; tail = tail->next) { - if (!tail->value->has_q_default) { + if (!tail->value->q_default) { return true; } } diff --git a/tests/qtest/qmp-test.c b/tests/qtest/qmp-test.c index 9a42480cef..22957fa49c 100644 --- a/tests/qtest/qmp-test.c +++ b/tests/qtest/qmp-test.c @@ -159,16 +159,19 @@ static void test_qmp_protocol(void) qtest_quit(qts); } +#ifndef _WIN32 + /* Out-of-band tests */ -char tmpdir[] = "/tmp/qmp-test-XXXXXX"; +char *tmpdir; char *fifo_name; static void setup_blocking_cmd(void) { - if (!mkdtemp(tmpdir)) { - g_error("mkdtemp: %s", strerror(errno)); - } + GError *err = NULL; + tmpdir = g_dir_make_tmp("qmp-test-XXXXXX", &err); + g_assert_no_error(err); + fifo_name = g_strdup_printf("%s/fifo", tmpdir); if (mkfifo(fifo_name, 0666)) { g_error("mkfifo: %s", strerror(errno)); @@ -179,6 +182,7 @@ static void cleanup_blocking_cmd(void) { unlink(fifo_name); rmdir(tmpdir); + g_free(tmpdir); } static void send_cmd_that_blocks(QTestState *s, const char *id) @@ -277,6 +281,8 @@ static void test_qmp_oob(void) qtest_quit(qts); } +#endif /* _WIN32 */ + /* Preconfig tests */ static void test_qmp_preconfig(void) @@ -336,7 +342,10 @@ int main(int argc, char *argv[]) g_test_init(&argc, &argv, NULL); qtest_add_func("qmp/protocol", test_qmp_protocol); +#ifndef _WIN32 + /* This case calls mkfifo() which does not exist on win32 */ qtest_add_func("qmp/oob", test_qmp_oob); +#endif qtest_add_func("qmp/preconfig", test_qmp_preconfig); qtest_add_func("qmp/missing-any-arg", test_qmp_missing_any_arg); diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index 7b871b2a31..d677f87c8e 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -14,14 +14,19 @@ #include "qemu/cutils.h" #include "libqtest.h" +static int verbosity_level; + static void test_properties(QTestState *qts, const char *path, bool recurse) { char *child_path; QDict *response, *tuple, *tmp; QList *list; QListEntry *entry; + GSList *children = NULL, *links = NULL; - g_test_message("Obtaining properties of %s", path); + if (verbosity_level >= 2) { + g_test_message("Obtaining properties of %s", path); + } response = qtest_qmp(qts, "{ 'execute': 'qom-list'," " 'arguments': { 'path': %s } }", path); g_assert(response); @@ -41,11 +46,16 @@ static void test_properties(QTestState *qts, const char *path, bool recurse) if (is_child || is_link) { child_path = g_strdup_printf("%s/%s", path, qdict_get_str(tuple, "name")); - test_properties(qts, child_path, is_child); - g_free(child_path); + if (is_child) { + children = g_slist_prepend(children, child_path); + } else { + links = g_slist_prepend(links, child_path); + } } else { const char *prop = qdict_get_str(tuple, "name"); - g_test_message("Testing property %s.%s", path, prop); + if (verbosity_level >= 3) { + g_test_message("-> %s", prop); + } tmp = qtest_qmp(qts, "{ 'execute': 'qom-get'," " 'arguments': { 'path': %s, 'property': %s } }", @@ -55,6 +65,18 @@ static void test_properties(QTestState *qts, const char *path, bool recurse) qobject_unref(tmp); } } + + while (links) { + test_properties(qts, links->data, false); + g_free(links->data); + links = g_slist_delete_link(links, links); + } + while (children) { + test_properties(qts, children->data, true); + g_free(children->data); + children = g_slist_delete_link(children, children); + } + qobject_unref(response); } @@ -87,6 +109,12 @@ static void add_machine_test_case(const char *mname) int main(int argc, char **argv) { + char *v_env = getenv("V"); + + if (v_env) { + verbosity_level = atoi(v_env); + } + g_test_init(&argc, &argv, NULL); qtest_cb_for_every_machine(add_machine_test_case, g_test_quick()); diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c index f97d0a08fd..5da4091ec3 100644 --- a/tests/qtest/qos-test.c +++ b/tests/qtest/qos-test.c @@ -25,7 +25,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-machine.h" #include "qapi/qapi-visit-qom.h" -#include "libqos/malloc.h" +#include "libqos/libqos-malloc.h" #include "libqos/qgraph.h" #include "libqos/qgraph_internal.h" #include "libqos/qos_external.h" @@ -185,7 +185,9 @@ static void run_one_test(const void *arg) static void subprocess_run_one_test(const void *arg) { const gchar *path = arg; - g_test_trap_subprocess(path, 0, 0); + g_test_trap_subprocess(path, 180 * G_USEC_PER_SEC, + G_TEST_SUBPROCESS_INHERIT_STDOUT | + G_TEST_SUBPROCESS_INHERIT_STDERR); g_test_trap_assert_passed(); } @@ -319,6 +321,11 @@ static void walk_path(QOSGraphNode *orig_path, int len) int main(int argc, char **argv, char** envp) { g_test_init(&argc, &argv, NULL); + + if (g_test_subprocess()) { + qos_printf("qos_test running single test in subprocess\n"); + } + if (g_test_verbose()) { qos_printf("ENVIRONMENT VARIABLES: {\n"); for (char **env = envp; *env != 0; env++) { diff --git a/tests/qtest/qtest_aspeed.c b/tests/qtest/qtest_aspeed.c new file mode 100644 index 0000000000..f6da9adea9 --- /dev/null +++ b/tests/qtest/qtest_aspeed.c @@ -0,0 +1,117 @@ +/* + * Aspeed i2c bus interface for reading from and writing to i2c device registers + * + * Copyright (c) 2023 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. + */ + +#include "qemu/osdep.h" + +#include "qtest_aspeed.h" +#include "hw/i2c/aspeed_i2c.h" + +static void aspeed_i2c_startup(QTestState *s, uint32_t baseaddr, + uint8_t slave_addr, uint8_t reg) +{ + uint32_t v; + static int once; + + if (!once) { + /* one time: enable master */ + qtest_writel(s, baseaddr + A_I2CC_FUN_CTRL, 0); + v = qtest_readl(s, baseaddr + A_I2CC_FUN_CTRL) | A_I2CD_MASTER_EN; + qtest_writel(s, baseaddr + A_I2CC_FUN_CTRL, v); + once = 1; + } + + /* select device */ + qtest_writel(s, baseaddr + A_I2CD_BYTE_BUF, slave_addr << 1); + qtest_writel(s, baseaddr + A_I2CD_CMD, + A_I2CD_M_START_CMD | A_I2CD_M_RX_CMD); + + /* select the register to write to */ + qtest_writel(s, baseaddr + A_I2CD_BYTE_BUF, reg); + qtest_writel(s, baseaddr + A_I2CD_CMD, A_I2CD_M_TX_CMD); +} + +static uint32_t aspeed_i2c_read_n(QTestState *s, + uint32_t baseaddr, uint8_t slave_addr, + uint8_t reg, size_t nbytes) +{ + uint32_t res = 0; + uint32_t v; + size_t i; + + aspeed_i2c_startup(s, baseaddr, slave_addr, reg); + + for (i = 0; i < nbytes; i++) { + qtest_writel(s, baseaddr + A_I2CD_CMD, A_I2CD_M_RX_CMD); + v = qtest_readl(s, baseaddr + A_I2CD_BYTE_BUF) >> 8; + res |= (v & 0xff) << (i * 8); + } + + qtest_writel(s, baseaddr + A_I2CD_CMD, A_I2CD_M_STOP_CMD); + + return res; +} + +uint32_t aspeed_i2c_readl(QTestState *s, + uint32_t baseaddr, uint8_t slave_addr, uint8_t reg) +{ + return aspeed_i2c_read_n(s, baseaddr, slave_addr, reg, sizeof(uint32_t)); +} + +uint16_t aspeed_i2c_readw(QTestState *s, + uint32_t baseaddr, uint8_t slave_addr, uint8_t reg) +{ + return aspeed_i2c_read_n(s, baseaddr, slave_addr, reg, sizeof(uint16_t)); +} + +uint8_t aspeed_i2c_readb(QTestState *s, + uint32_t baseaddr, uint8_t slave_addr, uint8_t reg) +{ + return aspeed_i2c_read_n(s, baseaddr, slave_addr, reg, sizeof(uint8_t)); +} + +static void aspeed_i2c_write_n(QTestState *s, + uint32_t baseaddr, uint8_t slave_addr, + uint8_t reg, uint32_t v, size_t nbytes) +{ + size_t i; + + aspeed_i2c_startup(s, baseaddr, slave_addr, reg); + + for (i = 0; i < nbytes; i++) { + qtest_writel(s, baseaddr + A_I2CD_BYTE_BUF, v & 0xff); + v >>= 8; + qtest_writel(s, baseaddr + A_I2CD_CMD, A_I2CD_M_TX_CMD); + } + + qtest_writel(s, baseaddr + A_I2CD_CMD, A_I2CD_M_STOP_CMD); +} + +void aspeed_i2c_writel(QTestState *s, + uint32_t baseaddr, uint8_t slave_addr, + uint8_t reg, uint32_t v) +{ + aspeed_i2c_write_n(s, baseaddr, slave_addr, reg, v, sizeof(v)); +} + +void aspeed_i2c_writew(QTestState *s, + uint32_t baseaddr, uint8_t slave_addr, + uint8_t reg, uint16_t v) +{ + aspeed_i2c_write_n(s, baseaddr, slave_addr, reg, v, sizeof(v)); +} + +void aspeed_i2c_writeb(QTestState *s, + uint32_t baseaddr, uint8_t slave_addr, + uint8_t reg, uint8_t v) +{ + aspeed_i2c_write_n(s, baseaddr, slave_addr, reg, v, sizeof(v)); +} diff --git a/tests/qtest/qtest_aspeed.h b/tests/qtest/qtest_aspeed.h new file mode 100644 index 0000000000..d35b0c7cba --- /dev/null +++ b/tests/qtest/qtest_aspeed.h @@ -0,0 +1,39 @@ +/* + * Aspeed i2c bus interface to reading and writing to i2c device registers + * + * Copyright (c) 2023 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 QTEST_ASPEED_H +#define QTEST_ASPEED_H + +#include "libqtest.h" + +#define AST2600_ASPEED_I2C_BASE_ADDR 0x1e78a000 + +/* Implements only AST2600 I2C controller */ + +static inline uint32_t ast2600_i2c_calc_bus_addr(uint8_t bus_num) +{ + return AST2600_ASPEED_I2C_BASE_ADDR + 0x80 + bus_num * 0x80; +} + +uint8_t aspeed_i2c_readb(QTestState *s, + uint32_t baseaddr, uint8_t slave_addr, uint8_t reg); +uint16_t aspeed_i2c_readw(QTestState *s, + uint32_t baseaddr, uint8_t slave_addr, uint8_t reg); +uint32_t aspeed_i2c_readl(QTestState *s, + uint32_t baseaddr, uint8_t slave_addr, uint8_t reg); +void aspeed_i2c_writeb(QTestState *s, uint32_t baseaddr, uint8_t slave_addr, + uint8_t reg, uint8_t v); +void aspeed_i2c_writew(QTestState *s, uint32_t baseaddr, uint8_t slave_addr, + uint8_t reg, uint16_t v); +void aspeed_i2c_writel(QTestState *s, uint32_t baseaddr, uint8_t slave_addr, + uint8_t reg, uint32_t v); + +#endif diff --git a/tests/qtest/readconfig-test.c b/tests/qtest/readconfig-test.c new file mode 100644 index 0000000000..760f974e63 --- /dev/null +++ b/tests/qtest/readconfig-test.c @@ -0,0 +1,418 @@ +/* + * Validate -readconfig + * + * Copyright (c) 2022 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 "libqtest.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-machine.h" +#include "qapi/qapi-visit-qom.h" +#include "qapi/qapi-visit-ui.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qmp/qstring.h" +#include "qemu/units.h" + +static QTestState *qtest_init_with_config(const char *cfgdata) +{ + GError *error = NULL; + g_autofree char *args = NULL; + int cfgfd = -1; + g_autofree char *cfgpath = NULL; + QTestState *qts; + ssize_t ret; + + cfgfd = g_file_open_tmp("readconfig-test-XXXXXX", &cfgpath, &error); + g_assert_no_error(error); + g_assert_cmpint(cfgfd, >=, 0); + + ret = qemu_write_full(cfgfd, cfgdata, strlen(cfgdata)); + close(cfgfd); + if (ret < 0) { + unlink(cfgpath); + } + g_assert_cmpint(ret, ==, strlen(cfgdata)); + + args = g_strdup_printf("-nodefaults -machine none -readconfig %s", cfgpath); + + qts = qtest_init(args); + + unlink(cfgpath); + + return qts; +} + +static void test_x86_memdev_resp(QObject *res, const char *mem_id, int size) +{ + Visitor *v; + g_autoptr(MemdevList) memdevs = NULL; + Memdev *memdev; + + g_assert(res); + v = qobject_input_visitor_new(res); + visit_type_MemdevList(v, NULL, &memdevs, &error_abort); + + g_assert(memdevs); + g_assert(memdevs->value); + g_assert(!memdevs->next); + + memdev = memdevs->value; + g_assert_cmpstr(memdev->id, ==, mem_id); + g_assert_cmpint(memdev->size, ==, size * MiB); + + visit_free(v); +} + +static void test_x86_memdev(void) +{ + QDict *resp; + QTestState *qts; + const char *cfgdata = + "[memory]\n" + "size = \"200\""; + + qts = qtest_init_with_config(cfgdata); + /* Test valid command */ + resp = qtest_qmp(qts, "{ 'execute': 'query-memdev' }"); + test_x86_memdev_resp(qdict_get(resp, "return"), "ram", 200); + qobject_unref(resp); + + qtest_quit(qts); +} + +/* FIXME: The test is currently broken on FreeBSD */ +#if defined(CONFIG_SPICE) && !defined(__FreeBSD__) +static void test_spice_resp(QObject *res) +{ + Visitor *v; + g_autoptr(SpiceInfo) spice = NULL; + + g_assert(res); + v = qobject_input_visitor_new(res); + visit_type_SpiceInfo(v, "spice", &spice, &error_abort); + + g_assert(spice); + g_assert(spice->enabled); + + visit_free(v); +} + +static void test_spice(void) +{ + QDict *resp; + QTestState *qts; + const char *cfgdata = + "[spice]\n" +#ifndef WIN32 + "unix = \"on\"\n" +#endif + "disable-ticketing = \"on\"\n"; + + qts = qtest_init_with_config(cfgdata); + /* Test valid command */ + resp = qtest_qmp(qts, "{ 'execute': 'query-spice' }"); + test_spice_resp(qdict_get(resp, "return")); + qobject_unref(resp); + + qtest_quit(qts); +} +#endif + +static void test_object_available(QObject *res, const char *name, + const char *type) +{ + Visitor *v; + g_autoptr(ObjectPropertyInfoList) objs = NULL; + ObjectPropertyInfoList *tmp; + ObjectPropertyInfo *obj; + bool object_available = false; + g_autofree char *childtype = g_strdup_printf("child<%s>", type); + + g_assert(res); + v = qobject_input_visitor_new(res); + visit_type_ObjectPropertyInfoList(v, NULL, &objs, &error_abort); + + g_assert(objs); + tmp = objs; + while (tmp) { + g_assert(tmp->value); + + obj = tmp->value; + if (g_str_equal(obj->name, name) && g_str_equal(obj->type, childtype)) { + object_available = true; + break; + } + + tmp = tmp->next; + } + + g_assert(object_available); + + visit_free(v); +} + +static void test_object_rng(void) +{ + QDict *resp; + QTestState *qts; + const char *cfgdata = + "[object]\n" + "qom-type = \"rng-builtin\"\n" + "id = \"rng0\"\n"; + + qts = qtest_init_with_config(cfgdata); + /* Test valid command */ + resp = qtest_qmp(qts, + "{ 'execute': 'qom-list'," + " 'arguments': {'path': '/objects' }}"); + test_object_available(qdict_get(resp, "return"), "rng0", "rng-builtin"); + qobject_unref(resp); + + qtest_quit(qts); +} + +static void test_docs_config_ich9(void) +{ + QTestState *qts; + QDict *resp; + QObject *qobj; + + qts = qtest_initf("-nodefaults -readconfig docs/config/ich9-ehci-uhci.cfg"); + + resp = qtest_qmp(qts, "{ 'execute': 'qom-list'," + " 'arguments': {'path': '/machine/peripheral' }}"); + qobj = qdict_get(resp, "return"); + test_object_available(qobj, "ehci", "ich9-usb-ehci1"); + test_object_available(qobj, "uhci-1", "ich9-usb-uhci1"); + test_object_available(qobj, "uhci-2", "ich9-usb-uhci2"); + test_object_available(qobj, "uhci-3", "ich9-usb-uhci3"); + qobject_unref(resp); + + qtest_quit(qts); +} + +#if defined(CONFIG_POSIX) && defined(CONFIG_SLIRP) + +static char *make_temp_img(const char *template, const char *format, int size) +{ + GError *error = NULL; + char *temp_name; + int fd; + + /* Create a temporary image names */ + fd = g_file_open_tmp(template, &temp_name, &error); + if (fd == -1) { + fprintf(stderr, "unable to create file: %s\n", error->message); + g_error_free(error); + return NULL; + } + close(fd); + + if (!mkimg(temp_name, format, size)) { + fprintf(stderr, "qemu-img failed to create %s\n", temp_name); + g_free(temp_name); + return NULL; + } + + return temp_name; +} + +struct device { + const char *name; + const char *type; +}; + +static void test_docs_q35(const char *input_file, struct device *devices) +{ + QTestState *qts; + QDict *resp; + QObject *qobj; + int ret, i; + g_autofree char *cfg_file = NULL, *sedcmd = NULL; + g_autofree char *hd_file = NULL, *cd_file = NULL; + + /* Check that all the devices are available in the QEMU binary */ + for (i = 0; devices[i].name; i++) { + if (!qtest_has_device(devices[i].type)) { + g_test_skip("one of the required devices is not available"); + return; + } + } + + hd_file = make_temp_img("qtest_disk_XXXXXX.qcow2", "qcow2", 1); + cd_file = make_temp_img("qtest_cdrom_XXXXXX.iso", "raw", 1); + if (!hd_file || !cd_file) { + g_test_skip("could not create disk images"); + goto cleanup; + } + + /* Create a temporary config file where we replace the disk image names */ + ret = g_file_open_tmp("q35-emulated-XXXXXX.cfg", &cfg_file, NULL); + if (ret == -1) { + g_test_skip("could not create temporary config file"); + goto cleanup; + } + close(ret); + + sedcmd = g_strdup_printf("sed -e 's,guest.qcow2,%s,' -e 's,install.iso,%s,'" + " %s %s > '%s'", + hd_file, cd_file, + !qtest_has_accel("kvm") ? "-e '/accel/d'" : "", + input_file, cfg_file); + ret = system(sedcmd); + if (ret) { + g_test_skip("could not modify temporary config file"); + goto cleanup; + } + + qts = qtest_initf("-machine none -nodefaults -readconfig %s", cfg_file); + + /* Check memory size */ + resp = qtest_qmp(qts, "{ 'execute': 'query-memdev' }"); + test_x86_memdev_resp(qdict_get(resp, "return"), "pc.ram", 1024); + qobject_unref(resp); + + resp = qtest_qmp(qts, "{ 'execute': 'qom-list'," + " 'arguments': {'path': '/machine/peripheral' }}"); + qobj = qdict_get(resp, "return"); + + /* Check that all the devices have been created */ + for (i = 0; devices[i].name; i++) { + test_object_available(qobj, devices[i].name, devices[i].type); + } + + qobject_unref(resp); + + qtest_quit(qts); + +cleanup: + if (hd_file) { + unlink(hd_file); + } + if (cd_file) { + unlink(cd_file); + } + if (cfg_file) { + unlink(cfg_file); + } +} + +static void test_docs_q35_emulated(void) +{ + struct device devices[] = { + { "ich9-pcie-port-1", "ioh3420" }, + { "ich9-pcie-port-2", "ioh3420" }, + { "ich9-pcie-port-3", "ioh3420" }, + { "ich9-pcie-port-4", "ioh3420" }, + { "ich9-pci-bridge", "i82801b11-bridge" }, + { "ich9-ehci-1", "ich9-usb-ehci1" }, + { "ich9-ehci-2", "ich9-usb-ehci2" }, + { "ich9-uhci-1", "ich9-usb-uhci1" }, + { "ich9-uhci-2", "ich9-usb-uhci2" }, + { "ich9-uhci-3", "ich9-usb-uhci3" }, + { "ich9-uhci-4", "ich9-usb-uhci4" }, + { "ich9-uhci-5", "ich9-usb-uhci5" }, + { "ich9-uhci-6", "ich9-usb-uhci6" }, + { "sata-disk", "ide-hd" }, + { "sata-optical-disk", "ide-cd" }, + { "net", "e1000" }, + { "video", "VGA" }, + { "ich9-hda-audio", "ich9-intel-hda" }, + { "ich9-hda-duplex", "hda-duplex" }, + { NULL, NULL } + }; + + test_docs_q35("docs/config/q35-emulated.cfg", devices); +} + +static void test_docs_q35_virtio_graphical(void) +{ + struct device devices[] = { + { "pcie.1", "pcie-root-port" }, + { "pcie.2", "pcie-root-port" }, + { "pcie.3", "pcie-root-port" }, + { "pcie.4", "pcie-root-port" }, + { "pcie.5", "pcie-root-port" }, + { "pcie.6", "pcie-root-port" }, + { "pcie.7", "pcie-root-port" }, + { "pcie.8", "pcie-root-port" }, + { "scsi", "virtio-scsi-pci" }, + { "scsi-disk", "scsi-hd" }, + { "scsi-optical-disk", "scsi-cd" }, + { "net", "virtio-net-pci" }, + { "usb", "nec-usb-xhci" }, + { "tablet", "usb-tablet" }, + { "video", "qxl-vga" }, + { "sound", "ich9-intel-hda" }, + { "duplex", "hda-duplex" }, + { NULL, NULL } + }; + + test_docs_q35("docs/config/q35-virtio-graphical.cfg", devices); +} + +static void test_docs_q35_virtio_serial(void) +{ + struct device devices[] = { + { "pcie.1", "pcie-root-port" }, + { "pcie.2", "pcie-root-port" }, + { "pcie.3", "pcie-root-port" }, + { "pcie.4", "pcie-root-port" }, + { "pcie.5", "pcie-root-port" }, + { "pcie.6", "pcie-root-port" }, + { "pcie.7", "pcie-root-port" }, + { "pcie.8", "pcie-root-port" }, + { "scsi", "virtio-scsi-pci" }, + { "scsi-disk", "scsi-hd" }, + { "scsi-optical-disk", "scsi-cd" }, + { "net", "virtio-net-pci" }, + { NULL, NULL } + }; + + test_docs_q35("docs/config/q35-virtio-serial.cfg", devices); +} + +#endif /* CONFIG_LINUX */ + +int main(int argc, char *argv[]) +{ + const char *arch; + g_test_init(&argc, &argv, NULL); + + arch = qtest_get_arch(); + + if (g_str_equal(arch, "i386") || + g_str_equal(arch, "x86_64")) { + qtest_add_func("readconfig/x86/memdev", test_x86_memdev); + if (qtest_has_device("ich9-usb-ehci1") && + qtest_has_device("ich9-usb-uhci1")) { + qtest_add_func("readconfig/x86/ich9-ehci-uhci", test_docs_config_ich9); + } +#if defined(CONFIG_POSIX) && defined(CONFIG_SLIRP) + qtest_add_func("readconfig/x86/q35-emulated", test_docs_q35_emulated); + qtest_add_func("readconfig/x86/q35-virtio-graphical", + test_docs_q35_virtio_graphical); + if (g_test_slow()) { + /* + * q35-virtio-serial.cfg is a subset of q35-virtio-graphical.cfg, + * so we can skip the test in quick mode + */ + qtest_add_func("readconfig/x86/q35-virtio-serial", + test_docs_q35_virtio_serial); + } +#endif + } +#if defined(CONFIG_SPICE) && !defined(__FreeBSD__) + qtest_add_func("readconfig/spice", test_spice); +#endif + + qtest_add_func("readconfig/object-rng", test_object_rng); + + return g_test_run(); +} diff --git a/tests/qtest/rtas-test.c b/tests/qtest/rtas-test.c index 50df60e5b2..1ba42b37d2 100644 --- a/tests/qtest/rtas-test.c +++ b/tests/qtest/rtas-test.c @@ -13,7 +13,7 @@ static void run_test_rtas_get_time_of_day(const char *machine) uint64_t ret; time_t t1, t2; - qs = qtest_spapr_boot(machine); + qs = qtest_spapr_boot("%s", machine); t1 = time(NULL); ret = qrtas_get_time_of_day(qs->qts, &qs->alloc, &tm, &ns); diff --git a/tests/qtest/rtc-test.c b/tests/qtest/rtc-test.c index 8126ab1bdb..02ed4e1238 100644 --- a/tests/qtest/rtc-test.c +++ b/tests/qtest/rtc-test.c @@ -111,7 +111,7 @@ static void cmos_get_date_time(struct tm *date) date->tm_mday = mday; date->tm_mon = mon - 1; date->tm_year = base_year + year - 1900; -#ifndef __sun__ +#if !defined(__sun__) && !defined(_WIN32) date->tm_gmtoff = 0; #endif diff --git a/tests/qtest/rtl8139-test.c b/tests/qtest/rtl8139-test.c index 8fa3313cc3..eedf90f65a 100644 --- a/tests/qtest/rtl8139-test.c +++ b/tests/qtest/rtl8139-test.c @@ -12,6 +12,8 @@ #include "libqos/pci-pc.h" #include "qemu/timer.h" +static int verbosity_level; + /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) { @@ -20,7 +22,7 @@ static void nop(void) #define CLK 33333333 static QPCIBus *pcibus; -static QPCIDevice *dev; +static QPCIDevice *pcidev; static QPCIBar dev_bar; static void save_fn(QPCIDevice *dev, int devfn, void *data) @@ -44,14 +46,18 @@ static QPCIDevice *get_device(void) #define PORT(name, len, val) \ static unsigned __attribute__((unused)) in_##name(void) \ { \ - unsigned res = qpci_io_read##len(dev, dev_bar, (val)); \ - g_test_message("*%s -> %x", #name, res); \ + unsigned res = qpci_io_read##len(pcidev, dev_bar, (val)); \ + if (verbosity_level >= 2) { \ + g_test_message("*%s -> %x", #name, res); \ + } \ return res; \ } \ static void out_##name(unsigned v) \ { \ - g_test_message("%x -> *%s", v, #name); \ - qpci_io_write##len(dev, dev_bar, (val), v); \ + if (verbosity_level >= 2) { \ + g_test_message("%x -> *%s", v, #name); \ + } \ + qpci_io_write##len(pcidev, dev_bar, (val), v); \ } PORT(Timer, l, 0x48) @@ -183,11 +189,11 @@ static void test_init(void) { uint64_t barsize; - dev = get_device(); + pcidev = get_device(); - dev_bar = qpci_iomap(dev, 0, &barsize); + dev_bar = qpci_iomap(pcidev, 0, &barsize); - qpci_device_enable(dev); + qpci_device_enable(pcidev); test_timer(); } @@ -195,10 +201,20 @@ static void test_init(void) int main(int argc, char **argv) { int ret; + char *v_env = getenv("V"); + + if (v_env) { + verbosity_level = atoi(v_env); + } + + g_test_init(&argc, &argv, NULL); + + if (!qtest_has_device("rtl8139")) { + return 0; + } qtest_start("-device rtl8139"); - g_test_init(&argc, &argv, NULL); qtest_add_func("/rtl8139/nop", nop); qtest_add_func("/rtl8139/timer", test_init); diff --git a/tests/qtest/sifive-e-aon-watchdog-test.c b/tests/qtest/sifive-e-aon-watchdog-test.c new file mode 100644 index 0000000000..1f313d16ad --- /dev/null +++ b/tests/qtest/sifive-e-aon-watchdog-test.c @@ -0,0 +1,450 @@ +/* + * QTest testcase for the watchdog timer of HiFive 1 rev b. + * + * Copyright (c) 2023 SiFive, 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/timer.h" +#include "qemu/bitops.h" +#include "libqtest.h" +#include "hw/registerfields.h" +#include "hw/misc/sifive_e_aon.h" + +FIELD(AON_WDT_WDOGCFG, SCALE, 0, 4) +FIELD(AON_WDT_WDOGCFG, RSVD0, 4, 4) +FIELD(AON_WDT_WDOGCFG, RSTEN, 8, 1) +FIELD(AON_WDT_WDOGCFG, ZEROCMP, 9, 1) +FIELD(AON_WDT_WDOGCFG, RSVD1, 10, 2) +FIELD(AON_WDT_WDOGCFG, EN_ALWAYS, 12, 1) +FIELD(AON_WDT_WDOGCFG, EN_CORE_AWAKE, 13, 1) +FIELD(AON_WDT_WDOGCFG, RSVD2, 14, 14) +FIELD(AON_WDT_WDOGCFG, IP0, 28, 1) +FIELD(AON_WDT_WDOGCFG, RSVD3, 29, 3) + +#define WDOG_BASE (0x10000000) +#define WDOGCFG (0x0) +#define WDOGCOUNT (0x8) +#define WDOGS (0x10) +#define WDOGFEED (0x18) +#define WDOGKEY (0x1c) +#define WDOGCMP0 (0x20) + +#define SIFIVE_E_AON_WDOGKEY (0x51F15E) +#define SIFIVE_E_AON_WDOGFEED (0xD09F00D) +#define SIFIVE_E_LFCLK_DEFAULT_FREQ (32768) + +static void test_init(QTestState *qts) +{ + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, 0); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCMP0, 0xBEEF); +} + +static void test_wdogcount(void) +{ + uint64_t tmp; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + tmp = qtest_readl(qts, WDOG_BASE + WDOGCOUNT); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0xBEEF); + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == tmp); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0xBEEF); + g_assert(0xBEEF == qtest_readl(qts, WDOG_BASE + WDOGCOUNT)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0xAAAAAAAA); + g_assert(0x2AAAAAAA == qtest_readl(qts, WDOG_BASE + WDOGCOUNT)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGFEED, 0xAAAAAAAA); + g_assert(0x2AAAAAAA == qtest_readl(qts, WDOG_BASE + WDOGCOUNT)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGFEED, SIFIVE_E_AON_WDOGFEED); + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCOUNT)); + + qtest_quit(qts); +} + +static void test_wdogcfg(void) +{ + uint32_t tmp_cfg; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + tmp_cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + qtest_writel(qts, WDOG_BASE + WDOGCFG, 0xFFFFFFFF); + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCFG) == tmp_cfg); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, 0xFFFFFFFF); + g_assert(0xFFFFFFFF == qtest_readl(qts, WDOG_BASE + WDOGCFG)); + + tmp_cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(15 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, 0); + tmp_cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, IP0)); + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCFG)); + + qtest_quit(qts); +} + +static void test_wdogcmp0(void) +{ + uint32_t tmp; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + tmp = qtest_readl(qts, WDOG_BASE + WDOGCMP0); + qtest_writel(qts, WDOG_BASE + WDOGCMP0, 0xBEEF); + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCMP0) == tmp); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCMP0, 0xBEEF); + g_assert(0xBEEF == qtest_readl(qts, WDOG_BASE + WDOGCMP0)); + + qtest_quit(qts); +} + +static void test_wdogkey(void) +{ + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGKEY)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, 0xFFFF); + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGKEY)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + g_assert(1 == qtest_readl(qts, WDOG_BASE + WDOGKEY)); + + qtest_writel(qts, WDOG_BASE + WDOGFEED, 0xAAAAAAAA); + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGKEY)); + + qtest_quit(qts); +} + +static void test_wdogfeed(void) +{ + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGFEED)); + + qtest_writel(qts, WDOG_BASE + WDOGFEED, 0xFFFF); + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGFEED)); + + qtest_quit(qts); +} + +static void test_scaled_wdogs(void) +{ + uint32_t cfg; + uint32_t fake_count = 0x12345678; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, fake_count); + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == fake_count); + g_assert((uint16_t)qtest_readl(qts, WDOG_BASE + WDOGS) == + (uint16_t)fake_count); + + for (int i = 0; i < 16; i++) { + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, i); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + g_assert((uint16_t)qtest_readl(qts, WDOG_BASE + WDOGS) == + (uint16_t)(fake_count >> + FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE))); + } + + qtest_quit(qts); +} + +static void test_watchdog(void) +{ + uint32_t cfg; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCMP0, SIFIVE_E_LFCLK_DEFAULT_FREQ); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 0); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + + qtest_clock_step(qts, NANOSECONDS_PER_SECOND); + + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == + SIFIVE_E_LFCLK_DEFAULT_FREQ); + g_assert(qtest_readl(qts, WDOG_BASE + WDOGS) == + SIFIVE_E_LFCLK_DEFAULT_FREQ); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_quit(qts); +} + +static void test_scaled_watchdog(void) +{ + uint32_t cfg; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCMP0, 10); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 15); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + + qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 10); + + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == + SIFIVE_E_LFCLK_DEFAULT_FREQ * 10); + + g_assert(10 == qtest_readl(qts, WDOG_BASE + WDOGS)); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_quit(qts); +} + +static void test_periodic_int(void) +{ + uint32_t cfg; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCMP0, SIFIVE_E_LFCLK_DEFAULT_FREQ); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 0); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, ZEROCMP, 1); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + + qtest_clock_step(qts, NANOSECONDS_PER_SECOND); + + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCOUNT)); + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGS)); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_clock_step(qts, NANOSECONDS_PER_SECOND); + + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCOUNT)); + g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGS)); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_quit(qts); +} + +static void test_enable_disable(void) +{ + uint32_t cfg; + QTestState *qts = qtest_init("-machine sifive_e"); + + test_init(qts); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCMP0, 10); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 15); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + + qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 2); + + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == + SIFIVE_E_LFCLK_DEFAULT_FREQ * 2); + g_assert(2 == qtest_readl(qts, WDOG_BASE + WDOGS)); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 0); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + + qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 8); + + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == + SIFIVE_E_LFCLK_DEFAULT_FREQ * 2); + g_assert(2 == qtest_readl(qts, WDOG_BASE + WDOGS)); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + + qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 8); + + g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == + SIFIVE_E_LFCLK_DEFAULT_FREQ * 10); + g_assert(10 == qtest_readl(qts, WDOG_BASE + WDOGS)); + + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS)); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE)); + g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0); + cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0); + qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY); + qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg); + cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG); + g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0)); + + qtest_quit(qts); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_func("/sifive-e-aon-watchdog-test/wdogcount", + test_wdogcount); + qtest_add_func("/sifive-e-aon-watchdog-test/wdogcfg", + test_wdogcfg); + qtest_add_func("/sifive-e-aon-watchdog-test/wdogcmp0", + test_wdogcmp0); + qtest_add_func("/sifive-e-aon-watchdog-test/wdogkey", + test_wdogkey); + qtest_add_func("/sifive-e-aon-watchdog-test/wdogfeed", + test_wdogfeed); + qtest_add_func("/sifive-e-aon-watchdog-test/scaled_wdogs", + test_scaled_wdogs); + qtest_add_func("/sifive-e-aon-watchdog-test/watchdog", + test_watchdog); + qtest_add_func("/sifive-e-aon-watchdog-test/scaled_watchdog", + test_scaled_watchdog); + qtest_add_func("/sifive-e-aon-watchdog-test/periodic_int", + test_periodic_int); + qtest_add_func("/sifive-e-aon-watchdog-test/enable_disable", + test_enable_disable); + return g_test_run(); +} diff --git a/tests/qtest/stm32l4x5_exti-test.c b/tests/qtest/stm32l4x5_exti-test.c new file mode 100644 index 0000000000..81830be8ae --- /dev/null +++ b/tests/qtest/stm32l4x5_exti-test.c @@ -0,0 +1,561 @@ +/* + * QTest testcase for STM32L4x5_EXTI + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * 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-single.h" + +#define EXTI_BASE_ADDR 0x40010400 +#define EXTI_IMR1 0x00 +#define EXTI_EMR1 0x04 +#define EXTI_RTSR1 0x08 +#define EXTI_FTSR1 0x0C +#define EXTI_SWIER1 0x10 +#define EXTI_PR1 0x14 +#define EXTI_IMR2 0x20 +#define EXTI_EMR2 0x24 +#define EXTI_RTSR2 0x28 +#define EXTI_FTSR2 0x2C +#define EXTI_SWIER2 0x30 +#define EXTI_PR2 0x34 + +#define NVIC_ISER 0xE000E100 +#define NVIC_ISPR 0xE000E200 +#define NVIC_ICPR 0xE000E280 + +#define EXTI0_IRQ 6 +#define EXTI1_IRQ 7 +#define EXTI5_9_IRQ 23 +#define EXTI35_IRQ 1 + +static void enable_nvic_irq(unsigned int n) +{ + writel(NVIC_ISER, 1 << n); +} + +static void unpend_nvic_irq(unsigned int n) +{ + writel(NVIC_ICPR, 1 << n); +} + +static bool check_nvic_pending(unsigned int n) +{ + return readl(NVIC_ISPR) & (1 << n); +} + +static void exti_writel(unsigned int offset, uint32_t value) +{ + writel(EXTI_BASE_ADDR + offset, value); +} + +static uint32_t exti_readl(unsigned int offset) +{ + return readl(EXTI_BASE_ADDR + offset); +} + +static void exti_set_irq(int num, int level) +{ + qtest_set_irq_in(global_qtest, "/machine/soc/exti", NULL, + num, level); +} + +static void test_reg_write_read(void) +{ + /* Test that non-reserved bits in xMR and xTSR can be set and cleared */ + + exti_writel(EXTI_IMR1, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_IMR1), ==, 0xFFFFFFFF); + exti_writel(EXTI_IMR1, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_IMR1), ==, 0x00000000); + + exti_writel(EXTI_EMR1, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_EMR1), ==, 0xFFFFFFFF); + exti_writel(EXTI_EMR1, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_EMR1), ==, 0x00000000); + + exti_writel(EXTI_RTSR1, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_RTSR1), ==, 0x007DFFFF); + exti_writel(EXTI_RTSR1, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_RTSR1), ==, 0x00000000); + + exti_writel(EXTI_FTSR1, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_FTSR1), ==, 0x007DFFFF); + exti_writel(EXTI_FTSR1, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_FTSR1), ==, 0x00000000); + + exti_writel(EXTI_IMR2, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_IMR2), ==, 0x000000FF); + exti_writel(EXTI_IMR2, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_IMR2), ==, 0x00000000); + + exti_writel(EXTI_EMR2, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_EMR2), ==, 0x000000FF); + exti_writel(EXTI_EMR2, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_EMR2), ==, 0x00000000); + + exti_writel(EXTI_RTSR2, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_RTSR2), ==, 0x00000078); + exti_writel(EXTI_RTSR2, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_RTSR2), ==, 0x00000000); + + exti_writel(EXTI_FTSR2, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_FTSR2), ==, 0x00000078); + exti_writel(EXTI_FTSR2, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_FTSR2), ==, 0x00000000); +} + +static void test_direct_lines_write(void) +{ + /* Test that direct lines reserved bits are not written to */ + + exti_writel(EXTI_RTSR1, 0xFF820000); + g_assert_cmpuint(exti_readl(EXTI_RTSR1), ==, 0x00000000); + + exti_writel(EXTI_FTSR1, 0xFF820000); + g_assert_cmpuint(exti_readl(EXTI_FTSR1), ==, 0x00000000); + + exti_writel(EXTI_SWIER1, 0xFF820000); + g_assert_cmpuint(exti_readl(EXTI_SWIER1), ==, 0x00000000); + + exti_writel(EXTI_PR1, 0xFF820000); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + + exti_writel(EXTI_RTSR2, 0x00000087); + g_assert_cmpuint(exti_readl(EXTI_RTSR2), ==, 0x00000000); + + exti_writel(EXTI_FTSR2, 0x00000087); + g_assert_cmpuint(exti_readl(EXTI_FTSR2), ==, 0x00000000); + + exti_writel(EXTI_SWIER2, 0x00000087); + g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000000); + + exti_writel(EXTI_PR2, 0x00000087); + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); +} + +static void test_reserved_bits_write(void) +{ + /* Test that reserved bits stay are not written to */ + + exti_writel(EXTI_IMR2, 0xFFFFFF00); + g_assert_cmpuint(exti_readl(EXTI_IMR2), ==, 0x00000000); + + exti_writel(EXTI_EMR2, 0xFFFFFF00); + g_assert_cmpuint(exti_readl(EXTI_EMR2), ==, 0x00000000); + + exti_writel(EXTI_RTSR2, 0xFFFFFF00); + g_assert_cmpuint(exti_readl(EXTI_RTSR2), ==, 0x00000000); + + exti_writel(EXTI_FTSR2, 0xFFFFFF00); + g_assert_cmpuint(exti_readl(EXTI_FTSR2), ==, 0x00000000); + + exti_writel(EXTI_SWIER2, 0xFFFFFF00); + g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000000); + + exti_writel(EXTI_PR2, 0xFFFFFF00); + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); +} + +static void test_software_interrupt(void) +{ + /* + * Test that we can launch a software irq by : + * - enabling its line in IMR + * - and then setting a bit from '0' to '1' in SWIER + * + * And that the interruption stays pending in NVIC + * even after clearing the pending bit in PR. + */ + + /* + * Testing interrupt line EXTI0 + * Bit 0 in EXTI_*1 registers (EXTI0) corresponds to GPIO Px_0 + */ + + enable_nvic_irq(EXTI0_IRQ); + /* Check that there are no interrupts already pending in PR */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that this specific interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* Enable interrupt line EXTI0 */ + exti_writel(EXTI_IMR1, 0x00000001); + /* Set the right SWIER bit from '0' to '1' */ + exti_writel(EXTI_SWIER1, 0x00000000); + exti_writel(EXTI_SWIER1, 0x00000001); + + /* Check that the write in SWIER was effective */ + g_assert_cmpuint(exti_readl(EXTI_SWIER1), ==, 0x00000001); + /* Check that the corresponding pending bit in PR is set */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + /* Check that the corresponding interrupt is pending in the NVIC */ + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* Clear the pending bit in PR */ + exti_writel(EXTI_PR1, 0x00000001); + + /* Check that the write in PR was effective */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that the corresponding bit in SWIER was cleared */ + g_assert_cmpuint(exti_readl(EXTI_SWIER1), ==, 0x00000000); + /* Check that the interrupt is still pending in the NVIC */ + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* + * Testing interrupt line EXTI35 + * Bit 3 in EXTI_*2 registers (EXTI35) corresponds to PVM 1 Wakeup + */ + + enable_nvic_irq(EXTI35_IRQ); + /* Check that there are no interrupts already pending */ + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + g_assert_false(check_nvic_pending(EXTI35_IRQ)); + + /* Enable interrupt line EXTI0 */ + exti_writel(EXTI_IMR2, 0x00000008); + /* Set the right SWIER bit from '0' to '1' */ + exti_writel(EXTI_SWIER2, 0x00000000); + exti_writel(EXTI_SWIER2, 0x00000008); + + /* Check that the write in SWIER was effective */ + g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000008); + /* Check that the corresponding pending bit in PR is set */ + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000008); + /* Check that the corresponding interrupt is pending in the NVIC */ + g_assert_true(check_nvic_pending(EXTI35_IRQ)); + + /* Clear the pending bit in PR */ + exti_writel(EXTI_PR2, 0x00000008); + + /* Check that the write in PR was effective */ + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + /* Check that the corresponding bit in SWIER was cleared */ + g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000000); + /* Check that the interrupt is still pending in the NVIC */ + g_assert_true(check_nvic_pending(EXTI35_IRQ)); + + /* Clean NVIC */ + unpend_nvic_irq(EXTI0_IRQ); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + unpend_nvic_irq(EXTI35_IRQ); + g_assert_false(check_nvic_pending(EXTI35_IRQ)); +} + +static void test_edge_selector(void) +{ + enable_nvic_irq(EXTI0_IRQ); + + /* Configure EXTI line 0 irq on rising edge */ + exti_set_irq(0, 1); + exti_writel(EXTI_IMR1, 0x00000001); + exti_writel(EXTI_RTSR1, 0x00000001); + exti_writel(EXTI_FTSR1, 0x00000000); + + /* Test that an irq is raised on rising edge only */ + exti_set_irq(0, 0); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + exti_set_irq(0, 1); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* Clean the test */ + exti_writel(EXTI_PR1, 0x00000001); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + unpend_nvic_irq(EXTI0_IRQ); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* Configure EXTI line 0 irq on falling edge */ + exti_set_irq(0, 0); + exti_writel(EXTI_IMR1, 0x00000001); + exti_writel(EXTI_RTSR1, 0x00000000); + exti_writel(EXTI_FTSR1, 0x00000001); + + /* Test that an irq is raised on falling edge only */ + exti_set_irq(0, 1); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + exti_set_irq(0, 0); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* Clean the test */ + exti_writel(EXTI_PR1, 0x00000001); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + unpend_nvic_irq(EXTI0_IRQ); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* Configure EXTI line 0 irq on falling and rising edge */ + exti_writel(EXTI_IMR1, 0x00000001); + exti_writel(EXTI_RTSR1, 0x00000001); + exti_writel(EXTI_FTSR1, 0x00000001); + + /* Test that an irq is raised on rising edge */ + exti_set_irq(0, 1); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* Clean the test */ + exti_writel(EXTI_PR1, 0x00000001); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + unpend_nvic_irq(EXTI0_IRQ); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* Test that an irq is raised on falling edge */ + exti_set_irq(0, 0); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* Clean the test */ + exti_writel(EXTI_PR1, 0x00000001); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + unpend_nvic_irq(EXTI0_IRQ); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* Configure EXTI line 0 irq without selecting an edge trigger */ + exti_writel(EXTI_IMR1, 0x00000001); + exti_writel(EXTI_RTSR1, 0x00000000); + exti_writel(EXTI_FTSR1, 0x00000000); + + /* Test that no irq is raised */ + exti_set_irq(0, 1); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + exti_set_irq(0, 0); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); +} + +static void test_no_software_interrupt(void) +{ + /* + * Test that software irq doesn't happen when : + * - corresponding bit in IMR isn't set + * - SWIER is set to 1 before IMR is set to 1 + */ + + /* + * Testing interrupt line EXTI0 + * Bit 0 in EXTI_*1 registers (EXTI0) corresponds to GPIO Px_0 + */ + + enable_nvic_irq(EXTI0_IRQ); + /* Check that there are no interrupts already pending in PR */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that this specific interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* Mask interrupt line EXTI0 */ + exti_writel(EXTI_IMR1, 0x00000000); + /* Set the corresponding SWIER bit from '0' to '1' */ + exti_writel(EXTI_SWIER1, 0x00000000); + exti_writel(EXTI_SWIER1, 0x00000001); + + /* Check that the write in SWIER was effective */ + g_assert_cmpuint(exti_readl(EXTI_SWIER1), ==, 0x00000001); + /* Check that the pending bit in PR wasn't set */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that the interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* Enable interrupt line EXTI0 */ + exti_writel(EXTI_IMR1, 0x00000001); + + /* Check that the pending bit in PR wasn't set */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that the interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* + * Testing interrupt line EXTI35 + * Bit 3 in EXTI_*2 registers (EXTI35) corresponds to PVM 1 Wakeup + */ + + enable_nvic_irq(EXTI35_IRQ); + /* Check that there are no interrupts already pending in PR */ + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + /* Check that this specific interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI35_IRQ)); + + /* Mask interrupt line EXTI35 */ + exti_writel(EXTI_IMR2, 0x00000000); + /* Set the corresponding SWIER bit from '0' to '1' */ + exti_writel(EXTI_SWIER2, 0x00000000); + exti_writel(EXTI_SWIER2, 0x00000008); + + /* Check that the write in SWIER was effective */ + g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000008); + /* Check that the pending bit in PR wasn't set */ + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + /* Check that the interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI35_IRQ)); + + /* Enable interrupt line EXTI35 */ + exti_writel(EXTI_IMR2, 0x00000008); + + /* Check that the pending bit in PR wasn't set */ + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + /* Check that the interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI35_IRQ)); +} + +static void test_masked_interrupt(void) +{ + /* + * Test that irq doesn't happen when : + * - corresponding bit in IMR isn't set + * - SWIER is set to 1 before IMR is set to 1 + */ + + /* + * Testing interrupt line EXTI1 + * with rising edge from GPIOx pin 1 + */ + + enable_nvic_irq(EXTI1_IRQ); + /* Check that there are no interrupts already pending in PR */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that this specific interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI1_IRQ)); + + /* Mask interrupt line EXTI1 */ + exti_writel(EXTI_IMR1, 0x00000000); + + /* Configure interrupt on rising edge */ + exti_writel(EXTI_RTSR1, 0x00000002); + + /* Simulate rising edge from GPIO line 1 */ + exti_set_irq(1, 1); + + /* Check that the pending bit in PR wasn't set */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that the interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI1_IRQ)); + + /* Enable interrupt line EXTI1 */ + exti_writel(EXTI_IMR1, 0x00000002); + + /* Check that the pending bit in PR wasn't set */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that the interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI1_IRQ)); +} + +static void test_interrupt(void) +{ + /* + * Test that we can launch an irq by : + * - enabling its line in IMR + * - configuring interrupt on rising edge + * - and then setting the input line from '0' to '1' + * + * And that the interruption stays pending in NVIC + * even after clearing the pending bit in PR. + */ + + /* + * Testing interrupt line EXTI1 + * with rising edge from GPIOx pin 1 + */ + + enable_nvic_irq(EXTI1_IRQ); + /* Check that there are no interrupts already pending in PR */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that this specific interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI1_IRQ)); + + /* Enable interrupt line EXTI1 */ + exti_writel(EXTI_IMR1, 0x00000002); + + /* Configure interrupt on rising edge */ + exti_writel(EXTI_RTSR1, 0x00000002); + + /* Simulate rising edge from GPIO line 1 */ + exti_set_irq(1, 1); + + /* Check that the pending bit in PR was set */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000002); + /* Check that the interrupt is pending in NVIC */ + g_assert_true(check_nvic_pending(EXTI1_IRQ)); + + /* Clear the pending bit in PR */ + exti_writel(EXTI_PR1, 0x00000002); + + /* Check that the write in PR was effective */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that the interrupt is still pending in the NVIC */ + g_assert_true(check_nvic_pending(EXTI1_IRQ)); + + /* Clean NVIC */ + unpend_nvic_irq(EXTI1_IRQ); + g_assert_false(check_nvic_pending(EXTI1_IRQ)); +} + +static void test_orred_interrupts(void) +{ + /* + * For lines EXTI5..9 (fanned-in to NVIC irq 23), + * test that raising the line pends interrupt + * 23 in NVIC. + */ + enable_nvic_irq(EXTI5_9_IRQ); + /* Check that there are no interrupts already pending in PR */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that this specific interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI5_9_IRQ)); + + /* Enable interrupt lines EXTI[5..9] */ + exti_writel(EXTI_IMR1, (0x1F << 5)); + + /* Configure interrupt on rising edge */ + exti_writel(EXTI_RTSR1, (0x1F << 5)); + + /* Raise GPIO line i, check that the interrupt is pending */ + for (unsigned i = 5; i < 10; i++) { + exti_set_irq(i, 1); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 1 << i); + g_assert_true(check_nvic_pending(EXTI5_9_IRQ)); + + exti_writel(EXTI_PR1, 1 << i); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_true(check_nvic_pending(EXTI5_9_IRQ)); + + unpend_nvic_irq(EXTI5_9_IRQ); + g_assert_false(check_nvic_pending(EXTI5_9_IRQ)); + } +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + qtest_add_func("stm32l4x5/exti/direct_lines", test_direct_lines_write); + qtest_add_func("stm32l4x5/exti/reserved_bits", test_reserved_bits_write); + qtest_add_func("stm32l4x5/exti/reg_write_read", test_reg_write_read); + qtest_add_func("stm32l4x5/exti/no_software_interrupt", + test_no_software_interrupt); + qtest_add_func("stm32l4x5/exti/software_interrupt", + test_software_interrupt); + qtest_add_func("stm32l4x5/exti/masked_interrupt", test_masked_interrupt); + qtest_add_func("stm32l4x5/exti/interrupt", test_interrupt); + qtest_add_func("stm32l4x5/exti/test_edge_selector", test_edge_selector); + qtest_add_func("stm32l4x5/exti/test_orred_interrupts", + test_orred_interrupts); + + qtest_start("-machine b-l475e-iot01a"); + ret = g_test_run(); + qtest_end(); + + return ret; +} diff --git a/tests/qtest/stm32l4x5_gpio-test.c b/tests/qtest/stm32l4x5_gpio-test.c new file mode 100644 index 0000000000..0f6bda54d3 --- /dev/null +++ b/tests/qtest/stm32l4x5_gpio-test.c @@ -0,0 +1,562 @@ +/* + * QTest testcase for STM32L4x5_GPIO + * + * Copyright (c) 2024 Arnaud Minier + * Copyright (c) 2024 Inès Varhol + * + * 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-single.h" + +#define GPIO_BASE_ADDR 0x48000000 +#define GPIO_SIZE 0x400 +#define NUM_GPIOS 8 +#define NUM_GPIO_PINS 16 + +#define GPIO_A 0x48000000 +#define GPIO_B 0x48000400 +#define GPIO_C 0x48000800 +#define GPIO_D 0x48000C00 +#define GPIO_E 0x48001000 +#define GPIO_F 0x48001400 +#define GPIO_G 0x48001800 +#define GPIO_H 0x48001C00 + +#define MODER 0x00 +#define OTYPER 0x04 +#define PUPDR 0x0C +#define IDR 0x10 +#define ODR 0x14 +#define BSRR 0x18 +#define BRR 0x28 + +#define MODER_INPUT 0 +#define MODER_OUTPUT 1 + +#define PUPDR_NONE 0 +#define PUPDR_PULLUP 1 +#define PUPDR_PULLDOWN 2 + +#define OTYPER_PUSH_PULL 0 +#define OTYPER_OPEN_DRAIN 1 + +const uint32_t moder_reset[NUM_GPIOS] = { + 0xABFFFFFF, + 0xFFFFFEBF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0x0000000F +}; + +const uint32_t pupdr_reset[NUM_GPIOS] = { + 0x64000000, + 0x00000100, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000 +}; + +const uint32_t idr_reset[NUM_GPIOS] = { + 0x0000A000, + 0x00000010, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000 +}; + +#define PIN_MASK 0xF +#define GPIO_ADDR_MASK (~(GPIO_SIZE - 1)) + +static inline void *test_data(uint32_t gpio_addr, uint8_t pin) +{ + return (void *)(uintptr_t)((gpio_addr & GPIO_ADDR_MASK) | (pin & PIN_MASK)); +} + +#define test_gpio_addr(data) ((uintptr_t)(data) & GPIO_ADDR_MASK) +#define test_pin(data) ((uintptr_t)(data) & PIN_MASK) + +static uint32_t gpio_readl(unsigned int gpio, unsigned int offset) +{ + return readl(gpio + offset); +} + +static void gpio_writel(unsigned int gpio, unsigned int offset, uint32_t value) +{ + writel(gpio + offset, value); +} + +static void gpio_set_bit(unsigned int gpio, unsigned int reg, + unsigned int pin, uint32_t value) +{ + uint32_t mask = 0xFFFFFFFF & ~(0x1 << pin); + gpio_writel(gpio, reg, (gpio_readl(gpio, reg) & mask) | value << pin); +} + +static void gpio_set_2bits(unsigned int gpio, unsigned int reg, + unsigned int pin, uint32_t value) +{ + uint32_t offset = 2 * pin; + uint32_t mask = 0xFFFFFFFF & ~(0x3 << offset); + gpio_writel(gpio, reg, (gpio_readl(gpio, reg) & mask) | value << offset); +} + +static unsigned int get_gpio_id(uint32_t gpio_addr) +{ + return (gpio_addr - GPIO_BASE_ADDR) / GPIO_SIZE; +} + +static void gpio_set_irq(unsigned int gpio, int num, int level) +{ + g_autofree char *name = g_strdup_printf("/machine/soc/gpio%c", + get_gpio_id(gpio) + 'a'); + qtest_set_irq_in(global_qtest, name, NULL, num, level); +} + +static void disconnect_all_pins(unsigned int gpio) +{ + g_autofree char *path = g_strdup_printf("/machine/soc/gpio%c", + get_gpio_id(gpio) + 'a'); + QDict *r; + + r = qtest_qmp(global_qtest, "{ 'execute': 'qom-set', 'arguments': " + "{ 'path': %s, 'property': 'disconnected-pins', 'value': %d } }", + path, 0xFFFF); + g_assert_false(qdict_haskey(r, "error")); + qobject_unref(r); +} + +static uint32_t get_disconnected_pins(unsigned int gpio) +{ + g_autofree char *path = g_strdup_printf("/machine/soc/gpio%c", + get_gpio_id(gpio) + 'a'); + uint32_t disconnected_pins = 0; + QDict *r; + + r = qtest_qmp(global_qtest, "{ 'execute': 'qom-get', 'arguments':" + " { 'path': %s, 'property': 'disconnected-pins'} }", path); + g_assert_false(qdict_haskey(r, "error")); + disconnected_pins = qdict_get_int(r, "return"); + qobject_unref(r); + return disconnected_pins; +} + +static uint32_t reset(uint32_t gpio, unsigned int offset) +{ + switch (offset) { + case MODER: + return moder_reset[get_gpio_id(gpio)]; + case PUPDR: + return pupdr_reset[get_gpio_id(gpio)]; + case IDR: + return idr_reset[get_gpio_id(gpio)]; + } + return 0x0; +} + +static void system_reset(void) +{ + QDict *r; + r = qtest_qmp(global_qtest, "{'execute': 'system_reset'}"); + g_assert_false(qdict_haskey(r, "error")); + qobject_unref(r); +} + +static void test_idr_reset_value(void) +{ + /* + * Checks that the values in MODER, OTYPER, PUPDR and ODR + * after reset are correct, and that the value in IDR is + * coherent. + * Since AF and analog modes aren't implemented, IDR reset + * values aren't the same as with a real board. + * + * Register IDR contains the actual values of all GPIO pins. + * Its value depends on the pins' configuration + * (intput/output/analog : register MODER, push-pull/open-drain : + * register OTYPER, pull-up/pull-down/none : register PUPDR) + * and on the values stored in register ODR + * (in case the pin is in output mode). + */ + + gpio_writel(GPIO_A, MODER, 0xDEADBEEF); + gpio_writel(GPIO_A, ODR, 0xDEADBEEF); + gpio_writel(GPIO_A, OTYPER, 0xDEADBEEF); + gpio_writel(GPIO_A, PUPDR, 0xDEADBEEF); + + gpio_writel(GPIO_B, MODER, 0xDEADBEEF); + gpio_writel(GPIO_B, ODR, 0xDEADBEEF); + gpio_writel(GPIO_B, OTYPER, 0xDEADBEEF); + gpio_writel(GPIO_B, PUPDR, 0xDEADBEEF); + + gpio_writel(GPIO_C, MODER, 0xDEADBEEF); + gpio_writel(GPIO_C, ODR, 0xDEADBEEF); + gpio_writel(GPIO_C, OTYPER, 0xDEADBEEF); + gpio_writel(GPIO_C, PUPDR, 0xDEADBEEF); + + gpio_writel(GPIO_H, MODER, 0xDEADBEEF); + gpio_writel(GPIO_H, ODR, 0xDEADBEEF); + gpio_writel(GPIO_H, OTYPER, 0xDEADBEEF); + gpio_writel(GPIO_H, PUPDR, 0xDEADBEEF); + + system_reset(); + + uint32_t moder = gpio_readl(GPIO_A, MODER); + uint32_t odr = gpio_readl(GPIO_A, ODR); + uint32_t otyper = gpio_readl(GPIO_A, OTYPER); + uint32_t pupdr = gpio_readl(GPIO_A, PUPDR); + uint32_t idr = gpio_readl(GPIO_A, IDR); + /* 15: AF, 14: AF, 13: AF, 12: Analog ... */ + /* here AF is the same as Analog and Input mode */ + g_assert_cmphex(moder, ==, reset(GPIO_A, MODER)); + g_assert_cmphex(odr, ==, reset(GPIO_A, ODR)); + g_assert_cmphex(otyper, ==, reset(GPIO_A, OTYPER)); + /* 15: pull-up, 14: pull-down, 13: pull-up, 12: neither ... */ + g_assert_cmphex(pupdr, ==, reset(GPIO_A, PUPDR)); + /* 15 : 1, 14: 0, 13: 1, 12 : reset value ... */ + g_assert_cmphex(idr, ==, reset(GPIO_A, IDR)); + + moder = gpio_readl(GPIO_B, MODER); + odr = gpio_readl(GPIO_B, ODR); + otyper = gpio_readl(GPIO_B, OTYPER); + pupdr = gpio_readl(GPIO_B, PUPDR); + idr = gpio_readl(GPIO_B, IDR); + /* ... 5: Analog, 4: AF, 3: AF, 2: Analog ... */ + /* here AF is the same as Analog and Input mode */ + g_assert_cmphex(moder, ==, reset(GPIO_B, MODER)); + g_assert_cmphex(odr, ==, reset(GPIO_B, ODR)); + g_assert_cmphex(otyper, ==, reset(GPIO_B, OTYPER)); + /* ... 5: neither, 4: pull-up, 3: neither ... */ + g_assert_cmphex(pupdr, ==, reset(GPIO_B, PUPDR)); + /* ... 5 : reset value, 4 : 1, 3 : reset value ... */ + g_assert_cmphex(idr, ==, reset(GPIO_B, IDR)); + + moder = gpio_readl(GPIO_C, MODER); + odr = gpio_readl(GPIO_C, ODR); + otyper = gpio_readl(GPIO_C, OTYPER); + pupdr = gpio_readl(GPIO_C, PUPDR); + idr = gpio_readl(GPIO_C, IDR); + /* Analog, same as Input mode*/ + g_assert_cmphex(moder, ==, reset(GPIO_C, MODER)); + g_assert_cmphex(odr, ==, reset(GPIO_C, ODR)); + g_assert_cmphex(otyper, ==, reset(GPIO_C, OTYPER)); + /* no pull-up or pull-down */ + g_assert_cmphex(pupdr, ==, reset(GPIO_C, PUPDR)); + /* reset value */ + g_assert_cmphex(idr, ==, reset(GPIO_C, IDR)); + + moder = gpio_readl(GPIO_H, MODER); + odr = gpio_readl(GPIO_H, ODR); + otyper = gpio_readl(GPIO_H, OTYPER); + pupdr = gpio_readl(GPIO_H, PUPDR); + idr = gpio_readl(GPIO_H, IDR); + /* Analog, same as Input mode */ + g_assert_cmphex(moder, ==, reset(GPIO_H, MODER)); + g_assert_cmphex(odr, ==, reset(GPIO_H, ODR)); + g_assert_cmphex(otyper, ==, reset(GPIO_H, OTYPER)); + /* no pull-up or pull-down */ + g_assert_cmphex(pupdr, ==, reset(GPIO_H, PUPDR)); + /* reset value */ + g_assert_cmphex(idr, ==, reset(GPIO_H, IDR)); +} + +static void test_gpio_output_mode(const void *data) +{ + /* + * Checks that setting a bit in ODR sets the corresponding + * GPIO line high : it should set the right bit in IDR + * and send an irq to syscfg. + * Additionally, it checks that values written to ODR + * when not in output mode are stored and not discarded. + */ + unsigned int pin = test_pin(data); + uint32_t gpio = test_gpio_addr(data); + unsigned int gpio_id = get_gpio_id(gpio); + + qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg"); + + /* Set a bit in ODR and check nothing happens */ + gpio_set_bit(gpio, ODR, pin, 1); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR)); + g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin)); + + /* Configure the relevant line as output and check the pin is high */ + gpio_set_2bits(gpio, MODER, pin, MODER_OUTPUT); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) | (1 << pin)); + g_assert_true(get_irq(gpio_id * NUM_GPIO_PINS + pin)); + + /* Reset the bit in ODR and check the pin is low */ + gpio_set_bit(gpio, ODR, pin, 0); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin)); + g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin)); + + /* Clean the test */ + gpio_writel(gpio, ODR, reset(gpio, ODR)); + gpio_writel(gpio, MODER, reset(gpio, MODER)); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR)); + g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin)); +} + +static void test_gpio_input_mode(const void *data) +{ + /* + * Test that setting a line high/low externally sets the + * corresponding GPIO line high/low : it should set the + * right bit in IDR and send an irq to syscfg. + */ + unsigned int pin = test_pin(data); + uint32_t gpio = test_gpio_addr(data); + unsigned int gpio_id = get_gpio_id(gpio); + + qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg"); + + /* Configure a line as input, raise it, and check that the pin is high */ + gpio_set_2bits(gpio, MODER, pin, MODER_INPUT); + gpio_set_irq(gpio, pin, 1); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) | (1 << pin)); + g_assert_true(get_irq(gpio_id * NUM_GPIO_PINS + pin)); + + /* Lower the line and check that the pin is low */ + gpio_set_irq(gpio, pin, 0); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin)); + g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin)); + + /* Clean the test */ + gpio_writel(gpio, MODER, reset(gpio, MODER)); + disconnect_all_pins(gpio); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR)); +} + +static void test_pull_up_pull_down(const void *data) +{ + /* + * Test that a floating pin with pull-up sets the pin + * high and vice-versa. + */ + unsigned int pin = test_pin(data); + uint32_t gpio = test_gpio_addr(data); + unsigned int gpio_id = get_gpio_id(gpio); + + qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg"); + + /* Configure a line as input with pull-up, check the line is set high */ + gpio_set_2bits(gpio, MODER, pin, MODER_INPUT); + gpio_set_2bits(gpio, PUPDR, pin, PUPDR_PULLUP); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) | (1 << pin)); + g_assert_true(get_irq(gpio_id * NUM_GPIO_PINS + pin)); + + /* Configure the line with pull-down, check the line is low */ + gpio_set_2bits(gpio, PUPDR, pin, PUPDR_PULLDOWN); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin)); + g_assert_false(get_irq(gpio_id * NUM_GPIO_PINS + pin)); + + /* Clean the test */ + gpio_writel(gpio, MODER, reset(gpio, MODER)); + gpio_writel(gpio, PUPDR, reset(gpio, PUPDR)); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR)); +} + +static void test_push_pull(const void *data) +{ + /* + * Test that configuring a line in push-pull output mode + * disconnects the pin, that the pin can't be set or reset + * externally afterwards. + */ + unsigned int pin = test_pin(data); + uint32_t gpio = test_gpio_addr(data); + uint32_t gpio2 = GPIO_BASE_ADDR + (GPIO_H - gpio); + + qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg"); + + /* Setting a line high externally, configuring it in push-pull output */ + /* And checking the pin was disconnected */ + gpio_set_irq(gpio, pin, 1); + gpio_set_2bits(gpio, MODER, pin, MODER_OUTPUT); + g_assert_cmphex(get_disconnected_pins(gpio), ==, 0xFFFF); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin)); + + /* Setting a line low externally, configuring it in push-pull output */ + /* And checking the pin was disconnected */ + gpio_set_irq(gpio2, pin, 0); + gpio_set_bit(gpio2, ODR, pin, 1); + gpio_set_2bits(gpio2, MODER, pin, MODER_OUTPUT); + g_assert_cmphex(get_disconnected_pins(gpio2), ==, 0xFFFF); + g_assert_cmphex(gpio_readl(gpio2, IDR), ==, reset(gpio2, IDR) | (1 << pin)); + + /* Trying to set a push-pull output pin, checking it doesn't work */ + gpio_set_irq(gpio, pin, 1); + g_assert_cmphex(get_disconnected_pins(gpio), ==, 0xFFFF); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin)); + + /* Trying to reset a push-pull output pin, checking it doesn't work */ + gpio_set_irq(gpio2, pin, 0); + g_assert_cmphex(get_disconnected_pins(gpio2), ==, 0xFFFF); + g_assert_cmphex(gpio_readl(gpio2, IDR), ==, reset(gpio2, IDR) | (1 << pin)); + + /* Clean the test */ + gpio_writel(gpio, MODER, reset(gpio, MODER)); + gpio_writel(gpio2, ODR, reset(gpio2, ODR)); + gpio_writel(gpio2, MODER, reset(gpio2, MODER)); +} + +static void test_open_drain(const void *data) +{ + /* + * Test that configuring a line in open-drain output mode + * disconnects a pin set high externally and that the pin + * can't be set high externally while configured in open-drain. + * + * However a pin set low externally shouldn't be disconnected, + * and it can be set low externally when in open-drain mode. + */ + unsigned int pin = test_pin(data); + uint32_t gpio = test_gpio_addr(data); + uint32_t gpio2 = GPIO_BASE_ADDR + (GPIO_H - gpio); + + qtest_irq_intercept_in(global_qtest, "/machine/soc/syscfg"); + + /* Setting a line high externally, configuring it in open-drain output */ + /* And checking the pin was disconnected */ + gpio_set_irq(gpio, pin, 1); + gpio_set_bit(gpio, OTYPER, pin, OTYPER_OPEN_DRAIN); + gpio_set_2bits(gpio, MODER, pin, MODER_OUTPUT); + g_assert_cmphex(get_disconnected_pins(gpio), ==, 0xFFFF); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin)); + + /* Setting a line low externally, configuring it in open-drain output */ + /* And checking the pin wasn't disconnected */ + gpio_set_irq(gpio2, pin, 0); + gpio_set_bit(gpio2, ODR, pin, 1); + gpio_set_bit(gpio2, OTYPER, pin, OTYPER_OPEN_DRAIN); + gpio_set_2bits(gpio2, MODER, pin, MODER_OUTPUT); + g_assert_cmphex(get_disconnected_pins(gpio2), ==, 0xFFFF & ~(1 << pin)); + g_assert_cmphex(gpio_readl(gpio2, IDR), ==, + reset(gpio2, IDR) & ~(1 << pin)); + + /* Trying to set a open-drain output pin, checking it doesn't work */ + gpio_set_irq(gpio, pin, 1); + g_assert_cmphex(get_disconnected_pins(gpio), ==, 0xFFFF); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR) & ~(1 << pin)); + + /* Trying to reset a open-drain output pin, checking it works */ + gpio_set_bit(gpio, ODR, pin, 1); + gpio_set_irq(gpio, pin, 0); + g_assert_cmphex(get_disconnected_pins(gpio2), ==, 0xFFFF & ~(1 << pin)); + g_assert_cmphex(gpio_readl(gpio2, IDR), ==, + reset(gpio2, IDR) & ~(1 << pin)); + + /* Clean the test */ + disconnect_all_pins(gpio2); + gpio_writel(gpio2, OTYPER, reset(gpio2, OTYPER)); + gpio_writel(gpio2, ODR, reset(gpio2, ODR)); + gpio_writel(gpio2, MODER, reset(gpio2, MODER)); + g_assert_cmphex(gpio_readl(gpio2, IDR), ==, reset(gpio2, IDR)); + disconnect_all_pins(gpio); + gpio_writel(gpio, OTYPER, reset(gpio, OTYPER)); + gpio_writel(gpio, ODR, reset(gpio, ODR)); + gpio_writel(gpio, MODER, reset(gpio, MODER)); + g_assert_cmphex(gpio_readl(gpio, IDR), ==, reset(gpio, IDR)); +} + +static void test_bsrr_brr(const void *data) +{ + /* + * Test that writing a '1' in BSS and BSRR + * has the desired effect on ODR. + * In BSRR, BSx has priority over BRx. + */ + unsigned int pin = test_pin(data); + uint32_t gpio = test_gpio_addr(data); + + gpio_writel(gpio, BSRR, (1 << pin)); + g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR) | (1 << pin)); + + gpio_writel(gpio, BSRR, (1 << (pin + NUM_GPIO_PINS))); + g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR)); + + gpio_writel(gpio, BSRR, (1 << pin)); + g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR) | (1 << pin)); + + gpio_writel(gpio, BRR, (1 << pin)); + g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR)); + + /* BSx should have priority over BRx */ + gpio_writel(gpio, BSRR, (1 << pin) | (1 << (pin + NUM_GPIO_PINS))); + g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR) | (1 << pin)); + + gpio_writel(gpio, BRR, (1 << pin)); + g_assert_cmphex(gpio_readl(gpio, ODR), ==, reset(gpio, ODR)); + + gpio_writel(gpio, ODR, reset(gpio, ODR)); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + qtest_add_func("stm32l4x5/gpio/test_idr_reset_value", + test_idr_reset_value); + /* + * The inputs for the tests (gpio and pin) can be changed, + * but the tests don't work for pins that are high at reset + * (GPIOA15, GPIO13 and GPIOB5). + * Specifically, rising the pin then checking `get_irq()` + * is problematic since the pin was already high. + */ + qtest_add_data_func("stm32l4x5/gpio/test_gpioc5_output_mode", + test_data(GPIO_C, 5), + test_gpio_output_mode); + qtest_add_data_func("stm32l4x5/gpio/test_gpioh3_output_mode", + test_data(GPIO_H, 3), + test_gpio_output_mode); + qtest_add_data_func("stm32l4x5/gpio/test_gpio_input_mode1", + test_data(GPIO_D, 6), + test_gpio_input_mode); + qtest_add_data_func("stm32l4x5/gpio/test_gpio_input_mode2", + test_data(GPIO_C, 10), + test_gpio_input_mode); + qtest_add_data_func("stm32l4x5/gpio/test_gpio_pull_up_pull_down1", + test_data(GPIO_B, 5), + test_pull_up_pull_down); + qtest_add_data_func("stm32l4x5/gpio/test_gpio_pull_up_pull_down2", + test_data(GPIO_F, 1), + test_pull_up_pull_down); + qtest_add_data_func("stm32l4x5/gpio/test_gpio_push_pull1", + test_data(GPIO_G, 6), + test_push_pull); + qtest_add_data_func("stm32l4x5/gpio/test_gpio_push_pull2", + test_data(GPIO_H, 3), + test_push_pull); + qtest_add_data_func("stm32l4x5/gpio/test_gpio_open_drain1", + test_data(GPIO_C, 4), + test_open_drain); + qtest_add_data_func("stm32l4x5/gpio/test_gpio_open_drain2", + test_data(GPIO_E, 11), + test_open_drain); + qtest_add_data_func("stm32l4x5/gpio/test_bsrr_brr1", + test_data(GPIO_A, 12), + test_bsrr_brr); + qtest_add_data_func("stm32l4x5/gpio/test_bsrr_brr2", + test_data(GPIO_D, 0), + test_bsrr_brr); + + qtest_start("-machine b-l475e-iot01a"); + ret = g_test_run(); + qtest_end(); + + return ret; +} diff --git a/tests/qtest/stm32l4x5_rcc-test.c b/tests/qtest/stm32l4x5_rcc-test.c new file mode 100644 index 0000000000..d927c655d1 --- /dev/null +++ b/tests/qtest/stm32l4x5_rcc-test.c @@ -0,0 +1,189 @@ +/* + * QTest testcase for STM32L4x5_RCC + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/registerfields.h" +#include "libqtest-single.h" +#include "hw/misc/stm32l4x5_rcc_internals.h" + +#define RCC_BASE_ADDR 0x40021000 +#define NVIC_ISER 0xE000E100 +#define NVIC_ISPR 0xE000E200 +#define NVIC_ICPR 0xE000E280 +#define RCC_IRQ 5 + +static void enable_nvic_irq(unsigned int n) +{ + writel(NVIC_ISER, 1 << n); +} + +static void unpend_nvic_irq(unsigned int n) +{ + writel(NVIC_ICPR, 1 << n); +} + +static bool check_nvic_pending(unsigned int n) +{ + return readl(NVIC_ISPR) & (1 << n); +} + +static void rcc_writel(unsigned int offset, uint32_t value) +{ + writel(RCC_BASE_ADDR + offset, value); +} + +static uint32_t rcc_readl(unsigned int offset) +{ + return readl(RCC_BASE_ADDR + offset); +} + +static void test_init_msi(void) +{ + /* MSIRANGE can be set only when MSI is OFF or READY */ + rcc_writel(A_CR, R_CR_MSION_MASK); + /* Wait until MSI is stable */ + g_assert_true((rcc_readl(A_CR) & R_CR_MSIRDY_MASK) == R_CR_MSIRDY_MASK); + /* TODO find a way to test MSI value */ +} + +static void test_set_msi_as_sysclk(void) +{ + /* Clocking from MSI, in case MSI was not the default source */ + rcc_writel(A_CFGR, 0); + /* Wait until MSI is selected and stable */ + g_assert_true((rcc_readl(A_CFGR) & R_CFGR_SWS_MASK) == 0); +} + +static void test_init_pll(void) +{ + uint32_t value; + + /* + * Update PLL and set MSI as the source clock. + * PLLM = 1 --> 000 + * PLLN = 40 --> 40 + * PPLLR = 2 --> 00 + * PLLDIV = unused, PLLP = unused (SAI3), PLLQ = unused (48M1) + * SRC = MSI --> 01 + */ + rcc_writel(A_PLLCFGR, R_PLLCFGR_PLLREN_MASK | + (40 << R_PLLCFGR_PLLN_SHIFT) | + (0b01 << R_PLLCFGR_PLLSRC_SHIFT)); + + /* PLL activation */ + value = rcc_readl(A_CR); + rcc_writel(A_CR, value | R_CR_PLLON_MASK); + + /* Waiting for PLL lock. */ + g_assert_true((rcc_readl(A_CR) & R_CR_PLLRDY_MASK) == R_CR_PLLRDY_MASK); + + /* Switches on the PLL clock source */ + value = rcc_readl(A_CFGR); + rcc_writel(A_CFGR, (value & ~R_CFGR_SW_MASK) | + (0b11 << R_CFGR_SW_SHIFT)); + + /* Wait until SYSCLK is stable. */ + g_assert_true((rcc_readl(A_CFGR) & R_CFGR_SWS_MASK) == + (0b11 << R_CFGR_SWS_SHIFT)); +} + +static void test_activate_lse(void) +{ + /* LSE activation, no LSE Bypass */ + rcc_writel(A_BDCR, R_BDCR_LSEDRV_MASK | R_BDCR_LSEON_MASK); + g_assert_true((rcc_readl(A_BDCR) & R_BDCR_LSERDY_MASK) == R_BDCR_LSERDY_MASK); +} + +static void test_irq(void) +{ + enable_nvic_irq(RCC_IRQ); + + rcc_writel(A_CIER, R_CIER_LSIRDYIE_MASK); + rcc_writel(A_CSR, R_CSR_LSION_MASK); + g_assert_true(check_nvic_pending(RCC_IRQ)); + rcc_writel(A_CICR, R_CICR_LSIRDYC_MASK); + unpend_nvic_irq(RCC_IRQ); + + rcc_writel(A_CIER, R_CIER_LSERDYIE_MASK); + rcc_writel(A_BDCR, R_BDCR_LSEON_MASK); + g_assert_true(check_nvic_pending(RCC_IRQ)); + rcc_writel(A_CICR, R_CICR_LSERDYC_MASK); + unpend_nvic_irq(RCC_IRQ); + + /* + * MSI has been enabled by previous tests, + * shouln't generate an interruption. + */ + rcc_writel(A_CIER, R_CIER_MSIRDYIE_MASK); + rcc_writel(A_CR, R_CR_MSION_MASK); + g_assert_false(check_nvic_pending(RCC_IRQ)); + + rcc_writel(A_CIER, R_CIER_HSIRDYIE_MASK); + rcc_writel(A_CR, R_CR_HSION_MASK); + g_assert_true(check_nvic_pending(RCC_IRQ)); + rcc_writel(A_CICR, R_CICR_HSIRDYC_MASK); + unpend_nvic_irq(RCC_IRQ); + + rcc_writel(A_CIER, R_CIER_HSERDYIE_MASK); + rcc_writel(A_CR, R_CR_HSEON_MASK); + g_assert_true(check_nvic_pending(RCC_IRQ)); + rcc_writel(A_CICR, R_CICR_HSERDYC_MASK); + unpend_nvic_irq(RCC_IRQ); + + /* + * PLL has been enabled by previous tests, + * shouln't generate an interruption. + */ + rcc_writel(A_CIER, R_CIER_PLLRDYIE_MASK); + rcc_writel(A_CR, R_CR_PLLON_MASK); + g_assert_false(check_nvic_pending(RCC_IRQ)); + + rcc_writel(A_CIER, R_CIER_PLLSAI1RDYIE_MASK); + rcc_writel(A_CR, R_CR_PLLSAI1ON_MASK); + g_assert_true(check_nvic_pending(RCC_IRQ)); + rcc_writel(A_CICR, R_CICR_PLLSAI1RDYC_MASK); + unpend_nvic_irq(RCC_IRQ); + + rcc_writel(A_CIER, R_CIER_PLLSAI2RDYIE_MASK); + rcc_writel(A_CR, R_CR_PLLSAI2ON_MASK); + g_assert_true(check_nvic_pending(RCC_IRQ)); + rcc_writel(A_CICR, R_CICR_PLLSAI2RDYC_MASK); + unpend_nvic_irq(RCC_IRQ); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + /* + * These test separately that we can enable the plls, change the sysclk, + * and enable different devices. + * They are dependent on one another. + * We assume that all operations that would take some time to have an effect + * (e.g. changing the PLL frequency) are done instantaneously. + */ + qtest_add_func("stm32l4x5/rcc/init_msi", test_init_msi); + qtest_add_func("stm32l4x5/rcc/set_msi_as_sysclk", + test_set_msi_as_sysclk); + qtest_add_func("stm32l4x5/rcc/activate_lse", test_activate_lse); + qtest_add_func("stm32l4x5/rcc/init_pll", test_init_pll); + + qtest_add_func("stm32l4x5/rcc/irq", test_irq); + + qtest_start("-machine b-l475e-iot01a"); + ret = g_test_run(); + qtest_end(); + + return ret; +} diff --git a/tests/qtest/stm32l4x5_syscfg-test.c b/tests/qtest/stm32l4x5_syscfg-test.c new file mode 100644 index 0000000000..ed4801798d --- /dev/null +++ b/tests/qtest/stm32l4x5_syscfg-test.c @@ -0,0 +1,331 @@ +/* + * QTest testcase for STM32L4x5_SYSCFG + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * 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-single.h" + +#define SYSCFG_BASE_ADDR 0x40010000 +#define SYSCFG_MEMRMP 0x00 +#define SYSCFG_CFGR1 0x04 +#define SYSCFG_EXTICR1 0x08 +#define SYSCFG_EXTICR2 0x0C +#define SYSCFG_EXTICR3 0x10 +#define SYSCFG_EXTICR4 0x14 +#define SYSCFG_SCSR 0x18 +#define SYSCFG_CFGR2 0x1C +#define SYSCFG_SWPR 0x20 +#define SYSCFG_SKR 0x24 +#define SYSCFG_SWPR2 0x28 +#define INVALID_ADDR 0x2C + +static void syscfg_writel(unsigned int offset, uint32_t value) +{ + writel(SYSCFG_BASE_ADDR + offset, value); +} + +static uint32_t syscfg_readl(unsigned int offset) +{ + return readl(SYSCFG_BASE_ADDR + offset); +} + +static void syscfg_set_irq(int num, int level) +{ + qtest_set_irq_in(global_qtest, "/machine/soc/syscfg", + NULL, num, level); +} + +static void system_reset(void) +{ + QDict *response; + response = qtest_qmp(global_qtest, "{'execute': 'system_reset'}"); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); +} + +static void test_reset(void) +{ + /* + * Test that registers are initialized at the correct values + */ + g_assert_cmpuint(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0x7C000001); + + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_SCSR), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR2), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_SWPR), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_SKR), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_SWPR2), ==, 0x00000000); +} + +static void test_reserved_bits(void) +{ + /* + * Test that reserved bits stay at reset value + * (which is 0 for all of them) by writing '1' + * in all reserved bits (keeping reset value for + * other bits) and checking that the + * register is still at reset value + */ + syscfg_writel(SYSCFG_MEMRMP, 0xFFFFFEF8); + g_assert_cmpuint(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); + + syscfg_writel(SYSCFG_CFGR1, 0x7F00FEFF); + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0x7C000001); + + syscfg_writel(SYSCFG_EXTICR1, 0xFFFF0000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR2, 0xFFFF0000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR3, 0xFFFF0000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR4, 0xFFFF0000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); + + syscfg_writel(SYSCFG_SKR, 0xFFFFFF00); + g_assert_cmpuint(syscfg_readl(SYSCFG_SKR), ==, 0x00000000); +} + +static void test_set_and_clear(void) +{ + /* + * Test that regular bits can be set and cleared + */ + syscfg_writel(SYSCFG_MEMRMP, 0x00000107); + g_assert_cmpuint(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000107); + syscfg_writel(SYSCFG_MEMRMP, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); + + /* cfgr1 bit 0 is clear only so we keep it set */ + syscfg_writel(SYSCFG_CFGR1, 0xFCFF0101); + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0xFCFF0101); + syscfg_writel(SYSCFG_CFGR1, 0x00000001); + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0x00000001); + + syscfg_writel(SYSCFG_EXTICR1, 0x0000FFFF); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR1), ==, 0x0000FFFF); + syscfg_writel(SYSCFG_EXTICR1, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR2, 0x0000FFFF); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR2), ==, 0x0000FFFF); + syscfg_writel(SYSCFG_EXTICR2, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR3, 0x0000FFFF); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR3), ==, 0x0000FFFF); + syscfg_writel(SYSCFG_EXTICR3, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR4, 0x0000FFFF); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR4), ==, 0x0000FFFF); + syscfg_writel(SYSCFG_EXTICR4, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); + + syscfg_writel(SYSCFG_SKR, 0x000000FF); + g_assert_cmpuint(syscfg_readl(SYSCFG_SKR), ==, 0x000000FF); + syscfg_writel(SYSCFG_SKR, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_SKR), ==, 0x00000000); +} + +static void test_clear_by_writing_1(void) +{ + /* + * Test that writing '1' doesn't set the bit + */ + syscfg_writel(SYSCFG_CFGR2, 0x00000100); + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR2), ==, 0x00000000); +} + +static void test_set_only_bits(void) +{ + /* + * Test that set only bits stay can't be cleared + */ + syscfg_writel(SYSCFG_CFGR2, 0x0000000F); + syscfg_writel(SYSCFG_CFGR2, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR2), ==, 0x0000000F); + + syscfg_writel(SYSCFG_SWPR, 0xFFFFFFFF); + syscfg_writel(SYSCFG_SWPR, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_SWPR), ==, 0xFFFFFFFF); + + syscfg_writel(SYSCFG_SWPR2, 0xFFFFFFFF); + syscfg_writel(SYSCFG_SWPR2, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_SWPR2), ==, 0xFFFFFFFF); + + system_reset(); +} + +static void test_clear_only_bits(void) +{ + /* + * Test that clear only bits stay can't be set + */ + syscfg_writel(SYSCFG_CFGR1, 0x00000000); + syscfg_writel(SYSCFG_CFGR1, 0x00000001); + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0x00000000); + + system_reset(); +} + +static void test_interrupt(void) +{ + /* + * Test that GPIO rising lines result in an irq + * with the right configuration + */ + qtest_irq_intercept_in(global_qtest, "/machine/soc/exti"); + + /* GPIOA is the default source for EXTI lines 0 to 15 */ + + syscfg_set_irq(0, 1); + + g_assert_true(get_irq(0)); + + + syscfg_set_irq(15, 1); + + g_assert_true(get_irq(15)); + + /* Configure GPIOB[1] as the source input for EXTI1 */ + syscfg_writel(SYSCFG_EXTICR1, 0x00000010); + + syscfg_set_irq(17, 1); + + g_assert_true(get_irq(1)); + + /* Clean the test */ + syscfg_writel(SYSCFG_EXTICR1, 0x00000000); + syscfg_set_irq(0, 0); + syscfg_set_irq(15, 0); + syscfg_set_irq(17, 0); +} + +static void test_irq_pin_multiplexer(void) +{ + /* + * Test that syscfg irq sets the right exti irq + */ + + qtest_irq_intercept_in(global_qtest, "/machine/soc/exti"); + + syscfg_set_irq(0, 1); + + /* Check that irq 0 was set and irq 15 wasn't */ + g_assert_true(get_irq(0)); + g_assert_false(get_irq(15)); + + /* Clean the test */ + syscfg_set_irq(0, 0); + + syscfg_set_irq(15, 1); + + /* Check that irq 15 was set and irq 0 wasn't */ + g_assert_true(get_irq(15)); + g_assert_false(get_irq(0)); + + /* Clean the test */ + syscfg_set_irq(15, 0); +} + +static void test_irq_gpio_multiplexer(void) +{ + /* + * Test that an irq is generated only by the right GPIO + */ + + qtest_irq_intercept_in(global_qtest, "/machine/soc/exti"); + + /* GPIOA is the default source for EXTI lines 0 to 15 */ + + /* Check that setting rising pin GPIOA[0] generates an irq */ + syscfg_set_irq(0, 1); + + g_assert_true(get_irq(0)); + + /* Clean the test */ + syscfg_set_irq(0, 0); + + /* Check that setting rising pin GPIOB[0] doesn't generate an irq */ + syscfg_set_irq(16, 1); + + g_assert_false(get_irq(0)); + + /* Clean the test */ + syscfg_set_irq(16, 0); + + /* Configure GPIOB[0] as the source input for EXTI0 */ + syscfg_writel(SYSCFG_EXTICR1, 0x00000001); + + /* Check that setting rising pin GPIOA[0] doesn't generate an irq */ + syscfg_set_irq(0, 1); + + g_assert_false(get_irq(0)); + + /* Clean the test */ + syscfg_set_irq(0, 0); + + /* Check that setting rising pin GPIOB[0] generates an irq */ + syscfg_set_irq(16, 1); + + g_assert_true(get_irq(0)); + + /* Clean the test */ + syscfg_set_irq(16, 0); + syscfg_writel(SYSCFG_EXTICR1, 0x00000000); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + + qtest_add_func("stm32l4x5/syscfg/test_reset", test_reset); + qtest_add_func("stm32l4x5/syscfg/test_reserved_bits", + test_reserved_bits); + qtest_add_func("stm32l4x5/syscfg/test_set_and_clear", + test_set_and_clear); + qtest_add_func("stm32l4x5/syscfg/test_clear_by_writing_1", + test_clear_by_writing_1); + qtest_add_func("stm32l4x5/syscfg/test_set_only_bits", + test_set_only_bits); + qtest_add_func("stm32l4x5/syscfg/test_clear_only_bits", + test_clear_only_bits); + qtest_add_func("stm32l4x5/syscfg/test_interrupt", + test_interrupt); + qtest_add_func("stm32l4x5/syscfg/test_irq_pin_multiplexer", + test_irq_pin_multiplexer); + qtest_add_func("stm32l4x5/syscfg/test_irq_gpio_multiplexer", + test_irq_gpio_multiplexer); + + qtest_start("-machine b-l475e-iot01a"); + ret = g_test_run(); + qtest_end(); + + return ret; +} diff --git a/tests/qtest/tco-test.c b/tests/qtest/tco-test.c index 254f735370..0547d41173 100644 --- a/tests/qtest/tco-test.c +++ b/tests/qtest/tco-test.c @@ -14,9 +14,9 @@ #include "libqos/pci-pc.h" #include "qapi/qmp/qdict.h" #include "hw/pci/pci_regs.h" -#include "hw/i386/ich9.h" +#include "hw/southbridge/ich9.h" #include "hw/acpi/ich9.h" -#include "hw/acpi/tco.h" +#include "hw/acpi/ich9_tco.h" #define RCBA_BASE_ADDR 0xfed1c000 #define PM_IO_BASE_ADDR 0xb000 @@ -60,7 +60,7 @@ static void test_init(TestData *d) QTestState *qs; qs = qtest_initf("-machine q35 %s %s", - d->noreboot ? "" : "-global ICH9-LPC.noreboot=false", + d->noreboot ? "-global ICH9-LPC.noreboot=true" : "", !d->args ? "" : d->args); qtest_irq_intercept_in(qs, "ioapic"); diff --git a/tests/qtest/test-filter-mirror.c b/tests/qtest/test-filter-mirror.c index c8b0a92b53..f3865f7519 100644 --- a/tests/qtest/test-filter-mirror.c +++ b/tests/qtest/test-filter-mirror.c @@ -16,9 +16,6 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__)) - static void test_mirror(void) { int send_sock[2], recv_sock[2]; @@ -52,7 +49,7 @@ static void test_mirror(void) }; /* send a qmp command to guarantee that 'connected' is setting to true. */ - qmp_discard_response(qts, "{ 'execute' : 'query-status'}"); + qtest_qmp_assert_success(qts, "{ 'execute' : 'query-status'}"); ret = iov_send(send_sock[0], iov, 2, 0, sizeof(size) + sizeof(send_buf)); g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); close(send_sock[0]); @@ -64,6 +61,7 @@ static void test_mirror(void) g_assert_cmpint(len, ==, sizeof(send_buf)); recv_buf = g_malloc(len); ret = recv(recv_sock[0], recv_buf, len, 0); + g_assert_cmpint(ret, ==, len); g_assert_cmpstr(recv_buf, ==, send_buf); g_free(recv_buf); @@ -76,12 +74,8 @@ static void test_mirror(void) int main(int argc, char **argv) { - int ret; - g_test_init(&argc, &argv, NULL); qtest_add_func("/netfilter/mirror", test_mirror); - ret = g_test_run(); - - return ret; + return g_test_run(); } diff --git a/tests/qtest/test-filter-redirector.c b/tests/qtest/test-filter-redirector.c index 24ca9280f8..a77d5fd8ec 100644 --- a/tests/qtest/test-filter-redirector.c +++ b/tests/qtest/test-filter-redirector.c @@ -58,9 +58,6 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__)) - static void test_redirector_tx(void) { int backend_sock[2], recv_sock; @@ -98,7 +95,7 @@ static void test_redirector_tx(void) g_assert_cmpint(recv_sock, !=, -1); /* send a qmp command to guarantee that 'connected' is setting to true. */ - qmp_discard_response(qts, "{ 'execute' : 'query-status'}"); + qtest_qmp_assert_success(qts, "{ 'execute' : 'query-status'}"); struct iovec iov[] = { { @@ -121,6 +118,7 @@ static void test_redirector_tx(void) g_assert_cmpint(len, ==, sizeof(send_buf)); recv_buf = g_malloc(len); ret = recv(recv_sock, recv_buf, len, 0); + g_assert_cmpint(ret, ==, len); g_assert_cmpstr(recv_buf, ==, send_buf); g_free(recv_buf); @@ -176,7 +174,7 @@ static void test_redirector_rx(void) send_sock = unix_connect(sock_path1, NULL); g_assert_cmpint(send_sock, !=, -1); /* send a qmp command to guarantee that 'connected' is setting to true. */ - qmp_discard_response(qts, "{ 'execute' : 'query-status'}"); + qtest_qmp_assert_success(qts, "{ 'execute' : 'query-status'}"); ret = iov_send(send_sock, iov, 2, 0, sizeof(size) + sizeof(send_buf)); g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); @@ -188,6 +186,7 @@ static void test_redirector_rx(void) g_assert_cmpint(len, ==, sizeof(send_buf)); recv_buf = g_malloc(len); ret = recv(backend_sock[0], recv_buf, len, 0); + g_assert_cmpint(ret, ==, len); g_assert_cmpstr(recv_buf, ==, send_buf); close(send_sock); diff --git a/tests/qtest/test-hmp.c b/tests/qtest/test-hmp.c index f8b22abe4c..1b2e07522f 100644 --- a/tests/qtest/test-hmp.c +++ b/tests/qtest/test-hmp.c @@ -45,9 +45,9 @@ static const char *hmp_cmds[] = { "log all", "log none", "memsave 0 4096 \"/dev/null\"", - "migrate_set_parameter xbzrle_cache_size 1", - "migrate_set_parameter downtime_limit 1", - "migrate_set_parameter max_bandwidth 1", + "migrate_set_parameter xbzrle-cache-size 64k", + "migrate_set_parameter downtime-limit 1", + "migrate_set_parameter max-bandwidth 1", "netdev_add user,id=net1", "set_link net1 off", "set_link net1 on", @@ -56,6 +56,7 @@ static const char *hmp_cmds[] = { "o /w 0 0x1234", "object_add memory-backend-ram,id=mem1,size=256M", "object_del mem1", + "one-insn-per-tb on", "pmemsave 0 4096 \"/dev/null\"", "p $pc + 8", "qom-list /", @@ -63,7 +64,6 @@ static const char *hmp_cmds[] = { "qom-get /machine initrd", "screendump /dev/null", "sendkey x", - "singlestep on", "wavcapture /dev/null", "stopcapture 0", "sum 0 512", @@ -151,7 +151,7 @@ int main(int argc, char **argv) { char *v_env = getenv("V"); - if (v_env && *v_env >= '2') { + if (v_env && atoi(v_env) >= 2) { verbose = true; } diff --git a/tests/qtest/test-x86-cpuid-compat.c b/tests/qtest/test-x86-cpuid-compat.c index b39c9055b3..6a39454fce 100644 --- a/tests/qtest/test-x86-cpuid-compat.c +++ b/tests/qtest/test-x86-cpuid-compat.c @@ -313,18 +313,10 @@ int main(int argc, char **argv) "xlevel2", 0); } /* - * QEMU 1.4.0 had auto-level enabled for CPUID[7], already, + * QEMU 2.3.0 had auto-level enabled for CPUID[7], already, * and the compat code that sets default level shouldn't * disable the auto-level=7 code: */ - if (qtest_has_machine("pc-i440fx-1.4")) { - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-1.4/off", - "-machine pc-i440fx-1.4 -cpu Nehalem", - "level", 2); - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-1.5/on", - "-machine pc-i440fx-1.4 -cpu Nehalem,smap=on", - "level", 7); - } if (qtest_has_machine("pc-i440fx-2.3")) { add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/off", "-machine pc-i440fx-2.3 -cpu Penryn", diff --git a/tests/qtest/tpm-crb-swtpm-test.c b/tests/qtest/tpm-crb-swtpm-test.c index 55fdb5657d..ffeb1c396b 100644 --- a/tests/qtest/tpm-crb-swtpm-test.c +++ b/tests/qtest/tpm-crb-swtpm-test.c @@ -13,16 +13,12 @@ */ #include "qemu/osdep.h" -#include #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; char *dst_tpm_path; @@ -62,9 +58,9 @@ int main(int argc, char **argv) tpm_crb_swtpm_migration_test); ret = g_test_run(); - g_rmdir(ts.dst_tpm_path); + tpm_util_rmdir(ts.dst_tpm_path); g_free(ts.dst_tpm_path); - g_rmdir(ts.src_tpm_path); + tpm_util_rmdir(ts.src_tpm_path); g_free(ts.src_tpm_path); g_free(ts.uri); diff --git a/tests/qtest/tpm-crb-test.c b/tests/qtest/tpm-crb-test.c index 7b94453390..396ae3f91c 100644 --- a/tests/qtest/tpm-crb-test.c +++ b/tests/qtest/tpm-crb-test.c @@ -19,9 +19,6 @@ #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/qtest/tpm-emu.c b/tests/qtest/tpm-emu.c index 2994d1cf42..2bf8ff4c86 100644 --- a/tests/qtest/tpm-emu.c +++ b/tests/qtest/tpm-emu.c @@ -36,11 +36,18 @@ void tpm_emu_test_wait_cond(TPMTestState *s) g_mutex_unlock(&s->data_mutex); } +static void tpm_emu_close_ioc(void *ioc) +{ + qio_channel_close(ioc, NULL); +} + static void *tpm_emu_tpm_thread(void *data) { TPMTestState *s = data; QIOChannel *ioc = s->tpm_ioc; + qtest_add_abrt_handler(tpm_emu_close_ioc, ioc); + s->tpm_msg = g_new(struct tpm_hdr, 1); while (true) { int minhlen = sizeof(s->tpm_msg->tag) + sizeof(s->tpm_msg->len); @@ -70,13 +77,14 @@ static void *tpm_emu_tpm_thread(void *data) s->tpm_msg->code = cpu_to_be32(TPM_FAIL); break; default: - g_debug("unsupport TPM version %u", s->tpm_version); + g_debug("unsupported TPM version %u", s->tpm_version); g_assert_not_reached(); } qio_channel_write(ioc, (char *)s->tpm_msg, be32_to_cpu(s->tpm_msg->len), &error_abort); } + qtest_remove_abrt_handler(ioc); g_free(s->tpm_msg); s->tpm_msg = NULL; object_unref(OBJECT(s->tpm_ioc)); @@ -99,6 +107,7 @@ void *tpm_emu_ctrl_thread(void *data) qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); ioc = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); g_assert(ioc); + qtest_add_abrt_handler(tpm_emu_close_ioc, ioc); { uint32_t cmd = 0; @@ -106,7 +115,7 @@ void *tpm_emu_ctrl_thread(void *data) int *pfd = NULL; size_t nfd = 0; - qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, &error_abort); + qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, 0, &error_abort); cmd = be32_to_cpu(cmd); g_assert_cmpint(cmd, ==, CMD_SET_DATAFD); g_assert_cmpint(nfd, ==, 1); @@ -190,6 +199,7 @@ void *tpm_emu_ctrl_thread(void *data) } } + qtest_remove_abrt_handler(ioc); object_unref(OBJECT(ioc)); object_unref(OBJECT(lioc)); return NULL; diff --git a/tests/qtest/tpm-tests.c b/tests/qtest/tpm-tests.c index 25073d1f9e..fb94496bbd 100644 --- a/tests/qtest/tpm-tests.c +++ b/tests/qtest/tpm-tests.c @@ -1,5 +1,5 @@ /* - * QTest TPM commont test code + * QTest TPM common test code * * Copyright (c) 2018 IBM Corporation * Copyright (c) 2018 Red Hat, Inc. diff --git a/tests/qtest/tpm-tests.h b/tests/qtest/tpm-tests.h index a5df35ab5b..07ba60d26e 100644 --- a/tests/qtest/tpm-tests.h +++ b/tests/qtest/tpm-tests.h @@ -1,5 +1,5 @@ /* - * QTest TPM commont test code + * QTest TPM common test code * * Copyright (c) 2018 IBM Corporation * diff --git a/tests/qtest/tpm-tis-device-swtpm-test.c b/tests/qtest/tpm-tis-device-swtpm-test.c index 7b20035142..517a077005 100644 --- a/tests/qtest/tpm-tis-device-swtpm-test.c +++ b/tests/qtest/tpm-tis-device-swtpm-test.c @@ -14,11 +14,11 @@ */ #include "qemu/osdep.h" -#include #include "libqtest.h" #include "qemu/module.h" #include "tpm-tests.h" +#include "tpm-tis-util.h" #include "hw/acpi/tpm.h" uint64_t tpm_tis_base_addr = 0xc000000; @@ -34,7 +34,7 @@ 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_test_swtpm_test(ts->src_tpm_path, tpm_tis_transfer, "tpm-tis-device", MACHINE_OPTIONS); } @@ -43,7 +43,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-device", + tpm_tis_transfer, "tpm-tis-device", MACHINE_OPTIONS); } @@ -66,9 +66,9 @@ int main(int argc, char **argv) tpm_tis_swtpm_migration_test); ret = g_test_run(); - g_rmdir(ts.dst_tpm_path); + tpm_util_rmdir(ts.dst_tpm_path); g_free(ts.dst_tpm_path); - g_rmdir(ts.src_tpm_path); + tpm_util_rmdir(ts.src_tpm_path); g_free(ts.src_tpm_path); g_free(ts.uri); diff --git a/tests/qtest/tpm-tis-i2c-test.c b/tests/qtest/tpm-tis-i2c-test.c new file mode 100644 index 0000000000..3a1af026f2 --- /dev/null +++ b/tests/qtest/tpm-tis-i2c-test.c @@ -0,0 +1,663 @@ +/* + * QTest testcases for TPM TIS on I2C (derived from TPM TIS test) + * + * Copyright (c) 2023 IBM Corporation + * Copyright (c) 2023 Red Hat, Inc. + * + * Authors: + * Stefan Berger + * 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 + +#include "libqtest-single.h" +#include "hw/acpi/tpm.h" +#include "hw/pci/pci_ids.h" +#include "qtest_aspeed.h" +#include "tpm-emu.h" + +#define DEBUG_TIS_TEST 0 + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_TIS_TEST) { \ + printf(fmt, ## __VA_ARGS__); \ + } \ +} while (0) + +#define DPRINTF_ACCESS \ + DPRINTF("%s: %d: locty=%d l=%d access=0x%02x pending_request_flag=0x%x\n", \ + __func__, __LINE__, locty, l, access, pending_request_flag) + +#define DPRINTF_STS \ + DPRINTF("%s: %d: sts = 0x%08x\n", __func__, __LINE__, sts) + +#define I2C_SLAVE_ADDR 0x2e +#define I2C_DEV_BUS_NUM 10 + +static const uint8_t TPM_CMD[12] = + "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"; + +static uint32_t aspeed_bus_addr; + +static uint8_t cur_locty = 0xff; + +static void tpm_tis_i2c_set_locty(uint8_t locty) +{ + if (cur_locty != locty) { + cur_locty = locty; + aspeed_i2c_writeb(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, + TPM_I2C_REG_LOC_SEL, locty); + } +} + +static uint8_t tpm_tis_i2c_readb(uint8_t locty, uint8_t reg) +{ + tpm_tis_i2c_set_locty(locty); + return aspeed_i2c_readb(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg); +} + +static uint16_t tpm_tis_i2c_readw(uint8_t locty, uint8_t reg) +{ + tpm_tis_i2c_set_locty(locty); + return aspeed_i2c_readw(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg); +} + +static uint32_t tpm_tis_i2c_readl(uint8_t locty, uint8_t reg) +{ + tpm_tis_i2c_set_locty(locty); + return aspeed_i2c_readl(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg); +} + +static void tpm_tis_i2c_writeb(uint8_t locty, uint8_t reg, uint8_t v) +{ + if (reg != TPM_I2C_REG_LOC_SEL) { + tpm_tis_i2c_set_locty(locty); + } + aspeed_i2c_writeb(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg, v); +} + +static void tpm_tis_i2c_writel(uint8_t locty, uint8_t reg, uint32_t v) +{ + if (reg != TPM_I2C_REG_LOC_SEL) { + tpm_tis_i2c_set_locty(locty); + } + aspeed_i2c_writel(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg, v); +} + +static void tpm_tis_i2c_test_basic(const void *data) +{ + uint8_t access; + uint32_t v, v2; + + /* + * All register accesses below must work without locality 0 being the + * active locality. Therefore, ensure access is released. + */ + tpm_tis_i2c_writeb(0, TPM_I2C_REG_ACCESS, + TPM_TIS_ACCESS_ACTIVE_LOCALITY); + access = tpm_tis_i2c_readb(0, TPM_I2C_REG_ACCESS); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* read interrupt capability -- none are supported */ + v = tpm_tis_i2c_readl(0, TPM_I2C_REG_INT_CAPABILITY); + g_assert_cmpint(v, ==, 0); + + /* try to enable all interrupts */ + tpm_tis_i2c_writel(0, TPM_I2C_REG_INT_ENABLE, 0xffffffff); + v = tpm_tis_i2c_readl(0, TPM_I2C_REG_INT_ENABLE); + /* none could be enabled */ + g_assert_cmpint(v, ==, 0); + + /* enable csum */ + tpm_tis_i2c_writeb(0, TPM_I2C_REG_DATA_CSUM_ENABLE, TPM_DATA_CSUM_ENABLED); + /* check csum enable register has bit 0 set */ + v = tpm_tis_i2c_readb(0, TPM_I2C_REG_DATA_CSUM_ENABLE); + g_assert_cmpint(v, ==, TPM_DATA_CSUM_ENABLED); + /* reading it as 32bit register returns same result */ + v = tpm_tis_i2c_readl(0, TPM_I2C_REG_DATA_CSUM_ENABLE); + g_assert_cmpint(v, ==, TPM_DATA_CSUM_ENABLED); + + /* disable csum */ + tpm_tis_i2c_writeb(0, TPM_I2C_REG_DATA_CSUM_ENABLE, 0); + /* check csum enable register has bit 0 clear */ + v = tpm_tis_i2c_readb(0, TPM_I2C_REG_DATA_CSUM_ENABLE); + g_assert_cmpint(v, ==, 0); + + /* write to unsupported register '1' */ + tpm_tis_i2c_writel(0, 1, 0x12345678); + v = tpm_tis_i2c_readl(0, 1); + g_assert_cmpint(v, ==, 0xffffffff); + + /* request use of locality */ + tpm_tis_i2c_writeb(0, TPM_I2C_REG_ACCESS, TPM_TIS_ACCESS_REQUEST_USE); + + /* read byte from STS + 3 */ + v = tpm_tis_i2c_readb(0, TPM_I2C_REG_STS + 3); + g_assert_cmpint(v, ==, 0); + + /* check STS after writing to STS + 3 */ + v = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS); + tpm_tis_i2c_writeb(0, TPM_I2C_REG_STS + 3, 0xf); + v2 = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS); + g_assert_cmpint(v, ==, v2); + + /* release access */ + tpm_tis_i2c_writeb(0, TPM_I2C_REG_ACCESS, + TPM_TIS_ACCESS_ACTIVE_LOCALITY); + + /* select locality 5 -- must not be possible */ + tpm_tis_i2c_writeb(0, TPM_I2C_REG_LOC_SEL, 5); + v = tpm_tis_i2c_readb(0, TPM_I2C_REG_LOC_SEL); + g_assert_cmpint(v, ==, 0); +} + +static void tpm_tis_i2c_test_check_localities(const void *data) +{ + uint8_t locty, l; + uint8_t access; + uint32_t capability, i2c_cap; + uint32_t didvid; + uint32_t rid; + + for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES; locty++) { + access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + capability = tpm_tis_i2c_readl(locty, TPM_I2C_REG_INTF_CAPABILITY); + i2c_cap = (TPM_I2C_CAP_INTERFACE_TYPE | + TPM_I2C_CAP_INTERFACE_VER | + TPM_I2C_CAP_TPM2_FAMILY | + TPM_I2C_CAP_LOCALITY_CAP | + TPM_I2C_CAP_BUS_SPEED | + TPM_I2C_CAP_DEV_ADDR_CHANGE); + g_assert_cmpint(capability, ==, i2c_cap); + + didvid = tpm_tis_i2c_readl(locty, TPM_I2C_REG_DID_VID); + g_assert_cmpint(didvid, ==, (1 << 16) | PCI_VENDOR_ID_IBM); + + rid = tpm_tis_i2c_readl(locty, TPM_I2C_REG_RID); + g_assert_cmpint(rid, !=, 0); + g_assert_cmpint(rid, !=, 0xffffffff); + + /* locality selection must be at locty */ + l = tpm_tis_i2c_readb(locty, TPM_I2C_REG_LOC_SEL); + g_assert_cmpint(l, ==, locty); + } +} + +static void tpm_tis_i2c_test_check_access_reg(const void *data) +{ + uint8_t locty; + uint8_t access; + + /* do not test locality 4 (hw only) */ + for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES - 1; locty++) { + access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* request use of locality */ + tpm_tis_i2c_writeb(locty, TPM_I2C_REG_ACCESS, + TPM_TIS_ACCESS_REQUEST_USE); + + access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* release access */ + tpm_tis_i2c_writeb(locty, TPM_I2C_REG_ACCESS, + TPM_TIS_ACCESS_ACTIVE_LOCALITY); + access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + } +} + +/* + * Test case for seizing access by a higher number locality + */ +static void tpm_tis_i2c_test_check_access_reg_seize(const void *data) +{ + int locty, l; + uint8_t access; + uint8_t pending_request_flag; + + /* do not test locality 4 (hw only) */ + for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES - 1; locty++) { + pending_request_flag = 0; + + access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* request use of locality */ + tpm_tis_i2c_writeb(locty, + TPM_I2C_REG_ACCESS, TPM_TIS_ACCESS_REQUEST_USE); + access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* lower localities cannot seize access */ + for (l = 0; l < locty; l++) { + /* lower locality is not active */ + access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* try to request use from 'l' */ + tpm_tis_i2c_writeb(l, + TPM_I2C_REG_ACCESS, + TPM_TIS_ACCESS_REQUEST_USE); + + /* + * requesting use from 'l' was not possible; + * we must see REQUEST_USE and possibly PENDING_REQUEST + */ + access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_REQUEST_USE | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* + * locality 'locty' must be unchanged; + * we must see PENDING_REQUEST + */ + access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + TPM_TIS_ACCESS_PENDING_REQUEST | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* try to seize from 'l' */ + tpm_tis_i2c_writeb(l, + TPM_I2C_REG_ACCESS, TPM_TIS_ACCESS_SEIZE); + /* seize from 'l' was not possible */ + access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_REQUEST_USE | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* locality 'locty' must be unchanged */ + access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + 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' + */ + 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. + */ + for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES - 1; l++) { + /* try to 'request use' from 'l' */ + tpm_tis_i2c_writeb(l, TPM_I2C_REG_ACCESS, + TPM_TIS_ACCESS_REQUEST_USE); + + /* + * requesting use from 'l' was not possible; we should see + * REQUEST_USE and may see PENDING_REQUEST + */ + access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_REQUEST_USE | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* + * locality 'l-1' must be unchanged; we should always + * see PENDING_REQUEST from 'l' requesting access + */ + access = tpm_tis_i2c_readb(l - 1, TPM_I2C_REG_ACCESS); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + TPM_TIS_ACCESS_PENDING_REQUEST | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* try to seize from 'l' */ + tpm_tis_i2c_writeb(l, TPM_I2C_REG_ACCESS, TPM_TIS_ACCESS_SEIZE); + + /* seize from 'l' was possible */ + access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* l - 1 should show that it has BEEN_SEIZED */ + access = tpm_tis_i2c_readb(l - 1, TPM_I2C_REG_ACCESS); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_BEEN_SEIZED | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* clear the BEEN_SEIZED flag and make sure it's gone */ + tpm_tis_i2c_writeb(l - 1, TPM_I2C_REG_ACCESS, + TPM_TIS_ACCESS_BEEN_SEIZED); + + access = tpm_tis_i2c_readb(l - 1, TPM_I2C_REG_ACCESS); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + pending_request_flag | + 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 + */ + if (locty <= 1) { + pending_request_flag = 0; + } + + /* release access from l - 1; this activates locty - 1 */ + l--; + + access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS); + DPRINTF_ACCESS; + + DPRINTF("%s: %d: relinquishing control on l = %d\n", + __func__, __LINE__, l); + tpm_tis_i2c_writeb(l, TPM_I2C_REG_ACCESS, + TPM_TIS_ACCESS_ACTIVE_LOCALITY); + + access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + for (l = locty - 1; l >= 0; l--) { + access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* release this locality */ + tpm_tis_i2c_writeb(l, TPM_I2C_REG_ACCESS, + TPM_TIS_ACCESS_ACTIVE_LOCALITY); + + if (l == 1) { + pending_request_flag = 0; + } + } + + /* no locality may be active now */ + for (l = 0; l < TPM_TIS_NUM_LOCALITIES - 1; l++) { + access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + } + } +} + +/* + * Test case for getting access when higher number locality relinquishes access + */ +static void tpm_tis_i2c_test_check_access_reg_release(const void *data) +{ + int locty, l; + uint8_t access; + uint8_t pending_request_flag; + + /* do not test locality 4 (hw only) */ + for (locty = TPM_TIS_NUM_LOCALITIES - 2; locty >= 0; locty--) { + pending_request_flag = 0; + + access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* request use of locality */ + tpm_tis_i2c_writeb(locty, TPM_I2C_REG_ACCESS, + TPM_TIS_ACCESS_REQUEST_USE); + access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + /* request use of all other localities */ + for (l = 0; l < TPM_TIS_NUM_LOCALITIES - 1; l++) { + if (l == locty) { + continue; + } + /* + * request use of locality 'l' -- we MUST see REQUEST USE and + * may see PENDING_REQUEST + */ + tpm_tis_i2c_writeb(l, TPM_I2C_REG_ACCESS, + TPM_TIS_ACCESS_REQUEST_USE); + access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_REQUEST_USE | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + pending_request_flag = TPM_TIS_ACCESS_PENDING_REQUEST; + } + /* release locality 'locty' */ + tpm_tis_i2c_writeb(locty, TPM_I2C_REG_ACCESS, + TPM_TIS_ACCESS_ACTIVE_LOCALITY); + /* + * highest locality should now be active; release it and make sure the + * next highest locality is active afterwards + */ + for (l = TPM_TIS_NUM_LOCALITIES - 2; l >= 0; l--) { + if (l == locty) { + continue; + } + /* 'l' should be active now */ + access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS); + DPRINTF_ACCESS; + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + /* 'l' relinquishes access */ + tpm_tis_i2c_writeb(l, TPM_I2C_REG_ACCESS, + TPM_TIS_ACCESS_ACTIVE_LOCALITY); + access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS); + DPRINTF_ACCESS; + if (l == 1 || (locty <= 1 && l == 2)) { + pending_request_flag = 0; + } + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + pending_request_flag | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + } + } +} + +/* + * Test case for transmitting packets + */ +static void tpm_tis_i2c_test_check_transmit(const void *data) +{ + const TPMTestState *s = data; + uint8_t access; + uint32_t sts, v; + uint16_t bcount, csum, bcount2; + size_t i; + + /* enable csum */ + tpm_tis_i2c_writeb(0, TPM_I2C_REG_DATA_CSUM_ENABLE, TPM_DATA_CSUM_ENABLED); + /* check csum enable register has bit 0 set */ + v = tpm_tis_i2c_readb(0, TPM_I2C_REG_DATA_CSUM_ENABLE); + g_assert_cmpint(v, ==, TPM_DATA_CSUM_ENABLED); + /* reading it as 32bit register returns same result */ + v = tpm_tis_i2c_readl(0, TPM_I2C_REG_DATA_CSUM_ENABLE); + g_assert_cmpint(v, ==, TPM_DATA_CSUM_ENABLED); + + /* request use of locality 0 */ + tpm_tis_i2c_writeb(0, TPM_I2C_REG_ACCESS, TPM_TIS_ACCESS_REQUEST_USE); + access = tpm_tis_i2c_readb(0, TPM_I2C_REG_ACCESS); + g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | + TPM_TIS_ACCESS_ACTIVE_LOCALITY | + TPM_TIS_ACCESS_TPM_ESTABLISHMENT); + + sts = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS); + DPRINTF_STS; + + g_assert_cmpint(sts & 0xff, ==, 0); + + bcount = (sts >> 8) & 0xffff; + g_assert_cmpint(bcount, >=, 128); + + /* read bcount from STS + 1 must work also */ + bcount2 = tpm_tis_i2c_readw(0, TPM_I2C_REG_STS + 1); + g_assert_cmpint(bcount, ==, bcount2); + + /* ic2 must have bits 26-31 zero */ + g_assert_cmpint(sts & (0x1f << 26), ==, 0); + + tpm_tis_i2c_writel(0, TPM_I2C_REG_STS, TPM_TIS_STS_COMMAND_READY); + sts = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS); + DPRINTF_STS; + g_assert_cmpint(sts & 0xff, ==, TPM_TIS_STS_COMMAND_READY); + + /* transmit command */ + for (i = 0; i < sizeof(TPM_CMD); i++) { + tpm_tis_i2c_writeb(0, TPM_I2C_REG_DATA_FIFO, TPM_CMD[i]); + sts = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS); + DPRINTF_STS; + if (i < sizeof(TPM_CMD) - 1) { + g_assert_cmpint(sts & 0xff, ==, + TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); + } else { + g_assert_cmpint(sts & 0xff, ==, TPM_TIS_STS_VALID); + } + g_assert_cmpint((sts >> 8) & 0xffff, ==, --bcount); + } + /* read the checksum */ + csum = tpm_tis_i2c_readw(0, TPM_I2C_REG_DATA_CSUM_GET); + g_assert_cmpint(csum, ==, 0x6733); + + /* start processing */ + tpm_tis_i2c_writeb(0, TPM_I2C_REG_STS, TPM_TIS_STS_TPM_GO); + + uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND; + do { + sts = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS); + if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) { + break; + } + } while (g_get_monotonic_time() < end_time); + + sts = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS); + DPRINTF_STS; + g_assert_cmpint(sts & 0xff, == , + TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); + bcount = (sts >> 8) & 0xffff; + + /* read response */ + uint8_t tpm_msg[sizeof(struct tpm_hdr)]; + g_assert_cmpint(sizeof(tpm_msg), ==, bcount); + + for (i = 0; i < sizeof(tpm_msg); i++) { + tpm_msg[i] = tpm_tis_i2c_readb(0, TPM_I2C_REG_DATA_FIFO); + sts = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS); + DPRINTF_STS; + if (sts & TPM_TIS_STS_DATA_AVAILABLE) { + g_assert_cmpint((sts >> 8) & 0xffff, ==, --bcount); + } + } + g_assert_cmpmem(tpm_msg, sizeof(tpm_msg), s->tpm_msg, sizeof(*s->tpm_msg)); + + /* relinquish use of locality 0 */ + tpm_tis_i2c_writeb(0, + TPM_I2C_REG_ACCESS, TPM_TIS_ACCESS_ACTIVE_LOCALITY); + access = tpm_tis_i2c_readb(0, TPM_I2C_REG_ACCESS); +} + +int main(int argc, char **argv) +{ + int ret; + char *args; + char *tmp_path = g_dir_make_tmp("qemu-tpm-tis-i2c-test.XXXXXX", NULL); + GThread *thread; + TPMTestState 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; + test.tpm_version = TPM_VERSION_2_0; + + thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test); + tpm_emu_test_wait_cond(&test); + + aspeed_bus_addr = ast2600_i2c_calc_bus_addr(I2C_DEV_BUS_NUM); + + args = g_strdup_printf( + "-machine rainier-bmc -accel tcg " + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=tpm0,chardev=chr " + "-device tpm-tis-i2c,tpmdev=tpm0,bus=aspeed.i2c.bus.%d,address=0x%x", + test.addr->u.q_unix.path, + I2C_DEV_BUS_NUM, + I2C_SLAVE_ADDR); + qtest_start(args); + + qtest_add_data_func("/tpm-tis-i2c/test_basic", &test, + tpm_tis_i2c_test_basic); + + qtest_add_data_func("/tpm-tis-i2c/test_check_localities", &test, + tpm_tis_i2c_test_check_localities); + + qtest_add_data_func("/tpm-tis-i2c/check_access_reg", &test, + tpm_tis_i2c_test_check_access_reg); + + qtest_add_data_func("/tpm-tis-i2c/check_access_reg_seize", &test, + tpm_tis_i2c_test_check_access_reg_seize); + + qtest_add_data_func("/tpm-tis-i2c/check_access_reg_release", &test, + tpm_tis_i2c_test_check_access_reg_release); + + qtest_add_data_func("/tpm-tis-i2c/test_check_transmit", &test, + tpm_tis_i2c_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-swtpm-test.c b/tests/qtest/tpm-tis-swtpm-test.c index 90131cb3c4..105e42e21d 100644 --- a/tests/qtest/tpm-tis-swtpm-test.c +++ b/tests/qtest/tpm-tis-swtpm-test.c @@ -13,11 +13,11 @@ */ #include "qemu/osdep.h" -#include #include "libqtest.h" #include "qemu/module.h" #include "tpm-tests.h" +#include "tpm-tis-util.h" #include "hw/acpi/tpm.h" uint64_t tpm_tis_base_addr = TPM_TIS_ADDR_BASE; @@ -32,7 +32,7 @@ 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_test_swtpm_test(ts->src_tpm_path, tpm_tis_transfer, "tpm-tis", NULL); } @@ -41,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", NULL); + tpm_tis_transfer, "tpm-tis", NULL); } int main(int argc, char **argv) @@ -61,9 +61,9 @@ int main(int argc, char **argv) tpm_tis_swtpm_migration_test); ret = g_test_run(); - g_rmdir(ts.dst_tpm_path); + tpm_util_rmdir(ts.dst_tpm_path); g_free(ts.dst_tpm_path); - g_rmdir(ts.src_tpm_path); + tpm_util_rmdir(ts.src_tpm_path); g_free(ts.src_tpm_path); g_free(ts.uri); diff --git a/tests/qtest/tpm-tis-util.c b/tests/qtest/tpm-tis-util.c index 939893bf01..862bb53248 100644 --- a/tests/qtest/tpm-tis-util.c +++ b/tests/qtest/tpm-tis-util.c @@ -52,7 +52,7 @@ void tpm_tis_test_check_localities(const void *data) uint32_t rid; for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES; locty++) { - access = readb(TIS_REG(0, TPM_TIS_REG_ACCESS)); + access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | TPM_TIS_ACCESS_TPM_ESTABLISHMENT); @@ -340,7 +340,7 @@ void tpm_tis_test_check_access_reg_release(const void *data) TPM_TIS_ACCESS_ACTIVE_LOCALITY); /* * highest locality should now be active; release it and make sure the - * next higest locality is active afterwards + * next highest locality is active afterwards */ for (l = TPM_TIS_NUM_LOCALITIES - 2; l >= 0; l--) { if (l == locty) { @@ -449,3 +449,48 @@ 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)); } + +void tpm_tis_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size) +{ + uint32_t sts; + uint16_t bcount; + size_t i; + + /* request use of locality 0 */ + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); + qtest_writel(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_COMMAND_READY); + + sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); + bcount = (sts >> 8) & 0xffff; + g_assert_cmpint(bcount, >=, req_size); + + /* transmit command */ + for (i = 0; i < req_size; i++) { + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO), req[i]); + } + + /* start processing */ + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_TPM_GO); + + uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND; + do { + sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); + if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) { + break; + } + } while (g_get_monotonic_time() < end_time); + + sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); + bcount = (sts >> 8) & 0xffff; + + memset(rsp, 0, rsp_size); + for (i = 0; i < bcount; i++) { + rsp[i] = qtest_readb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO)); + } + + /* relinquish use of locality 0 */ + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), + TPM_TIS_ACCESS_ACTIVE_LOCALITY); +} diff --git a/tests/qtest/tpm-tis-util.h b/tests/qtest/tpm-tis-util.h index d10efe86ae..03910a7ba7 100644 --- a/tests/qtest/tpm-tis-util.h +++ b/tests/qtest/tpm-tis-util.h @@ -20,4 +20,8 @@ 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); +void tpm_tis_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size); + #endif /* TESTS_TPM_TIS_UTIL_H */ diff --git a/tests/qtest/tpm-util.c b/tests/qtest/tpm-util.c index e0dc5da0af..1c0319e6e7 100644 --- a/tests/qtest/tpm-util.c +++ b/tests/qtest/tpm-util.c @@ -13,6 +13,7 @@ */ #include "qemu/osdep.h" +#include #include "hw/acpi/tpm.h" #include "libqtest.h" @@ -50,51 +51,6 @@ void tpm_util_crb_transfer(QTestState *s, qtest_memread(s, raddr, rsp, rsp_size); } -void tpm_util_tis_transfer(QTestState *s, - const unsigned char *req, size_t req_size, - unsigned char *rsp, size_t rsp_size) -{ - uint32_t sts; - uint16_t bcount; - size_t i; - - /* request use of locality 0 */ - qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); - qtest_writel(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_COMMAND_READY); - - sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); - bcount = (sts >> 8) & 0xffff; - g_assert_cmpint(bcount, >=, req_size); - - /* transmit command */ - for (i = 0; i < req_size; i++) { - qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO), req[i]); - } - - /* start processing */ - qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_TPM_GO); - - uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND; - do { - sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); - if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) { - break; - } - } while (g_get_monotonic_time() < end_time); - - sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); - bcount = (sts >> 8) & 0xffff; - - memset(rsp, 0, rsp_size); - for (i = 0; i < bcount; i++) { - rsp[i] = qtest_readb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO)); - } - - /* relinquish use of locality 0 */ - qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), - TPM_TIS_ACCESS_ACTIVE_LOCALITY); -} - void tpm_util_startup(QTestState *s, tx_func *tx) { unsigned char buffer[1024]; @@ -292,3 +248,21 @@ void tpm_util_migration_start_qemu(QTestState **src_qemu, g_free(src_qemu_args); g_free(dst_qemu_args); } + +/* Remove directory with remainders of swtpm */ +void tpm_util_rmdir(const char *path) +{ + char *filename; + int ret; + + filename = g_strdup_printf("%s/tpm2-00.permall", path); + g_unlink(filename); + g_free(filename); + + filename = g_strdup_printf("%s/.lock", path); + g_unlink(filename); + g_free(filename); + + ret = g_rmdir(path); + g_assert(!ret); +} diff --git a/tests/qtest/tpm-util.h b/tests/qtest/tpm-util.h index 3b97d69017..0cb28dd6e5 100644 --- a/tests/qtest/tpm-util.h +++ b/tests/qtest/tpm-util.h @@ -27,9 +27,6 @@ typedef void (tx_func)(QTestState *s, void tpm_util_crb_transfer(QTestState *s, const unsigned char *req, size_t req_size, unsigned char *rsp, size_t rsp_size); -void tpm_util_tis_transfer(QTestState *s, - const unsigned char *req, size_t req_size, - unsigned char *rsp, size_t rsp_size); void tpm_util_startup(QTestState *s, tx_func *tx); void tpm_util_pcrextend(QTestState *s, tx_func *tx); @@ -53,5 +50,6 @@ void tpm_util_migration_start_qemu(QTestState **src_qemu, const char *machine_options); void tpm_util_wait_for_migration_complete(QTestState *who); +void tpm_util_rmdir(const char *path); #endif /* TESTS_TPM_UTIL_H */ diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c new file mode 100644 index 0000000000..95e82f9472 --- /dev/null +++ b/tests/qtest/ufs-test.c @@ -0,0 +1,606 @@ +/* + * QTest testcase for UFS + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" +#include "scsi/constants.h" +#include "include/block/ufs.h" + +/* Test images sizes in Bytes */ +#define TEST_IMAGE_SIZE (64 * 1024 * 1024) +/* Timeout for various operations, in seconds. */ +#define TIMEOUT_SECONDS 10 +/* Maximum PRD entry count */ +#define MAX_PRD_ENTRY_COUNT 10 +#define PRD_ENTRY_DATA_SIZE 4096 +/* Constants to build upiu */ +#define UTP_COMMAND_DESCRIPTOR_SIZE 4096 +#define UTP_RESPONSE_UPIU_OFFSET 1024 +#define UTP_PRDT_UPIU_OFFSET 2048 + +typedef struct QUfs QUfs; + +struct QUfs { + QOSGraphObject obj; + QPCIDevice dev; + QPCIBar bar; + + uint64_t utrlba; + uint64_t utmrlba; + uint64_t cmd_desc_addr; + uint64_t data_buffer_addr; + + bool enabled; +}; + +static inline uint32_t ufs_rreg(QUfs *ufs, size_t offset) +{ + return qpci_io_readl(&ufs->dev, ufs->bar, offset); +} + +static inline void ufs_wreg(QUfs *ufs, size_t offset, uint32_t value) +{ + qpci_io_writel(&ufs->dev, ufs->bar, offset, value); +} + +static void ufs_wait_for_irq(QUfs *ufs) +{ + uint64_t end_time; + uint32_t is; + /* Wait for device to reset as the linux driver does. */ + end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; + do { + qtest_clock_step(ufs->dev.bus->qts, 100); + is = ufs_rreg(ufs, A_IS); + } while (is == 0 && g_get_monotonic_time() < end_time); +} + +static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr, + uint8_t slot, + uint32_t data_direction, + uint16_t prd_table_length) +{ + UtpTransferReqDesc req = { 0 }; + uint64_t command_desc_base_addr = + cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + + req.header.dword_0 = + cpu_to_le32(1 << 28 | data_direction | UFS_UTP_REQ_DESC_INT_CMD); + req.header.dword_2 = cpu_to_le32(UFS_OCS_INVALID_COMMAND_STATUS); + + req.command_desc_base_addr_hi = cpu_to_le32(command_desc_base_addr >> 32); + req.command_desc_base_addr_lo = + cpu_to_le32(command_desc_base_addr & 0xffffffff); + req.response_upiu_offset = + cpu_to_le16(UTP_RESPONSE_UPIU_OFFSET / sizeof(uint32_t)); + req.response_upiu_length = cpu_to_le16(sizeof(UtpUpiuRsp)); + req.prd_table_offset = cpu_to_le16(UTP_PRDT_UPIU_OFFSET / sizeof(uint32_t)); + req.prd_table_length = cpu_to_le16(prd_table_length); + return req; +} + +static void ufs_send_nop_out(QUfs *ufs, uint8_t slot, + UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) +{ + /* Build up utp transfer request descriptor */ + UtpTransferReqDesc utrd = ufs_build_req_utrd(ufs->cmd_desc_addr, slot, + UFS_UTP_NO_DATA_TRANSFER, 0); + uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); + uint64_t req_upiu_addr = + ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; + qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); + + /* Build up request upiu */ + UtpUpiuReq req_upiu = { 0 }; + req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_NOP_OUT; + req_upiu.header.task_tag = slot; + qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, + sizeof(req_upiu)); + + /* Ring Doorbell */ + ufs_wreg(ufs, A_UTRLDBR, 1); + ufs_wait_for_irq(ufs); + g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); + ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); + + qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); + qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); +} + +static void ufs_send_query(QUfs *ufs, uint8_t slot, uint8_t query_function, + uint8_t query_opcode, uint8_t idn, uint8_t index, + UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) +{ + /* Build up utp transfer request descriptor */ + UtpTransferReqDesc utrd = ufs_build_req_utrd(ufs->cmd_desc_addr, slot, + UFS_UTP_NO_DATA_TRANSFER, 0); + uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); + uint64_t req_upiu_addr = + ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; + qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); + + /* Build up request upiu */ + UtpUpiuReq req_upiu = { 0 }; + req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_QUERY_REQ; + req_upiu.header.query_func = query_function; + req_upiu.header.task_tag = slot; + /* + * QEMU UFS does not currently support Write descriptor and Write attribute, + * so the value of data_segment_length is always 0. + */ + req_upiu.header.data_segment_length = 0; + req_upiu.qr.opcode = query_opcode; + req_upiu.qr.idn = idn; + req_upiu.qr.index = index; + qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, + sizeof(req_upiu)); + + /* Ring Doorbell */ + ufs_wreg(ufs, A_UTRLDBR, 1); + ufs_wait_for_irq(ufs); + g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); + ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); + + qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); + qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); +} + +static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun, + const uint8_t *cdb, const uint8_t *data_in, + size_t data_in_len, uint8_t *data_out, + size_t data_out_len, + UtpTransferReqDesc *utrd_out, + UtpUpiuRsp *rsp_out) + +{ + /* Build up PRDT */ + UfshcdSgEntry entries[MAX_PRD_ENTRY_COUNT] = { + 0, + }; + uint8_t flags; + uint16_t prd_table_length, i; + uint32_t data_direction, data_len; + uint64_t req_upiu_addr = + ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + uint64_t prdt_addr = req_upiu_addr + UTP_PRDT_UPIU_OFFSET; + + g_assert_true(data_in_len < MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); + g_assert_true(data_out_len < MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); + if (data_in_len > 0) { + g_assert_nonnull(data_in); + data_direction = UFS_UTP_HOST_TO_DEVICE; + data_len = data_in_len; + flags = UFS_UPIU_CMD_FLAGS_WRITE; + } else if (data_out_len > 0) { + g_assert_nonnull(data_out); + data_direction = UFS_UTP_DEVICE_TO_HOST; + data_len = data_out_len; + flags = UFS_UPIU_CMD_FLAGS_READ; + } else { + data_direction = UFS_UTP_NO_DATA_TRANSFER; + data_len = 0; + flags = UFS_UPIU_CMD_FLAGS_NONE; + } + prd_table_length = DIV_ROUND_UP(data_len, PRD_ENTRY_DATA_SIZE); + + qtest_memset(ufs->dev.bus->qts, ufs->data_buffer_addr, 0, + MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); + if (data_in_len) { + qtest_memwrite(ufs->dev.bus->qts, ufs->data_buffer_addr, data_in, + data_in_len); + } + + for (i = 0; i < prd_table_length; i++) { + entries[i].addr = + cpu_to_le64(ufs->data_buffer_addr + i * sizeof(UfshcdSgEntry)); + if (i + 1 != prd_table_length) { + entries[i].size = cpu_to_le32(PRD_ENTRY_DATA_SIZE - 1); + } else { + entries[i].size = cpu_to_le32( + data_len - (PRD_ENTRY_DATA_SIZE * (prd_table_length - 1)) - 1); + } + } + qtest_memwrite(ufs->dev.bus->qts, prdt_addr, entries, + prd_table_length * sizeof(UfshcdSgEntry)); + + /* Build up utp transfer request descriptor */ + UtpTransferReqDesc utrd = ufs_build_req_utrd( + ufs->cmd_desc_addr, slot, data_direction, prd_table_length); + uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); + uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; + qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); + + /* Build up request upiu */ + UtpUpiuReq req_upiu = { 0 }; + req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_COMMAND; + req_upiu.header.flags = flags; + req_upiu.header.lun = lun; + req_upiu.header.task_tag = slot; + req_upiu.sc.exp_data_transfer_len = cpu_to_be32(data_len); + memcpy(req_upiu.sc.cdb, cdb, UFS_CDB_SIZE); + qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, + sizeof(req_upiu)); + + /* Ring Doorbell */ + ufs_wreg(ufs, A_UTRLDBR, 1); + ufs_wait_for_irq(ufs); + g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); + ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); + + qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); + qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); + if (data_out_len) { + qtest_memread(ufs->dev.bus->qts, ufs->data_buffer_addr, data_out, + data_out_len); + } +} + +/** + * Initialize Ufs host controller and logical unit. + * After running this function, you can make a transfer request to the UFS. + */ +static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) +{ + uint64_t end_time; + uint32_t nutrs, nutmrs; + uint32_t hcs, is, ucmdarg2, cap; + uint32_t hce = 0, ie = 0; + UtpTransferReqDesc utrd; + UtpUpiuRsp rsp_upiu; + + ufs->bar = qpci_iomap(&ufs->dev, 0, NULL); + qpci_device_enable(&ufs->dev); + + /* Start host controller initialization */ + hce = FIELD_DP32(hce, HCE, HCE, 1); + ufs_wreg(ufs, A_HCE, hce); + + /* Wait for device to reset */ + end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; + do { + qtest_clock_step(ufs->dev.bus->qts, 100); + hce = FIELD_EX32(ufs_rreg(ufs, A_HCE), HCE, HCE); + } while (hce == 0 && g_get_monotonic_time() < end_time); + g_assert_cmpuint(hce, ==, 1); + + /* Enable interrupt */ + ie = FIELD_DP32(ie, IE, UCCE, 1); + ie = FIELD_DP32(ie, IE, UHESE, 1); + ie = FIELD_DP32(ie, IE, UHXSE, 1); + ie = FIELD_DP32(ie, IE, UPMSE, 1); + ufs_wreg(ufs, A_IE, ie); + + /* Send DME_LINK_STARTUP uic command */ + hcs = ufs_rreg(ufs, A_HCS); + g_assert_true(FIELD_EX32(hcs, HCS, UCRDY)); + + ufs_wreg(ufs, A_UCMDARG1, 0); + ufs_wreg(ufs, A_UCMDARG2, 0); + ufs_wreg(ufs, A_UCMDARG3, 0); + ufs_wreg(ufs, A_UICCMD, UFS_UIC_CMD_DME_LINK_STARTUP); + + is = ufs_rreg(ufs, A_IS); + g_assert_true(FIELD_EX32(is, IS, UCCS)); + ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UCCS, 1)); + + ucmdarg2 = ufs_rreg(ufs, A_UCMDARG2); + g_assert_cmpuint(ucmdarg2, ==, 0); + is = ufs_rreg(ufs, A_IS); + g_assert_cmpuint(is, ==, 0); + hcs = ufs_rreg(ufs, A_HCS); + g_assert_true(FIELD_EX32(hcs, HCS, DP)); + g_assert_true(FIELD_EX32(hcs, HCS, UTRLRDY)); + g_assert_true(FIELD_EX32(hcs, HCS, UTMRLRDY)); + g_assert_true(FIELD_EX32(hcs, HCS, UCRDY)); + + /* Enable all interrupt functions */ + ie = FIELD_DP32(ie, IE, UTRCE, 1); + ie = FIELD_DP32(ie, IE, UEE, 1); + ie = FIELD_DP32(ie, IE, UPMSE, 1); + ie = FIELD_DP32(ie, IE, UHXSE, 1); + ie = FIELD_DP32(ie, IE, UHESE, 1); + ie = FIELD_DP32(ie, IE, UTMRCE, 1); + ie = FIELD_DP32(ie, IE, UCCE, 1); + ie = FIELD_DP32(ie, IE, DFEE, 1); + ie = FIELD_DP32(ie, IE, HCFEE, 1); + ie = FIELD_DP32(ie, IE, SBFEE, 1); + ie = FIELD_DP32(ie, IE, CEFEE, 1); + ufs_wreg(ufs, A_IE, ie); + ufs_wreg(ufs, A_UTRIACR, 0); + + /* Enable transfer request and task management request */ + cap = ufs_rreg(ufs, A_CAP); + nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1; + nutmrs = FIELD_EX32(cap, CAP, NUTMRS) + 1; + ufs->cmd_desc_addr = + guest_alloc(alloc, nutrs * UTP_COMMAND_DESCRIPTOR_SIZE); + ufs->data_buffer_addr = + guest_alloc(alloc, MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); + ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc)); + ufs->utmrlba = guest_alloc(alloc, nutmrs * sizeof(UtpTaskReqDesc)); + + ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff); + ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32); + ufs_wreg(ufs, A_UTMRLBA, ufs->utmrlba & 0xffffffff); + ufs_wreg(ufs, A_UTMRLBAU, ufs->utmrlba >> 32); + ufs_wreg(ufs, A_UTRLRSR, 1); + ufs_wreg(ufs, A_UTMRLRSR, 1); + + /* Send nop out to test transfer request */ + ufs_send_nop_out(ufs, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + + /* Set fDeviceInit flag via query request */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_SET_FLAG, + UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + + /* Wait for device to reset */ + end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; + do { + qtest_clock_step(ufs->dev.bus->qts, 100); + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, &utrd, &rsp_upiu); + } while (be32_to_cpu(rsp_upiu.qr.value) != 0 && + g_get_monotonic_time() < end_time); + g_assert_cmpuint(be32_to_cpu(rsp_upiu.qr.value), ==, 0); + + ufs->enabled = true; +} + +static void ufs_exit(QUfs *ufs, QGuestAllocator *alloc) +{ + if (ufs->enabled) { + guest_free(alloc, ufs->utrlba); + guest_free(alloc, ufs->utmrlba); + guest_free(alloc, ufs->cmd_desc_addr); + guest_free(alloc, ufs->data_buffer_addr); + } + + qpci_iounmap(&ufs->dev, ufs->bar); +} + +static void *ufs_get_driver(void *obj, const char *interface) +{ + QUfs *ufs = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &ufs->dev; + } + + fprintf(stderr, "%s not present in ufs\n", interface); + g_assert_not_reached(); +} + +static void *ufs_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QUfs *ufs = g_new0(QUfs, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&ufs->dev, bus, addr); + ufs->obj.get_driver = ufs_get_driver; + + return &ufs->obj; +} + +static void ufstest_reg_read(void *obj, void *data, QGuestAllocator *alloc) +{ + QUfs *ufs = obj; + uint32_t cap; + + ufs->bar = qpci_iomap(&ufs->dev, 0, NULL); + qpci_device_enable(&ufs->dev); + + cap = ufs_rreg(ufs, A_CAP); + g_assert_cmpuint(FIELD_EX32(cap, CAP, NUTRS), ==, 31); + g_assert_cmpuint(FIELD_EX32(cap, CAP, NUTMRS), ==, 7); + g_assert_cmpuint(FIELD_EX32(cap, CAP, 64AS), ==, 1); + + qpci_iounmap(&ufs->dev, ufs->bar); +} + +static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc) +{ + QUfs *ufs = obj; + + uint8_t buf[4096] = { 0 }; + const uint8_t report_luns_cdb[UFS_CDB_SIZE] = { + /* allocation length 4096 */ + REPORT_LUNS, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 + }; + const uint8_t test_unit_ready_cdb[UFS_CDB_SIZE] = { + TEST_UNIT_READY, + }; + const uint8_t request_sense_cdb[UFS_CDB_SIZE] = { + REQUEST_SENSE, + }; + UtpTransferReqDesc utrd; + UtpUpiuRsp rsp_upiu; + + ufs_init(ufs, alloc); + + /* Check REPORT_LUNS */ + ufs_send_scsi_command(ufs, 0, 0, report_luns_cdb, NULL, 0, buf, sizeof(buf), + &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + /* LUN LIST LENGTH should be 8, in big endian */ + g_assert_cmpuint(buf[3], ==, 8); + /* There is one logical unit whose lun is 0 */ + g_assert_cmpuint(buf[9], ==, 0); + + /* Clear Unit Attention */ + ufs_send_scsi_command(ufs, 0, 0, request_sense_cdb, NULL, 0, buf, + sizeof(buf), &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION); + + /* Check TEST_UNIT_READY */ + ufs_send_scsi_command(ufs, 0, 0, test_unit_ready_cdb, NULL, 0, NULL, 0, + &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + + ufs_exit(ufs, alloc); +} + +static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) +{ + QUfs *ufs = obj; + uint8_t read_buf[4096] = { 0 }; + uint8_t write_buf[4096] = { 0 }; + const uint8_t read_capacity_cdb[UFS_CDB_SIZE] = { + /* allocation length 4096 */ + SERVICE_ACTION_IN_16, + SAI_READ_CAPACITY_16, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x10, + 0x00, + 0x00, + 0x00 + }; + const uint8_t request_sense_cdb[UFS_CDB_SIZE] = { + REQUEST_SENSE, + }; + const uint8_t read_cdb[UFS_CDB_SIZE] = { + /* READ(10) to LBA 0, transfer length 1 */ + READ_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 + }; + const uint8_t write_cdb[UFS_CDB_SIZE] = { + /* WRITE(10) to LBA 0, transfer length 1 */ + WRITE_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 + }; + uint32_t block_size; + UtpTransferReqDesc utrd; + UtpUpiuRsp rsp_upiu; + const int test_lun = 1; + + ufs_init(ufs, alloc); + + /* Clear Unit Attention */ + ufs_send_scsi_command(ufs, 0, test_lun, request_sense_cdb, NULL, 0, + read_buf, sizeof(read_buf), &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION); + + /* Read capacity */ + ufs_send_scsi_command(ufs, 0, test_lun, read_capacity_cdb, NULL, 0, + read_buf, sizeof(read_buf), &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, + UFS_COMMAND_RESULT_SUCCESS); + block_size = ldl_be_p(&read_buf[8]); + g_assert_cmpuint(block_size, ==, 4096); + + /* Write data */ + memset(write_buf, 0xab, block_size); + ufs_send_scsi_command(ufs, 0, test_lun, write_cdb, write_buf, block_size, + NULL, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, + UFS_COMMAND_RESULT_SUCCESS); + + /* Read data and verify */ + ufs_send_scsi_command(ufs, 0, test_lun, read_cdb, NULL, 0, read_buf, + block_size, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, + UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpint(memcmp(read_buf, write_buf, block_size), ==, 0); + + ufs_exit(ufs, alloc); +} + +static void drive_destroy(void *path) +{ + unlink(path); + g_free(path); + qos_invalidate_command_line(); +} + +static char *drive_create(void) +{ + int fd, ret; + char *t_path; + + /* Create a temporary raw image */ + fd = g_file_open_tmp("qtest-ufs.XXXXXX", &t_path, NULL); + g_assert_cmpint(fd, >=, 0); + ret = ftruncate(fd, TEST_IMAGE_SIZE); + g_assert_cmpint(ret, ==, 0); + close(fd); + + g_test_queue_destroy(drive_destroy, t_path); + return t_path; +} + +static void *ufs_blk_test_setup(GString *cmd_line, void *arg) +{ + char *tmp_path = drive_create(); + + g_string_append_printf(cmd_line, + " -blockdev file,filename=%s,node-name=drv1 " + "-device ufs-lu,bus=ufs0,drive=drv1,lun=1 ", + tmp_path); + + return arg; +} + +static void ufs_register_nodes(void) +{ + const char *arch; + QOSGraphEdgeOptions edge_opts = { + .before_cmd_line = "-blockdev null-co,node-name=drv0,read-zeroes=on", + .after_cmd_line = "-device ufs-lu,bus=ufs0,drive=drv0,lun=0", + .extra_device_opts = "addr=04.0,id=ufs0,nutrs=32,nutmrs=8" + }; + + QOSGraphTestOptions io_test_opts = { + .before = ufs_blk_test_setup, + }; + + add_qpci_address(&edge_opts, &(QPCIAddress){ .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("ufs", ufs_create); + qos_node_consumes("ufs", "pci-bus", &edge_opts); + qos_node_produces("ufs", "pci-device"); + + qos_add_test("reg-read", "ufs", ufstest_reg_read, NULL); + + /* + * Check architecture + * TODO: Enable ufs io tests for ppc64 + */ + arch = qtest_get_arch(); + if (!strcmp(arch, "ppc64")) { + g_test_message("Skipping ufs io tests for ppc64"); + return; + } + qos_add_test("init", "ufs", ufstest_init, NULL); + qos_add_test("read-write", "ufs", ufstest_read_write, &io_test_opts); +} + +libqos_init(ufs_register_nodes); diff --git a/tests/qtest/usb-hcd-ehci-test.c b/tests/qtest/usb-hcd-ehci-test.c index c51e8bb223..87e37cdd7c 100644 --- a/tests/qtest/usb-hcd-ehci-test.c +++ b/tests/qtest/usb-hcd-ehci-test.c @@ -149,6 +149,11 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); + if (!qtest_has_device("ich9-usb-ehci1") || + !qtest_has_device("ich9-usb-uhci1")) { + return 0; + } + qtest_add_func("/ehci/pci/uhci-port-1", pci_uhci_port_1); qtest_add_func("/ehci/pci/ehci-port-1", pci_ehci_port_1); qtest_add_func("/ehci/pci/ehci-config", pci_ehci_config); diff --git a/tests/qtest/usb-hcd-uhci-test.c b/tests/qtest/usb-hcd-uhci-test.c index 7a117b64d9..4446555f08 100644 --- a/tests/qtest/usb-hcd-uhci-test.c +++ b/tests/qtest/usb-hcd-uhci-test.c @@ -17,10 +17,6 @@ static QOSState *qs; -static void test_uhci_init(void) -{ -} - static void test_port(int port) { struct qhc uhci; @@ -66,15 +62,21 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); - qtest_add_func("/uhci/pci/init", test_uhci_init); + if (!qtest_has_device("piix3-usb-uhci")) { + g_debug("piix3-usb-uhci not available"); + return 0; + } + qtest_add_func("/uhci/pci/port1", test_port_1); qtest_add_func("/uhci/pci/hotplug", test_uhci_hotplug); - qtest_add_func("/uhci/pci/hotplug/usb-storage", test_usb_storage_hotplug); + if (qtest_has_device("usb-storage")) { + qtest_add_func("/uhci/pci/hotplug/usb-storage", test_usb_storage_hotplug); + } if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qs = qtest_pc_boot(cmd); + qs = qtest_pc_boot("%s", cmd); } else if (strcmp(arch, "ppc64") == 0) { - qs = qtest_spapr_boot(cmd); + qs = qtest_spapr_boot("%s", cmd); } else { g_printerr("usb-hcd-uhci-test tests are only " "available on x86 or ppc64\n"); diff --git a/tests/qtest/usb-hcd-xhci-test.c b/tests/qtest/usb-hcd-xhci-test.c index 10ef9d2a91..0cccfd85a6 100644 --- a/tests/qtest/usb-hcd-xhci-test.c +++ b/tests/qtest/usb-hcd-xhci-test.c @@ -11,11 +11,6 @@ #include "libqtest-single.h" #include "libqos/usb.h" - -static void test_xhci_init(void) -{ -} - static void test_xhci_hotplug(void) { usb_test_hotplug(global_qtest, "xhci", "1", NULL); @@ -54,10 +49,13 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); - qtest_add_func("/xhci/pci/init", test_xhci_init); qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug); - qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug); - qtest_add_func("/xhci/pci/hotplug/usb-ccid", test_usb_ccid_hotplug); + if (qtest_has_device("usb-uas")) { + qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug); + } + if (qtest_has_device("usb-ccid")) { + qtest_add_func("/xhci/pci/hotplug/usb-ccid", test_usb_ccid_hotplug); + } qtest_start("-device nec-usb-xhci,id=xhci" " -drive id=drive0,if=none,file=null-co://," diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c index a81c2a2715..117b9acd10 100644 --- a/tests/qtest/vhost-user-blk-test.c +++ b/tests/qtest/vhost-user-blk-test.c @@ -841,7 +841,8 @@ static char *create_listen_socket(int *fd) char *path; /* No race because our pid makes the path unique */ - path = g_strdup_printf("/tmp/qtest-%d-sock.XXXXXX", getpid()); + path = g_strdup_printf("%s/qtest-%d-sock.XXXXXX", + g_get_tmp_dir(), getpid()); tmp_fd = mkstemp(path); g_assert_cmpint(tmp_fd, >=, 0); close(tmp_fd); @@ -960,7 +961,7 @@ static void *vhost_user_blk_test_setup(GString *cmd_line, void *arg) * Setup for hotplug. * * Since vhost-user server only serves one vhost-user client one time, - * another exprot + * another export * */ static void *vhost_user_blk_hotplug_test_setup(GString *cmd_line, void *arg) @@ -982,6 +983,12 @@ static void register_vhost_user_blk_test(void) .before = vhost_user_blk_test_setup, }; + if (!getenv("QTEST_QEMU_STORAGE_DAEMON_BINARY")) { + g_test_message("QTEST_QEMU_STORAGE_DAEMON_BINARY not defined, " + "skipping vhost-user-blk-test"); + return; + } + /* * tests for vhost-user-blk and vhost-user-blk-pci * The tests are borrowed from tests/virtio-blk-test.c. But some tests diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index 8bf390be20..d4e437265f 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -26,11 +26,14 @@ #include "libqos/virtio-pci.h" #include "libqos/malloc-pc.h" +#include "libqos/qgraph_internal.h" #include "hw/virtio/virtio-net.h" #include "standard-headers/linux/vhost_types.h" #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_net.h" +#include "standard-headers/linux/virtio_gpio.h" +#include "standard-headers/linux/virtio_scmi.h" #ifdef CONFIG_LINUX #include @@ -52,9 +55,12 @@ #define VHOST_MAX_VIRTQUEUES 0x100 #define VHOST_USER_F_PROTOCOL_FEATURES 30 +#define VIRTIO_F_VERSION_1 32 + #define VHOST_USER_PROTOCOL_F_MQ 0 #define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1 #define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN 6 +#define VHOST_USER_PROTOCOL_F_CONFIG 9 #define VHOST_LOG_PAGE 0x1000 @@ -78,6 +84,8 @@ typedef enum VhostUserRequest { VHOST_USER_SET_PROTOCOL_FEATURES = 16, VHOST_USER_GET_QUEUE_NUM = 17, VHOST_USER_SET_VRING_ENABLE = 18, + VHOST_USER_GET_CONFIG = 24, + VHOST_USER_SET_CONFIG = 25, VHOST_USER_MAX } VhostUserRequest; @@ -137,6 +145,8 @@ enum { enum { VHOST_USER_NET, + VHOST_USER_GPIO, + VHOST_USER_SCMI, }; typedef struct TestServer { @@ -168,10 +178,11 @@ struct vhost_user_ops { const char *chr_opts); /* VHOST-USER commands. */ + uint64_t (*get_features)(TestServer *s); void (*set_features)(TestServer *s, CharBackend *chr, - VhostUserMsg *msg); + VhostUserMsg *msg); void (*get_protocol_features)(TestServer *s, - CharBackend *chr, VhostUserMsg *msg); + CharBackend *chr, VhostUserMsg *msg); }; static const char *init_hugepagefs(void); @@ -194,6 +205,19 @@ static void append_vhost_net_opts(TestServer *s, GString *cmd_line, chr_opts, s->chr_name); } +/* + * For GPIO there are no other magic devices we need to add (like + * block or netdev) so all we need to worry about is the vhost-user + * chardev socket. + */ +static void append_vhost_gpio_opts(TestServer *s, GString *cmd_line, + const char *chr_opts) +{ + g_string_append_printf(cmd_line, QEMU_CMD_CHR, + s->chr_name, s->socket_path, + chr_opts); +} + static void append_mem_opts(TestServer *server, GString *cmd_line, int size, enum test_memfd memfd) { @@ -259,7 +283,7 @@ static void read_guest_mem_server(QTestState *qts, TestServer *s) /* iterate all regions */ for (i = 0; i < s->fds_num; i++) { - /* We'll check only the region statring at 0x0*/ + /* We'll check only the region starting at 0x0 */ if (s->memory.regions[i].guest_phys_addr != 0x0) { continue; } @@ -316,7 +340,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) } if (size != VHOST_USER_HDR_SIZE) { - g_test_message("Wrong message size received %d", size); + qos_printf("%s: Wrong message size received %d\n", __func__, size); return; } @@ -327,28 +351,30 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) p += VHOST_USER_HDR_SIZE; size = qemu_chr_fe_read_all(chr, p, msg.size); if (size != msg.size) { - g_test_message("Wrong message size received %d != %d", - size, msg.size); - return; + qos_printf("%s: Wrong message size received %d != %d\n", + __func__, size, msg.size); + goto out; } } switch (msg.request) { case VHOST_USER_GET_FEATURES: + /* Mandatory for tests to define get_features */ + g_assert(s->vu_ops->get_features); + /* send back features to qemu */ msg.flags |= VHOST_USER_REPLY_MASK; msg.size = sizeof(m.payload.u64); - msg.payload.u64 = 0x1ULL << VHOST_F_LOG_ALL | - 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES; - if (s->queues > 1) { - msg.payload.u64 |= 0x1ULL << VIRTIO_NET_F_MQ; - } + if (s->test_flags >= TEST_FLAGS_BAD) { msg.payload.u64 = 0; s->test_flags = TEST_FLAGS_END; + } else { + msg.payload.u64 = s->vu_ops->get_features(s); } - p = (uint8_t *) &msg; - qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); + + qemu_chr_fe_write_all(chr, (uint8_t *) &msg, + VHOST_USER_HDR_SIZE + msg.size); break; case VHOST_USER_SET_FEATURES: @@ -357,12 +383,55 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) } break; + case VHOST_USER_SET_OWNER: + /* + * We don't need to do anything here, the remote is just + * letting us know it is in charge. Just log it. + */ + qos_printf("set_owner: start of session\n"); + break; + case VHOST_USER_GET_PROTOCOL_FEATURES: if (s->vu_ops->get_protocol_features) { s->vu_ops->get_protocol_features(s, chr, &msg); } break; + case VHOST_USER_GET_CONFIG: + /* + * Treat GET_CONFIG as a NOP and just reply and let the guest + * consider we have updated its memory. Tests currently don't + * require working configs. + */ + msg.flags |= VHOST_USER_REPLY_MASK; + p = (uint8_t *) &msg; + qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); + break; + + case VHOST_USER_SET_PROTOCOL_FEATURES: + /* + * We did set VHOST_USER_F_PROTOCOL_FEATURES so its valid for + * the remote end to send this. There is no handshake reply so + * just log the details for debugging. + */ + qos_printf("set_protocol_features: 0x%"PRIx64 "\n", msg.payload.u64); + break; + + /* + * A real vhost-user backend would actually set the size and + * address of the vrings but we can simply report them. + */ + case VHOST_USER_SET_VRING_NUM: + qos_printf("set_vring_num: %d/%d\n", + msg.payload.state.index, msg.payload.state.num); + break; + case VHOST_USER_SET_VRING_ADDR: + qos_printf("set_vring_addr: 0x%"PRIx64"/0x%"PRIx64"/0x%"PRIx64"\n", + msg.payload.addr.avail_user_addr, + msg.payload.addr.desc_user_addr, + msg.payload.addr.used_user_addr); + break; + case VHOST_USER_GET_VRING_BASE: /* send back vring base to qemu */ msg.flags |= VHOST_USER_REPLY_MASK; @@ -427,10 +496,22 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); break; + case VHOST_USER_SET_VRING_ENABLE: + /* + * Another case we ignore as we don't need to respond. With a + * fully functioning vhost-user we would enable/disable the + * vring monitoring. + */ + qos_printf("set_vring(%d)=%s\n", msg.payload.state.index, + msg.payload.state.num ? "enabled" : "disabled"); + break; + default: + qos_printf("vhost-user: un-handled message: %d\n", msg.request); break; } +out: g_mutex_unlock(&s->data_mutex); } @@ -450,7 +531,7 @@ static const char *init_hugepagefs(void) } if (access(path, R_OK | W_OK | X_OK)) { - g_test_message("access on path (%s): %s", path, strerror(errno)); + qos_printf("access on path (%s): %s", path, strerror(errno)); g_test_fail(); return NULL; } @@ -460,13 +541,13 @@ static const char *init_hugepagefs(void) } while (ret != 0 && errno == EINTR); if (ret != 0) { - g_test_message("statfs on path (%s): %s", path, strerror(errno)); + qos_printf("statfs on path (%s): %s", path, strerror(errno)); g_test_fail(); return NULL; } if (fs.f_type != HUGETLBFS_MAGIC) { - g_test_message("Warning: path not on HugeTLBFS: %s", path); + qos_printf("Warning: path not on HugeTLBFS: %s", path); g_test_fail(); return NULL; } @@ -482,8 +563,8 @@ static TestServer *test_server_new(const gchar *name, struct vhost_user_ops *ops) { TestServer *server = g_new0(TestServer, 1); - char template[] = "/tmp/vhost-test-XXXXXX"; - const char *tmpfs; + g_autofree const char *tmpfs = NULL; + GError *err = NULL; server->context = g_main_context_new(); server->loop = g_main_loop_new(server->context, FALSE); @@ -491,9 +572,11 @@ static TestServer *test_server_new(const gchar *name, /* run the main loop thread so the chardev may operate */ server->thread = g_thread_new(NULL, thread_function, server->loop); - tmpfs = mkdtemp(template); + tmpfs = g_dir_make_tmp("vhost-test-XXXXXX", &err); if (!tmpfs) { - g_test_message("mkdtemp on path (%s): %s", template, strerror(errno)); + g_test_message("Can't create temporary directory in %s: %s", + g_get_tmp_dir(), err->message); + g_error_free(err); } g_assert(tmpfs); @@ -936,11 +1019,23 @@ static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc) wait_for_rings_started(s, s->queues * 2); } -static void vu_net_set_features(TestServer *s, CharBackend *chr, - VhostUserMsg *msg) + +static uint64_t vu_net_get_features(TestServer *s) { - g_assert_cmpint(msg->payload.u64 & - (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES), !=, 0ULL); + uint64_t features = 0x1ULL << VHOST_F_LOG_ALL | + 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES; + + if (s->queues > 1) { + features |= 0x1ULL << VIRTIO_NET_F_MQ; + } + + return features; +} + +static void vu_net_set_features(TestServer *s, CharBackend *chr, + VhostUserMsg *msg) +{ + g_assert(msg->payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES)); if (s->test_flags == TEST_FLAGS_DISCONNECT) { qemu_chr_fe_disconnect(chr); s->test_flags = TEST_FLAGS_BAD; @@ -967,6 +1062,7 @@ static struct vhost_user_ops g_vu_net_ops = { .append_opts = append_vhost_net_opts, + .get_features = vu_net_get_features, .set_features = vu_net_set_features, .get_protocol_features = vu_net_get_protocol_features, }; @@ -1015,3 +1111,93 @@ static void register_vhost_user_test(void) test_multiqueue, &opts); } libqos_init(register_vhost_user_test); + +static uint64_t vu_gpio_get_features(TestServer *s) +{ + return 0x1ULL << VIRTIO_F_VERSION_1 | + 0x1ULL << VIRTIO_GPIO_F_IRQ | + 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES; +} + +/* + * This stub can't handle all the message types but we should reply + * that we support VHOST_USER_PROTOCOL_F_CONFIG as gpio would use it + * talking to a read vhost-user daemon. + */ +static void vu_gpio_get_protocol_features(TestServer *s, CharBackend *chr, + VhostUserMsg *msg) +{ + /* send back features to qemu */ + msg->flags |= VHOST_USER_REPLY_MASK; + msg->size = sizeof(m.payload.u64); + msg->payload.u64 = 1ULL << VHOST_USER_PROTOCOL_F_CONFIG; + + qemu_chr_fe_write_all(chr, (uint8_t *)msg, VHOST_USER_HDR_SIZE + msg->size); +} + +static struct vhost_user_ops g_vu_gpio_ops = { + .type = VHOST_USER_GPIO, + + .append_opts = append_vhost_gpio_opts, + + .get_features = vu_gpio_get_features, + .set_features = vu_net_set_features, + .get_protocol_features = vu_gpio_get_protocol_features, +}; + +static void register_vhost_gpio_test(void) +{ + QOSGraphTestOptions opts = { + .before = vhost_user_test_setup, + .subprocess = true, + .arg = &g_vu_gpio_ops, + }; + + qemu_add_opts(&qemu_chardev_opts); + + qos_add_test("read-guest-mem/memfile", + "vhost-user-gpio", test_read_guest_mem, &opts); +} +libqos_init(register_vhost_gpio_test); + +static uint64_t vu_scmi_get_features(TestServer *s) +{ + return 0x1ULL << VIRTIO_F_VERSION_1 | + 0x1ULL << VIRTIO_SCMI_F_P2A_CHANNELS | + 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES; +} + +static void vu_scmi_get_protocol_features(TestServer *s, CharBackend *chr, + VhostUserMsg *msg) +{ + msg->flags |= VHOST_USER_REPLY_MASK; + msg->size = sizeof(m.payload.u64); + msg->payload.u64 = 1ULL << VHOST_USER_PROTOCOL_F_MQ; + + qemu_chr_fe_write_all(chr, (uint8_t *)msg, VHOST_USER_HDR_SIZE + msg->size); +} + +static struct vhost_user_ops g_vu_scmi_ops = { + .type = VHOST_USER_SCMI, + + .append_opts = append_vhost_gpio_opts, + + .get_features = vu_scmi_get_features, + .set_features = vu_net_set_features, + .get_protocol_features = vu_scmi_get_protocol_features, +}; + +static void register_vhost_scmi_test(void) +{ + QOSGraphTestOptions opts = { + .before = vhost_user_test_setup, + .subprocess = true, + .arg = &g_vu_scmi_ops, + }; + + qemu_add_opts(&qemu_chardev_opts); + + qos_add_test("scmi/read-guest-mem/memfile", + "vhost-user-scmi", test_read_guest_mem, &opts); +} +libqos_init(register_vhost_scmi_test); diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c index e28c71bd8f..3c8cd235cf 100644 --- a/tests/qtest/virtio-9p-test.c +++ b/tests/qtest/virtio-9p-test.c @@ -13,76 +13,27 @@ */ #include "qemu/osdep.h" -#include "libqtest-single.h" #include "qemu/module.h" -#include "hw/9pfs/9p.h" -#include "hw/9pfs/9p-synth.h" -#include "libqos/virtio-9p.h" -#include "libqos/qgraph.h" +#include "libqos/virtio-9p-client.h" -#define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000) -static QGuestAllocator *alloc; - -/* - * Used to auto generate new fids. Start with arbitrary high value to avoid - * collision with hard coded fids in basic test code. - */ -static uint32_t fid_generator = 1000; - -static uint32_t genfid(void) -{ - return fid_generator++; -} - -/** - * Splits the @a in string by @a delim into individual (non empty) strings - * and outputs them to @a out. The output array @a out is NULL terminated. - * - * Output array @a out must be freed by calling split_free(). - * - * @returns number of individual elements in output array @a out (without the - * final NULL terminating element) - */ -static int split(const char *in, const char *delim, char ***out) -{ - int n = 0, i = 0; - char *tmp, *p; - - tmp = g_strdup(in); - for (p = strtok(tmp, delim); p != NULL; p = strtok(NULL, delim)) { - if (strlen(p) > 0) { - ++n; - } - } - g_free(tmp); - - *out = g_new0(char *, n + 1); /* last element NULL delimiter */ - - tmp = g_strdup(in); - for (p = strtok(tmp, delim); p != NULL; p = strtok(NULL, delim)) { - if (strlen(p) > 0) { - (*out)[i++] = g_strdup(p); - } - } - g_free(tmp); - - return n; -} - -static void split_free(char ***out) -{ - int i; - for (i = 0; (*out)[i]; ++i) { - g_free((*out)[i]); - } - g_free(*out); - *out = NULL; -} +#define twalk(...) v9fs_twalk((TWalkOpt) __VA_ARGS__) +#define tversion(...) v9fs_tversion((TVersionOpt) __VA_ARGS__) +#define tattach(...) v9fs_tattach((TAttachOpt) __VA_ARGS__) +#define tgetattr(...) v9fs_tgetattr((TGetAttrOpt) __VA_ARGS__) +#define treaddir(...) v9fs_treaddir((TReadDirOpt) __VA_ARGS__) +#define tlopen(...) v9fs_tlopen((TLOpenOpt) __VA_ARGS__) +#define twrite(...) v9fs_twrite((TWriteOpt) __VA_ARGS__) +#define tflush(...) v9fs_tflush((TFlushOpt) __VA_ARGS__) +#define tmkdir(...) v9fs_tmkdir((TMkdirOpt) __VA_ARGS__) +#define tlcreate(...) v9fs_tlcreate((TlcreateOpt) __VA_ARGS__) +#define tsymlink(...) v9fs_tsymlink((TsymlinkOpt) __VA_ARGS__) +#define tlink(...) v9fs_tlink((TlinkOpt) __VA_ARGS__) +#define tunlinkat(...) v9fs_tunlinkat((TunlinkatOpt) __VA_ARGS__) static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; + v9fs_set_allocator(t_alloc); size_t tag_len = qvirtio_config_readw(v9p->vdev, 0); g_autofree char *tag = NULL; int i; @@ -96,556 +47,43 @@ static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc) g_assert_cmpmem(tag, tag_len, MOUNT_TAG, tag_len); } -#define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */ - -typedef struct { - QTestState *qts; - QVirtio9P *v9p; - uint16_t tag; - uint64_t t_msg; - uint32_t t_size; - uint64_t r_msg; - /* No r_size, it is hardcoded to P9_MAX_SIZE */ - size_t t_off; - size_t r_off; - uint32_t free_head; -} P9Req; - -static void v9fs_memwrite(P9Req *req, const void *addr, size_t len) +static inline bool is_same_qid(v9fs_qid a, v9fs_qid b) { - qtest_memwrite(req->qts, req->t_msg + req->t_off, addr, len); - req->t_off += len; -} - -static void v9fs_memskip(P9Req *req, size_t len) -{ - req->r_off += len; -} - -static void v9fs_memread(P9Req *req, void *addr, size_t len) -{ - qtest_memread(req->qts, req->r_msg + req->r_off, addr, 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); - - v9fs_memwrite(req, &le_val, 2); -} - -static void v9fs_uint16_read(P9Req *req, uint16_t *val) -{ - v9fs_memread(req, val, 2); - le16_to_cpus(val); -} - -static void v9fs_uint32_write(P9Req *req, uint32_t val) -{ - uint32_t le_val = cpu_to_le32(val); - - v9fs_memwrite(req, &le_val, 4); -} - -static void v9fs_uint64_write(P9Req *req, uint64_t val) -{ - uint64_t le_val = cpu_to_le64(val); - - v9fs_memwrite(req, &le_val, 8); -} - -static void v9fs_uint32_read(P9Req *req, uint32_t *val) -{ - v9fs_memread(req, val, 4); - 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) -{ - size_t len = strlen(string); - - g_assert_cmpint(len, <=, UINT16_MAX - 2); - - return 2 + len; -} - -static void v9fs_string_write(P9Req *req, const char *string) -{ - int len = strlen(string); - - g_assert_cmpint(len, <=, UINT16_MAX); - - v9fs_uint16_write(req, (uint16_t) len); - v9fs_memwrite(req, string, len); -} - -static void v9fs_string_read(P9Req *req, uint16_t *len, char **string) -{ - uint16_t local_len; - - v9fs_uint16_read(req, &local_len); - if (len) { - *len = local_len; - } - if (string) { - *string = g_malloc(local_len + 1); - v9fs_memread(req, *string, local_len); - (*string)[local_len] = 0; - } else { - v9fs_memskip(req, local_len); - } -} - - typedef struct { - uint32_t size; - uint8_t id; - uint16_t tag; -} QEMU_PACKED P9Hdr; - -static P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id, - uint16_t tag) -{ - P9Req *req = g_new0(P9Req, 1); - uint32_t total_size = 7; /* 9P header has well-known size of 7 bytes */ - P9Hdr hdr = { - .id = id, - .tag = cpu_to_le16(tag) - }; - - g_assert_cmpint(total_size, <=, UINT32_MAX - size); - total_size += size; - hdr.size = cpu_to_le32(total_size); - - g_assert_cmpint(total_size, <=, P9_MAX_SIZE); - - req->qts = global_qtest; - req->v9p = v9p; - req->t_size = total_size; - req->t_msg = guest_alloc(alloc, req->t_size); - v9fs_memwrite(req, &hdr, 7); - req->tag = tag; - return req; -} - -static void v9fs_req_send(P9Req *req) -{ - QVirtio9P *v9p = req->v9p; - - req->r_msg = guest_alloc(alloc, P9_MAX_SIZE); - req->free_head = qvirtqueue_add(req->qts, v9p->vq, req->t_msg, req->t_size, - false, true); - qvirtqueue_add(req->qts, v9p->vq, req->r_msg, P9_MAX_SIZE, true, false); - qvirtqueue_kick(req->qts, v9p->vdev, v9p->vq, req->free_head); - req->t_off = 0; -} - -static const char *rmessage_name(uint8_t id) -{ - return - id == P9_RLERROR ? "RLERROR" : - id == P9_RVERSION ? "RVERSION" : - id == P9_RATTACH ? "RATTACH" : - id == P9_RWALK ? "RWALK" : - id == P9_RLOPEN ? "RLOPEN" : - id == P9_RWRITE ? "RWRITE" : - id == P9_RMKDIR ? "RMKDIR" : - id == P9_RLCREATE ? "RLCREATE" : - id == P9_RSYMLINK ? "RSYMLINK" : - id == P9_RLINK ? "RLINK" : - id == P9_RUNLINKAT ? "RUNLINKAT" : - id == P9_RFLUSH ? "RFLUSH" : - id == P9_RREADDIR ? "READDIR" : - ""; -} - -static void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len) -{ - QVirtio9P *v9p = req->v9p; - - qvirtio_wait_used_elem(req->qts, v9p->vdev, v9p->vq, req->free_head, len, - QVIRTIO_9P_TIMEOUT_US); -} - -static void v9fs_req_recv(P9Req *req, uint8_t id) -{ - P9Hdr hdr; - - v9fs_memread(req, &hdr, 7); - hdr.size = ldl_le_p(&hdr.size); - hdr.tag = lduw_le_p(&hdr.tag); - - g_assert_cmpint(hdr.size, >=, 7); - g_assert_cmpint(hdr.size, <=, P9_MAX_SIZE); - g_assert_cmpint(hdr.tag, ==, req->tag); - - if (hdr.id != id) { - g_printerr("Received response %d (%s) instead of %d (%s)\n", - hdr.id, rmessage_name(hdr.id), id, rmessage_name(id)); - - if (hdr.id == P9_RLERROR) { - uint32_t err; - v9fs_uint32_read(req, &err); - g_printerr("Rlerror has errno %d (%s)\n", err, strerror(err)); - } - } - g_assert_cmpint(hdr.id, ==, id); -} - -static void v9fs_req_free(P9Req *req) -{ - guest_free(alloc, req->t_msg); - guest_free(alloc, req->r_msg); - g_free(req); -} - -/* size[4] Rlerror tag[2] ecode[4] */ -static void v9fs_rlerror(P9Req *req, uint32_t *err) -{ - v9fs_req_recv(req, P9_RLERROR); - v9fs_uint32_read(req, err); - v9fs_req_free(req); -} - -/* size[4] Tversion tag[2] msize[4] version[s] */ -static P9Req *v9fs_tversion(QVirtio9P *v9p, uint32_t msize, const char *version, - uint16_t tag) -{ - P9Req *req; - uint32_t body_size = 4; - uint16_t string_size = v9fs_string_size(version); - - g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); - body_size += string_size; - req = v9fs_req_init(v9p, body_size, P9_TVERSION, tag); - - v9fs_uint32_write(req, msize); - v9fs_string_write(req, version); - v9fs_req_send(req); - return req; -} - -/* size[4] Rversion tag[2] msize[4] version[s] */ -static void v9fs_rversion(P9Req *req, uint16_t *len, char **version) -{ - uint32_t msize; - - v9fs_req_recv(req, P9_RVERSION); - v9fs_uint32_read(req, &msize); - - g_assert_cmpint(msize, ==, P9_MAX_SIZE); - - if (len || version) { - v9fs_string_read(req, len, version); - } - - v9fs_req_free(req); -} - -/* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] */ -static P9Req *v9fs_tattach(QVirtio9P *v9p, uint32_t fid, uint32_t n_uname, - uint16_t tag) -{ - const char *uname = ""; /* ignored by QEMU */ - const char *aname = ""; /* ignored by QEMU */ - P9Req *req = v9fs_req_init(v9p, 4 + 4 + 2 + 2 + 4, P9_TATTACH, tag); - - v9fs_uint32_write(req, fid); - v9fs_uint32_write(req, P9_NOFID); - v9fs_string_write(req, uname); - v9fs_string_write(req, aname); - v9fs_uint32_write(req, n_uname); - v9fs_req_send(req); - return req; -} - -typedef char v9fs_qid[13]; - -/* size[4] Rattach tag[2] qid[13] */ -static void v9fs_rattach(P9Req *req, v9fs_qid *qid) -{ - v9fs_req_recv(req, P9_RATTACH); - if (qid) { - v9fs_memread(req, qid, 13); - } - v9fs_req_free(req); -} - -/* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) */ -static P9Req *v9fs_twalk(QVirtio9P *v9p, uint32_t fid, uint32_t newfid, - uint16_t nwname, char *const wnames[], uint16_t tag) -{ - P9Req *req; - int i; - uint32_t body_size = 4 + 4 + 2; - - for (i = 0; i < nwname; i++) { - uint16_t wname_size = v9fs_string_size(wnames[i]); - - g_assert_cmpint(body_size, <=, UINT32_MAX - wname_size); - body_size += wname_size; - } - req = v9fs_req_init(v9p, body_size, P9_TWALK, tag); - v9fs_uint32_write(req, fid); - v9fs_uint32_write(req, newfid); - v9fs_uint16_write(req, nwname); - for (i = 0; i < nwname; i++) { - v9fs_string_write(req, wnames[i]); - } - v9fs_req_send(req); - return req; -} - -/* size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) */ -static void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid) -{ - uint16_t local_nwqid; - - v9fs_req_recv(req, P9_RWALK); - v9fs_uint16_read(req, &local_nwqid); - if (nwqid) { - *nwqid = local_nwqid; - } - if (wqid) { - *wqid = g_malloc(local_nwqid * 13); - v9fs_memread(req, *wqid, local_nwqid * 13); - } - 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_new(struct V9fsDirent, 1); - if (entries) { - *entries = e; - } - } else { - e = e->next = g_new(struct V9fsDirent, 1); - } - 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) -{ - P9Req *req; - - req = v9fs_req_init(v9p, 4 + 4, P9_TLOPEN, tag); - v9fs_uint32_write(req, fid); - v9fs_uint32_write(req, flags); - v9fs_req_send(req); - return req; -} - -/* size[4] Rlopen tag[2] qid[13] iounit[4] */ -static void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit) -{ - v9fs_req_recv(req, P9_RLOPEN); - if (qid) { - v9fs_memread(req, qid, 13); - } else { - v9fs_memskip(req, 13); - } - if (iounit) { - v9fs_uint32_read(req, iounit); - } - v9fs_req_free(req); -} - -/* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] */ -static P9Req *v9fs_twrite(QVirtio9P *v9p, uint32_t fid, uint64_t offset, - uint32_t count, const void *data, uint16_t tag) -{ - P9Req *req; - uint32_t body_size = 4 + 8 + 4; - - g_assert_cmpint(body_size, <=, UINT32_MAX - count); - body_size += count; - req = v9fs_req_init(v9p, body_size, P9_TWRITE, tag); - v9fs_uint32_write(req, fid); - v9fs_uint64_write(req, offset); - v9fs_uint32_write(req, count); - v9fs_memwrite(req, data, count); - v9fs_req_send(req); - return req; -} - -/* size[4] Rwrite tag[2] count[4] */ -static void v9fs_rwrite(P9Req *req, uint32_t *count) -{ - v9fs_req_recv(req, P9_RWRITE); - if (count) { - v9fs_uint32_read(req, count); - } - v9fs_req_free(req); -} - -/* size[4] Tflush tag[2] oldtag[2] */ -static P9Req *v9fs_tflush(QVirtio9P *v9p, uint16_t oldtag, uint16_t tag) -{ - P9Req *req; - - req = v9fs_req_init(v9p, 2, P9_TFLUSH, tag); - v9fs_uint32_write(req, oldtag); - v9fs_req_send(req); - return req; -} - -/* size[4] Rflush tag[2] */ -static void v9fs_rflush(P9Req *req) -{ - v9fs_req_recv(req, P9_RFLUSH); - v9fs_req_free(req); -} - -static void do_version(QVirtio9P *v9p) -{ - const char *version = "9P2000.L"; - uint16_t server_len; - g_autofree char *server_version = NULL; - P9Req *req; - - req = v9fs_tversion(v9p, P9_MAX_SIZE, version, P9_NOTAG); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rversion(req, &server_len, &server_version); - - g_assert_cmpmem(server_version, server_len, version, strlen(version)); -} - -/* utility function: walk to requested dir and return fid for that dir */ -static uint32_t do_walk(QVirtio9P *v9p, const char *path) -{ - char **wnames; - P9Req *req; - const uint32_t fid = genfid(); - - int nwnames = split(path, "/", &wnames); - - req = v9fs_twalk(v9p, 0, fid, nwnames, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, NULL, NULL); - - split_free(&wnames); - return fid; + /* don't compare QID version for checking for file ID equalness */ + return a[0] == b[0] && memcmp(&a[5], &b[5], 8) == 0; } static void fs_version(void *obj, void *data, QGuestAllocator *t_alloc) { - alloc = t_alloc; - do_version(obj); -} - -static void do_attach(QVirtio9P *v9p) -{ - P9Req *req; - - do_version(v9p); - req = v9fs_tattach(v9p, 0, getuid(), 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rattach(req, NULL); + v9fs_set_allocator(t_alloc); + tversion({ .client = obj }); } static void fs_attach(void *obj, void *data, QGuestAllocator *t_alloc) { - alloc = t_alloc; - do_attach(obj); + v9fs_set_allocator(t_alloc); + tattach({ .client = obj }); } static void fs_walk(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; + v9fs_set_allocator(t_alloc); char *wnames[P9_MAXWELEM]; uint16_t nwqid; g_autofree v9fs_qid *wqid = NULL; int i; - P9Req *req; for (i = 0; i < P9_MAXWELEM; i++) { wnames[i] = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i); } - do_attach(v9p); - req = v9fs_twalk(v9p, 0, 1, P9_MAXWELEM, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, &nwqid, &wqid); + tattach({ .client = v9p }); + twalk({ + .client = v9p, .fid = 0, .newfid = 1, + .nwname = P9_MAXWELEM, .wnames = wnames, + .rwalk = { .nwqid = &nwqid, .wqid = &wqid } + }); g_assert_cmpint(nwqid, ==, P9_MAXWELEM); @@ -664,192 +102,37 @@ static bool fs_dirents_contain_name(struct V9fsDirent *e, const char* name) return false; } -/* size[4] Tmkdir tag[2] dfid[4] name[s] mode[4] gid[4] */ -static P9Req *v9fs_tmkdir(QVirtio9P *v9p, uint32_t dfid, const char *name, - uint32_t mode, uint32_t gid, uint16_t tag) -{ - P9Req *req; - - uint32_t body_size = 4 + 4 + 4; - uint16_t string_size = v9fs_string_size(name); - - g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); - body_size += string_size; - - req = v9fs_req_init(v9p, body_size, P9_TMKDIR, tag); - v9fs_uint32_write(req, dfid); - v9fs_string_write(req, name); - v9fs_uint32_write(req, mode); - v9fs_uint32_write(req, gid); - v9fs_req_send(req); - return req; -} - -/* size[4] Rmkdir tag[2] qid[13] */ -static void v9fs_rmkdir(P9Req *req, v9fs_qid *qid) -{ - v9fs_req_recv(req, P9_RMKDIR); - if (qid) { - v9fs_memread(req, qid, 13); - } else { - v9fs_memskip(req, 13); - } - v9fs_req_free(req); -} - -/* size[4] Tlcreate tag[2] fid[4] name[s] flags[4] mode[4] gid[4] */ -static P9Req *v9fs_tlcreate(QVirtio9P *v9p, uint32_t fid, const char *name, - uint32_t flags, uint32_t mode, uint32_t gid, - uint16_t tag) -{ - P9Req *req; - - uint32_t body_size = 4 + 4 + 4 + 4; - uint16_t string_size = v9fs_string_size(name); - - g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); - body_size += string_size; - - req = v9fs_req_init(v9p, body_size, P9_TLCREATE, tag); - v9fs_uint32_write(req, fid); - v9fs_string_write(req, name); - v9fs_uint32_write(req, flags); - v9fs_uint32_write(req, mode); - v9fs_uint32_write(req, gid); - v9fs_req_send(req); - return req; -} - -/* size[4] Rlcreate tag[2] qid[13] iounit[4] */ -static void v9fs_rlcreate(P9Req *req, v9fs_qid *qid, uint32_t *iounit) -{ - v9fs_req_recv(req, P9_RLCREATE); - if (qid) { - v9fs_memread(req, qid, 13); - } else { - v9fs_memskip(req, 13); - } - if (iounit) { - v9fs_uint32_read(req, iounit); - } - v9fs_req_free(req); -} - -/* size[4] Tsymlink tag[2] fid[4] name[s] symtgt[s] gid[4] */ -static P9Req *v9fs_tsymlink(QVirtio9P *v9p, uint32_t fid, const char *name, - const char *symtgt, uint32_t gid, uint16_t tag) -{ - P9Req *req; - - uint32_t body_size = 4 + 4; - uint16_t string_size = v9fs_string_size(name) + v9fs_string_size(symtgt); - - g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); - body_size += string_size; - - req = v9fs_req_init(v9p, body_size, P9_TSYMLINK, tag); - v9fs_uint32_write(req, fid); - v9fs_string_write(req, name); - v9fs_string_write(req, symtgt); - v9fs_uint32_write(req, gid); - v9fs_req_send(req); - return req; -} - -/* size[4] Rsymlink tag[2] qid[13] */ -static void v9fs_rsymlink(P9Req *req, v9fs_qid *qid) -{ - v9fs_req_recv(req, P9_RSYMLINK); - if (qid) { - v9fs_memread(req, qid, 13); - } else { - v9fs_memskip(req, 13); - } - v9fs_req_free(req); -} - -/* size[4] Tlink tag[2] dfid[4] fid[4] name[s] */ -static P9Req *v9fs_tlink(QVirtio9P *v9p, uint32_t dfid, uint32_t fid, - const char *name, uint16_t tag) -{ - P9Req *req; - - uint32_t body_size = 4 + 4; - uint16_t string_size = v9fs_string_size(name); - - g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); - body_size += string_size; - - req = v9fs_req_init(v9p, body_size, P9_TLINK, tag); - v9fs_uint32_write(req, dfid); - v9fs_uint32_write(req, fid); - v9fs_string_write(req, name); - v9fs_req_send(req); - return req; -} - -/* size[4] Rlink tag[2] */ -static void v9fs_rlink(P9Req *req) -{ - v9fs_req_recv(req, P9_RLINK); - v9fs_req_free(req); -} - -/* size[4] Tunlinkat tag[2] dirfd[4] name[s] flags[4] */ -static P9Req *v9fs_tunlinkat(QVirtio9P *v9p, uint32_t dirfd, const char *name, - uint32_t flags, uint16_t tag) -{ - P9Req *req; - - uint32_t body_size = 4 + 4; - uint16_t string_size = v9fs_string_size(name); - - g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); - body_size += string_size; - - req = v9fs_req_init(v9p, body_size, P9_TUNLINKAT, tag); - v9fs_uint32_write(req, dirfd); - v9fs_string_write(req, name); - v9fs_uint32_write(req, flags); - v9fs_req_send(req); - return req; -} - -/* size[4] Runlinkat tag[2] */ -static void v9fs_runlinkat(P9Req *req) -{ - v9fs_req_recv(req, P9_RUNLINKAT); - v9fs_req_free(req); -} - /* basic readdir test where reply fits into a single response message */ 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) }; + v9fs_set_allocator(t_alloc); + char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR) }; uint16_t nqid; v9fs_qid qid; uint32_t count, nentries; struct V9fsDirent *entries = NULL; - P9Req *req; - do_attach(v9p); - req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, &nqid, NULL); + tattach({ .client = v9p }); + twalk({ + .client = v9p, .fid = 0, .newfid = 1, + .nwname = 1, .wnames = wnames, .rwalk.nwqid = &nqid + }); 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); + tlopen({ + .client = v9p, .fid = 1, .flags = O_DIRECTORY, .rlopen.qid = &qid + }); /* * 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); + treaddir({ + .client = v9p, .fid = 1, .offset = 0, .count = P9_MAX_SIZE - 11, + .rreaddir = { + .count = &count, .nentries = &nentries, .entries = &entries + } + }); /* * Assuming msize (P9_MAX_SIZE) is large enough so we can retrieve all @@ -879,16 +162,15 @@ static void fs_readdir(void *obj, void *data, QGuestAllocator *t_alloc) /* readdir test where overall request is split over several messages */ static void do_readdir_split(QVirtio9P *v9p, uint32_t count) { - char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR) }; + char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_READDIR_DIR) }; uint16_t nqid; v9fs_qid qid; uint32_t nentries, npartialentries; struct V9fsDirent *entries, *tail, *partialentries; - P9Req *req; int fid; uint64_t offset; - do_attach(v9p); + tattach({ .client = v9p }); fid = 1; offset = 0; @@ -896,14 +178,15 @@ static void do_readdir_split(QVirtio9P *v9p, uint32_t count) nentries = 0; tail = NULL; - req = v9fs_twalk(v9p, 0, fid, 1, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, &nqid, NULL); + twalk({ + .client = v9p, .fid = 0, .newfid = fid, + .nwname = 1, .wnames = wnames, .rwalk.nwqid = &nqid + }); g_assert_cmpint(nqid, ==, 1); - req = v9fs_tlopen(v9p, fid, O_DIRECTORY, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rlopen(req, &qid, NULL); + tlopen({ + .client = v9p, .fid = fid, .flags = O_DIRECTORY, .rlopen.qid = &qid + }); /* * send as many Treaddir requests as required to get all directory @@ -913,9 +196,13 @@ static void do_readdir_split(QVirtio9P *v9p, uint32_t count) npartialentries = 0; partialentries = NULL; - req = v9fs_treaddir(v9p, fid, offset, count, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rreaddir(req, &count, &npartialentries, &partialentries); + treaddir({ + .client = v9p, .fid = fid, .offset = offset, .count = count, + .rreaddir = { + .count = &count, .nentries = &npartialentries, + .entries = &partialentries + } + }); if (npartialentries > 0 && partialentries) { if (!entries) { entries = partialentries; @@ -959,38 +246,113 @@ static void do_readdir_split(QVirtio9P *v9p, uint32_t count) static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; - char *const wnames[] = { g_strdup(" /") }; - P9Req *req; - uint32_t err; + v9fs_set_allocator(t_alloc); + char *wnames[] = { g_strdup(" /") }; - do_attach(v9p); - req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rlerror(req, &err); - - g_assert_cmpint(err, ==, ENOENT); + tattach({ .client = v9p }); + twalk({ + .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames, + .expectErr = ENOENT + }); g_free(wnames[0]); } +static void fs_walk_nonexistent(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + v9fs_set_allocator(t_alloc); + + tattach({ .client = v9p }); + /* + * The 9p2000 protocol spec says: "If the first element cannot be walked + * for any reason, Rerror is returned." + */ + twalk({ .client = v9p, .path = "non-existent", .expectErr = ENOENT }); +} + +static void fs_walk_2nd_nonexistent(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + v9fs_set_allocator(t_alloc); + v9fs_qid root_qid; + uint16_t nwqid; + uint32_t fid; + g_autofree v9fs_qid *wqid = NULL; + g_autofree char *path = g_strdup_printf( + QTEST_V9FS_SYNTH_WALK_FILE "/non-existent", 0 + ); + + tattach({ .client = v9p, .rattach.qid = &root_qid }); + fid = twalk({ + .client = v9p, .path = path, + .rwalk = { .nwqid = &nwqid, .wqid = &wqid } + }).newfid; + /* + * The 9p2000 protocol spec says: "nwqid is therefore either nwname or the + * index of the first elementwise walk that failed." + */ + assert(nwqid == 1); + + /* returned QID wqid[0] is file ID of 1st subdir */ + g_assert(wqid && wqid[0] && !is_same_qid(root_qid, wqid[0])); + + /* expect fid being unaffected by walk above */ + tgetattr({ + .client = v9p, .fid = fid, .request_mask = P9_GETATTR_BASIC, + .expectErr = ENOENT + }); +} + +static void fs_walk_none(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + v9fs_set_allocator(t_alloc); + v9fs_qid root_qid; + g_autofree v9fs_qid *wqid = NULL; + struct v9fs_attr attr; + + tversion({ .client = v9p }); + tattach({ + .client = v9p, .fid = 0, .n_uname = getuid(), + .rattach.qid = &root_qid + }); + + twalk({ + .client = v9p, .fid = 0, .newfid = 1, .nwname = 0, .wnames = NULL, + .rwalk.wqid = &wqid + }); + + /* special case: no QID is returned if nwname=0 was sent */ + g_assert(wqid == NULL); + + tgetattr({ + .client = v9p, .fid = 1, .request_mask = P9_GETATTR_BASIC, + .rgetattr.attr = &attr + }); + + g_assert(is_same_qid(root_qid, attr.qid)); +} + static void fs_walk_dotdot(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; - char *const wnames[] = { g_strdup("..") }; + v9fs_set_allocator(t_alloc); + char *wnames[] = { g_strdup("..") }; v9fs_qid root_qid; g_autofree v9fs_qid *wqid = NULL; - P9Req *req; - do_version(v9p); - req = v9fs_tattach(v9p, 0, getuid(), 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rattach(req, &root_qid); + tversion({ .client = v9p }); + tattach({ + .client = v9p, .fid = 0, .n_uname = getuid(), + .rattach.qid = &root_qid + }); - req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, NULL, &wqid); /* We now we'll get one qid */ + twalk({ + .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames, + .rwalk.wqid = &wqid /* We now we'll get one qid */ + }); g_assert_cmpmem(&root_qid, 13, wqid[0], 13); @@ -1000,18 +362,15 @@ static void fs_walk_dotdot(void *obj, void *data, QGuestAllocator *t_alloc) static void fs_lopen(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; - char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE) }; - P9Req *req; + v9fs_set_allocator(t_alloc); + char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE) }; - do_attach(v9p); - req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, NULL, NULL); + tattach({ .client = v9p }); + twalk({ + .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames + }); - req = v9fs_tlopen(v9p, 1, O_WRONLY, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rlopen(req, NULL, NULL); + tlopen({ .client = v9p, .fid = 1, .flags = O_WRONLY }); g_free(wnames[0]); } @@ -1019,25 +378,23 @@ static void fs_lopen(void *obj, void *data, QGuestAllocator *t_alloc) static void fs_write(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; + v9fs_set_allocator(t_alloc); static const uint32_t write_count = P9_MAX_SIZE / 2; - char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_WRITE_FILE) }; + char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_WRITE_FILE) }; g_autofree char *buf = g_malloc0(write_count); uint32_t count; - P9Req *req; - do_attach(v9p); - req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, NULL, NULL); + tattach({ .client = v9p }); + twalk({ + .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames + }); - req = v9fs_tlopen(v9p, 1, O_WRONLY, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rlopen(req, NULL, NULL); + tlopen({ .client = v9p, .fid = 1, .flags = O_WRONLY }); - req = v9fs_twrite(v9p, 1, 0, write_count, buf, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwrite(req, &count); + count = twrite({ + .client = v9p, .fid = 1, .offset = 0, .count = write_count, + .data = buf + }).count; g_assert_cmpint(count, ==, write_count); g_free(wnames[0]); @@ -1046,28 +403,32 @@ static void fs_write(void *obj, void *data, QGuestAllocator *t_alloc) static void fs_flush_success(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; - char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) }; + v9fs_set_allocator(t_alloc); + char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) }; P9Req *req, *flush_req; uint32_t reply_len; uint8_t should_block; - do_attach(v9p); - req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, NULL, NULL); + tattach({ .client = v9p }); + twalk({ + .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames + }); - req = v9fs_tlopen(v9p, 1, O_WRONLY, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rlopen(req, NULL, NULL); + tlopen({ .client = v9p, .fid = 1, .flags = O_WRONLY }); /* This will cause the 9p server to try to write data to the backend, * until the write request gets cancelled. */ should_block = 1; - req = v9fs_twrite(v9p, 1, 0, sizeof(should_block), &should_block, 0); + req = twrite({ + .client = v9p, .fid = 1, .offset = 0, + .count = sizeof(should_block), .data = &should_block, + .requestOnly = true + }).req; - flush_req = v9fs_tflush(v9p, req->tag, 1); + flush_req = tflush({ + .client = v9p, .oldtag = req->tag, .tag = 1, .requestOnly = true + }).req; /* The write request is supposed to be flushed: the server should just * mark the write request as used and reply to the flush request. @@ -1083,28 +444,32 @@ static void fs_flush_success(void *obj, void *data, QGuestAllocator *t_alloc) static void fs_flush_ignored(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; - char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) }; + v9fs_set_allocator(t_alloc); + char *wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) }; P9Req *req, *flush_req; uint32_t count; uint8_t should_block; - do_attach(v9p); - req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rwalk(req, NULL, NULL); + tattach({ .client = v9p }); + twalk({ + .client = v9p, .fid = 0, .newfid = 1, .nwname = 1, .wnames = wnames + }); - req = v9fs_tlopen(v9p, 1, O_WRONLY, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rlopen(req, NULL, NULL); + tlopen({ .client = v9p, .fid = 1, .flags = O_WRONLY }); /* This will cause the write request to complete right away, before it * could be actually cancelled. */ should_block = 0; - req = v9fs_twrite(v9p, 1, 0, sizeof(should_block), &should_block, 0); + req = twrite({ + .client = v9p, .fid = 1, .offset = 0, + .count = sizeof(should_block), .data = &should_block, + .requestOnly = true + }).req; - flush_req = v9fs_tflush(v9p, req->tag, 1); + flush_req = tflush({ + .client = v9p, .oldtag = req->tag, .tag = 1, .requestOnly = true + }).req; /* The write request is supposed to complete. The server should * reply to the write request and the flush request. @@ -1117,99 +482,24 @@ static void fs_flush_ignored(void *obj, void *data, QGuestAllocator *t_alloc) g_free(wnames[0]); } -static void do_mkdir(QVirtio9P *v9p, const char *path, const char *cname) -{ - g_autofree char *name = g_strdup(cname); - uint32_t fid; - P9Req *req; - - fid = do_walk(v9p, path); - - req = v9fs_tmkdir(v9p, fid, name, 0750, 0, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rmkdir(req, NULL); -} - -/* create a regular file with Tlcreate and return file's fid */ -static uint32_t do_lcreate(QVirtio9P *v9p, const char *path, - const char *cname) -{ - g_autofree char *name = g_strdup(cname); - uint32_t fid; - P9Req *req; - - fid = do_walk(v9p, path); - - req = v9fs_tlcreate(v9p, fid, name, 0, 0750, 0, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rlcreate(req, NULL, NULL); - - return fid; -} - -/* create symlink named @a clink in directory @a path pointing to @a to */ -static void do_symlink(QVirtio9P *v9p, const char *path, const char *clink, - const char *to) -{ - g_autofree char *name = g_strdup(clink); - g_autofree char *dst = g_strdup(to); - uint32_t fid; - P9Req *req; - - fid = do_walk(v9p, path); - - req = v9fs_tsymlink(v9p, fid, name, dst, 0, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rsymlink(req, NULL); -} - -/* create a hard link named @a clink in directory @a path pointing to @a to */ -static void do_hardlink(QVirtio9P *v9p, const char *path, const char *clink, - const char *to) -{ - uint32_t dfid, fid; - P9Req *req; - - dfid = do_walk(v9p, path); - fid = do_walk(v9p, to); - - req = v9fs_tlink(v9p, dfid, fid, clink, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_rlink(req); -} - -static void do_unlinkat(QVirtio9P *v9p, const char *atpath, const char *rpath, - uint32_t flags) -{ - g_autofree char *name = g_strdup(rpath); - uint32_t fid; - P9Req *req; - - fid = do_walk(v9p, atpath); - - req = v9fs_tunlinkat(v9p, fid, name, flags, 0); - v9fs_req_wait_for_reply(req, NULL); - v9fs_runlinkat(req); -} - static void fs_readdir_split_128(void *obj, void *data, QGuestAllocator *t_alloc) { - alloc = t_alloc; + v9fs_set_allocator(t_alloc); do_readdir_split(obj, 128); } static void fs_readdir_split_256(void *obj, void *data, QGuestAllocator *t_alloc) { - alloc = t_alloc; + v9fs_set_allocator(t_alloc); do_readdir_split(obj, 256); } static void fs_readdir_split_512(void *obj, void *data, QGuestAllocator *t_alloc) { - alloc = t_alloc; + v9fs_set_allocator(t_alloc); do_readdir_split(obj, 512); } @@ -1219,15 +509,15 @@ static void fs_readdir_split_512(void *obj, void *data, static void fs_create_dir(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; + v9fs_set_allocator(t_alloc); struct stat st; g_autofree char *root_path = virtio_9p_test_path(""); g_autofree char *new_dir = virtio_9p_test_path("01"); g_assert(root_path != NULL); - do_attach(v9p); - do_mkdir(v9p, "/", "01"); + tattach({ .client = v9p }); + tmkdir({ .client = v9p, .atPath = "/", .name = "01" }); /* check if created directory really exists now ... */ g_assert(stat(new_dir, &st) == 0); @@ -1238,22 +528,25 @@ static void fs_create_dir(void *obj, void *data, QGuestAllocator *t_alloc) static void fs_unlinkat_dir(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; + v9fs_set_allocator(t_alloc); struct stat st; g_autofree char *root_path = virtio_9p_test_path(""); g_autofree char *new_dir = virtio_9p_test_path("02"); g_assert(root_path != NULL); - do_attach(v9p); - do_mkdir(v9p, "/", "02"); + tattach({ .client = v9p }); + tmkdir({ .client = v9p, .atPath = "/", .name = "02" }); /* check if created directory really exists now ... */ g_assert(stat(new_dir, &st) == 0); /* ... and is actually a directory */ g_assert((st.st_mode & S_IFMT) == S_IFDIR); - do_unlinkat(v9p, "/", "02", P9_DOTL_AT_REMOVEDIR); + tunlinkat({ + .client = v9p, .atPath = "/", .name = "02", + .flags = P9_DOTL_AT_REMOVEDIR + }); /* directory should be gone now */ g_assert(stat(new_dir, &st) != 0); } @@ -1261,13 +554,13 @@ static void fs_unlinkat_dir(void *obj, void *data, QGuestAllocator *t_alloc) static void fs_create_file(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; + v9fs_set_allocator(t_alloc); struct stat st; g_autofree char *new_file = virtio_9p_test_path("03/1st_file"); - do_attach(v9p); - do_mkdir(v9p, "/", "03"); - do_lcreate(v9p, "03", "1st_file"); + tattach({ .client = v9p }); + tmkdir({ .client = v9p, .atPath = "/", .name = "03" }); + tlcreate({ .client = v9p, .atPath = "03", .name = "1st_file" }); /* check if created file exists now ... */ g_assert(stat(new_file, &st) == 0); @@ -1278,20 +571,20 @@ static void fs_create_file(void *obj, void *data, QGuestAllocator *t_alloc) static void fs_unlinkat_file(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; + v9fs_set_allocator(t_alloc); struct stat st; g_autofree char *new_file = virtio_9p_test_path("04/doa_file"); - do_attach(v9p); - do_mkdir(v9p, "/", "04"); - do_lcreate(v9p, "04", "doa_file"); + tattach({ .client = v9p }); + tmkdir({ .client = v9p, .atPath = "/", .name = "04" }); + tlcreate({ .client = v9p, .atPath = "04", .name = "doa_file" }); /* check if created file exists now ... */ g_assert(stat(new_file, &st) == 0); /* ... and is a regular file */ g_assert((st.st_mode & S_IFMT) == S_IFREG); - do_unlinkat(v9p, "04", "doa_file", 0); + tunlinkat({ .client = v9p, .atPath = "04", .name = "doa_file" }); /* file should be gone now */ g_assert(stat(new_file, &st) != 0); } @@ -1299,18 +592,21 @@ static void fs_unlinkat_file(void *obj, void *data, QGuestAllocator *t_alloc) static void fs_symlink_file(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; + v9fs_set_allocator(t_alloc); struct stat st; g_autofree char *real_file = virtio_9p_test_path("05/real_file"); g_autofree char *symlink_file = virtio_9p_test_path("05/symlink_file"); - do_attach(v9p); - do_mkdir(v9p, "/", "05"); - do_lcreate(v9p, "05", "real_file"); + tattach({ .client = v9p }); + tmkdir({ .client = v9p, .atPath = "/", .name = "05" }); + tlcreate({ .client = v9p, .atPath = "05", .name = "real_file" }); g_assert(stat(real_file, &st) == 0); g_assert((st.st_mode & S_IFMT) == S_IFREG); - do_symlink(v9p, "05", "symlink_file", "real_file"); + tsymlink({ + .client = v9p, .atPath = "05", .name = "symlink_file", + .symtgt = "real_file" + }); /* check if created link exists now */ g_assert(stat(symlink_file, &st) == 0); @@ -1320,21 +616,24 @@ static void fs_unlinkat_symlink(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; + v9fs_set_allocator(t_alloc); struct stat st; g_autofree char *real_file = virtio_9p_test_path("06/real_file"); g_autofree char *symlink_file = virtio_9p_test_path("06/symlink_file"); - do_attach(v9p); - do_mkdir(v9p, "/", "06"); - do_lcreate(v9p, "06", "real_file"); + tattach({ .client = v9p }); + tmkdir({ .client = v9p, .atPath = "/", .name = "06" }); + tlcreate({ .client = v9p, .atPath = "06", .name = "real_file" }); g_assert(stat(real_file, &st) == 0); g_assert((st.st_mode & S_IFMT) == S_IFREG); - do_symlink(v9p, "06", "symlink_file", "real_file"); + tsymlink({ + .client = v9p, .atPath = "06", .name = "symlink_file", + .symtgt = "real_file" + }); g_assert(stat(symlink_file, &st) == 0); - do_unlinkat(v9p, "06", "symlink_file", 0); + tunlinkat({ .client = v9p, .atPath = "06", .name = "symlink_file" }); /* symlink should be gone now */ g_assert(stat(symlink_file, &st) != 0); } @@ -1342,18 +641,21 @@ static void fs_unlinkat_symlink(void *obj, void *data, static void fs_hardlink_file(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; + v9fs_set_allocator(t_alloc); struct stat st_real, st_link; g_autofree char *real_file = virtio_9p_test_path("07/real_file"); g_autofree char *hardlink_file = virtio_9p_test_path("07/hardlink_file"); - do_attach(v9p); - do_mkdir(v9p, "/", "07"); - do_lcreate(v9p, "07", "real_file"); + tattach({ .client = v9p }); + tmkdir({ .client = v9p, .atPath = "/", .name = "07" }); + tlcreate({ .client = v9p, .atPath = "07", .name = "real_file" }); g_assert(stat(real_file, &st_real) == 0); g_assert((st_real.st_mode & S_IFMT) == S_IFREG); - do_hardlink(v9p, "07", "hardlink_file", "07/real_file"); + tlink({ + .client = v9p, .atPath = "07", .name = "hardlink_file", + .toPath = "07/real_file" + }); /* check if link exists now ... */ g_assert(stat(hardlink_file, &st_link) == 0); @@ -1367,30 +669,44 @@ static void fs_unlinkat_hardlink(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; - alloc = t_alloc; + v9fs_set_allocator(t_alloc); struct stat st_real, st_link; g_autofree char *real_file = virtio_9p_test_path("08/real_file"); g_autofree char *hardlink_file = virtio_9p_test_path("08/hardlink_file"); - do_attach(v9p); - do_mkdir(v9p, "/", "08"); - do_lcreate(v9p, "08", "real_file"); + tattach({ .client = v9p }); + tmkdir({ .client = v9p, .atPath = "/", .name = "08" }); + tlcreate({ .client = v9p, .atPath = "08", .name = "real_file" }); g_assert(stat(real_file, &st_real) == 0); g_assert((st_real.st_mode & S_IFMT) == S_IFREG); - do_hardlink(v9p, "08", "hardlink_file", "08/real_file"); + tlink({ + .client = v9p, .atPath = "08", .name = "hardlink_file", + .toPath = "08/real_file" + }); g_assert(stat(hardlink_file, &st_link) == 0); - do_unlinkat(v9p, "08", "hardlink_file", 0); + tunlinkat({ .client = v9p, .atPath = "08", .name = "hardlink_file" }); /* symlink should be gone now */ g_assert(stat(hardlink_file, &st_link) != 0); /* and old file should still exist */ g_assert(stat(real_file, &st_real) == 0); } +static void cleanup_9p_local_driver(void *data) +{ + /* remove previously created test dir when test is completed */ + virtio_9p_remove_local_test_dir(); +} + static void *assign_9p_local_driver(GString *cmd_line, void *arg) { + /* make sure test dir for the 'local' tests exists */ + virtio_9p_create_local_test_dir(); + virtio_9p_assign_local_driver(cmd_line, "security_model=mapped-xattr"); + + g_test_queue_destroy(cleanup_9p_local_driver, NULL); return arg; } @@ -1407,8 +723,13 @@ static void register_virtio_9p_test(void) qos_add_test("synth/walk/basic", "virtio-9p", fs_walk, &opts); qos_add_test("synth/walk/no_slash", "virtio-9p", fs_walk_no_slash, &opts); + qos_add_test("synth/walk/none", "virtio-9p", fs_walk_none, &opts); qos_add_test("synth/walk/dotdot_from_root", "virtio-9p", fs_walk_dotdot, &opts); + qos_add_test("synth/walk/non_existent", "virtio-9p", fs_walk_nonexistent, + &opts); + qos_add_test("synth/walk/2nd_non_existent", "virtio-9p", + fs_walk_2nd_nonexistent, &opts); qos_add_test("synth/lopen/basic", "virtio-9p", fs_lopen, &opts); qos_add_test("synth/write/basic", "virtio-9p", fs_write, &opts); qos_add_test("synth/flush/success", "virtio-9p", fs_flush_success, @@ -1425,15 +746,6 @@ static void register_virtio_9p_test(void) /* 9pfs test cases using the 'local' filesystem driver */ - - /* - * XXX: Until we are sure that these tests can run everywhere, - * keep them as "slow" so that they aren't run with "make check". - */ - if (!g_test_slow()) { - return; - } - opts.before = assign_9p_local_driver; qos_add_test("local/config", "virtio-9p", pci_config, &opts); qos_add_test("local/create_dir", "virtio-9p", fs_create_dir, &opts); @@ -1449,15 +761,3 @@ static void register_virtio_9p_test(void) } libqos_init(register_virtio_9p_test); - -static void __attribute__((constructor)) construct_9p_test(void) -{ - /* make sure test dir for the 'local' tests exists */ - virtio_9p_create_local_test_dir(); -} - -static void __attribute__((destructor)) destruct_9p_test(void) -{ - /* remove previously created test dir when test suite completed */ - virtio_9p_remove_local_test_dir(); -} diff --git a/tests/qtest/virtio-blk-test.c b/tests/qtest/virtio-blk-test.c index dc5eed31c8..98c906ebb4 100644 --- a/tests/qtest/virtio-blk-test.c +++ b/tests/qtest/virtio-blk-test.c @@ -17,9 +17,6 @@ #include "libqos/qgraph.h" #include "libqos/virtio-blk.h" -/* TODO actually test the results and get rid of this */ -#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) - #define TEST_IMAGE_SIZE (64 * 1024 * 1024) #define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000) #define PCI_SLOT_HP 0x06 @@ -49,10 +46,10 @@ static void drive_destroy(void *path) static char *drive_create(void) { int fd, ret; - char *t_path = g_strdup("/tmp/qtest.XXXXXX"); + char *t_path; /* Create a temporary raw image */ - fd = mkstemp(t_path); + fd = g_file_open_tmp("qtest.XXXXXX", &t_path, NULL); g_assert_cmpint(fd, >=, 0); ret = ftruncate(fd, TEST_IMAGE_SIZE); g_assert_cmpint(ret, ==, 0); @@ -453,9 +450,10 @@ static void config(void *obj, void *data, QGuestAllocator *t_alloc) qvirtio_set_driver_ok(dev); - qmp_discard_response("{ 'execute': 'block_resize', " - " 'arguments': { 'device': 'drive0', " - " 'size': %d } }", n_size); + qtest_qmp_assert_success(global_qtest, + "{ 'execute': 'block_resize', " + " 'arguments': { 'device': 'drive0', " + " 'size': %d } }", n_size); qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US); capacity = qvirtio_config_readq(dev, 0); @@ -502,9 +500,10 @@ static void msix(void *obj, void *u_data, QGuestAllocator *t_alloc) qvirtio_set_driver_ok(dev); - qmp_discard_response("{ 'execute': 'block_resize', " - " 'arguments': { 'device': 'drive0', " - " 'size': %d } }", n_size); + qtest_qmp_assert_success(global_qtest, + "{ 'execute': 'block_resize', " + " 'arguments': { 'device': 'drive0', " + " 'size': %d } }", n_size); qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US); @@ -758,9 +757,10 @@ static void resize(void *obj, void *data, QGuestAllocator *t_alloc) vq = test_basic(dev, t_alloc); - qmp_discard_response("{ 'execute': 'block_resize', " - " 'arguments': { 'device': 'drive0', " - " 'size': %d } }", n_size); + qtest_qmp_assert_success(global_qtest, + "{ 'execute': 'block_resize', " + " 'arguments': { 'device': 'drive0', " + " 'size': %d } }", n_size); qvirtio_wait_queue_isr(qts, dev, vq, QVIRTIO_BLK_TIMEOUT_US); diff --git a/tests/qtest/virtio-ccw-test.c b/tests/qtest/virtio-ccw-test.c index d05236407b..7a5357c212 100644 --- a/tests/qtest/virtio-ccw-test.c +++ b/tests/qtest/virtio-ccw-test.c @@ -17,12 +17,6 @@ #include "libqtest-single.h" #include "libqos/virtio.h" -static void virtio_balloon_nop(void) -{ - global_qtest = qtest_initf("-device virtio-balloon-ccw"); - qtest_end(); -} - static void virtconsole_nop(void) { global_qtest = qtest_initf("-device virtio-serial-ccw,id=vser0 " @@ -53,20 +47,6 @@ static void virtio_serial_hotplug(void) qtest_quit(qts); } -static void virtio_blk_nop(void) -{ - global_qtest = qtest_initf("-drive if=none,id=drv0,file=null-co://," - "file.read-zeroes=on,format=raw " - "-device virtio-blk-ccw,drive=drv0"); - qtest_end(); -} - -static void virtio_net_nop(void) -{ - global_qtest = qtest_initf("-device virtio-net-ccw"); - qtest_end(); -} - static void virtio_rng_nop(void) { global_qtest = qtest_initf("-device virtio-rng-ccw"); @@ -95,21 +75,20 @@ static void virtio_scsi_hotplug(void) int main(int argc, char **argv) { - int ret; - g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/balloon/nop", virtio_balloon_nop); - qtest_add_func("/virtio/console/nop", virtconsole_nop); - qtest_add_func("/virtio/serialport/nop", virtserialport_nop); - qtest_add_func("/virtio/serial/nop", virtio_serial_nop); - qtest_add_func("/virtio/serial/hotplug", virtio_serial_hotplug); - qtest_add_func("/virtio/block/nop", virtio_blk_nop); - qtest_add_func("/virtio/net/nop", virtio_net_nop); - qtest_add_func("/virtio/rng/nop", virtio_rng_nop); - qtest_add_func("/virtio/scsi/nop", virtio_scsi_nop); - qtest_add_func("/virtio/scsi/hotplug", virtio_scsi_hotplug); + if (qtest_has_device("virtio-serial-ccw")) { + qtest_add_func("/virtio/console/nop", virtconsole_nop); + qtest_add_func("/virtio/serialport/nop", virtserialport_nop); + qtest_add_func("/virtio/serial/nop", virtio_serial_nop); + qtest_add_func("/virtio/serial/hotplug", virtio_serial_hotplug); + } + if (qtest_has_device("virtio-rng-ccw")) { + qtest_add_func("/virtio/rng/nop", virtio_rng_nop); + } + if (qtest_has_device("virtio-scsi-ccw")) { + qtest_add_func("/virtio/scsi/nop", virtio_scsi_nop); + qtest_add_func("/virtio/scsi/hotplug", virtio_scsi_hotplug); + } - ret = g_test_run(); - - return ret; + return g_test_run(); } diff --git a/tests/qtest/virtio-iommu-test.c b/tests/qtest/virtio-iommu-test.c index 068e7a9e6c..afb225971d 100644 --- a/tests/qtest/virtio-iommu-test.c +++ b/tests/qtest/virtio-iommu-test.c @@ -34,7 +34,7 @@ static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc) uint8_t bypass = qvirtio_config_readb(dev, 36); g_assert_cmpint(input_range_start, ==, 0); - g_assert_cmphex(input_range_end, ==, UINT64_MAX); + g_assert_cmphex(input_range_end, >=, UINT32_MAX); g_assert_cmpint(domain_range_start, ==, 0); g_assert_cmpint(domain_range_end, ==, UINT32_MAX); g_assert_cmpint(bypass, ==, 1); diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c index 443ee56de9..73dfabc272 100644 --- a/tests/qtest/virtio-net-failover.c +++ b/tests/qtest/virtio-net-failover.c @@ -11,6 +11,7 @@ #include "libqtest.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" +#include "migration-helpers.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qapi/qmp/qjson.h" @@ -485,7 +486,7 @@ static void test_hotplug_1_reverse(void) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -516,7 +517,7 @@ static void test_hotplug_2(void) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -565,7 +566,7 @@ static void test_hotplug_2_reverse(void) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs0'," "'rombar': 0," "'romfile': ''," @@ -588,6 +589,7 @@ static void test_hotplug_2_reverse(void) machine_stop(qts); } +#ifndef _WIN32 static QDict *migrate_status(QTestState *qts) { QDict *resp, *ret; @@ -637,7 +639,7 @@ static void test_migrate_out(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -735,26 +737,10 @@ static void test_migrate_out(gconstpointer opaque) machine_stop(qts); } -static QDict *get_migration_event(QTestState *qts) -{ - QDict *resp; - QDict *data; - - resp = qtest_qmp_eventwait_ref(qts, "MIGRATION"); - g_assert(qdict_haskey(resp, "data")); - - data = qdict_get_qdict(resp, "data"); - g_assert(qdict_haskey(data, "status")); - qobject_ref(data); - qobject_unref(resp); - - return data; -} - static void test_migrate_in(gconstpointer opaque) { QTestState *qts; - QDict *resp, *args, *ret; + QDict *resp, *ret; g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque); qts = machine_start(BASE_MACHINE @@ -768,7 +754,7 @@ static void test_migrate_in(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -786,18 +772,7 @@ static void test_migrate_in(gconstpointer opaque) check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, false, "primary0", MAC_PRIMARY0); - args = qdict_from_jsonf_nofail("{}"); - g_assert_nonnull(args); - qdict_put_str(args, "uri", uri); - - resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}", - args); - g_assert(qdict_haskey(resp, "return")); - qobject_unref(resp); - - resp = get_migration_event(qts); - g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup"); - qobject_unref(resp); + migrate_incoming_qmp(qts, uri, "{}"); resp = get_failover_negociated_event(qts); g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0"); @@ -833,7 +808,7 @@ static void test_off_migrate_out(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'off'," + "'failover': false," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -887,7 +862,7 @@ static void test_off_migrate_out(gconstpointer opaque) static void test_off_migrate_in(gconstpointer opaque) { QTestState *qts; - QDict *resp, *args, *ret; + QDict *ret; g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque); qts = machine_start(BASE_MACHINE @@ -901,7 +876,7 @@ static void test_off_migrate_in(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'off'," + "'failover': false," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -919,18 +894,7 @@ static void test_off_migrate_in(gconstpointer opaque) check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, true, "primary0", MAC_PRIMARY0); - args = qdict_from_jsonf_nofail("{}"); - g_assert_nonnull(args); - qdict_put_str(args, "uri", uri); - - resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}", - args); - g_assert(qdict_haskey(resp, "return")); - qobject_unref(resp); - - resp = get_migration_event(qts); - g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup"); - qobject_unref(resp); + migrate_incoming_qmp(qts, uri, "{}"); check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, true, "primary0", MAC_PRIMARY0); @@ -963,7 +927,7 @@ static void test_guest_off_migrate_out(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -1025,7 +989,7 @@ static void test_guest_off_migrate_out(gconstpointer opaque) static void test_guest_off_migrate_in(gconstpointer opaque) { QTestState *qts; - QDict *resp, *args, *ret; + QDict *ret; g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque); qts = machine_start(BASE_MACHINE @@ -1039,7 +1003,7 @@ static void test_guest_off_migrate_in(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -1057,18 +1021,7 @@ static void test_guest_off_migrate_in(gconstpointer opaque) check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, false, "primary0", MAC_PRIMARY0); - args = qdict_from_jsonf_nofail("{}"); - g_assert_nonnull(args); - qdict_put_str(args, "uri", uri); - - resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}", - args); - g_assert(qdict_haskey(resp, "return")); - qobject_unref(resp); - - resp = get_migration_event(qts); - g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup"); - qobject_unref(resp); + migrate_incoming_qmp(qts, uri, "{}"); check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, false, "primary0", MAC_PRIMARY0); @@ -1101,7 +1054,7 @@ static void test_migrate_guest_off_abort(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -1201,7 +1154,7 @@ static void test_migrate_abort_wait_unplug(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -1290,7 +1243,7 @@ static void test_migrate_abort_active(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -1389,7 +1342,7 @@ static void test_migrate_off_abort(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'off'," + "'failover': false," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -1481,7 +1434,7 @@ static void test_migrate_abort_timeout(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -1577,7 +1530,7 @@ static void test_multi_out(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -1608,7 +1561,7 @@ static void test_multi_out(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby1", "{'bus': 'root2'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs2'," "'mac': '"MAC_STANDBY1"'}"); @@ -1727,7 +1680,7 @@ static void test_multi_out(gconstpointer opaque) static void test_multi_in(gconstpointer opaque) { QTestState *qts; - QDict *resp, *args, *ret; + QDict *resp, *ret; g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque); qts = machine_start(BASE_MACHINE @@ -1747,7 +1700,7 @@ static void test_multi_in(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby0", "{'bus': 'root0'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs0'," "'mac': '"MAC_STANDBY0"'}"); @@ -1771,7 +1724,7 @@ static void test_multi_in(gconstpointer opaque) qtest_qmp_device_add(qts, "virtio-net", "standby1", "{'bus': 'root2'," - "'failover': 'on'," + "'failover': true," "'netdev': 'hs2'," "'mac': '"MAC_STANDBY1"'}"); @@ -1793,18 +1746,7 @@ static void test_multi_in(gconstpointer opaque) check_one_card(qts, true, "standby1", MAC_STANDBY1); check_one_card(qts, false, "primary1", MAC_PRIMARY1); - args = qdict_from_jsonf_nofail("{}"); - g_assert_nonnull(args); - qdict_put_str(args, "uri", uri); - - resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}", - args); - g_assert(qdict_haskey(resp, "return")); - qobject_unref(resp); - - resp = get_migration_event(qts); - g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup"); - qobject_unref(resp); + migrate_incoming_qmp(qts, uri, "{}"); resp = get_failover_negociated_event(qts); g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0"); @@ -1827,6 +1769,7 @@ static void test_multi_in(gconstpointer opaque) machine_stop(qts); } +#endif /* _WIN32 */ int main(int argc, char **argv) { @@ -1857,7 +1800,11 @@ int main(int argc, char **argv) qtest_add_func("failover-virtio-net/hotplug/2_reverse", test_hotplug_2_reverse); - /* migration tests */ +#ifndef _WIN32 + /* + * These migration tests cases use the exec migration protocol, + * which is unsupported on Windows. + */ qtest_add_data_func("failover-virtio-net/migrate/on/out", tmpfile, test_migrate_out); qtest_add_data_func("failover-virtio-net/migrate/on/in", tmpfile, @@ -1886,6 +1833,7 @@ int main(int argc, char **argv) tmpfile, test_multi_out); qtest_add_data_func("failover-virtio-net/migrate/multi/in", tmpfile, test_multi_in); +#endif /* _WIN32 */ ret = g_test_run(); diff --git a/tests/qtest/virtio-net-test.c b/tests/qtest/virtio-net-test.c index 6ded252901..2df75c9780 100644 --- a/tests/qtest/virtio-net-test.c +++ b/tests/qtest/virtio-net-test.c @@ -91,6 +91,7 @@ static void tx_test(QVirtioDevice *dev, len = ntohl(len); ret = recv(socket, buffer, len, 0); + g_assert_cmpint(ret, ==, len); g_assert_cmpstr(buffer, ==, "TEST"); } @@ -165,8 +166,6 @@ static void stop_cont_test(void *obj, void *data, QGuestAllocator *t_alloc) rx_stop_cont_test(dev, t_alloc, rx, sv[0]); } -#endif - static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtioPCIDevice *dev = obj; @@ -214,7 +213,7 @@ static void announce_self(void *obj, void *data, QGuestAllocator *t_alloc) g_assert_cmpint(*proto, ==, htons(ETH_P_RARP)); /* - * Stop the announcment by settings rounds to 0 on the + * Stop the announcement by settings rounds to 0 on the * existing timer. */ rsp = qmp("{ 'execute' : 'announce-self', " @@ -286,6 +285,8 @@ static void *virtio_net_test_setup(GString *cmd_line, void *arg) return sv; } +#endif /* _WIN32 */ + static void large_tx(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtioNet *dev = obj; @@ -319,16 +320,15 @@ static void *virtio_net_test_setup_nosocket(GString *cmd_line, void *arg) static void register_virtio_net_test(void) { - QOSGraphTestOptions opts = { - .before = virtio_net_test_setup, - }; + QOSGraphTestOptions opts = { 0 }; - qos_add_test("hotplug", "virtio-net-pci", hotplug, &opts); #ifndef _WIN32 + opts.before = virtio_net_test_setup; + qos_add_test("hotplug", "virtio-net-pci", hotplug, &opts); qos_add_test("basic", "virtio-net", send_recv_test, &opts); qos_add_test("rx_stop_cont", "virtio-net", stop_cont_test, &opts); -#endif qos_add_test("announce-self", "virtio-net", announce_self, &opts); +#endif /* These tests do not need a loopback backend. */ opts.before = virtio_net_test_setup_nosocket; diff --git a/tests/qtest/virtio-scsi-test.c b/tests/qtest/virtio-scsi-test.c index 8ceb12aacd..db10d572d0 100644 --- a/tests/qtest/virtio-scsi-test.c +++ b/tests/qtest/virtio-scsi-test.c @@ -156,7 +156,7 @@ static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev) return vs; } -static void hotplug(void *obj, void *data, QGuestAllocator *alloc) +static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc) { QTestState *qts = global_qtest; @@ -268,7 +268,7 @@ static void test_iothread_attach_node(void *obj, void *data, QVirtioSCSIPCI *scsi_pci = obj; QVirtioSCSI *scsi = &scsi_pci->scsi; QVirtioSCSIQueues *vs; - char tmp_path[] = "/tmp/qtest.XXXXXX"; + g_autofree char *tmp_path = NULL; int fd; int ret; @@ -282,7 +282,7 @@ static void test_iothread_attach_node(void *obj, void *data, vs = qvirtio_scsi_init(scsi->vdev); /* Create a temporary qcow2 overlay*/ - fd = mkstemp(tmp_path); + fd = g_file_open_tmp("qtest.XXXXXX", &tmp_path, NULL); g_assert(fd >= 0); close(fd); diff --git a/tests/qtest/vmgenid-test.c b/tests/qtest/vmgenid-test.c index efba76e716..29fee9e7c0 100644 --- a/tests/qtest/vmgenid-test.c +++ b/tests/qtest/vmgenid-test.c @@ -19,7 +19,7 @@ #define VGID_GUID "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87" #define VMGENID_GUID_OFFSET 40 /* allow space for - * OVMF SDT Header Probe Supressor + * OVMF SDT Header Probe Suppressor */ #define RSDP_ADDR_INVALID 0x100000 /* RSDP must be below this address */ @@ -165,13 +165,18 @@ int main(int argc, char **argv) { int ret; + g_test_init(&argc, &argv, NULL); + + if (!qtest_has_accel("tcg") && !qtest_has_accel("kvm")) { + g_test_skip("No KVM or TCG accelerator available"); + return 0; + } + ret = boot_sector_init(disk); if (ret) { return ret; } - g_test_init(&argc, &argv, NULL); - qtest_add_func("/vmgenid/vmgenid/set-guid", vmgenid_set_guid_test); qtest_add_func("/vmgenid/vmgenid/set-guid-auto", diff --git a/tests/qtest/vnc-display-test.c b/tests/qtest/vnc-display-test.c new file mode 100644 index 0000000000..f8933b0761 --- /dev/null +++ b/tests/qtest/vnc-display-test.c @@ -0,0 +1,112 @@ +/* + * VNC display tests + * + * Copyright (c) 2022 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 "qemu/sockets.h" +#include "libqtest.h" +#include +#include + +typedef struct Test { + QTestState *qts; + VncConnection *conn; + GMainLoop *loop; +} Test; + +#if !defined(CONFIG_DARWIN) + +static void on_vnc_error(VncConnection* self, + const char* msg) +{ + g_error("vnc-error: %s", msg); +} + +static void on_vnc_auth_failure(VncConnection *self, + const char *msg) +{ + g_error("vnc-auth-failure: %s", msg); +} + +#endif + +static bool +test_setup(Test *test) +{ +#if defined(CONFIG_DARWIN) + g_test_skip("Broken on Darwin"); + return false; +#else + int pair[2]; + + test->qts = qtest_init("-M none -vnc none -name vnc-test"); + + g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0); + + qtest_qmp_add_client(test->qts, "vnc", pair[1]); + + test->conn = vnc_connection_new(); + g_signal_connect(test->conn, "vnc-error", + G_CALLBACK(on_vnc_error), NULL); + g_signal_connect(test->conn, "vnc-auth-failure", + G_CALLBACK(on_vnc_auth_failure), NULL); + vnc_connection_set_auth_type(test->conn, VNC_CONNECTION_AUTH_NONE); + +#ifdef WIN32 + vnc_connection_open_fd(test->conn, _get_osfhandle(pair[0])); +#else + vnc_connection_open_fd(test->conn, pair[0]); +#endif + + test->loop = g_main_loop_new(NULL, FALSE); + return true; +#endif +} + +static void +test_vnc_basic_on_vnc_initialized(VncConnection *self, + Test *test) +{ + const char *name = vnc_connection_get_name(test->conn); + + g_assert_cmpstr(name, ==, "QEMU (vnc-test)"); + g_main_loop_quit(test->loop); +} + +static void +test_vnc_basic(void) +{ + Test test; + + if (!test_setup(&test)) { + return; + } + + g_signal_connect(test.conn, "vnc-initialized", + G_CALLBACK(test_vnc_basic_on_vnc_initialized), &test); + + g_main_loop_run(test.loop); + + qtest_quit(test.qts); + g_object_unref(test.conn); + g_main_loop_unref(test.loop); +} + +int +main(int argc, char **argv) +{ + if (getenv("GTK_VNC_DEBUG")) { + vnc_util_set_debug(true); + } + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/vnc-display/basic", test_vnc_basic); + + return g_test_run(); +} diff --git a/tests/qtest/xlnx-canfd-test.c b/tests/qtest/xlnx-canfd-test.c new file mode 100644 index 0000000000..78ec9ef2a7 --- /dev/null +++ b/tests/qtest/xlnx-canfd-test.c @@ -0,0 +1,412 @@ +/* + * SPDX-License-Identifier: MIT + * + * QTests for the Xilinx Versal CANFD controller. + * + * Copyright (c) 2022 AMD Inc. + * + * Written-by: Vikram Garhwal + * + * 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 "libqtest.h" + +/* Base address. */ +#define CANFD0_BASE_ADDR 0xff060000 +#define CANFD1_BASE_ADDR 0xff070000 + +/* Register addresses. */ +#define R_SRR_OFFSET 0x00 +#define R_MSR_OFFSET 0x04 +#define R_FILTER_CONTROL_REGISTER 0xe0 +#define R_SR_OFFSET 0x18 +#define R_ISR_OFFSET 0x1c +#define R_IER_OFFSET 0x20 +#define R_ICR_OFFSET 0x24 +#define R_TX_READY_REQ_REGISTER 0x90 +#define RX_FIFO_STATUS_REGISTER 0xe8 +#define R_TXID_OFFSET 0x100 +#define R_TXDLC_OFFSET 0x104 +#define R_TXDATA1_OFFSET 0x108 +#define R_TXDATA2_OFFSET 0x10c +#define R_AFMR_REGISTER0 0xa00 +#define R_AFIR_REGISTER0 0xa04 +#define R_RX0_ID_OFFSET 0x2100 +#define R_RX0_DLC_OFFSET 0x2104 +#define R_RX0_DATA1_OFFSET 0x2108 +#define R_RX0_DATA2_OFFSET 0x210c + +/* CANFD modes. */ +#define SRR_CONFIG_MODE 0x00 +#define MSR_NORMAL_MODE 0x00 +#define MSR_LOOPBACK_MODE (1 << 1) +#define ENABLE_CANFD (1 << 1) + +/* CANFD status. */ +#define STATUS_CONFIG_MODE (1 << 0) +#define STATUS_NORMAL_MODE (1 << 3) +#define STATUS_LOOPBACK_MODE (1 << 1) +#define ISR_TXOK (1 << 1) +#define ISR_RXOK (1 << 4) + +#define ENABLE_ALL_FILTERS 0xffffffff +#define ENABLE_ALL_INTERRUPTS 0xffffffff + +/* We are sending one canfd message. */ +#define TX_READY_REG_VAL 0x1 + +#define FIRST_RX_STORE_INDEX 0x1 +#define STATUS_REG_MASK 0xf +#define DLC_FD_BIT_SHIFT 0x1b +#define DLC_FD_BIT_MASK 0xf8000000 +#define FIFO_STATUS_READ_INDEX_MASK 0x3f +#define FIFO_STATUS_FILL_LEVEL_MASK 0x7f00 +#define FILL_LEVEL_SHIFT 0x8 + +/* CANFD frame size ID, DLC and 16 DATA word. */ +#define CANFD_FRAME_SIZE 18 +/* CAN frame size ID, DLC and 2 DATA word. */ +#define CAN_FRAME_SIZE 4 + +/* Set the filters for CANFD controller. */ +static void enable_filters(QTestState *qts) +{ + const uint32_t arr_afmr[32] = { 0xb423deaa, 0xa2a40bdc, 0x1b64f486, + 0x95c0d4ee, 0xe0c44528, 0x4b407904, + 0xd2673f46, 0x9fc638d6, 0x8844f3d8, + 0xa607d1e8, 0x67871bf4, 0xc2557dc, + 0x9ea5b53e, 0x3643c0cc, 0x5a05ea8e, + 0x83a46d84, 0x4a25c2b8, 0x93a66008, + 0x2e467470, 0xedc66118, 0x9086f9f2, + 0xfa23dd36, 0xb6654b90, 0xb221b8ca, + 0x3467d1e2, 0xa3a55542, 0x5b26a012, + 0x2281ea7e, 0xcea0ece8, 0xdc61e588, + 0x2e5676a, 0x16821320 }; + + const uint32_t arr_afir[32] = { 0xa833dfa1, 0x255a477e, 0x3a4bb1c5, + 0x8f560a6c, 0x27f38903, 0x2fecec4d, + 0xa014c66d, 0xec289b8, 0x7e52dead, + 0x82e94f3c, 0xcf3e3c5c, 0x66059871, + 0x3f213df4, 0x25ac3959, 0xa12e9bef, + 0xa3ad3af, 0xbafd7fe, 0xb3cb40fd, + 0x5d9caa81, 0x2ed61902, 0x7cd64a0, + 0x4b1fa538, 0x9b5ced8c, 0x150de059, + 0xd2794227, 0x635e820a, 0xbb6b02cf, + 0xbb58176, 0x570025bb, 0xa78d9658, + 0x49d735df, 0xe5399d2f }; + + /* Passing the respective array values to all the AFMR and AFIR pairs. */ + for (int i = 0; i < 32; i++) { + /* For CANFD0. */ + qtest_writel(qts, CANFD0_BASE_ADDR + R_AFMR_REGISTER0 + 8 * i, + arr_afmr[i]); + qtest_writel(qts, CANFD0_BASE_ADDR + R_AFIR_REGISTER0 + 8 * i, + arr_afir[i]); + + /* For CANFD1. */ + qtest_writel(qts, CANFD1_BASE_ADDR + R_AFMR_REGISTER0 + 8 * i, + arr_afmr[i]); + qtest_writel(qts, CANFD1_BASE_ADDR + R_AFIR_REGISTER0 + 8 * i, + arr_afir[i]); + } + + /* Enable all the pairs from AFR register. */ + qtest_writel(qts, CANFD0_BASE_ADDR + R_FILTER_CONTROL_REGISTER, + ENABLE_ALL_FILTERS); + qtest_writel(qts, CANFD1_BASE_ADDR + R_FILTER_CONTROL_REGISTER, + ENABLE_ALL_FILTERS); +} + +static void configure_canfd(QTestState *qts, uint8_t mode) +{ + uint32_t status = 0; + + /* Put CANFD0 and CANFD1 in config mode. */ + qtest_writel(qts, CANFD0_BASE_ADDR + R_SRR_OFFSET, SRR_CONFIG_MODE); + qtest_writel(qts, CANFD1_BASE_ADDR + R_SRR_OFFSET, SRR_CONFIG_MODE); + + /* Write mode of operation in Mode select register. */ + qtest_writel(qts, CANFD0_BASE_ADDR + R_MSR_OFFSET, mode); + qtest_writel(qts, CANFD1_BASE_ADDR + R_MSR_OFFSET, mode); + + enable_filters(qts); + + /* Check here if CANFD0 and CANFD1 are in config mode. */ + status = qtest_readl(qts, CANFD0_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_CONFIG_MODE); + + status = qtest_readl(qts, CANFD1_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_CONFIG_MODE); + + qtest_writel(qts, CANFD1_BASE_ADDR + R_IER_OFFSET, ENABLE_ALL_INTERRUPTS); + qtest_writel(qts, CANFD1_BASE_ADDR + R_IER_OFFSET, ENABLE_ALL_INTERRUPTS); + + qtest_writel(qts, CANFD0_BASE_ADDR + R_SRR_OFFSET, ENABLE_CANFD); + qtest_writel(qts, CANFD1_BASE_ADDR + R_SRR_OFFSET, ENABLE_CANFD); +} + +static void generate_random_data(uint32_t *buf_tx, bool is_canfd_frame) +{ + /* Generate random TX data for CANFD frame. */ + if (is_canfd_frame) { + for (int i = 0; i < CANFD_FRAME_SIZE - 2; i++) { + buf_tx[2 + i] = g_random_int(); + } + } else { + /* Generate random TX data for CAN frame. */ + for (int i = 0; i < CAN_FRAME_SIZE - 2; i++) { + buf_tx[2 + i] = g_random_int(); + } + } +} + +static void read_data(QTestState *qts, uint64_t can_base_addr, uint32_t *buf_rx, + uint32_t frame_size) +{ + uint32_t int_status; + uint32_t fifo_status_reg_value; + /* At which RX FIFO the received data is stored. */ + uint8_t store_ind = 0; + + /* Read the interrupt on CANFD rx. */ + int_status = qtest_readl(qts, can_base_addr + R_ISR_OFFSET) & ISR_RXOK; + + g_assert_cmpint(int_status, ==, ISR_RXOK); + + /* Find the fill level and read index. */ + fifo_status_reg_value = qtest_readl(qts, can_base_addr + + RX_FIFO_STATUS_REGISTER); + + store_ind = (fifo_status_reg_value & FIFO_STATUS_READ_INDEX_MASK) + + ((fifo_status_reg_value & FIFO_STATUS_FILL_LEVEL_MASK) >> + FILL_LEVEL_SHIFT); + + g_assert_cmpint(store_ind, ==, FIRST_RX_STORE_INDEX); + + /* Read the RX register data for CANFD. */ + buf_rx[0] = qtest_readl(qts, can_base_addr + R_RX0_ID_OFFSET); + buf_rx[1] = qtest_readl(qts, can_base_addr + R_RX0_DLC_OFFSET); + + for (int i = 0; i < frame_size - 2; i++) { + buf_rx[i + 2] = qtest_readl(qts, + can_base_addr + R_RX0_DATA1_OFFSET + 4 * i); + } + + /* Clear the RX interrupt. */ + qtest_writel(qts, CANFD1_BASE_ADDR + R_ICR_OFFSET, ISR_RXOK); +} + +static void write_data(QTestState *qts, uint64_t can_base_addr, + const uint32_t *buf_tx, bool is_canfd_frame) +{ + /* Write the TX register data for CANFD. */ + qtest_writel(qts, can_base_addr + R_TXID_OFFSET, buf_tx[0]); + qtest_writel(qts, can_base_addr + R_TXDLC_OFFSET, buf_tx[1]); + + if (is_canfd_frame) { + for (int i = 0; i < CANFD_FRAME_SIZE - 2; i++) { + qtest_writel(qts, can_base_addr + R_TXDATA1_OFFSET + 4 * i, + buf_tx[2 + i]); + } + } else { + qtest_writel(qts, can_base_addr + R_TXDATA1_OFFSET, buf_tx[2]); + qtest_writel(qts, can_base_addr + R_TXDATA2_OFFSET, buf_tx[3]); + } +} + +static void send_data(QTestState *qts, uint64_t can_base_addr) +{ + uint32_t int_status; + + qtest_writel(qts, can_base_addr + R_TX_READY_REQ_REGISTER, + TX_READY_REG_VAL); + + /* Read the interrupt on CANFD for tx. */ + int_status = qtest_readl(qts, can_base_addr + R_ISR_OFFSET) & ISR_TXOK; + + g_assert_cmpint(int_status, ==, ISR_TXOK); + + /* Clear the interrupt for tx. */ + qtest_writel(qts, CANFD0_BASE_ADDR + R_ICR_OFFSET, ISR_TXOK); +} + +static void match_rx_tx_data(const uint32_t *buf_tx, const uint32_t *buf_rx, + bool is_canfd_frame) +{ + uint16_t size = 0; + uint8_t len = CAN_FRAME_SIZE; + + if (is_canfd_frame) { + len = CANFD_FRAME_SIZE; + } + + while (size < len) { + if (R_RX0_ID_OFFSET + 4 * size == R_RX0_DLC_OFFSET) { + g_assert_cmpint((buf_rx[size] & DLC_FD_BIT_MASK), ==, + (buf_tx[size] & DLC_FD_BIT_MASK)); + } else { + g_assert_cmpint(buf_rx[size], ==, buf_tx[size]); + } + + size++; + } +} +/* + * Xilinx CANFD supports both CAN and CANFD frames. This test will be + * transferring CAN frame i.e. 8 bytes of data from CANFD0 and CANFD1 through + * canbus. CANFD0 initiate the data transfer to can-bus, CANFD1 receives the + * data. Test compares the can frame data sent from CANFD0 and received on + * CANFD1. + */ +static void test_can_data_transfer(void) +{ + uint32_t buf_tx[CAN_FRAME_SIZE] = { 0x5a5bb9a4, 0x80000000, + 0x12345678, 0x87654321 }; + uint32_t buf_rx[CAN_FRAME_SIZE] = { 0x00, 0x00, 0x00, 0x00 }; + uint32_t status = 0; + + generate_random_data(buf_tx, false); + + QTestState *qts = qtest_init("-machine xlnx-versal-virt" + " -object can-bus,id=canbus" + " -machine canbus0=canbus" + " -machine canbus1=canbus" + ); + + configure_canfd(qts, MSR_NORMAL_MODE); + + /* Check if CANFD0 and CANFD1 are in Normal mode. */ + status = qtest_readl(qts, CANFD0_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_NORMAL_MODE); + + status = qtest_readl(qts, CANFD1_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_NORMAL_MODE); + + write_data(qts, CANFD0_BASE_ADDR, buf_tx, false); + + send_data(qts, CANFD0_BASE_ADDR); + read_data(qts, CANFD1_BASE_ADDR, buf_rx, CAN_FRAME_SIZE); + match_rx_tx_data(buf_tx, buf_rx, false); + + qtest_quit(qts); +} + +/* + * This test will be transferring CANFD frame i.e. 64 bytes of data from CANFD0 + * and CANFD1 through canbus. CANFD0 initiate the data transfer to can-bus, + * CANFD1 receives the data. Test compares the CANFD frame data sent from CANFD0 + * with received on CANFD1. + */ +static void test_canfd_data_transfer(void) +{ + uint32_t buf_tx[CANFD_FRAME_SIZE] = { 0x5a5bb9a4, 0xf8000000 }; + uint32_t buf_rx[CANFD_FRAME_SIZE] = { 0x00, 0x00, 0x00, 0x00 }; + uint32_t status = 0; + + generate_random_data(buf_tx, true); + + QTestState *qts = qtest_init("-machine xlnx-versal-virt" + " -object can-bus,id=canbus" + " -machine canbus0=canbus" + " -machine canbus1=canbus" + ); + + configure_canfd(qts, MSR_NORMAL_MODE); + + /* Check if CANFD0 and CANFD1 are in Normal mode. */ + status = qtest_readl(qts, CANFD0_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_NORMAL_MODE); + + status = qtest_readl(qts, CANFD1_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_NORMAL_MODE); + + write_data(qts, CANFD0_BASE_ADDR, buf_tx, true); + + send_data(qts, CANFD0_BASE_ADDR); + read_data(qts, CANFD1_BASE_ADDR, buf_rx, CANFD_FRAME_SIZE); + match_rx_tx_data(buf_tx, buf_rx, true); + + qtest_quit(qts); +} + +/* + * This test is performing loopback mode on CANFD0 and CANFD1. Data sent from + * TX of each CANFD0 and CANFD1 are compared with RX register data for + * respective CANFD Controller. + */ +static void test_can_loopback(void) +{ + uint32_t buf_tx[CANFD_FRAME_SIZE] = { 0x5a5bb9a4, 0xf8000000 }; + uint32_t buf_rx[CANFD_FRAME_SIZE] = { 0x00, 0x00, 0x00, 0x00 }; + uint32_t status = 0; + + generate_random_data(buf_tx, true); + + QTestState *qts = qtest_init("-machine xlnx-versal-virt" + " -object can-bus,id=canbus" + " -machine canbus0=canbus" + " -machine canbus1=canbus" + ); + + configure_canfd(qts, MSR_LOOPBACK_MODE); + + /* Check if CANFD0 and CANFD1 are set in correct loopback mode. */ + status = qtest_readl(qts, CANFD0_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_LOOPBACK_MODE); + + status = qtest_readl(qts, CANFD1_BASE_ADDR + R_SR_OFFSET); + status = status & STATUS_REG_MASK; + g_assert_cmpint(status, ==, STATUS_LOOPBACK_MODE); + + write_data(qts, CANFD0_BASE_ADDR, buf_tx, true); + + send_data(qts, CANFD0_BASE_ADDR); + read_data(qts, CANFD0_BASE_ADDR, buf_rx, CANFD_FRAME_SIZE); + match_rx_tx_data(buf_tx, buf_rx, true); + + generate_random_data(buf_tx, true); + + write_data(qts, CANFD1_BASE_ADDR, buf_tx, true); + + send_data(qts, CANFD1_BASE_ADDR); + read_data(qts, CANFD1_BASE_ADDR, buf_rx, CANFD_FRAME_SIZE); + match_rx_tx_data(buf_tx, buf_rx, true); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/net/canfd/can_data_transfer", test_can_data_transfer); + qtest_add_func("/net/canfd/canfd_data_transfer", test_canfd_data_transfer); + qtest_add_func("/net/canfd/can_loopback", test_can_loopback); + + return g_test_run(); +} diff --git a/tests/qtest/xlnx-versal-trng-test.c b/tests/qtest/xlnx-versal-trng-test.c new file mode 100644 index 0000000000..ba86f39d13 --- /dev/null +++ b/tests/qtest/xlnx-versal-trng-test.c @@ -0,0 +1,488 @@ +/* + * QTests for the Xilinx Versal True Random Number Generator device + * + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +/* Base Address */ +#define TRNG_BASEADDR (0xf1230000) + +/* TRNG_INT_CTRL */ +#define R_TRNG_INT_CTRL (0x0000) +#define TRNG_INT_CTRL_CERTF_RST_MASK (1 << 5) +#define TRNG_INT_CTRL_DTF_RST_MASK (1 << 4) +#define TRNG_INT_CTRL_DONE_RST_MASK (1 << 3) +#define TRNG_INT_CTRL_CERTF_EN_MASK (1 << 2) +#define TRNG_INT_CTRL_DTF_EN_MASK (1 << 1) +#define TRNG_INT_CTRL_DONE_EN_MASK (1) + +/* TRNG_STATUS */ +#define R_TRNG_STATUS (0x0004) +#define TRNG_STATUS_QCNT_SHIFT (9) +#define TRNG_STATUS_QCNT_MASK (7 << TRNG_STATUS_QCNT_SHIFT) +#define TRNG_STATUS_CERTF_MASK (1 << 3) +#define TRNG_STATUS_DTF_MASK (1 << 1) +#define TRNG_STATUS_DONE_MASK (1) + +/* TRNG_CTRL */ +#define R_TRNG_CTRL (0x0008) +#define TRNG_CTRL_PERSODISABLE_MASK (1 << 10) +#define TRNG_CTRL_SINGLEGENMODE_MASK (1 << 9) +#define TRNG_CTRL_PRNGMODE_MASK (1 << 7) +#define TRNG_CTRL_TSTMODE_MASK (1 << 6) +#define TRNG_CTRL_PRNGSTART_MASK (1 << 5) +#define TRNG_CTRL_PRNGXS_MASK (1 << 3) +#define TRNG_CTRL_TRSSEN_MASK (1 << 2) +#define TRNG_CTRL_QERTUEN_MASK (1 << 1) +#define TRNG_CTRL_PRNGSRST_MASK (1) + +/* TRNG_EXT_SEED_0 ... _11 */ +#define R_TRNG_EXT_SEED_0 (0x0040) +#define R_TRNG_EXT_SEED_11 (R_TRNG_EXT_SEED_0 + 4 * 11) + +/* TRNG_PER_STRNG_0 ... 11 */ +#define R_TRNG_PER_STRNG_0 (0x0080) +#define R_TRNG_PER_STRNG_11 (R_TRNG_PER_STRNG_0 + 4 * 11) + +/* TRNG_CORE_OUTPUT */ +#define R_TRNG_CORE_OUTPUT (0x00c0) + +/* TRNG_RESET */ +#define R_TRNG_RESET (0x00d0) +#define TRNG_RESET_VAL_MASK (1) + +/* TRNG_OSC_EN */ +#define R_TRNG_OSC_EN (0x00d4) +#define TRNG_OSC_EN_VAL_MASK (1) + +/* TRNG_TRNG_ISR, _IMR, _IER, _IDR */ +#define R_TRNG_ISR (0x00e0) +#define R_TRNG_IMR (0x00e4) +#define R_TRNG_IER (0x00e8) +#define R_TRNG_IDR (0x00ec) +#define TRNG_IRQ_SLVERR_MASK (1 << 1) +#define TRNG_IRQ_CORE_INT_MASK (1) + +/* + * End test with a formatted error message, by embedding the message + * in a GError. + */ +#define TRNG_FAILED(FMT, ...) \ + do { \ + g_autoptr(GError) err = g_error_new( \ + g_quark_from_static_string(trng_qname), 0, \ + FMT, ## __VA_ARGS__); \ + g_assert_no_error(err); \ + } while (0) + +static const gchar trng_qname[] = "xlnx-versal-trng-test"; + +static const uint32_t prng_seed[12] = { + 0x01234567, 0x12345678, 0x23456789, 0x3456789a, 0x456789ab, 0x56789abc, + 0x76543210, 0x87654321, 0x98765432, 0xa9876543, 0xba987654, 0xfedcba98, +}; + +static const uint32_t pers_str[12] = { + 0x76543210, 0x87654321, 0x98765432, 0xa9876543, 0xba987654, 0xfedcba98, + 0x01234567, 0x12345678, 0x23456789, 0x3456789a, 0x456789ab, 0x56789abc, +}; + +static void trng_test_start(void) +{ + qtest_start("-machine xlnx-versal-virt"); +} + +static void trng_test_stop(void) +{ + qtest_end(); +} + +static void trng_test_set_uint_prop(const char *name, uint64_t value) +{ + const char *path = "/machine/xlnx-versal/trng"; + QDict *response; + + response = qmp("{ 'execute': 'qom-set'," + " 'arguments': {" + " 'path': %s," + " 'property': %s," + " 'value': %llu" + "} }", path, + name, (unsigned long long)value); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); +} + +static void trng_write(unsigned ra, uint32_t val) +{ + writel(TRNG_BASEADDR + ra, val); +} + +static uint32_t trng_read(unsigned ra) +{ + return readl(TRNG_BASEADDR + ra); +} + +static void trng_bit_set(unsigned ra, uint32_t bits) +{ + trng_write(ra, (trng_read(ra) | bits)); +} + +static void trng_bit_clr(unsigned ra, uint32_t bits) +{ + trng_write(ra, (trng_read(ra) & ~bits)); +} + +static void trng_ctrl_set(uint32_t bits) +{ + trng_bit_set(R_TRNG_CTRL, bits); +} + +static void trng_ctrl_clr(uint32_t bits) +{ + trng_bit_clr(R_TRNG_CTRL, bits); +} + +static uint32_t trng_status(void) +{ + return trng_read(R_TRNG_STATUS); +} + +static unsigned trng_qcnt(void) +{ + uint32_t sta = trng_status(); + + return (sta & TRNG_STATUS_QCNT_MASK) >> TRNG_STATUS_QCNT_SHIFT; +} + +static const char *trng_info(void) +{ + uint32_t sta = trng_status(); + uint32_t ctl = trng_read(R_TRNG_CTRL); + + static char info[64]; + + snprintf(info, sizeof(info), "; status=0x%x, ctrl=0x%x", sta, ctl); + return info; +} + +static void trng_check_status(uint32_t status_mask, const char *act) +{ + uint32_t clear_mask = 0; + uint32_t status; + + /* + * Only selected bits are events in R_TRNG_STATUS, and + * clear them needs to go through R_INT_CTRL. + */ + if (status_mask & TRNG_STATUS_CERTF_MASK) { + clear_mask |= TRNG_INT_CTRL_CERTF_RST_MASK; + } + if (status_mask & TRNG_STATUS_DTF_MASK) { + clear_mask |= TRNG_INT_CTRL_DTF_RST_MASK; + } + if (status_mask & TRNG_STATUS_DONE_MASK) { + clear_mask |= TRNG_INT_CTRL_DONE_RST_MASK; + } + + status = trng_status(); + if ((status & status_mask) != status_mask) { + TRNG_FAILED("%s: Status bitmask 0x%x failed to be 1%s", + act, status_mask, trng_info()); + } + + /* Remove event */ + trng_bit_set(R_TRNG_INT_CTRL, clear_mask); + + if (!!(trng_read(R_TRNG_STATUS) & status_mask)) { + TRNG_FAILED("%s: Event 0x%0x stuck at 1 after clear: %s", + act, status_mask, trng_info()); + } +} + +static void trng_check_done_status(const char *act) +{ + trng_check_status(TRNG_STATUS_DONE_MASK, act); +} + +static void trng_check_dtf_status(void) +{ + trng_check_status(TRNG_STATUS_DTF_MASK, "DTF injection"); +} + +static void trng_check_certf_status(void) +{ + trng_check_status(TRNG_STATUS_CERTF_MASK, "CERTF injection"); +} + +static void trng_reset(void) +{ + trng_write(R_TRNG_RESET, TRNG_RESET_VAL_MASK); + trng_write(R_TRNG_RESET, 0); +} + +static void trng_load(unsigned r0, const uint32_t *b384) +{ + static const uint32_t zero[12] = { 0 }; + unsigned k; + + if (!b384) { + b384 = zero; + } + + for (k = 0; k < 12; k++) { + trng_write(r0 + 4 * k, b384[k]); + } +} + +static void trng_reseed(const uint32_t *seed) +{ + const char *act; + uint32_t ctl; + + ctl = TRNG_CTRL_PRNGSTART_MASK | + TRNG_CTRL_PRNGXS_MASK | + TRNG_CTRL_TRSSEN_MASK; + + trng_ctrl_clr(ctl | TRNG_CTRL_PRNGMODE_MASK); + + if (seed) { + trng_load(R_TRNG_EXT_SEED_0, seed); + act = "Reseed PRNG"; + ctl &= ~TRNG_CTRL_TRSSEN_MASK; + } else { + trng_write(R_TRNG_OSC_EN, TRNG_OSC_EN_VAL_MASK); + act = "Reseed TRNG"; + ctl &= ~TRNG_CTRL_PRNGXS_MASK; + } + + trng_ctrl_set(ctl); + trng_check_done_status(act); + trng_ctrl_clr(TRNG_CTRL_PRNGSTART_MASK); +} + +static void trng_generate(bool auto_enb) +{ + uint32_t ctl; + + ctl = TRNG_CTRL_PRNGSTART_MASK | TRNG_CTRL_SINGLEGENMODE_MASK; + trng_ctrl_clr(ctl); + + if (auto_enb) { + ctl &= ~TRNG_CTRL_SINGLEGENMODE_MASK; + } + + trng_ctrl_set(ctl | TRNG_CTRL_PRNGMODE_MASK); + + trng_check_done_status("Generate"); + g_assert(trng_qcnt() != 7); +} + +static size_t trng_collect(uint32_t *rnd, size_t cnt) +{ + size_t i; + + for (i = 0; i < cnt; i++) { + if (trng_qcnt() == 0) { + return i; + } + + rnd[i] = trng_read(R_TRNG_CORE_OUTPUT); + } + + return i; +} + +/* These tests all generate 512 bits of random data with the device */ +#define TEST_DATA_WORDS (512 / 32) + +static void trng_test_autogen(void) +{ + const size_t cnt = TEST_DATA_WORDS; + uint32_t rng[TEST_DATA_WORDS], prng[TEST_DATA_WORDS]; + size_t n; + + trng_reset(); + + /* PRNG run #1 */ + trng_reseed(prng_seed); + trng_generate(true); + + n = trng_collect(prng, cnt); + if (n != cnt) { + TRNG_FAILED("PRNG_1 Auto-gen test failed: expected = %u, got = %u", + (unsigned)cnt, (unsigned)n); + } + + /* TRNG, should not match PRNG */ + trng_reseed(NULL); + trng_generate(true); + + n = trng_collect(rng, cnt); + if (n != cnt) { + TRNG_FAILED("TRNG Auto-gen test failed: expected = %u, got = %u", + (unsigned)cnt, (unsigned)n); + } + + /* PRNG #2: should matches run #1 */ + trng_reseed(prng_seed); + trng_generate(true); + + n = trng_collect(rng, cnt); + if (n != cnt) { + TRNG_FAILED("PRNG_2 Auto-gen test failed: expected = %u, got = %u", + (unsigned)cnt, (unsigned)n); + } + + if (memcmp(rng, prng, sizeof(rng))) { + TRNG_FAILED("PRNG_2 Auto-gen test failed: does not match PRNG_1"); + } +} + +static void trng_test_oneshot(void) +{ + const size_t cnt = TEST_DATA_WORDS; + uint32_t rng[TEST_DATA_WORDS]; + size_t n; + + trng_reset(); + + /* PRNG run #1 */ + trng_reseed(prng_seed); + trng_generate(false); + + n = trng_collect(rng, cnt); + if (n == cnt) { + TRNG_FAILED("PRNG_1 One-shot gen test failed"); + } + + /* TRNG, should not match PRNG */ + trng_reseed(NULL); + trng_generate(false); + + n = trng_collect(rng, cnt); + if (n == cnt) { + TRNG_FAILED("TRNG One-shot test failed"); + } +} + +static void trng_test_per_str(void) +{ + const size_t cnt = TEST_DATA_WORDS; + uint32_t rng[TEST_DATA_WORDS], prng[TEST_DATA_WORDS]; + size_t n; + + trng_reset(); + + /* #1: disabled */ + trng_ctrl_set(TRNG_CTRL_PERSODISABLE_MASK); + trng_reseed(prng_seed); + trng_ctrl_clr(TRNG_CTRL_PERSODISABLE_MASK); + + trng_generate(true); + n = trng_collect(prng, cnt); + g_assert_cmpuint(n, ==, cnt); + + /* #2: zero string should match personalization disabled */ + trng_load(R_TRNG_PER_STRNG_0, NULL); + trng_reseed(prng_seed); + + trng_generate(true); + n = trng_collect(rng, cnt); + g_assert_cmpuint(n, ==, cnt); + + if (memcmp(rng, prng, sizeof(rng))) { + TRNG_FAILED("Failed: PER_DISABLE != PER_STRNG_ALL_ZERO"); + } + + /* #3: non-zero string should not match personalization disabled */ + trng_load(R_TRNG_PER_STRNG_0, pers_str); + trng_reseed(prng_seed); + + trng_generate(true); + n = trng_collect(rng, cnt); + g_assert_cmpuint(n, ==, cnt); + + if (!memcmp(rng, prng, sizeof(rng))) { + TRNG_FAILED("Failed: PER_DISABLE == PER_STRNG_NON_ZERO"); + } +} + +static void trng_test_forced_prng(void) +{ + const char *prop = "forced-prng"; + const uint64_t seed = 0xdeadbeefbad1bad0ULL; + + const size_t cnt = TEST_DATA_WORDS; + uint32_t rng[TEST_DATA_WORDS], prng[TEST_DATA_WORDS]; + size_t n; + + trng_reset(); + trng_test_set_uint_prop(prop, seed); + + /* TRNG run #1 */ + trng_reset(); + trng_reseed(NULL); + trng_generate(true); + + n = trng_collect(prng, cnt); + g_assert_cmpuint(n, ==, cnt); + + /* TRNG run #2 should match run #1 */ + trng_reset(); + trng_reseed(NULL); + trng_generate(true); + + n = trng_collect(rng, cnt); + g_assert_cmpuint(n, ==, cnt); + + if (memcmp(rng, prng, sizeof(rng))) { + TRNG_FAILED("Forced-prng test failed: results do not match"); + } +} + +static void trng_test_fault_events(void) +{ + const char *prop = "fips-fault-events"; + + trng_reset(); + + /* Fault events only when TRSS is enabled */ + trng_write(R_TRNG_OSC_EN, TRNG_OSC_EN_VAL_MASK); + trng_ctrl_set(TRNG_CTRL_TRSSEN_MASK); + + trng_test_set_uint_prop(prop, TRNG_STATUS_CERTF_MASK); + trng_check_certf_status(); + + trng_test_set_uint_prop(prop, TRNG_STATUS_DTF_MASK); + trng_check_dtf_status(); + + trng_reset(); +} + +int main(int argc, char **argv) +{ + int rc; + + g_test_init(&argc, &argv, NULL); + + #define TRNG_TEST_ADD(n) \ + qtest_add_func("/hw/misc/xlnx-versal-trng/" #n, trng_test_ ## n); + TRNG_TEST_ADD(autogen); + TRNG_TEST_ADD(oneshot); + TRNG_TEST_ADD(per_str); + TRNG_TEST_ADD(forced_prng); + TRNG_TEST_ADD(fault_events); + #undef TRNG_TEST_ADD + + trng_test_start(); + rc = g_test_run(); + trng_test_stop(); + + return rc; +} diff --git a/tests/requirements.txt b/tests/requirements.txt deleted file mode 100644 index 0ba561b6bd..0000000000 --- a/tests/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -# 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 -# Note that qemu.git/python/ is always implicitly installed. -avocado-framework==88.1 -pycdlib==1.11.0 diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index 1c605d5e19..9a67a984ea 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -31,7 +31,7 @@ all: -include ../config-host.mak --include ../config-$(TARGET).mak +-include config-target.mak # Get semihosting definitions for user-mode emulation ifeq ($(filter %-softmmu, $(TARGET)),) @@ -40,15 +40,25 @@ endif # for including , in command strings COMMA := , +NULL := +SPACE := $(NULL) # +TARGET_PREFIX=tests/tcg/$(TARGET):$(SPACE) -quiet-command = $(if $(V),$1,$(if $(2),@printf " %-7s %s\n" $2 $3 && $1, @$1)) +quiet-@ = $(if $(V),,@$(if $1,printf " %-7s %s\n" "$(strip $1)" "$(strip $2)" && )) +quiet-command = $(call quiet-@,$2,$3)$1 + +cc-test = $(CC) -Werror $1 -c -o /dev/null -xc /dev/null >/dev/null 2>&1 +cc-option = if $(call cc-test, $1); then \ + echo "$(TARGET_PREFIX)$1 detected" && echo "$(strip $2)=y" >&3; else \ + echo "$(TARGET_PREFIX)$1 not detected"; fi # $1 = test name, $2 = cmd, $3 = desc ifeq ($(filter %-softmmu, $(TARGET)),) -run-test = $(call quiet-command, timeout --foreground $(TIMEOUT) $2 > $1.out, \ - "TEST",$3) +run-test = $(call quiet-command, timeout -s KILL --foreground $(TIMEOUT) $2 > $1.out, \ + TEST,$(or $3, $*, $<) on $(TARGET_NAME)) else -run-test = $(call quiet-command, timeout --foreground $(TIMEOUT) $2,"TEST",$3) +run-test = $(call quiet-command, timeout -s KILL --foreground $(TIMEOUT) $2, \ + TEST,$(or $3, $*, $<) on $(TARGET_NAME)) endif # $1 = test name, $2 = reference @@ -56,7 +66,7 @@ endif # we know it failed and then force failure at the end. diff-out = $(call quiet-command, diff -q $1.out $2 || \ (diff -u $1.out $2 | head -n 10 && false), \ - "DIFF","$1.out with $2") + DIFF,$1.out with $2) # $1 = test name, $2 = reason skip-test = @printf " SKIPPED %s on $(TARGET_NAME) because %s\n" $1 $2 @@ -83,12 +93,9 @@ QEMU_OPTS= # If TCG debugging, or TCI is enabled things are a lot slower -# ??? Makefile no longer has any indication that TCI is enabled, -# but for the record: -# 15s original default -# 60s with --enable-debug -# 90s with --enable-tcg-interpreter -TIMEOUT=90 +# so we have to set our timeout for that. The current worst case +# offender is the system memory test running under TCI. +TIMEOUT=120 ifeq ($(filter %-softmmu, $(TARGET)),) # The order we include is important. We include multiarch first and @@ -109,10 +116,13 @@ endif %: %.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) +%: %.S + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) else -# For softmmu targets we include a different Makefile fragement as the +# For system targets we include a different Makefile fragment as the # build options for bare programs are usually pretty different. They # are expected to provide their own build recipes. +EXTRA_CFLAGS += -ffreestanding -fno-stack-protector -include $(SRC_PATH)/tests/tcg/minilib/Makefile.target -include $(SRC_PATH)/tests/tcg/multiarch/system/Makefile.softmmu-target -include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.softmmu-target @@ -141,45 +151,50 @@ PLUGINS=$(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c))) # We need to ensure expand the run-plugin-TEST-with-PLUGIN # pre-requistes manually here as we can't use stems to handle it. We -# also add some special helpers the run-plugin- rules can use bellow. +# only expand MULTIARCH_TESTS which are common on most of our targets +# to avoid an exponential explosion as new tests are added. We also +# add some special helpers the run-plugin- rules can use below. +ifneq ($(MULTIARCH_TESTS),) $(foreach p,$(PLUGINS), \ - $(foreach t,$(TESTS),\ + $(foreach t,$(MULTIARCH_TESTS),\ $(eval run-plugin-$(t)-with-$(p): $t $p) \ $(eval RUN_TESTS+=run-plugin-$(t)-with-$(p)))) -endif +endif # MULTIARCH_TESTS +endif # CONFIG_PLUGIN strip-plugin = $(wordlist 1, 1, $(subst -with-, ,$1)) extract-plugin = $(wordlist 2, 2, $(subst -with-, ,$1)) RUN_TESTS+=$(EXTRA_RUNS) +# Some plugins need additional arguments above the default to fully +# exercise things. We can define them on a per-test basis here. +run-plugin-%-with-libmem.so: PLUGIN_ARGS=$(COMMA)inline=true + ifeq ($(filter %-softmmu, $(TARGET)),) run-%: % - $(call run-test, $<, $(QEMU) $(QEMU_OPTS) $<, "$< on $(TARGET_NAME)") + $(call run-test, $<, $(QEMU) $(QEMU_OPTS) $<) run-plugin-%: $(call run-test, $@, $(QEMU) $(QEMU_OPTS) \ - -plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \ + -plugin $(PLUGIN_LIB)/$(call extract-plugin,$@)$(PLUGIN_ARGS) \ -d plugin -D $*.pout \ - $(call strip-plugin,$<), \ - "$* on $(TARGET_NAME)") + $(call strip-plugin,$<)) else run-%: % $(call run-test, $<, \ $(QEMU) -monitor none -display none \ -chardev file$(COMMA)path=$<.out$(COMMA)id=output \ - $(QEMU_OPTS) $<, \ - "$< on $(TARGET_NAME)") + $(QEMU_OPTS) $<) run-plugin-%: $(call run-test, $@, \ $(QEMU) -monitor none -display none \ -chardev file$(COMMA)path=$@.out$(COMMA)id=output \ - -plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \ + -plugin $(PLUGIN_LIB)/$(call extract-plugin,$@)$(PLUGIN_ARGS) \ -d plugin -D $*.pout \ - $(QEMU_OPTS) $(call strip-plugin,$<), \ - "$* on $(TARGET_NAME)") + $(QEMU_OPTS) $(call strip-plugin,$<)) endif gdb-%: % @@ -189,4 +204,14 @@ gdb-%: % run: $(RUN_TESTS) clean: - rm -f $(TESTS) *.o + rm -f $(TESTS) *.o $(CLEANFILES) + +distclean: + rm -f config-cc.mak config-target.mak ../config-$(TARGET).mak + +.PHONY: help +help: + @echo "TCG tests help $(TARGET_NAME)" + @echo "Built with $(CC)" + @echo "Available tests:" + @$(foreach t,$(RUN_TESTS),echo " $t";) diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target index f6fcd4829e..4b03ef602e 100644 --- a/tests/tcg/aarch64/Makefile.softmmu-target +++ b/tests/tcg/aarch64/Makefile.softmmu-target @@ -19,6 +19,11 @@ EXTRA_RUNS+=$(MULTIARCH_RUNS) CFLAGS+=-nostdlib -ggdb -O0 $(MINILIB_INC) LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc +config-cc.mak: Makefile + $(quiet-@)( \ + $(call cc-option,-march=armv8.3-a, CROSS_CC_HAS_ARMV8_3)) 3> config-cc.mak +-include config-cc.mak + # building head blobs .PRECIOUS: $(CRT_OBJS) @@ -31,18 +36,31 @@ LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc memory: CFLAGS+=-DCHECK_UNALIGNED=1 +memory-sve: memory.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +memory-sve: CFLAGS+=-DCHECK_UNALIGNED=1 -march=armv8.1-a+sve -O3 -fno-tree-loop-distribute-patterns + +TESTS+=memory-sve + # Running QEMU_BASE_MACHINE=-M virt -cpu max -display none -QEMU_OPTS+=$(QEMU_BASE_MACHINE) -semihosting-config enable=on,target=native,chardev=output -kernel +QEMU_BASE_ARGS=-semihosting-config enable=on,target=native,chardev=output +QEMU_OPTS+=$(QEMU_BASE_MACHINE) $(QEMU_BASE_ARGS) -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 +QEMU_SEMIHOST=-serial none -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") + $(if $(V),@printf " %-7s %s %s\n" "TO RUN" $(notdir $(QEMU)) "$(QEMU_OPTS) $<") run-plugin-semiconsole-with-%: semiconsole $(call skip-test, $<, "MANUAL ONLY") +# vtimer test needs EL2 +QEMU_EL2_MACHINE=-machine virt,virtualization=on,gic-version=2 -cpu cortex-a57 -smp 4 +run-vtimer: QEMU_OPTS=$(QEMU_EL2_MACHINE) $(QEMU_BASE_ARGS) -kernel + # Simple Record/Replay Test .PHONY: memory-record run-memory-record: memory-record memory @@ -50,8 +68,7 @@ run-memory-record: memory-record memory $(QEMU) -monitor none -display none \ -chardev file$(COMMA)path=$<.out$(COMMA)id=output \ -icount shift=5$(COMMA)rr=record$(COMMA)rrfile=record.bin \ - $(QEMU_OPTS) memory, \ - "$< on $(TARGET_NAME)") + $(QEMU_OPTS) memory) .PHONY: memory-replay run-memory-replay: memory-replay run-memory-record @@ -59,8 +76,7 @@ run-memory-replay: memory-replay run-memory-record $(QEMU) -monitor none -display none \ -chardev file$(COMMA)path=$<.out$(COMMA)id=output \ -icount shift=5$(COMMA)rr=replay$(COMMA)rrfile=record.bin \ - $(QEMU_OPTS) memory, \ - "$< on $(TARGET_NAME)") + $(QEMU_OPTS) memory) EXTRA_RUNS+=run-memory-replay @@ -71,6 +87,4 @@ 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 d6a74d24dc..70d728ae9a 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -9,27 +9,52 @@ AARCH64_SRC=$(SRC_PATH)/tests/tcg/aarch64 VPATH += $(AARCH64_SRC) # Base architecture tests -AARCH64_TESTS=fcvt pcalign-a64 +AARCH64_TESTS=fcvt pcalign-a64 lse2-fault +AARCH64_TESTS += test-2248 test-2150 fcvt: LDFLAGS+=-lm run-fcvt: fcvt - $(call run-test,$<,$(QEMU) $<, "$< on $(TARGET_NAME)") + $(call run-test,$<,$(QEMU) $<) $(call diff-out,$<,$(AARCH64_SRC)/fcvt.ref) +config-cc.mak: Makefile + $(quiet-@)( \ + $(call cc-option,-march=armv8.1-a+sve, CROSS_CC_HAS_SVE); \ + $(call cc-option,-march=armv8.1-a+sve2, CROSS_CC_HAS_SVE2); \ + $(call cc-option,-march=armv8.2-a, CROSS_CC_HAS_ARMV8_2); \ + $(call cc-option,-march=armv8.3-a, CROSS_CC_HAS_ARMV8_3); \ + $(call cc-option,-march=armv8.5-a, CROSS_CC_HAS_ARMV8_5); \ + $(call cc-option,-mbranch-protection=standard, CROSS_CC_HAS_ARMV8_BTI); \ + $(call cc-option,-march=armv8.5-a+memtag, CROSS_CC_HAS_ARMV8_MTE); \ + $(call cc-option,-Wa$(COMMA)-march=armv9-a+sme, CROSS_AS_HAS_ARMV9_SME)) 3> config-cc.mak +-include config-cc.mak + +ifneq ($(CROSS_CC_HAS_ARMV8_2),) +AARCH64_TESTS += dcpop +dcpop: CFLAGS += -march=armv8.2-a +endif +ifneq ($(CROSS_CC_HAS_ARMV8_5),) +AARCH64_TESTS += dcpodp +dcpodp: CFLAGS += -march=armv8.5-a +endif + # Pauth Tests ifneq ($(CROSS_CC_HAS_ARMV8_3),) AARCH64_TESTS += pauth-1 pauth-2 pauth-4 pauth-5 pauth-%: CFLAGS += -march=armv8.3-a -run-pauth-%: QEMU_OPTS += -cpu max -run-plugin-pauth-%: QEMU_OPTS += -cpu max +run-pauth-1: QEMU_OPTS += -cpu max +run-pauth-2: QEMU_OPTS += -cpu max +# Choose a cpu with FEAT_Pauth but without FEAT_FPAC for pauth-[45]. +run-pauth-4: QEMU_OPTS += -cpu neoverse-v1 +run-pauth-5: QEMU_OPTS += -cpu neoverse-v1 endif # BTI Tests # bti-1 tests the elf notes, so we require special compiler support. ifneq ($(CROSS_CC_HAS_ARMV8_BTI),) AARCH64_TESTS += bti-1 bti-3 -bti-1 bti-3: CFLAGS += -mbranch-protection=standard +bti-1 bti-3: CFLAGS += -fno-stack-protector -mbranch-protection=standard bti-1 bti-3: LDFLAGS += -nostdlib endif # bti-2 tests PROT_BTI, so no special compiler support required. @@ -41,21 +66,24 @@ AARCH64_TESTS += mte-1 mte-2 mte-3 mte-4 mte-5 mte-6 mte-7 mte-%: CFLAGS += -march=armv8.5-a+memtag endif -ifneq ($(CROSS_CC_HAS_SVE),) +# SME Tests +ifneq ($(CROSS_AS_HAS_ARMV9_SME),) +AARCH64_TESTS += sme-outprod1 sme-smopa-1 sme-smopa-2 +endif + # 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 +AARCH64_TESTS += test-aes +test-aes: CFLAGS += -O -march=armv8-a+aes +test-aes: test-aes-main.c.inc # Vector SHA1 sha1-vector: CFLAGS=-O3 sha1-vector: sha1.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) run-sha1-vector: sha1-vector run-sha1 - $(call run-test, $<, $(QEMU) $(QEMU_OPTS) $<, "$< on $(TARGET_NAME)") + $(call run-test, $<, $(QEMU) $(QEMU_OPTS) $<) $(call diff-out, sha1-vector, sha1.out) TESTS += sha1-vector @@ -67,22 +95,37 @@ sha512-vector: sha512.c TESTS += sha512-vector -ifneq ($(HAVE_GDB_BIN),) +ifneq ($(CROSS_CC_HAS_SVE),) +# SVE ioctl test +AARCH64_TESTS += sve-ioctls +sve-ioctls: CFLAGS+=-march=armv8.1-a+sve + +sha512-sve: CFLAGS=-O3 -march=armv8.1-a+sve +sha512-sve: sha512.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +sve-str: CFLAGS=-O1 -march=armv8.1-a+sve +sve-str: sve-str.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +TESTS += sha512-sve sve-str + +ifneq ($(GDB),) GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py run-gdbstub-sysregs: sysregs $(call run-test, $@, $(GDB_SCRIPT) \ - --gdb $(HAVE_GDB_BIN) \ + --gdb $(GDB) \ --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ --bin $< --test $(AARCH64_SRC)/gdbstub/test-sve.py, \ - "basic gdbstub SVE support") + basic gdbstub SVE support) run-gdbstub-sve-ioctls: sve-ioctls $(call run-test, $@, $(GDB_SCRIPT) \ - --gdb $(HAVE_GDB_BIN) \ + --gdb $(GDB) \ --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ --bin $< --test $(AARCH64_SRC)/gdbstub/test-sve-ioctl.py, \ - "basic gdbstub SVE ZLEN support") + basic gdbstub SVE ZLEN support) EXTRA_RUNS += run-gdbstub-sysregs run-gdbstub-sve-ioctls endif diff --git a/tests/tcg/aarch64/bti-1.c b/tests/tcg/aarch64/bti-1.c index 61924f0d7a..99a879af23 100644 --- a/tests/tcg/aarch64/bti-1.c +++ b/tests/tcg/aarch64/bti-1.c @@ -2,7 +2,7 @@ * Branch target identification, basic notskip cases. */ -#include "bti-crt.inc.c" +#include "bti-crt.c.inc" static void skip2_sigill(int sig, siginfo_t *info, ucontext_t *uc) { diff --git a/tests/tcg/aarch64/bti-3.c b/tests/tcg/aarch64/bti-3.c index a852856d9a..8c534c09d7 100644 --- a/tests/tcg/aarch64/bti-3.c +++ b/tests/tcg/aarch64/bti-3.c @@ -2,7 +2,7 @@ * BTI vs PACIASP */ -#include "bti-crt.inc.c" +#include "bti-crt.c.inc" static void skip2_sigill(int sig, siginfo_t *info, ucontext_t *uc) { diff --git a/tests/tcg/aarch64/bti-crt.inc.c b/tests/tcg/aarch64/bti-crt.c.inc similarity index 100% rename from tests/tcg/aarch64/bti-crt.inc.c rename to tests/tcg/aarch64/bti-crt.c.inc diff --git a/tests/tcg/aarch64/dcpodp.c b/tests/tcg/aarch64/dcpodp.c new file mode 100644 index 0000000000..2cf7df2e07 --- /dev/null +++ b/tests/tcg/aarch64/dcpodp.c @@ -0,0 +1,63 @@ +/* + * Test execution of DC CVADP instruction. + * + * Copyright (c) 2023 Zhuojia Shen + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include +#include +#include +#include + +#ifndef HWCAP2_DCPODP +#define HWCAP2_DCPODP (1 << 0) +#endif + +bool should_fail = false; + +static void signal_handler(int sig, siginfo_t *si, void *data) +{ + ucontext_t *uc = (ucontext_t *)data; + + if (should_fail) { + uc->uc_mcontext.pc += 4; + } else { + exit(EXIT_FAILURE); + } +} + +static int do_dc_cvadp(void) +{ + struct sigaction sa = { + .sa_flags = SA_SIGINFO, + .sa_sigaction = signal_handler, + }; + + sigemptyset(&sa.sa_mask); + if (sigaction(SIGSEGV, &sa, NULL) < 0) { + perror("sigaction"); + return EXIT_FAILURE; + } + + asm volatile("dc cvadp, %0\n\t" :: "r"(&sa)); + + should_fail = true; + asm volatile("dc cvadp, %0\n\t" :: "r"(NULL)); + should_fail = false; + + return EXIT_SUCCESS; +} + +int main(void) +{ + if (getauxval(AT_HWCAP2) & HWCAP2_DCPODP) { + return do_dc_cvadp(); + } else { + printf("SKIP: no HWCAP2_DCPODP on this system\n"); + return EXIT_SUCCESS; + } +} diff --git a/tests/tcg/aarch64/dcpop.c b/tests/tcg/aarch64/dcpop.c new file mode 100644 index 0000000000..a332a804a4 --- /dev/null +++ b/tests/tcg/aarch64/dcpop.c @@ -0,0 +1,63 @@ +/* + * Test execution of DC CVAP instruction. + * + * Copyright (c) 2023 Zhuojia Shen + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include +#include +#include +#include + +#ifndef HWCAP_DCPOP +#define HWCAP_DCPOP (1 << 16) +#endif + +bool should_fail = false; + +static void signal_handler(int sig, siginfo_t *si, void *data) +{ + ucontext_t *uc = (ucontext_t *)data; + + if (should_fail) { + uc->uc_mcontext.pc += 4; + } else { + exit(EXIT_FAILURE); + } +} + +static int do_dc_cvap(void) +{ + struct sigaction sa = { + .sa_flags = SA_SIGINFO, + .sa_sigaction = signal_handler, + }; + + sigemptyset(&sa.sa_mask); + if (sigaction(SIGSEGV, &sa, NULL) < 0) { + perror("sigaction"); + return EXIT_FAILURE; + } + + asm volatile("dc cvap, %0\n\t" :: "r"(&sa)); + + should_fail = true; + asm volatile("dc cvap, %0\n\t" :: "r"(NULL)); + should_fail = false; + + return EXIT_SUCCESS; +} + +int main(void) +{ + if (getauxval(AT_HWCAP) & HWCAP_DCPOP) { + return do_dc_cvap(); + } else { + printf("SKIP: no HWCAP_DCPOP on this system\n"); + return EXIT_SUCCESS; + } +} diff --git a/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py index b9ef169c1a..a78a3a2514 100644 --- a/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py +++ b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py @@ -8,19 +8,10 @@ from __future__ import print_function # import gdb -import sys +from test_gdbstub import main, report 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"): @@ -64,29 +55,5 @@ def run_test(): gdb.execute("c") -# -# This runs as the script it sourced (via -x, via run-test.py) -# -try: - inferior = gdb.selected_inferior() - 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") - - # 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) +main(run_test, expected_arch="aarch64") diff --git a/tests/tcg/aarch64/gdbstub/test-sve.py b/tests/tcg/aarch64/gdbstub/test-sve.py index b96bdbb99a..84cdcd4a32 100644 --- a/tests/tcg/aarch64/gdbstub/test-sve.py +++ b/tests/tcg/aarch64/gdbstub/test-sve.py @@ -1,25 +1,15 @@ from __future__ import print_function # -# Test the SVE registers are visable and changeable via gdbstub +# Test the SVE registers are visible and changeable via gdbstub # # This is launched via tests/guest-debug/run-test.py # import gdb -import sys +from test_gdbstub import main, report 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" @@ -54,27 +44,5 @@ def run_test(): 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() - 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") - - # 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) +main(run_test, expected_arch="aarch64") diff --git a/tests/tcg/aarch64/lse2-fault.c b/tests/tcg/aarch64/lse2-fault.c new file mode 100644 index 0000000000..2187219a08 --- /dev/null +++ b/tests/tcg/aarch64/lse2-fault.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include + +int main() +{ + int psize = getpagesize(); + int id; + void *p; + + /* + * We need a shared mapping to enter CF_PARALLEL mode. + * The easiest way to get that is shmat. + */ + id = shmget(IPC_PRIVATE, 2 * psize, IPC_CREAT | 0600); + if (id < 0) { + perror("shmget"); + return 2; + } + p = shmat(id, NULL, 0); + if (p == MAP_FAILED) { + perror("shmat"); + return 2; + } + + /* Protect the second page. */ + if (mprotect(p + psize, psize, PROT_NONE) < 0) { + perror("mprotect"); + return 2; + } + + /* + * Load 4 bytes, 6 bytes from the end of the page. + * On success this will load 0 from the newly allocated shm. + */ + return *(int *)(p + psize - 6); +} diff --git a/tests/tcg/aarch64/mte-7.c b/tests/tcg/aarch64/mte-7.c index a981de62d4..04974f9ebb 100644 --- a/tests/tcg/aarch64/mte-7.c +++ b/tests/tcg/aarch64/mte-7.c @@ -19,8 +19,7 @@ int main(int ac, char **av) p = (void *)((unsigned long)p | (1ul << 56)); /* Store tag in sequential granules. */ - asm("stg %0, [%0]" : : "r"(p + 0x0ff0)); - asm("stg %0, [%0]" : : "r"(p + 0x1000)); + asm("stz2g %0, [%0]" : : "r"(p + 0x0ff0)); /* * Perform an unaligned store with tag 1 crossing the pages. diff --git a/tests/tcg/aarch64/pauth-2.c b/tests/tcg/aarch64/pauth-2.c index 978652ede3..89ffdbf1df 100644 --- a/tests/tcg/aarch64/pauth-2.c +++ b/tests/tcg/aarch64/pauth-2.c @@ -1,5 +1,22 @@ #include +#include +#include #include +#include "pauth.h" + + +static void sigill(int sig, siginfo_t *info, void *vuc) +{ + ucontext_t *uc = vuc; + uint64_t test; + + /* There is only one insn below that is allowed to fault. */ + asm volatile("adr %0, auth2_insn" : "=r"(test)); + assert(test == uc->uc_mcontext.pc); + exit(0); +} + +static int pac_feature; void do_test(uint64_t value) { @@ -27,31 +44,52 @@ void do_test(uint64_t value) * An invalid salt usually fails authorization, but again there * is a chance of choosing another salt that works. * Iterate until we find another salt which does fail. + * + * With FEAT_FPAC, this will SIGILL instead of producing a result. */ for (salt2 = salt1 + 1; ; salt2++) { - asm volatile("autda %0, %2" : "=r"(decode) : "0"(encode), "r"(salt2)); + asm volatile("auth2_insn: autda %0, %2" + : "=r"(decode) : "0"(encode), "r"(salt2)); if (decode != value) { break; } } + assert(pac_feature < 4); /* No FEAT_FPAC */ + /* The VA bits, bit 55, and the TBI bits, should be unchanged. */ assert(((decode ^ value) & 0xff80ffffffffffffull) == 0); /* - * Bits [54:53] are an error indicator based on the key used; - * the DA key above is keynumber 0, so error == 0b01. Otherwise - * bit 55 of the original is sign-extended into the rest of the auth. + * Without FEAT_Pauth2, bits [54:53] are an error indicator based on + * the key used; the DA key above is keynumber 0, so error == 0b01. + * Otherwise, bit 55 of the original is sign-extended into the rest + * of the auth. */ - if ((value >> 55) & 1) { - assert(((decode >> 48) & 0xff) == 0b10111111); - } else { - assert(((decode >> 48) & 0xff) == 0b00100000); + if (pac_feature < 3) { + if ((value >> 55) & 1) { + assert(((decode >> 48) & 0xff) == 0b10111111); + } else { + assert(((decode >> 48) & 0xff) == 0b00100000); + } } } int main() { + static const struct sigaction sa = { + .sa_sigaction = sigill, + .sa_flags = SA_SIGINFO + }; + + pac_feature = get_pac_feature(); + assert(pac_feature != 0); + + if (pac_feature >= 4) { + /* FEAT_FPAC */ + sigaction(SIGILL, &sa, NULL); + } + do_test(0); do_test(0xda004acedeadbeefull); return 0; diff --git a/tests/tcg/aarch64/pauth-4.c b/tests/tcg/aarch64/pauth-4.c index 24a639e36c..b254f413af 100644 --- a/tests/tcg/aarch64/pauth-4.c +++ b/tests/tcg/aarch64/pauth-4.c @@ -2,14 +2,24 @@ #include #include #include +#include "pauth.h" #define TESTS 1000 int main() { + char base[TESTS]; int i, count = 0; float perc; - void *base = malloc(TESTS); + int pac_feature = get_pac_feature(); + + /* + * Exit if no PAuth or FEAT_FPAC, which will SIGILL on AUTIA failure + * rather than return an error for us to check below. + */ + if (pac_feature == 0 || pac_feature >= 4) { + return 0; + } for (i = 0; i < TESTS; i++) { uintptr_t in, x, y; @@ -17,7 +27,7 @@ int main() in = i + (uintptr_t) base; asm("mov %0, %[in]\n\t" - "pacia %0, sp\n\t" /* sigill if pauth not supported */ + "pacia %0, sp\n\t" "eor %0, %0, #4\n\t" /* corrupt single bit */ "mov %1, %0\n\t" "autia %1, sp\n\t" /* validate corrupted pointer */ @@ -36,10 +46,10 @@ int main() if (x != y) { count++; } - } + perc = (float) count / (float) TESTS; - printf("Checks Passed: %0.2f%%", perc * 100.0); + printf("Checks Passed: %0.2f%%\n", perc * 100.0); assert(perc > 0.95); return 0; } diff --git a/tests/tcg/aarch64/pauth-5.c b/tests/tcg/aarch64/pauth-5.c index 67c257918b..ed8d5a926b 100644 --- a/tests/tcg/aarch64/pauth-5.c +++ b/tests/tcg/aarch64/pauth-5.c @@ -1,4 +1,5 @@ #include +#include "pauth.h" static int x; @@ -6,6 +7,15 @@ int main() { int *p0 = &x, *p1, *p2, *p3; unsigned long salt = 0; + int pac_feature = get_pac_feature(); + + /* + * Exit if no PAuth or FEAT_FPAC, which will SIGILL on AUTDA failure + * rather than return an error for us to check below. + */ + if (pac_feature == 0 || pac_feature >= 4) { + return 0; + } /* * With TBI enabled and a 48-bit VA, there are 7 bits of auth, and so diff --git a/tests/tcg/aarch64/pauth.h b/tests/tcg/aarch64/pauth.h new file mode 100644 index 0000000000..543b234437 --- /dev/null +++ b/tests/tcg/aarch64/pauth.h @@ -0,0 +1,23 @@ +/* + * Helper for pauth test case + * + * Copyright (c) 2023 Linaro Ltd + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +static int get_pac_feature(void) +{ + unsigned long isar1, isar2; + + assert(getauxval(AT_HWCAP) & HWCAP_CPUID); + + asm("mrs %0, id_aa64isar1_el1" : "=r"(isar1)); + asm("mrs %0, S3_0_C0_C6_2" : "=r"(isar2)); /* id_aa64isar2_el1 */ + + return ((isar1 >> 4) & 0xf) /* APA */ + | ((isar1 >> 8) & 0xf) /* API */ + | ((isar2 >> 12) & 0xf); /* APA3 */ +} diff --git a/tests/tcg/aarch64/semicall.h b/tests/tcg/aarch64/semicall.h index 8a3fce35c5..30d4de9a54 100644 --- a/tests/tcg/aarch64/semicall.h +++ b/tests/tcg/aarch64/semicall.h @@ -1,10 +1,10 @@ /* * Semihosting Tests - AArch64 helper * - * Copyright (c) 2019 + * Copyright (c) 2019, 2024 * Written by Alex Bennée * - * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later */ uintptr_t __semi_call(uintptr_t type, uintptr_t arg0) diff --git a/tests/tcg/aarch64/sme-outprod1.c b/tests/tcg/aarch64/sme-outprod1.c new file mode 100644 index 0000000000..0c814ed529 --- /dev/null +++ b/tests/tcg/aarch64/sme-outprod1.c @@ -0,0 +1,83 @@ +/* + * SME outer product, 1 x 1. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +extern void foo(float *dst); + +asm( +" .arch_extension sme\n" +" .type foo, @function\n" +"foo:\n" +" stp x29, x30, [sp, -80]!\n" +" mov x29, sp\n" +" stp d8, d9, [sp, 16]\n" +" stp d10, d11, [sp, 32]\n" +" stp d12, d13, [sp, 48]\n" +" stp d14, d15, [sp, 64]\n" +" smstart\n" +" ptrue p0.s, vl4\n" +" fmov z0.s, #1.0\n" +/* + * An outer product of a vector of 1.0 by itself should be a matrix of 1.0. + * Note that we are using tile 1 here (za1.s) rather than tile 0. + */ +" zero {za}\n" +" fmopa za1.s, p0/m, p0/m, z0.s, z0.s\n" +/* + * Read the first 4x4 sub-matrix of elements from tile 1: + * Note that za1h should be interchangeable here. + */ +" mov w12, #0\n" +" mova z0.s, p0/m, za1v.s[w12, #0]\n" +" mova z1.s, p0/m, za1v.s[w12, #1]\n" +" mova z2.s, p0/m, za1v.s[w12, #2]\n" +" mova z3.s, p0/m, za1v.s[w12, #3]\n" +/* + * And store them to the input pointer (dst in the C code): + */ +" st1w {z0.s}, p0, [x0]\n" +" add x0, x0, #16\n" +" st1w {z1.s}, p0, [x0]\n" +" add x0, x0, #16\n" +" st1w {z2.s}, p0, [x0]\n" +" add x0, x0, #16\n" +" st1w {z3.s}, p0, [x0]\n" +" smstop\n" +" ldp d8, d9, [sp, 16]\n" +" ldp d10, d11, [sp, 32]\n" +" ldp d12, d13, [sp, 48]\n" +" ldp d14, d15, [sp, 64]\n" +" ldp x29, x30, [sp], 80\n" +" ret\n" +" .size foo, . - foo" +); + +int main() +{ + float dst[16]; + int i, j; + + foo(dst); + + for (i = 0; i < 16; i++) { + if (dst[i] != 1.0f) { + break; + } + } + + if (i == 16) { + return 0; /* success */ + } + + /* failure */ + for (i = 0; i < 4; ++i) { + for (j = 0; j < 4; ++j) { + printf("%f ", (double)dst[i * 4 + j]); + } + printf("\n"); + } + return 1; +} diff --git a/tests/tcg/aarch64/sme-smopa-1.c b/tests/tcg/aarch64/sme-smopa-1.c new file mode 100644 index 0000000000..c62d5e0007 --- /dev/null +++ b/tests/tcg/aarch64/sme-smopa-1.c @@ -0,0 +1,47 @@ +#include +#include + +int main() +{ + static const int cmp[4][4] = { + { 110, 134, 158, 182 }, + { 390, 478, 566, 654 }, + { 670, 822, 974, 1126 }, + { 950, 1166, 1382, 1598 } + }; + int dst[4][4]; + int *tmp = &dst[0][0]; + + asm volatile( + ".arch armv8-r+sme\n\t" + "smstart\n\t" + "index z0.b, #0, #1\n\t" + "movprfx z1, z0\n\t" + "add z1.b, z1.b, #16\n\t" + "ptrue p0.b\n\t" + "smopa za0.s, p0/m, p0/m, z0.b, z1.b\n\t" + "ptrue p0.s, vl4\n\t" + "mov w12, #0\n\t" + "st1w { za0h.s[w12, #0] }, p0, [%0]\n\t" + "add %0, %0, #16\n\t" + "st1w { za0h.s[w12, #1] }, p0, [%0]\n\t" + "add %0, %0, #16\n\t" + "st1w { za0h.s[w12, #2] }, p0, [%0]\n\t" + "add %0, %0, #16\n\t" + "st1w { za0h.s[w12, #3] }, p0, [%0]\n\t" + "smstop" + : "+r"(tmp) : : "memory"); + + if (memcmp(cmp, dst, sizeof(dst)) == 0) { + return 0; + } + + /* See above for correct results. */ + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + printf("%6d", dst[i][j]); + } + printf("\n"); + } + return 1; +} diff --git a/tests/tcg/aarch64/sme-smopa-2.c b/tests/tcg/aarch64/sme-smopa-2.c new file mode 100644 index 0000000000..c9f48c3bfc --- /dev/null +++ b/tests/tcg/aarch64/sme-smopa-2.c @@ -0,0 +1,54 @@ +#include +#include + +int main() +{ + static const long cmp[4][4] = { + { 110, 134, 158, 182 }, + { 390, 478, 566, 654 }, + { 670, 822, 974, 1126 }, + { 950, 1166, 1382, 1598 } + }; + long dst[4][4]; + long *tmp = &dst[0][0]; + long svl; + + /* Validate that we have a wide enough vector for 4 elements. */ + asm(".arch armv8-r+sme-i64\n\trdsvl %0, #1" : "=r"(svl)); + if (svl < 32) { + return 0; + } + + asm volatile( + "smstart\n\t" + "index z0.h, #0, #1\n\t" + "movprfx z1, z0\n\t" + "add z1.h, z1.h, #16\n\t" + "ptrue p0.b\n\t" + "smopa za0.d, p0/m, p0/m, z0.h, z1.h\n\t" + "ptrue p0.d, vl4\n\t" + "mov w12, #0\n\t" + "st1d { za0h.d[w12, #0] }, p0, [%0]\n\t" + "add %0, %0, #32\n\t" + "st1d { za0h.d[w12, #1] }, p0, [%0]\n\t" + "mov w12, #2\n\t" + "add %0, %0, #32\n\t" + "st1d { za0h.d[w12, #0] }, p0, [%0]\n\t" + "add %0, %0, #32\n\t" + "st1d { za0h.d[w12, #1] }, p0, [%0]\n\t" + "smstop" + : "+r"(tmp) : : "memory"); + + if (memcmp(cmp, dst, sizeof(dst)) == 0) { + return 0; + } + + /* See above for correct results. */ + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + printf("%6ld", dst[i][j]); + } + printf("\n"); + } + return 1; +} diff --git a/tests/tcg/aarch64/sve-str.c b/tests/tcg/aarch64/sve-str.c new file mode 100644 index 0000000000..ae271c9d87 --- /dev/null +++ b/tests/tcg/aarch64/sve-str.c @@ -0,0 +1,49 @@ +#include +#include + +#define N (256 + 16) + +static int __attribute__((noinline)) test(int vl) +{ + unsigned char buf[N]; + int err = 0; + + for (int i = 0; i < N; ++i) { + buf[i] = (unsigned char)i; + } + + asm volatile ( + "mov z0.b, #255\n\t" + "str z0, %0" + : : "m" (buf) : "z0", "memory"); + + for (int i = 0; i < vl; ++i) { + if (buf[i] != 0xff) { + fprintf(stderr, "vl %d, index %d, expected 255, got %d\n", + vl, i, buf[i]); + err = 1; + } + } + + for (int i = vl; i < N; ++i) { + if (buf[i] != (unsigned char)i) { + fprintf(stderr, "vl %d, index %d, expected %d, got %d\n", + vl, i, (unsigned char)i, buf[i]); + err = 1; + } + } + + return err; +} + +int main() +{ + int err = 0; + + for (int i = 16; i <= 256; i += 16) { + if (prctl(PR_SVE_SET_VL, i, 0, 0, 0, 0) == i) { + err |= test(i); + } + } + return err; +} diff --git a/tests/tcg/aarch64/sysregs.c b/tests/tcg/aarch64/sysregs.c index 40cf8d2877..301e61d0dd 100644 --- a/tests/tcg/aarch64/sysregs.c +++ b/tests/tcg/aarch64/sysregs.c @@ -22,6 +22,18 @@ #define HWCAP_CPUID (1 << 11) #endif +/* + * Older assemblers don't recognize newer system register names, + * but we can still access them by the Sn_n_Cn_Cn_n syntax. + * This also means we don't need to specifically request that the + * assembler enables whatever architectural features the ID registers + * syntax might be gated behind. + */ +#define SYS_ID_AA64ISAR2_EL1 S3_0_C0_C6_2 +#define SYS_ID_AA64MMFR2_EL1 S3_0_C0_C7_2 +#define SYS_ID_AA64ZFR0_EL1 S3_0_C0_C4_4 +#define SYS_ID_AA64SMFR0_EL1 S3_0_C0_C4_5 + int failed_bit_count; /* Read and print system register `id' value */ @@ -112,18 +124,21 @@ int main(void) * 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)); + get_cpu_reg_check_mask(id_aa64isar0_el1, _m(f0ff,ffff,f0ff,fff0)); + get_cpu_reg_check_mask(id_aa64isar1_el1, _m(00ff,f0ff,ffff,ffff)); + get_cpu_reg_check_mask(SYS_ID_AA64ISAR2_EL1, _m(00ff,0000,00ff,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); + get_cpu_reg_check_mask(id_aa64mmfr0_el1, _m(f000,0000,ff00,0000)); + get_cpu_reg_check_mask(id_aa64mmfr1_el1, _m(0000,f000,0000,0000)); + get_cpu_reg_check_mask(SYS_ID_AA64MMFR2_EL1, _m(0000,000f,0000,0000)); /* 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)); + get_cpu_reg_check_mask(id_aa64pfr1_el1, _m(0000,0000,0f00,0fff)); /* 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_mask(SYS_ID_AA64ZFR0_EL1, _m(0ff0,ff0f,0fff,00ff)); + get_cpu_reg_check_mask(SYS_ID_AA64SMFR0_EL1, _m(8ff1,fcff,0000,0000)); get_cpu_reg_check_zero(id_aa64afr0_el1); get_cpu_reg_check_zero(id_aa64afr1_el1); diff --git a/tests/tcg/aarch64/system/boot.S b/tests/tcg/aarch64/system/boot.S index e190b1efa6..501685d0ec 100644 --- a/tests/tcg/aarch64/system/boot.S +++ b/tests/tcg/aarch64/system/boot.S @@ -9,7 +9,7 @@ /* * Semihosting interface on ARM AArch64 - * See "Semihosting for AArch32 and AArch64 Relase 2.0" by ARM + * See "Semihosting for AArch32 and AArch64 Release 2.0" by ARM * w0 - semihosting call number * x1 - semihosting parameter */ @@ -147,7 +147,7 @@ __start: * T0SZ[5:0] = 2^(64 - 25) * * The size of T0SZ controls what the initial lookup level. It - * would be nice to start at level 2 but unfortunatly for a + * would be nice to start at level 2 but unfortunately for a * flat-mapping on the virt machine we need to handle IA's * with at least 1gb range to see RAM. So we start with a * level 1 lookup. @@ -179,16 +179,17 @@ __start: isb /* - * Enable FP registers. The standard C pre-amble will be + * Enable FP/SVE registers. The standard C pre-amble will be * saving these and A-profile compilers will use AdvSIMD * registers unless we tell it not to. */ mrs x0, cpacr_el1 orr x0, x0, #(3 << 20) + orr x0, x0, #(3 << 16) msr cpacr_el1, x0 /* Setup some stack space and enter the test code. - * Assume everthing except the return value is garbage when we + * Assume everything except the return value is garbage when we * return, we won't need it. */ adrp x0, stack_end diff --git a/tests/tcg/aarch64/system/pauth-3.c b/tests/tcg/aarch64/system/pauth-3.c index 42eff4d5ea..77a467277b 100644 --- a/tests/tcg/aarch64/system/pauth-3.c +++ b/tests/tcg/aarch64/system/pauth-3.c @@ -1,4 +1,4 @@ -#include +#include #include int main() diff --git a/tests/tcg/aarch64/system/semiconsole.c b/tests/tcg/aarch64/system/semiconsole.c index bfe7c9e26b..81324c639f 100644 --- a/tests/tcg/aarch64/system/semiconsole.c +++ b/tests/tcg/aarch64/system/semiconsole.c @@ -6,7 +6,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -#include +#include #include #define SYS_READC 0x7 diff --git a/tests/tcg/aarch64/system/semiheap.c b/tests/tcg/aarch64/system/semiheap.c index 4ed258476d..1a8c0f31a0 100644 --- a/tests/tcg/aarch64/system/semiheap.c +++ b/tests/tcg/aarch64/system/semiheap.c @@ -6,7 +6,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -#include +#include #include #include @@ -73,11 +73,11 @@ int main(int argc, char *argv[argc]) ml_printf("stack: %p <- %p\n", info.stack_limit, info.stack_base); /* finally can we read/write the heap */ - ptr_to_heap = (uint32_t *) info.heap_base; + ptr_to_heap = info.heap_base; for (i = 0; i < 512; i++) { *ptr_to_heap++ = i; } - ptr_to_heap = (uint32_t *) info.heap_base; + ptr_to_heap = info.heap_base; for (i = 0; i < 512; i++) { uint32_t tmp = *ptr_to_heap; if (tmp != i) { @@ -86,7 +86,7 @@ int main(int argc, char *argv[argc]) } ptr_to_heap++; } - ml_printf("r/w to heap upto %p\n", ptr_to_heap); + ml_printf("r/w to heap up to %p\n", ptr_to_heap); ml_printf("Passed HeapInfo checks\n"); return 0; diff --git a/tests/tcg/aarch64/system/vtimer.c b/tests/tcg/aarch64/system/vtimer.c new file mode 100644 index 0000000000..7d725eced3 --- /dev/null +++ b/tests/tcg/aarch64/system/vtimer.c @@ -0,0 +1,48 @@ +/* + * Simple Virtual Timer Test + * + * Copyright (c) 2020 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +/* grabbed from Linux */ +#define __stringify_1(x...) #x +#define __stringify(x...) __stringify_1(x) + +#define read_sysreg(r) ({ \ + uint64_t __val; \ + asm volatile("mrs %0, " __stringify(r) : "=r" (__val)); \ + __val; \ +}) + +#define write_sysreg(r, v) do { \ + uint64_t __val = (uint64_t)(v); \ + asm volatile("msr " __stringify(r) ", %x0" \ + : : "rZ" (__val)); \ +} while (0) + +int main(void) +{ + int i; + + ml_printf("VTimer Test\n"); + + write_sysreg(cntvoff_el2, 1); + write_sysreg(cntv_cval_el0, -1); + write_sysreg(cntv_ctl_el0, 1); + + ml_printf("cntvoff_el2=%lx\n", read_sysreg(cntvoff_el2)); + ml_printf("cntv_cval_el0=%lx\n", read_sysreg(cntv_cval_el0)); + ml_printf("cntv_ctl_el0=%lx\n", read_sysreg(cntv_ctl_el0)); + + /* Now read cval a few times */ + for (i = 0; i < 10; i++) { + ml_printf("%d: cntv_cval_el0=%lx\n", i, read_sysreg(cntv_cval_el0)); + } + + return 0; +} diff --git a/tests/tcg/aarch64/test-2150.c b/tests/tcg/aarch64/test-2150.c new file mode 100644 index 0000000000..fb86c11958 --- /dev/null +++ b/tests/tcg/aarch64/test-2150.c @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* See https://gitlab.com/qemu-project/qemu/-/issues/2150 */ + +int main() +{ + asm volatile( + "movi v6.4s, #1\n" + "movi v7.4s, #0\n" + "sub v6.2d, v7.2d, v6.2d\n" + : : : "v6", "v7"); + return 0; +} diff --git a/tests/tcg/aarch64/test-2248.c b/tests/tcg/aarch64/test-2248.c new file mode 100644 index 0000000000..aac2e17836 --- /dev/null +++ b/tests/tcg/aarch64/test-2248.c @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* See https://gitlab.com/qemu-project/qemu/-/issues/2248 */ + +#include + +__attribute__((noinline)) +long test(long x, long y, long sh) +{ + long r; + asm("cmp %1, %2\n\t" + "cset x12, lt\n\t" + "and w11, w12, #0xff\n\t" + "cmp w11, #0\n\t" + "csetm x14, ne\n\t" + "lsr x13, x14, %3\n\t" + "sxtb %0, w13" + : "=r"(r) + : "r"(x), "r"(y), "r"(sh) + : "x11", "x12", "x13", "x14"); + return r; +} + +int main() +{ + long r = test(0, 1, 2); + assert(r == -1); + return 0; +} diff --git a/tests/tcg/aarch64/test-aes.c b/tests/tcg/aarch64/test-aes.c new file mode 100644 index 0000000000..2cd324f09b --- /dev/null +++ b/tests/tcg/aarch64/test-aes.c @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "../multiarch/test-aes-main.c.inc" + +bool test_SB_SR(uint8_t *o, const uint8_t *i) +{ + /* aese also adds round key, so supply zero. */ + asm("ld1 { v0.16b }, [%1]\n\t" + "movi v1.16b, #0\n\t" + "aese v0.16b, v1.16b\n\t" + "st1 { v0.16b }, [%0]" + : : "r"(o), "r"(i) : "v0", "v1", "memory"); + return true; +} + +bool test_MC(uint8_t *o, const uint8_t *i) +{ + asm("ld1 { v0.16b }, [%1]\n\t" + "aesmc v0.16b, v0.16b\n\t" + "st1 { v0.16b }, [%0]" + : : "r"(o), "r"(i) : "v0", "memory"); + return true; +} + +bool test_SB_SR_MC_AK(uint8_t *o, const uint8_t *i, const uint8_t *k) +{ + return false; +} + +bool test_ISB_ISR(uint8_t *o, const uint8_t *i) +{ + /* aesd also adds round key, so supply zero. */ + asm("ld1 { v0.16b }, [%1]\n\t" + "movi v1.16b, #0\n\t" + "aesd v0.16b, v1.16b\n\t" + "st1 { v0.16b }, [%0]" + : : "r"(o), "r"(i) : "v0", "v1", "memory"); + return true; +} + +bool test_IMC(uint8_t *o, const uint8_t *i) +{ + asm("ld1 { v0.16b }, [%1]\n\t" + "aesimc v0.16b, v0.16b\n\t" + "st1 { v0.16b }, [%0]" + : : "r"(o), "r"(i) : "v0", "memory"); + return true; +} + +bool test_ISB_ISR_AK_IMC(uint8_t *o, const uint8_t *i, const uint8_t *k) +{ + return false; +} + +bool test_ISB_ISR_IMC_AK(uint8_t *o, const uint8_t *i, const uint8_t *k) +{ + return false; +} diff --git a/tests/tcg/alpha/Makefile.target b/tests/tcg/alpha/Makefile.target index a585080328..fdd7ddf64e 100644 --- a/tests/tcg/alpha/Makefile.target +++ b/tests/tcg/alpha/Makefile.target @@ -5,7 +5,7 @@ ALPHA_SRC=$(SRC_PATH)/tests/tcg/alpha VPATH+=$(ALPHA_SRC) -ALPHA_TESTS=hello-alpha test-cond test-cmov test-ovf +ALPHA_TESTS=hello-alpha test-cond test-cmov test-ovf test-cvttq TESTS+=$(ALPHA_TESTS) test-cmov: EXTRA_CFLAGS=-DTEST_CMOV @@ -13,6 +13,3 @@ test-cmov: test-cond.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) run-test-cmov: test-cmov - -# On Alpha Linux only supports 8k pages -EXTRA_RUNS+=run-test-mmap-8192 diff --git a/tests/tcg/alpha/test-cvttq.c b/tests/tcg/alpha/test-cvttq.c new file mode 100644 index 0000000000..d1ad995312 --- /dev/null +++ b/tests/tcg/alpha/test-cvttq.c @@ -0,0 +1,78 @@ +#include + +#define FPCR_SUM (1UL << 63) +#define FPCR_INED (1UL << 62) +#define FPCR_UNFD (1UL << 61) +#define FPCR_UNDZ (1UL << 60) +#define FPCR_DYN_SHIFT 58 +#define FPCR_DYN_CHOPPED (0UL << FPCR_DYN_SHIFT) +#define FPCR_DYN_MINUS (1UL << FPCR_DYN_SHIFT) +#define FPCR_DYN_NORMAL (2UL << FPCR_DYN_SHIFT) +#define FPCR_DYN_PLUS (3UL << FPCR_DYN_SHIFT) +#define FPCR_DYN_MASK (3UL << FPCR_DYN_SHIFT) +#define FPCR_IOV (1UL << 57) +#define FPCR_INE (1UL << 56) +#define FPCR_UNF (1UL << 55) +#define FPCR_OVF (1UL << 54) +#define FPCR_DZE (1UL << 53) +#define FPCR_INV (1UL << 52) +#define FPCR_OVFD (1UL << 51) +#define FPCR_DZED (1UL << 50) +#define FPCR_INVD (1UL << 49) +#define FPCR_DNZ (1UL << 48) +#define FPCR_DNOD (1UL << 47) +#define FPCR_STATUS_MASK (FPCR_IOV | FPCR_INE | FPCR_UNF \ + | FPCR_OVF | FPCR_DZE | FPCR_INV) + +static long test_cvttq(long *ret_e, double d) +{ + unsigned long reset = (FPCR_INED | FPCR_UNFD | FPCR_OVFD | FPCR_DZED | + FPCR_INVD | FPCR_DYN_NORMAL); + long r, e; + + asm("excb\n\t" + "mt_fpcr %3\n\t" + "excb\n\t" + "cvttq/svic %2, %0\n\t" + "excb\n\t" + "mf_fpcr %1\n\t" + "excb\n\t" + : "=f"(r), "=f"(e) + : "f"(d), "f"(reset)); + + *ret_e = e & FPCR_STATUS_MASK; + return r; +} + +int main (void) +{ + static const struct { + double d; + long r; + long e; + } T[] = { + { 1.0, 1, 0 }, + { -1.0, -1, 0 }, + { 1.5, 1, FPCR_INE }, + { 0x1.0p32, 0x0000000100000000ul, 0 }, + { -0x1.0p63, 0x8000000000000000ul, 0 }, + { 0x1.0p63, 0x8000000000000000ul, FPCR_IOV | FPCR_INE }, + { 0x1.0p64, 0x0000000000000000ul, FPCR_IOV | FPCR_INE }, + { 0x1.cccp64, 0xccc0000000000000ul, FPCR_IOV | FPCR_INE }, + { __builtin_inf(), 0, FPCR_INV }, + { __builtin_nan(""), 0, FPCR_INV }, + }; + + int i, err = 0; + + for (i = 0; i < sizeof(T)/sizeof(T[0]); i++) { + long e, r = test_cvttq(&e, T[i].d); + + if (r != T[i].r || e != T[i].e) { + printf("Fail %a: expect (%016lx : %04lx) got (%016lx : %04lx)\n", + T[i].d, T[i].r, T[i].e >> 48, r, e >> 48); + err = 1; + } + } + return err; +} diff --git a/tests/tcg/arm/Makefile.softmmu-target b/tests/tcg/arm/Makefile.softmmu-target index 3fe237ba39..4c9264057f 100644 --- a/tests/tcg/arm/Makefile.softmmu-target +++ b/tests/tcg/arm/Makefile.softmmu-target @@ -3,24 +3,78 @@ # ARM SoftMMU tests - included from tests/tcg/Makefile # -ARM_SRC=$(SRC_PATH)/tests/tcg/arm +ARM_SRC=$(SRC_PATH)/tests/tcg/arm/system # Set search path for all sources VPATH += $(ARM_SRC) -ARM_TESTS=test-armv6m-undef - -TESTS += $(ARM_TESTS) - -CFLAGS+=-Wl,--build-id=none -x assembler-with-cpp -LDFLAGS+=-nostdlib -N -static - -%: %.S %.ld - $(CC) $(CFLAGS) $(ASFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) -T $(ARM_SRC)/$@.ld - # Specific Test Rules -test-armv6m-undef: EXTRA_CFLAGS+=-mcpu=cortex-m0 +test-armv6m-undef: test-armv6m-undef.S + $(CC) -mcpu=cortex-m0 -mfloat-abi=soft \ + -Wl,--build-id=none -x assembler-with-cpp \ + $< -o $@ -nostdlib -N -static \ + -T $(ARM_SRC)/$@.ld run-test-armv6m-undef: QEMU_OPTS+=-semihosting -M microbit -kernel -run-plugin-test-armv6m-undef-%: QEMU_OPTS+=-semihosting -M microbit -kernel + +ARM_TESTS+=test-armv6m-undef + +# These objects provide the basic boot code and helper functions for all tests +CRT_OBJS=boot.o + +ARM_TEST_SRCS=$(wildcard $(ARM_SRC)/*.c) +ARM_TESTS+=$(patsubst $(ARM_SRC)/%.c, %, $(ARM_TEST_SRCS)) + +CRT_PATH=$(ARM_SRC) +LINK_SCRIPT=$(ARM_SRC)/kernel.ld +LDFLAGS=-Wl,-T$(LINK_SCRIPT) +CFLAGS+=-nostdlib -ggdb -O0 $(MINILIB_INC) +LDFLAGS+=-static -nostdlib -N $(CRT_OBJS) $(MINILIB_OBJS) -lgcc + +# building head blobs +.PRECIOUS: $(CRT_OBJS) + +%.o: $(ARM_SRC)/%.S + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@ + +# Build and link the tests +%: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +memory: CFLAGS+=-DCHECK_UNALIGNED=0 + +# Running +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=-serial none -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") + $(if $(V),@printf " %-7s %s %s\n" "TO RUN" $(notdir $(QEMU)) "$(QEMU_OPTS) $<") +run-plugin-semiconsole-with-%: semiconsole + $(call skip-test, $<, "MANUAL ONLY") + +# Simple Record/Replay Test +.PHONY: memory-record +run-memory-record: memory-record memory + $(call run-test, $<, \ + $(QEMU) -monitor none -display none \ + -chardev file$(COMMA)path=$<.out$(COMMA)id=output \ + -icount shift=5$(COMMA)rr=record$(COMMA)rrfile=record.bin \ + $(QEMU_OPTS) memory) + +.PHONY: memory-replay +run-memory-replay: memory-replay run-memory-record + $(call run-test, $<, \ + $(QEMU) -monitor none -display none \ + -chardev file$(COMMA)path=$<.out$(COMMA)id=output \ + -icount shift=5$(COMMA)rr=replay$(COMMA)rrfile=record.bin \ + $(QEMU_OPTS) memory) + +EXTRA_RUNS+=run-memory-replay + +TESTS += $(ARM_TESTS) $(MULTIARCH_TESTS) +EXTRA_RUNS+=$(MULTIARCH_RUNS) diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index 2f815120a5..0a1965fce7 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -12,7 +12,7 @@ float_madds: CFLAGS+=-mfpu=neon-vfpv4 # Basic Hello World ARM_TESTS = hello-arm -hello-arm: CFLAGS+=-marm -ffreestanding +hello-arm: CFLAGS+=-marm -ffreestanding -fno-stack-protector hello-arm: LDFLAGS+=-nostdlib # IWMXT floating point extensions @@ -26,7 +26,7 @@ ARM_TESTS += fcvt fcvt: LDFLAGS+=-lm # fcvt: CFLAGS+=-march=armv8.2-a+fp16 -mfpu=neon-fp-armv8 run-fcvt: fcvt - $(call run-test,fcvt,$(QEMU) $<,"$< on $(TARGET_NAME)") + $(call run-test,fcvt,$(QEMU) $<) $(call diff-out,fcvt,$(ARM_SRC)/fcvt.ref) # PC alignment test @@ -44,13 +44,7 @@ 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-arm-with-%: - $(call run-test, $@, $(QEMU) $(QEMU_OPTS) \ - -plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \ - $(call strip-plugin,$<) 2> $<.err, \ - "$< on $(TARGET_NAME) with $*") + $(call run-test,$<,$(QEMU) $< 2> $<.err) ARM_TESTS += semiconsole-arm @@ -63,9 +57,6 @@ semiconsole-arm: semihosting.c run-semiconsole-arm: semiconsole-arm $(call skip-test, $<, "MANUAL ONLY") -run-plugin-semiconsole-arm-with-%: - $(call skip-test, $<, "MANUAL ONLY") - endif ARM_TESTS += commpage @@ -75,7 +66,7 @@ sha1-vector: CFLAGS=-O3 sha1-vector: sha1.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) run-sha1-vector: sha1-vector run-sha1 - $(call run-test, $<, $(QEMU) $(QEMU_OPTS) $<, "$< on $(TARGET_NAME)") + $(call run-test, $<, $(QEMU) $(QEMU_OPTS) $<) $(call diff-out, sha1-vector, sha1.out) ARM_TESTS += sha1-vector @@ -88,6 +79,3 @@ sha512-vector: sha512.c ARM_TESTS += sha512-vector TESTS += $(ARM_TESTS) - -# On ARM Linux only supports 4k pages -EXTRA_RUNS+=run-test-mmap-4096 diff --git a/tests/tcg/arm/semicall.h b/tests/tcg/arm/semicall.h index ad8ac51310..624937c557 100644 --- a/tests/tcg/arm/semicall.h +++ b/tests/tcg/arm/semicall.h @@ -1,10 +1,10 @@ /* * Semihosting Tests - ARM Helper * - * Copyright (c) 2019 + * Copyright (c) 2019, 2024 * Written by Alex Bennée * - * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later */ uintptr_t __semi_call(uintptr_t type, uintptr_t arg0) diff --git a/tests/tcg/arm/system/boot.S b/tests/tcg/arm/system/boot.S new file mode 100644 index 0000000000..7d43372c66 --- /dev/null +++ b/tests/tcg/arm/system/boot.S @@ -0,0 +1,319 @@ +/* + * Minimal ArmV7 system boot code. + * + * Using semihosting for serial output and exit functions. + */ + +/* + * Semihosting interface on ARM AArch32 + * R0 - semihosting call number + * R1 - semihosting parameter + */ +#define semihosting_call svc 0x123456 +#define SYS_WRITEC 0x03 /* character to debug channel */ +#define SYS_WRITE0 0x04 /* string to debug channel */ +#define SYS_EXIT 0x18 + +#define ADP_Stopped_ApplicationExit 0x20026 +#define ADP_Stopped_InternalError 0x20024 + +/* + * Helper macro for annotating functions with elf type and size. + */ +.macro endf name + .global \name + .type \name, %function + .size \name, . - \name +.endm + + .section .interrupt_vector, "ax" + .align 5 + +vector_table: + b reset /* reset vector */ + b undef_instr /* undefined instruction vector */ + b software_intr /* software interrupt vector */ + b prefetch_abort /* prefetch abort vector */ + b data_abort /* data abort vector */ + nop /* reserved */ + b IRQ_handler /* IRQ vector */ + b FIQ_handler /* FIQ vector */ + +endf vector_table + + .text +__start: + ldr r0, =vector_table + mcr p15, 0, r0, c12, c0, 0 /* Set up VBAR */ + + ldr sp, =stack_end /* Set up the stack */ + bl mmu_setup /* Set up the MMU */ + bl main /* Jump to main */ + +endf __start + +_exit: + cmp r0, #0 + ite EQ // if-then-else. "EQ" is for if equal, else otherwise + ldreq r1, =ADP_Stopped_ApplicationExit // if r0 == 0 + ldrne r1, =ADP_Stopped_InternalError // else + mov r0, #SYS_EXIT + semihosting_call + +endf _exit + +/* + * Helper Functions + */ + +mmu_setup: + /* + * The MMU setup for this is very simple using two stage one + * translations. The first 1Mb section points to the text + * section and the second points to the data and rss. + * Currently the fattest test only needs ~50k for that so we + * have plenty of space. + * + * The short descriptor Section format is as follows: + * + * PA[31:20] - Section Base Address + * NS[19] - Non-secure bit + * 0[18] - Section (1 for Super Section) + * nG[17] - Not global bit + * S[16] - Shareable + * TEX[14:12] - Memory Region Attributes + * AP[15, 11:10] - Access Permission Bits + * IMPDEF[9] + * Domain[8:5] + * XN[4] - Execute never bit + * C[3] - Memory Region Attributes + * B[2] - Memory Region Attributes + * 1[1] + * PXN[0] - Privileged Execute Never + * + * r0 - point at the table + * r1 - address + * r2 - entry + * r3 - common section bits + * r4 - scratch + */ + + /* + * Memory Region Bits + * + * TEX[14:12] = 000 + * C[3] = 1 + * B[2] = 1 + * + * Outer and Inner WB, no write allocate + */ + mov r3, #0 + ldr r4, =(3 << 2) + orr r3, r4, r4 + + /* Section bit */ + orr r3, r3, #2 + + /* Page table setup (identity mapping). */ + ldr r0, =ttb + + /* First block: .text/RO/execute enabled */ + ldr r1, =.text + ldr r2, =0xFFF00000 /* 1MB block alignment */ + and r2, r1, r2 + orr r2, r2, r3 /* common bits */ + orr r2, r2, #(1 << 15) /* AP[2] = 1 */ + orr r2, r2, #(1 << 10) /* AP[0] = 1 => RO @ PL1 */ + + lsr r4, r2, #(20 - 2) + str r2, [r0, r4, lsl #0] /* write entry */ + + /* Second block: .data/RW/no execute */ + ldr r1, =.data + ldr r2, =0xFFF00000 /* 1MB block alignment */ + and r2, r1, r2 + orr r2, r2, r3 /* common bits */ + orr r2, r2, #(1 << 10) /* AP[0] = 1 => RW @ PL1 */ + orr r2, r2, #(1 << 4) /* XN[4] => no execute */ + + lsr r4, r2, #(20 - 2) + str r2, [r0, r4, lsl #0] /* write entry */ + + /* + * DACR - Domain Control + * + * Enable client mode for domain 0 (we don't use any others) + */ + ldr r0, =0x1 + mcr p15, 0, r0, c3, c0, 0 + + /* + * TTCBR - Translation Table Base Control Register + * + * EAE[31] = 0, 32-bit translation, short descriptor format + * N[2:0] = 5 ( TTBRO uses 31:14-5 => 9 bit lookup stage ) + */ + ldr r0, =0x5 + mcr p15, 0, r0, c1, c0, 2 + + /* + * TTBR0 -Translation Table Base Register 0 + * + * [31:9] = Base address of table + * + * QEMU doesn't really care about the cache sharing + * attributes so we don't need to either. + */ + ldr r0, =ttb + mcr p15, 0, r0, c2, c0, 0 + + /* + * SCTLR- System Control Register + * + * TE[30] = 0, exceptions to A32 state + * AFE[29] = 0, AP[0] is the access permissions bit + * EE[25] = 0, Little-endian + * WXN[19] = 0 = no effect, Write does not imply XN (execute never) + * I[12] = Instruction cachability control + * C[2] = Data cachability control + * M[0] = 1, enable stage 1 address translation for EL0/1 + * + * At this point virtual memory is enabled. + */ + ldr r0, =0x1005 + mcr p15, 0, r0, c1, c0, 0 + + isb + + mov pc, lr /* done, return to caller */ + +endf mmu_setup + +/* Output a single character to serial port */ +__sys_outc: + STMFD sp!, {r0-r1} // push r0, r1 onto stack + mov r1, sp + mov r0, #SYS_WRITEC + semihosting_call + LDMFD sp!, {r0-r1} // pop r0, r1 from stack + bx lr + +endf __sys_outc + +reset: + ldr r1, =reset_error + b exception_handler + +endf reset + +undef_instr: + ldr r1, =undef_intr_error + b exception_handler + +endf undef_instr + +software_intr: + ldr r1, =software_intr_error + b exception_handler + +endf software_intr + +prefetch_abort: + ldr r1, =prefetch_abort_error + b exception_handler + +endf prefetch_abort + +data_abort: + ldr r1, =data_abort_error + b exception_handler + +endf data_abort + +IRQ_handler: + ldr r1, =irq_error + b exception_handler + +endf IRQ_handler + +FIQ_handler: + ldr r1, =fiq_error + b exception_handler + +endf FIQ_handler + +/* + * Initiate a exit semihosting call whenever there is any exception + * r1 already holds the string. + */ +exception_handler: + mov r0, #SYS_WRITE0 + semihosting_call + mov r0, #SYS_EXIT + mov r1, #1 + semihosting_call + +endf exception_handler + +/* + * We implement a stub raise() function which errors out as tests + * shouldn't trigger maths errors. + */ + .global raise +raise: + mov r0, #SYS_WRITE0 + ldr r1, =maths_error + semihosting_call + mov r0, #SYS_EXIT + ldr r1, =ADP_Stopped_InternalError + semihosting_call + +endf raise + + .data + +.data + +reset_error: + .ascii "Reset exception occurred.\n\0" + +undef_intr_error: + .ascii "Undefined Instruction Exception Occurred.\n\0" + +software_intr_error: + .ascii "Software Interrupt Occurred.\n\0" + +prefetch_abort_error: + .ascii "Prefetch Abort Occurred.\n\0" + +data_abort_error: + .ascii "Data Abort Occurred.\n\0" + +irq_error: + .ascii "IRQ exception occurred.\n\0" + +fiq_error: + .ascii "FIQ exception occurred.\n\0" + +maths_error: + .ascii "Software maths exception.\n\0" + + + /* + * 1st Stage Translation table + * 4096 entries, indexed by [31:20] + * each entry covers 1Mb of address space + * aligned on 16kb + */ + .align 15 +ttb: + .space (4096 * 4), 0 + + .align 12 + + /* Space for stack */ + .align 5 + .section .bss +stack: + .space 65536, 0 +stack_end: diff --git a/tests/tcg/arm/system/kernel.ld b/tests/tcg/arm/system/kernel.ld new file mode 100644 index 0000000000..7b3a76dcbf --- /dev/null +++ b/tests/tcg/arm/system/kernel.ld @@ -0,0 +1,24 @@ +ENTRY(__start) + +SECTIONS +{ + /* virt machine, RAM starts at 1gb */ + . = (1 << 30); + .text : { + *(.text) + } + .rodata : { + *(.rodata) + } + /* align r/w section to next 2mb */ + . = ALIGN(1 << 21); + .data : { + *(.data) + } + .bss : { + *(.bss) + } + /DISCARD/ : { + *(.ARM.attributes) + } +} diff --git a/tests/tcg/arm/system/semiconsole.c b/tests/tcg/arm/system/semiconsole.c new file mode 100644 index 0000000000..206dd60eed --- /dev/null +++ b/tests/tcg/arm/system/semiconsole.c @@ -0,0 +1,42 @@ +/* + * 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("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)); + + 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/test-armv6m-undef.S b/tests/tcg/arm/system/test-armv6m-undef.S similarity index 100% rename from tests/tcg/arm/test-armv6m-undef.S rename to tests/tcg/arm/system/test-armv6m-undef.S diff --git a/tests/tcg/arm/test-armv6m-undef.ld b/tests/tcg/arm/system/test-armv6m-undef.ld similarity index 100% rename from tests/tcg/arm/test-armv6m-undef.ld rename to tests/tcg/arm/system/test-armv6m-undef.ld diff --git a/tests/tcg/cris/Makefile.target b/tests/tcg/cris/Makefile.target index e72d3cbdb2..713e2a5b6c 100644 --- a/tests/tcg/cris/Makefile.target +++ b/tests/tcg/cris/Makefile.target @@ -30,7 +30,7 @@ AS = $(CC) -x assembler-with-cpp LD = $(CC) # we rely on GCC inline:ing the stuff we tell it to in many places here. -CFLAGS = -Winline -Wall -g -O2 -static +CFLAGS = -Winline -Wall -g -O2 -static -fno-stack-protector NOSTDFLAGS = -nostartfiles -nostdlib ASFLAGS += -mcpu=v10 -g -Wa,-I,$(SRC_PATH)/tests/tcg/cris/bare CRT_FILES = crt.o sys.o @@ -56,4 +56,7 @@ SIMG:=cris-axis-linux-gnu-run # e.g.: make -f ../../tests/tcg/Makefile run-check_orm-on-sim run-%-on-sim: - $(call run-test, $<, $(SIMG) $<, "$< on $(TARGET_NAME) with SIM") + $(call run-test, $<, $(SIMG) $<) + +# We don't currently support the multiarch tests +undefine MULTIARCH_TESTS diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index 23b9870534..f839b2c0d5 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -1,5 +1,5 @@ ## -## Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -19,12 +19,12 @@ EXTRA_RUNS = CFLAGS += -Wno-incompatible-pointer-types -Wno-undefined-internal -CFLAGS += -fno-unroll-loops +CFLAGS += -fno-unroll-loops -fno-stack-protector HEX_SRC=$(SRC_PATH)/tests/tcg/hexagon VPATH += $(HEX_SRC) -first: $(HEX_SRC)/first.S +%: $(HEX_SRC)/%.S $(HEX_SRC)/crt.S $(CC) -static -mv67 -nostdlib $^ -o $@ HEX_TESTS = first @@ -35,6 +35,7 @@ HEX_TESTS += preg_alias HEX_TESTS += dual_stores HEX_TESTS += multi_result HEX_TESTS += mem_noshuf +HEX_TESTS += mem_noshuf_exception HEX_TESTS += circ HEX_TESTS += brev HEX_TESTS += load_unpack @@ -42,10 +43,88 @@ HEX_TESTS += load_align HEX_TESTS += atomics HEX_TESTS += fpstuff HEX_TESTS += overflow +HEX_TESTS += signal_context +HEX_TESTS += reg_mut +HEX_TESTS += read_write_overlap +HEX_TESTS += vector_add_int +HEX_TESTS += scatter_gather +HEX_TESTS += hvx_misc +HEX_TESTS += hvx_histogram +HEX_TESTS += invalid-slots + +run-and-check-exception = $(call run-test,$2,$3 2>$2.stderr; \ + test $$? -eq 1 && grep -q "exception $(strip $1)" $2.stderr) + +run-invalid-slots: invalid-slots + $(call run-and-check-exception, 0x15, $@, $(QEMU) $(QEMU_OPTS) $<) + +HEX_TESTS += test_abs +HEX_TESTS += test_bitcnt +HEX_TESTS += test_bitsplit +HEX_TESTS += test_call +HEX_TESTS += test_clobber +HEX_TESTS += test_cmp +HEX_TESTS += test_dotnew +HEX_TESTS += test_ext +HEX_TESTS += test_fibonacci +HEX_TESTS += test_hl +HEX_TESTS += test_hwloops +HEX_TESTS += test_jmp +HEX_TESTS += test_lsr +HEX_TESTS += test_mpyi +HEX_TESTS += test_packet +HEX_TESTS += test_reorder +HEX_TESTS += test_round +HEX_TESTS += test_vavgw +HEX_TESTS += test_vcmpb +HEX_TESTS += test_vcmpw +HEX_TESTS += test_vlsrw +HEX_TESTS += test_vmaxh +HEX_TESTS += test_vminh +HEX_TESTS += test_vpmpyh +HEX_TESTS += test_vspliceb + +HEX_TESTS += v68_scalar +HEX_TESTS += v68_hvx +HEX_TESTS += v69_hvx +HEX_TESTS += v73_scalar TESTS += $(HEX_TESTS) +atomics: atomics.c hex_test.h +brev: brev.c hex_test.h +circ: circ.c hex_test.h +dual_stores: dual_stores.c hex_test.h +fpstuff: fpstuff.c hex_test.h +hex_sigsegv: hex_sigsegv.c hex_test.h +load_align: load_align.c hex_test.h +load_unpack: load_unpack.c hex_test.h +mem_noshuf_exception: mem_noshuf_exception.c hex_test.h +mem_noshuf: mem_noshuf.c hex_test.h +misc: misc.c hex_test.h +multi_result: multi_result.c hex_test.h +overflow: overflow.c hex_test.h +preg_alias: preg_alias.c hex_test.h +read_write_overlap: read_write_overlap.c hex_test.h +reg_mut: reg_mut.c hex_test.h + # This test has to be compiled for the -mv67t target -usr: usr.c +usr: usr.c hex_test.h $(CC) $(CFLAGS) -mv67t -O2 -Wno-inline-asm -Wno-expansion-to-defined $< -o $@ $(LDFLAGS) +# Build this test with -mv71 to exercise the CABAC instruction +misc: misc.c + $(CC) $(CFLAGS) -mv71 -O2 $< -o $@ $(LDFLAGS) +scatter_gather: CFLAGS += -mhvx +vector_add_int: CFLAGS += -mhvx -fvectorize +hvx_misc: hvx_misc.c hvx_misc.h +hvx_misc: CFLAGS += -mhvx +hvx_histogram: CFLAGS += -mhvx -Wno-gnu-folding-constant +v68_hvx: v68_hvx.c hvx_misc.h v6mpy_ref.c.inc +v68_hvx: CFLAGS += -mhvx -Wno-unused-function +v69_hvx: v69_hvx.c hvx_misc.h +v69_hvx: CFLAGS += -mhvx -Wno-unused-function +v73_scalar: CFLAGS += -Wno-unused-function + +hvx_histogram: hvx_histogram.c hvx_histogram_row.S + $(CC) $(CFLAGS) $(CROSS_CC_GUEST_CFLAGS) $^ -o $@ $(LDFLAGS) diff --git a/tests/tcg/hexagon/atomics.c b/tests/tcg/hexagon/atomics.c index ff1ceee241..1c2169b28b 100644 --- a/tests/tcg/hexagon/atomics.c +++ b/tests/tcg/hexagon/atomics.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,15 +17,19 @@ #include #include +#include #include #include #include #include -/* Using volatile because we are testing atomics */ -static inline int atomic_inc32(volatile int *x) +int err; + +#include "hex_test.h" + +static inline int32_t atomic_inc32(int32_t *x) { - int old, dummy; + int32_t old, dummy; __asm__ __volatile__( "1: %0 = memw_locked(%2)\n\t" " %1 = add(%0, #1)\n\t" @@ -37,10 +41,9 @@ static inline int atomic_inc32(volatile int *x) return old; } -/* Using volatile because we are testing atomics */ -static inline long long atomic_inc64(volatile long long *x) +static inline int64_t atomic_inc64(int64_t *x) { - long long old, dummy; + int64_t old, dummy; __asm__ __volatile__( "1: %0 = memd_locked(%2)\n\t" " %1 = #1\n\t" @@ -53,10 +56,9 @@ static inline long long atomic_inc64(volatile long long *x) return old; } -/* Using volatile because we are testing atomics */ -static inline int atomic_dec32(volatile int *x) +static inline int32_t atomic_dec32(int32_t *x) { - int old, dummy; + int32_t old, dummy; __asm__ __volatile__( "1: %0 = memw_locked(%2)\n\t" " %1 = add(%0, #-1)\n\t" @@ -68,10 +70,9 @@ static inline int atomic_dec32(volatile int *x) return old; } -/* Using volatile because we are testing atomics */ -static inline long long atomic_dec64(volatile long long *x) +static inline int64_t atomic_dec64(int64_t *x) { - long long old, dummy; + int64_t old, dummy; __asm__ __volatile__( "1: %0 = memd_locked(%2)\n\t" " %1 = #-1\n\t" @@ -85,17 +86,12 @@ static inline long long atomic_dec64(volatile long long *x) } #define LOOP_CNT 1000 -/* Using volatile because we are testing atomics */ -volatile int tick32 = 1; -/* Using volatile because we are testing atomics */ -volatile long long tick64 = 1; -int err; +volatile int32_t tick32 = 1; /* Using volatile because we are testing atomics */ +volatile int64_t tick64 = 1; /* Using volatile because we are testing atomics */ void *thread1_func(void *arg) { - int i; - - for (i = 0; i < LOOP_CNT; i++) { + for (int i = 0; i < LOOP_CNT; i++) { atomic_inc32(&tick32); atomic_dec64(&tick64); } @@ -104,8 +100,7 @@ void *thread1_func(void *arg) void *thread2_func(void *arg) { - int i; - for (i = 0; i < LOOP_CNT; i++) { + for (int i = 0; i < LOOP_CNT; i++) { atomic_dec32(&tick32); atomic_inc64(&tick64); } @@ -121,14 +116,8 @@ void test_pthread(void) pthread_join(tid1, NULL); pthread_join(tid2, NULL); - if (tick32 != 1) { - printf("ERROR: tick32 %d != 1\n", tick32); - err++; - } - if (tick64 != 1) { - printf("ERROR: tick64 %lld != 1\n", tick64); - err++; - } + check32(tick32, 1); + check64(tick64, 1); } int main(int argc, char **argv) diff --git a/tests/tcg/hexagon/brev.c b/tests/tcg/hexagon/brev.c index 9736a2405d..6c7b134084 100644 --- a/tests/tcg/hexagon/brev.c +++ b/tests/tcg/hexagon/brev.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,16 +17,19 @@ #include #include +#include int err; +#include "hex_test.h" + #define NBITS 8 #define SIZE (1 << NBITS) -long long dbuf[SIZE] __attribute__((aligned(1 << 16))) = {0}; -int wbuf[SIZE] __attribute__((aligned(1 << 16))) = {0}; -short hbuf[SIZE] __attribute__((aligned(1 << 16))) = {0}; -unsigned char bbuf[SIZE] __attribute__((aligned(1 << 16))) = {0}; +int64_t dbuf[SIZE] __attribute__((aligned(1 << 16))) = {0}; +int32_t wbuf[SIZE] __attribute__((aligned(1 << 16))) = {0}; +int16_t hbuf[SIZE] __attribute__((aligned(1 << 16))) = {0}; +uint8_t bbuf[SIZE] __attribute__((aligned(1 << 16))) = {0}; /* * We use the C preporcessor to deal with the combinations of types @@ -90,11 +93,10 @@ unsigned char bbuf[SIZE] __attribute__((aligned(1 << 16))) = {0}; #define BREV_STORE_wnew(ADDR, VAL, INC) \ BREV_STORE_NEW(w, ADDR, VAL, INC) -int bitreverse(int x) +uint32_t bitreverse(uint32_t x) { - int result = 0; - int i; - for (i = 0; i < NBITS; i++) { + uint32_t result = 0; + for (int i = 0; i < NBITS; i++) { result <<= 1; result |= x & 1; x >>= 1; @@ -102,26 +104,18 @@ int bitreverse(int x) return result; } -int sext8(int x) +int32_t sext8(int32_t x) { return (x << 24) >> 24; } -void check(int i, long long result, long long expect) -{ - if (result != expect) { - printf("ERROR(%d): 0x%04llx != 0x%04llx\n", i, result, expect); - err++; - } -} - #define TEST_BREV_LOAD(SZ, TYPE, BUF, SHIFT, EXP) \ do { \ p = BUF; \ - for (i = 0; i < SIZE; i++) { \ + for (int i = 0; i < SIZE; i++) { \ TYPE result; \ BREV_LOAD_##SZ(result, p, 1 << (SHIFT - NBITS)); \ - check(i, result, EXP); \ + check32(result, EXP); \ } \ } while (0) @@ -129,11 +123,11 @@ void check(int i, long long result, long long expect) do { \ p = BUF; \ memset(BUF, 0xff, sizeof(BUF)); \ - for (i = 0; i < SIZE; i++) { \ + for (int i = 0; i < SIZE; i++) { \ BREV_STORE_##SZ(p, (TYPE)(VAL), 1 << (SHIFT - NBITS)); \ } \ - for (i = 0; i < SIZE; i++) { \ - check(i, BUF[i], bitreverse(i)); \ + for (int i = 0; i < SIZE; i++) { \ + check32(BUF[i], bitreverse(i)); \ } \ } while (0) @@ -141,11 +135,11 @@ void check(int i, long long result, long long expect) do { \ p = BUF; \ memset(BUF, 0xff, sizeof(BUF)); \ - for (i = 0; i < SIZE; i++) { \ + for (int i = 0; i < SIZE; i++) { \ BREV_STORE_##SZ(p, i, 1 << (SHIFT - NBITS)); \ } \ - for (i = 0; i < SIZE; i++) { \ - check(i, BUF[i], bitreverse(i)); \ + for (int i = 0; i < SIZE; i++) { \ + check32(BUF[i], bitreverse(i)); \ } \ } while (0) @@ -158,9 +152,8 @@ int high_half[SIZE]; int main() { void *p; - int i; - for (i = 0; i < SIZE; i++) { + for (int i = 0; i < SIZE; i++) { bbuf[i] = bitreverse(i); hbuf[i] = bitreverse(i); wbuf[i] = bitreverse(i); @@ -168,18 +161,18 @@ int main() high_half[i] = i << 16; } - TEST_BREV_LOAD(b, int, bbuf, 16, sext8(i)); - TEST_BREV_LOAD(ub, int, bbuf, 16, i); - TEST_BREV_LOAD(h, int, hbuf, 15, i); - TEST_BREV_LOAD(uh, int, hbuf, 15, i); - TEST_BREV_LOAD(w, int, wbuf, 14, i); - TEST_BREV_LOAD(d, long long, dbuf, 13, i); + TEST_BREV_LOAD(b, int32_t, bbuf, 16, sext8(i)); + TEST_BREV_LOAD(ub, int32_t, bbuf, 16, i); + TEST_BREV_LOAD(h, int32_t, hbuf, 15, i); + TEST_BREV_LOAD(uh, int32_t, hbuf, 15, i); + TEST_BREV_LOAD(w, int32_t, wbuf, 14, i); + TEST_BREV_LOAD(d, int64_t, dbuf, 13, i); - TEST_BREV_STORE(b, int, bbuf, i, 16); - TEST_BREV_STORE(h, int, hbuf, i, 15); - TEST_BREV_STORE(f, int, hbuf, high_half[i], 15); - TEST_BREV_STORE(w, int, wbuf, i, 14); - TEST_BREV_STORE(d, long long, dbuf, i, 13); + TEST_BREV_STORE(b, int32_t, bbuf, i, 16); + TEST_BREV_STORE(h, int32_t, hbuf, i, 15); + TEST_BREV_STORE(f, int32_t, hbuf, high_half[i], 15); + TEST_BREV_STORE(w, int32_t, wbuf, i, 14); + TEST_BREV_STORE(d, int64_t, dbuf, i, 13); TEST_BREV_STORE_NEW(bnew, bbuf, 16); TEST_BREV_STORE_NEW(hnew, hbuf, 15); diff --git a/tests/tcg/hexagon/circ.c b/tests/tcg/hexagon/circ.c index 354416eb6d..ab949ebef1 100644 --- a/tests/tcg/hexagon/circ.c +++ b/tests/tcg/hexagon/circ.c @@ -16,6 +16,11 @@ */ #include +#include + +int err; + +#include "hex_test.h" #define DEBUG 0 #define DEBUG_PRINTF(...) \ @@ -31,10 +36,10 @@ #define NWORDS (NBYTES / sizeof(int)) #define NDOBLS (NBYTES / sizeof(long long)) -long long dbuf[NDOBLS] __attribute__((aligned(1 << 12))) = {0}; -int wbuf[NWORDS] __attribute__((aligned(1 << 12))) = {0}; -short hbuf[NHALFS] __attribute__((aligned(1 << 12))) = {0}; -unsigned char bbuf[NBYTES] __attribute__((aligned(1 << 12))) = {0}; +int64_t dbuf[NDOBLS] __attribute__((aligned(1 << 12))) = {0}; +int32_t wbuf[NWORDS] __attribute__((aligned(1 << 12))) = {0}; +int16_t hbuf[NHALFS] __attribute__((aligned(1 << 12))) = {0}; +uint8_t bbuf[NBYTES] __attribute__((aligned(1 << 12))) = {0}; /* * We use the C preporcessor to deal with the combinations of types @@ -43,8 +48,7 @@ unsigned char bbuf[NBYTES] __attribute__((aligned(1 << 12))) = {0}; #define INIT(BUF, N) \ void init_##BUF(void) \ { \ - int i; \ - for (i = 0; i < N; i++) { \ + for (int i = 0; i < N; i++) { \ BUF[i] = i; \ } \ } \ @@ -91,7 +95,7 @@ INIT(dbuf, NDOBLS) * mreg[23:17] increment[6:0] * mreg[16:0] circular buffer length */ -static int build_mreg(int inc, int K, int len) +static int32_t build_mreg(int32_t inc, int32_t K, int32_t len) { return ((inc & 0x780) << 21) | ((K & 0xf) << 24) | @@ -213,31 +217,27 @@ static int build_mreg(int inc, int K, int len) CIRC_STORE_NEW_REG(w, VAL, ADDR, START, LEN, INC) -int err; - /* We'll test increments +1 and -1 */ -void check_load(int i, long long result, int inc, int size) +void __check_load(int line, int32_t i, int64_t res, int32_t inc, int32_t size) { - int expect = (i * inc); + int32_t expect = (i * inc); while (expect >= size) { expect -= size; } while (expect < 0) { expect += size; } - if (result != expect) { - printf("ERROR(%d): %lld != %d\n", i, result, expect); - err++; - } + __check32(line, res, expect); } +#define check_load(I, RES, INC, SZ) __check_load(__LINE__, I, RES, INC, SZ) + #define TEST_LOAD_IMM(SZ, TYPE, BUF, BUFSIZE, INC, FMT) \ void circ_test_load_imm_##SZ(void) \ { \ TYPE *p = (TYPE *)BUF; \ - int size = 10; \ - int i; \ - for (i = 0; i < BUFSIZE; i++) { \ + int32_t size = 10; \ + for (int i = 0; i < BUFSIZE; i++) { \ TYPE element; \ CIRC_LOAD_IMM_##SZ(element, p, BUF, size * sizeof(TYPE), (INC)); \ DEBUG_PRINTF("i = %2d, p = 0x%p, element = %2" #FMT "\n", \ @@ -245,7 +245,7 @@ void circ_test_load_imm_##SZ(void) \ check_load(i, element, ((INC) / (int)sizeof(TYPE)), size); \ } \ p = (TYPE *)BUF; \ - for (i = 0; i < BUFSIZE; i++) { \ + for (int i = 0; i < BUFSIZE; i++) { \ TYPE element; \ CIRC_LOAD_IMM_##SZ(element, p, BUF, size * sizeof(TYPE), -(INC)); \ DEBUG_PRINTF("i = %2d, p = 0x%p, element = %2" #FMT "\n", \ @@ -254,20 +254,19 @@ void circ_test_load_imm_##SZ(void) \ } \ } -TEST_LOAD_IMM(b, char, bbuf, NBYTES, 1, d) -TEST_LOAD_IMM(ub, unsigned char, bbuf, NBYTES, 1, d) -TEST_LOAD_IMM(h, short, hbuf, NHALFS, 2, d) -TEST_LOAD_IMM(uh, unsigned short, hbuf, NHALFS, 2, d) -TEST_LOAD_IMM(w, int, wbuf, NWORDS, 4, d) -TEST_LOAD_IMM(d, long long, dbuf, NDOBLS, 8, lld) +TEST_LOAD_IMM(b, int8_t, bbuf, NBYTES, 1, d) +TEST_LOAD_IMM(ub, uint8_t, bbuf, NBYTES, 1, d) +TEST_LOAD_IMM(h, int16_t, hbuf, NHALFS, 2, d) +TEST_LOAD_IMM(uh, uint16_t, hbuf, NHALFS, 2, d) +TEST_LOAD_IMM(w, int32_t, wbuf, NWORDS, 4, d) +TEST_LOAD_IMM(d, int64_t, dbuf, NDOBLS, 8, lld) #define TEST_LOAD_REG(SZ, TYPE, BUF, BUFSIZE, FMT) \ void circ_test_load_reg_##SZ(void) \ { \ TYPE *p = (TYPE *)BUF; \ - int size = 13; \ - int i; \ - for (i = 0; i < BUFSIZE; i++) { \ + int32_t size = 13; \ + for (int i = 0; i < BUFSIZE; i++) { \ TYPE element; \ CIRC_LOAD_REG_##SZ(element, p, BUF, size * sizeof(TYPE), 1); \ DEBUG_PRINTF("i = %2d, p = 0x%p, element = %2" #FMT "\n", \ @@ -275,7 +274,7 @@ void circ_test_load_reg_##SZ(void) \ check_load(i, element, 1, size); \ } \ p = (TYPE *)BUF; \ - for (i = 0; i < BUFSIZE; i++) { \ + for (int i = 0; i < BUFSIZE; i++) { \ TYPE element; \ CIRC_LOAD_REG_##SZ(element, p, BUF, size * sizeof(TYPE), -1); \ DEBUG_PRINTF("i = %2d, p = 0x%p, element = %2" #FMT "\n", \ @@ -284,16 +283,16 @@ void circ_test_load_reg_##SZ(void) \ } \ } -TEST_LOAD_REG(b, char, bbuf, NBYTES, d) -TEST_LOAD_REG(ub, unsigned char, bbuf, NBYTES, d) -TEST_LOAD_REG(h, short, hbuf, NHALFS, d) -TEST_LOAD_REG(uh, unsigned short, hbuf, NHALFS, d) -TEST_LOAD_REG(w, int, wbuf, NWORDS, d) -TEST_LOAD_REG(d, long long, dbuf, NDOBLS, lld) +TEST_LOAD_REG(b, int8_t, bbuf, NBYTES, d) +TEST_LOAD_REG(ub, uint8_t, bbuf, NBYTES, d) +TEST_LOAD_REG(h, int16_t, hbuf, NHALFS, d) +TEST_LOAD_REG(uh, uint16_t, hbuf, NHALFS, d) +TEST_LOAD_REG(w, int32_t, wbuf, NWORDS, d) +TEST_LOAD_REG(d, int64_t, dbuf, NDOBLS, lld) /* The circular stores will wrap around somewhere inside the buffer */ #define CIRC_VAL(SZ, TYPE, BUFSIZE) \ -TYPE circ_val_##SZ(int i, int inc, int size) \ +TYPE circ_val_##SZ(int i, int32_t inc, int32_t size) \ { \ int mod = BUFSIZE % size; \ int elem = i * inc; \ @@ -310,33 +309,25 @@ TYPE circ_val_##SZ(int i, int inc, int size) \ } \ } -CIRC_VAL(b, unsigned char, NBYTES) -CIRC_VAL(h, short, NHALFS) -CIRC_VAL(w, int, NWORDS) -CIRC_VAL(d, long long, NDOBLS) +CIRC_VAL(b, uint8_t, NBYTES) +CIRC_VAL(h, int16_t, NHALFS) +CIRC_VAL(w, int32_t, NWORDS) +CIRC_VAL(d, int64_t, NDOBLS) /* * Circular stores should only write to the first "size" elements of the buffer * the remainder of the elements should have BUF[i] == i */ #define CHECK_STORE(SZ, BUF, BUFSIZE, FMT) \ -void check_store_##SZ(int inc, int size) \ +void check_store_##SZ(int32_t inc, int32_t size) \ { \ - int i; \ - for (i = 0; i < size; i++) { \ + for (int i = 0; i < size; i++) { \ DEBUG_PRINTF(#BUF "[%3d] = 0x%02" #FMT ", guess = 0x%02" #FMT "\n", \ i, BUF[i], circ_val_##SZ(i, inc, size)); \ - if (BUF[i] != circ_val_##SZ(i, inc, size)) { \ - printf("ERROR(%3d): 0x%02" #FMT " != 0x%02" #FMT "\n", \ - i, BUF[i], circ_val_##SZ(i, inc, size)); \ - err++; \ - } \ + check64(BUF[i], circ_val_##SZ(i, inc, size)); \ } \ - for (i = size; i < BUFSIZE; i++) { \ - if (BUF[i] != i) { \ - printf("ERROR(%3d): 0x%02" #FMT " != 0x%02x\n", i, BUF[i], i); \ - err++; \ - } \ + for (int i = size; i < BUFSIZE; i++) { \ + check64(BUF[i], i); \ } \ } @@ -348,12 +339,11 @@ CHECK_STORE(d, dbuf, NDOBLS, llx) #define CIRC_TEST_STORE_IMM(SZ, CHK, TYPE, BUF, BUFSIZE, SHIFT, INC) \ void circ_test_store_imm_##SZ(void) \ { \ - unsigned int size = 27; \ + uint32_t size = 27; \ TYPE *p = BUF; \ TYPE val = 0; \ - int i; \ init_##BUF(); \ - for (i = 0; i < BUFSIZE; i++) { \ + for (int i = 0; i < BUFSIZE; i++) { \ CIRC_STORE_IMM_##SZ(val << SHIFT, p, BUF, size * sizeof(TYPE), INC); \ val++; \ } \ @@ -361,7 +351,7 @@ void circ_test_store_imm_##SZ(void) \ p = BUF; \ val = 0; \ init_##BUF(); \ - for (i = 0; i < BUFSIZE; i++) { \ + for (int i = 0; i < BUFSIZE; i++) { \ CIRC_STORE_IMM_##SZ(val << SHIFT, p, BUF, size * sizeof(TYPE), \ -(INC)); \ val++; \ @@ -369,24 +359,23 @@ void circ_test_store_imm_##SZ(void) \ check_store_##CHK((-(INC) / (int)sizeof(TYPE)), size); \ } -CIRC_TEST_STORE_IMM(b, b, unsigned char, bbuf, NBYTES, 0, 1) -CIRC_TEST_STORE_IMM(h, h, short, hbuf, NHALFS, 0, 2) -CIRC_TEST_STORE_IMM(f, h, short, hbuf, NHALFS, 16, 2) -CIRC_TEST_STORE_IMM(w, w, int, wbuf, NWORDS, 0, 4) -CIRC_TEST_STORE_IMM(d, d, long long, dbuf, NDOBLS, 0, 8) -CIRC_TEST_STORE_IMM(bnew, b, unsigned char, bbuf, NBYTES, 0, 1) -CIRC_TEST_STORE_IMM(hnew, h, short, hbuf, NHALFS, 0, 2) -CIRC_TEST_STORE_IMM(wnew, w, int, wbuf, NWORDS, 0, 4) +CIRC_TEST_STORE_IMM(b, b, uint8_t, bbuf, NBYTES, 0, 1) +CIRC_TEST_STORE_IMM(h, h, int16_t, hbuf, NHALFS, 0, 2) +CIRC_TEST_STORE_IMM(f, h, int16_t, hbuf, NHALFS, 16, 2) +CIRC_TEST_STORE_IMM(w, w, int32_t, wbuf, NWORDS, 0, 4) +CIRC_TEST_STORE_IMM(d, d, int64_t, dbuf, NDOBLS, 0, 8) +CIRC_TEST_STORE_IMM(bnew, b, uint8_t, bbuf, NBYTES, 0, 1) +CIRC_TEST_STORE_IMM(hnew, h, int16_t, hbuf, NHALFS, 0, 2) +CIRC_TEST_STORE_IMM(wnew, w, int32_t, wbuf, NWORDS, 0, 4) #define CIRC_TEST_STORE_REG(SZ, CHK, TYPE, BUF, BUFSIZE, SHIFT) \ void circ_test_store_reg_##SZ(void) \ { \ TYPE *p = BUF; \ - unsigned int size = 19; \ + uint32_t size = 19; \ TYPE val = 0; \ - int i; \ init_##BUF(); \ - for (i = 0; i < BUFSIZE; i++) { \ + for (int i = 0; i < BUFSIZE; i++) { \ CIRC_STORE_REG_##SZ(val << SHIFT, p, BUF, size * sizeof(TYPE), 1); \ val++; \ } \ @@ -394,35 +383,34 @@ void circ_test_store_reg_##SZ(void) \ p = BUF; \ val = 0; \ init_##BUF(); \ - for (i = 0; i < BUFSIZE; i++) { \ + for (int i = 0; i < BUFSIZE; i++) { \ CIRC_STORE_REG_##SZ(val << SHIFT, p, BUF, size * sizeof(TYPE), -1); \ val++; \ } \ check_store_##CHK(-1, size); \ } -CIRC_TEST_STORE_REG(b, b, unsigned char, bbuf, NBYTES, 0) -CIRC_TEST_STORE_REG(h, h, short, hbuf, NHALFS, 0) -CIRC_TEST_STORE_REG(f, h, short, hbuf, NHALFS, 16) -CIRC_TEST_STORE_REG(w, w, int, wbuf, NWORDS, 0) -CIRC_TEST_STORE_REG(d, d, long long, dbuf, NDOBLS, 0) -CIRC_TEST_STORE_REG(bnew, b, unsigned char, bbuf, NBYTES, 0) -CIRC_TEST_STORE_REG(hnew, h, short, hbuf, NHALFS, 0) -CIRC_TEST_STORE_REG(wnew, w, int, wbuf, NWORDS, 0) +CIRC_TEST_STORE_REG(b, b, uint8_t, bbuf, NBYTES, 0) +CIRC_TEST_STORE_REG(h, h, int16_t, hbuf, NHALFS, 0) +CIRC_TEST_STORE_REG(f, h, int16_t, hbuf, NHALFS, 16) +CIRC_TEST_STORE_REG(w, w, int32_t, wbuf, NWORDS, 0) +CIRC_TEST_STORE_REG(d, d, int64_t, dbuf, NDOBLS, 0) +CIRC_TEST_STORE_REG(bnew, b, uint8_t, bbuf, NBYTES, 0) +CIRC_TEST_STORE_REG(hnew, h, int16_t, hbuf, NHALFS, 0) +CIRC_TEST_STORE_REG(wnew, w, int32_t, wbuf, NWORDS, 0) /* Test the old scheme used in Hexagon V3 */ static void circ_test_v3(void) { int *p = wbuf; - int size = 15; + int32_t size = 15; /* set high bit in K to test unsigned extract in fcirc */ - int K = 8; /* 1024 bytes */ - int element; - int i; + int32_t K = 8; /* 1024 bytes */ + int32_t element; init_wbuf(); - for (i = 0; i < NWORDS; i++) { + for (int i = 0; i < NWORDS; i++) { __asm__( "r4 = %2\n\t" "m1 = r4\n\t" diff --git a/tests/tcg/hexagon/crt.S b/tests/tcg/hexagon/crt.S new file mode 100644 index 0000000000..f9e6bc80f7 --- /dev/null +++ b/tests/tcg/hexagon/crt.S @@ -0,0 +1,14 @@ +#define SYS_exit_group 94 + + .text + .globl pass +pass: + r0 = #0 + r6 = #SYS_exit_group + trap0(#1) + + .globl fail +fail: + r0 = #1 + r6 = #SYS_exit_group + trap0(#1) diff --git a/tests/tcg/hexagon/dual_stores.c b/tests/tcg/hexagon/dual_stores.c index a86a381ab9..775458e0fc 100644 --- a/tests/tcg/hexagon/dual_stores.c +++ b/tests/tcg/hexagon/dual_stores.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,13 +16,18 @@ */ #include +#include + +int err; + +#include "hex_test.h" /* * Make sure that two stores in the same packet honor proper * semantics: slot 1 executes first, then slot 0. * This is important when the addresses overlap. */ -static inline void dual_stores(int *p, char *q, int x, char y) +static inline void dual_stores(int32_t *p, int8_t *q, int32_t x, int8_t y) { asm volatile("{\n\t" " memw(%0) = %2\n\t" @@ -33,27 +38,17 @@ static inline void dual_stores(int *p, char *q, int x, char y) } typedef union { - int word; - char byte; + int32_t word; + int8_t byte; } Dual; -int err; - -static void check(Dual d, int expect) -{ - if (d.word != expect) { - printf("ERROR: 0x%08x != 0x%08x\n", d.word, expect); - err++; - } -} - int main() { Dual d; d.word = ~0; dual_stores(&d.word, &d.byte, 0x12345678, 0xff); - check(d, 0x123456ff); + check32(d.word, 0x123456ff); puts(err ? "FAIL" : "PASS"); return err; diff --git a/tests/tcg/hexagon/float_convd.ref b/tests/tcg/hexagon/float_convd.ref new file mode 100644 index 0000000000..aba1e13e35 --- /dev/null +++ b/tests/tcg/hexagon/float_convd.ref @@ -0,0 +1,988 @@ +### Rounding to nearest +from double: f64(nan:0x007ff4000000000000) + to single: f32(-nan:0xffffffff) (INVALID) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffffffff) (OK) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-inf:0xff800000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.3d5054450ed000000000p-1023:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.5731f750864200000000p-1023:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb600000000000000p+1:0x40490fdb) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.00000000000000000000p+31:0x4f000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(inf:0x7f800000) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(-nan:0xffffffff) (OK) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(-nan:0xffffffff) (INVALID) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(-nan:0xffffffff) (INVALID) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +### Rounding upwards +from double: f64(nan:0x007ff4000000000000) + to single: f32(-nan:0xffffffff) (INVALID) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffffffff) (OK) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000200000000000000p-25:0x33000001) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe800000000000000p-25:0x337ffff4) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801c00000000000000p-15:0x387fc00e) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000e00000000000000p-14:0x38800007) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.3d5054450ed000000000p-1023:0x000009ea82a2287680) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.5731f750864200000000p-1023:0x00000ab98fba843210) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0aa00000000000000p+1:0x402df855) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb600000000000000p+1:0x40490fdb) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.00000000000000000000p+31:0x4f000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(inf:0x7f800000) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(-nan:0xffffffff) (OK) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(-nan:0xffffffff) (INVALID) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(-nan:0xffffffff) (INVALID) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +### Rounding downwards +from double: f64(nan:0x007ff4000000000000) + to single: f32(-nan:0xffffffff) (INVALID) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffffffff) (OK) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-inf:0xff800000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x1.00000000000000000000p-149:0x80000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.3d5054450ed000000000p-1023:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.5731f750864200000000p-1023:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb400000000000000p+1:0x40490fda) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.fffffe00000000000000p+30:0x4effffff) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(-nan:0xffffffff) (OK) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(-nan:0xffffffff) (INVALID) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(-nan:0xffffffff) (INVALID) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +### Rounding to zero +from double: f64(nan:0x007ff4000000000000) + to single: f32(-nan:0xffffffff) (INVALID) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffffffff) (OK) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.3d5054450ed000000000p-1023:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.5731f750864200000000p-1023:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb400000000000000p+1:0x40490fda) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.fffffe00000000000000p+30:0x4effffff) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(-nan:0xffffffff) (OK) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(-nan:0xffffffff) (INVALID) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(-nan:0xffffffff) (INVALID) + to int32: -1 (INVALID) + to int64: -1 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) diff --git a/tests/tcg/hexagon/fpstuff.c b/tests/tcg/hexagon/fpstuff.c index 56bf562a40..6aadaccabd 100644 --- a/tests/tcg/hexagon/fpstuff.c +++ b/tests/tcg/hexagon/fpstuff.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2020-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2020-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,89 +20,44 @@ */ #include - -const int FPINVF_BIT = 1; /* Invalid */ -const int FPINVF = 1 << FPINVF_BIT; -const int FPDBZF_BIT = 2; /* Divide by zero */ -const int FPDBZF = 1 << FPDBZF_BIT; -const int FPOVFF_BIT = 3; /* Overflow */ -const int FPOVFF = 1 << FPOVFF_BIT; -const int FPUNFF_BIT = 4; /* Underflow */ -const int FPUNFF = 1 << FPUNFF_BIT; -const int FPINPF_BIT = 5; /* Inexact */ -const int FPINPF = 1 << FPINPF_BIT; - -const int SF_ZERO = 0x00000000; -const int SF_NaN = 0x7fc00000; -const int SF_NaN_special = 0x7f800001; -const int SF_ANY = 0x3f800000; -const int SF_HEX_NAN = 0xffffffff; -const int SF_small_neg = 0xab98fba8; -const int SF_denorm = 0x00000001; -const int SF_random = 0x346001d6; - -const long long DF_QNaN = 0x7ff8000000000000ULL; -const long long DF_SNaN = 0x7ff7000000000000ULL; -const long long DF_ANY = 0x3f80000000000000ULL; -const long long DF_HEX_NAN = 0xffffffffffffffffULL; -const long long DF_small_neg = 0xbd731f7500000000ULL; +#include +#include int err; -#define CLEAR_FPSTATUS \ - "r2 = usr\n\t" \ - "r2 = clrbit(r2, #1)\n\t" \ - "r2 = clrbit(r2, #2)\n\t" \ - "r2 = clrbit(r2, #3)\n\t" \ - "r2 = clrbit(r2, #4)\n\t" \ - "r2 = clrbit(r2, #5)\n\t" \ - "usr = r2\n\t" +#include "hex_test.h" -static void check_fpstatus_bit(int usr, int expect, int flag, const char *n) +static void check_fpstatus_bit(uint32_t usr, uint32_t expect, uint32_t flag, + const char *name) { - int bit = 1 << flag; + uint32_t bit = 1 << flag; if ((usr & bit) != (expect & bit)) { - printf("ERROR %s: usr = %d, expect = %d\n", n, + printf("ERROR %s: usr = %d, expect = %d\n", name, (usr >> flag) & 1, (expect >> flag) & 1); err++; } } -static void check_fpstatus(int usr, int expect) +static void check_fpstatus(uint32_t usr, uint32_t expect) { - check_fpstatus_bit(usr, expect, FPINVF_BIT, "Invalid"); - check_fpstatus_bit(usr, expect, FPDBZF_BIT, "Div by zero"); - check_fpstatus_bit(usr, expect, FPOVFF_BIT, "Overflow"); - check_fpstatus_bit(usr, expect, FPUNFF_BIT, "Underflow"); - check_fpstatus_bit(usr, expect, FPINPF_BIT, "Inexact"); -} - -static void check32(int val, int expect) -{ - if (val != expect) { - printf("ERROR: 0x%x != 0x%x\n", val, expect); - err++; - } -} -static void check64(unsigned long long val, unsigned long long expect) -{ - if (val != expect) { - printf("ERROR: 0x%llx != 0x%llx\n", val, expect); - err++; - } + check_fpstatus_bit(usr, expect, USR_FPINVF_BIT, "Invalid"); + check_fpstatus_bit(usr, expect, USR_FPDBZF_BIT, "Div by zero"); + check_fpstatus_bit(usr, expect, USR_FPOVFF_BIT, "Overflow"); + check_fpstatus_bit(usr, expect, USR_FPUNFF_BIT, "Underflow"); + check_fpstatus_bit(usr, expect, USR_FPINPF_BIT, "Inexact"); } static void check_compare_exception(void) { - int cmp; - int usr; + uint32_t cmp; + uint32_t usr; - /* Check that FP compares are quiet (don't raise any execptions) */ + /* Check that FP compares are quiet (don't raise any exceptions) */ asm (CLEAR_FPSTATUS "p0 = sfcmp.eq(%2, %3)\n\t" "%0 = p0\n\t" "%1 = usr\n\t" - : "=r"(cmp), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY) + : "=r"(cmp), "=r"(usr) : "r"(SF_QNaN), "r"(SF_any) : "r2", "p0", "usr"); check32(cmp, 0); check_fpstatus(usr, 0); @@ -111,7 +66,7 @@ static void check_compare_exception(void) "p0 = sfcmp.gt(%2, %3)\n\t" "%0 = p0\n\t" "%1 = usr\n\t" - : "=r"(cmp), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY) + : "=r"(cmp), "=r"(usr) : "r"(SF_QNaN), "r"(SF_any) : "r2", "p0", "usr"); check32(cmp, 0); check_fpstatus(usr, 0); @@ -120,7 +75,7 @@ static void check_compare_exception(void) "p0 = sfcmp.ge(%2, %3)\n\t" "%0 = p0\n\t" "%1 = usr\n\t" - : "=r"(cmp), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY) + : "=r"(cmp), "=r"(usr) : "r"(SF_QNaN), "r"(SF_any) : "r2", "p0", "usr"); check32(cmp, 0); check_fpstatus(usr, 0); @@ -129,7 +84,7 @@ static void check_compare_exception(void) "p0 = dfcmp.eq(%2, %3)\n\t" "%0 = p0\n\t" "%1 = usr\n\t" - : "=r"(cmp), "=r"(usr) : "r"(DF_QNaN), "r"(DF_ANY) + : "=r"(cmp), "=r"(usr) : "r"(DF_QNaN), "r"(DF_any) : "r2", "p0", "usr"); check32(cmp, 0); check_fpstatus(usr, 0); @@ -138,7 +93,7 @@ static void check_compare_exception(void) "p0 = dfcmp.gt(%2, %3)\n\t" "%0 = p0\n\t" "%1 = usr\n\t" - : "=r"(cmp), "=r"(usr) : "r"(DF_QNaN), "r"(DF_ANY) + : "=r"(cmp), "=r"(usr) : "r"(DF_QNaN), "r"(DF_any) : "r2", "p0", "usr"); check32(cmp, 0); check_fpstatus(usr, 0); @@ -147,7 +102,7 @@ static void check_compare_exception(void) "p0 = dfcmp.ge(%2, %3)\n\t" "%0 = p0\n\t" "%1 = usr\n\t" - : "=r"(cmp), "=r"(usr) : "r"(DF_QNaN), "r"(DF_ANY) + : "=r"(cmp), "=r"(usr) : "r"(DF_QNaN), "r"(DF_any) : "r2", "p0", "usr"); check32(cmp, 0); check_fpstatus(usr, 0); @@ -155,8 +110,8 @@ static void check_compare_exception(void) static void check_sfminmax(void) { - int minmax; - int usr; + uint32_t minmax; + uint32_t usr; /* * Execute sfmin/sfmax instructions with one operand as NaN @@ -167,46 +122,46 @@ static void check_sfminmax(void) asm (CLEAR_FPSTATUS "%0 = sfmin(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(minmax), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY) + : "=r"(minmax), "=r"(usr) : "r"(SF_QNaN), "r"(SF_any) : "r2", "usr"); - check64(minmax, SF_ANY); + check32(minmax, SF_any); check_fpstatus(usr, 0); asm (CLEAR_FPSTATUS "%0 = sfmax(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(minmax), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY) + : "=r"(minmax), "=r"(usr) : "r"(SF_QNaN), "r"(SF_any) : "r2", "usr"); - check64(minmax, SF_ANY); + check32(minmax, SF_any); check_fpstatus(usr, 0); /* * Execute sfmin/sfmax instructions with both operands NaN * Check that - * Result is SF_HEX_NAN + * Result is SF_HEX_NaN * Invalid bit in USR is set */ asm (CLEAR_FPSTATUS "%0 = sfmin(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(minmax), "=r"(usr) : "r"(SF_NaN), "r"(SF_NaN) + : "=r"(minmax), "=r"(usr) : "r"(SF_QNaN), "r"(SF_QNaN) : "r2", "usr"); - check64(minmax, SF_HEX_NAN); + check32(minmax, SF_HEX_NaN); check_fpstatus(usr, 0); asm (CLEAR_FPSTATUS "%0 = sfmax(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(minmax), "=r"(usr) : "r"(SF_NaN), "r"(SF_NaN) + : "=r"(minmax), "=r"(usr) : "r"(SF_QNaN), "r"(SF_QNaN) : "r2", "usr"); - check64(minmax, SF_HEX_NAN); + check32(minmax, SF_HEX_NaN); check_fpstatus(usr, 0); } static void check_dfminmax(void) { - unsigned long long minmax; - int usr; + uint64_t minmax; + uint32_t usr; /* * Execute dfmin/dfmax instructions with one operand as SNaN @@ -217,18 +172,18 @@ static void check_dfminmax(void) asm (CLEAR_FPSTATUS "%0 = dfmin(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(minmax), "=r"(usr) : "r"(DF_SNaN), "r"(DF_ANY) + : "=r"(minmax), "=r"(usr) : "r"(DF_SNaN), "r"(DF_any) : "r2", "usr"); - check64(minmax, DF_ANY); - check_fpstatus(usr, FPINVF); + check64(minmax, DF_any); + check_fpstatus(usr, USR_FPINVF); asm (CLEAR_FPSTATUS "%0 = dfmax(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(minmax), "=r"(usr) : "r"(DF_SNaN), "r"(DF_ANY) + : "=r"(minmax), "=r"(usr) : "r"(DF_SNaN), "r"(DF_any) : "r2", "usr"); - check64(minmax, DF_ANY); - check_fpstatus(usr, FPINVF); + check64(minmax, DF_any); + check_fpstatus(usr, USR_FPINVF); /* * Execute dfmin/dfmax instructions with one operand as QNaN @@ -239,23 +194,23 @@ static void check_dfminmax(void) asm (CLEAR_FPSTATUS "%0 = dfmin(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(minmax), "=r"(usr) : "r"(DF_QNaN), "r"(DF_ANY) + : "=r"(minmax), "=r"(usr) : "r"(DF_QNaN), "r"(DF_any) : "r2", "usr"); - check64(minmax, DF_ANY); + check64(minmax, DF_any); check_fpstatus(usr, 0); asm (CLEAR_FPSTATUS "%0 = dfmax(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(minmax), "=r"(usr) : "r"(DF_QNaN), "r"(DF_ANY) + : "=r"(minmax), "=r"(usr) : "r"(DF_QNaN), "r"(DF_any) : "r2", "usr"); - check64(minmax, DF_ANY); + check64(minmax, DF_any); check_fpstatus(usr, 0); /* * Execute dfmin/dfmax instructions with both operands SNaN * Check that - * Result is DF_HEX_NAN + * Result is DF_HEX_NaN * Invalid bit in USR is set */ asm (CLEAR_FPSTATUS @@ -263,21 +218,21 @@ static void check_dfminmax(void) "%1 = usr\n\t" : "=r"(minmax), "=r"(usr) : "r"(DF_SNaN), "r"(DF_SNaN) : "r2", "usr"); - check64(minmax, DF_HEX_NAN); - check_fpstatus(usr, FPINVF); + check64(minmax, DF_HEX_NaN); + check_fpstatus(usr, USR_FPINVF); asm (CLEAR_FPSTATUS "%0 = dfmax(%2, %3)\n\t" "%1 = usr\n\t" : "=r"(minmax), "=r"(usr) : "r"(DF_SNaN), "r"(DF_SNaN) : "r2", "usr"); - check64(minmax, DF_HEX_NAN); - check_fpstatus(usr, FPINVF); + check64(minmax, DF_HEX_NaN); + check_fpstatus(usr, USR_FPINVF); /* * Execute dfmin/dfmax instructions with both operands QNaN * Check that - * Result is DF_HEX_NAN + * Result is DF_HEX_NaN * No bit in USR is set */ asm (CLEAR_FPSTATUS @@ -285,7 +240,7 @@ static void check_dfminmax(void) "%1 = usr\n\t" : "=r"(minmax), "=r"(usr) : "r"(DF_QNaN), "r"(DF_QNaN) : "r2", "usr"); - check64(minmax, DF_HEX_NAN); + check64(minmax, DF_HEX_NaN); check_fpstatus(usr, 0); asm (CLEAR_FPSTATUS @@ -293,15 +248,15 @@ static void check_dfminmax(void) "%1 = usr\n\t" : "=r"(minmax), "=r"(usr) : "r"(DF_QNaN), "r"(DF_QNaN) : "r2", "usr"); - check64(minmax, DF_HEX_NAN); + check64(minmax, DF_HEX_NaN); check_fpstatus(usr, 0); } static void check_sfrecipa(void) { - int result; - int usr; - int pred; + uint32_t result; + uint32_t usr; + uint32_t pred; /* * Check that sfrecipa doesn't set status bits when @@ -310,25 +265,25 @@ static void check_sfrecipa(void) asm (CLEAR_FPSTATUS "%0,p0 = sfrecipa(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(result), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY) + : "=r"(result), "=r"(usr) : "r"(SF_QNaN), "r"(SF_any) : "r2", "p0", "usr"); - check32(result, SF_HEX_NAN); + check32(result, SF_HEX_NaN); check_fpstatus(usr, 0); asm (CLEAR_FPSTATUS "%0,p0 = sfrecipa(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(result), "=r"(usr) : "r"(SF_ANY), "r"(SF_NaN) + : "=r"(result), "=r"(usr) : "r"(SF_any), "r"(SF_QNaN) : "r2", "p0", "usr"); - check32(result, SF_HEX_NAN); + check32(result, SF_HEX_NaN); check_fpstatus(usr, 0); asm (CLEAR_FPSTATUS "%0,p0 = sfrecipa(%2, %2)\n\t" "%1 = usr\n\t" - : "=r"(result), "=r"(usr) : "r"(SF_NaN) + : "=r"(result), "=r"(usr) : "r"(SF_QNaN) : "r2", "p0", "usr"); - check32(result, SF_HEX_NAN); + check32(result, SF_HEX_NaN); check_fpstatus(usr, 0); /* @@ -338,26 +293,26 @@ static void check_sfrecipa(void) asm (CLEAR_FPSTATUS "%0,p0 = sfrecipa(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(result), "=r"(usr) : "r"(SF_NaN_special), "r"(SF_ANY) + : "=r"(result), "=r"(usr) : "r"(SF_QNaN_special), "r"(SF_any) : "r2", "p0", "usr"); - check32(result, SF_HEX_NAN); - check_fpstatus(usr, FPINVF); + check32(result, SF_HEX_NaN); + check_fpstatus(usr, USR_FPINVF); asm (CLEAR_FPSTATUS "%0,p0 = sfrecipa(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(result), "=r"(usr) : "r"(SF_ANY), "r"(SF_NaN_special) + : "=r"(result), "=r"(usr) : "r"(SF_any), "r"(SF_QNaN_special) : "r2", "p0", "usr"); - check32(result, SF_HEX_NAN); - check_fpstatus(usr, FPINVF); + check32(result, SF_HEX_NaN); + check_fpstatus(usr, USR_FPINVF); asm (CLEAR_FPSTATUS "%0,p0 = sfrecipa(%2, %2)\n\t" "%1 = usr\n\t" - : "=r"(result), "=r"(usr) : "r"(SF_NaN_special) + : "=r"(result), "=r"(usr) : "r"(SF_QNaN_special) : "r2", "p0", "usr"); - check32(result, SF_HEX_NAN); - check_fpstatus(usr, FPINVF); + check32(result, SF_HEX_NaN); + check_fpstatus(usr, USR_FPINVF); /* * Check that sfrecipa properly sets divid-by-zero @@ -368,12 +323,12 @@ static void check_sfrecipa(void) : "=r"(result), "=r"(usr) : "r"(0x885dc960), "r"(0x80000000) : "r2", "p0", "usr"); check32(result, 0x3f800000); - check_fpstatus(usr, FPDBZF); + check_fpstatus(usr, USR_FPDBZF); asm (CLEAR_FPSTATUS "%0,p0 = sfrecipa(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(result), "=r"(usr) : "r"(0x7f800000), "r"(SF_ZERO) + : "=r"(result), "=r"(usr) : "r"(0x7f800000), "r"(SF_zero) : "r2", "p0", "usr"); check32(result, 0x3f800000); check_fpstatus(usr, 0); @@ -392,79 +347,79 @@ static void check_sfrecipa(void) static void check_canonical_NaN(void) { - int sf_result; - unsigned long long df_result; - int usr; + uint32_t sf_result; + uint64_t df_result; + uint32_t usr; - /* Check that each FP instruction properly returns SF_HEX_NAN/DF_HEX_NAN */ + /* Check that each FP instruction properly returns SF_HEX_NaN/DF_HEX_NaN */ asm(CLEAR_FPSTATUS "%0 = sfadd(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(sf_result), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY) + : "=r"(sf_result), "=r"(usr) : "r"(SF_QNaN), "r"(SF_any) : "r2", "usr"); - check32(sf_result, SF_HEX_NAN); + check32(sf_result, SF_HEX_NaN); check_fpstatus(usr, 0); asm(CLEAR_FPSTATUS "%0 = sfsub(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(sf_result), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY) + : "=r"(sf_result), "=r"(usr) : "r"(SF_QNaN), "r"(SF_any) : "r2", "usr"); - check32(sf_result, SF_HEX_NAN); + check32(sf_result, SF_HEX_NaN); check_fpstatus(usr, 0); asm(CLEAR_FPSTATUS "%0 = sfmpy(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(sf_result), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY) + : "=r"(sf_result), "=r"(usr) : "r"(SF_QNaN), "r"(SF_any) : "r2", "usr"); - check32(sf_result, SF_HEX_NAN); + check32(sf_result, SF_HEX_NaN); check_fpstatus(usr, 0); - sf_result = SF_ZERO; + sf_result = SF_zero; asm(CLEAR_FPSTATUS "%0 += sfmpy(%2, %3)\n\t" "%1 = usr\n\t" - : "+r"(sf_result), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY) + : "+r"(sf_result), "=r"(usr) : "r"(SF_QNaN), "r"(SF_any) : "r2", "usr"); - check32(sf_result, SF_HEX_NAN); + check32(sf_result, SF_HEX_NaN); check_fpstatus(usr, 0); - sf_result = SF_ZERO; + sf_result = SF_zero; asm(CLEAR_FPSTATUS "p0 = !cmp.eq(r0, r0)\n\t" "%0 += sfmpy(%2, %3, p0):scale\n\t" "%1 = usr\n\t" - : "+r"(sf_result), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY) + : "+r"(sf_result), "=r"(usr) : "r"(SF_QNaN), "r"(SF_any) : "r2", "usr", "p0"); - check32(sf_result, SF_HEX_NAN); + check32(sf_result, SF_HEX_NaN); check_fpstatus(usr, 0); - sf_result = SF_ZERO; + sf_result = SF_zero; asm(CLEAR_FPSTATUS "%0 -= sfmpy(%2, %3)\n\t" "%1 = usr\n\t" - : "+r"(sf_result), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY) + : "+r"(sf_result), "=r"(usr) : "r"(SF_QNaN), "r"(SF_any) : "r2", "usr"); - check32(sf_result, SF_HEX_NAN); + check32(sf_result, SF_HEX_NaN); check_fpstatus(usr, 0); - sf_result = SF_ZERO; + sf_result = SF_zero; asm(CLEAR_FPSTATUS "%0 += sfmpy(%2, %3):lib\n\t" "%1 = usr\n\t" - : "+r"(sf_result), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY) + : "+r"(sf_result), "=r"(usr) : "r"(SF_QNaN), "r"(SF_any) : "r2", "usr"); - check32(sf_result, SF_HEX_NAN); + check32(sf_result, SF_HEX_NaN); check_fpstatus(usr, 0); - sf_result = SF_ZERO; + sf_result = SF_zero; asm(CLEAR_FPSTATUS "%0 -= sfmpy(%2, %3):lib\n\t" "%1 = usr\n\t" - : "+r"(sf_result), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY) + : "+r"(sf_result), "=r"(usr) : "r"(SF_QNaN), "r"(SF_any) : "r2", "usr"); - check32(sf_result, SF_HEX_NAN); + check32(sf_result, SF_HEX_NaN); check_fpstatus(usr, 0); asm(CLEAR_FPSTATUS @@ -472,38 +427,38 @@ static void check_canonical_NaN(void) "%1 = usr\n\t" : "=r"(sf_result), "=r"(usr) : "r"(DF_QNaN) : "r2", "usr"); - check32(sf_result, SF_HEX_NAN); + check32(sf_result, SF_HEX_NaN); check_fpstatus(usr, 0); asm(CLEAR_FPSTATUS "%0 = dfadd(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(df_result), "=r"(usr) : "r"(DF_QNaN), "r"(DF_ANY) + : "=r"(df_result), "=r"(usr) : "r"(DF_QNaN), "r"(DF_any) : "r2", "usr"); - check64(df_result, DF_HEX_NAN); + check64(df_result, DF_HEX_NaN); check_fpstatus(usr, 0); asm(CLEAR_FPSTATUS "%0 = dfsub(%2, %3)\n\t" "%1 = usr\n\t" - : "=r"(df_result), "=r"(usr) : "r"(DF_QNaN), "r"(DF_ANY) + : "=r"(df_result), "=r"(usr) : "r"(DF_QNaN), "r"(DF_any) : "r2", "usr"); - check64(df_result, DF_HEX_NAN); + check64(df_result, DF_HEX_NaN); check_fpstatus(usr, 0); asm(CLEAR_FPSTATUS "%0 = convert_sf2df(%2)\n\t" "%1 = usr\n\t" - : "=r"(df_result), "=r"(usr) : "r"(SF_NaN) + : "=r"(df_result), "=r"(usr) : "r"(SF_QNaN) : "r2", "usr"); - check64(df_result, DF_HEX_NAN); + check64(df_result, DF_HEX_NaN); check_fpstatus(usr, 0); } static void check_invsqrta(void) { - int result; - int predval; + uint32_t result; + uint32_t predval; asm volatile("%0,p0 = sfinvsqrta(%2)\n\t" "%1 = p0\n\t" @@ -516,7 +471,7 @@ static void check_invsqrta(void) static void check_sffixupn(void) { - int result; + uint32_t result; /* Check that sffixupn properly deals with denorm */ asm volatile("%0 = sffixupn(%1, %2)\n\t" @@ -527,7 +482,7 @@ static void check_sffixupn(void) static void check_sffixupd(void) { - int result; + uint32_t result; /* Check that sffixupd properly deals with denorm */ asm volatile("%0 = sffixupd(%1, %2)\n\t" @@ -536,11 +491,38 @@ static void check_sffixupd(void) check32(result, 0x146001d6); } +static void check_sffms(void) +{ + uint32_t result; + + /* Check that sffms properly deals with -0 */ + result = SF_zero_neg; + asm ("%0 -= sfmpy(%1 , %2)\n\t" + : "+r"(result) + : "r"(SF_zero), "r"(SF_zero) + : "r12", "r8"); + check32(result, SF_zero_neg); + + result = SF_zero; + asm ("%0 -= sfmpy(%1 , %2)\n\t" + : "+r"(result) + : "r"(SF_zero_neg), "r"(SF_zero) + : "r12", "r8"); + check32(result, SF_zero); + + result = SF_zero; + asm ("%0 -= sfmpy(%1 , %2)\n\t" + : "+r"(result) + : "r"(SF_zero), "r"(SF_zero_neg) + : "r12", "r8"); + check32(result, SF_zero); +} + static void check_float2int_convs() { - int res32; - long long res64; - int usr; + uint32_t res32; + uint64_t res64; + uint32_t usr; /* * Check that the various forms of float-to-unsigned @@ -552,7 +534,7 @@ static void check_float2int_convs() : "=r"(res32), "=r"(usr) : "r"(SF_small_neg) : "r2", "usr"); check32(res32, 0); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); asm(CLEAR_FPSTATUS "%0 = convert_sf2uw(%2):chop\n\t" @@ -560,7 +542,7 @@ static void check_float2int_convs() : "=r"(res32), "=r"(usr) : "r"(SF_small_neg) : "r2", "usr"); check32(res32, 0); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); asm(CLEAR_FPSTATUS "%0 = convert_sf2ud(%2)\n\t" @@ -568,7 +550,7 @@ static void check_float2int_convs() : "=r"(res64), "=r"(usr) : "r"(SF_small_neg) : "r2", "usr"); check64(res64, 0); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); asm(CLEAR_FPSTATUS "%0 = convert_sf2ud(%2):chop\n\t" @@ -576,7 +558,7 @@ static void check_float2int_convs() : "=r"(res64), "=r"(usr) : "r"(SF_small_neg) : "r2", "usr"); check64(res64, 0); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); asm(CLEAR_FPSTATUS "%0 = convert_df2uw(%2)\n\t" @@ -584,7 +566,7 @@ static void check_float2int_convs() : "=r"(res32), "=r"(usr) : "r"(DF_small_neg) : "r2", "usr"); check32(res32, 0); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); asm(CLEAR_FPSTATUS "%0 = convert_df2uw(%2):chop\n\t" @@ -592,7 +574,7 @@ static void check_float2int_convs() : "=r"(res32), "=r"(usr) : "r"(DF_small_neg) : "r2", "usr"); check32(res32, 0); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); asm(CLEAR_FPSTATUS "%0 = convert_df2ud(%2)\n\t" @@ -600,7 +582,7 @@ static void check_float2int_convs() : "=r"(res64), "=r"(usr) : "r"(DF_small_neg) : "r2", "usr"); check64(res64, 0); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); asm(CLEAR_FPSTATUS "%0 = convert_df2ud(%2):chop\n\t" @@ -608,7 +590,7 @@ static void check_float2int_convs() : "=r"(res64), "=r"(usr) : "r"(DF_small_neg) : "r2", "usr"); check64(res64, 0); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); /* * Check that the various forms of float-to-signed return -1 for NaN @@ -616,34 +598,34 @@ static void check_float2int_convs() asm(CLEAR_FPSTATUS "%0 = convert_sf2w(%2)\n\t" "%1 = usr\n\t" - : "=r"(res32), "=r"(usr) : "r"(SF_NaN) + : "=r"(res32), "=r"(usr) : "r"(SF_QNaN) : "r2", "usr"); check32(res32, -1); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); asm(CLEAR_FPSTATUS "%0 = convert_sf2w(%2):chop\n\t" "%1 = usr\n\t" - : "=r"(res32), "=r"(usr) : "r"(SF_NaN) + : "=r"(res32), "=r"(usr) : "r"(SF_QNaN) : "r2", "usr"); check32(res32, -1); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); asm(CLEAR_FPSTATUS "%0 = convert_sf2d(%2)\n\t" "%1 = usr\n\t" - : "=r"(res64), "=r"(usr) : "r"(SF_NaN) + : "=r"(res64), "=r"(usr) : "r"(SF_QNaN) : "r2", "usr"); check64(res64, -1); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); asm(CLEAR_FPSTATUS "%0 = convert_sf2d(%2):chop\n\t" "%1 = usr\n\t" - : "=r"(res64), "=r"(usr) : "r"(SF_NaN) + : "=r"(res64), "=r"(usr) : "r"(SF_QNaN) : "r2", "usr"); check64(res64, -1); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); asm(CLEAR_FPSTATUS "%0 = convert_df2w(%2)\n\t" @@ -651,7 +633,7 @@ static void check_float2int_convs() : "=r"(res32), "=r"(usr) : "r"(DF_QNaN) : "r2", "usr"); check32(res32, -1); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); asm(CLEAR_FPSTATUS "%0 = convert_df2w(%2):chop\n\t" @@ -659,7 +641,7 @@ static void check_float2int_convs() : "=r"(res32), "=r"(usr) : "r"(DF_QNaN) : "r2", "usr"); check32(res32, -1); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); asm(CLEAR_FPSTATUS "%0 = convert_df2d(%2)\n\t" @@ -667,7 +649,7 @@ static void check_float2int_convs() : "=r"(res64), "=r"(usr) : "r"(DF_QNaN) : "r2", "usr"); check64(res64, -1); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); asm(CLEAR_FPSTATUS "%0 = convert_df2d(%2):chop\n\t" @@ -675,7 +657,58 @@ static void check_float2int_convs() : "=r"(res64), "=r"(usr) : "r"(DF_QNaN) : "r2", "usr"); check64(res64, -1); - check_fpstatus(usr, FPINVF); + check_fpstatus(usr, USR_FPINVF); +} + +static void check_float_consts(void) +{ + uint32_t res32; + uint64_t res64; + + asm("%0 = sfmake(#%1):neg\n\t" : "=r"(res32) : "i"(0xf)); + check32(res32, 0xbc9e0000); + + asm("%0 = sfmake(#%1):pos\n\t" : "=r"(res32) : "i"(0xf)); + check32(res32, 0x3c9e0000); + + asm("%0 = dfmake(#%1):neg\n\t" : "=r"(res64) : "i"(0xf)); + check64(res64, 0xbf93c00000000000ULL); + + asm("%0 = dfmake(#%1):pos\n\t" : "=r"(res64) : "i"(0xf)); + check64(res64, 0x3f93c00000000000ULL); +} + +static inline uint64_t dfmpyll(double x, double y) +{ + uint64_t res64; + asm("%0 = dfmpyll(%1, %2)" : "=r"(res64) : "r"(x), "r"(y)); + return res64; +} + +static inline uint64_t dfmpylh(double acc, double x, double y) +{ + uint64_t res64 = *(uint64_t *)&acc; + asm("%0 += dfmpylh(%1, %2)" : "+r"(res64) : "r"(x), "r"(y)); + return res64; +} + +static void check_dfmpyxx(void) +{ + uint64_t res64; + + res64 = dfmpyll(DBL_MIN, DBL_MIN); + check64(res64, 0ULL); + res64 = dfmpyll(-1.0, DBL_MIN); + check64(res64, 0ULL); + res64 = dfmpyll(DBL_MAX, DBL_MAX); + check64(res64, 0x1fffffffdULL); + + res64 = dfmpylh(DBL_MIN, DBL_MIN, DBL_MIN); + check64(res64, 0x10000000000000ULL); + res64 = dfmpylh(-1.0, DBL_MAX, DBL_MIN); + check64(res64, 0xc00fffffffe00000ULL); + res64 = dfmpylh(DBL_MAX, 0.0, -1.0); + check64(res64, 0x7fefffffffffffffULL); } int main() @@ -688,7 +721,10 @@ int main() check_invsqrta(); check_sffixupn(); check_sffixupd(); + check_sffms(); check_float2int_convs(); + check_float_consts(); + check_dfmpyxx(); puts(err ? "FAIL" : "PASS"); return err ? 1 : 0; diff --git a/tests/tcg/hexagon/hex_sigsegv.c b/tests/tcg/hexagon/hex_sigsegv.c index dc2b349257..f43e0308f9 100644 --- a/tests/tcg/hexagon/hex_sigsegv.c +++ b/tests/tcg/hexagon/hex_sigsegv.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2021-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +25,8 @@ */ #include +#include +#include #include #include #include @@ -32,45 +34,23 @@ #include #include -typedef unsigned char uint8_t; - int err; -int segv_caught; + +#include "hex_test.h" + +bool segv_caught; #define SHOULD_NOT_CHANGE_VAL 5 -int should_not_change = SHOULD_NOT_CHANGE_VAL; +int32_t should_not_change = SHOULD_NOT_CHANGE_VAL; #define BUF_SIZE 300 -unsigned char buf[BUF_SIZE]; - - -static void __check(const char *filename, int line, int x, int expect) -{ - if (x != expect) { - printf("ERROR %s:%d - %d != %d\n", - filename, line, x, expect); - err++; - } -} - -#define check(x, expect) __check(__FILE__, __LINE__, (x), (expect)) - -static void __chk_error(const char *filename, int line, int ret) -{ - if (ret < 0) { - printf("ERROR %s:%d - %d\n", filename, line, ret); - err++; - } -} - -#define chk_error(ret) __chk_error(__FILE__, __LINE__, (ret)) - +uint8_t buf[BUF_SIZE]; jmp_buf jmp_env; static void sig_segv(int sig, siginfo_t *info, void *puc) { - check(sig, SIGSEGV); - segv_caught = 1; + check32(sig, SIGSEGV); + segv_caught = true; longjmp(jmp_env, 1); } @@ -98,8 +78,8 @@ int main() act.sa_flags = 0; chk_error(sigaction(SIGSEGV, &act, NULL)); - check(segv_caught, 1); - check(should_not_change, SHOULD_NOT_CHANGE_VAL); + check32(segv_caught, true); + check32(should_not_change, SHOULD_NOT_CHANGE_VAL); puts(err ? "FAIL" : "PASS"); return err ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/tests/tcg/hexagon/hex_test.h b/tests/tcg/hexagon/hex_test.h new file mode 100644 index 0000000000..cfed06a58b --- /dev/null +++ b/tests/tcg/hexagon/hex_test.h @@ -0,0 +1,145 @@ +/* + * Copyright(c) 2022-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + + +#ifndef HEX_TEST_H +#define HEX_TEST_H + +static inline void __check32(int line, uint32_t val, uint32_t expect) +{ + if (val != expect) { + printf("ERROR at line %d: 0x%08x != 0x%08x\n", line, val, expect); + err++; + } +} + +#define check32(RES, EXP) __check32(__LINE__, RES, EXP) + +static inline void __check64(int line, uint64_t val, uint64_t expect) +{ + if (val != expect) { + printf("ERROR at line %d: 0x%016llx != 0x%016llx\n", line, val, expect); + err++; + } +} + +#define check64(RES, EXP) __check64(__LINE__, RES, EXP) + +static inline void __chk_error(const char *filename, int line, int ret) +{ + if (ret < 0) { + printf("ERROR %s:%d - %d\n", filename, line, ret); + err++; + } +} + +#define chk_error(ret) __chk_error(__FILE__, __LINE__, (ret)) + +static inline void __checkp(int line, void *p, void *expect) +{ + if (p != expect) { + printf("ERROR at line %d: 0x%p != 0x%p\n", line, p, expect); + err++; + } +} + +#define checkp(RES, EXP) __checkp(__LINE__, RES, EXP) + +static inline void __check32_ne(int line, uint32_t val, uint32_t expect) +{ + if (val == expect) { + printf("ERROR at line %d: 0x%08x == 0x%08x\n", line, val, expect); + err++; + } +} + +#define check32_ne(RES, EXP) __check32_ne(__LINE__, RES, EXP) + +static inline void __check64_ne(int line, uint64_t val, uint64_t expect) +{ + if (val == expect) { + printf("ERROR at line %d: 0x%016llx == 0x%016llx\n", line, val, expect); + err++; + } +} + +#define check64_ne(RES, EXP) __check64_ne(__LINE__, RES, EXP) + +/* Define the bits in Hexagon USR register */ +#define USR_OVF_BIT 0 /* Sticky saturation overflow */ +#define USR_FPINVF_BIT 1 /* IEEE FP invalid sticky flag */ +#define USR_FPDBZF_BIT 2 /* IEEE FP divide-by-zero sticky flag */ +#define USR_FPOVFF_BIT 3 /* IEEE FP overflow sticky flag */ +#define USR_FPUNFF_BIT 4 /* IEEE FP underflow sticky flag */ +#define USR_FPINPF_BIT 5 /* IEEE FP inexact sticky flag */ + +/* Corresponding values in USR */ +#define USR_CLEAR 0 +#define USR_OVF (1 << USR_OVF_BIT) +#define USR_FPINVF (1 << USR_FPINVF_BIT) +#define USR_FPDBZF (1 << USR_FPDBZF_BIT) +#define USR_FPOVFF (1 << USR_FPOVFF_BIT) +#define USR_FPUNFF (1 << USR_FPUNFF_BIT) +#define USR_FPINPF (1 << USR_FPINPF_BIT) + +/* Clear bits 0-5 in USR */ +#define CLEAR_USRBITS \ + "r2 = usr\n\t" \ + "r2 = and(r2, #0xffffffc0)\n\t" \ + "usr = r2\n\t" + +/* Clear bits 1-5 in USR */ +#define CLEAR_FPSTATUS \ + "r2 = usr\n\t" \ + "r2 = and(r2, #0xffffffc1)\n\t" \ + "usr = r2\n\t" + +/* Some useful floating point values */ +const uint32_t SF_INF = 0x7f800000; +const uint32_t SF_QNaN = 0x7fc00000; +const uint32_t SF_QNaN_special = 0x7f800001; +const uint32_t SF_SNaN = 0x7fb00000; +const uint32_t SF_QNaN_neg = 0xffc00000; +const uint32_t SF_SNaN_neg = 0xffb00000; +const uint32_t SF_HEX_NaN = 0xffffffff; +const uint32_t SF_zero = 0x00000000; +const uint32_t SF_zero_neg = 0x80000000; +const uint32_t SF_one = 0x3f800000; +const uint32_t SF_one_recip = 0x3f7f0001; /* 0.9960... */ +const uint32_t SF_one_invsqrta = 0x3f7f0000; /* 0.99609375 */ +const uint32_t SF_two = 0x40000000; +const uint32_t SF_four = 0x40800000; +const uint32_t SF_small_neg = 0xab98fba8; +const uint32_t SF_large_pos = 0x5afa572e; +const uint32_t SF_any = 0x3f800000; +const uint32_t SF_denorm = 0x00000001; +const uint32_t SF_random = 0x346001d6; + +const uint64_t DF_QNaN = 0x7ff8000000000000ULL; +const uint64_t DF_SNaN = 0x7ff7000000000000ULL; +const uint64_t DF_QNaN_neg = 0xfff8000000000000ULL; +const uint64_t DF_SNaN_neg = 0xfff7000000000000ULL; +const uint64_t DF_HEX_NaN = 0xffffffffffffffffULL; +const uint64_t DF_zero = 0x0000000000000000ULL; +const uint64_t DF_zero_neg = 0x8000000000000000ULL; +const uint64_t DF_any = 0x3f80000000000000ULL; +const uint64_t DF_one = 0x3ff0000000000000ULL; +const uint64_t DF_one_hh = 0x3ff001ff80000000ULL; /* 1.00048... */ +const uint64_t DF_small_neg = 0xbd731f7500000000ULL; +const uint64_t DF_large_pos = 0x7f80000000000001ULL; + +#endif diff --git a/tests/tcg/hexagon/hvx_misc.c b/tests/tcg/hexagon/hvx_misc.c index b896f5897e..b45170acd1 100644 --- a/tests/tcg/hexagon/hvx_misc.c +++ b/tests/tcg/hexagon/hvx_misc.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2021-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2021-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,69 +23,7 @@ int err; -static void __check(int line, int i, int j, uint64_t result, uint64_t expect) -{ - if (result != expect) { - printf("ERROR at line %d: [%d][%d] 0x%016llx != 0x%016llx\n", - line, i, j, result, expect); - err++; - } -} - -#define check(RES, EXP) __check(__LINE__, RES, EXP) - -#define MAX_VEC_SIZE_BYTES 128 - -typedef union { - uint64_t ud[MAX_VEC_SIZE_BYTES / 8]; - int64_t d[MAX_VEC_SIZE_BYTES / 8]; - uint32_t uw[MAX_VEC_SIZE_BYTES / 4]; - int32_t w[MAX_VEC_SIZE_BYTES / 4]; - uint16_t uh[MAX_VEC_SIZE_BYTES / 2]; - int16_t h[MAX_VEC_SIZE_BYTES / 2]; - uint8_t ub[MAX_VEC_SIZE_BYTES / 1]; - int8_t b[MAX_VEC_SIZE_BYTES / 1]; -} MMVector; - -#define BUFSIZE 16 -#define OUTSIZE 16 -#define MASKMOD 3 - -MMVector buffer0[BUFSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); -MMVector buffer1[BUFSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); -MMVector mask[BUFSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); -MMVector output[OUTSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); -MMVector expect[OUTSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); - -#define CHECK_OUTPUT_FUNC(FIELD, FIELDSZ) \ -static void check_output_##FIELD(int line, size_t num_vectors) \ -{ \ - for (int i = 0; i < num_vectors; i++) { \ - for (int j = 0; j < MAX_VEC_SIZE_BYTES / FIELDSZ; j++) { \ - __check(line, i, j, output[i].FIELD[j], expect[i].FIELD[j]); \ - } \ - } \ -} - -CHECK_OUTPUT_FUNC(d, 8) -CHECK_OUTPUT_FUNC(w, 4) -CHECK_OUTPUT_FUNC(h, 2) -CHECK_OUTPUT_FUNC(b, 1) - -static void init_buffers(void) -{ - int counter0 = 0; - int counter1 = 17; - for (int i = 0; i < BUFSIZE; i++) { - for (int j = 0; j < MAX_VEC_SIZE_BYTES; j++) { - buffer0[i].b[j] = counter0++; - buffer1[i].b[j] = counter1++; - } - for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { - mask[i].w[j] = (i + j % MASKMOD == 0) ? 0 : 1; - } - } -} +#include "hvx_misc.h" static void test_load_tmp(void) { @@ -122,6 +60,36 @@ static void test_load_tmp(void) check_output_w(__LINE__, BUFSIZE); } +static void test_load_tmp2(void) +{ + void *pout0 = &output[0]; + void *pout1 = &output[1]; + + asm volatile( + "r0 = #0x03030303\n\t" + "v16 = vsplat(r0)\n\t" + "r0 = #0x04040404\n\t" + "v18 = vsplat(r0)\n\t" + "r0 = #0x05050505\n\t" + "v21 = vsplat(r0)\n\t" + "{\n\t" + " v25:24 += vmpyo(v18.w, v14.h)\n\t" + " v15:14.tmp = vcombine(v21, v16)\n\t" + "}\n\t" + "vmem(%0 + #0) = v24\n\t" + "vmem(%1 + #0) = v25\n\t" + : : "r"(pout0), "r"(pout1) + : "r0", "v16", "v18", "v21", "v24", "v25", "memory" + ); + + for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; ++i) { + expect[0].w[i] = 0x180c0000; + expect[1].w[i] = 0x000c1818; + } + + check_output_w(__LINE__, 2); +} + static void test_load_cur(void) { void *p0 = buffer0; @@ -322,100 +290,6 @@ static void test_max_temps() check_output_b(__LINE__, 5); } -#define VEC_OP1(ASM, EL, IN, OUT) \ - asm("v2 = vmem(%0 + #0)\n\t" \ - "v2" #EL " = " #ASM "(v2" #EL ")\n\t" \ - "vmem(%1 + #0) = v2\n\t" \ - : : "r"(IN), "r"(OUT) : "v2", "memory") - -#define VEC_OP2(ASM, EL, IN0, IN1, OUT) \ - asm("v2 = vmem(%0 + #0)\n\t" \ - "v3 = vmem(%1 + #0)\n\t" \ - "v2" #EL " = " #ASM "(v2" #EL ", v3" #EL ")\n\t" \ - "vmem(%2 + #0) = v2\n\t" \ - : : "r"(IN0), "r"(IN1), "r"(OUT) : "v2", "v3", "memory") - -#define TEST_VEC_OP1(NAME, ASM, EL, FIELD, FIELDSZ, OP) \ -static void test_##NAME(void) \ -{ \ - void *pin = buffer0; \ - void *pout = output; \ - for (int i = 0; i < BUFSIZE; i++) { \ - VEC_OP1(ASM, EL, pin, pout); \ - pin += sizeof(MMVector); \ - pout += sizeof(MMVector); \ - } \ - for (int i = 0; i < BUFSIZE; i++) { \ - for (int j = 0; j < MAX_VEC_SIZE_BYTES / FIELDSZ; j++) { \ - expect[i].FIELD[j] = OP buffer0[i].FIELD[j]; \ - } \ - } \ - check_output_##FIELD(__LINE__, BUFSIZE); \ -} - -#define TEST_VEC_OP2(NAME, ASM, EL, FIELD, FIELDSZ, OP) \ -static void test_##NAME(void) \ -{ \ - void *p0 = buffer0; \ - void *p1 = buffer1; \ - void *pout = output; \ - for (int i = 0; i < BUFSIZE; i++) { \ - VEC_OP2(ASM, EL, p0, p1, pout); \ - p0 += sizeof(MMVector); \ - p1 += sizeof(MMVector); \ - pout += sizeof(MMVector); \ - } \ - for (int i = 0; i < BUFSIZE; i++) { \ - for (int j = 0; j < MAX_VEC_SIZE_BYTES / FIELDSZ; j++) { \ - expect[i].FIELD[j] = buffer0[i].FIELD[j] OP buffer1[i].FIELD[j]; \ - } \ - } \ - check_output_##FIELD(__LINE__, BUFSIZE); \ -} - -#define THRESHOLD 31 - -#define PRED_OP2(ASM, IN0, IN1, OUT, INV) \ - asm("r4 = #%3\n\t" \ - "v1.b = vsplat(r4)\n\t" \ - "v2 = vmem(%0 + #0)\n\t" \ - "q0 = vcmp.gt(v2.b, v1.b)\n\t" \ - "v3 = vmem(%1 + #0)\n\t" \ - "q1 = vcmp.gt(v3.b, v1.b)\n\t" \ - "q2 = " #ASM "(q0, " INV "q1)\n\t" \ - "r4 = #0xff\n\t" \ - "v1.b = vsplat(r4)\n\t" \ - "if (q2) vmem(%2 + #0) = v1\n\t" \ - : : "r"(IN0), "r"(IN1), "r"(OUT), "i"(THRESHOLD) \ - : "r4", "v1", "v2", "v3", "q0", "q1", "q2", "memory") - -#define TEST_PRED_OP2(NAME, ASM, OP, INV) \ -static void test_##NAME(bool invert) \ -{ \ - void *p0 = buffer0; \ - void *p1 = buffer1; \ - void *pout = output; \ - memset(output, 0, sizeof(expect)); \ - for (int i = 0; i < BUFSIZE; i++) { \ - PRED_OP2(ASM, p0, p1, pout, INV); \ - p0 += sizeof(MMVector); \ - p1 += sizeof(MMVector); \ - pout += sizeof(MMVector); \ - } \ - for (int i = 0; i < BUFSIZE; i++) { \ - for (int j = 0; j < MAX_VEC_SIZE_BYTES; j++) { \ - bool p0 = (buffer0[i].b[j] > THRESHOLD); \ - bool p1 = (buffer1[i].b[j] > THRESHOLD); \ - if (invert) { \ - expect[i].b[j] = (p0 OP !p1) ? 0xff : 0x00; \ - } else { \ - expect[i].b[j] = (p0 OP p1) ? 0xff : 0x00; \ - } \ - } \ - } \ - check_output_b(__LINE__, BUFSIZE); \ -} - TEST_VEC_OP2(vadd_w, vadd, .w, w, 4, +) TEST_VEC_OP2(vadd_h, vadd, .h, h, 2, +) TEST_VEC_OP2(vadd_b, vadd, .b, b, 1, +) @@ -498,11 +372,100 @@ static void test_vsubuwsat_dv(void) check_output_w(__LINE__, 2); } +static void test_load_tmp_predicated(void) +{ + void *p0 = buffer0; + void *p1 = buffer1; + void *pout = output; + bool pred = true; + + for (int i = 0; i < BUFSIZE; i++) { + /* + * Load into v12 as .tmp with a predicate + * When the predicate is true, we get the vector from buffer1[i] + * When the predicate is false, we get a vector of all 1's + * Regardless of the predicate, the next packet should have + * a vector of all 1's + */ + asm("v3 = vmem(%0 + #0)\n\t" + "r1 = #1\n\t" + "v12 = vsplat(r1)\n\t" + "p1 = !cmp.eq(%3, #0)\n\t" + "{\n\t" + " if (p1) v12.tmp = vmem(%1 + #0)\n\t" + " v4.w = vadd(v12.w, v3.w)\n\t" + "}\n\t" + "v4.w = vadd(v4.w, v12.w)\n\t" + "vmem(%2 + #0) = v4\n\t" + : : "r"(p0), "r"(p1), "r"(pout), "r"(pred) + : "r1", "p1", "v12", "v3", "v4", "v6", "memory"); + p0 += sizeof(MMVector); + p1 += sizeof(MMVector); + pout += sizeof(MMVector); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + expect[i].w[j] = + pred ? buffer0[i].w[j] + buffer1[i].w[j] + 1 + : buffer0[i].w[j] + 2; + } + pred = !pred; + } + + check_output_w(__LINE__, BUFSIZE); +} + +static void test_load_cur_predicated(void) +{ + bool pred = true; + for (int i = 0; i < BUFSIZE; i++) { + asm volatile("p0 = !cmp.eq(%3, #0)\n\t" + "v3 = vmem(%0+#0)\n\t" + /* + * Preload v4 to make sure that the assignment from the + * packet below is not being ignored when pred is false. + */ + "r0 = #0x01237654\n\t" + "v4 = vsplat(r0)\n\t" + "{\n\t" + " if (p0) v3.cur = vmem(%1+#0)\n\t" + " v4 = v3\n\t" + "}\n\t" + "vmem(%2+#0) = v4\n\t" + : + : "r"(&buffer0[i]), "r"(&buffer1[i]), + "r"(&output[i]), "r"(pred) + : "r0", "p0", "v3", "v4", "memory"); + expect[i] = pred ? buffer1[i] : buffer0[i]; + pred = !pred; + } + check_output_w(__LINE__, BUFSIZE); +} + +static void test_vcombine(void) +{ + for (int i = 0; i < BUFSIZE / 2; i++) { + asm volatile("v2 = vsplat(%0)\n\t" + "v3 = vsplat(%1)\n\t" + "v3:2 = vcombine(v2, v3)\n\t" + "vmem(%2+#0) = v2\n\t" + "vmem(%2+#1) = v3\n\t" + : + : "r"(2 * i), "r"(2 * i + 1), "r"(&output[2 * i]) + : "v2", "v3", "memory"); + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + expect[2 * i].w[j] = 2 * i + 1; + expect[2 * i + 1].w[j] = 2 * i; + } + } + check_output_w(__LINE__, BUFSIZE); +} + int main() { init_buffers(); test_load_tmp(); + test_load_tmp2(); test_load_cur(); test_load_aligned(); test_load_unaligned(); @@ -533,6 +496,11 @@ int main() test_vadduwsat(); test_vsubuwsat_dv(); + test_load_tmp_predicated(); + test_load_cur_predicated(); + + test_vcombine(); + puts(err ? "FAIL" : "PASS"); return err ? 1 : 0; } diff --git a/tests/tcg/hexagon/hvx_misc.h b/tests/tcg/hexagon/hvx_misc.h new file mode 100644 index 0000000000..2e868340fd --- /dev/null +++ b/tests/tcg/hexagon/hvx_misc.h @@ -0,0 +1,178 @@ +/* + * Copyright(c) 2021-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef HVX_MISC_H +#define HVX_MISC_H + +static inline void check(int line, int i, int j, + uint64_t result, uint64_t expect) +{ + if (result != expect) { + printf("ERROR at line %d: [%d][%d] 0x%016llx != 0x%016llx\n", + line, i, j, result, expect); + err++; + } +} + +#define MAX_VEC_SIZE_BYTES 128 + +typedef union { + uint64_t ud[MAX_VEC_SIZE_BYTES / 8]; + int64_t d[MAX_VEC_SIZE_BYTES / 8]; + uint32_t uw[MAX_VEC_SIZE_BYTES / 4]; + int32_t w[MAX_VEC_SIZE_BYTES / 4]; + uint16_t uh[MAX_VEC_SIZE_BYTES / 2]; + int16_t h[MAX_VEC_SIZE_BYTES / 2]; + uint8_t ub[MAX_VEC_SIZE_BYTES / 1]; + int8_t b[MAX_VEC_SIZE_BYTES / 1]; +} MMVector; + +#define BUFSIZE 16 +#define OUTSIZE 16 +#define MASKMOD 3 + +MMVector buffer0[BUFSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); +MMVector buffer1[BUFSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); +MMVector mask[BUFSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); +MMVector output[OUTSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); +MMVector expect[OUTSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); + +#define CHECK_OUTPUT_FUNC(FIELD, FIELDSZ) \ +static inline void check_output_##FIELD(int line, size_t num_vectors) \ +{ \ + for (int i = 0; i < num_vectors; i++) { \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES / FIELDSZ; j++) { \ + check(line, i, j, output[i].FIELD[j], expect[i].FIELD[j]); \ + } \ + } \ +} + +CHECK_OUTPUT_FUNC(d, 8) +CHECK_OUTPUT_FUNC(w, 4) +CHECK_OUTPUT_FUNC(h, 2) +CHECK_OUTPUT_FUNC(b, 1) + +static inline void init_buffers(void) +{ + int counter0 = 0; + int counter1 = 17; + for (int i = 0; i < BUFSIZE; i++) { + for (int j = 0; j < MAX_VEC_SIZE_BYTES; j++) { + buffer0[i].b[j] = counter0++; + buffer1[i].b[j] = counter1++; + } + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + mask[i].w[j] = (i + j % MASKMOD == 0) ? 0 : 1; + } + } +} + +#define VEC_OP1(ASM, EL, IN, OUT) \ + asm("v2 = vmem(%0 + #0)\n\t" \ + "v2" #EL " = " #ASM "(v2" #EL ")\n\t" \ + "vmem(%1 + #0) = v2\n\t" \ + : : "r"(IN), "r"(OUT) : "v2", "memory") + +#define VEC_OP2(ASM, EL, IN0, IN1, OUT) \ + asm("v2 = vmem(%0 + #0)\n\t" \ + "v3 = vmem(%1 + #0)\n\t" \ + "v2" #EL " = " #ASM "(v2" #EL ", v3" #EL ")\n\t" \ + "vmem(%2 + #0) = v2\n\t" \ + : : "r"(IN0), "r"(IN1), "r"(OUT) : "v2", "v3", "memory") + +#define TEST_VEC_OP1(NAME, ASM, EL, FIELD, FIELDSZ, OP) \ +static inline void test_##NAME(void) \ +{ \ + void *pin = buffer0; \ + void *pout = output; \ + for (int i = 0; i < BUFSIZE; i++) { \ + VEC_OP1(ASM, EL, pin, pout); \ + pin += sizeof(MMVector); \ + pout += sizeof(MMVector); \ + } \ + for (int i = 0; i < BUFSIZE; i++) { \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES / FIELDSZ; j++) { \ + expect[i].FIELD[j] = OP buffer0[i].FIELD[j]; \ + } \ + } \ + check_output_##FIELD(__LINE__, BUFSIZE); \ +} + +#define TEST_VEC_OP2(NAME, ASM, EL, FIELD, FIELDSZ, OP) \ +static inline void test_##NAME(void) \ +{ \ + void *p0 = buffer0; \ + void *p1 = buffer1; \ + void *pout = output; \ + for (int i = 0; i < BUFSIZE; i++) { \ + VEC_OP2(ASM, EL, p0, p1, pout); \ + p0 += sizeof(MMVector); \ + p1 += sizeof(MMVector); \ + pout += sizeof(MMVector); \ + } \ + for (int i = 0; i < BUFSIZE; i++) { \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES / FIELDSZ; j++) { \ + expect[i].FIELD[j] = buffer0[i].FIELD[j] OP buffer1[i].FIELD[j]; \ + } \ + } \ + check_output_##FIELD(__LINE__, BUFSIZE); \ +} + +#define THRESHOLD 31 + +#define PRED_OP2(ASM, IN0, IN1, OUT, INV) \ + asm("r4 = #%3\n\t" \ + "v1.b = vsplat(r4)\n\t" \ + "v2 = vmem(%0 + #0)\n\t" \ + "q0 = vcmp.gt(v2.b, v1.b)\n\t" \ + "v3 = vmem(%1 + #0)\n\t" \ + "q1 = vcmp.gt(v3.b, v1.b)\n\t" \ + "q2 = " #ASM "(q0, " INV "q1)\n\t" \ + "r4 = #0xff\n\t" \ + "v1.b = vsplat(r4)\n\t" \ + "if (q2) vmem(%2 + #0) = v1\n\t" \ + : : "r"(IN0), "r"(IN1), "r"(OUT), "i"(THRESHOLD) \ + : "r4", "v1", "v2", "v3", "q0", "q1", "q2", "memory") + +#define TEST_PRED_OP2(NAME, ASM, OP, INV) \ +static inline void test_##NAME(bool invert) \ +{ \ + void *p0 = buffer0; \ + void *p1 = buffer1; \ + void *pout = output; \ + memset(output, 0, sizeof(expect)); \ + for (int i = 0; i < BUFSIZE; i++) { \ + PRED_OP2(ASM, p0, p1, pout, INV); \ + p0 += sizeof(MMVector); \ + p1 += sizeof(MMVector); \ + pout += sizeof(MMVector); \ + } \ + for (int i = 0; i < BUFSIZE; i++) { \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES; j++) { \ + bool p0 = (buffer0[i].b[j] > THRESHOLD); \ + bool p1 = (buffer1[i].b[j] > THRESHOLD); \ + if (invert) { \ + expect[i].b[j] = (p0 OP !p1) ? 0xff : 0x00; \ + } else { \ + expect[i].b[j] = (p0 OP p1) ? 0xff : 0x00; \ + } \ + } \ + } \ + check_output_b(__LINE__, BUFSIZE); \ +} + +#endif diff --git a/tests/tcg/hexagon/invalid-slots.c b/tests/tcg/hexagon/invalid-slots.c new file mode 100644 index 0000000000..366ce4f42f --- /dev/null +++ b/tests/tcg/hexagon/invalid-slots.c @@ -0,0 +1,29 @@ +/* + * Copyright(c) 2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +char mem[8] __attribute__((aligned(8))); + +int main() +{ + asm volatile( + "r0 = #mem\n" + /* Invalid packet (2 instructions at slot 0): */ + ".word 0xa1804100\n" /* { memw(r0) = r1; */ + ".word 0x28032804\n" /* r3 = #0; r4 = #0 } */ + : : : "r0", "r3", "r4", "memory"); + return 0; +} diff --git a/tests/tcg/hexagon/load_align.c b/tests/tcg/hexagon/load_align.c index 12fc9cbd8f..f5948fd539 100644 --- a/tests/tcg/hexagon/load_align.c +++ b/tests/tcg/hexagon/load_align.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,41 +29,22 @@ */ #include +#include #include int err; -char buf[16] __attribute__((aligned(1 << 16))); +#include "hex_test.h" + +int8_t buf[16] __attribute__((aligned(1 << 16))); void init_buf(void) { - int i; - for (i = 0; i < 16; i++) { + for (int i = 0; i < 16; i++) { buf[i] = i + 1; } } -void __check(int line, long long result, long long expect) -{ - if (result != expect) { - printf("ERROR at line %d: 0x%016llx != 0x%016llx\n", - line, result, expect); - err++; - } -} - -#define check(RES, EXP) __check(__LINE__, RES, EXP) - -void __checkp(int line, void *p, void *expect) -{ - if (p != expect) { - printf("ERROR at line %d: 0x%p != 0x%p\n", line, p, expect); - err++; - } -} - -#define checkp(RES, EXP) __checkp(__LINE__, RES, EXP) - /* **************************************************************************** * _io addressing mode (addr + offset) @@ -81,15 +62,15 @@ void __checkp(int line, void *p, void *expect) #define TEST_io(NAME, SZ, SIZE, EXP1, EXP2, EXP3, EXP4) \ void test_##NAME(void) \ { \ - long long result = ~0LL; \ + int64_t result = ~0LL; \ LOAD_io_##SZ(result, buf, 0 * (SIZE)); \ - check(result, (EXP1)); \ + check64(result, (EXP1)); \ LOAD_io_##SZ(result, buf, 1 * (SIZE)); \ - check(result, (EXP2)); \ + check64(result, (EXP2)); \ LOAD_io_##SZ(result, buf, 2 * (SIZE)); \ - check(result, (EXP3)); \ + check64(result, (EXP3)); \ LOAD_io_##SZ(result, buf, 3 * (SIZE)); \ - check(result, (EXP4)); \ + check64(result, (EXP4)); \ } TEST_io(loadalignb_io, b, 1, @@ -116,15 +97,15 @@ TEST_io(loadalignh_io, h, 2, #define TEST_ur(NAME, SZ, SHIFT, RES1, RES2, RES3, RES4) \ void test_##NAME(void) \ { \ - long long result = ~0LL; \ + uint64_t result = ~0LL; \ LOAD_ur_##SZ(result, (SHIFT), 0); \ - check(result, (RES1)); \ + check64(result, (RES1)); \ LOAD_ur_##SZ(result, (SHIFT), 1); \ - check(result, (RES2)); \ + check64(result, (RES2)); \ LOAD_ur_##SZ(result, (SHIFT), 2); \ - check(result, (RES3)); \ + check64(result, (RES3)); \ LOAD_ur_##SZ(result, (SHIFT), 3); \ - check(result, (RES4)); \ + check64(result, (RES4)); \ } TEST_ur(loadalignb_ur, b, 1, @@ -150,19 +131,19 @@ TEST_ur(loadalignh_ur, h, 1, #define TEST_ap(NAME, SZ, SIZE, RES1, RES2, RES3, RES4) \ void test_##NAME(void) \ { \ - long long result = ~0LL; \ + int64_t result = ~0LL; \ void *ptr; \ LOAD_ap_##SZ(result, ptr, (buf + 0 * (SIZE))); \ - check(result, (RES1)); \ + check64(result, (RES1)); \ checkp(ptr, &buf[0 * (SIZE)]); \ LOAD_ap_##SZ(result, ptr, (buf + 1 * (SIZE))); \ - check(result, (RES2)); \ + check64(result, (RES2)); \ checkp(ptr, &buf[1 * (SIZE)]); \ LOAD_ap_##SZ(result, ptr, (buf + 2 * (SIZE))); \ - check(result, (RES3)); \ + check64(result, (RES3)); \ checkp(ptr, &buf[2 * (SIZE)]); \ LOAD_ap_##SZ(result, ptr, (buf + 3 * (SIZE))); \ - check(result, (RES4)); \ + check64(result, (RES4)); \ checkp(ptr, &buf[3 * (SIZE)]); \ } @@ -192,19 +173,19 @@ TEST_ap(loadalignh_ap, h, 2, #define TEST_pr(NAME, SZ, SIZE, RES1, RES2, RES3, RES4) \ void test_##NAME(void) \ { \ - long long result = ~0LL; \ + int64_t result = ~0LL; \ void *ptr = buf; \ LOAD_pr_##SZ(result, ptr, (SIZE)); \ - check(result, (RES1)); \ + check64(result, (RES1)); \ checkp(ptr, &buf[1 * (SIZE)]); \ LOAD_pr_##SZ(result, ptr, (SIZE)); \ - check(result, (RES2)); \ + check64(result, (RES2)); \ checkp(ptr, &buf[2 * (SIZE)]); \ LOAD_pr_##SZ(result, ptr, (SIZE)); \ - check(result, (RES3)); \ + check64(result, (RES3)); \ checkp(ptr, &buf[3 * (SIZE)]); \ LOAD_pr_##SZ(result, ptr, (SIZE)); \ - check(result, (RES4)); \ + check64(result, (RES4)); \ checkp(ptr, &buf[4 * (SIZE)]); \ } @@ -235,16 +216,16 @@ TEST_pr(loadalignh_pr, h, 2, #define TEST_pbr(NAME, SZ, RES1, RES2, RES3, RES4) \ void test_##NAME(void) \ { \ - long long result = ~0LL; \ + int64_t result = ~0LL; \ void *ptr = buf; \ LOAD_pbr_##SZ(result, ptr); \ - check(result, (RES1)); \ + check64(result, (RES1)); \ LOAD_pbr_##SZ(result, ptr); \ - check(result, (RES2)); \ + check64(result, (RES2)); \ LOAD_pbr_##SZ(result, ptr); \ - check(result, (RES3)); \ + check64(result, (RES3)); \ LOAD_pbr_##SZ(result, ptr); \ - check(result, (RES4)); \ + check64(result, (RES4)); \ } TEST_pbr(loadalignb_pbr, b, @@ -270,19 +251,19 @@ TEST_pbr(loadalignh_pbr, h, #define TEST_pi(NAME, SZ, INC, RES1, RES2, RES3, RES4) \ void test_##NAME(void) \ { \ - long long result = ~0LL; \ + int64_t result = ~0LL; \ void *ptr = buf; \ LOAD_pi_##SZ(result, ptr, (INC)); \ - check(result, (RES1)); \ + check64(result, (RES1)); \ checkp(ptr, &buf[1 * (INC)]); \ LOAD_pi_##SZ(result, ptr, (INC)); \ - check(result, (RES2)); \ + check64(result, (RES2)); \ checkp(ptr, &buf[2 * (INC)]); \ LOAD_pi_##SZ(result, ptr, (INC)); \ - check(result, (RES3)); \ + check64(result, (RES3)); \ checkp(ptr, &buf[3 * (INC)]); \ LOAD_pi_##SZ(result, ptr, (INC)); \ - check(result, (RES4)); \ + check64(result, (RES4)); \ checkp(ptr, &buf[4 * (INC)]); \ } @@ -314,19 +295,19 @@ TEST_pi(loadalignh_pi, h, 2, #define TEST_pci(NAME, SZ, LEN, INC, RES1, RES2, RES3, RES4) \ void test_##NAME(void) \ { \ - long long result = ~0LL; \ + int64_t result = ~0LL; \ void *ptr = buf; \ LOAD_pci_##SZ(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES1)); \ + check64(result, (RES1)); \ checkp(ptr, &buf[(1 * (INC)) % (LEN)]); \ LOAD_pci_##SZ(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES2)); \ + check64(result, (RES2)); \ checkp(ptr, &buf[(2 * (INC)) % (LEN)]); \ LOAD_pci_##SZ(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES3)); \ + check64(result, (RES3)); \ checkp(ptr, &buf[(3 * (INC)) % (LEN)]); \ LOAD_pci_##SZ(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES4)); \ + check64(result, (RES4)); \ checkp(ptr, &buf[(4 * (INC)) % (LEN)]); \ } @@ -359,19 +340,19 @@ TEST_pci(loadalignh_pci, h, 4, 2, #define TEST_pcr(NAME, SZ, SIZE, LEN, INC, RES1, RES2, RES3, RES4) \ void test_##NAME(void) \ { \ - long long result = ~0LL; \ + int64_t result = ~0LL; \ void *ptr = buf; \ LOAD_pcr_##SZ(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES1)); \ + check64(result, (RES1)); \ checkp(ptr, &buf[(1 * (INC) * (SIZE)) % (LEN)]); \ LOAD_pcr_##SZ(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES2)); \ + check64(result, (RES2)); \ checkp(ptr, &buf[(2 * (INC) * (SIZE)) % (LEN)]); \ LOAD_pcr_##SZ(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES3)); \ + check64(result, (RES3)); \ checkp(ptr, &buf[(3 * (INC) * (SIZE)) % (LEN)]); \ LOAD_pcr_##SZ(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES4)); \ + check64(result, (RES4)); \ checkp(ptr, &buf[(4 * (INC) * (SIZE)) % (LEN)]); \ } diff --git a/tests/tcg/hexagon/load_unpack.c b/tests/tcg/hexagon/load_unpack.c index 3575a37a28..c30f4d80aa 100644 --- a/tests/tcg/hexagon/load_unpack.c +++ b/tests/tcg/hexagon/load_unpack.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,42 +31,23 @@ */ #include +#include #include int err; -char buf[16] __attribute__((aligned(1 << 16))); +#include "hex_test.h" + +int8_t buf[16] __attribute__((aligned(1 << 16))); void init_buf(void) { - int i; - for (i = 0; i < 16; i++) { + for (int i = 0; i < 16; i++) { int sign = i % 2 == 0 ? 0x80 : 0; buf[i] = sign | (i + 1); } } -void __check(int line, long long result, long long expect) -{ - if (result != expect) { - printf("ERROR at line %d: 0x%08llx != 0x%08llx\n", - line, result, expect); - err++; - } -} - -#define check(RES, EXP) __check(__LINE__, RES, EXP) - -void __checkp(int line, void *p, void *expect) -{ - if (p != expect) { - printf("ERROR at line %d: 0x%p != 0x%p\n", line, p, expect); - err++; - } -} - -#define checkp(RES, EXP) __checkp(__LINE__, RES, EXP) - /* **************************************************************************** * _io addressing mode (addr + offset) @@ -87,24 +68,24 @@ void test_##NAME(void) \ TYPE result; \ init_buf(); \ BxW_LOAD_io_##SIGN(result, buf, 0 * (SIZE)); \ - check(result, (EXP1) | (EXT)); \ + check64(result, (EXP1) | (EXT)); \ BxW_LOAD_io_##SIGN(result, buf, 1 * (SIZE)); \ - check(result, (EXP2) | (EXT)); \ + check64(result, (EXP2) | (EXT)); \ BxW_LOAD_io_##SIGN(result, buf, 2 * (SIZE)); \ - check(result, (EXP3) | (EXT)); \ + check64(result, (EXP3) | (EXT)); \ BxW_LOAD_io_##SIGN(result, buf, 3 * (SIZE)); \ - check(result, (EXP4) | (EXT)); \ + check64(result, (EXP4) | (EXT)); \ } -TEST_io(loadbzw2_io, int, Z, 2, 0x00000000, +TEST_io(loadbzw2_io, int32_t, Z, 2, 0x00000000, 0x00020081, 0x00040083, 0x00060085, 0x00080087) -TEST_io(loadbsw2_io, int, S, 2, 0x0000ff00, +TEST_io(loadbsw2_io, int32_t, S, 2, 0x0000ff00, 0x00020081, 0x00040083, 0x00060085, 0x00080087) -TEST_io(loadbzw4_io, long long, Z, 4, 0x0000000000000000LL, +TEST_io(loadbzw4_io, int64_t, Z, 4, 0x0000000000000000LL, 0x0004008300020081LL, 0x0008008700060085LL, 0x000c008b000a0089LL, 0x0010008f000e008dLL) -TEST_io(loadbsw4_io, long long, S, 4, 0x0000ff000000ff00LL, +TEST_io(loadbsw4_io, int64_t, S, 4, 0x0000ff000000ff00LL, 0x0004008300020081LL, 0x0008008700060085LL, 0x000c008b000a0089LL, 0x0010008f000e008dLL) @@ -128,23 +109,23 @@ void test_##NAME(void) \ TYPE result; \ init_buf(); \ BxW_LOAD_ur_##SIGN(result, (SHIFT), 0); \ - check(result, (RES1) | (EXT)); \ + check64(result, (RES1) | (EXT)); \ BxW_LOAD_ur_##SIGN(result, (SHIFT), 1); \ - check(result, (RES2) | (EXT)); \ + check64(result, (RES2) | (EXT)); \ BxW_LOAD_ur_##SIGN(result, (SHIFT), 2); \ - check(result, (RES3) | (EXT)); \ + check64(result, (RES3) | (EXT)); \ BxW_LOAD_ur_##SIGN(result, (SHIFT), 3); \ - check(result, (RES4) | (EXT)); \ + check64(result, (RES4) | (EXT)); \ } \ -TEST_ur(loadbzw2_ur, int, Z, 1, 0x00000000, +TEST_ur(loadbzw2_ur, int32_t, Z, 1, 0x00000000, 0x00020081, 0x00040083, 0x00060085, 0x00080087) -TEST_ur(loadbsw2_ur, int, S, 1, 0x0000ff00, +TEST_ur(loadbsw2_ur, int32_t, S, 1, 0x0000ff00, 0x00020081, 0x00040083, 0x00060085, 0x00080087) -TEST_ur(loadbzw4_ur, long long, Z, 2, 0x0000000000000000LL, +TEST_ur(loadbzw4_ur, int64_t, Z, 2, 0x0000000000000000LL, 0x0004008300020081LL, 0x0008008700060085LL, 0x000c008b000a0089LL, 0x0010008f000e008dLL) -TEST_ur(loadbsw4_ur, long long, S, 2, 0x0000ff000000ff00LL, +TEST_ur(loadbsw4_ur, int64_t, S, 2, 0x0000ff000000ff00LL, 0x0004008300020081LL, 0x0008008700060085LL, 0x000c008b000a0089LL, 0x0010008f000e008dLL) @@ -168,27 +149,27 @@ void test_##NAME(void) \ void *ptr; \ init_buf(); \ BxW_LOAD_ap_##SIGN(result, ptr, (buf + 0 * (SIZE))); \ - check(result, (RES1) | (EXT)); \ + check64(result, (RES1) | (EXT)); \ checkp(ptr, &buf[0 * (SIZE)]); \ BxW_LOAD_ap_##SIGN(result, ptr, (buf + 1 * (SIZE))); \ - check(result, (RES2) | (EXT)); \ + check64(result, (RES2) | (EXT)); \ checkp(ptr, &buf[1 * (SIZE)]); \ BxW_LOAD_ap_##SIGN(result, ptr, (buf + 2 * (SIZE))); \ - check(result, (RES3) | (EXT)); \ + check64(result, (RES3) | (EXT)); \ checkp(ptr, &buf[2 * (SIZE)]); \ BxW_LOAD_ap_##SIGN(result, ptr, (buf + 3 * (SIZE))); \ - check(result, (RES4) | (EXT)); \ + check64(result, (RES4) | (EXT)); \ checkp(ptr, &buf[3 * (SIZE)]); \ } -TEST_ap(loadbzw2_ap, int, Z, 2, 0x00000000, +TEST_ap(loadbzw2_ap, int32_t, Z, 2, 0x00000000, 0x00020081, 0x00040083, 0x00060085, 0x00080087) -TEST_ap(loadbsw2_ap, int, S, 2, 0x0000ff00, +TEST_ap(loadbsw2_ap, int32_t, S, 2, 0x0000ff00, 0x00020081, 0x00040083, 0x00060085, 0x00080087) -TEST_ap(loadbzw4_ap, long long, Z, 4, 0x0000000000000000LL, +TEST_ap(loadbzw4_ap, int64_t, Z, 4, 0x0000000000000000LL, 0x0004008300020081LL, 0x0008008700060085LL, 0x000c008b000a0089LL, 0x0010008f000e008dLL) -TEST_ap(loadbsw4_ap, long long, S, 4, 0x0000ff000000ff00LL, +TEST_ap(loadbsw4_ap, int64_t, S, 4, 0x0000ff000000ff00LL, 0x0004008300020081LL, 0x0008008700060085LL, 0x000c008b000a0089LL, 0x0010008f000e008dLL) @@ -215,27 +196,27 @@ void test_##NAME(void) \ void *ptr = buf; \ init_buf(); \ BxW_LOAD_pr_##SIGN(result, ptr, (SIZE)); \ - check(result, (RES1) | (EXT)); \ + check64(result, (RES1) | (EXT)); \ checkp(ptr, &buf[1 * (SIZE)]); \ BxW_LOAD_pr_##SIGN(result, ptr, (SIZE)); \ - check(result, (RES2) | (EXT)); \ + check64(result, (RES2) | (EXT)); \ checkp(ptr, &buf[2 * (SIZE)]); \ BxW_LOAD_pr_##SIGN(result, ptr, (SIZE)); \ - check(result, (RES3) | (EXT)); \ + check64(result, (RES3) | (EXT)); \ checkp(ptr, &buf[3 * (SIZE)]); \ BxW_LOAD_pr_##SIGN(result, ptr, (SIZE)); \ - check(result, (RES4) | (EXT)); \ + check64(result, (RES4) | (EXT)); \ checkp(ptr, &buf[4 * (SIZE)]); \ } -TEST_pr(loadbzw2_pr, int, Z, 2, 0x00000000, +TEST_pr(loadbzw2_pr, int32_t, Z, 2, 0x00000000, 0x00020081, 0x0040083, 0x00060085, 0x00080087) -TEST_pr(loadbsw2_pr, int, S, 2, 0x0000ff00, +TEST_pr(loadbsw2_pr, int32_t, S, 2, 0x0000ff00, 0x00020081, 0x0040083, 0x00060085, 0x00080087) -TEST_pr(loadbzw4_pr, long long, Z, 4, 0x0000000000000000LL, +TEST_pr(loadbzw4_pr, int64_t, Z, 4, 0x0000000000000000LL, 0x0004008300020081LL, 0x0008008700060085LL, 0x000c008b000a0089LL, 0x0010008f000e008dLL) -TEST_pr(loadbsw4_pr, long long, S, 4, 0x0000ff000000ff00LL, +TEST_pr(loadbsw4_pr, int64_t, S, 4, 0x0000ff000000ff00LL, 0x0004008300020081LL, 0x0008008700060085LL, 0x000c008b000a0089LL, 0x0010008f000e008dLL) @@ -245,7 +226,7 @@ TEST_pr(loadbsw4_pr, long long, S, 4, 0x0000ff000000ff00LL, */ #define BxW_LOAD_pbr(SZ, RES, PTR) \ __asm__( \ - "r4 = #(1 << (16 - 3))\n\t" \ + "r4 = #(1 << (16 - 4))\n\t" \ "m0 = r4\n\t" \ "%0 = mem" #SZ "(%1++m0:brev)\n\t" \ : "=r"(RES), "+r"(PTR) \ @@ -263,25 +244,25 @@ void test_##NAME(void) \ void *ptr = buf; \ init_buf(); \ BxW_LOAD_pbr_##SIGN(result, ptr); \ - check(result, (RES1) | (EXT)); \ + check64(result, (RES1) | (EXT)); \ BxW_LOAD_pbr_##SIGN(result, ptr); \ - check(result, (RES2) | (EXT)); \ + check64(result, (RES2) | (EXT)); \ BxW_LOAD_pbr_##SIGN(result, ptr); \ - check(result, (RES3) | (EXT)); \ + check64(result, (RES3) | (EXT)); \ BxW_LOAD_pbr_##SIGN(result, ptr); \ - check(result, (RES4) | (EXT)); \ + check64(result, (RES4) | (EXT)); \ } -TEST_pbr(loadbzw2_pbr, int, Z, 0x00000000, - 0x00020081, 0x00060085, 0x00040083, 0x00080087) -TEST_pbr(loadbsw2_pbr, int, S, 0x0000ff00, - 0x00020081, 0x00060085, 0x00040083, 0x00080087) -TEST_pbr(loadbzw4_pbr, long long, Z, 0x0000000000000000LL, - 0x0004008300020081LL, 0x0008008700060085LL, - 0x0006008500040083LL, 0x000a008900080087LL) -TEST_pbr(loadbsw4_pbr, long long, S, 0x0000ff000000ff00LL, - 0x0004008300020081LL, 0x0008008700060085LL, - 0x0006008500040083LL, 0x000a008900080087LL) +TEST_pbr(loadbzw2_pbr, int32_t, Z, 0x00000000, + 0x00020081, 0x000a0089, 0x00060085, 0x000e008d) +TEST_pbr(loadbsw2_pbr, int32_t, S, 0x0000ff00, + 0x00020081, 0x000aff89, 0x0006ff85, 0x000eff8d) +TEST_pbr(loadbzw4_pbr, int64_t, Z, 0x0000000000000000LL, + 0x0004008300020081LL, 0x000c008b000a0089LL, + 0x0008008700060085LL, 0x0010008f000e008dLL) +TEST_pbr(loadbsw4_pbr, int64_t, S, 0x0000ff000000ff00LL, + 0x0004008300020081LL, 0x000cff8b000aff89LL, + 0x0008ff870006ff85LL, 0x0010ff8f000eff8dLL) /* **************************************************************************** @@ -303,27 +284,27 @@ void test_##NAME(void) \ void *ptr = buf; \ init_buf(); \ BxW_LOAD_pi_##SIGN(result, ptr, (INC)); \ - check(result, (RES1) | (EXT)); \ + check64(result, (RES1) | (EXT)); \ checkp(ptr, &buf[1 * (INC)]); \ BxW_LOAD_pi_##SIGN(result, ptr, (INC)); \ - check(result, (RES2) | (EXT)); \ + check64(result, (RES2) | (EXT)); \ checkp(ptr, &buf[2 * (INC)]); \ BxW_LOAD_pi_##SIGN(result, ptr, (INC)); \ - check(result, (RES3) | (EXT)); \ + check64(result, (RES3) | (EXT)); \ checkp(ptr, &buf[3 * (INC)]); \ BxW_LOAD_pi_##SIGN(result, ptr, (INC)); \ - check(result, (RES4) | (EXT)); \ + check64(result, (RES4) | (EXT)); \ checkp(ptr, &buf[4 * (INC)]); \ } -TEST_pi(loadbzw2_pi, int, Z, 2, 0x00000000, +TEST_pi(loadbzw2_pi, int32_t, Z, 2, 0x00000000, 0x00020081, 0x00040083, 0x00060085, 0x00080087) -TEST_pi(loadbsw2_pi, int, S, 2, 0x0000ff00, +TEST_pi(loadbsw2_pi, int32_t, S, 2, 0x0000ff00, 0x00020081, 0x00040083, 0x00060085, 0x00080087) -TEST_pi(loadbzw4_pi, long long, Z, 4, 0x0000000000000000LL, +TEST_pi(loadbzw4_pi, int64_t, Z, 4, 0x0000000000000000LL, 0x0004008300020081LL, 0x0008008700060085LL, 0x000c008b000a0089LL, 0x0010008f000e008dLL) -TEST_pi(loadbsw4_pi, long long, S, 4, 0x0000ff000000ff00LL, +TEST_pi(loadbsw4_pi, int64_t, S, 4, 0x0000ff000000ff00LL, 0x0004008300020081LL, 0x0008008700060085LL, 0x000c008b000a0089LL, 0x0010008f000e008dLL) @@ -352,27 +333,27 @@ void test_##NAME(void) \ void *ptr = buf; \ init_buf(); \ BxW_LOAD_pci_##SIGN(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES1) | (EXT)); \ + check64(result, (RES1) | (EXT)); \ checkp(ptr, &buf[(1 * (INC)) % (LEN)]); \ BxW_LOAD_pci_##SIGN(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES2) | (EXT)); \ + check64(result, (RES2) | (EXT)); \ checkp(ptr, &buf[(2 * (INC)) % (LEN)]); \ BxW_LOAD_pci_##SIGN(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES3) | (EXT)); \ + check64(result, (RES3) | (EXT)); \ checkp(ptr, &buf[(3 * (INC)) % (LEN)]); \ BxW_LOAD_pci_##SIGN(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES4) | (EXT)); \ + check64(result, (RES4) | (EXT)); \ checkp(ptr, &buf[(4 * (INC)) % (LEN)]); \ } -TEST_pci(loadbzw2_pci, int, Z, 6, 2, 0x00000000, +TEST_pci(loadbzw2_pci, int32_t, Z, 6, 2, 0x00000000, 0x00020081, 0x00040083, 0x00060085, 0x00020081) -TEST_pci(loadbsw2_pci, int, S, 6, 2, 0x0000ff00, +TEST_pci(loadbsw2_pci, int32_t, S, 6, 2, 0x0000ff00, 0x00020081, 0x00040083, 0x00060085, 0x00020081) -TEST_pci(loadbzw4_pci, long long, Z, 8, 4, 0x0000000000000000LL, +TEST_pci(loadbzw4_pci, int64_t, Z, 8, 4, 0x0000000000000000LL, 0x0004008300020081LL, 0x0008008700060085LL, 0x0004008300020081LL, 0x0008008700060085LL) -TEST_pci(loadbsw4_pci, long long, S, 8, 4, 0x0000ff000000ff00LL, +TEST_pci(loadbsw4_pci, int64_t, S, 8, 4, 0x0000ff000000ff00LL, 0x0004008300020081LL, 0x0008008700060085LL, 0x0004008300020081LL, 0x0008008700060085LL) @@ -403,27 +384,27 @@ void test_##NAME(void) \ void *ptr = buf; \ init_buf(); \ BxW_LOAD_pcr_##SIGN(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES1) | (EXT)); \ + check64(result, (RES1) | (EXT)); \ checkp(ptr, &buf[(1 * (INC) * (SIZE)) % (LEN)]); \ BxW_LOAD_pcr_##SIGN(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES2) | (EXT)); \ + check64(result, (RES2) | (EXT)); \ checkp(ptr, &buf[(2 * (INC) * (SIZE)) % (LEN)]); \ BxW_LOAD_pcr_##SIGN(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES3) | (EXT)); \ + check64(result, (RES3) | (EXT)); \ checkp(ptr, &buf[(3 * (INC) * (SIZE)) % (LEN)]); \ BxW_LOAD_pcr_##SIGN(result, ptr, buf, (LEN), (INC)); \ - check(result, (RES4) | (EXT)); \ + check64(result, (RES4) | (EXT)); \ checkp(ptr, &buf[(4 * (INC) * (SIZE)) % (LEN)]); \ } -TEST_pcr(loadbzw2_pcr, int, Z, 2, 8, 2, 0x00000000, +TEST_pcr(loadbzw2_pcr, int32_t, Z, 2, 8, 2, 0x00000000, 0x00020081, 0x00060085, 0x00020081, 0x00060085) -TEST_pcr(loadbsw2_pcr, int, S, 2, 8, 2, 0x0000ff00, +TEST_pcr(loadbsw2_pcr, int32_t, S, 2, 8, 2, 0x0000ff00, 0x00020081, 0x00060085, 0x00020081, 0x00060085) -TEST_pcr(loadbzw4_pcr, long long, Z, 4, 8, 1, 0x0000000000000000LL, +TEST_pcr(loadbzw4_pcr, int64_t, Z, 4, 8, 1, 0x0000000000000000LL, 0x0004008300020081LL, 0x0008008700060085LL, 0x0004008300020081LL, 0x0008008700060085LL) -TEST_pcr(loadbsw4_pcr, long long, S, 4, 8, 1, 0x0000ff000000ff00LL, +TEST_pcr(loadbsw4_pcr, int64_t, S, 4, 8, 1, 0x0000ff000000ff00LL, 0x0004008300020081LL, 0x0008008700060085LL, 0x0004008300020081LL, 0x0008008700060085LL) diff --git a/tests/tcg/hexagon/mem_noshuf.c b/tests/tcg/hexagon/mem_noshuf.c index dd714d5e98..6263d5ef8e 100644 --- a/tests/tcg/hexagon/mem_noshuf.c +++ b/tests/tcg/hexagon/mem_noshuf.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,6 +16,12 @@ */ #include +#include +#include + +int err; + +#include "hex_test.h" /* * Make sure that the :mem_noshuf packet attribute is honored. @@ -25,9 +31,9 @@ */ #define MEM_NOSHUF32(NAME, ST_TYPE, LD_TYPE, ST_OP, LD_OP) \ -static inline unsigned int NAME(ST_TYPE * p, LD_TYPE * q, ST_TYPE x) \ +static inline uint32_t NAME(ST_TYPE * p, LD_TYPE * q, ST_TYPE x) \ { \ - unsigned int ret; \ + uint32_t ret; \ asm volatile("{\n\t" \ " " #ST_OP "(%1) = %3\n\t" \ " %0 = " #LD_OP "(%2)\n\t" \ @@ -39,9 +45,9 @@ static inline unsigned int NAME(ST_TYPE * p, LD_TYPE * q, ST_TYPE x) \ } #define MEM_NOSHUF64(NAME, ST_TYPE, LD_TYPE, ST_OP, LD_OP) \ -static inline unsigned long long NAME(ST_TYPE * p, LD_TYPE * q, ST_TYPE x) \ +static inline uint64_t NAME(ST_TYPE * p, LD_TYPE * q, ST_TYPE x) \ { \ - unsigned long long ret; \ + uint64_t ret; \ asm volatile("{\n\t" \ " " #ST_OP "(%1) = %3\n\t" \ " %0 = " #LD_OP "(%2)\n\t" \ @@ -53,40 +59,106 @@ static inline unsigned long long NAME(ST_TYPE * p, LD_TYPE * q, ST_TYPE x) \ } /* Store byte combinations */ -MEM_NOSHUF32(mem_noshuf_sb_lb, signed char, signed char, memb, memb) -MEM_NOSHUF32(mem_noshuf_sb_lub, signed char, unsigned char, memb, memub) -MEM_NOSHUF32(mem_noshuf_sb_lh, signed char, signed short, memb, memh) -MEM_NOSHUF32(mem_noshuf_sb_luh, signed char, unsigned short, memb, memuh) -MEM_NOSHUF32(mem_noshuf_sb_lw, signed char, signed int, memb, memw) -MEM_NOSHUF64(mem_noshuf_sb_ld, signed char, signed long long, memb, memd) +MEM_NOSHUF32(mem_noshuf_sb_lb, int8_t, int8_t, memb, memb) +MEM_NOSHUF32(mem_noshuf_sb_lub, int8_t, uint8_t, memb, memub) +MEM_NOSHUF32(mem_noshuf_sb_lh, int8_t, int16_t, memb, memh) +MEM_NOSHUF32(mem_noshuf_sb_luh, int8_t, uint16_t, memb, memuh) +MEM_NOSHUF32(mem_noshuf_sb_lw, int8_t, int32_t, memb, memw) +MEM_NOSHUF64(mem_noshuf_sb_ld, int8_t, int64_t, memb, memd) /* Store half combinations */ -MEM_NOSHUF32(mem_noshuf_sh_lb, signed short, signed char, memh, memb) -MEM_NOSHUF32(mem_noshuf_sh_lub, signed short, unsigned char, memh, memub) -MEM_NOSHUF32(mem_noshuf_sh_lh, signed short, signed short, memh, memh) -MEM_NOSHUF32(mem_noshuf_sh_luh, signed short, unsigned short, memh, memuh) -MEM_NOSHUF32(mem_noshuf_sh_lw, signed short, signed int, memh, memw) -MEM_NOSHUF64(mem_noshuf_sh_ld, signed short, signed long long, memh, memd) +MEM_NOSHUF32(mem_noshuf_sh_lb, int16_t, int8_t, memh, memb) +MEM_NOSHUF32(mem_noshuf_sh_lub, int16_t, uint8_t, memh, memub) +MEM_NOSHUF32(mem_noshuf_sh_lh, int16_t, int16_t, memh, memh) +MEM_NOSHUF32(mem_noshuf_sh_luh, int16_t, uint16_t, memh, memuh) +MEM_NOSHUF32(mem_noshuf_sh_lw, int16_t, int32_t, memh, memw) +MEM_NOSHUF64(mem_noshuf_sh_ld, int16_t, int64_t, memh, memd) /* Store word combinations */ -MEM_NOSHUF32(mem_noshuf_sw_lb, signed int, signed char, memw, memb) -MEM_NOSHUF32(mem_noshuf_sw_lub, signed int, unsigned char, memw, memub) -MEM_NOSHUF32(mem_noshuf_sw_lh, signed int, signed short, memw, memh) -MEM_NOSHUF32(mem_noshuf_sw_luh, signed int, unsigned short, memw, memuh) -MEM_NOSHUF32(mem_noshuf_sw_lw, signed int, signed int, memw, memw) -MEM_NOSHUF64(mem_noshuf_sw_ld, signed int, signed long long, memw, memd) +MEM_NOSHUF32(mem_noshuf_sw_lb, int32_t, int8_t, memw, memb) +MEM_NOSHUF32(mem_noshuf_sw_lub, int32_t, uint8_t, memw, memub) +MEM_NOSHUF32(mem_noshuf_sw_lh, int32_t, int16_t, memw, memh) +MEM_NOSHUF32(mem_noshuf_sw_luh, int32_t, uint16_t, memw, memuh) +MEM_NOSHUF32(mem_noshuf_sw_lw, int32_t, int32_t, memw, memw) +MEM_NOSHUF64(mem_noshuf_sw_ld, int32_t, int64_t, memw, memd) /* Store double combinations */ -MEM_NOSHUF32(mem_noshuf_sd_lb, long long, signed char, memd, memb) -MEM_NOSHUF32(mem_noshuf_sd_lub, long long, unsigned char, memd, memub) -MEM_NOSHUF32(mem_noshuf_sd_lh, long long, signed short, memd, memh) -MEM_NOSHUF32(mem_noshuf_sd_luh, long long, unsigned short, memd, memuh) -MEM_NOSHUF32(mem_noshuf_sd_lw, long long, signed int, memd, memw) -MEM_NOSHUF64(mem_noshuf_sd_ld, long long, signed long long, memd, memd) +MEM_NOSHUF32(mem_noshuf_sd_lb, int64_t, int8_t, memd, memb) +MEM_NOSHUF32(mem_noshuf_sd_lub, int64_t, uint8_t, memd, memub) +MEM_NOSHUF32(mem_noshuf_sd_lh, int64_t, int16_t, memd, memh) +MEM_NOSHUF32(mem_noshuf_sd_luh, int64_t, uint16_t, memd, memuh) +MEM_NOSHUF32(mem_noshuf_sd_lw, int64_t, int32_t, memd, memw) +MEM_NOSHUF64(mem_noshuf_sd_ld, int64_t, int64_t, memd, memd) -static inline unsigned int cancel_sw_lb(int pred, int *p, signed char *q, int x) +static inline int pred_lw_sw(bool pred, int32_t *p, int32_t *q, + int32_t x, int32_t y) { - unsigned int ret; + int ret; + asm volatile("p0 = cmp.eq(%5, #0)\n\t" + "%0 = %3\n\t" + "{\n\t" + " memw(%1) = %4\n\t" + " if (!p0) %0 = memw(%2)\n\t" + "}:mem_noshuf\n" + : "=&r"(ret) + : "r"(p), "r"(q), "r"(x), "r"(y), "r"(pred) + : "p0", "memory"); + return ret; +} + +static inline int pred_lw_sw_pi(bool pred, int32_t *p, int32_t *q, + int32_t x, int32_t y) +{ + int ret; + asm volatile("p0 = cmp.eq(%5, #0)\n\t" + "%0 = %3\n\t" + "r7 = %2\n\t" + "{\n\t" + " memw(%1) = %4\n\t" + " if (!p0) %0 = memw(r7++#4)\n\t" + "}:mem_noshuf\n" + : "=&r"(ret) + : "r"(p), "r"(q), "r"(x), "r"(y), "r"(pred) + : "r7", "p0", "memory"); + return ret; +} + +static inline int64_t pred_ld_sd(bool pred, int64_t *p, int64_t *q, + int64_t x, int64_t y) +{ + int64_t ret; + asm volatile("p0 = cmp.eq(%5, #0)\n\t" + "%0 = %3\n\t" + "{\n\t" + " memd(%1) = %4\n\t" + " if (!p0) %0 = memd(%2)\n\t" + "}:mem_noshuf\n" + : "=&r"(ret) + : "r"(p), "r"(q), "r"(x), "r"(y), "r"(pred) + : "p0", "memory"); + return ret; +} + +static inline int64_t pred_ld_sd_pi(bool pred, int64_t *p, int64_t *q, + int64_t x, int64_t y) +{ + int64_t ret; + asm volatile("p0 = cmp.eq(%5, #0)\n\t" + "%0 = %3\n\t" + "r7 = %2\n\t" + "{\n\t" + " memd(%1) = %4\n\t" + " if (!p0) %0 = memd(r7++#8)\n\t" + "}:mem_noshuf\n" + : "=&r"(ret) + : "r"(p), "r"(q), "r"(x), "r"(y), "r"(pred) + : "r7", "p0", "memory"); + return ret; +} + +static inline int32_t cancel_sw_lb(bool pred, int32_t *p, int8_t *q, int32_t x) +{ + int32_t ret; asm volatile("p0 = cmp.eq(%4, #0)\n\t" "{\n\t" " if (!p0) memw(%1) = %3\n\t" @@ -98,10 +170,9 @@ static inline unsigned int cancel_sw_lb(int pred, int *p, signed char *q, int x) return ret; } -static inline -unsigned long long cancel_sw_ld(int pred, int *p, long long *q, int x) +static inline int64_t cancel_sw_ld(bool pred, int32_t *p, int64_t *q, int32_t x) { - long long ret; + int64_t ret; asm volatile("p0 = cmp.eq(%4, #0)\n\t" "{\n\t" " if (!p0) memw(%1) = %3\n\t" @@ -114,39 +185,21 @@ unsigned long long cancel_sw_ld(int pred, int *p, long long *q, int x) } typedef union { - signed long long d[2]; - unsigned long long ud[2]; - signed int w[4]; - unsigned int uw[4]; - signed short h[8]; - unsigned short uh[8]; - signed char b[16]; - unsigned char ub[16]; + int64_t d[2]; + uint64_t ud[2]; + int32_t w[4]; + uint32_t uw[4]; + int16_t h[8]; + uint16_t uh[8]; + int8_t b[16]; + uint8_t ub[16]; } Memory; -int err; - -static void check32(int n, int expect) -{ - if (n != expect) { - printf("ERROR: 0x%08x != 0x%08x\n", n, expect); - err++; - } -} - -static void check64(long long n, long long expect) -{ - if (n != expect) { - printf("ERROR: 0x%08llx != 0x%08llx\n", n, expect); - err++; - } -} - int main() { Memory n; - unsigned int res32; - unsigned long long res64; + uint32_t res32; + uint64_t res64; /* * Store byte combinations @@ -260,30 +313,30 @@ int main() * Predicated word stores */ n.w[0] = ~0; - res32 = cancel_sw_lb(0, &n.w[0], &n.b[0], 0x12345678); + res32 = cancel_sw_lb(false, &n.w[0], &n.b[0], 0x12345678); check32(res32, 0xffffffff); n.w[0] = ~0; - res32 = cancel_sw_lb(1, &n.w[0], &n.b[0], 0x12345687); + res32 = cancel_sw_lb(true, &n.w[0], &n.b[0], 0x12345687); check32(res32, 0xffffff87); /* * Predicated double stores */ n.d[0] = ~0LL; - res64 = cancel_sw_ld(0, &n.w[0], &n.d[0], 0x12345678); + res64 = cancel_sw_ld(false, &n.w[0], &n.d[0], 0x12345678); check64(res64, 0xffffffffffffffffLL); n.d[0] = ~0LL; - res64 = cancel_sw_ld(1, &n.w[0], &n.d[0], 0x12345678); + res64 = cancel_sw_ld(true, &n.w[0], &n.d[0], 0x12345678); check64(res64, 0xffffffff12345678LL); n.d[0] = ~0LL; - res64 = cancel_sw_ld(0, &n.w[1], &n.d[0], 0x12345678); + res64 = cancel_sw_ld(false, &n.w[1], &n.d[0], 0x12345678); check64(res64, 0xffffffffffffffffLL); n.d[0] = ~0LL; - res64 = cancel_sw_ld(1, &n.w[1], &n.d[0], 0x12345678); + res64 = cancel_sw_ld(true, &n.w[1], &n.d[0], 0x12345678); check64(res64, 0x12345678ffffffffLL); /* @@ -323,6 +376,50 @@ int main() res64 = mem_noshuf_sd_ld(&n.d[0], &n.d[1], 0x123456789abcdef0LL); check64(res64, 0xffffffffffffffffLL); + n.w[0] = ~0; + res32 = pred_lw_sw(false, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda); + check32(res32, 0x12345678); + check32(n.w[0], 0xc0ffeeda); + + n.w[0] = ~0; + res32 = pred_lw_sw(true, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda); + check32(res32, 0xc0ffeeda); + check32(n.w[0], 0xc0ffeeda); + + n.w[0] = ~0; + res32 = pred_lw_sw_pi(false, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda); + check32(res32, 0x12345678); + check32(n.w[0], 0xc0ffeeda); + + n.w[0] = ~0; + res32 = pred_lw_sw_pi(true, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda); + check32(res32, 0xc0ffeeda); + check32(n.w[0], 0xc0ffeeda); + + n.d[0] = ~0LL; + res64 = pred_ld_sd(false, &n.d[0], &n.d[0], + 0x1234567812345678LL, 0xc0ffeedac0ffeedaLL); + check64(res64, 0x1234567812345678LL); + check64(n.d[0], 0xc0ffeedac0ffeedaLL); + + n.d[0] = ~0LL; + res64 = pred_ld_sd(true, &n.d[0], &n.d[0], + 0x1234567812345678LL, 0xc0ffeedac0ffeedaLL); + check64(res64, 0xc0ffeedac0ffeedaLL); + check64(n.d[0], 0xc0ffeedac0ffeedaLL); + + n.d[0] = ~0LL; + res64 = pred_ld_sd_pi(false, &n.d[0], &n.d[0], + 0x1234567812345678LL, 0xc0ffeedac0ffeedaLL); + check64(res64, 0x1234567812345678LL); + check64(n.d[0], 0xc0ffeedac0ffeedaLL); + + n.d[0] = ~0LL; + res64 = pred_ld_sd_pi(true, &n.d[0], &n.d[0], + 0x1234567812345678LL, 0xc0ffeedac0ffeedaLL); + check64(res64, 0xc0ffeedac0ffeedaLL); + check64(n.d[0], 0xc0ffeedac0ffeedaLL); + puts(err ? "FAIL" : "PASS"); return err; } diff --git a/tests/tcg/hexagon/mem_noshuf_exception.c b/tests/tcg/hexagon/mem_noshuf_exception.c new file mode 100644 index 0000000000..61108a99be --- /dev/null +++ b/tests/tcg/hexagon/mem_noshuf_exception.c @@ -0,0 +1,130 @@ +/* + * Copyright(c) 2022-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * Test the VLIW semantics of exceptions with mem_noshuf + * + * When a packet has the :mem_noshuf attribute, the semantics dictate + * That the load will get the data from the store if the addresses overlap. + * To accomplish this, we perform the store first. However, we have to + * handle the case where the store raises an exception. In that case, the + * store should not alter the machine state. + * + * We test this with a mem_noshuf packet with a store to a global variable, + * "should_not_change" and a load from NULL. After the SIGSEGV is caught, + * we check * that the "should_not_change" value is the same. + * + * We also check that a predicated load where the predicate is false doesn't + * raise an exception and allows the store to happen. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int err; + +#include "hex_test.h" + +bool segv_caught; + +#define SHOULD_NOT_CHANGE_VAL 5 +int32_t should_not_change = SHOULD_NOT_CHANGE_VAL; + +#define OK_TO_CHANGE_VAL 13 +int32_t ok_to_change = OK_TO_CHANGE_VAL; + +jmp_buf jmp_env; + +static void sig_segv(int sig, siginfo_t *info, void *puc) +{ + check32(sig, SIGSEGV); + segv_caught = true; + longjmp(jmp_env, 1); +} + +int main() +{ + struct sigaction act; + int32_t dummy32; + int64_t dummy64; + void *p; + + /* SIGSEGV test */ + act.sa_sigaction = sig_segv; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + chk_error(sigaction(SIGSEGV, &act, NULL)); + if (setjmp(jmp_env) == 0) { + asm volatile("r18 = ##should_not_change\n\t" + "r19 = #0\n\t" + "{\n\t" + " memw(r18) = #7\n\t" + " %0 = memw(r19)\n\t" + "}:mem_noshuf\n\t" + : "=r"(dummy32) : : "r18", "r19", "memory"); + } + + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + chk_error(sigaction(SIGSEGV, &act, NULL)); + + check32(segv_caught, true); + check32(should_not_change, SHOULD_NOT_CHANGE_VAL); + + /* + * Check that a predicated load where the predicate is false doesn't + * raise an exception and allows the store to happen. + */ + asm volatile("r18 = ##ok_to_change\n\t" + "r19 = #0\n\t" + "p0 = cmp.gt(r0, r0)\n\t" + "{\n\t" + " memw(r18) = #7\n\t" + " if (p0) %0 = memw(r19)\n\t" + "}:mem_noshuf\n\t" + : "=r"(dummy32) : : "r18", "r19", "p0", "memory"); + + check32(ok_to_change, 7); + + /* + * Also check that the post-increment doesn't happen when the + * predicate is false. + */ + ok_to_change = OK_TO_CHANGE_VAL; + p = NULL; + asm volatile("r18 = ##ok_to_change\n\t" + "p0 = cmp.gt(r0, r0)\n\t" + "{\n\t" + " memw(r18) = #9\n\t" + " if (p0) %1 = memd(%0 ++ #8)\n\t" + "}:mem_noshuf\n\t" + : "+r"(p), "=r"(dummy64) : : "r18", "p0", "memory"); + + check32(ok_to_change, 9); + check32((int)p, (int)NULL); + + puts(err ? "FAIL" : "PASS"); + return err ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/tests/tcg/hexagon/misc.c b/tests/tcg/hexagon/misc.c index f0b1947fb3..ca22bb79f7 100644 --- a/tests/tcg/hexagon/misc.c +++ b/tests/tcg/hexagon/misc.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,12 +16,15 @@ */ #include +#include +#include #include -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; +int err; +#include "hex_test.h" + +#define CORE_HAS_CABAC (__HEXAGON_ARCH__ <= 71) static inline void S4_storerhnew_rr(void *p, int index, uint16_t v) { @@ -73,7 +76,7 @@ static inline void *S4_storerinew_ap(uint32_t v) return ret; } -static inline void S4_storeirbt_io(void *p, int pred) +static inline void S4_storeirbt_io(void *p, bool pred) { asm volatile("p0 = cmp.eq(%0, #1)\n\t" "if (p0) memb(%1+#4)=#27\n\t" @@ -81,7 +84,7 @@ static inline void S4_storeirbt_io(void *p, int pred) : "p0", "memory"); } -static inline void S4_storeirbf_io(void *p, int pred) +static inline void S4_storeirbf_io(void *p, bool pred) { asm volatile("p0 = cmp.eq(%0, #1)\n\t" "if (!p0) memb(%1+#4)=#27\n\t" @@ -89,7 +92,7 @@ static inline void S4_storeirbf_io(void *p, int pred) : "p0", "memory"); } -static inline void S4_storeirbtnew_io(void *p, int pred) +static inline void S4_storeirbtnew_io(void *p, bool pred) { asm volatile("{\n\t" " p0 = cmp.eq(%0, #1)\n\t" @@ -99,7 +102,7 @@ static inline void S4_storeirbtnew_io(void *p, int pred) : "p0", "memory"); } -static inline void S4_storeirbfnew_io(void *p, int pred) +static inline void S4_storeirbfnew_io(void *p, bool pred) { asm volatile("{\n\t" " p0 = cmp.eq(%0, #1)\n\t" @@ -109,7 +112,7 @@ static inline void S4_storeirbfnew_io(void *p, int pred) : "p0", "memory"); } -static inline void S4_storeirht_io(void *p, int pred) +static inline void S4_storeirht_io(void *p, bool pred) { asm volatile("p0 = cmp.eq(%0, #1)\n\t" "if (p0) memh(%1+#4)=#27\n\t" @@ -117,7 +120,7 @@ static inline void S4_storeirht_io(void *p, int pred) : "p0", "memory"); } -static inline void S4_storeirhf_io(void *p, int pred) +static inline void S4_storeirhf_io(void *p, bool pred) { asm volatile("p0 = cmp.eq(%0, #1)\n\t" "if (!p0) memh(%1+#4)=#27\n\t" @@ -125,7 +128,7 @@ static inline void S4_storeirhf_io(void *p, int pred) : "p0", "memory"); } -static inline void S4_storeirhtnew_io(void *p, int pred) +static inline void S4_storeirhtnew_io(void *p, bool pred) { asm volatile("{\n\t" " p0 = cmp.eq(%0, #1)\n\t" @@ -135,7 +138,7 @@ static inline void S4_storeirhtnew_io(void *p, int pred) : "p0", "memory"); } -static inline void S4_storeirhfnew_io(void *p, int pred) +static inline void S4_storeirhfnew_io(void *p, bool pred) { asm volatile("{\n\t" " p0 = cmp.eq(%0, #1)\n\t" @@ -145,7 +148,7 @@ static inline void S4_storeirhfnew_io(void *p, int pred) : "p0", "memory"); } -static inline void S4_storeirit_io(void *p, int pred) +static inline void S4_storeirit_io(void *p, bool pred) { asm volatile("p0 = cmp.eq(%0, #1)\n\t" "if (p0) memw(%1+#4)=#27\n\t" @@ -153,7 +156,7 @@ static inline void S4_storeirit_io(void *p, int pred) : "p0", "memory"); } -static inline void S4_storeirif_io(void *p, int pred) +static inline void S4_storeirif_io(void *p, bool pred) { asm volatile("p0 = cmp.eq(%0, #1)\n\t" "if (!p0) memw(%1+#4)=#27\n\t" @@ -161,7 +164,7 @@ static inline void S4_storeirif_io(void *p, int pred) : "p0", "memory"); } -static inline void S4_storeiritnew_io(void *p, int pred) +static inline void S4_storeiritnew_io(void *p, bool pred) { asm volatile("{\n\t" " p0 = cmp.eq(%0, #1)\n\t" @@ -171,7 +174,7 @@ static inline void S4_storeiritnew_io(void *p, int pred) : "p0", "memory"); } -static inline void S4_storeirifnew_io(void *p, int pred) +static inline void S4_storeirifnew_io(void *p, bool pred) { asm volatile("{\n\t" " p0 = cmp.eq(%0, #1)\n\t" @@ -181,15 +184,15 @@ static inline void S4_storeirifnew_io(void *p, int pred) : "p0", "memory"); } -static int L2_ploadrifnew_pi(void *p, int pred) +static int32_t L2_ploadrifnew_pi(void *p, bool pred) { - int result; + int32_t result; asm volatile("%0 = #31\n\t" "{\n\t" - " p0 = cmp.eq(%1, #1)\n\t" - " if (!p0.new) %0 = memw(%2++#4)\n\t" + " p0 = cmp.eq(%2, #1)\n\t" + " if (!p0.new) %0 = memw(%1++#4)\n\t" "}\n\t" - : "=r"(result) : "r"(pred), "r"(p) + : "=&r"(result), "+r"(p) : "r"(pred) : "p0"); return result; } @@ -200,9 +203,9 @@ static int L2_ploadrifnew_pi(void *p, int pred) * account for auto-anding. Then, we can do the predicated * jump. */ -static inline int cmpnd_cmp_jump(void) +static inline int32_t cmpnd_cmp_jump(void) { - int retval; + int32_t retval; asm ("r5 = #7\n\t" "r6 = #9\n\t" "{\n\t" @@ -219,9 +222,9 @@ static inline int cmpnd_cmp_jump(void) return retval; } -static inline int test_clrtnew(int arg1, int old_val) +static inline int32_t test_clrtnew(int32_t arg1, int32_t old_val) { - int ret; + int32_t ret; asm volatile("r5 = %2\n\t" "{\n\t" "p0 = cmp.eq(%1, #1)\n\t" @@ -234,34 +237,16 @@ static inline int test_clrtnew(int arg1, int old_val) return ret; } -int err; - -static void check(int val, int expect) -{ - if (val != expect) { - printf("ERROR: 0x%04x != 0x%04x\n", val, expect); - err++; - } -} - -static void check64(long long val, long long expect) -{ - if (val != expect) { - printf("ERROR: 0x%016llx != 0x%016llx\n", val, expect); - err++; - } -} - uint32_t init[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; uint32_t array[10]; -uint32_t early_exit; +bool early_exit; /* * Write this as a function because we can't guarantee the compiler will * allocate a frame with just the SL2_return_tnew packet. */ -static void SL2_return_tnew(int x); +static void SL2_return_tnew(bool pred); asm ("SL2_return_tnew:\n\t" " allocframe(#0)\n\t" " r1 = #1\n\t" @@ -275,9 +260,9 @@ asm ("SL2_return_tnew:\n\t" " dealloc_return\n\t" ); -static long long creg_pair(int x, int y) +static int64_t creg_pair(int32_t x, int32_t y) { - long long retval; + int64_t retval; asm ("m0 = %1\n\t" "m1 = %2\n\t" "%0 = c7:6\n\t" @@ -285,20 +270,22 @@ static long long creg_pair(int x, int y) return retval; } -static long long decbin(long long x, long long y, int *pred) +#if CORE_HAS_CABAC +static int64_t decbin(int64_t x, int64_t y, bool *pred) { - long long retval; + int64_t retval; asm ("%0 = decbin(%2, %3)\n\t" "%1 = p0\n\t" : "=r"(retval), "=r"(*pred) : "r"(x), "r"(y)); return retval; } +#endif /* Check that predicates are auto-and'ed in a packet */ -static int auto_and(void) +static bool auto_and(void) { - int retval; + bool retval; asm ("r5 = #1\n\t" "{\n\t" " p0 = cmp.eq(r1, #1)\n\t" @@ -313,7 +300,7 @@ static int auto_and(void) void test_lsbnew(void) { - int result; + int32_t result; asm("r0 = #2\n\t" "r1 = #5\n\t" @@ -323,7 +310,7 @@ void test_lsbnew(void) "}\n\t" "%0 = r1\n\t" : "=r"(result) :: "r0", "r1", "p0"); - check(result, 5); + check32(result, 5); } void test_l2fetch(void) @@ -333,141 +320,233 @@ void test_l2fetch(void) "l2fetch(r0, r3:2)\n\t"); } +static inline int32_t ct0(uint32_t x) +{ + int32_t res; + asm("%0 = ct0(%1)\n\t" : "=r"(res) : "r"(x)); + return res; +} + +static inline int32_t ct1(uint32_t x) +{ + int32_t res; + asm("%0 = ct1(%1)\n\t" : "=r"(res) : "r"(x)); + return res; +} + +static inline int32_t ct0p(uint64_t x) +{ + int32_t res; + asm("%0 = ct0(%1)\n\t" : "=r"(res) : "r"(x)); + return res; +} + +static inline int32_t ct1p(uint64_t x) +{ + int32_t res; + asm("%0 = ct1(%1)\n\t" : "=r"(res) : "r"(x)); + return res; +} + +void test_count_trailing_zeros_ones(void) +{ + check32(ct0(0x0000000f), 0); + check32(ct0(0x00000000), 32); + check32(ct0(0x000000f0), 4); + + check32(ct1(0x000000f0), 0); + check32(ct1(0x0000000f), 4); + check32(ct1(0x00000000), 0); + check32(ct1(0xffffffff), 32); + + check32(ct0p(0x000000000000000fULL), 0); + check32(ct0p(0x0000000000000000ULL), 64); + check32(ct0p(0x00000000000000f0ULL), 4); + + check32(ct1p(0x00000000000000f0ULL), 0); + check32(ct1p(0x000000000000000fULL), 4); + check32(ct1p(0x0000000000000000ULL), 0); + check32(ct1p(0xffffffffffffffffULL), 64); + check32(ct1p(0xffffffffff0fffffULL), 20); + check32(ct1p(0xffffff0fffffffffULL), 36); +} + +static inline int32_t dpmpyss_rnd_s0(int32_t x, int32_t y) +{ + int32_t res; + asm("%0 = mpy(%1, %2):rnd\n\t" : "=r"(res) : "r"(x), "r"(y)); + return res; +} + +void test_dpmpyss_rnd_s0(void) +{ + check32(dpmpyss_rnd_s0(-1, 0x80000000), 1); + check32(dpmpyss_rnd_s0(0, 0x80000000), 0); + check32(dpmpyss_rnd_s0(1, 0x80000000), 0); + check32(dpmpyss_rnd_s0(0x7fffffff, 0x80000000), 0xc0000001); + check32(dpmpyss_rnd_s0(0x80000000, -1), 1); + check32(dpmpyss_rnd_s0(-1, -1), 0); + check32(dpmpyss_rnd_s0(0, -1), 0); + check32(dpmpyss_rnd_s0(1, -1), 0); + check32(dpmpyss_rnd_s0(0x7fffffff, -1), 0); + check32(dpmpyss_rnd_s0(0x80000000, 0), 0); + check32(dpmpyss_rnd_s0(-1, 0), 0); + check32(dpmpyss_rnd_s0(0, 0), 0); + check32(dpmpyss_rnd_s0(1, 0), 0); + check32(dpmpyss_rnd_s0(-1, -1), 0); + check32(dpmpyss_rnd_s0(0, -1), 0); + check32(dpmpyss_rnd_s0(1, -1), 0); + check32(dpmpyss_rnd_s0(0x7fffffff, 1), 0); + check32(dpmpyss_rnd_s0(0x80000000, 0x7fffffff), 0xc0000001); + check32(dpmpyss_rnd_s0(-1, 0x7fffffff), 0); + check32(dpmpyss_rnd_s0(0, 0x7fffffff), 0); + check32(dpmpyss_rnd_s0(1, 0x7fffffff), 0); + check32(dpmpyss_rnd_s0(0x7fffffff, 0x7fffffff), 0x3fffffff); +} + int main() { - int res; - long long res64; - int pred; + int32_t res; + int64_t res64; + bool pred; memcpy(array, init, sizeof(array)); S4_storerhnew_rr(array, 4, 0xffff); - check(array[4], 0xffff); + check32(array[4], 0xffff); data = ~0; - check((uint32_t)S4_storerbnew_ap(0x12), (uint32_t)&data); - check(data, 0xffffff12); + checkp(S4_storerbnew_ap(0x12), &data); + check32(data, 0xffffff12); data = ~0; - check((uint32_t)S4_storerhnew_ap(0x1234), (uint32_t)&data); - check(data, 0xffff1234); + checkp(S4_storerhnew_ap(0x1234), &data); + check32(data, 0xffff1234); data = ~0; - check((uint32_t)S4_storerinew_ap(0x12345678), (uint32_t)&data); - check(data, 0x12345678); + checkp(S4_storerinew_ap(0x12345678), &data); + check32(data, 0x12345678); /* Byte */ memcpy(array, init, sizeof(array)); - S4_storeirbt_io(&array[1], 1); - check(array[2], 27); - S4_storeirbt_io(&array[2], 0); - check(array[3], 3); + S4_storeirbt_io(&array[1], true); + check32(array[2], 27); + S4_storeirbt_io(&array[2], false); + check32(array[3], 3); memcpy(array, init, sizeof(array)); - S4_storeirbf_io(&array[3], 0); - check(array[4], 27); - S4_storeirbf_io(&array[4], 1); - check(array[5], 5); + S4_storeirbf_io(&array[3], false); + check32(array[4], 27); + S4_storeirbf_io(&array[4], true); + check32(array[5], 5); memcpy(array, init, sizeof(array)); - S4_storeirbtnew_io(&array[5], 1); - check(array[6], 27); - S4_storeirbtnew_io(&array[6], 0); - check(array[7], 7); + S4_storeirbtnew_io(&array[5], true); + check32(array[6], 27); + S4_storeirbtnew_io(&array[6], false); + check32(array[7], 7); memcpy(array, init, sizeof(array)); - S4_storeirbfnew_io(&array[7], 0); - check(array[8], 27); - S4_storeirbfnew_io(&array[8], 1); - check(array[9], 9); + S4_storeirbfnew_io(&array[7], false); + check32(array[8], 27); + S4_storeirbfnew_io(&array[8], true); + check32(array[9], 9); /* Half word */ memcpy(array, init, sizeof(array)); - S4_storeirht_io(&array[1], 1); - check(array[2], 27); - S4_storeirht_io(&array[2], 0); - check(array[3], 3); + S4_storeirht_io(&array[1], true); + check32(array[2], 27); + S4_storeirht_io(&array[2], false); + check32(array[3], 3); memcpy(array, init, sizeof(array)); - S4_storeirhf_io(&array[3], 0); - check(array[4], 27); - S4_storeirhf_io(&array[4], 1); - check(array[5], 5); + S4_storeirhf_io(&array[3], false); + check32(array[4], 27); + S4_storeirhf_io(&array[4], true); + check32(array[5], 5); memcpy(array, init, sizeof(array)); - S4_storeirhtnew_io(&array[5], 1); - check(array[6], 27); - S4_storeirhtnew_io(&array[6], 0); - check(array[7], 7); + S4_storeirhtnew_io(&array[5], true); + check32(array[6], 27); + S4_storeirhtnew_io(&array[6], false); + check32(array[7], 7); memcpy(array, init, sizeof(array)); - S4_storeirhfnew_io(&array[7], 0); - check(array[8], 27); - S4_storeirhfnew_io(&array[8], 1); - check(array[9], 9); + S4_storeirhfnew_io(&array[7], false); + check32(array[8], 27); + S4_storeirhfnew_io(&array[8], true); + check32(array[9], 9); /* Word */ memcpy(array, init, sizeof(array)); - S4_storeirit_io(&array[1], 1); - check(array[2], 27); - S4_storeirit_io(&array[2], 0); - check(array[3], 3); + S4_storeirit_io(&array[1], true); + check32(array[2], 27); + S4_storeirit_io(&array[2], false); + check32(array[3], 3); memcpy(array, init, sizeof(array)); - S4_storeirif_io(&array[3], 0); - check(array[4], 27); - S4_storeirif_io(&array[4], 1); - check(array[5], 5); + S4_storeirif_io(&array[3], false); + check32(array[4], 27); + S4_storeirif_io(&array[4], true); + check32(array[5], 5); memcpy(array, init, sizeof(array)); - S4_storeiritnew_io(&array[5], 1); - check(array[6], 27); - S4_storeiritnew_io(&array[6], 0); - check(array[7], 7); + S4_storeiritnew_io(&array[5], true); + check32(array[6], 27); + S4_storeiritnew_io(&array[6], false); + check32(array[7], 7); memcpy(array, init, sizeof(array)); - S4_storeirifnew_io(&array[7], 0); - check(array[8], 27); - S4_storeirifnew_io(&array[8], 1); - check(array[9], 9); + S4_storeirifnew_io(&array[7], false); + check32(array[8], 27); + S4_storeirifnew_io(&array[8], true); + check32(array[9], 9); memcpy(array, init, sizeof(array)); - res = L2_ploadrifnew_pi(&array[6], 0); - check(res, 6); - res = L2_ploadrifnew_pi(&array[7], 1); - check(res, 31); + res = L2_ploadrifnew_pi(&array[6], false); + check32(res, 6); + res = L2_ploadrifnew_pi(&array[7], true); + check32(res, 31); - int x = cmpnd_cmp_jump(); - check(x, 12); + res = cmpnd_cmp_jump(); + check32(res, 12); - SL2_return_tnew(0); - check(early_exit, 0); - SL2_return_tnew(1); - check(early_exit, 1); + SL2_return_tnew(false); + check32(early_exit, false); + SL2_return_tnew(true); + check32(early_exit, true); - long long pair = creg_pair(5, 7); - check((int)pair, 5); - check((int)(pair >> 32), 7); + res64 = creg_pair(5, 7); + check32((int32_t)res64, 5); + check32((int32_t)(res64 >> 32), 7); res = test_clrtnew(1, 7); - check(res, 0); + check32(res, 0); res = test_clrtnew(2, 7); - check(res, 7); + check32(res, 7); +#if CORE_HAS_CABAC res64 = decbin(0xf0f1f2f3f4f5f6f7LL, 0x7f6f5f4f3f2f1f0fLL, &pred); check64(res64, 0x357980003700010cLL); - check(pred, 0); + check32(pred, false); res64 = decbin(0xfLL, 0x1bLL, &pred); check64(res64, 0x78000100LL); - check(pred, 1); + check32(pred, true); +#else + puts("Skipping cabac tests"); +#endif - res = auto_and(); - check(res, 0); + pred = auto_and(); + check32(pred, false); test_lsbnew(); test_l2fetch(); + test_count_trailing_zeros_ones(); + + test_dpmpyss_rnd_s0(); + puts(err ? "FAIL" : "PASS"); return err; } diff --git a/tests/tcg/hexagon/multi_result.c b/tests/tcg/hexagon/multi_result.c index 52997b3128..38ee369e76 100644 --- a/tests/tcg/hexagon/multi_result.c +++ b/tests/tcg/hexagon/multi_result.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,11 +16,17 @@ */ #include +#include +#include -static int sfrecipa(int Rs, int Rt, int *pred_result) +int err; + +#include "hex_test.h" + +static int32_t sfrecipa(int32_t Rs, int32_t Rt, bool *pred_result) { - int result; - int predval; + int32_t result; + bool predval; asm volatile("%0,p0 = sfrecipa(%2, %3)\n\t" "%1 = p0\n\t" @@ -31,10 +37,10 @@ static int sfrecipa(int Rs, int Rt, int *pred_result) return result; } -static int sfinvsqrta(int Rs, int *pred_result) +static int32_t sfinvsqrta(int32_t Rs, int32_t *pred_result) { - int result; - int predval; + int32_t result; + int32_t predval; asm volatile("%0,p0 = sfinvsqrta(%2)\n\t" "%1 = p0\n\t" @@ -45,12 +51,12 @@ static int sfinvsqrta(int Rs, int *pred_result) return result; } -static long long vacsh(long long Rxx, long long Rss, long long Rtt, - int *pred_result, int *ovf_result) +static int64_t vacsh(int64_t Rxx, int64_t Rss, int64_t Rtt, + int *pred_result, bool *ovf_result) { - long long result = Rxx; + int64_t result = Rxx; int predval; - int usr; + uint32_t usr; /* * This instruction can set bit 0 (OVF/overflow) in usr @@ -70,11 +76,10 @@ static long long vacsh(long long Rxx, long long Rss, long long Rtt, return result; } -static long long vminub(long long Rtt, long long Rss, - int *pred_result) +static int64_t vminub(int64_t Rtt, int64_t Rss, int32_t *pred_result) { - long long result; - int predval; + int64_t result; + int32_t predval; asm volatile("%0,p0 = vminub(%2, %3)\n\t" "%1 = p0\n\t" @@ -85,11 +90,11 @@ static long long vminub(long long Rtt, long long Rss, return result; } -static long long add_carry(long long Rss, long long Rtt, - int pred_in, int *pred_result) +static int64_t add_carry(int64_t Rss, int64_t Rtt, + int32_t pred_in, int32_t *pred_result) { - long long result; - int predval = pred_in; + int64_t result; + int32_t predval = pred_in; asm volatile("p0 = %1\n\t" "%0 = add(%2, %3, p0):carry\n\t" @@ -101,11 +106,11 @@ static long long add_carry(long long Rss, long long Rtt, return result; } -static long long sub_carry(long long Rss, long long Rtt, - int pred_in, int *pred_result) +static int64_t sub_carry(int64_t Rss, int64_t Rtt, + int32_t pred_in, int32_t *pred_result) { - long long result; - int predval = pred_in; + int64_t result; + int32_t predval = pred_in; asm volatile("p0 = !cmp.eq(%1, #0)\n\t" "%0 = sub(%2, %3, p0):carry\n\t" @@ -117,155 +122,129 @@ static long long sub_carry(long long Rss, long long Rtt, return result; } -int err; - -static void check_ll(long long val, long long expect) -{ - if (val != expect) { - printf("ERROR: 0x%016llx != 0x%016llx\n", val, expect); - err++; - } -} - -static void check(int val, int expect) -{ - if (val != expect) { - printf("ERROR: 0x%08x != 0x%08x\n", val, expect); - err++; - } -} - -static void check_p(int val, int expect) -{ - if (val != expect) { - printf("ERROR: 0x%02x != 0x%02x\n", val, expect); - err++; - } -} - static void test_sfrecipa() { - int res; - int pred_result; + int32_t res; + bool pred_result; res = sfrecipa(0x04030201, 0x05060708, &pred_result); - check(res, 0x59f38001); - check_p(pred_result, 0x00); + check32(res, 0x59f38001); + check32(pred_result, false); } static void test_sfinvsqrta() { - int res; - int pred_result; + int32_t res; + int32_t pred_result; res = sfinvsqrta(0x04030201, &pred_result); - check(res, 0x4d330000); - check_p(pred_result, 0xe0); + check32(res, 0x4d330000); + check32(pred_result, 0xe0); res = sfinvsqrta(0x0, &pred_result); - check(res, 0x3f800000); - check_p(pred_result, 0x0); + check32(res, 0x3f800000); + check32(pred_result, 0x0); } static void test_vacsh() { - long long res64; - int pred_result; - int ovf_result; + int64_t res64; + int32_t pred_result; + bool ovf_result; res64 = vacsh(0x0004000300020001LL, 0x0001000200030004LL, 0x0000000000000000LL, &pred_result, &ovf_result); - check_ll(res64, 0x0004000300030004LL); - check_p(pred_result, 0xf0); - check(ovf_result, 0); + check64(res64, 0x0004000300030004LL); + check32(pred_result, 0xf0); + check32(ovf_result, false); res64 = vacsh(0x0004000300020001LL, 0x0001000200030004LL, 0x000affff000d0000LL, &pred_result, &ovf_result); - check_ll(res64, 0x000e0003000f0004LL); - check_p(pred_result, 0xcc); - check(ovf_result, 0); + check64(res64, 0x000e0003000f0004LL); + check32(pred_result, 0xcc); + check32(ovf_result, false); res64 = vacsh(0x00047fff00020001LL, 0x00017fff00030004LL, 0x000a0fff000d0000LL, &pred_result, &ovf_result); - check_ll(res64, 0x000e7fff000f0004LL); - check_p(pred_result, 0xfc); - check(ovf_result, 1); + check64(res64, 0x000e7fff000f0004LL); + check32(pred_result, 0xfc); + check32(ovf_result, true); res64 = vacsh(0x0004000300020001LL, 0x0001000200030009LL, 0x000affff000d0001LL, &pred_result, &ovf_result); - check_ll(res64, 0x000e0003000f0008LL); - check_p(pred_result, 0xcc); - check(ovf_result, 0); + check64(res64, 0x000e0003000f0008LL); + check32(pred_result, 0xcc); + check32(ovf_result, false); } static void test_vminub() { - long long res64; - int pred_result; + int64_t res64; + int32_t pred_result; res64 = vminub(0x0807060504030201LL, 0x0102030405060708LL, &pred_result); - check_ll(res64, 0x0102030404030201LL); - check_p(pred_result, 0xf0); + check64(res64, 0x0102030404030201LL); + check32(pred_result, 0xf0); res64 = vminub(0x0802060405030701LL, 0x0107030504060208LL, &pred_result); - check_ll(res64, 0x0102030404030201LL); - check_p(pred_result, 0xaa); + check64(res64, 0x0102030404030201LL); + check32(pred_result, 0xaa); } static void test_add_carry() { - long long res64; - int pred_result; + int64_t res64; + int32_t pred_result; res64 = add_carry(0x0000000000000000LL, 0xffffffffffffffffLL, 1, &pred_result); - check_ll(res64, 0x0000000000000000LL); - check_p(pred_result, 0xff); + check64(res64, 0x0000000000000000LL); + check32(pred_result, 0xff); res64 = add_carry(0x0000000100000000LL, 0xffffffffffffffffLL, 0, &pred_result); - check_ll(res64, 0x00000000ffffffffLL); - check_p(pred_result, 0xff); + check64(res64, 0x00000000ffffffffLL); + check32(pred_result, 0xff); res64 = add_carry(0x0000000100000000LL, 0xffffffffffffffffLL, 0, &pred_result); - check_ll(res64, 0x00000000ffffffffLL); - check_p(pred_result, 0xff); + check64(res64, 0x00000000ffffffffLL); + check32(pred_result, 0xff); } static void test_sub_carry() { - long long res64; - int pred_result; + int64_t res64; + int32_t pred_result; res64 = sub_carry(0x0000000000000000LL, 0x0000000000000000LL, 1, &pred_result); - check_ll(res64, 0x0000000000000000LL); - check_p(pred_result, 0xff); + check64(res64, 0x0000000000000000LL); + check32(pred_result, 0xff); res64 = sub_carry(0x0000000100000000LL, 0x0000000000000000LL, 0, &pred_result); - check_ll(res64, 0x00000000ffffffffLL); - check_p(pred_result, 0xff); + check64(res64, 0x00000000ffffffffLL); + check32(pred_result, 0xff); res64 = sub_carry(0x0000000100000000LL, 0x0000000000000000LL, 0, &pred_result); - check_ll(res64, 0x00000000ffffffffLL); - check_p(pred_result, 0xff); + check64(res64, 0x00000000ffffffffLL); + check32(pred_result, 0xff); } int main() diff --git a/tests/tcg/hexagon/overflow.c b/tests/tcg/hexagon/overflow.c index 94087851b0..7b5b9ebdde 100644 --- a/tests/tcg/hexagon/overflow.c +++ b/tests/tcg/hexagon/overflow.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2021-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2021-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,30 +17,21 @@ #include #include +#include #include #include #include #include #include - int err; -static void __check(const char *filename, int line, int x, int expect) -{ - if (x != expect) { - printf("ERROR %s:%d - %d != %d\n", - filename, line, x, expect); - err++; - } -} +#include "hex_test.h" -#define check(x, expect) __check(__FILE__, __LINE__, (x), (expect)) - -static int satub(int src, int *p, int *ovf_result) +static int32_t satub(int32_t src, int32_t *p, bool *ovf_result) { - int result; - int usr; + int32_t result; + uint32_t usr; /* * This instruction can set bit 0 (OVF/overflow) in usr @@ -65,30 +56,30 @@ static int satub(int src, int *p, int *ovf_result) return result; } -int read_usr_overflow(void) +bool read_usr_overflow(void) { - int result; - asm volatile("%0 = usr\n\t" : "=r"(result)); - return result & 1; + uint32_t usr; + asm volatile("%0 = usr\n\t" : "=r"(usr)); + return usr & 1; } -int get_usr_overflow(int usr) +bool get_usr_overflow(uint32_t usr) { return usr & 1; } -int get_usr_fp_invalid(int usr) +bool get_usr_fp_invalid(uint32_t usr) { return (usr >> 1) & 1; } -int get_usr_lpcfg(int usr) +int32_t get_usr_lpcfg(uint32_t usr) { return (usr >> 8) & 0x3; } jmp_buf jmp_env; -int usr_overflow; +bool usr_overflow; static void sig_segv(int sig, siginfo_t *info, void *puc) { @@ -98,9 +89,9 @@ static void sig_segv(int sig, siginfo_t *info, void *puc) static void test_packet(void) { - int convres; - int satres; - int usr; + int32_t convres; + int32_t satres; + uint32_t usr; asm("r2 = usr\n\t" "r2 = clrbit(r2, #0)\n\t" /* clear overflow bit */ @@ -115,10 +106,10 @@ static void test_packet(void) : "r"(0x6a051b86), "r"(0x0410eec0) : "r2", "usr"); - check(convres, 0xffffffff); - check(satres, 0x7f); - check(get_usr_overflow(usr), 1); - check(get_usr_fp_invalid(usr), 1); + check32(convres, 0xffffffff); + check32(satres, 0x7f); + check32(get_usr_overflow(usr), true); + check32(get_usr_fp_invalid(usr), true); asm("r2 = usr\n\t" "r2 = clrbit(r2, #0)\n\t" /* clear overflow bit */ @@ -134,15 +125,15 @@ static void test_packet(void) : "r"(0x0410eec0) : "r2", "usr", "p3", "sa0", "lc0"); - check(satres, 0x7f); - check(get_usr_overflow(usr), 1); - check(get_usr_lpcfg(usr), 2); + check32(satres, 0x7f); + check32(get_usr_overflow(usr), true); + check32(get_usr_lpcfg(usr), 2); } int main() { struct sigaction act; - int ovf; + bool ovf; /* SIGSEGV test */ act.sa_sigaction = sig_segv; @@ -157,7 +148,7 @@ int main() sigemptyset(&act.sa_mask); act.sa_flags = 0; - check(usr_overflow, 0); + check32(usr_overflow, false); test_packet(); diff --git a/tests/tcg/hexagon/preg_alias.c b/tests/tcg/hexagon/preg_alias.c index b44a8112b4..892ecbbdbf 100644 --- a/tests/tcg/hexagon/preg_alias.c +++ b/tests/tcg/hexagon/preg_alias.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,10 +16,15 @@ */ #include +#include -static inline int preg_alias(int v0, int v1, int v2, int v3) +int err; + +#include "hex_test.h" + +static uint32_t preg_alias(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3) { - int ret; + uint32_t ret; asm volatile("p0 = %1\n\t" "p1 = %2\n\t" "p2 = %3\n\t" @@ -31,9 +36,9 @@ static inline int preg_alias(int v0, int v1, int v2, int v3) return ret; } -static inline int preg_alias_pair(int v0, int v1, int v2, int v3) +static uint32_t preg_alias_pair(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3) { - long long c54; + uint64_t c54; asm volatile("p0 = %1\n\t" "p1 = %2\n\t" "p2 = %3\n\t" @@ -42,20 +47,20 @@ static inline int preg_alias_pair(int v0, int v1, int v2, int v3) : "=r"(c54) : "r"(v0), "r"(v1), "r"(v2), "r"(v3) : "p0", "p1", "p2", "p3"); - return (int)c54; + return (uint32_t)c54; } typedef union { - int creg; + uint32_t creg; struct { - unsigned char p0; - unsigned char p1; - unsigned char p2; - unsigned char p3; + uint8_t p0; + uint8_t p1; + uint8_t p2; + uint8_t p3; } pregs; } PRegs; -static inline void creg_alias(int cval, PRegs *pregs) +static inline void creg_alias(uint32_t cval, PRegs *pregs) { asm("c4 = %4\n\t" "%0 = p0\n\t" @@ -65,23 +70,13 @@ static inline void creg_alias(int cval, PRegs *pregs) : "=r"(pregs->pregs.p0), "=r"(pregs->pregs.p1), "=r"(pregs->pregs.p2), "=r"(pregs->pregs.p3) : "r"(cval) - : "p0", "p1", "p2", "p3"); + : "c4", "p0", "p1", "p2", "p3"); } -int err; - -static void check(int val, int expect) +static inline void creg_alias_pair(uint32_t cval, PRegs *pregs) { - if (val != expect) { - printf("ERROR: 0x%08x != 0x%08x\n", val, expect); - err++; - } -} - -static inline void creg_alias_pair(unsigned int cval, PRegs *pregs) -{ - unsigned long long cval_pair = (0xdeadbeefULL << 32) | cval; - int c5; + uint64_t cval_pair = (0xdeadbeefULL << 32) | cval; + uint32_t c5; asm ("c5:4 = %5\n\t" "%0 = p0\n\t" @@ -92,9 +87,9 @@ static inline void creg_alias_pair(unsigned int cval, PRegs *pregs) : "=r"(pregs->pregs.p0), "=r"(pregs->pregs.p1), "=r"(pregs->pregs.p2), "=r"(pregs->pregs.p3), "=r"(c5) : "r"(cval_pair) - : "p0", "p1", "p2", "p3"); + : "c4", "c5", "p0", "p1", "p2", "p3"); - check(c5, 0xdeadbeef); + check32(c5, 0xdeadbeef); } static void test_packet(void) @@ -104,8 +99,8 @@ static void test_packet(void) * that are read during the packet. */ - int result; - int old_val = 0x0000001c; + uint32_t result; + uint32_t old_val = 0x0000001c; /* Test a predicated register transfer */ result = old_val; @@ -117,8 +112,8 @@ static void test_packet(void) "}\n\t" : "+r"(result) : "r"(0xffffffff), "r"(0xff00ffff), "r"(0x837ed653) - : "p0", "p1", "p2", "p3"); - check(result, old_val); + : "c4", "p0", "p1", "p2", "p3"); + check32(result, old_val); /* Test a predicated store */ result = 0xffffffff; @@ -129,74 +124,74 @@ static void test_packet(void) "}\n\t" : : "r"(0), "r"(0xffffffff), "r"(&result) - : "p0", "p1", "p2", "p3", "memory"); - check(result, 0x0); + : "c4", "p0", "p1", "p2", "p3", "memory"); + check32(result, 0x0); } int main() { - int c4; + uint32_t c4; PRegs pregs; c4 = preg_alias(0xff, 0x00, 0xff, 0x00); - check(c4, 0x00ff00ff); + check32(c4, 0x00ff00ff); c4 = preg_alias(0xff, 0x00, 0x00, 0x00); - check(c4, 0x000000ff); + check32(c4, 0x000000ff); c4 = preg_alias(0x00, 0xff, 0x00, 0x00); - check(c4, 0x0000ff00); + check32(c4, 0x0000ff00); c4 = preg_alias(0x00, 0x00, 0xff, 0x00); - check(c4, 0x00ff0000); + check32(c4, 0x00ff0000); c4 = preg_alias(0x00, 0x00, 0x00, 0xff); - check(c4, 0xff000000); + check32(c4, 0xff000000); c4 = preg_alias(0xff, 0xff, 0xff, 0xff); - check(c4, 0xffffffff); + check32(c4, 0xffffffff); c4 = preg_alias_pair(0xff, 0x00, 0xff, 0x00); - check(c4, 0x00ff00ff); + check32(c4, 0x00ff00ff); c4 = preg_alias_pair(0xff, 0x00, 0x00, 0x00); - check(c4, 0x000000ff); + check32(c4, 0x000000ff); c4 = preg_alias_pair(0x00, 0xff, 0x00, 0x00); - check(c4, 0x0000ff00); + check32(c4, 0x0000ff00); c4 = preg_alias_pair(0x00, 0x00, 0xff, 0x00); - check(c4, 0x00ff0000); + check32(c4, 0x00ff0000); c4 = preg_alias_pair(0x00, 0x00, 0x00, 0xff); - check(c4, 0xff000000); + check32(c4, 0xff000000); c4 = preg_alias_pair(0xff, 0xff, 0xff, 0xff); - check(c4, 0xffffffff); + check32(c4, 0xffffffff); creg_alias(0x00ff00ff, &pregs); - check(pregs.creg, 0x00ff00ff); + check32(pregs.creg, 0x00ff00ff); creg_alias(0x00ffff00, &pregs); - check(pregs.creg, 0x00ffff00); + check32(pregs.creg, 0x00ffff00); creg_alias(0x00000000, &pregs); - check(pregs.creg, 0x00000000); + check32(pregs.creg, 0x00000000); creg_alias(0xff000000, &pregs); - check(pregs.creg, 0xff000000); + check32(pregs.creg, 0xff000000); creg_alias(0x00ff0000, &pregs); - check(pregs.creg, 0x00ff0000); + check32(pregs.creg, 0x00ff0000); creg_alias(0x0000ff00, &pregs); - check(pregs.creg, 0x0000ff00); + check32(pregs.creg, 0x0000ff00); creg_alias(0x000000ff, &pregs); - check(pregs.creg, 0x000000ff); + check32(pregs.creg, 0x000000ff); creg_alias(0xffffffff, &pregs); - check(pregs.creg, 0xffffffff); + check32(pregs.creg, 0xffffffff); creg_alias_pair(0x00ff00ff, &pregs); - check(pregs.creg, 0x00ff00ff); + check32(pregs.creg, 0x00ff00ff); creg_alias_pair(0x00ffff00, &pregs); - check(pregs.creg, 0x00ffff00); + check32(pregs.creg, 0x00ffff00); creg_alias_pair(0x00000000, &pregs); - check(pregs.creg, 0x00000000); + check32(pregs.creg, 0x00000000); creg_alias_pair(0xff000000, &pregs); - check(pregs.creg, 0xff000000); + check32(pregs.creg, 0xff000000); creg_alias_pair(0x00ff0000, &pregs); - check(pregs.creg, 0x00ff0000); + check32(pregs.creg, 0x00ff0000); creg_alias_pair(0x0000ff00, &pregs); - check(pregs.creg, 0x0000ff00); + check32(pregs.creg, 0x0000ff00); creg_alias_pair(0x000000ff, &pregs); - check(pregs.creg, 0x000000ff); + check32(pregs.creg, 0x000000ff); creg_alias_pair(0xffffffff, &pregs); - check(pregs.creg, 0xffffffff); + check32(pregs.creg, 0xffffffff); test_packet(); diff --git a/tests/tcg/hexagon/read_write_overlap.c b/tests/tcg/hexagon/read_write_overlap.c new file mode 100644 index 0000000000..95c54ccd63 --- /dev/null +++ b/tests/tcg/hexagon/read_write_overlap.c @@ -0,0 +1,127 @@ +/* + * Copyright(c) 2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * Test instructions where the semantics write to the destination + * before all the operand reads have been completed. + * + * These instructions are problematic when we short-circuit the + * register writes because the destination and source operands could + * be the same TCGv. + * + * We test by forcing the read and write to be register r7. + */ + +#include +#include +#include + +int err; + +#include "hex_test.h" + +#define insert(RES, X, WIDTH, OFFSET) \ + asm("r7 = %1\n\t" \ + "r7 = insert(r7, #" #WIDTH ", #" #OFFSET ")\n\t" \ + "%0 = r7\n\t" \ + : "=r"(RES) : "r"(X) : "r7") + +static void test_insert(void) +{ + uint32_t res; + + insert(res, 0x12345678, 8, 1); + check32(res, 0x123456f0); + insert(res, 0x12345678, 0, 1); + check32(res, 0x12345678); + insert(res, 0x12345678, 20, 16); + check32(res, 0x56785678); +} + +static inline uint32_t insert_rp(uint32_t x, uint32_t width, uint32_t offset) +{ + uint64_t width_offset = (uint64_t)width << 32 | offset; + uint32_t res; + asm("r7 = %1\n\t" + "r7 = insert(r7, %2)\n\t" + "%0 = r7\n\t" + : "=r"(res) : "r"(x), "r"(width_offset) : "r7"); + return res; + +} + +static void test_insert_rp(void) +{ + check32(insert_rp(0x12345678, 8, 1), 0x123456f0); + check32(insert_rp(0x12345678, 63, 8), 0x34567878); + check32(insert_rp(0x12345678, 127, 8), 0x34567878); + check32(insert_rp(0x12345678, 8, 24), 0x78345678); + check32(insert_rp(0x12345678, 8, 63), 0x12345678); + check32(insert_rp(0x12345678, 8, 64), 0x00000000); +} + +static inline uint32_t asr_r_svw_trun(uint64_t x, uint32_t y) +{ + uint32_t res; + asm("r7 = %2\n\t" + "r7 = vasrw(%1, r7)\n\t" + "%0 = r7\n\t" + : "=r"(res) : "r"(x), "r"(y) : "r7"); + return res; +} + +static void test_asr_r_svw_trun(void) +{ + check32(asr_r_svw_trun(0x1111111122222222ULL, 5), + 0x88881111); + check32(asr_r_svw_trun(0x1111111122222222ULL, 63), + 0x00000000); + check32(asr_r_svw_trun(0x1111111122222222ULL, 64), + 0x00000000); + check32(asr_r_svw_trun(0x1111111122222222ULL, 127), + 0x22224444); + check32(asr_r_svw_trun(0x1111111122222222ULL, 128), + 0x11112222); + check32(asr_r_svw_trun(0xffffffff22222222ULL, 128), + 0xffff2222); +} + +static inline uint32_t swiz(uint32_t x) +{ + uint32_t res; + asm("r7 = %1\n\t" + "r7 = swiz(r7)\n\t" + "%0 = r7\n\t" + : "=r"(res) : "r"(x) : "r7"); + return res; +} + +static void test_swiz(void) +{ + check32(swiz(0x11223344), 0x44332211); +} + +int main() +{ + test_insert(); + test_insert_rp(); + test_asr_r_svw_trun(); + test_swiz(); + + puts(err ? "FAIL" : "PASS"); + return err ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/tests/tcg/hexagon/reg_mut.c b/tests/tcg/hexagon/reg_mut.c new file mode 100644 index 0000000000..c5a39e5510 --- /dev/null +++ b/tests/tcg/hexagon/reg_mut.c @@ -0,0 +1,132 @@ + +/* + * Copyright(c) 2022-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include + +static int err; + +#include "hex_test.h" + +#define WRITE_REG_NOCLOBBER(output, reg_name, input) \ + asm volatile(reg_name " = %1\n\t" \ + "%0 = " reg_name "\n\t" \ + : "=r"(output) \ + : "r"(input) \ + : ); + +#define WRITE_REG_ENCODED(output, reg_name, input, encoding) \ + asm volatile("r0 = %1\n\t" \ + encoding "\n\t" \ + "%0 = " reg_name "\n\t" \ + : "=r"(output) \ + : "r"(input) \ + : "r0"); + +#define WRITE_REG_PAIR_ENCODED(output, reg_name, input, encoding) \ + asm volatile("r1:0 = %1\n\t" \ + encoding "\n\t" \ + "%0 = " reg_name "\n\t" \ + : "=r"(output) \ + : "r"(input) \ + : "r1:0"); + +/* + * Instruction word: { pc = r0 } + * + * This instruction is barred by the assembler. + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Opc[A2_tfrrcr] | Src[R0] |P P| | C9/PC | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +#define PC_EQ_R0 ".word 0x6220c009" +#define C9_8_EQ_R1_0 ".word 0x6320c008" + +static inline void write_control_registers(void) +{ + uint32_t result = 0; + + WRITE_REG_NOCLOBBER(result, "usr", 0xffffffff); + check32(result, 0x3ecfff3f); + + WRITE_REG_NOCLOBBER(result, "gp", 0xffffffff); + check32(result, 0xffffffc0); + + WRITE_REG_NOCLOBBER(result, "upcyclelo", 0xffffffff); + check32(result, 0x00000000); + + WRITE_REG_NOCLOBBER(result, "upcyclehi", 0xffffffff); + check32(result, 0x00000000); + + WRITE_REG_NOCLOBBER(result, "utimerlo", 0xffffffff); + check32(result, 0x00000000); + + WRITE_REG_NOCLOBBER(result, "utimerhi", 0xffffffff); + check32(result, 0x00000000); + + /* + * PC is special. Setting it to these values + * should cause a catastrophic failure. + */ + WRITE_REG_ENCODED(result, "pc", 0x00000000, PC_EQ_R0); + check32_ne(result, 0x00000000); + + WRITE_REG_ENCODED(result, "pc", 0x00000001, PC_EQ_R0); + check32_ne(result, 0x00000001); + + WRITE_REG_ENCODED(result, "pc", 0xffffffff, PC_EQ_R0); + check32_ne(result, 0xffffffff); +} + +static inline void write_control_register_pairs(void) +{ + uint64_t result = 0; + + WRITE_REG_NOCLOBBER(result, "c11:10", 0xffffffffffffffff); + check64(result, 0xffffffc0ffffffff); + + WRITE_REG_NOCLOBBER(result, "c15:14", 0xffffffffffffffff); + check64(result, 0x0000000000000000); + + WRITE_REG_NOCLOBBER(result, "c31:30", 0xffffffffffffffff); + check64(result, 0x0000000000000000); + + WRITE_REG_PAIR_ENCODED(result, "c9:8", (uint64_t) 0x0000000000000000, + C9_8_EQ_R1_0); + check64_ne(result, 0x000000000000000); + + WRITE_REG_PAIR_ENCODED(result, "c9:8", 0x0000000100000000, C9_8_EQ_R1_0); + check64_ne(result, 0x0000000100000000); + + WRITE_REG_PAIR_ENCODED(result, "c9:8", 0xffffffffffffffff, C9_8_EQ_R1_0); + check64_ne(result, 0xffffffffffffffff); +} + +int main() +{ + err = 0; + + write_control_registers(); + write_control_register_pairs(); + + puts(err ? "FAIL" : "PASS"); + return err; +} diff --git a/tests/tcg/hexagon/scatter_gather.c b/tests/tcg/hexagon/scatter_gather.c index b93eb18133..bf8b5e0317 100644 --- a/tests/tcg/hexagon/scatter_gather.c +++ b/tests/tcg/hexagon/scatter_gather.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,47 +40,6 @@ typedef long HVX_VectorPair __attribute__((__vector_size__(256))) typedef long HVX_VectorPred __attribute__((__vector_size__(128))) __attribute__((aligned(128))); -#define VSCATTER_16(BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermh_128B((int)BASE, RGN, OFF, VALS) -#define VSCATTER_16_MASKED(MASK, BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermhq_128B(MASK, (int)BASE, RGN, OFF, VALS) -#define VSCATTER_32(BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermw_128B((int)BASE, RGN, OFF, VALS) -#define VSCATTER_32_MASKED(MASK, BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermwq_128B(MASK, (int)BASE, RGN, OFF, VALS) -#define VSCATTER_16_32(BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermhw_128B((int)BASE, RGN, OFF, VALS) -#define VSCATTER_16_32_MASKED(MASK, BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermhwq_128B(MASK, (int)BASE, RGN, OFF, VALS) -#define VSCATTER_16_ACC(BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermh_add_128B((int)BASE, RGN, OFF, VALS) -#define VSCATTER_32_ACC(BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermw_add_128B((int)BASE, RGN, OFF, VALS) -#define VSCATTER_16_32_ACC(BASE, RGN, OFF, VALS) \ - __builtin_HEXAGON_V6_vscattermhw_add_128B((int)BASE, RGN, OFF, VALS) - -#define VGATHER_16(DSTADDR, BASE, RGN, OFF) \ - __builtin_HEXAGON_V6_vgathermh_128B(DSTADDR, (int)BASE, RGN, OFF) -#define VGATHER_16_MASKED(DSTADDR, MASK, BASE, RGN, OFF) \ - __builtin_HEXAGON_V6_vgathermhq_128B(DSTADDR, MASK, (int)BASE, RGN, OFF) -#define VGATHER_32(DSTADDR, BASE, RGN, OFF) \ - __builtin_HEXAGON_V6_vgathermw_128B(DSTADDR, (int)BASE, RGN, OFF) -#define VGATHER_32_MASKED(DSTADDR, MASK, BASE, RGN, OFF) \ - __builtin_HEXAGON_V6_vgathermwq_128B(DSTADDR, MASK, (int)BASE, RGN, OFF) -#define VGATHER_16_32(DSTADDR, BASE, RGN, OFF) \ - __builtin_HEXAGON_V6_vgathermhw_128B(DSTADDR, (int)BASE, RGN, OFF) -#define VGATHER_16_32_MASKED(DSTADDR, MASK, BASE, RGN, OFF) \ - __builtin_HEXAGON_V6_vgathermhwq_128B(DSTADDR, MASK, (int)BASE, RGN, OFF) - -#define VSHUFF_H(V) \ - __builtin_HEXAGON_V6_vshuffh_128B(V) -#define VSPLAT_H(X) \ - __builtin_HEXAGON_V6_lvsplath_128B(X) -#define VAND_VAL(PRED, VAL) \ - __builtin_HEXAGON_V6_vandvrt_128B(PRED, VAL) -#define VDEAL_H(V) \ - __builtin_HEXAGON_V6_vdealh_128B(V) - int err; /* define the number of rows/cols in a square matrix */ @@ -108,22 +67,22 @@ unsigned short vscatter16_32_ref[SCATTER_BUFFER_SIZE]; unsigned short vgather16_32_ref[MATRIX_SIZE]; /* declare the arrays of offsets */ -unsigned short half_offsets[MATRIX_SIZE]; -unsigned int word_offsets[MATRIX_SIZE]; +unsigned short half_offsets[MATRIX_SIZE] __attribute__((aligned(128))); +unsigned int word_offsets[MATRIX_SIZE] __attribute__((aligned(128))); /* declare the arrays of values */ -unsigned short half_values[MATRIX_SIZE]; -unsigned short half_values_acc[MATRIX_SIZE]; -unsigned short half_values_masked[MATRIX_SIZE]; -unsigned int word_values[MATRIX_SIZE]; -unsigned int word_values_acc[MATRIX_SIZE]; -unsigned int word_values_masked[MATRIX_SIZE]; +unsigned short half_values[MATRIX_SIZE] __attribute__((aligned(128))); +unsigned short half_values_acc[MATRIX_SIZE] __attribute__((aligned(128))); +unsigned short half_values_masked[MATRIX_SIZE] __attribute__((aligned(128))); +unsigned int word_values[MATRIX_SIZE] __attribute__((aligned(128))); +unsigned int word_values_acc[MATRIX_SIZE] __attribute__((aligned(128))); +unsigned int word_values_masked[MATRIX_SIZE] __attribute__((aligned(128))); /* declare the arrays of predicates */ -unsigned short half_predicates[MATRIX_SIZE]; -unsigned int word_predicates[MATRIX_SIZE]; +unsigned short half_predicates[MATRIX_SIZE] __attribute__((aligned(128))); +unsigned int word_predicates[MATRIX_SIZE] __attribute__((aligned(128))); -/* make this big enough for all the intrinsics */ +/* make this big enough for all the operations */ const size_t region_len = sizeof(vtcm); /* optionally add sync instructions */ @@ -261,164 +220,201 @@ void create_offsets_values_preds_16_32(void) } } -/* scatter the 16 bit elements using intrinsics */ +/* scatter the 16 bit elements using HVX */ void vector_scatter_16(void) { - /* copy the offsets and values to vectors */ - HVX_Vector offsets = *(HVX_Vector *)half_offsets; - HVX_Vector values = *(HVX_Vector *)half_values; - - VSCATTER_16(&vtcm.vscatter16, region_len, offsets, values); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%3 + #0)\n\t" + "vscatter(%0, m0, v0.h).h = v1\n\t" + : : "r"(vtcm.vscatter16), "r"(region_len), + "r"(half_offsets), "r"(half_values) + : "m0", "v0", "v1", "memory"); sync_scatter(vtcm.vscatter16); } -/* scatter-accumulate the 16 bit elements using intrinsics */ +/* scatter-accumulate the 16 bit elements using HVX */ void vector_scatter_16_acc(void) { - /* copy the offsets and values to vectors */ - HVX_Vector offsets = *(HVX_Vector *)half_offsets; - HVX_Vector values = *(HVX_Vector *)half_values_acc; - - VSCATTER_16_ACC(&vtcm.vscatter16, region_len, offsets, values); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%3 + #0)\n\t" + "vscatter(%0, m0, v0.h).h += v1\n\t" + : : "r"(vtcm.vscatter16), "r"(region_len), + "r"(half_offsets), "r"(half_values_acc) + : "m0", "v0", "v1", "memory"); sync_scatter(vtcm.vscatter16); } -/* scatter the 16 bit elements using intrinsics */ +/* masked scatter the 16 bit elements using HVX */ void vector_scatter_16_masked(void) { - /* copy the offsets and values to vectors */ - HVX_Vector offsets = *(HVX_Vector *)half_offsets; - HVX_Vector values = *(HVX_Vector *)half_values_masked; - HVX_Vector pred_reg = *(HVX_Vector *)half_predicates; - HVX_VectorPred preds = VAND_VAL(pred_reg, ~0); - - VSCATTER_16_MASKED(preds, &vtcm.vscatter16, region_len, offsets, values); + asm ("r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "v1 = vmem(%4 + #0)\n\t" + "if (q0) vscatter(%1, m0, v0.h).h = v1\n\t" + : : "r"(half_predicates), "r"(vtcm.vscatter16), "r"(region_len), + "r"(half_offsets), "r"(half_values_masked) + : "r1", "q0", "m0", "q0", "v0", "v1", "memory"); sync_scatter(vtcm.vscatter16); } -/* scatter the 32 bit elements using intrinsics */ +/* scatter the 32 bit elements using HVX */ void vector_scatter_32(void) { - /* copy the offsets and values to vectors */ - HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; - HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; - HVX_Vector valueslo = *(HVX_Vector *)word_values; - HVX_Vector valueshi = *(HVX_Vector *)&word_values[MATRIX_SIZE / 2]; + HVX_Vector *offsetslo = (HVX_Vector *)word_offsets; + HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + HVX_Vector *valueslo = (HVX_Vector *)word_values; + HVX_Vector *valueshi = (HVX_Vector *)&word_values[MATRIX_SIZE / 2]; - VSCATTER_32(&vtcm.vscatter32, region_len, offsetslo, valueslo); - VSCATTER_32(&vtcm.vscatter32, region_len, offsetshi, valueshi); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%3 + #0)\n\t" + "vscatter(%0, m0, v0.w).w = v1\n\t" + : : "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetslo), "r"(valueslo) + : "m0", "v0", "v1", "memory"); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%3 + #0)\n\t" + "vscatter(%0, m0, v0.w).w = v1\n\t" + : : "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetshi), "r"(valueshi) + : "m0", "v0", "v1", "memory"); sync_scatter(vtcm.vscatter32); } -/* scatter-acc the 32 bit elements using intrinsics */ +/* scatter-accumulate the 32 bit elements using HVX */ void vector_scatter_32_acc(void) { - /* copy the offsets and values to vectors */ - HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; - HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; - HVX_Vector valueslo = *(HVX_Vector *)word_values_acc; - HVX_Vector valueshi = *(HVX_Vector *)&word_values_acc[MATRIX_SIZE / 2]; + HVX_Vector *offsetslo = (HVX_Vector *)word_offsets; + HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + HVX_Vector *valueslo = (HVX_Vector *)word_values_acc; + HVX_Vector *valueshi = (HVX_Vector *)&word_values_acc[MATRIX_SIZE / 2]; - VSCATTER_32_ACC(&vtcm.vscatter32, region_len, offsetslo, valueslo); - VSCATTER_32_ACC(&vtcm.vscatter32, region_len, offsetshi, valueshi); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%3 + #0)\n\t" + "vscatter(%0, m0, v0.w).w += v1\n\t" + : : "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetslo), "r"(valueslo) + : "m0", "v0", "v1", "memory"); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%3 + #0)\n\t" + "vscatter(%0, m0, v0.w).w += v1\n\t" + : : "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetshi), "r"(valueshi) + : "m0", "v0", "v1", "memory"); sync_scatter(vtcm.vscatter32); } -/* scatter the 32 bit elements using intrinsics */ +/* masked scatter the 32 bit elements using HVX */ void vector_scatter_32_masked(void) { - /* copy the offsets and values to vectors */ - HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; - HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; - HVX_Vector valueslo = *(HVX_Vector *)word_values_masked; - HVX_Vector valueshi = *(HVX_Vector *)&word_values_masked[MATRIX_SIZE / 2]; - HVX_Vector pred_reglo = *(HVX_Vector *)word_predicates; - HVX_Vector pred_reghi = *(HVX_Vector *)&word_predicates[MATRIX_SIZE / 2]; - HVX_VectorPred predslo = VAND_VAL(pred_reglo, ~0); - HVX_VectorPred predshi = VAND_VAL(pred_reghi, ~0); + HVX_Vector *offsetslo = (HVX_Vector *)word_offsets; + HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + HVX_Vector *valueslo = (HVX_Vector *)word_values_masked; + HVX_Vector *valueshi = (HVX_Vector *)&word_values_masked[MATRIX_SIZE / 2]; + HVX_Vector *predslo = (HVX_Vector *)word_predicates; + HVX_Vector *predshi = (HVX_Vector *)&word_predicates[MATRIX_SIZE / 2]; - VSCATTER_32_MASKED(predslo, &vtcm.vscatter32, region_len, offsetslo, - valueslo); - VSCATTER_32_MASKED(predshi, &vtcm.vscatter32, region_len, offsetshi, - valueshi); + asm ("r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "v1 = vmem(%4 + #0)\n\t" + "if (q0) vscatter(%1, m0, v0.w).w = v1\n\t" + : : "r"(predslo), "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetslo), "r"(valueslo) + : "r1", "q0", "m0", "q0", "v0", "v1", "memory"); + asm ("r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "v1 = vmem(%4 + #0)\n\t" + "if (q0) vscatter(%1, m0, v0.w).w = v1\n\t" + : : "r"(predshi), "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetshi), "r"(valueshi) + : "r1", "q0", "m0", "q0", "v0", "v1", "memory"); - sync_scatter(vtcm.vscatter16); + sync_scatter(vtcm.vscatter32); } -/* scatter the 16 bit elements with 32 bit offsets using intrinsics */ +/* scatter the 16 bit elements with 32 bit offsets using HVX */ void vector_scatter_16_32(void) { - HVX_VectorPair offsets; - HVX_Vector values; - - /* get the word offsets in a vector pair */ - offsets = *(HVX_VectorPair *)word_offsets; - - /* these values need to be shuffled for the scatter */ - values = *(HVX_Vector *)half_values; - values = VSHUFF_H(values); - - VSCATTER_16_32(&vtcm.vscatter16_32, region_len, offsets, values); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%2 + #1)\n\t" + "v2 = vmem(%3 + #0)\n\t" + "v2.h = vshuff(v2.h)\n\t" /* shuffle the values for the scatter */ + "vscatter(%0, m0, v1:0.w).h = v2\n\t" + : : "r"(vtcm.vscatter16_32), "r"(region_len), + "r"(word_offsets), "r"(half_values) + : "m0", "v0", "v1", "v2", "memory"); sync_scatter(vtcm.vscatter16_32); } -/* scatter-acc the 16 bit elements with 32 bit offsets using intrinsics */ +/* scatter-accumulate the 16 bit elements with 32 bit offsets using HVX */ void vector_scatter_16_32_acc(void) { - HVX_VectorPair offsets; - HVX_Vector values; - - /* get the word offsets in a vector pair */ - offsets = *(HVX_VectorPair *)word_offsets; - - /* these values need to be shuffled for the scatter */ - values = *(HVX_Vector *)half_values_acc; - values = VSHUFF_H(values); - - VSCATTER_16_32_ACC(&vtcm.vscatter16_32, region_len, offsets, values); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%2 + #1)\n\t" + "v2 = vmem(%3 + #0)\n\t" \ + "v2.h = vshuff(v2.h)\n\t" /* shuffle the values for the scatter */ + "vscatter(%0, m0, v1:0.w).h += v2\n\t" + : : "r"(vtcm.vscatter16_32), "r"(region_len), + "r"(word_offsets), "r"(half_values_acc) + : "m0", "v0", "v1", "v2", "memory"); sync_scatter(vtcm.vscatter16_32); } -/* masked scatter the 16 bit elements with 32 bit offsets using intrinsics */ +/* masked scatter the 16 bit elements with 32 bit offsets using HVX */ void vector_scatter_16_32_masked(void) { - HVX_VectorPair offsets; - HVX_Vector values; - HVX_Vector pred_reg; - - /* get the word offsets in a vector pair */ - offsets = *(HVX_VectorPair *)word_offsets; - - /* these values need to be shuffled for the scatter */ - values = *(HVX_Vector *)half_values_masked; - values = VSHUFF_H(values); - - pred_reg = *(HVX_Vector *)half_predicates; - pred_reg = VSHUFF_H(pred_reg); - HVX_VectorPred preds = VAND_VAL(pred_reg, ~0); - - VSCATTER_16_32_MASKED(preds, &vtcm.vscatter16_32, region_len, offsets, - values); + asm ("r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "v0.h = vshuff(v0.h)\n\t" /* shuffle the predicates */ + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "v1 = vmem(%3 + #1)\n\t" + "v2 = vmem(%4 + #0)\n\t" \ + "v2.h = vshuff(v2.h)\n\t" /* shuffle the values for the scatter */ + "if (q0) vscatter(%1, m0, v1:0.w).h = v2\n\t" + : : "r"(half_predicates), "r"(vtcm.vscatter16_32), "r"(region_len), + "r"(word_offsets), "r"(half_values_masked) + : "r1", "q0", "m0", "v0", "v1", "v2", "memory"); sync_scatter(vtcm.vscatter16_32); } -/* gather the elements from the scatter16 buffer */ +/* gather the elements from the scatter16 buffer using HVX */ void vector_gather_16(void) { - HVX_Vector *vgather = (HVX_Vector *)&vtcm.vgather16; - HVX_Vector offsets = *(HVX_Vector *)half_offsets; + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "{ vtmp.h = vgather(%0, m0, v0.h).h\n\t" + " vmem(%3 + #0) = vtmp.new }\n\t" + : : "r"(vtcm.vscatter16), "r"(region_len), + "r"(half_offsets), "r"(vtcm.vgather16) + : "m0", "v0", "memory"); - VGATHER_16(vgather, &vtcm.vscatter16, region_len, offsets); - - sync_gather(vgather); + sync_gather(vtcm.vgather16); } static unsigned short gather_16_masked_init(void) @@ -427,31 +423,51 @@ static unsigned short gather_16_masked_init(void) return letter | (letter << 8); } +/* masked gather the elements from the scatter16 buffer using HVX */ void vector_gather_16_masked(void) { - HVX_Vector *vgather = (HVX_Vector *)&vtcm.vgather16; - HVX_Vector offsets = *(HVX_Vector *)half_offsets; - HVX_Vector pred_reg = *(HVX_Vector *)half_predicates; - HVX_VectorPred preds = VAND_VAL(pred_reg, ~0); + unsigned short init = gather_16_masked_init(); - *vgather = VSPLAT_H(gather_16_masked_init()); - VGATHER_16_MASKED(vgather, preds, &vtcm.vscatter16, region_len, offsets); + asm ("v0.h = vsplat(%5)\n\t" + "vmem(%4 + #0) = v0\n\t" /* initialize the write area */ + "r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "{ if (q0) vtmp.h = vgather(%1, m0, v0.h).h\n\t" + " vmem(%4 + #0) = vtmp.new }\n\t" + : : "r"(half_predicates), "r"(vtcm.vscatter16), "r"(region_len), + "r"(half_offsets), "r"(vtcm.vgather16), "r"(init) + : "r1", "q0", "m0", "v0", "memory"); - sync_gather(vgather); + sync_gather(vtcm.vgather16); } -/* gather the elements from the scatter32 buffer */ +/* gather the elements from the scatter32 buffer using HVX */ void vector_gather_32(void) { - HVX_Vector *vgatherlo = (HVX_Vector *)&vtcm.vgather32; - HVX_Vector *vgatherhi = - (HVX_Vector *)((int)&vtcm.vgather32 + (MATRIX_SIZE * 2)); - HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; - HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + HVX_Vector *vgatherlo = (HVX_Vector *)vtcm.vgather32; + HVX_Vector *vgatherhi = (HVX_Vector *)&vtcm.vgather32[MATRIX_SIZE / 2]; + HVX_Vector *offsetslo = (HVX_Vector *)word_offsets; + HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; - VGATHER_32(vgatherlo, &vtcm.vscatter32, region_len, offsetslo); - VGATHER_32(vgatherhi, &vtcm.vscatter32, region_len, offsetshi); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "{ vtmp.w = vgather(%0, m0, v0.w).w\n\t" + " vmem(%3 + #0) = vtmp.new }\n\t" + : : "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetslo), "r"(vgatherlo) + : "m0", "v0", "memory"); + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "{ vtmp.w = vgather(%0, m0, v0.w).w\n\t" + " vmem(%3 + #0) = vtmp.new }\n\t" + : : "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetshi), "r"(vgatherhi) + : "m0", "v0", "memory"); + sync_gather(vgatherlo); sync_gather(vgatherhi); } @@ -461,79 +477,88 @@ static unsigned int gather_32_masked_init(void) return letter | (letter << 8) | (letter << 16) | (letter << 24); } +/* masked gather the elements from the scatter32 buffer using HVX */ void vector_gather_32_masked(void) { - HVX_Vector *vgatherlo = (HVX_Vector *)&vtcm.vgather32; - HVX_Vector *vgatherhi = - (HVX_Vector *)((int)&vtcm.vgather32 + (MATRIX_SIZE * 2)); - HVX_Vector offsetslo = *(HVX_Vector *)word_offsets; - HVX_Vector offsetshi = *(HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; - HVX_Vector pred_reglo = *(HVX_Vector *)word_predicates; - HVX_VectorPred predslo = VAND_VAL(pred_reglo, ~0); - HVX_Vector pred_reghi = *(HVX_Vector *)&word_predicates[MATRIX_SIZE / 2]; - HVX_VectorPred predshi = VAND_VAL(pred_reghi, ~0); + unsigned int init = gather_32_masked_init(); + HVX_Vector *vgatherlo = (HVX_Vector *)vtcm.vgather32; + HVX_Vector *vgatherhi = (HVX_Vector *)&vtcm.vgather32[MATRIX_SIZE / 2]; + HVX_Vector *offsetslo = (HVX_Vector *)word_offsets; + HVX_Vector *offsetshi = (HVX_Vector *)&word_offsets[MATRIX_SIZE / 2]; + HVX_Vector *predslo = (HVX_Vector *)word_predicates; + HVX_Vector *predshi = (HVX_Vector *)&word_predicates[MATRIX_SIZE / 2]; - *vgatherlo = VSPLAT_H(gather_32_masked_init()); - *vgatherhi = VSPLAT_H(gather_32_masked_init()); - VGATHER_32_MASKED(vgatherlo, predslo, &vtcm.vscatter32, region_len, - offsetslo); - VGATHER_32_MASKED(vgatherhi, predshi, &vtcm.vscatter32, region_len, - offsetshi); + asm ("v0.h = vsplat(%5)\n\t" + "vmem(%4 + #0) = v0\n\t" /* initialize the write area */ + "r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "{ if (q0) vtmp.w = vgather(%1, m0, v0.w).w\n\t" + " vmem(%4 + #0) = vtmp.new }\n\t" + : : "r"(predslo), "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetslo), "r"(vgatherlo), "r"(init) + : "r1", "q0", "m0", "v0", "memory"); + asm ("v0.h = vsplat(%5)\n\t" + "vmem(%4 + #0) = v0\n\t" /* initialize the write area */ + "r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "{ if (q0) vtmp.w = vgather(%1, m0, v0.w).w\n\t" + " vmem(%4 + #0) = vtmp.new }\n\t" + : : "r"(predshi), "r"(vtcm.vscatter32), "r"(region_len), + "r"(offsetshi), "r"(vgatherhi), "r"(init) + : "r1", "q0", "m0", "v0", "memory"); sync_gather(vgatherlo); sync_gather(vgatherhi); } -/* gather the elements from the scatter16_32 buffer */ +/* gather the elements from the scatter16_32 buffer using HVX */ void vector_gather_16_32(void) { - HVX_Vector *vgather; - HVX_VectorPair offsets; - HVX_Vector values; + asm ("m0 = %1\n\t" + "v0 = vmem(%2 + #0)\n\t" + "v1 = vmem(%2 + #1)\n\t" + "{ vtmp.h = vgather(%0, m0, v1:0.w).h\n\t" + " vmem(%3 + #0) = vtmp.new }\n\t" + "v0 = vmem(%3 + #0)\n\t" + "v0.h = vdeal(v0.h)\n\t" /* deal the elements to get the order back */ + "vmem(%3 + #0) = v0\n\t" + : : "r"(vtcm.vscatter16_32), "r"(region_len), + "r"(word_offsets), "r"(vtcm.vgather16_32) + : "m0", "v0", "v1", "memory"); - /* get the vtcm address to gather from */ - vgather = (HVX_Vector *)&vtcm.vgather16_32; - - /* get the word offsets in a vector pair */ - offsets = *(HVX_VectorPair *)word_offsets; - - VGATHER_16_32(vgather, &vtcm.vscatter16_32, region_len, offsets); - - /* deal the elements to get the order back */ - values = *(HVX_Vector *)vgather; - values = VDEAL_H(values); - - /* write it back to vtcm address */ - *(HVX_Vector *)vgather = values; + sync_gather(vtcm.vgather16_32); } +/* masked gather the elements from the scatter16_32 buffer using HVX */ void vector_gather_16_32_masked(void) { - HVX_Vector *vgather; - HVX_VectorPair offsets; - HVX_Vector pred_reg; - HVX_VectorPred preds; - HVX_Vector values; + unsigned short init = gather_16_masked_init(); - /* get the vtcm address to gather from */ - vgather = (HVX_Vector *)&vtcm.vgather16_32; + asm ("v0.h = vsplat(%5)\n\t" + "vmem(%4 + #0) = v0\n\t" /* initialize the write area */ + "r1 = #-1\n\t" + "v0 = vmem(%0 + #0)\n\t" + "v0.h = vshuff(v0.h)\n\t" /* shuffle the predicates */ + "q0 = vand(v0, r1)\n\t" + "m0 = %2\n\t" + "v0 = vmem(%3 + #0)\n\t" + "v1 = vmem(%3 + #1)\n\t" + "{ if (q0) vtmp.h = vgather(%1, m0, v1:0.w).h\n\t" + " vmem(%4 + #0) = vtmp.new }\n\t" + "v0 = vmem(%4 + #0)\n\t" + "v0.h = vdeal(v0.h)\n\t" /* deal the elements to get the order back */ + "vmem(%4 + #0) = v0\n\t" + : : "r"(half_predicates), "r"(vtcm.vscatter16_32), "r"(region_len), + "r"(word_offsets), "r"(vtcm.vgather16_32), "r"(init) + : "r1", "q0", "m0", "v0", "v1", "memory"); - /* get the word offsets in a vector pair */ - offsets = *(HVX_VectorPair *)word_offsets; - pred_reg = *(HVX_Vector *)half_predicates; - pred_reg = VSHUFF_H(pred_reg); - preds = VAND_VAL(pred_reg, ~0); - - *vgather = VSPLAT_H(gather_16_masked_init()); - VGATHER_16_32_MASKED(vgather, preds, &vtcm.vscatter16_32, region_len, - offsets); - - /* deal the elements to get the order back */ - values = *(HVX_Vector *)vgather; - values = VDEAL_H(values); - - /* write it back to vtcm address */ - *(HVX_Vector *)vgather = values; + sync_gather(vtcm.vgather16_32); } static void check_buffer(const char *name, void *c, void *r, size_t size) @@ -579,6 +604,7 @@ void scalar_scatter_16_acc(unsigned short *vscatter16) } } +/* scatter-accumulate the 16 bit elements using C */ void check_scatter_16_acc() { memset(vscatter16_ref, FILL_CHAR, @@ -589,7 +615,7 @@ void check_scatter_16_acc() SCATTER_BUFFER_SIZE * sizeof(unsigned short)); } -/* scatter the 16 bit elements using C */ +/* masked scatter the 16 bit elements using C */ void scalar_scatter_16_masked(unsigned short *vscatter16) { for (int i = 0; i < MATRIX_SIZE; i++) { @@ -628,7 +654,7 @@ void check_scatter_32() SCATTER_BUFFER_SIZE * sizeof(unsigned int)); } -/* scatter the 32 bit elements using C */ +/* scatter-accumulate the 32 bit elements using C */ void scalar_scatter_32_acc(unsigned int *vscatter32) { for (int i = 0; i < MATRIX_SIZE; ++i) { @@ -646,7 +672,7 @@ void check_scatter_32_acc() SCATTER_BUFFER_SIZE * sizeof(unsigned int)); } -/* scatter the 32 bit elements using C */ +/* masked scatter the 32 bit elements using C */ void scalar_scatter_32_masked(unsigned int *vscatter32) { for (int i = 0; i < MATRIX_SIZE; i++) { @@ -667,7 +693,7 @@ void check_scatter_32_masked() SCATTER_BUFFER_SIZE * sizeof(unsigned int)); } -/* scatter the 32 bit elements using C */ +/* scatter the 16 bit elements with 32 bit offsets using C */ void scalar_scatter_16_32(unsigned short *vscatter16_32) { for (int i = 0; i < MATRIX_SIZE; ++i) { @@ -684,7 +710,7 @@ void check_scatter_16_32() SCATTER_BUFFER_SIZE * sizeof(unsigned short)); } -/* scatter the 32 bit elements using C */ +/* scatter-accumulate the 16 bit elements with 32 bit offsets using C */ void scalar_scatter_16_32_acc(unsigned short *vscatter16_32) { for (int i = 0; i < MATRIX_SIZE; ++i) { @@ -702,6 +728,7 @@ void check_scatter_16_32_acc() SCATTER_BUFFER_SIZE * sizeof(unsigned short)); } +/* masked scatter the 16 bit elements with 32 bit offsets using C */ void scalar_scatter_16_32_masked(unsigned short *vscatter16_32) { for (int i = 0; i < MATRIX_SIZE; i++) { @@ -738,6 +765,7 @@ void check_gather_16() MATRIX_SIZE * sizeof(unsigned short)); } +/* masked gather the elements from the scatter buffer using C */ void scalar_gather_16_masked(unsigned short *vgather16) { for (int i = 0; i < MATRIX_SIZE; ++i) { @@ -756,7 +784,7 @@ void check_gather_16_masked() MATRIX_SIZE * sizeof(unsigned short)); } -/* gather the elements from the scatter buffer using C */ +/* gather the elements from the scatter32 buffer using C */ void scalar_gather_32(unsigned int *vgather32) { for (int i = 0; i < MATRIX_SIZE; ++i) { @@ -772,6 +800,7 @@ void check_gather_32(void) MATRIX_SIZE * sizeof(unsigned int)); } +/* masked gather the elements from the scatter32 buffer using C */ void scalar_gather_32_masked(unsigned int *vgather32) { for (int i = 0; i < MATRIX_SIZE; ++i) { @@ -781,7 +810,6 @@ void scalar_gather_32_masked(unsigned int *vgather32) } } - void check_gather_32_masked(void) { memset(vgather32_ref, gather_32_masked_init(), @@ -791,7 +819,7 @@ void check_gather_32_masked(void) vgather32_ref, MATRIX_SIZE * sizeof(unsigned int)); } -/* gather the elements from the scatter buffer using C */ +/* gather the elements from the scatter16_32 buffer using C */ void scalar_gather_16_32(unsigned short *vgather16_32) { for (int i = 0; i < MATRIX_SIZE; ++i) { @@ -807,6 +835,7 @@ void check_gather_16_32(void) MATRIX_SIZE * sizeof(unsigned short)); } +/* masked gather the elements from the scatter16_32 buffer using C */ void scalar_gather_16_32_masked(unsigned short *vgather16_32) { for (int i = 0; i < MATRIX_SIZE; ++i) { diff --git a/tests/tcg/hexagon/signal_context.c b/tests/tcg/hexagon/signal_context.c new file mode 100644 index 0000000000..7202fa64b6 --- /dev/null +++ b/tests/tcg/hexagon/signal_context.c @@ -0,0 +1,84 @@ +/* + * Copyright(c) 2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include + +void sig_user(int sig, siginfo_t *info, void *puc) +{ + asm("r7 = #0\n\t" + "p0 = r7\n\t" + "p1 = r7\n\t" + "p2 = r7\n\t" + "p3 = r7\n\t" + : : : "r7", "p0", "p1", "p2", "p3"); +} + +int main() +{ + int err = 0; + unsigned int i = 100000; + struct sigaction act; + struct itimerspec it; + timer_t tid; + struct sigevent sev; + + act.sa_sigaction = sig_user; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + sigaction(SIGUSR1, &act, NULL); + sev.sigev_notify = SIGEV_SIGNAL; + sev.sigev_signo = SIGUSR1; + sev.sigev_value.sival_ptr = &tid; + timer_create(CLOCK_REALTIME, &sev, &tid); + it.it_interval.tv_sec = 0; + it.it_interval.tv_nsec = 100000; + it.it_value.tv_sec = 0; + it.it_value.tv_nsec = 100000; + timer_settime(tid, 0, &it, NULL); + + asm("loop0(1f, %1)\n\t" + "1: r8 = #0xff\n\t" + " p0 = r8\n\t" + " p1 = r8\n\t" + " p2 = r8\n\t" + " p3 = r8\n\t" + " jump 3f\n\t" + "2: memb(%0) = #1\n\t" + " jump 4f\n\t" + "3:\n\t" + " r8 = p0\n\t" + " p0 = cmp.eq(r8, #0xff)\n\t" + " if (!p0) jump 2b\n\t" + " r8 = p1\n\t" + " p0 = cmp.eq(r8, #0xff)\n\t" + " if (!p0) jump 2b\n\t" + " r8 = p2\n\t" + " p0 = cmp.eq(r8, #0xff)\n\t" + " if (!p0) jump 2b\n\t" + " r8 = p3\n\t" + " p0 = cmp.eq(r8, #0xff)\n\t" + " if (!p0) jump 2b\n\t" + "4: {}: endloop0\n\t" + : + : "r"(&err), "r"(i) + : "memory", "r8", "p0", "p1", "p2", "p3"); + + puts(err ? "FAIL" : "PASS"); + return err; +} diff --git a/tests/tcg/hexagon/test_abs.S b/tests/tcg/hexagon/test_abs.S new file mode 100644 index 0000000000..d68aea6f64 --- /dev/null +++ b/tests/tcg/hexagon/test_abs.S @@ -0,0 +1,17 @@ +/* Purpose: test example, verify the soundness of the abs operation */ + + .text + .globl _start + +_start: + { + r1 = #-2 + r2 = #2 + } + { + r3 = abs(r1) + } + { + p0 = cmp.eq(r3, r2); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_bitcnt.S b/tests/tcg/hexagon/test_bitcnt.S new file mode 100644 index 0000000000..624460488e --- /dev/null +++ b/tests/tcg/hexagon/test_bitcnt.S @@ -0,0 +1,40 @@ +/* + * Purpose: test example, verify the soundness of the cl[01] operations. + * + * The number 0x000001aa has 23 leading zeroes + * they become 55 when considered as 64 bit register + * and it has 1 trailing zero. + */ + .text + .globl _start + +_start: + { + r0 = #426 + r1 = #0 + } + { + r2 = cl0(r0) + } + { + p0 = cmp.eq(r2, #23); if (p0.new) jump:t test2 + jump fail + } + +test2: + { + r2 = cl0(r1:0) + } + { + p0 = cmp.eq(r2, #55); if (p0.new) jump:t test3 + jump fail + } + +test3: + { + r2 = ct0(r0) + } + { + p0 = cmp.eq(r2, #1); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_bitsplit.S b/tests/tcg/hexagon/test_bitsplit.S new file mode 100644 index 0000000000..275658e613 --- /dev/null +++ b/tests/tcg/hexagon/test_bitsplit.S @@ -0,0 +1,22 @@ +/* Purpose: test example, verify the soundness of the bitsplit operation */ + + .text + .globl _start + +_start: + { + r1 = #187 + } + { + r3:2 = bitsplit(r1, #3) + } + { + p0 = cmp.eq(r2, #3); if (p0.new) jump:t test2 + jump fail + } + +test2: + { + p0 = cmp.eq(r3, #23); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_call.S b/tests/tcg/hexagon/test_call.S new file mode 100644 index 0000000000..338cd04e40 --- /dev/null +++ b/tests/tcg/hexagon/test_call.S @@ -0,0 +1,64 @@ +/* + * Purpose: test function calls and duplex instructions. + * The string "Hello there, I'm a test string!" with the first letter replaced + * with a capital L should be printed out. + */ + +#define SYS_write 64 +#define FD_STDOUT 1 + + .text + .globl test +test: + { + jumpr r31 + memb(r0+#0) = #76 + } +.Lfunc_end0: +.Ltmp0: + .size test, .Ltmp0-test + + .globl _start +_start: + { + r0 = ##dummy_buffer + allocframe(#0) + call test + } + { + call write + } + { + deallocframe + jump pass + } +.Lfunc_end1: +.Ltmp1: + .size _start, .Ltmp1-_start + +write: + { + r6 = #SYS_write + r0 = #FD_STDOUT + r1 = ##dummy_buffer + r2 = #33 + } + { + trap0(#1) + } + { + jumpr r31 + } + +.Lfunc_end2: +.Ltmp2: + .size write, .Ltmp2-write + + .type dummy_buffer,@object + .data + .globl dummy_buffer + .p2align 3 +dummy_buffer: + .string "Hello there, I'm a test string!\n" + .space 223 + .size dummy_buffer, 256 diff --git a/tests/tcg/hexagon/test_clobber.S b/tests/tcg/hexagon/test_clobber.S new file mode 100644 index 0000000000..10046c30d2 --- /dev/null +++ b/tests/tcg/hexagon/test_clobber.S @@ -0,0 +1,29 @@ +/* + * Purpose: demonstrate the successful operation of the register save mechanism, + * in which the caller saves the registers that will be clobbered, and restores + * them after the call. + */ + + .text + .globl _start + +_start: + allocframe(#8) + { + r16 = #47 + r17 = #155 + } + memd(sp+#0) = r17:16 + { + r16 = #255 + r17 = #42 + } + { + deallocframe + r17:16 = memd(sp+#0) + } + { + p0 = cmp.eq(r16, #47) + p0 = cmp.eq(r17, #155); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_cmp.S b/tests/tcg/hexagon/test_cmp.S new file mode 100644 index 0000000000..1db87d3db5 --- /dev/null +++ b/tests/tcg/hexagon/test_cmp.S @@ -0,0 +1,31 @@ +/* Purpose: test a signed and unsigned comparison */ + + .text + .globl _start + +_start: + { + jump signed + } + + .globl signed +signed: + { + r0 = #-2 + r1 = #0 + } + { + p0 = cmp.lt(r0, r1); if (p0.new) jump:t unsigned + jump fail + } + + .globl unsigned +unsigned: + { + r0 = #-2 + r1 = #0 + } + { + p0 = cmp.gtu(r0, r1); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_dotnew.S b/tests/tcg/hexagon/test_dotnew.S new file mode 100644 index 0000000000..b18b6a72e2 --- /dev/null +++ b/tests/tcg/hexagon/test_dotnew.S @@ -0,0 +1,38 @@ +/* Purpose: test the .new operator while performing memory stores. */ + + .text + .globl _start + +_start: + { + allocframe(#16) + } + { + r0 = #1 + memw(sp+#0) = r0.new + } + { + r1 = #2 + memw(sp+#4) = r1.new + } + { + r2 = #3 + memw(sp+#8) = r2.new + } + { + r0 = memw(sp+#8) + } + { + r1 = memw(sp+#4) + } + { + r2 = memw(sp+#0) + } + { + r3 = mpyi(r1, r2) + } + { + deallocframe + p0 = cmp.eq(r3, #2); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_ext.S b/tests/tcg/hexagon/test_ext.S new file mode 100644 index 0000000000..03e7bce2a7 --- /dev/null +++ b/tests/tcg/hexagon/test_ext.S @@ -0,0 +1,13 @@ +/* Purpose: test immediate extender instructions. */ + + .text + .globl _start + +_start: + { + r2 = ##-559038737 + } + { + p0 = cmp.eq(r2, ##-559038737); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_fibonacci.S b/tests/tcg/hexagon/test_fibonacci.S new file mode 100644 index 0000000000..4ef2c3896e --- /dev/null +++ b/tests/tcg/hexagon/test_fibonacci.S @@ -0,0 +1,30 @@ +/* Purpose: computes the Fibonacci series up to a constant number. */ + + .text + .globl _start + +_start: + { + r2 = #100 + } + { + p0 = cmp.gt(r2, #0); if (!p0.new) jump:nt .LBB0_3 + } + { + r3 = #0 + r4 = #1 + } +.LBB0_2: + { + r5 = r4 + } + { + p0 = cmp.gt(r2, r5); if (p0.new) jump:nt .LBB0_2 + r4 = add(r3, r4) + r3 = r5 + } +.LBB0_3: + { + p0 = cmp.eq(r3, #144); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_hl.S b/tests/tcg/hexagon/test_hl.S new file mode 100644 index 0000000000..93ace46aeb --- /dev/null +++ b/tests/tcg/hexagon/test_hl.S @@ -0,0 +1,16 @@ +/* Purpose: test example, verify the soundness of the high/low assignment */ + + .text + .globl _start + +_start: + { + r0.H = #42 + } + { + r0.L = #69 + } + { + p0 = cmp.eq(r0, #2752581); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_hwloops.S b/tests/tcg/hexagon/test_hwloops.S new file mode 100644 index 0000000000..42785e6f25 --- /dev/null +++ b/tests/tcg/hexagon/test_hwloops.S @@ -0,0 +1,19 @@ +/* Purpose: simple C Program to test hardware loops. */ + + .text + .globl _start + +_start: + { + loop0(.LBB0_1, #10) + r2 = #0 + } +.LBB0_1: + { + r2 = add(r2, #1) + nop + }:endloop0 + { + p0 = cmp.eq(r2, #10); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_jmp.S b/tests/tcg/hexagon/test_jmp.S new file mode 100644 index 0000000000..5be25c52b2 --- /dev/null +++ b/tests/tcg/hexagon/test_jmp.S @@ -0,0 +1,22 @@ +/* Purpose: test example, verify the soundness of the jump operation */ + +#define SYS_exit_group 94 + + .text + .globl _start + +_start: + { + jump pass + } + /* + * Inlined fail label in crt.S so we can fail without + * having a functioning jump + */ + { + r0 = #1 + r6 = #SYS_exit_group + } + { + trap0(#1) + } diff --git a/tests/tcg/hexagon/test_lsr.S b/tests/tcg/hexagon/test_lsr.S new file mode 100644 index 0000000000..b30aa64673 --- /dev/null +++ b/tests/tcg/hexagon/test_lsr.S @@ -0,0 +1,36 @@ +/* Purpose: test the soundness of the lsr operation */ + + .text + .globl _start + +_start: + { + r0 = #-56984 + r1 = #2147483647 + } + { + r2 = #0x19 + } + { + r0 &= lsr(r1, r2) + } + { + p0 = cmp.eq(r0, #0x28); if (p0.new) jump:t test2 + jump fail + } + +test2: + { + r0 = #0x0000000a + r1 = #0x00000000 + } + { + r2 = #-1 + } + { + r1:0 = lsl(r1:0, r2) + } + { + p0 = cmp.eq(r0, #0x5); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_mpyi.S b/tests/tcg/hexagon/test_mpyi.S new file mode 100644 index 0000000000..953b46e57e --- /dev/null +++ b/tests/tcg/hexagon/test_mpyi.S @@ -0,0 +1,17 @@ +/* Purpose: test a simple multiplication operation */ + + .text + .globl _start + +_start: + { + r1 = #4 + r2 = #6 + } + { + r3 = mpyi(r1, r2) + } + { + p0 = cmp.eq(r3, #24); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_packet.S b/tests/tcg/hexagon/test_packet.S new file mode 100644 index 0000000000..9ec9d8d6fb --- /dev/null +++ b/tests/tcg/hexagon/test_packet.S @@ -0,0 +1,29 @@ +/* + * Purpose: test that writes of a register in a packet are performed only after + * that packet has finished its execution. + */ + + .text + .globl _start + +_start: + { + allocframe(#8) + } + { + r2 = #4 + r3 = #6 + } + { + memw(sp+#0) = r2 + } + { + r3 = memw(sp+#0) + r0 = add(r2, r3) + } + { + deallocframe + p0 = cmp.eq(r3, #4) + p0 = cmp.eq(r0, #10); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_reorder.S b/tests/tcg/hexagon/test_reorder.S new file mode 100644 index 0000000000..5ee0539836 --- /dev/null +++ b/tests/tcg/hexagon/test_reorder.S @@ -0,0 +1,33 @@ +/* + * Purpose: demonstrate handling of .new uses appearing before the associated + * definition. + * Here we perform a jump that skips the code resetting R2 from 0xDEADBEEF to 0, + * only if P0.new is true, but P0 is assigned to 1 (R4) in the next instruction + * in the packet. + */ + + .text + .globl _start + +_start: + { + r2 = #-559038737 + } + { + r4 = #1 + } + { + if (p0.new) jump:nt skip + p0 = r4; + } + +fallthrough: + { + r2 = #0 + } + +skip: + { + p0 = cmp.eq(r2, #-559038737); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_round.S b/tests/tcg/hexagon/test_round.S new file mode 100644 index 0000000000..3c83812fe8 --- /dev/null +++ b/tests/tcg/hexagon/test_round.S @@ -0,0 +1,29 @@ +/* + * Purpose: test example, verify the soundness of the cround operation + * 106 = 0b1101010 with the comma at third digit is 12.5 which is crounded to 12 + * but rounded to 13. + */ + + .text + .globl _start + +_start: + { + r1 = #200 + } + { + r2 = round(r1, #4) + } + { + p0 = cmp.eq(r2, #13); if (p0.new) jump:t test2 + jump fail + } + +test2: + { + r2 = cround(r1, #4) + } + { + p0 = cmp.eq(r2, #12); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_vavgw.S b/tests/tcg/hexagon/test_vavgw.S new file mode 100644 index 0000000000..53c9df706a --- /dev/null +++ b/tests/tcg/hexagon/test_vavgw.S @@ -0,0 +1,31 @@ +/* + * Purpose: test example, verify the soundness of the vavgw operation. + * + * 0x00030001 averaged with 0x00010003 results 0x00020002. + */ + + .text + .globl _start + +_start: + { + r0 = #3 + r1 = #1 + } + { + r2 = #1 + r3 = #3 + } + { + r1:0 = vavgw(r1:0, r3:2):crnd + } + { + p0 = cmp.eq(r0, #2); if (p0.new) jump:t test2 + jump fail + } + +test2: + { + p0 = cmp.eq(r1, #2); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_vcmpb.S b/tests/tcg/hexagon/test_vcmpb.S new file mode 100644 index 0000000000..66d253eb48 --- /dev/null +++ b/tests/tcg/hexagon/test_vcmpb.S @@ -0,0 +1,30 @@ +/* + * Purpose: test example, verify the soundness of the vector compare bytes + * operation. + * + * Vector byte comparison between 0x1234567887654321 and 0x1234567800000000 + * should result in 0b11110000 in binary, or 0xf0 in hex. + */ + + .text + .globl _start + +_start: + { + r0 = #0x87654321 + r1 = #0x12345678 + } + { + r2 = #0x00000000 + r3 = #0x12345678 + } + { + p2 = vcmpb.eq(r1:0, r3:2) + } + { + r4 = p2 + } + { + p0 = cmp.eq(r4, #0xf0); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_vcmpw.S b/tests/tcg/hexagon/test_vcmpw.S new file mode 100644 index 0000000000..5be88d1e2e --- /dev/null +++ b/tests/tcg/hexagon/test_vcmpw.S @@ -0,0 +1,30 @@ +/* + * Purpose: test example, verify the soundness of the vector compare words + * operation. + * + * Vector word comparison between 0x1234567887654321 and 0x1234567800000000 + * should result in 0b11110000 in binary, or 0xf0 in hex. + */ + + .text + .globl _start + +_start: + { + r0 = #0x87654321 + r1 = #0x12345678 + } + { + r2 = #0x00000000 + r3 = #0x12345678 + } + { + p2 = vcmpw.eq(r1:0, r3:2) + } + { + r4 = p2 + } + { + p0 = cmp.eq(r4, #0xf0); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_vlsrw.S b/tests/tcg/hexagon/test_vlsrw.S new file mode 100644 index 0000000000..912e49aa0b --- /dev/null +++ b/tests/tcg/hexagon/test_vlsrw.S @@ -0,0 +1,20 @@ +/* Purpose: test the soundness of the vlsrw operation */ + + .text + .globl _start + +_start: + { + r0 = #0x00000001 + r1 = #0x00000001 + } + { + r1:0 = vlsrw(r1:0, #1) + } + { + r0 = add(r0, r1) + } + { + p0 = cmp.eq(r0, #0); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_vmaxh.S b/tests/tcg/hexagon/test_vmaxh.S new file mode 100644 index 0000000000..4ea6bd9d96 --- /dev/null +++ b/tests/tcg/hexagon/test_vmaxh.S @@ -0,0 +1,35 @@ +/* + * Purpose: test example, verify the soundness of the vrmaxh operation. + * + * The maximum between 0x0002000300010005 and 0x0003000200020007 is + * 0x0003000300020007. + * + * input: r1 = 0x00010003 r0 = 0x00010005 r3 = 0x00030002 r2 = 0x00020007 + * output: r1 = 0x00030003 r0 = 0x00020007 + */ + + .text + .globl _start + +_start: + { + r0 = #65541 + r1 = #65539 + } + { + r2 = #131079 + r3 = #196610 + } + { + r1:0 = vmaxh(r1:0, r3:2) + } + { + p0 = cmp.eq(r0, #131079); if (p0.new) jump:t test2 + jump fail + } + +test2: + { + p0 = cmp.eq(r1, #196611); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_vminh.S b/tests/tcg/hexagon/test_vminh.S new file mode 100644 index 0000000000..e5fcf2eb94 --- /dev/null +++ b/tests/tcg/hexagon/test_vminh.S @@ -0,0 +1,35 @@ +/* + * Purpose: test example, verify the soundness of the vrmaxh operation. + * + * The minimum between 0x0002000300010005 and 0x0003000200020007 is + * 0x0003000300020007. + * + * input: r1 = 0x00010003 r0 = 0x00010005 r3 = 0x00030002 r2 = 0x00020007 + * output: r1 = 0x00010002 r0 = 0x00010005 + */ + + .text + .globl _start + +_start: + { + r0 = #65541 + r1 = #65539 + } + { + r2 = #131079 + r3 = #196610 + } + { + r1:0 = vminh(r1:0, r3:2) + } + { + p0 = cmp.eq(r0, #65541); if (p0.new) jump:t test2 + jump fail + } + +test2: + { + p0 = cmp.eq(r1, #65538); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_vpmpyh.S b/tests/tcg/hexagon/test_vpmpyh.S new file mode 100644 index 0000000000..f02758e449 --- /dev/null +++ b/tests/tcg/hexagon/test_vpmpyh.S @@ -0,0 +1,28 @@ +/* + * Purpose: test example, verify the soundness of the vpmpyh operator. + * + * 0x01020304 vector polynomial multiplied with 0x04030201 results + * 0x000400060b060b04. + */ + + .text + .globl _start + +_start: + { + r0 = #16909060 + r1 = #67305985 + } + { + r1:0 = vpmpyh(r0, r1) + } + { + p0 = cmp.eq(r0, #184945412); if (p0.new) jump:t test2 + jump fail + } + +test2: + { + p0 = cmp.eq(r1, #262150); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/test_vspliceb.S b/tests/tcg/hexagon/test_vspliceb.S new file mode 100644 index 0000000000..53c4a91c51 --- /dev/null +++ b/tests/tcg/hexagon/test_vspliceb.S @@ -0,0 +1,31 @@ +/* + * Purpose: test example, verify the soundness of the vspliceb operation + * the operation is a binary splice of two 64bit operators. + * + * vspliceb(0xffffffffffffffff,0x0000000000000000,5) = 0x000000ffffffffff. + */ + .text + .globl _start + +_start: + { + r0 = #-1 + r1 = #-1 + } + { + r2 = #0 + r3 = #0 + } + { + r5:4 = vspliceb(r1:0, r3:2, #5) + } + { + p0 = cmp.eq(r4, #-1); if (p0.new) jump:t test2 + jump fail + } + +test2: + { + p0 = cmp.eq(r5, #255); if (p0.new) jump:t pass + jump fail + } diff --git a/tests/tcg/hexagon/usr.c b/tests/tcg/hexagon/usr.c index a531511cec..92bc86a213 100644 --- a/tests/tcg/hexagon/usr.c +++ b/tests/tcg/hexagon/usr.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2022-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,35 +24,7 @@ int err; -static void __check(int line, uint32_t val, uint32_t expect) -{ - if (val != expect) { - printf("ERROR at line %d: %d != %d\n", line, val, expect); - err++; - } -} - -#define check(RES, EXP) __check(__LINE__, RES, EXP) - -static void __check32(int line, uint32_t val, uint32_t expect) -{ - if (val != expect) { - printf("ERROR at line %d: 0x%08x != 0x%08x\n", line, val, expect); - err++; - } -} - -#define check32(RES, EXP) __check32(__LINE__, RES, EXP) - -static void __check64(int line, uint64_t val, uint64_t expect) -{ - if (val != expect) { - printf("ERROR at line %d: 0x%016llx != 0x%016llx\n", line, val, expect); - err++; - } -} - -#define check64(RES, EXP) __check64(__LINE__, RES, EXP) +#include "hex_test.h" /* * Some of the instructions tested are only available on certain versions @@ -61,51 +33,6 @@ static void __check64(int line, uint64_t val, uint64_t expect) #define CORE_HAS_AUDIO (__HEXAGON_ARCH__ >= 67 && defined(__HEXAGON_AUDIO__)) #define CORE_IS_V67 (__HEXAGON_ARCH__ >= 67) -/* Define the bits in Hexagon USR register */ -#define USR_OVF_BIT 0 /* Sticky saturation overflow */ -#define USR_FPINVF_BIT 1 /* IEEE FP invalid sticky flag */ -#define USR_FPDBZF_BIT 2 /* IEEE FP divide-by-zero sticky flag */ -#define USR_FPOVFF_BIT 3 /* IEEE FP overflow sticky flag */ -#define USR_FPUNFF_BIT 4 /* IEEE FP underflow sticky flag */ -#define USR_FPINPF_BIT 5 /* IEEE FP inexact sticky flag */ - -/* Corresponding values in USR */ -#define USR_CLEAR 0 -#define USR_OVF (1 << USR_OVF_BIT) -#define USR_FPINVF (1 << USR_FPINVF_BIT) -#define USR_FPDBZF (1 << USR_FPDBZF_BIT) -#define USR_FPOVFF (1 << USR_FPOVFF_BIT) -#define USR_FPUNFF (1 << USR_FPUNFF_BIT) -#define USR_FPINPF (1 << USR_FPINPF_BIT) - -/* Some useful floating point values */ -const uint32_t SF_INF = 0x7f800000; -const uint32_t SF_QNaN = 0x7fc00000; -const uint32_t SF_SNaN = 0x7fb00000; -const uint32_t SF_QNaN_neg = 0xffc00000; -const uint32_t SF_SNaN_neg = 0xffb00000; -const uint32_t SF_HEX_NaN = 0xffffffff; -const uint32_t SF_zero = 0x00000000; -const uint32_t SF_one = 0x3f800000; -const uint32_t SF_one_recip = 0x3f7f0001; /* 0.9960... */ -const uint32_t SF_one_invsqrta = 0x3f7f0000; /* 0.99609375 */ -const uint32_t SF_two = 0x40000000; -const uint32_t SF_four = 0x40800000; -const uint32_t SF_small_neg = 0xab98fba8; -const uint32_t SF_large_pos = 0x5afa572e; - -const uint64_t DF_QNaN = 0x7ff8000000000000ULL; -const uint64_t DF_SNaN = 0x7ff7000000000000ULL; -const uint64_t DF_QNaN_neg = 0xfff8000000000000ULL; -const uint64_t DF_SNaN_neg = 0xfff7000000000000ULL; -const uint64_t DF_HEX_NaN = 0xffffffffffffffffULL; -const uint64_t DF_zero = 0x0000000000000000ULL; -const uint64_t DF_any = 0x3f80000000000000ULL; -const uint64_t DF_one = 0x3ff0000000000000ULL; -const uint64_t DF_one_hh = 0x3ff001ff80000000ULL; /* 1.00048... */ -const uint64_t DF_small_neg = 0xbd731f7500000000ULL; -const uint64_t DF_large_pos = 0x7f80000000000001ULL; - /* * Templates for functions to execute an instruction * @@ -120,12 +47,6 @@ const uint64_t DF_large_pos = 0x7f80000000000001ULL; * Xx read/write */ -/* Clear bits 0-5 in USR */ -#define CLEAR_USRBITS \ - "r2 = usr\n\t" \ - "r2 = and(r2, #0xffffffc0)\n\t" \ - "usr = r2\n\t" - /* Template for instructions with one register operand */ #define FUNC_x_OP_x(RESTYPE, SRCTYPE, NAME, INSN) \ static RESTYPE NAME(SRCTYPE src, uint32_t *usr_result) \ @@ -427,6 +348,7 @@ FUNC_P_OP_P(vabshsat, "%0 = vabsh(%2):sat") FUNC_P_OP_PP(vnavgwr, "%0 = vnavgw(%2, %3):rnd:sat") FUNC_R_OP_RI(round_ri_sat, "%0 = round(%2, #%3):sat") FUNC_R_OP_RR(asr_r_r_sat, "%0 = asr(%2, %3):sat") +FUNC_R_OP_RR(asl_r_r_sat, "%0 = asl(%2, %3):sat") FUNC_XPp_OP_PP(ACS, "%0, p2 = vacsh(%3, %4)") @@ -505,7 +427,7 @@ FUNC_Rp_OP_R(sfinvsqrta, "%0, p2 = sfinvsqrta(%3)") uint32_t usr_result; \ result = FUNC(src, &usr_result); \ CHECKFN(result, RES); \ - check(usr_result, USR_RES); \ + check32(usr_result, USR_RES); \ } while (0) #define TEST_R_OP_R(FUNC, SRC, RES, USR_RES) \ @@ -529,8 +451,8 @@ TEST_x_OP_x(uint64_t, check64, uint32_t, FUNC, SRC, RES, USR_RES) uint32_t usr_result; \ result = FUNC(src, &pred_result, &usr_result); \ CHECKFN(result, RES); \ - check(pred_result, PRED_RES); \ - check(usr_result, USR_RES); \ + check32(pred_result, PRED_RES); \ + check32(usr_result, USR_RES); \ } while (0) #define TEST_Rp_OP_R(FUNC, SRC, RES, PRED_RES, USR_RES) \ @@ -545,7 +467,7 @@ TEST_xp_OP_x(uint32_t, check32, uint32_t, FUNC, SRC, RES, PRED_RES, USR_RES) uint32_t usr_result; \ result = FUNC(src1, src2, &usr_result); \ CHECKFN(result, RES); \ - check(usr_result, USR_RES); \ + check32(usr_result, USR_RES); \ } while (0) #define TEST_P_OP_PP(FUNC, SRC1, SRC2, RES, USR_RES) \ @@ -582,8 +504,8 @@ TEST_x_OP_xx(uint64_t, check64, uint64_t, uint32_t, \ uint32_t usr_result; \ result = FUNC(src1, src2, &pred_result, &usr_result); \ CHECKFN(result, RES); \ - check(pred_result, PRED_RES); \ - check(usr_result, USR_RES); \ + check32(pred_result, PRED_RES); \ + check32(usr_result, USR_RES); \ } while (0) #define TEST_Rp_OP_RR(FUNC, SRC1, SRC2, RES, PRED_RES, USR_RES) \ @@ -599,7 +521,7 @@ TEST_xp_OP_xx(uint32_t, check32, uint32_t, uint32_t, FUNC, SRC1, SRC2, \ uint32_t usr_result; \ result = FUNC(src1, src2, &usr_result); \ CHECKFN(result, RES); \ - check(usr_result, USR_RES); \ + check32(usr_result, USR_RES); \ } while (0) #define TEST_R_OP_RI(FUNC, SRC1, SRC2, RES, USR_RES) \ @@ -619,7 +541,7 @@ TEST_x_OP_xI(uint32_t, check64, uint64_t, \ uint32_t usr_result; \ result = FUNC(result, src1, src2, &usr_result); \ CHECKFN(result, RES); \ - check(usr_result, USR_RES); \ + check32(usr_result, USR_RES); \ } while (0) #define TEST_XR_OP_RR(FUNC, RESIN, SRC1, SRC2, RES, USR_RES) \ @@ -644,7 +566,7 @@ TEST_Xx_OP_xx(uint64_t, check64, uint32_t, uint32_t, \ uint32_t usr_result; \ result = FUNC(result, src1, src2, &pred_res, &usr_result); \ CHECKFN(result, RES); \ - check(usr_result, USR_RES); \ + check32(usr_result, USR_RES); \ } while (0) #define TEST_XPp_OP_PP(FUNC, RESIN, SRC1, SRC2, RES, PRED_RES, USR_RES) \ @@ -661,7 +583,7 @@ TEST_Xxp_OP_xx(uint64_t, check64, uint64_t, uint64_t, FUNC, RESIN, SRC1, SRC2, \ uint32_t usr_result; \ result = FUNC(result, src1, src2, pred, &usr_result); \ CHECKFN(result, RES); \ - check(usr_result, USR_RES); \ + check32(usr_result, USR_RES); \ } while (0) #define TEST_XR_OP_RRp(FUNC, RESIN, SRC1, SRC2, PRED, RES, USR_RES) \ @@ -676,8 +598,8 @@ TEST_Xx_OP_xxp(uint32_t, check32, uint32_t, uint32_t, \ SRC2TYPE src2 = SRC2; \ uint32_t usr_result; \ result = FUNC(src1, src2, &usr_result); \ - check(result, RES); \ - check(usr_result, USR_RES); \ + check32(result, RES); \ + check32(usr_result, USR_RES); \ } while (0) #define TEST_CMP_RR(FUNC, SRC1, SRC2, RES, USR_RES) \ @@ -905,12 +827,33 @@ int main() TEST_R_OP_RI(round_ri_sat, 0x0000ffff, 2, 0x00004000, USR_CLEAR); TEST_R_OP_RI(round_ri_sat, 0x7fffffff, 2, 0x1fffffff, USR_OVF); - TEST_R_OP_RR(asr_r_r_sat, 0x0000ffff, 0x00000002, 0x00003fff, - USR_CLEAR); - TEST_R_OP_RR(asr_r_r_sat, 0x00ffffff, 0xfffffff5, 0x7fffffff, - USR_OVF); - TEST_R_OP_RR(asr_r_r_sat, 0x80000000, 0xfffffff5, 0x80000000, - USR_OVF); + TEST_R_OP_RR(asr_r_r_sat, 0x0000ffff, 0x02, 0x00003fff, USR_CLEAR); + TEST_R_OP_RR(asr_r_r_sat, 0x80000000, 0x01, 0xc0000000, USR_CLEAR); + TEST_R_OP_RR(asr_r_r_sat, 0xffffffff, 0x01, 0xffffffff, USR_CLEAR); + TEST_R_OP_RR(asr_r_r_sat, 0x00ffffff, 0xf5, 0x7fffffff, USR_OVF); + TEST_R_OP_RR(asr_r_r_sat, 0x80000000, 0xf5, 0x80000000, USR_OVF); + TEST_R_OP_RR(asr_r_r_sat, 0x7fff0000, 0x42, 0x7fffffff, USR_OVF); + TEST_R_OP_RR(asr_r_r_sat, 0xff000000, 0x42, 0x80000000, USR_OVF); + TEST_R_OP_RR(asr_r_r_sat, 4096, 32, 0x00000000, USR_CLEAR); + TEST_R_OP_RR(asr_r_r_sat, 4096, -32, 0x7fffffff, USR_OVF); + TEST_R_OP_RR(asr_r_r_sat, -4096, 32, 0xffffffff, USR_CLEAR); + TEST_R_OP_RR(asr_r_r_sat, -4096, -32, 0x80000000, USR_OVF); + TEST_R_OP_RR(asr_r_r_sat, 0, -32, 0x00000000, USR_CLEAR); + TEST_R_OP_RR(asr_r_r_sat, 1, -32, 0x7fffffff, USR_OVF); + + TEST_R_OP_RR(asl_r_r_sat, 0x00000000, 0x40, 0x00000000, USR_CLEAR); + TEST_R_OP_RR(asl_r_r_sat, 0x80000000, 0xff, 0xc0000000, USR_CLEAR); + TEST_R_OP_RR(asl_r_r_sat, 0xffffffff, 0xff, 0xffffffff, USR_CLEAR); + TEST_R_OP_RR(asl_r_r_sat, 0x00ffffff, 0x0b, 0x7fffffff, USR_OVF); + TEST_R_OP_RR(asl_r_r_sat, 0x80000000, 0x0b, 0x80000000, USR_OVF); + TEST_R_OP_RR(asl_r_r_sat, 0x7fff0000, 0xbe, 0x7fffffff, USR_OVF); + TEST_R_OP_RR(asl_r_r_sat, 0xff000000, 0xbe, 0x80000000, USR_OVF); + TEST_R_OP_RR(asl_r_r_sat, 4096, 32, 0x7fffffff, USR_OVF); + TEST_R_OP_RR(asl_r_r_sat, 4096, -32, 0x00000000, USR_CLEAR); + TEST_R_OP_RR(asl_r_r_sat, -4096, 32, 0x80000000, USR_OVF); + TEST_R_OP_RR(asl_r_r_sat, -4096, -32, 0xffffffff, USR_CLEAR); + TEST_R_OP_RR(asl_r_r_sat, 0, 32, 0x00000000, USR_CLEAR); + TEST_R_OP_RR(asl_r_r_sat, 1, 32, 0x7fffffff, USR_OVF); TEST_XPp_OP_PP(ACS, 0x0004000300020001ULL, 0x0001000200030004ULL, 0x0000000000000000ULL, 0x0004000300030004ULL, 0xf0, @@ -933,6 +876,8 @@ int main() TEST_R_OP_RR(sfmin, SF_QNaN, SF_one, SF_one, USR_CLEAR); TEST_R_OP_RR(sfmin, SF_SNaN, SF_QNaN, SF_HEX_NaN, USR_FPINVF); TEST_R_OP_RR(sfmin, SF_QNaN, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + TEST_R_OP_RR(sfmin, SF_zero, SF_zero_neg, SF_zero_neg, USR_CLEAR); + TEST_R_OP_RR(sfmin, SF_zero_neg, SF_zero, SF_zero_neg, USR_CLEAR); TEST_R_OP_RR(sfmax, SF_one, SF_small_neg, SF_one, USR_CLEAR); TEST_R_OP_RR(sfmax, SF_one, SF_SNaN, SF_one, USR_FPINVF); @@ -941,6 +886,8 @@ int main() TEST_R_OP_RR(sfmax, SF_QNaN, SF_one, SF_one, USR_CLEAR); TEST_R_OP_RR(sfmax, SF_SNaN, SF_QNaN, SF_HEX_NaN, USR_FPINVF); TEST_R_OP_RR(sfmax, SF_QNaN, SF_SNaN, SF_HEX_NaN, USR_FPINVF); + TEST_R_OP_RR(sfmax, SF_zero, SF_zero_neg, SF_zero, USR_CLEAR); + TEST_R_OP_RR(sfmax, SF_zero_neg, SF_zero, SF_zero, USR_CLEAR); TEST_R_OP_RR(sfadd, SF_one, SF_QNaN, SF_HEX_NaN, USR_CLEAR); TEST_R_OP_RR(sfadd, SF_one, SF_SNaN, SF_HEX_NaN, USR_FPINVF); @@ -1003,6 +950,8 @@ int main() TEST_P_OP_PP(dfmin, DF_QNaN, DF_any, DF_any, USR_CLEAR); TEST_P_OP_PP(dfmin, DF_SNaN, DF_QNaN, DF_HEX_NaN, USR_FPINVF); TEST_P_OP_PP(dfmin, DF_QNaN, DF_SNaN, DF_HEX_NaN, USR_FPINVF); + TEST_P_OP_PP(dfmin, DF_zero, DF_zero_neg, DF_zero_neg, USR_CLEAR); + TEST_P_OP_PP(dfmin, DF_zero_neg, DF_zero, DF_zero_neg, USR_CLEAR); TEST_P_OP_PP(dfmax, DF_any, DF_small_neg, DF_any, USR_CLEAR); TEST_P_OP_PP(dfmax, DF_any, DF_SNaN, DF_any, USR_FPINVF); @@ -1011,6 +960,8 @@ int main() TEST_P_OP_PP(dfmax, DF_QNaN, DF_any, DF_any, USR_CLEAR); TEST_P_OP_PP(dfmax, DF_SNaN, DF_QNaN, DF_HEX_NaN, USR_FPINVF); TEST_P_OP_PP(dfmax, DF_QNaN, DF_SNaN, DF_HEX_NaN, USR_FPINVF); + TEST_P_OP_PP(dfmax, DF_zero, DF_zero_neg, DF_zero, USR_CLEAR); + TEST_P_OP_PP(dfmax, DF_zero_neg, DF_zero, DF_zero, USR_CLEAR); TEST_XP_OP_PP(dfmpyhh, DF_one, DF_one, DF_one, DF_one_hh, USR_CLEAR); TEST_XP_OP_PP(dfmpyhh, DF_zero, DF_any, DF_QNaN, DF_HEX_NaN, USR_CLEAR); diff --git a/tests/tcg/hexagon/v68_hvx.c b/tests/tcg/hexagon/v68_hvx.c new file mode 100644 index 0000000000..02718722a3 --- /dev/null +++ b/tests/tcg/hexagon/v68_hvx.c @@ -0,0 +1,90 @@ +/* + * Copyright(c) 2022-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include + +int err; + +#include "hvx_misc.h" + +MMVector v6mpy_buffer0[BUFSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); +MMVector v6mpy_buffer1[BUFSIZE] __attribute__((aligned(MAX_VEC_SIZE_BYTES))); + +static void init_v6mpy_buffers(void) +{ + int counter0 = 0; + int counter1 = 17; + for (int i = 0; i < BUFSIZE; i++) { + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + v6mpy_buffer0[i].w[j] = counter0++; + v6mpy_buffer1[i].w[j] = counter1++; + } + } +} + +int v6mpy_ref[BUFSIZE][MAX_VEC_SIZE_BYTES / 4] = { +#include "v6mpy_ref.c.inc" +}; + +static void test_v6mpy(void) +{ + void *p00 = buffer0; + void *p01 = v6mpy_buffer0; + void *p10 = buffer1; + void *p11 = v6mpy_buffer1; + void *pout = output; + + memset(expect, 0xff, sizeof(expect)); + memset(output, 0xff, sizeof(expect)); + + for (int i = 0; i < BUFSIZE; i++) { + asm("v2 = vmem(%0 + #0)\n\t" + "v3 = vmem(%1 + #0)\n\t" + "v4 = vmem(%2 + #0)\n\t" + "v5 = vmem(%3 + #0)\n\t" + "v5:4.w = v6mpy(v5:4.ub, v3:2.b, #1):v\n\t" + "vmem(%4 + #0) = v4\n\t" + : : "r"(p00), "r"(p01), "r"(p10), "r"(p11), "r"(pout) + : "v2", "v3", "v4", "v5", "memory"); + p00 += sizeof(MMVector); + p01 += sizeof(MMVector); + p10 += sizeof(MMVector); + p11 += sizeof(MMVector); + pout += sizeof(MMVector); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + expect[i].w[j] = v6mpy_ref[i][j]; + } + } + + check_output_w(__LINE__, BUFSIZE); +} + +int main() +{ + init_buffers(); + init_v6mpy_buffers(); + + test_v6mpy(); + + puts(err ? "FAIL" : "PASS"); + return err ? 1 : 0; +} diff --git a/tests/tcg/hexagon/v68_scalar.c b/tests/tcg/hexagon/v68_scalar.c new file mode 100644 index 0000000000..7a8adb1130 --- /dev/null +++ b/tests/tcg/hexagon/v68_scalar.c @@ -0,0 +1,186 @@ +/* + * Copyright(c) 2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include + +/* + * Test the scalar core instructions that are new in v68 + */ + +int err; + +static int buffer32[] = { 1, 2, 3, 4 }; +static long long buffer64[] = { 5, 6, 7, 8 }; + +static void __check32(int line, uint32_t result, uint32_t expect) +{ + if (result != expect) { + printf("ERROR at line %d: 0x%08x != 0x%08x\n", + line, result, expect); + err++; + } +} + +#define check32(RES, EXP) __check32(__LINE__, RES, EXP) + +static void __check64(int line, uint64_t result, uint64_t expect) +{ + if (result != expect) { + printf("ERROR at line %d: 0x%016llx != 0x%016llx\n", + line, result, expect); + err++; + } +} + +#define check64(RES, EXP) __check64(__LINE__, RES, EXP) + +static inline int loadw_aq(int *p) +{ + int res; + asm volatile("%0 = memw_aq(%1)\n\t" + : "=r"(res) : "r"(p)); + return res; +} + +static void test_loadw_aq(void) +{ + int res; + + res = loadw_aq(&buffer32[0]); + check32(res, 1); + res = loadw_aq(&buffer32[1]); + check32(res, 2); +} + +static inline long long loadd_aq(long long *p) +{ + long long res; + asm volatile("%0 = memd_aq(%1)\n\t" + : "=r"(res) : "r"(p)); + return res; +} + +static void test_loadd_aq(void) +{ + long long res; + + res = loadd_aq(&buffer64[2]); + check64(res, 7); + res = loadd_aq(&buffer64[3]); + check64(res, 8); +} + +static inline void release_at(int *p) +{ + asm volatile("release(%0):at\n\t" + : : "r"(p)); +} + +static void test_release_at(void) +{ + release_at(&buffer32[2]); + check64(buffer32[2], 3); + release_at(&buffer32[3]); + check64(buffer32[3], 4); +} + +static inline void release_st(int *p) +{ + asm volatile("release(%0):st\n\t" + : : "r"(p)); +} + +static void test_release_st(void) +{ + release_st(&buffer32[2]); + check64(buffer32[2], 3); + release_st(&buffer32[3]); + check64(buffer32[3], 4); +} + +static inline void storew_rl_at(int *p, int val) +{ + asm volatile("memw_rl(%0):at = %1\n\t" + : : "r"(p), "r"(val) : "memory"); +} + +static void test_storew_rl_at(void) +{ + storew_rl_at(&buffer32[2], 9); + check64(buffer32[2], 9); + storew_rl_at(&buffer32[3], 10); + check64(buffer32[3], 10); +} + +static inline void stored_rl_at(long long *p, long long val) +{ + asm volatile("memd_rl(%0):at = %1\n\t" + : : "r"(p), "r"(val) : "memory"); +} + +static void test_stored_rl_at(void) +{ + stored_rl_at(&buffer64[2], 11); + check64(buffer64[2], 11); + stored_rl_at(&buffer64[3], 12); + check64(buffer64[3], 12); +} + +static inline void storew_rl_st(int *p, int val) +{ + asm volatile("memw_rl(%0):st = %1\n\t" + : : "r"(p), "r"(val) : "memory"); +} + +static void test_storew_rl_st(void) +{ + storew_rl_st(&buffer32[0], 13); + check64(buffer32[0], 13); + storew_rl_st(&buffer32[1], 14); + check64(buffer32[1], 14); +} + +static inline void stored_rl_st(long long *p, long long val) +{ + asm volatile("memd_rl(%0):st = %1\n\t" + : : "r"(p), "r"(val) : "memory"); +} + +static void test_stored_rl_st(void) +{ + stored_rl_st(&buffer64[0], 15); + check64(buffer64[0], 15); + stored_rl_st(&buffer64[1], 15); + check64(buffer64[1], 15); +} + +int main() +{ + test_loadw_aq(); + test_loadd_aq(); + test_release_at(); + test_release_st(); + test_storew_rl_at(); + test_stored_rl_at(); + test_storew_rl_st(); + test_stored_rl_st(); + + puts(err ? "FAIL" : "PASS"); + return err ? 1 : 0; +} diff --git a/tests/tcg/hexagon/v69_hvx.c b/tests/tcg/hexagon/v69_hvx.c new file mode 100644 index 0000000000..a0d567d142 --- /dev/null +++ b/tests/tcg/hexagon/v69_hvx.c @@ -0,0 +1,318 @@ +/* + * Copyright(c) 2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include + +int err; + +#include "hvx_misc.h" + +#define fVROUND(VAL, SHAMT) \ + ((VAL) + (((SHAMT) > 0) ? (1LL << ((SHAMT) - 1)) : 0)) + +#define fVSATUB(VAL) \ + ((((VAL) & 0xffLL) == (VAL)) ? \ + (VAL) : \ + ((((int32_t)(VAL)) < 0) ? 0 : 0xff)) + +#define fVSATUH(VAL) \ + ((((VAL) & 0xffffLL) == (VAL)) ? \ + (VAL) : \ + ((((int32_t)(VAL)) < 0) ? 0 : 0xffff)) + +static void test_vasrvuhubrndsat(void) +{ + void *p0 = buffer0; + void *p1 = buffer1; + void *pout = output; + + memset(expect, 0xaa, sizeof(expect)); + memset(output, 0xbb, sizeof(output)); + + for (int i = 0; i < BUFSIZE / 2; i++) { + asm("v4 = vmem(%0 + #0)\n\t" + "v5 = vmem(%0 + #1)\n\t" + "v6 = vmem(%1 + #0)\n\t" + "v5.ub = vasr(v5:4.uh, v6.ub):rnd:sat\n\t" + "vmem(%2) = v5\n\t" + : : "r"(p0), "r"(p1), "r"(pout) + : "v4", "v5", "v6", "memory"); + p0 += sizeof(MMVector) * 2; + p1 += sizeof(MMVector); + pout += sizeof(MMVector); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 2; j++) { + int shamt; + uint8_t byte0; + uint8_t byte1; + + shamt = buffer1[i].ub[2 * j + 0] & 0x7; + byte0 = fVSATUB(fVROUND(buffer0[2 * i + 0].uh[j], shamt) >> shamt); + shamt = buffer1[i].ub[2 * j + 1] & 0x7; + byte1 = fVSATUB(fVROUND(buffer0[2 * i + 1].uh[j], shamt) >> shamt); + expect[i].uh[j] = (byte1 << 8) | (byte0 & 0xff); + } + } + + check_output_h(__LINE__, BUFSIZE / 2); +} + +static void test_vasrvuhubsat(void) +{ + void *p0 = buffer0; + void *p1 = buffer1; + void *pout = output; + + memset(expect, 0xaa, sizeof(expect)); + memset(output, 0xbb, sizeof(output)); + + for (int i = 0; i < BUFSIZE / 2; i++) { + asm("v4 = vmem(%0 + #0)\n\t" + "v5 = vmem(%0 + #1)\n\t" + "v6 = vmem(%1 + #0)\n\t" + "v5.ub = vasr(v5:4.uh, v6.ub):sat\n\t" + "vmem(%2) = v5\n\t" + : : "r"(p0), "r"(p1), "r"(pout) + : "v4", "v5", "v6", "memory"); + p0 += sizeof(MMVector) * 2; + p1 += sizeof(MMVector); + pout += sizeof(MMVector); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 2; j++) { + int shamt; + uint8_t byte0; + uint8_t byte1; + + shamt = buffer1[i].ub[2 * j + 0] & 0x7; + byte0 = fVSATUB(buffer0[2 * i + 0].uh[j] >> shamt); + shamt = buffer1[i].ub[2 * j + 1] & 0x7; + byte1 = fVSATUB(buffer0[2 * i + 1].uh[j] >> shamt); + expect[i].uh[j] = (byte1 << 8) | (byte0 & 0xff); + } + } + + check_output_h(__LINE__, BUFSIZE / 2); +} + +static void test_vasrvwuhrndsat(void) +{ + void *p0 = buffer0; + void *p1 = buffer1; + void *pout = output; + + memset(expect, 0xaa, sizeof(expect)); + memset(output, 0xbb, sizeof(output)); + + for (int i = 0; i < BUFSIZE / 2; i++) { + asm("v4 = vmem(%0 + #0)\n\t" + "v5 = vmem(%0 + #1)\n\t" + "v6 = vmem(%1 + #0)\n\t" + "v5.uh = vasr(v5:4.w, v6.uh):rnd:sat\n\t" + "vmem(%2) = v5\n\t" + : : "r"(p0), "r"(p1), "r"(pout) + : "v4", "v5", "v6", "memory"); + p0 += sizeof(MMVector) * 2; + p1 += sizeof(MMVector); + pout += sizeof(MMVector); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + int shamt; + uint16_t half0; + uint16_t half1; + + shamt = buffer1[i].uh[2 * j + 0] & 0xf; + half0 = fVSATUH(fVROUND(buffer0[2 * i + 0].w[j], shamt) >> shamt); + shamt = buffer1[i].uh[2 * j + 1] & 0xf; + half1 = fVSATUH(fVROUND(buffer0[2 * i + 1].w[j], shamt) >> shamt); + expect[i].w[j] = (half1 << 16) | (half0 & 0xffff); + } + } + + check_output_w(__LINE__, BUFSIZE / 2); +} + +static void test_vasrvwuhsat(void) +{ + void *p0 = buffer0; + void *p1 = buffer1; + void *pout = output; + + memset(expect, 0xaa, sizeof(expect)); + memset(output, 0xbb, sizeof(output)); + + for (int i = 0; i < BUFSIZE / 2; i++) { + asm("v4 = vmem(%0 + #0)\n\t" + "v5 = vmem(%0 + #1)\n\t" + "v6 = vmem(%1 + #0)\n\t" + "v5.uh = vasr(v5:4.w, v6.uh):sat\n\t" + "vmem(%2) = v5\n\t" + : : "r"(p0), "r"(p1), "r"(pout) + : "v4", "v5", "v6", "memory"); + p0 += sizeof(MMVector) * 2; + p1 += sizeof(MMVector); + pout += sizeof(MMVector); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + int shamt; + uint16_t half0; + uint16_t half1; + + shamt = buffer1[i].uh[2 * j + 0] & 0xf; + half0 = fVSATUH(buffer0[2 * i + 0].w[j] >> shamt); + shamt = buffer1[i].uh[2 * j + 1] & 0xf; + half1 = fVSATUH(buffer0[2 * i + 1].w[j] >> shamt); + expect[i].w[j] = (half1 << 16) | (half0 & 0xffff); + } + } + + check_output_w(__LINE__, BUFSIZE / 2); +} + +static void test_vassign_tmp(void) +{ + void *p0 = buffer0; + void *pout = output; + + memset(expect, 0xaa, sizeof(expect)); + memset(output, 0xbb, sizeof(output)); + + for (int i = 0; i < BUFSIZE; i++) { + /* + * Assign into v12 as .tmp, then use it in the next packet + * Should get the new value within the same packet and + * the old value in the next packet + */ + asm("v3 = vmem(%0 + #0)\n\t" + "r1 = #1\n\t" + "v12 = vsplat(r1)\n\t" + "r1 = #2\n\t" + "v13 = vsplat(r1)\n\t" + "{\n\t" + " v12.tmp = v13\n\t" + " v4.w = vadd(v12.w, v3.w)\n\t" + "}\n\t" + "v4.w = vadd(v4.w, v12.w)\n\t" + "vmem(%1 + #0) = v4\n\t" + : : "r"(p0), "r"(pout) + : "r1", "v3", "v4", "v12", "v13", "memory"); + p0 += sizeof(MMVector); + pout += sizeof(MMVector); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + expect[i].w[j] = buffer0[i].w[j] + 3; + } + } + + check_output_w(__LINE__, BUFSIZE); +} + +static void test_vcombine_tmp(void) +{ + void *p0 = buffer0; + void *p1 = buffer1; + void *pout = output; + + memset(expect, 0xaa, sizeof(expect)); + memset(output, 0xbb, sizeof(output)); + + for (int i = 0; i < BUFSIZE; i++) { + /* + * Combine into v13:12 as .tmp, then use it in the next packet + * Should get the new value within the same packet and + * the old value in the next packet + */ + asm("v3 = vmem(%0 + #0)\n\t" + "r1 = #1\n\t" + "v12 = vsplat(r1)\n\t" + "r1 = #2\n\t" + "v13 = vsplat(r1)\n\t" + "r1 = #3\n\t" + "v14 = vsplat(r1)\n\t" + "r1 = #4\n\t" + "v15 = vsplat(r1)\n\t" + "{\n\t" + " v13:12.tmp = vcombine(v15, v14)\n\t" + " v4.w = vadd(v12.w, v3.w)\n\t" + " v16 = v13\n\t" + "}\n\t" + "v4.w = vadd(v4.w, v12.w)\n\t" + "v4.w = vadd(v4.w, v13.w)\n\t" + "v4.w = vadd(v4.w, v16.w)\n\t" + "vmem(%2 + #0) = v4\n\t" + : : "r"(p0), "r"(p1), "r"(pout) + : "r1", "v3", "v4", "v12", "v13", "v14", "v15", "v16", "memory"); + p0 += sizeof(MMVector); + p1 += sizeof(MMVector); + pout += sizeof(MMVector); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + expect[i].w[j] = buffer0[i].w[j] + 10; + } + } + + check_output_w(__LINE__, BUFSIZE); +} + +static void test_vmpyuhvs(void) +{ + void *p0 = buffer0; + void *p1 = buffer1; + void *pout = output; + + memset(expect, 0xaa, sizeof(expect)); + memset(output, 0xbb, sizeof(output)); + + for (int i = 0; i < BUFSIZE; i++) { + asm("v4 = vmem(%0 + #0)\n\t" + "v5 = vmem(%1 + #0)\n\t" + "v4.uh = vmpy(V4.uh, v5.uh):>>16\n\t" + "vmem(%2) = v4\n\t" + : : "r"(p0), "r"(p1), "r"(pout) + : "v4", "v5", "memory"); + p0 += sizeof(MMVector); + p1 += sizeof(MMVector); + pout += sizeof(MMVector); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 2; j++) { + expect[i].uh[j] = (buffer0[i].uh[j] * buffer1[i].uh[j]) >> 16; + } + } + + check_output_h(__LINE__, BUFSIZE); +} + +int main() +{ + init_buffers(); + + test_vasrvuhubrndsat(); + test_vasrvuhubsat(); + test_vasrvwuhrndsat(); + test_vasrvwuhsat(); + + test_vassign_tmp(); + test_vcombine_tmp(); + + test_vmpyuhvs(); + + puts(err ? "FAIL" : "PASS"); + return err ? 1 : 0; +} diff --git a/tests/tcg/hexagon/v6mpy_ref.c.inc b/tests/tcg/hexagon/v6mpy_ref.c.inc new file mode 100644 index 0000000000..8258cddcb1 --- /dev/null +++ b/tests/tcg/hexagon/v6mpy_ref.c.inc @@ -0,0 +1,161 @@ +/* + * Copyright(c) 2021-2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +{ 0xffffee11, 0xfffffcca, 0xffffc1b3, 0xffffd0cc, + 0xffffe215, 0xfffff58e, 0xffffaf37, 0xffffc310, + 0xffffd919, 0xfffff152, 0xffff9fbb, 0xffffb854, + 0xffffd31d, 0xfffff016, 0xffff933f, 0xffffb098, + 0xffffd021, 0xfffff1da, 0xffff89c3, 0xffffabdc, + 0xffffd025, 0xfffff69e, 0xffff8347, 0xffffaa20, + 0xffffd329, 0xfffffe62, 0xffff7fcb, 0xffffab64, + 0xffffd92d, 0x00000926, 0xffff7f4f, 0xffffafa8, + }, +{ 0xffffe231, 0x000016ea, 0xffff81d3, 0xffffb6ec, + 0xffffee35, 0x000027ae, 0xffff8757, 0xffffc130, + 0xfffffd39, 0x00003b72, 0xffff8fdb, 0xffffce74, + 0x00000f3d, 0x00005236, 0xffff9b5f, 0xffffdeb8, + 0x00002441, 0x00006bfa, 0xffffa9e3, 0xfffff1fc, + 0x00003c45, 0x000088be, 0xffffbb67, 0x00000840, + 0x00005749, 0x0000a882, 0xffffcfeb, 0xffffe684, + 0x0000494d, 0x00009a46, 0xffffb16f, 0x000002c8, + }, +{ 0xfffff351, 0x0000440a, 0xffff4af3, 0xffff9c0c, + 0xffffef55, 0x000044ce, 0xffff4077, 0xffff9650, + 0xffffee59, 0x00004892, 0xffff38fb, 0xffff9394, + 0xfffff05d, 0x00004f56, 0xffff347f, 0xffff93d8, + 0xfffff561, 0x0000591a, 0xffff3303, 0xffff971c, + 0xfffffd65, 0x000065de, 0xffff3487, 0xffff9d60, + 0x00000869, 0x000075a2, 0xffff390b, 0xffffa6a4, + 0x0000166d, 0x00008866, 0xffff408f, 0xffffb2e8, + }, +{ 0x00002771, 0x00009e2a, 0xffff4b13, 0xffffc22c, + 0x00003b75, 0x0000b6ee, 0xffff5897, 0xffffd470, + 0x00005279, 0x0000d2b2, 0xffff691b, 0xffffe9b4, + 0x00006c7d, 0x0000f176, 0xffff7c9f, 0x000001f8, + 0x00008981, 0x0001133a, 0xffff9323, 0x00001d3c, + 0x0000a985, 0x000137fe, 0xffffaca7, 0x00003b80, + 0x0000cc89, 0x00015fc2, 0xffffc92b, 0xffffe1c4, + 0x0000868d, 0x00011986, 0xffff72af, 0x00000608, + }, +{ 0xfffff891, 0x00008b4a, 0xfffed433, 0xffff674c, + 0xfffffc95, 0x0000940e, 0xfffed1b7, 0xffff6990, + 0x00000399, 0x00009fd2, 0xfffed23b, 0xffff6ed4, + 0x00000d9d, 0x0000ae96, 0xfffed5bf, 0xffff7718, + 0x00001aa1, 0x0000c05a, 0xfffedc43, 0xffff825c, + 0x00002aa5, 0x0000d51e, 0xfffee5c7, 0xffff90a0, + 0x00003da9, 0x0000ece2, 0xfffef24b, 0xffffa1e4, + 0x000053ad, 0x000107a6, 0xffff01cf, 0xffffb628, + }, +{ 0x00006cb1, 0x0001256a, 0xffff1453, 0xffffcd6c, + 0x000088b5, 0x0001462e, 0xffff29d7, 0xffffe7b0, + 0x0000a7b9, 0x000169f2, 0xffff425b, 0x000004f4, + 0x0000c9bd, 0x000190b6, 0xffff5ddf, 0x00002538, + 0x0000eec1, 0x0001ba7a, 0xffff7c63, 0x0000487c, + 0x000116c5, 0x0001e73e, 0xffff9de7, 0x00006ec0, + 0x000141c9, 0x00021702, 0xffffc26b, 0xffffdd04, + 0x0000c3cd, 0x000198c6, 0xffff33ef, 0x00000948, + }, +{ 0xfffffdd1, 0x0000d28a, 0xfffe5d73, 0xffff328c, + 0x000009d5, 0x0000e34e, 0xfffe62f7, 0xffff3cd0, + 0x000018d9, 0x0000f712, 0xfffe6b7b, 0xffff4a14, + 0x00002add, 0x00010dd6, 0xfffe76ff, 0xffff5a58, + 0x00003fe1, 0x0001279a, 0xfffe8583, 0xffff6d9c, + 0x000057e5, 0x0001445e, 0xfffe9707, 0xffff83e0, + 0x000072e9, 0x00016422, 0xfffeab8b, 0xffff9d24, + 0x000090ed, 0x000186e6, 0xfffec30f, 0xffffb968, + }, +{ 0x0000b1f1, 0x0001acaa, 0xfffedd93, 0xffffd8ac, + 0x0000d5f5, 0x0001d56e, 0xfffefb17, 0xfffffaf0, + 0x0000fcf9, 0x00020132, 0xffff1b9b, 0x00002034, + 0x000126fd, 0x00022ff6, 0xffff3f1f, 0x00008b36, + 0x000093c3, 0x00009d80, 0x00009d6d, 0x0000a78a, + 0x0000b4d7, 0x0000c354, 0x0000b801, 0x0000c6de, + 0x0000d4eb, 0x0000e828, 0x0000d195, 0xffffea32, + 0x00000fff, 0x000022fc, 0xfffffc29, 0x00000f86, + }, +{ 0xffffee13, 0xfffffcd0, 0xffffc1bd, 0xffffd0da, + 0xffffe327, 0xfffff6a4, 0xffffb051, 0xffffc42e, + 0xffffd73b, 0xffffef78, 0xffff9de5, 0xffffb682, + 0xffffd24f, 0xffffef4c, 0xffff9279, 0xffffafd6, + 0xffffd063, 0xfffff220, 0xffff8a0d, 0xffffac2a, + 0xffffd177, 0xfffff7f4, 0xffff84a1, 0xffffab7e, + 0xffffd18b, 0xfffffcc8, 0xffff7e35, 0xffffa9d2, + 0xffffd89f, 0x0000089c, 0xffff7ec9, 0xffffaf26, + }, +{ 0xffffe2b3, 0x00001770, 0xffff825d, 0xffffb77a, + 0xffffefc7, 0x00002944, 0xffff88f1, 0xffffc2ce, + 0xfffffbdb, 0x00003a18, 0xffff8e85, 0xffffcd22, + 0x00000eef, 0x000051ec, 0xffff9b19, 0xffffde76, + 0x00002503, 0x00006cc0, 0xffffaaad, 0xfffff2ca, + 0x00003e17, 0x00008a94, 0xffffbd41, 0x00000a1e, + 0x0000562b, 0x0000a768, 0xffffced5, 0xffffe572, + 0x0000493f, 0x00009a3c, 0xffffb169, 0x000002c6, + }, +{ 0xfffff353, 0x00004410, 0xffff4afd, 0xffff9c1a, + 0xfffff067, 0x000045e4, 0xffff4191, 0xffff976e, + 0xffffec7b, 0x000046b8, 0xffff3725, 0xffff91c2, + 0xffffef8f, 0x00004e8c, 0xffff33b9, 0xffff9316, + 0xfffff5a3, 0x00005960, 0xffff334d, 0xffff976a, + 0xfffffeb7, 0x00006734, 0xffff35e1, 0xffff9ebe, + 0x000006cb, 0x00007408, 0xffff3775, 0xffffa512, + 0x000015df, 0x000087dc, 0xffff4009, 0xffffb266, + }, +{ 0x000027f3, 0x00009eb0, 0xffff4b9d, 0xffffc2ba, + 0x00003d07, 0x0000b884, 0xffff5a31, 0xffffd60e, + 0x0000511b, 0x0000d158, 0xffff67c5, 0xffffe862, + 0x00006c2f, 0x0000f12c, 0xffff7c59, 0x000001b6, + 0x00008a43, 0x00011400, 0xffff93ed, 0x00001e0a, + 0x0000ab57, 0x000139d4, 0xffffae81, 0x00003d5e, + 0x0000cb6b, 0x00015ea8, 0xffffc815, 0xffffe0b2, + 0x0000867f, 0x0001197c, 0xffff72a9, 0x00000606, + }, +{ 0xfffff893, 0x00008b50, 0xfffed43d, 0xffff675a, + 0xfffffda7, 0x00009524, 0xfffed2d1, 0xffff6aae, + 0x000001bb, 0x00009df8, 0xfffed065, 0xffff6d02, + 0x00000ccf, 0x0000adcc, 0xfffed4f9, 0xffff7656, + 0x00001ae3, 0x0000c0a0, 0xfffedc8d, 0xffff82aa, + 0x00002bf7, 0x0000d674, 0xfffee721, 0xffff91fe, + 0x00003c0b, 0x0000eb48, 0xfffef0b5, 0xffffa052, + 0x0000531f, 0x0001071c, 0xffff0149, 0xffffb5a6, + }, +{ 0x00006d33, 0x000125f0, 0xffff14dd, 0xffffcdfa, + 0x00008a47, 0x000147c4, 0xffff2b71, 0xffffe94e, + 0x0000a65b, 0x00016898, 0xffff4105, 0x000003a2, + 0x0000c96f, 0x0001906c, 0xffff5d99, 0x000024f6, + 0x0000ef83, 0x0001bb40, 0xffff7d2d, 0x0000494a, + 0x00011897, 0x0001e914, 0xffff9fc1, 0x0000709e, + 0x000140ab, 0x000215e8, 0xffffc155, 0xffffdbf2, + 0x0000c3bf, 0x000198bc, 0xffff33e9, 0x00000946, + }, +{ 0xfffffdd3, 0x0000d290, 0xfffe5d7d, 0xffff329a, + 0x00000ae7, 0x0000e464, 0xfffe6411, 0xffff3dee, + 0x000016fb, 0x0000f538, 0xfffe69a5, 0xffff4842, + 0x00002a0f, 0x00010d0c, 0xfffe7639, 0xffff5996, + 0x00004023, 0x000127e0, 0xfffe85cd, 0xffff6dea, + 0x00005937, 0x000145b4, 0xfffe9861, 0xffff853e, + 0x0000714b, 0x00016288, 0xfffea9f5, 0xffff9b92, + 0x0000905f, 0x0001865c, 0xfffec289, 0xffffb8e6, + }, +{ 0x0000b273, 0x0001ad30, 0xfffede1d, 0xffffd93a, + 0x0000d787, 0x0001d704, 0xfffefcb1, 0xfffffc8e, + 0x0000fb9b, 0x0001ffd8, 0xffff1a45, 0x00001ee2, + 0x000126af, 0x00022fac, 0xffff3ed9, 0x00008af4, + 0x00009485, 0x00009e46, 0x00009e37, 0x0000a858, + 0x0000b6a9, 0x0000c52a, 0x0000b9db, 0x0000c8bc, + 0x0000d3cd, 0x0000e70e, 0x0000d07f, 0xffffe920, + 0x00000ff1, 0x000022f2, 0xfffffc23, 0x00000f84, + }, diff --git a/tests/tcg/hexagon/v73_scalar.c b/tests/tcg/hexagon/v73_scalar.c new file mode 100644 index 0000000000..fee67fc531 --- /dev/null +++ b/tests/tcg/hexagon/v73_scalar.c @@ -0,0 +1,96 @@ +/* + * Copyright(c) 2023 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include + +/* + * Test the scalar core instructions that are new in v73 + */ + +int err; + +static void __check32(int line, uint32_t result, uint32_t expect) +{ + if (result != expect) { + printf("ERROR at line %d: 0x%08x != 0x%08x\n", + line, result, expect); + err++; + } +} + +#define check32(RES, EXP) __check32(__LINE__, RES, EXP) + +static void __check64(int line, uint64_t result, uint64_t expect) +{ + if (result != expect) { + printf("ERROR at line %d: 0x%016llx != 0x%016llx\n", + line, result, expect); + err++; + } +} + +#define check64(RES, EXP) __check64(__LINE__, RES, EXP) + +static bool my_func_called; + +static void my_func(void) +{ + my_func_called = true; +} + +static inline void callrh(void *func) +{ + asm volatile("callrh %0\n\t" + : : "r"(func) + /* Mark the caller-save registers as clobbered */ + : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", + "r10", "r11", "r12", "r13", "r14", "r15", "r28", + "p0", "p1", "p2", "p3"); +} + +static void test_callrh(void) +{ + my_func_called = false; + callrh(&my_func); + check32(my_func_called, true); +} + +static void test_jumprh(void) +{ + uint32_t res; + asm ("%0 = #5\n\t" + "r0 = ##1f\n\t" + "jumprh r0\n\t" + "%0 = #3\n\t" + "jump 2f\n\t" + "1:\n\t" + "%0 = #1\n\t" + "2:\n\t" + : "=r"(res) : : "r0"); + check32(res, 1); +} + +int main() +{ + test_callrh(); + test_jumprh(); + + puts(err ? "FAIL" : "PASS"); + return err ? 1 : 0; +} diff --git a/tests/tcg/hppa/Makefile.target b/tests/tcg/hppa/Makefile.target index b78e6b4849..ea5ae2186d 100644 --- a/tests/tcg/hppa/Makefile.target +++ b/tests/tcg/hppa/Makefile.target @@ -2,16 +2,11 @@ # # HPPA specific tweaks - specifically masking out broken tests -# On parisc Linux supports 4K/16K/64K (but currently only 4k works) -EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-16384 run-test-mmap-65536 - # This triggers failures for hppa-linux about 1% of the time # HPPA is the odd target that can't use the sigtramp page; # it requires the full vdso with dwarf2 unwind info. run-signals: signals $(call skip-test, $<, "BROKEN awaiting vdso support") -run-plugin-signals-with-%: - $(call skip-test, $<, "BROKEN awaiting vdso support") VPATH += $(SRC_PATH)/tests/tcg/hppa TESTS += stby diff --git a/tests/tcg/i386/Makefile.softmmu-target b/tests/tcg/i386/Makefile.softmmu-target index 9b9038d0be..5266f2335a 100644 --- a/tests/tcg/i386/Makefile.softmmu-target +++ b/tests/tcg/i386/Makefile.softmmu-target @@ -33,15 +33,5 @@ EXTRA_RUNS+=$(MULTIARCH_RUNS) memory: CFLAGS+=-DCHECK_UNALIGNED=1 -# non-inline runs will trigger the duplicate instruction heuristics in libinsn.so -run-plugin-%-with-libinsn.so: - $(call run-test, $@, \ - $(QEMU) -monitor none -display none \ - -chardev file$(COMMA)path=$@.out$(COMMA)id=output \ - -plugin ../../plugin/libinsn.so$(COMMA)inline=on \ - -d plugin -D $*-with-libinsn.so.pout \ - $(QEMU_OPTS) $*, \ - "$* on $(TARGET_NAME)") - # Running QEMU_OPTS+=-device isa-debugcon,chardev=output -device isa-debug-exit,iobase=0xf4,iosize=0x4 -kernel diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index bd73c96d0d..bbe2c44b2a 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -5,26 +5,37 @@ I386_SRC=$(SRC_PATH)/tests/tcg/i386 # Set search path for all sources VPATH += $(I386_SRC) +config-cc.mak: Makefile + $(quiet-@)( \ + $(call cc-option,-fno-pie, CROSS_CC_HAS_I386_NOPIE)) 3> config-cc.mak + +-include config-cc.mak + I386_SRCS=$(notdir $(wildcard $(I386_SRC)/*.c)) ALL_X86_TESTS=$(I386_SRCS:.c=) -SKIP_I386_TESTS=test-i386-ssse3 -X86_64_TESTS:=$(filter test-i386-ssse3, $(ALL_X86_TESTS)) +SKIP_I386_TESTS=test-i386-ssse3 test-avx test-3dnow test-mmx test-flags +X86_64_TESTS:=$(filter test-i386-adcox test-i386-bmi2 $(SKIP_I386_TESTS), $(ALL_X86_TESTS)) test-i386-sse-exceptions: CFLAGS += -msse4.1 -mfpmath=sse run-test-i386-sse-exceptions: QEMU_OPTS += -cpu max -run-plugin-test-i386-sse-exceptions-%: QEMU_OPTS += -cpu max test-i386-pcmpistri: CFLAGS += -msse4.2 run-test-i386-pcmpistri: QEMU_OPTS += -cpu max -run-plugin-test-i386-pcmpistri-%: QEMU_OPTS += -cpu max +test-i386-bmi2: CFLAGS=-O2 run-test-i386-bmi2: QEMU_OPTS += -cpu max -run-plugin-test-i386-bmi2-%: QEMU_OPTS += -cpu max + +test-i386-adcox: CFLAGS=-O2 +run-test-i386-adcox: QEMU_OPTS += -cpu max + +test-aes: CFLAGS += -O -msse2 -maes +test-aes: test-aes-main.c.inc +run-test-aes: QEMU_OPTS += -cpu max # # hello-i386 is a barebones app # -hello-i386: CFLAGS+=-ffreestanding +hello-i386: CFLAGS+=-ffreestanding -fno-stack-protector hello-i386: LDFLAGS+=-nostdlib # test-386 includes a couple of additional objects that need to be @@ -41,8 +52,6 @@ test-i386: $(call skip-test, "BUILD of $@", "missing -no-pie compiler support") run-test-i386: $(call skip-test, "RUN of test-i386", "not built") -run-plugin-test-i386-with-%: - $(call skip-test, "RUN of test-i386 ($*)", "not built") endif ifeq ($(SPEED), slow) @@ -52,31 +61,42 @@ test-i386-fprem.ref: test-i386-fprem run-test-i386-fprem: TIMEOUT=60 run-test-i386-fprem: test-i386-fprem test-i386-fprem.ref - $(call run-test,test-i386-fprem, $(QEMU) $<,"$< on $(TARGET_NAME)") + $(call run-test,test-i386-fprem, $(QEMU) $<) $(call diff-out,test-i386-fprem, test-i386-fprem.ref) else SKIP_I386_TESTS+=test-i386-fprem endif -# non-inline runs will trigger the duplicate instruction heuristics in libinsn.so -run-plugin-%-with-libinsn.so: - $(call run-test, $@, $(QEMU) $(QEMU_OPTS) \ - -plugin ../../plugin/libinsn.so$(COMMA)inline=on \ - -d plugin -D $*-with-libinsn.so.pout $*, \ - "$* (inline) on $(TARGET_NAME)") - # Update TESTS I386_TESTS:=$(filter-out $(SKIP_I386_TESTS), $(ALL_X86_TESTS)) TESTS=$(MULTIARCH_TESTS) $(I386_TESTS) -# On i386 and x86_64 Linux only supports 4k pages (large pages are a different hack) -EXTRA_RUNS+=run-test-mmap-4096 - sha512-sse: CFLAGS=-msse4.1 -O3 sha512-sse: sha512.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) run-sha512-sse: QEMU_OPTS+=-cpu max -run-plugin-sha512-sse-with-%: QEMU_OPTS+=-cpu max TESTS+=sha512-sse + +CLEANFILES += test-avx.h test-mmx.h test-3dnow.h +test-3dnow.h: test-mmx.py x86.csv + $(PYTHON) $(I386_SRC)/test-mmx.py $(I386_SRC)/x86.csv $@ 3DNOW + +test-mmx.h: test-mmx.py x86.csv + $(PYTHON) $(I386_SRC)/test-mmx.py $(I386_SRC)/x86.csv $@ MMX SSE SSE2 SSE3 SSSE3 + +test-avx.h: test-avx.py x86.csv + $(PYTHON) $(I386_SRC)/test-avx.py $(I386_SRC)/x86.csv $@ + +test-3dnow: CFLAGS += -masm=intel -O -I. +run-test-3dnow: QEMU_OPTS += -cpu max +test-3dnow: test-3dnow.h + +test-mmx: CFLAGS += -masm=intel -O -I. +run-test-mmx: QEMU_OPTS += -cpu max +test-mmx: test-mmx.h + +test-avx: CFLAGS += -mavx -masm=intel -O -I. +run-test-avx: QEMU_OPTS += -cpu max +test-avx: test-avx.h diff --git a/tests/tcg/i386/README b/tests/tcg/i386/README index 09e88f30dc..403d10dad8 100644 --- a/tests/tcg/i386/README +++ b/tests/tcg/i386/README @@ -15,6 +15,15 @@ The Linux system call vm86() is used to test vm86 emulation. Various exceptions are raised to test most of the x86 user space exception reporting. +test-avx +-------- + +This program executes most SSE/AVX instructions and generates a text output, +for comparison with the output obtained with a real CPU or another emulator. + +test-avx.h is generate from x86.csv by test-avx.py +x86.csv comes from https://github.com/quasilyte/avx512test + linux-test ---------- diff --git a/tests/tcg/i386/system/boot.S b/tests/tcg/i386/system/boot.S index 794c2cb0ad..28902c400d 100644 --- a/tests/tcg/i386/system/boot.S +++ b/tests/tcg/i386/system/boot.S @@ -2,12 +2,12 @@ * i386 boot code, based on qemu-bmibug. * * Copyright 2019 Doug Gale - * Copyright 2019 Linaro + * Copyright 2019, 2024 Linaro * - * This work is licensed under the terms of the GNU GPL, version 3 or later. + * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. * - * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later */ .section .head @@ -71,7 +71,7 @@ _start: add $8,%esp /* - * Don't worry about stack frame, assume everthing + * Don't worry about stack frame, assume everything * is garbage when we return, we won't need it. */ call main diff --git a/tests/tcg/i386/test-3dnow.c b/tests/tcg/i386/test-3dnow.c new file mode 100644 index 0000000000..67abc68677 --- /dev/null +++ b/tests/tcg/i386/test-3dnow.c @@ -0,0 +1,3 @@ +#define EMMS "femms" +#define TEST_FILE "test-3dnow.h" +#include "test-mmx.c" diff --git a/tests/tcg/i386/test-aes.c b/tests/tcg/i386/test-aes.c new file mode 100644 index 0000000000..199395e6cc --- /dev/null +++ b/tests/tcg/i386/test-aes.c @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "../multiarch/test-aes-main.c.inc" +#include + +static bool test_SB_SR(uint8_t *o, const uint8_t *i) +{ + __m128i vi = _mm_loadu_si128((const __m128i_u *)i); + + /* aesenclast also adds round key, so supply zero. */ + vi = _mm_aesenclast_si128(vi, _mm_setzero_si128()); + + _mm_storeu_si128((__m128i_u *)o, vi); + return true; +} + +static bool test_MC(uint8_t *o, const uint8_t *i) +{ + return false; +} + +static bool test_SB_SR_MC_AK(uint8_t *o, const uint8_t *i, const uint8_t *k) +{ + __m128i vi = _mm_loadu_si128((const __m128i_u *)i); + __m128i vk = _mm_loadu_si128((const __m128i_u *)k); + + vi = _mm_aesenc_si128(vi, vk); + + _mm_storeu_si128((__m128i_u *)o, vi); + return true; +} + +static bool test_ISB_ISR(uint8_t *o, const uint8_t *i) +{ + __m128i vi = _mm_loadu_si128((const __m128i_u *)i); + + /* aesdeclast also adds round key, so supply zero. */ + vi = _mm_aesdeclast_si128(vi, _mm_setzero_si128()); + + _mm_storeu_si128((__m128i_u *)o, vi); + return true; +} + +static bool test_IMC(uint8_t *o, const uint8_t *i) +{ + __m128i vi = _mm_loadu_si128((const __m128i_u *)i); + + vi = _mm_aesimc_si128(vi); + + _mm_storeu_si128((__m128i_u *)o, vi); + return true; +} + +static bool test_ISB_ISR_AK_IMC(uint8_t *o, const uint8_t *i, const uint8_t *k) +{ + return false; +} + +static bool test_ISB_ISR_IMC_AK(uint8_t *o, const uint8_t *i, const uint8_t *k) +{ + __m128i vi = _mm_loadu_si128((const __m128i_u *)i); + __m128i vk = _mm_loadu_si128((const __m128i_u *)k); + + vi = _mm_aesdec_si128(vi, vk); + + _mm_storeu_si128((__m128i_u *)o, vi); + return true; +} diff --git a/tests/tcg/i386/test-avx.c b/tests/tcg/i386/test-avx.c new file mode 100644 index 0000000000..230e6d84b8 --- /dev/null +++ b/tests/tcg/i386/test-avx.c @@ -0,0 +1,375 @@ +#include +#include +#include +#include + +typedef void (*testfn)(void); + +typedef struct { + uint64_t q0, q1, q2, q3; +} __attribute__((aligned(32))) v4di; + +typedef struct { + uint64_t mm[8]; + v4di ymm[16]; + uint64_t r[16]; + uint64_t flags; + uint32_t ff; + uint64_t pad; + v4di mem[4]; + v4di mem0[4]; +} reg_state; + +typedef struct { + int n; + testfn fn; + const char *s; + reg_state *init; +} TestDef; + +reg_state initI; +reg_state initF16; +reg_state initF32; +reg_state initF64; + +static void dump_ymm(const char *name, int n, const v4di *r, int ff) +{ + printf("%s%d = %016lx %016lx %016lx %016lx\n", + name, n, r->q3, r->q2, r->q1, r->q0); + if (ff == 64) { + double v[4]; + memcpy(v, r, sizeof(v)); + printf(" %16g %16g %16g %16g\n", + v[3], v[2], v[1], v[0]); + } else if (ff == 32) { + float v[8]; + memcpy(v, r, sizeof(v)); + printf(" %8g %8g %8g %8g %8g %8g %8g %8g\n", + v[7], v[6], v[5], v[4], v[3], v[2], v[1], v[0]); + } +} + +static void dump_regs(reg_state *s) +{ + int i; + + for (i = 0; i < 16; i++) { + dump_ymm("ymm", i, &s->ymm[i], 0); + } + for (i = 0; i < 4; i++) { + dump_ymm("mem", i, &s->mem0[i], 0); + } +} + +static void compare_state(const reg_state *a, const reg_state *b) +{ + int i; + for (i = 0; i < 8; i++) { + if (a->mm[i] != b->mm[i]) { + printf("MM%d = %016lx\n", i, b->mm[i]); + } + } + for (i = 0; i < 16; i++) { + if (a->r[i] != b->r[i]) { + printf("r%d = %016lx\n", i, b->r[i]); + } + } + for (i = 0; i < 16; i++) { + if (memcmp(&a->ymm[i], &b->ymm[i], 32)) { + dump_ymm("ymm", i, &b->ymm[i], a->ff); + } + } + for (i = 0; i < 4; i++) { + if (memcmp(&a->mem0[i], &a->mem[i], 32)) { + dump_ymm("mem", i, &a->mem[i], a->ff); + } + } + if (a->flags != b->flags) { + printf("FLAGS = %016lx\n", b->flags); + } +} + +#define LOADMM(r, o) "movq " #r ", " #o "[%0]\n\t" +#define LOADYMM(r, o) "vmovdqa " #r ", " #o "[%0]\n\t" +#define STOREMM(r, o) "movq " #o "[%1], " #r "\n\t" +#define STOREYMM(r, o) "vmovdqa " #o "[%1], " #r "\n\t" +#define MMREG(F) \ + F(mm0, 0x00) \ + F(mm1, 0x08) \ + F(mm2, 0x10) \ + F(mm3, 0x18) \ + F(mm4, 0x20) \ + F(mm5, 0x28) \ + F(mm6, 0x30) \ + F(mm7, 0x38) +#define YMMREG(F) \ + F(ymm0, 0x040) \ + F(ymm1, 0x060) \ + F(ymm2, 0x080) \ + F(ymm3, 0x0a0) \ + F(ymm4, 0x0c0) \ + F(ymm5, 0x0e0) \ + F(ymm6, 0x100) \ + F(ymm7, 0x120) \ + F(ymm8, 0x140) \ + F(ymm9, 0x160) \ + F(ymm10, 0x180) \ + F(ymm11, 0x1a0) \ + F(ymm12, 0x1c0) \ + F(ymm13, 0x1e0) \ + F(ymm14, 0x200) \ + F(ymm15, 0x220) +#define LOADREG(r, o) "mov " #r ", " #o "[rax]\n\t" +#define STOREREG(r, o) "mov " #o "[rax], " #r "\n\t" +#define REG(F) \ + F(rbx, 0x248) \ + F(rcx, 0x250) \ + F(rdx, 0x258) \ + F(rsi, 0x260) \ + F(rdi, 0x268) \ + F(r8, 0x280) \ + F(r9, 0x288) \ + F(r10, 0x290) \ + F(r11, 0x298) \ + F(r12, 0x2a0) \ + F(r13, 0x2a8) \ + F(r14, 0x2b0) \ + F(r15, 0x2b8) \ + +static void run_test(const TestDef *t) +{ + reg_state result; + reg_state *init = t->init; + memcpy(init->mem, init->mem0, sizeof(init->mem)); + printf("%5d %s\n", t->n, t->s); + asm volatile( + MMREG(LOADMM) + YMMREG(LOADYMM) + "sub rsp, 128\n\t" + "push rax\n\t" + "push rbx\n\t" + "push rcx\n\t" + "push rdx\n\t" + "push %1\n\t" + "push %2\n\t" + "mov rax, %0\n\t" + "pushf\n\t" + "pop rbx\n\t" + "shr rbx, 8\n\t" + "shl rbx, 8\n\t" + "mov rcx, 0x2c0[rax]\n\t" + "and rcx, 0xff\n\t" + "or rbx, rcx\n\t" + "push rbx\n\t" + "popf\n\t" + REG(LOADREG) + "mov rax, 0x240[rax]\n\t" + "call [rsp]\n\t" + "mov [rsp], rax\n\t" + "mov rax, 8[rsp]\n\t" + REG(STOREREG) + "mov rbx, [rsp]\n\t" + "mov 0x240[rax], rbx\n\t" + "mov rbx, 0\n\t" + "mov 0x270[rax], rbx\n\t" + "mov 0x278[rax], rbx\n\t" + "pushf\n\t" + "pop rbx\n\t" + "and rbx, 0xff\n\t" + "mov 0x2c0[rax], rbx\n\t" + "add rsp, 16\n\t" + "pop rdx\n\t" + "pop rcx\n\t" + "pop rbx\n\t" + "pop rax\n\t" + "add rsp, 128\n\t" + MMREG(STOREMM) + YMMREG(STOREYMM) + : : "r"(init), "r"(&result), "r"(t->fn) + : "memory", "cc", + "rsi", "rdi", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", + "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", + "ymm6", "ymm7", "ymm8", "ymm9", "ymm10", "ymm11", + "ymm12", "ymm13", "ymm14", "ymm15" + ); + compare_state(init, &result); +} + +#define TEST(n, cmd, type) \ +static void __attribute__((naked)) test_##n(void) \ +{ \ + asm volatile(cmd); \ + asm volatile("ret"); \ +} +#include "test-avx.h" + + +static const TestDef test_table[] = { +#define TEST(n, cmd, type) {n, test_##n, cmd, &init##type}, +#include "test-avx.h" + {-1, NULL, "", NULL} +}; + +static void run_all(void) +{ + const TestDef *t; + for (t = test_table; t->fn; t++) { + run_test(t); + } +} + +#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0])) + +uint16_t val_f16[] = { 0x4000, 0xbc00, 0x44cd, 0x3a66, 0x4200, 0x7a1a, 0x4780, 0x4826 }; +float val_f32[] = {2.0, -1.0, 4.8, 0.8, 3, -42.0, 5e6, 7.5, 8.3}; +double val_f64[] = {2.0, -1.0, 4.8, 0.8, 3, -42.0, 5e6, 7.5}; +v4di val_i64[] = { + {0x3d6b3b6a9e4118f2lu, 0x355ae76d2774d78clu, + 0xac3ff76c4daa4b28lu, 0xe7fabd204cb54083lu}, + {0xd851c54a56bf1f29lu, 0x4a84d1d50bf4c4fflu, + 0x56621e553d52b56clu, 0xd0069553da8f584alu}, + {0x5826475e2c5fd799lu, 0xfd32edc01243f5e9lu, + 0x738ba2c66d3fe126lu, 0x5707219c6e6c26b4lu}, +}; + +v4di deadbeef = {0xa5a5a5a5deadbeefull, 0xa5a5a5a5deadbeefull, + 0xa5a5a5a5deadbeefull, 0xa5a5a5a5deadbeefull}; +/* &gather_mem[0x10] is 512 bytes from the base; indices must be >=-64, <64 + * to account for scaling by 8 */ +v4di indexq = {0x000000000000001full, 0x000000000000003dull, + 0xffffffffffffffffull, 0xffffffffffffffdfull}; +v4di indexd = {0x00000002ffffffcdull, 0xfffffff500000010ull, + 0x0000003afffffff0ull, 0x000000000000000eull}; + +v4di gather_mem[0x20]; +_Static_assert(sizeof(gather_mem) == 1024); + +void init_f16reg(v4di *r) +{ + memset(r, 0, sizeof(*r)); + memcpy(r, val_f16, sizeof(val_f16)); +} + +void init_f32reg(v4di *r) +{ + static int n; + float v[8]; + int i; + for (i = 0; i < 8; i++) { + v[i] = val_f32[n++]; + if (n == ARRAY_LEN(val_f32)) { + n = 0; + } + } + memcpy(r, v, sizeof(*r)); +} + +void init_f64reg(v4di *r) +{ + static int n; + double v[4]; + int i; + for (i = 0; i < 4; i++) { + v[i] = val_f64[n++]; + if (n == ARRAY_LEN(val_f64)) { + n = 0; + } + } + memcpy(r, v, sizeof(*r)); +} + +void init_intreg(v4di *r) +{ + static uint64_t mask; + static int n; + + r->q0 = val_i64[n].q0 ^ mask; + r->q1 = val_i64[n].q1 ^ mask; + r->q2 = val_i64[n].q2 ^ mask; + r->q3 = val_i64[n].q3 ^ mask; + n++; + if (n == ARRAY_LEN(val_i64)) { + n = 0; + mask *= 0x104C11DB7; + } +} + +static void init_all(reg_state *s) +{ + int i; + + s->r[3] = (uint64_t)&s->mem[0]; /* rdx */ + s->r[4] = (uint64_t)&gather_mem[ARRAY_LEN(gather_mem) / 2]; /* rsi */ + s->r[5] = (uint64_t)&s->mem[2]; /* rdi */ + s->flags = 2; + for (i = 0; i < 16; i++) { + s->ymm[i] = deadbeef; + } + s->ymm[13] = indexd; + s->ymm[14] = indexq; + for (i = 0; i < 4; i++) { + s->mem0[i] = deadbeef; + } +} + +int main(int argc, char *argv[]) +{ + int i; + + init_all(&initI); + init_intreg(&initI.ymm[0]); + init_intreg(&initI.ymm[9]); + init_intreg(&initI.ymm[10]); + init_intreg(&initI.ymm[11]); + init_intreg(&initI.ymm[12]); + init_intreg(&initI.mem0[1]); + printf("Int:\n"); + dump_regs(&initI); + + init_all(&initF16); + init_f16reg(&initF16.ymm[0]); + init_f16reg(&initF16.ymm[9]); + init_f16reg(&initF16.ymm[10]); + init_f16reg(&initF16.ymm[11]); + init_f16reg(&initF16.ymm[12]); + init_f16reg(&initF16.mem0[1]); + initF16.ff = 16; + printf("F16:\n"); + dump_regs(&initF16); + + init_all(&initF32); + init_f32reg(&initF32.ymm[0]); + init_f32reg(&initF32.ymm[9]); + init_f32reg(&initF32.ymm[10]); + init_f32reg(&initF32.ymm[11]); + init_f32reg(&initF32.ymm[12]); + init_f32reg(&initF32.mem0[1]); + initF32.ff = 32; + printf("F32:\n"); + dump_regs(&initF32); + + init_all(&initF64); + init_f64reg(&initF64.ymm[0]); + init_f64reg(&initF64.ymm[9]); + init_f64reg(&initF64.ymm[10]); + init_f64reg(&initF64.ymm[11]); + init_f64reg(&initF64.ymm[12]); + init_f64reg(&initF64.mem0[1]); + initF64.ff = 64; + printf("F64:\n"); + dump_regs(&initF64); + + for (i = 0; i < ARRAY_LEN(gather_mem); i++) { + init_intreg(&gather_mem[i]); + } + + if (argc > 1) { + int n = atoi(argv[1]); + run_test(&test_table[n]); + } else { + run_all(); + } + return 0; +} diff --git a/tests/tcg/i386/test-avx.py b/tests/tcg/i386/test-avx.py new file mode 100755 index 0000000000..6063fb2d11 --- /dev/null +++ b/tests/tcg/i386/test-avx.py @@ -0,0 +1,376 @@ +#! /usr/bin/env python3 + +# Generate test-avx.h from x86.csv + +import csv +import sys +from fnmatch import fnmatch + +archs = [ + "SSE", "SSE2", "SSE3", "SSSE3", "SSE4_1", "SSE4_2", + "AES", "AVX", "AVX2", "AES+AVX", "VAES+AVX", + "F16C", "FMA", "SHA", +] + +ignore = set(["FISTTP", + "LDMXCSR", "VLDMXCSR", "STMXCSR", "VSTMXCSR"]) + +imask = { + 'vBLENDPD': 0xff, + 'vBLENDPS': 0x0f, + 'CMP[PS][SD]': 0x07, + 'VCMP[PS][SD]': 0x1f, + 'vCVTPS2PH': 0x7, + 'vDPPD': 0x33, + 'vDPPS': 0xff, + 'vEXTRACTPS': 0x03, + 'vINSERTPS': 0xff, + 'MPSADBW': 0x7, + 'VMPSADBW': 0x3f, + 'vPALIGNR': 0x3f, + 'vPBLENDW': 0xff, + 'vPCMP[EI]STR*': 0x0f, + 'vPEXTRB': 0x0f, + 'vPEXTRW': 0x07, + 'vPEXTRD': 0x03, + 'vPEXTRQ': 0x01, + 'vPINSRB': 0x0f, + 'vPINSRW': 0x07, + 'vPINSRD': 0x03, + 'vPINSRQ': 0x01, + 'vPSHUF[DW]': 0xff, + 'vPSHUF[LH]W': 0xff, + 'vPS[LR][AL][WDQ]': 0x3f, + 'vPS[RL]LDQ': 0x1f, + 'vROUND[PS][SD]': 0x7, + 'SHA1RNDS4': 0x03, + 'vSHUFPD': 0x0f, + 'vSHUFPS': 0xff, + 'vAESKEYGENASSIST': 0xff, + 'VEXTRACT[FI]128': 0x01, + 'VINSERT[FI]128': 0x01, + 'VPBLENDD': 0xff, + 'VPERM2[FI]128': 0xbb, + 'VPERMPD': 0xff, + 'VPERMQ': 0xff, + 'VPERMILPS': 0xff, + 'VPERMILPD': 0x0f, + } + +def strip_comments(x): + for l in x: + if l != '' and l[0] != '#': + yield l + +def reg_w(w): + if w == 8: + return 'al' + elif w == 16: + return 'ax' + elif w == 32: + return 'eax' + elif w == 64: + return 'rax' + raise Exception("bad reg_w %d" % w) + +def mem_w(w): + if w == 8: + t = "BYTE" + elif w == 16: + t = "WORD" + elif w == 32: + t = "DWORD" + elif w == 64: + t = "QWORD" + elif w == 128: + t = "XMMWORD" + elif w == 256: + t = "YMMWORD" + else: + raise Exception() + + return t + " PTR 32[rdx]" + +class XMMArg(): + isxmm = True + def __init__(self, reg, mw): + if mw not in [0, 8, 16, 32, 64, 128, 256]: + raise Exception("Bad /m width: %s" % w) + self.reg = reg + self.mw = mw + self.ismem = mw != 0 + def regstr(self, n): + if n < 0: + return mem_w(self.mw) + else: + return "%smm%d" % (self.reg, n) + +class MMArg(): + isxmm = True + def __init__(self, mw): + if mw not in [0, 32, 64]: + raise Exception("Bad mem width: %s" % mw) + self.mw = mw + self.ismem = mw != 0 + def regstr(self, n): + return "mm%d" % (n & 7) + +def match(op, pattern): + if pattern[0] == 'v': + return fnmatch(op, pattern[1:]) or fnmatch(op, 'V'+pattern[1:]) + return fnmatch(op, pattern) + +class ArgVSIB(): + isxmm = True + ismem = False + def __init__(self, reg, w): + if w not in [32, 64]: + raise Exception("Bad vsib width: %s" % w) + self.w = w + self.reg = reg + def regstr(self, n): + reg = "%smm%d" % (self.reg, n >> 2) + return "[rsi + %s * %d]" % (reg, 1 << (n & 3)) + +class ArgImm8u(): + isxmm = False + ismem = False + def __init__(self, op): + for k, v in imask.items(): + if match(op, k): + self.mask = imask[k]; + return + raise Exception("Unknown immediate") + def vals(self): + mask = self.mask + yield 0 + n = 0 + while n != mask: + n += 1 + while (n & ~mask) != 0: + n += (n & ~mask) + yield n + +class ArgRM(): + isxmm = False + def __init__(self, rw, mw): + if rw not in [8, 16, 32, 64]: + raise Exception("Bad r/w width: %s" % w) + if mw not in [0, 8, 16, 32, 64]: + raise Exception("Bad r/w width: %s" % w) + self.rw = rw + self.mw = mw + self.ismem = mw != 0 + def regstr(self, n): + if n < 0: + return mem_w(self.mw) + else: + return reg_w(self.rw) + +class ArgMem(): + isxmm = False + ismem = True + def __init__(self, w): + if w not in [8, 16, 32, 64, 128, 256]: + raise Exception("Bad mem width: %s" % w) + self.w = w + def regstr(self, n): + return mem_w(self.w) + +class SkipInstruction(Exception): + pass + +def ArgGenerator(arg, op): + if arg[:3] == 'xmm' or arg[:3] == "ymm": + if "/" in arg: + r, m = arg.split('/') + if (m[0] != 'm'): + raise Exception("Expected /m: %s", arg) + return XMMArg(arg[0], int(m[1:])); + else: + return XMMArg(arg[0], 0); + elif arg[:2] == 'mm': + if "/" in arg: + r, m = arg.split('/') + if (m[0] != 'm'): + raise Exception("Expected /m: %s", arg) + return MMArg(int(m[1:])); + else: + return MMArg(0); + elif arg[:4] == 'imm8': + return ArgImm8u(op); + elif arg == '': + return None + elif arg[0] == 'r': + if '/m' in arg: + r, m = arg.split('/') + if (m[0] != 'm'): + raise Exception("Expected /m: %s", arg) + mw = int(m[1:]) + if r == 'r': + rw = mw + else: + rw = int(r[1:]) + return ArgRM(rw, mw) + + return ArgRM(int(arg[1:]), 0); + elif arg[0] == 'm': + return ArgMem(int(arg[1:])) + elif arg[:2] == 'vm': + return ArgVSIB(arg[-1], int(arg[2:-1])) + else: + raise Exception("Unrecognised arg: %s", arg) + +class InsnGenerator: + def __init__(self, op, args): + self.op = op + if op[-2:] in ["PH", "PS", "PD", "SS", "SD"]: + if op[-1] == 'H': + self.optype = 'F16' + elif op[-1] == 'S': + self.optype = 'F32' + else: + self.optype = 'F64' + else: + self.optype = 'I' + + try: + self.args = list(ArgGenerator(a, op) for a in args) + if not any((x.isxmm for x in self.args)): + raise SkipInstruction + if len(self.args) > 0 and self.args[-1] is None: + self.args = self.args[:-1] + except SkipInstruction: + raise + except Exception as e: + raise Exception("Bad arg %s: %s" % (op, e)) + + def gen(self): + regs = (10, 11, 12) + dest = 9 + + nreg = len(self.args) + if nreg == 0: + yield self.op + return + if isinstance(self.args[-1], ArgImm8u): + nreg -= 1 + immarg = self.args[-1] + else: + immarg = None + memarg = -1 + for n, arg in enumerate(self.args): + if arg.ismem: + memarg = n + + if (self.op.startswith("VGATHER") or self.op.startswith("VPGATHER")): + if "GATHERD" in self.op: + ireg = 13 << 2 + else: + ireg = 14 << 2 + regset = [ + (dest, ireg | 0, regs[0]), + (dest, ireg | 1, regs[0]), + (dest, ireg | 2, regs[0]), + (dest, ireg | 3, regs[0]), + ] + if memarg >= 0: + raise Exception("vsib with memory: %s" % self.op) + elif nreg == 1: + regset = [(regs[0],)] + if memarg == 0: + regset += [(-1,)] + elif nreg == 2: + regset = [ + (regs[0], regs[1]), + (regs[0], regs[0]), + ] + if memarg == 0: + regset += [(-1, regs[0])] + elif memarg == 1: + regset += [(dest, -1)] + elif nreg == 3: + regset = [ + (dest, regs[0], regs[1]), + (dest, regs[0], regs[0]), + (regs[0], regs[0], regs[1]), + (regs[0], regs[1], regs[0]), + (regs[0], regs[0], regs[0]), + ] + if memarg == 2: + regset += [ + (dest, regs[0], -1), + (regs[0], regs[0], -1), + ] + elif memarg > 0: + raise Exception("Memarg %d" % memarg) + elif nreg == 4: + regset = [ + (dest, regs[0], regs[1], regs[2]), + (dest, regs[0], regs[0], regs[1]), + (dest, regs[0], regs[1], regs[0]), + (dest, regs[1], regs[0], regs[0]), + (dest, regs[0], regs[0], regs[0]), + (regs[0], regs[0], regs[1], regs[2]), + (regs[0], regs[1], regs[0], regs[2]), + (regs[0], regs[1], regs[2], regs[0]), + (regs[0], regs[0], regs[0], regs[1]), + (regs[0], regs[0], regs[1], regs[0]), + (regs[0], regs[1], regs[0], regs[0]), + (regs[0], regs[0], regs[0], regs[0]), + ] + if memarg == 2: + regset += [ + (dest, regs[0], -1, regs[1]), + (dest, regs[0], -1, regs[0]), + (regs[0], regs[0], -1, regs[1]), + (regs[0], regs[1], -1, regs[0]), + (regs[0], regs[0], -1, regs[0]), + ] + elif memarg > 0: + raise Exception("Memarg4 %d" % memarg) + else: + raise Exception("Too many regs: %s(%d)" % (self.op, nreg)) + + for regv in regset: + argstr = [] + for i in range(nreg): + arg = self.args[i] + argstr.append(arg.regstr(regv[i])) + if immarg is None: + yield self.op + ' ' + ','.join(argstr) + else: + for immval in immarg.vals(): + yield self.op + ' ' + ','.join(argstr) + ',' + str(immval) + +def split0(s): + if s == '': + return [] + return s.split(',') + +def main(): + n = 0 + if len(sys.argv) != 3: + print("Usage: test-avx.py x86.csv test-avx.h") + exit(1) + csvfile = open(sys.argv[1], 'r', newline='') + with open(sys.argv[2], "w") as outf: + outf.write("// Generated by test-avx.py. Do not edit.\n") + for row in csv.reader(strip_comments(csvfile)): + insn = row[0].replace(',', '').split() + if insn[0] in ignore: + continue + cpuid = row[6] + if cpuid in archs: + try: + g = InsnGenerator(insn[0], insn[1:]) + for insn in g.gen(): + outf.write('TEST(%d, "%s", %s)\n' % (n, insn, g.optype)) + n += 1 + except SkipInstruction: + pass + outf.write("#undef TEST\n") + csvfile.close() + +if __name__ == "__main__": + main() diff --git a/tests/tcg/i386/test-flags.c b/tests/tcg/i386/test-flags.c new file mode 100644 index 0000000000..c379e29627 --- /dev/null +++ b/tests/tcg/i386/test-flags.c @@ -0,0 +1,37 @@ +#define _GNU_SOURCE +#include +#include +#include +#include + +volatile unsigned long flags; +volatile unsigned long flags_after; +int *addr; + +void sigsegv(int sig, siginfo_t *info, ucontext_t *uc) +{ + flags = uc->uc_mcontext.gregs[REG_EFL]; + mprotect(addr, 4096, PROT_READ|PROT_WRITE); +} + +int main() +{ + struct sigaction sa = { .sa_handler = (void *)sigsegv, .sa_flags = SA_SIGINFO }; + sigaction(SIGSEGV, &sa, NULL); + + /* fault in the page then protect it */ + addr = mmap (NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); + *addr = 0x1234; + mprotect(addr, 4096, PROT_READ); + + asm("# set flags to all ones \n" + "mov $-1, %%eax \n" + "movq addr, %%rdi \n" + "sahf \n" + "sub %%eax, (%%rdi) \n" + "pushf \n" + "pop flags_after(%%rip) \n" : : : "eax", "edi", "memory"); + + /* OF can have any value before the SUB instruction. */ + assert((flags & 0xff) == 0xd7 && (flags_after & 0x8ff) == 0x17); +} diff --git a/tests/tcg/i386/test-i386-adcox.c b/tests/tcg/i386/test-i386-adcox.c new file mode 100644 index 0000000000..16169efff8 --- /dev/null +++ b/tests/tcg/i386/test-i386-adcox.c @@ -0,0 +1,75 @@ +/* See if various BMI2 instructions give expected results */ +#include +#include +#include + +#define CC_C 1 +#define CC_O (1 << 11) + +#ifdef __x86_64__ +#define REG uint64_t +#else +#define REG uint32_t +#endif + +void test_adox_adcx(uint32_t in_c, uint32_t in_o, REG adcx_operand, REG adox_operand) +{ + REG flags; + REG out_adcx, out_adox; + + asm("pushf; pop %0" : "=r"(flags)); + flags &= ~(CC_C | CC_O); + flags |= (in_c ? CC_C : 0); + flags |= (in_o ? CC_O : 0); + + out_adcx = adcx_operand; + out_adox = adox_operand; + asm("push %0; popf;" + "adox %3, %2;" + "adcx %3, %1;" + "pushf; pop %0" + : "+r" (flags), "+r" (out_adcx), "+r" (out_adox) + : "r" ((REG)-1), "0" (flags), "1" (out_adcx), "2" (out_adox)); + + assert(out_adcx == in_c + adcx_operand - 1); + assert(out_adox == in_o + adox_operand - 1); + assert(!!(flags & CC_C) == (in_c || adcx_operand)); + assert(!!(flags & CC_O) == (in_o || adox_operand)); +} + +void test_adcx_adox(uint32_t in_c, uint32_t in_o, REG adcx_operand, REG adox_operand) +{ + REG flags; + REG out_adcx, out_adox; + + asm("pushf; pop %0" : "=r"(flags)); + flags &= ~(CC_C | CC_O); + flags |= (in_c ? CC_C : 0); + flags |= (in_o ? CC_O : 0); + + out_adcx = adcx_operand; + out_adox = adox_operand; + asm("push %0; popf;" + "adcx %3, %1;" + "adox %3, %2;" + "pushf; pop %0" + : "+r" (flags), "+r" (out_adcx), "+r" (out_adox) + : "r" ((REG)-1), "0" (flags), "1" (out_adcx), "2" (out_adox)); + + assert(out_adcx == in_c + adcx_operand - 1); + assert(out_adox == in_o + adox_operand - 1); + assert(!!(flags & CC_C) == (in_c || adcx_operand)); + assert(!!(flags & CC_O) == (in_o || adox_operand)); +} + +int main(int argc, char *argv[]) { + /* try all combinations of input CF, input OF, CF from op1+op2, OF from op2+op1 */ + int i; + for (i = 0; i <= 15; i++) { + printf("%d\n", i); + test_adcx_adox(!!(i & 1), !!(i & 2), !!(i & 4), !!(i & 8)); + test_adox_adcx(!!(i & 1), !!(i & 2), !!(i & 4), !!(i & 8)); + } + return 0; +} + diff --git a/tests/tcg/i386/test-i386-bmi2.c b/tests/tcg/i386/test-i386-bmi2.c index 935a4d2a73..0244df7987 100644 --- a/tests/tcg/i386/test-i386-bmi2.c +++ b/tests/tcg/i386/test-i386-bmi2.c @@ -1,41 +1,213 @@ /* See if various BMI2 instructions give expected results */ #include #include +#include + +#ifdef __x86_64 +typedef uint64_t reg_t; +#else +typedef uint32_t reg_t; +#endif + +#define insn1q(name, arg0) \ +static inline reg_t name##q(reg_t arg0) \ +{ \ + reg_t result64; \ + asm volatile (#name "q %1, %0" : "=r"(result64) : "rm"(arg0)); \ + return result64; \ +} + +#define insn1l(name, arg0) \ +static inline reg_t name##l(reg_t arg0) \ +{ \ + reg_t result32; \ + asm volatile (#name "l %k1, %k0" : "=r"(result32) : "rm"(arg0)); \ + return result32; \ +} + +#define insn2q(name, arg0, c0, arg1, c1) \ +static inline reg_t name##q(reg_t arg0, reg_t arg1) \ +{ \ + reg_t result64; \ + asm volatile (#name "q %2, %1, %0" : "=r"(result64) : c0(arg0), c1(arg1)); \ + return result64; \ +} + +#define insn2l(name, arg0, c0, arg1, c1) \ +static inline reg_t name##l(reg_t arg0, reg_t arg1) \ +{ \ + reg_t result32; \ + asm volatile (#name "l %k2, %k1, %k0" : "=r"(result32) : c0(arg0), c1(arg1)); \ + return result32; \ +} + +#ifdef __x86_64 +insn2q(pext, src, "r", mask, "rm") +insn2q(pdep, src, "r", mask, "rm") +insn2q(andn, clear, "rm", val, "r") +insn2q(bextr, range, "rm", val, "r") +insn2q(bzhi, pos, "rm", val, "r") +insn2q(rorx, val, "r", n, "i") +insn2q(sarx, val, "rm", n, "r") +insn2q(shlx, val, "rm", n, "r") +insn2q(shrx, val, "rm", n, "r") +insn1q(blsi, src) +insn1q(blsmsk, src) +insn1q(blsr, src) +#endif +insn2l(pext, src, "r", mask, "rm") +insn2l(pdep, src, "r", mask, "rm") +insn2l(andn, clear, "rm", val, "r") +insn2l(bextr, range, "rm", val, "r") +insn2l(bzhi, pos, "rm", val, "r") +insn2l(rorx, val, "r", n, "i") +insn2l(sarx, val, "rm", n, "r") +insn2l(shlx, val, "rm", n, "r") +insn2l(shrx, val, "rm", n, "r") +insn1l(blsi, src) +insn1l(blsmsk, src) +insn1l(blsr, src) int main(int argc, char *argv[]) { uint64_t ehlo = 0x202020204f4c4845ull; uint64_t mask = 0xa080800302020001ull; - uint32_t result32; + reg_t result; #ifdef __x86_64 - uint64_t result64; - /* 64 bits */ - asm volatile ("pextq %2, %1, %0" : "=r"(result64) : "r"(ehlo), "m"(mask)); - assert(result64 == 133); + result = andnq(mask, ehlo); + assert(result == 0x002020204d4c4844); - asm volatile ("pdepq %2, %1, %0" : "=r"(result64) : "r"(result64), "m"(mask)); - assert(result64 == (ehlo & mask)); + result = pextq(ehlo, mask); + assert(result == 133); - asm volatile ("pextq %2, %1, %0" : "=r"(result64) : "r"(-1ull), "m"(mask)); - assert(result64 == 511); /* mask has 9 bits set */ + result = pdepq(result, mask); + assert(result == (ehlo & mask)); - asm volatile ("pdepq %2, %1, %0" : "=r"(result64) : "r"(-1ull), "m"(mask)); - assert(result64 == mask); + result = pextq(-1ull, mask); + assert(result == 511); /* mask has 9 bits set */ + + result = pdepq(-1ull, mask); + assert(result == mask); + + result = bextrq(mask, 0x3f00); + assert(result == (mask & ~INT64_MIN)); + + result = bextrq(mask, 0x1038); + assert(result == 0xa0); + + result = bextrq(mask, 0x10f8); + assert(result == 0); + + result = bextrq(0xfedcba9876543210ull, 0x7f00); + assert(result == 0xfedcba9876543210ull); + + result = blsiq(0x30); + assert(result == 0x10); + + result = blsiq(0x30ull << 32); + assert(result == 0x10ull << 32); + + result = blsmskq(0x30); + assert(result == 0x1f); + + result = blsrq(0x30); + assert(result == 0x20); + + result = blsrq(0x30ull << 32); + assert(result == 0x20ull << 32); + + result = bzhiq(mask, 0x3f); + assert(result == (mask & ~INT64_MIN)); + + result = bzhiq(mask, 0x1f); + assert(result == (mask & ~(-1 << 30))); + + result = bzhiq(mask, 0x40); + assert(result == mask); + + result = rorxq(0x2132435465768798, 8); + assert(result == 0x9821324354657687); + + result = sarxq(0xffeeddccbbaa9988, 8); + assert(result == 0xffffeeddccbbaa99); + + result = sarxq(0x77eeddccbbaa9988, 8 | 64); + assert(result == 0x0077eeddccbbaa99); + + result = shrxq(0xffeeddccbbaa9988, 8); + assert(result == 0x00ffeeddccbbaa99); + + result = shrxq(0x77eeddccbbaa9988, 8 | 192); + assert(result == 0x0077eeddccbbaa99); + + result = shlxq(0xffeeddccbbaa9988, 8); + assert(result == 0xeeddccbbaa998800); #endif /* 32 bits */ - asm volatile ("pextl %2, %k1, %k0" : "=r"(result32) : "r"((uint32_t) ehlo), "m"(mask)); - assert(result32 == 5); + result = andnl(mask, ehlo); + assert(result == 0x04d4c4844); - asm volatile ("pdepl %2, %k1, %k0" : "=r"(result32) : "r"(result32), "m"(mask)); - assert(result32 == (uint32_t)(ehlo & mask)); + result = pextl((uint32_t) ehlo, mask); + assert(result == 5); - asm volatile ("pextl %2, %k1, %k0" : "=r"(result32) : "r"(-1ull), "m"(mask)); - assert(result32 == 7); /* mask has 3 bits set */ + result = pdepl(result, mask); + assert(result == (uint32_t)(ehlo & mask)); - asm volatile ("pdepl %2, %k1, %k0" : "=r"(result32) : "r"(-1ull), "m"(mask)); - assert(result32 == (uint32_t)mask); + result = pextl(-1u, mask); + assert(result == 7); /* mask has 3 bits set */ + + result = pdepl(-1u, mask); + assert(result == (uint32_t)mask); + + result = bextrl(mask, 0x1f00); + assert(result == (mask & ~INT32_MIN)); + + result = bextrl(ehlo, 0x1018); + assert(result == 0x4f); + + result = bextrl(mask, 0x1038); + assert(result == 0); + + result = bextrl((reg_t)0x8f635a775ad3b9b4ull, 0x3018); + assert(result == 0x5a); + + result = bextrl((reg_t)0xfedcba9876543210ull, 0x7f00); + assert(result == 0x76543210u); + + result = bextrl(-1, 0); + assert(result == 0); + + result = blsil(0xffff); + assert(result == 1); + + result = blsmskl(0x300); + assert(result == 0x1ff); + + result = blsrl(0xffc); + assert(result == 0xff8); + + result = bzhil(mask, 0xf); + assert(result == 1); + + result = rorxl(0x65768798, 8); + assert(result == 0x98657687); + + result = sarxl(0xffeeddcc, 8); + assert(result == 0xffffeedd); + + result = sarxl(0x77eeddcc, 8 | 32); + assert(result == 0x0077eedd); + + result = shrxl(0xffeeddcc, 8); + assert(result == 0x00ffeedd); + + result = shrxl(0x77eeddcc, 8 | 128); + assert(result == 0x0077eedd); + + result = shlxl(0xffeeddcc, 8); + assert(result == 0xeeddcc00); return 0; } diff --git a/tests/tcg/i386/test-i386-fp-exceptions.c b/tests/tcg/i386/test-i386-fp-exceptions.c index dfb7117c17..d445f13c33 100644 --- a/tests/tcg/i386/test-i386-fp-exceptions.c +++ b/tests/tcg/i386/test-i386-fp-exceptions.c @@ -423,35 +423,35 @@ int main(void) } __asm__ volatile ("fnclex"); - __asm__ volatile ("fistp %0" : "=m" (res_16) : "t" (1.5L) : "st"); + __asm__ volatile ("fistps %0" : "=m" (res_16) : "t" (1.5L) : "st"); __asm__ volatile ("fnstsw" : "=a" (sw)); if ((sw & EXC) != PE) { printf("FAIL: fistp inexact\n"); ret = 1; } __asm__ volatile ("fnclex"); - __asm__ volatile ("fistp %0" : "=m" (res_16) : "t" (32767.5L) : "st"); + __asm__ volatile ("fistps %0" : "=m" (res_16) : "t" (32767.5L) : "st"); __asm__ volatile ("fnstsw" : "=a" (sw)); if ((sw & EXC) != IE) { printf("FAIL: fistp 32767.5\n"); ret = 1; } __asm__ volatile ("fnclex"); - __asm__ volatile ("fistp %0" : "=m" (res_16) : "t" (-32768.51L) : "st"); + __asm__ volatile ("fistps %0" : "=m" (res_16) : "t" (-32768.51L) : "st"); __asm__ volatile ("fnstsw" : "=a" (sw)); if ((sw & EXC) != IE) { printf("FAIL: fistp -32768.51\n"); ret = 1; } __asm__ volatile ("fnclex"); - __asm__ volatile ("fistp %0" : "=m" (res_16) : "t" (ld_nan) : "st"); + __asm__ volatile ("fistps %0" : "=m" (res_16) : "t" (ld_nan) : "st"); __asm__ volatile ("fnstsw" : "=a" (sw)); if ((sw & EXC) != IE) { printf("FAIL: fistp nan\n"); ret = 1; } __asm__ volatile ("fnclex"); - __asm__ volatile ("fistp %0" : "=m" (res_16) : "t" (ld_invalid_1.ld) : + __asm__ volatile ("fistps %0" : "=m" (res_16) : "t" (ld_invalid_1.ld) : "st"); __asm__ volatile ("fnstsw" : "=a" (sw)); if ((sw & EXC) != IE) { @@ -538,49 +538,49 @@ int main(void) } __asm__ volatile ("fnclex"); - __asm__ volatile ("fisttp %0" : "=m" (res_16) : "t" (1.5L) : "st"); + __asm__ volatile ("fisttps %0" : "=m" (res_16) : "t" (1.5L) : "st"); __asm__ volatile ("fnstsw" : "=a" (sw)); if ((sw & EXC) != PE) { printf("FAIL: fisttp inexact\n"); ret = 1; } __asm__ volatile ("fnclex"); - __asm__ volatile ("fisttp %0" : "=m" (res_16) : "t" (32768.0L) : "st"); + __asm__ volatile ("fisttps %0" : "=m" (res_16) : "t" (32768.0L) : "st"); __asm__ volatile ("fnstsw" : "=a" (sw)); if ((sw & EXC) != IE) { printf("FAIL: fisttp 32768\n"); ret = 1; } __asm__ volatile ("fnclex"); - __asm__ volatile ("fisttp %0" : "=m" (res_16) : "t" (32768.5L) : "st"); + __asm__ volatile ("fisttps %0" : "=m" (res_16) : "t" (32768.5L) : "st"); __asm__ volatile ("fnstsw" : "=a" (sw)); if ((sw & EXC) != IE) { printf("FAIL: fisttp 32768.5\n"); ret = 1; } __asm__ volatile ("fnclex"); - __asm__ volatile ("fisttp %0" : "=m" (res_16) : "t" (-32769.0L) : "st"); + __asm__ volatile ("fisttps %0" : "=m" (res_16) : "t" (-32769.0L) : "st"); __asm__ volatile ("fnstsw" : "=a" (sw)); if ((sw & EXC) != IE) { printf("FAIL: fisttp -32769\n"); ret = 1; } __asm__ volatile ("fnclex"); - __asm__ volatile ("fisttp %0" : "=m" (res_16) : "t" (-32769.5L) : "st"); + __asm__ volatile ("fisttps %0" : "=m" (res_16) : "t" (-32769.5L) : "st"); __asm__ volatile ("fnstsw" : "=a" (sw)); if ((sw & EXC) != IE) { printf("FAIL: fisttp -32769.5\n"); ret = 1; } __asm__ volatile ("fnclex"); - __asm__ volatile ("fisttp %0" : "=m" (res_16) : "t" (ld_nan) : "st"); + __asm__ volatile ("fisttps %0" : "=m" (res_16) : "t" (ld_nan) : "st"); __asm__ volatile ("fnstsw" : "=a" (sw)); if ((sw & EXC) != IE) { printf("FAIL: fisttp nan\n"); ret = 1; } __asm__ volatile ("fnclex"); - __asm__ volatile ("fisttp %0" : "=m" (res_16) : "t" (ld_invalid_1.ld) : + __asm__ volatile ("fisttps %0" : "=m" (res_16) : "t" (ld_invalid_1.ld) : "st"); __asm__ volatile ("fnstsw" : "=a" (sw)); if ((sw & EXC) != IE) { diff --git a/tests/tcg/i386/test-i386.c b/tests/tcg/i386/test-i386.c index 18d5609665..864c4e620d 100644 --- a/tests/tcg/i386/test-i386.c +++ b/tests/tcg/i386/test-i386.c @@ -34,15 +34,8 @@ #endif //#define LINUX_VM86_IOPL_FIX //#define TEST_P4_FLAGS -#ifdef __SSE__ -#define TEST_SSE #define TEST_CMOV 1 #define TEST_FCOMI 1 -#else -#undef TEST_SSE -#define TEST_CMOV 1 -#define TEST_FCOMI 1 -#endif #if defined(__x86_64__) #define FMT64X "%016lx" @@ -866,7 +859,7 @@ void test_fcvt(double a) uint16_t val16; val16 = (fpuc & ~0x0c00) | (i << 10); asm volatile ("fldcw %0" : : "m" (val16)); - asm volatile ("fist %0" : "=m" (wa) : "t" (a)); + asm volatile ("fists %0" : "=m" (wa) : "t" (a)); asm volatile ("fistl %0" : "=m" (ia) : "t" (a)); asm volatile ("fistpll %0" : "=m" (lla) : "t" (a) : "st"); asm volatile ("frndint ; fstl %0" : "=m" (ra) : "t" (a)); @@ -1998,7 +1991,7 @@ uint8_t code[] = { 0xc3, /* ret */ }; -asm(".section \".data\"\n" +asm(".section \".data_x\",\"awx\"\n" "smc_code2:\n" "movl 4(%esp), %eax\n" "movl %eax, smc_patch_addr2 + 1\n" @@ -2104,568 +2097,6 @@ static void test_enter(void) TEST_ENTER("w", uint16_t, 31); } -#ifdef TEST_SSE - -typedef int __m64 __attribute__ ((vector_size(8))); -typedef float __m128 __attribute__ ((vector_size(16))); - -typedef union { - double d[2]; - float s[4]; - uint32_t l[4]; - uint64_t q[2]; - __m128 dq; -} XMMReg; - -static uint64_t __attribute__((aligned(16))) test_values[4][2] = { - { 0x456723c698694873, 0xdc515cff944a58ec }, - { 0x1f297ccd58bad7ab, 0x41f21efba9e3e146 }, - { 0x007c62c2085427f8, 0x231be9e8cde7438d }, - { 0x0f76255a085427f8, 0xc233e9e8c4c9439a }, -}; - -#define SSE_OP(op)\ -{\ - asm volatile (#op " %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\ - printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\ - #op,\ - a.q[1], a.q[0],\ - b.q[1], b.q[0],\ - r.q[1], r.q[0]);\ -} - -#define SSE_OP2(op)\ -{\ - int i;\ - for(i=0;i<2;i++) {\ - a.q[0] = test_values[2*i][0];\ - a.q[1] = test_values[2*i][1];\ - b.q[0] = test_values[2*i+1][0];\ - b.q[1] = test_values[2*i+1][1];\ - SSE_OP(op);\ - }\ -} - -#define MMX_OP2(op)\ -{\ - int i;\ - for(i=0;i<2;i++) {\ - a.q[0] = test_values[2*i][0];\ - b.q[0] = test_values[2*i+1][0];\ - asm volatile (#op " %2, %0" : "=y" (r.q[0]) : "0" (a.q[0]), "y" (b.q[0]));\ - printf("%-9s: a=" FMT64X " b=" FMT64X " r=" FMT64X "\n",\ - #op,\ - a.q[0],\ - b.q[0],\ - r.q[0]);\ - }\ - SSE_OP2(op);\ -} - -#define SHUF_OP(op, ib)\ -{\ - a.q[0] = test_values[0][0];\ - a.q[1] = test_values[0][1];\ - b.q[0] = test_values[1][0];\ - b.q[1] = test_values[1][1];\ - asm volatile (#op " $" #ib ", %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\ - printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\ - #op,\ - a.q[1], a.q[0],\ - b.q[1], b.q[0],\ - ib,\ - r.q[1], r.q[0]);\ -} - -#define PSHUF_OP(op, ib)\ -{\ - int i;\ - for(i=0;i<2;i++) {\ - a.q[0] = test_values[2*i][0];\ - a.q[1] = test_values[2*i][1];\ - asm volatile (#op " $" #ib ", %1, %0" : "=x" (r.dq) : "x" (a.dq));\ - printf("%-9s: a=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\ - #op,\ - a.q[1], a.q[0],\ - ib,\ - r.q[1], r.q[0]);\ - }\ -} - -#define SHIFT_IM(op, ib)\ -{\ - int i;\ - for(i=0;i<2;i++) {\ - a.q[0] = test_values[2*i][0];\ - a.q[1] = test_values[2*i][1];\ - asm volatile (#op " $" #ib ", %0" : "=x" (r.dq) : "0" (a.dq));\ - printf("%-9s: a=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\ - #op,\ - a.q[1], a.q[0],\ - ib,\ - r.q[1], r.q[0]);\ - }\ -} - -#define SHIFT_OP(op, ib)\ -{\ - int i;\ - SHIFT_IM(op, ib);\ - for(i=0;i<2;i++) {\ - a.q[0] = test_values[2*i][0];\ - a.q[1] = test_values[2*i][1];\ - b.q[0] = ib;\ - b.q[1] = 0;\ - asm volatile (#op " %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\ - printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\ - #op,\ - a.q[1], a.q[0],\ - b.q[1], b.q[0],\ - r.q[1], r.q[0]);\ - }\ -} - -#define MOVMSK(op)\ -{\ - int i, reg;\ - for(i=0;i<2;i++) {\ - a.q[0] = test_values[2*i][0];\ - a.q[1] = test_values[2*i][1];\ - asm volatile (#op " %1, %0" : "=r" (reg) : "x" (a.dq));\ - printf("%-9s: a=" FMT64X "" FMT64X " r=%08x\n",\ - #op,\ - a.q[1], a.q[0],\ - reg);\ - }\ -} - -#define SSE_OPS(a) \ -SSE_OP(a ## ps);\ -SSE_OP(a ## ss); - -#define SSE_OPD(a) \ -SSE_OP(a ## pd);\ -SSE_OP(a ## sd); - -#define SSE_COMI(op, field)\ -{\ - unsigned long eflags;\ - XMMReg a, b;\ - a.field[0] = a1;\ - b.field[0] = b1;\ - asm volatile (#op " %2, %1\n"\ - "pushf\n"\ - "pop %0\n"\ - : "=rm" (eflags)\ - : "x" (a.dq), "x" (b.dq));\ - printf("%-9s: a=%f b=%f cc=%04lx\n",\ - #op, a1, b1,\ - eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));\ -} - -void test_sse_comi(double a1, double b1) -{ - SSE_COMI(ucomiss, s); - SSE_COMI(ucomisd, d); - SSE_COMI(comiss, s); - SSE_COMI(comisd, d); -} - -#define CVT_OP_XMM(op)\ -{\ - asm volatile (#op " %1, %0" : "=x" (r.dq) : "x" (a.dq));\ - printf("%-9s: a=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\ - #op,\ - a.q[1], a.q[0],\ - r.q[1], r.q[0]);\ -} - -/* Force %xmm0 usage to avoid the case where both register index are 0 - to test instruction decoding more extensively */ -#define CVT_OP_XMM2MMX(op)\ -{\ - asm volatile (#op " %1, %0" : "=y" (r.q[0]) : "x" (a.dq) \ - : "%xmm0"); \ - asm volatile("emms\n"); \ - printf("%-9s: a=" FMT64X "" FMT64X " r=" FMT64X "\n",\ - #op,\ - a.q[1], a.q[0],\ - r.q[0]);\ -} - -#define CVT_OP_MMX2XMM(op)\ -{\ - asm volatile (#op " %1, %0" : "=x" (r.dq) : "y" (a.q[0]));\ - asm volatile("emms\n"); \ - printf("%-9s: a=" FMT64X " r=" FMT64X "" FMT64X "\n",\ - #op,\ - a.q[0],\ - r.q[1], r.q[0]);\ -} - -#define CVT_OP_REG2XMM(op)\ -{\ - asm volatile (#op " %1, %0" : "=x" (r.dq) : "r" (a.l[0]));\ - printf("%-9s: a=%08x r=" FMT64X "" FMT64X "\n",\ - #op,\ - a.l[0],\ - r.q[1], r.q[0]);\ -} - -#define CVT_OP_XMM2REG(op)\ -{\ - asm volatile (#op " %1, %0" : "=r" (r.l[0]) : "x" (a.dq));\ - printf("%-9s: a=" FMT64X "" FMT64X " r=%08x\n",\ - #op,\ - a.q[1], a.q[0],\ - r.l[0]);\ -} - -struct fpxstate { - uint16_t fpuc; - uint16_t fpus; - uint16_t fptag; - uint16_t fop; - uint32_t fpuip; - uint16_t cs_sel; - uint16_t dummy0; - uint32_t fpudp; - uint16_t ds_sel; - uint16_t dummy1; - uint32_t mxcsr; - uint32_t mxcsr_mask; - uint8_t fpregs1[8 * 16]; - uint8_t xmm_regs[8 * 16]; - uint8_t dummy2[224]; -}; - -static struct fpxstate fpx_state __attribute__((aligned(16))); -static struct fpxstate fpx_state2 __attribute__((aligned(16))); - -void test_fxsave(void) -{ - struct fpxstate *fp = &fpx_state; - struct fpxstate *fp2 = &fpx_state2; - int i, nb_xmm; - XMMReg a, b; - a.q[0] = test_values[0][0]; - a.q[1] = test_values[0][1]; - b.q[0] = test_values[1][0]; - b.q[1] = test_values[1][1]; - - asm("movdqa %2, %%xmm0\n" - "movdqa %3, %%xmm7\n" -#if defined(__x86_64__) - "movdqa %2, %%xmm15\n" -#endif - " fld1\n" - " fldpi\n" - " fldln2\n" - " fxsave %0\n" - " fxrstor %0\n" - " fxsave %1\n" - " fninit\n" - : "=m" (*(uint32_t *)fp2), "=m" (*(uint32_t *)fp) - : "m" (a), "m" (b)); - printf("fpuc=%04x\n", fp->fpuc); - printf("fpus=%04x\n", fp->fpus); - printf("fptag=%04x\n", fp->fptag); - for(i = 0; i < 3; i++) { - printf("ST%d: " FMT64X " %04x\n", - i, - *(uint64_t *)&fp->fpregs1[i * 16], - *(uint16_t *)&fp->fpregs1[i * 16 + 8]); - } - printf("mxcsr=%08x\n", fp->mxcsr & 0x1f80); -#if defined(__x86_64__) - nb_xmm = 16; -#else - nb_xmm = 8; -#endif - for(i = 0; i < nb_xmm; i++) { - printf("xmm%d: " FMT64X "" FMT64X "\n", - i, - *(uint64_t *)&fp->xmm_regs[i * 16], - *(uint64_t *)&fp->xmm_regs[i * 16 + 8]); - } -} - -void test_sse(void) -{ - XMMReg r, a, b; - int i; - - MMX_OP2(punpcklbw); - MMX_OP2(punpcklwd); - MMX_OP2(punpckldq); - MMX_OP2(packsswb); - MMX_OP2(pcmpgtb); - MMX_OP2(pcmpgtw); - MMX_OP2(pcmpgtd); - MMX_OP2(packuswb); - MMX_OP2(punpckhbw); - MMX_OP2(punpckhwd); - MMX_OP2(punpckhdq); - MMX_OP2(packssdw); - MMX_OP2(pcmpeqb); - MMX_OP2(pcmpeqw); - MMX_OP2(pcmpeqd); - - MMX_OP2(paddq); - MMX_OP2(pmullw); - MMX_OP2(psubusb); - MMX_OP2(psubusw); - MMX_OP2(pminub); - MMX_OP2(pand); - MMX_OP2(paddusb); - MMX_OP2(paddusw); - MMX_OP2(pmaxub); - MMX_OP2(pandn); - - MMX_OP2(pmulhuw); - MMX_OP2(pmulhw); - - MMX_OP2(psubsb); - MMX_OP2(psubsw); - MMX_OP2(pminsw); - MMX_OP2(por); - MMX_OP2(paddsb); - MMX_OP2(paddsw); - MMX_OP2(pmaxsw); - MMX_OP2(pxor); - MMX_OP2(pmuludq); - MMX_OP2(pmaddwd); - MMX_OP2(psadbw); - MMX_OP2(psubb); - MMX_OP2(psubw); - MMX_OP2(psubd); - MMX_OP2(psubq); - MMX_OP2(paddb); - MMX_OP2(paddw); - MMX_OP2(paddd); - - MMX_OP2(pavgb); - MMX_OP2(pavgw); - - asm volatile ("pinsrw $1, %1, %0" : "=y" (r.q[0]) : "r" (0x12345678)); - printf("%-9s: r=" FMT64X "\n", "pinsrw", r.q[0]); - - asm volatile ("pinsrw $5, %1, %0" : "=x" (r.dq) : "r" (0x12345678)); - printf("%-9s: r=" FMT64X "" FMT64X "\n", "pinsrw", r.q[1], r.q[0]); - - a.q[0] = test_values[0][0]; - a.q[1] = test_values[0][1]; - asm volatile ("pextrw $1, %1, %0" : "=r" (r.l[0]) : "y" (a.q[0])); - printf("%-9s: r=%08x\n", "pextrw", r.l[0]); - - asm volatile ("pextrw $5, %1, %0" : "=r" (r.l[0]) : "x" (a.dq)); - printf("%-9s: r=%08x\n", "pextrw", r.l[0]); - - asm volatile ("pmovmskb %1, %0" : "=r" (r.l[0]) : "y" (a.q[0])); - printf("%-9s: r=%08x\n", "pmovmskb", r.l[0]); - - asm volatile ("pmovmskb %1, %0" : "=r" (r.l[0]) : "x" (a.dq)); - printf("%-9s: r=%08x\n", "pmovmskb", r.l[0]); - - { - r.q[0] = -1; - r.q[1] = -1; - - a.q[0] = test_values[0][0]; - a.q[1] = test_values[0][1]; - b.q[0] = test_values[1][0]; - b.q[1] = test_values[1][1]; - asm volatile("maskmovq %1, %0" : - : "y" (a.q[0]), "y" (b.q[0]), "D" (&r) - : "memory"); - printf("%-9s: r=" FMT64X " a=" FMT64X " b=" FMT64X "\n", - "maskmov", - r.q[0], - a.q[0], - b.q[0]); - asm volatile("maskmovdqu %1, %0" : - : "x" (a.dq), "x" (b.dq), "D" (&r) - : "memory"); - printf("%-9s: r=" FMT64X "" FMT64X " a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X "\n", - "maskmov", - r.q[1], r.q[0], - a.q[1], a.q[0], - b.q[1], b.q[0]); - } - - asm volatile ("emms"); - - SSE_OP2(punpcklqdq); - SSE_OP2(punpckhqdq); - SSE_OP2(andps); - SSE_OP2(andpd); - SSE_OP2(andnps); - SSE_OP2(andnpd); - SSE_OP2(orps); - SSE_OP2(orpd); - SSE_OP2(xorps); - SSE_OP2(xorpd); - - SSE_OP2(unpcklps); - SSE_OP2(unpcklpd); - SSE_OP2(unpckhps); - SSE_OP2(unpckhpd); - - SHUF_OP(shufps, 0x78); - SHUF_OP(shufpd, 0x02); - - PSHUF_OP(pshufd, 0x78); - PSHUF_OP(pshuflw, 0x78); - PSHUF_OP(pshufhw, 0x78); - - SHIFT_OP(psrlw, 7); - SHIFT_OP(psrlw, 16); - SHIFT_OP(psraw, 7); - SHIFT_OP(psraw, 16); - SHIFT_OP(psllw, 7); - SHIFT_OP(psllw, 16); - - SHIFT_OP(psrld, 7); - SHIFT_OP(psrld, 32); - SHIFT_OP(psrad, 7); - SHIFT_OP(psrad, 32); - SHIFT_OP(pslld, 7); - SHIFT_OP(pslld, 32); - - SHIFT_OP(psrlq, 7); - SHIFT_OP(psrlq, 32); - SHIFT_OP(psllq, 7); - SHIFT_OP(psllq, 32); - - SHIFT_IM(psrldq, 16); - SHIFT_IM(psrldq, 7); - SHIFT_IM(pslldq, 16); - SHIFT_IM(pslldq, 7); - - MOVMSK(movmskps); - MOVMSK(movmskpd); - - /* FPU specific ops */ - - { - uint32_t mxcsr; - asm volatile("stmxcsr %0" : "=m" (mxcsr)); - printf("mxcsr=%08x\n", mxcsr & 0x1f80); - asm volatile("ldmxcsr %0" : : "m" (mxcsr)); - } - - test_sse_comi(2, -1); - test_sse_comi(2, 2); - test_sse_comi(2, 3); - test_sse_comi(2, q_nan.d); - test_sse_comi(q_nan.d, -1); - - for(i = 0; i < 2; i++) { - a.s[0] = 2.7; - a.s[1] = 3.4; - a.s[2] = 4; - a.s[3] = -6.3; - b.s[0] = 45.7; - b.s[1] = 353.4; - b.s[2] = 4; - b.s[3] = 56.3; - if (i == 1) { - a.s[0] = q_nan.d; - b.s[3] = q_nan.d; - } - - SSE_OPS(add); - SSE_OPS(mul); - SSE_OPS(sub); - SSE_OPS(min); - SSE_OPS(div); - SSE_OPS(max); - SSE_OPS(sqrt); - SSE_OPS(cmpeq); - SSE_OPS(cmplt); - SSE_OPS(cmple); - SSE_OPS(cmpunord); - SSE_OPS(cmpneq); - SSE_OPS(cmpnlt); - SSE_OPS(cmpnle); - SSE_OPS(cmpord); - - - a.d[0] = 2.7; - a.d[1] = -3.4; - b.d[0] = 45.7; - b.d[1] = -53.4; - if (i == 1) { - a.d[0] = q_nan.d; - b.d[1] = q_nan.d; - } - SSE_OPD(add); - SSE_OPD(mul); - SSE_OPD(sub); - SSE_OPD(min); - SSE_OPD(div); - SSE_OPD(max); - SSE_OPD(sqrt); - SSE_OPD(cmpeq); - SSE_OPD(cmplt); - SSE_OPD(cmple); - SSE_OPD(cmpunord); - SSE_OPD(cmpneq); - SSE_OPD(cmpnlt); - SSE_OPD(cmpnle); - SSE_OPD(cmpord); - } - - /* float to float/int */ - a.s[0] = 2.7; - a.s[1] = 3.4; - a.s[2] = 4; - a.s[3] = -6.3; - CVT_OP_XMM(cvtps2pd); - CVT_OP_XMM(cvtss2sd); - CVT_OP_XMM2MMX(cvtps2pi); - CVT_OP_XMM2MMX(cvttps2pi); - CVT_OP_XMM2REG(cvtss2si); - CVT_OP_XMM2REG(cvttss2si); - CVT_OP_XMM(cvtps2dq); - CVT_OP_XMM(cvttps2dq); - - a.d[0] = 2.6; - a.d[1] = -3.4; - CVT_OP_XMM(cvtpd2ps); - CVT_OP_XMM(cvtsd2ss); - CVT_OP_XMM2MMX(cvtpd2pi); - CVT_OP_XMM2MMX(cvttpd2pi); - CVT_OP_XMM2REG(cvtsd2si); - CVT_OP_XMM2REG(cvttsd2si); - CVT_OP_XMM(cvtpd2dq); - CVT_OP_XMM(cvttpd2dq); - - /* sse/mmx moves */ - CVT_OP_XMM2MMX(movdq2q); - CVT_OP_MMX2XMM(movq2dq); - - /* int to float */ - a.l[0] = -6; - a.l[1] = 2; - a.l[2] = 100; - a.l[3] = -60000; - CVT_OP_MMX2XMM(cvtpi2ps); - CVT_OP_MMX2XMM(cvtpi2pd); - CVT_OP_REG2XMM(cvtsi2ss); - CVT_OP_REG2XMM(cvtsi2sd); - CVT_OP_XMM(cvtdq2ps); - CVT_OP_XMM(cvtdq2pd); - - /* XXX: test PNI insns */ -#if 0 - SSE_OP2(movshdup); -#endif - asm volatile ("emms"); -} - -#endif - #define TEST_CONV_RAX(op)\ {\ unsigned long a, r;\ @@ -2756,9 +2187,5 @@ int main(int argc, char **argv) #endif test_enter(); test_conv(); -#ifdef TEST_SSE - test_sse(); - test_fxsave(); -#endif return 0; } diff --git a/tests/tcg/i386/test-mmx.c b/tests/tcg/i386/test-mmx.c new file mode 100644 index 0000000000..09e5d583ff --- /dev/null +++ b/tests/tcg/i386/test-mmx.c @@ -0,0 +1,315 @@ +#include +#include +#include +#include + +#ifndef TEST_FILE +#define TEST_FILE "test-mmx.h" +#endif +#ifndef EMMS +#define EMMS "emms" +#endif + +typedef void (*testfn)(void); + +typedef struct { + uint64_t q0, q1; +} __attribute__((aligned(16))) v2di; + +typedef struct { + uint64_t mm[8]; + v2di xmm[8]; + uint64_t r[16]; + uint64_t flags; + uint32_t ff; + uint64_t pad; + v2di mem[4]; + v2di mem0[4]; +} reg_state; + +typedef struct { + int n; + testfn fn; + const char *s; + reg_state *init; +} TestDef; + +reg_state initI; +reg_state initF32; +reg_state initF64; + +static void dump_mmx(int n, const uint64_t *r, int ff) +{ + if (ff == 32) { + float v[2]; + memcpy(v, r, sizeof(v)); + printf("MM%d = %016lx %8g %8g\n", n, *r, v[1], v[0]); + } else { + printf("MM%d = %016lx\n", n, *r); + } +} + +static void dump_xmm(const char *name, int n, const v2di *r, int ff) +{ + printf("%s%d = %016lx %016lx\n", + name, n, r->q1, r->q0); + if (ff == 32) { + float v[4]; + memcpy(v, r, sizeof(v)); + printf(" %8g %8g %8g %8g\n", + v[3], v[2], v[1], v[0]); + } +} + +static void dump_regs(reg_state *s, int ff) +{ + int i; + + for (i = 0; i < 8; i++) { + dump_mmx(i, &s->mm[i], ff); + } + for (i = 0; i < 4; i++) { + dump_xmm("mem", i, &s->mem0[i], 0); + } +} + +static void compare_state(const reg_state *a, const reg_state *b) +{ + int i; + for (i = 0; i < 8; i++) { + if (a->mm[i] != b->mm[i]) { + printf("MM%d = %016lx\n", i, b->mm[i]); + } + } + for (i = 0; i < 16; i++) { + if (a->r[i] != b->r[i]) { + printf("r%d = %016lx\n", i, b->r[i]); + } + } + for (i = 0; i < 8; i++) { + if (memcmp(&a->xmm[i], &b->xmm[i], 8)) { + dump_xmm("xmm", i, &b->xmm[i], a->ff); + } + } + for (i = 0; i < 4; i++) { + if (memcmp(&a->mem0[i], &a->mem[i], 16)) { + dump_xmm("mem", i, &a->mem[i], a->ff); + } + } + if (a->flags != b->flags) { + printf("FLAGS = %016lx\n", b->flags); + } +} + +#define LOADMM(r, o) "movq " #r ", " #o "[%0]\n\t" +#define LOADXMM(r, o) "movdqa " #r ", " #o "[%0]\n\t" +#define STOREMM(r, o) "movq " #o "[%1], " #r "\n\t" +#define STOREXMM(r, o) "movdqa " #o "[%1], " #r "\n\t" +#define MMREG(F) \ + F(mm0, 0x00) \ + F(mm1, 0x08) \ + F(mm2, 0x10) \ + F(mm3, 0x18) \ + F(mm4, 0x20) \ + F(mm5, 0x28) \ + F(mm6, 0x30) \ + F(mm7, 0x38) +#define XMMREG(F) \ + F(xmm0, 0x040) \ + F(xmm1, 0x050) \ + F(xmm2, 0x060) \ + F(xmm3, 0x070) \ + F(xmm4, 0x080) \ + F(xmm5, 0x090) \ + F(xmm6, 0x0a0) \ + F(xmm7, 0x0b0) +#define LOADREG(r, o) "mov " #r ", " #o "[rax]\n\t" +#define STOREREG(r, o) "mov " #o "[rax], " #r "\n\t" +#define REG(F) \ + F(rbx, 0xc8) \ + F(rcx, 0xd0) \ + F(rdx, 0xd8) \ + F(rsi, 0xe0) \ + F(rdi, 0xe8) \ + F(r8, 0x100) \ + F(r9, 0x108) \ + F(r10, 0x110) \ + F(r11, 0x118) \ + F(r12, 0x120) \ + F(r13, 0x128) \ + F(r14, 0x130) \ + F(r15, 0x138) \ + +static void run_test(const TestDef *t) +{ + reg_state result; + reg_state *init = t->init; + memcpy(init->mem, init->mem0, sizeof(init->mem)); + printf("%5d %s\n", t->n, t->s); + asm volatile( + MMREG(LOADMM) + XMMREG(LOADXMM) + "sub rsp, 128\n\t" + "push rax\n\t" + "push rbx\n\t" + "push rcx\n\t" + "push rdx\n\t" + "push %1\n\t" + "push %2\n\t" + "mov rax, %0\n\t" + "pushf\n\t" + "pop rbx\n\t" + "shr rbx, 8\n\t" + "shl rbx, 8\n\t" + "mov rcx, 0x140[rax]\n\t" + "and rcx, 0xff\n\t" + "or rbx, rcx\n\t" + "push rbx\n\t" + "popf\n\t" + REG(LOADREG) + "mov rax, 0xc0[rax]\n\t" + "call [rsp]\n\t" + "mov [rsp], rax\n\t" + "mov rax, 8[rsp]\n\t" + REG(STOREREG) + "mov rbx, [rsp]\n\t" + "mov 0xc0[rax], rbx\n\t" + "mov rbx, 0\n\t" + "mov 0xf0[rax], rbx\n\t" + "mov 0xf8[rax], rbx\n\t" + "pushf\n\t" + "pop rbx\n\t" + "and rbx, 0xff\n\t" + "mov 0x140[rax], rbx\n\t" + "add rsp, 16\n\t" + "pop rdx\n\t" + "pop rcx\n\t" + "pop rbx\n\t" + "pop rax\n\t" + "add rsp, 128\n\t" + MMREG(STOREMM) + EMMS "\n\t" + XMMREG(STOREXMM) + : : "r"(init), "r"(&result), "r"(t->fn) + : "memory", "cc", + "rsi", "rdi", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", + "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", + "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", + "xmm12", "xmm13", "xmm14", "xmm15" + ); + compare_state(init, &result); +} + +#define TEST(n, cmd, type) \ +static void __attribute__((naked)) test_##n(void) \ +{ \ + asm volatile(cmd); \ + asm volatile("ret"); \ +} +#include TEST_FILE + + +static const TestDef test_table[] = { +#define TEST(n, cmd, type) {n, test_##n, cmd, &init##type}, +#include TEST_FILE + {-1, NULL, "", NULL} +}; + +static void run_all(void) +{ + const TestDef *t; + for (t = test_table; t->fn; t++) { + run_test(t); + } +} + +#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0])) + +float val_f32[] = {2.0, -1.0, 4.8, 0.8, 3, -42.0, 5e6, 7.5, 8.3}; +uint64_t val_i64[] = { + 0x3d6b3b6a9e4118f2lu, 0x355ae76d2774d78clu, + 0xd851c54a56bf1f29lu, 0x4a84d1d50bf4c4fflu, + 0x5826475e2c5fd799lu, 0xfd32edc01243f5e9lu, +}; + +v2di deadbeef = {0xa5a5a5a5deadbeefull, 0xa5a5a5a5deadbeefull}; + +void init_f32reg(uint64_t *r) +{ + static int n; + float v[2]; + int i; + for (i = 0; i < 2; i++) { + v[i] = val_f32[n++]; + if (n == ARRAY_LEN(val_f32)) { + n = 0; + } + } + memcpy(r, v, sizeof(*r)); +} + +void init_intreg(uint64_t *r) +{ + static uint64_t mask; + static int n; + + *r = val_i64[n] ^ mask; + n++; + if (n == ARRAY_LEN(val_i64)) { + n = 0; + mask *= 0x104C11DB7; + } +} + +static void init_all(reg_state *s) +{ + int i; + + for (i = 0; i < 16; i++) { + init_intreg(&s->r[i]); + } + s->r[3] = (uint64_t)&s->mem[0]; /* rdx */ + s->r[5] = (uint64_t)&s->mem[2]; /* rdi */ + s->r[6] = 0; + s->r[7] = 0; + s->flags = 2; + for (i = 0; i < 8; i++) { + s->xmm[i] = deadbeef; + memcpy(&s->mm[i], &s->xmm[i], sizeof(s->mm[i])); + } + for (i = 0; i < 2; i++) { + s->mem0[i] = deadbeef; + } +} + +int main(int argc, char *argv[]) +{ + init_all(&initI); + init_intreg(&initI.mm[5]); + init_intreg(&initI.mm[6]); + init_intreg(&initI.mm[7]); + init_intreg(&initI.mem0[1].q0); + init_intreg(&initI.mem0[1].q1); + printf("Int:\n"); + dump_regs(&initI, 0); + + init_all(&initF32); + init_f32reg(&initF32.mm[5]); + init_f32reg(&initF32.mm[6]); + init_f32reg(&initF32.mm[7]); + init_f32reg(&initF32.mem0[1].q0); + init_f32reg(&initF32.mem0[1].q1); + initF32.ff = 32; + printf("F32:\n"); + dump_regs(&initF32, 32); + + if (argc > 1) { + int n = atoi(argv[1]); + run_test(&test_table[n]); + } else { + run_all(); + } + return 0; +} diff --git a/tests/tcg/i386/test-mmx.py b/tests/tcg/i386/test-mmx.py new file mode 100755 index 0000000000..392315e176 --- /dev/null +++ b/tests/tcg/i386/test-mmx.py @@ -0,0 +1,244 @@ +#! /usr/bin/env python3 + +# Generate test-avx.h from x86.csv + +import csv +import sys +from fnmatch import fnmatch + +ignore = set(["EMMS", "FEMMS", "FISTTP", + "LDMXCSR", "VLDMXCSR", "STMXCSR", "VSTMXCSR"]) + +imask = { + 'PALIGNR': 0x3f, + 'PEXTRB': 0x0f, + 'PEXTRW': 0x07, + 'PEXTRD': 0x03, + 'PEXTRQ': 0x01, + 'PINSRB': 0x0f, + 'PINSRW': 0x07, + 'PINSRD': 0x03, + 'PINSRQ': 0x01, + 'PSHUF[DW]': 0xff, + 'PSHUF[LH]W': 0xff, + 'PS[LR][AL][WDQ]': 0x3f, +} + +def strip_comments(x): + for l in x: + if l != '' and l[0] != '#': + yield l + +def reg_w(w): + if w == 8: + return 'al' + elif w == 16: + return 'ax' + elif w == 32: + return 'eax' + elif w == 64: + return 'rax' + raise Exception("bad reg_w %d" % w) + +def mem_w(w): + if w == 8: + t = "BYTE" + elif w == 16: + t = "WORD" + elif w == 32: + t = "DWORD" + elif w == 64: + t = "QWORD" + else: + raise Exception() + + return t + " PTR 32[rdx]" + +class MMArg(): + isxmm = True + + def __init__(self, mw): + if mw not in [0, 32, 64]: + raise Exception("Bad /m width: %s" % w) + self.mw = mw + self.ismem = mw != 0 + def regstr(self, n): + if n < 0: + return mem_w(self.mw) + else: + return "mm%d" % (n, ) + +def match(op, pattern): + return fnmatch(op, pattern) + +class ArgImm8u(): + isxmm = False + ismem = False + def __init__(self, op): + for k, v in imask.items(): + if match(op, k): + self.mask = imask[k]; + return + raise Exception("Unknown immediate") + def vals(self): + mask = self.mask + yield 0 + n = 0 + while n != mask: + n += 1 + while (n & ~mask) != 0: + n += (n & ~mask) + yield n + +class ArgRM(): + isxmm = False + def __init__(self, rw, mw): + if rw not in [8, 16, 32, 64]: + raise Exception("Bad r/w width: %s" % w) + if mw not in [0, 8, 16, 32, 64]: + raise Exception("Bad r/w width: %s" % w) + self.rw = rw + self.mw = mw + self.ismem = mw != 0 + def regstr(self, n): + if n < 0: + return mem_w(self.mw) + else: + return reg_w(self.rw) + +class ArgMem(): + isxmm = False + ismem = True + def __init__(self, w): + if w not in [8, 16, 32, 64, 128, 256]: + raise Exception("Bad mem width: %s" % w) + self.w = w + def regstr(self, n): + return mem_w(self.w) + +class SkipInstruction(Exception): + pass + +def ArgGenerator(arg, op): + if arg[:2] == 'mm': + if "/" in arg: + r, m = arg.split('/') + if (m[0] != 'm'): + raise Exception("Expected /m: %s", arg) + return MMArg(int(m[1:])); + else: + return MMArg(0); + elif arg[:4] == 'imm8': + return ArgImm8u(op); + elif arg[0] == 'r': + if '/m' in arg: + r, m = arg.split('/') + if (m[0] != 'm'): + raise Exception("Expected /m: %s", arg) + mw = int(m[1:]) + if r == 'r': + rw = mw + else: + rw = int(r[1:]) + return ArgRM(rw, mw) + + return ArgRM(int(arg[1:]), 0); + elif arg[0] == 'm': + return ArgMem(int(arg[1:])) + else: + raise SkipInstruction + +class InsnGenerator: + def __init__(self, op, args): + self.op = op + if op[0:2] == "PF": + self.optype = 'F32' + else: + self.optype = 'I' + + try: + self.args = list(ArgGenerator(a, op) for a in args) + if len(self.args) > 0 and self.args[-1] is None: + self.args = self.args[:-1] + except SkipInstruction: + raise + except Exception as e: + raise Exception("Bad arg %s: %s" % (op, e)) + + def gen(self): + regs = (5, 6, 7) + dest = 4 + + nreg = len(self.args) + if nreg == 0: + yield self.op + return + if isinstance(self.args[-1], ArgImm8u): + nreg -= 1 + immarg = self.args[-1] + else: + immarg = None + memarg = -1 + for n, arg in enumerate(self.args): + if arg.ismem: + memarg = n + + if nreg == 1: + regset = [(regs[0],)] + if memarg == 0: + regset += [(-1,)] + elif nreg == 2: + regset = [ + (regs[0], regs[1]), + (regs[0], regs[0]), + ] + if memarg == 0: + regset += [(-1, regs[0])] + elif memarg == 1: + regset += [(dest, -1)] + else: + raise Exception("Too many regs: %s(%d)" % (self.op, nreg)) + + for regv in regset: + argstr = [] + for i in range(nreg): + arg = self.args[i] + argstr.append(arg.regstr(regv[i])) + if immarg is None: + yield self.op + ' ' + ','.join(argstr) + else: + for immval in immarg.vals(): + yield self.op + ' ' + ','.join(argstr) + ',' + str(immval) + +def split0(s): + if s == '': + return [] + return s.split(',') + +def main(): + n = 0 + if len(sys.argv) <= 3: + print("Usage: test-mmx.py x86.csv test-mmx.h CPUID...") + exit(1) + csvfile = open(sys.argv[1], 'r', newline='') + archs = sys.argv[3:] + with open(sys.argv[2], "w") as outf: + outf.write("// Generated by test-mmx.py. Do not edit.\n") + for row in csv.reader(strip_comments(csvfile)): + insn = row[0].replace(',', '').split() + if insn[0] in ignore: + continue + cpuid = row[6] + if cpuid in archs: + try: + g = InsnGenerator(insn[0], insn[1:]) + for insn in g.gen(): + outf.write('TEST(%d, "%s", %s)\n' % (n, insn, g.optype)) + n += 1 + except SkipInstruction: + pass + outf.write("#undef TEST\n") + csvfile.close() + +if __name__ == "__main__": + main() diff --git a/tests/tcg/i386/x86.csv b/tests/tcg/i386/x86.csv new file mode 100644 index 0000000000..5c0f628e35 --- /dev/null +++ b/tests/tcg/i386/x86.csv @@ -0,0 +1,4658 @@ +# x86 instruction set description version 0.2x, 2018-05-08 +# +# https://golang.org/x/arch/x86 +# +# The latest version of the CSV file is +# available online at https://golang.org/s/x86.csv. +# +# This file contains a block of comment lines, each beginning with #, +# followed by entries in CSV format. All the # comments are at the top +# of the file, so a reader can skip past the comments and hand the +# rest of the file to a standard CSV reader. +# Each CSV line contains these fields: +# +# 1. The Intel manual instruction mnemonic. For example, "SHR r/m32, imm8". +# +# 2. The Go assembler instruction mnemonic. For example, "SHRL imm8, r/m32". +# +# 3. The GNU binutils instruction mnemonic. For example, "shrl imm8, r/m32". +# +# 4. The instruction encoding. For example, "C1 /4 ib". +# +# 5. The validity of the instruction in 32-bit (aka compatibility, legacy) mode. +# +# 6. The validity of the instruction in 64-bit mode. +# +# 7. The CPUID feature flags that signal support for the instruction. +# +# 8. Additional comma-separated tags containing hints about the instruction. +# +# 9. The read/write actions of the instruction on the arguments used in +# the Intel mnemonic. For example, "rw,r" to denote that "SHR r/m32, imm8" +# reads and writes its first argument but only reads its second argument. +# +# 10. Whether the opcode used in the Intel mnemonic has encoding forms +# distinguished only by operand size, like most arithmetic instructions. +# The string "Y" indicates yes, the string "" indicates no. +# +# 11. The data size of the operation in bits. In general this is the size corresponding +# to the Go and GNU assembler opcode suffix. +# Mnemonics (the opcode string) +# +# The instruction mnemonics are as used in the Intel manual, with a few exceptions. +# +# Mnemonics claiming general memory forms but that really require fixed addressing modes +# are omitted in favor of their equivalents with implicit arguments.. +# For example, "CMPS m16, m16" (really CMPS [SI], [DI]) is omitted in favor of "CMPSW". +# +# Instruction forms with an explicit REP, REPE, or REPNE prefix are also omitted. +# Encoders and decoders are expected to handle those prefixes separately. +# +# Perhaps most significantly, the argument syntaxes used in the mnemonic indicate +# exactly how to derive the argument from the instruction encoding, or vice versa. +# +# Immediate values: imm8, imm8u, imm16, imm16u, imm32, imm64. +# Immediates are signed by default; the u suffixes indicates an unsigned value. +# Immediates may have bitfield-like modifier that specifies how much bits +# are used. For example, imm8u:4 is encoded like 8bit immediate, +# but only 4bits are meaningful while the others are ignored or must be 0. +# +# Memory operands. The forms m, m128, m14/28byte, m16, m16&16, m16&32, m16&64, m16:16, m16:32, +# m16:64, m16int, m256, m2byte, m32, m32&32, m32fp, m32int, m512byte, m64, m64fp, m64int, +# m8, m80bcd, m80dec, m80fp, m94/108byte. These operands always correspond to the +# memory address specified by the r/m half of the modrm encoding. +# +# Integer registers. +# The forms r8, r16, r32, r64 indicate a register selected by the modrm reg encoding. +# The forms rmr16, rmr32, rmr64 indicate a register (never memory) selected by the modrm r/m encoding. +# The forms r/m8, r/m16, r/m32, and r/m64 indicate a register or memory selected by the modrm r/m encoding. +# Forms with two sizes, like r32/m16 also indicate a register or memory selected by the modrm r/m encodng, +# but the size for a register argument differs from the size of a memory argument. +# The forms r8V, r16V, r32V, r64V indicate a register selected by the VEX.vvvv bits. +# +# Multimedia registers. +# The forms mm1, xmm1, and ymm1 indicate a multimedia register selected by the +# modrm reg encoding. +# The forms mm2, xmm2, and ymm2 indicate a register (never memory) selected by +# the modrm r/m encoding. +# The forms mm2/m64, xmm2/m128, and so on indicate a register or memory +# selected by the modrm r/m encoding. +# The forms xmmV and ymmV indicate a register selected by the VEX.vvvv bits. +# The forms xmmI and ymmI indicate a register selected by the top four bits of an /is4 immediate byte. +# +# Bound registers. +# The form bnd1 indicates a bound register selected by the modrm reg encoding. +# The form bnd2 indicates a bound register (never memory) selected by the modrm r/m encoding. +# The forms bnd2/m64 and bnd2/m128 indicate a register or memorys selected by the modrm r/m encoding. +# TODO: Describe mib. +# +# One-of-a-kind operands: rel8, rel16, rel32, ptr16:16, ptr16:32, +# moffs8, moffs16, moffs32, moffs64, vm32x, vm32y, vm64x, and vm64y +# are all as in the Intel manual. +# +# Encodings +# +# The encodings are also as used in the Intel manual, with automated corrections. +# For example, the Intel manual sometimes omits the modrm /r indicator or other trailing bytes, +# and it also contains typographical errors. +# These problems are corrected so that the CSV data may be used to generate +# tools for processing x86 machine code. +# See https://golang.org/x/arch/x86/x86map for one such generator. +# +# Valid32 and Valid64 +# +# These columns hold validity abbreviations as defined in the Intel manual: +# V, I, N.E., N.P., N.S., or N.I. +# Tools processing the data are typically only concerned with whether the +# column is "V" (valid) or not. +# This data is also corrected compared to the manual. +# For example, the manual lists many instruction forms using REX bytes +# with an incorrect "V" in the Valid32 column. +# +# CPUID Feature Flags +# +# This column specifies CPUID feature flags that must be present in order +# to use the instruction. If multiple flags are required, +# they are listed separated by plus signs, as in PCLMULQDQ+AVX. +# The column can also list one of the values 486, Pentium, PentiumII, and P6, +# indicating that the instruction was introduced on that architecture version. +# +# Tags +# +# The tag column does not correspond to a traditional column in the Intel manual tables. +# Instead, it is itself a comma-separated list of tags or hints derived by analysis +# of the instruction set or the instruction encodings. +# +# The tags address16, address32, and address64 indicate that the instruction form +# applies when using the specified addressing size. It may therefore be necessary to use an +# address size prefix byte to access the instruction. +# If two address tags are listed, the instruction can be used with either of those +# address sizes. An instruction will never list all three address sizes. +# (In fact, today, no instruction lists two address sizes, but that may change.) +# +# The tags operand16, operand32, and operand64 indicate that the instruction form +# applies when using the specified operand size. It may therefore be necessary to use an +# operand size prefix byte to access the instruction. +# If two operand tags are listed, the instruction can be used with either of those +# operand sizes. An instruction will never list all three operand sizes. +# For some instructions, default64 is used instead of operand64, +# which specifies data promotion to 64-bit. +# For instructions with different possible data sizes, +# it also describes that default data size is 64-bit instead of 32-bit. +# Using refining prefix like 0x66 will lead to 32-bit operation (if supported). +# +# The tags modrm_regonly or modrm_memonly indicate that the modrm byte's +# r/m encoding must specify a register or memory, respectively. +# Especially in newer instructions, the modrm constraint may be the only way +# to distinguish two instruction forms. For example the MOVHLPS and MOVLPS +# instructions share the same encoding, except that the former requires the +# modrm byte's r/m to indicate a register, while the latter requires it to indicate memory. +# +# The tags pseudo and pseudo64 indicate that this instruction form is redundant +# with others listed in the table and should be ignored when generating disassembly +# or instruction scanning programs. The pseudo64 tag is reserved for the case where +# the manual lists an instruction twice, once with the optional 64-bit mode REX byte. +# Since most decoders will handle the REX byte separately, the form with the +# unnecessary REX is tagged pseudo64. +# +# The amd tag marks AMD-specific instructions. +# As an example, all instructions of SSE4a have such tag. +# +# The AVX512-specific tags: scaleX and bscaleX. +# scale1, scale2, scale4, scale8, scale16, scale32, scale64 specify +# the compressed displacement multiplier (scaling). +# For example, if displacement is 128 and scale32 is set, +# disp8 value should be calculated as 128/32. +# bscale4 and bscale8 have the same meaning, but are used +# when instruction uses embedded broadcast feature. +# If instruction does not have bscaleX tag, it does not support EVEX broadcasting. +# +# Related packages (can be a good source of additional documentation): +# x86csv - read and manipulate x86.csv +# x86spec - x86.csv generator +# x86map - x86asm table generator based on x86.csv +# x86avxgen - cmd/internal/obj/x86 optab generator based x86.csv +# All listed packages are located at golang.org/x/arch/x86/. +"PUSH imm32","-/PUSHL/PUSHQ imm32","-/pushl/pushq imm32","68 id","V","N.S.","","operand32","r","Y","" +"PUSH imm32","-/PUSHL/PUSHQ imm32","-/pushl/pushq imm32","68 id","N.S.","V","","default64","r","Y","" +"AAA","AAA","aaa","37","V","N.S.","","","","","" +"AAD","AAD","aad","D5 0A","V","I","","pseudo","","","" +"AAD imm8u","AAD imm8u","aad imm8u","D5 ib","V","N.S.","","","r","","" +"AAM","AAM","aam","D4 0A","V","I","","pseudo","","","" +"AAM imm8u","AAM imm8u","aam imm8u","D4 ib","V","N.S.","","","r","","" +"AAS","AAS","aas","3F","V","N.S.","","","","","" +"ADC AL, imm8","ADCB imm8, AL","adcb imm8, AL","14 ib","V","V","","","rw,r","Y","8" +"ADC r/m8, imm8","ADCB imm8, r/m8","adcb imm8, r/m8","80 /2 ib","V","V","","","rw,r","Y","8" +"ADC r/m8, imm8","ADCB imm8, r/m8","adcb imm8, r/m8","82 /2 ib","V","N.S.","","","rw,r","Y","8" +"ADC r/m8, imm8","ADCB imm8, r/m8","adcb imm8, r/m8","REX 80 /2 ib","N.E.","V","","pseudo64","rw,r","Y","8" +"ADC r8, r/m8","ADCB r/m8, r8","adcb r/m8, r8","12 /r","V","V","","","rw,r","Y","8" +"ADC r8, r/m8","ADCB r/m8, r8","adcb r/m8, r8","REX 12 /r","N.E.","V","","pseudo64","rw,r","Y","8" +"ADC r/m8, r8","ADCB r8, r/m8","adcb r8, r/m8","10 /r","V","V","","","rw,r","Y","8" +"ADC r/m8, r8","ADCB r8, r/m8","adcb r8, r/m8","REX 10 /r","N.E.","V","","pseudo64","rw,r","Y","8" +"ADC EAX, imm32","ADCL imm32, EAX","adcl imm32, EAX","15 id","V","V","","operand32","rw,r","Y","32" +"ADC r/m32, imm32","ADCL imm32, r/m32","adcl imm32, r/m32","81 /2 id","V","V","","operand32","rw,r","Y","32" +"ADC r/m32, imm8","ADCL imm8, r/m32","adcl imm8, r/m32","83 /2 ib","V","V","","operand32","rw,r","Y","32" +"ADC r32, r/m32","ADCL r/m32, r32","adcl r/m32, r32","13 /r","V","V","","operand32","rw,r","Y","32" +"ADC r/m32, r32","ADCL r32, r/m32","adcl r32, r/m32","11 /r","V","V","","operand32","rw,r","Y","32" +"ADC RAX, imm32","ADCQ imm32, RAX","adcq imm32, RAX","REX.W 15 id","N.S.","V","","","rw,r","Y","64" +"ADC r/m64, imm32","ADCQ imm32, r/m64","adcq imm32, r/m64","REX.W 81 /2 id","N.S.","V","","","rw,r","Y","64" +"ADC r/m64, imm8","ADCQ imm8, r/m64","adcq imm8, r/m64","REX.W 83 /2 ib","N.S.","V","","","rw,r","Y","64" +"ADC r64, r/m64","ADCQ r/m64, r64","adcq r/m64, r64","REX.W 13 /r","N.S.","V","","","rw,r","Y","64" +"ADC r/m64, r64","ADCQ r64, r/m64","adcq r64, r/m64","REX.W 11 /r","N.S.","V","","","rw,r","Y","64" +"ADC AX, imm16","ADCW imm16, AX","adcw imm16, AX","15 iw","V","V","","operand16","rw,r","Y","16" +"ADC r/m16, imm16","ADCW imm16, r/m16","adcw imm16, r/m16","81 /2 iw","V","V","","operand16","rw,r","Y","16" +"ADC r/m16, imm8","ADCW imm8, r/m16","adcw imm8, r/m16","83 /2 ib","V","V","","operand16","rw,r","Y","16" +"ADC r16, r/m16","ADCW r/m16, r16","adcw r/m16, r16","13 /r","V","V","","operand16","rw,r","Y","16" +"ADC r/m16, r16","ADCW r16, r/m16","adcw r16, r/m16","11 /r","V","V","","operand16","rw,r","Y","16" +"ADCX r32, r/m32","ADCXL r/m32, r32","adcxl r/m32, r32","66 0F 38 F6 /r","V","V","ADX","operand16,operand32","rw,r","Y","32" +"ADCX r64, r/m64","ADCXQ r/m64, r64","adcxq r/m64, r64","66 REX.W 0F 38 F6 /r","N.S.","V","ADX","","rw,r","Y","64" +"ADD AL, imm8","ADDB imm8, AL","addb imm8, AL","04 ib","V","V","","","rw,r","Y","8" +"ADD r/m8, imm8","ADDB imm8, r/m8","addb imm8, r/m8","80 /0 ib","V","V","","","rw,r","Y","8" +"ADD r/m8, imm8","ADDB imm8, r/m8","addb imm8, r/m8","82 /0 ib","V","N.S.","","","rw,r","Y","8" +"ADD r/m8, imm8","ADDB imm8, r/m8","addb imm8, r/m8","REX 80 /0 ib","N.E.","V","","pseudo64","rw,r","Y","8" +"ADD r8, r/m8","ADDB r/m8, r8","addb r/m8, r8","02 /r","V","V","","","rw,r","Y","8" +"ADD r8, r/m8","ADDB r/m8, r8","addb r/m8, r8","REX 02 /r","N.E.","V","","pseudo64","rw,r","Y","8" +"ADD r/m8, r8","ADDB r8, r/m8","addb r8, r/m8","00 /r","V","V","","","rw,r","Y","8" +"ADD r/m8, r8","ADDB r8, r/m8","addb r8, r/m8","REX 00 /r","N.E.","V","","pseudo64","rw,r","Y","8" +"ADD EAX, imm32","ADDL imm32, EAX","addl imm32, EAX","05 id","V","V","","operand32","rw,r","Y","32" +"ADD r/m32, imm32","ADDL imm32, r/m32","addl imm32, r/m32","81 /0 id","V","V","","operand32","rw,r","Y","32" +"ADD r/m32, imm8","ADDL imm8, r/m32","addl imm8, r/m32","83 /0 ib","V","V","","operand32","rw,r","Y","32" +"ADD r32, r/m32","ADDL r/m32, r32","addl r/m32, r32","03 /r","V","V","","operand32","rw,r","Y","32" +"ADD r/m32, r32","ADDL r32, r/m32","addl r32, r/m32","01 /r","V","V","","operand32","rw,r","Y","32" +"ADDPD xmm1, xmm2/m128","ADDPD xmm2/m128, xmm1","addpd xmm2/m128, xmm1","66 0F 58 /r","V","V","SSE2","","rw,r","","" +"ADDPS xmm1, xmm2/m128","ADDPS xmm2/m128, xmm1","addps xmm2/m128, xmm1","0F 58 /r","V","V","SSE","","rw,r","","" +"ADD RAX, imm32","ADDQ imm32, RAX","addq imm32, RAX","REX.W 05 id","N.S.","V","","","rw,r","Y","64" +"ADD r/m64, imm32","ADDQ imm32, r/m64","addq imm32, r/m64","REX.W 81 /0 id","N.S.","V","","","rw,r","Y","64" +"ADD r/m64, imm8","ADDQ imm8, r/m64","addq imm8, r/m64","REX.W 83 /0 ib","N.S.","V","","","rw,r","Y","64" +"ADD r64, r/m64","ADDQ r/m64, r64","addq r/m64, r64","REX.W 03 /r","N.S.","V","","","rw,r","Y","64" +"ADD r/m64, r64","ADDQ r64, r/m64","addq r64, r/m64","REX.W 01 /r","N.S.","V","","","rw,r","Y","64" +"ADDSD xmm1, xmm2/m64","ADDSD xmm2/m64, xmm1","addsd xmm2/m64, xmm1","F2 0F 58 /r","V","V","SSE2","","rw,r","","" +"ADDSS xmm1, xmm2/m32","ADDSS xmm2/m32, xmm1","addss xmm2/m32, xmm1","F3 0F 58 /r","V","V","SSE","","rw,r","","" +"ADDSUBPD xmm1, xmm2/m128","ADDSUBPD xmm2/m128, xmm1","addsubpd xmm2/m128, xmm1","66 0F D0 /r","V","V","SSE3","","rw,r","","" +"ADDSUBPS xmm1, xmm2/m128","ADDSUBPS xmm2/m128, xmm1","addsubps xmm2/m128, xmm1","F2 0F D0 /r","V","V","SSE3","","rw,r","","" +"ADD AX, imm16","ADDW imm16, AX","addw imm16, AX","05 iw","V","V","","operand16","rw,r","Y","16" +"ADD r/m16, imm16","ADDW imm16, r/m16","addw imm16, r/m16","81 /0 iw","V","V","","operand16","rw,r","Y","16" +"ADD r/m16, imm8","ADDW imm8, r/m16","addw imm8, r/m16","83 /0 ib","V","V","","operand16","rw,r","Y","16" +"ADD r16, r/m16","ADDW r/m16, r16","addw r/m16, r16","03 /r","V","V","","operand16","rw,r","Y","16" +"ADD r/m16, r16","ADDW r16, r/m16","addw r16, r/m16","01 /r","V","V","","operand16","rw,r","Y","16" +"ADOX r32, r/m32","ADOXL r/m32, r32","adoxl r/m32, r32","F3 0F 38 F6 /r","V","V","ADX","operand16,operand32","rw,r","Y","32" +"ADOX r64, r/m64","ADOXQ r/m64, r64","adoxq r/m64, r64","F3 REX.W 0F 38 F6 /r","N.S.","V","ADX","","rw,r","Y","64" +"AESDEC xmm1, xmm2/m128","AESDEC xmm2/m128, xmm1","aesdec xmm2/m128, xmm1","66 0F 38 DE /r","V","V","AES","","rw,r","","" +"AESDECLAST xmm1, xmm2/m128","AESDECLAST xmm2/m128, xmm1","aesdeclast xmm2/m128, xmm1","66 0F 38 DF /r","V","V","AES","","rw,r","","" +"AESENC xmm1, xmm2/m128","AESENC xmm2/m128, xmm1","aesenc xmm2/m128, xmm1","66 0F 38 DC /r","V","V","AES","","rw,r","","" +"AESENCLAST xmm1, xmm2/m128","AESENCLAST xmm2/m128, xmm1","aesenclast xmm2/m128, xmm1","66 0F 38 DD /r","V","V","AES","","rw,r","","" +"AESIMC xmm1, xmm2/m128","AESIMC xmm2/m128, xmm1","aesimc xmm2/m128, xmm1","66 0F 38 DB /r","V","V","AES","","w,r","","" +"AESKEYGENASSIST xmm1, xmm2/m128, imm8u","AESKEYGENASSIST imm8u, xmm2/m128, xmm1","aeskeygenassist imm8u, xmm2/m128, xmm1","66 0F 3A DF /r ib","V","V","AES","","w,r,r","","" +"AND AL, imm8","ANDB imm8, AL","andb imm8, AL","24 ib","V","V","","","rw,r","Y","8" +"AND r/m8, imm8","ANDB imm8, r/m8","andb imm8, r/m8","REX 80 /4 ib","N.E.","V","","pseudo64","rw,r","Y","8" +"AND r/m8, imm8u","ANDB imm8u, r/m8","andb imm8u, r/m8","80 /4 ib","V","V","","","rw,r","Y","8" +"AND r/m8, imm8u","ANDB imm8u, r/m8","andb imm8u, r/m8","82 /4 ib","V","N.S.","","","rw,r","Y","8" +"AND r8, r/m8","ANDB r/m8, r8","andb r/m8, r8","22 /r","V","V","","","rw,r","Y","8" +"AND r8, r/m8","ANDB r/m8, r8","andb r/m8, r8","REX 22 /r","N.E.","V","","pseudo64","rw,r","Y","8" +"AND r/m8, r8","ANDB r8, r/m8","andb r8, r/m8","20 /r","V","V","","","rw,r","Y","8" +"AND r/m8, r8","ANDB r8, r/m8","andb r8, r/m8","REX 20 /r","N.E.","V","","pseudo64","rw,r","Y","8" +"AND EAX, imm32","ANDL imm32, EAX","andl imm32, EAX","25 id","V","V","","operand32","rw,r","Y","32" +"AND r/m32, imm32","ANDL imm32, r/m32","andl imm32, r/m32","81 /4 id","V","V","","operand32","rw,r","Y","32" +"AND r/m32, imm8","ANDL imm8, r/m32","andl imm8, r/m32","83 /4 ib","V","V","","operand32","rw,r","Y","32" +"AND r32, r/m32","ANDL r/m32, r32","andl r/m32, r32","23 /r","V","V","","operand32","rw,r","Y","32" +"AND r/m32, r32","ANDL r32, r/m32","andl r32, r/m32","21 /r","V","V","","operand32","rw,r","Y","32" +"ANDN r32, r32V, r/m32","ANDNL r/m32, r32V, r32","andnl r/m32, r32V, r32","VEX.DDS.128.0F38.W0 F2 /r","V","V","BMI1","","rw,r,r","Y","32" +"ANDNPD xmm1, xmm2/m128","ANDNPD xmm2/m128, xmm1","andnpd xmm2/m128, xmm1","66 0F 55 /r","V","V","SSE2","","rw,r","","" +"ANDNPS xmm1, xmm2/m128","ANDNPS xmm2/m128, xmm1","andnps xmm2/m128, xmm1","0F 55 /r","V","V","SSE","","rw,r","","" +"ANDN r64, r64V, r/m64","ANDNQ r/m64, r64V, r64","andnq r/m64, r64V, r64","VEX.DDS.128.0F38.W1 F2 /r","N.S.","V","BMI1","","rw,r,r","Y","64" +"ANDPD xmm1, xmm2/m128","ANDPD xmm2/m128, xmm1","andpd xmm2/m128, xmm1","66 0F 54 /r","V","V","SSE2","","rw,r","","" +"ANDPS xmm1, xmm2/m128","ANDPS xmm2/m128, xmm1","andps xmm2/m128, xmm1","0F 54 /r","V","V","SSE","","rw,r","","" +"AND RAX, imm32","ANDQ imm32, RAX","andq imm32, RAX","REX.W 25 id","N.S.","V","","","rw,r","Y","64" +"AND r/m64, imm32","ANDQ imm32, r/m64","andq imm32, r/m64","REX.W 81 /4 id","N.S.","V","","","rw,r","Y","64" +"AND r/m64, imm8","ANDQ imm8, r/m64","andq imm8, r/m64","REX.W 83 /4 ib","N.S.","V","","","rw,r","Y","64" +"AND r64, r/m64","ANDQ r/m64, r64","andq r/m64, r64","REX.W 23 /r","N.S.","V","","","rw,r","Y","64" +"AND r/m64, r64","ANDQ r64, r/m64","andq r64, r/m64","REX.W 21 /r","N.S.","V","","","rw,r","Y","64" +"AND AX, imm16","ANDW imm16, AX","andw imm16, AX","25 iw","V","V","","operand16","rw,r","Y","16" +"AND r/m16, imm16","ANDW imm16, r/m16","andw imm16, r/m16","81 /4 iw","V","V","","operand16","rw,r","Y","16" +"AND r/m16, imm8","ANDW imm8, r/m16","andw imm8, r/m16","83 /4 ib","V","V","","operand16","rw,r","Y","16" +"AND r16, r/m16","ANDW r/m16, r16","andw r/m16, r16","23 /r","V","V","","operand16","rw,r","Y","16" +"AND r/m16, r16","ANDW r16, r/m16","andw r16, r/m16","21 /r","V","V","","operand16","rw,r","Y","16" +"ARPL r/m16, r16","ARPL r16, r/m16","arpl r16, r/m16","63 /r","V","N.S.","","","rw,r","","" +"BEXTR r32, r/m32, r32V","BEXTRL r32V, r/m32, r32","bextrl r32V, r/m32, r32","VEX.NDS.128.0F38.W0 F7 /r","V","V","BMI1","","w,r,r","Y","32" +"BEXTR r64, r/m64, r64V","BEXTRQ r64V, r/m64, r64","bextrq r64V, r/m64, r64","VEX.NDS.128.0F38.W1 F7 /r","N.S.","V","BMI1","","w,r,r","Y","64" +"BEXTR_XOP r32, r/m32, imm32u","BEXTR_XOPL imm32u, r/m32, r32","bextr_xopl imm32u, r/m32, r32","XOP.128.0A.WIG 10 /r","V","V","TBM","amd,operand16,operand32","w,r,r","Y","32" +"BEXTR_XOP r64, r/m64, imm32u","BEXTR_XOPQ imm32u, r/m64, r64","bextr_xopq imm32u, r/m64, r64","XOP.128.0A.WIG 10 /r","N.S.","V","TBM","amd,operand64","w,r,r","Y","64" +"BLCFILL r32V, r/m32","BLCFILLL r/m32, r32V","blcfill r/m32, r32V","XOP.NDD.128.09.WIG 01 /1","V","V","TBM","amd,operand16,operand32","w,r","Y","32" +"BLCFILL r64V, r/m64","BLCFILLQ r/m64, r64V","blcfill r/m64, r64V","XOP.NDD.128.09.W1 01 /1","N.S.","V","TBM","amd,operand64","w,r","Y","64" +"BLCIC r32V, r/m32","BLCICL r/m32, r32V","blcicl r/m32, r32V","XOP.NDD.128.09.WIG 01 /5","V","V","TBM","amd,operand16,operand32","w,r","Y","32" +"BLCIC r64V, r/m64","BLCICQ r/m64, r64V","blcicq r/m64, r64V","XOP.NDD.128.09.WIG 01 /5","N.S.","V","TBM","amd,operand64","w,r","Y","64" +"BLCI r32V, r/m32","BLCIL r/m32, r32V","blcil r/m32, r32V","XOP.NDD.128.09.WIG 02 /6","V","V","TBM","amd,operand16,operand32","w,r","Y","32" +"BLCI r64V, r/m64","BLCIQ r/m64, r64V","blciq r/m64, r64V","XOP.NDD.128.09.WIG 02 /6","N.S.","V","TBM","amd,operand64","w,r","Y","64" +"BLCMSK r32V, r/m32","BLCMSKL r/m32, r32V","blcmskl r/m32, r32V","XOP.NDD.128.09.WIG 02 /1","V","V","TBM","amd,operand16,operand32","w,r","Y","32" +"BLCMSK r64V, r/m64","BLCMSKQ r/m64, r64V","blcmskq r/m64, r64V","XOP.NDD.128.09.WIG 02 /1","N.S.","V","TBM","amd,operand64","w,r","Y","64" +"BLCS r32V, r/m32","BLCSL r/m32, r32V","blcsl r/m32, r32V","XOP.NDD.128.09.WIG 01 /3","V","V","TBM","amd,operand16,operand32","w,r","Y","32" +"BLCS r64V, r/m64","BLCSQ r/m64, r64V","blcsq r/m64, r64V","XOP.NDD.128.09.WIG 01 /3","N.S.","V","TBM","amd,operand64","w,r","Y","64" +"BLENDPD xmm1, xmm2/m128, imm8u","BLENDPD imm8u, xmm2/m128, xmm1","blendpd imm8u, xmm2/m128, xmm1","66 0F 3A 0D /r ib","V","V","SSE4_1","","rw,r,r","","" +"BLENDPS xmm1, xmm2/m128, imm8u","BLENDPS imm8u, xmm2/m128, xmm1","blendps imm8u, xmm2/m128, xmm1","66 0F 3A 0C /r ib","V","V","SSE4_1","","rw,r,r","","" +"BLENDVPD xmm1, xmm2/m128, ","BLENDVPD , xmm2/m128, xmm1","blendvpd , xmm2/m128, xmm1","66 0F 38 15 /r","V","V","SSE4_1","","rw,r,r","","" +"BLENDVPS xmm1, xmm2/m128, ","BLENDVPS , xmm2/m128, xmm1","blendvps , xmm2/m128, xmm1","66 0F 38 14 /r","V","V","SSE4_1","","rw,r,r","","" +"BLSFILL r32V, r/m32","BLSFILLL r/m32, r32V","blsfill r/m32, r32V","XOP.NDD.128.09.WIG 01 /2","V","V","TBM","amd,operand16,operand32","w,r","Y","32" +"BLSFILL r64V, r/m64","BLSFILLQ r/m64, r64V","blsfill r/m64, r64V","XOP.NDD.128.09.W1 01 /2","N.S.","V","TBM","amd,operand64","w,r","Y","64" +"BLSIC r32V, r/m32","BLSICL r/m32, r32V","blsicl r/m32, r32V","XOP.NDD.128.09.WIG 01 /6","V","V","TBM","amd,operand16,operand32","w,r","Y","32" +"BLSIC r64V, r/m64","BLSICQ r/m64, r64V","blsicq r/m64, r64V","XOP.NDD.128.09.WIG 01 /6","N.S.","V","TBM","amd,operand64","w,r","Y","64" +"BLSI r32V, r/m32","BLSIL r/m32, r32V","blsil r/m32, r32V","VEX.NDD.128.0F38.W0 F3 /3","V","V","BMI1","","w,r","Y","32" +"BLSI r64V, r/m64","BLSIQ r/m64, r64V","blsiq r/m64, r64V","VEX.NDD.128.0F38.W1 F3 /3","N.S.","V","BMI1","","w,r","Y","64" +"BLSMSK r32V, r/m32","BLSMSKL r/m32, r32V","blsmskl r/m32, r32V","VEX.NDD.128.0F38.W0 F3 /2","V","V","BMI1","","w,r","Y","32" +"BLSMSK r64V, r/m64","BLSMSKQ r/m64, r64V","blsmskq r/m64, r64V","VEX.NDD.128.0F38.W1 F3 /2","N.S.","V","BMI1","","w,r","Y","64" +"BLSR r32V, r/m32","BLSRL r/m32, r32V","blsrl r/m32, r32V","VEX.NDD.128.0F38.W0 F3 /1","V","V","BMI1","","w,r","Y","32" +"BLSR r64V, r/m64","BLSRQ r/m64, r64V","blsrq r/m64, r64V","VEX.NDD.128.0F38.W1 F3 /1","N.S.","V","BMI1","","w,r","Y","64" +"BNDCL bnd1, r/m32","BNDCL r/m32, bnd1","bndcl r/m32, bnd1","F3 0F 1A /r","V","N.S.","MPX","","r,r","","" +"BNDCL bnd1, r/m64","BNDCL r/m64, bnd1","bndcl r/m64, bnd1","F3 0F 1A /r","N.S.","V","MPX","","r,r","","" +"BNDCN bnd1, r/m32","BNDCN r/m32, bnd1","bndcn r/m32, bnd1","F2 0F 1B /r","V","N.S.","MPX","","r,r","","" +"BNDCN bnd1, r/m64","BNDCN r/m64, bnd1","bndcn r/m64, bnd1","F2 0F 1B /r","N.S.","V","MPX","","r,r","","" +"BNDCU bnd1, r/m32","BNDCU r/m32, bnd1","bndcu r/m32, bnd1","F2 0F 1A /r","V","N.S.","MPX","","r,r","","" +"BNDCU bnd1, r/m64","BNDCU r/m64, bnd1","bndcu r/m64, bnd1","F2 0F 1A /r","N.S.","V","MPX","","r,r","","" +"BNDLDX bnd1, mib","BNDLDX mib, bnd1","bndldx mib, bnd1","0F 1A /r","V","V","MPX","modrm_memonly","w,r","","" +"BNDMK bnd1, m32","BNDMK m32, bnd1","bndmk m32, bnd1","F3 0F 1B /r","V","N.S.","MPX","modrm_memonly","w,r","","" +"BNDMK bnd1, m64","BNDMK m64, bnd1","bndmk m64, bnd1","F3 0F 1B /r","N.S.","V","MPX","modrm_memonly","w,r","","" +"BNDMOV bnd2/m128, bnd1","BNDMOV bnd1, bnd2/m128","bndmov bnd1, bnd2/m128","66 0F 1B /r","N.S.","V","MPX","","w,r","","" +"BNDMOV bnd2/m64, bnd1","BNDMOV bnd1, bnd2/m64","bndmov bnd1, bnd2/m64","66 0F 1B /r","V","N.S.","MPX","","w,r","","" +"BNDMOV bnd1, bnd2/m128","BNDMOV bnd2/m128, bnd1","bndmov bnd2/m128, bnd1","66 0F 1A /r","N.S.","V","MPX","","w,r","","" +"BNDMOV bnd1, bnd2/m64","BNDMOV bnd2/m64, bnd1","bndmov bnd2/m64, bnd1","66 0F 1A /r","V","N.S.","MPX","","w,r","","" +"BNDSTX mib, bnd1","BNDSTX bnd1, mib","bndstx bnd1, mib","0F 1B /r","V","V","MPX","modrm_memonly","w,r","","" +"BOUND r32, m32&32","BOUNDL m32&32, r32","boundl r32, m32&32","62 /r","V","N.S.","","modrm_memonly,operand32","r,r","Y","32" +"BOUND r16, m16&16","BOUNDW m16&16, r16","boundw r16, m16&16","62 /r","V","N.S.","","modrm_memonly,operand16","r,r","Y","16" +"BSF r32, r/m32","BSFL r/m32, r32","bsfl r/m32, r32","0F BC /r","V","V","","operand32","rw,r","Y","32" +"BSF r32, r/m32","BSFL r/m32, r32","bsfl r/m32, r32","F3 0F BC /r","V","V","","operand32","rw,r","Y","32" +"BSF r64, r/m64","BSFQ r/m64, r64","bsfq r/m64, r64","F3 REX.W 0F BC /r","N.S.","V","","","rw,r","Y","64" +"BSF r64, r/m64","BSFQ r/m64, r64","bsfq r/m64, r64","REX.W 0F BC /r","N.S.","V","","","rw,r","Y","64" +"BSF r16, r/m16","BSFW r/m16, r16","bsfw r/m16, r16","0F BC /r","V","V","","operand16","rw,r","Y","16" +"BSF r16, r/m16","BSFW r/m16, r16","bsfw r/m16, r16","F3 0F BC /r","V","V","","operand16","rw,r","Y","16" +"BSR r32, r/m32","BSRL r/m32, r32","bsrl r/m32, r32","0F BD /r","V","V","","operand32","rw,r","Y","32" +"BSR r32, r/m32","BSRL r/m32, r32","bsrl r/m32, r32","F3 0F BD /r","V","V","","operand32","rw,r","Y","32" +"BSR r64, r/m64","BSRQ r/m64, r64","bsrq r/m64, r64","F3 REX.W 0F BD /r","N.S.","V","","","rw,r","Y","64" +"BSR r64, r/m64","BSRQ r/m64, r64","bsrq r/m64, r64","REX.W 0F BD /r","N.S.","V","","","rw,r","Y","64" +"BSR r16, r/m16","BSRW r/m16, r16","bsrw r/m16, r16","0F BD /r","V","V","","operand16","rw,r","Y","16" +"BSR r16, r/m16","BSRW r/m16, r16","bsrw r/m16, r16","F3 0F BD /r","V","V","","operand16","rw,r","Y","16" +"BSWAP r32op","BSWAPL r32op","bswap r32op","0F C8+rd","V","V","486","operand32","rw","Y","32" +"BSWAP r64op","BSWAPQ r64op","bswap r64op","REX.W 0F C8+ro","N.S.","V","486","","rw","Y","64" +"BSWAP r16op","BSWAPW r16op","bswap r16op","0F C8+rw","V","V","486","operand16","rw","Y","16" +"BTC r/m32, imm8u","BTCL imm8u, r/m32","btcl imm8u, r/m32","0F BA /7 ib","V","V","","operand32","rw,r","Y","32" +"BTC r/m32, r32","BTCL r32, r/m32","btcl r32, r/m32","0F BB /r","V","V","","operand32","rw,r","Y","32" +"BTC r/m64, imm8u","BTCQ imm8u, r/m64","btcq imm8u, r/m64","REX.W 0F BA /7 ib","N.S.","V","","","rw,r","Y","64" +"BTC r/m64, r64","BTCQ r64, r/m64","btcq r64, r/m64","REX.W 0F BB /r","N.S.","V","","","rw,r","Y","64" +"BTC r/m16, imm8u","BTCW imm8u, r/m16","btcw imm8u, r/m16","0F BA /7 ib","V","V","","operand16","rw,r","Y","16" +"BTC r/m16, r16","BTCW r16, r/m16","btcw r16, r/m16","0F BB /r","V","V","","operand16","rw,r","Y","16" +"BT r/m32, imm8u","BTL imm8u, r/m32","btl imm8u, r/m32","0F BA /4 ib","V","V","","operand32","r,r","Y","32" +"BT r/m32, r32","BTL r32, r/m32","btl r32, r/m32","0F A3 /r","V","V","","operand32","r,r","Y","32" +"BT r/m64, imm8u","BTQ imm8u, r/m64","btq imm8u, r/m64","REX.W 0F BA /4 ib","N.S.","V","","","r,r","Y","64" +"BT r/m64, r64","BTQ r64, r/m64","btq r64, r/m64","REX.W 0F A3 /r","N.S.","V","","","r,r","Y","64" +"BTR r/m32, imm8u","BTRL imm8u, r/m32","btrl imm8u, r/m32","0F BA /6 ib","V","V","","operand32","rw,r","Y","32" +"BTR r/m32, r32","BTRL r32, r/m32","btrl r32, r/m32","0F B3 /r","V","V","","operand32","rw,r","Y","32" +"BTR r/m64, imm8u","BTRQ imm8u, r/m64","btrq imm8u, r/m64","REX.W 0F BA /6 ib","N.S.","V","","","rw,r","Y","64" +"BTR r/m64, r64","BTRQ r64, r/m64","btrq r64, r/m64","REX.W 0F B3 /r","N.S.","V","","","rw,r","Y","64" +"BTR r/m16, imm8u","BTRW imm8u, r/m16","btrw imm8u, r/m16","0F BA /6 ib","V","V","","operand16","rw,r","Y","16" +"BTR r/m16, r16","BTRW r16, r/m16","btrw r16, r/m16","0F B3 /r","V","V","","operand16","rw,r","Y","16" +"BTS r/m32, imm8u","BTSL imm8u, r/m32","btsl imm8u, r/m32","0F BA /5 ib","V","V","","operand32","rw,r","Y","32" +"BTS r/m32, r32","BTSL r32, r/m32","btsl r32, r/m32","0F AB /r","V","V","","operand32","rw,r","Y","32" +"BTS r/m64, imm8u","BTSQ imm8u, r/m64","btsq imm8u, r/m64","REX.W 0F BA /5 ib","N.S.","V","","","rw,r","Y","64" +"BTS r/m64, r64","BTSQ r64, r/m64","btsq r64, r/m64","REX.W 0F AB /r","N.S.","V","","","rw,r","Y","64" +"BTS r/m16, imm8u","BTSW imm8u, r/m16","btsw imm8u, r/m16","0F BA /5 ib","V","V","","operand16","rw,r","Y","16" +"BTS r/m16, r16","BTSW r16, r/m16","btsw r16, r/m16","0F AB /r","V","V","","operand16","rw,r","Y","16" +"BT r/m16, imm8u","BTW imm8u, r/m16","btw imm8u, r/m16","0F BA /4 ib","V","V","","operand16","r,r","Y","16" +"BT r/m16, r16","BTW r16, r/m16","btw r16, r/m16","0F A3 /r","V","V","","operand16","r,r","Y","16" +"BZHI r32, r/m32, r32V","BZHIL r32V, r/m32, r32","bzhil r32V, r/m32, r32","VEX.NDS.128.0F38.W0 F5 /r","V","V","BMI2","","w,r,r","Y","32" +"BZHI r64, r/m64, r64V","BZHIQ r64V, r/m64, r64","bzhiq r64V, r/m64, r64","VEX.NDS.128.0F38.W1 F5 /r","N.S.","V","BMI2","","w,r,r","Y","64" +"CALL rel16","CALL rel16","call rel16","E8 cw","V","N.S.","","operand16","r","Y","" +"CALL rel32","CALL rel32","call rel32","E8 cd","V","N.S.","","operand32","r","Y","" +"CALL rel32","CALL rel32","call rel32","E8 cd","N.S.","V","","default64","r","Y","" +"CALL r/m32","CALLL* r/m32","calll* r/m32","FF /2","V","N.S.","","operand32","r","Y","32" +"CALL r/m64","CALLQ* r/m64","callq* r/m64","FF /2","N.S.","V","","default64","r","Y","64" +"CALL r/m16","CALLW* r/m16","callw* r/m16","FF /2","V","N.S.","","operand16","r","Y","16" +"CBW","CBW","cbtw","98","V","V","","operand16","","","" +"CDQ","CDQ","cltd","99","V","V","","operand32","","","" +"CDQE","CDQE","cltq","REX.W 98","N.S.","V","","","","","" +"CLAC","CLAC","clac","0F 01 CA","V","V","","","","","" +"CLC","CLC","clc","F8","V","V","","","","","" +"CLD","CLD","cld","FC","V","V","","","","","" +"CLFLUSH m8","CLFLUSH m8","clflush m8","0F AE /7","V","V","","modrm_memonly","r","","" +"CLFLUSHOPT m8","CLFLUSHOPT m8","clflushopt m8","66 0F AE /7","V","V","","modrm_memonly","r","","" +"CLGI","CLGI","clgi","0F 01 DD","V","V","SVM","amd","","","" +"CLI","CLI","cli","FA","V","V","","","","","" +"CLRSSBSY m64","CLRSSBSY m64","clrssbsy m64","F3 0F AE /6","V","V","CET","modrm_memonly","w","","" +"CLTS","CLTS","clts","0F 06","V","V","","","","","" +"CLWB m8","CLWB m8","clwb m8","66 0F AE /6","V","V","CLWB","modrm_memonly","r","","" +"CLZERO EAX","CLZEROL EAX","clzerol EAX","0F 01 FC","V","V","CLZERO","amd,modrm_regonly,operand32","r","Y","32" +"CLZERO RAX","CLZEROQ RAX","clzeroq RAX","REX.W 0F 01 FC","N.S.","V","CLZERO","amd,modrm_regonly","r","Y","64" +"CLZERO AX","CLZEROW AX","clzerow AX","0F 01 FC","V","V","CLZERO","amd,modrm_regonly,operand16","r","Y","16" +"CMC","CMC","cmc","F5","V","V","","","","","" +"CMOVC r16, r/m16","CMOVC r/m16, r16","cmovc r/m16, r16","0F 42 /r","V","V","","P6,operand16,pseudo","rw,r","","" +"CMOVC r32, r/m32","CMOVC r/m32, r32","cmovc r/m32, r32","0F 42 /r","V","V","","P6,operand32,pseudo","rw,r","","" +"CMOVC r64, r/m64","CMOVC r/m64, r64","cmovc r/m64, r64","REX.W 0F 42 /r","N.E.","V","","pseudo","rw,r","","" +"CMOVAE r32, r/m32","CMOVLCC r/m32, r32","cmovael r/m32, r32","0F 43 /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVB r32, r/m32","CMOVLCS r/m32, r32","cmovbl r/m32, r32","0F 42 /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVE r32, r/m32","CMOVLEQ r/m32, r32","cmovel r/m32, r32","0F 44 /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVGE r32, r/m32","CMOVLGE r/m32, r32","cmovgel r/m32, r32","0F 4D /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVG r32, r/m32","CMOVLGT r/m32, r32","cmovgl r/m32, r32","0F 4F /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVA r32, r/m32","CMOVLHI r/m32, r32","cmoval r/m32, r32","0F 47 /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVLE r32, r/m32","CMOVLLE r/m32, r32","cmovlel r/m32, r32","0F 4E /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVBE r32, r/m32","CMOVLLS r/m32, r32","cmovbel r/m32, r32","0F 46 /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVL r32, r/m32","CMOVLLT r/m32, r32","cmovll r/m32, r32","0F 4C /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVS r32, r/m32","CMOVLMI r/m32, r32","cmovsl r/m32, r32","0F 48 /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVNE r32, r/m32","CMOVLNE r/m32, r32","cmovnel r/m32, r32","0F 45 /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVNO r32, r/m32","CMOVLOC r/m32, r32","cmovnol r/m32, r32","0F 41 /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVO r32, r/m32","CMOVLOS r/m32, r32","cmovol r/m32, r32","0F 40 /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVNP r32, r/m32","CMOVLPC r/m32, r32","cmovnpl r/m32, r32","0F 4B /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVNS r32, r/m32","CMOVLPL r/m32, r32","cmovnsl r/m32, r32","0F 49 /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVP r32, r/m32","CMOVLPS r/m32, r32","cmovpl r/m32, r32","0F 4A /r","V","V","","P6,operand32","rw,r","Y","32" +"CMOVNA r16, r/m16","CMOVNA r/m16, r16","cmovna r/m16, r16","0F 46 /r","V","V","","P6,operand16,pseudo","rw,r","","" +"CMOVNA r32, r/m32","CMOVNA r/m32, r32","cmovna r/m32, r32","0F 46 /r","V","V","","P6,operand32,pseudo","rw,r","","" +"CMOVNA r64, r/m64","CMOVNA r/m64, r64","cmovna r/m64, r64","REX.W 0F 46 /r","N.E.","V","","pseudo","rw,r","","" +"CMOVNAE r16, r/m16","CMOVNAE r/m16, r16","cmovnae r/m16, r16","0F 42 /r","V","V","","P6,operand16,pseudo","rw,r","","" +"CMOVNAE r32, r/m32","CMOVNAE r/m32, r32","cmovnae r/m32, r32","0F 42 /r","V","V","","P6,operand32,pseudo","rw,r","","" +"CMOVNAE r64, r/m64","CMOVNAE r/m64, r64","cmovnae r/m64, r64","REX.W 0F 42 /r","N.E.","V","","pseudo","rw,r","","" +"CMOVNB r16, r/m16","CMOVNB r/m16, r16","cmovnb r/m16, r16","0F 43 /r","V","V","","P6,operand16,pseudo","rw,r","","" +"CMOVNB r32, r/m32","CMOVNB r/m32, r32","cmovnb r/m32, r32","0F 43 /r","V","V","","P6,operand32,pseudo","rw,r","","" +"CMOVNB r64, r/m64","CMOVNB r/m64, r64","cmovnb r/m64, r64","REX.W 0F 43 /r","N.E.","V","","pseudo","rw,r","","" +"CMOVNBE r16, r/m16","CMOVNBE r/m16, r16","cmovnbe r/m16, r16","0F 47 /r","V","V","","P6,operand16,pseudo","rw,r","","" +"CMOVNBE r32, r/m32","CMOVNBE r/m32, r32","cmovnbe r/m32, r32","0F 47 /r","V","V","","P6,operand32,pseudo","rw,r","","" +"CMOVNBE r64, r/m64","CMOVNBE r/m64, r64","cmovnbe r/m64, r64","REX.W 0F 47 /r","N.E.","V","","pseudo","rw,r","","" +"CMOVNC r16, r/m16","CMOVNC r/m16, r16","cmovnc r/m16, r16","0F 43 /r","V","V","","P6,operand16,pseudo","rw,r","","" +"CMOVNC r32, r/m32","CMOVNC r/m32, r32","cmovnc r/m32, r32","0F 43 /r","V","V","","P6,operand32,pseudo","rw,r","","" +"CMOVNC r64, r/m64","CMOVNC r/m64, r64","cmovnc r/m64, r64","REX.W 0F 43 /r","N.E.","V","","pseudo","rw,r","","" +"CMOVNG r16, r/m16","CMOVNG r/m16, r16","cmovng r/m16, r16","0F 4E /r","V","V","","P6,operand16,pseudo","rw,r","","" +"CMOVNG r32, r/m32","CMOVNG r/m32, r32","cmovng r/m32, r32","0F 4E /r","V","V","","P6,operand32,pseudo","rw,r","","" +"CMOVNG r64, r/m64","CMOVNG r/m64, r64","cmovng r/m64, r64","REX.W 0F 4E /r","N.E.","V","","pseudo","rw,r","","" +"CMOVNGE r16, r/m16","CMOVNGE r/m16, r16","cmovnge r/m16, r16","0F 4C /r","V","V","","P6,operand16,pseudo","rw,r","","" +"CMOVNGE r32, r/m32","CMOVNGE r/m32, r32","cmovnge r/m32, r32","0F 4C /r","V","V","","P6,operand32,pseudo","rw,r","","" +"CMOVNGE r64, r/m64","CMOVNGE r/m64, r64","cmovnge r/m64, r64","REX.W 0F 4C /r","N.E.","V","","pseudo","rw,r","","" +"CMOVNL r16, r/m16","CMOVNL r/m16, r16","cmovnl r/m16, r16","0F 4D /r","V","V","","P6,operand16,pseudo","rw,r","","" +"CMOVNL r32, r/m32","CMOVNL r/m32, r32","cmovnl r/m32, r32","0F 4D /r","V","V","","P6,operand32,pseudo","rw,r","","" +"CMOVNL r64, r/m64","CMOVNL r/m64, r64","cmovnl r/m64, r64","REX.W 0F 4D /r","N.E.","V","","pseudo","rw,r","","" +"CMOVNLE r16, r/m16","CMOVNLE r/m16, r16","cmovnle r/m16, r16","0F 4F /r","V","V","","P6,operand16,pseudo","rw,r","","" +"CMOVNLE r32, r/m32","CMOVNLE r/m32, r32","cmovnle r/m32, r32","0F 4F /r","V","V","","P6,operand32,pseudo","rw,r","","" +"CMOVNLE r64, r/m64","CMOVNLE r/m64, r64","cmovnle r/m64, r64","REX.W 0F 4F /r","N.E.","V","","pseudo","rw,r","","" +"CMOVNZ r16, r/m16","CMOVNZ r/m16, r16","cmovnz r/m16, r16","0F 45 /r","V","V","","P6,operand16,pseudo","rw,r","","" +"CMOVNZ r32, r/m32","CMOVNZ r/m32, r32","cmovnz r/m32, r32","0F 45 /r","V","V","","P6,operand32,pseudo","rw,r","","" +"CMOVNZ r64, r/m64","CMOVNZ r/m64, r64","cmovnz r/m64, r64","REX.W 0F 45 /r","N.E.","V","","pseudo","rw,r","","" +"CMOVPE r16, r/m16","CMOVPE r/m16, r16","cmovpe r/m16, r16","0F 4A /r","V","V","","P6,operand16,pseudo","rw,r","","" +"CMOVPE r32, r/m32","CMOVPE r/m32, r32","cmovpe r/m32, r32","0F 4A /r","V","V","","P6,operand32,pseudo","rw,r","","" +"CMOVPE r64, r/m64","CMOVPE r/m64, r64","cmovpe r/m64, r64","REX.W 0F 4A /r","N.E.","V","","pseudo","rw,r","","" +"CMOVPO r16, r/m16","CMOVPO r/m16, r16","cmovpo r/m16, r16","0F 4B /r","V","V","","P6,operand16,pseudo","rw,r","","" +"CMOVPO r32, r/m32","CMOVPO r/m32, r32","cmovpo r/m32, r32","0F 4B /r","V","V","","P6,operand32,pseudo","rw,r","","" +"CMOVPO r64, r/m64","CMOVPO r/m64, r64","cmovpo r/m64, r64","REX.W 0F 4B /r","N.E.","V","","pseudo","rw,r","","" +"CMOVAE r64, r/m64","CMOVQCC r/m64, r64","cmovaeq r/m64, r64","REX.W 0F 43 /r","N.S.","V","","","rw,r","Y","64" +"CMOVB r64, r/m64","CMOVQCS r/m64, r64","cmovbq r/m64, r64","REX.W 0F 42 /r","N.S.","V","","","rw,r","Y","64" +"CMOVE r64, r/m64","CMOVQEQ r/m64, r64","cmoveq r/m64, r64","REX.W 0F 44 /r","N.S.","V","","","rw,r","Y","64" +"CMOVGE r64, r/m64","CMOVQGE r/m64, r64","cmovgeq r/m64, r64","REX.W 0F 4D /r","N.S.","V","","","rw,r","Y","64" +"CMOVG r64, r/m64","CMOVQGT r/m64, r64","cmovgq r/m64, r64","REX.W 0F 4F /r","N.S.","V","","","rw,r","Y","64" +"CMOVA r64, r/m64","CMOVQHI r/m64, r64","cmovaq r/m64, r64","REX.W 0F 47 /r","N.S.","V","","","rw,r","Y","64" +"CMOVLE r64, r/m64","CMOVQLE r/m64, r64","cmovleq r/m64, r64","REX.W 0F 4E /r","N.S.","V","","","rw,r","Y","64" +"CMOVBE r64, r/m64","CMOVQLS r/m64, r64","cmovbeq r/m64, r64","REX.W 0F 46 /r","N.S.","V","","","rw,r","Y","64" +"CMOVL r64, r/m64","CMOVQLT r/m64, r64","cmovlq r/m64, r64","REX.W 0F 4C /r","N.S.","V","","","rw,r","Y","64" +"CMOVS r64, r/m64","CMOVQMI r/m64, r64","cmovsq r/m64, r64","REX.W 0F 48 /r","N.S.","V","","","rw,r","Y","64" +"CMOVNE r64, r/m64","CMOVQNE r/m64, r64","cmovneq r/m64, r64","REX.W 0F 45 /r","N.S.","V","","","rw,r","Y","64" +"CMOVNO r64, r/m64","CMOVQOC r/m64, r64","cmovnoq r/m64, r64","REX.W 0F 41 /r","N.S.","V","","","rw,r","Y","64" +"CMOVO r64, r/m64","CMOVQOS r/m64, r64","cmovoq r/m64, r64","REX.W 0F 40 /r","N.S.","V","","","rw,r","Y","64" +"CMOVNP r64, r/m64","CMOVQPC r/m64, r64","cmovnpq r/m64, r64","REX.W 0F 4B /r","N.S.","V","","","rw,r","Y","64" +"CMOVNS r64, r/m64","CMOVQPL r/m64, r64","cmovnsq r/m64, r64","REX.W 0F 49 /r","N.S.","V","","","rw,r","Y","64" +"CMOVP r64, r/m64","CMOVQPS r/m64, r64","cmovpq r/m64, r64","REX.W 0F 4A /r","N.S.","V","","","rw,r","Y","64" +"CMOVAE r16, r/m16","CMOVWCC r/m16, r16","cmovaew r/m16, r16","0F 43 /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVB r16, r/m16","CMOVWCS r/m16, r16","cmovbw r/m16, r16","0F 42 /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVE r16, r/m16","CMOVWEQ r/m16, r16","cmovew r/m16, r16","0F 44 /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVGE r16, r/m16","CMOVWGE r/m16, r16","cmovgew r/m16, r16","0F 4D /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVG r16, r/m16","CMOVWGT r/m16, r16","cmovgw r/m16, r16","0F 4F /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVA r16, r/m16","CMOVWHI r/m16, r16","cmovaw r/m16, r16","0F 47 /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVLE r16, r/m16","CMOVWLE r/m16, r16","cmovlew r/m16, r16","0F 4E /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVBE r16, r/m16","CMOVWLS r/m16, r16","cmovbew r/m16, r16","0F 46 /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVL r16, r/m16","CMOVWLT r/m16, r16","cmovlw r/m16, r16","0F 4C /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVS r16, r/m16","CMOVWMI r/m16, r16","cmovsw r/m16, r16","0F 48 /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVNE r16, r/m16","CMOVWNE r/m16, r16","cmovnew r/m16, r16","0F 45 /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVNO r16, r/m16","CMOVWOC r/m16, r16","cmovnow r/m16, r16","0F 41 /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVO r16, r/m16","CMOVWOS r/m16, r16","cmovow r/m16, r16","0F 40 /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVNP r16, r/m16","CMOVWPC r/m16, r16","cmovnpw r/m16, r16","0F 4B /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVNS r16, r/m16","CMOVWPL r/m16, r16","cmovnsw r/m16, r16","0F 49 /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVP r16, r/m16","CMOVWPS r/m16, r16","cmovpw r/m16, r16","0F 4A /r","V","V","","P6,operand16","rw,r","Y","16" +"CMOVZ r16, r/m16","CMOVZ r/m16, r16","cmovz r/m16, r16","0F 44 /r","V","V","","P6,operand16,pseudo","rw,r","","" +"CMOVZ r32, r/m32","CMOVZ r/m32, r32","cmovz r/m32, r32","0F 44 /r","V","V","","P6,operand32,pseudo","rw,r","","" +"CMOVZ r64, r/m64","CMOVZ r/m64, r64","cmovz r/m64, r64","REX.W 0F 44 /r","N.E.","V","","pseudo","rw,r","","" +"CMP AL, imm8","CMPB AL, imm8","cmpb imm8, AL","3C ib","V","V","","","r,r","Y","8" +"CMP r/m8, imm8","CMPB r/m8, imm8","cmpb imm8, r/m8","80 /7 ib","V","V","","","r,r","Y","8" +"CMP r/m8, imm8","CMPB r/m8, imm8","cmpb imm8, r/m8","82 /7 ib","V","N.S.","","","r,r","Y","8" +"CMP r/m8, imm8","CMPB r/m8, imm8","cmpb imm8, r/m8","REX 80 /7 ib","N.E.","V","","pseudo64","r,r","Y","8" +"CMP r/m8, r8","CMPB r/m8, r8","cmpb r8, r/m8","38 /r","V","V","","","r,r","Y","8" +"CMP r/m8, r8","CMPB r/m8, r8","cmpb r8, r/m8","REX 38 /r","N.E.","V","","pseudo64","r,r","Y","8" +"CMP r8, r/m8","CMPB r8, r/m8","cmpb r/m8, r8","3A /r","V","V","","","r,r","Y","8" +"CMP r8, r/m8","CMPB r8, r/m8","cmpb r/m8, r8","REX 3A /r","N.E.","V","","pseudo64","r,r","Y","8" +"CMP EAX, imm32","CMPL EAX, imm32","cmpl imm32, EAX","3D id","V","V","","operand32","r,r","Y","32" +"CMP r/m32, imm32","CMPL r/m32, imm32","cmpl imm32, r/m32","81 /7 id","V","V","","operand32","r,r","Y","32" +"CMP r/m32, imm8","CMPL r/m32, imm8","cmpl imm8, r/m32","83 /7 ib","V","V","","operand32","r,r","Y","32" +"CMP r/m32, r32","CMPL r/m32, r32","cmpl r32, r/m32","39 /r","V","V","","operand32","r,r","Y","32" +"CMP r32, r/m32","CMPL r32, r/m32","cmpl r/m32, r32","3B /r","V","V","","operand32","r,r","Y","32" +"CMPPD xmm1, xmm2/m128, imm8u","CMPPD imm8u, xmm1, xmm2/m128","cmppd imm8u, xmm2/m128, xmm1","66 0F C2 /r ib","V","V","SSE2","","rw,r,r","","" +"CMPPS xmm1, xmm2/m128, imm8u","CMPPS imm8u, xmm1, xmm2/m128","cmpps imm8u, xmm2/m128, xmm1","0F C2 /r ib","V","V","SSE","","rw,r,r","","" +"CMP RAX, imm32","CMPQ RAX, imm32","cmpq imm32, RAX","REX.W 3D id","N.S.","V","","","r,r","Y","64" +"CMP r/m64, imm32","CMPQ r/m64, imm32","cmpq imm32, r/m64","REX.W 81 /7 id","N.S.","V","","","r,r","Y","64" +"CMP r/m64, imm8","CMPQ r/m64, imm8","cmpq imm8, r/m64","REX.W 83 /7 ib","N.S.","V","","","r,r","Y","64" +"CMP r/m64, r64","CMPQ r/m64, r64","cmpq r64, r/m64","REX.W 39 /r","N.S.","V","","","r,r","Y","64" +"CMP r64, r/m64","CMPQ r64, r/m64","cmpq r/m64, r64","REX.W 3B /r","N.S.","V","","","r,r","Y","64" +"CMPSB","CMPSB","cmpsb","A6","V","V","","","","","" +"CMPSD xmm1, xmm2/m64, imm8u","CMPSD imm8u, xmm1, xmm2/m64","cmpsd imm8u, xmm2/m64, xmm1","F2 0F C2 /r ib","V","V","SSE2","","rw,r,r","","" +"CMPSD","CMPSL","cmpsl","A7","V","V","","operand32","","","" +"CMPSQ","CMPSQ","cmpsq","REX.W A7","N.S.","V","","","","","" +"CMPSS xmm1, xmm2/m32, imm8u","CMPSS imm8u, xmm1, xmm2/m32","cmpss imm8u, xmm2/m32, xmm1","F3 0F C2 /r ib","V","V","SSE","","rw,r,r","","" +"CMPSW","CMPSW","cmpsw","A7","V","V","","operand16","","","" +"CMP AX, imm16","CMPW AX, imm16","cmpw imm16, AX","3D iw","V","V","","operand16","r,r","Y","16" +"CMP r/m16, imm16","CMPW r/m16, imm16","cmpw imm16, r/m16","81 /7 iw","V","V","","operand16","r,r","Y","16" +"CMP r/m16, imm8","CMPW r/m16, imm8","cmpw imm8, r/m16","83 /7 ib","V","V","","operand16","r,r","Y","16" +"CMP r/m16, r16","CMPW r/m16, r16","cmpw r16, r/m16","39 /r","V","V","","operand16","r,r","Y","16" +"CMP r16, r/m16","CMPW r16, r/m16","cmpw r/m16, r16","3B /r","V","V","","operand16","r,r","Y","16" +"CMPXCHG16B m128","CMPXCHG16B m128","cmpxchg16b m128","REX.W 0F C7 /1","N.S.","V","","modrm_memonly","rw","","" +"CMPXCHG8B m64","CMPXCHG8B m64","cmpxchg8b m64","0F C7 /1","V","V","Pentium","modrm_memonly,operand16,operand32","rw","","" +"CMPXCHG r/m8, r8","CMPXCHGB r8, r/m8","cmpxchgb r8, r/m8","0F B0 /r","V","V","486","","rw,r","Y","8" +"CMPXCHG r/m8, r8","CMPXCHGB r8, r/m8","cmpxchgb r8, r/m8","REX 0F B0 /r","N.E.","V","","pseudo64","rw,r","Y","8" +"CMPXCHG r/m32, r32","CMPXCHGL r32, r/m32","cmpxchgl r32, r/m32","0F B1 /r","V","V","486","operand32","rw,r","Y","32" +"CMPXCHG r/m64, r64","CMPXCHGQ r64, r/m64","cmpxchgq r64, r/m64","REX.W 0F B1 /r","N.S.","V","486","","rw,r","Y","64" +"CMPXCHG r/m16, r16","CMPXCHGW r16, r/m16","cmpxchgw r16, r/m16","0F B1 /r","V","V","486","operand16","rw,r","Y","16" +"COMISD xmm1, xmm2/m64","COMISD xmm2/m64, xmm1","comisd xmm2/m64, xmm1","66 0F 2F /r","V","V","SSE2","","r,r","","" +"COMISS xmm1, xmm2/m32","COMISS xmm2/m32, xmm1","comiss xmm2/m32, xmm1","0F 2F /r","V","V","SSE","","r,r","","" +"CPUID","CPUID","cpuid","0F A2","V","V","486","","","","" +"CQO","CQO","cqto","REX.W 99","N.S.","V","","","","","" +"CRC32 r32, r/m8","CRC32B r/m8, r32","crc32b r/m8, r32","F2 0F 38 F0 /r","V","V","SSE4_2","operand16,operand32","rw,r","Y","8" +"CRC32 r32, r/m8","CRC32B r/m8, r32","crc32b r/m8, r32","F2 REX 0F 38 F0 /r","N.E.","V","","pseudo64","rw,r","Y","8" +"CRC32 r64, r/m8","CRC32B r/m8, r64","crc32b r/m8, r64","F2 REX.W 0F 38 F0 /r","N.S.","V","SSE4_2","","rw,r","Y","8" +"CRC32 r32, r/m32","CRC32L r/m32, r32","crc32l r/m32, r32","F2 0F 38 F1 /r","V","V","SSE4_2","operand32","rw,r","Y","32" +"CRC32 r64, r/m64","CRC32Q r/m64, r64","crc32q r/m64, r64","F2 REX.W 0F 38 F1 /r","N.S.","V","SSE4_2","","rw,r","Y","64" +"CRC32 r32, r/m16","CRC32W r/m16, r32","crc32w r/m16, r32","F2 0F 38 F1 /r","V","V","SSE4_2","operand16","rw,r","Y","16" +"CVTPD2PI mm1, xmm2/m128","CVTPD2PI xmm2/m128, mm1","cvtpd2pi xmm2/m128, mm1","66 0F 2D /r","V","V","SSE2","","w,r","","" +"CVTPD2DQ xmm1, xmm2/m128","CVTPD2PL xmm2/m128, xmm1","cvtpd2dq xmm2/m128, xmm1","F2 0F E6 /r","V","V","SSE2","","w,r","","" +"CVTPD2PS xmm1, xmm2/m128","CVTPD2PS xmm2/m128, xmm1","cvtpd2ps xmm2/m128, xmm1","66 0F 5A /r","V","V","SSE2","","w,r","","" +"CVTPI2PD xmm1, mm2/m64","CVTPI2PD mm2/m64, xmm1","cvtpi2pd mm2/m64, xmm1","66 0F 2A /r","V","V","SSE2","","w,r","","" +"CVTPI2PS xmm1, mm2/m64","CVTPI2PS mm2/m64, xmm1","cvtpi2ps mm2/m64, xmm1","0F 2A /r","V","V","SSE","","w,r","","" +"CVTDQ2PD xmm1, xmm2/m64","CVTPL2PD xmm2/m64, xmm1","cvtdq2pd xmm2/m64, xmm1","F3 0F E6 /r","V","V","SSE2","","w,r","","" +"CVTDQ2PS xmm1, xmm2/m128","CVTPL2PS xmm2/m128, xmm1","cvtdq2ps xmm2/m128, xmm1","0F 5B /r","V","V","SSE2","","w,r","","" +"CVTPS2PD xmm1, xmm2/m64","CVTPS2PD xmm2/m64, xmm1","cvtps2pd xmm2/m64, xmm1","0F 5A /r","V","V","SSE2","","w,r","","" +"CVTPS2PI mm1, xmm2/m64","CVTPS2PI xmm2/m64, mm1","cvtps2pi xmm2/m64, mm1","0F 2D /r","V","V","SSE","","w,r","","" +"CVTPS2DQ xmm1, xmm2/m128","CVTPS2PL xmm2/m128, xmm1","cvtps2dq xmm2/m128, xmm1","66 0F 5B /r","V","V","SSE2","","w,r","","" +"CVTSD2SI r32, xmm2/m64","CVTSD2SL xmm2/m64, r32","cvtsd2si xmm2/m64, r32","F2 0F 2D /r","V","V","SSE2","operand16,operand32","w,r","Y","32" +"CVTSD2SI r64, xmm2/m64","CVTSD2SL xmm2/m64, r64","cvtsd2siq xmm2/m64, r64","F2 REX.W 0F 2D /r","N.S.","V","SSE2","","w,r","Y","64" +"CVTSD2SS xmm1, xmm2/m64","CVTSD2SS xmm2/m64, xmm1","cvtsd2ss xmm2/m64, xmm1","F2 0F 5A /r","V","V","SSE2","","w,r","","" +"CVTSI2SD xmm1, r/m32","CVTSL2SD r/m32, xmm1","cvtsi2sdl r/m32, xmm1","F2 0F 2A /r","V","V","SSE2","operand16,operand32","w,r","Y","32" +"CVTSI2SS xmm1, r/m32","CVTSL2SS r/m32, xmm1","cvtsi2ssl r/m32, xmm1","F3 0F 2A /r","V","V","SSE","operand16,operand32","w,r","Y","32" +"CVTSI2SD xmm1, r/m64","CVTSQ2SD r/m64, xmm1","cvtsi2sdq r/m64, xmm1","F2 REX.W 0F 2A /r","N.S.","V","SSE2","","w,r","Y","64" +"CVTSI2SS xmm1, r/m64","CVTSQ2SS r/m64, xmm1","cvtsi2ssq r/m64, xmm1","F3 REX.W 0F 2A /r","N.S.","V","SSE","","w,r","Y","64" +"CVTSS2SD xmm1, xmm2/m32","CVTSS2SD xmm2/m32, xmm1","cvtss2sd xmm2/m32, xmm1","F3 0F 5A /r","V","V","SSE2","","w,r","","" +"CVTSS2SI r32, xmm2/m32","CVTSS2SL xmm2/m32, r32","cvtss2si xmm2/m32, r32","F3 0F 2D /r","V","V","SSE","operand16,operand32","w,r","Y","32" +"CVTSS2SI r64, xmm2/m32","CVTSS2SL xmm2/m32, r64","cvtss2siq xmm2/m32, r64","F3 REX.W 0F 2D /r","N.S.","V","SSE","","w,r","Y","64" +"CVTTPD2PI mm1, xmm2/m128","CVTTPD2PI xmm2/m128, mm1","cvttpd2pi xmm2/m128, mm1","66 0F 2C /r","V","V","SSE2","","w,r","","" +"CVTTPD2DQ xmm1, xmm2/m128","CVTTPD2PL xmm2/m128, xmm1","cvttpd2dq xmm2/m128, xmm1","66 0F E6 /r","V","V","SSE2","","w,r","","" +"CVTTPS2PI mm1, xmm2/m64","CVTTPS2PI xmm2/m64, mm1","cvttps2pi xmm2/m64, mm1","0F 2C /r","V","V","SSE","","w,r","","" +"CVTTPS2DQ xmm1, xmm2/m128","CVTTPS2PL xmm2/m128, xmm1","cvttps2dq xmm2/m128, xmm1","F3 0F 5B /r","V","V","SSE2","","w,r","","" +"CVTTSD2SI r32, xmm2/m64","CVTTSD2SL xmm2/m64, r32","cvttsd2si xmm2/m64, r32","F2 0F 2C /r","V","V","SSE2","operand16,operand32","w,r","Y","32" +"CVTTSD2SI r64, xmm2/m64","CVTTSD2SL xmm2/m64, r64","cvttsd2siq xmm2/m64, r64","F2 REX.W 0F 2C /r","N.S.","V","SSE2","","w,r","Y","64" +"CVTTSS2SI r32, xmm2/m32","CVTTSS2SL xmm2/m32, r32","cvttss2si xmm2/m32, r32","F3 0F 2C /r","V","V","SSE","operand16,operand32","w,r","Y","32" +"CVTTSS2SI r64, xmm2/m32","CVTTSS2SL xmm2/m32, r64","cvttss2siq xmm2/m32, r64","F3 REX.W 0F 2C /r","N.S.","V","SSE","","w,r","Y","64" +"CWD","CWD","cwtd","99","V","V","","operand16","","","" +"CWDE","CWDE","cwtl","98","V","V","","operand32","","","" +"DAA","DAA","daa","27","V","N.S.","","","","","" +"DAS","DAS","das","2F","V","N.S.","","","","","" +"DEC r/m8","DECB r/m8","decb r/m8","FE /1","V","V","","","rw","Y","8" +"DEC r/m8","DECB r/m8","decb r/m8","REX FE /1","N.E.","V","","pseudo64","rw","Y","8" +"DEC r/m32","DECL r/m32","decl r/m32","FF /1","V","V","","operand32","rw","Y","32" +"DEC r32op","DECL r32op","decl r32op","48+rd","V","N.S.","","operand32","rw","Y","32" +"DEC r/m64","DECQ r/m64","decq r/m64","REX.W FF /1","N.S.","V","","","rw","Y","64" +"DEC r/m16","DECW r/m16","decw r/m16","FF /1","V","V","","operand16","rw","Y","16" +"DEC r16op","DECW r16op","decw r16op","48+rw","V","N.S.","","operand16","rw","Y","16" +"DIV r/m8","DIVB r/m8","divb r/m8","F6 /6","V","V","","","r","Y","8" +"DIV r/m8","DIVB r/m8","divb r/m8","REX F6 /6","N.E.","V","","pseudo64","w","Y","8" +"DIV r/m32","DIVL r/m32","divl r/m32","F7 /6","V","V","","operand32","r","Y","32" +"DIVPD xmm1, xmm2/m128","DIVPD xmm2/m128, xmm1","divpd xmm2/m128, xmm1","66 0F 5E /r","V","V","SSE2","","rw,r","","" +"DIVPS xmm1, xmm2/m128","DIVPS xmm2/m128, xmm1","divps xmm2/m128, xmm1","0F 5E /r","V","V","SSE","","rw,r","","" +"DIV r/m64","DIVQ r/m64","divq r/m64","REX.W F7 /6","N.S.","V","","","r","Y","64" +"DIVSD xmm1, xmm2/m64","DIVSD xmm2/m64, xmm1","divsd xmm2/m64, xmm1","F2 0F 5E /r","V","V","SSE2","","rw,r","","" +"DIVSS xmm1, xmm2/m32","DIVSS xmm2/m32, xmm1","divss xmm2/m32, xmm1","F3 0F 5E /r","V","V","SSE","","rw,r","","" +"DIV r/m16","DIVW r/m16","divw r/m16","F7 /6","V","V","","operand16","r","Y","16" +"DPPD xmm1, xmm2/m128, imm8u","DPPD imm8u, xmm2/m128, xmm1","dppd imm8u, xmm2/m128, xmm1","66 0F 3A 41 /r ib","V","V","SSE4_1","","rw,r,r","","" +"DPPS xmm1, xmm2/m128, imm8u","DPPS imm8u, xmm2/m128, xmm1","dpps imm8u, xmm2/m128, xmm1","66 0F 3A 40 /r ib","V","V","SSE4_1","","rw,r,r","","" +"EMMS","EMMS","emms","0F 77","V","V","MMX","","","","" +"ENCLS","ENCLS","encls","0F 01 CF","V","V","","","","","" +"ENCLU","ENCLU","enclu","0F 01 D7","V","V","","","","","" +"ENDBR32","ENDBR32","endbr32","F3 0F 1E FB","V","V","CET","","","","" +"ENDBR64","ENDBR64","endbr64","F3 0F 1E FA","V","V","CET","","","Y","" +"ENTER imm16, 0","ENTER 0, imm16","enter imm16, 0","C8 iw 00","V","V","","pseudo","r,r","","" +"ENTER imm16, 1","ENTER 1, imm16","enter imm16, 1","C8 iw 01","V","V","","pseudo","r,r","","" +"ENTER imm16, imm8b","ENTERW/ENTERL/ENTERQ imm8b, imm16","enterw/enterl/enterq imm16, imm8b","C8 iw ib","V","V","","","r,r","","" +"EXTRACTPS r/m32, xmm1, imm8u:2","EXTRACTPS imm8u:2, xmm1, r/m32","extractps imm8u:2, xmm1, r/m32","66 0F 3A 17 /r ib","V","V","SSE4_1","","w,r,r","","" +"EXTRQ xmm1, imm8u, imm8u","EXTRQ imm8u, imm8u, xmm1","extrq imm8u, imm8u, xmm1","66 0F 78 /0 ib ib","V","V","SSE4a","amd,modrm_regonly","w,r,r","","" +"EXTRQ xmm1, xmm2","EXTRQ xmm2, xmm1","extrq xmm2, xmm1","66 0F 79 /r","V","V","SSE4a","amd,modrm_regonly","w,r","","" +"F2XM1","F2XM1","f2xm1","D9 F0","V","V","","","","","" +"FABS","FABS","fabs","D9 E1","V","V","","","","","" +"FADD ST(i), ST(0)","FADDD ST(0), ST(i)","fadd ST(0), ST(i)","DC C0+i","V","V","","","rw,r","Y","" +"FADD ST(0), ST(i)","FADDD ST(i), ST(0)","fadd ST(i), ST(0)","D8 C0+i","V","V","","","rw,r","Y","" +"FADD ST(0), m32fp","FADDD m32fp, ST(0)","fadds m32fp, ST(0)","D8 /0","V","V","","","rw,r","Y","32" +"FADD ST(0), m64fp","FADDD m64fp, ST(0)","faddl m64fp, ST(0)","DC /0","V","V","","","rw,r","Y","64" +"FADDP","FADDDP","faddp","DE C1","V","V","","pseudo","","","" +"FADDP ST(i), ST(0)","FADDDP ST(0), ST(i)","faddp ST(0), ST(i)","DE C0+i","V","V","","","rw,r","","" +"FBLD ST(0), m80dec","FBLD m80dec, ST(0)","fbld m80dec, ST(0)","DF /4","V","V","","","w,r","","" +"FBSTP m80dec, ST(0)","FBSTP ST(0), m80dec","fbstp ST(0), m80dec","DF /6","V","V","","","w,r","","" +"FCHS","FCHS","fchs","D9 E0","V","V","","","","","" +"FCLEX","FCLEX","fclex","9B DB E2","V","V","","pseudo","","","" +"FCMOVB ST(0), ST(i)","FCMOVB ST(i), ST(0)","fcmovb ST(i), ST(0)","DA C0+i","V","V","","P6","rw,r","","" +"FCMOVBE ST(0), ST(i)","FCMOVBE ST(i), ST(0)","fcmovbe ST(i), ST(0)","DA D0+i","V","V","","P6","rw,r","","" +"FCMOVE ST(0), ST(i)","FCMOVE ST(i), ST(0)","fcmove ST(i), ST(0)","DA C8+i","V","V","","P6","rw,r","","" +"FCMOVNB ST(0), ST(i)","FCMOVNB ST(i), ST(0)","fcmovnb ST(i), ST(0)","DB C0+i","V","V","","P6","rw,r","","" +"FCMOVNBE ST(0), ST(i)","FCMOVNBE ST(i), ST(0)","fcmovnbe ST(i), ST(0)","DB D0+i","V","V","","P6","rw,r","","" +"FCMOVNE ST(0), ST(i)","FCMOVNE ST(i), ST(0)","fcmovne ST(i), ST(0)","DB C8+i","V","V","","P6","rw,r","","" +"FCMOVNU ST(0), ST(i)","FCMOVNU ST(i), ST(0)","fcmovnu ST(i), ST(0)","DB D8+i","V","V","","P6","rw,r","","" +"FCMOVU ST(0), ST(i)","FCMOVU ST(i), ST(0)","fcmovu ST(i), ST(0)","DA D8+i","V","V","","P6","rw,r","","" +"FCOM","FCOMD","fcom","D8 D1","V","V","","pseudo","","Y","" +"FCOM ST(0), ST(i)","FCOMD ST(i), ST(0)","fcom ST(i), ST(0)","D8 D0+i","V","V","","","r,r","Y","" +"FCOM ST(0), ST(i)","FCOMD ST(i), ST(0)","fcom ST(i), ST(0)","DC D0+i","V","V","","","r,r","Y","" +"FCOM ST(0), m32fp","FCOMD m32fp, ST(0)","fcoms m32fp, ST(0)","D8 /2","V","V","","","r,r","Y","32" +"FCOM ST(0), m64fp","FCOMD m64fp, ST(0)","fcoml m64fp, ST(0)","DC /2","V","V","","","r,r","Y","64" +"FCOMP ST(0), m32fp","FCOMFP m32fp, ST(0)","fcomps m32fp, ST(0)","D8 /3","V","V","","","r,r","Y","32" +"FCOMI ST(0), ST(i)","FCOMI ST(i), ST(0)","fcomi ST(i), ST(0)","DB F0+i","V","V","PPRO","P6","r,r","","" +"FCOMIP ST(0), ST(i)","FCOMIP ST(i), ST(0)","fcomip ST(i), ST(0)","DF F0+i","V","V","PPRO","P6","r,r","","" +"FCOMP","FCOMP","fcomp","D8 D9","V","V","","pseudo","","Y","" +"FCOMP ST(0), ST(i)","FCOMP ST(i), ST(0)","fcomp ST(i), ST(0)","D8 D8+i","V","V","","","r,r","Y","" +"FCOMP ST(0), ST(i)","FCOMP ST(i), ST(0)","fcomp ST(i), ST(0)","DC D8+i","V","V","","","r,r","Y","" +"FCOMP ST(0), ST(i)","FCOMP ST(i), ST(0)","fcomp ST(i), ST(0)","DE D0+i","V","V","","","r,r","Y","" +"FCOMP ST(0), m64fp","FCOMPL m64fp, ST(0)","fcompl m64fp, ST(0)","DC /3","V","V","","","r,r","Y","64" +"FCOMPP","FCOMPP","fcompp","DE D9","V","V","","","","","" +"FCOS","FCOS","fcos","D9 FF","V","V","","","","","" +"FDECSTP","FDECSTP","fdecstp","D9 F6","V","V","","","","","" +"FDISI8087_NOP","FDISI8087_NOP","fdisi8087_nop","DB E1","V","V","","","","","" +"FDIVR ST(i), ST(0)","FDIVD ST(0), ST(i)","fdiv ST(0), ST(i)","DC F0+i","V","V","","","rw,r","Y","" +"FDIV ST(i), ST(0)","FDIVD ST(0), ST(i)","fdivr ST(0), ST(i)","DC F8+i","V","V","","","rw,r","Y","" +"FDIV ST(0), ST(i)","FDIVD ST(i), ST(0)","fdiv ST(i), ST(0)","D8 F0+i","V","V","","","rw,r","Y","" +"FDIV ST(0), m32fp","FDIVD m32fp, ST(0)","fdivs m32fp, ST(0)","D8 /6","V","V","","","rw,r","Y","32" +"FDIV ST(0), m64fp","FDIVD m64fp, ST(0)","fdivl m64fp, ST(0)","DC /6","V","V","","","rw,r","Y","64" +"FDIVR ST(0), m32fp","FDIVFR m32fp, ST(0)","fdivrs m32fp, ST(0)","D8 /7","V","V","","","rw,r","Y","32" +"FDIVP","FDIVP","fdivp","DE F9","V","V","","pseudo","","","" +"FDIVRP ST(i), ST(0)","FDIVP ST(0), ST(i)","fdivp ST(0), ST(i)","DE F0+i","V","V","","","rw,r","","" +"FDIVR ST(0), ST(i)","FDIVR ST(i), ST(0)","fdivr ST(i), ST(0)","D8 F8+i","V","V","","","rw,r","Y","" +"FDIVR ST(0), m64fp","FDIVRL m64fp, ST(0)","fdivrl m64fp, ST(0)","DC /7","V","V","","","rw,r","Y","64" +"FDIVRP","FDIVRP","fdivrp","DE F1","V","V","","pseudo","","","" +"FDIVP ST(i), ST(0)","FDIVRP ST(0), ST(i)","fdivrp ST(0), ST(i)","DE F8+i","V","V","","","rw,r","","" +"FEMMS","FEMMS","femms","0F 0E","V","V","3DNOW","amd","","","" +"FENI8087_NOP","FENI8087_NOP","feni8087_nop","DB E0","V","V","","","","","" +"FFREE ST(i)","FFREE ST(i)","ffree ST(i)","DD C0+i","V","V","","","r","","" +"FFREEP ST(i)","FFREEP ST(i)","ffreep ST(i)","DF C0+i","V","V","","","r","","" +"FIADD ST(0), m16int","FIADD m16int, ST(0)","fiadd m16int, ST(0)","DE /0","V","V","","","rw,r","Y","" +"FIADD ST(0), m32int","FIADDL m32int, ST(0)","fiaddl m32int, ST(0)","DA /0","V","V","","","rw,r","Y","32" +"FICOM ST(0), m16int","FICOM m16int, ST(0)","ficom m16int, ST(0)","DE /2","V","V","","","r,r","Y","" +"FICOM ST(0), m32int","FICOML m32int, ST(0)","ficoml m32int, ST(0)","DA /2","V","V","","","r,r","Y","32" +"FICOMP ST(0), m16int","FICOMP m16int, ST(0)","ficomp m16int, ST(0)","DE /3","V","V","","","r,r","Y","" +"FICOMP ST(0), m32int","FICOMPL m32int, ST(0)","ficompl m32int, ST(0)","DA /3","V","V","","","r,r","Y","32" +"FIDIV ST(0), m16int","FIDIV m16int, ST(0)","fidiv m16int, ST(0)","DE /6","V","V","","","rw,r","Y","" +"FIDIV ST(0), m32int","FIDIVL m32int, ST(0)","fidivl m32int, ST(0)","DA /6","V","V","","","rw,r","Y","32" +"FIDIVR ST(0), m16int","FIDIVR m16int, ST(0)","fidivr m16int, ST(0)","DE /7","V","V","","","rw,r","Y","" +"FIDIVR ST(0), m32int","FIDIVRL m32int, ST(0)","fidivrl m32int, ST(0)","DA /7","V","V","","","rw,r","Y","32" +"FILD ST(0), m16int","FILD m16int, ST(0)","fild m16int, ST(0)","DF /0","V","V","","","w,r","Y","" +"FILD ST(0), m32int","FILDL m32int, ST(0)","fildl m32int, ST(0)","DB /0","V","V","","","w,r","Y","32" +"FILD ST(0), m64int","FILDLL m64int, ST(0)","fildll m64int, ST(0)","DF /5","V","V","","","w,r","Y","64" +"FIMUL ST(0), m16int","FIMUL m16int, ST(0)","fimul m16int, ST(0)","DE /1","V","V","","","rw,r","Y","" +"FIMUL ST(0), m32int","FIMULL m32int, ST(0)","fimull m32int, ST(0)","DA /1","V","V","","","rw,r","Y","32" +"FINCSTP","FINCSTP","fincstp","D9 F7","V","V","","","","","" +"FINIT","FINIT","finit","9B DB E3","V","V","","pseudo","","","" +"FIST m16int, ST(0)","FIST ST(0), m16int","fist ST(0), m16int","DF /2","V","V","","","w,r","Y","" +"FIST m32int, ST(0)","FISTL ST(0), m32int","fistl ST(0), m32int","DB /2","V","V","","","w,r","Y","32" +"FISTP m16int, ST(0)","FISTP ST(0), m16int","fistp ST(0), m16int","DF /3","V","V","","","w,r","Y","" +"FISTP m32int, ST(0)","FISTPL ST(0), m32int","fistpl ST(0), m32int","DB /3","V","V","","","w,r","Y","32" +"FISTP m64int, ST(0)","FISTPLL ST(0), m64int","fistpll ST(0), m64int","DF /7","V","V","","","w,r","Y","64" +"FISTTP m16int, ST(0)","FISTTP ST(0), m16int","fisttp ST(0), m16int","DF /1","V","V","SSE3","modrm_memonly","w,r","Y","" +"FISTTP m32int, ST(0)","FISTTPL ST(0), m32int","fisttpl ST(0), m32int","DB /1","V","V","SSE3","modrm_memonly","w,r","Y","32" +"FISTTP m64int, ST(0)","FISTTPLL ST(0), m64int","fisttpll ST(0), m64int","DD /1","V","V","SSE3","modrm_memonly","w,r","Y","64" +"FISUB ST(0), m16int","FISUB m16int, ST(0)","fisub m16int, ST(0)","DE /4","V","V","","","rw,r","Y","" +"FISUB ST(0), m32int","FISUBL m32int, ST(0)","fisubl m32int, ST(0)","DA /4","V","V","","","rw,r","Y","32" +"FISUBR ST(0), m16int","FISUBR m16int, ST(0)","fisubr m16int, ST(0)","DE /5","V","V","","","rw,r","Y","" +"FISUBR ST(0), m32int","FISUBRL m32int, ST(0)","fisubrl m32int, ST(0)","DA /5","V","V","","","rw,r","Y","32" +"FLD ST(0), ST(i)","FLD ST(i), ST(0)","fld ST(i), ST(0)","D9 C0+i","V","V","","","w,r","Y","" +"FLD1","FLD1","fld1","D9 E8","V","V","","","","","" +"FLDCW m2byte","FLDCW m2byte","fldcw m2byte","D9 /5","V","V","","","r","","" +"FLDENV m28byte","FLDENV m28byte","fldenv m28byte","D9 /4","V","V","","operand32,operand64","r","","" +"FLDENV m14byte","FLDENVS m14byte","fldenv m14byte","D9 /4","V","V","","operand16","r","","" +"FLD ST(0), m64fp","FLDL m64fp, ST(0)","fldl m64fp, ST(0)","DD /0","V","V","","","w,r","Y","64" +"FLDL2E","FLDL2E","fldl2e","D9 EA","V","V","","","","","" +"FLDL2T","FLDL2T","fldl2t","D9 E9","V","V","","","","","" +"FLDLG2","FLDLG2","fldlg2","D9 EC","V","V","","","","","" +"FLDLN2","FLDLN2","fldln2","D9 ED","V","V","","","","","" +"FLDPI","FLDPI","fldpi","D9 EB","V","V","","","","","" +"FLD ST(0), m32fp","FLDS m32fp, ST(0)","flds m32fp, ST(0)","D9 /0","V","V","","","w,r","Y","32" +"FLD ST(0), m80fp","FLDT m80fp, ST(0)","fldt m80fp, ST(0)","DB /5","V","V","","","w,r","Y","80" +"FLDZ","FLDZ","fldz","D9 EE","V","V","","","","","" +"FMUL ST(i), ST(0)","FMUL ST(0), ST(i)","fmul ST(0), ST(i)","DC C8+i","V","V","","","rw,r","Y","" +"FMUL ST(0), ST(i)","FMUL ST(i), ST(0)","fmul ST(i), ST(0)","D8 C8+i","V","V","","","rw,r","Y","" +"FMUL ST(0), m64fp","FMULL m64fp, ST(0)","fmull m64fp, ST(0)","DC /1","V","V","","","rw,r","Y","64" +"FMULP","FMULP","fmulp","DE C9","V","V","","pseudo","","","" +"FMULP ST(i), ST(0)","FMULP ST(0), ST(i)","fmulp ST(0), ST(i)","DE C8+i","V","V","","","rw,r","","" +"FMUL ST(0), m32fp","FMULS m32fp, ST(0)","fmuls m32fp, ST(0)","D8 /1","V","V","","","rw,r","Y","32" +"FNCLEX","FNCLEX","fnclex","DB E2","V","V","","","","","" +"FNINIT","FNINIT","fninit","DB E3","V","V","","","","","" +"FNOP","FNOP","fnop","D9 D0","V","V","","","","","" +"FNSAVE m108byte","FNSAVE m108byte","fnsave m108byte","DD /6","V","V","","operand32,operand64","w","","" +"FNSAVE m94byte","FNSAVES m94byte","fnsave m94byte","DD /6","V","V","","operand16","w","","" +"FNSTCW m2byte","FNSTCW m2byte","fnstcw m2byte","D9 /7","V","V","","","w","","" +"FNSTENV m28byte","FNSTENV m28byte","fnstenv m28byte","D9 /6","V","V","","operand32,operand64","w","","" +"FNSTENV m14byte","FNSTENVS m14byte","fnstenv m14byte","D9 /6","V","V","","operand16","w","","" +"FNSTSW AX","FNSTSW AX","fnstsw AX","DF E0","V","V","","","w","","" +"FNSTSW m2byte","FNSTSW m2byte","fnstsw m2byte","DD /7","V","V","","","w","","" +"FPATAN","FPATAN","fpatan","D9 F3","V","V","","","","","" +"FPREM","FPREM","fprem","D9 F8","V","V","","","","","" +"FPREM1","FPREM1","fprem1","D9 F5","V","V","","","","","" +"FPTAN","FPTAN","fptan","D9 F2","V","V","","","","","" +"FRNDINT","FRNDINT","frndint","D9 FC","V","V","","","","","" +"FRSTOR m108byte","FRSTOR m108byte","frstor m108byte","DD /4","V","V","","operand32,operand64","r","","" +"FRSTOR m94byte","FRSTORS m94byte","frstor m94byte","DD /4","V","V","","operand16","r","","" +"FSAVE m94/108byte","FSAVE m94/108byte","fsave m94/108byte","9B DD /6","V","V","","pseudo","w","","" +"FSCALE","FSCALE","fscale","D9 FD","V","V","","","","","" +"FSETPM287_NOP","FSETPM287_NOP","fsetpm287_nop","DB E4","V","V","","","","","" +"FSIN","FSIN","fsin","D9 FE","V","V","","","","","" +"FSINCOS","FSINCOS","fsincos","D9 FB","V","V","","","","","" +"FSQRT","FSQRT","fsqrt","D9 FA","V","V","","","","","" +"FST ST(i), ST(0)","FST ST(0), ST(i)","fst ST(0), ST(i)","DD D0+i","V","V","","","w,r","Y","" +"FSTCW m2byte","FSTCW m2byte","fstcw m2byte","9B D9 /7","V","V","","pseudo","w","","" +"FSTENV m14/28byte","FSTENV m14/28byte","fstenv m14/28byte","9B D9 /6","V","V","","pseudo","w","","" +"FST m64fp, ST(0)","FSTL ST(0), m64fp","fstl ST(0), m64fp","DD /2","V","V","","","w,r","Y","64" +"FSTP ST(i), ST(0)","FSTP ST(0), ST(i)","fstp ST(0), ST(i)","DD D8+i","V","V","","","w,r","Y","" +"FSTP ST(i), ST(0)","FSTP ST(0), ST(i)","fstp ST(0), ST(i)","DF D0+i","V","V","","","w,r","Y","" +"FSTP ST(i), ST(0)","FSTP ST(0), ST(i)","fstp ST(0), ST(i)","DF D8+i","V","V","","","w,r","Y","" +"FSTP m64fp, ST(0)","FSTPL ST(0), m64fp","fstpl ST(0), m64fp","DD /3","V","V","","","w,r","Y","64" +"FSTPNCE ST(i), ST(0)","FSTPNCE ST(0), ST(i)","fstpnce ST(0), ST(i)","D9 D8+i","V","V","","","w,r","","" +"FSTP m32fp, ST(0)","FSTPS ST(0), m32fp","fstps ST(0), m32fp","D9 /3","V","V","","","w,r","Y","32" +"FSTP m80fp, ST(0)","FSTPT ST(0), m80fp","fstpt ST(0), m80fp","DB /7","V","V","","","w,r","Y","80" +"FST m32fp, ST(0)","FSTS ST(0), m32fp","fsts ST(0), m32fp","D9 /2","V","V","","","w,r","Y","32" +"FSTSW AX","FSTSW AX","fstsw AX","9B DF E0","V","V","","pseudo","w","","" +"FSTSW m2byte","FSTSW m2byte","fstsw m2byte","9B DD /7","V","V","","pseudo","w","","" +"FSUBR ST(i), ST(0)","FSUB ST(0), ST(i)","fsub ST(0), ST(i)","DC E0+i","V","V","","","rw,r","Y","" +"FSUB ST(0), ST(i)","FSUB ST(i), ST(0)","fsub ST(i), ST(0)","D8 E0+i","V","V","","","rw,r","Y","" +"FSUB ST(0), m64fp","FSUBL m64fp, ST(0)","fsubl m64fp, ST(0)","DC /4","V","V","","","rw,r","Y","64" +"FSUBP","FSUBP","fsubp","DE E9","V","V","","pseudo","","","" +"FSUBRP ST(i), ST(0)","FSUBP ST(0), ST(i)","fsubp ST(0), ST(i)","DE E0+i","V","V","","","rw,r","","" +"FSUB ST(i), ST(0)","FSUBR ST(0), ST(i)","fsubr ST(0), ST(i)","DC E8+i","V","V","","","rw,r","Y","" +"FSUBR ST(0), ST(i)","FSUBR ST(i), ST(0)","fsubr ST(i), ST(0)","D8 E8+i","V","V","","","rw,r","Y","" +"FSUBR ST(0), m64fp","FSUBRL m64fp, ST(0)","fsubrl m64fp, ST(0)","DC /5","V","V","","","rw,r","Y","64" +"FSUBRP","FSUBRP","fsubrp","DE E1","V","V","","pseudo","","","" +"FSUBP ST(i), ST(0)","FSUBRP ST(0), ST(i)","fsubrp ST(0), ST(i)","DE E8+i","V","V","","","rw,r","","" +"FSUBR ST(0), m32fp","FSUBRS m32fp, ST(0)","fsubrs m32fp, ST(0)","D8 /5","V","V","","","rw,r","Y","32" +"FSUB ST(0), m32fp","FSUBS m32fp, ST(0)","fsubs m32fp, ST(0)","D8 /4","V","V","","","rw,r","Y","32" +"FTST","FTST","ftst","D9 E4","V","V","","","","","" +"FUCOM","FUCOM","fucom","DD E1","V","V","","pseudo","","","" +"FUCOM ST(0), ST(i)","FUCOM ST(i), ST(0)","fucom ST(i), ST(0)","DD E0+i","V","V","","","r,r","","" +"FUCOMI ST(0), ST(i)","FUCOMI ST(i), ST(0)","fucomi ST(i), ST(0)","DB E8+i","V","V","PPRO","P6","r,r","","" +"FUCOMIP ST(0), ST(i)","FUCOMIP ST(i), ST(0)","fucomip ST(i), ST(0)","DF E8+i","V","V","PPRO","P6","r,r","","" +"FUCOMP","FUCOMP","fucomp","DD E9","V","V","","pseudo","","","" +"FUCOMP ST(0), ST(i)","FUCOMP ST(i), ST(0)","fucomp ST(i), ST(0)","DD E8+i","V","V","","","r,r","","" +"FUCOMPP","FUCOMPP","fucompp","DA E9","V","V","","","","","" +"FWAIT","FWAIT","fwait","9B","V","V","","","","","" +"FXAM","FXAM","fxam","D9 E5","V","V","","","","","" +"FXCH","FXCH","fxch","D9 C9","V","V","","pseudo","","","" +"FXCH ST(0), ST(i)","FXCH ST(i), ST(0)","fxch ST(i), ST(0)","D9 C8+i","V","V","","","rw,rw","","" +"FXCH_ALIAS1 ST(0), ST(i)","FXCH_ALIAS1 ST(i), ST(0)","fxch_alias1 ST(i), ST(0)","DD C8+i","V","V","","","rw,rw","","" +"FXCH_ALIAS2 ST(0), ST(i)","FXCH_ALIAS2 ST(i), ST(0)","fxch_alias2 ST(i), ST(0)","DF C8+i","V","V","","","rw,rw","","" +"FXRSTOR m512byte","FXRSTOR m512byte","fxrstor m512byte","0F AE /1","V","V","","modrm_memonly,operand16,operand32","r","","" +"FXRSTOR64 m512byte","FXRSTOR64 m512byte","fxrstor64 m512byte","REX.W 0F AE /1","N.S.","V","","modrm_memonly","r","","" +"FXSAVE m512byte","FXSAVE m512byte","fxsave m512byte","0F AE /0","V","V","","modrm_memonly,operand16,operand32","w","","" +"FXSAVE64 m512byte","FXSAVE64 m512byte","fxsave64 m512byte","REX.W 0F AE /0","N.S.","V","","modrm_memonly","w","","" +"FXTRACT","FXTRACT","fxtract","D9 F4","V","V","","","","","" +"FYL2X","FYL2X","fyl2x","D9 F1","V","V","","","","","" +"FYL2XP1","FYL2XP1","fyl2xp1","D9 F9","V","V","","","","","" +"GETSEC","GETSEC","getsec","0F 37","V","V","SMX","","","","" +"GF2P8AFFINEINVQB xmm1, xmm2/m128, imm8u","GF2P8AFFINEINVQB imm8u, xmm2/m128, xmm1","gf2p8affineinvqb imm8u, xmm2/m128, xmm1","66 0F 3A CF /r ib","V","V","GFNI","","rw,r,r","","" +"GF2P8AFFINEQB xmm1, xmm2/m128, imm8u","GF2P8AFFINEQB imm8u, xmm2/m128, xmm1","gf2p8affineqb imm8u, xmm2/m128, xmm1","66 0F 3A CE /r ib","V","V","GFNI","","rw,r,r","","" +"GF2P8MULB xmm1, xmm2/m128","GF2P8MULB xmm2/m128, xmm1","gf2p8mulb xmm2/m128, xmm1","66 0F 38 CF /r","V","V","GFNI","","rw,r","","" +"HADDPD xmm1, xmm2/m128","HADDPD xmm2/m128, xmm1","haddpd xmm2/m128, xmm1","66 0F 7C /r","V","V","SSE3","","rw,r","","" +"HADDPS xmm1, xmm2/m128","HADDPS xmm2/m128, xmm1","haddps xmm2/m128, xmm1","F2 0F 7C /r","V","V","SSE3","","rw,r","","" +"HLT","HLT","hlt","F4","V","V","","","","","" +"HSUBPD xmm1, xmm2/m128","HSUBPD xmm2/m128, xmm1","hsubpd xmm2/m128, xmm1","66 0F 7D /r","V","V","SSE3","","rw,r","","" +"HSUBPS xmm1, xmm2/m128","HSUBPS xmm2/m128, xmm1","hsubps xmm2/m128, xmm1","F2 0F 7D /r","V","V","SSE3","","rw,r","","" +"ICEBP","ICEBP","icebp","F1","V","V","","","","","" +"IDIV r/m8","IDIVB r/m8","idivb r/m8","F6 /7","V","V","","","r","Y","8" +"IDIV r/m8","IDIVB r/m8","idivb r/m8","REX F6 /7","N.E.","V","","pseudo64","r","Y","8" +"IDIV r/m32","IDIVL r/m32","idivl r/m32","F7 /7","V","V","","operand32","r","Y","32" +"IDIV r/m64","IDIVQ r/m64","idivq r/m64","REX.W F7 /7","N.S.","V","","","r","Y","64" +"IDIV r/m16","IDIVW r/m16","idivw r/m16","F7 /7","V","V","","operand16","r","Y","16" +"IMUL r32, r/m32, imm32","IMUL3 imm32, r/m32, r32","imull imm32, r/m32, r32","69 /r id","V","V","","operand32","w,r,r","Y","32" +"IMUL r64, r/m64, imm32","IMUL3 imm32, r/m64, r64","imulq imm32, r/m64, r64","REX.W 69 /r id","N.S.","V","","","w,r,r","Y","64" +"IMUL r16, r/m16, imm8","IMUL3 imm8, r/m16, r16","imulw imm8, r/m16, r16","6B /r ib","V","V","","operand16","w,r,r","Y","16" +"IMUL r32, r/m32, imm8","IMUL3 imm8, r/m32, r32","imull imm8, r/m32, r32","6B /r ib","V","V","","operand32","w,r,r","Y","32" +"IMUL r64, r/m64, imm8","IMUL3 imm8, r/m64, r64","imulq imm8, r/m64, r64","REX.W 6B /r ib","N.S.","V","","","w,r,r","Y","64" +"IMUL r/m8","IMULB r/m8","imulb r/m8","F6 /5","V","V","","","r","Y","8" +"IMUL r/m32","IMULL r/m32","imull r/m32","F7 /5","V","V","","operand32","r","Y","32" +"IMUL r32, r/m32","IMULL r/m32, r32","imull r/m32, r32","0F AF /r","V","V","","operand32","rw,r","Y","32" +"IMUL r/m64","IMULQ r/m64","imulq r/m64","REX.W F7 /5","N.S.","V","","","r","Y","64" +"IMUL r64, r/m64","IMULQ r/m64, r64","imulq r/m64, r64","REX.W 0F AF /r","N.S.","V","","","rw,r","Y","64" +"IMUL r16, r/m16, imm16","IMULW imm16, r/m16, r16","imulw imm16, r/m16, r16","69 /r iw","V","V","","operand16","w,r,r","Y","16" +"IMUL r/m16","IMULW r/m16","imulw r/m16","F7 /5","V","V","","operand16","r","Y","16" +"IMUL r16, r/m16","IMULW r/m16, r16","imulw r/m16, r16","0F AF /r","V","V","","operand16","rw,r","Y","16" +"IN AL, DX","INB DX, AL","inb DX, AL","EC","V","V","","","w,r","Y","8" +"IN AL, imm8u","INB imm8u, AL","inb imm8u, AL","E4 ib","V","V","","","w,r","Y","8" +"INC r/m8","INCB r/m8","incb r/m8","FE /0","V","V","","","rw","Y","8" +"INC r/m8","INCB r/m8","incb r/m8","REX FE /0","N.E.","V","","pseudo64","rw","Y","8" +"INC r/m32","INCL r/m32","incl r/m32","FF /0","V","V","","operand32","rw","Y","32" +"INC r32op","INCL r32op","incl r32op","40+rd","V","N.S.","","operand32","rw","Y","32" +"INC r/m64","INCQ r/m64","incq r/m64","REX.W FF /0","N.S.","V","","","rw","Y","64" +"INCSSPD rmr32","INCSSPD rmr32","incsspd rmr32","F3 0F AE /5","V","V","CET","modrm_regonly,operand16,operand32","r","","" +"INCSSPQ rmr64","INCSSPQ rmr64","incsspq rmr64","F3 REX.W 0F AE /5","N.S.","V","CET","modrm_regonly","r","","" +"INC r/m16","INCW r/m16","incw r/m16","FF /0","V","V","","operand16","rw","Y","16" +"INC r16op","INCW r16op","incw r16op","40+rw","V","N.S.","","operand16","rw","Y","16" +"IN EAX, DX","INL DX, EAX","inl DX, EAX","ED","V","V","","operand32,operand64","w,r","Y","32" +"IN EAX, imm8u","INL imm8u, EAX","inl imm8u, EAX","E5 ib","V","V","","operand32,operand64","w,r","Y","32" +"INSB","INSB","insb","6C","V","V","","","","","" +"INSERTPS xmm1, xmm2/m32, imm8u","INSERTPS imm8u, xmm2/m32, xmm1","insertps imm8u, xmm2/m32, xmm1","66 0F 3A 21 /r ib","V","V","SSE4_1","","rw,r,r","","" +"INSERTQ xmm1, xmm2, imm8u, imm8u","INSERTQ imm8u, imm8u, xmm2, xmm1","insertq imm8u, imm8u, xmm2, xmm1","F2 0F 78 /r ib ib","V","V","SSE4a","amd,modrm_regonly","w,r,r,r","","" +"INSERTQ xmm1, xmm2","INSERTQ xmm2, xmm1","insertq xmm2, xmm1","F2 0F 79 /r","V","V","SSE4a","amd,modrm_regonly","w,r","","" +"INSD","INSL","insl","6D","V","V","","operand32,operand64","","","" +"INSW","INSW","insw","6D","V","V","","operand16","","","" +"INT 3","INT 3","int 3","CC","V","V","","","r","","" +"INT imm8u","INT imm8u","int imm8u","CD ib","V","V","","","r","","" +"INTO","INTO","into","CE","V","N.S.","","","","","" +"INVD","INVD","invd","0F 08","V","V","486","","","","" +"INVEPT r32, m128","INVEPT m128, r32","invept m128, r32","66 0F 38 80 /r","V","N.S.","VTX","modrm_memonly","r,r","","" +"INVEPT r64, m128","INVEPT m128, r64","invept m128, r64","66 0F 38 80 /r","N.S.","V","VTX","default64,modrm_memonly","r,r","","" +"INVLPG m","INVLPG m","invlpg m","0F 01 /7","V","V","486","modrm_memonly","r","","" +"INVLPGA EAX, ECX","INVLPGAL ECX, EAX","invlpgal ECX, EAX","0F 01 DF","V","V","SVM","amd,modrm_regonly,operand32","r,r","Y","32" +"INVLPGA RAX, ECX","INVLPGAQ ECX, RAX","invlpgaq ECX, RAX","REX.W 0F 01 DF","N.S.","V","SVM","amd,modrm_regonly","r,r","Y","64" +"INVLPGA AX, ECX","INVLPGAW ECX, AX","invlpgaw ECX, AX","0F 01 DF","V","V","SVM","amd,modrm_regonly,operand16","r,r","Y","16" +"INVPCID r32, m128","INVPCID m128, r32","invpcid m128, r32","66 0F 38 82 /r","V","N.S.","INVPCID","modrm_memonly","r,r","","" +"INVPCID r64, m128","INVPCID m128, r64","invpcid m128, r64","66 0F 38 82 /r","N.S.","V","INVPCID","default64,modrm_memonly","r,r","","" +"INVVPID r32, m128","INVVPID m128, r32","invvpid m128, r32","66 0F 38 81 /r","V","N.S.","VTX","modrm_memonly","r,r","","" +"INVVPID r64, m128","INVVPID m128, r64","invvpid m128, r64","66 0F 38 81 /r","N.S.","V","VTX","default64,modrm_memonly","r,r","","" +"IN AX, DX","INW DX, AX","inw DX, AX","ED","V","V","","operand16","w,r","Y","16" +"IN AX, imm8u","INW imm8u, AX","inw imm8u, AX","E5 ib","V","V","","operand16","w,r","Y","16" +"IRETD","IRETL","iretl","CF","V","V","","operand32","","","" +"IRETQ","IRETQ","iretq","REX.W CF","N.S.","V","","","","","" +"IRET","IRETW","iretw","CF","V","V","","operand16","","","" +"JA rel16","JA rel16","ja rel16","0F 87 cw","V","N.S.","","operand16","r","","" +"JA rel32","JA rel32","ja rel32","0F 87 cd","V","N.S.","","operand32","r","","" +"JA rel32","JA rel32","ja rel32","0F 87 cd","N.S.","V","","default64","r","","" +"JA rel8","JA rel8","ja rel8","77 cb","N.S.","V","","default64","r","","" +"JA rel8","JA rel8","ja rel8","77 cb","V","N.S.","","","r","","" +"JAE rel16","JAE rel16","jae rel16","0F 83 cw","V","N.S.","","operand16","r","","" +"JAE rel32","JAE rel32","jae rel32","0F 83 cd","N.S.","V","","default64","r","","" +"JAE rel32","JAE rel32","jae rel32","0F 83 cd","V","N.S.","","operand32","r","","" +"JAE rel8","JAE rel8","jae rel8","73 cb","V","N.S.","","","r","","" +"JAE rel8","JAE rel8","jae rel8","73 cb","N.S.","V","","default64","r","","" +"JB rel16","JB rel16","jb rel16","0F 82 cw","V","N.S.","","operand16","r","","" +"JB rel32","JB rel32","jb rel32","0F 82 cd","V","N.S.","","operand32","r","","" +"JB rel32","JB rel32","jb rel32","0F 82 cd","N.S.","V","","default64","r","","" +"JB rel8","JB rel8","jb rel8","72 cb","N.S.","V","","default64","r","","" +"JB rel8","JB rel8","jb rel8","72 cb","V","N.S.","","","r","","" +"JBE rel16","JBE rel16","jbe rel16","0F 86 cw","V","N.S.","","operand16","r","","" +"JBE rel32","JBE rel32","jbe rel32","0F 86 cd","V","N.S.","","operand32","r","","" +"JBE rel32","JBE rel32","jbe rel32","0F 86 cd","N.S.","V","","default64","r","","" +"JBE rel8","JBE rel8","jbe rel8","76 cb","V","N.S.","","","r","","" +"JBE rel8","JBE rel8","jbe rel8","76 cb","N.S.","V","","default64","r","","" +"JC rel16","JC rel16","jc rel16","0F 82 cw","V","N.S.","","pseudo","r","","" +"JC rel32","JC rel32","jc rel32","0F 82 cd","V","V","","pseudo","r","","" +"JC rel8","JC rel8","jc rel8","72 cb","V","V","","pseudo","r","","" +"JCXZ rel8","JCXZ rel8","jcxz rel8","E3 cb","V","N.S.","","address16","r","","" +"JE rel16","JE rel16","je rel16","0F 84 cw","V","N.S.","","operand16","r","","" +"JE rel32","JE rel32","je rel32","0F 84 cd","V","N.S.","","operand32","r","","" +"JE rel32","JE rel32","je rel32","0F 84 cd","N.S.","V","","default64","r","","" +"JE rel8","JE rel8","je rel8","74 cb","N.S.","V","","default64","r","","" +"JE rel8","JE rel8","je rel8","74 cb","V","N.S.","","","r","","" +"JECXZ rel8","JECXZ rel8","jecxz rel8","E3 cb","V","V","","address32","r","","" +"JG rel16","JG rel16","jg rel16","0F 8F cw","V","N.S.","","operand16","r","","" +"JG rel32","JG rel32","jg rel32","0F 8F cd","N.S.","V","","default64","r","","" +"JG rel32","JG rel32","jg rel32","0F 8F cd","V","N.S.","","operand32","r","","" +"JG rel8","JG rel8","jg rel8","7F cb","V","N.S.","","","r","","" +"JG rel8","JG rel8","jg rel8","7F cb","N.S.","V","","default64","r","","" +"JGE rel16","JGE rel16","jge rel16","0F 8D cw","V","N.S.","","operand16","r","","" +"JGE rel32","JGE rel32","jge rel32","0F 8D cd","V","N.S.","","operand32","r","","" +"JGE rel32","JGE rel32","jge rel32","0F 8D cd","N.S.","V","","default64","r","","" +"JGE rel8","JGE rel8","jge rel8","7D cb","N.S.","V","","default64","r","","" +"JGE rel8","JGE rel8","jge rel8","7D cb","V","N.S.","","","r","","" +"JL rel16","JL rel16","jl rel16","0F 8C cw","V","N.S.","","operand16","r","","" +"JL rel32","JL rel32","jl rel32","0F 8C cd","V","N.S.","","operand32","r","","" +"JL rel32","JL rel32","jl rel32","0F 8C cd","N.S.","V","","default64","r","","" +"JL rel8","JL rel8","jl rel8","7C cb","V","N.S.","","","r","","" +"JL rel8","JL rel8","jl rel8","7C cb","N.S.","V","","default64","r","","" +"JLE rel16","JLE rel16","jle rel16","0F 8E cw","V","N.S.","","operand16","r","","" +"JLE rel32","JLE rel32","jle rel32","0F 8E cd","V","N.S.","","operand32","r","","" +"JLE rel32","JLE rel32","jle rel32","0F 8E cd","N.S.","V","","default64","r","","" +"JLE rel8","JLE rel8","jle rel8","7E cb","N.S.","V","","default64","r","","" +"JLE rel8","JLE rel8","jle rel8","7E cb","V","N.S.","","","r","","" +"JMP rel16","JMP rel16","jmp rel16","E9 cw","V","N.S.","","operand16","r","Y","" +"JMP rel32","JMP rel32","jmp rel32","E9 cd","N.S.","V","","default64","r","Y","" +"JMP rel32","JMP rel32","jmp rel32","E9 cd","V","N.S.","","operand32","r","Y","" +"JMP rel8","JMP rel8","jmp rel8","EB cb","N.S.","V","","default64","r","Y","" +"JMP rel8","JMP rel8","jmp rel8","EB cb","V","N.S.","","","r","Y","" +"JMP r/m32","JMPL* r/m32","jmpl* r/m32","FF /4","V","N.S.","","operand32","r","Y","32" +"JMP r/m64","JMPQ* r/m64","jmpq* r/m64","FF /4","N.S.","V","","","r","Y","64" +"JMP r/m16","JMPW* r/m16","jmpw* r/m16","FF /4","V","N.S.","","operand16","r","Y","16" +"JNA rel16","JNA rel16","jna rel16","0F 86 cw","V","N.S.","","pseudo","r","","" +"JNA rel32","JNA rel32","jna rel32","0F 86 cd","V","V","","pseudo","r","","" +"JNA rel8","JNA rel8","jna rel8","76 cb","V","V","","pseudo","r","","" +"JNAE rel16","JNAE rel16","jnae rel16","0F 82 cw","V","N.S.","","pseudo","r","","" +"JNAE rel32","JNAE rel32","jnae rel32","0F 82 cd","V","V","","pseudo","r","","" +"JNAE rel8","JNAE rel8","jnae rel8","72 cb","V","V","","pseudo","r","","" +"JNB rel16","JNB rel16","jnb rel16","0F 83 cw","V","N.S.","","pseudo","r","","" +"JNB rel32","JNB rel32","jnb rel32","0F 83 cd","V","V","","pseudo","r","","" +"JNB rel8","JNB rel8","jnb rel8","73 cb","V","V","","pseudo","r","","" +"JNBE rel16","JNBE rel16","jnbe rel16","0F 87 cw","V","N.S.","","pseudo","r","","" +"JNBE rel32","JNBE rel32","jnbe rel32","0F 87 cd","V","V","","pseudo","r","","" +"JNBE rel8","JNBE rel8","jnbe rel8","77 cb","V","V","","pseudo","r","","" +"JNC rel16","JNC rel16","jnc rel16","0F 83 cw","V","N.S.","","pseudo","r","","" +"JNC rel32","JNC rel32","jnc rel32","0F 83 cd","V","V","","pseudo","r","","" +"JNC rel8","JNC rel8","jnc rel8","73 cb","V","V","","pseudo","r","","" +"JNE rel16","JNE rel16","jne rel16","0F 85 cw","V","N.S.","","operand16","r","","" +"JNE rel32","JNE rel32","jne rel32","0F 85 cd","N.S.","V","","default64","r","","" +"JNE rel32","JNE rel32","jne rel32","0F 85 cd","V","N.S.","","operand32","r","","" +"JNE rel8","JNE rel8","jne rel8","75 cb","V","N.S.","","","r","","" +"JNE rel8","JNE rel8","jne rel8","75 cb","N.S.","V","","default64","r","","" +"JNG rel16","JNG rel16","jng rel16","0F 8E cw","V","N.S.","","pseudo","r","","" +"JNG rel32","JNG rel32","jng rel32","0F 8E cd","V","V","","pseudo","r","","" +"JNG rel8","JNG rel8","jng rel8","7E cb","V","V","","pseudo","r","","" +"JNGE rel16","JNGE rel16","jnge rel16","0F 8C cw","V","N.S.","","pseudo","r","","" +"JNGE rel32","JNGE rel32","jnge rel32","0F 8C cd","V","V","","pseudo","r","","" +"JNGE rel8","JNGE rel8","jnge rel8","7C cb","V","V","","pseudo","r","","" +"JNL rel16","JNL rel16","jnl rel16","0F 8D cw","V","N.S.","","pseudo","r","","" +"JNL rel32","JNL rel32","jnl rel32","0F 8D cd","V","V","","pseudo","r","","" +"JNL rel8","JNL rel8","jnl rel8","7D cb","V","V","","pseudo","r","","" +"JNLE rel16","JNLE rel16","jnle rel16","0F 8F cw","V","N.S.","","pseudo","r","","" +"JNLE rel32","JNLE rel32","jnle rel32","0F 8F cd","V","V","","pseudo","r","","" +"JNLE rel8","JNLE rel8","jnle rel8","7F cb","V","V","","pseudo","r","","" +"JNO rel16","JNO rel16","jno rel16","0F 81 cw","V","N.S.","","operand16","r","","" +"JNO rel32","JNO rel32","jno rel32","0F 81 cd","V","N.S.","","operand32","r","","" +"JNO rel32","JNO rel32","jno rel32","0F 81 cd","N.S.","V","","default64","r","","" +"JNO rel8","JNO rel8","jno rel8","71 cb","V","N.S.","","","r","","" +"JNO rel8","JNO rel8","jno rel8","71 cb","N.S.","V","","default64","r","","" +"JNP rel16","JNP rel16","jnp rel16","0F 8B cw","V","N.S.","","operand16","r","","" +"JNP rel32","JNP rel32","jnp rel32","0F 8B cd","V","N.S.","","operand32","r","","" +"JNP rel32","JNP rel32","jnp rel32","0F 8B cd","N.S.","V","","default64","r","","" +"JNP rel8","JNP rel8","jnp rel8","7B cb","N.S.","V","","default64","r","","" +"JNP rel8","JNP rel8","jnp rel8","7B cb","V","N.S.","","","r","","" +"JNS rel16","JNS rel16","jns rel16","0F 89 cw","V","N.S.","","operand16","r","","" +"JNS rel32","JNS rel32","jns rel32","0F 89 cd","N.S.","V","","default64","r","","" +"JNS rel32","JNS rel32","jns rel32","0F 89 cd","V","N.S.","","operand32","r","","" +"JNS rel8","JNS rel8","jns rel8","79 cb","V","N.S.","","","r","","" +"JNS rel8","JNS rel8","jns rel8","79 cb","N.S.","V","","default64","r","","" +"JNZ rel16","JNZ rel16","jnz rel16","0F 85 cw","V","N.S.","","pseudo","r","","" +"JNZ rel32","JNZ rel32","jnz rel32","0F 85 cd","V","V","","pseudo","r","","" +"JNZ rel8","JNZ rel8","jnz rel8","75 cb","V","V","","pseudo","r","","" +"JO rel16","JO rel16","jo rel16","0F 80 cw","V","N.S.","","operand16","r","","" +"JO rel32","JO rel32","jo rel32","0F 80 cd","V","N.S.","","operand32","r","","" +"JO rel32","JO rel32","jo rel32","0F 80 cd","N.S.","V","","default64","r","","" +"JO rel8","JO rel8","jo rel8","70 cb","V","N.S.","","","r","","" +"JO rel8","JO rel8","jo rel8","70 cb","N.S.","V","","default64","r","","" +"JP rel16","JP rel16","jp rel16","0F 8A cw","V","N.S.","","operand16","r","","" +"JP rel32","JP rel32","jp rel32","0F 8A cd","N.S.","V","","default64","r","","" +"JP rel32","JP rel32","jp rel32","0F 8A cd","V","N.S.","","operand32","r","","" +"JP rel8","JP rel8","jp rel8","7A cb","N.S.","V","","default64","r","","" +"JP rel8","JP rel8","jp rel8","7A cb","V","N.S.","","","r","","" +"JPE rel16","JPE rel16","jpe rel16","0F 8A cw","V","N.S.","","pseudo","r","","" +"JPE rel32","JPE rel32","jpe rel32","0F 8A cd","V","V","","pseudo","r","","" +"JPE rel8","JPE rel8","jpe rel8","7A cb","V","V","","pseudo","r","","" +"JPO rel16","JPO rel16","jpo rel16","0F 8B cw","V","N.S.","","pseudo","r","","" +"JPO rel32","JPO rel32","jpo rel32","0F 8B cd","V","V","","pseudo","r","","" +"JPO rel8","JPO rel8","jpo rel8","7B cb","V","V","","pseudo","r","","" +"JRCXZ rel8","JRCXZ rel8","jrcxz rel8","E3 cb","N.S.","V","","address64","r","","" +"JS rel16","JS rel16","js rel16","0F 88 cw","V","N.S.","","operand16","r","","" +"JS rel32","JS rel32","js rel32","0F 88 cd","V","N.S.","","operand32","r","","" +"JS rel32","JS rel32","js rel32","0F 88 cd","N.S.","V","","default64","r","","" +"JS rel8","JS rel8","js rel8","78 cb","V","N.S.","","","r","","" +"JS rel8","JS rel8","js rel8","78 cb","N.S.","V","","default64","r","","" +"JZ rel16","JZ rel16","jz rel16","0F 84 cw","V","N.S.","","operand16,pseudo","r","","" +"JZ rel32","JZ rel32","jz rel32","0F 84 cd","V","V","","operand32,pseudo","r","","" +"JZ rel8","JZ rel8","jz rel8","74 cb","V","V","","pseudo","r","","" +"KADDB k1, kV, k2","KADDB k2, kV, k1","kaddb k2, kV, k1","VEX.NDS.256.66.0F.W0 4A /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"KADDD k1, kV, k2","KADDD k2, kV, k1","kaddd k2, kV, k1","VEX.NDS.256.66.0F.W1 4A /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KADDQ k1, kV, k2","KADDQ k2, kV, k1","kaddq k2, kV, k1","VEX.NDS.256.0F.W1 4A /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KADDW k1, kV, k2","KADDW k2, kV, k1","kaddw k2, kV, k1","VEX.NDS.256.0F.W0 4A /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"KANDB k1, kV, k2","KANDB k2, kV, k1","kandb k2, kV, k1","VEX.NDS.256.66.0F.W0 41 /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"KANDD k1, kV, k2","KANDD k2, kV, k1","kandd k2, kV, k1","VEX.NDS.256.66.0F.W1 41 /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KANDNB k1, kV, k2","KANDNB k2, kV, k1","kandnb k2, kV, k1","VEX.NDS.256.66.0F.W0 42 /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"KANDND k1, kV, k2","KANDND k2, kV, k1","kandnd k2, kV, k1","VEX.NDS.256.66.0F.W1 42 /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KANDNQ k1, kV, k2","KANDNQ k2, kV, k1","kandnq k2, kV, k1","VEX.NDS.256.0F.W1 42 /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KANDNW k1, kV, k2","KANDNW k2, kV, k1","kandnw k2, kV, k1","VEX.NDS.256.0F.W0 42 /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"KANDQ k1, kV, k2","KANDQ k2, kV, k1","kandq k2, kV, k1","VEX.NDS.256.0F.W1 41 /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KANDW k1, kV, k2","KANDW k2, kV, k1","kandw k2, kV, k1","VEX.NDS.256.0F.W0 41 /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"KMOVB m8, k1","KMOVB k1, m8","kmovb k1, m8","VEX.128.66.0F.W0 91 /r","V","V","AVX512DQ","modrm_memonly","w,r","","" +"KMOVB r32, k2","KMOVB k2, r32","kmovb k2, r32","VEX.128.66.0F.W0 93 /r","V","V","AVX512DQ","modrm_regonly","w,r","","" +"KMOVB k1, k2/m8","KMOVB k2/m8, k1","kmovb k2/m8, k1","VEX.128.66.0F.W0 90 /r","V","V","AVX512DQ","","w,r","","" +"KMOVB k1, rmr32","KMOVB rmr32, k1","kmovb rmr32, k1","VEX.128.66.0F.W0 92 /r","V","V","AVX512DQ","modrm_regonly","w,r","","" +"KMOVD m32, k1","KMOVD k1, m32","kmovd k1, m32","VEX.128.66.0F.W1 91 /r","V","V","AVX512BW","modrm_memonly","w,r","","" +"KMOVD r32, k2","KMOVD k2, r32","kmovd k2, r32","VEX.128.F2.0F.W0 93 /r","V","V","AVX512BW","modrm_regonly","w,r","","" +"KMOVD k1, k2/m32","KMOVD k2/m32, k1","kmovd k2/m32, k1","VEX.128.66.0F.W1 90 /r","V","V","AVX512BW","","w,r","","" +"KMOVD k1, rmr32","KMOVD rmr32, k1","kmovd rmr32, k1","VEX.128.F2.0F.W0 92 /r","V","V","AVX512BW","modrm_regonly","w,r","","" +"KMOVQ m64, k1","KMOVQ k1, m64","kmovq k1, m64","VEX.128.0F.W1 91 /r","V","V","AVX512BW","modrm_memonly","w,r","","" +"KMOVQ r64, k2","KMOVQ k2, r64","kmovq k2, r64","VEX.128.F2.0F.W1 93 /r","N.S.","V","AVX512BW","modrm_regonly","w,r","","" +"KMOVQ k1, k2/m64","KMOVQ k2/m64, k1","kmovq k2/m64, k1","VEX.128.0F.W1 90 /r","V","V","AVX512BW","","w,r","","" +"KMOVQ k1, rmr64","KMOVQ rmr64, k1","kmovq rmr64, k1","VEX.128.F2.0F.W1 92 /r","N.S.","V","AVX512BW","modrm_regonly","w,r","","" +"KMOVW m16, k1","KMOVW k1, m16","kmovw k1, m16","VEX.128.0F.W0 91 /r","V","V","AVX512F","modrm_memonly","w,r","","" +"KMOVW r32, k2","KMOVW k2, r32","kmovw k2, r32","VEX.128.0F.W0 93 /r","V","V","AVX512F","modrm_regonly","w,r","","" +"KMOVW k1, k2/m16","KMOVW k2/m16, k1","kmovw k2/m16, k1","VEX.128.0F.W0 90 /r","V","V","AVX512F","","w,r","","" +"KMOVW k1, rmr32","KMOVW rmr32, k1","kmovw rmr32, k1","VEX.128.0F.W0 92 /r","V","V","AVX512F","modrm_regonly","w,r","","" +"KNOTB k1, k2","KNOTB k2, k1","knotb k2, k1","VEX.128.66.0F.W0 44 /r","V","V","AVX512DQ","modrm_regonly","w,r","","" +"KNOTD k1, k2","KNOTD k2, k1","knotd k2, k1","VEX.128.66.0F.W1 44 /r","V","V","AVX512BW","modrm_regonly","w,r","","" +"KNOTQ k1, k2","KNOTQ k2, k1","knotq k2, k1","VEX.128.0F.W1 44 /r","V","V","AVX512BW","modrm_regonly","w,r","","" +"KNOTW k1, k2","KNOTW k2, k1","knotw k2, k1","VEX.128.0F.W0 44 /r","V","V","AVX512F","modrm_regonly","w,r","","" +"KORB k1, kV, k2","KORB k2, kV, k1","korb k2, kV, k1","VEX.NDS.256.66.0F.W0 45 /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"KORD k1, kV, k2","KORD k2, kV, k1","kord k2, kV, k1","VEX.NDS.256.66.0F.W1 45 /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KORQ k1, kV, k2","KORQ k2, kV, k1","korq k2, kV, k1","VEX.NDS.256.0F.W1 45 /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KORTESTB k1, k2","KORTESTB k2, k1","kortestb k2, k1","VEX.128.66.0F.W0 98 /r","V","V","AVX512DQ","modrm_regonly","r,r","","" +"KORTESTD k1, k2","KORTESTD k2, k1","kortestd k2, k1","VEX.128.66.0F.W1 98 /r","V","V","AVX512BW","modrm_regonly","r,r","","" +"KORTESTQ k1, k2","KORTESTQ k2, k1","kortestq k2, k1","VEX.128.0F.W1 98 /r","V","V","AVX512BW","modrm_regonly","r,r","","" +"KORTESTW k1, k2","KORTESTW k2, k1","kortestw k2, k1","VEX.128.0F.W0 98 /r","V","V","AVX512F","modrm_regonly","r,r","","" +"KORW k1, kV, k2","KORW k2, kV, k1","korw k2, kV, k1","VEX.NDS.256.0F.W0 45 /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"KSHIFTLB k1, k2, imm8u","KSHIFTLB imm8u, k2, k1","kshiftlb imm8u, k2, k1","VEX.128.66.0F3A.W0 32 /r ib","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"KSHIFTLD k1, k2, imm8u","KSHIFTLD imm8u, k2, k1","kshiftld imm8u, k2, k1","VEX.128.66.0F3A.W0 33 /r ib","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KSHIFTLQ k1, k2, imm8u","KSHIFTLQ imm8u, k2, k1","kshiftlq imm8u, k2, k1","VEX.128.66.0F3A.W1 33 /r ib","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KSHIFTLW k1, k2, imm8u","KSHIFTLW imm8u, k2, k1","kshiftlw imm8u, k2, k1","VEX.128.66.0F3A.W1 32 /r ib","V","V","AVX512F","modrm_regonly","w,r,r","","" +"KSHIFTRB k1, k2, imm8u","KSHIFTRB imm8u, k2, k1","kshiftrb imm8u, k2, k1","VEX.128.66.0F3A.W0 30 /r ib","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"KSHIFTRD k1, k2, imm8u","KSHIFTRD imm8u, k2, k1","kshiftrd imm8u, k2, k1","VEX.128.66.0F3A.W0 31 /r ib","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KSHIFTRQ k1, k2, imm8u","KSHIFTRQ imm8u, k2, k1","kshiftrq imm8u, k2, k1","VEX.128.66.0F3A.W1 31 /r ib","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KSHIFTRW k1, k2, imm8u","KSHIFTRW imm8u, k2, k1","kshiftrw imm8u, k2, k1","VEX.128.66.0F3A.W1 30 /r ib","V","V","AVX512F","modrm_regonly","w,r,r","","" +"KTESTB k1, k2","KTESTB k2, k1","ktestb k2, k1","VEX.128.66.0F.W0 99 /r","V","V","AVX512DQ","modrm_regonly","r,r","","" +"KTESTD k1, k2","KTESTD k2, k1","ktestd k2, k1","VEX.128.66.0F.W1 99 /r","V","V","AVX512BW","modrm_regonly","r,r","","" +"KTESTQ k1, k2","KTESTQ k2, k1","ktestq k2, k1","VEX.128.0F.W1 99 /r","V","V","AVX512BW","modrm_regonly","r,r","","" +"KTESTW k1, k2","KTESTW k2, k1","ktestw k2, k1","VEX.128.0F.W0 99 /r","V","V","AVX512DQ","modrm_regonly","r,r","","" +"KUNPCKBW k1, kV, k2","KUNPCKBW k2, kV, k1","kunpckbw k2, kV, k1","VEX.NDS.256.66.0F.W0 4B /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"KUNPCKDQ k1, kV, k2","KUNPCKDQ k2, kV, k1","kunpckdq k2, kV, k1","VEX.NDS.256.0F.W1 4B /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KUNPCKWD k1, kV, k2","KUNPCKWD k2, kV, k1","kunpckwd k2, kV, k1","VEX.NDS.256.0F.W0 4B /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KXNORB k1, kV, k2","KXNORB k2, kV, k1","kxnorb k2, kV, k1","VEX.NDS.256.66.0F.W0 46 /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"KXNORD k1, kV, k2","KXNORD k2, kV, k1","kxnord k2, kV, k1","VEX.NDS.256.66.0F.W1 46 /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KXNORQ k1, kV, k2","KXNORQ k2, kV, k1","kxnorq k2, kV, k1","VEX.NDS.256.0F.W1 46 /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KXNORW k1, kV, k2","KXNORW k2, kV, k1","kxnorw k2, kV, k1","VEX.NDS.256.0F.W0 46 /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"KXORB k1, kV, k2","KXORB k2, kV, k1","kxorb k2, kV, k1","VEX.NDS.256.66.0F.W0 47 /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"KXORD k1, kV, k2","KXORD k2, kV, k1","kxord k2, kV, k1","VEX.NDS.256.66.0F.W1 47 /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KXORQ k1, kV, k2","KXORQ k2, kV, k1","kxorq k2, kV, k1","VEX.NDS.256.0F.W1 47 /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"KXORW k1, kV, k2","KXORW k2, kV, k1","kxorw k2, kV, k1","VEX.NDS.256.0F.W0 47 /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"LAHF","LAHF","lahf","9F","V","V","LAHFSAHF","","","","" +"LAR r32, r32/m16","LARL r32/m16, r32","larl r32/m16, r32","0F 02 /r","V","V","","operand32","rw,r","Y","32" +"LAR r64, r64/m16","LARQ r64/m16, r64","larq r64/m16, r64","REX.W 0F 02 /r","N.S.","V","","","rw,r","Y","64" +"LAR r16, r/m16","LARW r/m16, r16","larw r/m16, r16","0F 02 /r","V","V","","operand16","rw,r","Y","16" +"CALL_FAR ptr16:32","LCALLL ptr16:32","lcalll ptr16:32","9A cd iw","V","N.S.","","operand32","r","Y","" +"CALL_FAR m16:32","LCALLL* m16:32","lcalll* m16:32","FF /3","V","V","","modrm_memonly,operand32","r","Y","" +"CALL_FAR m16:64","LCALLQ* m16:64","lcallq* m16:64","REX.W FF /3","N.S.","V","","modrm_memonly","r","Y","" +"CALL_FAR ptr16:16","LCALLW ptr16:16","lcallw ptr16:16","9A cw iw","V","N.S.","","operand16","r","Y","" +"CALL_FAR m16:16","LCALLW* m16:16","lcallw* m16:16","FF /3","V","V","","modrm_memonly,operand16","r","Y","" +"LDDQU xmm1, m128","LDDQU m128, xmm1","lddqu m128, xmm1","F2 0F F0 /r","V","V","SSE3","modrm_memonly","w,r","","" +"LDMXCSR m32","LDMXCSR m32","ldmxcsr m32","0F AE /2","V","V","SSE","modrm_memonly","r","","" +"LDS r32, m16:32","LDSL m16:32, r32","ldsl m16:32, r32","C5 /r","V","N.S.","","modrm_memonly,operand32","w,r","Y","32" +"LDS r16, m16:16","LDSW m16:16, r16","ldsw m16:16, r16","C5 /r","V","N.S.","","modrm_memonly,operand16","w,r","Y","16" +"LEA r32, m","LEAL m, r32","leal m, r32","8D /r","V","V","","modrm_memonly,operand32","w,r","Y","32" +"LEA r64, m","LEAQ m, r64","leaq m, r64","REX.W 8D /r","N.S.","V","","modrm_memonly","w,r","Y","64" +"LEAVE","LEAVEW/LEAVEL/LEAVEQ","leavew/leavel/leaveq","C9","N.S.","V","","default64","","Y","" +"LEAVE","LEAVEW/LEAVEL/LEAVEQ","leavew/leavel/leaveq","C9","V","N.S.","","operand32","","Y","" +"LEAVE","LEAVEW/LEAVEL/LEAVEQ","leavew/leavel/leaveq","C9","V","V","","operand16","","Y","" +"LEA r16, m","LEAW m, r16","leaw m, r16","8D /r","V","V","","modrm_memonly,operand16","w,r","Y","16" +"LES r32, m16:32","LESL m16:32, r32","lesl m16:32, r32","C4 /r","V","N.S.","","modrm_memonly,operand32","w,r","Y","32" +"LES r16, m16:16","LESW m16:16, r16","lesw m16:16, r16","C4 /r","V","N.S.","","modrm_memonly,operand16","w,r","Y","16" +"LFENCE","LFENCE","lfence","0F AE /5","V","V","SSE2","","","","" +"LFS r32, m16:32","LFSL m16:32, r32","lfsl m16:32, r32","0F B4 /r","V","V","","modrm_memonly,operand32","w,r","Y","32" +"LFS r64, m16:64","LFSQ m16:64, r64","lfsq m16:64, r64","REX.W 0F B4 /r","N.S.","V","","modrm_memonly","w,r","Y","64" +"LFS r16, m16:16","LFSW m16:16, r16","lfsw m16:16, r16","0F B4 /r","V","V","","modrm_memonly,operand16","w,r","Y","16" +"LGDT m16&64","LGDT m16&64","lgdt m16&64","0F 01 /2","N.S.","V","","default64,modrm_memonly","r","","" +"LGDT m16&32","LGDTW/LGDTL m16&32","lgdtw/lgdtl m16&32","0F 01 /2","V","N.S.","","modrm_memonly","r","","" +"LGS r32, m16:32","LGSL m16:32, r32","lgsl m16:32, r32","0F B5 /r","V","V","","modrm_memonly,operand32","w,r","Y","32" +"LGS r64, m16:64","LGSQ m16:64, r64","lgsq m16:64, r64","REX.W 0F B5 /r","N.S.","V","","modrm_memonly","w,r","Y","64" +"LGS r16, m16:16","LGSW m16:16, r16","lgsw m16:16, r16","0F B5 /r","V","V","","modrm_memonly,operand16","w,r","Y","16" +"LIDT m16&64","LIDT m16&64","lidt m16&64","0F 01 /3","N.S.","V","","default64,modrm_memonly","r","","" +"LIDT m16&32","LIDTW/LIDTL m16&32","lidtw/lidtl m16&32","0F 01 /3","V","N.S.","","modrm_memonly","r","","" +"JMP_FAR ptr16:32","LJMPL ptr16:32","ljmpl ptr16:32","EA cd iw","V","N.S.","","operand32","r","Y","" +"JMP_FAR m16:32","LJMPL* m16:32","ljmpl* m16:32","FF /5","V","V","","modrm_memonly,operand32","r","Y","" +"JMP_FAR m16:64","LJMPQ* m16:64","ljmpq* m16:64","REX.W FF /5","N.S.","V","","modrm_memonly","r","Y","" +"JMP_FAR ptr16:16","LJMPW ptr16:16","ljmpw ptr16:16","EA cw iw","V","N.S.","","operand16","r","Y","" +"JMP_FAR m16:16","LJMPW* m16:16","ljmpw* m16:16","FF /5","V","V","","modrm_memonly,operand16","r","Y","" +"LLDT r/m16","LLDT r/m16","lldt r/m16","0F 00 /2","V","V","","","r","","" +"LLWPCB rmr32","LLWPCBL rmr32","llwpcbl rmr32","XOP.128.09.W0 12 /0","V","V","XOP","amd,modrm_regonly,operand16,operand32","w","Y","32" +"LLWPCB rmr64","LLWPCBQ rmr64","llwpcbq rmr64","XOP.128.09.W0 12 /0","N.S.","V","XOP","amd,modrm_regonly,operand64","w","Y","64" +"LMSW r/m16","LMSW r/m16","lmsw r/m16","0F 01 /6","V","V","","","r","","" +"LOCK","LOCK","lock","F0","V","V","","pseudo","","","" +"LODSB","LODSB","lodsb","AC","V","V","","","","","" +"LODSD","LODSL","lodsl","AD","V","V","","operand32","","","" +"LODSQ","LODSQ","lodsq","REX.W AD","N.S.","V","","","","","" +"LODSW","LODSW","lodsw","AD","V","V","","operand16","","","" +"LOOP rel8","LOOP rel8","loop rel8","E2 cb","V","V","","","r","","" +"LOOPE rel8","LOOPEQ rel8","loope rel8","E1 cb","V","V","","","r","","" +"LOOPNE rel8","LOOPNE rel8","loopne rel8","E0 cb","V","V","","","r","","" +"LSL r32, r32/m16","LSLL r32/m16, r32","lsll r32/m16, r32","0F 03 /r","V","V","","operand32","rw,r","Y","32" +"LSL r64, r32/m16","LSLQ r32/m16, r64","lslq r32/m16, r64","REX.W 0F 03 /r","N.S.","V","","","rw,r","Y","64" +"LSL r16, r/m16","LSLW r/m16, r16","lslw r/m16, r16","0F 03 /r","V","V","","operand16","rw,r","Y","16" +"LSS r32, m16:32","LSSL m16:32, r32","lssl m16:32, r32","0F B2 /r","V","V","","modrm_memonly,operand32","w,r","Y","32" +"LSS r64, m16:64","LSSQ m16:64, r64","lssq m16:64, r64","REX.W 0F B2 /r","N.S.","V","","modrm_memonly","w,r","Y","64" +"LSS r16, m16:16","LSSW m16:16, r16","lssw m16:16, r16","0F B2 /r","V","V","","modrm_memonly,operand16","w,r","Y","16" +"LTR r/m16","LTR r/m16","ltr r/m16","0F 00 /3","V","V","","","r","","" +"LWPINS r32V, r/m32, imm32u","LWPINS imm32u, r/m32, r32V","lwpins imm32u, r/m32, r32V","XOP.NDD.128.0A.W0 12 /0","V","V","XOP","amd,operand16,operand32","w,r,r","","" +"LWPINS r64V, r64/m32, imm32u","LWPINS imm32u, r64/m32, r64V","lwpins imm32u, r64/m32, r64V","XOP.NDD.128.0A.W0 12 /0","N.S.","V","XOP","amd,operand64","w,r,r","","" +"LWPVAL r32V, r/m32, imm32u","LWPVAL imm32u, r/m32, r32V","lwpval imm32u, r/m32, r32V","XOP.NDD.128.0A.W0 12 /1","V","V","XOP","amd,operand16,operand32","w,r,r","","" +"LWPVAL r64V, r64/m32, imm32u","LWPVAL imm32u, r64/m32, r64V","lwpval imm32u, r64/m32, r64V","XOP.NDD.128.0A.W0 12 /1","N.S.","V","XOP","amd,operand64","w,r,r","","" +"LZCNT r32, r/m32","LZCNTL r/m32, r32","lzcntl r/m32, r32","F3 0F BD /r","V","V","LZCNT","operand32","w,r","Y","32" +"LZCNT r32, r/m32","LZCNTL r/m32, r32","lzcntl r/m32, r32","F3 0F BD /r","V","V","AMD","amd,operand32","w,r","Y","32" +"LZCNT r64, r/m64","LZCNTQ r/m64, r64","lzcntq r/m64, r64","F3 REX.W 0F BD /r","N.S.","V","AMD","amd","w,r","Y","64" +"LZCNT r64, r/m64","LZCNTQ r/m64, r64","lzcntq r/m64, r64","F3 REX.W 0F BD /r","N.S.","V","LZCNT","","w,r","Y","64" +"LZCNT r16, r/m16","LZCNTW r/m16, r16","lzcntw r/m16, r16","F3 0F BD /r","V","V","AMD","amd,operand16","w,r","Y","16" +"LZCNT r16, r/m16","LZCNTW r/m16, r16","lzcntw r/m16, r16","F3 0F BD /r","V","V","LZCNT","operand16","w,r","Y","16" +"MASKMOVDQU xmm1, xmm2","MASKMOVOU xmm2, xmm1","maskmovdqu xmm2, xmm1","66 0F F7 /r","V","V","SSE2","modrm_regonly","r,r","","" +"MASKMOVQ mm1, mm2","MASKMOVQ mm2, mm1","maskmovq mm2, mm1","0F F7 /r","V","V","MMX","modrm_regonly","r,r","","" +"MAXPD xmm1, xmm2/m128","MAXPD xmm2/m128, xmm1","maxpd xmm2/m128, xmm1","66 0F 5F /r","V","V","SSE2","","rw,r","","" +"MAXPS xmm1, xmm2/m128","MAXPS xmm2/m128, xmm1","maxps xmm2/m128, xmm1","0F 5F /r","V","V","SSE","","rw,r","","" +"MAXSD xmm1, xmm2/m64","MAXSD xmm2/m64, xmm1","maxsd xmm2/m64, xmm1","F2 0F 5F /r","V","V","SSE2","","rw,r","","" +"MAXSS xmm1, xmm2/m32","MAXSS xmm2/m32, xmm1","maxss xmm2/m32, xmm1","F3 0F 5F /r","V","V","SSE","","rw,r","","" +"MFENCE","MFENCE","mfence","0F AE /6","V","V","SSE2","","","","" +"MINPD xmm1, xmm2/m128","MINPD xmm2/m128, xmm1","minpd xmm2/m128, xmm1","66 0F 5D /r","V","V","SSE2","","rw,r","","" +"MINPS xmm1, xmm2/m128","MINPS xmm2/m128, xmm1","minps xmm2/m128, xmm1","0F 5D /r","V","V","SSE","","rw,r","","" +"MINSD xmm1, xmm2/m64","MINSD xmm2/m64, xmm1","minsd xmm2/m64, xmm1","F2 0F 5D /r","V","V","SSE2","","rw,r","","" +"MINSS xmm1, xmm2/m32","MINSS xmm2/m32, xmm1","minss xmm2/m32, xmm1","F3 0F 5D /r","V","V","SSE","","rw,r","","" +"MONITOR","MONITOR","monitor","0F 01 C8","V","V","MONITOR","","","","" +"MOVAPD xmm2/m128, xmm1","MOVAPD xmm1, xmm2/m128","movapd xmm1, xmm2/m128","66 0F 29 /r","V","V","SSE2","","w,r","","" +"MOVAPD xmm1, xmm2/m128","MOVAPD xmm2/m128, xmm1","movapd xmm2/m128, xmm1","66 0F 28 /r","V","V","SSE2","","w,r","","" +"MOVAPS xmm2/m128, xmm1","MOVAPS xmm1, xmm2/m128","movaps xmm1, xmm2/m128","0F 29 /r","V","V","SSE","","w,r","","" +"MOVAPS xmm1, xmm2/m128","MOVAPS xmm2/m128, xmm1","movaps xmm2/m128, xmm1","0F 28 /r","V","V","SSE","","w,r","","" +"MOV r/m8, imm8u","MOVB imm8u, r/m8","movb imm8u, r/m8","C6 /0 ib","V","V","","","w,r","Y","8" +"MOV r/m8, imm8u","MOVB imm8u, r/m8","movb imm8u, r/m8","REX C6 /0 ib","N.E.","V","","pseudo64","w,r","Y","8" +"MOV r8op, imm8u","MOVB imm8u, r8op","movb imm8u, r8op","B0+rb ib","V","V","","","w,r","Y","8" +"MOV r8op, imm8u","MOVB imm8u, r8op","movb imm8u, r8op","REX B0+rb ib","N.E.","V","","pseudo64","w,r","Y","8" +"MOV r8, r/m8","MOVB r/m8, r8","movb r/m8, r8","8A /r","V","V","","","w,r","Y","8" +"MOV r8, r/m8","MOVB r/m8, r8","movb r/m8, r8","REX 8A /r","N.E.","V","","pseudo64","w,r","Y","8" +"MOV r/m8, r8","MOVB r8, r/m8","movb r8, r/m8","88 /r","V","V","","","w,r","Y","8" +"MOV r/m8, r8","MOVB r8, r/m8","movb r8, r/m8","REX 88 /r","N.E.","V","","pseudo64","w,r","Y","8" +"MOV moffs8, AL","MOVB/MOVB/MOVABSB AL, moffs8","movb/movb/movabsb AL, moffs8","A2 cm","V","V","","","w,r","Y","8" +"MOV moffs8, AL","MOVB/MOVB/MOVABSB AL, moffs8","movb/movb/movabsb AL, moffs8","REX.W A2 cm","N.E.","V","","pseudo","w,r","Y","8" +"MOV AL, moffs8","MOVB/MOVB/MOVABSB moffs8, AL","movb/movb/movabsb moffs8, AL","A0 cm","V","V","","","w,r","Y","8" +"MOV AL, moffs8","MOVB/MOVB/MOVABSB moffs8, AL","movb/movb/movabsb moffs8, AL","REX.W A0 cm","N.E.","V","","pseudo","w,r","Y","8" +"MOVBE r32, m32","MOVBELL m32, r32","movbell m32, r32","0F 38 F0 /r","V","V","MOVBE","modrm_memonly,operand32","w,r","Y","32" +"MOVBE m32, r32","MOVBELL r32, m32","movbell r32, m32","0F 38 F1 /r","V","V","MOVBE","modrm_memonly,operand32","w,r","Y","32" +"MOVBE r64, m64","MOVBEQQ m64, r64","movbeqq m64, r64","REX.W 0F 38 F0 /r","N.S.","V","MOVBE","modrm_memonly","w,r","Y","64" +"MOVBE m64, r64","MOVBEQQ r64, m64","movbeqq r64, m64","REX.W 0F 38 F1 /r","N.S.","V","MOVBE","modrm_memonly","w,r","Y","64" +"MOVBE r16, m16","MOVBEWW m16, r16","movbeww m16, r16","0F 38 F0 /r","V","V","MOVBE","modrm_memonly,operand16","w,r","Y","16" +"MOVBE m16, r16","MOVBEWW r16, m16","movbeww r16, m16","0F 38 F1 /r","V","V","MOVBE","modrm_memonly,operand16","w,r","Y","16" +"MOVSX r32, r/m8","MOVBLSX r/m8, r32","movsbl r/m8, r32","0F BE /r","V","V","","operand32","w,r","Y","32" +"MOVZX r32, r/m8","MOVBLZX r/m8, r32","movzbl r/m8, r32","0F B6 /r","V","V","","operand32","w,r","Y","32" +"MOVSX r64, r/m8","MOVBQSX r/m8, r64","movsbq r/m8, r64","REX.W 0F BE /r","N.S.","V","","","w,r","Y","64" +"MOVZX r64, r/m8","MOVBQZX r/m8, r64","movzbq r/m8, r64","REX.W 0F B6 /r","N.S.","V","","","w,r","Y","64" +"MOVSX r16, r/m8","MOVBWSX r/m8, r16","movsbw r/m8, r16","0F BE /r","V","V","","operand16","w,r","Y","16" +"MOVZX r16, r/m8","MOVBWZX r/m8, r16","movzbw r/m8, r16","0F B6 /r","V","V","","operand16","w,r","Y","16" +"MOVD r/m32, mm1","MOVD mm1, r/m32","movd mm1, r/m32","0F 7E /r","V","V","MMX","operand16,operand32","w,r","","" +"MOVD mm1, r/m32","MOVD r/m32, mm1","movd r/m32, mm1","0F 6E /r","V","V","MMX","operand16,operand32","w,r","","" +"MOVD xmm1, r/m32","MOVD r/m32, xmm1","movd r/m32, xmm1","66 0F 6E /r","V","V","SSE2","operand16,operand32","w,r","","" +"MOVD r/m32, xmm1","MOVD xmm1, r/m32","movd xmm1, r/m32","66 0F 7E /r","V","V","SSE2","operand16,operand32","w,r","","" +"MOVDDUP xmm1, xmm2/m64","MOVDDUP xmm2/m64, xmm1","movddup xmm2/m64, xmm1","F2 0F 12 /r","V","V","SSE3","","w,r","","" +"MOVHLPS xmm1, xmm2","MOVHLPS xmm2, xmm1","movhlps xmm2, xmm1","0F 12 /r","V","V","SSE","modrm_regonly","w,r","","" +"MOVHPD xmm1, m64","MOVHPD m64, xmm1","movhpd m64, xmm1","66 0F 16 /r","V","V","SSE2","modrm_memonly","w,r","","" +"MOVHPD m64, xmm1","MOVHPD xmm1, m64","movhpd xmm1, m64","66 0F 17 /r","V","V","SSE2","modrm_memonly","w,r","","" +"MOVHPS xmm1, m64","MOVHPS m64, xmm1","movhps m64, xmm1","0F 16 /r","V","V","SSE","modrm_memonly","w,r","","" +"MOVHPS m64, xmm1","MOVHPS xmm1, m64","movhps xmm1, m64","0F 17 /r","V","V","SSE","modrm_memonly","w,r","","" +"MOV rmr32, CR0-CR7","MOVL CR0-CR7, rmr32","movl CR0-CR7, rmr32","0F 20 /r","V","N.S.","","","w,r","Y","32" +"MOV rmr32, DR0-DR7","MOVL DR0-DR7, rmr32","movl DR0-DR7, rmr32","0F 21 /r","V","N.S.","","","w,r","Y","32" +"MOV moffs32, EAX","MOVL EAX, moffs32","movl EAX, moffs32","A3 cm","V","V","","operand32","w,r","Y","32" +"MOV r/m32, imm32","MOVL imm32, r/m32","movl imm32, r/m32","C7 /0 id","V","V","","operand32","w,r","Y","32" +"MOV r32op, imm32u","MOVL imm32u, r32op","movl imm32u, r32op","B8+rd id","V","V","","operand32","w,r","Y","32" +"MOV EAX, moffs32","MOVL moffs32, EAX","movl moffs32, EAX","A1 cm","V","V","","operand32","w,r","Y","32" +"MOV r32, r/m32","MOVL r/m32, r32","movl r/m32, r32","8B /r","V","V","","operand32","w,r","Y","32" +"MOV r/m32, r32","MOVL r32, r/m32","movl r32, r/m32","89 /r","V","V","","operand32","w,r","Y","32" +"MOV CR0-CR7, rmr32","MOVL rmr32, CR0-CR7","movl rmr32, CR0-CR7","0F 22 /r","V","N.S.","","","w,r","Y","32" +"MOV DR0-DR7, rmr32","MOVL rmr32, DR0-DR7","movl rmr32, DR0-DR7","0F 23 /r","V","N.S.","","","w,r","Y","32" +"MOVLHPS xmm1, xmm2","MOVLHPS xmm2, xmm1","movlhps xmm2, xmm1","0F 16 /r","V","V","SSE","modrm_regonly","w,r","","" +"MOVLPD xmm1, m64","MOVLPD m64, xmm1","movlpd m64, xmm1","66 0F 12 /r","V","V","SSE2","modrm_memonly","w,r","","" +"MOVLPD m64, xmm1","MOVLPD xmm1, m64","movlpd xmm1, m64","66 0F 13 /r","V","V","SSE2","modrm_memonly","w,r","","" +"MOVLPS xmm1, m64","MOVLPS m64, xmm1","movlps m64, xmm1","0F 12 /r","V","V","SSE","modrm_memonly","w,r","","" +"MOVLPS m64, xmm1","MOVLPS xmm1, m64","movlps xmm1, m64","0F 13 /r","V","V","SSE","modrm_memonly","w,r","","" +"MOVSXD r32, r/m32","MOVLQSX r/m32, r32","movsxdl r/m32, r32","63 /r","N.S.","V","","operand32","w,r","Y","32" +"MOVSXD r64, r/m32","MOVLQSX r/m32, r64","movslq r/m32, r64","REX.W 63 /r","N.S.","V","","","w,r","Y","64" +"MOVMSKPD r32, xmm2","MOVMSKPD xmm2, r32","movmskpd xmm2, r32","66 0F 50 /r","V","V","SSE2","modrm_regonly","w,r","","" +"MOVMSKPS r32, xmm2","MOVMSKPS xmm2, r32","movmskps xmm2, r32","0F 50 /r","V","V","SSE","modrm_regonly","w,r","","" +"MOVNTDQA xmm1, m128","MOVNTDQA m128, xmm1","movntdqa m128, xmm1","66 0F 38 2A /r","V","V","SSE4_1","modrm_memonly","w,r","","" +"MOVNTI m32, r32","MOVNTIL r32, m32","movntil r32, m32","0F C3 /r","V","V","SSE2","modrm_memonly,operand16,operand32","w,r","Y","32" +"MOVNTI m64, r64","MOVNTIQ r64, m64","movntiq r64, m64","REX.W 0F C3 /r","N.S.","V","SSE2","modrm_memonly","w,r","Y","64" +"MOVNTDQ m128, xmm1","MOVNTO xmm1, m128","movntdq xmm1, m128","66 0F E7 /r","V","V","SSE2","modrm_memonly","w,r","","" +"MOVNTPD m128, xmm1","MOVNTPD xmm1, m128","movntpd xmm1, m128","66 0F 2B /r","V","V","SSE2","modrm_memonly","w,r","","" +"MOVNTPS m128, xmm1","MOVNTPS xmm1, m128","movntps xmm1, m128","0F 2B /r","V","V","SSE","modrm_memonly","w,r","","" +"MOVNTQ m64, mm1","MOVNTQ mm1, m64","movntq mm1, m64","0F E7 /r","V","V","MMX","modrm_memonly","w,r","","" +"MOVNTSD m64, xmm1","MOVNTSD xmm1, m64","movntsd xmm1, m64","F2 0F 2B /r","V","V","SSE4a","amd,modrm_memonly","w,r","","" +"MOVNTSS m32, xmm1","MOVNTSS xmm1, m32","movntss xmm1, m32","F3 0F 2B /r","V","V","SSE4a","amd,modrm_memonly","w,r","","" +"MOVDQA xmm2/m128, xmm1","MOVO xmm1, xmm2/m128","movdqa xmm1, xmm2/m128","66 0F 7F /r","V","V","SSE2","","w,r","","" +"MOVDQA xmm1, xmm2/m128","MOVO xmm2/m128, xmm1","movdqa xmm2/m128, xmm1","66 0F 6F /r","V","V","SSE2","","w,r","","" +"MOVDQU xmm2/m128, xmm1","MOVOU xmm1, xmm2/m128","movdqu xmm1, xmm2/m128","F3 0F 7F /r","V","V","SSE2","","w,r","","" +"MOVDQU xmm1, xmm2/m128","MOVOU xmm2/m128, xmm1","movdqu xmm2/m128, xmm1","F3 0F 6F /r","V","V","SSE2","","w,r","","" +"MOV rmr64, CR0-CR7","MOVQ CR0-CR7, rmr64","movq CR0-CR7, rmr64","0F 20 /r","N.S.","V","","default64","w,r","Y","64" +"MOV rmr64, CR8","MOVQ CR8, rmr64","movq CR8, rmr64","REX.R + 0F 20 /0","N.E.","V","","modrm_regonly,pseudo","w,r","Y","64" +"MOV rmr64, DR0-DR7","MOVQ DR0-DR7, rmr64","movq DR0-DR7, rmr64","0F 21 /r","N.S.","V","","default64","w,r","Y","64" +"MOV moffs64, RAX","MOVQ RAX, moffs64","movabsq RAX, moffs64","REX.W A3 cm","N.S.","V","","","w,r","Y","64" +"MOV r/m64, imm32","MOVQ imm32, r/m64","movq imm32, r/m64","REX.W C7 /0 id","N.S.","V","","","w,r","Y","64" +"MOV r64op, imm64u","MOVQ imm64u, r64op","movq imm64u, r64op","REX.W B8+ro io","N.S.","V","","","w,r","Y","64" +"MOVQ mm2/m64, mm1","MOVQ mm1, mm2/m64","movq mm1, mm2/m64","0F 7F /r","V","V","MMX","","w,r","","" +"MOVQ r/m64, mm1","MOVQ mm1, r/m64","movq mm1, r/m64","REX.W 0F 7E /r","N.S.","V","MMX","","w,r","","" +"MOVQ mm1, mm2/m64","MOVQ mm2/m64, mm1","movq mm2/m64, mm1","0F 6F /r","V","V","MMX","","w,r","","" +"MOV RAX, moffs64","MOVQ moffs64, RAX","movabsq moffs64, RAX","REX.W A1 cm","N.S.","V","","","w,r","Y","64" +"MOVQ mm1, r/m64","MOVQ r/m64, mm1","movq r/m64, mm1","REX.W 0F 6E /r","N.S.","V","MMX","","w,r","","" +"MOV r64, r/m64","MOVQ r/m64, r64","movq r/m64, r64","REX.W 8B /r","N.S.","V","","","w,r","Y","64" +"MOVQ xmm1, r/m64","MOVQ r/m64, xmm1","movq r/m64, xmm1","66 REX.W 0F 6E /r","N.S.","V","SSE2","","w,r","","" +"MOV r/m64, r64","MOVQ r64, r/m64","movq r64, r/m64","REX.W 89 /r","N.S.","V","","","w,r","Y","64" +"MOV CR0-CR7, rmr64","MOVQ rmr64, CR0-CR7","movq rmr64, CR0-CR7","0F 22 /r","N.S.","V","","default64","w,r","Y","64" +"MOV CR8, rmr64","MOVQ rmr64, CR8","movq rmr64, CR8","REX.R + 0F 22 /0","N.E.","V","","modrm_regonly,pseudo","w,r","Y","64" +"MOV DR0-DR7, rmr64","MOVQ rmr64, DR0-DR7","movq rmr64, DR0-DR7","0F 23 /r","N.S.","V","","default64","w,r","Y","64" +"MOVQ r/m64, xmm1","MOVQ xmm1, r/m64","movq xmm1, r/m64","66 REX.W 0F 7E /r","N.S.","V","SSE2","","w,r","","" +"MOVQ xmm2/m64, xmm1","MOVQ xmm1, xmm2/m64","movq xmm1, xmm2/m64","66 0F D6 /r","V","V","SSE2","","w,r","","" +"MOVDQ2Q mm1, xmm2","MOVQ xmm2, mm1","movdq2q xmm2, mm1","F2 0F D6 /r","V","V","SSE2","modrm_regonly","w,r","","" +"MOVQ xmm1, xmm2/m64","MOVQ xmm2/m64, xmm1","movq xmm2/m64, xmm1","F3 0F 7E /r","V","V","SSE2","","w,r","","" +"MOVQ2DQ xmm1, mm2","MOVQOZX mm2, xmm1","movq2dq mm2, xmm1","F3 0F D6 /r","V","V","SSE2","modrm_regonly","w,r","","" +"MOVSB","MOVSB","movsb","A4","V","V","","","","","" +"MOVSD xmm2/m64, xmm1","MOVSD xmm1, xmm2/m64","movsd xmm1, xmm2/m64","F2 0F 11 /r","V","V","SSE2","","w,r","","" +"MOVSD xmm1, xmm2/m64","MOVSD xmm2/m64, xmm1","movsd xmm2/m64, xmm1","F2 0F 10 /r","V","V","SSE2","","w,r","","" +"MOVSHDUP xmm1, xmm2/m128","MOVSHDUP xmm2/m128, xmm1","movshdup xmm2/m128, xmm1","F3 0F 16 /r","V","V","SSE3","","w,r","","" +"MOVSD","MOVSL","movsl","A5","V","V","","operand32","","","" +"MOVSLDUP xmm1, xmm2/m128","MOVSLDUP xmm2/m128, xmm1","movsldup xmm2/m128, xmm1","F3 0F 12 /r","V","V","SSE3","","w,r","","" +"MOVSQ","MOVSQ","movsq","REX.W A5","N.S.","V","","","","","" +"MOVSS xmm2/m32, xmm1","MOVSS xmm1, xmm2/m32","movss xmm1, xmm2/m32","F3 0F 11 /r","V","V","SSE","","w,r","","" +"MOVSS xmm1, xmm2/m32","MOVSS xmm2/m32, xmm1","movss xmm2/m32, xmm1","F3 0F 10 /r","V","V","SSE","","w,r","","" +"MOVSW","MOVSW","movsw","A5","V","V","","operand16","","","" +"MOVSX r16, r/m16","MOVSWW r/m16, r16","movsww r/m16, r16","0F BF /r","V","V","","operand16","w,r","Y","16" +"MOVUPD xmm2/m128, xmm1","MOVUPD xmm1, xmm2/m128","movupd xmm1, xmm2/m128","66 0F 11 /r","V","V","SSE2","","w,r","","" +"MOVUPD xmm1, xmm2/m128","MOVUPD xmm2/m128, xmm1","movupd xmm2/m128, xmm1","66 0F 10 /r","V","V","SSE2","","w,r","","" +"MOVUPS xmm2/m128, xmm1","MOVUPS xmm1, xmm2/m128","movups xmm1, xmm2/m128","0F 11 /r","V","V","SSE","","w,r","","" +"MOVUPS xmm1, xmm2/m128","MOVUPS xmm2/m128, xmm1","movups xmm2/m128, xmm1","0F 10 /r","V","V","SSE","","w,r","","" +"MOV moffs16, AX","MOVW AX, moffs16","movw AX, moffs16","A3 cm","V","V","","operand16","w,r","Y","16" +"MOV r/m16, Sreg","MOVW Sreg, r/m16","movw Sreg, r/m16","8C /r","V","V","","operand16","w,r","Y","16" +"MOV r/m16, imm16","MOVW imm16, r/m16","movw imm16, r/m16","C7 /0 iw","V","V","","operand16","w,r","Y","16" +"MOV r16op, imm16u","MOVW imm16u, r16op","movw imm16u, r16op","B8+rw iw","V","V","","operand16","w,r","Y","16" +"MOV AX, moffs16","MOVW moffs16, AX","movw moffs16, AX","A1 cm","V","V","","operand16","w,r","Y","16" +"MOV Sreg, r/m16","MOVW r/m16, Sreg","movw r/m16, Sreg","8E /r","V","V","","","w,r","Y","16" +"MOV r16, r/m16","MOVW r/m16, r16","movw r/m16, r16","8B /r","V","V","","operand16","w,r","Y","16" +"MOV r/m16, r16","MOVW r16, r/m16","movw r16, r/m16","89 /r","V","V","","operand16","w,r","Y","16" +"MOVSX r32, r/m16","MOVWLSX r/m16, r32","movswl r/m16, r32","0F BF /r","V","V","","operand32","w,r","Y","32" +"MOVZX r32, r/m16","MOVWLZX r/m16, r32","movzwl r/m16, r32","0F B7 /r","V","V","","operand32","w,r","Y","32" +"MOVSX r64, r/m16","MOVWQSX r/m16, r64","movswq r/m16, r64","REX.W 0F BF /r","N.S.","V","","","w,r","Y","64" +"MOVSXD r16, r/m32","MOVWQSX r/m32, r16","movsxdw r/m32, r16","63 /r","N.S.","V","","operand16","w,r","Y","16" +"MOVZX r64, r/m16","MOVWQZX r/m16, r64","movzwq r/m16, r64","REX.W 0F B7 /r","N.S.","V","","","w,r","Y","64" +"MOVZX r16, r/m16","MOVZWW r/m16, r16","movzww r/m16, r16","0F B7 /r","V","V","","operand16","w,r","Y","16" +"MOV r32/m16, Sreg","MOV{L/W} Sreg, r32/m16","mov{l/w} Sreg, r32/m16","8C /r","V","V","","operand32","w,r","Y","" +"MOV r64/m16, Sreg","MOV{Q/W} Sreg, r64/m16","mov{q/w} Sreg, r64/m16","REX.W 8C /r","N.S.","V","","","w,r","Y","" +"MPSADBW xmm1, xmm2/m128, imm8u","MPSADBW imm8u, xmm2/m128, xmm1","mpsadbw imm8u, xmm2/m128, xmm1","66 0F 3A 42 /r ib","V","V","SSE4_1","","rw,r,r","","" +"MUL r/m8","MULB r/m8","mulb r/m8","F6 /4","V","V","","","r","Y","8" +"MUL r/m8","MULB r/m8","mulb r/m8","REX F6 /4","N.E.","V","","pseudo64","r","Y","8" +"MUL r/m32","MULL r/m32","mull r/m32","F7 /4","V","V","","operand32","r","Y","32" +"MULPD xmm1, xmm2/m128","MULPD xmm2/m128, xmm1","mulpd xmm2/m128, xmm1","66 0F 59 /r","V","V","SSE2","","rw,r","","" +"MULPS xmm1, xmm2/m128","MULPS xmm2/m128, xmm1","mulps xmm2/m128, xmm1","0F 59 /r","V","V","SSE","","rw,r","","" +"MUL r/m64","MULQ r/m64","mulq r/m64","REX.W F7 /4","N.S.","V","","","r","Y","64" +"MULSD xmm1, xmm2/m64","MULSD xmm2/m64, xmm1","mulsd xmm2/m64, xmm1","F2 0F 59 /r","V","V","SSE2","","rw,r","","" +"MULSS xmm1, xmm2/m32","MULSS xmm2/m32, xmm1","mulss xmm2/m32, xmm1","F3 0F 59 /r","V","V","SSE","","rw,r","","" +"MUL r/m16","MULW r/m16","mulw r/m16","F7 /4","V","V","","operand16","r","Y","16" +"MULX r32, r32V, r/m32","MULXL r/m32, r32V, r32","mulxl r/m32, r32V, r32","VEX.NDD.128.F2.0F38.W0 F6 /r","V","V","BMI2","","w,w,r","Y","32" +"MULX r64, r64V, r/m64","MULXQ r/m64, r64V, r64","mulxq r/m64, r64V, r64","VEX.NDD.128.F2.0F38.W1 F6 /r","N.S.","V","BMI2","","w,w,r","Y","64" +"MWAIT","MWAIT","mwait","0F 01 C9","V","V","MONITOR","","","","" +"NEG r/m8","NEGB r/m8","negb r/m8","F6 /3","V","V","","","rw","Y","8" +"NEG r/m8","NEGB r/m8","negb r/m8","REX F6 /3","N.E.","V","","pseudo64","rw","Y","8" +"NEG r/m32","NEGL r/m32","negl r/m32","F7 /3","V","V","","operand32","rw","Y","32" +"NEG r/m64","NEGQ r/m64","negq r/m64","REX.W F7 /3","N.S.","V","","","rw","Y","64" +"NEG r/m16","NEGW r/m16","negw r/m16","F7 /3","V","V","","operand16","rw","Y","16" +"NOP","NOP","nop","90","V","V","","pseudo","","Y","" +"NOP","NOP","nop","90+rd","V","V","","operand32,operand64","","Y","" +"NOP","NOP","nop","90+rw","V","V","","operand16,operand64","","Y","" +"NOP","NOP","nop","F3 90+rd","V","V","","operand32","","Y","" +"NOP","NOP","nop","F3 90+rw","V","V","","operand16","","Y","" +"NOP r/m32","NOPL r/m32","nopl r/m32","0F 18 /4","V","V","","operand32","r","Y","32" +"NOP r/m32","NOPL r/m32","nopl r/m32","0F 18 /5","V","V","","operand32","r","Y","32" +"NOP r/m32","NOPL r/m32","nopl r/m32","0F 18 /6","V","V","","operand32","r","Y","32" +"NOP r/m32","NOPL r/m32","nopl r/m32","0F 18 /7","V","V","","operand32","r","Y","32" +"NOP r/m32, r32","NOPL r32, r/m32","nopl r32, r/m32","0F 19 /r","V","V","","operand32","r,r","Y","32" +"NOP r/m32, r32","NOPL r32, r/m32","nopl r32, r/m32","0F 1A /r","V","V","","operand32","r,r","Y","32" +"NOP r/m32, r32","NOPL r32, r/m32","nopl r32, r/m32","0F 1B /r","V","V","","operand32","r,r","Y","32" +"NOP r/m32, r32","NOPL r32, r/m32","nopl r32, r/m32","0F 1C /r","V","V","","operand32","r,r","Y","32" +"NOP r/m32, r32","NOPL r32, r/m32","nopl r32, r/m32","0F 1D /r","V","V","","operand32","r,r","Y","32" +"NOP r/m32, r32","NOPL r32, r/m32","nopl r32, r/m32","0F 1E /r","V","V","PPRO","operand32","r,r","Y","32" +"NOP r/m32, r32","NOPL r32, r/m32","nopl r32, r/m32","0F 1E /r","V","V","","operand32","r,r","Y","32" +"NOP r/m32, r32","NOPL r32, r/m32","nopl r32, r/m32","0F 1F /r","V","V","","operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","0F 0D /r","V","V","PRFCHW","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","0F 1A /r","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","0F 1B /r","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","66 0F 1E /r","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F2 0F 1E /r","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1B /r","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E /0","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E /1","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E /2","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E /3","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E /4","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E /5","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E /6","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E F8","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E F9","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E FA","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E FB","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E FC","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E FD","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E FE","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32, r32","NOPL r32, rmr32","nopl r32, rmr32","F3 0F 1E FF","V","V","PPRO","modrm_regonly,operand32","r,r","Y","32" +"NOP rmr32","NOPL rmr32","nopl rmr32","0F 18 /0","V","V","","modrm_regonly,operand32","r","Y","32" +"NOP rmr32","NOPL rmr32","nopl rmr32","0F 18 /1","V","V","","modrm_regonly,operand32","r","Y","32" +"NOP rmr32","NOPL rmr32","nopl rmr32","0F 18 /2","V","V","","modrm_regonly,operand32","r","Y","32" +"NOP rmr32","NOPL rmr32","nopl rmr32","0F 18 /3","V","V","","modrm_regonly,operand32","r","Y","32" +"NOP r/m64","NOPQ r/m64","nopq r/m64","REX.W 0F 18 /4","N.S.","V","","","r","Y","64" +"NOP r/m64","NOPQ r/m64","nopq r/m64","REX.W 0F 18 /5","N.S.","V","","","r","Y","64" +"NOP r/m64","NOPQ r/m64","nopq r/m64","REX.W 0F 18 /6","N.S.","V","","","r","Y","64" +"NOP r/m64","NOPQ r/m64","nopq r/m64","REX.W 0F 18 /7","N.S.","V","","","r","Y","64" +"NOP r/m64, r64","NOPQ r64, r/m64","nopq r64, r/m64","REX.W 0F 19 /r","N.S.","V","","","r,r","Y","64" +"NOP r/m64, r64","NOPQ r64, r/m64","nopq r64, r/m64","REX.W 0F 1A /r","N.S.","V","","","r,r","Y","64" +"NOP r/m64, r64","NOPQ r64, r/m64","nopq r64, r/m64","REX.W 0F 1B /r","N.S.","V","","","r,r","Y","64" +"NOP r/m64, r64","NOPQ r64, r/m64","nopq r64, r/m64","REX.W 0F 1C /r","N.S.","V","","","r,r","Y","64" +"NOP r/m64, r64","NOPQ r64, r/m64","nopq r64, r/m64","REX.W 0F 1D /r","N.S.","V","","","r,r","Y","64" +"NOP r/m64, r64","NOPQ r64, r/m64","nopq r64, r/m64","REX.W 0F 1E /r","N.S.","V","","","r,r","Y","64" +"NOP r/m64, r64","NOPQ r64, r/m64","nopq r64, r/m64","REX.W 0F 1E /r","N.S.","V","PPRO","","r,r","Y","64" +"NOP r/m64, r64","NOPQ r64, r/m64","nopq r64, r/m64","REX.W 0F 1F /r","N.S.","V","","","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","66 REX.W 0F 1E /r","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F2 REX.W 0F 1E /r","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1B /r","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E /0","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E /1","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E /2","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E /3","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E /4","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E /5","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E /6","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E F8","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E F9","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E FA","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E FB","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E FC","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E FD","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E FE","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","F3 REX.W 0F 1E FF","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","REX.W 0F 0D /r","N.S.","V","PRFCHW","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","REX.W 0F 1A /r","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64, r64","NOPQ r64, rmr64","nopq r64, rmr64","REX.W 0F 1B /r","N.S.","V","PPRO","modrm_regonly","r,r","Y","64" +"NOP rmr64","NOPQ rmr64","nopq rmr64","REX.W 0F 18 /0","N.S.","V","","modrm_regonly","r","Y","64" +"NOP rmr64","NOPQ rmr64","nopq rmr64","REX.W 0F 18 /1","N.S.","V","","modrm_regonly","r","Y","64" +"NOP rmr64","NOPQ rmr64","nopq rmr64","REX.W 0F 18 /2","N.S.","V","","modrm_regonly","r","Y","64" +"NOP rmr64","NOPQ rmr64","nopq rmr64","REX.W 0F 18 /3","N.S.","V","","modrm_regonly","r","Y","64" +"NOP r/m16","NOPW r/m16","nopw r/m16","0F 18 /4","V","V","","operand16","r","Y","16" +"NOP r/m16","NOPW r/m16","nopw r/m16","0F 18 /5","V","V","","operand16","r","Y","16" +"NOP r/m16","NOPW r/m16","nopw r/m16","0F 18 /6","V","V","","operand16","r","Y","16" +"NOP r/m16","NOPW r/m16","nopw r/m16","0F 18 /7","V","V","","operand16","r","Y","16" +"NOP r/m16, r16","NOPW r16, r/m16","nopw r16, r/m16","0F 19 /r","V","V","","operand16","r,r","Y","16" +"NOP r/m16, r16","NOPW r16, r/m16","nopw r16, r/m16","0F 1A /r","V","V","","operand16","r,r","Y","16" +"NOP r/m16, r16","NOPW r16, r/m16","nopw r16, r/m16","0F 1B /r","V","V","","operand16","r,r","Y","16" +"NOP r/m16, r16","NOPW r16, r/m16","nopw r16, r/m16","0F 1C /r","V","V","","operand16","r,r","Y","16" +"NOP r/m16, r16","NOPW r16, r/m16","nopw r16, r/m16","0F 1D /r","V","V","","operand16","r,r","Y","16" +"NOP r/m16, r16","NOPW r16, r/m16","nopw r16, r/m16","0F 1E /r","V","V","","operand16","r,r","Y","16" +"NOP r/m16, r16","NOPW r16, r/m16","nopw r16, r/m16","0F 1E /r","V","V","PPRO","operand16","r,r","Y","16" +"NOP r/m16, r16","NOPW r16, r/m16","nopw r16, r/m16","0F 1F /r","V","V","","operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","0F 0D /r","V","V","PRFCHW","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","0F 1A /r","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","0F 1B /r","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","66 0F 1E /r","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F2 0F 1E /r","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1B /r","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E /0","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E /1","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E /2","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E /3","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E /4","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E /5","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E /6","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E F8","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E F9","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E FA","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E FB","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E FC","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E FD","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E FE","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16, r16","NOPW r16, rmr16","nopw r16, rmr16","F3 0F 1E FF","V","V","PPRO","modrm_regonly,operand16","r,r","Y","16" +"NOP rmr16","NOPW rmr16","nopw rmr16","0F 18 /0","V","V","","modrm_regonly,operand16","r","Y","16" +"NOP rmr16","NOPW rmr16","nopw rmr16","0F 18 /1","V","V","","modrm_regonly,operand16","r","Y","16" +"NOP rmr16","NOPW rmr16","nopw rmr16","0F 18 /2","V","V","","modrm_regonly,operand16","r","Y","16" +"NOP rmr16","NOPW rmr16","nopw rmr16","0F 18 /3","V","V","","modrm_regonly,operand16","r","Y","16" +"NOT r/m8","NOTB r/m8","notb r/m8","F6 /2","V","V","","","rw","Y","8" +"NOT r/m8","NOTB r/m8","notb r/m8","REX F6 /2","N.E.","V","","pseudo64","rw","Y","8" +"NOT r/m32","NOTL r/m32","notl r/m32","F7 /2","V","V","","operand32","rw","Y","32" +"NOT r/m64","NOTQ r/m64","notq r/m64","REX.W F7 /2","N.S.","V","","","rw","Y","64" +"NOT r/m16","NOTW r/m16","notw r/m16","F7 /2","V","V","","operand16","rw","Y","16" +"OR r/m8, imm8","ORB imm8, r/m8","orb imm8, r/m8","80 /1 ib","V","V","","","rw,r","Y","8" +"OR r/m8, imm8","ORB imm8, r/m8","orb imm8, r/m8","82 /1 ib","V","N.S.","","","rw,r","Y","8" +"OR r/m8, imm8","ORB imm8, r/m8","orb imm8, r/m8","REX 80 /1 ib","N.E.","V","","pseudo64","rw,r","Y","8" +"OR AL, imm8u","ORB imm8u, AL","orb imm8u, AL","0C ib","V","V","","","rw,r","Y","8" +"OR r8, r/m8","ORB r/m8, r8","orb r/m8, r8","0A /r","V","V","","","rw,r","Y","8" +"OR r8, r/m8","ORB r/m8, r8","orb r/m8, r8","REX 0A /r","N.E.","V","","pseudo64","rw,r","Y","8" +"OR r/m8, r8","ORB r8, r/m8","orb r8, r/m8","08 /r","V","V","","","rw,r","Y","8" +"OR r/m8, r8","ORB r8, r/m8","orb r8, r/m8","REX 08 /r","N.E.","V","","pseudo64","rw,r","Y","8" +"OR EAX, imm32","ORL imm32, EAX","orl imm32, EAX","0D id","V","V","","operand32","rw,r","Y","32" +"OR r/m32, imm32","ORL imm32, r/m32","orl imm32, r/m32","81 /1 id","V","V","","operand32","rw,r","Y","32" +"OR r/m32, imm8","ORL imm8, r/m32","orl imm8, r/m32","83 /1 ib","V","V","","operand32","rw,r","Y","32" +"OR r32, r/m32","ORL r/m32, r32","orl r/m32, r32","0B /r","V","V","","operand32","rw,r","Y","32" +"OR r/m32, r32","ORL r32, r/m32","orl r32, r/m32","09 /r","V","V","","operand32","rw,r","Y","32" +"ORPD xmm1, xmm2/m128","ORPD xmm2/m128, xmm1","orpd xmm2/m128, xmm1","66 0F 56 /r","V","V","SSE2","","rw,r","","" +"ORPS xmm1, xmm2/m128","ORPS xmm2/m128, xmm1","orps xmm2/m128, xmm1","0F 56 /r","V","V","SSE","","rw,r","","" +"OR RAX, imm32","ORQ imm32, RAX","orq imm32, RAX","REX.W 0D id","N.S.","V","","","rw,r","Y","64" +"OR r/m64, imm32","ORQ imm32, r/m64","orq imm32, r/m64","REX.W 81 /1 id","N.S.","V","","","rw,r","Y","64" +"OR r/m64, imm8","ORQ imm8, r/m64","orq imm8, r/m64","REX.W 83 /1 ib","N.S.","V","","","rw,r","Y","64" +"OR r64, r/m64","ORQ r/m64, r64","orq r/m64, r64","REX.W 0B /r","N.S.","V","","","rw,r","Y","64" +"OR r/m64, r64","ORQ r64, r/m64","orq r64, r/m64","REX.W 09 /r","N.S.","V","","","rw,r","Y","64" +"OR AX, imm16","ORW imm16, AX","orw imm16, AX","0D iw","V","V","","operand16","rw,r","Y","16" +"OR r/m16, imm16","ORW imm16, r/m16","orw imm16, r/m16","81 /1 iw","V","V","","operand16","rw,r","Y","16" +"OR r/m16, imm8","ORW imm8, r/m16","orw imm8, r/m16","83 /1 ib","V","V","","operand16","rw,r","Y","16" +"OR r16, r/m16","ORW r/m16, r16","orw r/m16, r16","0B /r","V","V","","operand16","rw,r","Y","16" +"OR r/m16, r16","ORW r16, r/m16","orw r16, r/m16","09 /r","V","V","","operand16","rw,r","Y","16" +"OUT DX, AL","OUTB AL, DX","outb AL, DX","EE","V","V","","","r,r","Y","8" +"OUT imm8u, AL","OUTB AL, imm8u","outb AL, imm8u","E6 ib","V","V","","","r,r","Y","8" +"OUT DX, EAX","OUTL EAX, DX","outl EAX, DX","EF","V","V","","operand32,operand64","r,r","Y","32" +"OUT imm8u, EAX","OUTL EAX, imm8u","outl EAX, imm8u","E7 ib","V","V","","operand32,operand64","r,r","Y","32" +"OUTSB","OUTSB","outsb","6E","V","V","","","","","" +"OUTSD","OUTSL","outsl","6F","V","V","","operand32,operand64","","","" +"OUTSW","OUTSW","outsw","6F","V","V","","operand16","","","" +"OUT DX, AX","OUTW AX, DX","outw AX, DX","EF","V","V","","operand16","r,r","Y","16" +"OUT imm8u, AX","OUTW AX, imm8u","outw AX, imm8u","E7 ib","V","V","","operand16","r,r","Y","16" +"PABSB mm1, mm2/m64","PABSB mm2/m64, mm1","pabsb mm2/m64, mm1","0F 38 1C /r","V","V","SSSE3","","w,r","","" +"PABSB xmm1, xmm2/m128","PABSB xmm2/m128, xmm1","pabsb xmm2/m128, xmm1","66 0F 38 1C /r","V","V","SSSE3","","w,r","","" +"PABSD mm1, mm2/m64","PABSD mm2/m64, mm1","pabsd mm2/m64, mm1","0F 38 1E /r","V","V","SSSE3","","w,r","","" +"PABSD xmm1, xmm2/m128","PABSD xmm2/m128, xmm1","pabsd xmm2/m128, xmm1","66 0F 38 1E /r","V","V","SSSE3","","w,r","","" +"PABSW mm1, mm2/m64","PABSW mm2/m64, mm1","pabsw mm2/m64, mm1","0F 38 1D /r","V","V","SSSE3","","w,r","","" +"PABSW xmm1, xmm2/m128","PABSW xmm2/m128, xmm1","pabsw xmm2/m128, xmm1","66 0F 38 1D /r","V","V","SSSE3","","w,r","","" +"PACKSSDW mm1, mm2/m64","PACKSSLW mm2/m64, mm1","packssdw mm2/m64, mm1","0F 6B /r","V","V","MMX","","rw,r","","" +"PACKSSDW xmm1, xmm2/m128","PACKSSLW xmm2/m128, xmm1","packssdw xmm2/m128, xmm1","66 0F 6B /r","V","V","SSE2","","rw,r","","" +"PACKSSWB mm1, mm2/m64","PACKSSWB mm2/m64, mm1","packsswb mm2/m64, mm1","0F 63 /r","V","V","MMX","","rw,r","","" +"PACKSSWB xmm1, xmm2/m128","PACKSSWB xmm2/m128, xmm1","packsswb xmm2/m128, xmm1","66 0F 63 /r","V","V","SSE2","","rw,r","","" +"PACKUSDW xmm1, xmm2/m128","PACKUSDW xmm2/m128, xmm1","packusdw xmm2/m128, xmm1","66 0F 38 2B /r","V","V","SSE4_1","","rw,r","","" +"PACKUSWB mm1, mm2/m64","PACKUSWB mm2/m64, mm1","packuswb mm2/m64, mm1","0F 67 /r","V","V","MMX","","rw,r","","" +"PACKUSWB xmm1, xmm2/m128","PACKUSWB xmm2/m128, xmm1","packuswb xmm2/m128, xmm1","66 0F 67 /r","V","V","SSE2","","rw,r","","" +"PADDB mm1, mm2/m64","PADDB mm2/m64, mm1","paddb mm2/m64, mm1","0F FC /r","V","V","MMX","","rw,r","","" +"PADDB xmm1, xmm2/m128","PADDB xmm2/m128, xmm1","paddb xmm2/m128, xmm1","66 0F FC /r","V","V","SSE2","","rw,r","","" +"PADDD mm1, mm2/m64","PADDL mm2/m64, mm1","paddd mm2/m64, mm1","0F FE /r","V","V","MMX","","rw,r","","" +"PADDD xmm1, xmm2/m128","PADDL xmm2/m128, xmm1","paddd xmm2/m128, xmm1","66 0F FE /r","V","V","SSE2","","rw,r","","" +"PADDQ mm1, mm2/m64","PADDQ mm2/m64, mm1","paddq mm2/m64, mm1","0F D4 /r","V","V","SSE2","","rw,r","","" +"PADDQ xmm1, xmm2/m128","PADDQ xmm2/m128, xmm1","paddq xmm2/m128, xmm1","66 0F D4 /r","V","V","SSE2","","rw,r","","" +"PADDSB mm1, mm2/m64","PADDSB mm2/m64, mm1","paddsb mm2/m64, mm1","0F EC /r","V","V","MMX","","rw,r","","" +"PADDSB xmm1, xmm2/m128","PADDSB xmm2/m128, xmm1","paddsb xmm2/m128, xmm1","66 0F EC /r","V","V","SSE2","","rw,r","","" +"PADDSW mm1, mm2/m64","PADDSW mm2/m64, mm1","paddsw mm2/m64, mm1","0F ED /r","V","V","MMX","","rw,r","","" +"PADDSW xmm1, xmm2/m128","PADDSW xmm2/m128, xmm1","paddsw xmm2/m128, xmm1","66 0F ED /r","V","V","SSE2","","rw,r","","" +"PADDUSB mm1, mm2/m64","PADDUSB mm2/m64, mm1","paddusb mm2/m64, mm1","0F DC /r","V","V","MMX","","rw,r","","" +"PADDUSB xmm1, xmm2/m128","PADDUSB xmm2/m128, xmm1","paddusb xmm2/m128, xmm1","66 0F DC /r","V","V","SSE2","","rw,r","","" +"PADDUSW mm1, mm2/m64","PADDUSW mm2/m64, mm1","paddusw mm2/m64, mm1","0F DD /r","V","V","MMX","","rw,r","","" +"PADDUSW xmm1, xmm2/m128","PADDUSW xmm2/m128, xmm1","paddusw xmm2/m128, xmm1","66 0F DD /r","V","V","SSE2","","rw,r","","" +"PADDW mm1, mm2/m64","PADDW mm2/m64, mm1","paddw mm2/m64, mm1","0F FD /r","V","V","MMX","","rw,r","","" +"PADDW xmm1, xmm2/m128","PADDW xmm2/m128, xmm1","paddw xmm2/m128, xmm1","66 0F FD /r","V","V","SSE2","","rw,r","","" +"PALIGNR mm1, mm2/m64, imm8u","PALIGNR imm8u, mm2/m64, mm1","palignr imm8u, mm2/m64, mm1","0F 3A 0F /r ib","V","V","SSSE3","","rw,r,r","","" +"PALIGNR xmm1, xmm2/m128, imm8u","PALIGNR imm8u, xmm2/m128, xmm1","palignr imm8u, xmm2/m128, xmm1","66 0F 3A 0F /r ib","V","V","SSSE3","","rw,r,r","","" +"PAND mm1, mm2/m64","PAND mm2/m64, mm1","pand mm2/m64, mm1","0F DB /r","V","V","MMX","","rw,r","","" +"PAND xmm1, xmm2/m128","PAND xmm2/m128, xmm1","pand xmm2/m128, xmm1","66 0F DB /r","V","V","SSE2","","rw,r","","" +"PANDN mm1, mm2/m64","PANDN mm2/m64, mm1","pandn mm2/m64, mm1","0F DF /r","V","V","MMX","","rw,r","","" +"PANDN xmm1, xmm2/m128","PANDN xmm2/m128, xmm1","pandn xmm2/m128, xmm1","66 0F DF /r","V","V","SSE2","","rw,r","","" +"PAUSE","PAUSE","pause","F3 90","V","V","","pseudo","","","" +"PAUSE","PAUSE","pause","F3 90+rd","V","V","","operand32","","Y","" +"PAUSE","PAUSE","pause","F3 90+rw","V","V","","operand16,operand64","","Y","" +"PAVGB mm1, mm2/m64","PAVGB mm2/m64, mm1","pavgb mm2/m64, mm1","0F E0 /r","V","V","MMX","","rw,r","","" +"PAVGB xmm1, xmm2/m128","PAVGB xmm2/m128, xmm1","pavgb xmm2/m128, xmm1","66 0F E0 /r","V","V","SSE2","","rw,r","","" +"PAVGUSB mm1, mm2/m64","PAVGUSB mm2/m64, mm1","pavgusb mm2/m64, mm1","0F 0F BF /r","V","V","3DNOW","amd","rw,r","","" +"PAVGW mm1, mm2/m64","PAVGW mm2/m64, mm1","pavgw mm2/m64, mm1","0F E3 /r","V","V","MMX","","rw,r","","" +"PAVGW xmm1, xmm2/m128","PAVGW xmm2/m128, xmm1","pavgw xmm2/m128, xmm1","66 0F E3 /r","V","V","SSE2","","rw,r","","" +"PBLENDVB xmm1, xmm2/m128, ","PBLENDVB , xmm2/m128, xmm1","pblendvb , xmm2/m128, xmm1","66 0F 38 10 /r","V","V","SSE4_1","","rw,r,r","","" +"PBLENDW xmm1, xmm2/m128, imm8u","PBLENDW imm8u, xmm2/m128, xmm1","pblendw imm8u, xmm2/m128, xmm1","66 0F 3A 0E /r ib","V","V","SSE4_1","","rw,r,r","","" +"PCLMULQDQ xmm1, xmm2/m128, imm8u","PCLMULQDQ imm8u, xmm2/m128, xmm1","pclmulqdq imm8u, xmm2/m128, xmm1","66 0F 3A 44 /r ib","V","V","PCLMULQDQ","","rw,r,r","","" +"PCMPEQB mm1, mm2/m64","PCMPEQB mm2/m64, mm1","pcmpeqb mm2/m64, mm1","0F 74 /r","V","V","MMX","","rw,r","","" +"PCMPEQB xmm1, xmm2/m128","PCMPEQB xmm2/m128, xmm1","pcmpeqb xmm2/m128, xmm1","66 0F 74 /r","V","V","SSE2","","rw,r","","" +"PCMPEQD mm1, mm2/m64","PCMPEQL mm2/m64, mm1","pcmpeqd mm2/m64, mm1","0F 76 /r","V","V","MMX","","rw,r","","" +"PCMPEQD xmm1, xmm2/m128","PCMPEQL xmm2/m128, xmm1","pcmpeqd xmm2/m128, xmm1","66 0F 76 /r","V","V","SSE2","","rw,r","","" +"PCMPEQQ xmm1, xmm2/m128","PCMPEQQ xmm2/m128, xmm1","pcmpeqq xmm2/m128, xmm1","66 0F 38 29 /r","V","V","SSE4_1","","rw,r","","" +"PCMPEQW mm1, mm2/m64","PCMPEQW mm2/m64, mm1","pcmpeqw mm2/m64, mm1","0F 75 /r","V","V","MMX","","rw,r","","" +"PCMPEQW xmm1, xmm2/m128","PCMPEQW xmm2/m128, xmm1","pcmpeqw xmm2/m128, xmm1","66 0F 75 /r","V","V","SSE2","","rw,r","","" +"PCMPESTRI xmm1, xmm2/m128, imm8u","PCMPESTRI imm8u, xmm2/m128, xmm1","pcmpestri imm8u, xmm2/m128, xmm1","66 0F 3A 61 /r ib","V","V","SSE4_2","","r,r,r","","" +"PCMPESTRM xmm1, xmm2/m128, imm8u","PCMPESTRM imm8u, xmm2/m128, xmm1","pcmpestrm imm8u, xmm2/m128, xmm1","66 0F 3A 60 /r ib","V","V","SSE4_2","","r,r,r","","" +"PCMPGTB mm1, mm2/m64","PCMPGTB mm2/m64, mm1","pcmpgtb mm2/m64, mm1","0F 64 /r","V","V","MMX","","rw,r","","" +"PCMPGTB xmm1, xmm2/m128","PCMPGTB xmm2/m128, xmm1","pcmpgtb xmm2/m128, xmm1","66 0F 64 /r","V","V","SSE2","","rw,r","","" +"PCMPGTD mm1, mm2/m64","PCMPGTL mm2/m64, mm1","pcmpgtd mm2/m64, mm1","0F 66 /r","V","V","MMX","","rw,r","","" +"PCMPGTD xmm1, xmm2/m128","PCMPGTL xmm2/m128, xmm1","pcmpgtd xmm2/m128, xmm1","66 0F 66 /r","V","V","SSE2","","rw,r","","" +"PCMPGTQ xmm1, xmm2/m128","PCMPGTQ xmm2/m128, xmm1","pcmpgtq xmm2/m128, xmm1","66 0F 38 37 /r","V","V","SSE4_2","","rw,r","","" +"PCMPGTW mm1, mm2/m64","PCMPGTW mm2/m64, mm1","pcmpgtw mm2/m64, mm1","0F 65 /r","V","V","MMX","","rw,r","","" +"PCMPGTW xmm1, xmm2/m128","PCMPGTW xmm2/m128, xmm1","pcmpgtw xmm2/m128, xmm1","66 0F 65 /r","V","V","SSE2","","rw,r","","" +"PCMPISTRI xmm1, xmm2/m128, imm8u","PCMPISTRI imm8u, xmm2/m128, xmm1","pcmpistri imm8u, xmm2/m128, xmm1","66 0F 3A 63 /r ib","V","V","SSE4_2","","r,r,r","","" +"PCMPISTRM xmm1, xmm2/m128, imm8u","PCMPISTRM imm8u, xmm2/m128, xmm1","pcmpistrm imm8u, xmm2/m128, xmm1","66 0F 3A 62 /r ib","V","V","SSE4_2","","r,r,r","","" +"PDEP r32, r32V, r/m32","PDEPL r/m32, r32V, r32","pdepl r/m32, r32V, r32","VEX.DDS.128.F2.0F38.W0 F5 /r","V","V","BMI2","","rw,r,r","Y","32" +"PDEP r64, r64V, r/m64","PDEPQ r/m64, r64V, r64","pdepq r/m64, r64V, r64","VEX.DDS.128.F2.0F38.W1 F5 /r","N.S.","V","BMI2","","rw,r,r","Y","64" +"PEXT r32, r32V, r/m32","PEXTL r/m32, r32V, r32","pextl r/m32, r32V, r32","VEX.DDS.128.F3.0F38.W0 F5 /r","V","V","BMI2","","rw,r,r","Y","32" +"PEXT r64, r64V, r/m64","PEXTQ r/m64, r64V, r64","pextq r/m64, r64V, r64","VEX.DDS.128.F3.0F38.W1 F5 /r","N.S.","V","BMI2","","rw,r,r","Y","64" +"PEXTRB r32/m8, xmm1, imm8u","PEXTRB imm8u, xmm1, r32/m8","pextrb imm8u, xmm1, r32/m8","66 0F 3A 14 /r ib","V","V","SSE4_1","","w,r,r","","" +"PEXTRD r/m32, xmm1, imm8u","PEXTRD imm8u, xmm1, r/m32","pextrd imm8u, xmm1, r/m32","66 0F 3A 16 /r ib","V","V","SSE4_1","operand16,operand32","w,r,r","","" +"PEXTRQ r/m64, xmm1, imm8u","PEXTRQ imm8u, xmm1, r/m64","pextrq imm8u, xmm1, r/m64","66 REX.W 0F 3A 16 /r ib","N.S.","V","SSE4_1","","w,r,r","","" +"PEXTRW r32, mm2, imm8u","PEXTRW imm8u, mm2, r32","pextrw imm8u, mm2, r32","0F C5 /r ib","V","V","MMX","modrm_regonly","w,r,r","","" +"PEXTRW r32/m16, xmm1, imm8u","PEXTRW imm8u, xmm1, r32/m16","pextrw imm8u, xmm1, r32/m16","66 0F 3A 15 /r ib","V","V","SSE4_1","","w,r,r","","" +"PEXTRW r32, xmm2, imm8u","PEXTRW imm8u, xmm2, r32","pextrw imm8u, xmm2, r32","66 0F C5 /r ib","V","V","SSE2","modrm_regonly","w,r,r","","" +"PF2ID mm1, mm2/m64","PF2ID mm2/m64, mm1","pf2id mm2/m64, mm1","0F 0F 1D /r","V","V","3DNOW","amd","rw,r","","" +"PF2IW mm1, mm2/m64","PF2IW mm2/m64, mm1","pf2iw mm2/m64, mm1","0F 0F 1C /r","V","V","3DNOW","amd","rw,r","","" +"PFACC mm1, mm2/m64","PFACC mm2/m64, mm1","pfacc mm2/m64, mm1","0F 0F AE /r","V","V","3DNOW","amd","rw,r","","" +"PFADD mm1, mm2/m64","PFADD mm2/m64, mm1","pfadd mm2/m64, mm1","0F 0F 9E /r","V","V","3DNOW","amd","rw,r","","" +"PFCMPEQ mm1, mm2/m64","PFCMPEQ mm2/m64, mm1","pfcmpeq mm2/m64, mm1","0F 0F B0 /r","V","V","3DNOW","amd","rw,r","","" +"PFCMPGE mm1, mm2/m64","PFCMPGE mm2/m64, mm1","pfcmpge mm2/m64, mm1","0F 0F 90 /r","V","V","3DNOW","amd","rw,r","","" +"PFCMPGT mm1, mm2/m64","PFCMPGT mm2/m64, mm1","pfcmpgt mm2/m64, mm1","0F 0F A0 /r","V","V","3DNOW","amd","rw,r","","" +"PFMAX mm1, mm2/m64","PFMAX mm2/m64, mm1","pfmax mm2/m64, mm1","0F 0F A4 /r","V","V","3DNOW","amd","rw,r","","" +"PFMIN mm1, mm2/m64","PFMIN mm2/m64, mm1","pfmin mm2/m64, mm1","0F 0F 94 /r","V","V","3DNOW","amd","rw,r","","" +"PFMUL mm1, mm2/m64","PFMUL mm2/m64, mm1","pfmul mm2/m64, mm1","0F 0F B4 /r","V","V","3DNOW","amd","rw,r","","" +"PFNACC mm1, mm2/m64","PFNACC mm2/m64, mm1","pfnacc mm2/m64, mm1","0F 0F 8A /r","V","V","3DNOW","amd","rw,r","","" +"PFPNACC mm1, mm2/m64","PFPNACC mm2/m64, mm1","pfpnacc mm2/m64, mm1","0F 0F 8E /r","V","V","3DNOW","amd","rw,r","","" +"PFRCP mm1, mm2/m64","PFRCP mm2/m64, mm1","pfrcp mm2/m64, mm1","0F 0F 96 /r","V","V","3DNOW","amd","rw,r","","" +"PFRCPIT1 mm1, mm2/m64","PFRCPIT1 mm2/m64, mm1","pfrcpit1 mm2/m64, mm1","0F 0F A6 /r","V","V","3DNOW","amd","rw,r","","" +"PFRCPIT2 mm1, mm2/m64","PFRCPIT2 mm2/m64, mm1","pfrcpit2 mm2/m64, mm1","0F 0F B6 /r","V","V","3DNOW","amd","rw,r","","" +"PFRSQIT1 mm1, mm2/m64","PFRSQIT1 mm2/m64, mm1","pfrsqit1 mm2/m64, mm1","0F 0F A7 /r","V","V","3DNOW","amd","rw,r","","" +"PFRSQRT mm1, mm2/m64","PFRSQRT mm2/m64, mm1","pfrsqrt mm2/m64, mm1","0F 0F 97 /r","V","V","3DNOW","amd","rw,r","","" +"PFSUB mm1, mm2/m64","PFSUB mm2/m64, mm1","pfsub mm2/m64, mm1","0F 0F 9A /r","V","V","3DNOW","amd","rw,r","","" +"PFSUBR mm1, mm2/m64","PFSUBR mm2/m64, mm1","pfsubr mm2/m64, mm1","0F 0F AA /r","V","V","3DNOW","amd","rw,r","","" +"PHADDD mm1, mm2/m64","PHADDD mm2/m64, mm1","phaddd mm2/m64, mm1","0F 38 02 /r","V","V","SSSE3","","rw,r","","" +"PHADDD xmm1, xmm2/m128","PHADDD xmm2/m128, xmm1","phaddd xmm2/m128, xmm1","66 0F 38 02 /r","V","V","SSSE3","","rw,r","","" +"PHADDSW mm1, mm2/m64","PHADDSW mm2/m64, mm1","phaddsw mm2/m64, mm1","0F 38 03 /r","V","V","SSSE3","","rw,r","","" +"PHADDSW xmm1, xmm2/m128","PHADDSW xmm2/m128, xmm1","phaddsw xmm2/m128, xmm1","66 0F 38 03 /r","V","V","SSSE3","","rw,r","","" +"PHADDW mm1, mm2/m64","PHADDW mm2/m64, mm1","phaddw mm2/m64, mm1","0F 38 01 /r","V","V","SSSE3","","rw,r","","" +"PHADDW xmm1, xmm2/m128","PHADDW xmm2/m128, xmm1","phaddw xmm2/m128, xmm1","66 0F 38 01 /r","V","V","SSSE3","","rw,r","","" +"PHMINPOSUW xmm1, xmm2/m128","PHMINPOSUW xmm2/m128, xmm1","phminposuw xmm2/m128, xmm1","66 0F 38 41 /r","V","V","SSE4_1","","w,r","","" +"PHSUBD mm1, mm2/m64","PHSUBD mm2/m64, mm1","phsubd mm2/m64, mm1","0F 38 06 /r","V","V","SSSE3","","rw,r","","" +"PHSUBD xmm1, xmm2/m128","PHSUBD xmm2/m128, xmm1","phsubd xmm2/m128, xmm1","66 0F 38 06 /r","V","V","SSSE3","","rw,r","","" +"PHSUBSW mm1, mm2/m64","PHSUBSW mm2/m64, mm1","phsubsw mm2/m64, mm1","0F 38 07 /r","V","V","SSSE3","","rw,r","","" +"PHSUBSW xmm1, xmm2/m128","PHSUBSW xmm2/m128, xmm1","phsubsw xmm2/m128, xmm1","66 0F 38 07 /r","V","V","SSSE3","","rw,r","","" +"PHSUBW mm1, mm2/m64","PHSUBW mm2/m64, mm1","phsubw mm2/m64, mm1","0F 38 05 /r","V","V","SSSE3","","rw,r","","" +"PHSUBW xmm1, xmm2/m128","PHSUBW xmm2/m128, xmm1","phsubw xmm2/m128, xmm1","66 0F 38 05 /r","V","V","SSSE3","","rw,r","","" +"PI2FD mm1, mm2/m64","PI2FD mm2/m64, mm1","pi2fd mm2/m64, mm1","0F 0F 0D /r","V","V","3DNOW","amd","rw,r","","" +"PI2FW mm1, mm2/m64","PI2FW mm2/m64, mm1","pi2fw mm2/m64, mm1","0F 0F 0C /r","V","V","3DNOW","amd","rw,r","","" +"PINSRB xmm1, r32/m8, imm8u","PINSRB imm8u, r32/m8, xmm1","pinsrb imm8u, r32/m8, xmm1","66 0F 3A 20 /r ib","V","V","SSE4_1","","rw,r,r","","" +"PINSRD xmm1, r/m32, imm8u","PINSRD imm8u, r/m32, xmm1","pinsrd imm8u, r/m32, xmm1","66 0F 3A 22 /r ib","V","V","SSE4_1","operand16,operand32","rw,r,r","","" +"PINSRQ xmm1, r/m64, imm8u","PINSRQ imm8u, r/m64, xmm1","pinsrq imm8u, r/m64, xmm1","66 REX.W 0F 3A 22 /r ib","N.S.","V","SSE4_1","","rw,r,r","","" +"PINSRW mm1, r32/m16, imm8u","PINSRW imm8u, r32/m16, mm1","pinsrw imm8u, r32/m16, mm1","0F C4 /r ib","V","V","MMX","","rw,r,r","","" +"PINSRW xmm1, r32/m16, imm8u","PINSRW imm8u, r32/m16, xmm1","pinsrw imm8u, r32/m16, xmm1","66 0F C4 /r ib","V","V","SSE2","","rw,r,r","","" +"PMADDUBSW mm1, mm2/m64","PMADDUBSW mm2/m64, mm1","pmaddubsw mm2/m64, mm1","0F 38 04 /r","V","V","SSSE3","","rw,r","","" +"PMADDUBSW xmm1, xmm2/m128","PMADDUBSW xmm2/m128, xmm1","pmaddubsw xmm2/m128, xmm1","66 0F 38 04 /r","V","V","SSSE3","","rw,r","","" +"PMADDWD mm1, mm2/m64","PMADDWL mm2/m64, mm1","pmaddwd mm2/m64, mm1","0F F5 /r","V","V","MMX","","rw,r","","" +"PMADDWD xmm1, xmm2/m128","PMADDWL xmm2/m128, xmm1","pmaddwd xmm2/m128, xmm1","66 0F F5 /r","V","V","SSE2","","rw,r","","" +"PMAXSB xmm1, xmm2/m128","PMAXSB xmm2/m128, xmm1","pmaxsb xmm2/m128, xmm1","66 0F 38 3C /r","V","V","SSE4_1","","rw,r","","" +"PMAXSD xmm1, xmm2/m128","PMAXSD xmm2/m128, xmm1","pmaxsd xmm2/m128, xmm1","66 0F 38 3D /r","V","V","SSE4_1","","rw,r","","" +"PMAXSW mm1, mm2/m64","PMAXSW mm2/m64, mm1","pmaxsw mm2/m64, mm1","0F EE /r","V","V","MMX","","rw,r","","" +"PMAXSW xmm1, xmm2/m128","PMAXSW xmm2/m128, xmm1","pmaxsw xmm2/m128, xmm1","66 0F EE /r","V","V","SSE2","","rw,r","","" +"PMAXUB mm1, mm2/m64","PMAXUB mm2/m64, mm1","pmaxub mm2/m64, mm1","0F DE /r","V","V","MMX","","rw,r","","" +"PMAXUB xmm1, xmm2/m128","PMAXUB xmm2/m128, xmm1","pmaxub xmm2/m128, xmm1","66 0F DE /r","V","V","SSE2","","rw,r","","" +"PMAXUD xmm1, xmm2/m128","PMAXUD xmm2/m128, xmm1","pmaxud xmm2/m128, xmm1","66 0F 38 3F /r","V","V","SSE4_1","","rw,r","","" +"PMAXUW xmm1, xmm2/m128","PMAXUW xmm2/m128, xmm1","pmaxuw xmm2/m128, xmm1","66 0F 38 3E /r","V","V","SSE4_1","","rw,r","","" +"PMINSB xmm1, xmm2/m128","PMINSB xmm2/m128, xmm1","pminsb xmm2/m128, xmm1","66 0F 38 38 /r","V","V","SSE4_1","","rw,r","","" +"PMINSD xmm1, xmm2/m128","PMINSD xmm2/m128, xmm1","pminsd xmm2/m128, xmm1","66 0F 38 39 /r","V","V","SSE4_1","","rw,r","","" +"PMINSW mm1, mm2/m64","PMINSW mm2/m64, mm1","pminsw mm2/m64, mm1","0F EA /r","V","V","MMX","","rw,r","","" +"PMINSW xmm1, xmm2/m128","PMINSW xmm2/m128, xmm1","pminsw xmm2/m128, xmm1","66 0F EA /r","V","V","SSE2","","rw,r","","" +"PMINUB mm1, mm2/m64","PMINUB mm2/m64, mm1","pminub mm2/m64, mm1","0F DA /r","V","V","MMX","","rw,r","","" +"PMINUB xmm1, xmm2/m128","PMINUB xmm2/m128, xmm1","pminub xmm2/m128, xmm1","66 0F DA /r","V","V","SSE2","","rw,r","","" +"PMINUD xmm1, xmm2/m128","PMINUD xmm2/m128, xmm1","pminud xmm2/m128, xmm1","66 0F 38 3B /r","V","V","SSE4_1","","rw,r","","" +"PMINUW xmm1, xmm2/m128","PMINUW xmm2/m128, xmm1","pminuw xmm2/m128, xmm1","66 0F 38 3A /r","V","V","SSE4_1","","rw,r","","" +"PMOVMSKB r32, mm2","PMOVMSKB mm2, r32","pmovmskb mm2, r32","0F D7 /r","V","V","SSE","modrm_regonly","w,r","","" +"PMOVMSKB r32, xmm2","PMOVMSKB xmm2, r32","pmovmskb xmm2, r32","66 0F D7 /r","V","V","SSE2","modrm_regonly","w,r","","" +"PMOVSXBD xmm1, xmm2/m32","PMOVSXBD xmm2/m32, xmm1","pmovsxbd xmm2/m32, xmm1","66 0F 38 21 /r","V","V","SSE4_1","","w,r","","" +"PMOVSXBQ xmm1, xmm2/m16","PMOVSXBQ xmm2/m16, xmm1","pmovsxbq xmm2/m16, xmm1","66 0F 38 22 /r","V","V","SSE4_1","","w,r","","" +"PMOVSXBW xmm1, xmm2/m64","PMOVSXBW xmm2/m64, xmm1","pmovsxbw xmm2/m64, xmm1","66 0F 38 20 /r","V","V","SSE4_1","","w,r","","" +"PMOVSXDQ xmm1, xmm2/m64","PMOVSXDQ xmm2/m64, xmm1","pmovsxdq xmm2/m64, xmm1","66 0F 38 25 /r","V","V","SSE4_1","","w,r","","" +"PMOVSXWD xmm1, xmm2/m64","PMOVSXWD xmm2/m64, xmm1","pmovsxwd xmm2/m64, xmm1","66 0F 38 23 /r","V","V","SSE4_1","","w,r","","" +"PMOVSXWQ xmm1, xmm2/m32","PMOVSXWQ xmm2/m32, xmm1","pmovsxwq xmm2/m32, xmm1","66 0F 38 24 /r","V","V","SSE4_1","","w,r","","" +"PMOVZXBD xmm1, xmm2/m32","PMOVZXBD xmm2/m32, xmm1","pmovzxbd xmm2/m32, xmm1","66 0F 38 31 /r","V","V","SSE4_1","","w,r","","" +"PMOVZXBQ xmm1, xmm2/m16","PMOVZXBQ xmm2/m16, xmm1","pmovzxbq xmm2/m16, xmm1","66 0F 38 32 /r","V","V","SSE4_1","","w,r","","" +"PMOVZXBW xmm1, xmm2/m64","PMOVZXBW xmm2/m64, xmm1","pmovzxbw xmm2/m64, xmm1","66 0F 38 30 /r","V","V","SSE4_1","","w,r","","" +"PMOVZXDQ xmm1, xmm2/m64","PMOVZXDQ xmm2/m64, xmm1","pmovzxdq xmm2/m64, xmm1","66 0F 38 35 /r","V","V","SSE4_1","","w,r","","" +"PMOVZXWD xmm1, xmm2/m64","PMOVZXWD xmm2/m64, xmm1","pmovzxwd xmm2/m64, xmm1","66 0F 38 33 /r","V","V","SSE4_1","","w,r","","" +"PMOVZXWQ xmm1, xmm2/m32","PMOVZXWQ xmm2/m32, xmm1","pmovzxwq xmm2/m32, xmm1","66 0F 38 34 /r","V","V","SSE4_1","","w,r","","" +"PMULDQ xmm1, xmm2/m128","PMULDQ xmm2/m128, xmm1","pmuldq xmm2/m128, xmm1","66 0F 38 28 /r","V","V","SSE4_1","","rw,r","","" +"PMULHRSW mm1, mm2/m64","PMULHRSW mm2/m64, mm1","pmulhrsw mm2/m64, mm1","0F 38 0B /r","V","V","SSSE3","","rw,r","","" +"PMULHRSW xmm1, xmm2/m128","PMULHRSW xmm2/m128, xmm1","pmulhrsw xmm2/m128, xmm1","66 0F 38 0B /r","V","V","SSSE3","","rw,r","","" +"PMULHRW mm1, mm2/m64","PMULHRW mm2/m64, mm1","pmulhrw mm2/m64, mm1","0F 0F B7 /r","V","V","3DNOW","amd","rw,r","","" +"PMULHUW mm1, mm2/m64","PMULHUW mm2/m64, mm1","pmulhuw mm2/m64, mm1","0F E4 /r","V","V","MMX","","rw,r","","" +"PMULHUW xmm1, xmm2/m128","PMULHUW xmm2/m128, xmm1","pmulhuw xmm2/m128, xmm1","66 0F E4 /r","V","V","SSE2","","rw,r","","" +"PMULHW mm1, mm2/m64","PMULHW mm2/m64, mm1","pmulhw mm2/m64, mm1","0F E5 /r","V","V","MMX","","rw,r","","" +"PMULHW xmm1, xmm2/m128","PMULHW xmm2/m128, xmm1","pmulhw xmm2/m128, xmm1","66 0F E5 /r","V","V","SSE2","","rw,r","","" +"PMULLD xmm1, xmm2/m128","PMULLD xmm2/m128, xmm1","pmulld xmm2/m128, xmm1","66 0F 38 40 /r","V","V","SSE4_1","","rw,r","","" +"PMULLW mm1, mm2/m64","PMULLW mm2/m64, mm1","pmullw mm2/m64, mm1","0F D5 /r","V","V","MMX","","rw,r","","" +"PMULLW xmm1, xmm2/m128","PMULLW xmm2/m128, xmm1","pmullw xmm2/m128, xmm1","66 0F D5 /r","V","V","SSE2","","rw,r","","" +"PMULUDQ mm1, mm2/m64","PMULULQ mm2/m64, mm1","pmuludq mm2/m64, mm1","0F F4 /r","V","V","SSE2","","rw,r","","" +"PMULUDQ xmm1, xmm2/m128","PMULULQ xmm2/m128, xmm1","pmuludq xmm2/m128, xmm1","66 0F F4 /r","V","V","SSE2","","rw,r","","" +"POPAD","POPAL","popal","61","V","N.S.","","operand32","","","" +"POPA","POPAW","popaw","61","V","N.S.","","operand16","","","" +"POPCNT r32, r/m32","POPCNTL r/m32, r32","popcntl r/m32, r32","F3 0F B8 /r","V","V","POPCNT","operand32","w,r","Y","32" +"POPCNT r64, r/m64","POPCNTQ r/m64, r64","popcntq r/m64, r64","F3 REX.W 0F B8 /r","N.S.","V","POPCNT","","w,r","Y","64" +"POPCNT r16, r/m16","POPCNTW r/m16, r16","popcntw r/m16, r16","F3 0F B8 /r","V","V","POPCNT","operand16","w,r","Y","16" +"POPFD","POPFL","popfl","9D","V","N.S.","","operand32","","","" +"POPFQ","POPFQ","popfq","9D","N.S.","V","","default64","","","" +"POPF","POPFW","popfw","9D","V","V","","operand16","","","" +"POP r/m32","POPL r/m32","popl r/m32","8F /0","V","N.S.","","operand32","w","Y","32" +"POP r32op","POPL r32op","popl r32op","58+rd","V","N.S.","","operand32","w","Y","32" +"POP r/m64","POPQ r/m64","popq r/m64","8F /0","N.S.","V","","default64","w","Y","64" +"POP r64op","POPQ r64op","popq r64op","58+ro","N.S.","V","","default64","w","Y","64" +"POP r/m16","POPW r/m16","popw r/m16","8F /0","V","V","","operand16","w","Y","16" +"POP r16op","POPW r16op","popw r16op","58+rw","V","V","","operand16","w","Y","16" +"POP DS","POPW/POPL/POPQ DS","popw/popl/popq DS","1F","V","N.S.","","","w","Y","" +"POP ES","POPW/POPL/POPQ ES","popw/popl/popq ES","07","V","N.S.","","","w","Y","" +"POP FS","POPW/POPL/POPQ FS","popw/popl/popq FS","0F A1","N.S.","V","","default64","w","Y","" +"POP FS","POPW/POPL/POPQ FS","popw/popl/popq FS","0F A1","V","N.S.","","operand32","w","Y","" +"POP FS","POPW/POPL/POPQ FS","popw/popl/popq FS","0F A1","V","V","","operand16","w","Y","" +"POP GS","POPW/POPL/POPQ GS","popw/popl/popq GS","0F A9","N.S.","V","","default64","w","Y","" +"POP GS","POPW/POPL/POPQ GS","popw/popl/popq GS","0F A9","V","V","","operand16","w","Y","" +"POP GS","POPW/POPL/POPQ GS","popw/popl/popq GS","0F A9","V","N.S.","","operand32","w","Y","" +"POP SS","POPW/POPL/POPQ SS","popw/popl/popq SS","17","V","N.S.","","","w","Y","" +"POR mm1, mm2/m64","POR mm2/m64, mm1","por mm2/m64, mm1","0F EB /r","V","V","MMX","","rw,r","","" +"POR xmm1, xmm2/m128","POR xmm2/m128, xmm1","por xmm2/m128, xmm1","66 0F EB /r","V","V","SSE2","","rw,r","","" +"PREFETCHNTA m8","PREFETCHNTA m8","prefetchnta m8","0F 18 /0","V","V","","modrm_memonly","r","","" +"PREFETCHT0 m8","PREFETCHT0 m8","prefetcht0 m8","0F 18 /1","V","V","","modrm_memonly","r","","" +"PREFETCHT1 m8","PREFETCHT1 m8","prefetcht1 m8","0F 18 /2","V","V","","modrm_memonly","r","","" +"PREFETCHT2 m8","PREFETCHT2 m8","prefetcht2 m8","0F 18 /3","V","V","","modrm_memonly","r","","" +"PREFETCHW m8","PREFETCHW m8","prefetchw m8","0F 0D /1","V","V","PRFCHW","modrm_memonly","r","","" +"PREFETCHWT1 m8","PREFETCHWT1 m8","prefetchwt1 m8","0F 0D /2","V","V","PREFETCHWT1","modrm_memonly","r","","" +"PREFETCHW_ALIAS m8","PREFETCHW_ALIAS m8","prefetchw_alias m8","0F 0D /3","V","V","PRFCHW","modrm_memonly","r","","" +"PREFETCH_EXCLUSIVE m8","PREFETCH_EXCLUSIVE m8","prefetch_exclusive m8","0F 0D /0","V","V","PRFCHW","modrm_memonly","r","","" +"PREFETCH_RESERVED m8","PREFETCH_RESERVED m8","prefetch_reserved m8","0F 0D /2","V","V","PRFCHW","modrm_memonly","r","Y","" +"PREFETCH_RESERVED m8","PREFETCH_RESERVED m8","prefetch_reserved m8","0F 0D /4","V","V","PRFCHW","modrm_memonly","r","Y","" +"PREFETCH_RESERVED m8","PREFETCH_RESERVED m8","prefetch_reserved m8","0F 0D /5","V","V","PRFCHW","modrm_memonly","r","Y","" +"PREFETCH_RESERVED m8","PREFETCH_RESERVED m8","prefetch_reserved m8","0F 0D /6","V","V","PRFCHW","modrm_memonly","r","Y","" +"PREFETCH_RESERVED m8","PREFETCH_RESERVED m8","prefetch_reserved m8","0F 0D /7","V","V","PRFCHW","modrm_memonly","r","Y","" +"PSADBW mm1, mm2/m64","PSADBW mm2/m64, mm1","psadbw mm2/m64, mm1","0F F6 /r","V","V","MMX","","rw,r","","" +"PSADBW xmm1, xmm2/m128","PSADBW xmm2/m128, xmm1","psadbw xmm2/m128, xmm1","66 0F F6 /r","V","V","SSE2","","rw,r","","" +"PSHUFB mm1, mm2/m64","PSHUFB mm2/m64, mm1","pshufb mm2/m64, mm1","0F 38 00 /r","V","V","SSSE3","","rw,r","","" +"PSHUFB xmm1, xmm2/m128","PSHUFB xmm2/m128, xmm1","pshufb xmm2/m128, xmm1","66 0F 38 00 /r","V","V","SSSE3","","rw,r","","" +"PSHUFD xmm1, xmm2/m128, imm8u","PSHUFD imm8u, xmm2/m128, xmm1","pshufd imm8u, xmm2/m128, xmm1","66 0F 70 /r ib","V","V","SSE2","","w,r,r","","" +"PSHUFHW xmm1, xmm2/m128, imm8u","PSHUFHW imm8u, xmm2/m128, xmm1","pshufhw imm8u, xmm2/m128, xmm1","F3 0F 70 /r ib","V","V","SSE2","","w,r,r","","" +"PSHUFLW xmm1, xmm2/m128, imm8u","PSHUFLW imm8u, xmm2/m128, xmm1","pshuflw imm8u, xmm2/m128, xmm1","F2 0F 70 /r ib","V","V","SSE2","","w,r,r","","" +"PSHUFW mm1, mm2/m64, imm8u","PSHUFW imm8u, mm2/m64, mm1","pshufw imm8u, mm2/m64, mm1","0F 70 /r ib","V","V","MMX","","w,r,r","","" +"PSIGNB mm1, mm2/m64","PSIGNB mm2/m64, mm1","psignb mm2/m64, mm1","0F 38 08 /r","V","V","SSSE3","","rw,r","","" +"PSIGNB xmm1, xmm2/m128","PSIGNB xmm2/m128, xmm1","psignb xmm2/m128, xmm1","66 0F 38 08 /r","V","V","SSSE3","","rw,r","","" +"PSIGND mm1, mm2/m64","PSIGND mm2/m64, mm1","psignd mm2/m64, mm1","0F 38 0A /r","V","V","SSSE3","","rw,r","","" +"PSIGND xmm1, xmm2/m128","PSIGND xmm2/m128, xmm1","psignd xmm2/m128, xmm1","66 0F 38 0A /r","V","V","SSSE3","","rw,r","","" +"PSIGNW mm1, mm2/m64","PSIGNW mm2/m64, mm1","psignw mm2/m64, mm1","0F 38 09 /r","V","V","SSSE3","","rw,r","","" +"PSIGNW xmm1, xmm2/m128","PSIGNW xmm2/m128, xmm1","psignw xmm2/m128, xmm1","66 0F 38 09 /r","V","V","SSSE3","","rw,r","","" +"PSLLD mm2, imm8u","PSLLL imm8u, mm2","pslld imm8u, mm2","0F 72 /6 ib","V","V","MMX","modrm_regonly","rw,r","","" +"PSLLD xmm2, imm8u","PSLLL imm8u, xmm2","pslld imm8u, xmm2","66 0F 72 /6 ib","V","V","SSE2","modrm_regonly","rw,r","","" +"PSLLD mm1, mm2/m64","PSLLL mm2/m64, mm1","pslld mm2/m64, mm1","0F F2 /r","V","V","MMX","","rw,r","","" +"PSLLD xmm1, xmm2/m128","PSLLL xmm2/m128, xmm1","pslld xmm2/m128, xmm1","66 0F F2 /r","V","V","SSE2","","rw,r","","" +"PSLLDQ xmm2, imm8u","PSLLO imm8u, xmm2","pslldq imm8u, xmm2","66 0F 73 /7 ib","V","V","SSE2","modrm_regonly","rw,r","","" +"PSLLQ mm2, imm8u","PSLLQ imm8u, mm2","psllq imm8u, mm2","0F 73 /6 ib","V","V","MMX","modrm_regonly","rw,r","","" +"PSLLQ xmm2, imm8u","PSLLQ imm8u, xmm2","psllq imm8u, xmm2","66 0F 73 /6 ib","V","V","SSE2","modrm_regonly","rw,r","","" +"PSLLQ mm1, mm2/m64","PSLLQ mm2/m64, mm1","psllq mm2/m64, mm1","0F F3 /r","V","V","MMX","","rw,r","","" +"PSLLQ xmm1, xmm2/m128","PSLLQ xmm2/m128, xmm1","psllq xmm2/m128, xmm1","66 0F F3 /r","V","V","SSE2","","rw,r","","" +"PSLLW mm2, imm8u","PSLLW imm8u, mm2","psllw imm8u, mm2","0F 71 /6 ib","V","V","MMX","modrm_regonly","rw,r","","" +"PSLLW xmm2, imm8u","PSLLW imm8u, xmm2","psllw imm8u, xmm2","66 0F 71 /6 ib","V","V","SSE2","modrm_regonly","rw,r","","" +"PSLLW mm1, mm2/m64","PSLLW mm2/m64, mm1","psllw mm2/m64, mm1","0F F1 /r","V","V","MMX","","rw,r","","" +"PSLLW xmm1, xmm2/m128","PSLLW xmm2/m128, xmm1","psllw xmm2/m128, xmm1","66 0F F1 /r","V","V","SSE2","","rw,r","","" +"PSRAD mm2, imm8u","PSRAL imm8u, mm2","psrad imm8u, mm2","0F 72 /4 ib","V","V","MMX","modrm_regonly","rw,r","","" +"PSRAD xmm2, imm8u","PSRAL imm8u, xmm2","psrad imm8u, xmm2","66 0F 72 /4 ib","V","V","SSE2","modrm_regonly","rw,r","","" +"PSRAD mm1, mm2/m64","PSRAL mm2/m64, mm1","psrad mm2/m64, mm1","0F E2 /r","V","V","MMX","","rw,r","","" +"PSRAD xmm1, xmm2/m128","PSRAL xmm2/m128, xmm1","psrad xmm2/m128, xmm1","66 0F E2 /r","V","V","SSE2","","rw,r","","" +"PSRAW mm2, imm8u","PSRAW imm8u, mm2","psraw imm8u, mm2","0F 71 /4 ib","V","V","MMX","modrm_regonly","rw,r","","" +"PSRAW xmm2, imm8u","PSRAW imm8u, xmm2","psraw imm8u, xmm2","66 0F 71 /4 ib","V","V","SSE2","modrm_regonly","rw,r","","" +"PSRAW mm1, mm2/m64","PSRAW mm2/m64, mm1","psraw mm2/m64, mm1","0F E1 /r","V","V","MMX","","rw,r","","" +"PSRAW xmm1, xmm2/m128","PSRAW xmm2/m128, xmm1","psraw xmm2/m128, xmm1","66 0F E1 /r","V","V","SSE2","","rw,r","","" +"PSRLD mm2, imm8u","PSRLL imm8u, mm2","psrld imm8u, mm2","0F 72 /2 ib","V","V","MMX","modrm_regonly","rw,r","","" +"PSRLD xmm2, imm8u","PSRLL imm8u, xmm2","psrld imm8u, xmm2","66 0F 72 /2 ib","V","V","SSE2","modrm_regonly","rw,r","","" +"PSRLD mm1, mm2/m64","PSRLL mm2/m64, mm1","psrld mm2/m64, mm1","0F D2 /r","V","V","MMX","","rw,r","","" +"PSRLD xmm1, xmm2/m128","PSRLL xmm2/m128, xmm1","psrld xmm2/m128, xmm1","66 0F D2 /r","V","V","SSE2","","rw,r","","" +"PSRLDQ xmm2, imm8u","PSRLO imm8u, xmm2","psrldq imm8u, xmm2","66 0F 73 /3 ib","V","V","SSE2","modrm_regonly","rw,r","","" +"PSRLQ mm2, imm8u","PSRLQ imm8u, mm2","psrlq imm8u, mm2","0F 73 /2 ib","V","V","MMX","modrm_regonly","rw,r","","" +"PSRLQ xmm2, imm8u","PSRLQ imm8u, xmm2","psrlq imm8u, xmm2","66 0F 73 /2 ib","V","V","SSE2","modrm_regonly","rw,r","","" +"PSRLQ mm1, mm2/m64","PSRLQ mm2/m64, mm1","psrlq mm2/m64, mm1","0F D3 /r","V","V","MMX","","rw,r","","" +"PSRLQ xmm1, xmm2/m128","PSRLQ xmm2/m128, xmm1","psrlq xmm2/m128, xmm1","66 0F D3 /r","V","V","SSE2","","rw,r","","" +"PSRLW mm2, imm8u","PSRLW imm8u, mm2","psrlw imm8u, mm2","0F 71 /2 ib","V","V","MMX","modrm_regonly","rw,r","","" +"PSRLW xmm2, imm8u","PSRLW imm8u, xmm2","psrlw imm8u, xmm2","66 0F 71 /2 ib","V","V","SSE2","modrm_regonly","rw,r","","" +"PSRLW mm1, mm2/m64","PSRLW mm2/m64, mm1","psrlw mm2/m64, mm1","0F D1 /r","V","V","MMX","","rw,r","","" +"PSRLW xmm1, xmm2/m128","PSRLW xmm2/m128, xmm1","psrlw xmm2/m128, xmm1","66 0F D1 /r","V","V","SSE2","","rw,r","","" +"PSUBB mm1, mm2/m64","PSUBB mm2/m64, mm1","psubb mm2/m64, mm1","0F F8 /r","V","V","MMX","","rw,r","","" +"PSUBB xmm1, xmm2/m128","PSUBB xmm2/m128, xmm1","psubb xmm2/m128, xmm1","66 0F F8 /r","V","V","SSE2","","rw,r","","" +"PSUBD mm1, mm2/m64","PSUBL mm2/m64, mm1","psubd mm2/m64, mm1","0F FA /r","V","V","MMX","","rw,r","","" +"PSUBD xmm1, xmm2/m128","PSUBL xmm2/m128, xmm1","psubd xmm2/m128, xmm1","66 0F FA /r","V","V","SSE2","","rw,r","","" +"PSUBQ mm1, mm2/m64","PSUBQ mm2/m64, mm1","psubq mm2/m64, mm1","0F FB /r","V","V","SSE2","","rw,r","","" +"PSUBQ xmm1, xmm2/m128","PSUBQ xmm2/m128, xmm1","psubq xmm2/m128, xmm1","66 0F FB /r","V","V","SSE2","","rw,r","","" +"PSUBSB mm1, mm2/m64","PSUBSB mm2/m64, mm1","psubsb mm2/m64, mm1","0F E8 /r","V","V","MMX","","rw,r","","" +"PSUBSB xmm1, xmm2/m128","PSUBSB xmm2/m128, xmm1","psubsb xmm2/m128, xmm1","66 0F E8 /r","V","V","SSE2","","rw,r","","" +"PSUBSW mm1, mm2/m64","PSUBSW mm2/m64, mm1","psubsw mm2/m64, mm1","0F E9 /r","V","V","MMX","","rw,r","","" +"PSUBSW xmm1, xmm2/m128","PSUBSW xmm2/m128, xmm1","psubsw xmm2/m128, xmm1","66 0F E9 /r","V","V","SSE2","","rw,r","","" +"PSUBUSB mm1, mm2/m64","PSUBUSB mm2/m64, mm1","psubusb mm2/m64, mm1","0F D8 /r","V","V","MMX","","rw,r","","" +"PSUBUSB xmm1, xmm2/m128","PSUBUSB xmm2/m128, xmm1","psubusb xmm2/m128, xmm1","66 0F D8 /r","V","V","SSE2","","rw,r","","" +"PSUBUSW mm1, mm2/m64","PSUBUSW mm2/m64, mm1","psubusw mm2/m64, mm1","0F D9 /r","V","V","MMX","","rw,r","","" +"PSUBUSW xmm1, xmm2/m128","PSUBUSW xmm2/m128, xmm1","psubusw xmm2/m128, xmm1","66 0F D9 /r","V","V","SSE2","","rw,r","","" +"PSUBW mm1, mm2/m64","PSUBW mm2/m64, mm1","psubw mm2/m64, mm1","0F F9 /r","V","V","MMX","","rw,r","","" +"PSUBW xmm1, xmm2/m128","PSUBW xmm2/m128, xmm1","psubw xmm2/m128, xmm1","66 0F F9 /r","V","V","SSE2","","rw,r","","" +"PSWAPD mm1, mm2/m64","PSWAPD mm2/m64, mm1","pswapd mm2/m64, mm1","0F 0F BB /r","V","V","3DNOW","amd","rw,r","","" +"PTEST xmm1, xmm2/m128","PTEST xmm2/m128, xmm1","ptest xmm2/m128, xmm1","66 0F 38 17 /r","V","V","SSE4_1","","r,r","","" +"PTWRITE r/m32","PTWRITEL r/m32","ptwritel r/m32","F3 0F AE /4","V","V","","operand16,operand32","r","Y","32" +"PTWRITE r/m64","PTWRITEQ r/m64","ptwriteq r/m64","F3 REX.W 0F AE /4","N.S.","V","","","r","Y","64" +"PUNPCKHBW mm1, mm2/m64","PUNPCKHBW mm2/m64, mm1","punpckhbw mm2/m64, mm1","0F 68 /r","V","V","MMX","","rw,r","","" +"PUNPCKHBW xmm1, xmm2/m128","PUNPCKHBW xmm2/m128, xmm1","punpckhbw xmm2/m128, xmm1","66 0F 68 /r","V","V","SSE2","","rw,r","","" +"PUNPCKHDQ mm1, mm2/m64","PUNPCKHLQ mm2/m64, mm1","punpckhdq mm2/m64, mm1","0F 6A /r","V","V","MMX","","rw,r","","" +"PUNPCKHDQ xmm1, xmm2/m128","PUNPCKHLQ xmm2/m128, xmm1","punpckhdq xmm2/m128, xmm1","66 0F 6A /r","V","V","SSE2","","rw,r","","" +"PUNPCKHQDQ xmm1, xmm2/m128","PUNPCKHQDQ xmm2/m128, xmm1","punpckhqdq xmm2/m128, xmm1","66 0F 6D /r","V","V","SSE2","","rw,r","","" +"PUNPCKHWD mm1, mm2/m64","PUNPCKHWL mm2/m64, mm1","punpckhwd mm2/m64, mm1","0F 69 /r","V","V","MMX","","rw,r","","" +"PUNPCKHWD xmm1, xmm2/m128","PUNPCKHWL xmm2/m128, xmm1","punpckhwd xmm2/m128, xmm1","66 0F 69 /r","V","V","SSE2","","rw,r","","" +"PUNPCKLBW mm1, mm2/m32","PUNPCKLBW mm2/m32, mm1","punpcklbw mm2/m32, mm1","0F 60 /r","V","V","MMX","","rw,r","","" +"PUNPCKLBW xmm1, xmm2/m128","PUNPCKLBW xmm2/m128, xmm1","punpcklbw xmm2/m128, xmm1","66 0F 60 /r","V","V","SSE2","","rw,r","","" +"PUNPCKLDQ mm1, mm2/m32","PUNPCKLLQ mm2/m32, mm1","punpckldq mm2/m32, mm1","0F 62 /r","V","V","MMX","","rw,r","","" +"PUNPCKLDQ xmm1, xmm2/m128","PUNPCKLLQ xmm2/m128, xmm1","punpckldq xmm2/m128, xmm1","66 0F 62 /r","V","V","SSE2","","rw,r","","" +"PUNPCKLQDQ xmm1, xmm2/m128","PUNPCKLQDQ xmm2/m128, xmm1","punpcklqdq xmm2/m128, xmm1","66 0F 6C /r","V","V","SSE2","","rw,r","","" +"PUNPCKLWD mm1, mm2/m32","PUNPCKLWL mm2/m32, mm1","punpcklwd mm2/m32, mm1","0F 61 /r","V","V","MMX","","rw,r","","" +"PUNPCKLWD xmm1, xmm2/m128","PUNPCKLWL xmm2/m128, xmm1","punpcklwd xmm2/m128, xmm1","66 0F 61 /r","V","V","SSE2","","rw,r","","" +"PUSHAD","PUSHAL","pushal","60","V","N.S.","","operand32","","","" +"PUSHA","PUSHAW","pushaw","60","V","N.S.","","operand16","","","" +"PUSHFD","PUSHFL","pushfl","9C","V","N.S.","","operand32","","","" +"PUSHFQ","PUSHFQ","pushfq","9C","N.S.","V","","default64","","","" +"PUSHF","PUSHFW","pushfw","9C","V","V","","operand16","","","" +"PUSH r/m32","PUSHL r/m32","pushl r/m32","FF /6","V","N.S.","","operand32","r","Y","32" +"PUSH r32op","PUSHL r32op","pushl r32op","50+rd","V","N.S.","","operand32","r","Y","32" +"PUSH r/m64","PUSHQ r/m64","pushq r/m64","FF /6","N.S.","V","","default64","r","Y","64" +"PUSH r64op","PUSHQ r64op","pushq r64op","50+ro","N.S.","V","","default64","r","Y","64" +"PUSH imm16","PUSHW imm16","pushw imm16","68 iw","V","V","","operand16","r","Y","" +"PUSH r/m16","PUSHW r/m16","pushw r/m16","FF /6","V","V","","operand16","r","Y","16" +"PUSH r16op","PUSHW r16op","pushw r16op","50+rw","V","V","","operand16","r","Y","16" +"PUSH CS","PUSHW/PUSHL/PUSHQ CS","pushw/pushl/pushq CS","0E","V","N.S.","","","r","Y","" +"PUSH DS","PUSHW/PUSHL/PUSHQ DS","pushw/pushl/pushq DS","1E","V","N.S.","","","r","Y","" +"PUSH ES","PUSHW/PUSHL/PUSHQ ES","pushw/pushl/pushq ES","06","V","N.S.","","","r","Y","" +"PUSH FS","PUSHW/PUSHL/PUSHQ FS","pushw/pushl/pushq FS","0F A0","V","V","","operand16","r","Y","" +"PUSH FS","PUSHW/PUSHL/PUSHQ FS","pushw/pushl/pushq FS","0F A0","N.S.","V","","default64","r","Y","" +"PUSH FS","PUSHW/PUSHL/PUSHQ FS","pushw/pushl/pushq FS","0F A0","V","N.S.","","operand32","r","Y","" +"PUSH GS","PUSHW/PUSHL/PUSHQ GS","pushw/pushl/pushq GS","0F A8","N.S.","V","","default64","r","Y","" +"PUSH GS","PUSHW/PUSHL/PUSHQ GS","pushw/pushl/pushq GS","0F A8","V","N.S.","","operand32","r","Y","" +"PUSH GS","PUSHW/PUSHL/PUSHQ GS","pushw/pushl/pushq GS","0F A8","V","V","","operand16","r","Y","" +"PUSH SS","PUSHW/PUSHL/PUSHQ SS","pushw/pushl/pushq SS","16","V","N.S.","","","r","Y","" +"PUSH imm8","PUSHW/PUSHL/PUSHQ imm8","pushw/pushl/pushq imm8","6A ib","V","N.S.","","operand32","r","Y","" +"PUSH imm8","PUSHW/PUSHL/PUSHQ imm8","pushw/pushl/pushq imm8","6A ib","N.S.","V","","default64","r","Y","" +"PUSH imm8","PUSHW/PUSHL/PUSHQ imm8","pushw/pushl/pushq imm8","6A ib","V","V","","operand16","r","Y","" +"PXOR mm1, mm2/m64","PXOR mm2/m64, mm1","pxor mm2/m64, mm1","0F EF /r","V","V","MMX","","rw,r","","" +"PXOR xmm1, xmm2/m128","PXOR xmm2/m128, xmm1","pxor xmm2/m128, xmm1","66 0F EF /r","V","V","SSE2","","rw,r","","" +"RCL r/m8, 1","RCLB 1, r/m8","rclb 1, r/m8","D0 /2","V","V","","","rw,r","Y","8" +"RCL r/m8, 1","RCLB 1, r/m8","rclb 1, r/m8","REX D0 /2","N.E.","V","","pseudo64","w,r","Y","8" +"RCL r/m8, CL","RCLB CL, r/m8","rclb CL, r/m8","D2 /2","V","V","","","rw,r","Y","8" +"RCL r/m8, CL","RCLB CL, r/m8","rclb CL, r/m8","REX D2 /2","N.E.","V","","pseudo64","w,r","Y","8" +"RCL r/m8, imm8","RCLB imm8, r/m8","rclb imm8, r/m8","REX C0 /2 ib","N.E.","V","","pseudo64","w,r","Y","8" +"RCL r/m8, imm8u","RCLB imm8u, r/m8","rclb imm8u, r/m8","C0 /2 ib","V","V","","","rw,r","Y","8" +"RCL r/m32, 1","RCLL 1, r/m32","rcll 1, r/m32","D1 /2","V","V","","operand32","rw,r","Y","32" +"RCL r/m32, CL","RCLL CL, r/m32","rcll CL, r/m32","D3 /2","V","V","","operand32","rw,r","Y","32" +"RCL r/m32, imm8u","RCLL imm8u, r/m32","rcll imm8u, r/m32","C1 /2 ib","V","V","","operand32","rw,r","Y","32" +"RCL r/m64, 1","RCLQ 1, r/m64","rclq 1, r/m64","REX.W D1 /2","N.S.","V","","","rw,r","Y","64" +"RCL r/m64, CL","RCLQ CL, r/m64","rclq CL, r/m64","REX.W D3 /2","N.S.","V","","","rw,r","Y","64" +"RCL r/m64, imm8u","RCLQ imm8u, r/m64","rclq imm8u, r/m64","REX.W C1 /2 ib","N.S.","V","","","rw,r","Y","64" +"RCL r/m16, 1","RCLW 1, r/m16","rclw 1, r/m16","D1 /2","V","V","","operand16","rw,r","Y","16" +"RCL r/m16, CL","RCLW CL, r/m16","rclw CL, r/m16","D3 /2","V","V","","operand16","rw,r","Y","16" +"RCL r/m16, imm8u","RCLW imm8u, r/m16","rclw imm8u, r/m16","C1 /2 ib","V","V","","operand16","rw,r","Y","16" +"RCPPS xmm1, xmm2/m128","RCPPS xmm2/m128, xmm1","rcpps xmm2/m128, xmm1","0F 53 /r","V","V","SSE","","w,r","","" +"RCPSS xmm1, xmm2/m32","RCPSS xmm2/m32, xmm1","rcpss xmm2/m32, xmm1","F3 0F 53 /r","V","V","SSE","","w,r","","" +"RCR r/m8, 1","RCRB 1, r/m8","rcrb 1, r/m8","D0 /3","V","V","","","rw,r","Y","8" +"RCR r/m8, 1","RCRB 1, r/m8","rcrb 1, r/m8","REX D0 /3","N.E.","V","","pseudo64","w,r","Y","8" +"RCR r/m8, CL","RCRB CL, r/m8","rcrb CL, r/m8","D2 /3","V","V","","","rw,r","Y","8" +"RCR r/m8, CL","RCRB CL, r/m8","rcrb CL, r/m8","REX D2 /3","N.E.","V","","pseudo64","w,r","Y","8" +"RCR r/m8, imm8","RCRB imm8, r/m8","rcrb imm8, r/m8","REX C0 /3 ib","N.E.","V","","pseudo64","w,r","Y","8" +"RCR r/m8, imm8u","RCRB imm8u, r/m8","rcrb imm8u, r/m8","C0 /3 ib","V","V","","","rw,r","Y","8" +"RCR r/m32, 1","RCRL 1, r/m32","rcrl 1, r/m32","D1 /3","V","V","","operand32","rw,r","Y","32" +"RCR r/m32, CL","RCRL CL, r/m32","rcrl CL, r/m32","D3 /3","V","V","","operand32","rw,r","Y","32" +"RCR r/m32, imm8u","RCRL imm8u, r/m32","rcrl imm8u, r/m32","C1 /3 ib","V","V","","operand32","rw,r","Y","32" +"RCR r/m64, 1","RCRQ 1, r/m64","rcrq 1, r/m64","REX.W D1 /3","N.S.","V","","","rw,r","Y","64" +"RCR r/m64, CL","RCRQ CL, r/m64","rcrq CL, r/m64","REX.W D3 /3","N.S.","V","","","rw,r","Y","64" +"RCR r/m64, imm8u","RCRQ imm8u, r/m64","rcrq imm8u, r/m64","REX.W C1 /3 ib","N.S.","V","","","rw,r","Y","64" +"RCR r/m16, 1","RCRW 1, r/m16","rcrw 1, r/m16","D1 /3","V","V","","operand16","rw,r","Y","16" +"RCR r/m16, CL","RCRW CL, r/m16","rcrw CL, r/m16","D3 /3","V","V","","operand16","rw,r","Y","16" +"RCR r/m16, imm8u","RCRW imm8u, r/m16","rcrw imm8u, r/m16","C1 /3 ib","V","V","","operand16","rw,r","Y","16" +"RDFSBASE rmr32","RDFSBASEL rmr32","rdfsbase rmr32","F3 0F AE /0","N.S.","V","FSGSBASE","modrm_regonly,operand16,operand32","w","Y","32" +"RDFSBASE rmr64","RDFSBASEQ rmr64","rdfsbase rmr64","F3 REX.W 0F AE /0","N.S.","V","FSGSBASE","modrm_regonly","w","Y","64" +"RDGSBASE rmr32","RDGSBASEL rmr32","rdgsbase rmr32","F3 0F AE /1","N.S.","V","FSGSBASE","modrm_regonly,operand16,operand32","w","Y","32" +"RDGSBASE rmr64","RDGSBASEQ rmr64","rdgsbase rmr64","F3 REX.W 0F AE /1","N.S.","V","FSGSBASE","modrm_regonly","w","Y","64" +"RDMSR","RDMSR","rdmsr","0F 32","V","V","Pentium","","","","" +"RDPKRU","RDPKRU","rdpkru","0F 01 EE","V","V","PKU","","","","" +"RDPMC","RDPMC","rdpmc","0F 33","V","V","","","","","" +"RDRAND rmr32","RDRANDL rmr32","rdrand rmr32","0F C7 /6","V","V","RDRAND","modrm_regonly,operand32","w","Y","32" +"RDRAND rmr64","RDRANDQ rmr64","rdrand rmr64","REX.W 0F C7 /6","N.S.","V","RDRAND","modrm_regonly","w","Y","64" +"RDRAND rmr16","RDRANDW rmr16","rdrand rmr16","0F C7 /6","V","V","RDRAND","modrm_regonly,operand16","w","Y","16" +"RDSEED rmr32","RDSEEDL rmr32","rdseed rmr32","0F C7 /7","V","V","RDSEED","modrm_regonly,operand32","w","Y","32" +"RDSEED rmr64","RDSEEDQ rmr64","rdseed rmr64","REX.W 0F C7 /7","N.S.","V","RDSEED","modrm_regonly","w","Y","64" +"RDSEED rmr16","RDSEEDW rmr16","rdseed rmr16","0F C7 /7","V","V","RDSEED","modrm_regonly,operand16","w","Y","16" +"RDSSPD rmr32","RDSSPD rmr32","rdsspd rmr32","F3 0F 1E /1","V","V","CET","modrm_regonly,operand16,operand32","w","","" +"RDSSPQ rmr64","RDSSPQ rmr64","rdsspq rmr64","F3 REX.W 0F 1E /1","N.S.","V","CET","modrm_regonly","w","","" +"RDTSC","RDTSC","rdtsc","0F 31","V","V","Pentium","","","","" +"RDTSCP","RDTSCP","rdtscp","0F 01 F9","V","V","RDTSCP","","","","" +"RET_FAR","RETFW/RETFL/RETFQ","lretw/lretl/lretl","CB","V","V","","","","","" +"RET_FAR imm16u","RETFW/RETFL/RETFQ imm16u","lretw/lretl/lretl imm16u","CA iw","V","V","","","r","","" +"RET","RETW/RETL/RETQ","retw/retl/retq","C3","N.S.","V","","default64","","","" +"RET","RETW/RETL/RETQ","retw/retl/retq","C3","V","N.S.","","","","","" +"RET imm16u","RETW/RETL/RETQ imm16u","retw/retl/retq imm16u","C2 iw","N.S.","V","","default64","r","","" +"RET imm16u","RETW/RETL/RETQ imm16u","retw/retl/retq imm16u","C2 iw","V","N.S.","","","r","","" +"ROL r/m8, 1","ROLB 1, r/m8","rolb 1, r/m8","D0 /0","V","V","","","rw,r","Y","8" +"ROL r/m8, 1","ROLB 1, r/m8","rolb 1, r/m8","REX D0 /0","N.E.","V","","pseudo64","w,r","Y","8" +"ROL r/m8, CL","ROLB CL, r/m8","rolb CL, r/m8","D2 /0","V","V","","","rw,r","Y","8" +"ROL r/m8, CL","ROLB CL, r/m8","rolb CL, r/m8","REX D2 /0","N.E.","V","","pseudo64","w,r","Y","8" +"ROL r/m8, imm8","ROLB imm8, r/m8","rolb imm8, r/m8","REX C0 /0 ib","N.E.","V","","pseudo64","w,r","Y","8" +"ROL r/m8, imm8u","ROLB imm8u, r/m8","rolb imm8u, r/m8","C0 /0 ib","V","V","","","rw,r","Y","8" +"ROL r/m32, 1","ROLL 1, r/m32","roll 1, r/m32","D1 /0","V","V","","operand32","rw,r","Y","32" +"ROL r/m32, CL","ROLL CL, r/m32","roll CL, r/m32","D3 /0","V","V","","operand32","rw,r","Y","32" +"ROL r/m32, imm8u","ROLL imm8u, r/m32","roll imm8u, r/m32","C1 /0 ib","V","V","","operand32","rw,r","Y","32" +"ROL r/m64, 1","ROLQ 1, r/m64","rolq 1, r/m64","REX.W D1 /0","N.S.","V","","","rw,r","Y","64" +"ROL r/m64, CL","ROLQ CL, r/m64","rolq CL, r/m64","REX.W D3 /0","N.S.","V","","","rw,r","Y","64" +"ROL r/m64, imm8u","ROLQ imm8u, r/m64","rolq imm8u, r/m64","REX.W C1 /0 ib","N.S.","V","","","rw,r","Y","64" +"ROL r/m16, 1","ROLW 1, r/m16","rolw 1, r/m16","D1 /0","V","V","","operand16","rw,r","Y","16" +"ROL r/m16, CL","ROLW CL, r/m16","rolw CL, r/m16","D3 /0","V","V","","operand16","rw,r","Y","16" +"ROL r/m16, imm8u","ROLW imm8u, r/m16","rolw imm8u, r/m16","C1 /0 ib","V","V","","operand16","rw,r","Y","16" +"ROR r/m8, 1","RORB 1, r/m8","rorb 1, r/m8","D0 /1","V","V","","","rw,r","Y","8" +"ROR r/m8, 1","RORB 1, r/m8","rorb 1, r/m8","REX D0 /1","N.E.","V","","pseudo64","w,r","Y","8" +"ROR r/m8, CL","RORB CL, r/m8","rorb CL, r/m8","D2 /1","V","V","","","rw,r","Y","8" +"ROR r/m8, CL","RORB CL, r/m8","rorb CL, r/m8","REX D2 /1","N.E.","V","","pseudo64","w,r","Y","8" +"ROR r/m8, imm8","RORB imm8, r/m8","rorb imm8, r/m8","REX C0 /1 ib","N.E.","V","","pseudo64","w,r","Y","8" +"ROR r/m8, imm8u","RORB imm8u, r/m8","rorb imm8u, r/m8","C0 /1 ib","V","V","","","rw,r","Y","8" +"ROR r/m32, 1","RORL 1, r/m32","rorl 1, r/m32","D1 /1","V","V","","operand32","rw,r","Y","32" +"ROR r/m32, CL","RORL CL, r/m32","rorl CL, r/m32","D3 /1","V","V","","operand32","rw,r","Y","32" +"ROR r/m32, imm8u","RORL imm8u, r/m32","rorl imm8u, r/m32","C1 /1 ib","V","V","","operand32","rw,r","Y","32" +"ROR r/m64, 1","RORQ 1, r/m64","rorq 1, r/m64","REX.W D1 /1","N.S.","V","","","rw,r","Y","64" +"ROR r/m64, CL","RORQ CL, r/m64","rorq CL, r/m64","REX.W D3 /1","N.S.","V","","","rw,r","Y","64" +"ROR r/m64, imm8u","RORQ imm8u, r/m64","rorq imm8u, r/m64","REX.W C1 /1 ib","N.S.","V","","","rw,r","Y","64" +"ROR r/m16, 1","RORW 1, r/m16","rorw 1, r/m16","D1 /1","V","V","","operand16","rw,r","Y","16" +"ROR r/m16, CL","RORW CL, r/m16","rorw CL, r/m16","D3 /1","V","V","","operand16","rw,r","Y","16" +"ROR r/m16, imm8u","RORW imm8u, r/m16","rorw imm8u, r/m16","C1 /1 ib","V","V","","operand16","rw,r","Y","16" +"RORX r32, r/m32, imm8u","RORXL imm8u, r/m32, r32","rorxl imm8u, r/m32, r32","VEX.128.F2.0F3A.W0 F0 /r ib","V","V","BMI2","","w,r,r","Y","32" +"RORX r64, r/m64, imm8u","RORXQ imm8u, r/m64, r64","rorxq imm8u, r/m64, r64","VEX.128.F2.0F3A.W1 F0 /r ib","N.S.","V","BMI2","","w,r,r","Y","64" +"ROUNDPD xmm1, xmm2/m128, imm8u","ROUNDPD imm8u, xmm2/m128, xmm1","roundpd imm8u, xmm2/m128, xmm1","66 0F 3A 09 /r ib","V","V","SSE4_1","","w,r,r","","" +"ROUNDPS xmm1, xmm2/m128, imm8u","ROUNDPS imm8u, xmm2/m128, xmm1","roundps imm8u, xmm2/m128, xmm1","66 0F 3A 08 /r ib","V","V","SSE4_1","","w,r,r","","" +"ROUNDSD xmm1, xmm2/m64, imm8u","ROUNDSD imm8u, xmm2/m64, xmm1","roundsd imm8u, xmm2/m64, xmm1","66 0F 3A 0B /r ib","V","V","SSE4_1","","w,r,r","","" +"ROUNDSS xmm1, xmm2/m32, imm8u","ROUNDSS imm8u, xmm2/m32, xmm1","roundss imm8u, xmm2/m32, xmm1","66 0F 3A 0A /r ib","V","V","SSE4_1","","w,r,r","","" +"RSM","RSM","rsm","0F AA","V","V","","","","","" +"RSQRTPS xmm1, xmm2/m128","RSQRTPS xmm2/m128, xmm1","rsqrtps xmm2/m128, xmm1","0F 52 /r","V","V","SSE","","w,r","","" +"RSQRTSS xmm1, xmm2/m32","RSQRTSS xmm2/m32, xmm1","rsqrtss xmm2/m32, xmm1","F3 0F 52 /r","V","V","SSE","","w,r","","" +"RSTORSSP m64","RSTORSSP m64","rstorssp m64","F3 0F 01 /5","V","V","CET","modrm_memonly","rw","","" +"SAHF","SAHF","sahf","9E","V","V","LAHFSAHF","","","","" +"SAL r/m8, 1","SALB 1, r/m8","salb 1, r/m8","D0 /4","V","V","","pseudo","rw,r","Y","8" +"SAL r/m8, 1","SALB 1, r/m8","salb 1, r/m8","REX D0 /4","N.E.","V","","pseudo","rw,r","Y","8" +"SAL r/m8, CL","SALB CL, r/m8","salb CL, r/m8","D2 /4","V","V","","pseudo","rw,r","Y","8" +"SAL r/m8, CL","SALB CL, r/m8","salb CL, r/m8","REX D2 /4","N.E.","V","","pseudo","rw,r","Y","8" +"SAL r/m8, imm8","SALB imm8, r/m8","salb imm8, r/m8","C0 /4 ib","V","V","","pseudo","rw,r","Y","8" +"SAL r/m8, imm8","SALB imm8, r/m8","salb imm8, r/m8","REX C0 /4 ib","N.E.","V","","pseudo","rw,r","Y","8" +"SALC","SALC","salc","D6","V","N.S.","","","","","" +"SAL r/m32, 1","SALL 1, r/m32","sall 1, r/m32","D1 /4","V","V","","operand32,pseudo","rw,r","Y","32" +"SAL r/m32, CL","SALL CL, r/m32","sall CL, r/m32","D3 /4","V","V","","operand32,pseudo","rw,r","Y","32" +"SAL r/m32, imm8","SALL imm8, r/m32","sall imm8, r/m32","C1 /4 ib","V","V","","operand32,pseudo","rw,r","Y","32" +"SAL r/m64, 1","SALQ 1, r/m64","salq 1, r/m64","REX.W D1 /4","N.E.","V","","pseudo","rw,r","Y","64" +"SAL r/m64, CL","SALQ CL, r/m64","salq CL, r/m64","REX.W D3 /4","N.E.","V","","pseudo","rw,r","Y","64" +"SAL r/m64, imm8","SALQ imm8, r/m64","salq imm8, r/m64","REX.W C1 /4 ib","N.E.","V","","pseudo","rw,r","Y","64" +"SAL r/m16, 1","SALW 1, r/m16","salw 1, r/m16","D1 /4","V","V","","operand16,pseudo","rw,r","Y","16" +"SAL r/m16, CL","SALW CL, r/m16","salw CL, r/m16","D3 /4","V","V","","operand16,pseudo","rw,r","Y","16" +"SAL r/m16, imm8","SALW imm8, r/m16","salw imm8, r/m16","C1 /4 ib","V","V","","operand16,pseudo","rw,r","Y","16" +"SAR r/m8, 1","SARB 1, r/m8","sarb 1, r/m8","D0 /7","V","V","","","rw,r","Y","8" +"SAR r/m8, 1","SARB 1, r/m8","sarb 1, r/m8","REX D0 /7","N.E.","V","","pseudo64","rw,r","Y","8" +"SAR r/m8, CL","SARB CL, r/m8","sarb CL, r/m8","D2 /7","V","V","","","rw,r","Y","8" +"SAR r/m8, CL","SARB CL, r/m8","sarb CL, r/m8","REX D2 /7","N.E.","V","","pseudo64","rw,r","Y","8" +"SAR r/m8, imm8","SARB imm8, r/m8","sarb imm8, r/m8","REX C0 /7 ib","N.E.","V","","pseudo64","rw,r","Y","8" +"SAR r/m8, imm8u","SARB imm8u, r/m8","sarb imm8u, r/m8","C0 /7 ib","V","V","","","rw,r","Y","8" +"SAR r/m32, 1","SARL 1, r/m32","sarl 1, r/m32","D1 /7","V","V","","operand32","rw,r","Y","32" +"SAR r/m32, CL","SARL CL, r/m32","sarl CL, r/m32","D3 /7","V","V","","operand32","rw,r","Y","32" +"SAR r/m32, imm8u","SARL imm8u, r/m32","sarl imm8u, r/m32","C1 /7 ib","V","V","","operand32","rw,r","Y","32" +"SAR r/m64, 1","SARQ 1, r/m64","sarq 1, r/m64","REX.W D1 /7","N.S.","V","","","rw,r","Y","64" +"SAR r/m64, CL","SARQ CL, r/m64","sarq CL, r/m64","REX.W D3 /7","N.S.","V","","","rw,r","Y","64" +"SAR r/m64, imm8u","SARQ imm8u, r/m64","sarq imm8u, r/m64","REX.W C1 /7 ib","N.S.","V","","","rw,r","Y","64" +"SAR r/m16, 1","SARW 1, r/m16","sarw 1, r/m16","D1 /7","V","V","","operand16","rw,r","Y","16" +"SAR r/m16, CL","SARW CL, r/m16","sarw CL, r/m16","D3 /7","V","V","","operand16","rw,r","Y","16" +"SAR r/m16, imm8u","SARW imm8u, r/m16","sarw imm8u, r/m16","C1 /7 ib","V","V","","operand16","rw,r","Y","16" +"SARX r32, r/m32, r32V","SARXL r32V, r/m32, r32","sarxl r32V, r/m32, r32","VEX.NDS.128.F3.0F38.W0 F7 /r","V","V","BMI2","","w,r,r","Y","32" +"SARX r64, r/m64, r64V","SARXQ r64V, r/m64, r64","sarxq r64V, r/m64, r64","VEX.NDS.128.F3.0F38.W1 F7 /r","N.S.","V","BMI2","","w,r,r","Y","64" +"SAVESSP","SAVESSP","savessp","F3 0F 01 EA","V","V","CET","","","","" +"SBB AL, imm8","SBBB imm8, AL","sbbb imm8, AL","1C ib","V","V","","","rw,r","Y","8" +"SBB r/m8, imm8","SBBB imm8, r/m8","sbbb imm8, r/m8","80 /3 ib","V","V","","","rw,r","Y","8" +"SBB r/m8, imm8","SBBB imm8, r/m8","sbbb imm8, r/m8","82 /3 ib","V","N.S.","","","rw,r","Y","8" +"SBB r/m8, imm8","SBBB imm8, r/m8","sbbb imm8, r/m8","REX 80 /3 ib","N.E.","V","","pseudo64","w,r","Y","8" +"SBB r8, r/m8","SBBB r/m8, r8","sbbb r/m8, r8","1A /r","V","V","","","rw,r","Y","8" +"SBB r8, r/m8","SBBB r/m8, r8","sbbb r/m8, r8","REX 1A /r","N.E.","V","","pseudo64","w,r","Y","8" +"SBB r/m8, r8","SBBB r8, r/m8","sbbb r8, r/m8","18 /r","V","V","","","rw,r","Y","8" +"SBB r/m8, r8","SBBB r8, r/m8","sbbb r8, r/m8","REX 18 /r","N.E.","V","","pseudo64","w,r","Y","8" +"SBB EAX, imm32","SBBL imm32, EAX","sbbl imm32, EAX","1D id","V","V","","operand32","rw,r","Y","32" +"SBB r/m32, imm32","SBBL imm32, r/m32","sbbl imm32, r/m32","81 /3 id","V","V","","operand32","rw,r","Y","32" +"SBB r/m32, imm8","SBBL imm8, r/m32","sbbl imm8, r/m32","83 /3 ib","V","V","","operand32","rw,r","Y","32" +"SBB r32, r/m32","SBBL r/m32, r32","sbbl r/m32, r32","1B /r","V","V","","operand32","rw,r","Y","32" +"SBB r/m32, r32","SBBL r32, r/m32","sbbl r32, r/m32","19 /r","V","V","","operand32","rw,r","Y","32" +"SBB RAX, imm32","SBBQ imm32, RAX","sbbq imm32, RAX","REX.W 1D id","N.S.","V","","","rw,r","Y","64" +"SBB r/m64, imm32","SBBQ imm32, r/m64","sbbq imm32, r/m64","REX.W 81 /3 id","N.S.","V","","","rw,r","Y","64" +"SBB r/m64, imm8","SBBQ imm8, r/m64","sbbq imm8, r/m64","REX.W 83 /3 ib","N.S.","V","","","rw,r","Y","64" +"SBB r64, r/m64","SBBQ r/m64, r64","sbbq r/m64, r64","REX.W 1B /r","N.S.","V","","","rw,r","Y","64" +"SBB r/m64, r64","SBBQ r64, r/m64","sbbq r64, r/m64","REX.W 19 /r","N.S.","V","","","rw,r","Y","64" +"SBB AX, imm16","SBBW imm16, AX","sbbw imm16, AX","1D iw","V","V","","operand16","rw,r","Y","16" +"SBB r/m16, imm16","SBBW imm16, r/m16","sbbw imm16, r/m16","81 /3 iw","V","V","","operand16","rw,r","Y","16" +"SBB r/m16, imm8","SBBW imm8, r/m16","sbbw imm8, r/m16","83 /3 ib","V","V","","operand16","rw,r","Y","16" +"SBB r16, r/m16","SBBW r/m16, r16","sbbw r/m16, r16","1B /r","V","V","","operand16","rw,r","Y","16" +"SBB r/m16, r16","SBBW r16, r/m16","sbbw r16, r/m16","19 /r","V","V","","operand16","rw,r","Y","16" +"SCASB","SCASB","scasb","AE","V","V","","","","","" +"SCASD","SCASL","scasl","AF","V","V","","operand32","","","" +"SCASQ","SCASQ","scasq","REX.W AF","N.S.","V","","","","","" +"SCASW","SCASW","scasw","AF","V","V","","operand16","","","" +"SETAE r/m8","SETCC r/m8","setae r/m8","0F 93 /r","V","V","","","w","","" +"SETNB r/m8","SETCC r/m8","setnb r/m8","0F 93 /r","V","V","","pseudo","r","","" +"SETNC r/m8","SETCC r/m8","setnc r/m8","0F 93 /r","V","V","","pseudo","r","","" +"SETAE r/m8","SETCC r/m8","setae r/m8","REX 0F 93 /r","N.E.","V","","pseudo64","r","","" +"SETNB r/m8","SETCC r/m8","setnb r/m8","REX 0F 93 /r","N.E.","V","","pseudo","r","","" +"SETNC r/m8","SETCC r/m8","setnc r/m8","REX 0F 93 /r","N.E.","V","","pseudo","r","","" +"SETB r/m8","SETCS r/m8","setb r/m8","0F 92 /r","V","V","","","w","","" +"SETC r/m8","SETCS r/m8","setc r/m8","0F 92 /r","V","V","","pseudo","r","","" +"SETNAE r/m8","SETCS r/m8","setnae r/m8","0F 92 /r","V","V","","pseudo","r","","" +"SETB r/m8","SETCS r/m8","setb r/m8","REX 0F 92 /r","N.E.","V","","pseudo64","r","","" +"SETC r/m8","SETCS r/m8","setc r/m8","REX 0F 92 /r","N.E.","V","","pseudo","r","","" +"SETNAE r/m8","SETCS r/m8","setnae r/m8","REX 0F 92 /r","N.E.","V","","pseudo","r","","" +"SETE r/m8","SETEQ r/m8","sete r/m8","0F 94 /r","V","V","","","w","","" +"SETZ r/m8","SETEQ r/m8","setz r/m8","0F 94 /r","V","V","","pseudo","r","","" +"SETE r/m8","SETEQ r/m8","sete r/m8","REX 0F 94 /r","N.E.","V","","pseudo64","r","","" +"SETZ r/m8","SETEQ r/m8","setz r/m8","REX 0F 94 /r","N.E.","V","","pseudo","r","","" +"SETGE r/m8","SETGE r/m8","setge r/m8","0F 9D /r","V","V","","","w","","" +"SETNL r/m8","SETGE r/m8","setnl r/m8","0F 9D /r","V","V","","pseudo","r","","" +"SETGE r/m8","SETGE r/m8","setge r/m8","REX 0F 9D /r","N.E.","V","","pseudo64","r","","" +"SETNL r/m8","SETGE r/m8","setnl r/m8","REX 0F 9D /r","N.E.","V","","pseudo","r","","" +"SETG r/m8","SETGT r/m8","setg r/m8","0F 9F /r","V","V","","","w","","" +"SETNLE r/m8","SETGT r/m8","setnle r/m8","0F 9F /r","V","V","","pseudo","r","","" +"SETG r/m8","SETGT r/m8","setg r/m8","REX 0F 9F /r","N.E.","V","","pseudo64","r","","" +"SETNLE r/m8","SETGT r/m8","setnle r/m8","REX 0F 9F /r","N.E.","V","","pseudo","r","","" +"SETA r/m8","SETHI r/m8","seta r/m8","0F 97 /r","V","V","","","w","","" +"SETNBE r/m8","SETHI r/m8","setnbe r/m8","0F 97 /r","V","V","","pseudo","r","","" +"SETA r/m8","SETHI r/m8","seta r/m8","REX 0F 97 /r","N.E.","V","","pseudo64","r","","" +"SETNBE r/m8","SETHI r/m8","setnbe r/m8","REX 0F 97 /r","N.E.","V","","pseudo","r","","" +"SETLE r/m8","SETLE r/m8","setle r/m8","0F 9E /r","V","V","","","w","","" +"SETNG r/m8","SETLE r/m8","setng r/m8","0F 9E /r","V","V","","pseudo","r","","" +"SETLE r/m8","SETLE r/m8","setle r/m8","REX 0F 9E /r","N.E.","V","","pseudo64","r","","" +"SETNG r/m8","SETLE r/m8","setng r/m8","REX 0F 9E /r","N.E.","V","","pseudo","r","","" +"SETBE r/m8","SETLS r/m8","setbe r/m8","0F 96 /r","V","V","","","w","","" +"SETNA r/m8","SETLS r/m8","setna r/m8","0F 96 /r","V","V","","pseudo","r","","" +"SETBE r/m8","SETLS r/m8","setbe r/m8","REX 0F 96 /r","N.E.","V","","pseudo64","r","","" +"SETNA r/m8","SETLS r/m8","setna r/m8","REX 0F 96 /r","N.E.","V","","pseudo","r","","" +"SETL r/m8","SETLT r/m8","setl r/m8","0F 9C /r","V","V","","","w","","" +"SETNGE r/m8","SETLT r/m8","setnge r/m8","0F 9C /r","V","V","","pseudo","r","","" +"SETL r/m8","SETLT r/m8","setl r/m8","REX 0F 9C /r","N.E.","V","","pseudo64","r","","" +"SETNGE r/m8","SETLT r/m8","setnge r/m8","REX 0F 9C /r","N.E.","V","","pseudo","r","","" +"SETS r/m8","SETMI r/m8","sets r/m8","0F 98 /r","V","V","","","w","","" +"SETS r/m8","SETMI r/m8","sets r/m8","REX 0F 98 /r","N.E.","V","","pseudo64","r","","" +"SETNE r/m8","SETNE r/m8","setne r/m8","0F 95 /r","V","V","","","w","","" +"SETNZ r/m8","SETNE r/m8","setnz r/m8","0F 95 /r","V","V","","pseudo","r","","" +"SETNE r/m8","SETNE r/m8","setne r/m8","REX 0F 95 /r","N.E.","V","","pseudo64","r","","" +"SETNZ r/m8","SETNE r/m8","setnz r/m8","REX 0F 95 /r","N.E.","V","","pseudo","r","","" +"SETNO r/m8","SETOC r/m8","setno r/m8","0F 91 /r","V","V","","","w","","" +"SETNO r/m8","SETOC r/m8","setno r/m8","REX 0F 91 /r","N.E.","V","","pseudo64","r","","" +"SETO r/m8","SETOS r/m8","seto r/m8","0F 90 /r","V","V","","","w","","" +"SETO r/m8","SETOS r/m8","seto r/m8","REX 0F 90 /r","N.E.","V","","pseudo64","r","","" +"SETNP r/m8","SETPC r/m8","setnp r/m8","0F 9B /r","V","V","","","w","","" +"SETPO r/m8","SETPC r/m8","setpo r/m8","0F 9B /r","V","V","","pseudo","r","","" +"SETNP r/m8","SETPC r/m8","setnp r/m8","REX 0F 9B /r","N.E.","V","","pseudo64","r","","" +"SETPO r/m8","SETPC r/m8","setpo r/m8","REX 0F 9B /r","N.E.","V","","pseudo","r","","" +"SETNS r/m8","SETPL r/m8","setns r/m8","0F 99 /r","V","V","","","w","","" +"SETNS r/m8","SETPL r/m8","setns r/m8","REX 0F 99 /r","N.E.","V","","pseudo64","r","","" +"SETP r/m8","SETPS r/m8","setp r/m8","0F 9A /r","V","V","","","w","","" +"SETPE r/m8","SETPS r/m8","setpe r/m8","0F 9A /r","V","V","","pseudo","r","","" +"SETP r/m8","SETPS r/m8","setp r/m8","REX 0F 9A /r","N.E.","V","","pseudo64","r","","" +"SETPE r/m8","SETPS r/m8","setpe r/m8","REX 0F 9A /r","N.E.","V","","pseudo","r","","" +"SETSSBSY","SETSSBSY","setssbsy","F3 0F 01 E8","V","V","CET","","","","" +"SFENCE","SFENCE","sfence","0F AE /7","V","V","SSE","","","","" +"SGDT m16&32","SGDT m16&32","sgdt m16&32","0F 01 /0","V","N.S.","","modrm_memonly","w","","" +"SGDT m16&64","SGDT m16&64","sgdt m16&64","0F 01 /0","N.S.","V","","default64,modrm_memonly","w","","" +"SHA1MSG1 xmm1, xmm2/m128","SHA1MSG1 xmm2/m128, xmm1","sha1msg1 xmm2/m128, xmm1","0F 38 C9 /r","V","V","SHA","","rw,r","","" +"SHA1MSG2 xmm1, xmm2/m128","SHA1MSG2 xmm2/m128, xmm1","sha1msg2 xmm2/m128, xmm1","0F 38 CA /r","V","V","SHA","","rw,r","","" +"SHA1NEXTE xmm1, xmm2/m128","SHA1NEXTE xmm2/m128, xmm1","sha1nexte xmm2/m128, xmm1","0F 38 C8 /r","V","V","SHA","","rw,r","","" +"SHA1RNDS4 xmm1, xmm2/m128, imm8u:2","SHA1RNDS4 imm8u:2, xmm2/m128, xmm1","sha1rnds4 imm8u:2, xmm2/m128, xmm1","0F 3A CC /r ib","V","V","SHA","","rw,r,r","","" +"SHA256MSG1 xmm1, xmm2/m128","SHA256MSG1 xmm2/m128, xmm1","sha256msg1 xmm2/m128, xmm1","0F 38 CC /r","V","V","SHA","","rw,r","","" +"SHA256MSG2 xmm1, xmm2/m128","SHA256MSG2 xmm2/m128, xmm1","sha256msg2 xmm2/m128, xmm1","0F 38 CD /r","V","V","SHA","","rw,r","","" +"SHA256RNDS2 xmm1, xmm2/m128, ","SHA256RNDS2 , xmm2/m128, xmm1","sha256rnds2 , xmm2/m128, xmm1","0F 38 CB /r","V","V","SHA","","rw,r,r","","" +"SHL r/m8, 1","SHLB 1, r/m8","shlb 1, r/m8","D0 /4","V","V","","","rw,r","Y","8" +"SHL r/m8, 1","SHLB 1, r/m8","shlb 1, r/m8","D0 /6","V","V","","","rw,r","Y","8" +"SHL r/m8, 1","SHLB 1, r/m8","shlb 1, r/m8","REX D0 /4","N.E.","V","","pseudo64","rw,r","Y","8" +"SHL r/m8, CL","SHLB CL, r/m8","shlb CL, r/m8","D2 /4","V","V","","","rw,r","Y","8" +"SHL r/m8, CL","SHLB CL, r/m8","shlb CL, r/m8","D2 /6","V","V","","","rw,r","Y","8" +"SHL r/m8, CL","SHLB CL, r/m8","shlb CL, r/m8","REX D2 /4","N.E.","V","","pseudo64","rw,r","Y","8" +"SHL r/m8, imm8","SHLB imm8, r/m8","shlb imm8, r/m8","REX C0 /4 ib","N.E.","V","","pseudo64","rw,r","Y","8" +"SHL r/m8, imm8u","SHLB imm8u, r/m8","shlb imm8u, r/m8","C0 /4 ib","V","V","","","rw,r","Y","8" +"SHL r/m8, imm8u","SHLB imm8u, r/m8","shlb imm8u, r/m8","C0 /6 ib","V","V","","","rw,r","Y","8" +"SHL r/m32, 1","SHLL 1, r/m32","shll 1, r/m32","D1 /4","V","V","","operand32","rw,r","Y","32" +"SHL r/m32, 1","SHLL 1, r/m32","shll 1, r/m32","D1 /6","V","V","","operand32","rw,r","Y","32" +"SHL r/m32, CL","SHLL CL, r/m32","shll CL, r/m32","D3 /4","V","V","","operand32","rw,r","Y","32" +"SHL r/m32, CL","SHLL CL, r/m32","shll CL, r/m32","D3 /6","V","V","","operand32","rw,r","Y","32" +"SHLD r/m32, r32, CL","SHLL CL, r32, r/m32","shldl CL, r32, r/m32","0F A5 /r","V","V","","operand32","rw,r,r","Y","32" +"SHL r/m32, imm8u","SHLL imm8u, r/m32","shll imm8u, r/m32","C1 /4 ib","V","V","","operand32","rw,r","Y","32" +"SHL r/m32, imm8u","SHLL imm8u, r/m32","shll imm8u, r/m32","C1 /6 ib","V","V","","operand32","rw,r","Y","32" +"SHLD r/m32, r32, imm8u","SHLL imm8u, r32, r/m32","shldl imm8u, r32, r/m32","0F A4 /r ib","V","V","","operand32","rw,r,r","Y","32" +"SHL r/m64, 1","SHLQ 1, r/m64","shlq 1, r/m64","REX.W D1 /4","N.S.","V","","","rw,r","Y","64" +"SHL r/m64, 1","SHLQ 1, r/m64","shlq 1, r/m64","REX.W D1 /6","N.S.","V","","","rw,r","Y","64" +"SHL r/m64, CL","SHLQ CL, r/m64","shlq CL, r/m64","REX.W D3 /4","N.S.","V","","","rw,r","Y","64" +"SHL r/m64, CL","SHLQ CL, r/m64","shlq CL, r/m64","REX.W D3 /6","N.S.","V","","","rw,r","Y","64" +"SHLD r/m64, r64, CL","SHLQ CL, r64, r/m64","shldq CL, r64, r/m64","REX.W 0F A5 /r","N.S.","V","","","rw,r,r","Y","64" +"SHL r/m64, imm8u","SHLQ imm8u, r/m64","shlq imm8u, r/m64","REX.W C1 /4 ib","N.S.","V","","","rw,r","Y","64" +"SHL r/m64, imm8u","SHLQ imm8u, r/m64","shlq imm8u, r/m64","REX.W C1 /6 ib","N.S.","V","","","rw,r","Y","64" +"SHLD r/m64, r64, imm8u","SHLQ imm8u, r64, r/m64","shldq imm8u, r64, r/m64","REX.W 0F A4 /r ib","N.S.","V","","","rw,r,r","Y","64" +"SHL r/m16, 1","SHLW 1, r/m16","shlw 1, r/m16","D1 /4","V","V","","operand16","rw,r","Y","16" +"SHL r/m16, 1","SHLW 1, r/m16","shlw 1, r/m16","D1 /6","V","V","","operand16","rw,r","Y","16" +"SHL r/m16, CL","SHLW CL, r/m16","shlw CL, r/m16","D3 /4","V","V","","operand16","rw,r","Y","16" +"SHL r/m16, CL","SHLW CL, r/m16","shlw CL, r/m16","D3 /6","V","V","","operand16","rw,r","Y","16" +"SHLD r/m16, r16, CL","SHLW CL, r16, r/m16","shldw CL, r16, r/m16","0F A5 /r","V","V","","operand16","rw,r,r","Y","16" +"SHL r/m16, imm8u","SHLW imm8u, r/m16","shlw imm8u, r/m16","C1 /4 ib","V","V","","operand16","rw,r","Y","16" +"SHL r/m16, imm8u","SHLW imm8u, r/m16","shlw imm8u, r/m16","C1 /6 ib","V","V","","operand16","rw,r","Y","16" +"SHLD r/m16, r16, imm8u","SHLW imm8u, r16, r/m16","shldw imm8u, r16, r/m16","0F A4 /r ib","V","V","","operand16","rw,r,r","Y","16" +"SHLX r32, r/m32, r32V","SHLXL r32V, r/m32, r32","shlxl r32V, r/m32, r32","VEX.NDS.128.66.0F38.W0 F7 /r","V","V","BMI2","","w,r,r","Y","32" +"SHLX r64, r/m64, r64V","SHLXQ r64V, r/m64, r64","shlxq r64V, r/m64, r64","VEX.NDS.128.66.0F38.W1 F7 /r","N.S.","V","BMI2","","w,r,r","Y","64" +"SHR r/m8, 1","SHRB 1, r/m8","shrb 1, r/m8","D0 /5","V","V","","","rw,r","Y","8" +"SHR r/m8, 1","SHRB 1, r/m8","shrb 1, r/m8","REX D0 /5","N.E.","V","","pseudo64","rw,r","Y","8" +"SHR r/m8, CL","SHRB CL, r/m8","shrb CL, r/m8","D2 /5","V","V","","","rw,r","Y","8" +"SHR r/m8, CL","SHRB CL, r/m8","shrb CL, r/m8","REX D2 /5","N.E.","V","","pseudo64","rw,r","Y","8" +"SHR r/m8, imm8","SHRB imm8, r/m8","shrb imm8, r/m8","REX C0 /5 ib","N.E.","V","","pseudo64","rw,r","Y","8" +"SHR r/m8, imm8u","SHRB imm8u, r/m8","shrb imm8u, r/m8","C0 /5 ib","V","V","","","rw,r","Y","8" +"SHR r/m32, 1","SHRL 1, r/m32","shrl 1, r/m32","D1 /5","V","V","","operand32","rw,r","Y","32" +"SHR r/m32, CL","SHRL CL, r/m32","shrl CL, r/m32","D3 /5","V","V","","operand32","rw,r","Y","32" +"SHRD r/m32, r32, CL","SHRL CL, r32, r/m32","shrdl CL, r32, r/m32","0F AD /r","V","V","","operand32","rw,r,r","Y","32" +"SHR r/m32, imm8u","SHRL imm8u, r/m32","shrl imm8u, r/m32","C1 /5 ib","V","V","","operand32","rw,r","Y","32" +"SHRD r/m32, r32, imm8u","SHRL imm8u, r32, r/m32","shrdl imm8u, r32, r/m32","0F AC /r ib","V","V","","operand32","rw,r,r","Y","32" +"SHR r/m64, 1","SHRQ 1, r/m64","shrq 1, r/m64","REX.W D1 /5","N.S.","V","","","rw,r","Y","64" +"SHR r/m64, CL","SHRQ CL, r/m64","shrq CL, r/m64","REX.W D3 /5","N.S.","V","","","rw,r","Y","64" +"SHRD r/m64, r64, CL","SHRQ CL, r64, r/m64","shrdq CL, r64, r/m64","REX.W 0F AD /r","N.S.","V","","","rw,r,r","Y","64" +"SHR r/m64, imm8u","SHRQ imm8u, r/m64","shrq imm8u, r/m64","REX.W C1 /5 ib","N.S.","V","","","rw,r","Y","64" +"SHRD r/m64, r64, imm8u","SHRQ imm8u, r64, r/m64","shrdq imm8u, r64, r/m64","REX.W 0F AC /r ib","N.S.","V","","","rw,r,r","Y","64" +"SHR r/m16, 1","SHRW 1, r/m16","shrw 1, r/m16","D1 /5","V","V","","operand16","rw,r","Y","16" +"SHR r/m16, CL","SHRW CL, r/m16","shrw CL, r/m16","D3 /5","V","V","","operand16","rw,r","Y","16" +"SHRD r/m16, r16, CL","SHRW CL, r16, r/m16","shrdw CL, r16, r/m16","0F AD /r","V","V","","operand16","rw,r,r","Y","16" +"SHR r/m16, imm8u","SHRW imm8u, r/m16","shrw imm8u, r/m16","C1 /5 ib","V","V","","operand16","rw,r","Y","16" +"SHRD r/m16, r16, imm8u","SHRW imm8u, r16, r/m16","shrdw imm8u, r16, r/m16","0F AC /r ib","V","V","","operand16","rw,r,r","Y","16" +"SHRX r32, r/m32, r32V","SHRXL r32V, r/m32, r32","shrxl r32V, r/m32, r32","VEX.NDS.128.F2.0F38.W0 F7 /r","V","V","BMI2","","w,r,r","Y","32" +"SHRX r64, r/m64, r64V","SHRXQ r64V, r/m64, r64","shrxq r64V, r/m64, r64","VEX.NDS.128.F2.0F38.W1 F7 /r","N.S.","V","BMI2","","w,r,r","Y","64" +"SHUFPD xmm1, xmm2/m128, imm8u","SHUFPD imm8u, xmm2/m128, xmm1","shufpd imm8u, xmm2/m128, xmm1","66 0F C6 /r ib","V","V","SSE2","","rw,r,r","","" +"SHUFPS xmm1, xmm2/m128, imm8u","SHUFPS imm8u, xmm2/m128, xmm1","shufps imm8u, xmm2/m128, xmm1","0F C6 /r ib","V","V","SSE","","rw,r,r","","" +"SIDT m16&32","SIDT m16&32","sidt m16&32","0F 01 /1","V","N.S.","","modrm_memonly","w","","" +"SIDT m16&64","SIDT m16&64","sidt m16&64","0F 01 /1","N.S.","V","","default64,modrm_memonly","w","","" +"SKINIT EAX","SKINIT EAX","skinit EAX","0F 01 DE","V","V","SVM","amd,modrm_regonly","r","","" +"SLDT r/m16","SLDTW r/m16","sldtw r/m16","0F 00 /0","V","V","","operand16","w","Y","16" +"SLDT r32/m16","SLDT{L/W} r32/m16","sldt{l/w} r32/m16","0F 00 /0","V","V","","operand32","w","Y","" +"SLDT r64/m16","SLDT{Q/W} r64/m16","sldt{q/w} r64/m16","REX.W 0F 00 /0","N.S.","V","","","w","Y","" +"SLWPCB rmr32","SLWPCBL rmr32","slwpcbl rmr32","XOP.128.09.W0 12 /1","V","V","XOP","amd,modrm_regonly,operand16,operand32","w","Y","32" +"SLWPCB rmr64","SLWPCBQ rmr64","slwpcbq rmr64","XOP.128.09.W0 12 /1","N.S.","V","XOP","amd,modrm_regonly,operand64","w","Y","64" +"SMSW r/m16","SMSWW r/m16","smsww r/m16","0F 01 /4","V","V","","operand16","w","Y","16" +"SMSW r32/m16","SMSW{L/W} r32/m16","smsw{l/w} r32/m16","0F 01 /4","V","V","","operand32","w","Y","" +"SMSW r64/m16","SMSW{Q/W} r64/m16","smsw{q/w} r64/m16","REX.W 0F 01 /4","N.S.","V","","","w","Y","" +"SQRTPD xmm1, xmm2/m128","SQRTPD xmm2/m128, xmm1","sqrtpd xmm2/m128, xmm1","66 0F 51 /r","V","V","SSE2","","w,r","","" +"SQRTPS xmm1, xmm2/m128","SQRTPS xmm2/m128, xmm1","sqrtps xmm2/m128, xmm1","0F 51 /r","V","V","SSE","","w,r","","" +"SQRTSD xmm1, xmm2/m64","SQRTSD xmm2/m64, xmm1","sqrtsd xmm2/m64, xmm1","F2 0F 51 /r","V","V","SSE2","","w,r","","" +"SQRTSS xmm1, xmm2/m32","SQRTSS xmm2/m32, xmm1","sqrtss xmm2/m32, xmm1","F3 0F 51 /r","V","V","SSE","","w,r","","" +"STAC","STAC","stac","0F 01 CB","V","V","","","","","" +"STC","STC","stc","F9","V","V","","","","","" +"STD","STD","std","FD","V","V","","","","","" +"STGI","STGI","stgi","0F 01 DC","V","V","SVM","amd","","","" +"STI","STI","sti","FB","V","V","","","","","" +"STMXCSR m32","STMXCSR m32","stmxcsr m32","0F AE /3","V","V","SSE","modrm_memonly","w","","" +"STOSB","STOSB","stosb","AA","V","V","","","","","" +"STOSD","STOSL","stosl","AB","V","V","","operand32","","","" +"STOSQ","STOSQ","stosq","REX.W AB","N.S.","V","","","","","" +"STOSW","STOSW","stosw","AB","V","V","","operand16","","","" +"STR r/m16","STRW r/m16","strw r/m16","0F 00 /1","V","V","","operand16","w","Y","16" +"STR r32/m16","STR{L/W} r32/m16","str{l/w} r32/m16","0F 00 /1","V","V","","operand32","w","Y","" +"STR r64/m16","STR{Q/W} r64/m16","str{q/w} r64/m16","REX.W 0F 00 /1","N.S.","V","","","w","Y","" +"SUB AL, imm8","SUBB imm8, AL","subb imm8, AL","2C ib","V","V","","","rw,r","Y","8" +"SUB r/m8, imm8","SUBB imm8, r/m8","subb imm8, r/m8","80 /5 ib","V","V","","","rw,r","Y","8" +"SUB r/m8, imm8","SUBB imm8, r/m8","subb imm8, r/m8","82 /5 ib","V","N.S.","","","rw,r","Y","8" +"SUB r/m8, imm8","SUBB imm8, r/m8","subb imm8, r/m8","REX 80 /5 ib","N.E.","V","","pseudo64","rw,r","Y","8" +"SUB r8, r/m8","SUBB r/m8, r8","subb r/m8, r8","2A /r","V","V","","","rw,r","Y","8" +"SUB r8, r/m8","SUBB r/m8, r8","subb r/m8, r8","REX 2A /r","N.E.","V","","pseudo64","rw,r","Y","8" +"SUB r/m8, r8","SUBB r8, r/m8","subb r8, r/m8","28 /r","V","V","","","rw,r","Y","8" +"SUB r/m8, r8","SUBB r8, r/m8","subb r8, r/m8","REX 28 /r","N.E.","V","","pseudo64","rw,r","Y","8" +"SUB EAX, imm32","SUBL imm32, EAX","subl imm32, EAX","2D id","V","V","","operand32","rw,r","Y","32" +"SUB r/m32, imm32","SUBL imm32, r/m32","subl imm32, r/m32","81 /5 id","V","V","","operand32","rw,r","Y","32" +"SUB r/m32, imm8","SUBL imm8, r/m32","subl imm8, r/m32","83 /5 ib","V","V","","operand32","rw,r","Y","32" +"SUB r32, r/m32","SUBL r/m32, r32","subl r/m32, r32","2B /r","V","V","","operand32","rw,r","Y","32" +"SUB r/m32, r32","SUBL r32, r/m32","subl r32, r/m32","29 /r","V","V","","operand32","rw,r","Y","32" +"SUBPD xmm1, xmm2/m128","SUBPD xmm2/m128, xmm1","subpd xmm2/m128, xmm1","66 0F 5C /r","V","V","SSE2","","rw,r","","" +"SUBPS xmm1, xmm2/m128","SUBPS xmm2/m128, xmm1","subps xmm2/m128, xmm1","0F 5C /r","V","V","SSE","","rw,r","","" +"SUB RAX, imm32","SUBQ imm32, RAX","subq imm32, RAX","REX.W 2D id","N.S.","V","","","rw,r","Y","64" +"SUB r/m64, imm32","SUBQ imm32, r/m64","subq imm32, r/m64","REX.W 81 /5 id","N.S.","V","","","rw,r","Y","64" +"SUB r/m64, imm8","SUBQ imm8, r/m64","subq imm8, r/m64","REX.W 83 /5 ib","N.S.","V","","","rw,r","Y","64" +"SUB r64, r/m64","SUBQ r/m64, r64","subq r/m64, r64","REX.W 2B /r","N.S.","V","","","rw,r","Y","64" +"SUB r/m64, r64","SUBQ r64, r/m64","subq r64, r/m64","REX.W 29 /r","N.S.","V","","","rw,r","Y","64" +"SUBSD xmm1, xmm2/m64","SUBSD xmm2/m64, xmm1","subsd xmm2/m64, xmm1","F2 0F 5C /r","V","V","SSE2","","rw,r","","" +"SUBSS xmm1, xmm2/m32","SUBSS xmm2/m32, xmm1","subss xmm2/m32, xmm1","F3 0F 5C /r","V","V","SSE","","rw,r","","" +"SUB AX, imm16","SUBW imm16, AX","subw imm16, AX","2D iw","V","V","","operand16","rw,r","Y","16" +"SUB r/m16, imm16","SUBW imm16, r/m16","subw imm16, r/m16","81 /5 iw","V","V","","operand16","rw,r","Y","16" +"SUB r/m16, imm8","SUBW imm8, r/m16","subw imm8, r/m16","83 /5 ib","V","V","","operand16","rw,r","Y","16" +"SUB r16, r/m16","SUBW r/m16, r16","subw r/m16, r16","2B /r","V","V","","operand16","rw,r","Y","16" +"SUB r/m16, r16","SUBW r16, r/m16","subw r16, r/m16","29 /r","V","V","","operand16","rw,r","Y","16" +"SWAPGS","SWAPGS","swapgs","0F 01 F8","N.S.","V","","","","","" +"SYSCALL","SYSCALL","syscall","0F 05","N.S.","V","","default64","","","" +"SYSCALL","SYSCALL","syscall","0F 05","V","N.S.","AMD","amd","","","" +"SYSENTER","SYSENTER","sysenter","0F 34","V","V","PPRO","","","","" +"SYSEXIT","SYSEXIT","sysexit","0F 35","V","V","PPRO","","","","" +"SYSEXIT","SYSEXIT","sysexit","REX.W 0F 35","N.E.","V","","pseudo","","","" +"SYSRET","SYSRET","sysretw/sysretl/sysretl","0F 07","V","N.S.","AMD","amd","","","" +"SYSRET","SYSRET","sysretw/sysretl/sysretl","0F 07","N.S.","V","","operand32,operand64","","","" +"SYSRET","SYSRET","sysretw/sysretl/sysretl","REX.W 0F 07","I","V","","pseudo","","","" +"T1MSKC r32V, r/m32","T1MSKCL r/m32, r32V","t1mskcl r/m32, r32V","XOP.NDD.128.09.WIG 01 /7","V","V","TBM","amd,operand16,operand32","w,r","Y","32" +"T1MSKC r64V, r/m64","T1MSKCQ r/m64, r64V","t1mskcq r/m64, r64V","XOP.NDD.128.09.WIG 01 /7","N.S.","V","TBM","amd,operand64","w,r","Y","64" +"TEST AL, imm8","TESTB imm8, AL","testb imm8, AL","A8 ib","V","V","","","r,r","Y","8" +"TEST r/m8, imm8","TESTB imm8, r/m8","testb imm8, r/m8","F6 /0 ib","V","V","","","r,r","Y","8" +"TEST r/m8, imm8","TESTB imm8, r/m8","testb imm8, r/m8","F6 /1 ib","V","V","","","r,r","Y","8" +"TEST r/m8, imm8","TESTB imm8, r/m8","testb imm8, r/m8","REX F6 /0 ib","N.E.","V","","pseudo64","r,r","Y","8" +"TEST r/m8, r8","TESTB r8, r/m8","testb r8, r/m8","84 /r","V","V","","","r,r","Y","8" +"TEST r/m8, r8","TESTB r8, r/m8","testb r8, r/m8","REX 84 /r","N.E.","V","","pseudo64","r,r","Y","8" +"TEST EAX, imm32","TESTL imm32, EAX","testl imm32, EAX","A9 id","V","V","","operand32","r,r","Y","32" +"TEST r/m32, imm32","TESTL imm32, r/m32","testl imm32, r/m32","F7 /0 id","V","V","","operand32","r,r","Y","32" +"TEST r/m32, imm32","TESTL imm32, r/m32","testl imm32, r/m32","F7 /1 id","V","V","","operand32","r,r","Y","32" +"TEST r/m32, r32","TESTL r32, r/m32","testl r32, r/m32","85 /r","V","V","","operand32","r,r","Y","32" +"TEST RAX, imm32","TESTQ imm32, RAX","testq imm32, RAX","REX.W A9 id","N.S.","V","","","r,r","Y","64" +"TEST r/m64, imm32","TESTQ imm32, r/m64","testq imm32, r/m64","REX.W F7 /0 id","N.S.","V","","","r,r","Y","64" +"TEST r/m64, imm32","TESTQ imm32, r/m64","testq imm32, r/m64","REX.W F7 /1 id","N.S.","V","","","r,r","Y","64" +"TEST r/m64, r64","TESTQ r64, r/m64","testq r64, r/m64","REX.W 85 /r","N.S.","V","","","r,r","Y","64" +"TEST AX, imm16","TESTW imm16, AX","testw imm16, AX","A9 iw","V","V","","operand16","r,r","Y","16" +"TEST r/m16, imm16","TESTW imm16, r/m16","testw imm16, r/m16","F7 /0 iw","V","V","","operand16","r,r","Y","16" +"TEST r/m16, imm16","TESTW imm16, r/m16","testw imm16, r/m16","F7 /1 iw","V","V","","operand16","r,r","Y","16" +"TEST r/m16, r16","TESTW r16, r/m16","testw r16, r/m16","85 /r","V","V","","operand16","r,r","Y","16" +"TZCNT r32, r/m32","TZCNTL r/m32, r32","tzcntl r/m32, r32","F3 0F BC /r","V","V","BMI1","operand32","w,r","Y","32" +"TZCNT r64, r/m64","TZCNTQ r/m64, r64","tzcntq r/m64, r64","F3 REX.W 0F BC /r","N.S.","V","BMI1","","w,r","Y","64" +"TZCNT r16, r/m16","TZCNTW r/m16, r16","tzcntw r/m16, r16","F3 0F BC /r","V","V","BMI1","operand16","w,r","Y","16" +"TZMSK r32V, r/m32","TZMSKL r/m32, r32V","tzmskl r/m32, r32V","XOP.NDD.128.09.WIG 01 /4","V","V","TBM","amd,operand16,operand32","w,r","Y","32" +"TZMSK r64V, r/m64","TZMSKQ r/m64, r64V","tzmskq r/m64, r64V","XOP.NDD.128.09.WIG 01 /4","N.S.","V","TBM","amd,operand64","w,r","Y","64" +"UCOMISD xmm1, xmm2/m64","UCOMISD xmm2/m64, xmm1","ucomisd xmm2/m64, xmm1","66 0F 2E /r","V","V","SSE2","","r,r","","" +"UCOMISS xmm1, xmm2/m32","UCOMISS xmm2/m32, xmm1","ucomiss xmm2/m32, xmm1","0F 2E /r","V","V","SSE","","r,r","","" +"UD0 r32, r/m32","UD0 r/m32, r32","ud0 r/m32, r32","0F FF /r","V","V","PPRO","","r,r","","" +"UD1 r32, r/m32","UD1 r/m32, r32","ud1 r/m32, r32","0F B9 /r","V","V","PPRO","","r,r","","" +"UD2","UD2","ud2","0F 0B","V","V","PPRO","","","","" +"UNPCKHPD xmm1, xmm2/m128","UNPCKHPD xmm2/m128, xmm1","unpckhpd xmm2/m128, xmm1","66 0F 15 /r","V","V","SSE2","","rw,r","","" +"UNPCKHPS xmm1, xmm2/m128","UNPCKHPS xmm2/m128, xmm1","unpckhps xmm2/m128, xmm1","0F 15 /r","V","V","SSE","","rw,r","","" +"UNPCKLPD xmm1, xmm2/m128","UNPCKLPD xmm2/m128, xmm1","unpcklpd xmm2/m128, xmm1","66 0F 14 /r","V","V","SSE2","","rw,r","","" +"UNPCKLPS xmm1, xmm2/m128","UNPCKLPS xmm2/m128, xmm1","unpcklps xmm2/m128, xmm1","0F 14 /r","V","V","SSE","","rw,r","","" +"V4FMADDPS zmm1, {k}{z}, zmmV+3, m128","V4FMADDPS m128, zmmV+3, {k}{z}, zmm1","v4fmaddps m128, zmmV+3, {k}{z}, zmm1","EVEX.DDS.512.F2.0F38.W0 9A /r","V","V","AVX512_4FMAPS","modrm_memonly,scale16","rw,r,r,r","","" +"V4FMADDSS xmm1, {k}{z}, xmmV+3, m128","V4FMADDSS m128, xmmV+3, {k}{z}, xmm1","v4fmaddss m128, xmmV+3, {k}{z}, xmm1","EVEX.DDS.LIG.F2.0F38.W0 9B /r","V","V","AVX512_4FMAPS","modrm_memonly,scale16","rw,r,r,r","","" +"V4FNMADDPS zmm1, {k}{z}, zmmV+3, m128","V4FNMADDPS m128, zmmV+3, {k}{z}, zmm1","v4fnmaddps m128, zmmV+3, {k}{z}, zmm1","EVEX.DDS.512.F2.0F38.W0 AA /r","V","V","AVX512_4FMAPS","modrm_memonly,scale16","rw,r,r,r","","" +"V4FNMADDSS xmm1, {k}{z}, xmmV+3, m128","V4FNMADDSS m128, xmmV+3, {k}{z}, xmm1","v4fnmaddss m128, xmmV+3, {k}{z}, xmm1","EVEX.DDS.LIG.F2.0F38.W0 AB /r","V","V","AVX512_4FMAPS","modrm_memonly,scale16","rw,r,r,r","","" +"VADDPD xmm1, xmmV, xmm2/m128","VADDPD xmm2/m128, xmmV, xmm1","vaddpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 58 /r","V","V","AVX","","w,r,r","","" +"VADDPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VADDPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vaddpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 58 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VADDPD ymm1, ymmV, ymm2/m256","VADDPD ymm2/m256, ymmV, ymm1","vaddpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 58 /r","V","V","AVX","","w,r,r","","" +"VADDPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VADDPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vaddpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 58 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VADDPD zmm1{er}, {k}{z}, zmmV, zmm2","VADDPD zmm2, zmmV, {k}{z}, zmm1{er}","vaddpd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.NDS.512.66.0F.W1 58 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VADDPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VADDPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vaddpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 58 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VADDPS xmm1, xmmV, xmm2/m128","VADDPS xmm2/m128, xmmV, xmm1","vaddps xmm2/m128, xmmV, xmm1","VEX.NDS.128.0F.WIG 58 /r","V","V","AVX","","w,r,r","","" +"VADDPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VADDPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vaddps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.0F.W0 58 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VADDPS ymm1, ymmV, ymm2/m256","VADDPS ymm2/m256, ymmV, ymm1","vaddps ymm2/m256, ymmV, ymm1","VEX.NDS.256.0F.WIG 58 /r","V","V","AVX","","w,r,r","","" +"VADDPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VADDPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vaddps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.0F.W0 58 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VADDPS zmm1{er}, {k}{z}, zmmV, zmm2","VADDPS zmm2, zmmV, {k}{z}, zmm1{er}","vaddps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.NDS.512.0F.W0 58 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VADDPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VADDPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vaddps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.0F.W0 58 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VADDSD xmm1{er}, {k}{z}, xmmV, xmm2","VADDSD xmm2, xmmV, {k}{z}, xmm1{er}","vaddsd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.NDS.128.F2.0F.W1 58 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VADDSD xmm1, xmmV, xmm2/m64","VADDSD xmm2/m64, xmmV, xmm1","vaddsd xmm2/m64, xmmV, xmm1","VEX.NDS.LIG.F2.0F.WIG 58 /r","V","V","AVX","","w,r,r","","" +"VADDSD xmm1, {k}{z}, xmmV, xmm2/m64","VADDSD xmm2/m64, xmmV, {k}{z}, xmm1","vaddsd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F2.0F.W1 58 /r","V","V","AVX512F","scale8","w,r,r,r","","" +"VADDSS xmm1{er}, {k}{z}, xmmV, xmm2","VADDSS xmm2, xmmV, {k}{z}, xmm1{er}","vaddss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.NDS.128.F3.0F.W0 58 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VADDSS xmm1, xmmV, xmm2/m32","VADDSS xmm2/m32, xmmV, xmm1","vaddss xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.F3.0F.WIG 58 /r","V","V","AVX","","w,r,r","","" +"VADDSS xmm1, {k}{z}, xmmV, xmm2/m32","VADDSS xmm2/m32, xmmV, {k}{z}, xmm1","vaddss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F3.0F.W0 58 /r","V","V","AVX512F","scale4","w,r,r,r","","" +"VADDSUBPD xmm1, xmmV, xmm2/m128","VADDSUBPD xmm2/m128, xmmV, xmm1","vaddsubpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG D0 /r","V","V","AVX","","w,r,r","","" +"VADDSUBPD ymm1, ymmV, ymm2/m256","VADDSUBPD ymm2/m256, ymmV, ymm1","vaddsubpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG D0 /r","V","V","AVX","","w,r,r","","" +"VADDSUBPS xmm1, xmmV, xmm2/m128","VADDSUBPS xmm2/m128, xmmV, xmm1","vaddsubps xmm2/m128, xmmV, xmm1","VEX.NDS.128.F2.0F.WIG D0 /r","V","V","AVX","","w,r,r","","" +"VADDSUBPS ymm1, ymmV, ymm2/m256","VADDSUBPS ymm2/m256, ymmV, ymm1","vaddsubps ymm2/m256, ymmV, ymm1","VEX.NDS.256.F2.0F.WIG D0 /r","V","V","AVX","","w,r,r","","" +"VAESDEC xmm1, xmmV, xmm2/m128","VAESDEC xmm2/m128, xmmV, xmm1","vaesdec xmm2/m128, xmmV, xmm1","EVEX.NDS.128.66.0F38.WIG DE /r","V","V","AES+AVX512VL","scale16","w,r,r","","" +"VAESDEC xmm1, xmmV, xmm2/m128","VAESDEC xmm2/m128, xmmV, xmm1","vaesdec xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG DE /r","V","V","AES+AVX","","w,r,r","","" +"VAESDEC ymm1, ymmV, ymm2/m256","VAESDEC ymm2/m256, ymmV, ymm1","vaesdec ymm2/m256, ymmV, ymm1","EVEX.NDS.256.66.0F38.WIG DE /r","V","V","AES+AVX512VL","scale32","w,r,r","","" +"VAESDEC ymm1, ymmV, ymm2/m256","VAESDEC ymm2/m256, ymmV, ymm1","vaesdec ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG DE /r","V","V","VAES+AVX","","w,r,r","","" +"VAESDEC zmm1, zmmV, zmm2/m512","VAESDEC zmm2/m512, zmmV, zmm1","vaesdec zmm2/m512, zmmV, zmm1","EVEX.NDS.512.66.0F38.WIG DE /r","V","V","AES+AVX512F","scale64","w,r,r","","" +"VAESDECLAST xmm1, xmmV, xmm2/m128","VAESDECLAST xmm2/m128, xmmV, xmm1","vaesdeclast xmm2/m128, xmmV, xmm1","EVEX.NDS.128.66.0F38.WIG DF /r","V","V","AES+AVX512VL","scale16","w,r,r","","" +"VAESDECLAST xmm1, xmmV, xmm2/m128","VAESDECLAST xmm2/m128, xmmV, xmm1","vaesdeclast xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG DF /r","V","V","AES+AVX","","w,r,r","","" +"VAESDECLAST ymm1, ymmV, ymm2/m256","VAESDECLAST ymm2/m256, ymmV, ymm1","vaesdeclast ymm2/m256, ymmV, ymm1","EVEX.NDS.256.66.0F38.WIG DF /r","V","V","AES+AVX512VL","scale32","w,r,r","","" +"VAESDECLAST ymm1, ymmV, ymm2/m256","VAESDECLAST ymm2/m256, ymmV, ymm1","vaesdeclast ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG DF /r","V","V","VAES+AVX","","w,r,r","","" +"VAESDECLAST zmm1, zmmV, zmm2/m512","VAESDECLAST zmm2/m512, zmmV, zmm1","vaesdeclast zmm2/m512, zmmV, zmm1","EVEX.NDS.512.66.0F38.WIG DF /r","V","V","AES+AVX512F","scale64","w,r,r","","" +"VAESENC xmm1, xmmV, xmm2/m128","VAESENC xmm2/m128, xmmV, xmm1","vaesenc xmm2/m128, xmmV, xmm1","EVEX.NDS.128.66.0F38.WIG DC /r","V","V","AES+AVX512VL","scale16","w,r,r","","" +"VAESENC xmm1, xmmV, xmm2/m128","VAESENC xmm2/m128, xmmV, xmm1","vaesenc xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG DC /r","V","V","AES+AVX","","w,r,r","","" +"VAESENC ymm1, ymmV, ymm2/m256","VAESENC ymm2/m256, ymmV, ymm1","vaesenc ymm2/m256, ymmV, ymm1","EVEX.NDS.256.66.0F38.WIG DC /r","V","V","AES+AVX512VL","scale32","w,r,r","","" +"VAESENC ymm1, ymmV, ymm2/m256","VAESENC ymm2/m256, ymmV, ymm1","vaesenc ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG DC /r","V","V","VAES+AVX","","w,r,r","","" +"VAESENC zmm1, zmmV, zmm2/m512","VAESENC zmm2/m512, zmmV, zmm1","vaesenc zmm2/m512, zmmV, zmm1","EVEX.NDS.512.66.0F38.WIG DC /r","V","V","AES+AVX512F","scale64","w,r,r","","" +"VAESENCLAST xmm1, xmmV, xmm2/m128","VAESENCLAST xmm2/m128, xmmV, xmm1","vaesenclast xmm2/m128, xmmV, xmm1","EVEX.NDS.128.66.0F38.WIG DD /r","V","V","AES+AVX512VL","scale16","w,r,r","","" +"VAESENCLAST xmm1, xmmV, xmm2/m128","VAESENCLAST xmm2/m128, xmmV, xmm1","vaesenclast xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG DD /r","V","V","AES+AVX","","w,r,r","","" +"VAESENCLAST ymm1, ymmV, ymm2/m256","VAESENCLAST ymm2/m256, ymmV, ymm1","vaesenclast ymm2/m256, ymmV, ymm1","EVEX.NDS.256.66.0F38.WIG DD /r","V","V","AES+AVX512VL","scale32","w,r,r","","" +"VAESENCLAST ymm1, ymmV, ymm2/m256","VAESENCLAST ymm2/m256, ymmV, ymm1","vaesenclast ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG DD /r","V","V","VAES+AVX","","w,r,r","","" +"VAESENCLAST zmm1, zmmV, zmm2/m512","VAESENCLAST zmm2/m512, zmmV, zmm1","vaesenclast zmm2/m512, zmmV, zmm1","EVEX.NDS.512.66.0F38.WIG DD /r","V","V","AES+AVX512F","scale64","w,r,r","","" +"VAESIMC xmm1, xmm2/m128","VAESIMC xmm2/m128, xmm1","vaesimc xmm2/m128, xmm1","VEX.128.66.0F38.WIG DB /r","V","V","AES+AVX","","w,r","","" +"VAESKEYGENASSIST xmm1, xmm2/m128, imm8u","VAESKEYGENASSIST imm8u, xmm2/m128, xmm1","vaeskeygenassist imm8u, xmm2/m128, xmm1","VEX.128.66.0F3A.WIG DF /r ib","V","V","AES+AVX","","w,r,r","","" +"VALIGND xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst, imm8u","VALIGND imm8u, xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","valignd imm8u, xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F3A.W0 03 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r,r","","" +"VALIGND ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst, imm8u","VALIGND imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","valignd imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W0 03 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r,r","","" +"VALIGND zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst, imm8u","VALIGND imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","valignd imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W0 03 /r ib","V","V","AVX512F","bscale4,scale64","w,r,r,r,r","","" +"VALIGNQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst, imm8u","VALIGNQ imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","valignq imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F3A.W1 03 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r,r","","" +"VALIGNQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst, imm8u","VALIGNQ imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","valignq imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W1 03 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r,r","","" +"VALIGNQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst, imm8u","VALIGNQ imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","valignq imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W1 03 /r ib","V","V","AVX512F","bscale8,scale64","w,r,r,r,r","","" +"VANDNPD xmm1, xmmV, xmm2/m128","VANDNPD xmm2/m128, xmmV, xmm1","vandnpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 55 /r","V","V","AVX","","w,r,r","","" +"VANDNPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VANDNPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vandnpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 55 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VANDNPD ymm1, ymmV, ymm2/m256","VANDNPD ymm2/m256, ymmV, ymm1","vandnpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 55 /r","V","V","AVX","","w,r,r","","" +"VANDNPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VANDNPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vandnpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 55 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VANDNPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VANDNPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vandnpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 55 /r","V","V","AVX512DQ","bscale8,scale64","w,r,r,r","","" +"VANDNPS xmm1, xmmV, xmm2/m128","VANDNPS xmm2/m128, xmmV, xmm1","vandnps xmm2/m128, xmmV, xmm1","VEX.NDS.128.0F.WIG 55 /r","V","V","AVX","","w,r,r","","" +"VANDNPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VANDNPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vandnps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.0F.W0 55 /r","V","V","AVX512DQ+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VANDNPS ymm1, ymmV, ymm2/m256","VANDNPS ymm2/m256, ymmV, ymm1","vandnps ymm2/m256, ymmV, ymm1","VEX.NDS.256.0F.WIG 55 /r","V","V","AVX","","w,r,r","","" +"VANDNPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VANDNPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vandnps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.0F.W0 55 /r","V","V","AVX512DQ+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VANDNPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VANDNPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vandnps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.0F.W0 55 /r","V","V","AVX512DQ","bscale4,scale64","w,r,r,r","","" +"VANDPD xmm1, xmmV, xmm2/m128","VANDPD xmm2/m128, xmmV, xmm1","vandpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 54 /r","V","V","AVX","","w,r,r","","" +"VANDPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VANDPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vandpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 54 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VANDPD ymm1, ymmV, ymm2/m256","VANDPD ymm2/m256, ymmV, ymm1","vandpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 54 /r","V","V","AVX","","w,r,r","","" +"VANDPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VANDPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vandpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 54 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VANDPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VANDPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vandpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 54 /r","V","V","AVX512DQ","bscale8,scale64","w,r,r,r","","" +"VANDPS xmm1, xmmV, xmm2/m128","VANDPS xmm2/m128, xmmV, xmm1","vandps xmm2/m128, xmmV, xmm1","VEX.NDS.128.0F.WIG 54 /r","V","V","AVX","","w,r,r","","" +"VANDPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VANDPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vandps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.0F.W0 54 /r","V","V","AVX512DQ+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VANDPS ymm1, ymmV, ymm2/m256","VANDPS ymm2/m256, ymmV, ymm1","vandps ymm2/m256, ymmV, ymm1","VEX.NDS.256.0F.WIG 54 /r","V","V","AVX","","w,r,r","","" +"VANDPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VANDPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vandps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.0F.W0 54 /r","V","V","AVX512DQ+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VANDPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VANDPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vandps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.0F.W0 54 /r","V","V","AVX512DQ","bscale4,scale64","w,r,r,r","","" +"VBLENDMPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VBLENDMPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vblendmpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 65 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VBLENDMPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VBLENDMPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vblendmpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 65 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VBLENDMPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VBLENDMPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vblendmpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 65 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VBLENDMPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VBLENDMPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vblendmps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 65 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VBLENDMPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VBLENDMPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vblendmps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 65 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VBLENDMPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VBLENDMPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vblendmps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 65 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VBLENDPD xmm1, xmmV, xmm2/m128, imm8u","VBLENDPD imm8u, xmm2/m128, xmmV, xmm1","vblendpd imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.WIG 0D /r ib","V","V","AVX","","w,r,r,r","","" +"VBLENDPD ymm1, ymmV, ymm2/m256, imm8u","VBLENDPD imm8u, ymm2/m256, ymmV, ymm1","vblendpd imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.WIG 0D /r ib","V","V","AVX","","w,r,r,r","","" +"VBLENDPS xmm1, xmmV, xmm2/m128, imm8u","VBLENDPS imm8u, xmm2/m128, xmmV, xmm1","vblendps imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.WIG 0C /r ib","V","V","AVX","","w,r,r,r","","" +"VBLENDPS ymm1, ymmV, ymm2/m256, imm8u","VBLENDPS imm8u, ymm2/m256, ymmV, ymm1","vblendps imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.WIG 0C /r ib","V","V","AVX","","w,r,r,r","","" +"VBLENDVPD xmm1, xmmV, xmm2/m128, xmmIH","VBLENDVPD xmmIH, xmm2/m128, xmmV, xmm1","vblendvpd xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 4B /r /is4","V","V","AVX","","w,r,r,r","","" +"VBLENDVPD ymm1, ymmV, ymm2/m256, ymmIH","VBLENDVPD ymmIH, ymm2/m256, ymmV, ymm1","vblendvpd ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 4B /r /is4","V","V","AVX","","w,r,r,r","","" +"VBLENDVPS xmm1, xmmV, xmm2/m128, xmmIH","VBLENDVPS xmmIH, xmm2/m128, xmmV, xmm1","vblendvps xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 4A /r /is4","V","V","AVX","","w,r,r,r","","" +"VBLENDVPS ymm1, ymmV, ymm2/m256, ymmIH","VBLENDVPS ymmIH, ymm2/m256, ymmV, ymm1","vblendvps ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 4A /r /is4","V","V","AVX","","w,r,r,r","","" +"VBROADCASTF128 ymm1, m128","VBROADCASTF128 m128, ymm1","vbroadcastf128 m128, ymm1","VEX.256.66.0F38.W0 1A /r","V","V","AVX","modrm_memonly","w,r","","" +"VBROADCASTF32X2 ymm1, {k}{z}, xmm2/m64","VBROADCASTF32X2 xmm2/m64, {k}{z}, ymm1","vbroadcastf32x2 xmm2/m64, {k}{z}, ymm1","EVEX.256.66.0F38.W0 19 /r","V","V","AVX512DQ+AVX512VL","scale8","w,r,r","","" +"VBROADCASTF32X2 zmm1, {k}{z}, xmm2/m64","VBROADCASTF32X2 xmm2/m64, {k}{z}, zmm1","vbroadcastf32x2 xmm2/m64, {k}{z}, zmm1","EVEX.512.66.0F38.W0 19 /r","V","V","AVX512DQ","scale8","w,r,r","","" +"VBROADCASTF32X4 ymm1, {k}{z}, m128","VBROADCASTF32X4 m128, {k}{z}, ymm1","vbroadcastf32x4 m128, {k}{z}, ymm1","EVEX.256.66.0F38.W0 1A /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale16","w,r,r","","" +"VBROADCASTF32X4 zmm1, {k}{z}, m128","VBROADCASTF32X4 m128, {k}{z}, zmm1","vbroadcastf32x4 m128, {k}{z}, zmm1","EVEX.512.66.0F38.W0 1A /r","V","V","AVX512F","modrm_memonly,scale16","w,r,r","","" +"VBROADCASTF32X8 zmm1, {k}{z}, m256","VBROADCASTF32X8 m256, {k}{z}, zmm1","vbroadcastf32x8 m256, {k}{z}, zmm1","EVEX.512.66.0F38.W0 1B /r","V","V","AVX512DQ","modrm_memonly,scale32","w,r,r","","" +"VBROADCASTF64X2 ymm1, {k}{z}, m128","VBROADCASTF64X2 m128, {k}{z}, ymm1","vbroadcastf64x2 m128, {k}{z}, ymm1","EVEX.256.66.0F38.W1 1A /r","V","V","AVX512DQ+AVX512VL","modrm_memonly,scale16","w,r,r","","" +"VBROADCASTF64X2 zmm1, {k}{z}, m128","VBROADCASTF64X2 m128, {k}{z}, zmm1","vbroadcastf64x2 m128, {k}{z}, zmm1","EVEX.512.66.0F38.W1 1A /r","V","V","AVX512DQ","modrm_memonly,scale16","w,r,r","","" +"VBROADCASTF64X4 zmm1, {k}{z}, m256","VBROADCASTF64X4 m256, {k}{z}, zmm1","vbroadcastf64x4 m256, {k}{z}, zmm1","EVEX.512.66.0F38.W1 1B /r","V","V","AVX512F","modrm_memonly,scale32","w,r,r","","" +"VBROADCASTI128 ymm1, m128","VBROADCASTI128 m128, ymm1","vbroadcasti128 m128, ymm1","VEX.256.66.0F38.W0 5A /r","V","V","AVX2","modrm_memonly","w,r","","" +"VBROADCASTI32X2 xmm1, {k}{z}, xmm2/m64","VBROADCASTI32X2 xmm2/m64, {k}{z}, xmm1","vbroadcasti32x2 xmm2/m64, {k}{z}, xmm1","EVEX.128.66.0F38.W0 59 /r","V","V","AVX512DQ+AVX512VL","scale8","w,r,r","","" +"VBROADCASTI32X2 ymm1, {k}{z}, xmm2/m64","VBROADCASTI32X2 xmm2/m64, {k}{z}, ymm1","vbroadcasti32x2 xmm2/m64, {k}{z}, ymm1","EVEX.256.66.0F38.W0 59 /r","V","V","AVX512DQ+AVX512VL","scale8","w,r,r","","" +"VBROADCASTI32X2 zmm1, {k}{z}, xmm2/m64","VBROADCASTI32X2 xmm2/m64, {k}{z}, zmm1","vbroadcasti32x2 xmm2/m64, {k}{z}, zmm1","EVEX.512.66.0F38.W0 59 /r","V","V","AVX512DQ","scale8","w,r,r","","" +"VBROADCASTI32X4 ymm1, {k}{z}, m128","VBROADCASTI32X4 m128, {k}{z}, ymm1","vbroadcasti32x4 m128, {k}{z}, ymm1","EVEX.256.66.0F38.W0 5A /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale16","w,r,r","","" +"VBROADCASTI32X4 zmm1, {k}{z}, m128","VBROADCASTI32X4 m128, {k}{z}, zmm1","vbroadcasti32x4 m128, {k}{z}, zmm1","EVEX.512.66.0F38.W0 5A /r","V","V","AVX512F","modrm_memonly,scale16","w,r,r","","" +"VBROADCASTI32X8 zmm1, {k}{z}, m256","VBROADCASTI32X8 m256, {k}{z}, zmm1","vbroadcasti32x8 m256, {k}{z}, zmm1","EVEX.512.66.0F38.W0 5B /r","V","V","AVX512DQ","modrm_memonly,scale32","w,r,r","","" +"VBROADCASTI64X2 ymm1, {k}{z}, m128","VBROADCASTI64X2 m128, {k}{z}, ymm1","vbroadcasti64x2 m128, {k}{z}, ymm1","EVEX.256.66.0F38.W1 5A /r","V","V","AVX512DQ+AVX512VL","modrm_memonly,scale16","w,r,r","","" +"VBROADCASTI64X2 zmm1, {k}{z}, m128","VBROADCASTI64X2 m128, {k}{z}, zmm1","vbroadcasti64x2 m128, {k}{z}, zmm1","EVEX.512.66.0F38.W1 5A /r","V","V","AVX512DQ","modrm_memonly,scale16","w,r,r","","" +"VBROADCASTI64X4 zmm1, {k}{z}, m256","VBROADCASTI64X4 m256, {k}{z}, zmm1","vbroadcasti64x4 m256, {k}{z}, zmm1","EVEX.512.66.0F38.W1 5B /r","V","V","AVX512F","modrm_memonly,scale32","w,r,r","","" +"VBROADCASTSD ymm1, m64","VBROADCASTSD m64, ymm1","vbroadcastsd m64, ymm1","VEX.256.66.0F38.W0 19 /r","V","V","AVX","modrm_memonly","w,r","","" +"VBROADCASTSD ymm1, xmm2","VBROADCASTSD xmm2, ymm1","vbroadcastsd xmm2, ymm1","VEX.256.66.0F38.W0 19 /r","V","V","AVX2","modrm_regonly","w,r","","" +"VBROADCASTSD ymm1, {k}{z}, xmm2/m64","VBROADCASTSD xmm2/m64, {k}{z}, ymm1","vbroadcastsd xmm2/m64, {k}{z}, ymm1","EVEX.256.66.0F38.W1 19 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VBROADCASTSD zmm1, {k}{z}, xmm2/m64","VBROADCASTSD xmm2/m64, {k}{z}, zmm1","vbroadcastsd xmm2/m64, {k}{z}, zmm1","EVEX.512.66.0F38.W1 19 /r","V","V","AVX512F","scale8","w,r,r","","" +"VBROADCASTSS xmm1, m32","VBROADCASTSS m32, xmm1","vbroadcastss m32, xmm1","VEX.128.66.0F38.W0 18 /r","V","V","AVX","modrm_memonly","w,r","","" +"VBROADCASTSS ymm1, m32","VBROADCASTSS m32, ymm1","vbroadcastss m32, ymm1","VEX.256.66.0F38.W0 18 /r","V","V","AVX","modrm_memonly","w,r","","" +"VBROADCASTSS xmm1, xmm2","VBROADCASTSS xmm2, xmm1","vbroadcastss xmm2, xmm1","VEX.128.66.0F38.W0 18 /r","V","V","AVX2","modrm_regonly","w,r","","" +"VBROADCASTSS ymm1, xmm2","VBROADCASTSS xmm2, ymm1","vbroadcastss xmm2, ymm1","VEX.256.66.0F38.W0 18 /r","V","V","AVX2","modrm_regonly","w,r","","" +"VBROADCASTSS xmm1, {k}{z}, xmm2/m32","VBROADCASTSS xmm2/m32, {k}{z}, xmm1","vbroadcastss xmm2/m32, {k}{z}, xmm1","EVEX.128.66.0F38.W0 18 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VBROADCASTSS ymm1, {k}{z}, xmm2/m32","VBROADCASTSS xmm2/m32, {k}{z}, ymm1","vbroadcastss xmm2/m32, {k}{z}, ymm1","EVEX.256.66.0F38.W0 18 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VBROADCASTSS zmm1, {k}{z}, xmm2/m32","VBROADCASTSS xmm2/m32, {k}{z}, zmm1","vbroadcastss xmm2/m32, {k}{z}, zmm1","EVEX.512.66.0F38.W0 18 /r","V","V","AVX512F","scale4","w,r,r","","" +"VCMPPD xmm1, xmmV, xmm2/m128, imm8u","VCMPPD imm8u, xmm2/m128, xmmV, xmm1","vcmppd imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG C2 /r ib","V","V","AVX","","w,r,r,r","","" +"VCMPPD k1, {k}, xmmV, xmm2/m128/m64bcst, imm8u","VCMPPD imm8u, xmm2/m128/m64bcst, xmmV, {k}, k1","vcmppd imm8u, xmm2/m128/m64bcst, xmmV, {k}, k1","EVEX.NDS.128.66.0F.W1 C2 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r,r","","" +"VCMPPD ymm1, ymmV, ymm2/m256, imm8u","VCMPPD imm8u, ymm2/m256, ymmV, ymm1","vcmppd imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG C2 /r ib","V","V","AVX","","w,r,r,r","","" +"VCMPPD k1, {k}, ymmV, ymm2/m256/m64bcst, imm8u","VCMPPD imm8u, ymm2/m256/m64bcst, ymmV, {k}, k1","vcmppd imm8u, ymm2/m256/m64bcst, ymmV, {k}, k1","EVEX.NDS.256.66.0F.W1 C2 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r,r","","" +"VCMPPD k1{sae}, {k}, zmmV, zmm2, imm8u","VCMPPD imm8u, zmm2, zmmV, {k}, k1{sae}","vcmppd imm8u, zmm2, zmmV, {k}, k1{sae}","EVEX.NDS.512.66.0F.W1 C2 /r ib","V","V","AVX512F","modrm_regonly","w,r,r,r,r","","" +"VCMPPD k1, {k}, zmmV, zmm2/m512/m64bcst, imm8u","VCMPPD imm8u, zmm2/m512/m64bcst, zmmV, {k}, k1","vcmppd imm8u, zmm2/m512/m64bcst, zmmV, {k}, k1","EVEX.NDS.512.66.0F.W1 C2 /r ib","V","V","AVX512F","bscale8,scale64","w,r,r,r,r","","" +"VCMPPS xmm1, xmmV, xmm2/m128, imm8u","VCMPPS imm8u, xmm2/m128, xmmV, xmm1","vcmpps imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.0F.WIG C2 /r ib","V","V","AVX","","w,r,r,r","","" +"VCMPPS k1, {k}, xmmV, xmm2/m128/m32bcst, imm8u","VCMPPS imm8u, xmm2/m128/m32bcst, xmmV, {k}, k1","vcmpps imm8u, xmm2/m128/m32bcst, xmmV, {k}, k1","EVEX.NDS.128.0F.W0 C2 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r,r","","" +"VCMPPS ymm1, ymmV, ymm2/m256, imm8u","VCMPPS imm8u, ymm2/m256, ymmV, ymm1","vcmpps imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.0F.WIG C2 /r ib","V","V","AVX","","w,r,r,r","","" +"VCMPPS k1, {k}, ymmV, ymm2/m256/m32bcst, imm8u","VCMPPS imm8u, ymm2/m256/m32bcst, ymmV, {k}, k1","vcmpps imm8u, ymm2/m256/m32bcst, ymmV, {k}, k1","EVEX.NDS.256.0F.W0 C2 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r,r","","" +"VCMPPS k1{sae}, {k}, zmmV, zmm2, imm8u","VCMPPS imm8u, zmm2, zmmV, {k}, k1{sae}","vcmpps imm8u, zmm2, zmmV, {k}, k1{sae}","EVEX.NDS.512.0F.W0 C2 /r ib","V","V","AVX512F","modrm_regonly","w,r,r,r,r","","" +"VCMPPS k1, {k}, zmmV, zmm2/m512/m32bcst, imm8u","VCMPPS imm8u, zmm2/m512/m32bcst, zmmV, {k}, k1","vcmpps imm8u, zmm2/m512/m32bcst, zmmV, {k}, k1","EVEX.NDS.512.0F.W0 C2 /r ib","V","V","AVX512F","bscale4,scale64","w,r,r,r,r","","" +"VCMPSD k1{sae}, {k}, xmmV, xmm2, imm8u","VCMPSD imm8u, xmm2, xmmV, {k}, k1{sae}","vcmpsd imm8u, xmm2, xmmV, {k}, k1{sae}","EVEX.NDS.128.F2.0F.W1 C2 /r ib","V","V","AVX512F","modrm_regonly","w,r,r,r,r","","" +"VCMPSD xmm1, xmmV, xmm2/m64, imm8u","VCMPSD imm8u, xmm2/m64, xmmV, xmm1","vcmpsd imm8u, xmm2/m64, xmmV, xmm1","VEX.NDS.LIG.F2.0F.WIG C2 /r ib","V","V","AVX","","w,r,r,r","","" +"VCMPSD k1, {k}, xmmV, xmm2/m64, imm8u","VCMPSD imm8u, xmm2/m64, xmmV, {k}, k1","vcmpsd imm8u, xmm2/m64, xmmV, {k}, k1","EVEX.NDS.LIG.F2.0F.W1 C2 /r ib","V","V","AVX512F","scale8","w,r,r,r,r","","" +"VCMPSS k1{sae}, {k}, xmmV, xmm2, imm8u","VCMPSS imm8u, xmm2, xmmV, {k}, k1{sae}","vcmpss imm8u, xmm2, xmmV, {k}, k1{sae}","EVEX.NDS.128.F3.0F.W0 C2 /r ib","V","V","AVX512F","modrm_regonly","w,r,r,r,r","","" +"VCMPSS xmm1, xmmV, xmm2/m32, imm8u","VCMPSS imm8u, xmm2/m32, xmmV, xmm1","vcmpss imm8u, xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.F3.0F.WIG C2 /r ib","V","V","AVX","","w,r,r,r","","" +"VCMPSS k1, {k}, xmmV, xmm2/m32, imm8u","VCMPSS imm8u, xmm2/m32, xmmV, {k}, k1","vcmpss imm8u, xmm2/m32, xmmV, {k}, k1","EVEX.NDS.LIG.F3.0F.W0 C2 /r ib","V","V","AVX512F","scale4","w,r,r,r,r","","" +"VCOMISD xmm1{sae}, xmm2","VCOMISD xmm2, xmm1{sae}","vcomisd xmm2, xmm1{sae}","EVEX.128.66.0F.W1 2F /r","V","V","AVX512F","modrm_regonly","r,r","","" +"VCOMISD xmm1, xmm2/m64","VCOMISD xmm2/m64, xmm1","vcomisd xmm2/m64, xmm1","EVEX.LIG.66.0F.W1 2F /r","V","V","AVX512F","scale8","r,r","","" +"VCOMISD xmm1, xmm2/m64","VCOMISD xmm2/m64, xmm1","vcomisd xmm2/m64, xmm1","VEX.LIG.66.0F.WIG 2F /r","V","V","AVX","","r,r","","" +"VCOMISS xmm1{sae}, xmm2","VCOMISS xmm2, xmm1{sae}","vcomiss xmm2, xmm1{sae}","EVEX.128.0F.W0 2F /r","V","V","AVX512F","modrm_regonly","r,r","","" +"VCOMISS xmm1, xmm2/m32","VCOMISS xmm2/m32, xmm1","vcomiss xmm2/m32, xmm1","EVEX.LIG.0F.W0 2F /r","V","V","AVX512F","scale4","r,r","","" +"VCOMISS xmm1, xmm2/m32","VCOMISS xmm2/m32, xmm1","vcomiss xmm2/m32, xmm1","VEX.LIG.0F.WIG 2F /r","V","V","AVX","","r,r","","" +"VCOMPRESSPD xmm2/m128, {k}{z}, xmm1","VCOMPRESSPD xmm1, {k}{z}, xmm2/m128","vcompresspd xmm1, {k}{z}, xmm2/m128","EVEX.128.66.0F38.W1 8A /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VCOMPRESSPD ymm2/m256, {k}{z}, ymm1","VCOMPRESSPD ymm1, {k}{z}, ymm2/m256","vcompresspd ymm1, {k}{z}, ymm2/m256","EVEX.256.66.0F38.W1 8A /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VCOMPRESSPD zmm2/m512, {k}{z}, zmm1","VCOMPRESSPD zmm1, {k}{z}, zmm2/m512","vcompresspd zmm1, {k}{z}, zmm2/m512","EVEX.512.66.0F38.W1 8A /r","V","V","AVX512F","scale8","w,r,r","","" +"VCOMPRESSPS xmm2/m128, {k}{z}, xmm1","VCOMPRESSPS xmm1, {k}{z}, xmm2/m128","vcompressps xmm1, {k}{z}, xmm2/m128","EVEX.128.66.0F38.W0 8A /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VCOMPRESSPS ymm2/m256, {k}{z}, ymm1","VCOMPRESSPS ymm1, {k}{z}, ymm2/m256","vcompressps ymm1, {k}{z}, ymm2/m256","EVEX.256.66.0F38.W0 8A /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VCOMPRESSPS zmm2/m512, {k}{z}, zmm1","VCOMPRESSPS zmm1, {k}{z}, zmm2/m512","vcompressps zmm1, {k}{z}, zmm2/m512","EVEX.512.66.0F38.W0 8A /r","V","V","AVX512F","scale4","w,r,r","","" +"VCVTDQ2PD ymm1, xmm2/m128","VCVTDQ2PD xmm2/m128, ymm1","vcvtdq2pd xmm2/m128, ymm1","VEX.256.F3.0F.WIG E6 /r","V","V","AVX","","w,r","","" +"VCVTDQ2PD xmm1, {k}{z}, xmm2/m128/m32bcst","VCVTDQ2PD xmm2/m128/m32bcst, {k}{z}, xmm1","vcvtdq2pd xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.F3.0F.W0 E6 /r","V","V","AVX512F+AVX512VL","bscale4,scale8","w,r,r","","" +"VCVTDQ2PD ymm1, {k}{z}, xmm2/m256/m32bcst","VCVTDQ2PD xmm2/m256/m32bcst, {k}{z}, ymm1","vcvtdq2pd xmm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.F3.0F.W0 E6 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r","","" +"VCVTDQ2PD xmm1, xmm2/m64","VCVTDQ2PD xmm2/m64, xmm1","vcvtdq2pd xmm2/m64, xmm1","VEX.128.F3.0F.WIG E6 /r","V","V","AVX","","w,r","","" +"VCVTDQ2PD zmm1, {k}{z}, ymm2/m512/m32bcst","VCVTDQ2PD ymm2/m512/m32bcst, {k}{z}, zmm1","vcvtdq2pd ymm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.F3.0F.W0 E6 /r","V","V","AVX512F","bscale4,scale32","w,r,r","","" +"VCVTDQ2PS xmm1, xmm2/m128","VCVTDQ2PS xmm2/m128, xmm1","vcvtdq2ps xmm2/m128, xmm1","VEX.128.0F.WIG 5B /r","V","V","AVX","","w,r","","" +"VCVTDQ2PS xmm1, {k}{z}, xmm2/m128/m32bcst","VCVTDQ2PS xmm2/m128/m32bcst, {k}{z}, xmm1","vcvtdq2ps xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.0F.W0 5B /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r","","" +"VCVTDQ2PS ymm1, ymm2/m256","VCVTDQ2PS ymm2/m256, ymm1","vcvtdq2ps ymm2/m256, ymm1","VEX.256.0F.WIG 5B /r","V","V","AVX","","w,r","","" +"VCVTDQ2PS ymm1, {k}{z}, ymm2/m256/m32bcst","VCVTDQ2PS ymm2/m256/m32bcst, {k}{z}, ymm1","vcvtdq2ps ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.0F.W0 5B /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r","","" +"VCVTDQ2PS zmm1{er}, {k}{z}, zmm2","VCVTDQ2PS zmm2, {k}{z}, zmm1{er}","vcvtdq2ps zmm2, {k}{z}, zmm1{er}","EVEX.512.0F.W0 5B /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"VCVTDQ2PS zmm1, {k}{z}, zmm2/m512/m32bcst","VCVTDQ2PS zmm2/m512/m32bcst, {k}{z}, zmm1","vcvtdq2ps zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.0F.W0 5B /r","V","V","AVX512F","bscale4,scale64","w,r,r","","" +"VCVTPD2DQ ymm1{er}, {k}{z}, zmm2","VCVTPD2DQ zmm2, {k}{z}, ymm1{er}","vcvtpd2dq zmm2, {k}{z}, ymm1{er}","EVEX.512.F2.0F.W1 E6 /r","V","V","AVX512F","modrm_regonly","w,r,r","Y","" +"VCVTPD2DQ ymm1, {k}{z}, zmm2/m512/m64bcst","VCVTPD2DQ zmm2/m512/m64bcst, {k}{z}, ymm1","vcvtpd2dq zmm2/m512/m64bcst, {k}{z}, ymm1","EVEX.512.F2.0F.W1 E6 /r","V","V","AVX512F","bscale8,scale64","w,r,r","Y","512" +"VCVTPD2DQ xmm1, xmm2/m128","VCVTPD2DQX xmm2/m128, xmm1","vcvtpd2dqx xmm2/m128, xmm1","VEX.128.F2.0F.WIG E6 /r","V","V","AVX","","w,r","Y","128" +"VCVTPD2DQ xmm1, {k}{z}, xmm2/m128/m64bcst","VCVTPD2DQX xmm2/m128/m64bcst, {k}{z}, xmm1","vcvtpd2dqx xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.F2.0F.W1 E6 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r","Y","128" +"VCVTPD2DQ xmm1, ymm2/m256","VCVTPD2DQY ymm2/m256, xmm1","vcvtpd2dqy ymm2/m256, xmm1","VEX.256.F2.0F.WIG E6 /r","V","V","AVX","","w,r","Y","256" +"VCVTPD2DQ xmm1, {k}{z}, ymm2/m256/m64bcst","VCVTPD2DQY ymm2/m256/m64bcst, {k}{z}, xmm1","vcvtpd2dqy ymm2/m256/m64bcst, {k}{z}, xmm1","EVEX.256.F2.0F.W1 E6 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r","Y","256" +"VCVTPD2PS ymm1{er}, {k}{z}, zmm2","VCVTPD2PS zmm2, {k}{z}, ymm1{er}","vcvtpd2ps zmm2, {k}{z}, ymm1{er}","EVEX.512.66.0F.W1 5A /r","V","V","AVX512F","modrm_regonly","w,r,r","Y","" +"VCVTPD2PS ymm1, {k}{z}, zmm2/m512/m64bcst","VCVTPD2PS zmm2/m512/m64bcst, {k}{z}, ymm1","vcvtpd2ps zmm2/m512/m64bcst, {k}{z}, ymm1","EVEX.512.66.0F.W1 5A /r","V","V","AVX512F","bscale8,scale64","w,r,r","Y","512" +"VCVTPD2PS xmm1, xmm2/m128","VCVTPD2PSX xmm2/m128, xmm1","vcvtpd2psx xmm2/m128, xmm1","VEX.128.66.0F.WIG 5A /r","V","V","AVX","","w,r","Y","128" +"VCVTPD2PS xmm1, {k}{z}, xmm2/m128/m64bcst","VCVTPD2PSX xmm2/m128/m64bcst, {k}{z}, xmm1","vcvtpd2psx xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F.W1 5A /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r","Y","128" +"VCVTPD2PS xmm1, ymm2/m256","VCVTPD2PSY ymm2/m256, xmm1","vcvtpd2psy ymm2/m256, xmm1","VEX.256.66.0F.WIG 5A /r","V","V","AVX","","w,r","Y","256" +"VCVTPD2PS xmm1, {k}{z}, ymm2/m256/m64bcst","VCVTPD2PSY ymm2/m256/m64bcst, {k}{z}, xmm1","vcvtpd2psy ymm2/m256/m64bcst, {k}{z}, xmm1","EVEX.256.66.0F.W1 5A /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r","Y","256" +"VCVTPD2QQ xmm1, {k}{z}, xmm2/m128/m64bcst","VCVTPD2QQ xmm2/m128/m64bcst, {k}{z}, xmm1","vcvtpd2qq xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F.W1 7B /r","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r","","" +"VCVTPD2QQ ymm1, {k}{z}, ymm2/m256/m64bcst","VCVTPD2QQ ymm2/m256/m64bcst, {k}{z}, ymm1","vcvtpd2qq ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F.W1 7B /r","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r","","" +"VCVTPD2QQ zmm1{er}, {k}{z}, zmm2","VCVTPD2QQ zmm2, {k}{z}, zmm1{er}","vcvtpd2qq zmm2, {k}{z}, zmm1{er}","EVEX.512.66.0F.W1 7B /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"VCVTPD2QQ zmm1, {k}{z}, zmm2/m512/m64bcst","VCVTPD2QQ zmm2/m512/m64bcst, {k}{z}, zmm1","vcvtpd2qq zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F.W1 7B /r","V","V","AVX512DQ","bscale8,scale64","w,r,r","","" +"VCVTPD2UDQ ymm1{er}, {k}{z}, zmm2","VCVTPD2UDQ zmm2, {k}{z}, ymm1{er}","vcvtpd2udq zmm2, {k}{z}, ymm1{er}","EVEX.512.0F.W1 79 /r","V","V","AVX512F","modrm_regonly","w,r,r","Y","" +"VCVTPD2UDQ ymm1, {k}{z}, zmm2/m512/m64bcst","VCVTPD2UDQ zmm2/m512/m64bcst, {k}{z}, ymm1","vcvtpd2udq zmm2/m512/m64bcst, {k}{z}, ymm1","EVEX.512.0F.W1 79 /r","V","V","AVX512F","bscale8,scale64","w,r,r","Y","512" +"VCVTPD2UDQ xmm1, {k}{z}, xmm2/m128/m64bcst","VCVTPD2UDQX xmm2/m128/m64bcst, {k}{z}, xmm1","vcvtpd2udqx xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.0F.W1 79 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r","Y","128" +"VCVTPD2UDQ xmm1, {k}{z}, ymm2/m256/m64bcst","VCVTPD2UDQY ymm2/m256/m64bcst, {k}{z}, xmm1","vcvtpd2udqy ymm2/m256/m64bcst, {k}{z}, xmm1","EVEX.256.0F.W1 79 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r","Y","256" +"VCVTPD2UQQ xmm1, {k}{z}, xmm2/m128/m64bcst","VCVTPD2UQQ xmm2/m128/m64bcst, {k}{z}, xmm1","vcvtpd2uqq xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F.W1 79 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r","","" +"VCVTPD2UQQ ymm1, {k}{z}, ymm2/m256/m64bcst","VCVTPD2UQQ ymm2/m256/m64bcst, {k}{z}, ymm1","vcvtpd2uqq ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F.W1 79 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r","","" +"VCVTPD2UQQ zmm1{er}, {k}{z}, zmm2","VCVTPD2UQQ zmm2, {k}{z}, zmm1{er}","vcvtpd2uqq zmm2, {k}{z}, zmm1{er}","EVEX.512.66.0F.W1 79 /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"VCVTPD2UQQ zmm1, {k}{z}, zmm2/m512/m64bcst","VCVTPD2UQQ zmm2/m512/m64bcst, {k}{z}, zmm1","vcvtpd2uqq zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F.W1 79 /r","V","V","AVX512DQ","bscale8,scale64","w,r,r","","" +"VCVTPH2PS ymm1, xmm2/m128","VCVTPH2PS xmm2/m128, ymm1","vcvtph2ps xmm2/m128, ymm1","VEX.256.66.0F38.W0 13 /r","V","V","F16C","","w,r","","" +"VCVTPH2PS ymm1, {k}{z}, xmm2/m128","VCVTPH2PS xmm2/m128, {k}{z}, ymm1","vcvtph2ps xmm2/m128, {k}{z}, ymm1","EVEX.256.66.0F38.W0 13 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VCVTPH2PS xmm1, xmm2/m64","VCVTPH2PS xmm2/m64, xmm1","vcvtph2ps xmm2/m64, xmm1","VEX.128.66.0F38.W0 13 /r","V","V","F16C","","w,r","","" +"VCVTPH2PS xmm1, {k}{z}, xmm2/m64","VCVTPH2PS xmm2/m64, {k}{z}, xmm1","vcvtph2ps xmm2/m64, {k}{z}, xmm1","EVEX.128.66.0F38.W0 13 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VCVTPH2PS zmm1{sae}, {k}{z}, ymm2","VCVTPH2PS ymm2, {k}{z}, zmm1{sae}","vcvtph2ps ymm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F38.W0 13 /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"VCVTPH2PS zmm1, {k}{z}, ymm2/m256","VCVTPH2PS ymm2/m256, {k}{z}, zmm1","vcvtph2ps ymm2/m256, {k}{z}, zmm1","EVEX.512.66.0F38.W0 13 /r","V","V","AVX512F","scale32","w,r,r","","" +"VCVTPS2DQ xmm1, xmm2/m128","VCVTPS2DQ xmm2/m128, xmm1","vcvtps2dq xmm2/m128, xmm1","VEX.128.66.0F.WIG 5B /r","V","V","AVX","","w,r","","" +"VCVTPS2DQ xmm1, {k}{z}, xmm2/m128/m32bcst","VCVTPS2DQ xmm2/m128/m32bcst, {k}{z}, xmm1","vcvtps2dq xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F.W0 5B /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r","","" +"VCVTPS2DQ ymm1, ymm2/m256","VCVTPS2DQ ymm2/m256, ymm1","vcvtps2dq ymm2/m256, ymm1","VEX.256.66.0F.WIG 5B /r","V","V","AVX","","w,r","","" +"VCVTPS2DQ ymm1, {k}{z}, ymm2/m256/m32bcst","VCVTPS2DQ ymm2/m256/m32bcst, {k}{z}, ymm1","vcvtps2dq ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F.W0 5B /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r","","" +"VCVTPS2DQ zmm1{er}, {k}{z}, zmm2","VCVTPS2DQ zmm2, {k}{z}, zmm1{er}","vcvtps2dq zmm2, {k}{z}, zmm1{er}","EVEX.512.66.0F.W0 5B /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"VCVTPS2DQ zmm1, {k}{z}, zmm2/m512/m32bcst","VCVTPS2DQ zmm2/m512/m32bcst, {k}{z}, zmm1","vcvtps2dq zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F.W0 5B /r","V","V","AVX512F","bscale4,scale64","w,r,r","","" +"VCVTPS2PD ymm1, xmm2/m128","VCVTPS2PD xmm2/m128, ymm1","vcvtps2pd xmm2/m128, ymm1","VEX.256.0F.WIG 5A /r","V","V","AVX","","w,r","","" +"VCVTPS2PD xmm1, {k}{z}, xmm2/m128/m32bcst","VCVTPS2PD xmm2/m128/m32bcst, {k}{z}, xmm1","vcvtps2pd xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.0F.W0 5A /r","V","V","AVX512F+AVX512VL","bscale4,scale8","w,r,r","","" +"VCVTPS2PD ymm1, {k}{z}, xmm2/m256/m32bcst","VCVTPS2PD xmm2/m256/m32bcst, {k}{z}, ymm1","vcvtps2pd xmm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.0F.W0 5A /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r","","" +"VCVTPS2PD xmm1, xmm2/m64","VCVTPS2PD xmm2/m64, xmm1","vcvtps2pd xmm2/m64, xmm1","VEX.128.0F.WIG 5A /r","V","V","AVX","","w,r","","" +"VCVTPS2PD zmm1{sae}, {k}{z}, ymm2","VCVTPS2PD ymm2, {k}{z}, zmm1{sae}","vcvtps2pd ymm2, {k}{z}, zmm1{sae}","EVEX.512.0F.W0 5A /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"VCVTPS2PD zmm1, {k}{z}, ymm2/m512/m32bcst","VCVTPS2PD ymm2/m512/m32bcst, {k}{z}, zmm1","vcvtps2pd ymm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.0F.W0 5A /r","V","V","AVX512F","bscale4,scale32","w,r,r","","" +"VCVTPS2PH xmm2/m64, xmm1, imm8u","VCVTPS2PH imm8u, xmm1, xmm2/m64","vcvtps2ph imm8u, xmm1, xmm2/m64","VEX.128.66.0F3A.W0 1D /r ib","V","V","F16C","","w,r,r","","" +"VCVTPS2PH xmm2/m64, {k}{z}, xmm1, imm8u","VCVTPS2PH imm8u, xmm1, {k}{z}, xmm2/m64","vcvtps2ph imm8u, xmm1, {k}{z}, xmm2/m64","EVEX.128.66.0F3A.W0 1D /r ib","V","V","AVX512F+AVX512VL","scale8","w,r,r,r","","" +"VCVTPS2PH xmm2/m128, ymm1, imm8u","VCVTPS2PH imm8u, ymm1, xmm2/m128","vcvtps2ph imm8u, ymm1, xmm2/m128","VEX.256.66.0F3A.W0 1D /r ib","V","V","F16C","","w,r,r","","" +"VCVTPS2PH xmm2/m128, {k}{z}, ymm1, imm8u","VCVTPS2PH imm8u, ymm1, {k}{z}, xmm2/m128","vcvtps2ph imm8u, ymm1, {k}{z}, xmm2/m128","EVEX.256.66.0F3A.W0 1D /r ib","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VCVTPS2PH ymm2/m256, {k}{z}, zmm1, imm8u","VCVTPS2PH imm8u, zmm1, {k}{z}, ymm2/m256","vcvtps2ph imm8u, zmm1, {k}{z}, ymm2/m256","EVEX.512.66.0F3A.W0 1D /r ib","V","V","AVX512F","scale32","w,r,r,r","","" +"VCVTPS2PH ymm2{sae}, {k}{z}, zmm1, imm8u","VCVTPS2PH imm8u, zmm1, {k}{z}, ymm2{sae}","vcvtps2ph imm8u, zmm1, {k}{z}, ymm2{sae}","EVEX.512.66.0F3A.W0 1D /r ib","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VCVTPS2QQ xmm1, {k}{z}, xmm2/m128/m32bcst","VCVTPS2QQ xmm2/m128/m32bcst, {k}{z}, xmm1","vcvtps2qq xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F.W0 7B /r","V","V","AVX512DQ+AVX512VL","bscale4,scale8","w,r,r","","" +"VCVTPS2QQ ymm1, {k}{z}, xmm2/m256/m32bcst","VCVTPS2QQ xmm2/m256/m32bcst, {k}{z}, ymm1","vcvtps2qq xmm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F.W0 7B /r","V","V","AVX512DQ+AVX512VL","bscale4,scale16","w,r,r","","" +"VCVTPS2QQ zmm1{er}, {k}{z}, ymm2","VCVTPS2QQ ymm2, {k}{z}, zmm1{er}","vcvtps2qq ymm2, {k}{z}, zmm1{er}","EVEX.512.66.0F.W0 7B /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"VCVTPS2QQ zmm1, {k}{z}, ymm2/m512/m32bcst","VCVTPS2QQ ymm2/m512/m32bcst, {k}{z}, zmm1","vcvtps2qq ymm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F.W0 7B /r","V","V","AVX512DQ","bscale4,scale32","w,r,r","","" +"VCVTPS2UDQ xmm1, {k}{z}, xmm2/m128/m32bcst","VCVTPS2UDQ xmm2/m128/m32bcst, {k}{z}, xmm1","vcvtps2udq xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.0F.W0 79 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r","","" +"VCVTPS2UDQ ymm1, {k}{z}, ymm2/m256/m32bcst","VCVTPS2UDQ ymm2/m256/m32bcst, {k}{z}, ymm1","vcvtps2udq ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.0F.W0 79 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r","","" +"VCVTPS2UDQ zmm1{er}, {k}{z}, zmm2","VCVTPS2UDQ zmm2, {k}{z}, zmm1{er}","vcvtps2udq zmm2, {k}{z}, zmm1{er}","EVEX.512.0F.W0 79 /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"VCVTPS2UDQ zmm1, {k}{z}, zmm2/m512/m32bcst","VCVTPS2UDQ zmm2/m512/m32bcst, {k}{z}, zmm1","vcvtps2udq zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.0F.W0 79 /r","V","V","AVX512F","bscale4,scale64","w,r,r","","" +"VCVTPS2UQQ xmm1, {k}{z}, xmm2/m128/m32bcst","VCVTPS2UQQ xmm2/m128/m32bcst, {k}{z}, xmm1","vcvtps2uqq xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F.W0 79 /r","V","V","AVX512DQ+AVX512VL","bscale4,scale8","w,r,r","","" +"VCVTPS2UQQ ymm1, {k}{z}, xmm2/m256/m32bcst","VCVTPS2UQQ xmm2/m256/m32bcst, {k}{z}, ymm1","vcvtps2uqq xmm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F.W0 79 /r","V","V","AVX512DQ+AVX512VL","bscale4,scale16","w,r,r","","" +"VCVTPS2UQQ zmm1{er}, {k}{z}, ymm2","VCVTPS2UQQ ymm2, {k}{z}, zmm1{er}","vcvtps2uqq ymm2, {k}{z}, zmm1{er}","EVEX.512.66.0F.W0 79 /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"VCVTPS2UQQ zmm1, {k}{z}, ymm2/m512/m32bcst","VCVTPS2UQQ ymm2/m512/m32bcst, {k}{z}, zmm1","vcvtps2uqq ymm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F.W0 79 /r","V","V","AVX512DQ","bscale4,scale32","w,r,r","","" +"VCVTQQ2PD xmm1, {k}{z}, xmm2/m128/m64bcst","VCVTQQ2PD xmm2/m128/m64bcst, {k}{z}, xmm1","vcvtqq2pd xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.F3.0F.W1 E6 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r","","" +"VCVTQQ2PD ymm1, {k}{z}, ymm2/m256/m64bcst","VCVTQQ2PD ymm2/m256/m64bcst, {k}{z}, ymm1","vcvtqq2pd ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.F3.0F.W1 E6 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r","","" +"VCVTQQ2PD zmm1{er}, {k}{z}, zmm2","VCVTQQ2PD zmm2, {k}{z}, zmm1{er}","vcvtqq2pd zmm2, {k}{z}, zmm1{er}","EVEX.512.F3.0F.W1 E6 /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"VCVTQQ2PD zmm1, {k}{z}, zmm2/m512/m64bcst","VCVTQQ2PD zmm2/m512/m64bcst, {k}{z}, zmm1","vcvtqq2pd zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.F3.0F.W1 E6 /r","V","V","AVX512DQ","bscale8,scale64","w,r,r","","" +"VCVTQQ2PS ymm1{er}, {k}{z}, zmm2","VCVTQQ2PS zmm2, {k}{z}, ymm1{er}","vcvtqq2ps zmm2, {k}{z}, ymm1{er}","EVEX.512.0F.W1 5B /r","V","V","AVX512DQ","modrm_regonly","w,r,r","Y","" +"VCVTQQ2PS ymm1, {k}{z}, zmm2/m512/m64bcst","VCVTQQ2PS zmm2/m512/m64bcst, {k}{z}, ymm1","vcvtqq2ps zmm2/m512/m64bcst, {k}{z}, ymm1","EVEX.512.0F.W1 5B /r","V","V","AVX512DQ","bscale8,scale64","w,r,r","Y","512" +"VCVTQQ2PS xmm1, {k}{z}, xmm2/m128/m64bcst","VCVTQQ2PSX xmm2/m128/m64bcst, {k}{z}, xmm1","vcvtqq2psx xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.0F.W1 5B /r","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r","Y","128" +"VCVTQQ2PS xmm1, {k}{z}, ymm2/m256/m64bcst","VCVTQQ2PSY ymm2/m256/m64bcst, {k}{z}, xmm1","vcvtqq2psy ymm2/m256/m64bcst, {k}{z}, xmm1","EVEX.256.0F.W1 5B /r","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r","Y","256" +"VCVTSD2SI r32{er}, xmm2","VCVTSD2SI xmm2, r32{er}","vcvtsd2si xmm2, r32{er}","EVEX.128.F2.0F.W0 2D /r","V","V","AVX512F","modrm_regonly","w,r","Y","32" +"VCVTSD2SI r32, xmm2/m64","VCVTSD2SI xmm2/m64, r32","vcvtsd2si xmm2/m64, r32","EVEX.LIG.F2.0F.W0 2D /r","V","V","AVX512F","scale8","w,r","Y","32" +"VCVTSD2SI r32, xmm2/m64","VCVTSD2SI xmm2/m64, r32","vcvtsd2si xmm2/m64, r32","VEX.LIG.F2.0F.W0 2D /r","V","V","AVX","","w,r","Y","32" +"VCVTSD2SI r64{er}, xmm2","VCVTSD2SIQ xmm2, r64{er}","vcvtsd2siq xmm2, r64{er}","EVEX.128.F2.0F.W1 2D /r","N.S.","V","AVX512F","modrm_regonly","w,r","Y","64" +"VCVTSD2SI r64, xmm2/m64","VCVTSD2SIQ xmm2/m64, r64","vcvtsd2siq xmm2/m64, r64","EVEX.LIG.F2.0F.W1 2D /r","N.S.","V","AVX512F","scale8","w,r","Y","64" +"VCVTSD2SI r64, xmm2/m64","VCVTSD2SIQ xmm2/m64, r64","vcvtsd2siq xmm2/m64, r64","VEX.LIG.F2.0F.W1 2D /r","N.S.","V","AVX","","w,r","Y","64" +"VCVTSD2SS xmm1{er}, {k}{z}, xmmV, xmm2","VCVTSD2SS xmm2, xmmV, {k}{z}, xmm1{er}","vcvtsd2ss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.NDS.128.F2.0F.W1 5A /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VCVTSD2SS xmm1, xmmV, xmm2/m64","VCVTSD2SS xmm2/m64, xmmV, xmm1","vcvtsd2ss xmm2/m64, xmmV, xmm1","VEX.NDS.LIG.F2.0F.WIG 5A /r","V","V","AVX","","w,r,r","","" +"VCVTSD2SS xmm1, {k}{z}, xmmV, xmm2/m64","VCVTSD2SS xmm2/m64, xmmV, {k}{z}, xmm1","vcvtsd2ss xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F2.0F.W1 5A /r","V","V","AVX512F","scale8","w,r,r,r","","" +"VCVTSD2USI r32{er}, xmm2","VCVTSD2USIL xmm2, r32{er}","vcvtsd2usi xmm2, r32{er}","EVEX.128.F2.0F.W0 79 /r","V","V","AVX512F","modrm_regonly","w,r","Y","32" +"VCVTSD2USI r32, xmm2/m64","VCVTSD2USIL xmm2/m64, r32","vcvtsd2usi xmm2/m64, r32","EVEX.LIG.F2.0F.W0 79 /r","V","V","AVX512F","scale8","w,r","Y","32" +"VCVTSD2USI r64{er}, xmm2","VCVTSD2USIQ xmm2, r64{er}","vcvtsd2usi xmm2, r64{er}","EVEX.128.F2.0F.W1 79 /r","N.S.","V","AVX512F","modrm_regonly","w,r","Y","64" +"VCVTSD2USI r64, xmm2/m64","VCVTSD2USIQ xmm2/m64, r64","vcvtsd2usi xmm2/m64, r64","EVEX.LIG.F2.0F.W1 79 /r","N.S.","V","AVX512F","scale8","w,r","Y","64" +"VCVTSI2SD xmm1, xmmV, r/m32","VCVTSI2SDL r/m32, xmmV, xmm1","vcvtsi2sdl r/m32, xmmV, xmm1","EVEX.NDS.LIG.F2.0F.W0 2A /r","V","V","AVX512F","scale4","w,r,r","Y","32" +"VCVTSI2SD xmm1, xmmV, r/m32","VCVTSI2SDL r/m32, xmmV, xmm1","vcvtsi2sdl r/m32, xmmV, xmm1","VEX.NDS.LIG.F2.0F.W0 2A /r","V","V","AVX","","w,r,r","Y","32" +"VCVTSI2SD xmm1, xmmV, r/m64","VCVTSI2SDQ r/m64, xmmV, xmm1","vcvtsi2sdq r/m64, xmmV, xmm1","EVEX.NDS.LIG.F2.0F.W1 2A /r","N.S.","V","AVX512F","scale8","w,r,r","Y","64" +"VCVTSI2SD xmm1, xmmV, r/m64","VCVTSI2SDQ r/m64, xmmV, xmm1","vcvtsi2sdq r/m64, xmmV, xmm1","VEX.NDS.LIG.F2.0F.W1 2A /r","N.S.","V","AVX","","w,r,r","Y","64" +"VCVTSI2SD xmm1{er}, xmmV, rmr64","VCVTSI2SDQ rmr64, xmmV, xmm1{er}","vcvtsi2sdq rmr64, xmmV, xmm1{er}","EVEX.NDS.128.F2.0F.W1 2A /r","N.S.","V","AVX512F","modrm_regonly","w,r,r","Y","64" +"VCVTSI2SS xmm1, xmmV, r/m32","VCVTSI2SSL r/m32, xmmV, xmm1","vcvtsi2ssl r/m32, xmmV, xmm1","EVEX.NDS.LIG.F3.0F.W0 2A /r","V","V","AVX512F","scale4","w,r,r","Y","32" +"VCVTSI2SS xmm1, xmmV, r/m32","VCVTSI2SSL r/m32, xmmV, xmm1","vcvtsi2ssl r/m32, xmmV, xmm1","VEX.NDS.LIG.F3.0F.W0 2A /r","V","V","AVX","","w,r,r","Y","32" +"VCVTSI2SS xmm1{er}, xmmV, rmr32","VCVTSI2SSL rmr32, xmmV, xmm1{er}","vcvtsi2ssl rmr32, xmmV, xmm1{er}","EVEX.NDS.128.F3.0F.W0 2A /r","V","V","AVX512F","modrm_regonly","w,r,r","Y","32" +"VCVTSI2SS xmm1, xmmV, r/m64","VCVTSI2SSQ r/m64, xmmV, xmm1","vcvtsi2ssq r/m64, xmmV, xmm1","EVEX.NDS.LIG.F3.0F.W1 2A /r","N.S.","V","AVX512F","scale8","w,r,r","Y","64" +"VCVTSI2SS xmm1, xmmV, r/m64","VCVTSI2SSQ r/m64, xmmV, xmm1","vcvtsi2ssq r/m64, xmmV, xmm1","VEX.NDS.LIG.F3.0F.W1 2A /r","N.S.","V","AVX","","w,r,r","Y","64" +"VCVTSI2SS xmm1{er}, xmmV, rmr64","VCVTSI2SSQ rmr64, xmmV, xmm1{er}","vcvtsi2ssq rmr64, xmmV, xmm1{er}","EVEX.NDS.128.F3.0F.W1 2A /r","N.S.","V","AVX512F","modrm_regonly","w,r,r","Y","64" +"VCVTSS2SD xmm1{sae}, {k}{z}, xmmV, xmm2","VCVTSS2SD xmm2, xmmV, {k}{z}, xmm1{sae}","vcvtss2sd xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.F3.0F.W0 5A /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VCVTSS2SD xmm1, xmmV, xmm2/m32","VCVTSS2SD xmm2/m32, xmmV, xmm1","vcvtss2sd xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.F3.0F.WIG 5A /r","V","V","AVX","","w,r,r","","" +"VCVTSS2SD xmm1, {k}{z}, xmmV, xmm2/m32","VCVTSS2SD xmm2/m32, xmmV, {k}{z}, xmm1","vcvtss2sd xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F3.0F.W0 5A /r","V","V","AVX512F","scale4","w,r,r,r","","" +"VCVTSS2SI r32{er}, xmm2","VCVTSS2SI xmm2, r32{er}","vcvtss2si xmm2, r32{er}","EVEX.128.F3.0F.W0 2D /r","V","V","AVX512F","modrm_regonly","w,r","Y","32" +"VCVTSS2SI r32, xmm2/m32","VCVTSS2SI xmm2/m32, r32","vcvtss2si xmm2/m32, r32","EVEX.LIG.F3.0F.W0 2D /r","V","V","AVX512F","scale4","w,r","Y","32" +"VCVTSS2SI r32, xmm2/m32","VCVTSS2SI xmm2/m32, r32","vcvtss2si xmm2/m32, r32","VEX.LIG.F3.0F.W0 2D /r","V","V","AVX","","w,r","Y","32" +"VCVTSS2SI r64{er}, xmm2","VCVTSS2SIQ xmm2, r64{er}","vcvtss2siq xmm2, r64{er}","EVEX.128.F3.0F.W1 2D /r","N.S.","V","AVX512F","modrm_regonly","w,r","Y","64" +"VCVTSS2SI r64, xmm2/m32","VCVTSS2SIQ xmm2/m32, r64","vcvtss2siq xmm2/m32, r64","EVEX.LIG.F3.0F.W1 2D /r","N.S.","V","AVX512F","scale4","w,r","Y","64" +"VCVTSS2SI r64, xmm2/m32","VCVTSS2SIQ xmm2/m32, r64","vcvtss2siq xmm2/m32, r64","VEX.LIG.F3.0F.W1 2D /r","N.S.","V","AVX","","w,r","Y","64" +"VCVTSS2USI r32{er}, xmm2","VCVTSS2USIL xmm2, r32{er}","vcvtss2usil xmm2, r32{er}","EVEX.128.F3.0F.W0 79 /r","V","V","AVX512F","modrm_regonly","w,r","Y","32" +"VCVTSS2USI r32, xmm2/m32","VCVTSS2USIL xmm2/m32, r32","vcvtss2usil xmm2/m32, r32","EVEX.LIG.F3.0F.W0 79 /r","V","V","AVX512F","scale4","w,r","Y","32" +"VCVTSS2USI r64{er}, xmm2","VCVTSS2USIQ xmm2, r64{er}","vcvtss2usiq xmm2, r64{er}","EVEX.128.F3.0F.W1 79 /r","N.S.","V","AVX512F","modrm_regonly","w,r","Y","64" +"VCVTSS2USI r64, xmm2/m32","VCVTSS2USIQ xmm2/m32, r64","vcvtss2usiq xmm2/m32, r64","EVEX.LIG.F3.0F.W1 79 /r","N.S.","V","AVX512F","scale4","w,r","Y","64" +"VCVTTPD2DQ ymm1{sae}, {k}{z}, zmm2","VCVTTPD2DQ zmm2, {k}{z}, ymm1{sae}","vcvttpd2dq zmm2, {k}{z}, ymm1{sae}","EVEX.512.66.0F.W1 E6 /r","V","V","AVX512F","modrm_regonly","w,r,r","Y","" +"VCVTTPD2DQ ymm1, {k}{z}, zmm2/m512/m64bcst","VCVTTPD2DQ zmm2/m512/m64bcst, {k}{z}, ymm1","vcvttpd2dq zmm2/m512/m64bcst, {k}{z}, ymm1","EVEX.512.66.0F.W1 E6 /r","V","V","AVX512F","bscale8,scale64","w,r,r","Y","512" +"VCVTTPD2DQ xmm1, xmm2/m128","VCVTTPD2DQX xmm2/m128, xmm1","vcvttpd2dqx xmm2/m128, xmm1","VEX.128.66.0F.WIG E6 /r","V","V","AVX","","w,r","Y","128" +"VCVTTPD2DQ xmm1, {k}{z}, xmm2/m128/m64bcst","VCVTTPD2DQX xmm2/m128/m64bcst, {k}{z}, xmm1","vcvttpd2dqx xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F.W1 E6 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r","Y","128" +"VCVTTPD2DQ xmm1, ymm2/m256","VCVTTPD2DQY ymm2/m256, xmm1","vcvttpd2dqy ymm2/m256, xmm1","VEX.256.66.0F.WIG E6 /r","V","V","AVX","","w,r","Y","256" +"VCVTTPD2DQ xmm1, {k}{z}, ymm2/m256/m64bcst","VCVTTPD2DQY ymm2/m256/m64bcst, {k}{z}, xmm1","vcvttpd2dqy ymm2/m256/m64bcst, {k}{z}, xmm1","EVEX.256.66.0F.W1 E6 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r","Y","256" +"VCVTTPD2QQ xmm1, {k}{z}, xmm2/m128/m64bcst","VCVTTPD2QQ xmm2/m128/m64bcst, {k}{z}, xmm1","vcvttpd2qq xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F.W1 7A /r","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r","","" +"VCVTTPD2QQ ymm1, {k}{z}, ymm2/m256/m64bcst","VCVTTPD2QQ ymm2/m256/m64bcst, {k}{z}, ymm1","vcvttpd2qq ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F.W1 7A /r","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r","","" +"VCVTTPD2QQ zmm1{sae}, {k}{z}, zmm2","VCVTTPD2QQ zmm2, {k}{z}, zmm1{sae}","vcvttpd2qq zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F.W1 7A /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"VCVTTPD2QQ zmm1, {k}{z}, zmm2/m512/m64bcst","VCVTTPD2QQ zmm2/m512/m64bcst, {k}{z}, zmm1","vcvttpd2qq zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F.W1 7A /r","V","V","AVX512DQ","bscale8,scale64","w,r,r","","" +"VCVTTPD2UDQ ymm1{sae}, {k}{z}, zmm2","VCVTTPD2UDQ zmm2, {k}{z}, ymm1{sae}","vcvttpd2udq zmm2, {k}{z}, ymm1{sae}","EVEX.512.0F.W1 78 /r","V","V","AVX512F","modrm_regonly","w,r,r","Y","" +"VCVTTPD2UDQ ymm1, {k}{z}, zmm2/m512/m64bcst","VCVTTPD2UDQ zmm2/m512/m64bcst, {k}{z}, ymm1","vcvttpd2udq zmm2/m512/m64bcst, {k}{z}, ymm1","EVEX.512.0F.W1 78 /r","V","V","AVX512F","bscale8,scale64","w,r,r","Y","512" +"VCVTTPD2UDQ xmm1, {k}{z}, xmm2/m128/m64bcst","VCVTTPD2UDQX xmm2/m128/m64bcst, {k}{z}, xmm1","vcvttpd2udqx xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.0F.W1 78 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r","Y","128" +"VCVTTPD2UDQ xmm1, {k}{z}, ymm2/m256/m64bcst","VCVTTPD2UDQY ymm2/m256/m64bcst, {k}{z}, xmm1","vcvttpd2udqy ymm2/m256/m64bcst, {k}{z}, xmm1","EVEX.256.0F.W1 78 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r","Y","256" +"VCVTTPD2UQQ xmm1, {k}{z}, xmm2/m128/m64bcst","VCVTTPD2UQQ xmm2/m128/m64bcst, {k}{z}, xmm1","vcvttpd2uqq xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F.W1 78 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r","","" +"VCVTTPD2UQQ ymm1, {k}{z}, ymm2/m256/m64bcst","VCVTTPD2UQQ ymm2/m256/m64bcst, {k}{z}, ymm1","vcvttpd2uqq ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F.W1 78 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r","","" +"VCVTTPD2UQQ zmm1{sae}, {k}{z}, zmm2","VCVTTPD2UQQ zmm2, {k}{z}, zmm1{sae}","vcvttpd2uqq zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F.W1 78 /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"VCVTTPD2UQQ zmm1, {k}{z}, zmm2/m512/m64bcst","VCVTTPD2UQQ zmm2/m512/m64bcst, {k}{z}, zmm1","vcvttpd2uqq zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F.W1 78 /r","V","V","AVX512DQ","bscale8,scale64","w,r,r","","" +"VCVTTPS2DQ xmm1, xmm2/m128","VCVTTPS2DQ xmm2/m128, xmm1","vcvttps2dq xmm2/m128, xmm1","VEX.128.F3.0F.WIG 5B /r","V","V","AVX","","w,r","","" +"VCVTTPS2DQ xmm1, {k}{z}, xmm2/m128/m32bcst","VCVTTPS2DQ xmm2/m128/m32bcst, {k}{z}, xmm1","vcvttps2dq xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.F3.0F.W0 5B /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r","","" +"VCVTTPS2DQ ymm1, ymm2/m256","VCVTTPS2DQ ymm2/m256, ymm1","vcvttps2dq ymm2/m256, ymm1","VEX.256.F3.0F.WIG 5B /r","V","V","AVX","","w,r","","" +"VCVTTPS2DQ ymm1, {k}{z}, ymm2/m256/m32bcst","VCVTTPS2DQ ymm2/m256/m32bcst, {k}{z}, ymm1","vcvttps2dq ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.F3.0F.W0 5B /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r","","" +"VCVTTPS2DQ zmm1{sae}, {k}{z}, zmm2","VCVTTPS2DQ zmm2, {k}{z}, zmm1{sae}","vcvttps2dq zmm2, {k}{z}, zmm1{sae}","EVEX.512.F3.0F.W0 5B /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"VCVTTPS2DQ zmm1, {k}{z}, zmm2/m512/m32bcst","VCVTTPS2DQ zmm2/m512/m32bcst, {k}{z}, zmm1","vcvttps2dq zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.F3.0F.W0 5B /r","V","V","AVX512F","bscale4,scale64","w,r,r","","" +"VCVTTPS2QQ xmm1, {k}{z}, xmm2/m128/m32bcst","VCVTTPS2QQ xmm2/m128/m32bcst, {k}{z}, xmm1","vcvttps2qq xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F.W0 7A /r","V","V","AVX512DQ+AVX512VL","bscale4,scale8","w,r,r","","" +"VCVTTPS2QQ ymm1, {k}{z}, xmm2/m256/m32bcst","VCVTTPS2QQ xmm2/m256/m32bcst, {k}{z}, ymm1","vcvttps2qq xmm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F.W0 7A /r","V","V","AVX512DQ+AVX512VL","bscale4,scale16","w,r,r","","" +"VCVTTPS2QQ zmm1{sae}, {k}{z}, ymm2","VCVTTPS2QQ ymm2, {k}{z}, zmm1{sae}","vcvttps2qq ymm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F.W0 7A /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"VCVTTPS2QQ zmm1, {k}{z}, ymm2/m512/m32bcst","VCVTTPS2QQ ymm2/m512/m32bcst, {k}{z}, zmm1","vcvttps2qq ymm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F.W0 7A /r","V","V","AVX512DQ","bscale4,scale32","w,r,r","","" +"VCVTTPS2UDQ xmm1, {k}{z}, xmm2/m128/m32bcst","VCVTTPS2UDQ xmm2/m128/m32bcst, {k}{z}, xmm1","vcvttps2udq xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.0F.W0 78 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r","","" +"VCVTTPS2UDQ ymm1, {k}{z}, ymm2/m256/m32bcst","VCVTTPS2UDQ ymm2/m256/m32bcst, {k}{z}, ymm1","vcvttps2udq ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.0F.W0 78 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r","","" +"VCVTTPS2UDQ zmm1{sae}, {k}{z}, zmm2","VCVTTPS2UDQ zmm2, {k}{z}, zmm1{sae}","vcvttps2udq zmm2, {k}{z}, zmm1{sae}","EVEX.512.0F.W0 78 /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"VCVTTPS2UDQ zmm1, {k}{z}, zmm2/m512/m32bcst","VCVTTPS2UDQ zmm2/m512/m32bcst, {k}{z}, zmm1","vcvttps2udq zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.0F.W0 78 /r","V","V","AVX512F","bscale4,scale64","w,r,r","","" +"VCVTTPS2UQQ xmm1, {k}{z}, xmm2/m128/m32bcst","VCVTTPS2UQQ xmm2/m128/m32bcst, {k}{z}, xmm1","vcvttps2uqq xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F.W0 78 /r","V","V","AVX512DQ+AVX512VL","bscale4,scale8","w,r,r","","" +"VCVTTPS2UQQ ymm1, {k}{z}, xmm2/m256/m32bcst","VCVTTPS2UQQ xmm2/m256/m32bcst, {k}{z}, ymm1","vcvttps2uqq xmm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F.W0 78 /r","V","V","AVX512DQ+AVX512VL","bscale4,scale16","w,r,r","","" +"VCVTTPS2UQQ zmm1{sae}, {k}{z}, ymm2","VCVTTPS2UQQ ymm2, {k}{z}, zmm1{sae}","vcvttps2uqq ymm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F.W0 78 /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"VCVTTPS2UQQ zmm1, {k}{z}, ymm2/m512/m32bcst","VCVTTPS2UQQ ymm2/m512/m32bcst, {k}{z}, zmm1","vcvttps2uqq ymm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F.W0 78 /r","V","V","AVX512DQ","bscale4,scale32","w,r,r","","" +"VCVTTSD2SI r32{sae}, xmm2","VCVTTSD2SI xmm2, r32{sae}","vcvttsd2si xmm2, r32{sae}","EVEX.128.F2.0F.W0 2C /r","V","V","AVX512F","modrm_regonly","w,r","Y","32" +"VCVTTSD2SI r32, xmm2/m64","VCVTTSD2SI xmm2/m64, r32","vcvttsd2si xmm2/m64, r32","EVEX.LIG.F2.0F.W0 2C /r","V","V","AVX512F","scale8","w,r","Y","32" +"VCVTTSD2SI r32, xmm2/m64","VCVTTSD2SI xmm2/m64, r32","vcvttsd2si xmm2/m64, r32","VEX.LIG.F2.0F.W0 2C /r","V","V","AVX","","w,r","Y","32" +"VCVTTSD2SI r64{sae}, xmm2","VCVTTSD2SIQ xmm2, r64{sae}","vcvttsd2siq xmm2, r64{sae}","EVEX.128.F2.0F.W1 2C /r","N.S.","V","AVX512F","modrm_regonly","w,r","Y","64" +"VCVTTSD2SI r64, xmm2/m64","VCVTTSD2SIQ xmm2/m64, r64","vcvttsd2siq xmm2/m64, r64","EVEX.LIG.F2.0F.W1 2C /r","N.S.","V","AVX512F","scale8","w,r","Y","64" +"VCVTTSD2SI r64, xmm2/m64","VCVTTSD2SIQ xmm2/m64, r64","vcvttsd2siq xmm2/m64, r64","VEX.LIG.F2.0F.W1 2C /r","N.S.","V","AVX","","w,r","Y","64" +"VCVTTSD2USI r32{sae}, xmm2","VCVTTSD2USIL xmm2, r32{sae}","vcvttsd2usil xmm2, r32{sae}","EVEX.128.F2.0F.W0 78 /r","V","V","AVX512F","modrm_regonly","w,r","Y","32" +"VCVTTSD2USI r32, xmm2/m64","VCVTTSD2USIL xmm2/m64, r32","vcvttsd2usil xmm2/m64, r32","EVEX.LIG.F2.0F.W0 78 /r","V","V","AVX512F","scale8","w,r","Y","32" +"VCVTTSD2USI r64{sae}, xmm2","VCVTTSD2USIQ xmm2, r64{sae}","vcvttsd2usiq xmm2, r64{sae}","EVEX.128.F2.0F.W1 78 /r","N.S.","V","AVX512F","modrm_regonly","w,r","Y","64" +"VCVTTSD2USI r64, xmm2/m64","VCVTTSD2USIQ xmm2/m64, r64","vcvttsd2usiq xmm2/m64, r64","EVEX.LIG.F2.0F.W1 78 /r","N.S.","V","AVX512F","scale8","w,r","Y","64" +"VCVTTSS2SI r32{sae}, xmm2","VCVTTSS2SI xmm2, r32{sae}","vcvttss2si xmm2, r32{sae}","EVEX.128.F3.0F.W0 2C /r","V","V","AVX512F","modrm_regonly","w,r","Y","32" +"VCVTTSS2SI r32, xmm2/m32","VCVTTSS2SI xmm2/m32, r32","vcvttss2si xmm2/m32, r32","EVEX.LIG.F3.0F.W0 2C /r","V","V","AVX512F","scale4","w,r","Y","32" +"VCVTTSS2SI r32, xmm2/m32","VCVTTSS2SI xmm2/m32, r32","vcvttss2si xmm2/m32, r32","VEX.LIG.F3.0F.W0 2C /r","V","V","AVX","","w,r","Y","32" +"VCVTTSS2SI r64{sae}, xmm2","VCVTTSS2SIQ xmm2, r64{sae}","vcvttss2siq xmm2, r64{sae}","EVEX.128.F3.0F.W1 2C /r","N.S.","V","AVX512F","modrm_regonly","w,r","Y","64" +"VCVTTSS2SI r64, xmm2/m32","VCVTTSS2SIQ xmm2/m32, r64","vcvttss2siq xmm2/m32, r64","EVEX.LIG.F3.0F.W1 2C /r","N.S.","V","AVX512F","scale4","w,r","Y","64" +"VCVTTSS2SI r64, xmm2/m32","VCVTTSS2SIQ xmm2/m32, r64","vcvttss2siq xmm2/m32, r64","VEX.LIG.F3.0F.W1 2C /r","N.S.","V","AVX","","w,r","Y","64" +"VCVTTSS2USI r32{sae}, xmm2","VCVTTSS2USIL xmm2, r32{sae}","vcvttss2usil xmm2, r32{sae}","EVEX.128.F3.0F.W0 78 /r","V","V","AVX512F","modrm_regonly","w,r","Y","32" +"VCVTTSS2USI r32, xmm2/m32","VCVTTSS2USIL xmm2/m32, r32","vcvttss2usil xmm2/m32, r32","EVEX.LIG.F3.0F.W0 78 /r","V","V","AVX512F","scale4","w,r","Y","32" +"VCVTTSS2USI r64{sae}, xmm2","VCVTTSS2USIQ xmm2, r64{sae}","vcvttss2usiq xmm2, r64{sae}","EVEX.128.F3.0F.W1 78 /r","N.S.","V","AVX512F","modrm_regonly","w,r","Y","64" +"VCVTTSS2USI r64, xmm2/m32","VCVTTSS2USIQ xmm2/m32, r64","vcvttss2usiq xmm2/m32, r64","EVEX.LIG.F3.0F.W1 78 /r","N.S.","V","AVX512F","scale4","w,r","Y","64" +"VCVTUDQ2PD xmm1, {k}{z}, xmm2/m128/m32bcst","VCVTUDQ2PD xmm2/m128/m32bcst, {k}{z}, xmm1","vcvtudq2pd xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.F3.0F.W0 7A /r","V","V","AVX512F+AVX512VL","bscale4,scale8","w,r,r","","" +"VCVTUDQ2PD ymm1, {k}{z}, xmm2/m256/m32bcst","VCVTUDQ2PD xmm2/m256/m32bcst, {k}{z}, ymm1","vcvtudq2pd xmm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.F3.0F.W0 7A /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r","","" +"VCVTUDQ2PD zmm1, {k}{z}, ymm2/m512/m32bcst","VCVTUDQ2PD ymm2/m512/m32bcst, {k}{z}, zmm1","vcvtudq2pd ymm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.F3.0F.W0 7A /r","V","V","AVX512F","bscale4,scale32","w,r,r","","" +"VCVTUDQ2PS xmm1, {k}{z}, xmm2/m128/m32bcst","VCVTUDQ2PS xmm2/m128/m32bcst, {k}{z}, xmm1","vcvtudq2ps xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.F2.0F.W0 7A /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r","","" +"VCVTUDQ2PS ymm1, {k}{z}, ymm2/m256/m32bcst","VCVTUDQ2PS ymm2/m256/m32bcst, {k}{z}, ymm1","vcvtudq2ps ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.F2.0F.W0 7A /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r","","" +"VCVTUDQ2PS zmm1{er}, {k}{z}, zmm2","VCVTUDQ2PS zmm2, {k}{z}, zmm1{er}","vcvtudq2ps zmm2, {k}{z}, zmm1{er}","EVEX.512.F2.0F.W0 7A /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"VCVTUDQ2PS zmm1, {k}{z}, zmm2/m512/m32bcst","VCVTUDQ2PS zmm2/m512/m32bcst, {k}{z}, zmm1","vcvtudq2ps zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.F2.0F.W0 7A /r","V","V","AVX512F","bscale4,scale64","w,r,r","","" +"VCVTUQQ2PD xmm1, {k}{z}, xmm2/m128/m64bcst","VCVTUQQ2PD xmm2/m128/m64bcst, {k}{z}, xmm1","vcvtuqq2pd xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.F3.0F.W1 7A /r","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r","","" +"VCVTUQQ2PD ymm1, {k}{z}, ymm2/m256/m64bcst","VCVTUQQ2PD ymm2/m256/m64bcst, {k}{z}, ymm1","vcvtuqq2pd ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.F3.0F.W1 7A /r","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r","","" +"VCVTUQQ2PD zmm1{er}, {k}{z}, zmm2","VCVTUQQ2PD zmm2, {k}{z}, zmm1{er}","vcvtuqq2pd zmm2, {k}{z}, zmm1{er}","EVEX.512.F3.0F.W1 7A /r","V","V","AVX512DQ","modrm_regonly","w,r,r","","" +"VCVTUQQ2PD zmm1, {k}{z}, zmm2/m512/m64bcst","VCVTUQQ2PD zmm2/m512/m64bcst, {k}{z}, zmm1","vcvtuqq2pd zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.F3.0F.W1 7A /r","V","V","AVX512DQ","bscale8,scale64","w,r,r","","" +"VCVTUQQ2PS ymm1{er}, {k}{z}, zmm2","VCVTUQQ2PS zmm2, {k}{z}, ymm1{er}","vcvtuqq2ps zmm2, {k}{z}, ymm1{er}","EVEX.512.F2.0F.W1 7A /r","V","V","AVX512DQ","modrm_regonly","w,r,r","Y","" +"VCVTUQQ2PS ymm1, {k}{z}, zmm2/m512/m64bcst","VCVTUQQ2PS zmm2/m512/m64bcst, {k}{z}, ymm1","vcvtuqq2ps zmm2/m512/m64bcst, {k}{z}, ymm1","EVEX.512.F2.0F.W1 7A /r","V","V","AVX512DQ","bscale8,scale64","w,r,r","Y","512" +"VCVTUQQ2PS xmm1, {k}{z}, xmm2/m128/m64bcst","VCVTUQQ2PSX xmm2/m128/m64bcst, {k}{z}, xmm1","vcvtuqq2psx xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.F2.0F.W1 7A /r","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r","Y","128" +"VCVTUQQ2PS xmm1, {k}{z}, ymm2/m256/m64bcst","VCVTUQQ2PSY ymm2/m256/m64bcst, {k}{z}, xmm1","vcvtuqq2psy ymm2/m256/m64bcst, {k}{z}, xmm1","EVEX.256.F2.0F.W1 7A /r","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r","Y","256" +"VCVTUSI2SD xmm1, xmmV, r/m32","VCVTUSI2SDL r/m32, xmmV, xmm1","vcvtusi2sd r/m32, xmmV, xmm1","EVEX.NDS.LIG.F2.0F.W0 7B /r","V","V","AVX512F","scale4","w,r,r","Y","32" +"VCVTUSI2SD xmm1, xmmV, r/m64","VCVTUSI2SDQ r/m64, xmmV, xmm1","vcvtusi2sd r/m64, xmmV, xmm1","EVEX.NDS.LIG.F2.0F.W1 7B /r","N.S.","V","AVX512F","scale8","w,r,r","Y","64" +"VCVTUSI2SD xmm1{er}, xmmV, rmr64","VCVTUSI2SDQ rmr64, xmmV, xmm1{er}","vcvtusi2sd rmr64, xmmV, xmm1{er}","EVEX.NDS.128.F2.0F.W1 7B /r","N.S.","V","AVX512F","modrm_regonly","w,r,r","Y","64" +"VCVTUSI2SS xmm1, xmmV, r/m32","VCVTUSI2SSL r/m32, xmmV, xmm1","vcvtusi2ssl r/m32, xmmV, xmm1","EVEX.NDS.LIG.F3.0F.W0 7B /r","V","V","AVX512F","scale4","w,r,r","Y","32" +"VCVTUSI2SS xmm1{er}, xmmV, rmr32","VCVTUSI2SSL rmr32, xmmV, xmm1{er}","vcvtusi2ssl rmr32, xmmV, xmm1{er}","EVEX.NDS.128.F3.0F.W0 7B /r","V","V","AVX512F","modrm_regonly","w,r,r","Y","32" +"VCVTUSI2SS xmm1, xmmV, r/m64","VCVTUSI2SSQ r/m64, xmmV, xmm1","vcvtusi2ssq r/m64, xmmV, xmm1","EVEX.NDS.LIG.F3.0F.W1 7B /r","N.S.","V","AVX512F","scale8","w,r,r","Y","64" +"VCVTUSI2SS xmm1{er}, xmmV, rmr64","VCVTUSI2SSQ rmr64, xmmV, xmm1{er}","vcvtusi2ssq rmr64, xmmV, xmm1{er}","EVEX.NDS.128.F3.0F.W1 7B /r","N.S.","V","AVX512F","modrm_regonly","w,r,r","Y","64" +"VDBPSADBW xmm1, {k}{z}, xmmV, xmm2/m128, imm8u","VDBPSADBW imm8u, xmm2/m128, xmmV, {k}{z}, xmm1","vdbpsadbw imm8u, xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F3A.W0 42 /r ib","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r,r","","" +"VDBPSADBW ymm1, {k}{z}, ymmV, ymm2/m256, imm8u","VDBPSADBW imm8u, ymm2/m256, ymmV, {k}{z}, ymm1","vdbpsadbw imm8u, ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W0 42 /r ib","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r,r","","" +"VDBPSADBW zmm1, {k}{z}, zmmV, zmm2/m512, imm8u","VDBPSADBW imm8u, zmm2/m512, zmmV, {k}{z}, zmm1","vdbpsadbw imm8u, zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W0 42 /r ib","V","V","AVX512BW","scale64","w,r,r,r,r","","" +"VDIVPD xmm1, xmmV, xmm2/m128","VDIVPD xmm2/m128, xmmV, xmm1","vdivpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 5E /r","V","V","AVX","","w,r,r","","" +"VDIVPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VDIVPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vdivpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 5E /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VDIVPD ymm1, ymmV, ymm2/m256","VDIVPD ymm2/m256, ymmV, ymm1","vdivpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 5E /r","V","V","AVX","","w,r,r","","" +"VDIVPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VDIVPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vdivpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 5E /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VDIVPD zmm1{er}, {k}{z}, zmmV, zmm2","VDIVPD zmm2, zmmV, {k}{z}, zmm1{er}","vdivpd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.NDS.512.66.0F.W1 5E /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VDIVPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VDIVPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vdivpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 5E /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VDIVPS xmm1, xmmV, xmm2/m128","VDIVPS xmm2/m128, xmmV, xmm1","vdivps xmm2/m128, xmmV, xmm1","VEX.NDS.128.0F.WIG 5E /r","V","V","AVX","","w,r,r","","" +"VDIVPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VDIVPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vdivps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.0F.W0 5E /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VDIVPS ymm1, ymmV, ymm2/m256","VDIVPS ymm2/m256, ymmV, ymm1","vdivps ymm2/m256, ymmV, ymm1","VEX.NDS.256.0F.WIG 5E /r","V","V","AVX","","w,r,r","","" +"VDIVPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VDIVPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vdivps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.0F.W0 5E /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VDIVPS zmm1{er}, {k}{z}, zmmV, zmm2","VDIVPS zmm2, zmmV, {k}{z}, zmm1{er}","vdivps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.NDS.512.0F.W0 5E /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VDIVPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VDIVPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vdivps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.0F.W0 5E /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VDIVSD xmm1{er}, {k}{z}, xmmV, xmm2","VDIVSD xmm2, xmmV, {k}{z}, xmm1{er}","vdivsd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.NDS.128.F2.0F.W1 5E /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VDIVSD xmm1, xmmV, xmm2/m64","VDIVSD xmm2/m64, xmmV, xmm1","vdivsd xmm2/m64, xmmV, xmm1","VEX.NDS.LIG.F2.0F.WIG 5E /r","V","V","AVX","","w,r,r","","" +"VDIVSD xmm1, {k}{z}, xmmV, xmm2/m64","VDIVSD xmm2/m64, xmmV, {k}{z}, xmm1","vdivsd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F2.0F.W1 5E /r","V","V","AVX512F","scale8","w,r,r,r","","" +"VDIVSS xmm1{er}, {k}{z}, xmmV, xmm2","VDIVSS xmm2, xmmV, {k}{z}, xmm1{er}","vdivss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.NDS.128.F3.0F.W0 5E /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VDIVSS xmm1, xmmV, xmm2/m32","VDIVSS xmm2/m32, xmmV, xmm1","vdivss xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.F3.0F.WIG 5E /r","V","V","AVX","","w,r,r","","" +"VDIVSS xmm1, {k}{z}, xmmV, xmm2/m32","VDIVSS xmm2/m32, xmmV, {k}{z}, xmm1","vdivss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F3.0F.W0 5E /r","V","V","AVX512F","scale4","w,r,r,r","","" +"VDPPD xmm1, xmmV, xmm2/m128, imm8u","VDPPD imm8u, xmm2/m128, xmmV, xmm1","vdppd imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.WIG 41 /r ib","V","V","AVX","","w,r,r,r","","" +"VDPPS xmm1, xmmV, xmm2/m128, imm8u","VDPPS imm8u, xmm2/m128, xmmV, xmm1","vdpps imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.WIG 40 /r ib","V","V","AVX","","w,r,r,r","","" +"VDPPS ymm1, ymmV, ymm2/m256, imm8u","VDPPS imm8u, ymm2/m256, ymmV, ymm1","vdpps imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.WIG 40 /r ib","V","V","AVX","","w,r,r,r","","" +"VERR r/m16","VERR r/m16","verr r/m16","0F 00 /4","V","V","","","r","","" +"VERW r/m16","VERW r/m16","verw r/m16","0F 00 /5","V","V","","","r","","" +"VEXP2PD zmm1{sae}, {k}{z}, zmm2","VEXP2PD zmm2, {k}{z}, zmm1{sae}","vexp2pd zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F38.W1 C8 /r","V","V","AVX512ER","modrm_regonly","w,r,r","","" +"VEXP2PD zmm1, {k}{z}, zmm2/m512/m64bcst","VEXP2PD zmm2/m512/m64bcst, {k}{z}, zmm1","vexp2pd zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W1 C8 /r","V","V","AVX512ER","bscale8,scale64","w,r,r","","" +"VEXP2PS zmm1{sae}, {k}{z}, zmm2","VEXP2PS zmm2, {k}{z}, zmm1{sae}","vexp2ps zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F38.W0 C8 /r","V","V","AVX512ER","modrm_regonly","w,r,r","","" +"VEXP2PS zmm1, {k}{z}, zmm2/m512/m32bcst","VEXP2PS zmm2/m512/m32bcst, {k}{z}, zmm1","vexp2ps zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W0 C8 /r","V","V","AVX512ER","bscale4,scale64","w,r,r","","" +"VEXPANDPD xmm1, {k}{z}, xmm2/m128","VEXPANDPD xmm2/m128, {k}{z}, xmm1","vexpandpd xmm2/m128, {k}{z}, xmm1","EVEX.128.66.0F38.W1 88 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VEXPANDPD ymm1, {k}{z}, ymm2/m256","VEXPANDPD ymm2/m256, {k}{z}, ymm1","vexpandpd ymm2/m256, {k}{z}, ymm1","EVEX.256.66.0F38.W1 88 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VEXPANDPD zmm1, {k}{z}, zmm2/m512","VEXPANDPD zmm2/m512, {k}{z}, zmm1","vexpandpd zmm2/m512, {k}{z}, zmm1","EVEX.512.66.0F38.W1 88 /r","V","V","AVX512F","scale8","w,r,r","","" +"VEXPANDPS xmm1, {k}{z}, xmm2/m128","VEXPANDPS xmm2/m128, {k}{z}, xmm1","vexpandps xmm2/m128, {k}{z}, xmm1","EVEX.128.66.0F38.W0 88 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VEXPANDPS ymm1, {k}{z}, ymm2/m256","VEXPANDPS ymm2/m256, {k}{z}, ymm1","vexpandps ymm2/m256, {k}{z}, ymm1","EVEX.256.66.0F38.W0 88 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VEXPANDPS zmm1, {k}{z}, zmm2/m512","VEXPANDPS zmm2/m512, {k}{z}, zmm1","vexpandps zmm2/m512, {k}{z}, zmm1","EVEX.512.66.0F38.W0 88 /r","V","V","AVX512F","scale4","w,r,r","","" +"VEXTRACTF128 xmm2/m128, ymm1, imm8u:1","VEXTRACTF128 imm8u:1, ymm1, xmm2/m128","vextractf128 imm8u:1, ymm1, xmm2/m128","VEX.256.66.0F3A.W0 19 /r ib","V","V","AVX","","w,r,r","","" +"VEXTRACTF32X4 xmm2/m128, {k}{z}, ymm1, imm8u:1","VEXTRACTF32X4 imm8u:1, ymm1, {k}{z}, xmm2/m128","vextractf32x4 imm8u:1, ymm1, {k}{z}, xmm2/m128","EVEX.256.66.0F3A.W0 19 /r ib","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VEXTRACTF32X4 xmm2/m128, {k}{z}, zmm1, imm8u:2","VEXTRACTF32X4 imm8u:2, zmm1, {k}{z}, xmm2/m128","vextractf32x4 imm8u:2, zmm1, {k}{z}, xmm2/m128","EVEX.512.66.0F3A.W0 19 /r ib","V","V","AVX512F","scale16","w,r,r,r","","" +"VEXTRACTF32X8 ymm2/m256, {k}{z}, zmm1, imm8u:1","VEXTRACTF32X8 imm8u:1, zmm1, {k}{z}, ymm2/m256","vextractf32x8 imm8u:1, zmm1, {k}{z}, ymm2/m256","EVEX.512.66.0F3A.W0 1B /r ib","V","V","AVX512DQ","scale32","w,r,r,r","","" +"VEXTRACTF64X2 xmm2/m128, {k}{z}, ymm1, imm8u:1","VEXTRACTF64X2 imm8u:1, ymm1, {k}{z}, xmm2/m128","vextractf64x2 imm8u:1, ymm1, {k}{z}, xmm2/m128","EVEX.256.66.0F3A.W1 19 /r ib","V","V","AVX512DQ+AVX512VL","scale16","w,r,r,r","","" +"VEXTRACTF64X2 xmm2/m128, {k}{z}, zmm1, imm8u:2","VEXTRACTF64X2 imm8u:2, zmm1, {k}{z}, xmm2/m128","vextractf64x2 imm8u:2, zmm1, {k}{z}, xmm2/m128","EVEX.512.66.0F3A.W1 19 /r ib","V","V","AVX512DQ","scale16","w,r,r,r","","" +"VEXTRACTF64X4 ymm2/m256, {k}{z}, zmm1, imm8u","VEXTRACTF64X4 imm8u, zmm1, {k}{z}, ymm2/m256","vextractf64x4 imm8u, zmm1, {k}{z}, ymm2/m256","EVEX.512.66.0F3A.W1 1B /r ib","V","V","AVX512F","scale32","w,r,r,r","","" +"VEXTRACTI128 xmm2/m128, ymm1, imm8u:1","VEXTRACTI128 imm8u:1, ymm1, xmm2/m128","vextracti128 imm8u:1, ymm1, xmm2/m128","VEX.256.66.0F3A.W0 39 /r ib","V","V","AVX2","","w,r,r","","" +"VEXTRACTI32X4 xmm2/m128, {k}{z}, ymm1, imm8u:1","VEXTRACTI32X4 imm8u:1, ymm1, {k}{z}, xmm2/m128","vextracti32x4 imm8u:1, ymm1, {k}{z}, xmm2/m128","EVEX.256.66.0F3A.W0 39 /r ib","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VEXTRACTI32X4 xmm2/m128, {k}{z}, zmm1, imm8u:2","VEXTRACTI32X4 imm8u:2, zmm1, {k}{z}, xmm2/m128","vextracti32x4 imm8u:2, zmm1, {k}{z}, xmm2/m128","EVEX.512.66.0F3A.W0 39 /r ib","V","V","AVX512F","scale16","w,r,r,r","","" +"VEXTRACTI32X8 ymm2/m256, {k}{z}, zmm1, imm8u:1","VEXTRACTI32X8 imm8u:1, zmm1, {k}{z}, ymm2/m256","vextracti32x8 imm8u:1, zmm1, {k}{z}, ymm2/m256","EVEX.512.66.0F3A.W0 3B /r ib","V","V","AVX512DQ","scale32","w,r,r,r","","" +"VEXTRACTI64X2 xmm2/m128, {k}{z}, ymm1, imm8u:1","VEXTRACTI64X2 imm8u:1, ymm1, {k}{z}, xmm2/m128","vextracti64x2 imm8u:1, ymm1, {k}{z}, xmm2/m128","EVEX.256.66.0F3A.W1 39 /r ib","V","V","AVX512DQ+AVX512VL","scale16","w,r,r,r","","" +"VEXTRACTI64X2 xmm2/m128, {k}{z}, zmm1, imm8u:2","VEXTRACTI64X2 imm8u:2, zmm1, {k}{z}, xmm2/m128","vextracti64x2 imm8u:2, zmm1, {k}{z}, xmm2/m128","EVEX.512.66.0F3A.W1 39 /r ib","V","V","AVX512DQ","scale16","w,r,r,r","","" +"VEXTRACTI64X4 ymm2/m256, {k}{z}, zmm1, imm8u:1","VEXTRACTI64X4 imm8u:1, zmm1, {k}{z}, ymm2/m256","vextracti64x4 imm8u:1, zmm1, {k}{z}, ymm2/m256","EVEX.512.66.0F3A.W1 3B /r ib","V","V","AVX512F","scale32","w,r,r,r","","" +"VEXTRACTPS r/m32, xmm1, imm8u:2","VEXTRACTPS imm8u:2, xmm1, r/m32","vextractps imm8u:2, xmm1, r/m32","EVEX.128.66.0F3A.WIG 17 /r ib","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VEXTRACTPS r/m32, xmm1, imm8u:2","VEXTRACTPS imm8u:2, xmm1, r/m32","vextractps imm8u:2, xmm1, r/m32","VEX.128.66.0F3A.WIG 17 /r ib","V","V","AVX","","w,r,r","","" +"VFIXUPIMMPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst, imm8u","VFIXUPIMMPD imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfixupimmpd imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F3A.W1 54 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r,r","","" +"VFIXUPIMMPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst, imm8u","VFIXUPIMMPD imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfixupimmpd imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F3A.W1 54 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r,r","","" +"VFIXUPIMMPD zmm1{sae}, {k}{z}, zmmV, zmm2, imm8u","VFIXUPIMMPD imm8u, zmm2, zmmV, {k}{z}, zmm1{sae}","vfixupimmpd imm8u, zmm2, zmmV, {k}{z}, zmm1{sae}","EVEX.DDS.512.66.0F3A.W1 54 /r ib","V","V","AVX512F","modrm_regonly","rw,r,r,r,r","","" +"VFIXUPIMMPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst, imm8u","VFIXUPIMMPD imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfixupimmpd imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F3A.W1 54 /r ib","V","V","AVX512F","bscale8,scale64","rw,r,r,r,r","","" +"VFIXUPIMMPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst, imm8u","VFIXUPIMMPS imm8u, xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfixupimmps imm8u, xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F3A.W0 54 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r,r","","" +"VFIXUPIMMPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst, imm8u","VFIXUPIMMPS imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfixupimmps imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F3A.W0 54 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r,r","","" +"VFIXUPIMMPS zmm1{sae}, {k}{z}, zmmV, zmm2, imm8u","VFIXUPIMMPS imm8u, zmm2, zmmV, {k}{z}, zmm1{sae}","vfixupimmps imm8u, zmm2, zmmV, {k}{z}, zmm1{sae}","EVEX.DDS.512.66.0F3A.W0 54 /r ib","V","V","AVX512F","modrm_regonly","rw,r,r,r,r","","" +"VFIXUPIMMPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst, imm8u","VFIXUPIMMPS imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfixupimmps imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F3A.W0 54 /r ib","V","V","AVX512F","bscale4,scale64","rw,r,r,r,r","","" +"VFIXUPIMMSD xmm1{sae}, {k}{z}, xmmV, xmm2, imm8u","VFIXUPIMMSD imm8u, xmm2, xmmV, {k}{z}, xmm1{sae}","vfixupimmsd imm8u, xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.DDS.128.66.0F3A.W1 55 /r ib","V","V","AVX512F","modrm_regonly","rw,r,r,r,r","","" +"VFIXUPIMMSD xmm1, {k}{z}, xmmV, xmm2/m64, imm8u","VFIXUPIMMSD imm8u, xmm2/m64, xmmV, {k}{z}, xmm1","vfixupimmsd imm8u, xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F3A.W1 55 /r ib","V","V","AVX512F","scale8","rw,r,r,r,r","","" +"VFIXUPIMMSS xmm1{sae}, {k}{z}, xmmV, xmm2, imm8u","VFIXUPIMMSS imm8u, xmm2, xmmV, {k}{z}, xmm1{sae}","vfixupimmss imm8u, xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.DDS.128.66.0F3A.W0 55 /r ib","V","V","AVX512F","modrm_regonly","rw,r,r,r,r","","" +"VFIXUPIMMSS xmm1, {k}{z}, xmmV, xmm2/m32, imm8u","VFIXUPIMMSS imm8u, xmm2/m32, xmmV, {k}{z}, xmm1","vfixupimmss imm8u, xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F3A.W0 55 /r ib","V","V","AVX512F","scale4","rw,r,r,r,r","","" +"VFMADD132PD xmm1, xmmV, xmm2/m128","VFMADD132PD xmm2/m128, xmmV, xmm1","vfmadd132pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 98 /r","V","V","FMA","","rw,r,r","","" +"VFMADD132PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFMADD132PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfmadd132pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 98 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFMADD132PD ymm1, ymmV, ymm2/m256","VFMADD132PD ymm2/m256, ymmV, ymm1","vfmadd132pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 98 /r","V","V","FMA","","rw,r,r","","" +"VFMADD132PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFMADD132PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfmadd132pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 98 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFMADD132PD zmm1{er}, {k}{z}, zmmV, zmm2","VFMADD132PD zmm2, zmmV, {k}{z}, zmm1{er}","vfmadd132pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 98 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADD132PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFMADD132PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfmadd132pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 98 /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFMADD132PS xmm1, xmmV, xmm2/m128","VFMADD132PS xmm2/m128, xmmV, xmm1","vfmadd132ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 98 /r","V","V","FMA","","rw,r,r","","" +"VFMADD132PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFMADD132PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfmadd132ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 98 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFMADD132PS ymm1, ymmV, ymm2/m256","VFMADD132PS ymm2/m256, ymmV, ymm1","vfmadd132ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 98 /r","V","V","FMA","","rw,r,r","","" +"VFMADD132PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFMADD132PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfmadd132ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 98 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFMADD132PS zmm1{er}, {k}{z}, zmmV, zmm2","VFMADD132PS zmm2, zmmV, {k}{z}, zmm1{er}","vfmadd132ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 98 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADD132PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFMADD132PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfmadd132ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 98 /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFMADD132SD xmm1{er}, {k}{z}, xmmV, xmm2","VFMADD132SD xmm2, xmmV, {k}{z}, xmm1{er}","vfmadd132sd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W1 99 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADD132SD xmm1, xmmV, xmm2/m64","VFMADD132SD xmm2/m64, xmmV, xmm1","vfmadd132sd xmm2/m64, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W1 99 /r","V","V","FMA","","rw,r,r","","" +"VFMADD132SD xmm1, {k}{z}, xmmV, xmm2/m64","VFMADD132SD xmm2/m64, xmmV, {k}{z}, xmm1","vfmadd132sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W1 99 /r","V","V","AVX512F","scale8","rw,r,r,r","","" +"VFMADD132SS xmm1{er}, {k}{z}, xmmV, xmm2","VFMADD132SS xmm2, xmmV, {k}{z}, xmm1{er}","vfmadd132ss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W0 99 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADD132SS xmm1, xmmV, xmm2/m32","VFMADD132SS xmm2/m32, xmmV, xmm1","vfmadd132ss xmm2/m32, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W0 99 /r","V","V","FMA","","rw,r,r","","" +"VFMADD132SS xmm1, {k}{z}, xmmV, xmm2/m32","VFMADD132SS xmm2/m32, xmmV, {k}{z}, xmm1","vfmadd132ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W0 99 /r","V","V","AVX512F","scale4","rw,r,r,r","","" +"VFMADD213PD xmm1, xmmV, xmm2/m128","VFMADD213PD xmm2/m128, xmmV, xmm1","vfmadd213pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 A8 /r","V","V","FMA","","rw,r,r","","" +"VFMADD213PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFMADD213PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfmadd213pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 A8 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFMADD213PD ymm1, ymmV, ymm2/m256","VFMADD213PD ymm2/m256, ymmV, ymm1","vfmadd213pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 A8 /r","V","V","FMA","","rw,r,r","","" +"VFMADD213PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFMADD213PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfmadd213pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 A8 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFMADD213PD zmm1{er}, {k}{z}, zmmV, zmm2","VFMADD213PD zmm2, zmmV, {k}{z}, zmm1{er}","vfmadd213pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 A8 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADD213PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFMADD213PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfmadd213pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 A8 /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFMADD213PS xmm1, xmmV, xmm2/m128","VFMADD213PS xmm2/m128, xmmV, xmm1","vfmadd213ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 A8 /r","V","V","FMA","","rw,r,r","","" +"VFMADD213PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFMADD213PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfmadd213ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 A8 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFMADD213PS ymm1, ymmV, ymm2/m256","VFMADD213PS ymm2/m256, ymmV, ymm1","vfmadd213ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 A8 /r","V","V","FMA","","rw,r,r","","" +"VFMADD213PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFMADD213PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfmadd213ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 A8 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFMADD213PS zmm1{er}, {k}{z}, zmmV, zmm2","VFMADD213PS zmm2, zmmV, {k}{z}, zmm1{er}","vfmadd213ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 A8 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADD213PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFMADD213PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfmadd213ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 A8 /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFMADD213SD xmm1{er}, {k}{z}, xmmV, xmm2","VFMADD213SD xmm2, xmmV, {k}{z}, xmm1{er}","vfmadd213sd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W1 A9 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADD213SD xmm1, xmmV, xmm2/m64","VFMADD213SD xmm2/m64, xmmV, xmm1","vfmadd213sd xmm2/m64, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W1 A9 /r","V","V","FMA","","rw,r,r","","" +"VFMADD213SD xmm1, {k}{z}, xmmV, xmm2/m64","VFMADD213SD xmm2/m64, xmmV, {k}{z}, xmm1","vfmadd213sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W1 A9 /r","V","V","AVX512F","scale8","rw,r,r,r","","" +"VFMADD213SS xmm1{er}, {k}{z}, xmmV, xmm2","VFMADD213SS xmm2, xmmV, {k}{z}, xmm1{er}","vfmadd213ss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W0 A9 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADD213SS xmm1, xmmV, xmm2/m32","VFMADD213SS xmm2/m32, xmmV, xmm1","vfmadd213ss xmm2/m32, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W0 A9 /r","V","V","FMA","","rw,r,r","","" +"VFMADD213SS xmm1, {k}{z}, xmmV, xmm2/m32","VFMADD213SS xmm2/m32, xmmV, {k}{z}, xmm1","vfmadd213ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W0 A9 /r","V","V","AVX512F","scale4","rw,r,r,r","","" +"VFMADD231PD xmm1, xmmV, xmm2/m128","VFMADD231PD xmm2/m128, xmmV, xmm1","vfmadd231pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 B8 /r","V","V","FMA","","rw,r,r","","" +"VFMADD231PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFMADD231PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfmadd231pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 B8 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFMADD231PD ymm1, ymmV, ymm2/m256","VFMADD231PD ymm2/m256, ymmV, ymm1","vfmadd231pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 B8 /r","V","V","FMA","","rw,r,r","","" +"VFMADD231PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFMADD231PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfmadd231pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 B8 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFMADD231PD zmm1{er}, {k}{z}, zmmV, zmm2","VFMADD231PD zmm2, zmmV, {k}{z}, zmm1{er}","vfmadd231pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 B8 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADD231PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFMADD231PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfmadd231pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 B8 /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFMADD231PS xmm1, xmmV, xmm2/m128","VFMADD231PS xmm2/m128, xmmV, xmm1","vfmadd231ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 B8 /r","V","V","FMA","","rw,r,r","","" +"VFMADD231PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFMADD231PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfmadd231ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 B8 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFMADD231PS ymm1, ymmV, ymm2/m256","VFMADD231PS ymm2/m256, ymmV, ymm1","vfmadd231ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 B8 /r","V","V","FMA","","rw,r,r","","" +"VFMADD231PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFMADD231PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfmadd231ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 B8 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFMADD231PS zmm1{er}, {k}{z}, zmmV, zmm2","VFMADD231PS zmm2, zmmV, {k}{z}, zmm1{er}","vfmadd231ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 B8 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADD231PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFMADD231PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfmadd231ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 B8 /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFMADD231SD xmm1{er}, {k}{z}, xmmV, xmm2","VFMADD231SD xmm2, xmmV, {k}{z}, xmm1{er}","vfmadd231sd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W1 B9 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADD231SD xmm1, xmmV, xmm2/m64","VFMADD231SD xmm2/m64, xmmV, xmm1","vfmadd231sd xmm2/m64, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W1 B9 /r","V","V","FMA","","rw,r,r","","" +"VFMADD231SD xmm1, {k}{z}, xmmV, xmm2/m64","VFMADD231SD xmm2/m64, xmmV, {k}{z}, xmm1","vfmadd231sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W1 B9 /r","V","V","AVX512F","scale8","rw,r,r,r","","" +"VFMADD231SS xmm1{er}, {k}{z}, xmmV, xmm2","VFMADD231SS xmm2, xmmV, {k}{z}, xmm1{er}","vfmadd231ss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W0 B9 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADD231SS xmm1, xmmV, xmm2/m32","VFMADD231SS xmm2/m32, xmmV, xmm1","vfmadd231ss xmm2/m32, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W0 B9 /r","V","V","FMA","","rw,r,r","","" +"VFMADD231SS xmm1, {k}{z}, xmmV, xmm2/m32","VFMADD231SS xmm2/m32, xmmV, {k}{z}, xmm1","vfmadd231ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W0 B9 /r","V","V","AVX512F","scale4","rw,r,r,r","","" +"VFMADDPD xmm1, xmmV, xmmIH, xmm2/m128","VFMADDPD xmm2/m128, xmmIH, xmmV, xmm1","vfmaddpd xmm2/m128, xmmIH, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 69 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDPD xmm1, xmmV, xmm2/m128, xmmIH","VFMADDPD xmmIH, xmm2/m128, xmmV, xmm1","vfmaddpd xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 69 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDPD ymm1, ymmV, ymmIH, ymm2/m256","VFMADDPD ymm2/m256, ymmIH, ymmV, ymm1","vfmaddpd ymm2/m256, ymmIH, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 69 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDPD ymm1, ymmV, ymm2/m256, ymmIH","VFMADDPD ymmIH, ymm2/m256, ymmV, ymm1","vfmaddpd ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 69 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDPS xmm1, xmmV, xmmIH, xmm2/m128","VFMADDPS xmm2/m128, xmmIH, xmmV, xmm1","vfmaddps xmm2/m128, xmmIH, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 68 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDPS xmm1, xmmV, xmm2/m128, xmmIH","VFMADDPS xmmIH, xmm2/m128, xmmV, xmm1","vfmaddps xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 68 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDPS ymm1, ymmV, ymmIH, ymm2/m256","VFMADDPS ymm2/m256, ymmIH, ymmV, ymm1","vfmaddps ymm2/m256, ymmIH, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 68 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDPS ymm1, ymmV, ymm2/m256, ymmIH","VFMADDPS ymmIH, ymm2/m256, ymmV, ymm1","vfmaddps ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 68 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDSD xmm1, xmmV, xmmIH, xmm2/m64","VFMADDSD xmm2/m64, xmmIH, xmmV, xmm1","vfmaddsd xmm2/m64, xmmIH, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W1 6B /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDSD xmm1, xmmV, xmm2/m64, xmmIH","VFMADDSD xmmIH, xmm2/m64, xmmV, xmm1","vfmaddsd xmmIH, xmm2/m64, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W0 6B /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDSS xmm1, xmmV, xmmIH, xmm2/m32","VFMADDSS xmm2/m32, xmmIH, xmmV, xmm1","vfmaddss xmm2/m32, xmmIH, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W1 6A /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDSS xmm1, xmmV, xmm2/m32, xmmIH","VFMADDSS xmmIH, xmm2/m32, xmmV, xmm1","vfmaddss xmmIH, xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W0 6A /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDSUB132PD xmm1, xmmV, xmm2/m128","VFMADDSUB132PD xmm2/m128, xmmV, xmm1","vfmaddsub132pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 96 /r","V","V","FMA","","rw,r,r","","" +"VFMADDSUB132PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFMADDSUB132PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfmaddsub132pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 96 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFMADDSUB132PD ymm1, ymmV, ymm2/m256","VFMADDSUB132PD ymm2/m256, ymmV, ymm1","vfmaddsub132pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 96 /r","V","V","FMA","","rw,r,r","","" +"VFMADDSUB132PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFMADDSUB132PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfmaddsub132pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 96 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFMADDSUB132PD zmm1{er}, {k}{z}, zmmV, zmm2","VFMADDSUB132PD zmm2, zmmV, {k}{z}, zmm1{er}","vfmaddsub132pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 96 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADDSUB132PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFMADDSUB132PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfmaddsub132pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 96 /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFMADDSUB132PS xmm1, xmmV, xmm2/m128","VFMADDSUB132PS xmm2/m128, xmmV, xmm1","vfmaddsub132ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 96 /r","V","V","FMA","","rw,r,r","","" +"VFMADDSUB132PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFMADDSUB132PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfmaddsub132ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 96 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFMADDSUB132PS ymm1, ymmV, ymm2/m256","VFMADDSUB132PS ymm2/m256, ymmV, ymm1","vfmaddsub132ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 96 /r","V","V","FMA","","rw,r,r","","" +"VFMADDSUB132PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFMADDSUB132PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfmaddsub132ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 96 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFMADDSUB132PS zmm1{er}, {k}{z}, zmmV, zmm2","VFMADDSUB132PS zmm2, zmmV, {k}{z}, zmm1{er}","vfmaddsub132ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 96 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADDSUB132PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFMADDSUB132PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfmaddsub132ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 96 /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFMADDSUB213PD xmm1, xmmV, xmm2/m128","VFMADDSUB213PD xmm2/m128, xmmV, xmm1","vfmaddsub213pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 A6 /r","V","V","FMA","","rw,r,r","","" +"VFMADDSUB213PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFMADDSUB213PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfmaddsub213pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 A6 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFMADDSUB213PD ymm1, ymmV, ymm2/m256","VFMADDSUB213PD ymm2/m256, ymmV, ymm1","vfmaddsub213pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 A6 /r","V","V","FMA","","rw,r,r","","" +"VFMADDSUB213PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFMADDSUB213PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfmaddsub213pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 A6 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFMADDSUB213PD zmm1{er}, {k}{z}, zmmV, zmm2","VFMADDSUB213PD zmm2, zmmV, {k}{z}, zmm1{er}","vfmaddsub213pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 A6 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADDSUB213PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFMADDSUB213PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfmaddsub213pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 A6 /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFMADDSUB213PS xmm1, xmmV, xmm2/m128","VFMADDSUB213PS xmm2/m128, xmmV, xmm1","vfmaddsub213ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 A6 /r","V","V","FMA","","rw,r,r","","" +"VFMADDSUB213PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFMADDSUB213PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfmaddsub213ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 A6 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFMADDSUB213PS ymm1, ymmV, ymm2/m256","VFMADDSUB213PS ymm2/m256, ymmV, ymm1","vfmaddsub213ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 A6 /r","V","V","FMA","","rw,r,r","","" +"VFMADDSUB213PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFMADDSUB213PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfmaddsub213ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 A6 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFMADDSUB213PS zmm1{er}, {k}{z}, zmmV, zmm2","VFMADDSUB213PS zmm2, zmmV, {k}{z}, zmm1{er}","vfmaddsub213ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 A6 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADDSUB213PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFMADDSUB213PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfmaddsub213ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 A6 /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFMADDSUB231PD xmm1, xmmV, xmm2/m128","VFMADDSUB231PD xmm2/m128, xmmV, xmm1","vfmaddsub231pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 B6 /r","V","V","FMA","","rw,r,r","","" +"VFMADDSUB231PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFMADDSUB231PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfmaddsub231pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 B6 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFMADDSUB231PD ymm1, ymmV, ymm2/m256","VFMADDSUB231PD ymm2/m256, ymmV, ymm1","vfmaddsub231pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 B6 /r","V","V","FMA","","rw,r,r","","" +"VFMADDSUB231PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFMADDSUB231PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfmaddsub231pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 B6 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFMADDSUB231PD zmm1{er}, {k}{z}, zmmV, zmm2","VFMADDSUB231PD zmm2, zmmV, {k}{z}, zmm1{er}","vfmaddsub231pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 B6 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADDSUB231PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFMADDSUB231PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfmaddsub231pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 B6 /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFMADDSUB231PS xmm1, xmmV, xmm2/m128","VFMADDSUB231PS xmm2/m128, xmmV, xmm1","vfmaddsub231ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 B6 /r","V","V","FMA","","rw,r,r","","" +"VFMADDSUB231PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFMADDSUB231PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfmaddsub231ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 B6 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFMADDSUB231PS ymm1, ymmV, ymm2/m256","VFMADDSUB231PS ymm2/m256, ymmV, ymm1","vfmaddsub231ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 B6 /r","V","V","FMA","","rw,r,r","","" +"VFMADDSUB231PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFMADDSUB231PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfmaddsub231ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 B6 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFMADDSUB231PS zmm1{er}, {k}{z}, zmmV, zmm2","VFMADDSUB231PS zmm2, zmmV, {k}{z}, zmm1{er}","vfmaddsub231ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 B6 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMADDSUB231PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFMADDSUB231PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfmaddsub231ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 B6 /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFMADDSUBPD xmm1, xmmV, xmmIH, xmm2/m128","VFMADDSUBPD xmm2/m128, xmmIH, xmmV, xmm1","vfmaddsubpd xmm2/m128, xmmIH, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 5D /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDSUBPD xmm1, xmmV, xmm2/m128, xmmIH","VFMADDSUBPD xmmIH, xmm2/m128, xmmV, xmm1","vfmaddsubpd xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 5D /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDSUBPD ymm1, ymmV, ymmIH, ymm2/m256","VFMADDSUBPD ymm2/m256, ymmIH, ymmV, ymm1","vfmaddsubpd ymm2/m256, ymmIH, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 5D /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDSUBPD ymm1, ymmV, ymm2/m256, ymmIH","VFMADDSUBPD ymmIH, ymm2/m256, ymmV, ymm1","vfmaddsubpd ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 5D /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDSUBPS xmm1, xmmV, xmmIH, xmm2/m128","VFMADDSUBPS xmm2/m128, xmmIH, xmmV, xmm1","vfmaddsubps xmm2/m128, xmmIH, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 5C /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDSUBPS xmm1, xmmV, xmm2/m128, xmmIH","VFMADDSUBPS xmmIH, xmm2/m128, xmmV, xmm1","vfmaddsubps xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 5C /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDSUBPS ymm1, ymmV, ymmIH, ymm2/m256","VFMADDSUBPS ymm2/m256, ymmIH, ymmV, ymm1","vfmaddsubps ymm2/m256, ymmIH, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 5C /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMADDSUBPS ymm1, ymmV, ymm2/m256, ymmIH","VFMADDSUBPS ymmIH, ymm2/m256, ymmV, ymm1","vfmaddsubps ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 5C /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUB132PD xmm1, xmmV, xmm2/m128","VFMSUB132PD xmm2/m128, xmmV, xmm1","vfmsub132pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 9A /r","V","V","FMA","","rw,r,r","","" +"VFMSUB132PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFMSUB132PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfmsub132pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 9A /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFMSUB132PD ymm1, ymmV, ymm2/m256","VFMSUB132PD ymm2/m256, ymmV, ymm1","vfmsub132pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 9A /r","V","V","FMA","","rw,r,r","","" +"VFMSUB132PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFMSUB132PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfmsub132pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 9A /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFMSUB132PD zmm1{er}, {k}{z}, zmmV, zmm2","VFMSUB132PD zmm2, zmmV, {k}{z}, zmm1{er}","vfmsub132pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 9A /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUB132PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFMSUB132PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfmsub132pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 9A /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFMSUB132PS xmm1, xmmV, xmm2/m128","VFMSUB132PS xmm2/m128, xmmV, xmm1","vfmsub132ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 9A /r","V","V","FMA","","rw,r,r","","" +"VFMSUB132PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFMSUB132PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfmsub132ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 9A /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFMSUB132PS ymm1, ymmV, ymm2/m256","VFMSUB132PS ymm2/m256, ymmV, ymm1","vfmsub132ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 9A /r","V","V","FMA","","rw,r,r","","" +"VFMSUB132PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFMSUB132PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfmsub132ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 9A /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFMSUB132PS zmm1{er}, {k}{z}, zmmV, zmm2","VFMSUB132PS zmm2, zmmV, {k}{z}, zmm1{er}","vfmsub132ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 9A /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUB132PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFMSUB132PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfmsub132ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 9A /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFMSUB132SD xmm1{er}, {k}{z}, xmmV, xmm2","VFMSUB132SD xmm2, xmmV, {k}{z}, xmm1{er}","vfmsub132sd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W1 9B /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUB132SD xmm1, xmmV, xmm2/m64","VFMSUB132SD xmm2/m64, xmmV, xmm1","vfmsub132sd xmm2/m64, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W1 9B /r","V","V","FMA","","rw,r,r","","" +"VFMSUB132SD xmm1, {k}{z}, xmmV, xmm2/m64","VFMSUB132SD xmm2/m64, xmmV, {k}{z}, xmm1","vfmsub132sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W1 9B /r","V","V","AVX512F","scale8","rw,r,r,r","","" +"VFMSUB132SS xmm1{er}, {k}{z}, xmmV, xmm2","VFMSUB132SS xmm2, xmmV, {k}{z}, xmm1{er}","vfmsub132ss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W0 9B /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUB132SS xmm1, xmmV, xmm2/m32","VFMSUB132SS xmm2/m32, xmmV, xmm1","vfmsub132ss xmm2/m32, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W0 9B /r","V","V","FMA","","rw,r,r","","" +"VFMSUB132SS xmm1, {k}{z}, xmmV, xmm2/m32","VFMSUB132SS xmm2/m32, xmmV, {k}{z}, xmm1","vfmsub132ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W0 9B /r","V","V","AVX512F","scale4","rw,r,r,r","","" +"VFMSUB213PD xmm1, xmmV, xmm2/m128","VFMSUB213PD xmm2/m128, xmmV, xmm1","vfmsub213pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 AA /r","V","V","FMA","","rw,r,r","","" +"VFMSUB213PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFMSUB213PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfmsub213pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 AA /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFMSUB213PD ymm1, ymmV, ymm2/m256","VFMSUB213PD ymm2/m256, ymmV, ymm1","vfmsub213pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 AA /r","V","V","FMA","","rw,r,r","","" +"VFMSUB213PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFMSUB213PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfmsub213pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 AA /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFMSUB213PD zmm1{er}, {k}{z}, zmmV, zmm2","VFMSUB213PD zmm2, zmmV, {k}{z}, zmm1{er}","vfmsub213pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 AA /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUB213PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFMSUB213PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfmsub213pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 AA /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFMSUB213PS xmm1, xmmV, xmm2/m128","VFMSUB213PS xmm2/m128, xmmV, xmm1","vfmsub213ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 AA /r","V","V","FMA","","rw,r,r","","" +"VFMSUB213PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFMSUB213PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfmsub213ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 AA /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFMSUB213PS ymm1, ymmV, ymm2/m256","VFMSUB213PS ymm2/m256, ymmV, ymm1","vfmsub213ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 AA /r","V","V","FMA","","rw,r,r","","" +"VFMSUB213PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFMSUB213PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfmsub213ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 AA /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFMSUB213PS zmm1{er}, {k}{z}, zmmV, zmm2","VFMSUB213PS zmm2, zmmV, {k}{z}, zmm1{er}","vfmsub213ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 AA /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUB213PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFMSUB213PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfmsub213ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 AA /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFMSUB213SD xmm1{er}, {k}{z}, xmmV, xmm2","VFMSUB213SD xmm2, xmmV, {k}{z}, xmm1{er}","vfmsub213sd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W1 AB /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUB213SD xmm1, xmmV, xmm2/m64","VFMSUB213SD xmm2/m64, xmmV, xmm1","vfmsub213sd xmm2/m64, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W1 AB /r","V","V","FMA","","rw,r,r","","" +"VFMSUB213SD xmm1, {k}{z}, xmmV, xmm2/m64","VFMSUB213SD xmm2/m64, xmmV, {k}{z}, xmm1","vfmsub213sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W1 AB /r","V","V","AVX512F","scale8","rw,r,r,r","","" +"VFMSUB213SS xmm1{er}, {k}{z}, xmmV, xmm2","VFMSUB213SS xmm2, xmmV, {k}{z}, xmm1{er}","vfmsub213ss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W0 AB /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUB213SS xmm1, xmmV, xmm2/m32","VFMSUB213SS xmm2/m32, xmmV, xmm1","vfmsub213ss xmm2/m32, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W0 AB /r","V","V","FMA","","rw,r,r","","" +"VFMSUB213SS xmm1, {k}{z}, xmmV, xmm2/m32","VFMSUB213SS xmm2/m32, xmmV, {k}{z}, xmm1","vfmsub213ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W0 AB /r","V","V","AVX512F","scale4","rw,r,r,r","","" +"VFMSUB231PD xmm1, xmmV, xmm2/m128","VFMSUB231PD xmm2/m128, xmmV, xmm1","vfmsub231pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 BA /r","V","V","FMA","","rw,r,r","","" +"VFMSUB231PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFMSUB231PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfmsub231pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 BA /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFMSUB231PD ymm1, ymmV, ymm2/m256","VFMSUB231PD ymm2/m256, ymmV, ymm1","vfmsub231pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 BA /r","V","V","FMA","","rw,r,r","","" +"VFMSUB231PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFMSUB231PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfmsub231pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 BA /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFMSUB231PD zmm1{er}, {k}{z}, zmmV, zmm2","VFMSUB231PD zmm2, zmmV, {k}{z}, zmm1{er}","vfmsub231pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 BA /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUB231PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFMSUB231PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfmsub231pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 BA /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFMSUB231PS xmm1, xmmV, xmm2/m128","VFMSUB231PS xmm2/m128, xmmV, xmm1","vfmsub231ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 BA /r","V","V","FMA","","rw,r,r","","" +"VFMSUB231PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFMSUB231PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfmsub231ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 BA /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFMSUB231PS ymm1, ymmV, ymm2/m256","VFMSUB231PS ymm2/m256, ymmV, ymm1","vfmsub231ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 BA /r","V","V","FMA","","rw,r,r","","" +"VFMSUB231PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFMSUB231PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfmsub231ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 BA /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFMSUB231PS zmm1{er}, {k}{z}, zmmV, zmm2","VFMSUB231PS zmm2, zmmV, {k}{z}, zmm1{er}","vfmsub231ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 BA /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUB231PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFMSUB231PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfmsub231ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 BA /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFMSUB231SD xmm1{er}, {k}{z}, xmmV, xmm2","VFMSUB231SD xmm2, xmmV, {k}{z}, xmm1{er}","vfmsub231sd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W1 BB /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUB231SD xmm1, xmmV, xmm2/m64","VFMSUB231SD xmm2/m64, xmmV, xmm1","vfmsub231sd xmm2/m64, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W1 BB /r","V","V","FMA","","rw,r,r","","" +"VFMSUB231SD xmm1, {k}{z}, xmmV, xmm2/m64","VFMSUB231SD xmm2/m64, xmmV, {k}{z}, xmm1","vfmsub231sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W1 BB /r","V","V","AVX512F","scale8","rw,r,r,r","","" +"VFMSUB231SS xmm1{er}, {k}{z}, xmmV, xmm2","VFMSUB231SS xmm2, xmmV, {k}{z}, xmm1{er}","vfmsub231ss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W0 BB /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUB231SS xmm1, xmmV, xmm2/m32","VFMSUB231SS xmm2/m32, xmmV, xmm1","vfmsub231ss xmm2/m32, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W0 BB /r","V","V","FMA","","rw,r,r","","" +"VFMSUB231SS xmm1, {k}{z}, xmmV, xmm2/m32","VFMSUB231SS xmm2/m32, xmmV, {k}{z}, xmm1","vfmsub231ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W0 BB /r","V","V","AVX512F","scale4","rw,r,r,r","","" +"VFMSUBADD132PD xmm1, xmmV, xmm2/m128","VFMSUBADD132PD xmm2/m128, xmmV, xmm1","vfmsubadd132pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 97 /r","V","V","FMA","","rw,r,r","","" +"VFMSUBADD132PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFMSUBADD132PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfmsubadd132pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 97 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFMSUBADD132PD ymm1, ymmV, ymm2/m256","VFMSUBADD132PD ymm2/m256, ymmV, ymm1","vfmsubadd132pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 97 /r","V","V","FMA","","rw,r,r","","" +"VFMSUBADD132PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFMSUBADD132PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfmsubadd132pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 97 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFMSUBADD132PD zmm1{er}, {k}{z}, zmmV, zmm2","VFMSUBADD132PD zmm2, zmmV, {k}{z}, zmm1{er}","vfmsubadd132pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 97 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUBADD132PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFMSUBADD132PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfmsubadd132pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 97 /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFMSUBADD132PS xmm1, xmmV, xmm2/m128","VFMSUBADD132PS xmm2/m128, xmmV, xmm1","vfmsubadd132ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 97 /r","V","V","FMA","","rw,r,r","","" +"VFMSUBADD132PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFMSUBADD132PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfmsubadd132ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 97 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFMSUBADD132PS ymm1, ymmV, ymm2/m256","VFMSUBADD132PS ymm2/m256, ymmV, ymm1","vfmsubadd132ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 97 /r","V","V","FMA","","rw,r,r","","" +"VFMSUBADD132PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFMSUBADD132PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfmsubadd132ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 97 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFMSUBADD132PS zmm1{er}, {k}{z}, zmmV, zmm2","VFMSUBADD132PS zmm2, zmmV, {k}{z}, zmm1{er}","vfmsubadd132ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 97 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUBADD132PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFMSUBADD132PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfmsubadd132ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 97 /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFMSUBADD213PD xmm1, xmmV, xmm2/m128","VFMSUBADD213PD xmm2/m128, xmmV, xmm1","vfmsubadd213pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 A7 /r","V","V","FMA","","rw,r,r","","" +"VFMSUBADD213PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFMSUBADD213PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfmsubadd213pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 A7 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFMSUBADD213PD ymm1, ymmV, ymm2/m256","VFMSUBADD213PD ymm2/m256, ymmV, ymm1","vfmsubadd213pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 A7 /r","V","V","FMA","","rw,r,r","","" +"VFMSUBADD213PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFMSUBADD213PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfmsubadd213pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 A7 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFMSUBADD213PD zmm1{er}, {k}{z}, zmmV, zmm2","VFMSUBADD213PD zmm2, zmmV, {k}{z}, zmm1{er}","vfmsubadd213pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 A7 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUBADD213PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFMSUBADD213PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfmsubadd213pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 A7 /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFMSUBADD213PS xmm1, xmmV, xmm2/m128","VFMSUBADD213PS xmm2/m128, xmmV, xmm1","vfmsubadd213ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 A7 /r","V","V","FMA","","rw,r,r","","" +"VFMSUBADD213PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFMSUBADD213PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfmsubadd213ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 A7 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFMSUBADD213PS ymm1, ymmV, ymm2/m256","VFMSUBADD213PS ymm2/m256, ymmV, ymm1","vfmsubadd213ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 A7 /r","V","V","FMA","","rw,r,r","","" +"VFMSUBADD213PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFMSUBADD213PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfmsubadd213ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 A7 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFMSUBADD213PS zmm1{er}, {k}{z}, zmmV, zmm2","VFMSUBADD213PS zmm2, zmmV, {k}{z}, zmm1{er}","vfmsubadd213ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 A7 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUBADD213PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFMSUBADD213PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfmsubadd213ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 A7 /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFMSUBADD231PD xmm1, xmmV, xmm2/m128","VFMSUBADD231PD xmm2/m128, xmmV, xmm1","vfmsubadd231pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 B7 /r","V","V","FMA","","rw,r,r","","" +"VFMSUBADD231PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFMSUBADD231PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfmsubadd231pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 B7 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFMSUBADD231PD ymm1, ymmV, ymm2/m256","VFMSUBADD231PD ymm2/m256, ymmV, ymm1","vfmsubadd231pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 B7 /r","V","V","FMA","","rw,r,r","","" +"VFMSUBADD231PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFMSUBADD231PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfmsubadd231pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 B7 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFMSUBADD231PD zmm1{er}, {k}{z}, zmmV, zmm2","VFMSUBADD231PD zmm2, zmmV, {k}{z}, zmm1{er}","vfmsubadd231pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 B7 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUBADD231PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFMSUBADD231PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfmsubadd231pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 B7 /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFMSUBADD231PS xmm1, xmmV, xmm2/m128","VFMSUBADD231PS xmm2/m128, xmmV, xmm1","vfmsubadd231ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 B7 /r","V","V","FMA","","rw,r,r","","" +"VFMSUBADD231PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFMSUBADD231PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfmsubadd231ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 B7 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFMSUBADD231PS ymm1, ymmV, ymm2/m256","VFMSUBADD231PS ymm2/m256, ymmV, ymm1","vfmsubadd231ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 B7 /r","V","V","FMA","","rw,r,r","","" +"VFMSUBADD231PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFMSUBADD231PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfmsubadd231ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 B7 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFMSUBADD231PS zmm1{er}, {k}{z}, zmmV, zmm2","VFMSUBADD231PS zmm2, zmmV, {k}{z}, zmm1{er}","vfmsubadd231ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 B7 /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFMSUBADD231PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFMSUBADD231PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfmsubadd231ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 B7 /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFMSUBADDPD xmm1, xmmV, xmmIH, xmm2/m128","VFMSUBADDPD xmm2/m128, xmmIH, xmmV, xmm1","vfmsubaddpd xmm2/m128, xmmIH, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 5F /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBADDPD xmm1, xmmV, xmm2/m128, xmmIH","VFMSUBADDPD xmmIH, xmm2/m128, xmmV, xmm1","vfmsubaddpd xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 5F /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBADDPD ymm1, ymmV, ymmIH, ymm2/m256","VFMSUBADDPD ymm2/m256, ymmIH, ymmV, ymm1","vfmsubaddpd ymm2/m256, ymmIH, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 5F /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBADDPD ymm1, ymmV, ymm2/m256, ymmIH","VFMSUBADDPD ymmIH, ymm2/m256, ymmV, ymm1","vfmsubaddpd ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 5F /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBADDPS xmm1, xmmV, xmmIH, xmm2/m128","VFMSUBADDPS xmm2/m128, xmmIH, xmmV, xmm1","vfmsubaddps xmm2/m128, xmmIH, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 5E /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBADDPS xmm1, xmmV, xmm2/m128, xmmIH","VFMSUBADDPS xmmIH, xmm2/m128, xmmV, xmm1","vfmsubaddps xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 5E /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBADDPS ymm1, ymmV, ymmIH, ymm2/m256","VFMSUBADDPS ymm2/m256, ymmIH, ymmV, ymm1","vfmsubaddps ymm2/m256, ymmIH, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 5E /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBADDPS ymm1, ymmV, ymm2/m256, ymmIH","VFMSUBADDPS ymmIH, ymm2/m256, ymmV, ymm1","vfmsubaddps ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 5E /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBPD xmm1, xmmV, xmmIH, xmm2/m128","VFMSUBPD xmm2/m128, xmmIH, xmmV, xmm1","vfmsubpd xmm2/m128, xmmIH, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 6D /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBPD xmm1, xmmV, xmm2/m128, xmmIH","VFMSUBPD xmmIH, xmm2/m128, xmmV, xmm1","vfmsubpd xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 6D /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBPD ymm1, ymmV, ymmIH, ymm2/m256","VFMSUBPD ymm2/m256, ymmIH, ymmV, ymm1","vfmsubpd ymm2/m256, ymmIH, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 6D /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBPD ymm1, ymmV, ymm2/m256, ymmIH","VFMSUBPD ymmIH, ymm2/m256, ymmV, ymm1","vfmsubpd ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 6D /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBPS xmm1, xmmV, xmmIH, xmm2/m128","VFMSUBPS xmm2/m128, xmmIH, xmmV, xmm1","vfmsubps xmm2/m128, xmmIH, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 6C /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBPS xmm1, xmmV, xmm2/m128, xmmIH","VFMSUBPS xmmIH, xmm2/m128, xmmV, xmm1","vfmsubps xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 6C /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBPS ymm1, ymmV, ymmIH, ymm2/m256","VFMSUBPS ymm2/m256, ymmIH, ymmV, ymm1","vfmsubps ymm2/m256, ymmIH, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 6C /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBPS ymm1, ymmV, ymm2/m256, ymmIH","VFMSUBPS ymmIH, ymm2/m256, ymmV, ymm1","vfmsubps ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 6C /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBSD xmm1, xmmV, xmmIH, xmm2/m64","VFMSUBSD xmm2/m64, xmmIH, xmmV, xmm1","vfmsubsd xmm2/m64, xmmIH, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W1 6F /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBSD xmm1, xmmV, xmm2/m64, xmmIH","VFMSUBSD xmmIH, xmm2/m64, xmmV, xmm1","vfmsubsd xmmIH, xmm2/m64, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W0 6F /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBSS xmm1, xmmV, xmmIH, xmm2/m32","VFMSUBSS xmm2/m32, xmmIH, xmmV, xmm1","vfmsubss xmm2/m32, xmmIH, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W1 6E /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFMSUBSS xmm1, xmmV, xmm2/m32, xmmIH","VFMSUBSS xmmIH, xmm2/m32, xmmV, xmm1","vfmsubss xmmIH, xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W0 6E /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMADD132PD xmm1, xmmV, xmm2/m128","VFNMADD132PD xmm2/m128, xmmV, xmm1","vfnmadd132pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 9C /r","V","V","FMA","","rw,r,r","","" +"VFNMADD132PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFNMADD132PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfnmadd132pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 9C /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFNMADD132PD ymm1, ymmV, ymm2/m256","VFNMADD132PD ymm2/m256, ymmV, ymm1","vfnmadd132pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 9C /r","V","V","FMA","","rw,r,r","","" +"VFNMADD132PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFNMADD132PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfnmadd132pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 9C /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFNMADD132PD zmm1{er}, {k}{z}, zmmV, zmm2","VFNMADD132PD zmm2, zmmV, {k}{z}, zmm1{er}","vfnmadd132pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 9C /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMADD132PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFNMADD132PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfnmadd132pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 9C /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFNMADD132PS xmm1, xmmV, xmm2/m128","VFNMADD132PS xmm2/m128, xmmV, xmm1","vfnmadd132ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 9C /r","V","V","FMA","","rw,r,r","","" +"VFNMADD132PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFNMADD132PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfnmadd132ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 9C /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFNMADD132PS ymm1, ymmV, ymm2/m256","VFNMADD132PS ymm2/m256, ymmV, ymm1","vfnmadd132ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 9C /r","V","V","FMA","","rw,r,r","","" +"VFNMADD132PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFNMADD132PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfnmadd132ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 9C /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFNMADD132PS zmm1{er}, {k}{z}, zmmV, zmm2","VFNMADD132PS zmm2, zmmV, {k}{z}, zmm1{er}","vfnmadd132ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 9C /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMADD132PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFNMADD132PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfnmadd132ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 9C /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFNMADD132SD xmm1{er}, {k}{z}, xmmV, xmm2","VFNMADD132SD xmm2, xmmV, {k}{z}, xmm1{er}","vfnmadd132sd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W1 9D /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMADD132SD xmm1, xmmV, xmm2/m64","VFNMADD132SD xmm2/m64, xmmV, xmm1","vfnmadd132sd xmm2/m64, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W1 9D /r","V","V","FMA","","rw,r,r","","" +"VFNMADD132SD xmm1, {k}{z}, xmmV, xmm2/m64","VFNMADD132SD xmm2/m64, xmmV, {k}{z}, xmm1","vfnmadd132sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W1 9D /r","V","V","AVX512F","scale8","rw,r,r,r","","" +"VFNMADD132SS xmm1{er}, {k}{z}, xmmV, xmm2","VFNMADD132SS xmm2, xmmV, {k}{z}, xmm1{er}","vfnmadd132ss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W0 9D /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMADD132SS xmm1, xmmV, xmm2/m32","VFNMADD132SS xmm2/m32, xmmV, xmm1","vfnmadd132ss xmm2/m32, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W0 9D /r","V","V","FMA","","rw,r,r","","" +"VFNMADD132SS xmm1, {k}{z}, xmmV, xmm2/m32","VFNMADD132SS xmm2/m32, xmmV, {k}{z}, xmm1","vfnmadd132ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W0 9D /r","V","V","AVX512F","scale4","rw,r,r,r","","" +"VFNMADD213PD xmm1, xmmV, xmm2/m128","VFNMADD213PD xmm2/m128, xmmV, xmm1","vfnmadd213pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 AC /r","V","V","FMA","","rw,r,r","","" +"VFNMADD213PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFNMADD213PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfnmadd213pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 AC /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFNMADD213PD ymm1, ymmV, ymm2/m256","VFNMADD213PD ymm2/m256, ymmV, ymm1","vfnmadd213pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 AC /r","V","V","FMA","","rw,r,r","","" +"VFNMADD213PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFNMADD213PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfnmadd213pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 AC /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFNMADD213PD zmm1{er}, {k}{z}, zmmV, zmm2","VFNMADD213PD zmm2, zmmV, {k}{z}, zmm1{er}","vfnmadd213pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 AC /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMADD213PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFNMADD213PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfnmadd213pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 AC /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFNMADD213PS xmm1, xmmV, xmm2/m128","VFNMADD213PS xmm2/m128, xmmV, xmm1","vfnmadd213ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 AC /r","V","V","FMA","","rw,r,r","","" +"VFNMADD213PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFNMADD213PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfnmadd213ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 AC /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFNMADD213PS ymm1, ymmV, ymm2/m256","VFNMADD213PS ymm2/m256, ymmV, ymm1","vfnmadd213ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 AC /r","V","V","FMA","","rw,r,r","","" +"VFNMADD213PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFNMADD213PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfnmadd213ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 AC /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFNMADD213PS zmm1{er}, {k}{z}, zmmV, zmm2","VFNMADD213PS zmm2, zmmV, {k}{z}, zmm1{er}","vfnmadd213ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 AC /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMADD213PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFNMADD213PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfnmadd213ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 AC /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFNMADD213SD xmm1{er}, {k}{z}, xmmV, xmm2","VFNMADD213SD xmm2, xmmV, {k}{z}, xmm1{er}","vfnmadd213sd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W1 AD /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMADD213SD xmm1, xmmV, xmm2/m64","VFNMADD213SD xmm2/m64, xmmV, xmm1","vfnmadd213sd xmm2/m64, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W1 AD /r","V","V","FMA","","rw,r,r","","" +"VFNMADD213SD xmm1, {k}{z}, xmmV, xmm2/m64","VFNMADD213SD xmm2/m64, xmmV, {k}{z}, xmm1","vfnmadd213sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W1 AD /r","V","V","AVX512F","scale8","rw,r,r,r","","" +"VFNMADD213SS xmm1{er}, {k}{z}, xmmV, xmm2","VFNMADD213SS xmm2, xmmV, {k}{z}, xmm1{er}","vfnmadd213ss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W0 AD /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMADD213SS xmm1, xmmV, xmm2/m32","VFNMADD213SS xmm2/m32, xmmV, xmm1","vfnmadd213ss xmm2/m32, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W0 AD /r","V","V","FMA","","rw,r,r","","" +"VFNMADD213SS xmm1, {k}{z}, xmmV, xmm2/m32","VFNMADD213SS xmm2/m32, xmmV, {k}{z}, xmm1","vfnmadd213ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W0 AD /r","V","V","AVX512F","scale4","rw,r,r,r","","" +"VFNMADD231PD xmm1, xmmV, xmm2/m128","VFNMADD231PD xmm2/m128, xmmV, xmm1","vfnmadd231pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 BC /r","V","V","FMA","","rw,r,r","","" +"VFNMADD231PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFNMADD231PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfnmadd231pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 BC /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFNMADD231PD ymm1, ymmV, ymm2/m256","VFNMADD231PD ymm2/m256, ymmV, ymm1","vfnmadd231pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 BC /r","V","V","FMA","","rw,r,r","","" +"VFNMADD231PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFNMADD231PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfnmadd231pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 BC /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFNMADD231PD zmm1{er}, {k}{z}, zmmV, zmm2","VFNMADD231PD zmm2, zmmV, {k}{z}, zmm1{er}","vfnmadd231pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 BC /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMADD231PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFNMADD231PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfnmadd231pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 BC /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFNMADD231PS xmm1, xmmV, xmm2/m128","VFNMADD231PS xmm2/m128, xmmV, xmm1","vfnmadd231ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 BC /r","V","V","FMA","","rw,r,r","","" +"VFNMADD231PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFNMADD231PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfnmadd231ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 BC /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFNMADD231PS ymm1, ymmV, ymm2/m256","VFNMADD231PS ymm2/m256, ymmV, ymm1","vfnmadd231ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 BC /r","V","V","FMA","","rw,r,r","","" +"VFNMADD231PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFNMADD231PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfnmadd231ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 BC /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFNMADD231PS zmm1{er}, {k}{z}, zmmV, zmm2","VFNMADD231PS zmm2, zmmV, {k}{z}, zmm1{er}","vfnmadd231ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 BC /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMADD231PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFNMADD231PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfnmadd231ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 BC /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFNMADD231SD xmm1{er}, {k}{z}, xmmV, xmm2","VFNMADD231SD xmm2, xmmV, {k}{z}, xmm1{er}","vfnmadd231sd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W1 BD /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMADD231SD xmm1, xmmV, xmm2/m64","VFNMADD231SD xmm2/m64, xmmV, xmm1","vfnmadd231sd xmm2/m64, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W1 BD /r","V","V","FMA","","rw,r,r","","" +"VFNMADD231SD xmm1, {k}{z}, xmmV, xmm2/m64","VFNMADD231SD xmm2/m64, xmmV, {k}{z}, xmm1","vfnmadd231sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W1 BD /r","V","V","AVX512F","scale8","rw,r,r,r","","" +"VFNMADD231SS xmm1{er}, {k}{z}, xmmV, xmm2","VFNMADD231SS xmm2, xmmV, {k}{z}, xmm1{er}","vfnmadd231ss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W0 BD /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMADD231SS xmm1, xmmV, xmm2/m32","VFNMADD231SS xmm2/m32, xmmV, xmm1","vfnmadd231ss xmm2/m32, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W0 BD /r","V","V","FMA","","rw,r,r","","" +"VFNMADD231SS xmm1, {k}{z}, xmmV, xmm2/m32","VFNMADD231SS xmm2/m32, xmmV, {k}{z}, xmm1","vfnmadd231ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W0 BD /r","V","V","AVX512F","scale4","rw,r,r,r","","" +"VFNMADDPD xmm1, xmmV, xmmIH, xmm2/m128","VFNMADDPD xmm2/m128, xmmIH, xmmV, xmm1","vfnmaddpd xmm2/m128, xmmIH, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 79 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMADDPD xmm1, xmmV, xmm2/m128, xmmIH","VFNMADDPD xmmIH, xmm2/m128, xmmV, xmm1","vfnmaddpd xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 79 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMADDPD ymm1, ymmV, ymmIH, ymm2/m256","VFNMADDPD ymm2/m256, ymmIH, ymmV, ymm1","vfnmaddpd ymm2/m256, ymmIH, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 79 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMADDPD ymm1, ymmV, ymm2/m256, ymmIH","VFNMADDPD ymmIH, ymm2/m256, ymmV, ymm1","vfnmaddpd ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 79 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMADDPS xmm1, xmmV, xmmIH, xmm2/m128","VFNMADDPS xmm2/m128, xmmIH, xmmV, xmm1","vfnmaddps xmm2/m128, xmmIH, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 78 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMADDPS xmm1, xmmV, xmm2/m128, xmmIH","VFNMADDPS xmmIH, xmm2/m128, xmmV, xmm1","vfnmaddps xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 78 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMADDPS ymm1, ymmV, ymmIH, ymm2/m256","VFNMADDPS ymm2/m256, ymmIH, ymmV, ymm1","vfnmaddps ymm2/m256, ymmIH, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 78 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMADDPS ymm1, ymmV, ymm2/m256, ymmIH","VFNMADDPS ymmIH, ymm2/m256, ymmV, ymm1","vfnmaddps ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 78 /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMADDSD xmm1, xmmV, xmmIH, xmm2/m64","VFNMADDSD xmm2/m64, xmmIH, xmmV, xmm1","vfnmaddsd xmm2/m64, xmmIH, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W1 7B /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMADDSD xmm1, xmmV, xmm2/m64, xmmIH","VFNMADDSD xmmIH, xmm2/m64, xmmV, xmm1","vfnmaddsd xmmIH, xmm2/m64, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W0 7B /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMADDSS xmm1, xmmV, xmmIH, xmm2/m32","VFNMADDSS xmm2/m32, xmmIH, xmmV, xmm1","vfnmaddss xmm2/m32, xmmIH, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W1 7A /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMADDSS xmm1, xmmV, xmm2/m32, xmmIH","VFNMADDSS xmmIH, xmm2/m32, xmmV, xmm1","vfnmaddss xmmIH, xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W0 7A /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMSUB132PD xmm1, xmmV, xmm2/m128","VFNMSUB132PD xmm2/m128, xmmV, xmm1","vfnmsub132pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 9E /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB132PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFNMSUB132PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfnmsub132pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 9E /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFNMSUB132PD ymm1, ymmV, ymm2/m256","VFNMSUB132PD ymm2/m256, ymmV, ymm1","vfnmsub132pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 9E /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB132PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFNMSUB132PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfnmsub132pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 9E /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFNMSUB132PD zmm1{er}, {k}{z}, zmmV, zmm2","VFNMSUB132PD zmm2, zmmV, {k}{z}, zmm1{er}","vfnmsub132pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 9E /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMSUB132PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFNMSUB132PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfnmsub132pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 9E /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFNMSUB132PS xmm1, xmmV, xmm2/m128","VFNMSUB132PS xmm2/m128, xmmV, xmm1","vfnmsub132ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 9E /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB132PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFNMSUB132PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfnmsub132ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 9E /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFNMSUB132PS ymm1, ymmV, ymm2/m256","VFNMSUB132PS ymm2/m256, ymmV, ymm1","vfnmsub132ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 9E /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB132PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFNMSUB132PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfnmsub132ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 9E /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFNMSUB132PS zmm1{er}, {k}{z}, zmmV, zmm2","VFNMSUB132PS zmm2, zmmV, {k}{z}, zmm1{er}","vfnmsub132ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 9E /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMSUB132PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFNMSUB132PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfnmsub132ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 9E /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFNMSUB132SD xmm1{er}, {k}{z}, xmmV, xmm2","VFNMSUB132SD xmm2, xmmV, {k}{z}, xmm1{er}","vfnmsub132sd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W1 9F /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMSUB132SD xmm1, xmmV, xmm2/m64","VFNMSUB132SD xmm2/m64, xmmV, xmm1","vfnmsub132sd xmm2/m64, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W1 9F /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB132SD xmm1, {k}{z}, xmmV, xmm2/m64","VFNMSUB132SD xmm2/m64, xmmV, {k}{z}, xmm1","vfnmsub132sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W1 9F /r","V","V","AVX512F","scale8","rw,r,r,r","","" +"VFNMSUB132SS xmm1{er}, {k}{z}, xmmV, xmm2","VFNMSUB132SS xmm2, xmmV, {k}{z}, xmm1{er}","vfnmsub132ss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W0 9F /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMSUB132SS xmm1, xmmV, xmm2/m32","VFNMSUB132SS xmm2/m32, xmmV, xmm1","vfnmsub132ss xmm2/m32, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W0 9F /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB132SS xmm1, {k}{z}, xmmV, xmm2/m32","VFNMSUB132SS xmm2/m32, xmmV, {k}{z}, xmm1","vfnmsub132ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W0 9F /r","V","V","AVX512F","scale4","rw,r,r,r","","" +"VFNMSUB213PD xmm1, xmmV, xmm2/m128","VFNMSUB213PD xmm2/m128, xmmV, xmm1","vfnmsub213pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 AE /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB213PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFNMSUB213PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfnmsub213pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 AE /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFNMSUB213PD ymm1, ymmV, ymm2/m256","VFNMSUB213PD ymm2/m256, ymmV, ymm1","vfnmsub213pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 AE /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB213PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFNMSUB213PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfnmsub213pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 AE /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFNMSUB213PD zmm1{er}, {k}{z}, zmmV, zmm2","VFNMSUB213PD zmm2, zmmV, {k}{z}, zmm1{er}","vfnmsub213pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 AE /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMSUB213PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFNMSUB213PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfnmsub213pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 AE /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFNMSUB213PS xmm1, xmmV, xmm2/m128","VFNMSUB213PS xmm2/m128, xmmV, xmm1","vfnmsub213ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 AE /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB213PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFNMSUB213PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfnmsub213ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 AE /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFNMSUB213PS ymm1, ymmV, ymm2/m256","VFNMSUB213PS ymm2/m256, ymmV, ymm1","vfnmsub213ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 AE /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB213PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFNMSUB213PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfnmsub213ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 AE /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFNMSUB213PS zmm1{er}, {k}{z}, zmmV, zmm2","VFNMSUB213PS zmm2, zmmV, {k}{z}, zmm1{er}","vfnmsub213ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 AE /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMSUB213PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFNMSUB213PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfnmsub213ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 AE /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFNMSUB213SD xmm1{er}, {k}{z}, xmmV, xmm2","VFNMSUB213SD xmm2, xmmV, {k}{z}, xmm1{er}","vfnmsub213sd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W1 AF /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMSUB213SD xmm1, xmmV, xmm2/m64","VFNMSUB213SD xmm2/m64, xmmV, xmm1","vfnmsub213sd xmm2/m64, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W1 AF /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB213SD xmm1, {k}{z}, xmmV, xmm2/m64","VFNMSUB213SD xmm2/m64, xmmV, {k}{z}, xmm1","vfnmsub213sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W1 AF /r","V","V","AVX512F","scale8","rw,r,r,r","","" +"VFNMSUB213SS xmm1{er}, {k}{z}, xmmV, xmm2","VFNMSUB213SS xmm2, xmmV, {k}{z}, xmm1{er}","vfnmsub213ss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W0 AF /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMSUB213SS xmm1, xmmV, xmm2/m32","VFNMSUB213SS xmm2/m32, xmmV, xmm1","vfnmsub213ss xmm2/m32, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W0 AF /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB213SS xmm1, {k}{z}, xmmV, xmm2/m32","VFNMSUB213SS xmm2/m32, xmmV, {k}{z}, xmm1","vfnmsub213ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W0 AF /r","V","V","AVX512F","scale4","rw,r,r,r","","" +"VFNMSUB231PD xmm1, xmmV, xmm2/m128","VFNMSUB231PD xmm2/m128, xmmV, xmm1","vfnmsub231pd xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W1 BE /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB231PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VFNMSUB231PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vfnmsub231pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 BE /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VFNMSUB231PD ymm1, ymmV, ymm2/m256","VFNMSUB231PD ymm2/m256, ymmV, ymm1","vfnmsub231pd ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W1 BE /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB231PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VFNMSUB231PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vfnmsub231pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 BE /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VFNMSUB231PD zmm1{er}, {k}{z}, zmmV, zmm2","VFNMSUB231PD zmm2, zmmV, {k}{z}, zmm1{er}","vfnmsub231pd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W1 BE /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMSUB231PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VFNMSUB231PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vfnmsub231pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 BE /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VFNMSUB231PS xmm1, xmmV, xmm2/m128","VFNMSUB231PS xmm2/m128, xmmV, xmm1","vfnmsub231ps xmm2/m128, xmmV, xmm1","VEX.DDS.128.66.0F38.W0 BE /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB231PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VFNMSUB231PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vfnmsub231ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 BE /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VFNMSUB231PS ymm1, ymmV, ymm2/m256","VFNMSUB231PS ymm2/m256, ymmV, ymm1","vfnmsub231ps ymm2/m256, ymmV, ymm1","VEX.DDS.256.66.0F38.W0 BE /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB231PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VFNMSUB231PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vfnmsub231ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 BE /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VFNMSUB231PS zmm1{er}, {k}{z}, zmmV, zmm2","VFNMSUB231PS zmm2, zmmV, {k}{z}, zmm1{er}","vfnmsub231ps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.DDS.512.66.0F38.W0 BE /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMSUB231PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VFNMSUB231PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vfnmsub231ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 BE /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VFNMSUB231SD xmm1{er}, {k}{z}, xmmV, xmm2","VFNMSUB231SD xmm2, xmmV, {k}{z}, xmm1{er}","vfnmsub231sd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W1 BF /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMSUB231SD xmm1, xmmV, xmm2/m64","VFNMSUB231SD xmm2/m64, xmmV, xmm1","vfnmsub231sd xmm2/m64, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W1 BF /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB231SD xmm1, {k}{z}, xmmV, xmm2/m64","VFNMSUB231SD xmm2/m64, xmmV, {k}{z}, xmm1","vfnmsub231sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W1 BF /r","V","V","AVX512F","scale8","rw,r,r,r","","" +"VFNMSUB231SS xmm1{er}, {k}{z}, xmmV, xmm2","VFNMSUB231SS xmm2, xmmV, {k}{z}, xmm1{er}","vfnmsub231ss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.DDS.128.66.0F38.W0 BF /r","V","V","AVX512F","modrm_regonly","rw,r,r,r","","" +"VFNMSUB231SS xmm1, xmmV, xmm2/m32","VFNMSUB231SS xmm2/m32, xmmV, xmm1","vfnmsub231ss xmm2/m32, xmmV, xmm1","VEX.DDS.LIG.66.0F38.W0 BF /r","V","V","FMA","","rw,r,r","","" +"VFNMSUB231SS xmm1, {k}{z}, xmmV, xmm2/m32","VFNMSUB231SS xmm2/m32, xmmV, {k}{z}, xmm1","vfnmsub231ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.DDS.LIG.66.0F38.W0 BF /r","V","V","AVX512F","scale4","rw,r,r,r","","" +"VFNMSUBPD xmm1, xmmV, xmmIH, xmm2/m128","VFNMSUBPD xmm2/m128, xmmIH, xmmV, xmm1","vfnmsubpd xmm2/m128, xmmIH, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 7D /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMSUBPD xmm1, xmmV, xmm2/m128, xmmIH","VFNMSUBPD xmmIH, xmm2/m128, xmmV, xmm1","vfnmsubpd xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 7D /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMSUBPD ymm1, ymmV, ymmIH, ymm2/m256","VFNMSUBPD ymm2/m256, ymmIH, ymmV, ymm1","vfnmsubpd ymm2/m256, ymmIH, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 7D /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMSUBPD ymm1, ymmV, ymm2/m256, ymmIH","VFNMSUBPD ymmIH, ymm2/m256, ymmV, ymm1","vfnmsubpd ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 7D /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMSUBPS xmm1, xmmV, xmmIH, xmm2/m128","VFNMSUBPS xmm2/m128, xmmIH, xmmV, xmm1","vfnmsubps xmm2/m128, xmmIH, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 7C /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMSUBPS xmm1, xmmV, xmm2/m128, xmmIH","VFNMSUBPS xmmIH, xmm2/m128, xmmV, xmm1","vfnmsubps xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 7C /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMSUBPS ymm1, ymmV, ymmIH, ymm2/m256","VFNMSUBPS ymm2/m256, ymmIH, ymmV, ymm1","vfnmsubps ymm2/m256, ymmIH, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 7C /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMSUBPS ymm1, ymmV, ymm2/m256, ymmIH","VFNMSUBPS ymmIH, ymm2/m256, ymmV, ymm1","vfnmsubps ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 7C /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMSUBSD xmm1, xmmV, xmmIH, xmm2/m64","VFNMSUBSD xmm2/m64, xmmIH, xmmV, xmm1","vfnmsubsd xmm2/m64, xmmIH, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W1 7F /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMSUBSD xmm1, xmmV, xmm2/m64, xmmIH","VFNMSUBSD xmmIH, xmm2/m64, xmmV, xmm1","vfnmsubsd xmmIH, xmm2/m64, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W0 7F /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMSUBSS xmm1, xmmV, xmmIH, xmm2/m32","VFNMSUBSS xmm2/m32, xmmIH, xmmV, xmm1","vfnmsubss xmm2/m32, xmmIH, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W1 7E /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFNMSUBSS xmm1, xmmV, xmm2/m32, xmmIH","VFNMSUBSS xmmIH, xmm2/m32, xmmV, xmm1","vfnmsubss xmmIH, xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.W0 7E /r /is4","V","V","FMA4","amd","w,r,r,r","","" +"VFPCLASSPD k1, {k}, xmm2/m128/m64bcst, imm8u","VFPCLASSPDX imm8u, xmm2/m128/m64bcst, {k}, k1","vfpclasspdx imm8u, xmm2/m128/m64bcst, {k}, k1","EVEX.128.66.0F3A.W1 66 /r ib","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r,r","Y","128" +"VFPCLASSPD k1, {k}, ymm2/m256/m64bcst, imm8u","VFPCLASSPDY imm8u, ymm2/m256/m64bcst, {k}, k1","vfpclasspdy imm8u, ymm2/m256/m64bcst, {k}, k1","EVEX.256.66.0F3A.W1 66 /r ib","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r,r","Y","256" +"VFPCLASSPD k1, {k}, zmm2/m512/m64bcst, imm8u","VFPCLASSPDZ imm8u, zmm2/m512/m64bcst, {k}, k1","vfpclasspdz imm8u, zmm2/m512/m64bcst, {k}, k1","EVEX.512.66.0F3A.W1 66 /r ib","V","V","AVX512DQ","bscale8,scale64","w,r,r,r","Y","512" +"VFPCLASSPS k1, {k}, xmm2/m128/m32bcst, imm8u","VFPCLASSPSX imm8u, xmm2/m128/m32bcst, {k}, k1","vfpclasspsx imm8u, xmm2/m128/m32bcst, {k}, k1","EVEX.128.66.0F3A.W0 66 /r ib","V","V","AVX512DQ+AVX512VL","bscale4,scale16","w,r,r,r","Y","128" +"VFPCLASSPS k1, {k}, ymm2/m256/m32bcst, imm8u","VFPCLASSPSY imm8u, ymm2/m256/m32bcst, {k}, k1","vfpclasspsy imm8u, ymm2/m256/m32bcst, {k}, k1","EVEX.256.66.0F3A.W0 66 /r ib","V","V","AVX512DQ+AVX512VL","bscale4,scale32","w,r,r,r","Y","256" +"VFPCLASSPS k1, {k}, zmm2/m512/m32bcst, imm8u","VFPCLASSPSZ imm8u, zmm2/m512/m32bcst, {k}, k1","vfpclasspsz imm8u, zmm2/m512/m32bcst, {k}, k1","EVEX.512.66.0F3A.W0 66 /r ib","V","V","AVX512DQ","bscale4,scale64","w,r,r,r","Y","512" +"VFPCLASSSD k1, {k}, xmm2/m64, imm8u","VFPCLASSSD imm8u, xmm2/m64, {k}, k1","vfpclasssd imm8u, xmm2/m64, {k}, k1","EVEX.LIG.66.0F3A.W1 67 /r ib","V","V","AVX512DQ","scale8","w,r,r,r","","" +"VFPCLASSSS k1, {k}, xmm2/m32, imm8u","VFPCLASSSS imm8u, xmm2/m32, {k}, k1","vfpclassss imm8u, xmm2/m32, {k}, k1","EVEX.LIG.66.0F3A.W0 67 /r ib","V","V","AVX512DQ","scale4","w,r,r,r","","" +"VFRCZPD xmm1, xmm2/m128","VFRCZPD xmm2/m128, xmm1","vfrczpd xmm2/m128, xmm1","XOP.128.09.W0 81 /r","V","V","XOP","amd","w,r","","" +"VFRCZPD ymm1, ymm2/m256","VFRCZPD ymm2/m256, ymm1","vfrczpd ymm2/m256, ymm1","XOP.256.09.W0 81 /r","V","V","XOP","amd","w,r","","" +"VFRCZPS xmm1, xmm2/m128","VFRCZPS xmm2/m128, xmm1","vfrczps xmm2/m128, xmm1","XOP.128.09.W0 80 /r","V","V","XOP","amd","w,r","","" +"VFRCZPS ymm1, ymm2/m256","VFRCZPS ymm2/m256, ymm1","vfrczps ymm2/m256, ymm1","XOP.256.09.W0 80 /r","V","V","XOP","amd","w,r","","" +"VFRCZSD xmm1, xmm2/m64","VFRCZSD xmm2/m64, xmm1","vfrczsd xmm2/m64, xmm1","XOP.128.09.W0 83 /r","V","V","XOP","amd","w,r","","" +"VFRCZSS xmm1, xmm2/m32","VFRCZSS xmm2/m32, xmm1","vfrczss xmm2/m32, xmm1","XOP.128.09.W0 82 /r","V","V","XOP","amd","w,r","","" +"VGATHERDPD xmm1, {k1-k7}, vm32x","VGATHERDPD vm32x, {k1-k7}, xmm1","vgatherdpd vm32x, {k1-k7}, xmm1","EVEX.128.66.0F38.W1 92 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VGATHERDPD ymm1, {k1-k7}, vm32x","VGATHERDPD vm32x, {k1-k7}, ymm1","vgatherdpd vm32x, {k1-k7}, ymm1","EVEX.256.66.0F38.W1 92 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VGATHERDPD zmm1, {k1-k7}, vm32y","VGATHERDPD vm32y, {k1-k7}, zmm1","vgatherdpd vm32y, {k1-k7}, zmm1","EVEX.512.66.0F38.W1 92 /vsib","V","V","AVX512F","modrm_memonly,scale8","w,rw,r","","" +"VGATHERDPD xmm1, vm32x, xmmV","VGATHERDPD xmmV, vm32x, xmm1","vgatherdpd xmmV, vm32x, xmm1","VEX.DDS.128.66.0F38.W1 92 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VGATHERDPD ymm1, vm32x, ymmV","VGATHERDPD ymmV, vm32x, ymm1","vgatherdpd ymmV, vm32x, ymm1","VEX.DDS.256.66.0F38.W1 92 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VGATHERDPS xmm1, {k1-k7}, vm32x","VGATHERDPS vm32x, {k1-k7}, xmm1","vgatherdps vm32x, {k1-k7}, xmm1","EVEX.128.66.0F38.W0 92 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VGATHERDPS ymm1, {k1-k7}, vm32y","VGATHERDPS vm32y, {k1-k7}, ymm1","vgatherdps vm32y, {k1-k7}, ymm1","EVEX.256.66.0F38.W0 92 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VGATHERDPS zmm1, {k1-k7}, vm32z","VGATHERDPS vm32z, {k1-k7}, zmm1","vgatherdps vm32z, {k1-k7}, zmm1","EVEX.512.66.0F38.W0 92 /vsib","V","V","AVX512F","modrm_memonly,scale4","w,rw,r","","" +"VGATHERDPS xmm1, vm32x, xmmV","VGATHERDPS xmmV, vm32x, xmm1","vgatherdps xmmV, vm32x, xmm1","VEX.DDS.128.66.0F38.W0 92 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VGATHERDPS ymm1, vm32y, ymmV","VGATHERDPS ymmV, vm32y, ymm1","vgatherdps ymmV, vm32y, ymm1","VEX.DDS.256.66.0F38.W0 92 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VGATHERPF0DPD vm32y, {k1-k7}","VGATHERPF0DPD {k1-k7}, vm32y","vgatherpf0dpd {k1-k7}, vm32y","EVEX.512.66.0F38.W1 C6 /1","V","V","AVX512PF","modrm_memonly,scale8","r,rw","","" +"VGATHERPF0DPS vm32z, {k1-k7}","VGATHERPF0DPS {k1-k7}, vm32z","vgatherpf0dps {k1-k7}, vm32z","EVEX.512.66.0F38.W0 C6 /1","V","V","AVX512PF","modrm_memonly,scale4","r,rw","","" +"VGATHERPF0QPD vm64z, {k1-k7}","VGATHERPF0QPD {k1-k7}, vm64z","vgatherpf0qpd {k1-k7}, vm64z","EVEX.512.66.0F38.W1 C7 /1","V","V","AVX512PF","modrm_memonly,scale8","r,rw","","" +"VGATHERPF0QPS vm64z, {k1-k7}","VGATHERPF0QPS {k1-k7}, vm64z","vgatherpf0qps {k1-k7}, vm64z","EVEX.512.66.0F38.W0 C7 /1","V","V","AVX512PF","modrm_memonly,scale4","r,rw","","" +"VGATHERPF1DPD vm32y, {k1-k7}","VGATHERPF1DPD {k1-k7}, vm32y","vgatherpf1dpd {k1-k7}, vm32y","EVEX.512.66.0F38.W1 C6 /2","V","V","AVX512PF","modrm_memonly,scale8","r,rw","","" +"VGATHERPF1DPS vm32z, {k1-k7}","VGATHERPF1DPS {k1-k7}, vm32z","vgatherpf1dps {k1-k7}, vm32z","EVEX.512.66.0F38.W0 C6 /2","V","V","AVX512PF","modrm_memonly,scale4","r,rw","","" +"VGATHERPF1QPD vm64z, {k1-k7}","VGATHERPF1QPD {k1-k7}, vm64z","vgatherpf1qpd {k1-k7}, vm64z","EVEX.512.66.0F38.W1 C7 /2","V","V","AVX512PF","modrm_memonly,scale8","r,rw","","" +"VGATHERPF1QPS vm64z, {k1-k7}","VGATHERPF1QPS {k1-k7}, vm64z","vgatherpf1qps {k1-k7}, vm64z","EVEX.512.66.0F38.W0 C7 /2","V","V","AVX512PF","modrm_memonly,scale4","r,rw","","" +"VGATHERQPD xmm1, {k1-k7}, vm64x","VGATHERQPD vm64x, {k1-k7}, xmm1","vgatherqpd vm64x, {k1-k7}, xmm1","EVEX.128.66.0F38.W1 93 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VGATHERQPD ymm1, {k1-k7}, vm64y","VGATHERQPD vm64y, {k1-k7}, ymm1","vgatherqpd vm64y, {k1-k7}, ymm1","EVEX.256.66.0F38.W1 93 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VGATHERQPD zmm1, {k1-k7}, vm64z","VGATHERQPD vm64z, {k1-k7}, zmm1","vgatherqpd vm64z, {k1-k7}, zmm1","EVEX.512.66.0F38.W1 93 /vsib","V","V","AVX512F","modrm_memonly,scale8","w,rw,r","","" +"VGATHERQPD xmm1, vm64x, xmmV","VGATHERQPD xmmV, vm64x, xmm1","vgatherqpd xmmV, vm64x, xmm1","VEX.DDS.128.66.0F38.W1 93 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VGATHERQPD ymm1, vm64y, ymmV","VGATHERQPD ymmV, vm64y, ymm1","vgatherqpd ymmV, vm64y, ymm1","VEX.DDS.256.66.0F38.W1 93 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VGATHERQPS xmm1, {k1-k7}, vm64x","VGATHERQPS vm64x, {k1-k7}, xmm1","vgatherqps vm64x, {k1-k7}, xmm1","EVEX.128.66.0F38.W0 93 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VGATHERQPS xmm1, {k1-k7}, vm64y","VGATHERQPS vm64y, {k1-k7}, xmm1","vgatherqps vm64y, {k1-k7}, xmm1","EVEX.256.66.0F38.W0 93 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VGATHERQPS ymm1, {k1-k7}, vm64z","VGATHERQPS vm64z, {k1-k7}, ymm1","vgatherqps vm64z, {k1-k7}, ymm1","EVEX.512.66.0F38.W0 93 /vsib","V","V","AVX512F","modrm_memonly,scale4","w,rw,r","","" +"VGATHERQPS xmm1, vm64x, xmmV","VGATHERQPS xmmV, vm64x, xmm1","vgatherqps xmmV, vm64x, xmm1","VEX.DDS.128.66.0F38.W0 93 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VGATHERQPS xmm1, vm64y, xmmV","VGATHERQPS xmmV, vm64y, xmm1","vgatherqps xmmV, vm64y, xmm1","VEX.DDS.256.66.0F38.W0 93 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VGETEXPPD xmm1, {k}{z}, xmm2/m128/m64bcst","VGETEXPPD xmm2/m128/m64bcst, {k}{z}, xmm1","vgetexppd xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F38.W1 42 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r","","" +"VGETEXPPD ymm1, {k}{z}, ymm2/m256/m64bcst","VGETEXPPD ymm2/m256/m64bcst, {k}{z}, ymm1","vgetexppd ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F38.W1 42 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r","","" +"VGETEXPPD zmm1{sae}, {k}{z}, zmm2","VGETEXPPD zmm2, {k}{z}, zmm1{sae}","vgetexppd zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F38.W1 42 /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"VGETEXPPD zmm1, {k}{z}, zmm2/m512/m64bcst","VGETEXPPD zmm2/m512/m64bcst, {k}{z}, zmm1","vgetexppd zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W1 42 /r","V","V","AVX512F","bscale8,scale64","w,r,r","","" +"VGETEXPPS xmm1, {k}{z}, xmm2/m128/m32bcst","VGETEXPPS xmm2/m128/m32bcst, {k}{z}, xmm1","vgetexpps xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F38.W0 42 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r","","" +"VGETEXPPS ymm1, {k}{z}, ymm2/m256/m32bcst","VGETEXPPS ymm2/m256/m32bcst, {k}{z}, ymm1","vgetexpps ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F38.W0 42 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r","","" +"VGETEXPPS zmm1{sae}, {k}{z}, zmm2","VGETEXPPS zmm2, {k}{z}, zmm1{sae}","vgetexpps zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F38.W0 42 /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"VGETEXPPS zmm1, {k}{z}, zmm2/m512/m32bcst","VGETEXPPS zmm2/m512/m32bcst, {k}{z}, zmm1","vgetexpps zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W0 42 /r","V","V","AVX512F","bscale4,scale64","w,r,r","","" +"VGETEXPSD xmm1{sae}, {k}{z}, xmmV, xmm2","VGETEXPSD xmm2, xmmV, {k}{z}, xmm1{sae}","vgetexpsd xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.66.0F38.W1 43 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VGETEXPSD xmm1, {k}{z}, xmmV, xmm2/m64","VGETEXPSD xmm2/m64, xmmV, {k}{z}, xmm1","vgetexpsd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F38.W1 43 /r","V","V","AVX512F","scale8","w,r,r,r","","" +"VGETEXPSS xmm1{sae}, {k}{z}, xmmV, xmm2","VGETEXPSS xmm2, xmmV, {k}{z}, xmm1{sae}","vgetexpss xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.66.0F38.W0 43 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VGETEXPSS xmm1, {k}{z}, xmmV, xmm2/m32","VGETEXPSS xmm2/m32, xmmV, {k}{z}, xmm1","vgetexpss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F38.W0 43 /r","V","V","AVX512F","scale4","w,r,r,r","","" +"VGETMANTPD xmm1, {k}{z}, xmm2/m128/m64bcst, imm8u:4","VGETMANTPD imm8u:4, xmm2/m128/m64bcst, {k}{z}, xmm1","vgetmantpd imm8u:4, xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F3A.W1 26 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VGETMANTPD ymm1, {k}{z}, ymm2/m256/m64bcst, imm8u:4","VGETMANTPD imm8u:4, ymm2/m256/m64bcst, {k}{z}, ymm1","vgetmantpd imm8u:4, ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F3A.W1 26 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VGETMANTPD zmm1{sae}, {k}{z}, zmm2, imm8u:4","VGETMANTPD imm8u:4, zmm2, {k}{z}, zmm1{sae}","vgetmantpd imm8u:4, zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F3A.W1 26 /r ib","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VGETMANTPD zmm1, {k}{z}, zmm2/m512/m64bcst, imm8u:4","VGETMANTPD imm8u:4, zmm2/m512/m64bcst, {k}{z}, zmm1","vgetmantpd imm8u:4, zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F3A.W1 26 /r ib","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VGETMANTPS xmm1, {k}{z}, xmm2/m128/m32bcst, imm8u:4","VGETMANTPS imm8u:4, xmm2/m128/m32bcst, {k}{z}, xmm1","vgetmantps imm8u:4, xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F3A.W0 26 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VGETMANTPS ymm1, {k}{z}, ymm2/m256/m32bcst, imm8u:4","VGETMANTPS imm8u:4, ymm2/m256/m32bcst, {k}{z}, ymm1","vgetmantps imm8u:4, ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F3A.W0 26 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VGETMANTPS zmm1{sae}, {k}{z}, zmm2, imm8u:4","VGETMANTPS imm8u:4, zmm2, {k}{z}, zmm1{sae}","vgetmantps imm8u:4, zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F3A.W0 26 /r ib","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VGETMANTPS zmm1, {k}{z}, zmm2/m512/m32bcst, imm8u:4","VGETMANTPS imm8u:4, zmm2/m512/m32bcst, {k}{z}, zmm1","vgetmantps imm8u:4, zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F3A.W0 26 /r ib","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VGETMANTSD xmm1{sae}, {k}{z}, xmmV, xmm2, imm8u:4","VGETMANTSD imm8u:4, xmm2, xmmV, {k}{z}, xmm1{sae}","vgetmantsd imm8u:4, xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.66.0F3A.W1 27 /r ib","V","V","AVX512F","modrm_regonly","w,r,r,r,r","","" +"VGETMANTSD xmm1, {k}{z}, xmmV, xmm2/m64, imm8u:4","VGETMANTSD imm8u:4, xmm2/m64, xmmV, {k}{z}, xmm1","vgetmantsd imm8u:4, xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F3A.W1 27 /r ib","V","V","AVX512F","scale8","w,r,r,r,r","","" +"VGETMANTSS xmm1{sae}, {k}{z}, xmmV, xmm2, imm8u:4","VGETMANTSS imm8u:4, xmm2, xmmV, {k}{z}, xmm1{sae}","vgetmantss imm8u:4, xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.66.0F3A.W0 27 /r ib","V","V","AVX512F","modrm_regonly","w,r,r,r,r","","" +"VGETMANTSS xmm1, {k}{z}, xmmV, xmm2/m32, imm8u:4","VGETMANTSS imm8u:4, xmm2/m32, xmmV, {k}{z}, xmm1","vgetmantss imm8u:4, xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F3A.W0 27 /r ib","V","V","AVX512F","scale4","w,r,r,r,r","","" +"VGF2P8AFFINEINVQB xmm1, xmmV, xmm2/m128, imm8u","VGF2P8AFFINEINVQB imm8u, xmm2/m128, xmmV, xmm1","vgf2p8affineinvqb imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 CF /r ib","V","V","GFNI+AVX","","w,r,r,r","","" +"VGF2P8AFFINEINVQB xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst, imm8u","VGF2P8AFFINEINVQB imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vgf2p8affineinvqb imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F3A.W1 CF /r ib","V","V","GFNI+AVX512VL","bscale8,scale16","w,r,r,r,r","","" +"VGF2P8AFFINEINVQB ymm1, ymmV, ymm2/m256, imm8u","VGF2P8AFFINEINVQB imm8u, ymm2/m256, ymmV, ymm1","vgf2p8affineinvqb imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 CF /r ib","V","V","GFNI+AVX","","w,r,r,r","","" +"VGF2P8AFFINEINVQB ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst, imm8u","VGF2P8AFFINEINVQB imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vgf2p8affineinvqb imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W1 CF /r ib","V","V","GFNI+AVX512VL","bscale8,scale32","w,r,r,r,r","","" +"VGF2P8AFFINEINVQB zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst, imm8u","VGF2P8AFFINEINVQB imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vgf2p8affineinvqb imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W1 CF /r ib","V","V","GFNI+AVX512F","bscale8,scale64","w,r,r,r,r","","" +"VGF2P8AFFINEQB xmm1, xmmV, xmm2/m128, imm8u","VGF2P8AFFINEQB imm8u, xmm2/m128, xmmV, xmm1","vgf2p8affineqb imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 CE /r ib","V","V","GFNI+AVX","","w,r,r,r","","" +"VGF2P8AFFINEQB xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst, imm8u","VGF2P8AFFINEQB imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vgf2p8affineqb imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F3A.W1 CE /r ib","V","V","GFNI+AVX512VL","bscale8,scale16","w,r,r,r,r","","" +"VGF2P8AFFINEQB ymm1, ymmV, ymm2/m256, imm8u","VGF2P8AFFINEQB imm8u, ymm2/m256, ymmV, ymm1","vgf2p8affineqb imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 CE /r ib","V","V","GFNI+AVX","","w,r,r,r","","" +"VGF2P8AFFINEQB ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst, imm8u","VGF2P8AFFINEQB imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vgf2p8affineqb imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W1 CE /r ib","V","V","GFNI+AVX512VL","bscale8,scale32","w,r,r,r,r","","" +"VGF2P8AFFINEQB zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst, imm8u","VGF2P8AFFINEQB imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vgf2p8affineqb imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W1 CE /r ib","V","V","GFNI+AVX512F","bscale8,scale64","w,r,r,r,r","","" +"VGF2P8MULB xmm1, xmmV, xmm2/m128","VGF2P8MULB xmm2/m128, xmmV, xmm1","vgf2p8mulb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.W0 CF /r","V","V","GFNI+AVX","","w,r,r","","" +"VGF2P8MULB xmm1, {k}{z}, xmmV, xmm2/m128","VGF2P8MULB xmm2/m128, xmmV, {k}{z}, xmm1","vgf2p8mulb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 CF /r","V","V","GFNI+AVX512VL","scale16","w,r,r,r","","" +"VGF2P8MULB ymm1, ymmV, ymm2/m256","VGF2P8MULB ymm2/m256, ymmV, ymm1","vgf2p8mulb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.W0 CF /r","V","V","GFNI+AVX","","w,r,r","","" +"VGF2P8MULB ymm1, {k}{z}, ymmV, ymm2/m256","VGF2P8MULB ymm2/m256, ymmV, {k}{z}, ymm1","vgf2p8mulb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 CF /r","V","V","GFNI+AVX512VL","scale32","w,r,r,r","","" +"VGF2P8MULB zmm1, {k}{z}, zmmV, zmm2/m512","VGF2P8MULB zmm2/m512, zmmV, {k}{z}, zmm1","vgf2p8mulb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 CF /r","V","V","GFNI+AVX512F","scale64","w,r,r,r","","" +"VHADDPD xmm1, xmmV, xmm2/m128","VHADDPD xmm2/m128, xmmV, xmm1","vhaddpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 7C /r","V","V","AVX","","w,r,r","","" +"VHADDPD ymm1, ymmV, ymm2/m256","VHADDPD ymm2/m256, ymmV, ymm1","vhaddpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 7C /r","V","V","AVX","","w,r,r","","" +"VHADDPS xmm1, xmmV, xmm2/m128","VHADDPS xmm2/m128, xmmV, xmm1","vhaddps xmm2/m128, xmmV, xmm1","VEX.NDS.128.F2.0F.WIG 7C /r","V","V","AVX","","w,r,r","","" +"VHADDPS ymm1, ymmV, ymm2/m256","VHADDPS ymm2/m256, ymmV, ymm1","vhaddps ymm2/m256, ymmV, ymm1","VEX.NDS.256.F2.0F.WIG 7C /r","V","V","AVX","","w,r,r","","" +"VHSUBPD xmm1, xmmV, xmm2/m128","VHSUBPD xmm2/m128, xmmV, xmm1","vhsubpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 7D /r","V","V","AVX","","w,r,r","","" +"VHSUBPD ymm1, ymmV, ymm2/m256","VHSUBPD ymm2/m256, ymmV, ymm1","vhsubpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 7D /r","V","V","AVX","","w,r,r","","" +"VHSUBPS xmm1, xmmV, xmm2/m128","VHSUBPS xmm2/m128, xmmV, xmm1","vhsubps xmm2/m128, xmmV, xmm1","VEX.NDS.128.F2.0F.WIG 7D /r","V","V","AVX","","w,r,r","","" +"VHSUBPS ymm1, ymmV, ymm2/m256","VHSUBPS ymm2/m256, ymmV, ymm1","vhsubps ymm2/m256, ymmV, ymm1","VEX.NDS.256.F2.0F.WIG 7D /r","V","V","AVX","","w,r,r","","" +"VINSERTF128 ymm1, ymmV, xmm2/m128, imm8u:1","VINSERTF128 imm8u:1, xmm2/m128, ymmV, ymm1","vinsertf128 imm8u:1, xmm2/m128, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 18 /r ib","V","V","AVX","","w,r,r,r","","" +"VINSERTF32X4 ymm1, {k}{z}, ymmV, xmm2/m128, imm8u:1","VINSERTF32X4 imm8u:1, xmm2/m128, ymmV, {k}{z}, ymm1","vinsertf32x4 imm8u:1, xmm2/m128, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W0 18 /r ib","V","V","AVX512F+AVX512VL","scale16","w,r,r,r,r","","" +"VINSERTF32X4 zmm1, {k}{z}, zmmV, xmm2/m128, imm8u:2","VINSERTF32X4 imm8u:2, xmm2/m128, zmmV, {k}{z}, zmm1","vinsertf32x4 imm8u:2, xmm2/m128, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W0 18 /r ib","V","V","AVX512F","scale16","w,r,r,r,r","","" +"VINSERTF32X8 zmm1, {k}{z}, zmmV, ymm2/m256, imm8u:1","VINSERTF32X8 imm8u:1, ymm2/m256, zmmV, {k}{z}, zmm1","vinsertf32x8 imm8u:1, ymm2/m256, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W0 1A /r ib","V","V","AVX512DQ","scale32","w,r,r,r,r","","" +"VINSERTF64X2 ymm1, {k}{z}, ymmV, xmm2/m128, imm8u:1","VINSERTF64X2 imm8u:1, xmm2/m128, ymmV, {k}{z}, ymm1","vinsertf64x2 imm8u:1, xmm2/m128, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W1 18 /r ib","V","V","AVX512DQ+AVX512VL","scale16","w,r,r,r,r","","" +"VINSERTF64X2 zmm1, {k}{z}, zmmV, xmm2/m128, imm8u:2","VINSERTF64X2 imm8u:2, xmm2/m128, zmmV, {k}{z}, zmm1","vinsertf64x2 imm8u:2, xmm2/m128, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W1 18 /r ib","V","V","AVX512DQ","scale16","w,r,r,r,r","","" +"VINSERTF64X4 zmm1, {k}{z}, zmmV, ymm2/m256, imm8u:1","VINSERTF64X4 imm8u:1, ymm2/m256, zmmV, {k}{z}, zmm1","vinsertf64x4 imm8u:1, ymm2/m256, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W1 1A /r ib","V","V","AVX512F","scale32","w,r,r,r,r","","" +"VINSERTI128 ymm1, ymmV, xmm2/m128, imm8u:1","VINSERTI128 imm8u:1, xmm2/m128, ymmV, ymm1","vinserti128 imm8u:1, xmm2/m128, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 38 /r ib","V","V","AVX2","","w,r,r,r","","" +"VINSERTI32X4 ymm1, {k}{z}, ymmV, xmm2/m128, imm8u:1","VINSERTI32X4 imm8u:1, xmm2/m128, ymmV, {k}{z}, ymm1","vinserti32x4 imm8u:1, xmm2/m128, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W0 38 /r ib","V","V","AVX512F+AVX512VL","scale16","w,r,r,r,r","","" +"VINSERTI32X4 zmm1, {k}{z}, zmmV, xmm2/m128, imm8u:2","VINSERTI32X4 imm8u:2, xmm2/m128, zmmV, {k}{z}, zmm1","vinserti32x4 imm8u:2, xmm2/m128, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W0 38 /r ib","V","V","AVX512F","scale16","w,r,r,r,r","","" +"VINSERTI32X8 zmm1, {k}{z}, zmmV, ymm2/m256, imm8u:1","VINSERTI32X8 imm8u:1, ymm2/m256, zmmV, {k}{z}, zmm1","vinserti32x8 imm8u:1, ymm2/m256, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W0 3A /r ib","V","V","AVX512DQ","scale32","w,r,r,r,r","","" +"VINSERTI64X2 ymm1, {k}{z}, ymmV, xmm2/m128, imm8u:1","VINSERTI64X2 imm8u:1, xmm2/m128, ymmV, {k}{z}, ymm1","vinserti64x2 imm8u:1, xmm2/m128, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W1 38 /r ib","V","V","AVX512DQ+AVX512VL","scale16","w,r,r,r,r","","" +"VINSERTI64X2 zmm1, {k}{z}, zmmV, xmm2/m128, imm8u:2","VINSERTI64X2 imm8u:2, xmm2/m128, zmmV, {k}{z}, zmm1","vinserti64x2 imm8u:2, xmm2/m128, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W1 38 /r ib","V","V","AVX512DQ","scale16","w,r,r,r,r","","" +"VINSERTI64X4 zmm1, {k}{z}, zmmV, ymm2/m256, imm8u:1","VINSERTI64X4 imm8u:1, ymm2/m256, zmmV, {k}{z}, zmm1","vinserti64x4 imm8u:1, ymm2/m256, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W1 3A /r ib","V","V","AVX512F","scale32","w,r,r,r,r","","" +"VINSERTPS xmm1, xmmV, xmm2/m32, imm8u","VINSERTPS imm8u, xmm2/m32, xmmV, xmm1","vinsertps imm8u, xmm2/m32, xmmV, xmm1","EVEX.NDS.128.66.0F3A.W0 21 /r ib","V","V","AVX512F+AVX512VL","scale4","w,r,r,r","","" +"VINSERTPS xmm1, xmmV, xmm2/m32, imm8u","VINSERTPS imm8u, xmm2/m32, xmmV, xmm1","vinsertps imm8u, xmm2/m32, xmmV, xmm1","VEX.NDS.128.66.0F3A.WIG 21 /r ib","V","V","AVX","","w,r,r,r","","" +"VLDDQU xmm1, m128","VLDDQU m128, xmm1","vlddqu m128, xmm1","VEX.128.F2.0F.WIG F0 /r","V","V","AVX","modrm_memonly","w,r","","" +"VLDDQU ymm1, m256","VLDDQU m256, ymm1","vlddqu m256, ymm1","VEX.256.F2.0F.WIG F0 /r","V","V","AVX","modrm_memonly","w,r","","" +"VLDMXCSR m32","VLDMXCSR m32","vldmxcsr m32","VEX.128.0F.WIG AE /2","V","V","AVX","modrm_memonly","r","","" +"VMASKMOVDQU xmm1, xmm2","VMASKMOVDQU xmm2, xmm1","vmaskmovdqu xmm2, xmm1","VEX.128.66.0F.WIG F7 /r","V","V","AVX","modrm_regonly","r,r","","" +"VMASKMOVPD xmm1, xmmV, m128","VMASKMOVPD m128, xmmV, xmm1","vmaskmovpd m128, xmmV, xmm1","VEX.NDS.128.66.0F38.W0 2D /r","V","V","AVX","modrm_memonly","w,r,r","","" +"VMASKMOVPD ymm1, ymmV, m256","VMASKMOVPD m256, ymmV, ymm1","vmaskmovpd m256, ymmV, ymm1","VEX.NDS.256.66.0F38.W0 2D /r","V","V","AVX","modrm_memonly","w,r,r","","" +"VMASKMOVPD m128, xmmV, xmm1","VMASKMOVPD xmm1, xmmV, m128","vmaskmovpd xmm1, xmmV, m128","VEX.NDS.128.66.0F38.W0 2F /r","V","V","AVX","modrm_memonly","w,r,r","","" +"VMASKMOVPD m256, ymmV, ymm1","VMASKMOVPD ymm1, ymmV, m256","vmaskmovpd ymm1, ymmV, m256","VEX.NDS.256.66.0F38.W0 2F /r","V","V","AVX","modrm_memonly","w,r,r","","" +"VMASKMOVPS xmm1, xmmV, m128","VMASKMOVPS m128, xmmV, xmm1","vmaskmovps m128, xmmV, xmm1","VEX.NDS.128.66.0F38.W0 2C /r","V","V","AVX","modrm_memonly","w,r,r","","" +"VMASKMOVPS ymm1, ymmV, m256","VMASKMOVPS m256, ymmV, ymm1","vmaskmovps m256, ymmV, ymm1","VEX.NDS.256.66.0F38.W0 2C /r","V","V","AVX","modrm_memonly","w,r,r","","" +"VMASKMOVPS m128, xmmV, xmm1","VMASKMOVPS xmm1, xmmV, m128","vmaskmovps xmm1, xmmV, m128","VEX.NDS.128.66.0F38.W0 2E /r","V","V","AVX","modrm_memonly","w,r,r","","" +"VMASKMOVPS m256, ymmV, ymm1","VMASKMOVPS ymm1, ymmV, m256","vmaskmovps ymm1, ymmV, m256","VEX.NDS.256.66.0F38.W0 2E /r","V","V","AVX","modrm_memonly","w,r,r","","" +"VMAXPD xmm1, xmmV, xmm2/m128","VMAXPD xmm2/m128, xmmV, xmm1","vmaxpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 5F /r","V","V","AVX","","w,r,r","","" +"VMAXPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VMAXPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vmaxpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 5F /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VMAXPD ymm1, ymmV, ymm2/m256","VMAXPD ymm2/m256, ymmV, ymm1","vmaxpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 5F /r","V","V","AVX","","w,r,r","","" +"VMAXPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VMAXPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vmaxpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 5F /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VMAXPD zmm1{sae}, {k}{z}, zmmV, zmm2","VMAXPD zmm2, zmmV, {k}{z}, zmm1{sae}","vmaxpd zmm2, zmmV, {k}{z}, zmm1{sae}","EVEX.NDS.512.66.0F.W1 5F /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMAXPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VMAXPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vmaxpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 5F /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VMAXPS xmm1, xmmV, xmm2/m128","VMAXPS xmm2/m128, xmmV, xmm1","vmaxps xmm2/m128, xmmV, xmm1","VEX.NDS.128.0F.WIG 5F /r","V","V","AVX","","w,r,r","","" +"VMAXPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VMAXPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vmaxps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.0F.W0 5F /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VMAXPS ymm1, ymmV, ymm2/m256","VMAXPS ymm2/m256, ymmV, ymm1","vmaxps ymm2/m256, ymmV, ymm1","VEX.NDS.256.0F.WIG 5F /r","V","V","AVX","","w,r,r","","" +"VMAXPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VMAXPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vmaxps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.0F.W0 5F /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VMAXPS zmm1{sae}, {k}{z}, zmmV, zmm2","VMAXPS zmm2, zmmV, {k}{z}, zmm1{sae}","vmaxps zmm2, zmmV, {k}{z}, zmm1{sae}","EVEX.NDS.512.0F.W0 5F /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMAXPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VMAXPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vmaxps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.0F.W0 5F /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VMAXSD xmm1{sae}, {k}{z}, xmmV, xmm2","VMAXSD xmm2, xmmV, {k}{z}, xmm1{sae}","vmaxsd xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.F2.0F.W1 5F /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMAXSD xmm1, xmmV, xmm2/m64","VMAXSD xmm2/m64, xmmV, xmm1","vmaxsd xmm2/m64, xmmV, xmm1","VEX.NDS.LIG.F2.0F.WIG 5F /r","V","V","AVX","","w,r,r","","" +"VMAXSD xmm1, {k}{z}, xmmV, xmm2/m64","VMAXSD xmm2/m64, xmmV, {k}{z}, xmm1","vmaxsd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F2.0F.W1 5F /r","V","V","AVX512F","scale8","w,r,r,r","","" +"VMAXSS xmm1{sae}, {k}{z}, xmmV, xmm2","VMAXSS xmm2, xmmV, {k}{z}, xmm1{sae}","vmaxss xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.F3.0F.W0 5F /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMAXSS xmm1, xmmV, xmm2/m32","VMAXSS xmm2/m32, xmmV, xmm1","vmaxss xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.F3.0F.WIG 5F /r","V","V","AVX","","w,r,r","","" +"VMAXSS xmm1, {k}{z}, xmmV, xmm2/m32","VMAXSS xmm2/m32, xmmV, {k}{z}, xmm1","vmaxss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F3.0F.W0 5F /r","V","V","AVX512F","scale4","w,r,r,r","","" +"VMCALL","VMCALL","vmcall","0F 01 C1","V","V","VTX","","","","" +"VMCLEAR m64","VMCLEAR m64","vmclear m64","66 0F C7 /6","V","V","VTX","modrm_memonly","r","","" +"VMFUNC","VMFUNC","vmfunc","0F 01 D4","V","V","","","","","" +"VMINPD xmm1, xmmV, xmm2/m128","VMINPD xmm2/m128, xmmV, xmm1","vminpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 5D /r","V","V","AVX","","w,r,r","","" +"VMINPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VMINPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vminpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 5D /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VMINPD ymm1, ymmV, ymm2/m256","VMINPD ymm2/m256, ymmV, ymm1","vminpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 5D /r","V","V","AVX","","w,r,r","","" +"VMINPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VMINPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vminpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 5D /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VMINPD zmm1{sae}, {k}{z}, zmmV, zmm2","VMINPD zmm2, zmmV, {k}{z}, zmm1{sae}","vminpd zmm2, zmmV, {k}{z}, zmm1{sae}","EVEX.NDS.512.66.0F.W1 5D /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMINPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VMINPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vminpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 5D /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VMINPS xmm1, xmmV, xmm2/m128","VMINPS xmm2/m128, xmmV, xmm1","vminps xmm2/m128, xmmV, xmm1","VEX.NDS.128.0F.WIG 5D /r","V","V","AVX","","w,r,r","","" +"VMINPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VMINPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vminps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.0F.W0 5D /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VMINPS ymm1, ymmV, ymm2/m256","VMINPS ymm2/m256, ymmV, ymm1","vminps ymm2/m256, ymmV, ymm1","VEX.NDS.256.0F.WIG 5D /r","V","V","AVX","","w,r,r","","" +"VMINPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VMINPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vminps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.0F.W0 5D /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VMINPS zmm1{sae}, {k}{z}, zmmV, zmm2","VMINPS zmm2, zmmV, {k}{z}, zmm1{sae}","vminps zmm2, zmmV, {k}{z}, zmm1{sae}","EVEX.NDS.512.0F.W0 5D /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMINPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VMINPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vminps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.0F.W0 5D /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VMINSD xmm1{sae}, {k}{z}, xmmV, xmm2","VMINSD xmm2, xmmV, {k}{z}, xmm1{sae}","vminsd xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.F2.0F.W1 5D /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMINSD xmm1, xmmV, xmm2/m64","VMINSD xmm2/m64, xmmV, xmm1","vminsd xmm2/m64, xmmV, xmm1","VEX.NDS.LIG.F2.0F.WIG 5D /r","V","V","AVX","","w,r,r","","" +"VMINSD xmm1, {k}{z}, xmmV, xmm2/m64","VMINSD xmm2/m64, xmmV, {k}{z}, xmm1","vminsd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F2.0F.W1 5D /r","V","V","AVX512F","scale8","w,r,r,r","","" +"VMINSS xmm1{sae}, {k}{z}, xmmV, xmm2","VMINSS xmm2, xmmV, {k}{z}, xmm1{sae}","vminss xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.F3.0F.W0 5D /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMINSS xmm1, xmmV, xmm2/m32","VMINSS xmm2/m32, xmmV, xmm1","vminss xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.F3.0F.WIG 5D /r","V","V","AVX","","w,r,r","","" +"VMINSS xmm1, {k}{z}, xmmV, xmm2/m32","VMINSS xmm2/m32, xmmV, {k}{z}, xmm1","vminss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F3.0F.W0 5D /r","V","V","AVX512F","scale4","w,r,r,r","","" +"VMLAUNCH","VMLAUNCH","vmlaunch","0F 01 C2","V","V","VTX","","","","" +"VMLOAD EAX","VMLOADL EAX","vmloadl EAX","0F 01 DA","V","V","SVM","amd,modrm_regonly,operand32","r","Y","32" +"VMLOAD RAX","VMLOADQ RAX","vmloadq RAX","REX.W 0F 01 DA","N.S.","V","SVM","amd,modrm_regonly","r","Y","64" +"VMLOAD AX","VMLOADW AX","vmloadw AX","0F 01 DA","V","V","SVM","amd,modrm_regonly,operand16","r","Y","16" +"VMMCALL","VMMCALL","vmmcall","0F 01 D9","V","V","SVM","amd","","","" +"VMOVAPD xmm2/m128, xmm1","VMOVAPD xmm1, xmm2/m128","vmovapd xmm1, xmm2/m128","VEX.128.66.0F.WIG 29 /r","V","V","AVX","","w,r","","" +"VMOVAPD xmm2/m128, {k}{z}, xmm1","VMOVAPD xmm1, {k}{z}, xmm2/m128","vmovapd xmm1, {k}{z}, xmm2/m128","EVEX.128.66.0F.W1 29 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VMOVAPD xmm1, xmm2/m128","VMOVAPD xmm2/m128, xmm1","vmovapd xmm2/m128, xmm1","VEX.128.66.0F.WIG 28 /r","V","V","AVX","","w,r","","" +"VMOVAPD xmm1, {k}{z}, xmm2/m128","VMOVAPD xmm2/m128, {k}{z}, xmm1","vmovapd xmm2/m128, {k}{z}, xmm1","EVEX.128.66.0F.W1 28 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VMOVAPD ymm2/m256, ymm1","VMOVAPD ymm1, ymm2/m256","vmovapd ymm1, ymm2/m256","VEX.256.66.0F.WIG 29 /r","V","V","AVX","","w,r","","" +"VMOVAPD ymm2/m256, {k}{z}, ymm1","VMOVAPD ymm1, {k}{z}, ymm2/m256","vmovapd ymm1, {k}{z}, ymm2/m256","EVEX.256.66.0F.W1 29 /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","","" +"VMOVAPD ymm1, ymm2/m256","VMOVAPD ymm2/m256, ymm1","vmovapd ymm2/m256, ymm1","VEX.256.66.0F.WIG 28 /r","V","V","AVX","","w,r","","" +"VMOVAPD ymm1, {k}{z}, ymm2/m256","VMOVAPD ymm2/m256, {k}{z}, ymm1","vmovapd ymm2/m256, {k}{z}, ymm1","EVEX.256.66.0F.W1 28 /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","","" +"VMOVAPD zmm2/m512, {k}{z}, zmm1","VMOVAPD zmm1, {k}{z}, zmm2/m512","vmovapd zmm1, {k}{z}, zmm2/m512","EVEX.512.66.0F.W1 29 /r","V","V","AVX512F","scale64","w,r,r","","" +"VMOVAPD zmm1, {k}{z}, zmm2/m512","VMOVAPD zmm2/m512, {k}{z}, zmm1","vmovapd zmm2/m512, {k}{z}, zmm1","EVEX.512.66.0F.W1 28 /r","V","V","AVX512F","scale64","w,r,r","","" +"VMOVAPS xmm2/m128, xmm1","VMOVAPS xmm1, xmm2/m128","vmovaps xmm1, xmm2/m128","VEX.128.0F.WIG 29 /r","V","V","AVX","","w,r","","" +"VMOVAPS xmm2/m128, {k}{z}, xmm1","VMOVAPS xmm1, {k}{z}, xmm2/m128","vmovaps xmm1, {k}{z}, xmm2/m128","EVEX.128.0F.W0 29 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VMOVAPS xmm1, xmm2/m128","VMOVAPS xmm2/m128, xmm1","vmovaps xmm2/m128, xmm1","VEX.128.0F.WIG 28 /r","V","V","AVX","","w,r","","" +"VMOVAPS xmm1, {k}{z}, xmm2/m128","VMOVAPS xmm2/m128, {k}{z}, xmm1","vmovaps xmm2/m128, {k}{z}, xmm1","EVEX.128.0F.W0 28 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VMOVAPS ymm2/m256, ymm1","VMOVAPS ymm1, ymm2/m256","vmovaps ymm1, ymm2/m256","VEX.256.0F.WIG 29 /r","V","V","AVX","","w,r","","" +"VMOVAPS ymm2/m256, {k}{z}, ymm1","VMOVAPS ymm1, {k}{z}, ymm2/m256","vmovaps ymm1, {k}{z}, ymm2/m256","EVEX.256.0F.W0 29 /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","","" +"VMOVAPS ymm1, ymm2/m256","VMOVAPS ymm2/m256, ymm1","vmovaps ymm2/m256, ymm1","VEX.256.0F.WIG 28 /r","V","V","AVX","","w,r","","" +"VMOVAPS ymm1, {k}{z}, ymm2/m256","VMOVAPS ymm2/m256, {k}{z}, ymm1","vmovaps ymm2/m256, {k}{z}, ymm1","EVEX.256.0F.W0 28 /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","","" +"VMOVAPS zmm2/m512, {k}{z}, zmm1","VMOVAPS zmm1, {k}{z}, zmm2/m512","vmovaps zmm1, {k}{z}, zmm2/m512","EVEX.512.0F.W0 29 /r","V","V","AVX512F","scale64","w,r,r","","" +"VMOVAPS zmm1, {k}{z}, zmm2/m512","VMOVAPS zmm2/m512, {k}{z}, zmm1","vmovaps zmm2/m512, {k}{z}, zmm1","EVEX.512.0F.W0 28 /r","V","V","AVX512F","scale64","w,r,r","","" +"VMOVD xmm1, r/m32","VMOVD r/m32, xmm1","vmovd r/m32, xmm1","EVEX.128.66.0F.W0 6E /r","V","V","AVX512F+AVX512VL","scale4","w,r","","" +"VMOVD xmm1, r/m32","VMOVD r/m32, xmm1","vmovd r/m32, xmm1","VEX.128.66.0F.W0 6E /r","V","V","AVX","","w,r","","" +"VMOVD r/m32, xmm1","VMOVD xmm1, r/m32","vmovd xmm1, r/m32","EVEX.128.66.0F.W0 7E /r","V","V","AVX512F+AVX512VL","scale4","w,r","","" +"VMOVD r/m32, xmm1","VMOVD xmm1, r/m32","vmovd xmm1, r/m32","VEX.128.66.0F.W0 7E /r","V","V","AVX","","w,r","","" +"VMOVDDUP xmm1, xmm2/m64","VMOVDDUP xmm2/m64, xmm1","vmovddup xmm2/m64, xmm1","VEX.128.F2.0F.WIG 12 /r","V","V","AVX","","w,r","","" +"VMOVDDUP xmm1, {k}{z}, xmm2/m64","VMOVDDUP xmm2/m64, {k}{z}, xmm1","vmovddup xmm2/m64, {k}{z}, xmm1","EVEX.128.F2.0F.W1 12 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VMOVDDUP ymm1, ymm2/m256","VMOVDDUP ymm2/m256, ymm1","vmovddup ymm2/m256, ymm1","VEX.256.F2.0F.WIG 12 /r","V","V","AVX","","w,r","","" +"VMOVDDUP ymm1, {k}{z}, ymm2/m256","VMOVDDUP ymm2/m256, {k}{z}, ymm1","vmovddup ymm2/m256, {k}{z}, ymm1","EVEX.256.F2.0F.W1 12 /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","","" +"VMOVDDUP zmm1, {k}{z}, zmm2/m512","VMOVDDUP zmm2/m512, {k}{z}, zmm1","vmovddup zmm2/m512, {k}{z}, zmm1","EVEX.512.F2.0F.W1 12 /r","V","V","AVX512F","scale64","w,r,r","","" +"VMOVDQA xmm2/m128, xmm1","VMOVDQA xmm1, xmm2/m128","vmovdqa xmm1, xmm2/m128","VEX.128.66.0F.WIG 7F /r","V","V","AVX","","w,r","","" +"VMOVDQA xmm1, xmm2/m128","VMOVDQA xmm2/m128, xmm1","vmovdqa xmm2/m128, xmm1","VEX.128.66.0F.WIG 6F /r","V","V","AVX","","w,r","","" +"VMOVDQA ymm2/m256, ymm1","VMOVDQA ymm1, ymm2/m256","vmovdqa ymm1, ymm2/m256","VEX.256.66.0F.WIG 7F /r","V","V","AVX","","w,r","","" +"VMOVDQA ymm1, ymm2/m256","VMOVDQA ymm2/m256, ymm1","vmovdqa ymm2/m256, ymm1","VEX.256.66.0F.WIG 6F /r","V","V","AVX","","w,r","","" +"VMOVDQA32 xmm2/m128, {k}{z}, xmm1","VMOVDQA32 xmm1, {k}{z}, xmm2/m128","vmovdqa32 xmm1, {k}{z}, xmm2/m128","EVEX.128.66.0F.W0 7F /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VMOVDQA32 xmm1, {k}{z}, xmm2/m128","VMOVDQA32 xmm2/m128, {k}{z}, xmm1","vmovdqa32 xmm2/m128, {k}{z}, xmm1","EVEX.128.66.0F.W0 6F /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VMOVDQA32 ymm2/m256, {k}{z}, ymm1","VMOVDQA32 ymm1, {k}{z}, ymm2/m256","vmovdqa32 ymm1, {k}{z}, ymm2/m256","EVEX.256.66.0F.W0 7F /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","","" +"VMOVDQA32 ymm1, {k}{z}, ymm2/m256","VMOVDQA32 ymm2/m256, {k}{z}, ymm1","vmovdqa32 ymm2/m256, {k}{z}, ymm1","EVEX.256.66.0F.W0 6F /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","","" +"VMOVDQA32 zmm2/m512, {k}{z}, zmm1","VMOVDQA32 zmm1, {k}{z}, zmm2/m512","vmovdqa32 zmm1, {k}{z}, zmm2/m512","EVEX.512.66.0F.W0 7F /r","V","V","AVX512F","scale64","w,r,r","","" +"VMOVDQA32 zmm1, {k}{z}, zmm2/m512","VMOVDQA32 zmm2/m512, {k}{z}, zmm1","vmovdqa32 zmm2/m512, {k}{z}, zmm1","EVEX.512.66.0F.W0 6F /r","V","V","AVX512F","scale64","w,r,r","","" +"VMOVDQA64 xmm2/m128, {k}{z}, xmm1","VMOVDQA64 xmm1, {k}{z}, xmm2/m128","vmovdqa64 xmm1, {k}{z}, xmm2/m128","EVEX.128.66.0F.W1 7F /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","Y","128" +"VMOVDQA64 xmm1, {k}{z}, xmm2/m128","VMOVDQA64 xmm2/m128, {k}{z}, xmm1","vmovdqa64 xmm2/m128, {k}{z}, xmm1","EVEX.128.66.0F.W1 6F /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","Y","128" +"VMOVDQA64 ymm2/m256, {k}{z}, ymm1","VMOVDQA64 ymm1, {k}{z}, ymm2/m256","vmovdqa64 ymm1, {k}{z}, ymm2/m256","EVEX.256.66.0F.W1 7F /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","Y","256" +"VMOVDQA64 ymm1, {k}{z}, ymm2/m256","VMOVDQA64 ymm2/m256, {k}{z}, ymm1","vmovdqa64 ymm2/m256, {k}{z}, ymm1","EVEX.256.66.0F.W1 6F /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","Y","256" +"VMOVDQA64 zmm2/m512, {k}{z}, zmm1","VMOVDQA64 zmm1, {k}{z}, zmm2/m512","vmovdqa64 zmm1, {k}{z}, zmm2/m512","EVEX.512.66.0F.W1 7F /r","V","V","AVX512F","scale64","w,r,r","Y","512" +"VMOVDQA64 zmm1, {k}{z}, zmm2/m512","VMOVDQA64 zmm2/m512, {k}{z}, zmm1","vmovdqa64 zmm2/m512, {k}{z}, zmm1","EVEX.512.66.0F.W1 6F /r","V","V","AVX512F","scale64","w,r,r","Y","512" +"VMOVDQU xmm2/m128, xmm1","VMOVDQU xmm1, xmm2/m128","vmovdqu xmm1, xmm2/m128","VEX.128.F3.0F.WIG 7F /r","V","V","AVX","","w,r","","" +"VMOVDQU xmm1, xmm2/m128","VMOVDQU xmm2/m128, xmm1","vmovdqu xmm2/m128, xmm1","VEX.128.F3.0F.WIG 6F /r","V","V","AVX","","w,r","","" +"VMOVDQU ymm2/m256, ymm1","VMOVDQU ymm1, ymm2/m256","vmovdqu ymm1, ymm2/m256","VEX.256.F3.0F.WIG 7F /r","V","V","AVX","","w,r","","" +"VMOVDQU ymm1, ymm2/m256","VMOVDQU ymm2/m256, ymm1","vmovdqu ymm2/m256, ymm1","VEX.256.F3.0F.WIG 6F /r","V","V","AVX","","w,r","","" +"VMOVDQU16 xmm2/m128, {k}{z}, xmm1","VMOVDQU16 xmm1, {k}{z}, xmm2/m128","vmovdqu16 xmm1, {k}{z}, xmm2/m128","EVEX.128.F2.0F.W1 7F /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r","","" +"VMOVDQU16 xmm1, {k}{z}, xmm2/m128","VMOVDQU16 xmm2/m128, {k}{z}, xmm1","vmovdqu16 xmm2/m128, {k}{z}, xmm1","EVEX.128.F2.0F.W1 6F /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r","","" +"VMOVDQU16 ymm2/m256, {k}{z}, ymm1","VMOVDQU16 ymm1, {k}{z}, ymm2/m256","vmovdqu16 ymm1, {k}{z}, ymm2/m256","EVEX.256.F2.0F.W1 7F /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r","","" +"VMOVDQU16 ymm1, {k}{z}, ymm2/m256","VMOVDQU16 ymm2/m256, {k}{z}, ymm1","vmovdqu16 ymm2/m256, {k}{z}, ymm1","EVEX.256.F2.0F.W1 6F /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r","","" +"VMOVDQU16 zmm2/m512, {k}{z}, zmm1","VMOVDQU16 zmm1, {k}{z}, zmm2/m512","vmovdqu16 zmm1, {k}{z}, zmm2/m512","EVEX.512.F2.0F.W1 7F /r","V","V","AVX512BW","scale64","w,r,r","","" +"VMOVDQU16 zmm1, {k}{z}, zmm2/m512","VMOVDQU16 zmm2/m512, {k}{z}, zmm1","vmovdqu16 zmm2/m512, {k}{z}, zmm1","EVEX.512.F2.0F.W1 6F /r","V","V","AVX512BW","scale64","w,r,r","","" +"VMOVDQU32 xmm2/m128, {k}{z}, xmm1","VMOVDQU32 xmm1, {k}{z}, xmm2/m128","vmovdqu32 xmm1, {k}{z}, xmm2/m128","EVEX.128.F3.0F.W0 7F /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","Y","128" +"VMOVDQU32 xmm1, {k}{z}, xmm2/m128","VMOVDQU32 xmm2/m128, {k}{z}, xmm1","vmovdqu32 xmm2/m128, {k}{z}, xmm1","EVEX.128.F3.0F.W0 6F /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","Y","128" +"VMOVDQU32 ymm2/m256, {k}{z}, ymm1","VMOVDQU32 ymm1, {k}{z}, ymm2/m256","vmovdqu32 ymm1, {k}{z}, ymm2/m256","EVEX.256.F3.0F.W0 7F /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","Y","256" +"VMOVDQU32 ymm1, {k}{z}, ymm2/m256","VMOVDQU32 ymm2/m256, {k}{z}, ymm1","vmovdqu32 ymm2/m256, {k}{z}, ymm1","EVEX.256.F3.0F.W0 6F /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","Y","256" +"VMOVDQU32 zmm2/m512, {k}{z}, zmm1","VMOVDQU32 zmm1, {k}{z}, zmm2/m512","vmovdqu32 zmm1, {k}{z}, zmm2/m512","EVEX.512.F3.0F.W0 7F /r","V","V","AVX512F","scale64","w,r,r","Y","512" +"VMOVDQU32 zmm1, {k}{z}, zmm2/m512","VMOVDQU32 zmm2/m512, {k}{z}, zmm1","vmovdqu32 zmm2/m512, {k}{z}, zmm1","EVEX.512.F3.0F.W0 6F /r","V","V","AVX512F","scale64","w,r,r","Y","512" +"VMOVDQU64 xmm2/m128, {k}{z}, xmm1","VMOVDQU64 xmm1, {k}{z}, xmm2/m128","vmovdqu64 xmm1, {k}{z}, xmm2/m128","EVEX.128.F3.0F.W1 7F /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","Y","128" +"VMOVDQU64 xmm1, {k}{z}, xmm2/m128","VMOVDQU64 xmm2/m128, {k}{z}, xmm1","vmovdqu64 xmm2/m128, {k}{z}, xmm1","EVEX.128.F3.0F.W1 6F /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","Y","128" +"VMOVDQU64 ymm2/m256, {k}{z}, ymm1","VMOVDQU64 ymm1, {k}{z}, ymm2/m256","vmovdqu64 ymm1, {k}{z}, ymm2/m256","EVEX.256.F3.0F.W1 7F /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","Y","256" +"VMOVDQU64 ymm1, {k}{z}, ymm2/m256","VMOVDQU64 ymm2/m256, {k}{z}, ymm1","vmovdqu64 ymm2/m256, {k}{z}, ymm1","EVEX.256.F3.0F.W1 6F /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","Y","256" +"VMOVDQU64 zmm2/m512, {k}{z}, zmm1","VMOVDQU64 zmm1, {k}{z}, zmm2/m512","vmovdqu64 zmm1, {k}{z}, zmm2/m512","EVEX.512.F3.0F.W1 7F /r","V","V","AVX512F","scale64","w,r,r","Y","512" +"VMOVDQU64 zmm1, {k}{z}, zmm2/m512","VMOVDQU64 zmm2/m512, {k}{z}, zmm1","vmovdqu64 zmm2/m512, {k}{z}, zmm1","EVEX.512.F3.0F.W1 6F /r","V","V","AVX512F","scale64","w,r,r","Y","512" +"VMOVDQU8 xmm2/m128, {k}{z}, xmm1","VMOVDQU8 xmm1, {k}{z}, xmm2/m128","vmovdqu8 xmm1, {k}{z}, xmm2/m128","EVEX.128.F2.0F.W0 7F /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r","Y","128" +"VMOVDQU8 xmm1, {k}{z}, xmm2/m128","VMOVDQU8 xmm2/m128, {k}{z}, xmm1","vmovdqu8 xmm2/m128, {k}{z}, xmm1","EVEX.128.F2.0F.W0 6F /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r","Y","128" +"VMOVDQU8 ymm2/m256, {k}{z}, ymm1","VMOVDQU8 ymm1, {k}{z}, ymm2/m256","vmovdqu8 ymm1, {k}{z}, ymm2/m256","EVEX.256.F2.0F.W0 7F /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r","Y","256" +"VMOVDQU8 ymm1, {k}{z}, ymm2/m256","VMOVDQU8 ymm2/m256, {k}{z}, ymm1","vmovdqu8 ymm2/m256, {k}{z}, ymm1","EVEX.256.F2.0F.W0 6F /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r","Y","256" +"VMOVDQU8 zmm2/m512, {k}{z}, zmm1","VMOVDQU8 zmm1, {k}{z}, zmm2/m512","vmovdqu8 zmm1, {k}{z}, zmm2/m512","EVEX.512.F2.0F.W0 7F /r","V","V","AVX512BW","scale64","w,r,r","Y","512" +"VMOVDQU8 zmm1, {k}{z}, zmm2/m512","VMOVDQU8 zmm2/m512, {k}{z}, zmm1","vmovdqu8 zmm2/m512, {k}{z}, zmm1","EVEX.512.F2.0F.W0 6F /r","V","V","AVX512BW","scale64","w,r,r","Y","512" +"VMOVHLPS xmm1, xmmV, xmm2","VMOVHLPS xmm2, xmmV, xmm1","vmovhlps xmm2, xmmV, xmm1","EVEX.NDS.128.0F.W0 12 /r","V","V","AVX512F+AVX512VL","modrm_regonly","w,r,r","","" +"VMOVHLPS xmm1, xmmV, xmm2","VMOVHLPS xmm2, xmmV, xmm1","vmovhlps xmm2, xmmV, xmm1","VEX.NDS.128.0F.WIG 12 /r","V","V","AVX","modrm_regonly","w,r,r","","" +"VMOVHPD xmm1, xmmV, m64","VMOVHPD m64, xmmV, xmm1","vmovhpd m64, xmmV, xmm1","EVEX.NDS.LIG.66.0F.W1 16 /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,r,r","","" +"VMOVHPD xmm1, xmmV, m64","VMOVHPD m64, xmmV, xmm1","vmovhpd m64, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 16 /r","V","V","AVX","modrm_memonly","w,r,r","","" +"VMOVHPD m64, xmm1","VMOVHPD xmm1, m64","vmovhpd xmm1, m64","EVEX.LIG.66.0F.W1 17 /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,r","","" +"VMOVHPD m64, xmm1","VMOVHPD xmm1, m64","vmovhpd xmm1, m64","VEX.128.66.0F.WIG 17 /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVHPS xmm1, xmmV, m64","VMOVHPS m64, xmmV, xmm1","vmovhps m64, xmmV, xmm1","EVEX.NDS.128.0F.W0 16 /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,r,r","","" +"VMOVHPS xmm1, xmmV, m64","VMOVHPS m64, xmmV, xmm1","vmovhps m64, xmmV, xmm1","VEX.NDS.128.0F.WIG 16 /r","V","V","AVX","modrm_memonly","w,r,r","","" +"VMOVHPS m64, xmm1","VMOVHPS xmm1, m64","vmovhps xmm1, m64","EVEX.128.0F.W0 17 /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,r","","" +"VMOVHPS m64, xmm1","VMOVHPS xmm1, m64","vmovhps xmm1, m64","VEX.128.0F.WIG 17 /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVLHPS xmm1, xmmV, xmm2","VMOVLHPS xmm2, xmmV, xmm1","vmovlhps xmm2, xmmV, xmm1","EVEX.NDS.128.0F.W0 16 /r","V","V","AVX512F+AVX512VL","modrm_regonly","w,r,r","","" +"VMOVLHPS xmm1, xmmV, xmm2","VMOVLHPS xmm2, xmmV, xmm1","vmovlhps xmm2, xmmV, xmm1","VEX.NDS.128.0F.WIG 16 /r","V","V","AVX","modrm_regonly","w,r,r","","" +"VMOVLPD xmm1, xmmV, m64","VMOVLPD m64, xmmV, xmm1","vmovlpd m64, xmmV, xmm1","EVEX.NDS.LIG.66.0F.W1 12 /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,r,r","","" +"VMOVLPD xmm1, xmmV, m64","VMOVLPD m64, xmmV, xmm1","vmovlpd m64, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 12 /r","V","V","AVX","modrm_memonly","w,r,r","","" +"VMOVLPD m64, xmm1","VMOVLPD xmm1, m64","vmovlpd xmm1, m64","EVEX.LIG.66.0F.W1 13 /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,r","","" +"VMOVLPD m64, xmm1","VMOVLPD xmm1, m64","vmovlpd xmm1, m64","VEX.128.66.0F.WIG 13 /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVLPS xmm1, xmmV, m64","VMOVLPS m64, xmmV, xmm1","vmovlps m64, xmmV, xmm1","EVEX.NDS.128.0F.W0 12 /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,r,r","","" +"VMOVLPS xmm1, xmmV, m64","VMOVLPS m64, xmmV, xmm1","vmovlps m64, xmmV, xmm1","VEX.NDS.128.0F.WIG 12 /r","V","V","AVX","modrm_memonly","w,r,r","","" +"VMOVLPS m64, xmm1","VMOVLPS xmm1, m64","vmovlps xmm1, m64","EVEX.128.0F.W0 13 /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,r","","" +"VMOVLPS m64, xmm1","VMOVLPS xmm1, m64","vmovlps xmm1, m64","VEX.128.0F.WIG 13 /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVMSKPD r32, xmm2","VMOVMSKPD xmm2, r32","vmovmskpd xmm2, r32","VEX.128.66.0F.WIG 50 /r","V","V","AVX","modrm_regonly","w,r","","" +"VMOVMSKPD r32, ymm2","VMOVMSKPD ymm2, r32","vmovmskpd ymm2, r32","VEX.256.66.0F.WIG 50 /r","V","V","AVX","modrm_regonly","w,r","","" +"VMOVMSKPS r32, xmm2","VMOVMSKPS xmm2, r32","vmovmskps xmm2, r32","VEX.128.0F.WIG 50 /r","V","V","AVX","modrm_regonly","w,r","","" +"VMOVMSKPS r32, ymm2","VMOVMSKPS ymm2, r32","vmovmskps ymm2, r32","VEX.256.0F.WIG 50 /r","V","V","AVX","modrm_regonly","w,r","","" +"VMOVNTDQ m128, xmm1","VMOVNTDQ xmm1, m128","vmovntdq xmm1, m128","EVEX.128.66.0F.W0 E7 /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale16","w,r","","" +"VMOVNTDQ m128, xmm1","VMOVNTDQ xmm1, m128","vmovntdq xmm1, m128","VEX.128.66.0F.WIG E7 /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVNTDQ m256, ymm1","VMOVNTDQ ymm1, m256","vmovntdq ymm1, m256","EVEX.256.66.0F.W0 E7 /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale32","w,r","","" +"VMOVNTDQ m256, ymm1","VMOVNTDQ ymm1, m256","vmovntdq ymm1, m256","VEX.256.66.0F.WIG E7 /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVNTDQ m512, zmm1","VMOVNTDQ zmm1, m512","vmovntdq zmm1, m512","EVEX.512.66.0F.W0 E7 /r","V","V","AVX512F","modrm_memonly,scale64","w,r","","" +"VMOVNTDQA xmm1, m128","VMOVNTDQA m128, xmm1","vmovntdqa m128, xmm1","EVEX.128.66.0F38.W0 2A /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale16","w,r","","" +"VMOVNTDQA xmm1, m128","VMOVNTDQA m128, xmm1","vmovntdqa m128, xmm1","VEX.128.66.0F38.WIG 2A /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVNTDQA ymm1, m256","VMOVNTDQA m256, ymm1","vmovntdqa m256, ymm1","EVEX.256.66.0F38.W0 2A /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale32","w,r","","" +"VMOVNTDQA ymm1, m256","VMOVNTDQA m256, ymm1","vmovntdqa m256, ymm1","VEX.256.66.0F38.WIG 2A /r","V","V","AVX2","modrm_memonly","w,r","","" +"VMOVNTDQA zmm1, m512","VMOVNTDQA m512, zmm1","vmovntdqa m512, zmm1","EVEX.512.66.0F38.W0 2A /r","V","V","AVX512F","modrm_memonly,scale64","w,r","","" +"VMOVNTPD m128, xmm1","VMOVNTPD xmm1, m128","vmovntpd xmm1, m128","EVEX.128.66.0F.W1 2B /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale16","w,r","","" +"VMOVNTPD m128, xmm1","VMOVNTPD xmm1, m128","vmovntpd xmm1, m128","VEX.128.66.0F.WIG 2B /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVNTPD m256, ymm1","VMOVNTPD ymm1, m256","vmovntpd ymm1, m256","EVEX.256.66.0F.W1 2B /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale32","w,r","","" +"VMOVNTPD m256, ymm1","VMOVNTPD ymm1, m256","vmovntpd ymm1, m256","VEX.256.66.0F.WIG 2B /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVNTPD m512, zmm1","VMOVNTPD zmm1, m512","vmovntpd zmm1, m512","EVEX.512.66.0F.W1 2B /r","V","V","AVX512F","modrm_memonly,scale64","w,r","","" +"VMOVNTPS m128, xmm1","VMOVNTPS xmm1, m128","vmovntps xmm1, m128","EVEX.128.0F.W0 2B /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale16","w,r","","" +"VMOVNTPS m128, xmm1","VMOVNTPS xmm1, m128","vmovntps xmm1, m128","VEX.128.0F.WIG 2B /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVNTPS m256, ymm1","VMOVNTPS ymm1, m256","vmovntps ymm1, m256","EVEX.256.0F.W0 2B /r","V","V","AVX512F+AVX512VL","modrm_memonly,scale32","w,r","","" +"VMOVNTPS m256, ymm1","VMOVNTPS ymm1, m256","vmovntps ymm1, m256","VEX.256.0F.WIG 2B /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVNTPS m512, zmm1","VMOVNTPS zmm1, m512","vmovntps zmm1, m512","EVEX.512.0F.W0 2B /r","V","V","AVX512F","modrm_memonly,scale64","w,r","","" +"VMOVQ xmm1, r/m64","VMOVQ r/m64, xmm1","vmovq r/m64, xmm1","EVEX.128.66.0F.W1 6E /r","N.S.","V","AVX512F+AVX512VL","scale8","w,r","","" +"VMOVQ xmm1, r/m64","VMOVQ r/m64, xmm1","vmovq r/m64, xmm1","VEX.128.66.0F.W1 6E /r","N.S.","V","AVX","","w,r","","" +"VMOVQ r/m64, xmm1","VMOVQ xmm1, r/m64","vmovq xmm1, r/m64","EVEX.128.66.0F.W1 7E /r","N.S.","V","AVX512F+AVX512VL","scale8","w,r","","" +"VMOVQ r/m64, xmm1","VMOVQ xmm1, r/m64","vmovq xmm1, r/m64","VEX.128.66.0F.W1 7E /r","N.S.","V","AVX","","w,r","","" +"VMOVQ xmm2/m64, xmm1","VMOVQ xmm1, xmm2/m64","vmovq xmm1, xmm2/m64","EVEX.LIG.66.0F.W1 D6 /r","V","V","AVX512F+AVX512VL","scale8","w,r","","" +"VMOVQ xmm2/m64, xmm1","VMOVQ xmm1, xmm2/m64","vmovq xmm1, xmm2/m64","VEX.128.66.0F.WIG D6 /r","V","V","AVX","","w,r","","" +"VMOVQ xmm1, xmm2/m64","VMOVQ xmm2/m64, xmm1","vmovq xmm2/m64, xmm1","EVEX.LIG.F3.0F.W1 7E /r","V","V","AVX512F+AVX512VL","scale8","w,r","","" +"VMOVQ xmm1, xmm2/m64","VMOVQ xmm2/m64, xmm1","vmovq xmm2/m64, xmm1","VEX.128.F3.0F.WIG 7E /r","V","V","AVX","","w,r","","" +"VMOVSD xmm1, m64","VMOVSD m64, xmm1","vmovsd m64, xmm1","VEX.LIG.F2.0F.WIG 10 /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVSD xmm1, {k}{z}, m64","VMOVSD m64, {k}{z}, xmm1","vmovsd m64, {k}{z}, xmm1","EVEX.LIG.F2.0F.W1 10 /r","V","V","AVX512F","modrm_memonly,scale8","w,r,r","","" +"VMOVSD m64, xmm1","VMOVSD xmm1, m64","vmovsd xmm1, m64","VEX.LIG.F2.0F.WIG 11 /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVSD xmm2, xmmV, xmm1","VMOVSD xmm1, xmmV, xmm2","vmovsd xmm1, xmmV, xmm2","VEX.NDS.LIG.F2.0F.WIG 11 /r","V","V","AVX","modrm_regonly","w,r,r","","" +"VMOVSD xmm2, {k}{z}, xmmV, xmm1","VMOVSD xmm1, xmmV, {k}{z}, xmm2","vmovsd xmm1, xmmV, {k}{z}, xmm2","EVEX.NDS.LIG.F2.0F.W1 11 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMOVSD m64, {k}, xmm1","VMOVSD xmm1, {k}, m64","vmovsd xmm1, {k}, m64","EVEX.LIG.F2.0F.W1 11 /r","V","V","AVX512F","modrm_memonly,scale8","w,r,r","","" +"VMOVSD xmm1, xmmV, xmm2","VMOVSD xmm2, xmmV, xmm1","vmovsd xmm2, xmmV, xmm1","VEX.NDS.LIG.F2.0F.WIG 10 /r","V","V","AVX","modrm_regonly","w,r,r","","" +"VMOVSD xmm1, {k}{z}, xmmV, xmm2","VMOVSD xmm2, xmmV, {k}{z}, xmm1","vmovsd xmm2, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F2.0F.W1 10 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMOVSHDUP xmm1, xmm2/m128","VMOVSHDUP xmm2/m128, xmm1","vmovshdup xmm2/m128, xmm1","VEX.128.F3.0F.WIG 16 /r","V","V","AVX","","w,r","","" +"VMOVSHDUP xmm1, {k}{z}, xmm2/m128","VMOVSHDUP xmm2/m128, {k}{z}, xmm1","vmovshdup xmm2/m128, {k}{z}, xmm1","EVEX.128.F3.0F.W0 16 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VMOVSHDUP ymm1, ymm2/m256","VMOVSHDUP ymm2/m256, ymm1","vmovshdup ymm2/m256, ymm1","VEX.256.F3.0F.WIG 16 /r","V","V","AVX","","w,r","","" +"VMOVSHDUP ymm1, {k}{z}, ymm2/m256","VMOVSHDUP ymm2/m256, {k}{z}, ymm1","vmovshdup ymm2/m256, {k}{z}, ymm1","EVEX.256.F3.0F.W0 16 /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","","" +"VMOVSHDUP zmm1, {k}{z}, zmm2/m512","VMOVSHDUP zmm2/m512, {k}{z}, zmm1","vmovshdup zmm2/m512, {k}{z}, zmm1","EVEX.512.F3.0F.W0 16 /r","V","V","AVX512F","scale64","w,r,r","","" +"VMOVSLDUP xmm1, xmm2/m128","VMOVSLDUP xmm2/m128, xmm1","vmovsldup xmm2/m128, xmm1","VEX.128.F3.0F.WIG 12 /r","V","V","AVX","","w,r","","" +"VMOVSLDUP xmm1, {k}{z}, xmm2/m128","VMOVSLDUP xmm2/m128, {k}{z}, xmm1","vmovsldup xmm2/m128, {k}{z}, xmm1","EVEX.128.F3.0F.W0 12 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VMOVSLDUP ymm1, ymm2/m256","VMOVSLDUP ymm2/m256, ymm1","vmovsldup ymm2/m256, ymm1","VEX.256.F3.0F.WIG 12 /r","V","V","AVX","","w,r","","" +"VMOVSLDUP ymm1, {k}{z}, ymm2/m256","VMOVSLDUP ymm2/m256, {k}{z}, ymm1","vmovsldup ymm2/m256, {k}{z}, ymm1","EVEX.256.F3.0F.W0 12 /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","","" +"VMOVSLDUP zmm1, {k}{z}, zmm2/m512","VMOVSLDUP zmm2/m512, {k}{z}, zmm1","vmovsldup zmm2/m512, {k}{z}, zmm1","EVEX.512.F3.0F.W0 12 /r","V","V","AVX512F","scale64","w,r,r","","" +"VMOVSS xmm1, m32","VMOVSS m32, xmm1","vmovss m32, xmm1","VEX.LIG.F3.0F.WIG 10 /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVSS xmm1, {k}{z}, m32","VMOVSS m32, {k}{z}, xmm1","vmovss m32, {k}{z}, xmm1","EVEX.LIG.F3.0F.W0 10 /r","V","V","AVX512F","modrm_memonly,scale4","w,r,r","","" +"VMOVSS m32, xmm1","VMOVSS xmm1, m32","vmovss xmm1, m32","VEX.LIG.F3.0F.WIG 11 /r","V","V","AVX","modrm_memonly","w,r","","" +"VMOVSS xmm2, xmmV, xmm1","VMOVSS xmm1, xmmV, xmm2","vmovss xmm1, xmmV, xmm2","VEX.NDS.LIG.F3.0F.WIG 11 /r","V","V","AVX","modrm_regonly","w,r,r","","" +"VMOVSS xmm2, {k}{z}, xmmV, xmm1","VMOVSS xmm1, xmmV, {k}{z}, xmm2","vmovss xmm1, xmmV, {k}{z}, xmm2","EVEX.NDS.LIG.F3.0F.W0 11 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMOVSS m32, {k}, xmm1","VMOVSS xmm1, {k}, m32","vmovss xmm1, {k}, m32","EVEX.LIG.F3.0F.W0 11 /r","V","V","AVX512F","modrm_memonly,scale4","w,r,r","","" +"VMOVSS xmm1, xmmV, xmm2","VMOVSS xmm2, xmmV, xmm1","vmovss xmm2, xmmV, xmm1","VEX.NDS.LIG.F3.0F.WIG 10 /r","V","V","AVX","modrm_regonly","w,r,r","","" +"VMOVSS xmm1, {k}{z}, xmmV, xmm2","VMOVSS xmm2, xmmV, {k}{z}, xmm1","vmovss xmm2, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F3.0F.W0 10 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMOVUPD xmm2/m128, xmm1","VMOVUPD xmm1, xmm2/m128","vmovupd xmm1, xmm2/m128","VEX.128.66.0F.WIG 11 /r","V","V","AVX","","w,r","","" +"VMOVUPD xmm2/m128, {k}{z}, xmm1","VMOVUPD xmm1, {k}{z}, xmm2/m128","vmovupd xmm1, {k}{z}, xmm2/m128","EVEX.128.66.0F.W1 11 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VMOVUPD xmm1, xmm2/m128","VMOVUPD xmm2/m128, xmm1","vmovupd xmm2/m128, xmm1","VEX.128.66.0F.WIG 10 /r","V","V","AVX","","w,r","","" +"VMOVUPD xmm1, {k}{z}, xmm2/m128","VMOVUPD xmm2/m128, {k}{z}, xmm1","vmovupd xmm2/m128, {k}{z}, xmm1","EVEX.128.66.0F.W1 10 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VMOVUPD ymm2/m256, ymm1","VMOVUPD ymm1, ymm2/m256","vmovupd ymm1, ymm2/m256","VEX.256.66.0F.WIG 11 /r","V","V","AVX","","w,r","","" +"VMOVUPD ymm2/m256, {k}{z}, ymm1","VMOVUPD ymm1, {k}{z}, ymm2/m256","vmovupd ymm1, {k}{z}, ymm2/m256","EVEX.256.66.0F.W1 11 /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","","" +"VMOVUPD ymm1, ymm2/m256","VMOVUPD ymm2/m256, ymm1","vmovupd ymm2/m256, ymm1","VEX.256.66.0F.WIG 10 /r","V","V","AVX","","w,r","","" +"VMOVUPD ymm1, {k}{z}, ymm2/m256","VMOVUPD ymm2/m256, {k}{z}, ymm1","vmovupd ymm2/m256, {k}{z}, ymm1","EVEX.256.66.0F.W1 10 /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","","" +"VMOVUPD zmm2/m512, {k}{z}, zmm1","VMOVUPD zmm1, {k}{z}, zmm2/m512","vmovupd zmm1, {k}{z}, zmm2/m512","EVEX.512.66.0F.W1 11 /r","V","V","AVX512F","scale64","w,r,r","","" +"VMOVUPD zmm1, {k}{z}, zmm2/m512","VMOVUPD zmm2/m512, {k}{z}, zmm1","vmovupd zmm2/m512, {k}{z}, zmm1","EVEX.512.66.0F.W1 10 /r","V","V","AVX512F","scale64","w,r,r","","" +"VMOVUPS xmm2/m128, xmm1","VMOVUPS xmm1, xmm2/m128","vmovups xmm1, xmm2/m128","VEX.128.0F.WIG 11 /r","V","V","AVX","","w,r","","" +"VMOVUPS xmm2/m128, {k}{z}, xmm1","VMOVUPS xmm1, {k}{z}, xmm2/m128","vmovups xmm1, {k}{z}, xmm2/m128","EVEX.128.0F.W0 11 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VMOVUPS xmm1, xmm2/m128","VMOVUPS xmm2/m128, xmm1","vmovups xmm2/m128, xmm1","VEX.128.0F.WIG 10 /r","V","V","AVX","","w,r","","" +"VMOVUPS xmm1, {k}{z}, xmm2/m128","VMOVUPS xmm2/m128, {k}{z}, xmm1","vmovups xmm2/m128, {k}{z}, xmm1","EVEX.128.0F.W0 10 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VMOVUPS ymm2/m256, ymm1","VMOVUPS ymm1, ymm2/m256","vmovups ymm1, ymm2/m256","VEX.256.0F.WIG 11 /r","V","V","AVX","","w,r","","" +"VMOVUPS ymm2/m256, {k}{z}, ymm1","VMOVUPS ymm1, {k}{z}, ymm2/m256","vmovups ymm1, {k}{z}, ymm2/m256","EVEX.256.0F.W0 11 /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","","" +"VMOVUPS ymm1, ymm2/m256","VMOVUPS ymm2/m256, ymm1","vmovups ymm2/m256, ymm1","VEX.256.0F.WIG 10 /r","V","V","AVX","","w,r","","" +"VMOVUPS ymm1, {k}{z}, ymm2/m256","VMOVUPS ymm2/m256, {k}{z}, ymm1","vmovups ymm2/m256, {k}{z}, ymm1","EVEX.256.0F.W0 10 /r","V","V","AVX512F+AVX512VL","scale32","w,r,r","","" +"VMOVUPS zmm2/m512, {k}{z}, zmm1","VMOVUPS zmm1, {k}{z}, zmm2/m512","vmovups zmm1, {k}{z}, zmm2/m512","EVEX.512.0F.W0 11 /r","V","V","AVX512F","scale64","w,r,r","","" +"VMOVUPS zmm1, {k}{z}, zmm2/m512","VMOVUPS zmm2/m512, {k}{z}, zmm1","vmovups zmm2/m512, {k}{z}, zmm1","EVEX.512.0F.W0 10 /r","V","V","AVX512F","scale64","w,r,r","","" +"VMPSADBW xmm1, xmmV, xmm2/m128, imm8u","VMPSADBW imm8u, xmm2/m128, xmmV, xmm1","vmpsadbw imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.WIG 42 /r ib","V","V","AVX","","w,r,r,r","","" +"VMPSADBW ymm1, ymmV, ymm2/m256, imm8u","VMPSADBW imm8u, ymm2/m256, ymmV, ymm1","vmpsadbw imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.WIG 42 /r ib","V","V","AVX2","","w,r,r,r","","" +"VMPTRLD m64","VMPTRLD m64","vmptrld m64","0F C7 /6","V","V","VTX","modrm_memonly","r","","" +"VMPTRST m64","VMPTRST m64","vmptrst m64","0F C7 /7","V","V","VTX","modrm_memonly","w","","" +"VMREAD r/m32, r32","VMREAD r32, r/m32","vmread r32, r/m32","0F 78 /r","V","N.S.","VTX","","rw,r","","" +"VMREAD r/m64, r64","VMREAD r64, r/m64","vmread r64, r/m64","0F 78 /r","N.S.","V","VTX","default64","rw,r","","" +"VMRESUME","VMRESUME","vmresume","0F 01 C3","V","V","VTX","","","","" +"VMRUN EAX","VMRUNL EAX","vmrunl EAX","0F 01 D8","V","V","SVM","amd,modrm_regonly,operand32","r","Y","32" +"VMRUN RAX","VMRUNQ RAX","vmrunq RAX","REX.W 0F 01 D8","N.S.","V","SVM","amd,modrm_regonly","r","Y","64" +"VMRUN AX","VMRUNW AX","vmrunw AX","0F 01 D8","V","V","SVM","amd,modrm_regonly,operand16","r","Y","16" +"VMSAVE","VMSAVE","vmsave","0F 01 DB","V","V","SVM","amd","","","" +"VMULPD xmm1, xmmV, xmm2/m128","VMULPD xmm2/m128, xmmV, xmm1","vmulpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 59 /r","V","V","AVX","","w,r,r","","" +"VMULPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VMULPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vmulpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 59 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VMULPD ymm1, ymmV, ymm2/m256","VMULPD ymm2/m256, ymmV, ymm1","vmulpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 59 /r","V","V","AVX","","w,r,r","","" +"VMULPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VMULPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vmulpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 59 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VMULPD zmm1{er}, {k}{z}, zmmV, zmm2","VMULPD zmm2, zmmV, {k}{z}, zmm1{er}","vmulpd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.NDS.512.66.0F.W1 59 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMULPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VMULPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vmulpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 59 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VMULPS xmm1, xmmV, xmm2/m128","VMULPS xmm2/m128, xmmV, xmm1","vmulps xmm2/m128, xmmV, xmm1","VEX.NDS.128.0F.WIG 59 /r","V","V","AVX","","w,r,r","","" +"VMULPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VMULPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vmulps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.0F.W0 59 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VMULPS ymm1, ymmV, ymm2/m256","VMULPS ymm2/m256, ymmV, ymm1","vmulps ymm2/m256, ymmV, ymm1","VEX.NDS.256.0F.WIG 59 /r","V","V","AVX","","w,r,r","","" +"VMULPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VMULPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vmulps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.0F.W0 59 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VMULPS zmm1{er}, {k}{z}, zmmV, zmm2","VMULPS zmm2, zmmV, {k}{z}, zmm1{er}","vmulps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.NDS.512.0F.W0 59 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMULPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VMULPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vmulps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.0F.W0 59 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VMULSD xmm1{er}, {k}{z}, xmmV, xmm2","VMULSD xmm2, xmmV, {k}{z}, xmm1{er}","vmulsd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.NDS.128.F2.0F.W1 59 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMULSD xmm1, xmmV, xmm2/m64","VMULSD xmm2/m64, xmmV, xmm1","vmulsd xmm2/m64, xmmV, xmm1","VEX.NDS.LIG.F2.0F.WIG 59 /r","V","V","AVX","","w,r,r","","" +"VMULSD xmm1, {k}{z}, xmmV, xmm2/m64","VMULSD xmm2/m64, xmmV, {k}{z}, xmm1","vmulsd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F2.0F.W1 59 /r","V","V","AVX512F","scale8","w,r,r,r","","" +"VMULSS xmm1{er}, {k}{z}, xmmV, xmm2","VMULSS xmm2, xmmV, {k}{z}, xmm1{er}","vmulss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.NDS.128.F3.0F.W0 59 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VMULSS xmm1, xmmV, xmm2/m32","VMULSS xmm2/m32, xmmV, xmm1","vmulss xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.F3.0F.WIG 59 /r","V","V","AVX","","w,r,r","","" +"VMULSS xmm1, {k}{z}, xmmV, xmm2/m32","VMULSS xmm2/m32, xmmV, {k}{z}, xmm1","vmulss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F3.0F.W0 59 /r","V","V","AVX512F","scale4","w,r,r,r","","" +"VMWRITE r32, r/m32","VMWRITE r/m32, r32","vmwrite r/m32, r32","0F 79 /r","V","N.S.","VTX","","r,r","","" +"VMWRITE r64, r/m64","VMWRITE r/m64, r64","vmwrite r/m64, r64","0F 79 /r","N.S.","V","VTX","default64","r,r","","" +"VMXOFF","VMXOFF","vmxoff","0F 01 C4","V","V","VTX","","","","" +"VMXON m64","VMXON m64","vmxon m64","F3 0F C7 /6","V","V","VTX","modrm_memonly","r","","" +"VORPD xmm1, xmmV, xmm2/m128","VORPD xmm2/m128, xmmV, xmm1","vorpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 56 /r","V","V","AVX","","w,r,r","","" +"VORPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VORPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vorpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 56 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VORPD ymm1, ymmV, ymm2/m256","VORPD ymm2/m256, ymmV, ymm1","vorpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 56 /r","V","V","AVX","","w,r,r","","" +"VORPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VORPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vorpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 56 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VORPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VORPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vorpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 56 /r","V","V","AVX512DQ","bscale8,scale64","w,r,r,r","","" +"VORPS xmm1, xmmV, xmm2/m128","VORPS xmm2/m128, xmmV, xmm1","vorps xmm2/m128, xmmV, xmm1","VEX.NDS.128.0F.WIG 56 /r","V","V","AVX","","w,r,r","","" +"VORPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VORPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vorps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.0F.W0 56 /r","V","V","AVX512DQ+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VORPS ymm1, ymmV, ymm2/m256","VORPS ymm2/m256, ymmV, ymm1","vorps ymm2/m256, ymmV, ymm1","VEX.NDS.256.0F.WIG 56 /r","V","V","AVX","","w,r,r","","" +"VORPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VORPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vorps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.0F.W0 56 /r","V","V","AVX512DQ+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VORPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VORPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vorps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.0F.W0 56 /r","V","V","AVX512DQ","bscale4,scale64","w,r,r,r","","" +"VP4DPWSSD zmm1, {k}{z}, zmmV+3, m128","VP4DPWSSD m128, zmmV+3, {k}{z}, zmm1","vp4dpwssd m128, zmmV+3, {k}{z}, zmm1","EVEX.DDS.512.F2.0F38.W0 52 /r","V","V","AVX512_4VNNIW","modrm_memonly,scale16","rw,r,r,r","","" +"VP4DPWSSDS zmm1, {k}{z}, zmmV+3, m128","VP4DPWSSDS m128, zmmV+3, {k}{z}, zmm1","vp4dpwssds m128, zmmV+3, {k}{z}, zmm1","EVEX.DDS.512.F2.0F38.W0 53 /r","V","V","AVX512_4VNNIW","modrm_memonly,scale16","rw,r,r,r","","" +"VPABSB xmm1, xmm2/m128","VPABSB xmm2/m128, xmm1","vpabsb xmm2/m128, xmm1","VEX.128.66.0F38.WIG 1C /r","V","V","AVX","","w,r","","" +"VPABSB xmm1, {k}{z}, xmm2/m128","VPABSB xmm2/m128, {k}{z}, xmm1","vpabsb xmm2/m128, {k}{z}, xmm1","EVEX.128.66.0F38.WIG 1C /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r","","" +"VPABSB ymm1, ymm2/m256","VPABSB ymm2/m256, ymm1","vpabsb ymm2/m256, ymm1","VEX.256.66.0F38.WIG 1C /r","V","V","AVX2","","w,r","","" +"VPABSB ymm1, {k}{z}, ymm2/m256","VPABSB ymm2/m256, {k}{z}, ymm1","vpabsb ymm2/m256, {k}{z}, ymm1","EVEX.256.66.0F38.WIG 1C /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r","","" +"VPABSB zmm1, {k}{z}, zmm2/m512","VPABSB zmm2/m512, {k}{z}, zmm1","vpabsb zmm2/m512, {k}{z}, zmm1","EVEX.512.66.0F38.WIG 1C /r","V","V","AVX512BW","scale64","w,r,r","","" +"VPABSD xmm1, xmm2/m128","VPABSD xmm2/m128, xmm1","vpabsd xmm2/m128, xmm1","VEX.128.66.0F38.WIG 1E /r","V","V","AVX","","w,r","","" +"VPABSD xmm1, {k}{z}, xmm2/m128/m32bcst","VPABSD xmm2/m128/m32bcst, {k}{z}, xmm1","vpabsd xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F38.W0 1E /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r","","" +"VPABSD ymm1, ymm2/m256","VPABSD ymm2/m256, ymm1","vpabsd ymm2/m256, ymm1","VEX.256.66.0F38.WIG 1E /r","V","V","AVX2","","w,r","","" +"VPABSD ymm1, {k}{z}, ymm2/m256/m32bcst","VPABSD ymm2/m256/m32bcst, {k}{z}, ymm1","vpabsd ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F38.W0 1E /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r","","" +"VPABSD zmm1, {k}{z}, zmm2/m512/m32bcst","VPABSD zmm2/m512/m32bcst, {k}{z}, zmm1","vpabsd zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W0 1E /r","V","V","AVX512F","bscale4,scale64","w,r,r","","" +"VPABSQ xmm1, {k}{z}, xmm2/m128/m64bcst","VPABSQ xmm2/m128/m64bcst, {k}{z}, xmm1","vpabsq xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F38.W1 1F /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r","","" +"VPABSQ ymm1, {k}{z}, ymm2/m256/m64bcst","VPABSQ ymm2/m256/m64bcst, {k}{z}, ymm1","vpabsq ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F38.W1 1F /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r","","" +"VPABSQ zmm1, {k}{z}, zmm2/m512/m64bcst","VPABSQ zmm2/m512/m64bcst, {k}{z}, zmm1","vpabsq zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W1 1F /r","V","V","AVX512F","bscale8,scale64","w,r,r","","" +"VPABSW xmm1, xmm2/m128","VPABSW xmm2/m128, xmm1","vpabsw xmm2/m128, xmm1","VEX.128.66.0F38.WIG 1D /r","V","V","AVX","","w,r","","" +"VPABSW xmm1, {k}{z}, xmm2/m128","VPABSW xmm2/m128, {k}{z}, xmm1","vpabsw xmm2/m128, {k}{z}, xmm1","EVEX.128.66.0F38.WIG 1D /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r","","" +"VPABSW ymm1, ymm2/m256","VPABSW ymm2/m256, ymm1","vpabsw ymm2/m256, ymm1","VEX.256.66.0F38.WIG 1D /r","V","V","AVX2","","w,r","","" +"VPABSW ymm1, {k}{z}, ymm2/m256","VPABSW ymm2/m256, {k}{z}, ymm1","vpabsw ymm2/m256, {k}{z}, ymm1","EVEX.256.66.0F38.WIG 1D /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r","","" +"VPABSW zmm1, {k}{z}, zmm2/m512","VPABSW zmm2/m512, {k}{z}, zmm1","vpabsw zmm2/m512, {k}{z}, zmm1","EVEX.512.66.0F38.WIG 1D /r","V","V","AVX512BW","scale64","w,r,r","","" +"VPACKSSDW xmm1, xmmV, xmm2/m128","VPACKSSDW xmm2/m128, xmmV, xmm1","vpackssdw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 6B /r","V","V","AVX","","w,r,r","","" +"VPACKSSDW xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPACKSSDW xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpackssdw xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W0 6B /r","V","V","AVX512BW+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPACKSSDW ymm1, ymmV, ymm2/m256","VPACKSSDW ymm2/m256, ymmV, ymm1","vpackssdw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 6B /r","V","V","AVX2","","w,r,r","","" +"VPACKSSDW ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPACKSSDW ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpackssdw ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W0 6B /r","V","V","AVX512BW+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPACKSSDW zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPACKSSDW zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpackssdw zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W0 6B /r","V","V","AVX512BW","bscale4,scale64","w,r,r,r","","" +"VPACKSSWB xmm1, xmmV, xmm2/m128","VPACKSSWB xmm2/m128, xmmV, xmm1","vpacksswb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 63 /r","V","V","AVX","","w,r,r","","" +"VPACKSSWB xmm1, {k}{z}, xmmV, xmm2/m128","VPACKSSWB xmm2/m128, xmmV, {k}{z}, xmm1","vpacksswb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG 63 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPACKSSWB ymm1, ymmV, ymm2/m256","VPACKSSWB ymm2/m256, ymmV, ymm1","vpacksswb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 63 /r","V","V","AVX2","","w,r,r","","" +"VPACKSSWB ymm1, {k}{z}, ymmV, ymm2/m256","VPACKSSWB ymm2/m256, ymmV, {k}{z}, ymm1","vpacksswb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG 63 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPACKSSWB zmm1, {k}{z}, zmmV, zmm2/m512","VPACKSSWB zmm2/m512, zmmV, {k}{z}, zmm1","vpacksswb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG 63 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPACKUSDW xmm1, xmmV, xmm2/m128","VPACKUSDW xmm2/m128, xmmV, xmm1","vpackusdw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 2B /r","V","V","AVX","","w,r,r","","" +"VPACKUSDW xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPACKUSDW xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpackusdw xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 2B /r","V","V","AVX512BW+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPACKUSDW ymm1, ymmV, ymm2/m256","VPACKUSDW ymm2/m256, ymmV, ymm1","vpackusdw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 2B /r","V","V","AVX2","","w,r,r","","" +"VPACKUSDW ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPACKUSDW ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpackusdw ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 2B /r","V","V","AVX512BW+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPACKUSDW zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPACKUSDW zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpackusdw zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 2B /r","V","V","AVX512BW","bscale4,scale64","w,r,r,r","","" +"VPACKUSWB xmm1, xmmV, xmm2/m128","VPACKUSWB xmm2/m128, xmmV, xmm1","vpackuswb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 67 /r","V","V","AVX","","w,r,r","","" +"VPACKUSWB xmm1, {k}{z}, xmmV, xmm2/m128","VPACKUSWB xmm2/m128, xmmV, {k}{z}, xmm1","vpackuswb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG 67 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPACKUSWB ymm1, ymmV, ymm2/m256","VPACKUSWB ymm2/m256, ymmV, ymm1","vpackuswb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 67 /r","V","V","AVX2","","w,r,r","","" +"VPACKUSWB ymm1, {k}{z}, ymmV, ymm2/m256","VPACKUSWB ymm2/m256, ymmV, {k}{z}, ymm1","vpackuswb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG 67 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPACKUSWB zmm1, {k}{z}, zmmV, zmm2/m512","VPACKUSWB zmm2/m512, zmmV, {k}{z}, zmm1","vpackuswb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG 67 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPADDB xmm1, xmmV, xmm2/m128","VPADDB xmm2/m128, xmmV, xmm1","vpaddb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG FC /r","V","V","AVX","","w,r,r","","" +"VPADDB xmm1, {k}{z}, xmmV, xmm2/m128","VPADDB xmm2/m128, xmmV, {k}{z}, xmm1","vpaddb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG FC /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPADDB ymm1, ymmV, ymm2/m256","VPADDB ymm2/m256, ymmV, ymm1","vpaddb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG FC /r","V","V","AVX2","","w,r,r","","" +"VPADDB ymm1, {k}{z}, ymmV, ymm2/m256","VPADDB ymm2/m256, ymmV, {k}{z}, ymm1","vpaddb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG FC /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPADDB zmm1, {k}{z}, zmmV, zmm2/m512","VPADDB zmm2/m512, zmmV, {k}{z}, zmm1","vpaddb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG FC /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPADDD xmm1, xmmV, xmm2/m128","VPADDD xmm2/m128, xmmV, xmm1","vpaddd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG FE /r","V","V","AVX","","w,r,r","","" +"VPADDD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPADDD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpaddd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W0 FE /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPADDD ymm1, ymmV, ymm2/m256","VPADDD ymm2/m256, ymmV, ymm1","vpaddd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG FE /r","V","V","AVX2","","w,r,r","","" +"VPADDD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPADDD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpaddd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W0 FE /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPADDD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPADDD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpaddd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W0 FE /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPADDQ xmm1, xmmV, xmm2/m128","VPADDQ xmm2/m128, xmmV, xmm1","vpaddq xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG D4 /r","V","V","AVX","","w,r,r","","" +"VPADDQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPADDQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpaddq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 D4 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPADDQ ymm1, ymmV, ymm2/m256","VPADDQ ymm2/m256, ymmV, ymm1","vpaddq ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG D4 /r","V","V","AVX2","","w,r,r","","" +"VPADDQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPADDQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpaddq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 D4 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPADDQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPADDQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpaddq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 D4 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPADDSB xmm1, xmmV, xmm2/m128","VPADDSB xmm2/m128, xmmV, xmm1","vpaddsb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG EC /r","V","V","AVX","","w,r,r","","" +"VPADDSB xmm1, {k}{z}, xmmV, xmm2/m128","VPADDSB xmm2/m128, xmmV, {k}{z}, xmm1","vpaddsb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG EC /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPADDSB ymm1, ymmV, ymm2/m256","VPADDSB ymm2/m256, ymmV, ymm1","vpaddsb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG EC /r","V","V","AVX2","","w,r,r","","" +"VPADDSB ymm1, {k}{z}, ymmV, ymm2/m256","VPADDSB ymm2/m256, ymmV, {k}{z}, ymm1","vpaddsb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG EC /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPADDSB zmm1, {k}{z}, zmmV, zmm2/m512","VPADDSB zmm2/m512, zmmV, {k}{z}, zmm1","vpaddsb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG EC /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPADDSW xmm1, xmmV, xmm2/m128","VPADDSW xmm2/m128, xmmV, xmm1","vpaddsw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG ED /r","V","V","AVX","","w,r,r","","" +"VPADDSW xmm1, {k}{z}, xmmV, xmm2/m128","VPADDSW xmm2/m128, xmmV, {k}{z}, xmm1","vpaddsw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG ED /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPADDSW ymm1, ymmV, ymm2/m256","VPADDSW ymm2/m256, ymmV, ymm1","vpaddsw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG ED /r","V","V","AVX2","","w,r,r","","" +"VPADDSW ymm1, {k}{z}, ymmV, ymm2/m256","VPADDSW ymm2/m256, ymmV, {k}{z}, ymm1","vpaddsw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG ED /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPADDSW zmm1, {k}{z}, zmmV, zmm2/m512","VPADDSW zmm2/m512, zmmV, {k}{z}, zmm1","vpaddsw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG ED /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPADDUSB xmm1, xmmV, xmm2/m128","VPADDUSB xmm2/m128, xmmV, xmm1","vpaddusb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG DC /r","V","V","AVX","","w,r,r","","" +"VPADDUSB xmm1, {k}{z}, xmmV, xmm2/m128","VPADDUSB xmm2/m128, xmmV, {k}{z}, xmm1","vpaddusb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG DC /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPADDUSB ymm1, ymmV, ymm2/m256","VPADDUSB ymm2/m256, ymmV, ymm1","vpaddusb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG DC /r","V","V","AVX2","","w,r,r","","" +"VPADDUSB ymm1, {k}{z}, ymmV, ymm2/m256","VPADDUSB ymm2/m256, ymmV, {k}{z}, ymm1","vpaddusb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG DC /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPADDUSB zmm1, {k}{z}, zmmV, zmm2/m512","VPADDUSB zmm2/m512, zmmV, {k}{z}, zmm1","vpaddusb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG DC /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPADDUSW xmm1, xmmV, xmm2/m128","VPADDUSW xmm2/m128, xmmV, xmm1","vpaddusw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG DD /r","V","V","AVX","","w,r,r","","" +"VPADDUSW xmm1, {k}{z}, xmmV, xmm2/m128","VPADDUSW xmm2/m128, xmmV, {k}{z}, xmm1","vpaddusw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG DD /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPADDUSW ymm1, ymmV, ymm2/m256","VPADDUSW ymm2/m256, ymmV, ymm1","vpaddusw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG DD /r","V","V","AVX2","","w,r,r","","" +"VPADDUSW ymm1, {k}{z}, ymmV, ymm2/m256","VPADDUSW ymm2/m256, ymmV, {k}{z}, ymm1","vpaddusw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG DD /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPADDUSW zmm1, {k}{z}, zmmV, zmm2/m512","VPADDUSW zmm2/m512, zmmV, {k}{z}, zmm1","vpaddusw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG DD /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPADDW xmm1, xmmV, xmm2/m128","VPADDW xmm2/m128, xmmV, xmm1","vpaddw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG FD /r","V","V","AVX","","w,r,r","","" +"VPADDW xmm1, {k}{z}, xmmV, xmm2/m128","VPADDW xmm2/m128, xmmV, {k}{z}, xmm1","vpaddw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG FD /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPADDW ymm1, ymmV, ymm2/m256","VPADDW ymm2/m256, ymmV, ymm1","vpaddw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG FD /r","V","V","AVX2","","w,r,r","","" +"VPADDW ymm1, {k}{z}, ymmV, ymm2/m256","VPADDW ymm2/m256, ymmV, {k}{z}, ymm1","vpaddw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG FD /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPADDW zmm1, {k}{z}, zmmV, zmm2/m512","VPADDW zmm2/m512, zmmV, {k}{z}, zmm1","vpaddw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG FD /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPALIGNR xmm1, xmmV, xmm2/m128, imm8u","VPALIGNR imm8u, xmm2/m128, xmmV, xmm1","vpalignr imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.WIG 0F /r ib","V","V","AVX","","w,r,r,r","","" +"VPALIGNR xmm1, {k}{z}, xmmV, xmm2/m128, imm8u","VPALIGNR imm8u, xmm2/m128, xmmV, {k}{z}, xmm1","vpalignr imm8u, xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F3A.WIG 0F /r ib","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r,r","","" +"VPALIGNR ymm1, ymmV, ymm2/m256, imm8u","VPALIGNR imm8u, ymm2/m256, ymmV, ymm1","vpalignr imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.WIG 0F /r ib","V","V","AVX2","","w,r,r,r","","" +"VPALIGNR ymm1, {k}{z}, ymmV, ymm2/m256, imm8u","VPALIGNR imm8u, ymm2/m256, ymmV, {k}{z}, ymm1","vpalignr imm8u, ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.WIG 0F /r ib","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r,r","","" +"VPALIGNR zmm1, {k}{z}, zmmV, zmm2/m512, imm8u","VPALIGNR imm8u, zmm2/m512, zmmV, {k}{z}, zmm1","vpalignr imm8u, zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.WIG 0F /r ib","V","V","AVX512BW","scale64","w,r,r,r,r","","" +"VPAND xmm1, xmmV, xmm2/m128","VPAND xmm2/m128, xmmV, xmm1","vpand xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG DB /r","V","V","AVX","","w,r,r","","" +"VPAND ymm1, ymmV, ymm2/m256","VPAND ymm2/m256, ymmV, ymm1","vpand ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG DB /r","V","V","AVX2","","w,r,r","","" +"VPANDD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPANDD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpandd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W0 DB /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPANDD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPANDD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpandd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W0 DB /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPANDD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPANDD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpandd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W0 DB /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPANDN xmm1, xmmV, xmm2/m128","VPANDN xmm2/m128, xmmV, xmm1","vpandn xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG DF /r","V","V","AVX","","w,r,r","","" +"VPANDN ymm1, ymmV, ymm2/m256","VPANDN ymm2/m256, ymmV, ymm1","vpandn ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG DF /r","V","V","AVX2","","w,r,r","","" +"VPANDND xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPANDND xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpandnd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W0 DF /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPANDND ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPANDND ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpandnd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W0 DF /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPANDND zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPANDND zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpandnd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W0 DF /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPANDNQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPANDNQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpandnq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 DF /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPANDNQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPANDNQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpandnq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 DF /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPANDNQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPANDNQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpandnq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 DF /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPANDQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPANDQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpandq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 DB /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPANDQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPANDQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpandq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 DB /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPANDQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPANDQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpandq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 DB /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPAVGB xmm1, xmmV, xmm2/m128","VPAVGB xmm2/m128, xmmV, xmm1","vpavgb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG E0 /r","V","V","AVX","","w,r,r","","" +"VPAVGB xmm1, {k}{z}, xmmV, xmm2/m128","VPAVGB xmm2/m128, xmmV, {k}{z}, xmm1","vpavgb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG E0 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPAVGB ymm1, ymmV, ymm2/m256","VPAVGB ymm2/m256, ymmV, ymm1","vpavgb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG E0 /r","V","V","AVX2","","w,r,r","","" +"VPAVGB ymm1, {k}{z}, ymmV, ymm2/m256","VPAVGB ymm2/m256, ymmV, {k}{z}, ymm1","vpavgb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG E0 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPAVGB zmm1, {k}{z}, zmmV, zmm2/m512","VPAVGB zmm2/m512, zmmV, {k}{z}, zmm1","vpavgb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG E0 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPAVGW xmm1, xmmV, xmm2/m128","VPAVGW xmm2/m128, xmmV, xmm1","vpavgw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG E3 /r","V","V","AVX","","w,r,r","","" +"VPAVGW xmm1, {k}{z}, xmmV, xmm2/m128","VPAVGW xmm2/m128, xmmV, {k}{z}, xmm1","vpavgw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG E3 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPAVGW ymm1, ymmV, ymm2/m256","VPAVGW ymm2/m256, ymmV, ymm1","vpavgw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG E3 /r","V","V","AVX2","","w,r,r","","" +"VPAVGW ymm1, {k}{z}, ymmV, ymm2/m256","VPAVGW ymm2/m256, ymmV, {k}{z}, ymm1","vpavgw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG E3 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPAVGW zmm1, {k}{z}, zmmV, zmm2/m512","VPAVGW zmm2/m512, zmmV, {k}{z}, zmm1","vpavgw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG E3 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPBLENDD xmm1, xmmV, xmm2/m128, imm8u","VPBLENDD imm8u, xmm2/m128, xmmV, xmm1","vpblendd imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 02 /r ib","V","V","AVX2","","w,r,r,r","","" +"VPBLENDD ymm1, ymmV, ymm2/m256, imm8u","VPBLENDD imm8u, ymm2/m256, ymmV, ymm1","vpblendd imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 02 /r ib","V","V","AVX2","","w,r,r,r","","" +"VPBLENDMB xmm1, {k}{z}, xmmV, xmm2/m128","VPBLENDMB xmm2/m128, xmmV, {k}{z}, xmm1","vpblendmb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 66 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPBLENDMB ymm1, {k}{z}, ymmV, ymm2/m256","VPBLENDMB ymm2/m256, ymmV, {k}{z}, ymm1","vpblendmb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 66 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPBLENDMB zmm1, {k}{z}, zmmV, zmm2/m512","VPBLENDMB zmm2/m512, zmmV, {k}{z}, zmm1","vpblendmb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 66 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPBLENDMD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPBLENDMD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpblendmd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 64 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPBLENDMD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPBLENDMD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpblendmd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 64 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPBLENDMD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPBLENDMD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpblendmd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 64 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPBLENDMQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPBLENDMQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpblendmq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 64 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPBLENDMQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPBLENDMQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpblendmq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 64 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPBLENDMQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPBLENDMQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpblendmq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 64 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPBLENDMW xmm1, {k}{z}, xmmV, xmm2/m128","VPBLENDMW xmm2/m128, xmmV, {k}{z}, xmm1","vpblendmw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 66 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPBLENDMW ymm1, {k}{z}, ymmV, ymm2/m256","VPBLENDMW ymm2/m256, ymmV, {k}{z}, ymm1","vpblendmw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 66 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPBLENDMW zmm1, {k}{z}, zmmV, zmm2/m512","VPBLENDMW zmm2/m512, zmmV, {k}{z}, zmm1","vpblendmw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 66 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPBLENDVB xmm1, xmmV, xmm2/m128, xmmIH","VPBLENDVB xmmIH, xmm2/m128, xmmV, xmm1","vpblendvb xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 4C /r /is4","V","V","AVX","","w,r,r,r","","" +"VPBLENDVB ymm1, ymmV, ymm2/m256, ymmIH","VPBLENDVB ymmIH, ymm2/m256, ymmV, ymm1","vpblendvb ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 4C /r /is4","V","V","AVX2","","w,r,r,r","","" +"VPBLENDW xmm1, xmmV, xmm2/m128, imm8u","VPBLENDW imm8u, xmm2/m128, xmmV, xmm1","vpblendw imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.WIG 0E /r ib","V","V","AVX","","w,r,r,r","","" +"VPBLENDW ymm1, ymmV, ymm2/m256, imm8u","VPBLENDW imm8u, ymm2/m256, ymmV, ymm1","vpblendw imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.WIG 0E /r ib","V","V","AVX2","","w,r,r,r","","" +"VPBROADCASTB xmm1, {k}{z}, rmr32","VPBROADCASTB rmr32, {k}{z}, xmm1","vpbroadcastb rmr32, {k}{z}, xmm1","EVEX.128.66.0F38.W0 7A /r","V","V","AVX512BW+AVX512VL","modrm_regonly","w,r,r","","" +"VPBROADCASTB ymm1, {k}{z}, rmr32","VPBROADCASTB rmr32, {k}{z}, ymm1","vpbroadcastb rmr32, {k}{z}, ymm1","EVEX.256.66.0F38.W0 7A /r","V","V","AVX512BW+AVX512VL","modrm_regonly","w,r,r","","" +"VPBROADCASTB zmm1, {k}{z}, rmr32","VPBROADCASTB rmr32, {k}{z}, zmm1","vpbroadcastb rmr32, {k}{z}, zmm1","EVEX.512.66.0F38.W0 7A /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"VPBROADCASTB xmm1, xmm2/m8","VPBROADCASTB xmm2/m8, xmm1","vpbroadcastb xmm2/m8, xmm1","VEX.128.66.0F38.W0 78 /r","V","V","AVX2","","w,r","","" +"VPBROADCASTB ymm1, xmm2/m8","VPBROADCASTB xmm2/m8, ymm1","vpbroadcastb xmm2/m8, ymm1","VEX.256.66.0F38.W0 78 /r","V","V","AVX2","","w,r","","" +"VPBROADCASTB xmm1, {k}{z}, xmm2/m8","VPBROADCASTB xmm2/m8, {k}{z}, xmm1","vpbroadcastb xmm2/m8, {k}{z}, xmm1","EVEX.128.66.0F38.W0 78 /r","V","V","AVX512BW+AVX512VL","scale1","w,r,r","","" +"VPBROADCASTB ymm1, {k}{z}, xmm2/m8","VPBROADCASTB xmm2/m8, {k}{z}, ymm1","vpbroadcastb xmm2/m8, {k}{z}, ymm1","EVEX.256.66.0F38.W0 78 /r","V","V","AVX512BW+AVX512VL","scale1","w,r,r","","" +"VPBROADCASTB zmm1, {k}{z}, xmm2/m8","VPBROADCASTB xmm2/m8, {k}{z}, zmm1","vpbroadcastb xmm2/m8, {k}{z}, zmm1","EVEX.512.66.0F38.W0 78 /r","V","V","AVX512BW","scale1","w,r,r","","" +"VPBROADCASTD xmm1, {k}{z}, rmr32","VPBROADCASTD rmr32, {k}{z}, xmm1","vpbroadcastd rmr32, {k}{z}, xmm1","EVEX.128.66.0F38.W0 7C /r","V","V","AVX512F+AVX512VL","modrm_regonly","w,r,r","","" +"VPBROADCASTD ymm1, {k}{z}, rmr32","VPBROADCASTD rmr32, {k}{z}, ymm1","vpbroadcastd rmr32, {k}{z}, ymm1","EVEX.256.66.0F38.W0 7C /r","V","V","AVX512F+AVX512VL","modrm_regonly","w,r,r","","" +"VPBROADCASTD zmm1, {k}{z}, rmr32","VPBROADCASTD rmr32, {k}{z}, zmm1","vpbroadcastd rmr32, {k}{z}, zmm1","EVEX.512.66.0F38.W0 7C /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"VPBROADCASTD xmm1, xmm2/m32","VPBROADCASTD xmm2/m32, xmm1","vpbroadcastd xmm2/m32, xmm1","VEX.128.66.0F38.W0 58 /r","V","V","AVX2","","w,r","","" +"VPBROADCASTD ymm1, xmm2/m32","VPBROADCASTD xmm2/m32, ymm1","vpbroadcastd xmm2/m32, ymm1","VEX.256.66.0F38.W0 58 /r","V","V","AVX2","","w,r","","" +"VPBROADCASTD xmm1, {k}{z}, xmm2/m32","VPBROADCASTD xmm2/m32, {k}{z}, xmm1","vpbroadcastd xmm2/m32, {k}{z}, xmm1","EVEX.128.66.0F38.W0 58 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPBROADCASTD ymm1, {k}{z}, xmm2/m32","VPBROADCASTD xmm2/m32, {k}{z}, ymm1","vpbroadcastd xmm2/m32, {k}{z}, ymm1","EVEX.256.66.0F38.W0 58 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPBROADCASTD zmm1, {k}{z}, xmm2/m32","VPBROADCASTD xmm2/m32, {k}{z}, zmm1","vpbroadcastd xmm2/m32, {k}{z}, zmm1","EVEX.512.66.0F38.W0 58 /r","V","V","AVX512F","scale4","w,r,r","","" +"VPBROADCASTMB2Q xmm1, k2","VPBROADCASTMB2Q k2, xmm1","vpbroadcastmb2q k2, xmm1","EVEX.128.F3.0F38.W1 2A /r","V","V","AVX512CD+AVX512VL","modrm_regonly","w,r","","" +"VPBROADCASTMB2Q ymm1, k2","VPBROADCASTMB2Q k2, ymm1","vpbroadcastmb2q k2, ymm1","EVEX.256.F3.0F38.W1 2A /r","V","V","AVX512CD+AVX512VL","modrm_regonly","w,r","","" +"VPBROADCASTMB2Q zmm1, k2","VPBROADCASTMB2Q k2, zmm1","vpbroadcastmb2q k2, zmm1","EVEX.512.F3.0F38.W1 2A /r","V","V","AVX512CD","modrm_regonly","w,r","","" +"VPBROADCASTMW2D xmm1, k2","VPBROADCASTMW2D k2, xmm1","vpbroadcastmw2d k2, xmm1","EVEX.128.F3.0F38.W0 3A /r","V","V","AVX512CD+AVX512VL","modrm_regonly","w,r","","" +"VPBROADCASTMW2D ymm1, k2","VPBROADCASTMW2D k2, ymm1","vpbroadcastmw2d k2, ymm1","EVEX.256.F3.0F38.W0 3A /r","V","V","AVX512CD+AVX512VL","modrm_regonly","w,r","","" +"VPBROADCASTMW2D zmm1, k2","VPBROADCASTMW2D k2, zmm1","vpbroadcastmw2d k2, zmm1","EVEX.512.F3.0F38.W0 3A /r","V","V","AVX512CD","modrm_regonly","w,r","","" +"VPBROADCASTQ xmm1, {k}{z}, rmr64","VPBROADCASTQ rmr64, {k}{z}, xmm1","vpbroadcastq rmr64, {k}{z}, xmm1","EVEX.128.66.0F38.W1 7C /r","N.S.","V","AVX512F+AVX512VL","modrm_regonly","w,r,r","","" +"VPBROADCASTQ ymm1, {k}{z}, rmr64","VPBROADCASTQ rmr64, {k}{z}, ymm1","vpbroadcastq rmr64, {k}{z}, ymm1","EVEX.256.66.0F38.W1 7C /r","N.S.","V","AVX512F+AVX512VL","modrm_regonly","w,r,r","","" +"VPBROADCASTQ zmm1, {k}{z}, rmr64","VPBROADCASTQ rmr64, {k}{z}, zmm1","vpbroadcastq rmr64, {k}{z}, zmm1","EVEX.512.66.0F38.W1 7C /r","N.S.","V","AVX512F","modrm_regonly","w,r,r","","" +"VPBROADCASTQ xmm1, xmm2/m64","VPBROADCASTQ xmm2/m64, xmm1","vpbroadcastq xmm2/m64, xmm1","VEX.128.66.0F38.W0 59 /r","V","V","AVX2","","w,r","","" +"VPBROADCASTQ ymm1, xmm2/m64","VPBROADCASTQ xmm2/m64, ymm1","vpbroadcastq xmm2/m64, ymm1","VEX.256.66.0F38.W0 59 /r","V","V","AVX2","","w,r","","" +"VPBROADCASTQ xmm1, {k}{z}, xmm2/m64","VPBROADCASTQ xmm2/m64, {k}{z}, xmm1","vpbroadcastq xmm2/m64, {k}{z}, xmm1","EVEX.128.66.0F38.W1 59 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPBROADCASTQ ymm1, {k}{z}, xmm2/m64","VPBROADCASTQ xmm2/m64, {k}{z}, ymm1","vpbroadcastq xmm2/m64, {k}{z}, ymm1","EVEX.256.66.0F38.W1 59 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPBROADCASTQ zmm1, {k}{z}, xmm2/m64","VPBROADCASTQ xmm2/m64, {k}{z}, zmm1","vpbroadcastq xmm2/m64, {k}{z}, zmm1","EVEX.512.66.0F38.W1 59 /r","V","V","AVX512F","scale8","w,r,r","","" +"VPBROADCASTW xmm1, {k}{z}, rmr32","VPBROADCASTW rmr32, {k}{z}, xmm1","vpbroadcastw rmr32, {k}{z}, xmm1","EVEX.128.66.0F38.W0 7B /r","V","V","AVX512BW+AVX512VL","modrm_regonly","w,r,r","","" +"VPBROADCASTW ymm1, {k}{z}, rmr32","VPBROADCASTW rmr32, {k}{z}, ymm1","vpbroadcastw rmr32, {k}{z}, ymm1","EVEX.256.66.0F38.W0 7B /r","V","V","AVX512BW+AVX512VL","modrm_regonly","w,r,r","","" +"VPBROADCASTW zmm1, {k}{z}, rmr32","VPBROADCASTW rmr32, {k}{z}, zmm1","vpbroadcastw rmr32, {k}{z}, zmm1","EVEX.512.66.0F38.W0 7B /r","V","V","AVX512BW","modrm_regonly","w,r,r","","" +"VPBROADCASTW xmm1, xmm2/m16","VPBROADCASTW xmm2/m16, xmm1","vpbroadcastw xmm2/m16, xmm1","VEX.128.66.0F38.W0 79 /r","V","V","AVX2","","w,r","","" +"VPBROADCASTW ymm1, xmm2/m16","VPBROADCASTW xmm2/m16, ymm1","vpbroadcastw xmm2/m16, ymm1","VEX.256.66.0F38.W0 79 /r","V","V","AVX2","","w,r","","" +"VPBROADCASTW xmm1, {k}{z}, xmm2/m16","VPBROADCASTW xmm2/m16, {k}{z}, xmm1","vpbroadcastw xmm2/m16, {k}{z}, xmm1","EVEX.128.66.0F38.W0 79 /r","V","V","AVX512BW+AVX512VL","scale2","w,r,r","","" +"VPBROADCASTW ymm1, {k}{z}, xmm2/m16","VPBROADCASTW xmm2/m16, {k}{z}, ymm1","vpbroadcastw xmm2/m16, {k}{z}, ymm1","EVEX.256.66.0F38.W0 79 /r","V","V","AVX512BW+AVX512VL","scale2","w,r,r","","" +"VPBROADCASTW zmm1, {k}{z}, xmm2/m16","VPBROADCASTW xmm2/m16, {k}{z}, zmm1","vpbroadcastw xmm2/m16, {k}{z}, zmm1","EVEX.512.66.0F38.W0 79 /r","V","V","AVX512BW","scale2","w,r,r","","" +"VPCLMULQDQ xmm1, xmmV, xmm2/m128, imm8u","VPCLMULQDQ imm8u, xmm2/m128, xmmV, xmm1","vpclmulqdq imm8u, xmm2/m128, xmmV, xmm1","EVEX.NDS.128.66.0F3A.WIG 44 /r ib","V","V","VPCLMULQDQ+AVX512VL","scale16","w,r,r,r","","" +"VPCLMULQDQ xmm1, xmmV, xmm2/m128, imm8u","VPCLMULQDQ imm8u, xmm2/m128, xmmV, xmm1","vpclmulqdq imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.WIG 44 /r ib","V","V","PCLMULQDQ+AVX","","w,r,r,r","","" +"VPCLMULQDQ ymm1, ymmV, ymm2/m256, imm8u","VPCLMULQDQ imm8u, ymm2/m256, ymmV, ymm1","vpclmulqdq imm8u, ymm2/m256, ymmV, ymm1","EVEX.NDS.256.66.0F3A.WIG 44 /r ib","V","V","VPCLMULQDQ+AVX512VL","scale32","w,r,r,r","","" +"VPCLMULQDQ ymm1, ymmV, ymm2/m256, imm8u","VPCLMULQDQ imm8u, ymm2/m256, ymmV, ymm1","vpclmulqdq imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.WIG 44 /r ib","V","V","VPCLMULQDQ","","w,r,r,r","","" +"VPCLMULQDQ zmm1, zmmV, zmm2/m512, imm8u","VPCLMULQDQ imm8u, zmm2/m512, zmmV, zmm1","vpclmulqdq imm8u, zmm2/m512, zmmV, zmm1","EVEX.NDS.512.66.0F3A.WIG 44 /r ib","V","V","VPCLMULQDQ+AVX512F","scale64","w,r,r,r","","" +"VPCMOV xmm1, xmmV, xmmIH, xmm2/m128","VPCMOV xmm2/m128, xmmIH, xmmV, xmm1","vpcmov xmm2/m128, xmmIH, xmmV, xmm1","XOP.NDS.128.08.W1 A2 /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPCMOV xmm1, xmmV, xmm2/m128, xmmIH","VPCMOV xmmIH, xmm2/m128, xmmV, xmm1","vpcmov xmmIH, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 A2 /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPCMOV ymm1, ymmV, ymmIH, ymm2/m256","VPCMOV ymm2/m256, ymmIH, ymmV, ymm1","vpcmov ymm2/m256, ymmIH, ymmV, ymm1","XOP.NDS.256.08.W1 A2 /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPCMOV ymm1, ymmV, ymm2/m256, ymmIH","VPCMOV ymmIH, ymm2/m256, ymmV, ymm1","vpcmov ymmIH, ymm2/m256, ymmV, ymm1","XOP.NDS.256.08.W0 A2 /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPCMPB k1, {k}, xmmV, xmm2/m128, imm8u","VPCMPB imm8u, xmm2/m128, xmmV, {k}, k1","vpcmpb imm8u, xmm2/m128, xmmV, {k}, k1","EVEX.NDS.128.66.0F3A.W0 3F /r ib","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r,r","","" +"VPCMPB k1, {k}, ymmV, ymm2/m256, imm8u","VPCMPB imm8u, ymm2/m256, ymmV, {k}, k1","vpcmpb imm8u, ymm2/m256, ymmV, {k}, k1","EVEX.NDS.256.66.0F3A.W0 3F /r ib","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r,r","","" +"VPCMPB k1, {k}, zmmV, zmm2/m512, imm8u","VPCMPB imm8u, zmm2/m512, zmmV, {k}, k1","vpcmpb imm8u, zmm2/m512, zmmV, {k}, k1","EVEX.NDS.512.66.0F3A.W0 3F /r ib","V","V","AVX512BW","scale64","w,r,r,r,r","","" +"VPCMPD k1, {k}, xmmV, xmm2/m128/m32bcst, imm8u","VPCMPD imm8u, xmm2/m128/m32bcst, xmmV, {k}, k1","vpcmpd imm8u, xmm2/m128/m32bcst, xmmV, {k}, k1","EVEX.NDS.128.66.0F3A.W0 1F /r ib","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r,r","","" +"VPCMPD k1, {k}, ymmV, ymm2/m256/m32bcst, imm8u","VPCMPD imm8u, ymm2/m256/m32bcst, ymmV, {k}, k1","vpcmpd imm8u, ymm2/m256/m32bcst, ymmV, {k}, k1","EVEX.NDS.256.66.0F3A.W0 1F /r ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r,r","","" +"VPCMPD k1, {k}, zmmV, zmm2/m512/m32bcst, imm8u","VPCMPD imm8u, zmm2/m512/m32bcst, zmmV, {k}, k1","vpcmpd imm8u, zmm2/m512/m32bcst, zmmV, {k}, k1","EVEX.NDS.512.66.0F3A.W0 1F /r ib","V","V","AVX512F","bscale4,scale64","w,r,r,r,r","","" +"VPCMPEQB xmm1, xmmV, xmm2/m128","VPCMPEQB xmm2/m128, xmmV, xmm1","vpcmpeqb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 74 /r","V","V","AVX","","w,r,r","","" +"VPCMPEQB k1, {k}, xmmV, xmm2/m128","VPCMPEQB xmm2/m128, xmmV, {k}, k1","vpcmpeqb xmm2/m128, xmmV, {k}, k1","EVEX.NDS.128.66.0F.WIG 74 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPCMPEQB ymm1, ymmV, ymm2/m256","VPCMPEQB ymm2/m256, ymmV, ymm1","vpcmpeqb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 74 /r","V","V","AVX2","","w,r,r","","" +"VPCMPEQB k1, {k}, ymmV, ymm2/m256","VPCMPEQB ymm2/m256, ymmV, {k}, k1","vpcmpeqb ymm2/m256, ymmV, {k}, k1","EVEX.NDS.256.66.0F.WIG 74 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPCMPEQB k1, {k}, zmmV, zmm2/m512","VPCMPEQB zmm2/m512, zmmV, {k}, k1","vpcmpeqb zmm2/m512, zmmV, {k}, k1","EVEX.NDS.512.66.0F.WIG 74 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPCMPEQD xmm1, xmmV, xmm2/m128","VPCMPEQD xmm2/m128, xmmV, xmm1","vpcmpeqd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 76 /r","V","V","AVX","","w,r,r","","" +"VPCMPEQD k1, {k}, xmmV, xmm2/m128/m32bcst","VPCMPEQD xmm2/m128/m32bcst, xmmV, {k}, k1","vpcmpeqd xmm2/m128/m32bcst, xmmV, {k}, k1","EVEX.NDS.128.66.0F.W0 76 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPCMPEQD ymm1, ymmV, ymm2/m256","VPCMPEQD ymm2/m256, ymmV, ymm1","vpcmpeqd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 76 /r","V","V","AVX2","","w,r,r","","" +"VPCMPEQD k1, {k}, ymmV, ymm2/m256/m32bcst","VPCMPEQD ymm2/m256/m32bcst, ymmV, {k}, k1","vpcmpeqd ymm2/m256/m32bcst, ymmV, {k}, k1","EVEX.NDS.256.66.0F.W0 76 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPCMPEQD k1, {k}, zmmV, zmm2/m512/m32bcst","VPCMPEQD zmm2/m512/m32bcst, zmmV, {k}, k1","vpcmpeqd zmm2/m512/m32bcst, zmmV, {k}, k1","EVEX.NDS.512.66.0F.W0 76 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPCMPEQQ xmm1, xmmV, xmm2/m128","VPCMPEQQ xmm2/m128, xmmV, xmm1","vpcmpeqq xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 29 /r","V","V","AVX","","w,r,r","","" +"VPCMPEQQ k1, {k}, xmmV, xmm2/m128/m64bcst","VPCMPEQQ xmm2/m128/m64bcst, xmmV, {k}, k1","vpcmpeqq xmm2/m128/m64bcst, xmmV, {k}, k1","EVEX.NDS.128.66.0F38.W1 29 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPCMPEQQ ymm1, ymmV, ymm2/m256","VPCMPEQQ ymm2/m256, ymmV, ymm1","vpcmpeqq ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 29 /r","V","V","AVX2","","w,r,r","","" +"VPCMPEQQ k1, {k}, ymmV, ymm2/m256/m64bcst","VPCMPEQQ ymm2/m256/m64bcst, ymmV, {k}, k1","vpcmpeqq ymm2/m256/m64bcst, ymmV, {k}, k1","EVEX.NDS.256.66.0F38.W1 29 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPCMPEQQ k1, {k}, zmmV, zmm2/m512/m64bcst","VPCMPEQQ zmm2/m512/m64bcst, zmmV, {k}, k1","vpcmpeqq zmm2/m512/m64bcst, zmmV, {k}, k1","EVEX.NDS.512.66.0F38.W1 29 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPCMPEQW xmm1, xmmV, xmm2/m128","VPCMPEQW xmm2/m128, xmmV, xmm1","vpcmpeqw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 75 /r","V","V","AVX","","w,r,r","","" +"VPCMPEQW k1, {k}, xmmV, xmm2/m128","VPCMPEQW xmm2/m128, xmmV, {k}, k1","vpcmpeqw xmm2/m128, xmmV, {k}, k1","EVEX.NDS.128.66.0F.WIG 75 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPCMPEQW ymm1, ymmV, ymm2/m256","VPCMPEQW ymm2/m256, ymmV, ymm1","vpcmpeqw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 75 /r","V","V","AVX2","","w,r,r","","" +"VPCMPEQW k1, {k}, ymmV, ymm2/m256","VPCMPEQW ymm2/m256, ymmV, {k}, k1","vpcmpeqw ymm2/m256, ymmV, {k}, k1","EVEX.NDS.256.66.0F.WIG 75 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPCMPEQW k1, {k}, zmmV, zmm2/m512","VPCMPEQW zmm2/m512, zmmV, {k}, k1","vpcmpeqw zmm2/m512, zmmV, {k}, k1","EVEX.NDS.512.66.0F.WIG 75 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPCMPESTRI xmm1, xmm2/m128, imm8u","VPCMPESTRI imm8u, xmm2/m128, xmm1","vpcmpestri imm8u, xmm2/m128, xmm1","VEX.128.66.0F3A.WIG 61 /r ib","V","V","AVX","","r,r,r","","" +"VPCMPESTRM xmm1, xmm2/m128, imm8u","VPCMPESTRM imm8u, xmm2/m128, xmm1","vpcmpestrm imm8u, xmm2/m128, xmm1","VEX.128.66.0F3A.WIG 60 /r ib","V","V","AVX","","r,r,r","","" +"VPCMPGTB xmm1, xmmV, xmm2/m128","VPCMPGTB xmm2/m128, xmmV, xmm1","vpcmpgtb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 64 /r","V","V","AVX","","w,r,r","","" +"VPCMPGTB k1, {k}, xmmV, xmm2/m128","VPCMPGTB xmm2/m128, xmmV, {k}, k1","vpcmpgtb xmm2/m128, xmmV, {k}, k1","EVEX.NDS.128.66.0F.WIG 64 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPCMPGTB ymm1, ymmV, ymm2/m256","VPCMPGTB ymm2/m256, ymmV, ymm1","vpcmpgtb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 64 /r","V","V","AVX2","","w,r,r","","" +"VPCMPGTB k1, {k}, ymmV, ymm2/m256","VPCMPGTB ymm2/m256, ymmV, {k}, k1","vpcmpgtb ymm2/m256, ymmV, {k}, k1","EVEX.NDS.256.66.0F.WIG 64 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPCMPGTB k1, {k}, zmmV, zmm2/m512","VPCMPGTB zmm2/m512, zmmV, {k}, k1","vpcmpgtb zmm2/m512, zmmV, {k}, k1","EVEX.NDS.512.66.0F.WIG 64 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPCMPGTD xmm1, xmmV, xmm2/m128","VPCMPGTD xmm2/m128, xmmV, xmm1","vpcmpgtd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 66 /r","V","V","AVX","","w,r,r","","" +"VPCMPGTD k1, {k}, xmmV, xmm2/m128/m32bcst","VPCMPGTD xmm2/m128/m32bcst, xmmV, {k}, k1","vpcmpgtd xmm2/m128/m32bcst, xmmV, {k}, k1","EVEX.NDS.128.66.0F.W0 66 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPCMPGTD ymm1, ymmV, ymm2/m256","VPCMPGTD ymm2/m256, ymmV, ymm1","vpcmpgtd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 66 /r","V","V","AVX2","","w,r,r","","" +"VPCMPGTD k1, {k}, ymmV, ymm2/m256/m32bcst","VPCMPGTD ymm2/m256/m32bcst, ymmV, {k}, k1","vpcmpgtd ymm2/m256/m32bcst, ymmV, {k}, k1","EVEX.NDS.256.66.0F.W0 66 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPCMPGTD k1, {k}, zmmV, zmm2/m512/m32bcst","VPCMPGTD zmm2/m512/m32bcst, zmmV, {k}, k1","vpcmpgtd zmm2/m512/m32bcst, zmmV, {k}, k1","EVEX.NDS.512.66.0F.W0 66 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPCMPGTQ xmm1, xmmV, xmm2/m128","VPCMPGTQ xmm2/m128, xmmV, xmm1","vpcmpgtq xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 37 /r","V","V","AVX","","w,r,r","","" +"VPCMPGTQ k1, {k}, xmmV, xmm2/m128/m64bcst","VPCMPGTQ xmm2/m128/m64bcst, xmmV, {k}, k1","vpcmpgtq xmm2/m128/m64bcst, xmmV, {k}, k1","EVEX.NDS.128.66.0F38.W1 37 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPCMPGTQ ymm1, ymmV, ymm2/m256","VPCMPGTQ ymm2/m256, ymmV, ymm1","vpcmpgtq ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 37 /r","V","V","AVX2","","w,r,r","","" +"VPCMPGTQ k1, {k}, ymmV, ymm2/m256/m64bcst","VPCMPGTQ ymm2/m256/m64bcst, ymmV, {k}, k1","vpcmpgtq ymm2/m256/m64bcst, ymmV, {k}, k1","EVEX.NDS.256.66.0F38.W1 37 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPCMPGTQ k1, {k}, zmmV, zmm2/m512/m64bcst","VPCMPGTQ zmm2/m512/m64bcst, zmmV, {k}, k1","vpcmpgtq zmm2/m512/m64bcst, zmmV, {k}, k1","EVEX.NDS.512.66.0F38.W1 37 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPCMPGTW xmm1, xmmV, xmm2/m128","VPCMPGTW xmm2/m128, xmmV, xmm1","vpcmpgtw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 65 /r","V","V","AVX","","w,r,r","","" +"VPCMPGTW k1, {k}, xmmV, xmm2/m128","VPCMPGTW xmm2/m128, xmmV, {k}, k1","vpcmpgtw xmm2/m128, xmmV, {k}, k1","EVEX.NDS.128.66.0F.WIG 65 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPCMPGTW ymm1, ymmV, ymm2/m256","VPCMPGTW ymm2/m256, ymmV, ymm1","vpcmpgtw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 65 /r","V","V","AVX2","","w,r,r","","" +"VPCMPGTW k1, {k}, ymmV, ymm2/m256","VPCMPGTW ymm2/m256, ymmV, {k}, k1","vpcmpgtw ymm2/m256, ymmV, {k}, k1","EVEX.NDS.256.66.0F.WIG 65 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPCMPGTW k1, {k}, zmmV, zmm2/m512","VPCMPGTW zmm2/m512, zmmV, {k}, k1","vpcmpgtw zmm2/m512, zmmV, {k}, k1","EVEX.NDS.512.66.0F.WIG 65 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPCMPISTRI xmm1, xmm2/m128, imm8u","VPCMPISTRI imm8u, xmm2/m128, xmm1","vpcmpistri imm8u, xmm2/m128, xmm1","VEX.128.66.0F3A.WIG 63 /r ib","V","V","AVX","","r,r,r","","" +"VPCMPISTRM xmm1, xmm2/m128, imm8u","VPCMPISTRM imm8u, xmm2/m128, xmm1","vpcmpistrm imm8u, xmm2/m128, xmm1","VEX.128.66.0F3A.WIG 62 /r ib","V","V","AVX","","r,r,r","","" +"VPCMPQ k1, {k}, xmmV, xmm2/m128/m64bcst, imm8u","VPCMPQ imm8u, xmm2/m128/m64bcst, xmmV, {k}, k1","vpcmpq imm8u, xmm2/m128/m64bcst, xmmV, {k}, k1","EVEX.NDS.128.66.0F3A.W1 1F /r ib","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r,r","","" +"VPCMPQ k1, {k}, ymmV, ymm2/m256/m64bcst, imm8u","VPCMPQ imm8u, ymm2/m256/m64bcst, ymmV, {k}, k1","vpcmpq imm8u, ymm2/m256/m64bcst, ymmV, {k}, k1","EVEX.NDS.256.66.0F3A.W1 1F /r ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r,r","","" +"VPCMPQ k1, {k}, zmmV, zmm2/m512/m64bcst, imm8u","VPCMPQ imm8u, zmm2/m512/m64bcst, zmmV, {k}, k1","vpcmpq imm8u, zmm2/m512/m64bcst, zmmV, {k}, k1","EVEX.NDS.512.66.0F3A.W1 1F /r ib","V","V","AVX512F","bscale8,scale64","w,r,r,r,r","","" +"VPCMPUB k1, {k}, xmmV, xmm2/m128, imm8u","VPCMPUB imm8u, xmm2/m128, xmmV, {k}, k1","vpcmpub imm8u, xmm2/m128, xmmV, {k}, k1","EVEX.NDS.128.66.0F3A.W0 3E /r ib","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r,r","","" +"VPCMPUB k1, {k}, ymmV, ymm2/m256, imm8u","VPCMPUB imm8u, ymm2/m256, ymmV, {k}, k1","vpcmpub imm8u, ymm2/m256, ymmV, {k}, k1","EVEX.NDS.256.66.0F3A.W0 3E /r ib","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r,r","","" +"VPCMPUB k1, {k}, zmmV, zmm2/m512, imm8u","VPCMPUB imm8u, zmm2/m512, zmmV, {k}, k1","vpcmpub imm8u, zmm2/m512, zmmV, {k}, k1","EVEX.NDS.512.66.0F3A.W0 3E /r ib","V","V","AVX512BW","scale64","w,r,r,r,r","","" +"VPCMPUD k1, {k}, xmmV, xmm2/m128/m32bcst, imm8u","VPCMPUD imm8u, xmm2/m128/m32bcst, xmmV, {k}, k1","vpcmpud imm8u, xmm2/m128/m32bcst, xmmV, {k}, k1","EVEX.NDS.128.66.0F3A.W0 1E /r ib","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r,r","","" +"VPCMPUD k1, {k}, ymmV, ymm2/m256/m32bcst, imm8u","VPCMPUD imm8u, ymm2/m256/m32bcst, ymmV, {k}, k1","vpcmpud imm8u, ymm2/m256/m32bcst, ymmV, {k}, k1","EVEX.NDS.256.66.0F3A.W0 1E /r ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r,r","","" +"VPCMPUD k1, {k}, zmmV, zmm2/m512/m32bcst, imm8u","VPCMPUD imm8u, zmm2/m512/m32bcst, zmmV, {k}, k1","vpcmpud imm8u, zmm2/m512/m32bcst, zmmV, {k}, k1","EVEX.NDS.512.66.0F3A.W0 1E /r ib","V","V","AVX512F","bscale4,scale64","w,r,r,r,r","","" +"VPCMPUQ k1, {k}, xmmV, xmm2/m128/m64bcst, imm8u","VPCMPUQ imm8u, xmm2/m128/m64bcst, xmmV, {k}, k1","vpcmpuq imm8u, xmm2/m128/m64bcst, xmmV, {k}, k1","EVEX.NDS.128.66.0F3A.W1 1E /r ib","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r,r","","" +"VPCMPUQ k1, {k}, ymmV, ymm2/m256/m64bcst, imm8u","VPCMPUQ imm8u, ymm2/m256/m64bcst, ymmV, {k}, k1","vpcmpuq imm8u, ymm2/m256/m64bcst, ymmV, {k}, k1","EVEX.NDS.256.66.0F3A.W1 1E /r ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r,r","","" +"VPCMPUQ k1, {k}, zmmV, zmm2/m512/m64bcst, imm8u","VPCMPUQ imm8u, zmm2/m512/m64bcst, zmmV, {k}, k1","vpcmpuq imm8u, zmm2/m512/m64bcst, zmmV, {k}, k1","EVEX.NDS.512.66.0F3A.W1 1E /r ib","V","V","AVX512F","bscale8,scale64","w,r,r,r,r","","" +"VPCMPUW k1, {k}, xmmV, xmm2/m128, imm8u","VPCMPUW imm8u, xmm2/m128, xmmV, {k}, k1","vpcmpuw imm8u, xmm2/m128, xmmV, {k}, k1","EVEX.NDS.128.66.0F3A.W1 3E /r ib","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r,r","","" +"VPCMPUW k1, {k}, ymmV, ymm2/m256, imm8u","VPCMPUW imm8u, ymm2/m256, ymmV, {k}, k1","vpcmpuw imm8u, ymm2/m256, ymmV, {k}, k1","EVEX.NDS.256.66.0F3A.W1 3E /r ib","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r,r","","" +"VPCMPUW k1, {k}, zmmV, zmm2/m512, imm8u","VPCMPUW imm8u, zmm2/m512, zmmV, {k}, k1","vpcmpuw imm8u, zmm2/m512, zmmV, {k}, k1","EVEX.NDS.512.66.0F3A.W1 3E /r ib","V","V","AVX512BW","scale64","w,r,r,r,r","","" +"VPCMPW k1, {k}, xmmV, xmm2/m128, imm8u","VPCMPW imm8u, xmm2/m128, xmmV, {k}, k1","vpcmpw imm8u, xmm2/m128, xmmV, {k}, k1","EVEX.NDS.128.66.0F3A.W1 3F /r ib","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r,r","","" +"VPCMPW k1, {k}, ymmV, ymm2/m256, imm8u","VPCMPW imm8u, ymm2/m256, ymmV, {k}, k1","vpcmpw imm8u, ymm2/m256, ymmV, {k}, k1","EVEX.NDS.256.66.0F3A.W1 3F /r ib","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r,r","","" +"VPCMPW k1, {k}, zmmV, zmm2/m512, imm8u","VPCMPW imm8u, zmm2/m512, zmmV, {k}, k1","vpcmpw imm8u, zmm2/m512, zmmV, {k}, k1","EVEX.NDS.512.66.0F3A.W1 3F /r ib","V","V","AVX512BW","scale64","w,r,r,r,r","","" +"VPCOMB xmm1, xmmV, xmm2/m128, imm8u","VPCOMB imm8u, xmm2/m128, xmmV, xmm1","vpcomb imm8u, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 CC /r ib","V","V","XOP","amd","w,r,r,r","","" +"VPCOMD xmm1, xmmV, xmm2/m128, imm8u","VPCOMD imm8u, xmm2/m128, xmmV, xmm1","vpcomd imm8u, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 CE /r ib","V","V","XOP","amd","w,r,r,r","","" +"VPCOMPRESSB xmm2/m128, {k}{z}, xmm1","VPCOMPRESSB xmm1, {k}{z}, xmm2/m128","vpcompressb xmm1, {k}{z}, xmm2/m128","EVEX.128.66.0F38.W0 63 /r","V","V","AVX512_VBMI2+AVX512VL","scale1","w,r,r","","" +"VPCOMPRESSB ymm2/m256, {k}{z}, ymm1","VPCOMPRESSB ymm1, {k}{z}, ymm2/m256","vpcompressb ymm1, {k}{z}, ymm2/m256","EVEX.256.66.0F38.W0 63 /r","V","V","AVX512_VBMI2+AVX512VL","scale1","w,r,r","","" +"VPCOMPRESSB zmm2/m512, {k}{z}, zmm1","VPCOMPRESSB zmm1, {k}{z}, zmm2/m512","vpcompressb zmm1, {k}{z}, zmm2/m512","EVEX.512.66.0F38.W0 63 /r","V","V","AVX512_VBMI2","scale1","w,r,r","","" +"VPCOMPRESSD xmm2/m128, {k}{z}, xmm1","VPCOMPRESSD xmm1, {k}{z}, xmm2/m128","vpcompressd xmm1, {k}{z}, xmm2/m128","EVEX.128.66.0F38.W0 8B /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPCOMPRESSD ymm2/m256, {k}{z}, ymm1","VPCOMPRESSD ymm1, {k}{z}, ymm2/m256","vpcompressd ymm1, {k}{z}, ymm2/m256","EVEX.256.66.0F38.W0 8B /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPCOMPRESSD zmm2/m512, {k}{z}, zmm1","VPCOMPRESSD zmm1, {k}{z}, zmm2/m512","vpcompressd zmm1, {k}{z}, zmm2/m512","EVEX.512.66.0F38.W0 8B /r","V","V","AVX512F","scale4","w,r,r","","" +"VPCOMPRESSQ xmm2/m128, {k}{z}, xmm1","VPCOMPRESSQ xmm1, {k}{z}, xmm2/m128","vpcompressq xmm1, {k}{z}, xmm2/m128","EVEX.128.66.0F38.W1 8B /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPCOMPRESSQ ymm2/m256, {k}{z}, ymm1","VPCOMPRESSQ ymm1, {k}{z}, ymm2/m256","vpcompressq ymm1, {k}{z}, ymm2/m256","EVEX.256.66.0F38.W1 8B /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPCOMPRESSQ zmm2/m512, {k}{z}, zmm1","VPCOMPRESSQ zmm1, {k}{z}, zmm2/m512","vpcompressq zmm1, {k}{z}, zmm2/m512","EVEX.512.66.0F38.W1 8B /r","V","V","AVX512F","scale8","w,r,r","","" +"VPCOMPRESSW xmm2/m128, {k}{z}, xmm1","VPCOMPRESSW xmm1, {k}{z}, xmm2/m128","vpcompressw xmm1, {k}{z}, xmm2/m128","EVEX.128.66.0F38.W1 63 /r","V","V","AVX512_VBMI2+AVX512VL","scale2","w,r,r","","" +"VPCOMPRESSW ymm2/m256, {k}{z}, ymm1","VPCOMPRESSW ymm1, {k}{z}, ymm2/m256","vpcompressw ymm1, {k}{z}, ymm2/m256","EVEX.256.66.0F38.W1 63 /r","V","V","AVX512_VBMI2+AVX512VL","scale2","w,r,r","","" +"VPCOMPRESSW zmm2/m512, {k}{z}, zmm1","VPCOMPRESSW zmm1, {k}{z}, zmm2/m512","vpcompressw zmm1, {k}{z}, zmm2/m512","EVEX.512.66.0F38.W1 63 /r","V","V","AVX512_VBMI2","scale2","w,r,r","","" +"VPCOMQ xmm1, xmmV, xmm2/m128, imm8u","VPCOMQ imm8u, xmm2/m128, xmmV, xmm1","vpcomq imm8u, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 CF /r ib","V","V","XOP","amd","w,r,r,r","","" +"VPCOMUB xmm1, xmmV, xmm2/m128, imm8u","VPCOMUB imm8u, xmm2/m128, xmmV, xmm1","vpcomub imm8u, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 EC /r ib","V","V","XOP","amd","w,r,r,r","","" +"VPCOMUD xmm1, xmmV, xmm2/m128, imm8u","VPCOMUD imm8u, xmm2/m128, xmmV, xmm1","vpcomud imm8u, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 EE /r ib","V","V","XOP","amd","w,r,r,r","","" +"VPCOMUQ xmm1, xmmV, xmm2/m128, imm8u","VPCOMUQ imm8u, xmm2/m128, xmmV, xmm1","vpcomuq imm8u, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 EF /r ib","V","V","XOP","amd","w,r,r,r","","" +"VPCOMUW xmm1, xmmV, xmm2/m128, imm8u","VPCOMUW imm8u, xmm2/m128, xmmV, xmm1","vpcomuw imm8u, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 ED /r ib","V","V","XOP","amd","w,r,r,r","","" +"VPCOMW xmm1, xmmV, xmm2/m128, imm8u","VPCOMW imm8u, xmm2/m128, xmmV, xmm1","vpcomw imm8u, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 CD /r ib","V","V","XOP","amd","w,r,r,r","","" +"VPCONFLICTD xmm1, {k}{z}, xmm2/m128/m32bcst","VPCONFLICTD xmm2/m128/m32bcst, {k}{z}, xmm1","vpconflictd xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F38.W0 C4 /r","V","V","AVX512CD+AVX512VL","bscale4,scale16","w,r,r","","" +"VPCONFLICTD ymm1, {k}{z}, ymm2/m256/m32bcst","VPCONFLICTD ymm2/m256/m32bcst, {k}{z}, ymm1","vpconflictd ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F38.W0 C4 /r","V","V","AVX512CD+AVX512VL","bscale4,scale32","w,r,r","","" +"VPCONFLICTD zmm1, {k}{z}, zmm2/m512/m32bcst","VPCONFLICTD zmm2/m512/m32bcst, {k}{z}, zmm1","vpconflictd zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W0 C4 /r","V","V","AVX512CD","bscale4,scale64","w,r,r","","" +"VPCONFLICTQ xmm1, {k}{z}, xmm2/m128/m64bcst","VPCONFLICTQ xmm2/m128/m64bcst, {k}{z}, xmm1","vpconflictq xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F38.W1 C4 /r","V","V","AVX512CD+AVX512VL","bscale8,scale16","w,r,r","","" +"VPCONFLICTQ ymm1, {k}{z}, ymm2/m256/m64bcst","VPCONFLICTQ ymm2/m256/m64bcst, {k}{z}, ymm1","vpconflictq ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F38.W1 C4 /r","V","V","AVX512CD+AVX512VL","bscale8,scale32","w,r,r","","" +"VPCONFLICTQ zmm1, {k}{z}, zmm2/m512/m64bcst","VPCONFLICTQ zmm2/m512/m64bcst, {k}{z}, zmm1","vpconflictq zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W1 C4 /r","V","V","AVX512CD","bscale8,scale64","w,r,r","","" +"VPDPBUSD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPDPBUSD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpdpbusd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 50 /r","V","V","AVX512_VNNI+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VPDPBUSD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPDPBUSD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpdpbusd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 50 /r","V","V","AVX512_VNNI+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VPDPBUSD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPDPBUSD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpdpbusd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 50 /r","V","V","AVX512_VNNI","bscale4,scale64","rw,r,r,r","","" +"VPDPBUSDS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPDPBUSDS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpdpbusds xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 51 /r","V","V","AVX512_VNNI+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VPDPBUSDS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPDPBUSDS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpdpbusds ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 51 /r","V","V","AVX512_VNNI+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VPDPBUSDS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPDPBUSDS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpdpbusds zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 51 /r","V","V","AVX512_VNNI","bscale4,scale64","rw,r,r,r","","" +"VPDPWSSD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPDPWSSD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpdpwssd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 52 /r","V","V","AVX512_VNNI+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VPDPWSSD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPDPWSSD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpdpwssd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 52 /r","V","V","AVX512_VNNI+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VPDPWSSD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPDPWSSD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpdpwssd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 52 /r","V","V","AVX512_VNNI","bscale4,scale64","rw,r,r,r","","" +"VPDPWSSDS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPDPWSSDS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpdpwssds xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 53 /r","V","V","AVX512_VNNI+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VPDPWSSDS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPDPWSSDS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpdpwssds ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 53 /r","V","V","AVX512_VNNI+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VPDPWSSDS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPDPWSSDS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpdpwssds zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 53 /r","V","V","AVX512_VNNI","bscale4,scale64","rw,r,r,r","","" +"VPERM2F128 ymm1, ymmV, ymm2/m256, imm8u","VPERM2F128 imm8u, ymm2/m256, ymmV, ymm1","vperm2f128 imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 06 /r ib","V","V","AVX","","w,r,r,r","","" +"VPERM2I128 ymm1, ymmV, ymm2/m256, imm8u","VPERM2I128 imm8u, ymm2/m256, ymmV, ymm1","vperm2i128 imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 46 /r ib","V","V","AVX2","","w,r,r,r","","" +"VPERMB xmm1, {k}{z}, xmmV, xmm2/m128","VPERMB xmm2/m128, xmmV, {k}{z}, xmm1","vpermb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 8D /r","V","V","AVX512_VBMI+AVX512VL","scale16","w,r,r,r","","" +"VPERMB ymm1, {k}{z}, ymmV, ymm2/m256","VPERMB ymm2/m256, ymmV, {k}{z}, ymm1","vpermb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 8D /r","V","V","AVX512_VBMI+AVX512VL","scale32","w,r,r,r","","" +"VPERMB zmm1, {k}{z}, zmmV, zmm2/m512","VPERMB zmm2/m512, zmmV, {k}{z}, zmm1","vpermb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 8D /r","V","V","AVX512_VBMI","scale64","w,r,r,r","","" +"VPERMD ymm1, ymmV, ymm2/m256","VPERMD ymm2/m256, ymmV, ymm1","vpermd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.W0 36 /r","V","V","AVX2","","w,r,r","","" +"VPERMD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPERMD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpermd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 36 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPERMD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPERMD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpermd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 36 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPERMI2B xmm1, {k}{z}, xmmV, xmm2/m128","VPERMI2B xmm2/m128, xmmV, {k}{z}, xmm1","vpermi2b xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 75 /r","V","V","AVX512_VBMI+AVX512VL","scale16","rw,r,r,r","","" +"VPERMI2B ymm1, {k}{z}, ymmV, ymm2/m256","VPERMI2B ymm2/m256, ymmV, {k}{z}, ymm1","vpermi2b ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 75 /r","V","V","AVX512_VBMI+AVX512VL","scale32","rw,r,r,r","","" +"VPERMI2B zmm1, {k}{z}, zmmV, zmm2/m512","VPERMI2B zmm2/m512, zmmV, {k}{z}, zmm1","vpermi2b zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 75 /r","V","V","AVX512_VBMI","scale64","rw,r,r,r","","" +"VPERMI2D xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPERMI2D xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpermi2d xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 76 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VPERMI2D ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPERMI2D ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpermi2d ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 76 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VPERMI2D zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPERMI2D zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpermi2d zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 76 /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VPERMI2PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPERMI2PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpermi2pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 77 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VPERMI2PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPERMI2PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpermi2pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 77 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VPERMI2PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPERMI2PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpermi2pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 77 /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VPERMI2PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPERMI2PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpermi2ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 77 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VPERMI2PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPERMI2PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpermi2ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 77 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VPERMI2PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPERMI2PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpermi2ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 77 /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VPERMI2Q xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPERMI2Q xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpermi2q xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 76 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VPERMI2Q ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPERMI2Q ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpermi2q ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 76 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VPERMI2Q zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPERMI2Q zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpermi2q zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 76 /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VPERMI2W xmm1, {k}{z}, xmmV, xmm2/m128","VPERMI2W xmm2/m128, xmmV, {k}{z}, xmm1","vpermi2w xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 75 /r","V","V","AVX512BW+AVX512VL","scale16","rw,r,r,r","","" +"VPERMI2W ymm1, {k}{z}, ymmV, ymm2/m256","VPERMI2W ymm2/m256, ymmV, {k}{z}, ymm1","vpermi2w ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 75 /r","V","V","AVX512BW+AVX512VL","scale32","rw,r,r,r","","" +"VPERMI2W zmm1, {k}{z}, zmmV, zmm2/m512","VPERMI2W zmm2/m512, zmmV, {k}{z}, zmm1","vpermi2w zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 75 /r","V","V","AVX512BW","scale64","rw,r,r,r","","" +"VPERMIL2PD xmm1, xmmV, xmmIH, xmm2/m128, imm8u","VPERMIL2PD imm8u, xmm2/m128, xmmIH, xmmV, xmm1","vpermil2pd imm8u, xmm2/m128, xmmIH, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 49 /r /is4","V","V","XOP","amd","w,r,r,r,r","","" +"VPERMIL2PD xmm1, xmmV, xmm2/m128, xmmIH, imm8u","VPERMIL2PD imm8u, xmmIH, xmm2/m128, xmmV, xmm1","vpermil2pd imm8u, xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 49 /r /is4","V","V","XOP","amd","w,r,r,r,r","","" +"VPERMIL2PD ymm1, ymmV, ymmIH, ymm2/m256, imm8u","VPERMIL2PD imm8u, ymm2/m256, ymmIH, ymmV, ymm1","vpermil2pd imm8u, ymm2/m256, ymmIH, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 49 /r /is4","V","V","XOP","amd","w,r,r,r,r","","" +"VPERMIL2PD ymm1, ymmV, ymm2/m256, ymmIH, imm8u","VPERMIL2PD imm8u, ymmIH, ymm2/m256, ymmV, ymm1","vpermil2pd imm8u, ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 49 /r /is4","V","V","XOP","amd","w,r,r,r,r","","" +"VPERMIL2PS xmm1, xmmV, xmmIH, xmm2/m128, imm8u","VPERMIL2PS imm8u, xmm2/m128, xmmIH, xmmV, xmm1","vpermil2ps imm8u, xmm2/m128, xmmIH, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 48 /r /is4","V","V","XOP","amd","w,r,r,r,r","","" +"VPERMIL2PS xmm1, xmmV, xmm2/m128, xmmIH, imm8u","VPERMIL2PS imm8u, xmmIH, xmm2/m128, xmmV, xmm1","vpermil2ps imm8u, xmmIH, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 48 /r /is4","V","V","XOP","amd","w,r,r,r,r","","" +"VPERMIL2PS ymm1, ymmV, ymmIH, ymm2/m256, imm8u","VPERMIL2PS imm8u, ymm2/m256, ymmIH, ymmV, ymm1","vpermil2ps imm8u, ymm2/m256, ymmIH, ymmV, ymm1","VEX.NDS.256.66.0F3A.W1 48 /r /is4","V","V","XOP","amd","w,r,r,r,r","","" +"VPERMIL2PS ymm1, ymmV, ymm2/m256, ymmIH, imm8u","VPERMIL2PS imm8u, ymmIH, ymm2/m256, ymmV, ymm1","vpermil2ps imm8u, ymmIH, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F3A.W0 48 /r /is4","V","V","XOP","amd","w,r,r,r,r","","" +"VPERMILPD xmm1, xmm2/m128, imm8u","VPERMILPD imm8u, xmm2/m128, xmm1","vpermilpd imm8u, xmm2/m128, xmm1","VEX.128.66.0F3A.W0 05 /r ib","V","V","AVX","","w,r,r","","" +"VPERMILPD xmm1, {k}{z}, xmm2/m128/m64bcst, imm8u","VPERMILPD imm8u, xmm2/m128/m64bcst, {k}{z}, xmm1","vpermilpd imm8u, xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F3A.W1 05 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPERMILPD ymm1, ymm2/m256, imm8u","VPERMILPD imm8u, ymm2/m256, ymm1","vpermilpd imm8u, ymm2/m256, ymm1","VEX.256.66.0F3A.W0 05 /r ib","V","V","AVX","","w,r,r","","" +"VPERMILPD ymm1, {k}{z}, ymm2/m256/m64bcst, imm8u","VPERMILPD imm8u, ymm2/m256/m64bcst, {k}{z}, ymm1","vpermilpd imm8u, ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F3A.W1 05 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPERMILPD zmm1, {k}{z}, zmm2/m512/m64bcst, imm8u","VPERMILPD imm8u, zmm2/m512/m64bcst, {k}{z}, zmm1","vpermilpd imm8u, zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F3A.W1 05 /r ib","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPERMILPD xmm1, xmmV, xmm2/m128","VPERMILPD xmm2/m128, xmmV, xmm1","vpermilpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.W0 0D /r","V","V","AVX","","w,r,r","","" +"VPERMILPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPERMILPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpermilpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 0D /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPERMILPD ymm1, ymmV, ymm2/m256","VPERMILPD ymm2/m256, ymmV, ymm1","vpermilpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.W0 0D /r","V","V","AVX","","w,r,r","","" +"VPERMILPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPERMILPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpermilpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 0D /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPERMILPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPERMILPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpermilpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 0D /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPERMILPS xmm1, xmm2/m128, imm8u","VPERMILPS imm8u, xmm2/m128, xmm1","vpermilps imm8u, xmm2/m128, xmm1","VEX.128.66.0F3A.W0 04 /r ib","V","V","AVX","","w,r,r","","" +"VPERMILPS xmm1, {k}{z}, xmm2/m128/m32bcst, imm8u","VPERMILPS imm8u, xmm2/m128/m32bcst, {k}{z}, xmm1","vpermilps imm8u, xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F3A.W0 04 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPERMILPS ymm1, ymm2/m256, imm8u","VPERMILPS imm8u, ymm2/m256, ymm1","vpermilps imm8u, ymm2/m256, ymm1","VEX.256.66.0F3A.W0 04 /r ib","V","V","AVX","","w,r,r","","" +"VPERMILPS ymm1, {k}{z}, ymm2/m256/m32bcst, imm8u","VPERMILPS imm8u, ymm2/m256/m32bcst, {k}{z}, ymm1","vpermilps imm8u, ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F3A.W0 04 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPERMILPS zmm1, {k}{z}, zmm2/m512/m32bcst, imm8u","VPERMILPS imm8u, zmm2/m512/m32bcst, {k}{z}, zmm1","vpermilps imm8u, zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F3A.W0 04 /r ib","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPERMILPS xmm1, xmmV, xmm2/m128","VPERMILPS xmm2/m128, xmmV, xmm1","vpermilps xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.W0 0C /r","V","V","AVX","","w,r,r","","" +"VPERMILPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPERMILPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpermilps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 0C /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPERMILPS ymm1, ymmV, ymm2/m256","VPERMILPS ymm2/m256, ymmV, ymm1","vpermilps ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.W0 0C /r","V","V","AVX","","w,r,r","","" +"VPERMILPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPERMILPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpermilps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 0C /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPERMILPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPERMILPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpermilps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 0C /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPERMPD ymm1, ymm2/m256, imm8u","VPERMPD imm8u, ymm2/m256, ymm1","vpermpd imm8u, ymm2/m256, ymm1","VEX.256.66.0F3A.W1 01 /r ib","V","V","AVX2","","w,r,r","","" +"VPERMPD ymm1, {k}{z}, ymm2/m256/m64bcst, imm8u","VPERMPD imm8u, ymm2/m256/m64bcst, {k}{z}, ymm1","vpermpd imm8u, ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F3A.W1 01 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPERMPD zmm1, {k}{z}, zmm2/m512/m64bcst, imm8u","VPERMPD imm8u, zmm2/m512/m64bcst, {k}{z}, zmm1","vpermpd imm8u, zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F3A.W1 01 /r ib","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPERMPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPERMPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpermpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 16 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPERMPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPERMPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpermpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 16 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPERMPS ymm1, ymmV, ymm2/m256","VPERMPS ymm2/m256, ymmV, ymm1","vpermps ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.W0 16 /r","V","V","AVX2","","w,r,r","","" +"VPERMPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPERMPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpermps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 16 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPERMPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPERMPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpermps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 16 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPERMQ ymm1, ymm2/m256, imm8u","VPERMQ imm8u, ymm2/m256, ymm1","vpermq imm8u, ymm2/m256, ymm1","VEX.256.66.0F3A.W1 00 /r ib","V","V","AVX2","","w,r,r","","" +"VPERMQ ymm1, {k}{z}, ymm2/m256/m64bcst, imm8u","VPERMQ imm8u, ymm2/m256/m64bcst, {k}{z}, ymm1","vpermq imm8u, ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F3A.W1 00 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPERMQ zmm1, {k}{z}, zmm2/m512/m64bcst, imm8u","VPERMQ imm8u, zmm2/m512/m64bcst, {k}{z}, zmm1","vpermq imm8u, zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F3A.W1 00 /r ib","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPERMQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPERMQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpermq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 36 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPERMQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPERMQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpermq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 36 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPERMT2B xmm1, {k}{z}, xmmV, xmm2/m128","VPERMT2B xmm2/m128, xmmV, {k}{z}, xmm1","vpermt2b xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 7D /r","V","V","AVX512_VBMI+AVX512VL","scale16","rw,r,r,r","","" +"VPERMT2B ymm1, {k}{z}, ymmV, ymm2/m256","VPERMT2B ymm2/m256, ymmV, {k}{z}, ymm1","vpermt2b ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 7D /r","V","V","AVX512_VBMI+AVX512VL","scale32","rw,r,r,r","","" +"VPERMT2B zmm1, {k}{z}, zmmV, zmm2/m512","VPERMT2B zmm2/m512, zmmV, {k}{z}, zmm1","vpermt2b zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 7D /r","V","V","AVX512_VBMI","scale64","rw,r,r,r","","" +"VPERMT2D xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPERMT2D xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpermt2d xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 7E /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VPERMT2D ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPERMT2D ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpermt2d ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 7E /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VPERMT2D zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPERMT2D zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpermt2d zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 7E /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VPERMT2PD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPERMT2PD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpermt2pd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 7F /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VPERMT2PD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPERMT2PD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpermt2pd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 7F /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VPERMT2PD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPERMT2PD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpermt2pd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 7F /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VPERMT2PS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPERMT2PS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpermt2ps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 7F /r","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VPERMT2PS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPERMT2PS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpermt2ps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 7F /r","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VPERMT2PS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPERMT2PS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpermt2ps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 7F /r","V","V","AVX512F","bscale4,scale64","rw,r,r,r","","" +"VPERMT2Q xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPERMT2Q xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpermt2q xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 7E /r","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VPERMT2Q ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPERMT2Q ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpermt2q ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 7E /r","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VPERMT2Q zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPERMT2Q zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpermt2q zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 7E /r","V","V","AVX512F","bscale8,scale64","rw,r,r,r","","" +"VPERMT2W xmm1, {k}{z}, xmmV, xmm2/m128","VPERMT2W xmm2/m128, xmmV, {k}{z}, xmm1","vpermt2w xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 7D /r","V","V","AVX512BW+AVX512VL","scale16","rw,r,r,r","","" +"VPERMT2W ymm1, {k}{z}, ymmV, ymm2/m256","VPERMT2W ymm2/m256, ymmV, {k}{z}, ymm1","vpermt2w ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 7D /r","V","V","AVX512BW+AVX512VL","scale32","rw,r,r,r","","" +"VPERMT2W zmm1, {k}{z}, zmmV, zmm2/m512","VPERMT2W zmm2/m512, zmmV, {k}{z}, zmm1","vpermt2w zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 7D /r","V","V","AVX512BW","scale64","rw,r,r,r","","" +"VPERMW xmm1, {k}{z}, xmmV, xmm2/m128","VPERMW xmm2/m128, xmmV, {k}{z}, xmm1","vpermw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 8D /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPERMW ymm1, {k}{z}, ymmV, ymm2/m256","VPERMW ymm2/m256, ymmV, {k}{z}, ymm1","vpermw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 8D /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPERMW zmm1, {k}{z}, zmmV, zmm2/m512","VPERMW zmm2/m512, zmmV, {k}{z}, zmm1","vpermw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 8D /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPEXPANDB xmm1, {k}{z}, xmm2/m128","VPEXPANDB xmm2/m128, {k}{z}, xmm1","vpexpandb xmm2/m128, {k}{z}, xmm1","EVEX.128.66.0F38.W0 62 /r","V","V","AVX512_VBMI2+AVX512VL","scale1","w,r,r","","" +"VPEXPANDB ymm1, {k}{z}, ymm2/m256","VPEXPANDB ymm2/m256, {k}{z}, ymm1","vpexpandb ymm2/m256, {k}{z}, ymm1","EVEX.256.66.0F38.W0 62 /r","V","V","AVX512_VBMI2+AVX512VL","scale1","w,r,r","","" +"VPEXPANDB zmm1, {k}{z}, zmm2/m512","VPEXPANDB zmm2/m512, {k}{z}, zmm1","vpexpandb zmm2/m512, {k}{z}, zmm1","EVEX.512.66.0F38.W0 62 /r","V","V","AVX512_VBMI2","scale1","w,r,r","","" +"VPEXPANDD xmm1, {k}{z}, xmm2/m128","VPEXPANDD xmm2/m128, {k}{z}, xmm1","vpexpandd xmm2/m128, {k}{z}, xmm1","EVEX.128.66.0F38.W0 89 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPEXPANDD ymm1, {k}{z}, ymm2/m256","VPEXPANDD ymm2/m256, {k}{z}, ymm1","vpexpandd ymm2/m256, {k}{z}, ymm1","EVEX.256.66.0F38.W0 89 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPEXPANDD zmm1, {k}{z}, zmm2/m512","VPEXPANDD zmm2/m512, {k}{z}, zmm1","vpexpandd zmm2/m512, {k}{z}, zmm1","EVEX.512.66.0F38.W0 89 /r","V","V","AVX512F","scale4","w,r,r","","" +"VPEXPANDQ xmm1, {k}{z}, xmm2/m128","VPEXPANDQ xmm2/m128, {k}{z}, xmm1","vpexpandq xmm2/m128, {k}{z}, xmm1","EVEX.128.66.0F38.W1 89 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPEXPANDQ ymm1, {k}{z}, ymm2/m256","VPEXPANDQ ymm2/m256, {k}{z}, ymm1","vpexpandq ymm2/m256, {k}{z}, ymm1","EVEX.256.66.0F38.W1 89 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPEXPANDQ zmm1, {k}{z}, zmm2/m512","VPEXPANDQ zmm2/m512, {k}{z}, zmm1","vpexpandq zmm2/m512, {k}{z}, zmm1","EVEX.512.66.0F38.W1 89 /r","V","V","AVX512F","scale8","w,r,r","","" +"VPEXPANDW xmm1, {k}{z}, xmm2/m128","VPEXPANDW xmm2/m128, {k}{z}, xmm1","vpexpandw xmm2/m128, {k}{z}, xmm1","EVEX.128.66.0F38.W1 62 /r","V","V","AVX512_VBMI2+AVX512VL","scale2","w,r,r","","" +"VPEXPANDW ymm1, {k}{z}, ymm2/m256","VPEXPANDW ymm2/m256, {k}{z}, ymm1","vpexpandw ymm2/m256, {k}{z}, ymm1","EVEX.256.66.0F38.W1 62 /r","V","V","AVX512_VBMI2+AVX512VL","scale2","w,r,r","","" +"VPEXPANDW zmm1, {k}{z}, zmm2/m512","VPEXPANDW zmm2/m512, {k}{z}, zmm1","vpexpandw zmm2/m512, {k}{z}, zmm1","EVEX.512.66.0F38.W1 62 /r","V","V","AVX512_VBMI2","scale2","w,r,r","","" +"VPEXTRB r32/m8, xmm1, imm8u","VPEXTRB imm8u, xmm1, r32/m8","vpextrb imm8u, xmm1, r32/m8","EVEX.128.66.0F3A.WIG 14 /r ib","V","V","AVX512BW+AVX512VL","scale1","w,r,r","","" +"VPEXTRB r32/m8, xmm1, imm8u","VPEXTRB imm8u, xmm1, r32/m8","vpextrb imm8u, xmm1, r32/m8","VEX.128.66.0F3A.WIG 14 /r ib","V","V","AVX","","w,r,r","","" +"VPEXTRD r/m32, xmm1, imm8u","VPEXTRD imm8u, xmm1, r/m32","vpextrd imm8u, xmm1, r/m32","EVEX.128.66.0F3A.W0 16 /r ib","V","V","AVX512DQ+AVX512VL","scale4","w,r,r","","" +"VPEXTRD r/m32, xmm1, imm8u","VPEXTRD imm8u, xmm1, r/m32","vpextrd imm8u, xmm1, r/m32","VEX.128.66.0F3A.W0 16 /r ib","V","V","AVX","","w,r,r","","" +"VPEXTRQ r/m64, xmm1, imm8u","VPEXTRQ imm8u, xmm1, r/m64","vpextrq imm8u, xmm1, r/m64","EVEX.128.66.0F3A.W1 16 /r ib","N.S.","V","AVX512DQ+AVX512VL","scale8","w,r,r","","" +"VPEXTRQ r/m64, xmm1, imm8u","VPEXTRQ imm8u, xmm1, r/m64","vpextrq imm8u, xmm1, r/m64","VEX.128.66.0F3A.W1 16 /r ib","N.S.","V","AVX","","w,r,r","","" +"VPEXTRW r32/m16, xmm1, imm8u","VPEXTRW imm8u, xmm1, r32/m16","vpextrw imm8u, xmm1, r32/m16","EVEX.128.66.0F3A.WIG 15 /r ib","V","V","AVX512BW+AVX512VL","scale2","w,r,r","","" +"VPEXTRW r32/m16, xmm1, imm8u","VPEXTRW imm8u, xmm1, r32/m16","vpextrw imm8u, xmm1, r32/m16","VEX.128.66.0F3A.WIG 15 /r ib","V","V","AVX","","w,r,r","","" +"VPEXTRW r32, xmm2, imm8u","VPEXTRW imm8u, xmm2, r32","vpextrw imm8u, xmm2, r32","EVEX.128.66.0F.WIG C5 /r ib","V","V","AVX512BW+AVX512VL","modrm_regonly","w,r,r","","" +"VPEXTRW r32, xmm2, imm8u","VPEXTRW imm8u, xmm2, r32","vpextrw imm8u, xmm2, r32","VEX.128.66.0F.WIG C5 /r ib","V","V","AVX","modrm_regonly","w,r,r","","" +"VPGATHERDD xmm1, {k1-k7}, vm32x","VPGATHERDD vm32x, {k1-k7}, xmm1","vpgatherdd vm32x, {k1-k7}, xmm1","EVEX.128.66.0F38.W0 90 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VPGATHERDD ymm1, {k1-k7}, vm32y","VPGATHERDD vm32y, {k1-k7}, ymm1","vpgatherdd vm32y, {k1-k7}, ymm1","EVEX.256.66.0F38.W0 90 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VPGATHERDD zmm1, {k1-k7}, vm32z","VPGATHERDD vm32z, {k1-k7}, zmm1","vpgatherdd vm32z, {k1-k7}, zmm1","EVEX.512.66.0F38.W0 90 /vsib","V","V","AVX512F","modrm_memonly,scale4","w,rw,r","","" +"VPGATHERDD xmm1, vm32x, xmmV","VPGATHERDD xmmV, vm32x, xmm1","vpgatherdd xmmV, vm32x, xmm1","VEX.DDS.128.66.0F38.W0 90 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VPGATHERDD ymm1, vm32y, ymmV","VPGATHERDD ymmV, vm32y, ymm1","vpgatherdd ymmV, vm32y, ymm1","VEX.DDS.256.66.0F38.W0 90 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VPGATHERDQ xmm1, {k1-k7}, vm32x","VPGATHERDQ vm32x, {k1-k7}, xmm1","vpgatherdq vm32x, {k1-k7}, xmm1","EVEX.128.66.0F38.W1 90 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VPGATHERDQ ymm1, {k1-k7}, vm32x","VPGATHERDQ vm32x, {k1-k7}, ymm1","vpgatherdq vm32x, {k1-k7}, ymm1","EVEX.256.66.0F38.W1 90 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VPGATHERDQ zmm1, {k1-k7}, vm32y","VPGATHERDQ vm32y, {k1-k7}, zmm1","vpgatherdq vm32y, {k1-k7}, zmm1","EVEX.512.66.0F38.W1 90 /vsib","V","V","AVX512F","modrm_memonly,scale8","w,rw,r","","" +"VPGATHERDQ xmm1, vm32x, xmmV","VPGATHERDQ xmmV, vm32x, xmm1","vpgatherdq xmmV, vm32x, xmm1","VEX.DDS.128.66.0F38.W1 90 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VPGATHERDQ ymm1, vm32x, ymmV","VPGATHERDQ ymmV, vm32x, ymm1","vpgatherdq ymmV, vm32x, ymm1","VEX.DDS.256.66.0F38.W1 90 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VPGATHERQD xmm1, {k1-k7}, vm64x","VPGATHERQD vm64x, {k1-k7}, xmm1","vpgatherqd vm64x, {k1-k7}, xmm1","EVEX.128.66.0F38.W0 91 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VPGATHERQD xmm1, {k1-k7}, vm64y","VPGATHERQD vm64y, {k1-k7}, xmm1","vpgatherqd vm64y, {k1-k7}, xmm1","EVEX.256.66.0F38.W0 91 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VPGATHERQD ymm1, {k1-k7}, vm64z","VPGATHERQD vm64z, {k1-k7}, ymm1","vpgatherqd vm64z, {k1-k7}, ymm1","EVEX.512.66.0F38.W0 91 /vsib","V","V","AVX512F","modrm_memonly,scale4","w,rw,r","","" +"VPGATHERQD xmm1, vm64x, xmmV","VPGATHERQD xmmV, vm64x, xmm1","vpgatherqd xmmV, vm64x, xmm1","VEX.DDS.128.66.0F38.W0 91 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VPGATHERQD xmm1, vm64y, xmmV","VPGATHERQD xmmV, vm64y, xmm1","vpgatherqd xmmV, vm64y, xmm1","VEX.DDS.256.66.0F38.W0 91 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VPGATHERQQ xmm1, {k1-k7}, vm64x","VPGATHERQQ vm64x, {k1-k7}, xmm1","vpgatherqq vm64x, {k1-k7}, xmm1","EVEX.128.66.0F38.W1 91 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VPGATHERQQ ymm1, {k1-k7}, vm64y","VPGATHERQQ vm64y, {k1-k7}, ymm1","vpgatherqq vm64y, {k1-k7}, ymm1","EVEX.256.66.0F38.W1 91 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VPGATHERQQ zmm1, {k1-k7}, vm64z","VPGATHERQQ vm64z, {k1-k7}, zmm1","vpgatherqq vm64z, {k1-k7}, zmm1","EVEX.512.66.0F38.W1 91 /vsib","V","V","AVX512F","modrm_memonly,scale8","w,rw,r","","" +"VPGATHERQQ xmm1, vm64x, xmmV","VPGATHERQQ xmmV, vm64x, xmm1","vpgatherqq xmmV, vm64x, xmm1","VEX.DDS.128.66.0F38.W1 91 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VPGATHERQQ ymm1, vm64y, ymmV","VPGATHERQQ ymmV, vm64y, ymm1","vpgatherqq ymmV, vm64y, ymm1","VEX.DDS.256.66.0F38.W1 91 /r","V","V","AVX2","modrm_memonly","rw,r,rw","","" +"VPHADDBD xmm1, xmm2/m128","VPHADDBD xmm2/m128, xmm1","vphaddbd xmm2/m128, xmm1","XOP.128.09.W0 C2 /r","V","V","XOP","amd","w,r","","" +"VPHADDBQ xmm1, xmm2/m128","VPHADDBQ xmm2/m128, xmm1","vphaddbq xmm2/m128, xmm1","XOP.128.09.W0 C3 /r","V","V","XOP","amd","w,r","","" +"VPHADDBW xmm1, xmm2/m128","VPHADDBW xmm2/m128, xmm1","vphaddbw xmm2/m128, xmm1","XOP.128.09.W0 C1 /r","V","V","XOP","amd","w,r","","" +"VPHADDD xmm1, xmmV, xmm2/m128","VPHADDD xmm2/m128, xmmV, xmm1","vphaddd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 02 /r","V","V","AVX","","w,r,r","","" +"VPHADDD ymm1, ymmV, ymm2/m256","VPHADDD ymm2/m256, ymmV, ymm1","vphaddd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 02 /r","V","V","AVX2","","w,r,r","","" +"VPHADDDQ xmm1, xmm2/m128","VPHADDDQ xmm2/m128, xmm1","vphadddq xmm2/m128, xmm1","XOP.128.09.W0 CB /r","V","V","XOP","amd","w,r","","" +"VPHADDSW xmm1, xmmV, xmm2/m128","VPHADDSW xmm2/m128, xmmV, xmm1","vphaddsw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 03 /r","V","V","AVX","","w,r,r","","" +"VPHADDSW ymm1, ymmV, ymm2/m256","VPHADDSW ymm2/m256, ymmV, ymm1","vphaddsw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 03 /r","V","V","AVX2","","w,r,r","","" +"VPHADDUBD xmm1, xmm2/m128","VPHADDUBD xmm2/m128, xmm1","vphaddubd xmm2/m128, xmm1","XOP.128.09.W0 D2 /r","V","V","XOP","amd","w,r","","" +"VPHADDUBQ xmm1, xmm2/m128","VPHADDUBQ xmm2/m128, xmm1","vphaddubq xmm2/m128, xmm1","XOP.128.09.W0 D3 /r","V","V","XOP","amd","w,r","","" +"VPHADDUBW xmm1, xmm2/m128","VPHADDUBW xmm2/m128, xmm1","vphaddubw xmm2/m128, xmm1","XOP.128.09.W0 D1 /r","V","V","XOP","amd","w,r","","" +"VPHADDUDQ xmm1, xmm2/m128","VPHADDUDQ xmm2/m128, xmm1","vphaddudq xmm2/m128, xmm1","XOP.128.09.W0 DB /r","V","V","XOP","amd","w,r","","" +"VPHADDUWD xmm1, xmm2/m128","VPHADDUWD xmm2/m128, xmm1","vphadduwd xmm2/m128, xmm1","XOP.128.09.W0 D6 /r","V","V","XOP","amd","w,r","","" +"VPHADDUWQ xmm1, xmm2/m128","VPHADDUWQ xmm2/m128, xmm1","vphadduwq xmm2/m128, xmm1","XOP.128.09.W0 D7 /r","V","V","XOP","amd","w,r","","" +"VPHADDW xmm1, xmmV, xmm2/m128","VPHADDW xmm2/m128, xmmV, xmm1","vphaddw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 01 /r","V","V","AVX","","w,r,r","","" +"VPHADDW ymm1, ymmV, ymm2/m256","VPHADDW ymm2/m256, ymmV, ymm1","vphaddw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 01 /r","V","V","AVX2","","w,r,r","","" +"VPHADDWD xmm1, xmm2/m128","VPHADDWD xmm2/m128, xmm1","vphaddwd xmm2/m128, xmm1","XOP.128.09.W0 C6 /r","V","V","XOP","amd","w,r","","" +"VPHADDWQ xmm1, xmm2/m128","VPHADDWQ xmm2/m128, xmm1","vphaddwq xmm2/m128, xmm1","XOP.128.09.W0 C7 /r","V","V","XOP","amd","w,r","","" +"VPHMINPOSUW xmm1, xmm2/m128","VPHMINPOSUW xmm2/m128, xmm1","vphminposuw xmm2/m128, xmm1","VEX.128.66.0F38.WIG 41 /r","V","V","AVX","","w,r","","" +"VPHSUBBW xmm1, xmm2/m128","VPHSUBBW xmm2/m128, xmm1","vphsubbw xmm2/m128, xmm1","XOP.128.09.W0 E1 /r","V","V","XOP","amd","w,r","","" +"VPHSUBD xmm1, xmmV, xmm2/m128","VPHSUBD xmm2/m128, xmmV, xmm1","vphsubd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 06 /r","V","V","AVX","","w,r,r","","" +"VPHSUBD ymm1, ymmV, ymm2/m256","VPHSUBD ymm2/m256, ymmV, ymm1","vphsubd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 06 /r","V","V","AVX2","","w,r,r","","" +"VPHSUBDQ xmm1, xmm2/m128","VPHSUBDQ xmm2/m128, xmm1","vphsubdq xmm2/m128, xmm1","XOP.128.09.W0 E3 /r","V","V","XOP","amd","w,r","","" +"VPHSUBSW xmm1, xmmV, xmm2/m128","VPHSUBSW xmm2/m128, xmmV, xmm1","vphsubsw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 07 /r","V","V","AVX","","w,r,r","","" +"VPHSUBSW ymm1, ymmV, ymm2/m256","VPHSUBSW ymm2/m256, ymmV, ymm1","vphsubsw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 07 /r","V","V","AVX2","","w,r,r","","" +"VPHSUBW xmm1, xmmV, xmm2/m128","VPHSUBW xmm2/m128, xmmV, xmm1","vphsubw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 05 /r","V","V","AVX","","w,r,r","","" +"VPHSUBW ymm1, ymmV, ymm2/m256","VPHSUBW ymm2/m256, ymmV, ymm1","vphsubw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 05 /r","V","V","AVX2","","w,r,r","","" +"VPHSUBWD xmm1, xmm2/m128","VPHSUBWD xmm2/m128, xmm1","vphsubwd xmm2/m128, xmm1","XOP.128.09.W0 E2 /r","V","V","XOP","amd","w,r","","" +"VPINSRB xmm1, xmmV, r32/m8, imm8u","VPINSRB imm8u, r32/m8, xmmV, xmm1","vpinsrb imm8u, r32/m8, xmmV, xmm1","EVEX.NDS.128.66.0F3A.WIG 20 /r ib","V","V","AVX512BW+AVX512VL","scale1","w,r,r,r","","" +"VPINSRB xmm1, xmmV, r32/m8, imm8u","VPINSRB imm8u, r32/m8, xmmV, xmm1","vpinsrb imm8u, r32/m8, xmmV, xmm1","VEX.NDS.128.66.0F3A.WIG 20 /r ib","V","V","AVX","","w,r,r,r","","" +"VPINSRD xmm1, xmmV, r/m32, imm8u","VPINSRD imm8u, r/m32, xmmV, xmm1","vpinsrd imm8u, r/m32, xmmV, xmm1","EVEX.NDS.128.66.0F3A.W0 22 /r ib","V","V","AVX512DQ+AVX512VL","scale4","w,r,r,r","","" +"VPINSRD xmm1, xmmV, r/m32, imm8u","VPINSRD imm8u, r/m32, xmmV, xmm1","vpinsrd imm8u, r/m32, xmmV, xmm1","VEX.NDS.128.66.0F3A.W0 22 /r ib","V","V","AVX","","w,r,r,r","","" +"VPINSRQ xmm1, xmmV, r/m64, imm8u","VPINSRQ imm8u, r/m64, xmmV, xmm1","vpinsrq imm8u, r/m64, xmmV, xmm1","EVEX.NDS.128.66.0F3A.W1 22 /r ib","N.S.","V","AVX512DQ+AVX512VL","scale8","w,r,r,r","","" +"VPINSRQ xmm1, xmmV, r/m64, imm8u","VPINSRQ imm8u, r/m64, xmmV, xmm1","vpinsrq imm8u, r/m64, xmmV, xmm1","VEX.NDS.128.66.0F3A.W1 22 /r ib","N.S.","V","AVX","","w,r,r,r","","" +"VPINSRW xmm1, xmmV, r32/m16, imm8u","VPINSRW imm8u, r32/m16, xmmV, xmm1","vpinsrw imm8u, r32/m16, xmmV, xmm1","EVEX.NDS.128.66.0F.WIG C4 /r ib","V","V","AVX512BW+AVX512VL","scale2","w,r,r,r","","" +"VPINSRW xmm1, xmmV, r32/m16, imm8u","VPINSRW imm8u, r32/m16, xmmV, xmm1","vpinsrw imm8u, r32/m16, xmmV, xmm1","VEX.NDS.128.66.0F.WIG C4 /r ib","V","V","AVX","","w,r,r,r","","" +"VPLZCNTD xmm1, {k}{z}, xmm2/m128/m32bcst","VPLZCNTD xmm2/m128/m32bcst, {k}{z}, xmm1","vplzcntd xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F38.W0 44 /r","V","V","AVX512CD+AVX512VL","bscale4,scale16","w,r,r","","" +"VPLZCNTD ymm1, {k}{z}, ymm2/m256/m32bcst","VPLZCNTD ymm2/m256/m32bcst, {k}{z}, ymm1","vplzcntd ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F38.W0 44 /r","V","V","AVX512CD+AVX512VL","bscale4,scale32","w,r,r","","" +"VPLZCNTD zmm1, {k}{z}, zmm2/m512/m32bcst","VPLZCNTD zmm2/m512/m32bcst, {k}{z}, zmm1","vplzcntd zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W0 44 /r","V","V","AVX512CD","bscale4,scale64","w,r,r","","" +"VPLZCNTQ xmm1, {k}{z}, xmm2/m128/m64bcst","VPLZCNTQ xmm2/m128/m64bcst, {k}{z}, xmm1","vplzcntq xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F38.W1 44 /r","V","V","AVX512CD+AVX512VL","bscale8,scale16","w,r,r","","" +"VPLZCNTQ ymm1, {k}{z}, ymm2/m256/m64bcst","VPLZCNTQ ymm2/m256/m64bcst, {k}{z}, ymm1","vplzcntq ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F38.W1 44 /r","V","V","AVX512CD+AVX512VL","bscale8,scale32","w,r,r","","" +"VPLZCNTQ zmm1, {k}{z}, zmm2/m512/m64bcst","VPLZCNTQ zmm2/m512/m64bcst, {k}{z}, zmm1","vplzcntq zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W1 44 /r","V","V","AVX512CD","bscale8,scale64","w,r,r","","" +"VPMACSDD xmm1, xmmV, xmm2/m128, xmmIH","VPMACSDD xmmIH, xmm2/m128, xmmV, xmm1","vpmacsdd xmmIH, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 9E /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPMACSDQH xmm1, xmmV, xmm2/m128, xmmIH","VPMACSDQH xmmIH, xmm2/m128, xmmV, xmm1","vpmacsdqh xmmIH, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 9F /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPMACSDQL xmm1, xmmV, xmm2/m128, xmmIH","VPMACSDQL xmmIH, xmm2/m128, xmmV, xmm1","vpmacsdql xmmIH, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 97 /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPMACSSDD xmm1, xmmV, xmm2/m128, xmmIH","VPMACSSDD xmmIH, xmm2/m128, xmmV, xmm1","vpmacssdd xmmIH, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 8E /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPMACSSDQH xmm1, xmmV, xmm2/m128, xmmIH","VPMACSSDQH xmmIH, xmm2/m128, xmmV, xmm1","vpmacssdqh xmmIH, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 8F /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPMACSSDQL xmm1, xmmV, xmm2/m128, xmmIH","VPMACSSDQL xmmIH, xmm2/m128, xmmV, xmm1","vpmacssdql xmmIH, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 87 /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPMACSSWD xmm1, xmmV, xmm2/m128, xmmIH","VPMACSSWD xmmIH, xmm2/m128, xmmV, xmm1","vpmacsswd xmmIH, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 86 /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPMACSSWW xmm1, xmmV, xmm2/m128, xmmIH","VPMACSSWW xmmIH, xmm2/m128, xmmV, xmm1","vpmacssww xmmIH, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 85 /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPMACSWD xmm1, xmmV, xmm2/m128, xmmIH","VPMACSWD xmmIH, xmm2/m128, xmmV, xmm1","vpmacswd xmmIH, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 96 /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPMACSWW xmm1, xmmV, xmm2/m128, xmmIH","VPMACSWW xmmIH, xmm2/m128, xmmV, xmm1","vpmacsww xmmIH, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 95 /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPMADCSSWD xmm1, xmmV, xmm2/m128, xmmIH","VPMADCSSWD xmmIH, xmm2/m128, xmmV, xmm1","vpmadcsswd xmmIH, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 A6 /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPMADCSWD xmm1, xmmV, xmm2/m128, xmmIH","VPMADCSWD xmmIH, xmm2/m128, xmmV, xmm1","vpmadcswd xmmIH, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 B6 /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPMADD52HUQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPMADD52HUQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpmadd52huq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 B5 /r","V","V","AVX512_IFMA+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VPMADD52HUQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPMADD52HUQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpmadd52huq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 B5 /r","V","V","AVX512_IFMA+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VPMADD52HUQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPMADD52HUQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpmadd52huq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 B5 /r","V","V","AVX512_IFMA","bscale8,scale64","rw,r,r,r","","" +"VPMADD52LUQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPMADD52LUQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpmadd52luq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 B4 /r","V","V","AVX512_IFMA+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VPMADD52LUQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPMADD52LUQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpmadd52luq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 B4 /r","V","V","AVX512_IFMA+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VPMADD52LUQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPMADD52LUQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpmadd52luq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 B4 /r","V","V","AVX512_IFMA","bscale8,scale64","rw,r,r,r","","" +"VPMADDUBSW xmm1, xmmV, xmm2/m128","VPMADDUBSW xmm2/m128, xmmV, xmm1","vpmaddubsw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 04 /r","V","V","AVX","","w,r,r","","" +"VPMADDUBSW xmm1, {k}{z}, xmmV, xmm2/m128","VPMADDUBSW xmm2/m128, xmmV, {k}{z}, xmm1","vpmaddubsw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.WIG 04 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPMADDUBSW ymm1, ymmV, ymm2/m256","VPMADDUBSW ymm2/m256, ymmV, ymm1","vpmaddubsw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 04 /r","V","V","AVX2","","w,r,r","","" +"VPMADDUBSW ymm1, {k}{z}, ymmV, ymm2/m256","VPMADDUBSW ymm2/m256, ymmV, {k}{z}, ymm1","vpmaddubsw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.WIG 04 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPMADDUBSW zmm1, {k}{z}, zmmV, zmm2/m512","VPMADDUBSW zmm2/m512, zmmV, {k}{z}, zmm1","vpmaddubsw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.WIG 04 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPMADDWD xmm1, xmmV, xmm2/m128","VPMADDWD xmm2/m128, xmmV, xmm1","vpmaddwd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG F5 /r","V","V","AVX","","w,r,r","","" +"VPMADDWD xmm1, {k}{z}, xmmV, xmm2/m128","VPMADDWD xmm2/m128, xmmV, {k}{z}, xmm1","vpmaddwd xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG F5 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPMADDWD ymm1, ymmV, ymm2/m256","VPMADDWD ymm2/m256, ymmV, ymm1","vpmaddwd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG F5 /r","V","V","AVX2","","w,r,r","","" +"VPMADDWD ymm1, {k}{z}, ymmV, ymm2/m256","VPMADDWD ymm2/m256, ymmV, {k}{z}, ymm1","vpmaddwd ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG F5 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPMADDWD zmm1, {k}{z}, zmmV, zmm2/m512","VPMADDWD zmm2/m512, zmmV, {k}{z}, zmm1","vpmaddwd zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG F5 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPMASKMOVD xmm1, xmmV, m128","VPMASKMOVD m128, xmmV, xmm1","vpmaskmovd m128, xmmV, xmm1","VEX.NDS.128.66.0F38.W0 8C /r","V","V","AVX2","modrm_memonly","w,r,r","","" +"VPMASKMOVD ymm1, ymmV, m256","VPMASKMOVD m256, ymmV, ymm1","vpmaskmovd m256, ymmV, ymm1","VEX.NDS.256.66.0F38.W0 8C /r","V","V","AVX2","modrm_memonly","w,r,r","","" +"VPMASKMOVD m128, xmmV, xmm1","VPMASKMOVD xmm1, xmmV, m128","vpmaskmovd xmm1, xmmV, m128","VEX.NDS.128.66.0F38.W0 8E /r","V","V","AVX2","modrm_memonly","w,r,r","","" +"VPMASKMOVD m256, ymmV, ymm1","VPMASKMOVD ymm1, ymmV, m256","vpmaskmovd ymm1, ymmV, m256","VEX.NDS.256.66.0F38.W0 8E /r","V","V","AVX2","modrm_memonly","w,r,r","","" +"VPMASKMOVQ xmm1, xmmV, m128","VPMASKMOVQ m128, xmmV, xmm1","vpmaskmovq m128, xmmV, xmm1","VEX.NDS.128.66.0F38.W1 8C /r","V","V","AVX2","modrm_memonly","w,r,r","","" +"VPMASKMOVQ ymm1, ymmV, m256","VPMASKMOVQ m256, ymmV, ymm1","vpmaskmovq m256, ymmV, ymm1","VEX.NDS.256.66.0F38.W1 8C /r","V","V","AVX2","modrm_memonly","w,r,r","","" +"VPMASKMOVQ m128, xmmV, xmm1","VPMASKMOVQ xmm1, xmmV, m128","vpmaskmovq xmm1, xmmV, m128","VEX.NDS.128.66.0F38.W1 8E /r","V","V","AVX2","modrm_memonly","w,r,r","","" +"VPMASKMOVQ m256, ymmV, ymm1","VPMASKMOVQ ymm1, ymmV, m256","vpmaskmovq ymm1, ymmV, m256","VEX.NDS.256.66.0F38.W1 8E /r","V","V","AVX2","modrm_memonly","w,r,r","","" +"VPMAXSB xmm1, xmmV, xmm2/m128","VPMAXSB xmm2/m128, xmmV, xmm1","vpmaxsb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 3C /r","V","V","AVX","","w,r,r","","" +"VPMAXSB xmm1, {k}{z}, xmmV, xmm2/m128","VPMAXSB xmm2/m128, xmmV, {k}{z}, xmm1","vpmaxsb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.WIG 3C /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPMAXSB ymm1, ymmV, ymm2/m256","VPMAXSB ymm2/m256, ymmV, ymm1","vpmaxsb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 3C /r","V","V","AVX2","","w,r,r","","" +"VPMAXSB ymm1, {k}{z}, ymmV, ymm2/m256","VPMAXSB ymm2/m256, ymmV, {k}{z}, ymm1","vpmaxsb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.WIG 3C /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPMAXSB zmm1, {k}{z}, zmmV, zmm2/m512","VPMAXSB zmm2/m512, zmmV, {k}{z}, zmm1","vpmaxsb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.WIG 3C /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPMAXSD xmm1, xmmV, xmm2/m128","VPMAXSD xmm2/m128, xmmV, xmm1","vpmaxsd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 3D /r","V","V","AVX","","w,r,r","","" +"VPMAXSD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPMAXSD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpmaxsd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 3D /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPMAXSD ymm1, ymmV, ymm2/m256","VPMAXSD ymm2/m256, ymmV, ymm1","vpmaxsd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 3D /r","V","V","AVX2","","w,r,r","","" +"VPMAXSD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPMAXSD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpmaxsd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 3D /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPMAXSD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPMAXSD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpmaxsd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 3D /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPMAXSQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPMAXSQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpmaxsq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 3D /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPMAXSQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPMAXSQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpmaxsq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 3D /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPMAXSQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPMAXSQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpmaxsq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 3D /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPMAXSW xmm1, xmmV, xmm2/m128","VPMAXSW xmm2/m128, xmmV, xmm1","vpmaxsw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG EE /r","V","V","AVX","","w,r,r","","" +"VPMAXSW xmm1, {k}{z}, xmmV, xmm2/m128","VPMAXSW xmm2/m128, xmmV, {k}{z}, xmm1","vpmaxsw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG EE /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPMAXSW ymm1, ymmV, ymm2/m256","VPMAXSW ymm2/m256, ymmV, ymm1","vpmaxsw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG EE /r","V","V","AVX2","","w,r,r","","" +"VPMAXSW ymm1, {k}{z}, ymmV, ymm2/m256","VPMAXSW ymm2/m256, ymmV, {k}{z}, ymm1","vpmaxsw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG EE /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPMAXSW zmm1, {k}{z}, zmmV, zmm2/m512","VPMAXSW zmm2/m512, zmmV, {k}{z}, zmm1","vpmaxsw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG EE /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPMAXUB xmm1, xmmV, xmm2/m128","VPMAXUB xmm2/m128, xmmV, xmm1","vpmaxub xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG DE /r","V","V","AVX","","w,r,r","","" +"VPMAXUB xmm1, {k}{z}, xmmV, xmm2/m128","VPMAXUB xmm2/m128, xmmV, {k}{z}, xmm1","vpmaxub xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG DE /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPMAXUB ymm1, ymmV, ymm2/m256","VPMAXUB ymm2/m256, ymmV, ymm1","vpmaxub ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG DE /r","V","V","AVX2","","w,r,r","","" +"VPMAXUB ymm1, {k}{z}, ymmV, ymm2/m256","VPMAXUB ymm2/m256, ymmV, {k}{z}, ymm1","vpmaxub ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG DE /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPMAXUB zmm1, {k}{z}, zmmV, zmm2/m512","VPMAXUB zmm2/m512, zmmV, {k}{z}, zmm1","vpmaxub zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG DE /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPMAXUD xmm1, xmmV, xmm2/m128","VPMAXUD xmm2/m128, xmmV, xmm1","vpmaxud xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 3F /r","V","V","AVX","","w,r,r","","" +"VPMAXUD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPMAXUD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpmaxud xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 3F /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPMAXUD ymm1, ymmV, ymm2/m256","VPMAXUD ymm2/m256, ymmV, ymm1","vpmaxud ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 3F /r","V","V","AVX2","","w,r,r","","" +"VPMAXUD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPMAXUD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpmaxud ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 3F /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPMAXUD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPMAXUD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpmaxud zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 3F /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPMAXUQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPMAXUQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpmaxuq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 3F /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPMAXUQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPMAXUQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpmaxuq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 3F /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPMAXUQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPMAXUQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpmaxuq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 3F /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPMAXUW xmm1, xmmV, xmm2/m128","VPMAXUW xmm2/m128, xmmV, xmm1","vpmaxuw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 3E /r","V","V","AVX","","w,r,r","","" +"VPMAXUW xmm1, {k}{z}, xmmV, xmm2/m128","VPMAXUW xmm2/m128, xmmV, {k}{z}, xmm1","vpmaxuw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.WIG 3E /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPMAXUW ymm1, ymmV, ymm2/m256","VPMAXUW ymm2/m256, ymmV, ymm1","vpmaxuw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 3E /r","V","V","AVX2","","w,r,r","","" +"VPMAXUW ymm1, {k}{z}, ymmV, ymm2/m256","VPMAXUW ymm2/m256, ymmV, {k}{z}, ymm1","vpmaxuw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.WIG 3E /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPMAXUW zmm1, {k}{z}, zmmV, zmm2/m512","VPMAXUW zmm2/m512, zmmV, {k}{z}, zmm1","vpmaxuw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.WIG 3E /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPMINSB xmm1, xmmV, xmm2/m128","VPMINSB xmm2/m128, xmmV, xmm1","vpminsb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 38 /r","V","V","AVX","","w,r,r","","" +"VPMINSB xmm1, {k}{z}, xmmV, xmm2/m128","VPMINSB xmm2/m128, xmmV, {k}{z}, xmm1","vpminsb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.WIG 38 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPMINSB ymm1, ymmV, ymm2/m256","VPMINSB ymm2/m256, ymmV, ymm1","vpminsb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 38 /r","V","V","AVX2","","w,r,r","","" +"VPMINSB ymm1, {k}{z}, ymmV, ymm2/m256","VPMINSB ymm2/m256, ymmV, {k}{z}, ymm1","vpminsb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.WIG 38 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPMINSB zmm1, {k}{z}, zmmV, zmm2/m512","VPMINSB zmm2/m512, zmmV, {k}{z}, zmm1","vpminsb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.WIG 38 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPMINSD xmm1, xmmV, xmm2/m128","VPMINSD xmm2/m128, xmmV, xmm1","vpminsd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 39 /r","V","V","AVX","","w,r,r","","" +"VPMINSD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPMINSD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpminsd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 39 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPMINSD ymm1, ymmV, ymm2/m256","VPMINSD ymm2/m256, ymmV, ymm1","vpminsd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 39 /r","V","V","AVX2","","w,r,r","","" +"VPMINSD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPMINSD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpminsd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 39 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPMINSD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPMINSD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpminsd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 39 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPMINSQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPMINSQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpminsq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 39 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPMINSQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPMINSQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpminsq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 39 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPMINSQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPMINSQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpminsq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 39 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPMINSW xmm1, xmmV, xmm2/m128","VPMINSW xmm2/m128, xmmV, xmm1","vpminsw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG EA /r","V","V","AVX","","w,r,r","","" +"VPMINSW xmm1, {k}{z}, xmmV, xmm2/m128","VPMINSW xmm2/m128, xmmV, {k}{z}, xmm1","vpminsw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG EA /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPMINSW ymm1, ymmV, ymm2/m256","VPMINSW ymm2/m256, ymmV, ymm1","vpminsw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG EA /r","V","V","AVX2","","w,r,r","","" +"VPMINSW ymm1, {k}{z}, ymmV, ymm2/m256","VPMINSW ymm2/m256, ymmV, {k}{z}, ymm1","vpminsw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG EA /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPMINSW zmm1, {k}{z}, zmmV, zmm2/m512","VPMINSW zmm2/m512, zmmV, {k}{z}, zmm1","vpminsw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG EA /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPMINUB xmm1, xmmV, xmm2/m128","VPMINUB xmm2/m128, xmmV, xmm1","vpminub xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG DA /r","V","V","AVX","","w,r,r","","" +"VPMINUB xmm1, {k}{z}, xmmV, xmm2/m128","VPMINUB xmm2/m128, xmmV, {k}{z}, xmm1","vpminub xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG DA /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPMINUB ymm1, ymmV, ymm2/m256","VPMINUB ymm2/m256, ymmV, ymm1","vpminub ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG DA /r","V","V","AVX2","","w,r,r","","" +"VPMINUB ymm1, {k}{z}, ymmV, ymm2/m256","VPMINUB ymm2/m256, ymmV, {k}{z}, ymm1","vpminub ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG DA /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPMINUB zmm1, {k}{z}, zmmV, zmm2/m512","VPMINUB zmm2/m512, zmmV, {k}{z}, zmm1","vpminub zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG DA /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPMINUD xmm1, xmmV, xmm2/m128","VPMINUD xmm2/m128, xmmV, xmm1","vpminud xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 3B /r","V","V","AVX","","w,r,r","","" +"VPMINUD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPMINUD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpminud xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 3B /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPMINUD ymm1, ymmV, ymm2/m256","VPMINUD ymm2/m256, ymmV, ymm1","vpminud ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 3B /r","V","V","AVX2","","w,r,r","","" +"VPMINUD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPMINUD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpminud ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 3B /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPMINUD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPMINUD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpminud zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 3B /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPMINUQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPMINUQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpminuq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 3B /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPMINUQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPMINUQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpminuq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 3B /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPMINUQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPMINUQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpminuq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 3B /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPMINUW xmm1, xmmV, xmm2/m128","VPMINUW xmm2/m128, xmmV, xmm1","vpminuw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 3A /r","V","V","AVX","","w,r,r","","" +"VPMINUW xmm1, {k}{z}, xmmV, xmm2/m128","VPMINUW xmm2/m128, xmmV, {k}{z}, xmm1","vpminuw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.WIG 3A /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPMINUW ymm1, ymmV, ymm2/m256","VPMINUW ymm2/m256, ymmV, ymm1","vpminuw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 3A /r","V","V","AVX2","","w,r,r","","" +"VPMINUW ymm1, {k}{z}, ymmV, ymm2/m256","VPMINUW ymm2/m256, ymmV, {k}{z}, ymm1","vpminuw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.WIG 3A /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPMINUW zmm1, {k}{z}, zmmV, zmm2/m512","VPMINUW zmm2/m512, zmmV, {k}{z}, zmm1","vpminuw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.WIG 3A /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPMOVB2M k1, xmm2","VPMOVB2M xmm2, k1","vpmovb2m xmm2, k1","EVEX.128.F3.0F38.W0 29 /r","V","V","AVX512BW+AVX512VL","modrm_regonly","w,r","","" +"VPMOVB2M k1, ymm2","VPMOVB2M ymm2, k1","vpmovb2m ymm2, k1","EVEX.256.F3.0F38.W0 29 /r","V","V","AVX512BW+AVX512VL","modrm_regonly","w,r","","" +"VPMOVB2M k1, zmm2","VPMOVB2M zmm2, k1","vpmovb2m zmm2, k1","EVEX.512.F3.0F38.W0 29 /r","V","V","AVX512BW","modrm_regonly","w,r","","" +"VPMOVD2M k1, xmm2","VPMOVD2M xmm2, k1","vpmovd2m xmm2, k1","EVEX.128.F3.0F38.W0 39 /r","V","V","AVX512DQ+AVX512VL","modrm_regonly","w,r","","" +"VPMOVD2M k1, ymm2","VPMOVD2M ymm2, k1","vpmovd2m ymm2, k1","EVEX.256.F3.0F38.W0 39 /r","V","V","AVX512DQ+AVX512VL","modrm_regonly","w,r","","" +"VPMOVD2M k1, zmm2","VPMOVD2M zmm2, k1","vpmovd2m zmm2, k1","EVEX.512.F3.0F38.W0 39 /r","V","V","AVX512DQ","modrm_regonly","w,r","","" +"VPMOVDB xmm2/m32, {k}{z}, xmm1","VPMOVDB xmm1, {k}{z}, xmm2/m32","vpmovdb xmm1, {k}{z}, xmm2/m32","EVEX.128.F3.0F38.W0 31 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVDB xmm2/m64, {k}{z}, ymm1","VPMOVDB ymm1, {k}{z}, xmm2/m64","vpmovdb ymm1, {k}{z}, xmm2/m64","EVEX.256.F3.0F38.W0 31 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVDB xmm2/m128, {k}{z}, zmm1","VPMOVDB zmm1, {k}{z}, xmm2/m128","vpmovdb zmm1, {k}{z}, xmm2/m128","EVEX.512.F3.0F38.W0 31 /r","V","V","AVX512F","scale16","w,r,r","","" +"VPMOVDW xmm2/m64, {k}{z}, xmm1","VPMOVDW xmm1, {k}{z}, xmm2/m64","vpmovdw xmm1, {k}{z}, xmm2/m64","EVEX.128.F3.0F38.W0 33 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVDW xmm2/m128, {k}{z}, ymm1","VPMOVDW ymm1, {k}{z}, xmm2/m128","vpmovdw ymm1, {k}{z}, xmm2/m128","EVEX.256.F3.0F38.W0 33 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VPMOVDW ymm2/m256, {k}{z}, zmm1","VPMOVDW zmm1, {k}{z}, ymm2/m256","vpmovdw zmm1, {k}{z}, ymm2/m256","EVEX.512.F3.0F38.W0 33 /r","V","V","AVX512F","scale32","w,r,r","","" +"VPMOVM2B xmm1, k2","VPMOVM2B k2, xmm1","vpmovm2b k2, xmm1","EVEX.128.F3.0F38.W0 28 /r","V","V","AVX512BW+AVX512VL","modrm_regonly","w,r","","" +"VPMOVM2B ymm1, k2","VPMOVM2B k2, ymm1","vpmovm2b k2, ymm1","EVEX.256.F3.0F38.W0 28 /r","V","V","AVX512BW+AVX512VL","modrm_regonly","w,r","","" +"VPMOVM2B zmm1, k2","VPMOVM2B k2, zmm1","vpmovm2b k2, zmm1","EVEX.512.F3.0F38.W0 28 /r","V","V","AVX512BW","modrm_regonly","w,r","","" +"VPMOVM2D xmm1, k2","VPMOVM2D k2, xmm1","vpmovm2d k2, xmm1","EVEX.128.F3.0F38.W0 38 /r","V","V","AVX512DQ+AVX512VL","modrm_regonly","w,r","","" +"VPMOVM2D ymm1, k2","VPMOVM2D k2, ymm1","vpmovm2d k2, ymm1","EVEX.256.F3.0F38.W0 38 /r","V","V","AVX512DQ+AVX512VL","modrm_regonly","w,r","","" +"VPMOVM2D zmm1, k2","VPMOVM2D k2, zmm1","vpmovm2d k2, zmm1","EVEX.512.F3.0F38.W0 38 /r","V","V","AVX512DQ","modrm_regonly","w,r","","" +"VPMOVM2Q xmm1, k2","VPMOVM2Q k2, xmm1","vpmovm2q k2, xmm1","EVEX.128.F3.0F38.W1 38 /r","V","V","AVX512DQ+AVX512VL","modrm_regonly","w,r","","" +"VPMOVM2Q ymm1, k2","VPMOVM2Q k2, ymm1","vpmovm2q k2, ymm1","EVEX.256.F3.0F38.W1 38 /r","V","V","AVX512DQ+AVX512VL","modrm_regonly","w,r","","" +"VPMOVM2Q zmm1, k2","VPMOVM2Q k2, zmm1","vpmovm2q k2, zmm1","EVEX.512.F3.0F38.W1 38 /r","V","V","AVX512DQ","modrm_regonly","w,r","","" +"VPMOVM2W xmm1, k2","VPMOVM2W k2, xmm1","vpmovm2w k2, xmm1","EVEX.128.F3.0F38.W1 28 /r","V","V","AVX512BW+AVX512VL","modrm_regonly","w,r","","" +"VPMOVM2W ymm1, k2","VPMOVM2W k2, ymm1","vpmovm2w k2, ymm1","EVEX.256.F3.0F38.W1 28 /r","V","V","AVX512BW+AVX512VL","modrm_regonly","w,r","","" +"VPMOVM2W zmm1, k2","VPMOVM2W k2, zmm1","vpmovm2w k2, zmm1","EVEX.512.F3.0F38.W1 28 /r","V","V","AVX512BW","modrm_regonly","w,r","","" +"VPMOVMSKB r32, xmm2","VPMOVMSKB xmm2, r32","vpmovmskb xmm2, r32","VEX.128.66.0F.WIG D7 /r","V","V","AVX","modrm_regonly","w,r","","" +"VPMOVMSKB r32, ymm2","VPMOVMSKB ymm2, r32","vpmovmskb ymm2, r32","VEX.256.66.0F.WIG D7 /r","V","V","AVX2","modrm_regonly","w,r","","" +"VPMOVQ2M k1, xmm2","VPMOVQ2M xmm2, k1","vpmovq2m xmm2, k1","EVEX.128.F3.0F38.W1 39 /r","V","V","AVX512DQ+AVX512VL","modrm_regonly","w,r","","" +"VPMOVQ2M k1, ymm2","VPMOVQ2M ymm2, k1","vpmovq2m ymm2, k1","EVEX.256.F3.0F38.W1 39 /r","V","V","AVX512DQ+AVX512VL","modrm_regonly","w,r","","" +"VPMOVQ2M k1, zmm2","VPMOVQ2M zmm2, k1","vpmovq2m zmm2, k1","EVEX.512.F3.0F38.W1 39 /r","V","V","AVX512DQ","modrm_regonly","w,r","","" +"VPMOVQB xmm2/m16, {k}{z}, xmm1","VPMOVQB xmm1, {k}{z}, xmm2/m16","vpmovqb xmm1, {k}{z}, xmm2/m16","EVEX.128.F3.0F38.W0 32 /r","V","V","AVX512F+AVX512VL","scale2","w,r,r","","" +"VPMOVQB xmm2/m32, {k}{z}, ymm1","VPMOVQB ymm1, {k}{z}, xmm2/m32","vpmovqb ymm1, {k}{z}, xmm2/m32","EVEX.256.F3.0F38.W0 32 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVQB xmm2/m64, {k}{z}, zmm1","VPMOVQB zmm1, {k}{z}, xmm2/m64","vpmovqb zmm1, {k}{z}, xmm2/m64","EVEX.512.F3.0F38.W0 32 /r","V","V","AVX512F","scale8","w,r,r","","" +"VPMOVQD xmm2/m64, {k}{z}, xmm1","VPMOVQD xmm1, {k}{z}, xmm2/m64","vpmovqd xmm1, {k}{z}, xmm2/m64","EVEX.128.F3.0F38.W0 35 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVQD xmm2/m128, {k}{z}, ymm1","VPMOVQD ymm1, {k}{z}, xmm2/m128","vpmovqd ymm1, {k}{z}, xmm2/m128","EVEX.256.F3.0F38.W0 35 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VPMOVQD ymm2/m256, {k}{z}, zmm1","VPMOVQD zmm1, {k}{z}, ymm2/m256","vpmovqd zmm1, {k}{z}, ymm2/m256","EVEX.512.F3.0F38.W0 35 /r","V","V","AVX512F","scale32","w,r,r","","" +"VPMOVQW xmm2/m32, {k}{z}, xmm1","VPMOVQW xmm1, {k}{z}, xmm2/m32","vpmovqw xmm1, {k}{z}, xmm2/m32","EVEX.128.F3.0F38.W0 34 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVQW xmm2/m64, {k}{z}, ymm1","VPMOVQW ymm1, {k}{z}, xmm2/m64","vpmovqw ymm1, {k}{z}, xmm2/m64","EVEX.256.F3.0F38.W0 34 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVQW xmm2/m128, {k}{z}, zmm1","VPMOVQW zmm1, {k}{z}, xmm2/m128","vpmovqw zmm1, {k}{z}, xmm2/m128","EVEX.512.F3.0F38.W0 34 /r","V","V","AVX512F","scale16","w,r,r","","" +"VPMOVSDB xmm2/m32, {k}{z}, xmm1","VPMOVSDB xmm1, {k}{z}, xmm2/m32","vpmovsdb xmm1, {k}{z}, xmm2/m32","EVEX.128.F3.0F38.W0 21 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVSDB xmm2/m64, {k}{z}, ymm1","VPMOVSDB ymm1, {k}{z}, xmm2/m64","vpmovsdb ymm1, {k}{z}, xmm2/m64","EVEX.256.F3.0F38.W0 21 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVSDB xmm2/m128, {k}{z}, zmm1","VPMOVSDB zmm1, {k}{z}, xmm2/m128","vpmovsdb zmm1, {k}{z}, xmm2/m128","EVEX.512.F3.0F38.W0 21 /r","V","V","AVX512F","scale16","w,r,r","","" +"VPMOVSDW xmm2/m64, {k}{z}, xmm1","VPMOVSDW xmm1, {k}{z}, xmm2/m64","vpmovsdw xmm1, {k}{z}, xmm2/m64","EVEX.128.F3.0F38.W0 23 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVSDW xmm2/m128, {k}{z}, ymm1","VPMOVSDW ymm1, {k}{z}, xmm2/m128","vpmovsdw ymm1, {k}{z}, xmm2/m128","EVEX.256.F3.0F38.W0 23 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VPMOVSDW ymm2/m256, {k}{z}, zmm1","VPMOVSDW zmm1, {k}{z}, ymm2/m256","vpmovsdw zmm1, {k}{z}, ymm2/m256","EVEX.512.F3.0F38.W0 23 /r","V","V","AVX512F","scale32","w,r,r","","" +"VPMOVSQB xmm2/m16, {k}{z}, xmm1","VPMOVSQB xmm1, {k}{z}, xmm2/m16","vpmovsqb xmm1, {k}{z}, xmm2/m16","EVEX.128.F3.0F38.W0 22 /r","V","V","AVX512F+AVX512VL","scale2","w,r,r","","" +"VPMOVSQB xmm2/m32, {k}{z}, ymm1","VPMOVSQB ymm1, {k}{z}, xmm2/m32","vpmovsqb ymm1, {k}{z}, xmm2/m32","EVEX.256.F3.0F38.W0 22 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVSQB xmm2/m64, {k}{z}, zmm1","VPMOVSQB zmm1, {k}{z}, xmm2/m64","vpmovsqb zmm1, {k}{z}, xmm2/m64","EVEX.512.F3.0F38.W0 22 /r","V","V","AVX512F","scale8","w,r,r","","" +"VPMOVSQD xmm2/m64, {k}{z}, xmm1","VPMOVSQD xmm1, {k}{z}, xmm2/m64","vpmovsqd xmm1, {k}{z}, xmm2/m64","EVEX.128.F3.0F38.W0 25 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVSQD xmm2/m128, {k}{z}, ymm1","VPMOVSQD ymm1, {k}{z}, xmm2/m128","vpmovsqd ymm1, {k}{z}, xmm2/m128","EVEX.256.F3.0F38.W0 25 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VPMOVSQD ymm2/m256, {k}{z}, zmm1","VPMOVSQD zmm1, {k}{z}, ymm2/m256","vpmovsqd zmm1, {k}{z}, ymm2/m256","EVEX.512.F3.0F38.W0 25 /r","V","V","AVX512F","scale32","w,r,r","","" +"VPMOVSQW xmm2/m32, {k}{z}, xmm1","VPMOVSQW xmm1, {k}{z}, xmm2/m32","vpmovsqw xmm1, {k}{z}, xmm2/m32","EVEX.128.F3.0F38.W0 24 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVSQW xmm2/m64, {k}{z}, ymm1","VPMOVSQW ymm1, {k}{z}, xmm2/m64","vpmovsqw ymm1, {k}{z}, xmm2/m64","EVEX.256.F3.0F38.W0 24 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVSQW xmm2/m128, {k}{z}, zmm1","VPMOVSQW zmm1, {k}{z}, xmm2/m128","vpmovsqw zmm1, {k}{z}, xmm2/m128","EVEX.512.F3.0F38.W0 24 /r","V","V","AVX512F","scale16","w,r,r","","" +"VPMOVSWB xmm2/m64, {k}{z}, xmm1","VPMOVSWB xmm1, {k}{z}, xmm2/m64","vpmovswb xmm1, {k}{z}, xmm2/m64","EVEX.128.F3.0F38.W0 20 /r","V","V","AVX512BW+AVX512VL","scale8","w,r,r","","" +"VPMOVSWB xmm2/m128, {k}{z}, ymm1","VPMOVSWB ymm1, {k}{z}, xmm2/m128","vpmovswb ymm1, {k}{z}, xmm2/m128","EVEX.256.F3.0F38.W0 20 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r","","" +"VPMOVSWB ymm2/m256, {k}{z}, zmm1","VPMOVSWB zmm1, {k}{z}, ymm2/m256","vpmovswb zmm1, {k}{z}, ymm2/m256","EVEX.512.F3.0F38.W0 20 /r","V","V","AVX512BW","scale32","w,r,r","","" +"VPMOVSXBD zmm1, {k}{z}, xmm2/m128","VPMOVSXBD xmm2/m128, {k}{z}, zmm1","vpmovsxbd xmm2/m128, {k}{z}, zmm1","EVEX.512.66.0F38.WIG 21 /r","V","V","AVX512F","scale16","w,r,r","","" +"VPMOVSXBD xmm1, xmm2/m32","VPMOVSXBD xmm2/m32, xmm1","vpmovsxbd xmm2/m32, xmm1","VEX.128.66.0F38.WIG 21 /r","V","V","AVX","","w,r","","" +"VPMOVSXBD xmm1, {k}{z}, xmm2/m32","VPMOVSXBD xmm2/m32, {k}{z}, xmm1","vpmovsxbd xmm2/m32, {k}{z}, xmm1","EVEX.128.66.0F38.WIG 21 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVSXBD ymm1, xmm2/m64","VPMOVSXBD xmm2/m64, ymm1","vpmovsxbd xmm2/m64, ymm1","VEX.256.66.0F38.WIG 21 /r","V","V","AVX2","","w,r","","" +"VPMOVSXBD ymm1, {k}{z}, xmm2/m64","VPMOVSXBD xmm2/m64, {k}{z}, ymm1","vpmovsxbd xmm2/m64, {k}{z}, ymm1","EVEX.256.66.0F38.WIG 21 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVSXBQ xmm1, xmm2/m16","VPMOVSXBQ xmm2/m16, xmm1","vpmovsxbq xmm2/m16, xmm1","VEX.128.66.0F38.WIG 22 /r","V","V","AVX","","w,r","","" +"VPMOVSXBQ xmm1, {k}{z}, xmm2/m16","VPMOVSXBQ xmm2/m16, {k}{z}, xmm1","vpmovsxbq xmm2/m16, {k}{z}, xmm1","EVEX.128.66.0F38.WIG 22 /r","V","V","AVX512F+AVX512VL","scale2","w,r,r","","" +"VPMOVSXBQ ymm1, xmm2/m32","VPMOVSXBQ xmm2/m32, ymm1","vpmovsxbq xmm2/m32, ymm1","VEX.256.66.0F38.WIG 22 /r","V","V","AVX2","","w,r","","" +"VPMOVSXBQ ymm1, {k}{z}, xmm2/m32","VPMOVSXBQ xmm2/m32, {k}{z}, ymm1","vpmovsxbq xmm2/m32, {k}{z}, ymm1","EVEX.256.66.0F38.WIG 22 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVSXBQ zmm1, {k}{z}, xmm2/m64","VPMOVSXBQ xmm2/m64, {k}{z}, zmm1","vpmovsxbq xmm2/m64, {k}{z}, zmm1","EVEX.512.66.0F38.WIG 22 /r","V","V","AVX512F","scale8","w,r,r","","" +"VPMOVSXBW ymm1, xmm2/m128","VPMOVSXBW xmm2/m128, ymm1","vpmovsxbw xmm2/m128, ymm1","VEX.256.66.0F38.WIG 20 /r","V","V","AVX2","","w,r","","" +"VPMOVSXBW ymm1, {k}{z}, xmm2/m128","VPMOVSXBW xmm2/m128, {k}{z}, ymm1","vpmovsxbw xmm2/m128, {k}{z}, ymm1","EVEX.256.66.0F38.WIG 20 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r","","" +"VPMOVSXBW xmm1, xmm2/m64","VPMOVSXBW xmm2/m64, xmm1","vpmovsxbw xmm2/m64, xmm1","VEX.128.66.0F38.WIG 20 /r","V","V","AVX","","w,r","","" +"VPMOVSXBW xmm1, {k}{z}, xmm2/m64","VPMOVSXBW xmm2/m64, {k}{z}, xmm1","vpmovsxbw xmm2/m64, {k}{z}, xmm1","EVEX.128.66.0F38.WIG 20 /r","V","V","AVX512BW+AVX512VL","scale8","w,r,r","","" +"VPMOVSXBW zmm1, {k}{z}, ymm2/m256","VPMOVSXBW ymm2/m256, {k}{z}, zmm1","vpmovsxbw ymm2/m256, {k}{z}, zmm1","EVEX.512.66.0F38.WIG 20 /r","V","V","AVX512BW","scale32","w,r,r","","" +"VPMOVSXDQ ymm1, xmm2/m128","VPMOVSXDQ xmm2/m128, ymm1","vpmovsxdq xmm2/m128, ymm1","VEX.256.66.0F38.WIG 25 /r","V","V","AVX2","","w,r","","" +"VPMOVSXDQ ymm1, {k}{z}, xmm2/m128","VPMOVSXDQ xmm2/m128, {k}{z}, ymm1","vpmovsxdq xmm2/m128, {k}{z}, ymm1","EVEX.256.66.0F38.W0 25 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VPMOVSXDQ xmm1, xmm2/m64","VPMOVSXDQ xmm2/m64, xmm1","vpmovsxdq xmm2/m64, xmm1","VEX.128.66.0F38.WIG 25 /r","V","V","AVX","","w,r","","" +"VPMOVSXDQ xmm1, {k}{z}, xmm2/m64","VPMOVSXDQ xmm2/m64, {k}{z}, xmm1","vpmovsxdq xmm2/m64, {k}{z}, xmm1","EVEX.128.66.0F38.W0 25 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVSXDQ zmm1, {k}{z}, ymm2/m256","VPMOVSXDQ ymm2/m256, {k}{z}, zmm1","vpmovsxdq ymm2/m256, {k}{z}, zmm1","EVEX.512.66.0F38.W0 25 /r","V","V","AVX512F","scale32","w,r,r","","" +"VPMOVSXWD ymm1, xmm2/m128","VPMOVSXWD xmm2/m128, ymm1","vpmovsxwd xmm2/m128, ymm1","VEX.256.66.0F38.WIG 23 /r","V","V","AVX2","","w,r","","" +"VPMOVSXWD ymm1, {k}{z}, xmm2/m128","VPMOVSXWD xmm2/m128, {k}{z}, ymm1","vpmovsxwd xmm2/m128, {k}{z}, ymm1","EVEX.256.66.0F38.WIG 23 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VPMOVSXWD xmm1, xmm2/m64","VPMOVSXWD xmm2/m64, xmm1","vpmovsxwd xmm2/m64, xmm1","VEX.128.66.0F38.WIG 23 /r","V","V","AVX","","w,r","","" +"VPMOVSXWD xmm1, {k}{z}, xmm2/m64","VPMOVSXWD xmm2/m64, {k}{z}, xmm1","vpmovsxwd xmm2/m64, {k}{z}, xmm1","EVEX.128.66.0F38.WIG 23 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVSXWD zmm1, {k}{z}, ymm2/m256","VPMOVSXWD ymm2/m256, {k}{z}, zmm1","vpmovsxwd ymm2/m256, {k}{z}, zmm1","EVEX.512.66.0F38.WIG 23 /r","V","V","AVX512F","scale32","w,r,r","","" +"VPMOVSXWQ zmm1, {k}{z}, xmm2/m128","VPMOVSXWQ xmm2/m128, {k}{z}, zmm1","vpmovsxwq xmm2/m128, {k}{z}, zmm1","EVEX.512.66.0F38.WIG 24 /r","V","V","AVX512F","scale16","w,r,r","","" +"VPMOVSXWQ xmm1, xmm2/m32","VPMOVSXWQ xmm2/m32, xmm1","vpmovsxwq xmm2/m32, xmm1","VEX.128.66.0F38.WIG 24 /r","V","V","AVX","","w,r","","" +"VPMOVSXWQ xmm1, {k}{z}, xmm2/m32","VPMOVSXWQ xmm2/m32, {k}{z}, xmm1","vpmovsxwq xmm2/m32, {k}{z}, xmm1","EVEX.128.66.0F38.WIG 24 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVSXWQ ymm1, xmm2/m64","VPMOVSXWQ xmm2/m64, ymm1","vpmovsxwq xmm2/m64, ymm1","VEX.256.66.0F38.WIG 24 /r","V","V","AVX2","","w,r","","" +"VPMOVSXWQ ymm1, {k}{z}, xmm2/m64","VPMOVSXWQ xmm2/m64, {k}{z}, ymm1","vpmovsxwq xmm2/m64, {k}{z}, ymm1","EVEX.256.66.0F38.WIG 24 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVUSDB xmm2/m32, {k}{z}, xmm1","VPMOVUSDB xmm1, {k}{z}, xmm2/m32","vpmovusdb xmm1, {k}{z}, xmm2/m32","EVEX.128.F3.0F38.W0 11 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVUSDB xmm2/m64, {k}{z}, ymm1","VPMOVUSDB ymm1, {k}{z}, xmm2/m64","vpmovusdb ymm1, {k}{z}, xmm2/m64","EVEX.256.F3.0F38.W0 11 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVUSDB xmm2/m128, {k}{z}, zmm1","VPMOVUSDB zmm1, {k}{z}, xmm2/m128","vpmovusdb zmm1, {k}{z}, xmm2/m128","EVEX.512.F3.0F38.W0 11 /r","V","V","AVX512F","scale16","w,r,r","","" +"VPMOVUSDW xmm2/m64, {k}{z}, xmm1","VPMOVUSDW xmm1, {k}{z}, xmm2/m64","vpmovusdw xmm1, {k}{z}, xmm2/m64","EVEX.128.F3.0F38.W0 13 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVUSDW xmm2/m128, {k}{z}, ymm1","VPMOVUSDW ymm1, {k}{z}, xmm2/m128","vpmovusdw ymm1, {k}{z}, xmm2/m128","EVEX.256.F3.0F38.W0 13 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VPMOVUSDW ymm2/m256, {k}{z}, zmm1","VPMOVUSDW zmm1, {k}{z}, ymm2/m256","vpmovusdw zmm1, {k}{z}, ymm2/m256","EVEX.512.F3.0F38.W0 13 /r","V","V","AVX512F","scale32","w,r,r","","" +"VPMOVUSQB xmm2/m16, {k}{z}, xmm1","VPMOVUSQB xmm1, {k}{z}, xmm2/m16","vpmovusqb xmm1, {k}{z}, xmm2/m16","EVEX.128.F3.0F38.W0 12 /r","V","V","AVX512F+AVX512VL","scale2","w,r,r","","" +"VPMOVUSQB xmm2/m32, {k}{z}, ymm1","VPMOVUSQB ymm1, {k}{z}, xmm2/m32","vpmovusqb ymm1, {k}{z}, xmm2/m32","EVEX.256.F3.0F38.W0 12 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVUSQB xmm2/m64, {k}{z}, zmm1","VPMOVUSQB zmm1, {k}{z}, xmm2/m64","vpmovusqb zmm1, {k}{z}, xmm2/m64","EVEX.512.F3.0F38.W0 12 /r","V","V","AVX512F","scale8","w,r,r","","" +"VPMOVUSQD xmm2/m64, {k}{z}, xmm1","VPMOVUSQD xmm1, {k}{z}, xmm2/m64","vpmovusqd xmm1, {k}{z}, xmm2/m64","EVEX.128.F3.0F38.W0 15 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVUSQD xmm2/m128, {k}{z}, ymm1","VPMOVUSQD ymm1, {k}{z}, xmm2/m128","vpmovusqd ymm1, {k}{z}, xmm2/m128","EVEX.256.F3.0F38.W0 15 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VPMOVUSQD ymm2/m256, {k}{z}, zmm1","VPMOVUSQD zmm1, {k}{z}, ymm2/m256","vpmovusqd zmm1, {k}{z}, ymm2/m256","EVEX.512.F3.0F38.W0 15 /r","V","V","AVX512F","scale32","w,r,r","","" +"VPMOVUSQW xmm2/m32, {k}{z}, xmm1","VPMOVUSQW xmm1, {k}{z}, xmm2/m32","vpmovusqw xmm1, {k}{z}, xmm2/m32","EVEX.128.F3.0F38.W0 14 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVUSQW xmm2/m64, {k}{z}, ymm1","VPMOVUSQW ymm1, {k}{z}, xmm2/m64","vpmovusqw ymm1, {k}{z}, xmm2/m64","EVEX.256.F3.0F38.W0 14 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVUSQW xmm2/m128, {k}{z}, zmm1","VPMOVUSQW zmm1, {k}{z}, xmm2/m128","vpmovusqw zmm1, {k}{z}, xmm2/m128","EVEX.512.F3.0F38.W0 14 /r","V","V","AVX512F","scale16","w,r,r","","" +"VPMOVUSWB xmm2/m64, {k}{z}, xmm1","VPMOVUSWB xmm1, {k}{z}, xmm2/m64","vpmovuswb xmm1, {k}{z}, xmm2/m64","EVEX.128.F3.0F38.W0 10 /r","V","V","AVX512BW+AVX512VL","scale8","w,r,r","","" +"VPMOVUSWB xmm2/m128, {k}{z}, ymm1","VPMOVUSWB ymm1, {k}{z}, xmm2/m128","vpmovuswb ymm1, {k}{z}, xmm2/m128","EVEX.256.F3.0F38.W0 10 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r","","" +"VPMOVUSWB ymm2/m256, {k}{z}, zmm1","VPMOVUSWB zmm1, {k}{z}, ymm2/m256","vpmovuswb zmm1, {k}{z}, ymm2/m256","EVEX.512.F3.0F38.W0 10 /r","V","V","AVX512BW","scale32","w,r,r","","" +"VPMOVW2M k1, xmm2","VPMOVW2M xmm2, k1","vpmovw2m xmm2, k1","EVEX.128.F3.0F38.W1 29 /r","V","V","AVX512BW+AVX512VL","modrm_regonly","w,r","","" +"VPMOVW2M k1, ymm2","VPMOVW2M ymm2, k1","vpmovw2m ymm2, k1","EVEX.256.F3.0F38.W1 29 /r","V","V","AVX512BW+AVX512VL","modrm_regonly","w,r","","" +"VPMOVW2M k1, zmm2","VPMOVW2M zmm2, k1","vpmovw2m zmm2, k1","EVEX.512.F3.0F38.W1 29 /r","V","V","AVX512BW","modrm_regonly","w,r","","" +"VPMOVWB xmm2/m64, {k}{z}, xmm1","VPMOVWB xmm1, {k}{z}, xmm2/m64","vpmovwb xmm1, {k}{z}, xmm2/m64","EVEX.128.F3.0F38.W0 30 /r","V","V","AVX512BW+AVX512VL","scale8","w,r,r","","" +"VPMOVWB xmm2/m128, {k}{z}, ymm1","VPMOVWB ymm1, {k}{z}, xmm2/m128","vpmovwb ymm1, {k}{z}, xmm2/m128","EVEX.256.F3.0F38.W0 30 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r","","" +"VPMOVWB ymm2/m256, {k}{z}, zmm1","VPMOVWB zmm1, {k}{z}, ymm2/m256","vpmovwb zmm1, {k}{z}, ymm2/m256","EVEX.512.F3.0F38.W0 30 /r","V","V","AVX512BW","scale32","w,r,r","","" +"VPMOVZXBD zmm1, {k}{z}, xmm2/m128","VPMOVZXBD xmm2/m128, {k}{z}, zmm1","vpmovzxbd xmm2/m128, {k}{z}, zmm1","EVEX.512.66.0F38.WIG 31 /r","V","V","AVX512F","scale16","w,r,r","","" +"VPMOVZXBD xmm1, xmm2/m32","VPMOVZXBD xmm2/m32, xmm1","vpmovzxbd xmm2/m32, xmm1","VEX.128.66.0F38.WIG 31 /r","V","V","AVX","","w,r","","" +"VPMOVZXBD xmm1, {k}{z}, xmm2/m32","VPMOVZXBD xmm2/m32, {k}{z}, xmm1","vpmovzxbd xmm2/m32, {k}{z}, xmm1","EVEX.128.66.0F38.WIG 31 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVZXBD ymm1, xmm2/m64","VPMOVZXBD xmm2/m64, ymm1","vpmovzxbd xmm2/m64, ymm1","VEX.256.66.0F38.WIG 31 /r","V","V","AVX2","","w,r","","" +"VPMOVZXBD ymm1, {k}{z}, xmm2/m64","VPMOVZXBD xmm2/m64, {k}{z}, ymm1","vpmovzxbd xmm2/m64, {k}{z}, ymm1","EVEX.256.66.0F38.WIG 31 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVZXBQ xmm1, xmm2/m16","VPMOVZXBQ xmm2/m16, xmm1","vpmovzxbq xmm2/m16, xmm1","VEX.128.66.0F38.WIG 32 /r","V","V","AVX","","w,r","","" +"VPMOVZXBQ xmm1, {k}{z}, xmm2/m16","VPMOVZXBQ xmm2/m16, {k}{z}, xmm1","vpmovzxbq xmm2/m16, {k}{z}, xmm1","EVEX.128.66.0F38.WIG 32 /r","V","V","AVX512F+AVX512VL","scale2","w,r,r","","" +"VPMOVZXBQ ymm1, xmm2/m32","VPMOVZXBQ xmm2/m32, ymm1","vpmovzxbq xmm2/m32, ymm1","VEX.256.66.0F38.WIG 32 /r","V","V","AVX2","","w,r","","" +"VPMOVZXBQ ymm1, {k}{z}, xmm2/m32","VPMOVZXBQ xmm2/m32, {k}{z}, ymm1","vpmovzxbq xmm2/m32, {k}{z}, ymm1","EVEX.256.66.0F38.WIG 32 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVZXBQ zmm1, {k}{z}, xmm2/m64","VPMOVZXBQ xmm2/m64, {k}{z}, zmm1","vpmovzxbq xmm2/m64, {k}{z}, zmm1","EVEX.512.66.0F38.WIG 32 /r","V","V","AVX512F","scale8","w,r,r","","" +"VPMOVZXBW ymm1, xmm2/m128","VPMOVZXBW xmm2/m128, ymm1","vpmovzxbw xmm2/m128, ymm1","VEX.256.66.0F38.WIG 30 /r","V","V","AVX2","","w,r","","" +"VPMOVZXBW ymm1, {k}{z}, xmm2/m128","VPMOVZXBW xmm2/m128, {k}{z}, ymm1","vpmovzxbw xmm2/m128, {k}{z}, ymm1","EVEX.256.66.0F38.WIG 30 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r","","" +"VPMOVZXBW xmm1, xmm2/m64","VPMOVZXBW xmm2/m64, xmm1","vpmovzxbw xmm2/m64, xmm1","VEX.128.66.0F38.WIG 30 /r","V","V","AVX","","w,r","","" +"VPMOVZXBW xmm1, {k}{z}, xmm2/m64","VPMOVZXBW xmm2/m64, {k}{z}, xmm1","vpmovzxbw xmm2/m64, {k}{z}, xmm1","EVEX.128.66.0F38.WIG 30 /r","V","V","AVX512BW+AVX512VL","scale8","w,r,r","","" +"VPMOVZXBW zmm1, {k}{z}, ymm2/m256","VPMOVZXBW ymm2/m256, {k}{z}, zmm1","vpmovzxbw ymm2/m256, {k}{z}, zmm1","EVEX.512.66.0F38.WIG 30 /r","V","V","AVX512BW","scale32","w,r,r","","" +"VPMOVZXDQ ymm1, xmm2/m128","VPMOVZXDQ xmm2/m128, ymm1","vpmovzxdq xmm2/m128, ymm1","VEX.256.66.0F38.WIG 35 /r","V","V","AVX2","","w,r","","" +"VPMOVZXDQ ymm1, {k}{z}, xmm2/m128","VPMOVZXDQ xmm2/m128, {k}{z}, ymm1","vpmovzxdq xmm2/m128, {k}{z}, ymm1","EVEX.256.66.0F38.W0 35 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VPMOVZXDQ xmm1, xmm2/m64","VPMOVZXDQ xmm2/m64, xmm1","vpmovzxdq xmm2/m64, xmm1","VEX.128.66.0F38.WIG 35 /r","V","V","AVX","","w,r","","" +"VPMOVZXDQ xmm1, {k}{z}, xmm2/m64","VPMOVZXDQ xmm2/m64, {k}{z}, xmm1","vpmovzxdq xmm2/m64, {k}{z}, xmm1","EVEX.128.66.0F38.W0 35 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVZXDQ zmm1, {k}{z}, ymm2/m256","VPMOVZXDQ ymm2/m256, {k}{z}, zmm1","vpmovzxdq ymm2/m256, {k}{z}, zmm1","EVEX.512.66.0F38.W0 35 /r","V","V","AVX512F","scale32","w,r,r","","" +"VPMOVZXWD ymm1, xmm2/m128","VPMOVZXWD xmm2/m128, ymm1","vpmovzxwd xmm2/m128, ymm1","VEX.256.66.0F38.WIG 33 /r","V","V","AVX2","","w,r","","" +"VPMOVZXWD ymm1, {k}{z}, xmm2/m128","VPMOVZXWD xmm2/m128, {k}{z}, ymm1","vpmovzxwd xmm2/m128, {k}{z}, ymm1","EVEX.256.66.0F38.WIG 33 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r","","" +"VPMOVZXWD xmm1, xmm2/m64","VPMOVZXWD xmm2/m64, xmm1","vpmovzxwd xmm2/m64, xmm1","VEX.128.66.0F38.WIG 33 /r","V","V","AVX","","w,r","","" +"VPMOVZXWD xmm1, {k}{z}, xmm2/m64","VPMOVZXWD xmm2/m64, {k}{z}, xmm1","vpmovzxwd xmm2/m64, {k}{z}, xmm1","EVEX.128.66.0F38.WIG 33 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMOVZXWD zmm1, {k}{z}, ymm2/m256","VPMOVZXWD ymm2/m256, {k}{z}, zmm1","vpmovzxwd ymm2/m256, {k}{z}, zmm1","EVEX.512.66.0F38.WIG 33 /r","V","V","AVX512F","scale32","w,r,r","","" +"VPMOVZXWQ zmm1, {k}{z}, xmm2/m128","VPMOVZXWQ xmm2/m128, {k}{z}, zmm1","vpmovzxwq xmm2/m128, {k}{z}, zmm1","EVEX.512.66.0F38.WIG 34 /r","V","V","AVX512F","scale16","w,r,r","","" +"VPMOVZXWQ xmm1, xmm2/m32","VPMOVZXWQ xmm2/m32, xmm1","vpmovzxwq xmm2/m32, xmm1","VEX.128.66.0F38.WIG 34 /r","V","V","AVX","","w,r","","" +"VPMOVZXWQ xmm1, {k}{z}, xmm2/m32","VPMOVZXWQ xmm2/m32, {k}{z}, xmm1","vpmovzxwq xmm2/m32, {k}{z}, xmm1","EVEX.128.66.0F38.WIG 34 /r","V","V","AVX512F+AVX512VL","scale4","w,r,r","","" +"VPMOVZXWQ ymm1, xmm2/m64","VPMOVZXWQ xmm2/m64, ymm1","vpmovzxwq xmm2/m64, ymm1","VEX.256.66.0F38.WIG 34 /r","V","V","AVX2","","w,r","","" +"VPMOVZXWQ ymm1, {k}{z}, xmm2/m64","VPMOVZXWQ xmm2/m64, {k}{z}, ymm1","vpmovzxwq xmm2/m64, {k}{z}, ymm1","EVEX.256.66.0F38.WIG 34 /r","V","V","AVX512F+AVX512VL","scale8","w,r,r","","" +"VPMULDQ xmm1, xmmV, xmm2/m128","VPMULDQ xmm2/m128, xmmV, xmm1","vpmuldq xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 28 /r","V","V","AVX","","w,r,r","","" +"VPMULDQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPMULDQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpmuldq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 28 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPMULDQ ymm1, ymmV, ymm2/m256","VPMULDQ ymm2/m256, ymmV, ymm1","vpmuldq ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 28 /r","V","V","AVX2","","w,r,r","","" +"VPMULDQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPMULDQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpmuldq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 28 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPMULDQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPMULDQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpmuldq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 28 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPMULHRSW xmm1, xmmV, xmm2/m128","VPMULHRSW xmm2/m128, xmmV, xmm1","vpmulhrsw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 0B /r","V","V","AVX","","w,r,r","","" +"VPMULHRSW xmm1, {k}{z}, xmmV, xmm2/m128","VPMULHRSW xmm2/m128, xmmV, {k}{z}, xmm1","vpmulhrsw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.WIG 0B /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPMULHRSW ymm1, ymmV, ymm2/m256","VPMULHRSW ymm2/m256, ymmV, ymm1","vpmulhrsw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 0B /r","V","V","AVX2","","w,r,r","","" +"VPMULHRSW ymm1, {k}{z}, ymmV, ymm2/m256","VPMULHRSW ymm2/m256, ymmV, {k}{z}, ymm1","vpmulhrsw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.WIG 0B /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPMULHRSW zmm1, {k}{z}, zmmV, zmm2/m512","VPMULHRSW zmm2/m512, zmmV, {k}{z}, zmm1","vpmulhrsw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.WIG 0B /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPMULHUW xmm1, xmmV, xmm2/m128","VPMULHUW xmm2/m128, xmmV, xmm1","vpmulhuw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG E4 /r","V","V","AVX","","w,r,r","","" +"VPMULHUW xmm1, {k}{z}, xmmV, xmm2/m128","VPMULHUW xmm2/m128, xmmV, {k}{z}, xmm1","vpmulhuw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG E4 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPMULHUW ymm1, ymmV, ymm2/m256","VPMULHUW ymm2/m256, ymmV, ymm1","vpmulhuw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG E4 /r","V","V","AVX2","","w,r,r","","" +"VPMULHUW ymm1, {k}{z}, ymmV, ymm2/m256","VPMULHUW ymm2/m256, ymmV, {k}{z}, ymm1","vpmulhuw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG E4 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPMULHUW zmm1, {k}{z}, zmmV, zmm2/m512","VPMULHUW zmm2/m512, zmmV, {k}{z}, zmm1","vpmulhuw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG E4 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPMULHW xmm1, xmmV, xmm2/m128","VPMULHW xmm2/m128, xmmV, xmm1","vpmulhw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG E5 /r","V","V","AVX","","w,r,r","","" +"VPMULHW xmm1, {k}{z}, xmmV, xmm2/m128","VPMULHW xmm2/m128, xmmV, {k}{z}, xmm1","vpmulhw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG E5 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPMULHW ymm1, ymmV, ymm2/m256","VPMULHW ymm2/m256, ymmV, ymm1","vpmulhw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG E5 /r","V","V","AVX2","","w,r,r","","" +"VPMULHW ymm1, {k}{z}, ymmV, ymm2/m256","VPMULHW ymm2/m256, ymmV, {k}{z}, ymm1","vpmulhw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG E5 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPMULHW zmm1, {k}{z}, zmmV, zmm2/m512","VPMULHW zmm2/m512, zmmV, {k}{z}, zmm1","vpmulhw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG E5 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPMULLD xmm1, xmmV, xmm2/m128","VPMULLD xmm2/m128, xmmV, xmm1","vpmulld xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 40 /r","V","V","AVX","","w,r,r","","" +"VPMULLD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPMULLD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpmulld xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 40 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPMULLD ymm1, ymmV, ymm2/m256","VPMULLD ymm2/m256, ymmV, ymm1","vpmulld ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 40 /r","V","V","AVX2","","w,r,r","","" +"VPMULLD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPMULLD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpmulld ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 40 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPMULLD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPMULLD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpmulld zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 40 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPMULLQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPMULLQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpmullq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 40 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPMULLQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPMULLQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpmullq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 40 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPMULLQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPMULLQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpmullq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 40 /r","V","V","AVX512DQ","bscale8,scale64","w,r,r,r","","" +"VPMULLW xmm1, xmmV, xmm2/m128","VPMULLW xmm2/m128, xmmV, xmm1","vpmullw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG D5 /r","V","V","AVX","","w,r,r","","" +"VPMULLW xmm1, {k}{z}, xmmV, xmm2/m128","VPMULLW xmm2/m128, xmmV, {k}{z}, xmm1","vpmullw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG D5 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPMULLW ymm1, ymmV, ymm2/m256","VPMULLW ymm2/m256, ymmV, ymm1","vpmullw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG D5 /r","V","V","AVX2","","w,r,r","","" +"VPMULLW ymm1, {k}{z}, ymmV, ymm2/m256","VPMULLW ymm2/m256, ymmV, {k}{z}, ymm1","vpmullw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG D5 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPMULLW zmm1, {k}{z}, zmmV, zmm2/m512","VPMULLW zmm2/m512, zmmV, {k}{z}, zmm1","vpmullw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG D5 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPMULTISHIFTQB xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPMULTISHIFTQB xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpmultishiftqb xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 83 /r","V","V","AVX512_VBMI+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPMULTISHIFTQB ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPMULTISHIFTQB ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpmultishiftqb ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 83 /r","V","V","AVX512_VBMI+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPMULTISHIFTQB zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPMULTISHIFTQB zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpmultishiftqb zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 83 /r","V","V","AVX512_VBMI","bscale8,scale64","w,r,r,r","","" +"VPMULUDQ xmm1, xmmV, xmm2/m128","VPMULUDQ xmm2/m128, xmmV, xmm1","vpmuludq xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG F4 /r","V","V","AVX","","w,r,r","","" +"VPMULUDQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPMULUDQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpmuludq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 F4 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPMULUDQ ymm1, ymmV, ymm2/m256","VPMULUDQ ymm2/m256, ymmV, ymm1","vpmuludq ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG F4 /r","V","V","AVX2","","w,r,r","","" +"VPMULUDQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPMULUDQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpmuludq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 F4 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPMULUDQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPMULUDQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpmuludq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 F4 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPOPCNTB xmm1, {k}{z}, xmm2/m128","VPOPCNTB xmm2/m128, {k}{z}, xmm1","vpopcntb xmm2/m128, {k}{z}, xmm1","EVEX.128.66.0F38.W0 54 /r","V","V","AVX512_BITALG+AVX512VL","scale16","w,r,r","","" +"VPOPCNTB ymm1, {k}{z}, ymm2/m256","VPOPCNTB ymm2/m256, {k}{z}, ymm1","vpopcntb ymm2/m256, {k}{z}, ymm1","EVEX.256.66.0F38.W0 54 /r","V","V","AVX512_BITALG+AVX512VL","scale32","w,r,r","","" +"VPOPCNTB zmm1, {k}{z}, zmm2/m512","VPOPCNTB zmm2/m512, {k}{z}, zmm1","vpopcntb zmm2/m512, {k}{z}, zmm1","EVEX.512.66.0F38.W0 54 /r","V","V","AVX512_BITALG","scale64","w,r,r","","" +"VPOPCNTD xmm1, {k}{z}, xmm2/m128/m32bcst","VPOPCNTD xmm2/m128/m32bcst, {k}{z}, xmm1","vpopcntd xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F38.W0 55 /r","V","V","AVX512_VPOPCNTDQ+AVX512VL","bscale4,scale16","w,r,r","","" +"VPOPCNTD ymm1, {k}{z}, ymm2/m256/m32bcst","VPOPCNTD ymm2/m256/m32bcst, {k}{z}, ymm1","vpopcntd ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F38.W0 55 /r","V","V","AVX512_VPOPCNTDQ+AVX512VL","bscale4,scale32","w,r,r","","" +"VPOPCNTD zmm1, {k}{z}, zmm2/m512/m32bcst","VPOPCNTD zmm2/m512/m32bcst, {k}{z}, zmm1","vpopcntd zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W0 55 /r","V","V","AVX512_VPOPCNTDQ","bscale4,scale64","w,r,r","","" +"VPOPCNTQ xmm1, {k}{z}, xmm2/m128/m64bcst","VPOPCNTQ xmm2/m128/m64bcst, {k}{z}, xmm1","vpopcntq xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F38.W1 55 /r","V","V","AVX512_VPOPCNTDQ+AVX512VL","bscale8,scale16","w,r,r","","" +"VPOPCNTQ ymm1, {k}{z}, ymm2/m256/m64bcst","VPOPCNTQ ymm2/m256/m64bcst, {k}{z}, ymm1","vpopcntq ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F38.W1 55 /r","V","V","AVX512_VPOPCNTDQ+AVX512VL","bscale8,scale32","w,r,r","","" +"VPOPCNTQ zmm1, {k}{z}, zmm2/m512/m64bcst","VPOPCNTQ zmm2/m512/m64bcst, {k}{z}, zmm1","vpopcntq zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W1 55 /r","V","V","AVX512_VPOPCNTDQ","bscale8,scale64","w,r,r","","" +"VPOPCNTW xmm1, {k}{z}, xmm2/m128","VPOPCNTW xmm2/m128, {k}{z}, xmm1","vpopcntw xmm2/m128, {k}{z}, xmm1","EVEX.128.66.0F38.W1 54 /r","V","V","AVX512_BITALG+AVX512VL","scale16","w,r,r","","" +"VPOPCNTW ymm1, {k}{z}, ymm2/m256","VPOPCNTW ymm2/m256, {k}{z}, ymm1","vpopcntw ymm2/m256, {k}{z}, ymm1","EVEX.256.66.0F38.W1 54 /r","V","V","AVX512_BITALG+AVX512VL","scale32","w,r,r","","" +"VPOPCNTW zmm1, {k}{z}, zmm2/m512","VPOPCNTW zmm2/m512, {k}{z}, zmm1","vpopcntw zmm2/m512, {k}{z}, zmm1","EVEX.512.66.0F38.W1 54 /r","V","V","AVX512_BITALG","scale64","w,r,r","","" +"VPOR xmm1, xmmV, xmm2/m128","VPOR xmm2/m128, xmmV, xmm1","vpor xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG EB /r","V","V","AVX","","w,r,r","","" +"VPOR ymm1, ymmV, ymm2/m256","VPOR ymm2/m256, ymmV, ymm1","vpor ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG EB /r","V","V","AVX2","","w,r,r","","" +"VPORD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPORD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpord xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W0 EB /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPORD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPORD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpord ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W0 EB /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPORD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPORD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpord zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W0 EB /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPORQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPORQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vporq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 EB /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPORQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPORQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vporq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 EB /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPORQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPORQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vporq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 EB /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPPERM xmm1, xmmV, xmmIH, xmm2/m128","VPPERM xmm2/m128, xmmIH, xmmV, xmm1","vpperm xmm2/m128, xmmIH, xmmV, xmm1","XOP.NDS.128.08.W1 A3 /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPPERM xmm1, xmmV, xmm2/m128, xmmIH","VPPERM xmmIH, xmm2/m128, xmmV, xmm1","vpperm xmmIH, xmm2/m128, xmmV, xmm1","XOP.NDS.128.08.W0 A3 /r /is4","V","V","XOP","amd","w,r,r,r","","" +"VPROLD xmmV, {k}{z}, xmm2/m128/m32bcst, imm8u","VPROLD imm8u, xmm2/m128/m32bcst, {k}{z}, xmmV","vprold imm8u, xmm2/m128/m32bcst, {k}{z}, xmmV","EVEX.NDD.128.66.0F.W0 72 /1 ib","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPROLD ymmV, {k}{z}, ymm2/m256/m32bcst, imm8u","VPROLD imm8u, ymm2/m256/m32bcst, {k}{z}, ymmV","vprold imm8u, ymm2/m256/m32bcst, {k}{z}, ymmV","EVEX.NDD.256.66.0F.W0 72 /1 ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPROLD zmmV, {k}{z}, zmm2/m512/m32bcst, imm8u","VPROLD imm8u, zmm2/m512/m32bcst, {k}{z}, zmmV","vprold imm8u, zmm2/m512/m32bcst, {k}{z}, zmmV","EVEX.NDD.512.66.0F.W0 72 /1 ib","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPROLQ xmmV, {k}{z}, xmm2/m128/m64bcst, imm8u","VPROLQ imm8u, xmm2/m128/m64bcst, {k}{z}, xmmV","vprolq imm8u, xmm2/m128/m64bcst, {k}{z}, xmmV","EVEX.NDD.128.66.0F.W1 72 /1 ib","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPROLQ ymmV, {k}{z}, ymm2/m256/m64bcst, imm8u","VPROLQ imm8u, ymm2/m256/m64bcst, {k}{z}, ymmV","vprolq imm8u, ymm2/m256/m64bcst, {k}{z}, ymmV","EVEX.NDD.256.66.0F.W1 72 /1 ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPROLQ zmmV, {k}{z}, zmm2/m512/m64bcst, imm8u","VPROLQ imm8u, zmm2/m512/m64bcst, {k}{z}, zmmV","vprolq imm8u, zmm2/m512/m64bcst, {k}{z}, zmmV","EVEX.NDD.512.66.0F.W1 72 /1 ib","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPROLVD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPROLVD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vprolvd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 15 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPROLVD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPROLVD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vprolvd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 15 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPROLVD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPROLVD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vprolvd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 15 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPROLVQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPROLVQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vprolvq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 15 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPROLVQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPROLVQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vprolvq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 15 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPROLVQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPROLVQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vprolvq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 15 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPRORD xmmV, {k}{z}, xmm2/m128/m32bcst, imm8u","VPRORD imm8u, xmm2/m128/m32bcst, {k}{z}, xmmV","vprord imm8u, xmm2/m128/m32bcst, {k}{z}, xmmV","EVEX.NDD.128.66.0F.W0 72 /0 ib","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPRORD ymmV, {k}{z}, ymm2/m256/m32bcst, imm8u","VPRORD imm8u, ymm2/m256/m32bcst, {k}{z}, ymmV","vprord imm8u, ymm2/m256/m32bcst, {k}{z}, ymmV","EVEX.NDD.256.66.0F.W0 72 /0 ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPRORD zmmV, {k}{z}, zmm2/m512/m32bcst, imm8u","VPRORD imm8u, zmm2/m512/m32bcst, {k}{z}, zmmV","vprord imm8u, zmm2/m512/m32bcst, {k}{z}, zmmV","EVEX.NDD.512.66.0F.W0 72 /0 ib","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPRORQ xmmV, {k}{z}, xmm2/m128/m64bcst, imm8u","VPRORQ imm8u, xmm2/m128/m64bcst, {k}{z}, xmmV","vprorq imm8u, xmm2/m128/m64bcst, {k}{z}, xmmV","EVEX.NDD.128.66.0F.W1 72 /0 ib","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPRORQ ymmV, {k}{z}, ymm2/m256/m64bcst, imm8u","VPRORQ imm8u, ymm2/m256/m64bcst, {k}{z}, ymmV","vprorq imm8u, ymm2/m256/m64bcst, {k}{z}, ymmV","EVEX.NDD.256.66.0F.W1 72 /0 ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPRORQ zmmV, {k}{z}, zmm2/m512/m64bcst, imm8u","VPRORQ imm8u, zmm2/m512/m64bcst, {k}{z}, zmmV","vprorq imm8u, zmm2/m512/m64bcst, {k}{z}, zmmV","EVEX.NDD.512.66.0F.W1 72 /0 ib","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPRORVD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPRORVD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vprorvd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 14 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPRORVD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPRORVD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vprorvd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 14 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPRORVD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPRORVD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vprorvd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 14 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPRORVQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPRORVQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vprorvq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 14 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPRORVQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPRORVQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vprorvq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 14 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPRORVQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPRORVQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vprorvq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 14 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPROTB xmm1, xmm2/m128, imm8u","VPROTB imm8u, xmm2/m128, xmm1","vprotb imm8u, xmm2/m128, xmm1","XOP.128.08.W0 C0 /r ib","V","V","XOP","amd","w,r,r","","" +"VPROTB xmm1, xmmV, xmm2/m128","VPROTB xmm2/m128, xmmV, xmm1","vprotb xmm2/m128, xmmV, xmm1","XOP.NDS.128.09.W1 90 /r","V","V","XOP","amd","w,r,r","","" +"VPROTB xmm1, xmm2/m128, xmmV","VPROTB xmmV, xmm2/m128, xmm1","vprotb xmmV, xmm2/m128, xmm1","XOP.NDS.128.09.W0 90 /r","V","V","XOP","amd","w,r,r","","" +"VPROTD xmm1, xmm2/m128, imm8u","VPROTD imm8u, xmm2/m128, xmm1","vprotd imm8u, xmm2/m128, xmm1","XOP.128.08.W0 C2 /r ib","V","V","XOP","amd","w,r,r","","" +"VPROTD xmm1, xmmV, xmm2/m128","VPROTD xmm2/m128, xmmV, xmm1","vprotd xmm2/m128, xmmV, xmm1","XOP.NDS.128.09.W1 92 /r","V","V","XOP","amd","w,r,r","","" +"VPROTD xmm1, xmm2/m128, xmmV","VPROTD xmmV, xmm2/m128, xmm1","vprotd xmmV, xmm2/m128, xmm1","XOP.NDS.128.09.W0 92 /r","V","V","XOP","amd","w,r,r","","" +"VPROTQ xmm1, xmm2/m128, imm8u","VPROTQ imm8u, xmm2/m128, xmm1","vprotq imm8u, xmm2/m128, xmm1","XOP.128.08.W0 C3 /r ib","V","V","XOP","amd","w,r,r","","" +"VPROTQ xmm1, xmmV, xmm2/m128","VPROTQ xmm2/m128, xmmV, xmm1","vprotq xmm2/m128, xmmV, xmm1","XOP.NDS.128.09.W1 93 /r","V","V","XOP","amd","w,r,r","","" +"VPROTQ xmm1, xmm2/m128, xmmV","VPROTQ xmmV, xmm2/m128, xmm1","vprotq xmmV, xmm2/m128, xmm1","XOP.NDS.128.09.W0 93 /r","V","V","XOP","amd","w,r,r","","" +"VPROTW xmm1, xmm2/m128, imm8u","VPROTW imm8u, xmm2/m128, xmm1","vprotw imm8u, xmm2/m128, xmm1","XOP.128.08.W0 C1 /r ib","V","V","XOP","amd","w,r,r","","" +"VPROTW xmm1, xmmV, xmm2/m128","VPROTW xmm2/m128, xmmV, xmm1","vprotw xmm2/m128, xmmV, xmm1","XOP.NDS.128.09.W1 91 /r","V","V","XOP","amd","w,r,r","","" +"VPROTW xmm1, xmm2/m128, xmmV","VPROTW xmmV, xmm2/m128, xmm1","vprotw xmmV, xmm2/m128, xmm1","XOP.NDS.128.09.W0 91 /r","V","V","XOP","amd","w,r,r","","" +"VPSADBW xmm1, xmmV, xmm2/m128","VPSADBW xmm2/m128, xmmV, xmm1","vpsadbw xmm2/m128, xmmV, xmm1","EVEX.NDS.128.66.0F.WIG F6 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r","","" +"VPSADBW xmm1, xmmV, xmm2/m128","VPSADBW xmm2/m128, xmmV, xmm1","vpsadbw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG F6 /r","V","V","AVX","","w,r,r","","" +"VPSADBW ymm1, ymmV, ymm2/m256","VPSADBW ymm2/m256, ymmV, ymm1","vpsadbw ymm2/m256, ymmV, ymm1","EVEX.NDS.256.66.0F.WIG F6 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r","","" +"VPSADBW ymm1, ymmV, ymm2/m256","VPSADBW ymm2/m256, ymmV, ymm1","vpsadbw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG F6 /r","V","V","AVX2","","w,r,r","","" +"VPSADBW zmm1, zmmV, zmm2/m512","VPSADBW zmm2/m512, zmmV, zmm1","vpsadbw zmm2/m512, zmmV, zmm1","EVEX.NDS.512.66.0F.WIG F6 /r","V","V","AVX512BW","scale64","w,r,r","","" +"VPSCATTERDD vm32x, {k1-k7}, xmm1","VPSCATTERDD xmm1, {k1-k7}, vm32x","vpscatterdd xmm1, {k1-k7}, vm32x","EVEX.128.66.0F38.W0 A0 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VPSCATTERDD vm32y, {k1-k7}, ymm1","VPSCATTERDD ymm1, {k1-k7}, vm32y","vpscatterdd ymm1, {k1-k7}, vm32y","EVEX.256.66.0F38.W0 A0 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VPSCATTERDD vm32z, {k1-k7}, zmm1","VPSCATTERDD zmm1, {k1-k7}, vm32z","vpscatterdd zmm1, {k1-k7}, vm32z","EVEX.512.66.0F38.W0 A0 /vsib","V","V","AVX512F","modrm_memonly,scale4","w,rw,r","","" +"VPSCATTERDQ vm32x, {k1-k7}, xmm1","VPSCATTERDQ xmm1, {k1-k7}, vm32x","vpscatterdq xmm1, {k1-k7}, vm32x","EVEX.128.66.0F38.W1 A0 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VPSCATTERDQ vm32x, {k1-k7}, ymm1","VPSCATTERDQ ymm1, {k1-k7}, vm32x","vpscatterdq ymm1, {k1-k7}, vm32x","EVEX.256.66.0F38.W1 A0 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VPSCATTERDQ vm32y, {k1-k7}, zmm1","VPSCATTERDQ zmm1, {k1-k7}, vm32y","vpscatterdq zmm1, {k1-k7}, vm32y","EVEX.512.66.0F38.W1 A0 /vsib","V","V","AVX512F","modrm_memonly,scale8","w,rw,r","","" +"VPSCATTERQD vm64x, {k1-k7}, xmm1","VPSCATTERQD xmm1, {k1-k7}, vm64x","vpscatterqd xmm1, {k1-k7}, vm64x","EVEX.128.66.0F38.W0 A1 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VPSCATTERQD vm64y, {k1-k7}, xmm1","VPSCATTERQD xmm1, {k1-k7}, vm64y","vpscatterqd xmm1, {k1-k7}, vm64y","EVEX.256.66.0F38.W0 A1 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VPSCATTERQD vm64z, {k1-k7}, ymm1","VPSCATTERQD ymm1, {k1-k7}, vm64z","vpscatterqd ymm1, {k1-k7}, vm64z","EVEX.512.66.0F38.W0 A1 /vsib","V","V","AVX512F","modrm_memonly,scale4","w,rw,r","","" +"VPSCATTERQQ vm64x, {k1-k7}, xmm1","VPSCATTERQQ xmm1, {k1-k7}, vm64x","vpscatterqq xmm1, {k1-k7}, vm64x","EVEX.128.66.0F38.W1 A1 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VPSCATTERQQ vm64y, {k1-k7}, ymm1","VPSCATTERQQ ymm1, {k1-k7}, vm64y","vpscatterqq ymm1, {k1-k7}, vm64y","EVEX.256.66.0F38.W1 A1 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VPSCATTERQQ vm64z, {k1-k7}, zmm1","VPSCATTERQQ zmm1, {k1-k7}, vm64z","vpscatterqq zmm1, {k1-k7}, vm64z","EVEX.512.66.0F38.W1 A1 /vsib","V","V","AVX512F","modrm_memonly,scale8","w,rw,r","","" +"VPSHAB xmm1, xmmV, xmm2/m128","VPSHAB xmm2/m128, xmmV, xmm1","vpshab xmm2/m128, xmmV, xmm1","XOP.NDS.128.09.W1 98 /r","V","V","XOP","amd","w,r,r","","" +"VPSHAB xmm1, xmm2/m128, xmmV","VPSHAB xmmV, xmm2/m128, xmm1","vpshab xmmV, xmm2/m128, xmm1","XOP.NDS.128.09.W0 98 /r","V","V","XOP","amd","w,r,r","","" +"VPSHAD xmm1, xmmV, xmm2/m128","VPSHAD xmm2/m128, xmmV, xmm1","vpshad xmm2/m128, xmmV, xmm1","XOP.NDS.128.09.W1 9A /r","V","V","XOP","amd","w,r,r","","" +"VPSHAD xmm1, xmm2/m128, xmmV","VPSHAD xmmV, xmm2/m128, xmm1","vpshad xmmV, xmm2/m128, xmm1","XOP.NDS.128.09.W0 9A /r","V","V","XOP","amd","w,r,r","","" +"VPSHAQ xmm1, xmmV, xmm2/m128","VPSHAQ xmm2/m128, xmmV, xmm1","vpshaq xmm2/m128, xmmV, xmm1","XOP.NDS.128.09.W1 9B /r","V","V","XOP","amd","w,r,r","","" +"VPSHAQ xmm1, xmm2/m128, xmmV","VPSHAQ xmmV, xmm2/m128, xmm1","vpshaq xmmV, xmm2/m128, xmm1","XOP.NDS.128.09.W0 9B /r","V","V","XOP","amd","w,r,r","","" +"VPSHAW xmm1, xmmV, xmm2/m128","VPSHAW xmm2/m128, xmmV, xmm1","vpshaw xmm2/m128, xmmV, xmm1","XOP.NDS.128.09.W1 99 /r","V","V","XOP","amd","w,r,r","","" +"VPSHAW xmm1, xmm2/m128, xmmV","VPSHAW xmmV, xmm2/m128, xmm1","vpshaw xmmV, xmm2/m128, xmm1","XOP.NDS.128.09.W0 99 /r","V","V","XOP","amd","w,r,r","","" +"VPSHLB xmm1, xmmV, xmm2/m128","VPSHLB xmm2/m128, xmmV, xmm1","vpshlb xmm2/m128, xmmV, xmm1","XOP.NDS.128.09.W1 94 /r","V","V","XOP","amd","w,r,r","","" +"VPSHLB xmm1, xmm2/m128, xmmV","VPSHLB xmmV, xmm2/m128, xmm1","vpshlb xmmV, xmm2/m128, xmm1","XOP.NDS.128.09.W0 94 /r","V","V","XOP","amd","w,r,r","","" +"VPSHLD xmm1, xmmV, xmm2/m128","VPSHLD xmm2/m128, xmmV, xmm1","vpshld xmm2/m128, xmmV, xmm1","XOP.NDS.128.09.W1 96 /r","V","V","XOP","amd","w,r,r","","" +"VPSHLD xmm1, xmm2/m128, xmmV","VPSHLD xmmV, xmm2/m128, xmm1","vpshld xmmV, xmm2/m128, xmm1","XOP.NDS.128.09.W0 96 /r","V","V","XOP","amd","w,r,r","","" +"VPSHLDD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst, imm8u","VPSHLDD imm8u, xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpshldd imm8u, xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F3A.W0 71 /r ib","V","V","AVX512_VBMI2+AVX512VL","bscale4,scale16","w,r,r,r,r","","" +"VPSHLDD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst, imm8u","VPSHLDD imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpshldd imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W0 71 /r ib","V","V","AVX512_VBMI2+AVX512VL","bscale4,scale32","w,r,r,r,r","","" +"VPSHLDD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst, imm8u","VPSHLDD imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpshldd imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W0 71 /r ib","V","V","AVX512_VBMI2","bscale4,scale64","w,r,r,r,r","","" +"VPSHLDQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst, imm8u","VPSHLDQ imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpshldq imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F3A.W1 71 /r ib","V","V","AVX512_VBMI2+AVX512VL","bscale8,scale16","w,r,r,r,r","","" +"VPSHLDQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst, imm8u","VPSHLDQ imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpshldq imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W1 71 /r ib","V","V","AVX512_VBMI2+AVX512VL","bscale8,scale32","w,r,r,r,r","","" +"VPSHLDQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst, imm8u","VPSHLDQ imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpshldq imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W1 71 /r ib","V","V","AVX512_VBMI2","bscale8,scale64","w,r,r,r,r","","" +"VPSHLDVD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPSHLDVD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpshldvd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 71 /r","V","V","AVX512_VBMI2+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VPSHLDVD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPSHLDVD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpshldvd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 71 /r","V","V","AVX512_VBMI2+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VPSHLDVD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPSHLDVD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpshldvd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 71 /r","V","V","AVX512_VBMI2","bscale4,scale64","rw,r,r,r","","" +"VPSHLDVQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPSHLDVQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpshldvq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 71 /r","V","V","AVX512_VBMI2+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VPSHLDVQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPSHLDVQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpshldvq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 71 /r","V","V","AVX512_VBMI2+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VPSHLDVQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPSHLDVQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpshldvq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 71 /r","V","V","AVX512_VBMI2","bscale8,scale64","rw,r,r,r","","" +"VPSHLDVW xmm1, {k}{z}, xmmV, xmm2/m128","VPSHLDVW xmm2/m128, xmmV, {k}{z}, xmm1","vpshldvw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 70 /r","V","V","AVX512_VBMI2+AVX512VL","scale16","rw,r,r,r","","" +"VPSHLDVW ymm1, {k}{z}, ymmV, ymm2/m256","VPSHLDVW ymm2/m256, ymmV, {k}{z}, ymm1","vpshldvw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 70 /r","V","V","AVX512_VBMI2+AVX512VL","scale32","rw,r,r,r","","" +"VPSHLDVW zmm1, {k}{z}, zmmV, zmm2/m512","VPSHLDVW zmm2/m512, zmmV, {k}{z}, zmm1","vpshldvw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 70 /r","V","V","AVX512_VBMI2","scale64","rw,r,r,r","","" +"VPSHLDW xmm1, {k}{z}, xmmV, xmm2/m128, imm8u","VPSHLDW imm8u, xmm2/m128, xmmV, {k}{z}, xmm1","vpshldw imm8u, xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F3A.W1 70 /r ib","V","V","AVX512_VBMI2+AVX512VL","scale16","w,r,r,r,r","","" +"VPSHLDW ymm1, {k}{z}, ymmV, ymm2/m256, imm8u","VPSHLDW imm8u, ymm2/m256, ymmV, {k}{z}, ymm1","vpshldw imm8u, ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W1 70 /r ib","V","V","AVX512_VBMI2+AVX512VL","scale32","w,r,r,r,r","","" +"VPSHLDW zmm1, {k}{z}, zmmV, zmm2/m512, imm8u","VPSHLDW imm8u, zmm2/m512, zmmV, {k}{z}, zmm1","vpshldw imm8u, zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W1 70 /r ib","V","V","AVX512_VBMI2","scale64","w,r,r,r,r","","" +"VPSHLQ xmm1, xmmV, xmm2/m128","VPSHLQ xmm2/m128, xmmV, xmm1","vpshlq xmm2/m128, xmmV, xmm1","XOP.NDS.128.09.W1 97 /r","V","V","XOP","amd","w,r,r","","" +"VPSHLQ xmm1, xmm2/m128, xmmV","VPSHLQ xmmV, xmm2/m128, xmm1","vpshlq xmmV, xmm2/m128, xmm1","XOP.NDS.128.09.W0 97 /r","V","V","XOP","amd","w,r,r","","" +"VPSHLW xmm1, xmmV, xmm2/m128","VPSHLW xmm2/m128, xmmV, xmm1","vpshlw xmm2/m128, xmmV, xmm1","XOP.NDS.128.09.W1 95 /r","V","V","XOP","amd","w,r,r","","" +"VPSHLW xmm1, xmm2/m128, xmmV","VPSHLW xmmV, xmm2/m128, xmm1","vpshlw xmmV, xmm2/m128, xmm1","XOP.NDS.128.09.W0 95 /r","V","V","XOP","amd","w,r,r","","" +"VPSHRDD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst, imm8u","VPSHRDD imm8u, xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpshrdd imm8u, xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F3A.W0 73 /r ib","V","V","AVX512_VBMI2+AVX512VL","bscale4,scale16","w,r,r,r,r","","" +"VPSHRDD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst, imm8u","VPSHRDD imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpshrdd imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W0 73 /r ib","V","V","AVX512_VBMI2+AVX512VL","bscale4,scale32","w,r,r,r,r","","" +"VPSHRDD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst, imm8u","VPSHRDD imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpshrdd imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W0 73 /r ib","V","V","AVX512_VBMI2","bscale4,scale64","w,r,r,r,r","","" +"VPSHRDQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst, imm8u","VPSHRDQ imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpshrdq imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F3A.W1 73 /r ib","V","V","AVX512_VBMI2+AVX512VL","bscale8,scale16","w,r,r,r,r","","" +"VPSHRDQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst, imm8u","VPSHRDQ imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpshrdq imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W1 73 /r ib","V","V","AVX512_VBMI2+AVX512VL","bscale8,scale32","w,r,r,r,r","","" +"VPSHRDQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst, imm8u","VPSHRDQ imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpshrdq imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W1 73 /r ib","V","V","AVX512_VBMI2","bscale8,scale64","w,r,r,r,r","","" +"VPSHRDVD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPSHRDVD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpshrdvd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W0 73 /r","V","V","AVX512_VBMI2+AVX512VL","bscale4,scale16","rw,r,r,r","","" +"VPSHRDVD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPSHRDVD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpshrdvd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W0 73 /r","V","V","AVX512_VBMI2+AVX512VL","bscale4,scale32","rw,r,r,r","","" +"VPSHRDVD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPSHRDVD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpshrdvd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W0 73 /r","V","V","AVX512_VBMI2","bscale4,scale64","rw,r,r,r","","" +"VPSHRDVQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPSHRDVQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpshrdvq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 73 /r","V","V","AVX512_VBMI2+AVX512VL","bscale8,scale16","rw,r,r,r","","" +"VPSHRDVQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPSHRDVQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpshrdvq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 73 /r","V","V","AVX512_VBMI2+AVX512VL","bscale8,scale32","rw,r,r,r","","" +"VPSHRDVQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPSHRDVQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpshrdvq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 73 /r","V","V","AVX512_VBMI2","bscale8,scale64","rw,r,r,r","","" +"VPSHRDVW xmm1, {k}{z}, xmmV, xmm2/m128","VPSHRDVW xmm2/m128, xmmV, {k}{z}, xmm1","vpshrdvw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F38.W1 72 /r","V","V","AVX512_VBMI2+AVX512VL","scale16","rw,r,r,r","","" +"VPSHRDVW ymm1, {k}{z}, ymmV, ymm2/m256","VPSHRDVW ymm2/m256, ymmV, {k}{z}, ymm1","vpshrdvw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F38.W1 72 /r","V","V","AVX512_VBMI2+AVX512VL","scale32","rw,r,r,r","","" +"VPSHRDVW zmm1, {k}{z}, zmmV, zmm2/m512","VPSHRDVW zmm2/m512, zmmV, {k}{z}, zmm1","vpshrdvw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F38.W1 72 /r","V","V","AVX512_VBMI2","scale64","rw,r,r,r","","" +"VPSHRDW xmm1, {k}{z}, xmmV, xmm2/m128, imm8u","VPSHRDW imm8u, xmm2/m128, xmmV, {k}{z}, xmm1","vpshrdw imm8u, xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F3A.W1 72 /r ib","V","V","AVX512_VBMI2+AVX512VL","scale16","w,r,r,r,r","","" +"VPSHRDW ymm1, {k}{z}, ymmV, ymm2/m256, imm8u","VPSHRDW imm8u, ymm2/m256, ymmV, {k}{z}, ymm1","vpshrdw imm8u, ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W1 72 /r ib","V","V","AVX512_VBMI2+AVX512VL","scale32","w,r,r,r,r","","" +"VPSHRDW zmm1, {k}{z}, zmmV, zmm2/m512, imm8u","VPSHRDW imm8u, zmm2/m512, zmmV, {k}{z}, zmm1","vpshrdw imm8u, zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W1 72 /r ib","V","V","AVX512_VBMI2","scale64","w,r,r,r,r","","" +"VPSHUFB xmm1, xmmV, xmm2/m128","VPSHUFB xmm2/m128, xmmV, xmm1","vpshufb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 00 /r","V","V","AVX","","w,r,r","","" +"VPSHUFB xmm1, {k}{z}, xmmV, xmm2/m128","VPSHUFB xmm2/m128, xmmV, {k}{z}, xmm1","vpshufb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.WIG 00 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSHUFB ymm1, ymmV, ymm2/m256","VPSHUFB ymm2/m256, ymmV, ymm1","vpshufb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 00 /r","V","V","AVX2","","w,r,r","","" +"VPSHUFB ymm1, {k}{z}, ymmV, ymm2/m256","VPSHUFB ymm2/m256, ymmV, {k}{z}, ymm1","vpshufb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.WIG 00 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSHUFB zmm1, {k}{z}, zmmV, zmm2/m512","VPSHUFB zmm2/m512, zmmV, {k}{z}, zmm1","vpshufb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.WIG 00 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPSHUFBITQMB k1, {k}, xmmV, xmm2/m128","VPSHUFBITQMB xmm2/m128, xmmV, {k}, k1","vpshufbitqmb xmm2/m128, xmmV, {k}, k1","EVEX.NDS.128.66.0F38.W0 8F /r","V","V","AVX512_BITALG+AVX512VL","scale16","w,r,r,r","","" +"VPSHUFBITQMB k1, {k}, ymmV, ymm2/m256","VPSHUFBITQMB ymm2/m256, ymmV, {k}, k1","vpshufbitqmb ymm2/m256, ymmV, {k}, k1","EVEX.NDS.256.66.0F38.W0 8F /r","V","V","AVX512_BITALG+AVX512VL","scale32","w,r,r,r","","" +"VPSHUFBITQMB k1, {k}, zmmV, zmm2/m512","VPSHUFBITQMB zmm2/m512, zmmV, {k}, k1","vpshufbitqmb zmm2/m512, zmmV, {k}, k1","EVEX.NDS.512.66.0F38.W0 8F /r","V","V","AVX512_BITALG","scale64","w,r,r,r","","" +"VPSHUFD xmm1, xmm2/m128, imm8u","VPSHUFD imm8u, xmm2/m128, xmm1","vpshufd imm8u, xmm2/m128, xmm1","VEX.128.66.0F.WIG 70 /r ib","V","V","AVX","","w,r,r","","" +"VPSHUFD xmm1, {k}{z}, xmm2/m128/m32bcst, imm8u","VPSHUFD imm8u, xmm2/m128/m32bcst, {k}{z}, xmm1","vpshufd imm8u, xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F.W0 70 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPSHUFD ymm1, ymm2/m256, imm8u","VPSHUFD imm8u, ymm2/m256, ymm1","vpshufd imm8u, ymm2/m256, ymm1","VEX.256.66.0F.WIG 70 /r ib","V","V","AVX2","","w,r,r","","" +"VPSHUFD ymm1, {k}{z}, ymm2/m256/m32bcst, imm8u","VPSHUFD imm8u, ymm2/m256/m32bcst, {k}{z}, ymm1","vpshufd imm8u, ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F.W0 70 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPSHUFD zmm1, {k}{z}, zmm2/m512/m32bcst, imm8u","VPSHUFD imm8u, zmm2/m512/m32bcst, {k}{z}, zmm1","vpshufd imm8u, zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F.W0 70 /r ib","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPSHUFHW xmm1, xmm2/m128, imm8u","VPSHUFHW imm8u, xmm2/m128, xmm1","vpshufhw imm8u, xmm2/m128, xmm1","VEX.128.F3.0F.WIG 70 /r ib","V","V","AVX","","w,r,r","","" +"VPSHUFHW xmm1, {k}{z}, xmm2/m128, imm8u","VPSHUFHW imm8u, xmm2/m128, {k}{z}, xmm1","vpshufhw imm8u, xmm2/m128, {k}{z}, xmm1","EVEX.128.F3.0F.WIG 70 /r ib","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSHUFHW ymm1, ymm2/m256, imm8u","VPSHUFHW imm8u, ymm2/m256, ymm1","vpshufhw imm8u, ymm2/m256, ymm1","VEX.256.F3.0F.WIG 70 /r ib","V","V","AVX2","","w,r,r","","" +"VPSHUFHW ymm1, {k}{z}, ymm2/m256, imm8u","VPSHUFHW imm8u, ymm2/m256, {k}{z}, ymm1","vpshufhw imm8u, ymm2/m256, {k}{z}, ymm1","EVEX.256.F3.0F.WIG 70 /r ib","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSHUFHW zmm1, {k}{z}, zmm2/m512, imm8u","VPSHUFHW imm8u, zmm2/m512, {k}{z}, zmm1","vpshufhw imm8u, zmm2/m512, {k}{z}, zmm1","EVEX.512.F3.0F.WIG 70 /r ib","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPSHUFLW xmm1, xmm2/m128, imm8u","VPSHUFLW imm8u, xmm2/m128, xmm1","vpshuflw imm8u, xmm2/m128, xmm1","VEX.128.F2.0F.WIG 70 /r ib","V","V","AVX","","w,r,r","","" +"VPSHUFLW xmm1, {k}{z}, xmm2/m128, imm8u","VPSHUFLW imm8u, xmm2/m128, {k}{z}, xmm1","vpshuflw imm8u, xmm2/m128, {k}{z}, xmm1","EVEX.128.F2.0F.WIG 70 /r ib","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSHUFLW ymm1, ymm2/m256, imm8u","VPSHUFLW imm8u, ymm2/m256, ymm1","vpshuflw imm8u, ymm2/m256, ymm1","VEX.256.F2.0F.WIG 70 /r ib","V","V","AVX2","","w,r,r","","" +"VPSHUFLW ymm1, {k}{z}, ymm2/m256, imm8u","VPSHUFLW imm8u, ymm2/m256, {k}{z}, ymm1","vpshuflw imm8u, ymm2/m256, {k}{z}, ymm1","EVEX.256.F2.0F.WIG 70 /r ib","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSHUFLW zmm1, {k}{z}, zmm2/m512, imm8u","VPSHUFLW imm8u, zmm2/m512, {k}{z}, zmm1","vpshuflw imm8u, zmm2/m512, {k}{z}, zmm1","EVEX.512.F2.0F.WIG 70 /r ib","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPSIGNB xmm1, xmmV, xmm2/m128","VPSIGNB xmm2/m128, xmmV, xmm1","vpsignb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 08 /r","V","V","AVX","","w,r,r","","" +"VPSIGNB ymm1, ymmV, ymm2/m256","VPSIGNB ymm2/m256, ymmV, ymm1","vpsignb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 08 /r","V","V","AVX2","","w,r,r","","" +"VPSIGND xmm1, xmmV, xmm2/m128","VPSIGND xmm2/m128, xmmV, xmm1","vpsignd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 0A /r","V","V","AVX","","w,r,r","","" +"VPSIGND ymm1, ymmV, ymm2/m256","VPSIGND ymm2/m256, ymmV, ymm1","vpsignd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 0A /r","V","V","AVX2","","w,r,r","","" +"VPSIGNW xmm1, xmmV, xmm2/m128","VPSIGNW xmm2/m128, xmmV, xmm1","vpsignw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.WIG 09 /r","V","V","AVX","","w,r,r","","" +"VPSIGNW ymm1, ymmV, ymm2/m256","VPSIGNW ymm2/m256, ymmV, ymm1","vpsignw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.WIG 09 /r","V","V","AVX2","","w,r,r","","" +"VPSLLD xmmV, xmm2, imm8u","VPSLLD imm8u, xmm2, xmmV","vpslld imm8u, xmm2, xmmV","VEX.NDD.128.66.0F.WIG 72 /6 ib","V","V","AVX","modrm_regonly","w,r,r","","" +"VPSLLD xmmV, {k}{z}, xmm2/m128/m32bcst, imm8u","VPSLLD imm8u, xmm2/m128/m32bcst, {k}{z}, xmmV","vpslld imm8u, xmm2/m128/m32bcst, {k}{z}, xmmV","EVEX.NDD.128.66.0F.W0 72 /6 ib","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPSLLD ymmV, ymm2, imm8u","VPSLLD imm8u, ymm2, ymmV","vpslld imm8u, ymm2, ymmV","VEX.NDD.256.66.0F.WIG 72 /6 ib","V","V","AVX2","modrm_regonly","w,r,r","","" +"VPSLLD ymmV, {k}{z}, ymm2/m256/m32bcst, imm8u","VPSLLD imm8u, ymm2/m256/m32bcst, {k}{z}, ymmV","vpslld imm8u, ymm2/m256/m32bcst, {k}{z}, ymmV","EVEX.NDD.256.66.0F.W0 72 /6 ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPSLLD zmmV, {k}{z}, zmm2/m512/m32bcst, imm8u","VPSLLD imm8u, zmm2/m512/m32bcst, {k}{z}, zmmV","vpslld imm8u, zmm2/m512/m32bcst, {k}{z}, zmmV","EVEX.NDD.512.66.0F.W0 72 /6 ib","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPSLLD xmm1, xmmV, xmm2/m128","VPSLLD xmm2/m128, xmmV, xmm1","vpslld xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG F2 /r","V","V","AVX","","w,r,r","","" +"VPSLLD xmm1, {k}{z}, xmmV, xmm2/m128","VPSLLD xmm2/m128, xmmV, {k}{z}, xmm1","vpslld xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W0 F2 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VPSLLD ymm1, ymmV, xmm2/m128","VPSLLD xmm2/m128, ymmV, ymm1","vpslld xmm2/m128, ymmV, ymm1","VEX.NDS.256.66.0F.WIG F2 /r","V","V","AVX2","","w,r,r","","" +"VPSLLD ymm1, {k}{z}, ymmV, xmm2/m128","VPSLLD xmm2/m128, ymmV, {k}{z}, ymm1","vpslld xmm2/m128, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W0 F2 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VPSLLD zmm1, {k}{z}, zmmV, xmm2/m128","VPSLLD xmm2/m128, zmmV, {k}{z}, zmm1","vpslld xmm2/m128, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W0 F2 /r","V","V","AVX512F","scale16","w,r,r,r","","" +"VPSLLDQ xmmV, xmm2, imm8u","VPSLLDQ imm8u, xmm2, xmmV","vpslldq imm8u, xmm2, xmmV","VEX.NDD.128.66.0F.WIG 73 /7 ib","V","V","AVX","modrm_regonly","w,r,r","","" +"VPSLLDQ xmmV, xmm2/m128, imm8u","VPSLLDQ imm8u, xmm2/m128, xmmV","vpslldq imm8u, xmm2/m128, xmmV","EVEX.NDD.128.66.0F.WIG 73 /7 ib","V","V","AVX512BW+AVX512VL","scale16","w,r,r","","" +"VPSLLDQ ymmV, ymm2, imm8u","VPSLLDQ imm8u, ymm2, ymmV","vpslldq imm8u, ymm2, ymmV","VEX.NDD.256.66.0F.WIG 73 /7 ib","V","V","AVX2","modrm_regonly","w,r,r","","" +"VPSLLDQ ymmV, ymm2/m256, imm8u","VPSLLDQ imm8u, ymm2/m256, ymmV","vpslldq imm8u, ymm2/m256, ymmV","EVEX.NDD.256.66.0F.WIG 73 /7 ib","V","V","AVX512BW+AVX512VL","scale32","w,r,r","","" +"VPSLLDQ zmmV, zmm2/m512, imm8u","VPSLLDQ imm8u, zmm2/m512, zmmV","vpslldq imm8u, zmm2/m512, zmmV","EVEX.NDD.512.66.0F.WIG 73 /7 ib","V","V","AVX512BW","scale64","w,r,r","","" +"VPSLLQ xmmV, xmm2, imm8u","VPSLLQ imm8u, xmm2, xmmV","vpsllq imm8u, xmm2, xmmV","VEX.NDD.128.66.0F.WIG 73 /6 ib","V","V","AVX","modrm_regonly","w,r,r","","" +"VPSLLQ xmmV, {k}{z}, xmm2/m128/m64bcst, imm8u","VPSLLQ imm8u, xmm2/m128/m64bcst, {k}{z}, xmmV","vpsllq imm8u, xmm2/m128/m64bcst, {k}{z}, xmmV","EVEX.NDD.128.66.0F.W1 73 /6 ib","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPSLLQ ymmV, ymm2, imm8u","VPSLLQ imm8u, ymm2, ymmV","vpsllq imm8u, ymm2, ymmV","VEX.NDD.256.66.0F.WIG 73 /6 ib","V","V","AVX2","modrm_regonly","w,r,r","","" +"VPSLLQ ymmV, {k}{z}, ymm2/m256/m64bcst, imm8u","VPSLLQ imm8u, ymm2/m256/m64bcst, {k}{z}, ymmV","vpsllq imm8u, ymm2/m256/m64bcst, {k}{z}, ymmV","EVEX.NDD.256.66.0F.W1 73 /6 ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPSLLQ zmmV, {k}{z}, zmm2/m512/m64bcst, imm8u","VPSLLQ imm8u, zmm2/m512/m64bcst, {k}{z}, zmmV","vpsllq imm8u, zmm2/m512/m64bcst, {k}{z}, zmmV","EVEX.NDD.512.66.0F.W1 73 /6 ib","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPSLLQ xmm1, xmmV, xmm2/m128","VPSLLQ xmm2/m128, xmmV, xmm1","vpsllq xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG F3 /r","V","V","AVX","","w,r,r","","" +"VPSLLQ xmm1, {k}{z}, xmmV, xmm2/m128","VPSLLQ xmm2/m128, xmmV, {k}{z}, xmm1","vpsllq xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 F3 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VPSLLQ ymm1, ymmV, xmm2/m128","VPSLLQ xmm2/m128, ymmV, ymm1","vpsllq xmm2/m128, ymmV, ymm1","VEX.NDS.256.66.0F.WIG F3 /r","V","V","AVX2","","w,r,r","","" +"VPSLLQ ymm1, {k}{z}, ymmV, xmm2/m128","VPSLLQ xmm2/m128, ymmV, {k}{z}, ymm1","vpsllq xmm2/m128, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 F3 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VPSLLQ zmm1, {k}{z}, zmmV, xmm2/m128","VPSLLQ xmm2/m128, zmmV, {k}{z}, zmm1","vpsllq xmm2/m128, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 F3 /r","V","V","AVX512F","scale16","w,r,r,r","","" +"VPSLLVD xmm1, xmmV, xmm2/m128","VPSLLVD xmm2/m128, xmmV, xmm1","vpsllvd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.W0 47 /r","V","V","AVX2","","w,r,r","","" +"VPSLLVD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPSLLVD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpsllvd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 47 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPSLLVD ymm1, ymmV, ymm2/m256","VPSLLVD ymm2/m256, ymmV, ymm1","vpsllvd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.W0 47 /r","V","V","AVX2","","w,r,r","","" +"VPSLLVD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPSLLVD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpsllvd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 47 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPSLLVD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPSLLVD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpsllvd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 47 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPSLLVQ xmm1, xmmV, xmm2/m128","VPSLLVQ xmm2/m128, xmmV, xmm1","vpsllvq xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.W1 47 /r","V","V","AVX2","","w,r,r","","" +"VPSLLVQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPSLLVQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpsllvq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 47 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPSLLVQ ymm1, ymmV, ymm2/m256","VPSLLVQ ymm2/m256, ymmV, ymm1","vpsllvq ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.W1 47 /r","V","V","AVX2","","w,r,r","","" +"VPSLLVQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPSLLVQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpsllvq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 47 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPSLLVQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPSLLVQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpsllvq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 47 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPSLLVW xmm1, {k}{z}, xmmV, xmm2/m128","VPSLLVW xmm2/m128, xmmV, {k}{z}, xmm1","vpsllvw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 12 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSLLVW ymm1, {k}{z}, ymmV, ymm2/m256","VPSLLVW ymm2/m256, ymmV, {k}{z}, ymm1","vpsllvw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 12 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSLLVW zmm1, {k}{z}, zmmV, zmm2/m512","VPSLLVW zmm2/m512, zmmV, {k}{z}, zmm1","vpsllvw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 12 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPSLLW xmmV, xmm2, imm8u","VPSLLW imm8u, xmm2, xmmV","vpsllw imm8u, xmm2, xmmV","VEX.NDD.128.66.0F.WIG 71 /6 ib","V","V","AVX","modrm_regonly","w,r,r","","" +"VPSLLW xmmV, {k}{z}, xmm2/m128, imm8u","VPSLLW imm8u, xmm2/m128, {k}{z}, xmmV","vpsllw imm8u, xmm2/m128, {k}{z}, xmmV","EVEX.NDD.128.66.0F.WIG 71 /6 ib","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSLLW ymmV, ymm2, imm8u","VPSLLW imm8u, ymm2, ymmV","vpsllw imm8u, ymm2, ymmV","VEX.NDD.256.66.0F.WIG 71 /6 ib","V","V","AVX2","modrm_regonly","w,r,r","","" +"VPSLLW ymmV, {k}{z}, ymm2/m256, imm8u","VPSLLW imm8u, ymm2/m256, {k}{z}, ymmV","vpsllw imm8u, ymm2/m256, {k}{z}, ymmV","EVEX.NDD.256.66.0F.WIG 71 /6 ib","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSLLW zmmV, {k}{z}, zmm2/m512, imm8u","VPSLLW imm8u, zmm2/m512, {k}{z}, zmmV","vpsllw imm8u, zmm2/m512, {k}{z}, zmmV","EVEX.NDD.512.66.0F.WIG 71 /6 ib","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPSLLW xmm1, xmmV, xmm2/m128","VPSLLW xmm2/m128, xmmV, xmm1","vpsllw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG F1 /r","V","V","AVX","","w,r,r","","" +"VPSLLW xmm1, {k}{z}, xmmV, xmm2/m128","VPSLLW xmm2/m128, xmmV, {k}{z}, xmm1","vpsllw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG F1 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSLLW ymm1, ymmV, xmm2/m128","VPSLLW xmm2/m128, ymmV, ymm1","vpsllw xmm2/m128, ymmV, ymm1","VEX.NDS.256.66.0F.WIG F1 /r","V","V","AVX2","","w,r,r","","" +"VPSLLW ymm1, {k}{z}, ymmV, xmm2/m128","VPSLLW xmm2/m128, ymmV, {k}{z}, ymm1","vpsllw xmm2/m128, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG F1 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSLLW zmm1, {k}{z}, zmmV, xmm2/m128","VPSLLW xmm2/m128, zmmV, {k}{z}, zmm1","vpsllw xmm2/m128, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG F1 /r","V","V","AVX512BW","scale16","w,r,r,r","","" +"VPSRAD xmmV, xmm2, imm8u","VPSRAD imm8u, xmm2, xmmV","vpsrad imm8u, xmm2, xmmV","VEX.NDD.128.66.0F.WIG 72 /4 ib","V","V","AVX","modrm_regonly","w,r,r","","" +"VPSRAD xmmV, {k}{z}, xmm2/m128/m32bcst, imm8u","VPSRAD imm8u, xmm2/m128/m32bcst, {k}{z}, xmmV","vpsrad imm8u, xmm2/m128/m32bcst, {k}{z}, xmmV","EVEX.NDD.128.66.0F.W0 72 /4 ib","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPSRAD ymmV, ymm2, imm8u","VPSRAD imm8u, ymm2, ymmV","vpsrad imm8u, ymm2, ymmV","VEX.NDD.256.66.0F.WIG 72 /4 ib","V","V","AVX2","modrm_regonly","w,r,r","","" +"VPSRAD ymmV, {k}{z}, ymm2/m256/m32bcst, imm8u","VPSRAD imm8u, ymm2/m256/m32bcst, {k}{z}, ymmV","vpsrad imm8u, ymm2/m256/m32bcst, {k}{z}, ymmV","EVEX.NDD.256.66.0F.W0 72 /4 ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPSRAD zmmV, {k}{z}, zmm2/m512/m32bcst, imm8u","VPSRAD imm8u, zmm2/m512/m32bcst, {k}{z}, zmmV","vpsrad imm8u, zmm2/m512/m32bcst, {k}{z}, zmmV","EVEX.NDD.512.66.0F.W0 72 /4 ib","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPSRAD xmm1, xmmV, xmm2/m128","VPSRAD xmm2/m128, xmmV, xmm1","vpsrad xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG E2 /r","V","V","AVX","","w,r,r","","" +"VPSRAD xmm1, {k}{z}, xmmV, xmm2/m128","VPSRAD xmm2/m128, xmmV, {k}{z}, xmm1","vpsrad xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W0 E2 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VPSRAD ymm1, ymmV, xmm2/m128","VPSRAD xmm2/m128, ymmV, ymm1","vpsrad xmm2/m128, ymmV, ymm1","VEX.NDS.256.66.0F.WIG E2 /r","V","V","AVX2","","w,r,r","","" +"VPSRAD ymm1, {k}{z}, ymmV, xmm2/m128","VPSRAD xmm2/m128, ymmV, {k}{z}, ymm1","vpsrad xmm2/m128, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W0 E2 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VPSRAD zmm1, {k}{z}, zmmV, xmm2/m128","VPSRAD xmm2/m128, zmmV, {k}{z}, zmm1","vpsrad xmm2/m128, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W0 E2 /r","V","V","AVX512F","scale16","w,r,r,r","","" +"VPSRAQ xmmV, {k}{z}, xmm2/m128/m64bcst, imm8u","VPSRAQ imm8u, xmm2/m128/m64bcst, {k}{z}, xmmV","vpsraq imm8u, xmm2/m128/m64bcst, {k}{z}, xmmV","EVEX.NDD.128.66.0F.W1 72 /4 ib","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPSRAQ ymmV, {k}{z}, ymm2/m256/m64bcst, imm8u","VPSRAQ imm8u, ymm2/m256/m64bcst, {k}{z}, ymmV","vpsraq imm8u, ymm2/m256/m64bcst, {k}{z}, ymmV","EVEX.NDD.256.66.0F.W1 72 /4 ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPSRAQ zmmV, {k}{z}, zmm2/m512/m64bcst, imm8u","VPSRAQ imm8u, zmm2/m512/m64bcst, {k}{z}, zmmV","vpsraq imm8u, zmm2/m512/m64bcst, {k}{z}, zmmV","EVEX.NDD.512.66.0F.W1 72 /4 ib","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPSRAQ xmm1, {k}{z}, xmmV, xmm2/m128","VPSRAQ xmm2/m128, xmmV, {k}{z}, xmm1","vpsraq xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 E2 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VPSRAQ ymm1, {k}{z}, ymmV, xmm2/m128","VPSRAQ xmm2/m128, ymmV, {k}{z}, ymm1","vpsraq xmm2/m128, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 E2 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VPSRAQ zmm1, {k}{z}, zmmV, xmm2/m128","VPSRAQ xmm2/m128, zmmV, {k}{z}, zmm1","vpsraq xmm2/m128, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 E2 /r","V","V","AVX512F","scale16","w,r,r,r","","" +"VPSRAVD xmm1, xmmV, xmm2/m128","VPSRAVD xmm2/m128, xmmV, xmm1","vpsravd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.W0 46 /r","V","V","AVX2","","w,r,r","","" +"VPSRAVD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPSRAVD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpsravd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 46 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPSRAVD ymm1, ymmV, ymm2/m256","VPSRAVD ymm2/m256, ymmV, ymm1","vpsravd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.W0 46 /r","V","V","AVX2","","w,r,r","","" +"VPSRAVD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPSRAVD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpsravd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 46 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPSRAVD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPSRAVD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpsravd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 46 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPSRAVQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPSRAVQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpsravq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 46 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPSRAVQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPSRAVQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpsravq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 46 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPSRAVQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPSRAVQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpsravq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 46 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPSRAVW xmm1, {k}{z}, xmmV, xmm2/m128","VPSRAVW xmm2/m128, xmmV, {k}{z}, xmm1","vpsravw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 11 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSRAVW ymm1, {k}{z}, ymmV, ymm2/m256","VPSRAVW ymm2/m256, ymmV, {k}{z}, ymm1","vpsravw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 11 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSRAVW zmm1, {k}{z}, zmmV, zmm2/m512","VPSRAVW zmm2/m512, zmmV, {k}{z}, zmm1","vpsravw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 11 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPSRAW xmmV, xmm2, imm8u","VPSRAW imm8u, xmm2, xmmV","vpsraw imm8u, xmm2, xmmV","VEX.NDD.128.66.0F.WIG 71 /4 ib","V","V","AVX","modrm_regonly","w,r,r","","" +"VPSRAW xmmV, {k}{z}, xmm2/m128, imm8u","VPSRAW imm8u, xmm2/m128, {k}{z}, xmmV","vpsraw imm8u, xmm2/m128, {k}{z}, xmmV","EVEX.NDD.128.66.0F.WIG 71 /4 ib","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSRAW ymmV, ymm2, imm8u","VPSRAW imm8u, ymm2, ymmV","vpsraw imm8u, ymm2, ymmV","VEX.NDD.256.66.0F.WIG 71 /4 ib","V","V","AVX2","modrm_regonly","w,r,r","","" +"VPSRAW ymmV, {k}{z}, ymm2/m256, imm8u","VPSRAW imm8u, ymm2/m256, {k}{z}, ymmV","vpsraw imm8u, ymm2/m256, {k}{z}, ymmV","EVEX.NDD.256.66.0F.WIG 71 /4 ib","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSRAW zmmV, {k}{z}, zmm2/m512, imm8u","VPSRAW imm8u, zmm2/m512, {k}{z}, zmmV","vpsraw imm8u, zmm2/m512, {k}{z}, zmmV","EVEX.NDD.512.66.0F.WIG 71 /4 ib","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPSRAW xmm1, xmmV, xmm2/m128","VPSRAW xmm2/m128, xmmV, xmm1","vpsraw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG E1 /r","V","V","AVX","","w,r,r","","" +"VPSRAW xmm1, {k}{z}, xmmV, xmm2/m128","VPSRAW xmm2/m128, xmmV, {k}{z}, xmm1","vpsraw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG E1 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSRAW ymm1, ymmV, xmm2/m128","VPSRAW xmm2/m128, ymmV, ymm1","vpsraw xmm2/m128, ymmV, ymm1","VEX.NDS.256.66.0F.WIG E1 /r","V","V","AVX2","","w,r,r","","" +"VPSRAW ymm1, {k}{z}, ymmV, xmm2/m128","VPSRAW xmm2/m128, ymmV, {k}{z}, ymm1","vpsraw xmm2/m128, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG E1 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSRAW zmm1, {k}{z}, zmmV, xmm2/m128","VPSRAW xmm2/m128, zmmV, {k}{z}, zmm1","vpsraw xmm2/m128, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG E1 /r","V","V","AVX512BW","scale16","w,r,r,r","","" +"VPSRLD xmmV, xmm2, imm8u","VPSRLD imm8u, xmm2, xmmV","vpsrld imm8u, xmm2, xmmV","VEX.NDD.128.66.0F.WIG 72 /2 ib","V","V","AVX","modrm_regonly","w,r,r","","" +"VPSRLD xmmV, {k}{z}, xmm2/m128/m32bcst, imm8u","VPSRLD imm8u, xmm2/m128/m32bcst, {k}{z}, xmmV","vpsrld imm8u, xmm2/m128/m32bcst, {k}{z}, xmmV","EVEX.NDD.128.66.0F.W0 72 /2 ib","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPSRLD ymmV, ymm2, imm8u","VPSRLD imm8u, ymm2, ymmV","vpsrld imm8u, ymm2, ymmV","VEX.NDD.256.66.0F.WIG 72 /2 ib","V","V","AVX2","modrm_regonly","w,r,r","","" +"VPSRLD ymmV, {k}{z}, ymm2/m256/m32bcst, imm8u","VPSRLD imm8u, ymm2/m256/m32bcst, {k}{z}, ymmV","vpsrld imm8u, ymm2/m256/m32bcst, {k}{z}, ymmV","EVEX.NDD.256.66.0F.W0 72 /2 ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPSRLD zmmV, {k}{z}, zmm2/m512/m32bcst, imm8u","VPSRLD imm8u, zmm2/m512/m32bcst, {k}{z}, zmmV","vpsrld imm8u, zmm2/m512/m32bcst, {k}{z}, zmmV","EVEX.NDD.512.66.0F.W0 72 /2 ib","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPSRLD xmm1, xmmV, xmm2/m128","VPSRLD xmm2/m128, xmmV, xmm1","vpsrld xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG D2 /r","V","V","AVX","","w,r,r","","" +"VPSRLD xmm1, {k}{z}, xmmV, xmm2/m128","VPSRLD xmm2/m128, xmmV, {k}{z}, xmm1","vpsrld xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W0 D2 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VPSRLD ymm1, ymmV, xmm2/m128","VPSRLD xmm2/m128, ymmV, ymm1","vpsrld xmm2/m128, ymmV, ymm1","VEX.NDS.256.66.0F.WIG D2 /r","V","V","AVX2","","w,r,r","","" +"VPSRLD ymm1, {k}{z}, ymmV, xmm2/m128","VPSRLD xmm2/m128, ymmV, {k}{z}, ymm1","vpsrld xmm2/m128, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W0 D2 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VPSRLD zmm1, {k}{z}, zmmV, xmm2/m128","VPSRLD xmm2/m128, zmmV, {k}{z}, zmm1","vpsrld xmm2/m128, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W0 D2 /r","V","V","AVX512F","scale16","w,r,r,r","","" +"VPSRLDQ xmmV, xmm2, imm8u","VPSRLDQ imm8u, xmm2, xmmV","vpsrldq imm8u, xmm2, xmmV","VEX.NDD.128.66.0F.WIG 73 /3 ib","V","V","AVX","modrm_regonly","w,r,r","","" +"VPSRLDQ xmmV, xmm2/m128, imm8u","VPSRLDQ imm8u, xmm2/m128, xmmV","vpsrldq imm8u, xmm2/m128, xmmV","EVEX.NDD.128.66.0F.WIG 73 /3 ib","V","V","AVX512BW+AVX512VL","scale16","w,r,r","","" +"VPSRLDQ ymmV, ymm2, imm8u","VPSRLDQ imm8u, ymm2, ymmV","vpsrldq imm8u, ymm2, ymmV","VEX.NDD.256.66.0F.WIG 73 /3 ib","V","V","AVX2","modrm_regonly","w,r,r","","" +"VPSRLDQ ymmV, ymm2/m256, imm8u","VPSRLDQ imm8u, ymm2/m256, ymmV","vpsrldq imm8u, ymm2/m256, ymmV","EVEX.NDD.256.66.0F.WIG 73 /3 ib","V","V","AVX512BW+AVX512VL","scale32","w,r,r","","" +"VPSRLDQ zmmV, zmm2/m512, imm8u","VPSRLDQ imm8u, zmm2/m512, zmmV","vpsrldq imm8u, zmm2/m512, zmmV","EVEX.NDD.512.66.0F.WIG 73 /3 ib","V","V","AVX512BW","scale64","w,r,r","","" +"VPSRLQ xmmV, xmm2, imm8u","VPSRLQ imm8u, xmm2, xmmV","vpsrlq imm8u, xmm2, xmmV","VEX.NDD.128.66.0F.WIG 73 /2 ib","V","V","AVX","modrm_regonly","w,r,r","","" +"VPSRLQ xmmV, {k}{z}, xmm2/m128/m64bcst, imm8u","VPSRLQ imm8u, xmm2/m128/m64bcst, {k}{z}, xmmV","vpsrlq imm8u, xmm2/m128/m64bcst, {k}{z}, xmmV","EVEX.NDD.128.66.0F.W1 73 /2 ib","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPSRLQ ymmV, ymm2, imm8u","VPSRLQ imm8u, ymm2, ymmV","vpsrlq imm8u, ymm2, ymmV","VEX.NDD.256.66.0F.WIG 73 /2 ib","V","V","AVX2","modrm_regonly","w,r,r","","" +"VPSRLQ ymmV, {k}{z}, ymm2/m256/m64bcst, imm8u","VPSRLQ imm8u, ymm2/m256/m64bcst, {k}{z}, ymmV","vpsrlq imm8u, ymm2/m256/m64bcst, {k}{z}, ymmV","EVEX.NDD.256.66.0F.W1 73 /2 ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPSRLQ zmmV, {k}{z}, zmm2/m512/m64bcst, imm8u","VPSRLQ imm8u, zmm2/m512/m64bcst, {k}{z}, zmmV","vpsrlq imm8u, zmm2/m512/m64bcst, {k}{z}, zmmV","EVEX.NDD.512.66.0F.W1 73 /2 ib","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPSRLQ xmm1, xmmV, xmm2/m128","VPSRLQ xmm2/m128, xmmV, xmm1","vpsrlq xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG D3 /r","V","V","AVX","","w,r,r","","" +"VPSRLQ xmm1, {k}{z}, xmmV, xmm2/m128","VPSRLQ xmm2/m128, xmmV, {k}{z}, xmm1","vpsrlq xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 D3 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VPSRLQ ymm1, ymmV, xmm2/m128","VPSRLQ xmm2/m128, ymmV, ymm1","vpsrlq xmm2/m128, ymmV, ymm1","VEX.NDS.256.66.0F.WIG D3 /r","V","V","AVX2","","w,r,r","","" +"VPSRLQ ymm1, {k}{z}, ymmV, xmm2/m128","VPSRLQ xmm2/m128, ymmV, {k}{z}, ymm1","vpsrlq xmm2/m128, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 D3 /r","V","V","AVX512F+AVX512VL","scale16","w,r,r,r","","" +"VPSRLQ zmm1, {k}{z}, zmmV, xmm2/m128","VPSRLQ xmm2/m128, zmmV, {k}{z}, zmm1","vpsrlq xmm2/m128, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 D3 /r","V","V","AVX512F","scale16","w,r,r,r","","" +"VPSRLVD xmm1, xmmV, xmm2/m128","VPSRLVD xmm2/m128, xmmV, xmm1","vpsrlvd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.W0 45 /r","V","V","AVX2","","w,r,r","","" +"VPSRLVD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPSRLVD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpsrlvd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 45 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPSRLVD ymm1, ymmV, ymm2/m256","VPSRLVD ymm2/m256, ymmV, ymm1","vpsrlvd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.W0 45 /r","V","V","AVX2","","w,r,r","","" +"VPSRLVD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPSRLVD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpsrlvd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 45 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPSRLVD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPSRLVD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpsrlvd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 45 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPSRLVQ xmm1, xmmV, xmm2/m128","VPSRLVQ xmm2/m128, xmmV, xmm1","vpsrlvq xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F38.W1 45 /r","V","V","AVX2","","w,r,r","","" +"VPSRLVQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPSRLVQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpsrlvq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 45 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPSRLVQ ymm1, ymmV, ymm2/m256","VPSRLVQ ymm2/m256, ymmV, ymm1","vpsrlvq ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F38.W1 45 /r","V","V","AVX2","","w,r,r","","" +"VPSRLVQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPSRLVQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpsrlvq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 45 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPSRLVQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPSRLVQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpsrlvq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 45 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPSRLVW xmm1, {k}{z}, xmmV, xmm2/m128","VPSRLVW xmm2/m128, xmmV, {k}{z}, xmm1","vpsrlvw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 10 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSRLVW ymm1, {k}{z}, ymmV, ymm2/m256","VPSRLVW ymm2/m256, ymmV, {k}{z}, ymm1","vpsrlvw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 10 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSRLVW zmm1, {k}{z}, zmmV, zmm2/m512","VPSRLVW zmm2/m512, zmmV, {k}{z}, zmm1","vpsrlvw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 10 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPSRLW xmmV, xmm2, imm8u","VPSRLW imm8u, xmm2, xmmV","vpsrlw imm8u, xmm2, xmmV","VEX.NDD.128.66.0F.WIG 71 /2 ib","V","V","AVX","modrm_regonly","w,r,r","","" +"VPSRLW xmmV, {k}{z}, xmm2/m128, imm8u","VPSRLW imm8u, xmm2/m128, {k}{z}, xmmV","vpsrlw imm8u, xmm2/m128, {k}{z}, xmmV","EVEX.NDD.128.66.0F.WIG 71 /2 ib","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSRLW ymmV, ymm2, imm8u","VPSRLW imm8u, ymm2, ymmV","vpsrlw imm8u, ymm2, ymmV","VEX.NDD.256.66.0F.WIG 71 /2 ib","V","V","AVX2","modrm_regonly","w,r,r","","" +"VPSRLW ymmV, {k}{z}, ymm2/m256, imm8u","VPSRLW imm8u, ymm2/m256, {k}{z}, ymmV","vpsrlw imm8u, ymm2/m256, {k}{z}, ymmV","EVEX.NDD.256.66.0F.WIG 71 /2 ib","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSRLW zmmV, {k}{z}, zmm2/m512, imm8u","VPSRLW imm8u, zmm2/m512, {k}{z}, zmmV","vpsrlw imm8u, zmm2/m512, {k}{z}, zmmV","EVEX.NDD.512.66.0F.WIG 71 /2 ib","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPSRLW xmm1, xmmV, xmm2/m128","VPSRLW xmm2/m128, xmmV, xmm1","vpsrlw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG D1 /r","V","V","AVX","","w,r,r","","" +"VPSRLW xmm1, {k}{z}, xmmV, xmm2/m128","VPSRLW xmm2/m128, xmmV, {k}{z}, xmm1","vpsrlw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG D1 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSRLW ymm1, ymmV, xmm2/m128","VPSRLW xmm2/m128, ymmV, ymm1","vpsrlw xmm2/m128, ymmV, ymm1","VEX.NDS.256.66.0F.WIG D1 /r","V","V","AVX2","","w,r,r","","" +"VPSRLW ymm1, {k}{z}, ymmV, xmm2/m128","VPSRLW xmm2/m128, ymmV, {k}{z}, ymm1","vpsrlw xmm2/m128, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG D1 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSRLW zmm1, {k}{z}, zmmV, xmm2/m128","VPSRLW xmm2/m128, zmmV, {k}{z}, zmm1","vpsrlw xmm2/m128, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG D1 /r","V","V","AVX512BW","scale16","w,r,r,r","","" +"VPSUBB xmm1, xmmV, xmm2/m128","VPSUBB xmm2/m128, xmmV, xmm1","vpsubb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG F8 /r","V","V","AVX","","w,r,r","","" +"VPSUBB xmm1, {k}{z}, xmmV, xmm2/m128","VPSUBB xmm2/m128, xmmV, {k}{z}, xmm1","vpsubb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG F8 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSUBB ymm1, ymmV, ymm2/m256","VPSUBB ymm2/m256, ymmV, ymm1","vpsubb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG F8 /r","V","V","AVX2","","w,r,r","","" +"VPSUBB ymm1, {k}{z}, ymmV, ymm2/m256","VPSUBB ymm2/m256, ymmV, {k}{z}, ymm1","vpsubb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG F8 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSUBB zmm1, {k}{z}, zmmV, zmm2/m512","VPSUBB zmm2/m512, zmmV, {k}{z}, zmm1","vpsubb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG F8 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPSUBD xmm1, xmmV, xmm2/m128","VPSUBD xmm2/m128, xmmV, xmm1","vpsubd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG FA /r","V","V","AVX","","w,r,r","","" +"VPSUBD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPSUBD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpsubd xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W0 FA /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPSUBD ymm1, ymmV, ymm2/m256","VPSUBD ymm2/m256, ymmV, ymm1","vpsubd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG FA /r","V","V","AVX2","","w,r,r","","" +"VPSUBD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPSUBD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpsubd ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W0 FA /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPSUBD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPSUBD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpsubd zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W0 FA /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPSUBQ xmm1, xmmV, xmm2/m128","VPSUBQ xmm2/m128, xmmV, xmm1","vpsubq xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG FB /r","V","V","AVX","","w,r,r","","" +"VPSUBQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPSUBQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpsubq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 FB /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPSUBQ ymm1, ymmV, ymm2/m256","VPSUBQ ymm2/m256, ymmV, ymm1","vpsubq ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG FB /r","V","V","AVX2","","w,r,r","","" +"VPSUBQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPSUBQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpsubq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 FB /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPSUBQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPSUBQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpsubq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 FB /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPSUBSB xmm1, xmmV, xmm2/m128","VPSUBSB xmm2/m128, xmmV, xmm1","vpsubsb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG E8 /r","V","V","AVX","","w,r,r","","" +"VPSUBSB xmm1, {k}{z}, xmmV, xmm2/m128","VPSUBSB xmm2/m128, xmmV, {k}{z}, xmm1","vpsubsb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG E8 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSUBSB ymm1, ymmV, ymm2/m256","VPSUBSB ymm2/m256, ymmV, ymm1","vpsubsb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG E8 /r","V","V","AVX2","","w,r,r","","" +"VPSUBSB ymm1, {k}{z}, ymmV, ymm2/m256","VPSUBSB ymm2/m256, ymmV, {k}{z}, ymm1","vpsubsb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG E8 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSUBSB zmm1, {k}{z}, zmmV, zmm2/m512","VPSUBSB zmm2/m512, zmmV, {k}{z}, zmm1","vpsubsb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG E8 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPSUBSW xmm1, xmmV, xmm2/m128","VPSUBSW xmm2/m128, xmmV, xmm1","vpsubsw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG E9 /r","V","V","AVX","","w,r,r","","" +"VPSUBSW xmm1, {k}{z}, xmmV, xmm2/m128","VPSUBSW xmm2/m128, xmmV, {k}{z}, xmm1","vpsubsw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG E9 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSUBSW ymm1, ymmV, ymm2/m256","VPSUBSW ymm2/m256, ymmV, ymm1","vpsubsw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG E9 /r","V","V","AVX2","","w,r,r","","" +"VPSUBSW ymm1, {k}{z}, ymmV, ymm2/m256","VPSUBSW ymm2/m256, ymmV, {k}{z}, ymm1","vpsubsw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG E9 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSUBSW zmm1, {k}{z}, zmmV, zmm2/m512","VPSUBSW zmm2/m512, zmmV, {k}{z}, zmm1","vpsubsw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG E9 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPSUBUSB xmm1, xmmV, xmm2/m128","VPSUBUSB xmm2/m128, xmmV, xmm1","vpsubusb xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG D8 /r","V","V","AVX","","w,r,r","","" +"VPSUBUSB xmm1, {k}{z}, xmmV, xmm2/m128","VPSUBUSB xmm2/m128, xmmV, {k}{z}, xmm1","vpsubusb xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG D8 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSUBUSB ymm1, ymmV, ymm2/m256","VPSUBUSB ymm2/m256, ymmV, ymm1","vpsubusb ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG D8 /r","V","V","AVX2","","w,r,r","","" +"VPSUBUSB ymm1, {k}{z}, ymmV, ymm2/m256","VPSUBUSB ymm2/m256, ymmV, {k}{z}, ymm1","vpsubusb ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG D8 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSUBUSB zmm1, {k}{z}, zmmV, zmm2/m512","VPSUBUSB zmm2/m512, zmmV, {k}{z}, zmm1","vpsubusb zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG D8 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPSUBUSW xmm1, xmmV, xmm2/m128","VPSUBUSW xmm2/m128, xmmV, xmm1","vpsubusw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG D9 /r","V","V","AVX","","w,r,r","","" +"VPSUBUSW xmm1, {k}{z}, xmmV, xmm2/m128","VPSUBUSW xmm2/m128, xmmV, {k}{z}, xmm1","vpsubusw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG D9 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSUBUSW ymm1, ymmV, ymm2/m256","VPSUBUSW ymm2/m256, ymmV, ymm1","vpsubusw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG D9 /r","V","V","AVX2","","w,r,r","","" +"VPSUBUSW ymm1, {k}{z}, ymmV, ymm2/m256","VPSUBUSW ymm2/m256, ymmV, {k}{z}, ymm1","vpsubusw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG D9 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSUBUSW zmm1, {k}{z}, zmmV, zmm2/m512","VPSUBUSW zmm2/m512, zmmV, {k}{z}, zmm1","vpsubusw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG D9 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPSUBW xmm1, xmmV, xmm2/m128","VPSUBW xmm2/m128, xmmV, xmm1","vpsubw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG F9 /r","V","V","AVX","","w,r,r","","" +"VPSUBW xmm1, {k}{z}, xmmV, xmm2/m128","VPSUBW xmm2/m128, xmmV, {k}{z}, xmm1","vpsubw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG F9 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPSUBW ymm1, ymmV, ymm2/m256","VPSUBW ymm2/m256, ymmV, ymm1","vpsubw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG F9 /r","V","V","AVX2","","w,r,r","","" +"VPSUBW ymm1, {k}{z}, ymmV, ymm2/m256","VPSUBW ymm2/m256, ymmV, {k}{z}, ymm1","vpsubw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG F9 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPSUBW zmm1, {k}{z}, zmmV, zmm2/m512","VPSUBW zmm2/m512, zmmV, {k}{z}, zmm1","vpsubw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG F9 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPTERNLOGD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst, imm8u","VPTERNLOGD imm8u, xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpternlogd imm8u, xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F3A.W0 25 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale16","rw,r,r,r,r","","" +"VPTERNLOGD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst, imm8u","VPTERNLOGD imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpternlogd imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F3A.W0 25 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale32","rw,r,r,r,r","","" +"VPTERNLOGD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst, imm8u","VPTERNLOGD imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpternlogd imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F3A.W0 25 /r ib","V","V","AVX512F","bscale4,scale64","rw,r,r,r,r","","" +"VPTERNLOGQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst, imm8u","VPTERNLOGQ imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpternlogq imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.DDS.128.66.0F3A.W1 25 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale16","rw,r,r,r,r","","" +"VPTERNLOGQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst, imm8u","VPTERNLOGQ imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpternlogq imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.DDS.256.66.0F3A.W1 25 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale32","rw,r,r,r,r","","" +"VPTERNLOGQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst, imm8u","VPTERNLOGQ imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpternlogq imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.DDS.512.66.0F3A.W1 25 /r ib","V","V","AVX512F","bscale8,scale64","rw,r,r,r,r","","" +"VPTEST xmm1, xmm2/m128","VPTEST xmm2/m128, xmm1","vptest xmm2/m128, xmm1","VEX.128.66.0F38.WIG 17 /r","V","V","AVX","","r,r","","" +"VPTEST ymm1, ymm2/m256","VPTEST ymm2/m256, ymm1","vptest ymm2/m256, ymm1","VEX.256.66.0F38.WIG 17 /r","V","V","AVX","","r,r","","" +"VPTESTMB k1, {k}, xmmV, xmm2/m128","VPTESTMB xmm2/m128, xmmV, {k}, k1","vptestmb xmm2/m128, xmmV, {k}, k1","EVEX.NDS.128.66.0F38.W0 26 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPTESTMB k1, {k}, ymmV, ymm2/m256","VPTESTMB ymm2/m256, ymmV, {k}, k1","vptestmb ymm2/m256, ymmV, {k}, k1","EVEX.NDS.256.66.0F38.W0 26 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPTESTMB k1, {k}, zmmV, zmm2/m512","VPTESTMB zmm2/m512, zmmV, {k}, k1","vptestmb zmm2/m512, zmmV, {k}, k1","EVEX.NDS.512.66.0F38.W0 26 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPTESTMD k1, {k}, xmmV, xmm2/m128/m32bcst","VPTESTMD xmm2/m128/m32bcst, xmmV, {k}, k1","vptestmd xmm2/m128/m32bcst, xmmV, {k}, k1","EVEX.NDS.128.66.0F38.W0 27 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPTESTMD k1, {k}, ymmV, ymm2/m256/m32bcst","VPTESTMD ymm2/m256/m32bcst, ymmV, {k}, k1","vptestmd ymm2/m256/m32bcst, ymmV, {k}, k1","EVEX.NDS.256.66.0F38.W0 27 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPTESTMD k1, {k}, zmmV, zmm2/m512/m32bcst","VPTESTMD zmm2/m512/m32bcst, zmmV, {k}, k1","vptestmd zmm2/m512/m32bcst, zmmV, {k}, k1","EVEX.NDS.512.66.0F38.W0 27 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPTESTMQ k1, {k}, xmmV, xmm2/m128/m64bcst","VPTESTMQ xmm2/m128/m64bcst, xmmV, {k}, k1","vptestmq xmm2/m128/m64bcst, xmmV, {k}, k1","EVEX.NDS.128.66.0F38.W1 27 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPTESTMQ k1, {k}, ymmV, ymm2/m256/m64bcst","VPTESTMQ ymm2/m256/m64bcst, ymmV, {k}, k1","vptestmq ymm2/m256/m64bcst, ymmV, {k}, k1","EVEX.NDS.256.66.0F38.W1 27 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPTESTMQ k1, {k}, zmmV, zmm2/m512/m64bcst","VPTESTMQ zmm2/m512/m64bcst, zmmV, {k}, k1","vptestmq zmm2/m512/m64bcst, zmmV, {k}, k1","EVEX.NDS.512.66.0F38.W1 27 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPTESTMW k1, {k}, xmmV, xmm2/m128","VPTESTMW xmm2/m128, xmmV, {k}, k1","vptestmw xmm2/m128, xmmV, {k}, k1","EVEX.NDS.128.66.0F38.W1 26 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPTESTMW k1, {k}, ymmV, ymm2/m256","VPTESTMW ymm2/m256, ymmV, {k}, k1","vptestmw ymm2/m256, ymmV, {k}, k1","EVEX.NDS.256.66.0F38.W1 26 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPTESTMW k1, {k}, zmmV, zmm2/m512","VPTESTMW zmm2/m512, zmmV, {k}, k1","vptestmw zmm2/m512, zmmV, {k}, k1","EVEX.NDS.512.66.0F38.W1 26 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPTESTNMB k1, {k}, xmmV, xmm2/m128","VPTESTNMB xmm2/m128, xmmV, {k}, k1","vptestnmb xmm2/m128, xmmV, {k}, k1","EVEX.NDS.128.F3.0F38.W0 26 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPTESTNMB k1, {k}, ymmV, ymm2/m256","VPTESTNMB ymm2/m256, ymmV, {k}, k1","vptestnmb ymm2/m256, ymmV, {k}, k1","EVEX.NDS.256.F3.0F38.W0 26 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPTESTNMB k1, {k}, zmmV, zmm2/m512","VPTESTNMB zmm2/m512, zmmV, {k}, k1","vptestnmb zmm2/m512, zmmV, {k}, k1","EVEX.NDS.512.F3.0F38.W0 26 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPTESTNMD k1, {k}, xmmV, xmm2/m128/m32bcst","VPTESTNMD xmm2/m128/m32bcst, xmmV, {k}, k1","vptestnmd xmm2/m128/m32bcst, xmmV, {k}, k1","EVEX.NDS.128.F3.0F38.W0 27 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPTESTNMD k1, {k}, ymmV, ymm2/m256/m32bcst","VPTESTNMD ymm2/m256/m32bcst, ymmV, {k}, k1","vptestnmd ymm2/m256/m32bcst, ymmV, {k}, k1","EVEX.NDS.256.F3.0F38.W0 27 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPTESTNMD k1, {k}, zmmV, zmm2/m512/m32bcst","VPTESTNMD zmm2/m512/m32bcst, zmmV, {k}, k1","vptestnmd zmm2/m512/m32bcst, zmmV, {k}, k1","EVEX.NDS.512.F3.0F38.W0 27 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPTESTNMQ k1, {k}, xmmV, xmm2/m128/m64bcst","VPTESTNMQ xmm2/m128/m64bcst, xmmV, {k}, k1","vptestnmq xmm2/m128/m64bcst, xmmV, {k}, k1","EVEX.NDS.128.F3.0F38.W1 27 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPTESTNMQ k1, {k}, ymmV, ymm2/m256/m64bcst","VPTESTNMQ ymm2/m256/m64bcst, ymmV, {k}, k1","vptestnmq ymm2/m256/m64bcst, ymmV, {k}, k1","EVEX.NDS.256.F3.0F38.W1 27 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPTESTNMQ k1, {k}, zmmV, zmm2/m512/m64bcst","VPTESTNMQ zmm2/m512/m64bcst, zmmV, {k}, k1","vptestnmq zmm2/m512/m64bcst, zmmV, {k}, k1","EVEX.NDS.512.F3.0F38.W1 27 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPTESTNMW k1, {k}, xmmV, xmm2/m128","VPTESTNMW xmm2/m128, xmmV, {k}, k1","vptestnmw xmm2/m128, xmmV, {k}, k1","EVEX.NDS.128.F3.0F38.W1 26 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPTESTNMW k1, {k}, ymmV, ymm2/m256","VPTESTNMW ymm2/m256, ymmV, {k}, k1","vptestnmw ymm2/m256, ymmV, {k}, k1","EVEX.NDS.256.F3.0F38.W1 26 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPTESTNMW k1, {k}, zmmV, zmm2/m512","VPTESTNMW zmm2/m512, zmmV, {k}, k1","vptestnmw zmm2/m512, zmmV, {k}, k1","EVEX.NDS.512.F3.0F38.W1 26 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPUNPCKHBW xmm1, xmmV, xmm2/m128","VPUNPCKHBW xmm2/m128, xmmV, xmm1","vpunpckhbw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 68 /r","V","V","AVX","","w,r,r","","" +"VPUNPCKHBW xmm1, {k}{z}, xmmV, xmm2/m128","VPUNPCKHBW xmm2/m128, xmmV, {k}{z}, xmm1","vpunpckhbw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG 68 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPUNPCKHBW ymm1, ymmV, ymm2/m256","VPUNPCKHBW ymm2/m256, ymmV, ymm1","vpunpckhbw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 68 /r","V","V","AVX2","","w,r,r","","" +"VPUNPCKHBW ymm1, {k}{z}, ymmV, ymm2/m256","VPUNPCKHBW ymm2/m256, ymmV, {k}{z}, ymm1","vpunpckhbw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG 68 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPUNPCKHBW zmm1, {k}{z}, zmmV, zmm2/m512","VPUNPCKHBW zmm2/m512, zmmV, {k}{z}, zmm1","vpunpckhbw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG 68 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPUNPCKHDQ xmm1, xmmV, xmm2/m128","VPUNPCKHDQ xmm2/m128, xmmV, xmm1","vpunpckhdq xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 6A /r","V","V","AVX","","w,r,r","","" +"VPUNPCKHDQ xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPUNPCKHDQ xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpunpckhdq xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W0 6A /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPUNPCKHDQ ymm1, ymmV, ymm2/m256","VPUNPCKHDQ ymm2/m256, ymmV, ymm1","vpunpckhdq ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 6A /r","V","V","AVX2","","w,r,r","","" +"VPUNPCKHDQ ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPUNPCKHDQ ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpunpckhdq ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W0 6A /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPUNPCKHDQ zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPUNPCKHDQ zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpunpckhdq zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W0 6A /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPUNPCKHQDQ xmm1, xmmV, xmm2/m128","VPUNPCKHQDQ xmm2/m128, xmmV, xmm1","vpunpckhqdq xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 6D /r","V","V","AVX","","w,r,r","","" +"VPUNPCKHQDQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPUNPCKHQDQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpunpckhqdq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 6D /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPUNPCKHQDQ ymm1, ymmV, ymm2/m256","VPUNPCKHQDQ ymm2/m256, ymmV, ymm1","vpunpckhqdq ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 6D /r","V","V","AVX2","","w,r,r","","" +"VPUNPCKHQDQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPUNPCKHQDQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpunpckhqdq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 6D /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPUNPCKHQDQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPUNPCKHQDQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpunpckhqdq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 6D /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPUNPCKHWD xmm1, xmmV, xmm2/m128","VPUNPCKHWD xmm2/m128, xmmV, xmm1","vpunpckhwd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 69 /r","V","V","AVX","","w,r,r","","" +"VPUNPCKHWD xmm1, {k}{z}, xmmV, xmm2/m128","VPUNPCKHWD xmm2/m128, xmmV, {k}{z}, xmm1","vpunpckhwd xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG 69 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPUNPCKHWD ymm1, ymmV, ymm2/m256","VPUNPCKHWD ymm2/m256, ymmV, ymm1","vpunpckhwd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 69 /r","V","V","AVX2","","w,r,r","","" +"VPUNPCKHWD ymm1, {k}{z}, ymmV, ymm2/m256","VPUNPCKHWD ymm2/m256, ymmV, {k}{z}, ymm1","vpunpckhwd ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG 69 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPUNPCKHWD zmm1, {k}{z}, zmmV, zmm2/m512","VPUNPCKHWD zmm2/m512, zmmV, {k}{z}, zmm1","vpunpckhwd zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG 69 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPUNPCKLBW xmm1, xmmV, xmm2/m128","VPUNPCKLBW xmm2/m128, xmmV, xmm1","vpunpcklbw xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 60 /r","V","V","AVX","","w,r,r","","" +"VPUNPCKLBW xmm1, {k}{z}, xmmV, xmm2/m128","VPUNPCKLBW xmm2/m128, xmmV, {k}{z}, xmm1","vpunpcklbw xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG 60 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPUNPCKLBW ymm1, ymmV, ymm2/m256","VPUNPCKLBW ymm2/m256, ymmV, ymm1","vpunpcklbw ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 60 /r","V","V","AVX2","","w,r,r","","" +"VPUNPCKLBW ymm1, {k}{z}, ymmV, ymm2/m256","VPUNPCKLBW ymm2/m256, ymmV, {k}{z}, ymm1","vpunpcklbw ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG 60 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPUNPCKLBW zmm1, {k}{z}, zmmV, zmm2/m512","VPUNPCKLBW zmm2/m512, zmmV, {k}{z}, zmm1","vpunpcklbw zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG 60 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPUNPCKLDQ xmm1, xmmV, xmm2/m128","VPUNPCKLDQ xmm2/m128, xmmV, xmm1","vpunpckldq xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 62 /r","V","V","AVX","","w,r,r","","" +"VPUNPCKLDQ xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPUNPCKLDQ xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpunpckldq xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W0 62 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPUNPCKLDQ ymm1, ymmV, ymm2/m256","VPUNPCKLDQ ymm2/m256, ymmV, ymm1","vpunpckldq ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 62 /r","V","V","AVX2","","w,r,r","","" +"VPUNPCKLDQ ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPUNPCKLDQ ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpunpckldq ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W0 62 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPUNPCKLDQ zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPUNPCKLDQ zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpunpckldq zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W0 62 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPUNPCKLQDQ xmm1, xmmV, xmm2/m128","VPUNPCKLQDQ xmm2/m128, xmmV, xmm1","vpunpcklqdq xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 6C /r","V","V","AVX","","w,r,r","","" +"VPUNPCKLQDQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPUNPCKLQDQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpunpcklqdq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 6C /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPUNPCKLQDQ ymm1, ymmV, ymm2/m256","VPUNPCKLQDQ ymm2/m256, ymmV, ymm1","vpunpcklqdq ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 6C /r","V","V","AVX2","","w,r,r","","" +"VPUNPCKLQDQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPUNPCKLQDQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpunpcklqdq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 6C /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPUNPCKLQDQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPUNPCKLQDQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpunpcklqdq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 6C /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VPUNPCKLWD xmm1, xmmV, xmm2/m128","VPUNPCKLWD xmm2/m128, xmmV, xmm1","vpunpcklwd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 61 /r","V","V","AVX","","w,r,r","","" +"VPUNPCKLWD xmm1, {k}{z}, xmmV, xmm2/m128","VPUNPCKLWD xmm2/m128, xmmV, {k}{z}, xmm1","vpunpcklwd xmm2/m128, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.WIG 61 /r","V","V","AVX512BW+AVX512VL","scale16","w,r,r,r","","" +"VPUNPCKLWD ymm1, ymmV, ymm2/m256","VPUNPCKLWD ymm2/m256, ymmV, ymm1","vpunpcklwd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 61 /r","V","V","AVX2","","w,r,r","","" +"VPUNPCKLWD ymm1, {k}{z}, ymmV, ymm2/m256","VPUNPCKLWD ymm2/m256, ymmV, {k}{z}, ymm1","vpunpcklwd ymm2/m256, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.WIG 61 /r","V","V","AVX512BW+AVX512VL","scale32","w,r,r,r","","" +"VPUNPCKLWD zmm1, {k}{z}, zmmV, zmm2/m512","VPUNPCKLWD zmm2/m512, zmmV, {k}{z}, zmm1","vpunpcklwd zmm2/m512, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.WIG 61 /r","V","V","AVX512BW","scale64","w,r,r,r","","" +"VPXOR xmm1, xmmV, xmm2/m128","VPXOR xmm2/m128, xmmV, xmm1","vpxor xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG EF /r","V","V","AVX","","w,r,r","","" +"VPXOR ymm1, ymmV, ymm2/m256","VPXOR ymm2/m256, ymmV, ymm1","vpxor ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG EF /r","V","V","AVX2","","w,r,r","","" +"VPXORD xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VPXORD xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vpxord xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W0 EF /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VPXORD ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VPXORD ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vpxord ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W0 EF /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VPXORD zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VPXORD zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vpxord zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W0 EF /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VPXORQ xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VPXORQ xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vpxorq xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 EF /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VPXORQ ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VPXORQ ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vpxorq ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 EF /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VPXORQ zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VPXORQ zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vpxorq zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 EF /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VRANGEPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst, imm8u:4","VRANGEPD imm8u:4, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vrangepd imm8u:4, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F3A.W1 50 /r ib","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r,r,r","","" +"VRANGEPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst, imm8u:4","VRANGEPD imm8u:4, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vrangepd imm8u:4, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W1 50 /r ib","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r,r,r","","" +"VRANGEPD zmm1{sae}, {k}{z}, zmmV, zmm2, imm8u:4","VRANGEPD imm8u:4, zmm2, zmmV, {k}{z}, zmm1{sae}","vrangepd imm8u:4, zmm2, zmmV, {k}{z}, zmm1{sae}","EVEX.NDS.512.66.0F3A.W1 50 /r ib","V","V","AVX512DQ","modrm_regonly","w,r,r,r,r","","" +"VRANGEPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst, imm8u:4","VRANGEPD imm8u:4, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vrangepd imm8u:4, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W1 50 /r ib","V","V","AVX512DQ","bscale8,scale64","w,r,r,r,r","","" +"VRANGEPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst, imm8u:4","VRANGEPS imm8u:4, xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vrangeps imm8u:4, xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F3A.W0 50 /r ib","V","V","AVX512DQ+AVX512VL","bscale4,scale16","w,r,r,r,r","","" +"VRANGEPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst, imm8u:4","VRANGEPS imm8u:4, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vrangeps imm8u:4, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W0 50 /r ib","V","V","AVX512DQ+AVX512VL","bscale4,scale32","w,r,r,r,r","","" +"VRANGEPS zmm1{sae}, {k}{z}, zmmV, zmm2, imm8u:4","VRANGEPS imm8u:4, zmm2, zmmV, {k}{z}, zmm1{sae}","vrangeps imm8u:4, zmm2, zmmV, {k}{z}, zmm1{sae}","EVEX.NDS.512.66.0F3A.W0 50 /r ib","V","V","AVX512DQ","modrm_regonly","w,r,r,r,r","","" +"VRANGEPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst, imm8u:4","VRANGEPS imm8u:4, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vrangeps imm8u:4, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W0 50 /r ib","V","V","AVX512DQ","bscale4,scale64","w,r,r,r,r","","" +"VRANGESD xmm1{sae}, {k}{z}, xmmV, xmm2, imm8u:4","VRANGESD imm8u:4, xmm2, xmmV, {k}{z}, xmm1{sae}","vrangesd imm8u:4, xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.66.0F3A.W1 51 /r ib","V","V","AVX512DQ","modrm_regonly","w,r,r,r,r","","" +"VRANGESD xmm1, {k}{z}, xmmV, xmm2/m64, imm8u:4","VRANGESD imm8u:4, xmm2/m64, xmmV, {k}{z}, xmm1","vrangesd imm8u:4, xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F3A.W1 51 /r ib","V","V","AVX512DQ","scale8","w,r,r,r,r","","" +"VRANGESS xmm1{sae}, {k}{z}, xmmV, xmm2, imm8u:4","VRANGESS imm8u:4, xmm2, xmmV, {k}{z}, xmm1{sae}","vrangess imm8u:4, xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.66.0F3A.W0 51 /r ib","V","V","AVX512DQ","modrm_regonly","w,r,r,r,r","","" +"VRANGESS xmm1, {k}{z}, xmmV, xmm2/m32, imm8u:4","VRANGESS imm8u:4, xmm2/m32, xmmV, {k}{z}, xmm1","vrangess imm8u:4, xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F3A.W0 51 /r ib","V","V","AVX512DQ","scale4","w,r,r,r,r","","" +"VRCP14PD xmm1, {k}{z}, xmm2/m128/m64bcst","VRCP14PD xmm2/m128/m64bcst, {k}{z}, xmm1","vrcp14pd xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F38.W1 4C /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r","","" +"VRCP14PD ymm1, {k}{z}, ymm2/m256/m64bcst","VRCP14PD ymm2/m256/m64bcst, {k}{z}, ymm1","vrcp14pd ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F38.W1 4C /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r","","" +"VRCP14PD zmm1, {k}{z}, zmm2/m512/m64bcst","VRCP14PD zmm2/m512/m64bcst, {k}{z}, zmm1","vrcp14pd zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W1 4C /r","V","V","AVX512F","bscale8,scale64","w,r,r","","" +"VRCP14PS xmm1, {k}{z}, xmm2/m128/m32bcst","VRCP14PS xmm2/m128/m32bcst, {k}{z}, xmm1","vrcp14ps xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F38.W0 4C /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r","","" +"VRCP14PS ymm1, {k}{z}, ymm2/m256/m32bcst","VRCP14PS ymm2/m256/m32bcst, {k}{z}, ymm1","vrcp14ps ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F38.W0 4C /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r","","" +"VRCP14PS zmm1, {k}{z}, zmm2/m512/m32bcst","VRCP14PS zmm2/m512/m32bcst, {k}{z}, zmm1","vrcp14ps zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W0 4C /r","V","V","AVX512F","bscale4,scale64","w,r,r","","" +"VRCP14SD xmm1, {k}{z}, xmmV, xmm2/m64","VRCP14SD xmm2/m64, xmmV, {k}{z}, xmm1","vrcp14sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F38.W1 4D /r","V","V","AVX512F","scale8","w,r,r,r","","" +"VRCP14SS xmm1, {k}{z}, xmmV, xmm2/m32","VRCP14SS xmm2/m32, xmmV, {k}{z}, xmm1","vrcp14ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F38.W0 4D /r","V","V","AVX512F","scale4","w,r,r,r","","" +"VRCP28PD zmm1{sae}, {k}{z}, zmm2","VRCP28PD zmm2, {k}{z}, zmm1{sae}","vrcp28pd zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F38.W1 CA /r","V","V","AVX512ER","modrm_regonly","w,r,r","","" +"VRCP28PD zmm1, {k}{z}, zmm2/m512/m64bcst","VRCP28PD zmm2/m512/m64bcst, {k}{z}, zmm1","vrcp28pd zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W1 CA /r","V","V","AVX512ER","bscale8,scale64","w,r,r","","" +"VRCP28PS zmm1{sae}, {k}{z}, zmm2","VRCP28PS zmm2, {k}{z}, zmm1{sae}","vrcp28ps zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F38.W0 CA /r","V","V","AVX512ER","modrm_regonly","w,r,r","","" +"VRCP28PS zmm1, {k}{z}, zmm2/m512/m32bcst","VRCP28PS zmm2/m512/m32bcst, {k}{z}, zmm1","vrcp28ps zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W0 CA /r","V","V","AVX512ER","bscale4,scale64","w,r,r","","" +"VRCP28SD xmm1{sae}, {k}{z}, xmmV, xmm2","VRCP28SD xmm2, xmmV, {k}{z}, xmm1{sae}","vrcp28sd xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.66.0F38.W1 CB /r","V","V","AVX512ER","modrm_regonly","w,r,r,r","","" +"VRCP28SD xmm1, {k}{z}, xmmV, xmm2/m64","VRCP28SD xmm2/m64, xmmV, {k}{z}, xmm1","vrcp28sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F38.W1 CB /r","V","V","AVX512ER","scale8","w,r,r,r","","" +"VRCP28SS xmm1{sae}, {k}{z}, xmmV, xmm2","VRCP28SS xmm2, xmmV, {k}{z}, xmm1{sae}","vrcp28ss xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.66.0F38.W0 CB /r","V","V","AVX512ER","modrm_regonly","w,r,r,r","","" +"VRCP28SS xmm1, {k}{z}, xmmV, xmm2/m32","VRCP28SS xmm2/m32, xmmV, {k}{z}, xmm1","vrcp28ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F38.W0 CB /r","V","V","AVX512ER","scale4","w,r,r,r","","" +"VRCPPS xmm1, xmm2/m128","VRCPPS xmm2/m128, xmm1","vrcpps xmm2/m128, xmm1","VEX.128.0F.WIG 53 /r","V","V","AVX","","w,r","","" +"VRCPPS ymm1, ymm2/m256","VRCPPS ymm2/m256, ymm1","vrcpps ymm2/m256, ymm1","VEX.256.0F.WIG 53 /r","V","V","AVX","","w,r","","" +"VRCPSS xmm1, xmmV, xmm2/m32","VRCPSS xmm2/m32, xmmV, xmm1","vrcpss xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.F3.0F.WIG 53 /r","V","V","AVX","","w,r,r","","" +"VREDUCEPD xmm1, {k}{z}, xmm2/m128/m64bcst, imm8u","VREDUCEPD imm8u, xmm2/m128/m64bcst, {k}{z}, xmm1","vreducepd imm8u, xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F3A.W1 56 /r ib","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VREDUCEPD ymm1, {k}{z}, ymm2/m256/m64bcst, imm8u","VREDUCEPD imm8u, ymm2/m256/m64bcst, {k}{z}, ymm1","vreducepd imm8u, ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F3A.W1 56 /r ib","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VREDUCEPD zmm1{sae}, {k}{z}, zmm2, imm8u","VREDUCEPD imm8u, zmm2, {k}{z}, zmm1{sae}","vreducepd imm8u, zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F3A.W1 56 /r ib","V","V","AVX512DQ","modrm_regonly","w,r,r,r","","" +"VREDUCEPD zmm1, {k}{z}, zmm2/m512/m64bcst, imm8u","VREDUCEPD imm8u, zmm2/m512/m64bcst, {k}{z}, zmm1","vreducepd imm8u, zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F3A.W1 56 /r ib","V","V","AVX512DQ","bscale8,scale64","w,r,r,r","","" +"VREDUCEPS xmm1, {k}{z}, xmm2/m128/m32bcst, imm8u","VREDUCEPS imm8u, xmm2/m128/m32bcst, {k}{z}, xmm1","vreduceps imm8u, xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F3A.W0 56 /r ib","V","V","AVX512DQ+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VREDUCEPS ymm1, {k}{z}, ymm2/m256/m32bcst, imm8u","VREDUCEPS imm8u, ymm2/m256/m32bcst, {k}{z}, ymm1","vreduceps imm8u, ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F3A.W0 56 /r ib","V","V","AVX512DQ+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VREDUCEPS zmm1{sae}, {k}{z}, zmm2, imm8u","VREDUCEPS imm8u, zmm2, {k}{z}, zmm1{sae}","vreduceps imm8u, zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F3A.W0 56 /r ib","V","V","AVX512DQ","modrm_regonly","w,r,r,r","","" +"VREDUCEPS zmm1, {k}{z}, zmm2/m512/m32bcst, imm8u","VREDUCEPS imm8u, zmm2/m512/m32bcst, {k}{z}, zmm1","vreduceps imm8u, zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F3A.W0 56 /r ib","V","V","AVX512DQ","bscale4,scale64","w,r,r,r","","" +"VREDUCESD xmm1{sae}, {k}{z}, xmmV, xmm2, imm8u","VREDUCESD imm8u, xmm2, xmmV, {k}{z}, xmm1{sae}","vreducesd imm8u, xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.66.0F3A.W1 57 /r ib","V","V","AVX512DQ","modrm_regonly","w,r,r,r,r","","" +"VREDUCESD xmm1, {k}{z}, xmmV, xmm2/m64, imm8u","VREDUCESD imm8u, xmm2/m64, xmmV, {k}{z}, xmm1","vreducesd imm8u, xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F3A.W1 57 /r ib","V","V","AVX512DQ","scale8","w,r,r,r,r","","" +"VREDUCESS xmm1{sae}, {k}{z}, xmmV, xmm2, imm8u","VREDUCESS imm8u, xmm2, xmmV, {k}{z}, xmm1{sae}","vreducess imm8u, xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.66.0F3A.W0 57 /r ib","V","V","AVX512DQ","modrm_regonly","w,r,r,r,r","","" +"VREDUCESS xmm1, {k}{z}, xmmV, xmm2/m32, imm8u","VREDUCESS imm8u, xmm2/m32, xmmV, {k}{z}, xmm1","vreducess imm8u, xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F3A.W0 57 /r ib","V","V","AVX512DQ","scale4","w,r,r,r,r","","" +"VRNDSCALEPD xmm1, {k}{z}, xmm2/m128/m64bcst, imm8u","VRNDSCALEPD imm8u, xmm2/m128/m64bcst, {k}{z}, xmm1","vrndscalepd imm8u, xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F3A.W1 09 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VRNDSCALEPD ymm1, {k}{z}, ymm2/m256/m64bcst, imm8u","VRNDSCALEPD imm8u, ymm2/m256/m64bcst, {k}{z}, ymm1","vrndscalepd imm8u, ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F3A.W1 09 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VRNDSCALEPD zmm1{sae}, {k}{z}, zmm2, imm8u","VRNDSCALEPD imm8u, zmm2, {k}{z}, zmm1{sae}","vrndscalepd imm8u, zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F3A.W1 09 /r ib","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VRNDSCALEPD zmm1, {k}{z}, zmm2/m512/m64bcst, imm8u","VRNDSCALEPD imm8u, zmm2/m512/m64bcst, {k}{z}, zmm1","vrndscalepd imm8u, zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F3A.W1 09 /r ib","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VRNDSCALEPS xmm1, {k}{z}, xmm2/m128/m32bcst, imm8u","VRNDSCALEPS imm8u, xmm2/m128/m32bcst, {k}{z}, xmm1","vrndscaleps imm8u, xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F3A.W0 08 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VRNDSCALEPS ymm1, {k}{z}, ymm2/m256/m32bcst, imm8u","VRNDSCALEPS imm8u, ymm2/m256/m32bcst, {k}{z}, ymm1","vrndscaleps imm8u, ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F3A.W0 08 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VRNDSCALEPS zmm1{sae}, {k}{z}, zmm2, imm8u","VRNDSCALEPS imm8u, zmm2, {k}{z}, zmm1{sae}","vrndscaleps imm8u, zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F3A.W0 08 /r ib","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VRNDSCALEPS zmm1, {k}{z}, zmm2/m512/m32bcst, imm8u","VRNDSCALEPS imm8u, zmm2/m512/m32bcst, {k}{z}, zmm1","vrndscaleps imm8u, zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F3A.W0 08 /r ib","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VRNDSCALESD xmm1{sae}, {k}{z}, xmmV, xmm2, imm8u","VRNDSCALESD imm8u, xmm2, xmmV, {k}{z}, xmm1{sae}","vrndscalesd imm8u, xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.66.0F3A.W1 0B /r ib","V","V","AVX512F","modrm_regonly","w,r,r,r,r","","" +"VRNDSCALESD xmm1, {k}{z}, xmmV, xmm2/m64, imm8u","VRNDSCALESD imm8u, xmm2/m64, xmmV, {k}{z}, xmm1","vrndscalesd imm8u, xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F3A.W1 0B /r ib","V","V","AVX512F","scale8","w,r,r,r,r","","" +"VRNDSCALESS xmm1{sae}, {k}{z}, xmmV, xmm2, imm8u","VRNDSCALESS imm8u, xmm2, xmmV, {k}{z}, xmm1{sae}","vrndscaless imm8u, xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.66.0F3A.W0 0A /r ib","V","V","AVX512F","modrm_regonly","w,r,r,r,r","","" +"VRNDSCALESS xmm1, {k}{z}, xmmV, xmm2/m32, imm8u","VRNDSCALESS imm8u, xmm2/m32, xmmV, {k}{z}, xmm1","vrndscaless imm8u, xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F3A.W0 0A /r ib","V","V","AVX512F","scale4","w,r,r,r,r","","" +"VROUNDPD xmm1, xmm2/m128, imm8u","VROUNDPD imm8u, xmm2/m128, xmm1","vroundpd imm8u, xmm2/m128, xmm1","VEX.128.66.0F3A.WIG 09 /r ib","V","V","AVX","","w,r,r","","" +"VROUNDPD ymm1, ymm2/m256, imm8u","VROUNDPD imm8u, ymm2/m256, ymm1","vroundpd imm8u, ymm2/m256, ymm1","VEX.256.66.0F3A.WIG 09 /r ib","V","V","AVX","","w,r,r","","" +"VROUNDPS xmm1, xmm2/m128, imm8u","VROUNDPS imm8u, xmm2/m128, xmm1","vroundps imm8u, xmm2/m128, xmm1","VEX.128.66.0F3A.WIG 08 /r ib","V","V","AVX","","w,r,r","","" +"VROUNDPS ymm1, ymm2/m256, imm8u","VROUNDPS imm8u, ymm2/m256, ymm1","vroundps imm8u, ymm2/m256, ymm1","VEX.256.66.0F3A.WIG 08 /r ib","V","V","AVX","","w,r,r","","" +"VROUNDSD xmm1, xmmV, xmm2/m64, imm8u","VROUNDSD imm8u, xmm2/m64, xmmV, xmm1","vroundsd imm8u, xmm2/m64, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.WIG 0B /r ib","V","V","AVX","","w,r,r,r","","" +"VROUNDSS xmm1, xmmV, xmm2/m32, imm8u","VROUNDSS imm8u, xmm2/m32, xmmV, xmm1","vroundss imm8u, xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.66.0F3A.WIG 0A /r ib","V","V","AVX","","w,r,r,r","","" +"VRSQRT14PD xmm1, {k}{z}, xmm2/m128/m64bcst","VRSQRT14PD xmm2/m128/m64bcst, {k}{z}, xmm1","vrsqrt14pd xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F38.W1 4E /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r","","" +"VRSQRT14PD ymm1, {k}{z}, ymm2/m256/m64bcst","VRSQRT14PD ymm2/m256/m64bcst, {k}{z}, ymm1","vrsqrt14pd ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F38.W1 4E /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r","","" +"VRSQRT14PD zmm1, {k}{z}, zmm2/m512/m64bcst","VRSQRT14PD zmm2/m512/m64bcst, {k}{z}, zmm1","vrsqrt14pd zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W1 4E /r","V","V","AVX512F","bscale8,scale64","w,r,r","","" +"VRSQRT14PS xmm1, {k}{z}, xmm2/m128/m32bcst","VRSQRT14PS xmm2/m128/m32bcst, {k}{z}, xmm1","vrsqrt14ps xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.66.0F38.W0 4E /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r","","" +"VRSQRT14PS ymm1, {k}{z}, ymm2/m256/m32bcst","VRSQRT14PS ymm2/m256/m32bcst, {k}{z}, ymm1","vrsqrt14ps ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.66.0F38.W0 4E /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r","","" +"VRSQRT14PS zmm1, {k}{z}, zmm2/m512/m32bcst","VRSQRT14PS zmm2/m512/m32bcst, {k}{z}, zmm1","vrsqrt14ps zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W0 4E /r","V","V","AVX512F","bscale4,scale64","w,r,r","","" +"VRSQRT14SD xmm1, {k}{z}, xmmV, xmm2/m64","VRSQRT14SD xmm2/m64, xmmV, {k}{z}, xmm1","vrsqrt14sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F38.W1 4F /r","V","V","AVX512F","scale8","w,r,r,r","","" +"VRSQRT14SS xmm1, {k}{z}, xmmV, xmm2/m32","VRSQRT14SS xmm2/m32, xmmV, {k}{z}, xmm1","vrsqrt14ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F38.W0 4F /r","V","V","AVX512F","scale4","w,r,r,r","","" +"VRSQRT28PD zmm1{sae}, {k}{z}, zmm2","VRSQRT28PD zmm2, {k}{z}, zmm1{sae}","vrsqrt28pd zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F38.W1 CC /r","V","V","AVX512ER","modrm_regonly","w,r,r","","" +"VRSQRT28PD zmm1, {k}{z}, zmm2/m512/m64bcst","VRSQRT28PD zmm2/m512/m64bcst, {k}{z}, zmm1","vrsqrt28pd zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W1 CC /r","V","V","AVX512ER","bscale8,scale64","w,r,r","","" +"VRSQRT28PS zmm1{sae}, {k}{z}, zmm2","VRSQRT28PS zmm2, {k}{z}, zmm1{sae}","vrsqrt28ps zmm2, {k}{z}, zmm1{sae}","EVEX.512.66.0F38.W0 CC /r","V","V","AVX512ER","modrm_regonly","w,r,r","","" +"VRSQRT28PS zmm1, {k}{z}, zmm2/m512/m32bcst","VRSQRT28PS zmm2/m512/m32bcst, {k}{z}, zmm1","vrsqrt28ps zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.66.0F38.W0 CC /r","V","V","AVX512ER","bscale4,scale64","w,r,r","","" +"VRSQRT28SD xmm1{sae}, {k}{z}, xmmV, xmm2","VRSQRT28SD xmm2, xmmV, {k}{z}, xmm1{sae}","vrsqrt28sd xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.66.0F38.W1 CD /r","V","V","AVX512ER","modrm_regonly","w,r,r,r","","" +"VRSQRT28SD xmm1, {k}{z}, xmmV, xmm2/m64","VRSQRT28SD xmm2/m64, xmmV, {k}{z}, xmm1","vrsqrt28sd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F38.W1 CD /r","V","V","AVX512ER","scale8","w,r,r,r","","" +"VRSQRT28SS xmm1{sae}, {k}{z}, xmmV, xmm2","VRSQRT28SS xmm2, xmmV, {k}{z}, xmm1{sae}","vrsqrt28ss xmm2, xmmV, {k}{z}, xmm1{sae}","EVEX.NDS.128.66.0F38.W0 CD /r","V","V","AVX512ER","modrm_regonly","w,r,r,r","","" +"VRSQRT28SS xmm1, {k}{z}, xmmV, xmm2/m32","VRSQRT28SS xmm2/m32, xmmV, {k}{z}, xmm1","vrsqrt28ss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F38.W0 CD /r","V","V","AVX512ER","scale4","w,r,r,r","","" +"VRSQRTPS xmm1, xmm2/m128","VRSQRTPS xmm2/m128, xmm1","vrsqrtps xmm2/m128, xmm1","VEX.128.0F.WIG 52 /r","V","V","AVX","","w,r","","" +"VRSQRTPS ymm1, ymm2/m256","VRSQRTPS ymm2/m256, ymm1","vrsqrtps ymm2/m256, ymm1","VEX.256.0F.WIG 52 /r","V","V","AVX","","w,r","","" +"VRSQRTSS xmm1, xmmV, xmm2/m32","VRSQRTSS xmm2/m32, xmmV, xmm1","vrsqrtss xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.F3.0F.WIG 52 /r","V","V","AVX","","w,r,r","","" +"VSCALEFPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VSCALEFPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vscalefpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W1 2C /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VSCALEFPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VSCALEFPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vscalefpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W1 2C /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VSCALEFPD zmm1{er}, {k}{z}, zmmV, zmm2","VSCALEFPD zmm2, zmmV, {k}{z}, zmm1{er}","vscalefpd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.NDS.512.66.0F38.W1 2C /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VSCALEFPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VSCALEFPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vscalefpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W1 2C /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VSCALEFPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VSCALEFPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vscalefps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F38.W0 2C /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VSCALEFPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VSCALEFPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vscalefps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F38.W0 2C /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VSCALEFPS zmm1{er}, {k}{z}, zmmV, zmm2","VSCALEFPS zmm2, zmmV, {k}{z}, zmm1{er}","vscalefps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.NDS.512.66.0F38.W0 2C /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VSCALEFPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VSCALEFPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vscalefps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F38.W0 2C /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VSCALEFSD xmm1{er}, {k}{z}, xmmV, xmm2","VSCALEFSD xmm2, xmmV, {k}{z}, xmm1{er}","vscalefsd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.NDS.128.66.0F38.W1 2D /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VSCALEFSD xmm1, {k}{z}, xmmV, xmm2/m64","VSCALEFSD xmm2/m64, xmmV, {k}{z}, xmm1","vscalefsd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F38.W1 2D /r","V","V","AVX512F","scale8","w,r,r,r","","" +"VSCALEFSS xmm1{er}, {k}{z}, xmmV, xmm2","VSCALEFSS xmm2, xmmV, {k}{z}, xmm1{er}","vscalefss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.NDS.128.66.0F38.W0 2D /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VSCALEFSS xmm1, {k}{z}, xmmV, xmm2/m32","VSCALEFSS xmm2/m32, xmmV, {k}{z}, xmm1","vscalefss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.66.0F38.W0 2D /r","V","V","AVX512F","scale4","w,r,r,r","","" +"VSCATTERDPD vm32x, {k1-k7}, xmm1","VSCATTERDPD xmm1, {k1-k7}, vm32x","vscatterdpd xmm1, {k1-k7}, vm32x","EVEX.128.66.0F38.W1 A2 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VSCATTERDPD vm32x, {k1-k7}, ymm1","VSCATTERDPD ymm1, {k1-k7}, vm32x","vscatterdpd ymm1, {k1-k7}, vm32x","EVEX.256.66.0F38.W1 A2 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VSCATTERDPD vm32y, {k1-k7}, zmm1","VSCATTERDPD zmm1, {k1-k7}, vm32y","vscatterdpd zmm1, {k1-k7}, vm32y","EVEX.512.66.0F38.W1 A2 /vsib","V","V","AVX512F","modrm_memonly,scale8","w,rw,r","","" +"VSCATTERDPS vm32x, {k1-k7}, xmm1","VSCATTERDPS xmm1, {k1-k7}, vm32x","vscatterdps xmm1, {k1-k7}, vm32x","EVEX.128.66.0F38.W0 A2 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VSCATTERDPS vm32y, {k1-k7}, ymm1","VSCATTERDPS ymm1, {k1-k7}, vm32y","vscatterdps ymm1, {k1-k7}, vm32y","EVEX.256.66.0F38.W0 A2 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VSCATTERDPS vm32z, {k1-k7}, zmm1","VSCATTERDPS zmm1, {k1-k7}, vm32z","vscatterdps zmm1, {k1-k7}, vm32z","EVEX.512.66.0F38.W0 A2 /vsib","V","V","AVX512F","modrm_memonly,scale4","w,rw,r","","" +"VSCATTERPF0DPD vm32y, {k1-k7}","VSCATTERPF0DPD {k1-k7}, vm32y","vscatterpf0dpd {k1-k7}, vm32y","EVEX.512.66.0F38.W1 C6 /5","V","V","AVX512PF","modrm_memonly,scale8","r,rw","","" +"VSCATTERPF0DPS vm32z, {k1-k7}","VSCATTERPF0DPS {k1-k7}, vm32z","vscatterpf0dps {k1-k7}, vm32z","EVEX.512.66.0F38.W0 C6 /5","V","V","AVX512PF","modrm_memonly,scale4","r,rw","","" +"VSCATTERPF0QPD vm64z, {k1-k7}","VSCATTERPF0QPD {k1-k7}, vm64z","vscatterpf0qpd {k1-k7}, vm64z","EVEX.512.66.0F38.W1 C7 /5","V","V","AVX512PF","modrm_memonly,scale8","r,rw","","" +"VSCATTERPF0QPS vm64z, {k1-k7}","VSCATTERPF0QPS {k1-k7}, vm64z","vscatterpf0qps {k1-k7}, vm64z","EVEX.512.66.0F38.W0 C7 /5","V","V","AVX512PF","modrm_memonly,scale4","r,rw","","" +"VSCATTERPF1DPD vm32y, {k1-k7}","VSCATTERPF1DPD {k1-k7}, vm32y","vscatterpf1dpd {k1-k7}, vm32y","EVEX.512.66.0F38.W1 C6 /6","V","V","AVX512PF","modrm_memonly,scale8","r,rw","","" +"VSCATTERPF1DPS vm32z, {k1-k7}","VSCATTERPF1DPS {k1-k7}, vm32z","vscatterpf1dps {k1-k7}, vm32z","EVEX.512.66.0F38.W0 C6 /6","V","V","AVX512PF","modrm_memonly,scale4","r,rw","","" +"VSCATTERPF1QPD vm64z, {k1-k7}","VSCATTERPF1QPD {k1-k7}, vm64z","vscatterpf1qpd {k1-k7}, vm64z","EVEX.512.66.0F38.W1 C7 /6","V","V","AVX512PF","modrm_memonly,scale8","r,rw","","" +"VSCATTERPF1QPS vm64z, {k1-k7}","VSCATTERPF1QPS {k1-k7}, vm64z","vscatterpf1qps {k1-k7}, vm64z","EVEX.512.66.0F38.W0 C7 /6","V","V","AVX512PF","modrm_memonly,scale4","r,rw","","" +"VSCATTERQPD vm64x, {k1-k7}, xmm1","VSCATTERQPD xmm1, {k1-k7}, vm64x","vscatterqpd xmm1, {k1-k7}, vm64x","EVEX.128.66.0F38.W1 A3 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VSCATTERQPD vm64y, {k1-k7}, ymm1","VSCATTERQPD ymm1, {k1-k7}, vm64y","vscatterqpd ymm1, {k1-k7}, vm64y","EVEX.256.66.0F38.W1 A3 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale8","w,rw,r","","" +"VSCATTERQPD vm64z, {k1-k7}, zmm1","VSCATTERQPD zmm1, {k1-k7}, vm64z","vscatterqpd zmm1, {k1-k7}, vm64z","EVEX.512.66.0F38.W1 A3 /vsib","V","V","AVX512F","modrm_memonly,scale8","w,rw,r","","" +"VSCATTERQPS vm64x, {k1-k7}, xmm1","VSCATTERQPS xmm1, {k1-k7}, vm64x","vscatterqps xmm1, {k1-k7}, vm64x","EVEX.128.66.0F38.W0 A3 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VSCATTERQPS vm64y, {k1-k7}, xmm1","VSCATTERQPS xmm1, {k1-k7}, vm64y","vscatterqps xmm1, {k1-k7}, vm64y","EVEX.256.66.0F38.W0 A3 /vsib","V","V","AVX512F+AVX512VL","modrm_memonly,scale4","w,rw,r","","" +"VSCATTERQPS vm64z, {k1-k7}, ymm1","VSCATTERQPS ymm1, {k1-k7}, vm64z","vscatterqps ymm1, {k1-k7}, vm64z","EVEX.512.66.0F38.W0 A3 /vsib","V","V","AVX512F","modrm_memonly,scale4","w,rw,r","","" +"VSHUFF32X4 ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst, imm8u","VSHUFF32X4 imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vshuff32x4 imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W0 23 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r,r","","" +"VSHUFF32X4 zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst, imm8u","VSHUFF32X4 imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vshuff32x4 imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W0 23 /r ib","V","V","AVX512F","bscale4,scale64","w,r,r,r,r","","" +"VSHUFF64X2 ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst, imm8u","VSHUFF64X2 imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vshuff64x2 imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W1 23 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r,r","","" +"VSHUFF64X2 zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst, imm8u","VSHUFF64X2 imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vshuff64x2 imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W1 23 /r ib","V","V","AVX512F","bscale8,scale64","w,r,r,r,r","","" +"VSHUFI32X4 ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst, imm8u","VSHUFI32X4 imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vshufi32x4 imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W0 43 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r,r","","" +"VSHUFI32X4 zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst, imm8u","VSHUFI32X4 imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vshufi32x4 imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W0 43 /r ib","V","V","AVX512F","bscale4,scale64","w,r,r,r,r","","" +"VSHUFI64X2 ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst, imm8u","VSHUFI64X2 imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vshufi64x2 imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F3A.W1 43 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r,r","","" +"VSHUFI64X2 zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst, imm8u","VSHUFI64X2 imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vshufi64x2 imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F3A.W1 43 /r ib","V","V","AVX512F","bscale8,scale64","w,r,r,r,r","","" +"VSHUFPD xmm1, xmmV, xmm2/m128, imm8u","VSHUFPD imm8u, xmm2/m128, xmmV, xmm1","vshufpd imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG C6 /r ib","V","V","AVX","","w,r,r,r","","" +"VSHUFPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst, imm8u","VSHUFPD imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vshufpd imm8u, xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 C6 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r,r","","" +"VSHUFPD ymm1, ymmV, ymm2/m256, imm8u","VSHUFPD imm8u, ymm2/m256, ymmV, ymm1","vshufpd imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG C6 /r ib","V","V","AVX","","w,r,r,r","","" +"VSHUFPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst, imm8u","VSHUFPD imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vshufpd imm8u, ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 C6 /r ib","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r,r","","" +"VSHUFPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst, imm8u","VSHUFPD imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vshufpd imm8u, zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 C6 /r ib","V","V","AVX512F","bscale8,scale64","w,r,r,r,r","","" +"VSHUFPS xmm1, xmmV, xmm2/m128, imm8u","VSHUFPS imm8u, xmm2/m128, xmmV, xmm1","vshufps imm8u, xmm2/m128, xmmV, xmm1","VEX.NDS.128.0F.WIG C6 /r ib","V","V","AVX","","w,r,r,r","","" +"VSHUFPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst, imm8u","VSHUFPS imm8u, xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vshufps imm8u, xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.0F.W0 C6 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r,r","","" +"VSHUFPS ymm1, ymmV, ymm2/m256, imm8u","VSHUFPS imm8u, ymm2/m256, ymmV, ymm1","vshufps imm8u, ymm2/m256, ymmV, ymm1","VEX.NDS.256.0F.WIG C6 /r ib","V","V","AVX","","w,r,r,r","","" +"VSHUFPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst, imm8u","VSHUFPS imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vshufps imm8u, ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.0F.W0 C6 /r ib","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r,r","","" +"VSHUFPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst, imm8u","VSHUFPS imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vshufps imm8u, zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.0F.W0 C6 /r ib","V","V","AVX512F","bscale4,scale64","w,r,r,r,r","","" +"VSQRTPD xmm1, xmm2/m128","VSQRTPD xmm2/m128, xmm1","vsqrtpd xmm2/m128, xmm1","VEX.128.66.0F.WIG 51 /r","V","V","AVX","","w,r","","" +"VSQRTPD xmm1, {k}{z}, xmm2/m128/m64bcst","VSQRTPD xmm2/m128/m64bcst, {k}{z}, xmm1","vsqrtpd xmm2/m128/m64bcst, {k}{z}, xmm1","EVEX.128.66.0F.W1 51 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r","","" +"VSQRTPD ymm1, ymm2/m256","VSQRTPD ymm2/m256, ymm1","vsqrtpd ymm2/m256, ymm1","VEX.256.66.0F.WIG 51 /r","V","V","AVX","","w,r","","" +"VSQRTPD ymm1, {k}{z}, ymm2/m256/m64bcst","VSQRTPD ymm2/m256/m64bcst, {k}{z}, ymm1","vsqrtpd ymm2/m256/m64bcst, {k}{z}, ymm1","EVEX.256.66.0F.W1 51 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r","","" +"VSQRTPD zmm1{er}, {k}{z}, zmm2","VSQRTPD zmm2, {k}{z}, zmm1{er}","vsqrtpd zmm2, {k}{z}, zmm1{er}","EVEX.512.66.0F.W1 51 /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"VSQRTPD zmm1, {k}{z}, zmm2/m512/m64bcst","VSQRTPD zmm2/m512/m64bcst, {k}{z}, zmm1","vsqrtpd zmm2/m512/m64bcst, {k}{z}, zmm1","EVEX.512.66.0F.W1 51 /r","V","V","AVX512F","bscale8,scale64","w,r,r","","" +"VSQRTPS xmm1, xmm2/m128","VSQRTPS xmm2/m128, xmm1","vsqrtps xmm2/m128, xmm1","VEX.128.0F.WIG 51 /r","V","V","AVX","","w,r","","" +"VSQRTPS xmm1, {k}{z}, xmm2/m128/m32bcst","VSQRTPS xmm2/m128/m32bcst, {k}{z}, xmm1","vsqrtps xmm2/m128/m32bcst, {k}{z}, xmm1","EVEX.128.0F.W0 51 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r","","" +"VSQRTPS ymm1, ymm2/m256","VSQRTPS ymm2/m256, ymm1","vsqrtps ymm2/m256, ymm1","VEX.256.0F.WIG 51 /r","V","V","AVX","","w,r","","" +"VSQRTPS ymm1, {k}{z}, ymm2/m256/m32bcst","VSQRTPS ymm2/m256/m32bcst, {k}{z}, ymm1","vsqrtps ymm2/m256/m32bcst, {k}{z}, ymm1","EVEX.256.0F.W0 51 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r","","" +"VSQRTPS zmm1{er}, {k}{z}, zmm2","VSQRTPS zmm2, {k}{z}, zmm1{er}","vsqrtps zmm2, {k}{z}, zmm1{er}","EVEX.512.0F.W0 51 /r","V","V","AVX512F","modrm_regonly","w,r,r","","" +"VSQRTPS zmm1, {k}{z}, zmm2/m512/m32bcst","VSQRTPS zmm2/m512/m32bcst, {k}{z}, zmm1","vsqrtps zmm2/m512/m32bcst, {k}{z}, zmm1","EVEX.512.0F.W0 51 /r","V","V","AVX512F","bscale4,scale64","w,r,r","","" +"VSQRTSD xmm1{er}, {k}{z}, xmmV, xmm2","VSQRTSD xmm2, xmmV, {k}{z}, xmm1{er}","vsqrtsd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.NDS.128.F2.0F.W1 51 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VSQRTSD xmm1, xmmV, xmm2/m64","VSQRTSD xmm2/m64, xmmV, xmm1","vsqrtsd xmm2/m64, xmmV, xmm1","VEX.NDS.LIG.F2.0F.WIG 51 /r","V","V","AVX","","w,r,r","","" +"VSQRTSD xmm1, {k}{z}, xmmV, xmm2/m64","VSQRTSD xmm2/m64, xmmV, {k}{z}, xmm1","vsqrtsd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F2.0F.W1 51 /r","V","V","AVX512F","scale8","w,r,r,r","","" +"VSQRTSS xmm1{er}, {k}{z}, xmmV, xmm2","VSQRTSS xmm2, xmmV, {k}{z}, xmm1{er}","vsqrtss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.NDS.128.F3.0F.W0 51 /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VSQRTSS xmm1, xmmV, xmm2/m32","VSQRTSS xmm2/m32, xmmV, xmm1","vsqrtss xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.F3.0F.WIG 51 /r","V","V","AVX","","w,r,r","","" +"VSQRTSS xmm1, {k}{z}, xmmV, xmm2/m32","VSQRTSS xmm2/m32, xmmV, {k}{z}, xmm1","vsqrtss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F3.0F.W0 51 /r","V","V","AVX512F","scale4","w,r,r,r","","" +"VSTMXCSR m32","VSTMXCSR m32","vstmxcsr m32","VEX.128.0F.WIG AE /3","V","V","AVX","modrm_memonly","w","","" +"VSUBPD xmm1, xmmV, xmm2/m128","VSUBPD xmm2/m128, xmmV, xmm1","vsubpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 5C /r","V","V","AVX","","w,r,r","","" +"VSUBPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VSUBPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vsubpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 5C /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VSUBPD ymm1, ymmV, ymm2/m256","VSUBPD ymm2/m256, ymmV, ymm1","vsubpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 5C /r","V","V","AVX","","w,r,r","","" +"VSUBPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VSUBPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vsubpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 5C /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VSUBPD zmm1{er}, {k}{z}, zmmV, zmm2","VSUBPD zmm2, zmmV, {k}{z}, zmm1{er}","vsubpd zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.NDS.512.66.0F.W1 5C /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VSUBPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VSUBPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vsubpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 5C /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VSUBPS xmm1, xmmV, xmm2/m128","VSUBPS xmm2/m128, xmmV, xmm1","vsubps xmm2/m128, xmmV, xmm1","VEX.NDS.128.0F.WIG 5C /r","V","V","AVX","","w,r,r","","" +"VSUBPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VSUBPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vsubps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.0F.W0 5C /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VSUBPS ymm1, ymmV, ymm2/m256","VSUBPS ymm2/m256, ymmV, ymm1","vsubps ymm2/m256, ymmV, ymm1","VEX.NDS.256.0F.WIG 5C /r","V","V","AVX","","w,r,r","","" +"VSUBPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VSUBPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vsubps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.0F.W0 5C /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VSUBPS zmm1{er}, {k}{z}, zmmV, zmm2","VSUBPS zmm2, zmmV, {k}{z}, zmm1{er}","vsubps zmm2, zmmV, {k}{z}, zmm1{er}","EVEX.NDS.512.0F.W0 5C /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VSUBPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VSUBPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vsubps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.0F.W0 5C /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VSUBSD xmm1{er}, {k}{z}, xmmV, xmm2","VSUBSD xmm2, xmmV, {k}{z}, xmm1{er}","vsubsd xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.NDS.128.F2.0F.W1 5C /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VSUBSD xmm1, xmmV, xmm2/m64","VSUBSD xmm2/m64, xmmV, xmm1","vsubsd xmm2/m64, xmmV, xmm1","VEX.NDS.LIG.F2.0F.WIG 5C /r","V","V","AVX","","w,r,r","","" +"VSUBSD xmm1, {k}{z}, xmmV, xmm2/m64","VSUBSD xmm2/m64, xmmV, {k}{z}, xmm1","vsubsd xmm2/m64, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F2.0F.W1 5C /r","V","V","AVX512F","scale8","w,r,r,r","","" +"VSUBSS xmm1{er}, {k}{z}, xmmV, xmm2","VSUBSS xmm2, xmmV, {k}{z}, xmm1{er}","vsubss xmm2, xmmV, {k}{z}, xmm1{er}","EVEX.NDS.128.F3.0F.W0 5C /r","V","V","AVX512F","modrm_regonly","w,r,r,r","","" +"VSUBSS xmm1, xmmV, xmm2/m32","VSUBSS xmm2/m32, xmmV, xmm1","vsubss xmm2/m32, xmmV, xmm1","VEX.NDS.LIG.F3.0F.WIG 5C /r","V","V","AVX","","w,r,r","","" +"VSUBSS xmm1, {k}{z}, xmmV, xmm2/m32","VSUBSS xmm2/m32, xmmV, {k}{z}, xmm1","vsubss xmm2/m32, xmmV, {k}{z}, xmm1","EVEX.NDS.LIG.F3.0F.W0 5C /r","V","V","AVX512F","scale4","w,r,r,r","","" +"VTESTPD xmm1, xmm2/m128","VTESTPD xmm2/m128, xmm1","vtestpd xmm2/m128, xmm1","VEX.128.66.0F38.W0 0F /r","V","V","AVX","","r,r","","" +"VTESTPD ymm1, ymm2/m256","VTESTPD ymm2/m256, ymm1","vtestpd ymm2/m256, ymm1","VEX.256.66.0F38.W0 0F /r","V","V","AVX","","r,r","","" +"VTESTPS xmm1, xmm2/m128","VTESTPS xmm2/m128, xmm1","vtestps xmm2/m128, xmm1","VEX.128.66.0F38.W0 0E /r","V","V","AVX","","r,r","","" +"VTESTPS ymm1, ymm2/m256","VTESTPS ymm2/m256, ymm1","vtestps ymm2/m256, ymm1","VEX.256.66.0F38.W0 0E /r","V","V","AVX","","r,r","","" +"VUCOMISD xmm1{sae}, xmm2","VUCOMISD xmm2, xmm1{sae}","vucomisd xmm2, xmm1{sae}","EVEX.128.66.0F.W1 2E /r","V","V","AVX512F","modrm_regonly","r,r","","" +"VUCOMISD xmm1, xmm2/m64","VUCOMISD xmm2/m64, xmm1","vucomisd xmm2/m64, xmm1","EVEX.LIG.66.0F.W1 2E /r","V","V","AVX512F","scale8","r,r","","" +"VUCOMISD xmm1, xmm2/m64","VUCOMISD xmm2/m64, xmm1","vucomisd xmm2/m64, xmm1","VEX.LIG.66.0F.WIG 2E /r","V","V","AVX","","r,r","","" +"VUCOMISS xmm1{sae}, xmm2","VUCOMISS xmm2, xmm1{sae}","vucomiss xmm2, xmm1{sae}","EVEX.128.0F.W0 2E /r","V","V","AVX512F","modrm_regonly","r,r","","" +"VUCOMISS xmm1, xmm2/m32","VUCOMISS xmm2/m32, xmm1","vucomiss xmm2/m32, xmm1","EVEX.LIG.0F.W0 2E /r","V","V","AVX512F","scale4","r,r","","" +"VUCOMISS xmm1, xmm2/m32","VUCOMISS xmm2/m32, xmm1","vucomiss xmm2/m32, xmm1","VEX.LIG.0F.WIG 2E /r","V","V","AVX","","r,r","","" +"VUNPCKHPD xmm1, xmmV, xmm2/m128","VUNPCKHPD xmm2/m128, xmmV, xmm1","vunpckhpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 15 /r","V","V","AVX","","w,r,r","","" +"VUNPCKHPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VUNPCKHPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vunpckhpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 15 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VUNPCKHPD ymm1, ymmV, ymm2/m256","VUNPCKHPD ymm2/m256, ymmV, ymm1","vunpckhpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 15 /r","V","V","AVX","","w,r,r","","" +"VUNPCKHPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VUNPCKHPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vunpckhpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 15 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VUNPCKHPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VUNPCKHPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vunpckhpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 15 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VUNPCKHPS xmm1, xmmV, xmm2/m128","VUNPCKHPS xmm2/m128, xmmV, xmm1","vunpckhps xmm2/m128, xmmV, xmm1","VEX.NDS.128.0F.WIG 15 /r","V","V","AVX","","w,r,r","","" +"VUNPCKHPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VUNPCKHPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vunpckhps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.0F.W0 15 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VUNPCKHPS ymm1, ymmV, ymm2/m256","VUNPCKHPS ymm2/m256, ymmV, ymm1","vunpckhps ymm2/m256, ymmV, ymm1","VEX.NDS.256.0F.WIG 15 /r","V","V","AVX","","w,r,r","","" +"VUNPCKHPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VUNPCKHPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vunpckhps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.0F.W0 15 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VUNPCKHPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VUNPCKHPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vunpckhps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.0F.W0 15 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VUNPCKLPD xmm1, xmmV, xmm2/m128","VUNPCKLPD xmm2/m128, xmmV, xmm1","vunpcklpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 14 /r","V","V","AVX","","w,r,r","","" +"VUNPCKLPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VUNPCKLPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vunpcklpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 14 /r","V","V","AVX512F+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VUNPCKLPD ymm1, ymmV, ymm2/m256","VUNPCKLPD ymm2/m256, ymmV, ymm1","vunpcklpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 14 /r","V","V","AVX","","w,r,r","","" +"VUNPCKLPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VUNPCKLPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vunpcklpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 14 /r","V","V","AVX512F+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VUNPCKLPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VUNPCKLPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vunpcklpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 14 /r","V","V","AVX512F","bscale8,scale64","w,r,r,r","","" +"VUNPCKLPS xmm1, xmmV, xmm2/m128","VUNPCKLPS xmm2/m128, xmmV, xmm1","vunpcklps xmm2/m128, xmmV, xmm1","VEX.NDS.128.0F.WIG 14 /r","V","V","AVX","","w,r,r","","" +"VUNPCKLPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VUNPCKLPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vunpcklps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.0F.W0 14 /r","V","V","AVX512F+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VUNPCKLPS ymm1, ymmV, ymm2/m256","VUNPCKLPS ymm2/m256, ymmV, ymm1","vunpcklps ymm2/m256, ymmV, ymm1","VEX.NDS.256.0F.WIG 14 /r","V","V","AVX","","w,r,r","","" +"VUNPCKLPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VUNPCKLPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vunpcklps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.0F.W0 14 /r","V","V","AVX512F+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VUNPCKLPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VUNPCKLPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vunpcklps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.0F.W0 14 /r","V","V","AVX512F","bscale4,scale64","w,r,r,r","","" +"VXORPD xmm1, xmmV, xmm2/m128","VXORPD xmm2/m128, xmmV, xmm1","vxorpd xmm2/m128, xmmV, xmm1","VEX.NDS.128.66.0F.WIG 57 /r","V","V","AVX","","w,r,r","","" +"VXORPD xmm1, {k}{z}, xmmV, xmm2/m128/m64bcst","VXORPD xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","vxorpd xmm2/m128/m64bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.66.0F.W1 57 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale16","w,r,r,r","","" +"VXORPD ymm1, ymmV, ymm2/m256","VXORPD ymm2/m256, ymmV, ymm1","vxorpd ymm2/m256, ymmV, ymm1","VEX.NDS.256.66.0F.WIG 57 /r","V","V","AVX","","w,r,r","","" +"VXORPD ymm1, {k}{z}, ymmV, ymm2/m256/m64bcst","VXORPD ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","vxorpd ymm2/m256/m64bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.66.0F.W1 57 /r","V","V","AVX512DQ+AVX512VL","bscale8,scale32","w,r,r,r","","" +"VXORPD zmm1, {k}{z}, zmmV, zmm2/m512/m64bcst","VXORPD zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","vxorpd zmm2/m512/m64bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.66.0F.W1 57 /r","V","V","AVX512DQ","bscale8,scale64","w,r,r,r","","" +"VXORPS xmm1, xmmV, xmm2/m128","VXORPS xmm2/m128, xmmV, xmm1","vxorps xmm2/m128, xmmV, xmm1","VEX.NDS.128.0F.WIG 57 /r","V","V","AVX","","w,r,r","","" +"VXORPS xmm1, {k}{z}, xmmV, xmm2/m128/m32bcst","VXORPS xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","vxorps xmm2/m128/m32bcst, xmmV, {k}{z}, xmm1","EVEX.NDS.128.0F.W0 57 /r","V","V","AVX512DQ+AVX512VL","bscale4,scale16","w,r,r,r","","" +"VXORPS ymm1, ymmV, ymm2/m256","VXORPS ymm2/m256, ymmV, ymm1","vxorps ymm2/m256, ymmV, ymm1","VEX.NDS.256.0F.WIG 57 /r","V","V","AVX","","w,r,r","","" +"VXORPS ymm1, {k}{z}, ymmV, ymm2/m256/m32bcst","VXORPS ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","vxorps ymm2/m256/m32bcst, ymmV, {k}{z}, ymm1","EVEX.NDS.256.0F.W0 57 /r","V","V","AVX512DQ+AVX512VL","bscale4,scale32","w,r,r,r","","" +"VXORPS zmm1, {k}{z}, zmmV, zmm2/m512/m32bcst","VXORPS zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","vxorps zmm2/m512/m32bcst, zmmV, {k}{z}, zmm1","EVEX.NDS.512.0F.W0 57 /r","V","V","AVX512DQ","bscale4,scale64","w,r,r,r","","" +"VZEROALL","VZEROALL","vzeroall","VEX.256.0F.WIG 77","V","V","AVX","","","","" +"VZEROUPPER","VZEROUPPER","vzeroupper","VEX.128.0F.WIG 77","V","V","AVX","","","","" +"WAIT","WAIT","wait","9B","V","V","","pseudo","","","" +"WBINVD","WBINVD","wbinvd","0F 09","V","V","486","","","","" +"WRFSBASE rmr32","WRFSBASE rmr32","wrfsbase rmr32","F3 0F AE /2","N.S.","V","FSGSBASE","modrm_regonly,operand16,operand32","r","Y","32" +"WRFSBASE rmr64","WRFSBASE rmr64","wrfsbase rmr64","F3 REX.W 0F AE /2","N.S.","V","FSGSBASE","modrm_regonly","r","Y","64" +"WRGSBASE rmr32","WRGSBASE rmr32","wrgsbase rmr32","F3 0F AE /3","N.S.","V","FSGSBASE","modrm_regonly,operand16,operand32","r","Y","32" +"WRGSBASE rmr64","WRGSBASE rmr64","wrgsbase rmr64","F3 REX.W 0F AE /3","N.S.","V","FSGSBASE","modrm_regonly","r","Y","64" +"WRMSR","WRMSR","wrmsr","0F 30","V","V","Pentium","","","","" +"WRPKRU","WRPKRU","wrpkru","0F 01 EF","V","V","PKU","","","","" +"WRSSD m32, r32","WRSSD r32, m32","wrssd r32, m32","0F 38 F6 /r","V","V","CET","modrm_memonly,operand16,operand32","w,r","","" +"WRSSQ m64, r64","WRSSQ r64, m64","wrssq r64, m64","REX.W 0F 38 F6 /r","N.S.","V","CET","modrm_memonly","w,r","","" +"WRUSSD m32, r32","WRUSSD r32, m32","wrussd r32, m32","66 0F 38 F5 /r","V","V","CET","modrm_memonly,operand16,operand32","w,r","","" +"WRUSSQ m64, r64","WRUSSQ r64, m64","wrussq r64, m64","66 REX.W 0F 38 F5 /r","N.S.","V","CET","modrm_memonly","w,r","","" +"XABORT imm8u","XABORT imm8u","xabort imm8u","C6 F8 ib","V","V","RTM","modrm_regonly","r","","" +"XACQUIRE","XACQUIRE","xacquire","F2","V","V","HLE","pseudo","","","" +"XADD r/m8, r8","XADDB r8, r/m8","xaddb r8, r/m8","0F C0 /r","V","V","486","","rw,rw","Y","8" +"XADD r/m8, r8","XADDB r8, r/m8","xaddb r8, r/m8","REX 0F C0 /r","N.E.","V","","pseudo64","rw,w","Y","8" +"XADD r/m32, r32","XADDL r32, r/m32","xaddl r32, r/m32","0F C1 /r","V","V","486","operand32","rw,rw","Y","32" +"XADD r/m64, r64","XADDQ r64, r/m64","xaddq r64, r/m64","REX.W 0F C1 /r","N.S.","V","486","","rw,rw","Y","64" +"XADD r/m16, r16","XADDW r16, r/m16","xaddw r16, r/m16","0F C1 /r","V","V","486","operand16","rw,rw","Y","16" +"XBEGIN rel16","XBEGIN rel16","xbegin rel16","C7 F8 cw","V","V","RTM","modrm_regonly,operand16","r","","" +"XBEGIN rel32","XBEGIN rel32","xbegin rel32","C7 F8 cd","V","V","RTM","modrm_regonly,operand32,operand64","r","","" +"XCHG r8, r/m8","XCHGB r/m8, r8","xchgb r/m8, r8","86 /r","V","V","","pseudo","w,r","Y","8" +"XCHG r8, r/m8","XCHGB r/m8, r8","xchgb r/m8, r8","REX 86 /r","N.E.","V","","pseudo","w,r","Y","8" +"XCHG r/m8, r8","XCHGB r8, r/m8","xchgb r8, r/m8","86 /r","V","V","","","rw,rw","Y","8" +"XCHG r/m8, r8","XCHGB r8, r/m8","xchgb r8, r/m8","REX 86 /r","N.E.","V","","pseudo64","rw,r","Y","8" +"XCHG r32op, EAX","XCHGL EAX, r32op","xchgl EAX, r32op","90+rd","V","V","","operand32","rw,rw","Y","32" +"XCHG r32, r/m32","XCHGL r/m32, r32","xchgl r/m32, r32","87 /r","V","V","","operand32,pseudo","w,r","Y","32" +"XCHG r/m32, r32","XCHGL r32, r/m32","xchgl r32, r/m32","87 /r","V","V","","operand32","rw,rw","Y","32" +"XCHG EAX, r32op","XCHGL r32op, EAX","xchgl r32op, EAX","90+rd","V","V","","operand32,pseudo","rw,rw","Y","32" +"XCHG r64op, RAX","XCHGQ RAX, r64op","xchgq RAX, r64op","REX.W 90+ro","N.S.","V","","","rw,rw","Y","64" +"XCHG r64, r/m64","XCHGQ r/m64, r64","xchgq r/m64, r64","REX.W 87 /r","N.E.","V","","pseudo","w,r","Y","64" +"XCHG r/m64, r64","XCHGQ r64, r/m64","xchgq r64, r/m64","REX.W 87 /r","N.S.","V","","","rw,rw","Y","64" +"XCHG RAX, r64op","XCHGQ r64op, RAX","xchgq r64op, RAX","REX.W 90+rd","N.E.","V","","pseudo","rw,rw","Y","64" +"XCHG r16op, AX","XCHGW AX, r16op","xchgw AX, r16op","90+rw","V","V","","operand16","rw,rw","Y","16" +"XCHG r16, r/m16","XCHGW r/m16, r16","xchgw r/m16, r16","87 /r","V","V","","operand16,pseudo","w,r","Y","16" +"XCHG r/m16, r16","XCHGW r16, r/m16","xchgw r16, r/m16","87 /r","V","V","","operand16","rw,rw","Y","16" +"XCHG AX, r16op","XCHGW r16op, AX","xchgw r16op, AX","90+rw","V","V","","operand16,pseudo","rw,rw","Y","16" +"XEND","XEND","xend","0F 01 D5","V","V","RTM","","","","" +"XGETBV","XGETBV","xgetbv","0F 01 D0","V","V","XSAVE","","","","" +"XLATB","XLAT","xlat","D7","V","V","","","","","" +"XLATB","XLAT","xlat","REX.W D7","N.E.","V","","pseudo","","","" +"XOR r/m8, imm8","XORB imm8, r/m8","xorb imm8, r/m8","REX 80 /6 ib","N.E.","V","","pseudo64","rw,r","Y","8" +"XOR AL, imm8u","XORB imm8u, AL","xorb imm8u, AL","34 ib","V","V","","","rw,r","Y","8" +"XOR r/m8, imm8u","XORB imm8u, r/m8","xorb imm8u, r/m8","80 /6 ib","V","V","","","rw,r","Y","8" +"XOR r/m8, imm8u","XORB imm8u, r/m8","xorb imm8u, r/m8","82 /6 ib","V","N.S.","","","rw,r","Y","8" +"XOR r8, r/m8","XORB r/m8, r8","xorb r/m8, r8","32 /r","V","V","","","rw,r","Y","8" +"XOR r8, r/m8","XORB r/m8, r8","xorb r/m8, r8","REX 32 /r","N.E.","V","","pseudo64","rw,r","Y","8" +"XOR r/m8, r8","XORB r8, r/m8","xorb r8, r/m8","30 /r","V","V","","","rw,r","Y","8" +"XOR r/m8, r8","XORB r8, r/m8","xorb r8, r/m8","REX 30 /r","N.E.","V","","pseudo64","rw,r","Y","8" +"XOR EAX, imm32","XORL imm32, EAX","xorl imm32, EAX","35 id","V","V","","operand32","rw,r","Y","32" +"XOR r/m32, imm32","XORL imm32, r/m32","xorl imm32, r/m32","81 /6 id","V","V","","operand32","rw,r","Y","32" +"XOR r/m32, imm8","XORL imm8, r/m32","xorl imm8, r/m32","83 /6 ib","V","V","","operand32","rw,r","Y","32" +"XOR r32, r/m32","XORL r/m32, r32","xorl r/m32, r32","33 /r","V","V","","operand32","rw,r","Y","32" +"XOR r/m32, r32","XORL r32, r/m32","xorl r32, r/m32","31 /r","V","V","","operand32","rw,r","Y","32" +"XORPD xmm1, xmm2/m128","XORPD xmm2/m128, xmm1","xorpd xmm2/m128, xmm1","66 0F 57 /r","V","V","SSE2","","rw,r","","" +"XORPS xmm1, xmm2/m128","XORPS xmm2/m128, xmm1","xorps xmm2/m128, xmm1","0F 57 /r","V","V","SSE","","rw,r","","" +"XOR RAX, imm32","XORQ imm32, RAX","xorq imm32, RAX","REX.W 35 id","N.S.","V","","","rw,r","Y","64" +"XOR r/m64, imm32","XORQ imm32, r/m64","xorq imm32, r/m64","REX.W 81 /6 id","N.S.","V","","","rw,r","Y","64" +"XOR r/m64, imm8","XORQ imm8, r/m64","xorq imm8, r/m64","REX.W 83 /6 ib","N.S.","V","","","rw,r","Y","64" +"XOR r64, r/m64","XORQ r/m64, r64","xorq r/m64, r64","REX.W 33 /r","N.S.","V","","","rw,r","Y","64" +"XOR r/m64, r64","XORQ r64, r/m64","xorq r64, r/m64","REX.W 31 /r","N.S.","V","","","rw,r","Y","64" +"XOR AX, imm16","XORW imm16, AX","xorw imm16, AX","35 iw","V","V","","operand16","rw,r","Y","16" +"XOR r/m16, imm16","XORW imm16, r/m16","xorw imm16, r/m16","81 /6 iw","V","V","","operand16","rw,r","Y","16" +"XOR r/m16, imm8","XORW imm8, r/m16","xorw imm8, r/m16","83 /6 ib","V","V","","operand16","rw,r","Y","16" +"XOR r16, r/m16","XORW r/m16, r16","xorw r/m16, r16","33 /r","V","V","","operand16","rw,r","Y","16" +"XOR r/m16, r16","XORW r16, r/m16","xorw r16, r/m16","31 /r","V","V","","operand16","rw,r","Y","16" +"XRELEASE","XRELEASE","xrelease","F3","V","V","HLE","pseudo","","","" +"XRSTOR mem","XRSTOR mem","xrstor mem","0F AE /5","V","V","XSAVE","modrm_memonly,operand16,operand32","r","","" +"XRSTOR64 mem","XRSTOR64 mem","xrstor64 mem","REX.W 0F AE /5","N.S.","V","XSAVE","modrm_memonly","r","","" +"XRSTORS mem","XRSTORS mem","xrstors mem","0F C7 /3","V","V","XSAVES","modrm_memonly,operand16,operand32","r","","" +"XRSTORS64 mem","XRSTORS64 mem","xrstors64 mem","REX.W 0F C7 /3","N.S.","V","XSAVES","modrm_memonly","r","","" +"XSAVE mem","XSAVE mem","xsave mem","0F AE /4","V","V","XSAVE","modrm_memonly,operand16,operand32","w","","" +"XSAVE64 mem","XSAVE64 mem","xsave64 mem","REX.W 0F AE /4","N.S.","V","XSAVE","modrm_memonly","w","","" +"XSAVEC mem","XSAVEC mem","xsavec mem","0F C7 /4","V","V","XSAVEC","modrm_memonly,operand16,operand32","w","","" +"XSAVEC64 mem","XSAVEC64 mem","xsavec64 mem","REX.W 0F C7 /4","N.S.","V","XSAVEC","modrm_memonly","w","","" +"XSAVEOPT mem","XSAVEOPT mem","xsaveopt mem","0F AE /6","V","V","XSAVEOPT","modrm_memonly,operand16,operand32","w","","" +"XSAVEOPT64 mem","XSAVEOPT64 mem","xsaveopt64 mem","REX.W 0F AE /6","N.S.","V","XSAVEOPT","modrm_memonly","w","","" +"XSAVES mem","XSAVES mem","xsaves mem","0F C7 /5","V","V","XSAVES","modrm_memonly,operand16,operand32","w","","" +"XSAVES64 mem","XSAVES64 mem","xsaves64 mem","REX.W 0F C7 /5","N.S.","V","XSAVES","modrm_memonly","w","","" +"XSETBV","XSETBV","xsetbv","0F 01 D1","V","V","XSAVE","","","","" +"XTEST","XTEST","xtest","0F 01 D6","V","V","HLE or RTM","","","","" diff --git a/tests/tcg/loongarch64/Makefile.target b/tests/tcg/loongarch64/Makefile.target new file mode 100644 index 0000000000..00030a1026 --- /dev/null +++ b/tests/tcg/loongarch64/Makefile.target @@ -0,0 +1,20 @@ +# -*- Mode: makefile -*- +# +# LoongArch64 specific tweaks + +# Loongarch64 doesn't support gdb, so skip the EXTRA_RUNS +EXTRA_RUNS = + +LOONGARCH64_SRC=$(SRC_PATH)/tests/tcg/loongarch64 +VPATH += $(LOONGARCH64_SRC) + +LDFLAGS+=-lm + +LOONGARCH64_TESTS = test_bit +LOONGARCH64_TESTS += test_div +LOONGARCH64_TESTS += test_fclass +LOONGARCH64_TESTS += test_fpcom +LOONGARCH64_TESTS += test_pcadd +LOONGARCH64_TESTS += test_fcsr + +TESTS += $(LOONGARCH64_TESTS) diff --git a/tests/tcg/loongarch64/float_convd.ref b/tests/tcg/loongarch64/float_convd.ref new file mode 100644 index 0000000000..08d3dfa2fe --- /dev/null +++ b/tests/tcg/loongarch64/float_convd.ref @@ -0,0 +1,988 @@ +### Rounding to nearest +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-inf:0xff800000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb600000000000000p+1:0x40490fdb) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.00000000000000000000p+31:0x4f000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(inf:0x7f800000) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding upwards +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000200000000000000p-25:0x33000001) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe800000000000000p-25:0x337ffff4) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801c00000000000000p-15:0x387fc00e) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000e00000000000000p-14:0x38800007) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x1.00000000000000000000p-149:0x00000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0aa00000000000000p+1:0x402df855) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb600000000000000p+1:0x40490fdb) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.00000000000000000000p+31:0x4f000000) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(inf:0x7f800000) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding downwards +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-inf:0xff800000) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x1.00000000000000000000p-149:0x80000001) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb400000000000000p+1:0x40490fda) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.fffffe00000000000000p+30:0x4effffff) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding to zero +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-nan:0x00fff8000000000000) + to single: f32(-nan:0xffc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(-inf:0x00fff0000000000000) + to single: f32(-inf:0xff800000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffffffffff0000000p+1023:0x00ffefffffffffffff) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OVERFLOW INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) + to single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.1874b135ff6540000000p+103:0x00c661874b135ff654) + to single: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.c0bab523323b90000000p+99:0x00c62c0bab523323b9) + to single: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) (INEXACT ) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from double: f64(-0x1.00000000000000000000p+1:0x00c000000000000000) + to single: f32(-0x1.00000000000000000000p+1:0xc0000000) (OK) + to int32: -2 (OK) + to int64: -2 (OK) + to uint32: -2 (OK) + to uint64: -2 (OK) +from double: f64(-0x1.00000000000000000000p+0:0x00bff0000000000000) + to single: f32(-0x1.00000000000000000000p+0:0xbf800000) (OK) + to int32: -1 (OK) + to int64: -1 (OK) + to uint32: -1 (OK) + to uint64: -1 (OK) +from double: f64(-0x1.00000000000000000000p-1022:0x008010000000000000) + to single: f32(-0x0.00000000000000000000p+0:0x80000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) + to single: f32(-0x1.00000000000000000000p-126:0x80800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.00000000000000000000p+0:00000000000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from double: f64(0x1.00000000000000000000p-126:0x003810000000000000) + to single: f32(0x1.00000000000000000000p-126:0x00800000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000001c5f680000000p-25:0x003e600000001c5f68) + to single: f32(0x1.00000000000000000000p-25:0x33000000) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ffffe6cb2fa820000000p-25:0x003e6ffffe6cb2fa82) + to single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.ff801a9af58a10000000p-15:0x003f0ff801a9af58a1) + to single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000c06a1ef50000000p-14:0x003f100000c06a1ef5) + to single: f32(0x1.00000c00000000000000p-14:0x38800006) (INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) + to single: f32(0x1.00400000000000000000p+0:0x3f802000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from double: f64(0x1.00000000000000000000p-1022:0x000010000000000000) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.9ea82a22876800000000p-1022:0x000009ea82a2287680) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x0.ab98fba8432100000000p-1022:0x00000ab98fba843210) + to single: f32(0x0.00000000000000000000p+0:0000000000) (UNDERFLOW INEXACT ) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) + to single: f32(0x1.00000000000000000000p+0:0x3f800000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from double: f64(0x1.00000000000000000000p+1:0x004000000000000000) + to single: f32(0x1.00000000000000000000p+1:0x40000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from double: f64(0x1.5bf0a8b1457690000000p+1:0x004005bf0a8b145769) + to single: f32(0x1.5bf0a800000000000000p+1:0x402df854) (INEXACT ) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from double: f64(0x1.921fb54442d180000000p+1:0x00400921fb54442d18) + to single: f32(0x1.921fb400000000000000p+1:0x40490fda) (INEXACT ) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) + to single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) + to single: f32(0x1.ffc00000000000000000p+15:0x477fe000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) + to single: f32(0x1.ffc20000000000000000p+15:0x477fe100) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) + to single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) + to single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) + to single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from double: f64(0x1.fffffffc000000000000p+30:0x0041dfffffffc00000) + to single: f32(0x1.fffffe00000000000000p+30:0x4effffff) (INEXACT ) + to int32: 2147483647 (OK) + to int64: 2147483647 (OK) + to uint32: 2147483647 (OK) + to uint64: 2147483647 (OK) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(0x1.fffffffffffff0000000p+1023:0x007fefffffffffffff) + to single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) (OVERFLOW INEXACT ) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from double: f64(inf:0x007ff0000000000000) + to single: f32(inf:0x7f800000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from double: f64(nan:0x007ff8000000000000) + to single: f32(nan:0x7fc00000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff0000000000001) + to single: f32(nan:0x7fc00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from double: f64(nan:0x007ff4000000000000) + to single: f32(nan:0x7fe00000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) diff --git a/tests/tcg/loongarch64/float_convs.ref b/tests/tcg/loongarch64/float_convs.ref new file mode 100644 index 0000000000..66c7679dec --- /dev/null +++ b/tests/tcg/loongarch64/float_convs.ref @@ -0,0 +1,748 @@ +### Rounding to nearest +from single: f32(-nan:0xffa00000) + to double: f64(-nan:0x00fffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fa00000) + to double: f64(nan:0x007ffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding upwards +from single: f32(-nan:0xffa00000) + to double: f64(-nan:0x00fffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fa00000) + to double: f64(nan:0x007ffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding downwards +from single: f32(-nan:0xffa00000) + to double: f64(-nan:0x00fffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fa00000) + to double: f64(nan:0x007ffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +### Rounding to zero +from single: f32(-nan:0xffa00000) + to double: f64(-nan:0x00fffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-nan:0xffc00000) + to double: f64(-nan:0x00fff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(-inf:0xff800000) + to double: f64(-inf:0x00fff0000000000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK) + to int32: -2147483648 (INVALID) + to int64: -9223372036854775808 (INVALID) + to uint32: -2147483648 (INVALID) + to uint64: -9223372036854775808 (INVALID) +from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8) + to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22) + to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(-0x1.00000000000000000000p-126:0x80800000) + to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x0.00000000000000000000p+0:0000000000) + to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK) + to int32: 0 (OK) + to int64: 0 (OK) + to uint32: 0 (OK) + to uint64: 0 (OK) +from single: f32(0x1.00000000000000000000p-126:0x00800000) + to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p-25:0x33000000) + to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3) + to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d) + to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000c00000000000000p-14:0x38800006) + to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK) + to int32: 0 (INEXACT ) + to int64: 0 (INEXACT ) + to uint32: 0 (INEXACT ) + to uint64: 0 (INEXACT ) +from single: f32(0x1.00000000000000000000p+0:0x3f800000) + to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK) + to int32: 1 (OK) + to int64: 1 (OK) + to uint32: 1 (OK) + to uint64: 1 (OK) +from single: f32(0x1.00400000000000000000p+0:0x3f802000) + to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK) + to int32: 1 (INEXACT ) + to int64: 1 (INEXACT ) + to uint32: 1 (INEXACT ) + to uint64: 1 (INEXACT ) +from single: f32(0x1.00000000000000000000p+1:0x40000000) + to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK) + to int32: 2 (OK) + to int64: 2 (OK) + to uint32: 2 (OK) + to uint64: 2 (OK) +from single: f32(0x1.5bf0a800000000000000p+1:0x402df854) + to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK) + to int32: 2 (INEXACT ) + to int64: 2 (INEXACT ) + to uint32: 2 (INEXACT ) + to uint64: 2 (INEXACT ) +from single: f32(0x1.921fb600000000000000p+1:0x40490fdb) + to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK) + to int32: 3 (INEXACT ) + to int64: 3 (INEXACT ) + to uint32: 3 (INEXACT ) + to uint64: 3 (INEXACT ) +from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK) + to int32: 65503 (OK) + to int64: 65503 (OK) + to uint32: 65503 (OK) + to uint64: 65503 (OK) +from single: f32(0x1.ffc00000000000000000p+15:0x477fe000) + to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK) + to int32: 65504 (OK) + to int64: 65504 (OK) + to uint32: 65504 (OK) + to uint64: 65504 (OK) +from single: f32(0x1.ffc20000000000000000p+15:0x477fe100) + to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK) + to int32: 65505 (OK) + to int64: 65505 (OK) + to uint32: 65505 (OK) + to uint64: 65505 (OK) +from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK) + to int32: 131007 (OK) + to int64: 131007 (OK) + to uint32: 131007 (OK) + to uint64: 131007 (OK) +from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000) + to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK) + to int32: 131008 (OK) + to int64: 131008 (OK) + to uint32: 131008 (OK) + to uint64: 131008 (OK) +from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080) + to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK) + to int32: 131009 (OK) + to int64: 131009 (OK) + to uint32: 131009 (OK) + to uint64: 131009 (OK) +from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b) + to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INEXACT INVALID) + to uint64: -1 (INEXACT INVALID) +from single: f32(inf:0x7f800000) + to double: f64(inf:0x007ff0000000000000) (OK) + to int32: 2147483647 (INVALID) + to int64: 9223372036854775807 (INVALID) + to uint32: -1 (INVALID) + to uint64: -1 (INVALID) +from single: f32(nan:0x7fc00000) + to double: f64(nan:0x007ff8000000000000) (OK) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) +from single: f32(nan:0x7fa00000) + to double: f64(nan:0x007ffc000000000000) (INVALID) + to int32: 0 (INVALID) + to int64: 0 (INVALID) + to uint32: 0 (INVALID) + to uint64: 0 (INVALID) diff --git a/tests/tcg/loongarch64/float_madds.ref b/tests/tcg/loongarch64/float_madds.ref new file mode 100644 index 0000000000..21c0539887 --- /dev/null +++ b/tests/tcg/loongarch64/float_madds.ref @@ -0,0 +1,768 @@ +### Rounding to nearest +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffe00000) flags=INVALID (0/0) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/1) +op : f32(-inf:0xff800000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/2) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(-nan:0xffc00000) flags=OK (1/0) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-nan:0xffc00000) +res: f32(-nan:0xffc00000) flags=OK (1/1) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffc00000) flags=OK (1/2) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(inf:0x7f800000) flags=OK (2/0) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-inf:0xff800000) +res: f32(-inf:0xff800000) flags=OK (2/1) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(inf:0x7f800000) flags=OK (2/2) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/0) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/1) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/2) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (4/0) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) flags=INEXACT (4/1) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) flags=INEXACT (4/2) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(0x1.0c27fa00000000000000p+60:0x5d8613fd) flags=INEXACT (5/0) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) flags=INEXACT (5/1) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(0x1.26c46200000000000000p+34:0x50936231) flags=INEXACT (5/2) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(0x1.91f94000000000000000p-106:0x0ac8fca0) flags=INEXACT (6/0) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(-0x1.31f75000000000000000p-40:0xab98fba8) flags=INEXACT (6/1) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=INEXACT (6/2) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (7/0) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (8/0) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (8/1) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(-0x0.00000000000000000000p+0:0x80000000) flags=UNDERFLOW INEXACT (8/2) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000000000000000000p-25:0x33000000) flags=OK (9/0) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (9/1) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (9/2) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.ffffe600000000000000p-25:0x337ffff3) flags=INEXACT (10/0) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.ffffe600000000000000p-50:0x26fffff3) flags=INEXACT (10/1) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000000000000000000p-25:0x33000000) flags=INEXACT (10/2) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (11/0) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.0007fe00000000000000p-25:0x330003ff) flags=INEXACT (11/1) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0001f200000000000000p-24:0x338000f9) flags=INEXACT (11/2) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00000c00000000000000p-14:0x38800006) flags=INEXACT (12/0) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0ffbf400000000000000p-24:0x3387fdfa) flags=INEXACT (12/1) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801c00000000000000p-15:0x387fc00e) flags=INEXACT (12/2) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00000000000000000000p+0:0x3f800000) flags=INEXACT (13/0) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/1) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/2) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/0) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/1) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00040200000000000000p+0:0x3f800201) flags=INEXACT (14/2) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.2e23d200000000000000p+2:0x409711e9) flags=INEXACT (16/2) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.12804200000000000000p+3:0x41094021) flags=INEXACT (17/0) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.51458000000000000000p+3:0x4128a2c0) flags=INEXACT (17/1) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.200c0400000000000000p+3:0x41100602) flags=INEXACT (17/2) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ffcf1400000000000000p+15:0x477fe78a) flags=INEXACT (18/0) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.91ed3c00000000000000p+17:0x4848f69e) flags=INEXACT (18/1) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.5bc56000000000000000p+17:0x482de2b0) flags=INEXACT (18/2) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.08edf000000000000000p+18:0x488476f8) flags=INEXACT (19/0) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.ff7e0800000000000000p+31:0x4f7fbf04) flags=INEXACT (19/1) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.08ee7a00000000000000p+18:0x4884773d) flags=INEXACT (19/2) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800800000000000000p+31:0x4f7fc004) flags=INEXACT (20/0) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ff840800000000000000p+31:0x4f7fc204) flags=INEXACT (20/1) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820800000000000000p+31:0x4f7fc104) flags=INEXACT (20/2) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff860800000000000000p+31:0x4f7fc304) flags=INEXACT (21/0) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820800000000000000p+32:0x4fffc104) flags=INEXACT (21/1) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800800000000000000p+32:0x4fffc004) flags=INEXACT (21/2) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff830800000000000000p+32:0x4fffc184) flags=INEXACT (22/0) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff7f8800000000000000p+33:0x507fbfc4) flags=INEXACT (22/1) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff840800000000000000p+32:0x4fffc204) flags=INEXACT (22/2) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.ff800800000000000000p+33:0x507fc004) flags=INEXACT (23/0) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff820800000000000000p+33:0x507fc104) flags=INEXACT (23/1) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff810800000000000000p+33:0x507fc084) flags=INEXACT (23/2) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(0x1.c0bab600000000000000p+99:0x71605d5b) flags=INEXACT (24/0) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.c0838000000000000000p+116:0x79e041c0) flags=INEXACT (24/1) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.c0829e00000000000000p+116:0x79e0414f) flags=INEXACT (24/2) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/0) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/1) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/2) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(inf:0x7f800000) flags=OK (26/0) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(inf:0x7f800000) flags=OK (26/1) +op : f32(inf:0x7f800000) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(inf:0x7f800000) flags=OK (26/2) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fc00000) flags=OK (27/0) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(nan:0x7fc00000) flags=OK (27/1) +op : f32(nan:0x7fc00000) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(nan:0x7fc00000) flags=OK (27/2) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/0) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(inf:0x7f800000) +res: f32(nan:0x7fe00000) flags=INVALID (28/1) +op : f32(nan:0x7fa00000) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/2) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (29/0) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/1) +op : f32(-nan:0xffa00000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/2) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/0) +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/1) +op : f32(-nan:0xffc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (30/2) +# LP184149 +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-1:0x3f000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=OK (31/0) +op : f32(0x1.00000000000000000000p-149:0x00000001) * f32(0x1.00000000000000000000p-149:0x00000001) + f32(0x1.00000000000000000000p-149:0x00000001) +res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (32/0) +### Rounding upwards +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffe00000) flags=INVALID (0/0) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/1) +op : f32(-inf:0xff800000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/2) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(-nan:0xffc00000) flags=OK (1/0) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-nan:0xffc00000) +res: f32(-nan:0xffc00000) flags=OK (1/1) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffc00000) flags=OK (1/2) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(inf:0x7f800000) flags=OK (2/0) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-inf:0xff800000) +res: f32(-inf:0xff800000) flags=OK (2/1) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(inf:0x7f800000) flags=OK (2/2) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/0) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/1) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/2) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (4/0) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) flags=INEXACT (4/1) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) flags=INEXACT (4/2) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(0x1.0c27fa00000000000000p+60:0x5d8613fd) flags=INEXACT (5/0) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) flags=INEXACT (5/1) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(0x1.26c46200000000000000p+34:0x50936231) flags=INEXACT (5/2) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(0x1.91f94000000000000000p-106:0x0ac8fca0) flags=INEXACT (6/0) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(-0x1.31f74e00000000000000p-40:0xab98fba7) flags=INEXACT (6/1) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544200000000000000p-66:0x9ea82a21) flags=INEXACT (6/2) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (7/0) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (8/0) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (8/1) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(-0x0.00000000000000000000p+0:0x80000000) flags=UNDERFLOW INEXACT (8/2) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000000000000000000p-25:0x33000000) flags=OK (9/0) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (9/1) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (9/2) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.ffffe800000000000000p-25:0x337ffff4) flags=INEXACT (10/0) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.ffffe800000000000000p-50:0x26fffff4) flags=INEXACT (10/1) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000200000000000000p-25:0x33000001) flags=INEXACT (10/2) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801c00000000000000p-15:0x387fc00e) flags=INEXACT (11/0) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00080000000000000000p-25:0x33000400) flags=INEXACT (11/1) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0001f400000000000000p-24:0x338000fa) flags=INEXACT (11/2) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00000e00000000000000p-14:0x38800007) flags=INEXACT (12/0) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0ffbf600000000000000p-24:0x3387fdfb) flags=INEXACT (12/1) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801c00000000000000p-15:0x387fc00e) flags=INEXACT (12/2) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00000200000000000000p+0:0x3f800001) flags=INEXACT (13/0) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ffc01a00000000000000p-14:0x38ffe00d) flags=INEXACT (13/1) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.ffc01a00000000000000p-14:0x38ffe00d) flags=INEXACT (13/2) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.00440200000000000000p+0:0x3f802201) flags=INEXACT (14/0) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00440200000000000000p+0:0x3f802201) flags=INEXACT (14/1) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00040200000000000000p+0:0x3f800201) flags=INEXACT (14/2) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.2e23d400000000000000p+2:0x409711ea) flags=INEXACT (16/2) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.12804200000000000000p+3:0x41094021) flags=INEXACT (17/0) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.51458200000000000000p+3:0x4128a2c1) flags=INEXACT (17/1) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.200c0600000000000000p+3:0x41100603) flags=INEXACT (17/2) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ffcf1600000000000000p+15:0x477fe78b) flags=INEXACT (18/0) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.91ed3c00000000000000p+17:0x4848f69e) flags=INEXACT (18/1) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.5bc56200000000000000p+17:0x482de2b1) flags=INEXACT (18/2) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.08edf000000000000000p+18:0x488476f8) flags=INEXACT (19/0) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.ff7e0a00000000000000p+31:0x4f7fbf05) flags=INEXACT (19/1) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.08ee7a00000000000000p+18:0x4884773d) flags=INEXACT (19/2) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800a00000000000000p+31:0x4f7fc005) flags=INEXACT (20/0) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ff840800000000000000p+31:0x4f7fc204) flags=INEXACT (20/1) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820800000000000000p+31:0x4f7fc104) flags=INEXACT (20/2) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff860800000000000000p+31:0x4f7fc304) flags=INEXACT (21/0) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820800000000000000p+32:0x4fffc104) flags=INEXACT (21/1) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800a00000000000000p+32:0x4fffc005) flags=INEXACT (21/2) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff830800000000000000p+32:0x4fffc184) flags=INEXACT (22/0) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff7f8a00000000000000p+33:0x507fbfc5) flags=INEXACT (22/1) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff840800000000000000p+32:0x4fffc204) flags=INEXACT (22/2) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.ff800a00000000000000p+33:0x507fc005) flags=INEXACT (23/0) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff820800000000000000p+33:0x507fc104) flags=INEXACT (23/1) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff810800000000000000p+33:0x507fc084) flags=INEXACT (23/2) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(0x1.c0bab800000000000000p+99:0x71605d5c) flags=INEXACT (24/0) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.c0838000000000000000p+116:0x79e041c0) flags=INEXACT (24/1) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.c082a000000000000000p+116:0x79e04150) flags=INEXACT (24/2) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/0) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/1) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/2) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(inf:0x7f800000) flags=OK (26/0) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(inf:0x7f800000) flags=OK (26/1) +op : f32(inf:0x7f800000) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(inf:0x7f800000) flags=OK (26/2) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fc00000) flags=OK (27/0) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(nan:0x7fc00000) flags=OK (27/1) +op : f32(nan:0x7fc00000) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(nan:0x7fc00000) flags=OK (27/2) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/0) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(inf:0x7f800000) +res: f32(nan:0x7fe00000) flags=INVALID (28/1) +op : f32(nan:0x7fa00000) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/2) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (29/0) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/1) +op : f32(-nan:0xffa00000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/2) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/0) +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/1) +op : f32(-nan:0xffc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (30/2) +# LP184149 +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-1:0x3f000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=OK (31/0) +op : f32(0x1.00000000000000000000p-149:0x00000001) * f32(0x1.00000000000000000000p-149:0x00000001) + f32(0x1.00000000000000000000p-149:0x00000001) +res: f32(0x1.00000000000000000000p-148:0x00000002) flags=UNDERFLOW INEXACT (32/0) +### Rounding downwards +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffe00000) flags=INVALID (0/0) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/1) +op : f32(-inf:0xff800000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/2) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(-nan:0xffc00000) flags=OK (1/0) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-nan:0xffc00000) +res: f32(-nan:0xffc00000) flags=OK (1/1) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffc00000) flags=OK (1/2) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(inf:0x7f800000) flags=OK (2/0) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-inf:0xff800000) +res: f32(-inf:0xff800000) flags=OK (2/1) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(inf:0x7f800000) flags=OK (2/2) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/0) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/1) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/2) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (4/0) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) flags=INEXACT (4/1) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) flags=INEXACT (4/2) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(0x1.0c27f800000000000000p+60:0x5d8613fc) flags=INEXACT (5/0) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) flags=INEXACT (5/1) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(0x1.26c46000000000000000p+34:0x50936230) flags=INEXACT (5/2) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(0x1.91f93e00000000000000p-106:0x0ac8fc9f) flags=INEXACT (6/0) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(-0x1.31f75000000000000000p-40:0xab98fba8) flags=INEXACT (6/1) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=INEXACT (6/2) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (7/0) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (8/0) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (8/1) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(-0x1.00000000000000000000p-149:0x80000001) flags=UNDERFLOW INEXACT (8/2) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000000000000000000p-25:0x33000000) flags=OK (9/0) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (9/1) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (9/2) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.ffffe600000000000000p-25:0x337ffff3) flags=INEXACT (10/0) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.ffffe600000000000000p-50:0x26fffff3) flags=INEXACT (10/1) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000000000000000000p-25:0x33000000) flags=INEXACT (10/2) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (11/0) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.0007fe00000000000000p-25:0x330003ff) flags=INEXACT (11/1) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0001f200000000000000p-24:0x338000f9) flags=INEXACT (11/2) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00000c00000000000000p-14:0x38800006) flags=INEXACT (12/0) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0ffbf400000000000000p-24:0x3387fdfa) flags=INEXACT (12/1) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (12/2) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00000000000000000000p+0:0x3f800000) flags=INEXACT (13/0) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/1) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/2) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/0) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/1) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00040000000000000000p+0:0x3f800200) flags=INEXACT (14/2) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.2e23d200000000000000p+2:0x409711e9) flags=INEXACT (16/2) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.12804000000000000000p+3:0x41094020) flags=INEXACT (17/0) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.51458000000000000000p+3:0x4128a2c0) flags=INEXACT (17/1) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.200c0400000000000000p+3:0x41100602) flags=INEXACT (17/2) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ffcf1400000000000000p+15:0x477fe78a) flags=INEXACT (18/0) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.91ed3a00000000000000p+17:0x4848f69d) flags=INEXACT (18/1) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.5bc56000000000000000p+17:0x482de2b0) flags=INEXACT (18/2) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.08edee00000000000000p+18:0x488476f7) flags=INEXACT (19/0) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.ff7e0800000000000000p+31:0x4f7fbf04) flags=INEXACT (19/1) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.08ee7800000000000000p+18:0x4884773c) flags=INEXACT (19/2) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800800000000000000p+31:0x4f7fc004) flags=INEXACT (20/0) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ff840600000000000000p+31:0x4f7fc203) flags=INEXACT (20/1) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820600000000000000p+31:0x4f7fc103) flags=INEXACT (20/2) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff860600000000000000p+31:0x4f7fc303) flags=INEXACT (21/0) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820600000000000000p+32:0x4fffc103) flags=INEXACT (21/1) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800800000000000000p+32:0x4fffc004) flags=INEXACT (21/2) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff830600000000000000p+32:0x4fffc183) flags=INEXACT (22/0) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff7f8800000000000000p+33:0x507fbfc4) flags=INEXACT (22/1) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff840600000000000000p+32:0x4fffc203) flags=INEXACT (22/2) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.ff800800000000000000p+33:0x507fc004) flags=INEXACT (23/0) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff820600000000000000p+33:0x507fc103) flags=INEXACT (23/1) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff810600000000000000p+33:0x507fc083) flags=INEXACT (23/2) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(0x1.c0bab600000000000000p+99:0x71605d5b) flags=INEXACT (24/0) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.c0837e00000000000000p+116:0x79e041bf) flags=INEXACT (24/1) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.c0829e00000000000000p+116:0x79e0414f) flags=INEXACT (24/2) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/0) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/1) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/2) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(inf:0x7f800000) flags=OK (26/0) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(inf:0x7f800000) flags=OK (26/1) +op : f32(inf:0x7f800000) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(inf:0x7f800000) flags=OK (26/2) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fc00000) flags=OK (27/0) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(nan:0x7fc00000) flags=OK (27/1) +op : f32(nan:0x7fc00000) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(nan:0x7fc00000) flags=OK (27/2) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/0) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(inf:0x7f800000) +res: f32(nan:0x7fe00000) flags=INVALID (28/1) +op : f32(nan:0x7fa00000) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/2) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (29/0) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/1) +op : f32(-nan:0xffa00000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/2) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/0) +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/1) +op : f32(-nan:0xffc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (30/2) +# LP184149 +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-1:0x3f000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=OK (31/0) +op : f32(0x1.00000000000000000000p-149:0x00000001) * f32(0x1.00000000000000000000p-149:0x00000001) + f32(0x1.00000000000000000000p-149:0x00000001) +res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (32/0) +### Rounding to zero +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffe00000) flags=INVALID (0/0) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/1) +op : f32(-inf:0xff800000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(-nan:0xffe00000) flags=INVALID (0/2) +op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(-nan:0xffc00000) flags=OK (1/0) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-nan:0xffc00000) +res: f32(-nan:0xffc00000) flags=OK (1/1) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-nan:0xffc00000) + f32(-inf:0xff800000) +res: f32(-nan:0xffc00000) flags=OK (1/2) +op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(inf:0x7f800000) flags=OK (2/0) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-inf:0xff800000) +res: f32(-inf:0xff800000) flags=OK (2/1) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(inf:0x7f800000) flags=OK (2/2) +op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/0) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/1) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/2) +op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (4/0) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59) +res: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) flags=INEXACT (4/1) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) flags=INEXACT (4/2) +op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(0x1.0c27f800000000000000p+60:0x5d8613fc) flags=INEXACT (5/0) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) +res: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) flags=INEXACT (5/1) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(0x1.26c46000000000000000p+34:0x50936230) flags=INEXACT (5/2) +op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(0x1.91f93e00000000000000p-106:0x0ac8fc9f) flags=INEXACT (6/0) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(-0x1.31f75000000000000000p-40:0xab98fba8) +res: f32(-0x1.31f74e00000000000000p-40:0xab98fba7) flags=INEXACT (6/1) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544200000000000000p-66:0x9ea82a21) flags=INEXACT (6/2) +op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (7/0) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22) +res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2) +op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (8/0) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(-0x1.00000000000000000000p-126:0x80800000) +res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (8/1) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(-0x0.00000000000000000000p+0:0x80000000) flags=UNDERFLOW INEXACT (8/2) +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000000000000000000p-25:0x33000000) flags=OK (9/0) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (9/1) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (9/2) +op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.ffffe600000000000000p-25:0x337ffff3) flags=INEXACT (10/0) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.00000000000000000000p-126:0x00800000) +res: f32(0x1.ffffe600000000000000p-50:0x26fffff3) flags=INEXACT (10/1) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.00000000000000000000p-25:0x33000000) flags=INEXACT (10/2) +op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (11/0) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000000000000000000p-25:0x33000000) +res: f32(0x1.0007fe00000000000000p-25:0x330003ff) flags=INEXACT (11/1) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0001f200000000000000p-24:0x338000f9) flags=INEXACT (11/2) +op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00000c00000000000000p-14:0x38800006) flags=INEXACT (12/0) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.ffffe600000000000000p-25:0x337ffff3) +res: f32(0x1.0ffbf400000000000000p-24:0x3387fdfa) flags=INEXACT (12/1) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (12/2) +op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00000000000000000000p+0:0x3f800000) flags=INEXACT (13/0) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.ff801a00000000000000p-15:0x387fc00d) +res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/1) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/2) +op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/0) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000c00000000000000p-14:0x38800006) +res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/1) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.00040000000000000000p+0:0x3f800200) flags=INEXACT (14/2) +op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000) +res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2) +op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000) +res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.2e23d200000000000000p+2:0x409711e9) flags=INEXACT (16/2) +op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.12804000000000000000p+3:0x41094020) flags=INEXACT (17/0) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.00000000000000000000p+1:0x40000000) +res: f32(0x1.51458000000000000000p+3:0x4128a2c0) flags=INEXACT (17/1) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.200c0400000000000000p+3:0x41100602) flags=INEXACT (17/2) +op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ffcf1400000000000000p+15:0x477fe78a) flags=INEXACT (18/0) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.5bf0a800000000000000p+1:0x402df854) +res: f32(0x1.91ed3a00000000000000p+17:0x4848f69d) flags=INEXACT (18/1) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.5bc56000000000000000p+17:0x482de2b0) flags=INEXACT (18/2) +op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.08edee00000000000000p+18:0x488476f7) flags=INEXACT (19/0) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.921fb600000000000000p+1:0x40490fdb) +res: f32(0x1.ff7e0800000000000000p+31:0x4f7fbf04) flags=INEXACT (19/1) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.08ee7800000000000000p+18:0x4884773c) flags=INEXACT (19/2) +op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800800000000000000p+31:0x4f7fc004) flags=INEXACT (20/0) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00) +res: f32(0x1.ff840600000000000000p+31:0x4f7fc203) flags=INEXACT (20/1) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820600000000000000p+31:0x4f7fc103) flags=INEXACT (20/2) +op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff860600000000000000p+31:0x4f7fc303) flags=INEXACT (21/0) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+15:0x477fe000) +res: f32(0x1.ff820600000000000000p+32:0x4fffc103) flags=INEXACT (21/1) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff800800000000000000p+32:0x4fffc004) flags=INEXACT (21/2) +op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff830600000000000000p+32:0x4fffc183) flags=INEXACT (22/0) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100) +res: f32(0x1.ff7f8800000000000000p+33:0x507fbfc4) flags=INEXACT (22/1) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff840600000000000000p+32:0x4fffc203) flags=INEXACT (22/2) +op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.ff800800000000000000p+33:0x507fc004) flags=INEXACT (23/0) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) +res: f32(0x1.ff820600000000000000p+33:0x507fc103) flags=INEXACT (23/1) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.ff810600000000000000p+33:0x507fc083) flags=INEXACT (23/2) +op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(0x1.c0bab600000000000000p+99:0x71605d5b) flags=INEXACT (24/0) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.ffc00000000000000000p+16:0x47ffe000) +res: f32(0x1.c0837e00000000000000p+116:0x79e041bf) flags=INEXACT (24/1) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.c0829e00000000000000p+116:0x79e0414f) flags=INEXACT (24/2) +op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/0) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(0x1.ffc10000000000000000p+16:0x47ffe080) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/1) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/2) +op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(inf:0x7f800000) flags=OK (26/0) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(0x1.c0bab600000000000000p+99:0x71605d5b) +res: f32(inf:0x7f800000) flags=OK (26/1) +op : f32(inf:0x7f800000) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(inf:0x7f800000) flags=OK (26/2) +op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fc00000) flags=OK (27/0) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff) +res: f32(nan:0x7fc00000) flags=OK (27/1) +op : f32(nan:0x7fc00000) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000) +res: f32(nan:0x7fc00000) flags=OK (27/2) +op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/0) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(inf:0x7f800000) +res: f32(nan:0x7fe00000) flags=INVALID (28/1) +op : f32(nan:0x7fa00000) * f32(inf:0x7f800000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (28/2) +op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (29/0) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(nan:0x7fc00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/1) +op : f32(-nan:0xffa00000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (29/2) +op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/0) +op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(nan:0x7fa00000) +res: f32(nan:0x7fe00000) flags=INVALID (30/1) +op : f32(-nan:0xffc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000) +res: f32(-nan:0xffe00000) flags=INVALID (30/2) +# LP184149 +op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-1:0x3f000000) + f32(0x0.00000000000000000000p+0:0000000000) +res: f32(0x0.00000000000000000000p+0:0000000000) flags=OK (31/0) +op : f32(0x1.00000000000000000000p-149:0x00000001) * f32(0x1.00000000000000000000p-149:0x00000001) + f32(0x1.00000000000000000000p-149:0x00000001) +res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (32/0) diff --git a/tests/tcg/loongarch64/system/boot.S b/tests/tcg/loongarch64/system/boot.S index 67eb1c04ce..37a81bafe7 100644 --- a/tests/tcg/loongarch64/system/boot.S +++ b/tests/tcg/loongarch64/system/boot.S @@ -21,9 +21,10 @@ _start: .align 16 _exit: 2: /* QEMU ACPI poweroff */ - li.w t0, 0xff - li.w t1, 0x10080010 - st.w t0, t1, 0 + li.w t0, 0x34 + li.w t1, 0x100e001c + st.b t0, t1, 0 + idle 0 bl 2b diff --git a/tests/tcg/loongarch64/test_bit.c b/tests/tcg/loongarch64/test_bit.c new file mode 100644 index 0000000000..a6d9904909 --- /dev/null +++ b/tests/tcg/loongarch64/test_bit.c @@ -0,0 +1,88 @@ +#include +#include + +#define ARRAY_SIZE(X) (sizeof(X) / sizeof(*(X))) +#define TEST_CLO(N) \ +static uint64_t test_clo_##N(uint64_t rj) \ +{ \ + uint64_t rd = 0; \ + \ + asm volatile("clo."#N" %0, %1\n\t" \ + : "=r"(rd) \ + : "r"(rj) \ + : ); \ + return rd; \ +} + +#define TEST_CLZ(N) \ +static uint64_t test_clz_##N(uint64_t rj) \ +{ \ + uint64_t rd = 0; \ + \ + asm volatile("clz."#N" %0, %1\n\t" \ + : "=r"(rd) \ + : "r"(rj) \ + : ); \ + return rd; \ +} + +#define TEST_CTO(N) \ +static uint64_t test_cto_##N(uint64_t rj) \ +{ \ + uint64_t rd = 0; \ + \ + asm volatile("cto."#N" %0, %1\n\t" \ + : "=r"(rd) \ + : "r"(rj) \ + : ); \ + return rd; \ +} + +#define TEST_CTZ(N) \ +static uint64_t test_ctz_##N(uint64_t rj) \ +{ \ + uint64_t rd = 0; \ + \ + asm volatile("ctz."#N" %0, %1\n\t" \ + : "=r"(rd) \ + : "r"(rj) \ + : ); \ + return rd; \ +} + +TEST_CLO(w) +TEST_CLO(d) +TEST_CLZ(w) +TEST_CLZ(d) +TEST_CTO(w) +TEST_CTO(d) +TEST_CTZ(w) +TEST_CTZ(d) + +struct vector { + uint64_t (*func)(uint64_t); + uint64_t u; + uint64_t r; +}; + +static struct vector vectors[] = { + {test_clo_w, 0xfff11fff392476ab, 0}, + {test_clo_d, 0xabd28a64000000, 0}, + {test_clz_w, 0xfaffff42392476ab, 2}, + {test_clz_d, 0xabd28a64000000, 8}, + {test_cto_w, 0xfff11fff392476ab, 2}, + {test_cto_d, 0xabd28a64000000, 0}, + {test_ctz_w, 0xfaffff42392476ab, 0}, + {test_ctz_d, 0xabd28a64000000, 26}, +}; + +int main() +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vectors); i++) { + assert((*vectors[i].func)(vectors[i].u) == vectors[i].r); + } + + return 0; +} diff --git a/tests/tcg/loongarch64/test_div.c b/tests/tcg/loongarch64/test_div.c new file mode 100644 index 0000000000..6c31fe97ae --- /dev/null +++ b/tests/tcg/loongarch64/test_div.c @@ -0,0 +1,54 @@ +#include +#include +#include + +#define TEST_DIV(N, M) \ +static void test_div_ ##N(uint ## M ## _t rj, \ + uint ## M ## _t rk, \ + uint64_t rm) \ +{ \ + uint64_t rd = 0; \ + \ + asm volatile("div."#N" %0,%1,%2\n\t" \ + : "=r"(rd) \ + : "r"(rj), "r"(rk) \ + : ); \ + assert(rd == rm); \ +} + +#define TEST_MOD(N, M) \ +static void test_mod_ ##N(uint ## M ## _t rj, \ + uint ## M ## _t rk, \ + uint64_t rm) \ +{ \ + uint64_t rd = 0; \ + \ + asm volatile("mod."#N" %0,%1,%2\n\t" \ + : "=r"(rd) \ + : "r"(rj), "r"(rk) \ + : ); \ + assert(rd == rm); \ +} + +TEST_DIV(w, 32) +TEST_DIV(wu, 32) +TEST_DIV(d, 64) +TEST_DIV(du, 64) +TEST_MOD(w, 32) +TEST_MOD(wu, 32) +TEST_MOD(d, 64) +TEST_MOD(du, 64) + +int main(void) +{ + test_div_w(0xffaced97, 0xc36abcde, 0x0); + test_div_wu(0xffaced97, 0xc36abcde, 0x1); + test_div_d(0xffaced973582005f, 0xef56832a358b, 0xffffffffffffffa8); + test_div_du(0xffaced973582005f, 0xef56832a358b, 0x11179); + test_mod_w(0x7cf18c32, 0xa04da650, 0x1d3f3282); + test_mod_wu(0x7cf18c32, 0xc04da650, 0x7cf18c32); + test_mod_d(0x7cf18c3200000000, 0xa04da65000000000, 0x1d3f328200000000); + test_mod_du(0x7cf18c3200000000, 0xc04da65000000000, 0x7cf18c3200000000); + + return 0; +} diff --git a/tests/tcg/loongarch64/test_fclass.c b/tests/tcg/loongarch64/test_fclass.c new file mode 100644 index 0000000000..7ba1d2c151 --- /dev/null +++ b/tests/tcg/loongarch64/test_fclass.c @@ -0,0 +1,130 @@ +#include + +/* float class */ +#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 TEST_FCLASS(N) \ +void test_fclass_##N(long s) \ +{ \ + double fd; \ + long rd; \ + \ + asm volatile("fclass."#N" %0, %2\n\t" \ + "movfr2gr."#N" %1, %2\n\t" \ + : "=f"(fd), "=r"(rd) \ + : "f"(s) \ + : ); \ + switch (rd) { \ + case FLOAT_CLASS_SIGNALING_NAN: \ + case FLOAT_CLASS_QUIET_NAN: \ + case FLOAT_CLASS_NEGATIVE_INFINITY: \ + case FLOAT_CLASS_NEGATIVE_NORMAL: \ + case FLOAT_CLASS_NEGATIVE_SUBNORMAL: \ + case FLOAT_CLASS_NEGATIVE_ZERO: \ + case FLOAT_CLASS_POSITIVE_INFINITY: \ + case FLOAT_CLASS_POSITIVE_NORMAL: \ + case FLOAT_CLASS_POSITIVE_SUBNORMAL: \ + case FLOAT_CLASS_POSITIVE_ZERO: \ + break; \ + default: \ + printf("fclass."#N" test failed.\n"); \ + break; \ + } \ +} + +/* + * float format + * type | S | Exponent | Fraction | example value + * 31 | 30 --23 | 22 | 21 --0 | + * | bit | + * SNAN 0/1 | 0xFF | 0 | !=0 | 0x7FBFFFFF + * QNAN 0/1 | 0xFF | 1 | | 0x7FCFFFFF + * -infinity 1 | 0xFF | 0 | 0xFF800000 + * -normal 1 | [1, 0xFE] | [0, 0x7FFFFF]| 0xFF7FFFFF + * -subnormal 1 | 0 | !=0 | 0x807FFFFF + * -0 1 | 0 | 0 | 0x80000000 + * +infinity 0 | 0xFF | 0 | 0x7F800000 + * +normal 0 | [1, 0xFE] | [0, 0x7FFFFF]| 0x7F7FFFFF + * +subnormal 0 | 0 | !=0 | 0x007FFFFF + * +0 0 | 0 | 0 | 0x00000000 + */ + +long float_snan = 0x7FBFFFFF; +long float_qnan = 0x7FCFFFFF; +long float_neg_infinity = 0xFF800000; +long float_neg_normal = 0xFF7FFFFF; +long float_neg_subnormal = 0x807FFFFF; +long float_neg_zero = 0x80000000; +long float_post_infinity = 0x7F800000; +long float_post_normal = 0x7F7FFFFF; +long float_post_subnormal = 0x007FFFFF; +long float_post_zero = 0x00000000; + +/* + * double format + * type | S | Exponent | Fraction | example value + * 63 | 62 -- 52 | 51 | 50 -- 0 | + * | bit | + * SNAN 0/1 | 0x7FF | 0 | !=0 | 0x7FF7FFFFFFFFFFFF + * QNAN 0/1 | 0x7FF | 1 | | 0x7FFFFFFFFFFFFFFF + * -infinity 1 | 0x7FF | 0 | 0xFFF0000000000000 + * -normal 1 |[1, 0x7FE] | | 0xFFEFFFFFFFFFFFFF + * -subnormal 1 | 0 | !=0 | 0x8007FFFFFFFFFFFF + * -0 1 | 0 | 0 | 0x8000000000000000 + * +infinity 0 | 0x7FF | 0 | 0x7FF0000000000000 + * +normal 0 |[1, 0x7FE] | | 0x7FEFFFFFFFFFFFFF + * +subnormal 0 | 0 | !=0 | 0x000FFFFFFFFFFFFF + * +0 0 | 0 | 0 | 0x0000000000000000 + */ + +long double_snan = 0x7FF7FFFFFFFFFFFF; +long double_qnan = 0x7FFFFFFFFFFFFFFF; +long double_neg_infinity = 0xFFF0000000000000; +long double_neg_normal = 0xFFEFFFFFFFFFFFFF; +long double_neg_subnormal = 0x8007FFFFFFFFFFFF; +long double_neg_zero = 0x8000000000000000; +long double_post_infinity = 0x7FF0000000000000; +long double_post_normal = 0x7FEFFFFFFFFFFFFF; +long double_post_subnormal = 0x000FFFFFFFFFFFFF; +long double_post_zero = 0x0000000000000000; + +TEST_FCLASS(s) +TEST_FCLASS(d) + +int main() +{ + /* fclass.s */ + test_fclass_s(float_snan); + test_fclass_s(float_qnan); + test_fclass_s(float_neg_infinity); + test_fclass_s(float_neg_normal); + test_fclass_s(float_neg_subnormal); + test_fclass_s(float_neg_zero); + test_fclass_s(float_post_infinity); + test_fclass_s(float_post_normal); + test_fclass_s(float_post_subnormal); + test_fclass_s(float_post_zero); + + /* fclass.d */ + test_fclass_d(double_snan); + test_fclass_d(double_qnan); + test_fclass_d(double_neg_infinity); + test_fclass_d(double_neg_normal); + test_fclass_d(double_neg_subnormal); + test_fclass_d(double_neg_zero); + test_fclass_d(double_post_infinity); + test_fclass_d(double_post_normal); + test_fclass_d(double_post_subnormal); + test_fclass_d(double_post_zero); + + return 0; +} diff --git a/tests/tcg/loongarch64/test_fcsr.c b/tests/tcg/loongarch64/test_fcsr.c new file mode 100644 index 0000000000..ad3609eb99 --- /dev/null +++ b/tests/tcg/loongarch64/test_fcsr.c @@ -0,0 +1,15 @@ +#include + +int main() +{ + unsigned fcsr; + + asm("movgr2fcsr $r0,$r0\n\t" + "movgr2fr.d $f0,$r0\n\t" + "fdiv.d $f0,$f0,$f0\n\t" + "movfcsr2gr %0,$r0" + : "=r"(fcsr) : : "f0"); + + assert(fcsr & (16 << 16)); /* Invalid */ + return 0; +} diff --git a/tests/tcg/loongarch64/test_fpcom.c b/tests/tcg/loongarch64/test_fpcom.c new file mode 100644 index 0000000000..9e81f767f9 --- /dev/null +++ b/tests/tcg/loongarch64/test_fpcom.c @@ -0,0 +1,37 @@ +#include + +#define TEST_COMP(N) \ +void test_##N(float fj, float fk) \ +{ \ + int rd = 0; \ + \ + asm volatile("fcmp."#N".s $fcc6,%1,%2\n" \ + "movcf2gr %0, $fcc6\n" \ + : "=r"(rd) \ + : "f"(fj), "f"(fk) \ + : ); \ + assert(rd == 1); \ +} + +TEST_COMP(ceq) +TEST_COMP(clt) +TEST_COMP(cle) +TEST_COMP(cne) +TEST_COMP(seq) +TEST_COMP(slt) +TEST_COMP(sle) +TEST_COMP(sne) + +int main() +{ + test_ceq(0xff700102, 0xff700102); + test_clt(0x00730007, 0xff730007); + test_cle(0xff70130a, 0xff70130b); + test_cne(0x1238acde, 0xff71111f); + test_seq(0xff766618, 0xff766619); + test_slt(0xff78881c, 0xff78901d); + test_sle(0xff780b22, 0xff790b22); + test_sne(0xff7bcd25, 0xff7a26cf); + + return 0; +} diff --git a/tests/tcg/loongarch64/test_pcadd.c b/tests/tcg/loongarch64/test_pcadd.c new file mode 100644 index 0000000000..da2a64db82 --- /dev/null +++ b/tests/tcg/loongarch64/test_pcadd.c @@ -0,0 +1,38 @@ +#include +#include +#include + +#define TEST_PCADDU(N) \ +void test_##N(int a) \ +{ \ + uint64_t rd1 = 0; \ + uint64_t rd2 = 0; \ + uint64_t rm, rn; \ + \ + asm volatile(""#N" %0, 0x104\n\t" \ + ""#N" %1, 0x12345\n\t" \ + : "=r"(rd1), "=r"(rd2) \ + : ); \ + rm = rd2 - rd1; \ + if (!strcmp(#N, "pcalau12i")) { \ + rn = ((0x12345UL - 0x104) << a) & ~0xfff; \ + } else { \ + rn = ((0x12345UL - 0x104) << a) + 4; \ + } \ + assert(rm == rn); \ +} + +TEST_PCADDU(pcaddi) +TEST_PCADDU(pcaddu12i) +TEST_PCADDU(pcaddu18i) +TEST_PCADDU(pcalau12i) + +int main() +{ + test_pcaddi(2); + test_pcaddu12i(12); + test_pcaddu18i(18); + test_pcalau12i(12); + + return 0; +} diff --git a/tests/tcg/m68k/Makefile.target b/tests/tcg/m68k/Makefile.target index 1163c7ef03..33f7b1b127 100644 --- a/tests/tcg/m68k/Makefile.target +++ b/tests/tcg/m68k/Makefile.target @@ -4,7 +4,4 @@ # VPATH += $(SRC_PATH)/tests/tcg/m68k -TESTS += trap - -# On m68k Linux supports 4k and 8k pages (but 8k is currently broken) -EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-8192 +TESTS += trap denormal diff --git a/tests/tcg/m68k/denormal.c b/tests/tcg/m68k/denormal.c new file mode 100644 index 0000000000..20bd8c7332 --- /dev/null +++ b/tests/tcg/m68k/denormal.c @@ -0,0 +1,53 @@ +/* + * Test m68k extended double denormals. + */ + +#include +#include + +#define TEST(X, Y) { X, Y, X * Y } + +static volatile long double test[][3] = { + TEST(0x1p+16383l, 0x1p-16446l), + TEST(0x1.1p-8223l, 0x1.1p-8224l), + TEST(1.0l, 0x1p-16383l), +}; + +#undef TEST + +static void dump_ld(const char *label, long double ld) +{ + union { + long double d; + struct { + uint32_t exp:16; + uint32_t space:16; + uint32_t h; + uint32_t l; + }; + } u; + + u.d = ld; + printf("%12s: % -27La 0x%04x 0x%08x 0x%08x\n", label, u.d, u.exp, u.h, u.l); +} + +int main(void) +{ + int i, n = sizeof(test) / sizeof(test[0]), err = 0; + + for (i = 0; i < n; ++i) { + long double x = test[i][0]; + long double y = test[i][1]; + long double build_mul = test[i][2]; + long double runtime_mul = x * y; + + if (runtime_mul != build_mul) { + dump_ld("x", x); + dump_ld("y", y); + dump_ld("build_mul", build_mul); + dump_ld("runtime_mul", runtime_mul); + err = 1; + } + } + return err; +} diff --git a/tests/tcg/minilib/Makefile.target b/tests/tcg/minilib/Makefile.target index c821d2806a..af0bf54be9 100644 --- a/tests/tcg/minilib/Makefile.target +++ b/tests/tcg/minilib/Makefile.target @@ -12,7 +12,7 @@ SYSTEM_MINILIB_SRC=$(SRC_PATH)/tests/tcg/minilib MINILIB_SRCS=$(wildcard $(SYSTEM_MINILIB_SRC)/*.c) MINILIB_OBJS=$(patsubst $(SYSTEM_MINILIB_SRC)/%.c, %.o, $(MINILIB_SRCS)) -MINILIB_CFLAGS+=-nostdlib -ggdb -O0 +MINILIB_CFLAGS+=-nostdlib -fno-stack-protector -ggdb -O0 MINILIB_INC=-isystem $(SYSTEM_MINILIB_SRC) .PRECIOUS: $(MINILIB_OBJS) diff --git a/tests/tcg/mips/Makefile.target b/tests/tcg/mips/Makefile.target index 1a994d5525..5d17c1706e 100644 --- a/tests/tcg/mips/Makefile.target +++ b/tests/tcg/mips/Makefile.target @@ -14,6 +14,6 @@ MIPS_TESTS=hello-mips TESTS += $(MIPS_TESTS) -hello-mips: CFLAGS+=-mno-abicalls -fno-PIC -mabi=32 +hello-mips: CFLAGS+=-mno-abicalls -fno-PIC -fno-stack-protector -mabi=32 hello-mips: LDFLAGS+=-nostdlib endif diff --git a/tests/tcg/mips/hello-mips.c b/tests/tcg/mips/hello-mips.c index 4e1cf501af..38e22d00e3 100644 --- a/tests/tcg/mips/hello-mips.c +++ b/tests/tcg/mips/hello-mips.c @@ -5,8 +5,8 @@ * http://www.linux-mips.org/wiki/MIPSABIHistory * http://www.linux.com/howtos/Assembly-HOWTO/mips.shtml * -* mipsel-linux-gcc -nostdlib -mno-abicalls -fno-PIC -mabi=32 \ -* -O2 -static -o hello-mips hello-mips.c +* mipsel-linux-gcc -nostdlib -mno-abicalls -fno-PIC -fno-stack-protector \ +* -mabi=32 -O2 -static -o hello-mips hello-mips.c * */ #define __NR_SYSCALL_BASE 4000 diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index 6bba523729..5e3391ec9d 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -26,7 +26,7 @@ float_%: float_%.c libs/float_helpers.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< $(MULTIARCH_SRC)/libs/float_helpers.c -o $@ $(LDFLAGS) run-float_%: float_% - $(call run-test,$<, $(QEMU) $(QEMU_OPTS) $<,"$< on $(TARGET_NAME)") + $(call run-test,$<, $(QEMU) $(QEMU_OPTS) $<) $(call conditional-diff-out,$<,$(SRC_PATH)/tests/tcg/$(TARGET_NAME)/$<.ref) @@ -36,50 +36,107 @@ threadcount: LDFLAGS+=-lpthread signals: LDFLAGS+=-lrt -lpthread -# We define the runner for test-mmap after the individual -# architectures have defined their supported pages sizes. If no -# additional page sizes are defined we only run the default test. +munmap-pthread: CFLAGS+=-pthread +munmap-pthread: LDFLAGS+=-pthread + +vma-pthread: CFLAGS+=-pthread +vma-pthread: LDFLAGS+=-pthread + +# The vma-pthread seems very sensitive on gitlab and we currently +# don't know if its exposing a real bug or the test is flaky. +ifneq ($(GITLAB_CI),) +run-vma-pthread: vma-pthread + $(call skip-test, $<, "flaky on CI?") +run-plugin-vma-pthread-with-%: vma-pthread + $(call skip-test, $<, "flaky on CI?") +endif -# default case (host page size) run-test-mmap: test-mmap - $(call run-test, test-mmap, $(QEMU) $<, \ - "$< (default) on $(TARGET_NAME)") + $(call run-test, test-mmap, $(QEMU) $<, $< (default)) -# additional page sizes (defined by each architecture adding to EXTRA_RUNS) -run-test-mmap-%: test-mmap - $(call run-test, test-mmap-$*, $(QEMU) -p $* $<,\ - "$< ($* byte pages) on $(TARGET_NAME)") - -ifneq ($(HAVE_GDB_BIN),) +ifneq ($(GDB),) GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py run-gdbstub-sha1: sha1 $(call run-test, $@, $(GDB_SCRIPT) \ - --gdb $(HAVE_GDB_BIN) \ + --gdb $(GDB) \ --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ --bin $< --test $(MULTIARCH_SRC)/gdbstub/sha1.py, \ - "basic gdbstub support") + basic gdbstub support) run-gdbstub-qxfer-auxv-read: sha1 $(call run-test, $@, $(GDB_SCRIPT) \ - --gdb $(HAVE_GDB_BIN) \ + --gdb $(GDB) \ --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ --bin $< --test $(MULTIARCH_SRC)/gdbstub/test-qxfer-auxv-read.py, \ - "basic gdbstub qXfer:auxv:read support") + basic gdbstub qXfer:auxv:read support) + +run-gdbstub-qxfer-siginfo-read: segfault + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin "$< -s" --test $(MULTIARCH_SRC)/gdbstub/test-qxfer-siginfo-read.py, \ + basic gdbstub qXfer:siginfo:read support) + +run-gdbstub-proc-mappings: sha1 + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/test-proc-mappings.py, \ + proc mappings support) run-gdbstub-thread-breakpoint: testthread $(call run-test, $@, $(GDB_SCRIPT) \ - --gdb $(HAVE_GDB_BIN) \ + --gdb $(GDB) \ --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ --bin $< --test $(MULTIARCH_SRC)/gdbstub/test-thread-breakpoint.py, \ - "hitting a breakpoint on non-main thread") + hitting a breakpoint on non-main thread) + +run-gdbstub-registers: sha512 + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/registers.py, \ + checking register enumeration) + +run-gdbstub-prot-none: prot-none + $(call run-test, $@, env PROT_NONE_PY=1 $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/prot-none.py, \ + accessing PROT_NONE memory) + +run-gdbstub-catch-syscalls: catch-syscalls + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/catch-syscalls.py, \ + hitting a syscall catchpoint) + +run-gdbstub-follow-fork-mode-child: follow-fork-mode + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/follow-fork-mode-child.py, \ + following children on fork) + +run-gdbstub-follow-fork-mode-parent: follow-fork-mode + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/follow-fork-mode-parent.py, \ + following parents on fork) else run-gdbstub-%: - $(call skip-test, "gdbstub test $*", "need working gdb") + $(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst -%,,$(TARGET_NAME)) support") endif EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \ - run-gdbstub-thread-breakpoint + run-gdbstub-proc-mappings run-gdbstub-thread-breakpoint \ + run-gdbstub-registers run-gdbstub-prot-none \ + run-gdbstub-catch-syscalls run-gdbstub-follow-fork-mode-child \ + run-gdbstub-follow-fork-mode-parent \ + run-gdbstub-qxfer-siginfo-read # ARM Compatible Semi Hosting Tests # @@ -94,13 +151,13 @@ VPATH += $(MULTIARCH_SRC)/arm-compat-semi semihosting: CFLAGS+=-I$(SRC_PATH)/tests/tcg/$(TARGET_NAME) run-semihosting: semihosting - $(call run-test,$<,$(QEMU) $< 2> $<.err, "$< on $(TARGET_NAME)") + $(call run-test,$<,$(QEMU) $< 2> $<.err) run-plugin-semihosting-with-%: $(call run-test, $@, $(QEMU) $(QEMU_OPTS) \ -plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \ $(call strip-plugin,$<) 2> $<.err, \ - "$< on $(TARGET_NAME) with $*") + $< with $*) semiconsole: CFLAGS+=-I$(SRC_PATH)/tests/tcg/$(TARGET_NAME) diff --git a/tests/tcg/multiarch/arm-compat-semi/semiconsole.c b/tests/tcg/multiarch/arm-compat-semi/semiconsole.c index 1d82efc589..1e2268f4b7 100644 --- a/tests/tcg/multiarch/arm-compat-semi/semiconsole.c +++ b/tests/tcg/multiarch/arm-compat-semi/semiconsole.c @@ -1,10 +1,10 @@ /* * linux-user semihosting console * - * Copyright (c) 2019 + * Copyright (c) 2024 * Written by Alex Bennée * - * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later */ #define SYS_READC 0x07 diff --git a/tests/tcg/multiarch/arm-compat-semi/semihosting.c b/tests/tcg/multiarch/arm-compat-semi/semihosting.c index 8627eee3cf..f609c01341 100644 --- a/tests/tcg/multiarch/arm-compat-semi/semihosting.c +++ b/tests/tcg/multiarch/arm-compat-semi/semihosting.c @@ -1,10 +1,10 @@ /* * linux-user semihosting checks * - * Copyright (c) 2019 + * Copyright (c) 2019, 2024 * Written by Alex Bennée * - * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later */ #define SYS_WRITE0 0x04 diff --git a/tests/tcg/multiarch/catch-syscalls.c b/tests/tcg/multiarch/catch-syscalls.c new file mode 100644 index 0000000000..d1ff1936a7 --- /dev/null +++ b/tests/tcg/multiarch/catch-syscalls.c @@ -0,0 +1,51 @@ +/* + * Test GDB syscall catchpoints. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#define _GNU_SOURCE +#include +#include + +const char *catch_syscalls_state = "start"; + +void end_of_main(void) +{ +} + +int main(void) +{ + int ret = EXIT_FAILURE; + char c0 = 'A', c1; + int fd[2]; + + catch_syscalls_state = "pipe2"; + if (pipe2(fd, 0)) { + goto out; + } + + catch_syscalls_state = "write"; + if (write(fd[1], &c0, sizeof(c0)) != sizeof(c0)) { + goto out_close; + } + + catch_syscalls_state = "read"; + if (read(fd[0], &c1, sizeof(c1)) != sizeof(c1)) { + goto out_close; + } + + catch_syscalls_state = "check"; + if (c0 == c1) { + ret = EXIT_SUCCESS; + } + +out_close: + catch_syscalls_state = "close"; + close(fd[0]); + close(fd[1]); + +out: + catch_syscalls_state = "end"; + end_of_main(); + return ret; +} diff --git a/tests/tcg/multiarch/float_convd.c b/tests/tcg/multiarch/float_convd.c index 0a1f0f93dc..58d7f8b4c5 100644 --- a/tests/tcg/multiarch/float_convd.c +++ b/tests/tcg/multiarch/float_convd.c @@ -1,9 +1,9 @@ /* * Floating Point Convert Doubles to Various * - * Copyright (c) 2019 Linaro + * Copyright (c) 2019, 2024 Linaro * - * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later */ #include diff --git a/tests/tcg/multiarch/float_convs.c b/tests/tcg/multiarch/float_convs.c index 2e4fa55324..cb1fdd439e 100644 --- a/tests/tcg/multiarch/float_convs.c +++ b/tests/tcg/multiarch/float_convs.c @@ -1,9 +1,9 @@ /* * Floating Point Convert Single to Various * - * Copyright (c) 2019 Linaro + * Copyright (c) 2019, 2024 Linaro * - * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later */ #include diff --git a/tests/tcg/multiarch/float_helpers.h b/tests/tcg/multiarch/float_helpers.h index 309f3f4bf1..c42ebe64b9 100644 --- a/tests/tcg/multiarch/float_helpers.h +++ b/tests/tcg/multiarch/float_helpers.h @@ -1,9 +1,9 @@ /* * Common Float Helpers * - * Copyright (c) 2019 Linaro + * Copyright (c) 2019, 2024 Linaro * - * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later */ #include diff --git a/tests/tcg/multiarch/float_madds.c b/tests/tcg/multiarch/float_madds.c index 4888f8641f..a692e052d5 100644 --- a/tests/tcg/multiarch/float_madds.c +++ b/tests/tcg/multiarch/float_madds.c @@ -1,9 +1,9 @@ /* * Fused Multiply Add (Single) * - * Copyright (c) 2019 Linaro + * Copyright (c) 2019, 2024 Linaro * - * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later */ #include diff --git a/tests/tcg/multiarch/follow-fork-mode.c b/tests/tcg/multiarch/follow-fork-mode.c new file mode 100644 index 0000000000..cb6b032b38 --- /dev/null +++ b/tests/tcg/multiarch/follow-fork-mode.c @@ -0,0 +1,56 @@ +/* + * Test GDB's follow-fork-mode. + * + * fork() a chain of processes. + * Parents sends one byte to their children, and children return their + * position in the chain, in order to prove that they survived GDB's fork() + * handling. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include + +void break_after_fork(void) +{ +} + +int main(void) +{ + int depth = 42, err, i, fd[2], status; + pid_t child, pid; + ssize_t n; + char b; + + for (i = 0; i < depth; i++) { + err = pipe(fd); + assert(err == 0); + child = fork(); + break_after_fork(); + assert(child != -1); + if (child == 0) { + close(fd[1]); + + n = read(fd[0], &b, 1); + close(fd[0]); + assert(n == 1); + assert(b == (char)i); + } else { + close(fd[0]); + + b = (char)i; + n = write(fd[1], &b, 1); + close(fd[1]); + assert(n == 1); + + pid = waitpid(child, &status, 0); + assert(pid == child); + assert(WIFEXITED(status)); + return WEXITSTATUS(status) - 1; + } + } + + return depth; +} diff --git a/tests/tcg/multiarch/gdbstub/catch-syscalls.py b/tests/tcg/multiarch/gdbstub/catch-syscalls.py new file mode 100644 index 0000000000..ccce35902f --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/catch-syscalls.py @@ -0,0 +1,53 @@ +"""Test GDB syscall catchpoints. + +SPDX-License-Identifier: GPL-2.0-or-later +""" +from test_gdbstub import main, report + + +def check_state(expected): + """Check the catch_syscalls_state value""" + actual = gdb.parse_and_eval("catch_syscalls_state").string() + report(actual == expected, "{} == {}".format(actual, expected)) + + +def run_test(): + """Run through the tests one by one""" + gdb.Breakpoint("main") + gdb.execute("continue") + + # Check that GDB stops for pipe2/read calls/returns, but not for write. + gdb.execute("delete") + try: + gdb.execute("catch syscall pipe2 read") + except gdb.error as exc: + exc_str = str(exc) + if "not supported on this architecture" in exc_str: + print("SKIP: {}".format(exc_str)) + return + raise + for _ in range(2): + gdb.execute("continue") + check_state("pipe2") + for _ in range(2): + gdb.execute("continue") + check_state("read") + + # Check that deletion works. + gdb.execute("delete") + gdb.Breakpoint("end_of_main") + gdb.execute("continue") + check_state("end") + + # Check that catch-all works (libc should at least call exit). + gdb.execute("delete") + gdb.execute("catch syscall") + gdb.execute("continue") + gdb.execute("delete") + gdb.execute("continue") + + exitcode = int(gdb.parse_and_eval("$_exitcode")) + report(exitcode == 0, "{} == 0".format(exitcode)) + + +main(run_test) diff --git a/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py b/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py new file mode 100644 index 0000000000..72a6e440c0 --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/follow-fork-mode-child.py @@ -0,0 +1,40 @@ +"""Test GDB's follow-fork-mode child. + +SPDX-License-Identifier: GPL-2.0-or-later +""" +from test_gdbstub import main, report + + +def run_test(): + """Run through the tests one by one""" + gdb.execute("set follow-fork-mode child") + # Check that the parent breakpoints are unset. + gdb.execute("break break_after_fork") + # Check that the parent syscall catchpoints are unset. + # Skip this check on the architectures that don't have them. + have_fork_syscall = False + for fork_syscall in ("fork", "clone", "clone2", "clone3"): + try: + gdb.execute("catch syscall {}".format(fork_syscall)) + except gdb.error: + pass + else: + have_fork_syscall = True + gdb.execute("continue") + for i in range(42): + if have_fork_syscall: + # syscall entry. + if i % 2 == 0: + # Check that the parent single-stepping is turned off. + gdb.execute("si") + else: + gdb.execute("continue") + # syscall exit. + gdb.execute("continue") + # break_after_fork() + gdb.execute("continue") + exitcode = int(gdb.parse_and_eval("$_exitcode")) + report(exitcode == 42, "{} == 42".format(exitcode)) + + +main(run_test) diff --git a/tests/tcg/multiarch/gdbstub/follow-fork-mode-parent.py b/tests/tcg/multiarch/gdbstub/follow-fork-mode-parent.py new file mode 100644 index 0000000000..5c2fe72208 --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/follow-fork-mode-parent.py @@ -0,0 +1,16 @@ +"""Test GDB's follow-fork-mode parent. + +SPDX-License-Identifier: GPL-2.0-or-later +""" +from test_gdbstub import main, report + + +def run_test(): + """Run through the tests one by one""" + gdb.execute("set follow-fork-mode parent") + gdb.execute("continue") + exitcode = int(gdb.parse_and_eval("$_exitcode")) + report(exitcode == 0, "{} == 0".format(exitcode)) + + +main(run_test) diff --git a/tests/tcg/multiarch/gdbstub/interrupt.py b/tests/tcg/multiarch/gdbstub/interrupt.py new file mode 100644 index 0000000000..90a45b5140 --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/interrupt.py @@ -0,0 +1,60 @@ +from __future__ import print_function +# +# Test some of the system debug features with the multiarch memory +# test. It is a port of the original vmlinux focused test case but +# using the "memory" test instead. +# +# This is launched via tests/guest-debug/run-test.py +# + +import gdb +from test_gdbstub import main, report + + +def check_interrupt(thread): + """ + Check that, if thread is resumed, we go back to the same thread when the + program gets interrupted. + """ + + # Switch to the thread we're going to be running the test in. + print("thread ", thread.num) + gdb.execute("thr %d" % thread.num) + + # Enter the loop() function on this thread. + # + # While there are cleaner ways to do this, we want to minimize the number of + # side effects on the gdbstub's internal state, since those may mask bugs. + # Ideally, there should be no difference between what we're doing here and + # the program reaching the loop() function on its own. + # + # For this to be safe, we only need the prologue of loop() to not have + # instructions that may have problems with what we're doing here. We don't + # have to worry about anything else, as this function never returns. + gdb.execute("set $pc = loop") + + # Continue and then interrupt the task. + gdb.post_event(lambda: gdb.execute("interrupt")) + gdb.execute("c") + + # Check whether the thread we're in after the interruption is the same we + # ran continue from. + return (thread.num == gdb.selected_thread().num) + + +def run_test(): + """ + Test if interrupting the code always lands us on the same thread when + running with scheduler-lock enabled. + """ + if len(gdb.selected_inferior().threads()) == 1: + print("SKIP: set to run on a single thread") + exit(0) + + gdb.execute("set scheduler-locking on") + for thread in gdb.selected_inferior().threads(): + report(check_interrupt(thread), + "thread %d resumes correctly on interrupt" % thread.num) + + +main(run_test) diff --git a/tests/tcg/multiarch/gdbstub/memory.py b/tests/tcg/multiarch/gdbstub/memory.py index 67864ad902..532b92e7fb 100644 --- a/tests/tcg/multiarch/gdbstub/memory.py +++ b/tests/tcg/multiarch/gdbstub/memory.py @@ -1,6 +1,6 @@ from __future__ import print_function # -# Test some of the softmmu debug features with the multiarch memory +# Test some of the system debug features with the multiarch memory # test. It is a port of the original vmlinux focused test case but # using the "memory" test instead. # @@ -9,18 +9,7 @@ from __future__ import print_function import gdb import sys - -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 +from test_gdbstub import main, report def check_step(): @@ -99,32 +88,5 @@ def run_test(): report(cbp.hit_count == 0, "didn't reach backstop") -# -# This runs as the script it sourced (via -x, via run-test.py) -# -try: - inferior = gdb.selected_inferior() - arch = inferior.architecture() - print("ATTACHED: %s" % arch.name()) -except (gdb.error, AttributeError): - print("SKIPPING (not connected)", file=sys.stderr) - exit(0) -if gdb.parse_and_eval('$pc') == 0: - print("SKIP: PC not set") - exit(0) - -try: - # These are not very useful in scripts - gdb.execute("set pagination off") - - # Run the actual tests - run_test() -except (gdb.error): - print("GDB Exception: %s" % (sys.exc_info()[0])) - failcount += 1 - pass - -# Finally kill the inferior and exit gdb with a count of failures -gdb.execute("kill") -exit(failcount) +main(run_test) diff --git a/tests/tcg/multiarch/gdbstub/prot-none.py b/tests/tcg/multiarch/gdbstub/prot-none.py new file mode 100644 index 0000000000..7e264589cb --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/prot-none.py @@ -0,0 +1,36 @@ +"""Test that GDB can access PROT_NONE pages. + +This runs as a sourced script (via -x, via run-test.py). + +SPDX-License-Identifier: GPL-2.0-or-later +""" +import ctypes +from test_gdbstub import main, report + + +def probe_proc_self_mem(): + buf = ctypes.create_string_buffer(b'aaa') + try: + with open("/proc/self/mem", "rb") as fp: + fp.seek(ctypes.addressof(buf)) + return fp.read(3) == b'aaa' + except OSError: + return False + + +def run_test(): + """Run through the tests one by one""" + if not probe_proc_self_mem(): + print("SKIP: /proc/self/mem is not usable") + exit(0) + gdb.Breakpoint("break_here") + gdb.execute("continue") + val = gdb.parse_and_eval("*(char[2] *)q").string() + report(val == "42", "{} == 42".format(val)) + gdb.execute("set *(char[3] *)q = \"24\"") + gdb.execute("continue") + exitcode = int(gdb.parse_and_eval("$_exitcode")) + report(exitcode == 0, "{} == 0".format(exitcode)) + + +main(run_test) diff --git a/tests/tcg/multiarch/gdbstub/registers.py b/tests/tcg/multiarch/gdbstub/registers.py new file mode 100644 index 0000000000..b3d13cb077 --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/registers.py @@ -0,0 +1,211 @@ +# Exercise the register functionality by exhaustively iterating +# through all supported registers on the system. +# +# This is launched via tests/guest-debug/run-test.py but you can also +# call it directly if using it for debugging/introspection: +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import gdb +import xml.etree.ElementTree as ET +from test_gdbstub import main, report + + +initial_vlen = 0 + + +def fetch_xml_regmap(): + """ + Iterate through the XML descriptions and validate. + + We check for any duplicate registers and report them. Return a + reg_map hash containing the names, regnums and initial values of + all registers. + """ + + # First check the XML descriptions we have sent. Most arches + # support XML but a few of the ancient ones don't in which case we + # need to gracefully fail. + + try: + xml = gdb.execute("maint print xml-tdesc", False, True) + except (gdb.error): + print("SKIP: target does not support XML") + return None + + total_regs = 0 + reg_map = {} + + tree = ET.fromstring(xml) + for f in tree.findall("feature"): + name = f.attrib["name"] + regs = f.findall("reg") + + total = len(regs) + total_regs += total + base = int(regs[0].attrib["regnum"]) + top = int(regs[-1].attrib["regnum"]) + + print(f"feature: {name} has {total} registers from {base} to {top}") + + for r in regs: + name = r.attrib["name"] + regnum = int(r.attrib["regnum"]) + + entry = { "name": name, "regnum": regnum } + + if name in reg_map: + report(False, f"duplicate register {entry} vs {reg_map[name]}") + continue + + reg_map[name] = entry + + # Validate we match + report(total_regs == len(reg_map.keys()), + f"counted all {total_regs} registers in XML") + + return reg_map + + +def get_register_by_regnum(reg_map, regnum): + """ + Helper to find a register from the map via its XML regnum + """ + for regname, entry in reg_map.items(): + if entry['regnum'] == regnum: + return entry + return None + + +def crosscheck_remote_xml(reg_map): + """ + Cross-check the list of remote-registers with the XML info. + """ + + remote = gdb.execute("maint print remote-registers", False, True) + r_regs = remote.split("\n") + + total_regs = len(reg_map.keys()) + total_r_regs = 0 + total_r_elided_regs = 0 + + for r in r_regs: + r = r.replace("long long", "long_long") + r = r.replace("long double", "long_double") + fields = r.split() + # Some of the registers reported here are "pseudo" registers that + # gdb invents based on actual registers so we need to filter them + # out. + if len(fields) == 8: + r_name = fields[0] + r_regnum = int(fields[6]) + + # Some registers are "hidden" so don't have a name + # although they still should have a register number + if r_name == "''": + total_r_elided_regs += 1 + x_reg = get_register_by_regnum(reg_map, r_regnum) + if x_reg is not None: + x_reg["hidden"] = True + continue + + # check in the XML + try: + x_reg = reg_map[r_name] + except KeyError: + report(False, f"{r_name} not in XML description") + continue + + x_reg["seen"] = True + x_regnum = x_reg["regnum"] + if r_regnum != x_regnum: + report(False, f"{r_name} {r_regnum} == {x_regnum} (xml)") + else: + total_r_regs += 1 + + report(total_regs == total_r_regs + total_r_elided_regs, + "All XML Registers accounted for") + + print(f"xml-tdesc has {total_regs} registers") + print(f"remote-registers has {total_r_regs} registers") + print(f"of which {total_r_elided_regs} are hidden") + + for x_key in reg_map.keys(): + x_reg = reg_map[x_key] + if "hidden" in x_reg: + print(f"{x_reg} elided by gdb") + elif "seen" not in x_reg: + print(f"{x_reg} wasn't seen in remote-registers") + + +def initial_register_read(reg_map): + """ + Do an initial read of all registers that we know gdb cares about + (so ignore the elided ones). + """ + frame = gdb.selected_frame() + + for e in reg_map.values(): + name = e["name"] + regnum = e["regnum"] + + try: + if "hidden" in e: + value = frame.read_register(regnum) + e["initial"] = value + elif "seen" in e: + value = frame.read_register(name) + e["initial"] = value + + except ValueError: + report(False, f"failed to read reg: {name}") + + +def complete_and_diff(reg_map): + """ + Let the program run to (almost) completion and then iterate + through all the registers we know about and report which ones have + changed. + """ + # Let the program get to the end and we can check what changed + b = gdb.Breakpoint("_exit") + if b.pending: # workaround Microblaze weirdness + b.delete() + gdb.Breakpoint("_Exit") + + gdb.execute("continue") + + frame = gdb.selected_frame() + changed = 0 + + for e in reg_map.values(): + if "initial" in e and "hidden" not in e: + name = e["name"] + old_val = e["initial"] + + try: + new_val = frame.read_register(name) + except ValueError: + report(False, f"failed to read {name} at end of run") + continue + + if new_val != old_val: + print(f"{name} changes from {old_val} to {new_val}") + changed += 1 + + # as long as something changed we can be confident its working + report(changed > 0, f"{changed} registers were changed") + + +def run_test(): + "Run through the tests" + + reg_map = fetch_xml_regmap() + + if reg_map is not None: + crosscheck_remote_xml(reg_map) + initial_register_read(reg_map) + complete_and_diff(reg_map) + + +main(run_test) diff --git a/tests/tcg/multiarch/gdbstub/sha1.py b/tests/tcg/multiarch/gdbstub/sha1.py index 423b720e6d..1ce711a402 100644 --- a/tests/tcg/multiarch/gdbstub/sha1.py +++ b/tests/tcg/multiarch/gdbstub/sha1.py @@ -7,19 +7,11 @@ from __future__ import print_function # import gdb -import sys +from test_gdbstub import main, report + 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 def check_break(sym_name): "Setup breakpoint, continue and check we stopped." @@ -35,6 +27,7 @@ def check_break(sym_name): bp.delete() + def run_test(): "Run through the tests one by one" @@ -57,32 +50,5 @@ def run_test(): # finally check we don't barf inspecting registers gdb.execute("info registers") -# -# This runs as the script it sourced (via -x, via run-test.py) -# -try: - inferior = gdb.selected_inferior() - arch = inferior.architecture() - print("ATTACHED: %s" % arch.name()) -except (gdb.error, AttributeError): - print("SKIPPING (not connected)", file=sys.stderr) - exit(0) -if gdb.parse_and_eval('$pc') == 0: - print("SKIP: PC not set") - 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 (gdb.error): - print ("GDB Exception: %s" % (sys.exc_info()[0])) - failcount += 1 - pass - -print("All tests complete: %d failures" % failcount) -exit(failcount) +main(run_test) diff --git a/tests/tcg/multiarch/gdbstub/test-proc-mappings.py b/tests/tcg/multiarch/gdbstub/test-proc-mappings.py new file mode 100644 index 0000000000..564613fabf --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/test-proc-mappings.py @@ -0,0 +1,27 @@ +"""Test that gdbstub has access to proc mappings. + +This runs as a sourced script (via -x, via run-test.py).""" +from __future__ import print_function +import gdb +from test_gdbstub import main, report + + +def run_test(): + """Run through the tests one by one""" + try: + mappings = gdb.execute("info proc mappings", False, True) + except gdb.error as exc: + exc_str = str(exc) + if "Not supported on this target." in exc_str: + # Detect failures due to an outstanding issue with how GDB handles + # the x86_64 QEMU's target.xml, which does not contain the + # definition of orig_rax. Skip the test in this case. + print("SKIP: {}".format(exc_str)) + return + raise + report(isinstance(mappings, str), "Fetched the mappings from the inferior") + # Broken with host page size > guest page size + # report("/sha1" in mappings, "Found the test binary name in the mappings") + + +main(run_test) diff --git a/tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py b/tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py index d91e8fdf19..00c26ab4a9 100644 --- a/tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py +++ b/tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py @@ -6,18 +6,8 @@ from __future__ import print_function # import gdb -import sys +from test_gdbstub import main, report -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" @@ -26,32 +16,5 @@ def run_test(): report(isinstance(auxv, str), "Fetched auxv from inferior") report(auxv.find("sha1"), "Found test binary name in auxv") -# -# This runs as the script it sourced (via -x, via run-test.py) -# -try: - inferior = gdb.selected_inferior() - arch = inferior.architecture() - print("ATTACHED: %s" % arch.name()) -except (gdb.error, AttributeError): - print("SKIPPING (not connected)", file=sys.stderr) - exit(0) -if gdb.parse_and_eval('$pc') == 0: - print("SKIP: PC not set") - 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 (gdb.error): - print ("GDB Exception: %s" % (sys.exc_info()[0])) - failcount += 1 - pass - -print("All tests complete: %d failures" % failcount) -exit(failcount) +main(run_test) diff --git a/tests/tcg/multiarch/gdbstub/test-qxfer-siginfo-read.py b/tests/tcg/multiarch/gdbstub/test-qxfer-siginfo-read.py new file mode 100644 index 0000000000..862596b07a --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/test-qxfer-siginfo-read.py @@ -0,0 +1,26 @@ +from __future__ import print_function +# +# Test gdbstub Xfer:siginfo:read stub. +# +# The test runs a binary that causes a SIGSEGV and then looks for additional +# info about the signal through printing GDB's '$_siginfo' special variable, +# which sends a Xfer:siginfo:read query to the gdbstub. +# +# The binary causes a SIGSEGV at dereferencing a pointer with value 0xdeadbeef, +# so the test looks for and checks if this address is correctly reported by the +# gdbstub. +# +# This is launched via tests/guest-debug/run-test.py +# + +import gdb +from test_gdbstub import main, report + +def run_test(): + "Run through the test" + + gdb.execute("continue", False, True) + resp = gdb.execute("print/x $_siginfo", False, True) + report(resp.find("si_addr = 0xdeadbeef"), "Found fault address.") + +main(run_test) diff --git a/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py b/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py index 798d508bc7..4d6b6b9fbe 100644 --- a/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py +++ b/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py @@ -6,18 +6,8 @@ from __future__ import print_function # import gdb -import sys +from test_gdbstub import main, report -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" @@ -29,32 +19,5 @@ def run_test(): frame = gdb.selected_frame() report(str(frame.function()) == "thread1_func", "break @ %s"%frame) -# -# This runs as the script it sourced (via -x, via run-test.py) -# -try: - inferior = gdb.selected_inferior() - arch = inferior.architecture() - print("ATTACHED: %s" % arch.name()) -except (gdb.error, AttributeError): - print("SKIPPING (not connected)", file=sys.stderr) - exit(0) -if gdb.parse_and_eval('$pc') == 0: - print("SKIP: PC not set") - 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 (gdb.error): - print ("GDB Exception: %s" % (sys.exc_info()[0])) - failcount += 1 - pass - -print("All tests complete: %d failures" % failcount) -exit(failcount) +main(run_test) diff --git a/tests/tcg/multiarch/libs/float_helpers.c b/tests/tcg/multiarch/libs/float_helpers.c index 4e68d2b659..fad5fc9893 100644 --- a/tests/tcg/multiarch/libs/float_helpers.c +++ b/tests/tcg/multiarch/libs/float_helpers.c @@ -5,9 +5,9 @@ * floating point constants useful for exercising the edge cases in * floating point tests. * - * Copyright (c) 2019 Linaro + * Copyright (c) 2019, 2024 Linaro * - * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later */ /* we want additional float type definitions */ diff --git a/tests/tcg/multiarch/linux/linux-madvise.c b/tests/tcg/multiarch/linux/linux-madvise.c new file mode 100644 index 0000000000..539fb3b772 --- /dev/null +++ b/tests/tcg/multiarch/linux/linux-madvise.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include + +static void test_anonymous(void) +{ + int pagesize = getpagesize(); + char *page; + int ret; + + page = mmap(NULL, pagesize, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + assert(page != MAP_FAILED); + + /* Check that mprotect() does not interfere with MADV_DONTNEED. */ + ret = mprotect(page, pagesize, PROT_READ | PROT_WRITE); + assert(ret == 0); + + /* Check that MADV_DONTNEED clears the page. */ + *page = 42; + ret = madvise(page, pagesize, MADV_DONTNEED); + assert(ret == 0); + assert(*page == 0); + + ret = munmap(page, pagesize); + assert(ret == 0); +} + +static void test_file(void) +{ + char tempname[] = "/tmp/.cmadviseXXXXXX"; + int pagesize = getpagesize(); + ssize_t written; + char c = 42; + char *page; + int ret; + int fd; + + fd = mkstemp(tempname); + assert(fd != -1); + ret = unlink(tempname); + assert(ret == 0); + written = write(fd, &c, sizeof(c)); + assert(written == sizeof(c)); + ret = ftruncate(fd, pagesize); + assert(ret == 0); + page = mmap(NULL, pagesize, PROT_READ, MAP_PRIVATE, fd, 0); + assert(page != MAP_FAILED); + + /* Check that mprotect() does not interfere with MADV_DONTNEED. */ + ret = mprotect(page, pagesize, PROT_READ | PROT_WRITE); + assert(ret == 0); + + /* Check that MADV_DONTNEED resets the page. */ + *page = 0; + ret = madvise(page, pagesize, MADV_DONTNEED); + assert(ret == 0); + assert(*page == c); + + ret = munmap(page, pagesize); + assert(ret == 0); + ret = close(fd); + assert(ret == 0); +} + +int main(void) +{ + test_anonymous(); + test_file(); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/multiarch/linux/linux-shmat-maps.c b/tests/tcg/multiarch/linux/linux-shmat-maps.c new file mode 100644 index 0000000000..0ccf7a973a --- /dev/null +++ b/tests/tcg/multiarch/linux/linux-shmat-maps.c @@ -0,0 +1,55 @@ +/* + * Test that shmat() does not break /proc/self/maps. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include +#include + +int main(void) +{ + char buf[128]; + int err, fd; + int shmid; + ssize_t n; + void *p; + + shmid = shmget(IPC_PRIVATE, 1, IPC_CREAT | 0600); + assert(shmid != -1); + + /* + * The original bug required a non-NULL address, which skipped the + * mmap_find_vma step, which could result in a host mapping smaller + * than the target mapping. Choose an address at random. + */ + p = shmat(shmid, (void *)0x800000, SHM_RND); + if (p == (void *)-1) { + /* + * Because we are now running the testcase for all guests for which + * we have a cross-compiler, the above random address might conflict + * with the guest executable in some way. Rather than stopping, + * continue with a system supplied address, which should never fail. + */ + p = shmat(shmid, NULL, 0); + assert(p != (void *)-1); + } + + fd = open("/proc/self/maps", O_RDONLY); + assert(fd != -1); + do { + n = read(fd, buf, sizeof(buf)); + assert(n >= 0); + } while (n != 0); + close(fd); + + err = shmdt(p); + assert(err == 0); + err = shmctl(shmid, IPC_RMID, NULL); + assert(err == 0); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/multiarch/linux/linux-shmat-null.c b/tests/tcg/multiarch/linux/linux-shmat-null.c new file mode 100644 index 0000000000..94eaaec371 --- /dev/null +++ b/tests/tcg/multiarch/linux/linux-shmat-null.c @@ -0,0 +1,38 @@ +/* + * Test shmat(NULL). + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include + +int main(void) +{ + int shmid; + char *p; + int err; + + /* Create, attach and intialize shared memory. */ + shmid = shmget(IPC_PRIVATE, 1, IPC_CREAT | 0600); + assert(shmid != -1); + p = shmat(shmid, NULL, 0); + assert(p != (void *)-1); + *p = 42; + + /* Reattach, check that the value is still there. */ + err = shmdt(p); + assert(err == 0); + p = shmat(shmid, NULL, 0); + assert(p != (void *)-1); + assert(*p == 42); + + /* Detach. */ + err = shmdt(p); + assert(err == 0); + err = shmctl(shmid, IPC_RMID, NULL); + assert(err == 0); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/multiarch/linux/linux-test.c b/tests/tcg/multiarch/linux/linux-test.c index 019d8175ca..64f57cb287 100644 --- a/tests/tcg/multiarch/linux/linux-test.c +++ b/tests/tcg/multiarch/linux/linux-test.c @@ -263,7 +263,7 @@ static int server_socket(void) sockaddr.sin_port = htons(0); /* choose random ephemeral port) */ sockaddr.sin_addr.s_addr = 0; chk_error(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))); - chk_error(listen(fd, 0)); + chk_error(listen(fd, 1)); return fd; } @@ -354,13 +354,17 @@ static void test_pipe(void) if (FD_ISSET(fds[0], &rfds)) { chk_error(read(fds[0], &ch, 1)); rcount++; - if (rcount >= WCOUNT_MAX) + if (rcount >= WCOUNT_MAX) { break; + } } if (FD_ISSET(fds[1], &wfds)) { ch = 'a'; chk_error(write(fds[1], &ch, 1)); wcount++; + if (wcount >= WCOUNT_MAX) { + break; + } } } } diff --git a/tests/tcg/multiarch/munmap-pthread.c b/tests/tcg/multiarch/munmap-pthread.c new file mode 100644 index 0000000000..1c79005846 --- /dev/null +++ b/tests/tcg/multiarch/munmap-pthread.c @@ -0,0 +1,65 @@ +/* Test that munmap() and thread creation do not race. */ +#include +#include +#include +#include +#include +#include +#include + +#include "nop_func.h" + +static void *thread_mmap_munmap(void *arg) +{ + volatile bool *run = arg; + char *p; + int ret; + + while (*run) { + p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert(p != MAP_FAILED); + + /* Create a small translation block. */ + memcpy(p, nop_func, sizeof(nop_func)); + ((void(*)(void))p)(); + + ret = munmap(p, getpagesize()); + assert(ret == 0); + } + + return NULL; +} + +static void *thread_dummy(void *arg) +{ + return NULL; +} + +int main(void) +{ + pthread_t mmap_munmap, dummy; + volatile bool run = true; + int i, ret; + + /* Without a template, nothing to test. */ + if (sizeof(nop_func) == 0) { + return EXIT_SUCCESS; + } + + ret = pthread_create(&mmap_munmap, NULL, thread_mmap_munmap, (void *)&run); + assert(ret == 0); + + for (i = 0; i < 1000; i++) { + ret = pthread_create(&dummy, NULL, thread_dummy, NULL); + assert(ret == 0); + ret = pthread_join(dummy, NULL); + assert(ret == 0); + } + + run = false; + ret = pthread_join(mmap_munmap, NULL); + assert(ret == 0); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/multiarch/noexec.c.inc b/tests/tcg/multiarch/noexec.c.inc new file mode 100644 index 0000000000..2ef539b721 --- /dev/null +++ b/tests/tcg/multiarch/noexec.c.inc @@ -0,0 +1,139 @@ +/* + * Common code for arch-specific MMU_INST_FETCH fault testing. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Forward declarations. */ + +static void *arch_mcontext_pc(const mcontext_t *ctx); +static int arch_mcontext_arg(const mcontext_t *ctx); +static void arch_flush(void *p, int len); + +/* Testing infrastructure. */ + +struct noexec_test { + const char *name; + const char *test_code; + int test_len; + int page_ofs; + int entry_ofs; + int expected_si_ofs; + int expected_pc_ofs; + int expected_arg; +}; + +static void *page_base; +static int page_size; +static const struct noexec_test *current_noexec_test; + +static void handle_err(const char *syscall) +{ + printf("[ FAILED ] %s: %s\n", syscall, strerror(errno)); + exit(EXIT_FAILURE); +} + +static void handle_segv(int sig, siginfo_t *info, void *ucontext) +{ + const struct noexec_test *test = current_noexec_test; + const mcontext_t *mc = &((ucontext_t *)ucontext)->uc_mcontext; + void *expected_si; + void *expected_pc; + void *pc; + int arg; + + if (test == NULL) { + printf("[ FAILED ] unexpected SEGV\n"); + exit(EXIT_FAILURE); + } + current_noexec_test = NULL; + + expected_si = page_base + test->expected_si_ofs; + if (info->si_addr != expected_si) { + printf("[ FAILED ] wrong si_addr (%p != %p)\n", + info->si_addr, expected_si); + exit(EXIT_FAILURE); + } + + pc = arch_mcontext_pc(mc); + expected_pc = page_base + test->expected_pc_ofs; + if (pc != expected_pc) { + printf("[ FAILED ] wrong pc (%p != %p)\n", pc, expected_pc); + exit(EXIT_FAILURE); + } + + arg = arch_mcontext_arg(mc); + if (arg != test->expected_arg) { + printf("[ FAILED ] wrong arg (%d != %d)\n", arg, test->expected_arg); + exit(EXIT_FAILURE); + } + + if (mprotect(page_base, page_size, + PROT_READ | PROT_WRITE | PROT_EXEC) < 0) { + handle_err("mprotect"); + } +} + +static void test_noexec_1(const struct noexec_test *test) +{ + void *start = page_base + test->page_ofs; + void (*fn)(int arg) = page_base + test->entry_ofs; + + memcpy(start, test->test_code, test->test_len); + arch_flush(start, test->test_len); + + /* Trigger TB creation in order to test invalidation. */ + fn(0); + + if (mprotect(page_base, page_size, PROT_NONE) < 0) { + handle_err("mprotect"); + } + + /* Trigger SEGV and check that handle_segv() ran. */ + current_noexec_test = test; + fn(0); + assert(current_noexec_test == NULL); +} + +static int test_noexec(struct noexec_test *tests, size_t n_tests) +{ + struct sigaction act; + size_t i; + + memset(&act, 0, sizeof(act)); + act.sa_sigaction = handle_segv; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGSEGV, &act, NULL) < 0) { + handle_err("sigaction"); + } + + page_size = getpagesize(); + page_base = mmap(NULL, 2 * page_size, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (page_base == MAP_FAILED) { + handle_err("mmap"); + } + page_base += page_size; + + for (i = 0; i < n_tests; i++) { + struct noexec_test *test = &tests[i]; + + printf("[ RUN ] %s\n", test->name); + test_noexec_1(test); + printf("[ OK ]\n"); + } + + printf("[ PASSED ]\n"); + return EXIT_SUCCESS; +} diff --git a/tests/tcg/multiarch/nop_func.h b/tests/tcg/multiarch/nop_func.h new file mode 100644 index 0000000000..f714d21000 --- /dev/null +++ b/tests/tcg/multiarch/nop_func.h @@ -0,0 +1,25 @@ +/* + * No-op functions that can be safely copied. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef NOP_FUNC_H +#define NOP_FUNC_H + +static const char nop_func[] = { +#if defined(__aarch64__) + 0xc0, 0x03, 0x5f, 0xd6, /* ret */ +#elif defined(__alpha__) + 0x01, 0x80, 0xFA, 0x6B, /* ret */ +#elif defined(__arm__) + 0x1e, 0xff, 0x2f, 0xe1, /* bx lr */ +#elif defined(__riscv) + 0x67, 0x80, 0x00, 0x00, /* ret */ +#elif defined(__s390__) + 0x07, 0xfe, /* br %r14 */ +#elif defined(__i386__) || defined(__x86_64__) + 0xc3, /* ret */ +#endif +}; + +#endif diff --git a/tests/tcg/multiarch/prot-none.c b/tests/tcg/multiarch/prot-none.c new file mode 100644 index 0000000000..dc56aadb3c --- /dev/null +++ b/tests/tcg/multiarch/prot-none.c @@ -0,0 +1,40 @@ +/* + * Test that GDB can access PROT_NONE pages. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include + +void break_here(void *q) +{ +} + +int main(void) +{ + long pagesize = sysconf(_SC_PAGESIZE); + void *p, *q; + int err; + + p = mmap(NULL, pagesize * 2, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert(p != MAP_FAILED); + q = p + pagesize - 1; + strcpy(q, "42"); + + err = mprotect(p, pagesize * 2, PROT_NONE); + assert(err == 0); + + break_here(q); + + err = mprotect(p, pagesize * 2, PROT_READ); + assert(err == 0); + if (getenv("PROT_NONE_PY")) { + assert(strcmp(q, "24") == 0); + } + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/multiarch/segfault.c b/tests/tcg/multiarch/segfault.c new file mode 100644 index 0000000000..e6c8ff31ca --- /dev/null +++ b/tests/tcg/multiarch/segfault.c @@ -0,0 +1,14 @@ +#include +#include + +/* Cause a segfault for testing purposes. */ + +int main(int argc, char *argv[]) +{ + int *ptr = (void *)0xdeadbeef; + + if (argc == 2 && strcmp(argv[1], "-s") == 0) { + /* Cause segfault. */ + printf("%d\n", *ptr); + } +} diff --git a/tests/tcg/multiarch/sha512.c b/tests/tcg/multiarch/sha512.c index e1729828b9..12c2b6c2b7 100644 --- a/tests/tcg/multiarch/sha512.c +++ b/tests/tcg/multiarch/sha512.c @@ -453,7 +453,7 @@ void sha512(struct sha512 *sha, const void *p, size_t size) /* From hex.h */ /** * hex_decode - Unpack a hex string. - * @str: the hexidecimal string + * @str: the hexadecimal string * @slen: the length of @str * @buf: the buffer to write the data into * @bufsize: the length of @buf @@ -855,8 +855,6 @@ plan_tests(unsigned int tests) static int exit_status_(void) { - int r; - /* If there's no plan, just return the number of failures */ if(no_plan || !have_plan) { return failures; @@ -865,15 +863,12 @@ exit_status_(void) /* Ran too many tests? Return the number of tests that were run that shouldn't have been */ if(e_tests < test_count) { - r = test_count - e_tests; - return r; + return test_count - e_tests; } /* Return the number of tests that failed + the number of tests that weren't run */ - r = failures + e_tests - test_count; - - return r; + return failures + e_tests - test_count; } int diff --git a/tests/tcg/multiarch/sigbus.c b/tests/tcg/multiarch/sigbus.c index 8134c5fd56..f47c7390e7 100644 --- a/tests/tcg/multiarch/sigbus.c +++ b/tests/tcg/multiarch/sigbus.c @@ -6,8 +6,13 @@ #include -unsigned long long x = 0x8877665544332211ull; -void * volatile p = (void *)&x + 1; +char x[32] __attribute__((aligned(16))) = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, +}; +void * volatile p = (void *)&x + 15; void sigbus(int sig, siginfo_t *info, void *uc) { @@ -60,9 +65,9 @@ int main() * We might as well validate the unaligned load worked. */ if (BYTE_ORDER == LITTLE_ENDIAN) { - assert(tmp == 0x55443322); + assert(tmp == 0x13121110); } else { - assert(tmp == 0x77665544); + assert(tmp == 0x10111213); } return EXIT_SUCCESS; } diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target index 625ed792c6..32dc0f9830 100644 --- a/tests/tcg/multiarch/system/Makefile.softmmu-target +++ b/tests/tcg/multiarch/system/Makefile.softmmu-target @@ -3,7 +3,7 @@ # Multiarch system tests # # We just collect the tests together here and rely on the actual guest -# architecture to add to the test dependancies and deal with the +# architecture to add to the test dependencies and deal with the # complications of building. # @@ -14,22 +14,54 @@ VPATH+=$(MULTIARCH_SYSTEM_SRC) MULTIARCH_TEST_SRCS=$(wildcard $(MULTIARCH_SYSTEM_SRC)/*.c) MULTIARCH_TESTS = $(patsubst $(MULTIARCH_SYSTEM_SRC)/%.c, %, $(MULTIARCH_TEST_SRCS)) -ifneq ($(HAVE_GDB_BIN),) +ifneq ($(GDB),) GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py run-gdbstub-memory: memory $(call run-test, $@, $(GDB_SCRIPT) \ - --gdb $(HAVE_GDB_BIN) \ + --gdb $(GDB) \ --qemu $(QEMU) \ --output $<.gdb.out \ --qargs \ "-monitor none -display none -chardev file$(COMMA)path=$<.out$(COMMA)id=output $(QEMU_OPTS)" \ --bin $< --test $(MULTIARCH_SRC)/gdbstub/memory.py, \ - "softmmu gdbstub support") + softmmu gdbstub support) +run-gdbstub-interrupt: interrupt + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) \ + --output $<.gdb.out \ + --qargs \ + "-smp 2 -monitor none -display none -chardev file$(COMMA)path=$<.out$(COMMA)id=output $(QEMU_OPTS)" \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/interrupt.py, \ + softmmu gdbstub support) +run-gdbstub-untimely-packet: hello + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --gdb-args "-ex 'set debug remote 1'" \ + --output untimely-packet.gdb.out \ + --stderr untimely-packet.gdb.err \ + --qemu $(QEMU) \ + --bin $< --qargs \ + "-monitor none -display none -chardev file$(COMMA)path=untimely-packet.out$(COMMA)id=output $(QEMU_OPTS)", \ + softmmu gdbstub untimely packets) + $(call quiet-command, \ + (! grep -Fq 'Packet instead of Ack, ignoring it' untimely-packet.gdb.err), \ + "GREP", file untimely-packet.gdb.err) +run-gdbstub-registers: memory + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) \ + --output $<.registers.gdb.out \ + --qargs \ + "-monitor none -display none -chardev file$(COMMA)path=$<.out$(COMMA)id=output $(QEMU_OPTS)" \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/registers.py, \ + softmmu gdbstub support) else run-gdbstub-%: - $(call skip-test, "gdbstub test $*", "need working gdb") + $(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst -%,,$(TARGET_NAME)) support") endif -MULTIARCH_RUNS += run-gdbstub-memory +MULTIARCH_RUNS += run-gdbstub-memory run-gdbstub-interrupt \ + run-gdbstub-untimely-packet run-gdbstub-registers diff --git a/tests/tcg/multiarch/system/interrupt.c b/tests/tcg/multiarch/system/interrupt.c new file mode 100644 index 0000000000..98d4f2eff9 --- /dev/null +++ b/tests/tcg/multiarch/system/interrupt.c @@ -0,0 +1,28 @@ +/* + * External interruption test. This test is structured in such a way that it + * passes the cases that require it to exit, but we can make it enter an + * infinite loop from GDB. + * + * We don't have the benefit of libc, just builtin C primitives and + * whatever is in minilib. + */ + +#include + +void loop(void) +{ + do { + /* + * Loop forever. Just make sure the condition is always a constant + * expression, so that this loop is not UB, as per the C + * standard. + */ + } while (1); +} + +int main(void) +{ + return 0; +} + + diff --git a/tests/tcg/multiarch/system/memory.c b/tests/tcg/multiarch/system/memory.c index 41c7f66e2e..6eb2eb16f7 100644 --- a/tests/tcg/multiarch/system/memory.c +++ b/tests/tcg/multiarch/system/memory.c @@ -1,18 +1,18 @@ /* * Memory Test * - * This is intended to test the softmmu code and ensure we properly + * This is intended to test the system-mode code and ensure we properly * behave across normal and unaligned accesses across several pages. * We are not replicating memory tests for stuck bits and other * hardware level failures but looking for issues with different size * accesses when access is: * * - unaligned at various sizes (if -DCHECK_UNALIGNED set) - * - spanning a (softmmu) page + * - spanning a (system) page * - sign extension when loading */ -#include +#include #include #include @@ -40,18 +40,21 @@ static void pdot(int count) } /* - * Helper macros for shift/extract so we can keep our endian handling - * in one place. + * Helper macros for endian handling. */ -#define BYTE_SHIFT(b, pos) ((uint64_t)b << (pos * 8)) -#define BYTE_EXTRACT(b, pos) ((b >> (pos * 8)) & 0xff) +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define BYTE_SHIFT(b, pos) (b << (pos * 8)) +#define BYTE_NEXT(b) ((b)++) +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define BYTE_SHIFT(b, pos) (b << ((sizeof(b) - 1 - (pos)) * 8)) +#define BYTE_NEXT(b) (--(b)) +#else +#error Unsupported __BYTE_ORDER__ +#endif /* - * Fill the data with ascending value bytes. - * - * Currently we only support Little Endian machines so write in - * ascending address order. When we read higher address bytes should - * either be zero or higher than the lower bytes. + * Fill the data with ascending (for little-endian) or descending (for + * big-endian) value bytes. */ static void init_test_data_u8(int unused_offset) @@ -62,14 +65,14 @@ static void init_test_data_u8(int unused_offset) ml_printf("Filling test area with u8:"); for (i = 0; i < TEST_SIZE; i++) { - *ptr++ = count++; + *ptr++ = BYTE_NEXT(count); pdot(i); } ml_printf("done\n"); } /* - * Full the data with alternating positive and negative bytes. This + * Fill the data with alternating positive and negative bytes. This * should mean for reads larger than a byte all subsequent reads will * stay either negative or positive. We never write 0. */ @@ -119,7 +122,7 @@ static void init_test_data_u16(int offset) reset_start_data(offset); for (i = 0; i < max; i++) { - uint8_t low = count++, high = count++; + uint16_t low = BYTE_NEXT(count), high = BYTE_NEXT(count); word = BYTE_SHIFT(high, 1) | BYTE_SHIFT(low, 0); *ptr++ = word; pdot(i); @@ -139,9 +142,10 @@ static void init_test_data_u32(int offset) reset_start_data(offset); for (i = 0; i < max; i++) { - uint8_t b4 = count++, b3 = count++; - uint8_t b2 = count++, b1 = count++; - word = BYTE_SHIFT(b1, 3) | BYTE_SHIFT(b2, 2) | BYTE_SHIFT(b3, 1) | b4; + uint32_t b4 = BYTE_NEXT(count), b3 = BYTE_NEXT(count); + uint32_t b2 = BYTE_NEXT(count), b1 = BYTE_NEXT(count); + word = BYTE_SHIFT(b1, 3) | BYTE_SHIFT(b2, 2) | BYTE_SHIFT(b3, 1) | + BYTE_SHIFT(b4, 0); *ptr++ = word; pdot(i); } @@ -160,13 +164,13 @@ static void init_test_data_u64(int offset) reset_start_data(offset); for (i = 0; i < max; i++) { - uint8_t b8 = count++, b7 = count++; - uint8_t b6 = count++, b5 = count++; - uint8_t b4 = count++, b3 = count++; - uint8_t b2 = count++, b1 = count++; + uint64_t b8 = BYTE_NEXT(count), b7 = BYTE_NEXT(count); + uint64_t b6 = BYTE_NEXT(count), b5 = BYTE_NEXT(count); + uint64_t b4 = BYTE_NEXT(count), b3 = BYTE_NEXT(count); + uint64_t b2 = BYTE_NEXT(count), b1 = BYTE_NEXT(count); word = BYTE_SHIFT(b1, 7) | BYTE_SHIFT(b2, 6) | BYTE_SHIFT(b3, 5) | BYTE_SHIFT(b4, 4) | BYTE_SHIFT(b5, 3) | BYTE_SHIFT(b6, 2) | - BYTE_SHIFT(b7, 1) | b8; + BYTE_SHIFT(b7, 1) | BYTE_SHIFT(b8, 0); *ptr++ = word; pdot(i); } @@ -374,12 +378,20 @@ static bool read_test_data_s16(int offset, bool neg_first) ml_printf("Reading s16 from %#lx (offset %d, %s):", ptr, offset, neg_first ? "neg" : "pos"); + /* + * If the first byte is negative, then the last byte is positive. + * Therefore the logic below must be flipped for big-endian. + */ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + neg_first = !neg_first; +#endif + for (i = 0; i < max; i++) { int32_t data = *ptr++; if (neg_first && data < 0) { pdot(i); - } else if (data > 0) { + } else if (!neg_first && data > 0) { pdot(i); } else { ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>'); @@ -399,12 +411,20 @@ static bool read_test_data_s32(int offset, bool neg_first) ml_printf("Reading s32 from %#lx (offset %d, %s):", ptr, offset, neg_first ? "neg" : "pos"); + /* + * If the first byte is negative, then the last byte is positive. + * Therefore the logic below must be flipped for big-endian. + */ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + neg_first = !neg_first; +#endif + for (i = 0; i < max; i++) { int64_t data = *ptr++; if (neg_first && data < 0) { pdot(i); - } else if (data > 0) { + } else if (!neg_first && data > 0) { pdot(i); } else { ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>'); @@ -419,8 +439,7 @@ static bool read_test_data_s32(int offset, bool neg_first) * Read the test data and verify at various offsets * * For everything except bytes all our reads should be either positive - * or negative depending on what offset we are reading from. Currently - * we only handle LE systems. + * or negative depending on what offset we are reading from. */ read_sfn read_sfns[] = { read_test_data_s8, read_test_data_s16, diff --git a/tests/tcg/multiarch/test-aes-main.c.inc b/tests/tcg/multiarch/test-aes-main.c.inc new file mode 100644 index 0000000000..4b5f7f98aa --- /dev/null +++ b/tests/tcg/multiarch/test-aes-main.c.inc @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include +#include +#include + +static bool test_SB_SR(uint8_t *o, const uint8_t *i); +static bool test_MC(uint8_t *o, const uint8_t *i); +static bool test_SB_SR_MC_AK(uint8_t *o, const uint8_t *i, const uint8_t *k); + +static bool test_ISB_ISR(uint8_t *o, const uint8_t *i); +static bool test_IMC(uint8_t *o, const uint8_t *i); +static bool test_ISB_ISR_AK_IMC(uint8_t *o, const uint8_t *i, const uint8_t *k); +static bool test_ISB_ISR_IMC_AK(uint8_t *o, const uint8_t *i, const uint8_t *k); + +/* + * From https://doi.org/10.6028/NIST.FIPS.197-upd1, + * Appendix B -- Cipher Example + * + * Note that the formatting of the 4x4 matrices in the document is + * column-major, whereas C is row-major. Therefore to get the bytes + * in the same order as the text, the matrices are transposed. + * + * Note that we are not going to test SubBytes or ShiftRows separately, + * so the "After SubBytes" column is omitted, using only the combined + * result "After ShiftRows" column. + */ + +/* Ease the inline assembly by aligning everything. */ +typedef struct { + uint8_t b[16] __attribute__((aligned(16))); +} State; + +typedef struct { + State start, after_sr, after_mc, round_key; +} Round; + +static const Round rounds[] = { + /* Round 1 */ + { { { 0x19, 0x3d, 0xe3, 0xbe, /* start */ + 0xa0, 0xf4, 0xe2, 0x2b, + 0x9a, 0xc6, 0x8d, 0x2a, + 0xe9, 0xf8, 0x48, 0x08, } }, + + { { 0xd4, 0xbf, 0x5d, 0x30, /* after shiftrows */ + 0xe0, 0xb4, 0x52, 0xae, + 0xb8, 0x41, 0x11, 0xf1, + 0x1e, 0x27, 0x98, 0xe5, } }, + + { { 0x04, 0x66, 0x81, 0xe5, /* after mixcolumns */ + 0xe0, 0xcb, 0x19, 0x9a, + 0x48, 0xf8, 0xd3, 0x7a, + 0x28, 0x06, 0x26, 0x4c, } }, + + { { 0xa0, 0xfa, 0xfe, 0x17, /* round key */ + 0x88, 0x54, 0x2c, 0xb1, + 0x23, 0xa3, 0x39, 0x39, + 0x2a, 0x6c, 0x76, 0x05, } } }, + + /* Round 2 */ + { { { 0xa4, 0x9c, 0x7f, 0xf2, /* start */ + 0x68, 0x9f, 0x35, 0x2b, + 0x6b, 0x5b, 0xea, 0x43, + 0x02, 0x6a, 0x50, 0x49, } }, + + { { 0x49, 0xdb, 0x87, 0x3b, /* after shiftrows */ + 0x45, 0x39, 0x53, 0x89, + 0x7f, 0x02, 0xd2, 0xf1, + 0x77, 0xde, 0x96, 0x1a, } }, + + { { 0x58, 0x4d, 0xca, 0xf1, /* after mixcolumns */ + 0x1b, 0x4b, 0x5a, 0xac, + 0xdb, 0xe7, 0xca, 0xa8, + 0x1b, 0x6b, 0xb0, 0xe5, } }, + + { { 0xf2, 0xc2, 0x95, 0xf2, /* round key */ + 0x7a, 0x96, 0xb9, 0x43, + 0x59, 0x35, 0x80, 0x7a, + 0x73, 0x59, 0xf6, 0x7f, } } }, + + /* Round 3 */ + { { { 0xaa, 0x8f, 0x5f, 0x03, /* start */ + 0x61, 0xdd, 0xe3, 0xef, + 0x82, 0xd2, 0x4a, 0xd2, + 0x68, 0x32, 0x46, 0x9a, } }, + + { { 0xac, 0xc1, 0xd6, 0xb8, /* after shiftrows */ + 0xef, 0xb5, 0x5a, 0x7b, + 0x13, 0x23, 0xcf, 0xdf, + 0x45, 0x73, 0x11, 0xb5, } }, + + { { 0x75, 0xec, 0x09, 0x93, /* after mixcolumns */ + 0x20, 0x0b, 0x63, 0x33, + 0x53, 0xc0, 0xcf, 0x7c, + 0xbb, 0x25, 0xd0, 0xdc, } }, + + { { 0x3d, 0x80, 0x47, 0x7d, /* round key */ + 0x47, 0x16, 0xfe, 0x3e, + 0x1e, 0x23, 0x7e, 0x44, + 0x6d, 0x7a, 0x88, 0x3b, } } }, +}; + +static void verify_log(const char *prefix, const State *s) +{ + printf("%s:", prefix); + for (int i = 0; i < sizeof(State); ++i) { + printf(" %02x", s->b[i]); + } + printf("\n"); +} + +static void verify(const State *ref, const State *tst, const char *which) +{ + if (!memcmp(ref, tst, sizeof(State))) { + return; + } + + printf("Mismatch on %s\n", which); + verify_log("ref", ref); + verify_log("tst", tst); + exit(EXIT_FAILURE); +} + +int main() +{ + int i, n = sizeof(rounds) / sizeof(Round); + State t; + + for (i = 0; i < n; ++i) { + if (test_SB_SR(t.b, rounds[i].start.b)) { + verify(&rounds[i].after_sr, &t, "SB+SR"); + } + } + + for (i = 0; i < n; ++i) { + if (test_MC(t.b, rounds[i].after_sr.b)) { + verify(&rounds[i].after_mc, &t, "MC"); + } + } + + /* The kernel of Cipher(). */ + for (i = 0; i < n - 1; ++i) { + if (test_SB_SR_MC_AK(t.b, rounds[i].start.b, rounds[i].round_key.b)) { + verify(&rounds[i + 1].start, &t, "SB+SR+MC+AK"); + } + } + + for (i = 0; i < n; ++i) { + if (test_ISB_ISR(t.b, rounds[i].after_sr.b)) { + verify(&rounds[i].start, &t, "ISB+ISR"); + } + } + + for (i = 0; i < n; ++i) { + if (test_IMC(t.b, rounds[i].after_mc.b)) { + verify(&rounds[i].after_sr, &t, "IMC"); + } + } + + /* The kernel of InvCipher(). */ + for (i = n - 1; i > 0; --i) { + if (test_ISB_ISR_AK_IMC(t.b, rounds[i].after_sr.b, + rounds[i - 1].round_key.b)) { + verify(&rounds[i - 1].after_sr, &t, "ISB+ISR+AK+IMC"); + } + } + + /* + * The kernel of EqInvCipher(). + * We must compute a different round key: apply InvMixColumns to + * the standard round key, per KeyExpansion vs KeyExpansionEIC. + */ + for (i = 1; i < n; ++i) { + if (test_IMC(t.b, rounds[i - 1].round_key.b) && + test_ISB_ISR_IMC_AK(t.b, rounds[i].after_sr.b, t.b)) { + verify(&rounds[i - 1].after_sr, &t, "ISB+ISR+IMC+AK"); + } + } + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/multiarch/test-vma.c b/tests/tcg/multiarch/test-vma.c new file mode 100644 index 0000000000..2893d60334 --- /dev/null +++ b/tests/tcg/multiarch/test-vma.c @@ -0,0 +1,22 @@ +/* + * Test very large vma allocations. + * The qemu out-of-memory condition was within the mmap syscall itself. + * If the syscall actually returns with MAP_FAILED, the test succeeded. + */ +#include + +int main() +{ + int n = sizeof(size_t) == 4 ? 32 : 45; + + for (int i = 28; i < n; i++) { + size_t l = (size_t)1 << i; + void *p = mmap(0, l, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); + if (p == MAP_FAILED) { + break; + } + munmap(p, l); + } + return 0; +} diff --git a/tests/tcg/multiarch/vma-pthread.c b/tests/tcg/multiarch/vma-pthread.c new file mode 100644 index 0000000000..7045da08fc --- /dev/null +++ b/tests/tcg/multiarch/vma-pthread.c @@ -0,0 +1,207 @@ +/* + * Test that VMA updates do not race. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Map a contiguous chunk of RWX memory. Split it into 8 equally sized + * regions, each of which is guaranteed to have a certain combination of + * protection bits set. + * + * Reader, writer and executor threads perform the respective operations on + * pages, which are guaranteed to have the respective protection bit set. + * Two mutator threads change the non-fixed protection bits randomly. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nop_func.h" + +#define PAGE_IDX_BITS 10 +#define PAGE_COUNT (1 << PAGE_IDX_BITS) +#define PAGE_IDX_MASK (PAGE_COUNT - 1) +#define REGION_IDX_BITS 3 +#define PAGE_IDX_R_MASK (1 << 7) +#define PAGE_IDX_W_MASK (1 << 8) +#define PAGE_IDX_X_MASK (1 << 9) +#define REGION_MASK (PAGE_IDX_R_MASK | PAGE_IDX_W_MASK | PAGE_IDX_X_MASK) +#define PAGES_PER_REGION (1 << (PAGE_IDX_BITS - REGION_IDX_BITS)) + +struct context { + int pagesize; + char *ptr; + int dev_null_fd; + volatile int mutator_count; +}; + +static void *thread_read(void *arg) +{ + struct context *ctx = arg; + ssize_t sret; + size_t i, j; + int ret; + + for (i = 0; ctx->mutator_count; i++) { + char *p; + + j = (i & PAGE_IDX_MASK) | PAGE_IDX_R_MASK; + p = &ctx->ptr[j * ctx->pagesize]; + + /* Read directly. */ + ret = memcmp(p, nop_func, sizeof(nop_func)); + if (ret != 0) { + fprintf(stderr, "fail direct read %p\n", p); + abort(); + } + + /* Read indirectly. */ + sret = write(ctx->dev_null_fd, p, 1); + if (sret != 1) { + if (sret < 0) { + fprintf(stderr, "fail indirect read %p (%m)\n", p); + } else { + fprintf(stderr, "fail indirect read %p (%zd)\n", p, sret); + } + abort(); + } + } + + return NULL; +} + +static void *thread_write(void *arg) +{ + struct context *ctx = arg; + struct timespec *ts; + size_t i, j; + int ret; + + for (i = 0; ctx->mutator_count; i++) { + j = (i & PAGE_IDX_MASK) | PAGE_IDX_W_MASK; + + /* Write directly. */ + memcpy(&ctx->ptr[j * ctx->pagesize], nop_func, sizeof(nop_func)); + + /* Write using a syscall. */ + ts = (struct timespec *)(&ctx->ptr[(j + 1) * ctx->pagesize] - + sizeof(struct timespec)); + ret = clock_gettime(CLOCK_REALTIME, ts); + if (ret != 0) { + fprintf(stderr, "fail indirect write %p (%m)\n", ts); + abort(); + } + } + + return NULL; +} + +static void *thread_execute(void *arg) +{ + struct context *ctx = arg; + size_t i, j; + + for (i = 0; ctx->mutator_count; i++) { + j = (i & PAGE_IDX_MASK) | PAGE_IDX_X_MASK; + ((void(*)(void))&ctx->ptr[j * ctx->pagesize])(); + } + + return NULL; +} + +static void *thread_mutate(void *arg) +{ + size_t i, start_idx, end_idx, page_idx, tmp; + struct context *ctx = arg; + unsigned int seed; + int prot, ret; + + seed = (unsigned int)time(NULL); + for (i = 0; i < 10000; i++) { + start_idx = rand_r(&seed) & PAGE_IDX_MASK; + end_idx = rand_r(&seed) & PAGE_IDX_MASK; + if (start_idx > end_idx) { + tmp = start_idx; + start_idx = end_idx; + end_idx = tmp; + } + prot = rand_r(&seed) & (PROT_READ | PROT_WRITE | PROT_EXEC); + for (page_idx = start_idx & REGION_MASK; page_idx <= end_idx; + page_idx += PAGES_PER_REGION) { + if (page_idx & PAGE_IDX_R_MASK) { + prot |= PROT_READ; + } + if (page_idx & PAGE_IDX_W_MASK) { + /* FIXME: qemu syscalls check for both read+write. */ + prot |= PROT_WRITE | PROT_READ; + } + if (page_idx & PAGE_IDX_X_MASK) { + prot |= PROT_EXEC; + } + } + ret = mprotect(&ctx->ptr[start_idx * ctx->pagesize], + (end_idx - start_idx + 1) * ctx->pagesize, prot); + assert(ret == 0); + } + + __atomic_fetch_sub(&ctx->mutator_count, 1, __ATOMIC_SEQ_CST); + + return NULL; +} + +int main(void) +{ + pthread_t threads[5]; + struct context ctx; + size_t i; + int ret; + + /* Without a template, nothing to test. */ + if (sizeof(nop_func) == 0) { + return EXIT_SUCCESS; + } + + /* Initialize memory chunk. */ + ctx.pagesize = getpagesize(); + ctx.ptr = mmap(NULL, PAGE_COUNT * ctx.pagesize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert(ctx.ptr != MAP_FAILED); + for (i = 0; i < PAGE_COUNT; i++) { + memcpy(&ctx.ptr[i * ctx.pagesize], nop_func, sizeof(nop_func)); + } + ctx.dev_null_fd = open("/dev/null", O_WRONLY); + assert(ctx.dev_null_fd >= 0); + ctx.mutator_count = 2; + + /* Start threads. */ + ret = pthread_create(&threads[0], NULL, thread_read, &ctx); + assert(ret == 0); + ret = pthread_create(&threads[1], NULL, thread_write, &ctx); + assert(ret == 0); + ret = pthread_create(&threads[2], NULL, thread_execute, &ctx); + assert(ret == 0); + for (i = 3; i <= 4; i++) { + ret = pthread_create(&threads[i], NULL, thread_mutate, &ctx); + assert(ret == 0); + } + + /* Wait for threads to stop. */ + for (i = 0; i < sizeof(threads) / sizeof(threads[0]); i++) { + ret = pthread_join(threads[i], NULL); + assert(ret == 0); + } + + /* Destroy memory chunk. */ + ret = close(ctx.dev_null_fd); + assert(ret == 0); + ret = munmap(ctx.ptr, PAGE_COUNT * ctx.pagesize); + assert(ret == 0); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/nios2/10m50-ghrd.ld b/tests/tcg/nios2/10m50-ghrd.ld index 7db0d59ad7..71cdda450c 100644 --- a/tests/tcg/nios2/10m50-ghrd.ld +++ b/tests/tcg/nios2/10m50-ghrd.ld @@ -44,11 +44,15 @@ SECTIONS .data : ALIGN(4) { *(.shdata) *(.data .data.* .gnu.linkonce.d.*) - . = ALIGN(4); - _gp = ABSOLUTE(. + 0x8000); - *(.got.plt) *(.got) - *(.lit8) - *(.lit4) + } >ram :RAM + + HIDDEN (_gp = ALIGN(16) + 0x7ff0); + PROVIDE_HIDDEN (gp = _gp); + .got : ALIGN(4) { + *(.got.plt) *(.igot.plt) *(.got) *(.igot) + } >ram :RAM + + .sdata : ALIGN(4) { *(.sdata .sdata.* .gnu.linkonce.s.*) } >ram :RAM diff --git a/tests/tcg/nios2/Makefile.softmmu-target b/tests/tcg/nios2/Makefile.softmmu-target index c3d0594a39..bc7fd55060 100644 --- a/tests/tcg/nios2/Makefile.softmmu-target +++ b/tests/tcg/nios2/Makefile.softmmu-target @@ -25,8 +25,7 @@ LDFLAGS += -Wl,-T$(LINK_SCRIPT) -static -nostdlib $(CRT_OBJS) -lgcc %: %.o $(LINK_SCRIPT) $(CRT_OBJS) $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS), LD, $@) -# FIXME: nios2 semihosting writes to stdout, not a chardev -QEMU_OPTS = -M 10m50-ghrd,vic=on -semihosting >$@.out -kernel +QEMU_OPTS = -M 10m50-ghrd,vic=on -semihosting-config enable=on,target=native,chardev=output -kernel memory: CFLAGS+=-DCHECK_UNALIGNED=0 TESTS += $(MULTIARCH_TESTS) diff --git a/tests/tcg/nios2/Makefile.target b/tests/tcg/nios2/Makefile.target new file mode 100644 index 0000000000..b38e2352b7 --- /dev/null +++ b/tests/tcg/nios2/Makefile.target @@ -0,0 +1,11 @@ +# nios2 specific test tweaks + +# Currently nios2 signal handling is broken +run-signals: signals + $(call skip-test, $<, "BROKEN") +run-plugin-signals-with-%: + $(call skip-test, $<, "BROKEN") +run-linux-test: linux-test + $(call skip-test, $<, "BROKEN") +run-plugin-linux-test-with-%: + $(call skip-test, $<, "BROKEN") diff --git a/tests/tcg/ppc/Makefile.target b/tests/tcg/ppc/Makefile.target deleted file mode 100644 index f5e08c7376..0000000000 --- a/tests/tcg/ppc/Makefile.target +++ /dev/null @@ -1,12 +0,0 @@ -# -*- Mode: makefile -*- -# -# PPC - included from tests/tcg/Makefile -# - -ifneq (,$(findstring 64,$(TARGET_NAME))) -# On PPC64 Linux can be configured with 4k (default) or 64k pages (currently broken) -EXTRA_RUNS+=run-test-mmap-4096 #run-test-mmap-65536 -else -# On PPC32 Linux supports 4K/16K/64K/256K (but currently only 4k works) -EXTRA_RUNS+=run-test-mmap-4096 #run-test-mmap-16384 run-test-mmap-65536 run-test-mmap-262144 -endif diff --git a/tests/tcg/ppc64/Makefile.target b/tests/tcg/ppc64/Makefile.target index babd209573..8c3e4e4038 100644 --- a/tests/tcg/ppc64/Makefile.target +++ b/tests/tcg/ppc64/Makefile.target @@ -3,30 +3,44 @@ # ppc64 specific tweaks VPATH += $(SRC_PATH)/tests/tcg/ppc64 -VPATH += $(SRC_PATH)/tests/tcg/ppc64le + +config-cc.mak: Makefile + $(quiet-@)( \ + $(call cc-option,-mpower8-vector, CROSS_CC_HAS_POWER8_VECTOR); \ + $(call cc-option,-mpower10, CROSS_CC_HAS_POWER10)) 3> config-cc.mak + +-include config-cc.mak ifneq ($(CROSS_CC_HAS_POWER8_VECTOR),) PPC64_TESTS=bcdsub non_signalling_xscv endif $(PPC64_TESTS): CFLAGS += -mpower8-vector +ifneq ($(CROSS_CC_HAS_POWER8_VECTOR),) +PPC64_TESTS += vsx_f2i_nan +endif +vsx_f2i_nan: CFLAGS += -mpower8-vector -I$(SRC_PATH)/include + PPC64_TESTS += mtfsf +PPC64_TESTS += mffsce ifneq ($(CROSS_CC_HAS_POWER10),) -PPC64_TESTS += byte_reverse sha512-vector +PPC64_TESTS += byte_reverse sha512-vector vector endif byte_reverse: CFLAGS += -mcpu=power10 run-byte_reverse: QEMU_OPTS+=-cpu POWER10 -run-plugin-byte_reverse-with-%: QEMU_OPTS+=-cpu POWER10 sha512-vector: CFLAGS +=-mcpu=power10 -O3 sha512-vector: sha512.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) run-sha512-vector: QEMU_OPTS+=-cpu POWER10 -run-plugin-sha512-vector-with-%: QEMU_OPTS+=-cpu POWER10 + +vector: CFLAGS += -mcpu=power10 -I$(SRC_PATH)/include +run-vector: QEMU_OPTS += -cpu POWER10 PPC64_TESTS += signal_save_restore_xer PPC64_TESTS += xxspltw +PPC64_TESTS += test-aes TESTS += $(PPC64_TESTS) diff --git a/tests/tcg/ppc64le/bcdsub.c b/tests/tcg/ppc64/bcdsub.c similarity index 100% rename from tests/tcg/ppc64le/bcdsub.c rename to tests/tcg/ppc64/bcdsub.c diff --git a/tests/tcg/ppc64le/byte_reverse.c b/tests/tcg/ppc64/byte_reverse.c similarity index 100% rename from tests/tcg/ppc64le/byte_reverse.c rename to tests/tcg/ppc64/byte_reverse.c diff --git a/tests/tcg/ppc64/mffsce.c b/tests/tcg/ppc64/mffsce.c new file mode 100644 index 0000000000..20d882cb45 --- /dev/null +++ b/tests/tcg/ppc64/mffsce.c @@ -0,0 +1,37 @@ +#include +#include +#include + +#define MTFSF(FLM, FRB) asm volatile ("mtfsf %0, %1" :: "i" (FLM), "f" (FRB)) +#define MFFS(FRT) asm("mffs %0" : "=f" (FRT)) +#define MFFSCE(FRT) asm("mffsce %0" : "=f" (FRT)) + +#define PPC_BIT_NR(nr) (63 - (nr)) + +#define FP_VE (1ull << PPC_BIT_NR(56)) +#define FP_UE (1ull << PPC_BIT_NR(58)) +#define FP_ZE (1ull << PPC_BIT_NR(59)) +#define FP_XE (1ull << PPC_BIT_NR(60)) +#define FP_NI (1ull << PPC_BIT_NR(61)) +#define FP_RN1 (1ull << PPC_BIT_NR(63)) + +int main(void) +{ + uint64_t frt, fpscr; + uint64_t test_value = FP_VE | FP_UE | FP_ZE | + FP_XE | FP_NI | FP_RN1; + MTFSF(0b11111111, test_value); /* set test value to cpu fpscr */ + MFFSCE(frt); + MFFS(fpscr); /* read the value that mffsce stored to cpu fpscr */ + + /* the returned value should be as the cpu fpscr was before */ + assert((frt & 0xff) == test_value); + + /* + * the cpu fpscr last 3 bits should be unchanged + * and enable bits should be unset + */ + assert((fpscr & 0xff) == (test_value & 0x7)); + + return 0; +} diff --git a/tests/tcg/ppc64le/mtfsf.c b/tests/tcg/ppc64/mtfsf.c similarity index 100% rename from tests/tcg/ppc64le/mtfsf.c rename to tests/tcg/ppc64/mtfsf.c diff --git a/tests/tcg/ppc64le/non_signalling_xscv.c b/tests/tcg/ppc64/non_signalling_xscv.c similarity index 100% rename from tests/tcg/ppc64le/non_signalling_xscv.c rename to tests/tcg/ppc64/non_signalling_xscv.c diff --git a/tests/tcg/ppc64le/signal_save_restore_xer.c b/tests/tcg/ppc64/signal_save_restore_xer.c similarity index 100% rename from tests/tcg/ppc64le/signal_save_restore_xer.c rename to tests/tcg/ppc64/signal_save_restore_xer.c diff --git a/tests/tcg/ppc64/test-aes.c b/tests/tcg/ppc64/test-aes.c new file mode 100644 index 0000000000..1d2be488e9 --- /dev/null +++ b/tests/tcg/ppc64/test-aes.c @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "../multiarch/test-aes-main.c.inc" + +#undef BIG_ENDIAN +#define BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + +static unsigned char bswap_le[16] __attribute__((aligned(16))) = { + 8,9,10,11,12,13,14,15, + 0,1,2,3,4,5,6,7 +}; + +bool test_SB_SR(uint8_t *o, const uint8_t *i) +{ + /* vcipherlast also adds round key, so supply zero. */ + if (BIG_ENDIAN) { + asm("lxvd2x 32,0,%1\n\t" + "vspltisb 1,0\n\t" + "vcipherlast 0,0,1\n\t" + "stxvd2x 32,0,%0" + : : "r"(o), "r"(i) : "memory", "v0", "v1"); + } else { + asm("lxvd2x 32,0,%1\n\t" + "lxvd2x 34,0,%2\n\t" + "vspltisb 1,0\n\t" + "vperm 0,0,0,2\n\t" + "vcipherlast 0,0,1\n\t" + "vperm 0,0,0,2\n\t" + "stxvd2x 32,0,%0" + : : "r"(o), "r"(i), "r"(bswap_le) : "memory", "v0", "v1", "v2"); + } + return true; +} + +bool test_MC(uint8_t *o, const uint8_t *i) +{ + return false; +} + +bool test_SB_SR_MC_AK(uint8_t *o, const uint8_t *i, const uint8_t *k) +{ + if (BIG_ENDIAN) { + asm("lxvd2x 32,0,%1\n\t" + "lxvd2x 33,0,%2\n\t" + "vcipher 0,0,1\n\t" + "stxvd2x 32,0,%0" + : : "r"(o), "r"(i), "r"(k) : "memory", "v0", "v1"); + } else { + asm("lxvd2x 32,0,%1\n\t" + "lxvd2x 33,0,%2\n\t" + "lxvd2x 34,0,%3\n\t" + "vperm 0,0,0,2\n\t" + "vperm 1,1,1,2\n\t" + "vcipher 0,0,1\n\t" + "vperm 0,0,0,2\n\t" + "stxvd2x 32,0,%0" + : : "r"(o), "r"(i), "r"(k), "r"(bswap_le) + : "memory", "v0", "v1", "v2"); + } + return true; +} + +bool test_ISB_ISR(uint8_t *o, const uint8_t *i) +{ + /* vcipherlast also adds round key, so supply zero. */ + if (BIG_ENDIAN) { + asm("lxvd2x 32,0,%1\n\t" + "vspltisb 1,0\n\t" + "vncipherlast 0,0,1\n\t" + "stxvd2x 32,0,%0" + : : "r"(o), "r"(i) : "memory", "v0", "v1"); + } else { + asm("lxvd2x 32,0,%1\n\t" + "lxvd2x 34,0,%2\n\t" + "vspltisb 1,0\n\t" + "vperm 0,0,0,2\n\t" + "vncipherlast 0,0,1\n\t" + "vperm 0,0,0,2\n\t" + "stxvd2x 32,0,%0" + : : "r"(o), "r"(i), "r"(bswap_le) : "memory", "v0", "v1", "v2"); + } + return true; +} + +bool test_IMC(uint8_t *o, const uint8_t *i) +{ + return false; +} + +bool test_ISB_ISR_AK_IMC(uint8_t *o, const uint8_t *i, const uint8_t *k) +{ + if (BIG_ENDIAN) { + asm("lxvd2x 32,0,%1\n\t" + "lxvd2x 33,0,%2\n\t" + "vncipher 0,0,1\n\t" + "stxvd2x 32,0,%0" + : : "r"(o), "r"(i), "r"(k) : "memory", "v0", "v1"); + } else { + asm("lxvd2x 32,0,%1\n\t" + "lxvd2x 33,0,%2\n\t" + "lxvd2x 34,0,%3\n\t" + "vperm 0,0,0,2\n\t" + "vperm 1,1,1,2\n\t" + "vncipher 0,0,1\n\t" + "vperm 0,0,0,2\n\t" + "stxvd2x 32,0,%0" + : : "r"(o), "r"(i), "r"(k), "r"(bswap_le) + : "memory", "v0", "v1", "v2"); + } + return true; +} + +bool test_ISB_ISR_IMC_AK(uint8_t *o, const uint8_t *i, const uint8_t *k) +{ + return false; +} diff --git a/tests/tcg/ppc64/vector.c b/tests/tcg/ppc64/vector.c new file mode 100644 index 0000000000..cbf4ae9332 --- /dev/null +++ b/tests/tcg/ppc64/vector.c @@ -0,0 +1,51 @@ +#include +#include +#include "qemu/compiler.h" + +int main(void) +{ + unsigned int result_wi; + vector unsigned char vbc_bi_src = { 0xFF, 0xFF, 0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, + 0, 0xFF, 0xFF}; + vector unsigned short vbc_hi_src = { 0xFFFF, 0, 0, 0xFFFF, + 0, 0, 0xFFFF, 0xFFFF}; + vector unsigned int vbc_wi_src = {0, 0, 0xFFFFFFFF, 0xFFFFFFFF}; + vector unsigned long long vbc_di_src = {0xFFFFFFFFFFFFFFFF, 0}; + vector __uint128_t vbc_qi_src; + + asm("vextractbm %0, %1" : "=r" (result_wi) : "v" (vbc_bi_src)); +#if HOST_BIG_ENDIAN + assert(result_wi == 0b1101111111000011); +#else + assert(result_wi == 0b1100001111111011); +#endif + + asm("vextracthm %0, %1" : "=r" (result_wi) : "v" (vbc_hi_src)); +#if HOST_BIG_ENDIAN + assert(result_wi == 0b10010011); +#else + assert(result_wi == 0b11001001); +#endif + + asm("vextractwm %0, %1" : "=r" (result_wi) : "v" (vbc_wi_src)); +#if HOST_BIG_ENDIAN + assert(result_wi == 0b0011); +#else + assert(result_wi == 0b1100); +#endif + + asm("vextractdm %0, %1" : "=r" (result_wi) : "v" (vbc_di_src)); +#if HOST_BIG_ENDIAN + assert(result_wi == 0b10); +#else + assert(result_wi == 0b01); +#endif + + vbc_qi_src[0] = 0x1; + vbc_qi_src[0] = vbc_qi_src[0] << 127; + asm("vextractqm %0, %1" : "=r" (result_wi) : "v" (vbc_qi_src)); + assert(result_wi == 0b1); + + return 0; +} diff --git a/tests/tcg/ppc64/vsx_f2i_nan.c b/tests/tcg/ppc64/vsx_f2i_nan.c new file mode 100644 index 0000000000..94b1a4eb02 --- /dev/null +++ b/tests/tcg/ppc64/vsx_f2i_nan.c @@ -0,0 +1,300 @@ +#include +#include "qemu/compiler.h" + +typedef vector float vsx_float32_vec_t; +typedef vector double vsx_float64_vec_t; +typedef vector signed int vsx_int32_vec_t; +typedef vector unsigned int vsx_uint32_vec_t; +typedef vector signed long long vsx_int64_vec_t; +typedef vector unsigned long long vsx_uint64_vec_t; + +#define DEFINE_VSX_F2I_FUNC(SRC_T, DEST_T, INSN) \ +static inline vsx_##DEST_T##_vec_t \ + vsx_convert_##SRC_T##_vec_to_##DEST_T##_vec(vsx_##SRC_T##_vec_t v) \ +{ \ + vsx_##DEST_T##_vec_t result; \ + asm(#INSN " %x0, %x1" : "=wa" (result) : "wa" (v)); \ + return result; \ +} + +DEFINE_VSX_F2I_FUNC(float32, int32, xvcvspsxws) +DEFINE_VSX_F2I_FUNC(float32, uint32, xvcvspuxws) +DEFINE_VSX_F2I_FUNC(float32, int64, xvcvspsxds) +DEFINE_VSX_F2I_FUNC(float32, uint64, xvcvspuxds) +DEFINE_VSX_F2I_FUNC(float64, int32, xvcvdpsxws) +DEFINE_VSX_F2I_FUNC(float64, uint32, xvcvdpuxws) +DEFINE_VSX_F2I_FUNC(float64, int64, xvcvdpsxds) +DEFINE_VSX_F2I_FUNC(float64, uint64, xvcvdpuxds) + +static inline vsx_float32_vec_t vsx_float32_is_nan(vsx_float32_vec_t v) +{ + vsx_float32_vec_t abs_v; + vsx_float32_vec_t result_mask; + const vsx_uint32_vec_t f32_pos_inf_bits = {0x7F800000U, 0x7F800000U, + 0x7F800000U, 0x7F800000U}; + + asm("xvabssp %x0, %x1" : "=wa" (abs_v) : "wa" (v)); + asm("vcmpgtuw %0, %1, %2" + : "=v" (result_mask) + : "v" (abs_v), "v" (f32_pos_inf_bits)); + return result_mask; +} + +static inline vsx_float64_vec_t vsx_float64_is_nan(vsx_float64_vec_t v) +{ + vsx_float64_vec_t abs_v; + vsx_float64_vec_t result_mask; + const vsx_uint64_vec_t f64_pos_inf_bits = {0x7FF0000000000000ULL, + 0x7FF0000000000000ULL}; + + asm("xvabsdp %x0, %x1" : "=wa" (abs_v) : "wa" (v)); + asm("vcmpgtud %0, %1, %2" + : "=v" (result_mask) + : "v" (abs_v), "v" (f64_pos_inf_bits)); + return result_mask; +} + +#define DEFINE_VSX_BINARY_LOGICAL_OP_INSN(LANE_TYPE, OP_NAME, OP_INSN) \ +static inline vsx_##LANE_TYPE##_vec_t vsx_##LANE_TYPE##_##OP_NAME( \ + vsx_##LANE_TYPE##_vec_t a, vsx_##LANE_TYPE##_vec_t b) \ +{ \ + vsx_##LANE_TYPE##_vec_t result; \ + asm(#OP_INSN " %x0, %x1, %x2" : "=wa" (result) : "wa" (a), "wa" (b)); \ + return result; \ +} + +DEFINE_VSX_BINARY_LOGICAL_OP_INSN(float32, logical_and, xxland) +DEFINE_VSX_BINARY_LOGICAL_OP_INSN(float64, logical_and, xxland) +DEFINE_VSX_BINARY_LOGICAL_OP_INSN(int32, logical_and, xxland) +DEFINE_VSX_BINARY_LOGICAL_OP_INSN(uint32, logical_and, xxland) +DEFINE_VSX_BINARY_LOGICAL_OP_INSN(int64, logical_and, xxland) +DEFINE_VSX_BINARY_LOGICAL_OP_INSN(uint64, logical_and, xxland) + +DEFINE_VSX_BINARY_LOGICAL_OP_INSN(float32, logical_andc, xxlandc) +DEFINE_VSX_BINARY_LOGICAL_OP_INSN(float64, logical_andc, xxlandc) + +DEFINE_VSX_BINARY_LOGICAL_OP_INSN(float32, logical_or, xxlor) +DEFINE_VSX_BINARY_LOGICAL_OP_INSN(float64, logical_or, xxlor) +DEFINE_VSX_BINARY_LOGICAL_OP_INSN(int32, logical_or, xxlor) +DEFINE_VSX_BINARY_LOGICAL_OP_INSN(uint32, logical_or, xxlor) +DEFINE_VSX_BINARY_LOGICAL_OP_INSN(int64, logical_or, xxlor) +DEFINE_VSX_BINARY_LOGICAL_OP_INSN(uint64, logical_or, xxlor) + +static inline vsx_int32_vec_t vsx_mask_out_float32_vec_to_int32_vec( + vsx_int32_vec_t v) +{ + return v; +} +static inline vsx_uint32_vec_t vsx_mask_out_float32_vec_to_uint32_vec( + vsx_uint32_vec_t v) +{ + return v; +} +static inline vsx_int64_vec_t vsx_mask_out_float32_vec_to_int64_vec( + vsx_int64_vec_t v) +{ + return v; +} +static inline vsx_uint64_vec_t vsx_mask_out_float32_vec_to_uint64_vec( + vsx_uint64_vec_t v) +{ + return v; +} + +static inline vsx_int32_vec_t vsx_mask_out_float64_vec_to_int32_vec( + vsx_int32_vec_t v) +{ +#if HOST_BIG_ENDIAN + const vsx_int32_vec_t valid_lanes_mask = {-1, 0, -1, 0}; +#else + const vsx_int32_vec_t valid_lanes_mask = {0, -1, 0, -1}; +#endif + + return vsx_int32_logical_and(v, valid_lanes_mask); +} + +static inline vsx_uint32_vec_t vsx_mask_out_float64_vec_to_uint32_vec( + vsx_uint32_vec_t v) +{ + return (vsx_uint32_vec_t)vsx_mask_out_float64_vec_to_int32_vec( + (vsx_int32_vec_t)v); +} + +static inline vsx_int64_vec_t vsx_mask_out_float64_vec_to_int64_vec( + vsx_int64_vec_t v) +{ + return v; +} +static inline vsx_uint64_vec_t vsx_mask_out_float64_vec_to_uint64_vec( + vsx_uint64_vec_t v) +{ + return v; +} + +static inline void print_vsx_float32_vec_elements(FILE *stream, + vsx_float32_vec_t vec) +{ + fprintf(stream, "%g, %g, %g, %g", (double)vec[0], (double)vec[1], + (double)vec[2], (double)vec[3]); +} + +static inline void print_vsx_float64_vec_elements(FILE *stream, + vsx_float64_vec_t vec) +{ + fprintf(stream, "%.17g, %.17g", vec[0], vec[1]); +} + +static inline void print_vsx_int32_vec_elements(FILE *stream, + vsx_int32_vec_t vec) +{ + fprintf(stream, "%d, %d, %d, %d", vec[0], vec[1], vec[2], vec[3]); +} + +static inline void print_vsx_uint32_vec_elements(FILE *stream, + vsx_uint32_vec_t vec) +{ + fprintf(stream, "%u, %u, %u, %u", vec[0], vec[1], vec[2], vec[3]); +} + +static inline void print_vsx_int64_vec_elements(FILE *stream, + vsx_int64_vec_t vec) +{ + fprintf(stream, "%lld, %lld", vec[0], vec[1]); +} + +static inline void print_vsx_uint64_vec_elements(FILE *stream, + vsx_uint64_vec_t vec) +{ + fprintf(stream, "%llu, %llu", vec[0], vec[1]); +} + +#define DEFINE_VSX_ALL_EQ_FUNC(LANE_TYPE, CMP_INSN) \ +static inline int vsx_##LANE_TYPE##_all_eq(vsx_##LANE_TYPE##_vec_t a, \ + vsx_##LANE_TYPE##_vec_t b) \ +{ \ + unsigned result; \ + vsx_##LANE_TYPE##_vec_t is_eq_mask_vec; \ + asm(#CMP_INSN ". %0, %2, %3\n\t" \ + "mfocrf %1, 2" \ + : "=v" (is_eq_mask_vec), "=r" (result) \ + : "v" (a), "v" (b) \ + : "cr6"); \ + return (int)((result >> 7) & 1u); \ +} + +DEFINE_VSX_ALL_EQ_FUNC(int32, vcmpequw) +DEFINE_VSX_ALL_EQ_FUNC(uint32, vcmpequw) +DEFINE_VSX_ALL_EQ_FUNC(int64, vcmpequd) +DEFINE_VSX_ALL_EQ_FUNC(uint64, vcmpequd) + +#define DEFINE_VSX_F2I_TEST_FUNC(SRC_T, DEST_T) \ +static inline int test_vsx_conv_##SRC_T##_vec_to_##DEST_T##_vec( \ + vsx_##SRC_T##_vec_t src_v) \ +{ \ + const vsx_##SRC_T##_vec_t is_nan_mask = vsx_##SRC_T##_is_nan(src_v); \ + const vsx_##SRC_T##_vec_t nan_src_v = \ + vsx_##SRC_T##_logical_and(src_v, is_nan_mask); \ + const vsx_##SRC_T##_vec_t non_nan_src_v = \ + vsx_##SRC_T##_logical_andc(src_v, is_nan_mask); \ + \ + const vsx_##DEST_T##_vec_t expected_result = \ + vsx_mask_out_##SRC_T##_vec_to_##DEST_T##_vec( \ + vsx_##DEST_T##_logical_or( \ + vsx_convert_##SRC_T##_vec_to_##DEST_T##_vec(nan_src_v), \ + vsx_convert_##SRC_T##_vec_to_##DEST_T##_vec( \ + non_nan_src_v))); \ + const vsx_##DEST_T##_vec_t actual_result = \ + vsx_mask_out_##SRC_T##_vec_to_##DEST_T##_vec( \ + vsx_convert_##SRC_T##_vec_to_##DEST_T##_vec(src_v)); \ + const int test_result = \ + vsx_##DEST_T##_all_eq(expected_result, actual_result); \ + \ + if (unlikely(test_result == 0)) { \ + fputs("FAIL: Conversion of " #SRC_T " vector to " #DEST_T \ + " vector failed\n", stdout); \ + fputs("Source values: ", stdout); \ + print_vsx_##SRC_T##_vec_elements(stdout, src_v); \ + fputs("\nExpected result: ", stdout); \ + print_vsx_##DEST_T##_vec_elements(stdout, expected_result); \ + fputs("\nActual result: ", stdout); \ + print_vsx_##DEST_T##_vec_elements(stdout, actual_result); \ + fputs("\n\n", stdout); \ + } \ + \ + return test_result; \ +} + + +DEFINE_VSX_F2I_TEST_FUNC(float32, int32) +DEFINE_VSX_F2I_TEST_FUNC(float32, uint32) +DEFINE_VSX_F2I_TEST_FUNC(float32, int64) +DEFINE_VSX_F2I_TEST_FUNC(float32, uint64) +DEFINE_VSX_F2I_TEST_FUNC(float64, int32) +DEFINE_VSX_F2I_TEST_FUNC(float64, uint32) +DEFINE_VSX_F2I_TEST_FUNC(float64, int64) +DEFINE_VSX_F2I_TEST_FUNC(float64, uint64) + +static inline vsx_int32_vec_t vsx_int32_vec_from_mask(int mask) +{ + const vsx_int32_vec_t bits_to_test = {1, 2, 4, 8}; + const vsx_int32_vec_t vec_mask = {mask, mask, mask, mask}; + vsx_int32_vec_t result; + + asm("vcmpequw %0, %1, %2" + : "=v" (result) + : "v" (vsx_int32_logical_and(vec_mask, bits_to_test)), + "v" (bits_to_test)); + return result; +} + +static inline vsx_int64_vec_t vsx_int64_vec_from_mask(int mask) +{ + const vsx_int64_vec_t bits_to_test = {1, 2}; + const vsx_int64_vec_t vec_mask = {mask, mask}; + vsx_int64_vec_t result; + + asm("vcmpequd %0, %1, %2" + : "=v" (result) + : "v" (vsx_int64_logical_and(vec_mask, bits_to_test)), + "v" (bits_to_test)); + return result; +} + +int main(void) +{ + const vsx_float32_vec_t f32_iota1 = {1.0f, 2.0f, 3.0f, 4.0f}; + const vsx_float64_vec_t f64_iota1 = {1.0, 2.0}; + + int num_of_tests_failed = 0; + + for (int i = 0; i < 16; i++) { + const vsx_int32_vec_t nan_mask = vsx_int32_vec_from_mask(i); + const vsx_float32_vec_t f32_v = + vsx_float32_logical_or(f32_iota1, (vsx_float32_vec_t)nan_mask); + num_of_tests_failed += + (int)(!test_vsx_conv_float32_vec_to_int32_vec(f32_v)); + num_of_tests_failed += + (int)(!test_vsx_conv_float32_vec_to_int64_vec(f32_v)); + num_of_tests_failed += + (int)(!test_vsx_conv_float32_vec_to_uint32_vec(f32_v)); + num_of_tests_failed += + (int)(!test_vsx_conv_float32_vec_to_uint64_vec(f32_v)); + } + + for (int i = 0; i < 4; i++) { + const vsx_int64_vec_t nan_mask = vsx_int64_vec_from_mask(i); + const vsx_float64_vec_t f64_v = + vsx_float64_logical_or(f64_iota1, (vsx_float64_vec_t)nan_mask); + num_of_tests_failed += + (int)(!test_vsx_conv_float64_vec_to_int32_vec(f64_v)); + num_of_tests_failed += + (int)(!test_vsx_conv_float64_vec_to_int64_vec(f64_v)); + num_of_tests_failed += + (int)(!test_vsx_conv_float64_vec_to_uint32_vec(f64_v)); + num_of_tests_failed += + (int)(!test_vsx_conv_float64_vec_to_uint64_vec(f64_v)); + } + + printf("%d tests failed\n", num_of_tests_failed); + return (int)(num_of_tests_failed != 0); +} diff --git a/tests/tcg/ppc64le/xxspltw.c b/tests/tcg/ppc64/xxspltw.c similarity index 100% rename from tests/tcg/ppc64le/xxspltw.c rename to tests/tcg/ppc64/xxspltw.c diff --git a/tests/tcg/ppc64le/Makefile.target b/tests/tcg/ppc64le/Makefile.target index 5b0eb5e870..daad5118a5 100644 --- a/tests/tcg/ppc64le/Makefile.target +++ b/tests/tcg/ppc64le/Makefile.target @@ -4,27 +4,4 @@ VPATH += $(SRC_PATH)/tests/tcg/ppc64le -ifneq ($(CROSS_CC_HAS_POWER8_VECTOR),) -PPC64LE_TESTS=bcdsub non_signalling_xscv -endif -$(PPC64LE_TESTS): CFLAGS += -mpower8-vector - -ifneq ($(CROSS_CC_HAS_POWER10),) -PPC64LE_TESTS += byte_reverse sha512-vector -endif -byte_reverse: CFLAGS += -mcpu=power10 -run-byte_reverse: QEMU_OPTS+=-cpu POWER10 -run-plugin-byte_reverse-with-%: QEMU_OPTS+=-cpu POWER10 - -sha512-vector: CFLAGS +=-mcpu=power10 -O3 -sha512-vector: sha512.c - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) - -run-sha512-vector: QEMU_OPTS+=-cpu POWER10 -run-plugin-sha512-vector-with-%: QEMU_OPTS+=-cpu POWER10 - -PPC64LE_TESTS += mtfsf -PPC64LE_TESTS += signal_save_restore_xer -PPC64LE_TESTS += xxspltw - -TESTS += $(PPC64LE_TESTS) +include $(SRC_PATH)/tests/tcg/ppc64/Makefile.target diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target new file mode 100644 index 0000000000..d5b126e5f1 --- /dev/null +++ b/tests/tcg/riscv64/Makefile.softmmu-target @@ -0,0 +1,24 @@ +# +# RISC-V system tests +# + +TEST_SRC = $(SRC_PATH)/tests/tcg/riscv64 +VPATH += $(TEST_SRC) + +LINK_SCRIPT = $(TEST_SRC)/semihost.ld +LDFLAGS = -T $(LINK_SCRIPT) +CFLAGS += -g -Og + +%.o: %.S + $(CC) $(CFLAGS) $< -c -o $@ +%: %.o $(LINK_SCRIPT) + $(LD) $(LDFLAGS) $< -o $@ + +QEMU_OPTS += -M virt -display none -semihosting -device loader,file= + +EXTRA_RUNS += run-issue1060 +run-issue1060: issue1060 + $(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<) + +# We don't currently support the multiarch system tests +undefine MULTIARCH_TESTS diff --git a/tests/tcg/riscv64/Makefile.target b/tests/tcg/riscv64/Makefile.target index d41bf6d60d..4da5b9a3b3 100644 --- a/tests/tcg/riscv64/Makefile.target +++ b/tests/tcg/riscv64/Makefile.target @@ -3,3 +3,18 @@ VPATH += $(SRC_PATH)/tests/tcg/riscv64 TESTS += test-div +TESTS += noexec + +# Disable compressed instructions for test-noc +TESTS += test-noc +test-noc: LDFLAGS = -nostdlib -static +run-test-noc: QEMU_OPTS += -cpu rv64,c=false + +TESTS += test-aes +run-test-aes: QEMU_OPTS += -cpu rv64,zk=on + +# Test for fcvtmod +TESTS += test-fcvtmod +test-fcvtmod: CFLAGS += -march=rv64imafdc +test-fcvtmod: LDFLAGS += -static +run-test-fcvtmod: QEMU_OPTS += -cpu rv64,d=true,zfa=true diff --git a/tests/tcg/riscv64/issue1060.S b/tests/tcg/riscv64/issue1060.S new file mode 100644 index 0000000000..17b7fe1be2 --- /dev/null +++ b/tests/tcg/riscv64/issue1060.S @@ -0,0 +1,53 @@ + .option norvc + + .text + .global _start +_start: + lla t0, trap + csrw mtvec, t0 + + # These are all illegal instructions + csrw time, x0 + .insn i CUSTOM_0, 0, x0, x0, 0x321 + csrw time, x0 + .insn i CUSTOM_0, 0, x0, x0, 0x123 + csrw cycle, x0 + + # Success! + li a0, 0 + j _exit + +trap: + # When an instruction traps, compare it to the insn in memory. + csrr t0, mepc + csrr t1, mtval + lwu t2, 0(t0) + bne t1, t2, fail + + # Skip the insn and continue. + addi t0, t0, 4 + csrw mepc, t0 + mret + +fail: + li a0, 1 + +# Exit code in a0 +_exit: + lla a1, semiargs + li t0, 0x20026 # ADP_Stopped_ApplicationExit + sd t0, 0(a1) + sd a0, 8(a1) + li a0, 0x20 # TARGET_SYS_EXIT_EXTENDED + + # Semihosting call sequence + .balign 16 + slli zero, zero, 0x1f + ebreak + srai zero, zero, 0x7 + j . + + .data + .balign 16 +semiargs: + .space 16 diff --git a/tests/tcg/riscv64/noexec.c b/tests/tcg/riscv64/noexec.c new file mode 100644 index 0000000000..86f64b28db --- /dev/null +++ b/tests/tcg/riscv64/noexec.c @@ -0,0 +1,79 @@ +#include "../multiarch/noexec.c.inc" + +static void *arch_mcontext_pc(const mcontext_t *ctx) +{ + return (void *)ctx->__gregs[REG_PC]; +} + +static int arch_mcontext_arg(const mcontext_t *ctx) +{ + return ctx->__gregs[REG_A0]; +} + +static void arch_flush(void *p, int len) +{ + __builtin___clear_cache(p, p + len); +} + +extern char noexec_1[]; +extern char noexec_2[]; +extern char noexec_end[]; + +asm(".option push\n" + ".option norvc\n" + "noexec_1:\n" + " li a0,1\n" /* a0 is 0 on entry, set 1. */ + "noexec_2:\n" + " li a0,2\n" /* a0 is 0/1; set 2. */ + " ret\n" + "noexec_end:\n" + ".option pop"); + +int main(void) +{ + struct noexec_test noexec_tests[] = { + { + .name = "fallthrough", + .test_code = noexec_1, + .test_len = noexec_end - noexec_1, + .page_ofs = noexec_1 - noexec_2, + .entry_ofs = noexec_1 - noexec_2, + .expected_si_ofs = 0, + .expected_pc_ofs = 0, + .expected_arg = 1, + }, + { + .name = "jump", + .test_code = noexec_1, + .test_len = noexec_end - noexec_1, + .page_ofs = noexec_1 - noexec_2, + .entry_ofs = 0, + .expected_si_ofs = 0, + .expected_pc_ofs = 0, + .expected_arg = 0, + }, + { + .name = "fallthrough [cross]", + .test_code = noexec_1, + .test_len = noexec_end - noexec_1, + .page_ofs = noexec_1 - noexec_2 - 2, + .entry_ofs = noexec_1 - noexec_2 - 2, + .expected_si_ofs = 0, + .expected_pc_ofs = -2, + .expected_arg = 1, + }, + { + .name = "jump [cross]", + .test_code = noexec_1, + .test_len = noexec_end - noexec_1, + .page_ofs = noexec_1 - noexec_2 - 2, + .entry_ofs = -2, + .expected_si_ofs = 0, + .expected_pc_ofs = -2, + .expected_arg = 0, + }, + }; + + return test_noexec(noexec_tests, + sizeof(noexec_tests) / sizeof(noexec_tests[0])); +} diff --git a/tests/tcg/riscv64/semicall.h b/tests/tcg/riscv64/semicall.h index f8c88f32dc..11d0650cb0 100644 --- a/tests/tcg/riscv64/semicall.h +++ b/tests/tcg/riscv64/semicall.h @@ -1,10 +1,10 @@ /* * Semihosting Tests - RiscV64 Helper * - * Copyright (c) 2021 + * Copyright (c) 2021, 2024 * Written by Alex Bennée * - * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later */ uintptr_t __semi_call(uintptr_t type, uintptr_t arg0) diff --git a/tests/tcg/riscv64/semihost.ld b/tests/tcg/riscv64/semihost.ld new file mode 100644 index 0000000000..a59cc56b28 --- /dev/null +++ b/tests/tcg/riscv64/semihost.ld @@ -0,0 +1,21 @@ +ENTRY(_start) + +SECTIONS +{ + /* virt machine, RAM starts at 2gb */ + . = 0x80000000; + .text : { + *(.text) + } + .rodata : { + *(.rodata) + } + /* align r/w section to next 2mb */ + . = ALIGN(1 << 21); + .data : { + *(.data) + } + .bss : { + *(.bss) + } +} diff --git a/tests/tcg/riscv64/test-aes.c b/tests/tcg/riscv64/test-aes.c new file mode 100644 index 0000000000..6a0ef77e7b --- /dev/null +++ b/tests/tcg/riscv64/test-aes.c @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "../multiarch/test-aes-main.c.inc" + +bool test_SB_SR(uint8_t *o, const uint8_t *i) +{ + uint64_t *o8 = (uint64_t *)o; + const uint64_t *i8 = (const uint64_t *)i; + + /* aes64es rd, rs1, rs2 = 0011001 rs2 rs1 000 rd 0110011 */ + asm(".insn r 0x33, 0x0, 0x19, %0, %2, %3\n\t" + ".insn r 0x33, 0x0, 0x19, %1, %3, %2" + : "=&r"(o8[0]), "=&r"(o8[1]) : "r"(i8[0]), "r"(i8[1])); + return true; +} + +bool test_MC(uint8_t *o, const uint8_t *i) +{ + return false; +} + +bool test_SB_SR_MC_AK(uint8_t *o, const uint8_t *i, const uint8_t *k) +{ + uint64_t *o8 = (uint64_t *)o; + const uint64_t *i8 = (const uint64_t *)i; + const uint64_t *k8 = (const uint64_t *)k; + + /* aesesm rd, rs1, rs2 = 0011011 rs2 rs1 000 rd 0110011 */ + asm(".insn r 0x33, 0x0, 0x1b, %0, %2, %3\n\t" + ".insn r 0x33, 0x0, 0x1b, %1, %3, %2\n\t" + "xor %0,%0,%4\n\t" + "xor %1,%1,%5" + : "=&r"(o8[0]), "=&r"(o8[1]) + : "r"(i8[0]), "r"(i8[1]), "r"(k8[0]), "r"(k8[1])); + return true; +} + +bool test_ISB_ISR(uint8_t *o, const uint8_t *i) +{ + uint64_t *o8 = (uint64_t *)o; + const uint64_t *i8 = (const uint64_t *)i; + + /* aes64ds rd, rs1, rs2 = 0011101 rs2 rs1 000 rd 0110011 */ + asm(".insn r 0x33, 0x0, 0x1d, %0, %2, %3\n\t" + ".insn r 0x33, 0x0, 0x1d, %1, %3, %2" + : "=&r"(o8[0]), "=&r"(o8[1]) : "r"(i8[0]), "r"(i8[1])); + return true; +} + +bool test_IMC(uint8_t *o, const uint8_t *i) +{ + uint64_t *o8 = (uint64_t *)o; + const uint64_t *i8 = (const uint64_t *)i; + + /* aes64im rd, rs1 = 0011000 00000 rs1 001 rd 0010011 */ + asm(".insn r 0x13, 0x1, 0x18, %0, %0, x0\n\t" + ".insn r 0x13, 0x1, 0x18, %1, %1, x0" + : "=r"(o8[0]), "=r"(o8[1]) : "0"(i8[0]), "1"(i8[1])); + return true; +} + +bool test_ISB_ISR_AK_IMC(uint8_t *o, const uint8_t *i, const uint8_t *k) +{ + return false; +} + +bool test_ISB_ISR_IMC_AK(uint8_t *o, const uint8_t *i, const uint8_t *k) +{ + uint64_t *o8 = (uint64_t *)o; + const uint64_t *i8 = (const uint64_t *)i; + const uint64_t *k8 = (const uint64_t *)k; + + /* aes64dsm rd, rs1, rs2 = 0011111 rs2 rs1 000 rd 0110011 */ + asm(".insn r 0x33, 0x0, 0x1f, %0, %2, %3\n\t" + ".insn r 0x33, 0x0, 0x1f, %1, %3, %2\n\t" + "xor %0,%0,%4\n\t" + "xor %1,%1,%5" + : "=&r"(o8[0]), "=&r"(o8[1]) + : "r"(i8[0]), "r"(i8[1]), "r"(k8[0]), "r"(k8[1])); + return true; +} diff --git a/tests/tcg/riscv64/test-fcvtmod.c b/tests/tcg/riscv64/test-fcvtmod.c new file mode 100644 index 0000000000..f050579974 --- /dev/null +++ b/tests/tcg/riscv64/test-fcvtmod.c @@ -0,0 +1,345 @@ +#include +#include +#include + +#define FFLAG_NX_SHIFT 0 /* inexact */ +#define FFLAG_UF_SHIFT 1 /* underflow */ +#define FFLAG_OF_SHIFT 2 /* overflow */ +#define FFLAG_DZ_SHIFT 3 /* divide by zero */ +#define FFLAG_NV_SHIFT 4 /* invalid operation */ + +#define FFLAG_NV (1UL << FFLAG_NV_SHIFT) +#define FFLAG_DZ (1UL << FFLAG_DZ_SHIFT) +#define FFLAG_OF (1UL << FFLAG_OF_SHIFT) +#define FFLAG_UF (1UL << FFLAG_UF_SHIFT) +#define FFLAG_NX (1UL << FFLAG_NX_SHIFT) + +typedef struct fp64_fcvt_fcvtmod_testcase { + const char* name; + union { + uint64_t inp_lu; + double inp_lf; + }; + uint64_t exp_fcvt; + uint8_t exp_fcvt_fflags; + uint64_t exp_fcvtmod; + uint8_t exp_fcvtmod_fflags; +} fp64_fcvt_fcvtmod_testcase_t; + +void print_fflags(uint8_t fflags) +{ + int set = 0; + + if (fflags == 0) { + printf("-"); + return; + } + + if (fflags & FFLAG_NV) { + printf("%sFFLAG_NV", set ? " | " : ""); + set = 1; + } + if (fflags & FFLAG_DZ) { + printf("%sFFLAG_DZ", set ? " | " : ""); + set = 1; + } + if (fflags & FFLAG_OF) { + printf("%sFFLAG_OF", set ? " | " : ""); + set = 1; + } + if (fflags & FFLAG_UF) { + printf("%sFFLAG_UF", set ? " | " : ""); + set = 1; + } + if (fflags & FFLAG_NX) { + printf("%sFFLAG_NX", set ? " | " : ""); + set = 1; + } +} + +/* Clear all FP flags. */ +static inline void clear_fflags() +{ + __asm__ __volatile__("fsflags zero"); +} + +/* Read all FP flags. */ +static inline uint8_t get_fflags() +{ + uint64_t v; + __asm__ __volatile__("frflags %0" : "=r"(v)); + return (uint8_t)v; +} + +/* Move input value (without conversations) into an FP register. */ +static inline double do_fmv_d_x(uint64_t inp) +{ + double fpr; + __asm__ __volatile__("fmv.d.x %0, %1" : "=f"(fpr) : "r"(inp)); + return fpr; +} + +static inline uint64_t do_fcvt_w_d(uint64_t inp, uint8_t *fflags) +{ + uint64_t ret; + double fpr = do_fmv_d_x(inp); + + clear_fflags(); + + __asm__ __volatile__("fcvt.w.d %0, %1, rtz" : "=r"(ret) : "f"(fpr)); + + *fflags = get_fflags(); + + return ret; +} + +static inline uint64_t do_fcvtmod_w_d(uint64_t inp, uint8_t *fflags) +{ + uint64_t ret; + double fpr = do_fmv_d_x(inp); + + clear_fflags(); + + /* fcvtmod.w.d rd, rs1, rtz = 1100001 01000 rs1 001 rd 1010011 */ + asm(".insn r 0x53, 0x1, 0x61, %0, %1, f8" : "=r"(ret) : "f"(fpr)); + + *fflags = get_fflags(); + + return ret; +} + +static const fp64_fcvt_fcvtmod_testcase_t tests[] = { + /* Zero (exp=0, frac=0) */ + { .name = "+0.0", + .inp_lf = 0x0p0, + .exp_fcvt = 0x0000000000000000, + .exp_fcvt_fflags = 0, + .exp_fcvtmod = 0x0000000000000000, + .exp_fcvtmod_fflags = 0 }, + { .name = "-0.0", + .inp_lf = -0x0p0, + .exp_fcvt = 0x0000000000000000, + .exp_fcvt_fflags = 0, + .exp_fcvtmod = 0x0000000000000000, + .exp_fcvtmod_fflags = 0 }, + + /* Subnormal: exp=0 frac!=0 */ + { .name = "Subnormal frac=1", + .inp_lu = 0x0000000000000001, + .exp_fcvt = 0x0000000000000000, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + { .name = "Subnormal frac=0xf..f", + .inp_lu = 0x0000ffffffffffff, + .exp_fcvt = 0x0000000000000000, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + { .name = "Neg subnormal frac=1", + .inp_lu = 0x0000000000000001, + .exp_fcvt = 0x0000000000000000, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + { .name = "Neg subnormal frac=0xf..f", + .inp_lu = 0x8000ffffffffffff, + .exp_fcvt = 0x0000000000000000, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + + /* Infinity: exp=0x7ff, frac=0 */ + { .name = "+INF", + .inp_lu = 0x7ff0000000000000, + .exp_fcvt = 0x000000007fffffff, /* int32 max */ + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NV }, + { .name = "-INF", + .inp_lu = 0xfff0000000000000, + .exp_fcvt = 0xffffffff80000000, /* int32 min */ + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NV }, + + /* NaN: exp=7ff, frac!=0 */ + { .name = "canonical NaN", + .inp_lu = 0x7ff8000000000000, + .exp_fcvt = 0x000000007fffffff, /* int32 max */ + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NV }, + { .name = "non-canonical NaN", + .inp_lu = 0x7ff8000000100000, + .exp_fcvt = 0x000000007fffffff, /* int32 min */ + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NV }, + + /* Normal numbers: exp!=0, exp!=7ff */ + { .name = "+smallest normal value", + .inp_lu = 0x0010000000000000, + .exp_fcvt = 0, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + { .name = "-smallest normal value", + .inp_lu = 0x8010000000000000, + .exp_fcvt = 0, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + + { .name = "+0.5", + .inp_lf = 0x1p-1, + .exp_fcvt = 0, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + { .name = "-0.5", + .inp_lf = -0x1p-1, + .exp_fcvt = 0, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + + { .name = "+value just below 1.0", + .inp_lu = 0x3fefffffffffffff, + .exp_fcvt = 0, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + { .name = "-value just above -1.0", + .inp_lu = 0xbfefffffffffffff, + .exp_fcvt = 0, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0, + .exp_fcvtmod_fflags = FFLAG_NX }, + + { .name = "+1.0", + .inp_lf = 0x1p0, + .exp_fcvt = 0x0000000000000001, + .exp_fcvt_fflags = 0, + .exp_fcvtmod = 0x0000000000000001, + .exp_fcvtmod_fflags = 0 }, + { .name = "-1.0", + .inp_lf = -0x1p0, + .exp_fcvt = 0xffffffffffffffff, + .exp_fcvt_fflags = 0, + .exp_fcvtmod = 0xffffffffffffffff, + .exp_fcvtmod_fflags = 0 }, + + { .name = "+1.5", + .inp_lu = 0x3ff8000000000000, + .exp_fcvt = 1, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 1, + .exp_fcvtmod_fflags = FFLAG_NX }, + { .name = "-1.5", + .inp_lu = 0xbff8000000000000, + .exp_fcvt = 0xffffffffffffffff, + .exp_fcvt_fflags = FFLAG_NX, + .exp_fcvtmod = 0xffffffffffffffff, + .exp_fcvtmod_fflags = FFLAG_NX }, + + { .name = "+max int32 (2147483647)", + .inp_lu = 0x41dfffffffc00000, + .exp_fcvt = 0x000000007fffffff, + .exp_fcvt_fflags = 0, + .exp_fcvtmod = 0x000000007fffffff, + .exp_fcvtmod_fflags = 0 }, + { .name = "+max int32 +1 (2147483648)", + .inp_lf = 0x1p31, + .exp_fcvt = 0x000000007fffffff, + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = (uint64_t)-2147483648l, /* int32 min */ + .exp_fcvtmod_fflags = FFLAG_NV }, + { .name = "+max int32 +2 (2147483649)", + .inp_lu = 0x41e0000000200000, + .exp_fcvt = 0x000000007fffffff, + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = (uint64_t)-2147483647l, /* int32 min +1 */ + .exp_fcvtmod_fflags = FFLAG_NV }, + + { .name = "-max int32 (-2147483648)", + .inp_lf = -0x1p31, + .exp_fcvt = 0xffffffff80000000, + .exp_fcvt_fflags = 0, + .exp_fcvtmod = 0xffffffff80000000, + .exp_fcvtmod_fflags = 0 }, + { .name = "-max int32 -1 (-2147483649)", + .inp_lf = -0x1.00000002p+31, + .exp_fcvt = 0xffffffff80000000, + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = 2147483647, /* int32 max */ + .exp_fcvtmod_fflags = FFLAG_NV }, + { .name = "-max int32 -2 (-2147483650)", + .inp_lf = -0x1.00000004p+31, + .exp_fcvt = 0xffffffff80000000, + .exp_fcvt_fflags = FFLAG_NV, + .exp_fcvtmod = 2147483646, /* int32 max -1 */ + .exp_fcvtmod_fflags = FFLAG_NV }, +}; + +int run_fcvtmod_tests() +{ + uint64_t act_fcvt; + uint8_t act_fcvt_fflags; + uint64_t act_fcvtmod; + uint8_t act_fcvtmod_fflags; + + for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) { + const fp64_fcvt_fcvtmod_testcase_t *t = &tests[i]; + + act_fcvt = do_fcvt_w_d(t->inp_lu, &act_fcvt_fflags); + int fcvt_correct = act_fcvt == t->exp_fcvt && + act_fcvt_fflags == t->exp_fcvt_fflags; + act_fcvtmod = do_fcvtmod_w_d(t->inp_lu, &act_fcvtmod_fflags); + int fcvtmod_correct = act_fcvtmod == t->exp_fcvtmod && + act_fcvtmod_fflags == t->exp_fcvtmod_fflags; + + if (fcvt_correct && fcvtmod_correct) { + continue; + } + + printf("Test %zu (%s) failed!\n", i, t->name); + + double fpr = do_fmv_d_x(t->inp_lu); + printf("inp_lu: 0x%016lx == %lf\n", t->inp_lu, fpr); + printf("inp_lf: %lf\n", t->inp_lf); + + uint32_t sign = (t->inp_lu >> 63); + uint32_t exp = (uint32_t)(t->inp_lu >> 52) & 0x7ff; + uint64_t frac = t->inp_lu & 0xfffffffffffffull; /* significand */ + int true_exp = exp - 1023; + int shift = true_exp - 52; + uint64_t true_frac = frac | 1ull << 52; + + printf("sign=%d, exp=0x%03x, frac=0x%012lx\n", sign, exp, frac); + printf("true_exp=%d, shift=%d, true_frac=0x%016lx\n", true_exp, shift, true_frac); + + if (!fcvt_correct) { + printf("act_fcvt: 0x%016lx == %li\n", act_fcvt, act_fcvt); + printf("exp_fcvt: 0x%016lx == %li\n", t->exp_fcvt, t->exp_fcvt); + printf("act_fcvt_fflags: "); print_fflags(act_fcvt_fflags); printf("\n"); + printf("exp_fcvt_fflags: "); print_fflags(t->exp_fcvt_fflags); printf("\n"); + } + + if (!fcvtmod_correct) { + printf("act_fcvtmod: 0x%016lx == %li\n", act_fcvtmod, act_fcvtmod); + printf("exp_fcvtmod: 0x%016lx == %li\n", t->exp_fcvtmod, t->exp_fcvtmod); + printf("act_fcvtmod_fflags: "); print_fflags(act_fcvtmod_fflags); printf("\n"); + printf("exp_fcvtmod_fflags: "); print_fflags(t->exp_fcvtmod_fflags); printf("\n"); + } + + return 1; + } + + return 0; +} + +int main() +{ + return run_fcvtmod_tests(); +} diff --git a/tests/tcg/riscv64/test-noc.S b/tests/tcg/riscv64/test-noc.S new file mode 100644 index 0000000000..e29d60c8b3 --- /dev/null +++ b/tests/tcg/riscv64/test-noc.S @@ -0,0 +1,32 @@ +#include + + .text + .globl _start +_start: + .option norvc + li a0, 4 /* SIGILL */ + la a1, sa + li a2, 0 + li a3, 8 + li a7, __NR_rt_sigaction + scall + + .option rvc + li a0, 1 + j exit + .option norvc + +pass: + li a0, 0 +exit: + li a7, __NR_exit + scall + + .data + /* struct kernel_sigaction sa = { .sa_handler = pass }; */ + .type sa, @object + .size sa, 32 +sa: + .dword pass + .zero 24 + diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target new file mode 100644 index 0000000000..1a1f088b28 --- /dev/null +++ b/tests/tcg/s390x/Makefile.softmmu-target @@ -0,0 +1,47 @@ +S390X_SRC=$(SRC_PATH)/tests/tcg/s390x +VPATH+=$(S390X_SRC) +QEMU_OPTS=-action panic=exit-failure -nographic -kernel +LINK_SCRIPT=$(S390X_SRC)/softmmu.ld +CFLAGS+=-ggdb -O0 +LDFLAGS=-nostdlib -static + +%.o: %.S + $(CC) -march=z13 -m64 -c $< -o $@ + +%.o: %.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -march=z13 -m64 -c $< -o $@ + +%: %.o + $(CC) $< -o $@ $(LDFLAGS) + +ASM_TESTS = \ + bal \ + cksm \ + clm \ + exrl-ssm-early \ + icm \ + sam \ + lpsw \ + lpswe-early \ + lra \ + mc \ + precise-smc-softmmu \ + ssm-early \ + stosm-early \ + stpq \ + unaligned-lowcore + +include $(S390X_SRC)/pgm-specification.mak +$(PGM_SPECIFICATION_TESTS): pgm-specification-softmmu.o +$(PGM_SPECIFICATION_TESTS): LDFLAGS+=pgm-specification-softmmu.o +ASM_TESTS += $(PGM_SPECIFICATION_TESTS) + +$(ASM_TESTS): LDFLAGS += -Wl,-T$(LINK_SCRIPT) -Wl,--build-id=none +$(ASM_TESTS): $(LINK_SCRIPT) +TESTS += $(ASM_TESTS) + +S390X_MULTIARCH_RUNTIME_OBJS = head64.o console.o $(MINILIB_OBJS) +$(MULTIARCH_TESTS): $(S390X_MULTIARCH_RUNTIME_OBJS) +$(MULTIARCH_TESTS): LDFLAGS += $(S390X_MULTIARCH_RUNTIME_OBJS) +$(MULTIARCH_TESTS): CFLAGS += $(MINILIB_INC) +memory: CFLAGS += -DCHECK_UNALIGNED=0 diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 3124172736..a8f86c9449 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -1,6 +1,16 @@ S390X_SRC=$(SRC_PATH)/tests/tcg/s390x VPATH+=$(S390X_SRC) CFLAGS+=-march=zEC12 -m64 + +%.o: %.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@ + +config-cc.mak: Makefile + $(quiet-@)( \ + $(call cc-option,-march=z14, CROSS_CC_HAS_Z14); \ + $(call cc-option,-march=z15, CROSS_CC_HAS_Z15)) 3> config-cc.mak +-include config-cc.mak + TESTS+=hello-s390x TESTS+=csst TESTS+=ipm @@ -16,26 +26,88 @@ TESTS+=shift TESTS+=trap TESTS+=signals-s390x TESTS+=branch-relative-long +TESTS+=noexec +TESTS+=div +TESTS+=clst +TESTS+=long-double +TESTS+=cdsg +TESTS+=chrl +TESTS+=rxsbg +TESTS+=ex-relative-long +TESTS+=ex-branch +TESTS+=mxdb +TESTS+=epsw +TESTS+=larl +TESTS+=mdeb +TESTS+=cgebra +TESTS+=clgebr +TESTS+=clc +TESTS+=laalg +TESTS+=add-logical-with-carry +TESTS+=lae +TESTS+=cvd +TESTS+=cvb +TESTS+=ts -VECTOR_TESTS=vxeh2_vs -VECTOR_TESTS+=vxeh2_vcvt -VECTOR_TESTS+=vxeh2_vlstr -$(VECTOR_TESTS): CFLAGS+=-march=z15 -O2 +cdsg: CFLAGS+=-pthread +cdsg: LDFLAGS+=-pthread -TESTS+=$(if $(shell $(CC) -march=z15 -S -o /dev/null -xc /dev/null \ - >/dev/null 2>&1 && echo OK),$(VECTOR_TESTS)) +rxsbg: CFLAGS+=-O2 -ifneq ($(HAVE_GDB_BIN),) +cgebra: LDFLAGS+=-lm +clgebr: LDFLAGS+=-lm + +include $(S390X_SRC)/pgm-specification.mak +$(PGM_SPECIFICATION_TESTS): pgm-specification-user.o +$(PGM_SPECIFICATION_TESTS): LDFLAGS+=pgm-specification-user.o +TESTS += $(PGM_SPECIFICATION_TESTS) + +Z13_TESTS=vistr +Z13_TESTS+=lcbb +Z13_TESTS+=locfhr +Z13_TESTS+=vcksm +Z13_TESTS+=vstl +Z13_TESTS+=vrep +Z13_TESTS+=precise-smc-user +$(Z13_TESTS): CFLAGS+=-march=z13 -O2 +TESTS+=$(Z13_TESTS) + +ifneq ($(CROSS_CC_HAS_Z14),) +Z14_TESTS=vfminmax +vfminmax: LDFLAGS+=-lm +$(Z14_TESTS): CFLAGS+=-march=z14 -O2 +TESTS+=$(Z14_TESTS) +endif + +ifneq ($(CROSS_CC_HAS_Z15),) +Z15_TESTS=vxeh2_vs +Z15_TESTS+=vxeh2_vcvt +Z15_TESTS+=vxeh2_vlstr +Z15_TESTS+=vxeh2_vstrs +$(Z15_TESTS): CFLAGS+=-march=z15 -O2 +TESTS+=$(Z15_TESTS) +endif + +ifneq ($(GDB),) GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py run-gdbstub-signals-s390x: signals-s390x $(call run-test, $@, $(GDB_SCRIPT) \ - --gdb $(HAVE_GDB_BIN) \ + --gdb $(GDB) \ --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ --bin $< --test $(S390X_SRC)/gdbstub/test-signals-s390x.py, \ - "mixing signals and debugging on s390x") + mixing signals and debugging) -EXTRA_RUNS += run-gdbstub-signals-s390x +hello-s390x-asm: CFLAGS+=-nostdlib + +run-gdbstub-svc: hello-s390x-asm + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(S390X_SRC)/gdbstub/test-svc.py, \ + single-stepping svc) + +EXTRA_RUNS += run-gdbstub-signals-s390x run-gdbstub-svc endif # MVX versions of sha512 diff --git a/tests/tcg/s390x/add-logical-with-carry.c b/tests/tcg/s390x/add-logical-with-carry.c new file mode 100644 index 0000000000..d982f8a651 --- /dev/null +++ b/tests/tcg/s390x/add-logical-with-carry.c @@ -0,0 +1,156 @@ +/* + * Test ADD LOGICAL WITH CARRY instructions. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +static const struct test { + const char *name; + unsigned long values[3]; + unsigned long exp_sum; + int exp_cc; +} tests[] = { + /* + * Each test starts with CC 0 and executes two chained ADD LOGICAL WITH + * CARRY instructions on three input values. The values must be compatible + * with both 32- and 64-bit test functions. + */ + + /* NAME VALUES EXP_SUM EXP_CC */ + { "cc0->cc0", {0, 0, 0}, 0, 0, }, + { "cc0->cc1", {0, 0, 42}, 42, 1, }, + /* cc0->cc2 is not possible */ + /* cc0->cc3 is not possible */ + /* cc1->cc0 is not possible */ + { "cc1->cc1", {-3, 1, 1}, -1, 1, }, + { "cc1->cc2", {-3, 1, 2}, 0, 2, }, + { "cc1->cc3", {-3, 1, -1}, -3, 3, }, + /* cc2->cc0 is not possible */ + { "cc2->cc1", {-1, 1, 1}, 2, 1, }, + { "cc2->cc2", {-1, 1, -1}, 0, 2, }, + /* cc2->cc3 is not possible */ + /* cc3->cc0 is not possible */ + { "cc3->cc1", {-1, 2, 1}, 3, 1, }, + { "cc3->cc2", {-1, 2, -2}, 0, 2, }, + { "cc3->cc3", {-1, 2, -1}, 1, 3, }, +}; + +/* Test ALCR (register variant) followed by ALC (memory variant). */ +static unsigned long test32rm(unsigned long a, unsigned long b, + unsigned long c, int *cc) +{ + unsigned int a32 = a, b32 = b, c32 = c; + + asm("xr %[cc],%[cc]\n" + "alcr %[a],%[b]\n" + "alc %[a],%[c]\n" + "ipm %[cc]" + : [a] "+&r" (a32), [cc] "+&r" (*cc) + : [b] "r" (b32), [c] "T" (c32) + : "cc"); + *cc >>= 28; + + return (int)a32; +} + +/* Test ALC (memory variant) followed by ALCR (register variant). */ +static unsigned long test32mr(unsigned long a, unsigned long b, + unsigned long c, int *cc) +{ + unsigned int a32 = a, b32 = b, c32 = c; + + asm("xr %[cc],%[cc]\n" + "alc %[a],%[b]\n" + "alcr %[c],%[a]\n" + "ipm %[cc]" + : [a] "+&r" (a32), [c] "+&r" (c32), [cc] "+&r" (*cc) + : [b] "T" (b32) + : "cc"); + *cc >>= 28; + + return (int)c32; +} + +/* Test ALCGR (register variant) followed by ALCG (memory variant). */ +static unsigned long test64rm(unsigned long a, unsigned long b, + unsigned long c, int *cc) +{ + asm("xr %[cc],%[cc]\n" + "alcgr %[a],%[b]\n" + "alcg %[a],%[c]\n" + "ipm %[cc]" + : [a] "+&r" (a), [cc] "+&r" (*cc) + : [b] "r" (b), [c] "T" (c) + : "cc"); + *cc >>= 28; + return a; +} + +/* Test ALCG (memory variant) followed by ALCGR (register variant). */ +static unsigned long test64mr(unsigned long a, unsigned long b, + unsigned long c, int *cc) +{ + asm("xr %[cc],%[cc]\n" + "alcg %[a],%[b]\n" + "alcgr %[c],%[a]\n" + "ipm %[cc]" + : [a] "+&r" (a), [c] "+&r" (c), [cc] "+&r" (*cc) + : [b] "T" (b) + : "cc"); + *cc >>= 28; + return c; +} + +static const struct test_func { + const char *name; + unsigned long (*ptr)(unsigned long, unsigned long, unsigned long, int *); +} test_funcs[] = { + { "test32rm", test32rm }, + { "test32mr", test32mr }, + { "test64rm", test64rm }, + { "test64mr", test64mr }, +}; + +static const struct test_perm { + const char *name; + size_t a_idx, b_idx, c_idx; +} test_perms[] = { + { "a, b, c", 0, 1, 2 }, + { "b, a, c", 1, 0, 2 }, +}; + +int main(void) +{ + unsigned long a, b, c, sum; + int result = EXIT_SUCCESS; + const struct test_func *f; + const struct test_perm *p; + size_t i, j, k; + const struct test *t; + int cc; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + t = &tests[i]; + for (j = 0; j < sizeof(test_funcs) / sizeof(test_funcs[0]); j++) { + f = &test_funcs[j]; + for (k = 0; k < sizeof(test_perms) / sizeof(test_perms[0]); k++) { + p = &test_perms[k]; + a = t->values[p->a_idx]; + b = t->values[p->b_idx]; + c = t->values[p->c_idx]; + sum = f->ptr(a, b, c, &cc); + if (sum != t->exp_sum || cc != t->exp_cc) { + fprintf(stderr, + "[ FAILED ] %s %s(0x%lx, 0x%lx, 0x%lx) returned 0x%lx cc %d, expected 0x%lx cc %d\n", + t->name, f->name, a, b, c, sum, cc, + t->exp_sum, t->exp_cc); + result = EXIT_FAILURE; + } + } + } + } + + return result; +} diff --git a/tests/tcg/s390x/bal.S b/tests/tcg/s390x/bal.S new file mode 100644 index 0000000000..e54d8874ff --- /dev/null +++ b/tests/tcg/s390x/bal.S @@ -0,0 +1,24 @@ + .org 0x200 /* lowcore padding */ + .globl _start +_start: + lpswe start24_psw +_start24: + lgrl %r0,initial_r0 + lgrl %r1,expected_r0 + bal %r0,0f +0: + cgrjne %r0,%r1,1f + lpswe success_psw +1: + lpswe failure_psw + .align 8 +start24_psw: + .quad 0x160000000000,_start24 /* 24-bit mode, cc = 1, pm = 6 */ +initial_r0: + .quad 0x1234567887654321 +expected_r0: + .quad 0x1234567896000000 + 0b /* ilc = 2, cc = 1, pm = 6 */ +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/br-odd.S b/tests/tcg/s390x/br-odd.S new file mode 100644 index 0000000000..2fae47a9e3 --- /dev/null +++ b/tests/tcg/s390x/br-odd.S @@ -0,0 +1,16 @@ +/* + * Test BRanching to a non-mapped odd address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + lgrl %r1,odd_addr + br %r1 + + .align 8 +odd_addr: + .quad 0xDDDDDDDDDDDDDDDD + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,0xDDDDDDDDDDDDDDDD diff --git a/tests/tcg/s390x/cdsg.c b/tests/tcg/s390x/cdsg.c new file mode 100644 index 0000000000..800618ff4b --- /dev/null +++ b/tests/tcg/s390x/cdsg.c @@ -0,0 +1,93 @@ +/* + * Test CDSG instruction. + * + * Increment the first half of aligned_quadword by 1, and the second half by 2 + * from 2 threads. Verify that the result is consistent. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include + +static volatile bool start; +typedef unsigned long aligned_quadword[2] __attribute__((__aligned__(16))); +static aligned_quadword val; +static const int n_iterations = 1000000; + +static inline int cdsg(unsigned long *orig0, unsigned long *orig1, + unsigned long new0, unsigned long new1, + aligned_quadword *mem) +{ + register unsigned long r0 asm("r0"); + register unsigned long r1 asm("r1"); + register unsigned long r2 asm("r2"); + register unsigned long r3 asm("r3"); + int cc; + + r0 = *orig0; + r1 = *orig1; + r2 = new0; + r3 = new1; + asm("cdsg %[r0],%[r2],%[db2]\n" + "ipm %[cc]" + : [r0] "+r" (r0) + , [r1] "+r" (r1) + , [db2] "+m" (*mem) + , [cc] "=r" (cc) + : [r2] "r" (r2) + , [r3] "r" (r3) + : "cc"); + *orig0 = r0; + *orig1 = r1; + + return (cc >> 28) & 3; +} + +void *cdsg_loop(void *arg) +{ + unsigned long orig0, orig1, new0, new1; + int cc; + int i; + + while (!start) { + } + + orig0 = val[0]; + orig1 = val[1]; + for (i = 0; i < n_iterations;) { + new0 = orig0 + 1; + new1 = orig1 + 2; + + cc = cdsg(&orig0, &orig1, new0, new1, &val); + + if (cc == 0) { + orig0 = new0; + orig1 = new1; + i++; + } else { + assert(cc == 1); + } + } + + return NULL; +} + +int main(void) +{ + pthread_t thread; + int ret; + + ret = pthread_create(&thread, NULL, cdsg_loop, NULL); + assert(ret == 0); + start = true; + cdsg_loop(NULL); + ret = pthread_join(thread, NULL); + assert(ret == 0); + + assert(val[0] == n_iterations * 2); + assert(val[1] == n_iterations * 4); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/cgebra.c b/tests/tcg/s390x/cgebra.c new file mode 100644 index 0000000000..f91e10d2d3 --- /dev/null +++ b/tests/tcg/s390x/cgebra.c @@ -0,0 +1,32 @@ +/* + * Test the CGEBRA instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include + +int main(void) +{ + float r2 = 1E+300; + long long r1; + int cc; + + feclearexcept(FE_ALL_EXCEPT); + asm("cgebra %[r1],%[m3],%[r2],%[m4]\n" + "ipm %[cc]\n" + : [r1] "=r" (r1) + , [cc] "=r" (cc) + : [m3] "i" (5) /* round toward 0 */ + , [r2] "f" (r2) + , [m4] "i" (8) /* bit 0 is set, but must be ignored; XxC is not set */ + : "cc"); + cc >>= 28; + + assert(r1 == 0x7fffffffffffffffLL); + assert(cc == 3); + assert(fetestexcept(FE_ALL_EXCEPT) == (FE_INVALID | FE_INEXACT)); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/cgrl-unaligned.S b/tests/tcg/s390x/cgrl-unaligned.S new file mode 100644 index 0000000000..164d68f2e6 --- /dev/null +++ b/tests/tcg/s390x/cgrl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test CGRL with a non-doubleword aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + cgrl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .long 0 +unaligned: + .quad 0 diff --git a/tests/tcg/s390x/chrl.c b/tests/tcg/s390x/chrl.c new file mode 100644 index 0000000000..b1c3a1c561 --- /dev/null +++ b/tests/tcg/s390x/chrl.c @@ -0,0 +1,80 @@ +#include +#include +#include + +static void test_chrl(void) +{ + uint32_t program_mask, cc; + + asm volatile ( + ".pushsection .rodata\n" + "0:\n\t" + ".short 1, 0x8000\n\t" + ".popsection\n\t" + + "chrl %[r], 0b\n\t" + "ipm %[program_mask]\n" + : [program_mask] "=r" (program_mask) + : [r] "r" (1) + ); + + cc = program_mask >> 28; + assert(!cc); + + asm volatile ( + ".pushsection .rodata\n" + "0:\n\t" + ".short -1, 0x8000\n\t" + ".popsection\n\t" + + "chrl %[r], 0b\n\t" + "ipm %[program_mask]\n" + : [program_mask] "=r" (program_mask) + : [r] "r" (-1) + ); + + cc = program_mask >> 28; + assert(!cc); +} + +static void test_cghrl(void) +{ + uint32_t program_mask, cc; + + asm volatile ( + ".pushsection .rodata\n" + "0:\n\t" + ".short 1, 0x8000, 0, 0\n\t" + ".popsection\n\t" + + "cghrl %[r], 0b\n\t" + "ipm %[program_mask]\n" + : [program_mask] "=r" (program_mask) + : [r] "r" (1L) + ); + + cc = program_mask >> 28; + assert(!cc); + + asm volatile ( + ".pushsection .rodata\n" + "0:\n\t" + ".short -1, 0x8000, 0, 0\n\t" + ".popsection\n\t" + + "cghrl %[r], 0b\n\t" + "ipm %[program_mask]\n" + : [program_mask] "=r" (program_mask) + : [r] "r" (-1L) + ); + + cc = program_mask >> 28; + assert(!cc); +} + +int main(void) +{ + test_chrl(); + test_cghrl(); + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/cksm.S b/tests/tcg/s390x/cksm.S new file mode 100644 index 0000000000..563fd3d233 --- /dev/null +++ b/tests/tcg/s390x/cksm.S @@ -0,0 +1,29 @@ + .org 0x8e +program_interruption_code: + .org 0x1d0 /* program new PSW */ + .quad 0,pgm + .org 0x200 /* lowcore padding */ + .globl _start +_start: + lmg %r0,%r1,cksm_args + cksm %r2,%r0 + c %r2,cksm_exp + jne failure + .insn rre,0xb2410000,%r2,%r15 /* cksm %r2,%r15 */ +failure: + lpswe failure_psw +pgm: + chhsi program_interruption_code,6 /* specification exception? */ + jne failure + lpswe success_psw +cksm_args: + .quad cksm_buf, 16 +cksm_buf: + .quad 0xaaaabbbbcccc0000, 0x12345678 +cksm_exp: + .long 0x89ab1234 + .align 8 +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/clc.c b/tests/tcg/s390x/clc.c new file mode 100644 index 0000000000..e14189bd75 --- /dev/null +++ b/tests/tcg/s390x/clc.c @@ -0,0 +1,48 @@ +/* + * Test the CLC instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include + +static void handle_sigsegv(int sig, siginfo_t *info, void *ucontext) +{ + mcontext_t *mcontext = &((ucontext_t *)ucontext)->uc_mcontext; + if (mcontext->gregs[0] != 600) { + write(STDERR_FILENO, "bad r0\n", 7); + _exit(EXIT_FAILURE); + } + if (((mcontext->psw.mask >> 44) & 3) != 1) { + write(STDERR_FILENO, "bad cc\n", 7); + _exit(EXIT_FAILURE); + } + _exit(EXIT_SUCCESS); +} + +int main(void) +{ + register unsigned long r0 asm("r0"); + unsigned long mem = 42, rhs = 500; + struct sigaction act; + int err; + + memset(&act, 0, sizeof(act)); + act.sa_sigaction = handle_sigsegv; + act.sa_flags = SA_SIGINFO; + err = sigaction(SIGSEGV, &act, NULL); + assert(err == 0); + + r0 = 100; + asm("algr %[r0],%[rhs]\n" + "clc 0(8,%[mem]),0(0)\n" /* The 2nd operand will cause a SEGV. */ + : [r0] "+r" (r0) + : [mem] "r" (&mem) + , [rhs] "r" (rhs) + : "cc", "memory"); + + return EXIT_FAILURE; +} diff --git a/tests/tcg/s390x/clgebr.c b/tests/tcg/s390x/clgebr.c new file mode 100644 index 0000000000..d491899b56 --- /dev/null +++ b/tests/tcg/s390x/clgebr.c @@ -0,0 +1,32 @@ +/* + * Test the CLGEBR instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include + +int main(void) +{ + float r2 = -1; + long long r1; + int cc; + + feclearexcept(FE_ALL_EXCEPT); + asm("clgebr %[r1],%[m3],%[r2],%[m4]\n" + "ipm %[cc]\n" + : [r1] "=r" (r1) + , [cc] "=r" (cc) + : [m3] "i" (5) /* round toward 0 */ + , [r2] "f" (r2) + , [m4] "i" (8) /* bit 0 is set, but must be ignored; XxC is not set */ + : "cc"); + cc >>= 28; + + assert(r1 == 0); + assert(cc == 3); + assert(fetestexcept(FE_ALL_EXCEPT) == (FE_INVALID | FE_INEXACT)); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/clm.S b/tests/tcg/s390x/clm.S new file mode 100644 index 0000000000..17156a81f2 --- /dev/null +++ b/tests/tcg/s390x/clm.S @@ -0,0 +1,29 @@ + .org 0x8e +program_interruption_code: + .org 0x1d0 /* program new PSW */ + .quad 0,pgm + .org 0x200 /* lowcore padding */ + .globl _start +_start: + lgrl %r0,op1 + clm %r0,6,op2 + jle failure + lgrl %r1,bad_addr + clm %r0,0,0(%r1) +failure: + lpswe failure_psw +pgm: + chhsi program_interruption_code,5 /* addressing exception? */ + jne failure + lpswe success_psw + .align 8 +op1: + .quad 0x1234567887654321 +op2: + .quad 0x3456789abcdef012 +bad_addr: + .quad 0xffffffff00000000 +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/clrl-unaligned.S b/tests/tcg/s390x/clrl-unaligned.S new file mode 100644 index 0000000000..182b1b6462 --- /dev/null +++ b/tests/tcg/s390x/clrl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test CLRL with a non-word aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + clrl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .short 0 +unaligned: + .long 0 diff --git a/tests/tcg/s390x/clst.c b/tests/tcg/s390x/clst.c new file mode 100644 index 0000000000..ed2fe7326c --- /dev/null +++ b/tests/tcg/s390x/clst.c @@ -0,0 +1,82 @@ +#define _GNU_SOURCE +#include +#include + +static int clst(char sep, const char **s1, const char **s2) +{ + const char *r1 = *s1; + const char *r2 = *s2; + int cc; + + do { + register int r0 asm("r0") = sep; + + asm("clst %[r1],%[r2]\n" + "ipm %[cc]\n" + "srl %[cc],28" + : [r1] "+r" (r1), [r2] "+r" (r2), "+r" (r0), [cc] "=r" (cc) + : + : "cc"); + *s1 = r1; + *s2 = r2; + } while (cc == 3); + + return cc; +} + +static const struct test { + const char *name; + char sep; + const char *s1; + const char *s2; + int exp_cc; + int exp_off; +} tests[] = { + { + .name = "cc0", + .sep = 0, + .s1 = "aa", + .s2 = "aa", + .exp_cc = 0, + .exp_off = 0, + }, + { + .name = "cc1", + .sep = 1, + .s1 = "a\x01", + .s2 = "aa\x01", + .exp_cc = 1, + .exp_off = 1, + }, + { + .name = "cc2", + .sep = 2, + .s1 = "abc\x02", + .s2 = "abb\x02", + .exp_cc = 2, + .exp_off = 2, + }, +}; + +int main(void) +{ + const struct test *t; + const char *s1, *s2; + size_t i; + int cc; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + t = &tests[i]; + s1 = t->s1; + s2 = t->s2; + cc = clst(t->sep, &s1, &s2); + if (cc != t->exp_cc || + s1 != t->s1 + t->exp_off || + s2 != t->s2 + t->exp_off) { + fprintf(stderr, "%s\n", t->name); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/console.c b/tests/tcg/s390x/console.c new file mode 100644 index 0000000000..d43ce3f44b --- /dev/null +++ b/tests/tcg/s390x/console.c @@ -0,0 +1,12 @@ +/* + * Console code for multiarch tests. + * Reuses the pc-bios/s390-ccw implementation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "../../../pc-bios/s390-ccw/sclp.c" + +void __sys_outc(char c) +{ + write(1, &c, sizeof(c)); +} diff --git a/tests/tcg/s390x/crl-unaligned.S b/tests/tcg/s390x/crl-unaligned.S new file mode 100644 index 0000000000..b86fbe0ef3 --- /dev/null +++ b/tests/tcg/s390x/crl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test CRL with a non-word aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + crl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .short 0 +unaligned: + .long 0 diff --git a/tests/tcg/s390x/cvb.c b/tests/tcg/s390x/cvb.c new file mode 100644 index 0000000000..e1735f6b81 --- /dev/null +++ b/tests/tcg/s390x/cvb.c @@ -0,0 +1,102 @@ +/* + * Test the CONVERT TO BINARY instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include + +static int signum; + +static void signal_handler(int n) +{ + signum = n; +} + +#define FAIL 0x1234567887654321 +#define OK32(x) (0x1234567800000000 | (uint32_t)(x)) + +static int64_t cvb(uint64_t x) +{ + int64_t ret = FAIL; + + signum = -1; + asm("cvb %[ret],%[x]" : [ret] "+r" (ret) : [x] "R" (x)); + + return ret; +} + +static int64_t cvby(uint64_t x) +{ + int64_t ret = FAIL; + + signum = -1; + asm("cvby %[ret],%[x]" : [ret] "+r" (ret) : [x] "T" (x)); + + return ret; +} + +static int64_t cvbg(__uint128_t x) +{ + int64_t ret = FAIL; + + signum = -1; + asm("cvbg %[ret],%[x]" : [ret] "+r" (ret) : [x] "T" (x)); + + return ret; +} + +int main(void) +{ + __uint128_t m = (((__uint128_t)0x9223372036854775) << 16) | 0x8070; + struct sigaction act; + int err; + + memset(&act, 0, sizeof(act)); + act.sa_handler = signal_handler; + err = sigaction(SIGFPE, &act, NULL); + assert(err == 0); + err = sigaction(SIGILL, &act, NULL); + assert(err == 0); + + assert(cvb(0xc) == OK32(0) && signum == -1); + assert(cvb(0x1c) == OK32(1) && signum == -1); + assert(cvb(0x25594c) == OK32(25594) && signum == -1); + assert(cvb(0x1d) == OK32(-1) && signum == -1); + assert(cvb(0x2147483647c) == OK32(0x7fffffff) && signum == -1); + assert(cvb(0x2147483648d) == OK32(-0x80000000) && signum == -1); + assert(cvb(0x7) == FAIL && signum == SIGILL); + assert(cvb(0x2147483648c) == OK32(0x80000000) && signum == SIGFPE); + assert(cvb(0x3000000000c) == OK32(0xb2d05e00) && signum == SIGFPE); + assert(cvb(0x2147483649d) == OK32(0x7fffffff) && signum == SIGFPE); + assert(cvb(0x3000000000d) == OK32(0x4d2fa200) && signum == SIGFPE); + + assert(cvby(0xc) == OK32(0)); + assert(cvby(0x1c) == OK32(1)); + assert(cvby(0x25594c) == OK32(25594)); + assert(cvby(0x1d) == OK32(-1)); + assert(cvby(0x2147483647c) == OK32(0x7fffffff)); + assert(cvby(0x2147483648d) == OK32(-0x80000000)); + assert(cvby(0x7) == FAIL && signum == SIGILL); + assert(cvby(0x2147483648c) == OK32(0x80000000) && signum == SIGFPE); + assert(cvby(0x3000000000c) == OK32(0xb2d05e00) && signum == SIGFPE); + assert(cvby(0x2147483649d) == OK32(0x7fffffff) && signum == SIGFPE); + assert(cvby(0x3000000000d) == OK32(0x4d2fa200) && signum == SIGFPE); + + assert(cvbg(0xc) == 0); + assert(cvbg(0x1c) == 1); + assert(cvbg(0x25594c) == 25594); + assert(cvbg(0x1d) == -1); + assert(cvbg(m + 0xc) == 0x7fffffffffffffff); + assert(cvbg(m + 0x1d) == -0x8000000000000000); + assert(cvbg(0x7) == FAIL && signum == SIGILL); + assert(cvbg(m + 0x1c) == FAIL && signum == SIGFPE); + assert(cvbg(m + 0x2d) == FAIL && signum == SIGFPE); + assert(cvbg(((__uint128_t)1 << 80) + 0xc) == FAIL && signum == SIGFPE); + assert(cvbg(((__uint128_t)1 << 80) + 0xd) == FAIL && signum == SIGFPE); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/cvd.c b/tests/tcg/s390x/cvd.c new file mode 100644 index 0000000000..d776688985 --- /dev/null +++ b/tests/tcg/s390x/cvd.c @@ -0,0 +1,63 @@ +/* + * Test the CONVERT TO DECIMAL instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include + +static uint64_t cvd(int32_t x) +{ + uint64_t ret; + + asm("cvd %[x],%[ret]" : [ret] "=R" (ret) : [x] "r" (x)); + + return ret; +} + +static uint64_t cvdy(int32_t x) +{ + uint64_t ret; + + asm("cvdy %[x],%[ret]" : [ret] "=T" (ret) : [x] "r" (x)); + + return ret; +} + +static __uint128_t cvdg(int64_t x) +{ + __uint128_t ret; + + asm("cvdg %[x],%[ret]" : [ret] "=T" (ret) : [x] "r" (x)); + + return ret; +} + +int main(void) +{ + __uint128_t m = (((__uint128_t)0x9223372036854775) << 16) | 0x8070; + + assert(cvd(0) == 0xc); + assert(cvd(1) == 0x1c); + assert(cvd(25594) == 0x25594c); + assert(cvd(-1) == 0x1d); + assert(cvd(0x7fffffff) == 0x2147483647c); + assert(cvd(-0x80000000) == 0x2147483648d); + + assert(cvdy(0) == 0xc); + assert(cvdy(1) == 0x1c); + assert(cvdy(25594) == 0x25594c); + assert(cvdy(-1) == 0x1d); + assert(cvdy(0x7fffffff) == 0x2147483647c); + assert(cvdy(-0x80000000) == 0x2147483648d); + + assert(cvdg(0) == 0xc); + assert(cvdg(1) == 0x1c); + assert(cvdg(25594) == 0x25594c); + assert(cvdg(-1) == 0x1d); + assert(cvdg(0x7fffffffffffffff) == (m + 0xc)); + assert(cvdg(-0x8000000000000000) == (m + 0x1d)); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/div.c b/tests/tcg/s390x/div.c new file mode 100644 index 0000000000..6ad9900e08 --- /dev/null +++ b/tests/tcg/s390x/div.c @@ -0,0 +1,75 @@ +#include +#include + +static void test_dr(void) +{ + register int32_t r0 asm("r0") = -1; + register int32_t r1 asm("r1") = -4241; + int32_t b = 101, q, r; + + asm("dr %[r0],%[b]" + : [r0] "+r" (r0), [r1] "+r" (r1) + : [b] "r" (b) + : "cc"); + q = r1; + r = r0; + assert(q == -41); + assert(r == -100); +} + +static void test_dlr(void) +{ + register uint32_t r0 asm("r0") = 0; + register uint32_t r1 asm("r1") = 4243; + uint32_t b = 101, q, r; + + asm("dlr %[r0],%[b]" + : [r0] "+r" (r0), [r1] "+r" (r1) + : [b] "r" (b) + : "cc"); + q = r1; + r = r0; + assert(q == 42); + assert(r == 1); +} + +static void test_dsgr(void) +{ + register int64_t r0 asm("r0") = -1; + register int64_t r1 asm("r1") = -4241; + int64_t b = 101, q, r; + + asm("dsgr %[r0],%[b]" + : [r0] "+r" (r0), [r1] "+r" (r1) + : [b] "r" (b) + : "cc"); + q = r1; + r = r0; + assert(q == -41); + assert(r == -100); +} + +static void test_dlgr(void) +{ + register uint64_t r0 asm("r0") = 0; + register uint64_t r1 asm("r1") = 4243; + uint64_t b = 101, q, r; + + asm("dlgr %[r0],%[b]" + : [r0] "+r" (r0), [r1] "+r" (r1) + : [b] "r" (b) + : "cc"); + q = r1; + r = r0; + assert(q == 42); + assert(r == 1); +} + +int main(void) +{ + test_dr(); + test_dlr(); + test_dsgr(); + test_dlgr(); + return 0; +} diff --git a/tests/tcg/s390x/epsw.c b/tests/tcg/s390x/epsw.c new file mode 100644 index 0000000000..affb1a5e3a --- /dev/null +++ b/tests/tcg/s390x/epsw.c @@ -0,0 +1,23 @@ +/* + * Test the EPSW instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +int main(void) +{ + unsigned long r1 = 0x1234567887654321UL, r2 = 0x8765432112345678UL; + + asm("cr %[r1],%[r2]\n" /* cc = 1 */ + "epsw %[r1],%[r2]" + : [r1] "+r" (r1), [r2] "+r" (r2) : : "cc"); + + /* Do not check the R and RI bits. */ + r1 &= ~0x40000008UL; + assert(r1 == 0x1234567807051001UL); + assert(r2 == 0x8765432180000000UL); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/ex-branch.c b/tests/tcg/s390x/ex-branch.c new file mode 100644 index 0000000000..c606719152 --- /dev/null +++ b/tests/tcg/s390x/ex-branch.c @@ -0,0 +1,158 @@ +/* Check EXECUTE with relative branch instructions as targets. */ +#include +#include +#include +#include +#include + +struct test { + const char *name; + void (*func)(long *link, long *magic); + long exp_link; +}; + +/* Branch instructions and their expected effects. */ +#define LINK_64(test) ((long)test ## _exp_link) +#define LINK_NONE(test) -1L +#define FOR_EACH_INSN(F) \ + F(bras, "%[link]", LINK_64) \ + F(brasl, "%[link]", LINK_64) \ + F(brc, "0x8", LINK_NONE) \ + F(brcl, "0x8", LINK_NONE) \ + F(brct, "%%r0", LINK_NONE) \ + F(brctg, "%%r0", LINK_NONE) \ + F(brxh, "%%r2,%%r0", LINK_NONE) \ + F(brxhg, "%%r2,%%r0", LINK_NONE) \ + F(brxle, "%%r0,%%r1", LINK_NONE) \ + F(brxlg, "%%r0,%%r1", LINK_NONE) \ + F(crj, "%%r0,%%r0,8", LINK_NONE) \ + F(cgrj, "%%r0,%%r0,8", LINK_NONE) \ + F(cij, "%%r0,0,8", LINK_NONE) \ + F(cgij, "%%r0,0,8", LINK_NONE) \ + F(clrj, "%%r0,%%r0,8", LINK_NONE) \ + F(clgrj, "%%r0,%%r0,8", LINK_NONE) \ + F(clij, "%%r0,0,8", LINK_NONE) \ + F(clgij, "%%r0,0,8", LINK_NONE) + +#define INIT_TEST \ + "xgr %%r0,%%r0\n" /* %r0 = 0; %cc = 0 */ \ + "lghi %%r1,1\n" /* %r1 = 1 */ \ + "lghi %%r2,2\n" /* %r2 = 2 */ + +#define CLOBBERS_TEST "cc", "0", "1", "2" + +#define DEFINE_TEST(insn, args, exp_link) \ + extern char insn ## _exp_link[]; \ + static void test_ ## insn(long *link, long *magic) \ + { \ + asm(INIT_TEST \ + #insn " " args ",0f\n" \ + ".globl " #insn "_exp_link\n" \ + #insn "_exp_link:\n" \ + ".org . + 90\n" \ + "0: lgfi %[magic],0x12345678\n" \ + : [link] "+r" (*link) \ + , [magic] "+r" (*magic) \ + : : CLOBBERS_TEST); \ + } \ + extern char ex_ ## insn ## _exp_link[]; \ + static void test_ex_ ## insn(long *link, long *magic) \ + { \ + unsigned long target; \ + \ + asm(INIT_TEST \ + "larl %[target],0f\n" \ + "ex %%r0,0(%[target])\n" \ + ".globl ex_" #insn "_exp_link\n" \ + "ex_" #insn "_exp_link:\n" \ + ".org . + 60\n" \ + "0: " #insn " " args ",1f\n" \ + ".org . + 120\n" \ + "1: lgfi %[magic],0x12345678\n" \ + : [target] "=r" (target) \ + , [link] "+r" (*link) \ + , [magic] "+r" (*magic) \ + : : CLOBBERS_TEST); \ + } \ + extern char exrl_ ## insn ## _exp_link[]; \ + static void test_exrl_ ## insn(long *link, long *magic) \ + { \ + asm(INIT_TEST \ + "exrl %%r0,0f\n" \ + ".globl exrl_" #insn "_exp_link\n" \ + "exrl_" #insn "_exp_link:\n" \ + ".org . + 60\n" \ + "0: " #insn " " args ",1f\n" \ + ".org . + 120\n" \ + "1: lgfi %[magic],0x12345678\n" \ + : [link] "+r" (*link) \ + , [magic] "+r" (*magic) \ + : : CLOBBERS_TEST); \ + } + +/* Test functions. */ +FOR_EACH_INSN(DEFINE_TEST) + +/* Test definitions. */ +#define REGISTER_TEST(insn, args, _exp_link) \ + { \ + .name = #insn, \ + .func = test_ ## insn, \ + .exp_link = (_exp_link(insn)), \ + }, \ + { \ + .name = "ex " #insn, \ + .func = test_ex_ ## insn, \ + .exp_link = (_exp_link(ex_ ## insn)), \ + }, \ + { \ + .name = "exrl " #insn, \ + .func = test_exrl_ ## insn, \ + .exp_link = (_exp_link(exrl_ ## insn)), \ + }, + +static const struct test tests[] = { + FOR_EACH_INSN(REGISTER_TEST) +}; + +int main(int argc, char **argv) +{ + const struct test *test; + int ret = EXIT_SUCCESS; + bool verbose = false; + long link, magic; + size_t i; + + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-v") == 0) { + verbose = true; + } + } + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + test = &tests[i]; + if (verbose) { + fprintf(stderr, "[ RUN ] %s\n", test->name); + } + link = -1; + magic = -1; + test->func(&link, &magic); +#define ASSERT_EQ(expected, actual) do { \ + if (expected != actual) { \ + fprintf(stderr, "%s: " #expected " (0x%lx) != " #actual " (0x%lx)\n", \ + test->name, expected, actual); \ + ret = EXIT_FAILURE; \ + } \ +} while (0) + ASSERT_EQ(test->exp_link, link); + ASSERT_EQ(0x12345678L, magic); +#undef ASSERT_EQ + } + + if (verbose) { + fprintf(stderr, ret == EXIT_SUCCESS ? "[ PASSED ]\n" : + "[ FAILED ]\n"); + } + + return ret; +} diff --git a/tests/tcg/s390x/ex-odd.S b/tests/tcg/s390x/ex-odd.S new file mode 100644 index 0000000000..4e42a47df3 --- /dev/null +++ b/tests/tcg/s390x/ex-odd.S @@ -0,0 +1,17 @@ +/* + * Test EXECUTing a non-mapped odd address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + lgrl %r1,odd_addr +fail: + ex 0,0(%r1) + + .align 8 +odd_addr: + .quad 0xDDDDDDDDDDDDDDDD + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,fail diff --git a/tests/tcg/s390x/ex-relative-long.c b/tests/tcg/s390x/ex-relative-long.c new file mode 100644 index 0000000000..21fbef6258 --- /dev/null +++ b/tests/tcg/s390x/ex-relative-long.c @@ -0,0 +1,156 @@ +/* Check EXECUTE with relative long instructions as targets. */ +#include +#include + +struct test { + const char *name; + long (*func)(long reg, long *cc); + long exp_reg; + long exp_mem; + long exp_cc; +}; + +/* + * Each test sets the MEM_IDXth element of the mem array to MEM and uses a + * single relative long instruction on it. The other elements remain zero. + * This is in order to prevent stumbling upon MEM in random memory in case + * there is an off-by-a-small-value bug. + * + * Note that while gcc supports the ZL constraint for relative long operands, + * clang doesn't, so the assembly code accesses mem[MEM_IDX] using MEM_ASM. + */ +static long mem[0x1000]; +#define MEM_IDX 0x800 +#define MEM_ASM "mem+0x800*8" + +/* Initial %r2 value. */ +#define REG 0x1234567887654321 + +/* Initial mem[MEM_IDX] value. */ +#define MEM 0xfedcba9889abcdef + +/* Initial cc value. */ +#define CC 0 + +/* Relative long instructions and their expected effects. */ +#define FOR_EACH_INSN(F) \ + F(cgfrl, REG, MEM, 2) \ + F(cghrl, REG, MEM, 2) \ + F(cgrl, REG, MEM, 2) \ + F(chrl, REG, MEM, 1) \ + F(clgfrl, REG, MEM, 2) \ + F(clghrl, REG, MEM, 2) \ + F(clgrl, REG, MEM, 1) \ + F(clhrl, REG, MEM, 2) \ + F(clrl, REG, MEM, 1) \ + F(crl, REG, MEM, 1) \ + F(larl, (long)&mem[MEM_IDX], MEM, CC) \ + F(lgfrl, 0xfffffffffedcba98, MEM, CC) \ + F(lghrl, 0xfffffffffffffedc, MEM, CC) \ + F(lgrl, MEM, MEM, CC) \ + F(lhrl, 0x12345678fffffedc, MEM, CC) \ + F(llghrl, 0x000000000000fedc, MEM, CC) \ + F(llhrl, 0x123456780000fedc, MEM, CC) \ + F(lrl, 0x12345678fedcba98, MEM, CC) \ + F(stgrl, REG, REG, CC) \ + F(sthrl, REG, 0x4321ba9889abcdef, CC) \ + F(strl, REG, 0x8765432189abcdef, CC) + +/* Test functions. */ +#define DEFINE_EX_TEST(insn, exp_reg, exp_mem, exp_cc) \ + static long test_ex_ ## insn(long reg, long *cc) \ + { \ + register long r2 asm("r2"); \ + char mask = 0x20; /* make target use %r2 */ \ + long pm, target; \ + \ + r2 = reg; \ + asm("larl %[target],0f\n" \ + "cr %%r0,%%r0\n" /* initial cc */ \ + "ex %[mask],0(%[target])\n" \ + "jg 1f\n" \ + "0: " #insn " %%r0," MEM_ASM "\n" \ + "1: ipm %[pm]\n" \ + : [target] "=&a" (target), [r2] "+r" (r2), [pm] "=r" (pm) \ + : [mask] "a" (mask) \ + : "cc", "memory"); \ + reg = r2; \ + *cc = (pm >> 28) & 3; \ + \ + return reg; \ + } + +#define DEFINE_EXRL_TEST(insn, exp_reg, exp_mem, exp_cc) \ + static long test_exrl_ ## insn(long reg, long *cc) \ + { \ + register long r2 asm("r2"); \ + char mask = 0x20; /* make target use %r2 */ \ + long pm; \ + \ + r2 = reg; \ + asm("cr %%r0,%%r0\n" /* initial cc */ \ + "exrl %[mask],0f\n" \ + "jg 1f\n" \ + "0: " #insn " %%r0," MEM_ASM "\n" \ + "1: ipm %[pm]\n" \ + : [r2] "+r" (r2), [pm] "=r" (pm) \ + : [mask] "a" (mask) \ + : "cc", "memory"); \ + reg = r2; \ + *cc = (pm >> 28) & 3; \ + \ + return reg; \ + } + +FOR_EACH_INSN(DEFINE_EX_TEST) +FOR_EACH_INSN(DEFINE_EXRL_TEST) + +/* Test definitions. */ +#define REGISTER_EX_EXRL_TEST(ex_insn, insn, _exp_reg, _exp_mem, _exp_cc) \ + { \ + .name = #ex_insn " " #insn, \ + .func = test_ ## ex_insn ## _ ## insn, \ + .exp_reg = (_exp_reg), \ + .exp_mem = (_exp_mem), \ + .exp_cc = (_exp_cc), \ + }, + +#define REGISTER_EX_TEST(insn, exp_reg, exp_mem, exp_cc) \ + REGISTER_EX_EXRL_TEST(ex, insn, exp_reg, exp_mem, exp_cc) + +#define REGISTER_EXRL_TEST(insn, exp_reg, exp_mem, exp_cc) \ + REGISTER_EX_EXRL_TEST(exrl, insn, exp_reg, exp_mem, exp_cc) + +static const struct test tests[] = { + FOR_EACH_INSN(REGISTER_EX_TEST) + FOR_EACH_INSN(REGISTER_EXRL_TEST) +}; + +/* Loop over all tests and run them. */ +int main(void) +{ + const struct test *test; + int ret = EXIT_SUCCESS; + long reg, cc; + size_t i; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + test = &tests[i]; + mem[MEM_IDX] = MEM; + cc = -1; + reg = test->func(REG, &cc); +#define ASSERT_EQ(expected, actual) do { \ + if (expected != actual) { \ + fprintf(stderr, "%s: " #expected " (0x%lx) != " #actual " (0x%lx)\n", \ + test->name, expected, actual); \ + ret = EXIT_FAILURE; \ + } \ +} while (0) + ASSERT_EQ(test->exp_reg, reg); + ASSERT_EQ(test->exp_mem, mem[MEM_IDX]); + ASSERT_EQ(test->exp_cc, cc); +#undef ASSERT_EQ + } + + return ret; +} diff --git a/tests/tcg/s390x/exrl-ssm-early.S b/tests/tcg/s390x/exrl-ssm-early.S new file mode 100644 index 0000000000..68fbd87b3a --- /dev/null +++ b/tests/tcg/s390x/exrl-ssm-early.S @@ -0,0 +1,43 @@ +/* + * Test early exception recognition using EXRL + SSM. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .org 0x8d +ilc: + .org 0x8e +program_interruption_code: + .org 0x150 +program_old_psw: + .org 0x1D0 /* program new PSW */ + .quad 0,pgm + .org 0x200 /* lowcore padding */ + + .globl _start +_start: + exrl %r0,ssm +expected_pswa: + j failure +ssm: + ssm ssm_op + +pgm: + chhsi program_interruption_code,0x6 /* specification exception? */ + jne failure + cli ilc,6 /* ilc for EXRL? */ + jne failure + clc program_old_psw(16),expected_old_psw /* correct old PSW? */ + jne failure + lpswe success_psw +failure: + lpswe failure_psw + +ssm_op: + .byte 0x08 /* bit 4 set */ + .align 8 +expected_old_psw: + .quad 0x0800000180000000,expected_pswa /* bit 2 set */ +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/gdbstub/test-signals-s390x.py b/tests/tcg/s390x/gdbstub/test-signals-s390x.py index 80a284b475..b6b7b39fc4 100644 --- a/tests/tcg/s390x/gdbstub/test-signals-s390x.py +++ b/tests/tcg/s390x/gdbstub/test-signals-s390x.py @@ -7,19 +7,7 @@ from __future__ import print_function # import gdb -import sys - -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 +from test_gdbstub import main, report def run_test(): @@ -42,35 +30,7 @@ def run_test(): gdb.Breakpoint("_exit") gdb.execute("c") status = int(gdb.parse_and_eval("$r2")) - report(status == 0, "status == 0"); + report(status == 0, "status == 0") -# -# This runs as the script it sourced (via -x, via run-test.py) -# -try: - inferior = gdb.selected_inferior() - arch = inferior.architecture() - print("ATTACHED: %s" % arch.name()) -except (gdb.error, AttributeError): - print("SKIPPING (not connected)", file=sys.stderr) - exit(0) - -if gdb.parse_and_eval("$pc") == 0: - print("SKIP: PC not set") - 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 (gdb.error): - print("GDB Exception: %s" % (sys.exc_info()[0])) - failcount += 1 - pass - -print("All tests complete: %d failures" % failcount) -exit(failcount) +main(run_test) diff --git a/tests/tcg/s390x/gdbstub/test-svc.py b/tests/tcg/s390x/gdbstub/test-svc.py new file mode 100644 index 0000000000..17210b4e02 --- /dev/null +++ b/tests/tcg/s390x/gdbstub/test-svc.py @@ -0,0 +1,25 @@ +"""Test single-stepping SVC. + +This runs as a sourced script (via -x, via run-test.py).""" +from __future__ import print_function +import gdb +from test_gdbstub import main, report + + +def run_test(): + """Run through the tests one by one""" + report("lghi\t" in gdb.execute("x/i $pc", False, True), "insn #1") + gdb.execute("si") + report("larl\t" in gdb.execute("x/i $pc", False, True), "insn #2") + gdb.execute("si") + report("lgrl\t" in gdb.execute("x/i $pc", False, True), "insn #3") + gdb.execute("si") + report("svc\t" in gdb.execute("x/i $pc", False, True), "insn #4") + gdb.execute("si") + report("xgr\t" in gdb.execute("x/i $pc", False, True), "insn #5") + gdb.execute("si") + report("svc\t" in gdb.execute("x/i $pc", False, True), "insn #6") + gdb.execute("si") + + +main(run_test) diff --git a/tests/tcg/s390x/head64.S b/tests/tcg/s390x/head64.S new file mode 100644 index 0000000000..4fe288388a --- /dev/null +++ b/tests/tcg/s390x/head64.S @@ -0,0 +1,28 @@ +/* + * Startup code for multiarch tests. + * Reuses the pc-bios/s390-ccw implementation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#define main main_pre +#include "../../../pc-bios/s390-ccw/start.S" +#undef main + +.text + +main_pre: + aghi %r15,-160 /* reserve stack for C code */ + brasl %r14,sclp_setup + brasl %r14,main + larl %r1,success_psw /* check main() return code */ + ltgr %r2,%r2 + je 0f + larl %r1,failure_psw +0: + lpswe 0(%r1) + + .align 8 +success_psw: + .quad 0x2000180000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000180000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/hello-s390x-asm.S b/tests/tcg/s390x/hello-s390x-asm.S new file mode 100644 index 0000000000..4dbda12d35 --- /dev/null +++ b/tests/tcg/s390x/hello-s390x-asm.S @@ -0,0 +1,22 @@ +/* + * Hello, World! in assembly. + */ + +.globl _start +_start: + +/* puts("Hello, World!"); */ +lghi %r2,1 +larl %r3,foo +lgrl %r4,foo_len +svc 4 + +/* exit(0); */ +xgr %r2,%r2 +svc 1 + +.align 2 +foo: .asciz "Hello, World!\n" +foo_end: +.align 8 +foo_len: .quad foo_end-foo diff --git a/tests/tcg/s390x/icm.S b/tests/tcg/s390x/icm.S new file mode 100644 index 0000000000..d24d1f52fb --- /dev/null +++ b/tests/tcg/s390x/icm.S @@ -0,0 +1,32 @@ + .org 0x8e +program_interruption_code: + .org 0x1d0 /* program new PSW */ + .quad 0,pgm + .org 0x200 /* lowcore padding */ + .globl _start +_start: + lgrl %r0,op1 + icm %r0,10,op2 + cg %r0,exp + jne failure + lgrl %r1,bad_addr + icm %r0,0,0(%r1) +failure: + lpswe failure_psw +pgm: + chhsi program_interruption_code,5 /* addressing exception? */ + jne failure + lpswe success_psw + .align 8 +op1: + .quad 0x1234567887654321 +op2: + .quad 0x0011223344556677 +exp: + .quad 0x1234567800651121 +bad_addr: + .quad 0xffffffff00000000 +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/laalg.c b/tests/tcg/s390x/laalg.c new file mode 100644 index 0000000000..797d168bb1 --- /dev/null +++ b/tests/tcg/s390x/laalg.c @@ -0,0 +1,27 @@ +/* + * Test the LAALG instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +int main(void) +{ + unsigned long cc = 0, op1, op2 = 40, op3 = 2; + + asm("slgfi %[cc],1\n" /* Set cc_src = -1. */ + "laalg %[op1],%[op3],%[op2]\n" + "ipm %[cc]" + : [cc] "+r" (cc) + , [op1] "=r" (op1) + , [op2] "+T" (op2) + : [op3] "r" (op3) + : "cc"); + + assert(cc == 0xffffffff10ffffff); + assert(op1 == 40); + assert(op2 == 42); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/lae.c b/tests/tcg/s390x/lae.c new file mode 100644 index 0000000000..59712b5e37 --- /dev/null +++ b/tests/tcg/s390x/lae.c @@ -0,0 +1,31 @@ +/* + * Test the LOAD ADDRESS EXTENDED instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +int main(void) +{ + unsigned long long ar = -1, b2 = 100000, r, x2 = 500; + /* + * Hardcode the register number, since clang does not allow using %rN in + * place of %aN. + */ + register unsigned long long r2 __asm__("2"); + int tmp; + + asm("ear %[tmp],%%a2\n" + "lae %%r2,42(%[x2],%[b2])\n" + "ear %[ar],%%a2\n" + "sar %%a2,%[tmp]" + : [tmp] "=&r" (tmp), "=&r" (r2), [ar] "+r" (ar) + : [b2] "r" (b2), [x2] "r" (x2) + : "memory"); + r = r2; + assert(ar == 0xffffffff00000000ULL); + assert(r == 100542); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/larl.c b/tests/tcg/s390x/larl.c new file mode 100644 index 0000000000..7c95f89be7 --- /dev/null +++ b/tests/tcg/s390x/larl.c @@ -0,0 +1,21 @@ +/* + * Test the LARL instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include + +int main(void) +{ + long algfi = (long)main; + long larl; + + /* + * The compiler may emit larl for the C addition, so compute the expected + * value using algfi. + */ + asm("algfi %[r],0xd0000000" : [r] "+r" (algfi) : : "cc"); + asm("larl %[r],main+0xd0000000" : [r] "=r" (larl)); + + return algfi == larl ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/tests/tcg/s390x/lcbb.c b/tests/tcg/s390x/lcbb.c new file mode 100644 index 0000000000..8d368e0998 --- /dev/null +++ b/tests/tcg/s390x/lcbb.c @@ -0,0 +1,51 @@ +/* + * Test the LCBB instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +static inline __attribute__((__always_inline__)) void +lcbb(long *r1, void *dxb2, int m3, int *cc) +{ + asm("lcbb %[r1],%[dxb2],%[m3]\n" + "ipm %[cc]" + : [r1] "+r" (*r1), [cc] "=r" (*cc) + : [dxb2] "R" (*(char *)dxb2), [m3] "i" (m3) + : "cc"); + *cc = (*cc >> 28) & 3; +} + +static char buf[0x1000] __attribute__((aligned(0x1000))); + +static inline __attribute__((__always_inline__)) void +test_lcbb(void *p, int m3, int exp_r1, int exp_cc) +{ + long r1 = 0xfedcba9876543210; + int cc; + + lcbb(&r1, p, m3, &cc); + assert(r1 == (0xfedcba9800000000 | exp_r1)); + assert(cc == exp_cc); +} + +int main(void) +{ + test_lcbb(&buf[0], 0, 16, 0); + test_lcbb(&buf[63], 0, 1, 3); + test_lcbb(&buf[0], 1, 16, 0); + test_lcbb(&buf[127], 1, 1, 3); + test_lcbb(&buf[0], 2, 16, 0); + test_lcbb(&buf[255], 2, 1, 3); + test_lcbb(&buf[0], 3, 16, 0); + test_lcbb(&buf[511], 3, 1, 3); + test_lcbb(&buf[0], 4, 16, 0); + test_lcbb(&buf[1023], 4, 1, 3); + test_lcbb(&buf[0], 5, 16, 0); + test_lcbb(&buf[2047], 5, 1, 3); + test_lcbb(&buf[0], 6, 16, 0); + test_lcbb(&buf[4095], 6, 1, 3); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/lgrl-unaligned.S b/tests/tcg/s390x/lgrl-unaligned.S new file mode 100644 index 0000000000..ef8d51d47c --- /dev/null +++ b/tests/tcg/s390x/lgrl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test LGRL from a non-doubleword aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + lgrl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .long 0 +unaligned: + .quad 0 diff --git a/tests/tcg/s390x/llgfrl-unaligned.S b/tests/tcg/s390x/llgfrl-unaligned.S new file mode 100644 index 0000000000..c9b4eeaecf --- /dev/null +++ b/tests/tcg/s390x/llgfrl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test LLGFRL from a non-word aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + llgfrl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .short 0 +unaligned: + .long 0 diff --git a/tests/tcg/s390x/locfhr.c b/tests/tcg/s390x/locfhr.c new file mode 100644 index 0000000000..ab9ff6e449 --- /dev/null +++ b/tests/tcg/s390x/locfhr.c @@ -0,0 +1,29 @@ +/* + * Test the LOCFHR instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +static inline __attribute__((__always_inline__)) long +locfhr(long r1, long r2, int m3, int cc) +{ + cc <<= 28; + asm("spm %[cc]\n" + "locfhr %[r1],%[r2],%[m3]\n" + : [r1] "+r" (r1) + : [cc] "r" (cc), [r2] "r" (r2), [m3] "i" (m3) + : "cc"); + return r1; +} + +int main(void) +{ + assert(locfhr(0x1111111122222222, 0x3333333344444444, 8, 0) == + 0x3333333322222222); + assert(locfhr(0x5555555566666666, 0x7777777788888888, 11, 1) == + 0x5555555566666666); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/long-double.c b/tests/tcg/s390x/long-double.c new file mode 100644 index 0000000000..757a6262fd --- /dev/null +++ b/tests/tcg/s390x/long-double.c @@ -0,0 +1,24 @@ +/* + * Perform some basic arithmetic with long double, as a sanity check. + * With small integral numbers, we can cross-check with integers. + */ + +#include + +int main() +{ + int i, j; + + for (i = 1; i < 5; i++) { + for (j = 1; j < 5; j++) { + long double la = (long double)i + j; + long double lm = (long double)i * j; + long double ls = (long double)i - j; + + assert(la == i + j); + assert(lm == i * j); + assert(ls == i - j); + } + } + return 0; +} diff --git a/tests/tcg/s390x/lpsw.S b/tests/tcg/s390x/lpsw.S new file mode 100644 index 0000000000..b37dec59b7 --- /dev/null +++ b/tests/tcg/s390x/lpsw.S @@ -0,0 +1,36 @@ +/* + * Test the LPSW instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .org 0x140 +svc_old_psw: + .org 0x1c0 /* supervisor call new PSW */ + .quad 0x80000000,svc /* 31-bit mode */ + .org 0x200 /* lowcore padding */ + + .globl _start +_start: + lpsw short_psw +lpsw_target: + svc 0 +expected_pswa: + j failure + +svc: + clc svc_old_psw(16),expected_psw /* correct full PSW? */ + jne failure + lpswe success_psw +failure: + lpswe failure_psw + + .align 8 +short_psw: + .long 0x90001,0x80000000+lpsw_target /* problem state, + 64-bit mode */ +expected_psw: + .quad 0x1000180000000,expected_pswa /* corresponds to short_psw */ +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/lpswe-early.S b/tests/tcg/s390x/lpswe-early.S new file mode 100644 index 0000000000..90a7f213df --- /dev/null +++ b/tests/tcg/s390x/lpswe-early.S @@ -0,0 +1,38 @@ +/* + * Test early exception recognition using LPSWE. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .org 0x8d +ilc: + .org 0x8e +program_interruption_code: + .org 0x150 +program_old_psw: + .org 0x1D0 /* program new PSW */ + .quad 0,pgm + .org 0x200 /* lowcore padding */ + + .globl _start +_start: + lpswe bad_psw + j failure + +pgm: + chhsi program_interruption_code,0x6 /* specification exception? */ + jne failure + cli ilc,0 /* ilc zero? */ + jne failure + clc program_old_psw(16),bad_psw /* correct old PSW? */ + jne failure + lpswe success_psw +failure: + lpswe failure_psw + + .align 8 +bad_psw: + .quad 0x8000000000000000,0xfedcba9876543210 /* bit 0 set */ +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/lpswe-unaligned.S b/tests/tcg/s390x/lpswe-unaligned.S new file mode 100644 index 0000000000..989f249a6a --- /dev/null +++ b/tests/tcg/s390x/lpswe-unaligned.S @@ -0,0 +1,18 @@ +/* + * Test LPSWE from a non-doubleword aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + larl %r1,unaligned +fail: + lpswe 0(%r1) + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,fail + .long 0 +unaligned: + .quad 0 diff --git a/tests/tcg/s390x/lra.S b/tests/tcg/s390x/lra.S new file mode 100644 index 0000000000..79ab86f36b --- /dev/null +++ b/tests/tcg/s390x/lra.S @@ -0,0 +1,19 @@ + .org 0x200 /* lowcore padding */ + .globl _start +_start: + lgrl %r1,initial_r1 + lra %r1,0(%r1) + cgrl %r1,expected_r1 + jne 1f + lpswe success_psw +1: + lpswe failure_psw + .align 8 +initial_r1: + .quad 0x8765432112345678 +expected_r1: + .quad 0x8765432180000038 /* ASCE type exception */ +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/lrl-unaligned.S b/tests/tcg/s390x/lrl-unaligned.S new file mode 100644 index 0000000000..11eb07f93a --- /dev/null +++ b/tests/tcg/s390x/lrl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test LRL from a non-word aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + lrl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .short 0 +unaligned: + .long 0 diff --git a/tests/tcg/s390x/mc.S b/tests/tcg/s390x/mc.S new file mode 100644 index 0000000000..e7466bb4b5 --- /dev/null +++ b/tests/tcg/s390x/mc.S @@ -0,0 +1,56 @@ + .org 0x8d +ilc: + .org 0x8e +program_interruption_code: + .org 0x94 +monitor_class: + .org 0xb0 +monitor_code: + .org 0x150 +program_old_psw: + .org 0x1d0 /* program new PSW */ + .quad 0x180000000,pgm /* 64-bit mode */ + .org 0x200 /* lowcore padding */ + .globl _start +_start: + stctg %c8,%c8,c8 /* enable only monitor class 1 */ + mvhhi c8+6,0x4000 + lctlg %c8,%c8,c8 +mc_nop: + mc 123,0 +mc_monitor_event: + mc 321,1 + j failure +mc_specification: + mc 333,16 + j failure +pgm: + lgrl %r0,program_old_psw+8 /* ilc adjustment */ + llgc %r1,ilc + sgr %r0,%r1 + larl %r1,mc_monitor_event /* dispatch based on old PSW */ + cgrje %r0,%r1,pgm_monitor_event + larl %r1,mc_specification + cgrje %r0,%r1,pgm_specification + j failure +pgm_monitor_event: + chhsi program_interruption_code,0x40 /* monitor event? */ + jne failure + chhsi monitor_class,1 /* class from mc_monitor_event? */ + jne failure + cghsi monitor_code,321 /* code from mc_monitor_event? */ + jne failure + j mc_specification /* next test */ +pgm_specification: + chhsi program_interruption_code,6 /* specification exception? */ + jne failure + lpswe success_psw +failure: + lpswe failure_psw + .align 8 +c8: + .quad 0 +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/mdeb.c b/tests/tcg/s390x/mdeb.c new file mode 100644 index 0000000000..4897d28069 --- /dev/null +++ b/tests/tcg/s390x/mdeb.c @@ -0,0 +1,30 @@ +/* + * Test the MDEB and MDEBR instructions. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +int main(void) +{ + union { + float f[2]; + double d; + } a; + float b; + + a.f[0] = 1.2345; + a.f[1] = 999; + b = 6.789; + asm("mdeb %[a],%[b]" : [a] "+f" (a.d) : [b] "R" (b)); + assert(a.d > 8.38 && a.d < 8.39); + + a.f[0] = 1.2345; + a.f[1] = 999; + b = 6.789; + asm("mdebr %[a],%[b]" : [a] "+f" (a.d) : [b] "f" (b)); + assert(a.d > 8.38 && a.d < 8.39); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/mie3-mvcrl.c b/tests/tcg/s390x/mie3-mvcrl.c index 93c7b0a290..6d3d049f2c 100644 --- a/tests/tcg/s390x/mie3-mvcrl.c +++ b/tests/tcg/s390x/mie3-mvcrl.c @@ -1,29 +1,55 @@ +#include #include +#include #include - -static inline void mvcrl_8(const char *dst, const char *src) +static void mvcrl(const char *dst, const char *src, size_t len) { + register long r0 asm("r0") = len; + asm volatile ( - "llill %%r0, 8\n" ".insn sse, 0xE50A00000000, 0(%[dst]), 0(%[src])" - : : [dst] "d" (dst), [src] "d" (src) - : "r0", "memory"); + : : [dst] "d" (dst), [src] "d" (src), "r" (r0) + : "memory"); } - -int main(int argc, char *argv[]) +static bool test(void) { const char *alpha = "abcdefghijklmnop"; /* array missing 'i' */ - char tstr[17] = "abcdefghjklmnop\0" ; + char tstr[17] = "abcdefghjklmnop\0"; /* mvcrl reference use: 'open a hole in an array' */ - mvcrl_8(tstr + 9, tstr + 8); + mvcrl(tstr + 9, tstr + 8, 8); /* place missing 'i' */ tstr[8] = 'i'; - return strncmp(alpha, tstr, 16ul); + return strncmp(alpha, tstr, 16ul) == 0; +} + +static bool test_bad_r0(void) +{ + char src[256] = { 0 }; + + /* + * PoP says: Bits 32-55 of general register 0 should contain zeros; + * otherwise, the program may not operate compatibly in the future. + * + * Try it anyway in order to check whether this would crash QEMU itself. + */ + mvcrl(src, src, (size_t)-1); + + return true; +} + +int main(void) +{ + bool ok = true; + + ok &= test(); + ok &= test_bad_r0(); + + return ok ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/tcg/s390x/mvc.c b/tests/tcg/s390x/mvc.c index 7ae4c44550..b572aa3ced 100644 --- a/tests/tcg/s390x/mvc.c +++ b/tests/tcg/s390x/mvc.c @@ -85,7 +85,7 @@ int main(void) } } - /* test if MVC works now correctly accross page boundaries */ + /* test if MVC works now correctly across page boundaries */ mvc_256(dst + 4096 - 128, src + 4096 - 128); for (i = 0; i < ALLOC_SIZE; i++) { if (src[i] != 0xff) { diff --git a/tests/tcg/s390x/mxdb.c b/tests/tcg/s390x/mxdb.c new file mode 100644 index 0000000000..ae922559d3 --- /dev/null +++ b/tests/tcg/s390x/mxdb.c @@ -0,0 +1,30 @@ +/* + * Test the MXDB and MXDBR instructions. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +int main(void) +{ + union { + double d[2]; + long double ld; + } a; + double b; + + a.d[0] = 1.2345; + a.d[1] = 999; + b = 6.789; + asm("mxdb %[a],%[b]" : [a] "+f" (a.ld) : [b] "R" (b)); + assert(a.ld > 8.38 && a.ld < 8.39); + + a.d[0] = 1.2345; + a.d[1] = 999; + b = 6.789; + asm("mxdbr %[a],%[b]" : [a] "+f" (a.ld) : [b] "f" (b)); + assert(a.ld > 8.38 && a.ld < 8.39); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/noexec.c b/tests/tcg/s390x/noexec.c new file mode 100644 index 0000000000..15d007d07f --- /dev/null +++ b/tests/tcg/s390x/noexec.c @@ -0,0 +1,106 @@ +#include "../multiarch/noexec.c.inc" + +static void *arch_mcontext_pc(const mcontext_t *ctx) +{ + return (void *)ctx->psw.addr; +} + +static int arch_mcontext_arg(const mcontext_t *ctx) +{ + return ctx->gregs[2]; +} + +static void arch_flush(void *p, int len) +{ +} + +extern char noexec_1[]; +extern char noexec_2[]; +extern char noexec_end[]; + +asm("noexec_1:\n" + " lgfi %r2,1\n" /* %r2 is 0 on entry, set 1. */ + "noexec_2:\n" + " lgfi %r2,2\n" /* %r2 is 0/1; set 2. */ + " br %r14\n" /* return */ + "noexec_end:"); + +extern char exrl_1[]; +extern char exrl_2[]; +extern char exrl_end[]; + +asm("exrl_1:\n" + " exrl %r0, exrl_2\n" + " br %r14\n" + "exrl_2:\n" + " lgfi %r2,2\n" + "exrl_end:"); + +int main(void) +{ + struct noexec_test noexec_tests[] = { + { + .name = "fallthrough", + .test_code = noexec_1, + .test_len = noexec_end - noexec_1, + .page_ofs = noexec_1 - noexec_2, + .entry_ofs = noexec_1 - noexec_2, + .expected_si_ofs = 0, + .expected_pc_ofs = 0, + .expected_arg = 1, + }, + { + .name = "jump", + .test_code = noexec_1, + .test_len = noexec_end - noexec_1, + .page_ofs = noexec_1 - noexec_2, + .entry_ofs = 0, + .expected_si_ofs = 0, + .expected_pc_ofs = 0, + .expected_arg = 0, + }, + { + .name = "exrl", + .test_code = exrl_1, + .test_len = exrl_end - exrl_1, + .page_ofs = exrl_1 - exrl_2, + .entry_ofs = exrl_1 - exrl_2, + .expected_si_ofs = 0, + .expected_pc_ofs = exrl_1 - exrl_2, + .expected_arg = 0, + }, + { + .name = "fallthrough [cross]", + .test_code = noexec_1, + .test_len = noexec_end - noexec_1, + .page_ofs = noexec_1 - noexec_2 - 2, + .entry_ofs = noexec_1 - noexec_2 - 2, + .expected_si_ofs = 0, + .expected_pc_ofs = -2, + .expected_arg = 1, + }, + { + .name = "jump [cross]", + .test_code = noexec_1, + .test_len = noexec_end - noexec_1, + .page_ofs = noexec_1 - noexec_2 - 2, + .entry_ofs = -2, + .expected_si_ofs = 0, + .expected_pc_ofs = -2, + .expected_arg = 0, + }, + { + .name = "exrl [cross]", + .test_code = exrl_1, + .test_len = exrl_end - exrl_1, + .page_ofs = exrl_1 - exrl_2 - 2, + .entry_ofs = exrl_1 - exrl_2 - 2, + .expected_si_ofs = 0, + .expected_pc_ofs = exrl_1 - exrl_2 - 2, + .expected_arg = 0, + }, + }; + + return test_noexec(noexec_tests, + sizeof(noexec_tests) / sizeof(noexec_tests[0])); +} diff --git a/tests/tcg/s390x/pgm-specification-softmmu.S b/tests/tcg/s390x/pgm-specification-softmmu.S new file mode 100644 index 0000000000..86c340aeef --- /dev/null +++ b/tests/tcg/s390x/pgm-specification-softmmu.S @@ -0,0 +1,40 @@ +/* + * Common system code for specification exception testing. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .section .head + .org 0x8d +ilc: + .org 0x8e +program_interruption_code: + .org 0x150 +program_old_psw: + .org 0x1D0 /* program new PSW */ + .quad 0x180000000,pgm /* 64-bit mode */ + .org 0x200 /* lowcore padding */ + + .globl _start +_start: + lpswe test_psw + +pgm: + chhsi program_interruption_code,0x6 /* PGM_SPECIFICATION? */ + jne failure + lg %r0,expected_old_psw+8 /* ilc adjustment */ + llgc %r1,ilc + agr %r0,%r1 + stg %r0,expected_old_psw+8 + clc expected_old_psw(16),program_old_psw /* correct location? */ + jne failure + lpswe success_psw +failure: + lpswe failure_psw + + .align 8 +test_psw: + .quad 0x180000000,test /* 64-bit mode */ +success_psw: + .quad 0x2000180000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000180000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/pgm-specification-user.c b/tests/tcg/s390x/pgm-specification-user.c new file mode 100644 index 0000000000..9ee6907b7c --- /dev/null +++ b/tests/tcg/s390x/pgm-specification-user.c @@ -0,0 +1,37 @@ +/* + * Common user code for specification exception testing. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include + +extern void test(void); +extern long expected_old_psw[2]; + +static void handle_sigill(int sig, siginfo_t *info, void *ucontext) +{ + if ((long)info->si_addr != expected_old_psw[1]) { + _exit(EXIT_FAILURE); + } + _exit(EXIT_SUCCESS); +} + +int main(void) +{ + struct sigaction act; + int err; + + memset(&act, 0, sizeof(act)); + act.sa_sigaction = handle_sigill; + act.sa_flags = SA_SIGINFO; + err = sigaction(SIGILL, &act, NULL); + assert(err == 0); + + test(); + + return EXIT_FAILURE; +} diff --git a/tests/tcg/s390x/pgm-specification.mak b/tests/tcg/s390x/pgm-specification.mak new file mode 100644 index 0000000000..73dc47af0d --- /dev/null +++ b/tests/tcg/s390x/pgm-specification.mak @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# List of specification exception tests. +# Shared between the system and the user makefiles. +PGM_SPECIFICATION_TESTS = \ + br-odd \ + cgrl-unaligned \ + clrl-unaligned \ + crl-unaligned \ + ex-odd \ + lgrl-unaligned \ + llgfrl-unaligned \ + lpswe-unaligned \ + lrl-unaligned \ + stgrl-unaligned \ + strl-unaligned diff --git a/tests/tcg/s390x/precise-smc-softmmu.S b/tests/tcg/s390x/precise-smc-softmmu.S new file mode 100644 index 0000000000..f7fa57d899 --- /dev/null +++ b/tests/tcg/s390x/precise-smc-softmmu.S @@ -0,0 +1,63 @@ +/* + * Test s390x-softmmu precise self-modifying code handling. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .org 0x8e +program_interruption_code: + .org 0x150 +program_old_psw: + .org 0x1D0 /* program new PSW */ + .quad 0x180000000,pgm /* 64-bit mode */ + .org 0x200 /* lowcore padding */ + .globl _start +_start: + lctlg %c0,%c0,c0 + lghi %r0,15 + + /* Test 1: replace sgr with agr. */ + lghi %r1,21 + vl %v0,patch1 + jg 1f /* start a new TB */ +0: + .org . + 6 /* pad patched code to 16 bytes */ +1: + vstl %v0,%r0,0b /* start writing before TB */ + sgr %r1,%r1 /* this becomes `agr %r1,%r1` */ + cgijne %r1,42,failure + + /* Test 2: replace agr with division by zero. */ + vl %v0,patch2 + jg 1f /* start a new TB */ +0: + .org . + 6 /* pad patched code to 16 bytes */ +1: + vstl %v0,%r0,0b /* start writing before TB */ + sgr %r1,%r1 /* this becomes `d %r0,zero` */ +failure: + lpswe failure_psw + +pgm: + chhsi program_interruption_code,0x9 /* divide exception? */ + jne failure + clc program_old_psw(16),expected_old_psw2 /* correct old PSW? */ + jne failure + lpswe success_psw + +patch1: + .fill 12 /* replaces padding and stpq */ + agr %r1,%r1 /* replaces sgr */ +patch2: + .fill 12 /* replaces padding and stpq */ + d %r0,zero /* replaces sgr */ +zero: + .long 0 +expected_old_psw2: + .quad 0x200180000000,failure /* cc is from addition */ + .align 8 +c0: + .quad 0x60000 /* AFP, VX */ +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/precise-smc-user.c b/tests/tcg/s390x/precise-smc-user.c new file mode 100644 index 0000000000..33a5270865 --- /dev/null +++ b/tests/tcg/s390x/precise-smc-user.c @@ -0,0 +1,39 @@ +/* + * Test s390x-linux-user precise self-modifying code handling. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include + +extern __uint128_t __attribute__((__aligned__(1))) smc; +extern __uint128_t __attribute__((__aligned__(1))) patch; + +int main(void) +{ + char *aligned_smc = (char *)((uintptr_t)&smc & ~0xFFFULL); + char *smc_end = (char *)&smc + sizeof(smc); + uint64_t value = 21; + int err; + + err = mprotect(aligned_smc, smc_end - aligned_smc, + PROT_READ | PROT_WRITE | PROT_EXEC); + assert(err == 0); + + asm("jg 0f\n" /* start a new TB */ + "patch: .byte 0,0,0,0,0,0\n" /* replaces padding */ + ".byte 0,0,0,0,0,0\n" /* replaces vstl */ + "agr %[value],%[value]\n" /* replaces sgr */ + "smc: .org . + 6\n" /* pad patched code to 16 bytes */ + "0: vstl %[patch],%[idx],%[smc]\n" /* start writing before TB */ + "sgr %[value],%[value]" /* this becomes `agr %r0,%r0` */ + : [smc] "=R" (smc) + , [value] "+r" (value) + : [patch] "v" (patch) + , [idx] "r" (sizeof(patch) - 1) + : "cc"); + + return value == 42 ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/tests/tcg/s390x/rxsbg.c b/tests/tcg/s390x/rxsbg.c new file mode 100644 index 0000000000..4b155db304 --- /dev/null +++ b/tests/tcg/s390x/rxsbg.c @@ -0,0 +1,46 @@ +/* + * Test the RXSBG instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +static inline __attribute__((__always_inline__)) void +rxsbg(unsigned long *r1, unsigned long r2, int i3, int i4, int i5, int *cc) +{ + asm("rxsbg %[r1],%[r2],%[i3],%[i4],%[i5]\n" + "ipm %[cc]" + : [r1] "+r" (*r1), [cc] "=r" (*cc) + : [r2] "r" (r2) , [i3] "i" (i3) , [i4] "i" (i4) , [i5] "i" (i5) + : "cc"); + *cc = (*cc >> 28) & 3; +} + +void test_cc0(void) +{ + unsigned long r1 = 6; + int cc; + + rxsbg(&r1, 3, 61 | 0x80, 62, 1, &cc); + assert(r1 == 6); + assert(cc == 0); +} + +void test_cc1(void) +{ + unsigned long r1 = 2; + int cc; + + rxsbg(&r1, 3, 61 | 0x80, 62, 1, &cc); + assert(r1 == 2); + assert(cc == 1); +} + +int main(void) +{ + test_cc0(); + test_cc1(); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/sam.S b/tests/tcg/s390x/sam.S new file mode 100644 index 0000000000..4cab2dd200 --- /dev/null +++ b/tests/tcg/s390x/sam.S @@ -0,0 +1,67 @@ +/* DAT on, home-space mode, 64-bit mode */ +#define DAT_PSWM 0x400c00180000000 +#define VIRTUAL_BASE 0x123456789abcd000 + + .org 0x8e +program_interruption_code: + .org 0x150 +program_old_psw: + .org 0x1d0 /* program new PSW */ + .quad 0,pgm_handler + .org 0x200 /* lowcore padding */ + + .globl _start +_start: + lctlg %c13,%c13,hasce + lpswe dat_psw +start_dat: + sam24 +sam24_suppressed: + /* sam24 should fail */ +fail: + basr %r12,%r0 + lpswe failure_psw-.(%r12) +pgm_handler: + chhsi program_interruption_code,6 /* specification exception? */ + jne fail + clc suppressed_psw(16),program_old_psw /* correct location? */ + jne fail + lpswe success_psw + + .align 8 +dat_psw: + .quad DAT_PSWM,VIRTUAL_BASE+start_dat +suppressed_psw: + .quad DAT_PSWM,VIRTUAL_BASE+sam24_suppressed +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ +hasce: + /* DT = 0b11 (region-first-table), TL = 3 (2k entries) */ + .quad region_first_table + (3 << 2) + 3 + .align 0x1000 +region_first_table: + .org region_first_table + ((VIRTUAL_BASE >> 53) & 0x7ff) * 8 + /* TT = 0b11 (region-first-table), TL = 3 (2k entries) */ + .quad region_second_table + (3 << 2) + 3 + .org region_first_table + 0x800 * 8 +region_second_table: + .org region_second_table + ((VIRTUAL_BASE >> 42) & 0x7ff) * 8 + /* TT = 0b10 (region-second-table), TL = 3 (2k entries) */ + .quad region_third_table + (2 << 2) + 3 + .org region_second_table + 0x800 * 8 +region_third_table: + .org region_third_table + ((VIRTUAL_BASE >> 31) & 0x7ff) * 8 + /* TT = 0b01 (region-third-table), TL = 3 (2k entries) */ + .quad segment_table + (1 << 2) + 3 + .org region_third_table + 0x800 * 8 +segment_table: + .org segment_table + ((VIRTUAL_BASE >> 20) & 0x7ff) * 8 + /* TT = 0b00 (segment-table) */ + .quad page_table + .org segment_table + 0x800 * 8 +page_table: + .org page_table + ((VIRTUAL_BASE >> 12) & 0xff) * 8 + .quad 0 + .org page_table + 0x100 * 8 diff --git a/tests/tcg/s390x/softmmu.ld b/tests/tcg/s390x/softmmu.ld new file mode 100644 index 0000000000..c7a8864407 --- /dev/null +++ b/tests/tcg/s390x/softmmu.ld @@ -0,0 +1,20 @@ +/* + * Linker script for the system test kernels. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +ENTRY(_start) + +SECTIONS { + . = 0; + + .text : { + *(.head) + *(.text) + } + + /DISCARD/ : { + *(*) + } +} diff --git a/tests/tcg/s390x/ssm-early.S b/tests/tcg/s390x/ssm-early.S new file mode 100644 index 0000000000..6dfe40c597 --- /dev/null +++ b/tests/tcg/s390x/ssm-early.S @@ -0,0 +1,41 @@ +/* + * Test early exception recognition using SSM. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .org 0x8d +ilc: + .org 0x8e +program_interruption_code: + .org 0x150 +program_old_psw: + .org 0x1D0 /* program new PSW */ + .quad 0,pgm + .org 0x200 /* lowcore padding */ + + .globl _start +_start: + ssm ssm_op +expected_pswa: + j failure + +pgm: + chhsi program_interruption_code,0x6 /* specification exception? */ + jne failure + cli ilc,4 /* ilc for SSM? */ + jne failure + clc program_old_psw(16),expected_old_psw /* correct old PSW? */ + jne failure + lpswe success_psw +failure: + lpswe failure_psw + +ssm_op: + .byte 0x20 /* bit 2 set */ + .align 8 +expected_old_psw: + .quad 0x2000000180000000,expected_pswa /* bit 2 set */ +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/stgrl-unaligned.S b/tests/tcg/s390x/stgrl-unaligned.S new file mode 100644 index 0000000000..32df37780a --- /dev/null +++ b/tests/tcg/s390x/stgrl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test STGRL to a non-doubleword aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + stgrl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .long 0 +unaligned: + .quad 0 diff --git a/tests/tcg/s390x/stosm-early.S b/tests/tcg/s390x/stosm-early.S new file mode 100644 index 0000000000..0689924f3a --- /dev/null +++ b/tests/tcg/s390x/stosm-early.S @@ -0,0 +1,41 @@ +/* + * Test early exception recognition using STOSM. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .org 0x8d +ilc: + .org 0x8e +program_interruption_code: + .org 0x150 +program_old_psw: + .org 0x1D0 /* program new PSW */ + .quad 0,pgm + .org 0x200 /* lowcore padding */ + + .globl _start +_start: + stosm ssm_op,0x10 /* bit 3 set */ +expected_pswa: + j failure + +pgm: + chhsi program_interruption_code,0x6 /* specification exception? */ + jne failure + cli ilc,4 /* ilc for STOSM? */ + jne failure + clc program_old_psw(16),expected_old_psw /* correct old PSW? */ + jne failure + lpswe success_psw +failure: + lpswe failure_psw + +ssm_op: + .byte 0 + .align 8 +expected_old_psw: + .quad 0x1000000180000000,expected_pswa /* bit 3 set */ +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/stpq.S b/tests/tcg/s390x/stpq.S new file mode 100644 index 0000000000..687a52eafa --- /dev/null +++ b/tests/tcg/s390x/stpq.S @@ -0,0 +1,20 @@ + .org 0x200 /* lowcore padding */ + .globl _start +_start: + lgrl %r0,value + lgrl %r1,value+8 + stpq %r0,stored_value + clc stored_value(16),value + jne failure + lpswe success_psw +failure: + lpswe failure_psw + .align 16 +value: + .quad 0x1234567887654321, 0x8765432112345678 +stored_value: + .quad 0, 0 +success_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000,0 /* disabled wait */ diff --git a/tests/tcg/s390x/strl-unaligned.S b/tests/tcg/s390x/strl-unaligned.S new file mode 100644 index 0000000000..1d248819f0 --- /dev/null +++ b/tests/tcg/s390x/strl-unaligned.S @@ -0,0 +1,16 @@ +/* + * Test STRL to a non-word aligned address. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + .globl test +test: + strl %r1,unaligned + + .align 8 + .globl expected_old_psw +expected_old_psw: + .quad 0x180000000,test + .short 0 +unaligned: + .long 0 diff --git a/tests/tcg/s390x/ts.c b/tests/tcg/s390x/ts.c new file mode 100644 index 0000000000..441faf30d9 --- /dev/null +++ b/tests/tcg/s390x/ts.c @@ -0,0 +1,35 @@ +/* + * Test the TEST AND SET instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +static int ts(char *p) +{ + int cc; + + asm("ts %[p]\n" + "ipm %[cc]" + : [cc] "=r" (cc) + , [p] "+Q" (*p) + : : "cc"); + + return (cc >> 28) & 3; +} + +int main(void) +{ + char c; + + c = 0x80; + assert(ts(&c) == 1); + assert(c == 0xff); + + c = 0x7f; + assert(ts(&c) == 0); + assert(c == 0xff); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/unaligned-lowcore.S b/tests/tcg/s390x/unaligned-lowcore.S new file mode 100644 index 0000000000..f5da2ae64c --- /dev/null +++ b/tests/tcg/s390x/unaligned-lowcore.S @@ -0,0 +1,19 @@ + .org 0x1D0 /* program new PSW */ + .quad 0x2000000000000,0 /* disabled wait */ + .org 0x200 /* lowcore padding */ + + .globl _start +_start: + lctlg %c0,%c0,_c0 + vst %v0,_unaligned + lpswe quiesce_psw + + .align 8 +quiesce_psw: + .quad 0x2000000000000,0xfff /* see is_special_wait_psw() */ +_c0: + .quad 0x10060000 /* lowcore protection, AFP, VX */ + + .byte 0 +_unaligned: + .octa 0 diff --git a/tests/tcg/s390x/vcksm.c b/tests/tcg/s390x/vcksm.c new file mode 100644 index 0000000000..452daaae6c --- /dev/null +++ b/tests/tcg/s390x/vcksm.c @@ -0,0 +1,31 @@ +/* + * Test the VCKSM instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include "vx.h" + +int main(void) +{ + S390Vector v1; + S390Vector v2 = { + .d[0] = 0xb2261c8140edce49ULL, + .d[1] = 0x387bf5a433af39d1ULL, + }; + S390Vector v3 = { + .d[0] = 0x73b03d2c7f9e654eULL, + .d[1] = 0x23d74e51fb479877ULL, + }; + S390Vector exp = {.d[0] = 0xdedd7f8eULL, .d[1] = 0ULL}; + + asm volatile("vcksm %[v1],%[v2],%[v3]" + : [v1] "=v" (v1.v) + : [v2] "v" (v2.v) + , [v3] "v" (v3.v)); + assert(memcmp(&v1, &exp, sizeof(v1)) == 0); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/vfminmax.c b/tests/tcg/s390x/vfminmax.c new file mode 100644 index 0000000000..22629df160 --- /dev/null +++ b/tests/tcg/s390x/vfminmax.c @@ -0,0 +1,411 @@ +#define _GNU_SOURCE +#include +#include +#include +#include + +/* + * vfmin/vfmax instruction execution. + */ +#define VFMIN 0xEE +#define VFMAX 0xEF + +extern char insn[6]; +asm(".pushsection .rwx,\"awx\",@progbits\n" + ".globl insn\n" + /* e7 89 a0 00 2e ef */ + "insn: vfmaxsb %v24,%v25,%v26,0\n" + ".popsection\n"); + +static void vfminmax(unsigned int op, + unsigned int m4, unsigned int m5, unsigned int m6, + void *v1, const void *v2, const void *v3) +{ + insn[3] = (m6 << 4) | m5; + insn[4] = (m4 << 4) | 0x0e; + insn[5] = op; + + asm("vl %%v25,%[v2]\n" + "vl %%v26,%[v3]\n" + "ex 0,%[insn]\n" + "vst %%v24,%[v1]\n" + : [v1] "=m" (*(char (*)[16])v1) + : [v2] "m" (*(char (*)[16])v2) + , [v3] "m" (*(char (*)[16])v3) + , [insn] "m"(insn) + : "v24", "v25", "v26"); +} + +/* + * Floating-point value classes. + */ +#define N_FORMATS 3 +#define N_SIGNED_CLASSES 8 +static const size_t float_sizes[N_FORMATS] = { + /* M4 == 2: short */ 4, + /* M4 == 3: long */ 8, + /* M4 == 4: extended */ 16, +}; +static const size_t e_bits[N_FORMATS] = { + /* M4 == 2: short */ 8, + /* M4 == 3: long */ 11, + /* M4 == 4: extended */ 15, +}; +static const unsigned char signed_floats[N_FORMATS][N_SIGNED_CLASSES][2][16] = { + /* M4 == 2: short */ + { + /* -inf */ {{0xff, 0x80, 0x00, 0x00}, + {0xff, 0x80, 0x00, 0x00}}, + /* -Fn */ {{0xc2, 0x28, 0x00, 0x00}, + {0xc2, 0x29, 0x00, 0x00}}, + /* -0 */ {{0x80, 0x00, 0x00, 0x00}, + {0x80, 0x00, 0x00, 0x00}}, + /* +0 */ {{0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}}, + /* +Fn */ {{0x42, 0x28, 0x00, 0x00}, + {0x42, 0x2a, 0x00, 0x00}}, + /* +inf */ {{0x7f, 0x80, 0x00, 0x00}, + {0x7f, 0x80, 0x00, 0x00}}, + /* QNaN */ {{0x7f, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xfe}}, + /* SNaN */ {{0x7f, 0xbf, 0xff, 0xff}, + {0x7f, 0xbf, 0xff, 0xfd}}, + }, + + /* M4 == 3: long */ + { + /* -inf */ {{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* -Fn */ {{0xc0, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xc0, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* -0 */ {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* +0 */ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* +Fn */ {{0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x40, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* +inf */ {{0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* QNaN */ {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}, + /* SNaN */ {{0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}}, + }, + + /* M4 == 4: extended */ + { + /* -inf */ {{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* -Fn */ {{0xc0, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xc0, 0x04, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* -0 */ {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* +0 */ {{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}}, + /* +Fn */ {{0x40, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x40, 0x04, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* +inf */ {{0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + /* QNaN */ {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}, + /* SNaN */ {{0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}}, + }, +}; + +/* + * PoP tables as close to the original as possible. + */ +struct signed_test { + int op; + int m6; + const char *m6_desc; + const char *table[N_SIGNED_CLASSES][N_SIGNED_CLASSES]; +} signed_tests[] = { + { + .op = VFMIN, + .m6 = 0, + .m6_desc = "IEEE MinNum", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* -0 */ "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* +0 */ "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* QNaN */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"}, + {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"}, + }, + }, + { + .op = VFMIN, + .m6 = 1, + .m6_desc = "JAVA Math.Min()", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"}, + {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"}, + {/* -0 */ "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"}, + {/* +0 */ "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"}, + {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "T(b)", "Xi: T(b*)"}, + {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"}, + {/* QNaN */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"}, + }, + }, + { + .op = VFMIN, + .m6 = 2, + .m6_desc = "C-style Min Macro", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b)", "Xi: T(b)"}, + {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b)", "Xi: T(b)"}, + {/* -0 */ "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "Xi: T(b)", "Xi: T(b)"}, + {/* +0 */ "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "Xi: T(b)", "Xi: T(b)"}, + {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "Xi: T(b)", "Xi: T(b)"}, + {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b)", "Xi: T(b)"}, + {/* QNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* SNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"}, + }, + }, + { + .op = VFMIN, + .m6 = 3, + .m6_desc = "C++ algorithm.min()", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* -0 */ "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* +0 */ "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* QNaN */ "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* SNaN */ "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"}, + }, + }, + { + .op = VFMIN, + .m6 = 4, + .m6_desc = "fmin()", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"}, + {/* -Fn */ "T(b)", "T(M(a,b))", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"}, + {/* -0 */ "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"}, + {/* +0 */ "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"}, + {/* +Fn */ "T(b)", "T(b)", "T(b)", "T(b)", "T(M(a,b))", "T(a)", "T(a)", "Xi: T(a)"}, + {/* +inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "T(a)", "Xi: T(a)"}, + {/* QNaN */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"}, + {/* SNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(a)", "Xi: T(a)"}, + }, + }, + + { + .op = VFMAX, + .m6 = 0, + .m6_desc = "IEEE MaxNum", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"}, + {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"}, + {/* -0 */ "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"}, + {/* +0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"}, + {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "T(a)", "Xi: T(b*)"}, + {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* QNaN */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(b*)"}, + {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"}, + }, + }, + { + .op = VFMAX, + .m6 = 1, + .m6_desc = "JAVA Math.Max()", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"}, + {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"}, + {/* -0 */ "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"}, + {/* +0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "Xi: T(b*)"}, + {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "T(b)", "Xi: T(b*)"}, + {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b*)"}, + {/* QNaN */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(b*)"}, + {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"}, + }, + }, + { + .op = VFMAX, + .m6 = 2, + .m6_desc = "C-style Max Macro", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* -0 */ "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* +0 */ "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* QNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"}, + {/* SNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)"}, + }, + }, + { + .op = VFMAX, + .m6 = 3, + .m6_desc = "C++ algorithm.max()", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(a)", "Xi: T(a)"}, + {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "Xi: T(a)", "Xi: T(a)"}, + {/* -0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "Xi: T(a)", "Xi: T(a)"}, + {/* +0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "Xi: T(a)", "Xi: T(a)"}, + {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "Xi: T(a)", "Xi: T(a)"}, + {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* QNaN */ "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"}, + {/* SNaN */ "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)", "Xi: T(a)"}, + }, + }, + { + .op = VFMAX, + .m6 = 4, + .m6_desc = "fmax()", + .table = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(a)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"}, + {/* -Fn */ "T(a)", "T(M(a,b))", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"}, + {/* -0 */ "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"}, + {/* +0 */ "T(a)", "T(a)", "T(a)", "T(a)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"}, + {/* +Fn */ "T(a)", "T(a)", "T(a)", "T(a)", "T(M(a,b))", "T(b)", "T(a)", "Xi: T(a)"}, + {/* +inf */ "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "T(a)", "Xi: T(a)"}, + {/* QNaN */ "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(b)", "T(a)", "Xi: T(a)"}, + {/* SNaN */ "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(b)", "Xi: T(a)", "Xi: T(a)"}, + }, + }, +}; + +static void dump_v(FILE *f, const void *v, size_t n) +{ + for (int i = 0; i < n; i++) { + fprintf(f, "%02x", ((const unsigned char *)v)[i]); + } +} + +static int signed_test(struct signed_test *test, int m4, int m5, + const void *v1_exp, bool xi_exp, + const void *v2, const void *v3) +{ + size_t n = (m5 & 8) ? float_sizes[m4 - 2] : 16; + char v1[16]; + bool xi; + + feclearexcept(FE_ALL_EXCEPT); + vfminmax(test->op, m4, m5, test->m6, v1, v2, v3); + xi = fetestexcept(FE_ALL_EXCEPT) == FE_INVALID; + + if (memcmp(v1, v1_exp, n) != 0 || xi != xi_exp) { + fprintf(stderr, "[ FAILED ] %s ", test->m6_desc); + dump_v(stderr, v2, n); + fprintf(stderr, ", "); + dump_v(stderr, v3, n); + fprintf(stderr, ", %d, %d, %d: actual=", m4, m5, test->m6); + dump_v(stderr, v1, n); + fprintf(stderr, "/%d, expected=", (int)xi); + dump_v(stderr, v1_exp, n); + fprintf(stderr, "/%d\n", (int)xi_exp); + return 1; + } + + return 0; +} + +static void snan_to_qnan(char *v, int m4) +{ + size_t bit = 1 + e_bits[m4 - 2]; + v[bit / 8] |= 1 << (7 - (bit % 8)); +} + +int main(void) +{ + int ret = 0; + size_t i; + + for (i = 0; i < sizeof(signed_tests) / sizeof(signed_tests[0]); i++) { + struct signed_test *test = &signed_tests[i]; + int m4; + + for (m4 = 2; m4 <= 4; m4++) { + const unsigned char (*floats)[2][16] = signed_floats[m4 - 2]; + size_t float_size = float_sizes[m4 - 2]; + int m5; + + for (m5 = 0; m5 <= 8; m5 += 8) { + char v1_exp[16], v2[16], v3[16]; + bool xi_exp = false; + int pos = 0; + int i2; + + for (i2 = 0; i2 < N_SIGNED_CLASSES * 2; i2++) { + int i3; + + for (i3 = 0; i3 < N_SIGNED_CLASSES * 2; i3++) { + const char *spec = test->table[i2 / 2][i3 / 2]; + + memcpy(&v2[pos], floats[i2 / 2][i2 % 2], float_size); + memcpy(&v3[pos], floats[i3 / 2][i3 % 2], float_size); + if (strcmp(spec, "T(a)") == 0 || + strcmp(spec, "Xi: T(a)") == 0) { + memcpy(&v1_exp[pos], &v2[pos], float_size); + } else if (strcmp(spec, "T(b)") == 0 || + strcmp(spec, "Xi: T(b)") == 0) { + memcpy(&v1_exp[pos], &v3[pos], float_size); + } else if (strcmp(spec, "Xi: T(a*)") == 0) { + memcpy(&v1_exp[pos], &v2[pos], float_size); + snan_to_qnan(&v1_exp[pos], m4); + } else if (strcmp(spec, "Xi: T(b*)") == 0) { + memcpy(&v1_exp[pos], &v3[pos], float_size); + snan_to_qnan(&v1_exp[pos], m4); + } else if (strcmp(spec, "T(M(a,b))") == 0) { + /* + * Comparing floats is risky, since the compiler + * might generate the same instruction that we are + * testing. Compare ints instead. This works, + * because we get here only for +-Fn, and the + * corresponding test values have identical + * exponents. + */ + int v2_int = *(int *)&v2[pos]; + int v3_int = *(int *)&v3[pos]; + + if ((v2_int < v3_int) == + ((test->op == VFMIN) != (v2_int < 0))) { + memcpy(&v1_exp[pos], &v2[pos], float_size); + } else { + memcpy(&v1_exp[pos], &v3[pos], float_size); + } + } else { + fprintf(stderr, "Unexpected spec: %s\n", spec); + return 1; + } + xi_exp |= spec[0] == 'X'; + pos += float_size; + + if ((m5 & 8) || pos == 16) { + ret |= signed_test(test, m4, m5, + v1_exp, xi_exp, v2, v3); + pos = 0; + xi_exp = false; + } + } + } + + if (pos != 0) { + ret |= signed_test(test, m4, m5, v1_exp, xi_exp, v2, v3); + } + } + } + } + + return ret; +} diff --git a/tests/tcg/s390x/vistr.c b/tests/tcg/s390x/vistr.c new file mode 100644 index 0000000000..8e3e987d71 --- /dev/null +++ b/tests/tcg/s390x/vistr.c @@ -0,0 +1,45 @@ +/* + * Test the VECTOR ISOLATE STRING (vistr) instruction + */ +#include +#include +#include "vx.h" + +static inline void vistr(S390Vector *v1, S390Vector *v2, + const uint8_t m3, const uint8_t m5) +{ + asm volatile("vistr %[v1], %[v2], %[m3], %[m5]\n" + : [v1] "=v" (v1->v) + : [v2] "v" (v2->v) + , [m3] "i" (m3) + , [m5] "i" (m5) + : "cc"); +} + +int main(int argc, char *argv[]) +{ + S390Vector vd = {}; + S390Vector vs16 = { + .h[0] = 0x1234, .h[1] = 0x0056, .h[2] = 0x7800, .h[3] = 0x0000, + .h[4] = 0x0078, .h[5] = 0x0000, .h[6] = 0x6543, .h[7] = 0x2100 + }; + S390Vector vs32 = { + .w[0] = 0x12340000, .w[1] = 0x78654300, + .w[2] = 0x0, .w[3] = 0x12, + }; + + vistr(&vd, &vs16, 1, 0); + if (vd.h[0] != 0x1234 || vd.h[1] != 0x0056 || vd.h[2] != 0x7800 || + vd.h[3] || vd.h[4] || vd.h[5] || vd.h[6] || vd.h[7]) { + puts("ERROR: vitrh failed!"); + return 1; + } + + vistr(&vd, &vs32, 2, 0); + if (vd.w[0] != 0x12340000 || vd.w[1] != 0x78654300 || vd.w[2] || vd.w[3]) { + puts("ERROR: vitrf failed!"); + return 1; + } + + return 0; +} diff --git a/tests/tcg/s390x/vrep.c b/tests/tcg/s390x/vrep.c new file mode 100644 index 0000000000..d5a3bd8eb2 --- /dev/null +++ b/tests/tcg/s390x/vrep.c @@ -0,0 +1,81 @@ +/* + * Test the VREP instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include +#include +#include +#include "vx.h" + +static void handle_sigill(int sig, siginfo_t *info, void *ucontext) +{ + mcontext_t *mcontext = &((ucontext_t *)ucontext)->uc_mcontext; + char *insn = (char *)info->si_addr; + + if (insn[0] != 0xe7 || insn[5] != 0x4d) { + _exit(EXIT_FAILURE); + } + + mcontext->gregs[2] = SIGILL; +} + +static inline __attribute__((__always_inline__)) unsigned long +vrep(S390Vector *v1, const S390Vector *v3, const uint16_t i2, const uint8_t m4) +{ + register unsigned long sig asm("r2") = -1; + + asm("vrep %[v1],%[v3],%[i2],%[m4]\n" + : [v1] "=v" (v1->v) + , [sig] "+r" (sig) + : [v3] "v" (v3->v) + , [i2] "i" (i2) + , [m4] "i" (m4)); + + return sig; +} + +int main(int argc, char *argv[]) +{ + S390Vector v3 = {.d[0] = 1, .d[1] = 2}; + struct sigaction act; + S390Vector v1; + int err; + + memset(&act, 0, sizeof(act)); + act.sa_sigaction = handle_sigill; + act.sa_flags = SA_SIGINFO; + err = sigaction(SIGILL, &act, NULL); + assert(err == 0); + + assert(vrep(&v1, &v3, 7, 0) == -1); + assert(v1.d[0] == 0x0101010101010101ULL); + assert(v1.d[1] == 0x0101010101010101ULL); + + assert(vrep(&v1, &v3, 7, 1) == -1); + assert(v1.d[0] == 0x0002000200020002ULL); + assert(v1.d[1] == 0x0002000200020002ULL); + + assert(vrep(&v1, &v3, 1, 2) == -1); + assert(v1.d[0] == 0x0000000100000001ULL); + assert(v1.d[1] == 0x0000000100000001ULL); + + assert(vrep(&v1, &v3, 1, 3) == -1); + assert(v1.d[0] == 2); + assert(v1.d[1] == 2); + + assert(vrep(&v1, &v3, 0x10, 0) == SIGILL); + assert(vrep(&v1, &v3, 0x101, 0) == SIGILL); + assert(vrep(&v1, &v3, 0x8, 1) == SIGILL); + assert(vrep(&v1, &v3, 0x108, 1) == SIGILL); + assert(vrep(&v1, &v3, 0x4, 2) == SIGILL); + assert(vrep(&v1, &v3, 0x104, 2) == SIGILL); + assert(vrep(&v1, &v3, 0x2, 3) == SIGILL); + assert(vrep(&v1, &v3, 0x102, 3) == SIGILL); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/vstl.c b/tests/tcg/s390x/vstl.c new file mode 100644 index 0000000000..bece952c7e --- /dev/null +++ b/tests/tcg/s390x/vstl.c @@ -0,0 +1,37 @@ +/* + * Test the VSTL instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include "vx.h" + +static inline void vstl(S390Vector *v1, void *db2, size_t r3) +{ + asm("vstl %[v1],%[r3],%[db2]" + : [db2] "=Q" (*(char *)db2) + : [v1] "v" (v1->v), [r3] "r" (r3) + : "memory"); +} + +int main(void) +{ + uint64_t buf[3] = {0x1122334455667788ULL, 0x99aabbccddeeffULL, + 0x5a5a5a5a5a5a5a5aULL}; + S390Vector v = {.d[0] = 0x1234567887654321ULL, + .d[1] = 0x9abcdef00fedcba9ULL}; + + vstl(&v, buf, 0); + assert(buf[0] == 0x1222334455667788ULL); + + vstl(&v, buf, 1); + assert(buf[0] == 0x1234334455667788ULL); + + vstl(&v, buf, -1); + assert(buf[0] == 0x1234567887654321ULL); + assert(buf[1] == 0x9abcdef00fedcba9ULL); + assert(buf[2] == 0x5a5a5a5a5a5a5a5aULL); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/vx.h b/tests/tcg/s390x/vx.h index 02e7fd518a..00701dbe35 100644 --- a/tests/tcg/s390x/vx.h +++ b/tests/tcg/s390x/vx.h @@ -1,6 +1,8 @@ #ifndef QEMU_TESTS_S390X_VX_H #define QEMU_TESTS_S390X_VX_H +#include + typedef union S390Vector { uint64_t d[2]; /* doubleword */ uint32_t w[4]; /* word */ diff --git a/tests/tcg/s390x/vxeh2_vstrs.c b/tests/tcg/s390x/vxeh2_vstrs.c new file mode 100644 index 0000000000..313ec1d728 --- /dev/null +++ b/tests/tcg/s390x/vxeh2_vstrs.c @@ -0,0 +1,88 @@ +/* + * Test the VSTRS instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include "vx.h" + +static inline __attribute__((__always_inline__)) int +vstrs(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, + const S390Vector *v4, const uint8_t m5, const uint8_t m6) +{ + int cc; + + asm("vstrs %[v1],%[v2],%[v3],%[v4],%[m5],%[m6]\n" + "ipm %[cc]" + : [v1] "=v" (v1->v) + , [cc] "=r" (cc) + : [v2] "v" (v2->v) + , [v3] "v" (v3->v) + , [v4] "v" (v4->v) + , [m5] "i" (m5) + , [m6] "i" (m6) + : "cc"); + + return (cc >> 28) & 3; +} + +static void test_ignored_match(void) +{ + S390Vector v1; + S390Vector v2 = {.d[0] = 0x222000205e410000ULL, .d[1] = 0}; + S390Vector v3 = {.d[0] = 0x205e410000000000ULL, .d[1] = 0}; + S390Vector v4 = {.d[0] = 3, .d[1] = 0}; + + assert(vstrs(&v1, &v2, &v3, &v4, 0, 2) == 1); + assert(v1.d[0] == 16); + assert(v1.d[1] == 0); +} + +static void test_empty_needle(void) +{ + S390Vector v1; + S390Vector v2 = {.d[0] = 0x5300000000000000ULL, .d[1] = 0}; + S390Vector v3 = {.d[0] = 0, .d[1] = 0}; + S390Vector v4 = {.d[0] = 0, .d[1] = 0}; + + assert(vstrs(&v1, &v2, &v3, &v4, 0, 0) == 2); + assert(v1.d[0] == 0); + assert(v1.d[1] == 0); +} + +static void test_max_length(void) +{ + S390Vector v1; + S390Vector v2 = {.d[0] = 0x1122334455667700ULL, .d[1] = 0}; + S390Vector v3 = {.d[0] = 0, .d[1] = 0}; + S390Vector v4 = {.d[0] = 16, .d[1] = 0}; + + assert(vstrs(&v1, &v2, &v3, &v4, 0, 0) == 3); + assert(v1.d[0] == 7); + assert(v1.d[1] == 0); +} + +static void test_no_match(void) +{ + S390Vector v1; + S390Vector v2 = {.d[0] = 0xffffff000fffff00ULL, .d[1] = 0x82b}; + S390Vector v3 = {.d[0] = 0xfffffffeffffffffULL, + .d[1] = 0xffffffff00000000ULL}; + S390Vector v4 = {.d[0] = 11, .d[1] = 0}; + + assert(vstrs(&v1, &v2, &v3, &v4, 0, 2) == 1); + assert(v1.d[0] == 16); + assert(v1.d[1] == 0); +} + +int main(void) +{ + test_ignored_match(); + test_empty_needle(); + test_max_length(); + test_no_match(); + return EXIT_SUCCESS; +} diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target index 35ebe6b4e3..4d09291c0c 100644 --- a/tests/tcg/sh4/Makefile.target +++ b/tests/tcg/sh4/Makefile.target @@ -3,9 +3,6 @@ # SuperH specific tweaks # -# On sh Linux supports 4k, 8k, 16k and 64k pages (but only 4k currently works) -EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-8192 run-test-mmap-16384 run-test-mmap-65536 - # This triggers failures for sh4-linux about 10% of the time. # Random SIGSEGV at unpredictable guest address, cause unknown. run-signals: signals @@ -13,14 +10,10 @@ run-signals: signals run-plugin-signals-with-%: $(call skip-test, $<, "BROKEN") -# This test is currently broken: https://gitlab.com/qemu-project/qemu/-/issues/704 -run-linux-test: linux-test - $(call skip-test, $<, "BROKEN") -run-plugin-linux-test-with-%: - $(call skip-test, $<, "BROKEN") +VPATH += $(SRC_PATH)/tests/tcg/sh4 -# This test is currently unreliable: https://gitlab.com/qemu-project/qemu/-/issues/856 -run-threadcount: - $(call skip-test, $<, "BROKEN") -run-plugin-threadcount-with-%: - $(call skip-test, $<, "BROKEN") +test-macl: CFLAGS += -O -g +TESTS += test-macl + +test-macw: CFLAGS += -O -g +TESTS += test-macw diff --git a/tests/tcg/sh4/test-macl.c b/tests/tcg/sh4/test-macl.c new file mode 100644 index 0000000000..b66c854365 --- /dev/null +++ b/tests/tcg/sh4/test-macl.c @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include + +#define MACL_S_MIN (-(1ll << 47)) +#define MACL_S_MAX ((1ll << 47) - 1) + +int64_t mac_l(int64_t mac, const int32_t *a, const int32_t *b) +{ + register uint32_t macl __asm__("macl") = mac; + register uint32_t mach __asm__("mach") = mac >> 32; + + asm volatile("mac.l @%0+,@%1+" + : "+r"(a), "+r"(b), "+x"(macl), "+x"(mach)); + + return ((uint64_t)mach << 32) | macl; +} + +typedef struct { + int64_t mac; + int32_t a, b; + int64_t res[2]; +} Test; + +__attribute__((noinline)) +void test(const Test *t, int sat) +{ + int64_t res; + + if (sat) { + asm volatile("sets"); + } else { + asm volatile("clrs"); + } + res = mac_l(t->mac, &t->a, &t->b); + + if (res != t->res[sat]) { + fprintf(stderr, "%#llx + (%#x * %#x) = %#llx -- got %#llx\n", + t->mac, t->a, t->b, t->res[sat], res); + abort(); + } +} + +int main() +{ + static const Test tests[] = { + { 0x00007fff12345678ll, INT32_MAX, INT32_MAX, + { 0x40007ffe12345679ll, MACL_S_MAX } }, + { MACL_S_MIN, -1, 1, + { 0xffff7fffffffffffll, MACL_S_MIN } }, + { INT64_MIN, -1, 1, + { INT64_MAX, MACL_S_MIN } }, + { 0x00007fff00000000ll, INT32_MAX, INT32_MAX, + { 0x40007ffe00000001ll, MACL_S_MAX } }, + { 4, 1, 2, { 6, 6 } }, + { -4, -1, -2, { -2, -2 } }, + }; + + for (int i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) { + for (int j = 0; j < 2; ++j) { + test(&tests[i], j); + } + } + return 0; +} diff --git a/tests/tcg/sh4/test-macw.c b/tests/tcg/sh4/test-macw.c new file mode 100644 index 0000000000..4eceec8634 --- /dev/null +++ b/tests/tcg/sh4/test-macw.c @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include + +int64_t mac_w(int64_t mac, const int16_t *a, const int16_t *b) +{ + register uint32_t macl __asm__("macl") = mac; + register uint32_t mach __asm__("mach") = mac >> 32; + + asm volatile("mac.w @%0+,@%1+" + : "+r"(a), "+r"(b), "+x"(macl), "+x"(mach)); + + return ((uint64_t)mach << 32) | macl; +} + +typedef struct { + int64_t mac; + int16_t a, b; + int64_t res[2]; +} Test; + +__attribute__((noinline)) +void test(const Test *t, int sat) +{ + int64_t res; + + if (sat) { + asm volatile("sets"); + } else { + asm volatile("clrs"); + } + res = mac_w(t->mac, &t->a, &t->b); + + if (res != t->res[sat]) { + fprintf(stderr, "%#llx + (%#x * %#x) = %#llx -- got %#llx\n", + t->mac, t->a, t->b, t->res[sat], res); + abort(); + } +} + +int main() +{ + static const Test tests[] = { + { 0, 2, 3, { 6, 6 } }, + { 0x123456787ffffffell, 2, -3, + { 0x123456787ffffff8ll, 0x123456787ffffff8ll } }, + { 0xabcdef127ffffffall, 2, 3, + { 0xabcdef1280000000ll, 0x000000017fffffffll } }, + { 0xfffffffffll, INT16_MAX, INT16_MAX, + { 0x103fff0000ll, 0xf3fff0000ll } }, + }; + + for (int i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) { + for (int j = 0; j < 2; ++j) { + test(&tests[i], j); + } + } + return 0; +} diff --git a/tests/tcg/sparc64/Makefile.target b/tests/tcg/sparc64/Makefile.target deleted file mode 100644 index 408dace783..0000000000 --- a/tests/tcg/sparc64/Makefile.target +++ /dev/null @@ -1,6 +0,0 @@ -# -*- Mode: makefile -*- -# -# sparc specific tweaks - -# On Sparc64 Linux support 8k pages -EXTRA_RUNS+=run-test-mmap-8192 diff --git a/tests/tcg/tricore/Makefile.softmmu-target b/tests/tcg/tricore/Makefile.softmmu-target index 5007c60ce8..258aeb40ae 100644 --- a/tests/tcg/tricore/Makefile.softmmu-target +++ b/tests/tcg/tricore/Makefile.softmmu-target @@ -1,26 +1,53 @@ TESTS_PATH = $(SRC_PATH)/tests/tcg/tricore +ASM_TESTS_PATH = $(TESTS_PATH)/asm +C_TESTS_PATH = $(TESTS_PATH)/c -LDFLAGS = -T$(TESTS_PATH)/link.ld -ASFLAGS = +LDFLAGS = -T$(TESTS_PATH)/link.ld --mcpu=tc162 +ASFLAGS = -mtc162 +CFLAGS = -mtc162 -c -I$(TESTS_PATH) -TESTS += test_abs.tst -TESTS += test_bmerge.tst -TESTS += test_clz.tst -TESTS += test_dvstep.tst -TESTS += test_fadd.tst -TESTS += test_fmul.tst -TESTS += test_ftoi.tst -TESTS += test_madd.tst -TESTS += test_msub.tst -TESTS += test_muls.tst +TESTS += test_abs.asm.tst +TESTS += test_bmerge.asm.tst +TESTS += test_clz.asm.tst +TESTS += test_crcn.asm.tst +TESTS += test_dextr.asm.tst +TESTS += test_dvstep.asm.tst +TESTS += test_fadd.asm.tst +TESTS += test_fmul.asm.tst +TESTS += test_ftohp.asm.tst +TESTS += test_ftoi.asm.tst +TESTS += test_ftou.asm.tst +TESTS += test_hptof.asm.tst +TESTS += test_imask.asm.tst +TESTS += test_insert.asm.tst +TESTS += test_ld_bu.asm.tst +TESTS += test_ld_h.asm.tst +TESTS += test_madd.asm.tst +TESTS += test_msub.asm.tst +TESTS += test_muls.asm.tst -QEMU_OPTS += -M tricore_testboard -nographic -kernel +TESTS += test_boot_to_main.c.tst +TESTS += test_context_save_areas.c.tst -%.pS: $(TESTS_PATH)/%.S - $(HOST_CC) -E -o $@ $< +QEMU_OPTS += -M tricore_testboard -cpu tc37x -nographic -kernel + +%.pS: $(ASM_TESTS_PATH)/%.S + $(CC) -E -o $@ $< %.o: %.pS $(AS) $(ASFLAGS) -o $@ $< -%.tst: %.o +%.asm.tst: %.o $(LD) $(LDFLAGS) $< -o $@ + +crt0-tc2x.o: $(C_TESTS_PATH)/crt0-tc2x.S + $(AS) $(ASFLAGS) -o $@ $< + +%.o: $(C_TESTS_PATH)/%.c + $(CC) $(CFLAGS) -o $@ $< + +%.c.tst: %.o crt0-tc2x.o + $(LD) $(LDFLAGS) -o $@ $^ + +# We don't currently support the multiarch system tests +undefine MULTIARCH_TESTS diff --git a/tests/tcg/tricore/macros.h b/tests/tcg/tricore/asm/macros.h similarity index 50% rename from tests/tcg/tricore/macros.h rename to tests/tcg/tricore/asm/macros.h index 0d76fc403a..e831f73721 100644 --- a/tests/tcg/tricore/macros.h +++ b/tests/tcg/tricore/asm/macros.h @@ -4,31 +4,39 @@ movh DREG_TEMP_LI, up:val; \ or reg, reg, DREG_TEMP_LI; \ +#define LIA(reg, val) \ + LI(DREG_TEMP, val) \ + mov.a reg, DREG_TEMP; + /* Address definitions */ #define TESTDEV_ADDR 0xf0000000 /* Register definitions */ #define DREG_RS1 %d0 -#define DREG_RS2 %d1 +#define DREG_RS2 %d2 #define DREG_RS3 %d4 -#define DREG_CALC_RESULT %d1 -#define DREG_CALC_PSW %d2 -#define DREG_CORRECT_PSW %d3 -#define DREG_TEMP_LI %d10 -#define DREG_TEMP %d11 -#define DREG_TEST_NUM %d14 -#define DREG_CORRECT_RESULT %d15 +#define DREG_CALC_RESULT %d5 +#define DREG_CALC_PSW %d6 +#define DREG_CORRECT_PSW %d7 +#define DREG_TEMP_LI %d13 +#define DREG_TEMP %d14 +#define DREG_TEST_NUM %d8 +#define DREG_CORRECT_RESULT %d9 +#define DREG_CORRECT_RESULT_2 %d10 + +#define AREG_ADDR %a0 +#define AREG_CORRECT_RESULT %a3 #define DREG_DEV_ADDR %a15 -#define EREG_RS1 %e6 -#define EREG_RS1_LO %d6 -#define EREG_RS1_HI %d7 -#define EREG_RS2 %e8 -#define EREG_RS2_LO %d8 -#define EREG_RS2_HI %d9 -#define EREG_CALC_RESULT %e8 -#define EREG_CALC_RESULT_HI %d9 -#define EREG_CALC_RESULT_LO %d8 +#define EREG_RS1 %e0 +#define EREG_RS1_LO %d0 +#define EREG_RS1_HI %d1 +#define EREG_RS2 %e2 +#define EREG_RS2_LO %d2 +#define EREG_RS2_HI %d3 +#define EREG_CALC_RESULT %e6 +#define EREG_CALC_RESULT_LO %d6 +#define EREG_CALC_RESULT_HI %d7 #define EREG_CORRECT_RESULT_LO %d0 #define EREG_CORRECT_RESULT_HI %d1 @@ -38,7 +46,8 @@ test_ ## num: \ code; \ LI(DREG_CORRECT_RESULT, correct) \ mov DREG_TEST_NUM, num; \ - jne testreg, DREG_CORRECT_RESULT, fail \ + jne testreg, DREG_CORRECT_RESULT, fail; \ + mov testreg, 0 #define TEST_CASE_E(num, correct_lo, correct_hi, code...) \ test_ ## num: \ @@ -60,11 +69,36 @@ test_ ## num: \ mov DREG_TEST_NUM, num; \ jne DREG_CALC_PSW, DREG_CORRECT_PSW, fail; +#define TEST_LD(insn, num, result, addr_result, ld_pattern) \ +test_ ## num: \ + LIA(AREG_ADDR, test_data) \ + insn DREG_CALC_RESULT, ld_pattern; \ + LI(DREG_CORRECT_RESULT, result) \ + mov DREG_TEST_NUM, num; \ + jne DREG_CALC_RESULT, DREG_CORRECT_RESULT, fail; \ + mov.d DREG_CALC_RESULT, AREG_ADDR; \ + LI(DREG_CORRECT_RESULT, addr_result) \ + jne DREG_CALC_RESULT, DREG_CORRECT_RESULT, fail; + +#define TEST_LD_SRO(insn, num, result, addr_result, ld_pattern) \ +test_ ## num: \ + LIA(AREG_ADDR, test_data) \ + insn %d15, ld_pattern; \ + LI(DREG_CORRECT_RESULT_2, result) \ + mov DREG_TEST_NUM, num; \ + jne %d15, DREG_CORRECT_RESULT_2, fail; \ + mov.d DREG_CALC_RESULT, AREG_ADDR; \ + LI(DREG_CORRECT_RESULT, addr_result) \ + jne DREG_CALC_RESULT, DREG_CORRECT_RESULT, fail; + + /* Actual test case type * e.g inst %dX, %dY -> TEST_D_D * inst %dX, %dY, %dZ -> TEST_D_DD * inst %eX, %dY, %dZ -> TEST_E_DD */ + + #define TEST_D_D(insn, num, result, rs1) \ TEST_CASE(num, DREG_CALC_RESULT, result, \ LI(DREG_RS1, rs1); \ @@ -78,6 +112,15 @@ test_ ## num: \ insn DREG_CORRECT_RESULT, DREG_RS1; \ ) +#define TEST_D_DDD(insn, num, result, rs1, rs2, rs3) \ + TEST_CASE(num, DREG_CALC_RESULT, result, \ + LI(DREG_RS1, rs1); \ + LI(DREG_RS2, rs2); \ + LI(DREG_RS3, rs3); \ + rstv; \ + insn DREG_CALC_RESULT, DREG_RS1, DREG_RS2, DREG_RS3; \ + ) + #define TEST_D_DD_PSW(insn, num, result, psw, rs1, rs2) \ TEST_CASE_PSW(num, DREG_CALC_RESULT, result, psw, \ LI(DREG_RS1, rs1); \ @@ -95,6 +138,14 @@ test_ ## num: \ insn DREG_CALC_RESULT, DREG_RS1, DREG_RS2, DREG_RS3; \ ) +#define TEST_D_DDI(insn, num, result, rs1, rs2, imm) \ + TEST_CASE(num, DREG_CALC_RESULT, result, \ + LI(DREG_RS1, rs1); \ + LI(DREG_RS2, rs2); \ + rstv; \ + insn DREG_CALC_RESULT, DREG_RS1, DREG_RS2, imm; \ + ) + #define TEST_D_DDI_PSW(insn, num, result, psw, rs1, rs2, imm) \ TEST_CASE_PSW(num, DREG_CALC_RESULT, result, psw, \ LI(DREG_RS1, rs1); \ @@ -103,6 +154,38 @@ test_ ## num: \ insn DREG_CALC_RESULT, DREG_RS1, DREG_RS2, imm; \ ) +#define TEST_D_DIDI(insn, num, result, rs1, imm1, rs2, imm2) \ + TEST_CASE(num, DREG_CALC_RESULT, result, \ + LI(DREG_RS1, rs1); \ + LI(DREG_RS2, rs1); \ + rstv; \ + insn DREG_CALC_RESULT, DREG_RS1, imm1, DREG_RS2, imm2; \ + ) + +#define TEST_D_DDII(insn, num, result, rs1, rs2, imm1, imm2) \ + TEST_CASE(num, DREG_CALC_RESULT, result, \ + LI(DREG_RS1, rs1); \ + LI(DREG_RS2, rs2); \ + rstv; \ + insn DREG_CALC_RESULT, DREG_RS1, DREG_RS2, imm1, imm2; \ + ) + +#define TEST_D_DIE(insn, num, result, rs1, imm1, rs2_lo, rs2_hi)\ + TEST_CASE(num, DREG_CALC_RESULT, result, \ + LI(DREG_RS1, rs1); \ + LI(EREG_RS2_LO, rs2_lo); \ + LI(EREG_RS2_HI, rs2_hi); \ + rstv; \ + insn DREG_CALC_RESULT, DREG_RS1, imm1, EREG_RS2; \ + ) + +#define TEST_D_DIII(insn, num, result, rs1, imm1, imm2, imm3)\ + TEST_CASE(num, DREG_CALC_RESULT, result, \ + LI(DREG_RS1, rs1); \ + rstv; \ + insn DREG_CALC_RESULT, DREG_RS1, imm1, imm2, imm3; \ + ) + #define TEST_E_ED(insn, num, res_hi, res_lo, rs1_hi, rs1_lo, rs2) \ TEST_CASE_E(num, res_lo, res_hi, \ LI(EREG_RS1_LO, rs1_lo); \ @@ -111,6 +194,15 @@ test_ ## num: \ insn EREG_CALC_RESULT, EREG_RS1, DREG_RS2; \ ) +#define TEST_E_IDI(insn, num, res_hi, res_lo, imm1, rs1, imm2) \ + TEST_CASE_E(num, res_lo, res_hi, \ + LI(DREG_RS1, rs1); \ + rstv; \ + insn EREG_CALC_RESULT, imm1, DREG_RS1, imm2; \ + ) + + + /* Pass/Fail handling part */ #define TEST_PASSFAIL \ j pass; \ diff --git a/tests/tcg/tricore/test_abs.S b/tests/tcg/tricore/asm/test_abs.S similarity index 100% rename from tests/tcg/tricore/test_abs.S rename to tests/tcg/tricore/asm/test_abs.S diff --git a/tests/tcg/tricore/test_bmerge.S b/tests/tcg/tricore/asm/test_bmerge.S similarity index 100% rename from tests/tcg/tricore/test_bmerge.S rename to tests/tcg/tricore/asm/test_bmerge.S diff --git a/tests/tcg/tricore/test_clz.S b/tests/tcg/tricore/asm/test_clz.S similarity index 100% rename from tests/tcg/tricore/test_clz.S rename to tests/tcg/tricore/asm/test_clz.S diff --git a/tests/tcg/tricore/asm/test_crcn.S b/tests/tcg/tricore/asm/test_crcn.S new file mode 100644 index 0000000000..51a22722a3 --- /dev/null +++ b/tests/tcg/tricore/asm/test_crcn.S @@ -0,0 +1,9 @@ +#include "macros.h" +.text +.global _start +_start: +# insn num result rs1 rs2 rs3 +# | | | | | | + TEST_D_DDD(crcn, 1, 0x00002bed, 0x0, 0xa10ddeed, 0x0) + + TEST_PASSFAIL diff --git a/tests/tcg/tricore/asm/test_dextr.S b/tests/tcg/tricore/asm/test_dextr.S new file mode 100644 index 0000000000..82c8fe5185 --- /dev/null +++ b/tests/tcg/tricore/asm/test_dextr.S @@ -0,0 +1,75 @@ +#include "macros.h" +.text +.global _start +_start: +# insn num result rs1 rs2 imm +# | | | | | | + TEST_D_DDI(dextr, 1, 0xabcdef01, 0xabcdef01, 0x23456789, 0) + TEST_D_DDI(dextr, 2, 0x579bde02, 0xabcdef01, 0x23456789, 1) + TEST_D_DDI(dextr, 3, 0xaf37bc04, 0xabcdef01, 0x23456789, 2) + TEST_D_DDI(dextr, 4, 0x5e6f7809, 0xabcdef01, 0x23456789, 3) + TEST_D_DDI(dextr, 5, 0xbcdef012, 0xabcdef01, 0x23456789, 4) + TEST_D_DDI(dextr, 6, 0x79bde024, 0xabcdef01, 0x23456789, 5) + TEST_D_DDI(dextr, 7, 0xf37bc048, 0xabcdef01, 0x23456789, 6) + TEST_D_DDI(dextr, 8, 0xe6f78091, 0xabcdef01, 0x23456789, 7) + TEST_D_DDI(dextr, 9, 0xcdef0123, 0xabcdef01, 0x23456789, 8) + TEST_D_DDI(dextr, 10, 0x9bde0246, 0xabcdef01, 0x23456789, 9) + TEST_D_DDI(dextr, 11, 0x37bc048d, 0xabcdef01, 0x23456789, 10) + TEST_D_DDI(dextr, 12, 0x6f78091a, 0xabcdef01, 0x23456789, 11) + TEST_D_DDI(dextr, 13, 0xdef01234, 0xabcdef01, 0x23456789, 12) + TEST_D_DDI(dextr, 14, 0xbde02468, 0xabcdef01, 0x23456789, 13) + TEST_D_DDI(dextr, 15, 0x7bc048d1, 0xabcdef01, 0x23456789, 14) + TEST_D_DDI(dextr, 16, 0xf78091a2, 0xabcdef01, 0x23456789, 15) + TEST_D_DDI(dextr, 17, 0xef012345, 0xabcdef01, 0x23456789, 16) + TEST_D_DDI(dextr, 18, 0xde02468a, 0xabcdef01, 0x23456789, 17) + TEST_D_DDI(dextr, 19, 0xbc048d15, 0xabcdef01, 0x23456789, 18) + TEST_D_DDI(dextr, 20, 0x78091a2b, 0xabcdef01, 0x23456789, 19) + TEST_D_DDI(dextr, 21, 0xf0123456, 0xabcdef01, 0x23456789, 20) + TEST_D_DDI(dextr, 22, 0xe02468ac, 0xabcdef01, 0x23456789, 21) + TEST_D_DDI(dextr, 23, 0xc048d159, 0xabcdef01, 0x23456789, 22) + TEST_D_DDI(dextr, 24, 0x8091a2b3, 0xabcdef01, 0x23456789, 23) + TEST_D_DDI(dextr, 25, 0x01234567, 0xabcdef01, 0x23456789, 24) + TEST_D_DDI(dextr, 26, 0x02468acf, 0xabcdef01, 0x23456789, 25) + TEST_D_DDI(dextr, 27, 0x048d159e, 0xabcdef01, 0x23456789, 26) + TEST_D_DDI(dextr, 28, 0x091a2b3c, 0xabcdef01, 0x23456789, 27) + TEST_D_DDI(dextr, 29, 0x12345678, 0xabcdef01, 0x23456789, 28) + TEST_D_DDI(dextr, 30, 0x2468acf1, 0xabcdef01, 0x23456789, 29) + TEST_D_DDI(dextr, 31, 0x48d159e2, 0xabcdef01, 0x23456789, 30) + TEST_D_DDI(dextr, 32, 0x91a2b3c4, 0xabcdef01, 0x23456789, 31) + +# insn num result rs1 rs2 rs3 +# | | | | | | + TEST_D_DDD(dextr, 33, 0xabcdef01, 0xabcdef01, 0x23456789, 0) + TEST_D_DDD(dextr, 34, 0x579bde02, 0xabcdef01, 0x23456789, 1) + TEST_D_DDD(dextr, 35, 0xaf37bc04, 0xabcdef01, 0x23456789, 2) + TEST_D_DDD(dextr, 36, 0x5e6f7809, 0xabcdef01, 0x23456789, 3) + TEST_D_DDD(dextr, 37, 0xbcdef012, 0xabcdef01, 0x23456789, 4) + TEST_D_DDD(dextr, 38, 0x79bde024, 0xabcdef01, 0x23456789, 5) + TEST_D_DDD(dextr, 39, 0xf37bc048, 0xabcdef01, 0x23456789, 6) + TEST_D_DDD(dextr, 40, 0xe6f78091, 0xabcdef01, 0x23456789, 7) + TEST_D_DDD(dextr, 41, 0xcdef0123, 0xabcdef01, 0x23456789, 8) + TEST_D_DDD(dextr, 42, 0x9bde0246, 0xabcdef01, 0x23456789, 9) + TEST_D_DDD(dextr, 43, 0x37bc048d, 0xabcdef01, 0x23456789, 10) + TEST_D_DDD(dextr, 44, 0x6f78091a, 0xabcdef01, 0x23456789, 11) + TEST_D_DDD(dextr, 45, 0xdef01234, 0xabcdef01, 0x23456789, 12) + TEST_D_DDD(dextr, 46, 0xbde02468, 0xabcdef01, 0x23456789, 13) + TEST_D_DDD(dextr, 47, 0x7bc048d1, 0xabcdef01, 0x23456789, 14) + TEST_D_DDD(dextr, 48, 0xf78091a2, 0xabcdef01, 0x23456789, 15) + TEST_D_DDD(dextr, 49, 0xef012345, 0xabcdef01, 0x23456789, 16) + TEST_D_DDD(dextr, 51, 0xde02468a, 0xabcdef01, 0x23456789, 17) + TEST_D_DDD(dextr, 52, 0xbc048d15, 0xabcdef01, 0x23456789, 18) + TEST_D_DDD(dextr, 53, 0x78091a2b, 0xabcdef01, 0x23456789, 19) + TEST_D_DDD(dextr, 54, 0xf0123456, 0xabcdef01, 0x23456789, 20) + TEST_D_DDD(dextr, 55, 0xe02468ac, 0xabcdef01, 0x23456789, 21) + TEST_D_DDD(dextr, 56, 0xc048d159, 0xabcdef01, 0x23456789, 22) + TEST_D_DDD(dextr, 57, 0x8091a2b3, 0xabcdef01, 0x23456789, 23) + TEST_D_DDD(dextr, 58, 0x01234567, 0xabcdef01, 0x23456789, 24) + TEST_D_DDD(dextr, 59, 0x02468acf, 0xabcdef01, 0x23456789, 25) + TEST_D_DDD(dextr, 60, 0x048d159e, 0xabcdef01, 0x23456789, 26) + TEST_D_DDD(dextr, 61, 0x091a2b3c, 0xabcdef01, 0x23456789, 27) + TEST_D_DDD(dextr, 62, 0x12345678, 0xabcdef01, 0x23456789, 28) + TEST_D_DDD(dextr, 63, 0x2468acf1, 0xabcdef01, 0x23456789, 29) + TEST_D_DDD(dextr, 64, 0x48d159e2, 0xabcdef01, 0x23456789, 30) + TEST_D_DDD(dextr, 65, 0x91a2b3c4, 0xabcdef01, 0x23456789, 31) + + TEST_PASSFAIL diff --git a/tests/tcg/tricore/test_dvstep.S b/tests/tcg/tricore/asm/test_dvstep.S similarity index 100% rename from tests/tcg/tricore/test_dvstep.S rename to tests/tcg/tricore/asm/test_dvstep.S diff --git a/tests/tcg/tricore/test_fadd.S b/tests/tcg/tricore/asm/test_fadd.S similarity index 100% rename from tests/tcg/tricore/test_fadd.S rename to tests/tcg/tricore/asm/test_fadd.S diff --git a/tests/tcg/tricore/test_fmul.S b/tests/tcg/tricore/asm/test_fmul.S similarity index 100% rename from tests/tcg/tricore/test_fmul.S rename to tests/tcg/tricore/asm/test_fmul.S diff --git a/tests/tcg/tricore/asm/test_ftohp.S b/tests/tcg/tricore/asm/test_ftohp.S new file mode 100644 index 0000000000..9e23141c1e --- /dev/null +++ b/tests/tcg/tricore/asm/test_ftohp.S @@ -0,0 +1,14 @@ +#include "macros.h" +.text +.global _start +_start: + TEST_D_D(ftohp, 1, 0xffff, 0xffffffff) + TEST_D_D(ftohp, 2, 0xfc00, 0xff800000) + TEST_D_D(ftohp, 3, 0x7c00, 0x7f800000) + TEST_D_D(ftohp, 4, 0x0, 0x0) + TEST_D_D(ftohp, 5, 0x5, 0x34a43580) + + #TEST_D_D_PSW(ftohp, 6, 0x400, 0x8c000b80, 0x387fee74) + + TEST_PASSFAIL + diff --git a/tests/tcg/tricore/test_ftoi.S b/tests/tcg/tricore/asm/test_ftoi.S similarity index 100% rename from tests/tcg/tricore/test_ftoi.S rename to tests/tcg/tricore/asm/test_ftoi.S diff --git a/tests/tcg/tricore/asm/test_ftou.S b/tests/tcg/tricore/asm/test_ftou.S new file mode 100644 index 0000000000..10f106ad62 --- /dev/null +++ b/tests/tcg/tricore/asm/test_ftou.S @@ -0,0 +1,12 @@ +#include "macros.h" +.text +.global _start +_start: + TEST_D_D(ftou, 1, 0x00000000, 0x1733f6c2) + TEST_D_D(ftou, 2, 0x00000000, 0x2c9d9cdc) + TEST_D_D(ftou, 3, 0xffffffff, 0x56eb7395) + TEST_D_D(ftou, 4, 0x79900800, 0x4ef32010) + TEST_D_D(ftou, 5, 0x0353f510, 0x4c54fd44) + + TEST_PASSFAIL + diff --git a/tests/tcg/tricore/asm/test_hptof.S b/tests/tcg/tricore/asm/test_hptof.S new file mode 100644 index 0000000000..8adc5e5273 --- /dev/null +++ b/tests/tcg/tricore/asm/test_hptof.S @@ -0,0 +1,12 @@ +#include "macros.h" +.text +.global _start +_start: + TEST_D_D(hptof, 1, 0xba190000, 0xcc0e90c8) + TEST_D_D(hptof, 2, 0x3eaea000, 0x8be23575) + TEST_D_D(hptof, 3, 0xc33b8000, 0xcc48d9dc) + TEST_D_D(hptof, 4, 0x43e2a000, 0xaef95f15) + TEST_D_D(hptof, 5, 0x3d55e000, 0x04932aaf) + + TEST_PASSFAIL + diff --git a/tests/tcg/tricore/asm/test_imask.S b/tests/tcg/tricore/asm/test_imask.S new file mode 100644 index 0000000000..356cf398b8 --- /dev/null +++ b/tests/tcg/tricore/asm/test_imask.S @@ -0,0 +1,10 @@ +#include "macros.h" +.text +.global _start +_start: +# res[31:0] +# insn num res[63:32] | imm1 rs1 imm2 +# | | | | | | | + TEST_E_IDI(imask, 1, 0x000f0000, 0x00050000, 0x5, 0x10, 0x4) + + TEST_PASSFAIL diff --git a/tests/tcg/tricore/asm/test_insert.S b/tests/tcg/tricore/asm/test_insert.S new file mode 100644 index 0000000000..223d7ce796 --- /dev/null +++ b/tests/tcg/tricore/asm/test_insert.S @@ -0,0 +1,23 @@ +#include "macros.h" +.text +.global _start +_start: +# insn num result rs1 imm1 rs2 imm2 +# | | | | | | | + TEST_D_DIDI(insert, 1, 0x7fffffff, 0xffffffff, 0xa, 0x10, 0x8) + +# insn num result rs1 imm1 imm2 imm3 +# | | | | | | | + TEST_D_DIII(insert, 2, 0xd38fe370, 0xd38fe370, 0x4, 0x4 , 0x0) + TEST_D_DIII(insert, 3, 0xd38fe374, 0xd38fe370, 0x4, 0x0 , 0x4) + +# insn num result rs1 rs2 pos width +# | | | | | | | + TEST_D_DDII(insert, 4, 0x03c1e53c, 0x03c1e53c, 0x45821385, 0x7 ,0x0) + +# insn num result rs1 imm1 rs2_h rs2_l +# | | | | | | | + TEST_D_DIE(insert, 5, 0xe30c308d, 0xe30c308d ,0x3 , 0x00000000 ,0x00000000) + TEST_D_DIE(insert, 6, 0x669b0120, 0x669b2820 ,0x2 , 0x5530a1c7 ,0x3a2b0f67) + + TEST_PASSFAIL diff --git a/tests/tcg/tricore/asm/test_ld_bu.S b/tests/tcg/tricore/asm/test_ld_bu.S new file mode 100644 index 0000000000..4a1f40c37b --- /dev/null +++ b/tests/tcg/tricore/asm/test_ld_bu.S @@ -0,0 +1,15 @@ +#include "macros.h" +.data +test_data: + .word 0xaffedead + .word 0x001122ff +.text +.global _start +_start: +# expect. addr reg val after load +# insn num expect. load value | pattern for loading +# | | | | | + TEST_LD(ld.bu, 1, 0xff, test_data + 4, [+AREG_ADDR]4) # pre_inc + TEST_LD(ld.bu, 2, 0xad, test_data + 4, [AREG_ADDR+]4) # post_inc + + TEST_PASSFAIL diff --git a/tests/tcg/tricore/asm/test_ld_h.S b/tests/tcg/tricore/asm/test_ld_h.S new file mode 100644 index 0000000000..f5e4959198 --- /dev/null +++ b/tests/tcg/tricore/asm/test_ld_h.S @@ -0,0 +1,15 @@ +#include "macros.h" +.data +test_data: + .word 0xaffedead + .word 0x001122ff +.text +.global _start +_start: +# expect. addr reg val after load +# insn num expect. load value | pattern for loading +# | | | | | + TEST_LD (ld.h, 1, 0xffffaffe, test_data, [AREG_ADDR]2) + TEST_LD_SRO(ld.h, 2, 0x000022ff, test_data, [AREG_ADDR]4) + + TEST_PASSFAIL diff --git a/tests/tcg/tricore/test_madd.S b/tests/tcg/tricore/asm/test_madd.S similarity index 100% rename from tests/tcg/tricore/test_madd.S rename to tests/tcg/tricore/asm/test_madd.S diff --git a/tests/tcg/tricore/test_msub.S b/tests/tcg/tricore/asm/test_msub.S similarity index 100% rename from tests/tcg/tricore/test_msub.S rename to tests/tcg/tricore/asm/test_msub.S diff --git a/tests/tcg/tricore/test_muls.S b/tests/tcg/tricore/asm/test_muls.S similarity index 100% rename from tests/tcg/tricore/test_muls.S rename to tests/tcg/tricore/asm/test_muls.S diff --git a/tests/tcg/tricore/c/crt0-tc2x.S b/tests/tcg/tricore/c/crt0-tc2x.S new file mode 100644 index 0000000000..399f112c35 --- /dev/null +++ b/tests/tcg/tricore/c/crt0-tc2x.S @@ -0,0 +1,335 @@ +/* + * crt0-tc2x.S -- Startup code for GNU/TriCore applications. + * + * Copyright (C) 1998-2014 HighTec EDV-Systeme GmbH. + * + * This file is part of GCC. + * + * GCC 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 3, or (at your option) + * any later version. + * + * GCC 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. + * + * Under Section 7 of GPL version 3, you are granted additional + * permissions described in the GCC Runtime Library Exception, version + * 3.1, as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License and + * a copy of the GCC Runtime Library Exception along with this program; + * see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + * . */ + +/* Define the Derivate Name as a hexvalue. This value + * is built-in defined in tricore-c.c (from tricore-devices.c) + * the derivate number as a hexvalue (e.g. TC1796 => 0x1796 + * This name will be used in the memory.x Memory description to + * to confirm that the crt0.o and the memory.x will be get from + * same directory + */ + .section ".startup_code", "ax", @progbits + .global _start + .type _start,@function + +/* default BMI header (only TC2xxx devices) */ + .word 0x00000000 + .word 0xb3590070 + .word 0x00000000 + .word 0x00000000 + .word 0x00000000 + .word 0x00000000 + .word 0x791eb864 + .word 0x86e1479b + +_start: + .code32 + j _startaddr + .align 2 + +_startaddr: + /* + * initialize user and interrupt stack pointers + */ + movh.a %sp,hi:__USTACK # load %sp + lea %sp,[%sp]lo:__USTACK + movh %d0,hi:__ISTACK # load $isp + addi %d0,%d0,lo:__ISTACK + mtcr $isp,%d0 + isync + +#; install trap handlers + + movh %d0,hi:first_trap_table #; load $btv + addi %d0,%d0,lo:first_trap_table + mtcr $btv,%d0 + isync + + /* + * initialize call depth counter + */ + + mfcr %d0,$psw + or %d0,%d0,0x7f # disable call depth counting + andn %d0,%d0,0x80 # clear CDE bit + mtcr $psw,%d0 + isync + + /* + * initialize access to system global registers + */ + + mfcr %d0,$psw + or %d0,%d0,0x100 # set GW bit + mtcr $psw,%d0 + isync + + /* + * initialize SDA base pointers + */ + .global _SMALL_DATA_,_SMALL_DATA2_,_SMALL_DATA3_,_SMALL_DATA4_ + .weak _SMALL_DATA_,_SMALL_DATA2_,_SMALL_DATA3_,_SMALL_DATA4_ + + movh.a %a0,hi:_SMALL_DATA_ # %a0 addresses .sdata/.sbss + lea %a0,[%a0]lo:_SMALL_DATA_ + movh.a %a1,hi:_SMALL_DATA2_ # %a1 addresses .sdata2/.sbss2 + lea %a1,[%a1]lo:_SMALL_DATA2_ + movh.a %a8,hi:_SMALL_DATA3_ # %a8 addresses .sdata3/.sbss3 + lea %a8,[%a8]lo:_SMALL_DATA3_ + movh.a %a9,hi:_SMALL_DATA4_ # %a9 addresses .sdata4/.sbss4 + lea %a9,[%a9]lo:_SMALL_DATA4_ + + /* + * reset access to system global registers + */ + + mfcr %d0,$psw + andn %d0,%d0,0x100 # clear GW bit + mtcr $psw,%d0 + isync + + /* + * initialize context save areas + */ + + jl __init_csa + + + + /* + * handle clear table (i.e., fill BSS with zeros) + */ + + jl __clear_table_func + + + /* + * handle copy table (support for romable code) + */ + + jl __copy_table_func + + + /* + * _exit (main (0, NULL)); + */ + mov %d4,0 # argc = 0 + sub.a %sp,8 + st.w [%sp]0,%d4 + st.w [%sp]4,%d4 + mov.aa %a4,%sp # argv + + call main # int retval = main (0, NULL); + mov.a %a14,%d2 # move exit code to match trap handler + j _exit # _exit (retval); + + debug # should never come here + + + /* + * initialize context save areas (CSAs), PCXI, LCX and FCX + */ + + .global __init_csa + .type __init_csa,function + +__init_csa: + movh %d0,0 + mtcr $pcxi,%d0 + isync + movh %d0,hi:__CSA_BEGIN #; %d0 = begin of CSA + addi %d0,%d0,lo:__CSA_BEGIN + addi %d0,%d0,63 #; force alignment (2^6) + andn %d0,%d0,63 + movh %d2,hi:__CSA_END #; %d2 = end of CSA + addi %d2,%d2,lo:__CSA_END + andn %d2,%d2,63 #; force alignment (2^6) + sub %d2,%d2,%d0 + sh %d2,%d2,-6 #; %d2 = number of CSAs + mov.a %a3,%d0 #; %a3 = address of first CSA + extr.u %d0,%d0,28,4 #; %d0 = segment << 16 + sh %d0,%d0,16 + lea %a4,0 #; %a4 = previous CSA = 0 + st.a [%a3],%a4 #; store it in 1st CSA + mov.aa %a4,%a3 #; %a4 = current CSA + lea %a3,[%a3]64 #; %a3 = %a3->nextCSA + mov.d %d1,%a3 + extr.u %d1,%d1,6,16 #; get CSA index + or %d1,%d1,%d0 #; add segment number + mtcr $lcx,%d1 #; initialize LCX + add %d2,%d2,-2 #; CSAs to initialize -= 2 + mov.a %a5,%d2 #; %a5 = loop counter +csa_loop: + mov.d %d1,%a4 #; %d1 = current CSA address + extr.u %d1,%d1,6,16 #; get CSA index + or %d1,%d1,%d0 #; add segment number + st.w [%a3],%d1 #; store "nextCSA" pointer + mov.aa %a4,%a3 #; %a4 = current CSA address + lea %a3,[%a3]64 #; %a3 = %a3->nextCSA + loop %a5,csa_loop #; repeat until done + + mov.d %d1,%a4 #; %d1 = current CSA address + extr.u %d1,%d1,6,16 #; get CSA index + or %d1,%d1,%d0 #; add segment number + mtcr $fcx,%d1 #; initialize FCX + isync + ji %a11 + + + + + /* + * handle clear table (i.e., fill BSS with zeros) + */ + .global __clear_table_func + .type __clear_table_func,@function + +__clear_table_func: + mov %d14,0 # %e14 = 0 + mov %d15,0 + movh.a %a13,hi:__clear_table # %a13 = &first table entry + lea %a13,[%a13]lo:__clear_table + +__clear_table_next: + ld.a %a15,[%a13+]4 # %a15 = current block base + ld.w %d3,[%a13+]4 # %d3 = current block length + jeq %d3,-1,__clear_table_done # length == -1 => end of table + sh %d0,%d3,-3 # %d0 = length / 8 (doublewords) + and %d1,%d3,7 # %d1 = length % 8 (rem. bytes) + jz %d0,__clear_word # block size < 8 => clear word + addi %d0,%d0,-1 # else doublewords -= 1 + mov.a %a2,%d0 # %a2 = loop counter +__clear_dword: + st.d [%a15+]8,%e14 # clear one doubleword + loop %a2,__clear_dword +__clear_word: + jz %d1,__clear_table_next + sh %d0,%d1,-2 # %d0 = length / 4 (words) + and %d1,%d1,3 # %d1 = length % 4 (rem. bytes) + jz %d0,__clear_hword # block size < 4 => clear hword + st.w [%a15+]4,%d15 # clear one word +__clear_hword: + jz %d1,__clear_table_next + sh %d0,%d1,-1 # %d0 = length / 2 (halfwords) + and %d1,%d1,1 # %d1 = length % 2 (rem. bytes) + jz %d0,__clear_byte # block size < 2 => clear byte + st.h [%a15+]2,%d15 # clear one halfword +__clear_byte: + jz %d1,__clear_table_next + st.b [%a15],%d15 # clear one byte + j __clear_table_next # handle next clear table entry +__clear_table_done: + + ji %a11 + + + + /* + * handle copy table (support for romable code) + */ + .global __copy_table_func + .type __copy_table_func,@function + +__copy_table_func: + movh.a %a13,hi:__copy_table # %a13 = &first table entry + lea %a13,[%a13]lo:__copy_table + +__copy_table_next: + ld.a %a15,[%a13+]4 # %a15 = src address + ld.a %a14,[%a13+]4 # %a14 = dst address + ld.w %d3,[%a13+]4 # %d3 = block length + jeq %d3,-1,__copy_table_done # length == -1 => end of table + sh %d0,%d3,-3 # %d0 = length / 8 (doublewords) + and %d1,%d3,7 # %d1 = length % 8 (rem. bytes) + jz %d0,__copy_word # block size < 8 => copy word + addi %d0,%d0,-1 # else doublewords -= 1 + mov.a %a2,%d0 # %a2 = loop counter +__copy_dword: + ld.d %e14,[%a15+]8 # copy one doubleword + st.d [%a14+]8,%e14 + loop %a2,__copy_dword +__copy_word: + jz %d1,__copy_table_next + sh %d0,%d1,-2 # %d0 = length / 4 (words) + and %d1,%d1,3 # %d1 = length % 4 (rem. bytes) + jz %d0,__copy_hword # block size < 4 => copy hword + ld.w %d14,[%a15+]4 # copy one word + st.w [%a14+]4,%d14 +__copy_hword: + jz %d1,__copy_table_next + sh %d0,%d1,-1 # %d0 = length / 2 (halfwords) + and %d1,%d1,1 # %d1 = length % 2 (rem. bytes) + jz %d0,__copy_byte # block size < 2 => copy byte + ld.h %d14,[%a15+]2 # copy one halfword + st.h [%a14+]2,%d14 +__copy_byte: + jz %d1,__copy_table_next + ld.b %d14,[%a15]0 # copy one byte + st.b [%a14],%d14 + j __copy_table_next # handle next copy table entry +__copy_table_done: + + ji %a11 + +_exit: + movh.a %a15, hi:__TESTDEVICE + lea %a15,[%a15]lo:__TESTDEVICE + mov.d %d2, %a14 + st.w [%a15], %d2 # write exit code to testdevice + debug + +/*============================================================================* + * Exception handlers (exceptions in startup code) + * + * This is a minimal trap vector table, which consists of eight + * entries, each consisting of eight words (32 bytes). + *============================================================================*/ + + +#; .section .traptab, "ax", @progbits + +.macro trapentry from=0, to=7 + mov.u %d14, \from << 8 + add %d14,%d14,%d15 + mov.a %a14,%d14 + addih.a %a14,%a14,0 # if we trap, we fail + j _exit +0: + j 0b + nop + rfe + .align 5 + + .if \to-\from + trapentry "(\from+1)",\to + .endif +.endm + + .align 8 + .global first_trap_table +first_trap_table: + trapentry 0, 7 + diff --git a/tests/tcg/tricore/c/test_boot_to_main.c b/tests/tcg/tricore/c/test_boot_to_main.c new file mode 100644 index 0000000000..fa28a5b433 --- /dev/null +++ b/tests/tcg/tricore/c/test_boot_to_main.c @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2023 Bastian Koppelmann + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#include "testdev_assert.h" +int main(int argc, char **argv) +{ + testdev_assert(1); + return 0; +} diff --git a/tests/tcg/tricore/c/test_context_save_areas.c b/tests/tcg/tricore/c/test_context_save_areas.c new file mode 100644 index 0000000000..a300ee2f9c --- /dev/null +++ b/tests/tcg/tricore/c/test_context_save_areas.c @@ -0,0 +1,15 @@ +#include "testdev_assert.h" + +static int fib(int n) +{ + if (n == 1 || n == 2) { + return 1; + } + return fib(n - 2) + fib(n - 1); +} + +int main(int argc, char **argv) +{ + testdev_assert(fib(10) == 55); + return 0; +} diff --git a/tests/tcg/tricore/c/testdev_assert.h b/tests/tcg/tricore/c/testdev_assert.h new file mode 100644 index 0000000000..ccd14f5025 --- /dev/null +++ b/tests/tcg/tricore/c/testdev_assert.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2023 Bastian Koppelmann + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +int *testdev = (int *)0xf0000000; + +#define FAIL 1 +static inline void testdev_assert(int condition) +{ + if (!condition) { + *testdev = FAIL; + asm("debug"); + } +} + diff --git a/tests/tcg/tricore/link.ld b/tests/tcg/tricore/link.ld index 364bcdc00a..acc1758c41 100644 --- a/tests/tcg/tricore/link.ld +++ b/tests/tcg/tricore/link.ld @@ -12,6 +12,7 @@ MEMORY /* * Define the sizes of the user and system stacks. */ +__ISTACK_SIZE = DEFINED (__ISTACK_SIZE) ? __ISTACK_SIZE : 256 ; __USTACK_SIZE = DEFINED (__USTACK_SIZE) ? __USTACK_SIZE : 1K ; /* * Define the start address and the size of the context save area. @@ -20,6 +21,8 @@ __CSA_BEGIN = 0xd0000000 ; __CSA_SIZE = 8k ; __CSA_END = __CSA_BEGIN + __CSA_SIZE ; +__TESTDEVICE = 0xf0000000 ; + SECTIONS { .text : @@ -32,6 +35,18 @@ SECTIONS { *(.rodata) *(.rodata1) + /* + * Create the clear and copy tables that tell the startup code + * which memory areas to clear and to copy, respectively. + */ + . = ALIGN(4) ; + PROVIDE(__clear_table = .) ; + LONG(0 + ADDR(.bss)); LONG(SIZEOF(.bss)); + LONG(-1); LONG(-1); + PROVIDE(__copy_table = .) ; + LONG(LOADADDR(.data)); LONG(0 + ADDR(.data)); LONG(SIZEOF(.data)); + LONG(-1); LONG(-1); LONG(-1); + . = ALIGN(8); } > data_ram .data : @@ -40,6 +55,7 @@ SECTIONS *(.data) *(.data.*) . = ALIGN(8) ; + __ISTACK = . + __ISTACK_SIZE ; __USTACK = . + __USTACK_SIZE -768; } > data_ram diff --git a/tests/tcg/x86_64/Makefile.softmmu-target b/tests/tcg/x86_64/Makefile.softmmu-target index 2afa3298bf..1bd763f2e6 100644 --- a/tests/tcg/x86_64/Makefile.softmmu-target +++ b/tests/tcg/x86_64/Makefile.softmmu-target @@ -33,15 +33,5 @@ EXTRA_RUNS+=$(MULTIARCH_RUNS) memory: CFLAGS+=-DCHECK_UNALIGNED=1 -# non-inline runs will trigger the duplicate instruction heuristics in libinsn.so -run-plugin-%-with-libinsn.so: - $(call run-test, $@, \ - $(QEMU) -monitor none -display none \ - -chardev file$(COMMA)path=$@.out$(COMMA)id=output \ - -plugin ../../plugin/libinsn.so$(COMMA)inline=on \ - -d plugin -D $*-with-libinsn.so.pout \ - $(QEMU_OPTS) $*, \ - "$* on $(TARGET_NAME)") - # Running QEMU_OPTS+=-device isa-debugcon,chardev=output -device isa-debug-exit,iobase=0xf4,iosize=0x4 -kernel diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target index b71a6bcd5e..e64aab1b81 100644 --- a/tests/tcg/x86_64/Makefile.target +++ b/tests/tcg/x86_64/Makefile.target @@ -10,15 +10,22 @@ include $(SRC_PATH)/tests/tcg/i386/Makefile.target ifeq ($(filter %-linux-user, $(TARGET)),$(TARGET)) X86_64_TESTS += vsyscall +X86_64_TESTS += noexec +X86_64_TESTS += cmpxchg +X86_64_TESTS += adox TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64 else TESTS=$(MULTIARCH_TESTS) endif -QEMU_OPTS += -cpu max + +adox: CFLAGS=-O2 + +run-test-i386-ssse3: QEMU_OPTS += -cpu max +run-plugin-test-i386-ssse3-%: QEMU_OPTS += -cpu max test-x86_64: LDFLAGS+=-lm -lc test-x86_64: test-i386.c test-i386.h test-i386-shift.h test-i386-muldiv.h $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) -vsyscall: $(SRC_PATH)/tests/tcg/x86_64/vsyscall.c +%: $(SRC_PATH)/tests/tcg/x86_64/%.c $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) diff --git a/tests/tcg/x86_64/adox.c b/tests/tcg/x86_64/adox.c new file mode 100644 index 0000000000..36be644c8b --- /dev/null +++ b/tests/tcg/x86_64/adox.c @@ -0,0 +1,69 @@ +/* See if ADOX give expected results */ + +#include +#include +#include + +static uint64_t adoxq(bool *c_out, uint64_t a, uint64_t b, bool c) +{ + asm ("addl $0x7fffffff, %k1\n\t" + "adoxq %2, %0\n\t" + "seto %b1" + : "+r"(a), "=&r"(c) : "r"(b), "1"((int)c)); + *c_out = c; + return a; +} + +static uint64_t adoxl(bool *c_out, uint64_t a, uint64_t b, bool c) +{ + asm ("addl $0x7fffffff, %k1\n\t" + "adoxl %k2, %k0\n\t" + "seto %b1" + : "+r"(a), "=&r"(c) : "r"(b), "1"((int)c)); + *c_out = c; + return a; +} + +int main() +{ + uint64_t r; + bool c; + + r = adoxq(&c, 0, 0, 0); + assert(r == 0); + assert(c == 0); + + r = adoxl(&c, 0, 0, 0); + assert(r == 0); + assert(c == 0); + + r = adoxl(&c, 0x100000000, 0, 0); + assert(r == 0); + assert(c == 0); + + r = adoxq(&c, 0, 0, 1); + assert(r == 1); + assert(c == 0); + + r = adoxl(&c, 0, 0, 1); + assert(r == 1); + assert(c == 0); + + r = adoxq(&c, -1, -1, 0); + assert(r == -2); + assert(c == 1); + + r = adoxl(&c, -1, -1, 0); + assert(r == 0xfffffffe); + assert(c == 1); + + r = adoxq(&c, -1, -1, 1); + assert(r == -1); + assert(c == 1); + + r = adoxl(&c, -1, -1, 1); + assert(r == 0xffffffff); + assert(c == 1); + + return 0; +} diff --git a/tests/tcg/x86_64/cmpxchg.c b/tests/tcg/x86_64/cmpxchg.c new file mode 100644 index 0000000000..5891735161 --- /dev/null +++ b/tests/tcg/x86_64/cmpxchg.c @@ -0,0 +1,42 @@ +#include + +static int mem; + +static unsigned long test_cmpxchgb(unsigned long orig) +{ + unsigned long ret; + mem = orig; + asm("cmpxchgb %b[cmp],%[mem]" + : [ mem ] "+m"(mem), [ rax ] "=a"(ret) + : [ cmp ] "r"(0x77), "a"(orig)); + return ret; +} + +static unsigned long test_cmpxchgw(unsigned long orig) +{ + unsigned long ret; + mem = orig; + asm("cmpxchgw %w[cmp],%[mem]" + : [ mem ] "+m"(mem), [ rax ] "=a"(ret) + : [ cmp ] "r"(0x7777), "a"(orig)); + return ret; +} + +static unsigned long test_cmpxchgl(unsigned long orig) +{ + unsigned long ret; + mem = orig; + asm("cmpxchgl %[cmp],%[mem]" + : [ mem ] "+m"(mem), [ rax ] "=a"(ret) + : [ cmp ] "r"(0x77777777u), "a"(orig)); + return ret; +} + +int main() +{ + unsigned long test = 0xdeadbeef12345678ull; + assert(test == test_cmpxchgb(test)); + assert(test == test_cmpxchgw(test)); + assert(test == test_cmpxchgl(test)); + return 0; +} diff --git a/tests/tcg/x86_64/noexec.c b/tests/tcg/x86_64/noexec.c new file mode 100644 index 0000000000..9b124901be --- /dev/null +++ b/tests/tcg/x86_64/noexec.c @@ -0,0 +1,75 @@ +#include "../multiarch/noexec.c.inc" + +static void *arch_mcontext_pc(const mcontext_t *ctx) +{ + return (void *)ctx->gregs[REG_RIP]; +} + +int arch_mcontext_arg(const mcontext_t *ctx) +{ + return ctx->gregs[REG_RDI]; +} + +static void arch_flush(void *p, int len) +{ +} + +extern char noexec_1[]; +extern char noexec_2[]; +extern char noexec_end[]; + +asm("noexec_1:\n" + " movq $1,%rdi\n" /* %rdi is 0 on entry, set 1. */ + "noexec_2:\n" + " movq $2,%rdi\n" /* %rdi is 0/1; set 2. */ + " ret\n" + "noexec_end:"); + +int main(void) +{ + struct noexec_test noexec_tests[] = { + { + .name = "fallthrough", + .test_code = noexec_1, + .test_len = noexec_end - noexec_1, + .page_ofs = noexec_1 - noexec_2, + .entry_ofs = noexec_1 - noexec_2, + .expected_si_ofs = 0, + .expected_pc_ofs = 0, + .expected_arg = 1, + }, + { + .name = "jump", + .test_code = noexec_1, + .test_len = noexec_end - noexec_1, + .page_ofs = noexec_1 - noexec_2, + .entry_ofs = 0, + .expected_si_ofs = 0, + .expected_pc_ofs = 0, + .expected_arg = 0, + }, + { + .name = "fallthrough [cross]", + .test_code = noexec_1, + .test_len = noexec_end - noexec_1, + .page_ofs = noexec_1 - noexec_2 - 2, + .entry_ofs = noexec_1 - noexec_2 - 2, + .expected_si_ofs = 0, + .expected_pc_ofs = -2, + .expected_arg = 1, + }, + { + .name = "jump [cross]", + .test_code = noexec_1, + .test_len = noexec_end - noexec_1, + .page_ofs = noexec_1 - noexec_2 - 2, + .entry_ofs = -2, + .expected_si_ofs = 0, + .expected_pc_ofs = -2, + .expected_arg = 0, + }, + }; + + return test_noexec(noexec_tests, + sizeof(noexec_tests) / sizeof(noexec_tests[0])); +} diff --git a/tests/tcg/x86_64/system/boot.S b/tests/tcg/x86_64/system/boot.S index ed0f638406..7213aec63b 100644 --- a/tests/tcg/x86_64/system/boot.S +++ b/tests/tcg/x86_64/system/boot.S @@ -1,16 +1,16 @@ /* * x86_64 boot and support code * - * Copyright 2019 Linaro + * Copyright 2019, 2024 Linaro * - * This work is licensed under the terms of the GNU GPL, version 3 or later. + * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. * * Unlike the i386 version we instead use Xen's PVHVM booting header * which should drop us automatically into 32 bit mode ready to go. I've * nabbed bits of the Linux kernel setup to achieve this. * - * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-License-Identifier: GPL-2.0-or-later */ .section .head @@ -121,7 +121,7 @@ _start: // Setup stack ASAP movq $stack_end,%rsp - /* don't worry about stack frame, assume everthing is garbage when we return */ + /* don't worry about stack frame, assume everything is garbage when we return */ call main _exit: /* output any non-zero result in eax to isa-debug-exit device */ @@ -195,7 +195,7 @@ idt_1F: .int 0, 0 * * This describes various memory areas (segments) through * segment descriptors. In 32 bit mode each segment each - * segement is associated with segment registers which are + * segment is associated with segment registers which are * implicitly (or explicitly) referenced depending on the * instruction. However in 64 bit mode selectors are flat and * segmented addressing isn't used. diff --git a/tests/tcg/xtensa/Makefile.softmmu-target b/tests/tcg/xtensa/Makefile.softmmu-target index 973e55298e..a29571b367 100644 --- a/tests/tcg/xtensa/Makefile.softmmu-target +++ b/tests/tcg/xtensa/Makefile.softmmu-target @@ -1,8 +1,9 @@ # -# Xtensa softmmu tests +# Xtensa system tests # -ifneq ($(TARGET_BIG_ENDIAN),y) +CORE=dc232b +ifneq ($(shell $(QEMU) -cpu help | grep -w $(CORE)),) XTENSA_SRC = $(SRC_PATH)/tests/tcg/xtensa XTENSA_ALL = $(filter-out $(XTENSA_SRC)/linker.ld.S,$(wildcard $(XTENSA_SRC)/*.S)) @@ -15,7 +16,6 @@ XTENSA_USABLE_TESTS = $(filter-out $(XTENSA_BROKEN_TESTS), $(XTENSA_TESTS)) TESTS += $(XTENSA_USABLE_TESTS) VPATH += $(XTENSA_SRC) -CORE=dc232b QEMU_OPTS+=-M sim -cpu $(CORE) -nographic -semihosting -icount 6 $(EXTFLAGS) -kernel INCLUDE_DIRS = $(SRC_PATH)/target/xtensa/core-$(CORE) @@ -26,6 +26,7 @@ ASFLAGS = -Wa,--no-absolute-literals LDFLAGS = -Tlinker.ld -nostartfiles -nostdlib CRT = crt.o vectors.o +CLEANFILES += linker.ld linker.ld: linker.ld.S $(CC) $(XTENSA_INC) -E -P $< -o $@ @@ -40,3 +41,6 @@ $(XTENSA_USABLE_TESTS): linker.ld macros.inc $(CRT) Makefile.softmmu-target $(CC) $(XTENSA_INC) $(ASFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) $(NOSTDFLAGS) $(CRT) endif + +# We don't currently support the multiarch system tests +undefine MULTIARCH_TESTS diff --git a/tests/tcg/xtensa/test_break.S b/tests/tcg/xtensa/test_break.S index 3aa18b5cec..4c618feb5b 100644 --- a/tests/tcg/xtensa/test_break.S +++ b/tests/tcg/xtensa/test_break.S @@ -129,7 +129,7 @@ test ibreak_remove 4: test_end -test ibreak_priority +test ibreak_break_priority set_vector debug_vector, 2f rsil a2, debug_level - 1 movi a2, 1f @@ -145,6 +145,29 @@ test ibreak_priority movi a3, 0x2 assert eq, a2, a3 test_end + +test ibreak_icount_priority + set_vector debug_vector, 2f + rsil a2, debug_level - 1 + movi a2, 1f + wsr a2, ibreaka0 + movi a2, 1 + wsr a2, ibreakenable + movi a2, -2 + wsr a2, icount + movi a2, 1 + wsr a2, icountlevel + isync + rsil a2, 0 + nop +1: + break 0, 0 + test_fail +2: + rsr a2, debugcause + movi a3, 0x1 + assert eq, a2, a3 +test_end #endif test icount diff --git a/tests/tcg/xtensaeb/Makefile.softmmu-target b/tests/tcg/xtensaeb/Makefile.softmmu-target new file mode 100644 index 0000000000..95d0528c37 --- /dev/null +++ b/tests/tcg/xtensaeb/Makefile.softmmu-target @@ -0,0 +1,5 @@ +# +# Xtensa system tests +# + +include $(SRC_PATH)/tests/tcg/xtensa/Makefile.softmmu-target diff --git a/tests/tsan/blacklist.tsan b/tests/tsan/ignore.tsan similarity index 57% rename from tests/tsan/blacklist.tsan rename to tests/tsan/ignore.tsan index 75e444f5dc..423e482d2f 100644 --- a/tests/tsan/blacklist.tsan +++ b/tests/tsan/ignore.tsan @@ -1,6 +1,6 @@ -# This is an example blacklist. -# To enable use of the blacklist add this to configure: -# "--extra-cflags=-fsanitize-blacklist=/tests/tsan/blacklist.tsan" +# This is an example ignore list. +# To enable use of the ignore list add this to configure: +# "--extra-cflags=-fsanitize-blacklist=/tests/tsan/ignore.tsan" # The eventual goal would be to fix these warnings. # TSan is not happy about setting/getting of dirty bits, diff --git a/tests/tsan/suppressions.tsan b/tests/tsan/suppressions.tsan index 73414b9ebd..b3ef59c27c 100644 --- a/tests/tsan/suppressions.tsan +++ b/tests/tsan/suppressions.tsan @@ -4,10 +4,9 @@ # TSan reports a double lock on RECURSIVE mutexes. # Since the recursive lock is intentional, we choose to ignore it. -mutex:aio_context_acquire mutex:pthread_mutex_lock -# TSan reports a race betwen pthread_mutex_init() and +# TSan reports a race between pthread_mutex_init() and # pthread_mutex_lock(). Since this is outside of QEMU, # we choose to ignore it. race:pthread_mutex_init diff --git a/tests/uefi-test-tools/Makefile b/tests/uefi-test-tools/Makefile index 471f0de981..0c003f2877 100644 --- a/tests/uefi-test-tools/Makefile +++ b/tests/uefi-test-tools/Makefile @@ -87,7 +87,7 @@ Build/%.fat: Build/%.efi .NOTPARALLEL: # In turn, the "build" utility of edk2 BaseTools invokes another "make". -# Although the outer "make" process advertizes its job server to all child +# Although the outer "make" process advertises its job server to all child # processes via MAKEFLAGS in the environment, the outer "make" closes the job # server file descriptors (exposed in MAKEFLAGS) before executing a recipe -- # unless the recipe is recognized as a recursive "make" recipe. Recipes that diff --git a/tests/unit/check-block-qdict.c b/tests/unit/check-block-qdict.c index 5a25825093..751c58e737 100644 --- a/tests/unit/check-block-qdict.c +++ b/tests/unit/check-block-qdict.c @@ -504,7 +504,7 @@ static void qdict_crumple_test_empty(void) src = qdict_new(); dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); - + g_assert(dst); g_assert_cmpint(qdict_size(dst), ==, 0); qobject_unref(src); diff --git a/tests/unit/check-qjson.c b/tests/unit/check-qjson.c index c4e0f851bf..a89293ce51 100644 --- a/tests/unit/check-qjson.c +++ b/tests/unit/check-qjson.c @@ -1486,7 +1486,7 @@ int main(int argc, char **argv) g_test_add_func("/literals/keyword", keyword_literal); g_test_add_func("/literals/interpolation/valid", interpolation_valid); - g_test_add_func("/literals/interpolation/unkown", interpolation_unknown); + g_test_add_func("/literals/interpolation/unknown", interpolation_unknown); g_test_add_func("/literals/interpolation/string", interpolation_string); g_test_add_func("/dicts/simple_dict", simple_dict); diff --git a/tests/unit/crypto-tls-psk-helpers.c b/tests/unit/crypto-tls-psk-helpers.c index 511e08cc9c..c6cc740772 100644 --- a/tests/unit/crypto-tls-psk-helpers.c +++ b/tests/unit/crypto-tls-psk-helpers.c @@ -27,15 +27,14 @@ static void test_tls_psk_init_common(const char *pskfile, const char *user, const char *key) { - FILE *fp; + g_autoptr(GError) gerr = NULL; + g_autofree char *line = g_strdup_printf("%s:%s\n", user, key); - fp = fopen(pskfile, "w"); - if (fp == NULL) { - g_critical("Failed to create pskfile %s: %s", pskfile, strerror(errno)); + g_file_set_contents(pskfile, line, strlen(line), &gerr); + if (gerr != NULL) { + g_critical("Failed to create pskfile %s: %s", pskfile, gerr->message); abort(); } - fprintf(fp, "%s:%s\n", user, key); - fclose(fp); } void test_tls_psk_init(const char *pskfile) diff --git a/tests/unit/io-channel-helpers.c b/tests/unit/io-channel-helpers.c index ff156ed3c4..c0799c21c2 100644 --- a/tests/unit/io-channel-helpers.c +++ b/tests/unit/io-channel-helpers.c @@ -25,7 +25,6 @@ struct QIOChannelTest { QIOChannel *src; QIOChannel *dst; - bool blocking; size_t len; size_t niov; char *input; @@ -42,8 +41,6 @@ static gpointer test_io_thread_writer(gpointer opaque) { QIOChannelTest *data = opaque; - qio_channel_set_blocking(data->src, data->blocking, NULL); - qio_channel_writev_all(data->src, data->inputv, data->niov, @@ -58,8 +55,6 @@ static gpointer test_io_thread_reader(gpointer opaque) { QIOChannelTest *data = opaque; - qio_channel_set_blocking(data->dst, data->blocking, NULL); - qio_channel_readv_all(data->dst, data->outputv, data->niov, @@ -113,7 +108,9 @@ void qio_channel_test_run_threads(QIOChannelTest *test, test->src = src; test->dst = dst; - test->blocking = blocking; + + qio_channel_set_blocking(test->dst, blocking, NULL); + qio_channel_set_blocking(test->src, blocking, NULL); reader = g_thread_new("reader", test_io_thread_reader, diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 287b367ec3..228a21d03c 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -11,6 +11,7 @@ tests = { 'check-qobject': [], 'check-qjson': [], 'check-qlit': [], + 'test-error-report': [], 'test-qobject-output-visitor': [testqapi], 'test-clone-visitor': [testqapi], 'test-qobject-input-visitor': [testqapi], @@ -20,8 +21,9 @@ tests = { 'test-opts-visitor': [testqapi], 'test-visitor-serialization': [testqapi], 'test-bitmap': [], - # all code tested by test-x86-cpuid is inside topology.h - 'test-x86-cpuid': [], + 'test-resv-mem': [], + # all code tested by test-x86-topo is inside topology.h + 'test-x86-topo': [], 'test-cutils': [], 'test-div128': [], 'test-shift128': [], @@ -35,6 +37,7 @@ tests = { 'test-rcu-slist': [], 'test-qdist': [], 'test-qht': [], + 'test-qtree': [], 'test-bitops': [], 'test-bitcnt': [], 'test-qgraph': ['../qtest/libqos/qgraph.c'], @@ -46,7 +49,9 @@ tests = { 'test-uuid': [], 'ptimer-test': ['ptimer-test-stubs.c', meson.project_source_root() / 'hw/core/ptimer.c'], 'test-qapi-util': [], - 'test-smp-parse': [qom, meson.project_source_root() / 'hw/core/machine-smp.c'], + 'test-interval-tree': [], + 'test-xs-node': [qom], + 'test-virtio-dmabuf': [meson.project_source_root() / 'hw/display/virtio-dmabuf.c'], } if have_system or have_tools @@ -55,7 +60,7 @@ if have_system or have_tools } if seccomp.found() - tests += {'test-seccomp': ['../../softmmu/qemu-seccomp.c', seccomp]} + tests += {'test-seccomp': ['../../system/qemu-seccomp.c', seccomp]} endif endif @@ -88,13 +93,14 @@ if have_block 'test-io-channel-file': ['io-channel-helpers.c', io], 'test-io-channel-command': ['io-channel-helpers.c', io], 'test-io-channel-buffer': ['io-channel-helpers.c', io], + 'test-io-channel-null': [io], 'test-crypto-ivgen': [io], 'test-crypto-afsplit': [io], 'test-crypto-block': [io], } if gnutls.found() and \ tasn1.found() and \ - 'CONFIG_POSIX' in config_host + host_os != 'windows' tests += { 'test-crypto-tlscredsx509': ['crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c', tasn1, crypto, gnutls], @@ -109,8 +115,11 @@ if have_block if xts == 'private' tests += {'test-crypto-xts': [crypto, io]} endif - if 'CONFIG_POSIX' in config_host - tests += {'test-image-locking': [testblock]} + if host_os != 'windows' + tests += { + 'test-image-locking': [testblock], + 'test-nested-aio-poll': [testblock], + } endif if config_host_data.get('CONFIG_REPLICATION') tests += {'test-replication': [testblock]} @@ -118,9 +127,6 @@ if have_block if nettle.found() or gcrypt.found() tests += {'test-crypto-pbkdf': [io]} endif - if config_host_data.get('CONFIG_EPOLL_CREATE1') - tests += {'test-fdmon-epoll': [testblock]} - endif endif if have_system @@ -132,6 +138,7 @@ if have_system 'test-util-sockets': ['socket-helpers.c'], 'test-base64': [], 'test-bufferiszero': [], + 'test-smp-parse': [qom, meson.project_source_root() / 'hw/core/machine-smp.c'], 'test-vmstate': [migration, io], 'test-yank': ['socket-helpers.c', qom, io, chardev] } @@ -142,8 +149,8 @@ if have_system # Some tests: test-char, test-qdev-global-props, and test-qga, # are not runnable under TSan due to a known issue. # https://github.com/google/sanitizers/issues/1116 - if 'CONFIG_TSAN' not in config_host - if 'CONFIG_POSIX' in config_host + if not get_option('tsan') + if host_os != 'windows' tests += { 'test-char': ['socket-helpers.c', qom, io, chardev] } @@ -155,7 +162,7 @@ if have_system endif endif -if have_ga and targetos == 'linux' +if have_ga and host_os == 'linux' tests += {'test-qga': ['../qtest/libqmp.c']} test_deps += {'test-qga': qga} endif @@ -165,8 +172,12 @@ test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) slow_tests = { - 'test-crypto-tlscredsx509': 45, - 'test-crypto-tlssession': 45 + 'test-aio-multithread' : 120, + 'test-bufferiszero': 60, + 'test-crypto-block' : 300, + 'test-crypto-tlscredsx509': 90, + 'test-crypto-tlssession': 90, + 'test-replication': 60, } foreach test_name, extra: tests diff --git a/tests/unit/ptimer-test-stubs.c b/tests/unit/ptimer-test-stubs.c index f5e75a96b6..8c9407c560 100644 --- a/tests/unit/ptimer-test-stubs.c +++ b/tests/unit/ptimer-test-stubs.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" #include "migration/vmstate.h" #include "ptimer-test.h" @@ -107,7 +107,8 @@ int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask) return deadline; } -QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name) +QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name, + MemReentrancyGuard *reentrancy_guard) { QEMUBH *bh = g_new(QEMUBH, 1); diff --git a/tests/unit/ptimer-test.c b/tests/unit/ptimer-test.c index a80ef5aff4..04b5f4e3d0 100644 --- a/tests/unit/ptimer-test.c +++ b/tests/unit/ptimer-test.c @@ -798,64 +798,64 @@ static void add_ptimer_tests(uint8_t policy) g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/set_count policy=%s", policy_name), - g_memdup(&policy, 1), check_set_count, g_free); + g_memdup2(&policy, 1), check_set_count, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/set_limit policy=%s", policy_name), - g_memdup(&policy, 1), check_set_limit, g_free); + g_memdup2(&policy, 1), check_set_limit, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/oneshot policy=%s", policy_name), - g_memdup(&policy, 1), check_oneshot, g_free); + g_memdup2(&policy, 1), check_oneshot, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/periodic policy=%s", policy_name), - g_memdup(&policy, 1), check_periodic, g_free); + g_memdup2(&policy, 1), check_periodic, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/on_the_fly_mode_change policy=%s", policy_name), - g_memdup(&policy, 1), check_on_the_fly_mode_change, g_free); + g_memdup2(&policy, 1), check_on_the_fly_mode_change, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/on_the_fly_period_change policy=%s", policy_name), - g_memdup(&policy, 1), check_on_the_fly_period_change, g_free); + g_memdup2(&policy, 1), check_on_the_fly_period_change, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/on_the_fly_freq_change policy=%s", policy_name), - g_memdup(&policy, 1), check_on_the_fly_freq_change, g_free); + g_memdup2(&policy, 1), check_on_the_fly_freq_change, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/run_with_period_0 policy=%s", policy_name), - g_memdup(&policy, 1), check_run_with_period_0, g_free); + g_memdup2(&policy, 1), check_run_with_period_0, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/run_with_delta_0 policy=%s", policy_name), - g_memdup(&policy, 1), check_run_with_delta_0, g_free); + g_memdup2(&policy, 1), check_run_with_delta_0, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/periodic_with_load_0 policy=%s", policy_name), - g_memdup(&policy, 1), check_periodic_with_load_0, g_free); + g_memdup2(&policy, 1), check_periodic_with_load_0, g_free); g_free(tmp); g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/oneshot_with_load_0 policy=%s", policy_name), - g_memdup(&policy, 1), check_oneshot_with_load_0, g_free); + g_memdup2(&policy, 1), check_oneshot_with_load_0, g_free); g_free(tmp); } diff --git a/tests/unit/rcutorture.c b/tests/unit/rcutorture.c index 495a4e6f42..7662081683 100644 --- a/tests/unit/rcutorture.c +++ b/tests/unit/rcutorture.c @@ -50,8 +50,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program. If not, see . * * Copyright (c) 2008 Paul E. McKenney, IBM Corporation. */ diff --git a/tests/unit/socket-helpers.c b/tests/unit/socket-helpers.c index 5af4de513b..f3439cc4e5 100644 --- a/tests/unit/socket-helpers.c +++ b/tests/unit/socket-helpers.c @@ -154,3 +154,21 @@ int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6) return 0; } + +void socket_check_afunix_support(bool *has_afunix) +{ + int fd; + + fd = socket(PF_UNIX, SOCK_STREAM, 0); + +#ifdef _WIN32 + *has_afunix = (fd != (int)INVALID_SOCKET); +#else + *has_afunix = (fd >= 0); +#endif + + if (*has_afunix) { + close(fd); + } + return; +} diff --git a/tests/unit/socket-helpers.h b/tests/unit/socket-helpers.h index 512a004811..ed8477ceb3 100644 --- a/tests/unit/socket-helpers.h +++ b/tests/unit/socket-helpers.h @@ -32,4 +32,13 @@ */ int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6); +/* + * @has_afunix: set to true on return if unix socket support is available + * + * Check whether unix domain socket support is available for use. + * On success, @has_afunix will be set to indicate whether AF_UNIX protocol + * is available. + */ +void socket_check_afunix_support(bool *has_afunix); + #endif diff --git a/tests/unit/test-aio-multithread.c b/tests/unit/test-aio-multithread.c index a555cc8835..08d4570ccb 100644 --- a/tests/unit/test-aio-multithread.c +++ b/tests/unit/test-aio-multithread.c @@ -107,8 +107,7 @@ static void test_lifecycle(void) /* aio_co_schedule test. */ static Coroutine *to_schedule[NUM_CONTEXTS]; - -static bool now_stopping; +static bool stop[NUM_CONTEXTS]; static int count_retry; static int count_here; @@ -136,6 +135,7 @@ static bool schedule_next(int n) static void finish_cb(void *opaque) { + stop[id] = true; schedule_next(id); } @@ -143,13 +143,19 @@ static coroutine_fn void test_multi_co_schedule_entry(void *opaque) { g_assert(to_schedule[id] == NULL); - while (!qatomic_mb_read(&now_stopping)) { + /* + * The next iteration will set to_schedule[id] again, but once finish_cb + * is scheduled there is no guarantee that it will actually be woken up, + * so at that point it must not go to sleep. + */ + while (!stop[id]) { int n; n = g_test_rand_int_range(0, NUM_CONTEXTS); schedule_next(n); - qatomic_mb_set(&to_schedule[id], qemu_coroutine_self()); + qatomic_set_mb(&to_schedule[id], qemu_coroutine_self()); + /* finish_cb can run here. */ qemu_coroutine_yield(); g_assert(to_schedule[id] == NULL); } @@ -161,7 +167,6 @@ static void test_multi_co_schedule(int seconds) int i; count_here = count_other = count_retry = 0; - now_stopping = false; create_aio_contexts(); for (i = 0; i < NUM_CONTEXTS; i++) { @@ -171,10 +176,10 @@ static void test_multi_co_schedule(int seconds) g_usleep(seconds * 1000000); - qatomic_mb_set(&now_stopping, true); + /* Guarantee that each AioContext is woken up from its last wait. */ for (i = 0; i < NUM_CONTEXTS; i++) { ctx_run(i, finish_cb, NULL); - to_schedule[i] = NULL; + g_assert(to_schedule[i] == NULL); } join_aio_contexts(); @@ -199,10 +204,11 @@ static uint32_t atomic_counter; static uint32_t running; static uint32_t counter; static CoMutex comutex; +static bool now_stopping; static void coroutine_fn test_multi_co_mutex_entry(void *opaque) { - while (!qatomic_mb_read(&now_stopping)) { + while (!qatomic_read(&now_stopping)) { qemu_co_mutex_lock(&comutex); counter++; qemu_co_mutex_unlock(&comutex); @@ -236,7 +242,7 @@ static void test_multi_co_mutex(int threads, int seconds) g_usleep(seconds * 1000000); - qatomic_mb_set(&now_stopping, true); + qatomic_set(&now_stopping, true); while (running > 0) { g_usleep(100000); } @@ -327,7 +333,7 @@ static void mcs_mutex_unlock(void) static void test_multi_fair_mutex_entry(void *opaque) { - while (!qatomic_mb_read(&now_stopping)) { + while (!qatomic_read(&now_stopping)) { mcs_mutex_lock(); counter++; mcs_mutex_unlock(); @@ -355,7 +361,7 @@ static void test_multi_fair_mutex(int threads, int seconds) g_usleep(seconds * 1000000); - qatomic_mb_set(&now_stopping, true); + qatomic_set(&now_stopping, true); while (running > 0) { g_usleep(100000); } @@ -383,7 +389,7 @@ static QemuMutex mutex; static void test_multi_mutex_entry(void *opaque) { - while (!qatomic_mb_read(&now_stopping)) { + while (!qatomic_read(&now_stopping)) { qemu_mutex_lock(&mutex); counter++; qemu_mutex_unlock(&mutex); @@ -411,7 +417,7 @@ static void test_multi_mutex(int threads, int seconds) g_usleep(seconds * 1000000); - qatomic_mb_set(&now_stopping, true); + qatomic_set(&now_stopping, true); while (running > 0) { g_usleep(100000); } diff --git a/tests/unit/test-aio.c b/tests/unit/test-aio.c index 178048d2f2..e77d86be87 100644 --- a/tests/unit/test-aio.c +++ b/tests/unit/test-aio.c @@ -16,7 +16,7 @@ #include "qemu/timer.h" #include "qemu/sockets.h" #include "qemu/error-report.h" -#include "qemu/coroutine.h" +#include "qemu/coroutine-core.h" #include "qemu/main-loop.h" static AioContext *ctx; @@ -100,74 +100,10 @@ static void event_ready_cb(EventNotifier *e) /* Tests using aio_*. */ -typedef struct { - QemuMutex start_lock; - EventNotifier notifier; - bool thread_acquired; -} AcquireTestData; - -static void *test_acquire_thread(void *opaque) -{ - AcquireTestData *data = opaque; - - /* Wait for other thread to let us start */ - qemu_mutex_lock(&data->start_lock); - qemu_mutex_unlock(&data->start_lock); - - /* event_notifier_set might be called either before or after - * the main thread's call to poll(). The test case's outcome - * should be the same in either case. - */ - event_notifier_set(&data->notifier); - aio_context_acquire(ctx); - aio_context_release(ctx); - - data->thread_acquired = true; /* success, we got here */ - - return NULL; -} - -static void set_event_notifier(AioContext *ctx, EventNotifier *notifier, +static void set_event_notifier(AioContext *nctx, EventNotifier *notifier, EventNotifierHandler *handler) { - aio_set_event_notifier(ctx, notifier, false, handler, NULL, NULL); -} - -static void dummy_notifier_read(EventNotifier *n) -{ - event_notifier_test_and_clear(n); -} - -static void test_acquire(void) -{ - QemuThread thread; - AcquireTestData data; - - /* Dummy event notifier ensures aio_poll() will block */ - event_notifier_init(&data.notifier, false); - set_event_notifier(ctx, &data.notifier, dummy_notifier_read); - g_assert(!aio_poll(ctx, false)); /* consume aio_notify() */ - - qemu_mutex_init(&data.start_lock); - qemu_mutex_lock(&data.start_lock); - data.thread_acquired = false; - - qemu_thread_create(&thread, "test_acquire_thread", - test_acquire_thread, - &data, QEMU_THREAD_JOINABLE); - - /* Block in aio_poll(), let other thread kick us and acquire context */ - aio_context_acquire(ctx); - qemu_mutex_unlock(&data.start_lock); /* let the thread run */ - g_assert(aio_poll(ctx, true)); - g_assert(!data.thread_acquired); - aio_context_release(ctx); - - qemu_thread_join(&thread); - set_event_notifier(ctx, &data.notifier, NULL); - event_notifier_cleanup(&data.notifier); - - g_assert(data.thread_acquired); + aio_set_event_notifier(nctx, notifier, handler, NULL, NULL); } static void test_bh_schedule(void) @@ -383,30 +319,6 @@ static void test_flush_event_notifier(void) event_notifier_cleanup(&data.e); } -static void test_aio_external_client(void) -{ - int i, j; - - for (i = 1; i < 3; i++) { - EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true }; - event_notifier_init(&data.e, false); - aio_set_event_notifier(ctx, &data.e, true, event_ready_cb, NULL, NULL); - event_notifier_set(&data.e); - for (j = 0; j < i; j++) { - aio_disable_external(ctx); - } - for (j = 0; j < i; j++) { - assert(!aio_poll(ctx, false)); - assert(event_notifier_test_and_clear(&data.e)); - event_notifier_set(&data.e); - aio_enable_external(ctx); - } - assert(aio_poll(ctx, false)); - set_event_notifier(ctx, &data.e, NULL); - event_notifier_cleanup(&data.e); - } -} - static void test_wait_event_notifier_noflush(void) { EventNotifierTestData data = { .n = 0 }; @@ -478,7 +390,7 @@ static void test_timer_schedule(void) g_assert_cmpint(data.n, ==, 0); - /* timer_mod may well cause an event notifer to have gone off, + /* timer_mod may well cause an event notifier to have gone off, * so clear that */ do {} while (aio_poll(ctx, false)); @@ -903,7 +815,7 @@ static void test_worker_thread_co_enter(void) qemu_thread_get_self(&this_thread); co = qemu_coroutine_create(co_check_current_thread, &this_thread); - qemu_thread_create(&worker_thread, "test_acquire_thread", + qemu_thread_create(&worker_thread, "test_aio_co_enter", test_aio_co_enter, co, QEMU_THREAD_JOINABLE); @@ -923,7 +835,6 @@ int main(int argc, char **argv) while (g_main_context_iteration(NULL, false)); g_test_init(&argc, &argv, NULL); - g_test_add_func("/aio/acquire", test_acquire); g_test_add_func("/aio/bh/schedule", test_bh_schedule); g_test_add_func("/aio/bh/schedule10", test_bh_schedule10); g_test_add_func("/aio/bh/cancel", test_bh_cancel); @@ -935,7 +846,6 @@ int main(int argc, char **argv) g_test_add_func("/aio/event/wait", test_wait_event_notifier); g_test_add_func("/aio/event/wait/no-flush-cb", test_wait_event_notifier_noflush); g_test_add_func("/aio/event/flush", test_flush_event_notifier); - g_test_add_func("/aio/external-client", test_aio_external_client); g_test_add_func("/aio/timer/schedule", test_timer_schedule); g_test_add_func("/aio/coroutine/queue-chaining", test_queue_chaining); diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index 36be84ae55..666880472b 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -23,7 +23,7 @@ */ #include "qemu/osdep.h" -#include "block/block.h" +#include "block/block_int.h" #include "block/blockjob_int.h" #include "sysemu/block-backend.h" #include "qapi/error.h" @@ -38,16 +38,26 @@ typedef struct BDRVTestState { bool sleep_in_drain_begin; } BDRVTestState; -static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs) +static void coroutine_fn sleep_in_drain_begin(void *opaque) +{ + BlockDriverState *bs = opaque; + + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); + bdrv_dec_in_flight(bs); +} + +static void bdrv_test_drain_begin(BlockDriverState *bs) { BDRVTestState *s = bs->opaque; s->drain_count++; if (s->sleep_in_drain_begin) { - qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); + Coroutine *co = qemu_coroutine_create(sleep_in_drain_begin, bs); + bdrv_inc_in_flight(bs); + aio_co_enter(bdrv_get_aio_context(bs), co); } } -static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs) +static void bdrv_test_drain_end(BlockDriverState *bs) { BDRVTestState *s = bs->opaque; s->drain_count--; @@ -86,9 +96,9 @@ static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, return 0; } -static int bdrv_test_change_backing_file(BlockDriverState *bs, - const char *backing_file, - const char *backing_fmt) +static int bdrv_test_co_change_backing_file(BlockDriverState *bs, + const char *backing_file, + const char *backing_fmt) { return 0; } @@ -101,12 +111,12 @@ static BlockDriver bdrv_test = { .bdrv_close = bdrv_test_close, .bdrv_co_preadv = bdrv_test_co_preadv, - .bdrv_co_drain_begin = bdrv_test_co_drain_begin, - .bdrv_co_drain_end = bdrv_test_co_drain_end, + .bdrv_drain_begin = bdrv_test_drain_begin, + .bdrv_drain_end = bdrv_test_drain_end, .bdrv_child_perm = bdrv_default_perms, - .bdrv_change_backing_file = bdrv_test_change_backing_file, + .bdrv_co_change_backing_file = bdrv_test_co_change_backing_file, }; static void aio_ret_cb(void *opaque, int ret) @@ -146,7 +156,6 @@ static void call_in_coroutine(void (*entry)(void)) enum drain_type { BDRV_DRAIN_ALL, BDRV_DRAIN, - BDRV_SUBTREE_DRAIN, DRAIN_TYPE_MAX, }; @@ -155,7 +164,6 @@ static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs) switch (drain_type) { case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break; case BDRV_DRAIN: bdrv_drained_begin(bs); break; - case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break; default: g_assert_not_reached(); } } @@ -165,52 +173,58 @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs) switch (drain_type) { case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break; case BDRV_DRAIN: bdrv_drained_end(bs); break; - case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break; default: g_assert_not_reached(); } } static void do_drain_begin_unlocked(enum drain_type drain_type, BlockDriverState *bs) { - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_acquire(bdrv_get_aio_context(bs)); - } do_drain_begin(drain_type, bs); - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_release(bdrv_get_aio_context(bs)); - } +} + +static BlockBackend * no_coroutine_fn test_setup(void) +{ + BlockBackend *blk; + BlockDriverState *bs, *backing; + + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, + &error_abort); + blk_insert_bs(blk, bs, &error_abort); + + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); + bdrv_set_backing_hd(bs, backing, &error_abort); + + bdrv_unref(backing); + bdrv_unref(bs); + + return blk; } static void do_drain_end_unlocked(enum drain_type drain_type, BlockDriverState *bs) { - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_acquire(bdrv_get_aio_context(bs)); - } do_drain_end(drain_type, bs); - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_release(bdrv_get_aio_context(bs)); - } } -static void test_drv_cb_common(enum drain_type drain_type, bool recursive) +/* + * Locking the block graph would be a bit cumbersome here because this function + * is called both in coroutine and non-coroutine context. We know this is a test + * and nothing else is running, so don't bother with TSA. + */ +static void coroutine_mixed_fn TSA_NO_TSA +test_drv_cb_common(BlockBackend *blk, enum drain_type drain_type, + bool recursive) { - BlockBackend *blk; - BlockDriverState *bs, *backing; + BlockDriverState *bs = blk_bs(blk); + BlockDriverState *backing = bs->backing->bs; BDRVTestState *s, *backing_s; BlockAIOCB *acb; int aio_ret; QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0); - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, - &error_abort); s = bs->opaque; - blk_insert_bs(blk, bs, &error_abort); - - backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); backing_s = backing->opaque; - bdrv_set_backing_hd(bs, backing, &error_abort); /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */ g_assert_cmpint(s->drain_count, ==, 0); @@ -245,101 +259,116 @@ static void test_drv_cb_common(enum drain_type drain_type, bool recursive) g_assert_cmpint(s->drain_count, ==, 0); g_assert_cmpint(backing_s->drain_count, ==, 0); - - bdrv_unref(backing); - bdrv_unref(bs); - blk_unref(blk); } static void test_drv_cb_drain_all(void) { - test_drv_cb_common(BDRV_DRAIN_ALL, true); + BlockBackend *blk = test_setup(); + test_drv_cb_common(blk, BDRV_DRAIN_ALL, true); + blk_unref(blk); } static void test_drv_cb_drain(void) { - test_drv_cb_common(BDRV_DRAIN, false); + BlockBackend *blk = test_setup(); + test_drv_cb_common(blk, BDRV_DRAIN, false); + blk_unref(blk); } -static void test_drv_cb_drain_subtree(void) +static void coroutine_fn test_drv_cb_co_drain_all_entry(void) { - test_drv_cb_common(BDRV_SUBTREE_DRAIN, true); + BlockBackend *blk = blk_all_next(NULL); + test_drv_cb_common(blk, BDRV_DRAIN_ALL, true); } static void test_drv_cb_co_drain_all(void) { - call_in_coroutine(test_drv_cb_drain_all); + BlockBackend *blk = test_setup(); + call_in_coroutine(test_drv_cb_co_drain_all_entry); + blk_unref(blk); +} + +static void coroutine_fn test_drv_cb_co_drain_entry(void) +{ + BlockBackend *blk = blk_all_next(NULL); + test_drv_cb_common(blk, BDRV_DRAIN, false); } static void test_drv_cb_co_drain(void) { - call_in_coroutine(test_drv_cb_drain); + BlockBackend *blk = test_setup(); + call_in_coroutine(test_drv_cb_co_drain_entry); + blk_unref(blk); } -static void test_drv_cb_co_drain_subtree(void) +/* + * Locking the block graph would be a bit cumbersome here because this function + * is called both in coroutine and non-coroutine context. We know this is a test + * and nothing else is running, so don't bother with TSA. + */ +static void coroutine_mixed_fn TSA_NO_TSA +test_quiesce_common(BlockBackend *blk, enum drain_type drain_type, + bool recursive) { - call_in_coroutine(test_drv_cb_drain_subtree); -} - -static void test_quiesce_common(enum drain_type drain_type, bool recursive) -{ - BlockBackend *blk; - BlockDriverState *bs, *backing; - - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, - &error_abort); - blk_insert_bs(blk, bs, &error_abort); - - backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); - bdrv_set_backing_hd(bs, backing, &error_abort); + BlockDriverState *bs = blk_bs(blk); + BlockDriverState *backing = bs->backing->bs; g_assert_cmpint(bs->quiesce_counter, ==, 0); g_assert_cmpint(backing->quiesce_counter, ==, 0); do_drain_begin(drain_type, bs); - g_assert_cmpint(bs->quiesce_counter, ==, 1); + if (drain_type == BDRV_DRAIN_ALL) { + g_assert_cmpint(bs->quiesce_counter, ==, 2); + } else { + g_assert_cmpint(bs->quiesce_counter, ==, 1); + } g_assert_cmpint(backing->quiesce_counter, ==, !!recursive); do_drain_end(drain_type, bs); g_assert_cmpint(bs->quiesce_counter, ==, 0); g_assert_cmpint(backing->quiesce_counter, ==, 0); - - bdrv_unref(backing); - bdrv_unref(bs); - blk_unref(blk); } static void test_quiesce_drain_all(void) { - test_quiesce_common(BDRV_DRAIN_ALL, true); + BlockBackend *blk = test_setup(); + test_quiesce_common(blk, BDRV_DRAIN_ALL, true); + blk_unref(blk); } static void test_quiesce_drain(void) { - test_quiesce_common(BDRV_DRAIN, false); + BlockBackend *blk = test_setup(); + test_quiesce_common(blk, BDRV_DRAIN, false); + blk_unref(blk); } -static void test_quiesce_drain_subtree(void) +static void coroutine_fn test_quiesce_co_drain_all_entry(void) { - test_quiesce_common(BDRV_SUBTREE_DRAIN, true); + BlockBackend *blk = blk_all_next(NULL); + test_quiesce_common(blk, BDRV_DRAIN_ALL, true); } static void test_quiesce_co_drain_all(void) { - call_in_coroutine(test_quiesce_drain_all); + BlockBackend *blk = test_setup(); + call_in_coroutine(test_quiesce_co_drain_all_entry); + blk_unref(blk); +} + +static void coroutine_fn test_quiesce_co_drain_entry(void) +{ + BlockBackend *blk = blk_all_next(NULL); + test_quiesce_common(blk, BDRV_DRAIN, false); } static void test_quiesce_co_drain(void) { - call_in_coroutine(test_quiesce_drain); -} - -static void test_quiesce_co_drain_subtree(void) -{ - call_in_coroutine(test_quiesce_drain_subtree); + BlockBackend *blk = test_setup(); + call_in_coroutine(test_quiesce_co_drain_entry); + blk_unref(blk); } static void test_nested(void) @@ -361,8 +390,8 @@ static void test_nested(void) for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) { for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) { - int backing_quiesce = (outer != BDRV_DRAIN) + - (inner != BDRV_DRAIN); + int backing_quiesce = (outer == BDRV_DRAIN_ALL) + + (inner == BDRV_DRAIN_ALL); g_assert_cmpint(bs->quiesce_counter, ==, 0); g_assert_cmpint(backing->quiesce_counter, ==, 0); @@ -372,10 +401,10 @@ static void test_nested(void) do_drain_begin(outer, bs); do_drain_begin(inner, bs); - g_assert_cmpint(bs->quiesce_counter, ==, 2); + g_assert_cmpint(bs->quiesce_counter, ==, 2 + !!backing_quiesce); g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce); - g_assert_cmpint(s->drain_count, ==, 2); - g_assert_cmpint(backing_s->drain_count, ==, backing_quiesce); + g_assert_cmpint(s->drain_count, ==, 1); + g_assert_cmpint(backing_s->drain_count, ==, !!backing_quiesce); do_drain_end(inner, bs); do_drain_end(outer, bs); @@ -392,158 +421,6 @@ static void test_nested(void) blk_unref(blk); } -static void test_multiparent(void) -{ - BlockBackend *blk_a, *blk_b; - BlockDriverState *bs_a, *bs_b, *backing; - BDRVTestState *a_s, *b_s, *backing_s; - - blk_a = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, - &error_abort); - a_s = bs_a->opaque; - blk_insert_bs(blk_a, bs_a, &error_abort); - - blk_b = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, - &error_abort); - b_s = bs_b->opaque; - blk_insert_bs(blk_b, bs_b, &error_abort); - - backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); - backing_s = backing->opaque; - bdrv_set_backing_hd(bs_a, backing, &error_abort); - bdrv_set_backing_hd(bs_b, backing, &error_abort); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 0); - g_assert_cmpint(bs_b->quiesce_counter, ==, 0); - g_assert_cmpint(backing->quiesce_counter, ==, 0); - g_assert_cmpint(a_s->drain_count, ==, 0); - g_assert_cmpint(b_s->drain_count, ==, 0); - g_assert_cmpint(backing_s->drain_count, ==, 0); - - do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 1); - g_assert_cmpint(bs_b->quiesce_counter, ==, 1); - g_assert_cmpint(backing->quiesce_counter, ==, 1); - g_assert_cmpint(a_s->drain_count, ==, 1); - g_assert_cmpint(b_s->drain_count, ==, 1); - g_assert_cmpint(backing_s->drain_count, ==, 1); - - do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 2); - g_assert_cmpint(bs_b->quiesce_counter, ==, 2); - g_assert_cmpint(backing->quiesce_counter, ==, 2); - g_assert_cmpint(a_s->drain_count, ==, 2); - g_assert_cmpint(b_s->drain_count, ==, 2); - g_assert_cmpint(backing_s->drain_count, ==, 2); - - do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 1); - g_assert_cmpint(bs_b->quiesce_counter, ==, 1); - g_assert_cmpint(backing->quiesce_counter, ==, 1); - g_assert_cmpint(a_s->drain_count, ==, 1); - g_assert_cmpint(b_s->drain_count, ==, 1); - g_assert_cmpint(backing_s->drain_count, ==, 1); - - do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 0); - g_assert_cmpint(bs_b->quiesce_counter, ==, 0); - g_assert_cmpint(backing->quiesce_counter, ==, 0); - g_assert_cmpint(a_s->drain_count, ==, 0); - g_assert_cmpint(b_s->drain_count, ==, 0); - g_assert_cmpint(backing_s->drain_count, ==, 0); - - bdrv_unref(backing); - bdrv_unref(bs_a); - bdrv_unref(bs_b); - blk_unref(blk_a); - blk_unref(blk_b); -} - -static void test_graph_change_drain_subtree(void) -{ - BlockBackend *blk_a, *blk_b; - BlockDriverState *bs_a, *bs_b, *backing; - BDRVTestState *a_s, *b_s, *backing_s; - - blk_a = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, - &error_abort); - a_s = bs_a->opaque; - blk_insert_bs(blk_a, bs_a, &error_abort); - - blk_b = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, - &error_abort); - b_s = bs_b->opaque; - blk_insert_bs(blk_b, bs_b, &error_abort); - - backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); - backing_s = backing->opaque; - bdrv_set_backing_hd(bs_a, backing, &error_abort); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 0); - g_assert_cmpint(bs_b->quiesce_counter, ==, 0); - g_assert_cmpint(backing->quiesce_counter, ==, 0); - g_assert_cmpint(a_s->drain_count, ==, 0); - g_assert_cmpint(b_s->drain_count, ==, 0); - g_assert_cmpint(backing_s->drain_count, ==, 0); - - do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); - do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); - do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); - do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); - do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); - - bdrv_set_backing_hd(bs_b, backing, &error_abort); - g_assert_cmpint(bs_a->quiesce_counter, ==, 5); - g_assert_cmpint(bs_b->quiesce_counter, ==, 5); - g_assert_cmpint(backing->quiesce_counter, ==, 5); - g_assert_cmpint(a_s->drain_count, ==, 5); - g_assert_cmpint(b_s->drain_count, ==, 5); - g_assert_cmpint(backing_s->drain_count, ==, 5); - - bdrv_set_backing_hd(bs_b, NULL, &error_abort); - g_assert_cmpint(bs_a->quiesce_counter, ==, 3); - g_assert_cmpint(bs_b->quiesce_counter, ==, 2); - g_assert_cmpint(backing->quiesce_counter, ==, 3); - g_assert_cmpint(a_s->drain_count, ==, 3); - g_assert_cmpint(b_s->drain_count, ==, 2); - g_assert_cmpint(backing_s->drain_count, ==, 3); - - bdrv_set_backing_hd(bs_b, backing, &error_abort); - g_assert_cmpint(bs_a->quiesce_counter, ==, 5); - g_assert_cmpint(bs_b->quiesce_counter, ==, 5); - g_assert_cmpint(backing->quiesce_counter, ==, 5); - g_assert_cmpint(a_s->drain_count, ==, 5); - g_assert_cmpint(b_s->drain_count, ==, 5); - g_assert_cmpint(backing_s->drain_count, ==, 5); - - do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); - do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); - do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); - do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); - do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 0); - g_assert_cmpint(bs_b->quiesce_counter, ==, 0); - g_assert_cmpint(backing->quiesce_counter, ==, 0); - g_assert_cmpint(a_s->drain_count, ==, 0); - g_assert_cmpint(b_s->drain_count, ==, 0); - g_assert_cmpint(backing_s->drain_count, ==, 0); - - bdrv_unref(backing); - bdrv_unref(bs_a); - bdrv_unref(bs_b); - blk_unref(blk_a); - blk_unref(blk_b); -} - static void test_graph_change_drain_all(void) { BlockBackend *blk_a, *blk_b; @@ -596,7 +473,6 @@ static void test_graph_change_drain_all(void) g_assert_cmpint(bs_b->quiesce_counter, ==, 0); g_assert_cmpint(b_s->drain_count, ==, 0); - g_assert_cmpint(qemu_get_aio_context()->external_disable_cnt, ==, 0); bdrv_unref(bs_b); blk_unref(blk_b); @@ -606,19 +482,19 @@ struct test_iothread_data { BlockDriverState *bs; enum drain_type drain_type; int *aio_ret; + bool co_done; }; -static void test_iothread_drain_entry(void *opaque) +static void coroutine_fn test_iothread_drain_co_entry(void *opaque) { struct test_iothread_data *data = opaque; - aio_context_acquire(bdrv_get_aio_context(data->bs)); do_drain_begin(data->drain_type, data->bs); g_assert_cmpint(*data->aio_ret, ==, 0); do_drain_end(data->drain_type, data->bs); - aio_context_release(bdrv_get_aio_context(data->bs)); - qemu_event_set(&done_event); + data->co_done = true; + aio_wait_kick(); } static void test_iothread_aio_cb(void *opaque, int ret) @@ -632,11 +508,8 @@ static void test_iothread_main_thread_bh(void *opaque) { struct test_iothread_data *data = opaque; - /* Test that the AioContext is not yet locked in a random BH that is - * executed during drain, otherwise this would deadlock. */ - aio_context_acquire(bdrv_get_aio_context(data->bs)); bdrv_flush(data->bs); - aio_context_release(bdrv_get_aio_context(data->bs)); + bdrv_dec_in_flight(data->bs); /* incremented by test_iothread_common() */ } /* @@ -654,6 +527,7 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) BlockDriverState *bs; BDRVTestState *s; BlockAIOCB *acb; + Coroutine *co; int aio_ret; struct test_iothread_data data; @@ -677,7 +551,6 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) blk_set_disable_request_queuing(blk, true); blk_set_aio_context(blk, ctx_a, &error_abort); - aio_context_acquire(ctx_a); s->bh_indirection_ctx = ctx_b; @@ -692,8 +565,6 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) g_assert(acb != NULL); g_assert_cmpint(aio_ret, ==, -EINPROGRESS); - aio_context_release(ctx_a); - data = (struct test_iothread_data) { .bs = bs, .drain_type = drain_type, @@ -702,10 +573,13 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) switch (drain_thread) { case 0: - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_acquire(ctx_a); - } - + /* + * Increment in_flight so that do_drain_begin() waits for + * test_iothread_main_thread_bh(). This prevents the race between + * test_iothread_main_thread_bh() in IOThread a and do_drain_begin() in + * this thread. test_iothread_main_thread_bh() decrements in_flight. + */ + bdrv_inc_in_flight(bs); aio_bh_schedule_oneshot(ctx_a, test_iothread_main_thread_bh, &data); /* The request is running on the IOThread a. Draining its block device @@ -716,32 +590,21 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) do_drain_begin(drain_type, bs); g_assert_cmpint(bs->in_flight, ==, 0); - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_release(ctx_a); - } qemu_event_wait(&done_event); - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_acquire(ctx_a); - } g_assert_cmpint(aio_ret, ==, 0); do_drain_end(drain_type, bs); - - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_release(ctx_a); - } break; case 1: - aio_bh_schedule_oneshot(ctx_a, test_iothread_drain_entry, &data); - qemu_event_wait(&done_event); + co = qemu_coroutine_create(test_iothread_drain_co_entry, &data); + aio_co_enter(ctx_a, co); + AIO_WAIT_WHILE_UNLOCKED(NULL, !data.co_done); break; default: g_assert_not_reached(); } - aio_context_acquire(ctx_a); blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); - aio_context_release(ctx_a); bdrv_unref(bs); blk_unref(blk); @@ -763,12 +626,6 @@ static void test_iothread_drain(void) test_iothread_common(BDRV_DRAIN, 1); } -static void test_iothread_drain_subtree(void) -{ - test_iothread_common(BDRV_SUBTREE_DRAIN, 0); - test_iothread_common(BDRV_SUBTREE_DRAIN, 1); -} - typedef struct TestBlockJob { BlockJob common; @@ -853,7 +710,6 @@ enum test_job_result { enum test_job_drain_node { TEST_JOB_DRAIN_SRC, TEST_JOB_DRAIN_SRC_CHILD, - TEST_JOB_DRAIN_SRC_PARENT, }; static void test_blockjob_common_drain_node(enum drain_type drain_type, @@ -866,7 +722,6 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, BlockJob *job; TestBlockJob *tjob; IOThread *iothread = NULL; - AioContext *ctx; int ret; src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR, @@ -891,19 +746,16 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, case TEST_JOB_DRAIN_SRC_CHILD: drain_bs = src_backing; break; - case TEST_JOB_DRAIN_SRC_PARENT: - drain_bs = src_overlay; - break; default: g_assert_not_reached(); } if (use_iothread) { + AioContext *ctx; + iothread = iothread_new(); ctx = iothread_get_aio_context(iothread); blk_set_aio_context(blk_src, ctx, &error_abort); - } else { - ctx = qemu_get_aio_context(); } target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR, @@ -912,13 +764,15 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, blk_insert_bs(blk_target, target, &error_abort); blk_set_allow_aio_context_change(blk_target, true); - aio_context_acquire(ctx); tjob = block_job_create("job0", &test_job_driver, NULL, src, 0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort); tjob->bs = src; job = &tjob->common; + + bdrv_graph_wrlock(); block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); + bdrv_graph_wrunlock(); switch (result) { case TEST_JOB_SUCCESS: @@ -932,7 +786,6 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, } job_start(&job->job); - aio_context_release(ctx); if (use_iothread) { /* job_co_entry() is run in the I/O thread, wait for the actual job @@ -943,68 +796,88 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, } } - g_assert_cmpint(job->job.pause_count, ==, 0); - g_assert_false(job->job.paused); - g_assert_true(tjob->running); - g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ + WITH_JOB_LOCK_GUARD() { + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_true(tjob->running); + g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ + } do_drain_begin_unlocked(drain_type, drain_bs); - if (drain_type == BDRV_DRAIN_ALL) { - /* bdrv_drain_all() drains both src and target */ - g_assert_cmpint(job->job.pause_count, ==, 2); - } else { - g_assert_cmpint(job->job.pause_count, ==, 1); + WITH_JOB_LOCK_GUARD() { + if (drain_type == BDRV_DRAIN_ALL) { + /* bdrv_drain_all() drains both src and target */ + g_assert_cmpint(job->job.pause_count, ==, 2); + } else { + g_assert_cmpint(job->job.pause_count, ==, 1); + } + g_assert_true(job->job.paused); + g_assert_false(job->job.busy); /* The job is paused */ } - g_assert_true(job->job.paused); - g_assert_false(job->job.busy); /* The job is paused */ do_drain_end_unlocked(drain_type, drain_bs); if (use_iothread) { - /* paused is reset in the I/O thread, wait for it */ + /* + * Here we are waiting for the paused status to change, + * so don't bother protecting the read every time. + * + * paused is reset in the I/O thread, wait for it + */ while (job->job.paused) { aio_poll(qemu_get_aio_context(), false); } } - g_assert_cmpint(job->job.pause_count, ==, 0); - g_assert_false(job->job.paused); - g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ + WITH_JOB_LOCK_GUARD() { + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ + } do_drain_begin_unlocked(drain_type, target); - if (drain_type == BDRV_DRAIN_ALL) { - /* bdrv_drain_all() drains both src and target */ - g_assert_cmpint(job->job.pause_count, ==, 2); - } else { - g_assert_cmpint(job->job.pause_count, ==, 1); + WITH_JOB_LOCK_GUARD() { + if (drain_type == BDRV_DRAIN_ALL) { + /* bdrv_drain_all() drains both src and target */ + g_assert_cmpint(job->job.pause_count, ==, 2); + } else { + g_assert_cmpint(job->job.pause_count, ==, 1); + } + g_assert_true(job->job.paused); + g_assert_false(job->job.busy); /* The job is paused */ } - g_assert_true(job->job.paused); - g_assert_false(job->job.busy); /* The job is paused */ do_drain_end_unlocked(drain_type, target); if (use_iothread) { - /* paused is reset in the I/O thread, wait for it */ + /* + * Here we are waiting for the paused status to change, + * so don't bother protecting the read every time. + * + * paused is reset in the I/O thread, wait for it + */ while (job->job.paused) { aio_poll(qemu_get_aio_context(), false); } } - g_assert_cmpint(job->job.pause_count, ==, 0); - g_assert_false(job->job.paused); - g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ + WITH_JOB_LOCK_GUARD() { + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ + } - aio_context_acquire(ctx); - ret = job_complete_sync(&job->job, &error_abort); + WITH_JOB_LOCK_GUARD() { + ret = job_complete_sync_locked(&job->job, &error_abort); + } g_assert_cmpint(ret, ==, (result == TEST_JOB_SUCCESS ? 0 : -EIO)); if (use_iothread) { blk_set_aio_context(blk_src, qemu_get_aio_context(), &error_abort); assert(blk_get_aio_context(blk_target) == qemu_get_aio_context()); } - aio_context_release(ctx); blk_unref(blk_src); blk_unref(blk_target); @@ -1023,10 +896,6 @@ static void test_blockjob_common(enum drain_type drain_type, bool use_iothread, TEST_JOB_DRAIN_SRC); test_blockjob_common_drain_node(drain_type, use_iothread, result, TEST_JOB_DRAIN_SRC_CHILD); - if (drain_type == BDRV_SUBTREE_DRAIN) { - test_blockjob_common_drain_node(drain_type, use_iothread, result, - TEST_JOB_DRAIN_SRC_PARENT); - } } static void test_blockjob_drain_all(void) @@ -1039,11 +908,6 @@ static void test_blockjob_drain(void) test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_SUCCESS); } -static void test_blockjob_drain_subtree(void) -{ - test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_SUCCESS); -} - static void test_blockjob_error_drain_all(void) { test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_FAIL_RUN); @@ -1056,12 +920,6 @@ static void test_blockjob_error_drain(void) test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_FAIL_PREPARE); } -static void test_blockjob_error_drain_subtree(void) -{ - test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_FAIL_RUN); - test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_FAIL_PREPARE); -} - static void test_blockjob_iothread_drain_all(void) { test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_SUCCESS); @@ -1072,11 +930,6 @@ static void test_blockjob_iothread_drain(void) test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_SUCCESS); } -static void test_blockjob_iothread_drain_subtree(void) -{ - test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_SUCCESS); -} - static void test_blockjob_iothread_error_drain_all(void) { test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_FAIL_RUN); @@ -1089,12 +942,6 @@ static void test_blockjob_iothread_error_drain(void) test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_FAIL_PREPARE); } -static void test_blockjob_iothread_error_drain_subtree(void) -{ - test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_FAIL_RUN); - test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_FAIL_PREPARE); -} - typedef struct BDRVTestTopState { BdrvChild *wait_child; @@ -1103,15 +950,17 @@ typedef struct BDRVTestTopState { static void bdrv_test_top_close(BlockDriverState *bs) { BdrvChild *c, *next_c; + + bdrv_graph_wrlock(); QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { bdrv_unref_child(bs, c); } + bdrv_graph_wrunlock(); } -static int coroutine_fn bdrv_test_top_co_preadv(BlockDriverState *bs, - int64_t offset, int64_t bytes, - QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +bdrv_test_top_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVTestTopState *tts = bs->opaque; return bdrv_co_preadv(tts->wait_child, offset, bytes, qiov, flags); @@ -1150,17 +999,23 @@ static void coroutine_fn test_co_delete_by_drain(void *opaque) * everything will be drained before we go back down the tree, but * we do not want that. We want to be in the middle of draining * when this following requests returns. */ + bdrv_graph_co_rdlock(); bdrv_co_preadv(tts->wait_child, 0, 65536, &qiov, 0); + bdrv_graph_co_rdunlock(); g_assert_cmpint(bs->refcnt, ==, 1); if (!dbdd->detach_instead_of_delete) { - blk_unref(blk); + blk_co_unref(blk); } else { BdrvChild *c, *next_c; + bdrv_graph_co_rdlock(); QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { - bdrv_unref_child(bs, c); + bdrv_graph_co_rdunlock(); + bdrv_co_unref_child(bs, c); + bdrv_graph_co_rdlock(); } + bdrv_graph_co_rdunlock(); } dbdd->done = true; @@ -1190,8 +1045,10 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, &error_abort); + bdrv_graph_wrlock(); bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, &error_abort); + bdrv_graph_wrunlock(); /* This child will be the one to pass to requests through to, and * it will stall until a drain occurs */ @@ -1199,17 +1056,21 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, &error_abort); child_bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; /* Takes our reference to child_bs */ + bdrv_graph_wrlock(); tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child", &child_of_bds, BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY, &error_abort); + bdrv_graph_wrunlock(); /* This child is just there to be deleted * (for detach_instead_of_delete == true) */ null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, &error_abort); + bdrv_graph_wrlock(); bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, &error_abort); + bdrv_graph_wrunlock(); blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); blk_insert_bs(blk, bs, &error_abort); @@ -1241,14 +1102,6 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, bdrv_drain(child_bs); bdrv_unref(child_bs); break; - case BDRV_SUBTREE_DRAIN: - /* Would have to ref/unref bs here for !detach_instead_of_delete, but - * then the whole test becomes pointless because the graph changes - * don't occur during the drain any more. */ - assert(detach_instead_of_delete); - bdrv_subtree_drained_begin(bs); - bdrv_subtree_drained_end(bs); - break; case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); bdrv_drain_all_end(); @@ -1283,11 +1136,6 @@ static void test_detach_by_drain(void) do_test_delete_by_drain(true, BDRV_DRAIN); } -static void test_detach_by_drain_subtree(void) -{ - do_test_delete_by_drain(true, BDRV_SUBTREE_DRAIN); -} - struct detach_by_parent_data { BlockDriverState *parent_b; @@ -1295,33 +1143,48 @@ struct detach_by_parent_data { BlockDriverState *c; BdrvChild *child_c; bool by_parent_cb; + bool detach_on_drain; }; static struct detach_by_parent_data detach_by_parent_data; -static void detach_indirect_bh(void *opaque) +static void no_coroutine_fn detach_indirect_bh(void *opaque) { struct detach_by_parent_data *data = opaque; + bdrv_dec_in_flight(data->child_b->bs); + + bdrv_graph_wrlock(); bdrv_unref_child(data->parent_b, data->child_b); bdrv_ref(data->c); data->child_c = bdrv_attach_child(data->parent_b, data->c, "PB-C", &child_of_bds, BDRV_CHILD_DATA, &error_abort); + bdrv_graph_wrunlock(); } -static void detach_by_parent_aio_cb(void *opaque, int ret) +static void coroutine_mixed_fn detach_by_parent_aio_cb(void *opaque, int ret) { struct detach_by_parent_data *data = &detach_by_parent_data; g_assert_cmpint(ret, ==, 0); if (data->by_parent_cb) { - detach_indirect_bh(data); + bdrv_inc_in_flight(data->child_b->bs); + aio_bh_schedule_oneshot(qemu_get_current_aio_context(), + detach_indirect_bh, &detach_by_parent_data); } } -static void detach_by_driver_cb_drained_begin(BdrvChild *child) +static void GRAPH_RDLOCK detach_by_driver_cb_drained_begin(BdrvChild *child) { + struct detach_by_parent_data *data = &detach_by_parent_data; + + if (!data->detach_on_drain) { + return; + } + data->detach_on_drain = false; + + bdrv_inc_in_flight(data->child_b->bs); aio_bh_schedule_oneshot(qemu_get_current_aio_context(), detach_indirect_bh, &detach_by_parent_data); child_of_bds.drained_begin(child); @@ -1349,7 +1212,7 @@ static BdrvChildClass detach_by_driver_cb_class; * state is messed up, but if it is only polled in the single * BDRV_POLL_WHILE() at the end of the drain, this should work fine. */ -static void test_detach_indirect(bool by_parent_cb) +static void TSA_NO_TSA test_detach_indirect(bool by_parent_cb) { BlockBackend *blk; BlockDriverState *parent_a, *parent_b, *a, *b, *c; @@ -1362,8 +1225,14 @@ static void test_detach_indirect(bool by_parent_cb) detach_by_driver_cb_class = child_of_bds; detach_by_driver_cb_class.drained_begin = detach_by_driver_cb_drained_begin; + detach_by_driver_cb_class.drained_end = NULL; + detach_by_driver_cb_class.drained_poll = NULL; } + detach_by_parent_data = (struct detach_by_parent_data) { + .detach_on_drain = false, + }; + /* Create all involved nodes */ parent_a = bdrv_new_open_driver(&bdrv_test, "parent-a", BDRV_O_RDWR, &error_abort); @@ -1389,6 +1258,7 @@ static void test_detach_indirect(bool by_parent_cb) /* Set child relationships */ bdrv_ref(b); bdrv_ref(a); + bdrv_graph_wrlock(); child_b = bdrv_attach_child(parent_b, b, "PB-B", &child_of_bds, BDRV_CHILD_DATA, &error_abort); child_a = bdrv_attach_child(parent_b, a, "PB-A", &child_of_bds, @@ -1398,6 +1268,7 @@ static void test_detach_indirect(bool by_parent_cb) bdrv_attach_child(parent_a, a, "PA-A", by_parent_cb ? &child_of_bds : &detach_by_driver_cb_class, BDRV_CHILD_DATA, &error_abort); + bdrv_graph_wrunlock(); g_assert_cmpint(parent_a->refcnt, ==, 1); g_assert_cmpint(parent_b->refcnt, ==, 1); @@ -1415,12 +1286,16 @@ static void test_detach_indirect(bool by_parent_cb) .child_b = child_b, .c = c, .by_parent_cb = by_parent_cb, + .detach_on_drain = true, }; acb = blk_aio_preadv(blk, 0, &qiov, 0, detach_by_parent_aio_cb, NULL); g_assert(acb != NULL); /* Drain and check the expected result */ - bdrv_subtree_drained_begin(parent_b); + bdrv_drained_begin(parent_b); + bdrv_drained_begin(a); + bdrv_drained_begin(b); + bdrv_drained_begin(c); g_assert(detach_by_parent_data.child_c != NULL); @@ -1435,12 +1310,15 @@ static void test_detach_indirect(bool by_parent_cb) g_assert(QLIST_NEXT(child_a, next) == NULL); g_assert_cmpint(parent_a->quiesce_counter, ==, 1); - g_assert_cmpint(parent_b->quiesce_counter, ==, 1); + g_assert_cmpint(parent_b->quiesce_counter, ==, 3); g_assert_cmpint(a->quiesce_counter, ==, 1); - g_assert_cmpint(b->quiesce_counter, ==, 0); + g_assert_cmpint(b->quiesce_counter, ==, 1); g_assert_cmpint(c->quiesce_counter, ==, 1); - bdrv_subtree_drained_end(parent_b); + bdrv_drained_end(parent_b); + bdrv_drained_end(a); + bdrv_drained_end(b); + bdrv_drained_end(c); bdrv_unref(parent_b); blk_unref(blk); @@ -1484,6 +1362,7 @@ static void test_append_to_drained(void) g_assert_cmpint(base->in_flight, ==, 0); bdrv_append(overlay, base, &error_abort); + g_assert_cmpint(base->in_flight, ==, 0); g_assert_cmpint(overlay->in_flight, ==, 0); @@ -1516,17 +1395,12 @@ static void test_set_aio_context(void) &error_abort); bdrv_drained_begin(bs); - bdrv_try_set_aio_context(bs, ctx_a, &error_abort); - - aio_context_acquire(ctx_a); + bdrv_try_change_aio_context(bs, ctx_a, NULL, &error_abort); bdrv_drained_end(bs); bdrv_drained_begin(bs); - bdrv_try_set_aio_context(bs, ctx_b, &error_abort); - aio_context_release(ctx_a); - aio_context_acquire(ctx_b); - bdrv_try_set_aio_context(bs, qemu_get_aio_context(), &error_abort); - aio_context_release(ctx_b); + bdrv_try_change_aio_context(bs, ctx_b, NULL, &error_abort); + bdrv_try_change_aio_context(bs, qemu_get_aio_context(), NULL, &error_abort); bdrv_drained_end(bs); bdrv_unref(bs); @@ -1671,6 +1545,7 @@ static void test_blockjob_commit_by_drained_end(void) bdrv_drained_begin(bs_child); g_assert(!job_has_completed); bdrv_drained_end(bs_child); + aio_poll(qemu_get_aio_context(), false); g_assert(job_has_completed); bdrv_unref(bs_parents[0]); @@ -1716,6 +1591,7 @@ static const BlockJobDriver test_simple_job_driver = { static int drop_intermediate_poll_update_filename(BdrvChild *child, BlockDriverState *new_base, const char *filename, + bool backing_mask_protocol, Error **errp) { /* @@ -1805,14 +1681,15 @@ static void test_drop_intermediate_poll(void) * Establish the chain last, so the chain links are the first * elements in the BDS.parents lists */ + bdrv_graph_wrlock(); for (i = 0; i < 3; i++) { if (i) { /* Takes the reference to chain[i - 1] */ - chain[i]->backing = bdrv_attach_child(chain[i], chain[i - 1], - "chain", &chain_child_class, - BDRV_CHILD_COW, &error_abort); + bdrv_attach_child(chain[i], chain[i - 1], "chain", + &chain_child_class, BDRV_CHILD_COW, &error_abort); } } + bdrv_graph_wrunlock(); job = block_job_create("job", &test_simple_job_driver, NULL, job_node, 0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort); @@ -1826,7 +1703,8 @@ static void test_drop_intermediate_poll(void) job->should_complete = true; g_assert(!job_has_completed); - ret = bdrv_drop_intermediate(chain[1], chain[0], NULL); + ret = bdrv_drop_intermediate(chain[1], chain[0], NULL, false); + aio_poll(qemu_get_aio_context(), false); g_assert(ret == 0); g_assert(job_has_completed); @@ -1835,6 +1713,7 @@ static void test_drop_intermediate_poll(void) typedef struct BDRVReplaceTestState { + bool setup_completed; bool was_drained; bool was_undrained; bool has_read; @@ -1860,11 +1739,9 @@ static void bdrv_replace_test_close(BlockDriverState *bs) * Otherwise: * Set .has_read to true and return success. */ -static int coroutine_fn bdrv_replace_test_co_preadv(BlockDriverState *bs, - int64_t offset, - int64_t bytes, - QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +bdrv_replace_test_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { BDRVReplaceTestState *s = bs->opaque; @@ -1895,52 +1772,83 @@ static int coroutine_fn bdrv_replace_test_co_preadv(BlockDriverState *bs, return 0; } +static void coroutine_fn bdrv_replace_test_drain_co(void *opaque) +{ + BlockDriverState *bs = opaque; + BDRVReplaceTestState *s = bs->opaque; + + /* Keep waking io_co up until it is done */ + while (s->io_co) { + aio_co_wake(s->io_co); + s->io_co = NULL; + qemu_coroutine_yield(); + } + s->drain_co = NULL; + bdrv_dec_in_flight(bs); +} + /** * If .drain_count is 0, wake up .io_co if there is one; and set * .was_drained. * Increment .drain_count. */ -static void coroutine_fn bdrv_replace_test_co_drain_begin(BlockDriverState *bs) +static void bdrv_replace_test_drain_begin(BlockDriverState *bs) { BDRVReplaceTestState *s = bs->opaque; - if (!s->drain_count) { - /* Keep waking io_co up until it is done */ - s->drain_co = qemu_coroutine_self(); - while (s->io_co) { - aio_co_wake(s->io_co); - s->io_co = NULL; - qemu_coroutine_yield(); - } - s->drain_co = NULL; + if (!s->setup_completed) { + return; + } + if (!s->drain_count) { + s->drain_co = qemu_coroutine_create(bdrv_replace_test_drain_co, bs); + bdrv_inc_in_flight(bs); + aio_co_enter(bdrv_get_aio_context(bs), s->drain_co); s->was_drained = true; } s->drain_count++; } +static void coroutine_fn bdrv_replace_test_read_entry(void *opaque) +{ + BlockDriverState *bs = opaque; + char data; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, &data, 1); + int ret; + + /* Queue a read request post-drain */ + bdrv_graph_co_rdlock(); + ret = bdrv_replace_test_co_preadv(bs, 0, 1, &qiov, 0); + bdrv_graph_co_rdunlock(); + + g_assert(ret >= 0); + bdrv_dec_in_flight(bs); +} + /** * Reduce .drain_count, set .was_undrained once it reaches 0. * If .drain_count reaches 0 and the node has a backing file, issue a * read request. */ -static void coroutine_fn bdrv_replace_test_co_drain_end(BlockDriverState *bs) +static void bdrv_replace_test_drain_end(BlockDriverState *bs) { BDRVReplaceTestState *s = bs->opaque; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + if (!s->setup_completed) { + return; + } + g_assert(s->drain_count > 0); if (!--s->drain_count) { - int ret; - s->was_undrained = true; if (bs->backing) { - char data; - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, &data, 1); - - /* Queue a read request post-drain */ - ret = bdrv_replace_test_co_preadv(bs, 0, 1, &qiov, 0); - g_assert(ret >= 0); + Coroutine *co = qemu_coroutine_create(bdrv_replace_test_read_entry, + bs); + bdrv_inc_in_flight(bs); + aio_co_enter(bdrv_get_aio_context(bs), co); } } } @@ -1948,12 +1856,13 @@ static void coroutine_fn bdrv_replace_test_co_drain_end(BlockDriverState *bs) static BlockDriver bdrv_replace_test = { .format_name = "replace_test", .instance_size = sizeof(BDRVReplaceTestState), + .supports_backing = true, .bdrv_close = bdrv_replace_test_close, .bdrv_co_preadv = bdrv_replace_test_co_preadv, - .bdrv_co_drain_begin = bdrv_replace_test_co_drain_begin, - .bdrv_co_drain_end = bdrv_replace_test_co_drain_end, + .bdrv_drain_begin = bdrv_replace_test_drain_begin, + .bdrv_drain_end = bdrv_replace_test_drain_end, .bdrv_child_perm = bdrv_default_perms, }; @@ -2027,9 +1936,11 @@ static void do_test_replace_child_mid_drain(int old_drain_count, new_child_bs->total_sectors = 1; bdrv_ref(old_child_bs); - parent_bs->backing = bdrv_attach_child(parent_bs, old_child_bs, "child", - &child_of_bds, BDRV_CHILD_COW, - &error_abort); + bdrv_graph_wrlock(); + bdrv_attach_child(parent_bs, old_child_bs, "child", &child_of_bds, + BDRV_CHILD_COW, &error_abort); + bdrv_graph_wrunlock(); + parent_s->setup_completed = true; for (i = 0; i < old_drain_count; i++) { bdrv_drained_begin(old_child_bs); @@ -2057,7 +1968,13 @@ static void do_test_replace_child_mid_drain(int old_drain_count, parent_s->was_undrained = false; g_assert(parent_bs->quiesce_counter == old_drain_count); + bdrv_drained_begin(old_child_bs); + bdrv_drained_begin(new_child_bs); + bdrv_graph_wrlock(); bdrv_replace_node(old_child_bs, new_child_bs, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drained_end(new_child_bs); + bdrv_drained_end(old_child_bs); g_assert(parent_bs->quiesce_counter == new_drain_count); if (!old_drain_count && !new_drain_count) { @@ -2151,70 +2068,47 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain); - g_test_add_func("/bdrv-drain/driver-cb/drain_subtree", - test_drv_cb_drain_subtree); g_test_add_func("/bdrv-drain/driver-cb/co/drain_all", test_drv_cb_co_drain_all); g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain); - g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree", - test_drv_cb_co_drain_subtree); - g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all); g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain); - g_test_add_func("/bdrv-drain/quiesce/drain_subtree", - test_quiesce_drain_subtree); g_test_add_func("/bdrv-drain/quiesce/co/drain_all", test_quiesce_co_drain_all); g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain); - g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree", - test_quiesce_co_drain_subtree); g_test_add_func("/bdrv-drain/nested", test_nested); - g_test_add_func("/bdrv-drain/multiparent", test_multiparent); - g_test_add_func("/bdrv-drain/graph-change/drain_subtree", - test_graph_change_drain_subtree); g_test_add_func("/bdrv-drain/graph-change/drain_all", test_graph_change_drain_all); g_test_add_func("/bdrv-drain/iothread/drain_all", test_iothread_drain_all); g_test_add_func("/bdrv-drain/iothread/drain", test_iothread_drain); - g_test_add_func("/bdrv-drain/iothread/drain_subtree", - test_iothread_drain_subtree); g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); - g_test_add_func("/bdrv-drain/blockjob/drain_subtree", - test_blockjob_drain_subtree); g_test_add_func("/bdrv-drain/blockjob/error/drain_all", test_blockjob_error_drain_all); g_test_add_func("/bdrv-drain/blockjob/error/drain", test_blockjob_error_drain); - g_test_add_func("/bdrv-drain/blockjob/error/drain_subtree", - test_blockjob_error_drain_subtree); g_test_add_func("/bdrv-drain/blockjob/iothread/drain_all", test_blockjob_iothread_drain_all); g_test_add_func("/bdrv-drain/blockjob/iothread/drain", test_blockjob_iothread_drain); - g_test_add_func("/bdrv-drain/blockjob/iothread/drain_subtree", - test_blockjob_iothread_drain_subtree); g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain_all", test_blockjob_iothread_error_drain_all); g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain", test_blockjob_iothread_error_drain); - g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain_subtree", - test_blockjob_iothread_error_drain_subtree); g_test_add_func("/bdrv-drain/deletion/drain", test_delete_by_drain); g_test_add_func("/bdrv-drain/detach/drain_all", test_detach_by_drain_all); g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); - g_test_add_func("/bdrv-drain/detach/drain_subtree", test_detach_by_drain_subtree); g_test_add_func("/bdrv-drain/detach/parent_cb", test_detach_by_parent_cb); g_test_add_func("/bdrv-drain/detach/driver_cb", test_detach_by_driver_cb); diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c index a6e3bb79be..cafc023db4 100644 --- a/tests/unit/test-bdrv-graph-mod.c +++ b/tests/unit/test-bdrv-graph-mod.c @@ -26,6 +26,8 @@ static BlockDriver bdrv_pass_through = { .format_name = "pass-through", + .is_filter = true, + .filtered_child_is_backing = true, .bdrv_child_perm = bdrv_default_perms, }; @@ -57,6 +59,8 @@ static void exclusive_write_perms(BlockDriverState *bs, BdrvChild *c, static BlockDriver bdrv_exclusive_writer = { .format_name = "exclusive-writer", + .is_filter = true, + .filtered_child_is_backing = true, .bdrv_child_perm = exclusive_write_perms, }; @@ -94,9 +98,9 @@ static BlockDriverState *exclusive_writer_node(const char *name) * | perm: write, read * | shared: except write * v - * +-------------------+ +----------------+ - * | passtrough filter |---------->| null-co node | - * +-------------------+ +----------------+ + * +--------------------+ +----------------+ + * | passthrough filter |--------->| null-co node | + * +--------------------+ +----------------+ * * * and then, tries to append filter under node. Expected behavior: fail. @@ -110,9 +114,9 @@ static BlockDriverState *exclusive_writer_node(const char *name) * | perm: write, read * | shared: except write * v - * +-------------------+ - * | passtrough filter | - * +-------------------+ + * +--------------------+ + * | passthrough filter | + * +--------------------+ * | | * perm: write, read | | perm: write, read * shared: except write | | shared: except write @@ -133,8 +137,10 @@ static void test_update_perm_tree(void) blk_insert_bs(root, bs, &error_abort); + bdrv_graph_wrlock(); bdrv_attach_child(filter, bs, "child", &child_of_bds, - BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); + BDRV_CHILD_DATA, &error_abort); + bdrv_graph_wrunlock(); ret = bdrv_append(filter, bs, NULL); g_assert_cmpint(ret, <, 0); @@ -198,11 +204,16 @@ static void test_should_update_child(void) bdrv_set_backing_hd(target, bs, &error_abort); + bdrv_graph_wrlock(); g_assert(target->backing->bs == bs); bdrv_attach_child(filter, target, "target", &child_of_bds, BDRV_CHILD_DATA, &error_abort); + bdrv_graph_wrunlock(); bdrv_append(filter, bs, &error_abort); + + bdrv_graph_rdlock_main_loop(); g_assert(target->backing->bs == bs); + bdrv_graph_rdunlock_main_loop(); bdrv_unref(filter); bdrv_unref(bs); @@ -222,32 +233,59 @@ static void test_parallel_exclusive_write(void) BlockDriverState *fl1 = pass_through_node("fl1"); BlockDriverState *fl2 = pass_through_node("fl2"); + bdrv_drained_begin(fl1); + bdrv_drained_begin(fl2); + /* * bdrv_attach_child() eats child bs reference, so we need two @base - * references for two filters: + * references for two filters. We also need an additional @fl1 reference so + * that it still exists when we want to undrain it. */ bdrv_ref(base); + bdrv_ref(fl1); - bdrv_attach_child(top, fl1, "backing", &child_of_bds, BDRV_CHILD_DATA, + bdrv_graph_wrlock(); + bdrv_attach_child(top, fl1, "backing", &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); - bdrv_attach_child(fl1, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED, + bdrv_attach_child(fl1, base, "backing", &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); - bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED, + bdrv_attach_child(fl2, base, "backing", &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); bdrv_replace_node(fl1, fl2, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drained_end(fl2); + bdrv_drained_end(fl1); + + bdrv_unref(fl1); bdrv_unref(fl2); bdrv_unref(top); } -static void write_to_file_perms(BlockDriverState *bs, BdrvChild *c, - BdrvChildRole role, - BlockReopenQueue *reopen_queue, - uint64_t perm, uint64_t shared, - uint64_t *nperm, uint64_t *nshared) +/* + * write-to-selected node may have several DATA children, one of them may be + * "selected". Exclusive write permission is taken on selected child. + * + * We don't realize write handler itself, as we need only to test how permission + * update works. + */ +typedef struct BDRVWriteToSelectedState { + BdrvChild *selected; +} BDRVWriteToSelectedState; + +static void write_to_selected_perms(BlockDriverState *bs, BdrvChild *c, + BdrvChildRole role, + BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared) { - if (bs->file && c == bs->file) { + BDRVWriteToSelectedState *s = bs->opaque; + + if (s->selected && c == s->selected) { *nperm = BLK_PERM_WRITE; *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE; } else { @@ -256,9 +294,10 @@ static void write_to_file_perms(BlockDriverState *bs, BdrvChild *c, } } -static BlockDriver bdrv_write_to_file = { - .format_name = "tricky-perm", - .bdrv_child_perm = write_to_file_perms, +static BlockDriver bdrv_write_to_selected = { + .format_name = "write-to-selected", + .instance_size = sizeof(BDRVWriteToSelectedState), + .bdrv_child_perm = write_to_selected_perms, }; @@ -266,15 +305,18 @@ static BlockDriver bdrv_write_to_file = { * The following test shows that topological-sort order is required for * permission update, simple DFS is not enough. * - * Consider the block driver which has two filter children: one active - * with exclusive write access and one inactive with no specific - * permissions. + * Consider the block driver (write-to-selected) which has two children: one is + * selected so we have exclusive write access to it and for the other one we + * don't need any specific permissions. * * And, these two children has a common base child, like this: + * (additional "top" on top is used in test just because the only public + * function to update permission should get a specific child to update. + * Making bdrv_refresh_perms() public just for this test isn't worth it) * - * ┌─────┐ ┌──────┐ - * │ fl2 │ ◀── │ top │ - * └─────┘ └──────┘ + * ┌─────┐ ┌───────────────────┐ ┌─────┐ + * │ fl2 │ ◀── │ write-to-selected │ ◀── │ top │ + * └─────┘ └───────────────────┘ └─────┘ * │ │ * │ │ w * │ ▼ @@ -290,14 +332,14 @@ static BlockDriver bdrv_write_to_file = { * * So, exclusive write is propagated. * - * Assume, we want to make fl2 active instead of fl1. - * So, we set some option for top driver and do permission update. + * Assume, we want to select fl2 instead of fl1. + * So, we set some option for write-to-selected driver and do permission update. * * With simple DFS, if permission update goes first through - * top->fl1->base branch it will succeed: it firstly drop exclusive write - * permissions and than apply them for another BdrvChildren. - * But if permission update goes first through top->fl2->base branch it - * will fail, as when we try to update fl2->base child, old not yet + * write-to-selected -> fl1 -> base branch it will succeed: it firstly drop + * exclusive write permissions and than apply them for another BdrvChildren. + * But if permission update goes first through write-to-selected -> fl2 -> base + * branch it will fail, as when we try to update fl2->base child, old not yet * updated fl1->base child will be in conflict. * * With topological-sort order we always update parents before children, so fl1 @@ -306,9 +348,10 @@ static BlockDriver bdrv_write_to_file = { static void test_parallel_perm_update(void) { BlockDriverState *top = no_perm_node("top"); - BlockDriverState *tricky = - bdrv_new_open_driver(&bdrv_write_to_file, "tricky", BDRV_O_RDWR, + BlockDriverState *ws = + bdrv_new_open_driver(&bdrv_write_to_selected, "ws", BDRV_O_RDWR, &error_abort); + BDRVWriteToSelectedState *s = ws->opaque; BlockDriverState *base = no_perm_node("base"); BlockDriverState *fl1 = pass_through_node("fl1"); BlockDriverState *fl2 = pass_through_node("fl2"); @@ -320,38 +363,46 @@ static void test_parallel_perm_update(void) */ bdrv_ref(base); - bdrv_attach_child(top, tricky, "file", &child_of_bds, BDRV_CHILD_DATA, + bdrv_graph_wrlock(); + bdrv_attach_child(top, ws, "file", &child_of_bds, BDRV_CHILD_DATA, &error_abort); - c_fl1 = bdrv_attach_child(tricky, fl1, "first", &child_of_bds, - BDRV_CHILD_FILTERED, &error_abort); - c_fl2 = bdrv_attach_child(tricky, fl2, "second", &child_of_bds, - BDRV_CHILD_FILTERED, &error_abort); - bdrv_attach_child(fl1, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED, + c_fl1 = bdrv_attach_child(ws, fl1, "first", &child_of_bds, + BDRV_CHILD_DATA, &error_abort); + c_fl2 = bdrv_attach_child(ws, fl2, "second", &child_of_bds, + BDRV_CHILD_DATA, &error_abort); + bdrv_attach_child(fl1, base, "backing", &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); - bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED, + bdrv_attach_child(fl2, base, "backing", &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); + bdrv_graph_wrunlock(); /* Select fl1 as first child to be active */ - tricky->file = c_fl1; + s->selected = c_fl1; + + bdrv_graph_rdlock_main_loop(); + bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort); assert(c_fl1->perm & BLK_PERM_WRITE); assert(!(c_fl2->perm & BLK_PERM_WRITE)); /* Now, try to switch active child and update permissions */ - tricky->file = c_fl2; + s->selected = c_fl2; bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort); assert(c_fl2->perm & BLK_PERM_WRITE); assert(!(c_fl1->perm & BLK_PERM_WRITE)); /* Switch once more, to not care about real child order in the list */ - tricky->file = c_fl1; + s->selected = c_fl1; bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort); assert(c_fl1->perm & BLK_PERM_WRITE); assert(!(c_fl2->perm & BLK_PERM_WRITE)); + bdrv_graph_rdunlock_main_loop(); bdrv_unref(top); } @@ -379,8 +430,11 @@ static void test_append_greedy_filter(void) BlockDriverState *base = no_perm_node("base"); BlockDriverState *fl = exclusive_writer_node("fl1"); - bdrv_attach_child(top, base, "backing", &child_of_bds, BDRV_CHILD_COW, + bdrv_graph_wrlock(); + bdrv_attach_child(top, base, "backing", &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); + bdrv_graph_wrunlock(); bdrv_append(fl, base, &error_abort); bdrv_unref(fl); diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index 94718c9319..3766d5de6b 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "block/block.h" +#include "block/block_int-global-state.h" #include "block/blockjob_int.h" #include "sysemu/block-backend.h" #include "qapi/error.h" @@ -88,11 +89,11 @@ static void test_sync_op_pread(BdrvChild *c) int ret; /* Success */ - ret = bdrv_pread(c, 0, buf, sizeof(buf)); - g_assert_cmpint(ret, ==, 512); + ret = bdrv_pread(c, 0, sizeof(buf), buf, 0); + g_assert_cmpint(ret, ==, 0); /* Early error: Negative offset */ - ret = bdrv_pread(c, -2, buf, sizeof(buf)); + ret = bdrv_pread(c, -2, sizeof(buf), buf, 0); g_assert_cmpint(ret, ==, -EIO); } @@ -102,11 +103,11 @@ static void test_sync_op_pwrite(BdrvChild *c) int ret; /* Success */ - ret = bdrv_pwrite(c, 0, buf, sizeof(buf)); - g_assert_cmpint(ret, ==, 512); + ret = bdrv_pwrite(c, 0, sizeof(buf), buf, 0); + g_assert_cmpint(ret, ==, 0); /* Early error: Negative offset */ - ret = bdrv_pwrite(c, -2, buf, sizeof(buf)); + ret = bdrv_pwrite(c, -2, sizeof(buf), buf, 0); g_assert_cmpint(ret, ==, -EIO); } @@ -116,11 +117,11 @@ static void test_sync_op_blk_pread(BlockBackend *blk) int ret; /* Success */ - ret = blk_pread(blk, 0, buf, sizeof(buf)); - g_assert_cmpint(ret, ==, 512); + ret = blk_pread(blk, 0, sizeof(buf), buf, 0); + g_assert_cmpint(ret, ==, 0); /* Early error: Negative offset */ - ret = blk_pread(blk, -2, buf, sizeof(buf)); + ret = blk_pread(blk, -2, sizeof(buf), buf, 0); g_assert_cmpint(ret, ==, -EIO); } @@ -130,11 +131,98 @@ static void test_sync_op_blk_pwrite(BlockBackend *blk) int ret; /* Success */ - ret = blk_pwrite(blk, 0, buf, sizeof(buf), 0); - g_assert_cmpint(ret, ==, 512); + ret = blk_pwrite(blk, 0, sizeof(buf), buf, 0); + g_assert_cmpint(ret, ==, 0); /* Early error: Negative offset */ - ret = blk_pwrite(blk, -2, buf, sizeof(buf), 0); + ret = blk_pwrite(blk, -2, sizeof(buf), buf, 0); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_preadv(BlockBackend *blk) +{ + uint8_t buf[512]; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, sizeof(buf)); + int ret; + + /* Success */ + ret = blk_preadv(blk, 0, sizeof(buf), &qiov, 0); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = blk_preadv(blk, -2, sizeof(buf), &qiov, 0); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_pwritev(BlockBackend *blk) +{ + uint8_t buf[512] = { 0 }; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, sizeof(buf)); + int ret; + + /* Success */ + ret = blk_pwritev(blk, 0, sizeof(buf), &qiov, 0); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = blk_pwritev(blk, -2, sizeof(buf), &qiov, 0); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_preadv_part(BlockBackend *blk) +{ + uint8_t buf[512]; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, sizeof(buf)); + int ret; + + /* Success */ + ret = blk_preadv_part(blk, 0, sizeof(buf), &qiov, 0, 0); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = blk_preadv_part(blk, -2, sizeof(buf), &qiov, 0, 0); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_pwritev_part(BlockBackend *blk) +{ + uint8_t buf[512] = { 0 }; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, sizeof(buf)); + int ret; + + /* Success */ + ret = blk_pwritev_part(blk, 0, sizeof(buf), &qiov, 0, 0); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = blk_pwritev_part(blk, -2, sizeof(buf), &qiov, 0, 0); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_pwrite_compressed(BlockBackend *blk) +{ + uint8_t buf[512] = { 0 }; + int ret; + + /* Late error: Not supported */ + ret = blk_pwrite_compressed(blk, 0, sizeof(buf), buf); + g_assert_cmpint(ret, ==, -ENOTSUP); + + /* Early error: Negative offset */ + ret = blk_pwrite_compressed(blk, -2, sizeof(buf), buf); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_pwrite_zeroes(BlockBackend *blk) +{ + int ret; + + /* Success */ + ret = blk_pwrite_zeroes(blk, 0, 512, 0); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = blk_pwrite_zeroes(blk, -2, 512, 0); g_assert_cmpint(ret, ==, -EIO); } @@ -211,7 +299,21 @@ static void test_sync_op_truncate(BdrvChild *c) c->bs->open_flags |= BDRV_O_RDWR; } -static void test_sync_op_block_status(BdrvChild *c) +static void test_sync_op_blk_truncate(BlockBackend *blk) +{ + int ret; + + /* Normal success path */ + ret = blk_truncate(blk, 65536, false, PREALLOC_MODE_OFF, 0, NULL); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = blk_truncate(blk, -2, false, PREALLOC_MODE_OFF, 0, NULL); + g_assert_cmpint(ret, ==, -EINVAL); +} + +/* Disable TSA to make bdrv_test.bdrv_co_block_status writable */ +static void TSA_NO_TSA test_sync_op_block_status(BdrvChild *c) { int ret; int64_t n; @@ -281,6 +383,9 @@ static void test_sync_op_check(BdrvChild *c) static void test_sync_op_activate(BdrvChild *c) { + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + /* Early success: Image is not inactive */ bdrv_activate(c->bs, NULL); } @@ -301,6 +406,30 @@ const SyncOpTest sync_op_tests[] = { .name = "/sync-op/pwrite", .fn = test_sync_op_pwrite, .blkfn = test_sync_op_blk_pwrite, + }, { + .name = "/sync-op/preadv", + .fn = NULL, + .blkfn = test_sync_op_blk_preadv, + }, { + .name = "/sync-op/pwritev", + .fn = NULL, + .blkfn = test_sync_op_blk_pwritev, + }, { + .name = "/sync-op/preadv_part", + .fn = NULL, + .blkfn = test_sync_op_blk_preadv_part, + }, { + .name = "/sync-op/pwritev_part", + .fn = NULL, + .blkfn = test_sync_op_blk_pwritev_part, + }, { + .name = "/sync-op/pwrite_compressed", + .fn = NULL, + .blkfn = test_sync_op_blk_pwrite_compressed, + }, { + .name = "/sync-op/pwrite_zeroes", + .fn = NULL, + .blkfn = test_sync_op_blk_pwrite_zeroes, }, { .name = "/sync-op/load_vmstate", .fn = test_sync_op_load_vmstate, @@ -314,6 +443,7 @@ const SyncOpTest sync_op_tests[] = { }, { .name = "/sync-op/truncate", .fn = test_sync_op_truncate, + .blkfn = test_sync_op_blk_truncate, }, { .name = "/sync-op/block_status", .fn = test_sync_op_block_status, @@ -341,20 +471,25 @@ static void test_sync_op(const void *opaque) BlockDriverState *bs; BdrvChild *c; + GLOBAL_STATE_CODE(); + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); bs->total_sectors = 65536 / BDRV_SECTOR_SIZE; blk_insert_bs(blk, bs, &error_abort); + + bdrv_graph_rdlock_main_loop(); c = QLIST_FIRST(&bs->parents); + bdrv_graph_rdunlock_main_loop(); blk_set_aio_context(blk, ctx, &error_abort); - aio_context_acquire(ctx); - t->fn(c); + if (t->fn) { + t->fn(c); + } if (t->blkfn) { t->blkfn(blk); } blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); - aio_context_release(ctx); bdrv_unref(bs); blk_unref(blk); @@ -439,9 +574,7 @@ static void test_attach_blockjob(void) aio_poll(qemu_get_aio_context(), false); } - aio_context_acquire(ctx); blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); - aio_context_release(ctx); tjob->n = 0; while (tjob->n == 0) { @@ -455,10 +588,10 @@ static void test_attach_blockjob(void) aio_poll(qemu_get_aio_context(), false); } - aio_context_acquire(ctx); - job_complete_sync(&tjob->common.job, &error_abort); + WITH_JOB_LOCK_GUARD() { + job_complete_sync_locked(&tjob->common.job, &error_abort); + } blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); - aio_context_release(ctx); bdrv_unref(bs); blk_unref(blk); @@ -515,9 +648,7 @@ static void test_propagate_basic(void) /* Switch the AioContext back */ main_ctx = qemu_get_aio_context(); - aio_context_acquire(ctx); blk_set_aio_context(blk, main_ctx, &error_abort); - aio_context_release(ctx); g_assert(blk_get_aio_context(blk) == main_ctx); g_assert(bdrv_get_aio_context(bs_a) == main_ctx); g_assert(bdrv_get_aio_context(bs_verify) == main_ctx); @@ -593,9 +724,7 @@ static void test_propagate_diamond(void) /* Switch the AioContext back */ main_ctx = qemu_get_aio_context(); - aio_context_acquire(ctx); blk_set_aio_context(blk, main_ctx, &error_abort); - aio_context_release(ctx); g_assert(blk_get_aio_context(blk) == main_ctx); g_assert(bdrv_get_aio_context(bs_verify) == main_ctx); g_assert(bdrv_get_aio_context(bs_a) == main_ctx); @@ -630,20 +759,21 @@ static void test_propagate_mirror(void) BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, false, "filter_node", MIRROR_COPY_MODE_BACKGROUND, &error_abort); - job = job_get("job0"); + + WITH_JOB_LOCK_GUARD() { + job = job_get_locked("job0"); + } filter = bdrv_find_node("filter_node"); /* Change the AioContext of src */ - bdrv_try_set_aio_context(src, ctx, &error_abort); + bdrv_try_change_aio_context(src, ctx, NULL, &error_abort); g_assert(bdrv_get_aio_context(src) == ctx); g_assert(bdrv_get_aio_context(target) == ctx); g_assert(bdrv_get_aio_context(filter) == ctx); g_assert(job->aio_context == ctx); /* Change the AioContext of target */ - aio_context_acquire(ctx); - bdrv_try_set_aio_context(target, main_ctx, &error_abort); - aio_context_release(ctx); + bdrv_try_change_aio_context(target, main_ctx, NULL, &error_abort); g_assert(bdrv_get_aio_context(src) == main_ctx); g_assert(bdrv_get_aio_context(target) == main_ctx); g_assert(bdrv_get_aio_context(filter) == main_ctx); @@ -652,7 +782,7 @@ static void test_propagate_mirror(void) blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); blk_insert_bs(blk, src, &error_abort); - bdrv_try_set_aio_context(target, ctx, &local_err); + bdrv_try_change_aio_context(target, ctx, NULL, &local_err); error_free_or_abort(&local_err); g_assert(blk_get_aio_context(blk) == main_ctx); @@ -661,10 +791,8 @@ static void test_propagate_mirror(void) g_assert(bdrv_get_aio_context(filter) == main_ctx); /* ...unless we explicitly allow it */ - aio_context_acquire(ctx); blk_set_allow_aio_context_change(blk, true); - bdrv_try_set_aio_context(target, ctx, &error_abort); - aio_context_release(ctx); + bdrv_try_change_aio_context(target, ctx, NULL, &error_abort); g_assert(blk_get_aio_context(blk) == ctx); g_assert(bdrv_get_aio_context(src) == ctx); @@ -673,10 +801,8 @@ static void test_propagate_mirror(void) job_cancel_sync_all(); - aio_context_acquire(ctx); blk_set_aio_context(blk, main_ctx, &error_abort); - bdrv_try_set_aio_context(target, main_ctx, &error_abort); - aio_context_release(ctx); + bdrv_try_change_aio_context(target, main_ctx, NULL, &error_abort); blk_unref(blk); bdrv_unref(src); @@ -701,13 +827,12 @@ static void test_attach_second_node(void) qdict_put_str(options, "file", "base"); filter = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort); + g_assert(blk_get_aio_context(blk) == ctx); g_assert(bdrv_get_aio_context(bs) == ctx); g_assert(bdrv_get_aio_context(filter) == ctx); - aio_context_acquire(ctx); blk_set_aio_context(blk, main_ctx, &error_abort); - aio_context_release(ctx); g_assert(blk_get_aio_context(blk) == main_ctx); g_assert(bdrv_get_aio_context(bs) == main_ctx); g_assert(bdrv_get_aio_context(filter) == main_ctx); @@ -734,9 +859,7 @@ static void test_attach_preserve_blk_ctx(void) g_assert(bdrv_get_aio_context(bs) == ctx); /* Remove the node again */ - aio_context_acquire(ctx); blk_remove_bs(blk); - aio_context_release(ctx); g_assert(blk_get_aio_context(blk) == ctx); g_assert(bdrv_get_aio_context(bs) == qemu_get_aio_context()); @@ -745,9 +868,7 @@ static void test_attach_preserve_blk_ctx(void) g_assert(blk_get_aio_context(blk) == ctx); g_assert(bdrv_get_aio_context(bs) == ctx); - aio_context_acquire(ctx); blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); - aio_context_release(ctx); bdrv_unref(bs); blk_unref(blk); } diff --git a/tests/unit/test-blockjob-txn.c b/tests/unit/test-blockjob-txn.c index c69028b450..d3b0bb24be 100644 --- a/tests/unit/test-blockjob-txn.c +++ b/tests/unit/test-blockjob-txn.c @@ -116,8 +116,10 @@ static void test_single_job(int expected) job = test_block_job_start(1, true, expected, &result, txn); job_start(&job->job); - if (expected == -ECANCELED) { - job_cancel(&job->job, false); + WITH_JOB_LOCK_GUARD() { + if (expected == -ECANCELED) { + job_cancel_locked(&job->job, false); + } } while (result == -EINPROGRESS) { @@ -160,13 +162,15 @@ static void test_pair_jobs(int expected1, int expected2) /* Release our reference now to trigger as many nice * use-after-free bugs as possible. */ - job_txn_unref(txn); + WITH_JOB_LOCK_GUARD() { + job_txn_unref_locked(txn); - if (expected1 == -ECANCELED) { - job_cancel(&job1->job, false); - } - if (expected2 == -ECANCELED) { - job_cancel(&job2->job, false); + if (expected1 == -ECANCELED) { + job_cancel_locked(&job1->job, false); + } + if (expected2 == -ECANCELED) { + job_cancel_locked(&job2->job, false); + } } while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { @@ -219,7 +223,9 @@ static void test_pair_jobs_fail_cancel_race(void) job_start(&job1->job); job_start(&job2->job); - job_cancel(&job1->job, false); + WITH_JOB_LOCK_GUARD() { + job_cancel_locked(&job1->job, false); + } /* Now make job2 finish before the main loop kicks jobs. This simulates * the race between a pending kick and another job completing. diff --git a/tests/unit/test-blockjob.c b/tests/unit/test-blockjob.c index 4c9e1bf1e5..fe3e0d2d38 100644 --- a/tests/unit/test-blockjob.c +++ b/tests/unit/test-blockjob.c @@ -211,8 +211,11 @@ static CancelJob *create_common(Job **pjob) bjob = mk_job(blk, "Steve", &test_cancel_driver, true, JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); job = &bjob->job; - job_ref(job); - assert(job->status == JOB_STATUS_CREATED); + WITH_JOB_LOCK_GUARD() { + job_ref_locked(job); + assert(job->status == JOB_STATUS_CREATED); + } + s = container_of(bjob, CancelJob, common); s->blk = blk; @@ -225,21 +228,19 @@ static void cancel_common(CancelJob *s) BlockJob *job = &s->common; BlockBackend *blk = s->blk; JobStatus sts = job->job.status; - AioContext *ctx; - - ctx = job->job.aio_context; - aio_context_acquire(ctx); job_cancel_sync(&job->job, true); - if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { - Job *dummy = &job->job; - job_dismiss(&dummy, &error_abort); + WITH_JOB_LOCK_GUARD() { + if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { + Job *dummy = &job->job; + job_dismiss_locked(&dummy, &error_abort); + } + assert(job->job.status == JOB_STATUS_NULL); + job_unref_locked(&job->job); } - assert(job->job.status == JOB_STATUS_NULL); - job_unref(&job->job); + destroy_blk(blk); - aio_context_release(ctx); } static void test_cancel_created(void) @@ -251,6 +252,13 @@ static void test_cancel_created(void) cancel_common(s); } +static void assert_job_status_is(Job *job, int status) +{ + WITH_JOB_LOCK_GUARD() { + assert(job->status == status); + } +} + static void test_cancel_running(void) { Job *job; @@ -259,7 +267,7 @@ static void test_cancel_running(void) s = create_common(&job); job_start(job); - assert(job->status == JOB_STATUS_RUNNING); + assert_job_status_is(job, JOB_STATUS_RUNNING); cancel_common(s); } @@ -272,11 +280,12 @@ static void test_cancel_paused(void) s = create_common(&job); job_start(job); - assert(job->status == JOB_STATUS_RUNNING); - - job_user_pause(job, &error_abort); + WITH_JOB_LOCK_GUARD() { + assert(job->status == JOB_STATUS_RUNNING); + job_user_pause_locked(job, &error_abort); + } job_enter(job); - assert(job->status == JOB_STATUS_PAUSED); + assert_job_status_is(job, JOB_STATUS_PAUSED); cancel_common(s); } @@ -289,11 +298,11 @@ static void test_cancel_ready(void) s = create_common(&job); job_start(job); - assert(job->status == JOB_STATUS_RUNNING); + assert_job_status_is(job, JOB_STATUS_RUNNING); s->should_converge = true; job_enter(job); - assert(job->status == JOB_STATUS_READY); + assert_job_status_is(job, JOB_STATUS_READY); cancel_common(s); } @@ -306,15 +315,16 @@ static void test_cancel_standby(void) s = create_common(&job); job_start(job); - assert(job->status == JOB_STATUS_RUNNING); + assert_job_status_is(job, JOB_STATUS_RUNNING); s->should_converge = true; job_enter(job); - assert(job->status == JOB_STATUS_READY); - - job_user_pause(job, &error_abort); + WITH_JOB_LOCK_GUARD() { + assert(job->status == JOB_STATUS_READY); + job_user_pause_locked(job, &error_abort); + } job_enter(job); - assert(job->status == JOB_STATUS_STANDBY); + assert_job_status_is(job, JOB_STATUS_STANDBY); cancel_common(s); } @@ -327,20 +337,21 @@ static void test_cancel_pending(void) s = create_common(&job); job_start(job); - assert(job->status == JOB_STATUS_RUNNING); + assert_job_status_is(job, JOB_STATUS_RUNNING); s->should_converge = true; job_enter(job); - assert(job->status == JOB_STATUS_READY); - - job_complete(job, &error_abort); + WITH_JOB_LOCK_GUARD() { + assert(job->status == JOB_STATUS_READY); + job_complete_locked(job, &error_abort); + } job_enter(job); while (!job->deferred_to_main_loop) { aio_poll(qemu_get_aio_context(), true); } - assert(job->status == JOB_STATUS_READY); + assert_job_status_is(job, JOB_STATUS_READY); aio_poll(qemu_get_aio_context(), true); - assert(job->status == JOB_STATUS_PENDING); + assert_job_status_is(job, JOB_STATUS_PENDING); cancel_common(s); } @@ -353,146 +364,28 @@ static void test_cancel_concluded(void) s = create_common(&job); job_start(job); - assert(job->status == JOB_STATUS_RUNNING); + assert_job_status_is(job, JOB_STATUS_RUNNING); s->should_converge = true; job_enter(job); - assert(job->status == JOB_STATUS_READY); - - job_complete(job, &error_abort); + WITH_JOB_LOCK_GUARD() { + assert(job->status == JOB_STATUS_READY); + job_complete_locked(job, &error_abort); + } job_enter(job); while (!job->deferred_to_main_loop) { aio_poll(qemu_get_aio_context(), true); } - assert(job->status == JOB_STATUS_READY); + assert_job_status_is(job, JOB_STATUS_READY); aio_poll(qemu_get_aio_context(), true); - assert(job->status == JOB_STATUS_PENDING); + assert_job_status_is(job, 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); -} - -/* (See test_yielding_driver for the job description) */ -typedef struct YieldingJob { - BlockJob common; - bool should_complete; -} YieldingJob; - -static void yielding_job_complete(Job *job, Error **errp) -{ - YieldingJob *s = container_of(job, YieldingJob, common.job); - s->should_complete = true; - job_enter(job); -} - -static int coroutine_fn yielding_job_run(Job *job, Error **errp) -{ - YieldingJob *s = container_of(job, YieldingJob, common.job); - - job_transition_to_ready(job); - - while (!s->should_complete) { - job_yield(job); + WITH_JOB_LOCK_GUARD() { + job_finalize_locked(job, &error_abort); + assert(job->status == JOB_STATUS_CONCLUDED); } - return 0; -} - -/* - * This job transitions immediately to the READY state, and then - * yields until it is to complete. - */ -static const BlockJobDriver test_yielding_driver = { - .job_driver = { - .instance_size = sizeof(YieldingJob), - .free = block_job_free, - .user_resume = block_job_user_resume, - .run = yielding_job_run, - .complete = yielding_job_complete, - }, -}; - -/* - * Test that job_complete() works even on jobs that are in a paused - * state (i.e., STANDBY). - * - * To do this, run YieldingJob in an IO thread, get it into the READY - * state, then have a drained section. Before ending the section, - * acquire the context so the job will not be entered and will thus - * remain on STANDBY. - * - * job_complete() should still work without error. - * - * Note that on the QMP interface, it is impossible to lock an IO - * thread before a drained section ends. In practice, the - * bdrv_drain_all_end() and the aio_context_acquire() will be - * reversed. However, that makes for worse reproducibility here: - * Sometimes, the job would no longer be in STANDBY then but already - * be started. We cannot prevent that, because the IO thread runs - * concurrently. We can only prevent it by taking the lock before - * ending the drained section, so we do that. - * - * (You can reverse the order of operations and most of the time the - * test will pass, but sometimes the assert(status == STANDBY) will - * fail.) - */ -static void test_complete_in_standby(void) -{ - BlockBackend *blk; - IOThread *iothread; - AioContext *ctx; - Job *job; - BlockJob *bjob; - - /* Create a test drive, move it to an IO thread */ - blk = create_blk(NULL); - iothread = iothread_new(); - - ctx = iothread_get_aio_context(iothread); - blk_set_aio_context(blk, ctx, &error_abort); - - /* Create our test job */ - bjob = mk_job(blk, "job", &test_yielding_driver, true, - JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); - job = &bjob->job; - assert(job->status == JOB_STATUS_CREATED); - - /* Wait for the job to become READY */ - job_start(job); - aio_context_acquire(ctx); - AIO_WAIT_WHILE(ctx, job->status != JOB_STATUS_READY); - aio_context_release(ctx); - - /* Begin the drained section, pausing the job */ - bdrv_drain_all_begin(); - assert(job->status == JOB_STATUS_STANDBY); - /* Lock the IO thread to prevent the job from being run */ - aio_context_acquire(ctx); - /* This will schedule the job to resume it */ - bdrv_drain_all_end(); - - /* But the job cannot run, so it will remain on standby */ - assert(job->status == JOB_STATUS_STANDBY); - - /* Even though the job is on standby, this should work */ - job_complete(job, &error_abort); - - /* The test is done now, clean up. */ - job_finish_sync(job, NULL, &error_abort); - assert(job->status == JOB_STATUS_PENDING); - - job_finalize(job, &error_abort); - assert(job->status == JOB_STATUS_CONCLUDED); - - job_dismiss(&job, &error_abort); - - destroy_blk(blk); - aio_context_release(ctx); - iothread_join(iothread); + cancel_common(s); } int main(int argc, char **argv) @@ -509,6 +402,5 @@ int main(int argc, char **argv) g_test_add_func("/blockjob/cancel/standby", test_cancel_standby); g_test_add_func("/blockjob/cancel/pending", test_cancel_pending); g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded); - g_test_add_func("/blockjob/complete_in_standby", test_complete_in_standby); return g_test_run(); } diff --git a/tests/unit/test-char.c b/tests/unit/test-char.c index 5b3b48ebac..f273ce5226 100644 --- a/tests/unit/test-char.c +++ b/tests/unit/test-char.c @@ -556,7 +556,7 @@ static int make_udp_socket(int *port) socklen_t alen = sizeof(addr); int ret, sock = qemu_socket(PF_INET, SOCK_DGRAM, 0); - g_assert_cmpint(sock, >, 0); + g_assert_cmpint(sock, >=, 0); addr.sin_family = AF_INET ; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = 0; @@ -1203,6 +1203,30 @@ static void char_serial_test(void) } #endif +#if defined(HAVE_CHARDEV_PARALLEL) && !defined(WIN32) +static void char_parallel_test(void) +{ + QemuOpts *opts; + Chardev *chr; + + opts = qemu_opts_create(qemu_find_opts("chardev"), "parallel-id", + 1, &error_abort); + qemu_opt_set(opts, "backend", "parallel", &error_abort); + qemu_opt_set(opts, "path", "/dev/null", &error_abort); + + chr = qemu_chr_new_from_opts(opts, NULL, NULL); +#ifdef __linux__ + /* fails to PPCLAIM, see qemu_chr_open_pp_fd() */ + g_assert_null(chr); +#else + g_assert_nonnull(chr); + object_unparent(OBJECT(chr)); +#endif + + qemu_opts_del(opts); +} +#endif + #ifndef _WIN32 static void char_file_fifo_test(void) { @@ -1212,7 +1236,6 @@ static void char_file_fifo_test(void) char *fifo = g_build_filename(tmp_path, "fifo", NULL); char *out = g_build_filename(tmp_path, "out", NULL); ChardevFile file = { .in = fifo, - .has_in = true, .out = out }; ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE, .u.file.data = &file }; @@ -1384,7 +1407,7 @@ static void char_hotswap_test(void) int port; int sock = make_udp_socket(&port); - g_assert_cmpint(sock, >, 0); + g_assert_cmpint(sock, >=, 0); chr_args = g_strdup_printf("udp:127.0.0.1:%d", port); @@ -1545,6 +1568,9 @@ int main(int argc, char **argv) g_test_add_func("/char/udp", char_udp_test); #if defined(HAVE_CHARDEV_SERIAL) && !defined(WIN32) g_test_add_func("/char/serial", char_serial_test); +#endif +#if defined(HAVE_CHARDEV_PARALLEL) && !defined(WIN32) + g_test_add_func("/char/parallel", char_parallel_test); #endif g_test_add_func("/char/hotswap", char_hotswap_test); g_test_add_func("/char/websocket", char_websock_test); diff --git a/tests/unit/test-coroutine.c b/tests/unit/test-coroutine.c index aa77a3bcb3..49d4d9b251 100644 --- a/tests/unit/test-coroutine.c +++ b/tests/unit/test-coroutine.c @@ -12,9 +12,7 @@ */ #include "qemu/osdep.h" -#include "qemu/coroutine.h" #include "qemu/coroutine_int.h" -#include "qemu/lockable.h" /* * Check that qemu_in_coroutine() works @@ -197,7 +195,7 @@ static void test_no_dangling_access(void) } static bool locked; -static int done; +static int done_count; static void coroutine_fn mutex_fn(void *opaque) { @@ -208,7 +206,7 @@ static void coroutine_fn mutex_fn(void *opaque) qemu_coroutine_yield(); locked = false; qemu_co_mutex_unlock(m); - done++; + done_count++; } static void coroutine_fn lockable_fn(void *opaque) @@ -220,7 +218,7 @@ static void coroutine_fn lockable_fn(void *opaque) qemu_coroutine_yield(); locked = false; qemu_lockable_unlock(x); - done++; + done_count++; } static void do_test_co_mutex(CoroutineEntry *entry, void *opaque) @@ -228,7 +226,7 @@ static void do_test_co_mutex(CoroutineEntry *entry, void *opaque) Coroutine *c1 = qemu_coroutine_create(entry, opaque); Coroutine *c2 = qemu_coroutine_create(entry, opaque); - done = 0; + done_count = 0; qemu_coroutine_enter(c1); g_assert(locked); qemu_coroutine_enter(c2); @@ -237,11 +235,11 @@ static void do_test_co_mutex(CoroutineEntry *entry, void *opaque) * terminates. */ qemu_coroutine_enter(c1); - g_assert_cmpint(done, ==, 1); + g_assert_cmpint(done_count, ==, 1); g_assert(locked); qemu_coroutine_enter(c2); - g_assert_cmpint(done, ==, 2); + g_assert_cmpint(done_count, ==, 2); g_assert(!locked); } @@ -610,7 +608,7 @@ static void perf_baseline(void) g_test_message("Function call %u iterations: %f s", maxcycles, duration); } -static __attribute__((noinline)) void perf_cost_func(void *opaque) +static __attribute__((noinline)) void coroutine_fn perf_cost_func(void *opaque) { qemu_coroutine_yield(); } @@ -647,7 +645,7 @@ int main(int argc, char **argv) * with a sentinel value. If there is no freelist this would legitimately * crash, so skip it. */ - if (CONFIG_COROUTINE_POOL) { + if (IS_ENABLED(CONFIG_COROUTINE_POOL)) { g_test_add_func("/basic/no-dangling-access", test_no_dangling_access); } diff --git a/tests/unit/test-crypto-block.c b/tests/unit/test-crypto-block.c index 3b1f0d509f..6cfc817a92 100644 --- a/tests/unit/test-crypto-block.c +++ b/tests/unit/test-crypto-block.c @@ -22,6 +22,7 @@ #include "qapi/error.h" #include "crypto/init.h" #include "crypto/block.h" +#include "crypto/block-luks-priv.h" #include "qemu/buffer.h" #include "qemu/module.h" #include "crypto/secret.h" @@ -30,7 +31,8 @@ #endif #if (defined(_WIN32) || defined RUSAGE_THREAD) && \ - (defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT)) + (defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT) || \ + defined(CONFIG_GNUTLS_CRYPTO)) #define TEST_LUKS #else #undef TEST_LUKS @@ -39,7 +41,6 @@ static QCryptoBlockCreateOptions qcow_create_opts = { .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, .u.qcow = { - .has_key_secret = true, .key_secret = (char *)"sec0", }, }; @@ -47,7 +48,6 @@ static QCryptoBlockCreateOptions qcow_create_opts = { static QCryptoBlockOpenOptions qcow_open_opts = { .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, .u.qcow = { - .has_key_secret = true, .key_secret = (char *)"sec0", }, }; @@ -57,7 +57,6 @@ static QCryptoBlockOpenOptions qcow_open_opts = { static QCryptoBlockOpenOptions luks_open_opts = { .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, .u.luks = { - .has_key_secret = true, .key_secret = (char *)"sec0", }, }; @@ -67,7 +66,6 @@ static QCryptoBlockOpenOptions luks_open_opts = { static QCryptoBlockCreateOptions luks_create_opts_default = { .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, .u.luks = { - .has_key_secret = true, .key_secret = (char *)"sec0", }, }; @@ -77,7 +75,6 @@ static QCryptoBlockCreateOptions luks_create_opts_default = { static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_plain64 = { .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, .u.luks = { - .has_key_secret = true, .key_secret = (char *)"sec0", .has_cipher_alg = true, .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, @@ -92,7 +89,6 @@ static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_plain64 = { static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_essiv = { .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, .u.luks = { - .has_key_secret = true, .key_secret = (char *)"sec0", .has_cipher_alg = true, .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, @@ -188,12 +184,12 @@ static struct QCryptoBlockTestData { }; -static ssize_t test_block_read_func(QCryptoBlock *block, - size_t offset, - uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp) +static int test_block_read_func(QCryptoBlock *block, + size_t offset, + uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp) { Buffer *header = opaque; @@ -201,14 +197,14 @@ static ssize_t test_block_read_func(QCryptoBlock *block, memcpy(buf, header->buffer + offset, buflen); - return buflen; + return 0; } -static ssize_t test_block_init_func(QCryptoBlock *block, - size_t headerlen, - void *opaque, - Error **errp) +static int test_block_init_func(QCryptoBlock *block, + size_t headerlen, + void *opaque, + Error **errp) { Buffer *header = opaque; @@ -216,16 +212,16 @@ static ssize_t test_block_init_func(QCryptoBlock *block, buffer_reserve(header, headerlen); - return headerlen; + return 0; } -static ssize_t test_block_write_func(QCryptoBlock *block, - size_t offset, - const uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp) +static int test_block_write_func(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp) { Buffer *header = opaque; @@ -234,7 +230,7 @@ static ssize_t test_block_write_func(QCryptoBlock *block, memcpy(header->buffer + offset, buf, buflen); header->offset = offset + buflen; - return buflen; + return 0; } @@ -287,6 +283,7 @@ static void test_block(gconstpointer opaque) test_block_init_func, test_block_write_func, &header, + 0, &error_abort); g_assert(blk); @@ -344,6 +341,231 @@ static void test_block(gconstpointer opaque) } +#ifdef TEST_LUKS +typedef const char *(*LuksHeaderDoBadStuff)(QCryptoBlockLUKSHeader *hdr); + +static void +test_luks_bad_header(gconstpointer data) +{ + LuksHeaderDoBadStuff badstuff = data; + QCryptoBlock *blk; + Buffer buf; + Object *sec = test_block_secret(); + QCryptoBlockLUKSHeader hdr; + Error *err = NULL; + const char *msg; + + memset(&buf, 0, sizeof(buf)); + buffer_init(&buf, "header"); + + /* Correctly create the volume initially */ + blk = qcrypto_block_create(&luks_create_opts_default, NULL, + test_block_init_func, + test_block_write_func, + &buf, + 0, + &error_abort); + g_assert(blk); + + qcrypto_block_free(blk); + + /* Mangle it in some unpleasant way */ + g_assert(buf.offset >= sizeof(hdr)); + memcpy(&hdr, buf.buffer, sizeof(hdr)); + qcrypto_block_luks_to_disk_endian(&hdr); + + msg = badstuff(&hdr); + + qcrypto_block_luks_from_disk_endian(&hdr); + memcpy(buf.buffer, &hdr, sizeof(hdr)); + + /* Check that we fail to open it again */ + blk = qcrypto_block_open(&luks_open_opts, NULL, + test_block_read_func, + &buf, + 0, + 1, + &err); + g_assert(!blk); + g_assert(err); + + g_assert_cmpstr(error_get_pretty(err), ==, msg); + error_free(err); + + object_unparent(sec); + + buffer_free(&buf); +} + +static const char *luks_bad_null_term_cipher_name(QCryptoBlockLUKSHeader *hdr) +{ + /* Replace NUL termination with spaces */ + char *offset = hdr->cipher_name + strlen(hdr->cipher_name); + memset(offset, ' ', sizeof(hdr->cipher_name) - (offset - hdr->cipher_name)); + + return "LUKS header cipher name is not NUL terminated"; +} + +static const char *luks_bad_null_term_cipher_mode(QCryptoBlockLUKSHeader *hdr) +{ + /* Replace NUL termination with spaces */ + char *offset = hdr->cipher_mode + strlen(hdr->cipher_mode); + memset(offset, ' ', sizeof(hdr->cipher_mode) - (offset - hdr->cipher_mode)); + + return "LUKS header cipher mode is not NUL terminated"; +} + +static const char *luks_bad_null_term_hash_spec(QCryptoBlockLUKSHeader *hdr) +{ + /* Replace NUL termination with spaces */ + char *offset = hdr->hash_spec + strlen(hdr->hash_spec); + memset(offset, ' ', sizeof(hdr->hash_spec) - (offset - hdr->hash_spec)); + + return "LUKS header hash spec is not NUL terminated"; +} + +static const char *luks_bad_cipher_name_empty(QCryptoBlockLUKSHeader *hdr) +{ + memcpy(hdr->cipher_name, "", 1); + + return "Algorithm '' with key size 32 bytes not supported"; +} + +static const char *luks_bad_cipher_name_unknown(QCryptoBlockLUKSHeader *hdr) +{ + memcpy(hdr->cipher_name, "aess", 5); + + return "Algorithm 'aess' with key size 32 bytes not supported"; +} + +static const char *luks_bad_cipher_xts_size(QCryptoBlockLUKSHeader *hdr) +{ + hdr->master_key_len = 33; + + return "XTS cipher key length should be a multiple of 2"; +} + +static const char *luks_bad_cipher_cbc_size(QCryptoBlockLUKSHeader *hdr) +{ + hdr->master_key_len = 33; + memcpy(hdr->cipher_mode, "cbc-essiv", 10); + + return "Algorithm 'aes' with key size 33 bytes not supported"; +} + +static const char *luks_bad_cipher_mode_empty(QCryptoBlockLUKSHeader *hdr) +{ + memcpy(hdr->cipher_mode, "", 1); + + return "Unexpected cipher mode string format ''"; +} + +static const char *luks_bad_cipher_mode_unknown(QCryptoBlockLUKSHeader *hdr) +{ + memcpy(hdr->cipher_mode, "xfs", 4); + + return "Unexpected cipher mode string format 'xfs'"; +} + +static const char *luks_bad_ivgen_separator(QCryptoBlockLUKSHeader *hdr) +{ + memcpy(hdr->cipher_mode, "xts:plain64", 12); + + return "Unexpected cipher mode string format 'xts:plain64'"; +} + +static const char *luks_bad_ivgen_name_empty(QCryptoBlockLUKSHeader *hdr) +{ + memcpy(hdr->cipher_mode, "xts-", 5); + + return "IV generator '' not supported"; +} + +static const char *luks_bad_ivgen_name_unknown(QCryptoBlockLUKSHeader *hdr) +{ + memcpy(hdr->cipher_mode, "xts-plain65", 12); + + return "IV generator 'plain65' not supported"; +} + +static const char *luks_bad_ivgen_hash_empty(QCryptoBlockLUKSHeader *hdr) +{ + memcpy(hdr->cipher_mode, "xts-plain65:", 13); + + return "Hash algorithm '' not supported"; +} + +static const char *luks_bad_ivgen_hash_unknown(QCryptoBlockLUKSHeader *hdr) +{ + memcpy(hdr->cipher_mode, "xts-plain65:sha257", 19); + + return "Hash algorithm 'sha257' not supported"; +} + +static const char *luks_bad_hash_spec_empty(QCryptoBlockLUKSHeader *hdr) +{ + memcpy(hdr->hash_spec, "", 1); + + return "Hash algorithm '' not supported"; +} + +static const char *luks_bad_hash_spec_unknown(QCryptoBlockLUKSHeader *hdr) +{ + memcpy(hdr->hash_spec, "sha2566", 8); + + return "Hash algorithm 'sha2566' not supported"; +} + +static const char *luks_bad_stripes(QCryptoBlockLUKSHeader *hdr) +{ + hdr->key_slots[0].stripes = 3999; + + return "Keyslot 0 is corrupted (stripes 3999 != 4000)"; +} + +static const char *luks_bad_key_overlap_header(QCryptoBlockLUKSHeader *hdr) +{ + hdr->key_slots[0].key_offset_sector = 2; + + return "Keyslot 0 is overlapping with the LUKS header"; +} + +static const char *luks_bad_key_overlap_key(QCryptoBlockLUKSHeader *hdr) +{ + hdr->key_slots[0].key_offset_sector = hdr->key_slots[1].key_offset_sector; + + return "Keyslots 0 and 1 are overlapping in the header"; +} + +static const char *luks_bad_key_overlap_payload(QCryptoBlockLUKSHeader *hdr) +{ + hdr->key_slots[0].key_offset_sector = hdr->payload_offset_sector + 42; + + return "Keyslot 0 is overlapping with the encrypted payload"; +} + +static const char *luks_bad_payload_overlap_header(QCryptoBlockLUKSHeader *hdr) +{ + hdr->payload_offset_sector = 2; + + return "LUKS payload is overlapping with the header"; +} + +static const char *luks_bad_key_iterations(QCryptoBlockLUKSHeader *hdr) +{ + hdr->key_slots[0].iterations = 0; + + return "Keyslot 0 iteration count is zero"; +} + +static const char *luks_bad_iterations(QCryptoBlockLUKSHeader *hdr) +{ + hdr->master_key_iterations = 0; + + return "LUKS key iteration count is zero"; +} +#endif + int main(int argc, char **argv) { gsize i; @@ -364,5 +586,79 @@ int main(int argc, char **argv) } } +#ifdef TEST_LUKS + if (g_test_slow()) { + g_test_add_data_func("/crypto/block/luks/bad/cipher-name-nul-term", + luks_bad_null_term_cipher_name, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/cipher-mode-nul-term", + luks_bad_null_term_cipher_mode, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/hash-spec-nul-term", + luks_bad_null_term_hash_spec, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/cipher-name-empty", + luks_bad_cipher_name_empty, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/cipher-name-unknown", + luks_bad_cipher_name_unknown, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/cipher-xts-size", + luks_bad_cipher_xts_size, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/cipher-cbc-size", + luks_bad_cipher_cbc_size, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/cipher-mode-empty", + luks_bad_cipher_mode_empty, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/cipher-mode-unknown", + luks_bad_cipher_mode_unknown, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/ivgen-separator", + luks_bad_ivgen_separator, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/ivgen-name-empty", + luks_bad_ivgen_name_empty, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/ivgen-name-unknown", + luks_bad_ivgen_name_unknown, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/ivgen-hash-empty", + luks_bad_ivgen_hash_empty, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/ivgen-hash-unknown", + luks_bad_ivgen_hash_unknown, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/hash-spec-empty", + luks_bad_hash_spec_empty, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/hash-spec-unknown", + luks_bad_hash_spec_unknown, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/stripes", + luks_bad_stripes, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/key-overlap-header", + luks_bad_key_overlap_header, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/key-overlap-key", + luks_bad_key_overlap_key, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/key-overlap-payload", + luks_bad_key_overlap_payload, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/payload-overlap-header", + luks_bad_payload_overlap_header, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/iterations", + luks_bad_iterations, + test_luks_bad_header); + g_test_add_data_func("/crypto/block/luks/bad/key-iterations", + luks_bad_key_iterations, + test_luks_bad_header); + } +#endif + return g_test_run(); } diff --git a/tests/unit/test-crypto-cipher.c b/tests/unit/test-crypto-cipher.c index d9d9d078ff..f5152e569d 100644 --- a/tests/unit/test-crypto-cipher.c +++ b/tests/unit/test-crypto-cipher.c @@ -382,6 +382,19 @@ static QCryptoCipherTestData test_data[] = { .plaintext = "90afe91bb288544f2c32dc239b2635e6", .ciphertext = "6cb4561c40bf0a9705931cb6d408e7fa", }, +#ifdef CONFIG_CRYPTO_SM4 + { + /* SM4, GB/T 32907-2016, Appendix A.1 */ + .path = "/crypto/cipher/sm4", + .alg = QCRYPTO_CIPHER_ALG_SM4, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "0123456789abcdeffedcba9876543210", + .plaintext = + "0123456789abcdeffedcba9876543210", + .ciphertext = + "681edf34d206965e86b3e94f536e4246", + }, +#endif { /* #1 32 byte key, 32 byte PTX */ .path = "/crypto/cipher/aes-xts-128-1", @@ -663,9 +676,8 @@ static void test_cipher(const void *opaque) cipher = qcrypto_cipher_new( data->alg, data->mode, key, nkey, - &err); + data->plaintext ? &error_abort : &err); if (data->plaintext) { - g_assert(err == NULL); g_assert(cipher != NULL); } else { error_free_or_abort(&err); @@ -809,6 +821,10 @@ int main(int argc, char **argv) for (i = 0; i < G_N_ELEMENTS(test_data); i++) { if (qcrypto_cipher_supports(test_data[i].alg, test_data[i].mode)) { g_test_add_data_func(test_data[i].path, &test_data[i], test_cipher); + } else { + g_printerr("# skip unsupported %s:%s\n", + QCryptoCipherAlgorithm_str(test_data[i].alg), + QCryptoCipherMode_str(test_data[i].mode)); } } diff --git a/tests/unit/test-crypto-der.c b/tests/unit/test-crypto-der.c index aed0f28d68..d218a7f170 100644 --- a/tests/unit/test-crypto-der.c +++ b/tests/unit/test-crypto-der.c @@ -147,13 +147,58 @@ static const uint8_t test_rsa2048_priv_key[] = "\x4e\x2f\x4c\xf9\xab\x97\x38\xe4\x20\x32\x32\x96\xc8\x9e\x79\xd3" "\x12"; +static const uint8_t test_ecdsa_p192_priv_key[] = + "\x30\x53" /* SEQUENCE, offset 0, length 83 */ + "\x02\x01\x01" /* INTEGER, offset 2, length 1 */ + "\x04\x18" /* OCTET STRING, offset 5, length 24 */ + "\xcb\xc8\x86\x0e\x66\x3c\xf7\x5a\x44\x13\xb8\xef\xea\x1d\x7b\xa6" + "\x1c\xda\xf4\x1b\xc7\x67\x6b\x35" + "\xa1\x34" /* CONTEXT SPECIFIC 1, offset 31, length 52 */ + "\x03\x32" /* BIT STRING, offset 33, length 50 */ + "\x00\x04\xc4\x16\xb3\xff\xac\xd5\x87\x98\xf7\xd9\x45\xfe\xd3\x5c" + "\x17\x9d\xb2\x36\x22\xcc\x07\xb3\x6d\x3c\x4e\x04\x5f\xeb\xb6\x52" + "\x58\xfb\x36\x10\x52\xb7\x01\x62\x0e\x94\x51\x1d\xe2\xef\x10\x82" + "\x88\x78"; + +static const uint8_t test_ecdsa_p256_priv_key[] = + "\x30\x77" /* SEQUENCE, offset 0, length 119 */ + "\x02\x01\x01" /* INTEGER, offset 2, length 1 */ + "\x04\x20" /* OCTET STRING, offset 5, length 32 */ + "\xf6\x92\xdd\x29\x1c\x6e\xef\xb6\xb2\x73\x9f\x40\x1b\xb3\x2a\x28" + "\xd2\x37\xd6\x4a\x5b\xe4\x40\x4c\x6a\x95\x99\xfa\xf7\x92\x49\xbe" + "\xa0\x0a" /* CONTEXT SPECIFIC 0, offset 39, length 10 */ + "\x06\x08" /* OID, offset 41, length 8 */ + "\x2a\x86\x48\xce\x3d\x03\x01\x07" + "\xa1\x44" /* CONTEXT SPECIFIC 1, offset 51, length 68 */ + "\x03\x42" /* BIT STRING, offset 53, length 66 */ + "\x00\x04\xed\x42\x9c\x67\x79\xbe\x46\x83\x88\x3e\x8c\xc1\x33\xf3" + "\xc3\xf6\x2c\xf3\x13\x6a\x00\xc2\xc9\x3e\x87\x7f\x86\x39\xe6\xae" + "\xe3\xb9\xba\x2f\x58\x63\x32\x62\x62\x54\x07\x27\xf9\x5a\x3a\xc7" + "\x3a\x6b\x5b\xbc\x0d\x33\xba\xbb\xd4\xa3\xff\x4f\x9e\xdd\xf5\x59" + "\xc0\xf6"; + #define MAX_CHECKER_COUNT 32 +static int qcrypto_wrapped_decode_ctx_tag0(const uint8_t **data, size_t *dlen, + QCryptoDERDecodeCb cb, void *opaque, + Error **errp) +{ + return qcrypto_der_decode_ctx_tag(data, dlen, 0, cb, opaque, errp); +} + +static int qcrypto_wrapped_decode_ctx_tag1(const uint8_t **data, size_t *dlen, + QCryptoDERDecodeCb cb, void *opaque, + Error **errp) +{ + return qcrypto_der_decode_ctx_tag(data, dlen, 1, cb, opaque, errp); +} + typedef struct QCryptoAns1DecoderResultChecker QCryptoAns1DecoderResultChecker; struct QCryptoAns1DecoderResultChecker { int (*action) (const uint8_t **data, size_t *dlen, QCryptoDERDecodeCb cb, void *opaque, Error **errp); QCryptoDERDecodeCb cb; + bool constructed; const uint8_t *exp_value; size_t exp_vlen; }; @@ -204,7 +249,7 @@ static void test_ans1(const void *opaque) g_assert(checker->action(&c->data, &c->dlen, checker_callback, (void *)checker, &error_abort) == checker->exp_vlen); - if (checker->action == qcrypto_der_decode_seq) { + if (checker->constructed) { ++seq_depth; ctx[seq_depth].data = checker->exp_value; ctx[seq_depth].dlen = checker->exp_vlen; @@ -225,25 +270,25 @@ static QCryptoAns1DecoderTestData test_data[] = { .test_data = test_rsa512_priv_key, .test_data_len = sizeof(test_rsa512_priv_key) - 1, .checker = { - { qcrypto_der_decode_seq, checker_callback, + { qcrypto_der_decode_seq, checker_callback, true, test_rsa512_priv_key + 4, 313 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa512_priv_key + 4 + 2, 1 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa512_priv_key + 7 + 2, 65 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa512_priv_key + 74 + 2, 3 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa512_priv_key + 79 + 2, 64 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa512_priv_key + 145 + 2, 33 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa512_priv_key + 180 + 2, 33 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa512_priv_key + 215 + 2, 32 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa512_priv_key + 249 + 2, 32 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa512_priv_key + 283 + 2, 32 }, }, }, @@ -252,29 +297,66 @@ static QCryptoAns1DecoderTestData test_data[] = { .test_data = test_rsa2048_priv_key, .test_data_len = sizeof(test_rsa2048_priv_key) - 1, .checker = { - { qcrypto_der_decode_seq, checker_callback, + { qcrypto_der_decode_seq, checker_callback, true, test_rsa2048_priv_key + 4, 1190 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa2048_priv_key + 4 + 2, 1 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa2048_priv_key + 7 + 4, 257 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa2048_priv_key + 268 + 2, 3 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa2048_priv_key + 273 + 4, 257 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa2048_priv_key + 534 + 3, 129 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa2048_priv_key + 666 + 3, 129 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa2048_priv_key + 798 + 3, 129 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa2048_priv_key + 930 + 3, 129 }, - { qcrypto_der_decode_int, checker_callback, + { qcrypto_der_decode_int, checker_callback, false, test_rsa2048_priv_key + 1062 + 3, 129 }, }, }, - +{ + .path = "/crypto/der/parse-ecdsa-p192-priv-key", + .test_data = test_ecdsa_p192_priv_key, + .test_data_len = sizeof(test_ecdsa_p192_priv_key) - 1, + .checker = { + { qcrypto_der_decode_seq, checker_callback, true, + test_ecdsa_p192_priv_key + 2, 83 }, + { qcrypto_der_decode_int, checker_callback, false, + test_ecdsa_p192_priv_key + 2 + 2, 1 }, + { qcrypto_der_decode_octet_str, checker_callback, false, + test_ecdsa_p192_priv_key + 5 + 2, 24 }, + { qcrypto_wrapped_decode_ctx_tag1, checker_callback, true, + test_ecdsa_p192_priv_key + 31 + 2, 52 }, + { qcrypto_der_decode_bit_str , checker_callback, false, + test_ecdsa_p192_priv_key + 33 + 2, 50 }, + }, +}, +{ + .path = "/crypto/der/parse-ecdsa-p256-priv-key", + .test_data = test_ecdsa_p256_priv_key, + .test_data_len = sizeof(test_ecdsa_p256_priv_key) - 1, + .checker = { + { qcrypto_der_decode_seq, checker_callback, true, + test_ecdsa_p256_priv_key + 2, 119 }, + { qcrypto_der_decode_int, checker_callback, false, + test_ecdsa_p256_priv_key + 2 + 2, 1 }, + { qcrypto_der_decode_octet_str, checker_callback, false, + test_ecdsa_p256_priv_key + 5 + 2, 32 }, + { qcrypto_wrapped_decode_ctx_tag0, checker_callback, true, + test_ecdsa_p256_priv_key + 39 + 2, 10 }, + { qcrypto_der_decode_oid, checker_callback, false, + test_ecdsa_p256_priv_key + 41 + 2, 8 }, + { qcrypto_wrapped_decode_ctx_tag1, checker_callback, true, + test_ecdsa_p256_priv_key + 51 + 2, 68 }, + { qcrypto_der_decode_bit_str , checker_callback, false, + test_ecdsa_p256_priv_key + 53 + 2, 66 }, + }, +}, }; int main(int argc, char **argv) diff --git a/tests/unit/test-crypto-secret.c b/tests/unit/test-crypto-secret.c index 34a4aecc12..ffd13ff70e 100644 --- a/tests/unit/test-crypto-secret.c +++ b/tests/unit/test-crypto-secret.c @@ -24,7 +24,7 @@ #include "crypto/secret.h" #include "qapi/error.h" #include "qemu/module.h" -#ifdef CONFIG_KEYUTILS +#if defined(CONFIG_KEYUTILS) && defined(CONFIG_SECRET_KEYRING) #include "crypto/secret_keyring.h" #include #endif @@ -128,7 +128,7 @@ static void test_secret_indirect_emptyfile(void) g_free(fname); } -#ifdef CONFIG_KEYUTILS +#if defined(CONFIG_KEYUTILS) && defined(CONFIG_SECRET_KEYRING) #define DESCRIPTION "qemu_test_secret" #define PAYLOAD "Test Payload" @@ -244,7 +244,7 @@ static void test_secret_keyring_bad_key_access_right(void) char key_str[16]; Object *sec; - g_test_skip("TODO: Need responce from Linux kernel maintainers"); + g_test_skip("TODO: Need response from Linux kernel maintainers"); return; int32_t key = add_key("user", DESCRIPTION, PAYLOAD, @@ -268,7 +268,7 @@ static void test_secret_keyring_bad_key_access_right(void) keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING); } -#endif /* CONFIG_KEYUTILS */ +#endif /* CONFIG_KEYUTILS && CONFIG_SECRET_KEYRING */ static void test_secret_noconv_base64_good(void) { @@ -571,7 +571,7 @@ int main(int argc, char **argv) g_test_add_func("/crypto/secret/indirect/emptyfile", test_secret_indirect_emptyfile); -#ifdef CONFIG_KEYUTILS +#if defined(CONFIG_KEYUTILS) && defined(CONFIG_SECRET_KEYRING) g_test_add_func("/crypto/secret/keyring/good", test_secret_keyring_good); g_test_add_func("/crypto/secret/keyring/revoked_key", @@ -582,7 +582,7 @@ int main(int argc, char **argv) test_secret_keyring_bad_serial_key); g_test_add_func("/crypto/secret/keyring/bad_key_access_right", test_secret_keyring_bad_key_access_right); -#endif /* CONFIG_KEYUTILS */ +#endif /* CONFIG_KEYUTILS && CONFIG_SECRET_KEYRING */ g_test_add_func("/crypto/secret/noconv/base64/good", test_secret_noconv_base64_good); diff --git a/tests/unit/test-crypto-tlscredsx509.c b/tests/unit/test-crypto-tlscredsx509.c index aab4149b56..3c25d75ca1 100644 --- a/tests/unit/test-crypto-tlscredsx509.c +++ b/tests/unit/test-crypto-tlscredsx509.c @@ -75,7 +75,7 @@ static void test_tls_creds(const void *opaque) QCryptoTLSCreds *creds; #define CERT_DIR "tests/test-crypto-tlscredsx509-certs/" - mkdir(CERT_DIR, 0700); + g_mkdir_with_parents(CERT_DIR, 0700); unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); if (data->isServer) { @@ -141,7 +141,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); - mkdir(WORKDIR, 0700); + g_mkdir_with_parents(WORKDIR, 0700); test_tls_init(KEYFILE); diff --git a/tests/unit/test-crypto-tlssession.c b/tests/unit/test-crypto-tlssession.c index f222959d36..b12e7b6879 100644 --- a/tests/unit/test-crypto-tlssession.c +++ b/tests/unit/test-crypto-tlssession.c @@ -82,7 +82,7 @@ static void test_crypto_tls_session_psk(void) int ret; /* We'll use this for our fake client-server connection */ - ret = socketpair(AF_UNIX, SOCK_STREAM, 0, channel); + ret = qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, channel); g_assert(ret == 0); /* @@ -236,7 +236,7 @@ static void test_crypto_tls_session_x509(const void *opaque) int ret; /* We'll use this for our fake client-server connection */ - ret = socketpair(AF_UNIX, SOCK_STREAM, 0, channel); + ret = qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, channel); g_assert(ret == 0); /* @@ -249,8 +249,8 @@ static void test_crypto_tls_session_x509(const void *opaque) #define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/" #define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/" - mkdir(CLIENT_CERT_DIR, 0700); - mkdir(SERVER_CERT_DIR, 0700); + g_mkdir_with_parents(CLIENT_CERT_DIR, 0700); + g_mkdir_with_parents(SERVER_CERT_DIR, 0700); unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); @@ -398,7 +398,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); - mkdir(WORKDIR, 0700); + g_mkdir_with_parents(WORKDIR, 0700); test_tls_init(KEYFILE); test_tls_psk_init(PSKFILE); diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 98671f1ac3..227acc5995 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -1,7 +1,7 @@ /* * cutils.c unit-tests * - * Copyright (C) 2013 Red Hat Inc. + * Copyright Red Hat * * Authors: * Eduardo Habkost @@ -25,203 +25,274 @@ * THE SOFTWARE. */ +#include + #include "qemu/osdep.h" -#include "qemu/units.h" #include "qemu/cutils.h" #include "qemu/units.h" static void test_parse_uint_null(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; int r; - r = parse_uint(NULL, &i, &endptr, 0); + r = parse_uint(NULL, &endptr, 0, &i); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); - g_assert(endptr == NULL); + g_assert_cmpuint(i, ==, 0); + g_assert_null(endptr); } static void test_parse_uint_empty(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = ""; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); - g_assert(endptr == str); + g_assert_cmpuint(i, ==, 0); + g_assert_true(endptr == str); } static void test_parse_uint_whitespace(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = " \t "; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); - g_assert(endptr == str); + g_assert_cmpuint(i, ==, 0); + g_assert_true(endptr == str); } static void test_parse_uint_invalid(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = " \t xxx"; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); - g_assert(endptr == str); + g_assert_cmpuint(i, ==, 0); + g_assert_true(endptr == str); } static void test_parse_uint_trailing(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = "123xxx"; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); - g_assert(endptr == str + 3); + g_assert_cmpuint(i, ==, 123); + g_assert_true(endptr == str + 3); } static void test_parse_uint_correct(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = "123"; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_cmpuint(i, ==, 123); + g_assert_true(endptr == str + strlen(str)); } static void test_parse_uint_octal(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = "0123"; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_cmpuint(i, ==, 0123); + g_assert_true(endptr == str + strlen(str)); } static void test_parse_uint_decimal(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = "0123"; int r; - r = parse_uint(str, &i, &endptr, 10); + r = parse_uint(str, &endptr, 10, &i); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_cmpuint(i, ==, 123); + g_assert_true(endptr == str + strlen(str)); } - static void test_parse_uint_llong_max(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; char *str = g_strdup_printf("%llu", (unsigned long long)LLONG_MAX + 1); int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, (unsigned long long)LLONG_MAX + 1); - g_assert(endptr == str + strlen(str)); + g_assert_cmpuint(i, ==, (unsigned long long)LLONG_MAX + 1); + g_assert_true(endptr == str + strlen(str)); + + g_free(str); +} + +static void test_parse_uint_max(void) +{ + uint64_t i = 999; + const char *endptr = "somewhere"; + char *str = g_strdup_printf("%llu", ULLONG_MAX); + int r; + + r = parse_uint(str, &endptr, 0, &i); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpuint(i, ==, ULLONG_MAX); + g_assert_true(endptr == str + strlen(str)); g_free(str); } static void test_parse_uint_overflow(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; - const char *str = "99999999999999999999999999999999999999"; + uint64_t i; + const char *endptr; + const char *str; int r; - r = parse_uint(str, &i, &endptr, 0); - + i = 999; + endptr = "somewhere"; + str = "99999999999999999999999999999999999999"; + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -ERANGE); - g_assert_cmpint(i, ==, ULLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_cmpuint(i, ==, ULLONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + i = 999; + endptr = "somewhere"; + str = "0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + r = parse_uint(str, &endptr, 0, &i); + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpuint(i, ==, ULLONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + i = 999; + endptr = "somewhere"; + str = "0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + r = parse_uint(str, &endptr, 0, &i); + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpuint(i, ==, ULLONG_MAX); + g_assert_true(endptr == str + strlen(str)); } static void test_parse_uint_negative(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; - const char *str = " \t -321"; + uint64_t i; + const char *endptr; + const char *str; int r; - r = parse_uint(str, &i, &endptr, 0); - + i = 999; + endptr = "somewhere"; + str = " \t -321"; + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -ERANGE); - g_assert_cmpint(i, ==, 0); - g_assert(endptr == str + strlen(str)); + g_assert_cmpuint(i, ==, 0); + g_assert_true(endptr == str + strlen(str)); + + i = 999; + endptr = "somewhere"; + str = "-0xffffffff00000001"; + r = parse_uint(str, &endptr, 0, &i); + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpuint(i, ==, 0); + g_assert_true(endptr == str + strlen(str)); } +static void test_parse_uint_negzero(void) +{ + uint64_t i = 999; + const char *endptr = "somewhere"; + const char *str = " -0"; + int r; + + r = parse_uint(str, &endptr, 0, &i); + + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpuint(i, ==, 0); + g_assert_true(endptr == str + strlen(str)); +} static void test_parse_uint_full_trailing(void) { - unsigned long long i = 999; + uint64_t i = 999; const char *str = "123xxx"; int r; - r = parse_uint_full(str, &i, 0); + r = parse_uint_full(str, 0, &i); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); + g_assert_cmpuint(i, ==, 0); } static void test_parse_uint_full_correct(void) { - unsigned long long i = 999; + uint64_t i = 999; const char *str = "123"; int r; - r = parse_uint_full(str, &i, 0); + r = parse_uint_full(str, 0, &i); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); + g_assert_cmpuint(i, ==, 123); +} + +static void test_parse_uint_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + uint64_t i = 999; + const char *str = "-2junk"; + int r; + + r = parse_uint_full(str, 0, &i); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpuint(i, ==, 0); +} + +static void test_parse_uint_full_null(void) +{ + uint64_t i = 999; + const char *str = NULL; + int r; + + r = parse_uint_full(str, 0, &i); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpuint(i, ==, 0); } static void test_qemu_strtoi_correct(void) @@ -236,7 +307,7 @@ static void test_qemu_strtoi_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtoi_null(void) @@ -249,7 +320,8 @@ static void test_qemu_strtoi_null(void) err = qemu_strtoi(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_cmpint(res, ==, 0); + g_assert_null(endptr); } static void test_qemu_strtoi_empty(void) @@ -263,7 +335,8 @@ static void test_qemu_strtoi_empty(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtoi_whitespace(void) @@ -277,7 +350,8 @@ static void test_qemu_strtoi_whitespace(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtoi_invalid(void) @@ -291,7 +365,8 @@ static void test_qemu_strtoi_invalid(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtoi_trailing(void) @@ -306,7 +381,7 @@ static void test_qemu_strtoi_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtoi_octal(void) @@ -321,7 +396,7 @@ static void test_qemu_strtoi_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); res = 999; endptr = &f; @@ -329,7 +404,7 @@ static void test_qemu_strtoi_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi_decimal(void) @@ -344,7 +419,7 @@ static void test_qemu_strtoi_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; res = 999; @@ -353,7 +428,7 @@ static void test_qemu_strtoi_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi_hex(void) @@ -368,7 +443,7 @@ static void test_qemu_strtoi_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; res = 999; @@ -377,7 +452,7 @@ static void test_qemu_strtoi_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; res = 999; @@ -386,7 +461,7 @@ static void test_qemu_strtoi_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtoi_max(void) @@ -401,45 +476,61 @@ static void test_qemu_strtoi_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, INT_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } static void test_qemu_strtoi_overflow(void) { - char *str = g_strdup_printf("%lld", (long long)INT_MAX + 1ll); - char f = 'X'; - const char *endptr = &f; - int res = 999; + const char *str; + const char *endptr; + int res; int err; + str = "2147483648"; /* INT_MAX + 1ll */ + endptr = "somewhere"; + res = 999; err = qemu_strtoi(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, INT_MAX); - g_assert(endptr == str + strlen(str)); - g_free(str); -} - -static void test_qemu_strtoi_underflow(void) -{ - char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll); - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_true(endptr == str + strlen(str)); + str = "0x7fffffffffffffff"; /* LLONG_MAX */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, INT_MIN); - g_assert(endptr == str + strlen(str)); - g_free(str); + g_assert_cmpint(res, ==, INT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x8000000000000000"; /* (uint64_t)LLONG_MIN */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, 32-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MAX); + g_assert_true(endptr == str + strlen(str)); } -static void test_qemu_strtoi_negative(void) +static void test_qemu_strtoi_min(void) { - const char *str = " \t -321"; + char *str = g_strdup_printf("%d", INT_MIN); char f = 'X'; const char *endptr = &f; int res = 999; @@ -447,9 +538,105 @@ static void test_qemu_strtoi_negative(void) err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoi_underflow(void) +{ + const char *str; + const char *endptr; + int res; + int err; + + str = "-2147483649"; /* INT_MIN - 1ll */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x7fffffffffffffff"; /* -LLONG_MAX */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x8000000000000000"; /* (uint64_t)LLONG_MIN */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-18446744073709551615"; /* -UINT64_MAX (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, 32-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_negative(void) +{ + const char *str; + const char *endptr; + int res; + int err; + + str = " \t -321"; + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, -321); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); + + str = "-2147483648"; /* INT_MIN */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi_full_correct(void) @@ -474,18 +661,20 @@ static void test_qemu_strtoi_full_null(void) err = qemu_strtoi(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_cmpint(res, ==, 0); + g_assert_null(endptr); } static void test_qemu_strtoi_full_empty(void) { const char *str = ""; - int res = 999L; + int res = 999; int err; - err = qemu_strtoi(str, NULL, 0, &res); + err = qemu_strtoi(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); } static void test_qemu_strtoi_full_negative(void) @@ -500,21 +689,34 @@ static void test_qemu_strtoi_full_negative(void) g_assert_cmpint(res, ==, -321); } +static void test_qemu_strtoi_full_negzero(void) +{ + const char *str = " -0"; + int res = 999; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); +} + static void test_qemu_strtoi_full_trailing(void) { const char *str = "123xxx"; - int res; + int res = 999; int err; err = qemu_strtoi(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 123); } static void test_qemu_strtoi_full_max(void) { char *str = g_strdup_printf("%d", INT_MAX); - int res; + int res = 999; int err; err = qemu_strtoi(str, NULL, 0, &res); @@ -524,6 +726,19 @@ static void test_qemu_strtoi_full_max(void) g_free(str); } +static void test_qemu_strtoi_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-9999999999junk"; + int res = 999; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, INT_MIN); +} + static void test_qemu_strtoui_correct(void) { const char *str = "12345 foo"; @@ -536,7 +751,7 @@ static void test_qemu_strtoui_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtoui_null(void) @@ -549,7 +764,8 @@ static void test_qemu_strtoui_null(void) err = qemu_strtoui(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_cmpuint(res, ==, 0); + g_assert_null(endptr); } static void test_qemu_strtoui_empty(void) @@ -563,7 +779,8 @@ static void test_qemu_strtoui_empty(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtoui_whitespace(void) @@ -577,7 +794,8 @@ static void test_qemu_strtoui_whitespace(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtoui_invalid(void) @@ -591,7 +809,8 @@ static void test_qemu_strtoui_invalid(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtoui_trailing(void) @@ -606,7 +825,7 @@ static void test_qemu_strtoui_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtoui_octal(void) @@ -621,7 +840,7 @@ static void test_qemu_strtoui_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); res = 999; endptr = &f; @@ -629,7 +848,7 @@ static void test_qemu_strtoui_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoui_decimal(void) @@ -644,7 +863,7 @@ static void test_qemu_strtoui_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; res = 999; @@ -653,7 +872,7 @@ static void test_qemu_strtoui_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoui_hex(void) @@ -668,7 +887,7 @@ static void test_qemu_strtoui_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; res = 999; @@ -677,7 +896,7 @@ static void test_qemu_strtoui_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; res = 999; @@ -686,7 +905,23 @@ static void test_qemu_strtoui_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); +} + +static void test_qemu_strtoui_wrap(void) +{ + /* wraparound is consistent with 32-bit strtoul */ + const char *str = "-4294967295"; /* 1 mod 2^32 */ + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 1); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoui_max(void) @@ -701,40 +936,120 @@ static void test_qemu_strtoui_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, UINT_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } static void test_qemu_strtoui_overflow(void) { - char *str = g_strdup_printf("%lld", (long long)UINT_MAX + 1ll); - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; + const char *str; + const char *endptr; + unsigned int res; int err; + str = "4294967296"; /* UINT_MAX + 1ll */ + endptr = "somewhere"; + res = 999; err = qemu_strtoui(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, UINT_MAX); - g_assert(endptr == str + strlen(str)); - g_free(str); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x7fffffffffffffff"; /* LLONG_MAX */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x8000000000000000"; /* (uint64_t)LLONG_MIN */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0xffffffff00000001"; /* ULLONG_MAX - UINT_MAX + 1 (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0xfffffffffffffffe"; /* ULLONG_MAX - 1 (not UINT_MAX - 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, 32-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoui_underflow(void) { - char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll); - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; + const char *str; + const char *endptr; + unsigned int res; int err; - err = qemu_strtoui(str, &endptr, 0, &res); - + str = "-4294967296"; /* -(long long)UINT_MAX - 1ll */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpuint(res, ==, (unsigned int)-1); - g_assert(endptr == str + strlen(str)); - g_free(str); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-18446744073709551615"; /* -UINT64_MAX (not -(-1)) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-0xffffffff00000002"; + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, 32-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoui_negative(void) @@ -749,7 +1064,22 @@ static void test_qemu_strtoui_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, (unsigned int)-321); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtoui_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoui_full_correct(void) @@ -772,6 +1102,7 @@ static void test_qemu_strtoui_full_null(void) err = qemu_strtoui(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); } static void test_qemu_strtoui_full_empty(void) @@ -783,7 +1114,9 @@ static void test_qemu_strtoui_full_empty(void) err = qemu_strtoui(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); } + static void test_qemu_strtoui_full_negative(void) { const char *str = " \t -321"; @@ -795,15 +1128,27 @@ static void test_qemu_strtoui_full_negative(void) g_assert_cmpuint(res, ==, (unsigned int)-321); } +static void test_qemu_strtoui_full_negzero(void) +{ + const char *str = " -0"; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); +} + static void test_qemu_strtoui_full_trailing(void) { const char *str = "123xxx"; - unsigned int res; + unsigned int res = 999; int err; err = qemu_strtoui(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 123); } static void test_qemu_strtoui_full_max(void) @@ -819,6 +1164,19 @@ static void test_qemu_strtoui_full_max(void) g_free(str); } +static void test_qemu_strtoui_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-9999999999junk"; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, UINT_MAX); +} + static void test_qemu_strtol_correct(void) { const char *str = "12345 foo"; @@ -831,7 +1189,7 @@ static void test_qemu_strtol_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtol_null(void) @@ -844,7 +1202,8 @@ static void test_qemu_strtol_null(void) err = qemu_strtol(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_cmpint(res, ==, 0); + g_assert_null(endptr); } static void test_qemu_strtol_empty(void) @@ -858,7 +1217,8 @@ static void test_qemu_strtol_empty(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtol_whitespace(void) @@ -872,7 +1232,8 @@ static void test_qemu_strtol_whitespace(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtol_invalid(void) @@ -886,7 +1247,8 @@ static void test_qemu_strtol_invalid(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtol_trailing(void) @@ -901,7 +1263,7 @@ static void test_qemu_strtol_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtol_octal(void) @@ -916,7 +1278,7 @@ static void test_qemu_strtol_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); res = 999; endptr = &f; @@ -924,7 +1286,7 @@ static void test_qemu_strtol_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtol_decimal(void) @@ -939,7 +1301,7 @@ static void test_qemu_strtol_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; res = 999; @@ -948,7 +1310,7 @@ static void test_qemu_strtol_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtol_hex(void) @@ -963,7 +1325,7 @@ static void test_qemu_strtol_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; res = 999; @@ -972,7 +1334,7 @@ static void test_qemu_strtol_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; res = 999; @@ -981,7 +1343,7 @@ static void test_qemu_strtol_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtol_max(void) @@ -996,13 +1358,56 @@ static void test_qemu_strtol_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, LONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } static void test_qemu_strtol_overflow(void) { - const char *str = "99999999999999999999999999999999999999999999"; + const char *str; + const char *endptr; + long res; + int err; + + /* 1 more than LONG_MAX */ + str = LONG_MAX == INT_MAX ? "2147483648" : "9223372036854775808"; + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + if (LONG_MAX == INT_MAX) { + str = "0xffffffff00000001"; /* ULLONG_MAX - UINT_MAX + 1 (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MAX); + g_assert_true(endptr == str + strlen(str)); + } + + str = "0x10000000000000000"; /* 65 bits, either sign bit position clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, either sign bit position set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MAX); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_min(void) +{ + char *str = g_strdup_printf("%ld", LONG_MIN); char f = 'X'; const char *endptr = &f; long res = 999; @@ -1010,24 +1415,53 @@ static void test_qemu_strtol_overflow(void) err = qemu_strtol(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, LONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, LONG_MIN); + g_assert_true(endptr == str + strlen(str)); + g_free(str); } static void test_qemu_strtol_underflow(void) { - const char *str = "-99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - long res = 999; + const char *str; + const char *endptr; + long res; int err; - err = qemu_strtol(str, &endptr, 0, &res); - + /* 1 less than LONG_MIN */ + str = LONG_MIN == INT_MIN ? "-2147483649" : "-9223372036854775809"; + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LONG_MIN); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); + + if (LONG_MAX == INT_MAX) { + str = "-18446744073709551615"; /* -UINT64_MAX (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MIN); + g_assert_true(endptr == str + strlen(str)); + } + + str = "-0x10000000000000000"; /* 65 bits, either sign bit position clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, either sign bit position set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MIN); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtol_negative(void) @@ -1042,7 +1476,22 @@ static void test_qemu_strtol_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, -321); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtol_full_correct(void) @@ -1067,7 +1516,8 @@ static void test_qemu_strtol_full_null(void) err = qemu_strtol(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_cmpint(res, ==, 0); + g_assert_null(endptr); } static void test_qemu_strtol_full_empty(void) @@ -1076,9 +1526,10 @@ static void test_qemu_strtol_full_empty(void) long res = 999L; int err; - err = qemu_strtol(str, NULL, 0, &res); + err = qemu_strtol(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); } static void test_qemu_strtol_full_negative(void) @@ -1093,21 +1544,34 @@ static void test_qemu_strtol_full_negative(void) g_assert_cmpint(res, ==, -321); } +static void test_qemu_strtol_full_negzero(void) +{ + const char *str = " -0"; + long res = 999; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); +} + static void test_qemu_strtol_full_trailing(void) { const char *str = "123xxx"; - long res; + long res = 999; int err; err = qemu_strtol(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 123); } static void test_qemu_strtol_full_max(void) { char *str = g_strdup_printf("%ld", LONG_MAX); - long res; + long res = 999; int err; err = qemu_strtol(str, NULL, 0, &res); @@ -1117,6 +1581,19 @@ static void test_qemu_strtol_full_max(void) g_free(str); } +static void test_qemu_strtol_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-99999999999999999999junk"; + long res = 999; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, LONG_MIN); +} + static void test_qemu_strtoul_correct(void) { const char *str = "12345 foo"; @@ -1129,7 +1606,7 @@ static void test_qemu_strtoul_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtoul_null(void) @@ -1142,7 +1619,8 @@ static void test_qemu_strtoul_null(void) err = qemu_strtoul(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_cmpuint(res, ==, 0); + g_assert_null(endptr); } static void test_qemu_strtoul_empty(void) @@ -1156,7 +1634,8 @@ static void test_qemu_strtoul_empty(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtoul_whitespace(void) @@ -1170,7 +1649,8 @@ static void test_qemu_strtoul_whitespace(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtoul_invalid(void) @@ -1184,7 +1664,8 @@ static void test_qemu_strtoul_invalid(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtoul_trailing(void) @@ -1199,7 +1680,7 @@ static void test_qemu_strtoul_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtoul_octal(void) @@ -1214,7 +1695,7 @@ static void test_qemu_strtoul_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); res = 999; endptr = &f; @@ -1222,7 +1703,7 @@ static void test_qemu_strtoul_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_decimal(void) @@ -1237,7 +1718,7 @@ static void test_qemu_strtoul_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; res = 999; @@ -1246,7 +1727,7 @@ static void test_qemu_strtoul_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_hex(void) @@ -1261,7 +1742,7 @@ static void test_qemu_strtoul_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; res = 999; @@ -1270,7 +1751,7 @@ static void test_qemu_strtoul_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; res = 999; @@ -1279,7 +1760,24 @@ static void test_qemu_strtoul_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); +} + +static void test_qemu_strtoul_wrap(void) +{ + const char *str; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + /* 1 mod 2^(sizeof(long)*8) */ + str = LONG_MAX == INT_MAX ? "-4294967295" : "-18446744073709551615"; + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 1); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_max(void) @@ -1294,38 +1792,94 @@ static void test_qemu_strtoul_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, ULONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } static void test_qemu_strtoul_overflow(void) { - const char *str = "99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; + const char *str; + const char *endptr; + unsigned long res; int err; + /* 1 more than ULONG_MAX */ + str = ULONG_MAX == UINT_MAX ? "4294967296" : "18446744073709551616"; + endptr = "somewhere"; + res = 999; err = qemu_strtoul(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, ULONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + if (LONG_MAX == INT_MAX) { + str = "0xffffffff00000001"; /* UINT64_MAX - UINT_MAX + 1 (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + } + + str = "0x10000000000000000"; /* 65 bits, either sign bit position clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, either sign bit position set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_underflow(void) { - const char *str = "-99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; + const char *str; + const char *endptr; + unsigned long res; int err; - err = qemu_strtoul(str, &endptr, 0, &res); - + /* 1 less than -ULONG_MAX */ + str = ULONG_MAX == UINT_MAX ? "-4294967296" : "-18446744073709551616"; + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpuint(res, ==, -1ul); - g_assert(endptr == str + strlen(str)); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + if (LONG_MAX == INT_MAX) { + str = "-0xffffffff00000002"; + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + } + + str = "-0x10000000000000000"; /* 65 bits, either sign bit position clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, either sign bit position set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_negative(void) @@ -1340,7 +1894,22 @@ static void test_qemu_strtoul_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, -321ul); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtoul_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_full_correct(void) @@ -1363,6 +1932,7 @@ static void test_qemu_strtoul_full_null(void) err = qemu_strtoul(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); } static void test_qemu_strtoul_full_empty(void) @@ -1374,7 +1944,9 @@ static void test_qemu_strtoul_full_empty(void) err = qemu_strtoul(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); } + static void test_qemu_strtoul_full_negative(void) { const char *str = " \t -321"; @@ -1386,15 +1958,27 @@ static void test_qemu_strtoul_full_negative(void) g_assert_cmpuint(res, ==, -321ul); } +static void test_qemu_strtoul_full_negzero(void) +{ + const char *str = " -0"; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); +} + static void test_qemu_strtoul_full_trailing(void) { const char *str = "123xxx"; - unsigned long res; + unsigned long res = 999; int err; err = qemu_strtoul(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 123); } static void test_qemu_strtoul_full_max(void) @@ -1410,6 +1994,19 @@ static void test_qemu_strtoul_full_max(void) g_free(str); } +static void test_qemu_strtoul_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-99999999999999999999junk"; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, ULONG_MAX); +} + static void test_qemu_strtoi64_correct(void) { const char *str = "12345 foo"; @@ -1422,7 +2019,7 @@ static void test_qemu_strtoi64_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtoi64_null(void) @@ -1435,7 +2032,8 @@ static void test_qemu_strtoi64_null(void) err = qemu_strtoi64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_cmpint(res, ==, 0); + g_assert_null(endptr); } static void test_qemu_strtoi64_empty(void) @@ -1449,7 +2047,8 @@ static void test_qemu_strtoi64_empty(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtoi64_whitespace(void) @@ -1463,7 +2062,8 @@ static void test_qemu_strtoi64_whitespace(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtoi64_invalid(void) @@ -1477,7 +2077,8 @@ static void test_qemu_strtoi64_invalid(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtoi64_trailing(void) @@ -1492,7 +2093,7 @@ static void test_qemu_strtoi64_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtoi64_octal(void) @@ -1507,7 +2108,7 @@ static void test_qemu_strtoi64_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); endptr = &f; res = 999; @@ -1515,7 +2116,7 @@ static void test_qemu_strtoi64_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi64_decimal(void) @@ -1530,7 +2131,7 @@ static void test_qemu_strtoi64_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; endptr = &f; @@ -1539,7 +2140,7 @@ static void test_qemu_strtoi64_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi64_hex(void) @@ -1554,7 +2155,7 @@ static void test_qemu_strtoi64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; endptr = &f; @@ -1563,7 +2164,7 @@ static void test_qemu_strtoi64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; endptr = &f; @@ -1572,7 +2173,7 @@ static void test_qemu_strtoi64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtoi64_max(void) @@ -1587,13 +2188,45 @@ static void test_qemu_strtoi64_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, LLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } static void test_qemu_strtoi64_overflow(void) { - const char *str = "99999999999999999999999999999999999999999999"; + const char *str; + const char *endptr; + int64_t res; + int err; + + str = "9223372036854775808"; /* 1 more than INT64_MAX */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MAX); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi64_min(void) +{ + char *str = g_strdup_printf("%lld", LLONG_MIN); char f = 'X'; const char *endptr = &f; int64_t res = 999; @@ -1601,24 +2234,42 @@ static void test_qemu_strtoi64_overflow(void) err = qemu_strtoi64(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, LLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, LLONG_MIN); + g_assert_true(endptr == str + strlen(str)); + g_free(str); } static void test_qemu_strtoi64_underflow(void) { - const char *str = "-99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; + const char *str; + const char *endptr; + int64_t res; int err; - err = qemu_strtoi64(str, &endptr, 0, &res); - + str = "-9223372036854775809"; /* 1 less than INT64_MIN */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, LLONG_MIN); - g_assert(endptr == str + strlen(str)); + g_assert_cmpint(res, ==, INT64_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MIN); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi64_negative(void) @@ -1633,7 +2284,22 @@ static void test_qemu_strtoi64_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, -321); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi64_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi64_full_correct(void) @@ -1656,6 +2322,7 @@ static void test_qemu_strtoi64_full_null(void) err = qemu_strtoi64(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); } static void test_qemu_strtoi64_full_empty(void) @@ -1667,6 +2334,7 @@ static void test_qemu_strtoi64_full_empty(void) err = qemu_strtoi64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); } static void test_qemu_strtoi64_full_negative(void) @@ -1681,6 +2349,18 @@ static void test_qemu_strtoi64_full_negative(void) g_assert_cmpint(res, ==, -321); } +static void test_qemu_strtoi64_full_negzero(void) +{ + const char *str = " -0"; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); +} + static void test_qemu_strtoi64_full_trailing(void) { const char *str = "123xxx"; @@ -1690,13 +2370,14 @@ static void test_qemu_strtoi64_full_trailing(void) err = qemu_strtoi64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 123); } static void test_qemu_strtoi64_full_max(void) { char *str = g_strdup_printf("%lld", LLONG_MAX); - int64_t res; + int64_t res = 999; int err; err = qemu_strtoi64(str, NULL, 0, &res); @@ -1706,6 +2387,19 @@ static void test_qemu_strtoi64_full_max(void) g_free(str); } +static void test_qemu_strtoi64_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-99999999999999999999junk"; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, INT64_MIN); +} + static void test_qemu_strtou64_correct(void) { const char *str = "12345 foo"; @@ -1718,7 +2412,7 @@ static void test_qemu_strtou64_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtou64_null(void) @@ -1731,7 +2425,8 @@ static void test_qemu_strtou64_null(void) err = qemu_strtou64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_cmpuint(res, ==, 0); + g_assert_null(endptr); } static void test_qemu_strtou64_empty(void) @@ -1745,7 +2440,8 @@ static void test_qemu_strtou64_empty(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtou64_whitespace(void) @@ -1759,7 +2455,8 @@ static void test_qemu_strtou64_whitespace(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtou64_invalid(void) @@ -1773,7 +2470,8 @@ static void test_qemu_strtou64_invalid(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str); } static void test_qemu_strtou64_trailing(void) @@ -1788,7 +2486,7 @@ static void test_qemu_strtou64_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtou64_octal(void) @@ -1803,7 +2501,7 @@ static void test_qemu_strtou64_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); endptr = &f; res = 999; @@ -1811,7 +2509,7 @@ static void test_qemu_strtou64_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_decimal(void) @@ -1826,7 +2524,7 @@ static void test_qemu_strtou64_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; endptr = &f; @@ -1835,7 +2533,7 @@ static void test_qemu_strtou64_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_hex(void) @@ -1850,7 +2548,7 @@ static void test_qemu_strtou64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; endptr = &f; @@ -1859,7 +2557,7 @@ static void test_qemu_strtou64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; endptr = &f; @@ -1868,7 +2566,22 @@ static void test_qemu_strtou64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); +} + +static void test_qemu_strtou64_wrap(void) +{ + const char *str = "-18446744073709551615"; /* 1 mod 2^64 */ + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 1); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_max(void) @@ -1883,38 +2596,72 @@ static void test_qemu_strtou64_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, ULLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } static void test_qemu_strtou64_overflow(void) { - const char *str = "99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; + const char *str; + const char *endptr; + uint64_t res; int err; + str = "18446744073709551616"; /* 1 more than UINT64_MAX */ + endptr = "somewhere"; + res = 999; err = qemu_strtou64(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, ULLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_cmpuint(res, ==, UINT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT64_MAX); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_underflow(void) { - const char *str = "-99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; + const char *str; + const char *endptr; + uint64_t res; int err; - err = qemu_strtou64(str, &endptr, 0, &res); - + str = "-99999999999999999999999999999999999999999999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, -1ull); - g_assert(endptr == str + strlen(str)); + g_assert_cmpuint(res, ==, UINT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT64_MAX); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_negative(void) @@ -1929,7 +2676,22 @@ static void test_qemu_strtou64_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, -321ull); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtou64_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_full_correct(void) @@ -1952,6 +2714,7 @@ static void test_qemu_strtou64_full_null(void) err = qemu_strtou64(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); } static void test_qemu_strtou64_full_empty(void) @@ -1963,6 +2726,7 @@ static void test_qemu_strtou64_full_empty(void) err = qemu_strtou64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); } static void test_qemu_strtou64_full_negative(void) @@ -1977,6 +2741,18 @@ static void test_qemu_strtou64_full_negative(void) g_assert_cmpuint(res, ==, -321ull); } +static void test_qemu_strtou64_full_negzero(void) +{ + const char *str = " -0"; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); +} + static void test_qemu_strtou64_full_trailing(void) { const char *str = "18446744073709551614xxxxxx"; @@ -1986,6 +2762,7 @@ static void test_qemu_strtou64_full_trailing(void) err = qemu_strtou64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 18446744073709551614ULL); } static void test_qemu_strtou64_full_max(void) @@ -2001,453 +2778,852 @@ static void test_qemu_strtou64_full_max(void) g_free(str); } -static void test_qemu_strtosz_simple(void) +static void test_qemu_strtou64_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-99999999999999999999junk"; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, UINT64_MAX); +} + +static void test_qemu_strtod_simple(void) { const char *str; const char *endptr; int err; - uint64_t res; + double res; - str = "0"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); + /* no radix or exponent */ + str = "1"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_true(endptr == str + 1); + + /* leading space and sign */ + str = " -0.0"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, -0.0); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 5); + + /* fraction only */ + str = "+.5"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 0.5); + g_assert_true(endptr == str + 3); + + /* exponent */ + str = "1.e+1"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 10.0); + g_assert_true(endptr == str + 5); + + /* hex without radix */ + str = "0x10"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 16.0); + g_assert_true(endptr == str + 4); +} + +static void test_qemu_strtod_einval(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* empty */ + str = ""; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); + + /* NULL */ + str = NULL; + endptr = "random"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_null(endptr); + + /* not recognizable */ + str = " junk"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); +} + +static void test_qemu_strtod_erange(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* overflow */ + str = "9e999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, ==, HUGE_VAL); + g_assert_true(endptr == str + 5); + + str = "-9e+999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, ==, -HUGE_VAL); + g_assert_true(endptr == str + 7); + + /* underflow */ + str = "-9e-999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, >=, -DBL_MIN); + g_assert_cmpfloat(res, <=, -0.0); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 7); +} + +static void test_qemu_strtod_nonfinite(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* infinity */ + str = "inf"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_true(isinf(res)); + g_assert_false(signbit(res)); + g_assert_true(endptr == str + 3); + + str = "-infinity"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_true(isinf(res)); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 9); + + /* not a number */ + str = " NaN"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_true(isnan(res)); + g_assert_true(endptr == str + 4); +} + +static void test_qemu_strtod_trailing(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* trailing whitespace */ + str = "1. "; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_true(endptr == str + 2); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 1.0); + + /* trailing e is not an exponent */ + str = ".5e"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 0.5); + g_assert_true(endptr == str + 2); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.5); + + /* trailing ( not part of long NaN */ + str = "nan("; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_true(isnan(res)); + g_assert_true(endptr == str + 3); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_true(isnan(res)); +} + +static void test_qemu_strtod_erange_junk(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* ERANGE with trailing junk... */ + str = "1e-999junk"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, <=, DBL_MIN); + g_assert_cmpfloat(res, >=, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str + 6); + + /* ...has less priority than EINVAL when full parse not possible */ + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); +} + +static void test_qemu_strtod_finite_simple(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* no radix or exponent */ + str = "1"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_true(endptr == str + 1); + + /* leading space and sign */ + str = " -0.0"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, -0.0); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 5); + + /* fraction only */ + str = "+.5"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 0.5); + g_assert_true(endptr == str + 3); + + /* exponent */ + str = "1.e+1"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 10.0); + g_assert_true(endptr == str + 5); + + /* hex without radix */ + str = "0x10"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 16.0); + g_assert_true(endptr == str + 4); +} + +static void test_qemu_strtod_finite_einval(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* empty */ + str = ""; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); + + /* NULL */ + str = NULL; + endptr = "random"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_null(endptr); + + /* not recognizable */ + str = " junk"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); +} + +static void test_qemu_strtod_finite_erange(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* overflow turns into EINVAL */ + str = "9e999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); + + str = "-9e+999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); + + /* underflow is still possible */ + str = "-9e-999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, >=, -DBL_MIN); + g_assert_cmpfloat(res, <=, -0.0); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 7); +} + +static void test_qemu_strtod_finite_nonfinite(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* infinity */ + str = "inf"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); + + str = "-infinity"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); + + /* not a number */ + str = " NaN"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); +} + +static void test_qemu_strtod_finite_trailing(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* trailing whitespace */ + str = "1. "; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_true(endptr == str + 2); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_false(signbit(res)); + + /* trailing e is not an exponent */ + str = ".5e"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 0.5); + g_assert_true(endptr == str + 2); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.5); + + /* trailing ( not part of long NaN */ + str = "nan("; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); +} + +static void test_qemu_strtod_finite_erange_junk(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* ERANGE with trailing junk... */ + str = "1e-999junk"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, <=, DBL_MIN); + g_assert_cmpfloat(res, >=, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str + 6); + + /* ...has less priority than EINVAL when full parse not possible */ + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); +} + +typedef int (*qemu_strtosz_fn)(const char *, const char **, uint64_t *); +static void do_strtosz_full(const char *str, qemu_strtosz_fn fn, + int exp_ptr_ret, uint64_t exp_ptr_val, + size_t exp_ptr_offset, int exp_null_ret, + uint64_t exp_null_val) +{ + const char *endptr = "somewhere"; + uint64_t val = 0xbaadf00d; + int ret; + + ret = fn(str, &endptr, &val); + g_assert_cmpint(ret, ==, exp_ptr_ret); + g_assert_cmpuint(val, ==, exp_ptr_val); + if (str) { + g_assert_true(endptr == str + exp_ptr_offset); + } else { + g_assert_cmpint(exp_ptr_offset, ==, 0); + g_assert_null(endptr); + } + + val = 0xbaadf00d; + ret = fn(str, NULL, &val); + g_assert_cmpint(ret, ==, exp_null_ret); + g_assert_cmpuint(val, ==, exp_null_val); +} + +static void do_strtosz(const char *str, int exp_ret, uint64_t exp_val, + size_t exp_offset) +{ + do_strtosz_full(str, qemu_strtosz, exp_ret, exp_val, exp_offset, + exp_ret, exp_val); +} + +static void do_strtosz_MiB(const char *str, int exp_ret, uint64_t exp_val, + size_t exp_offset) +{ + do_strtosz_full(str, qemu_strtosz_MiB, exp_ret, exp_val, exp_offset, + exp_ret, exp_val); +} + +static void do_strtosz_metric(const char *str, int exp_ret, uint64_t exp_val, + size_t exp_offset) +{ + do_strtosz_full(str, qemu_strtosz_metric, exp_ret, exp_val, exp_offset, + exp_ret, exp_val); +} + +static void test_qemu_strtosz_simple(void) +{ + do_strtosz("0", 0, 0, 1); /* Leading 0 gives decimal results, not octal */ - str = "08"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 8); - g_assert(endptr == str + 2); + do_strtosz("08", 0, 8, 2); - /* Leading space is ignored */ - str = " 12345"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 6); + /* Leading space and + are ignored */ + do_strtosz(" +12345", 0, 12345, 7); - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345); + /* 2^53-1 */ + do_strtosz("9007199254740991", 0, 0x1fffffffffffffULL, 16); - str = "9007199254740991"; /* 2^53-1 */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x1fffffffffffff); - g_assert(endptr == str + 16); + /* 2^53 */ + do_strtosz("9007199254740992", 0, 0x20000000000000ULL, 16); - str = "9007199254740992"; /* 2^53 */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x20000000000000); - g_assert(endptr == str + 16); + /* 2^53+1 */ + do_strtosz("9007199254740993", 0, 0x20000000000001ULL, 16); - str = "9007199254740993"; /* 2^53+1 */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x20000000000001); - g_assert(endptr == str + 16); + /* 0xfffffffffffff800 (53 msbs set) */ + do_strtosz("18446744073709549568", 0, 0xfffffffffffff800ULL, 20); - str = "18446744073709549568"; /* 0xfffffffffffff800 (53 msbs set) */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0xfffffffffffff800); - g_assert(endptr == str + 20); + /* 0xfffffffffffffbff */ + do_strtosz("18446744073709550591", 0, 0xfffffffffffffbffULL, 20); - str = "18446744073709550591"; /* 0xfffffffffffffbff */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0xfffffffffffffbff); - g_assert(endptr == str + 20); - - str = "18446744073709551615"; /* 0xffffffffffffffff */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0xffffffffffffffff); - g_assert(endptr == str + 20); + /* 0xffffffffffffffff */ + do_strtosz("18446744073709551615", 0, 0xffffffffffffffffULL, 20); } static void test_qemu_strtosz_hex(void) { - const char *str; - const char *endptr; - int err; - uint64_t res; + do_strtosz("0x0", 0, 0, 3); - str = "0x0"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 3); + do_strtosz("0xab", 0, 171, 4); - str = "0xab"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 171); - g_assert(endptr == str + 4); - - str = "0xae"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 174); - g_assert(endptr == str + 4); + do_strtosz(" +0xae", 0, 174, 6); } static void test_qemu_strtosz_units(void) { - const char *none = "1"; - const char *b = "1B"; - const char *k = "1K"; - const char *m = "1M"; - const char *g = "1G"; - const char *t = "1T"; - const char *p = "1P"; - const char *e = "1E"; - int err; - const char *endptr; - uint64_t res; + /* default scale depends on function */ + do_strtosz("1", 0, 1, 1); + do_strtosz_MiB("1", 0, MiB, 1); + do_strtosz_metric("1", 0, 1, 1); - /* default is M */ - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz_MiB(none, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, MiB); - g_assert(endptr == none + 1); + /* Explicit byte suffix works for all functions */ + do_strtosz("1B", 0, 1, 2); + do_strtosz_MiB("1B", 0, 1, 2); + do_strtosz_metric("1B", 0, 1, 2); - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(b, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 1); - g_assert(endptr == b + 2); + /* Expose the scale */ + do_strtosz("1K", 0, KiB, 2); + do_strtosz_MiB("1K", 0, KiB, 2); + do_strtosz_metric("1K", 0, 1000, 2); - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(k, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, KiB); - g_assert(endptr == k + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(m, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, MiB); - g_assert(endptr == m + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(g, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, GiB); - g_assert(endptr == g + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(t, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, TiB); - g_assert(endptr == t + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(p, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, PiB); - g_assert(endptr == p + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(e, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, EiB); - g_assert(endptr == e + 2); + /* Other suffixes, see also test_qemu_strtosz_metric */ + do_strtosz("1M", 0, MiB, 2); + do_strtosz("1G", 0, GiB, 2); + do_strtosz("1T", 0, TiB, 2); + do_strtosz("1P", 0, PiB, 2); + do_strtosz("1E", 0, EiB, 2); } static void test_qemu_strtosz_float(void) { - const char *str; - int err; - const char *endptr; - uint64_t res; + do_strtosz("0.5E", 0, EiB / 2, 4); - str = "0.5E"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, EiB / 2); - g_assert(endptr == str + 4); + /* Implied M suffix okay */ + do_strtosz_MiB("0.5", 0, MiB / 2, 3); /* For convenience, a fraction of 0 is tolerated even on bytes */ - str = "1.0B"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 1); - g_assert(endptr == str + 4); + do_strtosz("1.0B", 0, 1, 4); - /* An empty fraction is tolerated */ - str = "1.k"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 1024); - g_assert(endptr == str + 3); + /* An empty fraction tail is tolerated */ + do_strtosz("1.k", 0, 1024, 3); + + /* An empty fraction head is tolerated */ + do_strtosz(" .5k", 0, 512, 4); /* For convenience, we permit values that are not byte-exact */ - str = "12.345M"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, (uint64_t) (12.345 * MiB + 0.5)); - g_assert(endptr == str + 7); + do_strtosz("12.345M", 0, (uint64_t) (12.345 * MiB + 0.5), 7); + + /* Fraction tail can round up */ + do_strtosz("1.9999k", 0, 2048, 7); + do_strtosz("1.9999999999999999999999999999999999999999999999999999k", 0, + 2048, 55); + + /* ERANGE underflow in the fraction tail does not matter for 'k' */ + do_strtosz("1." + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "1k", 0, 1024, 354); } static void test_qemu_strtosz_invalid(void) { - const char *str; - const char *endptr; - int err; - uint64_t res = 0xbaadf00d; + do_strtosz(NULL, -EINVAL, 0, 0); - str = ""; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + /* Must parse at least one digit */ + do_strtosz("", -EINVAL, 0, 0); + do_strtosz(" \t ", -EINVAL, 0, 0); + do_strtosz(".", -EINVAL, 0, 0); + do_strtosz(" .", -EINVAL, 0, 0); + do_strtosz(" .k", -EINVAL, 0, 0); + do_strtosz("inf", -EINVAL, 0, 0); + do_strtosz("NaN", -EINVAL, 0, 0); - str = " \t "; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); - - str = "crap"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); - - str = "inf"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); - - str = "NaN"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + /* Lone suffix is not okay */ + do_strtosz("k", -EINVAL, 0, 0); + do_strtosz(" M", -EINVAL, 0, 0); /* Fractional values require scale larger than bytes */ - str = "1.1B"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + do_strtosz("1.1B", -EINVAL, 0, 0); + do_strtosz("1.1", -EINVAL, 0, 0); - str = "1.1"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); - - /* No floating point exponents */ - str = "1.5e1k"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); - - str = "1.5E+0k"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + /* 'B' cannot have any nonzero fraction, even with rounding or underflow */ + do_strtosz("1.00001B", -EINVAL, 0, 0); + do_strtosz("1.00000000000000000001B", -EINVAL, 0, 0); + do_strtosz("1." + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "1B", -EINVAL, 0, 0); /* No hex fractions */ - str = "0x1.8k"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + do_strtosz("0x1.8k", -EINVAL, 0, 0); + do_strtosz("0x1.k", -EINVAL, 0, 0); - /* No negative values */ - str = "-0"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + /* No hex suffixes */ + do_strtosz("0x18M", -EINVAL, 0, 0); + do_strtosz("0x1p1", -EINVAL, 0, 0); - str = "-1"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + /* decimal in place of scaling suffix */ + do_strtosz("1.1.k", -EINVAL, 0, 0); + do_strtosz("1.1.", -EINVAL, 0, 0); } static void test_qemu_strtosz_trailing(void) { - const char *str; - const char *endptr; - int err; - uint64_t res; + /* Trailing whitespace */ + do_strtosz_full("1k ", qemu_strtosz, 0, 1024, 2, -EINVAL, 0); - str = "123xxx"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz_MiB(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123 * MiB); - g_assert(endptr == str + 3); + /* Unknown suffix overrides even implied scale*/ + do_strtosz_full("123xxx", qemu_strtosz, 0, 123, 3, -EINVAL, 0); - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + /* Implied scale allows partial parse */ + do_strtosz_full("123xxx", qemu_strtosz_MiB, 0, 123 * MiB, 3, -EINVAL, 0); + do_strtosz_full("1.5.k", qemu_strtosz_MiB, 0, 1.5 * MiB, 3, -EINVAL, 0); - str = "1kiB"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 1024); - g_assert(endptr == str + 2); + /* Junk after one-byte suffix */ + do_strtosz_full("1kiB", qemu_strtosz, 0, 1024, 2, -EINVAL, 0); - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + /* Incomplete hex is an unknown suffix */ + do_strtosz_full("0x", qemu_strtosz, 0, 0, 1, -EINVAL, 0); - str = "0x"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + /* Hex literals use only one leading zero */ + do_strtosz_full("00x1", qemu_strtosz, 0, 0, 2, -EINVAL, 0); - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + /* No support for binary literals; 'b' is valid suffix */ + do_strtosz_full("0b1000", qemu_strtosz, 0, 0, 2, -EINVAL, 0); - str = "0.NaN"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 2); + /* Junk after decimal */ + do_strtosz_full("0.NaN", qemu_strtosz, 0, 0, 2, -EINVAL, 0); - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + /* Although negatives are invalid, '-' may be in trailing junk */ + do_strtosz_full("123-45", qemu_strtosz, 0, 123, 3, -EINVAL, 0); + do_strtosz_full(" 123 - 45", qemu_strtosz, 0, 123, 4, -EINVAL, 0); - str = "123-45"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); - - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + /* Parse stops at 'e', which is not a floating point exponent */ + do_strtosz_full("1.5e1k", qemu_strtosz, 0, EiB * 1.5, 4, -EINVAL, 0); + do_strtosz_full("1.5E+0k", qemu_strtosz, 0, EiB * 1.5, 4, -EINVAL, 0); + do_strtosz_full("1.5E999", qemu_strtosz, 0, EiB * 1.5, 4, -EINVAL, 0); } static void test_qemu_strtosz_erange(void) { - const char *str; - const char *endptr; - int err; - uint64_t res = 0xbaadf00d; + /* no negative values */ + do_strtosz(" -0", -ERANGE, 0, 3); + do_strtosz("-1", -ERANGE, 0, 2); + do_strtosz_full("-2M", qemu_strtosz, -ERANGE, 0, 2, -EINVAL, 0); + do_strtosz(" -.0", -ERANGE, 0, 4); + do_strtosz_full("-.1k", qemu_strtosz, -ERANGE, 0, 3, -EINVAL, 0); + do_strtosz_full(" -." + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "1M", qemu_strtosz, -ERANGE, 0, 354, -EINVAL, 0); - str = "18446744073709551616"; /* 2^64; see strtosz_simple for 2^64-1 */ - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str + 20); + /* 2^64; see strtosz_simple for 2^64-1 */ + do_strtosz("18446744073709551616", -ERANGE, 0, 20); - str = "20E"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str + 3); + do_strtosz("20E", -ERANGE, 0, 3); + + /* Fraction tail can cause ERANGE overflow */ + do_strtosz("15.9999999999999999999999999999999999999999999999999999E", + -ERANGE, 0, 56); + + /* EINVAL has priority over ERANGE */ + do_strtosz_full("100000Pjunk", qemu_strtosz, -ERANGE, 0, 7, -EINVAL, 0); } static void test_qemu_strtosz_metric(void) { - const char *str; - int err; - const char *endptr; - uint64_t res; + do_strtosz_metric("12345k", 0, 12345000, 6); + do_strtosz_metric("12.345M", 0, 12345000, 7); - str = "12345k"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz_metric(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345000); - g_assert(endptr == str + 6); + /* Fraction is affected by floating-point rounding */ + /* This would be 0xfffffffffffffbff with infinite precision */ + do_strtosz_metric("18.446744073709550591E", 0, 0xfffffffffffffc0cULL, 22); +} - str = "12.345M"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz_metric(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345000); - g_assert(endptr == str + 7); +static void test_freq_to_str(void) +{ + char *str; + + str = freq_to_str(999); + g_assert_cmpstr(str, ==, "999 Hz"); + g_free(str); + + str = freq_to_str(1000); + g_assert_cmpstr(str, ==, "1 KHz"); + g_free(str); + + str = freq_to_str(1010); + g_assert_cmpstr(str, ==, "1.01 KHz"); + g_free(str); +} + +static void test_size_to_str(void) +{ + char *str; + + str = size_to_str(0); + g_assert_cmpstr(str, ==, "0 B"); + g_free(str); + + str = size_to_str(1); + g_assert_cmpstr(str, ==, "1 B"); + g_free(str); + + str = size_to_str(1016); + g_assert_cmpstr(str, ==, "0.992 KiB"); + g_free(str); + + str = size_to_str(1024); + g_assert_cmpstr(str, ==, "1 KiB"); + g_free(str); + + str = size_to_str(512ull << 20); + g_assert_cmpstr(str, ==, "512 MiB"); + g_free(str); +} + +static void test_iec_binary_prefix(void) +{ + g_assert_cmpstr(iec_binary_prefix(0), ==, ""); + g_assert_cmpstr(iec_binary_prefix(10), ==, "Ki"); + g_assert_cmpstr(iec_binary_prefix(20), ==, "Mi"); + g_assert_cmpstr(iec_binary_prefix(30), ==, "Gi"); + g_assert_cmpstr(iec_binary_prefix(40), ==, "Ti"); + g_assert_cmpstr(iec_binary_prefix(50), ==, "Pi"); + g_assert_cmpstr(iec_binary_prefix(60), ==, "Ei"); +} + +static void test_si_prefix(void) +{ + g_assert_cmpstr(si_prefix(-18), ==, "a"); + g_assert_cmpstr(si_prefix(-15), ==, "f"); + g_assert_cmpstr(si_prefix(-12), ==, "p"); + g_assert_cmpstr(si_prefix(-9), ==, "n"); + g_assert_cmpstr(si_prefix(-6), ==, "u"); + g_assert_cmpstr(si_prefix(-3), ==, "m"); + g_assert_cmpstr(si_prefix(0), ==, ""); + g_assert_cmpstr(si_prefix(3), ==, "K"); + g_assert_cmpstr(si_prefix(6), ==, "M"); + g_assert_cmpstr(si_prefix(9), ==, "G"); + g_assert_cmpstr(si_prefix(12), ==, "T"); + g_assert_cmpstr(si_prefix(15), ==, "P"); + g_assert_cmpstr(si_prefix(18), ==, "E"); } int main(int argc, char **argv) @@ -2464,12 +3640,18 @@ int main(int argc, char **argv) g_test_add_func("/cutils/parse_uint/octal", test_parse_uint_octal); g_test_add_func("/cutils/parse_uint/decimal", test_parse_uint_decimal); g_test_add_func("/cutils/parse_uint/llong_max", test_parse_uint_llong_max); + g_test_add_func("/cutils/parse_uint/max", test_parse_uint_max); g_test_add_func("/cutils/parse_uint/overflow", test_parse_uint_overflow); g_test_add_func("/cutils/parse_uint/negative", test_parse_uint_negative); + g_test_add_func("/cutils/parse_uint/negzero", test_parse_uint_negzero); g_test_add_func("/cutils/parse_uint_full/trailing", test_parse_uint_full_trailing); g_test_add_func("/cutils/parse_uint_full/correct", test_parse_uint_full_correct); + g_test_add_func("/cutils/parse_uint_full/erange_junk", + test_parse_uint_full_erange_junk); + g_test_add_func("/cutils/parse_uint_full/null", + test_parse_uint_full_null); /* qemu_strtoi() tests */ g_test_add_func("/cutils/qemu_strtoi/correct", @@ -2494,10 +3676,14 @@ int main(int argc, char **argv) test_qemu_strtoi_max); g_test_add_func("/cutils/qemu_strtoi/overflow", test_qemu_strtoi_overflow); + g_test_add_func("/cutils/qemu_strtoi/min", + test_qemu_strtoi_min); g_test_add_func("/cutils/qemu_strtoi/underflow", test_qemu_strtoi_underflow); g_test_add_func("/cutils/qemu_strtoi/negative", test_qemu_strtoi_negative); + g_test_add_func("/cutils/qemu_strtoi/negzero", + test_qemu_strtoi_negzero); g_test_add_func("/cutils/qemu_strtoi_full/correct", test_qemu_strtoi_full_correct); g_test_add_func("/cutils/qemu_strtoi_full/null", @@ -2506,10 +3692,14 @@ int main(int argc, char **argv) test_qemu_strtoi_full_empty); g_test_add_func("/cutils/qemu_strtoi_full/negative", test_qemu_strtoi_full_negative); + g_test_add_func("/cutils/qemu_strtoi_full/negzero", + test_qemu_strtoi_full_negzero); g_test_add_func("/cutils/qemu_strtoi_full/trailing", test_qemu_strtoi_full_trailing); g_test_add_func("/cutils/qemu_strtoi_full/max", test_qemu_strtoi_full_max); + g_test_add_func("/cutils/qemu_strtoi_full/erange_junk", + test_qemu_strtoi_full_erange_junk); /* qemu_strtoui() tests */ g_test_add_func("/cutils/qemu_strtoui/correct", @@ -2530,6 +3720,8 @@ int main(int argc, char **argv) test_qemu_strtoui_decimal); g_test_add_func("/cutils/qemu_strtoui/hex", test_qemu_strtoui_hex); + g_test_add_func("/cutils/qemu_strtoui/wrap", + test_qemu_strtoui_wrap); g_test_add_func("/cutils/qemu_strtoui/max", test_qemu_strtoui_max); g_test_add_func("/cutils/qemu_strtoui/overflow", @@ -2538,6 +3730,8 @@ int main(int argc, char **argv) test_qemu_strtoui_underflow); g_test_add_func("/cutils/qemu_strtoui/negative", test_qemu_strtoui_negative); + g_test_add_func("/cutils/qemu_strtoui/negzero", + test_qemu_strtoui_negzero); g_test_add_func("/cutils/qemu_strtoui_full/correct", test_qemu_strtoui_full_correct); g_test_add_func("/cutils/qemu_strtoui_full/null", @@ -2546,10 +3740,14 @@ int main(int argc, char **argv) test_qemu_strtoui_full_empty); g_test_add_func("/cutils/qemu_strtoui_full/negative", test_qemu_strtoui_full_negative); + g_test_add_func("/cutils/qemu_strtoui_full/negzero", + test_qemu_strtoui_full_negzero); g_test_add_func("/cutils/qemu_strtoui_full/trailing", test_qemu_strtoui_full_trailing); g_test_add_func("/cutils/qemu_strtoui_full/max", test_qemu_strtoui_full_max); + g_test_add_func("/cutils/qemu_strtoui_full/erange_junk", + test_qemu_strtoui_full_erange_junk); /* qemu_strtol() tests */ g_test_add_func("/cutils/qemu_strtol/correct", @@ -2574,10 +3772,14 @@ int main(int argc, char **argv) test_qemu_strtol_max); g_test_add_func("/cutils/qemu_strtol/overflow", test_qemu_strtol_overflow); + g_test_add_func("/cutils/qemu_strtol/min", + test_qemu_strtol_min); g_test_add_func("/cutils/qemu_strtol/underflow", test_qemu_strtol_underflow); g_test_add_func("/cutils/qemu_strtol/negative", test_qemu_strtol_negative); + g_test_add_func("/cutils/qemu_strtol/negzero", + test_qemu_strtol_negzero); g_test_add_func("/cutils/qemu_strtol_full/correct", test_qemu_strtol_full_correct); g_test_add_func("/cutils/qemu_strtol_full/null", @@ -2586,10 +3788,14 @@ int main(int argc, char **argv) test_qemu_strtol_full_empty); g_test_add_func("/cutils/qemu_strtol_full/negative", test_qemu_strtol_full_negative); + g_test_add_func("/cutils/qemu_strtol_full/negzero", + test_qemu_strtol_full_negzero); g_test_add_func("/cutils/qemu_strtol_full/trailing", test_qemu_strtol_full_trailing); g_test_add_func("/cutils/qemu_strtol_full/max", test_qemu_strtol_full_max); + g_test_add_func("/cutils/qemu_strtol_full/erange_junk", + test_qemu_strtol_full_erange_junk); /* qemu_strtoul() tests */ g_test_add_func("/cutils/qemu_strtoul/correct", @@ -2610,6 +3816,8 @@ int main(int argc, char **argv) test_qemu_strtoul_decimal); g_test_add_func("/cutils/qemu_strtoul/hex", test_qemu_strtoul_hex); + g_test_add_func("/cutils/qemu_strtoul/wrap", + test_qemu_strtoul_wrap); g_test_add_func("/cutils/qemu_strtoul/max", test_qemu_strtoul_max); g_test_add_func("/cutils/qemu_strtoul/overflow", @@ -2618,6 +3826,8 @@ int main(int argc, char **argv) test_qemu_strtoul_underflow); g_test_add_func("/cutils/qemu_strtoul/negative", test_qemu_strtoul_negative); + g_test_add_func("/cutils/qemu_strtoul/negzero", + test_qemu_strtoul_negzero); g_test_add_func("/cutils/qemu_strtoul_full/correct", test_qemu_strtoul_full_correct); g_test_add_func("/cutils/qemu_strtoul_full/null", @@ -2626,10 +3836,14 @@ int main(int argc, char **argv) test_qemu_strtoul_full_empty); g_test_add_func("/cutils/qemu_strtoul_full/negative", test_qemu_strtoul_full_negative); + g_test_add_func("/cutils/qemu_strtoul_full/negzero", + test_qemu_strtoul_full_negzero); g_test_add_func("/cutils/qemu_strtoul_full/trailing", test_qemu_strtoul_full_trailing); g_test_add_func("/cutils/qemu_strtoul_full/max", test_qemu_strtoul_full_max); + g_test_add_func("/cutils/qemu_strtoul_full/erange_junk", + test_qemu_strtoul_full_erange_junk); /* qemu_strtoi64() tests */ g_test_add_func("/cutils/qemu_strtoi64/correct", @@ -2640,8 +3854,7 @@ int main(int argc, char **argv) test_qemu_strtoi64_empty); g_test_add_func("/cutils/qemu_strtoi64/whitespace", test_qemu_strtoi64_whitespace); - g_test_add_func("/cutils/qemu_strtoi64/invalid" - , + g_test_add_func("/cutils/qemu_strtoi64/invalid", test_qemu_strtoi64_invalid); g_test_add_func("/cutils/qemu_strtoi64/trailing", test_qemu_strtoi64_trailing); @@ -2655,10 +3868,14 @@ int main(int argc, char **argv) test_qemu_strtoi64_max); g_test_add_func("/cutils/qemu_strtoi64/overflow", test_qemu_strtoi64_overflow); + g_test_add_func("/cutils/qemu_strtoi64/min", + test_qemu_strtoi64_min); g_test_add_func("/cutils/qemu_strtoi64/underflow", test_qemu_strtoi64_underflow); g_test_add_func("/cutils/qemu_strtoi64/negative", test_qemu_strtoi64_negative); + g_test_add_func("/cutils/qemu_strtoi64/negzero", + test_qemu_strtoi64_negzero); g_test_add_func("/cutils/qemu_strtoi64_full/correct", test_qemu_strtoi64_full_correct); g_test_add_func("/cutils/qemu_strtoi64_full/null", @@ -2667,10 +3884,14 @@ int main(int argc, char **argv) test_qemu_strtoi64_full_empty); g_test_add_func("/cutils/qemu_strtoi64_full/negative", test_qemu_strtoi64_full_negative); + g_test_add_func("/cutils/qemu_strtoi64_full/negzero", + test_qemu_strtoi64_full_negzero); g_test_add_func("/cutils/qemu_strtoi64_full/trailing", test_qemu_strtoi64_full_trailing); g_test_add_func("/cutils/qemu_strtoi64_full/max", test_qemu_strtoi64_full_max); + g_test_add_func("/cutils/qemu_strtoi64_full/erange_junk", + test_qemu_strtoi64_full_erange_junk); /* qemu_strtou64() tests */ g_test_add_func("/cutils/qemu_strtou64/correct", @@ -2691,6 +3912,8 @@ int main(int argc, char **argv) test_qemu_strtou64_decimal); g_test_add_func("/cutils/qemu_strtou64/hex", test_qemu_strtou64_hex); + g_test_add_func("/cutils/qemu_strtou64/wrap", + test_qemu_strtou64_wrap); g_test_add_func("/cutils/qemu_strtou64/max", test_qemu_strtou64_max); g_test_add_func("/cutils/qemu_strtou64/overflow", @@ -2699,6 +3922,8 @@ int main(int argc, char **argv) test_qemu_strtou64_underflow); g_test_add_func("/cutils/qemu_strtou64/negative", test_qemu_strtou64_negative); + g_test_add_func("/cutils/qemu_strtou64/negzero", + test_qemu_strtou64_negzero); g_test_add_func("/cutils/qemu_strtou64_full/correct", test_qemu_strtou64_full_correct); g_test_add_func("/cutils/qemu_strtou64_full/null", @@ -2707,11 +3932,44 @@ int main(int argc, char **argv) test_qemu_strtou64_full_empty); g_test_add_func("/cutils/qemu_strtou64_full/negative", test_qemu_strtou64_full_negative); + g_test_add_func("/cutils/qemu_strtou64_full/negzero", + test_qemu_strtou64_full_negzero); g_test_add_func("/cutils/qemu_strtou64_full/trailing", test_qemu_strtou64_full_trailing); g_test_add_func("/cutils/qemu_strtou64_full/max", test_qemu_strtou64_full_max); + g_test_add_func("/cutils/qemu_strtou64_full/erange_junk", + test_qemu_strtou64_full_erange_junk); + /* qemu_strtod() tests */ + g_test_add_func("/cutils/qemu_strtod/simple", + test_qemu_strtod_simple); + g_test_add_func("/cutils/qemu_strtod/einval", + test_qemu_strtod_einval); + g_test_add_func("/cutils/qemu_strtod/erange", + test_qemu_strtod_erange); + g_test_add_func("/cutils/qemu_strtod/nonfinite", + test_qemu_strtod_nonfinite); + g_test_add_func("/cutils/qemu_strtod/trailing", + test_qemu_strtod_trailing); + g_test_add_func("/cutils/qemu_strtod/erange_junk", + test_qemu_strtod_erange_junk); + + /* qemu_strtod_finite() tests */ + g_test_add_func("/cutils/qemu_strtod_finite/simple", + test_qemu_strtod_finite_simple); + g_test_add_func("/cutils/qemu_strtod_finite/einval", + test_qemu_strtod_finite_einval); + g_test_add_func("/cutils/qemu_strtod_finite/erange", + test_qemu_strtod_finite_erange); + g_test_add_func("/cutils/qemu_strtod_finite/nonfinite", + test_qemu_strtod_finite_nonfinite); + g_test_add_func("/cutils/qemu_strtod_finite/trailing", + test_qemu_strtod_finite_trailing); + g_test_add_func("/cutils/qemu_strtod_finite/erange_junk", + test_qemu_strtod_finite_erange_junk); + + /* qemu_strtosz() tests */ g_test_add_func("/cutils/strtosz/simple", test_qemu_strtosz_simple); g_test_add_func("/cutils/strtosz/hex", @@ -2729,5 +3987,13 @@ int main(int argc, char **argv) g_test_add_func("/cutils/strtosz/metric", test_qemu_strtosz_metric); + g_test_add_func("/cutils/size_to_str", + test_size_to_str); + g_test_add_func("/cutils/freq_to_str", + test_freq_to_str); + g_test_add_func("/cutils/iec_binary_prefix", + test_iec_binary_prefix); + g_test_add_func("/cutils/si_prefix", + test_si_prefix); return g_test_run(); } diff --git a/tests/unit/test-error-report.c b/tests/unit/test-error-report.c new file mode 100644 index 0000000000..54319c86c9 --- /dev/null +++ b/tests/unit/test-error-report.c @@ -0,0 +1,139 @@ +/* + * Error reporting test + * + * Copyright (C) 2022 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 "glib-compat.h" +#include + +#include "qemu/error-report.h" +#include "qapi/error.h" + +static void +test_error_report_simple(void) +{ + if (g_test_subprocess()) { + error_report("%s", "test error"); + warn_report("%s", "test warn"); + info_report("%s", "test info"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +test-error-report: test error*\ +test-error-report: warning: test warn*\ +test-error-report: info: test info*\ +"); +} + +static void +test_error_report_loc(void) +{ + if (g_test_subprocess()) { + loc_set_file("some-file.c", 7717); + error_report("%s", "test error1"); + loc_set_none(); + error_report("%s", "test error2"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +test-error-report:some-file.c:7717: test error1*\ +test-error-report: test error2*\ +"); +} + +static void +test_error_report_glog(void) +{ + if (g_test_subprocess()) { + g_message("gmessage"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("test-error-report: info: gmessage*"); +} + +static void +test_error_report_once(void) +{ + int i; + + if (g_test_subprocess()) { + for (i = 0; i < 3; i++) { + warn_report_once("warn"); + error_report_once("err"); + } + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +test-error-report: warning: warn*\ +test-error-report: err*\ +"); +} + +static void +test_error_report_timestamp(void) +{ + if (g_test_subprocess()) { + message_with_timestamp = true; + warn_report("warn"); + error_report("err"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +*-*-*:*:* test-error-report: warning: warn*\ +*-*-*:*:* test-error-report: err*\ +"); +} + +static void +test_error_warn(void) +{ + if (g_test_subprocess()) { + error_setg(&error_warn, "Testing &error_warn"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr("\ +test-error-report: warning: Testing &error_warn*\ +"); +} + + +int +main(int argc, char *argv[]) +{ + setlocale(LC_ALL, ""); + + g_test_init(&argc, &argv, NULL); + error_init("test-error-report"); + + g_test_add_func("/error-report/simple", test_error_report_simple); + g_test_add_func("/error-report/loc", test_error_report_loc); + g_test_add_func("/error-report/glog", test_error_report_glog); + g_test_add_func("/error-report/once", test_error_report_once); + g_test_add_func("/error-report/timestamp", test_error_report_timestamp); + g_test_add_func("/error-report/warn", test_error_warn); + + return g_test_run(); +} diff --git a/tests/unit/test-fdmon-epoll.c b/tests/unit/test-fdmon-epoll.c deleted file mode 100644 index ef5a856d09..0000000000 --- a/tests/unit/test-fdmon-epoll.c +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * fdmon-epoll tests - * - * Copyright (c) 2020 Red Hat, Inc. - */ - -#include "qemu/osdep.h" -#include "block/aio.h" -#include "qapi/error.h" -#include "qemu/main-loop.h" - -static AioContext *ctx; - -static void dummy_fd_handler(EventNotifier *notifier) -{ - event_notifier_test_and_clear(notifier); -} - -static void add_event_notifiers(EventNotifier *notifiers, size_t n) -{ - for (size_t i = 0; i < n; i++) { - event_notifier_init(¬ifiers[i], false); - aio_set_event_notifier(ctx, ¬ifiers[i], false, - dummy_fd_handler, NULL, NULL); - } -} - -static void remove_event_notifiers(EventNotifier *notifiers, size_t n) -{ - for (size_t i = 0; i < n; i++) { - aio_set_event_notifier(ctx, ¬ifiers[i], false, NULL, NULL, NULL); - event_notifier_cleanup(¬ifiers[i]); - } -} - -/* Check that fd handlers work when external clients are disabled */ -static void test_external_disabled(void) -{ - EventNotifier notifiers[100]; - - /* fdmon-epoll is only enabled when many fd handlers are registered */ - add_event_notifiers(notifiers, G_N_ELEMENTS(notifiers)); - - event_notifier_set(¬ifiers[0]); - assert(aio_poll(ctx, true)); - - aio_disable_external(ctx); - event_notifier_set(¬ifiers[0]); - assert(aio_poll(ctx, true)); - aio_enable_external(ctx); - - remove_event_notifiers(notifiers, G_N_ELEMENTS(notifiers)); -} - -int main(int argc, char **argv) -{ - /* - * This code relies on the fact that fdmon-io_uring disables itself when - * the glib main loop is in use. The main loop uses fdmon-poll and upgrades - * to fdmon-epoll when the number of fds exceeds a threshold. - */ - qemu_init_main_loop(&error_fatal); - ctx = qemu_get_aio_context(); - - while (g_main_context_iteration(NULL, false)) { - /* Do nothing */ - } - - g_test_init(&argc, &argv, NULL); - g_test_add_func("/fdmon-epoll/external-disabled", test_external_disabled); - return g_test_run(); -} diff --git a/tests/unit/test-image-locking.c b/tests/unit/test-image-locking.c index ba057bd66c..2624cec6a0 100644 --- a/tests/unit/test-image-locking.c +++ b/tests/unit/test-image-locking.c @@ -76,10 +76,10 @@ static void check_locked_bytes(int fd, uint64_t perm_locks, static void test_image_locking_basic(void) { BlockBackend *blk1, *blk2, *blk3; - char img_path[] = "/tmp/qtest.XXXXXX"; + g_autofree char *img_path = NULL; uint64_t perm, shared_perm; - int fd = mkstemp(img_path); + int fd = g_file_open_tmp("qemu-tst-img-lock.XXXXXX", &img_path, NULL); assert(fd >= 0); perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ; @@ -117,10 +117,10 @@ static void test_image_locking_basic(void) static void test_set_perm_abort(void) { BlockBackend *blk1, *blk2; - char img_path[] = "/tmp/qtest.XXXXXX"; + g_autofree char *img_path = NULL; uint64_t perm, shared_perm; int r; - int fd = mkstemp(img_path); + int fd = g_file_open_tmp("qemu-tst-img-lock.XXXXXX", &img_path, NULL); assert(fd >= 0); perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ; @@ -140,6 +140,8 @@ static void test_set_perm_abort(void) check_locked_bytes(fd, perm, ~shared_perm); blk_unref(blk1); blk_unref(blk2); + close(fd); + unlink(img_path); } int main(int argc, char **argv) diff --git a/tests/unit/test-interval-tree.c b/tests/unit/test-interval-tree.c new file mode 100644 index 0000000000..119817a019 --- /dev/null +++ b/tests/unit/test-interval-tree.c @@ -0,0 +1,209 @@ +/* + * Test interval trees + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/interval-tree.h" + +static IntervalTreeNode nodes[20]; +static IntervalTreeRoot root; + +static void rand_interval(IntervalTreeNode *n, uint64_t start, uint64_t last) +{ + gint32 s_ofs, l_ofs, l_max; + + if (last - start > INT32_MAX) { + l_max = INT32_MAX; + } else { + l_max = last - start; + } + s_ofs = g_test_rand_int_range(0, l_max); + l_ofs = g_test_rand_int_range(s_ofs, l_max); + + n->start = start + s_ofs; + n->last = start + l_ofs; +} + +static void test_empty(void) +{ + g_assert(root.rb_root.rb_node == NULL); + g_assert(root.rb_leftmost == NULL); + g_assert(interval_tree_iter_first(&root, 0, UINT64_MAX) == NULL); +} + +static void test_find_one_point(void) +{ + /* Create a tree of a single node, which is the point [1,1]. */ + nodes[0].start = 1; + nodes[0].last = 1; + + interval_tree_insert(&nodes[0], &root); + + g_assert(interval_tree_iter_first(&root, 0, 9) == &nodes[0]); + g_assert(interval_tree_iter_next(&nodes[0], 0, 9) == NULL); + g_assert(interval_tree_iter_first(&root, 0, 0) == NULL); + g_assert(interval_tree_iter_next(&nodes[0], 0, 0) == NULL); + g_assert(interval_tree_iter_first(&root, 0, 1) == &nodes[0]); + g_assert(interval_tree_iter_first(&root, 1, 1) == &nodes[0]); + g_assert(interval_tree_iter_first(&root, 1, 2) == &nodes[0]); + g_assert(interval_tree_iter_first(&root, 2, 2) == NULL); + + interval_tree_remove(&nodes[0], &root); + g_assert(root.rb_root.rb_node == NULL); + g_assert(root.rb_leftmost == NULL); +} + +static void test_find_two_point(void) +{ + IntervalTreeNode *find0, *find1; + + /* Create a tree of a two nodes, which are both the point [1,1]. */ + nodes[0].start = 1; + nodes[0].last = 1; + nodes[1] = nodes[0]; + + interval_tree_insert(&nodes[0], &root); + interval_tree_insert(&nodes[1], &root); + + find0 = interval_tree_iter_first(&root, 0, 9); + g_assert(find0 == &nodes[0] || find0 == &nodes[1]); + + find1 = interval_tree_iter_next(find0, 0, 9); + g_assert(find1 == &nodes[0] || find1 == &nodes[1]); + g_assert(find0 != find1); + + interval_tree_remove(&nodes[1], &root); + + g_assert(interval_tree_iter_first(&root, 0, 9) == &nodes[0]); + g_assert(interval_tree_iter_next(&nodes[0], 0, 9) == NULL); + + interval_tree_remove(&nodes[0], &root); +} + +static void test_find_one_range(void) +{ + /* Create a tree of a single node, which is the range [1,8]. */ + nodes[0].start = 1; + nodes[0].last = 8; + + interval_tree_insert(&nodes[0], &root); + + g_assert(interval_tree_iter_first(&root, 0, 9) == &nodes[0]); + g_assert(interval_tree_iter_next(&nodes[0], 0, 9) == NULL); + g_assert(interval_tree_iter_first(&root, 0, 0) == NULL); + g_assert(interval_tree_iter_first(&root, 0, 1) == &nodes[0]); + g_assert(interval_tree_iter_first(&root, 1, 1) == &nodes[0]); + g_assert(interval_tree_iter_first(&root, 4, 6) == &nodes[0]); + g_assert(interval_tree_iter_first(&root, 8, 8) == &nodes[0]); + g_assert(interval_tree_iter_first(&root, 9, 9) == NULL); + + interval_tree_remove(&nodes[0], &root); +} + +static void test_find_one_range_many(void) +{ + int i; + + /* + * Create a tree of many nodes in [0,99] and [200,299], + * but only one node with exactly [110,190]. + */ + nodes[0].start = 110; + nodes[0].last = 190; + + for (i = 1; i < ARRAY_SIZE(nodes) / 2; ++i) { + rand_interval(&nodes[i], 0, 99); + } + for (; i < ARRAY_SIZE(nodes); ++i) { + rand_interval(&nodes[i], 200, 299); + } + + for (i = 0; i < ARRAY_SIZE(nodes); ++i) { + interval_tree_insert(&nodes[i], &root); + } + + /* Test that we find exactly the one node. */ + g_assert(interval_tree_iter_first(&root, 100, 199) == &nodes[0]); + g_assert(interval_tree_iter_next(&nodes[0], 100, 199) == NULL); + g_assert(interval_tree_iter_first(&root, 100, 109) == NULL); + g_assert(interval_tree_iter_first(&root, 100, 110) == &nodes[0]); + g_assert(interval_tree_iter_first(&root, 111, 120) == &nodes[0]); + g_assert(interval_tree_iter_first(&root, 111, 199) == &nodes[0]); + g_assert(interval_tree_iter_first(&root, 190, 199) == &nodes[0]); + g_assert(interval_tree_iter_first(&root, 192, 199) == NULL); + + /* + * Test that if there are multiple matches, we return the one + * with the minimal start. + */ + g_assert(interval_tree_iter_first(&root, 100, 300) == &nodes[0]); + + /* Test that we don't find it after it is removed. */ + interval_tree_remove(&nodes[0], &root); + g_assert(interval_tree_iter_first(&root, 100, 199) == NULL); + + for (i = 1; i < ARRAY_SIZE(nodes); ++i) { + interval_tree_remove(&nodes[i], &root); + } +} + +static void test_find_many_range(void) +{ + IntervalTreeNode *find; + int i, n; + + n = g_test_rand_int_range(ARRAY_SIZE(nodes) / 3, ARRAY_SIZE(nodes) / 2); + + /* + * Create a fair few nodes in [2000,2999], with the others + * distributed around. + */ + for (i = 0; i < n; ++i) { + rand_interval(&nodes[i], 2000, 2999); + } + for (; i < ARRAY_SIZE(nodes) * 2 / 3; ++i) { + rand_interval(&nodes[i], 1000, 1899); + } + for (; i < ARRAY_SIZE(nodes); ++i) { + rand_interval(&nodes[i], 3100, 3999); + } + + for (i = 0; i < ARRAY_SIZE(nodes); ++i) { + interval_tree_insert(&nodes[i], &root); + } + + /* Test that we find all of the nodes. */ + find = interval_tree_iter_first(&root, 2000, 2999); + for (i = 0; find != NULL; i++) { + find = interval_tree_iter_next(find, 2000, 2999); + } + g_assert_cmpint(i, ==, n); + + g_assert(interval_tree_iter_first(&root, 0, 999) == NULL); + g_assert(interval_tree_iter_first(&root, 1900, 1999) == NULL); + g_assert(interval_tree_iter_first(&root, 3000, 3099) == NULL); + g_assert(interval_tree_iter_first(&root, 4000, UINT64_MAX) == NULL); + + for (i = 0; i < ARRAY_SIZE(nodes); ++i) { + interval_tree_remove(&nodes[i], &root); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/interval-tree/empty", test_empty); + g_test_add_func("/interval-tree/find-one-point", test_find_one_point); + g_test_add_func("/interval-tree/find-two-point", test_find_two_point); + g_test_add_func("/interval-tree/find-one-range", test_find_one_range); + g_test_add_func("/interval-tree/find-one-range-many", + test_find_one_range_many); + g_test_add_func("/interval-tree/find-many-range", test_find_many_range); + + return g_test_run(); +} diff --git a/tests/unit/test-io-channel-command.c b/tests/unit/test-io-channel-command.c index 99056e07c0..4f022617df 100644 --- a/tests/unit/test-io-channel-command.c +++ b/tests/unit/test-io-channel-command.c @@ -19,37 +19,39 @@ */ #include "qemu/osdep.h" +#include +#include +#include #include "io/channel-command.h" #include "io-channel-helpers.h" #include "qapi/error.h" #include "qemu/module.h" -#ifndef WIN32 +#define TEST_FIFO "test-io-channel-command.fifo" + +static char *socat = NULL; + +#if !defined(_WIN32) && !defined(CONFIG_DARWIN) static void test_io_channel_command_fifo(bool async) { -#define TEST_FIFO "tests/test-io-channel-command.fifo" + g_autofree gchar *tmpdir = g_dir_make_tmp("qemu-test-io-channel.XXXXXX", NULL); + g_autofree gchar *fifo = g_build_filename(tmpdir, TEST_FIFO, NULL); + g_autofree gchar *srcargs = g_strdup_printf("%s - PIPE:%s,wronly", socat, fifo); + g_autofree gchar *dstargs = g_strdup_printf("%s PIPE:%s,rdonly -", socat, fifo); + g_auto(GStrv) srcargv = g_strsplit(srcargs, " ", -1); + g_auto(GStrv) dstargv = g_strsplit(dstargs, " ", -1); QIOChannel *src, *dst; QIOChannelTest *test; - const char *srcfifo = "PIPE:" TEST_FIFO ",wronly"; - const char *dstfifo = "PIPE:" TEST_FIFO ",rdonly"; - const char *srcargv[] = { - "/bin/socat", "-", srcfifo, NULL, - }; - const char *dstargv[] = { - "/bin/socat", dstfifo, "-", NULL, - }; + int err; - unlink(TEST_FIFO); - if (access("/bin/socat", X_OK) < 0) { - return; /* Pretend success if socat is not present */ + if (mkfifo(fifo, 0600)) { + g_error("mkfifo: %s", strerror(errno)); } - if (mkfifo(TEST_FIFO, 0600) < 0) { - abort(); - } - src = QIO_CHANNEL(qio_channel_command_new_spawn(srcargv, + + src = QIO_CHANNEL(qio_channel_command_new_spawn((const char **) srcargv, O_WRONLY, &error_abort)); - dst = QIO_CHANNEL(qio_channel_command_new_spawn(dstargv, + dst = QIO_CHANNEL(qio_channel_command_new_spawn((const char **) dstargv, O_RDONLY, &error_abort)); @@ -60,19 +62,32 @@ static void test_io_channel_command_fifo(bool async) object_unref(OBJECT(src)); object_unref(OBJECT(dst)); - unlink(TEST_FIFO); + err = g_unlink(fifo); + g_assert(err == 0); + err = g_rmdir(tmpdir); + g_assert(err == 0); } - static void test_io_channel_command_fifo_async(void) { + if (!socat) { + g_test_skip("socat is not found in PATH"); + return; + } + test_io_channel_command_fifo(true); } static void test_io_channel_command_fifo_sync(void) { + if (!socat) { + g_test_skip("socat is not found in PATH"); + return; + } + test_io_channel_command_fifo(false); } +#endif static void test_io_channel_command_echo(bool async) @@ -80,11 +95,12 @@ static void test_io_channel_command_echo(bool async) QIOChannel *ioc; QIOChannelTest *test; const char *socatargv[] = { - "/bin/socat", "-", "-", NULL, + socat, "-", "-", NULL, }; - if (access("/bin/socat", X_OK) < 0) { - return; /* Pretend success if socat is not present */ + if (!socat) { + g_test_skip("socat is not found in PATH"); + return; } ioc = QIO_CHANNEL(qio_channel_command_new_spawn(socatargv, @@ -107,7 +123,6 @@ static void test_io_channel_command_echo_sync(void) { test_io_channel_command_echo(false); } -#endif int main(int argc, char **argv) { @@ -115,16 +130,18 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); -#ifndef WIN32 + socat = g_find_program_in_path("socat"); + +#if !defined(_WIN32) && !defined(CONFIG_DARWIN) g_test_add_func("/io/channel/command/fifo/sync", test_io_channel_command_fifo_sync); g_test_add_func("/io/channel/command/fifo/async", test_io_channel_command_fifo_async); +#endif g_test_add_func("/io/channel/command/echo/sync", test_io_channel_command_echo_sync); g_test_add_func("/io/channel/command/echo/async", test_io_channel_command_echo_async); -#endif return g_test_run(); } diff --git a/tests/unit/test-io-channel-null.c b/tests/unit/test-io-channel-null.c new file mode 100644 index 0000000000..b3aab17ccc --- /dev/null +++ b/tests/unit/test-io-channel-null.c @@ -0,0 +1,95 @@ +/* + * QEMU I/O channel null test + * + * Copyright (c) 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "io/channel-null.h" +#include "qapi/error.h" + +static gboolean test_io_channel_watch(QIOChannel *ioc, + GIOCondition condition, + gpointer opaque) +{ + GIOCondition *gotcond = opaque; + *gotcond = condition; + return G_SOURCE_REMOVE; +} + +static void test_io_channel_null_io(void) +{ + g_autoptr(QIOChannelNull) null = qio_channel_null_new(); + char buf[1024]; + GIOCondition gotcond = 0; + Error *local_err = NULL; + + g_assert(qio_channel_write(QIO_CHANNEL(null), + "Hello World", 11, + &error_abort) == 11); + + g_assert(qio_channel_read(QIO_CHANNEL(null), + buf, sizeof(buf), + &error_abort) == 0); + + qio_channel_add_watch(QIO_CHANNEL(null), + G_IO_IN, + test_io_channel_watch, + &gotcond, + NULL); + + g_main_context_iteration(NULL, false); + + g_assert(gotcond == G_IO_IN); + + qio_channel_add_watch(QIO_CHANNEL(null), + G_IO_IN | G_IO_OUT, + test_io_channel_watch, + &gotcond, + NULL); + + g_main_context_iteration(NULL, false); + + g_assert(gotcond == (G_IO_IN | G_IO_OUT)); + + qio_channel_close(QIO_CHANNEL(null), &error_abort); + + g_assert(qio_channel_write(QIO_CHANNEL(null), + "Hello World", 11, + &local_err) == -1); + g_assert_nonnull(local_err); + + g_clear_pointer(&local_err, error_free); + + g_assert(qio_channel_read(QIO_CHANNEL(null), + buf, sizeof(buf), + &local_err) == -1); + g_assert_nonnull(local_err); + + g_clear_pointer(&local_err, error_free); +} + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/io/channel/null/io", test_io_channel_null_io); + + return g_test_run(); +} diff --git a/tests/unit/test-io-channel-socket.c b/tests/unit/test-io-channel-socket.c index 6713886d02..b964bb202d 100644 --- a/tests/unit/test-io-channel-socket.c +++ b/tests/unit/test-io-channel-socket.c @@ -179,10 +179,12 @@ static void test_io_channel(bool async, test_io_channel_setup_async(listen_addr, connect_addr, &srv, &src, &dst); +#ifndef _WIN32 g_assert(!passFD || qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); g_assert(!passFD || qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); +#endif g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN)); g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN)); @@ -206,10 +208,12 @@ static void test_io_channel(bool async, test_io_channel_setup_async(listen_addr, connect_addr, &srv, &src, &dst); +#ifndef _WIN32 g_assert(!passFD || qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); g_assert(!passFD || qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); +#endif g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN)); g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN)); @@ -236,10 +240,12 @@ static void test_io_channel(bool async, test_io_channel_setup_sync(listen_addr, connect_addr, &srv, &src, &dst); +#ifndef _WIN32 g_assert(!passFD || qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); g_assert(!passFD || qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); +#endif g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN)); g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN)); @@ -263,10 +269,12 @@ static void test_io_channel(bool async, test_io_channel_setup_sync(listen_addr, connect_addr, &srv, &src, &dst); +#ifndef _WIN32 g_assert(!passFD || qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); g_assert(!passFD || qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); +#endif g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN)); g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN)); @@ -367,7 +375,6 @@ static void test_io_channel_ipv6_async(void) } -#ifndef _WIN32 static void test_io_channel_unix(bool async) { SocketAddress *listen_addr = g_new0(SocketAddress, 1); @@ -398,6 +405,7 @@ static void test_io_channel_unix_async(void) return test_io_channel_unix(true); } +#ifndef _WIN32 static void test_io_channel_unix_fd_pass(void) { SocketAddress *listen_addr = g_new0(SocketAddress, 1); @@ -452,6 +460,7 @@ static void test_io_channel_unix_fd_pass(void) G_N_ELEMENTS(iorecv), &fdrecv, &nfdrecv, + 0, &error_abort); g_assert(nfdrecv == G_N_ELEMENTS(fdsend)); @@ -491,6 +500,7 @@ static void test_io_channel_unix_fd_pass(void) } g_free(fdrecv); } +#endif /* _WIN32 */ static void test_io_channel_unix_listen_cleanup(void) { @@ -522,9 +532,6 @@ static void test_io_channel_unix_listen_cleanup(void) unlink(TEST_SOCKET); } -#endif /* _WIN32 */ - - static void test_io_channel_ipv4_fd(void) { QIOChannel *ioc; @@ -555,7 +562,7 @@ static void test_io_channel_ipv4_fd(void) int main(int argc, char **argv) { - bool has_ipv4, has_ipv6; + bool has_ipv4, has_ipv6, has_afunix; module_call_init(MODULE_INIT_QOM); qemu_init_main_loop(&error_abort); @@ -588,16 +595,19 @@ int main(int argc, char **argv) test_io_channel_ipv6_async); } + socket_check_afunix_support(&has_afunix); + if (has_afunix) { + g_test_add_func("/io/channel/socket/unix-sync", + test_io_channel_unix_sync); + g_test_add_func("/io/channel/socket/unix-async", + test_io_channel_unix_async); #ifndef _WIN32 - g_test_add_func("/io/channel/socket/unix-sync", - test_io_channel_unix_sync); - g_test_add_func("/io/channel/socket/unix-async", - test_io_channel_unix_async); - g_test_add_func("/io/channel/socket/unix-fd-pass", - test_io_channel_unix_fd_pass); - g_test_add_func("/io/channel/socket/unix-listen-cleanup", - test_io_channel_unix_listen_cleanup); -#endif /* _WIN32 */ + g_test_add_func("/io/channel/socket/unix-fd-pass", + test_io_channel_unix_fd_pass); +#endif + g_test_add_func("/io/channel/socket/unix-listen-cleanup", + test_io_channel_unix_listen_cleanup); + } end: return g_test_run(); diff --git a/tests/unit/test-io-channel-tls.c b/tests/unit/test-io-channel-tls.c index f6fb988c01..e036ac5df4 100644 --- a/tests/unit/test-io-channel-tls.c +++ b/tests/unit/test-io-channel-tls.c @@ -121,12 +121,12 @@ static void test_io_channel_tls(const void *opaque) GMainContext *mainloop; /* We'll use this for our fake client-server connection */ - g_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == 0); + g_assert(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == 0); #define CLIENT_CERT_DIR "tests/test-io-channel-tls-client/" #define SERVER_CERT_DIR "tests/test-io-channel-tls-server/" - mkdir(CLIENT_CERT_DIR, 0700); - mkdir(SERVER_CERT_DIR, 0700); + g_mkdir_with_parents(CLIENT_CERT_DIR, 0700); + g_mkdir_with_parents(SERVER_CERT_DIR, 0700); unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); @@ -273,7 +273,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); - mkdir(WORKDIR, 0700); + g_mkdir_with_parents(WORKDIR, 0700); test_tls_init(KEYFILE); diff --git a/tests/unit/test-io-task.c b/tests/unit/test-io-task.c index 953a50ae66..115dba8970 100644 --- a/tests/unit/test-io-task.c +++ b/tests/unit/test-io-task.c @@ -25,7 +25,7 @@ #include "qapi/error.h" #include "qemu/module.h" -#define TYPE_DUMMY "qemu:dummy" +#define TYPE_DUMMY "qemu-dummy" typedef struct DummyObject DummyObject; typedef struct DummyObjectClass DummyObjectClass; diff --git a/tests/unit/test-iov.c b/tests/unit/test-iov.c index 93bda00f0e..75bc3be005 100644 --- a/tests/unit/test-iov.c +++ b/tests/unit/test-iov.c @@ -172,7 +172,7 @@ static void test_io(void) } iov_from_buf(iov, niov, 0, buf, sz); - siov = g_memdup(iov, sizeof(*iov) * niov); + siov = g_memdup2(iov, sizeof(*iov) * niov); if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0) { perror("socketpair"); @@ -197,15 +197,17 @@ static void test_io(void) s = g_test_rand_int_range(0, j - k + 1); r = iov_send(sv[1], iov, niov, k, s); g_assert(memcmp(iov, siov, sizeof(*iov)*niov) == 0); - if (r >= 0) { - k += r; - usleep(g_test_rand_int_range(0, 30)); - } else if (errno == EAGAIN) { - select(sv[1]+1, NULL, &fds, NULL, NULL); - continue; - } else { - perror("send"); - exit(1); + if (r < 0) { + if (errno == EAGAIN) { + r = 0; + } else { + perror("send"); + exit(1); + } + } + k += r; + if (k < j) { + select(sv[1] + 1, NULL, &fds, NULL, NULL); } } while(k < j); } @@ -349,7 +351,7 @@ static void test_discard_front_undo(void) /* Discard zero bytes */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, 0, &undo); @@ -360,7 +362,7 @@ static void test_discard_front_undo(void) /* Discard more bytes than vector size */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; size = iov_size(iov, iov_cnt); @@ -372,7 +374,7 @@ static void test_discard_front_undo(void) /* Discard entire vector */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; size = iov_size(iov, iov_cnt); @@ -384,7 +386,7 @@ static void test_discard_front_undo(void) /* Discard within first element */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; size = g_test_rand_int_range(1, iov->iov_len); @@ -396,7 +398,7 @@ static void test_discard_front_undo(void) /* Discard entire first element */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, iov->iov_len, &undo); @@ -407,7 +409,7 @@ static void test_discard_front_undo(void) /* Discard within second element */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_tmp = iov; iov_cnt_tmp = iov_cnt; size = iov->iov_len + g_test_rand_int_range(1, iov[1].iov_len); @@ -498,7 +500,7 @@ static void test_discard_back_undo(void) /* Discard zero bytes */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_cnt_tmp = iov_cnt; iov_discard_back_undoable(iov, &iov_cnt_tmp, 0, &undo); iov_discard_undo(&undo); @@ -508,7 +510,7 @@ static void test_discard_back_undo(void) /* Discard more bytes than vector size */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_cnt_tmp = iov_cnt; size = iov_size(iov, iov_cnt); iov_discard_back_undoable(iov, &iov_cnt_tmp, size + 1, &undo); @@ -519,7 +521,7 @@ static void test_discard_back_undo(void) /* Discard entire vector */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_cnt_tmp = iov_cnt; size = iov_size(iov, iov_cnt); iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo); @@ -530,7 +532,7 @@ static void test_discard_back_undo(void) /* Discard within last element */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_cnt_tmp = iov_cnt; size = g_test_rand_int_range(1, iov[iov_cnt - 1].iov_len); iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo); @@ -541,7 +543,7 @@ static void test_discard_back_undo(void) /* Discard entire last element */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_cnt_tmp = iov_cnt; size = iov[iov_cnt - 1].iov_len; iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo); @@ -552,7 +554,7 @@ static void test_discard_back_undo(void) /* Discard within second-to-last element */ iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_orig = g_memdup2(iov, sizeof(iov[0]) * iov_cnt); iov_cnt_tmp = iov_cnt; size = iov[iov_cnt - 1].iov_len + g_test_rand_int_range(1, iov[iov_cnt - 2].iov_len); diff --git a/tests/unit/test-nested-aio-poll.c b/tests/unit/test-nested-aio-poll.c new file mode 100644 index 0000000000..db33742af3 --- /dev/null +++ b/tests/unit/test-nested-aio-poll.c @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Test that poll handlers are not re-entrant in nested aio_poll() + * + * Copyright Red Hat + * + * Poll handlers are usually level-triggered. That means they continue firing + * until the condition is reset (e.g. a virtqueue becomes empty). If a poll + * handler calls nested aio_poll() before the condition is reset, then infinite + * recursion occurs. + * + * aio_poll() is supposed to prevent this by disabling poll handlers in nested + * aio_poll() calls. This test case checks that this is indeed what happens. + */ +#include "qemu/osdep.h" +#include "block/aio.h" +#include "qapi/error.h" + +typedef struct { + AioContext *ctx; + + /* This is the EventNotifier that drives the test */ + EventNotifier poll_notifier; + + /* This EventNotifier is only used to wake aio_poll() */ + EventNotifier dummy_notifier; + + bool nested; +} TestData; + +static void io_read(EventNotifier *notifier) +{ + fprintf(stderr, "%s %p\n", __func__, notifier); + event_notifier_test_and_clear(notifier); +} + +static bool io_poll_true(void *opaque) +{ + fprintf(stderr, "%s %p\n", __func__, opaque); + return true; +} + +static bool io_poll_false(void *opaque) +{ + fprintf(stderr, "%s %p\n", __func__, opaque); + return false; +} + +static void io_poll_ready(EventNotifier *notifier) +{ + TestData *td = container_of(notifier, TestData, poll_notifier); + + fprintf(stderr, "> %s\n", __func__); + + g_assert(!td->nested); + td->nested = true; + + /* Wake the following nested aio_poll() call */ + event_notifier_set(&td->dummy_notifier); + + /* This nested event loop must not call io_poll()/io_poll_ready() */ + g_assert(aio_poll(td->ctx, true)); + + td->nested = false; + + fprintf(stderr, "< %s\n", __func__); +} + +/* dummy_notifier never triggers */ +static void io_poll_never_ready(EventNotifier *notifier) +{ + g_assert_not_reached(); +} + +static void test(void) +{ + TestData td = { + .ctx = aio_context_new(&error_abort), + }; + + qemu_set_current_aio_context(td.ctx); + + /* Enable polling */ + aio_context_set_poll_params(td.ctx, 1000000, 2, 2, &error_abort); + + /* + * The GSource is unused but this has the side-effect of changing the fdmon + * that AioContext uses. + */ + aio_get_g_source(td.ctx); + + /* Make the event notifier active (set) right away */ + event_notifier_init(&td.poll_notifier, 1); + aio_set_event_notifier(td.ctx, &td.poll_notifier, + io_read, io_poll_true, io_poll_ready); + + /* This event notifier will be used later */ + event_notifier_init(&td.dummy_notifier, 0); + aio_set_event_notifier(td.ctx, &td.dummy_notifier, + io_read, io_poll_false, io_poll_never_ready); + + /* Consume aio_notify() */ + g_assert(!aio_poll(td.ctx, false)); + + /* + * Run the io_read() handler. This has the side-effect of activating + * polling in future aio_poll() calls. + */ + g_assert(aio_poll(td.ctx, true)); + + /* The second time around the io_poll()/io_poll_ready() handler runs */ + g_assert(aio_poll(td.ctx, true)); + + /* Run io_poll()/io_poll_ready() one more time to show it keeps working */ + g_assert(aio_poll(td.ctx, true)); + + aio_set_event_notifier(td.ctx, &td.dummy_notifier, NULL, NULL, NULL); + aio_set_event_notifier(td.ctx, &td.poll_notifier, NULL, NULL, NULL); + event_notifier_cleanup(&td.dummy_notifier); + event_notifier_cleanup(&td.poll_notifier); + aio_context_unref(td.ctx); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/nested-aio-poll", test); + return g_test_run(); +} diff --git a/tests/unit/test-qga.c b/tests/unit/test-qga.c index 530317044b..8cddf5dc37 100644 --- a/tests/unit/test-qga.c +++ b/tests/unit/test-qga.c @@ -32,6 +32,7 @@ static int connect_qga(char *path) g_usleep(G_USEC_PER_SEC); } if (i++ == 10) { + close(s); return -1; } } while (ret == -1); @@ -59,8 +60,8 @@ fixture_setup(TestFixture *fixture, gconstpointer data, gchar **envp) fixture->loop = g_main_loop_new(NULL, FALSE); - fixture->test_dir = g_strdup("/tmp/qgatest.XXXXXX"); - g_assert_nonnull(mkdtemp(fixture->test_dir)); + fixture->test_dir = g_strdup_printf("%s/qgatest.XXXXXX", g_get_tmp_dir()); + g_assert_nonnull(g_mkdtemp(fixture->test_dir)); path = g_build_filename(fixture->test_dir, "sock", NULL); cwd = g_get_current_dir(); @@ -629,7 +630,7 @@ static void test_qga_get_time(gconstpointer fix) g_assert_cmpint(time, >, 0); } -static void test_qga_blacklist(gconstpointer data) +static void test_qga_blockedrpcs(gconstpointer data) { TestFixture fix; QDict *ret, *error; @@ -637,7 +638,7 @@ static void test_qga_blacklist(gconstpointer data) fixture_setup(&fix, "-b guest-ping,guest-get-time", NULL); - /* check blacklist */ + /* check blocked RPCs */ ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); g_assert_nonnull(ret); error = qdict_get_qdict(ret, "error"); @@ -664,6 +665,36 @@ static void test_qga_blacklist(gconstpointer data) fixture_tear_down(&fix, NULL); } +static void test_qga_allowedrpcs(gconstpointer data) +{ + TestFixture fix; + QDict *ret, *error; + const gchar *class, *desc; + + fixture_setup(&fix, "-a guest-ping,guest-get-time", NULL); + + /* check allowed RPCs */ + ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); + qmp_assert_no_error(ret); + qobject_unref(ret); + + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); + qmp_assert_no_error(ret); + qobject_unref(ret); + + /* check something else */ + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); + g_assert_nonnull(ret); + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + desc = qdict_get_try_str(error, "desc"); + g_assert_cmpstr(class, ==, "CommandNotFound"); + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); + qobject_unref(ret); + + fixture_tear_down(&fix, NULL); +} + static void test_qga_config(gconstpointer data) { GError *error = NULL; @@ -729,7 +760,7 @@ static void test_qga_config(gconstpointer data) g_assert_true(g_key_file_get_boolean(kf, "general", "verbose", &error)); g_assert_no_error(error); - strv = g_key_file_get_string_list(kf, "general", "blacklist", &n, &error); + strv = g_key_file_get_string_list(kf, "general", "block-rpcs", &n, &error); g_assert_cmpint(n, ==, 2); g_assert_true(g_strv_contains((const char * const *)strv, "guest-ping")); @@ -754,32 +785,16 @@ static void test_qga_fsfreeze_status(gconstpointer fix) g_assert_cmpstr(status, ==, "thawed"); } -static void test_qga_guest_exec(gconstpointer fix) +static QDict *wait_for_guest_exec_completion(int fd, int64_t pid) { - const TestFixture *fixture = fix; - g_autoptr(QDict) ret = NULL; - QDict *val; - const gchar *out; - g_autofree guchar *decoded = NULL; - int64_t pid, now, exitcode; - gsize len; + QDict *ret = NULL; + int64_t now; bool exited; + QDict *val; - /* exec 'echo foo bar' */ - ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" - " 'path': '/bin/echo', 'arg': [ '-n', '\" test_str \"' ]," - " 'capture-output': true } }"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - val = qdict_get_qdict(ret, "return"); - pid = qdict_get_int(val, "pid"); - g_assert_cmpint(pid, >, 0); - qobject_unref(ret); - - /* wait for completion */ now = g_get_monotonic_time(); do { - ret = qmp_fd(fixture->fd, + ret = qmp_fd(fd, "{'execute': 'guest-exec-status'," " 'arguments': { 'pid': %" PRId64 " } }", pid); g_assert_nonnull(ret); @@ -792,7 +807,34 @@ static void test_qga_guest_exec(gconstpointer fix) g_get_monotonic_time() < now + 5 * G_TIME_SPAN_SECOND); g_assert(exited); + return ret; +} + +static void test_qga_guest_exec(gconstpointer fix) +{ + const TestFixture *fixture = fix; + g_autoptr(QDict) ret = NULL; + QDict *val; + const gchar *out; + g_autofree guchar *decoded = NULL; + int64_t pid, exitcode; + gsize len; + + /* exec 'echo foo bar' */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" + " 'path': 'echo', 'arg': [ '-n', '\" test_str \"' ]," + " 'capture-output': true } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + val = qdict_get_qdict(ret, "return"); + pid = qdict_get_int(val, "pid"); + g_assert_cmpint(pid, >, 0); + qobject_unref(ret); + + ret = wait_for_guest_exec_completion(fixture->fd, pid); + /* check stdout */ + val = qdict_get_qdict(ret, "return"); exitcode = qdict_get_int(val, "exitcode"); g_assert_cmpint(exitcode, ==, 0); out = qdict_get_str(val, "out-data"); @@ -801,6 +843,115 @@ static void test_qga_guest_exec(gconstpointer fix) g_assert_cmpstr((char *)decoded, ==, "\" test_str \""); } +#if defined(G_OS_WIN32) +static void test_qga_guest_exec_separated(gconstpointer fix) +{ +} +static void test_qga_guest_exec_merged(gconstpointer fix) +{ + const TestFixture *fixture = fix; + g_autoptr(QDict) ret = NULL; + QDict *val; + const gchar *class, *desc; + g_autofree guchar *decoded = NULL; + + /* exec 'echo foo bar' */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" + " 'path': 'echo'," + " 'arg': [ 'execution never reaches here' ]," + " 'capture-output': 'merged' } }"); + + g_assert_nonnull(ret); + val = qdict_get_qdict(ret, "error"); + g_assert_nonnull(val); + class = qdict_get_str(val, "class"); + desc = qdict_get_str(val, "desc"); + g_assert_cmpstr(class, ==, "GenericError"); + g_assert_cmpint(strlen(desc), >, 0); +} +#else +static void test_qga_guest_exec_separated(gconstpointer fix) +{ + const TestFixture *fixture = fix; + g_autoptr(QDict) ret = NULL; + QDict *val; + const gchar *out, *err; + g_autofree guchar *out_decoded = NULL; + g_autofree guchar *err_decoded = NULL; + int64_t pid, exitcode; + gsize len; + + /* exec 'echo foo bar' */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" + " 'path': 'bash'," + " 'arg': [ '-c', 'for i in $(seq 4); do if (( $i %% 2 )); then echo stdout; else echo stderr 1>&2; fi; done;' ]," + " 'capture-output': 'separated' } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + val = qdict_get_qdict(ret, "return"); + pid = qdict_get_int(val, "pid"); + g_assert_cmpint(pid, >, 0); + qobject_unref(ret); + + ret = wait_for_guest_exec_completion(fixture->fd, pid); + + val = qdict_get_qdict(ret, "return"); + exitcode = qdict_get_int(val, "exitcode"); + g_assert_cmpint(exitcode, ==, 0); + + /* check stdout */ + out = qdict_get_str(val, "out-data"); + out_decoded = g_base64_decode(out, &len); + g_assert_cmpint(len, ==, 14); + g_assert_cmpstr((char *)out_decoded, ==, "stdout\nstdout\n"); + + /* check stderr */ + err = qdict_get_try_str(val, "err-data"); + err_decoded = g_base64_decode(err, &len); + g_assert_cmpint(len, ==, 14); + g_assert_cmpstr((char *)err_decoded, ==, "stderr\nstderr\n"); +} + +static void test_qga_guest_exec_merged(gconstpointer fix) +{ + const TestFixture *fixture = fix; + g_autoptr(QDict) ret = NULL; + QDict *val; + const gchar *out, *err; + g_autofree guchar *decoded = NULL; + int64_t pid, exitcode; + gsize len; + + /* exec 'echo foo bar' */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" + " 'path': 'bash'," + " 'arg': [ '-c', 'for i in $(seq 4); do if (( $i %% 2 )); then echo stdout; else echo stderr 1>&2; fi; done;' ]," + " 'capture-output': 'merged' } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + val = qdict_get_qdict(ret, "return"); + pid = qdict_get_int(val, "pid"); + g_assert_cmpint(pid, >, 0); + qobject_unref(ret); + + ret = wait_for_guest_exec_completion(fixture->fd, pid); + + val = qdict_get_qdict(ret, "return"); + exitcode = qdict_get_int(val, "exitcode"); + g_assert_cmpint(exitcode, ==, 0); + + /* check stdout */ + out = qdict_get_str(val, "out-data"); + decoded = g_base64_decode(out, &len); + g_assert_cmpint(len, ==, 28); + g_assert_cmpstr((char *)decoded, ==, "stdout\nstderr\nstdout\nstderr\n"); + + /* check stderr */ + err = qdict_get_try_str(val, "err-data"); + g_assert_null(err); +} +#endif + static void test_qga_guest_exec_invalid(gconstpointer fix) { const TestFixture *fixture = fix; @@ -968,9 +1119,14 @@ int main(int argc, char **argv) g_test_add_data_func("/qga/fsfreeze-status", &fix, test_qga_fsfreeze_status); - g_test_add_data_func("/qga/blacklist", NULL, test_qga_blacklist); + g_test_add_data_func("/qga/blockedrpcs", NULL, test_qga_blockedrpcs); + g_test_add_data_func("/qga/allowedrpcs", NULL, test_qga_allowedrpcs); g_test_add_data_func("/qga/config", NULL, test_qga_config); g_test_add_data_func("/qga/guest-exec", &fix, test_qga_guest_exec); + g_test_add_data_func("/qga/guest-exec-separated", &fix, + test_qga_guest_exec_separated); + g_test_add_data_func("/qga/guest-exec-merged", &fix, + test_qga_guest_exec_merged); g_test_add_data_func("/qga/guest-exec-invalid", &fix, test_qga_guest_exec_invalid); g_test_add_data_func("/qga/guest-get-osinfo", &fix, diff --git a/tests/unit/test-qmp-cmds.c b/tests/unit/test-qmp-cmds.c index 6085c09995..6d52b4e5d8 100644 --- a/tests/unit/test-qmp-cmds.c +++ b/tests/unit/test-qmp-cmds.c @@ -43,15 +43,15 @@ void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp) { } -FeatureStruct1 *qmp_test_features0(bool has_fs0, FeatureStruct0 *fs0, - bool has_fs1, FeatureStruct1 *fs1, - bool has_fs2, FeatureStruct2 *fs2, - bool has_fs3, FeatureStruct3 *fs3, - bool has_fs4, FeatureStruct4 *fs4, - bool has_cfs1, CondFeatureStruct1 *cfs1, - bool has_cfs2, CondFeatureStruct2 *cfs2, - bool has_cfs3, CondFeatureStruct3 *cfs3, - bool has_cfs4, CondFeatureStruct4 *cfs4, +FeatureStruct1 *qmp_test_features0(FeatureStruct0 *fs0, + FeatureStruct1 *fs1, + FeatureStruct2 *fs2, + FeatureStruct3 *fs3, + FeatureStruct4 *fs4, + CondFeatureStruct1 *cfs1, + CondFeatureStruct2 *cfs2, + CondFeatureStruct3 *cfs3, + CondFeatureStruct4 *cfs4, Error **errp) { return g_new0(FeatureStruct1, 1); @@ -77,8 +77,7 @@ void qmp_test_command_cond_features3(Error **errp) { } -UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a, - bool has_udb1, UserDefOne *ud1b, +UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a, UserDefOne *ud1b, Error **errp) { UserDefTwo *ret; @@ -87,8 +86,8 @@ UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a, ud1c->string = strdup(ud1a->string); ud1c->integer = ud1a->integer; - ud1d->string = strdup(has_udb1 ? ud1b->string : "blah0"); - ud1d->integer = has_udb1 ? ud1b->integer : 0; + ud1d->string = strdup(ud1b ? ud1b->string : "blah0"); + ud1d->integer = ud1b ? ud1b->integer : 0; ret = g_new0(UserDefTwo, 1); ret->string0 = strdup("blah1"); @@ -98,7 +97,6 @@ UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a, ret->dict1->dict2->userdef = ud1c; ret->dict1->dict2->string = strdup("blah3"); ret->dict1->dict3 = g_new0(UserDefTwoDictDict, 1); - ret->dict1->has_dict3 = true; ret->dict1->dict3->userdef = ud1d; ret->dict1->dict3->string = strdup("blah4"); @@ -140,6 +138,7 @@ void qmp___org_qemu_x_command(__org_qemu_x_EnumList *a, } +G_GNUC_PRINTF(2, 3) static QObject *do_qmp_dispatch(bool allow_oob, const char *template, ...) { va_list ap; @@ -162,6 +161,7 @@ static QObject *do_qmp_dispatch(bool allow_oob, const char *template, ...) return ret; } +G_GNUC_PRINTF(3, 4) static void do_qmp_dispatch_error(bool allow_oob, ErrorClass cls, const char *template, ...) { @@ -271,7 +271,7 @@ static void test_dispatch_cmd_io(void) static void test_dispatch_cmd_deprecated(void) { - const char *cmd = "{ 'execute': 'test-command-features1' }"; + #define cmd "{ 'execute': 'test-command-features1' }" QDict *ret; memset(&compat_policy, 0, sizeof(compat_policy)); @@ -289,12 +289,13 @@ static void test_dispatch_cmd_deprecated(void) compat_policy.deprecated_input = COMPAT_POLICY_INPUT_REJECT; do_qmp_dispatch_error(false, ERROR_CLASS_COMMAND_NOT_FOUND, cmd); + #undef cmd } static void test_dispatch_cmd_arg_deprecated(void) { - const char *cmd = "{ 'execute': 'test-features0'," - " 'arguments': { 'fs1': { 'foo': 42 } } }"; + #define cmd "{ 'execute': 'test-features0'," \ + " 'arguments': { 'fs1': { 'foo': 42 } } }" QDict *ret; memset(&compat_policy, 0, sizeof(compat_policy)); @@ -312,11 +313,12 @@ static void test_dispatch_cmd_arg_deprecated(void) compat_policy.deprecated_input = COMPAT_POLICY_INPUT_REJECT; do_qmp_dispatch_error(false, ERROR_CLASS_GENERIC_ERROR, cmd); + #undef cmd } static void test_dispatch_cmd_ret_deprecated(void) { - const char *cmd = "{ 'execute': 'test-features0' }"; + #define cmd "{ 'execute': 'test-features0' }" QDict *ret; memset(&compat_policy, 0, sizeof(compat_policy)); @@ -336,6 +338,7 @@ static void test_dispatch_cmd_ret_deprecated(void) ret = qobject_to(QDict, do_qmp_dispatch(false, cmd)); assert(ret && qdict_size(ret) == 0); qobject_unref(ret); + #undef cmd } /* test generated dealloc functions for generated types */ diff --git a/tests/unit/test-qmp-event.c b/tests/unit/test-qmp-event.c index 7d961d716a..08e95a382b 100644 --- a/tests/unit/test-qmp-event.c +++ b/tests/unit/test-qmp-event.c @@ -24,19 +24,15 @@ #include "test-qapi-events.h" #include "test-qapi-emit-events.h" -typedef struct TestEventData { - QDict *expect; - bool emitted; -} TestEventData; - -TestEventData *test_event_data; -static GMutex test_event_lock; +static QDict *expected_event; void test_qapi_event_emit(test_QAPIEvent event, QDict *d) { QDict *t; int64_t s, ms; + g_assert(expected_event); + /* Verify that we have timestamp, then remove it to compare other fields */ t = qdict_get_qdict(d, "timestamp"); g_assert(t); @@ -52,71 +48,38 @@ void test_qapi_event_emit(test_QAPIEvent event, QDict *d) qdict_del(d, "timestamp"); - g_assert(qobject_is_equal(QOBJECT(d), QOBJECT(test_event_data->expect))); - test_event_data->emitted = true; + g_assert(qobject_is_equal(QOBJECT(d), QOBJECT(expected_event))); + qobject_unref(expected_event); + expected_event = NULL; } -static void event_prepare(TestEventData *data, - const void *unused) +static void test_event_a(void) { - /* 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); - test_event_data = data; -} - -static void event_teardown(TestEventData *data, - const void *unused) -{ - test_event_data = NULL; - g_mutex_unlock(&test_event_lock); -} - -static void event_test_add(const char *testpath, - void (*test_func)(TestEventData *data, - const void *user_data)) -{ - g_test_add(testpath, TestEventData, NULL, event_prepare, test_func, - event_teardown); -} - - -/* Test cases */ - -static void test_event_a(TestEventData *data, - const void *unused) -{ - data->expect = qdict_from_jsonf_nofail("{ 'event': 'EVENT_A' }"); + expected_event = qdict_from_jsonf_nofail("{ 'event': 'EVENT_A' }"); qapi_event_send_event_a(); - g_assert(data->emitted); - qobject_unref(data->expect); + g_assert(!expected_event); } -static void test_event_b(TestEventData *data, - const void *unused) +static void test_event_b(void) { - data->expect = qdict_from_jsonf_nofail("{ 'event': 'EVENT_B' }"); + expected_event = qdict_from_jsonf_nofail("{ 'event': 'EVENT_B' }"); qapi_event_send_event_b(); - g_assert(data->emitted); - qobject_unref(data->expect); + g_assert(!expected_event); } -static void test_event_c(TestEventData *data, - const void *unused) +static void test_event_c(void) { UserDefOne b = { .integer = 2, .string = (char *)"test1" }; - data->expect = qdict_from_jsonf_nofail( + expected_event = 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_assert(data->emitted); - qobject_unref(data->expect); + qapi_event_send_event_c(true, 1, &b, "test2"); + g_assert(!expected_event); } /* Complex type */ -static void test_event_d(TestEventData *data, - const void *unused) +static void test_event_d(void) { UserDefOne struct1 = { .integer = 2, .string = (char *)"test1", @@ -129,65 +92,56 @@ static void test_event_d(TestEventData *data, .enum2 = ENUM_ONE_VALUE2, }; - data->expect = qdict_from_jsonf_nofail( + expected_event = 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_assert(data->emitted); - qobject_unref(data->expect); + qapi_event_send_event_d(&a, "test3", NULL, true, ENUM_ONE_VALUE3); + g_assert(!expected_event); } -static void test_event_deprecated(TestEventData *data, const void *unused) +static void test_event_deprecated(void) { - data->expect = qdict_from_jsonf_nofail("{ 'event': 'TEST_EVENT_FEATURES1' }"); + expected_event = qdict_from_jsonf_nofail("{ 'event': 'TEST_EVENT_FEATURES1' }"); memset(&compat_policy, 0, sizeof(compat_policy)); qapi_event_send_test_event_features1(); - g_assert(data->emitted); + g_assert(!expected_event); compat_policy.has_deprecated_output = true; compat_policy.deprecated_output = COMPAT_POLICY_OUTPUT_HIDE; - data->emitted = false; qapi_event_send_test_event_features1(); - g_assert(!data->emitted); - - qobject_unref(data->expect); } -static void test_event_deprecated_data(TestEventData *data, const void *unused) +static void test_event_deprecated_data(void) { memset(&compat_policy, 0, sizeof(compat_policy)); - data->expect = qdict_from_jsonf_nofail("{ 'event': 'TEST_EVENT_FEATURES0'," + expected_event = qdict_from_jsonf_nofail("{ 'event': 'TEST_EVENT_FEATURES0'," " 'data': { 'foo': 42 } }"); qapi_event_send_test_event_features0(42); - g_assert(data->emitted); + g_assert(!expected_event); - qobject_unref(data->expect); compat_policy.has_deprecated_output = true; compat_policy.deprecated_output = COMPAT_POLICY_OUTPUT_HIDE; - data->expect = qdict_from_jsonf_nofail("{ 'event': 'TEST_EVENT_FEATURES0' }"); + expected_event = qdict_from_jsonf_nofail("{ 'event': 'TEST_EVENT_FEATURES0' }"); qapi_event_send_test_event_features0(42); - g_assert(data->emitted); - - qobject_unref(data->expect); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - event_test_add("/event/event_a", test_event_a); - event_test_add("/event/event_b", test_event_b); - event_test_add("/event/event_c", test_event_c); - event_test_add("/event/event_d", test_event_d); - event_test_add("/event/deprecated", test_event_deprecated); - event_test_add("/event/deprecated_data", test_event_deprecated_data); + g_test_add_func("/event/event_a", test_event_a); + g_test_add_func("/event/event_b", test_event_b); + g_test_add_func("/event/event_c", test_event_c); + g_test_add_func("/event/event_d", test_event_d); + g_test_add_func("/event/deprecated", test_event_deprecated); + g_test_add_func("/event/deprecated_data", test_event_deprecated_data); g_test_run(); return 0; diff --git a/tests/unit/test-qobject-input-visitor.c b/tests/unit/test-qobject-input-visitor.c index 14329dabcf..024e26c49e 100644 --- a/tests/unit/test-qobject-input-visitor.c +++ b/tests/unit/test-qobject-input-visitor.c @@ -94,7 +94,7 @@ Visitor *visitor_input_test_init(TestInputVisitorData *data, /* similar to visitor_input_test_init(), but does not expect a string * literal/format json_string argument and so can be used for - * programatically generated strings (and we can't pass in programatically + * programmatically generated strings (and we can't pass in programmatically * generated strings via %s format parameters since qobject_from_jsonv() * will wrap those in double-quotes and treat the entire object as a * string) @@ -431,7 +431,7 @@ static void test_visitor_in_struct_nested(TestInputVisitorData *data, g_assert_cmpint(udp->dict1->dict2->userdef->integer, ==, 42); g_assert_cmpstr(udp->dict1->dict2->userdef->string, ==, "string"); g_assert_cmpstr(udp->dict1->dict2->string, ==, "string2"); - g_assert(udp->dict1->has_dict3 == false); + g_assert(!udp->dict1->dict3); } static void test_visitor_in_list(TestInputVisitorData *data, @@ -447,9 +447,8 @@ static void test_visitor_in_list(TestInputVisitorData *data, g_assert(head != NULL); for (i = 0, item = head; item; item = item->next, i++) { - char string[12]; + g_autofree char *string = g_strdup_printf("string%d", i); - snprintf(string, sizeof(string), "string%d", i); g_assert_cmpstr(item->value->string, ==, string); g_assert_cmpint(item->value->integer, ==, 42 + i); } @@ -707,6 +706,51 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data, g_assert(&base->enum1 == &tmp->enum1); } +static void test_visitor_in_union_in_union(TestInputVisitorData *data, + const void *unused) +{ + Visitor *v; + g_autoptr(TestUnionInUnion) tmp = NULL; + + v = visitor_input_test_init(data, + "{ 'type': 'value-a', " + " 'type-a': 'value-a1', " + " 'integer': 2, " + " 'name': 'fish' }"); + + visit_type_TestUnionInUnion(v, NULL, &tmp, &error_abort); + g_assert_cmpint(tmp->type, ==, TEST_UNION_ENUM_VALUE_A); + g_assert_cmpint(tmp->u.value_a.type_a, ==, TEST_UNION_ENUMA_VALUE_A1); + g_assert_cmpint(tmp->u.value_a.u.value_a1.integer, ==, 2); + g_assert_cmpint(strcmp(tmp->u.value_a.u.value_a1.name, "fish"), ==, 0); + + qapi_free_TestUnionInUnion(tmp); + + v = visitor_input_test_init(data, + "{ 'type': 'value-a', " + " 'type-a': 'value-a2', " + " 'integer': 1729, " + " 'size': 87539319 }"); + + visit_type_TestUnionInUnion(v, NULL, &tmp, &error_abort); + g_assert_cmpint(tmp->type, ==, TEST_UNION_ENUM_VALUE_A); + g_assert_cmpint(tmp->u.value_a.type_a, ==, TEST_UNION_ENUMA_VALUE_A2); + g_assert_cmpint(tmp->u.value_a.u.value_a2.integer, ==, 1729); + g_assert_cmpint(tmp->u.value_a.u.value_a2.size, ==, 87539319); + + qapi_free_TestUnionInUnion(tmp); + + v = visitor_input_test_init(data, + "{ 'type': 'value-b', " + " 'integer': 1729, " + " 'onoff': true }"); + + visit_type_TestUnionInUnion(v, NULL, &tmp, &error_abort); + g_assert_cmpint(tmp->type, ==, TEST_UNION_ENUM_VALUE_B); + g_assert_cmpint(tmp->u.value_b.integer, ==, 1729); + g_assert_cmpint(tmp->u.value_b.onoff, ==, true); +} + static void test_visitor_in_alternate(TestInputVisitorData *data, const void *unused) { @@ -1217,6 +1261,8 @@ int main(int argc, char **argv) NULL, test_visitor_in_null); input_visitor_test_add("/visitor/input/union-flat", NULL, test_visitor_in_union_flat); + input_visitor_test_add("/visitor/input/union-in-union", + NULL, test_visitor_in_union_in_union); input_visitor_test_add("/visitor/input/alternate", NULL, test_visitor_in_alternate); input_visitor_test_add("/visitor/input/errors", diff --git a/tests/unit/test-qobject-output-visitor.c b/tests/unit/test-qobject-output-visitor.c index 66b27fad66..1535b3ad17 100644 --- a/tests/unit/test-qobject-output-visitor.c +++ b/tests/unit/test-qobject-output-visitor.c @@ -182,7 +182,6 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data, ud2->dict1->dict2->string = g_strdup(strings[2]); ud2->dict1->dict3 = g_malloc0(sizeof(*ud2->dict1->dict3)); - ud2->dict1->has_dict3 = true; ud2->dict1->dict3->userdef = g_new0(UserDefOne, 1); ud2->dict1->dict3->userdef->string = g_strdup(string); ud2->dict1->dict3->userdef->integer = value; @@ -284,7 +283,6 @@ static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data, value->dict1->dict2->userdef->string = g_strdup(string); value->dict1->dict2->userdef->integer = 42; value->dict1->dict2->string = g_strdup(string); - value->dict1->has_dict3 = false; QAPI_LIST_PREPEND(head, value); } @@ -354,6 +352,62 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data, qapi_free_UserDefFlatUnion(tmp); } +static void test_visitor_out_union_in_union(TestOutputVisitorData *data, + const void *unused) +{ + QDict *qdict; + + TestUnionInUnion *tmp = g_new0(TestUnionInUnion, 1); + tmp->type = TEST_UNION_ENUM_VALUE_A; + tmp->u.value_a.type_a = TEST_UNION_ENUMA_VALUE_A1; + tmp->u.value_a.u.value_a1.integer = 42; + tmp->u.value_a.u.value_a1.name = g_strdup("fish"); + + visit_type_TestUnionInUnion(data->ov, NULL, &tmp, &error_abort); + qdict = qobject_to(QDict, visitor_get(data)); + g_assert(qdict); + g_assert_cmpstr(qdict_get_str(qdict, "type"), ==, "value-a"); + g_assert_cmpstr(qdict_get_str(qdict, "type-a"), ==, "value-a1"); + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42); + g_assert_cmpstr(qdict_get_str(qdict, "name"), ==, "fish"); + + qapi_free_TestUnionInUnion(tmp); + + + visitor_reset(data); + tmp = g_new0(TestUnionInUnion, 1); + tmp->type = TEST_UNION_ENUM_VALUE_A; + tmp->u.value_a.type_a = TEST_UNION_ENUMA_VALUE_A2; + tmp->u.value_a.u.value_a2.integer = 1729; + tmp->u.value_a.u.value_a2.size = 87539319; + + visit_type_TestUnionInUnion(data->ov, NULL, &tmp, &error_abort); + qdict = qobject_to(QDict, visitor_get(data)); + g_assert(qdict); + g_assert_cmpstr(qdict_get_str(qdict, "type"), ==, "value-a"); + g_assert_cmpstr(qdict_get_str(qdict, "type-a"), ==, "value-a2"); + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 1729); + g_assert_cmpint(qdict_get_int(qdict, "size"), ==, 87539319); + + qapi_free_TestUnionInUnion(tmp); + + + visitor_reset(data); + tmp = g_new0(TestUnionInUnion, 1); + tmp->type = TEST_UNION_ENUM_VALUE_B; + tmp->u.value_b.integer = 1729; + tmp->u.value_b.onoff = true; + + visit_type_TestUnionInUnion(data->ov, NULL, &tmp, &error_abort); + qdict = qobject_to(QDict, visitor_get(data)); + g_assert(qdict); + g_assert_cmpstr(qdict_get_str(qdict, "type"), ==, "value-b"); + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 1729); + g_assert_cmpint(qdict_get_bool(qdict, "onoff"), ==, true); + + qapi_free_TestUnionInUnion(tmp); +} + static void test_visitor_out_alternate(TestOutputVisitorData *data, const void *unused) { @@ -588,6 +642,8 @@ int main(int argc, char **argv) &out_visitor_data, test_visitor_out_list_qapi_free); output_visitor_test_add("/visitor/output/union-flat", &out_visitor_data, test_visitor_out_union_flat); + output_visitor_test_add("/visitor/output/union-in-union", + &out_visitor_data, test_visitor_out_union_in_union); output_visitor_test_add("/visitor/output/alternate", &out_visitor_data, test_visitor_out_alternate); output_visitor_test_add("/visitor/output/null", diff --git a/tests/unit/test-qtree.c b/tests/unit/test-qtree.c new file mode 100644 index 0000000000..4d836d22c7 --- /dev/null +++ b/tests/unit/test-qtree.c @@ -0,0 +1,333 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * Tests for QTree. + * Original source: glib + * https://gitlab.gnome.org/GNOME/glib/-/blob/main/glib/tests/tree.c + * LGPL license. + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + */ + +#include "qemu/osdep.h" +#include "qemu/qtree.h" + +static gint my_compare(gconstpointer a, gconstpointer b) +{ + const char *cha = a; + const char *chb = b; + + return *cha - *chb; +} + +static gint my_compare_with_data(gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + const char *cha = a; + const char *chb = b; + + /* just check that we got the right data */ + g_assert(GPOINTER_TO_INT(user_data) == 123); + + return *cha - *chb; +} + +static gint my_search(gconstpointer a, gconstpointer b) +{ + return my_compare(b, a); +} + +static gpointer destroyed_key; +static gpointer destroyed_value; +static guint destroyed_key_count; +static guint destroyed_value_count; + +static void my_key_destroy(gpointer key) +{ + destroyed_key = key; + destroyed_key_count++; +} + +static void my_value_destroy(gpointer value) +{ + destroyed_value = value; + destroyed_value_count++; +} + +static gint my_traverse(gpointer key, gpointer value, gpointer data) +{ + char *ch = key; + + g_assert((*ch) > 0); + + if (*ch == 'd') { + return TRUE; + } + + return FALSE; +} + +char chars[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + +char chars2[] = + "0123456789" + "abcdefghijklmnopqrstuvwxyz"; + +static gint check_order(gpointer key, gpointer value, gpointer data) +{ + char **p = data; + char *ch = key; + + g_assert(**p == *ch); + + (*p)++; + + return FALSE; +} + +static void test_tree_search(void) +{ + gint i; + QTree *tree; + gboolean removed; + gchar c; + gchar *p, *d; + + tree = q_tree_new_with_data(my_compare_with_data, GINT_TO_POINTER(123)); + + for (i = 0; chars[i]; i++) { + q_tree_insert(tree, &chars[i], &chars[i]); + } + + q_tree_foreach(tree, my_traverse, NULL); + + g_assert(q_tree_nnodes(tree) == strlen(chars)); + g_assert(q_tree_height(tree) == 6); + + p = chars; + q_tree_foreach(tree, check_order, &p); + + for (i = 0; i < 26; i++) { + removed = q_tree_remove(tree, &chars[i + 10]); + g_assert(removed); + } + + c = '\0'; + removed = q_tree_remove(tree, &c); + g_assert(!removed); + + q_tree_foreach(tree, my_traverse, NULL); + + g_assert(q_tree_nnodes(tree) == strlen(chars2)); + g_assert(q_tree_height(tree) == 6); + + p = chars2; + q_tree_foreach(tree, check_order, &p); + + for (i = 25; i >= 0; i--) { + q_tree_insert(tree, &chars[i + 10], &chars[i + 10]); + } + + p = chars; + q_tree_foreach(tree, check_order, &p); + + c = '0'; + p = q_tree_lookup(tree, &c); + g_assert(p && *p == c); + g_assert(q_tree_lookup_extended(tree, &c, (gpointer *)&d, (gpointer *)&p)); + g_assert(c == *d && c == *p); + + c = 'A'; + p = q_tree_lookup(tree, &c); + g_assert(p && *p == c); + + c = 'a'; + p = q_tree_lookup(tree, &c); + g_assert(p && *p == c); + + c = 'z'; + p = q_tree_lookup(tree, &c); + g_assert(p && *p == c); + + c = '!'; + p = q_tree_lookup(tree, &c); + g_assert(p == NULL); + + c = '='; + p = q_tree_lookup(tree, &c); + g_assert(p == NULL); + + c = '|'; + p = q_tree_lookup(tree, &c); + g_assert(p == NULL); + + c = '0'; + p = q_tree_search(tree, my_search, &c); + g_assert(p && *p == c); + + c = 'A'; + p = q_tree_search(tree, my_search, &c); + g_assert(p && *p == c); + + c = 'a'; + p = q_tree_search(tree, my_search, &c); + g_assert(p && *p == c); + + c = 'z'; + p = q_tree_search(tree, my_search, &c); + g_assert(p && *p == c); + + c = '!'; + p = q_tree_search(tree, my_search, &c); + g_assert(p == NULL); + + c = '='; + p = q_tree_search(tree, my_search, &c); + g_assert(p == NULL); + + c = '|'; + p = q_tree_search(tree, my_search, &c); + g_assert(p == NULL); + + q_tree_destroy(tree); +} + +static void test_tree_remove(void) +{ + QTree *tree; + char c, d; + gint i; + gboolean removed; + + tree = q_tree_new_full((GCompareDataFunc)my_compare, NULL, + my_key_destroy, + my_value_destroy); + + for (i = 0; chars[i]; i++) { + q_tree_insert(tree, &chars[i], &chars[i]); + } + + c = '0'; + q_tree_insert(tree, &c, &c); + g_assert(destroyed_key == &c); + g_assert(destroyed_value == &chars[0]); + destroyed_key = NULL; + destroyed_value = NULL; + + d = '1'; + q_tree_replace(tree, &d, &d); + g_assert(destroyed_key == &chars[1]); + g_assert(destroyed_value == &chars[1]); + destroyed_key = NULL; + destroyed_value = NULL; + + c = '2'; + removed = q_tree_remove(tree, &c); + g_assert(removed); + g_assert(destroyed_key == &chars[2]); + g_assert(destroyed_value == &chars[2]); + destroyed_key = NULL; + destroyed_value = NULL; + + c = '3'; + removed = q_tree_steal(tree, &c); + g_assert(removed); + g_assert(destroyed_key == NULL); + g_assert(destroyed_value == NULL); + + const gchar *remove = "omkjigfedba"; + for (i = 0; remove[i]; i++) { + removed = q_tree_remove(tree, &remove[i]); + g_assert(removed); + } + + q_tree_destroy(tree); +} + +static void test_tree_destroy(void) +{ + QTree *tree; + gint i; + + tree = q_tree_new(my_compare); + + for (i = 0; chars[i]; i++) { + q_tree_insert(tree, &chars[i], &chars[i]); + } + + g_assert(q_tree_nnodes(tree) == strlen(chars)); + + g_test_message("nnodes: %d", q_tree_nnodes(tree)); + q_tree_ref(tree); + q_tree_destroy(tree); + + g_test_message("nnodes: %d", q_tree_nnodes(tree)); + g_assert(q_tree_nnodes(tree) == 0); + + q_tree_unref(tree); +} + +static void test_tree_insert(void) +{ + QTree *tree; + gchar *p; + gint i; + gchar *scrambled; + + tree = q_tree_new(my_compare); + + for (i = 0; chars[i]; i++) { + q_tree_insert(tree, &chars[i], &chars[i]); + } + p = chars; + q_tree_foreach(tree, check_order, &p); + + q_tree_unref(tree); + tree = q_tree_new(my_compare); + + for (i = strlen(chars) - 1; i >= 0; i--) { + q_tree_insert(tree, &chars[i], &chars[i]); + } + p = chars; + q_tree_foreach(tree, check_order, &p); + + q_tree_unref(tree); + tree = q_tree_new(my_compare); + + scrambled = g_strdup(chars); + + for (i = 0; i < 30; i++) { + gchar tmp; + gint a, b; + + a = g_random_int_range(0, strlen(scrambled)); + b = g_random_int_range(0, strlen(scrambled)); + tmp = scrambled[a]; + scrambled[a] = scrambled[b]; + scrambled[b] = tmp; + } + + for (i = 0; scrambled[i]; i++) { + q_tree_insert(tree, &scrambled[i], &scrambled[i]); + } + p = chars; + q_tree_foreach(tree, check_order, &p); + + g_free(scrambled); + q_tree_unref(tree); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/qtree/search", test_tree_search); + g_test_add_func("/qtree/remove", test_tree_remove); + g_test_add_func("/qtree/destroy", test_tree_destroy); + g_test_add_func("/qtree/insert", test_tree_insert); + + return g_test_run(); +} diff --git a/tests/unit/test-rcu-list.c b/tests/unit/test-rcu-list.c index 64b81ae058..8f0adb8b00 100644 --- a/tests/unit/test-rcu-list.c +++ b/tests/unit/test-rcu-list.c @@ -14,8 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program. If not, see . * * Copyright (c) 2013 Mike D. Day, IBM Corporation. */ @@ -152,7 +151,7 @@ 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) + 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) diff --git a/tests/unit/test-replication.c b/tests/unit/test-replication.c index afff908d77..5d2003b8ce 100644 --- a/tests/unit/test-replication.c +++ b/tests/unit/test-replication.c @@ -199,17 +199,13 @@ static BlockBackend *start_primary(void) static void teardown_primary(void) { BlockBackend *blk; - AioContext *ctx; /* remove P_ID */ blk = blk_by_name(P_ID); assert(blk); - ctx = blk_get_aio_context(blk); - aio_context_acquire(ctx); monitor_remove_blk(blk); blk_unref(blk); - aio_context_release(ctx); } static void test_primary_read(void) @@ -345,27 +341,20 @@ static void teardown_secondary(void) { /* only need to destroy two BBs */ BlockBackend *blk; - AioContext *ctx; /* remove S_LOCAL_DISK_ID */ blk = blk_by_name(S_LOCAL_DISK_ID); assert(blk); - ctx = blk_get_aio_context(blk); - aio_context_acquire(ctx); monitor_remove_blk(blk); blk_unref(blk); - aio_context_release(ctx); /* remove S_ID */ blk = blk_by_name(S_ID); assert(blk); - ctx = blk_get_aio_context(blk); - aio_context_acquire(ctx); monitor_remove_blk(blk); blk_unref(blk); - aio_context_release(ctx); } static void test_secondary_read(void) diff --git a/tests/unit/test-resv-mem.c b/tests/unit/test-resv-mem.c new file mode 100644 index 0000000000..cd8f7318cc --- /dev/null +++ b/tests/unit/test-resv-mem.c @@ -0,0 +1,320 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * reserved-region/range.c unit-tests. + * + * Copyright (C) 2023, Red Hat, Inc. + * + * Author: Eric Auger + */ + +#include "qemu/osdep.h" +#include "qemu/range.h" +#include "exec/memory.h" +#include "qemu/reserved-region.h" + +#define DEBUG 0 + +#if DEBUG +static void print_ranges(const char *prefix, GList *ranges) +{ + GList *l; + int i = 0; + + if (!g_list_length(ranges)) { + printf("%s is void\n", prefix); + return; + } + for (l = ranges; l; l = l->next) { + Range *r = (Range *)l->data; + + printf("%s rev[%i] = [0x%"PRIx64",0x%"PRIx64"]\n", + prefix, i, range_lob(r), range_upb(r)); + i++; + } +} +#endif + +static void compare_ranges(const char *prefix, GList *ranges, + GList *expected) +{ + GList *l, *e; + +#if DEBUG + print_ranges("out", ranges); + print_ranges("expected", expected); +#endif + if (!expected) { + g_assert_true(!ranges); + return; + } + g_assert_cmpint(g_list_length(ranges), ==, g_list_length(expected)); + for (l = ranges, e = expected; l ; l = l->next, e = e->next) { + Range *r = (Range *)l->data; + Range *er = (Range *)e->data; + + g_assert_true(range_lob(r) == range_lob(er) && + range_upb(r) == range_upb(er)); + } +} + +static GList *insert_sorted_range(GList *list, uint64_t lob, uint64_t upb) +{ + Range *new = g_new0(Range, 1); + + range_set_bounds(new, lob, upb); + return range_list_insert(list, new); +} + +static void reset(GList **in, GList **out, GList **expected) +{ + g_list_free_full(*in, g_free); + g_list_free_full(*out, g_free); + g_list_free_full(*expected, g_free); + *in = NULL; + *out = NULL; + *expected = NULL; +} + +static void +run_range_inverse_array(const char *prefix, GList **in, GList **expected, + uint64_t low, uint64_t high) +{ + GList *out = NULL; + range_inverse_array(*in, &out, low, high); + compare_ranges(prefix, out, *expected); + reset(in, &out, expected); +} + +static void check_range_reverse_array(void) +{ + GList *in = NULL, *expected = NULL; + + /* test 1 */ + + in = insert_sorted_range(in, 0x10000, UINT64_MAX); + expected = insert_sorted_range(expected, 0x0, 0xFFFF); + run_range_inverse_array("test1", &in, &expected, 0x0, UINT64_MAX); + + /* test 2 */ + + in = insert_sorted_range(in, 0x10000, 0xFFFFFFFFFFFF); + expected = insert_sorted_range(expected, 0x0, 0xFFFF); + expected = insert_sorted_range(expected, 0x1000000000000, UINT64_MAX); + run_range_inverse_array("test1", &in, &expected, 0x0, UINT64_MAX); + + /* test 3 */ + + in = insert_sorted_range(in, 0x0, 0xFFFF); + in = insert_sorted_range(in, 0x10000, 0x2FFFF); + expected = insert_sorted_range(expected, 0x30000, UINT64_MAX); + run_range_inverse_array("test1", &in, &expected, 0x0, UINT64_MAX); + + /* test 4 */ + + in = insert_sorted_range(in, 0x50000, 0x5FFFF); + in = insert_sorted_range(in, 0x60000, 0xFFFFFFFFFFFF); + expected = insert_sorted_range(expected, 0x0, 0x4FFFF); + expected = insert_sorted_range(expected, 0x1000000000000, UINT64_MAX); + run_range_inverse_array("test1", &in, &expected, 0x0, UINT64_MAX); + + /* test 5 */ + + in = insert_sorted_range(in, 0x0, UINT64_MAX); + run_range_inverse_array("test1", &in, &expected, 0x0, UINT64_MAX); + + /* test 6 */ + in = insert_sorted_range(in, 0x10000, 0x1FFFF); + in = insert_sorted_range(in, 0x30000, 0x6FFFF); + in = insert_sorted_range(in, 0x90000, UINT64_MAX); + expected = insert_sorted_range(expected, 0x0, 0xFFFF); + expected = insert_sorted_range(expected, 0x20000, 0x2FFFF); + expected = insert_sorted_range(expected, 0x70000, 0x8FFFF); + run_range_inverse_array("test1", &in, &expected, 0x0, UINT64_MAX); +} + +static void check_range_reverse_array_low_end(void) +{ + GList *in = NULL, *expected = NULL; + + /* test 1 */ + in = insert_sorted_range(in, 0x0, UINT64_MAX); + run_range_inverse_array("test1", &in, &expected, 0x10000, 0xFFFFFF); + + /* test 2 */ + + in = insert_sorted_range(in, 0x0, 0xFFFF); + in = insert_sorted_range(in, 0x20000, 0x2FFFF); + expected = insert_sorted_range(expected, 0x40000, 0xFFFFFFFFFFFF); + run_range_inverse_array("test2", &in, &expected, 0x40000, 0xFFFFFFFFFFFF); + + /* test 3 */ + in = insert_sorted_range(in, 0x0, 0xFFFF); + in = insert_sorted_range(in, 0x20000, 0x2FFFF); + in = insert_sorted_range(in, 0x1000000000000, UINT64_MAX); + expected = insert_sorted_range(expected, 0x40000, 0xFFFFFFFFFFFF); + run_range_inverse_array("test3", &in, &expected, 0x40000, 0xFFFFFFFFFFFF); + + /* test 4 */ + + in = insert_sorted_range(in, 0x0, 0xFFFF); + in = insert_sorted_range(in, 0x20000, 0x2FFFF); + in = insert_sorted_range(in, 0x1000000000000, UINT64_MAX); + expected = insert_sorted_range(expected, 0x30000, 0xFFFFFFFFFFFF); + run_range_inverse_array("test4", &in, &expected, 0x20000, 0xFFFFFFFFFFFF); + + /* test 5 */ + + in = insert_sorted_range(in, 0x2000, 0xFFFF); + in = insert_sorted_range(in, 0x20000, 0x2FFFF); + in = insert_sorted_range(in, 0x100000000, 0x1FFFFFFFF); + expected = insert_sorted_range(expected, 0x1000, 0x1FFF); + expected = insert_sorted_range(expected, 0x10000, 0x1FFFF); + expected = insert_sorted_range(expected, 0x30000, 0xFFFFFFFF); + expected = insert_sorted_range(expected, 0x200000000, 0xFFFFFFFFFFFF); + run_range_inverse_array("test5", &in, &expected, 0x1000, 0xFFFFFFFFFFFF); + + /* test 6 */ + + in = insert_sorted_range(in, 0x10000000 , 0x1FFFFFFF); + in = insert_sorted_range(in, 0x100000000, 0x1FFFFFFFF); + expected = insert_sorted_range(expected, 0x0, 0xFFFF); + run_range_inverse_array("test6", &in, &expected, 0x0, 0xFFFF); +} + +static ReservedRegion *alloc_resv_mem(unsigned type, uint64_t lob, uint64_t upb) +{ + ReservedRegion *r; + + r = g_new0(ReservedRegion, 1); + r->type = type; + range_set_bounds(&r->range, lob, upb); + return r; +} + +static void print_resv_region_list(const char *prefix, GList *list, + uint32_t expected_length) +{ + int i = g_list_length(list); + + g_assert_cmpint(i, ==, expected_length); +#if DEBUG + i = 0; + for (GList *l = list; l; l = l->next) { + ReservedRegion *r = (ReservedRegion *)l->data; + Range *range = &r->range; + + printf("%s item[%d]=[0x%x, 0x%"PRIx64", 0x%"PRIx64"]\n", + prefix, i++, r->type, range_lob(range), range_upb(range)); + } +#endif +} + +static void free_resv_region(gpointer data) +{ + ReservedRegion *reg = (ReservedRegion *)data; + + g_free(reg); +} + +static void check_resv_region_list_insert(void) +{ + ReservedRegion *r[10]; + GList *l = NULL; + + r[0] = alloc_resv_mem(0xA, 0, 0xFFFF); + r[1] = alloc_resv_mem(0xA, 0x20000, 0x2FFFF); + l = resv_region_list_insert(l, r[0]); + l = resv_region_list_insert(l, r[1]); + print_resv_region_list("test1", l, 2); + + /* adjacent on left */ + r[2] = alloc_resv_mem(0xB, 0x0, 0xFFF); + l = resv_region_list_insert(l, r[2]); + /* adjacent on right */ + r[3] = alloc_resv_mem(0xC, 0x21000, 0x2FFFF); + l = resv_region_list_insert(l, r[3]); + print_resv_region_list("test2", l, 4); + + /* exact overlap of D into C*/ + r[4] = alloc_resv_mem(0xD, 0x21000, 0x2FFFF); + l = resv_region_list_insert(l, r[4]); + print_resv_region_list("test3", l, 4); + + /* in the middle */ + r[5] = alloc_resv_mem(0xE, 0x22000, 0x23FFF); + l = resv_region_list_insert(l, r[5]); + print_resv_region_list("test4", l, 6); + + /* overwrites several existing ones */ + r[6] = alloc_resv_mem(0xF, 0x10000, 0x2FFFF); + l = resv_region_list_insert(l, r[6]); + print_resv_region_list("test5", l, 3); + + /* contiguous at the end */ + r[7] = alloc_resv_mem(0x0, 0x30000, 0x40000); + l = resv_region_list_insert(l, r[7]); + print_resv_region_list("test6", l, 4); + + g_list_free_full(l, free_resv_region); + l = NULL; + + r[0] = alloc_resv_mem(0x0, 0x10000, 0x1FFFF); + l = resv_region_list_insert(l, r[0]); + /* insertion before the 1st item */ + r[1] = alloc_resv_mem(0x1, 0x0, 0xFF); + l = resv_region_list_insert(l, r[1]); + print_resv_region_list("test8", l, 2); + + /* collision on the left side */ + r[2] = alloc_resv_mem(0xA, 0x1200, 0x11FFF); + l = resv_region_list_insert(l, r[2]); + print_resv_region_list("test9", l, 3); + + /* collision on the right side */ + r[3] = alloc_resv_mem(0xA, 0x1F000, 0x2FFFF); + l = resv_region_list_insert(l, r[3]); + print_resv_region_list("test10", l, 4); + + /* override everything */ + r[4] = alloc_resv_mem(0xF, 0x0, UINT64_MAX); + l = resv_region_list_insert(l, r[4]); + print_resv_region_list("test11", l, 1); + + g_list_free_full(l, free_resv_region); + l = NULL; + + r[0] = alloc_resv_mem(0xF, 0x1000000000000, UINT64_MAX); + l = resv_region_list_insert(l, r[0]); + print_resv_region_list("test12", l, 1); + + r[1] = alloc_resv_mem(0xA, 0x0, 0xFFFFFFF); + l = resv_region_list_insert(l, r[1]); + print_resv_region_list("test12", l, 2); + + r[2] = alloc_resv_mem(0xB, 0x100000000, 0x1FFFFFFFF); + l = resv_region_list_insert(l, r[2]); + print_resv_region_list("test12", l, 3); + + r[3] = alloc_resv_mem(0x0, 0x010000000, 0x2FFFFFFFF); + l = resv_region_list_insert(l, r[3]); + print_resv_region_list("test12", l, 3); + + g_list_free_full(l, free_resv_region); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/resv-mem/range_reverse_array", + check_range_reverse_array); + g_test_add_func("/resv-mem/range_reverse_array_low_end", + check_range_reverse_array_low_end); + g_test_add_func("/resv-mem/resv_region_list_insert", + check_resv_region_list_insert); + + g_test_run(); + + return 0; +} diff --git a/tests/unit/test-seccomp.c b/tests/unit/test-seccomp.c index 3d7771e46c..bab93fd6da 100644 --- a/tests/unit/test-seccomp.c +++ b/tests/unit/test-seccomp.c @@ -25,7 +25,6 @@ #include "qapi/error.h" #include "qemu/module.h" -#include #include static void test_seccomp_helper(const char *args, bool killed, @@ -230,26 +229,26 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); if (can_play_with_seccomp()) { #ifdef SYS_fork - g_test_add_func("/softmmu/seccomp/sys-fork/on", + g_test_add_func("/seccomp/sys-fork/on", test_seccomp_sys_fork_on); - g_test_add_func("/softmmu/seccomp/sys-fork/on-nospawn", + g_test_add_func("/seccomp/sys-fork/on-nospawn", test_seccomp_sys_fork_on_nospawn); - g_test_add_func("/softmmu/seccomp/sys-fork/off", + g_test_add_func("/seccomp/sys-fork/off", test_seccomp_sys_fork_off); #endif - g_test_add_func("/softmmu/seccomp/fork/on", + g_test_add_func("/seccomp/fork/on", test_seccomp_fork_on); - g_test_add_func("/softmmu/seccomp/fork/on-nospawn", + g_test_add_func("/seccomp/fork/on-nospawn", test_seccomp_fork_on_nospawn); - g_test_add_func("/softmmu/seccomp/fork/off", + g_test_add_func("/seccomp/fork/off", test_seccomp_fork_off); - g_test_add_func("/softmmu/seccomp/thread/on", + g_test_add_func("/seccomp/thread/on", test_seccomp_thread_on); - g_test_add_func("/softmmu/seccomp/thread/on-nospawn", + g_test_add_func("/seccomp/thread/on-nospawn", test_seccomp_thread_on_nospawn); - g_test_add_func("/softmmu/seccomp/thread/off", + g_test_add_func("/seccomp/thread/off", test_seccomp_thread_off); if (doit_sched() == 0) { @@ -257,11 +256,11 @@ int main(int argc, char **argv) * musl doesn't impl sched_setscheduler, hence * we check above if it works first */ - g_test_add_func("/softmmu/seccomp/sched/on", + g_test_add_func("/seccomp/sched/on", test_seccomp_sched_on); - g_test_add_func("/softmmu/seccomp/sched/on-nores", + g_test_add_func("/seccomp/sched/on-nores", test_seccomp_sched_on_nores); - g_test_add_func("/softmmu/seccomp/sched/off", + g_test_add_func("/seccomp/sched/off", test_seccomp_sched_off); } } diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index fdc39a846c..8994337e12 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -20,8 +20,8 @@ #define T true #define F false -#define MIN_CPUS 1 /* set the min CPUs supported by the machine as 1 */ -#define MAX_CPUS 512 /* set the max CPUs supported by the machine as 512 */ +#define MIN_CPUS 1 /* set the min CPUs supported by the machine as 1 */ +#define MAX_CPUS 4096 /* set the max CPUs supported by the machine as 4096 */ #define SMP_MACHINE_NAME "TEST-SMP" @@ -75,6 +75,40 @@ .has_maxcpus = hf, .maxcpus = f, \ } +/* + * Currently a 5-level topology hierarchy is supported on s390 ccw machines + * -drawers/books/sockets/cores/threads + */ +#define SMP_CONFIG_WITH_BOOKS_DRAWERS(ha, a, hb, b, hc, c, hd, \ + d, he, e, hf, f, hg, g) \ + { \ + .has_cpus = ha, .cpus = a, \ + .has_drawers = hb, .drawers = b, \ + .has_books = hc, .books = c, \ + .has_sockets = hd, .sockets = d, \ + .has_cores = he, .cores = e, \ + .has_threads = hf, .threads = f, \ + .has_maxcpus = hg, .maxcpus = g, \ + } + +/* + * Currently QEMU supports up to a 7-level topology hierarchy, which is the + * QEMU's unified abstract representation of CPU topology. + * -drawers/books/sockets/dies/clusters/cores/threads + */ +#define SMP_CONFIG_WITH_FULL_TOPO(a, b, c, d, e, f, g, h, i) \ + { \ + .has_cpus = true, .cpus = a, \ + .has_drawers = true, .drawers = b, \ + .has_books = true, .books = c, \ + .has_sockets = true, .sockets = d, \ + .has_dies = true, .dies = e, \ + .has_clusters = true, .clusters = f, \ + .has_cores = true, .cores = g, \ + .has_threads = true, .threads = h, \ + .has_maxcpus = true, .maxcpus = i, \ + } + /** * @config - the given SMP configuration * @expect_prefer_sockets - the expected parsing result for the @@ -308,6 +342,16 @@ static const struct SMPTestData data_generic_invalid[] = { /* config: -smp 2,clusters=2 */ .config = SMP_CONFIG_WITH_CLUSTERS(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0), .expect_error = "clusters not supported by this machine's CPU topology", + }, { + /* config: -smp 2,books=2 */ + .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 2, F, 0, T, 2, F, + 0, F, 0, F, 0, F, 0), + .expect_error = "books not supported by this machine's CPU topology", + }, { + /* config: -smp 2,drawers=2 */ + .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 2, T, 2, F, 0, F, + 0, F, 0, F, 0, F, 0), + .expect_error = "drawers not supported by this machine's CPU topology", }, { /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=8 */ .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 8), @@ -323,17 +367,23 @@ static const struct SMPTestData data_generic_invalid[] = { "sockets (2) * cores (4) * threads (2) " "== maxcpus (16) < smp_cpus (18)", }, { - /* config: -smp 1 - * should tweak the supported min CPUs to 2 for testing */ - .config = SMP_CONFIG_GENERIC(T, 1, F, 0, F, 0, F, 0, F, 0), + /* + * config: -smp 1 + * The test machine should tweak the supported min CPUs to + * 2 (MIN_CPUS + 1) for testing. + */ + .config = SMP_CONFIG_GENERIC(T, MIN_CPUS, F, 0, F, 0, F, 0, F, 0), .expect_error = "Invalid SMP CPUs 1. The min CPUs supported " "by machine '" SMP_MACHINE_NAME "' is 2", }, { - /* config: -smp 512 - * should tweak the supported max CPUs to 511 for testing */ - .config = SMP_CONFIG_GENERIC(T, 512, F, 0, F, 0, F, 0, F, 0), - .expect_error = "Invalid SMP CPUs 512. The max CPUs supported " - "by machine '" SMP_MACHINE_NAME "' is 511", + /* + * config: -smp 4096 + * The test machine should tweak the supported max CPUs to + * 4095 (MAX_CPUS - 1) for testing. + */ + .config = SMP_CONFIG_GENERIC(T, 4096, F, 0, F, 0, F, 0, F, 0), + .expect_error = "Invalid SMP CPUs 4096. The max CPUs supported " + "by machine '" SMP_MACHINE_NAME "' is 4095", }, }; @@ -373,11 +423,199 @@ static const struct SMPTestData data_with_clusters_invalid[] = { }, }; +static const struct SMPTestData data_with_books_invalid[] = { + { + /* config: -smp 16,books=2,sockets=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 16, F, 1, T, 2, T, + 2, T, 4, T, 2, T, 16), + .expect_error = "Invalid CPU topology: " + "product of the hierarchy must match maxcpus: " + "books (2) * sockets (2) * cores (4) * threads (2) " + "!= maxcpus (16)", + }, { + /* config: -smp 34,books=2,sockets=2,cores=4,threads=2,maxcpus=32 */ + .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 34, F, 1, T, 2, T, + 2, T, 4, T, 2, T, 32), + .expect_error = "Invalid CPU topology: " + "maxcpus must be equal to or greater than smp: " + "books (2) * sockets (2) * cores (4) * threads (2) " + "== maxcpus (32) < smp_cpus (34)", + }, +}; + +static const struct SMPTestData data_with_drawers_invalid[] = { + { + /* config: -smp 16,drawers=2,sockets=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 16, T, 2, F, 1, T, + 2, T, 4, T, 2, T, 16), + .expect_error = "Invalid CPU topology: " + "product of the hierarchy must match maxcpus: " + "drawers (2) * sockets (2) * cores (4) * threads (2) " + "!= maxcpus (16)", + }, { + /* config: -smp 34,drawers=2,sockets=2,cores=4,threads=2,maxcpus=32 */ + .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 34, T, 2, F, 1, T, + 2, T, 4, T, 2, T, 32), + .expect_error = "Invalid CPU topology: " + "maxcpus must be equal to or greater than smp: " + "drawers (2) * sockets (2) * cores (4) * threads (2) " + "== maxcpus (32) < smp_cpus (34)", + }, +}; + +static const struct SMPTestData data_with_drawers_books_invalid[] = { + { + /* + * config: -smp 200,drawers=2,books=2,sockets=2,cores=4,\ + * threads=2,maxcpus=200 + */ + .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 200, T, 3, T, 5, T, + 2, T, 4, T, 2, T, 200), + .expect_error = "Invalid CPU topology: " + "product of the hierarchy must match maxcpus: " + "drawers (3) * books (5) * sockets (2) * " + "cores (4) * threads (2) != maxcpus (200)", + }, { + /* + * config: -smp 242,drawers=2,books=2,sockets=2,cores=4,\ + * threads=2,maxcpus=240 + */ + .config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 242, T, 3, T, 5, T, + 2, T, 4, T, 2, T, 240), + .expect_error = "Invalid CPU topology: " + "maxcpus must be equal to or greater than smp: " + "drawers (3) * books (5) * sockets (2) * " + "cores (4) * threads (2) " + "== maxcpus (240) < smp_cpus (242)", + }, +}; + +static const struct SMPTestData data_full_topo_invalid[] = { + { + /* + * config: -smp 200,drawers=3,books=5,sockets=2,dies=4,\ + * clusters=2,cores=7,threads=2,maxcpus=200 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(200, 3, 5, 2, 4, 2, 7, 2, 200), + .expect_error = "Invalid CPU topology: " + "product of the hierarchy must match maxcpus: " + "drawers (3) * books (5) * sockets (2) * dies (4) * " + "clusters (2) * cores (7) * threads (2) " + "!= maxcpus (200)", + }, { + /* + * config: -smp 3361,drawers=3,books=5,sockets=2,dies=4,\ + * clusters=2,cores=7,threads=2,maxcpus=3360 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(3361, 3, 5, 2, 4, 2, 7, 2, 3360), + .expect_error = "Invalid CPU topology: " + "maxcpus must be equal to or greater than smp: " + "drawers (3) * books (5) * sockets (2) * dies (4) * " + "clusters (2) * cores (7) * threads (2) " + "== maxcpus (3360) < smp_cpus (3361)", + }, { + /* + * config: -smp 1,drawers=3,books=5,sockets=2,dies=4,\ + * clusters=2,cores=7,threads=3,maxcpus=5040 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(3361, 3, 5, 2, 4, 2, 7, 3, 5040), + .expect_error = "Invalid SMP CPUs 5040. The max CPUs supported " + "by machine '" SMP_MACHINE_NAME "' is 4096", + }, +}; + +static const struct SMPTestData data_zero_topo_invalid[] = { + { + /* + * Test "cpus=0". + * config: -smp 0,drawers=1,books=1,sockets=1,dies=1,\ + * clusters=1,cores=1,threads=1,maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(0, 1, 1, 1, 1, 1, 1, 1, 1), + .expect_error = "Invalid CPU topology: CPU topology parameters must " + "be greater than zero", + }, { + /* + * Test "drawers=0". + * config: -smp 1,drawers=0,books=1,sockets=1,dies=1,\ + * clusters=1,cores=1,threads=1,maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 0, 1, 1, 1, 1, 1, 1, 1), + .expect_error = "Invalid CPU topology: CPU topology parameters must " + "be greater than zero", + }, { + /* + * Test "books=0". + * config: -smp 1,drawers=1,books=0,sockets=1,dies=1,\ + * clusters=1,cores=1,threads=1,maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 0, 1, 1, 1, 1, 1, 1), + .expect_error = "Invalid CPU topology: CPU topology parameters must " + "be greater than zero", + }, { + /* + * Test "sockets=0". + * config: -smp 1,drawers=1,books=1,sockets=0,dies=1,\ + * clusters=1,cores=1,threads=1,maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 0, 1, 1, 1, 1, 1), + .expect_error = "Invalid CPU topology: CPU topology parameters must " + "be greater than zero", + }, { + /* + * Test "dies=0". + * config: -smp 1,drawers=1,books=1,sockets=1,dies=0,\ + * clusters=1,cores=1,threads=1,maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 0, 1, 1, 1, 1), + .expect_error = "Invalid CPU topology: CPU topology parameters must " + "be greater than zero", + }, { + /* + * Test "clusters=0". + * config: -smp 1,drawers=1,books=1,sockets=1,dies=1,\ + * clusters=0,cores=1,threads=1,maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 0, 1, 1, 1), + .expect_error = "Invalid CPU topology: CPU topology parameters must " + "be greater than zero", + }, { + /* + * Test "cores=0". + * config: -smp 1,drawers=1,books=1,sockets=1,dies=1,\ + * clusters=1,cores=0,threads=1,maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 1, 0, 1, 1), + .expect_error = "Invalid CPU topology: CPU topology parameters must " + "be greater than zero", + }, { + /* + * Test "threads=0". + * config: -smp 1,drawers=1,books=1,sockets=1,dies=1,\ + * clusters=1,cores=1,threads=0,maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 1, 1, 0, 1), + .expect_error = "Invalid CPU topology: CPU topology parameters must " + "be greater than zero", + }, { + /* + * Test "maxcpus=0". + * config: -smp 1,drawers=1,books=1,sockets=1,dies=1,\ + * clusters=1,cores=1,threads=1,maxcpus=0 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 1, 1, 1, 0), + .expect_error = "Invalid CPU topology: CPU topology parameters must " + "be greater than zero", + }, +}; + static char *smp_config_to_string(const SMPConfiguration *config) { return g_strdup_printf( "(SMPConfiguration) {\n" " .has_cpus = %5s, cpus = %" PRId64 ",\n" + " .has_drawers = %5s, drawers = %" PRId64 ",\n" + " .has_books = %5s, books = %" PRId64 ",\n" " .has_sockets = %5s, sockets = %" PRId64 ",\n" " .has_dies = %5s, dies = %" PRId64 ",\n" " .has_clusters = %5s, clusters = %" PRId64 ",\n" @@ -386,6 +624,8 @@ static char *smp_config_to_string(const SMPConfiguration *config) " .has_maxcpus = %5s, maxcpus = %" PRId64 ",\n" "}", config->has_cpus ? "true" : "false", config->cpus, + config->has_drawers ? "true" : "false", config->drawers, + config->has_books ? "true" : "false", config->books, config->has_sockets ? "true" : "false", config->sockets, config->has_dies ? "true" : "false", config->dies, config->has_clusters ? "true" : "false", config->clusters, @@ -394,46 +634,100 @@ static char *smp_config_to_string(const SMPConfiguration *config) config->has_maxcpus ? "true" : "false", config->maxcpus); } -static char *cpu_topology_to_string(const CpuTopology *topo) +/* Use the different calculation than machine_topo_get_threads_per_socket(). */ +static unsigned int cpu_topology_get_threads_per_socket(const CpuTopology *topo) +{ + /* Check the divisor to avoid invalid topology examples causing SIGFPE. */ + if (!topo->drawers || !topo->books || !topo->sockets) { + return 0; + } else { + return topo->max_cpus / topo->drawers / topo->books / topo->sockets; + } +} + +/* Use the different calculation than machine_topo_get_cores_per_socket(). */ +static unsigned int cpu_topology_get_cores_per_socket(const CpuTopology *topo) +{ + /* Check the divisor to avoid invalid topology examples causing SIGFPE. */ + if (!topo->threads) { + return 0; + } else { + return cpu_topology_get_threads_per_socket(topo) / topo->threads; + } +} + +static char *cpu_topology_to_string(const CpuTopology *topo, + unsigned int threads_per_socket, + unsigned int cores_per_socket, + bool has_clusters) { return g_strdup_printf( "(CpuTopology) {\n" - " .cpus = %u,\n" - " .sockets = %u,\n" - " .dies = %u,\n" - " .clusters = %u,\n" - " .cores = %u,\n" - " .threads = %u,\n" - " .max_cpus = %u,\n" + " .cpus = %u,\n" + " .drawers = %u,\n" + " .books = %u,\n" + " .sockets = %u,\n" + " .dies = %u,\n" + " .clusters = %u,\n" + " .cores = %u,\n" + " .threads = %u,\n" + " .max_cpus = %u,\n" + " .threads_per_socket = %u,\n" + " .cores_per_socket = %u,\n" + " .has_clusters = %s,\n" "}", - topo->cpus, topo->sockets, topo->dies, topo->clusters, - topo->cores, topo->threads, topo->max_cpus); + topo->cpus, topo->drawers, topo->books, + topo->sockets, topo->dies, topo->clusters, + topo->cores, topo->threads, topo->max_cpus, + threads_per_socket, cores_per_socket, + has_clusters ? "true" : "false"); } static void check_parse(MachineState *ms, const SMPConfiguration *config, const CpuTopology *expect_topo, const char *expect_err, bool is_valid) { + MachineClass *mc = MACHINE_GET_CLASS(ms); g_autofree char *config_str = smp_config_to_string(config); - g_autofree char *expect_topo_str = cpu_topology_to_string(expect_topo); - g_autofree char *output_topo_str = NULL; + g_autofree char *expect_topo_str = NULL, *output_topo_str = NULL; + unsigned int expect_threads_per_socket, expect_cores_per_socket; + unsigned int ms_threads_per_socket, ms_cores_per_socket; Error *err = NULL; + expect_threads_per_socket = + cpu_topology_get_threads_per_socket(expect_topo); + expect_cores_per_socket = + cpu_topology_get_cores_per_socket(expect_topo); + expect_topo_str = cpu_topology_to_string(expect_topo, + expect_threads_per_socket, + expect_cores_per_socket, + config->has_clusters); + /* call the generic parser */ machine_parse_smp_config(ms, config, &err); - output_topo_str = cpu_topology_to_string(&ms->smp); + ms_threads_per_socket = machine_topo_get_threads_per_socket(ms); + ms_cores_per_socket = machine_topo_get_cores_per_socket(ms); + output_topo_str = cpu_topology_to_string(&ms->smp, + ms_threads_per_socket, + ms_cores_per_socket, + mc->smp_props.has_clusters); /* when the configuration is supposed to be valid */ if (is_valid) { if ((err == NULL) && (ms->smp.cpus == expect_topo->cpus) && + (ms->smp.drawers == expect_topo->drawers) && + (ms->smp.books == expect_topo->books) && (ms->smp.sockets == expect_topo->sockets) && (ms->smp.dies == expect_topo->dies) && (ms->smp.clusters == expect_topo->clusters) && (ms->smp.cores == expect_topo->cores) && (ms->smp.threads == expect_topo->threads) && - (ms->smp.max_cpus == expect_topo->max_cpus)) { + (ms->smp.max_cpus == expect_topo->max_cpus) && + (ms_threads_per_socket == expect_threads_per_socket) && + (ms_cores_per_socket == expect_cores_per_socket) && + (mc->smp_props.has_clusters == config->has_clusters)) { return; } @@ -517,6 +811,16 @@ static void unsupported_params_init(const MachineClass *mc, SMPTestData *data) data->expect_prefer_sockets.clusters = 1; data->expect_prefer_cores.clusters = 1; } + + if (!mc->smp_props.books_supported) { + data->expect_prefer_sockets.books = 1; + data->expect_prefer_cores.books = 1; + } + + if (!mc->smp_props.drawers_supported) { + data->expect_prefer_sockets.drawers = 1; + data->expect_prefer_cores.drawers = 1; + } } static void machine_base_class_init(ObjectClass *oc, void *data) @@ -534,8 +838,8 @@ static void machine_generic_invalid_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); /* Force invalid min CPUs and max CPUs */ - mc->min_cpus = 2; - mc->max_cpus = 511; + mc->min_cpus = MIN_CPUS + 1; + mc->max_cpus = MAX_CPUS - 1; } static void machine_with_dies_class_init(ObjectClass *oc, void *data) @@ -552,6 +856,38 @@ static void machine_with_clusters_class_init(ObjectClass *oc, void *data) mc->smp_props.clusters_supported = true; } +static void machine_with_books_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->smp_props.books_supported = true; +} + +static void machine_with_drawers_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->smp_props.drawers_supported = true; +} + +static void machine_with_drawers_books_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->smp_props.drawers_supported = true; + mc->smp_props.books_supported = true; +} + +static void machine_full_topo_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->smp_props.drawers_supported = true; + mc->smp_props.books_supported = true; + mc->smp_props.dies_supported = true; + mc->smp_props.clusters_supported = true; +} + static void test_generic_valid(const void *opaque) { const char *machine_type = opaque; @@ -566,11 +902,6 @@ static void test_generic_valid(const void *opaque) unsupported_params_init(mc, &data); smp_parse_test(ms, &data, true); - - /* Unsupported parameters can be provided with their values as 1 */ - data.config.has_dies = true; - data.config.dies = 1; - smp_parse_test(ms, &data, true); } object_unref(obj); @@ -695,6 +1026,248 @@ static void test_with_clusters(const void *opaque) object_unref(obj); } +static void test_with_books(const void *opaque) +{ + const char *machine_type = opaque; + Object *obj = object_new(machine_type); + MachineState *ms = MACHINE(obj); + MachineClass *mc = MACHINE_GET_CLASS(obj); + SMPTestData data = {}; + unsigned int num_books = 2; + int i; + + for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { + data = data_generic_valid[i]; + unsupported_params_init(mc, &data); + + /* when books parameter is omitted, it will be set as 1 */ + data.expect_prefer_sockets.books = 1; + data.expect_prefer_cores.books = 1; + + smp_parse_test(ms, &data, true); + + /* when books parameter is specified */ + data.config.has_books = true; + data.config.books = num_books; + if (data.config.has_cpus) { + data.config.cpus *= num_books; + } + if (data.config.has_maxcpus) { + data.config.maxcpus *= num_books; + } + + data.expect_prefer_sockets.books = num_books; + data.expect_prefer_sockets.cpus *= num_books; + data.expect_prefer_sockets.max_cpus *= num_books; + data.expect_prefer_cores.books = num_books; + data.expect_prefer_cores.cpus *= num_books; + data.expect_prefer_cores.max_cpus *= num_books; + + smp_parse_test(ms, &data, true); + } + + for (i = 0; i < ARRAY_SIZE(data_with_books_invalid); i++) { + data = data_with_books_invalid[i]; + unsupported_params_init(mc, &data); + + smp_parse_test(ms, &data, false); + } + + object_unref(obj); +} + +static void test_with_drawers(const void *opaque) +{ + const char *machine_type = opaque; + Object *obj = object_new(machine_type); + MachineState *ms = MACHINE(obj); + MachineClass *mc = MACHINE_GET_CLASS(obj); + SMPTestData data = {}; + unsigned int num_drawers = 2; + int i; + + for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { + data = data_generic_valid[i]; + unsupported_params_init(mc, &data); + + /* when drawers parameter is omitted, it will be set as 1 */ + data.expect_prefer_sockets.drawers = 1; + data.expect_prefer_cores.drawers = 1; + + smp_parse_test(ms, &data, true); + + /* when drawers parameter is specified */ + data.config.has_drawers = true; + data.config.drawers = num_drawers; + if (data.config.has_cpus) { + data.config.cpus *= num_drawers; + } + if (data.config.has_maxcpus) { + data.config.maxcpus *= num_drawers; + } + + data.expect_prefer_sockets.drawers = num_drawers; + data.expect_prefer_sockets.cpus *= num_drawers; + data.expect_prefer_sockets.max_cpus *= num_drawers; + data.expect_prefer_cores.drawers = num_drawers; + data.expect_prefer_cores.cpus *= num_drawers; + data.expect_prefer_cores.max_cpus *= num_drawers; + + smp_parse_test(ms, &data, true); + } + + for (i = 0; i < ARRAY_SIZE(data_with_drawers_invalid); i++) { + data = data_with_drawers_invalid[i]; + unsupported_params_init(mc, &data); + + smp_parse_test(ms, &data, false); + } + + object_unref(obj); +} + +static void test_with_drawers_books(const void *opaque) +{ + const char *machine_type = opaque; + Object *obj = object_new(machine_type); + MachineState *ms = MACHINE(obj); + MachineClass *mc = MACHINE_GET_CLASS(obj); + SMPTestData data = {}; + unsigned int num_drawers = 5, num_books = 3; + int i; + + for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { + data = data_generic_valid[i]; + unsupported_params_init(mc, &data); + + /* + * when drawers and books parameters are omitted, they will + * be both set as 1. + */ + data.expect_prefer_sockets.drawers = 1; + data.expect_prefer_sockets.books = 1; + data.expect_prefer_cores.drawers = 1; + data.expect_prefer_cores.books = 1; + + smp_parse_test(ms, &data, true); + + /* when drawers and books parameters are both specified */ + data.config.has_drawers = true; + data.config.drawers = num_drawers; + data.config.has_books = true; + data.config.books = num_books; + + if (data.config.has_cpus) { + data.config.cpus *= num_drawers * num_books; + } + if (data.config.has_maxcpus) { + data.config.maxcpus *= num_drawers * num_books; + } + + data.expect_prefer_sockets.drawers = num_drawers; + data.expect_prefer_sockets.books = num_books; + data.expect_prefer_sockets.cpus *= num_drawers * num_books; + data.expect_prefer_sockets.max_cpus *= num_drawers * num_books; + + data.expect_prefer_cores.drawers = num_drawers; + data.expect_prefer_cores.books = num_books; + data.expect_prefer_cores.cpus *= num_drawers * num_books; + data.expect_prefer_cores.max_cpus *= num_drawers * num_books; + + smp_parse_test(ms, &data, true); + } + + for (i = 0; i < ARRAY_SIZE(data_with_drawers_books_invalid); i++) { + data = data_with_drawers_books_invalid[i]; + unsupported_params_init(mc, &data); + + smp_parse_test(ms, &data, false); + } + + object_unref(obj); +} + +static void test_full_topo(const void *opaque) +{ + const char *machine_type = opaque; + Object *obj = object_new(machine_type); + MachineState *ms = MACHINE(obj); + MachineClass *mc = MACHINE_GET_CLASS(obj); + SMPTestData data = {}; + unsigned int drawers = 5, books = 3, dies = 2, clusters = 7, multiplier; + int i; + + multiplier = drawers * books * dies * clusters; + for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { + data = data_generic_valid[i]; + unsupported_params_init(mc, &data); + + /* + * when drawers, books, dies and clusters parameters are omitted, + * they will be set as 1. + */ + data.expect_prefer_sockets.drawers = 1; + data.expect_prefer_sockets.books = 1; + data.expect_prefer_sockets.dies = 1; + data.expect_prefer_sockets.clusters = 1; + data.expect_prefer_cores.drawers = 1; + data.expect_prefer_cores.books = 1; + data.expect_prefer_cores.dies = 1; + data.expect_prefer_cores.clusters = 1; + + smp_parse_test(ms, &data, true); + + /* when drawers, books, dies and clusters parameters are specified. */ + data.config.has_drawers = true; + data.config.drawers = drawers; + data.config.has_books = true; + data.config.books = books; + data.config.has_dies = true; + data.config.dies = dies; + data.config.has_clusters = true; + data.config.clusters = clusters; + + if (data.config.has_cpus) { + data.config.cpus *= multiplier; + } + if (data.config.has_maxcpus) { + data.config.maxcpus *= multiplier; + } + + data.expect_prefer_sockets.drawers = drawers; + data.expect_prefer_sockets.books = books; + data.expect_prefer_sockets.dies = dies; + data.expect_prefer_sockets.clusters = clusters; + data.expect_prefer_sockets.cpus *= multiplier; + data.expect_prefer_sockets.max_cpus *= multiplier; + + data.expect_prefer_cores.drawers = drawers; + data.expect_prefer_cores.books = books; + data.expect_prefer_cores.dies = dies; + data.expect_prefer_cores.clusters = clusters; + data.expect_prefer_cores.cpus *= multiplier; + data.expect_prefer_cores.max_cpus *= multiplier; + + smp_parse_test(ms, &data, true); + } + + for (i = 0; i < ARRAY_SIZE(data_full_topo_invalid); i++) { + data = data_full_topo_invalid[i]; + unsupported_params_init(mc, &data); + + smp_parse_test(ms, &data, false); + } + + for (i = 0; i < ARRAY_SIZE(data_zero_topo_invalid); i++) { + data = data_zero_topo_invalid[i]; + unsupported_params_init(mc, &data); + + smp_parse_test(ms, &data, false); + } + + object_unref(obj); +} + /* Type info of the tested machine */ static const TypeInfo smp_machine_types[] = { { @@ -719,6 +1292,22 @@ static const TypeInfo smp_machine_types[] = { .name = MACHINE_TYPE_NAME("smp-with-clusters"), .parent = TYPE_MACHINE, .class_init = machine_with_clusters_class_init, + }, { + .name = MACHINE_TYPE_NAME("smp-with-books"), + .parent = TYPE_MACHINE, + .class_init = machine_with_books_class_init, + }, { + .name = MACHINE_TYPE_NAME("smp-with-drawers"), + .parent = TYPE_MACHINE, + .class_init = machine_with_drawers_class_init, + }, { + .name = MACHINE_TYPE_NAME("smp-with-drawers-books"), + .parent = TYPE_MACHINE, + .class_init = machine_with_drawers_books_class_init, + }, { + .name = MACHINE_TYPE_NAME("smp-full-topo"), + .parent = TYPE_MACHINE, + .class_init = machine_full_topo_class_init, } }; @@ -742,6 +1331,18 @@ int main(int argc, char *argv[]) g_test_add_data_func("/test-smp-parse/with_clusters", MACHINE_TYPE_NAME("smp-with-clusters"), test_with_clusters); + g_test_add_data_func("/test-smp-parse/with_books", + MACHINE_TYPE_NAME("smp-with-books"), + test_with_books); + g_test_add_data_func("/test-smp-parse/with_drawers", + MACHINE_TYPE_NAME("smp-with-drawers"), + test_with_drawers); + g_test_add_data_func("/test-smp-parse/with_drawers_books", + MACHINE_TYPE_NAME("smp-with-drawers-books"), + test_with_drawers_books); + g_test_add_data_func("/test-smp-parse/full", + MACHINE_TYPE_NAME("smp-full-topo"), + test_full_topo); g_test_run(); diff --git a/tests/unit/test-thread-pool.c b/tests/unit/test-thread-pool.c index 6020e65d69..1483e53473 100644 --- a/tests/unit/test-thread-pool.c +++ b/tests/unit/test-thread-pool.c @@ -8,7 +8,6 @@ #include "qemu/main-loop.h" static AioContext *ctx; -static ThreadPool *pool; static int active; typedef struct { @@ -47,7 +46,7 @@ static void done_cb(void *opaque, int ret) static void test_submit(void) { WorkerTestData data = { .n = 0 }; - thread_pool_submit(pool, worker_cb, &data); + thread_pool_submit(worker_cb, &data); while (data.n == 0) { aio_poll(ctx, true); } @@ -57,7 +56,7 @@ static void test_submit(void) static void test_submit_aio(void) { WorkerTestData data = { .n = 0, .ret = -EINPROGRESS }; - data.aiocb = thread_pool_submit_aio(pool, worker_cb, &data, + data.aiocb = thread_pool_submit_aio(worker_cb, &data, done_cb, &data); /* The callbacks are not called until after the first wait. */ @@ -71,14 +70,14 @@ static void test_submit_aio(void) g_assert_cmpint(data.ret, ==, 0); } -static void co_test_cb(void *opaque) +static void coroutine_fn co_test_cb(void *opaque) { WorkerTestData *data = opaque; active = 1; data->n = 0; data->ret = -EINPROGRESS; - thread_pool_submit_co(pool, worker_cb, data); + thread_pool_submit_co(worker_cb, data); /* The test continues in test_submit_co, after qemu_coroutine_enter... */ @@ -122,7 +121,7 @@ static void test_submit_many(void) for (i = 0; i < 100; i++) { data[i].n = 0; data[i].ret = -EINPROGRESS; - thread_pool_submit_aio(pool, worker_cb, &data[i], done_cb, &data[i]); + thread_pool_submit_aio(worker_cb, &data[i], done_cb, &data[i]); } active = 100; @@ -150,7 +149,7 @@ static void do_test_cancel(bool sync) for (i = 0; i < 100; i++) { data[i].n = 0; data[i].ret = -EINPROGRESS; - data[i].aiocb = thread_pool_submit_aio(pool, long_cb, &data[i], + data[i].aiocb = thread_pool_submit_aio(long_cb, &data[i], done_cb, &data[i]); } @@ -235,7 +234,6 @@ int main(int argc, char **argv) { qemu_init_main_loop(&error_abort); ctx = qemu_get_current_aio_context(); - pool = aio_get_thread_pool(ctx); g_test_init(&argc, &argv, NULL); g_test_add_func("/thread-pool/submit", test_submit); diff --git a/tests/unit/test-throttle.c b/tests/unit/test-throttle.c index 7adb5e6652..24032a0266 100644 --- a/tests/unit/test-throttle.c +++ b/tests/unit/test-throttle.c @@ -127,15 +127,15 @@ static void test_compute_wait(void) bkt.avg = 10; bkt.max = 200; for (i = 0; i < 22; i++) { - double units = bkt.max / 10; + double units = bkt.max / 10.0; bkt.level += units; bkt.burst_level += units; throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 10); wait = throttle_compute_wait(&bkt); g_assert(double_cmp(bkt.burst_level, 0)); - g_assert(double_cmp(bkt.level, (i + 1) * (bkt.max - bkt.avg) / 10)); + g_assert(double_cmp(bkt.level, (i + 1) * (bkt.max - bkt.avg) / 10.0)); /* We can do bursts for the 2 seconds we have configured in - * burst_length. We have 100 extra miliseconds of burst + * burst_length. We have 100 extra milliseconds of burst * because bkt.level has been leaking during this time. * After that, we have to wait. */ result = i < 21 ? 0 : 1.8 * NANOSECONDS_PER_SECOND; @@ -169,8 +169,72 @@ static void test_init(void) /* check initialized fields */ g_assert(tt->clock_type == QEMU_CLOCK_VIRTUAL); - g_assert(tt->timers[0]); - g_assert(tt->timers[1]); + g_assert(tt->timers[THROTTLE_READ]); + g_assert(tt->timers[THROTTLE_WRITE]); + + /* check other fields where cleared */ + g_assert(!ts.previous_leak); + g_assert(!ts.cfg.op_size); + for (i = 0; i < BUCKETS_COUNT; i++) { + g_assert(!ts.cfg.buckets[i].avg); + g_assert(!ts.cfg.buckets[i].max); + g_assert(!ts.cfg.buckets[i].level); + } + + throttle_timers_destroy(tt); +} + +static void test_init_readonly(void) +{ + int i; + + tt = &tgm.throttle_timers; + + /* fill the structures with crap */ + memset(&ts, 1, sizeof(ts)); + memset(tt, 1, sizeof(*tt)); + + /* init structures */ + throttle_init(&ts); + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, NULL, &ts); + + /* check initialized fields */ + g_assert(tt->clock_type == QEMU_CLOCK_VIRTUAL); + g_assert(tt->timers[THROTTLE_READ]); + g_assert(!tt->timers[THROTTLE_WRITE]); + + /* check other fields where cleared */ + g_assert(!ts.previous_leak); + g_assert(!ts.cfg.op_size); + for (i = 0; i < BUCKETS_COUNT; i++) { + g_assert(!ts.cfg.buckets[i].avg); + g_assert(!ts.cfg.buckets[i].max); + g_assert(!ts.cfg.buckets[i].level); + } + + throttle_timers_destroy(tt); +} + +static void test_init_writeonly(void) +{ + int i; + + tt = &tgm.throttle_timers; + + /* fill the structures with crap */ + memset(&ts, 1, sizeof(ts)); + memset(tt, 1, sizeof(*tt)); + + /* init structures */ + throttle_init(&ts); + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, + NULL, write_timer_cb, &ts); + + /* check initialized fields */ + g_assert(tt->clock_type == QEMU_CLOCK_VIRTUAL); + g_assert(!tt->timers[THROTTLE_READ]); + g_assert(tt->timers[THROTTLE_WRITE]); /* check other fields where cleared */ g_assert(!ts.previous_leak); @@ -191,7 +255,7 @@ static void test_destroy(void) throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, read_timer_cb, write_timer_cb, &ts); throttle_timers_destroy(tt); - for (i = 0; i < 2; i++) { + for (i = 0; i < THROTTLE_MAX; i++) { g_assert(!tt->timers[i]); } } @@ -375,11 +439,11 @@ static void test_is_valid_for_value(int value, bool should_be_valid) static void test_is_valid(void) { - /* negative number are invalid */ + /* negative numbesr are invalid */ test_is_valid_for_value(-1, false); - /* zero are valids */ + /* zero is valid */ test_is_valid_for_value(0, true); - /* positives numers are valids */ + /* positives numbers are valid */ test_is_valid_for_value(1, true); } @@ -554,14 +618,13 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ { THROTTLE_OPS_TOTAL, THROTTLE_OPS_READ, THROTTLE_OPS_WRITE, } }; - ThrottleConfig cfg; BucketType index; int i; throttle_config_init(&cfg); for (i = 0; i < 3; i++) { - BucketType index = to_test[is_ops][i]; + index = to_test[is_ops][i]; cfg.buckets[index].avg = avg; } @@ -573,9 +636,9 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ throttle_config(&ts, QEMU_CLOCK_VIRTUAL, &cfg); /* account a read */ - throttle_account(&ts, false, size); + throttle_account(&ts, THROTTLE_READ, size); /* account a write */ - throttle_account(&ts, true, size); + throttle_account(&ts, THROTTLE_WRITE, size); /* check total result */ index = to_test[is_ops][0]; @@ -752,6 +815,8 @@ int main(int argc, char **argv) g_test_add_func("/throttle/leak_bucket", test_leak_bucket); g_test_add_func("/throttle/compute_wait", test_compute_wait); g_test_add_func("/throttle/init", test_init); + g_test_add_func("/throttle/init_readonly", test_init_readonly); + g_test_add_func("/throttle/init_writeonly", test_init_writeonly); g_test_add_func("/throttle/destroy", test_destroy); g_test_add_func("/throttle/have_timer", test_have_timer); g_test_add_func("/throttle/detach_attach", test_detach_attach); diff --git a/tests/unit/test-util-filemonitor.c b/tests/unit/test-util-filemonitor.c index b629e10857..02e67fc96a 100644 --- a/tests/unit/test-util-filemonitor.c +++ b/tests/unit/test-util-filemonitor.c @@ -132,7 +132,7 @@ qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec) * the file monitor event handler. Since events are * emitted in the background thread running the event * loop, we can't assume there is a record available - * immediately. Thus we will sleep for upto 5 seconds + * immediately. Thus we will sleep for up to 5 seconds * to wait for the event to be queued for us. */ static QFileMonitorTestRecord * @@ -360,6 +360,14 @@ test_file_monitor_events(void) { .type = QFILE_MONITOR_TEST_OP_EVENT, .filesrc = "one.txt", .watchid = &watch4, .eventid = QFILE_MONITOR_EVENT_DELETED }, +#ifdef __FreeBSD__ + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch2, + .eventid = QFILE_MONITOR_EVENT_DELETED }, +#endif { .type = QFILE_MONITOR_TEST_OP_EVENT, .filesrc = "two.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_CREATED }, diff --git a/tests/unit/test-util-sockets.c b/tests/unit/test-util-sockets.c index 63909ccb2b..4c9dd0b271 100644 --- a/tests/unit/test-util-sockets.c +++ b/tests/unit/test-util-sockets.c @@ -326,6 +326,7 @@ static void test_socket_unix_abstract(void) test_socket_unix_abstract_row(&matrix[i]); } + unlink(addr.u.q_unix.path); g_free(addr.u.q_unix.path); } diff --git a/tests/unit/test-uuid.c b/tests/unit/test-uuid.c index c111de5fc1..739b91583c 100644 --- a/tests/unit/test-uuid.c +++ b/tests/unit/test-uuid.c @@ -145,7 +145,7 @@ static void test_uuid_unparse(void) int i; for (i = 0; i < ARRAY_SIZE(uuid_test_data); i++) { - char out[37]; + char out[UUID_STR_LEN]; if (!uuid_test_data[i].check_unparse) { continue; @@ -171,6 +171,32 @@ static void test_uuid_unparse_strdup(void) } } +static void test_uuid_hash(void) +{ + QemuUUID uuid; + int i; + + for (i = 0; i < 100; i++) { + qemu_uuid_generate(&uuid); + /* Obtain the UUID hash */ + uint32_t hash_a = qemu_uuid_hash(&uuid); + int data_idx = g_random_int_range(0, 15); + /* Change a single random byte of the UUID */ + if (uuid.data[data_idx] < 0xFF) { + uuid.data[data_idx]++; + } else { + uuid.data[data_idx]--; + } + /* Obtain the UUID hash again */ + uint32_t hash_b = qemu_uuid_hash(&uuid); + /* + * Both hashes shall be different (avoid collision) + * for any change in the UUID fields + */ + g_assert_cmpint(hash_a, !=, hash_b); + } +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -179,6 +205,7 @@ int main(int argc, char **argv) g_test_add_func("/uuid/parse", test_uuid_parse); g_test_add_func("/uuid/unparse", test_uuid_unparse); g_test_add_func("/uuid/unparse_strdup", test_uuid_unparse_strdup); + g_test_add_func("/uuid/hash", test_uuid_hash); return g_test_run(); } diff --git a/tests/unit/test-virtio-dmabuf.c b/tests/unit/test-virtio-dmabuf.c new file mode 100644 index 0000000000..a45ec52f42 --- /dev/null +++ b/tests/unit/test-virtio-dmabuf.c @@ -0,0 +1,137 @@ +/* + * QEMU tests for shared dma-buf API + * + * Copyright (c) 2023 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 "hw/virtio/virtio-dmabuf.h" + + +static void test_add_remove_resources(void) +{ + QemuUUID uuid; + int i, dmabuf_fd; + + for (i = 0; i < 100; ++i) { + qemu_uuid_generate(&uuid); + dmabuf_fd = g_random_int_range(3, 500); + /* Add a new resource */ + g_assert(virtio_add_dmabuf(&uuid, dmabuf_fd)); + g_assert_cmpint(virtio_lookup_dmabuf(&uuid), ==, dmabuf_fd); + /* Remove the resource */ + g_assert(virtio_remove_resource(&uuid)); + /* Resource is not found anymore */ + g_assert_cmpint(virtio_lookup_dmabuf(&uuid), ==, -1); + } +} + +static void test_add_remove_dev(void) +{ + QemuUUID uuid; + struct vhost_dev *dev = g_new0(struct vhost_dev, 1); + int i; + + for (i = 0; i < 100; ++i) { + qemu_uuid_generate(&uuid); + virtio_add_vhost_device(&uuid, dev); + /* vhost device is found */ + g_assert(virtio_lookup_vhost_device(&uuid) != NULL); + /* Remove the vhost device */ + g_assert(virtio_remove_resource(&uuid)); + /* vhost device is not found anymore */ + g_assert(virtio_lookup_vhost_device(&uuid) == NULL); + } + g_free(dev); +} + +static void test_remove_invalid_resource(void) +{ + QemuUUID uuid; + int i; + + for (i = 0; i < 20; ++i) { + qemu_uuid_generate(&uuid); + g_assert_cmpint(virtio_lookup_dmabuf(&uuid), ==, -1); + /* Removing a resource that does not exist returns false */ + g_assert_false(virtio_remove_resource(&uuid)); + } +} + +static void test_add_invalid_resource(void) +{ + QemuUUID uuid; + struct vhost_dev *dev = NULL; + int i, dmabuf_fd = -2, alt_dmabuf = 2; + + for (i = 0; i < 20; ++i) { + qemu_uuid_generate(&uuid); + /* Add a new resource with invalid (negative) resource fd */ + g_assert_false(virtio_add_dmabuf(&uuid, dmabuf_fd)); + /* Resource is not found */ + g_assert_cmpint(virtio_lookup_dmabuf(&uuid), ==, -1); + /* Add a new vhost device with invalid (NULL) pointer */ + g_assert_false(virtio_add_vhost_device(&uuid, dev)); + /* vhost device is not found */ + g_assert(virtio_lookup_vhost_device(&uuid) == NULL); + } + + for (i = 0; i < 20; ++i) { + /* Add a valid resource */ + qemu_uuid_generate(&uuid); + dmabuf_fd = g_random_int_range(3, 500); + g_assert(virtio_add_dmabuf(&uuid, dmabuf_fd)); + g_assert_cmpint(virtio_lookup_dmabuf(&uuid), ==, dmabuf_fd); + /* Add a new resource with repeated uuid returns false */ + g_assert_false(virtio_add_dmabuf(&uuid, alt_dmabuf)); + /* The value for the uuid key is not replaced */ + g_assert_cmpint(virtio_lookup_dmabuf(&uuid), ==, dmabuf_fd); + } +} + +static void test_free_resources(void) +{ + QemuUUID uuids[20]; + int i, dmabuf_fd; + + for (i = 0; i < ARRAY_SIZE(uuids); ++i) { + qemu_uuid_generate(&uuids[i]); + dmabuf_fd = g_random_int_range(3, 500); + g_assert(virtio_add_dmabuf(&uuids[i], dmabuf_fd)); + g_assert_cmpint(virtio_lookup_dmabuf(&uuids[i]), ==, dmabuf_fd); + } + virtio_free_resources(); + for (i = 0; i < ARRAY_SIZE(uuids); ++i) { + /* None of the resources is found after free'd */ + g_assert_cmpint(virtio_lookup_dmabuf(&uuids[i]), ==, -1); + } + +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/virtio-dmabuf/add_rm_res", test_add_remove_resources); + g_test_add_func("/virtio-dmabuf/add_rm_dev", test_add_remove_dev); + g_test_add_func("/virtio-dmabuf/rm_invalid_res", + test_remove_invalid_resource); + g_test_add_func("/virtio-dmabuf/add_invalid_res", + test_add_invalid_resource); + g_test_add_func("/virtio-dmabuf/free_res", test_free_resources); + + return g_test_run(); +} diff --git a/tests/unit/test-visitor-serialization.c b/tests/unit/test-visitor-serialization.c index 907263d030..c2056c3eaa 100644 --- a/tests/unit/test-visitor-serialization.c +++ b/tests/unit/test-visitor-serialization.c @@ -223,7 +223,6 @@ static UserDefTwo *nested_struct_create(void) udnp->dict1->dict2->userdef->string = strdup("test_string"); udnp->dict1->dict2->string = strdup("test_string2"); udnp->dict1->dict3 = g_malloc0(sizeof(*udnp->dict1->dict3)); - udnp->dict1->has_dict3 = true; udnp->dict1->dict3->userdef = g_new0(UserDefOne, 1); udnp->dict1->dict3->userdef->integer = 43; udnp->dict1->dict3->userdef->string = strdup("test_string"); @@ -243,7 +242,7 @@ static void nested_struct_compare(UserDefTwo *udnp1, UserDefTwo *udnp2) udnp2->dict1->dict2->userdef->string); g_assert_cmpstr(udnp1->dict1->dict2->string, ==, udnp2->dict1->dict2->string); - g_assert(udnp1->dict1->has_dict3 == udnp2->dict1->has_dict3); + g_assert(!udnp1->dict1->dict3 == !udnp2->dict1->dict3); g_assert_cmpint(udnp1->dict1->dict3->userdef->integer, ==, udnp2->dict1->dict3->userdef->integer); g_assert_cmpstr(udnp1->dict1->dict3->userdef->string, ==, @@ -427,131 +426,117 @@ static void test_primitive_lists(gconstpointer opaque) ops->deserialize((void **)&pl_copy_ptr, serialize_data, visit_primitive_list, &error_abort); - i = 0; + + switch (pl_copy.type) { + case PTYPE_STRING: + cur_head = pl_copy.value.strings; + break; + case PTYPE_INTEGER: + cur_head = pl_copy.value.integers; + break; + case PTYPE_S8: + cur_head = pl_copy.value.s8_integers; + break; + case PTYPE_S16: + cur_head = pl_copy.value.s16_integers; + break; + case PTYPE_S32: + cur_head = pl_copy.value.s32_integers; + break; + case PTYPE_S64: + cur_head = pl_copy.value.s64_integers; + break; + case PTYPE_U8: + cur_head = pl_copy.value.u8_integers; + break; + case PTYPE_U16: + cur_head = pl_copy.value.u16_integers; + break; + case PTYPE_U32: + cur_head = pl_copy.value.u32_integers; + break; + case PTYPE_U64: + cur_head = pl_copy.value.u64_integers; + break; + case PTYPE_NUMBER: + cur_head = pl_copy.value.numbers; + break; + case PTYPE_BOOLEAN: + cur_head = pl_copy.value.booleans; + break; + default: + g_assert_not_reached(); + } /* compare our deserialized list of primitives to the original */ - do { + i = 0; + while (cur_head) { switch (pl_copy.type) { case PTYPE_STRING: { - strList *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.strings; - } + strList *ptr = cur_head; + cur_head = ptr->next; g_assert_cmpstr(pt->value.string, ==, ptr->value); break; } case PTYPE_INTEGER: { - intList *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.integers; - } + intList *ptr = cur_head; + cur_head = ptr->next; g_assert_cmpint(pt->value.integer, ==, ptr->value); break; } case PTYPE_S8: { - int8List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.s8_integers; - } + int8List *ptr = cur_head; + cur_head = ptr->next; g_assert_cmpint(pt->value.s8, ==, ptr->value); break; } case PTYPE_S16: { - int16List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.s16_integers; - } + int16List *ptr = cur_head; + cur_head = ptr->next; g_assert_cmpint(pt->value.s16, ==, ptr->value); break; } case PTYPE_S32: { - int32List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.s32_integers; - } + int32List *ptr = cur_head; + cur_head = ptr->next; g_assert_cmpint(pt->value.s32, ==, ptr->value); break; } case PTYPE_S64: { - int64List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.s64_integers; - } + int64List *ptr = cur_head; + cur_head = ptr->next; g_assert_cmpint(pt->value.s64, ==, ptr->value); break; } case PTYPE_U8: { - uint8List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.u8_integers; - } + uint8List *ptr = cur_head; + cur_head = ptr->next; g_assert_cmpint(pt->value.u8, ==, ptr->value); break; } case PTYPE_U16: { - uint16List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.u16_integers; - } + uint16List *ptr = cur_head; + cur_head = ptr->next; g_assert_cmpint(pt->value.u16, ==, ptr->value); break; } case PTYPE_U32: { - uint32List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.u32_integers; - } + uint32List *ptr = cur_head; + cur_head = ptr->next; g_assert_cmpint(pt->value.u32, ==, ptr->value); break; } case PTYPE_U64: { - uint64List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.u64_integers; - } + uint64List *ptr = cur_head; + cur_head = ptr->next; g_assert_cmpint(pt->value.u64, ==, ptr->value); break; } case PTYPE_NUMBER: { - numberList *ptr; GString *double_expected = g_string_new(""); GString *double_actual = g_string_new(""); - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.numbers; - } + numberList *ptr = cur_head; + cur_head = ptr->next; /* we serialize with %f for our reference visitors, so rather than * fuzzy floating math to test "equality", just compare the * formatted values @@ -564,13 +549,8 @@ static void test_primitive_lists(gconstpointer opaque) break; } case PTYPE_BOOLEAN: { - boolList *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.booleans; - } + boolList *ptr = cur_head; + cur_head = ptr->next; g_assert_cmpint(!!pt->value.boolean, ==, !!ptr->value); break; } @@ -578,9 +558,9 @@ static void test_primitive_lists(gconstpointer opaque) g_assert_not_reached(); } i++; - } while (cur_head); + } - g_assert_cmpint(i, ==, 33); + g_assert_cmpint(i, ==, 32); ops->cleanup(serialize_data); dealloc_helper(&pl, visit_primitive_list, &error_abort); diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c index 6a417bb102..63f28f26f4 100644 --- a/tests/unit/test-vmstate.c +++ b/tests/unit/test-vmstate.c @@ -24,13 +24,10 @@ #include "qemu/osdep.h" -#include "../migration/migration.h" #include "migration/vmstate.h" #include "migration/qemu-file-types.h" #include "../migration/qemu-file.h" -#include "../migration/qemu-file-channel.h" #include "../migration/savevm.h" -#include "qemu/coroutine.h" #include "qemu/module.h" #include "io/channel-file.h" @@ -52,9 +49,9 @@ static QEMUFile *open_test_file(bool write) } ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd)); if (write) { - f = qemu_fopen_channel_output(ioc); + f = qemu_file_new_output(ioc); } else { - f = qemu_fopen_channel_input(ioc); + f = qemu_file_new_input(ioc); } object_unref(OBJECT(ioc)); return f; @@ -88,17 +85,16 @@ static void save_buffer(const uint8_t *buf, size_t buf_size) static void compare_vmstate(const uint8_t *wire, size_t size) { QEMUFile *f = open_test_file(false); - uint8_t result[size]; + g_autofree uint8_t *result = g_malloc(size); /* read back as binary */ - g_assert_cmpint(qemu_get_buffer(f, result, sizeof(result)), ==, - sizeof(result)); + g_assert_cmpint(qemu_get_buffer(f, result, size), ==, size); g_assert(!qemu_file_get_error(f)); /* Compare that what is on the file is the same that what we expected to be there */ - SUCCESS(memcmp(result, wire, sizeof(result))); + SUCCESS(memcmp(result, wire, size)); /* Must reach EOF */ qemu_get_byte(f); @@ -200,7 +196,7 @@ static const VMStateDescription vmstate_simple_primitive = { .name = "simple/primitive", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(b_1, TestSimple), VMSTATE_BOOL(b_2, TestSimple), VMSTATE_UINT8(u8_1, TestSimple), @@ -302,7 +298,7 @@ static const VMStateDescription vmstate_simple_arr = { .name = "simple/array", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16_ARRAY(u16_1, TestSimpleArray, 3), VMSTATE_END_OF_LIST() } @@ -344,7 +340,7 @@ static const VMStateDescription vmstate_versioned = { .name = "test/versioned", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(a, TestStruct), VMSTATE_UINT32_V(b, TestStruct, 2), /* Versioned field in the middle, so * we catch bugs more easily. @@ -415,7 +411,7 @@ static const VMStateDescription vmstate_skipping = { .name = "test/skip", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(a, TestStruct), VMSTATE_UINT32(b, TestStruct), VMSTATE_UINT32_TEST(c, TestStruct, test_skip), @@ -527,7 +523,7 @@ const VMStateDescription vmsd_tst = { .name = "test/tst", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(i, TestStructTriv), VMSTATE_END_OF_LIST() } @@ -545,7 +541,7 @@ const VMStateDescription vmsd_arps = { .name = "test/arps", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(ar, TestArrayOfPtrToStuct, AR_SIZE, 0, vmsd_tst, TestStructTriv), VMSTATE_END_OF_LIST() @@ -633,7 +629,7 @@ const VMStateDescription vmsd_arpp = { .name = "test/arps", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_ARRAY_OF_POINTER(ar, TestArrayOfPtrToInt, AR_SIZE, 0, vmstate_info_int32, int32_t*), VMSTATE_END_OF_LIST() @@ -688,7 +684,7 @@ static const VMStateDescription vmstate_q_element = { .name = "test/queue-element", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(b, TestQtailqElement), VMSTATE_UINT8(u8, TestQtailqElement), VMSTATE_END_OF_LIST() @@ -699,7 +695,7 @@ static const VMStateDescription vmstate_q = { .name = "test/queue", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT16(i16, TestQtailq), VMSTATE_QTAILQ_V(q, TestQtailq, 1, vmstate_q_element, TestQtailqElement, next), @@ -824,7 +820,7 @@ typedef struct TestGTreeInterval { .name = "interval", \ .version_id = 1, \ .minimum_version_id = 1, \ - .fields = (VMStateField[]) { \ + .fields = (const VMStateField[]) { \ VMSTATE_UINT64(low, TestGTreeInterval), \ VMSTATE_UINT64(high, TestGTreeInterval), \ VMSTATE_END_OF_LIST() \ @@ -842,7 +838,7 @@ typedef struct TestGTreeMapping { .name = "mapping", \ .version_id = 1, \ .minimum_version_id = 1, \ - .fields = (VMStateField[]) { \ + .fields = (const VMStateField[]) { \ VMSTATE_UINT64(phys_addr, TestGTreeMapping), \ VMSTATE_UINT32(flags, TestGTreeMapping), \ VMSTATE_END_OF_LIST() \ @@ -918,7 +914,7 @@ static const VMStateDescription vmstate_domain = { .version_id = 1, .minimum_version_id = 1, .pre_load = domain_preload, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(id, TestGTreeDomain), VMSTATE_GTREE_V(mappings, TestGTreeDomain, 1, vmstate_interval_mapping, @@ -943,7 +939,7 @@ static const VMStateDescription vmstate_qlist_element = { .name = "test/queue list", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(id, TestQListElement), VMSTATE_END_OF_LIST() } @@ -954,7 +950,7 @@ static const VMStateDescription vmstate_iommu = { .version_id = 1, .minimum_version_id = 1, .pre_load = iommu_preload, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(id, TestGTreeIOMMU), VMSTATE_GTREE_DIRECT_KEY_V(domains, TestGTreeIOMMU, 1, &vmstate_domain, TestGTreeDomain), @@ -966,7 +962,7 @@ static const VMStateDescription vmstate_container = { .name = "test/container/qlist", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(id, TestQListContainer), VMSTATE_QLIST_V(list, TestQListContainer, 1, vmstate_qlist_element, TestQListElement, next), @@ -1076,7 +1072,6 @@ static gboolean diff_tree(gpointer key, gpointer value, gpointer data) struct match_node_data d = {tp->tree2, key, value}; g_tree_foreach(tp->tree2, tp->match_node, &d); - g_tree_remove(tp->tree1, key); return false; } @@ -1085,9 +1080,9 @@ static void compare_trees(GTree *tree1, GTree *tree2, { struct tree_cmp_data tp = {tree1, tree2, function}; + assert(g_tree_nnodes(tree1) == g_tree_nnodes(tree2)); g_tree_foreach(tree1, diff_tree, &tp); - assert(g_tree_nnodes(tree1) == 0); - assert(g_tree_nnodes(tree2) == 0); + g_tree_destroy(g_tree_ref(tree1)); } static void diff_domain(TestGTreeDomain *d1, TestGTreeDomain *d2) @@ -1418,7 +1413,7 @@ static int tmp_child_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_tmp_back_to_parent = { .name = "test/tmp_child_parent", - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(f, TestStruct), VMSTATE_END_OF_LIST() } @@ -1428,7 +1423,7 @@ static const VMStateDescription vmstate_tmp_child = { .name = "test/tmp_child", .pre_save = tmp_child_pre_save, .post_load = tmp_child_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64(diff, TmpTestStruct), VMSTATE_STRUCT_POINTER(parent, TmpTestStruct, vmstate_tmp_back_to_parent, TestStruct), @@ -1439,7 +1434,7 @@ static const VMStateDescription vmstate_tmp_child = { static const VMStateDescription vmstate_with_tmp = { .name = "test/with_tmp", .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(a, TestStruct), VMSTATE_UINT64(d, TestStruct), VMSTATE_WITH_TMP(TestStruct, TmpTestStruct, vmstate_tmp_child), diff --git a/tests/unit/test-x86-cpuid.c b/tests/unit/test-x86-topo.c similarity index 99% rename from tests/unit/test-x86-cpuid.c rename to tests/unit/test-x86-topo.c index bfabc0403a..2b104f86d7 100644 --- a/tests/unit/test-x86-cpuid.c +++ b/tests/unit/test-x86-topo.c @@ -1,5 +1,5 @@ /* - * Test code for x86 CPUID and Topology functions + * Test code for x86 APIC ID and Topology functions * * Copyright (c) 2012 Red Hat Inc. * diff --git a/tests/unit/test-xbzrle.c b/tests/unit/test-xbzrle.c index ef951b6e54..b6996de69a 100644 --- a/tests/unit/test-xbzrle.c +++ b/tests/unit/test-xbzrle.c @@ -54,8 +54,8 @@ static void test_encode_decode_zero(void) buffer[1000 + diff_len + 5] = 105; /* encode zero page */ - dlen = xbzrle_encode_buffer(buffer, buffer, XBZRLE_PAGE_SIZE, compressed, - XBZRLE_PAGE_SIZE); + dlen = xbzrle_encode_buffer(buffer, buffer, XBZRLE_PAGE_SIZE, + compressed, XBZRLE_PAGE_SIZE); g_assert(dlen == 0); g_free(buffer); @@ -78,8 +78,8 @@ static void test_encode_decode_unchanged(void) test[1000 + diff_len + 5] = 109; /* test unchanged buffer */ - dlen = xbzrle_encode_buffer(test, test, XBZRLE_PAGE_SIZE, compressed, - XBZRLE_PAGE_SIZE); + dlen = xbzrle_encode_buffer(test, test, XBZRLE_PAGE_SIZE, + compressed, XBZRLE_PAGE_SIZE); g_assert(dlen == 0); g_free(test); @@ -96,8 +96,8 @@ static void test_encode_decode_1_byte(void) test[XBZRLE_PAGE_SIZE - 1] = 1; - dlen = xbzrle_encode_buffer(buffer, test, XBZRLE_PAGE_SIZE, compressed, - XBZRLE_PAGE_SIZE); + dlen = xbzrle_encode_buffer(buffer, test, XBZRLE_PAGE_SIZE, + compressed, XBZRLE_PAGE_SIZE); g_assert(dlen == (uleb128_encode_small(&buf[0], 4095) + 2)); rc = xbzrle_decode_buffer(compressed, dlen, buffer, XBZRLE_PAGE_SIZE); @@ -121,8 +121,8 @@ static void test_encode_decode_overflow(void) } /* encode overflow */ - rc = xbzrle_encode_buffer(buffer, test, XBZRLE_PAGE_SIZE, compressed, - XBZRLE_PAGE_SIZE); + rc = xbzrle_encode_buffer(buffer, test, XBZRLE_PAGE_SIZE, + compressed, XBZRLE_PAGE_SIZE); g_assert(rc == -1); g_free(buffer); @@ -152,8 +152,8 @@ static void encode_decode_range(void) test[1000 + diff_len + 5] = 109; /* test encode/decode */ - dlen = xbzrle_encode_buffer(test, buffer, XBZRLE_PAGE_SIZE, compressed, - XBZRLE_PAGE_SIZE); + dlen = xbzrle_encode_buffer(test, buffer, XBZRLE_PAGE_SIZE, + compressed, XBZRLE_PAGE_SIZE); rc = xbzrle_decode_buffer(compressed, dlen, test, XBZRLE_PAGE_SIZE); g_assert(rc < XBZRLE_PAGE_SIZE); diff --git a/tests/unit/test-xs-node.c b/tests/unit/test-xs-node.c new file mode 100644 index 0000000000..ac94e7ed6c --- /dev/null +++ b/tests/unit/test-xs-node.c @@ -0,0 +1,871 @@ +/* + * QEMU XenStore XsNode testing + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + * 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/error.h" +#include "qemu/module.h" + +static int nr_xs_nodes; +static GList *xs_node_list; + +#define XS_NODE_UNIT_TEST + +/* + * We don't need the core Xen definitions. And we *do* want to be able + * to run the unit tests even on architectures that Xen doesn't support + * (because life's too short to bother doing otherwise, and test coverage + * doesn't hurt). + */ +#define __XEN_PUBLIC_XEN_H__ +typedef unsigned long xen_pfn_t; + +#include "hw/i386/kvm/xenstore_impl.c" + +#define DOMID_QEMU 0 +#define DOMID_GUEST 1 + +static void dump_ref(const char *name, XsNode *n, int indent) +{ + int i; + + if (!indent && name) { + printf("%s:\n", name); + } + + for (i = 0; i < indent; i++) { + printf(" "); + } + + printf("->%p(%d, '%s'): '%.*s'%s%s\n", n, n->ref, n->name, + (int)(n->content ? n->content->len : strlen("")), + n->content ? (char *)n->content->data : "", + n->modified_in_tx ? " MODIFIED" : "", + n->deleted_in_tx ? " DELETED" : ""); + + if (n->children) { + g_hash_table_foreach(n->children, (void *)dump_ref, + GINT_TO_POINTER(indent + 2)); + } +} + +/* This doesn't happen in qemu but we want to make valgrind happy */ +static void xs_impl_delete(XenstoreImplState *s, bool last) +{ + int err; + + xs_impl_reset_watches(s, DOMID_GUEST); + g_assert(!s->nr_domu_watches); + + err = xs_impl_rm(s, DOMID_QEMU, XBT_NULL, "/local"); + g_assert(!err); + g_assert(s->nr_nodes == 1); + + g_hash_table_unref(s->watches); + g_hash_table_unref(s->transactions); + xs_node_unref(s->root); + g_free(s); + + if (!last) { + return; + } + + if (xs_node_list) { + GList *l; + for (l = xs_node_list; l; l = l->next) { + XsNode *n = l->data; + printf("Remaining node at %p name %s ref %u\n", n, n->name, + n->ref); + } + } + g_assert(!nr_xs_nodes); +} + +struct compare_walk { + char path[XENSTORE_ABS_PATH_MAX + 1]; + XsNode *parent_2; + bool compare_ok; +}; + + +static bool compare_perms(GList *p1, GList *p2) +{ + while (p1) { + if (!p2 || g_strcmp0(p1->data, p2->data)) { + return false; + } + p1 = p1->next; + p2 = p2->next; + } + return (p2 == NULL); +} + +static bool compare_content(GByteArray *c1, GByteArray *c2) +{ + size_t len1 = 0, len2 = 0; + + if (c1) { + len1 = c1->len; + } + if (c2) { + len2 = c2->len; + } + if (len1 != len2) { + return false; + } + + if (!len1) { + return true; + } + + return !memcmp(c1->data, c2->data, len1); +} + +static void compare_child(gpointer, gpointer, gpointer); + +static void compare_nodes(struct compare_walk *cw, XsNode *n1, XsNode *n2) +{ + int nr_children1 = 0, nr_children2 = 0; + + if (n1->children) { + nr_children1 = g_hash_table_size(n1->children); + } + if (n2->children) { + nr_children2 = g_hash_table_size(n2->children); + } + + if (n1->ref != n2->ref || + n1->deleted_in_tx != n2->deleted_in_tx || + n1->modified_in_tx != n2->modified_in_tx || + !compare_perms(n1->perms, n2->perms) || + !compare_content(n1->content, n2->content) || + nr_children1 != nr_children2) { + cw->compare_ok = false; + printf("Compare failure on '%s'\n", cw->path); + } + + if (nr_children1) { + XsNode *oldparent = cw->parent_2; + cw->parent_2 = n2; + g_hash_table_foreach(n1->children, compare_child, cw); + + cw->parent_2 = oldparent; + } +} + +static void compare_child(gpointer key, gpointer val, gpointer opaque) +{ + struct compare_walk *cw = opaque; + char *childname = key; + XsNode *child1 = val; + XsNode *child2 = g_hash_table_lookup(cw->parent_2->children, childname); + int pathlen = strlen(cw->path); + + if (!child2) { + cw->compare_ok = false; + printf("Child '%s' does not exist under '%s'\n", childname, cw->path); + return; + } + + strncat(cw->path, "/", sizeof(cw->path) - 1); + strncat(cw->path, childname, sizeof(cw->path) - 1); + + compare_nodes(cw, child1, child2); + cw->path[pathlen] = '\0'; +} + +static bool compare_trees(XsNode *n1, XsNode *n2) +{ + struct compare_walk cw; + + cw.path[0] = '\0'; + cw.parent_2 = n2; + cw.compare_ok = true; + + if (!n1 || !n2) { + return false; + } + + compare_nodes(&cw, n1, n2); + return cw.compare_ok; +} + +static void compare_tx(gpointer key, gpointer val, gpointer opaque) +{ + XenstoreImplState *s2 = opaque; + XsTransaction *t1 = val, *t2; + unsigned int tx_id = GPOINTER_TO_INT(key); + + t2 = g_hash_table_lookup(s2->transactions, key); + g_assert(t2); + + g_assert(t1->tx_id == tx_id); + g_assert(t2->tx_id == tx_id); + g_assert(t1->base_tx == t2->base_tx); + g_assert(t1->dom_id == t2->dom_id); + if (!compare_trees(t1->root, t2->root)) { + printf("Comparison failure in TX %u after serdes:\n", tx_id); + dump_ref("Original", t1->root, 0); + dump_ref("Deserialised", t2->root, 0); + g_assert(0); + } + g_assert(t1->nr_nodes == t2->nr_nodes); +} + +static int write_str(XenstoreImplState *s, unsigned int dom_id, + unsigned int tx_id, const char *path, + const char *content) +{ + GByteArray *d = g_byte_array_new(); + int err; + + g_byte_array_append(d, (void *)content, strlen(content)); + err = xs_impl_write(s, dom_id, tx_id, path, d); + g_byte_array_unref(d); + return err; +} + +static void watch_cb(void *_str, const char *path, const char *token) +{ + GString *str = _str; + + g_string_append(str, path); + g_string_append(str, token); +} + +static void check_serdes(XenstoreImplState *s) +{ + XenstoreImplState *s2 = xs_impl_create(DOMID_GUEST); + GByteArray *bytes = xs_impl_serialize(s); + int nr_transactions1, nr_transactions2; + int ret; + + ret = xs_impl_deserialize(s2, bytes, DOMID_GUEST, watch_cb, NULL); + g_assert(!ret); + + g_byte_array_unref(bytes); + + g_assert(s->last_tx == s2->last_tx); + g_assert(s->root_tx == s2->root_tx); + + if (!compare_trees(s->root, s2->root)) { + printf("Comparison failure in main tree after serdes:\n"); + dump_ref("Original", s->root, 0); + dump_ref("Deserialised", s2->root, 0); + g_assert(0); + } + + nr_transactions1 = g_hash_table_size(s->transactions); + nr_transactions2 = g_hash_table_size(s2->transactions); + g_assert(nr_transactions1 == nr_transactions2); + + g_hash_table_foreach(s->transactions, compare_tx, s2); + + g_assert(s->nr_domu_watches == s2->nr_domu_watches); + g_assert(s->nr_domu_transactions == s2->nr_domu_transactions); + g_assert(s->nr_nodes == s2->nr_nodes); + xs_impl_delete(s2, false); +} + +static XenstoreImplState *setup(void) +{ + XenstoreImplState *s = xs_impl_create(DOMID_GUEST); + char *abspath; + GList *perms; + int err; + + abspath = g_strdup_printf("/local/domain/%u", DOMID_GUEST); + + err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, ""); + g_assert(!err); + g_assert(s->nr_nodes == 4); + + perms = g_list_append(NULL, g_strdup_printf("n%u", DOMID_QEMU)); + perms = g_list_append(perms, g_strdup_printf("r%u", DOMID_GUEST)); + + err = xs_impl_set_perms(s, DOMID_QEMU, XBT_NULL, abspath, perms); + g_assert(!err); + + g_list_free_full(perms, g_free); + g_free(abspath); + + abspath = g_strdup_printf("/local/domain/%u/some", DOMID_GUEST); + + err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, ""); + g_assert(!err); + g_assert(s->nr_nodes == 5); + + perms = g_list_append(NULL, g_strdup_printf("n%u", DOMID_GUEST)); + + err = xs_impl_set_perms(s, DOMID_QEMU, XBT_NULL, abspath, perms); + g_assert(!err); + + g_list_free_full(perms, g_free); + g_free(abspath); + + return s; +} + +static void test_xs_node_simple(void) +{ + GByteArray *data = g_byte_array_new(); + XenstoreImplState *s = setup(); + GString *guest_watches = g_string_new(NULL); + GString *qemu_watches = g_string_new(NULL); + GList *items = NULL; + XsNode *old_root; + uint64_t gencnt; + int err; + + g_assert(s); + + err = xs_impl_watch(s, DOMID_GUEST, "some", "guestwatch", + watch_cb, guest_watches); + g_assert(!err); + g_assert(guest_watches->len == strlen("someguestwatch")); + g_assert(!strcmp(guest_watches->str, "someguestwatch")); + g_string_truncate(guest_watches, 0); + + err = xs_impl_watch(s, 0, "/local/domain/1/some", "qemuwatch", + watch_cb, qemu_watches); + g_assert(!err); + g_assert(qemu_watches->len == strlen("/local/domain/1/someqemuwatch")); + g_assert(!strcmp(qemu_watches->str, "/local/domain/1/someqemuwatch")); + g_string_truncate(qemu_watches, 0); + + /* Read gives ENOENT when it should */ + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "foo", data); + g_assert(err == ENOENT); + + /* Write works */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", + "something"); + g_assert(s->nr_nodes == 7); + g_assert(!err); + g_assert(!strcmp(guest_watches->str, + "some/relative/pathguestwatch")); + g_assert(!strcmp(qemu_watches->str, + "/local/domain/1/some/relative/pathqemuwatch")); + + g_string_truncate(qemu_watches, 0); + g_string_truncate(guest_watches, 0); + xs_impl_reset_watches(s, 0); + + /* Read gives back what we wrote */ + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data); + g_assert(!err); + g_assert(data->len == strlen("something")); + g_assert(!memcmp(data->data, "something", data->len)); + + /* Even if we use an absolute path */ + g_byte_array_set_size(data, 0); + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, + "/local/domain/1/some/relative/path", data); + g_assert(!err); + g_assert(data->len == strlen("something")); + + g_assert(!qemu_watches->len); + g_assert(!guest_watches->len); + /* Keep a copy, to force COW mode */ + old_root = xs_node_ref(s->root); + + /* Write somewhere we aren't allowed, in COW mode */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "/local/domain/badplace", + "moredata"); + g_assert(err == EACCES); + g_assert(s->nr_nodes == 7); + + /* Write works again */ + err = write_str(s, DOMID_GUEST, XBT_NULL, + "/local/domain/1/some/relative/path2", + "something else"); + g_assert(!err); + g_assert(s->nr_nodes == 8); + g_assert(!qemu_watches->len); + g_assert(!strcmp(guest_watches->str, "some/relative/path2guestwatch")); + g_string_truncate(guest_watches, 0); + + /* Overwrite an existing node */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", + "another thing"); + g_assert(!err); + g_assert(s->nr_nodes == 8); + g_assert(!qemu_watches->len); + g_assert(!strcmp(guest_watches->str, "some/relative/pathguestwatch")); + g_string_truncate(guest_watches, 0); + + /* We can list the two files we wrote */ + err = xs_impl_directory(s, DOMID_GUEST, XBT_NULL, "some/relative", &gencnt, + &items); + g_assert(!err); + g_assert(items); + g_assert(gencnt == 2); + g_assert(!strcmp(items->data, "path")); + g_assert(items->next); + g_assert(!strcmp(items->next->data, "path2")); + g_assert(!items->next->next); + g_list_free_full(items, g_free); + + err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch", + watch_cb, guest_watches); + g_assert(!err); + + err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch", + watch_cb, guest_watches); + g_assert(err == ENOENT); + + err = xs_impl_watch(s, DOMID_GUEST, "some/relative/path2", "watchp2", + watch_cb, guest_watches); + g_assert(!err); + g_assert(guest_watches->len == strlen("some/relative/path2watchp2")); + g_assert(!strcmp(guest_watches->str, "some/relative/path2watchp2")); + g_string_truncate(guest_watches, 0); + + err = xs_impl_watch(s, DOMID_GUEST, "/local/domain/1/some/relative", + "watchrel", watch_cb, guest_watches); + g_assert(!err); + g_assert(guest_watches->len == + strlen("/local/domain/1/some/relativewatchrel")); + g_assert(!strcmp(guest_watches->str, + "/local/domain/1/some/relativewatchrel")); + g_string_truncate(guest_watches, 0); + + /* Write somewhere else which already existed */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "moredata"); + g_assert(!err); + g_assert(s->nr_nodes == 8); + + /* Write somewhere we aren't allowed */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "/local/domain/badplace", + "moredata"); + g_assert(err == EACCES); + + g_assert(!strcmp(guest_watches->str, + "/local/domain/1/some/relativewatchrel")); + g_string_truncate(guest_watches, 0); + + g_byte_array_set_size(data, 0); + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); + g_assert(!err); + g_assert(data->len == strlen("moredata")); + g_assert(!memcmp(data->data, "moredata", data->len)); + + /* Overwrite existing data */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "otherdata"); + g_assert(!err); + g_string_truncate(guest_watches, 0); + + g_byte_array_set_size(data, 0); + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); + g_assert(!err); + g_assert(data->len == strlen("otherdata")); + g_assert(!memcmp(data->data, "otherdata", data->len)); + + /* Remove the subtree */ + err = xs_impl_rm(s, DOMID_GUEST, XBT_NULL, "some/relative"); + g_assert(!err); + g_assert(s->nr_nodes == 5); + + /* Each watch fires with the least specific relevant path */ + g_assert(strstr(guest_watches->str, + "some/relative/path2watchp2")); + g_assert(strstr(guest_watches->str, + "/local/domain/1/some/relativewatchrel")); + g_string_truncate(guest_watches, 0); + + g_byte_array_set_size(data, 0); + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data); + g_assert(err == ENOENT); + g_byte_array_unref(data); + + xs_impl_reset_watches(s, DOMID_GUEST); + g_string_free(qemu_watches, true); + g_string_free(guest_watches, true); + xs_node_unref(old_root); + xs_impl_delete(s, true); +} + + +static void do_test_xs_node_tx(bool fail, bool commit) +{ + XenstoreImplState *s = setup(); + GString *watches = g_string_new(NULL); + GByteArray *data = g_byte_array_new(); + unsigned int tx_id = XBT_NULL; + int err; + + g_assert(s); + + /* Set a watch */ + err = xs_impl_watch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + g_assert(watches->len == strlen("somewatch")); + g_assert(!strcmp(watches->str, "somewatch")); + g_string_truncate(watches, 0); + + /* Write something */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", + "something"); + g_assert(s->nr_nodes == 7); + g_assert(!err); + g_assert(!strcmp(watches->str, + "some/relative/pathwatch")); + g_string_truncate(watches, 0); + + /* Create a transaction */ + err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id); + g_assert(!err); + + if (fail) { + /* Write something else in the root */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path", + "another thing"); + g_assert(!err); + g_assert(s->nr_nodes == 7); + g_assert(!strcmp(watches->str, + "some/relative/pathwatch")); + g_string_truncate(watches, 0); + } + + g_assert(!watches->len); + + /* Perform a write in the transaction */ + err = write_str(s, DOMID_GUEST, tx_id, "some/relative/path", + "something else"); + g_assert(!err); + g_assert(s->nr_nodes == 7); + g_assert(!watches->len); + + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data); + g_assert(!err); + if (fail) { + g_assert(data->len == strlen("another thing")); + g_assert(!memcmp(data->data, "another thing", data->len)); + } else { + g_assert(data->len == strlen("something")); + g_assert(!memcmp(data->data, "something", data->len)); + } + g_byte_array_set_size(data, 0); + + err = xs_impl_read(s, DOMID_GUEST, tx_id, "some/relative/path", data); + g_assert(!err); + g_assert(data->len == strlen("something else")); + g_assert(!memcmp(data->data, "something else", data->len)); + g_byte_array_set_size(data, 0); + + check_serdes(s); + + /* Attempt to commit the transaction */ + err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, commit); + if (commit && fail) { + g_assert(err == EAGAIN); + } else { + g_assert(!err); + } + if (commit && !fail) { + g_assert(!strcmp(watches->str, + "some/relative/pathwatch")); + g_string_truncate(watches, 0); + } else { + g_assert(!watches->len); + } + g_assert(s->nr_nodes == 7); + + check_serdes(s); + + err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data); + g_assert(!err); + if (fail) { + g_assert(data->len == strlen("another thing")); + g_assert(!memcmp(data->data, "another thing", data->len)); + } else if (commit) { + g_assert(data->len == strlen("something else")); + g_assert(!memcmp(data->data, "something else", data->len)); + } else { + g_assert(data->len == strlen("something")); + g_assert(!memcmp(data->data, "something", data->len)); + } + g_byte_array_unref(data); + g_string_free(watches, true); + xs_impl_delete(s, true); +} + +static void test_xs_node_tx_fail(void) +{ + do_test_xs_node_tx(true, true); +} + +static void test_xs_node_tx_abort(void) +{ + do_test_xs_node_tx(false, false); + do_test_xs_node_tx(true, false); +} +static void test_xs_node_tx_succeed(void) +{ + do_test_xs_node_tx(false, true); +} + +static void test_xs_node_tx_rm(void) +{ + XenstoreImplState *s = setup(); + GString *watches = g_string_new(NULL); + GByteArray *data = g_byte_array_new(); + unsigned int tx_id = XBT_NULL; + int err; + + g_assert(s); + + /* Set a watch */ + err = xs_impl_watch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + g_assert(watches->len == strlen("somewatch")); + g_assert(!strcmp(watches->str, "somewatch")); + g_string_truncate(watches, 0); + + /* Write something */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", + "something"); + g_assert(!err); + g_assert(s->nr_nodes == 9); + g_assert(!strcmp(watches->str, + "some/deep/dark/relative/pathwatch")); + g_string_truncate(watches, 0); + + /* Create a transaction */ + err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id); + g_assert(!err); + + /* Delete the tree in the transaction */ + err = xs_impl_rm(s, DOMID_GUEST, tx_id, "some/deep/dark"); + g_assert(!err); + g_assert(s->nr_nodes == 9); + g_assert(!watches->len); + + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", + data); + g_assert(!err); + g_assert(data->len == strlen("something")); + g_assert(!memcmp(data->data, "something", data->len)); + g_byte_array_set_size(data, 0); + + check_serdes(s); + + /* Commit the transaction */ + err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, true); + g_assert(!err); + g_assert(s->nr_nodes == 6); + + g_assert(!strcmp(watches->str, "some/deep/darkwatch")); + g_string_truncate(watches, 0); + + /* Now the node is gone */ + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", + data); + g_assert(err == ENOENT); + g_byte_array_unref(data); + + err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + + g_string_free(watches, true); + xs_impl_delete(s, true); +} + +static void test_xs_node_tx_resurrect(void) +{ + XenstoreImplState *s = setup(); + GString *watches = g_string_new(NULL); + GByteArray *data = g_byte_array_new(); + unsigned int tx_id = XBT_NULL; + int err; + + g_assert(s); + + /* Write something */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", + "something"); + g_assert(!err); + g_assert(s->nr_nodes == 9); + + /* Another node to remain shared */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/place/safe", "keepme"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + /* This node will be wiped and resurrected */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark", + "foo"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + /* Set a watch */ + err = xs_impl_watch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + g_assert(watches->len == strlen("somewatch")); + g_assert(!strcmp(watches->str, "somewatch")); + g_string_truncate(watches, 0); + + /* Create a transaction */ + err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id); + g_assert(!err); + + /* Delete the tree in the transaction */ + err = xs_impl_rm(s, DOMID_GUEST, tx_id, "some/deep"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + g_assert(!watches->len); + + /* Resurrect part of it */ + err = write_str(s, DOMID_GUEST, tx_id, "some/deep/dark/different/path", + "something"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + check_serdes(s); + + /* Commit the transaction */ + err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, true); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + check_serdes(s); + + /* lost data */ + g_assert(strstr(watches->str, "some/deep/dark/different/pathwatch")); + /* topmost deleted */ + g_assert(strstr(watches->str, "some/deep/dark/relativewatch")); + /* lost data */ + g_assert(strstr(watches->str, "some/deep/darkwatch")); + + g_string_truncate(watches, 0); + + /* Now the node is gone */ + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", + data); + g_assert(err == ENOENT); + g_byte_array_unref(data); + + check_serdes(s); + + err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + + g_string_free(watches, true); + xs_impl_delete(s, true); +} + +static void test_xs_node_tx_resurrect2(void) +{ + XenstoreImplState *s = setup(); + GString *watches = g_string_new(NULL); + GByteArray *data = g_byte_array_new(); + unsigned int tx_id = XBT_NULL; + int err; + + g_assert(s); + + /* Write something */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", + "something"); + g_assert(!err); + g_assert(s->nr_nodes == 9); + + /* Another node to remain shared */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/place/safe", "keepme"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + /* This node will be wiped and resurrected */ + err = write_str(s, DOMID_GUEST, XBT_NULL, "some/deep/dark", + "foo"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + /* Set a watch */ + err = xs_impl_watch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + g_assert(watches->len == strlen("somewatch")); + g_assert(!strcmp(watches->str, "somewatch")); + g_string_truncate(watches, 0); + + /* Create a transaction */ + err = xs_impl_transaction_start(s, DOMID_GUEST, &tx_id); + g_assert(!err); + + /* Delete the tree in the transaction */ + err = xs_impl_rm(s, DOMID_GUEST, tx_id, "some/deep"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + g_assert(!watches->len); + + /* Resurrect part of it */ + err = write_str(s, DOMID_GUEST, tx_id, "some/deep/dark/relative/path", + "something"); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + check_serdes(s); + + /* Commit the transaction */ + err = xs_impl_transaction_end(s, DOMID_GUEST, tx_id, true); + g_assert(!err); + g_assert(s->nr_nodes == 11); + + check_serdes(s); + + /* lost data */ + g_assert(strstr(watches->str, "some/deep/dark/relative/pathwatch")); + /* lost data */ + g_assert(strstr(watches->str, "some/deep/darkwatch")); + + g_string_truncate(watches, 0); + + /* Now the node is gone */ + err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/deep/dark/relative/path", + data); + g_assert(!err); + g_assert(data->len == strlen("something")); + g_assert(!memcmp(data->data, "something", data->len)); + + g_byte_array_unref(data); + + check_serdes(s); + + err = xs_impl_unwatch(s, DOMID_GUEST, "some", "watch", + watch_cb, watches); + g_assert(!err); + + g_string_free(watches, true); + xs_impl_delete(s, true); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + module_call_init(MODULE_INIT_QOM); + + g_test_add_func("/xs_node/simple", test_xs_node_simple); + g_test_add_func("/xs_node/tx_abort", test_xs_node_tx_abort); + g_test_add_func("/xs_node/tx_fail", test_xs_node_tx_fail); + g_test_add_func("/xs_node/tx_succeed", test_xs_node_tx_succeed); + g_test_add_func("/xs_node/tx_rm", test_xs_node_tx_rm); + g_test_add_func("/xs_node/tx_resurrect", test_xs_node_tx_resurrect); + g_test_add_func("/xs_node/tx_resurrect2", test_xs_node_tx_resurrect2); + + return g_test_run(); +} diff --git a/tests/vhost-user-bridge.c b/tests/vhost-user-bridge.c index 9b1dab2f28..a5c711b1de 100644 --- a/tests/vhost-user-bridge.c +++ b/tests/vhost-user-bridge.c @@ -331,9 +331,7 @@ vubr_backend_recv_cb(int sock, void *ctx) .msg_iovlen = num, .msg_flags = MSG_DONTWAIT, }; - do { - ret = recvmsg(vubr->backend_udp_sock, &msg, 0); - } while (ret == -1 && (errno == EINTR)); + ret = RETRY_ON_EINTR(recvmsg(vubr->backend_udp_sock, &msg, 0)); if (i == 0) { iov_restore_front(elem->in_sg, sg, hdrlen); @@ -631,7 +629,6 @@ static void *notifier_thread(void *arg) static void vubr_host_notifier_setup(VubrDev *dev) { - char template[] = "/tmp/vubr-XXXXXX"; pthread_t thread; size_t length; void *addr; @@ -639,7 +636,7 @@ vubr_host_notifier_setup(VubrDev *dev) length = qemu_real_host_page_size() * VHOST_USER_BRIDGE_MAX_QUEUES; - fd = mkstemp(template); + fd = g_file_open_tmp("vubr-XXXXXX", NULL, NULL); if (fd < 0) { vubr_die("mkstemp()"); } diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include index 588bc999cc..ac56824a87 100644 --- a/tests/vm/Makefile.include +++ b/tests/vm/Makefile.include @@ -1,19 +1,27 @@ # Makefile for VM tests +# Hack to allow running in an unconfigured build tree +ifeq ($(realpath $(SRC_PATH)),$(realpath .)) +VM_PYTHON = PYTHONPATH=$(SRC_PATH)/python /usr/bin/env python3 +VM_VENV = +else +VM_PYTHON = $(PYTHON) +VM_VENV = check-venv +endif + .PHONY: vm-build-all vm-clean-all -HOST_ARCH = $(if $(ARCH),$(ARCH),$(shell uname -m)) - EFI_AARCH64 = $(wildcard $(BUILD_DIR)/pc-bios/edk2-aarch64-code.fd) -X86_IMAGES := freebsd netbsd openbsd centos fedora haiku.x86_64 +X86_IMAGES := freebsd netbsd openbsd haiku.x86_64 ifneq ($(GENISOIMAGE),) -X86_IMAGES += ubuntu.i386 centos +X86_IMAGES += centos ifneq ($(EFI_AARCH64),) ARM64_IMAGES += ubuntu.aarch64 centos.aarch64 endif endif +HOST_ARCH = $(shell uname -m) ifeq ($(HOST_ARCH),x86_64) IMAGES=$(X86_IMAGES) $(if $(USE_TCG),$(ARM64_IMAGES)) else ifeq ($(HOST_ARCH),aarch64) @@ -36,10 +44,8 @@ vm-help vm-test: @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-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" ifneq ($(EFI_AARCH64),) @echo " vm-build-ubuntu.aarch64 - Build QEMU in ubuntu aarch64 VM" @echo " vm-build-centos.aarch64 - Build QEMU in CentOS aarch64 VM" @@ -75,7 +81,7 @@ endif @echo " QEMU_IMG=/path/to/qemu-img - Change path to qemu-img tool" @echo " QEMU_LOCAL=1 - Use QEMU binary local to this build." @echo " TARGET_LIST=a,b,c - Override target list in builds" - @echo " V=1 - Enable verbose ouput on host and guest commands" + @echo " V=1 - Enable verbose output on host and guest commands" vm-build-all: $(addprefix vm-build-, $(IMAGES)) @@ -85,10 +91,10 @@ vm-clean-all: $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \ $(SRC_PATH)/tests/vm/basevm.py \ $(SRC_PATH)/tests/vm/Makefile.include \ - check-venv + $(VM_VENV) @mkdir -p $(IMAGES_DIR) $(call quiet-command, \ - $(TESTS_PYTHON) $< \ + $(VM_PYTHON) $< \ $(if $(V)$(DEBUG), --debug) \ $(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \ $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \ @@ -96,15 +102,14 @@ $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \ $(if $(LOG_CONSOLE),--log-console) \ --source-path $(SRC_PATH) \ --image "$@" \ - --force \ + $(if $(filter-out check-venv, $?), --force) \ --build-image $@, \ " VM-IMAGE $*") - # Build in VM $(IMAGE) -vm-build-%: $(IMAGES_DIR)/%.img check-venv +vm-build-%: $(IMAGES_DIR)/%.img $(VM_VENV) $(call quiet-command, \ - $(TESTS_PYTHON) $(SRC_PATH)/tests/vm/$* \ + $(VM_PYTHON) $(SRC_PATH)/tests/vm/$* \ $(if $(V)$(DEBUG), --debug) \ $(if $(DEBUG), --interactive) \ $(if $(J),--jobs $(J)) \ @@ -128,9 +133,9 @@ vm-boot-serial-%: $(IMAGES_DIR)/%.img -device virtio-net-pci,netdev=vnet \ || true -vm-boot-ssh-%: $(IMAGES_DIR)/%.img check-venv +vm-boot-ssh-%: $(IMAGES_DIR)/%.img $(VM_VENV) $(call quiet-command, \ - $(TESTS_PYTHON) $(SRC_PATH)/tests/vm/$* \ + $(VM_PYTHON) $(SRC_PATH)/tests/vm/$* \ $(if $(J),--jobs $(J)) \ $(if $(V)$(DEBUG), --debug) \ $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \ diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index d7d0413df3..4a1af04b9a 100644 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -27,6 +27,7 @@ import shutil import multiprocessing import traceback import shlex +import json from qemu.machine import QEMUMachine from qemu.utils import get_info_usernet_hostfwd_port, kvm_available @@ -99,6 +100,11 @@ class BaseVM(object): self._source_path = args.source_path # Allow input config to override defaults. self._config = DEFAULT_CONFIG.copy() + + # 1GB per core, minimum of 4. This is only a default. + mem = max(4, args.jobs) + self._config['memory'] = f"{mem}G" + if config != None: self._config.update(config) self.validate_ssh_keys() @@ -228,7 +234,8 @@ class BaseVM(object): "-o", "UserKnownHostsFile=" + os.devnull, "-o", "ConnectTimeout={}".format(self._config["ssh_timeout"]), - "-p", str(self.ssh_port), "-i", self._ssh_tmp_key_file] + "-p", str(self.ssh_port), "-i", self._ssh_tmp_key_file, + "-o", "IdentitiesOnly=yes"] # If not in debug mode, set ssh to quiet mode to # avoid printing the results of commands. if not self.debug: @@ -305,8 +312,8 @@ class BaseVM(object): self._guest = guest # Init console so we can start consuming the chars. self.console_init() - usernet_info = guest.qmp("human-monitor-command", - command_line="info usernet").get("return") + usernet_info = guest.cmd("human-monitor-command", + command_line="info usernet") self.ssh_port = get_info_usernet_hostfwd_port(usernet_info) if not self.ssh_port: raise Exception("Cannot find ssh port from 'info usernet':\n%s" % \ @@ -324,8 +331,8 @@ class BaseVM(object): def console_log(self, text): for line in re.split("[\r\n]", text): # filter out terminal escape sequences - line = re.sub("\x1b\[[0-9;?]*[a-zA-Z]", "", line) - line = re.sub("\x1b\([0-9;?]*[a-zA-Z]", "", line) + line = re.sub("\x1b\\[[0-9;?]*[a-zA-Z]", "", line) + line = re.sub("\x1b\\([0-9;?]*[a-zA-Z]", "", line) # replace unprintable chars line = re.sub("\x1b", "", line) line = re.sub("[\x00-\x1f]", ".", line) @@ -416,6 +423,8 @@ class BaseVM(object): def console_sshd_config(self, prompt): self.console_wait(prompt) self.console_send("echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config\n") + self.console_wait(prompt) + self.console_send("echo 'UseDNS no' >> /etc/ssh/sshd_config\n") for var in self.envvars: self.console_wait(prompt) self.console_send("echo 'AcceptEnv %s' >> /etc/ssh/sshd_config\n" % var) @@ -495,6 +504,16 @@ class BaseVM(object): stderr=self._stdout) return os.path.join(cidir, "cloud-init.iso") + def get_qemu_packages_from_lcitool_json(self, json_path=None): + """Parse a lcitool variables json file and return the PKGS list.""" + if json_path is None: + json_path = os.path.join( + os.path.dirname(__file__), "generated", self.name + ".json" + ) + with open(json_path, "r") as fh: + return json.load(fh)["pkgs"] + + def get_qemu_path(arch, build_path=None): """Fetch the path to the qemu binary.""" # If QEMU environment variable set, it takes precedence @@ -513,7 +532,7 @@ def get_qemu_version(qemu_path): and return the major number.""" output = subprocess.check_output([qemu_path, '--version']) version_line = output.decode("utf-8") - version_num = re.split(' |\(', version_line)[3].split('.')[0] + version_num = re.split(r' |\(', version_line)[3].split('.')[0] return int(version_num) def parse_config(config, args): @@ -563,8 +582,7 @@ def parse_args(vmcls): # more cores. but only up to a reasonable limit. User # can always override these limits with --jobs. return min(multiprocessing.cpu_count() // 2, 8) - else: - return 1 + return 1 parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, @@ -588,7 +606,7 @@ def parse_args(vmcls): parser.add_argument("--build-qemu", help="build QEMU from source in guest") parser.add_argument("--build-target", - help="QEMU build target", default="check") + help="QEMU build target", default="all check") parser.add_argument("--build-path", default=None, help="Path of build directory, "\ "for using build tree QEMU binary. ") @@ -628,9 +646,9 @@ def main(vmcls, config=None): vm = vmcls(args, config=config) if args.build_image: if os.path.exists(args.image) and not args.force: - sys.stderr.writelines(["Image file exists: %s\n" % args.image, + sys.stderr.writelines(["Image file exists, skipping build: %s\n" % args.image, "Use --force option to overwrite\n"]) - return 1 + return 0 return vm.build_image(args.image) if args.build_qemu: vm.add_source_dir(args.build_qemu) diff --git a/tests/vm/centos b/tests/vm/centos index 5c7bc1c1a9..097a9ca14d 100755 --- a/tests/vm/centos +++ b/tests/vm/centos @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # -# CentOS image +# CentOS 8 Stream image # -# Copyright 2018 Red Hat Inc. +# Copyright 2018, 2022 Red Hat Inc. # # Authors: # Fam Zheng @@ -28,13 +28,12 @@ class CentosVM(basevm.BaseVM): tar -xf $SRC_ARCHIVE; make docker-test-block@centos8 {verbose} J={jobs} NETWORK=1; make docker-test-quick@centos8 {verbose} J={jobs} NETWORK=1; - make docker-test-mingw@fedora {verbose} J={jobs} NETWORK=1; """ def build_image(self, img): - cimg = self._download_with_cache("https://cloud.centos.org/centos/8/x86_64/images/CentOS-8-GenericCloud-8.3.2011-20201204.2.x86_64.qcow2") + cimg = self._download_with_cache("https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20220125.1.x86_64.qcow2") img_tmp = img + ".tmp" - subprocess.check_call(["ln", "-f", cimg, img_tmp]) + subprocess.check_call(['cp', '-f', cimg, img_tmp]) self.exec_qemu_img("resize", img_tmp, "50G") self.boot(img_tmp, extra_args = ["-cdrom", self.gen_cloud_init_iso()]) self.wait_ssh() diff --git a/tests/vm/centos.aarch64 b/tests/vm/centos.aarch64 index 96c450f8be..3f58de1e64 100755 --- a/tests/vm/centos.aarch64 +++ b/tests/vm/centos.aarch64 @@ -20,150 +20,38 @@ import time import traceback import aarch64vm + DEFAULT_CONFIG = { 'cpu' : "max", 'machine' : "virt,gic-version=max", - 'install_cmds' : "yum install -y make ninja-build git python3 gcc gcc-c++ flex bison, "\ - "yum install -y glib2-devel perl pixman-devel zlib-devel, "\ - "alternatives --set python /usr/bin/python3, "\ - "sudo dnf config-manager "\ - "--add-repo=https://download.docker.com/linux/centos/docker-ce.repo,"\ - "sudo dnf install --nobest -y docker-ce.aarch64,"\ - "systemctl enable docker", + 'install_cmds' : ( + "dnf config-manager --set-enabled powertools, " + "dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo, " + "dnf install -y make ninja-build git python38 gcc gcc-c++ flex bison "\ + "glib2-devel pixman-devel zlib-devel docker-ce.aarch64, " + "systemctl enable docker, " + ), # We increase beyond the default time since during boot # it can take some time (many seconds) to log into the VM. 'ssh_timeout' : 60, } + class CentosAarch64VM(basevm.BaseVM): - name = "centos.aarch64" + name = "centos8.aarch64" arch = "aarch64" - login_prompt = "localhost login:" - prompt = '[root@localhost ~]#' - image_name = "CentOS-8-aarch64-1905-dvd1.iso" - image_link = "http://mirrors.usc.edu/pub/linux/distributions/centos/8.0.1905/isos/aarch64/" + image_name = "CentOS-Stream-GenericCloud-8-20220125.1.aarch64.qcow2" + image_link = "https://cloud.centos.org/centos/8-stream/aarch64/images/" image_link += image_name BUILD_SCRIPT = """ set -e; cd $(mktemp -d); - sudo chmod a+r /dev/vdb; - tar --checkpoint=.10 -xf /dev/vdb; + export SRC_ARCHIVE=/dev/vdb; + sudo chmod a+r $SRC_ARCHIVE; + tar -xf $SRC_ARCHIVE; ./configure {configure_opts}; make --output-sync {target} -j{jobs} {verbose}; """ - def set_key_perm(self): - """Set permissions properly on certain files to allow - ssh access.""" - self.console_wait_send(self.prompt, - "/usr/sbin/restorecon -R -v /root/.ssh\n") - self.console_wait_send(self.prompt, - "/usr/sbin/restorecon -R -v "\ - "/home/{}/.ssh\n".format(self._config["guest_user"])) - - def create_kickstart(self): - """Generate the kickstart file used to generate the centos image.""" - # Start with the template for the kickstart. - ks_file = self._source_path + "/tests/vm/centos-8-aarch64.ks" - subprocess.check_call("cp {} ./ks.cfg".format(ks_file), shell=True) - # Append the ssh keys to the kickstart file - # as the post processing phase of installation. - with open("ks.cfg", "a") as f: - # Add in the root pw and guest user. - rootpw = "rootpw --plaintext {}\n" - f.write(rootpw.format(self._config["root_pass"])) - add_user = "user --groups=wheel --name={} "\ - "--password={} --plaintext\n" - f.write(add_user.format(self._config["guest_user"], - self._config["guest_pass"])) - # Add the ssh keys. - f.write("%post --log=/root/ks-post.log\n") - f.write("mkdir -p /root/.ssh\n") - addkey = 'echo "{}" >> /root/.ssh/authorized_keys\n' - addkey_cmd = addkey.format(self._config["ssh_pub_key"]) - f.write(addkey_cmd) - f.write('mkdir -p /home/{}/.ssh\n'.format(self._config["guest_user"])) - addkey = 'echo "{}" >> /home/{}/.ssh/authorized_keys\n' - addkey_cmd = addkey.format(self._config["ssh_pub_key"], - self._config["guest_user"]) - f.write(addkey_cmd) - f.write("%end\n") - # Take our kickstart file and create an .iso from it. - # The .iso will be provided to qemu as we boot - # from the install dvd. - # Anaconda will recognize the label "OEMDRV" and will - # start the automated installation. - gen_iso_img = 'genisoimage -output ks.iso -volid "OEMDRV" ks.cfg' - subprocess.check_call(gen_iso_img, shell=True) - - def wait_for_shutdown(self): - """We wait for qemu to shutdown the VM and exit. - While this happens we display the console view - for easier debugging.""" - # The image creation is essentially done, - # so whether or not the wait is successful we want to - # wait for qemu to exit (the self.wait()) before we return. - try: - self.console_wait("reboot: Power down") - except Exception as e: - sys.stderr.write("Exception hit\n") - if isinstance(e, SystemExit) and e.code == 0: - return 0 - traceback.print_exc() - finally: - self.wait() - - def build_base_image(self, dest_img): - """Run through the centos installer to create - a base image with name dest_img.""" - # We create the temp image, and only rename - # to destination when we are done. - img = dest_img + ".tmp" - # Create an empty image. - # We will provide this as the install destination. - qemu_img_create = "qemu-img create {} 50G".format(img) - subprocess.check_call(qemu_img_create, shell=True) - - # Create our kickstart file to be fed to the installer. - self.create_kickstart() - # Boot the install dvd with the params as our ks.iso - os_img = self._download_with_cache(self.image_link) - dvd_iso = "centos-8-dvd.iso" - subprocess.check_call(["cp", "-f", os_img, dvd_iso]) - extra_args = "-cdrom ks.iso" - extra_args += " -drive file={},if=none,id=drive1,cache=writeback" - extra_args += " -device virtio-blk,drive=drive1,bootindex=1" - extra_args = extra_args.format(dvd_iso).split(" ") - self.boot(img, extra_args=extra_args) - self.console_wait_send("change the selection", "\n") - # We seem to need to hit esc (chr(27)) twice to abort the - # media check, which takes a long time. - # Waiting a bit seems to be more reliable before hitting esc. - self.console_wait("Checking") - time.sleep(5) - self.console_wait_send("Checking", chr(27)) - time.sleep(5) - self.console_wait_send("Checking", chr(27)) - print("Found Checking") - # Give sufficient time for the installer to create the image. - self.console_init(timeout=7200) - self.wait_for_shutdown() - os.rename(img, dest_img) - print("Done with base image build: {}".format(dest_img)) - - def check_create_base_img(self, img_base, img_dest): - """Create a base image using the installer. - We will use the base image if it exists. - This helps cut down on install time in case we - need to restart image creation, - since the base image creation can take a long time.""" - if not os.path.exists(img_base): - print("Generate new base image: {}".format(img_base)) - self.build_base_image(img_base); - else: - print("Use existing base image: {}".format(img_base)) - # Save a copy of the base image and copy it to dest. - # which we will use going forward. - subprocess.check_call(["cp", img_base, img_dest]) def boot(self, img, extra_args=None): aarch64vm.create_flash_images(self._tmpdir, self._efi_aarch64) @@ -185,42 +73,28 @@ class CentosAarch64VM(basevm.BaseVM): super(CentosAarch64VM, self).boot(img, extra_args=extra_args) def build_image(self, img): + cimg = self._download_with_cache(self.image_link) img_tmp = img + ".tmp" - self.check_create_base_img(img + ".base", img_tmp) - - # Boot the new image for the first time to finish installation. - self.boot(img_tmp) - self.console_init() - self.console_wait_send(self.login_prompt, "root\n") - self.console_wait_send("Password:", - "{}\n".format(self._config["root_pass"])) - - self.set_key_perm() - self.console_wait_send(self.prompt, "rpm -q centos-release\n") - enable_adapter = "sed -i 's/ONBOOT=no/ONBOOT=yes/g'" \ - " /etc/sysconfig/network-scripts/ifcfg-enp0s1\n" - self.console_wait_send(self.prompt, enable_adapter) - self.console_wait_send(self.prompt, "ifup enp0s1\n") - self.console_wait_send(self.prompt, - 'echo "qemu ALL=(ALL) NOPASSWD:ALL" | '\ - 'sudo tee /etc/sudoers.d/qemu\n') - self.console_wait(self.prompt) - - # Rest of the commands we issue through ssh. + subprocess.run(['cp', '-f', cimg, img_tmp]) + self.exec_qemu_img("resize", img_tmp, "50G") + self.boot(img_tmp, extra_args = ["-cdrom", self.gen_cloud_init_iso()]) self.wait_ssh(wait_root=True) + self.ssh_root_check("touch /etc/cloud/cloud-init.disabled") # If the user chooses *not* to do the second phase, # then we will jump right to the graceful shutdown if self._config['install_cmds'] != "": install_cmds = self._config['install_cmds'].split(',') for cmd in install_cmds: - self.ssh_root(cmd) + self.ssh_root_check(cmd) + self.ssh_root("poweroff") - self.wait_for_shutdown() + self.wait() os.rename(img_tmp, img) print("image creation complete: {}".format(img)) return 0 + if __name__ == "__main__": defaults = aarch64vm.get_config_defaults(CentosAarch64VM, DEFAULT_CONFIG) sys.exit(basevm.main(CentosAarch64VM, defaults)) diff --git a/tests/vm/fedora b/tests/vm/fedora deleted file mode 100755 index 92b78d6e2c..0000000000 --- a/tests/vm/fedora +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/env python3 -# -# Fedora VM image -# -# Copyright 2019 Red Hat Inc. -# -# Authors: -# Gerd Hoffmann -# -# This code is licensed under the GPL version 2 or later. See -# the COPYING file in the top-level directory. -# - -import os -import re -import sys -import time -import socket -import subprocess -import basevm - -class FedoraVM(basevm.BaseVM): - name = "fedora" - arch = "x86_64" - - base = "https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/30/" - link = base + "Server/x86_64/iso/Fedora-Server-netinst-x86_64-30-1.2.iso" - repo = base + "Server/x86_64/os/" - full = base + "Everything/x86_64/os/" - csum = "5e4eac4566d8c572bfb3bcf54b7d6c82006ec3c6c882a2c9235c6d3494d7b100" - size = "20G" - pkgs = [ - # tools - 'git-core', - 'gcc', 'binutils', 'make', 'ninja-build', - - # perl - 'perl', - - # libs: usb - '"pkgconfig(libusb-1.0)"', - '"pkgconfig(libusbredirparser-0.5)"', - - # libs: crypto - '"pkgconfig(gnutls)"', - - # libs: ui - '"pkgconfig(sdl2)"', - '"pkgconfig(gtk+-3.0)"', - '"pkgconfig(ncursesw)"', - - # libs: audio - '"pkgconfig(libpulse)"', - '"pkgconfig(alsa)"', - - # libs: migration - '"pkgconfig(libzstd)"', -] - - BUILD_SCRIPT = """ - set -e; - rm -rf /home/qemu/qemu-test.* - cd $(mktemp -d /home/qemu/qemu-test.XXXXXX); - mkdir src build; cd src; - tar -xf /dev/vdb; - cd ../build - ../src/configure --python=python3 {configure_opts}; - gmake --output-sync -j{jobs} {target} {verbose}; - """ - - def build_image(self, img): - self.print_step("Downloading install iso") - cimg = self._download_with_cache(self.link, sha256sum=self.csum) - img_tmp = img + ".tmp" - iso = img + ".install.iso" - - self.print_step("Preparing iso and disk image") - subprocess.check_call(["cp", "-f", cimg, iso]) - 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) - self.console_wait("installation process.") - time.sleep(0.3) - self.console_send("\t") - time.sleep(0.3) - self.console_send(" console=ttyS0") - proxy = os.environ.get("http_proxy") - if not proxy is None: - self.console_send(" proxy=%s" % proxy) - self.console_send(" inst.proxy=%s" % proxy) - self.console_send(" inst.repo=%s" % self.repo) - self.console_send("\n") - - self.console_wait_send("2) Use text mode", "2\n") - - self.console_wait_send("5) [!] Installation Dest", "5\n") - self.console_wait_send("1) [x]", "c\n") - self.console_wait_send("2) [ ] Use All Space", "2\n") - self.console_wait_send("2) [x] Use All Space", "c\n") - self.console_wait_send("1) [ ] Standard Part", "1\n") - self.console_wait_send("1) [x] Standard Part", "c\n") - - self.console_wait_send("7) [!] Root password", "7\n") - self.console_wait("Password:") - self.console_send("%s\n" % self._config["root_pass"]) - self.console_wait("Password (confirm):") - self.console_send("%s\n" % self._config["root_pass"]) - - self.console_wait_send("8) [ ] User creation", "8\n") - self.console_wait_send("1) [ ] Create user", "1\n") - self.console_wait_send("3) User name", "3\n") - self.console_wait_send("ENTER:", "%s\n" % self._config["guest_user"]) - self.console_wait_send("4) [ ] Use password", "4\n") - self.console_wait_send("5) Password", "5\n") - self.console_wait("Password:") - self.console_send("%s\n" % self._config["guest_pass"]) - self.console_wait("Password (confirm):") - self.console_send("%s\n" % self._config["guest_pass"]) - self.console_wait_send("7) Groups", "c\n") - - while True: - good = self.console_wait("3) [x] Installation", - "3) [!] Installation") - self.console_send("r\n") - if good: - break - time.sleep(10) - - while True: - good = self.console_wait("4) [x] Software", - "4) [!] Software") - self.console_send("r\n") - if good: - break - time.sleep(10) - self.console_send("r\n" % self._config["guest_pass"]) - - self.console_wait_send("'b' to begin install", "b\n") - - self.print_step("Installation started now, this will take a while") - - self.console_wait_send("Installation complete", "\n") - self.print_step("Installation finished, rebooting") - - # setup qemu user - prompt = " ~]$" - self.console_ssh_init(prompt, self._config["guest_user"], - self._config["guest_pass"]) - self.console_wait_send(prompt, "exit\n") - - # setup root user - prompt = " ~]#" - self.console_ssh_init(prompt, "root", self._config["root_pass"]) - self.console_sshd_config(prompt) - - # setup virtio-blk #1 (tarfile) - self.console_wait(prompt) - self.console_send("echo 'KERNEL==\"vdb\" MODE=\"666\"' >> %s\n" % - "/etc/udev/rules.d/99-qemu.rules") - - self.print_step("Configuration finished, rebooting") - self.console_wait_send(prompt, "reboot\n") - self.console_wait("login:") - self.wait_ssh() - - self.print_step("Installing packages") - self.ssh_root_check("rm -vf /etc/yum.repos.d/fedora*.repo\n") - self.ssh_root_check("echo '[fedora]' >> /etc/yum.repos.d/qemu.repo\n") - self.ssh_root_check("echo 'baseurl=%s' >> /etc/yum.repos.d/qemu.repo\n" % self.full) - self.ssh_root_check("echo 'gpgcheck=0' >> /etc/yum.repos.d/qemu.repo\n") - self.ssh_root_check("dnf install -y %s\n" % " ".join(self.pkgs)) - - # shutdown - self.ssh_root(self.poweroff) - self.console_wait("sleep state S5") - self.wait() - - if os.path.exists(img): - os.remove(img) - os.rename(img_tmp, img) - os.remove(iso) - self.print_step("All done") - -if __name__ == "__main__": - sys.exit(basevm.main(FedoraVM)) diff --git a/tests/vm/freebsd b/tests/vm/freebsd index 805db759d6..1247f40a38 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -28,42 +28,9 @@ class FreeBSDVM(basevm.BaseVM): name = "freebsd" arch = "x86_64" - link = "https://download.freebsd.org/ftp/releases/ISO-IMAGES/12.3/FreeBSD-12.3-RELEASE-amd64-disc1.iso.xz" - csum = "36dd0de50f1fe5f0a88e181e94657656de26fb64254412f74e80e128e8b938b4" + link = "https://download.freebsd.org/releases/CI-IMAGES/13.2-RELEASE/amd64/Latest/FreeBSD-13.2-RELEASE-amd64-BASIC-CI.raw.xz" + csum = "a4fb3b6c7b75dd4d58fb0d75e4caf72844bffe0ca00e66459c028b198ffb3c0e" size = "20G" - pkgs = [ - # build tools - "git", - "pkgconf", - "bzip2", - "python37", - "ninja", - - # gnu tools - "bash", - "gmake", - "gsed", - "gettext", - - # libs: crypto - "gnutls", - - # libs: images - "jpeg-turbo", - "png", - - # libs: ui - "sdl2", - "gtk3", - "libxkbcommon", - - # libs: opengl - "libepoxy", - "mesa-libs", - - # libs: migration - "zstd", - ] BUILD_SCRIPT = """ set -e; @@ -71,74 +38,44 @@ class FreeBSDVM(basevm.BaseVM): cd $(mktemp -d /home/qemu/qemu-test.XXXXXX); mkdir src build; cd src; tar -xf /dev/vtbd1; - cd ../build - ../src/configure --python=python3.7 {configure_opts}; + cd ../build; + ../src/configure --python=python3.9 --extra-ldflags=-L/usr/local/lib \ + --extra-cflags=-I/usr/local/include {configure_opts}; gmake --output-sync -j{jobs} {target} {verbose}; """ - def console_boot_serial(self): - self.console_wait_send("Autoboot", "3") - self.console_wait_send("OK", "set console=comconsole\n") - self.console_wait_send("OK", "boot\n") - def build_image(self, img): - self.print_step("Downloading install iso") + self.print_step("Downloading disk image") cimg = self._download_with_cache(self.link, sha256sum=self.csum) - img_tmp = img + ".tmp" - iso = img + ".install.iso" - iso_xz = iso + ".xz" + tmp_raw = img + ".tmp.raw" + tmp_raw_xz = tmp_raw + ".xz" + img_tmp = img + ".tmp.qcow2" - self.print_step("Preparing iso and disk image") - subprocess.check_call(["cp", "-f", cimg, iso_xz]) - subprocess.check_call(["xz", "-dvf", iso_xz]) - self.exec_qemu_img("create", "-f", "qcow2", img_tmp, self.size) + self.print_step("Preparing disk image") + subprocess.check_call(["cp", "-f", cimg, tmp_raw_xz]) + subprocess.check_call(["xz", "-dvf", tmp_raw_xz]) + self.exec_qemu_img("convert", "-O", "qcow2", tmp_raw, img_tmp) + self.exec_qemu_img("resize", img_tmp, self.size) + os.remove(tmp_raw) - self.print_step("Booting installer") + self.print_step("Preparing disk image") self.boot(img_tmp, extra_args = [ - "-bios", "pc-bios/bios-256k.bin", "-machine", "graphics=off", - "-device", "VGA", - "-cdrom", iso + "-vga", "none" ]) self.console_init() - self.console_boot_serial() - self.console_wait_send("Console type", "xterm\n") + self.console_wait_send("login:", "root\n") + self.console_wait_send("~ #", "service growfs onestart\n") - # pre-install configuration - self.console_wait_send("Welcome", "\n") - self.console_wait_send("Keymap Selection", "\n") - self.console_wait_send("Set Hostname", "freebsd\n") - self.console_wait_send("Distribution Select", "\n") - self.console_wait_send("Partitioning", "\n") - self.console_wait_send("Partition", "\n") - self.console_wait_send("Scheme", "\n") - self.console_wait_send("Editor", "f") - self.console_wait_send("Confirmation", "c") - - self.print_step("Installation started now, this will take a while") - - # post-install configuration + # root user + self.console_wait_send("~ #", "passwd\n") self.console_wait("New Password:") self.console_send("%s\n" % self._config["root_pass"]) self.console_wait("Retype New Password:") self.console_send("%s\n" % self._config["root_pass"]) - self.console_wait_send("Network Configuration", "\n") - self.console_wait_send("IPv4", "y") - self.console_wait_send("DHCP", "y") - self.console_wait_send("IPv6", "n") - self.console_wait_send("Resolver", "\n") - - self.console_wait_send("Time Zone Selector", "0\n") - self.console_wait_send("Confirmation", "y") - self.console_wait_send("Time & Date", "\n") - self.console_wait_send("Time & Date", "\n") - - self.console_wait_send("System Configuration", "\n") - self.console_wait_send("System Hardening", "\n") - # qemu user - self.console_wait_send("Add User Accounts", "y") + self.console_wait_send("~ #", "adduser\n") self.console_wait("Username") self.console_send("%s\n" % self._config["guest_user"]) self.console_wait("Full name") @@ -160,13 +97,7 @@ class FreeBSDVM(basevm.BaseVM): self.console_wait_send("Lock out", "\n") self.console_wait_send("OK", "yes\n") self.console_wait_send("Add another user", "no\n") - - self.console_wait_send("Final Configuration", "\n") - self.console_wait_send("Manual Configuration", "\n") - self.console_wait_send("Complete", "\n") - - self.print_step("Installation finished, rebooting") - self.console_boot_serial() + self.console_wait_send("~ #", "exit\n") # setup qemu user prompt = "$" @@ -177,36 +108,23 @@ class FreeBSDVM(basevm.BaseVM): prompt = "root@freebsd:~ #" self.console_ssh_init(prompt, "root", self._config["root_pass"]) self.console_sshd_config(prompt) - - # setup serial console - self.console_wait(prompt) - self.console_send("echo 'console=comconsole' >> /boot/loader.conf\n") - - # setup boot delay - self.console_wait(prompt) - self.console_send("echo 'autoboot_delay=1' >> /boot/loader.conf\n") + self.console_wait_send(prompt, "service sshd reload\n") # setup virtio-blk #1 (tarfile) self.console_wait(prompt) self.console_send("echo 'chmod 666 /dev/vtbd1' >> /etc/rc.local\n") - self.print_step("Configuration finished, rebooting") - self.console_wait_send(prompt, "reboot\n") - self.console_wait("login:") - self.wait_ssh() - + pkgs = self.get_qemu_packages_from_lcitool_json() self.print_step("Installing packages") - self.ssh_root_check("pkg install -y %s\n" % " ".join(self.pkgs)) + self.ssh_root_check("pkg install -y %s\n" % " ".join(pkgs)) # shutdown self.ssh_root(self.poweroff) - self.console_wait("Uptime:") self.wait() if os.path.exists(img): os.remove(img) os.rename(img_tmp, img) - os.remove(iso) self.print_step("All done") if __name__ == "__main__": diff --git a/tests/vm/generated/README b/tests/vm/generated/README new file mode 100644 index 0000000000..7ccc6ffd3d --- /dev/null +++ b/tests/vm/generated/README @@ -0,0 +1,5 @@ +# FILES IN THIS FOLDER WERE AUTO-GENERATED +# +# $ make lcitool-refresh +# +# https://gitlab.com/libvirt/libvirt-ci diff --git a/tests/vm/generated/freebsd.json b/tests/vm/generated/freebsd.json new file mode 100644 index 0000000000..2d5895ebed --- /dev/null +++ b/tests/vm/generated/freebsd.json @@ -0,0 +1,78 @@ +{ + "ccache": "/usr/local/bin/ccache", + "cpan_pkgs": [], + "cross_pkgs": [], + "make": "/usr/local/bin/gmake", + "ninja": "/usr/local/bin/ninja", + "packaging_command": "pkg", + "pip3": "/usr/local/bin/pip-3.8", + "pkgs": [ + "alsa-lib", + "bash", + "bison", + "bzip2", + "ca_root_nss", + "capstone4", + "ccache", + "cmocka", + "ctags", + "curl", + "cyrus-sasl", + "dbus", + "diffutils", + "dtc", + "flex", + "fusefs-libs3", + "gettext", + "git", + "glib", + "gmake", + "gnutls", + "gsed", + "gtk3", + "json-c", + "libepoxy", + "libffi", + "libgcrypt", + "libjpeg-turbo", + "libnfs", + "libslirp", + "libspice-server", + "libssh", + "libtasn1", + "llvm", + "lzo2", + "meson", + "mtools", + "ncurses", + "nettle", + "ninja", + "opencv", + "pixman", + "pkgconf", + "png", + "py39-numpy", + "py39-pillow", + "py39-pip", + "py39-sphinx", + "py39-sphinx_rtd_theme", + "py39-tomli", + "py39-yaml", + "python3", + "rpm2cpio", + "sdl2", + "sdl2_image", + "snappy", + "sndio", + "socat", + "spice-protocol", + "tesseract", + "usbredir", + "virglrenderer", + "vte3", + "xorriso", + "zstd" + ], + "pypi_pkgs": [], + "python": "/usr/local/bin/python3" +} diff --git a/tests/vm/haiku.x86_64 b/tests/vm/haiku.x86_64 index 936f7d2ae2..71cf75a9a3 100755 --- a/tests/vm/haiku.x86_64 +++ b/tests/vm/haiku.x86_64 @@ -48,8 +48,8 @@ class HaikuVM(basevm.BaseVM): name = "haiku" arch = "x86_64" - link = "https://app.vagrantup.com/haiku-os/boxes/r1beta3-x86_64/versions/20220216/providers/libvirt.box" - csum = "e67d4aacbcc687013d5cc91990ddd86cc5d70a5d28432ae2691944f8ce5d5041" + link = "https://app.vagrantup.com/haiku-os/boxes/r1beta4-x86_64/versions/20230114/providers/libvirt.box" + csum = "6e72a2a470e03dbc3c5e808664e057bb4022b390dca88e4c7da6188f26f6a3c9" poweroff = "shutdown" @@ -71,6 +71,7 @@ class HaikuVM(basevm.BaseVM): "devel:libpixman_1", "devel:libpng16", "devel:libsdl2_2.0", + "devel:libslirp", "devel:libsnappy", "devel:libssh2", "devel:libtasn1", @@ -79,17 +80,16 @@ class HaikuVM(basevm.BaseVM): "ninja", ] - # https://dev.haiku-os.org/ticket/16512 virtio disk1 shows up as 0 (reversed order) BUILD_SCRIPT = """ set -e; rm -rf /tmp/qemu-test.* cd $(mktemp -d /tmp/qemu-test.XXXXXX); mkdir src build; cd src; - tar -xf /dev/disk/virtual/virtio_block/0/raw; + tar -xf /dev/disk/virtual/virtio_block/1/raw; mkdir -p /usr/bin ln -s /boot/system/bin/env /usr/bin/env cd ../build - ../src/configure --disable-slirp {configure_opts}; + ../src/configure {configure_opts}; make --output-sync -j{jobs} {target} {verbose}; """ diff --git a/tests/vm/netbsd b/tests/vm/netbsd index 45aa9a7fda..a3f6dd6b3c 100755 --- a/tests/vm/netbsd +++ b/tests/vm/netbsd @@ -22,15 +22,15 @@ class NetBSDVM(basevm.BaseVM): name = "netbsd" arch = "x86_64" - link = "https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.2/images/NetBSD-9.2-amd64.iso" - csum = "5ee0ea101f73386b9b424f5d1041e371db3c42fdd6f4e4518dc79c4a08f31d43091ebe93425c9f0dcaaed2b51131836fe6774f33f89030b58d64709b35fda72f" + link = "https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.3/images/NetBSD-9.3-amd64.iso" + csum = "2bfce544f762a579f61478e7106c436fc48731ff25cf6f79b392ba5752e6f5ec130364286f7471716290a5f033637cf56aacee7fedb91095face59adf36300c3" size = "20G" pkgs = [ # tools "git-base", "pkgconf", "xz", - "python37", + "python311", "ninja-build", # gnu tools @@ -39,6 +39,9 @@ class NetBSDVM(basevm.BaseVM): "gsed", "gettext-tools", + # libs: basic + "dtc", + # libs: crypto "gnutls", @@ -54,6 +57,9 @@ class NetBSDVM(basevm.BaseVM): # libs: migration "zstd", + + # libs: networking + "libslirp", ] BUILD_SCRIPT = """ @@ -63,7 +69,8 @@ class NetBSDVM(basevm.BaseVM): mkdir src build; cd src; tar -xf /dev/rld1a; cd ../build - ../src/configure --python=python3.7 --disable-opengl {configure_opts}; + ../src/configure --disable-opengl --extra-ldflags=-L/usr/pkg/lib \ + --extra-cflags=-I/usr/pkg/include {configure_opts}; gmake --output-sync -j{jobs} {target} {verbose}; """ poweroff = "/sbin/poweroff" @@ -86,7 +93,6 @@ class NetBSDVM(basevm.BaseVM): self.print_step("Booting installer") self.boot(img_tmp, extra_args = [ - "-bios", "pc-bios/bios-256k.bin", "-machine", "graphics=off", "-cdrom", iso ]) diff --git a/tests/vm/openbsd b/tests/vm/openbsd index 13c8254214..85c9863633 100755 --- a/tests/vm/openbsd +++ b/tests/vm/openbsd @@ -22,11 +22,12 @@ class OpenBSDVM(basevm.BaseVM): name = "openbsd" arch = "x86_64" - link = "https://cdn.openbsd.org/pub/OpenBSD/7.1/amd64/install71.iso" - csum = "d3a7c5b9bf890bc404304a1c96f9ee72e1d9bbcf9cc849c1133bdb0d67843396" + link = "https://cdn.openbsd.org/pub/OpenBSD/7.4/amd64/install74.iso" + csum = "a1001736ed9fe2307965b5fcdb426ae11f9b80d26eb21e404a705144a0a224a0" size = "20G" pkgs = [ # tools + "dtc", "git", "pkgconf", "bzip2", "xz", @@ -56,6 +57,9 @@ class OpenBSDVM(basevm.BaseVM): # libs: migration "zstd", + + # libs: networking + "libslirp", ] BUILD_SCRIPT = """ @@ -64,8 +68,9 @@ class OpenBSDVM(basevm.BaseVM): cd $(mktemp -d /home/qemu/qemu-test.XXXXXX); mkdir src build; cd src; tar -xf /dev/rsd1c; - cd ../build - ../src/configure --cc=cc --python=python3 {configure_opts}; + cd ../build; + ../src/configure --cc=cc --extra-cflags=-I/usr/local/include \ + --extra-ldflags=-L/usr/local/lib {configure_opts}; gmake --output-sync -j{jobs} {target} {verbose}; """ poweroff = "halt -p" @@ -82,7 +87,6 @@ class OpenBSDVM(basevm.BaseVM): self.print_step("Booting installer") self.boot(img_tmp, extra_args = [ - "-bios", "pc-bios/bios-256k.bin", "-machine", "graphics=off", "-device", "VGA", "-cdrom", iso @@ -95,17 +99,16 @@ class OpenBSDVM(basevm.BaseVM): self.console_wait_send("(I)nstall", "i\n") self.console_wait_send("Terminal type", "xterm\n") self.console_wait_send("System hostname", "openbsd\n") - self.console_wait_send("Which network interface", "vio0\n") + self.console_wait_send("Network interface to configure", "vio0\n") self.console_wait_send("IPv4 address", "autoconf\n") self.console_wait_send("IPv6 address", "none\n") - self.console_wait_send("Which network interface", "done\n") + self.console_wait_send("Network interface to configure", "done\n") self.console_wait("Password for root account") self.console_send("%s\n" % self._config["root_pass"]) self.console_wait("Password for root account") self.console_send("%s\n" % self._config["root_pass"]) self.console_wait_send("Start sshd(8)", "yes\n") - self.console_wait_send("X Window System", "\n") - self.console_wait_send("xenodm", "\n") + self.console_wait_send("X Window System", "no\n") self.console_wait_send("console to com0", "\n") self.console_wait_send("Which speed", "\n") @@ -121,8 +124,34 @@ class OpenBSDVM(basevm.BaseVM): self.console_wait_send("Allow root ssh login", "yes\n") self.console_wait_send("timezone", "UTC\n") self.console_wait_send("root disk", "\n") + self.console_wait_send("Encrypt the root disk with a passphrase", "no\n") self.console_wait_send("(W)hole disk", "\n") - self.console_wait_send("(A)uto layout", "\n") + self.console_wait_send("(A)uto layout", "c\n") + + # 4000 MB / as /dev/sd0a, at start of disk + self.console_wait_send("sd0>", "a a\n") + self.console_wait_send("offset:", "\n") + self.console_wait_send("size:", "4000M\n") + self.console_wait_send("FS type", "4.2BSD\n") + self.console_wait_send("mount point:", "/\n") + + # 256 MB swap as /dev/sd0b + self.console_wait_send("sd0*>", "a b\n") + self.console_wait_send("offset:", "\n") + self.console_wait_send("size:", "256M\n") + self.console_wait_send("FS type", "swap\n") + + # All remaining space for /home as /dev/sd0d + # NB, 'c' isn't allowed to be used. + self.console_wait_send("sd0*>", "a d\n") + self.console_wait_send("offset:", "\n") + self.console_wait_send("size:", "\n") + self.console_wait_send("FS type", "4.2BSD\n") + self.console_wait_send("mount point:", "/home\n") + + self.console_wait_send("sd0*>", "q\n") + self.console_wait_send("Write new label?:", "y\n") + self.console_wait_send("Location of sets", "cd0\n") self.console_wait_send("Pathname to the sets", "\n") self.console_wait_send("Set name(s)", "\n") diff --git a/tests/vm/ubuntu.aarch64 b/tests/vm/ubuntu.aarch64 index b291945a7e..eeda281f87 100755 --- a/tests/vm/ubuntu.aarch64 +++ b/tests/vm/ubuntu.aarch64 @@ -25,16 +25,20 @@ DEFAULT_CONFIG = { "apt-get install -y libfdt-dev pkg-config language-pack-en ninja-build", # We increase beyond the default time since during boot # it can take some time (many seconds) to log into the VM - # especially using softmmu. + # especially using TCG. 'ssh_timeout' : 60, } class UbuntuAarch64VM(ubuntuvm.UbuntuVM): name = "ubuntu.aarch64" arch = "aarch64" - image_name = "ubuntu-18.04-server-cloudimg-arm64.img" - image_link = "https://cloud-images.ubuntu.com/releases/18.04/release/" + image_name - image_sha256="0fdcba761965735a8a903d8b88df8e47f156f48715c00508e4315c506d7d3cb1" + # NOTE: The Ubuntu 20.04 cloud images are periodically updated. The + # fixed image chosen below is the latest release at time of + # writing. Using a rolling latest instead would mean that the SHA + # would be incorrect at an indeterminate point in the future. + image_name = "focal-server-cloudimg-arm64.img" + image_link = "https://cloud-images.ubuntu.com/focal/20220615/" + image_name + image_sha256="95a027336e197debe88c92ff2e554598e23c409139e1e750b71b3b820b514832" BUILD_SCRIPT = """ set -e; cd $(mktemp -d); diff --git a/tests/vm/ubuntu.i386 b/tests/vm/ubuntu.i386 deleted file mode 100755 index 47681b6f87..0000000000 --- a/tests/vm/ubuntu.i386 +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python3 -# -# Ubuntu i386 image -# -# Copyright 2017 Red Hat Inc. -# -# Authors: -# Fam Zheng -# -# This code is licensed under the GPL version 2 or later. See -# the COPYING file in the top-level directory. -# - -import sys -import basevm -import ubuntuvm - -DEFAULT_CONFIG = { - 'install_cmds' : "apt-get update,"\ - "apt-get build-dep -y qemu,"\ - "apt-get install -y libfdt-dev language-pack-en ninja-build", -} - -class UbuntuX86VM(ubuntuvm.UbuntuVM): - name = "ubuntu.i386" - arch = "i386" - image_link="https://cloud-images.ubuntu.com/releases/bionic/"\ - "release-20191114/ubuntu-18.04-server-cloudimg-i386.img" - image_sha256="28969840626d1ea80bb249c08eef1a4533e8904aa51a327b40f37ac4b4ff04ef" - BUILD_SCRIPT = """ - set -e; - cd $(mktemp -d); - sudo chmod a+r /dev/vdb; - tar -xf /dev/vdb; - ./configure {configure_opts}; - make --output-sync {target} -j{jobs} {verbose}; - """ - -if __name__ == "__main__": - sys.exit(basevm.main(UbuntuX86VM, DEFAULT_CONFIG)) diff --git a/tests/vm/ubuntuvm.py b/tests/vm/ubuntuvm.py index 6689ad87aa..15c530c571 100644 --- a/tests/vm/ubuntuvm.py +++ b/tests/vm/ubuntuvm.py @@ -51,7 +51,7 @@ class UbuntuVM(basevm.BaseVM): # then we will jump right to the graceful shutdown if self._config['install_cmds'] != "": # Issue the install commands. - # This can be overriden by the user in the config .yml. + # This can be overridden by the user in the config .yml. install_cmds = self._config['install_cmds'].split(',') for cmd in install_cmds: self.ssh_root(cmd) diff --git a/tools/ebpf/Makefile.ebpf b/tools/ebpf/Makefile.ebpf index 8f327ae3b8..3391e7ce08 100755 --- a/tools/ebpf/Makefile.ebpf +++ b/tools/ebpf/Makefile.ebpf @@ -1,9 +1,9 @@ OBJS = rss.bpf.o -LLC ?= llc +LLVM_STRIP ?= llvm-strip CLANG ?= clang INC_FLAGS = `$(CLANG) -print-file-name=include` -EXTRA_CFLAGS ?= -O2 -emit-llvm -fno-stack-protector +EXTRA_CFLAGS ?= -O2 -g -target bpf all: $(OBJS) @@ -11,11 +11,13 @@ all: $(OBJS) clean: rm -f $(OBJS) + rm -f rss.bpf.skeleton.h $(OBJS): %.o:%.c $(CLANG) $(INC_FLAGS) \ -D__KERNEL__ -D__ASM_SYSREG_H \ -I../include $(LINUXINCLUDE) \ - $(EXTRA_CFLAGS) -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@ + $(EXTRA_CFLAGS) -c $< -o $@ + $(LLVM_STRIP) -g $@ bpftool gen skeleton rss.bpf.o > rss.bpf.skeleton.h cp rss.bpf.skeleton.h ../../ebpf/ diff --git a/tools/ebpf/rss.bpf.c b/tools/ebpf/rss.bpf.c index e85ec55f9b..9715d1170e 100644 --- a/tools/ebpf/rss.bpf.c +++ b/tools/ebpf/rss.bpf.c @@ -76,29 +76,29 @@ struct packet_hash_info_t { }; }; -struct bpf_map_def SEC("maps") -tap_rss_map_configurations = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(__u32), - .value_size = sizeof(struct rss_config_t), - .max_entries = 1, -}; +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(struct rss_config_t)); + __uint(max_entries, 1); + __uint(map_flags, BPF_F_MMAPABLE); +} tap_rss_map_configurations SEC(".maps"); -struct bpf_map_def SEC("maps") -tap_rss_map_toeplitz_key = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(__u32), - .value_size = sizeof(struct toeplitz_key_data_t), - .max_entries = 1, -}; +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(struct toeplitz_key_data_t)); + __uint(max_entries, 1); + __uint(map_flags, BPF_F_MMAPABLE); +} tap_rss_map_toeplitz_key SEC(".maps"); -struct bpf_map_def SEC("maps") -tap_rss_map_indirection_table = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(__u32), - .value_size = sizeof(__u16), - .max_entries = INDIRECTION_TABLE_SIZE, -}; +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u16)); + __uint(max_entries, INDIRECTION_TABLE_SIZE); + __uint(map_flags, BPF_F_MMAPABLE); +} tap_rss_map_indirection_table SEC(".maps"); static inline void net_rx_rss_add_chunk(__u8 *rss_input, size_t *bytes_written, const void *ptr, size_t size) { @@ -320,7 +320,7 @@ static inline int parse_packet(struct __sk_buff *skb, info->in_src = ip.saddr; info->in_dst = ip.daddr; - info->is_fragmented = !!ip.frag_off; + info->is_fragmented = !!(bpf_ntohs(ip.frag_off) & (0x2000 | 0x1fff)); l4_protocol = ip.protocol; l4_offset = ip.ihl * 4; @@ -531,7 +531,7 @@ static inline __u32 calculate_rss_hash(struct __sk_buff *skb, return result; } -SEC("tun_rss_steering") +SEC("socket") int tun_rss_steering_prog(struct __sk_buff *skb) { diff --git a/tools/meson.build b/tools/meson.build index 10eb3a043f..e69de29bb2 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -1,13 +0,0 @@ -have_virtiofsd = get_option('virtiofsd') \ - .require(targetos == 'linux', - error_message: 'virtiofsd requires Linux') \ - .require(seccomp.found() and libcap_ng.found(), - error_message: 'virtiofsd requires libcap-ng-devel and seccomp-devel') \ - .require(have_vhost_user, - error_message: 'virtiofsd needs vhost-user-support') \ - .disable_auto_if(not have_tools and not have_system) \ - .allowed() - -if have_virtiofsd - subdir('virtiofsd') -endif diff --git a/tools/virtiofsd/50-qemu-virtiofsd.json.in b/tools/virtiofsd/50-qemu-virtiofsd.json.in deleted file mode 100644 index 9bcd86f8dc..0000000000 --- a/tools/virtiofsd/50-qemu-virtiofsd.json.in +++ /dev/null @@ -1,5 +0,0 @@ -{ - "description": "QEMU virtiofsd vhost-user-fs", - "type": "fs", - "binary": "@libexecdir@/virtiofsd" -} diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c deleted file mode 100644 index b5f04be356..0000000000 --- a/tools/virtiofsd/buffer.c +++ /dev/null @@ -1,350 +0,0 @@ -/* - * 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" - -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 = g_try_new0(struct iovec, iovcnt); - 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; - } - - g_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); - - if (!buf) { - return 0; - } - - 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 deleted file mode 100644 index bf46954dab..0000000000 --- a/tools/virtiofsd/fuse_common.h +++ /dev/null @@ -1,837 +0,0 @@ -/* - * 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" - -/** 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) - -/** - * Indicates that the kernel supports the FUSE_ATTR_SUBMOUNT flag. - * - * Setting (or unsetting) this flag in the `want` field has *no effect*. - */ -#define FUSE_CAP_SUBMOUNTS (1 << 27) - -/** - * Indicates that the filesystem is responsible for clearing - * security.capability xattr and clearing setuid and setgid bits. Following - * are the rules. - * - clear "security.capability" on write, truncate and chown unconditionally - * - clear suid/sgid if following is true. Note, sgid is cleared only if - * group executable bit is set. - * o setattr has FATTR_SIZE and FATTR_KILL_SUIDGID set. - * o setattr has FATTR_UID or FATTR_GID - * o open has O_TRUNC and FUSE_OPEN_KILL_SUIDGID - * o create has O_TRUNC and FUSE_OPEN_KILL_SUIDGID flag set. - * o write has FUSE_WRITE_KILL_SUIDGID - */ -#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1 << 28) - -/** - * Indicates that file server supports extended struct fuse_setxattr_in - */ -#define FUSE_CAP_SETXATTR_EXT (1 << 29) - -/** - * Indicates that file server supports creating file security context - */ -#define FUSE_CAP_SECURITY_CTX (1ULL << 32) - -/** - * 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) - */ - uint64_t capable; - - /** - * Capability flags that the filesystem wants to enable. - * - * libfuse attempts to initialize this field with - * reasonable default values before calling the init() handler. - */ - uint64_t 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! - */ -QEMU_BUILD_BUG_ON(sizeof(off_t) != 8); - -#endif /* FUSE_COMMON_H_ */ diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h deleted file mode 100644 index a5572fa4ae..0000000000 --- a/tools/virtiofsd/fuse_i.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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_security_context { - const char *name; - uint32_t ctxlen; - const void *ctx; -}; - -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_security_context secctx; -}; - -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; - char *vu_socket_group; - 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 deleted file mode 100644 index 745d88cd2a..0000000000 --- a/tools/virtiofsd/fuse_log.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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" - - -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 deleted file mode 100644 index 8d7091bd4d..0000000000 --- a/tools/virtiofsd/fuse_log.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 - */ - - -/** - * 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 deleted file mode 100644 index 752928741d..0000000000 --- a/tools/virtiofsd/fuse_lowlevel.c +++ /dev/null @@ -1,2744 +0,0 @@ -/* - * 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 - -#define THREAD_POOL_SIZE 0 - -#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); - g_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 = g_try_new0(struct fuse_req, 1); - 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; - g_autofree struct iovec *padded_iov = NULL; - - padded_iov = g_try_new(struct iovec, count + 1); - 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); - - 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); - - arg->attr.flags = e->attr_flags; -} - -/* - * `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 = g_try_new(struct fuse_ioctl_iovec, 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; - g_autofree struct fuse_ioctl_iovec *in_fiov = NULL; - g_autofree 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); - return res; - } - - if (in_count) { - in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count); - if (!in_fiov) { - res = fuse_reply_err(req, ENOMEM); - return res; - } - - 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) { - res = fuse_reply_err(req, ENOMEM); - return res; - } - - 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); - - return res; -} - -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) -{ - g_autofree struct iovec *padded_iov = NULL; - struct fuse_ioctl_out arg; - int res; - - padded_iov = g_try_new(struct iovec, count + 2); - 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); - - 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 | FUSE_SET_ATTR_KILL_SUIDGID; - - 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 int parse_secctx_fill_req(fuse_req_t req, struct fuse_mbuf_iter *iter) -{ - struct fuse_secctx_header *fsecctx_header; - struct fuse_secctx *fsecctx; - const void *secctx; - const char *name; - - fsecctx_header = fuse_mbuf_iter_advance(iter, sizeof(*fsecctx_header)); - if (!fsecctx_header) { - return -EINVAL; - } - - /* - * As of now maximum of one security context is supported. It can - * change in future though. - */ - if (fsecctx_header->nr_secctx > 1) { - return -EINVAL; - } - - /* No security context sent. Maybe no LSM supports it */ - if (!fsecctx_header->nr_secctx) { - return 0; - } - - fsecctx = fuse_mbuf_iter_advance(iter, sizeof(*fsecctx)); - if (!fsecctx) { - return -EINVAL; - } - - /* struct fsecctx with zero sized context is not expected */ - if (!fsecctx->size) { - return -EINVAL; - } - name = fuse_mbuf_iter_advance_str(iter); - if (!name) { - return -EINVAL; - } - - secctx = fuse_mbuf_iter_advance(iter, fsecctx->size); - if (!secctx) { - return -EINVAL; - } - - req->secctx.name = name; - req->secctx.ctx = secctx; - req->secctx.ctxlen = fsecctx->size; - return 0; -} - -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; - bool secctx_enabled = req->se->conn.want & FUSE_CAP_SECURITY_CTX; - int err; - - 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 (secctx_enabled) { - err = parse_secctx_fill_req(req, iter); - if (err) { - fuse_reply_err(req, -err); - return; - } - } - - 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; - bool secctx_enabled = req->se->conn.want & FUSE_CAP_SECURITY_CTX; - int err; - - 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 (secctx_enabled) { - err = parse_secctx_fill_req(req, iter); - if (err) { - fuse_reply_err(req, err); - return; - } - } - - 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); - bool secctx_enabled = req->se->conn.want & FUSE_CAP_SECURITY_CTX; - int err; - - if (!name || !linkname) { - fuse_reply_err(req, EINVAL); - return; - } - - if (secctx_enabled) { - err = parse_secctx_fill_req(req, iter); - if (err) { - fuse_reply_err(req, err); - 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) -{ - bool secctx_enabled = req->se->conn.want & FUSE_CAP_SECURITY_CTX; - - 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; - } - - if (secctx_enabled) { - int err; - err = parse_secctx_fill_req(req, iter); - if (err) { - fuse_reply_err(req, err); - return; - } - } - - memset(&fi, 0, sizeof(fi)); - fi.flags = arg->flags; - fi.kill_priv = arg->open_flags & FUSE_OPEN_KILL_SUIDGID; - - 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; - } - - /* File creation is handled by do_create() or do_mknod() */ - if (arg->flags & (O_CREAT | O_TMPFILE)) { - fuse_reply_err(req, EINVAL); - return; - } - - memset(&fi, 0, sizeof(fi)); - fi.flags = arg->flags; - fi.kill_priv = arg->open_flags & FUSE_OPEN_KILL_SUIDGID; - - 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; - bool setxattr_ext = req->se->conn.want & FUSE_CAP_SETXATTR_EXT; - - if (setxattr_ext) { - arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); - } else { - arg = fuse_mbuf_iter_advance(iter, FUSE_COMPAT_SETXATTR_IN_SIZE); - } - 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) { - uint32_t setxattr_flags = setxattr_ext ? arg->setxattr_flags : 0; - req->se->op.setxattr(req, nodeid, name, value, arg->size, arg->flags, - setxattr_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); - g_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_syncfs(fuse_req_t req, fuse_ino_t nodeid, - struct fuse_mbuf_iter *iter) -{ - if (req->se->op.syncfs) { - req->se->op.syncfs(req, nodeid); - } 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); - size_t compat2_size = offsetof(struct fuse_init_in, flags) + - sizeof(uint32_t); - /* Fuse structure extended with minor version 36 */ - size_t compat3_size = endof(struct fuse_init_in, unused); - 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); - uint64_t flags = 0; - - (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, compat2_size - compat_size)) { - fuse_reply_err(req, EINVAL); - return; - } - flags |= arg->flags; - } - - /* - * fuse_init_in was extended again with minor version 36. Just read - * current known size of fuse_init so that future extension and - * header rebase does not cause breakage. - */ - if (sizeof(*arg) > compat2_size && (arg->flags & FUSE_INIT_EXT)) { - if (!fuse_mbuf_iter_advance(iter, compat3_size - compat2_size)) { - fuse_reply_err(req, EINVAL); - return; - } - flags |= (uint64_t) arg->flags2 << 32; - } - - 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%016llx\n", 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 (flags & FUSE_ASYNC_READ) { - se->conn.capable |= FUSE_CAP_ASYNC_READ; - } - if (flags & FUSE_POSIX_LOCKS) { - se->conn.capable |= FUSE_CAP_POSIX_LOCKS; - } - if (flags & FUSE_ATOMIC_O_TRUNC) { - se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; - } - if (flags & FUSE_EXPORT_SUPPORT) { - se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; - } - if (flags & FUSE_DONT_MASK) { - se->conn.capable |= FUSE_CAP_DONT_MASK; - } - if (flags & FUSE_FLOCK_LOCKS) { - se->conn.capable |= FUSE_CAP_FLOCK_LOCKS; - } - if (flags & FUSE_AUTO_INVAL_DATA) { - se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA; - } - if (flags & FUSE_DO_READDIRPLUS) { - se->conn.capable |= FUSE_CAP_READDIRPLUS; - } - if (flags & FUSE_READDIRPLUS_AUTO) { - se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO; - } - if (flags & FUSE_ASYNC_DIO) { - se->conn.capable |= FUSE_CAP_ASYNC_DIO; - } - if (flags & FUSE_WRITEBACK_CACHE) { - se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; - } - if (flags & FUSE_NO_OPEN_SUPPORT) { - se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT; - } - if (flags & FUSE_PARALLEL_DIROPS) { - se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; - } - if (flags & FUSE_POSIX_ACL) { - se->conn.capable |= FUSE_CAP_POSIX_ACL; - } - if (flags & FUSE_HANDLE_KILLPRIV) { - se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV; - } - if (flags & FUSE_NO_OPENDIR_SUPPORT) { - se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT; - } - if (!(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; - } - } - if (flags & FUSE_SUBMOUNTS) { - se->conn.capable |= FUSE_CAP_SUBMOUNTS; - } - if (flags & FUSE_HANDLE_KILLPRIV_V2) { - se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV_V2; - } - if (flags & FUSE_SETXATTR_EXT) { - se->conn.capable |= FUSE_CAP_SETXATTR_EXT; - } - if (flags & FUSE_SECURITY_CTX) { - se->conn.capable |= FUSE_CAP_SECURITY_CTX; - } -#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%llx 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 (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; - - if (se->conn.want & FUSE_CAP_HANDLE_KILLPRIV_V2) { - outarg.flags |= FUSE_HANDLE_KILLPRIV_V2; - } - - if (se->conn.want & FUSE_CAP_SETXATTR_EXT) { - outarg.flags |= FUSE_SETXATTR_EXT; - } - - if (se->conn.want & FUSE_CAP_SECURITY_CTX) { - /* bits 32..63 get shifted down 32 bits into the flags2 field */ - outarg.flags2 |= FUSE_SECURITY_CTX >> 32; - } - - fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor); - fuse_log(FUSE_LOG_DEBUG, " flags2=0x%08x flags=0x%08x\n", outarg.flags2, - 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); -} - -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" }, - [FUSE_SYNCFS] = { do_syncfs, "SYNCFS" }, -}; - -#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("--socket-group=%s", vu_socket_group, 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" - " --socket-group=GRNAME name of group 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; - - g_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 = g_try_new0(struct fuse_session, 1); - 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; - } - if (se->vu_socket_group && !se->vu_socket_path) { - fuse_log(FUSE_LOG_ERR, - "fuse: --socket-group can only be used with --socket-path\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: - g_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 deleted file mode 100644 index b889dae4de..0000000000 --- a/tools/virtiofsd/fuse_lowlevel.h +++ /dev/null @@ -1,1988 +0,0 @@ -/* - * 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 - -/* - * 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; - - /** - * Flags for fuse_attr.flags that do not fit into attr. - */ - uint32_t attr_flags; -}; - -/** - * 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) -#define FUSE_SET_ATTR_KILL_SUIDGID (1 << 11) - -/* - * 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, - uint32_t setxattr_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); - - /** - * Synchronize file system content - * - * If this request is answered with an error code of ENOSYS, - * this is treated as success and future calls to syncfs() will - * succeed automatically without being sent to the filesystem - * process. - * - * @param req request handle - * @param ino the inode number - */ - void (*syncfs)(fuse_req_t req, fuse_ino_t ino); -}; - -/** - * 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 filesystem 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 filesystem 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; - unsigned long rlimit_nofile; -}; - -/** - * 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 deleted file mode 100644 index f252baa752..0000000000 --- a/tools/virtiofsd/fuse_misc.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 - -/* - * 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 deleted file mode 100644 index 9d371448e9..0000000000 --- a/tools/virtiofsd/fuse_opt.c +++ /dev/null @@ -1,446 +0,0 @@ -/* - * 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" - - -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 = g_try_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); - g_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 deleted file mode 100644 index 8f59b4d301..0000000000 --- a/tools/virtiofsd/fuse_opt.h +++ /dev/null @@ -1,272 +0,0 @@ -/* - * 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 deleted file mode 100644 index 1de46de1ce..0000000000 --- a/tools/virtiofsd/fuse_signals.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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" - - -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 deleted file mode 100644 index a52eacf82e..0000000000 --- a/tools/virtiofsd/fuse_virtio.c +++ /dev/null @@ -1,1081 +0,0 @@ -/* - * 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 "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; -}; - -/* 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 size_t copy_from_iov(struct fuse_buf *buf, size_t out_num, - const struct iovec *out_sg, - size_t max) -{ - void *dest = buf->mem; - size_t copied = 0; - - while (out_num && max) { - size_t onelen = out_sg->iov_len; - onelen = MIN(onelen, max); - memcpy(dest, out_sg->iov_base, onelen); - dest += onelen; - copied += onelen; - out_sg++; - out_num--; - max -= onelen; - } - - return copied; -} - -/* - * Skip 'skip' bytes in the iov; 'sg_1stindex' is set as - * the index for the 1st iovec to read data from, and - * 'sg_1stskip' is the number of bytes to skip in that entry. - * - * Returns True if there are at least 'skip' bytes in the iovec - * - */ -static bool skip_iov(const struct iovec *sg, size_t sg_size, - size_t skip, - size_t *sg_1stindex, size_t *sg_1stskip) -{ - size_t vec; - - for (vec = 0; vec < sg_size; vec++) { - if (sg[vec].iov_len > skip) { - *sg_1stskip = skip; - *sg_1stindex = vec; - - return true; - } - - skip -= sg[vec].iov_len; - } - - *sg_1stindex = vec; - *sg_1stskip = 0; - return skip == 0; -} - -/* - * 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--; - } -} - -/* - * pthread_rwlock_rdlock() and pthread_rwlock_wrlock can fail if - * a deadlock condition is detected or the current thread already - * owns the lock. They can also fail, like pthread_rwlock_unlock(), - * if the mutex wasn't properly initialized. None of these are ever - * expected to happen. - */ -static void vu_dispatch_rdlock(struct fv_VuDev *vud) -{ - int ret = pthread_rwlock_rdlock(&vud->vu_dispatch_rwlock); - assert(ret == 0); -} - -static void vu_dispatch_wrlock(struct fv_VuDev *vud) -{ - int ret = pthread_rwlock_wrlock(&vud->vu_dispatch_rwlock); - assert(ret == 0); -} - -static void vu_dispatch_unlock(struct fv_VuDev *vud) -{ - int ret = pthread_rwlock_unlock(&vud->vu_dispatch_rwlock); - assert(ret == 0); -} - -static void vq_send_element(struct fv_QueueInfo *qi, VuVirtqElement *elem, - ssize_t len) -{ - struct fuse_session *se = qi->virtio_dev->se; - VuDev *dev = &se->virtio_dev->dev; - VuVirtq *q = vu_get_queue(dev, qi->qidx); - - vu_dispatch_rdlock(qi->virtio_dev); - pthread_mutex_lock(&qi->vq_lock); - vu_queue_push(dev, q, elem, len); - vu_queue_notify(dev, q); - pthread_mutex_unlock(&qi->vq_lock); - vu_dispatch_unlock(qi->virtio_dev); -} - -/* - * 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; - 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); - - vq_send_element(qi, elem, tosend_len); - 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; - VuVirtqElement *elem = &req->elem; - int ret = 0; - g_autofree struct iovec *in_sg_cpy = NULL; - - 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); - return E2BIG; - } - 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); - return E2BIG; - } - - /* 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 - */ - in_sg_cpy = g_new(struct iovec, in_num); - 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; - unsigned int in_sg_cpy_count = in_num; - - /* skip over parts of in_sg that contained the header iov */ - iov_discard_front(&in_sg_ptr, &in_sg_cpy_count, iov_len); - - do { - fuse_log(FUSE_LOG_DEBUG, "%s: in_sg_cpy_count=%d len remaining=%zd\n", - __func__, in_sg_cpy_count, len); - - ret = preadv(buf->buf[0].fd, in_sg_ptr, in_sg_cpy_count, - buf->buf[0].pos); - - if (ret == -1) { - ret = errno; - if (ret == EINTR) { - continue; - } - fuse_log(FUSE_LOG_DEBUG, "%s: preadv failed (%m) len=%zd\n", - __func__, len); - return ret; - } - - if (!ret) { - /* EOF case? */ - fuse_log(FUSE_LOG_DEBUG, "%s: !ret len remaining=%zd\n", __func__, - len); - break; - } - fuse_log(FUSE_LOG_DEBUG, "%s: preadv ret=%d len=%zd\n", __func__, - ret, len); - - len -= ret; - /* Short read. Retry reading remaining bytes */ - if (len) { - fuse_log(FUSE_LOG_DEBUG, "%s: ret < len\n", __func__); - /* Skip over this much next time around */ - iov_discard_front(&in_sg_ptr, &in_sg_cpy_count, ret); - buf->buf[0].pos += ret; - } - } while (len); - - /* 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; - } - - vq_send_element(qi, elem, tosend_len); - req->reply_sent = true; - return 0; -} - -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; - FVRequest *req = data; - VuVirtqElement *elem = &req->elem; - struct fuse_buf fbuf = {}; - bool allocated_bufv = false; - struct fuse_bufvec bufv; - struct fuse_bufvec *pbufv; - struct fuse_in_header inh; - - 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 = g_malloc(se->bufsize); - - 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 fuse_in_header and look at it */ - copy_from_iov(&fbuf, out_num, out_sg, - sizeof(struct fuse_in_header)); - memcpy(&inh, fbuf.mem, sizeof(struct fuse_in_header)); - - pbufv = NULL; /* Compiler thinks an unitialised path */ - if (inh.opcode == FUSE_WRITE && - out_len >= (sizeof(struct fuse_in_header) + - 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__); - - fbuf.size = copy_from_iov(&fbuf, out_num, out_sg, - sizeof(struct fuse_in_header) + - sizeof(struct fuse_write_in)); - /* That copy reread the in_header, make sure we use the original */ - memcpy(fbuf.mem, &inh, sizeof(struct fuse_in_header)); - - /* Allocate the bufv, with space for the rest of the iov */ - pbufv = g_try_malloc(sizeof(struct fuse_bufvec) + - sizeof(struct fuse_buf) * out_num); - 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, iov_bytes_skip; - pbufvindex = 1; /* 2 headers, 1 fusebuf */ - - if (!skip_iov(out_sg, out_num, - sizeof(struct fuse_in_header) + - sizeof(struct fuse_write_in), - &iovindex, &iov_bytes_skip)) { - fuse_log(FUSE_LOG_ERR, "%s: skip failed\n", - __func__); - goto out; - } - - 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; - - if (iov_bytes_skip) { - pbufv->buf[pbufvindex].mem += iov_bytes_skip; - pbufv->buf[pbufvindex].size -= iov_bytes_skip; - iov_bytes_skip = 0; - } - } - } else { - /* Normal (non fast write) path */ - - copy_from_iov(&fbuf, out_num, out_sg, se->bufsize); - /* That copy reread the in_header, make sure we use the original */ - memcpy(fbuf.mem, &inh, sizeof(struct fuse_in_header)); - 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) { - g_free(pbufv); - } - - /* If the request has no reply, still recycle the virtqueue element */ - if (!req->reply_sent) { - fuse_log(FUSE_LOG_DEBUG, "%s: elem %d no reply sent\n", __func__, - elem->index); - vq_send_element(qi, elem, 0); - } - - pthread_mutex_destroy(&req->ch.lock); - g_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 = NULL; - GList *req_list = NULL; - - if (se->thread_pool_size) { - fuse_log(FUSE_LOG_DEBUG, "%s: Creating thread pool for Queue %d\n", - __func__, qi->qidx); - pool = g_thread_pool_new(fv_queue_worker, qi, se->thread_pool_size, - FALSE, 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]; - - 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() */ - vu_dispatch_rdlock(qi->virtio_dev); - 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; - - if (!se->thread_pool_size) { - req_list = g_list_prepend(req_list, req); - } else { - g_thread_pool_push(pool, req, NULL); - } - } - - pthread_mutex_unlock(&qi->vq_lock); - vu_dispatch_unlock(qi->virtio_dev); - - /* Process all the requests. */ - if (!se->thread_pool_size && req_list != NULL) { - req_list = g_list_reverse(req_list); - g_list_foreach(req_list, fv_queue_worker, qi); - g_list_free(req_list); - req_list = NULL; - } - } - - if (pool) { - 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; - g_free(vud->qi[qidx]); - vud->qi[qidx] = NULL; -} - -static void stop_all_queues(struct fv_VuDev *vud) -{ - for (int i = 0; i < vud->nqueues; i++) { - if (!vud->qi[i]) { - continue; - } - - fuse_log(FUSE_LOG_INFO, "%s: Stopping queue %d thread\n", __func__, i); - fv_queue_cleanup_thread(vud, i); - } -} - -/* 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 = g_realloc_n(vud->qi, qidx + 1, sizeof(vud->qi[0])); - 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] = g_new0(struct fv_QueueInfo, 1); - 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 { - /* - * Temporarily drop write-lock taken in virtio_loop() so that - * the queue thread doesn't block in virtio_send_msg(). - */ - vu_dispatch_unlock(vud); - fv_queue_cleanup_thread(vud, qidx); - vu_dispatch_wrlock(vud); - } -} - -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; - 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() */ - vu_dispatch_wrlock(se->virtio_dev); - - ok = vu_dispatch(&se->virtio_dev->dev); - - vu_dispatch_unlock(se->virtio_dev); - - 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. - */ - stop_all_queues(se->virtio_dev); - 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 *state = NULL; - g_autofree gchar *dir = NULL; - Error *local_err = NULL; - - state = qemu_get_local_state_dir(); - dir = g_build_filename(state, "run", "virtiofsd", NULL); - - if (g_mkdir_with_parents(dir, S_IRWXU) < 0) { - fuse_log(FUSE_LOG_ERR, "%s: Failed to create directory %s: %s\n", - __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 appropriately and restore it later. - */ - if (se->vu_socket_group) { - old_umask = umask(S_IROTH | S_IWOTH | S_IXOTH); - } else { - old_umask = umask(S_IRGRP | S_IWGRP | S_IXGRP | - S_IROTH | S_IWOTH | S_IXOTH); - } - 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; - } - if (se->vu_socket_group) { - struct group *g = getgrnam(se->vu_socket_group); - if (g) { - if (chown(se->vu_socket_path, -1, g->gr_gid) == -1) { - fuse_log(FUSE_LOG_WARNING, - "vhost socket failed to set group to %s (%d): %m\n", - se->vu_socket_group, g->gr_gid); - } - } else { - fuse_log(FUSE_LOG_ERR, - "vhost socket: unable to find group '%s'\n", - se->vu_socket_group); - 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; - - /* - * Test that unshare(CLONE_FS) works. fv_queue_worker() will need it. It's - * an unprivileged system call but some Docker/Moby versions are known to - * reject it via seccomp when CAP_SYS_ADMIN is not given. - * - * Note that the program is single-threaded here so this syscall has no - * visible effect and is safe to make. - */ - ret = unshare(CLONE_FS); - if (ret == -1 && errno == EPERM) { - fuse_log(FUSE_LOG_ERR, "unshare(CLONE_FS) failed with EPERM. If " - "running in a container please check that the container " - "runtime seccomp policy allows unshare.\n"); - return -1; - } - - 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 = g_new0(struct fv_VuDev, 1); - - se->vu_socketfd = data_sock; - se->virtio_dev->se = se; - pthread_rwlock_init(&se->virtio_dev->vu_dispatch_rwlock, NULL); - if (!vu_init(&se->virtio_dev->dev, 2, se->vu_socketfd, fv_panic, NULL, - fv_set_watch, fv_remove_watch, &fv_iface)) { - fuse_log(FUSE_LOG_ERR, "%s: vu_init failed\n", __func__); - return -1; - } - - return 0; -} - -void virtio_session_close(struct fuse_session *se) -{ - close(se->vu_socketfd); - - if (!se->virtio_dev) { - return; - } - - g_free(se->virtio_dev->qi); - pthread_rwlock_destroy(&se->virtio_dev->vu_dispatch_rwlock); - g_free(se->virtio_dev); - se->virtio_dev = NULL; -} diff --git a/tools/virtiofsd/fuse_virtio.h b/tools/virtiofsd/fuse_virtio.h deleted file mode 100644 index 111684032c..0000000000 --- a/tools/virtiofsd/fuse_virtio.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 deleted file mode 100644 index f5f66f292c..0000000000 --- a/tools/virtiofsd/helper.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - * 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 - -#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("--rlimit-nofile=%lu", rlimit_nofile), - 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 posix_lock|no_posix_lock\n" - " enable/disable remote posix lock\n" - " default: no_posix_lock\n" - " -o readdirplus|no_readdirplus\n" - " enable/disable readirplus\n" - " default: readdirplus except with " - "cache=none\n" - " -o sandbox=namespace|chroot\n" - " sandboxing mode:\n" - " - namespace: mount, pid, and net\n" - " namespaces with pivot_root(2)\n" - " into shared directory\n" - " - chroot: chroot(2) into shared\n" - " directory (use in containers)\n" - " default: namespace\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" - " -o xattrmap= Enable xattr mapping (enables xattr)\n" - " is a string consists of a series of rules\n" - " e.g. -o xattrmap=:map::user.virtiofs.:\n" - " -o modcaps=CAPLIST Modify the list of capabilities\n" - " e.g. -o modcaps=+sys_admin:-chown\n" - " --rlimit-nofile= set maximum number of file descriptors\n" - " (0 leaves rlimit unchanged)\n" - " default: min(1000000, fs.file-max - 16384)\n" - " if the current rlimit is lower\n" - " -o allow_direct_io|no_allow_direct_io\n" - " retain/discard O_DIRECT flags passed down\n" - " to virtiofsd from guest applications.\n" - " default: no_allow_direct_io\n" - " -o announce_submounts Announce sub-mount points to the guest\n" - " -o posix_acl/no_posix_acl Enable/Disable posix_acl. (default: disabled)\n" - " -o security_label/no_security_label Enable/Disable security label. (default: disabled)\n" - " -o killpriv_v2/no_killpriv_v2\n" - " Enable/Disable FUSE_HANDLE_KILLPRIV_V2.\n" - " (default: enabled as long as client supports it)\n" - ); -} - -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; - } -} - -static unsigned long get_default_rlimit_nofile(void) -{ - g_autofree gchar *file_max_str = NULL; - const rlim_t reserved_fds = 16384; /* leave at least this many fds free */ - rlim_t max_fds = 1000000; /* our default RLIMIT_NOFILE target */ - rlim_t file_max; - struct rlimit rlim; - - /* - * Reduce max_fds below the system-wide maximum, if necessary. This - * ensures there are fds available for other processes so we don't - * cause resource exhaustion. - */ - if (!g_file_get_contents("/proc/sys/fs/file-max", &file_max_str, - NULL, NULL)) { - fuse_log(FUSE_LOG_ERR, "can't read /proc/sys/fs/file-max\n"); - exit(1); - } - file_max = g_ascii_strtoull(file_max_str, NULL, 10); - if (file_max < 2 * reserved_fds) { - fuse_log(FUSE_LOG_ERR, - "The fs.file-max sysctl is too low (%lu) to allow a " - "reasonable number of open files.\n", - (unsigned long)file_max); - exit(1); - } - max_fds = MIN(file_max - reserved_fds, max_fds); - - 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 0; /* we have more fds available than required! */ - } - return max_fds; -} - -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->rlimit_nofile = get_default_rlimit_nofile(); - 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 (!g_unix_open_pipe(waiter, FD_CLOEXEC, NULL)) { - 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/meson.build b/tools/virtiofsd/meson.build deleted file mode 100644 index c134ba633f..0000000000 --- a/tools/virtiofsd/meson.build +++ /dev/null @@ -1,18 +0,0 @@ -executable('virtiofsd', files( - 'buffer.c', - 'fuse_opt.c', - 'fuse_log.c', - 'fuse_lowlevel.c', - 'fuse_signals.c', - 'fuse_virtio.c', - 'helper.c', - 'passthrough_ll.c', - 'passthrough_seccomp.c'), - dependencies: [seccomp, qemuutil, libcap_ng, vhost_user], - install: true, - install_dir: get_option('libexecdir')) - -configure_file(input: '50-qemu-virtiofsd.json.in', - output: '50-qemu-virtiofsd.json', - configuration: { 'libexecdir' : get_option('prefix') / get_option('libexecdir') }, - install_dir: qemu_datadir / 'vhost-user') diff --git a/tools/virtiofsd/passthrough_helpers.h b/tools/virtiofsd/passthrough_helpers.h deleted file mode 100644 index 0b98275ed5..0000000000 --- a/tools/virtiofsd/passthrough_helpers.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 deleted file mode 100644 index b15c631ca5..0000000000 --- a/tools/virtiofsd/passthrough_ll.c +++ /dev/null @@ -1,4526 +0,0 @@ -/* - * 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 "qemu-version.h" -#include "qemu/help-texts.h" -#include "fuse_virtio.h" -#include "fuse_log.h" -#include "fuse_lowlevel.h" -#include "standard-headers/linux/fuse.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "qemu/cutils.h" -#include "passthrough_helpers.h" -#include "passthrough_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; - uint64_t mnt_id; -}; - -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 a FUSE_FORGET request. - * - * 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; - mode_t umask; -}; - -enum { - CACHE_NONE, - CACHE_AUTO, - CACHE_ALWAYS, -}; - -enum { - SANDBOX_NAMESPACE, - SANDBOX_CHROOT, -}; - -typedef struct xattr_map_entry { - char *key; - char *prepend; - unsigned int flags; -} XattrMapEntry; - -struct lo_data { - pthread_mutex_t mutex; - int sandbox; - int debug; - int writeback; - int flock; - int posix_lock; - int xattr; - char *xattrmap; - char *xattr_security_capability; - char *source; - char *modcaps; - double timeout; - int cache; - int timeout_set; - int readdirplus_set; - int readdirplus_clear; - int allow_direct_io; - int announce_submounts; - bool use_statx; - 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 */ - XattrMapEntry *xattr_map_list; - size_t xattr_map_nentries; - - /* An O_PATH file descriptor to /proc/self/fd/ */ - int proc_self_fd; - /* An O_PATH file descriptor to /proc/self/task/ */ - int proc_self_task; - int user_killpriv_v2, killpriv_v2; - /* If set, virtiofsd is responsible for setting umask during creation */ - bool change_umask; - int user_posix_acl, posix_acl; - /* Keeps track if /proc//attr/fscreate should be used or not */ - bool use_fscreate; - int user_security_label; -}; - -static const struct fuse_opt lo_opts[] = { - { "sandbox=namespace", - offsetof(struct lo_data, sandbox), - SANDBOX_NAMESPACE }, - { "sandbox=chroot", - offsetof(struct lo_data, sandbox), - SANDBOX_CHROOT }, - { "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 }, - { "xattrmap=%s", offsetof(struct lo_data, xattrmap), 0 }, - { "modcaps=%s", offsetof(struct lo_data, modcaps), 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 }, - { "readdirplus", offsetof(struct lo_data, readdirplus_set), 1 }, - { "no_readdirplus", offsetof(struct lo_data, readdirplus_clear), 1 }, - { "allow_direct_io", offsetof(struct lo_data, allow_direct_io), 1 }, - { "no_allow_direct_io", offsetof(struct lo_data, allow_direct_io), 0 }, - { "announce_submounts", offsetof(struct lo_data, announce_submounts), 1 }, - { "killpriv_v2", offsetof(struct lo_data, user_killpriv_v2), 1 }, - { "no_killpriv_v2", offsetof(struct lo_data, user_killpriv_v2), 0 }, - { "posix_acl", offsetof(struct lo_data, user_posix_acl), 1 }, - { "no_posix_acl", offsetof(struct lo_data, user_posix_acl), 0 }, - { "security_label", offsetof(struct lo_data, user_security_label), 1 }, - { "no_security_label", offsetof(struct lo_data, user_security_label), 0 }, - 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, - uint64_t mnt_id); -static int xattr_map_client(const struct lo_data *lo, const char *client_name, - char **out_name); - -#define FCHDIR_NOFAIL(fd) do { \ - int fchdir_res = fchdir(fd); \ - assert(fchdir_res == 0); \ - } while (0) - -static bool 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 bool is_safe_path_component(const char *path) -{ - if (strchr(path, '/')) { - return false; - } - - return !is_dot_or_dotdot(path); -} - -static bool is_empty(const char *name) -{ - return name[0] == '\0'; -} - -static struct lo_data *lo_data(fuse_req_t req) -{ - return (struct lo_data *)fuse_req_userdata(req); -} - -/* - * Tries to figure out if /proc//attr/fscreate is usable or not. With - * selinux=0, read from fscreate returns -EINVAL. - * - * TODO: Link with libselinux and use is_selinux_enabled() instead down - * the line. It probably will be more reliable indicator. - */ -static bool is_fscreate_usable(struct lo_data *lo) -{ - char procname[64]; - int fscreate_fd; - size_t bytes_read; - - sprintf(procname, "%ld/attr/fscreate", syscall(SYS_gettid)); - fscreate_fd = openat(lo->proc_self_task, procname, O_RDWR); - if (fscreate_fd == -1) { - return false; - } - - bytes_read = read(fscreate_fd, procname, 64); - close(fscreate_fd); - if (bytes_read == -1) { - return false; - } - return true; -} - -/* Helpers to set/reset fscreate */ -static int open_set_proc_fscreate(struct lo_data *lo, const void *ctx, - size_t ctxlen, int *fd) -{ - char procname[64]; - int fscreate_fd, err = 0; - size_t written; - - sprintf(procname, "%ld/attr/fscreate", syscall(SYS_gettid)); - fscreate_fd = openat(lo->proc_self_task, procname, O_WRONLY); - err = fscreate_fd == -1 ? errno : 0; - if (err) { - return err; - } - - written = write(fscreate_fd, ctx, ctxlen); - err = written == -1 ? errno : 0; - if (err) { - goto out; - } - - *fd = fscreate_fd; - return 0; -out: - close(fscreate_fd); - return err; -} - -static void close_reset_proc_fscreate(int fd) -{ - if ((write(fd, NULL, 0)) == -1) { - fuse_log(FUSE_LOG_WARNING, "Failed to reset fscreate. err=%d\n", errno); - } - close(fd); - return; -} - -/* - * 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; -} - -/* - * The host kernel normally drops security.capability xattr's on - * any write, however if we're remapping xattr names we need to drop - * whatever the clients security.capability is actually stored as. - */ -static int drop_security_capability(const struct lo_data *lo, int fd) -{ - if (!lo->xattr_security_capability) { - /* We didn't remap the name, let the host kernel do it */ - return 0; - } - if (!fremovexattr(fd, lo->xattr_security_capability)) { - /* All good */ - return 0; - } - - switch (errno) { - case ENODATA: - /* Attribute didn't exist, that's fine */ - return 0; - - case ENOTSUP: - /* FS didn't support attribute anyway, also fine */ - return 0; - - default: - /* Hmm other error */ - return errno; - } -} - -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) -{ - g_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 = g_try_realloc_n(map->elems, new_nelems, sizeof(map->elems[0])); - 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(struct lo_data *lo, int fd) -{ - struct lo_map_elem *elem; - - elem = lo_map_alloc_elem(&lo->fd_map); - if (!elem) { - return -1; - } - - elem->fd = fd; - return elem - lo->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; -} - -/* - * Open a file descriptor for an inode. Returns -EBADF if the inode is not a - * regular file or a directory. - * - * Use this helper function instead of raw openat(2) to prevent security issues - * when a malicious client opens special files such as block device nodes. - * Symlink inodes are also rejected since symlinks must already have been - * traversed on the client side. - */ -static int lo_inode_open(struct lo_data *lo, struct lo_inode *inode, - int open_flags) -{ - g_autofree char *fd_str = g_strdup_printf("%d", inode->fd); - int fd; - - if (!S_ISREG(inode->filetype) && !S_ISDIR(inode->filetype)) { - return -EBADF; - } - - /* - * The file is a symlink so O_NOFOLLOW must be ignored. We checked earlier - * that the inode is not a special file but if an external process races - * with us then symlinks are traversed here. It is not possible to escape - * the shared directory since it is mounted as "/" though. - */ - fd = openat(lo->proc_self_fd, fd_str, open_flags & ~O_NOFOLLOW); - if (fd < 0) { - return -errno; - } - 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; - } - - if (!(conn->capable & FUSE_CAP_SUBMOUNTS) && lo->announce_submounts) { - fuse_log(FUSE_LOG_WARNING, "lo_init: Cannot announce submounts, client " - "does not support it\n"); - lo->announce_submounts = false; - } - - if (lo->user_killpriv_v2 == 1) { - /* - * User explicitly asked for this option. Enable it unconditionally. - * If connection does not have this capability, it should fail - * in fuse_lowlevel.c - */ - fuse_log(FUSE_LOG_DEBUG, "lo_init: enabling killpriv_v2\n"); - conn->want |= FUSE_CAP_HANDLE_KILLPRIV_V2; - lo->killpriv_v2 = 1; - } else if (lo->user_killpriv_v2 == -1 && - conn->capable & FUSE_CAP_HANDLE_KILLPRIV_V2) { - /* - * User did not specify a value for killpriv_v2. By default enable it - * if connection offers this capability - */ - fuse_log(FUSE_LOG_DEBUG, "lo_init: enabling killpriv_v2\n"); - conn->want |= FUSE_CAP_HANDLE_KILLPRIV_V2; - lo->killpriv_v2 = 1; - } else { - /* - * Either user specified to disable killpriv_v2, or connection does - * not offer this capability. Disable killpriv_v2 in both the cases - */ - fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling killpriv_v2\n"); - conn->want &= ~FUSE_CAP_HANDLE_KILLPRIV_V2; - lo->killpriv_v2 = 0; - } - - if (lo->user_posix_acl == 1) { - /* - * User explicitly asked for this option. Enable it unconditionally. - * If connection does not have this capability, print error message - * now. It will fail later in fuse_lowlevel.c - */ - if (!(conn->capable & FUSE_CAP_POSIX_ACL) || - !(conn->capable & FUSE_CAP_DONT_MASK) || - !(conn->capable & FUSE_CAP_SETXATTR_EXT)) { - fuse_log(FUSE_LOG_ERR, "lo_init: Can not enable posix acl." - " kernel does not support FUSE_POSIX_ACL, FUSE_DONT_MASK" - " or FUSE_SETXATTR_EXT capability.\n"); - } else { - fuse_log(FUSE_LOG_DEBUG, "lo_init: enabling posix acl\n"); - } - - conn->want |= FUSE_CAP_POSIX_ACL | FUSE_CAP_DONT_MASK | - FUSE_CAP_SETXATTR_EXT; - lo->change_umask = true; - lo->posix_acl = true; - } else { - /* User either did not specify anything or wants it disabled */ - fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling posix_acl\n"); - conn->want &= ~FUSE_CAP_POSIX_ACL; - } - - if (lo->user_security_label == 1) { - if (!(conn->capable & FUSE_CAP_SECURITY_CTX)) { - fuse_log(FUSE_LOG_ERR, "lo_init: Can not enable security label." - " kernel does not support FUSE_SECURITY_CTX capability.\n"); - } - conn->want |= FUSE_CAP_SECURITY_CTX; - } else { - fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling security label\n"); - conn->want &= ~FUSE_CAP_SECURITY_CTX; - } -} - -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); -} - -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 = -1; - - 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) { - saverr = errno; - 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; - - saverr = drop_security_capability(lo, ifd); - if (saverr) { - goto out_err; - } - - res = fchownat(ifd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); - if (res == -1) { - saverr = errno; - goto out_err; - } - } - if (valid & FUSE_SET_ATTR_SIZE) { - int truncfd; - bool kill_suidgid; - bool cap_fsetid_dropped = false; - - kill_suidgid = lo->killpriv_v2 && (valid & FUSE_SET_ATTR_KILL_SUIDGID); - if (fi) { - truncfd = fd; - } else { - truncfd = lo_inode_open(lo, inode, O_RDWR); - if (truncfd < 0) { - saverr = -truncfd; - goto out_err; - } - } - - saverr = drop_security_capability(lo, truncfd); - if (saverr) { - if (!fi) { - close(truncfd); - } - goto out_err; - } - - if (kill_suidgid) { - res = drop_effective_cap("FSETID", &cap_fsetid_dropped); - if (res != 0) { - saverr = res; - if (!fi) { - close(truncfd); - } - goto out_err; - } - } - - res = ftruncate(truncfd, attr->st_size); - saverr = res == -1 ? errno : 0; - - if (cap_fsetid_dropped) { - if (gain_effective_cap("FSETID")) { - fuse_log(FUSE_LOG_ERR, "Failed to gain CAP_FSETID\n"); - } - } - if (!fi) { - close(truncfd); - } - 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 { - sprintf(procname, "%i", inode->fd); - res = utimensat(lo->proc_self_fd, procname, tv, 0); - } - if (res == -1) { - saverr = errno; - goto out_err; - } - } - lo_inode_put(lo, &inode); - - return lo_getattr(req, ino, fi); - -out_err: - lo_inode_put(lo, &inode); - fuse_reply_err(req, saverr); -} - -static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st, - uint64_t mnt_id) -{ - struct lo_inode *p; - struct lo_key key = { - .ino = st->st_ino, - .dev = st->st_dev, - .mnt_id = mnt_id, - }; - - 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); -} - -static int do_statx(struct lo_data *lo, int dirfd, const char *pathname, - struct stat *statbuf, int flags, uint64_t *mnt_id) -{ - int res; - -#if defined(CONFIG_STATX) && defined(CONFIG_STATX_MNT_ID) - if (lo->use_statx) { - struct statx statxbuf; - - res = statx(dirfd, pathname, flags, STATX_BASIC_STATS | STATX_MNT_ID, - &statxbuf); - if (!res) { - memset(statbuf, 0, sizeof(*statbuf)); - statbuf->st_dev = makedev(statxbuf.stx_dev_major, - statxbuf.stx_dev_minor); - statbuf->st_ino = statxbuf.stx_ino; - statbuf->st_mode = statxbuf.stx_mode; - statbuf->st_nlink = statxbuf.stx_nlink; - statbuf->st_uid = statxbuf.stx_uid; - statbuf->st_gid = statxbuf.stx_gid; - statbuf->st_rdev = makedev(statxbuf.stx_rdev_major, - statxbuf.stx_rdev_minor); - statbuf->st_size = statxbuf.stx_size; - statbuf->st_blksize = statxbuf.stx_blksize; - statbuf->st_blocks = statxbuf.stx_blocks; - statbuf->st_atim.tv_sec = statxbuf.stx_atime.tv_sec; - statbuf->st_atim.tv_nsec = statxbuf.stx_atime.tv_nsec; - statbuf->st_mtim.tv_sec = statxbuf.stx_mtime.tv_sec; - statbuf->st_mtim.tv_nsec = statxbuf.stx_mtime.tv_nsec; - statbuf->st_ctim.tv_sec = statxbuf.stx_ctime.tv_sec; - statbuf->st_ctim.tv_nsec = statxbuf.stx_ctime.tv_nsec; - - if (statxbuf.stx_mask & STATX_MNT_ID) { - *mnt_id = statxbuf.stx_mnt_id; - } else { - *mnt_id = 0; - } - return 0; - } else if (errno != ENOSYS) { - return -1; - } - lo->use_statx = false; - /* fallback */ - } -#endif - res = fstatat(dirfd, pathname, statbuf, flags); - if (res == -1) { - return -1; - } - *mnt_id = 0; - - return 0; -} - -/* - * Increments nlookup on the inode on success. unref_inode_lolocked() must be - * called eventually to decrement nlookup again. If inodep is non-NULL, the - * inode pointer is stored and the caller must call lo_inode_put(). - */ -static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - struct fuse_entry_param *e, - struct lo_inode **inodep) -{ - int newfd; - int res; - int saverr; - uint64_t mnt_id; - struct lo_data *lo = lo_data(req); - struct lo_inode *inode = NULL; - struct lo_inode *dir = lo_inode(req, parent); - - if (inodep) { - *inodep = NULL; /* in case there is an error */ - } - - /* - * 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 = do_statx(lo, newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, - &mnt_id); - if (res == -1) { - goto out_err; - } - - if (S_ISDIR(e->attr.st_mode) && lo->announce_submounts && - (e->attr.st_dev != dir->key.dev || mnt_id != dir->key.mnt_id)) { - e->attr_flags |= FUSE_ATTR_SUBMOUNT; - } - - inode = lo_find(lo, &e->attr, mnt_id); - 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; - inode->key.mnt_id = mnt_id; - if (lo->posix_lock) { - 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; - - /* Transfer ownership of inode pointer to caller or drop it */ - if (inodep) { - *inodep = inode; - } else { - 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); - - if (is_empty(name)) { - fuse_reply_err(req, ENOENT); - return; - } - - /* - * 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, NULL); - 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 - -static void drop_supplementary_groups(void) -{ - int ret; - - ret = getgroups(0, NULL); - if (ret == -1) { - fuse_log(FUSE_LOG_ERR, "getgroups() failed with error=%d:%s\n", - errno, strerror(errno)); - exit(1); - } - - if (!ret) { - return; - } - - /* Drop all supplementary groups. We should not need it */ - ret = setgroups(0, NULL); - if (ret == -1) { - fuse_log(FUSE_LOG_ERR, "setgroups() failed with error=%d:%s\n", - errno, strerror(errno)); - exit(1); - } -} - -/* - * 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, - bool change_umask) -{ - 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; - } - - if (change_umask) { - old->umask = umask(req->ctx.umask); - } - return 0; -} - -/* Regain Privileges */ -static void lo_restore_cred(struct lo_cred *old, bool restore_umask) -{ - 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); - } - - if (restore_umask) - umask(old->umask); -} - -/* - * A helper to change cred and drop capability. Returns 0 on success and - * errno on error - */ -static int lo_drop_cap_change_cred(fuse_req_t req, struct lo_cred *old, - bool change_umask, const char *cap_name, - bool *cap_dropped) -{ - int ret; - bool __cap_dropped; - - assert(cap_name); - - ret = drop_effective_cap(cap_name, &__cap_dropped); - if (ret) { - return ret; - } - - ret = lo_change_cred(req, old, change_umask); - if (ret) { - if (__cap_dropped) { - if (gain_effective_cap(cap_name)) { - fuse_log(FUSE_LOG_ERR, "Failed to gain CAP_%s\n", cap_name); - } - } - } - - if (cap_dropped) { - *cap_dropped = __cap_dropped; - } - return ret; -} - -static void lo_restore_cred_gain_cap(struct lo_cred *old, bool restore_umask, - const char *cap_name) -{ - assert(cap_name); - - lo_restore_cred(old, restore_umask); - - if (gain_effective_cap(cap_name)) { - fuse_log(FUSE_LOG_ERR, "Failed to gain CAP_%s\n", cap_name); - } -} - -static int do_mknod_symlink_secctx(fuse_req_t req, struct lo_inode *dir, - const char *name, const char *secctx_name) -{ - int path_fd, err; - char procname[64]; - struct lo_data *lo = lo_data(req); - - if (!req->secctx.ctxlen) { - return 0; - } - - /* Open newly created element with O_PATH */ - path_fd = openat(dir->fd, name, O_PATH | O_NOFOLLOW); - err = path_fd == -1 ? errno : 0; - if (err) { - return err; - } - sprintf(procname, "%i", path_fd); - FCHDIR_NOFAIL(lo->proc_self_fd); - /* Set security context. This is not atomic w.r.t file creation */ - err = setxattr(procname, secctx_name, req->secctx.ctx, req->secctx.ctxlen, - 0); - if (err) { - err = errno; - } - FCHDIR_NOFAIL(lo->root.fd); - close(path_fd); - return err; -} - -static int do_mknod_symlink(fuse_req_t req, struct lo_inode *dir, - const char *name, mode_t mode, dev_t rdev, - const char *link) -{ - int err, fscreate_fd = -1; - const char *secctx_name = req->secctx.name; - struct lo_cred old = {}; - struct lo_data *lo = lo_data(req); - char *mapped_name = NULL; - bool secctx_enabled = req->secctx.ctxlen; - bool do_fscreate = false; - - if (secctx_enabled && lo->xattrmap) { - err = xattr_map_client(lo, req->secctx.name, &mapped_name); - if (err < 0) { - return -err; - } - secctx_name = mapped_name; - } - - /* - * If security xattr has not been remapped and selinux is enabled on - * host, set fscreate and no need to do a setxattr() after file creation - */ - if (secctx_enabled && !mapped_name && lo->use_fscreate) { - do_fscreate = true; - err = open_set_proc_fscreate(lo, req->secctx.ctx, req->secctx.ctxlen, - &fscreate_fd); - if (err) { - goto out; - } - } - - err = lo_change_cred(req, &old, lo->change_umask && !S_ISLNK(mode)); - if (err) { - goto out; - } - - err = mknod_wrapper(dir->fd, name, link, mode, rdev); - err = err == -1 ? errno : 0; - lo_restore_cred(&old, lo->change_umask && !S_ISLNK(mode)); - if (err) { - goto out; - } - - if (!do_fscreate) { - err = do_mknod_symlink_secctx(req, dir, name, secctx_name); - if (err) { - unlinkat(dir->fd, name, S_ISDIR(mode) ? AT_REMOVEDIR : 0); - } - } -out: - if (fscreate_fd != -1) { - close_reset_proc_fscreate(fscreate_fd); - } - g_free(mapped_name); - return err; -} - -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 saverr; - struct lo_data *lo = lo_data(req); - struct lo_inode *dir; - struct fuse_entry_param e; - - if (is_empty(name)) { - fuse_reply_err(req, ENOENT); - return; - } - - 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 = do_mknod_symlink(req, dir, name, mode, rdev, link); - if (saverr) { - goto out; - } - - saverr = lo_do_lookup(req, parent, name, &e, NULL); - 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 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; - char procname[64]; - int saverr; - - if (is_empty(name)) { - fuse_reply_err(req, ENOENT); - return; - } - - 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; - - sprintf(procname, "%i", inode->fd); - res = linkat(lo->proc_self_fd, procname, parent_inode->fd, name, - AT_SYMLINK_FOLLOW); - 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; - uint64_t mnt_id; - struct stat attr; - struct lo_data *lo = lo_data(req); - struct lo_inode *dir = lo_inode(req, parent); - - if (!dir) { - return NULL; - } - - res = do_statx(lo, dir->fd, name, &attr, AT_SYMLINK_NOFOLLOW, &mnt_id); - lo_inode_put(lo, &dir); - if (res == -1) { - return NULL; - } - - return lo_find(lo, &attr, mnt_id); -} - -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_empty(name)) { - fuse_reply_err(req, ENOENT); - return; - } - - 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_empty(name) || is_empty(newname)) { - fuse_reply_err(req, ENOENT); - return; - } - - 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_empty(name)) { - fuse_reply_err(req, ENOENT); - return; - } - - 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 (lo->posix_lock) { - 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; - g_autofree 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 = g_try_malloc0(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, NULL); - 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); - } -} - -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, int allow_direct_io, - 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. Therefore, we discard it by default - * ('-o no_allow_direct_io'). If somebody needs that behavior, - * the '-o allow_direct_io' option should be set. - */ - if (!allow_direct_io) { - fi->flags &= ~O_DIRECT; - } -} - -/* - * Open a regular file, set up an fd mapping, and fill out the struct - * fuse_file_info for it. If existing_fd is not negative, use that fd instead - * opening a new one. Takes ownership of existing_fd. - * - * Returns 0 on success or a positive errno. - */ -static int lo_do_open(struct lo_data *lo, struct lo_inode *inode, - int existing_fd, struct fuse_file_info *fi) -{ - ssize_t fh; - int fd = existing_fd; - int err; - bool cap_fsetid_dropped = false; - bool kill_suidgid = lo->killpriv_v2 && fi->kill_priv; - - update_open_flags(lo->writeback, lo->allow_direct_io, fi); - - if (fd < 0) { - if (kill_suidgid) { - err = drop_effective_cap("FSETID", &cap_fsetid_dropped); - if (err) { - return err; - } - } - - fd = lo_inode_open(lo, inode, fi->flags); - - if (cap_fsetid_dropped) { - if (gain_effective_cap("FSETID")) { - fuse_log(FUSE_LOG_ERR, "Failed to gain CAP_FSETID\n"); - } - } - if (fd < 0) { - return -fd; - } - if (fi->flags & (O_TRUNC)) { - int err = drop_security_capability(lo, fd); - if (err) { - close(fd); - return err; - } - } - } - - pthread_mutex_lock(&lo->mutex); - fh = lo_add_fd_mapping(lo, fd); - pthread_mutex_unlock(&lo->mutex); - if (fh == -1) { - close(fd); - return ENOMEM; - } - - fi->fh = fh; - if (lo->cache == CACHE_NONE) { - fi->direct_io = 1; - } else if (lo->cache == CACHE_ALWAYS) { - fi->keep_cache = 1; - } - return 0; -} - -static int do_create_nosecctx(fuse_req_t req, struct lo_inode *parent_inode, - const char *name, mode_t mode, - struct fuse_file_info *fi, int *open_fd, - bool tmpfile) -{ - int err, fd; - struct lo_cred old = {}; - struct lo_data *lo = lo_data(req); - int flags; - - if (tmpfile) { - flags = fi->flags | O_TMPFILE; - /* - * Don't use O_EXCL as we want to link file later. Also reset O_CREAT - * otherwise openat() returns -EINVAL. - */ - flags &= ~(O_CREAT | O_EXCL); - - /* O_TMPFILE needs either O_RDWR or O_WRONLY */ - if ((flags & O_ACCMODE) == O_RDONLY) { - flags |= O_RDWR; - } - } else { - flags = fi->flags | O_CREAT | O_EXCL; - } - - err = lo_change_cred(req, &old, lo->change_umask); - if (err) { - return err; - } - - /* Try to create a new file but don't open existing files */ - fd = openat(parent_inode->fd, name, flags, mode); - err = fd == -1 ? errno : 0; - lo_restore_cred(&old, lo->change_umask); - if (!err) { - *open_fd = fd; - } - return err; -} - -static int do_create_secctx_fscreate(fuse_req_t req, - struct lo_inode *parent_inode, - const char *name, mode_t mode, - struct fuse_file_info *fi, int *open_fd) -{ - int err = 0, fd = -1, fscreate_fd = -1; - struct lo_data *lo = lo_data(req); - - err = open_set_proc_fscreate(lo, req->secctx.ctx, req->secctx.ctxlen, - &fscreate_fd); - if (err) { - return err; - } - - err = do_create_nosecctx(req, parent_inode, name, mode, fi, &fd, false); - - close_reset_proc_fscreate(fscreate_fd); - if (!err) { - *open_fd = fd; - } - return err; -} - -static int do_create_secctx_tmpfile(fuse_req_t req, - struct lo_inode *parent_inode, - const char *name, mode_t mode, - struct fuse_file_info *fi, - const char *secctx_name, int *open_fd) -{ - int err, fd = -1; - struct lo_data *lo = lo_data(req); - char procname[64]; - - err = do_create_nosecctx(req, parent_inode, ".", mode, fi, &fd, true); - if (err) { - return err; - } - - err = fsetxattr(fd, secctx_name, req->secctx.ctx, req->secctx.ctxlen, 0); - if (err) { - err = errno; - goto out; - } - - /* Security context set on file. Link it in place */ - sprintf(procname, "%d", fd); - FCHDIR_NOFAIL(lo->proc_self_fd); - err = linkat(AT_FDCWD, procname, parent_inode->fd, name, - AT_SYMLINK_FOLLOW); - err = err == -1 ? errno : 0; - FCHDIR_NOFAIL(lo->root.fd); - -out: - if (!err) { - *open_fd = fd; - } else if (fd != -1) { - close(fd); - } - return err; -} - -static int do_create_secctx_noatomic(fuse_req_t req, - struct lo_inode *parent_inode, - const char *name, mode_t mode, - struct fuse_file_info *fi, - const char *secctx_name, int *open_fd) -{ - int err = 0, fd = -1; - - err = do_create_nosecctx(req, parent_inode, name, mode, fi, &fd, false); - if (err) { - goto out; - } - - /* Set security context. This is not atomic w.r.t file creation */ - err = fsetxattr(fd, secctx_name, req->secctx.ctx, req->secctx.ctxlen, 0); - err = err == -1 ? errno : 0; -out: - if (!err) { - *open_fd = fd; - } else { - if (fd != -1) { - close(fd); - unlinkat(parent_inode->fd, name, 0); - } - } - return err; -} - -static int do_lo_create(fuse_req_t req, struct lo_inode *parent_inode, - const char *name, mode_t mode, - struct fuse_file_info *fi, int *open_fd) -{ - struct lo_data *lo = lo_data(req); - char *mapped_name = NULL; - int err; - const char *ctxname = req->secctx.name; - bool secctx_enabled = req->secctx.ctxlen; - - if (secctx_enabled && lo->xattrmap) { - err = xattr_map_client(lo, req->secctx.name, &mapped_name); - if (err < 0) { - return -err; - } - - ctxname = mapped_name; - } - - if (secctx_enabled) { - /* - * If security.selinux has not been remapped and selinux is enabled, - * use fscreate to set context before file creation. If not, use - * tmpfile method for regular files. Otherwise fallback to - * non-atomic method of file creation and xattr settting. - */ - if (!mapped_name && lo->use_fscreate) { - err = do_create_secctx_fscreate(req, parent_inode, name, mode, fi, - open_fd); - goto out; - } else if (S_ISREG(mode)) { - err = do_create_secctx_tmpfile(req, parent_inode, name, mode, fi, - ctxname, open_fd); - /* - * If filesystem does not support O_TMPFILE, fallback to non-atomic - * method. - */ - if (!err || err != EOPNOTSUPP) { - goto out; - } - } - - err = do_create_secctx_noatomic(req, parent_inode, name, mode, fi, - ctxname, open_fd); - } else { - err = do_create_nosecctx(req, parent_inode, name, mode, fi, open_fd, - false); - } - -out: - g_free(mapped_name); - return err; -} - -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 = -1; - struct lo_data *lo = lo_data(req); - struct lo_inode *parent_inode; - struct lo_inode *inode = NULL; - struct fuse_entry_param e; - int err; - - fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)" - " kill_priv=%d\n", parent, name, fi->kill_priv); - - 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; - } - - update_open_flags(lo->writeback, lo->allow_direct_io, fi); - - err = do_lo_create(req, parent_inode, name, mode, fi, &fd); - - /* Ignore the error if file exists and O_EXCL was not given */ - if (err && (err != EEXIST || (fi->flags & O_EXCL))) { - goto out; - } - - err = lo_do_lookup(req, parent, name, &e, &inode); - if (err) { - goto out; - } - - err = lo_do_open(lo, inode, fd, fi); - fd = -1; /* lo_do_open() takes ownership of fd */ - if (err) { - /* Undo lo_do_lookup() nlookup ref */ - unref_inode_lolocked(lo, inode, 1); - } - -out: - lo_inode_put(lo, &inode); - lo_inode_put(lo, &parent_inode); - - if (err) { - if (fd >= 0) { - close(fd); - } - - 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; - 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. */ - /* TODO: What if file is not writable? */ - fd = lo_inode_open(lo, inode, O_RDWR); - if (fd < 0) { - *err = -fd; - 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%" PRIx64 ", l_type=%d l_start=0x%" PRIx64 - " l_len=0x%" PRIx64 "\n", - ino, fi->flags, fi->lock_owner, lock->l_type, - (uint64_t)lock->l_start, (uint64_t)lock->l_len); - - if (!lo->posix_lock) { - fuse_reply_err(req, ENOSYS); - 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; - } - - 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%" PRIx64 " sleep=%d l_whence=%d" - " l_start=0x%" PRIx64 " l_len=0x%" PRIx64 "\n", - ino, fi->flags, lock->l_type, lock->l_pid, fi->lock_owner, sleep, - lock->l_whence, (uint64_t)lock->l_start, (uint64_t)lock->l_len); - - if (!lo->posix_lock) { - fuse_reply_err(req, ENOSYS); - return; - } - - 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) -{ - struct lo_data *lo = lo_data(req); - struct lo_inode *inode = lo_inode(req, ino); - int err; - - fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d, kill_priv=%d)" - "\n", ino, fi->flags, fi->kill_priv); - - if (!inode) { - fuse_reply_err(req, EBADF); - return; - } - - err = lo_do_open(lo, inode, -1, fi); - lo_inode_put(lo, &inode); - if (err) { - fuse_reply_err(req, err); - } else { - 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; - struct lo_data *lo = lo_data(req); - - inode = lo_inode(req, ino); - if (!inode) { - fuse_reply_err(req, EBADF); - return; - } - - if (!S_ISREG(inode->filetype)) { - lo_inode_put(lo, &inode); - fuse_reply_err(req, EBADF); - return; - } - - /* An fd is going away. Cleanup associated posix locks */ - if (lo->posix_lock) { - 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, &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) -{ - struct lo_inode *inode = lo_inode(req, ino); - struct lo_data *lo = lo_data(req); - int res; - int fd; - - fuse_log(FUSE_LOG_DEBUG, "lo_fsync(ino=%" PRIu64 ", fi=0x%p)\n", ino, - (void *)fi); - - if (!inode) { - fuse_reply_err(req, EBADF); - return; - } - - if (!fi) { - fd = lo_inode_open(lo, inode, O_RDWR); - if (fd < 0) { - res = -fd; - goto out; - } - } else { - fd = lo_fi_fd(req, fi); - } - - if (datasync) { - res = fdatasync(fd) == -1 ? errno : 0; - } else { - res = fsync(fd) == -1 ? errno : 0; - } - if (!fi) { - close(fd); - } -out: - lo_inode_put(lo, &inode); - fuse_reply_err(req, res); -} - -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 kill_priv=%d)\n", - ino, out_buf.buf[0].size, (unsigned long)off, fi->kill_priv); - - res = drop_security_capability(lo_data(req), out_buf.buf[0].fd); - if (res) { - fuse_reply_err(req, res); - return; - } - - /* - * If kill_priv is set, drop CAP_FSETID which should lead to kernel - * clearing setuid/setgid on file. Note, for WRITE, we need to do - * this even if killpriv_v2 is not enabled. fuse direct write path - * relies on this. - */ - 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; - - if (!(op & LOCK_NB)) { - /* - * Blocking flock can deadlock as there is only one thread - * serving the queue. - */ - fuse_reply_err(req, EOPNOTSUPP); - return; - } - - res = flock(lo_fi_fd(req, fi), op); - - fuse_reply_err(req, res == -1 ? errno : 0); -} - -/* types */ -/* - * Exit; process attribute unmodified if matched. - * An empty key applies to all. - */ -#define XATTR_MAP_FLAG_OK (1 << 0) -/* - * The attribute is unwanted; - * EPERM on write, hidden on read. - */ -#define XATTR_MAP_FLAG_BAD (1 << 1) -/* - * For attr that start with 'key' prepend 'prepend' - * 'key' may be empty to prepend for all attrs - * key is defined from set/remove point of view. - * Automatically reversed on read - */ -#define XATTR_MAP_FLAG_PREFIX (1 << 2) -/* - * The attribute is unsupported; - * ENOTSUP on write, hidden on read. - */ -#define XATTR_MAP_FLAG_UNSUPPORTED (1 << 3) - -/* scopes */ -/* Apply rule to get/set/remove */ -#define XATTR_MAP_FLAG_CLIENT (1 << 16) -/* Apply rule to list */ -#define XATTR_MAP_FLAG_SERVER (1 << 17) -/* Apply rule to all */ -#define XATTR_MAP_FLAG_ALL (XATTR_MAP_FLAG_SERVER | XATTR_MAP_FLAG_CLIENT) - -static void add_xattrmap_entry(struct lo_data *lo, - const XattrMapEntry *new_entry) -{ - XattrMapEntry *res = g_realloc_n(lo->xattr_map_list, - lo->xattr_map_nentries + 1, - sizeof(XattrMapEntry)); - res[lo->xattr_map_nentries++] = *new_entry; - - lo->xattr_map_list = res; -} - -static void free_xattrmap(struct lo_data *lo) -{ - XattrMapEntry *map = lo->xattr_map_list; - size_t i; - - if (!map) { - return; - } - - for (i = 0; i < lo->xattr_map_nentries; i++) { - g_free(map[i].key); - g_free(map[i].prepend); - }; - - g_free(map); - lo->xattr_map_list = NULL; - lo->xattr_map_nentries = -1; -} - -/* - * Handle the 'map' type, which is sugar for a set of commands - * for the common case of prefixing a subset or everything, - * and allowing anything not prefixed through. - * It must be the last entry in the stream, although there - * can be other entries before it. - * The form is: - * :map:key:prefix: - * - * key maybe empty in which case all entries are prefixed. - */ -static void parse_xattrmap_map(struct lo_data *lo, - const char *rule, char sep) -{ - const char *tmp; - char *key; - char *prefix; - XattrMapEntry tmp_entry; - - if (*rule != sep) { - fuse_log(FUSE_LOG_ERR, - "%s: Expecting '%c' after 'map' keyword, found '%c'\n", - __func__, sep, *rule); - exit(1); - } - - rule++; - - /* At start of 'key' field */ - tmp = strchr(rule, sep); - if (!tmp) { - fuse_log(FUSE_LOG_ERR, - "%s: Missing '%c' at end of key field in map rule\n", - __func__, sep); - exit(1); - } - - key = g_strndup(rule, tmp - rule); - rule = tmp + 1; - - /* At start of prefix field */ - tmp = strchr(rule, sep); - if (!tmp) { - fuse_log(FUSE_LOG_ERR, - "%s: Missing '%c' at end of prefix field in map rule\n", - __func__, sep); - exit(1); - } - - prefix = g_strndup(rule, tmp - rule); - rule = tmp + 1; - - /* - * This should be the end of the string, we don't allow - * any more commands after 'map'. - */ - if (*rule) { - fuse_log(FUSE_LOG_ERR, - "%s: Expecting end of command after map, found '%c'\n", - __func__, *rule); - exit(1); - } - - /* 1st: Prefix matches/everything */ - tmp_entry.flags = XATTR_MAP_FLAG_PREFIX | XATTR_MAP_FLAG_ALL; - tmp_entry.key = g_strdup(key); - tmp_entry.prepend = g_strdup(prefix); - add_xattrmap_entry(lo, &tmp_entry); - - if (!*key) { - /* Prefix all case */ - - /* 2nd: Hide any non-prefixed entries on the host */ - tmp_entry.flags = XATTR_MAP_FLAG_BAD | XATTR_MAP_FLAG_ALL; - tmp_entry.key = g_strdup(""); - tmp_entry.prepend = g_strdup(""); - add_xattrmap_entry(lo, &tmp_entry); - } else { - /* Prefix matching case */ - - /* 2nd: Hide non-prefixed but matching entries on the host */ - tmp_entry.flags = XATTR_MAP_FLAG_BAD | XATTR_MAP_FLAG_SERVER; - tmp_entry.key = g_strdup(""); /* Not used */ - tmp_entry.prepend = g_strdup(key); - add_xattrmap_entry(lo, &tmp_entry); - - /* 3rd: Stop the client accessing prefixed attributes directly */ - tmp_entry.flags = XATTR_MAP_FLAG_BAD | XATTR_MAP_FLAG_CLIENT; - tmp_entry.key = g_strdup(prefix); - tmp_entry.prepend = g_strdup(""); /* Not used */ - add_xattrmap_entry(lo, &tmp_entry); - - /* 4th: Everything else is OK */ - tmp_entry.flags = XATTR_MAP_FLAG_OK | XATTR_MAP_FLAG_ALL; - tmp_entry.key = g_strdup(""); - tmp_entry.prepend = g_strdup(""); - add_xattrmap_entry(lo, &tmp_entry); - } - - g_free(key); - g_free(prefix); -} - -static void parse_xattrmap(struct lo_data *lo) -{ - const char *map = lo->xattrmap; - const char *tmp; - int ret; - - lo->xattr_map_nentries = 0; - while (*map) { - XattrMapEntry tmp_entry; - char sep; - - if (isspace(*map)) { - map++; - continue; - } - /* The separator is the first non-space of the rule */ - sep = *map++; - if (!sep) { - break; - } - - tmp_entry.flags = 0; - /* Start of 'type' */ - if (strstart(map, "prefix", &map)) { - tmp_entry.flags |= XATTR_MAP_FLAG_PREFIX; - } else if (strstart(map, "ok", &map)) { - tmp_entry.flags |= XATTR_MAP_FLAG_OK; - } else if (strstart(map, "bad", &map)) { - tmp_entry.flags |= XATTR_MAP_FLAG_BAD; - } else if (strstart(map, "unsupported", &map)) { - tmp_entry.flags |= XATTR_MAP_FLAG_UNSUPPORTED; - } else if (strstart(map, "map", &map)) { - /* - * map is sugar that adds a number of rules, and must be - * the last entry. - */ - parse_xattrmap_map(lo, map, sep); - break; - } else { - fuse_log(FUSE_LOG_ERR, - "%s: Unexpected type;" - "Expecting 'prefix', 'ok', 'bad', 'unsupported' or 'map'" - " in rule %zu\n", __func__, lo->xattr_map_nentries); - exit(1); - } - - if (*map++ != sep) { - fuse_log(FUSE_LOG_ERR, - "%s: Missing '%c' at end of type field of rule %zu\n", - __func__, sep, lo->xattr_map_nentries); - exit(1); - } - - /* Start of 'scope' */ - if (strstart(map, "client", &map)) { - tmp_entry.flags |= XATTR_MAP_FLAG_CLIENT; - } else if (strstart(map, "server", &map)) { - tmp_entry.flags |= XATTR_MAP_FLAG_SERVER; - } else if (strstart(map, "all", &map)) { - tmp_entry.flags |= XATTR_MAP_FLAG_ALL; - } else { - fuse_log(FUSE_LOG_ERR, - "%s: Unexpected scope;" - " Expecting 'client', 'server', or 'all', in rule %zu\n", - __func__, lo->xattr_map_nentries); - exit(1); - } - - if (*map++ != sep) { - fuse_log(FUSE_LOG_ERR, - "%s: Expecting '%c' found '%c'" - " after scope in rule %zu\n", - __func__, sep, *map, lo->xattr_map_nentries); - exit(1); - } - - /* At start of 'key' field */ - tmp = strchr(map, sep); - if (!tmp) { - fuse_log(FUSE_LOG_ERR, - "%s: Missing '%c' at end of key field of rule %zu", - __func__, sep, lo->xattr_map_nentries); - exit(1); - } - tmp_entry.key = g_strndup(map, tmp - map); - map = tmp + 1; - - /* At start of 'prepend' field */ - tmp = strchr(map, sep); - if (!tmp) { - fuse_log(FUSE_LOG_ERR, - "%s: Missing '%c' at end of prepend field of rule %zu", - __func__, sep, lo->xattr_map_nentries); - exit(1); - } - tmp_entry.prepend = g_strndup(map, tmp - map); - map = tmp + 1; - - add_xattrmap_entry(lo, &tmp_entry); - /* End of rule - go around again for another rule */ - } - - if (!lo->xattr_map_nentries) { - fuse_log(FUSE_LOG_ERR, "Empty xattr map\n"); - exit(1); - } - - ret = xattr_map_client(lo, "security.capability", - &lo->xattr_security_capability); - if (ret) { - fuse_log(FUSE_LOG_ERR, "Failed to map security.capability: %s\n", - strerror(ret)); - exit(1); - } - if (!lo->xattr_security_capability || - !strcmp(lo->xattr_security_capability, "security.capability")) { - /* 1-1 mapping, don't need to do anything */ - free(lo->xattr_security_capability); - lo->xattr_security_capability = NULL; - } -} - -/* - * For use with getxattr/setxattr/removexattr, where the client - * gives us a name and we may need to choose a different one. - * Allocates a buffer for the result placing it in *out_name. - * If there's no change then *out_name is not set. - * Returns 0 on success - * Can return -EPERM to indicate we block a given attribute - * (in which case out_name is not allocated) - * Can return -ENOMEM to indicate out_name couldn't be allocated. - */ -static int xattr_map_client(const struct lo_data *lo, const char *client_name, - char **out_name) -{ - size_t i; - for (i = 0; i < lo->xattr_map_nentries; i++) { - const XattrMapEntry *cur_entry = lo->xattr_map_list + i; - - if ((cur_entry->flags & XATTR_MAP_FLAG_CLIENT) && - (strstart(client_name, cur_entry->key, NULL))) { - if (cur_entry->flags & XATTR_MAP_FLAG_BAD) { - return -EPERM; - } - if (cur_entry->flags & XATTR_MAP_FLAG_UNSUPPORTED) { - return -ENOTSUP; - } - if (cur_entry->flags & XATTR_MAP_FLAG_OK) { - /* Unmodified name */ - return 0; - } - if (cur_entry->flags & XATTR_MAP_FLAG_PREFIX) { - *out_name = g_try_malloc(strlen(client_name) + - strlen(cur_entry->prepend) + 1); - if (!*out_name) { - return -ENOMEM; - } - sprintf(*out_name, "%s%s", cur_entry->prepend, client_name); - return 0; - } - } - } - - return -EPERM; -} - -/* - * For use with listxattr where the server fs gives us a name and we may need - * to sanitize this for the client. - * Returns a pointer to the result in *out_name - * This is always the original string or the current string with some prefix - * removed; no reallocation is done. - * Returns 0 on success - * Can return -ENODATA to indicate the name should be dropped from the list. - */ -static int xattr_map_server(const struct lo_data *lo, const char *server_name, - const char **out_name) -{ - size_t i; - const char *end; - - for (i = 0; i < lo->xattr_map_nentries; i++) { - const XattrMapEntry *cur_entry = lo->xattr_map_list + i; - - if ((cur_entry->flags & XATTR_MAP_FLAG_SERVER) && - (strstart(server_name, cur_entry->prepend, &end))) { - if (cur_entry->flags & XATTR_MAP_FLAG_BAD || - cur_entry->flags & XATTR_MAP_FLAG_UNSUPPORTED) { - return -ENODATA; - } - if (cur_entry->flags & XATTR_MAP_FLAG_OK) { - *out_name = server_name; - return 0; - } - if (cur_entry->flags & XATTR_MAP_FLAG_PREFIX) { - /* Remove prefix */ - *out_name = end; - return 0; - } - } - } - - return -ENODATA; -} - -static bool block_xattr(struct lo_data *lo, const char *name) -{ - /* - * If user explicitly enabled posix_acl or did not provide any option, - * do not block acl. Otherwise block system.posix_acl_access and - * system.posix_acl_default xattrs. - */ - if (lo->user_posix_acl) { - return false; - } - if (!strcmp(name, "system.posix_acl_access") || - !strcmp(name, "system.posix_acl_default")) - return true; - - return false; -} - -/* - * Returns number of bytes in xattr_list after filtering on success. This - * could be zero as well if nothing is left after filtering. - * - * Returns negative error code on failure. - * xattr_list is modified in place. - */ -static int remove_blocked_xattrs(struct lo_data *lo, char *xattr_list, - unsigned in_size) -{ - size_t out_index, in_index; - - /* - * As of now we only filter out acl xattrs. If acls are enabled or - * they have not been explicitly disabled, there is nothing to - * filter. - */ - if (lo->user_posix_acl) { - return in_size; - } - - out_index = 0; - in_index = 0; - while (in_index < in_size) { - char *in_ptr = xattr_list + in_index; - - /* Length of current attribute name */ - size_t in_len = strlen(xattr_list + in_index) + 1; - - if (!block_xattr(lo, in_ptr)) { - if (in_index != out_index) { - memmove(xattr_list + out_index, xattr_list + in_index, in_len); - } - out_index += in_len; - } - in_index += in_len; - } - return out_index; -} - -static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, - size_t size) -{ - struct lo_data *lo = lo_data(req); - g_autofree char *value = NULL; - char procname[64]; - const char *name; - char *mapped_name; - struct lo_inode *inode; - ssize_t ret; - int saverr; - int fd = -1; - - if (block_xattr(lo, in_name)) { - fuse_reply_err(req, EOPNOTSUPP); - return; - } - - mapped_name = NULL; - name = in_name; - if (lo->xattrmap) { - ret = xattr_map_client(lo, in_name, &mapped_name); - if (ret < 0) { - if (ret == -EPERM) { - ret = -ENODATA; - } - fuse_reply_err(req, -ret); - return; - } - if (mapped_name) { - name = mapped_name; - } - } - - inode = lo_inode(req, ino); - if (!inode) { - fuse_reply_err(req, EBADF); - g_free(mapped_name); - 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 = g_try_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); - saverr = ret == -1 ? errno : 0; - } else { - /* fchdir should not fail here */ - FCHDIR_NOFAIL(lo->proc_self_fd); - ret = getxattr(procname, name, value, size); - saverr = ret == -1 ? errno : 0; - FCHDIR_NOFAIL(lo->root.fd); - } - - if (ret == -1) { - goto out; - } - if (size) { - saverr = 0; - if (ret == 0) { - goto out; - } - fuse_reply_buf(req, value, ret); - } else { - fuse_reply_xattr(req, ret); - } -out_free: - if (fd >= 0) { - close(fd); - } - - lo_inode_put(lo, &inode); - return; - -out_err: - saverr = errno; -out: - fuse_reply_err(req, saverr); - g_free(mapped_name); - 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); - g_autofree 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 = g_try_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); - saverr = ret == -1 ? errno : 0; - } else { - /* fchdir should not fail here */ - FCHDIR_NOFAIL(lo->proc_self_fd); - ret = listxattr(procname, value, size); - saverr = ret == -1 ? errno : 0; - FCHDIR_NOFAIL(lo->root.fd); - } - - if (ret == -1) { - goto out; - } - if (size) { - saverr = 0; - if (ret == 0) { - goto out; - } - - if (lo->xattr_map_list) { - /* - * Map the names back, some attributes might be dropped, - * some shortened, but not increased, so we shouldn't - * run out of room. - */ - size_t out_index, in_index; - out_index = 0; - in_index = 0; - while (in_index < ret) { - const char *map_out; - char *in_ptr = value + in_index; - /* Length of current attribute name */ - size_t in_len = strlen(value + in_index) + 1; - - int mapret = xattr_map_server(lo, in_ptr, &map_out); - if (mapret != -ENODATA && mapret != 0) { - /* Shouldn't happen */ - saverr = -mapret; - goto out; - } - if (mapret == 0) { - /* Either unchanged, or truncated */ - size_t out_len; - if (map_out != in_ptr) { - /* +1 copies the NIL */ - out_len = strlen(map_out) + 1; - } else { - /* No change */ - out_len = in_len; - } - /* - * Move result along, may still be needed for an unchanged - * entry if a previous entry was changed. - */ - memmove(value + out_index, map_out, out_len); - - out_index += out_len; - } - in_index += in_len; - } - ret = out_index; - if (ret == 0) { - goto out; - } - } - - ret = remove_blocked_xattrs(lo, value, ret); - if (ret <= 0) { - saverr = -ret; - goto out; - } - fuse_reply_buf(req, value, ret); - } else { - /* - * xattrmap only ever shortens the result, - * so we don't need to do anything clever with the - * allocation length here. - */ - fuse_reply_xattr(req, ret); - } -out_free: - 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 *in_name, - const char *value, size_t size, int flags, - uint32_t extra_flags) -{ - char procname[64]; - const char *name; - char *mapped_name; - struct lo_data *lo = lo_data(req); - struct lo_inode *inode; - ssize_t ret; - int saverr; - int fd = -1; - bool switched_creds = false; - bool cap_fsetid_dropped = false; - struct lo_cred old = {}; - - if (block_xattr(lo, in_name)) { - fuse_reply_err(req, EOPNOTSUPP); - return; - } - - mapped_name = NULL; - name = in_name; - if (lo->xattrmap) { - ret = xattr_map_client(lo, in_name, &mapped_name); - if (ret < 0) { - fuse_reply_err(req, -ret); - return; - } - if (mapped_name) { - name = mapped_name; - } - } - - inode = lo_inode(req, ino); - if (!inode) { - fuse_reply_err(req, EBADF); - g_free(mapped_name); - 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 we are setting posix access acl and if SGID needs to be - * cleared, then switch to caller's gid and drop CAP_FSETID - * and that should make sure host kernel clears SGID. - * - * This probably will not work when we support idmapped mounts. - * In that case we will need to find a non-root gid and switch - * to it. (Instead of gid in request). Fix it when we support - * idmapped mounts. - */ - if (lo->posix_acl && !strcmp(name, "system.posix_acl_access") - && (extra_flags & FUSE_SETXATTR_ACL_KILL_SGID)) { - ret = lo_drop_cap_change_cred(req, &old, false, "FSETID", - &cap_fsetid_dropped); - if (ret) { - saverr = ret; - goto out; - } - switched_creds = true; - } - 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); - saverr = ret == -1 ? errno : 0; - } else { - /* fchdir should not fail here */ - FCHDIR_NOFAIL(lo->proc_self_fd); - ret = setxattr(procname, name, value, size, flags); - saverr = ret == -1 ? errno : 0; - FCHDIR_NOFAIL(lo->root.fd); - } - if (switched_creds) { - if (cap_fsetid_dropped) - lo_restore_cred_gain_cap(&old, false, "FSETID"); - else - lo_restore_cred(&old, false); - } - -out: - if (fd >= 0) { - close(fd); - } - - lo_inode_put(lo, &inode); - g_free(mapped_name); - fuse_reply_err(req, saverr); -} - -static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *in_name) -{ - char procname[64]; - const char *name; - char *mapped_name; - struct lo_data *lo = lo_data(req); - struct lo_inode *inode; - ssize_t ret; - int saverr; - int fd = -1; - - if (block_xattr(lo, in_name)) { - fuse_reply_err(req, EOPNOTSUPP); - return; - } - - mapped_name = NULL; - name = in_name; - if (lo->xattrmap) { - ret = xattr_map_client(lo, in_name, &mapped_name); - if (ret < 0) { - fuse_reply_err(req, -ret); - return; - } - if (mapped_name) { - name = mapped_name; - } - } - - inode = lo_inode(req, ino); - if (!inode) { - fuse_reply_err(req, EBADF); - g_free(mapped_name); - 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); - saverr = ret == -1 ? errno : 0; - } else { - /* fchdir should not fail here */ - FCHDIR_NOFAIL(lo->proc_self_fd); - ret = removexattr(procname, name); - saverr = ret == -1 ? errno : 0; - FCHDIR_NOFAIL(lo->root.fd); - } - -out: - if (fd >= 0) { - close(fd); - } - - lo_inode_put(lo, &inode); - g_free(mapped_name); - 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=%ju, ino=%" PRIu64 "/fd=%d, " - "off=%ju, size=%zd, flags=0x%x)\n", - ino_in, in_fd, (intmax_t)off_in, - ino_out, out_fd, (intmax_t)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 int lo_do_syncfs(struct lo_data *lo, struct lo_inode *inode) -{ - int fd, ret = 0; - - fuse_log(FUSE_LOG_DEBUG, "lo_do_syncfs(ino=%" PRIu64 ")\n", - inode->fuse_ino); - - fd = lo_inode_open(lo, inode, O_RDONLY); - if (fd < 0) { - return -fd; - } - - if (syncfs(fd) < 0) { - ret = errno; - } - - close(fd); - return ret; -} - -static void lo_syncfs(fuse_req_t req, fuse_ino_t ino) -{ - struct lo_data *lo = lo_data(req); - struct lo_inode *inode = lo_inode(req, ino); - int err; - - if (!inode) { - fuse_reply_err(req, EBADF); - return; - } - - err = lo_do_syncfs(lo, inode); - lo_inode_put(lo, &inode); - - /* - * If submounts aren't announced, the client only sends a request to - * sync the root inode. TODO: Track submounts internally and iterate - * over them as well. - */ - - fuse_reply_err(req, err); -} - -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, - .syncfs = lo_syncfs, - .destroy = lo_destroy, -}; - -/* Print vhost-user.json backend program capabilities */ -static void print_capabilities(void) -{ - printf("{\n"); - printf(" \"type\": \"fs\"\n"); - printf("}\n"); -} - -/* - * Drop all Linux capabilities because the wait parent process only needs to - * sit in waitpid(2) and terminate. - */ -static void setup_wait_parent_capabilities(void) -{ - capng_setpid(syscall(SYS_gettid)); - capng_clear(CAPNG_SELECT_BOTH); - capng_apply(CAPNG_SELECT_BOTH); -} - -/* - * 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; - - setup_wait_parent_capabilities(); - - /* 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); - } - - /* Get the /proc/self/task descriptor */ - lo->proc_self_task = open("/proc/self/task/", O_PATH); - if (lo->proc_self_task == -1) { - fuse_log(FUSE_LOG_ERR, "open(/proc/self/task, O_PATH): %m\n"); - exit(1); - } - - lo->use_fscreate = is_fscreate_usable(lo); - - /* - * We only need /proc/self/fd. Prevent ".." from accessing parent - * directories of /proc/self/fd by bind-mounting it over /proc. Since / was - * previously remounted with MS_REC | MS_SLAVE this mount change only - * affects our process. - */ - if (mount("/proc/self/fd", "/proc", NULL, MS_BIND, NULL) < 0) { - fuse_log(FUSE_LOG_ERR, "mount(/proc/self/fd, MS_BIND): %m\n"); - exit(1); - } - - /* Get the /proc (actually /proc/self/fd, see above) file descriptor */ - lo->proc_self_fd = open("/proc", O_PATH); - if (lo->proc_self_fd == -1) { - fuse_log(FUSE_LOG_ERR, "open(/proc, 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 | MS_REC, 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); -} - -/* - * Only keep capabilities in allowlist that are needed for file system operation - * The (possibly NULL) modcaps_in string passed in is free'd before exit. - */ -static void setup_capabilities(char *modcaps_in) -{ - char *modcaps = modcaps_in; - pthread_mutex_lock(&cap.mutex); - capng_restore_state(&cap.saved); - - /* - * Add to allowlist file system-related capabilities that are needed for a - * file server to act like root. Drop everything else like networking and - * sysadmin capabilities. - * - * Exclusions: - * 1. CAP_LINUX_IMMUTABLE is not included because it's only used via ioctl - * and we don't support that. - * 2. CAP_MAC_OVERRIDE is not included because it only seems to be - * used by the Smack LSM. Omit it until there is demand for it. - */ - capng_setpid(syscall(SYS_gettid)); - capng_clear(CAPNG_SELECT_BOTH); - if (capng_updatev(CAPNG_ADD, CAPNG_PERMITTED | CAPNG_EFFECTIVE, - CAP_CHOWN, - CAP_DAC_OVERRIDE, - CAP_FOWNER, - CAP_FSETID, - CAP_SETGID, - CAP_SETUID, - CAP_MKNOD, - CAP_SETFCAP, - -1)) { - fuse_log(FUSE_LOG_ERR, "%s: capng_updatev failed\n", __func__); - exit(1); - } - - /* - * The modcaps option is a colon separated list of caps, - * each preceded by either + or -. - */ - while (modcaps) { - capng_act_t action; - int cap; - - char *next = strchr(modcaps, ':'); - if (next) { - *next = '\0'; - next++; - } - - switch (modcaps[0]) { - case '+': - action = CAPNG_ADD; - break; - - case '-': - action = CAPNG_DROP; - break; - - default: - fuse_log(FUSE_LOG_ERR, - "%s: Expecting '+'/'-' in modcaps but found '%c'\n", - __func__, modcaps[0]); - exit(1); - } - cap = capng_name_to_capability(modcaps + 1); - if (cap < 0) { - fuse_log(FUSE_LOG_ERR, "%s: Unknown capability '%s'\n", __func__, - modcaps); - exit(1); - } - if (capng_update(action, CAPNG_PERMITTED | CAPNG_EFFECTIVE, cap)) { - fuse_log(FUSE_LOG_ERR, "%s: capng_update failed for '%s'\n", - __func__, modcaps); - exit(1); - } - - modcaps = next; - } - g_free(modcaps_in); - - if (capng_apply(CAPNG_SELECT_BOTH)) { - fuse_log(FUSE_LOG_ERR, "%s: capng_apply failed\n", __func__); - exit(1); - } - - cap.saved = capng_save_state(); - if (!cap.saved) { - fuse_log(FUSE_LOG_ERR, "%s: capng_save_state failed\n", __func__); - exit(1); - } - pthread_mutex_unlock(&cap.mutex); -} - -/* - * Use chroot as a weaker sandbox for environments where the process is - * launched without CAP_SYS_ADMIN. - */ -static void setup_chroot(struct lo_data *lo) -{ - 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); - } - - lo->proc_self_task = open("/proc/self/task", O_PATH); - if (lo->proc_self_fd == -1) { - fuse_log(FUSE_LOG_ERR, "open(\"/proc/self/task\", O_PATH): %m\n"); - exit(1); - } - - lo->use_fscreate = is_fscreate_usable(lo); - - /* - * Make the shared directory the file system root so that FUSE_OPEN - * (lo_open()) cannot escape the shared directory by opening a symlink. - * - * The chroot(2) syscall is later disabled by seccomp and the - * CAP_SYS_CHROOT capability is dropped so that tampering with the chroot - * is not possible. - * - * However, it's still possible to escape the chroot via lo->proc_self_fd - * but that requires first gaining control of the process. - */ - if (chroot(lo->source) != 0) { - fuse_log(FUSE_LOG_ERR, "chroot(\"%s\"): %m\n", lo->source); - exit(1); - } - - /* Move into the chroot */ - if (chdir("/") != 0) { - fuse_log(FUSE_LOG_ERR, "chdir(\"/\"): %m\n"); - exit(1); - } -} - -/* - * 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) -{ - if (lo->sandbox == SANDBOX_NAMESPACE) { - setup_namespaces(lo, se); - setup_mounts(lo->source); - } else { - setup_chroot(lo); - } - - setup_seccomp(enable_syslog); - setup_capabilities(g_strdup(lo->modcaps)); -} - -/* Set the maximum number of open file descriptors */ -static void setup_nofile_rlimit(unsigned long rlimit_nofile) -{ - struct rlimit rlim = { - .rlim_cur = rlimit_nofile, - .rlim_max = rlimit_nofile, - }; - - if (rlimit_nofile == 0) { - return; /* nothing to do */ - } - - 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) { - /* no timestamp needed */ - localfmt = g_strdup_printf("[ID: %08ld] %s", syscall(__NR_gettid), - fmt); - } else { - g_autoptr(GDateTime) now = g_date_time_new_now_utc(); - g_autofree char *nowstr = g_date_time_format(now, "%Y-%m-%d %H:%M:%S.%f%z"); - localfmt = g_strdup_printf("[%s] [ID: %08ld] %s", - nowstr, 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; - uint64_t mnt_id; - - fd = open("/", O_PATH); - if (fd == -1) { - fuse_log(FUSE_LOG_ERR, "open(%s, O_PATH): %m\n", lo->source); - exit(1); - } - - res = do_statx(lo, fd, "", &stat, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, - &mnt_id); - 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->key.mnt_id = mnt_id; - root->nlookup = 2; - g_atomic_int_set(&root->refcount, 2); - if (lo->posix_lock) { - pthread_mutex_init(&root->plock_mutex, NULL); - root->posix_locks = g_hash_table_new_full( - g_direct_hash, g_direct_equal, NULL, posix_locks_value_destroy); - } -} - -static guint lo_key_hash(gconstpointer key) -{ - const struct lo_key *lkey = key; - - return (guint)lkey->ino + (guint)lkey->dev + (guint)lkey->mnt_id; -} - -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 && la->mnt_id == lb->mnt_id; -} - -static void fuse_lo_data_cleanup(struct lo_data *lo) -{ - if (lo->inodes) { - g_hash_table_destroy(lo->inodes); - } - - if (lo->root.posix_locks) { - g_hash_table_destroy(lo->root.posix_locks); - } - 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->proc_self_task >= 0) { - close(lo->proc_self_task); - } - - if (lo->root.fd >= 0) { - close(lo->root.fd); - } - - free(lo->xattrmap); - free_xattrmap(lo); - free(lo->xattr_security_capability); - free(lo->source); -} - -static void qemu_version(void) -{ - printf("virtiofsd version " QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n"); -} - -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 = { - .sandbox = SANDBOX_NAMESPACE, - .debug = 0, - .writeback = 0, - .posix_lock = 0, - .allow_direct_io = 0, - .proc_self_fd = -1, - .proc_self_task = -1, - .user_killpriv_v2 = -1, - .user_posix_acl = -1, - .user_security_label = -1, - }; - struct lo_map_elem *root_elem; - struct lo_map_elem *reserve_elem; - int ret = -1; - - /* Initialize time conversion information for localtime_r(). */ - tzset(); - - /* Don't mask creation mode, kernel already did that */ - umask(0); - - qemu_init_exec_dir(argv[0]); - - drop_supplementary_groups(); - - 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); - reserve_elem = lo_map_reserve(&lo.ino_map, 0); - if (!reserve_elem) { - fuse_log(FUSE_LOG_ERR, "failed to alloc reserve_elem.\n"); - goto err_out1; - } - reserve_elem->in_use = false; - root_elem = lo_map_reserve(&lo.ino_map, lo.root.fuse_ino); - if (!root_elem) { - fuse_log(FUSE_LOG_ERR, "failed to alloc root_elem.\n"); - goto err_out1; - } - 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) { - qemu_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; - } - - if (opts.log_level != 0) { - current_log_level = opts.log_level; - } else { - /* default log level is INFO */ - current_log_level = FUSE_LOG_INFO; - } - 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.source) { - fuse_log(FUSE_LOG_ERR, "failed to strdup source\n"); - goto err_out1; - } - } - - if (lo.xattrmap) { - lo.xattr = 1; - parse_xattrmap(&lo); - } - - 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); - } - - if (lo.user_posix_acl == 1 && !lo.xattr) { - fuse_log(FUSE_LOG_ERR, "Can't enable posix ACLs. xattrs are disabled." - "\n"); - exit(1); - } - - lo.use_statx = true; - - 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(opts.rlimit_nofile); - - /* 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/passthrough_seccomp.c b/tools/virtiofsd/passthrough_seccomp.c deleted file mode 100644 index 888295c073..0000000000 --- a/tools/virtiofsd/passthrough_seccomp.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Seccomp sandboxing for virtiofsd - * - * Copyright (C) 2019 Red Hat, Inc. - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "passthrough_seccomp.h" -#include "fuse_i.h" -#include "fuse_log.h" -#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_allowlist[] = { - /* 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(fchmod), - 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(fstatfs64), - 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(_llseek), /* For POWER */ - 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(statx), - 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(restart_syscall), -#ifdef __NR_rseq - SCMP_SYS(rseq), /* required since glibc 2.35 */ -#endif - SCMP_SYS(rt_sigaction), - SCMP_SYS(rt_sigprocmask), - SCMP_SYS(rt_sigreturn), - SCMP_SYS(sched_getattr), - SCMP_SYS(sched_setattr), - 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(syncfs), - 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), - SCMP_SYS(umask), -}; - -/* Syscalls used when --syslog is enabled */ -static const int syscall_allowlist_syslog[] = { - SCMP_SYS(send), - SCMP_SYS(sendto), -}; - -static void add_allowlist(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_allowlist(ctx, syscall_allowlist, G_N_ELEMENTS(syscall_allowlist)); - if (enable_syslog) { - add_allowlist(ctx, syscall_allowlist_syslog, - G_N_ELEMENTS(syscall_allowlist_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/passthrough_seccomp.h b/tools/virtiofsd/passthrough_seccomp.h deleted file mode 100644 index 12674fc050..0000000000 --- a/tools/virtiofsd/passthrough_seccomp.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Seccomp sandboxing for virtiofsd - * - * Copyright (C) 2019 Red Hat, Inc. - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef VIRTIOFSD_PASSTHROUGH_SECCOMP_H -#define VIRTIOFSD_PASSTHROUGH_SECCOMP_H - -void setup_seccomp(bool enable_syslog); - -#endif /* VIRTIOFSD_PASSTHROUGH_SECCOMP_H */ diff --git a/trace-events b/trace-events index bc71006675..dd318ed1af 100644 --- a/trace-events +++ b/trace-events @@ -42,38 +42,6 @@ find_ram_offset(uint64_t size, uint64_t offset) "size: 0x%" PRIx64 " @ 0x%" PRIx find_ram_offset_loop(uint64_t size, uint64_t candidate, uint64_t offset, uint64_t next, uint64_t mingap) "trying size: 0x%" PRIx64 " @ 0x%" PRIx64 ", offset: 0x%" PRIx64" next: 0x%" PRIx64 " mingap: 0x%" PRIx64 ram_block_discard_range(const char *rbname, void *hva, size_t length, bool need_madvise, bool need_fallocate, int ret) "%s@%p + 0x%zx: madvise: %d fallocate: %d ret: %d" -# accel/tcg/cputlb.c -memory_notdirty_write_access(uint64_t vaddr, uint64_t ram_addr, unsigned size) "0x%" PRIx64 " ram_addr 0x%" PRIx64 " size %u" -memory_notdirty_set_dirty(uint64_t vaddr) "0x%" PRIx64 - -# gdbstub.c -gdbstub_op_start(const char *device) "Starting gdbstub using device %s" -gdbstub_op_exiting(uint8_t code) "notifying exit with code=0x%02x" -gdbstub_op_continue(void) "Continuing all CPUs" -gdbstub_op_continue_cpu(int cpu_index) "Continuing CPU %d" -gdbstub_op_stepping(int cpu_index) "Stepping CPU %d" -gdbstub_op_extra_info(const char *info) "Thread extra info: %s" -gdbstub_hit_watchpoint(const char *type, int cpu_gdb_index, uint64_t vaddr) "Watchpoint hit, type=\"%s\" cpu=%d, vaddr=0x%" PRIx64 "" -gdbstub_hit_internal_error(void) "RUN_STATE_INTERNAL_ERROR" -gdbstub_hit_break(void) "RUN_STATE_DEBUG" -gdbstub_hit_paused(void) "RUN_STATE_PAUSED" -gdbstub_hit_shutdown(void) "RUN_STATE_SHUTDOWN" -gdbstub_hit_io_error(void) "RUN_STATE_IO_ERROR" -gdbstub_hit_watchdog(void) "RUN_STATE_WATCHDOG" -gdbstub_hit_unknown(int state) "Unknown run state=0x%x" -gdbstub_io_reply(const char *message) "Sent: %s" -gdbstub_io_binaryreply(size_t ofs, const char *line) "0x%04zx: %s" -gdbstub_io_command(const char *command) "Received: %s" -gdbstub_io_got_ack(void) "Got ACK" -gdbstub_io_got_unexpected(uint8_t ch) "Got 0x%02x when expecting ACK/NACK" -gdbstub_err_got_nack(void) "Got NACK, retransmitting" -gdbstub_err_garbage(uint8_t ch) "received garbage between packets: 0x%02x" -gdbstub_err_overrun(void) "command buffer overrun, dropping command" -gdbstub_err_invalid_repeat(uint8_t ch) "got invalid RLE count: 0x%02x" -gdbstub_err_invalid_rle(void) "got invalid RLE sequence" -gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x%02x" -gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packet with incorrect checksum, expected=0x%02x, received=0x%02x" - # job.c job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" @@ -86,53 +54,3 @@ qmp_job_resume(void *job) "job %p" qmp_job_complete(void *job) "job %p" qmp_job_finalize(void *job) "job %p" qmp_job_dismiss(void *job) "job %p" - - -### Guest events, keep at bottom - - -## vCPU - -# trace/control-target.c - -# Hot-plug a new virtual (guest) CPU -# -# Mode: user, softmmu -# Targets: all -vcpu guest_cpu_enter(void) - -# trace/control.c - -# Hot-unplug a virtual (guest) CPU -# -# Mode: user, softmmu -# Targets: all -vcpu guest_cpu_exit(void) - -# hw/core/cpu.c - -# Reset the state of a virtual (guest) CPU -# -# Mode: user, softmmu -# Targets: all -vcpu guest_cpu_reset(void) - -# include/user/syscall-trace.h - -# @num: System call number. -# @arg*: System call argument value. -# -# Start executing a guest system call in syscall emulation mode. -# -# Mode: user -# Targets: TCG(all) -vcpu guest_user_syscall(uint64_t num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6, uint64_t arg7, uint64_t arg8) "num=0x%016"PRIx64" arg1=0x%016"PRIx64" arg2=0x%016"PRIx64" arg3=0x%016"PRIx64" arg4=0x%016"PRIx64" arg5=0x%016"PRIx64" arg6=0x%016"PRIx64" arg7=0x%016"PRIx64" arg8=0x%016"PRIx64 - -# @num: System call number. -# @ret: System call result value. -# -# Finish executing a guest system call in syscall emulation mode. -# -# Mode: user -# Targets: TCG(all) -vcpu guest_user_syscall_ret(uint64_t num, uint64_t ret) "num=0x%016"PRIx64" ret=0x%016"PRIx64 diff --git a/trace/control-internal.h b/trace/control-internal.h index 8b2b50a7cf..8d818d359b 100644 --- a/trace/control-internal.h +++ b/trace/control-internal.h @@ -25,16 +25,6 @@ static inline uint32_t trace_event_get_id(TraceEvent *ev) return ev->id; } -static inline uint32_t trace_event_get_vcpu_id(TraceEvent *ev) -{ - return ev->vcpu_id; -} - -static inline bool trace_event_is_vcpu(TraceEvent *ev) -{ - return ev->vcpu_id != TRACE_VCPU_EVENT_NONE; -} - static inline const char * trace_event_get_name(TraceEvent *ev) { assert(ev != NULL); diff --git a/trace/control-target.c b/trace/control-target.c index 8418673c18..97f21e476d 100644 --- a/trace/control-target.c +++ b/trace/control-target.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" +#include "qemu/lockable.h" #include "cpu.h" #include "trace/trace-root.h" #include "trace/control.h" @@ -35,115 +36,22 @@ void trace_event_set_state_dynamic_init(TraceEvent *ev, bool state) void trace_event_set_state_dynamic(TraceEvent *ev, bool state) { - CPUState *vcpu; assert(trace_event_get_state_static(ev)); - if (trace_event_is_vcpu(ev) && likely(first_cpu != NULL)) { - CPU_FOREACH(vcpu) { - trace_event_set_vcpu_state_dynamic(vcpu, ev, state); - } - } else { - /* - * Without the "vcpu" property, dstate can only be 1 or 0. With it, we - * haven't instantiated any vCPU yet, so we will set a global state - * instead, and trace_init_vcpu will reconcile it afterwards. - */ - bool state_pre = *ev->dstate; - if (state_pre != state) { - if (state) { - trace_events_enabled_count++; - *ev->dstate = 1; - } else { - trace_events_enabled_count--; - *ev->dstate = 0; - } - } - } -} -static void trace_event_synchronize_vcpu_state_dynamic( - CPUState *vcpu, run_on_cpu_data ignored) -{ - bitmap_copy(vcpu->trace_dstate, vcpu->trace_dstate_delayed, - CPU_TRACE_DSTATE_MAX_EVENTS); - cpu_tb_jmp_cache_clear(vcpu); -} - -void trace_event_set_vcpu_state_dynamic(CPUState *vcpu, - TraceEvent *ev, bool state) -{ - uint32_t vcpu_id; - bool state_pre; - assert(trace_event_get_state_static(ev)); - assert(trace_event_is_vcpu(ev)); - vcpu_id = trace_event_get_vcpu_id(ev); - state_pre = test_bit(vcpu_id, vcpu->trace_dstate); + /* + * There is no longer a "vcpu" property, dstate can only be 1 or + * 0. With it, we haven't instantiated any vCPU yet, so we will + * set a global state instead, and trace_init_vcpu will reconcile + * it afterwards. + */ + bool state_pre = *ev->dstate; if (state_pre != state) { if (state) { trace_events_enabled_count++; - set_bit(vcpu_id, vcpu->trace_dstate_delayed); - (*ev->dstate)++; + *ev->dstate = 1; } else { trace_events_enabled_count--; - clear_bit(vcpu_id, vcpu->trace_dstate_delayed); - (*ev->dstate)--; - } - if (vcpu->created) { - /* - * Delay changes until next TB; we want all TBs to be built from a - * single set of dstate values to ensure consistency of generated - * tracing code. - */ - async_run_on_cpu(vcpu, trace_event_synchronize_vcpu_state_dynamic, - RUN_ON_CPU_NULL); - } else { - trace_event_synchronize_vcpu_state_dynamic(vcpu, RUN_ON_CPU_NULL); + *ev->dstate = 0; } } } - -static bool adding_first_cpu1(void) -{ - CPUState *cpu; - size_t count = 0; - CPU_FOREACH(cpu) { - count++; - if (count > 1) { - return false; - } - } - return true; -} - -static bool adding_first_cpu(void) -{ - bool res; - cpu_list_lock(); - res = adding_first_cpu1(); - cpu_list_unlock(); - return res; -} - -void trace_init_vcpu(CPUState *vcpu) -{ - TraceEventIter iter; - TraceEvent *ev; - trace_event_iter_init_all(&iter); - while ((ev = trace_event_iter_next(&iter)) != NULL) { - if (trace_event_is_vcpu(ev) && - trace_event_get_state_static(ev) && - trace_event_get_state_dynamic(ev)) { - if (adding_first_cpu()) { - /* check preconditions */ - assert(*ev->dstate == 1); - /* disable early-init state ... */ - *ev->dstate = 0; - trace_events_enabled_count--; - /* ... and properly re-enable */ - trace_event_set_vcpu_state_dynamic(vcpu, ev, true); - } else { - trace_event_set_vcpu_state_dynamic(vcpu, ev, true); - } - } - } - trace_guest_cpu_enter(vcpu); -} diff --git a/trace/control-vcpu.h b/trace/control-vcpu.h deleted file mode 100644 index 0f98ebe7b5..0000000000 --- a/trace/control-vcpu.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Interface for configuring and controlling the state of tracing events. - * - * Copyright (C) 2011-2016 Lluís Vilanova - * - * 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 TRACE__CONTROL_VCPU_H -#define TRACE__CONTROL_VCPU_H - -#include "control.h" -#include "event-internal.h" -#include "hw/core/cpu.h" - -/** - * trace_event_get_vcpu_state: - * @vcpu: Target vCPU. - * @id: Event identifier name. - * - * Get the tracing state of an event (both static and dynamic) for the given - * vCPU. - * - * If the event has the disabled property, the check will have no performance - * impact. - */ -#define trace_event_get_vcpu_state(vcpu, id) \ - ((id ##_ENABLED) && \ - trace_event_get_vcpu_state_dynamic_by_vcpu_id( \ - vcpu, _ ## id ## _EVENT.vcpu_id)) - -/** - * trace_event_get_vcpu_state_dynamic: - * - * Get the dynamic tracing state of an event for the given vCPU. - */ -static bool trace_event_get_vcpu_state_dynamic(CPUState *vcpu, TraceEvent *ev); - -#include "control-internal.h" - -static inline bool -trace_event_get_vcpu_state_dynamic_by_vcpu_id(CPUState *vcpu, - uint32_t vcpu_id) -{ - /* it's on fast path, avoid consistency checks (asserts) */ - if (unlikely(trace_events_enabled_count)) { - return test_bit(vcpu_id, vcpu->trace_dstate); - } else { - return false; - } -} - -static inline bool trace_event_get_vcpu_state_dynamic(CPUState *vcpu, - TraceEvent *ev) -{ - uint32_t vcpu_id; - assert(trace_event_is_vcpu(ev)); - vcpu_id = trace_event_get_vcpu_id(ev); - return trace_event_get_vcpu_state_dynamic_by_vcpu_id(vcpu, vcpu_id); -} - -#endif diff --git a/trace/control.c b/trace/control.c index 6c77cc6318..ef107829ac 100644 --- a/trace/control.c +++ b/trace/control.c @@ -68,16 +68,6 @@ void trace_event_register_group(TraceEvent **events) size_t i; for (i = 0; events[i] != NULL; i++) { events[i]->id = next_id++; - if (events[i]->vcpu_id == TRACE_VCPU_EVENT_NONE) { - continue; - } - - if (likely(next_vcpu_id < CPU_TRACE_DSTATE_MAX_EVENTS)) { - events[i]->vcpu_id = next_vcpu_id++; - } else { - warn_report("too many vcpu trace events; dropping '%s'", - events[i]->name); - } } event_groups = g_renew(TraceEventGroup, event_groups, nevent_groups + 1); event_groups[nevent_groups].events = events; @@ -272,24 +262,6 @@ void trace_init_file(void) #endif } -void trace_fini_vcpu(CPUState *vcpu) -{ - TraceEventIter iter; - TraceEvent *ev; - - trace_guest_cpu_exit(vcpu); - - trace_event_iter_init_all(&iter); - while ((ev = trace_event_iter_next(&iter)) != NULL) { - if (trace_event_is_vcpu(ev) && - trace_event_get_state_static(ev) && - trace_event_get_vcpu_state_dynamic(vcpu, ev)) { - /* must disable to affect the global counter */ - trace_event_set_vcpu_state_dynamic(vcpu, ev, false); - } - } -} - bool trace_init_backends(void) { #ifdef CONFIG_TRACE_SIMPLE @@ -313,10 +285,10 @@ bool trace_init_backends(void) return true; } -void trace_opt_parse(const char *optarg) +void trace_opt_parse(const char *optstr) { QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("trace"), - optarg, true); + optstr, true); if (!opts) { exit(1); } diff --git a/trace/control.h b/trace/control.h index 23b8393b29..6754bfe052 100644 --- a/trace/control.h +++ b/trace/control.h @@ -89,23 +89,6 @@ static bool trace_event_is_pattern(const char *str); */ static uint32_t trace_event_get_id(TraceEvent *ev); -/** - * trace_event_get_vcpu_id: - * - * Get the per-vCPU identifier of an event. - * - * Special value #TRACE_VCPU_EVENT_NONE means the event is not vCPU-specific - * (does not have the "vcpu" property). - */ -static uint32_t trace_event_get_vcpu_id(TraceEvent *ev); - -/** - * trace_event_is_vcpu: - * - * Whether this is a per-vCPU event. - */ -static bool trace_event_is_vcpu(TraceEvent *ev); - /** * trace_event_get_name: * @@ -172,21 +155,6 @@ static bool trace_event_get_state_dynamic(TraceEvent *ev); */ void trace_event_set_state_dynamic(TraceEvent *ev, bool state); -/** - * trace_event_set_vcpu_state_dynamic: - * - * Set the dynamic tracing state of an event for the given vCPU. - * - * Pre-condition: trace_event_get_vcpu_state_static(ev) == true - * - * Note: Changes for execution-time events with the 'tcg' property will not be - * propagated until the next TB is executed (iff executing in TCG mode). - */ -void trace_event_set_vcpu_state_dynamic(CPUState *vcpu, - TraceEvent *ev, bool state); - - - /** * trace_init_backends: * @@ -205,22 +173,6 @@ bool trace_init_backends(void); */ void trace_init_file(void); -/** - * trace_init_vcpu: - * @vcpu: Added vCPU. - * - * Set initial dynamic event state for a hot-plugged vCPU. - */ -void trace_init_vcpu(CPUState *vcpu); - -/** - * trace_fini_vcpu: - * @vcpu: Removed vCPU. - * - * Disable dynamic event state for a hot-unplugged vCPU. - */ -void trace_fini_vcpu(CPUState *vcpu); - /** * trace_list_events: * @f: Where to send output. @@ -245,11 +197,11 @@ extern QemuOptsList qemu_trace_opts; /** * trace_opt_parse: - * @optarg: A string argument of --trace command line argument + * @optstr: A string argument of --trace command line argument * * Initialize tracing subsystem. */ -void trace_opt_parse(const char *optarg); +void trace_opt_parse(const char *optstr); /** * trace_get_vcpu_event_count: diff --git a/trace/event-internal.h b/trace/event-internal.h index f63500b37e..0c24e01b52 100644 --- a/trace/event-internal.h +++ b/trace/event-internal.h @@ -19,7 +19,6 @@ /** * TraceEvent: * @id: Unique event identifier. - * @vcpu_id: Unique per-vCPU event identifier. * @name: Event name. * @sstate: Static tracing state. * @dstate: Dynamic tracing state @@ -33,7 +32,6 @@ */ typedef struct TraceEvent { uint32_t id; - uint32_t vcpu_id; const char * name; const bool sstate; uint16_t *dstate; diff --git a/trace/meson.build b/trace/meson.build index 26b54714d5..c3412dc0ba 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -1,3 +1,4 @@ +system_ss.add(files('trace-hmp-cmds.c')) specific_ss.add(files('control-target.c')) @@ -63,7 +64,7 @@ trace_events_all = custom_target('trace-events-all', input: trace_events_files, command: [ 'cat', '@INPUT@' ], capture: true, - install: true, + install: get_option('trace_backends') != [ 'nop' ], install_dir: qemu_datadir) if 'ust' in get_option('trace_backends') @@ -88,4 +89,6 @@ if 'ftrace' in get_option('trace_backends') trace_ss.add(files('ftrace.c')) endif trace_ss.add(files('control.c')) -trace_ss.add(files('qmp.c')) +if have_system or have_tools or have_ga + trace_ss.add(files('qmp.c')) +endif diff --git a/trace/qmp.c b/trace/qmp.c index 3b4f4702b4..3e3971c6a8 100644 --- a/trace/qmp.c +++ b/trace/qmp.c @@ -10,23 +10,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qapi-commands-trace.h" -#include "control-vcpu.h" +#include "control.h" -static CPUState *get_cpu(bool has_vcpu, int vcpu, Error **errp) -{ - if (has_vcpu) { - CPUState *cpu = qemu_get_cpu(vcpu); - if (cpu == NULL) { - error_setg(errp, "invalid vCPU index %u", vcpu); - } - return cpu; - } else { - return NULL; - } -} - -static bool check_events(bool has_vcpu, bool ignore_unavailable, bool is_pattern, +static bool check_events(bool ignore_unavailable, bool is_pattern, const char *name, Error **errp) { if (!is_pattern) { @@ -38,12 +25,6 @@ static bool check_events(bool has_vcpu, bool ignore_unavailable, bool is_pattern return false; } - /* error for non-vcpu event */ - if (has_vcpu && !trace_event_is_vcpu(ev)) { - error_setg(errp, "event \"%s\" is not vCPU-specific", name); - return false; - } - /* error for unavailable event */ if (!ignore_unavailable && !trace_event_get_state_static(ev)) { error_setg(errp, "event \"%s\" is disabled", name); @@ -70,22 +51,13 @@ TraceEventInfoList *qmp_trace_event_get_state(const char *name, bool has_vcpu, int64_t vcpu, Error **errp) { - Error *err = NULL; TraceEventInfoList *events = NULL; TraceEventIter iter; TraceEvent *ev; bool is_pattern = trace_event_is_pattern(name); - CPUState *cpu; - - /* Check provided vcpu */ - cpu = get_cpu(has_vcpu, vcpu, &err); - if (err) { - error_propagate(errp, err); - return NULL; - } /* Check events */ - if (!check_events(has_vcpu, true, is_pattern, name, errp)) { + if (!check_events(true, is_pattern, name, errp)) { return NULL; } @@ -93,33 +65,17 @@ TraceEventInfoList *qmp_trace_event_get_state(const char *name, trace_event_iter_init_pattern(&iter, name); while ((ev = trace_event_iter_next(&iter)) != NULL) { TraceEventInfo *value; - bool is_vcpu = trace_event_is_vcpu(ev); - if (has_vcpu && !is_vcpu) { - continue; - } value = g_new(TraceEventInfo, 1); - value->vcpu = is_vcpu; value->name = g_strdup(trace_event_get_name(ev)); if (!trace_event_get_state_static(ev)) { value->state = TRACE_EVENT_STATE_UNAVAILABLE; } else { - if (has_vcpu) { - if (is_vcpu) { - if (trace_event_get_vcpu_state_dynamic(cpu, ev)) { - value->state = TRACE_EVENT_STATE_ENABLED; - } else { - value->state = TRACE_EVENT_STATE_DISABLED; - } - } - /* else: already skipped above */ + if (trace_event_get_state_dynamic(ev)) { + value->state = TRACE_EVENT_STATE_ENABLED; } else { - if (trace_event_get_state_dynamic(ev)) { - value->state = TRACE_EVENT_STATE_ENABLED; - } else { - value->state = TRACE_EVENT_STATE_DISABLED; - } + value->state = TRACE_EVENT_STATE_DISABLED; } } QAPI_LIST_PREPEND(events, value); @@ -133,21 +89,12 @@ void qmp_trace_event_set_state(const char *name, bool enable, bool has_vcpu, int64_t vcpu, Error **errp) { - Error *err = NULL; TraceEventIter iter; TraceEvent *ev; bool is_pattern = trace_event_is_pattern(name); - CPUState *cpu; - - /* Check provided vcpu */ - cpu = get_cpu(has_vcpu, vcpu, &err); - if (err) { - error_propagate(errp, err); - return; - } /* Check events */ - if (!check_events(has_vcpu, has_ignore_unavailable && ignore_unavailable, + if (!check_events(has_ignore_unavailable && ignore_unavailable, is_pattern, name, errp)) { return; } @@ -155,14 +102,9 @@ void qmp_trace_event_set_state(const char *name, bool enable, /* Apply changes (all errors checked above) */ trace_event_iter_init_pattern(&iter, name); while ((ev = trace_event_iter_next(&iter)) != NULL) { - if (!trace_event_get_state_static(ev) || - (has_vcpu && !trace_event_is_vcpu(ev))) { + if (!trace_event_get_state_static(ev)) { continue; } - if (has_vcpu) { - trace_event_set_vcpu_state_dynamic(cpu, ev, enable); - } else { - trace_event_set_state_dynamic(ev, enable); - } + trace_event_set_state_dynamic(ev, enable); } } diff --git a/trace/trace-hmp-cmds.c b/trace/trace-hmp-cmds.c new file mode 100644 index 0000000000..86211fce27 --- /dev/null +++ b/trace/trace-hmp-cmds.c @@ -0,0 +1,136 @@ +/* + * HMP commands related to tracing + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-trace.h" +#include "qapi/qmp/qdict.h" +#include "trace/control.h" +#ifdef CONFIG_TRACE_SIMPLE +#include "trace/simple.h" +#endif + +void hmp_trace_event(Monitor *mon, const QDict *qdict) +{ + const char *tp_name = qdict_get_str(qdict, "name"); + bool new_state = qdict_get_bool(qdict, "option"); + Error *local_err = NULL; + + qmp_trace_event_set_state(tp_name, new_state, + true, true, false, 0, &local_err); + if (local_err) { + error_report_err(local_err); + } +} + +#ifdef CONFIG_TRACE_SIMPLE +void hmp_trace_file(Monitor *mon, const QDict *qdict) +{ + const char *op = qdict_get_try_str(qdict, "op"); + const char *arg = qdict_get_try_str(qdict, "arg"); + + if (!op) { + st_print_trace_file_status(); + } else if (!strcmp(op, "on")) { + st_set_trace_file_enabled(true); + } else if (!strcmp(op, "off")) { + st_set_trace_file_enabled(false); + } else if (!strcmp(op, "flush")) { + st_flush_trace_buffer(); + } else if (!strcmp(op, "set")) { + if (arg) { + st_set_trace_file(arg); + } + } else { + monitor_printf(mon, "unexpected argument \"%s\"\n", op); + hmp_help_cmd(mon, "trace-file"); + } +} +#endif + +void hmp_info_trace_events(Monitor *mon, const QDict *qdict) +{ + const char *name = qdict_get_try_str(qdict, "name"); + TraceEventInfoList *events; + TraceEventInfoList *elem; + Error *local_err = NULL; + + if (name == NULL) { + name = "*"; + } + + events = qmp_trace_event_get_state(name, false, 0, &local_err); + if (local_err) { + error_report_err(local_err); + return; + } + + for (elem = events; elem != NULL; elem = elem->next) { + monitor_printf(mon, "%s : state %u\n", + elem->value->name, + elem->value->state == TRACE_EVENT_STATE_ENABLED ? 1 : 0); + } + qapi_free_TraceEventInfoList(events); +} + +void info_trace_events_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + + len = strlen(str); + readline_set_completion_index(rs, len); + if (nb_args == 2) { + TraceEventIter iter; + TraceEvent *ev; + char *pattern = g_strdup_printf("%s*", str); + trace_event_iter_init_pattern(&iter, pattern); + while ((ev = trace_event_iter_next(&iter)) != NULL) { + readline_add_completion(rs, trace_event_get_name(ev)); + } + g_free(pattern); + } +} + +void trace_event_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + + len = strlen(str); + readline_set_completion_index(rs, len); + if (nb_args == 2) { + TraceEventIter iter; + TraceEvent *ev; + char *pattern = g_strdup_printf("%s*", str); + trace_event_iter_init_pattern(&iter, pattern); + while ((ev = trace_event_iter_next(&iter)) != NULL) { + readline_add_completion(rs, trace_event_get_name(ev)); + } + g_free(pattern); + } else if (nb_args == 3) { + readline_add_completion_of(rs, str, "on"); + readline_add_completion_of(rs, str, "off"); + } +} diff --git a/ui/clipboard.c b/ui/clipboard.c index 9079ef829b..4264884a6c 100644 --- a/ui/clipboard.c +++ b/ui/clipboard.c @@ -1,5 +1,6 @@ #include "qemu/osdep.h" #include "ui/clipboard.h" +#include "trace.h" static NotifierList clipboard_notifiers = NOTIFIER_LIST_INITIALIZER(clipboard_notifiers); @@ -43,27 +44,45 @@ void qemu_clipboard_peer_release(QemuClipboardPeer *peer, bool qemu_clipboard_check_serial(QemuClipboardInfo *info, bool client) { + bool ok; + if (!info->has_serial || !cbinfo[info->selection] || !cbinfo[info->selection]->has_serial) { + trace_clipboard_check_serial(-1, -1, true); return true; } if (client) { - return cbinfo[info->selection]->serial >= info->serial; + ok = info->serial >= cbinfo[info->selection]->serial; } else { - return cbinfo[info->selection]->serial > info->serial; + ok = info->serial > cbinfo[info->selection]->serial; } + + trace_clipboard_check_serial(cbinfo[info->selection]->serial, info->serial, ok); + return ok; } void qemu_clipboard_update(QemuClipboardInfo *info) { + uint32_t type; QemuClipboardNotify notify = { .type = QEMU_CLIPBOARD_UPDATE_INFO, .info = info, }; assert(info->selection < QEMU_CLIPBOARD_SELECTION__COUNT); + for (type = 0; type < QEMU_CLIPBOARD_TYPE__COUNT; type++) { + /* + * If data is missing, the clipboard owner's 'request' callback needs to + * be set. Otherwise, there is no way to get the clipboard data and + * qemu_clipboard_request() cannot be called. + */ + if (info->types[type].available && !info->types[type].data) { + assert(info->owner && info->owner->request); + } + } + notifier_list_notify(&clipboard_notifiers, ¬ify); if (cbinfo[info->selection] != info) { @@ -125,6 +144,8 @@ void qemu_clipboard_request(QemuClipboardInfo *info, !info->owner) return; + assert(info->owner->request); + info->types[type].requested = true; info->owner->request(info, type); } @@ -132,7 +153,14 @@ void qemu_clipboard_request(QemuClipboardInfo *info, void qemu_clipboard_reset_serial(void) { QemuClipboardNotify notify = { .type = QEMU_CLIPBOARD_RESET_SERIAL }; + int i; + for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) { + QemuClipboardInfo *info = qemu_clipboard_info(i); + if (info) { + info->serial = 0; + } + } notifier_list_notify(&clipboard_notifiers, ¬ify); } @@ -149,9 +177,15 @@ void qemu_clipboard_set_data(QemuClipboardPeer *peer, } g_free(info->types[type].data); - info->types[type].data = g_memdup(data, size); - info->types[type].size = size; - info->types[type].available = true; + if (size) { + info->types[type].data = g_memdup2(data, size); + info->types[type].size = size; + info->types[type].available = true; + } else { + info->types[type].data = NULL; + info->types[type].size = 0; + info->types[type].available = false; + } if (update) { qemu_clipboard_update(info); diff --git a/ui/cocoa.m b/ui/cocoa.m index 09a62817f2..25e0db9dd0 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -35,6 +35,7 @@ #include "ui/kbd-state.h" #include "sysemu/sysemu.h" #include "sysemu/runstate.h" +#include "sysemu/runstate-action.h" #include "sysemu/cpu-throttle.h" #include "qapi/error.h" #include "qapi/qapi-commands-block.h" @@ -45,6 +46,7 @@ #include "qemu/cutils.h" #include "qemu/main-loop.h" #include "qemu/module.h" +#include "qemu/error-report.h" #include #include "hw/core/cpu.h" @@ -52,6 +54,10 @@ #define MAC_OS_X_VERSION_10_13 101300 #endif +#ifndef MAC_OS_VERSION_14_0 +#define MAC_OS_VERSION_14_0 140000 +#endif + /* 10.14 deprecates NSOnState and NSOffState in favor of * NSControlStateValueOn/Off, which were introduced in 10.13. * Define for older versions @@ -71,6 +77,9 @@ #define cgrect(nsrect) (*(CGRect *)&(nsrect)) +#define UC_CTRL_KEY "\xe2\x8c\x83" +#define UC_ALT_KEY "\xe2\x8c\xa5" + typedef struct { int width; int height; @@ -84,7 +93,6 @@ static void cocoa_switch(DisplayChangeListener *dcl, static void cocoa_refresh(DisplayChangeListener *dcl); -static NSWindow *normalWindow; static const DisplayChangeListenerOps dcl_ops = { .dpy_name = "cocoa", .dpy_gfx_update = cocoa_update, @@ -94,51 +102,47 @@ static const DisplayChangeListenerOps dcl_ops = { static DisplayChangeListener dcl = { .ops = &dcl_ops, }; -static int last_buttons; +static QKbdState *kbd; static int cursor_hide = 1; static int left_command_key_enabled = 1; static bool swap_opt_cmd; -static int gArgc; -static char **gArgv; -static bool stretch_video; +static CGInterpolationQuality zoom_interpolation = kCGInterpolationNone; static NSTextField *pauseLabel; -static QemuSemaphore display_init_sem; -static QemuSemaphore app_started_sem; static bool allow_events; static NSInteger cbchangecount = -1; static QemuClipboardInfo *cbinfo; static QemuEvent cbevent; -// Utility functions to run specified code block with iothread lock held +// Utility functions to run specified code block with the BQL held typedef void (^CodeBlock)(void); typedef bool (^BoolCodeBlock)(void); -static void with_iothread_lock(CodeBlock block) +static void with_bql(CodeBlock block) { - bool locked = qemu_mutex_iothread_locked(); + bool locked = bql_locked(); if (!locked) { - qemu_mutex_lock_iothread(); + bql_lock(); } block(); if (!locked) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } } -static bool bool_with_iothread_lock(BoolCodeBlock block) +static bool bool_with_bql(BoolCodeBlock block) { - bool locked = qemu_mutex_iothread_locked(); + bool locked = bql_locked(); bool val; if (!locked) { - qemu_mutex_lock_iothread(); + bql_lock(); } val = block(); if (!locked) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } return val; } @@ -304,19 +308,14 @@ static void handleAnyDeviceErrors(Error * err) @interface QemuCocoaView : NSView { QEMUScreen screen; - NSWindow *fullScreenWindow; - float cx,cy,cw,ch,cdx,cdy; pixman_image_t *pixman_image; - QKbdState *kbd; BOOL isMouseGrabbed; - BOOL isFullscreen; BOOL isAbsoluteEnabled; CFMachPortRef eventsTap; } - (void) switchSurface:(pixman_image_t *)image; - (void) grabMouse; - (void) ungrabMouse; -- (void) toggleFullScreen:(id)sender; - (void) setFullGrab:(id)sender; - (void) handleMonitorInput:(NSEvent *)event; - (bool) handleEvent:(NSEvent *)event; @@ -332,8 +331,6 @@ static void handleAnyDeviceErrors(Error * err) */ - (BOOL) isMouseGrabbed; - (BOOL) isAbsoluteEnabled; -- (float) cdx; -- (float) cdy; - (QEMUScreen) gscreen; - (void) raiseAllKeys; @end @@ -342,9 +339,9 @@ QemuCocoaView *cocoaView; static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEventRef cgEvent, void *userInfo) { - QemuCocoaView *cocoaView = userInfo; + QemuCocoaView *view = userInfo; NSEvent *event = [NSEvent eventWithCGEvent:cgEvent]; - if ([cocoaView isMouseGrabbed] && [cocoaView handleEvent:event]) { + if ([view isMouseGrabbed] && [view handleEvent:event]) { COCOA_DEBUG("Global events tap: qemu handled the event, capturing!\n"); return NULL; } @@ -361,9 +358,24 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven self = [super initWithFrame:frameRect]; if (self) { + NSTrackingAreaOptions options = NSTrackingActiveInKeyWindow | + NSTrackingMouseEnteredAndExited | + NSTrackingMouseMoved | + NSTrackingInVisibleRect; + + NSTrackingArea *trackingArea = + [[NSTrackingArea alloc] initWithRect:CGRectZero + options:options + owner:self + userInfo:nil]; + + [self addTrackingArea:trackingArea]; + [trackingArea release]; screen.width = frameRect.size.width; screen.height = frameRect.size.height; - kbd = qkbd_state_init(dcl.con); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_14_0 + [self setClipsToBounds:YES]; +#endif } return self; @@ -377,8 +389,6 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven pixman_image_unref(pixman_image); } - qkbd_state_free(kbd); - if (eventsTap) { CFRelease(eventsTap); } @@ -391,44 +401,23 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven return YES; } -- (BOOL) screenContainsPoint:(NSPoint) p +- (void) viewDidMoveToWindow { - return (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height); + [self resizeWindow]; } -/* Get location of event and convert to virtual screen coordinate */ -- (CGPoint) screenLocationOfEvent:(NSEvent *)ev +- (void) selectConsoleLocked:(unsigned int)index { - NSWindow *eventWindow = [ev window]; - // XXX: Use CGRect and -convertRectFromScreen: to support macOS 10.10 - CGRect r = CGRectZero; - r.origin = [ev locationInWindow]; - if (!eventWindow) { - if (!isFullscreen) { - return [[self window] convertRectFromScreen:r].origin; - } else { - CGPoint locationInSelfWindow = [[self window] convertRectFromScreen:r].origin; - CGPoint loc = [self convertPoint:locationInSelfWindow fromView:nil]; - if (stretch_video) { - loc.x /= cdx; - loc.y /= cdy; - } - return loc; - } - } else if ([[self window] isEqual:eventWindow]) { - if (!isFullscreen) { - return r.origin; - } else { - CGPoint loc = [self convertPoint:r.origin fromView:nil]; - if (stretch_video) { - loc.x /= cdx; - loc.y /= cdy; - } - return loc; - } - } else { - return [[self window] convertRectFromScreen:[eventWindow convertRectToScreen:r]].origin; + QemuConsole *con = qemu_console_lookup_by_index(index); + if (!con) { + return; } + + unregister_displaychangelistener(&dcl); + qkbd_state_switch_console(kbd, con); + dcl.con = con; + register_displaychangelistener(&dcl); + [self updateUIInfo]; } - (void) hideCursor @@ -454,7 +443,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven // get CoreGraphic context CGContextRef viewContextRef = [[NSGraphicsContext currentContext] CGContext]; - CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone); + CGContextSetInterpolationQuality (viewContextRef, zoom_interpolation); CGContextSetShouldAntialias (viewContextRef, NO); // draw screen bitmap directly to Core Graphics context @@ -496,10 +485,8 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven [self getRectsBeingDrawn:&rectList count:&rectCount]; for (i = 0; i < rectCount; i++) { - clipRect.origin.x = rectList[i].origin.x / cdx; - clipRect.origin.y = (float)h - (rectList[i].origin.y + rectList[i].size.height) / cdy; - clipRect.size.width = rectList[i].size.width / cdx; - clipRect.size.height = rectList[i].size.height / cdy; + clipRect = rectList[i]; + clipRect.origin.y = (float)h - (clipRect.origin.y + clipRect.size.height); clipImageRef = CGImageCreateWithImageInRect( imageRef, clipRect @@ -512,42 +499,75 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven } } -- (void) setContentDimensions +- (NSSize)fixAspectRatio:(NSSize)max { - COCOA_DEBUG("QemuCocoaView: setContentDimensions\n"); + NSSize scaled; + NSSize fixed; - if (isFullscreen) { - cdx = [[NSScreen mainScreen] frame].size.width / (float)screen.width; - cdy = [[NSScreen mainScreen] frame].size.height / (float)screen.height; + scaled.width = screen.width * max.height; + scaled.height = screen.height * max.width; - /* stretches video, but keeps same aspect ratio */ - if (stretch_video == true) { - /* use smallest stretch value - prevents clipping on sides */ - if (MIN(cdx, cdy) == cdx) { - cdy = cdx; - } else { - cdx = cdy; - } - } else { /* No stretching */ - cdx = cdy = 1; - } - cw = screen.width * cdx; - ch = screen.height * cdy; - cx = ([[NSScreen mainScreen] frame].size.width - cw) / 2.0; - cy = ([[NSScreen mainScreen] frame].size.height - ch) / 2.0; + /* + * Here screen is our guest's output size, and max is the size of the + * largest possible area of the screen we can display on. + * We want to scale up (screen.width x screen.height) by either: + * 1) max.height / screen.height + * 2) max.width / screen.width + * With the first scale factor the scale will result in an output height of + * max.height (i.e. we will fill the whole height of the available screen + * space and have black bars left and right) and with the second scale + * factor the scaling will result in an output width of max.width (i.e. we + * fill the whole width of the available screen space and have black bars + * top and bottom). We need to pick whichever keeps the whole of the guest + * output on the screen, which is to say the smaller of the two scale + * factors. + * To avoid doing more division than strictly necessary, instead of directly + * comparing scale factors 1 and 2 we instead calculate and compare those + * two scale factors multiplied by (screen.height * screen.width). + */ + if (scaled.width < scaled.height) { + fixed.width = scaled.width / screen.height; + fixed.height = max.height; } else { - cx = 0; - cy = 0; - cw = screen.width; - ch = screen.height; - cdx = 1.0; - cdy = 1.0; + fixed.width = max.width; + fixed.height = scaled.height / screen.width; } + + return fixed; +} + +- (NSSize) screenSafeAreaSize +{ + NSSize size = [[[self window] screen] frame].size; + NSEdgeInsets insets = [[[self window] screen] safeAreaInsets]; + size.width -= insets.left + insets.right; + size.height -= insets.top + insets.bottom; + return size; +} + +- (void) resizeWindow +{ + [[self window] setContentAspectRatio:NSMakeSize(screen.width, screen.height)]; + + if (!([[self window] styleMask] & NSWindowStyleMaskResizable)) { + [[self window] setContentSize:NSMakeSize(screen.width, screen.height)]; + [[self window] center]; + } else if ([[self window] styleMask] & NSWindowStyleMaskFullScreen) { + [[self window] setContentSize:[self fixAspectRatio:[self screenSafeAreaSize]]]; + [[self window] center]; + } else { + [[self window] setContentSize:[self fixAspectRatio:[self frame].size]]; + } +} + +- (void) updateBounds +{ + [self setBoundsSize:NSMakeSize(screen.width, screen.height)]; } - (void) updateUIInfoLocked { - /* Must be called with the iothread lock, i.e. via updateUIInfo */ + /* Must be called with the BQL, i.e. via updateUIInfo */ NSSize frameSize; QemuUIInfo info; @@ -560,8 +580,21 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven CGDirectDisplayID display = [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]; NSSize screenSize = [[[self window] screen] frame].size; CGSize screenPhysicalSize = CGDisplayScreenSize(display); + bool isFullscreen = ([[self window] styleMask] & NSWindowStyleMaskFullScreen) != 0; + CVDisplayLinkRef displayLink; + + frameSize = isFullscreen ? [self screenSafeAreaSize] : [self frame].size; + + if (!CVDisplayLinkCreateWithCGDisplay(display, &displayLink)) { + CVTime period = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLink); + CVDisplayLinkRelease(displayLink); + if (!(period.flags & kCVTimeIsIndefinite)) { + update_displaychangelistener(&dcl, + 1000 * period.timeValue / period.timeScale); + info.refresh_rate = (int64_t)1000 * period.timeScale / period.timeValue; + } + } - frameSize = isFullscreen ? screenSize : [self frame].size; info.width_mm = frameSize.width / screenSize.width * screenPhysicalSize.width; info.height_mm = frameSize.height / screenSize.height * screenPhysicalSize.height; } else { @@ -584,7 +617,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven /* * Don't try to tell QEMU about UI information in the application * startup phase -- we haven't yet registered dcl with the QEMU UI - * layer, and also trying to take the iothread lock would deadlock. + * layer. * When cocoa_display_init() does register the dcl, the UI layer * will call cocoa_switch(), which will call updateUIInfo, so * we don't lose any information here. @@ -592,36 +625,25 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven return; } - with_iothread_lock(^{ + with_bql(^{ [self updateUIInfoLocked]; }); } -- (void)viewDidMoveToWindow -{ - [self updateUIInfo]; -} - - (void) switchSurface:(pixman_image_t *)image { COCOA_DEBUG("QemuCocoaView: switchSurface\n"); int w = pixman_image_get_width(image); int h = pixman_image_get_height(image); - /* cdx == 0 means this is our very first surface, in which case we need - * to recalculate the content dimensions even if it happens to be the size - * of the initial empty window. - */ - bool isResize = (w != screen.width || h != screen.height || cdx == 0.0); - int oldh = screen.height; - if (isResize) { + if (w != screen.width || h != screen.height) { // Resize before we trigger the redraw, or we'll redraw at the wrong size COCOA_DEBUG("switchSurface: new size %d x %d\n", w, h); screen.width = w; screen.height = h; - [self setContentDimensions]; - [self setFrame:NSMakeRect(cx, cy, cw, ch)]; + [self resizeWindow]; + [self updateBounds]; } // update screenBuffer @@ -630,51 +652,6 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven } pixman_image = image; - - // update windows - if (isFullscreen) { - [[fullScreenWindow contentView] setFrame:[[NSScreen mainScreen] frame]]; - [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + oldh, w, h + [normalWindow frame].size.height - oldh) display:NO animate:NO]; - } else { - if (qemu_name) - [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]]; - [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + oldh, w, h + [normalWindow frame].size.height - oldh) display:YES animate:NO]; - } - - if (isResize) { - [normalWindow center]; - } -} - -- (void) toggleFullScreen:(id)sender -{ - COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n"); - - if (isFullscreen) { // switch from fullscreen to desktop - isFullscreen = FALSE; - [self ungrabMouse]; - [self setContentDimensions]; - [fullScreenWindow close]; - [normalWindow setContentView: self]; - [normalWindow makeKeyAndOrderFront: self]; - [NSMenu setMenuBarVisible:YES]; - } else { // switch from desktop to fullscreen - isFullscreen = TRUE; - [normalWindow orderOut: nil]; /* Hide the window */ - [self grabMouse]; - [self setContentDimensions]; - [NSMenu setMenuBarVisible:NO]; - fullScreenWindow = [[NSWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame] - styleMask:NSWindowStyleMaskBorderless - backing:NSBackingStoreBuffered - defer:NO]; - [fullScreenWindow setAcceptsMouseMovedEvents: YES]; - [fullScreenWindow setHasShadow:NO]; - [fullScreenWindow setBackgroundColor: [NSColor blackColor]]; - [self setFrame:NSMakeRect(cx, cy, cw, ch)]; - [[fullScreenWindow contentView] addSubview: self]; - [fullScreenWindow makeKeyAndOrderFront:self]; - } } - (void) setFullGrab:(id)sender @@ -771,23 +748,14 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven } if (keysym) { - kbd_put_keysym(keysym); + QemuTextConsole *con = QEMU_TEXT_CONSOLE(dcl.con); + qemu_text_console_put_keysym(con, keysym); } } - (bool) handleEvent:(NSEvent *)event { - if(!allow_events) { - /* - * Just let OSX have all events that arrive before - * applicationDidFinishLaunching. - * This avoids a deadlock on the iothread lock, which cocoa_display_init() - * will not drop until after the app_started_sem is posted. (In theory - * there should not be any such events, but OSX Catalina now emits some.) - */ - return false; - } - return bool_with_iothread_lock(^{ + return bool_with_bql(^{ return [self handleEventLocked:event]; }); } @@ -796,12 +764,8 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven { /* Return true if we handled the event, false if it should be given to OSX */ COCOA_DEBUG("QemuCocoaView: handleEvent\n"); - int buttons = 0; + InputButton button; int keycode = 0; - bool mouse_event = false; - static bool switched_to_fullscreen = false; - // Location of event in virtual screen coordinates - NSPoint p = [self screenLocationOfEvent:event]; NSUInteger modifiers = [event modifierFlags]; /* @@ -945,19 +909,12 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven } break; } - break; + return true; case NSEventTypeKeyDown: keycode = cocoa_keycode_to_qemu([event keyCode]); // forward command key combos to the host UI unless the mouse is grabbed if (!isMouseGrabbed && ([event modifierFlags] & NSEventModifierFlagCommand)) { - /* - * Prevent the command key from being stuck down in the guest - * when using Command-F to switch to full screen mode. - */ - if (keycode == Q_KEY_CODE_F) { - switched_to_fullscreen = true; - } return false; } @@ -972,7 +929,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven // enable graphic console case '1' ... '9': - console_select(key - '0' - 1); /* ascii math */ + [self selectConsoleLocked:key - '0' - 1]; /* ascii math */ return true; // release the mouse grab @@ -983,12 +940,12 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven } } - if (qemu_console_is_graphic(NULL)) { + if (qemu_console_is_graphic(dcl.con)) { qkbd_state_key_event(kbd, keycode, true); } else { [self handleMonitorInput: event]; } - break; + return true; case NSEventTypeKeyUp: keycode = cocoa_keycode_to_qemu([event keyCode]); @@ -998,156 +955,154 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven return true; } - if (qemu_console_is_graphic(NULL)) { + if (qemu_console_is_graphic(dcl.con)) { qkbd_state_key_event(kbd, keycode, false); } - break; - case NSEventTypeMouseMoved: - if (isAbsoluteEnabled) { - // Cursor re-entered into a window might generate events bound to screen coordinates - // and `nil` window property, and in full screen mode, current window might not be - // key window, where event location alone should suffice. - if (![self screenContainsPoint:p] || !([[self window] isKeyWindow] || isFullscreen)) { - if (isMouseGrabbed) { - [self ungrabMouse]; - } - } else { - if (!isMouseGrabbed) { - [self grabMouse]; - } - } - } - mouse_event = true; - break; - case NSEventTypeLeftMouseDown: - buttons |= MOUSE_EVENT_LBUTTON; - mouse_event = true; - break; - case NSEventTypeRightMouseDown: - buttons |= MOUSE_EVENT_RBUTTON; - mouse_event = true; - break; - case NSEventTypeOtherMouseDown: - buttons |= MOUSE_EVENT_MBUTTON; - mouse_event = true; - break; - case NSEventTypeLeftMouseDragged: - buttons |= MOUSE_EVENT_LBUTTON; - mouse_event = true; - break; - case NSEventTypeRightMouseDragged: - buttons |= MOUSE_EVENT_RBUTTON; - mouse_event = true; - break; - case NSEventTypeOtherMouseDragged: - buttons |= MOUSE_EVENT_MBUTTON; - mouse_event = true; - break; - case NSEventTypeLeftMouseUp: - mouse_event = true; - if (!isMouseGrabbed && [self screenContainsPoint:p]) { - /* - * In fullscreen mode, the window of cocoaView may not be the - * key window, therefore the position relative to the virtual - * screen alone will be sufficient. - */ - if(isFullscreen || [[self window] isKeyWindow]) { - [self grabMouse]; - } - } - break; - case NSEventTypeRightMouseUp: - mouse_event = true; - break; - case NSEventTypeOtherMouseUp: - mouse_event = true; - break; + return true; case NSEventTypeScrollWheel: /* * Send wheel events to the guest regardless of window focus. * This is in-line with standard Mac OS X UI behaviour. */ - /* - * We shouldn't have got a scroll event when deltaY and delta Y - * are zero, hence no harm in dropping the event - */ - if ([event deltaY] != 0 || [event deltaX] != 0) { /* Determine if this is a scroll up or scroll down event */ - if ([event deltaY] != 0) { - buttons = ([event deltaY] > 0) ? + if ([event deltaY] != 0) { + button = ([event deltaY] > 0) ? INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN; - } else if ([event deltaX] != 0) { - buttons = ([event deltaX] > 0) ? + } else if ([event deltaX] != 0) { + button = ([event deltaX] > 0) ? INPUT_BUTTON_WHEEL_LEFT : INPUT_BUTTON_WHEEL_RIGHT; - } - - qemu_input_queue_btn(dcl.con, buttons, true); - qemu_input_event_sync(); - qemu_input_queue_btn(dcl.con, buttons, false); - qemu_input_event_sync(); + } else { + /* + * We shouldn't have got a scroll event when deltaY and delta Y + * are zero, hence no harm in dropping the event + */ + return true; } - /* - * Since deltaX/deltaY also report scroll wheel events we prevent mouse - * movement code from executing. - */ - mouse_event = false; - break; + qemu_input_queue_btn(dcl.con, button, true); + qemu_input_event_sync(); + qemu_input_queue_btn(dcl.con, button, false); + qemu_input_event_sync(); + + return true; default: return false; } +} - if (mouse_event) { - /* Don't send button events to the guest unless we've got a - * mouse grab or window focus. If we have neither then this event - * is the user clicking on the background window to activate and - * bring us to the front, which will be done by the sendEvent - * call below. We definitely don't want to pass that click through - * to the guest. - */ - if ((isMouseGrabbed || [[self window] isKeyWindow]) && - (last_buttons != buttons)) { - static uint32_t bmap[INPUT_BUTTON__MAX] = { - [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, - [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, - [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON - }; - qemu_input_update_buttons(dcl.con, bmap, last_buttons, buttons); - last_buttons = buttons; - } - if (isMouseGrabbed) { - if (isAbsoluteEnabled) { - /* Note that the origin for Cocoa mouse coords is bottom left, not top left. - * The check on screenContainsPoint is to avoid sending out of range values for - * clicks in the titlebar. - */ - if ([self screenContainsPoint:p]) { - qemu_input_queue_abs(dcl.con, INPUT_AXIS_X, p.x, 0, screen.width); - qemu_input_queue_abs(dcl.con, INPUT_AXIS_Y, screen.height - p.y, 0, screen.height); - } - } else { - qemu_input_queue_rel(dcl.con, INPUT_AXIS_X, (int)[event deltaX]); - qemu_input_queue_rel(dcl.con, INPUT_AXIS_Y, (int)[event deltaY]); - } - } else { - return false; - } - qemu_input_event_sync(); +- (void) handleMouseEvent:(NSEvent *)event button:(InputButton)button down:(bool)down +{ + if (!isMouseGrabbed) { + return; } - return true; + + with_bql(^{ + qemu_input_queue_btn(dcl.con, button, down); + }); + + [self handleMouseEvent:event]; +} + +- (void) handleMouseEvent:(NSEvent *)event +{ + if (!isMouseGrabbed) { + return; + } + + with_bql(^{ + if (isAbsoluteEnabled) { + CGFloat d = (CGFloat)screen.height / [self frame].size.height; + NSPoint p = [event locationInWindow]; + + /* Note that the origin for Cocoa mouse coords is bottom left, not top left. */ + qemu_input_queue_abs(dcl.con, INPUT_AXIS_X, p.x * d, 0, screen.width); + qemu_input_queue_abs(dcl.con, INPUT_AXIS_Y, screen.height - p.y * d, 0, screen.height); + } else { + qemu_input_queue_rel(dcl.con, INPUT_AXIS_X, [event deltaX]); + qemu_input_queue_rel(dcl.con, INPUT_AXIS_Y, [event deltaY]); + } + + qemu_input_event_sync(); + }); +} + +- (void) mouseExited:(NSEvent *)event +{ + if (isAbsoluteEnabled && isMouseGrabbed) { + [self ungrabMouse]; + } +} + +- (void) mouseEntered:(NSEvent *)event +{ + if (isAbsoluteEnabled && !isMouseGrabbed) { + [self grabMouse]; + } +} + +- (void) mouseMoved:(NSEvent *)event +{ + [self handleMouseEvent:event]; +} + +- (void) mouseDown:(NSEvent *)event +{ + [self handleMouseEvent:event button:INPUT_BUTTON_LEFT down:true]; +} + +- (void) rightMouseDown:(NSEvent *)event +{ + [self handleMouseEvent:event button:INPUT_BUTTON_RIGHT down:true]; +} + +- (void) otherMouseDown:(NSEvent *)event +{ + [self handleMouseEvent:event button:INPUT_BUTTON_MIDDLE down:true]; +} + +- (void) mouseDragged:(NSEvent *)event +{ + [self handleMouseEvent:event]; +} + +- (void) rightMouseDragged:(NSEvent *)event +{ + [self handleMouseEvent:event]; +} + +- (void) otherMouseDragged:(NSEvent *)event +{ + [self handleMouseEvent:event]; +} + +- (void) mouseUp:(NSEvent *)event +{ + if (!isMouseGrabbed) { + [self grabMouse]; + } + + [self handleMouseEvent:event button:INPUT_BUTTON_LEFT down:false]; +} + +- (void) rightMouseUp:(NSEvent *)event +{ + [self handleMouseEvent:event button:INPUT_BUTTON_RIGHT down:false]; +} + +- (void) otherMouseUp:(NSEvent *)event +{ + [self handleMouseEvent:event button:INPUT_BUTTON_MIDDLE down:false]; } - (void) grabMouse { COCOA_DEBUG("QemuCocoaView: grabMouse\n"); - if (!isFullscreen) { - if (qemu_name) - [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s - (Press ctrl + alt + g to release Mouse)", qemu_name]]; - else - [normalWindow setTitle:@"QEMU - (Press ctrl + alt + g to release Mouse)"]; - } + if (qemu_name) + [[self window] setTitle:[NSString stringWithFormat:@"QEMU %s - (Press " UC_CTRL_KEY " " UC_ALT_KEY " G to release Mouse)", qemu_name]]; + else + [[self window] setTitle:@"QEMU - (Press " UC_CTRL_KEY " " UC_ALT_KEY " G to release Mouse)"]; [self hideCursor]; CGAssociateMouseAndMouseCursorPosition(isAbsoluteEnabled); isMouseGrabbed = TRUE; // while isMouseGrabbed = TRUE, QemuCocoaApp sends all events to [cocoaView handleEvent:] @@ -1157,15 +1112,14 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven { COCOA_DEBUG("QemuCocoaView: ungrabMouse\n"); - if (!isFullscreen) { - if (qemu_name) - [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]]; - else - [normalWindow setTitle:@"QEMU"]; - } + if (qemu_name) + [[self window] setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]]; + else + [[self window] setTitle:@"QEMU"]; [self unhideCursor]; CGAssociateMouseAndMouseCursorPosition(TRUE); isMouseGrabbed = FALSE; + [self raiseAllButtons]; } - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled { @@ -1176,8 +1130,6 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven } - (BOOL) isMouseGrabbed {return isMouseGrabbed;} - (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;} -- (float) cdx {return cdx;} -- (float) cdy {return cdy;} - (QEMUScreen) gscreen {return screen;} /* @@ -1187,10 +1139,19 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven */ - (void) raiseAllKeys { - with_iothread_lock(^{ + with_bql(^{ qkbd_state_lift_all_keys(kbd); }); } + +- (void) raiseAllButtons +{ + with_bql(^{ + qemu_input_queue_btn(dcl.con, INPUT_BUTTON_LEFT, false); + qemu_input_queue_btn(dcl.con, INPUT_BUTTON_RIGHT, false); + qemu_input_queue_btn(dcl.con, INPUT_BUTTON_MIDDLE, false); + }); +} @end @@ -1205,7 +1166,6 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven { } - (void)doToggleFullScreen:(id)sender; -- (void)toggleFullScreen:(id)sender; - (void)showQEMUDoc:(id)sender; - (void)zoomToFit:(id) sender; - (void)displayConsole:(id)sender; @@ -1226,6 +1186,8 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven @implementation QemuCocoaAppController - (id) init { + NSWindow *window; + COCOA_DEBUG("QemuCocoaAppController: init\n"); self = [super init]; @@ -1239,20 +1201,20 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven } // create a window - normalWindow = [[NSWindow alloc] initWithContentRect:[cocoaView frame] + window = [[NSWindow alloc] initWithContentRect:[cocoaView frame] styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskMiniaturizable|NSWindowStyleMaskClosable backing:NSBackingStoreBuffered defer:NO]; - if(!normalWindow) { + if(!window) { error_report("(cocoa) can't create window"); exit(1); } - [normalWindow setAcceptsMouseMovedEvents:YES]; - [normalWindow setTitle:@"QEMU"]; - [normalWindow setContentView:cocoaView]; - [normalWindow makeKeyAndOrderFront:self]; - [normalWindow center]; - [normalWindow setDelegate: self]; - stretch_video = false; + [window setAcceptsMouseMovedEvents:YES]; + [window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + [window setTitle:qemu_name ? [NSString stringWithFormat:@"QEMU %s", qemu_name] : @"QEMU"]; + [window setContentView:cocoaView]; + [window makeKeyAndOrderFront:self]; + [window center]; + [window setDelegate: self]; /* Used for displaying pause on the screen */ pauseLabel = [NSTextField new]; @@ -1282,15 +1244,16 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven { COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n"); allow_events = true; - /* Tell cocoa_display_init to proceed */ - qemu_sem_post(&app_started_sem); } - (void)applicationWillTerminate:(NSNotification *)aNotification { COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n"); - qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI); + with_bql(^{ + shutdown_action = SHUTDOWN_ACTION_POWEROFF; + qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI); + }); /* * Sleep here, because returning will cause OSX to kill us @@ -1317,8 +1280,20 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven [cocoaView updateUIInfo]; } +- (void)windowDidEnterFullScreen:(NSNotification *)notification +{ + [cocoaView grabMouse]; +} + +- (void)windowDidExitFullScreen:(NSNotification *)notification +{ + [cocoaView resizeWindow]; + [cocoaView ungrabMouse]; +} + - (void)windowDidResize:(NSNotification *)notification { + [cocoaView updateBounds]; [cocoaView updateUIInfo]; } @@ -1335,10 +1310,23 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven return NO; } -/* Called when QEMU goes into the background */ -- (void) applicationWillResignActive: (NSNotification *)aNotification +- (NSApplicationPresentationOptions) window:(NSWindow *)window + willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions; + { - COCOA_DEBUG("QemuCocoaAppController: applicationWillResignActive\n"); + return (proposedOptions & ~(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)) | + NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; +} + +/* + * Called when QEMU goes into the background. Note that + * [-NSWindowDelegate windowDidResignKey:] is used here instead of + * [-NSApplicationDelegate applicationWillResignActive:] because it cannot + * detect that the window loses focus when the deck is clicked on macOS 13.2.1. + */ +- (void) windowDidResignKey: (NSNotification *)aNotification +{ + COCOA_DEBUG("%s\n", __func__); [cocoaView ungrabMouse]; [cocoaView raiseAllKeys]; } @@ -1349,14 +1337,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven */ - (void) doToggleFullScreen:(id)sender { - [self toggleFullScreen:(id)sender]; -} - -- (void)toggleFullScreen:(id)sender -{ - COCOA_DEBUG("QemuCocoaAppController: toggleFullScreen\n"); - - [cocoaView toggleFullScreen:sender]; + [[cocoaView window] toggleFullScreen:sender]; } - (void) setFullGrab:(id)sender @@ -1403,10 +1384,20 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven /* Stretches video to fit host monitor size */ - (void)zoomToFit:(id) sender { - stretch_video = !stretch_video; - if (stretch_video == true) { + NSWindowStyleMask styleMask = [[cocoaView window] styleMask] ^ NSWindowStyleMaskResizable; + + [[cocoaView window] setStyleMask:styleMask]; + [sender setState:styleMask & NSWindowStyleMaskResizable ? NSControlStateValueOn : NSControlStateValueOff]; + [cocoaView resizeWindow]; +} + +- (void)toggleZoomInterpolation:(id) sender +{ + if (zoom_interpolation == kCGInterpolationNone) { + zoom_interpolation = kCGInterpolationLow; [sender setState: NSControlStateValueOn]; } else { + zoom_interpolation = kCGInterpolationNone; [sender setState: NSControlStateValueOff]; } } @@ -1414,13 +1405,15 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven /* Displays the console on the screen */ - (void)displayConsole:(id)sender { - console_select([sender tag]); + with_bql(^{ + [cocoaView selectConsoleLocked:[sender tag]]; + }); } /* Pause the guest */ - (void)pauseQEMU:(id)sender { - with_iothread_lock(^{ + with_bql(^{ qmp_stop(NULL); }); [sender setEnabled: NO]; @@ -1431,7 +1424,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven /* Resume running the guest operating system */ - (void)resumeQEMU:(id) sender { - with_iothread_lock(^{ + with_bql(^{ qmp_cont(NULL); }); [sender setEnabled: NO]; @@ -1444,8 +1437,8 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven { /* Coordinates have to be calculated each time because the window can change its size */ int xCoord, yCoord, width, height; - xCoord = ([normalWindow frame].size.width - [pauseLabel frame].size.width)/2; - yCoord = [normalWindow frame].size.height - [pauseLabel frame].size.height - ([pauseLabel frame].size.height * .5); + xCoord = ([cocoaView frame].size.width - [pauseLabel frame].size.width)/2; + yCoord = [cocoaView frame].size.height - [pauseLabel frame].size.height - ([pauseLabel frame].size.height * .5); width = [pauseLabel frame].size.width; height = [pauseLabel frame].size.height; [pauseLabel setFrame: NSMakeRect(xCoord, yCoord, width, height)]; @@ -1461,7 +1454,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven /* Restarts QEMU */ - (void)restartQEMU:(id)sender { - with_iothread_lock(^{ + with_bql(^{ qmp_system_reset(NULL); }); } @@ -1469,7 +1462,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven /* Powers down QEMU */ - (void)powerDownQEMU:(id)sender { - with_iothread_lock(^{ + with_bql(^{ qmp_system_powerdown(NULL); }); } @@ -1488,9 +1481,9 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven } __block Error *err = NULL; - with_iothread_lock(^{ - qmp_eject(true, [drive cStringUsingEncoding: NSASCIIStringEncoding], - false, NULL, false, false, &err); + with_bql(^{ + qmp_eject([drive cStringUsingEncoding: NSASCIIStringEncoding], + NULL, false, false, &err); }); handleAnyDeviceErrors(err); } @@ -1523,14 +1516,13 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven } __block Error *err = NULL; - with_iothread_lock(^{ - qmp_blockdev_change_medium(true, - [drive cStringUsingEncoding: + with_bql(^{ + qmp_blockdev_change_medium([drive cStringUsingEncoding: NSASCIIStringEncoding], - false, NULL, + NULL, [file cStringUsingEncoding: NSASCIIStringEncoding], - true, "raw", + "raw", true, false, false, 0, &err); @@ -1606,7 +1598,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven // get the throttle percentage throttle_pct = [sender tag]; - with_iothread_lock(^{ + with_bql(^{ cpu_throttle_set(throttle_pct); }); COCOA_DEBUG("cpu throttling at %d%c\n", cpu_throttle_get_percentage(), '%'); @@ -1671,7 +1663,12 @@ static void create_initial_menus(void) // View menu menu = [[NSMenu alloc] initWithTitle:@"View"]; [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Enter Fullscreen" action:@selector(doToggleFullScreen:) keyEquivalent:@"f"] autorelease]]; // Fullscreen - [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Zoom To Fit" action:@selector(zoomToFit:) keyEquivalent:@""] autorelease]]; + menuItem = [[[NSMenuItem alloc] initWithTitle:@"Zoom To Fit" action:@selector(zoomToFit:) keyEquivalent:@""] autorelease]; + [menuItem setState: [[cocoaView window] styleMask] & NSWindowStyleMaskResizable ? NSControlStateValueOn : NSControlStateValueOff]; + [menu addItem: menuItem]; + menuItem = [[[NSMenuItem alloc] initWithTitle:@"Zoom Interpolation" action:@selector(toggleZoomInterpolation:) keyEquivalent:@""] autorelease]; + [menuItem setState: zoom_interpolation == kCGInterpolationLow ? NSControlStateValueOn : NSControlStateValueOff]; + [menu addItem: menuItem]; menuItem = [[[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""] autorelease]; [menuItem setSubmenu:menu]; [[NSApp mainMenu] addItem:menuItem]; @@ -1818,7 +1815,7 @@ static void addRemovableDevicesMenuItems(void) return; } - with_iothread_lock(^{ + with_bql(^{ QemuClipboardInfo *info = qemu_clipboard_info_ref(cbinfo); qemu_event_reset(&cbevent); qemu_clipboard_request(info, QEMU_CLIPBOARD_TYPE_TEXT); @@ -1826,9 +1823,9 @@ static void addRemovableDevicesMenuItems(void) while (info == cbinfo && info->types[QEMU_CLIPBOARD_TYPE_TEXT].available && info->types[QEMU_CLIPBOARD_TYPE_TEXT].data == NULL) { - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_event_wait(&cbevent); - qemu_mutex_lock_iothread(); + bql_lock(); } if (info == cbinfo) { @@ -1890,16 +1887,18 @@ static void cocoa_clipboard_notify(Notifier *notifier, void *data) static void cocoa_clipboard_request(QemuClipboardInfo *info, QemuClipboardType type) { + NSAutoreleasePool *pool; NSData *text; switch (type) { case QEMU_CLIPBOARD_TYPE_TEXT: + pool = [[NSAutoreleasePool alloc] init]; text = [[NSPasteboard generalPasteboard] dataForType:NSPasteboardTypeString]; if (text) { qemu_clipboard_set_data(&cbpeer, info, type, [text length], [text bytes], true); - [text release]; } + [pool release]; break; default: break; @@ -1909,92 +1908,45 @@ static void cocoa_clipboard_request(QemuClipboardInfo *info, /* * The startup process for the OSX/Cocoa UI is complicated, because * OSX insists that the UI runs on the initial main thread, and so we - * need to start a second thread which runs the vl.c qemu_main(): - * - * Initial thread: 2nd thread: + * need to start a second thread which runs the qemu_default_main(): * in main(): - * create qemu-main thread - * wait on display_init semaphore - * call qemu_main() - * ... - * in cocoa_display_init(): - * post the display_init semaphore - * wait on app_started semaphore - * create application, menus, etc - * enter OSX run loop - * in applicationDidFinishLaunching: - * post app_started semaphore - * tell main thread to fullscreen if needed - * [...] - * run qemu main-loop - * - * We do this in two stages so that we don't do the creation of the - * GUI application menus and so on for command line options like --help - * where we want to just print text to stdout and exit immediately. + * in cocoa_display_init(): + * assign cocoa_main to qemu_main + * create application, menus, etc + * in cocoa_main(): + * create qemu-main thread + * enter OSX run loop */ static void *call_qemu_main(void *opaque) { int status; - COCOA_DEBUG("Second thread: calling qemu_main()\n"); - status = qemu_main(gArgc, gArgv, *_NSGetEnviron()); - COCOA_DEBUG("Second thread: qemu_main() returned, exiting\n"); + COCOA_DEBUG("Second thread: calling qemu_default_main()\n"); + bql_lock(); + status = qemu_default_main(); + bql_unlock(); + COCOA_DEBUG("Second thread: qemu_default_main() returned, exiting\n"); [cbowner release]; exit(status); } -int main (int argc, char **argv) { +static int cocoa_main(void) +{ QemuThread thread; - COCOA_DEBUG("Entered main()\n"); - gArgc = argc; - gArgv = argv; - - qemu_sem_init(&display_init_sem, 0); - qemu_sem_init(&app_started_sem, 0); + COCOA_DEBUG("Entered %s()\n", __func__); + bql_unlock(); qemu_thread_create(&thread, "qemu_main", call_qemu_main, NULL, QEMU_THREAD_DETACHED); - COCOA_DEBUG("Main thread: waiting for display_init_sem\n"); - qemu_sem_wait(&display_init_sem); - COCOA_DEBUG("Main thread: initializing app\n"); - - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - - // Pull this console process up to being a fully-fledged graphical - // app with a menubar and Dock icon - ProcessSerialNumber psn = { 0, kCurrentProcess }; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - - [QemuApplication sharedApplication]; - - create_initial_menus(); - - /* - * Create the menu entries which depend on QEMU state (for consoles - * and removeable devices). These make calls back into QEMU functions, - * which is OK because at this point we know that the second thread - * holds the iothread lock and is synchronously waiting for us to - * finish. - */ - add_console_menu_entries(); - addRemovableDevicesMenuItems(); - - // Create an Application controller - QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init]; - [NSApp setDelegate:appController]; - // Start the main event loop COCOA_DEBUG("Main thread: entering OSX run loop\n"); [NSApp run]; - COCOA_DEBUG("Main thread: left OSX run loop, exiting\n"); + COCOA_DEBUG("Main thread: left OSX run loop, which should never happen\n"); - [appController release]; - [pool release]; - - return 0; + abort(); } @@ -2006,16 +1958,7 @@ static void cocoa_update(DisplayChangeListener *dcl, COCOA_DEBUG("qemu_cocoa: cocoa_update\n"); dispatch_async(dispatch_get_main_queue(), ^{ - NSRect rect; - if ([cocoaView cdx] == 1.0) { - rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h); - } else { - rect = NSMakeRect( - x * [cocoaView cdx], - ([cocoaView gscreen].height - y - h) * [cocoaView cdy], - w * [cocoaView cdx], - h * [cocoaView cdy]); - } + NSRect rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h); [cocoaView setNeedsDisplayInRect:rect]; }); } @@ -2034,7 +1977,6 @@ static void cocoa_switch(DisplayChangeListener *dcl, pixman_image_ref(image); dispatch_async(dispatch_get_main_queue(), ^{ - [cocoaView updateUIInfo]; [cocoaView switchSurface:image]; }); } @@ -2044,9 +1986,9 @@ static void cocoa_refresh(DisplayChangeListener *dcl) NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n"); - graphic_hw_update(NULL); + graphic_hw_update(dcl->con); - if (qemu_input_is_absolute()) { + if (qemu_input_is_absolute(dcl->con)) { dispatch_async(dispatch_get_main_queue(), ^{ if (![cocoaView isAbsoluteEnabled]) { if ([cocoaView isMouseGrabbed]) { @@ -2073,25 +2015,29 @@ static void cocoa_refresh(DisplayChangeListener *dcl) static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n"); - /* Tell main thread to go ahead and create the app and enter the run loop */ - qemu_sem_post(&display_init_sem); - qemu_sem_wait(&app_started_sem); - COCOA_DEBUG("cocoa_display_init: app start completed\n"); + qemu_main = cocoa_main; + + // Pull this console process up to being a fully-fledged graphical + // app with a menubar and Dock icon + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + + [QemuApplication sharedApplication]; + + // Create an Application controller + QemuCocoaAppController *controller = [[QemuCocoaAppController alloc] init]; + [NSApp setDelegate:controller]; - QemuCocoaAppController *controller = (QemuCocoaAppController *)[[NSApplication sharedApplication] delegate]; /* if fullscreen mode is to be used */ if (opts->has_full_screen && opts->full_screen) { - dispatch_async(dispatch_get_main_queue(), ^{ - [NSApp activateIgnoringOtherApps: YES]; - [controller toggleFullScreen: nil]; - }); + [[cocoaView window] toggleFullScreen: nil]; } if (opts->u.cocoa.has_full_grab && opts->u.cocoa.full_grab) { - dispatch_async(dispatch_get_main_queue(), ^{ - [controller setFullGrab: nil]; - }); + [controller setFullGrab: nil]; } if (opts->has_show_cursor && opts->show_cursor) { @@ -2105,12 +2051,37 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) left_command_key_enabled = 0; } + if (opts->u.cocoa.has_zoom_to_fit && opts->u.cocoa.zoom_to_fit) { + [cocoaView window].styleMask |= NSWindowStyleMaskResizable; + } + + if (opts->u.cocoa.has_zoom_interpolation && opts->u.cocoa.zoom_interpolation) { + zoom_interpolation = kCGInterpolationLow; + } + + create_initial_menus(); + /* + * Create the menu entries which depend on QEMU state (for consoles + * and removable devices). These make calls back into QEMU functions, + * which is OK because at this point we know that the second thread + * holds the BQL and is synchronously waiting for us to + * finish. + */ + add_console_menu_entries(); + addRemovableDevicesMenuItems(); + + dcl.con = qemu_console_lookup_default(); + kbd = qkbd_state_init(dcl.con); + // register vga output callbacks register_displaychangelistener(&dcl); + [cocoaView updateUIInfo]; qemu_event_init(&cbevent, false); cbowner = [[QemuCocoaPasteboardTypeOwner alloc] init]; qemu_clipboard_peer_register(&cbpeer); + + [pool release]; } static QemuDisplay qemu_display_cocoa = { diff --git a/ui/console-gl.c b/ui/console-gl.c index 8e3c9a3c8c..103b954017 100644 --- a/ui/console-gl.c +++ b/ui/console-gl.c @@ -53,7 +53,7 @@ void surface_gl_create_texture(QemuGLShader *gls, return; } - switch (surface->format) { + switch (surface_format(surface)) { case PIXMAN_BE_b8g8r8x8: case PIXMAN_BE_b8g8r8a8: surface->glformat = GL_BGRA_EXT; diff --git a/ui/console-priv.h b/ui/console-priv.h new file mode 100644 index 0000000000..43ceb8122f --- /dev/null +++ b/ui/console-priv.h @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * QEMU UI Console + */ +#ifndef CONSOLE_PRIV_H +#define CONSOLE_PRIV_H + +#include "ui/console.h" +#include "qemu/coroutine.h" +#include "qemu/timer.h" + +#include "vgafont.h" + +#define FONT_HEIGHT 16 +#define FONT_WIDTH 8 + +struct QemuConsole { + Object parent; + + int index; + DisplayState *ds; + DisplaySurface *surface; + DisplayScanout scanout; + int dcls; + DisplayGLCtx *gl; + int gl_block; + QEMUTimer *gl_unblock_timer; + int window_id; + QemuUIInfo ui_info; + QEMUTimer *ui_timer; + const GraphicHwOps *hw_ops; + void *hw; + CoQueue dump_queue; + + QTAILQ_ENTRY(QemuConsole) next; +}; + +void qemu_text_console_update_size(QemuTextConsole *c); +const char * qemu_text_console_get_label(QemuTextConsole *c); +void qemu_text_console_update_cursor(void); +void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym); + +#endif diff --git a/ui/console-vc-stubs.c b/ui/console-vc-stubs.c new file mode 100644 index 0000000000..b63e2fb234 --- /dev/null +++ b/ui/console-vc-stubs.c @@ -0,0 +1,33 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * QEMU VC stubs + */ +#include "qemu/osdep.h" + +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/option.h" +#include "chardev/char.h" +#include "ui/console-priv.h" + +void qemu_text_console_update_size(QemuTextConsole *c) +{ +} + +const char * +qemu_text_console_get_label(QemuTextConsole *c) +{ + return NULL; +} + +void qemu_text_console_update_cursor(void) +{ +} + +void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym) +{ +} + +void qemu_console_early_init(void) +{ +} diff --git a/ui/console-vc.c b/ui/console-vc.c new file mode 100644 index 0000000000..899fa11c94 --- /dev/null +++ b/ui/console-vc.c @@ -0,0 +1,1078 @@ +/* + * SPDX-License-Identifier: MIT + * QEMU VC + */ +#include "qemu/osdep.h" + +#include "chardev/char.h" +#include "qapi/error.h" +#include "qemu/fifo8.h" +#include "qemu/option.h" +#include "ui/console.h" + +#include "trace.h" +#include "console-priv.h" + +#define DEFAULT_BACKSCROLL 512 +#define CONSOLE_CURSOR_PERIOD 500 + +typedef struct TextAttributes { + uint8_t fgcol:4; + uint8_t bgcol:4; + uint8_t bold:1; + uint8_t uline:1; + uint8_t blink:1; + uint8_t invers:1; + uint8_t unvisible:1; +} TextAttributes; + +#define TEXT_ATTRIBUTES_DEFAULT ((TextAttributes) { \ + .fgcol = QEMU_COLOR_WHITE, \ + .bgcol = QEMU_COLOR_BLACK \ +}) + +typedef struct TextCell { + uint8_t ch; + TextAttributes t_attrib; +} TextCell; + +#define MAX_ESC_PARAMS 3 + +enum TTYState { + TTY_STATE_NORM, + TTY_STATE_ESC, + TTY_STATE_CSI, +}; + +typedef struct QemuTextConsole { + QemuConsole parent; + + int width; + int height; + int total_height; + int backscroll_height; + int x, y; + int y_displayed; + int y_base; + TextCell *cells; + int text_x[2], text_y[2], cursor_invalidate; + int echo; + + int update_x0; + int update_y0; + int update_x1; + int update_y1; + + Chardev *chr; + /* fifo for key pressed */ + Fifo8 out_fifo; +} QemuTextConsole; + +typedef QemuConsoleClass QemuTextConsoleClass; + +OBJECT_DEFINE_TYPE(QemuTextConsole, qemu_text_console, QEMU_TEXT_CONSOLE, QEMU_CONSOLE) + +typedef struct QemuFixedTextConsole { + QemuTextConsole parent; +} QemuFixedTextConsole; + +typedef QemuTextConsoleClass QemuFixedTextConsoleClass; + +OBJECT_DEFINE_TYPE(QemuFixedTextConsole, qemu_fixed_text_console, QEMU_FIXED_TEXT_CONSOLE, QEMU_TEXT_CONSOLE) + +struct VCChardev { + Chardev parent; + QemuTextConsole *console; + + enum TTYState state; + int esc_params[MAX_ESC_PARAMS]; + int nb_esc_params; + TextAttributes t_attrib; /* currently active text attributes */ + int x_saved, y_saved; +}; +typedef struct VCChardev VCChardev; + +static const pixman_color_t color_table_rgb[2][8] = { + { /* dark */ + [QEMU_COLOR_BLACK] = QEMU_PIXMAN_COLOR_BLACK, + [QEMU_COLOR_BLUE] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xaa), /* blue */ + [QEMU_COLOR_GREEN] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0x00), /* green */ + [QEMU_COLOR_CYAN] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0xaa), /* cyan */ + [QEMU_COLOR_RED] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0x00), /* red */ + [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0xaa), /* magenta */ + [QEMU_COLOR_YELLOW] = QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0x00), /* yellow */ + [QEMU_COLOR_WHITE] = QEMU_PIXMAN_COLOR_GRAY, + }, + { /* bright */ + [QEMU_COLOR_BLACK] = QEMU_PIXMAN_COLOR_BLACK, + [QEMU_COLOR_BLUE] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xff), /* blue */ + [QEMU_COLOR_GREEN] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0x00), /* green */ + [QEMU_COLOR_CYAN] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0xff), /* cyan */ + [QEMU_COLOR_RED] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0x00), /* red */ + [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0xff), /* magenta */ + [QEMU_COLOR_YELLOW] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0x00), /* yellow */ + [QEMU_COLOR_WHITE] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0xff), /* white */ + } +}; + +static bool cursor_visible_phase; +static QEMUTimer *cursor_timer; + +const char * +qemu_text_console_get_label(QemuTextConsole *c) +{ + return c->chr ? c->chr->label : NULL; +} + +static void qemu_console_fill_rect(QemuConsole *con, int posx, int posy, + int width, int height, pixman_color_t color) +{ + DisplaySurface *surface = qemu_console_surface(con); + pixman_rectangle16_t rect = { + .x = posx, .y = posy, .width = width, .height = height + }; + + assert(surface); + pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image, + &color, 1, &rect); +} + +/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */ +static void qemu_console_bitblt(QemuConsole *con, + int xs, int ys, int xd, int yd, int w, int h) +{ + DisplaySurface *surface = qemu_console_surface(con); + + assert(surface); + pixman_image_composite(PIXMAN_OP_SRC, + surface->image, NULL, surface->image, + xs, ys, 0, 0, xd, yd, w, h); +} + +static void vga_putcharxy(QemuConsole *s, int x, int y, int ch, + TextAttributes *t_attrib) +{ + static pixman_image_t *glyphs[256]; + DisplaySurface *surface = qemu_console_surface(s); + pixman_color_t fgcol, bgcol; + + assert(surface); + if (t_attrib->invers) { + bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol]; + fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol]; + } else { + fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol]; + bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol]; + } + + if (!glyphs[ch]) { + glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch); + } + qemu_pixman_glyph_render(glyphs[ch], surface->image, + &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT); +} + +static void invalidate_xy(QemuTextConsole *s, int x, int y) +{ + if (!qemu_console_is_visible(QEMU_CONSOLE(s))) { + return; + } + if (s->update_x0 > x * FONT_WIDTH) + s->update_x0 = x * FONT_WIDTH; + if (s->update_y0 > y * FONT_HEIGHT) + s->update_y0 = y * FONT_HEIGHT; + if (s->update_x1 < (x + 1) * FONT_WIDTH) + s->update_x1 = (x + 1) * FONT_WIDTH; + if (s->update_y1 < (y + 1) * FONT_HEIGHT) + s->update_y1 = (y + 1) * FONT_HEIGHT; +} + +static void console_show_cursor(QemuTextConsole *s, int show) +{ + TextCell *c; + int y, y1; + int x = s->x; + + s->cursor_invalidate = 1; + + if (x >= s->width) { + x = s->width - 1; + } + y1 = (s->y_base + s->y) % s->total_height; + y = y1 - s->y_displayed; + if (y < 0) { + y += s->total_height; + } + if (y < s->height) { + c = &s->cells[y1 * s->width + x]; + if (show && cursor_visible_phase) { + TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT; + t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */ + vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &t_attrib); + } else { + vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &(c->t_attrib)); + } + invalidate_xy(s, x, y); + } +} + +static void console_refresh(QemuTextConsole *s) +{ + DisplaySurface *surface = qemu_console_surface(QEMU_CONSOLE(s)); + TextCell *c; + int x, y, y1; + + assert(surface); + s->text_x[0] = 0; + s->text_y[0] = 0; + s->text_x[1] = s->width - 1; + s->text_y[1] = s->height - 1; + s->cursor_invalidate = 1; + + qemu_console_fill_rect(QEMU_CONSOLE(s), 0, 0, surface_width(surface), surface_height(surface), + color_table_rgb[0][QEMU_COLOR_BLACK]); + y1 = s->y_displayed; + for (y = 0; y < s->height; y++) { + c = s->cells + y1 * s->width; + for (x = 0; x < s->width; x++) { + vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, + &(c->t_attrib)); + c++; + } + if (++y1 == s->total_height) { + y1 = 0; + } + } + console_show_cursor(s, 1); + dpy_gfx_update(QEMU_CONSOLE(s), 0, 0, + surface_width(surface), surface_height(surface)); +} + +static void console_scroll(QemuTextConsole *s, int ydelta) +{ + int i, y1; + + if (ydelta > 0) { + for(i = 0; i < ydelta; i++) { + if (s->y_displayed == s->y_base) + break; + if (++s->y_displayed == s->total_height) + s->y_displayed = 0; + } + } else { + ydelta = -ydelta; + i = s->backscroll_height; + if (i > s->total_height - s->height) + i = s->total_height - s->height; + y1 = s->y_base - i; + if (y1 < 0) + y1 += s->total_height; + for(i = 0; i < ydelta; i++) { + if (s->y_displayed == y1) + break; + if (--s->y_displayed < 0) + s->y_displayed = s->total_height - 1; + } + } + console_refresh(s); +} + +static void kbd_send_chars(QemuTextConsole *s) +{ + uint32_t len, avail; + + len = qemu_chr_be_can_write(s->chr); + avail = fifo8_num_used(&s->out_fifo); + while (len > 0 && avail > 0) { + const uint8_t *buf; + uint32_t size; + + buf = fifo8_pop_buf(&s->out_fifo, MIN(len, avail), &size); + qemu_chr_be_write(s->chr, buf, size); + len = qemu_chr_be_can_write(s->chr); + avail -= size; + } +} + +/* called when an ascii key is pressed */ +void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym) +{ + uint8_t buf[16], *q; + int c; + uint32_t num_free; + + switch(keysym) { + case QEMU_KEY_CTRL_UP: + console_scroll(s, -1); + break; + case QEMU_KEY_CTRL_DOWN: + console_scroll(s, 1); + break; + case QEMU_KEY_CTRL_PAGEUP: + console_scroll(s, -10); + break; + case QEMU_KEY_CTRL_PAGEDOWN: + console_scroll(s, 10); + break; + default: + /* convert the QEMU keysym to VT100 key string */ + q = buf; + if (keysym >= 0xe100 && keysym <= 0xe11f) { + *q++ = '\033'; + *q++ = '['; + c = keysym - 0xe100; + if (c >= 10) + *q++ = '0' + (c / 10); + *q++ = '0' + (c % 10); + *q++ = '~'; + } else if (keysym >= 0xe120 && keysym <= 0xe17f) { + *q++ = '\033'; + *q++ = '['; + *q++ = keysym & 0xff; + } else if (s->echo && (keysym == '\r' || keysym == '\n')) { + qemu_chr_write(s->chr, (uint8_t *)"\r", 1, true); + *q++ = '\n'; + } else { + *q++ = keysym; + } + if (s->echo) { + qemu_chr_write(s->chr, buf, q - buf, true); + } + num_free = fifo8_num_free(&s->out_fifo); + fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf)); + kbd_send_chars(s); + break; + } +} + +static void text_console_update(void *opaque, console_ch_t *chardata) +{ + QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque); + int i, j, src; + + if (s->text_x[0] <= s->text_x[1]) { + src = (s->y_base + s->text_y[0]) * s->width; + chardata += s->text_y[0] * s->width; + for (i = s->text_y[0]; i <= s->text_y[1]; i ++) + for (j = 0; j < s->width; j++, src++) { + console_write_ch(chardata ++, + ATTR2CHTYPE(s->cells[src].ch, + s->cells[src].t_attrib.fgcol, + s->cells[src].t_attrib.bgcol, + s->cells[src].t_attrib.bold)); + } + dpy_text_update(QEMU_CONSOLE(s), s->text_x[0], s->text_y[0], + s->text_x[1] - s->text_x[0], i - s->text_y[0]); + s->text_x[0] = s->width; + s->text_y[0] = s->height; + s->text_x[1] = 0; + s->text_y[1] = 0; + } + if (s->cursor_invalidate) { + dpy_text_cursor(QEMU_CONSOLE(s), s->x, s->y); + s->cursor_invalidate = 0; + } +} + +static void text_console_resize(QemuTextConsole *t) +{ + QemuConsole *s = QEMU_CONSOLE(t); + TextCell *cells, *c, *c1; + int w1, x, y, last_width, w, h; + + assert(s->scanout.kind == SCANOUT_SURFACE); + + w = surface_width(s->surface) / FONT_WIDTH; + h = surface_height(s->surface) / FONT_HEIGHT; + if (w == t->width && h == t->height) { + return; + } + + last_width = t->width; + t->width = w; + t->height = h; + + w1 = MIN(t->width, last_width); + + cells = g_new(TextCell, t->width * t->total_height + 1); + for (y = 0; y < t->total_height; y++) { + c = &cells[y * t->width]; + if (w1 > 0) { + c1 = &t->cells[y * last_width]; + for (x = 0; x < w1; x++) { + *c++ = *c1++; + } + } + for (x = w1; x < t->width; x++) { + c->ch = ' '; + c->t_attrib = TEXT_ATTRIBUTES_DEFAULT; + c++; + } + } + g_free(t->cells); + t->cells = cells; +} + +static void vc_put_lf(VCChardev *vc) +{ + QemuTextConsole *s = vc->console; + TextCell *c; + int x, y1; + + s->y++; + if (s->y >= s->height) { + s->y = s->height - 1; + + if (s->y_displayed == s->y_base) { + if (++s->y_displayed == s->total_height) + s->y_displayed = 0; + } + if (++s->y_base == s->total_height) + s->y_base = 0; + if (s->backscroll_height < s->total_height) + s->backscroll_height++; + y1 = (s->y_base + s->height - 1) % s->total_height; + c = &s->cells[y1 * s->width]; + for(x = 0; x < s->width; x++) { + c->ch = ' '; + c->t_attrib = TEXT_ATTRIBUTES_DEFAULT; + c++; + } + if (s->y_displayed == s->y_base) { + s->text_x[0] = 0; + s->text_y[0] = 0; + s->text_x[1] = s->width - 1; + s->text_y[1] = s->height - 1; + + qemu_console_bitblt(QEMU_CONSOLE(s), 0, FONT_HEIGHT, 0, 0, + s->width * FONT_WIDTH, + (s->height - 1) * FONT_HEIGHT); + qemu_console_fill_rect(QEMU_CONSOLE(s), 0, (s->height - 1) * FONT_HEIGHT, + s->width * FONT_WIDTH, FONT_HEIGHT, + color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]); + s->update_x0 = 0; + s->update_y0 = 0; + s->update_x1 = s->width * FONT_WIDTH; + s->update_y1 = s->height * FONT_HEIGHT; + } + } +} + +/* Set console attributes depending on the current escape codes. + * NOTE: I know this code is not very efficient (checking every color for it + * self) but it is more readable and better maintainable. + */ +static void vc_handle_escape(VCChardev *vc) +{ + int i; + + for (i = 0; i < vc->nb_esc_params; i++) { + switch (vc->esc_params[i]) { + case 0: /* reset all console attributes to default */ + vc->t_attrib = TEXT_ATTRIBUTES_DEFAULT; + break; + case 1: + vc->t_attrib.bold = 1; + break; + case 4: + vc->t_attrib.uline = 1; + break; + case 5: + vc->t_attrib.blink = 1; + break; + case 7: + vc->t_attrib.invers = 1; + break; + case 8: + vc->t_attrib.unvisible = 1; + break; + case 22: + vc->t_attrib.bold = 0; + break; + case 24: + vc->t_attrib.uline = 0; + break; + case 25: + vc->t_attrib.blink = 0; + break; + case 27: + vc->t_attrib.invers = 0; + break; + case 28: + vc->t_attrib.unvisible = 0; + break; + /* set foreground color */ + case 30: + vc->t_attrib.fgcol = QEMU_COLOR_BLACK; + break; + case 31: + vc->t_attrib.fgcol = QEMU_COLOR_RED; + break; + case 32: + vc->t_attrib.fgcol = QEMU_COLOR_GREEN; + break; + case 33: + vc->t_attrib.fgcol = QEMU_COLOR_YELLOW; + break; + case 34: + vc->t_attrib.fgcol = QEMU_COLOR_BLUE; + break; + case 35: + vc->t_attrib.fgcol = QEMU_COLOR_MAGENTA; + break; + case 36: + vc->t_attrib.fgcol = QEMU_COLOR_CYAN; + break; + case 37: + vc->t_attrib.fgcol = QEMU_COLOR_WHITE; + break; + /* set background color */ + case 40: + vc->t_attrib.bgcol = QEMU_COLOR_BLACK; + break; + case 41: + vc->t_attrib.bgcol = QEMU_COLOR_RED; + break; + case 42: + vc->t_attrib.bgcol = QEMU_COLOR_GREEN; + break; + case 43: + vc->t_attrib.bgcol = QEMU_COLOR_YELLOW; + break; + case 44: + vc->t_attrib.bgcol = QEMU_COLOR_BLUE; + break; + case 45: + vc->t_attrib.bgcol = QEMU_COLOR_MAGENTA; + break; + case 46: + vc->t_attrib.bgcol = QEMU_COLOR_CYAN; + break; + case 47: + vc->t_attrib.bgcol = QEMU_COLOR_WHITE; + break; + } + } +} + +static void vc_update_xy(VCChardev *vc, int x, int y) +{ + QemuTextConsole *s = vc->console; + TextCell *c; + int y1, y2; + + s->text_x[0] = MIN(s->text_x[0], x); + s->text_x[1] = MAX(s->text_x[1], x); + s->text_y[0] = MIN(s->text_y[0], y); + s->text_y[1] = MAX(s->text_y[1], y); + + y1 = (s->y_base + y) % s->total_height; + y2 = y1 - s->y_displayed; + if (y2 < 0) { + y2 += s->total_height; + } + if (y2 < s->height) { + if (x >= s->width) { + x = s->width - 1; + } + c = &s->cells[y1 * s->width + x]; + vga_putcharxy(QEMU_CONSOLE(s), x, y2, c->ch, + &(c->t_attrib)); + invalidate_xy(s, x, y2); + } +} + +static void vc_clear_xy(VCChardev *vc, int x, int y) +{ + QemuTextConsole *s = vc->console; + int y1 = (s->y_base + y) % s->total_height; + if (x >= s->width) { + x = s->width - 1; + } + TextCell *c = &s->cells[y1 * s->width + x]; + c->ch = ' '; + c->t_attrib = TEXT_ATTRIBUTES_DEFAULT; + vc_update_xy(vc, x, y); +} + +static void vc_put_one(VCChardev *vc, int ch) +{ + QemuTextConsole *s = vc->console; + TextCell *c; + int y1; + if (s->x >= s->width) { + /* line wrap */ + s->x = 0; + vc_put_lf(vc); + } + y1 = (s->y_base + s->y) % s->total_height; + c = &s->cells[y1 * s->width + s->x]; + c->ch = ch; + c->t_attrib = vc->t_attrib; + vc_update_xy(vc, s->x, s->y); + s->x++; +} + +static void vc_respond_str(VCChardev *vc, const char *buf) +{ + while (*buf) { + vc_put_one(vc, *buf); + buf++; + } +} + +/* set cursor, checking bounds */ +static void vc_set_cursor(VCChardev *vc, int x, int y) +{ + QemuTextConsole *s = vc->console; + + if (x < 0) { + x = 0; + } + if (y < 0) { + y = 0; + } + if (y >= s->height) { + y = s->height - 1; + } + if (x >= s->width) { + x = s->width - 1; + } + + s->x = x; + s->y = y; +} + +static void vc_putchar(VCChardev *vc, int ch) +{ + QemuTextConsole *s = vc->console; + int i; + int x, y; + char response[40]; + + switch(vc->state) { + case TTY_STATE_NORM: + switch(ch) { + case '\r': /* carriage return */ + s->x = 0; + break; + case '\n': /* newline */ + vc_put_lf(vc); + break; + case '\b': /* backspace */ + if (s->x > 0) + s->x--; + break; + case '\t': /* tabspace */ + if (s->x + (8 - (s->x % 8)) > s->width) { + s->x = 0; + vc_put_lf(vc); + } else { + s->x = s->x + (8 - (s->x % 8)); + } + break; + case '\a': /* alert aka. bell */ + /* TODO: has to be implemented */ + break; + case 14: + /* SI (shift in), character set 0 (ignored) */ + break; + case 15: + /* SO (shift out), character set 1 (ignored) */ + break; + case 27: /* esc (introducing an escape sequence) */ + vc->state = TTY_STATE_ESC; + break; + default: + vc_put_one(vc, ch); + break; + } + break; + case TTY_STATE_ESC: /* check if it is a terminal escape sequence */ + if (ch == '[') { + for(i=0;iesc_params[i] = 0; + vc->nb_esc_params = 0; + vc->state = TTY_STATE_CSI; + } else { + vc->state = TTY_STATE_NORM; + } + break; + case TTY_STATE_CSI: /* handle escape sequence parameters */ + if (ch >= '0' && ch <= '9') { + if (vc->nb_esc_params < MAX_ESC_PARAMS) { + int *param = &vc->esc_params[vc->nb_esc_params]; + int digit = (ch - '0'); + + *param = (*param <= (INT_MAX - digit) / 10) ? + *param * 10 + digit : INT_MAX; + } + } else { + if (vc->nb_esc_params < MAX_ESC_PARAMS) + vc->nb_esc_params++; + if (ch == ';' || ch == '?') { + break; + } + trace_console_putchar_csi(vc->esc_params[0], vc->esc_params[1], + ch, vc->nb_esc_params); + vc->state = TTY_STATE_NORM; + switch(ch) { + case 'A': + /* move cursor up */ + if (vc->esc_params[0] == 0) { + vc->esc_params[0] = 1; + } + vc_set_cursor(vc, s->x, s->y - vc->esc_params[0]); + break; + case 'B': + /* move cursor down */ + if (vc->esc_params[0] == 0) { + vc->esc_params[0] = 1; + } + vc_set_cursor(vc, s->x, s->y + vc->esc_params[0]); + break; + case 'C': + /* move cursor right */ + if (vc->esc_params[0] == 0) { + vc->esc_params[0] = 1; + } + vc_set_cursor(vc, s->x + vc->esc_params[0], s->y); + break; + case 'D': + /* move cursor left */ + if (vc->esc_params[0] == 0) { + vc->esc_params[0] = 1; + } + vc_set_cursor(vc, s->x - vc->esc_params[0], s->y); + break; + case 'G': + /* move cursor to column */ + vc_set_cursor(vc, vc->esc_params[0] - 1, s->y); + break; + case 'f': + case 'H': + /* move cursor to row, column */ + vc_set_cursor(vc, vc->esc_params[1] - 1, vc->esc_params[0] - 1); + break; + case 'J': + switch (vc->esc_params[0]) { + case 0: + /* clear to end of screen */ + for (y = s->y; y < s->height; y++) { + for (x = 0; x < s->width; x++) { + if (y == s->y && x < s->x) { + continue; + } + vc_clear_xy(vc, x, y); + } + } + break; + case 1: + /* clear from beginning of screen */ + for (y = 0; y <= s->y; y++) { + for (x = 0; x < s->width; x++) { + if (y == s->y && x > s->x) { + break; + } + vc_clear_xy(vc, x, y); + } + } + break; + case 2: + /* clear entire screen */ + for (y = 0; y <= s->height; y++) { + for (x = 0; x < s->width; x++) { + vc_clear_xy(vc, x, y); + } + } + break; + } + break; + case 'K': + switch (vc->esc_params[0]) { + case 0: + /* clear to eol */ + for(x = s->x; x < s->width; x++) { + vc_clear_xy(vc, x, s->y); + } + break; + case 1: + /* clear from beginning of line */ + for (x = 0; x <= s->x && x < s->width; x++) { + vc_clear_xy(vc, x, s->y); + } + break; + case 2: + /* clear entire line */ + for(x = 0; x < s->width; x++) { + vc_clear_xy(vc, x, s->y); + } + break; + } + break; + case 'm': + vc_handle_escape(vc); + break; + case 'n': + switch (vc->esc_params[0]) { + case 5: + /* report console status (always succeed)*/ + vc_respond_str(vc, "\033[0n"); + break; + case 6: + /* report cursor position */ + sprintf(response, "\033[%d;%dR", + (s->y_base + s->y) % s->total_height + 1, + s->x + 1); + vc_respond_str(vc, response); + break; + } + break; + case 's': + /* save cursor position */ + vc->x_saved = s->x; + vc->y_saved = s->y; + break; + case 'u': + /* restore cursor position */ + s->x = vc->x_saved; + s->y = vc->y_saved; + break; + default: + trace_console_putchar_unhandled(ch); + break; + } + break; + } + } +} + +#define TYPE_CHARDEV_VC "chardev-vc" +DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV, + TYPE_CHARDEV_VC) + +static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len) +{ + VCChardev *drv = VC_CHARDEV(chr); + QemuTextConsole *s = drv->console; + int i; + + s->update_x0 = s->width * FONT_WIDTH; + s->update_y0 = s->height * FONT_HEIGHT; + s->update_x1 = 0; + s->update_y1 = 0; + console_show_cursor(s, 0); + for(i = 0; i < len; i++) { + vc_putchar(drv, buf[i]); + } + console_show_cursor(s, 1); + if (s->update_x0 < s->update_x1) { + dpy_gfx_update(QEMU_CONSOLE(s), s->update_x0, s->update_y0, + s->update_x1 - s->update_x0, + s->update_y1 - s->update_y0); + } + return len; +} + +void qemu_text_console_update_cursor(void) +{ + cursor_visible_phase = !cursor_visible_phase; + + if (qemu_invalidate_text_consoles()) { + timer_mod(cursor_timer, + qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2); + } +} + +static void +cursor_timer_cb(void *opaque) +{ + qemu_text_console_update_cursor(); +} + +static void text_console_invalidate(void *opaque) +{ + QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque); + + if (!QEMU_IS_FIXED_TEXT_CONSOLE(s)) { + text_console_resize(QEMU_TEXT_CONSOLE(s)); + } + console_refresh(s); +} + +static void +qemu_text_console_finalize(Object *obj) +{ +} + +static void +qemu_text_console_class_init(ObjectClass *oc, void *data) +{ + if (!cursor_timer) { + cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cursor_timer_cb, NULL); + } +} + +static const GraphicHwOps text_console_ops = { + .invalidate = text_console_invalidate, + .text_update = text_console_update, +}; + +static void +qemu_text_console_init(Object *obj) +{ + QemuTextConsole *c = QEMU_TEXT_CONSOLE(obj); + + fifo8_create(&c->out_fifo, 16); + c->total_height = DEFAULT_BACKSCROLL; + QEMU_CONSOLE(c)->hw_ops = &text_console_ops; + QEMU_CONSOLE(c)->hw = c; +} + +static void +qemu_fixed_text_console_finalize(Object *obj) +{ +} + +static void +qemu_fixed_text_console_class_init(ObjectClass *oc, void *data) +{ +} + +static void +qemu_fixed_text_console_init(Object *obj) +{ +} + +static void vc_chr_accept_input(Chardev *chr) +{ + VCChardev *drv = VC_CHARDEV(chr); + + kbd_send_chars(drv->console); +} + +static void vc_chr_set_echo(Chardev *chr, bool echo) +{ + VCChardev *drv = VC_CHARDEV(chr); + + drv->console->echo = echo; +} + +void qemu_text_console_update_size(QemuTextConsole *c) +{ + dpy_text_resize(QEMU_CONSOLE(c), c->width, c->height); +} + +static void vc_chr_open(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) +{ + ChardevVC *vc = backend->u.vc.data; + VCChardev *drv = VC_CHARDEV(chr); + QemuTextConsole *s; + unsigned width = 0; + unsigned height = 0; + + if (vc->has_width) { + width = vc->width; + } else if (vc->has_cols) { + width = vc->cols * FONT_WIDTH; + } + + if (vc->has_height) { + height = vc->height; + } else if (vc->has_rows) { + height = vc->rows * FONT_HEIGHT; + } + + trace_console_txt_new(width, height); + if (width == 0 || height == 0) { + s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE)); + width = 80 * FONT_WIDTH; + height = 24 * FONT_HEIGHT; + } else { + s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE)); + } + + dpy_gfx_replace_surface(QEMU_CONSOLE(s), qemu_create_displaysurface(width, height)); + + s->chr = chr; + drv->console = s; + + /* set current text attributes to default */ + drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT; + text_console_resize(s); + + if (chr->label) { + char *msg; + + drv->t_attrib.bgcol = QEMU_COLOR_BLUE; + msg = g_strdup_printf("%s console\r\n", chr->label); + qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true); + g_free(msg); + drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT; + } + + *be_opened = true; +} + +static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp) +{ + int val; + ChardevVC *vc; + + backend->type = CHARDEV_BACKEND_KIND_VC; + vc = backend->u.vc.data = g_new0(ChardevVC, 1); + qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc)); + + val = qemu_opt_get_number(opts, "width", 0); + if (val != 0) { + vc->has_width = true; + vc->width = val; + } + + val = qemu_opt_get_number(opts, "height", 0); + if (val != 0) { + vc->has_height = true; + vc->height = val; + } + + val = qemu_opt_get_number(opts, "cols", 0); + if (val != 0) { + vc->has_cols = true; + vc->cols = val; + } + + val = qemu_opt_get_number(opts, "rows", 0); + if (val != 0) { + vc->has_rows = true; + vc->rows = val; + } +} + +static void char_vc_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->parse = vc_chr_parse; + cc->open = vc_chr_open; + cc->chr_write = vc_chr_write; + cc->chr_accept_input = vc_chr_accept_input; + cc->chr_set_echo = vc_chr_set_echo; +} + +static const TypeInfo char_vc_type_info = { + .name = TYPE_CHARDEV_VC, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(VCChardev), + .class_init = char_vc_class_init, +}; + +void qemu_console_early_init(void) +{ + /* set the default vc driver */ + if (!object_class_by_name(TYPE_CHARDEV_VC)) { + type_register(&char_vc_type_info); + } +} diff --git a/ui/console.c b/ui/console.c index 36c80cd1de..43226c5c14 100644 --- a/ui/console.c +++ b/ui/console.c @@ -27,132 +27,55 @@ #include "hw/qdev-core.h" #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" -#include "qemu/fifo8.h" +#include "qapi/visitor.h" +#include "qemu/coroutine.h" +#include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/option.h" -#include "qemu/timer.h" #include "chardev/char.h" #include "trace.h" #include "exec/memory.h" -#include "io/channel-file.h" #include "qom/object.h" -#ifdef CONFIG_PNG -#include -#endif -#define DEFAULT_BACKSCROLL 512 -#define CONSOLE_CURSOR_PERIOD 500 +#include "console-priv.h" -typedef struct TextAttributes { - uint8_t fgcol:4; - uint8_t bgcol:4; - uint8_t bold:1; - uint8_t uline:1; - uint8_t blink:1; - uint8_t invers:1; - uint8_t unvisible:1; -} TextAttributes; +OBJECT_DEFINE_ABSTRACT_TYPE(QemuConsole, qemu_console, QEMU_CONSOLE, OBJECT) -typedef struct TextCell { - uint8_t ch; - TextAttributes t_attrib; -} TextCell; +typedef struct QemuGraphicConsole { + QemuConsole parent; -#define MAX_ESC_PARAMS 3 - -enum TTYState { - TTY_STATE_NORM, - TTY_STATE_ESC, - TTY_STATE_CSI, -}; - -typedef enum { - GRAPHIC_CONSOLE, - TEXT_CONSOLE, - TEXT_CONSOLE_FIXED_SIZE -} console_type_t; - -struct QemuConsole { - Object parent; - - int index; - console_type_t console_type; - DisplayState *ds; - DisplaySurface *surface; - DisplayScanout scanout; - int dcls; - DisplayGLCtx *gl; - int gl_block; - QEMUTimer *gl_unblock_timer; - int window_id; - - /* Graphic console state. */ Object *device; uint32_t head; - QemuUIInfo ui_info; - QEMUTimer *ui_timer; - const GraphicHwOps *hw_ops; - void *hw; - /* Text console state */ - int width; - int height; - int total_height; - int backscroll_height; - int x, y; - int x_saved, y_saved; - int y_displayed; - int y_base; - TextAttributes t_attrib_default; /* default text attributes */ - TextAttributes t_attrib; /* currently active text attributes */ - TextCell *cells; - int text_x[2], text_y[2], cursor_invalidate; - int echo; + QEMUCursor *cursor; + int cursor_x, cursor_y, cursor_on; +} QemuGraphicConsole; - int update_x0; - int update_y0; - int update_x1; - int update_y1; +typedef QemuConsoleClass QemuGraphicConsoleClass; - enum TTYState state; - int esc_params[MAX_ESC_PARAMS]; - int nb_esc_params; - - Chardev *chr; - /* fifo for key pressed */ - Fifo8 out_fifo; - CoQueue dump_queue; - - QTAILQ_ENTRY(QemuConsole) next; -}; +OBJECT_DEFINE_TYPE(QemuGraphicConsole, qemu_graphic_console, QEMU_GRAPHIC_CONSOLE, QEMU_CONSOLE) struct DisplayState { QEMUTimer *gui_timer; uint64_t last_update; uint64_t update_interval; bool refreshing; - bool have_gfx; - bool have_text; QLIST_HEAD(, DisplayChangeListener) listeners; }; static DisplayState *display_state; -static QemuConsole *active_console; static QTAILQ_HEAD(, QemuConsole) consoles = QTAILQ_HEAD_INITIALIZER(consoles); -static bool cursor_visible_phase; -static QEMUTimer *cursor_timer; -static void text_console_do_init(Chardev *chr, DisplayState *ds); 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 displaychangelistener_has_dmabuf(DisplayChangeListener *dcl); static bool console_compatible_with(QemuConsole *con, DisplayChangeListener *dcl, Error **errp); +static QemuConsole *qemu_graphic_console_lookup_unused(void); +static void dpy_set_ui_info_timer(void *opaque); static void gui_update(void *opaque) { @@ -160,7 +83,6 @@ static void gui_update(void *opaque) uint64_t dcl_interval; DisplayState *ds = opaque; DisplayChangeListener *dcl; - QemuConsole *con; ds->refreshing = true; dpy_refresh(ds); @@ -175,11 +97,6 @@ static void gui_update(void *opaque) } if (ds->update_interval != interval) { ds->update_interval = interval; - QTAILQ_FOREACH(con, &consoles, next) { - if (con->hw_ops->update_interval) { - con->hw_ops->update_interval(con->hw, interval); - } - } trace_console_refresh(interval); } ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); @@ -190,19 +107,11 @@ static void gui_setup_refresh(DisplayState *ds) { DisplayChangeListener *dcl; bool need_timer = false; - bool have_gfx = false; - bool have_text = false; QLIST_FOREACH(dcl, &ds->listeners, next) { if (dcl->ops->dpy_refresh != NULL) { need_timer = true; } - if (dcl->ops->dpy_gfx_update != NULL) { - have_gfx = true; - } - if (dcl->ops->dpy_text_update != NULL) { - have_text = true; - } } if (need_timer && ds->gui_timer == NULL) { @@ -213,9 +122,6 @@ static void gui_setup_refresh(DisplayState *ds) timer_free(ds->gui_timer); ds->gui_timer = NULL; } - - ds->have_gfx = have_gfx; - ds->have_text = have_text; } void graphic_hw_update_done(QemuConsole *con) @@ -228,7 +134,6 @@ void graphic_hw_update_done(QemuConsole *con) void graphic_hw_update(QemuConsole *con) { bool async = false; - con = con ? con : active_console; if (!con) { return; } @@ -241,6 +146,22 @@ void graphic_hw_update(QemuConsole *con) } } +static void graphic_hw_update_bh(void *con) +{ + graphic_hw_update(con); +} + +void qemu_console_co_wait_update(QemuConsole *con) +{ + if (qemu_co_queue_empty(&con->dump_queue)) { + /* Defer the update, it will restart the pending coroutines */ + aio_bh_schedule_oneshot(qemu_get_aio_context(), + graphic_hw_update_bh, con); + } + qemu_co_queue_wait(&con->dump_queue, NULL); + +} + static void graphic_hw_gl_unblock_timer(void *opaque) { warn_report("console: no gl-unblock within one second"); @@ -286,873 +207,18 @@ void qemu_console_set_window_id(QemuConsole *con, int window_id) void graphic_hw_invalidate(QemuConsole *con) { - if (!con) { - con = active_console; - } if (con && con->hw_ops->invalidate) { con->hw_ops->invalidate(con->hw); } } -#ifdef CONFIG_PNG -/** - * png_save: Take a screenshot as PNG - * - * Saves screendump as a PNG file - * - * Returns true for success or false for error. - * - * @fd: File descriptor for PNG file. - * @image: Image data in pixman format. - * @errp: Pointer to an error. - */ -static bool png_save(int fd, pixman_image_t *image, Error **errp) -{ - int width = pixman_image_get_width(image); - int height = pixman_image_get_height(image); - g_autofree png_struct *png_ptr = NULL; - g_autofree png_info *info_ptr = NULL; - g_autoptr(pixman_image_t) linebuf = - qemu_pixman_linebuf_create(PIXMAN_a8r8g8b8, width); - uint8_t *buf = (uint8_t *)pixman_image_get_data(linebuf); - FILE *f = fdopen(fd, "wb"); - int y; - if (!f) { - error_setg_errno(errp, errno, - "Failed to create file from file descriptor"); - return false; - } - - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, - NULL, NULL); - if (!png_ptr) { - error_setg(errp, "PNG creation failed. Unable to write struct"); - fclose(f); - return false; - } - - info_ptr = png_create_info_struct(png_ptr); - - if (!info_ptr) { - error_setg(errp, "PNG creation failed. Unable to write info"); - fclose(f); - png_destroy_write_struct(&png_ptr, &info_ptr); - return false; - } - - png_init_io(png_ptr, f); - - png_set_IHDR(png_ptr, info_ptr, width, height, 8, - PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - - png_write_info(png_ptr, info_ptr); - - for (y = 0; y < height; ++y) { - qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); - png_write_row(png_ptr, buf); - } - qemu_pixman_image_unref(linebuf); - - png_write_end(png_ptr, NULL); - - png_destroy_write_struct(&png_ptr, &info_ptr); - - if (fclose(f) != 0) { - error_setg_errno(errp, errno, - "PNG creation failed. Unable to close file"); - return false; - } - - return true; -} - -#else /* no png support */ - -static bool png_save(int fd, pixman_image_t *image, Error **errp) -{ - error_setg(errp, "Enable PNG support with libpng for screendump"); - return false; -} - -#endif /* CONFIG_PNG */ - -static bool ppm_save(int fd, pixman_image_t *image, Error **errp) -{ - int width = pixman_image_get_width(image); - int height = pixman_image_get_height(image); - 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; - - trace_ppm_save(fd, image); - - 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, image, width, 0, y); - if (qio_channel_write_all(QIO_CHANNEL(ioc), - (char *)pixman_image_get_data(linebuf), - pixman_image_get_stride(linebuf), errp) < 0) { - return false; - } - } - - return true; -} - -static void graphic_hw_update_bh(void *con) -{ - graphic_hw_update(con); -} - -/* Safety: coroutine-only, concurrent-coroutine safe, main thread only */ -void coroutine_fn -qmp_screendump(const char *filename, bool has_device, const char *device, - bool has_head, int64_t head, - bool has_format, ImageFormat format, Error **errp) -{ - g_autoptr(pixman_image_t) image = NULL; - QemuConsole *con; - DisplaySurface *surface; - int fd; - - if (has_device) { - con = qemu_console_lookup_by_device_name(device, has_head ? head : 0, - errp); - if (!con) { - return; - } - } else { - if (has_head) { - error_setg(errp, "'head' must be specified together with 'device'"); - return; - } - con = qemu_console_lookup_by_index(0); - if (!con) { - error_setg(errp, "There is no console to take a screendump from"); - return; - } - } - - if (qemu_co_queue_empty(&con->dump_queue)) { - /* Defer the update, it will restart the pending coroutines */ - aio_bh_schedule_oneshot(qemu_get_aio_context(), - graphic_hw_update_bh, con); - } - qemu_co_queue_wait(&con->dump_queue, NULL); - - /* - * All pending coroutines are woken up, while the BQL is held. No - * further graphic update are possible until it is released. Take - * an image ref before that. - */ - surface = qemu_console_surface(con); - if (!surface) { - error_setg(errp, "no surface"); - return; - } - image = pixman_image_ref(surface->image); - - fd = qemu_open_old(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; - } - - /* - * The image content could potentially be updated as the coroutine - * yields and releases the BQL. It could produce corrupted dump, but - * it should be otherwise safe. - */ - if (has_format && format == IMAGE_FORMAT_PNG) { - /* PNG format specified for screendump */ - if (!png_save(fd, image, errp)) { - qemu_unlink(filename); - } - } else { - /* PPM format specified/default for screendump */ - if (!ppm_save(fd, image, errp)) { - qemu_unlink(filename); - } - } -} - void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata) { - if (!con) { - con = active_console; - } if (con && con->hw_ops->text_update) { con->hw_ops->text_update(con->hw, chardata); } } -static void vga_fill_rect(QemuConsole *con, - int posx, int posy, int width, int height, - pixman_color_t color) -{ - DisplaySurface *surface = qemu_console_surface(con); - pixman_rectangle16_t rect = { - .x = posx, .y = posy, .width = width, .height = height - }; - - pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image, - &color, 1, &rect); -} - -/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */ -static void vga_bitblt(QemuConsole *con, - int xs, int ys, int xd, int yd, int w, int h) -{ - DisplaySurface *surface = qemu_console_surface(con); - - pixman_image_composite(PIXMAN_OP_SRC, - surface->image, NULL, surface->image, - xs, ys, 0, 0, xd, yd, w, h); -} - -/***********************************************************/ -/* basic char display */ - -#define FONT_HEIGHT 16 -#define FONT_WIDTH 8 - -#include "vgafont.h" - -#define QEMU_RGB(r, g, b) \ - { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff } - -static const pixman_color_t color_table_rgb[2][8] = { - { /* dark */ - [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */ - [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xaa), /* blue */ - [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xaa, 0x00), /* green */ - [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */ - [QEMU_COLOR_RED] = QEMU_RGB(0xaa, 0x00, 0x00), /* red */ - [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */ - [QEMU_COLOR_YELLOW] = QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */ - [QEMU_COLOR_WHITE] = QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */ - }, - { /* bright */ - [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */ - [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xff), /* blue */ - [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xff, 0x00), /* green */ - [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xff, 0xff), /* cyan */ - [QEMU_COLOR_RED] = QEMU_RGB(0xff, 0x00, 0x00), /* red */ - [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xff, 0x00, 0xff), /* magenta */ - [QEMU_COLOR_YELLOW] = QEMU_RGB(0xff, 0xff, 0x00), /* yellow */ - [QEMU_COLOR_WHITE] = QEMU_RGB(0xff, 0xff, 0xff), /* white */ - } -}; - -static void vga_putcharxy(QemuConsole *s, int x, int y, int ch, - TextAttributes *t_attrib) -{ - static pixman_image_t *glyphs[256]; - DisplaySurface *surface = qemu_console_surface(s); - pixman_color_t fgcol, bgcol; - - if (t_attrib->invers) { - bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol]; - fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol]; - } else { - fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol]; - bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol]; - } - - if (!glyphs[ch]) { - glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch); - } - qemu_pixman_glyph_render(glyphs[ch], surface->image, - &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT); -} - -static void text_console_resize(QemuConsole *s) -{ - TextCell *cells, *c, *c1; - int w1, x, y, last_width; - - assert(s->scanout.kind == SCANOUT_SURFACE); - - last_width = s->width; - s->width = surface_width(s->surface) / FONT_WIDTH; - s->height = surface_height(s->surface) / FONT_HEIGHT; - - w1 = last_width; - if (s->width < w1) - w1 = s->width; - - cells = g_new(TextCell, s->width * s->total_height + 1); - for(y = 0; y < s->total_height; y++) { - c = &cells[y * s->width]; - if (w1 > 0) { - c1 = &s->cells[y * last_width]; - for(x = 0; x < w1; x++) { - *c++ = *c1++; - } - } - for(x = w1; x < s->width; x++) { - c->ch = ' '; - c->t_attrib = s->t_attrib_default; - c++; - } - } - g_free(s->cells); - s->cells = cells; -} - -static inline void text_update_xy(QemuConsole *s, int x, int y) -{ - s->text_x[0] = MIN(s->text_x[0], x); - s->text_x[1] = MAX(s->text_x[1], x); - s->text_y[0] = MIN(s->text_y[0], y); - s->text_y[1] = MAX(s->text_y[1], y); -} - -static void invalidate_xy(QemuConsole *s, int x, int y) -{ - if (!qemu_console_is_visible(s)) { - return; - } - if (s->update_x0 > x * FONT_WIDTH) - s->update_x0 = x * FONT_WIDTH; - if (s->update_y0 > y * FONT_HEIGHT) - s->update_y0 = y * FONT_HEIGHT; - if (s->update_x1 < (x + 1) * FONT_WIDTH) - s->update_x1 = (x + 1) * FONT_WIDTH; - if (s->update_y1 < (y + 1) * FONT_HEIGHT) - s->update_y1 = (y + 1) * FONT_HEIGHT; -} - -static void update_xy(QemuConsole *s, int x, int y) -{ - TextCell *c; - int y1, y2; - - if (s->ds->have_text) { - text_update_xy(s, x, y); - } - - y1 = (s->y_base + y) % s->total_height; - y2 = y1 - s->y_displayed; - if (y2 < 0) { - y2 += s->total_height; - } - if (y2 < s->height) { - if (x >= s->width) { - x = s->width - 1; - } - c = &s->cells[y1 * s->width + x]; - vga_putcharxy(s, x, y2, c->ch, - &(c->t_attrib)); - invalidate_xy(s, x, y2); - } -} - -static void console_show_cursor(QemuConsole *s, int show) -{ - TextCell *c; - int y, y1; - int x = s->x; - - if (s->ds->have_text) { - s->cursor_invalidate = 1; - } - - if (x >= s->width) { - x = s->width - 1; - } - y1 = (s->y_base + s->y) % s->total_height; - y = y1 - s->y_displayed; - if (y < 0) { - y += s->total_height; - } - if (y < s->height) { - c = &s->cells[y1 * s->width + x]; - if (show && cursor_visible_phase) { - TextAttributes t_attrib = s->t_attrib_default; - t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */ - vga_putcharxy(s, x, y, c->ch, &t_attrib); - } else { - vga_putcharxy(s, x, y, c->ch, &(c->t_attrib)); - } - invalidate_xy(s, x, y); - } -} - -static void console_refresh(QemuConsole *s) -{ - DisplaySurface *surface = qemu_console_surface(s); - TextCell *c; - int x, y, y1; - - if (s->ds->have_text) { - s->text_x[0] = 0; - s->text_y[0] = 0; - s->text_x[1] = s->width - 1; - s->text_y[1] = s->height - 1; - s->cursor_invalidate = 1; - } - - vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface), - color_table_rgb[0][QEMU_COLOR_BLACK]); - y1 = s->y_displayed; - for (y = 0; y < s->height; y++) { - c = s->cells + y1 * s->width; - for (x = 0; x < s->width; x++) { - vga_putcharxy(s, x, y, c->ch, - &(c->t_attrib)); - c++; - } - if (++y1 == s->total_height) { - y1 = 0; - } - } - console_show_cursor(s, 1); - dpy_gfx_update(s, 0, 0, - surface_width(surface), surface_height(surface)); -} - -static void console_scroll(QemuConsole *s, int ydelta) -{ - int i, y1; - - if (ydelta > 0) { - for(i = 0; i < ydelta; i++) { - if (s->y_displayed == s->y_base) - break; - if (++s->y_displayed == s->total_height) - s->y_displayed = 0; - } - } else { - ydelta = -ydelta; - i = s->backscroll_height; - if (i > s->total_height - s->height) - i = s->total_height - s->height; - y1 = s->y_base - i; - if (y1 < 0) - y1 += s->total_height; - for(i = 0; i < ydelta; i++) { - if (s->y_displayed == y1) - break; - if (--s->y_displayed < 0) - s->y_displayed = s->total_height - 1; - } - } - console_refresh(s); -} - -static void console_put_lf(QemuConsole *s) -{ - TextCell *c; - int x, y1; - - s->y++; - if (s->y >= s->height) { - s->y = s->height - 1; - - if (s->y_displayed == s->y_base) { - if (++s->y_displayed == s->total_height) - s->y_displayed = 0; - } - if (++s->y_base == s->total_height) - s->y_base = 0; - if (s->backscroll_height < s->total_height) - s->backscroll_height++; - y1 = (s->y_base + s->height - 1) % s->total_height; - c = &s->cells[y1 * s->width]; - for(x = 0; x < s->width; x++) { - c->ch = ' '; - c->t_attrib = s->t_attrib_default; - c++; - } - if (s->y_displayed == s->y_base) { - if (s->ds->have_text) { - s->text_x[0] = 0; - s->text_y[0] = 0; - s->text_x[1] = s->width - 1; - s->text_y[1] = s->height - 1; - } - - vga_bitblt(s, 0, FONT_HEIGHT, 0, 0, - s->width * FONT_WIDTH, - (s->height - 1) * FONT_HEIGHT); - vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT, - s->width * FONT_WIDTH, FONT_HEIGHT, - color_table_rgb[0][s->t_attrib_default.bgcol]); - s->update_x0 = 0; - s->update_y0 = 0; - s->update_x1 = s->width * FONT_WIDTH; - s->update_y1 = s->height * FONT_HEIGHT; - } - } -} - -/* Set console attributes depending on the current escape codes. - * NOTE: I know this code is not very efficient (checking every color for it - * self) but it is more readable and better maintainable. - */ -static void console_handle_escape(QemuConsole *s) -{ - int i; - - for (i=0; inb_esc_params; i++) { - switch (s->esc_params[i]) { - case 0: /* reset all console attributes to default */ - s->t_attrib = s->t_attrib_default; - break; - case 1: - s->t_attrib.bold = 1; - break; - case 4: - s->t_attrib.uline = 1; - break; - case 5: - s->t_attrib.blink = 1; - break; - case 7: - s->t_attrib.invers = 1; - break; - case 8: - s->t_attrib.unvisible = 1; - break; - case 22: - s->t_attrib.bold = 0; - break; - case 24: - s->t_attrib.uline = 0; - break; - case 25: - s->t_attrib.blink = 0; - break; - case 27: - s->t_attrib.invers = 0; - break; - case 28: - s->t_attrib.unvisible = 0; - break; - /* set foreground color */ - case 30: - s->t_attrib.fgcol = QEMU_COLOR_BLACK; - break; - case 31: - s->t_attrib.fgcol = QEMU_COLOR_RED; - break; - case 32: - s->t_attrib.fgcol = QEMU_COLOR_GREEN; - break; - case 33: - s->t_attrib.fgcol = QEMU_COLOR_YELLOW; - break; - case 34: - s->t_attrib.fgcol = QEMU_COLOR_BLUE; - break; - case 35: - s->t_attrib.fgcol = QEMU_COLOR_MAGENTA; - break; - case 36: - s->t_attrib.fgcol = QEMU_COLOR_CYAN; - break; - case 37: - s->t_attrib.fgcol = QEMU_COLOR_WHITE; - break; - /* set background color */ - case 40: - s->t_attrib.bgcol = QEMU_COLOR_BLACK; - break; - case 41: - s->t_attrib.bgcol = QEMU_COLOR_RED; - break; - case 42: - s->t_attrib.bgcol = QEMU_COLOR_GREEN; - break; - case 43: - s->t_attrib.bgcol = QEMU_COLOR_YELLOW; - break; - case 44: - s->t_attrib.bgcol = QEMU_COLOR_BLUE; - break; - case 45: - s->t_attrib.bgcol = QEMU_COLOR_MAGENTA; - break; - case 46: - s->t_attrib.bgcol = QEMU_COLOR_CYAN; - break; - case 47: - s->t_attrib.bgcol = QEMU_COLOR_WHITE; - break; - } - } -} - -static void console_clear_xy(QemuConsole *s, int x, int y) -{ - int y1 = (s->y_base + y) % s->total_height; - if (x >= s->width) { - x = s->width - 1; - } - TextCell *c = &s->cells[y1 * s->width + x]; - c->ch = ' '; - c->t_attrib = s->t_attrib_default; - update_xy(s, x, y); -} - -static void console_put_one(QemuConsole *s, int ch) -{ - TextCell *c; - int y1; - if (s->x >= s->width) { - /* line wrap */ - s->x = 0; - console_put_lf(s); - } - y1 = (s->y_base + s->y) % s->total_height; - c = &s->cells[y1 * s->width + s->x]; - c->ch = ch; - c->t_attrib = s->t_attrib; - update_xy(s, s->x, s->y); - s->x++; -} - -static void console_respond_str(QemuConsole *s, const char *buf) -{ - while (*buf) { - console_put_one(s, *buf); - buf++; - } -} - -/* set cursor, checking bounds */ -static void set_cursor(QemuConsole *s, int x, int y) -{ - if (x < 0) { - x = 0; - } - if (y < 0) { - y = 0; - } - if (y >= s->height) { - y = s->height - 1; - } - if (x >= s->width) { - x = s->width - 1; - } - - s->x = x; - s->y = y; -} - -static void console_putchar(QemuConsole *s, int ch) -{ - int i; - int x, y; - char response[40]; - - switch(s->state) { - case TTY_STATE_NORM: - switch(ch) { - case '\r': /* carriage return */ - s->x = 0; - break; - case '\n': /* newline */ - console_put_lf(s); - break; - case '\b': /* backspace */ - if (s->x > 0) - s->x--; - break; - case '\t': /* tabspace */ - if (s->x + (8 - (s->x % 8)) > s->width) { - s->x = 0; - console_put_lf(s); - } else { - s->x = s->x + (8 - (s->x % 8)); - } - break; - case '\a': /* alert aka. bell */ - /* TODO: has to be implemented */ - break; - case 14: - /* SI (shift in), character set 0 (ignored) */ - break; - case 15: - /* SO (shift out), character set 1 (ignored) */ - break; - case 27: /* esc (introducing an escape sequence) */ - s->state = TTY_STATE_ESC; - break; - default: - console_put_one(s, ch); - break; - } - break; - case TTY_STATE_ESC: /* check if it is a terminal escape sequence */ - if (ch == '[') { - for(i=0;iesc_params[i] = 0; - s->nb_esc_params = 0; - s->state = TTY_STATE_CSI; - } else { - s->state = TTY_STATE_NORM; - } - break; - case TTY_STATE_CSI: /* handle escape sequence parameters */ - if (ch >= '0' && ch <= '9') { - if (s->nb_esc_params < MAX_ESC_PARAMS) { - int *param = &s->esc_params[s->nb_esc_params]; - int digit = (ch - '0'); - - *param = (*param <= (INT_MAX - digit) / 10) ? - *param * 10 + digit : INT_MAX; - } - } else { - if (s->nb_esc_params < MAX_ESC_PARAMS) - s->nb_esc_params++; - if (ch == ';' || ch == '?') { - break; - } - trace_console_putchar_csi(s->esc_params[0], s->esc_params[1], - ch, s->nb_esc_params); - s->state = TTY_STATE_NORM; - switch(ch) { - case 'A': - /* move cursor up */ - if (s->esc_params[0] == 0) { - s->esc_params[0] = 1; - } - set_cursor(s, s->x, s->y - s->esc_params[0]); - break; - case 'B': - /* move cursor down */ - if (s->esc_params[0] == 0) { - s->esc_params[0] = 1; - } - set_cursor(s, s->x, s->y + s->esc_params[0]); - break; - case 'C': - /* move cursor right */ - if (s->esc_params[0] == 0) { - s->esc_params[0] = 1; - } - set_cursor(s, s->x + s->esc_params[0], s->y); - break; - case 'D': - /* move cursor left */ - if (s->esc_params[0] == 0) { - s->esc_params[0] = 1; - } - set_cursor(s, s->x - s->esc_params[0], s->y); - break; - case 'G': - /* move cursor to column */ - set_cursor(s, s->esc_params[0] - 1, s->y); - break; - case 'f': - case 'H': - /* move cursor to row, column */ - set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1); - break; - case 'J': - switch (s->esc_params[0]) { - case 0: - /* clear to end of screen */ - for (y = s->y; y < s->height; y++) { - for (x = 0; x < s->width; x++) { - if (y == s->y && x < s->x) { - continue; - } - console_clear_xy(s, x, y); - } - } - break; - case 1: - /* clear from beginning of screen */ - for (y = 0; y <= s->y; y++) { - for (x = 0; x < s->width; x++) { - if (y == s->y && x > s->x) { - break; - } - console_clear_xy(s, x, y); - } - } - break; - case 2: - /* clear entire screen */ - for (y = 0; y <= s->height; y++) { - for (x = 0; x < s->width; x++) { - console_clear_xy(s, x, y); - } - } - break; - } - break; - case 'K': - switch (s->esc_params[0]) { - case 0: - /* clear to eol */ - for(x = s->x; x < s->width; x++) { - console_clear_xy(s, x, s->y); - } - break; - case 1: - /* clear from beginning of line */ - for (x = 0; x <= s->x && x < s->width; x++) { - console_clear_xy(s, x, s->y); - } - break; - case 2: - /* clear entire line */ - for(x = 0; x < s->width; x++) { - console_clear_xy(s, x, s->y); - } - break; - } - break; - case 'm': - console_handle_escape(s); - break; - case 'n': - switch (s->esc_params[0]) { - case 5: - /* report console status (always succeed)*/ - console_respond_str(s, "\033[0n"); - break; - case 6: - /* report cursor position */ - sprintf(response, "\033[%d;%dR", - (s->y_base + s->y) % s->total_height + 1, - s->x + 1); - console_respond_str(s, response); - break; - } - break; - case 's': - /* save cursor position */ - s->x_saved = s->x; - s->y_saved = s->y; - break; - case 'u': - /* restore cursor position */ - s->x = s->x_saved; - s->y = s->y_saved; - break; - default: - trace_console_putchar_unhandled(ch); - break; - } - break; - } - } -} - static void displaychangelistener_gfx_switch(DisplayChangeListener *dcl, struct DisplaySurface *new_surface, bool update) @@ -1191,12 +257,12 @@ static void dpy_gfx_update_texture(QemuConsole *con, DisplaySurface *surface, } static void displaychangelistener_display_console(DisplayChangeListener *dcl, - QemuConsole *con, Error **errp) { static const char nodev[] = "This VM has no graphic display device."; static DisplaySurface *dummy; + QemuConsole *con = dcl->con; if (!con || !console_compatible_with(con, dcl, errp)) { if (!dummy) { @@ -1226,142 +292,14 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl, con->scanout.texture.x, con->scanout.texture.y, con->scanout.texture.width, - con->scanout.texture.height); + con->scanout.texture.height, + con->scanout.texture.d3d_tex2d); } } -void console_select(unsigned int index) +void qemu_text_console_put_keysym(QemuTextConsole *s, int keysym) { - DisplayChangeListener *dcl; - QemuConsole *s; - - trace_console_select(index); - s = qemu_console_lookup_by_index(index); - if (s) { - DisplayState *ds = s->ds; - - active_console = s; - if (ds->have_gfx) { - QLIST_FOREACH(dcl, &ds->listeners, next) { - if (dcl->con != NULL) { - continue; - } - displaychangelistener_display_console(dcl, s, NULL); - } - } - if (ds->have_text) { - dpy_text_resize(s, s->width, s->height); - } - text_console_update_cursor(NULL); - } -} - -struct VCChardev { - Chardev parent; - QemuConsole *console; -}; -typedef struct VCChardev VCChardev; - -#define TYPE_CHARDEV_VC "chardev-vc" -DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV, - TYPE_CHARDEV_VC) - -static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len) -{ - VCChardev *drv = VC_CHARDEV(chr); - QemuConsole *s = drv->console; - int i; - - if (!s->ds) { - return 0; - } - - s->update_x0 = s->width * FONT_WIDTH; - s->update_y0 = s->height * FONT_HEIGHT; - s->update_x1 = 0; - s->update_y1 = 0; - console_show_cursor(s, 0); - for(i = 0; i < len; i++) { - console_putchar(s, buf[i]); - } - console_show_cursor(s, 1); - if (s->ds->have_gfx && s->update_x0 < s->update_x1) { - dpy_gfx_update(s, s->update_x0, s->update_y0, - s->update_x1 - s->update_x0, - s->update_y1 - s->update_y0); - } - return len; -} - -static void kbd_send_chars(QemuConsole *s) -{ - uint32_t len, avail; - - len = qemu_chr_be_can_write(s->chr); - avail = fifo8_num_used(&s->out_fifo); - while (len > 0 && avail > 0) { - const uint8_t *buf; - uint32_t size; - - buf = fifo8_pop_buf(&s->out_fifo, MIN(len, avail), &size); - qemu_chr_be_write(s->chr, (uint8_t *)buf, size); - len = qemu_chr_be_can_write(s->chr); - avail -= size; - } -} - -/* called when an ascii key is pressed */ -void kbd_put_keysym_console(QemuConsole *s, int keysym) -{ - uint8_t buf[16], *q; - int c; - uint32_t num_free; - - if (!s || (s->console_type == GRAPHIC_CONSOLE)) - return; - - switch(keysym) { - case QEMU_KEY_CTRL_UP: - console_scroll(s, -1); - break; - case QEMU_KEY_CTRL_DOWN: - console_scroll(s, 1); - break; - case QEMU_KEY_CTRL_PAGEUP: - console_scroll(s, -10); - break; - case QEMU_KEY_CTRL_PAGEDOWN: - console_scroll(s, 10); - break; - default: - /* convert the QEMU keysym to VT100 key string */ - q = buf; - if (keysym >= 0xe100 && keysym <= 0xe11f) { - *q++ = '\033'; - *q++ = '['; - c = keysym - 0xe100; - if (c >= 10) - *q++ = '0' + (c / 10); - *q++ = '0' + (c % 10); - *q++ = '~'; - } else if (keysym >= 0xe120 && keysym <= 0xe17f) { - *q++ = '\033'; - *q++ = '['; - *q++ = keysym & 0xff; - } else if (s->echo && (keysym == '\r' || keysym == '\n')) { - vc_chr_write(s->chr, (const uint8_t *) "\r", 1); - *q++ = '\n'; - } else { - *q++ = keysym; - } - if (s->echo) { - vc_chr_write(s->chr, buf, q - buf); - } - num_free = fifo8_num_free(&s->out_fifo); - fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf)); - kbd_send_chars(s); - break; - } + qemu_text_console_handle_keysym(s, keysym); } static const int qcode_to_keysym[Q_KEY_CODE__MAX] = { @@ -1374,6 +312,7 @@ static const int qcode_to_keysym[Q_KEY_CODE__MAX] = { [Q_KEY_CODE_PGUP] = QEMU_KEY_PAGEUP, [Q_KEY_CODE_PGDN] = QEMU_KEY_PAGEDOWN, [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE, + [Q_KEY_CODE_TAB] = QEMU_KEY_TAB, [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE, }; @@ -1388,7 +327,7 @@ static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = { [Q_KEY_CODE_PGDN] = QEMU_KEY_CTRL_PAGEDOWN, }; -bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl) +bool qemu_text_console_put_qcode(QemuTextConsole *s, int qcode, bool ctrl) { int keysym; @@ -1396,96 +335,31 @@ bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl) if (keysym == 0) { return false; } - kbd_put_keysym_console(s, keysym); + qemu_text_console_put_keysym(s, keysym); return true; } -void kbd_put_string_console(QemuConsole *s, const char *str, int len) +void qemu_text_console_put_string(QemuTextConsole *s, const char *str, int len) { int i; for (i = 0; i < len && str[i]; i++) { - kbd_put_keysym_console(s, str[i]); + qemu_text_console_put_keysym(s, str[i]); } } -void kbd_put_keysym(int keysym) +static void +qemu_console_register(QemuConsole *c) { - kbd_put_keysym_console(active_console, keysym); -} - -static void text_console_invalidate(void *opaque) -{ - QemuConsole *s = (QemuConsole *) opaque; - - if (s->ds->have_text && s->console_type == TEXT_CONSOLE) { - text_console_resize(s); - } - console_refresh(s); -} - -static void text_console_update(void *opaque, console_ch_t *chardata) -{ - QemuConsole *s = (QemuConsole *) opaque; - int i, j, src; - - if (s->text_x[0] <= s->text_x[1]) { - src = (s->y_base + s->text_y[0]) * s->width; - chardata += s->text_y[0] * s->width; - for (i = s->text_y[0]; i <= s->text_y[1]; i ++) - for (j = 0; j < s->width; j++, src++) { - console_write_ch(chardata ++, - ATTR2CHTYPE(s->cells[src].ch, - s->cells[src].t_attrib.fgcol, - s->cells[src].t_attrib.bgcol, - s->cells[src].t_attrib.bold)); - } - dpy_text_update(s, s->text_x[0], s->text_y[0], - s->text_x[1] - s->text_x[0], i - s->text_y[0]); - s->text_x[0] = s->width; - s->text_y[0] = s->height; - s->text_x[1] = 0; - s->text_y[1] = 0; - } - if (s->cursor_invalidate) { - dpy_text_cursor(s, s->x, s->y); - s->cursor_invalidate = 0; - } -} - -static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, - uint32_t head) -{ - Object *obj; - QemuConsole *s; int i; - obj = object_new(TYPE_QEMU_CONSOLE); - s = QEMU_CONSOLE(obj); - qemu_co_queue_init(&s->dump_queue); - s->head = head; - object_property_add_link(obj, "device", TYPE_DEVICE, - (Object **)&s->device, - object_property_allow_set_link, - OBJ_PROP_LINK_STRONG); - object_property_add_uint32_ptr(obj, "head", &s->head, - OBJ_PROP_FLAG_READ); - - if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && - (console_type == GRAPHIC_CONSOLE))) { - active_console = s; - } - s->ds = ds; - s->console_type = console_type; - s->window_id = -1; - if (QTAILQ_EMPTY(&consoles)) { - s->index = 0; - QTAILQ_INSERT_TAIL(&consoles, s, next); - } else if (console_type != GRAPHIC_CONSOLE || phase_check(PHASE_MACHINE_READY)) { + c->index = 0; + QTAILQ_INSERT_TAIL(&consoles, c, next); + } else if (!QEMU_IS_GRAPHIC_CONSOLE(c) || phase_check(PHASE_MACHINE_READY)) { QemuConsole *last = QTAILQ_LAST(&consoles); - s->index = last->index + 1; - QTAILQ_INSERT_TAIL(&consoles, s, next); + c->index = last->index + 1; + QTAILQ_INSERT_TAIL(&consoles, c, next); } else { /* * HACK: Put graphical consoles before text consoles. @@ -1493,40 +367,143 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, * Only do that for coldplugged devices. After initial device * initialization we will not renumber the consoles any more. */ - QemuConsole *c = QTAILQ_FIRST(&consoles); + QemuConsole *it = QTAILQ_FIRST(&consoles); - while (QTAILQ_NEXT(c, next) != NULL && - c->console_type == GRAPHIC_CONSOLE) { - c = QTAILQ_NEXT(c, next); + while (QTAILQ_NEXT(it, next) != NULL && QEMU_IS_GRAPHIC_CONSOLE(it)) { + it = QTAILQ_NEXT(it, next); } - if (c->console_type == GRAPHIC_CONSOLE) { + if (QEMU_IS_GRAPHIC_CONSOLE(it)) { /* have no text consoles */ - s->index = c->index + 1; - QTAILQ_INSERT_AFTER(&consoles, c, s, next); + c->index = it->index + 1; + QTAILQ_INSERT_AFTER(&consoles, it, c, next); } else { - s->index = c->index; - QTAILQ_INSERT_BEFORE(c, s, next); + c->index = it->index; + QTAILQ_INSERT_BEFORE(it, c, next); /* renumber text consoles */ - for (i = s->index + 1; c != NULL; c = QTAILQ_NEXT(c, next), i++) { - c->index = i; + for (i = c->index + 1; it != NULL; it = QTAILQ_NEXT(it, next), i++) { + it->index = i; } } } - return s; } +static void +qemu_console_finalize(Object *obj) +{ + QemuConsole *c = QEMU_CONSOLE(obj); + + /* TODO: check this code path, and unregister from consoles */ + g_clear_pointer(&c->surface, qemu_free_displaysurface); + g_clear_pointer(&c->gl_unblock_timer, timer_free); + g_clear_pointer(&c->ui_timer, timer_free); +} + +static void +qemu_console_class_init(ObjectClass *oc, void *data) +{ +} + +static void +qemu_console_init(Object *obj) +{ + QemuConsole *c = QEMU_CONSOLE(obj); + DisplayState *ds = get_alloc_displaystate(); + + qemu_co_queue_init(&c->dump_queue); + c->ds = ds; + c->window_id = -1; + c->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, + dpy_set_ui_info_timer, c); + qemu_console_register(c); +} + +static void +qemu_graphic_console_finalize(Object *obj) +{ + QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(obj); + + g_clear_pointer(&c->device, object_unref); +} + +static void +qemu_graphic_console_prop_get_head(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(obj); + + visit_type_uint32(v, name, &c->head, errp); +} + +static void +qemu_graphic_console_class_init(ObjectClass *oc, void *data) +{ + object_class_property_add_link(oc, "device", TYPE_DEVICE, + offsetof(QemuGraphicConsole, device), + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG); + object_class_property_add(oc, "head", "uint32", + qemu_graphic_console_prop_get_head, + NULL, NULL, NULL); +} + +static void +qemu_graphic_console_init(Object *obj) +{ +} + +#ifdef WIN32 +void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, + HANDLE h, uint32_t offset) +{ + assert(!surface->handle); + + surface->handle = h; + surface->handle_offset = offset; +} + +static void +win32_pixman_image_destroy(pixman_image_t *image, void *data) +{ + DisplaySurface *surface = data; + + if (!surface->handle) { + return; + } + + assert(surface->handle_offset == 0); + + qemu_win32_map_free( + pixman_image_get_data(surface->image), + surface->handle, + &error_warn + ); +} +#endif + DisplaySurface *qemu_create_displaysurface(int width, int height) { - DisplaySurface *surface = g_new0(DisplaySurface, 1); + DisplaySurface *surface; + void *bits = NULL; +#ifdef WIN32 + HANDLE handle = NULL; +#endif - trace_displaysurface_create(surface, width, height); - surface->format = PIXMAN_x8r8g8b8; - surface->image = pixman_image_create_bits(surface->format, - width, height, - NULL, width * 4); - assert(surface->image != NULL); + trace_displaysurface_create(width, height); + +#ifdef WIN32 + bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort); +#endif + + surface = qemu_create_displaysurface_from( + width, height, + PIXMAN_x8r8g8b8, + width * 4, bits + ); surface->flags = QEMU_ALLOCATED_FLAG; +#ifdef WIN32 + qemu_displaysurface_win32_set_handle(surface, handle, 0); +#endif return surface; } @@ -1537,11 +514,14 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height, DisplaySurface *surface = g_new0(DisplaySurface, 1); trace_displaysurface_create_from(surface, width, height, format); - surface->format = format; - surface->image = pixman_image_create_bits(surface->format, + surface->image = pixman_image_create_bits(format, width, height, (void *)data, linesize); assert(surface->image != NULL); +#ifdef WIN32 + pixman_image_set_destroy_function(surface->image, + win32_pixman_image_destroy, surface); +#endif return surface; } @@ -1551,7 +531,6 @@ DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image) DisplaySurface *surface = g_new0(DisplaySurface, 1); trace_displaysurface_create_pixman(surface); - surface->format = pixman_image_get_format(image); surface->image = pixman_image_ref(image); return surface; @@ -1561,8 +540,9 @@ DisplaySurface *qemu_create_placeholder_surface(int w, int h, const char *msg) { DisplaySurface *surface = qemu_create_displaysurface(w, h); - pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK]; - pixman_color_t fg = color_table_rgb[0][QEMU_COLOR_WHITE]; +#ifdef CONFIG_PIXMAN + pixman_color_t bg = QEMU_PIXMAN_COLOR_BLACK; + pixman_color_t fg = QEMU_PIXMAN_COLOR_GRAY; pixman_image_t *glyph; int len, x, y, i; @@ -1575,6 +555,7 @@ DisplaySurface *qemu_create_placeholder_surface(int w, int h, x+i, y, FONT_WIDTH, FONT_HEIGHT); qemu_pixman_image_unref(glyph); } +#endif surface->flags |= QEMU_PLACEHOLDER_FLAG; return surface; } @@ -1637,6 +618,71 @@ static bool console_compatible_with(QemuConsole *con, return true; } +void console_handle_touch_event(QemuConsole *con, + struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX], + uint64_t num_slot, + int width, int height, + double x, double y, + InputMultiTouchType type, + Error **errp) +{ + struct touch_slot *slot; + bool needs_sync = false; + int update; + int i; + + if (num_slot >= INPUT_EVENT_SLOTS_MAX) { + error_setg(errp, + "Unexpected touch slot number: % " PRId64" >= %d", + num_slot, INPUT_EVENT_SLOTS_MAX); + return; + } + + slot = &touch_slots[num_slot]; + slot->x = x; + slot->y = y; + + if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) { + slot->tracking_id = num_slot; + } + + for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) { + if (i == num_slot) { + update = type; + } else { + update = INPUT_MULTI_TOUCH_TYPE_UPDATE; + } + + slot = &touch_slots[i]; + + if (slot->tracking_id == -1) { + continue; + } + + if (update == INPUT_MULTI_TOUCH_TYPE_END) { + slot->tracking_id = -1; + qemu_input_queue_mtt(con, update, i, slot->tracking_id); + needs_sync = true; + } else { + qemu_input_queue_mtt(con, update, i, slot->tracking_id); + qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true); + qemu_input_queue_mtt_abs(con, + INPUT_AXIS_X, (int) slot->x, + 0, width, + i, slot->tracking_id); + qemu_input_queue_mtt_abs(con, + INPUT_AXIS_Y, (int) slot->y, + 0, height, + i, slot->tracking_id); + needs_sync = true; + } + } + + if (needs_sync) { + qemu_input_event_sync(); + } +} + void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl) { /* display has opengl support */ @@ -1648,10 +694,19 @@ void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl) con->gl = gl; } +static void +dcl_set_graphic_cursor(DisplayChangeListener *dcl, QemuGraphicConsole *con) +{ + if (con && con->cursor && dcl->ops->dpy_cursor_define) { + dcl->ops->dpy_cursor_define(dcl, con->cursor); + } + if (con && dcl->ops->dpy_mouse_set) { + dcl->ops->dpy_mouse_set(dcl, con->cursor_x, con->cursor_y, con->cursor_on); + } +} + void register_displaychangelistener(DisplayChangeListener *dcl) { - QemuConsole *con; - assert(!dcl->ds); trace_displaychangelistener_register(dcl, dcl->ops->dpy_name); @@ -1660,12 +715,14 @@ void register_displaychangelistener(DisplayChangeListener *dcl) gui_setup_refresh(dcl->ds); if (dcl->con) { dcl->con->dcls++; - con = dcl->con; - } else { - con = active_console; } - displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL); - text_console_update_cursor(NULL); + displaychangelistener_display_console(dcl, &error_fatal); + if (QEMU_IS_GRAPHIC_CONSOLE(dcl->con)) { + dcl_set_graphic_cursor(dcl, QEMU_GRAPHIC_CONSOLE(dcl->con)); + } else if (QEMU_IS_TEXT_CONSOLE(dcl->con)) { + qemu_text_console_update_size(QEMU_TEXT_CONSOLE(dcl->con)); + } + qemu_text_console_update_cursor(); } void update_displaychangelistener(DisplayChangeListener *dcl, @@ -1694,14 +751,15 @@ void unregister_displaychangelistener(DisplayChangeListener *dcl) static void dpy_set_ui_info_timer(void *opaque) { QemuConsole *con = opaque; + uint32_t head = qemu_console_get_head(con); - con->hw_ops->ui_info(con->hw, con->head, &con->ui_info); + con->hw_ops->ui_info(con->hw, head, &con->ui_info); } -bool dpy_ui_info_supported(QemuConsole *con) +bool dpy_ui_info_supported(const QemuConsole *con) { if (con == NULL) { - con = active_console; + return false; } return con->hw_ops->ui_info != NULL; @@ -1709,19 +767,13 @@ bool dpy_ui_info_supported(QemuConsole *con) const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con) { - if (con == NULL) { - con = active_console; - } + assert(dpy_ui_info_supported(con)); return &con->ui_info; } int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay) { - if (con == NULL) { - con = active_console; - } - if (!dpy_ui_info_supported(con)) { return -1; } @@ -1760,7 +812,7 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) } dpy_gfx_update_texture(con, con->surface, x, y, w, h); QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (con != dcl->con) { continue; } if (dcl->ops->dpy_gfx_update) { @@ -1783,6 +835,7 @@ void dpy_gfx_replace_surface(QemuConsole *con, static const char placeholder_msg[] = "Display output is not active."; DisplayState *s = con->ds; DisplaySurface *old_surface = con->surface; + DisplaySurface *new_surface = surface; DisplayChangeListener *dcl; int width; int height; @@ -1796,19 +849,19 @@ void dpy_gfx_replace_surface(QemuConsole *con, height = 480; } - surface = qemu_create_placeholder_surface(width, height, placeholder_msg); + new_surface = qemu_create_placeholder_surface(width, height, placeholder_msg); } - assert(old_surface != surface); + assert(old_surface != new_surface); con->scanout.kind = SCANOUT_SURFACE; - con->surface = surface; - dpy_gfx_create_texture(con, surface); + con->surface = new_surface; + dpy_gfx_create_texture(con, new_surface); QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (con != dcl->con) { continue; } - displaychangelistener_gfx_switch(dcl, surface, FALSE); + displaychangelistener_gfx_switch(dcl, new_surface, surface ? FALSE : TRUE); } dpy_gfx_destroy_texture(con, old_surface); qemu_free_displaysurface(old_surface); @@ -1859,7 +912,7 @@ void dpy_text_cursor(QemuConsole *con, int x, int y) return; } QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (con != dcl->con) { continue; } if (dcl->ops->dpy_text_cursor) { @@ -1877,7 +930,7 @@ void dpy_text_update(QemuConsole *con, int x, int y, int w, int h) return; } QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (con != dcl->con) { continue; } if (dcl->ops->dpy_text_update) { @@ -1895,7 +948,7 @@ void dpy_text_resize(QemuConsole *con, int w, int h) return; } QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (con != dcl->con) { continue; } if (dcl->ops->dpy_text_resize) { @@ -1904,16 +957,20 @@ void dpy_text_resize(QemuConsole *con, int w, int h) } } -void dpy_mouse_set(QemuConsole *con, int x, int y, int on) +void dpy_mouse_set(QemuConsole *c, int x, int y, int on) { - DisplayState *s = con->ds; + QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c); + DisplayState *s = c->ds; DisplayChangeListener *dcl; - if (!qemu_console_is_visible(con)) { + con->cursor_x = x; + con->cursor_y = y; + con->cursor_on = on; + if (!qemu_console_is_visible(c)) { return; } QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (c != dcl->con) { continue; } if (dcl->ops->dpy_mouse_set) { @@ -1922,16 +979,19 @@ void dpy_mouse_set(QemuConsole *con, int x, int y, int on) } } -void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor) +void dpy_cursor_define(QemuConsole *c, QEMUCursor *cursor) { - DisplayState *s = con->ds; + QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c); + DisplayState *s = c->ds; DisplayChangeListener *dcl; - if (!qemu_console_is_visible(con)) { + cursor_unref(con->cursor); + con->cursor = cursor_ref(cursor); + if (!qemu_console_is_visible(c)) { return; } QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (c != dcl->con) { continue; } if (dcl->ops->dpy_cursor_define) { @@ -1981,7 +1041,7 @@ void dpy_gl_scanout_disable(QemuConsole *con) con->scanout.kind = SCANOUT_NONE; } QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (con != dcl->con) { continue; } if (dcl->ops->dpy_gl_scanout_disable) { @@ -1996,7 +1056,8 @@ void dpy_gl_scanout_texture(QemuConsole *con, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, - uint32_t width, uint32_t height) + uint32_t width, uint32_t height, + void *d3d_tex2d) { DisplayState *s = con->ds; DisplayChangeListener *dcl; @@ -2004,17 +1065,18 @@ void dpy_gl_scanout_texture(QemuConsole *con, con->scanout.kind = SCANOUT_TEXTURE; con->scanout.texture = (ScanoutTexture) { backing_id, backing_y_0_top, backing_width, backing_height, - x, y, width, height + x, y, width, height, d3d_tex2d, }; QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (con != dcl->con) { continue; } if (dcl->ops->dpy_gl_scanout_texture) { dcl->ops->dpy_gl_scanout_texture(dcl, backing_id, backing_y_0_top, backing_width, backing_height, - x, y, width, height); + x, y, width, height, + d3d_tex2d); } } } @@ -2028,7 +1090,7 @@ void dpy_gl_scanout_dmabuf(QemuConsole *con, con->scanout.kind = SCANOUT_DMABUF; con->scanout.dmabuf = dmabuf; QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (con != dcl->con) { continue; } if (dcl->ops->dpy_gl_scanout_dmabuf) { @@ -2044,7 +1106,7 @@ void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf, DisplayChangeListener *dcl; QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (con != dcl->con) { continue; } if (dcl->ops->dpy_gl_cursor_dmabuf) { @@ -2061,7 +1123,7 @@ void dpy_gl_cursor_position(QemuConsole *con, DisplayChangeListener *dcl; QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (con != dcl->con) { continue; } if (dcl->ops->dpy_gl_cursor_position) { @@ -2077,7 +1139,7 @@ void dpy_gl_release_dmabuf(QemuConsole *con, DisplayChangeListener *dcl; QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (con != dcl->con) { continue; } if (dcl->ops->dpy_gl_release_dmabuf) { @@ -2096,7 +1158,7 @@ void dpy_gl_update(QemuConsole *con, graphic_hw_gl_block(con, true); QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != (dcl->con ? dcl->con : active_console)) { + if (con != dcl->con) { continue; } if (dcl->ops->dpy_gl_update) { @@ -2114,8 +1176,6 @@ static DisplayState *get_alloc_displaystate(void) { if (!display_state) { display_state = g_new0(DisplayState, 1); - cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, - text_console_update_cursor, NULL); } return display_state; } @@ -2129,14 +1189,8 @@ DisplayState *init_displaystate(void) gchar *name; QemuConsole *con; - get_alloc_displaystate(); QTAILQ_FOREACH(con, &consoles, next) { - if (con->console_type != GRAPHIC_CONSOLE && - con->ds == NULL) { - text_console_do_init(con->chr, display_state); - } - - /* Hook up into the qom tree here (not in new_console()), once + /* Hook up into the qom tree here (not in object_new()), once * all QemuConsoles are created and the order / numbering * doesn't change any more */ name = g_strdup_printf("console[%d]", con->index); @@ -2165,21 +1219,18 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, int width = 640; int height = 480; QemuConsole *s; - DisplayState *ds; DisplaySurface *surface; - ds = get_alloc_displaystate(); - s = qemu_console_lookup_unused(); + s = qemu_graphic_console_lookup_unused(); if (s) { trace_console_gfx_reuse(s->index); width = qemu_console_get_width(s, 0); height = qemu_console_get_height(s, 0); } else { trace_console_gfx_new(); - s = new_console(ds, GRAPHIC_CONSOLE, head); - s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, - dpy_set_ui_info_timer, s); + s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE); } + QEMU_GRAPHIC_CONSOLE(s)->head = head; graphic_console_set_hwops(s, hw_ops, opaque); if (dev) { object_property_set_link(OBJECT(s), "device", OBJECT(dev), @@ -2216,6 +1267,18 @@ void graphic_console_close(QemuConsole *con) dpy_gfx_replace_surface(con, surface); } +QemuConsole *qemu_console_lookup_default(void) +{ + QemuConsole *con; + + QTAILQ_FOREACH(con, &consoles, next) { + if (QEMU_IS_GRAPHIC_CONSOLE(con)) { + return con; + } + } + return QTAILQ_FIRST(&consoles); +} + QemuConsole *qemu_console_lookup_by_index(unsigned int index) { QemuConsole *con; @@ -2273,13 +1336,13 @@ QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, return con; } -QemuConsole *qemu_console_lookup_unused(void) +static QemuConsole *qemu_graphic_console_lookup_unused(void) { QemuConsole *con; Object *obj; QTAILQ_FOREACH(con, &consoles, next) { - if (con->hw_ops != &unused_ops) { + if (!QEMU_IS_GRAPHIC_CONSOLE(con) || con->hw_ops != &unused_ops) { continue; } obj = object_property_get_link(OBJECT(con), @@ -2292,25 +1355,24 @@ QemuConsole *qemu_console_lookup_unused(void) return NULL; } +QEMUCursor *qemu_console_get_cursor(QemuConsole *con) +{ + return QEMU_IS_GRAPHIC_CONSOLE(con) ? QEMU_GRAPHIC_CONSOLE(con)->cursor : NULL; +} + bool qemu_console_is_visible(QemuConsole *con) { - return (con == active_console) || (con->dcls > 0); + return con->dcls > 0; } bool qemu_console_is_graphic(QemuConsole *con) { - if (con == NULL) { - con = active_console; - } - return con && (con->console_type == GRAPHIC_CONSOLE); + return con && QEMU_IS_GRAPHIC_CONSOLE(con); } bool qemu_console_is_fixedsize(QemuConsole *con) { - if (con == NULL) { - con = active_console; - } - return con && (con->console_type != TEXT_CONSOLE); + return con && (QEMU_IS_GRAPHIC_CONSOLE(con) || QEMU_IS_FIXED_TEXT_CONSOLE(con)); } bool qemu_console_is_gl_blocked(QemuConsole *con) @@ -2319,42 +1381,79 @@ bool qemu_console_is_gl_blocked(QemuConsole *con) return con->gl_block; } +static bool qemu_graphic_console_is_multihead(QemuGraphicConsole *c) +{ + QemuConsole *con; + + QTAILQ_FOREACH(con, &consoles, next) { + QemuGraphicConsole *candidate; + + if (!QEMU_IS_GRAPHIC_CONSOLE(con)) { + continue; + } + + candidate = QEMU_GRAPHIC_CONSOLE(con); + if (candidate->device != c->device) { + continue; + } + + if (candidate->head != c->head) { + return true; + } + } + return false; +} + char *qemu_console_get_label(QemuConsole *con) { - if (con->console_type == GRAPHIC_CONSOLE) { - if (con->device) { - return g_strdup(object_get_typename(con->device)); + if (QEMU_IS_GRAPHIC_CONSOLE(con)) { + QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(con); + if (c->device) { + DeviceState *dev; + bool multihead; + + dev = DEVICE(c->device); + multihead = qemu_graphic_console_is_multihead(c); + if (multihead) { + return g_strdup_printf("%s.%d", dev->id ? + dev->id : + object_get_typename(c->device), + c->head); + } else { + return g_strdup_printf("%s", dev->id ? + dev->id : + object_get_typename(c->device)); + } } return g_strdup("VGA"); - } else { - if (con->chr && con->chr->label) { - return g_strdup(con->chr->label); + } else if (QEMU_IS_TEXT_CONSOLE(con)) { + const char *label = qemu_text_console_get_label(QEMU_TEXT_CONSOLE(con)); + if (label) { + return g_strdup(label); } - return g_strdup_printf("vc%d", con->index); } + + return g_strdup_printf("vc%d", con->index); } int qemu_console_get_index(QemuConsole *con) { - if (con == NULL) { - con = active_console; - } return con ? con->index : -1; } uint32_t qemu_console_get_head(QemuConsole *con) { if (con == NULL) { - con = active_console; + return -1; } - return con ? con->head : -1; + if (QEMU_IS_GRAPHIC_CONSOLE(con)) { + return QEMU_GRAPHIC_CONSOLE(con)->head; + } + return 0; } int qemu_console_get_width(QemuConsole *con, int fallback) { - if (con == NULL) { - con = active_console; - } if (con == NULL) { return fallback; } @@ -2372,9 +1471,6 @@ int qemu_console_get_width(QemuConsole *con, int fallback) int qemu_console_get_height(QemuConsole *con, int fallback) { - if (con == NULL) { - con = active_console; - } if (con == NULL) { return fallback; } @@ -2390,35 +1486,11 @@ int qemu_console_get_height(QemuConsole *con, int fallback) } } -static void vc_chr_accept_input(Chardev *chr) -{ - VCChardev *drv = VC_CHARDEV(chr); - QemuConsole *s = drv->console; - - kbd_send_chars(s); -} - -static void vc_chr_set_echo(Chardev *chr, bool echo) -{ - VCChardev *drv = VC_CHARDEV(chr); - QemuConsole *s = drv->console; - - s->echo = echo; -} - -static void text_console_update_cursor_timer(void) -{ - timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - + CONSOLE_CURSOR_PERIOD / 2); -} - -static void text_console_update_cursor(void *opaque) +int qemu_invalidate_text_consoles(void) { QemuConsole *s; int count = 0; - cursor_visible_phase = !cursor_visible_phase; - QTAILQ_FOREACH(s, &consoles, next) { if (qemu_console_is_graphic(s) || !qemu_console_is_visible(s)) { @@ -2428,125 +1500,18 @@ static void text_console_update_cursor(void *opaque) graphic_hw_invalidate(s); } - if (count) { - text_console_update_cursor_timer(); - } -} - -static const GraphicHwOps text_console_ops = { - .invalidate = text_console_invalidate, - .text_update = text_console_update, -}; - -static void text_console_do_init(Chardev *chr, DisplayState *ds) -{ - VCChardev *drv = VC_CHARDEV(chr); - QemuConsole *s = drv->console; - int g_width = 80 * FONT_WIDTH; - int g_height = 24 * FONT_HEIGHT; - - fifo8_create(&s->out_fifo, 16); - s->ds = ds; - - s->y_displayed = 0; - s->y_base = 0; - s->total_height = DEFAULT_BACKSCROLL; - s->x = 0; - s->y = 0; - if (s->scanout.kind != SCANOUT_SURFACE) { - if (active_console && active_console->scanout.kind == SCANOUT_SURFACE) { - g_width = qemu_console_get_width(active_console, g_width); - g_height = qemu_console_get_height(active_console, g_height); - } - s->surface = qemu_create_displaysurface(g_width, g_height); - s->scanout.kind = SCANOUT_SURFACE; - } - - s->hw_ops = &text_console_ops; - s->hw = s; - - /* Set text attribute defaults */ - s->t_attrib_default.bold = 0; - s->t_attrib_default.uline = 0; - s->t_attrib_default.blink = 0; - s->t_attrib_default.invers = 0; - s->t_attrib_default.unvisible = 0; - s->t_attrib_default.fgcol = QEMU_COLOR_WHITE; - s->t_attrib_default.bgcol = QEMU_COLOR_BLACK; - /* set current text attributes to default */ - s->t_attrib = s->t_attrib_default; - text_console_resize(s); - - if (chr->label) { - char *msg; - - s->t_attrib.bgcol = QEMU_COLOR_BLUE; - msg = g_strdup_printf("%s console\r\n", chr->label); - vc_chr_write(chr, (uint8_t *)msg, strlen(msg)); - g_free(msg); - s->t_attrib = s->t_attrib_default; - } - - qemu_chr_be_event(chr, CHR_EVENT_OPENED); -} - -static void vc_chr_open(Chardev *chr, - ChardevBackend *backend, - bool *be_opened, - Error **errp) -{ - ChardevVC *vc = backend->u.vc.data; - VCChardev *drv = VC_CHARDEV(chr); - QemuConsole *s; - unsigned width = 0; - unsigned height = 0; - - if (vc->has_width) { - width = vc->width; - } else if (vc->has_cols) { - width = vc->cols * FONT_WIDTH; - } - - if (vc->has_height) { - height = vc->height; - } else if (vc->has_rows) { - height = vc->rows * FONT_HEIGHT; - } - - trace_console_txt_new(width, height); - if (width == 0 || height == 0) { - s = new_console(NULL, TEXT_CONSOLE, 0); - } else { - s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0); - s->scanout.kind = SCANOUT_SURFACE; - s->surface = qemu_create_displaysurface(width, height); - } - - if (!s) { - error_setg(errp, "cannot create text console"); - return; - } - - s->chr = chr; - drv->console = s; - - if (display_state) { - text_console_do_init(chr, display_state); - } - - /* console/chardev init sometimes completes elsewhere in a 2nd - * stage, so defer OPENED events until they are fully initialized - */ - *be_opened = false; + return count; } void qemu_console_resize(QemuConsole *s, int width, int height) { - DisplaySurface *surface; + DisplaySurface *surface = qemu_console_surface(s); - assert(s->console_type == GRAPHIC_CONSOLE); + assert(QEMU_IS_GRAPHIC_CONSOLE(s)); - if (qemu_console_get_width(s, -1) == width && + if ((s->scanout.kind != SCANOUT_SURFACE || + (surface && !is_buffer_shared(surface) && !is_placeholder(surface))) && + qemu_console_get_width(s, -1) == width && qemu_console_get_height(s, -1) == height) { return; } @@ -2597,7 +1562,11 @@ bool qemu_display_find_default(DisplayOptions *opts) for (i = 0; i < (int)ARRAY_SIZE(prio); i++) { if (dpys[prio[i]] == NULL) { - ui_module_load_one(DisplayType_str(prio[i])); + Error *local_err = NULL; + int rv = ui_module_load(DisplayType_str(prio[i]), &local_err); + if (rv < 0) { + error_report_err(local_err); + } } if (dpys[prio[i]] == NULL) { continue; @@ -2615,7 +1584,11 @@ void qemu_display_early_init(DisplayOptions *opts) return; } if (dpys[opts->type] == NULL) { - ui_module_load_one(DisplayType_str(opts->type)); + Error *local_err = NULL; + int rv = ui_module_load(DisplayType_str(opts->type), &local_err); + if (rv < 0) { + error_report_err(local_err); + } } if (dpys[opts->type] == NULL) { error_report("Display '%s' is not available.", @@ -2637,6 +1610,21 @@ void qemu_display_init(DisplayState *ds, DisplayOptions *opts) dpys[opts->type]->init(ds, opts); } +const char *qemu_display_get_vc(DisplayOptions *opts) +{ +#ifdef CONFIG_PIXMAN + const char *vc = "vc:80Cx24C"; +#else + const char *vc = NULL; +#endif + + assert(opts->type < DISPLAY_TYPE__MAX); + if (dpys[opts->type] && dpys[opts->type]->vc) { + vc = dpys[opts->type]->vc; + } + return vc; +} + void qemu_display_help(void) { int idx; @@ -2645,84 +1633,14 @@ void qemu_display_help(void) printf("none\n"); for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) { if (!dpys[idx]) { - ui_module_load_one(DisplayType_str(idx)); + Error *local_err = NULL; + int rv = ui_module_load(DisplayType_str(idx), &local_err); + if (rv < 0) { + error_report_err(local_err); + } } if (dpys[idx]) { printf("%s\n", DisplayType_str(dpys[idx]->type)); } } } - -void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp) -{ - int val; - ChardevVC *vc; - - backend->type = CHARDEV_BACKEND_KIND_VC; - vc = backend->u.vc.data = g_new0(ChardevVC, 1); - qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc)); - - val = qemu_opt_get_number(opts, "width", 0); - if (val != 0) { - vc->has_width = true; - vc->width = val; - } - - val = qemu_opt_get_number(opts, "height", 0); - if (val != 0) { - vc->has_height = true; - vc->height = val; - } - - val = qemu_opt_get_number(opts, "cols", 0); - if (val != 0) { - vc->has_cols = true; - vc->cols = val; - } - - val = qemu_opt_get_number(opts, "rows", 0); - if (val != 0) { - vc->has_rows = true; - vc->rows = val; - } -} - -static const TypeInfo qemu_console_info = { - .name = TYPE_QEMU_CONSOLE, - .parent = TYPE_OBJECT, - .instance_size = sizeof(QemuConsole), - .class_size = sizeof(QemuConsoleClass), -}; - -static void char_vc_class_init(ObjectClass *oc, void *data) -{ - ChardevClass *cc = CHARDEV_CLASS(oc); - - cc->parse = qemu_chr_parse_vc; - cc->open = vc_chr_open; - cc->chr_write = vc_chr_write; - cc->chr_accept_input = vc_chr_accept_input; - cc->chr_set_echo = vc_chr_set_echo; -} - -static const TypeInfo char_vc_type_info = { - .name = TYPE_CHARDEV_VC, - .parent = TYPE_CHARDEV, - .instance_size = sizeof(VCChardev), - .class_init = char_vc_class_init, -}; - -void qemu_console_early_init(void) -{ - /* set the default vc driver */ - if (!object_class_by_name(TYPE_CHARDEV_VC)) { - type_register(&char_vc_type_info); - } -} - -static void register_types(void) -{ - type_register_static(&qemu_console_info); -} - -type_init(register_types); diff --git a/ui/curses.c b/ui/curses.c index 861d63244c..ec61615f7c 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -69,7 +69,7 @@ static void curses_update(DisplayChangeListener *dcl, int x, int y, int w, int h) { console_ch_t *line; - cchar_t curses_line[width]; + g_autofree cchar_t *curses_line = g_new(cchar_t, width); wchar_t wch[CCHARW_MAX]; attr_t attrs; short colors; @@ -98,7 +98,7 @@ static void curses_update(DisplayChangeListener *dcl, static void curses_calc_pad(void) { - if (qemu_console_is_fixedsize(NULL)) { + if (qemu_console_is_fixedsize(dcl->con)) { width = gwidth; height = gheight; } else { @@ -201,7 +201,7 @@ static void curses_cursor_position(DisplayChangeListener *dcl, curs_set(1); /* it seems that curs_set(1) must always be called before * curs_set(2) for the latter to have effect */ - if (!qemu_console_is_graphic(NULL)) { + if (!qemu_console_is_graphic(dcl->con)) { curs_set(2); } return; @@ -274,11 +274,11 @@ static void curses_refresh(DisplayChangeListener *dcl) clear(); refresh(); curses_calc_pad(); - graphic_hw_invalidate(NULL); + graphic_hw_invalidate(dcl->con); invalidate = 0; } - graphic_hw_text_update(NULL, screen); + graphic_hw_text_update(dcl->con, screen); while (1) { /* while there are any pending key strokes to process */ @@ -318,11 +318,16 @@ static void curses_refresh(DisplayChangeListener *dcl) /* process keys reserved for qemu */ if (keycode >= QEMU_KEY_CONSOLE0 && keycode < QEMU_KEY_CONSOLE0 + 9) { - erase(); - wnoutrefresh(stdscr); - console_select(keycode - QEMU_KEY_CONSOLE0); + QemuConsole *con = qemu_console_lookup_by_index(keycode - QEMU_KEY_CONSOLE0); + if (con) { + erase(); + wnoutrefresh(stdscr); + unregister_displaychangelistener(dcl); + dcl->con = con; + register_displaychangelistener(dcl); - invalidate = 1; + invalidate = 1; + } continue; } } @@ -354,45 +359,45 @@ static void curses_refresh(DisplayChangeListener *dcl) if (keycode == -1) continue; - if (qemu_console_is_graphic(NULL)) { + if (qemu_console_is_graphic(dcl->con)) { /* since terminals don't know about key press and release * events, we need to emit both for each key received */ if (keycode & SHIFT) { - qemu_input_event_send_key_number(NULL, SHIFT_CODE, true); + qemu_input_event_send_key_number(dcl->con, SHIFT_CODE, true); qemu_input_event_send_key_delay(0); } if (keycode & CNTRL) { - qemu_input_event_send_key_number(NULL, CNTRL_CODE, true); + qemu_input_event_send_key_number(dcl->con, CNTRL_CODE, true); qemu_input_event_send_key_delay(0); } if (keycode & ALT) { - qemu_input_event_send_key_number(NULL, ALT_CODE, true); + qemu_input_event_send_key_number(dcl->con, ALT_CODE, true); qemu_input_event_send_key_delay(0); } if (keycode & ALTGR) { - qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true); + qemu_input_event_send_key_number(dcl->con, GREY | ALT_CODE, true); qemu_input_event_send_key_delay(0); } - qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true); + qemu_input_event_send_key_number(dcl->con, keycode & KEY_MASK, true); qemu_input_event_send_key_delay(0); - qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false); + qemu_input_event_send_key_number(dcl->con, keycode & KEY_MASK, false); qemu_input_event_send_key_delay(0); if (keycode & ALTGR) { - qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false); + qemu_input_event_send_key_number(dcl->con, GREY | ALT_CODE, false); qemu_input_event_send_key_delay(0); } if (keycode & ALT) { - qemu_input_event_send_key_number(NULL, ALT_CODE, false); + qemu_input_event_send_key_number(dcl->con, ALT_CODE, false); qemu_input_event_send_key_delay(0); } if (keycode & CNTRL) { - qemu_input_event_send_key_number(NULL, CNTRL_CODE, false); + qemu_input_event_send_key_number(dcl->con, CNTRL_CODE, false); qemu_input_event_send_key_delay(0); } if (keycode & SHIFT) { - qemu_input_event_send_key_number(NULL, SHIFT_CODE, false); + qemu_input_event_send_key_number(dcl->con, SHIFT_CODE, false); qemu_input_event_send_key_delay(0); } } else { @@ -400,7 +405,7 @@ static void curses_refresh(DisplayChangeListener *dcl) if (keysym == -1) keysym = chr; - kbd_put_keysym(keysym); + qemu_text_console_put_keysym(QEMU_TEXT_CONSOLE(dcl->con), keysym); } } } @@ -798,6 +803,7 @@ static void curses_display_init(DisplayState *ds, DisplayOptions *opts) curses_winch_init(); dcl = g_new0(DisplayChangeListener, 1); + dcl->con = qemu_console_lookup_default(); dcl->ops = &dcl_ops; register_displaychangelistener(dcl); diff --git a/ui/curses_keys.h b/ui/curses_keys.h index 71e04acdc7..88a2208ed1 100644 --- a/ui/curses_keys.h +++ b/ui/curses_keys.h @@ -210,6 +210,12 @@ static const int _curses2keycode[CURSES_CHARS] = { ['N' - '@'] = 49 | CNTRL, /* Control + n */ /* Control + m collides with the keycode for Enter */ + ['@' - '@'] = 3 | CNTRL, /* Control + @ */ + /* Control + [ collides with the keycode for Escape */ + ['\\' - '@'] = 43 | CNTRL, /* Control + Backslash */ + [']' - '@'] = 27 | CNTRL, /* Control + ] */ + ['^' - '@'] = 7 | CNTRL, /* Control + ^ */ + ['_' - '@'] = 12 | CNTRL, /* Control + Underscore */ }; static const int _curseskey2keycode[CURSES_KEYS] = { diff --git a/ui/cursor.c b/ui/cursor.c index 835f0802f9..29717b3ecb 100644 --- a/ui/cursor.c +++ b/ui/cursor.c @@ -90,11 +90,12 @@ QEMUCursor *cursor_builtin_left_ptr(void) return cursor_parse_xpm(cursor_left_ptr_xpm); } -QEMUCursor *cursor_alloc(int width, int height) +QEMUCursor *cursor_alloc(uint16_t width, uint16_t height) { QEMUCursor *c; size_t datasize = width * height * sizeof(uint32_t); + /* Modern physical hardware typically uses 512x512 sprites */ if (width > 512 || height > 512) { return NULL; } @@ -106,12 +107,13 @@ QEMUCursor *cursor_alloc(int width, int height) return c; } -void cursor_get(QEMUCursor *c) +QEMUCursor *cursor_ref(QEMUCursor *c) { c->refcount++; + return c; } -void cursor_put(QEMUCursor *c) +void cursor_unref(QEMUCursor *c) { if (c == NULL) return; diff --git a/ui/dbus-chardev.c b/ui/dbus-chardev.c index 940ef937cd..1d3a7122a1 100644 --- a/ui/dbus-chardev.c +++ b/ui/dbus-chardev.c @@ -27,7 +27,9 @@ #include "qemu/config-file.h" #include "qemu/option.h" +#ifdef G_OS_UNIX #include +#endif #include "dbus.h" @@ -112,13 +114,20 @@ static gboolean dbus_chr_register( DBusChardev *dc, GDBusMethodInvocation *invocation, +#ifdef G_OS_UNIX GUnixFDList *fd_list, +#endif GVariant *arg_stream, QemuDBusDisplay1Chardev *object) { g_autoptr(GError) err = NULL; int fd; +#ifdef G_OS_WIN32 + if (!dbus_win32_import_socket(invocation, arg_stream, &fd)) { + return DBUS_METHOD_INVOCATION_HANDLED; + } +#else fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_stream), &err); if (err) { g_dbus_method_invocation_return_error( @@ -128,13 +137,18 @@ dbus_chr_register( "Couldn't get peer FD: %s", err->message); return DBUS_METHOD_INVOCATION_HANDLED; } +#endif if (qemu_chr_add_client(CHARDEV(dc), fd) < 0) { g_dbus_method_invocation_return_error(invocation, DBUS_DISPLAY_ERROR, DBUS_DISPLAY_ERROR_FAILED, "Couldn't register FD!"); +#ifdef G_OS_WIN32 + closesocket(fd); +#else close(fd); +#endif return DBUS_METHOD_INVOCATION_HANDLED; } @@ -142,7 +156,11 @@ dbus_chr_register( "owner", g_dbus_method_invocation_get_sender(invocation), NULL); - qemu_dbus_display1_chardev_complete_register(object, invocation, NULL); + qemu_dbus_display1_chardev_complete_register(object, invocation +#ifndef G_OS_WIN32 + , NULL +#endif + ); return DBUS_METHOD_INVOCATION_HANDLED; } diff --git a/ui/dbus-clipboard.c b/ui/dbus-clipboard.c index 5843d26cd2..fe7fcdecb6 100644 --- a/ui/dbus-clipboard.c +++ b/ui/dbus-clipboard.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" #include "qemu/dbus.h" +#include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qom/object_interfaces.h" #include "sysemu/sysemu.h" @@ -203,15 +204,6 @@ dbus_clipboard_unregister_proxy(DBusDisplay *dpy) g_clear_object(&dpy->clipboard_proxy); } -static void -dbus_on_clipboard_proxy_name_owner_changed( - DBusDisplay *dpy, - GObject *object, - GParamSpec *pspec) -{ - dbus_clipboard_unregister_proxy(dpy); -} - static gboolean dbus_clipboard_register( DBusDisplay *dpy, @@ -219,6 +211,7 @@ dbus_clipboard_register( { g_autoptr(GError) err = NULL; const char *name = NULL; + GDBusConnection *connection = g_dbus_method_invocation_get_connection(invocation); if (dpy->clipboard_proxy) { g_dbus_method_invocation_return_error( @@ -231,7 +224,7 @@ dbus_clipboard_register( dpy->clipboard_proxy = qemu_dbus_display1_clipboard_proxy_new_sync( - g_dbus_method_invocation_get_connection(invocation), + connection, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, g_dbus_method_invocation_get_sender(invocation), "/org/qemu/Display1/Clipboard", @@ -251,7 +244,11 @@ dbus_clipboard_register( g_object_connect(dpy->clipboard_proxy, "swapped-signal::notify::g-name-owner", - dbus_on_clipboard_proxy_name_owner_changed, dpy, + dbus_clipboard_unregister_proxy, dpy, + NULL); + g_object_connect(connection, + "swapped-signal::closed", + dbus_clipboard_unregister_proxy, dpy, NULL); qemu_clipboard_reset_serial(); diff --git a/ui/dbus-console.c b/ui/dbus-console.c index 898a4ac8a5..49da9ccc83 100644 --- a/ui/dbus-console.c +++ b/ui/dbus-console.c @@ -22,15 +22,20 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "ui/input.h" #include "ui/kbd-state.h" #include "trace.h" +#ifdef G_OS_UNIX #include +#endif #include "dbus.h" +static struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX]; + struct _DBusDisplayConsole { GDBusObjectSkeleton parent_instance; DisplayChangeListener dcl; @@ -43,6 +48,7 @@ struct _DBusDisplayConsole { QKbdState *kbd; QemuDBusDisplay1Mouse *iface_mouse; + QemuDBusDisplay1MultiTouch *iface_touch; gboolean last_set; guint last_x; guint last_y; @@ -92,7 +98,8 @@ dbus_gl_scanout_texture(DisplayChangeListener *dcl, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, - uint32_t w, uint32_t h) + uint32_t w, uint32_t h, + void *d3d_tex2d) { DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl); @@ -143,6 +150,8 @@ dbus_display_console_dispose(GObject *object) DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object); unregister_displaychangelistener(&ddc->dcl); + g_clear_object(&ddc->iface_touch); + g_clear_object(&ddc->iface_mouse); g_clear_object(&ddc->iface_kbd); g_clear_object(&ddc->iface); g_clear_pointer(&ddc->listeners, g_hash_table_unref); @@ -203,10 +212,47 @@ dbus_console_set_ui_info(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } +#ifdef G_OS_WIN32 +bool +dbus_win32_import_socket(GDBusMethodInvocation *invocation, + GVariant *arg_listener, int *socket) +{ + gsize n; + WSAPROTOCOL_INFOW *info = (void *)g_variant_get_fixed_array(arg_listener, &n, 1); + + if (!info || n != sizeof(*info)) { + g_dbus_method_invocation_return_error( + invocation, + DBUS_DISPLAY_ERROR, + DBUS_DISPLAY_ERROR_FAILED, + "Failed to get socket infos"); + return false; + } + + *socket = WSASocketW(FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + info, 0, 0); + if (*socket == INVALID_SOCKET) { + g_autofree gchar *emsg = g_win32_error_message(WSAGetLastError()); + g_dbus_method_invocation_return_error( + invocation, + DBUS_DISPLAY_ERROR, + DBUS_DISPLAY_ERROR_FAILED, + "Couldn't create socket: %s", emsg); + return false; + } + + return true; +} +#endif + static gboolean dbus_console_register_listener(DBusDisplayConsole *ddc, GDBusMethodInvocation *invocation, +#ifdef G_OS_UNIX GUnixFDList *fd_list, +#endif GVariant *arg_listener) { const char *sender = g_dbus_method_invocation_get_sender(invocation); @@ -228,6 +274,11 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } +#ifdef G_OS_WIN32 + if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) { + return DBUS_METHOD_INVOCATION_HANDLED; + } +#else fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err); if (err) { g_dbus_method_invocation_return_error( @@ -237,6 +288,7 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, "Couldn't get peer fd: %s", err->message); return DBUS_METHOD_INVOCATION_HANDLED; } +#endif socket = g_socket_new_from_fd(fd, &err); if (err) { @@ -245,13 +297,21 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, DBUS_DISPLAY_ERROR, DBUS_DISPLAY_ERROR_FAILED, "Couldn't make a socket: %s", err->message); +#ifdef G_OS_WIN32 + closesocket(fd); +#else close(fd); +#endif return DBUS_METHOD_INVOCATION_HANDLED; } socket_conn = g_socket_connection_factory_create_connection(socket); qemu_dbus_display1_console_complete_register_listener( - ddc->iface, invocation, NULL); + ddc->iface, invocation +#ifdef G_OS_UNIX + , NULL +#endif + ); listener_conn = g_dbus_connection_new_sync( G_IO_STREAM(socket_conn), @@ -326,7 +386,7 @@ dbus_mouse_rel_motion(DBusDisplayConsole *ddc, { trace_dbus_mouse_rel_motion(dx, dy); - if (qemu_input_is_absolute()) { + if (qemu_input_is_absolute(ddc->dcl.con)) { g_dbus_method_invocation_return_error( invocation, DBUS_DISPLAY_ERROR, DBUS_DISPLAY_ERROR_INVALID, @@ -344,6 +404,46 @@ dbus_mouse_rel_motion(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } +static gboolean +dbus_touch_send_event(DBusDisplayConsole *ddc, + GDBusMethodInvocation *invocation, + guint kind, uint64_t num_slot, + double x, double y) +{ + Error *error = NULL; + int width, height; + trace_dbus_touch_send_event(kind, num_slot, x, y); + + if (kind != INPUT_MULTI_TOUCH_TYPE_BEGIN && + kind != INPUT_MULTI_TOUCH_TYPE_UPDATE && + kind != INPUT_MULTI_TOUCH_TYPE_CANCEL && + kind != INPUT_MULTI_TOUCH_TYPE_END) + { + g_dbus_method_invocation_return_error( + invocation, DBUS_DISPLAY_ERROR, + DBUS_DISPLAY_ERROR_INVALID, + "Invalid touch event kind"); + return DBUS_METHOD_INVOCATION_HANDLED; + } + width = qemu_console_get_width(ddc->dcl.con, 0); + height = qemu_console_get_height(ddc->dcl.con, 0); + + console_handle_touch_event(ddc->dcl.con, touch_slots, + num_slot, width, height, + x, y, kind, &error); + if (error != NULL) { + g_dbus_method_invocation_return_error( + invocation, DBUS_DISPLAY_ERROR, + DBUS_DISPLAY_ERROR_INVALID, + error_get_pretty(error), NULL); + error_free(error); + } else { + qemu_dbus_display1_multi_touch_complete_send_event(ddc->iface_touch, + invocation); + } + return DBUS_METHOD_INVOCATION_HANDLED; +} + static gboolean dbus_mouse_set_pos(DBusDisplayConsole *ddc, GDBusMethodInvocation *invocation, @@ -353,7 +453,7 @@ dbus_mouse_set_pos(DBusDisplayConsole *ddc, trace_dbus_mouse_set_pos(x, y); - if (!qemu_input_is_absolute()) { + if (!qemu_input_is_absolute(ddc->dcl.con)) { g_dbus_method_invocation_return_error( invocation, DBUS_DISPLAY_ERROR, DBUS_DISPLAY_ERROR_INVALID, @@ -410,15 +510,21 @@ dbus_mouse_release(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } +static void +dbus_mouse_update_is_absolute(DBusDisplayConsole *ddc) +{ + g_object_set(ddc->iface_mouse, + "is-absolute", qemu_input_is_absolute(ddc->dcl.con), + NULL); +} + static void dbus_mouse_mode_change(Notifier *notify, void *data) { DBusDisplayConsole *ddc = container_of(notify, DBusDisplayConsole, mouse_mode_notifier); - g_object_set(ddc->iface_mouse, - "is-absolute", qemu_input_is_absolute(), - NULL); + dbus_mouse_update_is_absolute(ddc); } int dbus_display_console_get_index(DBusDisplayConsole *ddc) @@ -433,7 +539,13 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con) g_autofree char *label = NULL; char device_addr[256] = ""; DBusDisplayConsole *ddc; - int idx; + int idx, i; + const char *interfaces[] = { + "org.qemu.Display1.Keyboard", + "org.qemu.Display1.Mouse", + "org.qemu.Display1.MultiTouch", + NULL + }; assert(display); assert(con); @@ -458,6 +570,7 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con) "width", qemu_console_get_width(con, 0), "height", qemu_console_get_height(con, 0), "device-address", device_addr, + "interfaces", interfaces, NULL); g_object_connect(ddc->iface, "swapped-signal::handle-register-listener", @@ -488,9 +601,24 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con) g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc), G_DBUS_INTERFACE_SKELETON(ddc->iface_mouse)); + ddc->iface_touch = qemu_dbus_display1_multi_touch_skeleton_new(); + g_object_connect(ddc->iface_touch, + "swapped-signal::handle-send-event", dbus_touch_send_event, ddc, + NULL); + qemu_dbus_display1_multi_touch_set_max_slots(ddc->iface_touch, + INPUT_EVENT_SLOTS_MAX); + g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc), + G_DBUS_INTERFACE_SKELETON(ddc->iface_touch)); + + for (i = 0; i < INPUT_EVENT_SLOTS_MAX; i++) { + struct touch_slot *slot = &touch_slots[i]; + slot->tracking_id = -1; + } + register_displaychangelistener(&ddc->dcl); ddc->mouse_mode_notifier.notify = dbus_mouse_mode_change; qemu_add_mouse_mode_change_notifier(&ddc->mouse_mode_notifier); + dbus_mouse_update_is_absolute(ddc); return ddc; } diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml index c3b2293376..ce35d64eea 100644 --- a/ui/dbus-display1.xml +++ b/ui/dbus-display1.xml @@ -26,6 +26,20 @@ The list of consoles available on ``/org/qemu/Display1/Console_$id``. --> + + + + + + + + + + + + @@ -164,7 +200,8 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index f9fc8eda51..4a0a5d78f9 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -22,15 +22,35 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" #include "sysemu/sysemu.h" #include "dbus.h" +#ifdef G_OS_UNIX #include +#endif +#ifdef WIN32 +#include +#include +#endif +#ifdef CONFIG_OPENGL #include "ui/shader.h" #include "ui/egl-helpers.h" #include "ui/egl-context.h" +#include "ui/qemu-pixman.h" +#endif #include "trace.h" +static void dbus_gfx_switch(DisplayChangeListener *dcl, + struct DisplaySurface *new_surface); + +enum share_kind { + SHARE_KIND_NONE, + SHARE_KIND_MAPPED, + SHARE_KIND_D3DTEX, +}; + struct _DBusDisplayListener { GObject parent; @@ -40,51 +60,218 @@ struct _DBusDisplayListener { QemuDBusDisplay1Listener *proxy; +#ifdef CONFIG_PIXMAN + /* Keep track of the damage region */ + pixman_region32_t gl_damage; +#else + int gl_damage; +#endif + DisplayChangeListener dcl; DisplaySurface *ds; - int gl_updates; + enum share_kind ds_share; + + bool ds_mapped; + bool can_share_map; + +#ifdef WIN32 + QemuDBusDisplay1ListenerWin32Map *map_proxy; + QemuDBusDisplay1ListenerWin32D3d11 *d3d11_proxy; + HANDLE peer_process; + ID3D11Texture2D *d3d_texture; +#ifdef CONFIG_OPENGL + egl_fb fb; +#endif +#endif + + guint dbus_filter; + guint32 out_serial_to_discard; }; G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT) +static void dbus_gfx_update(DisplayChangeListener *dcl, + int x, int y, int w, int h); + +static void ddl_discard_pending_messages(DBusDisplayListener *ddl) +{ + ddl->out_serial_to_discard = g_dbus_connection_get_last_serial( + g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy))); +} + +#ifdef CONFIG_OPENGL +static void dbus_scanout_disable(DisplayChangeListener *dcl) +{ + DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); + + qemu_dbus_display1_listener_call_disable( + ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); +} + +#ifdef WIN32 +static bool d3d_texture2d_share(ID3D11Texture2D *d3d_texture, + HANDLE *handle, Error **errp) +{ + IDXGIResource1 *dxgiResource = NULL; + HRESULT hr; + + hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture, + &IID_IDXGIResource1, + (void **)&dxgiResource); + if (FAILED(hr)) { + goto fail; + } + + hr = dxgiResource->lpVtbl->CreateSharedHandle( + dxgiResource, + NULL, + DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, + NULL, + handle + ); + + dxgiResource->lpVtbl->Release(dxgiResource); + + if (SUCCEEDED(hr)) { + return true; + } + +fail: + error_setg_win32(errp, GetLastError(), "failed to create shared handle"); + return false; +} + +static bool d3d_texture2d_acquire0(ID3D11Texture2D *d3d_texture, Error **errp) +{ + IDXGIKeyedMutex *dxgiMutex = NULL; + HRESULT hr; + + hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture, + &IID_IDXGIKeyedMutex, + (void **)&dxgiMutex); + if (FAILED(hr)) { + goto fail; + } + + hr = dxgiMutex->lpVtbl->AcquireSync(dxgiMutex, 0, INFINITE); + + dxgiMutex->lpVtbl->Release(dxgiMutex); + + if (SUCCEEDED(hr)) { + return true; + } + +fail: + error_setg_win32(errp, GetLastError(), "failed to acquire texture mutex"); + return false; +} + +static bool d3d_texture2d_release0(ID3D11Texture2D *d3d_texture, Error **errp) +{ + IDXGIKeyedMutex *dxgiMutex = NULL; + HRESULT hr; + + hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture, + &IID_IDXGIKeyedMutex, + (void **)&dxgiMutex); + if (FAILED(hr)) { + goto fail; + } + + hr = dxgiMutex->lpVtbl->ReleaseSync(dxgiMutex, 0); + + dxgiMutex->lpVtbl->Release(dxgiMutex); + + if (SUCCEEDED(hr)) { + return true; + } + +fail: + error_setg_win32(errp, GetLastError(), "failed to release texture mutex"); + return false; +} +#endif /* WIN32 */ + +#if defined(CONFIG_GBM) || defined(WIN32) static void dbus_update_gl_cb(GObject *source_object, - GAsyncResult *res, - gpointer user_data) + GAsyncResult *res, + gpointer user_data) { g_autoptr(GError) err = NULL; DBusDisplayListener *ddl = user_data; + bool success; - if (!qemu_dbus_display1_listener_call_update_dmabuf_finish(ddl->proxy, - res, &err)) { +#ifdef CONFIG_GBM + success = qemu_dbus_display1_listener_call_update_dmabuf_finish( + ddl->proxy, res, &err); +#endif + +#ifdef WIN32 + success = qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d_finish( + ddl->d3d11_proxy, res, &err); + d3d_texture2d_acquire0(ddl->d3d_texture, &error_warn); +#endif + + if (!success) { error_report("Failed to call update: %s", err->message); } graphic_hw_gl_block(ddl->dcl.con, false); g_object_unref(ddl); } +#endif -static void dbus_call_update_gl(DBusDisplayListener *ddl, +static void dbus_call_update_gl(DisplayChangeListener *dcl, int x, int y, int w, int h) { - graphic_hw_gl_block(ddl->dcl.con, true); +#if defined(CONFIG_GBM) || defined(WIN32) + DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); +#endif + + trace_dbus_update_gl(x, y, w, h); + glFlush(); +#ifdef CONFIG_GBM + graphic_hw_gl_block(ddl->dcl.con, true); qemu_dbus_display1_listener_call_update_dmabuf(ddl->proxy, x, y, w, h, G_DBUS_CALL_FLAGS_NONE, DBUS_DEFAULT_TIMEOUT, NULL, dbus_update_gl_cb, g_object_ref(ddl)); +#endif + +#ifdef WIN32 + switch (ddl->ds_share) { + case SHARE_KIND_MAPPED: + egl_fb_read_rect(ddl->ds, &ddl->fb, x, y, w, h); + dbus_gfx_update(dcl, x, y, w, h); + break; + case SHARE_KIND_D3DTEX: { + Error *err = NULL; + assert(ddl->d3d_texture); + + graphic_hw_gl_block(ddl->dcl.con, true); + if (!d3d_texture2d_release0(ddl->d3d_texture, &err)) { + error_report_err(err); + return; + } + qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d( + ddl->d3d11_proxy, + x, y, w, h, + G_DBUS_CALL_FLAGS_NONE, + DBUS_DEFAULT_TIMEOUT, NULL, + dbus_update_gl_cb, + g_object_ref(ddl)); + break; + } + default: + g_warn_if_reached(); + } +#endif } -static void dbus_scanout_disable(DisplayChangeListener *dcl) -{ - DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); - - ddl->ds = NULL; - qemu_dbus_display1_listener_call_disable( - ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); -} - +#ifdef CONFIG_GBM static void dbus_scanout_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf) { @@ -98,6 +285,9 @@ static void dbus_scanout_dmabuf(DisplayChangeListener *dcl, return; } + ddl_discard_pending_messages(ddl); + + /* FIXME: add missing x/y/w/h support */ qemu_dbus_display1_listener_call_scanout_dmabuf( ddl->proxy, g_variant_new_handle(0), @@ -112,19 +302,150 @@ static void dbus_scanout_dmabuf(DisplayChangeListener *dcl, fd_list, NULL, NULL, NULL); } +#endif /* GBM */ +#endif /* OPENGL */ +#ifdef WIN32 +static bool dbus_scanout_map(DBusDisplayListener *ddl) +{ + g_autoptr(GError) err = NULL; + BOOL success; + HANDLE target_handle; + + if (ddl->ds_share == SHARE_KIND_MAPPED) { + return true; + } + + if (!ddl->can_share_map || !ddl->ds->handle) { + return false; + } + + success = DuplicateHandle( + GetCurrentProcess(), + ddl->ds->handle, + ddl->peer_process, + &target_handle, + FILE_MAP_READ | SECTION_QUERY, + FALSE, 0); + if (!success) { + g_autofree char *msg = g_win32_error_message(GetLastError()); + g_debug("Failed to DuplicateHandle: %s", msg); + ddl->can_share_map = false; + return false; + } + + ddl_discard_pending_messages(ddl); + + if (!qemu_dbus_display1_listener_win32_map_call_scanout_map_sync( + ddl->map_proxy, + GPOINTER_TO_UINT(target_handle), + ddl->ds->handle_offset, + surface_width(ddl->ds), + surface_height(ddl->ds), + surface_stride(ddl->ds), + surface_format(ddl->ds), + G_DBUS_CALL_FLAGS_NONE, + DBUS_DEFAULT_TIMEOUT, + NULL, + &err)) { + g_debug("Failed to call ScanoutMap: %s", err->message); + ddl->can_share_map = false; + return false; + } + + ddl->ds_share = SHARE_KIND_MAPPED; + + return true; +} + +#ifdef CONFIG_OPENGL +static bool +dbus_scanout_share_d3d_texture( + DBusDisplayListener *ddl, + ID3D11Texture2D *tex, + bool backing_y_0_top, + uint32_t backing_width, + uint32_t backing_height, + uint32_t x, uint32_t y, + uint32_t w, uint32_t h) +{ + Error *err = NULL; + BOOL success; + HANDLE share_handle, target_handle; + + if (!d3d_texture2d_release0(tex, &err)) { + error_report_err(err); + return false; + } + + if (!d3d_texture2d_share(tex, &share_handle, &err)) { + error_report_err(err); + return false; + } + + success = DuplicateHandle( + GetCurrentProcess(), + share_handle, + ddl->peer_process, + &target_handle, + 0, + FALSE, DUPLICATE_SAME_ACCESS); + if (!success) { + g_autofree char *msg = g_win32_error_message(GetLastError()); + g_debug("Failed to DuplicateHandle: %s", msg); + CloseHandle(share_handle); + return false; + } + + ddl_discard_pending_messages(ddl); + + qemu_dbus_display1_listener_win32_d3d11_call_scanout_texture2d( + ddl->d3d11_proxy, + GPOINTER_TO_INT(target_handle), + backing_width, + backing_height, + backing_y_0_top, + x, y, w, h, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, NULL, NULL); + + CloseHandle(share_handle); + + if (!d3d_texture2d_acquire0(tex, &err)) { + error_report_err(err); + return false; + } + + ddl->d3d_texture = tex; + ddl->ds_share = SHARE_KIND_D3DTEX; + + return true; +} +#endif /* CONFIG_OPENGL */ +#endif /* WIN32 */ + +#ifdef CONFIG_OPENGL static void dbus_scanout_texture(DisplayChangeListener *dcl, uint32_t tex_id, bool backing_y_0_top, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, - uint32_t w, uint32_t h) + uint32_t w, uint32_t h, + void *d3d_tex2d) { + trace_dbus_scanout_texture(tex_id, backing_y_0_top, + backing_width, backing_height, x, y, w, h); +#ifdef CONFIG_GBM QemuDmaBuf dmabuf = { - .width = backing_width, - .height = backing_height, + .width = w, + .height = h, .y0_top = backing_y_0_top, + .x = x, + .y = y, + .backing_width = backing_width, + .backing_height = backing_height, }; assert(tex_id); @@ -139,8 +460,26 @@ static void dbus_scanout_texture(DisplayChangeListener *dcl, dbus_scanout_dmabuf(dcl, &dmabuf); close(dmabuf.fd); +#endif + +#ifdef WIN32 + DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); + + /* there must be a matching gfx_switch before */ + assert(surface_width(ddl->ds) == w); + assert(surface_height(ddl->ds) == h); + + if (d3d_tex2d) { + dbus_scanout_share_d3d_texture(ddl, d3d_tex2d, backing_y_0_top, + backing_width, backing_height, x, y, w, h); + } else { + dbus_scanout_map(ddl); + egl_fb_setup_for_tex(&ddl->fb, backing_width, backing_height, tex_id, false); + } +#endif } +#ifdef CONFIG_GBM static void dbus_cursor_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf, bool have_hot, uint32_t hot_x, uint32_t hot_y) @@ -148,7 +487,7 @@ static void dbus_cursor_dmabuf(DisplayChangeListener *dcl, DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); DisplaySurface *ds; GVariant *v_data = NULL; - egl_fb cursor_fb; + egl_fb cursor_fb = EGL_FB_INIT; if (!dmabuf) { qemu_dbus_display1_listener_call_mouse_set( @@ -187,7 +526,14 @@ static void dbus_cursor_dmabuf(DisplayChangeListener *dcl, NULL); } -static void dbus_cursor_position(DisplayChangeListener *dcl, +static void dbus_release_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf) +{ + dbus_scanout_disable(dcl); +} +#endif /* GBM */ + +static void dbus_gl_cursor_position(DisplayChangeListener *dcl, uint32_t pos_x, uint32_t pos_y) { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); @@ -197,19 +543,11 @@ static void dbus_cursor_position(DisplayChangeListener *dcl, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } -static void dbus_release_dmabuf(DisplayChangeListener *dcl, - QemuDmaBuf *dmabuf) -{ - dbus_scanout_disable(dcl); -} - static void dbus_scanout_update(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { - DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); - - dbus_call_update_gl(ddl, x, y, w, h); + dbus_call_update_gl(dcl, x, y, w, h); } static void dbus_gl_refresh(DisplayChangeListener *dcl) @@ -222,65 +560,77 @@ static void dbus_gl_refresh(DisplayChangeListener *dcl) return; } - if (ddl->gl_updates) { - dbus_call_update_gl(ddl, 0, 0, - surface_width(ddl->ds), surface_height(ddl->ds)); - ddl->gl_updates = 0; +#ifdef CONFIG_PIXMAN + int n_rects = pixman_region32_n_rects(&ddl->gl_damage); + + for (int i = 0; i < n_rects; i++) { + pixman_box32_t *box; + box = pixman_region32_rectangles(&ddl->gl_damage, NULL) + i; + /* TODO: Add a UpdateList call to send multiple updates at once */ + dbus_call_update_gl(dcl, box->x1, box->y1, + box->x2 - box->x1, box->y2 - box->y1); } + pixman_region32_clear(&ddl->gl_damage); +#else + if (ddl->gl_damage) { + dbus_call_update_gl(dcl, 0, 0, + surface_width(ddl->ds), surface_height(ddl->ds)); + ddl->gl_damage = 0; + } +#endif } +#endif /* OPENGL */ static void dbus_refresh(DisplayChangeListener *dcl) { graphic_hw_update(dcl->con); } +#ifdef CONFIG_OPENGL static void dbus_gl_gfx_update(DisplayChangeListener *dcl, int x, int y, int w, int h) { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); - ddl->gl_updates++; +#ifdef CONFIG_PIXMAN + pixman_region32_t rect_region; + pixman_region32_init_rect(&rect_region, x, y, w, h); + pixman_region32_union(&ddl->gl_damage, &ddl->gl_damage, &rect_region); + pixman_region32_fini(&rect_region); +#else + ddl->gl_damage++; +#endif } +#endif -static void dbus_gfx_update(DisplayChangeListener *dcl, - int x, int y, int w, int h) +static void dbus_gfx_update_sub(DBusDisplayListener *ddl, + int x, int y, int w, int h) { - DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); pixman_image_t *img; - GVariant *v_data; size_t stride; - - assert(ddl->ds); - stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8); - - trace_dbus_update(x, y, w, h); - - if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) { - v_data = g_variant_new_from_data( - G_VARIANT_TYPE("ay"), - surface_data(ddl->ds), - surface_stride(ddl->ds) * surface_height(ddl->ds), - TRUE, - (GDestroyNotify)pixman_image_unref, - pixman_image_ref(ddl->ds->image)); - qemu_dbus_display1_listener_call_scanout( - ddl->proxy, - surface_width(ddl->ds), - surface_height(ddl->ds), - surface_stride(ddl->ds), - surface_format(ddl->ds), - v_data, - G_DBUS_CALL_FLAGS_NONE, - DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); - return; - } + GVariant *v_data; /* make a copy, since gvariant only handles linear data */ + stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8); img = pixman_image_create_bits(surface_format(ddl->ds), w, h, NULL, stride); +#ifdef CONFIG_PIXMAN pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img, x, y, 0, 0, 0, 0, w, h); +#else + { + uint8_t *src = (uint8_t *)pixman_image_get_data(ddl->ds->image); + uint8_t *dst = (uint8_t *)pixman_image_get_data(img); + int bp = PIXMAN_FORMAT_BPP(surface_format(ddl->ds)) / 8; + int hh; + for (hh = 0; hh < h; hh++) { + memcpy(&dst[stride * hh], + &src[surface_stride(ddl->ds) * (hh + y) + x * bp], + stride); + } + } +#endif v_data = g_variant_new_from_data( G_VARIANT_TYPE("ay"), pixman_image_get_data(img), @@ -295,21 +645,71 @@ static void dbus_gfx_update(DisplayChangeListener *dcl, DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); } +static void ddl_scanout(DBusDisplayListener *ddl) +{ + GVariant *v_data; + + v_data = g_variant_new_from_data( + G_VARIANT_TYPE("ay"), surface_data(ddl->ds), + surface_stride(ddl->ds) * surface_height(ddl->ds), TRUE, + (GDestroyNotify)pixman_image_unref, pixman_image_ref(ddl->ds->image)); + + ddl_discard_pending_messages(ddl); + + qemu_dbus_display1_listener_call_scanout( + ddl->proxy, surface_width(ddl->ds), surface_height(ddl->ds), + surface_stride(ddl->ds), surface_format(ddl->ds), v_data, + G_DBUS_CALL_FLAGS_NONE, DBUS_DEFAULT_TIMEOUT, NULL, NULL, + g_object_ref(ddl)); +} + +static void dbus_gfx_update(DisplayChangeListener *dcl, + int x, int y, int w, int h) +{ + DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); + + assert(ddl->ds); + + trace_dbus_update(x, y, w, h); + +#ifdef WIN32 + if (dbus_scanout_map(ddl)) { + qemu_dbus_display1_listener_win32_map_call_update_map( + ddl->map_proxy, + x, y, w, h, + G_DBUS_CALL_FLAGS_NONE, + DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); + return; + } +#endif + + if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) { + return ddl_scanout(ddl); + } + + dbus_gfx_update_sub(ddl, x, y, w, h); +} + +#ifdef CONFIG_OPENGL static void dbus_gl_gfx_switch(DisplayChangeListener *dcl, struct DisplaySurface *new_surface) { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); + trace_dbus_gl_gfx_switch(new_surface); + ddl->ds = new_surface; + ddl->ds_share = SHARE_KIND_NONE; if (ddl->ds) { int width = surface_width(ddl->ds); int height = surface_height(ddl->ds); /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */ dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false, - width, height, 0, 0, width, height); + width, height, 0, 0, width, height, NULL); } } +#endif static void dbus_gfx_switch(DisplayChangeListener *dcl, struct DisplaySurface *new_surface) @@ -317,10 +717,7 @@ static void dbus_gfx_switch(DisplayChangeListener *dcl, DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); ddl->ds = new_surface; - if (!ddl->ds) { - /* why not call disable instead? */ - return; - } + ddl->ds_share = SHARE_KIND_NONE; } static void dbus_mouse_set(DisplayChangeListener *dcl, @@ -338,14 +735,13 @@ static void dbus_cursor_define(DisplayChangeListener *dcl, DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); GVariant *v_data = NULL; - cursor_get(c); v_data = g_variant_new_from_data( G_VARIANT_TYPE("ay"), c->data, c->width * c->height * 4, TRUE, - (GDestroyNotify)cursor_put, - c); + (GDestroyNotify)cursor_unref, + cursor_ref(c)); qemu_dbus_display1_listener_call_cursor_define( ddl->proxy, @@ -361,6 +757,7 @@ static void dbus_cursor_define(DisplayChangeListener *dcl, NULL); } +#ifdef CONFIG_OPENGL const DisplayChangeListenerOps dbus_gl_dcl_ops = { .dpy_name = "dbus-gl", .dpy_gfx_update = dbus_gl_gfx_update, @@ -372,12 +769,15 @@ const DisplayChangeListenerOps dbus_gl_dcl_ops = { .dpy_gl_scanout_disable = dbus_scanout_disable, .dpy_gl_scanout_texture = dbus_scanout_texture, +#ifdef CONFIG_GBM .dpy_gl_scanout_dmabuf = dbus_scanout_dmabuf, .dpy_gl_cursor_dmabuf = dbus_cursor_dmabuf, - .dpy_gl_cursor_position = dbus_cursor_position, .dpy_gl_release_dmabuf = dbus_release_dmabuf, +#endif + .dpy_gl_cursor_position = dbus_gl_cursor_position, .dpy_gl_update = dbus_scanout_update, }; +#endif const DisplayChangeListenerOps dbus_dcl_ops = { .dpy_name = "dbus", @@ -397,6 +797,17 @@ dbus_display_listener_dispose(GObject *object) g_clear_object(&ddl->conn); g_clear_pointer(&ddl->bus_name, g_free); g_clear_object(&ddl->proxy); +#ifdef WIN32 + g_clear_object(&ddl->map_proxy); + g_clear_object(&ddl->d3d11_proxy); + g_clear_pointer(&ddl->peer_process, CloseHandle); +#ifdef CONFIG_PIXMAN + pixman_region32_fini(&ddl->gl_damage); +#endif +#ifdef CONFIG_OPENGL + egl_fb_destroy(&ddl->fb); +#endif +#endif G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object); } @@ -406,11 +817,12 @@ dbus_display_listener_constructed(GObject *object) { DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object); + ddl->dcl.ops = &dbus_dcl_ops; +#ifdef CONFIG_OPENGL if (display_opengl) { ddl->dcl.ops = &dbus_gl_dcl_ops; - } else { - ddl->dcl.ops = &dbus_dcl_ops; } +#endif G_OBJECT_CLASS(dbus_display_listener_parent_class)->constructed(object); } @@ -427,6 +839,9 @@ dbus_display_listener_class_init(DBusDisplayListenerClass *klass) static void dbus_display_listener_init(DBusDisplayListener *ddl) { +#ifdef CONFIG_PIXMAN + pixman_region32_init(&ddl->gl_damage); +#endif } const char * @@ -441,6 +856,152 @@ dbus_display_listener_get_console(DBusDisplayListener *ddl) return ddl->console; } +#ifdef WIN32 +static bool +dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface) +{ + QemuDBusDisplay1Listener *l = QEMU_DBUS_DISPLAY1_LISTENER(ddl->proxy); + bool implements; + + implements = g_strv_contains(qemu_dbus_display1_listener_get_interfaces(l), iface); + if (!implements) { + g_debug("Display listener does not implement: `%s`", iface); + } + + return implements; +} + +static bool +dbus_display_listener_setup_peer_process(DBusDisplayListener *ddl) +{ + g_autoptr(GError) err = NULL; + GDBusConnection *conn; + GIOStream *stream; + GSocket *sock; + g_autoptr(GCredentials) creds = NULL; + DWORD *pid; + + if (ddl->peer_process) { + return true; + } + + conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy)); + stream = g_dbus_connection_get_stream(conn); + + if (!G_IS_UNIX_CONNECTION(stream)) { + return false; + } + + sock = g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream)); + creds = g_socket_get_credentials(sock, &err); + + if (!creds) { + g_debug("Failed to get peer credentials: %s", err->message); + return false; + } + + pid = g_credentials_get_native(creds, G_CREDENTIALS_TYPE_WIN32_PID); + + if (pid == NULL) { + g_debug("Failed to get peer PID"); + return false; + } + + ddl->peer_process = OpenProcess( + PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, + false, *pid); + + if (!ddl->peer_process) { + g_autofree char *msg = g_win32_error_message(GetLastError()); + g_debug("Failed to OpenProcess: %s", msg); + return false; + } + + return true; +} +#endif + +static void +dbus_display_listener_setup_d3d11(DBusDisplayListener *ddl) +{ +#ifdef WIN32 + g_autoptr(GError) err = NULL; + + if (!dbus_display_listener_implements(ddl, + "org.qemu.Display1.Listener.Win32.D3d11")) { + return; + } + + if (!dbus_display_listener_setup_peer_process(ddl)) { + return; + } + + ddl->d3d11_proxy = + qemu_dbus_display1_listener_win32_d3d11_proxy_new_sync(ddl->conn, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + NULL, + "/org/qemu/Display1/Listener", + NULL, + &err); + if (!ddl->d3d11_proxy) { + g_debug("Failed to setup win32 d3d11 proxy: %s", err->message); + return; + } +#endif +} + +static void +dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl) +{ +#ifdef WIN32 + g_autoptr(GError) err = NULL; + + if (!dbus_display_listener_implements(ddl, "org.qemu.Display1.Listener.Win32.Map")) { + return; + } + + if (!dbus_display_listener_setup_peer_process(ddl)) { + return; + } + + ddl->map_proxy = + qemu_dbus_display1_listener_win32_map_proxy_new_sync(ddl->conn, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + NULL, + "/org/qemu/Display1/Listener", + NULL, + &err); + if (!ddl->map_proxy) { + g_debug("Failed to setup win32 map proxy: %s", err->message); + return; + } + + ddl->can_share_map = true; +#endif +} + +static GDBusMessage * +dbus_filter(GDBusConnection *connection, + GDBusMessage *message, + gboolean incoming, + gpointer user_data) +{ + DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(user_data); + guint32 serial; + + if (incoming) { + return message; + } + + serial = g_dbus_message_get_serial(message); + if (serial <= ddl->out_serial_to_discard) { + trace_dbus_filter(serial, ddl->out_serial_to_discard); + return NULL; + } + + return message; +} + DBusDisplayListener * dbus_display_listener_new(const char *bus_name, GDBusConnection *conn, @@ -465,10 +1026,14 @@ dbus_display_listener_new(const char *bus_name, return NULL; } + ddl->dbus_filter = g_dbus_connection_add_filter(conn, dbus_filter, g_object_ref(ddl), g_object_unref); ddl->bus_name = g_strdup(bus_name); ddl->conn = conn; ddl->console = console; + dbus_display_listener_setup_shared_map(ddl); + dbus_display_listener_setup_d3d11(ddl); + con = qemu_console_lookup_by_index(dbus_display_console_get_index(console)); assert(con); ddl->dcl.con = con; diff --git a/ui/dbus.c b/ui/dbus.c index 7a87612379..e08b5de064 100644 --- a/ui/dbus.c +++ b/ui/dbus.c @@ -23,14 +23,17 @@ */ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/error-report.h" #include "qemu/dbus.h" #include "qemu/main-loop.h" #include "qemu/option.h" #include "qom/object_interfaces.h" #include "sysemu/sysemu.h" #include "ui/dbus-module.h" +#ifdef CONFIG_OPENGL #include "ui/egl-helpers.h" #include "ui/egl-context.h" +#endif #include "audio/audio.h" #include "audio/audio_int.h" #include "qapi/error.h" @@ -40,6 +43,7 @@ static DBusDisplay *dbus_display; +#ifdef CONFIG_OPENGL static QEMUGLContext dbus_create_context(DisplayGLCtx *dgc, QEMUGLParams *params) { @@ -52,7 +56,9 @@ static bool dbus_is_compatible_dcl(DisplayGLCtx *dgc, DisplayChangeListener *dcl) { - return dcl->ops == &dbus_gl_dcl_ops || dcl->ops == &dbus_console_dcl_ops; + return + dcl->ops == &dbus_gl_dcl_ops || + dcl->ops == &dbus_console_dcl_ops; } static void @@ -83,6 +89,7 @@ static const DisplayGLCtxOps dbus_gl_ops = { .dpy_gl_ctx_destroy_texture = dbus_destroy_texture, .dpy_gl_ctx_update_texture = dbus_update_texture, }; +#endif static NotifierList dbus_display_notifiers = NOTIFIER_LIST_INITIALIZER(dbus_display_notifiers); @@ -111,10 +118,12 @@ dbus_display_init(Object *o) DBusDisplay *dd = DBUS_DISPLAY(o); g_autoptr(GDBusObjectSkeleton) vm = NULL; +#ifdef CONFIG_OPENGL dd->glctx.ops = &dbus_gl_ops; if (display_opengl) { dd->glctx.gls = qemu_gl_init_shader(); } +#endif dd->iface = qemu_dbus_display1_vm_skeleton_new(); dd->consoles = g_ptr_array_new_with_free_func(g_object_unref); @@ -151,7 +160,9 @@ dbus_display_finalize(Object *o) g_clear_object(&dd->iface); g_free(dd->dbus_addr); g_free(dd->audiodev); +#ifdef CONFIG_OPENGL g_clear_pointer(&dd->glctx.gls, qemu_gl_fini_shader); +#endif dbus_display = NULL; } @@ -209,9 +220,8 @@ dbus_display_complete(UserCreatable *uc, Error **errp) } if (dd->audiodev && *dd->audiodev) { - AudioState *audio_state = audio_state_by_name(dd->audiodev); + AudioState *audio_state = audio_state_by_name(dd->audiodev, errp); if (!audio_state) { - error_setg(errp, "Audiodev '%s' not found", dd->audiodev); return; } if (!g_str_equal(audio_state->drv->name, "dbus")) { @@ -219,7 +229,7 @@ dbus_display_complete(UserCreatable *uc, Error **errp) dd->audiodev); return; } - audio_state->drv->set_dbus_server(audio_state, dd->server); + audio_state->drv->set_dbus_server(audio_state, dd->server, dd->p2p); } consoles = g_array_new(FALSE, FALSE, sizeof(guint32)); @@ -268,6 +278,7 @@ dbus_display_add_client_ready(GObject *source_object, } g_dbus_object_manager_server_set_connection(dbus_display->server, conn); + g_dbus_connection_start_message_processing(conn); } @@ -288,11 +299,20 @@ dbus_display_add_client(int csock, Error **errp) g_cancellable_cancel(dbus_display->add_client_cancellable); } +#ifdef WIN32 + socket = g_socket_new_from_fd(_get_osfhandle(csock), &err); +#else socket = g_socket_new_from_fd(csock, &err); +#endif if (!socket) { error_setg(errp, "Failed to setup D-Bus socket: %s", err->message); + close(csock); return false; } +#ifdef WIN32 + /* socket owns the SOCKET handle now, so release our osf handle */ + qemu_close_socket_osfhandle(csock); +#endif conn = g_socket_connection_factory_create_connection(socket); @@ -300,7 +320,8 @@ dbus_display_add_client(int csock, Error **errp) g_dbus_connection_new(G_IO_STREAM(conn), guid, - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER | + G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING, NULL, dbus_display->add_client_cancellable, dbus_display_add_client_ready, @@ -448,12 +469,11 @@ early_dbus_init(DisplayOptions *opts) DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_OFF; if (mode != DISPLAYGL_MODE_OFF) { - if (egl_rendernode_init(opts->u.dbus.rendernode, mode) < 0) { - error_report("dbus: render node init failed"); - exit(1); - } - - display_opengl = 1; +#ifdef CONFIG_OPENGL + egl_init(opts->u.dbus.rendernode, mode, &error_fatal); +#else + error_report("dbus: GL rendering is not supported"); +#endif } type_register(&dbus_vc_type_info); @@ -498,6 +518,7 @@ static QemuDisplay qemu_display_dbus = { .type = DISPLAY_TYPE_DBUS, .early_init = early_dbus_init, .init = dbus_init, + .vc = "vc", }; static void register_dbus(void) diff --git a/ui/dbus.h b/ui/dbus.h index c001c11f70..1e8c24a48e 100644 --- a/ui/dbus.h +++ b/ui/dbus.h @@ -31,7 +31,7 @@ #include "ui/console.h" #include "ui/clipboard.h" -#include "dbus-display1.h" +#include "ui/dbus-display1.h" typedef struct DBusClipboardRequest { GDBusMethodInvocation *invocation; @@ -62,6 +62,12 @@ struct DBusDisplay { Notifier notifier; }; +#ifdef WIN32 +bool +dbus_win32_import_socket(GDBusMethodInvocation *invocation, + GVariant *arg_listener, int *socket); +#endif + #define TYPE_DBUS_DISPLAY "dbus-display" OBJECT_DECLARE_SIMPLE_TYPE(DBusDisplay, DBUS_DISPLAY) diff --git a/ui/egl-context.c b/ui/egl-context.c index eb5f520fc4..9e0df466f3 100644 --- a/ui/egl-context.c +++ b/ui/egl-context.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "ui/egl-context.h" QEMUGLContext qemu_egl_create_context(DisplayGLCtx *dgc, @@ -32,6 +33,11 @@ void qemu_egl_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx) int qemu_egl_make_context_current(DisplayGLCtx *dgc, QEMUGLContext ctx) { - return eglMakeCurrent(qemu_egl_display, - EGL_NO_SURFACE, EGL_NO_SURFACE, ctx); + if (!eglMakeCurrent(qemu_egl_display, + EGL_NO_SURFACE, EGL_NO_SURFACE, ctx)) { + error_report("egl: eglMakeCurrent failed: %s", qemu_egl_get_error_string()); + return -1; + } + + return 0; } diff --git a/ui/egl-headless.c b/ui/egl-headless.c index 7a30fd9777..d5637dadb2 100644 --- a/ui/egl-headless.c +++ b/ui/egl-headless.c @@ -1,6 +1,7 @@ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu/module.h" -#include "sysemu/sysemu.h" +#include "qapi/error.h" #include "ui/console.h" #include "ui/egl-helpers.h" #include "ui/egl-context.h" @@ -60,7 +61,8 @@ static void egl_scanout_texture(DisplayChangeListener *dcl, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, - uint32_t w, uint32_t h) + uint32_t w, uint32_t h, + void *d3d_tex2d) { egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); @@ -78,6 +80,8 @@ static void egl_scanout_texture(DisplayChangeListener *dcl, } } +#ifdef CONFIG_GBM + static void egl_scanout_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf) { @@ -88,7 +92,7 @@ static void egl_scanout_dmabuf(DisplayChangeListener *dcl, egl_scanout_texture(dcl, dmabuf->texture, false, dmabuf->width, dmabuf->height, - 0, 0, dmabuf->width, dmabuf->height); + 0, 0, dmabuf->width, dmabuf->height, NULL); } static void egl_cursor_dmabuf(DisplayChangeListener *dcl, @@ -109,6 +113,14 @@ static void egl_cursor_dmabuf(DisplayChangeListener *dcl, } } +static void egl_release_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf) +{ + egl_dmabuf_release_texture(dmabuf); +} + +#endif + static void egl_cursor_position(DisplayChangeListener *dcl, uint32_t pos_x, uint32_t pos_y) { @@ -118,12 +130,6 @@ static void egl_cursor_position(DisplayChangeListener *dcl, edpy->pos_y = pos_y; } -static void egl_release_dmabuf(DisplayChangeListener *dcl, - QemuDmaBuf *dmabuf) -{ - egl_dmabuf_release_texture(dmabuf); -} - static void egl_scanout_flush(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h) @@ -159,10 +165,12 @@ static const DisplayChangeListenerOps egl_ops = { .dpy_gl_scanout_disable = egl_scanout_disable, .dpy_gl_scanout_texture = egl_scanout_texture, +#ifdef CONFIG_GBM .dpy_gl_scanout_dmabuf = egl_scanout_dmabuf, .dpy_gl_cursor_dmabuf = egl_cursor_dmabuf, - .dpy_gl_cursor_position = egl_cursor_position, .dpy_gl_release_dmabuf = egl_release_dmabuf, +#endif + .dpy_gl_cursor_position = egl_cursor_position, .dpy_gl_update = egl_scanout_flush, }; @@ -190,21 +198,21 @@ static const DisplayGLCtxOps eglctx_ops = { static void early_egl_headless_init(DisplayOptions *opts) { - display_opengl = 1; + DisplayGLMode mode = DISPLAYGL_MODE_ON; + + if (opts->has_gl) { + mode = opts->gl; + } + + egl_init(opts->u.egl_headless.rendernode, mode, &error_fatal); } static void egl_headless_init(DisplayState *ds, DisplayOptions *opts) { - DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON; QemuConsole *con; egl_dpy *edpy; int idx; - if (egl_rendernode_init(opts->u.egl_headless.rendernode, mode) < 0) { - error_report("egl: render node init failed"); - exit(1); - } - for (idx = 0;; idx++) { DisplayGLCtx *ctx; diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 3a88245b67..3d19dbe382 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -15,17 +15,62 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" + #include "qemu/drm.h" #include "qemu/error-report.h" #include "ui/console.h" #include "ui/egl-helpers.h" +#include "sysemu/sysemu.h" +#include "qapi/error.h" +#include "trace.h" EGLDisplay *qemu_egl_display; EGLConfig qemu_egl_config; DisplayGLMode qemu_egl_mode; +bool qemu_egl_angle_d3d; /* ------------------------------------------------------------------ */ +const char *qemu_egl_get_error_string(void) +{ + EGLint error = eglGetError(); + + switch (error) { + case EGL_SUCCESS: + return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: + return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: + return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: + return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: + return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONTEXT: + return "EGL_BAD_CONTEXT"; + case EGL_BAD_CONFIG: + return "EGL_BAD_CONFIG"; + case EGL_BAD_CURRENT_SURFACE: + return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: + return "EGL_BAD_DISPLAY"; + case EGL_BAD_SURFACE: + return "EGL_BAD_SURFACE"; + case EGL_BAD_MATCH: + return "EGL_BAD_MATCH"; + case EGL_BAD_PARAMETER: + return "EGL_BAD_PARAMETER"; + case EGL_BAD_NATIVE_PIXMAP: + return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: + return "EGL_BAD_NATIVE_WINDOW"; + case EGL_CONTEXT_LOST: + return "EGL_CONTEXT_LOST"; + default: + return "Unknown EGL error"; + } +} + static void egl_fb_delete_texture(egl_fb *fb) { if (!fb->delete_texture) { @@ -103,8 +148,8 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip) if (src->dmabuf) { x1 = src->dmabuf->x; y1 = src->dmabuf->y; - w = src->dmabuf->scanout_width; - h = src->dmabuf->scanout_height; + w = src->dmabuf->width; + h = src->dmabuf->height; } w = (x1 + w) > src->width ? src->width - x1 : w; @@ -127,6 +172,20 @@ void egl_fb_read(DisplaySurface *dst, egl_fb *src) GL_BGRA, GL_UNSIGNED_BYTE, surface_data(dst)); } +void egl_fb_read_rect(DisplaySurface *dst, egl_fb *src, int x, int y, int w, int h) +{ + assert(surface_width(dst) == src->width); + assert(surface_height(dst) == src->height); + assert(surface_format(dst) == PIXMAN_x8r8g8b8); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer); + glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); + glPixelStorei(GL_PACK_ROW_LENGTH, surface_stride(dst) / 4); + glReadPixels(x, y, w, h, + GL_BGRA, GL_UNSIGNED_BYTE, surface_data(dst) + x * 4); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); +} + void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip) { glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer); @@ -157,11 +216,12 @@ void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip, /* ---------------------------------------------------------------------- */ +EGLContext qemu_egl_rn_ctx; + #ifdef CONFIG_GBM int qemu_egl_rn_fd; struct gbm_device *qemu_egl_rn_gbm_dev; -EGLContext qemu_egl_rn_ctx; int egl_rendernode_init(const char *rendernode, DisplayGLMode mode) { @@ -254,9 +314,9 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) } attrs[i++] = EGL_WIDTH; - attrs[i++] = dmabuf->width; + attrs[i++] = dmabuf->backing_width; attrs[i++] = EGL_HEIGHT; - attrs[i++] = dmabuf->height; + attrs[i++] = dmabuf->backing_height; attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT; attrs[i++] = dmabuf->fourcc; @@ -358,7 +418,7 @@ EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win) /* ---------------------------------------------------------------------- */ -#if defined(CONFIG_X11) || defined(CONFIG_GBM) +#if defined(CONFIG_X11) || defined(CONFIG_GBM) || defined(WIN32) /* * Taken from glamor_egl.h from the Xorg xserver, which is MIT licensed @@ -395,10 +455,8 @@ static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native, /* In practise any EGL 1.5 implementation would support the EXT extension */ if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) { - PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT = - (void *) eglGetProcAddress("eglGetPlatformDisplayEXT"); - if (getPlatformDisplayEXT && platform != 0) { - dpy = getPlatformDisplayEXT(platform, native, NULL); + if (platform != 0) { + dpy = eglGetPlatformDisplayEXT(platform, native, NULL); } } @@ -438,20 +496,20 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, qemu_egl_display = qemu_egl_get_display(dpy, platform); if (qemu_egl_display == EGL_NO_DISPLAY) { - error_report("egl: eglGetDisplay failed"); + error_report("egl: eglGetDisplay failed: %s", qemu_egl_get_error_string()); return -1; } b = eglInitialize(qemu_egl_display, &major, &minor); if (b == EGL_FALSE) { - error_report("egl: eglInitialize failed"); + error_report("egl: eglInitialize failed: %s", qemu_egl_get_error_string()); return -1; } b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API); if (b == EGL_FALSE) { - error_report("egl: eglBindAPI failed (%s mode)", - gles ? "gles" : "core"); + error_report("egl: eglBindAPI failed (%s mode): %s", + gles ? "gles" : "core", qemu_egl_get_error_string()); return -1; } @@ -459,8 +517,8 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, gles ? conf_att_gles : conf_att_core, &qemu_egl_config, 1, &n); if (b == EGL_FALSE || n != 1) { - error_report("egl: eglChooseConfig failed (%s mode)", - gles ? "gles" : "core"); + error_report("egl: eglChooseConfig failed (%s mode): %s", + gles ? "gles" : "core", qemu_egl_get_error_string()); return -1; } @@ -468,6 +526,9 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, return 0; } +#endif + +#if defined(CONFIG_X11) || defined(CONFIG_GBM) int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode) { #ifdef EGL_KHR_platform_x11 @@ -485,7 +546,45 @@ int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode) return qemu_egl_init_dpy(dpy, 0, mode); #endif } +#endif + +#ifdef WIN32 +int qemu_egl_init_dpy_win32(EGLNativeDisplayType dpy, DisplayGLMode mode) +{ + /* prefer GL ES, as that's what ANGLE supports */ + if (mode == DISPLAYGL_MODE_ON) { + mode = DISPLAYGL_MODE_ES; + } + + if (qemu_egl_init_dpy(dpy, 0, mode) < 0) { + return -1; + } + +#ifdef EGL_D3D11_DEVICE_ANGLE + if (epoxy_has_egl_extension(qemu_egl_display, "EGL_EXT_device_query")) { + EGLDeviceEXT device; + void *d3d11_device; + + if (!eglQueryDisplayAttribEXT(qemu_egl_display, + EGL_DEVICE_EXT, + (EGLAttrib *)&device)) { + return 0; + } + + if (!eglQueryDeviceAttribEXT(device, + EGL_D3D11_DEVICE_ANGLE, + (EGLAttrib *)&d3d11_device)) { + return 0; + } + + trace_egl_init_d3d11_device(device); + qemu_egl_angle_d3d = device != NULL; + } +#endif + + return 0; +} #endif bool qemu_egl_has_dmabuf(void) @@ -527,3 +626,38 @@ EGLContext qemu_egl_init_ctx(void) return ectx; } + +bool egl_init(const char *rendernode, DisplayGLMode mode, Error **errp) +{ + ERRP_GUARD(); + + if (mode == DISPLAYGL_MODE_OFF) { + error_setg(errp, "egl: turning off GL doesn't make sense"); + return false; + } + +#ifdef WIN32 + if (qemu_egl_init_dpy_win32(EGL_DEFAULT_DISPLAY, mode) < 0) { + error_setg(errp, "egl: init failed"); + return false; + } + qemu_egl_rn_ctx = qemu_egl_init_ctx(); + if (!qemu_egl_rn_ctx) { + error_setg(errp, "egl: egl_init_ctx failed"); + return false; + } +#elif defined(CONFIG_GBM) + if (egl_rendernode_init(rendernode, mode) < 0) { + error_setg(errp, "egl: render node init failed"); + return false; + } +#endif + + if (!qemu_egl_rn_ctx) { + error_setg(errp, "egl: not available on this platform"); + return false; + } + + display_opengl = 1; + return true; +} diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index e3bd4bc274..3af5ac5bcf 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" +#include "qemu/error-report.h" #include "trace.h" @@ -31,6 +32,8 @@ static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout) vc->gfx.scanout_mode = scanout; if (!vc->gfx.scanout_mode) { + eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, + vc->gfx.esurface, vc->gfx.ectx); egl_fb_destroy(&vc->gfx.guest_fb); if (vc->gfx.surface) { surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); @@ -66,15 +69,16 @@ void gd_egl_draw(VirtualConsole *vc) #ifdef CONFIG_GBM QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; #endif - int ww, wh; + int ww, wh, ws; if (!vc->gfx.gls) { return; } window = gtk_widget_get_window(vc->gfx.drawing_area); - ww = gdk_window_get_width(window); - wh = gdk_window_get_height(window); + ws = gdk_window_get_scale_factor(window); + ww = gdk_window_get_width(window) * ws; + wh = gdk_window_get_height(window) * ws; if (vc->gfx.scanout_mode) { #ifdef CONFIG_GBM @@ -88,8 +92,8 @@ void gd_egl_draw(VirtualConsole *vc) #endif gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h); - vc->gfx.scale_x = (double)ww / vc->gfx.w; - vc->gfx.scale_y = (double)wh / vc->gfx.h; + vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds); + vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds); glFlush(); #ifdef CONFIG_GBM @@ -134,14 +138,20 @@ void gd_egl_update(DisplayChangeListener *dcl, vc->gfx.esurface, vc->gfx.ectx); surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h); vc->gfx.glupdates++; + eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); } void gd_egl_refresh(DisplayChangeListener *dcl) { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); - vc->gfx.dcl.update_interval = gd_monitor_update_interval( - vc->window ? vc->window : vc->gfx.drawing_area); + gd_update_monitor_refresh_rate( + vc, vc->window ? vc->window : vc->gfx.drawing_area); + + if (vc->gfx.guest_fb.dmabuf && vc->gfx.guest_fb.dmabuf->draw_submitted) { + return; + } if (!vc->gfx.esurface) { gd_egl_init(vc); @@ -195,6 +205,9 @@ void gd_egl_switch(DisplayChangeListener *dcl, if (resized) { gd_update_windowsize(vc); } + + eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); } QEMUGLContext gd_egl_create_context(DisplayGLCtx *dgc, @@ -220,7 +233,8 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl, uint32_t backing_id, bool backing_y_0_top, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, - uint32_t w, uint32_t h) + uint32_t w, uint32_t h, + void *d3d_tex2d) { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); @@ -230,6 +244,13 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl, vc->gfx.h = h; vc->gfx.y0_top = backing_y_0_top; + if (!vc->gfx.esurface) { + gd_egl_init(vc); + if (!vc->gfx.esurface) { + return; + } + } + eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, vc->gfx.esurface, vc->gfx.ectx); @@ -253,8 +274,10 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, } gd_egl_scanout_texture(dcl, dmabuf->texture, - false, dmabuf->width, dmabuf->height, - 0, 0, dmabuf->width, dmabuf->height); + dmabuf->y0_top, + dmabuf->backing_width, dmabuf->backing_height, + dmabuf->x, dmabuf->y, dmabuf->width, + dmabuf->height, NULL); if (dmabuf->allow_fences) { vc->gfx.guest_fb.dmabuf = dmabuf; @@ -274,7 +297,8 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl, if (!dmabuf->texture) { return; } - egl_fb_setup_for_tex(&vc->gfx.cursor_fb, dmabuf->width, dmabuf->height, + egl_fb_setup_for_tex(&vc->gfx.cursor_fb, + dmabuf->backing_width, dmabuf->backing_height, dmabuf->texture, false); } else { egl_fb_destroy(&vc->gfx.cursor_fb); @@ -296,7 +320,7 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl, { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GdkWindow *window; - int ww, wh; + int ww, wh, ws; if (!vc->gfx.scanout_mode) { return; @@ -309,8 +333,9 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl, vc->gfx.esurface, vc->gfx.ectx); window = gtk_widget_get_window(vc->gfx.drawing_area); - ww = gdk_window_get_width(window); - wh = gdk_window_get_height(window); + ws = gdk_window_get_scale_factor(window); + ww = gdk_window_get_width(window) * ws; + wh = gdk_window_get_height(window) * ws; egl_fb_setup_default(&vc->gfx.win_fb, ww, wh); if (vc->gfx.cursor_fb.texture) { egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb, @@ -338,9 +363,10 @@ void gd_egl_flush(DisplayChangeListener *dcl, VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GtkWidget *area = vc->gfx.drawing_area; - if (vc->gfx.guest_fb.dmabuf) { + if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) { graphic_hw_gl_block(vc->gfx.dcl.con, true); vc->gfx.guest_fb.dmabuf->draw_submitted = true; + gtk_egl_set_scanout_mode(vc, true); gtk_widget_queue_draw_area(area, x, y, w, h); return; } @@ -365,6 +391,11 @@ int gd_egl_make_current(DisplayGLCtx *dgc, { VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc); - return eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, - vc->gfx.esurface, ctx); + if (!eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, + vc->gfx.esurface, ctx)) { + error_report("egl: eglMakeCurrent failed: %s", qemu_egl_get_error_string()); + return -1; + } + + return 0; } diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index fc5a082eb8..52dcac161e 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -26,6 +26,7 @@ static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, bool scanout) vc->gfx.scanout_mode = scanout; if (!vc->gfx.scanout_mode) { + gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); egl_fb_destroy(&vc->gfx.guest_fb); if (vc->gfx.surface) { surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); @@ -115,14 +116,18 @@ void gd_gl_area_update(DisplayChangeListener *dcl, gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h); vc->gfx.glupdates++; + gdk_gl_context_clear_current(); } void gd_gl_area_refresh(DisplayChangeListener *dcl) { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); - vc->gfx.dcl.update_interval = gd_monitor_update_interval( - vc->window ? vc->window : vc->gfx.drawing_area); + gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area); + + if (vc->gfx.guest_fb.dmabuf && vc->gfx.guest_fb.dmabuf->draw_submitted) { + return; + } if (!vc->gfx.gls) { if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { @@ -170,6 +175,23 @@ void gd_gl_area_switch(DisplayChangeListener *dcl, } } +static int gd_cmp_gl_context_version(int major, int minor, QEMUGLParams *params) +{ + if (major > params->major_ver) { + return 1; + } + if (major < params->major_ver) { + return -1; + } + if (minor > params->minor_ver) { + return 1; + } + if (minor < params->minor_ver) { + return -1; + } + return 0; +} + QEMUGLContext gd_gl_area_create_context(DisplayGLCtx *dgc, QEMUGLParams *params) { @@ -177,8 +199,8 @@ QEMUGLContext gd_gl_area_create_context(DisplayGLCtx *dgc, GdkWindow *window; GdkGLContext *ctx; GError *err = NULL; + int major, minor; - gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); window = gtk_widget_get_window(vc->gfx.drawing_area); ctx = gdk_window_create_gl_context(window, &err); if (err) { @@ -196,12 +218,30 @@ QEMUGLContext gd_gl_area_create_context(DisplayGLCtx *dgc, g_clear_object(&ctx); return NULL; } + + gdk_gl_context_make_current(ctx); + gdk_gl_context_get_version(ctx, &major, &minor); + gdk_gl_context_clear_current(); + gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); + + if (gd_cmp_gl_context_version(major, minor, params) == -1) { + /* created ctx version < requested version */ + g_clear_object(&ctx); + } + + trace_gd_gl_area_create_context(ctx, params->major_ver, params->minor_ver); return ctx; } void gd_gl_area_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx) { - /* FIXME */ + GdkGLContext *current_ctx = gdk_gl_context_get_current(); + + trace_gd_gl_area_destroy_context(ctx, current_ctx); + if (ctx == current_ctx) { + gdk_gl_context_clear_current(); + } + g_clear_object(&ctx); } void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, @@ -210,7 +250,8 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, - uint32_t w, uint32_t h) + uint32_t w, uint32_t h, + void *d3d_tex2d) { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); @@ -244,9 +285,10 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); - if (vc->gfx.guest_fb.dmabuf) { + if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) { graphic_hw_gl_block(vc->gfx.dcl.con, true); vc->gfx.guest_fb.dmabuf->draw_submitted = true; + gtk_gl_area_set_scanout_mode(vc, true); } gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); } @@ -264,8 +306,10 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl, } gd_gl_area_scanout_texture(dcl, dmabuf->texture, - false, dmabuf->width, dmabuf->height, - 0, 0, dmabuf->width, dmabuf->height); + dmabuf->y0_top, + dmabuf->backing_width, dmabuf->backing_height, + dmabuf->x, dmabuf->y, dmabuf->width, + dmabuf->height, NULL); if (dmabuf->allow_fences) { vc->gfx.guest_fb.dmabuf = dmabuf; diff --git a/ui/gtk.c b/ui/gtk.c index c57c36749e..810d7fc796 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -36,6 +36,7 @@ #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" #include "qemu/cutils.h" +#include "qemu/error-report.h" #include "qemu/main-loop.h" #include "ui/console.h" @@ -53,7 +54,6 @@ #include #include "trace.h" -#include "qemu/cutils.h" #include "ui/input.h" #include "sysemu/runstate.h" #include "sysemu/sysemu.h" @@ -130,6 +130,8 @@ typedef struct VCChardev VCChardev; DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV, TYPE_CHARDEV_VC) +static struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX]; + bool gtk_use_gl_area; static void gd_grab_pointer(VirtualConsole *vc, const char *reason); @@ -202,7 +204,7 @@ static void gd_update_cursor(VirtualConsole *vc) } window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area)); - if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) { + if (s->full_screen || qemu_input_is_absolute(vc->gfx.dcl.con) || s->ptr_owner == vc) { gdk_window_set_cursor(window, s->null_cursor); } else { gdk_window_set_cursor(window, NULL); @@ -450,7 +452,8 @@ static void gd_mouse_set(DisplayChangeListener *dcl, GdkDisplay *dpy; gint x_root, y_root; - if (qemu_input_is_absolute()) { + if (!gtk_widget_get_realized(vc->gfx.drawing_area) || + qemu_input_is_absolute(dcl->con)) { return; } @@ -511,7 +514,7 @@ static void gd_switch(DisplayChangeListener *dcl, } vc->gfx.ds = surface; - if (surface->format == PIXMAN_x8r8g8b8) { + if (surface_format(surface) == PIXMAN_x8r8g8b8) { /* * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24 * @@ -580,7 +583,12 @@ static void gd_gl_release_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf) { #ifdef CONFIG_GBM + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + egl_dmabuf_release_texture(dmabuf); + if (vc->gfx.guest_fb.dmabuf == dmabuf) { + vc->gfx.guest_fb.dmabuf = NULL; + } #endif } @@ -681,9 +689,13 @@ static void gd_mouse_mode_change(Notifier *notify, void *data) s = container_of(notify, GtkDisplayState, mouse_mode_notifier); /* release the grab at switching to absolute mode */ - if (qemu_input_is_absolute() && gd_is_grab_active(s)) { - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), - FALSE); + if (s->ptr_owner && qemu_input_is_absolute(s->ptr_owner->gfx.dcl.con)) { + if (!s->ptr_owner->window) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), + FALSE); + } else { + gd_ungrab_pointer(s); + } } for (i = 0; i < s->nb_vcs; i++) { VirtualConsole *vc = &s->vc[i]; @@ -710,11 +722,28 @@ static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event, return TRUE; } -static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height) +static void gd_set_ui_refresh_rate(VirtualConsole *vc, int refresh_rate) { QemuUIInfo info; - memset(&info, 0, sizeof(info)); + if (!dpy_ui_info_supported(vc->gfx.dcl.con)) { + return; + } + + info = *dpy_get_ui_info(vc->gfx.dcl.con); + info.refresh_rate = refresh_rate; + dpy_set_ui_info(vc->gfx.dcl.con, &info, true); +} + +static void gd_set_ui_size(VirtualConsole *vc, gint width, gint height) +{ + QemuUIInfo info; + + if (!dpy_ui_info_supported(vc->gfx.dcl.con)) { + return; + } + + info = *dpy_get_ui_info(vc->gfx.dcl.con); info.width = width; info.height = height; dpy_set_ui_info(vc->gfx.dcl.con, &info, true); @@ -738,33 +767,32 @@ static void gd_resize_event(GtkGLArea *area, { VirtualConsole *vc = (void *)opaque; - gd_set_ui_info(vc, width, height); + gd_set_ui_size(vc, width, height); } #endif -/* - * If available, return the update interval of the monitor in ms, - * else return 0 (the default update interval). - */ -int gd_monitor_update_interval(GtkWidget *widget) +void gd_update_monitor_refresh_rate(VirtualConsole *vc, GtkWidget *widget) { #ifdef GDK_VERSION_3_22 GdkWindow *win = gtk_widget_get_window(widget); + int refresh_rate; if (win) { GdkDisplay *dpy = gtk_widget_get_display(widget); GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win); - int refresh_rate = gdk_monitor_get_refresh_rate(monitor); /* [mHz] */ - - if (refresh_rate) { - /* T = 1 / f = 1 [s*Hz] / f = 1000*1000 [ms*mHz] / f */ - return MIN(1000 * 1000 / refresh_rate, - GUI_REFRESH_INTERVAL_DEFAULT); - } + refresh_rate = gdk_monitor_get_refresh_rate(monitor); /* [mHz] */ + } else { + refresh_rate = 0; } + + gd_set_ui_refresh_rate(vc, refresh_rate); + + /* T = 1 / f = 1 [s*Hz] / f = 1000*1000 [ms*mHz] / f */ + vc->gfx.dcl.update_interval = refresh_rate ? + MIN(1000 * 1000 / refresh_rate, GUI_REFRESH_INTERVAL_DEFAULT) : + GUI_REFRESH_INTERVAL_DEFAULT; #endif - return 0; } static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) @@ -801,8 +829,7 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) return FALSE; } - vc->gfx.dcl.update_interval = - gd_monitor_update_interval(vc->window ? vc->window : s->window); + gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : s->window); fbw = surface_width(vc->gfx.ds); fbh = surface_height(vc->gfx.ds); @@ -857,7 +884,6 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, { VirtualConsole *vc = opaque; GtkDisplayState *s = vc->s; - GdkWindow *window; int x, y; int mx, my; int fbh, fbw; @@ -870,10 +896,9 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; - window = gtk_widget_get_window(vc->gfx.drawing_area); - ww = gdk_window_get_width(window); - wh = gdk_window_get_height(window); - ws = gdk_window_get_scale_factor(window); + ww = gtk_widget_get_allocated_width(widget); + wh = gtk_widget_get_allocated_height(widget); + ws = gtk_widget_get_scale_factor(widget); mx = my = 0; if (ww > fbw) { @@ -886,7 +911,7 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, x = (motion->x - mx) / vc->gfx.scale_x * ws; y = (motion->y - my) / vc->gfx.scale_y * ws; - if (qemu_input_is_absolute()) { + if (qemu_input_is_absolute(vc->gfx.dcl.con)) { if (x < 0 || y < 0 || x >= surface_width(vc->gfx.ds) || y >= surface_height(vc->gfx.ds)) { @@ -906,15 +931,15 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, s->last_y = y; s->last_set = TRUE; - if (!qemu_input_is_absolute() && s->ptr_owner == vc) { + if (!qemu_input_is_absolute(vc->gfx.dcl.con) && s->ptr_owner == vc) { GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area); GdkDisplay *dpy = gtk_widget_get_display(widget); GdkWindow *win = gtk_widget_get_window(widget); GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win); GdkRectangle geometry; - int x = (int)motion->x_root; - int y = (int)motion->y_root; + int xr = (int)motion->x_root; + int yr = (int)motion->y_root; gdk_monitor_get_geometry(monitor, &geometry); @@ -925,13 +950,13 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, * may still be only half way across the screen. Without * this warp, the server pointer would thus appear to hit * an invisible wall */ - if (x <= geometry.x || x - geometry.x >= geometry.width - 1 || - y <= geometry.y || y - geometry.y >= geometry.height - 1) { + if (xr <= geometry.x || xr - geometry.x >= geometry.width - 1 || + yr <= geometry.y || yr - geometry.y >= geometry.height - 1) { GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion); - x = geometry.x + geometry.width / 2; - y = geometry.y + geometry.height / 2; + xr = geometry.x + geometry.width / 2; + yr = geometry.y + geometry.height / 2; - gdk_device_warp(dev, screen, x, y); + gdk_device_warp(dev, screen, xr, yr); s->last_set = FALSE; return FALSE; } @@ -948,7 +973,7 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, /* implicitly grab the input at the first click in the relative mode */ if (button->button == 1 && button->type == GDK_BUTTON_PRESS && - !qemu_input_is_absolute() && s->ptr_owner != vc) { + !qemu_input_is_absolute(vc->gfx.dcl.con) && s->ptr_owner != vc) { if (!vc->window) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE); @@ -1047,6 +1072,36 @@ static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll, } +static gboolean gd_touch_event(GtkWidget *widget, GdkEventTouch *touch, + void *opaque) +{ + VirtualConsole *vc = opaque; + uint64_t num_slot = GPOINTER_TO_UINT(touch->sequence); + int type = -1; + + switch (touch->type) { + case GDK_TOUCH_BEGIN: + type = INPUT_MULTI_TOUCH_TYPE_BEGIN; + break; + case GDK_TOUCH_UPDATE: + type = INPUT_MULTI_TOUCH_TYPE_UPDATE; + break; + case GDK_TOUCH_END: + case GDK_TOUCH_CANCEL: + type = INPUT_MULTI_TOUCH_TYPE_END; + break; + default: + warn_report("gtk: unexpected touch event type\n"); + return FALSE; + } + + console_handle_touch_event(vc->gfx.dcl.con, touch_slots, + num_slot, surface_width(vc->gfx.ds), + surface_height(vc->gfx.ds), touch->x, + touch->y, type, &error_warn); + return TRUE; +} + static const guint16 *gd_get_keymap(size_t *maplen) { GdkDisplay *dpy = gdk_display_get_default(); @@ -1140,15 +1195,15 @@ static gboolean gd_text_key_down(GtkWidget *widget, GdkEventKey *key, void *opaque) { VirtualConsole *vc = opaque; - QemuConsole *con = vc->gfx.dcl.con; + QemuTextConsole *con = QEMU_TEXT_CONSOLE(vc->gfx.dcl.con); if (key->keyval == GDK_KEY_Delete) { - kbd_put_qcode_console(con, Q_KEY_CODE_DELETE, false); + qemu_text_console_put_qcode(con, Q_KEY_CODE_DELETE, false); } else if (key->length) { - kbd_put_string_console(con, key->string, key->length); + qemu_text_console_put_string(con, key->string, key->length); } else { int qcode = gd_map_keycode(gd_get_keycode(key)); - kbd_put_qcode_console(con, qcode, false); + qemu_text_console_put_qcode(con, qcode, false); } return TRUE; } @@ -1345,7 +1400,7 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque) eglDestroySurface(qemu_egl_display, vc->gfx.esurface); vc->gfx.esurface = NULL; } - if (vc->gfx.esurface) { + if (vc->gfx.ectx) { eglDestroyContext(qemu_egl_display, vc->gfx.ectx); vc->gfx.ectx = NULL; } @@ -1691,7 +1746,7 @@ static gboolean gd_configure(GtkWidget *widget, { VirtualConsole *vc = opaque; - gd_set_ui_info(vc, cfg->width, cfg->height); + gd_set_ui_size(vc, cfg->width, cfg->height); return FALSE; } @@ -1752,7 +1807,7 @@ static void gd_vc_send_chars(VirtualConsole *vc) uint32_t size; buf = fifo8_pop_buf(&vc->vte.out_fifo, MIN(len, avail), &size); - qemu_chr_be_write(vc->vte.chr, (uint8_t *)buf, size); + qemu_chr_be_write(vc->vte.chr, buf, size); len = qemu_chr_be_can_write(vc->vte.chr); avail -= size; } @@ -1772,7 +1827,9 @@ static void gd_vc_chr_accept_input(Chardev *chr) VCChardev *vcd = VC_CHARDEV(chr); VirtualConsole *vc = vcd->console; - gd_vc_send_chars(vc); + if (vc) { + gd_vc_send_chars(vc); + } } static void gd_vc_chr_set_echo(Chardev *chr, bool echo) @@ -1811,7 +1868,6 @@ static void char_gd_vc_class_init(ObjectClass *oc, void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); - cc->parse = qemu_chr_parse_vc; cc->open = gd_vc_open; cc->chr_write = gd_vc_chr_write; cc->chr_accept_input = gd_vc_chr_accept_input; @@ -1966,6 +2022,8 @@ static void gd_connect_vc_gfx_signals(VirtualConsole *vc) G_CALLBACK(gd_key_event), vc); g_signal_connect(vc->gfx.drawing_area, "key-release-event", G_CALLBACK(gd_key_event), vc); + g_signal_connect(vc->gfx.drawing_area, "touch-event", + G_CALLBACK(gd_touch_event), vc); g_signal_connect(vc->gfx.drawing_area, "enter-notify-event", G_CALLBACK(gd_enter_event), vc); @@ -2075,6 +2133,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, GSList *group, GtkWidget *view_menu) { bool zoom_to_fit = false; + int i; vc->label = qemu_console_get_label(con); vc->s = s; @@ -2122,6 +2181,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK | + GDK_TOUCH_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_SCROLL_MASK | @@ -2157,10 +2217,15 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, s->free_scale = true; } + for (i = 0; i < INPUT_EVENT_SLOTS_MAX; i++) { + struct touch_slot *slot = &touch_slots[i]; + slot->tracking_id = -1; + } + return group; } -static GtkWidget *gd_create_menu_view(GtkDisplayState *s) +static GtkWidget *gd_create_menu_view(GtkDisplayState *s, DisplayOptions *opts) { GSList *group = NULL; GtkWidget *view_menu; @@ -2258,7 +2323,8 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s) s->show_menubar_item = gtk_check_menu_item_new_with_mnemonic( _("Show Menubar")); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->show_menubar_item), - TRUE); + !opts->u.gtk.has_show_menubar || + opts->u.gtk.show_menubar); gtk_accel_group_connect(s->accel_group, GDK_KEY_m, HOTKEY_MODIFIERS, 0, g_cclosure_new_swap(G_CALLBACK(gd_accel_show_menubar), s, NULL)); gtk_accel_label_set_accel( @@ -2269,13 +2335,13 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s) return view_menu; } -static void gd_create_menus(GtkDisplayState *s) +static void gd_create_menus(GtkDisplayState *s, DisplayOptions *opts) { GtkSettings *settings; s->accel_group = gtk_accel_group_new(); s->machine_menu = gd_create_menu_machine(s); - s->view_menu = gd_create_menu_view(s); + s->view_menu = gd_create_menu_view(s, opts); s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine")); gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item), @@ -2301,16 +2367,18 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) { VirtualConsole *vc; - GtkDisplayState *s = g_malloc0(sizeof(*s)); + GtkDisplayState *s; GdkDisplay *window_display; GtkIconTheme *theme; char *dir; + int idx; if (!gtkinit) { fprintf(stderr, "gtk initialization failed\n"); exit(1); } assert(opts->type == DISPLAY_TYPE_GTK); + s = g_malloc0(sizeof(*s)); s->opts = opts; theme = gtk_icon_theme_get_default(); @@ -2352,7 +2420,7 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) gtk_window_set_icon_name(GTK_WINDOW(s->window), "qemu"); - gd_create_menus(s); + gd_create_menus(s, opts); gd_connect_signals(s); @@ -2368,6 +2436,19 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) gtk_widget_show_all(s->window); + for (idx = 0;; idx++) { + QemuConsole *con = qemu_console_lookup_by_index(idx); + if (!con) { + break; + } + gtk_widget_realize(s->vc[idx].gfx.drawing_area); + } + + if (opts->u.gtk.has_show_menubar && + !opts->u.gtk.show_menubar) { + gtk_widget_hide(s->menu_bar); + } + vc = gd_vc_find_current(s); gtk_widget_set_sensitive(s->view_menu, vc != NULL); #ifdef CONFIG_VTE @@ -2383,7 +2464,13 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) opts->u.gtk.grab_on_hover) { gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item)); } + if (opts->u.gtk.has_show_tabs && + opts->u.gtk.show_tabs) { + gtk_menu_item_activate(GTK_MENU_ITEM(s->show_tabs_item)); + } +#ifdef CONFIG_GTK_CLIPBOARD gd_clipboard_init(s); +#endif /* CONFIG_GTK_CLIPBOARD */ } static void early_gtk_display_init(DisplayOptions *opts) @@ -2420,6 +2507,12 @@ static void early_gtk_display_init(DisplayOptions *opts) gtk_use_gl_area = true; gtk_gl_area_init(); } else +#endif +#if defined(GDK_WINDOWING_WIN32) + if (GDK_IS_WIN32_DISPLAY(gdk_display_get_default())) { + gtk_use_gl_area = true; + gtk_gl_area_init(); + } else #endif { #ifdef CONFIG_X11 @@ -2441,6 +2534,7 @@ static QemuDisplay qemu_display_gtk = { .type = DISPLAY_TYPE_GTK, .early_init = early_gtk_display_init, .init = gtk_display_init, + .vc = "vc", }; static void register_gtk(void) diff --git a/ui/input-legacy.c b/ui/input-legacy.c index 46ea74e44d..210ae5eaca 100644 --- a/ui/input-legacy.c +++ b/ui/input-legacy.c @@ -127,7 +127,7 @@ static void legacy_kbd_event(DeviceState *dev, QemuConsole *src, } } -static QemuInputHandler legacy_kbd_handler = { +static const QemuInputHandler legacy_kbd_handler = { .name = "legacy-kbd", .mask = INPUT_EVENT_MASK_KEY, .event = legacy_kbd_event, diff --git a/ui/input.c b/ui/input.c index 8ac407dec4..dc745860f4 100644 --- a/ui/input.c +++ b/ui/input.c @@ -2,8 +2,6 @@ #include "sysemu/sysemu.h" #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" -#include "qapi/qmp/qdict.h" -#include "qemu/error-report.h" #include "trace.h" #include "ui/input.h" #include "ui/console.h" @@ -12,7 +10,7 @@ struct QemuInputHandlerState { DeviceState *dev; - QemuInputHandler *handler; + const QemuInputHandler *handler; int id; int events; QemuConsole *con; @@ -48,7 +46,7 @@ static uint32_t queue_count; static uint32_t queue_limit = 1024; QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev, - QemuInputHandler *handler) + const QemuInputHandler *handler) { QemuInputHandlerState *s = g_new0(QemuInputHandlerState, 1); static int id = 1; @@ -58,7 +56,7 @@ QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev, s->id = id++; QTAILQ_INSERT_TAIL(&handlers, s, node); - qemu_input_check_mode_change(); + notifier_list_notify(&mouse_mode_notifiers, NULL); return s; } @@ -66,21 +64,21 @@ void qemu_input_handler_activate(QemuInputHandlerState *s) { QTAILQ_REMOVE(&handlers, s, node); QTAILQ_INSERT_HEAD(&handlers, s, node); - qemu_input_check_mode_change(); + notifier_list_notify(&mouse_mode_notifiers, NULL); } void qemu_input_handler_deactivate(QemuInputHandlerState *s) { QTAILQ_REMOVE(&handlers, s, node); QTAILQ_INSERT_TAIL(&handlers, s, node); - qemu_input_check_mode_change(); + notifier_list_notify(&mouse_mode_notifiers, NULL); } void qemu_input_handler_unregister(QemuInputHandlerState *s) { QTAILQ_REMOVE(&handlers, s, node); g_free(s); - qemu_input_check_mode_change(); + notifier_list_notify(&mouse_mode_notifiers, NULL); } void qemu_input_handler_bind(QemuInputHandlerState *s, @@ -124,7 +122,7 @@ qemu_input_find_handler(uint32_t mask, QemuConsole *con) return NULL; } -void qmp_input_send_event(bool has_device, const char *device, +void qmp_input_send_event(const char *device, bool has_head, int64_t head, InputEventList *events, Error **errp) { @@ -133,7 +131,7 @@ void qmp_input_send_event(bool has_device, const char *device, Error *err = NULL; con = NULL; - if (has_device) { + if (device) { if (!has_head) { head = 0; } @@ -214,6 +212,7 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) InputKeyEvent *key; InputBtnEvent *btn; InputMoveEvent *move; + InputMultiTouchEvent *mtt; if (src) { idx = qemu_console_get_index(src); @@ -252,6 +251,11 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) name = InputAxis_str(move->axis); trace_input_event_abs(idx, name, move->value); break; + case INPUT_EVENT_KIND_MTT: + mtt = evt->u.mtt.data; + name = InputAxis_str(mtt->axis); + trace_input_event_mtt(idx, name, mtt->value); + break; case INPUT_EVENT_KIND__MAX: /* keep gcc happy */ break; @@ -364,7 +368,7 @@ void qemu_input_event_send(QemuConsole *src, InputEvent *evt) * when 'alt+print' was pressed. This flaw is now fixed and the * 'sysrq' key serves no further purpose. We normalize it to * 'print', so that downstream receivers of the event don't - * neeed to deal with this mistake + * need to deal with this mistake */ if (evt->type == INPUT_EVENT_KIND_KEY && evt->u.key.data->key->u.qcode.data == Q_KEY_CODE_SYSRQ) { @@ -490,12 +494,12 @@ void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map, } } -bool qemu_input_is_absolute(void) +bool qemu_input_is_absolute(QemuConsole *con) { QemuInputHandlerState *s; s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS, - NULL); + con); return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS); } @@ -543,19 +547,40 @@ void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value, qemu_input_event_send(src, &evt); } -void qemu_input_check_mode_change(void) +void qemu_input_queue_mtt(QemuConsole *src, InputMultiTouchType type, + int slot, int tracking_id) { - static int current_is_absolute; - int is_absolute; + InputMultiTouchEvent mtt = { + .type = type, + .slot = slot, + .tracking_id = tracking_id, + }; + InputEvent evt = { + .type = INPUT_EVENT_KIND_MTT, + .u.mtt.data = &mtt, + }; - is_absolute = qemu_input_is_absolute(); + qemu_input_event_send(src, &evt); +} - if (is_absolute != current_is_absolute) { - trace_input_mouse_mode(is_absolute); - notifier_list_notify(&mouse_mode_notifiers, NULL); - } +void qemu_input_queue_mtt_abs(QemuConsole *src, InputAxis axis, int value, + int min_in, int max_in, int slot, int tracking_id) +{ + InputMultiTouchEvent mtt = { + .type = INPUT_MULTI_TOUCH_TYPE_DATA, + .slot = slot, + .tracking_id = tracking_id, + .axis = axis, + .value = qemu_input_scale_axis(value, min_in, max_in, + INPUT_EVENT_ABS_MIN, + INPUT_EVENT_ABS_MAX), + }; + InputEvent evt = { + .type = INPUT_EVENT_KIND_MTT, + .u.mtt.data = &mtt, + }; - current_is_absolute = is_absolute; + qemu_input_event_send(src, &evt); } void qemu_add_mouse_mode_change_notifier(Notifier *notify) @@ -594,29 +619,29 @@ MouseInfoList *qmp_query_mice(Error **errp) return mice_list; } -void hmp_mouse_set(Monitor *mon, const QDict *qdict) +bool qemu_mouse_set(int index, Error **errp) { QemuInputHandlerState *s; - int index = qdict_get_int(qdict, "index"); - int found = 0; QTAILQ_FOREACH(s, &handlers, node) { - if (s->id != index) { - continue; + if (s->id == index) { + break; } - if (!(s->handler->mask & (INPUT_EVENT_MASK_REL | - INPUT_EVENT_MASK_ABS))) { - error_report("Input device '%s' is not a mouse", s->handler->name); - return; - } - found = 1; - qemu_input_handler_activate(s); - break; } - if (!found) { - error_report("Mouse at index '%d' not found", index); + if (!s) { + error_setg(errp, "Mouse at index '%d' not found", index); + return false; } - qemu_input_check_mode_change(); + if (!(s->handler->mask & (INPUT_EVENT_MASK_REL | + INPUT_EVENT_MASK_ABS))) { + error_setg(errp, "Input device '%s' is not a mouse", + s->handler->name); + return false; + } + + qemu_input_handler_activate(s); + notifier_list_notify(&mouse_mode_notifiers, NULL); + return true; } diff --git a/ui/kbd-state.c b/ui/kbd-state.c index 62d42a7a22..52ed28b8a8 100644 --- a/ui/kbd-state.c +++ b/ui/kbd-state.c @@ -117,6 +117,12 @@ void qkbd_state_lift_all_keys(QKbdState *kbd) } } +void qkbd_state_switch_console(QKbdState *kbd, QemuConsole *con) +{ + qkbd_state_lift_all_keys(kbd); + kbd->con = con; +} + void qkbd_state_set_delay(QKbdState *kbd, int delay_ms) { kbd->key_delay_ms = delay_ms; diff --git a/ui/keycodemapdb b/ui/keycodemapdb deleted file mode 160000 index d21009b1c9..0000000000 --- a/ui/keycodemapdb +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d21009b1c9f94b740ea66be8e48a1d8ad8124023 diff --git a/ui/keymaps.h b/ui/keymaps.h index 6473405485..3d52c0882a 100644 --- a/ui/keymaps.h +++ b/ui/keymaps.h @@ -44,7 +44,7 @@ typedef struct { /* "up" flag */ #define SCANCODE_UP 0x80 -/* Additional modifiers to use if not catched another way. */ +/* Additional modifiers to use if not caught another way. */ #define SCANCODE_SHIFT 0x100 #define SCANCODE_CTRL 0x200 #define SCANCODE_ALT 0x400 diff --git a/ui/meson.build b/ui/meson.build index e9f48c5315..a5ce22a678 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -1,9 +1,9 @@ -softmmu_ss.add(pixman) -specific_ss.add(when: ['CONFIG_SOFTMMU'], if_true: pixman) # for the include path -specific_ss.add(when: ['CONFIG_SOFTMMU'], if_true: opengl) # for the include path +system_ss.add(pixman) +specific_ss.add(when: ['CONFIG_SYSTEM_ONLY'], if_true: pixman) # for the include path +specific_ss.add(when: ['CONFIG_SYSTEM_ONLY'], if_true: opengl) # for the include path -softmmu_ss.add(png) -softmmu_ss.add(files( +system_ss.add(png) +system_ss.add(files( 'clipboard.c', 'console.c', 'cursor.c', @@ -14,19 +14,21 @@ softmmu_ss.add(files( 'kbd-state.c', 'keymaps.c', 'qemu-pixman.c', + 'ui-hmp-cmds.c', + 'ui-qmp-cmds.c', 'util.c', )) +system_ss.add(when: pixman, if_true: files('console-vc.c'), if_false: files('console-vc-stubs.c')) if dbus_display - softmmu_ss.add(files('dbus-module.c')) + system_ss.add(files('dbus-module.c')) endif -softmmu_ss.add([spice_headers, files('spice-module.c')]) -softmmu_ss.add(when: spice_protocol, if_true: files('vdagent.c')) +system_ss.add([spice_headers, files('spice-module.c')]) +system_ss.add(when: spice_protocol, if_true: files('vdagent.c')) -softmmu_ss.add(when: 'CONFIG_LINUX', if_true: files( - 'input-linux.c', - 'udmabuf.c', -)) -softmmu_ss.add(when: cocoa, if_true: files('cocoa.m')) +if host_os == 'linux' + system_ss.add(files('input-linux.c', 'udmabuf.c')) +endif +system_ss.add(when: cocoa, if_true: files('cocoa.m')) vnc_ss = ss.source_set() vnc_ss.add(files( @@ -43,8 +45,8 @@ vnc_ss.add(files( )) vnc_ss.add(zlib, jpeg, gnutls) vnc_ss.add(when: sasl, if_true: files('vnc-auth-sasl.c')) -softmmu_ss.add_all(when: vnc, if_true: vnc_ss) -softmmu_ss.add(when: vnc, if_false: files('vnc-stubs.c')) +system_ss.add_all(when: [vnc, pixman], if_true: vnc_ss) +system_ss.add(when: vnc, if_false: files('vnc-stubs.c')) ui_modules = {} @@ -54,34 +56,42 @@ if curses.found() ui_modules += {'curses' : curses_ss} endif -softmmu_ss.add(opengl) +system_ss.add(opengl) if opengl.found() opengl_ss = ss.source_set() - opengl_ss.add(gbm) - opengl_ss.add(when: [opengl, pixman], + opengl_ss.add(gbm, pixman) + opengl_ss.add(when: [opengl], if_true: files('shader.c', 'console-gl.c', 'egl-helpers.c', 'egl-context.c')) ui_modules += {'opengl' : opengl_ss} endif -if opengl.found() and gbm.found() +if opengl.found() egl_headless_ss = ss.source_set() - egl_headless_ss.add(when: [opengl, gbm, pixman], - if_true: files('egl-headless.c')) + egl_headless_ss.add(when: [opengl, pixman], + if_true: [files('egl-headless.c'), gbm]) ui_modules += {'egl-headless' : egl_headless_ss} endif if dbus_display dbus_ss = ss.source_set() + env = environment() + env.set('HOST_OS', host_os) + xml = custom_target('dbus-display preprocess', + input: 'dbus-display1.xml', + output: 'dbus-display1.xml', + env: env, + command: [xml_pp, '@INPUT@', '@OUTPUT@']) dbus_display1 = custom_target('dbus-display gdbus-codegen', output: ['dbus-display1.h', 'dbus-display1.c'], - input: files('dbus-display1.xml'), + input: xml, command: [gdbus_codegen, '@INPUT@', '--glib-min-required', '2.64', '--output-directory', meson.current_build_dir(), '--interface-prefix', 'org.qemu.', '--c-namespace', 'QemuDBus', '--generate-c-code', '@BASENAME@']) - dbus_ss.add(when: [gio, pixman, opengl], + dbus_display1_dep = declare_dependency(sources: dbus_display1, dependencies: gio) + dbus_ss.add(when: [gio, dbus_display1_dep], if_true: [files( 'dbus-chardev.c', 'dbus-clipboard.c', @@ -89,15 +99,20 @@ if dbus_display 'dbus-error.c', 'dbus-listener.c', 'dbus.c', - ), dbus_display1]) + ), opengl, gbm, pixman]) ui_modules += {'dbus' : dbus_ss} endif if gtk.found() - softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('win32-kbd-hook.c')) + if host_os == 'windows' + system_ss.add(files('win32-kbd-hook.c')) + endif gtk_ss = ss.source_set() - gtk_ss.add(gtk, vte, pixman, files('gtk.c', 'gtk-clipboard.c')) + gtk_ss.add(gtk, vte, pixman, files('gtk.c')) + if have_gtk_clipboard + gtk_ss.add(files('gtk-clipboard.c')) + endif gtk_ss.add(when: x11, if_true: files('x_keymap.c')) gtk_ss.add(when: opengl, if_true: files('gtk-gl-area.c')) gtk_ss.add(when: [x11, opengl], if_true: files('gtk-egl.c')) @@ -105,7 +120,9 @@ if gtk.found() endif if sdl.found() - softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('win32-kbd-hook.c')) + if host_os == 'windows' + system_ss.add(files('win32-kbd-hook.c')) + endif sdl_ss = ss.source_set() sdl_ss.add(sdl, sdl_image, pixman, glib, files( @@ -126,12 +143,12 @@ if spice.found() 'spice-display.c' )) ui_modules += {'spice-core' : spice_core_ss} -endif -if spice.found() and gio.found() - spice_ss = ss.source_set() - spice_ss.add(spice, gio, pixman, files('spice-app.c')) - ui_modules += {'spice-app': spice_ss} + if gio.found() + spice_ss = ss.source_set() + spice_ss.add(spice, gio, pixman, files('spice-app.c')) + ui_modules += {'spice-app': spice_ss} + endif endif keymaps = [ @@ -155,15 +172,15 @@ keymaps = [ ] if have_system or xkbcommon.found() + keycodemapdb_proj = subproject('keycodemapdb', required: true) foreach e : keymaps output = 'input-keymap-@0@-to-@1@.c.inc'.format(e[0], e[1]) genh += custom_target(output, output: output, capture: true, - input: files('keycodemapdb/data/keymaps.csv'), - command: [python, files('keycodemapdb/tools/keymap-gen'), - 'code-map', - '--lang', 'glib2', + input: keycodemapdb_proj.get_variable('keymaps_csv'), + command: [python, keycodemapdb_proj.get_variable('keymap_gen').full_path(), + 'code-map', '--lang', 'glib2', '--varname', 'qemu_input_map_@0@_to_@1@'.format(e[0], e[1]), '@INPUT0@', e[0], e[1]]) endforeach diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index 3ab7e2e958..5ca55dd199 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -6,6 +6,7 @@ #include "qemu/osdep.h" #include "ui/console.h" #include "standard-headers/drm/drm_fourcc.h" +#include "trace.h" PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format) { @@ -95,7 +96,9 @@ static const struct { } drm_format_pixman_map[] = { { DRM_FORMAT_RGB888, PIXMAN_LE_r8g8b8 }, { DRM_FORMAT_ARGB8888, PIXMAN_LE_a8r8g8b8 }, - { DRM_FORMAT_XRGB8888, PIXMAN_LE_x8r8g8b8 } + { DRM_FORMAT_XRGB8888, PIXMAN_LE_x8r8g8b8 }, + { DRM_FORMAT_XBGR8888, PIXMAN_LE_x8b8g8r8 }, + { DRM_FORMAT_ABGR8888, PIXMAN_LE_a8b8g8r8 }, }; pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format) @@ -142,6 +145,7 @@ int qemu_pixman_get_type(int rshift, int gshift, int bshift) return type; } +#ifdef CONFIG_PIXMAN pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf) { pixman_format_code_t format; @@ -155,6 +159,7 @@ pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf) } return format; } +#endif /* * Return true for known-good pixman conversions. @@ -183,6 +188,7 @@ bool qemu_pixman_check_format(DisplayChangeListener *dcl, } } +#ifdef CONFIG_PIXMAN pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format, int width) { @@ -199,14 +205,6 @@ void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb, x, y, 0, 0, 0, 0, width, 1); } -/* copy linebuf to framebuffer */ -void qemu_pixman_linebuf_copy(pixman_image_t *fb, int width, int x, int y, - pixman_image_t *linebuf) -{ - pixman_image_composite(PIXMAN_OP_SRC, linebuf, NULL, fb, - 0, 0, 0, 0, x, y, width, 1); -} - pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format, pixman_image_t *image) { @@ -216,6 +214,7 @@ pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format, NULL, pixman_image_get_stride(image)); } +#endif void qemu_pixman_image_unref(pixman_image_t *image) { @@ -225,17 +224,7 @@ void qemu_pixman_image_unref(pixman_image_t *image) pixman_image_unref(image); } -pixman_color_t qemu_pixman_color(PixelFormat *pf, uint32_t color) -{ - pixman_color_t c; - - c.red = ((color & pf->rmask) >> pf->rshift) << (16 - pf->rbits); - c.green = ((color & pf->gmask) >> pf->gshift) << (16 - pf->gbits); - c.blue = ((color & pf->bmask) >> pf->bshift) << (16 - pf->bbits); - c.alpha = ((color & pf->amask) >> pf->ashift) << (16 - pf->abits); - return c; -} - +#ifdef CONFIG_PIXMAN pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font, unsigned int ch) { @@ -278,3 +267,4 @@ void qemu_pixman_glyph_render(pixman_image_t *glyph, pixman_image_unref(ifg); pixman_image_unref(ibg); } +#endif /* CONFIG_PIXMAN */ diff --git a/ui/sdl2-2d.c b/ui/sdl2-2d.c index bfebbdeaea..06468cd493 100644 --- a/ui/sdl2-2d.c +++ b/ui/sdl2-2d.c @@ -150,7 +150,7 @@ bool sdl2_2d_check_format(DisplayChangeListener *dcl, { /* * We let SDL convert for us a few more formats than, - * the native ones. Thes are the ones I have tested. + * the native ones. These are the ones I have tested. */ return (format == PIXMAN_x8r8g8b8 || format == PIXMAN_a8r8g8b8 || diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c index 39cab8cde7..28d796607c 100644 --- a/ui/sdl2-gl.c +++ b/ui/sdl2-gl.c @@ -67,6 +67,10 @@ void sdl2_gl_update(DisplayChangeListener *dcl, assert(scon->opengl); + if (!scon->real_window) { + return; + } + SDL_GL_MakeCurrent(scon->real_window, scon->winctx); surface_gl_update_texture(scon->gls, scon->surface, x, y, w, h); scon->updates++; @@ -201,7 +205,8 @@ void sdl2_gl_scanout_texture(DisplayChangeListener *dcl, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, - uint32_t w, uint32_t h) + uint32_t w, uint32_t h, + void *d3d_tex2d) { struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); diff --git a/ui/sdl2-input.c b/ui/sdl2-input.c index f068382209..b02a89ee7c 100644 --- a/ui/sdl2-input.c +++ b/ui/sdl2-input.c @@ -43,15 +43,16 @@ void sdl2_process_key(struct sdl2_console *scon, ev->type == SDL_KEYDOWN ? "down" : "up"); qkbd_state_key_event(scon->kbd, qcode, ev->type == SDL_KEYDOWN); - if (!qemu_console_is_graphic(con)) { + if (QEMU_IS_TEXT_CONSOLE(con)) { + QemuTextConsole *s = QEMU_TEXT_CONSOLE(con); bool ctrl = qkbd_state_modifier_get(scon->kbd, QKBD_MOD_CTRL); if (ev->type == SDL_KEYDOWN) { switch (qcode) { case Q_KEY_CODE_RET: - kbd_put_keysym_console(con, '\n'); + qemu_text_console_put_keysym(s, '\n'); break; default: - kbd_put_qcode_console(con, qcode, ctrl); + qemu_text_console_put_qcode(s, qcode, ctrl); break; } } diff --git a/ui/sdl2.c b/ui/sdl2.c index 8cb77416af..4971963f00 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -58,6 +58,11 @@ static Notifier mouse_mode_notifier; #define SDL2_MAX_IDLE_COUNT (2 * GUI_REFRESH_INTERVAL_DEFAULT \ / SDL2_REFRESH_INTERVAL_BUSY + 1) +/* introduced in SDL 2.0.10 */ +#ifndef SDL_HINT_RENDER_BATCHING +#define SDL_HINT_RENDER_BATCHING "SDL_RENDER_BATCHING" +#endif + static void sdl_update_caption(struct sdl2_console *scon); static struct sdl2_console *get_scon_from_window(uint32_t window_id) @@ -99,9 +104,20 @@ void sdl2_window_create(struct sdl2_console *scon) surface_width(scon->surface), surface_height(scon->surface), flags); - scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); if (scon->opengl) { - scon->winctx = SDL_GL_GetCurrentContext(); + const char *driver = "opengl"; + + if (scon->opts->gl == DISPLAYGL_MODE_ES) { + driver = "opengles2"; + } + + SDL_SetHint(SDL_HINT_RENDER_DRIVER, driver); + SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1"); + + scon->winctx = SDL_GL_CreateContext(scon->real_window); + } else { + /* The SDL renderer is only used by sdl2-2D, when OpenGL is disabled */ + scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); } sdl_update_caption(scon); } @@ -112,8 +128,14 @@ void sdl2_window_destroy(struct sdl2_console *scon) return; } - SDL_DestroyRenderer(scon->real_renderer); - scon->real_renderer = NULL; + if (scon->winctx) { + SDL_GL_DeleteContext(scon->winctx); + scon->winctx = NULL; + } + if (scon->real_renderer) { + SDL_DestroyRenderer(scon->real_renderer); + scon->real_renderer = NULL; + } SDL_DestroyWindow(scon->real_window); scon->real_window = NULL; } @@ -150,11 +172,19 @@ static void sdl_update_caption(struct sdl2_console *scon) status = " [Stopped]"; } else if (gui_grab) { if (alt_grab) { +#ifdef CONFIG_DARWIN + status = " - Press ⌃⌥⇧G to exit grab"; +#else status = " - Press Ctrl-Alt-Shift-G to exit grab"; +#endif } else if (ctrl_grab) { status = " - Press Right-Ctrl-G to exit grab"; } else { +#ifdef CONFIG_DARWIN + status = " - Press ⌃⌥G to exit grab"; +#else status = " - Press Ctrl-Alt-G to exit grab"; +#endif } } @@ -181,7 +211,7 @@ static void sdl_hide_cursor(struct sdl2_console *scon) SDL_ShowCursor(SDL_DISABLE); SDL_SetCursor(sdl_cursor_hidden); - if (!qemu_input_is_absolute()) { + if (!qemu_input_is_absolute(scon->dcl.con)) { SDL_SetRelativeMouseMode(SDL_TRUE); } } @@ -192,12 +222,12 @@ static void sdl_show_cursor(struct sdl2_console *scon) return; } - if (!qemu_input_is_absolute()) { + if (!qemu_input_is_absolute(scon->dcl.con)) { SDL_SetRelativeMouseMode(SDL_FALSE); } if (guest_cursor && - (gui_grab || qemu_input_is_absolute() || absolute_enabled)) { + (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled)) { SDL_SetCursor(guest_sprite); } else { SDL_SetCursor(sdl_cursor_normal); @@ -223,7 +253,7 @@ static void sdl_grab_start(struct sdl2_console *scon) } if (guest_cursor) { SDL_SetCursor(guest_sprite); - if (!qemu_input_is_absolute() && !absolute_enabled) { + if (!qemu_input_is_absolute(scon->dcl.con) && !absolute_enabled) { SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y); } } else { @@ -258,7 +288,7 @@ static void absolute_mouse_grab(struct sdl2_console *scon) static void sdl_mouse_mode_change(Notifier *notify, void *data) { - if (qemu_input_is_absolute()) { + if (qemu_input_is_absolute(sdl2_console[0].dcl.con)) { if (!absolute_enabled) { absolute_enabled = 1; SDL_SetRelativeMouseMode(SDL_FALSE); @@ -289,7 +319,7 @@ static void sdl_send_mouse_event(struct sdl2_console *scon, int dx, int dy, prev_state = state; } - if (qemu_input_is_absolute()) { + if (qemu_input_is_absolute(scon->dcl.con)) { qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, x, 0, surface_width(scon->surface)); qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, @@ -461,10 +491,9 @@ static void handle_textinput(SDL_Event *ev) return; } - if (qemu_console_is_graphic(con)) { - return; + if (QEMU_IS_TEXT_CONSOLE(con)) { + qemu_text_console_put_string(QEMU_TEXT_CONSOLE(con), ev->text.text, strlen(ev->text.text)); } - kbd_put_string_console(con, ev->text.text, strlen(ev->text.text)); } static void handle_mousemotion(SDL_Event *ev) @@ -476,7 +505,7 @@ static void handle_mousemotion(SDL_Event *ev) return; } - if (qemu_input_is_absolute() || absolute_enabled) { + if (qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { int scr_w, scr_h; SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); max_x = scr_w - 1; @@ -492,7 +521,7 @@ static void handle_mousemotion(SDL_Event *ev) sdl_grab_start(scon); } } - if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { + if (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel, ev->motion.x, ev->motion.y, ev->motion.state); } @@ -509,7 +538,7 @@ static void handle_mousebutton(SDL_Event *ev) } bev = &ev->button; - if (!gui_grab && !qemu_input_is_absolute()) { + if (!gui_grab && !qemu_input_is_absolute(scon->dcl.con)) { if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) { /* start grabbing all events */ sdl_grab_start(scon); @@ -582,7 +611,7 @@ static void handle_windowevent(SDL_Event *ev) } /* fall through */ case SDL_WINDOWEVENT_ENTER: - if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) { + if (!gui_grab && (qemu_input_is_absolute(scon->dcl.con) || absolute_enabled)) { absolute_mouse_grab(scon); } /* If a new console window opened using a hotkey receives the @@ -712,9 +741,9 @@ static void sdl_mouse_warp(DisplayChangeListener *dcl, if (!guest_cursor) { sdl_show_cursor(scon); } - if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { + if (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { SDL_SetCursor(guest_sprite); - if (!qemu_input_is_absolute() && !absolute_enabled) { + if (!qemu_input_is_absolute(scon->dcl.con) && !absolute_enabled) { SDL_WarpMouseInWindow(scon->real_window, x, y); } } @@ -752,7 +781,7 @@ static void sdl_mouse_define(DisplayChangeListener *dcl, return; } if (guest_cursor && - (gui_grab || qemu_input_is_absolute() || absolute_enabled)) { + (gui_grab || qemu_input_is_absolute(dcl->con) || absolute_enabled)) { SDL_SetCursor(guest_sprite); } } @@ -825,21 +854,9 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) assert(o->type == DISPLAY_TYPE_SDL); -#ifdef __linux__ - /* on Linux, SDL may use fbcon|directfb|svgalib when run without - * accessible $DISPLAY to open X11 window. This is often the case - * when qemu is run using sudo. But in this case, and when actually - * run in X11 environment, SDL fights with X11 for the video card, - * making current display unavailable, often until reboot. - * So make x11 the default SDL video driver if this variable is unset. - * This is a bit hackish but saves us from bigger problem. - * Maybe it's a good idea to fix this in SDL instead. - */ - if (!g_setenv("SDL_VIDEODRIVER", "x11", 0)) { - fprintf(stderr, "Could not set SDL_VIDEODRIVER environment variable\n"); - exit(1); + if (SDL_GetHintBoolean("QEMU_ENABLE_SDL_LOGGING", SDL_FALSE)) { + SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); } -#endif if (SDL_Init(SDL_INIT_VIDEO)) { fprintf(stderr, "Could not initialize SDL(%s) - exiting\n", @@ -849,7 +866,14 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) #ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR /* only available since SDL 2.0.8 */ SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); #endif +#ifndef CONFIG_WIN32 + /* QEMU uses its own low level keyboard hook procedure on Windows */ SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1"); +#endif +#ifdef SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED + SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0"); +#endif + SDL_SetHint(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, "1"); memset(&info, 0, sizeof(info)); SDL_VERSION(&info.version); diff --git a/ui/shader/meson.build b/ui/shader/meson.build index 592bf596b9..3137e65578 100644 --- a/ui/shader/meson.build +++ b/ui/shader/meson.build @@ -10,5 +10,6 @@ foreach e : shaders output: output, capture: true, input: files('@0@.@1@'.format(e[0], e[1])), + build_by_default: false, command: [shaderinclude, '@INPUT0@']) endforeach diff --git a/ui/shader/texture-blit-flip.vert b/ui/shader/texture-blit-flip.vert index ba081fa5a6..f7a448d229 100644 --- a/ui/shader/texture-blit-flip.vert +++ b/ui/shader/texture-blit-flip.vert @@ -1,4 +1,3 @@ - #version 300 es in vec2 in_position; diff --git a/ui/shader/texture-blit.frag b/ui/shader/texture-blit.frag index bfa202c22b..8ed95a46b6 100644 --- a/ui/shader/texture-blit.frag +++ b/ui/shader/texture-blit.frag @@ -1,4 +1,3 @@ - #version 300 es uniform sampler2D image; diff --git a/ui/shader/texture-blit.vert b/ui/shader/texture-blit.vert index 6fe2744d68..fb48d70665 100644 --- a/ui/shader/texture-blit.vert +++ b/ui/shader/texture-blit.vert @@ -1,4 +1,3 @@ - #version 300 es in vec2 in_position; diff --git a/ui/spice-app.c b/ui/spice-app.c index 7e71e18da9..a10b4a58fe 100644 --- a/ui/spice-app.c +++ b/ui/spice-app.c @@ -29,6 +29,7 @@ #include "ui/console.h" #include "ui/spice-display.h" #include "qemu/config-file.h" +#include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/cutils.h" #include "qemu/module.h" @@ -95,6 +96,11 @@ static void vc_chr_set_echo(Chardev *chr, bool echo) /* TODO: set echo for frontends QMP and qtest */ } +static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp) +{ + /* fqdn is dealt with in vc_chr_open() */ +} + static void char_vc_class_init(ObjectClass *oc, void *data) { VCChardevClass *vc = CHARDEV_VC_CLASS(oc); @@ -102,7 +108,7 @@ static void char_vc_class_init(ObjectClass *oc, void *data) vc->parent_open = cc->open; - cc->parse = qemu_chr_parse_vc; + cc->parse = vc_chr_parse; cc->open = vc_chr_open; cc->chr_set_echo = vc_chr_set_echo; } @@ -214,6 +220,7 @@ static QemuDisplay qemu_display_spice_app = { .type = DISPLAY_TYPE_SPICE_APP, .early_init = spice_app_display_early_init, .init = spice_app_display_init, + .vc = "vc", }; static void register_spice_app(void) diff --git a/ui/spice-core.c b/ui/spice-core.c index c3ac20ad43..15be640286 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -42,7 +42,7 @@ /* core bits */ static SpiceServer *spice_server; -static Notifier migration_state; +static NotifierWithReturn migration_state; static const char *auth = "spice"; static char *auth_passwd; static time_t auth_expires = TIME_MAX; @@ -90,13 +90,23 @@ struct SpiceWatch { static void watch_read(void *opaque) { SpiceWatch *watch = opaque; - watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque); + int fd = watch->fd; + +#ifdef WIN32 + fd = _get_osfhandle(fd); +#endif + watch->func(fd, SPICE_WATCH_EVENT_READ, watch->opaque); } static void watch_write(void *opaque) { SpiceWatch *watch = opaque; - watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque); + int fd = watch->fd; + +#ifdef WIN32 + fd = _get_osfhandle(fd); +#endif + watch->func(fd, SPICE_WATCH_EVENT_WRITE, watch->opaque); } static void watch_update_mask(SpiceWatch *watch, int event_mask) @@ -117,6 +127,14 @@ static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void * { SpiceWatch *watch; +#ifdef WIN32 + fd = _open_osfhandle(fd, _O_BINARY); + if (fd < 0) { + error_setg_win32(&error_warn, WSAGetLastError(), "Couldn't associate a FD with the SOCKET"); + return NULL; + } +#endif + watch = g_malloc0(sizeof(*watch)); watch->fd = fd; watch->func = func; @@ -129,6 +147,10 @@ static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void * static void watch_remove(SpiceWatch *watch) { qemu_set_fd_handler(watch->fd, NULL, NULL, NULL); +#ifdef WIN32 + /* SOCKET is owned by spice */ + qemu_close_socket_osfhandle(watch->fd); +#endif g_free(watch); } @@ -195,12 +217,12 @@ static void channel_event(int event, SpiceChannelEventInfo *info) * not do that. It isn't that easy to fix it in spice and even * when it is fixed we still should cover the already released * spice versions. So detect that we've been called from another - * thread and grab the iothread lock if so before calling qemu + * thread and grab the BQL if so before calling qemu * functions. */ bool need_lock = !qemu_thread_is_self(&me); if (need_lock) { - qemu_mutex_lock_iothread(); + bql_lock(); } if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) { @@ -222,7 +244,6 @@ static void channel_event(int event, SpiceChannelEventInfo *info) break; case SPICE_CHANNEL_EVENT_INITIALIZED: if (auth) { - server->has_auth = true; server->auth = g_strdup(auth); } add_channel_info(client, info); @@ -239,7 +260,7 @@ static void channel_event(int event, SpiceChannelEventInfo *info) } if (need_lock) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } qapi_free_SpiceServerInfo(server); @@ -413,9 +434,6 @@ static QemuOptsList qemu_spice_opts = { .name = "unix", .type = QEMU_OPT_BOOL, #endif - },{ - .name = "password", - .type = QEMU_OPT_STRING, },{ .name = "password-secret", .type = QEMU_OPT_STRING, @@ -522,13 +540,9 @@ static SpiceInfo *qmp_query_spice_real(Error **errp) port = qemu_opt_get_number(opts, "port", 0); tls_port = qemu_opt_get_number(opts, "tls-port", 0); - info->has_auth = true; info->auth = g_strdup(auth); - - info->has_host = true; info->host = g_strdup(addr ? addr : "*"); - info->has_compiled_version = true; major = (SPICE_SERVER_VERSION & 0xff0000) >> 16; minor = (SPICE_SERVER_VERSION & 0xff00) >> 8; micro = SPICE_SERVER_VERSION & 0xff; @@ -554,24 +568,23 @@ static SpiceInfo *qmp_query_spice_real(Error **errp) return info; } -static void migration_state_notifier(Notifier *notifier, void *data) +static int migration_state_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, Error **errp) { - MigrationState *s = data; - if (!spice_have_target_host) { - return; + return 0; } - if (migration_in_setup(s)) { + if (e->type == MIG_EVENT_PRECOPY_SETUP) { spice_server_migrate_start(spice_server); - } else if (migration_has_finished(s) || - migration_in_postcopy_after_devices(s)) { + } else if (e->type == MIG_EVENT_PRECOPY_DONE) { spice_server_migrate_end(spice_server, true); spice_have_target_host = false; - } else if (migration_has_failed(s)) { + } else if (e->type == MIG_EVENT_PRECOPY_FAILED) { spice_server_migrate_end(spice_server, false); spice_have_target_host = false; } + return 0; } int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, @@ -671,20 +684,8 @@ static void qemu_spice_init(void) } passwordSecret = qemu_opt_get(opts, "password-secret"); if (passwordSecret) { - if (qemu_opt_get(opts, "password")) { - error_report("'password' option is mutually exclusive with " - "'password-secret'"); - exit(1); - } password = qcrypto_secret_lookup_as_utf8(passwordSecret, &error_fatal); - } else { - str = qemu_opt_get(opts, "password"); - if (str) { - warn_report("'password' option is deprecated and insecure, " - "use 'password-secret' instead"); - password = g_strdup(str); - } } if (tls_port) { @@ -819,8 +820,7 @@ static void qemu_spice_init(void) }; using_spice = 1; - migration_state.notify = migration_state_notifier; - add_migration_state_change_notifier(&migration_state); + migration_add_notifier(&migration_state, migration_state_notifier); spice_migrate.base.sif = &migrate_interface.base; qemu_spice.add_interface(&spice_migrate.base); @@ -840,12 +840,7 @@ static void qemu_spice_init(void) "incompatible with -spice port/tls-port"); exit(1); } - if (egl_rendernode_init(qemu_opt_get(opts, "rendernode"), - DISPLAYGL_MODE_ON) != 0) { - error_report("Failed to initialize EGL render node for SPICE GL"); - exit(1); - } - display_opengl = 1; + egl_init(qemu_opt_get(opts, "rendernode"), DISPLAYGL_MODE_ON, &error_fatal); spice_opengl = 1; } #endif @@ -933,6 +928,9 @@ static int qemu_spice_set_pw_expire(time_t expires) static int qemu_spice_display_add_client(int csock, int skipauth, int tls) { +#ifdef WIN32 + csock = qemu_close_socket_osfhandle(csock); +#endif if (tls) { return spice_server_add_ssl_client(spice_server, csock, skipauth); } else { diff --git a/ui/spice-display.c b/ui/spice-display.c index 494168e7fe..6eb98a5a5c 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "ui/qemu-spice.h" +#include "qemu/error-report.h" #include "qemu/timer.h" #include "qemu/lockable.h" #include "qemu/main-loop.h" @@ -188,7 +189,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) { static const int blksize = 32; int blocks = DIV_ROUND_UP(surface_width(ssd->ds), blksize); - int dirty_top[blocks]; + g_autofree int *dirty_top = NULL; int y, yoff1, yoff2, x, xoff, blk, bw; int bpp = surface_bytes_per_pixel(ssd->ds); uint8_t *guest, *mirror; @@ -197,6 +198,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) return; }; + dirty_top = g_new(int, blocks); for (blk = 0; blk < blocks; blk++) { dirty_top[blk] = -1; } @@ -435,7 +437,7 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd, } if (ssd->ds) { ssd->surface = pixman_image_ref(ssd->ds->image); - ssd->mirror = qemu_pixman_mirror_create(ssd->ds->format, + ssd->mirror = qemu_pixman_mirror_create(surface_format(ssd->ds), ssd->ds->image); qemu_spice_create_host_primary(ssd); } @@ -459,11 +461,11 @@ void qemu_spice_cursor_refresh_bh(void *opaque) if (ssd->cursor) { QEMUCursor *c = ssd->cursor; assert(ssd->dcl.con); - cursor_get(c); + cursor_ref(c); qemu_mutex_unlock(&ssd->lock); dpy_cursor_define(ssd->dcl.con, c); qemu_mutex_lock(&ssd->lock); - cursor_put(c); + cursor_unref(c); } if (ssd->mouse_x != -1 && ssd->mouse_y != -1) { @@ -517,13 +519,6 @@ static void interface_set_compression_level(QXLInstance *sin, int level) /* nothing to do */ } -#if SPICE_NEEDS_SET_MM_TIME -static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) -{ - /* nothing to do */ -} -#endif - static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) { SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); @@ -715,9 +710,6 @@ static const QXLInterface dpy_interface = { .attache_worker = interface_attach_worker, #endif .set_compression_level = interface_set_compression_level, -#if SPICE_NEEDS_SET_MM_TIME - .set_mm_time = interface_set_mm_time, -#endif .get_init_info = interface_get_init_info, /* the callbacks below are called from spice server thread context */ @@ -774,8 +766,8 @@ static void display_mouse_define(DisplayChangeListener *dcl, SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); qemu_mutex_lock(&ssd->lock); - cursor_get(c); - cursor_put(ssd->cursor); + cursor_ref(c); + cursor_unref(ssd->cursor); ssd->cursor = c; ssd->hot_x = c->hot_x; ssd->hot_y = c->hot_y; @@ -944,7 +936,8 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, - uint32_t w, uint32_t h) + uint32_t w, uint32_t h, + void *d3d_tex2d) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); EGLint stride = 0, fourcc = 0; @@ -1088,15 +1081,16 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, } if (render_cursor) { - int x, y; + int ptr_x, ptr_y; + qemu_mutex_lock(&ssd->lock); - x = ssd->ptr_x; - y = ssd->ptr_y; + ptr_x = ssd->ptr_x; + ptr_y = ssd->ptr_y; qemu_mutex_unlock(&ssd->lock); egl_texture_blit(ssd->gls, &ssd->blit_fb, &ssd->guest_fb, !y_0_top); egl_texture_blend(ssd->gls, &ssd->blit_fb, &ssd->cursor_fb, - !y_0_top, x, y, 1.0, 1.0); + !y_0_top, ptr_x, ptr_y, 1.0, 1.0); glFlush(); } diff --git a/ui/spice-input.c b/ui/spice-input.c index bbd502564e..a5c5d78474 100644 --- a/ui/spice-input.c +++ b/ui/spice-input.c @@ -224,7 +224,7 @@ static const SpiceTabletInterface tablet_interface = { static void mouse_mode_notifier(Notifier *notifier, void *data) { QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode); - bool is_absolute = qemu_input_is_absolute(); + bool is_absolute = qemu_input_is_absolute(NULL); if (pointer->absolute == is_absolute) { return; diff --git a/ui/trace-events b/ui/trace-events index f78b5e6606..e6a2894303 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -9,7 +9,7 @@ console_putchar_unhandled(int ch) "unhandled escape character '%c'" console_txt_new(int w, int h) "%dx%d" console_select(int nr) "%d" console_refresh(int interval) "interval %d ms" -displaysurface_create(void *display_surface, int w, int h) "surface=%p, %dx%d" +displaysurface_create(int w, int h) "%dx%d" displaysurface_create_from(void *display_surface, int w, int h, uint32_t format) "surface=%p, %dx%d, format 0x%x" displaysurface_create_pixman(void *display_surface) "surface=%p" displaysurface_free(void *display_surface) "surface=%p" @@ -26,6 +26,8 @@ gd_key_event(const char *tab, int gdk_keycode, int qkeycode, const char *action) gd_grab(const char *tab, const char *device, const char *reason) "tab=%s, dev=%s, reason=%s" gd_ungrab(const char *tab, const char *device) "tab=%s, dev=%s" gd_keymap_windowing(const char *name) "backend=%s" +gd_gl_area_create_context(void *ctx, int major, int minor) "ctx=%p, major=%d, minor=%d" +gd_gl_area_destroy_context(void *ctx, void *current_ctx) "ctx=%p, current_ctx=%p" # vnc-auth-sasl.c # vnc-auth-vencrypt.c @@ -88,8 +90,8 @@ input_event_key_qcode(int conidx, const char *qcode, bool down) "con %d, key qco input_event_btn(int conidx, const char *btn, bool down) "con %d, button %s, down %d" input_event_rel(int conidx, const char *axis, int value) "con %d, axis %s, value %d" input_event_abs(int conidx, const char *axis, int value) "con %d, axis %s, value 0x%x" +input_event_mtt(int conidx, const char *axis, int value) "con %d, axis %s, value 0x%x" input_event_sync(void) "" -input_mouse_mode(int absolute) "absolute %d" # sdl2-input.c sdl2_process_key(int sdl_scancode, int qcode, const char *action) "translated SDL scancode %d to QKeyCode %d (%s)" @@ -125,15 +127,20 @@ xkeymap_vendor(const char *name) "vendor '%s'" xkeymap_keycodes(const char *name) "keycodes '%s'" xkeymap_keymap(const char *name) "keymap '%s'" +# clipboard.c +clipboard_check_serial(int cur, int recv, bool ok) "cur:%d recv:%d %d" + # vdagent.c vdagent_open(void) "" vdagent_close(void) "" +vdagent_disconnect(void) "" vdagent_send(const char *name) "msg %s" vdagent_send_empty_clipboard(void) "" vdagent_recv_chunk(uint32_t size) "size %d" vdagent_recv_msg(const char *name, uint32_t size) "msg %s, size %d" vdagent_peer_cap(const char *name) "cap %s" vdagent_cb_grab_selection(const char *name) "selection %s" +vdagent_cb_grab_discard(const char *name, int cur, int recv) "selection %s, cur:%d recv:%d" vdagent_cb_grab_type(const char *name) "type %s" vdagent_cb_serial_discard(uint32_t current, uint32_t received) "current=%u, received=%u" @@ -146,7 +153,15 @@ dbus_mouse_press(unsigned int button) "button %u" dbus_mouse_release(unsigned int button) "button %u" dbus_mouse_set_pos(unsigned int x, unsigned int y) "x=%u, y=%u" dbus_mouse_rel_motion(int dx, int dy) "dx=%d, dy=%d" +dbus_touch_send_event(unsigned int kind, uint32_t num_slot, uint32_t x, uint32_t y) "kind=%u, num_slot=%u, x=%d, y=%d" dbus_update(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d" +dbus_update_gl(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d" dbus_clipboard_grab_failed(void) "" dbus_clipboard_register(const char *bus_name) "peer %s" dbus_clipboard_unregister(const char *bus_name) "peer %s" +dbus_scanout_texture(uint32_t tex_id, bool backing_y_0_top, uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h) "tex_id:%u y0top:%d back:%ux%u %u+%u-%ux%u" +dbus_gl_gfx_switch(void *p) "surf: %p" +dbus_filter(unsigned int serial, unsigned int filter) "serial=%u (<= %u)" + +# egl-helpers.c +egl_init_d3d11_device(void *p) "d3d device: %p" diff --git a/ui/udmabuf.c b/ui/udmabuf.c index cebceb2610..6a0a11a85d 100644 --- a/ui/udmabuf.c +++ b/ui/udmabuf.c @@ -7,8 +7,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "ui/console.h" +#include "qemu/error-report.h" -#include #include int udmabuf_fd(void) diff --git a/ui/ui-hmp-cmds.c b/ui/ui-hmp-cmds.c new file mode 100644 index 0000000000..26c8ced1f2 --- /dev/null +++ b/ui/ui-hmp-cmds.c @@ -0,0 +1,479 @@ +/* + * HMP commands related to UI + * + * 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. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#ifdef CONFIG_SPICE +#include +#endif +#include "monitor/hmp.h" +#include "monitor/monitor-internal.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-ui.h" +#include "qapi/qmp/qdict.h" +#include "qemu/cutils.h" +#include "ui/console.h" +#include "ui/input.h" + +static int mouse_button_state; + +void hmp_mouse_move(Monitor *mon, const QDict *qdict) +{ + int dx, dy, dz, button; + const char *dx_str = qdict_get_str(qdict, "dx_str"); + const char *dy_str = qdict_get_str(qdict, "dy_str"); + const char *dz_str = qdict_get_try_str(qdict, "dz_str"); + + dx = strtol(dx_str, NULL, 0); + dy = strtol(dy_str, NULL, 0); + qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx); + qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy); + + if (dz_str) { + dz = strtol(dz_str, NULL, 0); + if (dz != 0) { + button = (dz > 0) ? INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN; + qemu_input_queue_btn(NULL, button, true); + qemu_input_event_sync(); + qemu_input_queue_btn(NULL, button, false); + } + } + qemu_input_event_sync(); +} + +void hmp_mouse_button(Monitor *mon, const QDict *qdict) +{ + static uint32_t bmap[INPUT_BUTTON__MAX] = { + [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, + [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, + [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, + }; + int button_state = qdict_get_int(qdict, "button_state"); + + if (mouse_button_state == button_state) { + return; + } + qemu_input_update_buttons(NULL, bmap, mouse_button_state, button_state); + qemu_input_event_sync(); + mouse_button_state = button_state; +} + +void hmp_mouse_set(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qemu_mouse_set(qdict_get_int(qdict, "index"), &err); + hmp_handle_error(mon, err); +} + +void hmp_info_mice(Monitor *mon, const QDict *qdict) +{ + MouseInfoList *mice_list, *mouse; + + mice_list = qmp_query_mice(NULL); + if (!mice_list) { + monitor_printf(mon, "No mouse devices connected\n"); + return; + } + + for (mouse = mice_list; mouse; mouse = mouse->next) { + monitor_printf(mon, "%c Mouse #%" PRId64 ": %s%s\n", + mouse->value->current ? '*' : ' ', + mouse->value->index, mouse->value->name, + mouse->value->absolute ? " (absolute)" : ""); + } + + qapi_free_MouseInfoList(mice_list); +} + +#ifdef CONFIG_VNC +/* Helper for hmp_info_vnc_clients, _servers */ +static void hmp_info_VncBasicInfo(Monitor *mon, VncBasicInfo *info, + const char *name) +{ + monitor_printf(mon, " %s: %s:%s (%s%s)\n", + name, + info->host, + info->service, + NetworkAddressFamily_str(info->family), + info->websocket ? " (Websocket)" : ""); +} + +/* Helper displaying and auth and crypt info */ +static void hmp_info_vnc_authcrypt(Monitor *mon, const char *indent, + VncPrimaryAuth auth, + VncVencryptSubAuth *vencrypt) +{ + monitor_printf(mon, "%sAuth: %s (Sub: %s)\n", indent, + VncPrimaryAuth_str(auth), + vencrypt ? VncVencryptSubAuth_str(*vencrypt) : "none"); +} + +static void hmp_info_vnc_clients(Monitor *mon, VncClientInfoList *client) +{ + while (client) { + VncClientInfo *cinfo = client->value; + + hmp_info_VncBasicInfo(mon, qapi_VncClientInfo_base(cinfo), "Client"); + monitor_printf(mon, " x509_dname: %s\n", + cinfo->x509_dname ?: "none"); + monitor_printf(mon, " sasl_username: %s\n", + cinfo->sasl_username ?: "none"); + + client = client->next; + } +} + +static void hmp_info_vnc_servers(Monitor *mon, VncServerInfo2List *server) +{ + while (server) { + VncServerInfo2 *sinfo = server->value; + hmp_info_VncBasicInfo(mon, qapi_VncServerInfo2_base(sinfo), "Server"); + hmp_info_vnc_authcrypt(mon, " ", sinfo->auth, + sinfo->has_vencrypt ? &sinfo->vencrypt : NULL); + server = server->next; + } +} + +void hmp_info_vnc(Monitor *mon, const QDict *qdict) +{ + VncInfo2List *info2l, *info2l_head; + Error *err = NULL; + + info2l = qmp_query_vnc_servers(&err); + info2l_head = info2l; + if (hmp_handle_error(mon, err)) { + return; + } + if (!info2l) { + monitor_printf(mon, "None\n"); + return; + } + + while (info2l) { + VncInfo2 *info = info2l->value; + monitor_printf(mon, "%s:\n", info->id); + hmp_info_vnc_servers(mon, info->server); + hmp_info_vnc_clients(mon, info->clients); + if (!info->server) { + /* + * The server entry displays its auth, we only need to + * display in the case of 'reverse' connections where + * there's no server. + */ + hmp_info_vnc_authcrypt(mon, " ", info->auth, + info->has_vencrypt ? &info->vencrypt : NULL); + } + if (info->display) { + monitor_printf(mon, " Display: %s\n", info->display); + } + info2l = info2l->next; + } + + qapi_free_VncInfo2List(info2l_head); + +} +#endif + +#ifdef CONFIG_SPICE +void hmp_info_spice(Monitor *mon, const QDict *qdict) +{ + SpiceChannelList *chan; + SpiceInfo *info; + const char *channel_name; + static const char *const channel_names[] = { + [SPICE_CHANNEL_MAIN] = "main", + [SPICE_CHANNEL_DISPLAY] = "display", + [SPICE_CHANNEL_INPUTS] = "inputs", + [SPICE_CHANNEL_CURSOR] = "cursor", + [SPICE_CHANNEL_PLAYBACK] = "playback", + [SPICE_CHANNEL_RECORD] = "record", + [SPICE_CHANNEL_TUNNEL] = "tunnel", + [SPICE_CHANNEL_SMARTCARD] = "smartcard", + [SPICE_CHANNEL_USBREDIR] = "usbredir", + [SPICE_CHANNEL_PORT] = "port", + [SPICE_CHANNEL_WEBDAV] = "webdav", + }; + + info = qmp_query_spice(NULL); + + if (!info->enabled) { + monitor_printf(mon, "Server: disabled\n"); + goto out; + } + + monitor_printf(mon, "Server:\n"); + if (info->has_port) { + monitor_printf(mon, " address: %s:%" PRId64 "\n", + info->host, info->port); + } + if (info->has_tls_port) { + monitor_printf(mon, " address: %s:%" PRId64 " [tls]\n", + info->host, info->tls_port); + } + monitor_printf(mon, " migrated: %s\n", + info->migrated ? "true" : "false"); + monitor_printf(mon, " auth: %s\n", info->auth); + monitor_printf(mon, " compiled: %s\n", info->compiled_version); + monitor_printf(mon, " mouse-mode: %s\n", + SpiceQueryMouseMode_str(info->mouse_mode)); + + if (!info->has_channels || info->channels == NULL) { + monitor_printf(mon, "Channels: none\n"); + } else { + for (chan = info->channels; chan; chan = chan->next) { + monitor_printf(mon, "Channel:\n"); + monitor_printf(mon, " address: %s:%s%s\n", + chan->value->host, chan->value->port, + chan->value->tls ? " [tls]" : ""); + monitor_printf(mon, " session: %" PRId64 "\n", + chan->value->connection_id); + monitor_printf(mon, " channel: %" PRId64 ":%" PRId64 "\n", + chan->value->channel_type, chan->value->channel_id); + + channel_name = "unknown"; + if (chan->value->channel_type > 0 && + chan->value->channel_type < ARRAY_SIZE(channel_names) && + channel_names[chan->value->channel_type]) { + channel_name = channel_names[chan->value->channel_type]; + } + + monitor_printf(mon, " channel name: %s\n", channel_name); + } + } + +out: + qapi_free_SpiceInfo(info); +} +#endif + +void hmp_set_password(Monitor *mon, const QDict *qdict) +{ + const char *protocol = qdict_get_str(qdict, "protocol"); + const char *password = qdict_get_str(qdict, "password"); + const char *display = qdict_get_try_str(qdict, "display"); + const char *connected = qdict_get_try_str(qdict, "connected"); + Error *err = NULL; + + SetPasswordOptions opts = { + .password = (char *)password, + .has_connected = !!connected, + }; + + opts.connected = qapi_enum_parse(&SetPasswordAction_lookup, connected, + SET_PASSWORD_ACTION_KEEP, &err); + if (err) { + goto out; + } + + opts.protocol = qapi_enum_parse(&DisplayProtocol_lookup, protocol, + DISPLAY_PROTOCOL_VNC, &err); + if (err) { + goto out; + } + + if (opts.protocol == DISPLAY_PROTOCOL_VNC) { + opts.u.vnc.display = (char *)display; + } + + qmp_set_password(&opts, &err); + +out: + hmp_handle_error(mon, err); +} + +void hmp_expire_password(Monitor *mon, const QDict *qdict) +{ + const char *protocol = qdict_get_str(qdict, "protocol"); + const char *whenstr = qdict_get_str(qdict, "time"); + const char *display = qdict_get_try_str(qdict, "display"); + Error *err = NULL; + + ExpirePasswordOptions opts = { + .time = (char *)whenstr, + }; + + opts.protocol = qapi_enum_parse(&DisplayProtocol_lookup, protocol, + DISPLAY_PROTOCOL_VNC, &err); + if (err) { + goto out; + } + + if (opts.protocol == DISPLAY_PROTOCOL_VNC) { + opts.u.vnc.display = (char *)display; + } + + qmp_expire_password(&opts, &err); + +out: + hmp_handle_error(mon, err); +} + +#ifdef CONFIG_VNC +static void hmp_change_read_arg(void *opaque, const char *password, + void *readline_opaque) +{ + qmp_change_vnc_password(password, NULL); + monitor_read_command(opaque, 1); +} + +void hmp_change_vnc(Monitor *mon, const char *device, const char *target, + const char *arg, const char *read_only, bool force, + Error **errp) +{ + if (read_only) { + error_setg(errp, "Parameter 'read-only-mode' is invalid for VNC"); + return; + } + if (strcmp(target, "passwd") && strcmp(target, "password")) { + error_setg(errp, "Expected 'password' after 'vnc'"); + return; + } + if (!arg) { + MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common); + monitor_read_password(hmp_mon, hmp_change_read_arg, NULL); + } else { + qmp_change_vnc_password(arg, errp); + } +} +#endif + +void hmp_sendkey(Monitor *mon, const QDict *qdict) +{ + const char *keys = qdict_get_str(qdict, "keys"); + KeyValue *v = NULL; + KeyValueList *head = NULL, **tail = &head; + int has_hold_time = qdict_haskey(qdict, "hold-time"); + int hold_time = qdict_get_try_int(qdict, "hold-time", -1); + Error *err = NULL; + const char *separator; + int keyname_len; + + while (1) { + separator = qemu_strchrnul(keys, '-'); + keyname_len = separator - keys; + + /* Be compatible with old interface, convert user inputted "<" */ + if (keys[0] == '<' && keyname_len == 1) { + keys = "less"; + keyname_len = 4; + } + + v = g_malloc0(sizeof(*v)); + + if (strstart(keys, "0x", NULL)) { + const char *endp; + int value; + + if (qemu_strtoi(keys, &endp, 0, &value) < 0) { + goto err_out; + } + assert(endp <= keys + keyname_len); + if (endp != keys + keyname_len) { + goto err_out; + } + v->type = KEY_VALUE_KIND_NUMBER; + v->u.number.data = value; + } else { + int idx = index_from_key(keys, keyname_len); + if (idx == Q_KEY_CODE__MAX) { + goto err_out; + } + v->type = KEY_VALUE_KIND_QCODE; + v->u.qcode.data = idx; + } + QAPI_LIST_APPEND(tail, v); + v = NULL; + + if (!*separator) { + break; + } + keys = separator + 1; + } + + qmp_send_key(head, has_hold_time, hold_time, &err); + hmp_handle_error(mon, err); + +out: + qapi_free_KeyValue(v); + qapi_free_KeyValueList(head); + return; + +err_out: + monitor_printf(mon, "invalid parameter: %.*s\n", keyname_len, keys); + goto out; +} + +void sendkey_completion(ReadLineState *rs, int nb_args, const char *str) +{ + int i; + char *sep; + size_t len; + + if (nb_args != 2) { + return; + } + sep = strrchr(str, '-'); + if (sep) { + str = sep + 1; + } + len = strlen(str); + readline_set_completion_index(rs, len); + for (i = 0; i < Q_KEY_CODE__MAX; i++) { + if (!strncmp(str, QKeyCode_str(i), len)) { + readline_add_completion(rs, QKeyCode_str(i)); + } + } +} + +#ifdef CONFIG_PIXMAN +void coroutine_fn +hmp_screendump(Monitor *mon, const QDict *qdict) +{ + const char *filename = qdict_get_str(qdict, "filename"); + const char *id = qdict_get_try_str(qdict, "device"); + int64_t head = qdict_get_try_int(qdict, "head", 0); + const char *input_format = qdict_get_try_str(qdict, "format"); + Error *err = NULL; + ImageFormat format; + + format = qapi_enum_parse(&ImageFormat_lookup, input_format, + IMAGE_FORMAT_PPM, &err); + if (err) { + goto end; + } + + qmp_screendump(filename, id, id != NULL, head, + input_format != NULL, format, &err); +end: + hmp_handle_error(mon, err); +} +#endif + +void hmp_client_migrate_info(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *protocol = qdict_get_str(qdict, "protocol"); + const char *hostname = qdict_get_str(qdict, "hostname"); + bool has_port = qdict_haskey(qdict, "port"); + int port = qdict_get_try_int(qdict, "port", -1); + bool has_tls_port = qdict_haskey(qdict, "tls-port"); + int tls_port = qdict_get_try_int(qdict, "tls-port", -1); + const char *cert_subject = qdict_get_try_str(qdict, "cert-subject"); + + qmp_client_migrate_info(protocol, hostname, + has_port, port, has_tls_port, tls_port, + cert_subject, &err); + hmp_handle_error(mon, err); +} diff --git a/ui/ui-qmp-cmds.c b/ui/ui-qmp-cmds.c new file mode 100644 index 0000000000..74fa6c6ec5 --- /dev/null +++ b/ui/ui-qmp-cmds.c @@ -0,0 +1,396 @@ +/* + * QMP commands related to UI + * + * 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. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" + +#include "io/channel-file.h" +#include "monitor/qmp-helpers.h" +#include "qapi/qapi-commands-ui.h" +#include "qapi/qmp/qerror.h" +#include "qemu/coroutine.h" +#include "qemu/cutils.h" +#include "trace.h" +#include "ui/console.h" +#include "ui/dbus-display.h" +#include "ui/qemu-spice.h" +#ifdef CONFIG_PNG +#include +#endif + +void qmp_set_password(SetPasswordOptions *opts, Error **errp) +{ + int rc; + + if (opts->protocol == DISPLAY_PROTOCOL_SPICE) { + if (!qemu_using_spice(errp)) { + return; + } + rc = qemu_spice.set_passwd(opts->password, + opts->connected == SET_PASSWORD_ACTION_FAIL, + opts->connected == SET_PASSWORD_ACTION_DISCONNECT); + } else { + assert(opts->protocol == DISPLAY_PROTOCOL_VNC); + if (opts->connected != SET_PASSWORD_ACTION_KEEP) { + /* vnc supports "connected=keep" only */ + error_setg(errp, "parameter 'connected' must be 'keep'" + " when 'protocol' is 'vnc'"); + return; + } + /* + * Note that setting an empty password will not disable login + * through this interface. + */ + rc = vnc_display_password(opts->u.vnc.display, opts->password); + } + + if (rc != 0) { + error_setg(errp, "Could not set password"); + } +} + +void qmp_expire_password(ExpirePasswordOptions *opts, Error **errp) +{ + time_t when; + int rc; + const char *whenstr = opts->time; + const char *numstr = NULL; + uint64_t num; + + if (strcmp(whenstr, "now") == 0) { + when = 0; + } else if (strcmp(whenstr, "never") == 0) { + when = TIME_MAX; + } else if (whenstr[0] == '+') { + when = time(NULL); + numstr = whenstr + 1; + } else { + when = 0; + numstr = whenstr; + } + + if (numstr) { + if (qemu_strtou64(numstr, NULL, 10, &num) < 0) { + error_setg(errp, "Parameter 'time' doesn't take value '%s'", + whenstr); + return; + } + when += num; + } + + if (opts->protocol == DISPLAY_PROTOCOL_SPICE) { + if (!qemu_using_spice(errp)) { + return; + } + rc = qemu_spice.set_pw_expire(when); + } else { + assert(opts->protocol == DISPLAY_PROTOCOL_VNC); + rc = vnc_display_pw_expire(opts->u.vnc.display, when); + } + + if (rc != 0) { + error_setg(errp, "Could not set password expire time"); + } +} + +#ifdef CONFIG_VNC +void qmp_change_vnc_password(const char *password, Error **errp) +{ + if (vnc_display_password(NULL, password) < 0) { + error_setg(errp, "Could not set password"); + } +} +#endif + +bool qmp_add_client_spice(int fd, bool has_skipauth, bool skipauth, + bool has_tls, bool tls, Error **errp) +{ + if (!qemu_using_spice(errp)) { + return false; + } + skipauth = has_skipauth ? skipauth : false; + tls = has_tls ? tls : false; + if (qemu_spice.display_add_client(fd, skipauth, tls) < 0) { + error_setg(errp, "spice failed to add client"); + return false; + } + return true; +} + +#ifdef CONFIG_VNC +bool qmp_add_client_vnc(int fd, bool has_skipauth, bool skipauth, + bool has_tls, bool tls, Error **errp) +{ + skipauth = has_skipauth ? skipauth : false; + vnc_display_add_client(NULL, fd, skipauth); + return true; +} +#endif + +#ifdef CONFIG_DBUS_DISPLAY +bool qmp_add_client_dbus_display(int fd, bool has_skipauth, bool skipauth, + bool has_tls, bool tls, Error **errp) +{ + if (!qemu_using_dbus_display(errp)) { + return false; + } + if (!qemu_dbus_display.add_client(fd, errp)) { + return false; + } + return true; +} +#endif + +void qmp_display_reload(DisplayReloadOptions *arg, Error **errp) +{ + switch (arg->type) { + case DISPLAY_RELOAD_TYPE_VNC: +#ifdef CONFIG_VNC + if (arg->u.vnc.has_tls_certs && arg->u.vnc.tls_certs) { + vnc_display_reload_certs(NULL, errp); + } +#else + error_setg(errp, "vnc is invalid, missing 'CONFIG_VNC'"); +#endif + break; + default: + abort(); + } +} + +void qmp_display_update(DisplayUpdateOptions *arg, Error **errp) +{ + switch (arg->type) { + case DISPLAY_UPDATE_TYPE_VNC: +#ifdef CONFIG_VNC + vnc_display_update(&arg->u.vnc, errp); +#else + error_setg(errp, "vnc is invalid, missing 'CONFIG_VNC'"); +#endif + break; + default: + abort(); + } +} + +void qmp_client_migrate_info(const char *protocol, const char *hostname, + bool has_port, int64_t port, + bool has_tls_port, int64_t tls_port, + const char *cert_subject, + Error **errp) +{ + if (strcmp(protocol, "spice") == 0) { + if (!qemu_using_spice(errp)) { + return; + } + + if (!has_port && !has_tls_port) { + error_setg(errp, "parameter 'port' or 'tls-port' is required"); + return; + } + + if (qemu_spice.migrate_info(hostname, + has_port ? port : -1, + has_tls_port ? tls_port : -1, + cert_subject)) { + error_setg(errp, "Could not set up display for migration"); + return; + } + return; + } + + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "'spice'"); +} + +#ifdef CONFIG_PIXMAN +#ifdef CONFIG_PNG +/** + * png_save: Take a screenshot as PNG + * + * Saves screendump as a PNG file + * + * Returns true for success or false for error. + * + * @fd: File descriptor for PNG file. + * @image: Image data in pixman format. + * @errp: Pointer to an error. + */ +static bool png_save(int fd, pixman_image_t *image, Error **errp) +{ + int width = pixman_image_get_width(image); + int height = pixman_image_get_height(image); + png_struct *png_ptr; + png_info *info_ptr; + g_autoptr(pixman_image_t) linebuf = + qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width); + uint8_t *buf = (uint8_t *)pixman_image_get_data(linebuf); + FILE *f = fdopen(fd, "wb"); + int y; + if (!f) { + error_setg_errno(errp, errno, + "Failed to create file from file descriptor"); + return false; + } + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL); + if (!png_ptr) { + error_setg(errp, "PNG creation failed. Unable to write struct"); + fclose(f); + return false; + } + + info_ptr = png_create_info_struct(png_ptr); + + if (!info_ptr) { + error_setg(errp, "PNG creation failed. Unable to write info"); + fclose(f); + png_destroy_write_struct(&png_ptr, &info_ptr); + return false; + } + + png_init_io(png_ptr, f); + + png_set_IHDR(png_ptr, info_ptr, width, height, 8, + PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_write_info(png_ptr, info_ptr); + + for (y = 0; y < height; ++y) { + qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); + png_write_row(png_ptr, buf); + } + + png_write_end(png_ptr, NULL); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + if (fclose(f) != 0) { + error_setg_errno(errp, errno, + "PNG creation failed. Unable to close file"); + return false; + } + + return true; +} + +#else /* no png support */ + +static bool png_save(int fd, pixman_image_t *image, Error **errp) +{ + error_setg(errp, "Enable PNG support with libpng for screendump"); + return false; +} + +#endif /* CONFIG_PNG */ + +static bool ppm_save(int fd, pixman_image_t *image, Error **errp) +{ + int width = pixman_image_get_width(image); + int height = pixman_image_get_height(image); + 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; + + trace_ppm_save(fd, image); + + 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, image, width, 0, y); + if (qio_channel_write_all(QIO_CHANNEL(ioc), + (char *)pixman_image_get_data(linebuf), + pixman_image_get_stride(linebuf), errp) < 0) { + return false; + } + } + + return true; +} + +/* Safety: coroutine-only, concurrent-coroutine safe, main thread only */ +void coroutine_fn +qmp_screendump(const char *filename, const char *device, + bool has_head, int64_t head, + bool has_format, ImageFormat format, Error **errp) +{ + g_autoptr(pixman_image_t) image = NULL; + QemuConsole *con; + DisplaySurface *surface; + int fd; + + if (device) { + con = qemu_console_lookup_by_device_name(device, has_head ? head : 0, + errp); + if (!con) { + return; + } + } else { + if (has_head) { + error_setg(errp, "'head' must be specified together with 'device'"); + return; + } + con = qemu_console_lookup_by_index(0); + if (!con) { + error_setg(errp, "There is no console to take a screendump from"); + return; + } + } + + qemu_console_co_wait_update(con); + + /* + * All pending coroutines are woken up, while the BQL is held. No + * further graphic update are possible until it is released. Take + * an image ref before that. + */ + surface = qemu_console_surface(con); + if (!surface) { + error_setg(errp, "no surface"); + return; + } + image = pixman_image_ref(surface->image); + + fd = qemu_open_old(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; + } + + /* + * The image content could potentially be updated as the coroutine + * yields and releases the BQL. It could produce corrupted dump, but + * it should be otherwise safe. + */ + if (has_format && format == IMAGE_FORMAT_PNG) { + /* PNG format specified for screendump */ + if (!png_save(fd, image, errp)) { + qemu_unlink(filename); + } + } else { + /* PPM format specified/default for screendump */ + if (!ppm_save(fd, image, errp)) { + qemu_unlink(filename); + } + } +} +#endif /* CONFIG_PIXMAN */ diff --git a/ui/util.c b/ui/util.c index 7e8fc1ea53..d54bbb74fb 100644 --- a/ui/util.c +++ b/ui/util.c @@ -17,7 +17,7 @@ #include "qemu/osdep.h" -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pci_bus.h" #include "qapi/error.h" #include "ui/console.h" @@ -51,7 +51,6 @@ bool qemu_console_fill_device_address(QemuConsole *con, size_t size, Error **errp) { - ERRP_GUARD(); DeviceState *dev = DEVICE(object_property_get_link(OBJECT(con), "device", &error_abort)); diff --git a/ui/vdagent.c b/ui/vdagent.c index aa6167f0b4..64d7ab245a 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -2,6 +2,7 @@ #include "qapi/error.h" #include "chardev/char.h" #include "qemu/buffer.h" +#include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/units.h" #include "hw/qdev-core.h" @@ -87,9 +88,7 @@ static const char *cap_name[] = { [VD_AGENT_CAP_MONITORS_CONFIG_POSITION] = "monitors-config-position", [VD_AGENT_CAP_FILE_XFER_DISABLED] = "file-xfer-disabled", [VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS] = "file-xfer-detailed-errors", -#if CHECK_SPICE_PROTOCOL_VERSION(0, 14, 0) [VD_AGENT_CAP_GRAPHICS_DEVICE_INFO] = "graphics-device-info", -#endif #if CHECK_SPICE_PROTOCOL_VERSION(0, 14, 1) [VD_AGENT_CAP_CLIPBOARD_NO_RELEASE_ON_REGRAB] = "clipboard-no-release-on-regrab", [VD_AGENT_CAP_CLIPBOARD_GRAB_SERIAL] = "clipboard-grab-serial", @@ -112,9 +111,7 @@ static const char *msg_name[] = { [VD_AGENT_CLIENT_DISCONNECTED] = "client-disconnected", [VD_AGENT_MAX_CLIPBOARD] = "max-clipboard", [VD_AGENT_AUDIO_VOLUME_SYNC] = "audio-volume-sync", -#if CHECK_SPICE_PROTOCOL_VERSION(0, 14, 0) [VD_AGENT_GRAPHICS_DEVICE_INFO] = "graphics-device-info", -#endif }; static const char *sel_name[] = { @@ -300,7 +297,7 @@ static void vdagent_pointer_sync(DeviceState *dev) } } -static QemuInputHandler vdagent_mouse_handler = { +static const QemuInputHandler vdagent_mouse_handler = { .name = "vdagent mouse", .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, .event = vdagent_pointer_event, @@ -471,7 +468,7 @@ static void vdagent_clipboard_reset_serial(VDAgentChardev *vd) /* reopen the agent connection to reset the serial state */ qemu_chr_be_event(chr, CHR_EVENT_CLOSED); - qemu_chr_be_event(chr, CHR_EVENT_OPENED); + /* OPENED again after the guest disconnected, see set_fe_open */ } static void vdagent_clipboard_notify(Notifier *notifier, void *data) @@ -533,6 +530,8 @@ static void vdagent_clipboard_recv_grab(VDAgentChardev *vd, uint8_t s, uint32_t info->has_serial = true; info->serial = *(uint32_t *)data; if (info->serial < vd->last_serial[s]) { + trace_vdagent_cb_grab_discard(GET_NAME(sel_name, s), + vd->last_serial[s], info->serial); /* discard lower-ordering guest grab */ return; } @@ -544,7 +543,7 @@ static void vdagent_clipboard_recv_grab(VDAgentChardev *vd, uint8_t s, uint32_t if (size > sizeof(uint32_t) * 10) { /* * spice has 6 types as of 2021. Limiting to 10 entries - * so we we have some wiggle room. + * so we have some wiggle room. */ return; } @@ -672,7 +671,7 @@ static void vdagent_chr_open(Chardev *chr, return; #endif - if (migrate_add_blocker(vd->migration_blocker, errp) != 0) { + if (migrate_add_blocker(&vd->migration_blocker, errp) != 0) { return; } @@ -717,8 +716,10 @@ static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg) if (have_mouse(vd) && vd->mouse_hs) { qemu_input_handler_activate(vd->mouse_hs); } + + memset(vd->last_serial, 0, sizeof(vd->last_serial)); + if (have_clipboard(vd) && vd->cbpeer.notifier.notify == NULL) { - memset(vd->last_serial, 0, sizeof(vd->last_serial)); vd->cbpeer.name = "vdagent"; vd->cbpeer.notifier.notify = vdagent_clipboard_notify; vd->cbpeer.request = vdagent_clipboard_request; @@ -853,6 +854,8 @@ static void vdagent_chr_accept_input(Chardev *chr) static void vdagent_disconnect(VDAgentChardev *vd) { + trace_vdagent_disconnect(); + buffer_reset(&vd->outbuf); vdagent_reset_bufs(vd); vd->caps = 0; @@ -867,8 +870,14 @@ static void vdagent_disconnect(VDAgentChardev *vd) static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open) { + VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr); + if (!fe_open) { trace_vdagent_close(); + vdagent_disconnect(vd); + /* To reset_serial, we CLOSED our side. Make sure the other end knows we + * are ready again. */ + qemu_chr_be_event(chr, CHR_EVENT_OPENED); return; } @@ -915,10 +924,12 @@ static void vdagent_chr_fini(Object *obj) { VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj); - migrate_del_blocker(vd->migration_blocker); + migrate_del_blocker(&vd->migration_blocker); vdagent_disconnect(vd); + if (vd->mouse_hs) { + qemu_input_handler_unregister(vd->mouse_hs); + } buffer_free(&vd->outbuf); - error_free(vd->migration_blocker); } static const TypeInfo vdagent_chr_type_info = { diff --git a/ui/vgafont.h b/ui/vgafont.h index 3606dd7338..7e1fc473f7 100644 --- a/ui/vgafont.h +++ b/ui/vgafont.h @@ -1,4611 +1,4611 @@ static const uint8_t vgafont16[256 * 16] = { - /* 0 0x00 '^@' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 1 0x01 '^A' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x81, /* 10000001 */ - 0xa5, /* 10100101 */ - 0x81, /* 10000001 */ - 0x81, /* 10000001 */ - 0xbd, /* 10111101 */ - 0x99, /* 10011001 */ - 0x81, /* 10000001 */ - 0x81, /* 10000001 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 2 0x02 '^B' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xff, /* 11111111 */ - 0xdb, /* 11011011 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xc3, /* 11000011 */ - 0xe7, /* 11100111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 3 0x03 '^C' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 4 0x04 '^D' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 5 0x05 '^E' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0xe7, /* 11100111 */ - 0xe7, /* 11100111 */ - 0xe7, /* 11100111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 6 0x06 '^F' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 7 0x07 '^G' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 8 0x08 '^H' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xe7, /* 11100111 */ - 0xc3, /* 11000011 */ - 0xc3, /* 11000011 */ - 0xe7, /* 11100111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 9 0x09 '^I' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x42, /* 01000010 */ - 0x42, /* 01000010 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 10 0x0a '^J' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xc3, /* 11000011 */ - 0x99, /* 10011001 */ - 0xbd, /* 10111101 */ - 0xbd, /* 10111101 */ - 0x99, /* 10011001 */ - 0xc3, /* 11000011 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 11 0x0b '^K' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1e, /* 00011110 */ - 0x0e, /* 00001110 */ - 0x1a, /* 00011010 */ - 0x32, /* 00110010 */ - 0x78, /* 01111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 12 0x0c '^L' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 13 0x0d '^M' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x33, /* 00110011 */ - 0x3f, /* 00111111 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x70, /* 01110000 */ - 0xf0, /* 11110000 */ - 0xe0, /* 11100000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 14 0x0e '^N' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7f, /* 01111111 */ - 0x63, /* 01100011 */ - 0x7f, /* 01111111 */ - 0x63, /* 01100011 */ - 0x63, /* 01100011 */ - 0x63, /* 01100011 */ - 0x63, /* 01100011 */ - 0x67, /* 01100111 */ - 0xe7, /* 11100111 */ - 0xe6, /* 11100110 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 15 0x0f '^O' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xdb, /* 11011011 */ - 0x3c, /* 00111100 */ - 0xe7, /* 11100111 */ - 0x3c, /* 00111100 */ - 0xdb, /* 11011011 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 16 0x10 '^P' */ - 0x00, /* 00000000 */ - 0x80, /* 10000000 */ - 0xc0, /* 11000000 */ - 0xe0, /* 11100000 */ - 0xf0, /* 11110000 */ - 0xf8, /* 11111000 */ - 0xfe, /* 11111110 */ - 0xf8, /* 11111000 */ - 0xf0, /* 11110000 */ - 0xe0, /* 11100000 */ - 0xc0, /* 11000000 */ - 0x80, /* 10000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 17 0x11 '^Q' */ - 0x00, /* 00000000 */ - 0x02, /* 00000010 */ - 0x06, /* 00000110 */ - 0x0e, /* 00001110 */ - 0x1e, /* 00011110 */ - 0x3e, /* 00111110 */ - 0xfe, /* 11111110 */ - 0x3e, /* 00111110 */ - 0x1e, /* 00011110 */ - 0x0e, /* 00001110 */ - 0x06, /* 00000110 */ - 0x02, /* 00000010 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 18 0x12 '^R' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 19 0x13 '^S' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 20 0x14 '^T' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7f, /* 01111111 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7b, /* 01111011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 21 0x15 '^U' */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x0c, /* 00001100 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 22 0x16 '^V' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 23 0x17 '^W' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 24 0x18 '^X' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 25 0x19 '^Y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 26 0x1a '^Z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 27 0x1b '^[' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xfe, /* 11111110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 28 0x1c '^\' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 29 0x1d '^]' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x28, /* 00101000 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x28, /* 00101000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 30 0x1e '^^' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0x7c, /* 01111100 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 31 0x1f '^_' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 32 0x20 ' ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 33 0x21 '!' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 34 0x22 '"' */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x24, /* 00100100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 35 0x23 '#' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 36 0x24 '$' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc2, /* 11000010 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x86, /* 10000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 37 0x25 '%' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc2, /* 11000010 */ - 0xc6, /* 11000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc6, /* 11000110 */ - 0x86, /* 10000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 38 0x26 '&' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 39 0x27 ''' */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 40 0x28 '(' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 41 0x29 ')' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 42 0x2a '*' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0xff, /* 11111111 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 43 0x2b '+' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 44 0x2c ',' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 45 0x2d '-' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 46 0x2e '.' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 47 0x2f '/' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x02, /* 00000010 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0x80, /* 10000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 48 0x30 '0' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 49 0x31 '1' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x38, /* 00111000 */ - 0x78, /* 01111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 50 0x32 '2' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 51 0x33 '3' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x3c, /* 00111100 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 52 0x34 '4' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x1c, /* 00011100 */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x1e, /* 00011110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 53 0x35 '5' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 54 0x36 '6' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 55 0x37 '7' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 56 0x38 '8' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 57 0x39 '9' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 58 0x3a ':' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 59 0x3b ';' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 60 0x3c '<' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 61 0x3d '=' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 62 0x3e '>' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 63 0x3f '?' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 64 0x40 '@' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xde, /* 11011110 */ - 0xde, /* 11011110 */ - 0xde, /* 11011110 */ - 0xdc, /* 11011100 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 65 0x41 'A' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 66 0x42 'B' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 67 0x43 'C' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc2, /* 11000010 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc2, /* 11000010 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 68 0x44 'D' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 69 0x45 'E' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x66, /* 01100110 */ - 0x62, /* 01100010 */ - 0x68, /* 01101000 */ - 0x78, /* 01111000 */ - 0x68, /* 01101000 */ - 0x60, /* 01100000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 70 0x46 'F' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x66, /* 01100110 */ - 0x62, /* 01100010 */ - 0x68, /* 01101000 */ - 0x78, /* 01111000 */ - 0x68, /* 01101000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 71 0x47 'G' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc2, /* 11000010 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xde, /* 11011110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x66, /* 01100110 */ - 0x3a, /* 00111010 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 72 0x48 'H' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 73 0x49 'I' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 74 0x4a 'J' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1e, /* 00011110 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 75 0x4b 'K' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xe6, /* 11100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x78, /* 01111000 */ - 0x78, /* 01111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 76 0x4c 'L' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf0, /* 11110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 77 0x4d 'M' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xee, /* 11101110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 78 0x4e 'N' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xe6, /* 11100110 */ - 0xf6, /* 11110110 */ - 0xfe, /* 11111110 */ - 0xde, /* 11011110 */ - 0xce, /* 11001110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 79 0x4f 'O' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 80 0x50 'P' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 81 0x51 'Q' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xde, /* 11011110 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0x0e, /* 00001110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 82 0x52 'R' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 83 0x53 'S' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x38, /* 00111000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 84 0x54 'T' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x5a, /* 01011010 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 85 0x55 'U' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 86 0x56 'V' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 87 0x57 'W' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xfe, /* 11111110 */ - 0xee, /* 11101110 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 88 0x58 'X' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 89 0x59 'Y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 90 0x5a 'Z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x86, /* 10000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc2, /* 11000010 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 91 0x5b '[' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 92 0x5c '\' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x80, /* 10000000 */ - 0xc0, /* 11000000 */ - 0xe0, /* 11100000 */ - 0x70, /* 01110000 */ - 0x38, /* 00111000 */ - 0x1c, /* 00011100 */ - 0x0e, /* 00001110 */ - 0x06, /* 00000110 */ - 0x02, /* 00000010 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 93 0x5d ']' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 94 0x5e '^' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 95 0x5f '_' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 96 0x60 '`' */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 97 0x61 'a' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 98 0x62 'b' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x78, /* 01111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 99 0x63 'c' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 100 0x64 'd' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1c, /* 00011100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 101 0x65 'e' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 102 0x66 'f' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1c, /* 00011100 */ - 0x36, /* 00110110 */ - 0x32, /* 00110010 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 103 0x67 'g' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - - /* 104 0x68 'h' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x6c, /* 01101100 */ - 0x76, /* 01110110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 105 0x69 'i' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 106 0x6a 'j' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x0e, /* 00001110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 107 0x6b 'k' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x78, /* 01111000 */ - 0x78, /* 01111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 108 0x6c 'l' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 109 0x6d 'm' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xec, /* 11101100 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 110 0x6e 'n' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 111 0x6f 'o' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 112 0x70 'p' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - - /* 113 0x71 'q' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x1e, /* 00011110 */ - 0x00, /* 00000000 */ - - /* 114 0x72 'r' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x76, /* 01110110 */ - 0x66, /* 01100110 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 115 0x73 's' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x38, /* 00111000 */ - 0x0c, /* 00001100 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 116 0x74 't' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0xfc, /* 11111100 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x36, /* 00110110 */ - 0x1c, /* 00011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 117 0x75 'u' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 118 0x76 'v' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 119 0x77 'w' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 120 0x78 'x' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 121 0x79 'y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - - /* 122 0x7a 'z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xcc, /* 11001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 123 0x7b '{' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0e, /* 00001110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 124 0x7c '|' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 125 0x7d '}' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x70, /* 01110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 126 0x7e '~' */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 127 0x7f '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 128 0x80 '€' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc2, /* 11000010 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc2, /* 11000010 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 129 0x81 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 130 0x82 '‚' */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 131 0x83 'ƒ' */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 132 0x84 '„' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 133 0x85 '…' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 134 0x86 '†' */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 135 0x87 '‡' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 136 0x88 'ˆ' */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 137 0x89 '‰' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 138 0x8a 'Š' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 139 0x8b '‹' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 140 0x8c 'Œ' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 141 0x8d '' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 142 0x8e 'Ž' */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 143 0x8f '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 144 0x90 '' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x66, /* 01100110 */ - 0x62, /* 01100010 */ - 0x68, /* 01101000 */ - 0x78, /* 01111000 */ - 0x68, /* 01101000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 145 0x91 '‘' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xec, /* 11101100 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x7e, /* 01111110 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x6e, /* 01101110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 146 0x92 '’' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3e, /* 00111110 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xfe, /* 11111110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xce, /* 11001110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 147 0x93 '“' */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 148 0x94 '”' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 149 0x95 '•' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 150 0x96 '–' */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 151 0x97 '—' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 152 0x98 '˜' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - - /* 153 0x99 '™' */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 154 0x9a 'š' */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 155 0x9b '›' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 156 0x9c 'œ' */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x64, /* 01100100 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xe6, /* 11100110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 157 0x9d '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 158 0x9e 'ž' */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xf8, /* 11111000 */ - 0xc4, /* 11000100 */ - 0xcc, /* 11001100 */ - 0xde, /* 11011110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 159 0x9f 'Ÿ' */ - 0x00, /* 00000000 */ - 0x0e, /* 00001110 */ - 0x1b, /* 00011011 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 160 0xa0 ' ' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 161 0xa1 '¡' */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 162 0xa2 '¢' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 163 0xa3 '£' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 164 0xa4 '¤' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 165 0xa5 '¥' */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xe6, /* 11100110 */ - 0xf6, /* 11110110 */ - 0xfe, /* 11111110 */ - 0xde, /* 11011110 */ - 0xce, /* 11001110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 166 0xa6 '¦' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x3e, /* 00111110 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 167 0xa7 '§' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 168 0xa8 '¨' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 169 0xa9 '©' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 170 0xaa 'ª' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 171 0xab '«' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0xe0, /* 11100000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xdc, /* 11011100 */ - 0x86, /* 10000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x3e, /* 00111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 172 0xac '¬' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0xe0, /* 11100000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x66, /* 01100110 */ - 0xce, /* 11001110 */ - 0x9a, /* 10011010 */ - 0x3f, /* 00111111 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 173 0xad '­' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 174 0xae '®' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x36, /* 00110110 */ - 0x6c, /* 01101100 */ - 0xd8, /* 11011000 */ - 0x6c, /* 01101100 */ - 0x36, /* 00110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 175 0xaf '¯' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xd8, /* 11011000 */ - 0x6c, /* 01101100 */ - 0x36, /* 00110110 */ - 0x6c, /* 01101100 */ - 0xd8, /* 11011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 176 0xb0 '°' */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - - /* 177 0xb1 '±' */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - - /* 178 0xb2 '²' */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - - /* 179 0xb3 '³' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 180 0xb4 '´' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 181 0xb5 'µ' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 182 0xb6 '¶' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 183 0xb7 '·' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 184 0xb8 '¸' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 185 0xb9 '¹' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x06, /* 00000110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 186 0xba 'º' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 187 0xbb '»' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 188 0xbc '¼' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x06, /* 00000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 189 0xbd '½' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 190 0xbe '¾' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 191 0xbf '¿' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 192 0xc0 'À' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 193 0xc1 'Á' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 194 0xc2 'Â' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 195 0xc3 'Ã' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 196 0xc4 'Ä' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 197 0xc5 'Å' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 198 0xc6 'Æ' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 199 0xc7 'Ç' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 200 0xc8 'È' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x30, /* 00110000 */ - 0x3f, /* 00111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 201 0xc9 'É' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x30, /* 00110000 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 202 0xca 'Ê' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf7, /* 11110111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 203 0xcb 'Ë' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xf7, /* 11110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 204 0xcc 'Ì' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x30, /* 00110000 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 205 0xcd 'Í' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 206 0xce 'Î' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf7, /* 11110111 */ - 0x00, /* 00000000 */ - 0xf7, /* 11110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 207 0xcf 'Ï' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 208 0xd0 'Ð' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 209 0xd1 'Ñ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 210 0xd2 'Ò' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 211 0xd3 'Ó' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x3f, /* 00111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 212 0xd4 'Ô' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 213 0xd5 'Õ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 214 0xd6 'Ö' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 215 0xd7 '×' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xff, /* 11111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 216 0xd8 'Ø' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 217 0xd9 'Ù' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 218 0xda 'Ú' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 219 0xdb 'Û' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 220 0xdc 'Ü' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 221 0xdd 'Ý' */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - - /* 222 0xde 'Þ' */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - - /* 223 0xdf 'ß' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 224 0xe0 'à' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xdc, /* 11011100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 225 0xe1 'á' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xd8, /* 11011000 */ - 0xcc, /* 11001100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 226 0xe2 'â' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 227 0xe3 'ã' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 228 0xe4 'ä' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 229 0xe5 'å' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 230 0xe6 'æ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 231 0xe7 'ç' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 232 0xe8 'è' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 233 0xe9 'é' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 234 0xea 'ê' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xee, /* 11101110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 235 0xeb 'ë' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1e, /* 00011110 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x3e, /* 00111110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 236 0xec 'ì' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 237 0xed 'í' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x03, /* 00000011 */ - 0x06, /* 00000110 */ - 0x7e, /* 01111110 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0xf3, /* 11110011 */ - 0x7e, /* 01111110 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 238 0xee 'î' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1c, /* 00011100 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x1c, /* 00011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 239 0xef 'ï' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 240 0xf0 'ð' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 241 0xf1 'ñ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 242 0xf2 'ò' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 243 0xf3 'ó' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 244 0xf4 'ô' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0e, /* 00001110 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 245 0xf5 'õ' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 246 0xf6 'ö' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 247 0xf7 '÷' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 248 0xf8 'ø' */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 249 0xf9 'ù' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 250 0xfa 'ú' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 251 0xfb 'û' */ - 0x00, /* 00000000 */ - 0x0f, /* 00001111 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0xec, /* 11101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x3c, /* 00111100 */ - 0x1c, /* 00011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 252 0xfc 'ü' */ - 0x00, /* 00000000 */ - 0x6c, /* 01101100 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 253 0xfd 'ý' */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x32, /* 00110010 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 254 0xfe 'þ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 255 0xff 'ÿ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 1 0x01 '^A' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x81, /* 10000001 */ + 0xa5, /* 10100101 */ + 0x81, /* 10000001 */ + 0x81, /* 10000001 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0x81, /* 10000001 */ + 0x81, /* 10000001 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 2 0x02 '^B' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xdb, /* 11011011 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 3 0x03 '^C' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 4 0x04 '^D' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 5 0x05 '^E' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 6 0x06 '^F' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 7 0x07 '^G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 8 0x08 '^H' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xe7, /* 11100111 */ + 0xc3, /* 11000011 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 9 0x09 '^I' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x42, /* 01000010 */ + 0x42, /* 01000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 10 0x0a '^J' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0x99, /* 10011001 */ + 0xbd, /* 10111101 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0xc3, /* 11000011 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 11 0x0b '^K' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x0e, /* 00001110 */ + 0x1a, /* 00011010 */ + 0x32, /* 00110010 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 12 0x0c '^L' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 13 0x0d '^M' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x33, /* 00110011 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x70, /* 01110000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 14 0x0e '^N' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x67, /* 01100111 */ + 0xe7, /* 11100111 */ + 0xe6, /* 11100110 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 15 0x0f '^O' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xdb, /* 11011011 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0x3c, /* 00111100 */ + 0xdb, /* 11011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 16 0x10 '^P' */ + 0x00, /* 00000000 */ + 0x80, /* 10000000 */ + 0xc0, /* 11000000 */ + 0xe0, /* 11100000 */ + 0xf0, /* 11110000 */ + 0xf8, /* 11111000 */ + 0xfe, /* 11111110 */ + 0xf8, /* 11111000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + 0xc0, /* 11000000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 17 0x11 '^Q' */ + 0x00, /* 00000000 */ + 0x02, /* 00000010 */ + 0x06, /* 00000110 */ + 0x0e, /* 00001110 */ + 0x1e, /* 00011110 */ + 0x3e, /* 00111110 */ + 0xfe, /* 11111110 */ + 0x3e, /* 00111110 */ + 0x1e, /* 00011110 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 18 0x12 '^R' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 19 0x13 '^S' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 20 0x14 '^T' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7f, /* 01111111 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7b, /* 01111011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 21 0x15 '^U' */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 22 0x16 '^V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 23 0x17 '^W' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 24 0x18 '^X' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 25 0x19 '^Y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 26 0x1a '^Z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 27 0x1b '^[' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xfe, /* 11111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 28 0x1c '^\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 29 0x1d '^]' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x28, /* 00101000 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x28, /* 00101000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 30 0x1e '^^' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 31 0x1f '^_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 32 0x20 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 33 0x21 '!' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 34 0x22 '"' */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 35 0x23 '#' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 36 0x24 '$' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x86, /* 10000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 37 0x25 '%' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc2, /* 11000010 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0x86, /* 10000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 38 0x26 '&' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 39 0x27 ''' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 40 0x28 '(' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 41 0x29 ')' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 42 0x2a '*' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0xff, /* 11111111 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 43 0x2b '+' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 44 0x2c ',' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 45 0x2d '-' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 46 0x2e '.' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 47 0x2f '/' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x02, /* 00000010 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 48 0x30 '0' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 49 0x31 '1' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x38, /* 00111000 */ + 0x78, /* 01111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 50 0x32 '2' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 51 0x33 '3' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x3c, /* 00111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 52 0x34 '4' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x1c, /* 00011100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 53 0x35 '5' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 54 0x36 '6' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 55 0x37 '7' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 56 0x38 '8' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 57 0x39 '9' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 58 0x3a ':' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 59 0x3b ';' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 60 0x3c '<' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 61 0x3d '=' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 62 0x3e '>' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 63 0x3f '?' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 64 0x40 '@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xdc, /* 11011100 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 65 0x41 'A' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 66 0x42 'B' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 67 0x43 'C' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc2, /* 11000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 68 0x44 'D' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 69 0x45 'E' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 70 0x46 'F' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 71 0x47 'G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xde, /* 11011110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x66, /* 01100110 */ + 0x3a, /* 00111010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 72 0x48 'H' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 73 0x49 'I' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 74 0x4a 'J' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 75 0x4b 'K' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe6, /* 11100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 76 0x4c 'L' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 77 0x4d 'M' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xee, /* 11101110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 78 0x4e 'N' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xfe, /* 11111110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 79 0x4f 'O' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 80 0x50 'P' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 81 0x51 'Q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xde, /* 11011110 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 82 0x52 'R' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 83 0x53 'S' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 84 0x54 'T' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x5a, /* 01011010 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 85 0x55 'U' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 86 0x56 'V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 87 0x57 'W' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0xee, /* 11101110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 88 0x58 'X' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 89 0x59 'Y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 90 0x5a 'Z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x86, /* 10000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc2, /* 11000010 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 91 0x5b '[' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 92 0x5c '\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x80, /* 10000000 */ + 0xc0, /* 11000000 */ + 0xe0, /* 11100000 */ + 0x70, /* 01110000 */ + 0x38, /* 00111000 */ + 0x1c, /* 00011100 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 93 0x5d ']' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 94 0x5e '^' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 95 0x5f '_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 96 0x60 '`' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 97 0x61 'a' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 98 0x62 'b' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 99 0x63 'c' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 100 0x64 'd' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 101 0x65 'e' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 102 0x66 'f' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x36, /* 00110110 */ + 0x32, /* 00110010 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 103 0x67 'g' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 104 0x68 'h' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x6c, /* 01101100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 105 0x69 'i' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 106 0x6a 'j' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 107 0x6b 'k' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 108 0x6c 'l' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 109 0x6d 'm' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 110 0x6e 'n' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 111 0x6f 'o' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 112 0x70 'p' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 113 0x71 'q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + + /* 114 0x72 'r' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 115 0x73 's' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 116 0x74 't' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0xfc, /* 11111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x36, /* 00110110 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 117 0x75 'u' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 118 0x76 'v' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 119 0x77 'w' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 120 0x78 'x' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 121 0x79 'y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + + /* 122 0x7a 'z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 123 0x7b '{' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 124 0x7c '|' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 125 0x7d '}' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 126 0x7e '~' */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 127 0x7f '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 128 0x80 '€' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc2, /* 11000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 129 0x81 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 130 0x82 '‚' */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 131 0x83 'ƒ' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 132 0x84 '„' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 133 0x85 '…' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 134 0x86 '†' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 135 0x87 '‡' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 136 0x88 'ˆ' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 137 0x89 '‰' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 138 0x8a 'Š' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 139 0x8b '‹' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 140 0x8c 'Œ' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 141 0x8d '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 142 0x8e 'Ž' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 143 0x8f '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 144 0x90 '' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 145 0x91 '‘' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x6e, /* 01101110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 146 0x92 '’' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3e, /* 00111110 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 147 0x93 '“' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 148 0x94 '”' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 149 0x95 '•' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 150 0x96 '–' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 151 0x97 '—' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 152 0x98 '˜' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 153 0x99 '™' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 154 0x9a 'š' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 155 0x9b '›' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 156 0x9c 'œ' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x64, /* 01100100 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xe6, /* 11100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 157 0x9d '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 158 0x9e 'ž' */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xf8, /* 11111000 */ + 0xc4, /* 11000100 */ + 0xcc, /* 11001100 */ + 0xde, /* 11011110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 159 0x9f 'Ÿ' */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 160 0xa0 ' ' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 161 0xa1 '¡' */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 162 0xa2 '¢' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 163 0xa3 '£' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 164 0xa4 '¤' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 165 0xa5 '¥' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xfe, /* 11111110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 166 0xa6 '¦' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 167 0xa7 '§' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 168 0xa8 '¨' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 169 0xa9 '©' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 170 0xaa 'ª' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 171 0xab '«' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0xe0, /* 11100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xdc, /* 11011100 */ + 0x86, /* 10000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 172 0xac '¬' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0xe0, /* 11100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x66, /* 01100110 */ + 0xce, /* 11001110 */ + 0x9a, /* 10011010 */ + 0x3f, /* 00111111 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 173 0xad '­' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 174 0xae '®' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x36, /* 00110110 */ + 0x6c, /* 01101100 */ + 0xd8, /* 11011000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 175 0xaf '¯' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xd8, /* 11011000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x6c, /* 01101100 */ + 0xd8, /* 11011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 176 0xb0 '°' */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + + /* 177 0xb1 '±' */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + + /* 178 0xb2 '²' */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + + /* 179 0xb3 '³' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 180 0xb4 '´' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 181 0xb5 'µ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 182 0xb6 '¶' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 183 0xb7 '·' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 184 0xb8 '¸' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 185 0xb9 '¹' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 186 0xba 'º' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 187 0xbb '»' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 188 0xbc '¼' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 189 0xbd '½' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 190 0xbe '¾' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 191 0xbf '¿' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 192 0xc0 'À' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 193 0xc1 'Á' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 194 0xc2 'Â' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 195 0xc3 'Ã' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 196 0xc4 'Ä' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 197 0xc5 'Å' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 198 0xc6 'Æ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 199 0xc7 'Ç' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 200 0xc8 'È' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 201 0xc9 'É' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 202 0xca 'Ê' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 203 0xcb 'Ë' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 204 0xcc 'Ì' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 205 0xcd 'Í' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 206 0xce 'Î' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 207 0xcf 'Ï' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 208 0xd0 'Ð' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 209 0xd1 'Ñ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 210 0xd2 'Ò' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 211 0xd3 'Ó' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 212 0xd4 'Ô' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 213 0xd5 'Õ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 214 0xd6 'Ö' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 215 0xd7 '×' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 216 0xd8 'Ø' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 217 0xd9 'Ù' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 218 0xda 'Ú' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 219 0xdb 'Û' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 220 0xdc 'Ü' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 221 0xdd 'Ý' */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + + /* 222 0xde 'Þ' */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + + /* 223 0xdf 'ß' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 224 0xe0 'à' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 225 0xe1 'á' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xd8, /* 11011000 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 226 0xe2 'â' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 227 0xe3 'ã' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 228 0xe4 'ä' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 229 0xe5 'å' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 230 0xe6 'æ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + + /* 231 0xe7 'ç' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 232 0xe8 'è' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 233 0xe9 'é' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 234 0xea 'ê' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xee, /* 11101110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 235 0xeb 'ë' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x3e, /* 00111110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 236 0xec 'ì' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 237 0xed 'í' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x03, /* 00000011 */ + 0x06, /* 00000110 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xf3, /* 11110011 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 238 0xee 'î' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 239 0xef 'ï' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 240 0xf0 'ð' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 241 0xf1 'ñ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 242 0xf2 'ò' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 243 0xf3 'ó' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 244 0xf4 'ô' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 245 0xf5 'õ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 246 0xf6 'ö' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 247 0xf7 '÷' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 248 0xf8 'ø' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 249 0xf9 'ù' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 250 0xfa 'ú' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 251 0xfb 'û' */ + 0x00, /* 00000000 */ + 0x0f, /* 00001111 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xec, /* 11101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3c, /* 00111100 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 252 0xfc 'ü' */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 253 0xfd 'ý' */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 254 0xfe 'þ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 255 0xff 'ÿ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ }; diff --git a/ui/vnc-clipboard.c b/ui/vnc-clipboard.c index 8aeadfaa21..124b6fbd9c 100644 --- a/ui/vnc-clipboard.c +++ b/ui/vnc-clipboard.c @@ -50,8 +50,11 @@ static uint8_t *inflate_buffer(uint8_t *in, uint32_t in_len, uint32_t *size) ret = inflate(&stream, Z_FINISH); switch (ret) { case Z_OK: - case Z_STREAM_END: break; + case Z_STREAM_END: + *size = stream.total_out; + inflateEnd(&stream); + return out; case Z_BUF_ERROR: out_len <<= 1; if (out_len > (1 << 20)) { diff --git a/ui/vnc-enc-hextile-template.h b/ui/vnc-enc-hextile-template.h index 0c56262aff..8ee92086ac 100644 --- a/ui/vnc-enc-hextile-template.h +++ b/ui/vnc-enc-hextile-template.h @@ -7,6 +7,8 @@ #define NAME BPP #endif +#define MAX_BYTES_PER_PIXEL 4 + static void CONCAT(send_hextile_tile_, NAME)(VncState *vs, int x, int y, int w, int h, void *last_bg_, @@ -25,10 +27,13 @@ static void CONCAT(send_hextile_tile_, NAME)(VncState *vs, int bg_count = 0; int fg_count = 0; int flags = 0; - uint8_t data[(vs->client_pf.bytes_per_pixel + 2) * 16 * 16]; + uint8_t data[(MAX_BYTES_PER_PIXEL + 2) * 16 * 16]; int n_data = 0; int n_subtiles = 0; + /* Enforced by set_pixel_format() */ + assert(vs->client_pf.bytes_per_pixel <= MAX_BYTES_PER_PIXEL); + for (j = 0; j < h; j++) { for (i = 0; i < w; i++) { switch (n_colors) { @@ -205,6 +210,7 @@ static void CONCAT(send_hextile_tile_, NAME)(VncState *vs, } } +#undef MAX_BYTES_PER_PIXEL #undef NAME #undef pixel_t #undef CONCAT_I diff --git a/ui/vnc-enc-hextile.c b/ui/vnc-enc-hextile.c index 4215bd7daf..c763256f29 100644 --- a/ui/vnc-enc-hextile.c +++ b/ui/vnc-enc-hextile.c @@ -50,8 +50,8 @@ int vnc_hextile_send_framebuffer_update(VncState *vs, int x, int has_fg, has_bg; uint8_t *last_fg, *last_bg; - last_fg = (uint8_t *) g_malloc(VNC_SERVER_FB_BYTES); - last_bg = (uint8_t *) g_malloc(VNC_SERVER_FB_BYTES); + last_fg = g_malloc(VNC_SERVER_FB_BYTES); + last_bg = g_malloc(VNC_SERVER_FB_BYTES); has_fg = has_bg = 0; for (j = y; j < (y + h); j += 16) { for (i = x; i < (x + w); i += 16) { diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index 09200d71b8..41f559eb83 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -77,7 +77,7 @@ static int tight_send_framebuffer_update(VncState *vs, int x, int y, #ifdef CONFIG_VNC_JPEG static const struct { - double jpeg_freq_min; /* Don't send JPEG if the freq is bellow */ + double jpeg_freq_min; /* Don't send JPEG if the freq is below */ double jpeg_freq_threshold; /* Always send JPEG if the freq is above */ int jpeg_idx; /* Allow indexed JPEG */ int jpeg_full; /* Allow full color JPEG */ @@ -1097,13 +1097,13 @@ static int send_palette_rect(VncState *vs, int x, int y, switch (vs->client_pf.bytes_per_pixel) { case 4: { - size_t old_offset, offset; - uint32_t header[palette_size(palette)]; + size_t old_offset, offset, palette_sz = palette_size(palette); + g_autofree uint32_t *header = g_new(uint32_t, palette_sz); struct palette_cb_priv priv = { vs, (uint8_t *)header }; old_offset = vs->output.offset; palette_iter(palette, write_palette, &priv); - vnc_write(vs, header, sizeof(header)); + vnc_write(vs, header, palette_sz * sizeof(uint32_t)); if (vs->tight->pixel24) { tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset); @@ -1115,11 +1115,12 @@ static int send_palette_rect(VncState *vs, int x, int y, } case 2: { - uint16_t header[palette_size(palette)]; + size_t palette_sz = palette_size(palette); + g_autofree uint16_t *header = g_new(uint16_t, palette_sz); struct palette_cb_priv priv = { vs, (uint8_t *)header }; palette_iter(palette, write_palette, &priv); - vnc_write(vs, header, sizeof(header)); + vnc_write(vs, header, palette_sz * sizeof(uint16_t)); tight_encode_indexed_rect16(vs->tight->tight.buffer, w * h, palette); break; } diff --git a/ui/vnc-enc-zrle.c.inc b/ui/vnc-enc-zrle.c.inc index c107d8affc..2ef7501d52 100644 --- a/ui/vnc-enc-zrle.c.inc +++ b/ui/vnc-enc-zrle.c.inc @@ -110,7 +110,7 @@ static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, ZRLE_PIXEL *end = ptr + h * w; *end = ~*(end-1); /* one past the end is different so the while loop ends */ - /* Real limit is 127 but we wan't a way to know if there is more than 127 */ + /* Real limit is 127 but we want a way to know if there is more than 127 */ palette_init(palette, 256, ZRLE_BPP); while (ptr < end) { @@ -153,11 +153,12 @@ static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, } if (use_rle) { - ZRLE_PIXEL *ptr = data; - ZRLE_PIXEL *end = ptr + w * h; ZRLE_PIXEL *run_start; ZRLE_PIXEL pix; + ptr = data; + end = ptr + w * h; + while (ptr < end) { int len; int index = 0; @@ -198,7 +199,7 @@ static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, } } else if (use_palette) { /* no RLE */ int bppp; - ZRLE_PIXEL *ptr = data; + ptr = data; /* packed pixels */ @@ -241,8 +242,6 @@ static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, #endif { #ifdef ZRLE_COMPACT_PIXEL - ZRLE_PIXEL *ptr; - for (ptr = data; ptr < data + w * h; ptr++) { ZRLE_WRITE_PIXEL(vs, *ptr); } diff --git a/ui/vnc-enc-zywrle-template.c b/ui/vnc-enc-zywrle-template.c index e9be55966e..4afa583e76 100644 --- a/ui/vnc-enc-zywrle-template.c +++ b/ui/vnc-enc-zywrle-template.c @@ -86,17 +86,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #undef L_2 #if ZYWRLE_ENDIAN == ENDIAN_BIG -# define S_0 1 -# define S_1 0 -# define L_0 3 -# define L_1 2 -# define L_2 1 +# define S_0 1 +# define S_1 0 +# define L_0 3 +# define L_1 2 +# define L_2 1 #else -# define S_0 0 -# define S_1 1 -# define L_0 0 -# define L_1 1 -# define L_2 2 +# define S_0 0 +# define S_1 1 +# define L_0 0 +# define L_1 1 +# define L_2 2 #endif #define ZYWRLE_QUANTIZE diff --git a/ui/vnc-enc-zywrle.h b/ui/vnc-enc-zywrle.h index 9b7f698975..64fbc90ee7 100644 --- a/ui/vnc-enc-zywrle.h +++ b/ui/vnc-enc-zywrle.h @@ -51,14 +51,14 @@ static const unsigned int zywrle_param[3][3]={ {0x0000F000, 0x00000000, 0x00000000}, {0x0000C000, 0x00F0F0F0, 0x00000000}, {0x0000C000, 0x00C0C0C0, 0x00F0F0F0}, -/* {0x0000FF00, 0x00000000, 0x00000000}, +/* {0x0000FF00, 0x00000000, 0x00000000}, {0x0000FF00, 0x00FFFFFF, 0x00000000}, {0x0000FF00, 0x00FFFFFF, 0x00FFFFFF}, */ }; #else /* Type B:Non liner quantization filter. */ static const int8_t zywrle_conv[4][256]={ -{ /* bi=5, bo=5 r=0.0:PSNR=24.849 */ +{ /* bi=5, bo=5 r=0.0:PSNR=24.849 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -92,7 +92,7 @@ static const int8_t zywrle_conv[4][256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, -{ /* bi=5, bo=5 r=2.0:PSNR=74.031 */ +{ /* bi=5, bo=5 r=2.0:PSNR=74.031 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, @@ -126,7 +126,7 @@ static const int8_t zywrle_conv[4][256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, -{ /* bi=5, bo=4 r=2.0:PSNR=64.441 */ +{ /* bi=5, bo=4 r=2.0:PSNR=64.441 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -160,7 +160,7 @@ static const int8_t zywrle_conv[4][256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, -{ /* bi=5, bo=2 r=2.0:PSNR=43.175 */ +{ /* bi=5, bo=2 r=2.0:PSNR=43.175 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -274,14 +274,14 @@ static inline void harr(int8_t *px0, int8_t *px1) x1 += x0; if (((x1 ^ orgx1) & 0x80) == 0) { /* |x1| > |x0| */ - x0 -= x1; /* H = -B */ + x0 -= x1; /* H = -B */ } } else { /* same sign */ x0 -= x1; if (((x0 ^ orgx0) & 0x80) == 0) { /* |x0| > |x1| */ - x1 += x0; /* L = A */ + x1 += x0; /* L = A */ } } *px0 = (int8_t)x1; @@ -485,7 +485,7 @@ static inline void wavelet(int *buf, int width, int height, int level) /* RGB <=> YUV conversion stuffs. - YUV coversion is explained as following formula in strict meaning: + YUV conversion is explained as following formula in strict meaning: Y = 0.299R + 0.587G + 0.114B ( 0<=Y<=255) U = -0.169R - 0.331G + 0.500B (-128<=U<=127) V = 0.500R - 0.419G - 0.081B (-128<=V<=127) @@ -539,7 +539,7 @@ static inline void wavelet(int *buf, int width, int height, int level) +------+------+ So, we must transfer each sub images individually in strict meaning. - But at least ZRLE meaning, following one decompositon image is same as + But at least ZRLE meaning, following one decomposition image is same as avobe individual sub image. I use this format. (Strictly saying, transfer order is reverse(Hxy->Hy->Hx->L) for simplified procedure for any wavelet level.) @@ -585,7 +585,7 @@ static inline void wavelet(int *buf, int width, int height, int level) } \ } while (0) -#define ZYWRLE_PACK_COEFF(buf, data, t, width, height, scanline, level) \ +#define ZYWRLE_PACK_COEFF(buf, data, t, width, height, scanline, level) \ ZYWRLE_TRANSFER_COEFF(buf, data, t, width, height, scanline, level, \ ZYWRLE_LOAD_COEFF(ph, r, g, b); \ ZYWRLE_SAVE_PIXEL(data, r, g, b);) diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index 4562bf8928..fcca7ec632 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -250,12 +250,13 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) /* Here job can only be NULL if queue->exit is true */ job = QTAILQ_FIRST(&queue->jobs); vnc_unlock_queue(queue); - assert(job->vs->magic == VNC_MAGIC); if (queue->exit) { return -1; } + assert(job->vs->magic == VNC_MAGIC); + vnc_lock_output(job->vs); if (job->vs->ioc == NULL || job->vs->abort == true) { vnc_unlock_output(job->vs); @@ -373,7 +374,7 @@ void vnc_start_worker_thread(void) VncJobQueue *q; if (vnc_worker_thread_running()) - return ; + return; q = vnc_queue_init(); qemu_thread_create(&q->thread, "vnc_worker", vnc_worker_thread, q, diff --git a/ui/vnc-palette.c b/ui/vnc-palette.c index dc7c0ba997..4e88c412f0 100644 --- a/ui/vnc-palette.c +++ b/ui/vnc-palette.c @@ -86,8 +86,6 @@ int palette_put(VncPalette *palette, uint32_t color) return 0; } if (!entry) { - VncPaletteEntry *entry; - entry = &palette->pool[palette->size]; entry->color = color; entry->idx = idx; diff --git a/ui/vnc-stubs.c b/ui/vnc-stubs.c index b4eb3ce718..a96bc86236 100644 --- a/ui/vnc-stubs.c +++ b/ui/vnc-stubs.c @@ -10,15 +10,3 @@ int vnc_display_pw_expire(const char *id, time_t expires) { return -ENODEV; }; -void vnc_parse(const char *str) -{ - if (strcmp(str, "none") == 0) { - return; - } - error_setg(&error_fatal, "VNC support is disabled"); -} -int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp) -{ - error_setg(errp, "VNC support is disabled"); - return -1; -} diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c index 6d79f3e5a5..9e3503d93d 100644 --- a/ui/vnc-ws.c +++ b/ui/vnc-ws.c @@ -40,9 +40,9 @@ static void vncws_tls_handshake_done(QIOTask *task, if (vs->ioc_tag) { g_source_remove(vs->ioc_tag); } - vs->ioc_tag = qio_channel_add_watch( - QIO_CHANNEL(vs->ioc), G_IO_IN | G_IO_HUP | G_IO_ERR, - vncws_handshake_io, vs, NULL); + vs->ioc_tag = qio_channel_add_watch(vs->ioc, + G_IO_IN | G_IO_HUP | G_IO_ERR, + vncws_handshake_io, vs, NULL); } } diff --git a/ui/vnc.c b/ui/vnc.c index 6a05d06147..b3fd78022b 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -244,7 +244,6 @@ static VncServerInfo *vnc_server_info_get(VncDisplay *vd) info = g_malloc0(sizeof(*info)); vnc_init_basic_info_from_server_addr(vd->listener->sioc[0], qapi_VncServerInfo_base(info), &err); - info->has_auth = true; info->auth = g_strdup(vnc_auth_name(vd)); if (err) { qapi_free_VncServerInfo(info); @@ -263,13 +262,10 @@ static void vnc_client_cache_auth(VncState *client) if (client->tls) { client->info->x509_dname = qcrypto_tls_session_get_peer_name(client->tls); - client->info->has_x509_dname = - client->info->x509_dname != NULL; } #ifdef CONFIG_VNC_SASL if (client->sasl.conn && client->sasl.username) { - client->info->has_sasl_username = true; client->info->sasl_username = g_strdup(client->sasl.username); } #endif @@ -341,11 +337,9 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client) if (client->tls) { info->x509_dname = qcrypto_tls_session_get_peer_name(client->tls); - info->has_x509_dname = info->x509_dname != NULL; } #ifdef CONFIG_VNC_SASL if (client->sasl.conn && client->sasl.username) { - info->has_sasl_username = true; info->sasl_username = g_strdup(client->sasl.username); } #endif @@ -426,11 +420,8 @@ VncInfo *qmp_query_vnc(Error **errp) abort(); } - info->has_host = true; - info->has_service = true; info->has_family = true; - info->has_auth = true; info->auth = g_strdup(vnc_auth_name(vd)); } @@ -568,7 +559,6 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp) if (vd->dcl.con) { dev = DEVICE(object_property_get_link(OBJECT(vd->dcl.con), "device", &error_abort)); - info->has_display = true; info->display = g_strdup(dev->id); } for (i = 0; vd->listener != NULL && i < vd->listener->nsioc; i++) { @@ -843,7 +833,7 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl, /* guest surface */ qemu_pixman_image_unref(vd->guest.fb); vd->guest.fb = pixman_image_ref(surface->image); - vd->guest.format = surface->format; + vd->guest.format = surface_format(surface); if (pageflip) { @@ -998,10 +988,10 @@ static void vnc_mouse_set(DisplayChangeListener *dcl, static int vnc_cursor_define(VncState *vs) { - QEMUCursor *c = vs->vd->cursor; + QEMUCursor *c = qemu_console_get_cursor(vs->vd->dcl.con); int isize; - if (!vs->vd->cursor) { + if (!c) { return -1; } @@ -1039,11 +1029,7 @@ static void vnc_dpy_cursor_define(DisplayChangeListener *dcl, VncDisplay *vd = container_of(dcl, VncDisplay, dcl); VncState *vs; - cursor_put(vd->cursor); g_free(vd->cursor_mask); - - vd->cursor = c; - cursor_get(vd->cursor); vd->cursor_msize = cursor_get_mono_bpl(c) * c->height; vd->cursor_mask = g_malloc0(vd->cursor_msize); cursor_get_mono_mask(c, 0, vd->cursor_mask); @@ -1598,15 +1584,15 @@ static void vnc_jobs_bh(void *opaque) */ static int vnc_client_read(VncState *vs) { - size_t ret; + size_t sz; #ifdef CONFIG_VNC_SASL if (vs->sasl.conn && vs->sasl.runSSF) - ret = vnc_client_read_sasl(vs); + sz = vnc_client_read_sasl(vs); else #endif /* CONFIG_VNC_SASL */ - ret = vnc_client_read_plain(vs); - if (!ret) { + sz = vnc_client_read_plain(vs); + if (!sz) { if (vs->disconnecting) { vnc_disconnect_finish(vs); return -1; @@ -1785,7 +1771,7 @@ uint32_t read_u32(uint8_t *data, size_t offset) static void check_pointer_type_change(Notifier *notifier, void *data) { VncState *vs = container_of(notifier, VncState, mouse_mode_notifier); - int absolute = qemu_input_is_absolute(); + int absolute = qemu_input_is_absolute(vs->vd->dcl.con); if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) { vnc_lock_output(vs); @@ -1886,12 +1872,16 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) /* QEMU console switch */ switch (qcode) { case Q_KEY_CODE_1 ... Q_KEY_CODE_9: /* '1' to '9' keys */ - if (vs->vd->dcl.con == NULL && down && + if (down && qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL) && qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_ALT)) { - /* Reset the modifiers sent to the current console */ - qkbd_state_lift_all_keys(vs->vd->kbd); - console_select(qcode - Q_KEY_CODE_1); + QemuConsole *con = qemu_console_lookup_by_index(qcode - Q_KEY_CODE_1); + if (con) { + unregister_displaychangelistener(&vs->vd->dcl); + qkbd_state_switch_console(vs->vd->kbd, con); + vs->vd->dcl.con = con; + register_displaychangelistener(&vs->vd->dcl); + } return; } default: @@ -1945,7 +1935,8 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) } qkbd_state_key_event(vs->vd->kbd, qcode, down); - if (!qemu_console_is_graphic(NULL)) { + if (!qemu_console_is_graphic(vs->vd->dcl.con)) { + QemuTextConsole *con = QEMU_TEXT_CONSOLE(vs->vd->dcl.con); bool numlock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK); bool control = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL); /* QEMU console emulation */ @@ -1959,88 +1950,88 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) case 0xb8: /* Right ALT */ break; case 0xc8: - kbd_put_keysym(QEMU_KEY_UP); + qemu_text_console_put_keysym(con, QEMU_KEY_UP); break; case 0xd0: - kbd_put_keysym(QEMU_KEY_DOWN); + qemu_text_console_put_keysym(con, QEMU_KEY_DOWN); break; case 0xcb: - kbd_put_keysym(QEMU_KEY_LEFT); + qemu_text_console_put_keysym(con, QEMU_KEY_LEFT); break; case 0xcd: - kbd_put_keysym(QEMU_KEY_RIGHT); + qemu_text_console_put_keysym(con, QEMU_KEY_RIGHT); break; case 0xd3: - kbd_put_keysym(QEMU_KEY_DELETE); + qemu_text_console_put_keysym(con, QEMU_KEY_DELETE); break; case 0xc7: - kbd_put_keysym(QEMU_KEY_HOME); + qemu_text_console_put_keysym(con, QEMU_KEY_HOME); break; case 0xcf: - kbd_put_keysym(QEMU_KEY_END); + qemu_text_console_put_keysym(con, QEMU_KEY_END); break; case 0xc9: - kbd_put_keysym(QEMU_KEY_PAGEUP); + qemu_text_console_put_keysym(con, QEMU_KEY_PAGEUP); break; case 0xd1: - kbd_put_keysym(QEMU_KEY_PAGEDOWN); + qemu_text_console_put_keysym(con, QEMU_KEY_PAGEDOWN); break; case 0x47: - kbd_put_keysym(numlock ? '7' : QEMU_KEY_HOME); + qemu_text_console_put_keysym(con, numlock ? '7' : QEMU_KEY_HOME); break; case 0x48: - kbd_put_keysym(numlock ? '8' : QEMU_KEY_UP); + qemu_text_console_put_keysym(con, numlock ? '8' : QEMU_KEY_UP); break; case 0x49: - kbd_put_keysym(numlock ? '9' : QEMU_KEY_PAGEUP); + qemu_text_console_put_keysym(con, numlock ? '9' : QEMU_KEY_PAGEUP); break; case 0x4b: - kbd_put_keysym(numlock ? '4' : QEMU_KEY_LEFT); + qemu_text_console_put_keysym(con, numlock ? '4' : QEMU_KEY_LEFT); break; case 0x4c: - kbd_put_keysym('5'); + qemu_text_console_put_keysym(con, '5'); break; case 0x4d: - kbd_put_keysym(numlock ? '6' : QEMU_KEY_RIGHT); + qemu_text_console_put_keysym(con, numlock ? '6' : QEMU_KEY_RIGHT); break; case 0x4f: - kbd_put_keysym(numlock ? '1' : QEMU_KEY_END); + qemu_text_console_put_keysym(con, numlock ? '1' : QEMU_KEY_END); break; case 0x50: - kbd_put_keysym(numlock ? '2' : QEMU_KEY_DOWN); + qemu_text_console_put_keysym(con, numlock ? '2' : QEMU_KEY_DOWN); break; case 0x51: - kbd_put_keysym(numlock ? '3' : QEMU_KEY_PAGEDOWN); + qemu_text_console_put_keysym(con, numlock ? '3' : QEMU_KEY_PAGEDOWN); break; case 0x52: - kbd_put_keysym('0'); + qemu_text_console_put_keysym(con, '0'); break; case 0x53: - kbd_put_keysym(numlock ? '.' : QEMU_KEY_DELETE); + qemu_text_console_put_keysym(con, numlock ? '.' : QEMU_KEY_DELETE); break; case 0xb5: - kbd_put_keysym('/'); + qemu_text_console_put_keysym(con, '/'); break; case 0x37: - kbd_put_keysym('*'); + qemu_text_console_put_keysym(con, '*'); break; case 0x4a: - kbd_put_keysym('-'); + qemu_text_console_put_keysym(con, '-'); break; case 0x4e: - kbd_put_keysym('+'); + qemu_text_console_put_keysym(con, '+'); break; case 0x9c: - kbd_put_keysym('\n'); + qemu_text_console_put_keysym(con, '\n'); break; default: if (control) { - kbd_put_keysym(sym & 0x1f); + qemu_text_console_put_keysym(con, sym & 0x1f); } else { - kbd_put_keysym(sym); + qemu_text_console_put_keysym(con, sym); } break; } @@ -2058,7 +2049,7 @@ static void key_event(VncState *vs, int down, uint32_t sym) int keycode; int lsym = sym; - if (lsym >= 'A' && lsym <= 'Z' && qemu_console_is_graphic(NULL)) { + if (lsym >= 'A' && lsym <= 'Z' && qemu_console_is_graphic(vs->vd->dcl.con)) { lsym = lsym - 'A' + 'a'; } @@ -2158,16 +2149,16 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) vs->vnc_encoding = enc; break; case VNC_ENCODING_HEXTILE: - vs->features |= VNC_FEATURE_HEXTILE_MASK; + vnc_set_feature(vs, VNC_FEATURE_HEXTILE); vs->vnc_encoding = enc; break; case VNC_ENCODING_TIGHT: - vs->features |= VNC_FEATURE_TIGHT_MASK; + vnc_set_feature(vs, VNC_FEATURE_TIGHT); vs->vnc_encoding = enc; break; #ifdef CONFIG_PNG case VNC_ENCODING_TIGHT_PNG: - vs->features |= VNC_FEATURE_TIGHT_PNG_MASK; + vnc_set_feature(vs, VNC_FEATURE_TIGHT_PNG); vs->vnc_encoding = enc; break; #endif @@ -2177,54 +2168,57 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) * 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; + if (!vnc_has_feature(vs, VNC_FEATURE_ZRLE)) { + vnc_set_feature(vs, VNC_FEATURE_ZLIB); vs->vnc_encoding = enc; } break; case VNC_ENCODING_ZRLE: - vs->features |= VNC_FEATURE_ZRLE_MASK; + vnc_set_feature(vs, VNC_FEATURE_ZRLE); vs->vnc_encoding = enc; break; case VNC_ENCODING_ZYWRLE: - vs->features |= VNC_FEATURE_ZYWRLE_MASK; + vnc_set_feature(vs, VNC_FEATURE_ZYWRLE); vs->vnc_encoding = enc; break; case VNC_ENCODING_DESKTOPRESIZE: - vs->features |= VNC_FEATURE_RESIZE_MASK; + vnc_set_feature(vs, VNC_FEATURE_RESIZE); break; case VNC_ENCODING_DESKTOP_RESIZE_EXT: - vs->features |= VNC_FEATURE_RESIZE_EXT_MASK; + vnc_set_feature(vs, VNC_FEATURE_RESIZE_EXT); break; case VNC_ENCODING_POINTER_TYPE_CHANGE: - vs->features |= VNC_FEATURE_POINTER_TYPE_CHANGE_MASK; + vnc_set_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE); break; case VNC_ENCODING_RICH_CURSOR: - vs->features |= VNC_FEATURE_RICH_CURSOR_MASK; + vnc_set_feature(vs, VNC_FEATURE_RICH_CURSOR); break; case VNC_ENCODING_ALPHA_CURSOR: - vs->features |= VNC_FEATURE_ALPHA_CURSOR_MASK; + vnc_set_feature(vs, VNC_FEATURE_ALPHA_CURSOR); break; case VNC_ENCODING_EXT_KEY_EVENT: send_ext_key_event_ack(vs); break; case VNC_ENCODING_AUDIO: - send_ext_audio_ack(vs); + if (vs->vd->audio_state) { + vnc_set_feature(vs, VNC_FEATURE_AUDIO); + send_ext_audio_ack(vs); + } break; case VNC_ENCODING_WMVi: - vs->features |= VNC_FEATURE_WMVI_MASK; + vnc_set_feature(vs, VNC_FEATURE_WMVI); break; case VNC_ENCODING_LED_STATE: - vs->features |= VNC_FEATURE_LED_STATE_MASK; + vnc_set_feature(vs, VNC_FEATURE_LED_STATE); break; case VNC_ENCODING_XVP: if (vs->vd->power_control) { - vs->features |= VNC_FEATURE_XVP; + vnc_set_feature(vs, VNC_FEATURE_XVP); send_xvp_message(vs, VNC_XVP_CODE_INIT); } break; case VNC_ENCODING_CLIPBOARD_EXT: - vs->features |= VNC_FEATURE_CLIPBOARD_EXT_MASK; + vnc_set_feature(vs, VNC_FEATURE_CLIPBOARD_EXT); vnc_server_cut_text_caps(vs); break; case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9: @@ -2442,8 +2436,8 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 1) { return 8; } + uint32_t dlen = abs(read_s32(data, 4)); if (len == 8) { - uint32_t dlen = abs(read_s32(data, 4)); if (dlen > (1 << 20)) { error_report("vnc: client_cut_text msg payload has %u bytes" " which exceeds our limit of 1MB.", dlen); @@ -2456,14 +2450,24 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) } if (read_s32(data, 4) < 0) { - vnc_client_cut_text_ext(vs, abs(read_s32(data, 4)), - read_u32(data, 8), data + 12); + if (!vnc_has_feature(vs, VNC_FEATURE_CLIPBOARD_EXT)) { + error_report("vnc: extended clipboard message while disabled"); + vnc_client_error(vs); + break; + } + if (dlen < 4) { + error_report("vnc: malformed payload (header less than 4 bytes)" + " in extended clipboard pseudo-encoding."); + vnc_client_error(vs); + break; + } + vnc_client_cut_text_ext(vs, dlen, read_u32(data, 8), data + 12); break; } vnc_client_cut_text(vs, read_u32(data, 4), data + 8); break; case VNC_MSG_CLIENT_XVP: - if (!(vs->features & VNC_FEATURE_XVP)) { + if (!vnc_has_feature(vs, VNC_FEATURE_XVP)) { error_report("vnc: xvp client message while disabled"); vnc_client_error(vs); break; @@ -2511,6 +2515,12 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) read_u32(data, 4), read_u32(data, 8)); break; case VNC_MSG_CLIENT_QEMU_AUDIO: + if (!vnc_has_feature(vs, VNC_FEATURE_AUDIO)) { + error_report("Audio message %d with audio disabled", read_u8(data, 2)); + vnc_client_error(vs); + break; + } + if (len == 2) return 4; @@ -2560,7 +2570,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) vs, vs->ioc, vs->as.fmt, vs->as.nchannels, vs->as.freq); break; default: - VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 4)); + VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 2)); vnc_client_error(vs); break; } @@ -3080,7 +3090,7 @@ static void vnc_rect_updated(VncDisplay *vd, int x, int y, struct timeval * tv) rect = vnc_stat_rect(vd, x, y); if (rect->updated) { - return ; + return; } rect->times[rect->idx] = *tv; rect->idx = (rect->idx + 1) % ARRAY_SIZE(rect->times); @@ -3127,8 +3137,8 @@ static int vnc_refresh_server_surface(VncDisplay *vd) cmp_bytes = MIN(VNC_DIRTY_PIXELS_PER_BIT * VNC_SERVER_FB_BYTES, server_stride); if (vd->guest.format != VNC_SERVER_FB_FORMAT) { - int width = pixman_image_get_width(vd->server); - tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width); + int w = pixman_image_get_width(vd->server); + tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, w); } else { int guest_bpp = PIXMAN_FORMAT_BPP(pixman_image_get_format(vd->guest.fb)); @@ -3737,7 +3747,7 @@ static int vnc_display_get_address(const char *addrstr, } else { const char *port; size_t hostlen; - unsigned long long baseport = 0; + uint64_t baseport = 0; InetSocketAddress *inet; port = strrchr(addrstr, ':'); @@ -3760,7 +3770,7 @@ static int vnc_display_get_address(const char *addrstr, addr->type = SOCKET_ADDRESS_TYPE_INET; inet = &addr->u.inet; - if (addrstr[0] == '[' && addrstr[hostlen - 1] == ']') { + if (hostlen && addrstr[0] == '[' && addrstr[hostlen - 1] == ']') { inet->host = g_strndup(addrstr + 1, hostlen - 2); } else { inet->host = g_strndup(addrstr, hostlen); @@ -3785,7 +3795,7 @@ static int vnc_display_get_address(const char *addrstr, } } else { int offset = reverse ? 0 : 5900; - if (parse_uint_full(port, &baseport, 10) < 0) { + if (parse_uint_full(port, 10, &baseport) < 0) { error_setg(errp, "can't convert to a number: %s", port); goto cleanup; } @@ -4181,11 +4191,12 @@ void vnc_display_open(const char *id, Error **errp) audiodev = qemu_opt_get(opts, "audiodev"); if (audiodev) { - vd->audio_state = audio_state_by_name(audiodev); + vd->audio_state = audio_state_by_name(audiodev, errp); if (!vd->audio_state) { - error_setg(errp, "Audiodev '%s' not found", audiodev); goto fail; } + } else { + vd->audio_state = audio_get_default_audio_state(NULL); } device_id = qemu_opt_get(opts, "display"); @@ -4199,7 +4210,7 @@ void vnc_display_open(const char *id, Error **errp) goto fail; } } else { - con = NULL; + con = qemu_console_lookup_default(); } if (con != vd->dcl.con) { diff --git a/ui/vnc.h b/ui/vnc.h index a60fb13115..4521dc88f7 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -159,7 +159,6 @@ struct VncDisplay QKbdState *kbd; QemuMutex mutex; - QEMUCursor *cursor; int cursor_msize; uint8_t *cursor_mask; @@ -465,24 +464,9 @@ enum VncFeatures { VNC_FEATURE_LED_STATE, VNC_FEATURE_XVP, VNC_FEATURE_CLIPBOARD_EXT, + VNC_FEATURE_AUDIO, }; -#define VNC_FEATURE_RESIZE_MASK (1 << VNC_FEATURE_RESIZE) -#define VNC_FEATURE_RESIZE_EXT_MASK (1 << VNC_FEATURE_RESIZE_EXT) -#define VNC_FEATURE_HEXTILE_MASK (1 << VNC_FEATURE_HEXTILE) -#define VNC_FEATURE_POINTER_TYPE_CHANGE_MASK (1 << VNC_FEATURE_POINTER_TYPE_CHANGE) -#define VNC_FEATURE_WMVI_MASK (1 << VNC_FEATURE_WMVI) -#define VNC_FEATURE_TIGHT_MASK (1 << VNC_FEATURE_TIGHT) -#define VNC_FEATURE_ZLIB_MASK (1 << VNC_FEATURE_ZLIB) -#define VNC_FEATURE_RICH_CURSOR_MASK (1 << VNC_FEATURE_RICH_CURSOR) -#define VNC_FEATURE_ALPHA_CURSOR_MASK (1 << VNC_FEATURE_ALPHA_CURSOR) -#define VNC_FEATURE_TIGHT_PNG_MASK (1 << VNC_FEATURE_TIGHT_PNG) -#define VNC_FEATURE_ZRLE_MASK (1 << VNC_FEATURE_ZRLE) -#define VNC_FEATURE_ZYWRLE_MASK (1 << VNC_FEATURE_ZYWRLE) -#define VNC_FEATURE_LED_STATE_MASK (1 << VNC_FEATURE_LED_STATE) -#define VNC_FEATURE_XVP_MASK (1 << VNC_FEATURE_XVP) -#define VNC_FEATURE_CLIPBOARD_EXT_MASK (1 << VNC_FEATURE_CLIPBOARD_EXT) - /* Client -> Server message IDs */ #define VNC_MSG_CLIENT_SET_PIXEL_FORMAT 0 @@ -598,6 +582,11 @@ static inline uint32_t vnc_has_feature(VncState *vs, int feature) { return (vs->features & (1 << feature)); } +static inline void vnc_set_feature(VncState *vs, enum VncFeatures feature) +{ + vs->features |= (1 << feature); +} + /* Framebuffer */ void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, int32_t encoding); diff --git a/ui/vnc_keysym.h b/ui/vnc_keysym.h index e8a2ec73c5..016405e74b 100644 --- a/ui/vnc_keysym.h +++ b/ui/vnc_keysym.h @@ -102,7 +102,7 @@ static const name2keysym_t name2keysym[]={ /* latin 1 extensions */ { "nobreakspace", 0x0a0}, { "exclamdown", 0x0a1}, -{ "cent", 0x0a2}, +{ "cent", 0x0a2}, { "sterling", 0x0a3}, { "currency", 0x0a4}, { "yen", 0x0a5}, diff --git a/util/aio-posix.c b/util/aio-posix.c index 731f3826c0..266c9dd35f 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -99,7 +99,6 @@ static bool aio_remove_fd_handler(AioContext *ctx, AioHandler *node) void aio_set_fd_handler(AioContext *ctx, int fd, - bool is_external, IOHandler *io_read, IOHandler *io_write, AioPollFn *io_poll, @@ -144,7 +143,6 @@ void aio_set_fd_handler(AioContext *ctx, new_node->io_poll = io_poll; new_node->io_poll_ready = io_poll_ready; new_node->opaque = opaque; - new_node->is_external = is_external; if (is_new) { new_node->pfd.fd = fd; @@ -180,9 +178,9 @@ void aio_set_fd_handler(AioContext *ctx, } } -void aio_set_fd_poll(AioContext *ctx, int fd, - IOHandler *io_poll_begin, - IOHandler *io_poll_end) +static void aio_set_fd_poll(AioContext *ctx, int fd, + IOHandler *io_poll_begin, + IOHandler *io_poll_end) { AioHandler *node = find_aio_handler(ctx, fd); @@ -196,12 +194,11 @@ void aio_set_fd_poll(AioContext *ctx, int fd, void aio_set_event_notifier(AioContext *ctx, EventNotifier *notifier, - bool is_external, EventNotifierHandler *io_read, AioPollFn *io_poll, EventNotifierHandler *io_poll_ready) { - aio_set_fd_handler(ctx, event_notifier_get_fd(notifier), is_external, + aio_set_fd_handler(ctx, event_notifier_get_fd(notifier), (IOHandler *)io_read, NULL, io_poll, (IOHandler *)io_poll_ready, notifier); } @@ -285,13 +282,11 @@ bool aio_pending(AioContext *ctx) /* TODO should this check poll ready? */ revents = node->pfd.revents & node->pfd.events; - if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read && - aio_node_check(ctx, node->is_external)) { + if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) { result = true; break; } - if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write && - aio_node_check(ctx, node->is_external)) { + if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write) { result = true; break; } @@ -350,11 +345,20 @@ static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node) QLIST_INSERT_HEAD(&ctx->poll_aio_handlers, node, node_poll); } if (!QLIST_IS_INSERTED(node, node_deleted) && - poll_ready && revents == 0 && - aio_node_check(ctx, node->is_external) && - node->io_poll_ready) { + poll_ready && revents == 0 && node->io_poll_ready) { + /* + * Remove temporarily to avoid infinite loops when ->io_poll_ready() + * calls aio_poll() before clearing the condition that made the poll + * handler become ready. + */ + QLIST_SAFE_REMOVE(node, node_poll); + node->io_poll_ready(node->opaque); + if (!QLIST_IS_INSERTED(node, node_poll)) { + QLIST_INSERT_HEAD(&ctx->poll_aio_handlers, node, node_poll); + } + /* * Return early since revents was zero. aio_notify() does not count as * progress. @@ -364,7 +368,6 @@ static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node) 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); @@ -375,7 +378,6 @@ static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node) } 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; @@ -436,8 +438,7 @@ static bool run_poll_handlers_once(AioContext *ctx, AioHandler *tmp; QLIST_FOREACH_SAFE(node, &ctx->poll_aio_handlers, node_poll, tmp) { - if (aio_node_check(ctx, node->is_external) && - node->io_poll(node->opaque)) { + if (node->io_poll(node->opaque)) { aio_add_poll_ready_handler(ready_list, node); node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS; @@ -585,18 +586,16 @@ static bool try_poll_mode(AioContext *ctx, AioHandlerList *ready_list, max_ns = qemu_soonest_timeout(*timeout, ctx->poll_ns); if (max_ns && !ctx->fdmon_ops->need_wait(ctx)) { + /* + * Enable poll mode. It pairs with the poll_set_started() in + * aio_poll() which disables poll mode. + */ poll_set_started(ctx, ready_list, true); if (run_poll_handlers(ctx, ready_list, max_ns, timeout)) { return true; } } - - if (poll_set_started(ctx, ready_list, false)) { - *timeout = 0; - return true; - } - return false; } @@ -657,6 +656,17 @@ bool aio_poll(AioContext *ctx, bool blocking) * system call---a single round of run_poll_handlers_once suffices. */ if (timeout || ctx->fdmon_ops->need_wait(ctx)) { + /* + * Disable poll mode. poll mode should be disabled before the call + * of ctx->fdmon_ops->wait() so that guest's notification can wake + * up IO threads when some work becomes pending. It is essential to + * avoid hangs or unnecessary latency. + */ + if (poll_set_started(ctx, &ready_list, false)) { + timeout = 0; + progress = true; + } + ctx->fdmon_ops->wait(ctx, &ready_list, timeout); } @@ -767,8 +777,7 @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, aio_notify(ctx); } -void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch, - Error **errp) +void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch) { /* * No thread synchronization here, it doesn't matter if an incorrect value diff --git a/util/aio-posix.h b/util/aio-posix.h index 80b927c7f4..4264c518be 100644 --- a/util/aio-posix.h +++ b/util/aio-posix.h @@ -38,7 +38,6 @@ struct AioHandler { #endif int64_t poll_idle_timeout; /* when to stop userspace polling */ bool poll_ready; /* has polling detected an event? */ - bool is_external; }; /* Add a handler to a ready list */ diff --git a/util/aio-wait.c b/util/aio-wait.c index bdb3d3af22..b5336cf5fd 100644 --- a/util/aio-wait.c +++ b/util/aio-wait.c @@ -35,7 +35,21 @@ static void dummy_bh_cb(void *opaque) void aio_wait_kick(void) { - /* The barrier (or an atomic op) is in the caller. */ + /* + * Paired with smp_mb in AIO_WAIT_WHILE. Here we have: + * write(condition); + * aio_wait_kick() { + * smp_mb(); + * read(num_waiters); + * } + * + * And in AIO_WAIT_WHILE: + * write(num_waiters); + * smp_mb(); + * read(condition); + */ + smp_mb(); + if (qatomic_read(&global_aio_wait.num_waiters)) { aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL); } @@ -68,5 +82,5 @@ void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque) assert(qemu_get_current_aio_context() == qemu_get_aio_context()); aio_bh_schedule_oneshot(ctx, aio_wait_bh, &data); - AIO_WAIT_WHILE(ctx, !data.done); + AIO_WAIT_WHILE_UNLOCKED(NULL, !data.done); } diff --git a/util/aio-win32.c b/util/aio-win32.c index 44003d645e..d144f9391f 100644 --- a/util/aio-win32.c +++ b/util/aio-win32.c @@ -22,6 +22,7 @@ #include "qemu/sockets.h" #include "qapi/error.h" #include "qemu/rcu_queue.h" +#include "qemu/error-report.h" struct AioHandler { EventNotifier *e; @@ -31,7 +32,6 @@ struct AioHandler { GPollFD pfd; int deleted; void *opaque; - bool is_external; QLIST_ENTRY(AioHandler) node; }; @@ -63,20 +63,26 @@ static void aio_remove_fd_handler(AioContext *ctx, AioHandler *node) void aio_set_fd_handler(AioContext *ctx, int fd, - bool is_external, IOHandler *io_read, IOHandler *io_write, AioPollFn *io_poll, IOHandler *io_poll_ready, void *opaque) { - /* fd is a SOCKET in our case */ AioHandler *old_node; AioHandler *node = NULL; + SOCKET s; + + if (!fd_is_socket(fd)) { + error_report("fd=%d is not a socket, AIO implementation is missing", fd); + return; + } + + s = _get_osfhandle(fd); qemu_lockcnt_lock(&ctx->list_lock); QLIST_FOREACH(old_node, &ctx->aio_handlers, node) { - if (old_node->pfd.fd == fd && !old_node->deleted) { + if (old_node->pfd.fd == s && !old_node->deleted) { break; } } @@ -87,7 +93,7 @@ void aio_set_fd_handler(AioContext *ctx, /* Alloc and insert if it's not already there */ node = g_new0(AioHandler, 1); - node->pfd.fd = fd; + node->pfd.fd = s; node->pfd.events = 0; if (node->io_read) { @@ -103,7 +109,6 @@ void aio_set_fd_handler(AioContext *ctx, node->opaque = opaque; node->io_read = io_read; node->io_write = io_write; - node->is_external = is_external; if (io_read) { bitmask |= FD_READ | FD_ACCEPT | FD_CLOSE; @@ -115,7 +120,7 @@ void aio_set_fd_handler(AioContext *ctx, QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, node, node); event = event_notifier_get_handle(&ctx->notifier); - WSAEventSelect(node->pfd.fd, event, bitmask); + qemu_socket_select(fd, event, bitmask, NULL); } if (old_node) { aio_remove_fd_handler(ctx, old_node); @@ -125,16 +130,8 @@ void aio_set_fd_handler(AioContext *ctx, aio_notify(ctx); } -void aio_set_fd_poll(AioContext *ctx, int fd, - IOHandler *io_poll_begin, - IOHandler *io_poll_end) -{ - /* Not implemented */ -} - void aio_set_event_notifier(AioContext *ctx, EventNotifier *e, - bool is_external, EventNotifierHandler *io_notify, AioPollFn *io_poll, EventNotifierHandler *io_poll_ready) @@ -160,7 +157,6 @@ void aio_set_event_notifier(AioContext *ctx, node->e = e; node->pfd.fd = (uintptr_t)event_notifier_get_handle(e); node->pfd.events = G_IO_IN; - node->is_external = is_external; QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, node, node); g_source_add_poll(&ctx->source, &node->pfd); @@ -326,9 +322,9 @@ void aio_dispatch(AioContext *ctx) bool aio_poll(AioContext *ctx, bool blocking) { AioHandler *node; - HANDLE events[MAXIMUM_WAIT_OBJECTS + 1]; + HANDLE events[MAXIMUM_WAIT_OBJECTS]; bool progress, have_select_revents, first; - int count; + unsigned count; int timeout; /* @@ -367,8 +363,8 @@ bool aio_poll(AioContext *ctx, bool blocking) /* fill fd sets */ count = 0; QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { - if (!node->deleted && node->io_notify - && aio_node_check(ctx, node->is_external)) { + if (!node->deleted && node->io_notify) { + assert(count < MAXIMUM_WAIT_OBJECTS); events[count++] = event_notifier_get_handle(node->e); } } @@ -442,7 +438,6 @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, } } -void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch, - Error **errp) +void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch) { } diff --git a/util/async.c b/util/async.c index 63434ddae4..0467890052 100644 --- a/util/async.c +++ b/util/async.c @@ -27,6 +27,7 @@ #include "qapi/error.h" #include "block/aio.h" #include "block/thread-pool.h" +#include "block/graph-lock.h" #include "qemu/main-loop.h" #include "qemu/atomic.h" #include "qemu/rcu_queue.h" @@ -64,6 +65,7 @@ struct QEMUBH { void *opaque; QSLIST_ENTRY(QEMUBH) next; unsigned flags; + MemReentrancyGuard *reentrancy_guard; }; /* Called concurrently from any thread */ @@ -73,25 +75,34 @@ static void aio_bh_enqueue(QEMUBH *bh, unsigned new_flags) unsigned old_flags; /* - * The memory barrier implicit in qatomic_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. + * Synchronizes with atomic_fetch_and() in aio_bh_dequeue(), ensuring that + * insertion starts after BH_PENDING is set. */ old_flags = qatomic_fetch_or(&bh->flags, BH_PENDING | new_flags); + if (!(old_flags & BH_PENDING)) { + /* + * At this point the bottom half becomes visible to aio_bh_poll(). + * This insertion thus synchronizes with QSLIST_MOVE_ATOMIC in + * aio_bh_poll(), ensuring that: + * 1. any writes needed by the callback are visible from the callback + * after aio_bh_dequeue() returns bh. + * 2. ctx is loaded before the callback has a chance to execute and bh + * could be freed. + */ QSLIST_INSERT_HEAD_ATOMIC(&ctx->bh_list, bh, next); } aio_notify(ctx); - /* - * Workaround for record/replay. - * vCPU execution should be suspended when new BH is set. - * This is needed to avoid guest timeouts caused - * by the long cycles of the execution. - */ - icount_notify_exit(); + if (unlikely(icount_enabled())) { + /* + * Workaround for record/replay. + * vCPU execution should be suspended when new BH is set. + * This is needed to avoid guest timeouts caused + * by the long cycles of the execution. + */ + icount_notify_exit(); + } } /* Only called from aio_bh_poll() and aio_ctx_finalize() */ @@ -106,11 +117,8 @@ static QEMUBH *aio_bh_dequeue(BHList *head, unsigned *flags) QSLIST_REMOVE_HEAD(head, next); /* - * The qatomic_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. + * Synchronizes with qatomic_fetch_or() in aio_bh_enqueue(), ensuring that + * the removal finishes before BH_PENDING is reset. */ *flags = qatomic_fetch_and(&bh->flags, ~(BH_PENDING | BH_SCHEDULED | BH_IDLE)); @@ -132,7 +140,7 @@ void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb, } QEMUBH *aio_bh_new_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, - const char *name) + const char *name, MemReentrancyGuard *reentrancy_guard) { QEMUBH *bh; bh = g_new(QEMUBH, 1); @@ -141,13 +149,30 @@ QEMUBH *aio_bh_new_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, .cb = cb, .opaque = opaque, .name = name, + .reentrancy_guard = reentrancy_guard, }; return bh; } void aio_bh_call(QEMUBH *bh) { + bool last_engaged_in_io = false; + + /* Make a copy of the guard-pointer as cb may free the bh */ + MemReentrancyGuard *reentrancy_guard = bh->reentrancy_guard; + if (reentrancy_guard) { + last_engaged_in_io = reentrancy_guard->engaged_in_io; + if (reentrancy_guard->engaged_in_io) { + trace_reentrant_aio(bh->ctx, bh->name); + } + reentrancy_guard->engaged_in_io = true; + } + bh->cb(bh->opaque); + + if (reentrancy_guard) { + reentrancy_guard->engaged_in_io = last_engaged_in_io; + } } /* Multiple occurrences of aio_bh_poll cannot be called concurrently. */ @@ -157,8 +182,23 @@ int aio_bh_poll(AioContext *ctx) BHListSlice *s; int ret = 0; + /* Synchronizes with QSLIST_INSERT_HEAD_ATOMIC in aio_bh_enqueue(). */ QSLIST_MOVE_ATOMIC(&slice.bh_list, &ctx->bh_list); + + /* + * GCC13 [-Werror=dangling-pointer=] complains that the local variable + * 'slice' is being stored in the global 'ctx->bh_slice_list' but the + * list is emptied before this function returns. + */ +#if !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wdangling-pointer=" +#endif QSIMPLEQ_INSERT_TAIL(&ctx->bh_slice_list, &slice, next); +#if !defined(__clang__) +#pragma GCC diagnostic pop +#endif while ((s = QSIMPLEQ_FIRST(&ctx->bh_slice_list))) { QEMUBH *bh; @@ -371,11 +411,12 @@ aio_ctx_finalize(GSource *source) g_free(bh); } - aio_set_event_notifier(ctx, &ctx->notifier, false, NULL, NULL, NULL); + aio_set_event_notifier(ctx, &ctx->notifier, NULL, NULL, NULL); event_notifier_cleanup(&ctx->notifier); qemu_rec_mutex_destroy(&ctx->lock); qemu_lockcnt_destroy(&ctx->list_lock); timerlistgroup_deinit(&ctx->tlg); + unregister_aiocontext(ctx); aio_context_destroy(ctx); } @@ -446,15 +487,15 @@ LuringState *aio_get_linux_io_uring(AioContext *ctx) void aio_notify(AioContext *ctx) { /* - * Write e.g. bh->flags before writing ctx->notified. Pairs with smp_mb in - * aio_notify_accept. + * Write e.g. ctx->bh_list before writing ctx->notified. Pairs with + * smp_mb() in aio_notify_accept(). */ smp_wmb(); qatomic_set(&ctx->notified, true); /* - * Write ctx->notified before reading ctx->notify_me. Pairs - * with smp_mb in aio_ctx_prepare or aio_poll. + * Write ctx->notified (and also ctx->bh_list) before reading ctx->notify_me. + * Pairs with smp_mb() in aio_ctx_prepare or aio_poll. */ smp_mb(); if (qatomic_read(&ctx->notify_me)) { @@ -467,8 +508,9 @@ void aio_notify_accept(AioContext *ctx) qatomic_set(&ctx->notified, false); /* - * Write ctx->notified before reading e.g. bh->flags. Pairs with smp_wmb - * in aio_notify. + * Order reads of ctx->notified (in aio_context_notifier_poll()) and the + * above clearing of ctx->notified before reads of e.g. bh->flags. Pairs + * with smp_wmb() in aio_notify. */ smp_mb(); } @@ -491,6 +533,11 @@ static bool aio_context_notifier_poll(void *opaque) EventNotifier *e = opaque; AioContext *ctx = container_of(e, AioContext, notifier); + /* + * No need for load-acquire because we just want to kick the + * event loop. aio_notify_accept() takes care of synchronizing + * the event loop with the producers. + */ return qatomic_read(&ctx->notified); } @@ -517,12 +564,10 @@ static void co_schedule_bh_cb(void *opaque) Coroutine *co = QSLIST_FIRST(&straight); QSLIST_REMOVE_HEAD(&straight, co_scheduled_next); trace_aio_co_schedule_bh_cb(ctx, co); - aio_context_acquire(ctx); /* Protected by write barrier in qemu_aio_coroutine_enter */ qatomic_set(&co->scheduled, NULL); qemu_aio_coroutine_enter(ctx, co); - aio_context_release(ctx); } } @@ -548,7 +593,6 @@ AioContext *aio_context_new(Error **errp) QSLIST_INIT(&ctx->scheduled_coroutines); aio_set_event_notifier(ctx, &ctx->notifier, - false, aio_context_notifier_cb, aio_context_notifier_poll, aio_context_notifier_poll_ready); @@ -574,6 +618,8 @@ AioContext *aio_context_new(Error **errp) ctx->thread_pool_min = 0; ctx->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT; + register_aiocontext(ctx); + return ctx; fail: g_source_destroy(&ctx->source); @@ -636,7 +682,7 @@ void coroutine_fn aio_co_reschedule_self(AioContext *new_ctx) } } -void aio_co_wake(struct Coroutine *co) +void aio_co_wake(Coroutine *co) { AioContext *ctx; @@ -649,7 +695,7 @@ void aio_co_wake(struct Coroutine *co) aio_co_enter(ctx, co); } -void aio_co_enter(AioContext *ctx, struct Coroutine *co) +void aio_co_enter(AioContext *ctx, Coroutine *co) { if (ctx != qemu_get_current_aio_context()) { aio_co_schedule(ctx, co); @@ -661,9 +707,7 @@ void aio_co_enter(AioContext *ctx, struct Coroutine *co) assert(self != co); QSIMPLEQ_INSERT_TAIL(&self->co_queue_wakeup, co, co_queue_next); } else { - aio_context_acquire(ctx); qemu_aio_coroutine_enter(ctx, co); - aio_context_release(ctx); } } @@ -677,16 +721,6 @@ void aio_context_unref(AioContext *ctx) g_source_unref(&ctx->source); } -void aio_context_acquire(AioContext *ctx) -{ - qemu_rec_mutex_lock(&ctx->lock); -} - -void aio_context_release(AioContext *ctx) -{ - qemu_rec_mutex_unlock(&ctx->lock); -} - QEMU_DEFINE_STATIC_CO_TLS(AioContext *, my_aiocontext) AioContext *qemu_get_current_aio_context(void) @@ -695,7 +729,7 @@ AioContext *qemu_get_current_aio_context(void) if (ctx) { return ctx; } - if (qemu_mutex_iothread_locked()) { + if (bql_locked()) { /* Possibly in a vCPU thread. */ return qemu_get_aio_context(); } diff --git a/util/bitmap.c b/util/bitmap.c index f81d8057a7..8d12e90a5a 100644 --- a/util/bitmap.c +++ b/util/bitmap.c @@ -240,6 +240,51 @@ void bitmap_clear(unsigned long *map, long start, long nr) } } +bool bitmap_test_and_clear(unsigned long *map, long start, long nr) +{ + unsigned long *p = map + BIT_WORD(start); + const long size = start + nr; + int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG); + unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start); + bool dirty = false; + + assert(start >= 0 && nr >= 0); + + /* First word */ + if (nr - bits_to_clear > 0) { + if ((*p) & mask_to_clear) { + dirty = true; + } + *p &= ~mask_to_clear; + nr -= bits_to_clear; + bits_to_clear = BITS_PER_LONG; + p++; + } + + /* Full words */ + if (bits_to_clear == BITS_PER_LONG) { + while (nr >= BITS_PER_LONG) { + if (*p) { + dirty = true; + *p = 0; + } + nr -= BITS_PER_LONG; + p++; + } + } + + /* Last word */ + if (nr) { + mask_to_clear &= BITMAP_LAST_WORD_MASK(size); + if ((*p) & mask_to_clear) { + dirty = true; + } + *p &= ~mask_to_clear; + } + + return dirty; +} + bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr) { unsigned long *p = map + BIT_WORD(start); diff --git a/util/bitops.c b/util/bitops.c index 3fe6b1c4f1..4b647b3eff 100644 --- a/util/bitops.c +++ b/util/bitops.c @@ -71,8 +71,8 @@ unsigned long find_next_bit(const unsigned long *addr, unsigned long size, found_first: tmp &= (~0UL >> (BITS_PER_LONG - size)); - if (tmp == 0UL) { /* Are any bits set? */ - return result + size; /* Nope. */ + if (tmp == 0UL) { /* Are any bits set? */ + return result + size; /* Nope. */ } found_middle: return result + ctzl(tmp); @@ -120,8 +120,8 @@ unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, found_first: tmp |= ~0UL << size; - if (tmp == ~0UL) { /* Are any bits zero? */ - return result + size; /* Nope. */ + if (tmp == ~0UL) { /* Are any bits zero? */ + return result + size; /* Nope. */ } found_middle: return result + ctzl(~tmp); diff --git a/util/bufferiszero.c b/util/bufferiszero.c index ec3cd4ca15..3e6a5dfd63 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/bswap.h" +#include "host/cpuinfo.h" static bool buffer_zero_int(const void *buf, size_t len) @@ -64,18 +65,11 @@ buffer_zero_int(const void *buf, size_t len) } #if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) || defined(__SSE2__) -/* Do not use push_options pragmas unnecessarily, because clang - * does not support them. - */ -#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) -#pragma GCC push_options -#pragma GCC target("sse2") -#endif -#include +#include /* Note that each of these vectorized functions require len >= 64. */ -static bool +static bool __attribute__((target("sse2"))) buffer_zero_sse2(const void *buf, size_t len) { __m128i t = _mm_loadu_si128(buf); @@ -104,20 +98,9 @@ buffer_zero_sse2(const void *buf, size_t len) return _mm_movemask_epi8(_mm_cmpeq_epi8(t, zero)) == 0xFFFF; } -#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) -#pragma GCC pop_options -#endif #ifdef CONFIG_AVX2_OPT -/* Note that due to restrictions/bugs wrt __builtin functions in gcc <= 4.8, - * the includes have to be within the corresponding push_options region, and - * therefore the regions themselves have to be ordered with increasing ISA. - */ -#pragma GCC push_options -#pragma GCC target("sse4") -#include - -static bool +static bool __attribute__((target("sse4"))) buffer_zero_sse4(const void *buf, size_t len) { __m128i t = _mm_loadu_si128(buf); @@ -145,12 +128,7 @@ buffer_zero_sse4(const void *buf, size_t len) return _mm_testz_si128(t, t); } -#pragma GCC pop_options -#pragma GCC push_options -#pragma GCC target("avx2") -#include - -static bool +static bool __attribute__((target("avx2"))) buffer_zero_avx2(const void *buf, size_t len) { /* Begin with an unaligned head of 32 bytes. */ @@ -176,15 +154,10 @@ buffer_zero_avx2(const void *buf, size_t len) return _mm256_testz_si256(t, t); } -#pragma GCC pop_options #endif /* CONFIG_AVX2_OPT */ #ifdef CONFIG_AVX512F_OPT -#pragma GCC push_options -#pragma GCC target("avx512f") -#include - -static bool +static bool __attribute__((target("avx512f"))) buffer_zero_avx512(const void *buf, size_t len) { /* Begin with an unaligned head of 64 bytes. */ @@ -210,115 +183,77 @@ buffer_zero_avx512(const void *buf, size_t len) return !_mm512_test_epi64_mask(t, t); } -#pragma GCC pop_options -#endif +#endif /* CONFIG_AVX512F_OPT */ - -/* Note that for test_buffer_is_zero_next_accel, the most preferred - * ISA must have the least significant bit. - */ -#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 +/* + * 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. */ #if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) -# define INIT_CACHE 0 -# define INIT_ACCEL buffer_zero_int +# define INIT_USED 0 +# define INIT_LENGTH 0 +# define INIT_ACCEL buffer_zero_int #else # ifndef __SSE2__ # error "ISA selection confusion" # endif -# define INIT_CACHE CACHE_SSE2 -# define INIT_ACCEL buffer_zero_sse2 +# define INIT_USED CPUINFO_SSE2 +# define INIT_LENGTH 64 +# define INIT_ACCEL buffer_zero_sse2 #endif -static unsigned cpuid_cache = INIT_CACHE; +static unsigned used_accel = INIT_USED; +static unsigned length_to_accel = INIT_LENGTH; static bool (*buffer_accel)(const void *, size_t) = INIT_ACCEL; -static int length_to_accel = 64; -static void init_accel(unsigned cache) +static unsigned __attribute__((noinline)) +select_accel_cpuinfo(unsigned info) { - 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 + /* Array is sorted in order of algorithm preference. */ + static const struct { + unsigned bit; + unsigned len; + bool (*fn)(const void *, size_t); + } all[] = { #ifdef CONFIG_AVX512F_OPT - if (cache & CACHE_AVX512F) { - fn = buffer_zero_avx512; - length_to_accel = 256; - } + { CPUINFO_AVX512F, 256, buffer_zero_avx512 }, #endif - buffer_accel = fn; +#ifdef CONFIG_AVX2_OPT + { CPUINFO_AVX2, 128, buffer_zero_avx2 }, + { CPUINFO_SSE4, 64, buffer_zero_sse4 }, +#endif + { CPUINFO_SSE2, 64, buffer_zero_sse2 }, + { CPUINFO_ALWAYS, 0, buffer_zero_int }, + }; + + for (unsigned i = 0; i < ARRAY_SIZE(all); ++i) { + if (info & all[i].bit) { + length_to_accel = all[i].len; + buffer_accel = all[i].fn; + return all[i].bit; + } + } + return 0; } #if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) -#include "qemu/cpuid.h" - -static void __attribute__((constructor)) init_cpuid_cache(void) +static void __attribute__((constructor)) init_accel(void) { - unsigned max = __get_cpuid_max(0, NULL); - int a, b, c, d; - unsigned cache = 0; - - if (max >= 1) { - __cpuid(1, a, b, c, d); - if (d & bit_SSE2) { - cache |= CACHE_SSE2; - } - if (c & bit_SSE4_1) { - cache |= CACHE_SSE4; - } - - /* We must check that AVX is not just available, but usable. */ - if ((c & bit_OSXSAVE) && (c & bit_AVX) && max >= 7) { - int bv; - __asm("xgetbv" : "=a"(bv), "=d"(d) : "c"(0)); - __cpuid_count(7, 0, a, b, c, d); - 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; - init_accel(cache); + used_accel = select_accel_cpuinfo(cpuinfo_init()); } #endif /* CONFIG_AVX2_OPT */ bool test_buffer_is_zero_next_accel(void) { - /* If no bits set, we just tested buffer_zero_int, and there - are no more acceleration options to test. */ - if (cpuid_cache == 0) { - return false; - } - /* Disable the accelerator we used before and select a new one. */ - cpuid_cache &= cpuid_cache - 1; - init_accel(cpuid_cache); - return true; + /* + * Accumulate the accelerators that we've already tested, and + * remove them from the set to test this round. We'll get back + * a zero from select_accel_cpuinfo when there are no more. + */ + unsigned used = select_accel_cpuinfo(cpuinfo & ~used_accel); + used_accel |= used; + return used; } static bool select_accel_fn(const void *buf, size_t len) diff --git a/util/cacheflush.c b/util/cacheflush.c index 4b57186d89..a08906155a 100644 --- a/util/cacheflush.c +++ b/util/cacheflush.c @@ -1,5 +1,5 @@ /* - * Flush the host cpu caches. + * Info about, and flushing the host cpu caches. * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -9,36 +9,250 @@ #include "qemu/cacheflush.h" #include "qemu/cacheinfo.h" #include "qemu/bitops.h" +#include "qemu/host-utils.h" +#include "qemu/atomic.h" +int qemu_icache_linesize = 0; +int qemu_icache_linesize_log; +int qemu_dcache_linesize = 0; +int qemu_dcache_linesize_log; + +/* + * Operating system specific cache detection mechanisms. + */ + +#if defined(_WIN32) + +static void sys_cache_info(int *isize, int *dsize) +{ + SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buf; + DWORD size = 0; + BOOL success; + size_t i, n; + + /* + * Check for the required buffer size first. Note that if the zero + * size we use for the probe results in success, then there is no + * data available; fail in that case. + */ + success = GetLogicalProcessorInformation(0, &size); + if (success || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + return; + } + + n = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + size = n * sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + buf = g_new0(SYSTEM_LOGICAL_PROCESSOR_INFORMATION, n); + if (!GetLogicalProcessorInformation(buf, &size)) { + goto fail; + } + + for (i = 0; i < n; i++) { + if (buf[i].Relationship == RelationCache + && buf[i].Cache.Level == 1) { + switch (buf[i].Cache.Type) { + case CacheUnified: + *isize = *dsize = buf[i].Cache.LineSize; + break; + case CacheInstruction: + *isize = buf[i].Cache.LineSize; + break; + case CacheData: + *dsize = buf[i].Cache.LineSize; + break; + default: + break; + } + } + } + fail: + g_free(buf); +} + +#elif defined(CONFIG_DARWIN) +# include +static void sys_cache_info(int *isize, int *dsize) +{ + /* There's only a single sysctl for both I/D cache line sizes. */ + long size; + size_t len = sizeof(size); + if (!sysctlbyname("hw.cachelinesize", &size, &len, NULL, 0)) { + *isize = *dsize = size; + } +} +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +# include +static void sys_cache_info(int *isize, int *dsize) +{ + /* There's only a single sysctl for both I/D cache line sizes. */ + int size; + size_t len = sizeof(size); + if (!sysctlbyname("machdep.cacheline_size", &size, &len, NULL, 0)) { + *isize = *dsize = size; + } +} +#else +/* POSIX */ + +static void sys_cache_info(int *isize, int *dsize) +{ +# ifdef _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 + int tmp_dsize = (int) sysconf(_SC_LEVEL1_DCACHE_LINESIZE); + if (tmp_dsize > 0) { + *dsize = tmp_dsize; + } +# endif +} +#endif /* sys_cache_info */ + + +/* + * Architecture (+ OS) specific cache detection mechanisms. + */ + +#if defined(__powerpc__) +static bool have_coherent_icache; +#endif + +#if defined(__aarch64__) && !defined(CONFIG_DARWIN) && !defined(CONFIG_WIN32) +/* + * Apple does not expose CTR_EL0, so we must use system interfaces. + * Windows neither, but we use a generic implementation of flush_idcache_range + * in this case. + */ +static uint64_t save_ctr_el0; +static void arch_cache_info(int *isize, int *dsize) +{ + uint64_t ctr; + + /* + * The real cache geometry is in CCSIDR_EL1/CLIDR_EL1/CSSELR_EL1, + * but (at least under Linux) these are marked protected by the + * kernel. However, CTR_EL0 contains the minimum linesize in the + * entire hierarchy, and is used by userspace cache flushing. + * + * We will also use this value in flush_idcache_range. + */ + asm volatile("mrs\t%0, ctr_el0" : "=r"(ctr)); + save_ctr_el0 = ctr; + + if (*isize == 0 || *dsize == 0) { + if (*isize == 0) { + *isize = 4 << (ctr & 0xf); + } + if (*dsize == 0) { + *dsize = 4 << ((ctr >> 16) & 0xf); + } + } +} + +#elif defined(_ARCH_PPC) && defined(__linux__) +# include "elf.h" + +static void arch_cache_info(int *isize, int *dsize) +{ + if (*isize == 0) { + *isize = qemu_getauxval(AT_ICACHEBSIZE); + } + if (*dsize == 0) { + *dsize = qemu_getauxval(AT_DCACHEBSIZE); + } + have_coherent_icache = qemu_getauxval(AT_HWCAP) & PPC_FEATURE_ICACHE_SNOOP; +} + +#else +static void arch_cache_info(int *isize, int *dsize) { } +#endif /* arch_cache_info */ + +/* + * ... and if all else fails ... + */ + +static void fallback_cache_info(int *isize, int *dsize) +{ + /* If we can only find one of the two, assume they're the same. */ + if (*isize) { + if (*dsize) { + /* Success! */ + } else { + *dsize = *isize; + } + } else if (*dsize) { + *isize = *dsize; + } else { +#if defined(_ARCH_PPC) + /* + * For PPC, we're going to use the cache sizes computed for + * flush_idcache_range. Which means that we must use the + * architecture minimum. + */ + *isize = *dsize = 16; +#else + /* Otherwise, 64 bytes is not uncommon. */ + *isize = *dsize = 64; +#endif + } +} + +static void __attribute__((constructor)) init_cache_info(void) +{ + int isize = 0, dsize = 0; + + sys_cache_info(&isize, &dsize); + arch_cache_info(&isize, &dsize); + fallback_cache_info(&isize, &dsize); + + assert((isize & (isize - 1)) == 0); + assert((dsize & (dsize - 1)) == 0); + + qemu_icache_linesize = isize; + qemu_icache_linesize_log = ctz32(isize); + qemu_dcache_linesize = dsize; + qemu_dcache_linesize_log = ctz32(dsize); + + qatomic64_init(); +} + + +/* + * Architecture (+ OS) specific cache flushing mechanisms. + */ + #if defined(__i386__) || defined(__x86_64__) || defined(__s390__) /* Caches are coherent and do not require flushing; symbol inline. */ -#elif defined(__aarch64__) +#elif defined(__aarch64__) && !defined(CONFIG_WIN32) +/* + * For Windows, we use generic implementation of flush_idcache_range, that + * performs a call to FlushInstructionCache, through __builtin___clear_cache. + */ #ifdef CONFIG_DARWIN /* Apple does not expose CTR_EL0, so we must use system interfaces. */ -extern void sys_icache_invalidate(void *start, size_t len); -extern void sys_dcache_flush(void *start, size_t len); +#include + void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) { - sys_dcache_flush((void *)rw, len); + if (rx == rw) { + /* + * sys_icache_invalidate() syncs the dcache and icache, + * so no need to call sys_dcache_flush(). + */ + } else { + sys_dcache_flush((void *)rw, len); + } sys_icache_invalidate((void *)rx, len); } #else -/* - * TODO: unify this with cacheinfo.c. - * We want to save the whole contents of CTR_EL0, so that we - * have more than the linesize, but also IDC and DIC. - */ -static uint64_t save_ctr_el0; -static void __attribute__((constructor)) init_ctr_el0(void) -{ - asm volatile("mrs\t%0, ctr_el0" : "=r"(save_ctr_el0)); -} - /* * This is a copy of gcc's __aarch64_sync_cache_range, modified * to fit this three-operand interface. @@ -48,8 +262,8 @@ void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) const unsigned CTR_IDC = 1u << 28; const unsigned CTR_DIC = 1u << 29; const uint64_t ctr_el0 = save_ctr_el0; - const uintptr_t icache_lsize = 4 << extract64(ctr_el0, 0, 4); - const uintptr_t dcache_lsize = 4 << extract64(ctr_el0, 16, 4); + const uintptr_t icache_lsize = qemu_icache_linesize; + const uintptr_t dcache_lsize = qemu_dcache_linesize; uintptr_t p; /* @@ -104,8 +318,24 @@ void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) { uintptr_t p, b, e; - size_t dsize = qemu_dcache_linesize; - size_t isize = qemu_icache_linesize; + size_t dsize, isize; + + /* + * Some processors have coherent caches and support a simplified + * flushing procedure. See + * POWER9 UM, 4.6.2.2 Instruction Cache Block Invalidate (icbi) + * https://ibm.ent.box.com/s/tmklq90ze7aj8f4n32er1mu3sy9u8k3k + */ + if (have_coherent_icache) { + asm volatile ("sync\n\t" + "icbi 0,%0\n\t" + "isync" + : : "r"(rx) : "memory"); + return; + } + + dsize = qemu_dcache_linesize; + isize = qemu_icache_linesize; b = rw & ~(dsize - 1); e = (rw + len + dsize - 1) & ~(dsize - 1); diff --git a/util/cacheinfo.c b/util/cacheinfo.c deleted file mode 100644 index ab1644d490..0000000000 --- a/util/cacheinfo.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * cacheinfo.c - helpers to query the host about its caches - * - * Copyright (C) 2017, Emilio G. Cota - * License: GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qemu/host-utils.h" -#include "qemu/atomic.h" -#include "qemu/cacheinfo.h" - -int qemu_icache_linesize = 0; -int qemu_icache_linesize_log; -int qemu_dcache_linesize = 0; -int qemu_dcache_linesize_log; - -/* - * Operating system specific detection mechanisms. - */ - -#if defined(_WIN32) - -static void sys_cache_info(int *isize, int *dsize) -{ - SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buf; - DWORD size = 0; - BOOL success; - size_t i, n; - - /* Check for the required buffer size first. Note that if the zero - size we use for the probe results in success, then there is no - data available; fail in that case. */ - success = GetLogicalProcessorInformation(0, &size); - if (success || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - return; - } - - n = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); - size = n * sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); - buf = g_new0(SYSTEM_LOGICAL_PROCESSOR_INFORMATION, n); - if (!GetLogicalProcessorInformation(buf, &size)) { - goto fail; - } - - for (i = 0; i < n; i++) { - if (buf[i].Relationship == RelationCache - && buf[i].Cache.Level == 1) { - switch (buf[i].Cache.Type) { - case CacheUnified: - *isize = *dsize = buf[i].Cache.LineSize; - break; - case CacheInstruction: - *isize = buf[i].Cache.LineSize; - break; - case CacheData: - *dsize = buf[i].Cache.LineSize; - break; - default: - break; - } - } - } - fail: - g_free(buf); -} - -#elif defined(__APPLE__) -# include -static void sys_cache_info(int *isize, int *dsize) -{ - /* There's only a single sysctl for both I/D cache line sizes. */ - long size; - size_t len = sizeof(size); - if (!sysctlbyname("hw.cachelinesize", &size, &len, NULL, 0)) { - *isize = *dsize = size; - } -} -#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) -# include -static void sys_cache_info(int *isize, int *dsize) -{ - /* There's only a single sysctl for both I/D cache line sizes. */ - int size; - size_t len = sizeof(size); - if (!sysctlbyname("machdep.cacheline_size", &size, &len, NULL, 0)) { - *isize = *dsize = size; - } -} -#else -/* POSIX */ - -static void sys_cache_info(int *isize, int *dsize) -{ -# ifdef _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 - int tmp_dsize = (int) sysconf(_SC_LEVEL1_DCACHE_LINESIZE); - if (tmp_dsize > 0) { - *dsize = tmp_dsize; - } -# endif -} -#endif /* sys_cache_info */ - -/* - * Architecture (+ OS) specific detection mechanisms. - */ - -#if defined(__aarch64__) - -static void arch_cache_info(int *isize, int *dsize) -{ - if (*isize == 0 || *dsize == 0) { - uint64_t ctr; - - /* The real cache geometry is in CCSIDR_EL1/CLIDR_EL1/CSSELR_EL1, - but (at least under Linux) these are marked protected by the - kernel. However, CTR_EL0 contains the minimum linesize in the - entire hierarchy, and is used by userspace cache flushing. */ - asm volatile("mrs\t%0, ctr_el0" : "=r"(ctr)); - if (*isize == 0) { - *isize = 4 << (ctr & 0xf); - } - if (*dsize == 0) { - *dsize = 4 << ((ctr >> 16) & 0xf); - } - } -} - -#elif defined(_ARCH_PPC) && defined(__linux__) -# include "elf.h" - -static void arch_cache_info(int *isize, int *dsize) -{ - if (*isize == 0) { - *isize = qemu_getauxval(AT_ICACHEBSIZE); - } - if (*dsize == 0) { - *dsize = qemu_getauxval(AT_DCACHEBSIZE); - } -} - -#else -static void arch_cache_info(int *isize, int *dsize) { } -#endif /* arch_cache_info */ - -/* - * ... and if all else fails ... - */ - -static void fallback_cache_info(int *isize, int *dsize) -{ - /* If we can only find one of the two, assume they're the same. */ - if (*isize) { - if (*dsize) { - /* Success! */ - } else { - *dsize = *isize; - } - } else if (*dsize) { - *isize = *dsize; - } else { -#if defined(_ARCH_PPC) - /* - * For PPC, we're going to use the cache sizes computed for - * flush_idcache_range. Which means that we must use the - * architecture minimum. - */ - *isize = *dsize = 16; -#else - /* Otherwise, 64 bytes is not uncommon. */ - *isize = *dsize = 64; -#endif - } -} - -static void __attribute__((constructor)) init_cache_info(void) -{ - int isize = 0, dsize = 0; - - sys_cache_info(&isize, &dsize); - arch_cache_info(&isize, &dsize); - fallback_cache_info(&isize, &dsize); - - assert((isize & (isize - 1)) == 0); - assert((dsize & (dsize - 1)) == 0); - - qemu_icache_linesize = isize; - qemu_icache_linesize_log = ctz32(isize); - qemu_dcache_linesize = dsize; - qemu_dcache_linesize_log = ctz32(dsize); - - qatomic64_init(); -} diff --git a/util/chardev_open.c b/util/chardev_open.c new file mode 100644 index 0000000000..f776429788 --- /dev/null +++ b/util/chardev_open.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2019, Mellanox Technologies. All rights reserved. + * Copyright (C) 2023 Intel Corporation. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - 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. + * + * 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: Yi Liu + * + * Copied from + * https://github.com/linux-rdma/rdma-core/blob/master/util/open_cdev.c + * + */ + +#include "qemu/osdep.h" +#include "qemu/chardev_open.h" + +static int open_cdev_internal(const char *path, dev_t cdev) +{ + struct stat st; + int fd; + + fd = qemu_open_old(path, O_RDWR); + if (fd == -1) { + return -1; + } + if (fstat(fd, &st) || !S_ISCHR(st.st_mode) || + (cdev != 0 && st.st_rdev != cdev)) { + close(fd); + return -1; + } + return fd; +} + +static int open_cdev_robust(dev_t cdev) +{ + g_autofree char *devpath = NULL; + + /* + * This assumes that udev is being used and is creating the /dev/char/ + * symlinks. + */ + devpath = g_strdup_printf("/dev/char/%u:%u", major(cdev), minor(cdev)); + return open_cdev_internal(devpath, cdev); +} + +int open_cdev(const char *devpath, dev_t cdev) +{ + int fd; + + fd = open_cdev_internal(devpath, cdev); + if (fd == -1 && cdev != 0) { + return open_cdev_robust(cdev); + } + return fd; +} diff --git a/util/coroutine-sigaltstack.c b/util/coroutine-sigaltstack.c index e2690c5f41..037d6416c4 100644 --- a/util/coroutine-sigaltstack.c +++ b/util/coroutine-sigaltstack.c @@ -22,9 +22,9 @@ */ /* XXX Is there a nicer way to disable glibc's stack check for longjmp? */ -#ifdef _FORTIFY_SOURCE #undef _FORTIFY_SOURCE -#endif +#define _FORTIFY_SOURCE 0 + #include "qemu/osdep.h" #include #include "qemu/coroutine_int.h" diff --git a/util/coroutine-ucontext.c b/util/coroutine-ucontext.c index ddc98fb4f8..8ef603d081 100644 --- a/util/coroutine-ucontext.c +++ b/util/coroutine-ucontext.c @@ -19,9 +19,9 @@ */ /* XXX Is there a nicer way to disable glibc's stack check for longjmp? */ -#ifdef _FORTIFY_SOURCE #undef _FORTIFY_SOURCE -#endif +#define _FORTIFY_SOURCE 0 + #include "qemu/osdep.h" #include #include "qemu/coroutine_int.h" @@ -119,13 +119,11 @@ void finish_switch_fiber(void *fake_stack_save) /* always_inline is required to avoid TSan runtime fatal errors. */ static inline __attribute__((always_inline)) -void start_switch_fiber_asan(CoroutineAction action, void **fake_stack_save, +void start_switch_fiber_asan(void **fake_stack_save, const void *bottom, size_t size) { #ifdef CONFIG_ASAN - __sanitizer_start_switch_fiber( - action == COROUTINE_TERMINATE ? NULL : fake_stack_save, - bottom, size); + __sanitizer_start_switch_fiber(fake_stack_save, bottom, size); #endif } @@ -165,7 +163,7 @@ static void coroutine_trampoline(int i0, int i1) if (!sigsetjmp(self->env, 0)) { CoroutineUContext *leaderp = get_ptr_leader(); - start_switch_fiber_asan(COROUTINE_YIELD, &fake_stack_save, + start_switch_fiber_asan(&fake_stack_save, leaderp->stack, leaderp->stack_size); start_switch_fiber_tsan(&fake_stack_save, self, true); /* true=caller */ siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); @@ -226,8 +224,7 @@ Coroutine *qemu_coroutine_new(void) /* swapcontext() in, siglongjmp() back out */ if (!sigsetjmp(old_env, 0)) { - start_switch_fiber_asan(COROUTINE_YIELD, &fake_stack_save, co->stack, - co->stack_size); + start_switch_fiber_asan(&fake_stack_save, co->stack, co->stack_size); start_switch_fiber_tsan(&fake_stack_save, co, false); /* false=not caller */ @@ -269,10 +266,28 @@ static inline void valgrind_stack_deregister(CoroutineUContext *co) #endif #endif +#if defined(CONFIG_ASAN) && defined(CONFIG_COROUTINE_POOL) +static void coroutine_fn terminate_asan(void *opaque) +{ + CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, opaque); + + set_current(opaque); + start_switch_fiber_asan(NULL, to->stack, to->stack_size); + G_STATIC_ASSERT(!IS_ENABLED(CONFIG_TSAN)); + siglongjmp(to->env, COROUTINE_ENTER); +} +#endif + void qemu_coroutine_delete(Coroutine *co_) { CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_); +#if defined(CONFIG_ASAN) && defined(CONFIG_COROUTINE_POOL) + co_->entry_arg = qemu_coroutine_self(); + co_->entry = terminate_asan; + qemu_coroutine_switch(co_->entry_arg, co_, COROUTINE_ENTER); +#endif + #ifdef CONFIG_VALGRIND_H valgrind_stack_deregister(co); #endif @@ -305,8 +320,10 @@ qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, ret = sigsetjmp(from->env, 0); if (ret == 0) { - start_switch_fiber_asan(action, &fake_stack_save, to->stack, - to->stack_size); + start_switch_fiber_asan(IS_ENABLED(CONFIG_COROUTINE_POOL) || + action != COROUTINE_TERMINATE ? + &fake_stack_save : NULL, + to->stack, to->stack_size); start_switch_fiber_tsan(&fake_stack_save, to, false); /* false=not caller */ siglongjmp(to->env, action); diff --git a/util/coroutine-win32.c b/util/coroutine-windows.c similarity index 100% rename from util/coroutine-win32.c rename to util/coroutine-windows.c diff --git a/util/cpuinfo-aarch64.c b/util/cpuinfo-aarch64.c new file mode 100644 index 0000000000..4c8a005715 --- /dev/null +++ b/util/cpuinfo-aarch64.c @@ -0,0 +1,78 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for AArch64. + */ + +#include "qemu/osdep.h" +#include "host/cpuinfo.h" + +#ifdef CONFIG_LINUX +# ifdef CONFIG_GETAUXVAL +# include +# else +# include +# include "elf.h" +# endif +# ifndef HWCAP2_BTI +# define HWCAP2_BTI 0 /* added in glibc 2.32 */ +# endif +#endif +#ifdef CONFIG_DARWIN +# include +#endif + +unsigned cpuinfo; + +#ifdef CONFIG_DARWIN +static bool sysctl_for_bool(const char *name) +{ + int val = 0; + size_t len = sizeof(val); + + if (sysctlbyname(name, &val, &len, NULL, 0) == 0) { + return val != 0; + } + + /* + * We might in the future ask for properties not present in older kernels, + * but we're only asking about static properties, all of which should be + * 'int'. So we shouldn't see ENOMEM (val too small), or any of the other + * more exotic errors. + */ + assert(errno == ENOENT); + return false; +} +#endif + +/* Called both as constructor and (possibly) via other constructors. */ +unsigned __attribute__((constructor)) cpuinfo_init(void) +{ + unsigned info = cpuinfo; + + if (info) { + return info; + } + + info = CPUINFO_ALWAYS; + +#ifdef CONFIG_LINUX + unsigned long hwcap = qemu_getauxval(AT_HWCAP); + info |= (hwcap & HWCAP_ATOMICS ? CPUINFO_LSE : 0); + info |= (hwcap & HWCAP_USCAT ? CPUINFO_LSE2 : 0); + info |= (hwcap & HWCAP_AES ? CPUINFO_AES : 0); + info |= (hwcap & HWCAP_PMULL ? CPUINFO_PMULL : 0); + + unsigned long hwcap2 = qemu_getauxval(AT_HWCAP2); + info |= (hwcap2 & HWCAP2_BTI ? CPUINFO_BTI : 0); +#endif +#ifdef CONFIG_DARWIN + info |= sysctl_for_bool("hw.optional.arm.FEAT_LSE") * CPUINFO_LSE; + info |= sysctl_for_bool("hw.optional.arm.FEAT_LSE2") * CPUINFO_LSE2; + info |= sysctl_for_bool("hw.optional.arm.FEAT_AES") * CPUINFO_AES; + info |= sysctl_for_bool("hw.optional.arm.FEAT_PMULL") * CPUINFO_PMULL; + info |= sysctl_for_bool("hw.optional.arm.FEAT_BTI") * CPUINFO_BTI; +#endif + + cpuinfo = info; + return info; +} diff --git a/util/cpuinfo-i386.c b/util/cpuinfo-i386.c new file mode 100644 index 0000000000..9fddb18303 --- /dev/null +++ b/util/cpuinfo-i386.c @@ -0,0 +1,103 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for x86. + */ + +#include "qemu/osdep.h" +#include "host/cpuinfo.h" +#ifdef CONFIG_CPUID_H +# include "qemu/cpuid.h" +#endif + +unsigned cpuinfo; + +/* Called both as constructor and (possibly) via other constructors. */ +unsigned __attribute__((constructor)) cpuinfo_init(void) +{ + unsigned info = cpuinfo; + + if (info) { + return info; + } + +#ifdef CONFIG_CPUID_H + unsigned max, a, b, c, d, b7 = 0, c7 = 0; + + max = __get_cpuid_max(0, 0); + + if (max >= 7) { + __cpuid_count(7, 0, a, b7, c7, d); + info |= (b7 & bit_BMI ? CPUINFO_BMI1 : 0); + info |= (b7 & bit_BMI2 ? CPUINFO_BMI2 : 0); + } + + if (max >= 1) { + __cpuid(1, a, b, c, d); + + info |= (d & bit_CMOV ? CPUINFO_CMOV : 0); + info |= (d & bit_SSE2 ? CPUINFO_SSE2 : 0); + info |= (c & bit_SSE4_1 ? CPUINFO_SSE4 : 0); + info |= (c & bit_MOVBE ? CPUINFO_MOVBE : 0); + info |= (c & bit_POPCNT ? CPUINFO_POPCNT : 0); + info |= (c & bit_PCLMUL ? CPUINFO_PCLMUL : 0); + + /* Our AES support requires PSHUFB as well. */ + info |= ((c & bit_AES) && (c & bit_SSSE3) ? CPUINFO_AES : 0); + + /* For AVX features, we must check available and usable. */ + if ((c & bit_AVX) && (c & bit_OSXSAVE)) { + unsigned bv = xgetbv_low(0); + + if ((bv & 6) == 6) { + info |= CPUINFO_AVX1; + info |= (b7 & bit_AVX2 ? CPUINFO_AVX2 : 0); + + if ((bv & 0xe0) == 0xe0) { + info |= (b7 & bit_AVX512F ? CPUINFO_AVX512F : 0); + info |= (b7 & bit_AVX512VL ? CPUINFO_AVX512VL : 0); + info |= (b7 & bit_AVX512BW ? CPUINFO_AVX512BW : 0); + info |= (b7 & bit_AVX512DQ ? CPUINFO_AVX512DQ : 0); + info |= (c7 & bit_AVX512VBMI2 ? CPUINFO_AVX512VBMI2 : 0); + } + + /* + * The Intel SDM has added: + * Processors that enumerate support for Intel® AVX + * (by setting the feature flag CPUID.01H:ECX.AVX[bit 28]) + * guarantee that the 16-byte memory operations performed + * by the following instructions will always be carried + * out atomically: + * - MOVAPD, MOVAPS, and MOVDQA. + * - VMOVAPD, VMOVAPS, and VMOVDQA when encoded with VEX.128. + * - VMOVAPD, VMOVAPS, VMOVDQA32, and VMOVDQA64 when encoded + * with EVEX.128 and k0 (masking disabled). + * Note that these instructions require the linear addresses + * of their memory operands to be 16-byte aligned. + * + * AMD has provided an even stronger guarantee that processors + * with AVX provide 16-byte atomicity for all cacheable, + * naturally aligned single loads and stores, e.g. MOVDQU. + * + * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104688 + */ + __cpuid(0, a, b, c, d); + if (c == signature_INTEL_ecx) { + info |= CPUINFO_ATOMIC_VMOVDQA; + } else if (c == signature_AMD_ecx) { + info |= CPUINFO_ATOMIC_VMOVDQA | CPUINFO_ATOMIC_VMOVDQU; + } + } + } + } + + max = __get_cpuid_max(0x8000000, 0); + if (max >= 1) { + __cpuid(0x80000001, a, b, c, d); + info |= (c & bit_LZCNT ? CPUINFO_LZCNT : 0); + } +#endif + + info |= CPUINFO_ALWAYS; + cpuinfo = info; + return info; +} diff --git a/util/cpuinfo-loongarch.c b/util/cpuinfo-loongarch.c new file mode 100644 index 0000000000..08b6d7460c --- /dev/null +++ b/util/cpuinfo-loongarch.c @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for LoongArch. + */ + +#include "qemu/osdep.h" +#include "host/cpuinfo.h" + +#ifdef CONFIG_GETAUXVAL +# include +#else +# include "elf.h" +#endif +#include + +unsigned cpuinfo; + +/* Called both as constructor and (possibly) via other constructors. */ +unsigned __attribute__((constructor)) cpuinfo_init(void) +{ + unsigned info = cpuinfo; + unsigned long hwcap; + + if (info) { + return info; + } + + hwcap = qemu_getauxval(AT_HWCAP); + + info = CPUINFO_ALWAYS; + info |= (hwcap & HWCAP_LOONGARCH_LSX ? CPUINFO_LSX : 0); + + cpuinfo = info; + return info; +} diff --git a/util/cpuinfo-ppc.c b/util/cpuinfo-ppc.c new file mode 100644 index 0000000000..b2d8893a06 --- /dev/null +++ b/util/cpuinfo-ppc.c @@ -0,0 +1,64 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for ppc. + */ + +#include "qemu/osdep.h" +#include "host/cpuinfo.h" + +#include +#ifdef CONFIG_GETAUXVAL +# include +#else +# include "elf.h" +#endif + +unsigned cpuinfo; + +/* Called both as constructor and (possibly) via other constructors. */ +unsigned __attribute__((constructor)) cpuinfo_init(void) +{ + unsigned info = cpuinfo; + unsigned long hwcap, hwcap2; + + if (info) { + return info; + } + + hwcap = qemu_getauxval(AT_HWCAP); + hwcap2 = qemu_getauxval(AT_HWCAP2); + info = CPUINFO_ALWAYS; + + /* Version numbers are monotonic, and so imply all lower versions. */ + if (hwcap2 & PPC_FEATURE2_ARCH_3_1) { + info |= CPUINFO_V3_1 | CPUINFO_V3_0 | CPUINFO_V2_07 | CPUINFO_V2_06; + } else if (hwcap2 & PPC_FEATURE2_ARCH_3_00) { + info |= CPUINFO_V3_0 | CPUINFO_V2_07 | CPUINFO_V2_06; + } else if (hwcap2 & PPC_FEATURE2_ARCH_2_07) { + info |= CPUINFO_V2_07 | CPUINFO_V2_06; + } else if (hwcap & PPC_FEATURE_ARCH_2_06) { + info |= CPUINFO_V2_06; + } + + if (hwcap2 & PPC_FEATURE2_ISEL) { + info |= CPUINFO_ISEL; + } + if (hwcap & PPC_FEATURE_HAS_ALTIVEC) { + info |= CPUINFO_ALTIVEC; + /* We only care about the portion of VSX that overlaps Altivec. */ + if (hwcap & PPC_FEATURE_HAS_VSX) { + info |= CPUINFO_VSX; + /* + * We use VSX especially for little-endian, but we should + * always have both anyway, since VSX came with Power7 + * and crypto came with Power8. + */ + if (hwcap2 & PPC_FEATURE2_VEC_CRYPTO) { + info |= CPUINFO_CRYPTO; + } + } + } + + cpuinfo = info; + return info; +} diff --git a/util/crc32c.c b/util/crc32c.c index 762657d853..ea7f345de8 100644 --- a/util/crc32c.c +++ b/util/crc32c.c @@ -113,3 +113,11 @@ uint32_t crc32c(uint32_t crc, const uint8_t *data, unsigned int length) return crc^0xffffffff; } +uint32_t iov_crc32c(uint32_t crc, const struct iovec *iov, size_t iov_cnt) +{ + while (iov_cnt--) { + crc = crc32c(crc, iov->iov_base, iov->iov_len) ^ 0xffffffff; + iov++; + } + return crc ^ 0xffffffff; +} diff --git a/util/cutils.c b/util/cutils.c index a58bcfd80e..42364039a5 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -35,6 +35,19 @@ #include #endif +#ifdef __HAIKU__ +#include +#endif + +#ifdef __APPLE__ +#include +#endif + +#ifdef G_OS_WIN32 +#include +#include +#endif + #include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/error-report.h" @@ -181,88 +194,124 @@ static int64_t suffix_mul(char suffix, int64_t unit) * - 12345 - decimal, scale determined by @default_suffix and @unit * - 12345{bBkKmMgGtTpPeE} - decimal, scale determined by suffix and @unit * - 12345.678{kKmMgGtTpPeE} - decimal, scale determined by suffix, and - * fractional portion is truncated to byte + * fractional portion is truncated to byte, either side of . may be empty * - 0x7fEE - hexadecimal, unit determined by @default_suffix * - * The following cause a deprecation warning, and may be removed in the future - * - 0xabc{kKmMgGtTpP} - hex with scaling suffix - * * The following are intentionally not supported - * - octal, such as 08 - * - fractional hex, such as 0x1.8 - * - floating point exponents, such as 1e3 + * - hex with scaling suffix, such as 0x20M or 0x1p3 (both fail with + * -EINVAL), while 0x1b is 27 (not 1 with byte scale) + * - octal, such as 08 (parsed as decimal instead) + * - binary, such as 0b1000 (parsed as 0b with trailing garbage "1000") + * - fractional hex, such as 0x1.8 (parsed as 0 with trailing garbage "x1.8") + * - negative values, including -0 (fail with -ERANGE) + * - floating point exponents, such as 1e3 (parsed as 1e with trailing + * garbage "3") or 0x1p3 (rejected as hex with scaling suffix) + * - non-finite values, such as inf or NaN (fail with -EINVAL) * * The end pointer will be returned in *end, if not NULL. If there is * no fraction, the input can be decimal or hexadecimal; if there is a - * fraction, then the input must be decimal and there must be a suffix - * (possibly by @default_suffix) larger than Byte, and the fractional - * portion may suffer from precision loss or rounding. The input must - * be positive. + * non-zero fraction, then the input must be decimal and there must be + * a suffix (possibly by @default_suffix) larger than Byte, and the + * fractional portion may suffer from precision loss or rounding. The + * input must be positive. * * Return -ERANGE on overflow (with *@end advanced), and -EINVAL on - * other error (with *@end left unchanged). + * other error (with *@end at @nptr). Unlike strtoull, *@result is + * set to 0 on all errors, as returning UINT64_MAX on overflow is less + * likely to be usable as a size. */ static int do_strtosz(const char *nptr, const char **end, const char default_suffix, int64_t unit, uint64_t *result) { int retval; - const char *endptr, *f; + const char *endptr; unsigned char c; - bool hex = false; - uint64_t val, valf = 0; + uint64_t val = 0, valf = 0; int64_t mul; /* Parse integral portion as decimal. */ - retval = qemu_strtou64(nptr, &endptr, 10, &val); - if (retval) { + retval = parse_uint(nptr, &endptr, 10, &val); + if (retval == -ERANGE || !nptr) { goto out; } - if (memchr(nptr, '-', endptr - nptr) != NULL) { - endptr = nptr; - retval = -EINVAL; - goto out; - } - if (val == 0 && (*endptr == 'x' || *endptr == 'X')) { - /* Input looks like hex, reparse, and insist on no fraction. */ + if (retval == 0 && val == 0 && (*endptr == 'x' || *endptr == 'X')) { + /* Input looks like hex; reparse, and insist on no fraction or suffix. */ retval = qemu_strtou64(nptr, &endptr, 16, &val); if (retval) { goto out; } - if (*endptr == '.') { + if (*endptr == '.' || suffix_mul(*endptr, unit) > 0) { endptr = nptr; retval = -EINVAL; goto out; } - hex = true; - } else if (*endptr == '.') { + } else if (*endptr == '.' || (endptr == nptr && strchr(nptr, '.'))) { /* * Input looks like a fraction. Make sure even 1.k works - * without fractional digits. If we see an exponent, treat - * the entire input as invalid instead. + * without fractional digits. strtod tries to treat 'e' as an + * exponent, but we want to treat it as a scaling suffix; + * doing this requires modifying a copy of the fraction. */ - double fraction; + double fraction = 0.0; - f = endptr; - retval = qemu_strtod_finite(f, &endptr, &fraction); - if (retval) { + if (retval == 0 && *endptr == '.' && !isdigit(endptr[1])) { + /* If we got here, we parsed at least one digit already. */ endptr++; - } else if (memchr(f, 'e', endptr - f) || memchr(f, 'E', endptr - f)) { - endptr = nptr; - retval = -EINVAL; - goto out; } else { - /* Extract into a 64-bit fixed-point fraction. */ - valf = (uint64_t)(fraction * 0x1p64); + char *e; + const char *tail; + g_autofree char *copy = g_strdup(endptr); + + e = strchr(copy, 'e'); + if (e) { + *e = '\0'; + } + e = strchr(copy, 'E'); + if (e) { + *e = '\0'; + } + /* + * If this is a floating point, we are guaranteed that '.' + * appears before any possible digits in copy. If it is + * not a floating point, strtod will fail. Either way, + * there is now no exponent in copy, so if it parses, we + * know 0.0 <= abs(result) <= 1.0 (after rounding), and + * ERANGE is only possible on underflow which is okay. + */ + retval = qemu_strtod_finite(copy, &tail, &fraction); + endptr += tail - copy; + if (signbit(fraction)) { + retval = -ERANGE; + goto out; + } } + + /* Extract into a 64-bit fixed-point fraction. */ + if (fraction == 1.0) { + if (val == UINT64_MAX) { + retval = -ERANGE; + goto out; + } + val++; + } else if (retval == -ERANGE) { + /* See comments above about underflow */ + valf = 1; + retval = 0; + } else { + /* We want non-zero valf for any non-zero fraction */ + valf = (uint64_t)(fraction * 0x1p64); + if (valf == 0 && fraction > 0.0) { + valf = 1; + } + } + } + if (retval) { + goto out; } c = *endptr; mul = suffix_mul(c, unit); if (mul > 0) { - if (hex) { - warn_report("Using a multiplier suffix on hex numbers " - "is deprecated: %s", nptr); - } endptr++; } else { mul = suffix_mul(default_suffix, unit); @@ -301,11 +350,16 @@ static int do_strtosz(const char *nptr, const char **end, out: if (end) { *end = endptr; - } else if (*endptr) { + } else if (nptr && *endptr) { retval = -EINVAL; } if (retval == 0) { *result = val; + } else { + *result = 0; + if (end && retval == -EINVAL) { + *end = nptr; + } } return retval; @@ -372,12 +426,13 @@ static int check_strtox_error(const char *nptr, char *ep, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, 0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows @result, store INT_MAX in @result, * and return -ERANGE. @@ -386,6 +441,9 @@ static int check_strtox_error(const char *nptr, char *ep, * and return -ERANGE. * * Else store the converted value in @result, and return zero. + * + * This matches the behavior of strtol() on 32-bit platforms, even on + * platforms where long is 64-bits. */ int qemu_strtoi(const char *nptr, const char **endptr, int base, int *result) @@ -395,6 +453,7 @@ int qemu_strtoi(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -424,12 +483,13 @@ int qemu_strtoi(const char *nptr, const char **endptr, int base, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, 0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows @result, store UINT_MAX in @result, * and return -ERANGE. @@ -438,16 +498,19 @@ int qemu_strtoi(const char *nptr, const char **endptr, int base, * * Note that a number with a leading minus sign gets converted without * the minus sign, checked for overflow (see above), then negated (in - * @result's type). This is exactly how strtoul() works. + * @result's type). This matches the behavior of strtoul() on 32-bit + * platforms, even on platforms where long is 64-bits. */ int qemu_strtoui(const char *nptr, const char **endptr, int base, unsigned int *result) { char *ep; - long long lresult; + unsigned long long lresult; + bool neg; assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -461,14 +524,22 @@ int qemu_strtoui(const char *nptr, const char **endptr, int base, if (errno == ERANGE) { *result = -1; } else { + /* + * Note that platforms with 32-bit strtoul only accept input + * in the range [-4294967295, 4294967295]; but we used 64-bit + * strtoull which wraps -18446744073709551615 to 1 instead of + * declaring overflow. So we must check if '-' was parsed, + * and if so, undo the negation before doing our bounds check. + */ + neg = memchr(nptr, '-', ep - nptr) != NULL; + if (neg) { + lresult = -lresult; + } if (lresult > UINT_MAX) { *result = UINT_MAX; errno = ERANGE; - } else if (lresult < INT_MIN) { - *result = UINT_MAX; - errno = ERANGE; } else { - *result = lresult; + *result = neg ? -lresult : lresult; } } return check_strtox_error(nptr, ep, endptr, lresult == 0, errno); @@ -483,12 +554,13 @@ int qemu_strtoui(const char *nptr, const char **endptr, int base, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, 0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows @result, store LONG_MAX in @result, * and return -ERANGE. @@ -505,6 +577,7 @@ int qemu_strtol(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -525,12 +598,13 @@ int qemu_strtol(const char *nptr, const char **endptr, int base, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, 0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows @result, store ULONG_MAX in @result, * and return -ERANGE. @@ -548,6 +622,7 @@ int qemu_strtoul(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -576,6 +651,7 @@ int qemu_strtoi64(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -593,6 +669,8 @@ int qemu_strtoi64(const char *nptr, const char **endptr, int base, * Convert string @nptr to an uint64_t. * * Works like qemu_strtoul(), except it stores UINT64_MAX on overflow. + * (If you want to prohibit negative numbers that wrap around to + * positive, use parse_uint()). */ int qemu_strtou64(const char *nptr, const char **endptr, int base, uint64_t *result) @@ -601,6 +679,7 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -627,12 +706,13 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, +0.0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows, store +/-HUGE_VAL in @result, depending * on the sign, and return -ERANGE. @@ -647,6 +727,7 @@ int qemu_strtod(const char *nptr, const char **endptr, double *result) char *ep; if (!nptr) { + *result = 0.0; if (endptr) { *endptr = nptr; } @@ -661,24 +742,28 @@ int qemu_strtod(const char *nptr, const char **endptr, double *result) /** * Convert string @nptr to a finite double. * - * Works like qemu_strtod(), except that "NaN" and "inf" are rejected - * with -EINVAL and no conversion is performed. + * Works like qemu_strtod(), except that "NaN", "inf", and strings + * that cause ERANGE overflow errors are rejected with -EINVAL as if + * no conversion is performed, storing 0.0 into @result regardless of + * any sign. -ERANGE failures for underflow still preserve the parsed + * sign. */ int qemu_strtod_finite(const char *nptr, const char **endptr, double *result) { - double tmp; + const char *tmp; int ret; - ret = qemu_strtod(nptr, endptr, &tmp); - if (!ret && !isfinite(tmp)) { + ret = qemu_strtod(nptr, &tmp, result); + if (!isfinite(*result)) { if (endptr) { *endptr = nptr; } + *result = 0.0; + ret = -EINVAL; + } else if (endptr) { + *endptr = tmp; + } else if (*tmp) { ret = -EINVAL; - } - - if (ret != -EINVAL) { - *result = tmp; } return ret; } @@ -702,32 +787,33 @@ const char *qemu_strchrnul(const char *s, int c) * parse_uint: * * @s: String to parse - * @value: Destination for parsed integer value * @endptr: Destination for pointer to first character not consumed * @base: integer base, between 2 and 36 inclusive, or 0 + * @value: Destination for parsed integer value * * Parse unsigned integer * * Parsed syntax is like strtoull()'s: arbitrary whitespace, a single optional * '+' or '-', an optional "0x" if @base is 0 or 16, one or more digits. * - * If @s is null, or @base is invalid, or @s doesn't start with an - * integer in the syntax above, set *@value to 0, *@endptr to @s, and - * return -EINVAL. + * If @s is null, or @s doesn't start with an integer in the syntax + * above, set *@value to 0, *@endptr to @s, and return -EINVAL. * * Set *@endptr to point right beyond the parsed integer (even if the integer * overflows or is negative, all digits will be parsed and *@endptr will - * point right beyond them). + * point right beyond them). If @endptr is %NULL, any trailing character + * instead causes a result of -EINVAL with *@value of 0. * * If the integer is negative, set *@value to 0, and return -ERANGE. + * (If you want to allow negative numbers that wrap around within + * bounds, use qemu_strtou64()). * * If the integer overflows unsigned long long, set *@value to * ULLONG_MAX, and return -ERANGE. * * Else, set *@value to the parsed integer, and return 0. */ -int parse_uint(const char *s, unsigned long long *value, char **endptr, - int base) +int parse_uint(const char *s, const char **endptr, int base, uint64_t *value) { int r = 0; char *endp = (char *)s; @@ -763,7 +849,12 @@ int parse_uint(const char *s, unsigned long long *value, char **endptr, out: *value = val; - *endptr = endp; + if (endptr) { + *endptr = endp; + } else if (s && *endp) { + r = -EINVAL; + *value = 0; + } return r; } @@ -771,31 +862,16 @@ out: * parse_uint_full: * * @s: String to parse - * @value: Destination for parsed integer value * @base: integer base, between 2 and 36 inclusive, or 0 + * @value: Destination for parsed integer value * - * Parse unsigned integer from entire string + * Parse unsigned integer from entire string, rejecting any trailing slop. * - * Have the same behavior of parse_uint(), but with an additional check - * for additional data after the parsed number. If extra characters are present - * after the parsed number, the function will return -EINVAL, and *@v will - * be set to 0. + * Shorthand for parse_uint(s, NULL, base, value). */ -int parse_uint_full(const char *s, unsigned long long *value, int base) +int parse_uint_full(const char *s, int base, uint64_t *value) { - char *endp; - int r; - - r = parse_uint(s, value, &endp, base); - if (r < 0) { - return r; - } - if (*endp) { - *value = 0; - return -EINVAL; - } - - return 0; + return parse_uint(s, NULL, base, value); } int qemu_parse_fd(const char *param) @@ -872,6 +948,25 @@ int parse_debug_env(const char *name, int max, int initial) return debug; } +const char *si_prefix(unsigned int exp10) +{ + static const char *prefixes[] = { + "a", "f", "p", "n", "u", "m", "", "K", "M", "G", "T", "P", "E" + }; + + exp10 += 18; + assert(exp10 % 3 == 0 && exp10 / 3 < ARRAY_SIZE(prefixes)); + return prefixes[exp10 / 3]; +} + +const char *iec_binary_prefix(unsigned int exp2) +{ + static const char *prefixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; + + assert(exp2 % 10 == 0 && exp2 / 10 < ARRAY_SIZE(prefixes)); + return prefixes[exp2 / 10]; +} + /* * Return human readable string for size @val. * @val can be anything that uint64_t allows (no more than "16 EiB"). @@ -880,7 +975,6 @@ int parse_debug_env(const char *name, int max, int initial) */ char *size_to_str(uint64_t val) { - static const char *suffixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; uint64_t div; int i; @@ -891,25 +985,23 @@ char *size_to_str(uint64_t val) * (see e41b509d68afb1f for more info) */ frexp(val / (1000.0 / 1024.0), &i); - i = (i - 1) / 10; - div = 1ULL << (i * 10); + i = (i - 1) / 10 * 10; + div = 1ULL << i; - return g_strdup_printf("%0.3g %sB", (double)val / div, suffixes[i]); + return g_strdup_printf("%0.3g %sB", (double)val / div, iec_binary_prefix(i)); } char *freq_to_str(uint64_t freq_hz) { - static const char *const suffixes[] = { "", "K", "M", "G", "T", "P", "E" }; double freq = freq_hz; - size_t idx = 0; + size_t exp10 = 0; while (freq >= 1000.0) { freq /= 1000.0; - idx++; + exp10 += 3; } - assert(idx < ARRAY_SIZE(suffixes)); - return g_strdup_printf("%0.3g %sHz", freq, suffixes[idx]); + return g_strdup_printf("%0.3g %sHz", freq, si_prefix(exp10)); } int qemu_pstrcmp0(const char **str1, const char **str2) @@ -920,8 +1012,17 @@ int qemu_pstrcmp0(const char **str1, const char **str2) static inline bool starts_with_prefix(const char *dir) { size_t prefix_len = strlen(CONFIG_PREFIX); + /* + * dir[prefix_len] is only accessed if the length of dir is + * >= prefix_len, so no out of bounds access is possible. + */ +#pragma GCC diagnostic push +#if !defined(__clang__) || __has_warning("-Warray-bounds=") +#pragma GCC diagnostic ignored "-Warray-bounds=" +#endif return !memcmp(dir, CONFIG_PREFIX, prefix_len) && (!dir[prefix_len] || G_IS_DIR_SEPARATOR(dir[prefix_len])); +#pragma GCC diagnostic pop } /* Return the next path component in dir, and store its length in *p_len. */ @@ -1052,37 +1153,66 @@ char *get_relocated_path(const char *dir) { size_t prefix_len = strlen(CONFIG_PREFIX); const char *bindir = CONFIG_BINDIR; - const char *exec_dir = qemu_get_exec_dir(); GString *result; int len_dir, len_bindir; /* Fail if qemu_init_exec_dir was not called. */ assert(exec_dir[0]); - if (!starts_with_prefix(dir) || !starts_with_prefix(bindir)) { - return g_strdup(dir); - } result = g_string_new(exec_dir); + g_string_append(result, "/qemu-bundle"); + if (access(result->str, R_OK) == 0) { +#ifdef G_OS_WIN32 + const char *src = dir; + size_t size = mbsrtowcs(NULL, &src, 0, &(mbstate_t){0}) + 1; + PWSTR wdir = g_new(WCHAR, size); + mbsrtowcs(wdir, &src, size, &(mbstate_t){0}); - /* Advance over common components. */ - len_dir = len_bindir = prefix_len; - do { - dir += len_dir; - bindir += len_bindir; - dir = next_component(dir, &len_dir); - bindir = next_component(bindir, &len_bindir); - } while (len_dir && len_dir == len_bindir && !memcmp(dir, bindir, len_dir)); + PCWSTR wdir_skipped_root; + if (PathCchSkipRoot(wdir, &wdir_skipped_root) == S_OK) { + size = wcsrtombs(NULL, &wdir_skipped_root, 0, &(mbstate_t){0}); + char *cursor = result->str + result->len; + g_string_set_size(result, result->len + size); + wcsrtombs(cursor, &wdir_skipped_root, size + 1, &(mbstate_t){0}); + } else { + g_string_append(result, dir); + } - /* Ascend from bindir to the common prefix with dir. */ - while (len_bindir) { - bindir += len_bindir; - g_string_append(result, "/.."); - bindir = next_component(bindir, &len_bindir); + g_free(wdir); +#else + g_string_append(result, dir); +#endif + goto out; } - if (*dir) { - assert(G_IS_DIR_SEPARATOR(dir[-1])); - g_string_append(result, dir - 1); + if (IS_ENABLED(CONFIG_RELOCATABLE) && + starts_with_prefix(dir) && starts_with_prefix(bindir)) { + g_string_assign(result, exec_dir); + + /* Advance over common components. */ + len_dir = len_bindir = prefix_len; + do { + dir += len_dir; + bindir += len_bindir; + dir = next_component(dir, &len_dir); + bindir = next_component(bindir, &len_bindir); + } while (len_dir && len_dir == len_bindir && !memcmp(dir, bindir, len_dir)); + + /* Ascend from bindir to the common prefix with dir. */ + while (len_bindir) { + bindir += len_bindir; + g_string_append(result, "/.."); + bindir = next_component(bindir, &len_bindir); + } + + if (*dir) { + assert(G_IS_DIR_SEPARATOR(dir[-1])); + g_string_append(result, dir - 1); + } + goto out; } + + g_string_assign(result, dir); +out: return g_string_free(result, false); } diff --git a/util/defer-call.c b/util/defer-call.c new file mode 100644 index 0000000000..037dc0abf0 --- /dev/null +++ b/util/defer-call.c @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Deferred calls + * + * Copyright Red Hat. + * + * This API defers a function call within a defer_call_begin()/defer_call_end() + * section, allowing multiple calls to batch up. This is a performance + * optimization that is used in the block layer to submit several I/O requests + * at once instead of individually: + * + * defer_call_begin(); <-- start of section + * ... + * defer_call(my_func, my_obj); <-- deferred my_func(my_obj) call + * defer_call(my_func, my_obj); <-- another + * defer_call(my_func, my_obj); <-- another + * ... + * defer_call_end(); <-- end of section, my_func(my_obj) is called once + */ + +#include "qemu/osdep.h" +#include "qemu/coroutine-tls.h" +#include "qemu/notify.h" +#include "qemu/thread.h" +#include "qemu/defer-call.h" + +/* A function call that has been deferred until defer_call_end() */ +typedef struct { + void (*fn)(void *); + void *opaque; +} DeferredCall; + +/* Per-thread state */ +typedef struct { + unsigned nesting_level; + GArray *deferred_call_array; +} DeferCallThreadState; + +/* Use get_ptr_defer_call_thread_state() to fetch this thread-local value */ +QEMU_DEFINE_STATIC_CO_TLS(DeferCallThreadState, defer_call_thread_state); + +/* Called at thread cleanup time */ +static void defer_call_atexit(Notifier *n, void *value) +{ + DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); + g_array_free(thread_state->deferred_call_array, TRUE); +} + +/* This won't involve coroutines, so use __thread */ +static __thread Notifier defer_call_atexit_notifier; + +/** + * defer_call: + * @fn: a function pointer to be invoked + * @opaque: a user-defined argument to @fn() + * + * Call @fn(@opaque) immediately if not within a + * defer_call_begin()/defer_call_end() section. + * + * Otherwise defer the call until the end of the outermost + * defer_call_begin()/defer_call_end() section in this thread. If the same + * @fn/@opaque pair has already been deferred, it will only be called once upon + * defer_call_end() so that accumulated calls are batched into a single call. + * + * The caller must ensure that @opaque is not freed before @fn() is invoked. + */ +void defer_call(void (*fn)(void *), void *opaque) +{ + DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); + + /* Call immediately if we're not deferring calls */ + if (thread_state->nesting_level == 0) { + fn(opaque); + return; + } + + GArray *array = thread_state->deferred_call_array; + if (!array) { + array = g_array_new(FALSE, FALSE, sizeof(DeferredCall)); + thread_state->deferred_call_array = array; + defer_call_atexit_notifier.notify = defer_call_atexit; + qemu_thread_atexit_add(&defer_call_atexit_notifier); + } + + DeferredCall *fns = (DeferredCall *)array->data; + DeferredCall new_fn = { + .fn = fn, + .opaque = opaque, + }; + + /* + * There won't be many, so do a linear search. If this becomes a bottleneck + * then a binary search (glib 2.62+) or different data structure could be + * used. + */ + for (guint i = 0; i < array->len; i++) { + if (memcmp(&fns[i], &new_fn, sizeof(new_fn)) == 0) { + return; /* already exists */ + } + } + + g_array_append_val(array, new_fn); +} + +/** + * defer_call_begin: Defer defer_call() functions until defer_call_end() + * + * defer_call_begin() and defer_call_end() are thread-local operations. The + * caller must ensure that each defer_call_begin() has a matching + * defer_call_end() in the same thread. + * + * Nesting is supported. defer_call() functions are only called at the + * outermost defer_call_end(). + */ +void defer_call_begin(void) +{ + DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); + + assert(thread_state->nesting_level < UINT32_MAX); + + thread_state->nesting_level++; +} + +/** + * defer_call_end: Run any pending defer_call() functions + * + * There must have been a matching defer_call_begin() call in the same thread + * prior to this defer_call_end() call. + */ +void defer_call_end(void) +{ + DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); + + assert(thread_state->nesting_level > 0); + + if (--thread_state->nesting_level > 0) { + return; + } + + GArray *array = thread_state->deferred_call_array; + if (!array) { + return; + } + + DeferredCall *fns = (DeferredCall *)array->data; + + for (guint i = 0; i < array->len; i++) { + fns[i].fn(fns[i].opaque); + } + + /* + * This resets the array without freeing memory so that appending is cheap + * in the future. + */ + g_array_set_size(array, 0); +} diff --git a/util/envlist.c b/util/envlist.c index ab5553498a..db937c0427 100644 --- a/util/envlist.c +++ b/util/envlist.c @@ -3,13 +3,13 @@ #include "qemu/envlist.h" struct envlist_entry { - const char *ev_var; /* actual env value */ - QLIST_ENTRY(envlist_entry) ev_link; + const char *ev_var; /* actual env value */ + QLIST_ENTRY(envlist_entry) ev_link; }; struct envlist { - QLIST_HEAD(, envlist_entry) el_entries; /* actual entries */ - size_t el_count; /* number of entries */ + QLIST_HEAD(, envlist_entry) el_entries; /* actual entries */ + size_t el_count; /* number of entries */ }; static int envlist_parse(envlist_t *envlist, @@ -21,14 +21,14 @@ static int envlist_parse(envlist_t *envlist, envlist_t * envlist_create(void) { - envlist_t *envlist; + envlist_t *envlist; - envlist = g_malloc(sizeof(*envlist)); + envlist = g_malloc(sizeof(*envlist)); - QLIST_INIT(&envlist->el_entries); - envlist->el_count = 0; + QLIST_INIT(&envlist->el_entries); + envlist->el_count = 0; - return (envlist); + return (envlist); } /* @@ -37,18 +37,18 @@ envlist_create(void) void envlist_free(envlist_t *envlist) { - struct envlist_entry *entry; + struct envlist_entry *entry; - assert(envlist != NULL); + assert(envlist != NULL); - while (envlist->el_entries.lh_first != NULL) { - entry = envlist->el_entries.lh_first; - QLIST_REMOVE(entry, ev_link); + while (envlist->el_entries.lh_first != NULL) { + entry = envlist->el_entries.lh_first; + QLIST_REMOVE(entry, ev_link); - g_free((char *)entry->ev_var); - g_free(entry); - } - g_free(envlist); + g_free((char *)entry->ev_var); + g_free(entry); + } + g_free(envlist); } /* @@ -65,7 +65,7 @@ envlist_free(envlist_t *envlist) int envlist_parse_set(envlist_t *envlist, const char *env) { - return (envlist_parse(envlist, env, &envlist_setenv)); + return (envlist_parse(envlist, env, &envlist_setenv)); } /* @@ -77,7 +77,7 @@ envlist_parse_set(envlist_t *envlist, const char *env) int envlist_parse_unset(envlist_t *envlist, const char *env) { - return (envlist_parse(envlist, env, &envlist_unsetenv)); + return (envlist_parse(envlist, env, &envlist_unsetenv)); } /* @@ -90,15 +90,15 @@ static int envlist_parse(envlist_t *envlist, const char *env, int (*callback)(envlist_t *, const char *)) { - char *tmpenv, *envvar; - char *envsave = NULL; + char *tmpenv, *envvar; + char *envsave = NULL; int ret = 0; assert(callback != NULL); - if ((envlist == NULL) || (env == NULL)) - return (EINVAL); + if ((envlist == NULL) || (env == NULL)) + return (EINVAL); - tmpenv = g_strdup(env); + tmpenv = g_strdup(env); envsave = tmpenv; do { @@ -109,7 +109,7 @@ envlist_parse(envlist_t *envlist, const char *env, if ((*callback)(envlist, tmpenv) != 0) { ret = errno; break; - } + } tmpenv = envvar + 1; } while (envvar != NULL); @@ -126,42 +126,42 @@ envlist_parse(envlist_t *envlist, const char *env, int envlist_setenv(envlist_t *envlist, const char *env) { - struct envlist_entry *entry = NULL; - const char *eq_sign; - size_t envname_len; + struct envlist_entry *entry = NULL; + const char *eq_sign; + size_t envname_len; - if ((envlist == NULL) || (env == NULL)) - return (EINVAL); + if ((envlist == NULL) || (env == NULL)) + return (EINVAL); - /* find out first equals sign in given env */ - if ((eq_sign = strchr(env, '=')) == NULL) - return (EINVAL); - envname_len = eq_sign - env + 1; + /* find out first equals sign in given env */ + if ((eq_sign = strchr(env, '=')) == NULL) + return (EINVAL); + envname_len = eq_sign - env + 1; - /* - * If there already exists variable with given name - * we remove and release it before allocating a whole - * new entry. - */ - for (entry = envlist->el_entries.lh_first; entry != NULL; - entry = entry->ev_link.le_next) { - if (strncmp(entry->ev_var, env, envname_len) == 0) - break; - } + /* + * If there already exists variable with given name + * we remove and release it before allocating a whole + * new entry. + */ + for (entry = envlist->el_entries.lh_first; entry != NULL; + entry = entry->ev_link.le_next) { + if (strncmp(entry->ev_var, env, envname_len) == 0) + break; + } - if (entry != NULL) { - QLIST_REMOVE(entry, ev_link); - g_free((char *)entry->ev_var); - g_free(entry); - } else { - envlist->el_count++; - } + if (entry != NULL) { + QLIST_REMOVE(entry, ev_link); + g_free((char *)entry->ev_var); + g_free(entry); + } else { + envlist->el_count++; + } - entry = g_malloc(sizeof(*entry)); - entry->ev_var = g_strdup(env); - QLIST_INSERT_HEAD(&envlist->el_entries, entry, ev_link); + entry = g_malloc(sizeof(*entry)); + entry->ev_var = g_strdup(env); + QLIST_INSERT_HEAD(&envlist->el_entries, entry, ev_link); - return (0); + return (0); } /* @@ -171,34 +171,34 @@ envlist_setenv(envlist_t *envlist, const char *env) int envlist_unsetenv(envlist_t *envlist, const char *env) { - struct envlist_entry *entry; - size_t envname_len; + struct envlist_entry *entry; + size_t envname_len; - if ((envlist == NULL) || (env == NULL)) - return (EINVAL); + if ((envlist == NULL) || (env == NULL)) + return (EINVAL); - /* env is not allowed to contain '=' */ - if (strchr(env, '=') != NULL) - return (EINVAL); + /* env is not allowed to contain '=' */ + if (strchr(env, '=') != NULL) + return (EINVAL); - /* - * Find out the requested entry and remove - * it from the list. - */ - envname_len = strlen(env); - for (entry = envlist->el_entries.lh_first; entry != NULL; - entry = entry->ev_link.le_next) { - if (strncmp(entry->ev_var, env, envname_len) == 0) - break; - } - if (entry != NULL) { - QLIST_REMOVE(entry, ev_link); - g_free((char *)entry->ev_var); - g_free(entry); + /* + * Find out the requested entry and remove + * it from the list. + */ + envname_len = strlen(env); + for (entry = envlist->el_entries.lh_first; entry != NULL; + entry = entry->ev_link.le_next) { + if (strncmp(entry->ev_var, env, envname_len) == 0) + break; + } + if (entry != NULL) { + QLIST_REMOVE(entry, ev_link); + g_free((char *)entry->ev_var); + g_free(entry); - envlist->el_count--; - } - return (0); + envlist->el_count--; + } + return (0); } /* @@ -214,19 +214,19 @@ envlist_unsetenv(envlist_t *envlist, const char *env) char ** envlist_to_environ(const envlist_t *envlist, size_t *count) { - struct envlist_entry *entry; - char **env, **penv; + struct envlist_entry *entry; + char **env, **penv; - penv = env = g_new(char *, envlist->el_count + 1); + penv = env = g_new(char *, envlist->el_count + 1); - for (entry = envlist->el_entries.lh_first; entry != NULL; - entry = entry->ev_link.le_next) { - *(penv++) = g_strdup(entry->ev_var); - } - *penv = NULL; /* NULL terminate the list */ + for (entry = envlist->el_entries.lh_first; entry != NULL; + entry = entry->ev_link.le_next) { + *(penv++) = g_strdup(entry->ev_var); + } + *penv = NULL; /* NULL terminate the list */ - if (count != NULL) - *count = envlist->el_count; + if (count != NULL) + *count = envlist->el_count; - return (env); + return (env); } diff --git a/util/error-report.c b/util/error-report.c index 5edb2e6040..6e44a55732 100644 --- a/util/error-report.c +++ b/util/error-report.c @@ -193,6 +193,7 @@ real_time_iso8601(void) * a single phrase, with no newline or trailing punctuation. * Prepend the current location and append a newline. */ +G_GNUC_PRINTF(2, 0) static void vreport(report_type type, const char *fmt, va_list ap) { gchar *timestr; diff --git a/util/error.c b/util/error.c index b6c89d1412..e5e247209a 100644 --- a/util/error.c +++ b/util/error.c @@ -27,8 +27,9 @@ struct Error Error *error_abort; Error *error_fatal; +Error *error_warn; -static void error_handle_fatal(Error **errp, Error *err) +static void error_handle(Error **errp, Error *err) { if (errp == &error_abort) { fprintf(stderr, "Unexpected error in %s() at %s:%d:\n", @@ -43,8 +44,16 @@ static void error_handle_fatal(Error **errp, Error *err) error_report_err(err); exit(1); } + if (errp == &error_warn) { + warn_report_err(err); + } else if (errp && !*errp) { + *errp = err; + } else { + error_free(err); + } } +G_GNUC_PRINTF(6, 0) static void error_setv(Error **errp, const char *src, int line, const char *func, ErrorClass err_class, const char *fmt, va_list ap, @@ -70,8 +79,7 @@ static void error_setv(Error **errp, err->line = line; err->func = func; - error_handle_fatal(errp, err); - *errp = err; + error_handle(errp, err); errno = saved_errno; } @@ -283,12 +291,7 @@ void error_propagate(Error **dst_errp, Error *local_err) if (!local_err) { return; } - error_handle_fatal(dst_errp, local_err); - if (dst_errp && !*dst_errp) { - *dst_errp = local_err; - } else { - error_free(local_err); - } + error_handle(dst_errp, local_err); } void error_propagate_prepend(Error **dst_errp, Error *err, diff --git a/util/fdmon-epoll.c b/util/fdmon-epoll.c index e11a8a022e..c6413cb18f 100644 --- a/util/fdmon-epoll.c +++ b/util/fdmon-epoll.c @@ -64,11 +64,6 @@ static int fdmon_epoll_wait(AioContext *ctx, AioHandlerList *ready_list, int i, ret = 0; struct epoll_event events[128]; - /* Fall back while external clients are disabled */ - if (qatomic_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) { @@ -127,23 +122,29 @@ static bool fdmon_epoll_try_enable(AioContext *ctx) bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd) { + bool ok; + if (ctx->epollfd < 0) { return false; } - /* Do not upgrade while external clients are disabled */ - if (qatomic_read(&ctx->external_disable_cnt)) { + if (npfd < EPOLL_ENABLE_THRESHOLD) { return false; } - if (npfd >= EPOLL_ENABLE_THRESHOLD) { - if (fdmon_epoll_try_enable(ctx)) { - return true; - } else { - fdmon_epoll_disable(ctx); - } + /* The list must not change while we add fds to epoll */ + if (!qemu_lockcnt_dec_if_lock(&ctx->list_lock)) { + return false; } - return false; + + ok = fdmon_epoll_try_enable(ctx); + + qemu_lockcnt_inc_and_unlock(&ctx->list_lock); + + if (!ok) { + fdmon_epoll_disable(ctx); + } + return ok; } void fdmon_epoll_setup(AioContext *ctx) diff --git a/util/fdmon-io_uring.c b/util/fdmon-io_uring.c index ab43052dd7..b0d68bdc44 100644 --- a/util/fdmon-io_uring.c +++ b/util/fdmon-io_uring.c @@ -180,10 +180,11 @@ static void add_poll_remove_sqe(AioContext *ctx, AioHandler *node) struct io_uring_sqe *sqe = get_sqe(ctx); #ifdef LIBURING_HAVE_DATA64 - io_uring_prep_poll_remove(sqe, (__u64)(uintptr_t)node); + io_uring_prep_poll_remove(sqe, (uintptr_t)node); #else io_uring_prep_poll_remove(sqe, node); #endif + io_uring_sqe_set_data(sqe, NULL); } /* Add a timeout that self-cancels when another cqe becomes ready */ @@ -197,6 +198,7 @@ static void add_timeout_sqe(AioContext *ctx, int64_t ns) sqe = get_sqe(ctx); io_uring_prep_timeout(sqe, &ts, 1, 0); + io_uring_sqe_set_data(sqe, NULL); } /* Add sqes from ctx->submit_list for submission */ @@ -276,11 +278,6 @@ static int fdmon_io_uring_wait(AioContext *ctx, AioHandlerList *ready_list, unsigned wait_nr = 1; /* block until at least one cqe is ready */ int ret; - /* Fall back while external clients are disabled */ - if (qatomic_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) { @@ -315,8 +312,7 @@ static bool fdmon_io_uring_need_wait(AioContext *ctx) return true; } - /* Are we falling back to fdmon-poll? */ - return qatomic_read(&ctx->external_disable_cnt); + return false; } static const FDMonOps fdmon_io_uring_ops = { diff --git a/util/fdmon-poll.c b/util/fdmon-poll.c index 5fe3b47865..17df917cf9 100644 --- a/util/fdmon-poll.c +++ b/util/fdmon-poll.c @@ -65,8 +65,7 @@ static int fdmon_poll_wait(AioContext *ctx, AioHandlerList *ready_list, 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)) { + if (!QLIST_IS_INSERTED(node, node_deleted) && node->pfd.events) { add_pollfd(node); } } diff --git a/util/fifo8.c b/util/fifo8.c index d4d1c135e0..4e01b532d9 100644 --- a/util/fifo8.c +++ b/util/fifo8.c @@ -66,19 +66,37 @@ uint8_t fifo8_pop(Fifo8 *fifo) return ret; } -const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num) +static const uint8_t *fifo8_peekpop_buf(Fifo8 *fifo, uint32_t max, + uint32_t *numptr, bool do_pop) { uint8_t *ret; + uint32_t num; assert(max > 0 && max <= fifo->num); - *num = MIN(fifo->capacity - fifo->head, max); + num = MIN(fifo->capacity - fifo->head, max); ret = &fifo->data[fifo->head]; - fifo->head += *num; - fifo->head %= fifo->capacity; - fifo->num -= *num; + + if (do_pop) { + fifo->head += num; + fifo->head %= fifo->capacity; + fifo->num -= num; + } + if (numptr) { + *numptr = num; + } return ret; } +const uint8_t *fifo8_peek_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr) +{ + return fifo8_peekpop_buf(fifo, max, numptr, false); +} + +const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr) +{ + return fifo8_peekpop_buf(fifo, max, numptr, true); +} + void fifo8_reset(Fifo8 *fifo) { fifo->num = 0; @@ -109,7 +127,7 @@ const VMStateDescription vmstate_fifo8 = { .name = "Fifo8", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VBUFFER_UINT32(data, Fifo8, 1, NULL, capacity), VMSTATE_UINT32(head, Fifo8), VMSTATE_UINT32(num, Fifo8), diff --git a/util/filemonitor-inotify.c b/util/filemonitor-inotify.c index 2c45f7f176..7352b9fe53 100644 --- a/util/filemonitor-inotify.c +++ b/util/filemonitor-inotify.c @@ -81,16 +81,25 @@ static void qemu_file_monitor_watch(void *arg) /* Loop over all events in the buffer */ while (used < len) { - struct inotify_event *ev = - (struct inotify_event *)(buf + used); - const char *name = ev->len ? ev->name : ""; - QFileMonitorDir *dir = g_hash_table_lookup(mon->idmap, - GINT_TO_POINTER(ev->wd)); - uint32_t iev = ev->mask & - (IN_CREATE | IN_MODIFY | IN_DELETE | IN_IGNORED | - IN_MOVED_TO | IN_MOVED_FROM | IN_ATTRIB); + const char *name; + QFileMonitorDir *dir; + uint32_t iev; int qev; gsize i; + struct inotify_event *ev = (struct inotify_event *)(buf + used); + + /* + * We trust the kernel to provide valid buffer with complete event + * records. + */ + assert(len - used >= sizeof(struct inotify_event)); + assert(len - used - sizeof(struct inotify_event) >= ev->len); + + name = ev->len ? ev->name : ""; + dir = g_hash_table_lookup(mon->idmap, GINT_TO_POINTER(ev->wd)); + iev = ev->mask & + (IN_CREATE | IN_MODIFY | IN_DELETE | IN_IGNORED | + IN_MOVED_TO | IN_MOVED_FROM | IN_ATTRIB); used += sizeof(struct inotify_event) + ev->len; diff --git a/util/guest-random.c b/util/guest-random.c index 23643f86cc..33607d5ff2 100644 --- a/util/guest-random.c +++ b/util/guest-random.c @@ -14,7 +14,7 @@ #include "qapi/error.h" #include "qemu/guest-random.h" #include "crypto/random.h" -#include "sysemu/replay.h" +#include "exec/replay-core.h" static __thread GRand *thread_rand; @@ -87,11 +87,11 @@ void qemu_guest_random_seed_thread_part2(uint64_t seed) } } -int qemu_guest_random_seed_main(const char *optarg, Error **errp) +int qemu_guest_random_seed_main(const char *seedstr, Error **errp) { - unsigned long long seed; - if (parse_uint_full(optarg, &seed, 0)) { - error_setg(errp, "Invalid seed number: %s", optarg); + uint64_t seed; + if (parse_uint_full(seedstr, 0, &seed)) { + error_setg(errp, "Invalid seed number: %s", seedstr); return -1; } else { deterministic = true; diff --git a/util/hbitmap.c b/util/hbitmap.c index ea989e1f0e..6d6e1b595d 100644 --- a/util/hbitmap.c +++ b/util/hbitmap.c @@ -331,7 +331,7 @@ bool hbitmap_status(const HBitmap *hb, int64_t start, int64_t count, assert(next_zero > start); *pnum = next_zero - start; - return false; + return true; } bool hbitmap_empty(const HBitmap *hb) @@ -873,11 +873,6 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size) } } -bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b) -{ - return (a->orig_size == b->orig_size); -} - /** * hbitmap_sparse_merge: performs dst = dst | src * works with differing granularities. @@ -901,28 +896,24 @@ static void hbitmap_sparse_merge(HBitmap *dst, const HBitmap *src) * Given HBitmaps A and B, let R := A (BITOR) B. * Bitmaps A and B will not be modified, * except when bitmap R is an alias of A or B. - * - * @return true if the merge was successful, - * false if it was not attempted. + * Bitmaps must have same size. */ -bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) +void hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) { int i; uint64_t j; - if (!hbitmap_can_merge(a, b) || !hbitmap_can_merge(a, result)) { - return false; - } - assert(hbitmap_can_merge(b, result)); + assert(a->orig_size == result->orig_size); + assert(b->orig_size == result->orig_size); if ((!hbitmap_count(a) && result == b) || (!hbitmap_count(b) && result == a)) { - return true; + return; } if (!hbitmap_count(a) && !hbitmap_count(b)) { hbitmap_reset_all(result); - return true; + return; } if (a->granularity != b->granularity) { @@ -935,7 +926,7 @@ bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) if (b != result) { hbitmap_sparse_merge(result, b); } - return true; + return; } /* This merge is O(size), as BITS_PER_LONG and HBITMAP_LEVELS are constant. @@ -951,8 +942,6 @@ bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) /* Recompute the dirty count */ result->count = hb_count_between(result, 0, result->size - 1); - - return true; } char *hbitmap_sha256(const HBitmap *bitmap, Error **errp) diff --git a/util/host-utils.c b/util/host-utils.c index 96d5dc0bed..fb91bcba82 100644 --- a/util/host-utils.c +++ b/util/host-utils.c @@ -266,3 +266,183 @@ void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow) *plow = *plow << shift; } } + +/* + * Unsigned 256-by-128 division. + * Returns the remainder via r. + * Returns lower 128 bit of quotient. + * Needs a normalized divisor (most significant bit set to 1). + * + * Adapted from include/qemu/host-utils.h udiv_qrnnd, + * from the GNU Multi Precision Library - longlong.h __udiv_qrnnd + * (https://gmplib.org/repo/gmp/file/tip/longlong.h) + * + * Licensed under the GPLv2/LGPLv3 + */ +static Int128 udiv256_qrnnd(Int128 *r, Int128 n1, Int128 n0, Int128 d) +{ + Int128 d0, d1, q0, q1, r1, r0, m; + uint64_t mp0, mp1; + + d0 = int128_make64(int128_getlo(d)); + d1 = int128_make64(int128_gethi(d)); + + r1 = int128_remu(n1, d1); + q1 = int128_divu(n1, d1); + mp0 = int128_getlo(q1); + mp1 = int128_gethi(q1); + mulu128(&mp0, &mp1, int128_getlo(d0)); + m = int128_make128(mp0, mp1); + r1 = int128_make128(int128_gethi(n0), int128_getlo(r1)); + if (int128_ult(r1, m)) { + q1 = int128_sub(q1, int128_one()); + r1 = int128_add(r1, d); + if (int128_uge(r1, d)) { + if (int128_ult(r1, m)) { + q1 = int128_sub(q1, int128_one()); + r1 = int128_add(r1, d); + } + } + } + r1 = int128_sub(r1, m); + + r0 = int128_remu(r1, d1); + q0 = int128_divu(r1, d1); + mp0 = int128_getlo(q0); + mp1 = int128_gethi(q0); + mulu128(&mp0, &mp1, int128_getlo(d0)); + m = int128_make128(mp0, mp1); + r0 = int128_make128(int128_getlo(n0), int128_getlo(r0)); + if (int128_ult(r0, m)) { + q0 = int128_sub(q0, int128_one()); + r0 = int128_add(r0, d); + if (int128_uge(r0, d)) { + if (int128_ult(r0, m)) { + q0 = int128_sub(q0, int128_one()); + r0 = int128_add(r0, d); + } + } + } + r0 = int128_sub(r0, m); + + *r = r0; + return int128_or(int128_lshift(q1, 64), q0); +} + +/* + * Unsigned 256-by-128 division. + * Returns the remainder. + * Returns quotient via plow and phigh. + * Also returns the remainder via the function return value. + */ +Int128 divu256(Int128 *plow, Int128 *phigh, Int128 divisor) +{ + Int128 dhi = *phigh; + Int128 dlo = *plow; + Int128 rem, dhighest; + int sh; + + if (!int128_nz(divisor) || !int128_nz(dhi)) { + *plow = int128_divu(dlo, divisor); + *phigh = int128_zero(); + return int128_remu(dlo, divisor); + } else { + sh = clz128(divisor); + + if (int128_ult(dhi, divisor)) { + if (sh != 0) { + /* normalize the divisor, shifting the dividend accordingly */ + divisor = int128_lshift(divisor, sh); + dhi = int128_or(int128_lshift(dhi, sh), + int128_urshift(dlo, (128 - sh))); + dlo = int128_lshift(dlo, sh); + } + + *phigh = int128_zero(); + *plow = udiv256_qrnnd(&rem, dhi, dlo, divisor); + } else { + if (sh != 0) { + /* normalize the divisor, shifting the dividend accordingly */ + divisor = int128_lshift(divisor, sh); + dhighest = int128_rshift(dhi, (128 - sh)); + dhi = int128_or(int128_lshift(dhi, sh), + int128_urshift(dlo, (128 - sh))); + dlo = int128_lshift(dlo, sh); + + *phigh = udiv256_qrnnd(&dhi, dhighest, dhi, divisor); + } else { + /* + * dhi >= divisor + * Since the MSB of divisor is set (sh == 0), + * (dhi - divisor) < divisor + * + * Thus, the high part of the quotient is 1, and we can + * calculate the low part with a single call to udiv_qrnnd + * after subtracting divisor from dhi + */ + dhi = int128_sub(dhi, divisor); + *phigh = int128_one(); + } + + *plow = udiv256_qrnnd(&rem, dhi, dlo, divisor); + } + + /* + * since the dividend/divisor might have been normalized, + * the remainder might also have to be shifted back + */ + rem = int128_urshift(rem, sh); + return rem; + } +} + +/* + * Signed 256-by-128 division. + * Returns quotient via plow and phigh. + * Also returns the remainder via the function return value. + */ +Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor) +{ + bool neg_quotient = false, neg_remainder = false; + Int128 unsig_hi = *phigh, unsig_lo = *plow; + Int128 rem; + + if (!int128_nonneg(*phigh)) { + neg_quotient = !neg_quotient; + neg_remainder = !neg_remainder; + + if (!int128_nz(unsig_lo)) { + unsig_hi = int128_neg(unsig_hi); + } else { + unsig_hi = int128_not(unsig_hi); + unsig_lo = int128_neg(unsig_lo); + } + } + + if (!int128_nonneg(divisor)) { + neg_quotient = !neg_quotient; + + divisor = int128_neg(divisor); + } + + rem = divu256(&unsig_lo, &unsig_hi, divisor); + + if (neg_quotient) { + if (!int128_nz(unsig_lo)) { + *phigh = int128_neg(unsig_hi); + *plow = int128_zero(); + } else { + *phigh = int128_not(unsig_hi); + *plow = int128_neg(unsig_lo); + } + } else { + *phigh = unsig_hi; + *plow = unsig_lo; + } + + if (neg_remainder) { + return int128_neg(rem); + } else { + return rem; + } +} diff --git a/util/int128.c b/util/int128.c index ed8f25fef1..df6c6331bd 100644 --- a/util/int128.c +++ b/util/int128.c @@ -144,4 +144,46 @@ Int128 int128_rems(Int128 a, Int128 b) return r; } +#elif defined(CONFIG_TCG_INTERPRETER) + +Int128 int128_divu(Int128 a_s, Int128 b_s) +{ + Int128Alias r, a, b; + + a.s = a_s; + b.s = b_s; + r.u = a.u / b.u; + return r.s; +} + +Int128 int128_remu(Int128 a_s, Int128 b_s) +{ + Int128Alias r, a, b; + + a.s = a_s; + b.s = b_s; + r.u = a.u % b.u; + return r.s; +} + +Int128 int128_divs(Int128 a_s, Int128 b_s) +{ + Int128Alias r, a, b; + + a.s = a_s; + b.s = b_s; + r.i = a.i / b.i; + return r.s; +} + +Int128 int128_rems(Int128 a_s, Int128 b_s) +{ + Int128Alias r, a, b; + + a.s = a_s; + b.s = b_s; + r.i = a.i % b.i; + return r.s; +} + #endif diff --git a/util/interval-tree.c b/util/interval-tree.c new file mode 100644 index 0000000000..53465182e6 --- /dev/null +++ b/util/interval-tree.c @@ -0,0 +1,899 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/interval-tree.h" +#include "qemu/atomic.h" + +/* + * Red Black Trees. + * + * For now, don't expose Linux Red-Black Trees separately, but retain the + * separate type definitions to keep the implementation sane, and allow + * the possibility of separating them later. + * + * Derived from include/linux/rbtree_augmented.h and its dependencies. + */ + +/* + * red-black trees properties: https://en.wikipedia.org/wiki/Rbtree + * + * 1) A node is either red or black + * 2) The root is black + * 3) All leaves (NULL) are black + * 4) Both children of every red node are black + * 5) Every simple path from root to leaves contains the same number + * of black nodes. + * + * 4 and 5 give the O(log n) guarantee, since 4 implies you cannot have two + * consecutive red nodes in a path and every red node is therefore followed by + * a black. So if B is the number of black nodes on every simple path (as per + * 5), then the longest possible path due to 4 is 2B. + * + * We shall indicate color with case, where black nodes are uppercase and red + * nodes will be lowercase. Unknown color nodes shall be drawn as red within + * parentheses and have some accompanying text comment. + * + * Notes on lockless lookups: + * + * All stores to the tree structure (rb_left and rb_right) must be done using + * WRITE_ONCE [qatomic_set for QEMU]. And we must not inadvertently cause + * (temporary) loops in the tree structure as seen in program order. + * + * These two requirements will allow lockless iteration of the tree -- not + * correct iteration mind you, tree rotations are not atomic so a lookup might + * miss entire subtrees. + * + * But they do guarantee that any such traversal will only see valid elements + * and that it will indeed complete -- does not get stuck in a loop. + * + * It also guarantees that if the lookup returns an element it is the 'correct' + * one. But not returning an element does _NOT_ mean it's not present. + */ + +typedef enum RBColor +{ + RB_RED, + RB_BLACK, +} RBColor; + +typedef struct RBAugmentCallbacks { + void (*propagate)(RBNode *node, RBNode *stop); + void (*copy)(RBNode *old, RBNode *new); + void (*rotate)(RBNode *old, RBNode *new); +} RBAugmentCallbacks; + +static inline uintptr_t rb_pc(const RBNode *n) +{ + return qatomic_read(&n->rb_parent_color); +} + +static inline void rb_set_pc(RBNode *n, uintptr_t pc) +{ + qatomic_set(&n->rb_parent_color, pc); +} + +static inline RBNode *pc_parent(uintptr_t pc) +{ + return (RBNode *)(pc & ~1); +} + +static inline RBNode *rb_parent(const RBNode *n) +{ + return pc_parent(rb_pc(n)); +} + +static inline RBNode *rb_red_parent(const RBNode *n) +{ + return (RBNode *)rb_pc(n); +} + +static inline RBColor pc_color(uintptr_t pc) +{ + return (RBColor)(pc & 1); +} + +static inline bool pc_is_red(uintptr_t pc) +{ + return pc_color(pc) == RB_RED; +} + +static inline bool pc_is_black(uintptr_t pc) +{ + return !pc_is_red(pc); +} + +static inline RBColor rb_color(const RBNode *n) +{ + return pc_color(rb_pc(n)); +} + +static inline bool rb_is_red(const RBNode *n) +{ + return pc_is_red(rb_pc(n)); +} + +static inline bool rb_is_black(const RBNode *n) +{ + return pc_is_black(rb_pc(n)); +} + +static inline void rb_set_black(RBNode *n) +{ + rb_set_pc(n, rb_pc(n) | RB_BLACK); +} + +static inline void rb_set_parent_color(RBNode *n, RBNode *p, RBColor color) +{ + rb_set_pc(n, (uintptr_t)p | color); +} + +static inline void rb_set_parent(RBNode *n, RBNode *p) +{ + rb_set_parent_color(n, p, rb_color(n)); +} + +static inline void rb_link_node(RBNode *node, RBNode *parent, RBNode **rb_link) +{ + node->rb_parent_color = (uintptr_t)parent; + node->rb_left = node->rb_right = NULL; + + /* + * Ensure that node is initialized before insertion, + * as viewed by a concurrent search. + */ + qatomic_set_mb(rb_link, node); +} + +static RBNode *rb_next(RBNode *node) +{ + RBNode *parent; + + /* OMIT: if empty node, return null. */ + + /* + * If we have a right-hand child, go down and then left as far as we can. + */ + if (node->rb_right) { + node = node->rb_right; + while (node->rb_left) { + node = node->rb_left; + } + return node; + } + + /* + * No right-hand children. Everything down and left is smaller than us, + * so any 'next' node must be in the general direction of our parent. + * Go up the tree; any time the ancestor is a right-hand child of its + * parent, keep going up. First time it's a left-hand child of its + * parent, said parent is our 'next' node. + */ + while ((parent = rb_parent(node)) && node == parent->rb_right) { + node = parent; + } + + return parent; +} + +static inline void rb_change_child(RBNode *old, RBNode *new, + RBNode *parent, RBRoot *root) +{ + if (!parent) { + qatomic_set(&root->rb_node, new); + } else if (parent->rb_left == old) { + qatomic_set(&parent->rb_left, new); + } else { + qatomic_set(&parent->rb_right, new); + } +} + +static inline void rb_rotate_set_parents(RBNode *old, RBNode *new, + RBRoot *root, RBColor color) +{ + uintptr_t pc = rb_pc(old); + RBNode *parent = pc_parent(pc); + + rb_set_pc(new, pc); + rb_set_parent_color(old, new, color); + rb_change_child(old, new, parent, root); +} + +static void rb_insert_augmented(RBNode *node, RBRoot *root, + const RBAugmentCallbacks *augment) +{ + RBNode *parent = rb_red_parent(node), *gparent, *tmp; + + while (true) { + /* + * Loop invariant: node is red. + */ + if (unlikely(!parent)) { + /* + * The inserted node is root. Either this is the first node, or + * we recursed at Case 1 below and are no longer violating 4). + */ + rb_set_parent_color(node, NULL, RB_BLACK); + break; + } + + /* + * If there is a black parent, we are done. Otherwise, take some + * corrective action as, per 4), we don't want a red root or two + * consecutive red nodes. + */ + if (rb_is_black(parent)) { + break; + } + + gparent = rb_red_parent(parent); + + tmp = gparent->rb_right; + if (parent != tmp) { /* parent == gparent->rb_left */ + if (tmp && rb_is_red(tmp)) { + /* + * Case 1 - node's uncle is red (color flips). + * + * G g + * / \ / \ + * p u --> P U + * / / + * n n + * + * However, since g's parent might be red, and 4) does not + * allow this, we need to recurse at g. + */ + rb_set_parent_color(tmp, gparent, RB_BLACK); + rb_set_parent_color(parent, gparent, RB_BLACK); + node = gparent; + parent = rb_parent(node); + rb_set_parent_color(node, parent, RB_RED); + continue; + } + + tmp = parent->rb_right; + if (node == tmp) { + /* + * Case 2 - node's uncle is black and node is + * the parent's right child (left rotate at parent). + * + * G G + * / \ / \ + * p U --> n U + * \ / + * n p + * + * This still leaves us in violation of 4), the + * continuation into Case 3 will fix that. + */ + tmp = node->rb_left; + qatomic_set(&parent->rb_right, tmp); + qatomic_set(&node->rb_left, parent); + if (tmp) { + rb_set_parent_color(tmp, parent, RB_BLACK); + } + rb_set_parent_color(parent, node, RB_RED); + augment->rotate(parent, node); + parent = node; + tmp = node->rb_right; + } + + /* + * Case 3 - node's uncle is black and node is + * the parent's left child (right rotate at gparent). + * + * G P + * / \ / \ + * p U --> n g + * / \ + * n U + */ + qatomic_set(&gparent->rb_left, tmp); /* == parent->rb_right */ + qatomic_set(&parent->rb_right, gparent); + if (tmp) { + rb_set_parent_color(tmp, gparent, RB_BLACK); + } + rb_rotate_set_parents(gparent, parent, root, RB_RED); + augment->rotate(gparent, parent); + break; + } else { + tmp = gparent->rb_left; + if (tmp && rb_is_red(tmp)) { + /* Case 1 - color flips */ + rb_set_parent_color(tmp, gparent, RB_BLACK); + rb_set_parent_color(parent, gparent, RB_BLACK); + node = gparent; + parent = rb_parent(node); + rb_set_parent_color(node, parent, RB_RED); + continue; + } + + tmp = parent->rb_left; + if (node == tmp) { + /* Case 2 - right rotate at parent */ + tmp = node->rb_right; + qatomic_set(&parent->rb_left, tmp); + qatomic_set(&node->rb_right, parent); + if (tmp) { + rb_set_parent_color(tmp, parent, RB_BLACK); + } + rb_set_parent_color(parent, node, RB_RED); + augment->rotate(parent, node); + parent = node; + tmp = node->rb_left; + } + + /* Case 3 - left rotate at gparent */ + qatomic_set(&gparent->rb_right, tmp); /* == parent->rb_left */ + qatomic_set(&parent->rb_left, gparent); + if (tmp) { + rb_set_parent_color(tmp, gparent, RB_BLACK); + } + rb_rotate_set_parents(gparent, parent, root, RB_RED); + augment->rotate(gparent, parent); + break; + } + } +} + +static void rb_insert_augmented_cached(RBNode *node, + RBRootLeftCached *root, bool newleft, + const RBAugmentCallbacks *augment) +{ + if (newleft) { + root->rb_leftmost = node; + } + rb_insert_augmented(node, &root->rb_root, augment); +} + +static void rb_erase_color(RBNode *parent, RBRoot *root, + const RBAugmentCallbacks *augment) +{ + RBNode *node = NULL, *sibling, *tmp1, *tmp2; + + while (true) { + /* + * Loop invariants: + * - node is black (or NULL on first iteration) + * - node is not the root (parent is not NULL) + * - All leaf paths going through parent and node have a + * black node count that is 1 lower than other leaf paths. + */ + sibling = parent->rb_right; + if (node != sibling) { /* node == parent->rb_left */ + if (rb_is_red(sibling)) { + /* + * Case 1 - left rotate at parent + * + * P S + * / \ / \ + * N s --> p Sr + * / \ / \ + * Sl Sr N Sl + */ + tmp1 = sibling->rb_left; + qatomic_set(&parent->rb_right, tmp1); + qatomic_set(&sibling->rb_left, parent); + rb_set_parent_color(tmp1, parent, RB_BLACK); + rb_rotate_set_parents(parent, sibling, root, RB_RED); + augment->rotate(parent, sibling); + sibling = tmp1; + } + tmp1 = sibling->rb_right; + if (!tmp1 || rb_is_black(tmp1)) { + tmp2 = sibling->rb_left; + if (!tmp2 || rb_is_black(tmp2)) { + /* + * Case 2 - sibling color flip + * (p could be either color here) + * + * (p) (p) + * / \ / \ + * N S --> N s + * / \ / \ + * Sl Sr Sl Sr + * + * This leaves us violating 5) which + * can be fixed by flipping p to black + * if it was red, or by recursing at p. + * p is red when coming from Case 1. + */ + rb_set_parent_color(sibling, parent, RB_RED); + if (rb_is_red(parent)) { + rb_set_black(parent); + } else { + node = parent; + parent = rb_parent(node); + if (parent) { + continue; + } + } + break; + } + /* + * Case 3 - right rotate at sibling + * (p could be either color here) + * + * (p) (p) + * / \ / \ + * N S --> N sl + * / \ \ + * sl Sr S + * \ + * Sr + * + * Note: p might be red, and then bot + * p and sl are red after rotation (which + * breaks property 4). This is fixed in + * Case 4 (in rb_rotate_set_parents() + * which set sl the color of p + * and set p RB_BLACK) + * + * (p) (sl) + * / \ / \ + * N sl --> P S + * \ / \ + * S N Sr + * \ + * Sr + */ + tmp1 = tmp2->rb_right; + qatomic_set(&sibling->rb_left, tmp1); + qatomic_set(&tmp2->rb_right, sibling); + qatomic_set(&parent->rb_right, tmp2); + if (tmp1) { + rb_set_parent_color(tmp1, sibling, RB_BLACK); + } + augment->rotate(sibling, tmp2); + tmp1 = sibling; + sibling = tmp2; + } + /* + * Case 4 - left rotate at parent + color flips + * (p and sl could be either color here. + * After rotation, p becomes black, s acquires + * p's color, and sl keeps its color) + * + * (p) (s) + * / \ / \ + * N S --> P Sr + * / \ / \ + * (sl) sr N (sl) + */ + tmp2 = sibling->rb_left; + qatomic_set(&parent->rb_right, tmp2); + qatomic_set(&sibling->rb_left, parent); + rb_set_parent_color(tmp1, sibling, RB_BLACK); + if (tmp2) { + rb_set_parent(tmp2, parent); + } + rb_rotate_set_parents(parent, sibling, root, RB_BLACK); + augment->rotate(parent, sibling); + break; + } else { + sibling = parent->rb_left; + if (rb_is_red(sibling)) { + /* Case 1 - right rotate at parent */ + tmp1 = sibling->rb_right; + qatomic_set(&parent->rb_left, tmp1); + qatomic_set(&sibling->rb_right, parent); + rb_set_parent_color(tmp1, parent, RB_BLACK); + rb_rotate_set_parents(parent, sibling, root, RB_RED); + augment->rotate(parent, sibling); + sibling = tmp1; + } + tmp1 = sibling->rb_left; + if (!tmp1 || rb_is_black(tmp1)) { + tmp2 = sibling->rb_right; + if (!tmp2 || rb_is_black(tmp2)) { + /* Case 2 - sibling color flip */ + rb_set_parent_color(sibling, parent, RB_RED); + if (rb_is_red(parent)) { + rb_set_black(parent); + } else { + node = parent; + parent = rb_parent(node); + if (parent) { + continue; + } + } + break; + } + /* Case 3 - left rotate at sibling */ + tmp1 = tmp2->rb_left; + qatomic_set(&sibling->rb_right, tmp1); + qatomic_set(&tmp2->rb_left, sibling); + qatomic_set(&parent->rb_left, tmp2); + if (tmp1) { + rb_set_parent_color(tmp1, sibling, RB_BLACK); + } + augment->rotate(sibling, tmp2); + tmp1 = sibling; + sibling = tmp2; + } + /* Case 4 - right rotate at parent + color flips */ + tmp2 = sibling->rb_right; + qatomic_set(&parent->rb_left, tmp2); + qatomic_set(&sibling->rb_right, parent); + rb_set_parent_color(tmp1, sibling, RB_BLACK); + if (tmp2) { + rb_set_parent(tmp2, parent); + } + rb_rotate_set_parents(parent, sibling, root, RB_BLACK); + augment->rotate(parent, sibling); + break; + } + } +} + +static void rb_erase_augmented(RBNode *node, RBRoot *root, + const RBAugmentCallbacks *augment) +{ + RBNode *child = node->rb_right; + RBNode *tmp = node->rb_left; + RBNode *parent, *rebalance; + uintptr_t pc; + + if (!tmp) { + /* + * Case 1: node to erase has no more than 1 child (easy!) + * + * Note that if there is one child it must be red due to 5) + * and node must be black due to 4). We adjust colors locally + * so as to bypass rb_erase_color() later on. + */ + pc = rb_pc(node); + parent = pc_parent(pc); + rb_change_child(node, child, parent, root); + if (child) { + rb_set_pc(child, pc); + rebalance = NULL; + } else { + rebalance = pc_is_black(pc) ? parent : NULL; + } + tmp = parent; + } else if (!child) { + /* Still case 1, but this time the child is node->rb_left */ + pc = rb_pc(node); + parent = pc_parent(pc); + rb_set_pc(tmp, pc); + rb_change_child(node, tmp, parent, root); + rebalance = NULL; + tmp = parent; + } else { + RBNode *successor = child, *child2; + tmp = child->rb_left; + if (!tmp) { + /* + * Case 2: node's successor is its right child + * + * (n) (s) + * / \ / \ + * (x) (s) -> (x) (c) + * \ + * (c) + */ + parent = successor; + child2 = successor->rb_right; + + augment->copy(node, successor); + } else { + /* + * Case 3: node's successor is leftmost under + * node's right child subtree + * + * (n) (s) + * / \ / \ + * (x) (y) -> (x) (y) + * / / + * (p) (p) + * / / + * (s) (c) + * \ + * (c) + */ + do { + parent = successor; + successor = tmp; + tmp = tmp->rb_left; + } while (tmp); + child2 = successor->rb_right; + qatomic_set(&parent->rb_left, child2); + qatomic_set(&successor->rb_right, child); + rb_set_parent(child, successor); + + augment->copy(node, successor); + augment->propagate(parent, successor); + } + + tmp = node->rb_left; + qatomic_set(&successor->rb_left, tmp); + rb_set_parent(tmp, successor); + + pc = rb_pc(node); + tmp = pc_parent(pc); + rb_change_child(node, successor, tmp, root); + + if (child2) { + rb_set_parent_color(child2, parent, RB_BLACK); + rebalance = NULL; + } else { + rebalance = rb_is_black(successor) ? parent : NULL; + } + rb_set_pc(successor, pc); + tmp = successor; + } + + augment->propagate(tmp, NULL); + + if (rebalance) { + rb_erase_color(rebalance, root, augment); + } +} + +static void rb_erase_augmented_cached(RBNode *node, RBRootLeftCached *root, + const RBAugmentCallbacks *augment) +{ + if (root->rb_leftmost == node) { + root->rb_leftmost = rb_next(node); + } + rb_erase_augmented(node, &root->rb_root, augment); +} + + +/* + * Interval trees. + * + * Derived from lib/interval_tree.c and its dependencies, + * especially include/linux/interval_tree_generic.h. + */ + +#define rb_to_itree(N) container_of(N, IntervalTreeNode, rb) + +static bool interval_tree_compute_max(IntervalTreeNode *node, bool exit) +{ + IntervalTreeNode *child; + uint64_t max = node->last; + + if (node->rb.rb_left) { + child = rb_to_itree(node->rb.rb_left); + if (child->subtree_last > max) { + max = child->subtree_last; + } + } + if (node->rb.rb_right) { + child = rb_to_itree(node->rb.rb_right); + if (child->subtree_last > max) { + max = child->subtree_last; + } + } + if (exit && node->subtree_last == max) { + return true; + } + node->subtree_last = max; + return false; +} + +static void interval_tree_propagate(RBNode *rb, RBNode *stop) +{ + while (rb != stop) { + IntervalTreeNode *node = rb_to_itree(rb); + if (interval_tree_compute_max(node, true)) { + break; + } + rb = rb_parent(&node->rb); + } +} + +static void interval_tree_copy(RBNode *rb_old, RBNode *rb_new) +{ + IntervalTreeNode *old = rb_to_itree(rb_old); + IntervalTreeNode *new = rb_to_itree(rb_new); + + new->subtree_last = old->subtree_last; +} + +static void interval_tree_rotate(RBNode *rb_old, RBNode *rb_new) +{ + IntervalTreeNode *old = rb_to_itree(rb_old); + IntervalTreeNode *new = rb_to_itree(rb_new); + + new->subtree_last = old->subtree_last; + interval_tree_compute_max(old, false); +} + +static const RBAugmentCallbacks interval_tree_augment = { + .propagate = interval_tree_propagate, + .copy = interval_tree_copy, + .rotate = interval_tree_rotate, +}; + +/* Insert / remove interval nodes from the tree */ +void interval_tree_insert(IntervalTreeNode *node, IntervalTreeRoot *root) +{ + RBNode **link = &root->rb_root.rb_node, *rb_parent = NULL; + uint64_t start = node->start, last = node->last; + IntervalTreeNode *parent; + bool leftmost = true; + + while (*link) { + rb_parent = *link; + parent = rb_to_itree(rb_parent); + + if (parent->subtree_last < last) { + parent->subtree_last = last; + } + if (start < parent->start) { + link = &parent->rb.rb_left; + } else { + link = &parent->rb.rb_right; + leftmost = false; + } + } + + node->subtree_last = last; + rb_link_node(&node->rb, rb_parent, link); + rb_insert_augmented_cached(&node->rb, root, leftmost, + &interval_tree_augment); +} + +void interval_tree_remove(IntervalTreeNode *node, IntervalTreeRoot *root) +{ + rb_erase_augmented_cached(&node->rb, root, &interval_tree_augment); +} + +/* + * Iterate over intervals intersecting [start;last] + * + * Note that a node's interval intersects [start;last] iff: + * Cond1: node->start <= last + * and + * Cond2: start <= node->last + */ + +static IntervalTreeNode *interval_tree_subtree_search(IntervalTreeNode *node, + uint64_t start, + uint64_t last) +{ + while (true) { + /* + * Loop invariant: start <= node->subtree_last + * (Cond2 is satisfied by one of the subtree nodes) + */ + RBNode *tmp = qatomic_read(&node->rb.rb_left); + if (tmp) { + IntervalTreeNode *left = rb_to_itree(tmp); + + if (start <= left->subtree_last) { + /* + * Some nodes in left subtree satisfy Cond2. + * Iterate to find the leftmost such node N. + * If it also satisfies Cond1, that's the + * match we are looking for. Otherwise, there + * is no matching interval as nodes to the + * right of N can't satisfy Cond1 either. + */ + node = left; + continue; + } + } + if (node->start <= last) { /* Cond1 */ + if (start <= node->last) { /* Cond2 */ + return node; /* node is leftmost match */ + } + tmp = qatomic_read(&node->rb.rb_right); + if (tmp) { + node = rb_to_itree(tmp); + if (start <= node->subtree_last) { + continue; + } + } + } + return NULL; /* no match */ + } +} + +IntervalTreeNode *interval_tree_iter_first(IntervalTreeRoot *root, + uint64_t start, uint64_t last) +{ + IntervalTreeNode *node, *leftmost; + + if (!root || !root->rb_root.rb_node) { + return NULL; + } + + /* + * Fastpath range intersection/overlap between A: [a0, a1] and + * B: [b0, b1] is given by: + * + * a0 <= b1 && b0 <= a1 + * + * ... where A holds the lock range and B holds the smallest + * 'start' and largest 'last' in the tree. For the later, we + * rely on the root node, which by augmented interval tree + * property, holds the largest value in its last-in-subtree. + * This allows mitigating some of the tree walk overhead for + * for non-intersecting ranges, maintained and consulted in O(1). + */ + node = rb_to_itree(root->rb_root.rb_node); + if (node->subtree_last < start) { + return NULL; + } + + leftmost = rb_to_itree(root->rb_leftmost); + if (leftmost->start > last) { + return NULL; + } + + return interval_tree_subtree_search(node, start, last); +} + +IntervalTreeNode *interval_tree_iter_next(IntervalTreeNode *node, + uint64_t start, uint64_t last) +{ + RBNode *rb, *prev; + + rb = qatomic_read(&node->rb.rb_right); + while (true) { + /* + * Loop invariants: + * Cond1: node->start <= last + * rb == node->rb.rb_right + * + * First, search right subtree if suitable + */ + if (rb) { + IntervalTreeNode *right = rb_to_itree(rb); + + if (start <= right->subtree_last) { + return interval_tree_subtree_search(right, start, last); + } + } + + /* Move up the tree until we come from a node's left child */ + do { + rb = rb_parent(&node->rb); + if (!rb) { + return NULL; + } + prev = &node->rb; + node = rb_to_itree(rb); + rb = qatomic_read(&node->rb.rb_right); + } while (prev == rb); + + /* Check if the node intersects [start;last] */ + if (last < node->start) { /* !Cond1 */ + return NULL; + } + if (start <= node->last) { /* Cond2 */ + return node; + } + } +} + +/* Occasionally useful for calling from within the debugger. */ +#if 0 +static void debug_interval_tree_int(IntervalTreeNode *node, + const char *dir, int level) +{ + printf("%4d %*s %s [%" PRIu64 ",%" PRIu64 "] subtree_last:%" PRIu64 "\n", + level, level + 1, dir, rb_is_red(&node->rb) ? "r" : "b", + node->start, node->last, node->subtree_last); + + if (node->rb.rb_left) { + debug_interval_tree_int(rb_to_itree(node->rb.rb_left), "<", level + 1); + } + if (node->rb.rb_right) { + debug_interval_tree_int(rb_to_itree(node->rb.rb_right), ">", level + 1); + } +} + +void debug_interval_tree(IntervalTreeNode *node); +void debug_interval_tree(IntervalTreeNode *node) +{ + if (node) { + debug_interval_tree_int(node, "*", 0); + } else { + printf("null\n"); + } +} +#endif diff --git a/util/iov.c b/util/iov.c index 22d6996cce..7e73948f5e 100644 --- a/util/iov.c +++ b/util/iov.c @@ -111,12 +111,17 @@ do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send) /*XXX Note: windows has WSASend() and WSARecv() */ unsigned i = 0; ssize_t ret = 0; + ssize_t off = 0; while (i < iov_cnt) { ssize_t r = do_send - ? send(sockfd, iov[i].iov_base, iov[i].iov_len, 0) - : recv(sockfd, iov[i].iov_base, iov[i].iov_len, 0); + ? send(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0) + : recv(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0); if (r > 0) { ret += r; + off += r; + if (off < iov[i].iov_len) { + continue; + } } else if (!r) { break; } else if (errno == EINTR) { @@ -129,6 +134,7 @@ do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send) } break; } + off = 0; i++; } return ret; @@ -372,15 +378,15 @@ static struct iovec *iov_skip_offset(struct iovec *iov, size_t offset, } /* - * qiov_slice + * qemu_iovec_slice * * Find subarray of iovec's, containing requested range. @head would * be offset in first iov (returned by the function), @tail would be * count of extra bytes in last iovec (returned iov + @niov - 1). */ -static struct iovec *qiov_slice(QEMUIOVector *qiov, - size_t offset, size_t len, - size_t *head, size_t *tail, int *niov) +struct iovec *qemu_iovec_slice(QEMUIOVector *qiov, + size_t offset, size_t len, + size_t *head, size_t *tail, int *niov) { struct iovec *iov, *end_iov; @@ -405,75 +411,11 @@ int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len) size_t head, tail; int niov; - qiov_slice(qiov, offset, len, &head, &tail, &niov); + qemu_iovec_slice(qiov, offset, len, &head, &tail, &niov); return niov; } -/* - * Compile new iovec, combining @head_buf buffer, sub-qiov of @mid_qiov, - * and @tail_buf buffer into new qiov. - */ -int qemu_iovec_init_extended( - QEMUIOVector *qiov, - void *head_buf, size_t head_len, - QEMUIOVector *mid_qiov, size_t mid_offset, size_t mid_len, - void *tail_buf, size_t tail_len) -{ - size_t mid_head, mid_tail; - int total_niov, mid_niov = 0; - struct iovec *p, *mid_iov = NULL; - - assert(mid_qiov->niov <= IOV_MAX); - - if (SIZE_MAX - head_len < mid_len || - SIZE_MAX - head_len - mid_len < tail_len) - { - return -EINVAL; - } - - if (mid_len) { - mid_iov = qiov_slice(mid_qiov, mid_offset, mid_len, - &mid_head, &mid_tail, &mid_niov); - } - - total_niov = !!head_len + mid_niov + !!tail_len; - if (total_niov > IOV_MAX) { - return -EINVAL; - } - - if (total_niov == 1) { - qemu_iovec_init_buf(qiov, NULL, 0); - p = &qiov->local_iov; - } else { - qiov->niov = qiov->nalloc = total_niov; - qiov->size = head_len + mid_len + tail_len; - p = qiov->iov = g_new(struct iovec, qiov->niov); - } - - if (head_len) { - p->iov_base = head_buf; - p->iov_len = head_len; - p++; - } - - assert(!mid_niov == !mid_len); - if (mid_niov) { - memcpy(p, mid_iov, mid_niov * sizeof(*p)); - p[0].iov_base = (uint8_t *)p[0].iov_base + mid_head; - p[0].iov_len -= mid_head; - p[mid_niov - 1].iov_len -= mid_tail; - p += mid_niov; - } - - if (tail_len) { - p->iov_base = tail_buf; - p->iov_len = tail_len; - } - - return 0; -} - /* * Check if the contents of subrange of qiov data is all zeroes. */ @@ -505,14 +447,21 @@ bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t offset, size_t bytes) void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source, size_t offset, size_t len) { - int ret; + struct iovec *slice_iov; + int slice_niov; + size_t slice_head, slice_tail; assert(source->size >= len); assert(source->size - len >= offset); - /* We shrink the request, so we can't overflow neither size_t nor MAX_IOV */ - ret = qemu_iovec_init_extended(qiov, NULL, 0, source, offset, len, NULL, 0); - assert(ret == 0); + slice_iov = qemu_iovec_slice(source, offset, len, + &slice_head, &slice_tail, &slice_niov); + if (slice_niov == 1) { + qemu_iovec_init_buf(qiov, slice_iov[0].iov_base + slice_head, len); + } else { + qemu_iovec_init(qiov, slice_niov); + qemu_iovec_concat_iov(qiov, slice_iov, slice_niov, slice_head, len); + } } void qemu_iovec_destroy(QEMUIOVector *qiov) @@ -622,7 +571,7 @@ static int sortelem_cmp_src_index(const void *a, const void *b) */ void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf) { - IOVectorSortElem sortelems[src->niov]; + g_autofree IOVectorSortElem *sortelems = g_new(IOVectorSortElem, src->niov); void *last_end; int i; diff --git a/util/iova-tree.c b/util/iova-tree.c index 6dff29c1f6..536789797e 100644 --- a/util/iova-tree.c +++ b/util/iova-tree.c @@ -164,15 +164,13 @@ void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator) g_tree_foreach(tree->tree, iova_tree_traverse, iterator); } -int iova_tree_remove(IOVATree *tree, const DMAMap *map) +void iova_tree_remove(IOVATree *tree, DMAMap map) { const DMAMap *overlap; - while ((overlap = iova_tree_find(tree, map))) { + while ((overlap = iova_tree_find(tree, &map))) { g_tree_remove(tree->tree, overlap); } - - return IOVA_OK; } /** diff --git a/util/log.c b/util/log.c index d6eb0378c3..d36c98da0b 100644 --- a/util/log.c +++ b/util/log.c @@ -42,9 +42,9 @@ static QemuMutex global_mutex; static char *global_filename; static FILE *global_file; static __thread FILE *thread_file; +static __thread Notifier qemu_log_thread_cleanup_notifier; int qemu_loglevel; -static bool log_append; static bool log_per_thread; static GArray *debug_regions; @@ -77,9 +77,17 @@ static int log_thread_id(void) #endif } +static void qemu_log_thread_cleanup(Notifier *n, void *unused) +{ + if (thread_file != stderr) { + fclose(thread_file); + thread_file = NULL; + } +} + /* Lock/unlock output. */ -FILE *qemu_log_trylock(void) +static FILE *qemu_log_trylock_with_err(Error **errp) { FILE *logfile; @@ -90,9 +98,14 @@ FILE *qemu_log_trylock(void) = g_strdup_printf(global_filename, log_thread_id()); logfile = fopen(filename, "w"); if (!logfile) { + error_setg_errno(errp, errno, + "Error opening logfile %s for thread %d", + filename, log_thread_id()); return NULL; } thread_file = logfile; + qemu_log_thread_cleanup_notifier.notify = qemu_log_thread_cleanup; + qemu_thread_atexit_add(&qemu_log_thread_cleanup_notifier); } else { rcu_read_lock(); /* @@ -114,6 +127,11 @@ FILE *qemu_log_trylock(void) return logfile; } +FILE *qemu_log_trylock(void) +{ + return qemu_log_trylock_with_err(NULL); +} + void qemu_log_unlock(FILE *logfile) { if (logfile) { @@ -197,6 +215,15 @@ static bool qemu_set_log_internal(const char *filename, bool changed_name, QEMU_LOCK_GUARD(&global_mutex); logfile = global_file; + /* The per-thread flag is immutable. */ + if (log_per_thread) { + log_flags |= LOG_PER_THREAD; + } else { + if (global_filename) { + log_flags &= ~LOG_PER_THREAD; + } + } + per_thread = log_flags & LOG_PER_THREAD; if (changed_name) { @@ -248,40 +275,63 @@ static bool qemu_set_log_internal(const char *filename, bool changed_name, #endif qemu_loglevel = log_flags; - /* - * In all cases we only log if qemu_loglevel is set. - * Also: - * If per-thread, open the file for each thread in qemu_log_lock. - * If not daemonized we will always log either to stderr - * or to a file (if there is a filename). - * If we are daemonized, we will only log if there is a filename. - */ daemonized = is_daemonized(); - need_to_open_file = log_flags && !per_thread && (!daemonized || filename); + need_to_open_file = false; + if (!daemonized) { + /* + * If not daemonized we only log if qemu_loglevel is set, either to + * stderr or to a file (if there is a filename). + * If per-thread, open the file for each thread in qemu_log_trylock(). + */ + need_to_open_file = qemu_loglevel && !log_per_thread; + } else { + /* + * If we are daemonized, we will only log if there is a filename. + */ + need_to_open_file = filename != NULL; + } - if (logfile && (!need_to_open_file || changed_name)) { - qatomic_rcu_set(&global_file, NULL); - if (logfile != stderr) { + if (logfile) { + fflush(logfile); + if (changed_name && logfile != stderr) { RCUCloseFILE *r = g_new0(RCUCloseFILE, 1); r->fd = logfile; + qatomic_rcu_set(&global_file, NULL); call_rcu(r, rcu_close_file, rcu); } - logfile = NULL; + if (changed_name) { + logfile = NULL; + } + } + + if (log_per_thread && daemonized) { + logfile = thread_file; } if (!logfile && need_to_open_file) { if (filename) { - logfile = fopen(filename, log_append ? "a" : "w"); - if (!logfile) { - error_setg_errno(errp, errno, "Error opening logfile %s", - filename); - return false; + if (log_per_thread) { + logfile = qemu_log_trylock_with_err(errp); + if (!logfile) { + return false; + } + qemu_log_unlock(logfile); + } else { + logfile = fopen(filename, "w"); + if (!logfile) { + error_setg_errno(errp, errno, "Error opening logfile %s", + filename); + return false; + } } /* In case we are a daemon redirect stderr to logfile */ if (daemonized) { dup2(fileno(logfile), STDERR_FILENO); fclose(logfile); - /* This will skip closing logfile in rcu_close_file. */ + /* + * This will skip closing logfile in rcu_close_file() + * or qemu_log_thread_cleanup(). + */ logfile = stderr; } } else { @@ -290,9 +340,11 @@ static bool qemu_set_log_internal(const char *filename, bool changed_name, logfile = stderr; } - log_append = 1; - - qatomic_rcu_set(&global_file, logfile); + if (log_per_thread && daemonized) { + thread_file = logfile; + } else { + qatomic_rcu_set(&global_file, logfile); + } } return true; } @@ -439,12 +491,14 @@ const QEMULogItem qemu_log_items[] = { "do not chain compiled TBs so that \"exec\" and \"cpu\" show\n" "complete traces" }, #ifdef CONFIG_PLUGIN - { CPU_LOG_PLUGIN, "plugin", "output from TCG plugins\n"}, + { CPU_LOG_PLUGIN, "plugin", "output from TCG plugins"}, #endif { LOG_STRACE, "strace", "log every user-mode syscall, its input, and its result" }, { LOG_PER_THREAD, "tid", "open a separate log file per thread; filename must contain '%d'" }, + { CPU_LOG_TB_VPU, "vpu", + "include VPU registers in the 'cpu' logging" }, { 0, NULL, NULL }, }; diff --git a/util/main-loop.c b/util/main-loop.c index f00a25451b..a0386cfeb6 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -33,7 +33,6 @@ #include "block/thread-pool.h" #include "qemu/error-report.h" #include "qemu/queue.h" -#include "qemu/compiler.h" #include "qom/object.h" #ifndef _WIN32 @@ -48,7 +47,7 @@ */ /* * Disable CFI checks. - * We are going to call a signal hander directly. Such handler may or may not + * We are going to call a signal handler directly. Such handler may or may not * have been defined in our binary, so there's no guarantee that the pointer * used to set the handler is a cfi-valid pointer. Since the handlers are * stored in kernel memory, changing the handler to an attacker-defined @@ -64,9 +63,7 @@ static void sigfd_handler(void *opaque) ssize_t len; while (1) { - do { - len = read(fd, &info, sizeof(info)); - } while (len == -1 && errno == EINTR); + len = RETRY_ON_EINTR(read(fd, &info, sizeof(info))); if (len == -1 && errno == EAGAIN) { break; @@ -195,10 +192,7 @@ static void main_loop_update_params(EventLoopBase *base, Error **errp) return; } - aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp); - if (*errp) { - return; - } + aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch); aio_context_set_thread_pool_params(qemu_aio_context, base->thread_pool_min, base->thread_pool_max, errp); @@ -255,10 +249,6 @@ static int max_priority; static int glib_pollfds_idx; static int glib_n_poll_fds; -void qemu_fd_register(int fd) -{ -} - static void glib_pollfds_fill(int64_t *cur_timeout) { GMainContext *context = g_main_context_default(); @@ -309,13 +299,13 @@ static int os_host_main_loop_wait(int64_t timeout) glib_pollfds_fill(&timeout); - qemu_mutex_unlock_iothread(); + bql_unlock(); replay_mutex_unlock(); ret = qemu_poll_ns((GPollFD *)gpollfds->data, gpollfds->len, timeout); replay_mutex_lock(); - qemu_mutex_lock_iothread(); + bql_lock(); glib_pollfds_poll(); @@ -363,20 +353,30 @@ void qemu_del_polling_cb(PollingFunc *func, void *opaque) /* Wait objects support */ typedef struct WaitObjects { int num; - int revents[MAXIMUM_WAIT_OBJECTS + 1]; - HANDLE events[MAXIMUM_WAIT_OBJECTS + 1]; - WaitObjectFunc *func[MAXIMUM_WAIT_OBJECTS + 1]; - void *opaque[MAXIMUM_WAIT_OBJECTS + 1]; + int revents[MAXIMUM_WAIT_OBJECTS]; + HANDLE events[MAXIMUM_WAIT_OBJECTS]; + WaitObjectFunc *func[MAXIMUM_WAIT_OBJECTS]; + void *opaque[MAXIMUM_WAIT_OBJECTS]; } WaitObjects; static WaitObjects wait_objects = {0}; int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) { + int i; WaitObjects *w = &wait_objects; + if (w->num >= MAXIMUM_WAIT_OBJECTS) { return -1; } + + for (i = 0; i < w->num; i++) { + /* check if the same handle is added twice */ + if (w->events[i] == handle) { + return -1; + } + } + w->events[w->num] = handle; w->func[w->num] = func; w->opaque[w->num] = opaque; @@ -395,7 +395,7 @@ void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) if (w->events[i] == handle) { found = 1; } - if (found) { + if (found && i < (MAXIMUM_WAIT_OBJECTS - 1)) { w->events[i] = w->events[i + 1]; w->func[i] = w->func[i + 1]; w->opaque[i] = w->opaque[i + 1]; @@ -407,13 +407,6 @@ void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) } } -void qemu_fd_register(int fd) -{ - WSAEventSelect(fd, event_notifier_get_handle(&qemu_aio_context->notifier), - FD_READ | FD_ACCEPT | FD_CLOSE | - FD_CONNECT | FD_WRITE | FD_OOB); -} - static int pollfds_fill(GArray *pollfds, fd_set *rfds, fd_set *wfds, fd_set *xfds) { @@ -521,7 +514,7 @@ static int os_host_main_loop_wait(int64_t timeout) poll_timeout_ns = qemu_soonest_timeout(poll_timeout_ns, timeout); - qemu_mutex_unlock_iothread(); + bql_unlock(); replay_mutex_unlock(); @@ -529,7 +522,7 @@ static int os_host_main_loop_wait(int64_t timeout) replay_mutex_lock(); - qemu_mutex_lock_iothread(); + bql_lock(); if (g_poll_ret > 0) { for (i = 0; i < w->num; i++) { w->revents[i] = poll_fds[n_poll_fds + i].revents; @@ -609,9 +602,11 @@ void main_loop_wait(int nonblocking) /* Functions to operate on the main QEMU AioContext. */ -QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name) +QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name, + MemReentrancyGuard *reentrancy_guard) { - return aio_bh_new_full(qemu_aio_context, cb, opaque, name); + return aio_bh_new_full(qemu_aio_context, cb, opaque, name, + reentrancy_guard); } /* @@ -646,14 +641,13 @@ void qemu_set_fd_handler(int fd, void *opaque) { iohandler_init(); - aio_set_fd_handler(iohandler_ctx, fd, false, - fd_read, fd_write, NULL, NULL, opaque); + aio_set_fd_handler(iohandler_ctx, fd, fd_read, fd_write, NULL, NULL, + opaque); } void event_notifier_set_handler(EventNotifier *e, EventNotifierHandler *handler) { iohandler_init(); - aio_set_event_notifier(iohandler_ctx, e, false, - handler, NULL, NULL); + aio_set_event_notifier(iohandler_ctx, e, handler, NULL, NULL); } diff --git a/util/meson.build b/util/meson.build index 8f16018cd4..0ef9886be0 100644 --- a/util/meson.build +++ b/util/meson.build @@ -1,33 +1,42 @@ util_ss.add(files('osdep.c', 'cutils.c', 'unicode.c', 'qemu-timer-common.c')) +util_ss.add(files('thread-context.c'), numa) if not config_host_data.get('CONFIG_ATOMIC64') util_ss.add(files('atomic64.c')) endif -util_ss.add(when: 'CONFIG_POSIX', if_true: files('aio-posix.c')) -util_ss.add(when: 'CONFIG_POSIX', if_true: files('fdmon-poll.c')) -if config_host_data.get('CONFIG_EPOLL_CREATE1') - util_ss.add(files('fdmon-epoll.c')) +if host_os != 'windows' + util_ss.add(files('aio-posix.c')) + util_ss.add(files('fdmon-poll.c')) + if config_host_data.get('CONFIG_EPOLL_CREATE1') + util_ss.add(files('fdmon-epoll.c')) + endif + util_ss.add(files('compatfd.c')) + util_ss.add(files('event_notifier-posix.c')) + util_ss.add(files('mmap-alloc.c')) + freebsd_dep = [] + if host_os == 'freebsd' + freebsd_dep = util + endif + util_ss.add(files('oslib-posix.c'), freebsd_dep) + util_ss.add(files('qemu-thread-posix.c')) + util_ss.add(files('memfd.c')) + util_ss.add(files('drm.c')) +else + util_ss.add(files('aio-win32.c')) + util_ss.add(files('event_notifier-win32.c')) + util_ss.add(files('oslib-win32.c')) + util_ss.add(files('qemu-thread-win32.c')) + util_ss.add(winmm, pathcch) endif util_ss.add(when: linux_io_uring, if_true: files('fdmon-io_uring.c')) -util_ss.add(when: 'CONFIG_POSIX', if_true: files('compatfd.c')) -util_ss.add(when: 'CONFIG_POSIX', if_true: files('event_notifier-posix.c')) -util_ss.add(when: 'CONFIG_POSIX', if_true: files('mmap-alloc.c')) -freebsd_dep = [] -if targetos == 'freebsd' - freebsd_dep = util +if glib_has_gslice + util_ss.add(files('qtree.c')) endif -util_ss.add(when: 'CONFIG_POSIX', if_true: [files('oslib-posix.c'), freebsd_dep]) -util_ss.add(when: 'CONFIG_POSIX', if_true: files('qemu-thread-posix.c')) -util_ss.add(when: 'CONFIG_POSIX', if_true: files('memfd.c')) -util_ss.add(when: 'CONFIG_WIN32', if_true: files('aio-win32.c')) -util_ss.add(when: 'CONFIG_WIN32', if_true: files('event_notifier-win32.c')) -util_ss.add(when: 'CONFIG_WIN32', if_true: files('oslib-win32.c')) -util_ss.add(when: 'CONFIG_WIN32', if_true: files('qemu-thread-win32.c')) -util_ss.add(when: 'CONFIG_WIN32', if_true: winmm) +util_ss.add(files('defer-call.c')) util_ss.add(files('envlist.c', 'path.c', 'module.c')) util_ss.add(files('host-utils.c')) util_ss.add(files('bitmap.c', 'bitops.c')) util_ss.add(files('fifo8.c')) -util_ss.add(files('cacheinfo.c', 'cacheflush.c')) +util_ss.add(files('cacheflush.c')) util_ss.add(files('error.c', 'error-report.c')) util_ss.add(files('qemu-print.c')) util_ss.add(files('id.c')) @@ -46,14 +55,16 @@ util_ss.add(files('qdist.c')) util_ss.add(files('qht.c')) util_ss.add(files('qsp.c')) util_ss.add(files('range.c')) +util_ss.add(files('reserved-region.c')) util_ss.add(files('stats64.c')) util_ss.add(files('systemd.c')) util_ss.add(files('transactions.c')) -util_ss.add(when: 'CONFIG_POSIX', if_true: files('drm.c')) util_ss.add(files('guest-random.c')) util_ss.add(files('yank.c')) util_ss.add(files('int128.c')) util_ss.add(files('memalign.c')) +util_ss.add(files('interval-tree.c')) +util_ss.add(files('lockcnt.c')) if have_user util_ss.add(files('selfmap.c')) @@ -62,37 +73,58 @@ endif if have_system util_ss.add(files('crc-ccitt.c')) util_ss.add(when: gio, if_true: files('dbus.c')) - util_ss.add(when: 'CONFIG_LINUX', if_true: files('userfaultfd.c')) + if host_os == 'linux' + util_ss.add(files('userfaultfd.c')) + endif endif -if have_block - util_ss.add(files('aiocb.c', 'async.c', 'aio-wait.c')) +if have_block or have_ga + util_ss.add(files('aiocb.c', 'async.c')) util_ss.add(files('base64.c')) + util_ss.add(files('main-loop.c')) + util_ss.add(files('qemu-coroutine.c', 'qemu-coroutine-lock.c', 'qemu-coroutine-io.c')) + util_ss.add(files(f'coroutine-@coroutine_backend@.c')) + util_ss.add(files('thread-pool.c', 'qemu-timer.c')) + util_ss.add(files('qemu-sockets.c')) +endif +if have_block + util_ss.add(files('aio-wait.c')) util_ss.add(files('buffer.c')) util_ss.add(files('bufferiszero.c')) - util_ss.add(files('coroutine-@0@.c'.format(config_host['CONFIG_COROUTINE_BACKEND']))) util_ss.add(files('hbitmap.c')) util_ss.add(files('hexdump.c')) util_ss.add(files('iova-tree.c')) - util_ss.add(files('iov.c', 'qemu-sockets.c', 'uri.c')) - util_ss.add(files('lockcnt.c')) - util_ss.add(files('main-loop.c')) + util_ss.add(files('iov.c', 'uri.c')) util_ss.add(files('nvdimm-utils.c')) - util_ss.add(files('qemu-coroutine.c', 'qemu-coroutine-lock.c', 'qemu-coroutine-io.c')) - util_ss.add(when: 'CONFIG_LINUX', if_true: [ - files('vhost-user-server.c'), vhost_user - ]) util_ss.add(files('block-helpers.c')) util_ss.add(files('qemu-coroutine-sleep.c')) util_ss.add(files('qemu-co-shared-resource.c')) - util_ss.add(files('thread-pool.c', 'qemu-timer.c')) + util_ss.add(files('qemu-co-timeout.c')) util_ss.add(files('readline.c')) util_ss.add(files('throttle.c')) util_ss.add(files('timed-average.c')) if config_host_data.get('CONFIG_INOTIFY1') - util_ss.add(files('filemonitor-inotify.c')) + freebsd_dep = [] + if host_os == 'freebsd' + freebsd_dep = inotify + endif + util_ss.add(files('filemonitor-inotify.c'), freebsd_dep) else util_ss.add(files('filemonitor-stub.c')) endif - util_ss.add(when: 'CONFIG_LINUX', if_true: files('vfio-helpers.c')) + if host_os == 'linux' + util_ss.add(files('vhost-user-server.c'), vhost_user) + util_ss.add(files('vfio-helpers.c')) + util_ss.add(files('chardev_open.c')) + endif +endif + +if cpu == 'aarch64' + util_ss.add(files('cpuinfo-aarch64.c')) +elif cpu in ['x86', 'x86_64'] + util_ss.add(files('cpuinfo-i386.c')) +elif cpu == 'loongarch64' + util_ss.add(files('cpuinfo-loongarch.c')) +elif cpu in ['ppc', 'ppc64'] + util_ss.add(files('cpuinfo-ppc.c')) endif diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index 5b90cb68ea..ed14f9c64d 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -27,8 +27,36 @@ #ifdef CONFIG_LINUX #include +#include #endif +QemuFsType qemu_fd_getfs(int fd) +{ +#ifdef CONFIG_LINUX + struct statfs fs; + int ret; + + if (fd < 0) { + return QEMU_FS_TYPE_UNKNOWN; + } + + do { + ret = fstatfs(fd, &fs); + } while (ret != 0 && errno == EINTR); + + switch (fs.f_type) { + case TMPFS_MAGIC: + return QEMU_FS_TYPE_TMPFS; + case HUGETLBFS_MAGIC: + return QEMU_FS_TYPE_HUGETLBFS; + default: + return QEMU_FS_TYPE_UNKNOWN; + } +#else + return QEMU_FS_TYPE_UNKNOWN; +#endif +} + size_t qemu_fd_getpagesize(int fd) { #ifdef CONFIG_LINUX @@ -53,37 +81,6 @@ size_t qemu_fd_getpagesize(int fd) return qemu_real_host_page_size(); } -size_t qemu_mempath_getpagesize(const char *mem_path) -{ -#ifdef CONFIG_LINUX - struct statfs fs; - int ret; - - if (mem_path) { - do { - ret = statfs(mem_path, &fs); - } while (ret != 0 && errno == EINTR); - - if (ret != 0) { - fprintf(stderr, "Couldn't statfs() memory path: %s\n", - strerror(errno)); - exit(1); - } - - if (fs.f_type == HUGETLBFS_MAGIC) { - /* It's hugepage, return the huge page size */ - return fs.f_bsize; - } - } -#ifdef __sparc__ - /* SPARC Linux needs greater alignment than the pagesize */ - return QEMU_VMALLOC_ALIGN; -#endif -#endif - - return qemu_real_host_page_size(); -} - #define OVERCOMMIT_MEMORY_PATH "/proc/sys/vm/overcommit_memory" static bool map_noreserve_effective(int fd, uint32_t qemu_map_flags) { diff --git a/util/module.c b/util/module.c index 6bb4ad915a..32e263163c 100644 --- a/util/module.c +++ b/util/module.c @@ -21,6 +21,7 @@ #include "qemu/module.h" #include "qemu/cutils.h" #include "qemu/config-file.h" +#include "qapi/error.h" #ifdef CONFIG_MODULE_UPGRADES #include "qemu-version.h" #endif @@ -144,25 +145,22 @@ static bool module_check_arch(const QemuModinfo *modinfo) return true; } -static int module_load_file(const char *fname, bool mayfail, bool export_symbols) +/* + * module_load_dso: attempt to load an existing dso file + * + * fname: full pathname of the file to load + * export_symbols: if true, add the symbols to the global name space + * errp: error to set. + * + * Return value: true on success, false on error, and errp will be set. + */ +static bool module_load_dso(const char *fname, bool export_symbols, + Error **errp) { GModule *g_module; void (*sym)(void); - const char *dsosuf = CONFIG_HOST_DSOSUF; - int len = strlen(fname); - int suf_len = strlen(dsosuf); ModuleEntry *e, *next; - int ret, flags; - - if (len <= suf_len || strcmp(&fname[len - suf_len], dsosuf)) { - /* wrong suffix */ - ret = -EINVAL; - goto out; - } - if (access(fname, F_OK)) { - ret = -ENOENT; - goto out; - } + int flags; assert(QTAILQ_EMPTY(&dso_init_list)); @@ -172,48 +170,38 @@ static int module_load_file(const char *fname, bool mayfail, bool export_symbols } g_module = g_module_open(fname, flags); if (!g_module) { - if (!mayfail) { - fprintf(stderr, "Failed to open module: %s\n", - g_module_error()); - } - ret = -EINVAL; - goto out; + error_setg(errp, "failed to open module: %s", g_module_error()); + return false; } if (!g_module_symbol(g_module, DSO_STAMP_FUN_STR, (gpointer *)&sym)) { - fprintf(stderr, "Failed to initialize module: %s\n", - fname); - /* Print some info if this is a QEMU module (but from different build), - * this will make debugging user problems easier. */ + error_setg(errp, "failed to initialize module: %s", fname); + /* + * Print some info if this is a QEMU module (but from different build), + * this will make debugging user problems easier. + */ if (g_module_symbol(g_module, "qemu_module_dummy", (gpointer *)&sym)) { - fprintf(stderr, - "Note: only modules from the same build can be loaded.\n"); + error_append_hint(errp, + "Only modules from the same build can be loaded.\n"); } g_module_close(g_module); - ret = -EINVAL; - } else { - QTAILQ_FOREACH(e, &dso_init_list, node) { - e->init(); - register_module_init(e->init, e->type); - } - ret = 0; + return false; } + QTAILQ_FOREACH(e, &dso_init_list, node) { + e->init(); + register_module_init(e->init, e->type); + } trace_module_load_module(fname); QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) { QTAILQ_REMOVE(&dso_init_list, e, node); g_free(e); } -out: - return ret; + return true; } -#endif -bool module_load_one(const char *prefix, const char *lib_name, bool mayfail) +int module_load(const char *prefix, const char *name, Error **errp) { - bool success = false; - -#ifdef CONFIG_MODULES - char *fname = NULL; + int rv = -1; #ifdef CONFIG_MODULE_UPGRADES char *version_dir; #endif @@ -221,34 +209,52 @@ bool module_load_one(const char *prefix, const char *lib_name, bool mayfail) char *dirs[5]; char *module_name; int i = 0, n_dirs = 0; - int ret; bool export_symbols = false; static GHashTable *loaded_modules; const QemuModinfo *modinfo; const char **sl; if (!g_module_supported()) { - fprintf(stderr, "Module is not supported by system.\n"); - return false; + error_setg(errp, "%s", "this platform does not support GLib modules"); + return -1; } if (!loaded_modules) { loaded_modules = g_hash_table_new(g_str_hash, g_str_equal); } - module_name = g_strdup_printf("%s%s", prefix, lib_name); + /* allocate all resources managed by the out: label here */ + module_name = g_strdup_printf("%s%s", prefix, name); if (g_hash_table_contains(loaded_modules, module_name)) { g_free(module_name); - return true; + return 2; /* module already loaded */ } g_hash_table_add(loaded_modules, module_name); + search_dir = getenv("QEMU_MODULE_DIR"); + if (search_dir != NULL) { + dirs[n_dirs++] = g_strdup_printf("%s", search_dir); + } + dirs[n_dirs++] = get_relocated_path(CONFIG_QEMU_MODDIR); + +#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)); + + /* end of resources managed by the out: label */ + for (modinfo = module_info; modinfo->name != NULL; modinfo++) { if (modinfo->arch) { if (strcmp(modinfo->name, module_name) == 0) { if (!module_check_arch(modinfo)) { - return false; + error_setg(errp, "module arch does not match: " + "expected '%s', got '%s'", module_arch, modinfo->arch); + goto out; } } } @@ -256,7 +262,11 @@ bool module_load_one(const char *prefix, const char *lib_name, bool mayfail) if (strcmp(modinfo->name, module_name) == 0) { /* we depend on other module(s) */ for (sl = modinfo->deps; *sl != NULL; sl++) { - module_load_one("", *sl, false); + int subrv = module_load("", *sl, errp); + if (subrv <= 0) { + rv = subrv; + goto out; + } } } else { for (sl = modinfo->deps; *sl != NULL; sl++) { @@ -269,59 +279,52 @@ bool module_load_one(const char *prefix, const char *lib_name, bool mayfail) } } - search_dir = getenv("QEMU_MODULE_DIR"); - if (search_dir != NULL) { - dirs[n_dirs++] = g_strdup_printf("%s", search_dir); - } - dirs[n_dirs++] = get_relocated_path(CONFIG_QEMU_MODDIR); - dirs[n_dirs++] = g_strdup(qemu_get_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)); - for (i = 0; i < n_dirs; i++) { - fname = g_strdup_printf("%s/%s%s", - dirs[i], module_name, CONFIG_HOST_DSOSUF); - ret = module_load_file(fname, mayfail, export_symbols); - g_free(fname); - fname = NULL; - /* Try loading until loaded a module file */ - if (!ret) { - success = true; - break; + char *fname = g_strdup_printf("%s/%s%s", + dirs[i], module_name, CONFIG_HOST_DSOSUF); + int ret = access(fname, F_OK); + if (ret != 0 && (errno == ENOENT || errno == ENOTDIR)) { + /* + * if we don't find the module in this dir, try the next one. + * If we don't find it in any dir, that can be fine too: user + * did not install the module. We will return 0 in this case + * with no error set. + */ + g_free(fname); + continue; + } else if (ret != 0) { + /* most common is EACCES here */ + error_setg_errno(errp, errno, "error trying to access %s", fname); + } else if (module_load_dso(fname, export_symbols, errp)) { + rv = 1; /* module successfully loaded */ } + g_free(fname); + goto out; } + rv = 0; /* module not found */ - if (!success) { +out: + if (rv <= 0) { g_hash_table_remove(loaded_modules, module_name); g_free(module_name); } - for (i = 0; i < n_dirs; i++) { g_free(dirs[i]); } - -#endif - return success; + return rv; } -#ifdef CONFIG_MODULES - static bool module_loaded_qom_all; -void module_load_qom_one(const char *type) +int module_load_qom(const char *type, Error **errp) { const QemuModinfo *modinfo; const char **sl; + int rv = 0; if (!type) { - return; + error_setg(errp, "%s", "type is NULL"); + return -1; } trace_module_lookup_object_type(type); @@ -334,15 +337,24 @@ void module_load_qom_one(const char *type) } for (sl = modinfo->objs; *sl != NULL; sl++) { if (strcmp(type, *sl) == 0) { - module_load_one("", modinfo->name, false); + if (rv > 0) { + error_setg(errp, "multiple modules providing '%s'", type); + return -1; + } + rv = module_load("", modinfo->name, errp); + if (rv < 0) { + return rv; + } } } } + return rv; } void module_load_qom_all(void) { const QemuModinfo *modinfo; + Error *local_err = NULL; if (module_loaded_qom_all) { return; @@ -355,7 +367,9 @@ void module_load_qom_all(void) if (!module_check_arch(modinfo)) { continue; } - module_load_one("", modinfo->name, false); + if (module_load("", modinfo->name, &local_err) < 0) { + error_report_err(local_err); + } } module_loaded_qom_all = true; } @@ -371,7 +385,10 @@ void qemu_load_module_for_opts(const char *group) } for (sl = modinfo->opts; *sl != NULL; sl++) { if (strcmp(group, *sl) == 0) { - module_load_one("", modinfo->name, false); + Error *local_err = NULL; + if (module_load("", modinfo->name, &local_err) < 0) { + error_report_err(local_err); + } } } } @@ -381,7 +398,8 @@ void qemu_load_module_for_opts(const char *group) void module_allow_arch(const char *arch) {} void qemu_load_module_for_opts(const char *group) {} -void module_load_qom_one(const char *type) {} +int module_load(const char *prefix, const char *name, Error **errp) { return 2; } +int module_load_qom(const char *type, Error **errp) { return 2; } void module_load_qom_all(void) {} #endif diff --git a/util/notify.c b/util/notify.c index 76bab212ae..c6e158ffb3 100644 --- a/util/notify.c +++ b/util/notify.c @@ -61,13 +61,14 @@ void notifier_with_return_remove(NotifierWithReturn *notifier) QLIST_REMOVE(notifier, node); } -int notifier_with_return_list_notify(NotifierWithReturnList *list, void *data) +int notifier_with_return_list_notify(NotifierWithReturnList *list, void *data, + Error **errp) { NotifierWithReturn *notifier, *next; int ret = 0; QLIST_FOREACH_SAFE(notifier, &list->notifiers, node, next) { - ret = notifier->notify(notifier, data); + ret = notifier->notify(notifier, data, errp); if (ret != 0) { break; } diff --git a/util/osdep.c b/util/osdep.c index 60fcbbaebe..e996c4744a 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -244,9 +244,7 @@ static int qemu_lock_fcntl(int fd, int64_t start, int64_t len, int fl_type) .l_type = fl_type, }; qemu_probe_lock_ops(); - do { - ret = fcntl(fd, fcntl_op_setlk, &fl); - } while (ret == -1 && errno == EINTR); + ret = RETRY_ON_EINTR(fcntl(fd, fcntl_op_setlk, &fl)); return ret == -1 ? -errno : 0; } @@ -502,6 +500,28 @@ int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen) return ret; } +ssize_t qemu_send_full(int s, const void *buf, size_t count) +{ + ssize_t ret = 0; + ssize_t total = 0; + + while (count) { + ret = send(s, buf, count, 0); + if (ret < 0) { + if (errno == EINTR) { + continue; + } + break; + } + + count -= ret; + buf += ret; + total += ret; + } + + return total; +} + void qemu_set_hw_version(const char *version) { hw_version = version; @@ -538,18 +558,22 @@ int socket_init(void) #ifndef CONFIG_IOVEC -/* helper function for iov_send_recv() */ static ssize_t readv_writev(int fd, const struct iovec *iov, int iov_cnt, bool do_write) { unsigned i = 0; ssize_t ret = 0; + ssize_t off = 0; while (i < iov_cnt) { ssize_t r = do_write - ? write(fd, iov[i].iov_base, iov[i].iov_len) - : read(fd, iov[i].iov_base, iov[i].iov_len); + ? write(fd, iov[i].iov_base + off, iov[i].iov_len - off) + : read(fd, iov[i].iov_base + off, iov[i].iov_len - off); if (r > 0) { ret += r; + off += r; + if (off < iov[i].iov_len) { + continue; + } } else if (!r) { break; } else if (errno == EINTR) { @@ -562,6 +586,7 @@ readv_writev(int fd, const struct iovec *iov, int iov_cnt, bool do_write) } break; } + off = 0; i++; } return ret; diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 7a34c1657c..e76441695b 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -40,8 +40,9 @@ #include "qemu/thread.h" #include #include "qemu/cutils.h" -#include "qemu/compiler.h" #include "qemu/units.h" +#include "qemu/thread-context.h" +#include "qemu/main-loop.h" #ifdef CONFIG_LINUX #include @@ -49,7 +50,6 @@ #ifdef __FreeBSD__ #include -#include #include #include #endif @@ -58,29 +58,21 @@ #include #endif -#ifdef __APPLE__ -#include -#endif - -#ifdef __HAIKU__ -#include -#endif - #include "qemu/mmap-alloc.h" -#ifdef CONFIG_DEBUG_STACK_USAGE -#include "qemu/error-report.h" -#endif - #define MAX_MEM_PREALLOC_THREAD_COUNT 16 struct MemsetThread; +static QLIST_HEAD(, MemsetContext) memset_contexts = + QLIST_HEAD_INITIALIZER(memset_contexts); + typedef struct MemsetContext { bool all_threads_created; bool any_thread_failed; struct MemsetThread *threads; int num_threads; + QLIST_ENTRY(MemsetContext) next; } MemsetContext; struct MemsetThread { @@ -261,6 +253,25 @@ void qemu_set_cloexec(int fd) assert(f != -1); } +int qemu_socketpair(int domain, int type, int protocol, int sv[2]) +{ + int ret; + +#ifdef SOCK_CLOEXEC + ret = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv); + if (ret != -1 || errno != EINVAL) { + return ret; + } +#endif + ret = socketpair(domain, type, protocol, sv);; + if (ret == 0) { + qemu_set_cloexec(sv[0]); + qemu_set_cloexec(sv[1]); + } + + return ret; +} + char * qemu_get_local_state_dir(void) { @@ -318,7 +329,7 @@ static void sigbus_handler(int signal) return; } #endif /* CONFIG_LINUX */ - warn_report("os_mem_prealloc: unrelated SIGBUS detected and ignored"); + warn_report("qemu_prealloc_mem: unrelated SIGBUS detected and ignored"); } static void *do_touch_pages(void *arg) @@ -388,13 +399,13 @@ static void *do_madv_populate_write_pages(void *arg) } static inline int get_memset_num_threads(size_t hpagesize, size_t numpages, - int smp_cpus) + int max_threads) { long host_procs = sysconf(_SC_NPROCESSORS_ONLN); int ret = 1; if (host_procs > 0) { - ret = MIN(MIN(host_procs, MAX_MEM_PREALLOC_THREAD_COUNT), smp_cpus); + ret = MIN(MIN(host_procs, MAX_MEM_PREALLOC_THREAD_COUNT), max_threads); } /* Especially with gigantic pages, don't create more threads than pages. */ @@ -406,18 +417,44 @@ static inline int get_memset_num_threads(size_t hpagesize, size_t numpages, return ret; } +static int wait_and_free_mem_prealloc_context(MemsetContext *context) +{ + int i, ret = 0, tmp; + + for (i = 0; i < context->num_threads; i++) { + tmp = (uintptr_t)qemu_thread_join(&context->threads[i].pgthread); + + if (tmp) { + ret = tmp; + } + } + g_free(context->threads); + g_free(context); + return ret; +} + static int touch_all_pages(char *area, size_t hpagesize, size_t numpages, - int smp_cpus, bool use_madv_populate_write) + int max_threads, ThreadContext *tc, bool async, + bool use_madv_populate_write) { static gsize initialized = 0; - MemsetContext context = { - .num_threads = get_memset_num_threads(hpagesize, numpages, smp_cpus), - }; + MemsetContext *context = g_malloc0(sizeof(MemsetContext)); size_t numpages_per_thread, leftover; void *(*touch_fn)(void *); - int ret = 0, i = 0; + int ret, i = 0; char *addr = area; + /* + * Asynchronous preallocation is only allowed when using MADV_POPULATE_WRITE + * and prealloc context for thread placement. + */ + if (!use_madv_populate_write || !tc) { + async = false; + } + + context->num_threads = + get_memset_num_threads(hpagesize, numpages, max_threads); + if (g_once_init_enter(&initialized)) { qemu_mutex_init(&page_mutex); qemu_cond_init(&page_cond); @@ -425,56 +462,104 @@ static int touch_all_pages(char *area, size_t hpagesize, size_t numpages, } if (use_madv_populate_write) { - /* Avoid creating a single thread for MADV_POPULATE_WRITE */ - if (context.num_threads == 1) { + /* + * Avoid creating a single thread for MADV_POPULATE_WRITE when + * preallocating synchronously. + */ + if (context->num_threads == 1 && !async) { + ret = 0; if (qemu_madvise(area, hpagesize * numpages, QEMU_MADV_POPULATE_WRITE)) { - return -errno; + ret = -errno; } - return 0; + g_free(context); + return ret; } touch_fn = do_madv_populate_write_pages; } else { touch_fn = do_touch_pages; } - context.threads = g_new0(MemsetThread, context.num_threads); - numpages_per_thread = numpages / context.num_threads; - leftover = numpages % context.num_threads; - for (i = 0; i < context.num_threads; i++) { - context.threads[i].addr = addr; - context.threads[i].numpages = numpages_per_thread + (i < leftover); - context.threads[i].hpagesize = hpagesize; - context.threads[i].context = &context; - qemu_thread_create(&context.threads[i].pgthread, "touch_pages", - touch_fn, &context.threads[i], - QEMU_THREAD_JOINABLE); - addr += context.threads[i].numpages * hpagesize; + context->threads = g_new0(MemsetThread, context->num_threads); + numpages_per_thread = numpages / context->num_threads; + leftover = numpages % context->num_threads; + for (i = 0; i < context->num_threads; i++) { + context->threads[i].addr = addr; + context->threads[i].numpages = numpages_per_thread + (i < leftover); + context->threads[i].hpagesize = hpagesize; + context->threads[i].context = context; + if (tc) { + thread_context_create_thread(tc, &context->threads[i].pgthread, + "touch_pages", + touch_fn, &context->threads[i], + QEMU_THREAD_JOINABLE); + } else { + qemu_thread_create(&context->threads[i].pgthread, "touch_pages", + touch_fn, &context->threads[i], + QEMU_THREAD_JOINABLE); + } + addr += context->threads[i].numpages * hpagesize; + } + + if (async) { + /* + * async requests currently require the BQL. Add it to the list and kick + * preallocation off during qemu_finish_async_prealloc_mem(). + */ + assert(bql_locked()); + QLIST_INSERT_HEAD(&memset_contexts, context, next); + return 0; } if (!use_madv_populate_write) { - sigbus_memset_context = &context; + sigbus_memset_context = context; } qemu_mutex_lock(&page_mutex); - context.all_threads_created = true; + context->all_threads_created = true; qemu_cond_broadcast(&page_cond); qemu_mutex_unlock(&page_mutex); - for (i = 0; i < context.num_threads; i++) { - int tmp = (uintptr_t)qemu_thread_join(&context.threads[i].pgthread); + ret = wait_and_free_mem_prealloc_context(context); + if (!use_madv_populate_write) { + sigbus_memset_context = NULL; + } + return ret; +} + +bool qemu_finish_async_prealloc_mem(Error **errp) +{ + int ret = 0, tmp; + MemsetContext *context, *next_context; + + /* Waiting for preallocation requires the BQL. */ + assert(bql_locked()); + if (QLIST_EMPTY(&memset_contexts)) { + return true; + } + + qemu_mutex_lock(&page_mutex); + QLIST_FOREACH(context, &memset_contexts, next) { + context->all_threads_created = true; + } + qemu_cond_broadcast(&page_cond); + qemu_mutex_unlock(&page_mutex); + + QLIST_FOREACH_SAFE(context, &memset_contexts, next, next_context) { + QLIST_REMOVE(context, next); + tmp = wait_and_free_mem_prealloc_context(context); if (tmp) { ret = tmp; } } - if (!use_madv_populate_write) { - sigbus_memset_context = NULL; + if (ret) { + error_setg_errno(errp, -ret, + "qemu_prealloc_mem: preallocating memory failed"); + return false; } - g_free(context.threads); - - return ret; + return true; } static bool madv_populate_write_possible(char *area, size_t pagesize) @@ -483,15 +568,16 @@ static bool madv_populate_write_possible(char *area, size_t pagesize) errno != EINVAL; } -void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus, - Error **errp) +bool qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, + ThreadContext *tc, bool async, Error **errp) { static gsize initialized; int ret; size_t hpagesize = qemu_fd_getpagesize(fd); - size_t numpages = DIV_ROUND_UP(memory, hpagesize); + size_t numpages = DIV_ROUND_UP(sz, hpagesize); bool use_madv_populate_write; struct sigaction act; + bool rv = true; /* * Sense on every invocation, as MADV_POPULATE_WRITE cannot be used for @@ -519,28 +605,30 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus, if (ret) { qemu_mutex_unlock(&sigbus_mutex); error_setg_errno(errp, errno, - "os_mem_prealloc: failed to install signal handler"); - return; + "qemu_prealloc_mem: failed to install signal handler"); + return false; } } /* touch pages simultaneously */ - ret = touch_all_pages(area, hpagesize, numpages, smp_cpus, + ret = touch_all_pages(area, hpagesize, numpages, max_threads, tc, async, use_madv_populate_write); if (ret) { error_setg_errno(errp, -ret, - "os_mem_prealloc: preallocating memory failed"); + "qemu_prealloc_mem: preallocating memory failed"); + rv = false; } if (!use_madv_populate_write) { ret = sigaction(SIGBUS, &sigbus_oldact, NULL); if (ret) { /* Terminate QEMU since it can't recover from error */ - perror("os_mem_prealloc: failed to reinstall signal handler"); + perror("qemu_prealloc_mem: failed to reinstall signal handler"); exit(1); } qemu_mutex_unlock(&sigbus_mutex); } + return rv; } char *qemu_get_pid_name(pid_t pid) @@ -569,79 +657,9 @@ char *qemu_get_pid_name(pid_t pid) } -pid_t qemu_fork(Error **errp) -{ - sigset_t oldmask, newmask; - struct sigaction sig_action; - int saved_errno; - pid_t pid; - - /* - * Need to block signals now, so that child process can safely - * kill off caller's signal handlers without a race. - */ - sigfillset(&newmask); - if (pthread_sigmask(SIG_SETMASK, &newmask, &oldmask) != 0) { - error_setg_errno(errp, errno, - "cannot block signals"); - return -1; - } - - pid = fork(); - saved_errno = errno; - - if (pid < 0) { - /* attempt to restore signal mask, but ignore failure, to - * avoid obscuring the fork failure */ - (void)pthread_sigmask(SIG_SETMASK, &oldmask, NULL); - error_setg_errno(errp, saved_errno, - "cannot fork child process"); - errno = saved_errno; - return -1; - } else if (pid) { - /* parent process */ - - /* Restore our original signal mask now that the child is - * safely running. Only documented failures are EFAULT (not - * possible, since we are using just-grabbed mask) or EINVAL - * (not possible, since we are using correct arguments). */ - (void)pthread_sigmask(SIG_SETMASK, &oldmask, NULL); - } else { - /* child process */ - size_t i; - - /* Clear out all signal handlers from parent so nothing - * unexpected can happen in our child once we unblock - * signals */ - sig_action.sa_handler = SIG_DFL; - sig_action.sa_flags = 0; - sigemptyset(&sig_action.sa_mask); - - for (i = 1; i < NSIG; i++) { - /* Only possible errors are EFAULT or EINVAL The former - * won't happen, the latter we expect, so no need to check - * return value */ - (void)sigaction(i, &sig_action, NULL); - } - - /* Unmask all signals in child, since we've no idea what the - * caller's done with their signal mask and don't want to - * propagate that to children */ - sigemptyset(&newmask); - if (pthread_sigmask(SIG_SETMASK, &newmask, NULL) != 0) { - Error *local_err = NULL; - error_setg_errno(&local_err, errno, - "cannot unblock signals"); - error_report_err(local_err); - _exit(1); - } - } - return pid; -} - void *qemu_alloc_stack(size_t *sz) { - void *ptr, *guardpage; + void *ptr; int flags; #ifdef CONFIG_DEBUG_STACK_USAGE void *ptr2; @@ -674,17 +692,8 @@ void *qemu_alloc_stack(size_t *sz) abort(); } -#if defined(HOST_IA64) - /* separate register stack */ - guardpage = ptr + (((*sz - pagesz) / 2) & ~pagesz); -#elif defined(HOST_HPPA) - /* stack grows up */ - guardpage = ptr + *sz - pagesz; -#else - /* stack grows down */ - guardpage = ptr; -#endif - if (mprotect(guardpage, pagesz, PROT_NONE) != 0) { + /* Stack grows down -- guard page at the bottom. */ + if (mprotect(ptr, pagesz, PROT_NONE) != 0) { perror("failed to set up stack guard page"); abort(); } @@ -727,7 +736,7 @@ void qemu_free_stack(void *stack, size_t sz) /* * Disable CFI checks. - * We are going to call a signal hander directly. Such handler may or may not + * We are going to call a signal handler directly. Such handler may or may not * have been defined in our binary, so there's no guarantee that the pointer * used to set the handler is a cfi-valid pointer. Since the handlers are * stored in kernel memory, changing the handler to an attacker-defined diff --git a/util/oslib-win32.c b/util/oslib-win32.c index 5723d3eb4c..b623830d62 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -24,10 +24,6 @@ * 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. - * - * The implementation of g_poll (functions poll_rest, g_poll) at the end of - * this file are based on code from GNOME glib-2 and use a different license, - * see the license comment there. */ #include "qemu/osdep.h" @@ -184,7 +180,7 @@ static int socket_error(void) void qemu_socket_set_block(int fd) { unsigned long opt = 0; - WSAEventSelect(fd, NULL, 0); + qemu_socket_unselect(fd, NULL); ioctlsocket(fd, FIONBIO, &opt); } @@ -268,16 +264,24 @@ int getpagesize(void) return system_info.dwPageSize; } -void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus, - Error **errp) +bool qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, + ThreadContext *tc, bool async, Error **errp) { int i; size_t pagesize = qemu_real_host_page_size(); - memory = (memory + pagesize - 1) & -pagesize; - for (i = 0; i < memory / pagesize; i++) { + sz = (sz + pagesize - 1) & -pagesize; + for (i = 0; i < sz / pagesize; i++) { memset(area + pagesize * i, 0, 1); } + + return true; +} + +bool qemu_finish_async_prealloc_mem(Error **errp) +{ + /* async prealloc not supported, there is nothing to finish */ + return true; } char *qemu_get_pid_name(pid_t pid) @@ -287,21 +291,155 @@ char *qemu_get_pid_name(pid_t pid) } -pid_t qemu_fork(Error **errp) +bool qemu_socket_select(int sockfd, WSAEVENT hEventObject, + long lNetworkEvents, Error **errp) { - errno = ENOSYS; - error_setg_errno(errp, errno, - "cannot fork child process"); - return -1; + SOCKET s = _get_osfhandle(sockfd); + + if (errp == NULL) { + errp = &error_warn; + } + + if (s == INVALID_SOCKET) { + error_setg(errp, "invalid socket fd=%d", sockfd); + return false; + } + + if (WSAEventSelect(s, hEventObject, lNetworkEvents) != 0) { + error_setg_win32(errp, WSAGetLastError(), "failed to WSAEventSelect()"); + return false; + } + + return true; } +bool qemu_socket_unselect(int sockfd, Error **errp) +{ + return qemu_socket_select(sockfd, NULL, 0, errp); +} + +int qemu_socketpair(int domain, int type, int protocol, int sv[2]) +{ + struct sockaddr_un addr = { + 0, + }; + socklen_t socklen; + int listener = -1; + int client = -1; + int server = -1; + g_autofree char *path = NULL; + int tmpfd; + u_long arg; + int ret = -1; + + g_return_val_if_fail(sv != NULL, -1); + + addr.sun_family = AF_UNIX; + socklen = sizeof(addr); + + tmpfd = g_file_open_tmp(NULL, &path, NULL); + if (tmpfd == -1 || !path) { + errno = EACCES; + goto out; + } + + close(tmpfd); + + if (strlen(path) >= sizeof(addr.sun_path)) { + errno = EINVAL; + goto out; + } + + strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + + listener = socket(domain, type, protocol); + if (listener == -1) { + goto out; + } + + if (DeleteFile(path) == 0 && GetLastError() != ERROR_FILE_NOT_FOUND) { + errno = EACCES; + goto out; + } + g_clear_pointer(&path, g_free); + + if (bind(listener, (struct sockaddr *)&addr, socklen) == -1) { + goto out; + } + + if (listen(listener, 1) == -1) { + goto out; + } + + client = socket(domain, type, protocol); + if (client == -1) { + goto out; + } + + arg = 1; + if (ioctlsocket(client, FIONBIO, &arg) != NO_ERROR) { + goto out; + } + + if (connect(client, (struct sockaddr *)&addr, socklen) == -1 && + WSAGetLastError() != WSAEWOULDBLOCK) { + goto out; + } + + server = accept(listener, NULL, NULL); + if (server == -1) { + goto out; + } + + arg = 0; + if (ioctlsocket(client, FIONBIO, &arg) != NO_ERROR) { + goto out; + } + + arg = 0; + if (ioctlsocket(client, SIO_AF_UNIX_GETPEERPID, &arg) != NO_ERROR) { + goto out; + } + + if (arg != GetCurrentProcessId()) { + errno = EPERM; + goto out; + } + + sv[0] = server; + server = -1; + sv[1] = client; + client = -1; + ret = 0; + +out: + if (listener != -1) { + close(listener); + } + if (client != -1) { + close(client); + } + if (server != -1) { + close(server); + } + if (path) { + DeleteFile(path); + } + return ret; +} #undef connect int qemu_connect_wrap(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { int ret; - ret = connect(sockfd, addr, addrlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = connect(s, addr, addrlen); if (ret < 0) { if (WSAGetLastError() == WSAEWOULDBLOCK) { errno = EINPROGRESS; @@ -317,7 +455,13 @@ int qemu_connect_wrap(int sockfd, const struct sockaddr *addr, int qemu_listen_wrap(int sockfd, int backlog) { int ret; - ret = listen(sockfd, backlog); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = listen(s, backlog); if (ret < 0) { errno = socket_error(); } @@ -330,23 +474,113 @@ int qemu_bind_wrap(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { int ret; - ret = bind(sockfd, addr, addrlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = bind(s, addr, addrlen); if (ret < 0) { errno = socket_error(); } return ret; } +QEMU_USED EXCEPTION_DISPOSITION +win32_close_exception_handler(struct _EXCEPTION_RECORD *exception_record, + void *registration, struct _CONTEXT *context, + void *dispatcher) +{ + return EXCEPTION_EXECUTE_HANDLER; +} + +#undef close +int qemu_close_socket_osfhandle(int fd) +{ + SOCKET s = _get_osfhandle(fd); + DWORD flags = 0; + + /* + * If we were to just call _close on the descriptor, it would close the + * HANDLE, but it wouldn't free any of the resources associated to the + * SOCKET, and we can't call _close after calling closesocket, because + * closesocket has already closed the HANDLE, and _close would attempt to + * close the HANDLE again, resulting in a double free. We can however + * protect the HANDLE from actually being closed long enough to close the + * file descriptor, then close the socket itself. + */ + if (!GetHandleInformation((HANDLE)s, &flags)) { + errno = EACCES; + return -1; + } + + if (!SetHandleInformation((HANDLE)s, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE)) { + errno = EACCES; + return -1; + } + + __try1(win32_close_exception_handler) { + /* + * close() returns EBADF since we PROTECT_FROM_CLOSE the underlying + * handle, but the FD is actually freed + */ + if (close(fd) < 0 && errno != EBADF) { + return -1; + } + } + __except1 { + } + + if (!SetHandleInformation((HANDLE)s, flags, flags)) { + errno = EACCES; + return -1; + } + + return 0; +} + +int qemu_close_wrap(int fd) +{ + SOCKET s = INVALID_SOCKET; + int ret = -1; + + if (!fd_is_socket(fd)) { + return close(fd); + } + + s = _get_osfhandle(fd); + qemu_close_socket_osfhandle(fd); + + ret = closesocket(s); + if (ret < 0) { + errno = socket_error(); + } + + return ret; +} + #undef socket int qemu_socket_wrap(int domain, int type, int protocol) { - int ret; - ret = socket(domain, type, protocol); - if (ret < 0) { + SOCKET s; + int fd; + + s = socket(domain, type, protocol); + if (s == -1) { errno = socket_error(); + return -1; } - return ret; + + fd = _open_osfhandle(s, _O_BINARY); + if (fd < 0) { + closesocket(s); + /* _open_osfhandle may not set errno, and closesocket() may override it */ + errno = ENOMEM; + } + + return fd; } @@ -354,12 +588,27 @@ int qemu_socket_wrap(int domain, int type, int protocol) int qemu_accept_wrap(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { - int ret; - ret = accept(sockfd, addr, addrlen); - if (ret < 0) { - errno = socket_error(); + int fd; + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; } - return ret; + + s = accept(s, addr, addrlen); + if (s == -1) { + errno = socket_error(); + return -1; + } + + fd = _open_osfhandle(s, _O_BINARY); + if (fd < 0) { + closesocket(s); + /* _open_osfhandle may not set errno, and closesocket() may override it */ + errno = ENOMEM; + } + + return fd; } @@ -367,7 +616,13 @@ int qemu_accept_wrap(int sockfd, struct sockaddr *addr, int qemu_shutdown_wrap(int sockfd, int how) { int ret; - ret = shutdown(sockfd, how); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = shutdown(s, how); if (ret < 0) { errno = socket_error(); } @@ -379,19 +634,13 @@ int qemu_shutdown_wrap(int sockfd, int how) int qemu_ioctlsocket_wrap(int fd, int req, void *val) { int ret; - ret = ioctlsocket(fd, req, val); - if (ret < 0) { - errno = socket_error(); + SOCKET s = _get_osfhandle(fd); + + if (s == INVALID_SOCKET) { + return -1; } - return ret; -} - -#undef closesocket -int qemu_closesocket_wrap(int fd) -{ - int ret; - ret = closesocket(fd); + ret = ioctlsocket(s, req, val); if (ret < 0) { errno = socket_error(); } @@ -404,7 +653,13 @@ int qemu_getsockopt_wrap(int sockfd, int level, int optname, void *optval, socklen_t *optlen) { int ret; - ret = getsockopt(sockfd, level, optname, optval, optlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = getsockopt(s, level, optname, optval, optlen); if (ret < 0) { errno = socket_error(); } @@ -417,7 +672,13 @@ int qemu_setsockopt_wrap(int sockfd, int level, int optname, const void *optval, socklen_t optlen) { int ret; - ret = setsockopt(sockfd, level, optname, optval, optlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = setsockopt(s, level, optname, optval, optlen); if (ret < 0) { errno = socket_error(); } @@ -430,7 +691,13 @@ int qemu_getpeername_wrap(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { int ret; - ret = getpeername(sockfd, addr, addrlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = getpeername(s, addr, addrlen); if (ret < 0) { errno = socket_error(); } @@ -443,7 +710,13 @@ int qemu_getsockname_wrap(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { int ret; - ret = getsockname(sockfd, addr, addrlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = getsockname(s, addr, addrlen); if (ret < 0) { errno = socket_error(); } @@ -455,7 +728,13 @@ int qemu_getsockname_wrap(int sockfd, struct sockaddr *addr, ssize_t qemu_send_wrap(int sockfd, const void *buf, size_t len, int flags) { int ret; - ret = send(sockfd, buf, len, flags); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = send(s, buf, len, flags); if (ret < 0) { errno = socket_error(); } @@ -468,7 +747,13 @@ ssize_t qemu_sendto_wrap(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen) { int ret; - ret = sendto(sockfd, buf, len, flags, addr, addrlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = sendto(s, buf, len, flags, addr, addrlen); if (ret < 0) { errno = socket_error(); } @@ -480,7 +765,13 @@ ssize_t qemu_sendto_wrap(int sockfd, const void *buf, size_t len, int flags, ssize_t qemu_recv_wrap(int sockfd, void *buf, size_t len, int flags) { int ret; - ret = recv(sockfd, buf, len, flags); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = recv(s, buf, len, flags); if (ret < 0) { errno = socket_error(); } @@ -493,7 +784,13 @@ ssize_t qemu_recvfrom_wrap(int sockfd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen) { int ret; - ret = recvfrom(sockfd, buf, len, flags, addr, addrlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = recvfrom(s, buf, len, flags, addr, addrlen); if (ret < 0) { errno = socket_error(); } @@ -547,3 +844,36 @@ int qemu_msync(void *addr, size_t length, int fd) */ return qemu_fdatasync(fd); } + +void *qemu_win32_map_alloc(size_t size, HANDLE *h, Error **errp) +{ + void *bits; + + trace_win32_map_alloc(size); + + *h = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, + size, NULL); + if (*h == NULL) { + error_setg_win32(errp, GetLastError(), "Failed to CreateFileMapping"); + return NULL; + } + + bits = MapViewOfFile(*h, FILE_MAP_ALL_ACCESS, 0, 0, size); + if (bits == NULL) { + error_setg_win32(errp, GetLastError(), "Failed to MapViewOfFile"); + CloseHandle(*h); + return NULL; + } + + return bits; +} + +void qemu_win32_map_free(void *ptr, HANDLE h, Error **errp) +{ + trace_win32_map_free(ptr, h); + + if (UnmapViewOfFile(ptr) == 0) { + error_setg_win32(errp, GetLastError(), "Failed to UnmapViewOfFile"); + } + CloseHandle(h); +} diff --git a/util/qdist.c b/util/qdist.c index 5f75e24c29..ef3566b03a 100644 --- a/util/qdist.c +++ b/util/qdist.c @@ -210,7 +210,7 @@ void qdist_bin__internal(struct qdist *to, const struct qdist *from, size_t n) /* * To avoid double-counting we capture [left, right) ranges, except for - * the righmost bin, which captures a [left, right] range. + * the rightmost bin, which captures a [left, right] range. */ while (j < from->n && (from->entries[j].x < right || i == n - 1)) { struct qdist_entry *o = &from->entries[j]; diff --git a/util/qemu-co-timeout.c b/util/qemu-co-timeout.c new file mode 100644 index 0000000000..00cd335649 --- /dev/null +++ b/util/qemu-co-timeout.c @@ -0,0 +1,89 @@ +/* + * Helper functionality for distributing a fixed total amount of + * an abstract resource among multiple coroutines. + * + * Copyright (c) 2022 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. + */ + +#include "qemu/osdep.h" +#include "qemu/coroutine.h" +#include "block/aio.h" + +typedef struct QemuCoTimeoutState { + CoroutineEntry *entry; + void *opaque; + QemuCoSleep sleep_state; + bool marker; + CleanupFunc *clean; +} QemuCoTimeoutState; + +static void coroutine_fn qemu_co_timeout_entry(void *opaque) +{ + QemuCoTimeoutState *s = opaque; + + s->entry(s->opaque); + + if (s->marker) { + assert(!s->sleep_state.to_wake); + /* .marker set by qemu_co_timeout, it have been failed */ + if (s->clean) { + s->clean(s->opaque); + } + g_free(s); + } else { + s->marker = true; + qemu_co_sleep_wake(&s->sleep_state); + } +} + +int coroutine_fn qemu_co_timeout(CoroutineEntry *entry, void *opaque, + uint64_t timeout_ns, CleanupFunc clean) +{ + QemuCoTimeoutState *s; + Coroutine *co; + + if (timeout_ns == 0) { + entry(opaque); + return 0; + } + + s = g_new(QemuCoTimeoutState, 1); + *s = (QemuCoTimeoutState) { + .entry = entry, + .opaque = opaque, + .clean = clean + }; + + co = qemu_coroutine_create(qemu_co_timeout_entry, s); + + aio_co_enter(qemu_get_current_aio_context(), co); + qemu_co_sleep_ns_wakeable(&s->sleep_state, QEMU_CLOCK_REALTIME, timeout_ns); + + if (s->marker) { + /* .marker set by qemu_co_timeout_entry, success */ + g_free(s); + return 0; + } + + /* Don't free s, as we can't cancel qemu_co_timeout_entry execution */ + s->marker = true; + return -ETIMEDOUT; +} diff --git a/util/qemu-config.c b/util/qemu-config.c index 433488aa56..42076efe1e 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -2,12 +2,12 @@ #include "block/qdict.h" /* for qdict_extract_subqdict() */ #include "qapi/error.h" #include "qapi/qapi-commands-misc.h" -#include "qapi/qmp/qerror.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/config-file.h" +#include "hw/boards.h" static QemuOptsList *vm_config_groups[48]; static QemuOptsList *drive_config_groups[5]; @@ -80,14 +80,8 @@ static CommandLineParameterInfoList *query_option_descs(const QemuOptDesc *desc) break; } - if (desc[i].help) { - info->has_help = true; - info->help = g_strdup(desc[i].help); - } - if (desc[i].def_value_str) { - info->has_q_default = true; - info->q_default = g_strdup(desc[i].def_value_str); - } + info->help = g_strdup(desc[i].help); + info->q_default = g_strdup(desc[i].def_value_str); QAPI_LIST_PREPEND(param_list, info); } @@ -149,100 +143,82 @@ static CommandLineParameterInfoList *get_drive_infolist(void) return head; } -/* restore machine options that are now machine's properties */ -static QemuOptsList machine_opts = { - .merge_lists = true, - .head = QTAILQ_HEAD_INITIALIZER(machine_opts.head), - .desc = { - { - .name = "type", - .type = QEMU_OPT_STRING, - .help = "emulated machine" - },{ - .name = "accel", - .type = QEMU_OPT_STRING, - .help = "accelerator list", - },{ - .name = "kernel_irqchip", - .type = QEMU_OPT_BOOL, - .help = "use KVM in-kernel irqchip", - },{ - .name = "kvm_shadow_mem", - .type = QEMU_OPT_SIZE, - .help = "KVM shadow MMU size", - },{ - .name = "kernel", - .type = QEMU_OPT_STRING, - .help = "Linux kernel image file", - },{ - .name = "initrd", - .type = QEMU_OPT_STRING, - .help = "Linux initial ramdisk file", - },{ - .name = "append", - .type = QEMU_OPT_STRING, - .help = "Linux kernel command line", - },{ - .name = "dtb", - .type = QEMU_OPT_STRING, - .help = "Linux kernel device tree file", - },{ - .name = "dumpdtb", - .type = QEMU_OPT_STRING, - .help = "Dump current dtb to a file and quit", - },{ - .name = "phandle_start", - .type = QEMU_OPT_NUMBER, - .help = "The first phandle ID we may generate dynamically", - },{ - .name = "dt_compatible", - .type = QEMU_OPT_STRING, - .help = "Overrides the \"compatible\" property of the dt root node", - },{ - .name = "dump-guest-core", - .type = QEMU_OPT_BOOL, - .help = "Include guest memory in a core dump", - },{ - .name = "mem-merge", - .type = QEMU_OPT_BOOL, - .help = "enable/disable memory merge support", - },{ - .name = "usb", - .type = QEMU_OPT_BOOL, - .help = "Set on/off to enable/disable usb", - },{ - .name = "firmware", - .type = QEMU_OPT_STRING, - .help = "firmware image", - },{ - .name = "iommu", - .type = QEMU_OPT_BOOL, - .help = "Set on/off to enable/disable Intel IOMMU (VT-d)", - },{ - .name = "suppress-vmdesc", - .type = QEMU_OPT_BOOL, - .help = "Set on to disable self-describing migration", - },{ - .name = "aes-key-wrap", - .type = QEMU_OPT_BOOL, - .help = "enable/disable AES key wrapping using the CPACF wrapping key", - },{ - .name = "dea-key-wrap", - .type = QEMU_OPT_BOOL, - .help = "enable/disable DEA key wrapping using the CPACF wrapping key", - },{ - .name = "loadparm", - .type = QEMU_OPT_STRING, - .help = "Up to 8 chars in set of [A-Za-z0-9. ](lower case chars" - " converted to upper case) to pass to machine" - " loader, boot manager, and guest kernel", - }, - { /* End of list */ } - } -}; +static CommandLineParameterInfo *objprop_to_cmdline_prop(ObjectProperty *prop) +{ + CommandLineParameterInfo *info; -CommandLineOptionInfoList *qmp_query_command_line_options(bool has_option, - const char *option, + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(prop->name); + + if (g_str_equal(prop->type, "bool") || g_str_equal(prop->type, "OnOffAuto")) { + info->type = COMMAND_LINE_PARAMETER_TYPE_BOOLEAN; + } else if (g_str_equal(prop->type, "int")) { + info->type = COMMAND_LINE_PARAMETER_TYPE_NUMBER; + } else if (g_str_equal(prop->type, "size")) { + info->type = COMMAND_LINE_PARAMETER_TYPE_SIZE; + } else { + info->type = COMMAND_LINE_PARAMETER_TYPE_STRING; + } + + if (prop->description) { + info->help = g_strdup(prop->description); + } + + return info; +} + +static CommandLineParameterInfoList *query_all_machine_properties(void) +{ + CommandLineParameterInfoList *params = NULL, *clpiter; + CommandLineParameterInfo *info; + GSList *machines, *curr_mach; + ObjectPropertyIterator op_iter; + ObjectProperty *prop; + bool is_new; + + machines = object_class_get_list(TYPE_MACHINE, false); + assert(machines); + + /* Loop over all machine classes */ + for (curr_mach = machines; curr_mach; curr_mach = curr_mach->next) { + object_class_property_iter_init(&op_iter, curr_mach->data); + /* ... and over the properties of each machine: */ + while ((prop = object_property_iter_next(&op_iter))) { + if (!prop->set) { + continue; + } + /* + * Check whether the property has already been put into the list + * (via another machine class) + */ + is_new = true; + for (clpiter = params; clpiter != NULL; clpiter = clpiter->next) { + if (g_str_equal(clpiter->value->name, prop->name)) { + is_new = false; + break; + } + } + /* If it hasn't been added before, add it now to the list */ + if (is_new) { + info = objprop_to_cmdline_prop(prop); + QAPI_LIST_PREPEND(params, info); + } + } + } + + g_slist_free(machines); + + /* Add entry for the "type" parameter */ + info = g_malloc0(sizeof(*info)); + info->name = g_strdup("type"); + info->type = COMMAND_LINE_PARAMETER_TYPE_STRING; + info->help = g_strdup("machine type"); + QAPI_LIST_PREPEND(params, info); + + return params; +} + +CommandLineOptionInfoList *qmp_query_command_line_options(const char *option, Error **errp) { CommandLineOptionInfoList *conf_list = NULL; @@ -250,7 +226,7 @@ CommandLineOptionInfoList *qmp_query_command_line_options(bool has_option, int i; for (i = 0; vm_config_groups[i] != NULL; i++) { - if (!has_option || !strcmp(option, vm_config_groups[i]->name)) { + if (!option || !strcmp(option, vm_config_groups[i]->name)) { info = g_malloc0(sizeof(*info)); info->option = g_strdup(vm_config_groups[i]->name); if (!strcmp("drive", vm_config_groups[i]->name)) { @@ -263,10 +239,10 @@ CommandLineOptionInfoList *qmp_query_command_line_options(bool has_option, } } - if (!has_option || !strcmp(option, "machine")) { + if (!option || !strcmp(option, "machine")) { info = g_malloc0(sizeof(*info)); info->option = g_strdup("machine"); - info->parameters = query_option_descs(machine_opts.desc); + info->parameters = query_all_machine_properties(); QAPI_LIST_PREPEND(conf_list, info); } @@ -318,9 +294,9 @@ void qemu_add_opts(QemuOptsList *list) static int qemu_config_foreach(FILE *fp, QEMUConfigCB *cb, void *opaque, const char *fname, Error **errp) { + ERRP_GUARD(); char line[1024], prev_group[64], group[64], arg[64], value[1024]; Location loc; - Error *local_err = NULL; QDict *qdict = NULL; int res = -EINVAL, lno = 0; int count = 0; @@ -348,10 +324,9 @@ static int qemu_config_foreach(FILE *fp, QEMUConfigCB *cb, void *opaque, } if (qdict != prev) { if (prev) { - cb(prev_group, prev, opaque, &local_err); + cb(prev_group, prev, opaque, errp); qobject_unref(prev); - if (local_err) { - error_propagate(errp, local_err); + if (*errp) { goto out; } } @@ -423,12 +398,12 @@ int qemu_read_config_file(const char *filename, QEMUConfigCB *cb, Error **errp) return ret; } -static void config_parse_qdict_section(QDict *options, QemuOptsList *opts, +static bool config_parse_qdict_section(QDict *options, QemuOptsList *opts, Error **errp) { QemuOpts *subopts; - QDict *subqdict; - QList *list = NULL; + g_autoptr(QDict) subqdict = NULL; + g_autoptr(QList) list = NULL; size_t orig_size, enum_size; char *prefix; @@ -437,23 +412,23 @@ static void config_parse_qdict_section(QDict *options, QemuOptsList *opts, g_free(prefix); orig_size = qdict_size(subqdict); if (!orig_size) { - goto out; + return true; } subopts = qemu_opts_create(opts, NULL, 0, errp); if (!subopts) { - goto out; + return false; } if (!qemu_opts_absorb_qdict(subopts, subqdict, errp)) { - goto out; + return false; } enum_size = qdict_size(subqdict); if (enum_size < orig_size && enum_size) { error_setg(errp, "Unknown option '%s' for [%s]", qdict_first(subqdict)->key, opts->name); - goto out; + return false; } if (enum_size) { @@ -468,7 +443,7 @@ static void config_parse_qdict_section(QDict *options, QemuOptsList *opts, if (qdict_size(subqdict)) { error_setg(errp, "Unused option '%s' for [%s]", qdict_first(subqdict)->key, opts->name); - goto out; + return false; } QLIST_FOREACH_ENTRY(list, list_entry) { @@ -478,46 +453,43 @@ static void config_parse_qdict_section(QDict *options, QemuOptsList *opts, if (!section) { error_setg(errp, "[%s] section (index %u) does not consist of " "keys", opts->name, i); - goto out; + return false; } opt_name = g_strdup_printf("%s.%u", opts->name, i++); subopts = qemu_opts_create(opts, opt_name, 1, errp); g_free(opt_name); if (!subopts) { - goto out; + return false; } if (!qemu_opts_absorb_qdict(subopts, section, errp)) { qemu_opts_del(subopts); - goto out; + return false; } if (qdict_size(section)) { error_setg(errp, "[%s] section doesn't support the option '%s'", opts->name, qdict_first(section)->key); qemu_opts_del(subopts); - goto out; + return false; } } } -out: - qobject_unref(subqdict); - qobject_unref(list); + return true; } -void qemu_config_parse_qdict(QDict *options, QemuOptsList **lists, +bool qemu_config_parse_qdict(QDict *options, QemuOptsList **lists, Error **errp) { int i; - Error *local_err = NULL; for (i = 0; lists[i]; i++) { - config_parse_qdict_section(options, lists[i], &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; + if (!config_parse_qdict_section(options, lists[i], errp)) { + return false; } } + + return true; } diff --git a/util/qemu-coroutine-io.c b/util/qemu-coroutine-io.c index d791932d63..364f4d5abf 100644 --- a/util/qemu-coroutine-io.c +++ b/util/qemu-coroutine-io.c @@ -74,8 +74,7 @@ typedef struct { static void fd_coroutine_enter(void *opaque) { FDYieldUntilData *data = opaque; - aio_set_fd_handler(data->ctx, data->fd, false, - NULL, NULL, NULL, NULL, NULL); + aio_set_fd_handler(data->ctx, data->fd, NULL, NULL, NULL, NULL, NULL); qemu_coroutine_enter(data->co); } @@ -87,7 +86,7 @@ void coroutine_fn yield_until_fd_readable(int fd) data.ctx = qemu_get_current_aio_context(); data.co = qemu_coroutine_self(); data.fd = fd; - aio_set_fd_handler( - data.ctx, fd, false, fd_coroutine_enter, NULL, NULL, NULL, &data); + aio_set_fd_handler(data.ctx, fd, fd_coroutine_enter, NULL, NULL, NULL, + &data); qemu_coroutine_yield(); } diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c index 9ad24ab1af..2534435388 100644 --- a/util/qemu-coroutine-lock.c +++ b/util/qemu-coroutine-lock.c @@ -27,7 +27,6 @@ */ #include "qemu/osdep.h" -#include "qemu/coroutine.h" #include "qemu/coroutine_int.h" #include "qemu/processor.h" #include "qemu/queue.h" @@ -39,10 +38,15 @@ void qemu_co_queue_init(CoQueue *queue) QSIMPLEQ_INIT(&queue->entries); } -void coroutine_fn qemu_co_queue_wait_impl(CoQueue *queue, QemuLockable *lock) +void coroutine_fn qemu_co_queue_wait_impl(CoQueue *queue, QemuLockable *lock, + CoQueueWaitFlags flags) { Coroutine *self = qemu_coroutine_self(); - QSIMPLEQ_INSERT_TAIL(&queue->entries, self, co_queue_next); + if (flags & CO_QUEUE_WAIT_FRONT) { + QSIMPLEQ_INSERT_HEAD(&queue->entries, self, co_queue_next); + } else { + QSIMPLEQ_INSERT_TAIL(&queue->entries, self, co_queue_next); + } if (lock) { qemu_lockable_unlock(lock); @@ -135,7 +139,7 @@ typedef struct CoWaitRecord { QSLIST_ENTRY(CoWaitRecord) next; } CoWaitRecord; -static void push_waiter(CoMutex *mutex, CoWaitRecord *w) +static void coroutine_fn push_waiter(CoMutex *mutex, CoWaitRecord *w) { w->co = qemu_coroutine_self(); QSLIST_INSERT_HEAD_ATOMIC(&mutex->from_push, w, next); @@ -197,10 +201,16 @@ static void coroutine_fn qemu_co_mutex_lock_slowpath(AioContext *ctx, trace_qemu_co_mutex_lock_entry(mutex, self); push_waiter(mutex, &w); + /* + * Add waiter before reading mutex->handoff. Pairs with qatomic_set_mb + * in qemu_co_mutex_unlock. + */ + smp_mb__after_rmw(); + /* This is the "Responsibility Hand-Off" protocol; a lock() picks from * a concurrent unlock() the responsibility of waking somebody up. */ - old_handoff = qatomic_mb_read(&mutex->handoff); + old_handoff = qatomic_read(&mutex->handoff); if (old_handoff && has_waiters(mutex) && qatomic_cmpxchg(&mutex->handoff, old_handoff, 0) == old_handoff) { @@ -299,7 +309,8 @@ void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) } our_handoff = mutex->sequence; - qatomic_mb_set(&mutex->handoff, our_handoff); + /* Set handoff before checking for waiters. */ + qatomic_set_mb(&mutex->handoff, our_handoff); if (!has_waiters(mutex)) { /* The concurrent lock has not added itself yet, so it * will be able to pick our handoff. @@ -332,7 +343,7 @@ void qemu_co_rwlock_init(CoRwlock *lock) } /* Releases the internal CoMutex. */ -static void qemu_co_rwlock_maybe_wake_one(CoRwlock *lock) +static void coroutine_fn qemu_co_rwlock_maybe_wake_one(CoRwlock *lock) { CoRwTicket *tkt = QSIMPLEQ_FIRST(&lock->tickets); Coroutine *co = NULL; @@ -365,7 +376,7 @@ static void qemu_co_rwlock_maybe_wake_one(CoRwlock *lock) } } -void qemu_co_rwlock_rdlock(CoRwlock *lock) +void coroutine_fn qemu_co_rwlock_rdlock(CoRwlock *lock) { Coroutine *self = qemu_coroutine_self(); @@ -390,7 +401,7 @@ void qemu_co_rwlock_rdlock(CoRwlock *lock) self->locks_held++; } -void qemu_co_rwlock_unlock(CoRwlock *lock) +void coroutine_fn qemu_co_rwlock_unlock(CoRwlock *lock) { Coroutine *self = qemu_coroutine_self(); @@ -408,7 +419,7 @@ void qemu_co_rwlock_unlock(CoRwlock *lock) qemu_co_rwlock_maybe_wake_one(lock); } -void qemu_co_rwlock_downgrade(CoRwlock *lock) +void coroutine_fn qemu_co_rwlock_downgrade(CoRwlock *lock) { qemu_co_mutex_lock(&lock->mutex); assert(lock->owners == -1); @@ -418,7 +429,7 @@ void qemu_co_rwlock_downgrade(CoRwlock *lock) qemu_co_rwlock_maybe_wake_one(lock); } -void qemu_co_rwlock_wrlock(CoRwlock *lock) +void coroutine_fn qemu_co_rwlock_wrlock(CoRwlock *lock) { Coroutine *self = qemu_coroutine_self(); @@ -438,7 +449,7 @@ void qemu_co_rwlock_wrlock(CoRwlock *lock) self->locks_held++; } -void qemu_co_rwlock_upgrade(CoRwlock *lock) +void coroutine_fn qemu_co_rwlock_upgrade(CoRwlock *lock) { qemu_co_mutex_lock(&lock->mutex); assert(lock->owners > 0); diff --git a/util/qemu-coroutine-sleep.c b/util/qemu-coroutine-sleep.c index 571ab521ff..af59f9af98 100644 --- a/util/qemu-coroutine-sleep.c +++ b/util/qemu-coroutine-sleep.c @@ -12,7 +12,6 @@ */ #include "qemu/osdep.h" -#include "qemu/coroutine.h" #include "qemu/coroutine_int.h" #include "qemu/timer.h" #include "block/aio.h" diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c index 4a8bd63ef0..eb4eebefdf 100644 --- a/util/qemu-coroutine.c +++ b/util/qemu-coroutine.c @@ -16,74 +16,210 @@ #include "trace.h" #include "qemu/thread.h" #include "qemu/atomic.h" -#include "qemu/coroutine.h" #include "qemu/coroutine_int.h" #include "qemu/coroutine-tls.h" +#include "qemu/cutils.h" #include "block/aio.h" -/** - * The minimal batch size is always 64, coroutines from the release_pool are - * reused as soon as there are 64 coroutines in it. The maximum pool size starts - * with 64 and is increased on demand so that coroutines are not deleted even if - * they are not immediately reused. - */ enum { - POOL_MIN_BATCH_SIZE = 64, - POOL_INITIAL_MAX_SIZE = 64, + COROUTINE_POOL_BATCH_MAX_SIZE = 128, }; -/** Free list to speed up creation */ -static QSLIST_HEAD(, Coroutine) release_pool = QSLIST_HEAD_INITIALIZER(pool); -static unsigned int pool_max_size = POOL_INITIAL_MAX_SIZE; -static unsigned int release_pool_size; +/* + * Coroutine creation and deletion is expensive so a pool of unused coroutines + * is kept as a cache. When the pool has coroutines available, they are + * recycled instead of creating new ones from scratch. Coroutines are added to + * the pool upon termination. + * + * The pool is global but each thread maintains a small local pool to avoid + * global pool contention. Threads fetch and return batches of coroutines from + * the global pool to maintain their local pool. The local pool holds up to two + * batches whereas the maximum size of the global pool is controlled by the + * qemu_coroutine_inc_pool_size() API. + * + * .-----------------------------------. + * | Batch 1 | Batch 2 | Batch 3 | ... | global_pool + * `-----------------------------------' + * + * .-------------------. + * | Batch 1 | Batch 2 | per-thread local_pool (maximum 2 batches) + * `-------------------' + */ +typedef struct CoroutinePoolBatch { + /* Batches are kept in a list */ + QSLIST_ENTRY(CoroutinePoolBatch) next; -typedef QSLIST_HEAD(, Coroutine) CoroutineQSList; -QEMU_DEFINE_STATIC_CO_TLS(CoroutineQSList, alloc_pool); -QEMU_DEFINE_STATIC_CO_TLS(unsigned int, alloc_pool_size); -QEMU_DEFINE_STATIC_CO_TLS(Notifier, coroutine_pool_cleanup_notifier); + /* This batch holds up to @COROUTINE_POOL_BATCH_MAX_SIZE coroutines */ + QSLIST_HEAD(, Coroutine) list; + unsigned int size; +} CoroutinePoolBatch; -static void coroutine_pool_cleanup(Notifier *n, void *value) +typedef QSLIST_HEAD(, CoroutinePoolBatch) CoroutinePool; + +/* Host operating system limit on number of pooled coroutines */ +static unsigned int global_pool_hard_max_size; + +static QemuMutex global_pool_lock; /* protects the following variables */ +static CoroutinePool global_pool = QSLIST_HEAD_INITIALIZER(global_pool); +static unsigned int global_pool_size; +static unsigned int global_pool_max_size = COROUTINE_POOL_BATCH_MAX_SIZE; + +QEMU_DEFINE_STATIC_CO_TLS(CoroutinePool, local_pool); +QEMU_DEFINE_STATIC_CO_TLS(Notifier, local_pool_cleanup_notifier); + +static CoroutinePoolBatch *coroutine_pool_batch_new(void) +{ + CoroutinePoolBatch *batch = g_new(CoroutinePoolBatch, 1); + + QSLIST_INIT(&batch->list); + batch->size = 0; + return batch; +} + +static void coroutine_pool_batch_delete(CoroutinePoolBatch *batch) { Coroutine *co; Coroutine *tmp; - CoroutineQSList *alloc_pool = get_ptr_alloc_pool(); - QSLIST_FOREACH_SAFE(co, alloc_pool, pool_next, tmp) { - QSLIST_REMOVE_HEAD(alloc_pool, pool_next); + QSLIST_FOREACH_SAFE(co, &batch->list, pool_next, tmp) { + QSLIST_REMOVE_HEAD(&batch->list, pool_next); qemu_coroutine_delete(co); } + g_free(batch); +} + +static void local_pool_cleanup(Notifier *n, void *value) +{ + CoroutinePool *local_pool = get_ptr_local_pool(); + CoroutinePoolBatch *batch; + CoroutinePoolBatch *tmp; + + QSLIST_FOREACH_SAFE(batch, local_pool, next, tmp) { + QSLIST_REMOVE_HEAD(local_pool, next); + coroutine_pool_batch_delete(batch); + } +} + +/* Ensure the atexit notifier is registered */ +static void local_pool_cleanup_init_once(void) +{ + Notifier *notifier = get_ptr_local_pool_cleanup_notifier(); + if (!notifier->notify) { + notifier->notify = local_pool_cleanup; + qemu_thread_atexit_add(notifier); + } +} + +/* Helper to get the next unused coroutine from the local pool */ +static Coroutine *coroutine_pool_get_local(void) +{ + CoroutinePool *local_pool = get_ptr_local_pool(); + CoroutinePoolBatch *batch = QSLIST_FIRST(local_pool); + Coroutine *co; + + if (unlikely(!batch)) { + return NULL; + } + + co = QSLIST_FIRST(&batch->list); + QSLIST_REMOVE_HEAD(&batch->list, pool_next); + batch->size--; + + if (batch->size == 0) { + QSLIST_REMOVE_HEAD(local_pool, next); + coroutine_pool_batch_delete(batch); + } + return co; +} + +/* Get the next batch from the global pool */ +static void coroutine_pool_refill_local(void) +{ + CoroutinePool *local_pool = get_ptr_local_pool(); + CoroutinePoolBatch *batch; + + WITH_QEMU_LOCK_GUARD(&global_pool_lock) { + batch = QSLIST_FIRST(&global_pool); + + if (batch) { + QSLIST_REMOVE_HEAD(&global_pool, next); + global_pool_size -= batch->size; + } + } + + if (batch) { + QSLIST_INSERT_HEAD(local_pool, batch, next); + local_pool_cleanup_init_once(); + } +} + +/* Add a batch of coroutines to the global pool */ +static void coroutine_pool_put_global(CoroutinePoolBatch *batch) +{ + WITH_QEMU_LOCK_GUARD(&global_pool_lock) { + unsigned int max = MIN(global_pool_max_size, + global_pool_hard_max_size); + + if (global_pool_size < max) { + QSLIST_INSERT_HEAD(&global_pool, batch, next); + + /* Overshooting the max pool size is allowed */ + global_pool_size += batch->size; + return; + } + } + + /* The global pool was full, so throw away this batch */ + coroutine_pool_batch_delete(batch); +} + +/* Get the next unused coroutine from the pool or return NULL */ +static Coroutine *coroutine_pool_get(void) +{ + Coroutine *co; + + co = coroutine_pool_get_local(); + if (!co) { + coroutine_pool_refill_local(); + co = coroutine_pool_get_local(); + } + return co; +} + +static void coroutine_pool_put(Coroutine *co) +{ + CoroutinePool *local_pool = get_ptr_local_pool(); + CoroutinePoolBatch *batch = QSLIST_FIRST(local_pool); + + if (unlikely(!batch)) { + batch = coroutine_pool_batch_new(); + QSLIST_INSERT_HEAD(local_pool, batch, next); + local_pool_cleanup_init_once(); + } + + if (unlikely(batch->size >= COROUTINE_POOL_BATCH_MAX_SIZE)) { + CoroutinePoolBatch *next = QSLIST_NEXT(batch, next); + + /* Is the local pool full? */ + if (next) { + QSLIST_REMOVE_HEAD(local_pool, next); + coroutine_pool_put_global(batch); + } + + batch = coroutine_pool_batch_new(); + QSLIST_INSERT_HEAD(local_pool, batch, next); + } + + QSLIST_INSERT_HEAD(&batch->list, co, pool_next); + batch->size++; } Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque) { Coroutine *co = NULL; - if (CONFIG_COROUTINE_POOL) { - CoroutineQSList *alloc_pool = get_ptr_alloc_pool(); - - co = QSLIST_FIRST(alloc_pool); - if (!co) { - if (release_pool_size > POOL_MIN_BATCH_SIZE) { - /* Slow path; a good place to register the destructor, too. */ - Notifier *notifier = get_ptr_coroutine_pool_cleanup_notifier(); - if (!notifier->notify) { - notifier->notify = coroutine_pool_cleanup; - qemu_thread_atexit_add(notifier); - } - - /* This is not exact; there could be a little skew between - * release_pool_size and the actual size of release_pool. But - * it is just a heuristic, it does not need to be perfect. - */ - set_alloc_pool_size(qatomic_xchg(&release_pool_size, 0)); - QSLIST_MOVE_ATOMIC(alloc_pool, &release_pool); - co = QSLIST_FIRST(alloc_pool); - } - } - if (co) { - QSLIST_REMOVE_HEAD(alloc_pool, pool_next); - set_alloc_pool_size(get_alloc_pool_size() - 1); - } + if (IS_ENABLED(CONFIG_COROUTINE_POOL)) { + co = coroutine_pool_get(); } if (!co) { @@ -100,20 +236,11 @@ static void coroutine_delete(Coroutine *co) { co->caller = NULL; - if (CONFIG_COROUTINE_POOL) { - if (release_pool_size < qatomic_read(&pool_max_size) * 2) { - QSLIST_INSERT_HEAD_ATOMIC(&release_pool, co, pool_next); - qatomic_inc(&release_pool_size); - return; - } - if (get_alloc_pool_size() < qatomic_read(&pool_max_size)) { - QSLIST_INSERT_HEAD(get_ptr_alloc_pool(), co, pool_next); - set_alloc_pool_size(get_alloc_pool_size() + 1); - return; - } + if (IS_ENABLED(CONFIG_COROUTINE_POOL)) { + coroutine_pool_put(co); + } else { + qemu_coroutine_delete(co); } - - qemu_coroutine_delete(co); } void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co) @@ -128,9 +255,13 @@ void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co) Coroutine *to = QSIMPLEQ_FIRST(&pending); CoroutineAction ret; - /* Cannot rely on the read barrier for to in aio_co_wake(), as there are - * callers outside of aio_co_wake() */ - const char *scheduled = qatomic_mb_read(&to->scheduled); + /* + * Read to before to->scheduled; pairs with qatomic_cmpxchg in + * qemu_co_sleep(), aio_co_schedule() etc. + */ + smp_read_barrier_depends(); + + const char *scheduled = qatomic_read(&to->scheduled); QSIMPLEQ_REMOVE_HEAD(&pending, co_queue_next); @@ -213,17 +344,58 @@ bool qemu_coroutine_entered(Coroutine *co) return co->caller; } -AioContext *coroutine_fn qemu_coroutine_get_aio_context(Coroutine *co) +AioContext *qemu_coroutine_get_aio_context(Coroutine *co) { return co->ctx; } void qemu_coroutine_inc_pool_size(unsigned int additional_pool_size) { - qatomic_add(&pool_max_size, additional_pool_size); + QEMU_LOCK_GUARD(&global_pool_lock); + global_pool_max_size += additional_pool_size; } void qemu_coroutine_dec_pool_size(unsigned int removing_pool_size) { - qatomic_sub(&pool_max_size, removing_pool_size); + QEMU_LOCK_GUARD(&global_pool_lock); + global_pool_max_size -= removing_pool_size; +} + +static unsigned int get_global_pool_hard_max_size(void) +{ +#ifdef __linux__ + g_autofree char *contents = NULL; + int max_map_count; + + /* + * Linux processes can have up to max_map_count virtual memory areas + * (VMAs). mmap(2), mprotect(2), etc fail with ENOMEM beyond this limit. We + * must limit the coroutine pool to a safe size to avoid running out of + * VMAs. + */ + if (g_file_get_contents("/proc/sys/vm/max_map_count", &contents, NULL, + NULL) && + qemu_strtoi(contents, NULL, 10, &max_map_count) == 0) { + /* + * This is an upper bound that avoids exceeding max_map_count. Leave a + * fixed amount for non-coroutine users like library dependencies, + * vhost-user, etc. Each coroutine takes up 2 VMAs so halve the + * remaining amount. + */ + if (max_map_count > 5000) { + return (max_map_count - 5000) / 2; + } else { + /* Disable the global pool but threads still have local pools */ + return 0; + } + } +#endif + + return UINT_MAX; +} + +static void __attribute__((constructor)) qemu_coroutine_init(void) +{ + qemu_mutex_init(&global_pool_lock); + global_pool_hard_max_size = get_global_pool_hard_max_size(); } diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 13b5b197f9..60c44b2b56 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -210,7 +210,8 @@ static int inet_listen_saddr(InetSocketAddress *saddr, int num, Error **errp) { - struct addrinfo ai,*res,*e; + ERRP_GUARD(); + struct addrinfo ai, *res, *e; char port[33]; char uaddr[INET6_ADDRSTRLEN+1]; char uport[33]; @@ -218,7 +219,6 @@ static int inet_listen_saddr(InetSocketAddress *saddr, int slisten = -1; int saved_errno = 0; bool socket_created = false; - Error *err = NULL; if (saddr->keep_alive) { error_setg(errp, "keep-alive option is not supported for passive " @@ -231,11 +231,9 @@ static int inet_listen_saddr(InetSocketAddress *saddr, if (saddr->has_numeric && saddr->numeric) { ai.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; } - ai.ai_family = inet_ai_family_from_address(saddr, &err); ai.ai_socktype = SOCK_STREAM; - - if (err) { - error_propagate(errp, err); + ai.ai_family = inet_ai_family_from_address(saddr, errp); + if (*errp) { return -1; } @@ -251,12 +249,12 @@ static int inet_listen_saddr(InetSocketAddress *saddr, /* lookup */ if (port_offset) { - unsigned long long baseport; + uint64_t baseport; if (strlen(port) == 0) { error_setg(errp, "port not specified"); return -1; } - if (parse_uint_full(port, &baseport, 10) < 0) { + if (parse_uint_full(port, 10, &baseport) < 0) { error_setg(errp, "can't convert to a number: %s", port); return -1; } @@ -328,7 +326,7 @@ static int inet_listen_saddr(InetSocketAddress *saddr, * recover from this situation, so we need to recreate the * socket to allow bind attempts for subsequent ports: */ - closesocket(slisten); + close(slisten); slisten = -1; } } @@ -339,7 +337,7 @@ static int inet_listen_saddr(InetSocketAddress *saddr, listen_failed: saved_errno = errno; if (slisten >= 0) { - closesocket(slisten); + close(slisten); } freeaddrinfo(res); errno = saved_errno; @@ -382,7 +380,7 @@ static int inet_connect_addr(const InetSocketAddress *saddr, if (rc < 0) { error_setg_errno(errp, errno, "Failed to connect to '%s:%s'", saddr->host, saddr->port); - closesocket(sock); + close(sock); return -1; } @@ -392,9 +390,9 @@ static int inet_connect_addr(const InetSocketAddress *saddr, static struct addrinfo *inet_parse_connect_saddr(InetSocketAddress *saddr, Error **errp) { + ERRP_GUARD(); struct addrinfo ai, *res; int rc; - Error *err = NULL; static int useV4Mapped = 1; memset(&ai, 0, sizeof(ai)); @@ -403,11 +401,9 @@ static struct addrinfo *inet_parse_connect_saddr(InetSocketAddress *saddr, if (qatomic_read(&useV4Mapped)) { ai.ai_flags |= AI_V4MAPPED; } - ai.ai_family = inet_ai_family_from_address(saddr, &err); ai.ai_socktype = SOCK_STREAM; - - if (err) { - error_propagate(errp, err); + ai.ai_family = inet_ai_family_from_address(saddr, errp); + if (*errp) { return NULL; } @@ -499,20 +495,18 @@ static int inet_dgram_saddr(InetSocketAddress *sraddr, InetSocketAddress *sladdr, Error **errp) { + ERRP_GUARD(); struct addrinfo ai, *peer = NULL, *local = NULL; const char *addr; const char *port; int sock = -1, rc; - Error *err = NULL; /* lookup peer addr */ memset(&ai,0, sizeof(ai)); ai.ai_flags = AI_CANONNAME | AI_V4MAPPED | AI_ADDRCONFIG; - ai.ai_family = inet_ai_family_from_address(sraddr, &err); ai.ai_socktype = SOCK_DGRAM; - - if (err) { - error_propagate(errp, err); + ai.ai_family = inet_ai_family_from_address(sraddr, errp); + if (*errp) { goto err; } @@ -586,7 +580,7 @@ static int inet_dgram_saddr(InetSocketAddress *sraddr, err: if (sock != -1) { - closesocket(sock); + close(sock); } if (local) { freeaddrinfo(local); @@ -738,19 +732,19 @@ static bool vsock_parse_vaddr_to_sockaddr(const VsockSocketAddress *vaddr, struct sockaddr_vm *svm, Error **errp) { - unsigned long long val; + uint64_t val; memset(svm, 0, sizeof(*svm)); svm->svm_family = AF_VSOCK; - if (parse_uint_full(vaddr->cid, &val, 10) < 0 || + if (parse_uint_full(vaddr->cid, 10, &val) < 0 || val > UINT32_MAX) { error_setg(errp, "Failed to parse cid '%s'", vaddr->cid); return false; } svm->svm_cid = val; - if (parse_uint_full(vaddr->port, &val, 10) < 0 || + if (parse_uint_full(vaddr->port, 10, &val) < 0 || val > UINT32_MAX) { error_setg(errp, "Failed to parse port '%s'", vaddr->port); return false; @@ -783,7 +777,7 @@ static int vsock_connect_addr(const VsockSocketAddress *vaddr, if (rc < 0) { error_setg_errno(errp, errno, "Failed to connect to '%s:%s'", vaddr->cid, vaddr->port); - closesocket(sock); + close(sock); return -1; } @@ -820,13 +814,13 @@ static int vsock_listen_saddr(VsockSocketAddress *vaddr, if (bind(slisten, (const struct sockaddr *)&svm, sizeof(svm)) != 0) { error_setg_errno(errp, errno, "Failed to bind socket"); - closesocket(slisten); + close(slisten); return -1; } if (listen(slisten, num) != 0) { error_setg_errno(errp, errno, "Failed to listen on socket"); - closesocket(slisten); + close(slisten); return -1; } return slisten; @@ -880,8 +874,6 @@ static int vsock_parse(VsockSocketAddress *addr, const char *str, } #endif /* CONFIG_AF_VSOCK */ -#ifndef _WIN32 - static bool saddr_is_abstract(UnixSocketAddress *saddr) { #ifdef CONFIG_LINUX @@ -921,9 +913,8 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, if (saddr->path[0] || abstract) { path = saddr->path; } else { - const char *tmpdir = getenv("TMPDIR"); - tmpdir = tmpdir ? tmpdir : "/tmp"; - path = pathbuf = g_strdup_printf("%s/qemu-socket-XXXXXX", tmpdir); + path = pathbuf = g_strdup_printf("%s/qemu-socket-XXXXXX", + g_get_tmp_dir()); } pathlen = strlen(path); @@ -938,7 +929,7 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, if (pathbuf != NULL) { /* - * This dummy fd usage silences the mktemp() unsecure warning. + * This dummy fd usage silences the mktemp() insecure warning. * Using mkstemp() doesn't make things more secure here * though. bind() complains about existing files, so we have * to unlink first and thus re-open the race window. The @@ -987,7 +978,7 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, err: g_free(pathbuf); - closesocket(sock); + close(sock); return -1; } @@ -1054,25 +1045,6 @@ static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) return -1; } -#else - -static int unix_listen_saddr(UnixSocketAddress *saddr, - int num, - Error **errp) -{ - error_setg(errp, "unix sockets are not available on windows"); - errno = ENOTSUP; - return -1; -} - -static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) -{ - error_setg(errp, "unix sockets are not available on windows"); - errno = ENOTSUP; - return -1; -} -#endif - /* compatibility wrapper */ int unix_listen(const char *str, Error **errp) { @@ -1098,6 +1070,26 @@ int unix_connect(const char *path, Error **errp) return sock; } +char *socket_uri(SocketAddress *addr) +{ + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + return g_strdup_printf("tcp:%s:%s", + addr->u.inet.host, + addr->u.inet.port); + case SOCKET_ADDRESS_TYPE_UNIX: + return g_strdup_printf("unix:%s", + addr->u.q_unix.path); + case SOCKET_ADDRESS_TYPE_FD: + return g_strdup_printf("fd:%s", addr->u.fd.str); + case SOCKET_ADDRESS_TYPE_VSOCK: + return g_strdup_printf("vsock:%s:%s", + addr->u.vsock.cid, + addr->u.vsock.port); + default: + return g_strdup("unknown address type"); + } +} SocketAddress *socket_parse(const char *str, Error **errp) { @@ -1125,6 +1117,11 @@ SocketAddress *socket_parse(const char *str, Error **errp) if (vsock_parse(&addr->u.vsock, str + strlen("vsock:"), errp)) { goto fail; } + } else if (strstart(str, "tcp:", NULL)) { + addr->type = SOCKET_ADDRESS_TYPE_INET; + if (inet_parse(&addr->u.inet, str + strlen("tcp:"), errp)) { + goto fail; + } } else { addr->type = SOCKET_ADDRESS_TYPE_INET; if (inet_parse(&addr->u.inet, str, errp)) { @@ -1241,7 +1238,7 @@ int socket_listen(SocketAddress *addr, int num, Error **errp) */ if (listen(fd, num) != 0) { error_setg_errno(errp, errno, "Failed to listen on fd socket"); - closesocket(fd); + close(fd); return -1; } break; @@ -1335,7 +1332,6 @@ socket_sockaddr_to_address_inet(struct sockaddr_storage *sa, } -#ifndef WIN32 static SocketAddress * socket_sockaddr_to_address_unix(struct sockaddr_storage *sa, socklen_t salen, @@ -1362,7 +1358,6 @@ socket_sockaddr_to_address_unix(struct sockaddr_storage *sa, addr->u.q_unix.path = g_strndup(su->sun_path, salen); return addr; } -#endif /* WIN32 */ #ifdef CONFIG_AF_VSOCK static SocketAddress * @@ -1394,10 +1389,8 @@ socket_sockaddr_to_address(struct sockaddr_storage *sa, case AF_INET6: return socket_sockaddr_to_address_inet(sa, salen, errp); -#ifndef WIN32 case AF_UNIX: return socket_sockaddr_to_address_unix(sa, salen, errp); -#endif /* WIN32 */ #ifdef CONFIG_AF_VSOCK case AF_VSOCK: @@ -1471,7 +1464,8 @@ SocketAddress *socket_address_flatten(SocketAddressLegacy *addr_legacy) break; case SOCKET_ADDRESS_TYPE_FD: addr->type = SOCKET_ADDRESS_TYPE_FD; - QAPI_CLONE_MEMBERS(String, &addr->u.fd, addr_legacy->u.fd.data); + QAPI_CLONE_MEMBERS(FdSocketAddress, &addr->u.fd, + addr_legacy->u.fd.data); break; default: abort(); diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index ac1d56e673..b2e26e2120 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -16,6 +16,11 @@ #include "qemu/notify.h" #include "qemu-thread-common.h" #include "qemu/tsan.h" +#include "qemu/bitmap.h" + +#ifdef CONFIG_PTHREAD_SET_NAME_NP +#include +#endif static bool name_threads; @@ -24,7 +29,8 @@ void qemu_thread_naming(bool enable) name_threads = enable; #if !defined CONFIG_PTHREAD_SETNAME_NP_W_TID && \ - !defined CONFIG_PTHREAD_SETNAME_NP_WO_TID + !defined CONFIG_PTHREAD_SETNAME_NP_WO_TID && \ + !defined CONFIG_PTHREAD_SET_NAME_NP /* This is a debugging option, not fatal */ if (enable) { fprintf(stderr, "qemu: thread naming not supported on this host\n"); @@ -222,7 +228,7 @@ void qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, const char *file, con error_exit(err, __func__); } -static bool +static bool TSA_NO_TSA qemu_cond_timedwait_ts(QemuCond *cond, QemuMutex *mutex, struct timespec *ts, const char *file, const int line) { @@ -378,13 +384,21 @@ void qemu_event_destroy(QemuEvent *ev) void qemu_event_set(QemuEvent *ev) { - /* qemu_event_set has release semantics, but because it *loads* + assert(ev->initialized); + + /* + * Pairs with both qemu_event_reset() and qemu_event_wait(). + * + * qemu_event_set has release semantics, but because it *loads* * ev->value we need a full memory barrier here. */ - assert(ev->initialized); smp_mb(); if (qatomic_read(&ev->value) != EV_SET) { - if (qatomic_xchg(&ev->value, EV_SET) == EV_BUSY) { + int old = qatomic_xchg(&ev->value, EV_SET); + + /* Pairs with memory barrier in kernel futex_wait system call. */ + smp_mb__after_rmw(); + if (old == EV_BUSY) { /* There were waiters, wake them up. */ qemu_futex_wake(ev, INT_MAX); } @@ -393,18 +407,19 @@ void qemu_event_set(QemuEvent *ev) void qemu_event_reset(QemuEvent *ev) { - unsigned value; - assert(ev->initialized); - value = qatomic_read(&ev->value); - smp_mb_acquire(); - if (value == EV_SET) { - /* - * If there was a concurrent reset (or even reset+wait), - * do nothing. Otherwise change EV_SET->EV_FREE. - */ - qatomic_or(&ev->value, EV_FREE); - } + + /* + * If there was a concurrent reset (or even reset+wait), + * do nothing. Otherwise change EV_SET->EV_FREE. + */ + qatomic_or(&ev->value, EV_FREE); + + /* + * Order reset before checking the condition in the caller. + * Pairs with the first memory barrier in qemu_event_set(). + */ + smp_mb__after_rmw(); } void qemu_event_wait(QemuEvent *ev) @@ -412,20 +427,40 @@ void qemu_event_wait(QemuEvent *ev) unsigned value; assert(ev->initialized); - value = qatomic_read(&ev->value); - smp_mb_acquire(); + + /* + * qemu_event_wait must synchronize with qemu_event_set even if it does + * not go down the slow path, so this load-acquire is needed that + * synchronizes with the first memory barrier in qemu_event_set(). + * + * If we do go down the slow path, there is no requirement at all: we + * might miss a qemu_event_set() here but ultimately the memory barrier in + * qemu_futex_wait() will ensure the check is done correctly. + */ + value = qatomic_load_acquire(&ev->value); if (value != EV_SET) { if (value == EV_FREE) { /* - * Leave the event reset and tell qemu_event_set that there - * are waiters. No need to retry, because there cannot be - * a concurrent busy->free transition. After the CAS, the - * event will be either set or busy. + * Leave the event reset and tell qemu_event_set that there are + * waiters. No need to retry, because there cannot be a concurrent + * busy->free transition. After the CAS, the event will be either + * set or busy. + * + * This cmpxchg doesn't have particular ordering requirements if it + * succeeds (moving the store earlier can only cause qemu_event_set() + * to issue _more_ wakeups), the failing case needs acquire semantics + * like the load above. */ if (qatomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) { return; } } + + /* + * This is the final check for a concurrent set, so it does need + * a smp_mb() pairing with the second barrier of qemu_event_set(). + * The barrier is inside the FUTEX_WAIT system call. + */ qemu_futex_wait(ev, EV_BUSY); } } @@ -479,6 +514,8 @@ static void *qemu_thread_start(void *args) pthread_setname_np(pthread_self(), qemu_thread_args->name); # elif defined(CONFIG_PTHREAD_SETNAME_NP_WO_TID) pthread_setname_np(qemu_thread_args->name); +# elif defined(CONFIG_PTHREAD_SET_NAME_NP) + pthread_set_name_np(pthread_self(), qemu_thread_args->name); # endif } QEMU_TSAN_ANNOTATE_THREAD_NAME(qemu_thread_args->name); @@ -552,6 +589,75 @@ void qemu_thread_create(QemuThread *thread, const char *name, pthread_attr_destroy(&attr); } +int qemu_thread_set_affinity(QemuThread *thread, unsigned long *host_cpus, + unsigned long nbits) +{ +#if defined(CONFIG_PTHREAD_AFFINITY_NP) + const size_t setsize = CPU_ALLOC_SIZE(nbits); + unsigned long value; + cpu_set_t *cpuset; + int err; + + cpuset = CPU_ALLOC(nbits); + g_assert(cpuset); + + CPU_ZERO_S(setsize, cpuset); + value = find_first_bit(host_cpus, nbits); + while (value < nbits) { + CPU_SET_S(value, setsize, cpuset); + value = find_next_bit(host_cpus, nbits, value + 1); + } + + err = pthread_setaffinity_np(thread->thread, setsize, cpuset); + CPU_FREE(cpuset); + return err; +#else + return -ENOSYS; +#endif +} + +int qemu_thread_get_affinity(QemuThread *thread, unsigned long **host_cpus, + unsigned long *nbits) +{ +#if defined(CONFIG_PTHREAD_AFFINITY_NP) + unsigned long tmpbits; + cpu_set_t *cpuset; + size_t setsize; + int i, err; + + tmpbits = CPU_SETSIZE; + while (true) { + setsize = CPU_ALLOC_SIZE(tmpbits); + cpuset = CPU_ALLOC(tmpbits); + g_assert(cpuset); + + err = pthread_getaffinity_np(thread->thread, setsize, cpuset); + if (err) { + CPU_FREE(cpuset); + if (err != -EINVAL) { + return err; + } + tmpbits *= 2; + } else { + break; + } + } + + /* Convert the result into a proper bitmap. */ + *nbits = tmpbits; + *host_cpus = bitmap_new(tmpbits); + for (i = 0; i < tmpbits; i++) { + if (CPU_ISSET(i, cpuset)) { + set_bit(i, *host_cpus); + } + } + CPU_FREE(cpuset); + return 0; +#else + return -ENOSYS; +#endif +} + void qemu_thread_get_self(QemuThread *thread) { thread->thread = pthread_self(); diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index a2d5a6e825..a7fe3cc345 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -19,12 +19,39 @@ static bool name_threads; +typedef HRESULT (WINAPI *pSetThreadDescription) (HANDLE hThread, + PCWSTR lpThreadDescription); +static pSetThreadDescription SetThreadDescriptionFunc; +static HMODULE kernel32_module; + +static bool load_set_thread_description(void) +{ + static gsize _init_once = 0; + + if (g_once_init_enter(&_init_once)) { + kernel32_module = LoadLibrary("kernel32.dll"); + if (kernel32_module) { + SetThreadDescriptionFunc = + (pSetThreadDescription)GetProcAddress(kernel32_module, + "SetThreadDescription"); + if (!SetThreadDescriptionFunc) { + FreeLibrary(kernel32_module); + } + } + g_once_init_leave(&_init_once, 1); + } + + return !!SetThreadDescriptionFunc; +} + void qemu_thread_naming(bool enable) { - /* But note we don't actually name them on Windows yet */ name_threads = enable; - fprintf(stderr, "qemu: thread naming not supported on this host\n"); + if (enable && !load_set_thread_description()) { + fprintf(stderr, "qemu: thread naming not supported on this host\n"); + name_threads = false; + } } static void error_exit(int err, const char *msg) @@ -245,12 +272,20 @@ void qemu_event_destroy(QemuEvent *ev) void qemu_event_set(QemuEvent *ev) { assert(ev->initialized); - /* qemu_event_set has release semantics, but because it *loads* + + /* + * Pairs with both qemu_event_reset() and qemu_event_wait(). + * + * qemu_event_set has release semantics, but because it *loads* * ev->value we need a full memory barrier here. */ smp_mb(); if (qatomic_read(&ev->value) != EV_SET) { - if (qatomic_xchg(&ev->value, EV_SET) == EV_BUSY) { + int old = qatomic_xchg(&ev->value, EV_SET); + + /* Pairs with memory barrier after ResetEvent. */ + smp_mb__after_rmw(); + if (old == EV_BUSY) { /* There were waiters, wake them up. */ SetEvent(ev->event); } @@ -259,17 +294,19 @@ void qemu_event_set(QemuEvent *ev) void qemu_event_reset(QemuEvent *ev) { - unsigned value; - assert(ev->initialized); - value = qatomic_read(&ev->value); - smp_mb_acquire(); - if (value == EV_SET) { - /* If there was a concurrent reset (or even reset+wait), - * do nothing. Otherwise change EV_SET->EV_FREE. - */ - qatomic_or(&ev->value, EV_FREE); - } + + /* + * If there was a concurrent reset (or even reset+wait), + * do nothing. Otherwise change EV_SET->EV_FREE. + */ + qatomic_or(&ev->value, EV_FREE); + + /* + * Order reset before checking the condition in the caller. + * Pairs with the first memory barrier in qemu_event_set(). + */ + smp_mb__after_rmw(); } void qemu_event_wait(QemuEvent *ev) @@ -277,29 +314,49 @@ void qemu_event_wait(QemuEvent *ev) unsigned value; assert(ev->initialized); - value = qatomic_read(&ev->value); - smp_mb_acquire(); + + /* + * qemu_event_wait must synchronize with qemu_event_set even if it does + * not go down the slow path, so this load-acquire is needed that + * synchronizes with the first memory barrier in qemu_event_set(). + * + * If we do go down the slow path, there is no requirement at all: we + * might miss a qemu_event_set() here but ultimately the memory barrier in + * qemu_futex_wait() will ensure the check is done correctly. + */ + value = qatomic_load_acquire(&ev->value); if (value != EV_SET) { if (value == EV_FREE) { - /* qemu_event_set is not yet going to call SetEvent, but we are - * going to do another check for EV_SET below when setting EV_BUSY. - * At that point it is safe to call WaitForSingleObject. + /* + * Here the underlying kernel event is reset, but qemu_event_set is + * not yet going to call SetEvent. However, there will be another + * check for EV_SET below when setting EV_BUSY. At that point it + * is safe to call WaitForSingleObject. */ ResetEvent(ev->event); - /* Tell qemu_event_set that there are waiters. No need to retry - * because there cannot be a concurrent busy->free transition. - * After the CAS, the event will be either set or busy. + /* + * It is not clear whether ResetEvent provides this barrier; kernel + * APIs (KeResetEvent/KeClearEvent) do not. Better safe than sorry! + */ + smp_mb(); + + /* + * Leave the event reset and tell qemu_event_set that there are + * waiters. No need to retry, because there cannot be a concurrent + * busy->free transition. After the CAS, the event will be either + * set or busy. */ if (qatomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) { - value = EV_SET; - } else { - value = EV_BUSY; + return; } } - if (value == EV_BUSY) { - WaitForSingleObject(ev->event, INFINITE); - } + + /* + * ev->value is now EV_BUSY. Since we didn't observe EV_SET, + * qemu_event_set() must observe EV_BUSY and call SetEvent(). + */ + WaitForSingleObject(ev->event, INFINITE); } } @@ -400,6 +457,25 @@ void *qemu_thread_join(QemuThread *thread) return ret; } +static bool set_thread_description(HANDLE h, const char *name) +{ + HRESULT hr; + g_autofree wchar_t *namew = NULL; + + if (!load_set_thread_description()) { + return false; + } + + namew = g_utf8_to_utf16(name, -1, NULL, NULL, NULL); + if (!namew) { + return false; + } + + hr = SetThreadDescriptionFunc(h, namew); + + return SUCCEEDED(hr); +} + void qemu_thread_create(QemuThread *thread, const char *name, void *(*start_routine)(void *), void *arg, int mode) @@ -423,10 +499,26 @@ void qemu_thread_create(QemuThread *thread, const char *name, if (!hThread) { error_exit(GetLastError(), __func__); } + if (name_threads && name && !set_thread_description(hThread, name)) { + fprintf(stderr, "qemu: failed to set thread description: %s\n", name); + } CloseHandle(hThread); + thread->data = data; } +int qemu_thread_set_affinity(QemuThread *thread, unsigned long *host_cpus, + unsigned long nbits) +{ + return -ENOSYS; +} + +int qemu_thread_get_affinity(QemuThread *thread, unsigned long **host_cpus, + unsigned long *nbits) +{ + return -ENOSYS; +} + void qemu_thread_get_self(QemuThread *thread) { thread->data = qemu_thread_data; diff --git a/util/qemu-timer.c b/util/qemu-timer.c index a670a57881..6a0de33dd2 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -261,6 +261,9 @@ int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask) } QLIST_FOREACH(timer_list, &clock->timerlists, list) { + if (!qatomic_read(&timer_list->active_timers)) { + continue; + } qemu_mutex_lock(&timer_list->active_timers_lock); ts = timer_list->active_timers; /* Skip all external timers */ diff --git a/util/qht.c b/util/qht.c index 065fc501f4..92c6b78759 100644 --- a/util/qht.c +++ b/util/qht.c @@ -151,6 +151,22 @@ struct qht_bucket { QEMU_BUILD_BUG_ON(sizeof(struct qht_bucket) > QHT_BUCKET_ALIGN); +/* + * Under TSAN, we use striped locks instead of one lock per bucket chain. + * This avoids crashing under TSAN, since TSAN aborts the program if more than + * 64 locks are held (this is a hardcoded limit in TSAN). + * When resizing a QHT we grab all the buckets' locks, which can easily + * go over TSAN's limit. By using striped locks, we avoid this problem. + * + * Note: this number must be a power of two for easy index computation. + */ +#define QHT_TSAN_BUCKET_LOCKS_BITS 4 +#define QHT_TSAN_BUCKET_LOCKS (1 << QHT_TSAN_BUCKET_LOCKS_BITS) + +struct qht_tsan_lock { + QemuSpin lock; +} QEMU_ALIGNED(QHT_BUCKET_ALIGN); + /** * struct qht_map - structure to track an array of buckets * @rcu: used by RCU. Keep it as the top field in the struct to help valgrind @@ -160,6 +176,7 @@ QEMU_BUILD_BUG_ON(sizeof(struct qht_bucket) > QHT_BUCKET_ALIGN); * @n_added_buckets: number of added (i.e. "non-head") buckets * @n_added_buckets_threshold: threshold to trigger an upward resize once the * number of added buckets surpasses it. + * @tsan_bucket_locks: Array of striped locks to be used only under TSAN. * * Buckets are tracked in what we call a "map", i.e. this structure. */ @@ -169,6 +186,9 @@ struct qht_map { size_t n_buckets; size_t n_added_buckets; size_t n_added_buckets_threshold; +#ifdef CONFIG_TSAN + struct qht_tsan_lock tsan_bucket_locks[QHT_TSAN_BUCKET_LOCKS]; +#endif }; /* trigger a resize when n_added_buckets > n_buckets / div */ @@ -229,10 +249,56 @@ static inline size_t qht_elems_to_buckets(size_t n_elems) return pow2ceil(n_elems / QHT_BUCKET_ENTRIES); } -static inline void qht_head_init(struct qht_bucket *b) +/* + * When using striped locks (i.e. under TSAN), we have to be careful not + * to operate on the same lock twice (e.g. when iterating through all buckets). + * We achieve this by operating only on each stripe's first matching lock. + */ +static inline void qht_do_if_first_in_stripe(struct qht_map *map, + struct qht_bucket *b, + void (*func)(QemuSpin *spin)) +{ +#ifdef CONFIG_TSAN + unsigned long bucket_idx = b - map->buckets; + bool is_first_in_stripe = (bucket_idx >> QHT_TSAN_BUCKET_LOCKS_BITS) == 0; + if (is_first_in_stripe) { + unsigned long lock_idx = bucket_idx & (QHT_TSAN_BUCKET_LOCKS - 1); + func(&map->tsan_bucket_locks[lock_idx].lock); + } +#else + func(&b->lock); +#endif +} + +static inline void qht_bucket_lock_do(struct qht_map *map, + struct qht_bucket *b, + void (*func)(QemuSpin *lock)) +{ +#ifdef CONFIG_TSAN + unsigned long bucket_idx = b - map->buckets; + unsigned long lock_idx = bucket_idx & (QHT_TSAN_BUCKET_LOCKS - 1); + func(&map->tsan_bucket_locks[lock_idx].lock); +#else + func(&b->lock); +#endif +} + +static inline void qht_bucket_lock(struct qht_map *map, + struct qht_bucket *b) +{ + qht_bucket_lock_do(map, b, qemu_spin_lock); +} + +static inline void qht_bucket_unlock(struct qht_map *map, + struct qht_bucket *b) +{ + qht_bucket_lock_do(map, b, qemu_spin_unlock); +} + +static inline void qht_head_init(struct qht_map *map, struct qht_bucket *b) { memset(b, 0, sizeof(*b)); - qemu_spin_init(&b->lock); + qht_do_if_first_in_stripe(map, b, qemu_spin_init); seqlock_init(&b->sequence); } @@ -250,7 +316,7 @@ static void qht_map_lock_buckets(struct qht_map *map) for (i = 0; i < map->n_buckets; i++) { struct qht_bucket *b = &map->buckets[i]; - qemu_spin_lock(&b->lock); + qht_do_if_first_in_stripe(map, b, qemu_spin_lock); } } @@ -261,7 +327,7 @@ static void qht_map_unlock_buckets(struct qht_map *map) for (i = 0; i < map->n_buckets; i++) { struct qht_bucket *b = &map->buckets[i]; - qemu_spin_unlock(&b->lock); + qht_do_if_first_in_stripe(map, b, qemu_spin_unlock); } } @@ -308,7 +374,7 @@ void qht_map_lock_buckets__no_stale(struct qht *ht, struct qht_map **pmap) * Get a head bucket and lock it, making sure its parent map is not stale. * @pmap is filled with a pointer to the bucket's parent map. * - * Unlock with qemu_spin_unlock(&b->lock). + * Unlock with qht_bucket_unlock. * * Note: callers cannot have ht->lock held. */ @@ -322,18 +388,18 @@ struct qht_bucket *qht_bucket_lock__no_stale(struct qht *ht, uint32_t hash, map = qatomic_rcu_read(&ht->map); b = qht_map_to_bucket(map, hash); - qemu_spin_lock(&b->lock); + qht_bucket_lock(map, b); if (likely(!qht_map_is_stale__locked(ht, map))) { *pmap = map; return b; } - qemu_spin_unlock(&b->lock); + qht_bucket_unlock(map, b); /* we raced with a resize; acquire ht->lock to see the updated ht->map */ qht_lock(ht); map = ht->map; b = qht_map_to_bucket(map, hash); - qemu_spin_lock(&b->lock); + qht_bucket_lock(map, b); qht_unlock(ht); *pmap = map; return b; @@ -345,12 +411,13 @@ static inline bool qht_map_needs_resize(const struct qht_map *map) map->n_added_buckets_threshold; } -static inline void qht_chain_destroy(const struct qht_bucket *head) +static inline void qht_chain_destroy(struct qht_map *map, + struct qht_bucket *head) { struct qht_bucket *curr = head->next; struct qht_bucket *prev; - qemu_spin_destroy(&head->lock); + qht_do_if_first_in_stripe(map, head, qemu_spin_destroy); while (curr) { prev = curr; curr = curr->next; @@ -364,7 +431,7 @@ static void qht_map_destroy(struct qht_map *map) size_t i; for (i = 0; i < map->n_buckets; i++) { - qht_chain_destroy(&map->buckets[i]); + qht_chain_destroy(map, &map->buckets[i]); } qemu_vfree(map->buckets); g_free(map); @@ -390,7 +457,7 @@ static struct qht_map *qht_map_create(size_t n_buckets) map->buckets = qemu_memalign(QHT_BUCKET_ALIGN, sizeof(*map->buckets) * n_buckets); for (i = 0; i < n_buckets; i++) { - qht_head_init(&map->buckets[i]); + qht_head_init(map, &map->buckets[i]); } return map; } @@ -638,7 +705,7 @@ bool qht_insert(struct qht *ht, void *p, uint32_t hash, void **existing) b = qht_bucket_lock__no_stale(ht, hash, &map); prev = qht_insert__locked(ht, map, b, p, hash, &needs_resize); qht_bucket_debug__locked(b); - qemu_spin_unlock(&b->lock); + qht_bucket_unlock(map, b); if (unlikely(needs_resize) && ht->mode & QHT_MODE_AUTO_RESIZE) { qht_grow_maybe(ht); @@ -688,7 +755,7 @@ static inline void qht_bucket_remove_entry(struct qht_bucket *orig, int pos) int i; if (qht_entry_is_last(orig, pos)) { - orig->hashes[pos] = 0; + qatomic_set(&orig->hashes[pos], 0); qatomic_set(&orig->pointers[pos], NULL); return; } @@ -749,7 +816,7 @@ bool qht_remove(struct qht *ht, const void *p, uint32_t hash) b = qht_bucket_lock__no_stale(ht, hash, &map); ret = qht_remove__locked(b, p, hash); qht_bucket_debug__locked(b); - qemu_spin_unlock(&b->lock); + qht_bucket_unlock(map, b); return ret; } diff --git a/util/qsp.c b/util/qsp.c index 8562b14a87..6b783e2e7f 100644 --- a/util/qsp.c +++ b/util/qsp.c @@ -124,7 +124,7 @@ static const char * const qsp_typenames[] = { [QSP_CONDVAR] = "condvar", }; -QemuMutexLockFunc qemu_bql_mutex_lock_func = qemu_mutex_lock_impl; +QemuMutexLockFunc bql_mutex_lock_func = qemu_mutex_lock_impl; QemuMutexLockFunc qemu_mutex_lock_func = qemu_mutex_lock_impl; QemuMutexTrylockFunc qemu_mutex_trylock_func = qemu_mutex_trylock_impl; QemuRecMutexLockFunc qemu_rec_mutex_lock_func = qemu_rec_mutex_lock_impl; @@ -144,7 +144,7 @@ uint32_t do_qsp_callsite_hash(const QSPCallSite *callsite, uint64_t ab) uint32_t e = callsite->line; uint32_t f = callsite->type; - return qemu_xxhash6(ab, cd, e, f); + return qemu_xxhash8(ab, cd, 0, e, f); } static inline @@ -439,7 +439,7 @@ void qsp_enable(void) { qatomic_set(&qemu_mutex_lock_func, qsp_mutex_lock); qatomic_set(&qemu_mutex_trylock_func, qsp_mutex_trylock); - qatomic_set(&qemu_bql_mutex_lock_func, qsp_bql_mutex_lock); + qatomic_set(&bql_mutex_lock_func, qsp_bql_mutex_lock); qatomic_set(&qemu_rec_mutex_lock_func, qsp_rec_mutex_lock); qatomic_set(&qemu_rec_mutex_trylock_func, qsp_rec_mutex_trylock); qatomic_set(&qemu_cond_wait_func, qsp_cond_wait); @@ -450,7 +450,7 @@ void qsp_disable(void) { qatomic_set(&qemu_mutex_lock_func, qemu_mutex_lock_impl); qatomic_set(&qemu_mutex_trylock_func, qemu_mutex_trylock_impl); - qatomic_set(&qemu_bql_mutex_lock_func, qemu_mutex_lock_impl); + qatomic_set(&bql_mutex_lock_func, qemu_mutex_lock_impl); qatomic_set(&qemu_rec_mutex_lock_func, qemu_rec_mutex_lock_impl); qatomic_set(&qemu_rec_mutex_trylock_func, qemu_rec_mutex_trylock_impl); qatomic_set(&qemu_cond_wait_func, qemu_cond_wait_impl); diff --git a/util/qtree.c b/util/qtree.c new file mode 100644 index 0000000000..31f0b46182 --- /dev/null +++ b/util/qtree.c @@ -0,0 +1,1390 @@ +/* + * GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 . + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +/* + * MT safe + */ + +#include "qemu/osdep.h" +#include "qemu/qtree.h" + +/** + * SECTION:trees-binary + * @title: Balanced Binary Trees + * @short_description: a sorted collection of key/value pairs optimized + * for searching and traversing in order + * + * The #QTree structure and its associated functions provide a sorted + * collection of key/value pairs optimized for searching and traversing + * in order. This means that most of the operations (access, search, + * insertion, deletion, ...) on #QTree are O(log(n)) in average and O(n) + * in worst case for time complexity. But, note that maintaining a + * balanced sorted #QTree of n elements is done in time O(n log(n)). + * + * To create a new #QTree use q_tree_new(). + * + * To insert a key/value pair into a #QTree use q_tree_insert() + * (O(n log(n))). + * + * To remove a key/value pair use q_tree_remove() (O(n log(n))). + * + * To look up the value corresponding to a given key, use + * q_tree_lookup() and q_tree_lookup_extended(). + * + * To find out the number of nodes in a #QTree, use q_tree_nnodes(). To + * get the height of a #QTree, use q_tree_height(). + * + * To traverse a #QTree, calling a function for each node visited in + * the traversal, use q_tree_foreach(). + * + * To destroy a #QTree, use q_tree_destroy(). + **/ + +#define MAX_GTREE_HEIGHT 40 + +/** + * QTree: + * + * The QTree struct is an opaque data structure representing a + * [balanced binary tree][glib-Balanced-Binary-Trees]. It should be + * accessed only by using the following functions. + */ +struct _QTree { + QTreeNode *root; + GCompareDataFunc key_compare; + GDestroyNotify key_destroy_func; + GDestroyNotify value_destroy_func; + gpointer key_compare_data; + guint nnodes; + gint ref_count; +}; + +struct _QTreeNode { + gpointer key; /* key for this node */ + gpointer value; /* value stored at this node */ + QTreeNode *left; /* left subtree */ + QTreeNode *right; /* right subtree */ + gint8 balance; /* height (right) - height (left) */ + guint8 left_child; + guint8 right_child; +}; + + +static QTreeNode *q_tree_node_new(gpointer key, + gpointer value); +static QTreeNode *q_tree_insert_internal(QTree *tree, + gpointer key, + gpointer value, + gboolean replace); +static gboolean q_tree_remove_internal(QTree *tree, + gconstpointer key, + gboolean steal); +static QTreeNode *q_tree_node_balance(QTreeNode *node); +static QTreeNode *q_tree_find_node(QTree *tree, + gconstpointer key); +static QTreeNode *q_tree_node_search(QTreeNode *node, + GCompareFunc search_func, + gconstpointer data); +static QTreeNode *q_tree_node_rotate_left(QTreeNode *node); +static QTreeNode *q_tree_node_rotate_right(QTreeNode *node); +#ifdef Q_TREE_DEBUG +static void q_tree_node_check(QTreeNode *node); +#endif + +static QTreeNode* +q_tree_node_new(gpointer key, + gpointer value) +{ + QTreeNode *node = g_new(QTreeNode, 1); + + node->balance = 0; + node->left = NULL; + node->right = NULL; + node->left_child = FALSE; + node->right_child = FALSE; + node->key = key; + node->value = value; + + return node; +} + +/** + * q_tree_new: + * @key_compare_func: the function used to order the nodes in the #QTree. + * It should return values similar to the standard strcmp() function - + * 0 if the two arguments are equal, a negative value if the first argument + * comes before the second, or a positive value if the first argument comes + * after the second. + * + * Creates a new #QTree. + * + * Returns: a newly allocated #QTree + */ +QTree * +q_tree_new(GCompareFunc key_compare_func) +{ + g_return_val_if_fail(key_compare_func != NULL, NULL); + + return q_tree_new_full((GCompareDataFunc) key_compare_func, NULL, + NULL, NULL); +} + +/** + * q_tree_new_with_data: + * @key_compare_func: qsort()-style comparison function + * @key_compare_data: data to pass to comparison function + * + * Creates a new #QTree with a comparison function that accepts user data. + * See q_tree_new() for more details. + * + * Returns: a newly allocated #QTree + */ +QTree * +q_tree_new_with_data(GCompareDataFunc key_compare_func, + gpointer key_compare_data) +{ + g_return_val_if_fail(key_compare_func != NULL, NULL); + + return q_tree_new_full(key_compare_func, key_compare_data, + NULL, NULL); +} + +/** + * q_tree_new_full: + * @key_compare_func: qsort()-style comparison function + * @key_compare_data: data to pass to comparison function + * @key_destroy_func: a function to free the memory allocated for the key + * used when removing the entry from the #QTree or %NULL if you don't + * want to supply such a function + * @value_destroy_func: a function to free the memory allocated for the + * value used when removing the entry from the #QTree or %NULL if you + * don't want to supply such a function + * + * Creates a new #QTree like q_tree_new() and allows to specify functions + * to free the memory allocated for the key and value that get called when + * removing the entry from the #QTree. + * + * Returns: a newly allocated #QTree + */ +QTree * +q_tree_new_full(GCompareDataFunc key_compare_func, + gpointer key_compare_data, + GDestroyNotify key_destroy_func, + GDestroyNotify value_destroy_func) +{ + QTree *tree; + + g_return_val_if_fail(key_compare_func != NULL, NULL); + + tree = g_new(QTree, 1); + tree->root = NULL; + tree->key_compare = key_compare_func; + tree->key_destroy_func = key_destroy_func; + tree->value_destroy_func = value_destroy_func; + tree->key_compare_data = key_compare_data; + tree->nnodes = 0; + tree->ref_count = 1; + + return tree; +} + +/** + * q_tree_node_first: + * @tree: a #QTree + * + * Returns the first in-order node of the tree, or %NULL + * for an empty tree. + * + * Returns: (nullable) (transfer none): the first node in the tree + * + * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API. + */ +static QTreeNode * +q_tree_node_first(QTree *tree) +{ + QTreeNode *tmp; + + g_return_val_if_fail(tree != NULL, NULL); + + if (!tree->root) { + return NULL; + } + + tmp = tree->root; + + while (tmp->left_child) { + tmp = tmp->left; + } + + return tmp; +} + +/** + * q_tree_node_previous + * @node: a #QTree node + * + * Returns the previous in-order node of the tree, or %NULL + * if the passed node was already the first one. + * + * Returns: (nullable) (transfer none): the previous node in the tree + * + * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API. + */ +static QTreeNode * +q_tree_node_previous(QTreeNode *node) +{ + QTreeNode *tmp; + + g_return_val_if_fail(node != NULL, NULL); + + tmp = node->left; + + if (node->left_child) { + while (tmp->right_child) { + tmp = tmp->right; + } + } + + return tmp; +} + +/** + * q_tree_node_next + * @node: a #QTree node + * + * Returns the next in-order node of the tree, or %NULL + * if the passed node was already the last one. + * + * Returns: (nullable) (transfer none): the next node in the tree + * + * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API. + */ +static QTreeNode * +q_tree_node_next(QTreeNode *node) +{ + QTreeNode *tmp; + + g_return_val_if_fail(node != NULL, NULL); + + tmp = node->right; + + if (node->right_child) { + while (tmp->left_child) { + tmp = tmp->left; + } + } + + return tmp; +} + +/** + * q_tree_remove_all: + * @tree: a #QTree + * + * Removes all nodes from a #QTree and destroys their keys and values, + * then resets the #QTree’s root to %NULL. + * + * Since: 2.70 in GLib. Internal in Qtree, i.e. not in the public API. + */ +static void QEMU_DISABLE_CFI +q_tree_remove_all(QTree *tree) +{ + QTreeNode *node; + QTreeNode *next; + + g_return_if_fail(tree != NULL); + + node = q_tree_node_first(tree); + + while (node) { + next = q_tree_node_next(node); + + if (tree->key_destroy_func) { + tree->key_destroy_func(node->key); + } + if (tree->value_destroy_func) { + tree->value_destroy_func(node->value); + } + g_free(node); + +#ifdef Q_TREE_DEBUG + g_assert(tree->nnodes > 0); + tree->nnodes--; +#endif + + node = next; + } + +#ifdef Q_TREE_DEBUG + g_assert(tree->nnodes == 0); +#endif + + tree->root = NULL; +#ifndef Q_TREE_DEBUG + tree->nnodes = 0; +#endif +} + +/** + * q_tree_ref: + * @tree: a #QTree + * + * Increments the reference count of @tree by one. + * + * It is safe to call this function from any thread. + * + * Returns: the passed in #QTree + * + * Since: 2.22 + */ +QTree * +q_tree_ref(QTree *tree) +{ + g_return_val_if_fail(tree != NULL, NULL); + + g_atomic_int_inc(&tree->ref_count); + + return tree; +} + +/** + * q_tree_unref: + * @tree: a #QTree + * + * Decrements the reference count of @tree by one. + * If the reference count drops to 0, all keys and values will + * be destroyed (if destroy functions were specified) and all + * memory allocated by @tree will be released. + * + * It is safe to call this function from any thread. + * + * Since: 2.22 + */ +void +q_tree_unref(QTree *tree) +{ + g_return_if_fail(tree != NULL); + + if (g_atomic_int_dec_and_test(&tree->ref_count)) { + q_tree_remove_all(tree); + g_free(tree); + } +} + +/** + * q_tree_destroy: + * @tree: a #QTree + * + * Removes all keys and values from the #QTree and decreases its + * reference count by one. If keys and/or values are dynamically + * allocated, you should either free them first or create the #QTree + * using q_tree_new_full(). In the latter case the destroy functions + * you supplied will be called on all keys and values before destroying + * the #QTree. + */ +void +q_tree_destroy(QTree *tree) +{ + g_return_if_fail(tree != NULL); + + q_tree_remove_all(tree); + q_tree_unref(tree); +} + +/** + * q_tree_insert_node: + * @tree: a #QTree + * @key: the key to insert + * @value: the value corresponding to the key + * + * Inserts a key/value pair into a #QTree. + * + * If the given key already exists in the #QTree its corresponding value + * is set to the new value. If you supplied a @value_destroy_func when + * creating the #QTree, the old value is freed using that function. If + * you supplied a @key_destroy_func when creating the #QTree, the passed + * key is freed using that function. + * + * The tree is automatically 'balanced' as new key/value pairs are added, + * so that the distance from the root to every leaf is as small as possible. + * The cost of maintaining a balanced tree while inserting new key/value + * result in a O(n log(n)) operation where most of the other operations + * are O(log(n)). + * + * Returns: (transfer none): the inserted (or set) node. + * + * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API. + */ +static QTreeNode * +q_tree_insert_node(QTree *tree, + gpointer key, + gpointer value) +{ + QTreeNode *node; + + g_return_val_if_fail(tree != NULL, NULL); + + node = q_tree_insert_internal(tree, key, value, FALSE); + +#ifdef Q_TREE_DEBUG + q_tree_node_check(tree->root); +#endif + + return node; +} + +/** + * q_tree_insert: + * @tree: a #QTree + * @key: the key to insert + * @value: the value corresponding to the key + * + * Inserts a key/value pair into a #QTree. + * + * Inserts a new key and value into a #QTree as q_tree_insert_node() does, + * only this function does not return the inserted or set node. + */ +void +q_tree_insert(QTree *tree, + gpointer key, + gpointer value) +{ + q_tree_insert_node(tree, key, value); +} + +/** + * q_tree_replace_node: + * @tree: a #QTree + * @key: the key to insert + * @value: the value corresponding to the key + * + * Inserts a new key and value into a #QTree similar to q_tree_insert_node(). + * The difference is that if the key already exists in the #QTree, it gets + * replaced by the new key. If you supplied a @value_destroy_func when + * creating the #QTree, the old value is freed using that function. If you + * supplied a @key_destroy_func when creating the #QTree, the old key is + * freed using that function. + * + * The tree is automatically 'balanced' as new key/value pairs are added, + * so that the distance from the root to every leaf is as small as possible. + * + * Returns: (transfer none): the inserted (or set) node. + * + * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API. + */ +static QTreeNode * +q_tree_replace_node(QTree *tree, + gpointer key, + gpointer value) +{ + QTreeNode *node; + + g_return_val_if_fail(tree != NULL, NULL); + + node = q_tree_insert_internal(tree, key, value, TRUE); + +#ifdef Q_TREE_DEBUG + q_tree_node_check(tree->root); +#endif + + return node; +} + +/** + * q_tree_replace: + * @tree: a #QTree + * @key: the key to insert + * @value: the value corresponding to the key + * + * Inserts a new key and value into a #QTree as q_tree_replace_node() does, + * only this function does not return the inserted or set node. + */ +void +q_tree_replace(QTree *tree, + gpointer key, + gpointer value) +{ + q_tree_replace_node(tree, key, value); +} + +/* internal insert routine */ +static QTreeNode * QEMU_DISABLE_CFI +q_tree_insert_internal(QTree *tree, + gpointer key, + gpointer value, + gboolean replace) +{ + QTreeNode *node, *retnode; + QTreeNode *path[MAX_GTREE_HEIGHT]; + int idx; + + g_return_val_if_fail(tree != NULL, NULL); + + if (!tree->root) { + tree->root = q_tree_node_new(key, value); + tree->nnodes++; + return tree->root; + } + + idx = 0; + path[idx++] = NULL; + node = tree->root; + + while (1) { + int cmp = tree->key_compare(key, node->key, tree->key_compare_data); + + if (cmp == 0) { + if (tree->value_destroy_func) { + tree->value_destroy_func(node->value); + } + + node->value = value; + + if (replace) { + if (tree->key_destroy_func) { + tree->key_destroy_func(node->key); + } + + node->key = key; + } else { + /* free the passed key */ + if (tree->key_destroy_func) { + tree->key_destroy_func(key); + } + } + + return node; + } else if (cmp < 0) { + if (node->left_child) { + path[idx++] = node; + node = node->left; + } else { + QTreeNode *child = q_tree_node_new(key, value); + + child->left = node->left; + child->right = node; + node->left = child; + node->left_child = TRUE; + node->balance -= 1; + + tree->nnodes++; + + retnode = child; + break; + } + } else { + if (node->right_child) { + path[idx++] = node; + node = node->right; + } else { + QTreeNode *child = q_tree_node_new(key, value); + + child->right = node->right; + child->left = node; + node->right = child; + node->right_child = TRUE; + node->balance += 1; + + tree->nnodes++; + + retnode = child; + break; + } + } + } + + /* + * Restore balance. This is the goodness of a non-recursive + * implementation, when we are done with balancing we 'break' + * the loop and we are done. + */ + while (1) { + QTreeNode *bparent = path[--idx]; + gboolean left_node = (bparent && node == bparent->left); + g_assert(!bparent || bparent->left == node || bparent->right == node); + + if (node->balance < -1 || node->balance > 1) { + node = q_tree_node_balance(node); + if (bparent == NULL) { + tree->root = node; + } else if (left_node) { + bparent->left = node; + } else { + bparent->right = node; + } + } + + if (node->balance == 0 || bparent == NULL) { + break; + } + + if (left_node) { + bparent->balance -= 1; + } else { + bparent->balance += 1; + } + + node = bparent; + } + + return retnode; +} + +/** + * q_tree_remove: + * @tree: a #QTree + * @key: the key to remove + * + * Removes a key/value pair from a #QTree. + * + * If the #QTree was created using q_tree_new_full(), the key and value + * are freed using the supplied destroy functions, otherwise you have to + * make sure that any dynamically allocated values are freed yourself. + * If the key does not exist in the #QTree, the function does nothing. + * + * The cost of maintaining a balanced tree while removing a key/value + * result in a O(n log(n)) operation where most of the other operations + * are O(log(n)). + * + * Returns: %TRUE if the key was found (prior to 2.8, this function + * returned nothing) + */ +gboolean +q_tree_remove(QTree *tree, + gconstpointer key) +{ + gboolean removed; + + g_return_val_if_fail(tree != NULL, FALSE); + + removed = q_tree_remove_internal(tree, key, FALSE); + +#ifdef Q_TREE_DEBUG + q_tree_node_check(tree->root); +#endif + + return removed; +} + +/** + * q_tree_steal: + * @tree: a #QTree + * @key: the key to remove + * + * Removes a key and its associated value from a #QTree without calling + * the key and value destroy functions. + * + * If the key does not exist in the #QTree, the function does nothing. + * + * Returns: %TRUE if the key was found (prior to 2.8, this function + * returned nothing) + */ +gboolean +q_tree_steal(QTree *tree, + gconstpointer key) +{ + gboolean removed; + + g_return_val_if_fail(tree != NULL, FALSE); + + removed = q_tree_remove_internal(tree, key, TRUE); + +#ifdef Q_TREE_DEBUG + q_tree_node_check(tree->root); +#endif + + return removed; +} + +/* internal remove routine */ +static gboolean QEMU_DISABLE_CFI +q_tree_remove_internal(QTree *tree, + gconstpointer key, + gboolean steal) +{ + QTreeNode *node, *parent, *balance; + QTreeNode *path[MAX_GTREE_HEIGHT]; + int idx; + gboolean left_node; + + g_return_val_if_fail(tree != NULL, FALSE); + + if (!tree->root) { + return FALSE; + } + + idx = 0; + path[idx++] = NULL; + node = tree->root; + + while (1) { + int cmp = tree->key_compare(key, node->key, tree->key_compare_data); + + if (cmp == 0) { + break; + } else if (cmp < 0) { + if (!node->left_child) { + return FALSE; + } + + path[idx++] = node; + node = node->left; + } else { + if (!node->right_child) { + return FALSE; + } + + path[idx++] = node; + node = node->right; + } + } + + /* + * The following code is almost equal to q_tree_remove_node, + * except that we do not have to call q_tree_node_parent. + */ + balance = parent = path[--idx]; + g_assert(!parent || parent->left == node || parent->right == node); + left_node = (parent && node == parent->left); + + if (!node->left_child) { + if (!node->right_child) { + if (!parent) { + tree->root = NULL; + } else if (left_node) { + parent->left_child = FALSE; + parent->left = node->left; + parent->balance += 1; + } else { + parent->right_child = FALSE; + parent->right = node->right; + parent->balance -= 1; + } + } else { + /* node has a right child */ + QTreeNode *tmp = q_tree_node_next(node); + tmp->left = node->left; + + if (!parent) { + tree->root = node->right; + } else if (left_node) { + parent->left = node->right; + parent->balance += 1; + } else { + parent->right = node->right; + parent->balance -= 1; + } + } + } else { + /* node has a left child */ + if (!node->right_child) { + QTreeNode *tmp = q_tree_node_previous(node); + tmp->right = node->right; + + if (parent == NULL) { + tree->root = node->left; + } else if (left_node) { + parent->left = node->left; + parent->balance += 1; + } else { + parent->right = node->left; + parent->balance -= 1; + } + } else { + /* node has a both children (pant, pant!) */ + QTreeNode *prev = node->left; + QTreeNode *next = node->right; + QTreeNode *nextp = node; + int old_idx = idx + 1; + idx++; + + /* path[idx] == parent */ + /* find the immediately next node (and its parent) */ + while (next->left_child) { + path[++idx] = nextp = next; + next = next->left; + } + + path[old_idx] = next; + balance = path[idx]; + + /* remove 'next' from the tree */ + if (nextp != node) { + if (next->right_child) { + nextp->left = next->right; + } else { + nextp->left_child = FALSE; + } + nextp->balance += 1; + + next->right_child = TRUE; + next->right = node->right; + } else { + node->balance -= 1; + } + + /* set the prev to point to the right place */ + while (prev->right_child) { + prev = prev->right; + } + prev->right = next; + + /* prepare 'next' to replace 'node' */ + next->left_child = TRUE; + next->left = node->left; + next->balance = node->balance; + + if (!parent) { + tree->root = next; + } else if (left_node) { + parent->left = next; + } else { + parent->right = next; + } + } + } + + /* restore balance */ + if (balance) { + while (1) { + QTreeNode *bparent = path[--idx]; + g_assert(!bparent || + bparent->left == balance || + bparent->right == balance); + left_node = (bparent && balance == bparent->left); + + if (balance->balance < -1 || balance->balance > 1) { + balance = q_tree_node_balance(balance); + if (!bparent) { + tree->root = balance; + } else if (left_node) { + bparent->left = balance; + } else { + bparent->right = balance; + } + } + + if (balance->balance != 0 || !bparent) { + break; + } + + if (left_node) { + bparent->balance += 1; + } else { + bparent->balance -= 1; + } + + balance = bparent; + } + } + + if (!steal) { + if (tree->key_destroy_func) { + tree->key_destroy_func(node->key); + } + if (tree->value_destroy_func) { + tree->value_destroy_func(node->value); + } + } + + g_free(node); + + tree->nnodes--; + + return TRUE; +} + +/** + * q_tree_lookup_node: + * @tree: a #QTree + * @key: the key to look up + * + * Gets the tree node corresponding to the given key. Since a #QTree is + * automatically balanced as key/value pairs are added, key lookup + * is O(log n) (where n is the number of key/value pairs in the tree). + * + * Returns: (nullable) (transfer none): the tree node corresponding to + * the key, or %NULL if the key was not found + * + * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API. + */ +static QTreeNode * +q_tree_lookup_node(QTree *tree, + gconstpointer key) +{ + g_return_val_if_fail(tree != NULL, NULL); + + return q_tree_find_node(tree, key); +} + +/** + * q_tree_lookup: + * @tree: a #QTree + * @key: the key to look up + * + * Gets the value corresponding to the given key. Since a #QTree is + * automatically balanced as key/value pairs are added, key lookup + * is O(log n) (where n is the number of key/value pairs in the tree). + * + * Returns: the value corresponding to the key, or %NULL + * if the key was not found + */ +gpointer +q_tree_lookup(QTree *tree, + gconstpointer key) +{ + QTreeNode *node; + + node = q_tree_lookup_node(tree, key); + + return node ? node->value : NULL; +} + +/** + * q_tree_lookup_extended: + * @tree: a #QTree + * @lookup_key: the key to look up + * @orig_key: (out) (optional) (nullable): returns the original key + * @value: (out) (optional) (nullable): returns the value associated with + * the key + * + * Looks up a key in the #QTree, returning the original key and the + * associated value. This is useful if you need to free the memory + * allocated for the original key, for example before calling + * q_tree_remove(). + * + * Returns: %TRUE if the key was found in the #QTree + */ +gboolean +q_tree_lookup_extended(QTree *tree, + gconstpointer lookup_key, + gpointer *orig_key, + gpointer *value) +{ + QTreeNode *node; + + g_return_val_if_fail(tree != NULL, FALSE); + + node = q_tree_find_node(tree, lookup_key); + + if (node) { + if (orig_key) { + *orig_key = node->key; + } + if (value) { + *value = node->value; + } + return TRUE; + } else { + return FALSE; + } +} + +/** + * q_tree_foreach: + * @tree: a #QTree + * @func: the function to call for each node visited. + * If this function returns %TRUE, the traversal is stopped. + * @user_data: user data to pass to the function + * + * Calls the given function for each of the key/value pairs in the #QTree. + * The function is passed the key and value of each pair, and the given + * @data parameter. The tree is traversed in sorted order. + * + * The tree may not be modified while iterating over it (you can't + * add/remove items). To remove all items matching a predicate, you need + * to add each item to a list in your #GTraverseFunc as you walk over + * the tree, then walk the list and remove each item. + */ +void +q_tree_foreach(QTree *tree, + GTraverseFunc func, + gpointer user_data) +{ + QTreeNode *node; + + g_return_if_fail(tree != NULL); + + if (!tree->root) { + return; + } + + node = q_tree_node_first(tree); + + while (node) { + if ((*func)(node->key, node->value, user_data)) { + break; + } + + node = q_tree_node_next(node); + } +} + +/** + * q_tree_search_node: + * @tree: a #QTree + * @search_func: a function used to search the #QTree + * @user_data: the data passed as the second argument to @search_func + * + * Searches a #QTree using @search_func. + * + * The @search_func is called with a pointer to the key of a key/value + * pair in the tree, and the passed in @user_data. If @search_func returns + * 0 for a key/value pair, then the corresponding node is returned as + * the result of q_tree_search(). If @search_func returns -1, searching + * will proceed among the key/value pairs that have a smaller key; if + * @search_func returns 1, searching will proceed among the key/value + * pairs that have a larger key. + * + * Returns: (nullable) (transfer none): the node corresponding to the + * found key, or %NULL if the key was not found + * + * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API. + */ +static QTreeNode * +q_tree_search_node(QTree *tree, + GCompareFunc search_func, + gconstpointer user_data) +{ + g_return_val_if_fail(tree != NULL, NULL); + + if (!tree->root) { + return NULL; + } + + return q_tree_node_search(tree->root, search_func, user_data); +} + +/** + * q_tree_search: + * @tree: a #QTree + * @search_func: a function used to search the #QTree + * @user_data: the data passed as the second argument to @search_func + * + * Searches a #QTree using @search_func. + * + * The @search_func is called with a pointer to the key of a key/value + * pair in the tree, and the passed in @user_data. If @search_func returns + * 0 for a key/value pair, then the corresponding value is returned as + * the result of q_tree_search(). If @search_func returns -1, searching + * will proceed among the key/value pairs that have a smaller key; if + * @search_func returns 1, searching will proceed among the key/value + * pairs that have a larger key. + * + * Returns: the value corresponding to the found key, or %NULL + * if the key was not found + */ +gpointer +q_tree_search(QTree *tree, + GCompareFunc search_func, + gconstpointer user_data) +{ + QTreeNode *node; + + node = q_tree_search_node(tree, search_func, user_data); + + return node ? node->value : NULL; +} + +/** + * q_tree_height: + * @tree: a #QTree + * + * Gets the height of a #QTree. + * + * If the #QTree contains no nodes, the height is 0. + * If the #QTree contains only one root node the height is 1. + * If the root node has children the height is 2, etc. + * + * Returns: the height of @tree + */ +gint +q_tree_height(QTree *tree) +{ + QTreeNode *node; + gint height; + + g_return_val_if_fail(tree != NULL, 0); + + if (!tree->root) { + return 0; + } + + height = 0; + node = tree->root; + + while (1) { + height += 1 + MAX(node->balance, 0); + + if (!node->left_child) { + return height; + } + + node = node->left; + } +} + +/** + * q_tree_nnodes: + * @tree: a #QTree + * + * Gets the number of nodes in a #QTree. + * + * Returns: the number of nodes in @tree + */ +gint +q_tree_nnodes(QTree *tree) +{ + g_return_val_if_fail(tree != NULL, 0); + + return tree->nnodes; +} + +static QTreeNode * +q_tree_node_balance(QTreeNode *node) +{ + if (node->balance < -1) { + if (node->left->balance > 0) { + node->left = q_tree_node_rotate_left(node->left); + } + node = q_tree_node_rotate_right(node); + } else if (node->balance > 1) { + if (node->right->balance < 0) { + node->right = q_tree_node_rotate_right(node->right); + } + node = q_tree_node_rotate_left(node); + } + + return node; +} + +static QTreeNode * QEMU_DISABLE_CFI +q_tree_find_node(QTree *tree, + gconstpointer key) +{ + QTreeNode *node; + gint cmp; + + node = tree->root; + if (!node) { + return NULL; + } + + while (1) { + cmp = tree->key_compare(key, node->key, tree->key_compare_data); + if (cmp == 0) { + return node; + } else if (cmp < 0) { + if (!node->left_child) { + return NULL; + } + + node = node->left; + } else { + if (!node->right_child) { + return NULL; + } + + node = node->right; + } + } +} + +static QTreeNode * +q_tree_node_search(QTreeNode *node, + GCompareFunc search_func, + gconstpointer data) +{ + gint dir; + + if (!node) { + return NULL; + } + + while (1) { + dir = (*search_func)(node->key, data); + if (dir == 0) { + return node; + } else if (dir < 0) { + if (!node->left_child) { + return NULL; + } + + node = node->left; + } else { + if (!node->right_child) { + return NULL; + } + + node = node->right; + } + } +} + +static QTreeNode * +q_tree_node_rotate_left(QTreeNode *node) +{ + QTreeNode *right; + gint a_bal; + gint b_bal; + + right = node->right; + + if (right->left_child) { + node->right = right->left; + } else { + node->right_child = FALSE; + right->left_child = TRUE; + } + right->left = node; + + a_bal = node->balance; + b_bal = right->balance; + + if (b_bal <= 0) { + if (a_bal >= 1) { + right->balance = b_bal - 1; + } else { + right->balance = a_bal + b_bal - 2; + } + node->balance = a_bal - 1; + } else { + if (a_bal <= b_bal) { + right->balance = a_bal - 2; + } else { + right->balance = b_bal - 1; + } + node->balance = a_bal - b_bal - 1; + } + + return right; +} + +static QTreeNode * +q_tree_node_rotate_right(QTreeNode *node) +{ + QTreeNode *left; + gint a_bal; + gint b_bal; + + left = node->left; + + if (left->right_child) { + node->left = left->right; + } else { + node->left_child = FALSE; + left->right_child = TRUE; + } + left->right = node; + + a_bal = node->balance; + b_bal = left->balance; + + if (b_bal <= 0) { + if (b_bal > a_bal) { + left->balance = b_bal + 1; + } else { + left->balance = a_bal + 2; + } + node->balance = a_bal - b_bal + 1; + } else { + if (a_bal <= -1) { + left->balance = b_bal + 1; + } else { + left->balance = a_bal + b_bal + 2; + } + node->balance = a_bal + 1; + } + + return left; +} + +#ifdef Q_TREE_DEBUG +static gint +q_tree_node_height(QTreeNode *node) +{ + gint left_height; + gint right_height; + + if (node) { + left_height = 0; + right_height = 0; + + if (node->left_child) { + left_height = q_tree_node_height(node->left); + } + + if (node->right_child) { + right_height = q_tree_node_height(node->right); + } + + return MAX(left_height, right_height) + 1; + } + + return 0; +} + +static void q_tree_node_check(QTreeNode *node) +{ + gint left_height; + gint right_height; + gint balance; + QTreeNode *tmp; + + if (node) { + if (node->left_child) { + tmp = q_tree_node_previous(node); + g_assert(tmp->right == node); + } + + if (node->right_child) { + tmp = q_tree_node_next(node); + g_assert(tmp->left == node); + } + + left_height = 0; + right_height = 0; + + if (node->left_child) { + left_height = q_tree_node_height(node->left); + } + if (node->right_child) { + right_height = q_tree_node_height(node->right); + } + + balance = right_height - left_height; + g_assert(balance == node->balance); + + if (node->left_child) { + q_tree_node_check(node->left); + } + if (node->right_child) { + q_tree_node_check(node->right); + } + } +} +#endif diff --git a/util/range.c b/util/range.c index 098d9d2dc0..f3f40098d5 100644 --- a/util/range.c +++ b/util/range.c @@ -20,11 +20,7 @@ #include "qemu/osdep.h" #include "qemu/range.h" -/* - * Return -1 if @a < @b, 1 @a > @b, and 0 if they touch or overlap. - * Both @a and @b must not be empty. - */ -static inline int range_compare(Range *a, Range *b) +int range_compare(Range *a, Range *b) { assert(!range_is_empty(a) && !range_is_empty(b)); @@ -70,3 +66,58 @@ GList *range_list_insert(GList *list, Range *data) return list; } + +static inline +GList *append_new_range(GList *list, uint64_t lob, uint64_t upb) +{ + Range *new = g_new0(Range, 1); + + range_set_bounds(new, lob, upb); + return g_list_append(list, new); +} + + +void range_inverse_array(GList *in, GList **rev, + uint64_t low, uint64_t high) +{ + Range *r, *rn; + GList *l = in, *out = *rev; + + for (l = in; l && range_upb(l->data) < low; l = l->next) { + continue; + } + + if (!l) { + out = append_new_range(out, low, high); + goto exit; + } + r = (Range *)l->data; + + /* first range lob is greater than min, insert a first range */ + if (range_lob(r) > low) { + out = append_new_range(out, low, MIN(range_lob(r) - 1, high)); + } + + /* insert a range in between each original range until we reach high */ + for (; l->next; l = l->next) { + r = (Range *)l->data; + rn = (Range *)l->next->data; + if (range_lob(r) >= high) { + goto exit; + } + if (range_compare(r, rn)) { + out = append_new_range(out, range_upb(r) + 1, + MIN(range_lob(rn) - 1, high)); + } + } + + /* last range */ + r = (Range *)l->data; + + /* last range upb is less than max, insert a last range */ + if (range_upb(r) < high) { + out = append_new_range(out, range_upb(r) + 1, high); + } +exit: + *rev = out; +} diff --git a/util/rcu.c b/util/rcu.c index b6d6c71cff..fa32c942e4 100644 --- a/util/rcu.c +++ b/util/rcu.c @@ -83,12 +83,6 @@ static void wait_for_readers(void) */ qemu_event_reset(&rcu_gp_event); - /* Instead of using qatomic_mb_set for index->waiting, and - * qatomic_mb_read for index->ctr, memory barriers are placed - * manually since writes to different threads are independent. - * qemu_event_reset has acquire semantics, so no memory barrier - * is needed here. - */ QLIST_FOREACH(index, ®istry, node) { qatomic_set(&index->waiting, true); } @@ -96,6 +90,10 @@ static void wait_for_readers(void) /* Here, order the stores to index->waiting before the loads of * index->ctr. Pairs with smp_mb_placeholder() in rcu_read_unlock(), * ensuring that the loads of index->ctr are sequentially consistent. + * + * If this is the last iteration, this barrier also prevents + * frees from seeping upwards, and orders the two wait phases + * on architectures with 32-bit longs; see synchronize_rcu(). */ smp_mb_global(); @@ -104,7 +102,7 @@ static void wait_for_readers(void) QLIST_REMOVE(index, node); QLIST_INSERT_HEAD(&qsreaders, index, node); - /* No need for mb_set here, worst of all we + /* No need for memory barriers here, worst of all we * get some extra futex wakeups. */ qatomic_set(&index->waiting, false); @@ -149,26 +147,26 @@ void synchronize_rcu(void) /* Write RCU-protected pointers before reading p_rcu_reader->ctr. * Pairs with smp_mb_placeholder() in rcu_read_lock(). + * + * Also orders write to RCU-protected pointers before + * write to rcu_gp_ctr. */ smp_mb_global(); QEMU_LOCK_GUARD(&rcu_registry_lock); if (!QLIST_EMPTY(®istry)) { - /* In either case, the qatomic_mb_set below blocks stores that free - * old RCU-protected pointers. - */ if (sizeof(rcu_gp_ctr) < 8) { /* For architectures with 32-bit longs, a two-subphases algorithm * ensures we do not encounter overflow bugs. * * Switch parity: 0 -> 1, 1 -> 0. */ - qatomic_mb_set(&rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR); + qatomic_set(&rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR); wait_for_readers(); - qatomic_mb_set(&rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR); + qatomic_set(&rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR); } else { /* Increment current grace period. */ - qatomic_mb_set(&rcu_gp_ctr, rcu_gp_ctr + RCU_GP_CTR); + qatomic_set(&rcu_gp_ctr, rcu_gp_ctr + RCU_GP_CTR); } wait_for_readers(); @@ -191,8 +189,22 @@ static void enqueue(struct rcu_head *node) struct rcu_head **old_tail; node->next = NULL; + + /* + * Make this node the tail of the list. The node will be + * used by further enqueue operations, but it will not + * be dequeued yet... + */ old_tail = qatomic_xchg(&tail, &node->next); - qatomic_mb_set(old_tail, node); + + /* + * ... until it is pointed to from another item in the list. + * In the meantime, try_dequeue() will find a NULL next pointer + * and loop. + * + * Synchronizes with qatomic_load_acquire() in try_dequeue(). + */ + qatomic_store_release(old_tail, node); } static struct rcu_head *try_dequeue(void) @@ -200,26 +212,31 @@ static struct rcu_head *try_dequeue(void) struct rcu_head *node, *next; retry: - /* Test for an empty list, which we do not expect. Note that for + /* Head is only written by this thread, so no need for barriers. */ + node = head; + + /* + * If the head node has NULL in its next pointer, the value is + * wrong and we need to wait until its enqueuer finishes the update. + */ + next = qatomic_load_acquire(&node->next); + if (!next) { + return NULL; + } + + /* + * Test for an empty list, which we do not expect. Note that for * the consumer head and tail are always consistent. The head * is consistent because only the consumer reads/writes it. * The tail, because it is the first step in the enqueuing. * It is only the next pointers that might be inconsistent. */ - if (head == &dummy && qatomic_mb_read(&tail) == &dummy.next) { + if (head == &dummy && qatomic_read(&tail) == &dummy.next) { abort(); } - /* If the head node has NULL in its next pointer, the value is - * wrong and we need to wait until its enqueuer finishes the update. - */ - node = head; - next = qatomic_mb_read(&head->next); - if (!next) { - return NULL; - } - - /* Since we are the sole consumer, and we excluded the empty case + /* + * Since we are the sole consumer, and we excluded the empty case * above, the queue will always have at least two nodes: the * dummy node, and the one being removed. So we do not need to update * the tail pointer. @@ -266,24 +283,24 @@ static void *call_rcu_thread(void *opaque) qatomic_sub(&rcu_call_count, n); synchronize_rcu(); - qemu_mutex_lock_iothread(); + bql_lock(); while (n > 0) { node = try_dequeue(); while (!node) { - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_event_reset(&rcu_call_ready_event); node = try_dequeue(); if (!node) { qemu_event_wait(&rcu_call_ready_event); node = try_dequeue(); } - qemu_mutex_lock_iothread(); + bql_lock(); } n--; node->func(node); } - qemu_mutex_unlock_iothread(); + bql_unlock(); } abort(); } @@ -320,13 +337,13 @@ static void drain_rcu_callback(struct rcu_head *node) void drain_call_rcu(void) { struct rcu_drain rcu_drain; - bool locked = qemu_mutex_iothread_locked(); + bool locked = bql_locked(); memset(&rcu_drain, 0, sizeof(struct rcu_drain)); qemu_event_init(&rcu_drain.drain_complete_event, false); if (locked) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } @@ -338,7 +355,7 @@ void drain_call_rcu(void) * * Note that since we have only one global queue of the RCU callbacks, * we also end up waiting for most of RCU callbacks that were registered - * on the other threads, but this is a side effect that shoudn't be + * on the other threads, but this is a side effect that shouldn't be * assumed. */ @@ -348,7 +365,7 @@ void drain_call_rcu(void) qatomic_dec(&in_drain_call_rcu); if (locked) { - qemu_mutex_lock_iothread(); + bql_lock(); } } @@ -392,7 +409,7 @@ static void rcu_init_complete(void) qemu_event_init(&rcu_call_ready_event, false); - /* The caller is assumed to have iothread lock, so the call_rcu thread + /* The caller is assumed to have BQL, so the call_rcu thread * must have been quiescent even after forking, just recreate it. */ qemu_thread_create(&thread, "call_rcu", call_rcu_thread, diff --git a/util/readline.c b/util/readline.c index f1ac6e4769..494a3d924e 100644 --- a/util/readline.c +++ b/util/readline.c @@ -286,6 +286,14 @@ void readline_add_completion(ReadLineState *rs, const char *str) } } +void readline_add_completion_of(ReadLineState *rs, + const char *pfx, const char *str) +{ + if (!strncmp(str, pfx, strlen(pfx))) { + readline_add_completion(rs, str); + } +} + void readline_set_completion_index(ReadLineState *rs, int index) { rs->completion_index = index; diff --git a/util/reserved-region.c b/util/reserved-region.c new file mode 100644 index 0000000000..18f83eb4c6 --- /dev/null +++ b/util/reserved-region.c @@ -0,0 +1,91 @@ +/* + * QEMU ReservedRegion helpers + * + * Copyright (c) 2023 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/range.h" +#include "qemu/reserved-region.h" + +GList *resv_region_list_insert(GList *list, ReservedRegion *reg) +{ + ReservedRegion *resv_iter, *new_reg; + Range *r = ®->range; + Range *range_iter; + GList *l; + + for (l = list; l ; ) { + resv_iter = (ReservedRegion *)l->data; + range_iter = &resv_iter->range; + + /* Skip all list elements strictly less than range to add */ + if (range_compare(range_iter, r) < 0) { + l = l->next; + } else if (range_compare(range_iter, r) > 0) { + return g_list_insert_before(list, l, reg); + } else { /* there is an overlap */ + if (range_contains_range(r, range_iter)) { + /* new range contains current item, simply remove this latter */ + GList *prev = l->prev; + g_free(l->data); + list = g_list_delete_link(list, l); + if (prev) { + l = prev->next; + } else { + l = list; + } + } else if (range_contains_range(range_iter, r)) { + /* new region is included in the current region */ + if (range_lob(range_iter) == range_lob(r)) { + /* adjacent on the left side, derives into 2 regions */ + range_set_bounds(range_iter, range_upb(r) + 1, + range_upb(range_iter)); + return g_list_insert_before(list, l, reg); + } else if (range_upb(range_iter) == range_upb(r)) { + /* adjacent on the right side, derives into 2 regions */ + range_set_bounds(range_iter, range_lob(range_iter), + range_lob(r) - 1); + l = l->next; + } else { + uint64_t lob = range_lob(range_iter); + /* + * the new range is in the middle of an existing one, + * split this latter into 3 regs instead + */ + range_set_bounds(range_iter, range_upb(r) + 1, + range_upb(range_iter)); + new_reg = g_new0(ReservedRegion, 1); + new_reg->type = resv_iter->type; + range_set_bounds(&new_reg->range, + lob, range_lob(r) - 1); + list = g_list_insert_before(list, l, new_reg); + return g_list_insert_before(list, l, reg); + } + } else if (range_lob(r) < range_lob(range_iter)) { + range_set_bounds(range_iter, range_upb(r) + 1, + range_upb(range_iter)); + return g_list_insert_before(list, l, reg); + } else { /* intersection on the upper range */ + range_set_bounds(range_iter, range_lob(range_iter), + range_lob(r) - 1); + l = l->next; + } + } /* overlap */ + } + return g_list_append(list, reg); +} + diff --git a/util/selfmap.c b/util/selfmap.c index 2c14f019ce..483cb617e2 100644 --- a/util/selfmap.c +++ b/util/selfmap.c @@ -10,74 +10,100 @@ #include "qemu/cutils.h" #include "qemu/selfmap.h" -GSList *read_self_maps(void) +IntervalTreeRoot *read_self_maps(void) { - gchar *maps; - GSList *map_info = NULL; + IntervalTreeRoot *root; + gchar *maps, **lines; + guint i, nlines; - 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); + if (!g_file_get_contents("/proc/self/maps", &maps, NULL, NULL)) { + return NULL; + } - 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 = 0; - const char *end; + root = g_new0(IntervalTreeRoot, 1); + lines = g_strsplit(maps, "\n", 0); + nlines = g_strv_length(lines); - errors |= qemu_strtoul(fields[0], &end, 16, &e->start); - errors |= qemu_strtoul(end + 1, NULL, 16, &e->end); + for (i = 0; i < nlines; i++) { + gchar **fields = g_strsplit(lines[i], " ", 6); + guint nfields = g_strv_length(fields); + + if (nfields > 4) { + uint64_t start, end, offset, inode; + unsigned dev_maj, dev_min; + int errors = 0; + const char *p; + + errors |= qemu_strtou64(fields[0], &p, 16, &start); + errors |= qemu_strtou64(p + 1, NULL, 16, &end); + errors |= qemu_strtou64(fields[2], NULL, 16, &offset); + errors |= qemu_strtoui(fields[3], &p, 16, &dev_maj); + errors |= qemu_strtoui(p + 1, NULL, 16, &dev_min); + errors |= qemu_strtou64(fields[4], NULL, 10, &inode); + + if (!errors) { + size_t path_len; + MapInfo *e; + + if (nfields == 6) { + p = fields[5]; + p += strspn(p, " "); + path_len = strlen(p) + 1; + } else { + p = NULL; + path_len = 0; + } + + e = g_malloc0(sizeof(*e) + path_len); + + e->itree.start = start; + e->itree.last = end - 1; + e->offset = offset; + e->dev = makedev(dev_maj, dev_min); + e->inode = inode; 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); - - if (!errors) { - /* - * 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); - } else { - g_free(e->dev); - g_free(e); + if (path_len) { + e->path = memcpy(e + 1, p, path_len); } + + interval_tree_insert(&e->itree, root); } - - g_strfreev(fields); } - g_strfreev(lines); - g_free(maps); + 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); + return root; } /** * free_self_maps: - * @info: a GSlist + * @root: an interval tree * - * Free a list of MapInfo structures. + * Free a tree of MapInfo structures. + * Since we allocated each MapInfo in one chunk, we need not consider the + * contents and can simply free each RBNode. */ -static void free_info(gpointer data) + +static void free_rbnode(RBNode *n) { - MapInfo *e = (MapInfo *) data; - g_free(e->dev); - g_free(e->path); - g_free(e); + if (n) { + free_rbnode(n->rb_left); + free_rbnode(n->rb_right); + g_free(n); + } } -void free_self_maps(GSList *info) +void free_self_maps(IntervalTreeRoot *root) { - g_slist_free_full(info, &free_info); + if (root) { + free_rbnode(root->rb_root.rb_node); + g_free(root); + } } diff --git a/util/stats64.c b/util/stats64.c index 897613c949..09736014ec 100644 --- a/util/stats64.c +++ b/util/stats64.c @@ -57,6 +57,17 @@ uint64_t stat64_get(const Stat64 *s) return ((uint64_t)high << 32) | low; } +void stat64_set(Stat64 *s, uint64_t val) +{ + while (!stat64_wrtrylock(s)) { + cpu_relax(); + } + + qatomic_set(&s->high, val >> 32); + qatomic_set(&s->low, val); + stat64_wrunlock(s); +} + bool stat64_add32_carry(Stat64 *s, uint32_t low, uint32_t high) { uint32_t old; diff --git a/util/systemd.c b/util/systemd.c index 5bcac9b401..ced518f771 100644 --- a/util/systemd.c +++ b/util/systemd.c @@ -51,6 +51,7 @@ unsigned int check_socket_activation(void) /* So these are not passed to any child processes we might start. */ unsetenv("LISTEN_FDS"); unsetenv("LISTEN_PID"); + unsetenv("LISTEN_FDNAMES"); /* So the file descriptors don't leak into child processes. */ for (i = 0; i < nr_fds; ++i) { diff --git a/util/thread-context.c b/util/thread-context.c new file mode 100644 index 0000000000..2bc7883b9e --- /dev/null +++ b/util/thread-context.c @@ -0,0 +1,356 @@ +/* + * QEMU Thread Context + * + * Copyright Red Hat Inc., 2022 + * + * Authors: + * David Hildenbrand + * + * 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/thread-context.h" +#include "qapi/error.h" +#include "qapi/qapi-builtin-visit.h" +#include "qapi/visitor.h" +#include "qemu/config-file.h" +#include "qapi/qapi-builtin-visit.h" +#include "qom/object_interfaces.h" +#include "qemu/module.h" +#include "qemu/bitmap.h" + +#ifdef CONFIG_NUMA +#include +#endif + +enum { + TC_CMD_NONE = 0, + TC_CMD_STOP, + TC_CMD_NEW, +}; + +typedef struct ThreadContextCmdNew { + QemuThread *thread; + const char *name; + void *(*start_routine)(void *); + void *arg; + int mode; +} ThreadContextCmdNew; + +static void *thread_context_run(void *opaque) +{ + ThreadContext *tc = opaque; + + tc->thread_id = qemu_get_thread_id(); + qemu_sem_post(&tc->sem); + + while (true) { + /* + * Threads inherit the CPU affinity of the creating thread. For this + * reason, we create new (especially short-lived) threads from our + * persistent context thread. + * + * Especially when QEMU is not allowed to set the affinity itself, + * management tools can simply set the affinity of the context thread + * after creating the context, to have new threads created via + * the context inherit the CPU affinity automatically. + */ + switch (tc->thread_cmd) { + case TC_CMD_NONE: + break; + case TC_CMD_STOP: + tc->thread_cmd = TC_CMD_NONE; + qemu_sem_post(&tc->sem); + return NULL; + case TC_CMD_NEW: { + ThreadContextCmdNew *cmd_new = tc->thread_cmd_data; + + qemu_thread_create(cmd_new->thread, cmd_new->name, + cmd_new->start_routine, cmd_new->arg, + cmd_new->mode); + tc->thread_cmd = TC_CMD_NONE; + tc->thread_cmd_data = NULL; + qemu_sem_post(&tc->sem); + break; + } + default: + g_assert_not_reached(); + } + qemu_sem_wait(&tc->sem_thread); + } +} + +static void thread_context_set_cpu_affinity(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + ThreadContext *tc = THREAD_CONTEXT(obj); + uint16List *l, *host_cpus = NULL; + unsigned long *bitmap = NULL; + int nbits = 0, ret; + + if (tc->init_cpu_bitmap) { + error_setg(errp, "Mixing CPU and node affinity not supported"); + return; + } + + if (!visit_type_uint16List(v, name, &host_cpus, errp)) { + return; + } + + if (!host_cpus) { + error_setg(errp, "CPU list is empty"); + goto out; + } + + for (l = host_cpus; l; l = l->next) { + nbits = MAX(nbits, l->value + 1); + } + bitmap = bitmap_new(nbits); + for (l = host_cpus; l; l = l->next) { + set_bit(l->value, bitmap); + } + + if (tc->thread_id != -1) { + /* + * Note: we won't be adjusting the affinity of any thread that is still + * around, but only the affinity of the context thread. + */ + ret = qemu_thread_set_affinity(&tc->thread, bitmap, nbits); + if (ret) { + error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret)); + } + } else { + tc->init_cpu_bitmap = bitmap; + bitmap = NULL; + tc->init_cpu_nbits = nbits; + } +out: + g_free(bitmap); + qapi_free_uint16List(host_cpus); +} + +static void thread_context_get_cpu_affinity(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + unsigned long *bitmap, nbits, value; + ThreadContext *tc = THREAD_CONTEXT(obj); + uint16List *host_cpus = NULL; + uint16List **tail = &host_cpus; + int ret; + + if (tc->thread_id == -1) { + error_setg(errp, "Object not initialized yet"); + return; + } + + ret = qemu_thread_get_affinity(&tc->thread, &bitmap, &nbits); + if (ret) { + error_setg(errp, "Getting CPU affinity failed: %s", strerror(ret)); + return; + } + + value = find_first_bit(bitmap, nbits); + while (value < nbits) { + QAPI_LIST_APPEND(tail, value); + + value = find_next_bit(bitmap, nbits, value + 1); + } + g_free(bitmap); + + visit_type_uint16List(v, name, &host_cpus, errp); + qapi_free_uint16List(host_cpus); +} + +static void thread_context_set_node_affinity(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ +#ifdef CONFIG_NUMA + const int nbits = numa_num_possible_cpus(); + ThreadContext *tc = THREAD_CONTEXT(obj); + uint16List *l, *host_nodes = NULL; + unsigned long *bitmap = NULL; + struct bitmask *tmp_cpus; + int ret, i; + + if (tc->init_cpu_bitmap) { + error_setg(errp, "Mixing CPU and node affinity not supported"); + return; + } + + if (!visit_type_uint16List(v, name, &host_nodes, errp)) { + return; + } + + if (!host_nodes) { + error_setg(errp, "Node list is empty"); + goto out; + } + + bitmap = bitmap_new(nbits); + tmp_cpus = numa_allocate_cpumask(); + for (l = host_nodes; l; l = l->next) { + numa_bitmask_clearall(tmp_cpus); + ret = numa_node_to_cpus(l->value, tmp_cpus); + if (ret) { + /* We ignore any errors, such as impossible nodes. */ + continue; + } + for (i = 0; i < nbits; i++) { + if (numa_bitmask_isbitset(tmp_cpus, i)) { + set_bit(i, bitmap); + } + } + } + numa_free_cpumask(tmp_cpus); + + if (bitmap_empty(bitmap, nbits)) { + error_setg(errp, "The nodes select no CPUs"); + goto out; + } + + if (tc->thread_id != -1) { + /* + * Note: we won't be adjusting the affinity of any thread that is still + * around for now, but only the affinity of the context thread. + */ + ret = qemu_thread_set_affinity(&tc->thread, bitmap, nbits); + if (ret) { + error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret)); + } + } else { + tc->init_cpu_bitmap = bitmap; + bitmap = NULL; + tc->init_cpu_nbits = nbits; + } +out: + g_free(bitmap); + qapi_free_uint16List(host_nodes); +#else + error_setg(errp, "NUMA node affinity is not supported by this QEMU"); +#endif +} + +static void thread_context_get_thread_id(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + ThreadContext *tc = THREAD_CONTEXT(obj); + uint64_t value = tc->thread_id; + + visit_type_uint64(v, name, &value, errp); +} + +static void thread_context_instance_complete(UserCreatable *uc, Error **errp) +{ + ThreadContext *tc = THREAD_CONTEXT(uc); + char *thread_name; + int ret; + + thread_name = g_strdup_printf("TC %s", + object_get_canonical_path_component(OBJECT(uc))); + qemu_thread_create(&tc->thread, thread_name, thread_context_run, tc, + QEMU_THREAD_JOINABLE); + g_free(thread_name); + + /* Wait until initialization of the thread is done. */ + while (tc->thread_id == -1) { + qemu_sem_wait(&tc->sem); + } + + if (tc->init_cpu_bitmap) { + ret = qemu_thread_set_affinity(&tc->thread, tc->init_cpu_bitmap, + tc->init_cpu_nbits); + if (ret) { + error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret)); + } + g_free(tc->init_cpu_bitmap); + tc->init_cpu_bitmap = NULL; + } +} + +static void thread_context_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = thread_context_instance_complete; + object_class_property_add(oc, "thread-id", "int", + thread_context_get_thread_id, NULL, NULL, + NULL); + object_class_property_add(oc, "cpu-affinity", "int", + thread_context_get_cpu_affinity, + thread_context_set_cpu_affinity, NULL, NULL); + object_class_property_add(oc, "node-affinity", "int", NULL, + thread_context_set_node_affinity, NULL, NULL); +} + +static void thread_context_instance_init(Object *obj) +{ + ThreadContext *tc = THREAD_CONTEXT(obj); + + tc->thread_id = -1; + qemu_sem_init(&tc->sem, 0); + qemu_sem_init(&tc->sem_thread, 0); + qemu_mutex_init(&tc->mutex); +} + +static void thread_context_instance_finalize(Object *obj) +{ + ThreadContext *tc = THREAD_CONTEXT(obj); + + if (tc->thread_id != -1) { + tc->thread_cmd = TC_CMD_STOP; + qemu_sem_post(&tc->sem_thread); + qemu_thread_join(&tc->thread); + } + qemu_sem_destroy(&tc->sem); + qemu_sem_destroy(&tc->sem_thread); + qemu_mutex_destroy(&tc->mutex); +} + +static const TypeInfo thread_context_info = { + .name = TYPE_THREAD_CONTEXT, + .parent = TYPE_OBJECT, + .class_init = thread_context_class_init, + .instance_size = sizeof(ThreadContext), + .instance_init = thread_context_instance_init, + .instance_finalize = thread_context_instance_finalize, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void thread_context_register_types(void) +{ + type_register_static(&thread_context_info); +} +type_init(thread_context_register_types) + +void thread_context_create_thread(ThreadContext *tc, QemuThread *thread, + const char *name, + void *(*start_routine)(void *), void *arg, + int mode) +{ + ThreadContextCmdNew data = { + .thread = thread, + .name = name, + .start_routine = start_routine, + .arg = arg, + .mode = mode, + }; + + qemu_mutex_lock(&tc->mutex); + tc->thread_cmd = TC_CMD_NEW; + tc->thread_cmd_data = &data; + qemu_sem_post(&tc->sem_thread); + + while (tc->thread_cmd != TC_CMD_NONE) { + qemu_sem_wait(&tc->sem); + } + qemu_mutex_unlock(&tc->mutex); +} diff --git a/util/thread-pool.c b/util/thread-pool.c index 31113b5860..27eb777e85 100644 --- a/util/thread-pool.c +++ b/util/thread-pool.c @@ -15,6 +15,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ #include "qemu/osdep.h" +#include "qemu/defer-call.h" #include "qemu/queue.h" #include "qemu/thread.h" #include "qemu/coroutine.h" @@ -48,7 +49,7 @@ struct ThreadPoolElement { /* Access to this list is protected by lock. */ QTAILQ_ENTRY(ThreadPoolElement) reqs; - /* Access to this list is protected by the global mutex. */ + /* This list is only written by the thread pool's mother thread. */ QLIST_ENTRY(ThreadPoolElement) all; }; @@ -120,13 +121,13 @@ static void *worker_thread(void *opaque) pool->cur_threads--; qemu_cond_signal(&pool->worker_stopped); - qemu_mutex_unlock(&pool->lock); /* * Wake up another thread, in case we got a wakeup but decided * to exit due to pool->cur_threads > pool->max_threads. */ qemu_cond_signal(&pool->request_cond); + qemu_mutex_unlock(&pool->lock); return NULL; } @@ -175,7 +176,8 @@ static void thread_pool_completion_bh(void *opaque) ThreadPool *pool = opaque; ThreadPoolElement *elem, *next; - aio_context_acquire(pool->ctx); + defer_call_begin(); /* cb() may use defer_call() to coalesce work */ + restart: QLIST_FOREACH_SAFE(elem, &pool->head, all, next) { if (elem->state != THREAD_DONE) { @@ -195,9 +197,7 @@ restart: */ qemu_bh_schedule(pool->completion_bh); - aio_context_release(pool->ctx); elem->common.cb(elem->common.opaque, elem->ret); - aio_context_acquire(pool->ctx); /* We can safely cancel the completion_bh here regardless of someone * else having scheduled it meanwhile because we reenter the @@ -211,7 +211,8 @@ restart: qemu_aio_unref(elem); } } - aio_context_release(pool->ctx); + + defer_call_end(); } static void thread_pool_cancel(BlockAIOCB *acb) @@ -232,24 +233,20 @@ static void thread_pool_cancel(BlockAIOCB *acb) } -static AioContext *thread_pool_get_aio_context(BlockAIOCB *acb) -{ - ThreadPoolElement *elem = (ThreadPoolElement *)acb; - ThreadPool *pool = elem->pool; - return pool->ctx; -} - static const AIOCBInfo thread_pool_aiocb_info = { .aiocb_size = sizeof(ThreadPoolElement), .cancel_async = thread_pool_cancel, - .get_aio_context = thread_pool_get_aio_context, }; -BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool, - ThreadPoolFunc *func, void *arg, - BlockCompletionFunc *cb, void *opaque) +BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, + BlockCompletionFunc *cb, void *opaque) { ThreadPoolElement *req; + AioContext *ctx = qemu_get_current_aio_context(); + ThreadPool *pool = aio_get_thread_pool(ctx); + + /* Assert that the thread submitting work is the same running the pool */ + assert(pool->ctx == qemu_get_current_aio_context()); req = qemu_aio_get(&thread_pool_aiocb_info, NULL, cb, opaque); req->func = func; @@ -284,19 +281,18 @@ static void thread_pool_co_cb(void *opaque, int ret) aio_co_wake(co->co); } -int coroutine_fn thread_pool_submit_co(ThreadPool *pool, ThreadPoolFunc *func, - void *arg) +int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg) { ThreadPoolCo tpc = { .co = qemu_coroutine_self(), .ret = -EINPROGRESS }; assert(qemu_in_coroutine()); - thread_pool_submit_aio(pool, func, arg, thread_pool_co_cb, &tpc); + thread_pool_submit_aio(func, arg, thread_pool_co_cb, &tpc); qemu_coroutine_yield(); return tpc.ret; } -void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg) +void thread_pool_submit(ThreadPoolFunc *func, void *arg) { - thread_pool_submit_aio(pool, func, arg, NULL, NULL); + thread_pool_submit_aio(func, arg, NULL, NULL); } void thread_pool_update_params(ThreadPool *pool, AioContext *ctx) diff --git a/util/throttle.c b/util/throttle.c index 81f247a8d1..9582899da3 100644 --- a/util/throttle.c +++ b/util/throttle.c @@ -136,13 +136,14 @@ int64_t throttle_compute_wait(LeakyBucket *bkt) /* This function compute the time that must be waited while this IO * - * @is_write: true if the current IO is a write, false if it's a read + * @direction: throttle direction * @ret: time to wait */ static int64_t throttle_compute_wait_for(ThrottleState *ts, - bool is_write) + ThrottleDirection direction) { - BucketType to_check[2][4] = { {THROTTLE_BPS_TOTAL, + static const BucketType to_check[THROTTLE_MAX][4] = { + {THROTTLE_BPS_TOTAL, THROTTLE_OPS_TOTAL, THROTTLE_BPS_READ, THROTTLE_OPS_READ}, @@ -153,8 +154,8 @@ static int64_t throttle_compute_wait_for(ThrottleState *ts, int64_t wait, max_wait = 0; int i; - for (i = 0; i < 4; i++) { - BucketType index = to_check[is_write][i]; + for (i = 0; i < ARRAY_SIZE(to_check[THROTTLE_READ]); i++) { + BucketType index = to_check[direction][i]; wait = throttle_compute_wait(&ts->cfg.buckets[index]); if (wait > max_wait) { max_wait = wait; @@ -166,13 +167,13 @@ static int64_t throttle_compute_wait_for(ThrottleState *ts, /* compute the timer for this type of operation * - * @is_write: the type of operation + * @direction: throttle direction * @now: the current clock timestamp * @next_timestamp: the resulting timer * @ret: true if a timer must be set */ static bool throttle_compute_timer(ThrottleState *ts, - bool is_write, + ThrottleDirection direction, int64_t now, int64_t *next_timestamp) { @@ -182,7 +183,7 @@ static bool throttle_compute_timer(ThrottleState *ts, throttle_do_leak(ts, now); /* compute the wait time if any */ - wait = throttle_compute_wait_for(ts, is_write); + wait = throttle_compute_wait_for(ts, direction); /* if the code must wait compute when the next timer should fire */ if (wait) { @@ -199,10 +200,15 @@ static bool throttle_compute_timer(ThrottleState *ts, void throttle_timers_attach_aio_context(ThrottleTimers *tt, AioContext *new_context) { - tt->timers[0] = aio_timer_new(new_context, tt->clock_type, SCALE_NS, - tt->read_timer_cb, tt->timer_opaque); - tt->timers[1] = aio_timer_new(new_context, tt->clock_type, SCALE_NS, - tt->write_timer_cb, tt->timer_opaque); + ThrottleDirection dir; + + for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) { + if (tt->timer_cb[dir]) { + tt->timers[dir] = + aio_timer_new(new_context, tt->clock_type, SCALE_NS, + tt->timer_cb[dir], tt->timer_opaque); + } + } } /* @@ -233,11 +239,12 @@ void throttle_timers_init(ThrottleTimers *tt, QEMUTimerCB *write_timer_cb, void *timer_opaque) { + assert(read_timer_cb || write_timer_cb); memset(tt, 0, sizeof(ThrottleTimers)); tt->clock_type = clock_type; - tt->read_timer_cb = read_timer_cb; - tt->write_timer_cb = write_timer_cb; + tt->timer_cb[THROTTLE_READ] = read_timer_cb; + tt->timer_cb[THROTTLE_WRITE] = write_timer_cb; tt->timer_opaque = timer_opaque; throttle_timers_attach_aio_context(tt, aio_context); } @@ -245,7 +252,9 @@ void throttle_timers_init(ThrottleTimers *tt, /* destroy a timer */ static void throttle_timer_destroy(QEMUTimer **timer) { - assert(*timer != NULL); + if (*timer == NULL) { + return; + } timer_free(*timer); *timer = NULL; @@ -254,10 +263,10 @@ static void throttle_timer_destroy(QEMUTimer **timer) /* Remove timers from event loop */ void throttle_timers_detach_aio_context(ThrottleTimers *tt) { - int i; + ThrottleDirection dir; - for (i = 0; i < 2; i++) { - throttle_timer_destroy(&tt->timers[i]); + for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) { + throttle_timer_destroy(&tt->timers[dir]); } } @@ -270,8 +279,12 @@ void throttle_timers_destroy(ThrottleTimers *tt) /* is any throttling timer configured */ bool throttle_timers_are_initialized(ThrottleTimers *tt) { - if (tt->timers[0]) { - return true; + ThrottleDirection dir; + + for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) { + if (tt->timers[dir]) { + return true; + } } return false; @@ -413,19 +426,24 @@ void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg) * NOTE: this function is not unit tested due to it's usage of timer_mod * * @tt: the timers structure - * @is_write: the type of operation (read/write) + * @direction: throttle direction * @ret: true if the timer has been scheduled else false */ bool throttle_schedule_timer(ThrottleState *ts, ThrottleTimers *tt, - bool is_write) + ThrottleDirection direction) { int64_t now = qemu_clock_get_ns(tt->clock_type); int64_t next_timestamp; + QEMUTimer *timer; bool must_wait; + assert(direction < THROTTLE_MAX); + timer = tt->timers[direction]; + assert(timer); + must_wait = throttle_compute_timer(ts, - is_write, + direction, now, &next_timestamp); @@ -435,48 +453,50 @@ bool throttle_schedule_timer(ThrottleState *ts, } /* request throttled and timer pending -> do nothing */ - if (timer_pending(tt->timers[is_write])) { + if (timer_pending(timer)) { return true; } /* request throttled and timer not pending -> arm timer */ - timer_mod(tt->timers[is_write], next_timestamp); + timer_mod(timer, next_timestamp); return true; } /* do the accounting for this operation * - * @is_write: the type of operation (read/write) + * @direction: throttle direction * @size: the size of the operation */ -void throttle_account(ThrottleState *ts, bool is_write, uint64_t size) +void throttle_account(ThrottleState *ts, ThrottleDirection direction, + uint64_t size) { - const BucketType bucket_types_size[2][2] = { + static const BucketType bucket_types_size[THROTTLE_MAX][2] = { { THROTTLE_BPS_TOTAL, THROTTLE_BPS_READ }, { THROTTLE_BPS_TOTAL, THROTTLE_BPS_WRITE } }; - const BucketType bucket_types_units[2][2] = { + static const BucketType bucket_types_units[THROTTLE_MAX][2] = { { THROTTLE_OPS_TOTAL, THROTTLE_OPS_READ }, { THROTTLE_OPS_TOTAL, THROTTLE_OPS_WRITE } }; double units = 1.0; unsigned i; + assert(direction < THROTTLE_MAX); /* if cfg.op_size is defined and smaller than size we compute unit count */ if (ts->cfg.op_size && size > ts->cfg.op_size) { units = (double) size / ts->cfg.op_size; } - for (i = 0; i < 2; i++) { + for (i = 0; i < ARRAY_SIZE(bucket_types_size[THROTTLE_READ]); i++) { LeakyBucket *bkt; - bkt = &ts->cfg.buckets[bucket_types_size[is_write][i]]; + bkt = &ts->cfg.buckets[bucket_types_size[direction][i]]; bkt->level += size; if (bkt->burst_length > 1) { bkt->burst_level += size; } - bkt = &ts->cfg.buckets[bucket_types_units[is_write][i]]; + bkt = &ts->cfg.buckets[bucket_types_units[direction][i]]; bkt->level += units; if (bkt->burst_length > 1) { bkt->burst_level += units; diff --git a/util/trace-events b/util/trace-events index c8f53d7d9f..49a4962e18 100644 --- a/util/trace-events +++ b/util/trace-events @@ -11,6 +11,7 @@ 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" aio_co_schedule_bh_cb(void *ctx, void *co) "ctx %p co %p" +reentrant_aio(void *ctx, const char *name) "ctx %p name %s" # thread-pool.c thread_pool_submit(void *pool, void *req, void *opaque) "pool %p req %p opaque %p" @@ -51,6 +52,10 @@ qemu_anon_ram_alloc(size_t size, void *ptr) "size %zu ptr %p" qemu_vfree(void *ptr) "ptr %p" qemu_anon_ram_free(void *ptr, size_t size) "ptr %p size %zu" +# oslib-win32.c +win32_map_alloc(size_t size) "size:%zd" +win32_map_free(void *ptr, void *h) "ptr:%p handle:%p" + # hbitmap.c hbitmap_iter_skip_words(const void *hb, void *hbi, uint64_t pos, unsigned long cur) "hb %p hbi %p pos %"PRId64" cur 0x%lx" hbitmap_reset(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64 @@ -93,6 +98,7 @@ qemu_vfio_region_info(const char *desc, uint64_t region_ofs, uint64_t region_siz qemu_vfio_pci_map_bar(int index, uint64_t region_ofs, uint64_t region_size, int ofs, void *host) "map region bar#%d addr 0x%"PRIx64" size 0x%"PRIx64" ofs 0x%x host %p" #userfaultfd.c +uffd_detect_open_mode(int mode) "%d" uffd_query_features_nosys(int err) "errno: %i" uffd_query_features_api_failed(int err) "errno: %i" uffd_create_fd_nosys(int err) "errno: %i" diff --git a/util/uri.c b/util/uri.c index ff72c6005f..573174bf47 100644 --- a/util/uri.c +++ b/util/uri.c @@ -43,8 +43,7 @@ * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library. If not, see . * * Authors: * Richard W.M. Jones @@ -164,19 +163,6 @@ static void uri_clean(URI *uri); ((*(p) == '+')) || ((*(p) == ',')) || ((*(p) == ';')) || \ ((*(p) == '=')) || ((*(p) == '\''))) -/* - * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" - */ -#define ISA_GEN_DELIM(p) \ - (((*(p) == ':')) || ((*(p) == '/')) || ((*(p) == '?')) || \ - ((*(p) == '#')) || ((*(p) == '[')) || ((*(p) == ']')) || \ - ((*(p) == '@'))) - -/* - * reserved = gen-delims / sub-delims - */ -#define ISA_RESERVED(p) (ISA_GEN_DELIM(p) || (ISA_SUB_DELIM(p))) - /* * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" */ @@ -268,7 +254,7 @@ static int rfc3986_parse_fragment(URI *uri, const char **str) if (uri->cleanup & 2) { uri->fragment = g_strndup(*str, cur - *str); } else { - uri->fragment = uri_string_unescape(*str, cur - *str, NULL); + uri->fragment = g_uri_unescape_segment(*str, cur, NULL); } } *str = cur; @@ -369,7 +355,7 @@ static int rfc3986_parse_user_info(URI *uri, const char **str) if (uri->cleanup & 2) { uri->user = g_strndup(*str, cur - *str); } else { - uri->user = uri_string_unescape(*str, cur - *str, NULL); + uri->user = g_uri_unescape_segment(*str, cur, NULL); } } *str = cur; @@ -497,7 +483,7 @@ found: if (uri->cleanup & 2) { uri->server = g_strndup(host, cur - host); } else { - uri->server = uri_string_unescape(host, cur - host, NULL); + uri->server = g_uri_unescape_segment(host, cur, NULL); } } else { uri->server = NULL; @@ -615,7 +601,7 @@ static int rfc3986_parse_path_ab_empty(URI *uri, const char **str) if (uri->cleanup & 2) { uri->path = g_strndup(*str, cur - *str); } else { - uri->path = uri_string_unescape(*str, cur - *str, NULL); + uri->path = g_uri_unescape_segment(*str, cur, NULL); } } else { uri->path = NULL; @@ -664,7 +650,7 @@ static int rfc3986_parse_path_absolute(URI *uri, const char **str) if (uri->cleanup & 2) { uri->path = g_strndup(*str, cur - *str); } else { - uri->path = uri_string_unescape(*str, cur - *str, NULL); + uri->path = g_uri_unescape_segment(*str, cur, NULL); } } else { uri->path = NULL; @@ -710,7 +696,7 @@ static int rfc3986_parse_path_rootless(URI *uri, const char **str) if (uri->cleanup & 2) { uri->path = g_strndup(*str, cur - *str); } else { - uri->path = uri_string_unescape(*str, cur - *str, NULL); + uri->path = g_uri_unescape_segment(*str, cur, NULL); } } else { uri->path = NULL; @@ -756,7 +742,7 @@ static int rfc3986_parse_path_no_scheme(URI *uri, const char **str) if (uri->cleanup & 2) { uri->path = g_strndup(*str, cur - *str); } else { - uri->path = uri_string_unescape(*str, cur - *str, NULL); + uri->path = g_uri_unescape_segment(*str, cur, NULL); } } else { uri->path = NULL; @@ -1350,846 +1336,12 @@ void uri_free(URI *uri) g_free(uri); } -/************************************************************************ - * * - * Helper functions * - * * - ************************************************************************/ - -/** - * normalize_uri_path: - * @path: pointer to the path string - * - * Applies the 5 normalization steps to a path string--that is, RFC 2396 - * Section 5.2, steps 6.c through 6.g. - * - * Normalization occurs directly on the string, no new allocation is done - * - * Returns 0 or an error code - */ -static int normalize_uri_path(char *path) -{ - char *cur, *out; - - if (path == NULL) { - return -1; - } - - /* Skip all initial "/" chars. We want to get to the beginning of the - * first non-empty segment. - */ - cur = path; - while (cur[0] == '/') { - ++cur; - } - if (cur[0] == '\0') { - return 0; - } - - /* Keep everything we've seen so far. */ - out = cur; - - /* - * Analyze each segment in sequence for cases (c) and (d). - */ - while (cur[0] != '\0') { - /* - * c) All occurrences of "./", where "." is a complete path segment, - * are removed from the buffer string. - */ - if ((cur[0] == '.') && (cur[1] == '/')) { - cur += 2; - /* '//' normalization should be done at this point too */ - while (cur[0] == '/') { - cur++; - } - continue; - } - - /* - * d) If the buffer string ends with "." as a complete path segment, - * that "." is removed. - */ - if ((cur[0] == '.') && (cur[1] == '\0')) { - break; - } - - /* Otherwise keep the segment. */ - while (cur[0] != '/') { - if (cur[0] == '\0') { - goto done_cd; - } - (out++)[0] = (cur++)[0]; - } - /* nomalize // */ - while ((cur[0] == '/') && (cur[1] == '/')) { - cur++; - } - - (out++)[0] = (cur++)[0]; - } -done_cd: - out[0] = '\0'; - - /* Reset to the beginning of the first segment for the next sequence. */ - cur = path; - while (cur[0] == '/') { - ++cur; - } - if (cur[0] == '\0') { - return 0; - } - - /* - * Analyze each segment in sequence for cases (e) and (f). - * - * e) All occurrences of "/../", where is a - * complete path segment not equal to "..", are removed from the - * buffer string. Removal of these path segments is performed - * iteratively, removing the leftmost matching pattern on each - * iteration, until no matching pattern remains. - * - * f) If the buffer string ends with "/..", where - * is a complete path segment not equal to "..", that - * "/.." is removed. - * - * To satisfy the "iterative" clause in (e), we need to collapse the - * string every time we find something that needs to be removed. Thus, - * we don't need to keep two pointers into the string: we only need a - * "current position" pointer. - */ - while (1) { - char *segp, *tmp; - - /* At the beginning of each iteration of this loop, "cur" points to - * the first character of the segment we want to examine. - */ - - /* Find the end of the current segment. */ - segp = cur; - while ((segp[0] != '/') && (segp[0] != '\0')) { - ++segp; - } - - /* If this is the last segment, we're done (we need at least two - * segments to meet the criteria for the (e) and (f) cases). - */ - if (segp[0] == '\0') { - break; - } - - /* If the first segment is "..", or if the next segment _isn't_ "..", - * keep this segment and try the next one. - */ - ++segp; - if (((cur[0] == '.') && (cur[1] == '.') && (segp == cur + 3)) || - ((segp[0] != '.') || (segp[1] != '.') || - ((segp[2] != '/') && (segp[2] != '\0')))) { - cur = segp; - continue; - } - - /* If we get here, remove this segment and the next one and back up - * to the previous segment (if there is one), to implement the - * "iteratively" clause. It's pretty much impossible to back up - * while maintaining two pointers into the buffer, so just compact - * the whole buffer now. - */ - - /* If this is the end of the buffer, we're done. */ - if (segp[2] == '\0') { - cur[0] = '\0'; - break; - } - /* Valgrind complained, strcpy(cur, segp + 3); */ - /* string will overlap, do not use strcpy */ - tmp = cur; - segp += 3; - while ((*tmp++ = *segp++) != 0) { - /* No further work */ - } - - /* If there are no previous segments, then keep going from here. */ - segp = cur; - while ((segp > path) && ((--segp)[0] == '/')) { - /* No further work */ - } - if (segp == path) { - continue; - } - - /* "segp" is pointing to the end of a previous segment; find it's - * start. We need to back up to the previous segment and start - * over with that to handle things like "foo/bar/../..". If we - * don't do this, then on the first pass we'll remove the "bar/..", - * but be pointing at the second ".." so we won't realize we can also - * remove the "foo/..". - */ - cur = segp; - while ((cur > path) && (cur[-1] != '/')) { - --cur; - } - } - out[0] = '\0'; - - /* - * g) If the resulting buffer string still begins with one or more - * complete path segments of "..", then the reference is - * considered to be in error. Implementations may handle this - * error by retaining these components in the resolved path (i.e., - * treating them as part of the final URI), by removing them from - * the resolved path (i.e., discarding relative levels above the - * root), or by avoiding traversal of the reference. - * - * We discard them from the final path. - */ - if (path[0] == '/') { - cur = path; - while ((cur[0] == '/') && (cur[1] == '.') && (cur[2] == '.') && - ((cur[3] == '/') || (cur[3] == '\0'))) { - cur += 3; - } - - if (cur != path) { - out = path; - while (cur[0] != '\0') { - (out++)[0] = (cur++)[0]; - } - out[0] = 0; - } - } - - return 0; -} - -static int is_hex(char c) -{ - if (((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'f')) || - ((c >= 'A') && (c <= 'F'))) { - return 1; - } - return 0; -} - -/** - * uri_string_unescape: - * @str: the string to unescape - * @len: the length in bytes to unescape (or <= 0 to indicate full string) - * @target: optional destination buffer - * - * Unescaping routine, but does not check that the string is an URI. The - * output is a direct unsigned char translation of %XX values (no encoding) - * Note that the length of the result can only be smaller or same size as - * the input string. - * - * Returns a copy of the string, but unescaped, will return NULL only in case - * of error - */ -char *uri_string_unescape(const char *str, int len, char *target) -{ - char *ret, *out; - const char *in; - - if (str == NULL) { - return NULL; - } - if (len <= 0) { - len = strlen(str); - } - if (len < 0) { - return NULL; - } - - if (target == NULL) { - ret = g_malloc(len + 1); - } else { - ret = target; - } - in = str; - out = ret; - while (len > 0) { - if ((len > 2) && (*in == '%') && (is_hex(in[1])) && (is_hex(in[2]))) { - in++; - if ((*in >= '0') && (*in <= '9')) { - *out = (*in - '0'); - } else if ((*in >= 'a') && (*in <= 'f')) { - *out = (*in - 'a') + 10; - } else if ((*in >= 'A') && (*in <= 'F')) { - *out = (*in - 'A') + 10; - } - in++; - if ((*in >= '0') && (*in <= '9')) { - *out = *out * 16 + (*in - '0'); - } else if ((*in >= 'a') && (*in <= 'f')) { - *out = *out * 16 + (*in - 'a') + 10; - } else if ((*in >= 'A') && (*in <= 'F')) { - *out = *out * 16 + (*in - 'A') + 10; - } - in++; - len -= 3; - out++; - } else { - *out++ = *in++; - len--; - } - } - *out = 0; - return ret; -} - -/** - * uri_string_escape: - * @str: string to escape - * @list: exception list string of chars not to escape - * - * This routine escapes a string to hex, ignoring reserved characters (a-z) - * and the characters in the exception list. - * - * Returns a new escaped string or NULL in case of error. - */ -char *uri_string_escape(const char *str, const char *list) -{ - char *ret, ch; - char *temp; - const char *in; - int len, out; - - if (str == NULL) { - return NULL; - } - if (str[0] == 0) { - return g_strdup(str); - } - len = strlen(str); - if (!(len > 0)) { - return NULL; - } - - len += 20; - ret = g_malloc(len); - in = str; - out = 0; - while (*in != 0) { - if (len - out <= 3) { - temp = realloc2n(ret, &len); - ret = temp; - } - - ch = *in; - - if ((ch != '@') && (!IS_UNRESERVED(ch)) && (!strchr(list, ch))) { - unsigned char val; - ret[out++] = '%'; - val = ch >> 4; - if (val <= 9) { - ret[out++] = '0' + val; - } else { - ret[out++] = 'A' + val - 0xA; - } - val = ch & 0xF; - if (val <= 9) { - ret[out++] = '0' + val; - } else { - ret[out++] = 'A' + val - 0xA; - } - in++; - } else { - ret[out++] = *in++; - } - } - ret[out] = 0; - return ret; -} - /************************************************************************ * * * Public functions * * * ************************************************************************/ -/** - * uri_resolve: - * @URI: the URI instance found in the document - * @base: the base value - * - * Computes he final URI of the reference done by checking that - * the given URI is valid, and building the final URI using the - * base URI. This is processed according to section 5.2 of the - * RFC 2396 - * - * 5.2. Resolving Relative References to Absolute Form - * - * Returns a new URI string (to be freed by the caller) or NULL in case - * of error. - */ -char *uri_resolve(const char *uri, const char *base) -{ - char *val = NULL; - int ret, len, indx, cur, out; - URI *ref = NULL; - URI *bas = NULL; - URI *res = NULL; - - /* - * 1) The URI reference is parsed into the potential four components and - * fragment identifier, as described in Section 4.3. - * - * NOTE that a completely empty URI is treated by modern browsers - * as a reference to "." rather than as a synonym for the current - * URI. Should we do that here? - */ - if (uri == NULL) { - ret = -1; - } else { - if (*uri) { - ref = uri_new(); - ret = uri_parse_into(ref, uri); - } else { - ret = 0; - } - } - if (ret != 0) { - goto done; - } - if ((ref != NULL) && (ref->scheme != NULL)) { - /* - * The URI is absolute don't modify. - */ - val = g_strdup(uri); - goto done; - } - if (base == NULL) { - ret = -1; - } else { - bas = uri_new(); - ret = uri_parse_into(bas, base); - } - if (ret != 0) { - if (ref) { - val = uri_to_string(ref); - } - goto done; - } - if (ref == NULL) { - /* - * the base fragment must be ignored - */ - g_free(bas->fragment); - bas->fragment = NULL; - val = uri_to_string(bas); - goto done; - } - - /* - * 2) If the path component is empty and the scheme, authority, and - * query components are undefined, then it is a reference to the - * current document and we are done. Otherwise, the reference URI's - * query and fragment components are defined as found (or not found) - * within the URI reference and not inherited from the base URI. - * - * NOTE that in modern browsers, the parsing differs from the above - * in the following aspect: the query component is allowed to be - * defined while still treating this as a reference to the current - * document. - */ - res = uri_new(); - if ((ref->scheme == NULL) && (ref->path == NULL) && - ((ref->authority == NULL) && (ref->server == NULL))) { - res->scheme = g_strdup(bas->scheme); - if (bas->authority != NULL) { - res->authority = g_strdup(bas->authority); - } else if (bas->server != NULL) { - res->server = g_strdup(bas->server); - res->user = g_strdup(bas->user); - res->port = bas->port; - } - res->path = g_strdup(bas->path); - if (ref->query != NULL) { - res->query = g_strdup(ref->query); - } else { - res->query = g_strdup(bas->query); - } - res->fragment = g_strdup(ref->fragment); - goto step_7; - } - - /* - * 3) If the scheme component is defined, indicating that the reference - * starts with a scheme name, then the reference is interpreted as an - * absolute URI and we are done. Otherwise, the reference URI's - * scheme is inherited from the base URI's scheme component. - */ - if (ref->scheme != NULL) { - val = uri_to_string(ref); - goto done; - } - res->scheme = g_strdup(bas->scheme); - - res->query = g_strdup(ref->query); - res->fragment = g_strdup(ref->fragment); - - /* - * 4) If the authority component is defined, then the reference is a - * network-path and we skip to step 7. Otherwise, the reference - * URI's authority is inherited from the base URI's authority - * component, which will also be undefined if the URI scheme does not - * use an authority component. - */ - if ((ref->authority != NULL) || (ref->server != NULL)) { - if (ref->authority != NULL) { - res->authority = g_strdup(ref->authority); - } else { - res->server = g_strdup(ref->server); - res->user = g_strdup(ref->user); - res->port = ref->port; - } - res->path = g_strdup(ref->path); - goto step_7; - } - if (bas->authority != NULL) { - res->authority = g_strdup(bas->authority); - } else if (bas->server != NULL) { - res->server = g_strdup(bas->server); - res->user = g_strdup(bas->user); - res->port = bas->port; - } - - /* - * 5) If the path component begins with a slash character ("/"), then - * the reference is an absolute-path and we skip to step 7. - */ - if ((ref->path != NULL) && (ref->path[0] == '/')) { - res->path = g_strdup(ref->path); - goto step_7; - } - - /* - * 6) If this step is reached, then we are resolving a relative-path - * reference. The relative path needs to be merged with the base - * URI's path. Although there are many ways to do this, we will - * describe a simple method using a separate string buffer. - * - * Allocate a buffer large enough for the result string. - */ - len = 2; /* extra / and 0 */ - if (ref->path != NULL) { - len += strlen(ref->path); - } - if (bas->path != NULL) { - len += strlen(bas->path); - } - res->path = g_malloc(len); - res->path[0] = 0; - - /* - * a) All but the last segment of the base URI's path component is - * copied to the buffer. In other words, any characters after the - * last (right-most) slash character, if any, are excluded. - */ - cur = 0; - out = 0; - if (bas->path != NULL) { - while (bas->path[cur] != 0) { - while ((bas->path[cur] != 0) && (bas->path[cur] != '/')) { - cur++; - } - if (bas->path[cur] == 0) { - break; - } - - cur++; - while (out < cur) { - res->path[out] = bas->path[out]; - out++; - } - } - } - res->path[out] = 0; - - /* - * b) The reference's path component is appended to the buffer - * string. - */ - if (ref->path != NULL && ref->path[0] != 0) { - indx = 0; - /* - * Ensure the path includes a '/' - */ - if ((out == 0) && (bas->server != NULL)) { - res->path[out++] = '/'; - } - while (ref->path[indx] != 0) { - res->path[out++] = ref->path[indx++]; - } - } - res->path[out] = 0; - - /* - * Steps c) to h) are really path normalization steps - */ - normalize_uri_path(res->path); - -step_7: - - /* - * 7) The resulting URI components, including any inherited from the - * base URI, are recombined to give the absolute form of the URI - * reference. - */ - val = uri_to_string(res); - -done: - uri_free(ref); - uri_free(bas); - uri_free(res); - return val; -} - -/** - * uri_resolve_relative: - * @URI: the URI reference under consideration - * @base: the base value - * - * Expresses the URI of the reference in terms relative to the - * base. Some examples of this operation include: - * base = "http://site1.com/docs/book1.html" - * URI input URI returned - * docs/pic1.gif pic1.gif - * docs/img/pic1.gif img/pic1.gif - * img/pic1.gif ../img/pic1.gif - * http://site1.com/docs/pic1.gif pic1.gif - * http://site2.com/docs/pic1.gif http://site2.com/docs/pic1.gif - * - * base = "docs/book1.html" - * URI input URI returned - * docs/pic1.gif pic1.gif - * docs/img/pic1.gif img/pic1.gif - * img/pic1.gif ../img/pic1.gif - * http://site1.com/docs/pic1.gif http://site1.com/docs/pic1.gif - * - * - * Note: if the URI reference is really weird or complicated, it may be - * worthwhile to first convert it into a "nice" one by calling - * uri_resolve (using 'base') before calling this routine, - * since this routine (for reasonable efficiency) assumes URI has - * already been through some validation. - * - * Returns a new URI string (to be freed by the caller) or NULL in case - * error. - */ -char *uri_resolve_relative(const char *uri, const char *base) -{ - char *val = NULL; - int ret; - int ix; - int pos = 0; - int nbslash = 0; - int len; - URI *ref = NULL; - URI *bas = NULL; - char *bptr, *uptr, *vptr; - int remove_path = 0; - - if ((uri == NULL) || (*uri == 0)) { - return NULL; - } - - /* - * First parse URI into a standard form - */ - ref = uri_new(); - /* If URI not already in "relative" form */ - if (uri[0] != '.') { - ret = uri_parse_into(ref, uri); - if (ret != 0) { - goto done; /* Error in URI, return NULL */ - } - } else { - ref->path = g_strdup(uri); - } - - /* - * Next parse base into the same standard form - */ - if ((base == NULL) || (*base == 0)) { - val = g_strdup(uri); - goto done; - } - bas = uri_new(); - if (base[0] != '.') { - ret = uri_parse_into(bas, base); - if (ret != 0) { - goto done; /* Error in base, return NULL */ - } - } else { - bas->path = g_strdup(base); - } - - /* - * If the scheme / server on the URI differs from the base, - * just return the URI - */ - if ((ref->scheme != NULL) && - ((bas->scheme == NULL) || (strcmp(bas->scheme, ref->scheme)) || - (strcmp(bas->server, ref->server)))) { - val = g_strdup(uri); - goto done; - } - if (bas->path == ref->path || - (bas->path && ref->path && !strcmp(bas->path, ref->path))) { - val = g_strdup(""); - goto done; - } - if (bas->path == NULL) { - val = g_strdup(ref->path); - goto done; - } - if (ref->path == NULL) { - ref->path = (char *)"/"; - remove_path = 1; - } - - /* - * At this point (at last!) we can compare the two paths - * - * First we take care of the special case where either of the - * two path components may be missing (bug 316224) - */ - if (bas->path == NULL) { - if (ref->path != NULL) { - uptr = ref->path; - if (*uptr == '/') { - uptr++; - } - /* exception characters from uri_to_string */ - val = uri_string_escape(uptr, "/;&=+$,"); - } - goto done; - } - bptr = bas->path; - if (ref->path == NULL) { - for (ix = 0; bptr[ix] != 0; ix++) { - if (bptr[ix] == '/') { - nbslash++; - } - } - uptr = NULL; - len = 1; /* this is for a string terminator only */ - } else { - /* - * Next we compare the two strings and find where they first differ - */ - if ((ref->path[pos] == '.') && (ref->path[pos + 1] == '/')) { - pos += 2; - } - if ((*bptr == '.') && (bptr[1] == '/')) { - bptr += 2; - } else if ((*bptr == '/') && (ref->path[pos] != '/')) { - bptr++; - } - while ((bptr[pos] == ref->path[pos]) && (bptr[pos] != 0)) { - pos++; - } - - if (bptr[pos] == ref->path[pos]) { - val = g_strdup(""); - goto done; /* (I can't imagine why anyone would do this) */ - } - - /* - * In URI, "back up" to the last '/' encountered. This will be the - * beginning of the "unique" suffix of URI - */ - ix = pos; - if ((ref->path[ix] == '/') && (ix > 0)) { - ix--; - } else if ((ref->path[ix] == 0) && (ix > 1) - && (ref->path[ix - 1] == '/')) { - ix -= 2; - } - for (; ix > 0; ix--) { - if (ref->path[ix] == '/') { - break; - } - } - if (ix == 0) { - uptr = ref->path; - } else { - ix++; - uptr = &ref->path[ix]; - } - - /* - * In base, count the number of '/' from the differing point - */ - if (bptr[pos] != ref->path[pos]) { /* check for trivial URI == base */ - for (; bptr[ix] != 0; ix++) { - if (bptr[ix] == '/') { - nbslash++; - } - } - } - len = strlen(uptr) + 1; - } - - if (nbslash == 0) { - if (uptr != NULL) { - /* exception characters from uri_to_string */ - val = uri_string_escape(uptr, "/;&=+$,"); - } - goto done; - } - - /* - * Allocate just enough space for the returned string - - * length of the remainder of the URI, plus enough space - * for the "../" groups, plus one for the terminator - */ - val = g_malloc(len + 3 * nbslash); - vptr = val; - /* - * Put in as many "../" as needed - */ - for (; nbslash > 0; nbslash--) { - *vptr++ = '.'; - *vptr++ = '.'; - *vptr++ = '/'; - } - /* - * Finish up with the end of the URI - */ - if (uptr != NULL) { - if ((vptr > val) && (len > 0) && (uptr[0] == '/') && - (vptr[-1] == '/')) { - memcpy(vptr, uptr + 1, len - 1); - vptr[len - 2] = 0; - } else { - memcpy(vptr, uptr, len); - vptr[len - 1] = 0; - } - } else { - vptr[len - 1] = 0; - } - - /* escape the freshly-built path */ - vptr = val; - /* exception characters from uri_to_string */ - val = uri_string_escape(vptr, "/;&=+$,"); - g_free(vptr); - -done: - /* - * Free the working variables - */ - if (remove_path != 0) { - ref->path = NULL; - } - uri_free(ref); - uri_free(bas); - - return val; -} - /* * Utility functions to help parse and assemble query strings. */ @@ -2275,14 +1427,14 @@ struct QueryParams *query_params_parse(const char *query) * and consistent with CGI.pm we assume value is "". */ else if (!eq) { - name = uri_string_unescape(query, end - query, NULL); + name = g_uri_unescape_segment(query, end, NULL); value = NULL; } /* Or if we have "name=" here (works around annoying * problem when calling uri_string_unescape with len = 0). */ else if (eq + 1 == end) { - name = uri_string_unescape(query, eq - query, NULL); + name = g_uri_unescape_segment(query, eq, NULL); value = g_new0(char, 1); } /* If the '=' character is at the beginning then we have @@ -2294,8 +1446,8 @@ struct QueryParams *query_params_parse(const char *query) /* Otherwise it's "name=value". */ else { - name = uri_string_unescape(query, eq - query, NULL); - value = uri_string_unescape(eq + 1, end - (eq + 1), NULL); + name = g_uri_unescape_segment(query, eq, NULL); + value = g_uri_unescape_segment(eq + 1, end, NULL); } /* Append to the parameter set. */ diff --git a/util/userfaultfd.c b/util/userfaultfd.c index f1cd6af2b1..1b2fa949d4 100644 --- a/util/userfaultfd.c +++ b/util/userfaultfd.c @@ -19,6 +19,46 @@ #include #include +typedef enum { + UFFD_UNINITIALIZED = 0, + UFFD_USE_DEV_PATH, + UFFD_USE_SYSCALL, +} uffd_open_mode; + +int uffd_open(int flags) +{ +#if defined(__NR_userfaultfd) + static uffd_open_mode open_mode; + static int uffd_dev; + + /* Detect how to generate uffd desc when run the 1st time */ + if (open_mode == UFFD_UNINITIALIZED) { + /* + * Make /dev/userfaultfd the default approach because it has better + * permission controls, meanwhile allows kernel faults without any + * privilege requirement (e.g. SYS_CAP_PTRACE). + */ + uffd_dev = open("/dev/userfaultfd", O_RDWR | O_CLOEXEC); + if (uffd_dev >= 0) { + open_mode = UFFD_USE_DEV_PATH; + } else { + /* Fallback to the system call */ + open_mode = UFFD_USE_SYSCALL; + } + trace_uffd_detect_open_mode(open_mode); + } + + if (open_mode == UFFD_USE_DEV_PATH) { + assert(uffd_dev >= 0); + return ioctl(uffd_dev, USERFAULTFD_IOC_NEW, flags); + } + + return syscall(__NR_userfaultfd, flags); +#else + return -EINVAL; +#endif +} + /** * uffd_query_features: query UFFD features * @@ -32,7 +72,7 @@ int uffd_query_features(uint64_t *features) struct uffdio_api api_struct = { 0 }; int ret = -1; - uffd_fd = syscall(__NR_userfaultfd, O_CLOEXEC); + uffd_fd = uffd_open(O_CLOEXEC); if (uffd_fd < 0) { trace_uffd_query_features_nosys(errno); return -1; @@ -69,7 +109,7 @@ int uffd_create_fd(uint64_t features, bool non_blocking) uint64_t ioctl_mask = BIT(_UFFDIO_REGISTER) | BIT(_UFFDIO_UNREGISTER); flags = O_CLOEXEC | (non_blocking ? O_NONBLOCK : 0); - uffd_fd = syscall(__NR_userfaultfd, flags); + uffd_fd = uffd_open(flags); if (uffd_fd < 0) { trace_uffd_create_fd_nosys(errno); return -1; diff --git a/util/uuid.c b/util/uuid.c index b1108dde78..234619dd5e 100644 --- a/util/uuid.c +++ b/util/uuid.c @@ -51,7 +51,7 @@ int qemu_uuid_is_equal(const QemuUUID *lhv, const QemuUUID *rhv) void qemu_uuid_unparse(const QemuUUID *uuid, char *out) { const unsigned char *uu = &uuid->data[0]; - snprintf(out, UUID_FMT_LEN + 1, UUID_FMT, + snprintf(out, UUID_STR_LEN, UUID_FMT, uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7], uu[8], uu[9], uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]); } @@ -116,3 +116,17 @@ QemuUUID qemu_uuid_bswap(QemuUUID uuid) bswap16s(&uuid.fields.time_high_and_version); return uuid; } + +/* djb2 hash algorithm */ +uint32_t qemu_uuid_hash(const void *uuid) +{ + QemuUUID *qid = (QemuUUID *) uuid; + uint32_t h = 5381; + int i; + + for (i = 0; i < ARRAY_SIZE(qid->data); i++) { + h = (h << 5) + h + qid->data[i]; + } + + return h; +} diff --git a/util/vfio-helpers.c b/util/vfio-helpers.c index 5ba01177bf..f8bab46c68 100644 --- a/util/vfio-helpers.c +++ b/util/vfio-helpers.c @@ -106,15 +106,17 @@ struct QEMUVFIOState { */ static char *sysfs_find_group_file(const char *device, Error **errp) { + g_autoptr(GError) gerr = NULL; char *sysfs_link; char *sysfs_group; char *p; char *path = NULL; sysfs_link = g_strdup_printf("/sys/bus/pci/devices/%s/iommu_group", device); - sysfs_group = g_malloc0(PATH_MAX); - if (readlink(sysfs_link, sysfs_group, PATH_MAX - 1) == -1) { - error_setg_errno(errp, errno, "Failed to find iommu group sysfs path"); + sysfs_group = g_file_read_link(sysfs_link, &gerr); + if (gerr) { + error_setg(errp, "Failed to find iommu group sysfs path: %s", + gerr->message); goto out; } p = strrchr(sysfs_group, '/'); @@ -240,9 +242,9 @@ static int qemu_vfio_pci_read_config(QEMUVFIOState *s, void *buf, s->config_region_info.offset, s->config_region_info.size); assert(QEMU_IS_ALIGNED(s->config_region_info.offset + ofs, size)); - do { - ret = pread(s->device, buf, size, s->config_region_info.offset + ofs); - } while (ret == -1 && errno == EINTR); + ret = RETRY_ON_EINTR( + pread(s->device, buf, size, s->config_region_info.offset + ofs) + ); return ret == size ? 0 : -errno; } @@ -254,9 +256,9 @@ static int qemu_vfio_pci_write_config(QEMUVFIOState *s, void *buf, int size, int s->config_region_info.offset, s->config_region_info.size); assert(QEMU_IS_ALIGNED(s->config_region_info.offset + ofs, size)); - do { - ret = pwrite(s->device, buf, size, s->config_region_info.offset + ofs); - } while (ret == -1 && errno == EINTR); + ret = RETRY_ON_EINTR( + pwrite(s->device, buf, size, s->config_region_info.offset + ofs) + ); return ret == size ? 0 : -errno; } @@ -271,7 +273,7 @@ static void collect_usable_iova_ranges(QEMUVFIOState *s, void *buf) if (!cap->next) { return; } - cap = (struct vfio_info_cap_header *)(buf + cap->next); + cap = buf + cap->next; } cap_iova_range = (struct vfio_iommu_type1_info_cap_iova_range *)cap; @@ -847,10 +849,13 @@ void qemu_vfio_close(QEMUVFIOState *s) if (!s) { return; } + + ram_block_notifier_remove(&s->ram_notifier); + for (i = 0; i < s->nr_mappings; ++i) { qemu_vfio_undo_mapping(s, &s->mappings[i], NULL); } - ram_block_notifier_remove(&s->ram_notifier); + g_free(s->usable_iova_ranges); s->nb_iova_ranges = 0; qemu_vfio_reset(s); diff --git a/util/vhost-user-server.c b/util/vhost-user-server.c index 232984ace6..3bfb1ad3ec 100644 --- a/util/vhost-user-server.c +++ b/util/vhost-user-server.c @@ -8,6 +8,7 @@ * later. See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/vhost-user-server.h" #include "block/aio-wait.h" @@ -74,20 +75,26 @@ static void panic_cb(VuDev *vu_dev, const char *buf) error_report("vu_panic: %s", buf); } -void vhost_user_server_ref(VuServer *server) +void vhost_user_server_inc_in_flight(VuServer *server) { assert(!server->wait_idle); - server->refcount++; + qatomic_inc(&server->in_flight); } -void vhost_user_server_unref(VuServer *server) +void vhost_user_server_dec_in_flight(VuServer *server) { - server->refcount--; - if (server->wait_idle && !server->refcount) { - aio_co_wake(server->co_trip); + if (qatomic_fetch_dec(&server->in_flight) == 1) { + if (server->wait_idle) { + aio_co_wake(server->co_trip); + } } } +bool vhost_user_server_has_in_flight(VuServer *server) +{ + return qatomic_load_acquire(&server->in_flight) > 0; +} + static bool coroutine_fn vu_message_read(VuDev *vu_dev, int conn_fd, VhostUserMsg *vmsg) { @@ -116,11 +123,17 @@ vu_message_read(VuDev *vu_dev, int conn_fd, VhostUserMsg *vmsg) * qio_channel_readv_full may have short reads, keeping calling it * until getting VHOST_USER_HDR_SIZE or 0 bytes in total */ - rc = qio_channel_readv_full(ioc, &iov, 1, &fds, &nfds, &local_err); + rc = qio_channel_readv_full(ioc, &iov, 1, &fds, &nfds, 0, &local_err); if (rc < 0) { if (rc == QIO_CHANNEL_ERR_BLOCK) { assert(local_err == NULL); - qio_channel_yield(ioc, G_IO_IN); + if (server->ctx) { + server->in_qio_channel_yield = true; + qio_channel_yield(ioc, G_IO_IN); + server->in_qio_channel_yield = false; + } else { + return false; + } continue; } else { error_report_err(local_err); @@ -187,17 +200,25 @@ static coroutine_fn void vu_client_trip(void *opaque) VuServer *server = opaque; VuDev *vu_dev = &server->vu_dev; - while (!vu_dev->broken && vu_dispatch(vu_dev)) { - /* Keep running */ + while (!vu_dev->broken) { + if (server->quiescing) { + server->co_trip = NULL; + aio_wait_kick(); + return; + } + /* vu_dispatch() returns false if server->ctx went away */ + if (!vu_dispatch(vu_dev) && server->ctx) { + break; + } } - if (server->refcount) { + if (vhost_user_server_has_in_flight(server)) { /* Wait for requests to complete before we can unmap the memory */ server->wait_idle = true; qemu_coroutine_yield(); server->wait_idle = false; } - assert(server->refcount == 0); + assert(!vhost_user_server_has_in_flight(server)); vu_deinit(vu_dev); @@ -264,14 +285,14 @@ set_watch(VuDev *vu_dev, int fd, int vu_evt, VuFdWatch *vu_fd_watch = find_vu_fd_watch(server, fd); if (!vu_fd_watch) { - VuFdWatch *vu_fd_watch = g_new0(VuFdWatch, 1); + vu_fd_watch = g_new0(VuFdWatch, 1); QTAILQ_INSERT_TAIL(&server->vu_fd_watches, vu_fd_watch, next); vu_fd_watch->fd = fd; vu_fd_watch->cb = cb; qemu_socket_set_nonblock(fd); - aio_set_fd_handler(server->ioc->ctx, fd, true, kick_handler, + aio_set_fd_handler(server->ctx, fd, kick_handler, NULL, NULL, NULL, vu_fd_watch); vu_fd_watch->vu_dev = vu_dev; vu_fd_watch->pvt = pvt; @@ -292,8 +313,7 @@ static void remove_watch(VuDev *vu_dev, int fd) if (!vu_fd_watch) { return; } - aio_set_fd_handler(server->ioc->ctx, fd, true, - NULL, NULL, NULL, NULL, NULL); + aio_set_fd_handler(server->ctx, fd, NULL, NULL, NULL, NULL, NULL); QTAILQ_REMOVE(&server->vu_fd_watches, vu_fd_watch, next); g_free(vu_fd_watch); @@ -338,17 +358,14 @@ static void vu_accept(QIONetListener *listener, QIOChannelSocket *sioc, /* TODO vu_message_write() spins if non-blocking! */ qio_channel_set_blocking(server->ioc, false, NULL); - server->co_trip = qemu_coroutine_create(vu_client_trip, server); + qio_channel_set_follow_coroutine_ctx(server->ioc, true); - aio_context_acquire(server->ctx); vhost_user_server_attach_aio_context(server, server->ctx); - aio_context_release(server->ctx); } +/* server->ctx acquired by caller */ void vhost_user_server_stop(VuServer *server) { - aio_context_acquire(server->ctx); - qemu_bh_delete(server->restart_listener_bh); server->restart_listener_bh = NULL; @@ -356,7 +373,7 @@ void vhost_user_server_stop(VuServer *server) VuFdWatch *vu_fd_watch; QTAILQ_FOREACH(vu_fd_watch, &server->vu_fd_watches, next) { - aio_set_fd_handler(server->ctx, vu_fd_watch->fd, true, + aio_set_fd_handler(server->ctx, vu_fd_watch->fd, NULL, NULL, NULL, NULL, vu_fd_watch); } @@ -365,8 +382,6 @@ void vhost_user_server_stop(VuServer *server) AIO_WAIT_WHILE(server->ctx, server->co_trip); } - aio_context_release(server->ctx); - if (server->listener) { qio_net_listener_disconnect(server->listener); object_unref(OBJECT(server->listener)); @@ -396,14 +411,30 @@ void vhost_user_server_attach_aio_context(VuServer *server, AioContext *ctx) return; } - qio_channel_attach_aio_context(server->ioc, ctx); - QTAILQ_FOREACH(vu_fd_watch, &server->vu_fd_watches, next) { - aio_set_fd_handler(ctx, vu_fd_watch->fd, true, kick_handler, NULL, + aio_set_fd_handler(ctx, vu_fd_watch->fd, kick_handler, NULL, NULL, NULL, vu_fd_watch); } - aio_co_schedule(ctx, server->co_trip); + if (server->co_trip) { + /* + * The caller didn't fully shut down co_trip (this can happen on + * non-polling drains like in bdrv_graph_wrlock()). This is okay as long + * as it no longer tries to shut it down and we're guaranteed to still + * be in the same AioContext as before. + * + * co_ctx can still be NULL if we get multiple calls and only just + * scheduled a new coroutine in the else branch. + */ + AioContext *co_ctx = qemu_coroutine_get_aio_context(server->co_trip); + + assert(!server->quiescing); + assert(!co_ctx || co_ctx == ctx); + } else { + server->co_trip = qemu_coroutine_create(vu_client_trip, server); + assert(!server->in_qio_channel_yield); + aio_co_schedule(ctx, server->co_trip); + } } /* Called with server->ctx acquired */ @@ -413,14 +444,19 @@ void vhost_user_server_detach_aio_context(VuServer *server) VuFdWatch *vu_fd_watch; QTAILQ_FOREACH(vu_fd_watch, &server->vu_fd_watches, next) { - aio_set_fd_handler(server->ctx, vu_fd_watch->fd, true, + aio_set_fd_handler(server->ctx, vu_fd_watch->fd, NULL, NULL, NULL, NULL, vu_fd_watch); } - - qio_channel_detach_aio_context(server->ioc); } server->ctx = NULL; + + if (server->ioc) { + if (server->in_qio_channel_yield) { + /* Stop receiving the next vhost-user message */ + qio_channel_wake_read(server->ioc); + } + } } bool vhost_user_server_start(VuServer *server, diff --git a/util/yank.c b/util/yank.c index abf47c346d..eaac50539c 100644 --- a/util/yank.c +++ b/util/yank.c @@ -35,7 +35,7 @@ typedef struct YankInstanceEntry YankInstanceEntry; /* * This lock protects the yank_instance_list below. Because it's taken by * OOB-capable commands, it must be "fast", i.e. it may only be held for a - * bounded, short time. See docs/devel/qapi-code-gen.txt for additional + * bounded, short time. See docs/devel/qapi-code-gen.rst for additional * information. */ static QemuMutex yank_lock;